Repository: BartoszCichecki/LenovoLegionToolkit Branch: master Commit: 63c173024495 Files: 761 Total size: 5.4 MB Directory structure: gitextract_7y507dhv/ ├── .gitattributes ├── .github/ │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── 1_feature_request.yml │ │ ├── 2_bug_report.yml │ │ ├── 3_compatibility_request.yml │ │ └── config.yml │ ├── pull_request_template.md │ └── workflows/ │ ├── build.yml │ └── release.yml ├── .gitignore ├── CONTRIBUTING.md ├── CONTRIBUTING_ja-JP.md ├── CONTRIBUTING_zh-hans.md ├── InnoDependencies/ │ ├── Arabic.isl │ ├── ChineseSimplified.isl │ ├── ChineseTraditional.isl │ ├── Greek.isl │ ├── Latvian.isl │ ├── Romanian.isl │ ├── Vietnamese.isl │ └── install_dotnet.iss ├── LICENSE ├── LenovoLegionToolkit.CLI/ │ ├── Flags.cs │ ├── IpcClient.cs │ ├── LenovoLegionToolkit.CLI.csproj │ └── Program.cs ├── LenovoLegionToolkit.CLI.Lib/ │ ├── Constants.cs │ ├── Extensions/ │ │ └── PipeStreamExtensions.cs │ ├── IpcConnectException.cs │ ├── IpcException.cs │ ├── IpcRequest.cs │ ├── IpcResponse.cs │ └── LenovoLegionToolkit.CLI.Lib.csproj ├── LenovoLegionToolkit.Lib/ │ ├── AutoListeners/ │ │ ├── AbstractAutoListener.cs │ │ ├── GameAutoListener.cs │ │ ├── IAutoListener.cs │ │ ├── InstanceStartedEventAutoAutoListener.cs │ │ ├── InstanceStoppedEventAutoAutoListener.cs │ │ ├── ProcessAutoListener.cs │ │ ├── TimeAutoListener.cs │ │ ├── UserInactivityAutoListener.cs │ │ └── WiFiAutoListener.cs │ ├── Controllers/ │ │ ├── AIController.cs │ │ ├── DisplayBrightnessController.cs │ │ ├── GPUController.cs │ │ ├── GPUOverclockController.cs │ │ ├── GodMode/ │ │ │ ├── AbstractGodModeController.cs │ │ │ ├── GodModeController.cs │ │ │ ├── GodModeControllerV1.cs │ │ │ ├── GodModeControllerV2.cs │ │ │ └── IGodModeController.cs │ │ ├── RGBKeyboardBacklightController.cs │ │ ├── Sensors/ │ │ │ ├── AbstractSensorsController.cs │ │ │ ├── ISensorsController.cs │ │ │ ├── SensorsController.cs │ │ │ ├── SensorsControllerV1.cs │ │ │ ├── SensorsControllerV2.cs │ │ │ └── SensorsControllerV3.cs │ │ ├── SmartFnLockController.cs │ │ ├── SpectrumKeyboardBacklightController.cs │ │ ├── WindowsPowerModeController.cs │ │ └── WindowsPowerPlanController.cs │ ├── Enums.cs │ ├── Extensions/ │ │ ├── AssemblyExtensions.cs │ │ ├── ContainerBuilderExtensions.cs │ │ ├── DateTimeExtensions.cs │ │ ├── DictionaryExtensions.cs │ │ ├── DisplayExtensions.cs │ │ ├── DisplayPossibleSettingExtensions.cs │ │ ├── DisplaySettingExtensions.cs │ │ ├── EnumExtensions.cs │ │ ├── EnumerableExtensions.cs │ │ ├── HttpClientExtensions.cs │ │ ├── IntExtensions.cs │ │ ├── ListExtensions.cs │ │ ├── LogoInfoFormatExtensions.cs │ │ ├── ManagementObjectSearcherExtensions.cs │ │ ├── MathExtensions.cs │ │ ├── OSExtensions.cs │ │ ├── PInvokeExtensions.cs │ │ ├── PhyscialGPUExtensions.cs │ │ ├── ProcessExtensions.cs │ │ ├── PropertyDataCollectionExtensions.cs │ │ ├── RGBKeyboardBacklightPresetExtensions.cs │ │ ├── RegistrationBuilderExtensions.cs │ │ ├── ServiceControllerExtension.cs │ │ ├── SpectrumKeyboardBacklightEffectTypeExtensions.cs │ │ ├── StreamExtensions.cs │ │ ├── StringExtensions.cs │ │ ├── TaskExtensions.cs │ │ ├── TimeExtensions.cs │ │ ├── UintExtensions.cs │ │ └── VersionExtensions.cs │ ├── Features/ │ │ ├── AbstractCapabilityFeature.cs │ │ ├── AbstractCompositeFeature.cs │ │ ├── AbstractDriverFeature.cs │ │ ├── AbstractLenovoLightingFeature.cs │ │ ├── AbstractUEFIFeature.cs │ │ ├── AbstractWmiFeature.cs │ │ ├── AlwaysOnUsbFeature.cs │ │ ├── BatteryFeature.cs │ │ ├── BatteryNightChargeFeature.cs │ │ ├── DpiScaleFeature.cs │ │ ├── FlipToStart/ │ │ │ ├── FlipToStartCapabilityFeature.cs │ │ │ ├── FlipToStartFeature.cs │ │ │ └── FlipToStartUEFIFeature.cs │ │ ├── FnLockFeature.cs │ │ ├── HDRFeature.cs │ │ ├── Hybrid/ │ │ │ ├── GSyncFeature.cs │ │ │ ├── HybridModeFeature.cs │ │ │ ├── IGPUModeCapabilityFeature.cs │ │ │ ├── IGPUModeChangeException.cs │ │ │ ├── IGPUModeFeature.cs │ │ │ ├── IGPUModeFeatureFlagsFeature.cs │ │ │ ├── IGPUModeGamezoneFeature.cs │ │ │ └── Notify/ │ │ │ ├── AbstractDGPUNotify.cs │ │ │ ├── DGPUCapabilityNotify.cs │ │ │ ├── DGPUFeatureFlagsNotify.cs │ │ │ ├── DGPUGamezoneNotify.cs │ │ │ ├── DGPUNotify.cs │ │ │ └── IDGPUNotify.cs │ │ ├── IFeature.cs │ │ ├── InstantBoot/ │ │ │ ├── InstantBootCapabilityFeature.cs │ │ │ ├── InstantBootFeature.cs │ │ │ └── InstantBootFeatureFlagsFeature.cs │ │ ├── MicrophoneFeature.cs │ │ ├── OneLevelWhiteKeyboardBacklightFeature.cs │ │ ├── OverDrive/ │ │ │ ├── OverDriveCapabilityFeature.cs │ │ │ ├── OverDriveFeature.cs │ │ │ └── OverDriveGameZoneFeature.cs │ │ ├── PanelLogo/ │ │ │ ├── PanelLogoBacklightFeature.cs │ │ │ ├── PanelLogoLenovoLightingBacklightFeature.cs │ │ │ └── PanelLogoSpectrumBacklightFeature.cs │ │ ├── PortsBacklightFeature.cs │ │ ├── PowerModeFeature.cs │ │ ├── RefreshRateFeature.cs │ │ ├── ResolutionFeature.cs │ │ ├── SpeakerFeature.cs │ │ ├── TouchpadLockFeature.cs │ │ ├── WhiteKeyboardBacklight/ │ │ │ ├── WhiteKeyboardBacklightFeature.cs │ │ │ ├── WhiteKeyboardDriverBacklightFeature.cs │ │ │ └── WhiteKeyboardLenovoLightingBacklightFeature.cs │ │ └── WinKeyFeature.cs │ ├── GameDetection/ │ │ ├── EffectiveGameModeDetector.cs │ │ └── GameConfigStoreDetector.cs │ ├── GlobalSuppressions.cs │ ├── HttpClientFactory.cs │ ├── Integrations/ │ │ └── HWiNFOIntegration.cs │ ├── Interfaces.cs │ ├── IoCContainer.cs │ ├── IoCModule.cs │ ├── LenovoLegionToolkit.Lib.csproj │ ├── LenovoLegionToolkit.Lib.csproj.DotSettings │ ├── Listeners/ │ │ ├── AbstractEventLogListener.cs │ │ ├── AbstractWMIListener.cs │ │ ├── DisplayBrightnessListener.cs │ │ ├── DisplayConfigurationListener.cs │ │ ├── DriverKeyListener.cs │ │ ├── IListener.cs │ │ ├── INotifyingListener.cs │ │ ├── LightingChangeListener.cs │ │ ├── NativeWindowsMessageListener.cs │ │ ├── PowerModeListener.cs │ │ ├── PowerStateListener.cs │ │ ├── RGBKeyboardBacklightListener.cs │ │ ├── SessionLockUnlockListener.cs │ │ ├── SpecialKeyListener.cs │ │ ├── SystemThemeListener.cs │ │ ├── ThermalModeListener.cs │ │ └── WinKeyListener.cs │ ├── Messaging/ │ │ ├── Messages/ │ │ │ ├── FeatureStateMessage.cs │ │ │ ├── IMessage.cs │ │ │ ├── NotificationMessage.cs │ │ │ ├── RGBKeyboardBacklightChangedMessage.cs │ │ │ └── SpectrumBacklightChangedMessage.cs │ │ └── MessagingCenter.cs │ ├── Native.cs │ ├── NativeMethods.json │ ├── NativeMethods.txt │ ├── PackageDownloader/ │ │ ├── AbstractPackageDownloader.cs │ │ ├── Detectors/ │ │ │ ├── Rules/ │ │ │ │ ├── AndPackageRule.cs │ │ │ │ ├── BiosPackageRule.cs │ │ │ │ ├── CpuAddressWidthPackageRule.cs │ │ │ │ ├── DriverPackageRule.cs │ │ │ │ ├── ExternalDetectionRule.cs │ │ │ │ ├── IPackageRule.cs │ │ │ │ ├── NotPackageRule.cs │ │ │ │ ├── OrPackageRule.cs │ │ │ │ ├── OsPackageRule.cs │ │ │ │ ├── PnPIdPackageRule.cs │ │ │ │ ├── RegistryKeyPackageRule.cs │ │ │ │ ├── RegistryKeyValuePackageRule.cs │ │ │ │ └── WindowsBuildVersionPackageRule.cs │ │ │ └── VantagePackageUpdateDetector.cs │ │ ├── IPackageDownloader.cs │ │ ├── PCSupportPackageDownloader.cs │ │ ├── PackageDownloaderFactory.cs │ │ ├── UpdateCatalogNotFoundException.cs │ │ └── VantagePackageDownloader.cs │ ├── Resources/ │ │ ├── Resource.Designer.cs │ │ ├── Resource.ar.resx │ │ ├── Resource.bg.resx │ │ ├── Resource.bs.resx │ │ ├── Resource.ca.resx │ │ ├── Resource.cs.resx │ │ ├── Resource.de.resx │ │ ├── Resource.el.resx │ │ ├── Resource.es.resx │ │ ├── Resource.fr.resx │ │ ├── Resource.hu.resx │ │ ├── Resource.it.resx │ │ ├── Resource.ja.resx │ │ ├── Resource.ko.resx │ │ ├── Resource.lv.resx │ │ ├── Resource.nl-nl.resx │ │ ├── Resource.pl.resx │ │ ├── Resource.pt-br.resx │ │ ├── Resource.pt.resx │ │ ├── Resource.resx │ │ ├── Resource.ro.resx │ │ ├── Resource.ru.resx │ │ ├── Resource.sk.resx │ │ ├── Resource.tr.resx │ │ ├── Resource.uk.resx │ │ ├── Resource.uz-latn-uz.resx │ │ ├── Resource.vi.resx │ │ ├── Resource.zh-hans.resx │ │ └── Resource.zh-hant.resx │ ├── Services/ │ │ └── BatteryDischargeRateMonitorService.cs │ ├── Settings/ │ │ ├── AbstractSettings.cs │ │ ├── ApplicationSettings.cs │ │ ├── BalanceModeSettings.cs │ │ ├── GPUOverclockSettings.cs │ │ ├── GodModeSettings.cs │ │ ├── IntegrationsSettings.cs │ │ ├── PackageDownloaderSettings.cs │ │ ├── RGBKeyboardSettings.cs │ │ ├── SpectrumKeyboardSettings.cs │ │ ├── SunriseSunsetSettings.cs │ │ └── UpdateCheckSettings.cs │ ├── SoftwareDisabler/ │ │ ├── AbstractSoftwareDisabler.cs │ │ ├── FnKeysDisabler.cs │ │ ├── LegionZoneDisabler.cs │ │ └── VantageDisabler.cs │ ├── Structs.cs │ ├── System/ │ │ ├── AirplaneMode.cs │ │ ├── Autorun.cs │ │ ├── Battery.cs │ │ ├── BootLogo.cs │ │ ├── CMD.cs │ │ ├── Devices.cs │ │ ├── Displays.cs │ │ ├── Drivers.cs │ │ ├── ExternalDisplays.cs │ │ ├── InternalDisplay.cs │ │ ├── KnownFolders.cs │ │ ├── Management/ │ │ │ ├── WMI.LenovoCapabilityData00.cs │ │ │ ├── WMI.LenovoCapabilityData01.cs │ │ │ ├── WMI.LenovoCpuMethod.cs │ │ │ ├── WMI.LenovoDefaultValueInDifferentModeData.cs │ │ │ ├── WMI.LenovoDiscreteData.cs │ │ │ ├── WMI.LenovoFanMethod.cs │ │ │ ├── WMI.LenovoFanTableData.cs │ │ │ ├── WMI.LenovoGameZoneData.cs │ │ │ ├── WMI.LenovoGameZoneKeyLockStatusEvent.cs │ │ │ ├── WMI.LenovoGameZoneLightProfileChangeEvent.cs │ │ │ ├── WMI.LenovoGameZoneSmartFanModeEvent.cs │ │ │ ├── WMI.LenovoGameZoneThermalModeEvent.cs │ │ │ ├── WMI.LenovoGpuMethod.cs │ │ │ ├── WMI.LenovoIntelligentOPList.cs │ │ │ ├── WMI.LenovoLightingData.cs │ │ │ ├── WMI.LenovoLightingEvent.cs │ │ │ ├── WMI.LenovoLightingMethod.cs │ │ │ ├── WMI.LenovoOtherMethod.cs │ │ │ ├── WMI.LenovoUtilityEvent.cs │ │ │ ├── WMI.Win32.cs │ │ │ ├── WMI.WmiMonitorBrightnessEvent.cs │ │ │ ├── WMI.WmiMonitorBrightnessMethods.cs │ │ │ └── WMI.cs │ │ ├── NVAPI.cs │ │ ├── Power.cs │ │ ├── Registry.cs │ │ ├── SystemPath.cs │ │ ├── SystemTheme.cs │ │ └── WiFi.cs │ └── Utils/ │ ├── Compatibility.cs │ ├── Crc32Adler.cs │ ├── Folders.cs │ ├── IMainThreadDispatcher.cs │ ├── LambdaAsyncDisposable.cs │ ├── LambdaDisposable.cs │ ├── Log.cs │ ├── NullSafeHandle.cs │ ├── RetryHelper.cs │ ├── SafePerformanceCounter.cs │ ├── StructSafeHandle.cs │ ├── SunriseSunset.cs │ ├── ThreadSafeBool.cs │ ├── ThreadSafeCounter.cs │ ├── ThrottleFirstDispatcher.cs │ ├── ThrottleLastDispatcher.cs │ ├── TokenManipulator.cs │ ├── UpdateChecker.cs │ └── WarrantyChecker.cs ├── LenovoLegionToolkit.Lib.Automation/ │ ├── AutomationContext.cs │ ├── AutomationEnvironment.cs │ ├── AutomationProcessor.cs │ ├── Enums.cs │ ├── GlobalSuppressions.cs │ ├── IAutomationEvent.cs │ ├── IoCModule.cs │ ├── LenovoLegionToolkit.Lib.Automation.csproj │ ├── LenovoLegionToolkit.Lib.Automation.csproj.DotSettings │ ├── Pipeline/ │ │ ├── AutomationPipeline.cs │ │ └── Triggers/ │ │ ├── ACAdapterConnectedAutomationPipelineTrigger.cs │ │ ├── ACAdapterDisconnectedAutomationPipelineTrigger.cs │ │ ├── AndAutomationPipelineTrigger.cs │ │ ├── DeviceConnectedAutomationPipelineTrigger.cs │ │ ├── DeviceDisconnectedAutomationPipelineTrigger.cs │ │ ├── DisplayOffAutomationPipelineTrigger.cs │ │ ├── DisplayOnAutomationPipelineTrigger.cs │ │ ├── ExternalDisplayConnectedAutomationPipelineTrigger.cs │ │ ├── ExternalDisplayDisconnectedAutomationPipelineTrigger.cs │ │ ├── GamesAreRunningAutomationPipelineTrigger.cs │ │ ├── GamesStopAutomationPipelineTrigger.cs │ │ ├── GodModePresetChangedAutomationPipelineTrigger.cs │ │ ├── HDROffAutomationPipelineTrigger.cs │ │ ├── HDROnAutomationPipelineTrigger.cs │ │ ├── IAutomationPipelineTrigger.cs │ │ ├── LidClosedAutomationPipelineTrigger.cs │ │ ├── LidOpenedAutomationPipelineTrigger.cs │ │ ├── LowWattageACAdapterConnectedAutomationPipelineTrigger.cs │ │ ├── OnResumeAutomationPipelineTrigger.cs │ │ ├── OnStartupAutomationPipelineTrigger.cs │ │ ├── PeriodicAutomationPipelineTrigger.cs │ │ ├── PowerModeAutomationPipelineTrigger.cs │ │ ├── ProcessesAreRunningAutomationPipelineTrigger.cs │ │ ├── ProcessesStopRunningAutomationPipelineTrigger.cs │ │ ├── SessionLockAutomationPipelineTrigger.cs │ │ ├── SessionUnlockAutomationPipelineTrigger.cs │ │ ├── TimeAutomationPipelineTrigger.cs │ │ ├── UserInactivityAutomationPipelineTrigger.cs │ │ ├── WiFiConnectedAutomationPipelineTrigger.cs │ │ └── WiFiDisconnectedAutomationPipelineTrigger.cs │ ├── Resources/ │ │ ├── Resource.Designer.cs │ │ ├── Resource.ar.resx │ │ ├── Resource.bg.resx │ │ ├── Resource.bs.resx │ │ ├── Resource.ca.resx │ │ ├── Resource.cs.resx │ │ ├── Resource.de.resx │ │ ├── Resource.el.resx │ │ ├── Resource.es.resx │ │ ├── Resource.fr.resx │ │ ├── Resource.hu.resx │ │ ├── Resource.it.resx │ │ ├── Resource.ja.resx │ │ ├── Resource.ko.resx │ │ ├── Resource.lv.resx │ │ ├── Resource.nl-nl.resx │ │ ├── Resource.pl.resx │ │ ├── Resource.pt-br.resx │ │ ├── Resource.pt.resx │ │ ├── Resource.resx │ │ ├── Resource.ro.resx │ │ ├── Resource.ru.resx │ │ ├── Resource.sk.resx │ │ ├── Resource.tr.resx │ │ ├── Resource.uk.resx │ │ ├── Resource.uz-latn-uz.resx │ │ ├── Resource.vi.resx │ │ ├── Resource.zh-hans.resx │ │ └── Resource.zh-hant.resx │ ├── Steps/ │ │ ├── AbstractFeatureAutomationStep.cs │ │ ├── AlwaysOnUsbAutomationStep.cs │ │ ├── BatteryAutomationStep.cs │ │ ├── BatteryNightChargeAutomationStep.cs │ │ ├── DeactivateGPUAutomationStep.cs │ │ ├── DelayAutomationStep.cs │ │ ├── DisplayBrightnessAutomationStep.cs │ │ ├── DpiScaleAutomationStep.cs │ │ ├── FlipToStartAutomationStep.cs │ │ ├── FnLockAutomationStep.cs │ │ ├── GodModePresetAutomationStep.cs │ │ ├── HDRAutomationStep.cs │ │ ├── HybridModeAutomationStep.cs │ │ ├── IAutomationStep.cs │ │ ├── InstantBootAutomationStep.cs │ │ ├── MacroAutomationStep.cs │ │ ├── MicrophoneAutomationStep.cs │ │ ├── NotificationAutomationStep.cs │ │ ├── OneLevelWhiteKeyboardBacklightAutomationStep.cs │ │ ├── OverDriveAutomationStep.cs │ │ ├── OverclockDiscreteGPUAutomationStep.cs │ │ ├── PanelLogoBacklightAutomationStep.cs │ │ ├── PlaySoundAutomationStep.cs │ │ ├── PortsBacklightAutomationStep.cs │ │ ├── PowerModeAutomationStep.cs │ │ ├── QuickActionAutomationStep.cs │ │ ├── RGBKeyboardBacklightAutomationStep.cs │ │ ├── RefreshRateAutomationStep.cs │ │ ├── ResolutionAutomationStep.cs │ │ ├── RunAutomationStep.cs │ │ ├── SpeakerAutomationStep.cs │ │ ├── SpectrumKeyboardBacklightBrightnessAutomationStep.cs │ │ ├── SpectrumKeyboardBacklightImportProfileAutomationStep.cs │ │ ├── SpectrumKeyboardBacklightProfileAutomationStep.cs │ │ ├── TouchpadLockAutomationStep.cs │ │ ├── TurnOffMonitorsAutomationStep.cs │ │ ├── TurnOffWiFiAutomationStep.cs │ │ ├── TurnOnWiFiAutomationStep.cs │ │ ├── WhiteKeyboardBacklightAutomationStep.cs │ │ └── WinKeyAutomationStep.cs │ ├── Structs.cs │ └── Utils/ │ └── AutomationSettings.cs ├── LenovoLegionToolkit.Lib.Macro/ │ ├── Enums.cs │ ├── IoCModule.cs │ ├── LenovoLegionToolkit.Lib.Macro.csproj │ ├── MacroController.cs │ ├── Resources/ │ │ ├── Resource.Designer.cs │ │ ├── Resource.ar.resx │ │ ├── Resource.bg.resx │ │ ├── Resource.bs.resx │ │ ├── Resource.cs.resx │ │ ├── Resource.de.resx │ │ ├── Resource.el.resx │ │ ├── Resource.es.resx │ │ ├── Resource.fr.resx │ │ ├── Resource.hu.resx │ │ ├── Resource.it.resx │ │ ├── Resource.ja.resx │ │ ├── Resource.ko.resx │ │ ├── Resource.lv.resx │ │ ├── Resource.nl-nl.resx │ │ ├── Resource.pl.resx │ │ ├── Resource.pt-br.resx │ │ ├── Resource.pt.resx │ │ ├── Resource.resx │ │ ├── Resource.ro.resx │ │ ├── Resource.ru.resx │ │ ├── Resource.sk.resx │ │ ├── Resource.tr.resx │ │ ├── Resource.uk.resx │ │ ├── Resource.uz-latn-uz.resx │ │ ├── Resource.vi.resx │ │ ├── Resource.zh-hans.resx │ │ └── Resource.zh-hant.resx │ ├── Structs.cs │ └── Utils/ │ ├── MacroPlayer.cs │ ├── MacroRecorder.cs │ ├── MacroSettings.cs │ └── TypeConverters/ │ └── MacroIdentifierTypeConverter.cs ├── LenovoLegionToolkit.SpectrumTester/ │ ├── LenovoLegionToolkit.SpectrumTester.csproj │ └── Program.cs ├── LenovoLegionToolkit.WPF/ │ ├── App.xaml │ ├── App.xaml.cs │ ├── Assets/ │ │ ├── AssetResources.Designer.cs │ │ └── AssetResources.resx │ ├── Behaviors/ │ │ └── ProgressBarAnimateBehavior.cs │ ├── CLI/ │ │ ├── Features/ │ │ │ ├── FeatureRegistration.cs │ │ │ ├── FeatureRegistry.cs │ │ │ └── IFeatureRegistration.cs │ │ └── IpcServer.cs │ ├── Constants.cs │ ├── Controls/ │ │ ├── AbstractComboBoxFeatureCardControl.cs │ │ ├── AbstractRefreshingControl.cs │ │ ├── AbstractToggleFeatureCardControl.cs │ │ ├── Automation/ │ │ │ ├── AbstractAutomationStepControl.cs │ │ │ ├── AbstractComboBoxAutomationStepControl.cs │ │ │ ├── AutomationPipelineControl.cs │ │ │ └── Steps/ │ │ │ ├── AlwaysOnUsbAutomationStepControl.cs │ │ │ ├── BatteryAutomationStepControl.cs │ │ │ ├── BatteryNightChargeAutomationStepControl.cs │ │ │ ├── DeactivateGPUAutomationStepControl.cs │ │ │ ├── DelayAutomationStepControl.cs │ │ │ ├── DisplayBrightnessAutomationStepControl.cs │ │ │ ├── DpiScaleAutomationStepControl.cs │ │ │ ├── FlipToStartAutomationStepControl.cs │ │ │ ├── FnLockAutomationStepControl.cs │ │ │ ├── GodModePresetAutomationStepControl.cs │ │ │ ├── HDRAutomationStepControl.cs │ │ │ ├── HybridModeAutomationStepControl.cs │ │ │ ├── InstantBootAutomationStepControl.cs │ │ │ ├── MacroAutomationStepControl.cs │ │ │ ├── MicrophoneAutomationStepControl.cs │ │ │ ├── NotificationAutomationStepControl.cs │ │ │ ├── OneLevelWhiteKeyboardBacklightAutomationStepControl.cs │ │ │ ├── OverDriveAutomationStepControl.cs │ │ │ ├── OverclockDiscreteGPUAutomationStepControl.cs │ │ │ ├── PanelLogoBacklightAutomationStepControl.cs │ │ │ ├── PlaySoundAutomationStepControl.cs │ │ │ ├── PortsBacklightAutomationStepControl.cs │ │ │ ├── PowerModeAutomationStepControl.cs │ │ │ ├── QuickActionAutomationStepControl.cs │ │ │ ├── RGBKeyboardBacklightAutomationStepControl.cs │ │ │ ├── RefreshRateAutomationStepControl.cs │ │ │ ├── ResolutionAutomationStepControl.cs │ │ │ ├── RunAutomationStepControl.cs │ │ │ ├── SpeakerAutomationStepControl.cs │ │ │ ├── SpectrumKeyboardBacklightBrightnessAutomationStepControl.cs │ │ │ ├── SpectrumKeyboardBacklightImportProfileAutomationStepControl.cs │ │ │ ├── SpectrumKeyboardBacklightProfileAutomationStepControl.cs │ │ │ ├── TouchpadLockAutomationStepControl.cs │ │ │ ├── TurnOffMonitorsAutomationStepControl.cs │ │ │ ├── TurnOffWiFiAutomationStepControl.cs │ │ │ ├── TurnOnWiFiAutomationStepControl.cs │ │ │ ├── WhiteKeyboardBacklightAutomationStepControl.cs │ │ │ └── WinKeyAutomationStepControl.cs │ │ ├── CardHeaderControl.cs │ │ ├── ColorPickerControl.xaml │ │ ├── ColorPickerControl.xaml.cs │ │ ├── Custom/ │ │ │ ├── Badge.cs │ │ │ ├── CardAction.cs │ │ │ ├── CardControl.cs │ │ │ ├── CardExpander.cs │ │ │ ├── InfoBar.cs │ │ │ └── NavigationItem.cs │ │ ├── Dashboard/ │ │ │ ├── AlwaysOnUSBControl.cs │ │ │ ├── BatteryModeControl.cs │ │ │ ├── BatteryNightChargeModeControl.cs │ │ │ ├── DashboardGroupControl.cs │ │ │ ├── DiscreteGPUControl.xaml │ │ │ ├── DiscreteGPUControl.xaml.cs │ │ │ ├── DpiScaleControl.cs │ │ │ ├── Edit/ │ │ │ │ ├── EditDashboardGroupControl.cs │ │ │ │ └── EditDashboardItemControl.cs │ │ │ ├── FlipToStartControl.cs │ │ │ ├── FnLockControl.cs │ │ │ ├── GodMode/ │ │ │ │ ├── GodModeValueControl.xaml │ │ │ │ └── GodModeValueControl.xaml.cs │ │ │ ├── HDRControl.cs │ │ │ ├── HybridModeControlFactory.cs │ │ │ ├── InstantBootControl.cs │ │ │ ├── MicrophoneControl.cs │ │ │ ├── OneLevelWhiteKeyboardBacklightControl.cs │ │ │ ├── OverDriveControl.cs │ │ │ ├── OverclockDiscreteGPUControl.cs │ │ │ ├── PanelLogoBacklightControl.cs │ │ │ ├── PortsBacklightControl.cs │ │ │ ├── PowerModeControl.cs │ │ │ ├── RefreshRateControl.cs │ │ │ ├── ResolutionControl.cs │ │ │ ├── SensorsControl.xaml │ │ │ ├── SensorsControl.xaml.cs │ │ │ ├── TouchpadLockControl.cs │ │ │ ├── TurnOffMonitorsControl.xaml │ │ │ ├── TurnOffMonitorsControl.xaml.cs │ │ │ ├── WhiteKeyboardBacklightControl.cs │ │ │ └── WinKeyControl.cs │ │ ├── FanCurveControl.xaml │ │ ├── FanCurveControl.xaml.cs │ │ ├── KeyboardBacklight/ │ │ │ ├── RGB/ │ │ │ │ ├── AbstractComboBoxRGBKeyboardCardControl.cs │ │ │ │ ├── RGBKeyboardBacklightBrightnessCardControl.cs │ │ │ │ ├── RGBKeyboardBacklightControl.xaml │ │ │ │ ├── RGBKeyboardBacklightControl.xaml.cs │ │ │ │ ├── RGBKeyboardBacklightEffectCardControl.cs │ │ │ │ └── RGBKeyboardBacklightSpeedCardControl.cs │ │ │ └── Spectrum/ │ │ │ ├── Device/ │ │ │ │ ├── SpectrumDeviceControl.cs │ │ │ │ ├── SpectrumDeviceFullAlternativeControl.xaml │ │ │ │ ├── SpectrumDeviceFullAlternativeControl.xaml.cs │ │ │ │ ├── SpectrumDeviceFullControl.xaml │ │ │ │ ├── SpectrumDeviceFullControl.xaml.cs │ │ │ │ ├── SpectrumDeviceKeyboardAndFrontControl.xaml │ │ │ │ ├── SpectrumDeviceKeyboardAndFrontControl.xaml.cs │ │ │ │ ├── SpectrumDeviceKeyboardOnlyControl.xaml │ │ │ │ ├── SpectrumDeviceKeyboardOnlyControl.xaml.cs │ │ │ │ ├── SpectrumKeyboardANSIControl.xaml │ │ │ │ ├── SpectrumKeyboardANSIControl.xaml.cs │ │ │ │ ├── SpectrumKeyboardControl.cs │ │ │ │ ├── SpectrumKeyboardISOControl.xaml │ │ │ │ ├── SpectrumKeyboardISOControl.xaml.cs │ │ │ │ ├── SpectrumKeyboardJisControl.xaml │ │ │ │ ├── SpectrumKeyboardJisControl.xaml.cs │ │ │ │ ├── SpectrumZoneControl.xaml │ │ │ │ └── SpectrumZoneControl.xaml.cs │ │ │ ├── SpectrumKeyboardBacklightControl.xaml │ │ │ ├── SpectrumKeyboardBacklightControl.xaml.cs │ │ │ ├── SpectrumKeyboardEffectControl.xaml │ │ │ └── SpectrumKeyboardEffectControl.xaml.cs │ │ ├── LoadableControl.cs │ │ ├── Macro/ │ │ │ ├── AbstractMacroEventControl.xaml │ │ │ ├── AbstractMacroEventControl.xaml.cs │ │ │ ├── MacroSequenceControl.xaml │ │ │ ├── MacroSequenceControl.xaml.cs │ │ │ ├── MultiAbstractMacroEventControl.cs │ │ │ └── SingleAbstractMacroEventControl.cs │ │ ├── MultiColorPickerControl.xaml │ │ ├── MultiColorPickerControl.xaml.cs │ │ ├── MultiColorPickerItemControl.xaml │ │ ├── MultiColorPickerItemControl.xaml.cs │ │ ├── Packages/ │ │ │ ├── PackageControl.xaml │ │ │ └── PackageControl.xaml.cs │ │ └── SelectableControl.cs │ ├── Enums.cs │ ├── Extensions/ │ │ ├── AutomationPipelineTriggerExtensions.cs │ │ ├── ClipboardExtensions.cs │ │ ├── ColorExtensions.cs │ │ ├── ComboBoxExtensions.cs │ │ ├── DashboardItemExtensions.cs │ │ ├── DispatcherExtensions.cs │ │ ├── ImageSourceExtensions.cs │ │ ├── ItemCollectionExtensions.cs │ │ ├── NavigationStoreExtensions.cs │ │ ├── PowerModeStateExtensions.cs │ │ ├── RGBColorExtensions.cs │ │ ├── UIElementExtensions.cs │ │ ├── UriExtensions.cs │ │ └── WindowExtensions.cs │ ├── Flags.cs │ ├── GlobalSuppressions.cs │ ├── IoCModule.cs │ ├── LenovoLegionToolkit.WPF.csproj │ ├── LenovoLegionToolkit.WPF.csproj.DotSettings │ ├── Pages/ │ │ ├── AboutPage.xaml │ │ ├── AboutPage.xaml.cs │ │ ├── AutomationPage.xaml │ │ ├── AutomationPage.xaml.cs │ │ ├── BatteryPage.xaml │ │ ├── BatteryPage.xaml.cs │ │ ├── DashboardPage.xaml │ │ ├── DashboardPage.xaml.cs │ │ ├── DonatePage.xaml │ │ ├── DonatePage.xaml.cs │ │ ├── KeyboardBacklightPage.xaml │ │ ├── KeyboardBacklightPage.xaml.cs │ │ ├── MacroPage.xaml │ │ ├── MacroPage.xaml.cs │ │ ├── PackagesPage.xaml │ │ ├── PackagesPage.xaml.cs │ │ ├── SettingsPage.xaml │ │ └── SettingsPage.xaml.cs │ ├── Properties/ │ │ └── launchSettings.json │ ├── Resources/ │ │ ├── Resource.Designer.cs │ │ ├── Resource.ar.resx │ │ ├── Resource.bg.resx │ │ ├── Resource.bs.resx │ │ ├── Resource.ca.resx │ │ ├── Resource.cs.resx │ │ ├── Resource.de.resx │ │ ├── Resource.el.resx │ │ ├── Resource.es.resx │ │ ├── Resource.fr.resx │ │ ├── Resource.hu.resx │ │ ├── Resource.it.resx │ │ ├── Resource.ja.resx │ │ ├── Resource.ko.resx │ │ ├── Resource.lv.resx │ │ ├── Resource.nl-nl.resx │ │ ├── Resource.pl.resx │ │ ├── Resource.pt-br.resx │ │ ├── Resource.pt.resx │ │ ├── Resource.resx │ │ ├── Resource.ro.resx │ │ ├── Resource.ru.resx │ │ ├── Resource.sk.resx │ │ ├── Resource.tr.resx │ │ ├── Resource.uk.resx │ │ ├── Resource.uz-latn-uz.resx │ │ ├── Resource.vi.resx │ │ ├── Resource.zh-hans.resx │ │ └── Resource.zh-hant.resx │ ├── Settings/ │ │ └── DashboardSettings.cs │ ├── Structs.cs │ ├── Styles/ │ │ ├── Badge.xaml │ │ ├── CardAction.xaml │ │ ├── CardControl.xaml │ │ ├── CardExpander.xaml │ │ ├── DynamicScrollBar.xaml │ │ ├── InfoBar.xaml │ │ └── NavigationStore.xaml │ ├── Utils/ │ │ ├── FullscreenHelper.cs │ │ ├── LocalizationHelper.cs │ │ ├── MainThreadDispatcher.cs │ │ ├── MesageBoxHelper.cs │ │ ├── NotificationsManager.cs │ │ ├── NotifyIcon.cs │ │ ├── ScreenHelper.cs │ │ ├── SmartKeyHelper.cs │ │ ├── SnackbarHelper.cs │ │ ├── SpectrumScreenCapture.cs │ │ ├── ThemeManager.cs │ │ └── TrayHelper.cs │ ├── Windows/ │ │ ├── Automation/ │ │ │ ├── AddAutomationStepWindow.xaml │ │ │ ├── AddAutomationStepWindow.xaml.cs │ │ │ ├── AutomationPipelineTriggerConfigurationWindow.xaml │ │ │ ├── AutomationPipelineTriggerConfigurationWindow.xaml.cs │ │ │ ├── CreateAutomationPipelineWindow.xaml │ │ │ ├── CreateAutomationPipelineWindow.xaml.cs │ │ │ └── TabItemContent/ │ │ │ ├── DeviceAutomationPipelineTriggerTabItemContent.xaml │ │ │ ├── DeviceAutomationPipelineTriggerTabItemContent.xaml.cs │ │ │ ├── GodModePresetPipelineTriggerTabItemContent.xaml │ │ │ ├── GodModePresetPipelineTriggerTabItemContent.xaml.cs │ │ │ ├── IAutomationPipelineTriggerTabItemContent.cs │ │ │ ├── PeriodicActionPipelineTriggerTabItemContent.xaml │ │ │ ├── PeriodicActionPipelineTriggerTabItemContent.xaml.cs │ │ │ ├── PowerModeAutomationPipelineTriggerTabItemContent.xaml │ │ │ ├── PowerModeAutomationPipelineTriggerTabItemContent.xaml.cs │ │ │ ├── ProcessAutomationPipelineTriggerTabItemControl.xaml │ │ │ ├── ProcessAutomationPipelineTriggerTabItemControl.xaml.cs │ │ │ ├── TimeAutomationPipelineTriggerTabItemContent.xaml │ │ │ ├── TimeAutomationPipelineTriggerTabItemContent.xaml.cs │ │ │ ├── UserInactivityPipelineTriggerTabItemContent.xaml │ │ │ ├── UserInactivityPipelineTriggerTabItemContent.xaml.cs │ │ │ ├── WiFiConnectedPipelineTriggerTabItemContent.xaml │ │ │ └── WiFiConnectedPipelineTriggerTabItemContent.xaml.cs │ │ ├── BaseWindow.cs │ │ ├── Dashboard/ │ │ │ ├── AddDashboardItemWindow.xaml │ │ │ ├── AddDashboardItemWindow.xaml.cs │ │ │ ├── BalanceModeSettingsWindow.xaml │ │ │ ├── BalanceModeSettingsWindow.xaml.cs │ │ │ ├── EditDashboardWindow.xaml │ │ │ ├── EditDashboardWindow.xaml.cs │ │ │ ├── ExtendedHybridModeInfoWindow.xaml │ │ │ ├── ExtendedHybridModeInfoWindow.xaml.cs │ │ │ ├── GodModeSettingsWindow.xaml │ │ │ ├── GodModeSettingsWindow.xaml.cs │ │ │ ├── OverclockDiscreteGPUSettingsWindow.xaml │ │ │ └── OverclockDiscreteGPUSettingsWindow.xaml.cs │ │ ├── KeyboardBacklight/ │ │ │ └── Spectrum/ │ │ │ ├── SpectrumKeyboardBacklightEditEffectWindow.xaml │ │ │ └── SpectrumKeyboardBacklightEditEffectWindow.xaml.cs │ │ ├── Macro/ │ │ │ └── MacroRecordingWindow.cs │ │ ├── MainWindow.xaml │ │ ├── MainWindow.xaml.cs │ │ ├── Settings/ │ │ │ ├── BootLogoWindow.xaml │ │ │ ├── BootLogoWindow.xaml.cs │ │ │ ├── ExcludeRefreshRatesWindow.xaml │ │ │ ├── ExcludeRefreshRatesWindow.xaml.cs │ │ │ ├── NotificationsSettingsWindow.xaml │ │ │ ├── NotificationsSettingsWindow.xaml.cs │ │ │ ├── SelectSmartKeyPipelinesWindow.xaml │ │ │ ├── SelectSmartKeyPipelinesWindow.xaml.cs │ │ │ ├── WindowsPowerModesWindow.xaml │ │ │ ├── WindowsPowerModesWindow.xaml.cs │ │ │ ├── WindowsPowerPlansWindow.xaml │ │ │ └── WindowsPowerPlansWindow.xaml.cs │ │ └── Utils/ │ │ ├── DeviceInformationWindow.xaml │ │ ├── DeviceInformationWindow.xaml.cs │ │ ├── INotificationWindow.cs │ │ ├── LanguageSelectorWindow.xaml │ │ ├── LanguageSelectorWindow.xaml.cs │ │ ├── NativeLayeredWindow.cs │ │ ├── NotificationAoTWindow.cs │ │ ├── NotificationWindow.cs │ │ ├── StatusWindow.xaml │ │ ├── StatusWindow.xaml.cs │ │ ├── SymbolRegularPicker.xaml │ │ ├── SymbolRegularPicker.xaml.cs │ │ ├── UnsupportedWindow.xaml │ │ ├── UnsupportedWindow.xaml.cs │ │ ├── UpdateWindow.xaml │ │ └── UpdateWindow.xaml.cs │ └── app.manifest ├── LenovoLegionToolkit.sln ├── LenovoLegionToolkit.sln.DotSettings ├── README.md ├── README_ja-JP.md ├── README_zh-hans.md ├── clean.bat ├── crowdin.yml ├── make.bat └── make_installer.iss ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ ############################################################################### # Set default behavior to automatically normalize line endings. ############################################################################### * text=auto ############################################################################### # Set default behavior for command prompt diff. # # This is need for earlier builds of msysgit that does not have it on by # default for csharp files. # Note: This is only used by command line ############################################################################### #*.cs diff=csharp ############################################################################### # Set the merge driver for project and solution files # # Merging from the command prompt will add diff markers to the files if there # are conflicts (Merging from VS is not affected by the settings below, in VS # the diff markers are never inserted). Diff markers may cause the following # file extensions to fail to load in VS. An alternative would be to treat # these files as binary and thus will always conflict and require user # intervention with every merge. To do so, just uncomment the entries below ############################################################################### #*.sln merge=binary #*.csproj merge=binary #*.vbproj merge=binary #*.vcxproj merge=binary #*.vcproj merge=binary #*.dbproj merge=binary #*.fsproj merge=binary #*.lsproj merge=binary #*.wixproj merge=binary #*.modelproj merge=binary #*.sqlproj merge=binary #*.wwaproj merge=binary ############################################################################### # behavior for image files # # image files are treated as binary by default. ############################################################################### #*.jpg binary #*.png binary #*.gif binary ############################################################################### # diff behavior for common document formats # # Convert binary document formats to text before diffing them. This feature # is only available from the command line. Turn it on by uncommenting the # entries below. ############################################################################### #*.doc diff=astextplain #*.DOC diff=astextplain #*.docx diff=astextplain #*.DOCX diff=astextplain #*.dot diff=astextplain #*.DOT diff=astextplain #*.pdf diff=astextplain #*.PDF diff=astextplain #*.rtf diff=astextplain #*.RTF diff=astextplain ================================================ FILE: .github/FUNDING.yml ================================================ github: [BartoszCichecki] custom: ["https://paypal.me/bartoszcichecki"] ================================================ FILE: .github/ISSUE_TEMPLATE/1_feature_request.yml ================================================ name: Feature Request description: Suggest an idea for this project labels: ["feature"] body: - type: markdown attributes: value: | > [!IMPORTANT] > Before you start, read [the rules](https://github.com/BartoszCichecki/LenovoLegionToolkit/blob/master/CONTRIBUTING.md) for contributing. - type: input id: version attributes: label: Version description: Which Lenovo Legion Toolkit version do you use? placeholder: e.g. 2.14.0 validations: required: true - type: input id: os attributes: label: OS description: Which operating system do you use? placeholder: e.g. Windows 11 21H2 build 22000 validations: required: true - type: input id: device attributes: label: Device description: Which laptop do you use? placeholder: e.g. Legion 5 Pro 16ACH6 validations: required: true - type: textarea id: problem attributes: label: Is your feature request related to a problem? description: A clear and concise description of what the problem is. placeholder: A clear and concise description of what the problem is. validations: required: true - type: textarea id: solution attributes: label: How would you like the problem to be solved? description: A clear and concise description of what you would like to happen. placeholder: A clear and concise description of what you would like to happen. validations: required: true - type: textarea id: alternatives attributes: label: What alternatives have you considered? description: A clear and concise description of any alternative solutions or features you have considered. placeholder: A clear and concise description of any alternative solutions or features you have considered. validations: required: true - type: textarea id: additional attributes: label: Additional information description: If applicable, add screenshots or other relevant information to help explain your problem. ================================================ FILE: .github/ISSUE_TEMPLATE/2_bug_report.yml ================================================ name: Bug Report description: Something isn't working correctly labels: ["bug"] body: - type: markdown attributes: value: | > [!IMPORTANT] > Before you start, read [the rules](https://github.com/BartoszCichecki/LenovoLegionToolkit/blob/master/CONTRIBUTING.md) for contributing. - type: input id: version attributes: label: Version description: Which Lenovo Legion Toolkit version do you use? placeholder: e.g. 2.14.0 validations: required: true - type: input id: os attributes: label: OS description: Which operating system do you use? placeholder: e.g. Windows 11 21H2 build 22000 validations: required: true - type: input id: device attributes: label: Device description: Which laptop do you use? placeholder: e.g. Legion 5 Pro 16ACH6 validations: required: true - type: input id: bios attributes: label: BIOS version description: Which BIOS version do you have? placeholder: e.g. GKCN57WW validations: required: true - type: textarea id: troubleshoot attributes: label: What is wrong? description: Provide detailed description of what is wrong or does not work as expected. placeholder: Provide detailed description of what is wrong or does not work as expected. validations: required: true - type: textarea id: description attributes: label: What did you try already? description: Provide detailed description of how did you troubleshoot the issue and what have you tried already. placeholder: Provide detailed description of how did you troubleshoot the issue and what have you tried already. validations: required: true - type: textarea id: reproduce attributes: label: How to reproduce the bug? description: Describe how to reproduce the behavior. Be as specific as possible and provide as many details as possible. placeholder: | 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error validations: required: true - type: textarea id: expected-behaviour attributes: label: What is the behavior that you expected? description: Describe what did you expect would happen. placeholder: Describe what did you expect would happen. validations: required: true - type: textarea id: logs attributes: label: Logs placeholder: Please drag and drop log files here. You can check README for instructions on how to collect logs. description: Please drag and drop log files here. You can check README for instructions on how to collect logs. validations: required: true - type: textarea id: additional attributes: label: Additional information description: If applicable, add screenshots or other relevant information to help explain your problem. ================================================ FILE: .github/ISSUE_TEMPLATE/3_compatibility_request.yml ================================================ name: Compatibility Request description: Request support for a device that is not supported labels: ["compatibility"] body: - type: markdown attributes: value: | > [!IMPORTANT] > Before you start, read [the rules](https://github.com/BartoszCichecki/LenovoLegionToolkit/blob/master/CONTRIBUTING.md) for contributing. - type: input id: version attributes: label: Version description: Which Lenovo Legion Toolkit version do you use? placeholder: e.g. 2.14.0 validations: required: true - type: input id: os attributes: label: OS description: Which operating system do you use? placeholder: e.g. Windows 11 21H2 build 22000 validations: required: true - type: input id: device attributes: label: Device description: Which laptop do you use? placeholder: e.g. Legion 5 Pro 16ACH6 validations: required: true - type: input id: bios attributes: label: BIOS version description: Which BIOS version do you have? placeholder: e.g. GKCN57WW validations: required: true - type: textarea id: what-was-test attributes: label: Which features of the application did you test? description: A clear and concise description of features that were tested. placeholder: Type here... validations: required: true - type: textarea id: what-is-broken attributes: label: Which features of the application do not work as expected? description: A clear and concise description of features that do not work as expected. placeholder: Type here... validations: required: true - type: textarea id: logs attributes: label: Logs description: If you encountered problems, please drag and drop log files here. You can check README for instructions on how to collect logs. - type: textarea id: additional attributes: label: Additional information description: If applicable, add screenshots or other relevant information to help explain your problem. ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: false contact_links: - name: General questions url: https://github.com/BartoszCichecki/LenovoLegionToolkit/discussions/new?category=q-a about: Please ask questions here. ================================================ FILE: .github/pull_request_template.md ================================================ ================================================ FILE: .github/workflows/build.yml ================================================ name: Build on: push: branches: [ master ] pull_request: branches: [ master ] jobs: build: runs-on: windows-2022 steps: - name: Checkout uses: actions/checkout@v4 - name: Setup .NET uses: actions/setup-dotnet@v3 with: dotnet-version: 8.0.x - name: Restore run: dotnet restore - name: Build run: .\make.bat - name: Upload artifact uses: actions/upload-artifact@v4 with: name: installer path: build_installer\LenovoLegionToolkitSetup.exe ================================================ FILE: .github/workflows/release.yml ================================================ name: Release on: push: tags: - "[0-9]*.[0-9]*.[0-9]*" jobs: build: runs-on: windows-2022 steps: - name: Checkout uses: actions/checkout@v4 - name: Setup .NET uses: actions/setup-dotnet@v3 with: dotnet-version: 8.0.x - name: Restore run: dotnet restore - name: Build run: .\make.bat ${{ github.ref_name }} - name: Release uses: softprops/action-gh-release@v0.1.13 with: draft: true files: build_installer/LenovoLegionToolkitSetup.exe fail_on_unmatched_files: true - name: Upload artifact uses: actions/upload-artifact@v4 with: name: installer path: build_installer/LenovoLegionToolkitSetup.exe ================================================ FILE: .gitignore ================================================ # Created by https://www.toptal.com/developers/gitignore/api/visualstudio,dotnetcore,csharp # Edit at https://www.toptal.com/developers/gitignore?templates=visualstudio,dotnetcore,csharp ### Csharp ### ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore # User-specific files *.rsuser *.suo *.user *.userosscache *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Mono auto generated files mono_crash.* # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ x64/ x86/ [Ww][Ii][Nn]32/ [Aa][Rr][Mm]/ [Aa][Rr][Mm]64/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ [Ll]ogs/ build/ # Visual Studio 2015/2017 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # Visual Studio 2017 auto generated files Generated\ Files/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUnit *.VisualState.xml TestResult.xml nunit-*.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # Benchmark Results BenchmarkDotNet.Artifacts/ # .NET Core project.lock.json project.fragment.lock.json artifacts/ # ASP.NET Scaffolding ScaffoldingReadMe.txt # StyleCop StyleCopReport.xml # Files built by Visual Studio *_i.c *_p.c *_h.h *.ilk *.meta *.obj *.iobj *.pch *.pdb *.ipdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *_wpftmp.csproj *.log *.tlog *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opendb *.opensdf *.sdf *.cachefile *.VC.db *.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx *.sap # Visual Studio Trace Files *.e2e # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # AxoCover is a Code Coverage Tool .axoCover/* !.axoCover/settings.json # Coverlet is a free, cross platform Code Coverage Tool coverage*.json coverage*.xml coverage*.info # Visual Studio code coverage results *.coverage *.coveragexml # NCrunch _NCrunch_* .*crunch*.local.xml nCrunchTemp_* # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # Note: Comment the next line if you want to checkin your web deploy settings, # but database connection strings (with potential passwords) will be unencrypted *.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to # checkin your Azure Web App publish settings, but sensitive information contained # in these scripts will be unencrypted PublishScripts/ # NuGet Packages *.nupkg # NuGet Symbol Packages *.snupkg # except build/, which is used as an MSBuild target. !**/[Pp]ackages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/[Pp]ackages/repositories.config # NuGet v3's project.json files produces more ignorable files *.nuget.props *.nuget.targets # Nuget personal access tokens and Credentials nuget.config # Microsoft Azure Build Output csx/ *.build.csdef # Microsoft Azure Emulator ecf/ rcf/ # Windows Store app package directories and files AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt *.appx *.appxbundle *.appxupload # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache !?*.[Cc]ache/ # Others ClientBin/ ~$* *~ *.dbmdl *.dbproj.schemaview *.jfm *.pfx *.publishsettings orleans.codegen.cs # Including strong name files can present a security risk # (https://github.com/github/gitignore/pull/2483#issue-259490424) #*.snk # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm ServiceFabricBackup/ *.rptproj.bak # SQL Server files *.mdf *.ldf *.ndf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings *.rptproj.rsuser *- [Bb]ackup.rdl *- [Bb]ackup ([0-9]).rdl *- [Bb]ackup ([0-9][0-9]).rdl # Microsoft Fakes FakesAssemblies/ # GhostDoc plugin setting file *.GhostDoc.xml # Node.js Tools for Visual Studio .ntvs_analysis.dat node_modules/ # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) *.vbw # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml **/*.Server/GeneratedArtifacts **/*.Server/ModelManifest.xml _Pvt_Extensions # Paket dependency manager .paket/paket.exe paket-files/ # FAKE - F# Make .fake/ # CodeRush personal settings .cr/personal # Python Tools for Visual Studio (PTVS) __pycache__/ *.pyc # Cake - Uncomment if you are using it # tools/** # !tools/packages.config # Tabs Studio *.tss # Telerik's JustMock configuration file *.jmconfig # BizTalk build output *.btp.cs *.btm.cs *.odx.cs *.xsd.cs # OpenCover UI analysis results OpenCover/ # Azure Stream Analytics local run output ASALocalRun/ # MSBuild Binary and Structured Log *.binlog # NVidia Nsight GPU debugger configuration file *.nvuser # MFractors (Xamarin productivity tool) working folder .mfractor/ # Local History for Visual Studio .localhistory/ # BeatPulse healthcheck temp database healthchecksdb # Backup folder for Package Reference Convert tool in Visual Studio 2017 MigrationBackup/ # Ionide (cross platform F# VS Code tools) working folder .ionide/ # Fody - auto-generated XML schema FodyWeavers.xsd # VS Code files for those working on multiple tools .vscode/* !.vscode/settings.json !.vscode/tasks.json !.vscode/launch.json !.vscode/extensions.json *.code-workspace # Local History for Visual Studio Code .history/ # Windows Installer files from build outputs *.cab *.msi *.msix *.msm *.msp # JetBrains Rider .idea/ *.sln.iml ### DotnetCore ### # .NET Core build folders bin/ obj/ # Common node modules locations /node_modules /wwwroot/node_modules ### VisualStudio ### # User-specific files # User-specific files (MonoDevelop/Xamarin Studio) # Mono auto generated files # Build results # Visual Studio 2015/2017 cache/options directory # Uncomment if you have tasks that create the project's static files in wwwroot # Visual Studio 2017 auto generated files # MSTest test Results # NUnit # Build Results of an ATL Project # Benchmark Results # .NET Core # ASP.NET Scaffolding # StyleCop # Files built by Visual Studio # Chutzpah Test files # Visual C++ cache files # Visual Studio profiler # Visual Studio Trace Files # TFS 2012 Local Workspace # Guidance Automation Toolkit # ReSharper is a .NET coding add-in # TeamCity is a build add-in # DotCover is a Code Coverage Tool # AxoCover is a Code Coverage Tool # Coverlet is a free, cross platform Code Coverage Tool # Visual Studio code coverage results # NCrunch # MightyMoose # Web workbench (sass) # Installshield output folder # DocProject is a documentation generator add-in # Click-Once directory # Publish Web Output # Note: Comment the next line if you want to checkin your web deploy settings, # but database connection strings (with potential passwords) will be unencrypted # Microsoft Azure Web App publish settings. Comment the next line if you want to # checkin your Azure Web App publish settings, but sensitive information contained # in these scripts will be unencrypted # NuGet Packages # NuGet Symbol Packages # The packages folder can be ignored because of Package Restore # except build/, which is used as an MSBuild target. # Uncomment if necessary however generally it will be regenerated when needed # NuGet v3's project.json files produces more ignorable files # Nuget personal access tokens and Credentials # Microsoft Azure Build Output # Microsoft Azure Emulator # Windows Store app package directories and files # Visual Studio cache files # files ending in .cache can be ignored # but keep track of directories ending in .cache # Others # Including strong name files can present a security risk # (https://github.com/github/gitignore/pull/2483#issue-259490424) # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) # RIA/Silverlight projects # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) # SQL Server files # Business Intelligence projects # Microsoft Fakes # GhostDoc plugin setting file # Node.js Tools for Visual Studio # Visual Studio 6 build log # Visual Studio 6 workspace options file # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) # Visual Studio LightSwitch build output # Paket dependency manager # FAKE - F# Make # CodeRush personal settings # Python Tools for Visual Studio (PTVS) # Cake - Uncomment if you are using it # tools/** # !tools/packages.config # Tabs Studio # Telerik's JustMock configuration file # BizTalk build output # OpenCover UI analysis results # Azure Stream Analytics local run output # MSBuild Binary and Structured Log # NVidia Nsight GPU debugger configuration file # MFractors (Xamarin productivity tool) working folder # Local History for Visual Studio # BeatPulse healthcheck temp database # Backup folder for Package Reference Convert tool in Visual Studio 2017 # Ionide (cross platform F# VS Code tools) working folder # Fody - auto-generated XML schema # VS Code files for those working on multiple tools # Local History for Visual Studio Code # Windows Installer files from build outputs # JetBrains Rider ### VisualStudio Patch ### # Additional files built by Visual Studio # End of https://www.toptal.com/developers/gitignore/api/visualstudio,dotnetcore,csharp build_installer ================================================ FILE: CONTRIBUTING.md ================================================ ## Welcome to Lenvo Legion Toolkit contributing guide! ### Other language versions of this contributing guide: * [简体中文版开发者指南](CONTRIBUTING_zh-hans.md) * [日本語版の貢献ガイド](CONTRIBUTING_ja-JP.md) Thanks for investing your time in contributing to this project! Giving the growing popularity of LLT, here are a few rules to follow to ensure that your contribution goes smoothly.
_Due to large number of issues created, those that do not meet the criteria will be deleted without warning. Repeating offenders will be banned._
**1. Before reporting an issue make yourself familiar with the README** [README](https://github.com/BartoszCichecki/LenovoLegionToolkit/blob/master/README.md) is regularly updated to include answers to frequently asked questions as well as information about most common issues. Take your time to go through what is there before creating an issue or starting a discussion. **2. Check already reported issues** Go through [issues](https://github.com/BartoszCichecki/LenovoLegionToolkit/issues?q=is%3Aissue) that were already reported, as well as [discussions](https://github.com/BartoszCichecki/LenovoLegionToolkit/discussions?discussions_q=). Do not create duplicate issues or discussions. Even if the issue is marked as closed, you can still leave a comment there. **3. Use English** This makes it easier for everyone to follow the conversation. **4. Respect scope of the project** This is not meant to be a do-it-all type of application. The vision for the project is clear: provide a replacement of Vantage for Legion laptops. Do not request support for other types/models/etc devices. **5. Verify your problem before creating an issue** Make sure that a bug is really a bug in LLT - this isn't a free system troubleshooting forum. If you use modified version of Windows or your Windows is acting funny, that's on you. **6. Describe your problem as best as you can** Providing good description is key. Fill out all the fields of the form when creating an issue, including logs. The better the description, the higher the chance that we will understand each other correctly. **7. Give a good title to issues and discussions** It is a lot easier to browse the list and follow issues and discussions. "Error when using LLT" is not a good title. **8. Stay on topic** Do not leave unnecessary comments or spam. **9. One problem per issue** Do not report many problems or request many features withing one issue. Make one issue or discussion per problem/topic/idea. This makes it easier to follow. **10. Translations** Translation contributions are done using [Crowdin](https://crowdin.com/project/llt), so please request access to the project there if you want to contribute. **11. Pull requests** Pull requests are welcome (of course). Unless you create a very simple and understandable PR, make an issue first and describe the problem you are solving. It doesn't make sense to spend time working on an idea that will be rejected, because it doesn't fit the project vision. Follow the code style and architecture of the project.
Once again, thanks for investing your time in helping LLT get better! ================================================ FILE: CONTRIBUTING_ja-JP.md ================================================ ## Lenovo Legion Toolkit への貢献ガイドへようこそ! ### この貢献ガイドの他の言語バージョン: * [英語版の貢献ガイド](CONTRIBUTING.md) * [簡体字中国語版の貢献ガイド](CONTRIBUTING_zh-hans.md) このプロジェクトに貢献するために時間を割いていただきありがとうございます! LLTの人気が高まっているため、貢献がスムーズに進むようにするために、いくつかのルールに従ってください。
_作成された問題の数が多いため、基準を満たさないものは警告なしに削除されます。繰り返し違反する者は禁止されます。_
**1. 問題を報告する前にREADMEをよく読んでください** [README](https://github.com/BartoszCichecki/LenovoLegionToolkit/blob/master/README.md)は、よくある質問への回答や最も一般的な問題に関する情報を���むように定期的に更新されます。問題を作成する前に、そこにある内容を確認してください。 **2. 既に報告されている問題を確認してください** 既に報告されている[問題](https://github.com/BartoszCichecki/LenovoLegionToolkit/issues?q=is%3Aissue)や[ディスカッション](https://github.com/BartoszCichecki/LenovoLegionToolkit/discussions?discussions_q=)を確認してください。重複する問題やディスカッションを作成しないでください。問題がクローズされていても、コメントを残すことができます。 **3. 英語を使用してください** これにより、すべての人が会話を追跡しやすくなります。 **4. プロジェクトの範囲を尊重してください** これは万能のアプリケーションを目指しているわけではありません。プロジェクトのビジョンは明確です:LegionノートパソコンのためのVantageの代替品を提供すること。他のタイプ/モデル/デバイスのサポートを要求しないでください。 **5. 問題を作成する前に問題を確認してください** バグが本当にLLTのバグであることを確認してください - これは無料のシステムトラブルシューティングフォーラムではありません。Windowsの改造版を使用している場合や、Windowsが正常に動作していない場合、それはあなたの責任です。 **6. 問題をできるだけ詳しく説明してください** 良い説明を提供することが重要です。問題を作成する際には、フォームのすべてのフィールドを記入し、ログを含めてください。説明が良ければ良いほど、私たちが正しく理解する可能性が高くなります。 **7. 問題やディスカッションに良いタイトルを付けてください** リストを閲覧し、問題やディスカッションを追跡するのがはるかに簡単です。「LLTを使用中にエラーが発生しました」は良いタイトルではありません。 **8. トピックに集中してください** 不要なコメントやスパムを残さないでください。 **9. 1つの問題につき1つの問題** 1つの問題で多くの問題を報告したり、多くの機能を要求したりしないでください。問題/トピック/アイデアごとに1つの問題やディスカッションを作成してください。これにより、追跡が容易になります。 **10. 翻訳** 翻訳の貢献は[Crowdin](https://crowdin.com/project/llt)を使用して行われるため、貢献したい場合はそこでプロジェクトへのアクセスをリクエストしてください。 **11. プルリクエスト** プルリクエストは歓迎されます(もちろんです)。非常にシンプルで理解しやすいPRを作成しない限り、最初に問題を作成し、解決している問題を説明してください。プロジェクトのビジョンに合わないアイデアに時間を費やすのは意味がありません。プロジェクトのコードスタイルとアーキテクチャに従ってください。
もう一度、LLTをより良くするために時間を割いていただきありがとうございます! ================================================ FILE: CONTRIBUTING_zh-hans.md ================================================ ## 欢迎来到拯救者工具箱开发者指南 首先感谢你花时间为此项目做出贡献!随着拯救者工具箱的热度越来越高,为了确保你的贡献能够被迅速采纳,你应该遵守一定的格式和规则。
_由于 Issues 总量的增加,不符合标准的 Issue 会在无预先警告的情况下被关闭或删除。屡次违反者将被本项目封禁。_
**1. 在报告 Issue 前请仔细阅读 README** 绝大多数常见问题的解决方法和重要信息都已在 [README](https://github.com/BartoszCichecki/LenovoLegionToolkit/blob/master/README_zh-hans.md) 内阐明。请务必在报告 Issue 或发起讨论前通读其中的内容。 **2. 检查已被报告的 Issues** 请检查项目仓库下的 [Issues](https://github.com/BartoszCichecki/LenovoLegionToolkit/issues?q=is%3Aissue) 和 [Discussions](https://github.com/BartoszCichecki/LenovoLegionToolkit/discussions?discussions_q=) 栏目。请不要报告重复的 Issue 或发起重复的讨论。即使你找到的 Issue 已经被关闭,你一样可以在那里留言。 **3. 使用英语** 这会让所有人之间的交流都更加便利。 译者提示:若你无法流畅地使用英语表达,你可以在使用中文完成草稿后使用百度翻译或 [DeepL](https://www.deepl.com/zh/translator) 等翻译网站或软件将草稿翻译为英语后提交。 **4. 尊重项目目标** 这不是一个万能的应用。项目的愿景很明确:为拯救者笔记本提供一个 Legion Zone(海外版则为 Vantage)的替代品。请勿要求支持其它类型或型号的设备。 **5. 在新建 Issue 前审查你的问题** 请确保 Bug 确实是 LLT 的 Bug。这不是一个免费的系统故障排除论坛,如果你在使用被修改过的 Windows 版本或你的系统本身已经出现问题,请自行解决。 **6. 尽所能详细描述你的问题** 详细的描述是解决问题的关键所在。请在新建 Issue 时填写表单内的所有项目并提供日志文件。只有提供良好的描述我们才能更快地解决问题。 **7. 为你的 Issue 或条论起一个好的标题** 这样可以极大方便浏览 Issue 和讨论列表。“使用 LLT 时出现问题”并不是一个好的标题。 **8. 围绕主题** 不要发表与主题无关或无意义的留言。 **9. 一个 Issue 一个问题** 请不要在一个 Issue 内同时报告多个问题或请求添加多个功能。请为每一个问题、主题或想法新建一个单独的 Issue 或讨论,这会让后期跟进更加容易。 **10. 翻译** 我们使用 [Crowdin](https://crowdin.com/project/llt) 作为软件翻译平台。如果你想为翻译做出贡献,请在那里申请访问项目的权限。 **11. Pull requests** 我们欢迎你提交 PR(当然了)。除非你提交了一个非常简单易懂的 PR,请先创建一个 Issue 并描述你正在解决的问题。为一个会被拒绝的点子花时间并没有什么意义,因为这不符合本项目的愿景。同时请遵循现有的代码风格和项目组织。
再次感谢你花时间帮助 LLT 变得更好! ================================================ FILE: InnoDependencies/Arabic.isl ================================================ ; *** Inno Setup version 6.1.0+ arabic messages *** ; ; Translated by nacer baaziz (nacerstile@gmail.com) ; http://www.jrsoftware.org/files/istrans/ ; ; Note: When translating this text, do not add periods (.) to the end of ; messages that didn't have them already, because on those messages Inno ; Setup adds the periods automatically (appending a period would result in ; two periods being displayed). [LangOptions] ; The following three entries are very important. Be sure to read and ; understand the '[LangOptions] section' topic in the help file. LanguageName=arabic LanguageID=$0401 LanguageCodePage=0 RightToLeft=yes ; If the language you are translating to requires special font faces or ; sizes, uncomment any of the following entries and change them accordingly. ;DialogFontName= ;DialogFontSize=8 ;WelcomeFontName=Verdana ;WelcomeFontSize=12 ;TitleFontName=Arial ;TitleFontSize=29 ;CopyrightFontName=Arial ;CopyrightFontSize=8 [Messages] ; *** Application titles SetupAppTitle=إعداد SetupWindowTitle=إعداد - %1 UninstallAppTitle=إزالة التثبيت UninstallAppFullTitle=إزالة تثبيت %1 ; *** Misc. common InformationTitle=معلومات ConfirmTitle=تأكيد ErrorTitle=خطأ ; *** SetupLdr messages SetupLdrStartupMessage=هذا المعالج سيقوم بتثبيت %1. هل تريد المتابعة? LdrCannotCreateTemp=تعذر إنشاء الملفات المؤقتة, تم فشل معالج التثبيت. LdrCannotExecTemp=تعذر تشغيل الملفات من المجلد المؤقت. فشل معالج التثبيت. HelpTextNote= ; *** Startup error messages LastErrorMessage=%1.%n%n خطأ %2: %3 SetupFileMissing=الملف %1 مفقود من دليل التثبيت. الرجاء تصحيح المشكلة أو الحصول على نسخة جديدة من البرنامج. SetupFileCorrupt=ملفات الإعداد تالفة. الرجاء الحصول على نسخة جديدة من البرنامج. SetupFileCorruptOrWrongVer=ملفات الإعداد تالفة أو غير متوافقة مع هذا الإصدار من برنامج الإعداد. الرجاء تصحيح المشكلة أو الحصول على نسخة جديدة من البرنامج. InvalidParameter=تم تمرير أوامر غير صالحة على سطر الأوامر : %n%n%1 SetupAlreadyRunning=برنامج الإعداد قيد التشغيل بالفعل. WindowsVersionNotSupported=لا يدعم هذا البرنامج إصدار Windows الذي يعمل به الكمبيوتر. WindowsServicePackRequired=هذا البرنامج يتطلب %1 حزمة الخدمة %2 أو أعلى. NotOnThisPlatform=لن يتم تشغيل هذا البرنامج على %1. OnlyOnThisPlatform=يجب تشغيل هذا البرنامج على %1. OnlyOnTheseArchitectures=يمكن تثبيت هذا البرنامج فقط على إصدارات Windows المصممة لهندسة المعالج التالية : %n%n%1 WinVersionTooLowError=هذا البرنامج يتطلب %1 الإصدار %2 أو أعلى. WinVersionTooHighError=لا يمكن تثبيت هذا البرنامج على %1 الإصدار %2 أو أعلى. AdminPrivilegesRequired=يجب أن يتم تسجيل دخولك كمسؤول عند تثبيت هذا البرنامج. PowerUserPrivilegesRequired=يجب أن يتم تسجيل دخولك كمسؤول أو كعضو في مجموعة Power Users عند تثبيت هذا البرنامج. SetupAppRunningError=لقد كشف معالج الإعداد أن %1 يعمل بالفعل. %n%n يرجى إغلاق كل أجزائه الآن , ثم إضغط حسنا للمتابعة أو إلغاء الأمر للخروج. UninstallAppRunningError=كشف معالج إلغاء التثبيت بأن %1 يعمل بالفعل.%n%n يرجى إغلاق كل أجزائه الآن , ثم إضغط حسنا للمتابعة أو إلغاء الأمر للخروج. ; *** Startup questions PrivilegesRequiredOverrideTitle=تحديد وضع تثبيت الإعداد PrivilegesRequiredOverrideInstruction=تحديد وضع التثبيت PrivilegesRequiredOverrideText1=يمكن ل %1 أن يُثَبَّت على جميع المستخدمين (يتطلب إمتيازات المسؤول), أو لك فقط.. PrivilegesRequiredOverrideText2=.يمكن ل %1 أن يُثَبَّت لك فقط, أو أن يُثَبَّت على جميع المستخدمين (يتطلب إمتيازات المسؤول). PrivilegesRequiredOverrideAllUsers=التثبيت ل&كافة المستخدمين PrivilegesRequiredOverrideAllUsersRecommended=تثبيت ل&كافة المستخدمين (مستحسن) PrivilegesRequiredOverrideCurrentUser=تثبيت لي &فقط PrivilegesRequiredOverrideCurrentUserRecommended=تثبيت بالنسبة لي &فقط (مستحسن) ; *** Misc. errors ErrorCreatingDir=تعذر على برنامج الإعداد إنشاء الدليل "%1" ErrorTooManyFilesInDir=تعذر إنشاء ملف في الدليل "%1" لأنه يحتوي على ملفات كثيرة جداً ; *** Setup common messages ExitSetupTitle=الخروج من معالج التثبيت ExitSetupMessage=لم يكتمل الإعداد. إذا قمت بالخروج الآن، لن يتم تثبيت البرنامج.%n%nYou يمكنك تشغيل برنامج الإعداد مرة أخرى في وقت آخر لإكمال التثبيت.%n%n إنهاء الإعداد؟ AboutSetupMenuItem=&حول الإعداد... AboutSetupTitle=حول برنامج الإعداد AboutSetupMessage=%1 الإصدار %2%n%3%n%n%1 صفحة الأنترنت:%n%4 AboutSetupNote= TranslatorNote=تم ترجمة المعالج إلى اللغة العربية بواسطة ناصر بعزيز ; *** Buttons ButtonBack=< ال&سابق ButtonNext=ال&تالي > ButtonInstall=&تثبيت ButtonOK=&حسنا ButtonCancel=إل&غاء الأمر ButtonYes=&نعم ButtonYesToAll=نعم لل&كل ButtonNo=&لا ButtonNoToAll=لا &للكل ButtonFinish=إ&نهاء ButtonBrowse=اس&تعراض... ButtonWizardBrowse=اس&تعراض... ButtonNewFolder=إن&شاء مجلد جديد ; *** "Select Language" dialog messages SelectLanguageTitle=إختر لغة معالج الإعداد SelectLanguageLabel=حدد اللغة التي يجب استخدامها أثناء التثبيت. ; *** Common wizard text ClickNext=انقر فوق التالي للمتابعة، أو إلغاء الأمر لإنهاء الإعداد. BeveledLabel= BrowseDialogTitle=تصفح لاختيار مجلد BrowseDialogLabel=حدد مجلدًا في القائمة أدناه، ثم انقر فوق حسنا. NewFolderName=مجلد جديد ; *** "Welcome" wizard page WelcomeLabel1=مرحبا بكم في معالج تثبيت [name] WelcomeLabel2=هذا المعالج سيقوم بتثبيت [name/ver] على جهازك. %n%nمن المستحسن أن تقوم بإغلاق كافة التطبيقات الأخرى قبل المتابعة. ; *** "Password" wizard page WizardPassword=كلمة السر PasswordLabel1=هذا التثبيت محمي بكلمة سر. PasswordLabel3=الرجاء تقديم كلمة المرور، ثم انقر فوق التالي للمتابعة. كلمات المرور حساسة لحالة الأحرف. PasswordEditLabel=&كلمة السر: IncorrectPassword=كلمة السر التي أدخلتها غير صحيحة. يرجى إعادة المحاولة. ; *** "License Agreement" wizard page WizardLicense=اتفاقية الترخيص LicenseLabel=يرجى قراءة المعلومات الهامة التالية قبل المتابعة. LicenseLabel3=الرجاء قراءة اتفاقية الترخيص التالية. يجب قبول شروط هذه الاتفاقية قبل متابعة التثبيت. LicenseAccepted=أنا أواف&ق على هذه الإتفاقية LicenseNotAccepted=أنا &لا أوافق على الإتفاقية ; *** "Information" wizard pages WizardInfoBefore=معلومات InfoBeforeLabel=يرجى قراءة المعلومات الهامة التالية قبل المتابعة. InfoBeforeClickLabel=عندما تكون جاهزًا للمتابعة مع الإعداد، انقر فوق التالي. WizardInfoAfter=معلومات InfoAfterLabel=يرجى قراءة المعلومات الهامة التالية قبل المتابعة. InfoAfterClickLabel=عندما تكون جاهزًا للمتابعة مع الإعداد، انقر فوق التالي. ; *** "User Information" wizard page WizardUserInfo=معلومات المستخدم UserInfoDesc=يرجى إدخال معلوماتك. UserInfoName=إسم ال&مستخدم : UserInfoOrg=المن&ظمة: UserInfoSerial=&الرقم التسلسلي: UserInfoNameRequired=يجب إدخال إسم. ; *** "Select Destination Location" wizard page WizardSelectDir=تحديد موقع الوِجْهة SelectDirDesc=أين يجب تثبيت [name]؟ SelectDirLabel3=سيقوم معالج التثبيت بتثبيت [name] في المجلد التالي. SelectDirBrowseLabel=للمتابعة، انقر فوق التالي. إذا كنت ترغب في تحديد مجلد آخر، انقر فوق استعراض. DiskSpaceGBLabel=تحتاج على الأقل [gb] GB من المساحة لتثبيت البرنامج. DiskSpaceMBLabel=تحتاج على الأقل [mb] MB من المساحة لتثبيت البرنامج. CannotInstallToNetworkDrive=يتعذر على برنامج الإعداد التثبيت على محرك أقراص شبكة اتصال. CannotInstallToUNCPath=يتعذر على برنامج الإعداد تثبيت مسار UNC. InvalidPath=يجب إدخال مسار كامل مع حرف محرك الأقراص; على سبيل المثال: %n%nC:\APP%n%أو مسار UNC في النموذج:%n%n\\server\share InvalidDrive=محرك الأقراص أو مشاركة UNC التي حددتها غير موجود أو غير قابل للوصول. الرجاء تحديد آخر. DiskSpaceWarningTitle=مساحة القرص غير كافية DiskSpaceWarning=Sيتطلب الإعداد على الأقل %1 KB من المساحة الفارغة للتثبيت، ولكن محرك الأقراص المحدد فيه فقط %2 KB متوفرة.%n%nهل تريد المتابعة على أية حال؟ DirNameTooLong=اسم المجلد أو المسار طويل جداً. InvalidDirName=اسم المجلد غير صالح. BadDirName32=لا يمكن لأسماء المجلدات تضمين أي من الأحرف التالية:%n%n%1 DirExistsTitle=المجلد موجود بالفعل DirExists=المجلد:%n%n%1%n%n موجود بالفعل. هل ترغب في التثبيت على هذا المجلد على أي حال؟ DirDoesntExistTitle=المجلد غير موجود DirDoesntExist=المجلد:%n%n%1%n%nغير موجود. هل تريد إنشاء المجلد؟ ; *** "Select Components" wizard page WizardSelectComponents=تحديد المكونات SelectComponentsDesc=ما هي المكونات التي يجب تثبيتها؟ SelectComponentsLabel2=حدد المكونات التي تريد تثبيتها ؛ امسح المكونات التي لا تريد تثبيتها. انقر فوق "التالي" عندما تكون مستعدًا للمتابعة. FullInstallation=تثبيت كامل ; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language) CompactInstallation=تثبيت مدمج CustomInstallation=تثبيت مخصص NoUninstallWarningTitle=مكونات موجودة NoUninstallWarning=اكتشف برنامج الإعداد أن المكونات التالية مثبتة بالفعل على جهاز الكمبيوتر الخاص بك: %n%n%1%n%nلن يؤدي إلغاء تحديد هذه المكونات إلى إزالة تثبيتها.%n%nهل ترغب في الاستمرار على أي حال? ComponentSize1=%1 KB ComponentSize2=%1 MB ComponentsDiskSpaceGBLabel=الاختيار الحالي يتطلب على الأقل [gb] GB من مساحة القرص. ComponentsDiskSpaceMBLabel=الاختيار الحالي يتطلب على الأقل [mb] MB من مساحة القرص. ; *** "Select Additional Tasks" wizard page WizardSelectTasks=حدد المهام الإضافية SelectTasksDesc=ما المهام الإضافية التي ينبغي تنفيذها؟ SelectTasksLabel2=حدد المهام الإضافية التي ترغب في أن يقوم الإعداد بتنفيذها أثناء تثبيت [name], ثم إضغط التالي. ; *** "Select Start Menu Folder" wizard page WizardSelectProgramGroup=حدد مجلد قائمة ابدأ SelectStartMenuFolderDesc=أين يجب أن يضع الإعداد اختصارات البرنامج؟ SelectStartMenuFolderLabel3=سيقوم برنامج الإعداد بإنشاء اختصارات البرنامج في مجلد قائمة ابدأ التالية. SelectStartMenuFolderBrowseLabel=للمتابعة، انقر فوق التالي. إذا كنت ترغب في تحديد مجلد آخر، انقر فوق استعراض. MustEnterGroupName=يجب إدخال اسم مجلد. GroupNameTooLong=اسم المجلد أو المسار طويل جداً. InvalidGroupName=اسم المجلد غير صالح. BadGroupName=لا يمكن أن يتضمن اسم المجلد أي من الأحرف التالية:%n%n%1 NoProgramGroupCheck2=&عدم إنشاء مجلد قائمة ابدأ ; *** "Ready to Install" wizard page WizardReady=جاهز للتثبيت ReadyLabel1=الإعداد جاهز الآن لبدء تثبيت [name] على جهازك. ReadyLabel2a=انقر فوق تثبيت لمتابعة التثبيت، أو انقر فوق "السابق" إذا كنت ترغب في مراجعة أو تغيير أية إعدادات. ReadyLabel2b=انقر فوق تثبيت لمتابعة التثبيت. ReadyMemoUserInfo=معلومات المستخدم: ReadyMemoDir=مسار الوِجْهة: ReadyMemoType=نوع الإعداد: ReadyMemoComponents=المكونات المحددة: ReadyMemoGroup=مجلد قائمة ابدأ: ReadyMemoTasks=مهام إضافية: ; *** TDownloadWizardPage wizard page and DownloadTemporaryFile DownloadingLabel=تحميل الملفات الإضافية... ButtonStopDownload=إي&قاف التحميل StopDownload=هل أنت متأكد من أنك ترغب في إيقاف التحميل؟ ErrorDownloadAborted=تم إلغاء التحميل ErrorDownloadFailed=فشل التحميل: %1 %2 ErrorDownloadSizeFailed=خطأ في قراءة الحجم: %1 %2 ErrorFileHash1=خطأ في قراءة الهاش الخاص بالملف: %1 ErrorFileHash2=خطأ في هاش الملف: كان من المتوقع أن يكن : %1, بينما تم إيجاد : %2 ErrorProgress=خطأ في الحصول على نسبة التقدم: %1 من %2 ErrorFileSize=خطأ في حجم الملف: المتوقع هو : %1, الحجم الذي وجدناه هو : %2 ; *** "Preparing to Install" wizard page WizardPreparing=التحضير للتثبيت PreparingDesc=الإعداد يستعد لتثبيت [name] على جهازك. PreviousInstallNotCompleted=لم يكتمل التثبيت / إزالة البرنامج السابق. ستحتاج إلى إعادة تشغيل الكمبيوتر لإكمال هذا التثبيت.%n%nبعد إعادة تشغيل جهاز الكمبيوتر الخاص بك ، شغّل برنامج الإعداد مرة أخرى لإكمال تثبيت [name]. CannotContinue=لا يمكن لبرنامج الإعداد المتابعة. يرجى النقر فوق "إلغاء" للخروج. ApplicationsFound=تستخدم التطبيقات التالية الملفات التي تحتاج إلى تحديث بواسطة برنامج الإعداد. يوصى بالسماح لبرنامج الإعداد بإغلاق هذه التطبيقات تلقائيًا. ApplicationsFound2=تستخدم التطبيقات التالية الملفات التي تحتاج إلى تحديث بواسطة برنامج الإعداد. يوصى بالسماح لبرنامج الإعداد بإغلاق هذه التطبيقات تلقائيًا. بعد اكتمال التثبيت ، سيحاول برنامج الإعداد إعادة تشغيل التطبيقات. CloseApplications=أغلق التطبيقات &تلقائيًا DontCloseApplications=&لا تغلق التطبيقات ErrorCloseApplications=لم يتمكن الإعداد من إغلاق جميع التطبيقات تلقائيًا. يوصى بإغلاق جميع التطبيقات التي تستخدم الملفات التي تحتاج إلى تحديث بواسطة برنامج الإعداد قبل المتابعة. PrepareToInstallNeedsRestart=برنامج الإعداد يجب أن يقوم بإعادة تشغيل الجهاز. بعد إعادة تشغيل جهازك, قم بتشغيل برنامج الإعداد مرة أخرى لإكمال تثبيت [name].%n%nهل تحب إعادة التشغيل الآن? ; *** "Installing" wizard page WizardInstalling=جاري التثبيت InstallingLabel=يرجى الانتظار حتى يقوم برنامج الإعداد بتثبي [name] على جهازك. ; *** "Setup Completed" wizard page FinishedHeadingLabel=إنهاء معالج تثبيت [name] FinishedLabelNoIcons=إكتمل معالج التثبيت من تثبيت [name] على جهازك. FinishedLabel=اكتمل معالج التثبيت من تثبيت [name] على جهازك. قد يتم تشغيل التطبيق عن طريق تحديد الاختصارات المثبتة. ClickFinish=إضغط إنهاء للخروج من معالج التثبيت FinishedRestartLabel=لاستكمال تثبيت [name], يجب على برنامج الإعداد إعادة تشغيل جهاز الكمبيوتر الخاص بك. هل ترغب في إعادة التشغيل الآن؟ FinishedRestartMessage=لاستكمال تثبيت [name], يجب على برنامج الإعداد إعادة تشغيل جهاز الكمبيوتر الخاص بك.%n%nهل ترغب في إعادة التشغيل الآن؟ ShowReadmeCheck=نعم ، أرغب عرض ملف README YesRadio=&نعم أعد تشغيل الكومبيوتر الان NoRadio=&لا ، سأعيد تشغيل الكمبيوتر لاحقًا ; used for example as 'Run MyProg.exe' RunEntryExec=تشغيل %1 ; used for example as 'View Readme.txt' RunEntryShellExec=عرض %1 ; *** "Setup Needs the Next Disk" stuff ChangeDiskTitle=يحتاج برنامج الإعداد إلى القرص التالي SelectDiskLabel2=الرجاء إدراج القرص %1 وانقر فوق حسنا.%n%nإذا كان يمكن العثور على الملفات الموجودة على هذا القرص في مجلد غير الذي يظهر أدناه، أدخل المسار الصحيح أو انقر فوق استعراض. PathLabel=&مسار : FileNotInDir2=لم نتمكن من العثور على الملف "%1" في "%2". الرجاء إدراج القرص الصحيح أو تحديد مجلد آخر. SelectDirectoryLabel=الرجاء تحديد موقع القرص التالي. ; *** Installation phase messages SetupAborted=لم يتم إكمال الإعداد. %n%nالرجاء تصحيح المشكلة وتشغيل الإعداد مرة أخرى. AbortRetryIgnoreSelectAction=حدد إجراء AbortRetryIgnoreRetry=أ&عد مجددا AbortRetryIgnoreIgnore=&تجاهل الخطأ والمتابعة AbortRetryIgnoreCancel=إلغاء التثبيت ; *** Installation status messages StatusClosingApplications=إغلاق التطبيقات... StatusCreateDirs=إنشاء المجلدات... StatusExtractFiles=استخراج الملفات... StatusCreateIcons=إنشاء الإختصارات... StatusCreateIniEntries=إنشاء مدخلات INI... StatusCreateRegistryEntries=إنشاء مفاتيح السجل... StatusRegisterFiles=تسجيل الملفات... StatusSavingUninstall=تسجيل معلومات إزالة التثبيت... StatusRunProgram=الإنتهاء من التثبيت... StatusRestartingApplications=إعادة تشغيل التطبيقات... StatusRollback=التراجع عن التغييرات... ; *** Misc. errors ErrorInternal2=خطأ داخلي: %1 ErrorFunctionFailedNoCode=فشل %1 ErrorFunctionFailed=فشل %1; رقم الخطء %2 ErrorFunctionFailedWithMessage=فشل %1; رقم الخطء %2.%n%3 ErrorExecutingProgram=الإعداد غير قابل على تشغيل الملف:%n%1 ; *** Registry errors ErrorRegOpenKey=خطأ في فتح مفتاح التسجيل:%n%1\%2 ErrorRegCreateKey=خطأ في إنشاء مفتاح التسجيل:%n%1\%2 ErrorRegWriteKey=خطأ في الكتابة على مفتاح التسجيل:%n%1\%2 ; *** INI errors ErrorIniEntry=حدث خطأ في إنشاء إدخال INI في الملف "%1". ; *** File copying errors FileAbortRetryIgnoreSkipNotRecommended=&تخطي هذا الملف (غير مستحسن) FileAbortRetryIgnoreIgnoreNotRecommended=&تجاهل الخطأ والمتابعة (غير مستحسن) SourceIsCorrupted=الملف المصدر تالف SourceDoesntExist=الملف "%1"غير موجود ExistingFileReadOnly2=تعذر استبدال الملف الموجود لأنه تم وضع علامة للقراءة فقط. ExistingFileReadOnlyRetry=&أزل القراءة فقط عن الملفات ثم حاول مرة أخرى ExistingFileReadOnlyKeepExisting=&إحتفظ بالملفات الموجودة ErrorReadingExistingDest=حدث خطأ أثناء محاولة قراءة الملف الموجود: FileExistsSelectAction=اختر إجراء FileExists2=الملف موجود بالفعل. FileExistsOverwriteExisting=&استبدال الملف الموجود FileExistsKeepExisting=ا&بقاء الملف الموجود FileExistsOverwriteOrKeepAll=ا&فعل هذا للنزاعات القادمة ExistingFileNewerSelectAction=اختر إجراء ExistingFileNewer2=الملف الموجود أحدث من الملف الذي سيقوم معالج الإعداد بتثبيته. ExistingFileNewerOverwriteExisting=&&استبدال الملف الموجود ExistingFileNewerKeepExisting=ال&ابقاء على الملف الموجود (مستحسن) ExistingFileNewerOverwriteOrKeepAll=ا&فعل هذا مع النزاعات القادمة ErrorChangingAttr=حدث خطأ أثناء محاولة تغيير سمات الملف الموجود: ErrorCreatingTemp=حدث خطأ أثناء محاولة إنشاء ملف في الدليل الوجهة: ErrorReadingSource=حدث خطأ أثناء محاولة قراءة ملف مصدر: ErrorCopying=حدث خطأ أثناء محاولة نسخ ملف: ErrorReplacingExistingFile=حدث خطأ أثناء محاولة استبدال الملف الموجود: ErrorRestartReplace=فشل إعادة تشغيل "استبدال": ErrorRenamingTemp=حدث خطأ أثناء محاولة إعادة تسمية ملف في الدليل الوجهة: ErrorRegisterServer=تعذر تسجيل ملفات DLL/OCX: %1 ErrorRegSvr32Failed=فشل RegSvr32 مع رمز الخروج %1 ErrorRegisterTypeLib=الإعداد غير قادر على تسجيل مكتبة النوع: %1 ; *** Uninstall display name markings ; used for example as 'My Program (32-bit)' UninstallDisplayNameMark=%1 (%2) ; used for example as 'My Program (32-bit, All users)' UninstallDisplayNameMarks=%1 (%2, %3) UninstallDisplayNameMark32Bit=32-bit UninstallDisplayNameMark64Bit=64-bit UninstallDisplayNameMarkAllUsers=كافة المستخدمين UninstallDisplayNameMarkCurrentUser=المستخدم الحالي ; *** Post-installation errors ErrorOpeningReadme=حدث خطأ أثناء محاولة فتح ملف إقرأني. ErrorRestartingComputer=لم يتمكن برنامج الإعداد من إعادة تشغيل الكمبيوتر. الرجاء القيام بذلك يدوياً. ; *** Uninstaller messages UninstallNotFound=الملف "%1" غير موجود. لا يمكن إزالة التثبيت. UninstallOpenError=تعذر فتح "%1". لا يمكن إزالة التثبيت. UninstallUnsupportedVer=ملف سجل الإزالة "%1" في تنسيق غير معروف من قبل هذا الإصدار من برنامج إلغاء التثبيت. لا يمكن إزالة التثبيت UninstallUnknownEntry=إدخال غير معروف (%1) تمت مصادفة في سجل إلغاء التثبيت ConfirmUninstall=هل أنت متأكد من أنك تريد إزالة %1 تماما, وجميع مكوناته? UninstallOnlyOnWin64=يمكن إلغاء تثبيت هذا التثبيت على Windows 64-بت فقط. OnlyAdminCanUninstall=يمكن إلغاء تثبيت هذا التثبيت فقط من قبل مستخدم له امتيازات إدارية. UninstallStatusLabel=يرجى الإنتظار ريت ما يتم إزالة تثبيت %1 من جهازك. UninstalledAll=تم إزالة %1 تماما من جهازك بنجاح. UninstalledMost=اكتمل إزالة %1.%n%nتعذر إزالة بعض العناصر. يمكن إزالة هذه يدوياً. UninstalledAndNeedsRestart=لإكمال إلغاء تثبيت %1, يجب إعادة تشغيل الكمبيوتر.%n%nهل تريد إعادة تشغيل الآن؟ UninstallDataCorrupted=الملف "%1" تالف. لا يمكن إزالة التثبيت ; *** Uninstallation phase messages ConfirmDeleteSharedFileTitle=إزالة ملف مشترك؟ ConfirmDeleteSharedFile2=يشير النظام إلى أن الملف المشترك التالي لم يعد في الاستخدام من قبل أي برامج. هل ترغب في أن يقوم إلغاء التثبيت بإزالة هذا الملف المشترك?%n%nإذا كانت أية برامج لا تزال تستخدم هذا الملف وتتم إزالته، قد لا تعمل هذه البرامج بشكل صحيح. إذا كنت غير متأكد، اختر لا. ترك الملف على النظام الخاص بك لن يسبب أي ضرر. SharedFileNameLabel=اسم الملف: SharedFileLocationLabel=الموقع : WizardUninstalling=حالة إزالة التثبيت StatusUninstalling=جاري إزالة تثبيت %1... ; *** Shutdown block reasons ShutdownBlockReasonInstallingApp=جاري تثبيت %1. ShutdownBlockReasonUninstallingApp=جاري تثبيت %1. ; The custom messages below aren't used by Setup itself, but if you make ; use of them in your scripts, you'll want to translate them. [CustomMessages] NameAndVersion=%1 الإصدار %2 AdditionalIcons=اختصارات إضافية: CreateDesktopIcon=إنشاء اختصار في &سطح المكتب CreateQuickLaunchIcon=إنشاء اختصار "الت&شغيل السريع" ProgramOnTheWeb=%1 على الأنترنت UninstallProgram=إزالة تثبيت %1 LaunchProgram=تشغيل %1 AssocFileExtension=اربط %1 مع صيغة ملف %2 AssocingFileExtension=جاري ربط %1 مع صيغة ملف %2 AutoStartProgramGroupDescription=بدأ التشغيل: AutoStartProgram=تشغيل %1 تلقائيا AddonHostProgramNotFound= تعذر العثور على %1 في الموقع الذي إخترته.%n%nهل تريد المتابعة على أية حال؟ ================================================ FILE: InnoDependencies/ChineseSimplified.isl ================================================ ; *** Inno Setup version 6.1.0+ Chinese Simplified messages *** ; ; To download user-contributed translations of this file, go to: ; https://jrsoftware.org/files/istrans/ ; ; Note: When translating this text, do not add periods (.) to the end of ; messages that didn't have them already, because on those messages Inno ; Setup adds the periods automatically (appending a period would result in ; two periods being displayed). ; ; Maintained by Zhenghan Yang ; Email: 847320916@QQ.com ; Translation based on network resource ; The latest Translation is on https://github.com/kira-96/Inno-Setup-Chinese-Simplified-Translation ; [LangOptions] ; The following three entries are very important. Be sure to read and ; understand the '[LangOptions] section' topic in the help file. LanguageName=简体中文 ; If Language Name display incorrect, uncomment next line ; LanguageName=<7B80><4F53><4E2D><6587> ; About LanguageID, to reference link: ; https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-lcid/a9eac961-e77d-41a6-90a5-ce1a8b0cdb9c LanguageID=$0804 LanguageCodePage=936 ; If the language you are translating to requires special font faces or ; sizes, uncomment any of the following entries and change them accordingly. ;DialogFontName= ;DialogFontSize=8 ;WelcomeFontName=Verdana ;WelcomeFontSize=12 ;TitleFontName=Arial ;TitleFontSize=29 ;CopyrightFontName=Arial ;CopyrightFontSize=8 [Messages] ; *** 应用程序标题 SetupAppTitle=安装 SetupWindowTitle=安装 - %1 UninstallAppTitle=卸载 UninstallAppFullTitle=%1 卸载 ; *** Misc. common InformationTitle=信息 ConfirmTitle=确认 ErrorTitle=错误 ; *** SetupLdr messages SetupLdrStartupMessage=现在将安装 %1。您想要继续吗? LdrCannotCreateTemp=不能创建临时文件。安装中断。 LdrCannotExecTemp=不能执行临时目录中的文件。安装中断。 HelpTextNote= ; *** 启动错误消息 LastErrorMessage=%1.%n%n错误 %2: %3 SetupFileMissing=安装目录中的文件 %1 丢失。请修正这个问题或者获取程序的新副本。 SetupFileCorrupt=安装文件已损坏。请获取程序的新副本。 SetupFileCorruptOrWrongVer=安装文件已损坏,或是与这个安装程序的版本不兼容。请修正这个问题或获取新的程序副本。 InvalidParameter=无效的命令行参数:%n%n%1 SetupAlreadyRunning=安装程序正在运行。 WindowsVersionNotSupported=这个程序不支持当前计算机运行的Windows版本。 WindowsServicePackRequired=这个程序需要 %1 服务包 %2 或更高。 NotOnThisPlatform=这个程序将不能运行于 %1。 OnlyOnThisPlatform=这个程序必须运行于 %1。 OnlyOnTheseArchitectures=这个程序只能在为下列处理器结构设计的Windows版本中进行安装:%n%n%1 WinVersionTooLowError=这个程序需要 %1 版本 %2 或更高。 WinVersionTooHighError=这个程序不能安装于 %1 版本 %2 或更高。 AdminPrivilegesRequired=在安装这个程序时您必须以管理员身份登录。 PowerUserPrivilegesRequired=在安装这个程序时您必须以管理员身份或有权限的用户组身份登录。 SetupAppRunningError=安装程序发现 %1 当前正在运行。%n%n请先关闭所有运行的窗口,然后点击“确定”继续,或按“取消”退出。 UninstallAppRunningError=卸载程序发现 %1 当前正在运行。%n%n请先关闭所有运行的窗口,然后点击“确定”继续,或按“取消”退出。 ; *** 启动问题 PrivilegesRequiredOverrideTitle=选择安装程序模式 PrivilegesRequiredOverrideInstruction=选择安装模式 PrivilegesRequiredOverrideText1=%1 可以为所有用户安装(需要管理员权限),或仅为您安装。 PrivilegesRequiredOverrideText2=%1 只能为您安装,或为所有用户安装(需要管理员权限)。 PrivilegesRequiredOverrideAllUsers=为所有用户安装(&A) PrivilegesRequiredOverrideAllUsersRecommended=为所有用户安装(&A) (建议选项) PrivilegesRequiredOverrideCurrentUser=只为我安装(&M) PrivilegesRequiredOverrideCurrentUserRecommended=只为我安装(&M) (建议选项) ; *** 其它错误 ErrorCreatingDir=安装程序不能创建目录“%1”。 ErrorTooManyFilesInDir=不能在目录“%1”中创建文件,因为里面的文件太多 ; *** 安装程序公共消息 ExitSetupTitle=退出安装程序 ExitSetupMessage=安装程序还未完成安装。如果您现在退出,程序将不能安装。%n%n您可以以后再运行安装程序完成安装。%n%n现在退出安装程序吗? AboutSetupMenuItem=关于安装程序(&A)... AboutSetupTitle=关于安装程序 AboutSetupMessage=%1 版本 %2%n%3%n%n%1 主页:%n%4 AboutSetupNote= TranslatorNote= ; *** 按钮 ButtonBack=< 上一步(&B) ButtonNext=下一步(&N) > ButtonInstall=安装(&I) ButtonOK=确定 ButtonCancel=取消 ButtonYes=是(&Y) ButtonYesToAll=全是(&A) ButtonNo=否(&N) ButtonNoToAll=全否(&O) ButtonFinish=完成(&F) ButtonBrowse=浏览(&B)... ButtonWizardBrowse=浏览(&R)... ButtonNewFolder=新建文件夹(&M) ; *** “选择语言”对话框消息 SelectLanguageTitle=选择安装语言 SelectLanguageLabel=选择安装时要使用的语言。 ; *** 公共向导文字 ClickNext=点击“下一步”继续,或点击“取消”退出安装程序。 BeveledLabel= BrowseDialogTitle=浏览文件夹 BrowseDialogLabel=在下列列表中选择一个文件夹,然后点击“确定”。 NewFolderName=新建文件夹 ; *** “欢迎”向导页 WelcomeLabel1=欢迎使用 [name] 安装向导 WelcomeLabel2=现在将安装 [name/ver] 到您的电脑中。%n%n推荐您在继续安装前关闭所有其它应用程序。 ; *** “密码”向导页 WizardPassword=密码 PasswordLabel1=这个安装程序有密码保护。 PasswordLabel3=请输入密码,然后点击“下一步”继续。密码区分大小写。 PasswordEditLabel=密码(&P): IncorrectPassword=您所输入的密码不正确,请重试。 ; *** “许可协议”向导页 WizardLicense=许可协议 LicenseLabel=继续安装前请阅读下列重要信息。 LicenseLabel3=请仔细阅读下列许可协议。您在继续安装前必须同意这些协议条款。 LicenseAccepted=我同意此协议(&A) LicenseNotAccepted=我不同意此协议(&D) ; *** “信息”向导页 WizardInfoBefore=信息 InfoBeforeLabel=请在继续安装前阅读下列重要信息。 InfoBeforeClickLabel=如果您想继续安装,点击“下一步”。 WizardInfoAfter=信息 InfoAfterLabel=请在继续安装前阅读下列重要信息。 InfoAfterClickLabel=如果您想继续安装,点击“下一步”。 ; *** “用户信息”向导页 WizardUserInfo=用户信息 UserInfoDesc=请输入您的信息。 UserInfoName=用户名(&U): UserInfoOrg=组织(&O): UserInfoSerial=序列号(&S): UserInfoNameRequired=您必须输入用户名。 ; *** “选择目标目录”向导页 WizardSelectDir=选择目标位置 SelectDirDesc=您想将 [name] 安装在哪里? SelectDirLabel3=安装程序将安装 [name] 到下列文件夹中。 SelectDirBrowseLabel=点击“下一步”继续。如果您想选择其它文件夹,点击“浏览”。 DiskSpaceGBLabel=至少需要有 [gb] GB 的可用磁盘空间。 DiskSpaceMBLabel=至少需要有 [mb] MB 的可用磁盘空间。 CannotInstallToNetworkDrive=安装程序无法安装到一个网络驱动器。 CannotInstallToUNCPath=安装程序无法安装到一个UNC路径。 InvalidPath=您必须输入一个带驱动器卷标的完整路径,例如:%n%nC:\APP%n%n或下列形式的UNC路径:%n%n\\server\share InvalidDrive=您选定的驱动器或 UNC 共享不存在或不能访问。请选选择其它位置。 DiskSpaceWarningTitle=没有足够的磁盘空间 DiskSpaceWarning=安装程序至少需要 %1 KB 的可用空间才能安装,但选定驱动器只有 %2 KB 的可用空间。%n%n您一定要继续吗? DirNameTooLong=文件夹名称或路径太长。 InvalidDirName=文件夹名称无效。 BadDirName32=文件夹名称不能包含下列任何字符:%n%n%1 DirExistsTitle=文件夹已存在 DirExists=文件夹:%n%n%1%n%n已经存在。您一定要安装到这个文件夹中吗? DirDoesntExistTitle=文件夹不存在 DirDoesntExist=文件夹:%n%n%1%n%n不存在。您想要创建此文件夹吗? ; *** “选择组件”向导页 WizardSelectComponents=选择组件 SelectComponentsDesc=您想安装哪些程序的组件? SelectComponentsLabel2=选择您想要安装的组件;清除您不想安装的组件。然后点击“下一步”继续。 FullInstallation=完全安装 ; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language) CompactInstallation=简洁安装 CustomInstallation=自定义安装 NoUninstallWarningTitle=组件已存在 NoUninstallWarning=安装程序检测到下列组件已在您的电脑中安装:%n%n%1%n%n取消选定这些组件将不能卸载它们。%n%n您一定要继续吗? ComponentSize1=%1 KB ComponentSize2=%1 MB ComponentsDiskSpaceGBLabel=当前选择的组件至少需要 [gb] GB 的磁盘空间。 ComponentsDiskSpaceMBLabel=当前选择的组件至少需要 [mb] MB 的磁盘空间。 ; *** “选择附加任务”向导页 WizardSelectTasks=选择附加任务 SelectTasksDesc=您想要安装程序执行哪些附加任务? SelectTasksLabel2=选择您想要安装程序在安装 [name] 时执行的附加任务,然后点击“下一步”。 ; *** “选择开始菜单文件夹”向导页 WizardSelectProgramGroup=选择开始菜单文件夹 SelectStartMenuFolderDesc=安装程序应该在哪里放置程序的快捷方式? SelectStartMenuFolderLabel3=安装程序现在将在下列开始菜单文件夹中创建程序的快捷方式。 SelectStartMenuFolderBrowseLabel=点击“下一步”继续。如果您想选择其它文件夹,点击“浏览”。 MustEnterGroupName=您必须输入一个文件夹名。 GroupNameTooLong=文件夹名或路径太长。 InvalidGroupName=文件夹名是无效的。 BadGroupName=文件夹名不能包含下列任何字符:%n%n%1 NoProgramGroupCheck2=不创建开始菜单文件夹(&D) ; *** “准备安装”向导页 WizardReady=准备安装 ReadyLabel1=安装程序现在准备开始安装 [name] 到您的电脑中。 ReadyLabel2a=点击“安装”继续此安装程序。如果您想要回顾或修改设置,请点击“上一步”。 ReadyLabel2b=点击“安装”继续此安装程序? ReadyMemoUserInfo=用户信息: ReadyMemoDir=目标位置: ReadyMemoType=安装类型: ReadyMemoComponents=选定组件: ReadyMemoGroup=开始菜单文件夹: ReadyMemoTasks=附加任务: ; *** TDownloadWizardPage wizard page and DownloadTemporaryFile DownloadingLabel=正在下载附加文件... ButtonStopDownload=停止下载(&S) StopDownload=您确定要停止下载吗? ErrorDownloadAborted=下载已中止 ErrorDownloadFailed=下载失败:%1 %2 ErrorDownloadSizeFailed=获取下载大小失败:%1 %2 ErrorFileHash1=校验文件哈希失败:%1 ErrorFileHash2=无效的文件哈希:预期 %1,实际 %2 ErrorProgress=无效的进度:%1,总共%2 ErrorFileSize=文件大小错误:预期 %1,实际 %2 ; *** “正在准备安装”向导页 WizardPreparing=正在准备安装 PreparingDesc=安装程序正在准备安装 [name] 到您的电脑中。 PreviousInstallNotCompleted=先前程序的安装/卸载未完成。您需要重新启动您的电脑才能完成安装。%n%n在重新启动电脑后,再运行安装完成 [name] 的安装。 CannotContinue=安装程序不能继续。请点击“取消”退出。 ApplicationsFound=下列应用程序正在使用的文件需要更新设置。它是建议您允许安装程序自动关闭这些应用程序。 ApplicationsFound2=下列应用程序正在使用的文件需要更新设置。它是建议您允许安装程序自动关闭这些应用程序。安装完成后,安装程序将尝试重新启动应用程序。 CloseApplications=自动关闭该应用程序(&A) DontCloseApplications=不要关闭该应用程序(&D) ErrorCloseApplications=安装程序无法自动关闭所有应用程序。在继续之前,我们建议您关闭所有使用需要更新的安装程序文件。 PrepareToInstallNeedsRestart=安装程序必须重新启动计算机。重新启动计算机后,请再次运行安装程序以完成 [name] 的安装。%n%n是否立即重新启动? ; *** “正在安装”向导页 WizardInstalling=正在安装 InstallingLabel=安装程序正在安装 [name] 到您的电脑中,请稍等。 ; *** “安装完成”向导页 FinishedHeadingLabel=[name] 安装完成 FinishedLabelNoIcons=安装程序已在您的电脑中安装了 [name]。 FinishedLabel=安装程序已在您的电脑中安装了 [name]。此应用程序可以通过选择安装的快捷方式运行。 ClickFinish=点击“完成”退出安装程序。 FinishedRestartLabel=要完成 [name] 的安装,安装程序必须重新启动您的电脑。您想要立即重新启动吗? FinishedRestartMessage=要完成 [name] 的安装,安装程序必须重新启动您的电脑。%n%n您想要立即重新启动吗? ShowReadmeCheck=是,我想查阅自述文件 YesRadio=是,立即重新启动电脑(&Y) NoRadio=否,稍后重新启动电脑(&N) ; used for example as 'Run MyProg.exe' RunEntryExec=运行 %1 ; used for example as 'View Readme.txt' RunEntryShellExec=查阅 %1 ; *** “安装程序需要下一张磁盘”提示 ChangeDiskTitle=安装程序需要下一张磁盘 SelectDiskLabel2=请插入磁盘 %1 并点击“确定”。%n%n如果这个磁盘中的文件可以在下列文件夹之外的文件夹中找到,请输入正确的路径或点击“浏览”。 PathLabel=路径(&P): FileNotInDir2=文件“%1”不能在“%2”定位。请插入正确的磁盘或选择其它文件夹。 SelectDirectoryLabel=请指定下一张磁盘的位置。 ; *** 安装状态消息 SetupAborted=安装程序未完成安装。%n%n请修正这个问题并重新运行安装程序。 AbortRetryIgnoreSelectAction=选择操作 AbortRetryIgnoreRetry=重试(&T) AbortRetryIgnoreIgnore=忽略错误并继续(&I) AbortRetryIgnoreCancel=关闭安装程序 ; *** 安装状态消息 StatusClosingApplications=正在关闭应用程序... StatusCreateDirs=正在创建目录... StatusExtractFiles=正在解压缩文件... StatusCreateIcons=正在创建快捷方式... StatusCreateIniEntries=正在创建 INI 条目... StatusCreateRegistryEntries=正在创建注册表条目... StatusRegisterFiles=正在注册文件... StatusSavingUninstall=正在保存卸载信息... StatusRunProgram=正在完成安装... StatusRestartingApplications=正在重启应用程序... StatusRollback=正在撤销更改... ; *** 其它错误 ErrorInternal2=内部错误:%1 ErrorFunctionFailedNoCode=%1 失败 ErrorFunctionFailed=%1 失败;错误代码 %2 ErrorFunctionFailedWithMessage=%1 失败;错误代码 %2.%n%3 ErrorExecutingProgram=不能执行文件:%n%1 ; *** 注册表错误 ErrorRegOpenKey=打开注册表项时出错:%n%1\%2 ErrorRegCreateKey=创建注册表项时出错:%n%1\%2 ErrorRegWriteKey=写入注册表项时出错:%n%1\%2 ; *** INI 错误 ErrorIniEntry=在文件“%1”中创建INI条目时出错。 ; *** 文件复制错误 FileAbortRetryIgnoreSkipNotRecommended=跳过这个文件(&S) (不推荐) FileAbortRetryIgnoreIgnoreNotRecommended=忽略错误并继续(&I) (不推荐) SourceIsCorrupted=源文件已损坏 SourceDoesntExist=源文件“%1”不存在 ExistingFileReadOnly2=无法替换现有文件,因为它是只读的。 ExistingFileReadOnlyRetry=移除只读属性并重试(&R) ExistingFileReadOnlyKeepExisting=保留现有文件(&K) ErrorReadingExistingDest=尝试读取现有文件时出错: FileExistsSelectAction=选择操作 FileExists2=文件已经存在。 FileExistsOverwriteExisting=覆盖已经存在的文件(&O) FileExistsKeepExisting=保留现有的文件(&K) FileExistsOverwriteOrKeepAll=为所有的冲突文件执行此操作(&D) ExistingFileNewerSelectAction=选择操作 ExistingFileNewer2=现有的文件比安装程序将要安装的文件更新。 ExistingFileNewerOverwriteExisting=覆盖已经存在的文件(&O) ExistingFileNewerKeepExisting=保留现有的文件(&K) (推荐) ExistingFileNewerOverwriteOrKeepAll=为所有的冲突文件执行此操作(&D) ErrorChangingAttr=尝试改变下列现有的文件的属性时出错: ErrorCreatingTemp=尝试在目标目录创建文件时出错: ErrorReadingSource=尝试读取下列源文件时出错: ErrorCopying=尝试复制下列文件时出错: ErrorReplacingExistingFile=尝试替换现有的文件时出错: ErrorRestartReplace=重新启动替换失败: ErrorRenamingTemp=尝试重新命名以下目标目录中的一个文件时出错: ErrorRegisterServer=无法注册 DLL/OCX:%1 ErrorRegSvr32Failed=RegSvr32 失败;退出代码 %1 ErrorRegisterTypeLib=无法注册类型库:%1 ; *** 卸载显示名字标记 ; used for example as 'My Program (32-bit)' UninstallDisplayNameMark=%1 (%2) ; used for example as 'My Program (32-bit, All users)' UninstallDisplayNameMarks=%1 (%2, %3) UninstallDisplayNameMark32Bit=32位 UninstallDisplayNameMark64Bit=64位 UninstallDisplayNameMarkAllUsers=所有用户 UninstallDisplayNameMarkCurrentUser=当前用户 ; *** 安装后错误 ErrorOpeningReadme=尝试打开自述文件时出错。 ErrorRestartingComputer=安装程序不能重新启动电脑,请手动重启。 ; *** 卸载消息 UninstallNotFound=文件“%1”不存在。无法卸载。 UninstallOpenError=文件“%1”不能打开。无法卸载。 UninstallUnsupportedVer=此版本的卸载程序无法识别卸载日志文件“%1”的格式。无法卸载 UninstallUnknownEntry=在卸载日志中遇到一个未知的条目 (%1) ConfirmUninstall=您确认想要完全删除 %1 及它的所有组件吗? UninstallOnlyOnWin64=这个安装程序只能在64位Windows中进行卸载。 OnlyAdminCanUninstall=这个安装的程序需要有管理员权限的用户才能卸载。 UninstallStatusLabel=正在从您的电脑中删除 %1,请稍等。 UninstalledAll=%1 已顺利地从您的电脑中删除。 UninstalledMost=%1 卸载完成。%n%n有一些内容无法被删除。您可以手动删除它们。 UninstalledAndNeedsRestart=要完成 %1 的卸载,您的电脑必须重新启动。%n%n您想立即重新启动电脑吗? UninstallDataCorrupted=文件“%1”已损坏,无法卸载 ; *** 卸载状态消息 ConfirmDeleteSharedFileTitle=删除共享文件吗? ConfirmDeleteSharedFile2=系统中包含的下列共享文件已经不再被其它程序使用。您想要卸载程序删除这些共享文件吗?%n%n如果这些文件被删除,但还有程序正在使用这些文件,这些程序可能不能正确执行。如果您不能确定,选择“否”。把这些文件保留在系统中以免引起问题。 SharedFileNameLabel=文件名: SharedFileLocationLabel=位置: WizardUninstalling=卸载状态 StatusUninstalling=正在卸载 %1... ; *** Shutdown block reasons ShutdownBlockReasonInstallingApp=正在安装 %1。 ShutdownBlockReasonUninstallingApp=正在卸载 %1。 ; The custom messages below aren't used by Setup itself, but if you make ; use of them in your scripts, you'll want to translate them. [CustomMessages] NameAndVersion=%1 版本 %2 AdditionalIcons=附加快捷方式: CreateDesktopIcon=创建桌面快捷方式(&D) CreateQuickLaunchIcon=创建快速运行栏快捷方式(&Q) ProgramOnTheWeb=%1 网站 UninstallProgram=卸载 %1 LaunchProgram=运行 %1 AssocFileExtension=将 %2 文件扩展名与 %1 建立关联(&A) AssocingFileExtension=正在将 %2 文件扩展名与 %1 建立关联... AutoStartProgramGroupDescription=启动组: AutoStartProgram=自动启动 %1 AddonHostProgramNotFound=%1无法找到您所选择的文件夹。%n%n您想要继续吗? ================================================ FILE: InnoDependencies/ChineseTraditional.isl ================================================ ; *** Inno Setup version 6.1.0+ Chinese (Traditional) messages *** ; Name: Enfeng Tsao, nelson22768384@gmail.com ; Based on 5.5.3+ translations by Samuel Lee, Email: 751555749@qq.com ; Translation based on network resource ; ; Note: When translating this text, do not add periods (.) to the end of ; messages that didn't have them already, because on those messages Inno ; Setup adds the periods automatically (appending a period would result in ; two periods being displayed). [LangOptions] ; The following three entries are very important. Be sure to read and ; understand the '[LangOptions] section' topic in the help file. LanguageName=<7e41><9ad4><4e2d><6587> LanguageID=$0404 LanguageCodepage=950 ; If the language you are translating to requires special font faces or ; sizes, uncomment any of the following entries and change them accordingly. ;DialogFontName= ;DialogFontSize=8 ;TitleFontName=Arial ;TitleFontSize=29 ;WelcomeFontName=Verdana ;WelcomeFontSize=12 ;CopyrightFontName=Arial ;CopyrightFontSize=8 [Messages] ; *** Application titles SetupAppTitle=安裝程式 SetupWindowTitle=%1 安裝程式 UninstallAppTitle=解除安裝 UninstallAppFullTitle=解除安裝 %1 ; *** Misc. common InformationTitle=訊息 ConfirmTitle=確認 ErrorTitle=錯誤 ; *** SetupLdr messages SetupLdrStartupMessage=這將會安裝 %1。您想要繼續嗎? LdrCannotCreateTemp=無法建立暫存檔案。安裝程式將會結束。 LdrCannotExecTemp=無法執行暫存檔案。安裝程式將會結束。 HelpTextNote= ; *** Startup error messages LastErrorMessage=%1%n%n錯誤 %2: %3 SetupFileMissing=安裝資料夾中遺失檔案 %1。請修正此問題或重新取得此軟體。 SetupFileCorrupt=安裝檔案已經損毀。請重新取得此軟體。 SetupFileCorruptOrWrongVer=安裝檔案已經損毀,或與安裝程式的版本不符。請重新取得此軟體。 InvalidParameter=某個無效的變量已被傳遞到了命令列:%n%n%1 SetupAlreadyRunning=安裝程式已經在執行。 WindowsVersionNotSupported=本安裝程式並不支援目前在電腦所運行的 Windows 版本。 WindowsServicePackRequired=本安裝程式需要 %1 Service Pack %2 或更新。 NotOnThisPlatform=這個程式無法在 %1 執行。 OnlyOnThisPlatform=這個程式必須在 %1 執行。 OnlyOnTheseArchitectures=這個程式只能在專門為以下處理器架構而設計的 Windows 上安裝:%n%n%1 WinVersionTooLowError=這個程式必須在 %1 版本 %2 或以上的系統執行。 WinVersionTooHighError=這個程式無法安裝在 %1 版本 %2 或以上的系統。 AdminPrivilegesRequired=您必須登入成系統管理員以安裝這個程式。 PowerUserPrivilegesRequired=您必須登入成具有系統管理員或 Power User 權限的使用者以安裝這個程式。 SetupAppRunningError=安裝程式偵測到 %1 正在執行。%n%n請關閉該程式後按 「確定」 繼續,或按 「取消」 離開。 UninstallAppRunningError=解除安裝程式偵測到 %1 正在執行。%n%n請關閉該程式後按 「確定」 繼續,或按 「取消」 離開。 ; *** Startup questions PrivilegesRequiredOverrideTitle=選擇安裝程式安裝模式 PrivilegesRequiredOverrideInstruction=選擇安裝模式 PrivilegesRequiredOverrideText1=可以為所有使用者安裝 %1 (需要系統管理權限),或是僅為您安裝。 PrivilegesRequiredOverrideText2=可以僅為您安裝 %1,或是為所有使用者安裝 (需要系統管理權限)。 PrivilegesRequiredOverrideAllUsers=為所有使用者安裝 (&A) PrivilegesRequiredOverrideAllUsersRecommended=為所有使用者安裝 (建議選項) (&A) PrivilegesRequiredOverrideCurrentUser=僅為我安裝 (&M) PrivilegesRequiredOverrideCurrentUserRecommended=僅為我安裝 (建議選項) (&M) ; *** Misc. errors ErrorCreatingDir=安裝程式無法建立資料夾“%1”。 ErrorTooManyFilesInDir=無法在資料夾“%1”內建立檔案,因為資料夾內有太多的檔案。 ; *** Setup common messages ExitSetupTitle=結束安裝程式 ExitSetupMessage=安裝尚未完成。如果您現在結束安裝程式,這個程式將不會被安裝。%n%n您可以稍後再執行安裝程式以完成安裝程序。您現在要結束安裝程式嗎? AboutSetupMenuItem=關於安裝程式 (&A)... AboutSetupTitle=關於安裝程式 AboutSetupMessage=%1 版本 %2%n%3%n%n%1 網址:%n%4 AboutSetupNote= TranslatorNote= ; *** Buttons ButtonBack=< 上一步(&B) ButtonInstall=安裝(&I) ButtonNext=下一步(&N) > ButtonOK=確定 ButtonCancel=取消 ButtonYes=是(&Y) ButtonYesToAll=全部皆是 (&A) ButtonNo=否(&N) ButtonNoToAll=全部皆否 (&O) ButtonFinish=完成 (&F) ButtonBrowse=瀏覽 (&B)... ButtonWizardBrowse=瀏覽 (&R)... ButtonNewFolder=建立新資料夾 (&M) ; *** "Select Language" dialog messages SelectLanguageTitle=選擇安裝語言 SelectLanguageLabel=選擇在安裝過程中使用的語言: ; *** Common wizard text ClickNext=按 「下一步」 繼續安裝,或按 「取消」 結束安裝程式。 BeveledLabel= BrowseDialogTitle=瀏覽資料夾 BrowseDialogLabel=在下面的資料夾列表中選擇一個資料夾,然後按 「確定」。 NewFolderName=新資料夾 ; *** "Welcome" wizard page WelcomeLabel1=歡迎使用 [name] 安裝程式 WelcomeLabel2=這個安裝程式將會安裝 [name/ver] 到您的電腦。%n%n我們強烈建議您在安裝過程中關閉其它的應用程式,以避免與安裝程式發生沖突。 ; *** "Password" wizard page WizardPassword=密碼 PasswordLabel1=這個安裝程式具有密碼保護。 PasswordLabel3=請輸入密碼,然後按 「下一步」 繼續。密碼是區分大小寫的。 PasswordEditLabel=密碼 (&P): IncorrectPassword=您輸入的密碼不正確,請重新輸入。 ; *** "License Agreement" wizard page WizardLicense=授權合約 LicenseLabel=請閱讀以下授權合約。 LicenseLabel3=請閱讀以下授權合約,您必須接受合約的各項條款才能繼續安裝。 LicenseAccepted=我同意 (&A) LicenseNotAccepted=我不同意 (&D) ; *** "Information" wizard pages WizardInfoBefore=訊息 InfoBeforeLabel=在繼續安裝之前請閱讀以下重要資訊。 InfoBeforeClickLabel=當您準備好繼續安裝,請按 「下一步」。 WizardInfoAfter=訊息 InfoAfterLabel=在繼續安裝之前請閱讀以下重要資訊。 InfoAfterClickLabel=當您準備好繼續安裝,請按 「下一步」。 ; *** "User Information" wizard page WizardUserInfo=使用者資訊 UserInfoDesc=請輸入您的資料。 UserInfoName=使用者名稱(&U): UserInfoOrg=組織(&O): UserInfoSerial=序號(&S): UserInfoNameRequired=您必須輸入您的名稱。 ; *** "Select Destination Location" wizard page WizardSelectDir=選擇目的資料夾 SelectDirDesc=選擇安裝程式安裝 [name] 的位置。 SelectDirLabel3=安裝程式將會把 [name] 安裝到下面的資料夾。 SelectDirBrowseLabel=按 「下一步」 繼續,如果您想選擇另一個資料夾,請按 「瀏覽」。 DiskSpaceGBLabel=最少需要 [gb] GB 磁碟空間。 DiskSpaceMBLabel=最少需要 [mb] MB 磁碟空間。 CannotInstallToNetworkDrive=安裝程式無法安裝於網絡磁碟機。 CannotInstallToUNCPath=安裝程式無法安裝於 UNC 路徑。 InvalidPath=您必須輸入完整的路徑名稱及磁碟機代碼。%n%n例如 C:\App 或 UNC 路徑格式 \\伺服器\共用資料夾。 InvalidDrive=您選取的磁碟機或 UNC 名稱不存在或無法存取,請選擇其他的目的地。 DiskSpaceWarningTitle=磁碟空間不足 DiskSpaceWarning=安裝程式需要至少 %1 KB 的磁碟空間,您所選取的磁碟只有 %2 KB 可用空間。%n%n您要繼續安裝嗎? DirNameTooLong=資料夾名稱或路徑太長。 InvalidDirName=資料夾名稱不正確。 BadDirName32=資料夾名稱不得包含以下特殊字元:%n%n%1 DirExistsTitle=資料夾已經存在 DirExists=資料夾:%n%n%1%n%n 已經存在。仍要安裝到該資料夾嗎? DirDoesntExistTitle=資料夾不存在 DirDoesntExist=資料夾:%n%n%1%n%n 不存在。要建立該資料夾嗎? ; *** "Select Components" wizard page WizardSelectComponents=選擇元件 SelectComponentsDesc=選擇將會被安裝的元件。 SelectComponentsLabel2=選擇您想要安裝的元件;清除您不想安裝的元件。然後按 「下一步」 繼續安裝。 FullInstallation=完整安裝 ; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language) CompactInstallation=最小安裝 CustomInstallation=自訂安裝 NoUninstallWarningTitle=元件已存在 NoUninstallWarning=安裝程式偵測到以下元件已經安裝在您的電腦上:%n%n%1%n%n取消選擇這些元件將不會移除它們。%n%n您仍然要繼續嗎? ComponentSize1=%1 KB ComponentSize2=%1 MB ComponentsDiskSpaceGBLabel=目前的選擇需要至少 [gb] GB 磁碟空間。 ComponentsDiskSpaceMBLabel=目前的選擇需要至少 [mb] MB 磁碟空間。 ; *** "Select Additional Tasks" wizard page WizardSelectTasks=選擇附加的工作 SelectTasksDesc=選擇要執行的附加工作。 SelectTasksLabel2=選擇安裝程式在安裝 [name] 時要執行的附加工作,然後按 「下一步」。 ; *** "Select Start Menu Folder" wizard page WizardSelectProgramGroup=選擇「開始」功能表的資料夾 SelectStartMenuFolderDesc=選擇安裝程式建立程式的捷徑的位置。 SelectStartMenuFolderLabel3=安裝程式將會把程式的捷徑建立在下面的「開始」功能表資料夾。 SelectStartMenuFolderBrowseLabel=按 「下一步」 繼續,如果您想選擇另一個資料夾,請按 「瀏覽」。 MustEnterGroupName=您必須輸入一個資料夾的名稱。 GroupNameTooLong=資料夾名稱或路徑太長。 InvalidGroupName=資料夾名稱不正確。 BadGroupName=資料夾名稱不得包含下列字元:%n%n%1 NoProgramGroupCheck2=不要在「開始」功能表中建立資料夾 (&D) ; *** "Ready to Install" wizard page WizardReady=準備安裝 ReadyLabel1=安裝程式將開始安裝 [name] 到您的電腦中。 ReadyLabel2a=按下 「安裝」 繼續安裝,或按 「上一步」 重新檢視或設定各選項的內容。 ReadyLabel2b=按下 「安裝」 繼續安裝。 ReadyMemoUserInfo=使用者資訊 ReadyMemoDir=目的資料夾: ReadyMemoType=安裝型態: ReadyMemoComponents=選擇的元件: ReadyMemoGroup=「開始」功能表資料夾: ReadyMemoTasks=附加工作: ; *** TDownloadWizardPage wizard page and DownloadTemporaryFile DownloadingLabel=正在下載額外檔案... ButtonStopDownload=停止下載 (&S) StopDownload=您確定要停止下載嗎? ErrorDownloadAborted=已停止下載 ErrorDownloadFailed=下載失敗: %1 %2 ErrorDownloadSizeFailed=取得檔案大小失敗: %1 %2 ErrorFileHash1=檔案雜湊失敗: %1 ErrorFileHash2=檔案雜湊無效: 必須為 %1,收到 %2 ErrorProgress=進度無效: %1 之 %2 ErrorFileSize=檔案大小無效: 必須為 %1,收到 %2 ; *** "Preparing to Install" wizard page WizardPreparing=準備安裝程式 PreparingDesc=安裝程式準備將 [name] 安裝到您的電腦上。 PreviousInstallNotCompleted=先前的安裝/ 解除安裝尚未完成,您必須重新啟動電腦以完成該安裝。%n%n在重新啟動電腦之後,請再執行這個程式來安裝 [name]。 CannotContinue=安裝程式無法繼續。請按 「取消」 離開。 ApplicationsFound=下面的應用程式正在使用安裝程式所需要更新的檔案。建議您允許安裝程式自動關閉這些應用程式。 ApplicationsFound2=下面的應用程式正在使用安裝程式所需要更新的檔案。建議您允許安裝程式自動關閉這些應用程式。當安裝過程結束後,本安裝程式將會嘗試重新開啟該應用程式。 CloseApplications=關閉應用程式 (&A) DontCloseApplications=不要關閉應用程式 (&D) ErrorCloseApplications=安裝程式無法自動關閉所有應用程式。建議您在繼續前先關閉所有應用程式使用的檔案。 PrepareToInstallNeedsRestart=安裝程式必須重新啟動您的電腦。重新啟動後,請再次執行安裝程式以完成 [name] 的安裝。%n%n您想要現在重新啟動電腦嗎? ; *** "Installing" wizard page WizardInstalling=正在安裝 InstallingLabel=請稍候,安裝程式正在將 [name] 安裝到您的電腦上 ; *** "Setup Completed" wizard page FinishedHeadingLabel=安裝完成 FinishedLabelNoIcons=安裝程式已經將 [name] 安裝在您的電腦上。 FinishedLabel=安裝程式已經將 [name] 安裝在您的電腦中,您可以選擇程式的圖示來執行該應用程式。 ClickFinish=按 「完成」 以結束安裝程式。 FinishedRestartLabel=要完成 [name] 的安裝,安裝程式必須重新啟動您的電腦。您想要現在重新啟動電腦嗎? FinishedRestartMessage=要完成 [name] 的安裝,安裝程式必須重新啟動您的電腦。%n%n您想要現在重新啟動電腦嗎? ShowReadmeCheck=是,我要閱讀讀我檔案。 YesRadio=是,立即重新啟動電腦(&Y) NoRadio=否,我稍後重新啟動電腦(&N) ; used for example as 'Run MyProg.exe' RunEntryExec=執行 %1 ; used for example as 'View Readme.txt' RunEntryShellExec=檢視 %1 ; *** "Setup Needs the Next Disk" ChangeDiskTitle=安裝程式需要下一張磁片 SelectDiskLabel2=請插入磁片 %1,然後按 「確定」。%n%n如果檔案不在以下所顯示的資料夾之中,請輸入正確的資料夾名稱或按 [瀏覽] 選取。 PathLabel=路徑(&P): FileNotInDir2=檔案“%1”無法在“%2”找到。請插入正確的磁片或選擇其它的資料夾。 SelectDirectoryLabel=請指定下一張磁片的位置。 ; *** Installation phase messages SetupAborted=安裝沒有完成。%n%n請更正問題後重新安裝一次。 AbortRetryIgnoreSelectAction=選取動作 AbortRetryIgnoreRetry=請再試一次 (&T) AbortRetryIgnoreIgnore=略過錯誤並繼續 (&I) AbortRetryIgnoreCancel=取消安裝 ; *** Installation status messages StatusClosingApplications=正在關閉應用程式... StatusCreateDirs=正在建立資料夾... StatusExtractFiles=正在解壓縮檔案... StatusCreateIcons=正在建立程式集圖示... StatusCreateIniEntries=寫入 INI 檔案的項目... StatusCreateRegistryEntries=正在更新系統登錄... StatusRegisterFiles=正在登錄檔案... StatusSavingUninstall=儲存解除安裝資訊... StatusRunProgram=正在完成安裝... StatusRestartingApplications=正在重新開啟應用程式... StatusRollback=正在復原變更... ; *** Misc. errors ErrorInternal2=內部錯誤: %1 ErrorFunctionFailedNoCode=%1 失敗 ErrorFunctionFailed=%1 失敗;代碼 %2 ErrorFunctionFailedWithMessage=%1 失敗;代碼 %2.%n%3 ErrorExecutingProgram=無法執行檔案:%n%1 ; *** Registry errors ErrorRegOpenKey=無法開啟登錄鍵:%n%1\%2 ErrorRegCreateKey=無法建立登錄項目:%n%1\%2 ErrorRegWriteKey=無法變更登錄項目:%n%1\%2 ; *** INI errors ErrorIniEntry=在檔案“%1”建立 INI 項目錯誤。 ; *** File copying errors FileAbortRetryIgnoreSkipNotRecommended=略過這個檔案 (不建議) (&S) FileAbortRetryIgnoreIgnoreNotRecommended=略過錯誤並繼續 (不建議) (&I) SourceDoesntExist=來源檔案“%1”不存在。 SourceIsCorrupted=來源檔案已經損毀。 ExistingFileReadOnly2=無法取代現有檔案,因為檔案已標示為唯讀。 ExistingFileReadOnlyRetry=移除唯讀屬性並重試 (&R) ExistingFileReadOnlyKeepExisting=保留現有檔案 (&K) ErrorReadingExistingDest=讀取一個已存在的檔案時發生錯誤: FileExistsSelectAction=選擇操作 FileExists2=檔案已存在。 FileExistsOverwriteExisting=覆寫現有檔案 FileExistsKeepExisting=保留現有檔案 (&O) FileExistsOverwriteOrKeepAll=對下次衝突執行相同操作 (&D) ExistingFileNewerSelectAction=選擇操作 ExistingFileNewer2=現有檔案比安裝程式嘗試安裝的檔案還新。 ExistingFileNewerOverwriteExisting=覆寫現有檔案 (&O) ExistingFileNewerKeepExisting=保留現有檔案 (&K) (建議選項) ExistingFileNewerOverwriteOrKeepAll=對下次衝突執行相同操作 (&D) ErrorChangingAttr=在變更檔案屬性時發生錯誤: ErrorCreatingTemp=在目的資料夾中建立檔案時發生錯誤: ErrorReadingSource=讀取原始檔案時發生錯誤: ErrorCopying=複製檔案時發生錯誤: ErrorReplacingExistingFile=取代檔案時發生錯誤: ErrorRestartReplace=重新啟動電腦後取代檔案失敗: ErrorRenamingTemp=在目的資料夾變更檔案名稱時發生錯誤: ErrorRegisterServer=無法注冊 DLL/OCX 檔案: %1。 ErrorRegSvr32Failed=RegSvr32 失敗;退出代碼 %1 ErrorRegisterTypeLib=無法注冊類型庫: %1。 ; *** Uninstall display name markings ; used for example as 'My Program (32-bit)' UninstallDisplayNameMark=%1 (%2) ; used for example as 'My Program (32-bit, All users)' UninstallDisplayNameMarks=%1 (%2, %3) UninstallDisplayNameMark32Bit=32 位元 UninstallDisplayNameMark64Bit=64 位元 UninstallDisplayNameMarkAllUsers=所有使用者 UninstallDisplayNameMarkCurrentUser=目前使用者 ; *** Post-installation errors ErrorOpeningReadme=開啟讀我檔案時發生錯誤。 ErrorRestartingComputer=安裝程式無法重新啟動電腦,請自行重新啟動。 ; *** Uninstaller messages UninstallNotFound=檔案“%1”不存在,無法解除安裝。 UninstallOpenError=無法開啟檔案“%1”,無法解除安裝 UninstallUnsupportedVer=這個版本的解除安裝程式無法辨識記錄檔 “%1” 之格式,無法解除安裝。 UninstallUnknownEntry=解除安裝記錄檔中發現未知的記錄 (%1)。 ConfirmUninstall=您確定要完全移除 %1 及其相關的檔案嗎? UninstallOnlyOnWin64=這個程式只能在 64 位元的 Windows 上解除安裝。 OnlyAdminCanUninstall=這個程式要具備系統管理員權限的使用者方可解除安裝。 UninstallStatusLabel=正在從您的電腦移除 %1 中,請稍候... UninstalledAll=%1 已經成功從您的電腦中移除。 UninstalledMost=%1 解除安裝完成。%n%n某些檔案及元件無法移除,您可以自行刪除這些檔案。 UninstalledAndNeedsRestart=要完成 %1 的解除安裝程序,您必須重新啟動電腦。%n%n您想要現在重新啟動電腦嗎? UninstallDataCorrupted=檔案“%1”已經損毀,無法解除安裝 ; *** Uninstallation phase messages ConfirmDeleteSharedFileTitle=移除共用檔案 ConfirmDeleteSharedFile2=系統顯示下列共用檔案已不再被任何程式所使用,您要移除這些檔案嗎?%n%n%1%n%n倘若您移除了以上檔案但仍有程式需要使用它們,將造成這些程式無法正常執行,因此您若無法確定請選擇 [否]。保留這些檔案在您的系統中不會造成任何損害。 SharedFileNameLabel=檔案名稱: SharedFileLocationLabel=位置: WizardUninstalling=解除安裝狀態 StatusUninstalling=正在解除安裝 %1... ; *** Shutdown block reasons ShutdownBlockReasonInstallingApp=正在安裝 %1。 ShutdownBlockReasonUninstallingApp=正在解除安裝 %1。 ; The custom messages below aren't used by Setup itself, but if you make ; use of them in your scripts, you'll want to translate them. [CustomMessages] NameAndVersion=%1 版本 %2 AdditionalIcons=附加圖示: CreateDesktopIcon=建立桌面圖示(&D) CreateQuickLaunchIcon=建立快速啟動圖示(&Q) ProgramOnTheWeb=%1 的網站 UninstallProgram=解除安裝 %1 LaunchProgram=啟動 %1 AssocFileExtension=將 %1 與檔案副檔名 %2 產生關聯(&A) AssocingFileExtension=正在將 %1 與檔案副檔名 %2 產生關聯... AutoStartProgramGroupDescription=開啟: AutoStartProgram=自動開啟 %1 AddonHostProgramNotFound=%1 無法在您所選的資料夾中找到。%n%n您是否還要繼續? ================================================ FILE: InnoDependencies/Greek.isl ================================================ ; *** Inno Setup version 6.1.0+ Greek messages *** ; ; To download user-contributed translations of this file, go to: ; https://jrsoftware.org/files/istrans/ ; ; Note: When translating this text, do not add periods (.) to the end of ; messages that didn't have them already, because on those messages Inno ; Setup adds the periods automatically (appending a period would result in ; two periods being displayed). ; ; Originally translated by Anastasis Chatzioglou, baldycom@hotmail.com ; Updated by XhmikosR [XhmikosR, my_nickname at yahoo dot com] ; Updated to version 6.1.0+ by Vasileios Karamichail, v.karamichail@outlook.com ; [LangOptions] ; The following three entries are very important. Be sure to read and ; understand the '[LangOptions] section' topic in the help file. LanguageName=Ελληνικά LanguageID=$0408 LanguageCodePage=1253 ; If the language you are translating to requires special font faces or ; sizes, uncomment any of the following entries and change them accordingly. ;DialogFontName= ;DialogFontSize=8 ;WelcomeFontName=Verdana ;WelcomeFontSize=12 ;TitleFontName=Arial ;TitleFontSize=29 ;CopyrightFontName=Arial ;CopyrightFontSize=8 [Messages] ; *** Application titles SetupAppTitle=Εγκατάσταση SetupWindowTitle=Εγκατάσταση - %1 UninstallAppTitle=Απεγκατάσταση UninstallAppFullTitle=%1 Απεγκατάσταση ; *** Misc. common InformationTitle=Πληροφορίες ConfirmTitle=Επιβεβαίωση ErrorTitle=Σφάλμα ; *** SetupLdr messages SetupLdrStartupMessage=Θα εκτελεστεί η εγκατάσταση του %1. Θέλετε να συνεχίσετε; LdrCannotCreateTemp=Σφάλμα στη δημιουργία προσωρινού αρχείου. Η εγκατάσταση τερματίστηκε LdrCannotExecTemp=Αδύνατη η εκτέλεση αρχείου στον φάκελο προσωρινών αρχείων. Η εγκατάσταση τερματίστηκε HelpTextNote= ; *** Startup error messages LastErrorMessage=%1.%n%nΣφάλμα %2: %3 SetupFileMissing=Το αρχείο %1 λείπει από τον κατάλογο εγκατάστασης. Διορθώστε το πρόβλημα ή αποκτήστε ένα νέο αντίγραφο του προγράμματος. SetupFileCorrupt=Το αρχείο εγκατάστασης είναι κατεστραμμένο. Παρακαλώ προμηθευτείτε ένα νέο αντίγραφο του προγράμματος. SetupFileCorruptOrWrongVer=Το αρχείο εγκατάστασης είναι κατεστραμμένο ή δεν είναι συμβατό με αυτήν την έκδοση του προγράμματος εγκατάστασης. Διορθώστε το πρόβλημα ή αποκτήστε ένα νέο αντίγραφο του προγράμματος. InvalidParameter=Μία μη έγκυρη παράμετρος χρησιμοποιήθηκε στη γραμμή εντολών:%n%n%1 SetupAlreadyRunning=Η εγκατάσταση τρέχει ήδη. WindowsVersionNotSupported=Αυτό το πρόγραμμα δεν υποστηρίζει την έκδοση των Windows που εκτελεί ο υπολογιστής σας. WindowsServicePackRequired=Αυτό το πρόγραμμα χρειάζεται το %1 Service Pack %2 ή νεότερο. NotOnThisPlatform=Αυτό το πρόγραμμα δεν μπορεί να εκτελεστεί σε %1. OnlyOnThisPlatform=Αυτό το πρόγραμμα μπορεί να εκτελεστεί μόνο σε %1. OnlyOnTheseArchitectures=Αυτό το πρόγραμμα μπορεί να εγκατασταθεί μόνο σε εκδόσεις των Windows που έχουν σχεδιαστεί για τις ακόλουθες αρχιτεκτονικές επεξεργαστών:%n%n%1 WinVersionTooLowError=Αυτό το πρόγραμμα απαιτεί %1 έκδοση %2 ή μεταγενέστερη. WinVersionTooHighError=Αυτό το πρόγραμμα δεν μπορεί να εγκατασταθεί σε %1 έκδοση %2 ή μεταγενέστερη. AdminPrivilegesRequired=Πρέπει να είστε συνδεδεμένοι ως διαχειριστής κατά την εγκατάσταση αυτού του προγράμματος. PowerUserPrivilegesRequired=Πρέπει να είστε συνδεδεμένοι ως διαχειριστής ή ως μέλος της ομάδας Power User κατά την εγκατάσταση αυτού του προγράμματος. SetupAppRunningError=Ο Οδηγός Εγκατάστασης εντόπισε ότι η εφαρμογή %1 εκτελείται ήδη.%n%nΠαρακαλώ κλείστε την εφαρμογή τώρα και πατήστε ΟΚ για να συνεχίσετε, ή Άκυρο για έξοδο. UninstallAppRunningError=Ο Οδηγός Απεγκατάστασης εντόπισε ότι η εφαρμογή %1 εκτελείται ήδη.%n%nΠαρακαλώ κλείστε την εφαρμογή τώρα και πατήστε ΟΚ για να συνεχίσετε, ή Άκυρο για έξοδο. ; *** Startup questions PrivilegesRequiredOverrideTitle=Επιλέξτε Τρόπο Εγκατάστασης PrivilegesRequiredOverrideInstruction=Επιλέξτε τον τρόπο εγκατάστασης PrivilegesRequiredOverrideText1=Το %1 μπορεί να εγκατασταθεί για όλους τους χρήστες (απαιτεί δικαιώματα διαχειριστή) ή μόνο για εσάς. PrivilegesRequiredOverrideText2=Το %1 μπορεί να εγκατασταθεί μόνο για εσάς ή για όλους τους χρήστες (απαιτεί δικαιώματα διαχειριστή). PrivilegesRequiredOverrideAllUsers=Εγκατάσταση για &όλους τους χρήστες PrivilegesRequiredOverrideAllUsersRecommended=Εγκατάσταση για όλ&ους τους χρήστες (συνιστάται) PrivilegesRequiredOverrideCurrentUser=Εγκατάσταση μόνο για &εμένα PrivilegesRequiredOverrideCurrentUserRecommended=Εγκατάσταση μόνο για &εμένα (συνιστάται) ; *** Misc. errors ErrorCreatingDir=Η εγκατάσταση δεν μπόρεσε να δημιουργήσει τον φάκελο "%1" ErrorTooManyFilesInDir=Δεν είναι δυνατή η δημιουργία ενός αρχείου στον φάκελο "%1" επειδή περιέχει πολλά αρχεία ; *** Setup common messages ExitSetupTitle=Τέλος Εγκατάστασης ExitSetupMessage=Η εγκατάσταση δεν έχει ολοκληρωθεί. Αν την τερματίσετε τώρα, το πρόγραμμα δεν θα εγκατασταθεί.%n%nΜπορείτε να εκτελέσετε ξανά την εγκατάσταση αργότερα.%n%nΈξοδος; AboutSetupMenuItem=&Σχετικά με την Εγκατάσταση... AboutSetupTitle=Σχετικά με την Εγκατάσταση AboutSetupMessage=%1 έκδοση %2%n%3%n%n%1 αρχική σελίδα:%n%4 AboutSetupNote= TranslatorNote= ; *** Buttons ButtonBack=< &Πίσω ButtonNext=&Επόμενο > ButtonInstall=&Εγκατάσταση ButtonOK=ΟΚ ButtonCancel=&Ακυρο ButtonYes=Ν&αι ButtonYesToAll=Ναι σε &Ολα ButtonNo=Ό&χι ButtonNoToAll=Όχι &σε όλα ButtonFinish=&Τέλος ButtonBrowse=&Αναζήτηση... ButtonWizardBrowse=Ανα&ζήτηση... ButtonNewFolder=&Δημιουργία νέου φακέλου ; *** "Select Language" dialog messages SelectLanguageTitle=Επιλογή Γλώσσας Οδηγού Εγκατάστασης SelectLanguageLabel=Επιλέξτε τη γλώσσα που θέλετε να χρησιμοποιήσετε κατά την εγκατάσταση. ; *** Common wizard text ClickNext=Πατήστε Επόμενο για να συνεχίσετε ή Άκυρο για να τερματίσετε την εγκατάσταση. BeveledLabel= BrowseDialogTitle=Αναζήτηση Φακέλου BrowseDialogLabel=Επιλέξτε ένα φάκελο από την ακόλουθη λίστα και πατήστε ΟΚ. NewFolderName=Νέος φάκελος ; *** "Welcome" wizard page WelcomeLabel1=Καλως ορίσατε στον Οδηγό Εγκατάστασης του [name] WelcomeLabel2=Θα γίνει εγκατάσταση του [name/ver] στον υπολογιστή σας.%n%nΣυνιστάται να κλείσετε όλες τις άλλες εφαρμογές πριν συνεχίσετε. ; *** "Password" wizard page WizardPassword=Κωδικός Πρόσβασης PasswordLabel1=Αυτή η εγκατάσταση προστατεύεται με κωδικό πρόσβασης. PasswordLabel3=Παρακαλώ εισάγετε τον κωδικό και πατήστε Επόμενο. PasswordEditLabel=&Κωδικός: IncorrectPassword=Ο κωδικός που έχετε εισάγει είναι λανθασμένος. Παρακαλώ, προσπαθήστε ξανά. ; *** "License Agreement" wizard page WizardLicense=Άδεια Χρήσης LicenseLabel=Παρακαλώ διαβάστε προσεκτικά τις ακόλουθες πληροφορίες πριν συνεχίσετε. LicenseLabel3=Παρακαλώ διαβάστε την ακόλουθη Άδεια Χρήσης. Θα πρέπει να αποδεχτείτε τους όρους της πριν συνεχίσετε την εγκατάσταση. LicenseAccepted=&Δέχομαι τους όρους της Άδειας Χρήσης LicenseNotAccepted=Δεν &αποδέχομαι τους όρους της Άδειας Χρήσης ; *** "Information" wizard pages WizardInfoBefore=Πληροφορίες InfoBeforeLabel=Παρακαλώ διαβάστε προσεκτικά τις ακόλουθες πληροφορίες πριν συνεχίσετε. InfoBeforeClickLabel=Όταν είστε έτοιμοι να συνεχίσετε με τον Οδηγό Εγκατάστασης, πατήστε Επόμενο. WizardInfoAfter=Πληροφορίες InfoAfterLabel=Παρακαλώ διαβάστε προσεκτικά τις ακόλουθες πληροφορίες πριν συνεχίσετε. InfoAfterClickLabel=Όταν είστε έτοιμοι να συνεχίσετε με τον Οδηγό Εγκατάστασης, πατήστε Επόμενο. ; *** "User Information" wizard page WizardUserInfo=Πληροφορίες Χρήστη UserInfoDesc=Παρακαλώ εισάγετε τα στοιχεία σας. UserInfoName=&Ονομα Χρήστη: UserInfoOrg=&Εταιρεία: UserInfoSerial=&Σειριακός Αριθμός: UserInfoNameRequired=Πρέπει να εισάγετε ένα όνομα. ; *** "Select Destination Location" wizard page WizardSelectDir=Επιλογή Φακέλου Εγκατάστασης SelectDirDesc=Πού θέλετε να εγκατασταθεί το [name]; SelectDirLabel3=Ο Οδηγός Εγκατάστασης θα εγκαταστήσει το [name] στον ακόλουθο φάκελο. SelectDirBrowseLabel=Για να συνεχίσετε, πατήστε Επόμενο. Εάν θέλετε να επιλέξετε διαφορετικό φάκελο, πατήστε Αναζήτηση. DiskSpaceGBLabel=Απαιτούνται τουλάχιστον [gb] GB ελεύθερου χώρου στο δίσκο. DiskSpaceMBLabel=Απαιτούνται τουλάχιστον [mb] MB ελεύθερου χώρου στο δίσκο. CannotInstallToNetworkDrive=Η εγκατάσταση δεν μπορεί να γίνει σε δίσκο δικτύου. CannotInstallToUNCPath=Η εγκατάσταση δεν μπορεί να γίνει σε διαδρομή UNC. InvalidPath=Πρέπει να δώσετε την πλήρη διαδρομή με το γράμμα δίσκου, για παράδειγμα:%n%nC:\APP%n%nή μια διαδρομή UNC της μορφής:%n%n\\server\share InvalidDrive=Ο τοπικός δίσκος ή ο δίσκος δικτύου που έχετε επιλέξει δεν υπάρχει ή δεν είναι προσβάσιμος. Παρακαλώ, επιλέξτε άλλον. DiskSpaceWarningTitle=Ανεπαρκής Χώρος στο Δίσκο DiskSpaceWarning=Η εγκατάσταση χρειάζεται τουλάχιστον %1 KB ελεύθερο χώρο στο δίσκο αλλά ο επιλεγμένος δίσκος διαθέτει μόνον %2 KB.%n%nΘέλετε να συνεχίσετε παρόλα αυτά; DirNameTooLong=Το όνομα ή η διαδρομή του φακέλου είναι πολύ μεγάλη. InvalidDirName=Το όνομα του φακέλου δεν είναι έγκυρο. BadDirName32=Το όνομα του φακέλου δεν μπορεί να περιλαμβάνει κανέναν από τους παρακάτω χαρακτήρες:%n%n%1 DirExistsTitle=Ο Φάκελος Υπάρχει DirExists=Ο φάκελος:%n%n%1%n%nυπάρχει ήδη. Θέλετε να γίνει η εγκατάσταση σε αυτόν τον φάκελο παρόλα αυτά; DirDoesntExistTitle=Ο Φάκελος Δεν Υπάρχει DirDoesntExist=Ο φάκελος:%n%n%1%n%nδεν υπάρχει. Θέλετε να δημιουργηθεί; ; *** "Select Components" wizard page WizardSelectComponents=Επιλογή Λειτουργιών Μονάδων SelectComponentsDesc=Ποια στοιχεία θέλετε να εγκατασταθούν; SelectComponentsLabel2=Επιλέξτε τα στοιχεία που θέλετε να εγκαταστήσετε, αποεπιλέξτε τα στοιχεία που δεν θέλετε να εγκαταστήσετε. Πατήστε Επόμενο όταν είστε έτοιμοι να συνεχίσετε. FullInstallation=Πλήρης εγκατάσταση ; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language) CompactInstallation=Τυπική εγκατάσταση CustomInstallation=Προσαρμοσμένη εγκατάσταση NoUninstallWarningTitle=Οι Λειτουργικές Μονάδες Υπάρχουν NoUninstallWarning=Ο Οδηγός Εγκατάστασης εντόπισε ότι τα ακόλουθα στοιχεία είναι ήδη εγκατεστημένα στον υπολογιστή σας:%n%n%1%n%nΑποεπιλέγοντας αυτά τα στοιχεία δεν θα απεγκατασταθούν.%n%nΘέλετε να συνεχίσετε παρόλα αυτά; ComponentSize1=%1 KB ComponentSize2=%1 MB ComponentsDiskSpaceGBLabel=Η τρέχουσα επιλογή απαιτεί τουλάχιστον [gb] GB χώρου στο δίσκο. ComponentsDiskSpaceMBLabel=Η τρέχουσα επιλογή απαιτεί τουλάχιστον [mb] MB χώρου στο δίσκο. ; *** "Select Additional Tasks" wizard page WizardSelectTasks=Επιλογή Επιπλέον Ενεργειών SelectTasksDesc=Ποιες επιπλέον ενέργειες θέλετε να γίνουν; SelectTasksLabel2=Επιλέξτε τις επιπλέον ενέργειες που θέλετε να γίνουν κατά την εγκατάσταση του [name] και πατήστε Επόμενο. ; *** "Select Start Menu Folder" wizard page WizardSelectProgramGroup=Επιλογή Φακέλου Μενού Έναρξης SelectStartMenuFolderDesc=Πού θέλετε να τοποθετηθούν οι συντομεύσεις του προγράμματος; SelectStartMenuFolderLabel3=Η εγκατάσταση θα δημιουργήσει τις συντομεύσεις του προγράμματος στον ακόλουθο φάκελο του μενού Έναρξη. SelectStartMenuFolderBrowseLabel=Για να συνεχίσετε, πατήστε Επόμενο. Αν θέλετε διαφορετικό φάκελο, πατήστε Αναζήτηση. MustEnterGroupName=Πρέπει να εισαγάγετε ένα όνομα φακέλου. GroupNameTooLong=Το όνομα ή η διαδρομή του φακέλου είναι πολύ μεγάλη. InvalidGroupName=Το όνομα του φακέλου δεν είναι έγκυρο. BadGroupName=Το όνομα του φακέλου δεν μπορεί να περιλαμβάνει κανέναν από τους παρακάτω χαρακτήρες:%n%n%1 NoProgramGroupCheck2=&Χωρίς δημιουργία φακέλου στο μενού Έναρξης. ; *** "Ready to Install" wizard page WizardReady=Έτοιμα για Εγκατάσταση ReadyLabel1=Ο Οδηγός Εγκατάστασης είναι έτοιμος να ξεκινήσει την εγκατάσταση του [name] στον υπολογιστή σας. ReadyLabel2a=Πατήστε Εγκατάσταση για να συνεχίσετε με την εγκατάσταση ή πατήστε Πίσω, εάν θέλετε να ελέγξετε ή να αλλάξετε τυχόν ρυθμίσεις. ReadyLabel2b=Πατήστε Εγκατάσταση για να συνεχίσετε την εγκατάσταση. ReadyMemoUserInfo=Πληροφορίες Χρήστη: ReadyMemoDir=Φάκελος προορισμού: ReadyMemoType=Είδος εγκατάστασης: ReadyMemoComponents=Επιλεγμένες λειτουργικές μονάδες: ReadyMemoGroup=Φάκελος στο μενού Έναρξη: ReadyMemoTasks=Επιπλέον ενέργειες: ; *** TDownloadWizardPage wizard page and DownloadTemporaryFile DownloadingLabel=Λήψη πρόσθετων αρχείων... ButtonStopDownload=&Διακοπή λήψης StopDownload=Είστε βέβαιοι ότι θέλετε να διακόψετε τη λήψη; ErrorDownloadAborted=Η λήψη ακυρώθηκε ErrorDownloadFailed=Η λήψη απέτυχε: %1 %2 ErrorDownloadSizeFailed=Η λήψη του μεγέθους απέτυχε: %1 %2 ErrorFileHash1=Αποτυχία υπολογισμού hash: %1 ErrorFileHash2=Μη έγκυρο hash: αναμενόμενο %1, βρέθηκε %2 ErrorProgress=Μη έγκυρη πρόοδος: %1 από %2 ErrorFileSize=Μη έγκυρο μέγεθος αρχείου: αναμενόμενο %1, βρέθηκε %2 ; *** "Preparing to Install" wizard page WizardPreparing=Προετοιμασία Εγκατάστασης PreparingDesc=Ο Οδηγός Εγκατάστασης προετοιμάζεται για την εγκατάσταση του [name] στον υπολογιστή σας. PreviousInstallNotCompleted=Η εγκατάσταση/αφαίρεση ενός προηγούμενου προγράμματος δεν ολοκληρώθηκε. Θα χρειαστεί να κάνετε επανεκκίνηση του υπολογιστή σας για να ολοκληρωθεί.%n%nΜετά την επανεκκίνηση του υπολογιστή σας, εκτελέστε ξανά τον Οδηγό Εγκατάστασης για να ολοκληρώσετε την εγκατάσταση/αφαίρεση του [name]. CannotContinue=Η εγκατάσταση δεν μπορεί να συνεχιστεί. Παρακαλώ πατήστε Άκυρο για τερματισμό. ApplicationsFound=Οι ακόλουθες εφαρμογές χρησιμοποιούν αρχεία που πρέπει να ενημερωθούν από τον Οδηγό Εγκατάστασης. Συνιστάται να επιτρέψετε στον Οδηγό Εγκατάστασης να κλείσει αυτόματα αυτές τις εφαρμογές. ApplicationsFound2=Οι ακόλουθες εφαρμογές χρησιμοποιούν αρχεία που πρέπει να ενημερωθούν από τον Οδηγό Εγκατάστασης. Συνιστάται να επιτρέψετε στον Οδηγό Εγκατάστασης να κλείσει αυτόματα αυτές τις εφαρμογές. Μετά την ολοκλήρωση της εγκατάστασης, ο Οδηγός Εγκατάστασης θα επιχειρήσει να κάνει επανεκκίνηση των εφαρμογών. CloseApplications=&Αυτόματο κλείσιμο των εφαρμογών DontCloseApplications=&Χωρίς κλείσιμο των εφαρμογών ErrorCloseApplications=Ο Οδηγός Εγκατάστασης δεν μπόρεσε να κλείσει αυτόματα όλες τις εφαρμογές. Συνιστάται να κλείσετε όλες τις εφαρμογές που χρησιμοποιούν αρχεία που πρέπει να ενημερωθούν από τον Οδηγό Εγκατάστασης προτού συνεχίσετε. PrepareToInstallNeedsRestart=Ο Οδηγός Εγκατάστασης πρέπει να κάνει επανεκκίνηση του υπολογιστή σας. Μετά την επανεκκίνηση του υπολογιστή σας, εκτελέστε ξανά τον Οδηγό Εγκατάστασης για να ολοκληρώσετε την εγκατάσταση του [name].%n%nΘα θέλατε να κάνετε επανεκκίνηση τώρα; ; *** "Installing" wizard page WizardInstalling=Εγκατάσταση InstallingLabel=Παρακαλώ περιμένετε καθώς γίνεται η εγκατάσταση του [name] στον υπολογιστή σας. ; *** "Setup Completed" wizard page FinishedHeadingLabel=Ολοκλήρωση του Οδηγού Εγκατάστασης του [name] FinishedLabelNoIcons=Ο Οδηγός Εγκατάστασης ολοκλήρωσε την εγκατάσταση του [name] στον υπολογιστή σας. FinishedLabel=Ο Οδηγός Εγκατάστασης ολοκλήρωσε την εγκατάσταση του [name] στον υπολογιστή σας. Η εφαρμογή μπορεί να ξεκινήσει επιλέγοντας κάποια από τις εγκατεστημένες συντομεύσεις. ClickFinish=Πατήστε Τέλος για να τερματίσετε τον Οδηγό Εγκατάστασης. FinishedRestartLabel=Για να ολοκληρώσετε την εγκατάσταση του [name], ο Οδηγός Εγκατάστασης πρέπει να κάνει επανεκκίνηση του υπολογιστή σας. Θα θέλατε να κάνετε επανεκκίνηση τώρα; FinishedRestartMessage=Για να ολοκληρώσετε την εγκατάσταση του [name], ο Οδηγός Εγκατάστασης πρέπει να κάνει επανεκκίνηση του υπολογιστή σας.%n%nΘα θέλατε να κάνετε επανεκκίνηση τώρα; ShowReadmeCheck=Ναι, θα ήθελα να δω το αρχείο README YesRadio=&Ναι, να γίνει επανεκκίνηση τώρα NoRadio=&Οχι, θα κάνω επανεκκίνηση αργότερα ; used for example as 'Run MyProg.exe' RunEntryExec=Εκτέλεση του %1 ; used for example as 'View Readme.txt' RunEntryShellExec=Προβολή του %1 ; *** "Setup Needs the Next Disk" stuff ChangeDiskTitle=Ο Οδηγός Εγκατάστασης χρειάζεται τον επόμενο δίσκο SelectDiskLabel2=Παρακαλώ, εισάγετε τον δίσκο %1 και πατήστε ΟΚ.%n%nΕάν τα αρχεία αυτού του δίσκου βρίσκονται σε φάκελο διαφορετικό από αυτόν που εμφανίζεται παρακάτω, πληκτρολογήστε τη σωστή διαδρομή ή πατήστε Αναζήτηση. PathLabel=&Διαδρομή: FileNotInDir2=Το αρχείο "%1" δε βρέθηκε στο "%2". Παρακαλώ εισάγετε το σωστό δίσκο ή επιλέξτε κάποιον άλλο φάκελο. SelectDirectoryLabel=Παρακαλώ καθορίσετε την τοποθεσία του επόμενου δίσκου. ; *** Installation phase messages SetupAborted=Η εγκατάσταση δεν ολοκληρώθηκε.%n%nΠαρακαλώ, διορθώστε το πρόβλημα και εκτελέστε ξανά τον Οδηγό Εγκατάστασης. AbortRetryIgnoreSelectAction=Επιλέξτε ενέργεια AbortRetryIgnoreRetry=&Δοκιμή AbortRetryIgnoreIgnore=&Αγνόηση και συνέχεια AbortRetryIgnoreCancel=Ακυρώση εγκατάστασης ; *** Installation status messages StatusClosingApplications=Κλείσιμο εφαρμογών... StatusCreateDirs=Δημιουργία φακέλων... StatusExtractFiles=Αποσυμπίεση αρχείων... StatusCreateIcons=Δημιουργία συντομεύσεων... StatusCreateIniEntries=Δημιουργία καταχωρήσεων INI... StatusCreateRegistryEntries=Δημιουργία καταχωρήσεων στο μητρώο... StatusRegisterFiles=Καταχώρηση αρχείων... StatusSavingUninstall=Αποθήκευση πληροφοριών απεγκατάστασης... StatusRunProgram=Ολοκλήρωση εγκατάστασης... StatusRestartingApplications=Επανεκκίνηση εφαρμογών... StatusRollback=Επαναφορά αλλαγών... ; *** Misc. errors ErrorInternal2=Εσωτερικό σφάλμα: %1 ErrorFunctionFailedNoCode=%1 απέτυχε ErrorFunctionFailed=%1 απέτυχε, κωδικός %2 ErrorFunctionFailedWithMessage=%1 απέτυχε, κωδικός %2.%n%3 ErrorExecutingProgram=Δεν είναι δυνατή η εκτέλεση του αρχείου:%n%1 ; *** Registry errors ErrorRegOpenKey=Σφάλμα ανάγνωσης κλειδιού μητρώου:%n%1\%2 ErrorRegCreateKey=Σφάλμα δημιουργίας κλειδιού μητρώου:%n%1\%2 ErrorRegWriteKey=Σφάλμα καταχώρησης κλειδιού μητρώου:%n%1\%2 ; *** INI errors ErrorIniEntry=Σφάλμα στη δημιουργία καταχώρησης INI στο αρχείο "%1". ; *** File copying errors FileAbortRetryIgnoreSkipNotRecommended=&Παράλειψη αυτού του αρχείου (δεν συνιστάται) FileAbortRetryIgnoreIgnoreNotRecommended=Παράλειψη σφάλματος και &συνέχεια (δεν συνιστάται) SourceIsCorrupted=Το αρχείο προέλευσης είναι κατεστραμμένο SourceDoesntExist=Το αρχείο προέλευσης "%1" δεν υπάρχει ExistingFileReadOnly2=Το υπάρχον αρχείο δεν μπόρεσε να αντικατασταθεί επειδή είναι μόνο για ανάγνωση. ExistingFileReadOnlyRetry=&Καταργήστε το χαρακτηριστικό μόνο για ανάγνωση και δοκιμάστε ξανά ExistingFileReadOnlyKeepExisting=&Διατηρήστε το υπάρχον αρχείο ErrorReadingExistingDest=Παρουσιάστηκε σφάλμα κατά την προσπάθεια ανάγνωσης του υπάρχοντος αρχείου: FileExistsSelectAction=Επιλέξτε ενέργεια FileExists2=Το αρχείο υπάρχει ήδη. FileExistsOverwriteExisting=&Αντικατάσταση υπάρχοντος αρχείου FileExistsKeepExisting=&Διατήρηση υπάρχοντος αρχείου FileExistsOverwriteOrKeepAll=&Να γίνει το ίδιο για τις επόμενες διενέξεις ExistingFileNewerSelectAction=Επιλέξτε ενέργεια ExistingFileNewer2=Το υπάρχον αρχείο είναι νεότερο από αυτό που προσπαθεί να εγκαταστήσει ο Οδηγός Εγκατάστασης. ExistingFileNewerOverwriteExisting=&Αντικατάσταση υπάρχοντος αρχείου ExistingFileNewerKeepExisting=&Διατήρηση υπάρχοντος αρχείου (συνιστάται) ExistingFileNewerOverwriteOrKeepAll=&Να γίνει το ίδιο για τις επόμενες διενέξεις ErrorChangingAttr=Παρουσιάστηκε σφάλμα κατά την προσπάθεια αλλαγής των χαρακτηριστικών του υπάρχοντος αρχείου: ErrorCreatingTemp=Παρουσιάστηκε σφάλμα κατά την προσπάθεια δημιουργίας ενός αρχείου στον φακέλο προορισμού: ErrorReadingSource=Παρουσιάστηκε σφάλμα κατά την προσπάθεια ανάγνωσης του αρχείου προέλευσης: ErrorCopying=Παρουσιάστηκε σφάλμα κατά την προσπάθεια αντιγραφής ενός αρχείου: ErrorReplacingExistingFile=Παρουσιάστηκε σφάλμα κατά την προσπάθεια αντικατάστασης του υπάρχοντος αρχείου: ErrorRestartReplace=Η ΕπανεκκίνησηΑντικατάσταση απέτυχε: ErrorRenamingTemp=Παρουσιάστηκε σφάλμα κατά την προσπάθεια μετονομασίας ενός αρχείου στον φακέλο προορισμού: ErrorRegisterServer=Δεν είναι δυνατή η καταχώριση του DLL/OCX: %1 ErrorRegSvr32Failed=Το RegSvr32 απέτυχε με κωδικό εξόδου %1 ErrorRegisterTypeLib=Δεν είναι δυνατή η καταχώριση της βιβλιοθήκης τύπων: %1 ; *** Uninstall display name markings ; used for example as 'My Program (32-bit)' UninstallDisplayNameMark=%1 (%2) ; used for example as 'My Program (32-bit, All users)' UninstallDisplayNameMarks=%1 (%2, %3) UninstallDisplayNameMark32Bit=32-bit UninstallDisplayNameMark64Bit=64-bit UninstallDisplayNameMarkAllUsers=Ολοι οι χρήστες UninstallDisplayNameMarkCurrentUser=Τρέχων χρήστης ; *** Post-installation errors ErrorOpeningReadme=Παρουσιάστηκε σφάλμα κατά την προσπάθεια ανοίγματος του αρχείου README. ErrorRestartingComputer=Ο Οδηγός Εγκατάστασης δεν μπόρεσε να κάνει επανεκκίνηση του υπολογιστή. Παρακαλώ επανεκκινήσετε τον υπολογιστή μόνοι σας. ; *** Uninstaller messages UninstallNotFound=Το αρχείο "%1" δεν υπάρχει. Δεν είναι δυνατή η απεγκατάσταση. UninstallOpenError=Το αρχείο "%1" δεν ήταν δυνατό να ανοίξει. Δεν είναι δυνατή η απεγκατάσταση UninstallUnsupportedVer=Το αρχείο καταγραφής απεγκατάστασης "%1" είναι σε μορφή που δεν αναγνωρίζεται από αυτήν την έκδοση του Οδηγού Απεγκατάστασης. Δεν ήταν δυνατή η απεγκατάσταση UninstallUnknownEntry=Μια άγνωστη καταχώρηση (%1) εντοπίστηκε στο αρχείο καταγραφής απεγκατάστασης ConfirmUninstall=Είστε βέβαιοι ότι θέλετε να καταργήσετε εντελώς το %1 και όλα τα στοιχεία του; UninstallOnlyOnWin64=Αυτή η εγκατάσταση μπορεί να απεγκατασταθεί μόνο σε Windows 64-bit. OnlyAdminCanUninstall=Αυτή η εγκατάσταση μπορεί να απεγκατασταθεί μόνο από χρήστη με δικαιώματα διαχειριστή. UninstallStatusLabel=Παρακαλώ περιμένετε μέχρι να καταργηθεί το %1 από τον υπολογιστή σας. UninstalledAll=Το %1 αφαιρέθηκε με επιτυχία από τον υπολογιστή σας. UninstalledMost=Το %1 αφαιρέθηκε με επιτυχία.%n%nΟρισμένα στοιχεία δεν ήταν δυνατό να καταργηθούν. Αυτά μπορούν να αφαιρεθούν από εσάς. UninstalledAndNeedsRestart=Για να ολοκληρώσετε την απεγκατάσταση του %1, ο υπολογιστής σας πρέπει να επανεκκινηθεί.%n%nΘα θέλατε να κάνετε επανεκκίνηση τώρα; UninstallDataCorrupted=Το "%1" αρχείο είναι κατεστραμμένο. Δεν ήταν δυνατή η απεγκατάσταση ; *** Uninstallation phase messages ConfirmDeleteSharedFileTitle=Κατάργηση Κοινόχρηστου Αρχείου; ConfirmDeleteSharedFile2=Το σύστημα υποδεικνύει ότι το ακόλουθο κοινόχρηστο αρχείο δεν χρησιμοποιείται πλέον από κανένα πρόγραμμα. Θέλετε να καταργηθεί αυτό το κοινόχρηστο αρχείο;%n%nΕάν κάποιο πρόγραμμα εξακολουθεί να το χρησιμοποιεί, ενδέχεται να μην λειτουργήσει σωστά. Εάν δεν είστε βέβαιοι, επιλέξτε Όχι. Αφήνοντάς το στο σύστημά σας δεν θα προκληθεί καμία ζημιά. SharedFileNameLabel=Όνομα Αρχείου: SharedFileLocationLabel=Τοποθεσία: WizardUninstalling=Πρόοδος Απεγκατάστασης StatusUninstalling=Απεγκατάσταση %1... ; *** Shutdown block reasons ShutdownBlockReasonInstallingApp=Εγκατάσταση του %1. ShutdownBlockReasonUninstallingApp=Απεγκατάσταση του %1. ; The custom messages below aren't used by Setup itself, but if you make ; use of them in your scripts, you'll want to translate them. [CustomMessages] NameAndVersion=%1 έκδοση %2 AdditionalIcons=Επιπλέον συντομεύσεις: CreateDesktopIcon=Δημιουργία συντόμευσης στην &επιφάνεια εργασίας CreateQuickLaunchIcon=Δημιουργία συντόμευσης στη &Γρήγορη Εκκίνηση ProgramOnTheWeb=Το %1 στο Internet UninstallProgram=Απεγκατάσταση του %1 LaunchProgram=Εκκίνηση του %1 AssocFileExtension=&Συσχέτιση του %1 με την επέκταση αρχείου %2 AssocingFileExtension=Γίνεται συσχέτιση του %1 με την επέκταση αρχείου "%2"... AutoStartProgramGroupDescription=Εκκίνηση: AutoStartProgram=Αυτόματη εκκίνηση του %1 AddonHostProgramNotFound=Το %1 δε βρέθηκε στο φάκελο που επιλέξατε.%n%nΘέλετε να συνεχίσετε παρόλα αυτά; ================================================ FILE: InnoDependencies/Latvian.isl ================================================ ; *** Inno Setup version 6.1.0+ Latvian messages *** ; ; Translated from English by Zorgaats, zorgaats@gmail.com ; ; Note: When translating this text, do not add periods (.) to the end of ; messages that didn't have them already, because on those messages Inno ; Setup adds the periods automatically (appending a period would result in ; two periods being displayed). [LangOptions] LanguageName=Latviski LanguageID=$0426 LanguageCodePage=1257 [Messages] ; *** Application titles SetupAppTitle=Uzstādīšana SetupWindowTitle=Uzstādīšana — %1 UninstallAppTitle=Noņemšana UninstallAppFullTitle=Noņemšana — % ; *** Misc. common InformationTitle=Informācija ConfirmTitle=Apstiprināt ErrorTitle=Kļūda ; *** SetupLdr messages SetupLdrStartupMessage=Tiks uzstādīta programma %1 uz Jūsu datora. Vai vēlaties turpināt? LdrCannotCreateTemp=Neiespējami izveidot pagaidu datnes. Uzstādīšana pārtraukta LdrCannotExecTemp=Neiespējami palaist datni no pagaidu mapes. Uzstādīšana pārtraukta HelpTextNote= ; *** Startup error messages LastErrorMessage=%1.%n%nKļūda %2: %3 SetupFileMissing=Datne %1 nav atrodama uzstādīšanas mapē. Lūdzu izlabojiet kļūdu vai iegādājaties jaunu programmas kopiju. SetupFileCorrupt=Uzstādīšanas datnes ir bojātas. Lūdzu iegādājaties jaunu programmas kopiju. SetupFileCorruptOrWrongVer=Uzstādīšanas datnes ir bojātas vai nav savienojamas ar šo uzstādīšanas programmu. Lūdzu izlabojiet kļūdu vai iegādājaties jaunu programmas kopiju. InvalidParameter=Komandrinda satur nepieļaujamu parametru:%n%n%1 SetupAlreadyRunning=Uzstādīšanas programma jau ir palaista. WindowsVersionNotSupported=Šī programma neatbalsta Windows versiju, kas uzstādīta uz šī datora. WindowsServicePackRequired=Programma pieprasa %1 Service Pack %2 vai jaunāku versiju. NotOnThisPlatform=Šī pragramma nevar darboties uz %1. OnlyOnThisPlatform=Programmu var palaist tikai uz %1. OnlyOnTheseArchitectures=Programmu var uzstādīt tikai uz Windows versijas ar šādu procesoru arhitektūru:%n%n%1 WinVersionTooLowError=Programma pieprasa %1 versiju %2 vai jaunāku. WinVersionTooHighError=Programmu nevar uzstādīt uz %1 versijas %2 vai jaunākas. AdminPrivilegesRequired=Jums ir jābūt administratoram, lai varētu uzsākt uzstādīšanu. PowerUserPrivilegesRequired=Jums ir jābūt administratoram vai pilnvarotam lietotājam, lai uzstādītu šo programmu. SetupAppRunningError=Ir atrasts palaists eksemplārs %1.%n%nLūdzu,aizveriet visas programmas un spiediet "Ok" lai turpinātu vai "Atcelt", lai izietu. UninstallAppRunningError=Noņemšana ir atklājusi, ka darbojas eksemplārs %1.%n%nLūdzu,aizveriet visas programmas un spiediet "Ok" lai turpinātu vai "Atcelt", lai izietu. ; *** Startup questions PrivilegesRequiredOverrideTitle=Uzstādīšanas režīma izvēle PrivilegesRequiredOverrideInstruction=Izvēlieties uzstādīšanas režīmu PrivilegesRequiredOverrideText1=%1 var tikt uzstādīts vai nu visiem lietotājiem (nepieciešamas administratora privilēģijas), vai arī tikai Jums. PrivilegesRequiredOverrideText2=%1 var tikt uzstādīts vai nu tikai Jums, vai arī visiem lietotājiem (nepieciešamas administratora privilēģijas). PrivilegesRequiredOverrideAllUsers=Uzstādīt &visiem lietotājiem PrivilegesRequiredOverrideAllUsersRecommended=Uzstādīt &visiem lietotājiem (rekomendējas) PrivilegesRequiredOverrideCurrentUser=Uzstādīt tikai &man PrivilegesRequiredOverrideCurrentUserRecommended=Uzstādīt tikai &man (rekomendējas) ; *** Misc. errors ErrorCreatingDir=Nevar izveidot mapi "%1" ErrorTooManyFilesInDir=Neiespējami izveidot datnes mapē "%1", jo tā satur pārāk daudz datņu ; *** Setup common messages ExitSetupTitle=Iziet no uzstādīšanas ExitSetupMessage=Uzstādīšana nav pabeigta. Ja Jūs tagad iziesiet, programma netiks uzstādīta.%n%nLai uzstādītu programmu, Jums būs atkal jāpalaiž uzstādīšana. %n%nIziet no uzstādīšanas? AboutSetupMenuItem=&Par uzstādīšanu... AboutSetupTitle=Par uzstādīšanu AboutSetupMessage=%1, varsija %2%n%3%n%n%1mājas lapa:%n%4 AboutSetupNote= TranslatorNote=Latvian translation by Zorgaats ; *** Buttons ButtonBack=< &Atpakaļ ButtonNext=&Tālāk > ButtonInstall=&Uzstādīt ButtonOK=OK ButtonCancel=Atcelt ButtonYes=&Jā ButtonYesToAll=Jā &Visam ButtonNo=&Nē ButtonNoToAll=Nē V&isam ButtonFinish=&Pabeigt ButtonBrowse=Pā&rlūkot... ButtonWizardBrowse=Pārlū&kot... ButtonNewFolder=I&zveidot jaunu mapi ; *** "Select Language" dialog messages SelectLanguageTitle=Izvēlieties uzstādīšanas valodu SelectLanguageLabel=Izvēlieties valodu, kurā notiks uzstādīšana: ; *** Common wizard text ClickNext=Spiediet "Tālāk", lai turpinātu, vai "Atcelt", lai izietu no uzstādīšanas. BeveledLabel= BrowseDialogTitle=Pārlūkot mapi BrowseDialogLabel=Izvēlieties mapi no saraksta, tad spiediet "Ok". NewFolderName=Jauna mape ; *** "Welcome" wizard page WelcomeLabel1=Вас приветствует Мастер установки [name] WelcomeLabel2=Programma uzstādīs [name/ver] uz Jūsu datora.%n%nPirms uzstādīšanas vēlams aizvērt visas programmas. ; *** "Password" wizard page WizardPassword=Parole PasswordLabel1=Uzstādīšana ir aizsargāta ar paroli. PasswordLabel3=Lūdzu, ievadiet paroli, tad spiediet "Tālāk", lai turpinātu. Parole ir reģistrjūtīga. PasswordEditLabel=&Parole: IncorrectPassword=Jūsu ievadītā parole ir nepareiza. Lūdzu, mēģiniet vēlreiz. ; *** "License Agreement" wizard page WizardLicense=Licence LicenseLabel=Lūdzu, izlasiet sekojošo informāciju, pirms turpināt. LicenseLabel3=Lūdzu, izlasiet Līgumu. Jums ir jāapstiprina Līgums, lai turpinātu uzstādīšanu. LicenseAccepted=Es &piekrītu līgumam LicenseNotAccepted=Es &nepiekrītu līgumam ; *** "Information" wizard pages WizardInfoBefore=Informācija InfoBeforeLabel=Lūdzu, izlasiet šo informāciju. InfoBeforeClickLabel=Kad esat gatavs turpināt uzstādīšanu, spiediet "Tālāk". WizardInfoAfter=Informācija InfoAfterLabel=Lūdzu, izlasiet šo informāciju. InfoAfterClickLabel=Kad esat gatavs turpināt uzstādīšanu, spiediet "Tālāk". ; *** "User Information" wizard page WizardUserInfo=Lietotāja informācija UserInfoDesc=Lūdzu, ievadiet datus par sevi. UserInfoName=&Lietotāja vārds: UserInfoOrg=&Organizācija: UserInfoSerial=&Sērijas numurs: UserInfoNameRequired=Jums ir jāievada savs vārds. ; *** "Select Destination Location" wizard page WizardSelectDir=Uzstādīšanas mapes izvēle SelectDirDesc=Kur [name] tiks instalēts? SelectDirLabel3=[name] datnes tiks instalētas norādītajā mapē. SelectDirBrowseLabel=Lai turpinātu, spiediet "Tālāk". Ja vēlaties norādīt citu mapi, spiediet "Pārlūkot". DiskSpaceGBLabel=Ir nepieciešami brīvi [gb] GB uz cietā diska. DiskSpaceMBLabel=Ir nepieciešami brīvi [mb] MB uz cietā diska. CannotInstallToNetworkDrive=Uzstādīšana nevar tikt veikta uz tīkla diska. CannotInstallToUNCPath=Uzstādīšana nevar tikt veikta mapē pa UNC-adresi. InvalidPath=Jums ir jānorāda pilna uzstādīšanas adrese, piemērs:%n%nC:\APP%n%nvai UNC adrese:%n%n\\server\share InvalidDrive=Disks vai tīkla adrese, kuru Jūs izvēlējāties, nepastāv vai arī nav pieejams. Lūdzu, izvēlieties citu. DiskSpaceWarningTitle=Nepietiek vietas uz diska DiskSpaceWarning=Uzstādīšanai ir nepieciešami vismaz %1 KB brīvas vietas uz diska, bet pieejami ir tikai %2 KB.%n%nVai vēlaties turpināt? DirNameTooLong=Mapes nosaukums vai adrese ir pārāk gara. InvalidDirName=Mapes nosaukums nav derīgs. BadDirName32=Mapes nosaukumā nedrīkst būt šādi simboli: %n%n%1 DirExistsTitle=Mape jau pastāv. DirExists=Mape:%n%n%1%n%njau pastāv. Vai vienalga vēlaties turpināt? DirDoesntExistTitle=Mape nepastāv DirDoesntExist=Mape%n%n%1%n%nnepastāv. Vai vēlaties to izveidot? ; *** "Select Components" wizard page WizardSelectComponents=Izvēlieties sastāvdaļas SelectComponentsDesc=Kurus komponentus vēlaties uzstādīt? SelectComponentsLabel2=Izvēlieties komponentus, kurus vēlaties uzstādīt. Spiediet "Tālāk", lai turpinātu. FullInstallation=Pilna uzstādīšana ; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language) CompactInstallation=Kompakta uzstādīšana CustomInstallation=Izveidot uzstādīšanu NoUninstallWarningTitle=Komponenti jau pastāv NoUninstallWarning=Uzstādīšana ir atklājusi, ka šādi komponenti jau ir uzstādīti:%n%n%1%n%nŠo komponentu uzstādīšanas atcelšana neizdzēsīs tos.%n%nVai turpināt? ComponentSize1=%1 Кб ComponentSize2=%1 Мб ComponentsDiskSpaceGBLabel=Pašlaik izvēlētie komponenti aizņem [gb] GB uz cietā diska. ComponentsDiskSpaceMBLabel=Pašlaik izvēlētie komponenti aizņem [mb] MB uz cietā diska. ; *** "Select Additional Tasks" wizard page WizardSelectTasks=Papilduzdevumu izvēlne SelectTasksDesc=Kurus papilduzdevumus vajadzētu veikt? SelectTasksLabel2=Izvēlieties, kādi papilduzdevumi tiks veikti [name] uzstādīšanas laikā, tad spiediet "Tālāk". ; *** "Select Start Menu Folder" wizard page WizardSelectProgramGroup=Izvēlieties Start Menu mapi SelectStartMenuFolderDesc=Kur uzstādīšanas programmai vajadzētu likt īsinājumikonas? SelectStartMenuFolderLabel3=Uzstādīšana izveidos īsinājumikonas Start Menu mapē. SelectStartMenuFolderBrowseLabel=Lai turpinātu, spiediet "Tālāk". Ja vēlaties norādīt citu mapi, spiediet "Pārlūkot". MustEnterGroupName=Jums ir jānorāda mape. GroupNameTooLong=Mapes nosaukums ir pārāk garš. InvalidGroupName=Mape nav derīga. BadGroupName=Mapes nosaukums satur kādu no šiem simboliem:%n%n%1 NoProgramGroupCheck2=&Neizveidot Start Menu mapi ; *** "Ready to Install" wizard page WizardReady=Gatavs uzstādīšanai ReadyLabel1=Uzstādīšana ir gatava instalēt [name] uz Jūsu datora. ReadyLabel2a=Spiediet "Uzstādīt", lai sāktu uzstādīšanu, vai spiediet "Atpakaļ", lai izmainītu parametrus. ReadyLabel2b=Spiediet "Uzstādīt", lai sāktu uzstādīšanu. ReadyMemoUserInfo=Lietotāja informācija: ReadyMemoDir=Galamēķis: ReadyMemoType=Uzstādīšanas tips: ReadyMemoComponents=Izvēlētie komponenti: ReadyMemoGroup=Start Menu mape: ReadyMemoTasks=Papilduzdevumi: ; *** TDownloadWizardPage wizard page and DownloadTemporaryFile DownloadingLabel=Papildus datņu lejupielāde... ButtonStopDownload=&Pārtraukt ielādi StopDownload=Jūs tiešām vēlaties pārtraukt lejupielādi? ErrorDownloadAborted=Lejupielāde pārtraukta ErrorDownloadFailed=Lejupielādes kļūda: %1 %2 ErrorDownloadSizeFailed=Izmēra kļūda: %1 %2 ErrorFileHash1=Ошибка хэша файла: %1 ErrorFileHash2=Неверный хэш файла: ожидался %1, получен %2 ErrorProgress=Izpildes kļūda: %1 из %2 ErrorFileSize=Kļūdains faila izmērs: tika gaidīts %1, iegūts %2 ; *** "Preparing to Install" wizard page WizardPreparing=Gatavoties uzstādīšanai PreparingDesc=Uzstādīšana ir gatava instalēt [name] uz Jūsu datora. PreviousInstallNotCompleted=Uzstādīšana/noņemšana iepriekšējai programmai nav pabeigta. Jums ir jāpārstartē dators, lai pabeigtu uzstādīšanu.%n%nPēc pārstartēšanas palaidiet uzstādīšanu no jauna, lai pabeigtu uzstādīt [name]. CannotContinue=Uzstādīšanu nevar turpināt. Lūdzu, spiediet "Atcelt", lai izietu. ApplicationsFound=Sekojošas programmas izmanto datnes, kuras uzstādīšanai jāatjauno. Rekomendējas uzstādīšanai atļaut automātiski aizvērt šīs programmas. ApplicationsFound2=Sekojošas programmas izmanto datnes, kuras uzstādīšanai jāatjauno. Rekomendējas uzstādīšanai atļaut automātiski aizvērt šīs programmas. Kad instalācija būs pabeigta, uzstādīšana mēģinās tās atkal palaist. CloseApplications=&Automātiski aizvērt šīs programmas DontCloseApplications=&Neaizvērt šīs programmas ErrorCloseApplications=Uzstādīšanai neizdevās automātiski aizvērt visas programmas.Pirms uzstādīšanas rekomendējas aizvērt visas programmas, kas izmanto atjaunināmās datnes. PrepareToInstallNeedsRestart=Uzstādīšanai nepieciešams pārstartēt Jūsu datoru. Kad dators pārstartēsies, lūdzu, palaidiet uzstādīšanas programmu vēlreiz, lai pabeigtu uzstādīšanu [name].%n%nVeikt pārstartēšanu tūlīt? ; *** "Installing" wizard page WizardInstalling=Uzstādīšana... InstallingLabel=Lūdzu, uzgaidiet, kamēr [name] tiks uzstādīts uz Jūsu datora. ; *** "Setup Completed" wizard page FinishedHeadingLabel=Pabeigta [name] uzstādīšana FinishedLabelNoIcons=Uzstādīšana pabeigta. FinishedLabel=Programma [name] ir uzstādīta uz Jūsu datora. Programmu var palaist, uzklikšķinot uz izveidotajām ikonām. ClickFinish=Spiediet "Pabeigt", lai aizvērtu uzstādīšanu. FinishedRestartLabel=Lai pabeigtu [name] uzstādīšanu, nepieciešams pārstartēt Jūsu datoru. Vai vēlaties to darīt tagad? FinishedRestartMessage=Lai pabeigtu [name] uzstādīšanu, nepieciešams pārstartēt Jūsu datoru.%n%nVai vēlaties to darīt tagad? ShowReadmeCheck=Jā, vēlos apskatīt README failu YesRadio=&Jā, pārstartēt datoru tagad NoRadio=&Nē, datoru pārstartēšu vēlāk ; used for example as 'Run MyProg.exe' RunEntryExec=Palaist %1 ; used for example as 'View Readme.txt' RunEntryShellExec=Apskatīt %1 ; *** "Setup Needs the Next Disk" stuff ChangeDiskTitle=Uzstādīšanai ir nepieciešams nākamais disks SelectDiskLabel2=Lūdzu, ielieciet %1 disku un spiediet "Ok".%n%nJa datne ir atrodama uz šī paša diska kādā citā mapē, norādiet tās atrašanās vietu vai spiediet "Pārlūkot", lai to norādītu. PathLabel=&Ceļš: FileNotInDir2=Datne "%1" neatrodas "%2". Lūdzu, ielieciet pareizo disku vai norādiet pareizo mapi. SelectDirectoryLabel=Lūdzu, norādiet nākamā diska atrašanās vietu. ; *** Installation phase messages SetupAborted=Uzstādīšana netika pabeigta.%n%nLūdzu, izlabojiet kļūdu un palaidiet uzstādīšanu no jauna. AbortRetryIgnoreSelectAction=Izvēlieties darbību AbortRetryIgnoreRetry=Mēģināt no &jauna AbortRetryIgnoreIgnore=&Ignorēt kļūdu un turpināt AbortRetryIgnoreCancel=Pārtraukt uzstādīšanu ; *** Installation status messages StatusClosingApplications=Programmu aizvēršana... StatusCreateDirs=Mapju izveidošana... StatusExtractFiles=Datņu kopēšana... StatusCreateIcons=Īsinājumikonu izveidošana... StatusCreateIniEntries=Izveido INI ierakstu... StatusCreateRegistryEntries=Izveido reģistra ierakstus... StatusRegisterFiles=Reģistrē datnes... StatusSavingUninstall=Saglabā noņemšanas datus... StatusRunProgram=Pabeidz uzstādīšanu... StatusRestartingApplications=Programmu restartēšana... StatusRollback=Izmaiņu atiestatīšana... ; *** Misc. errors ErrorInternal2=Iekšēja kļūda: %1 ErrorFunctionFailedNoCode=%1: cieta neveiksmi ErrorFunctionFailed=%1: cieta neveiksmi; kods %2 ErrorFunctionFailedWithMessage=%1: cieta neveiksmi; kods %2.%n%3 ErrorExecutingProgram=Nespēju palaist failu:%n%1 ; *** Registry errors ErrorRegOpenKey=Kļūda, atverot reģistra atslēgu:%n%1\%2 ErrorRegCreateKey=Kļūda, izveidojot reģistra atslēgu:%n%1\%2 ErrorRegWriteKey=Kļūda, rakstot reģistra atslēgu:%n%1\%2 ; *** INI errors ErrorIniEntry=Kļūda, izveidojot INI ieraksta datni "%1". ; *** File copying errors FileAbortRetryIgnoreSkipNotRecommended=I&zlaist šo failu (nerekomendējas) FileAbortRetryIgnoreIgnoreNotRecommended=&Ignorēt kļūdu un turpināt (nerekomendējas) SourceIsCorrupted=Datnes avots ir bojāts SourceDoesntExist=Datnes avots "%1" nepastāv ExistingFileReadOnly2=Nevar aizstāt esošo failu, tā kā tas ir iezīmēts kā "read only". ExistingFileReadOnlyRetry=&Dzēst atribūtu "read only" un atkārtot mēģinājumu ExistingFileReadOnlyKeepExisting=&Paturēt esošo failu ErrorReadingExistingDest=Kļūda, mēģinot lasīt pastāvošo failu: FileExistsSelectAction=Izvēlieties darbību FileExists2=Fails jau pastāv. FileExistsOverwriteExisting=&Aizstāt esošo failu FileExistsKeepExisting=&Saglabāt esošo failu FileExistsOverwriteOrKeepAll=A&tkārtot darbību visiem turpmākajiem konfliktiem ExistingFileNewerSelectAction=Izvēlieties darbību ExistingFileNewer2=Esošais fails ir jaunāks nekā uzstādāmais. ExistingFileNewerOverwriteExisting=&Aizstāt esošo failu ExistingFileNewerKeepExisting=&Saglabāt esošo failu (rekomendējas) ExistingFileNewerOverwriteOrKeepAll=A&tkārtot darbību visiem turpmākajiem konfliktiem ErrorChangingAttr=Radusies kļūda, mēģinot nomainīt datnes īpašību: ErrorCreatingTemp=Radusies kļūda, izveidojot datni galamērķa mapē: ErrorReadingSource=Radusies kļūda, nolasot datni: ErrorCopying=Radusies kļūda, pārkopējot datni: ErrorReplacingExistingFile=Radusies kļūda, pārrakstot jau pastāvošo datni: ErrorRestartReplace=Atkārtota aizstāšana cietusi neveiksmi: ErrorRenamingTemp=Radusies kļūda, nomainot nosaukumu datnei galamērķa mapē: ErrorRegisterServer=Neiespējami reģistrēt DLL/OCX: %1 ErrorRegSvr32Failed=Kļūda, palaižot RegSvr32, kods %1 ErrorRegisterTypeLib=Neiespējami reģistrēt tipa bibliotēku: %1 ; *** Uninstall display name markings UninstallDisplayNameMark=%1 (%2) UninstallDisplayNameMarks=%1 (%2, %3) UninstallDisplayNameMark32Bit=32 biti UninstallDisplayNameMark64Bit=64 biti UninstallDisplayNameMarkAllUsers=Visi lietotāji UninstallDisplayNameMarkCurrentUser=Tekošais lietotājs ; *** Post-installation errors ErrorOpeningReadme=Radusies kļūda, atverot README datni. ErrorRestartingComputer=Uzstādīšana nevar pārstartēt datoru. Lūdzu, izdariet to manuāli. ; *** Uninstaller messages UninstallNotFound=Datne "%1" nepastāv. Nevar noņemt. UninstallOpenError=Datni "%1" nevar atvērt. Nevar noņemt UninstallUnsupportedVer=Noņemšanas datne "%1" nav atpazīstama šai noņemšanas programmai. Nevar noņemt UninstallUnknownEntry=Nezināms ieraksts (%1) izveidoja sadursmi ar noņemšanu ConfirmUninstall=Vai esat pārliecināts, ka vēlaties pilnībā noņemt %1 un visus tā komponentus? UninstallOnlyOnWin64=Noņemšanu var veikt tikai ar 64-bitu Windows. OnlyAdminCanUninstall=Noņemšanu var veikt tikai lietotājs ar Adminstratora privilēģijām. UninstallStatusLabel=Lūdzu uzgaidiet, kamēr %1 tiek noņemts no Jūsu datora. UninstalledAll=%1 tika veiksmīgi noņemts no Jūsu datora. UninstalledMost=%1 noņemšana pabeigta.%n%nDažus elementus nevarēja noņemt. Tos var noņemt manuāli. UninstalledAndNeedsRestart=Lai pabeigtu %1 noņemšanu, Jūsu dators jāpārstartē.%n%nVai vēlaties to darīt tagad? UninstallDataCorrupted="%1" datne ir bojāta. Nevar noņemt ; *** Uninstallation phase messages ConfirmDeleteSharedFileTitle=Noņemt kopīgo datni? ConfirmDeleteSharedFile2=Sistēma ir secinājusi, ka šī koplietošanas datne vairs netiks lietota. Vai vēlaties to noņemt?%n%nJa kāda cita programma izmanto šo datni, tad šī programma var strādāt nekorekti. Ja neesat drošs, izvēlieties "Nē". Atstājot šo datni, Jūsu datoram netiks nodarīti nekādi bojājumi. SharedFileNameLabel=Faila nosaukums: SharedFileLocationLabel=Atrašanās vieta: WizardUninstalling=Noņemšanas statuss StatusUninstalling=Noņem %1... ; *** Shutdown block reasons ShutdownBlockReasonInstallingApp=%1 uzstādīšana. ShutdownBlockReasonUninstallingApp=%1 noņemšana. ; The custom messages below aren't used by Setup itself, but if you make ; use of them in your scripts, you'll want to translate them. [CustomMessages] NameAndVersion=%1, versija %2 AdditionalIcons=Papildu ikonas: CreateDesktopIcon=Izveidot &darbvisrmas ikonu CreateQuickLaunchIcon=Izveidot &Quick Launch ikonu ProgramOnTheWeb=%1 vietne Internetā UninstallProgram=Noņemt %1 LaunchProgram=Palaist %1 AssocFileExtension=&Apvienot %1 ar %2 faila paplašinājumu AssocingFileExtension=Apvieno %1 ar %2 faila paplašinājumu... AutoStartProgramGroupDescription=Automātiskā palaišana: AutoStartProgram=Automātiski palaist %1 AddonHostProgramNotFound=%1 nav atrasts Jūsu norādītajā mapē.%n%nTomēr turpināt? ================================================ FILE: InnoDependencies/Romanian.isl ================================================ ; *** Inno Setup version 5.5.3+ Romanian messages *** ; Translator : Alexandru Bogdan Munteanu (muntealb@gmail.com) ; ; To download user-contributed translations of this file, go to: ; http://www.jrsoftware.org/files/istrans/ ; ; Note: When translating this text, do not add periods (.) to the end of ; messages that didn't have them already, because on those messages Inno ; Setup adds the periods automatically (appending a period would result in ; two periods being displayed). [LangOptions] ; The following three entries are very important. Be sure to read and ; understand the '[LangOptions] section' topic in the help file. LanguageName=Rom<00E2>n<0103> LanguageID=$0418 LanguageCodePage=1250 ; If the language you are translating to requires special font faces or ; sizes, uncomment any of the following entries and change them accordingly. ;DialogFontName= ;DialogFontSize=8 ;WelcomeFontName=Verdana ;WelcomeFontSize=12 ;TitleFontName=Arial ;TitleFontSize=29 ;CopyrightFontName=Arial ;CopyrightFontSize=8 [Messages] ; *** Application titles SetupAppTitle=Instalare SetupWindowTitle=Instalare - %1 UninstallAppTitle=Dezinstalare UninstallAppFullTitle=Dezinstalare %1 ; *** Misc. common InformationTitle=Informaii ConfirmTitle=Confirmare ErrorTitle=Eroare ; *** SetupLdr messages SetupLdrStartupMessage=Va fi instalat programul %1. Vrei s continui? LdrCannotCreateTemp=Nu pot crea o fil temporar. Instalare abandonat LdrCannotExecTemp=Nu pot executa o fil din dosarul temporar. Instalare abandonat ; *** Startup error messages LastErrorMessage=%1.%n%nEroarea %2: %3 SetupFileMissing=Fila %1 lipsete din dosarul de instalare. Corecteaz problema sau folosete o alt copie a programului. SetupFileCorrupt=Filele de instalare snt stricate (corupte). Folosete o alt copie a programului. SetupFileCorruptOrWrongVer=Filele de instalare snt stricate (corupte) sau snt incompatibile cu aceast versiune a Instalatorului. Remediaz problema sau folosete o alt copie a programului. InvalidParameter=Un parametru invalid a fost trecut ctre linia de comand:%n%n%1 SetupAlreadyRunning=Instalarea ruleaz deja. WindowsVersionNotSupported=Acest program nu suport versiunea de Windows care ruleaz pe calculatorul tu. WindowsServicePackRequired=Acest program necesit %1 Service Pack %2 sau mai nou. NotOnThisPlatform=Acest program nu va rula pe %1. OnlyOnThisPlatform=Acest program trebuie s ruleze pe %1. OnlyOnTheseArchitectures=Acest program poate fi instalat doar pe versiuni de Windows proiectate pentru urmtoarele arhitecturi de procesor:%n%n%1 MissingWOW64APIs=Versiunea de Windows pe care o rulezi nu include funcionalitatea cerut de Instalator pentru a realiza o instalare pe 64-bii. Pentru a corecta problema, va trebui s instalezi Service Pack %1. WinVersionTooLowError=Acest program necesit %1 versiunea %2 sau mai nou. WinVersionTooHighError=Acest program nu poate fi instalat pe %1 versiunea %2 sau mai nou. AdminPrivilegesRequired=Trebuie s fii logat ca Administrator pentru instalarea acestui program. PowerUserPrivilegesRequired=Trebuie s fii logat ca Administrator sau ca Membru al Grupului de Utilizatori Pricepui ("Power Users") pentru a instala acest program. SetupAppRunningError=Instalatorul a detectat c %1 ruleaz n acest moment.%n%nnchide toate instanele programului respectiv, apoi clicheaz OK pentru a continua sau Anuleaz pentru a abandona instalarea. UninstallAppRunningError=Dezinstalatorul a detectat c %1 ruleaz n acest moment.%n%nnchide toate instanele programului respectiv, apoi clicheaz OK pentru a continua sau Anuleaz pentru a abandona dezinstalarea. ; *** Misc. errors ErrorCreatingDir=Instalatorul nu a putut crea dosarul "%1" ErrorTooManyFilesInDir=Nu pot crea o fil n dosarul "%1" din cauz c are deja prea multe file ; *** Setup common messages ExitSetupTitle=Abandonarea Instalrii ExitSetupMessage=Instalarea nu este terminat. Dac o abandonezi acum, programul nu va fi instalat.%n%nPoi s rulezi Instalatorul din nou alt dat pentru a termina instalarea.%n%nAbandonezi Instalarea? AboutSetupMenuItem=&Despre Instalator... AboutSetupTitle=Despre Instalator AboutSetupMessage=%1 versiunea %2%n%3%n%n%1 sit:%n%4 AboutSetupNote= TranslatorNote= ; *** Buttons ButtonBack=< na&poi ButtonNext=&Continu > ButtonInstall=&Instaleaz ButtonOK=OK ButtonCancel=Anuleaz ButtonYes=&Da ButtonYesToAll=Da la &Tot ButtonNo=&Nu ButtonNoToAll=N&u la Tot ButtonFinish=&Finalizeaz ButtonBrowse=&Exploreaz... ButtonWizardBrowse=Explo&reaz... ButtonNewFolder=Creea&z Dosar Nou ; *** "Select Language" dialog messages SelectLanguageTitle=Selectarea Limbii Instalatorului SelectLanguageLabel=Selecteaz limba folosit pentru instalare: ; *** Common wizard text ClickNext=Clicheaz pe Continu pentru a avansa cu instalarea sau pe Anuleaz pentru a o abandona. BeveledLabel= BrowseDialogTitle=Explorare dup Dosar BrowseDialogLabel=Selecteaz un dosar din lista de mai jos, apoi clicheaz pe OK. NewFolderName=Dosar Nou ; *** "Welcome" wizard page WelcomeLabel1=Bun venit la Instalarea [name] WelcomeLabel2=Programul [name/ver] va fi instalat pe calculator.%n%nEste recomandat s nchizi toate celelalte aplicaii nainte de a continua. ; *** "Password" wizard page WizardPassword=Parol PasswordLabel1=Aceast instalare este protejat prin parol. PasswordLabel3=Completeaz parola, apoi clicheaz pe Continu pentru a merge mai departe. Tipul literelor din parol (Majuscule/minuscule) este luat n considerare. PasswordEditLabel=&Parol: IncorrectPassword=Parola pe care ai introdus-o nu este corect. Rencearc. ; *** "License Agreement" wizard page WizardLicense=Acord de Liceniere LicenseLabel=Citete informaiile urmtoare nainte de a continua, snt importante. LicenseLabel3=Citete urmtorul Acord de Liceniere. Trebuie s accepi termenii acestui acord nainte de a continua instalarea. LicenseAccepted=&Accept licena LicenseNotAccepted=&Nu accept licena ; *** "Information" wizard pages WizardInfoBefore=Informaii InfoBeforeLabel=Citete informaiile urmtoare nainte de a continua, snt importante. InfoBeforeClickLabel=Cnd eti gata de a trece la Instalare, clicheaz pe Continu. WizardInfoAfter=Informaii InfoAfterLabel=Citete informaiile urmtoare nainte de a continua, snt importante. InfoAfterClickLabel=Cnd eti gata de a trece la Instalare, clicheaz pe Continu. ; *** "User Information" wizard page WizardUserInfo=Informaii despre Utilizator UserInfoDesc=Completeaz informaiile cerute. UserInfoName=&Utilizator: UserInfoOrg=&Organizaie: UserInfoSerial=Numr de &Serie: UserInfoNameRequired=Trebuie s introduci un nume. ; *** "Select Destination Location" wizard page WizardSelectDir=Selectarea Locului de Destinaie SelectDirDesc=Unde vrei s instalezi [name]? SelectDirLabel3=Instalatorul va pune [name] n dosarul specificat mai jos. SelectDirBrowseLabel=Pentru a avansa cu instalarea, clicheaz pe Continu. Dac vrei s selectezi un alt dosar, clicheaz pe Exploreaz. DiskSpaceMBLabel=Este necesar un spaiu liber de stocare de cel puin [mb] MB. CannotInstallToNetworkDrive=Instalatorul nu poate realiza instalarea pe un dispozitiv de reea. CannotInstallToUNCPath=Instalatorul nu poate realiza instalarea pe o cale n format UNC. InvalidPath=Trebuie s introduci o cale complet, inclusiv litera dispozitivului; de exemplu:%n%nC:\APP%n%nsau o cale UNC de forma:%n%n\\server\share InvalidDrive=Dispozitivul sau partajul UNC pe care l-ai selectat nu exist sau nu este accesibil. Selecteaz altul. DiskSpaceWarningTitle=Spaiu de Stocare Insuficient DiskSpaceWarning=Instalarea necesit cel puin %1 KB de spaiu de stocare liber, dar dispozitivul selectat are doar %2 KB liberi.%n%nVrei s continui oricum? DirNameTooLong=Numele dosarului sau al cii este prea lung. InvalidDirName=Numele dosarului nu este valid. BadDirName32=Numele dosarelor nu pot include unul din urmtoarele caractere:%n%n%1 DirExistsTitle=Dosarul Exist DirExists=Dosarul:%n%n%1%n%nexist deja. Vrei totui s instalezi n acel dosar? DirDoesntExistTitle=Dosarul Nu Exist DirDoesntExist=Dosarul:%n%n%1%n%nnu exist. Vrei ca el s fie creat? ; *** "Select Components" wizard page WizardSelectComponents=Selectarea Componentelor SelectComponentsDesc=Care dintre componente trebuie instalate? SelectComponentsLabel2=Selecteaz componentele de instalat; deselecteaz componentele care nu trebuie instalate. Clicheaz pe Continu pentru a merge mai departe. FullInstallation=Instalare Complet ; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language) CompactInstallation=Instalare Compact CustomInstallation=Instalare Personalizat NoUninstallWarningTitle=Componentele Exist NoUninstallWarning=Instalatorul a detectat c urmtoarele componente snt deja instalate pe calculator:%n%n%1%n%nDeselectarea lor nu le va dezinstala.%n%nVrei s continui oricum? ComponentSize1=%1 KB ComponentSize2=%1 MB ComponentsDiskSpaceMBLabel=Selecia curent necesit cel puin [mb] MB spaiu de stocare. ; *** "Select Additional Tasks" wizard page WizardSelectTasks=Selectarea Sarcinilor Suplimentare SelectTasksDesc=Ce sarcini suplimentare trebuie ndeplinite? SelectTasksLabel2=Selecteaz sarcinile suplimentare care trebuie ndeplinite n timpul instalrii [name], apoi clicheaz pe Continu. ; *** "Select Start Menu Folder" wizard page WizardSelectProgramGroup=Selectarea Dosarului din Meniul de Start SelectStartMenuFolderDesc=Unde trebuie s fie plasate scurtturile programului? SelectStartMenuFolderLabel3=Scurtturile vor fi plasate n dosarul specificat mai jos al Meniului de Start. SelectStartMenuFolderBrowseLabel=Pentru a avansa cu instalarea, clicheaz pe Continu. Dac vrei s selectezi un alt dosar, clicheaz pe Exploreaz. MustEnterGroupName=Trebuie s introduci numele dosarului. GroupNameTooLong=Numele dosarului sau al cii este prea lung. InvalidGroupName=Numele dosarului nu este valid. BadGroupName=Numele dosarului nu poate include unul dintre caracterele urmtoarele:%n%n%1 NoProgramGroupCheck2=Nu crea un &dosar n Meniul de Start ; *** "Ready to Install" wizard page WizardReady=Pregtit de Instalare ReadyLabel1=Instalatorul e pregtit pentru instalarea [name] pe calculator. ReadyLabel2a=Clicheaz pe Instaleaz pentru a continua cu instalarea, sau clicheaz pe napoi dac vrei s revezi sau s schimbi setrile. ReadyLabel2b=Clicheaz pe Instaleaz pentru a continua cu instalarea. ReadyMemoUserInfo=Info Utilizator: ReadyMemoDir=Loc de Destinaie: ReadyMemoType=Tip de Instalare: ReadyMemoComponents=Componente Selectate: ReadyMemoGroup=Dosarul Meniului de Start: ReadyMemoTasks=Sarcini Suplimentare: ; *** "Preparing to Install" wizard page WizardPreparing=Pregtire pentru Instalare PreparingDesc=Instalatorul pregtete instalarea [name] pe calculator. PreviousInstallNotCompleted=Instalarea/dezinstalarea anterioar a unui program nu a fost terminat. Va trebui s reporneti calculatorul pentru a termina operaia precedent.%n%nDup repornirea calculatorului, ruleaz Instalatorul din nou pentru a realiza instalarea [name]. CannotContinue=Instalarea nu poate continua. Clicheaz pe Anuleaz pentru a o nchide. ApplicationsFound=Aplicaiile urmtoare folosesc file care trebuie actualizate de ctre Instalator. Este recomandat s permii Instalatorului s nchid automat aplicaiile respective. ApplicationsFound2=Aplicaiile urmtoare folosesc file care trebuie actualizate de ctre Instalator. Este recomandat s permii Instalatorului s nchid automat aplicaiile respective. Dup ce instalarea e terminat, Instalatorul va ncerca s reporneasc aplicaiile. CloseApplications=nchide &automat aplicaiile DontCloseApplications=Nu nchi&de aplicaiile ErrorCloseApplications=Instalatorul nu a putut nchide automat toate aplicaiile. nainte de a continua, e recomandat s nchizi manual toate aplicaiile care folosesc file ce trebuie actualizate de Instalator. ; *** "Installing" wizard page WizardInstalling=Instalare n Desfurare InstallingLabel=Ateapt s se termine instalarea [name] pe calculator. ; *** "Setup Completed" wizard page FinishedHeadingLabel=Finalizarea Instalrii [name] FinishedLabelNoIcons=Instalarea [name] pe calculator a fost terminat. FinishedLabel=Instalarea [name] pe calculator a fost terminat. Aplicaia poate fi lansat prin clicarea pe icoanele instalate. ClickFinish=Clicheaz pe Finalizeaz pentru a prsi Instalatorul. FinishedRestartLabel=Pentru a termina instalarea [name], trebuie repornit calculatorul. Vrei s fie repornit acum? FinishedRestartMessage=Pentru a termina instalarea [name], trebuie repornit calculatorul.%n%nVrei s fie repornit acum? ShowReadmeCheck=Da, vreau s vd fila de informare (README) YesRadio=&Da, repornete calculatorul acum NoRadio=&Nu, voi reporni eu calculatorul mai trziu ; used for example as 'Run MyProg.exe' RunEntryExec=Ruleaz %1 ; used for example as 'View Readme.txt' RunEntryShellExec=Vezi %1 ; *** "Setup Needs the Next Disk" stuff ChangeDiskTitle=Instalatorul Necesit Discul Urmtor SelectDiskLabel2=Introdu Discul %1 i clicheaz pe OK.%n%nDac filele de pe acest disc pot fi gsite ntr-un alt dosar dect cel afiat mai jos, introdu calea corect sau clicheaz pe Exploreaz. PathLabel=&Cale: FileNotInDir2=Fila "%1" nu poate fi gsit n "%2". Introdu discul corect sau selecteaz alt dosar. SelectDirectoryLabel=Specific locul discului urmtor. ; *** Installation phase messages SetupAborted=Instalarea nu a fost terminat.%n%nCorecteaz problema i apoi ruleaz Instalarea din nou. EntryAbortRetryIgnore=Clicheaz pe Rencearc pentru a ncerca din nou, pe Ignor pentru a continua oricum, sau pe Abandoneaz pentru a anula instalarea. ; *** Installation status messages StatusClosingApplications=nchid aplicaiile... StatusCreateDirs=Creez dosarele... StatusExtractFiles=Extrag filele... StatusCreateIcons=Creez scurtturile... StatusCreateIniEntries=Creez intrrile INI... StatusCreateRegistryEntries=Creez intrrile n registru... StatusRegisterFiles=nregistrez filele... StatusSavingUninstall=Salvez informaiile de dezinstalare... StatusRunProgram=Finalizez instalarea... StatusRestartingApplications=Repornesc aplicaiile... StatusRollback=Rentorc la starea iniial, prin anularea modificrilor fcute... ; *** Misc. errors ErrorInternal2=Eroare Intern: %1 ErrorFunctionFailedNoCode=%1 a euat ErrorFunctionFailed=%1 a euat; cod %2 ErrorFunctionFailedWithMessage=%1 a euat; cod %2.%n%3 ErrorExecutingProgram=Nu pot executa fila:%n%1 ; *** Registry errors ErrorRegOpenKey=Eroare la deschiderea cheii de registru:%n%1\%2 ErrorRegCreateKey=Eroare la crearea cheii de registru:%n%1\%2 ErrorRegWriteKey=Eroare la scrierea n cheia de registru:%n%1\%2 ; *** INI errors ErrorIniEntry=Eroare la crearea intrrii INI n fiierul "%1". ; *** File copying errors FileAbortRetryIgnore=Clicheaz pe Rencearc pentru a ncerca din nou, pe Ignor pentru a sri aceast fil (nerecomandat), sau pe Abandoneaz pentru a anula instalarea. FileAbortRetryIgnore2=Clicheaz pe Rencearc pentru a ncerca din nou, pe Ignor pentru a continua oricum (nerecomandat), sau pe Abandoneaz pentru a anula instalarea. SourceIsCorrupted=Fila surs este stricat (corupt) SourceDoesntExist=Fila surs "%1" nu exist ExistingFileReadOnly=Fila deja existent este marcat doar-citire.%n%nClicheaz pe Rencearc pentru a nltura atributul doar-citire i a ncerca din nou, pe Ignor pentru a sri aceast fil, sau pe Abandoneaz pentru a anula instalarea. ErrorReadingExistingDest=A aprut o eroare n timpul citirii filei deja existente: FileExists=Fila exist deja.%n%Vrei ca ea s fie suprascris de Instalator? ExistingFileNewer=Fila deja existent este mai nou dect cea care trebuie instalat. Este recomandat s-o pstrezi pe cea existent.%n%nVrei s pstrezi fila deja existent? ErrorChangingAttr=A aprut o eroare n timpul schimbrii atributelor filei deja existente: ErrorCreatingTemp=A aprut o eroare n timpul crerii filei n dosarul de destinaie: ErrorReadingSource=A aprut o eroare n timpul citirii filei surs: ErrorCopying=A aprut o eroare n timpul copierii filei: ErrorReplacingExistingFile=A aprut o eroare n timpul nlocuirii filei deja existente: ErrorRestartReplace=Repornirea/nlocuirea a euat: ErrorRenamingTemp=A aprut o eroare n timpul renumirii unei file din dosarul de destinaie: ErrorRegisterServer=Nu pot nregistra DLL/OCX: %1 ErrorRegSvr32Failed=RegSvr32 a euat, avnd codul de ieire %1 ErrorRegisterTypeLib=Nu pot nregistra biblioteca de tipuri: %1 ; *** Post-installation errors ErrorOpeningReadme=A aprut o eroare la deschiderea filei de informare (README). ErrorRestartingComputer=Instalatorul nu a putut reporni calculatorul. Va trebui s-l reporneti manual. ; *** Uninstaller messages UninstallNotFound=Fila "%1" nu exist. Dezinstalarea nu poate fi fcut. UninstallOpenError=Fila "%1" nu poate fi deschis. Dezinstalarea nu poate fi fcut UninstallUnsupportedVer=Fila "%1" ce conine jurnalul de dezinstalare este ntr-un format nerecunoscut de aceast versiune a dezinstalatorului. Dezinstalarea nu poate fi fcut UninstallUnknownEntry=A fost ntlnit o intrare necunoscut (%1) n jurnalul de dezinstalare ConfirmUninstall=Sigur vrei s nlturi complet %1 i componentele sale? UninstallOnlyOnWin64=Aceast instalare poate fi dezinstalat doar pe un sistem Windows 64-bii. OnlyAdminCanUninstall=Aceast instalare poate fi dezinstalat doar de ctre un utilizator cu drepturi de Administrator. UninstallStatusLabel=Ateapt ca %1 s fie nlturat de pe calculator. UninstalledAll=%1 a fost nlturat cu succes de pe calculator. UninstalledMost=Dezinstalare complet a %1.%n%nAnumite elemente nu au putut fi nlturate. Acestea pot fi nlturate manual. UninstalledAndNeedsRestart=Pentru a termina dezinstalarea %1, calculatorul trebuie repornit.%n%nVrei s fie repornit acum? UninstallDataCorrupted=Fila "%1" este stricat (corupt). Dezinstalarea nu poate fi fcut ; *** Uninstallation phase messages ConfirmDeleteSharedFileTitle=terg Fila Partajat? ConfirmDeleteSharedFile2=Sistemul indic faptul c fila partajat urmtoare pare s nu mai fie folosit de vreun alt program. Vrei ca Dezinstalatorul s tearg aceast fil partajat?%n%nDac totui mai exist programe care folosesc fila i ea este tears, acele programe ar putea s funcioneze greit. Dac nu eti sigur, alege Nu. Lsarea filei n sistem nu va produce nici o neplcere. SharedFileNameLabel=Nume Fil: SharedFileLocationLabel=Loc: WizardUninstalling=Starea Dezinstalrii StatusUninstalling=Dezinstalez %1... ; *** Shutdown block reasons ShutdownBlockReasonInstallingApp=Instalez %1. ShutdownBlockReasonUninstallingApp=Dezinstalez %1. ; The custom messages below aren't used by Setup itself, but if you make ; use of them in your scripts, you'll want to translate them. [CustomMessages] NameAndVersion=%1 versiunea %2 AdditionalIcons=Icoane suplimentare: CreateDesktopIcon=Creeaz o icoan pe &Birou ("Desktop") CreateQuickLaunchIcon=Creeaz o icoan n Bara de &Lansare Rapid ("Quick Launch") ProgramOnTheWeb=%1 pe internet UninstallProgram=Dezinstaleaz %1 LaunchProgram=Lanseaz %1 AssocFileExtension=&Asociaz %1 cu extensia de file %2 AssocingFileExtension=Asociez %1 cu extensia de file %2... AutoStartProgramGroupDescription=Pornire: AutoStartProgram=Pornete automat %1 AddonHostProgramNotFound=%1 nu poate fi gsit n dosarul selectat.%n%nVrei s continui oricum? ================================================ FILE: InnoDependencies/Vietnamese.isl ================================================ ; *** Inno Setup version 6.1.0+ Vietnamese messages *** ; Translated by Vu Khac Hiep (email: vukhachiep@gmail.com) ; To download user-contributed translations of this file, go to: ; https://jrsoftware.org/files/istrans/ ; ; Note: When translating this text, do not add periods (.) to the end of ; messages that didn't have them already, because on those messages Inno ; Setup adds the periods automatically (appending a period would result in ; two periods being displayed). [LangOptions] ; The following three entries are very important. Be sure to read and ; understand the '[LangOptions] section' topic in the help file. LanguageName=Vietnamese LanguageID=$042A LanguageCodePage=0 ; If the language you are translating to requires special font faces or ; sizes, uncomment any of the following entries and change them accordingly. ;DialogFontName= ;DialogFontSize=8 ;WelcomeFontName=Verdana ;WelcomeFontSize=12 ;TitleFontName=Arial ;TitleFontSize=29 ;CopyrightFontName=Arial ;CopyrightFontSize=8 [Messages] ; *** Application titles SetupAppTitle=Cài đặt SetupWindowTitle=Cài đặt - %1 UninstallAppTitle=Gỡ cài đặt UninstallAppFullTitle=Gỡ cài đặt - %1 ; *** Misc. common InformationTitle=Thông tin ConfirmTitle=Xác nhận ErrorTitle=Lỗi ; *** SetupLdr messages SetupLdrStartupMessage=Chương trình này sẽ cài đặt %1. Bạn có muốn tiếp tục không? LdrCannotCreateTemp=Không thể tạo tệp tạm thời. Cài đặt bị hủy bỏ LdrCannotExecTemp=Không thể chạy tệp trong thư mục tạm thời. Cài đặt bị hủy bỏ HelpTextNote= ; *** Startup error messages LastErrorMessage=%1.%n%nLỗi %2: %3 SetupFileMissing=Tệp %1 bị thiếu trong thư mục cài đặt. Hãy sửa lỗi hoặc lấy một bản sao mới của chương trình. SetupFileCorrupt=Các tệp cài đặt đã bị hỏng. Hãy sửa lỗi hoặc lấy một bản sao của chương trình. SetupFileCorruptOrWrongVer=Các tệp cài đặt bị hỏng, hoặc không tương thích với bản cài đặt này. Hãy sửa lỗi hoặc lấy một bản sao mới của chương trình. InvalidParameter=Một thông số không hợp lệ đã được đưa vào dòng lệnh:%n%n%1 SetupAlreadyRunning=Cài đặt này đang chạy. WindowsVersionNotSupported=Chương trình này không tương thích với phiên bản Windows bạn đang chạy. WindowsServicePackRequired=Chương trình này yêu cầu %1 Service Pack %2 hoặc mới hơn. NotOnThisPlatform=Chương trình này sẽ không chạy trên %1. OnlyOnThisPlatform=Chương trình này phải chạy trên %1. OnlyOnTheseArchitectures=Chương trình này chỉ có thể được cài đặt trên phiên bản Windows được thiết kế cho các hệ vi xử lí:%n%n%1 WinVersionTooLowError=Chương trình này yêu cầu %1 phiên bản %2 hoặc mới hơn. WinVersionTooHighError=Chương trình này không thể được cài đặt trên %1 phiên bản %2 hoặc mới hơn. AdminPrivilegesRequired=Bạn phải được đăng nhập như người quản trị khi cài đặt chương trình này. PowerUserPrivilegesRequired=Bạn phải được đăng nhập như người quản trị hoặc thành viên trong nhóm Người dùng mạnh khi cài đặt chương trình này. SetupAppRunningError=Cài đặt phát hiện %1 đang chạy.%n%nHãy đóng tất cả các tiến trình của nó ngay, rồi click OK để tiếp tục, hoặc Hủy để thoát. UninstallAppRunningError=Gỡ cài đặt phát hiện %1 đang chạy.%n%nHãy đóng tất cả các tiến trình của nó ngay, rồi click OK để tiếp tục, hoặc Hủy để thoát. ; *** Startup questions PrivilegesRequiredOverrideTitle=Select Setup Install Mode PrivilegesRequiredOverrideInstruction=Select install mode PrivilegesRequiredOverrideText1=%1 can be installed for all users (requires administrative privileges), or for you only. PrivilegesRequiredOverrideText2=%1 can be installed for you only, or for all users (requires administrative privileges). PrivilegesRequiredOverrideAllUsers=Install for &all users PrivilegesRequiredOverrideAllUsersRecommended=Install for &all users (recommended) PrivilegesRequiredOverrideCurrentUser=Install for &me only PrivilegesRequiredOverrideCurrentUserRecommended=Install for &me only (recommended) ; *** Misc. errors ErrorCreatingDir=Cài đặt không thể tạo ra thư mục "%1" ErrorTooManyFilesInDir=Không thể tạo một tệp trong thư mục "%1" vì nó chứa quá nhiều tệp ; *** Setup common messages ExitSetupTitle=Thoát cài đặt ExitSetupMessage=Cài đặt chưa hoàn thành. Nếu bạn thoát bây giờ, chương trình sẽ không được cài đặt.%n%nBạn có thể chạy lại Cài đặt một lần khác để hoàn thành cài đặt.%n%nThoát ngay? AboutSetupMenuItem=&Về trình cài đặt... AboutSetupTitle=Về trình cài đặt AboutSetupMessage=%1 phiên bản %2%n%3%n%n%1 trang chủ:%n%4 AboutSetupNote= TranslatorNote=Giao diện người dùng tiếng Việt bởi: Vũ Khắc Hiệp ; *** Buttons ButtonBack=< &Trước ButtonNext=T&iếp > ButtonInstall=&Cài đặt ButtonOK=OK ButtonCancel=Hủy ButtonYes=&Có ButtonYesToAll=Có c&ho tất cả ButtonNo=&Không ButtonNoToAll=Khô&ng cho tất cả ButtonFinish=&Hoàn thành ButtonBrowse=&Duyệt... ButtonWizardBrowse=D&uyệt... ButtonNewFolder=Tạ&o thư mục mới ; *** "Select Language" dialog messages SelectLanguageTitle=Chọn ngôn ngữ cài đặt SelectLanguageLabel=Chọn ngôn ngữ để sử dụng khi cài đặt: ; *** Common wizard text ClickNext=Nhấn Tiếp để tiếp tục, hoặc Hủy để thoát cài đặt BeveledLabel= BrowseDialogTitle=Tìm thư mục BrowseDialogLabel=Chọn một thư mục trong danh sách sau rồi ấn OK. NewFolderName=Tạo thư mục mới ; *** "Welcome" wizard page WelcomeLabel1=Chào mừng tới trình cài đặt [name] WelcomeLabel2=Chương trình này sẽ cài [name/ver] trên máy tính của bạn.%n%nChúng tôi khuyên bạn đóng mọi chương trình khác lại trước khi cài đặt. ; *** "Password" wizard page WizardPassword=Mật khẩu PasswordLabel1=Việc cài đặt được bảo vệ bằng mật khẩu. PasswordLabel3=Hãy nhập mật khẩu, rồi nhấn Tiếp để tiếp tục. Mật khẩu phân biệt chữ hoa/thường. PasswordEditLabel=&Mật khẩu: IncorrectPassword=Mật khẩu bạn đã nhập không đúng. Hãy thử lại. ; *** "License Agreement" wizard page WizardLicense=Thỏa thuận cấp phép LicenseLabel=Hãy đọc những thông tin quan trọng sau trước khi tiếp tục. LicenseLabel3=Hãy đọc Thỏa thuận cấp phép sau. Bạn phải chấp nhận các điều khoản của cài đặt này trước khi tiếp tục. LicenseAccepted=Tô&i chấp nhận thỏa thuận LicenseNotAccepted=Tôi khôn&g chấp nhận thỏa thuận ; *** "Information" wizard pages WizardInfoBefore=Thông tin InfoBeforeLabel=Hãy đọc những thông tin quan trọng sau trước khi tiếp tục. InfoBeforeClickLabel=Khi bạn đã sẵn sàng cài đặt tiếp, click Tiếp. WizardInfoAfter=Thông tin InfoAfterLabel=Hãy đọc những thông tin quan trọng sau trước khi tiếp tục. InfoAfterClickLabel=Khi bạn đã sẵn sàng cài đặt tiếp, click Tiếp. ; *** "User Information" wizard page WizardUserInfo=Thông tin người dùng UserInfoDesc=Hãy nhập thông tin của bạn. UserInfoName=Tên n&gười dùng: UserInfoOrg=Tổ c&hức: UserInfoSerial=&Số serial: UserInfoNameRequired=Bạn phải nhập một tên. ; *** "Select Destination Location" wizard page WizardSelectDir=Chọn vị trí cài đặt SelectDirDesc=[name] nên được cài đặt ở đâu? SelectDirLabel3=[name] sẽ được cài đặt vào thư mục sau: SelectDirBrowseLabel=Để tiếp tục. nhấn Tiếp. Nếu bạn muốn chọn một thư mục khác, nhấn Duyệt. DiskSpaceGBLabel=Cần có ít nhất [gb] GB ổ đĩa trống. DiskSpaceMBLabel=Cần có ít nhất [mb] MB ổ đĩa trống. CannotInstallToNetworkDrive=Cài đặt không thể cài vào một ổ đĩa mạng. CannotInstallToUNCPath=Cài đặt không thể cài vào đường dẫn UNC. InvalidPath=Bạn phải nhập đường dẫn đầy đủ với chữ cái ổ đĩa, ví dụ:%n%nC:\APP%n%nhoặc một đường dẫn UNC theo mẫu:%n%n\\server\share InvalidDrive=Ổ đĩa hoặc chia sẻ UNC bạn đã chọn không tồn tại hoặc không truy cập được. Hãy chọn cái khác. DiskSpaceWarningTitle=Không đủ dung lượng đĩa DiskSpaceWarning=Cài đặt yêu cầu ít nhất %1 KB dung lượng trống để cài đặt, nhưng ổ đĩa đã chọn chỉ còn %2KB.%n%nBạn muốn tiếp tục bằng mọi giá? DirNameTooLong=Tên thư mục hoặc đường dẫn quá dài. InvalidDirName=Tên thư mục không hợp lệ. BadDirName32=Tên thư mục không được chứa các kí tự sau:%n%n%1 DirExistsTitle=Thư mục đã tồn tại DirExists=Thư mục:%n%n%1%n%nđã tồn tại. Bạn có muốn cài đặt vào thư mục đó bằng mọi giá? DirDoesntExistTitle=Thư mục không tồn tại DirDoesntExist=Thư mục:%n%n%1%n%nkhông tồn tại. Bạn có muốn tạo thư mục không? ; *** "Select Components" wizard page WizardSelectComponents=Chọn các thành phần SelectComponentsDesc=Những thành phần nào nên được cài đặt? SelectComponentsLabel2=Chọn các thành phần bạn muốn cài đặt, bỏ chọn các thành phần bạn không muốn. Click Tiếp khi bạn đã sẵn sàng để tiếp tục. FullInstallation=Cài đặt đầy đủ ; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language) CompactInstallation=Cài đặt rút gọn CustomInstallation=Cài đặt tủy chỉnh NoUninstallWarningTitle=Thành phần đã tồn tại NoUninstallWarning=Cài đặt phát hiện các thành phần sau đã được cài đặt trên máy tính của bạn:%n%n%1%n%nBỏ chọn những thành phần này sẽ không cài đặt chúng.%n%nBạn có muốn tiếp tục bằng mọi giá? ComponentSize1=%1 KB ComponentSize2=%1 MB ComponentsDiskSpaceGBLabel=Lựa chọn này yêu cầu ít nhất [gb] GB không gian đĩa. ComponentsDiskSpaceMBLabel=Lựa chọn này yêu cầu ít nhất [mb] MB không gian đĩa. ; *** "Select Additional Tasks" wizard page WizardSelectTasks=Chọn các tác vụ bổ sung SelectTasksDesc=Các tác vụ bổ sung nào nên được thực hiện? SelectTasksLabel2=Chọn các tác vụ bổ sung mà bạn muốn cài đặt thực hiện khi cài đặt [name], rồi nhấn Tiếp. ; *** "Select Start Menu Folder" wizard page WizardSelectProgramGroup=Chọn thư mục bắt đầu SelectStartMenuFolderDesc=Các lối tắt đến chương trình nên được đặt ở đâu? SelectStartMenuFolderLabel3=Cài đặt sẽ tạo các lối tắt đến chương trình trong thư mục bắt đầu sau. SelectStartMenuFolderBrowseLabel=Để tiếp tục, click Tiếp. Nếu bạn muốn chọn thư mục khác, click Duyệt. MustEnterGroupName=Bạn phải nhập tên một thư mục. GroupNameTooLong=Tên thư mục hoặc đường dẫn quá dài. InvalidGroupName=Tên thư mục không hợp lệ. BadGroupName=Tên thư mục không được chứa các kí tự sau:%n%n%1 NoProgramGroupCheck2=&Không tạo thư mục bắt đầu ; *** "Ready to Install" wizard page WizardReady=Sẵn sàng cài đặt ReadyLabel1=[name] đã sẵn sàng để dược cài đặt trên máy tính của bạn. ReadyLabel2a=Click Cài đặt để tiếp tục, hoặc click Trước nếu bạn muốn xem lại/thay đổi bất kì cài đặt nào. ReadyLabel2b=Click Cài đặt để tiếp tục cài đặt. ReadyMemoUserInfo=Thông tin người dùng: ReadyMemoDir=Vị trí đích: ReadyMemoType=Kiểu cài đặt: ReadyMemoComponents=Các thành phần được chọn: ReadyMemoGroup=Thư mục bắt đầu: ReadyMemoTasks=Các tác vụ bổ sung: ; *** TDownloadWizardPage wizard page and DownloadTemporaryFile DownloadingLabel=Đang tải các tập tin bổ sung... ButtonStopDownload=&Dừng tải xuống StopDownload=Bạn có chắc chắn muốn dừng tải xuống không? ErrorDownloadAborted=Tải xuống bị hủy bỏ ErrorDownloadFailed=Tải xuống không thành công: %1 %2 ErrorDownloadSizeFailed=Getting size failed: %1 %2 ErrorFileHash1=File hash failed: %1 ErrorFileHash2=Invalid file hash: expected %1, found %2 ErrorProgress=Invalid progress: %1 of %2 ErrorFileSize=Invalid file size: expected %1, found %2 ; *** "Preparing to Install" wizard page WizardPreparing=Chuẩn bị cài đặt PreparingDesc=[name] đang chuẩn bị được cài đặt trên máy tính của bạn. PreviousInstallNotCompleted=Việc cài đặt/gỡ bỏ một chương trình chưa được hoàn tất trước đó. Bạn sẽ phải khởi động lại máy tính để hoàn tất cài đặt đó.%n%nSau khi chởi động lại, chạy Cài đặt một lần nữa để hoàn tất cài đặt [name]. CannotContinue=Cài đặt không thể tiếp tục. Nhấn Hủy để thoát. ApplicationsFound=Những chương trình sau đang sử dụng các tệp cần được cập nhật bởi trình cài đặt. Chúng tôi khuyên bạn cho phép Cài đặt đóng các chương trình này. ApplicationsFound2=Những chương trình sau đang sử dụng các tệp cần được cập nhật bởi trình cài đặt. Chúng tôi khuyên bạn cho phép Cài đặt đóng các chương trình này. Sau khi hoàn thành cài đặt, chúng tôi sẽ thử khởi động lại các chương trình này. CloseApplications=Tự độn&g đóng các chương trình này DontCloseApplications=Không đóng các chương t&rình này ErrorCloseApplications=Cài đặt không thể đóng mọi chương trình. Chúng tôi khuyên bạn đóng các chương trình đang sử dụng các tệp cần được cập nhật bởi Cài đặt một cách thủ công trước khi tiếp tục. PrepareToInstallNeedsRestart=Setup must restart your computer. After restarting your computer, run Setup again to complete the installation of [name].%n%nWould you like to restart now? ; *** "Installing" wizard page WizardInstalling=Đang cài đặt InstallingLabel=Hãy đợi khi [name] đang được cài đặt trên máy tính của bạn. ; *** "Setup Completed" wizard page FinishedHeadingLabel=Hoàn thành cài đặt [name] FinishedLabelNoIcons=[name] đã được cài đặt xong trên máy tính của bạn. FinishedLabel=[name] đã được cài đặt xong trên máy tính của bạn. Chương trình có thể được khởi động bằng cách click vào lối tắt đến chương trình. ClickFinish=Click Hoàn thành để thoát Cài đặt. FinishedRestartLabel=Để hoàn thành cài đặt [name], máy tính của bạn cần đươc khởi động lại. Bạn có muốn khởi động lại ngay? FinishedRestartMessage=Để hoàn thành cài đặt [name], máy tính của bạn cần đươc khởi động lại.%n%nBạn có muốn khởi động lại ngay? ShowReadmeCheck=Có, tôi muốn xem tệp README YesRadio=&Có, khởi động lại máy tính ngay NoRadio=&Không, tôi sẽ khởi động lại máy tính sau ; used for example as 'Run MyProg.exe' RunEntryExec=Chạy %1 ; used for example as 'View Readme.txt' RunEntryShellExec=Xem %1 ; *** "Setup Needs the Next Disk" stuff ChangeDiskTitle=Cài đặt cần đĩa tiếp theo SelectDiskLabel2=Hãy chèn đĩa %1 và click OK.%n%nNếu các tệp trên đĩa này có thể được tìm thấy trên một thư mục khác với được hiển thị dưới đây, nhập đường dẫn hoặc click Duyệt. PathLabel=Đườ&ng dẫn: FileNotInDir2=Tệp "%1" không thể được xác định trong "%2". Hãy chọn đia xđúng hoặc chọn thư mục khác. SelectDirectoryLabel=Hãy chọn vị trí của đĩa tiếp theo. ; *** Installation phase messages SetupAborted=Cài đặt không được hoàn thành.%n%nHãy sửa lỗi và chạy Cài đặt lại. AbortRetryIgnoreSelectAction=Chọn hành động AbortRetryIgnoreRetry=&Thử lại AbortRetryIgnoreIgnore=&Bỏ qua lỗi và tiếp tục AbortRetryIgnoreCancel=Hủy ; *** Installation status messages StatusClosingApplications=Đang đóng các chương trình... StatusCreateDirs=Đang tạo các thư mục... StatusExtractFiles=Đang giải nén các tệp... StatusCreateIcons=Đang tạo các lối tắt... StatusCreateIniEntries=Đang tạo các đầu vào INI... StatusCreateRegistryEntries=Đang tạo các đầu vào registry... StatusRegisterFiles=Đang đăng kí các tệp... StatusSavingUninstall=Đang lưu thông tin gỡ cài đặt... StatusRunProgram=Đang hoàn thành cài đặt... StatusRestartingApplications=Đang khởi động lại các chương trình... StatusRollback=Đang hoàn lại các thay đổi... ; *** Misc. errors ErrorInternal2=Lỗi nội bộ: %1 ErrorFunctionFailedNoCode=%1 thất bại ErrorFunctionFailed=%1 thất bại với mã lỗi %2 ErrorFunctionFailedWithMessage=%1 thất bại với mã lỗi %2.%n%3 ErrorExecutingProgram=Không thể chạy tệp:%n%1 ; *** Registry errors ErrorRegOpenKey=Lỗi khi mở registry:%n%1\%2 ErrorRegCreateKey=Lỗi khi tạo registry:%n%1\%2 ErrorRegWriteKey=Lỗi khi viết registry:%n%1\%2 ; *** INI errors ErrorIniEntry=Lỗi tạo đầu vào INI cho tệp "%1". ; *** File copying errors FileAbortRetryIgnoreSkipNotRecommended=&Bỏ qua tệp này (không khuyến nghị) FileAbortRetryIgnoreIgnoreNotRecommended=&Bỏ qua để tiếp tục bằng mọi giá (không khuyến nghị) SourceIsCorrupted=Tệp nguồn bị hỏng SourceDoesntExist=Tệp nguồn "%1" không tồn tại ExistingFileReadOnly2=Tệp đã tồn tại với đánh dấu chỉ đọc. ExistingFileReadOnlyRetry=&Xóa thuộc tính chỉ đọc và thử lại ExistingFileReadOnlyKeepExisting=&Giữ tập tin hiện có ErrorReadingExistingDest=Một lỗi đã xảy ra khi đọc tệp: FileExistsSelectAction=Select action FileExists2=Tệp đã tồn tại. FileExistsOverwriteExisting=G&hi đè tệp hiện có FileExistsKeepExisting=&Giữ tệp hiện có FileExistsOverwriteOrKeepAll=&Do this for the next conflicts ExistingFileNewerSelectAction=Select action ExistingFileNewer2=Tệp hiện có mới hơn tệp mà Thiết lập đang cố gắng cài đặt. ExistingFileNewerOverwriteExisting=&Ghi đè tệp hiện có ExistingFileNewerKeepExisting=&Giữ tệp hiện có (khuyến nghị) ExistingFileNewerOverwriteOrKeepAll=&Do this for the next conflicts ErrorChangingAttr=Một lỗi đã xảy ra khi thay đổi thuộc tính của tệp sau: ErrorCreatingTemp=Một lỗi đã xảy ra khi tạo một tệp trong thư mục đích: ErrorReadingSource=Một lỗi đã xảy ra khi đọc tệp nguồn: ErrorCopying=Một lỗi đã xảy ra khi sao chép tệp: ErrorReplacingExistingFile=Một lỗi đã xảy ra khi thay thế tệp: ErrorRestartReplace=Khởi động lại & Thay thế (RestartReplace) thất bại: ErrorRenamingTemp=Một lỗi đã xảy ra khi đổi tên tệp trong thư mục đích: ErrorRegisterServer=Không thể đăng kí DLL/OCX: %1 ErrorRegSvr32Failed=RegSvr32 thất bại với mã thoát %1 ErrorRegisterTypeLib=Không thể đăng kí thư viện kiểu: %1 ; *** Uninstall display name markings ; used for example as 'My Program (32-bit)' UninstallDisplayNameMark=%1 (%2) ; used for example as 'My Program (32-bit, All users)' UninstallDisplayNameMarks=%1 (%2, %3) UninstallDisplayNameMark32Bit=32-bit UninstallDisplayNameMark64Bit=64-bit UninstallDisplayNameMarkAllUsers=All users UninstallDisplayNameMarkCurrentUser=Current user ; *** Post-installation errors ErrorOpeningReadme=Một lỗi đã xảy ra khi mở tệp README. ErrorRestartingComputer=Cài đặt không thể khởi động lại máy tính. Hãy làm việc này một cách thủ công. ; *** Uninstaller messages UninstallNotFound=Tệp "%1" không tồn tại. Không thể gỡ cài đặt. UninstallOpenError=Tệp "%1" không thể được mở. Không thể gỡ cài đặt UninstallUnsupportedVer=Tệp nhật kí gỡ cài đặt "%1" có định dạng không thể được xác định bởi phiên bản gỡ cài đặt này. Không thể gỡ cài đặt UninstallUnknownEntry=Một đầu vào không xác định (%1) đã bị phát hiện trong nhật kí gỡ cài đặt ConfirmUninstall=Bạn có muốn dỡ bỏ hoàn toàn %1 và mọi thành phần của nó? UninstallOnlyOnWin64=Cài đặt này chỉ có thể được gỡ bỏ trên Windows 64 bit. OnlyAdminCanUninstall=Cài đặt này chỉ có thể được gỡ bỏ bằng một người dùng có quyền người quản trị. UninstallStatusLabel=Hãy đợi khi %1 được gỡ khỏi máy tính của bạn. UninstalledAll=%1 đã được gỡ bỏ thành công khỏi máy tính của bạn. UninstalledMost=%1 đã được gỡ bỏ thành công.%n%nMột số thành phần không thể được gỡ bỏ. Hãy làm việc này một cách thủ công. UninstalledAndNeedsRestart=Để hoàn thành việc gỡ cài đặt %1, bạn phải khởi động lại máy tính.%n%nBạn có muốn khởi động lại ngay? UninstallDataCorrupted=Tệp "%1" bị hỏng. Không thể gỡ cài đặt ; *** Uninstallation phase messages ConfirmDeleteSharedFileTitle=Gỡ bỏ tệp được chia sẻ? ConfirmDeleteSharedFile2=Hệ thống chỉ ra các tệp được chia sẻ sau không được sử dụng bởi chương trình nào. Bạn có muốn gỡ bỏ tệp này?%n%nNếu có một chương trình vẫn sử dụng tệp này mà tệp bị gỡ bỏ, chúng có thể không chạy tốt. Nếu bạn không chắc chắn, chọn Không. Để lại tệp trên hệ thống của bạn sẽ không gây ra tổn hại. SharedFileNameLabel=Tên tệp: SharedFileLocationLabel=Vị trí: WizardUninstalling=Trạng thái gỡ cài đặt StatusUninstalling=Đang gỡ cài đặt %1... ; *** Shutdown block reasons ShutdownBlockReasonInstallingApp=Đang cài đặt %1. ShutdownBlockReasonUninstallingApp=Đang gỡ cài đặt %1. ; The custom messages below aren't used by Setup itself, but if you make ; use of them in your scripts, you'll want to translate them. [CustomMessages] NameAndVersion=%1 phiên bản %2 AdditionalIcons=Các lối tắt bổ sung: CreateDesktopIcon=Tạo một &lối tắt trên Desktop CreateQuickLaunchIcon=Tạo một lối tắt &Khởi động nhanh ProgramOnTheWeb=%1 trên Web UninstallProgram=Gỡ cài đặt %1 LaunchProgram=Khởi động %1 AssocFileExtension=&Gán %1 với đuôi tệp %2 AssocingFileExtension=Đang gán %1 với đuôi tệp %2... AutoStartProgramGroupDescription=Khởi động: AutoStartProgram=Tự động khởi động %1 AddonHostProgramNotFound=%1 không thể được xác định trong thư mục bạn đã chọn.%n%nBạn có muốn tiếp tục bằng mọi giá? ================================================ FILE: InnoDependencies/install_dotnet.iss ================================================ #define DotNetPrettyName "Microsoft .NET Desktop Runtime" #define DotNetName "Microsoft.WindowsDesktop.App 8" #define DotNetVersion "8.0.15" #define DotNetURL "https://builds.dotnet.microsoft.com/dotnet/WindowsDesktop/8.0.15/windowsdesktop-runtime-8.0.15-win-x64.exe" #define DotNetExeName "dotnet8.exe" #define DotNetExeArgs "/install /repair /passive /norestart" [Code] var NeedsInstall: Boolean; Dependency_DownloadPage: TDownloadWizardPage; Dependency_Memo: String; function SplitString(Text: String; Separator: String): TArrayOfString; var i, p: Integer; dest: TArrayOfString; begin i := 0; repeat SetArrayLength(dest, i + 1); p := Pos(Separator, Text); if p > 0 then begin dest[i] := Copy(Text, 1, p - 1); Text := Copy(Text, p + Length(Separator), Length(Text)); i := i + 1; end else begin dest[i] := Text; Text := ''; end; until Length(Text)=0; Result := dest end; function CheckDotNetVersionEqualOrHigher(StringArray: TArrayOfString; DotNetName: String; MinimumVersionString: String): Boolean; var i, p: Integer; str, verStr: String; strArray: TArrayOfString; minVer, ver: Int64; begin Result := False; if StrToVersion(MinimumVersionString, minVer) then begin for i := 0 to GetArrayLength(StringArray) - 1 do begin str := StringArray[i]; p := Pos(DotNetName, str); if p > 0 then begin strArray := SplitString(str, ' '); if GetArrayLength(strArray) >= 3 then begin verStr := strArray[1]; if StrToVersion(verStr, ver) then begin if ComparePackedVersion(minVer, ver) <= 0 then begin Result := True; break; end; end; end; end; end; end; end; function IsDotNetInstalled(DotNetName: string; DotNetVersion: string): Boolean; var cmd, args, fileName, command: string; output: AnsiString; resultCode: Integer; begin command := 'dotnet --list-runtimes'; fileName := ExpandConstant('{tmp}\dotnet.txt'); cmd := ExpandConstant('{cmd}'); args := '/C ' + command + ' > "' + fileName + '" 2>&1'; if Exec(cmd, args, '', SW_HIDE, ewWaitUntilTerminated, resultCode) and (resultCode = 0) then begin if LoadStringFromFile(fileName, output) then begin if CheckDotNetVersionEqualOrHigher(SplitString(output, #13#10), DotNetName, DotNetVersion) then begin Log('"' + DotNetName + '" version "' + DotNetVersion + '" or higher found in output of "' + command + '"'); Result := True; end else begin Log('"' + DotNetName + '" version "' + DotNetVersion + '" or higher not found in output of "' + command + '"'); Result := False; end; end else begin Log('Failed to read output of "' + command + '"'); Result := False; end; end else begin Log('Failed to execute "' + command + '"'); Result := False; end; DeleteFile(fileName); end; procedure InstallDotNet6DesktopRuntime; begin if not IsDotNetInstalled('{#DotNetName}', '{#DotNetVersion}') then begin NeedsInstall := True; Dependency_Memo := Dependency_Memo + #13#10 + '%1' + '{#DotNetPrettyName} {#DotNetVersion}'; end; end; procedure Dependency_InitializeWizard; begin Dependency_DownloadPage := CreateDownloadPage(SetupMessage(msgWizardPreparing), SetupMessage(msgPreparingDesc), nil); end; function Dependency_UpdateReadyMemo(const Space, NewLine, MemoUserInfoInfo, MemoDirInfo, MemoTypeInfo, MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo: String): String; begin Result := ''; if MemoUserInfoInfo <> '' then begin Result := Result + MemoUserInfoInfo + Newline + NewLine; end; if MemoDirInfo <> '' then begin Result := Result + MemoDirInfo + Newline + NewLine; end; if MemoTypeInfo <> '' then begin Result := Result + MemoTypeInfo + Newline + NewLine; end; if MemoComponentsInfo <> '' then begin Result := Result + MemoComponentsInfo + Newline + NewLine; end; if MemoGroupInfo <> '' then begin Result := Result + MemoGroupInfo + Newline + NewLine; end; if MemoTasksInfo <> '' then begin Result := Result + MemoTasksInfo; end; if Dependency_Memo <> '' then begin if MemoTasksInfo = '' then begin Result := Result + SetupMessage(msgReadyMemoTasks); end; Result := Result + FmtMessage(Dependency_Memo, [Space]); end; end; function Dependency_PrepareToInstall(var NeedsRestart: Boolean): String; var prettyName: String; retry, abort: Boolean; resultCode: Integer; begin if NeedsInstall then begin prettyName := '{#DotNetPrettyName} {#DotNetVersion}' Dependency_DownloadPage.Show; Dependency_DownloadPage.Clear; Dependency_DownloadPage.Add('{#DotNetURL}', '{#DotNetExeName}', ''); retry := True; while retry do begin retry := False; abort := False; try Dependency_DownloadPage.Download; except if Dependency_DownloadPage.AbortedByUser then begin abort := True end else begin case SuppressibleMsgBox(AddPeriod(GetExceptionMessage), mbError, MB_RETRYCANCEL, IDRETRY) of IDCANCEL: begin abort := True; end; IDRETRY: begin retry := True; end; end; end; end; end; if not abort then begin Dependency_DownloadPage.SetText(prettyName, ''); Dependency_DownloadPage.SetProgress(1, 1); while True do begin resultCode := 0; if ShellExec('', ExpandConstant('{tmp}{\}') + '{#DotNetExeName}', '{#DotNetExeArgs}', '', SW_SHOWNORMAL, ewWaitUntilTerminated, resultCode) then begin if (resultCode = 0) then begin break; end; end; case SuppressibleMsgBox(FmtMessage(SetupMessage(msgErrorFunctionFailed), [prettyName, IntToStr(ResultCode)]), mbError, MB_RETRYCANCEL, IDRETRY) of IDCANCEL: begin abort := True; break; end; end; end; end; Dependency_DownloadPage.Hide; end; end; ================================================ FILE: LICENSE ================================================ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ================================================ FILE: LenovoLegionToolkit.CLI/Flags.cs ================================================ using System.Collections.Generic; using System.Linq; namespace LenovoLegionToolkit.CLI; public readonly struct Flags { public bool Silent { get; } public string? QuickActionRunName { get; } public bool Help { get; } public static Flags Create(IEnumerable startupArgs) { try { return new(startupArgs); } catch { return default; } } private Flags(IEnumerable startupArgs) { var args = startupArgs.ToArray(); QuickActionRunName = StringValue(args, "--quickAction") ?? StringValue(args, "-qa"); Silent = BoolValue(args, "--silent") || BoolValue(args, "-s"); Help = BoolValue(args, "--help") || BoolValue(args, "-h"); } private static bool BoolValue(IEnumerable values, string key) => values.Contains(key); private static string? StringValue(IEnumerable values, string key) { var value = values.FirstOrDefault(s => s.StartsWith(key)); return value?.Remove(0, key.Length + 1); } } ================================================ FILE: LenovoLegionToolkit.CLI/IpcClient.cs ================================================ using System; using System.IO.Pipes; using System.Threading; using System.Threading.Tasks; using LenovoLegionToolkit.CLI.Lib; using LenovoLegionToolkit.CLI.Lib.Extensions; namespace LenovoLegionToolkit.CLI; public static class IpcClient { public static async Task ListQuickActionsAsync() { var req = new IpcRequest { Operation = IpcRequest.OperationType.ListQuickActions }; return await SendRequestAsync(req).ConfigureAwait(false) ?? throw new IpcException("Missing return message"); } public static Task RunQuickActionAsync(string name) { var req = new IpcRequest { Operation = IpcRequest.OperationType.QuickAction, Name = name }; return SendRequestAsync(req); } public static async Task ListFeaturesAsync() { var req = new IpcRequest { Operation = IpcRequest.OperationType.ListFeatures, }; return await SendRequestAsync(req).ConfigureAwait(false) ?? throw new IpcException("Missing return message"); } public static async Task ListFeatureValuesAsync(string name) { var req = new IpcRequest { Operation = IpcRequest.OperationType.ListFeatureValues, Name = name, }; return await SendRequestAsync(req).ConfigureAwait(false) ?? throw new IpcException("Missing return message"); } public static Task SetFeatureValueAsync(string name, string value) { var req = new IpcRequest { Operation = IpcRequest.OperationType.SetFeatureValue, Name = name, Value = value }; return SendRequestAsync(req); } public static async Task GetFeatureValueAsync(string name) { var req = new IpcRequest { Operation = IpcRequest.OperationType.GetFeatureValue, Name = name }; return await SendRequestAsync(req).ConfigureAwait(false) ?? throw new IpcException("Missing return message"); } public static async Task GetSpectrumProfileAsync() { var req = new IpcRequest { Operation = IpcRequest.OperationType.GetSpectrumProfile }; return await SendRequestAsync(req).ConfigureAwait(false) ?? throw new IpcException("Missing return message"); } public static Task SetSpectrumProfileAsync(string value) { var req = new IpcRequest { Operation = IpcRequest.OperationType.SetSpectrumProfile, Value = value }; return SendRequestAsync(req); } public static async Task GetSpectrumBrightnessAsync() { var req = new IpcRequest { Operation = IpcRequest.OperationType.GetSpectrumBrightness }; return await SendRequestAsync(req).ConfigureAwait(false) ?? throw new IpcException("Missing return message"); } public static Task SetSpectrumBrightnessAsync(string value) { var req = new IpcRequest { Operation = IpcRequest.OperationType.SetSpectrumBrightness, Value = value }; return SendRequestAsync(req); } public static async Task GetRGBPresetAsync() { var req = new IpcRequest { Operation = IpcRequest.OperationType.GetRGBPreset }; return await SendRequestAsync(req).ConfigureAwait(false) ?? throw new IpcException("Missing return message"); } public static Task SetRGBPresetAsync(string value) { var req = new IpcRequest { Operation = IpcRequest.OperationType.SetRGBPreset, Value = value }; return SendRequestAsync(req); } private static async Task SendRequestAsync(IpcRequest req) { await using var pipe = new NamedPipeClientStream(Constants.PIPE_NAME); await ConnectAsync(pipe).ConfigureAwait(false); await pipe.WriteObjectAsync(req).ConfigureAwait(false); var res = await pipe.ReadObjectAsync().ConfigureAwait(false); if (res is null || !res.Success) throw new IpcException(res?.Message ?? "Unknown failure"); return res.Message; } private static async Task ConnectAsync(NamedPipeClientStream pipe) { var retries = 3; while (retries >= 0) { try { await pipe.ConnectAsync(TimeSpan.FromMilliseconds(500), CancellationToken.None).ConfigureAwait(false); pipe.ReadMode = PipeTransmissionMode.Message; return; } catch (TimeoutException) { } retries--; } throw new IpcConnectException(); } } ================================================ FILE: LenovoLegionToolkit.CLI/LenovoLegionToolkit.CLI.csproj ================================================  Exe net8.0-windows win-x64 enable © 2024 Bartosz Cichecki llt x64 0.0.1 0.0.1 en build$([System.DateTime]::UtcNow.ToString("yyyyMMddHHmmss")) en en portable embedded ================================================ FILE: LenovoLegionToolkit.CLI/Program.cs ================================================ using System; using System.CommandLine; using System.CommandLine.Builder; using System.CommandLine.Invocation; using System.CommandLine.IO; using System.CommandLine.Parsing; using System.Threading.Tasks; using LenovoLegionToolkit.CLI.Lib; namespace LenovoLegionToolkit.CLI; public class Program { public static Task Main(string[] args) => BuildCommandLine().InvokeAsync(args); private static Parser BuildCommandLine() { var root = new RootCommand("Utility that controls Lenovo Legion Toolkit from command line.\n\n" + "Lenovo Legion Toolkit must be running in the background and CLI setting must be " + "turned on for this utility to work."); var builder = new CommandLineBuilder(root) .UseDefaults() .UseExceptionHandler(OnException); root.AddCommand(BuildQuickActionsCommand()); root.AddCommand(BuildFeatureCommand()); root.AddCommand(BuildSpectrumCommand()); root.AddCommand(BuildRGBCommand()); return builder.Build(); } private static Command BuildQuickActionsCommand() { var nameArgument = new Argument("name", "Name of the Quick Action") { Arity = ArgumentArity.ZeroOrOne }; var listOption = new Option("--list", "List available Quick Actions") { Arity = ArgumentArity.ZeroOrOne }; listOption.AddAlias("-l"); var cmd = new Command("quickAction", "Run Quick Action"); cmd.AddAlias("qa"); cmd.AddArgument(nameArgument); cmd.AddOption(listOption); cmd.SetHandler(async (name, list) => { if (list) { var result = await IpcClient.ListQuickActionsAsync(); Console.WriteLine(result); return; } await IpcClient.RunQuickActionAsync(name); }, nameArgument, listOption); cmd.AddValidator(result => { if (result.FindResultFor(nameArgument) is not null) return; if (result.FindResultFor(listOption) is not null) return; result.ErrorMessage = $"{nameArgument.Name} or --{listOption.Name} should be specified"; }); return cmd; } private static Command BuildFeatureCommand() { var getCmd = BuildGetFeatureCommand(); var setCmd = BuildSetFeatureCommand(); var listOption = new Option("--list", "List available features") { Arity = ArgumentArity.ZeroOrOne }; listOption.AddAlias("-l"); var cmd = new Command("feature", "Control features"); cmd.AddAlias("f"); cmd.AddCommand(getCmd); cmd.AddCommand(setCmd); cmd.AddOption(listOption); cmd.SetHandler(async list => { if (!list.HasValue || !list.Value) return; var value = await IpcClient.ListFeaturesAsync(); Console.WriteLine(value); }, listOption); cmd.AddValidator(result => { if (result.FindResultFor(getCmd) is not null) return; if (result.FindResultFor(setCmd) is not null) return; if (result.FindResultFor(listOption) is not null) return; result.ErrorMessage = $"{getCmd.Name}, {setCmd.Name} or --{listOption.Name} should be specified"; }); return cmd; } private static Command BuildGetFeatureCommand() { var nameArgument = new Argument("name", "Name of the feature") { Arity = ArgumentArity.ExactlyOne }; var cmd = new Command("get", "Get value of a feature"); cmd.AddAlias("g"); cmd.AddArgument(nameArgument); cmd.SetHandler(async name => { var result = await IpcClient.GetFeatureValueAsync(name); Console.WriteLine(result); }, nameArgument); return cmd; } private static Command BuildSetFeatureCommand() { var nameArgument = new Argument("name", "Name of the feature") { Arity = ArgumentArity.ExactlyOne }; var valueArgument = new Argument("value", "Value of the feature") { Arity = ArgumentArity.ZeroOrOne }; var listOption = new Option("--list", "List available feature values") { Arity = ArgumentArity.ZeroOrOne }; listOption.AddAlias("-l"); var cmd = new Command("set", "Set value of a feature"); cmd.AddAlias("s"); cmd.AddArgument(nameArgument); cmd.AddArgument(valueArgument); cmd.AddOption(listOption); cmd.SetHandler(async (name, value, list) => { if (list) { var result = await IpcClient.ListFeatureValuesAsync(name); Console.WriteLine(result); return; } await IpcClient.SetFeatureValueAsync(name, value); }, nameArgument, valueArgument, listOption); cmd.AddValidator(result => { if (result.FindResultFor(nameArgument) is not null) return; if (result.FindResultFor(listOption) is not null) return; result.ErrorMessage = $"{nameArgument.Name} or --{listOption.Name} should be specified"; }); return cmd; } private static Command BuildSpectrumCommand() { var profileCommand = BuildSpectrumProfileCommand(); var brightnessCommand = BuildSpectrumBrightnessCommand(); var cmd = new Command("spectrum", "Control Spectrum backlight"); cmd.AddAlias("s"); cmd.AddCommand(profileCommand); cmd.AddCommand(brightnessCommand); return cmd; } private static Command BuildSpectrumProfileCommand() { var getCmd = BuildGetSpectrumProfileCommand(); var setCmd = BuildSetSpectrumProfileCommand(); var cmd = new Command("profile", "Control Spectrum backlight profile"); cmd.AddAlias("p"); cmd.AddCommand(getCmd); cmd.AddCommand(setCmd); return cmd; } private static Command BuildGetSpectrumProfileCommand() { var cmd = new Command("get", "Get current Spectrum profile"); cmd.AddAlias("g"); cmd.SetHandler(async _ => { var result = await IpcClient.GetSpectrumProfileAsync(); Console.WriteLine(result); }); return cmd; } private static Command BuildSetSpectrumProfileCommand() { var valueArgument = new Argument("profile", "Profile to set") { Arity = ArgumentArity.ExactlyOne }; var cmd = new Command("set", "Set current Spectrum profile"); cmd.AddAlias("s"); cmd.AddArgument(valueArgument); cmd.SetHandler(async value => { await IpcClient.SetSpectrumProfileAsync($"{value}"); }, valueArgument); return cmd; } private static Command BuildSpectrumBrightnessCommand() { var getCmd = BuildGetSpectrumBrightnessCommand(); var setCmd = BuildSetSpectrumBrightnessCommand(); var cmd = new Command("brightness", "Control Spectrum brightness"); cmd.AddAlias("b"); cmd.AddCommand(getCmd); cmd.AddCommand(setCmd); return cmd; } private static Command BuildGetSpectrumBrightnessCommand() { var cmd = new Command("get", "Get current Spectrum brightness"); cmd.AddAlias("g"); cmd.SetHandler(async _ => { var result = await IpcClient.GetSpectrumBrightnessAsync(); Console.WriteLine(result); }); return cmd; } private static Command BuildSetSpectrumBrightnessCommand() { var valueArgument = new Argument("brightness", "Brightness to set") { Arity = ArgumentArity.ExactlyOne }; var cmd = new Command("set", "Set current Spectrum brightness"); cmd.AddAlias("s"); cmd.AddArgument(valueArgument); cmd.SetHandler(async value => { await IpcClient.SetSpectrumBrightnessAsync($"{value}"); }, valueArgument); return cmd; } private static Command BuildRGBCommand() { var getCmd = BuildGetRGBCommand(); var setCmd = BuildSetRGBCommand(); var cmd = new Command("rgb", "Control RGB backlight preset"); cmd.AddAlias("r"); cmd.AddCommand(getCmd); cmd.AddCommand(setCmd); return cmd; } private static Command BuildGetRGBCommand() { var cmd = new Command("get", "Get current RGB preset"); cmd.AddAlias("g"); cmd.SetHandler(async _ => { var result = await IpcClient.GetRGBPresetAsync(); Console.WriteLine(result); }); return cmd; } private static Command BuildSetRGBCommand() { var valueArgument = new Argument("preset", "Preset to set") { Arity = ArgumentArity.ExactlyOne }; var cmd = new Command("set", "Set current RGB preset"); cmd.AddAlias("s"); cmd.AddArgument(valueArgument); cmd.SetHandler(async value => { await IpcClient.SetRGBPresetAsync($"{value}"); }, valueArgument); return cmd; } private static void OnException(Exception ex, InvocationContext context) { var message = ex switch { IpcConnectException => "Failed to connect. " + "Make sure that Lenovo Legion Toolkit is running " + "in background and CLI is enabled in Settings.", IpcException => ex.Message, _ => ex.ToString() }; var exitCode = ex switch { IpcConnectException => -1, IpcException => -2, _ => -99 }; if (!Console.IsOutputRedirected) { Console.ResetColor(); Console.ForegroundColor = ConsoleColor.Red; } context.Console.Error.WriteLine(message); context.ExitCode = exitCode; if (!Console.IsOutputRedirected) Console.ResetColor(); } } ================================================ FILE: LenovoLegionToolkit.CLI.Lib/Constants.cs ================================================ namespace LenovoLegionToolkit.CLI.Lib; public static class Constants { public const string PIPE_NAME = "LenovoLegionToolkit-IPC-0"; } ================================================ FILE: LenovoLegionToolkit.CLI.Lib/Extensions/PipeStreamExtensions.cs ================================================ using System; using System.IO.Pipes; using System.Text; using System.Threading; using System.Threading.Tasks; using Newtonsoft.Json; namespace LenovoLegionToolkit.CLI.Lib.Extensions; public static class PipeStreamExtensions { private static readonly Encoding Encoding = Encoding.UTF8; public static async Task WriteObjectAsync(this PipeStream stream, T obj, CancellationToken token = default) { if (stream.ReadMode != PipeTransmissionMode.Message) throw new InvalidOperationException("ReadMode is not PipeTransmissionMode.Message"); var str = JsonConvert.SerializeObject(obj); var bytes = Encoding.GetBytes(str); await stream.WriteAsync(bytes, token).ConfigureAwait(false); } public static async Task ReadObjectAsync(this PipeStream stream, CancellationToken token = default) { if (stream.ReadMode != PipeTransmissionMode.Message) throw new InvalidOperationException("ReadMode is not PipeTransmissionMode.Message"); var buffer = new byte[1024]; var builder = new StringBuilder(); do { _ = await stream.ReadAsync(buffer, token).ConfigureAwait(false); builder.Append(Encoding.GetString(buffer)); } while (!stream.IsMessageComplete); return JsonConvert.DeserializeObject(builder.ToString()); } } ================================================ FILE: LenovoLegionToolkit.CLI.Lib/IpcConnectException.cs ================================================ using System; namespace LenovoLegionToolkit.CLI.Lib; public class IpcConnectException : Exception; ================================================ FILE: LenovoLegionToolkit.CLI.Lib/IpcException.cs ================================================ using System; namespace LenovoLegionToolkit.CLI.Lib; public class IpcException(string? name) : Exception(name); ================================================ FILE: LenovoLegionToolkit.CLI.Lib/IpcRequest.cs ================================================ namespace LenovoLegionToolkit.CLI.Lib; public class IpcRequest { public enum OperationType { Unknown, ListFeatures, ListFeatureValues, ListQuickActions, GetFeatureValue, SetFeatureValue, GetSpectrumProfile, SetSpectrumProfile, GetSpectrumBrightness, SetSpectrumBrightness, GetRGBPreset, SetRGBPreset, QuickAction, } public OperationType? Operation { get; init; } public string? Name { get; init; } public string? Value { get; init; } } ================================================ FILE: LenovoLegionToolkit.CLI.Lib/IpcResponse.cs ================================================ namespace LenovoLegionToolkit.CLI.Lib; public class IpcResponse { public bool Success { get; init; } public string? Message { get; init; } } ================================================ FILE: LenovoLegionToolkit.CLI.Lib/LenovoLegionToolkit.CLI.Lib.csproj ================================================  net8.0-windows win-x64 x64 disable enable © 2024 Bartosz Cichecki en ================================================ FILE: LenovoLegionToolkit.Lib/AutoListeners/AbstractAutoListener.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Utils; using NeoSmart.AsyncLock; namespace LenovoLegionToolkit.Lib.AutoListeners; public abstract class AbstractAutoListener : IAutoListener where TEventArgs : EventArgs { private readonly AsyncLock _startStopLock = new(); private bool _started; private event EventHandler? Changed; public async Task SubscribeChangedAsync(EventHandler eventHandler) { Changed += eventHandler; await StartStopAsync().ConfigureAwait(false); } public async Task UnsubscribeChangedAsync(EventHandler eventHandler) { Changed -= eventHandler; await StartStopAsync().ConfigureAwait(false); } private async Task StartStopAsync() { using (await _startStopLock.LockAsync().ConfigureAwait(false)) { var subscribers = Changed?.GetInvocationList().Length ?? 0; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Subscribers: {subscribers}. [type={GetType().Name}]"); if (subscribers > 0) await StartInternalAsync().ConfigureAwait(false); else await StopInternalAsync().ConfigureAwait(false); } } private async Task StartInternalAsync() { if (_started) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Already started. [type={GetType().Name}]"); return; } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Starting... [type={GetType().Name}]"); await StartAsync().ConfigureAwait(false); _started = true; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Started. [type={GetType().Name}]"); } private async Task StopInternalAsync() { if (!_started) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Already stopped. [type={GetType().Name}]"); return; } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Stopping... [type={GetType().Name}]"); await StopAsync().ConfigureAwait(false); _started = false; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Stopped. [type={GetType().Name}]"); } protected abstract Task StartAsync(); protected abstract Task StopAsync(); protected void RaiseChanged(TEventArgs value) => Changed?.Invoke(this, value); } ================================================ FILE: LenovoLegionToolkit.Lib/AutoListeners/GameAutoListener.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.GameDetection; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.AutoListeners; public class GameAutoListener : AbstractAutoListener { public class ChangedEventArgs(bool running) : EventArgs { public bool Running { get; } = running; } private class ProcessEqualityComparer : IEqualityComparer { public bool Equals(Process? x, Process? y) { if (ReferenceEquals(x, y)) return true; if (x is null) return false; if (y is null) return false; if (x.GetType() != y.GetType()) return false; return x.Id == y.Id; } public int GetHashCode(Process obj) => obj.Id; } private static readonly object Lock = new(); private readonly InstanceStartedEventAutoAutoListener _instanceStartedEventAutoAutoListener; private readonly GameConfigStoreDetector _gameConfigStoreDetector; private readonly EffectiveGameModeDetector _effectiveGameModeDetector; private readonly HashSet _detectedGamePathsCache = []; private readonly HashSet _processCache = new(new ProcessEqualityComparer()); private bool _lastState; public GameAutoListener(InstanceStartedEventAutoAutoListener instanceStartedEventAutoAutoListener) { _instanceStartedEventAutoAutoListener = instanceStartedEventAutoAutoListener; _gameConfigStoreDetector = new GameConfigStoreDetector(); _gameConfigStoreDetector.GamesDetected += GameConfigStoreDetectorGamesConfigStoreDetected; _effectiveGameModeDetector = new EffectiveGameModeDetector(); _effectiveGameModeDetector.Changed += EffectiveGameModeDetectorChanged; } protected override async Task StartAsync() { lock (Lock) { foreach (var gamePath in GameConfigStoreDetector.GetDetectedGamePaths()) _detectedGamePathsCache.Add(gamePath); } await _gameConfigStoreDetector.StartAsync().ConfigureAwait(false); await _effectiveGameModeDetector.StartAsync().ConfigureAwait(false); await _instanceStartedEventAutoAutoListener.SubscribeChangedAsync(InstanceStartedEventAutoAutoListener_Changed).ConfigureAwait(false); } protected override async Task StopAsync() { await _instanceStartedEventAutoAutoListener.UnsubscribeChangedAsync(InstanceStartedEventAutoAutoListener_Changed).ConfigureAwait(false); await _gameConfigStoreDetector.StopAsync().ConfigureAwait(false); await _effectiveGameModeDetector.StopAsync().ConfigureAwait(false); lock (Lock) { foreach (var process in _processCache) Detach(process); _processCache.Clear(); _detectedGamePathsCache.Clear(); _lastState = false; } } public bool AreGamesRunning() { lock (Lock) { return _lastState; } } private void GameConfigStoreDetectorGamesConfigStoreDetected(object? sender, GameConfigStoreDetector.GameDetectedEventArgs e) { lock (Lock) { _detectedGamePathsCache.Clear(); foreach (var game in e.Games) { _detectedGamePathsCache.Add(game); foreach (var process in Process.GetProcessesByName(game.Name)) { try { var processPath = process.MainModule?.FileName; if (game.ExecutablePath is null || !game.ExecutablePath.Equals(processPath, StringComparison.CurrentCultureIgnoreCase)) continue; if (!_processCache.Contains(process)) { Attach(process); _processCache.Add(process); } RaiseChangedIfNeeded(true); } catch (Exception) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Can't get game \"{game}\" details."); } } } } } private void EffectiveGameModeDetectorChanged(object? sender, bool e) { lock (Lock) { if (_processCache.Count != 0) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Ignoring, process cache is not empty."); return; } RaiseChangedIfNeeded(e); } } private void InstanceStartedEventAutoAutoListener_Changed(object? sender, InstanceStartedEventAutoAutoListener.ChangedEventArgs e) { lock (Lock) { if (e.ProcessId < 0) return; if (!_detectedGamePathsCache.Any(p => e.ProcessName.Equals(p.Name, StringComparison.CurrentCultureIgnoreCase))) return; try { var process = Process.GetProcessById(e.ProcessId); var processPath = process.GetFileName(); if (string.IsNullOrEmpty(processPath)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Can't get path for {e.ProcessName}. [processId={e.ProcessId}]"); return; } var processInfo = ProcessInfo.FromPath(processPath); if (!_detectedGamePathsCache.Contains(processInfo)) return; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Game {processInfo} is running. [processId={e.ProcessId}, processPath={processPath}]"); Attach(process); _processCache.Add(process); RaiseChangedIfNeeded(true); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to attach to {e.ProcessName}. [processId={e.ProcessId}]", ex); } } } private void RaiseChangedIfNeeded(bool newState) { lock (Lock) { if (newState == _lastState) return; _lastState = newState; RaiseChanged(new ChangedEventArgs(newState)); } } private void Attach(Process process) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Attaching to process {process.Id}..."); process.EnableRaisingEvents = true; process.Exited += Process_Exited; } private void Detach(Process process) { process.EnableRaisingEvents = false; process.Exited -= Process_Exited; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Detached from process {process.Id}."); } private void Process_Exited(object? o, EventArgs args) { lock (Lock) { if (o is not Process process) return; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Process {process.Id} exited."); var staleProcesses = _processCache.RemoveWhere(p => { try { return p.HasExited; } catch { return true; } }); if (staleProcesses > 1) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Removed {staleProcesses} stale processes."); } if (_processCache.Count != 0) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"More games are running..."); return; } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"No more games are running."); RaiseChangedIfNeeded(false); } } } ================================================ FILE: LenovoLegionToolkit.Lib/AutoListeners/IAutoListener.cs ================================================ using System; using System.Threading.Tasks; namespace LenovoLegionToolkit.Lib.AutoListeners; public interface IAutoListener where T : EventArgs { Task SubscribeChangedAsync(EventHandler eventHandler); Task UnsubscribeChangedAsync(EventHandler eventHandler); } ================================================ FILE: LenovoLegionToolkit.Lib/AutoListeners/InstanceStartedEventAutoAutoListener.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.System.Management; namespace LenovoLegionToolkit.Lib.AutoListeners; public class InstanceStartedEventAutoAutoListener : AbstractAutoListener { public class ChangedEventArgs(int processId, string processName) : EventArgs { public int ProcessId { get; } = processId; public string ProcessName { get; } = processName; } private IDisposable? _disposable; protected override Task StartAsync() { _disposable = WMI.Win32.ProcessStartTrace.Listen(Handle); return Task.CompletedTask; } protected override Task StopAsync() { _disposable?.Dispose(); _disposable = null; return Task.CompletedTask; } private void Handle(int processId, string processName) => RaiseChanged(new ChangedEventArgs(processId, processName)); } ================================================ FILE: LenovoLegionToolkit.Lib/AutoListeners/InstanceStoppedEventAutoAutoListener.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.System.Management; namespace LenovoLegionToolkit.Lib.AutoListeners; public class InstanceStoppedEventAutoAutoListener : AbstractAutoListener { public class ChangedEventArgs(int processId, string processName) : EventArgs { public int ProcessId { get; } = processId; public string ProcessName { get; } = processName; } private IDisposable? _disposable; protected override Task StartAsync() { _disposable = WMI.Win32.ProcessStopTrace.Listen(Handle); return Task.CompletedTask; } protected override Task StopAsync() { _disposable?.Dispose(); _disposable = null; return Task.CompletedTask; } private void Handle(int processId, string processName) => RaiseChanged(new ChangedEventArgs(processId, processName)); } ================================================ FILE: LenovoLegionToolkit.Lib/AutoListeners/ProcessAutoListener.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.AutoListeners; public class ProcessAutoListener( InstanceStartedEventAutoAutoListener instanceStartedEventAutoAutoListener, InstanceStoppedEventAutoAutoListener instanceStoppedEventAutoAutoListener) : AbstractAutoListener { public class ChangedEventArgs(ProcessEventInfoType type, ProcessInfo processInfo) : EventArgs { public ProcessEventInfoType Type { get; } = type; public ProcessInfo ProcessInfo { get; } = processInfo; } private static readonly object Lock = new(); // ReSharper disable StringLiteralTypo private static readonly string[] IgnoredNames = [ "backgroundTaskHost", "cmd", "CompPkgSrv", "conhost", "dllhost", "Lenovo Legion Toolkit", "msedge", "msedgewebview2", "NvOAWrapperCache", "SearchProtocolHost", "svchost", "taskhostw", "WmiApSrv", "WmiPrvSE" ]; // ReSharper restore StringLiteralTypo private static readonly string[] IgnoredPaths = [ Environment.GetFolderPath(Environment.SpecialFolder.Windows), ]; private readonly Dictionary _processCache = []; protected override async Task StartAsync() { await instanceStartedEventAutoAutoListener.SubscribeChangedAsync(InstanceStartedEventAutoListener_Changed).ConfigureAwait(false); await instanceStoppedEventAutoAutoListener.SubscribeChangedAsync(InstanceStoppedEventAutoListener_Changed).ConfigureAwait(false); } protected override async Task StopAsync() { await instanceStartedEventAutoAutoListener.UnsubscribeChangedAsync(InstanceStartedEventAutoListener_Changed).ConfigureAwait(false); await instanceStoppedEventAutoAutoListener.UnsubscribeChangedAsync(InstanceStoppedEventAutoListener_Changed).ConfigureAwait(false); lock (Lock) _processCache.Clear(); } private void InstanceStartedEventAutoListener_Changed(object? sender, InstanceStartedEventAutoAutoListener.ChangedEventArgs e) { lock (Lock) { if (e.ProcessId < 0) return; if (string.IsNullOrWhiteSpace(e.ProcessName)) return; if (IgnoredNames.Contains(e.ProcessName, StringComparer.InvariantCultureIgnoreCase)) return; string? processPath = null; try { processPath = Process.GetProcessById(e.ProcessId).GetFileName(); } catch (ArgumentException) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Process {e.ProcessName} isn't running, ignoring... [processId={e.ProcessId}]"); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Can't get process {e.ProcessName} details. [processId={e.ProcessId}]", ex); } if (!string.IsNullOrEmpty(processPath) && IgnoredPaths.Any(p => processPath.StartsWith(p, StringComparison.InvariantCultureIgnoreCase))) return; var processInfo = new ProcessInfo(e.ProcessName, processPath); _processCache[e.ProcessId] = processInfo; CleanUpCacheIfNecessary(); RaiseChanged(new ChangedEventArgs(ProcessEventInfoType.Started, processInfo)); } } private void InstanceStoppedEventAutoListener_Changed(object? sender, InstanceStoppedEventAutoAutoListener.ChangedEventArgs e) { lock (Lock) { CleanUpCacheIfNecessary(); if (e.ProcessId < 0) return; if (string.IsNullOrWhiteSpace(e.ProcessName)) return; if (IgnoredNames.Contains(e.ProcessName, StringComparer.InvariantCultureIgnoreCase)) return; if (!_processCache.Remove(e.ProcessId, out var processInfo)) return; RaiseChanged(new ChangedEventArgs(ProcessEventInfoType.Stopped, processInfo)); } } private void CleanUpCacheIfNecessary() { if (_processCache.Count < 250) return; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Cleaning up process cache. Current size: {_processCache.Count}."); foreach (var (processId, _) in _processCache) { try { _ = Process.GetProcessById(processId); } catch (ArgumentException) { _processCache.Remove(processId); } } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Cleaned up process cache. Current size: {_processCache.Count}."); } } ================================================ FILE: LenovoLegionToolkit.Lib/AutoListeners/TimeAutoListener.cs ================================================ using System; using System.Threading.Tasks; using System.Timers; using LenovoLegionToolkit.Lib.Extensions; namespace LenovoLegionToolkit.Lib.AutoListeners; public class TimeAutoListener : AbstractAutoListener { public class ChangedEventArgs(Time time, DayOfWeek day) : EventArgs { public Time Time { get; } = time; public DayOfWeek Day { get; } = day; } private readonly Timer _timer; public TimeAutoListener() { _timer = new Timer(60_000); _timer.Elapsed += Timer_Elapsed; _timer.AutoReset = true; } protected override Task StartAsync() { if (!_timer.Enabled) _timer.Enabled = true; return Task.CompletedTask; } protected override Task StopAsync() { _timer.Enabled = false; return Task.CompletedTask; } private void Timer_Elapsed(object? sender, ElapsedEventArgs e) => RaiseChanged(new ChangedEventArgs(TimeExtensions.UtcNow, DateTime.UtcNow.DayOfWeek)); } ================================================ FILE: LenovoLegionToolkit.Lib/AutoListeners/UserInactivityAutoListener.cs ================================================ using System; using System.Threading.Tasks; using System.Windows.Forms; using LenovoLegionToolkit.Lib.Utils; using Windows.Win32; using Windows.Win32.Foundation; using Windows.Win32.UI.WindowsAndMessaging; using Timer = System.Threading.Timer; namespace LenovoLegionToolkit.Lib.AutoListeners; public class UserInactivityAutoListener(IMainThreadDispatcher mainThreadDispatcher) : AbstractAutoListener { public class ChangedEventArgs(TimeSpan timerResolution, uint tickCount) : EventArgs { public TimeSpan TimerResolution { get; } = timerResolution; public uint TickCount { get; } = tickCount; } private class UserInactivityWindow : NativeWindow, IDisposable { private readonly Action _callback; private readonly HOOKPROC _hookProc; private HHOOK _kbHook; private HHOOK _mouseHook; public UserInactivityWindow(Action callback) { _callback = callback; _hookProc = HookProc; } public void Create() { CreateHandle(new CreateParams { Caption = "LenovoLegionToolkit_UserInactivityListenerWindow", Parent = new IntPtr(-3) }); _kbHook = PInvoke.SetWindowsHookEx(WINDOWS_HOOK_ID.WH_KEYBOARD_LL, _hookProc, HINSTANCE.Null, 0); _mouseHook = PInvoke.SetWindowsHookEx(WINDOWS_HOOK_ID.WH_MOUSE_LL, _hookProc, HINSTANCE.Null, 0); } private LRESULT HookProc(int nCode, WPARAM wParam, LPARAM lParam) { _callback(); return PInvoke.CallNextHookEx(HHOOK.Null, nCode, wParam, lParam); } public void Dispose() { PInvoke.UnhookWindowsHookEx(_kbHook); PInvoke.UnhookWindowsHookEx(_mouseHook); _kbHook = HHOOK.Null; _mouseHook = HHOOK.Null; ReleaseHandle(); GC.SuppressFinalize(this); } } private readonly TimeSpan _timerResolution = TimeSpan.FromSeconds(10); private readonly object _lock = new(); private UserInactivityWindow? _window; private uint _tickCount; private Timer? _timer; public TimeSpan InactivityTimeSpan => _timerResolution * _tickCount; protected override Task StartAsync() => mainThreadDispatcher.DispatchAsync(() => { lock (_lock) { _timer = new Timer(TimerCallback, null, _timerResolution, _timerResolution); _tickCount = 0; var window = new UserInactivityWindow(WindowCallback); window.Create(); _window = window; } return Task.CompletedTask; }); protected override Task StopAsync() => mainThreadDispatcher.DispatchAsync(() => { lock (_lock) { _timer?.Dispose(); _timer = null; _tickCount = 0; _window?.Dispose(); _window = null; } return Task.CompletedTask; }); private void WindowCallback() { lock (_lock) { _timer?.Change(_timerResolution, _timerResolution); if (_tickCount < 1) return; _tickCount = 0; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"User became active."); RaiseChanged(new ChangedEventArgs(_timerResolution, 0)); } } private void TimerCallback(object? state) { lock (_lock) { _tickCount++; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"User is not active [time={_timerResolution * _tickCount}]"); RaiseChanged(new ChangedEventArgs(_timerResolution, _tickCount)); } } } ================================================ FILE: LenovoLegionToolkit.Lib/AutoListeners/WiFiAutoListener.cs ================================================ using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Utils; using Windows.Win32; using Windows.Win32.Foundation; using Windows.Win32.NetworkManagement.WiFi; namespace LenovoLegionToolkit.Lib.AutoListeners; public class WiFiAutoListener : AbstractAutoListener { public class ChangedEventArgs(bool isConnected, string? ssid) : EventArgs { public bool IsConnected { get; } = isConnected; public string? Ssid { get; } = ssid; } private readonly IMainThreadDispatcher _mainThreadDispatcher; private readonly WLAN_NOTIFICATION_CALLBACK _wlanCallback; private LambdaDisposable? _wlanNotificationDisposable; public unsafe WiFiAutoListener(IMainThreadDispatcher mainThreadDispatcher) { _mainThreadDispatcher = mainThreadDispatcher; _wlanCallback = WlanCallback; } protected override Task StartAsync() => _mainThreadDispatcher.DispatchAsync(() => { _wlanNotificationDisposable = RegisterWlanNotification(); return Task.CompletedTask; }); protected override Task StopAsync() => _mainThreadDispatcher.DispatchAsync(() => { _wlanNotificationDisposable?.Dispose(); _wlanNotificationDisposable = null; return Task.CompletedTask; }); private unsafe LambdaDisposable? RegisterWlanNotification() { var handlePtr = IntPtr.Zero; try { handlePtr = Marshal.AllocHGlobal(Marshal.SizeOf()); if (PInvoke.WlanOpenHandle(2, out _, (HANDLE*)handlePtr) != 0) return null; var handle = Marshal.PtrToStructure(handlePtr); var result = PInvoke.WlanRegisterNotification(handle, WLAN_NOTIFICATION_SOURCES.WLAN_NOTIFICATION_SOURCE_ACM, true, _wlanCallback, null, null, null); if (result != 0) return null; return new LambdaDisposable(() => { _ = PInvoke.WlanRegisterNotification(handle, WLAN_NOTIFICATION_SOURCES.WLAN_NOTIFICATION_SOURCE_NONE, true, _wlanCallback, null, null, null); _ = PInvoke.WlanCloseHandle(handle, null); }); } finally { Marshal.FreeHGlobal(handlePtr); } } private unsafe void WlanCallback(L2_NOTIFICATION_DATA* param0, void* param1) { ref var data = ref Unsafe.AsRef(param0); switch (data.NotificationCode) { case 0x0A: /* Connected */ ref var notificationData = ref Unsafe.AsRef(data.pData); var dot11Ssid = notificationData.dot11Ssid; var ssid = Encoding.UTF8.GetString(dot11Ssid.ucSSID.Value, (int)dot11Ssid.uSSIDLength); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"WiFi connected. [ssid={ssid}]"); RaiseChanged(new ChangedEventArgs(true, ssid)); break; case 0x15: /* Disconnected */ if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"WiFi disconnected."); RaiseChanged(new ChangedEventArgs(false, null)); break; } } } ================================================ FILE: LenovoLegionToolkit.Lib/Controllers/AIController.cs ================================================ using System; using System.Diagnostics; using System.IO; using System.Linq; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.AutoListeners; using LenovoLegionToolkit.Lib.Features; using LenovoLegionToolkit.Lib.Listeners; using LenovoLegionToolkit.Lib.Settings; using LenovoLegionToolkit.Lib.System; using LenovoLegionToolkit.Lib.System.Management; using LenovoLegionToolkit.Lib.Utils; using NeoSmart.AsyncLock; namespace LenovoLegionToolkit.Lib.Controllers; public class AIController( PowerModeListener powerModeListener, PowerStateListener powerStateListener, GameAutoListener gameAutoListener, PowerModeFeature powerModeFeature, BalanceModeSettings settings) { private readonly ThrottleLastDispatcher _dispatcher = new(TimeSpan.FromSeconds(1), nameof(AIController)); private readonly AsyncLock _startStopLock = new(); public bool IsAIModeEnabled { get => settings.Store.AIModeEnabled; set { settings.Store.AIModeEnabled = value; settings.SynchronizeStore(); } } public async Task StartIfNeededAsync() { if (!await IsSupportedAsync().ConfigureAwait(false)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Not supported."); return; } await StopAsync().ConfigureAwait(false); if (!IsAIModeEnabled) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"AI Mode is not enabled."); return; } using (await _startStopLock.LockAsync().ConfigureAwait(false)) { powerModeListener.Changed += PowerModeListener_Changed; powerStateListener.Changed += PowerStateListener_Changed; await gameAutoListener.SubscribeChangedAsync(GameAutoListener_Changed).ConfigureAwait(false); await RefreshAsync().ConfigureAwait(false); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Started."); } } public async Task StopAsync() { if (!await IsSupportedAsync().ConfigureAwait(false)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Not supported."); return; } using (await _startStopLock.LockAsync().ConfigureAwait(false)) { powerModeListener.Changed -= PowerModeListener_Changed; powerStateListener.Changed -= PowerStateListener_Changed; await gameAutoListener.UnsubscribeChangedAsync(GameAutoListener_Changed).ConfigureAwait(false); if (await ShouldDisableAsync().ConfigureAwait(false)) await DisableAsync().ConfigureAwait(false); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Stopped."); } } private async void PowerModeListener_Changed(object? sender, PowerModeListener.ChangedEventArgs e) => await _dispatcher.DispatchAsync(RefreshAsync).ConfigureAwait(false); private async void PowerStateListener_Changed(object? sender, PowerStateListener.ChangedEventArgs e) => await _dispatcher.DispatchAsync(RefreshAsync).ConfigureAwait(false); private async void GameAutoListener_Changed(object? sender, GameAutoListener.ChangedEventArgs e) => await _dispatcher.DispatchAsync(RefreshAsync).ConfigureAwait(false); private async Task RefreshAsync() { if (!await IsSupportedAsync().ConfigureAwait(false)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Not supported."); return; } using (await _startStopLock.LockAsync().ConfigureAwait(false)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Refreshing..."); if (await ShouldDisableAsync().ConfigureAwait(false)) await DisableAsync().ConfigureAwait(false); if (await ShouldEnableAsync().ConfigureAwait(false)) await EnableAsync().ConfigureAwait(false); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Refreshed"); } } private static async Task IsSupportedAsync() { var mi = await Compatibility.GetMachineInformationAsync().ConfigureAwait(false); return mi.Properties.SupportsAIMode; } private async Task ShouldEnableAsync() { if (await Power.IsPowerAdapterConnectedAsync().ConfigureAwait(false) != PowerAdapterStatus.Connected) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Power adapter not connected."); return false; } if (await powerModeFeature.GetStateAsync().ConfigureAwait(false) != PowerModeState.Balance) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Not in balanced mode."); return false; } if (!gameAutoListener.AreGamesRunning()) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Games aren't running."); return false; } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"All conditions met."); return true; } private async Task ShouldDisableAsync() { if (await powerModeFeature.GetStateAsync().ConfigureAwait(false) != PowerModeState.Balance) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Not in balanced mode."); return false; } if (await WMI.LenovoGameZoneData.GetIntelligentSubModeAsync().ConfigureAwait(false) == 0) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Not needed."); return false; } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"All conditions met."); return true; } private static async Task EnableAsync() { try { var targetSubMode = 1; var intelligentOpList = await WMI.LenovoIntelligentOPList.ReadAsync().ConfigureAwait(false); foreach (var (processName, subMode) in intelligentOpList) { var process = Process.GetProcessesByName(Path.GetFileNameWithoutExtension(processName)).FirstOrDefault(); if (process is null) continue; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Found running process {processName}. [processId={process.Id}, subMode={subMode}]"); targetSubMode = subMode; break; } await WMI.LenovoGameZoneData.SetIntelligentSubModeAsync(targetSubMode).ConfigureAwait(false); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Initial sub mode set."); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to start.", ex); } } private static async Task DisableAsync() { try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Stopping..."); await WMI.LenovoGameZoneData.SetIntelligentSubModeAsync(0).ConfigureAwait(false); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Stopped"); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to stop.", ex); } } } ================================================ FILE: LenovoLegionToolkit.Lib/Controllers/DisplayBrightnessController.cs ================================================ using System.Threading.Tasks; using LenovoLegionToolkit.Lib.System.Management; namespace LenovoLegionToolkit.Lib.Controllers; public class DisplayBrightnessController { public Task SetBrightnessAsync(int brightness) => WMI.WmiMonitorBrightnessMethods.WmiSetBrightness(brightness, 1); } ================================================ FILE: LenovoLegionToolkit.Lib/Controllers/GPUController.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Resources; using LenovoLegionToolkit.Lib.System; using LenovoLegionToolkit.Lib.System.Management; using LenovoLegionToolkit.Lib.Utils; using NeoSmart.AsyncLock; namespace LenovoLegionToolkit.Lib.Controllers; public class GPUController { private readonly AsyncLock _lock = new(); private Task? _refreshTask; private CancellationTokenSource? _refreshCancellationTokenSource; private GPUState _state = GPUState.Unknown; private List _processes = []; private string? _gpuInstanceId; private string? _performanceState; public event EventHandler? Refreshed; public bool IsStarted { get => _refreshTask != null; } public bool IsSupported() { try { NVAPI.Initialize(); return NVAPI.GetGPU() is not null; } catch { return false; } finally { try { NVAPI.Unload(); } catch { /* Ignored. */ } } } public async Task GetLastKnownStateAsync() { using (await _lock.LockAsync().ConfigureAwait(false)) return _state; } public async Task RefreshNowAsync() { using (await _lock.LockAsync().ConfigureAwait(false)) { await RefreshLoopAsync(0, 0, CancellationToken.None).ConfigureAwait(false); return new GPUStatus(_state, _performanceState, _processes); } } public Task StartAsync(int delay = 1_000, int interval = 5_000) { if (IsStarted) return Task.CompletedTask; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Starting... [delay={delay}, interval={interval}]"); _refreshCancellationTokenSource = new CancellationTokenSource(); var token = _refreshCancellationTokenSource.Token; _refreshTask = Task.Run(() => RefreshLoopAsync(delay, interval, token), token); return Task.CompletedTask; } public async Task StopAsync(bool waitForFinish = false) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Stopping... [refreshTask.isNull={_refreshTask is null}, _refreshCancellationTokenSource.IsCancellationRequested={_refreshCancellationTokenSource?.IsCancellationRequested}]"); if (_refreshCancellationTokenSource is not null) await _refreshCancellationTokenSource.CancelAsync().ConfigureAwait(false); if (waitForFinish) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Waiting to finish..."); if (_refreshTask is not null) { try { await _refreshTask.ConfigureAwait(false); } catch (OperationCanceledException) { } } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Finished"); } _refreshCancellationTokenSource = null; _refreshTask = null; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Stopped"); } public async Task RestartGPUAsync() { using (await _lock.LockAsync().ConfigureAwait(false)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Deactivating... [state={_state}, gpuInstanceId={_gpuInstanceId}]"); if (_state is not GPUState.Active and not GPUState.Inactive) return; if (string.IsNullOrEmpty(_gpuInstanceId)) return; await CMD.RunAsync("pnputil", $"/restart-device \"{_gpuInstanceId}\"").ConfigureAwait(false); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Deactivating... [state= {_state}, gpuInstanceId={_gpuInstanceId}]"); } } public async Task KillGPUProcessesAsync() { using (await _lock.LockAsync().ConfigureAwait(false)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Deactivating... [state= {_state}, gpuInstanceId={_gpuInstanceId}]"); if (_state is not GPUState.Active) return; if (string.IsNullOrEmpty(_gpuInstanceId)) return; foreach (var process in _processes) { try { process.Kill(true); await process.WaitForExitAsync().ConfigureAwait(false); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Couldn't kill process. [pid={process.Id}, name={process.ProcessName}]", ex); } } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Deactivating... [state= {_state}, gpuInstanceId={_gpuInstanceId}]"); } } private async Task RefreshLoopAsync(int delay, int interval, CancellationToken token) { try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Initializing NVAPI..."); NVAPI.Initialize(); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Initialized NVAPI"); await Task.Delay(delay, token).ConfigureAwait(false); while (true) { token.ThrowIfCancellationRequested(); using (await _lock.LockAsync(token).ConfigureAwait(false)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Will refresh..."); await RefreshStateAsync().ConfigureAwait(false); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Refreshed"); Refreshed?.Invoke(this, new GPUStatus(_state, _performanceState, _processes)); } if (interval > 0) await Task.Delay(interval, token).ConfigureAwait(false); else break; } } catch (Exception ex) when (ex is not OperationCanceledException) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Exception occurred", ex); throw; } finally { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Unloading NVAPI..."); NVAPI.Unload(); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Unloaded NVAPI"); } } private async Task RefreshStateAsync() { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Refresh in progress..."); _state = GPUState.Unknown; _processes = []; _gpuInstanceId = null; _performanceState = null; var gpu = NVAPI.GetGPU(); if (gpu is null) { _state = GPUState.NvidiaGpuNotFound; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"GPU present [state={_state}, processes.Count={_processes.Count}, gpuInstanceId={_gpuInstanceId}]"); return; } try { var stateId = gpu.PerformanceStatesInfo.CurrentPerformanceState.StateId.ToString().GetUntilOrEmpty("_"); _performanceState = Resource.GPUController_PoweredOn; if (!string.IsNullOrWhiteSpace(stateId)) _performanceState += $", {stateId}"; } catch (Exception ex) when (ex.Message == "NVAPI_GPU_NOT_POWERED") { _state = GPUState.PoweredOff; _performanceState = Resource.GPUController_PoweredOff; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Powered off [state={_state}, processes.Count={_processes.Count}, gpuInstanceId={_gpuInstanceId}]"); return; } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"GPU status exception.", ex); _performanceState = "Unknown"; } var pnpDeviceIdPart = NVAPI.GetGPUId(gpu); if (string.IsNullOrEmpty(pnpDeviceIdPart)) throw new InvalidOperationException("pnpDeviceIdPart is null or empty"); var gpuInstanceId = await WMI.Win32.PnpEntity.GetDeviceIDAsync(pnpDeviceIdPart).ConfigureAwait(false); var processNames = NVAPIExtensions.GetActiveProcesses(gpu); if (NVAPI.IsDisplayConnected(gpu)) { _processes = processNames; _state = GPUState.MonitorConnected; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace( $"Monitor connected [state={_state}, processes.Count={_processes.Count}, gpuInstanceId={_gpuInstanceId}]"); } else if (processNames.Count != 0) { _processes = processNames; _state = GPUState.Active; _gpuInstanceId = gpuInstanceId; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Active [state={_state}, processes.Count={_processes.Count}, gpuInstanceId={_gpuInstanceId}, pnpDeviceIdPart={pnpDeviceIdPart}]"); } else { _state = GPUState.Inactive; _gpuInstanceId = gpuInstanceId; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Inactive [state={_state}, processes.Count={_processes.Count}, gpuInstanceId={_gpuInstanceId}]"); } } } ================================================ FILE: LenovoLegionToolkit.Lib/Controllers/GPUOverclockController.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Listeners; using LenovoLegionToolkit.Lib.Settings; using LenovoLegionToolkit.Lib.SoftwareDisabler; using LenovoLegionToolkit.Lib.System; using LenovoLegionToolkit.Lib.System.Management; using LenovoLegionToolkit.Lib.Utils; using NvAPIWrapper.GPU; using NvAPIWrapper.Native; using NvAPIWrapper.Native.GPU; using NvAPIWrapper.Native.GPU.Structures; namespace LenovoLegionToolkit.Lib.Controllers; public class GPUOverclockController { private readonly GPUOverclockSettings _settings; private readonly VantageDisabler _vantageDisabler; private readonly LegionZoneDisabler _legionZoneDisabler; private readonly NativeWindowsMessageListener _nativeWindowsMessageListener; public event EventHandler? Changed; public GPUOverclockController(GPUOverclockSettings settings, VantageDisabler vantageDisabler, LegionZoneDisabler legionZoneDisabler, NativeWindowsMessageListener nativeWindowsMessageListener) { _settings = settings; _vantageDisabler = vantageDisabler; _legionZoneDisabler = legionZoneDisabler; _nativeWindowsMessageListener = nativeWindowsMessageListener; _nativeWindowsMessageListener.Changed += NativeWindowsMessageListenerOnChanged; } public static int GetMaxCoreDeltaMhz() => 500; public static int GetMaxMemoryDeltaMhz() { try { NVAPI.Initialize(); return GetMaxMemoryDeltaMhz(NVAPI.GetGPU()); } finally { try { NVAPI.Unload(); } catch { /* Ignored */ } } } public async Task IsSupportedAsync() { bool isSupported; try { NVAPI.Initialize(); isSupported = NVAPI.GetGPU() is not null; } catch { isSupported = false; } finally { try { NVAPI.Unload(); } catch { /* Ignored */ } } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"NVAPI status: {isSupported}."); if (!isSupported) return isSupported; try { isSupported = await WMI.LenovoGameZoneData.IsSupportGpuOCAsync().ConfigureAwait(false) > 0; if (!isSupported) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Clearing settings..."); _settings.Store.Enabled = false; _settings.Store.Info = GPUOverclockInfo.Zero; _settings.SynchronizeStore(); } } catch { isSupported = false; } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Supports GPU OC status: {isSupported}"); return isSupported; } public (bool, GPUOverclockInfo) GetState() => (_settings.Store.Enabled, _settings.Store.Info); public void SaveState(bool enabled, GPUOverclockInfo info) { _settings.Store.Enabled = enabled; _settings.Store.Info = info; _settings.SynchronizeStore(); } public async Task ApplyStateAsync(bool force = false) { if (await _vantageDisabler.GetStatusAsync().ConfigureAwait(false) == SoftwareStatus.Enabled) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Can't correctly apply state when Vantage is running."); Changed?.Invoke(this, EventArgs.Empty); return; } if (await _legionZoneDisabler.GetStatusAsync().ConfigureAwait(false) == SoftwareStatus.Enabled) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Can't correctly apply state when Legion Zone is running."); Changed?.Invoke(this, EventArgs.Empty); return; } var enabled = _settings.Store.Enabled; var info = _settings.Store.Info; if (force) { info = enabled ? info : GPUOverclockInfo.Zero; enabled = true; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Forcing... [enabled=true, info={info}]"); } if (!enabled) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Not enabled."); Changed?.Invoke(this, EventArgs.Empty); return; } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Applying overclock: {info}."); try { NVAPI.Initialize(); var gpu = NVAPI.GetGPU(); if (gpu is null) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"dGPU not found."); Changed?.Invoke(this, EventArgs.Empty); return; } SetOverclockInfo(gpu, info); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Applied overclock: {info}, current: {GetOverclockInfo(gpu)}."); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to apply overclock: {info}, clearing settings...", ex); _settings.Store.Enabled = false; _settings.Store.Info = GPUOverclockInfo.Zero; _settings.SynchronizeStore(); } finally { Changed?.Invoke(this, EventArgs.Empty); try { NVAPI.Unload(); } catch { /* Ignored */ } } } public async Task EnsureOverclockIsAppliedAsync() { var (enabled, _) = GetState(); if (!enabled) return false; await ApplyStateAsync().ConfigureAwait(false); return true; } private async void NativeWindowsMessageListenerOnChanged(object? sender, NativeWindowsMessageListener.ChangedEventArgs e) { if (e.Message != NativeWindowsMessage.OnDisplayDeviceArrival) return; if (await IsSupportedAsync().ConfigureAwait(false)) await ApplyStateAsync().ConfigureAwait(false); } private static int GetMaxMemoryDeltaMhz(PhysicalGPU? gpu) => gpu?.MemoryInformation.RAMMaker switch { GPUMemoryMaker.Samsung => 1500, _ => 750 }; private static void SetOverclockInfo(PhysicalGPU gpu, GPUOverclockInfo info) { var coreDelta = Math.Clamp(info.CoreDeltaMhz, 0, GetMaxCoreDeltaMhz()); var memoryDelta = Math.Clamp(info.MemoryDeltaMhz, 0, GetMaxMemoryDeltaMhz(gpu)); var clockEntries = new[] { new PerformanceStates20ClockEntryV1(PublicClockDomain.Graphics, new PerformanceStates20ParameterDelta(coreDelta * 1000)), new PerformanceStates20ClockEntryV1(PublicClockDomain.Memory, new PerformanceStates20ParameterDelta(memoryDelta * 1000)) }; var voltageEntries = Array.Empty(); var performanceStateInfo = new[] { new PerformanceStates20InfoV1.PerformanceState20(PerformanceStateId.P0_3DPerformance, clockEntries, voltageEntries) }; var overclock = new PerformanceStates20InfoV1(performanceStateInfo, 2, 0); GPUApi.SetPerformanceStates20(gpu.Handle, overclock); } private static GPUOverclockInfo GetOverclockInfo(PhysicalGPU gpu) { var states = GPUApi.GetPerformanceStates20(gpu.Handle); var core = states.Clocks[PerformanceStateId.P0_3DPerformance][0].FrequencyDeltaInkHz.DeltaValue / 1000; var memory = states.Clocks[PerformanceStateId.P0_3DPerformance][1].FrequencyDeltaInkHz.DeltaValue / 1000; return new(core, memory); } } ================================================ FILE: LenovoLegionToolkit.Lib/Controllers/GodMode/AbstractGodModeController.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Settings; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.Controllers.GodMode; public abstract class AbstractGodModeController(GodModeSettings settings) : IGodModeController { public event EventHandler? PresetChanged; public abstract Task NeedsVantageDisabledAsync(); public abstract Task NeedsLegionZoneDisabledAsync(); public Task GetActivePresetIdAsync() => Task.FromResult(settings.Store.ActivePresetId); public Task GetActivePresetNameAsync() { var store = settings.Store; var name = store.Presets .Where(p => p.Key == store.ActivePresetId) .Select(p => p.Value.Name) .FirstOrDefault(); return Task.FromResult(name); } public async Task GetStateAsync() { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Getting state..."); var store = settings.Store; var defaultState = await GetDefaultStateAsync().ConfigureAwait(false); if (!IsValidStore(store)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Loading default state..."); var id = Guid.NewGuid(); return new GodModeState { ActivePresetId = id, Presets = new Dictionary { { id, defaultState } }.AsReadOnlyDictionary() }; } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Loading state from store..."); return await LoadStateFromStoreAsync(store, defaultState).ConfigureAwait(false); } public Task SetStateAsync(GodModeState state) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Setting state..."); var activePresetId = state.ActivePresetId; var presets = new Dictionary(); foreach (var (id, preset) in state.Presets) { presets.Add(id, new() { Name = preset.Name, CPULongTermPowerLimit = preset.CPULongTermPowerLimit, CPUShortTermPowerLimit = preset.CPUShortTermPowerLimit, CPUPeakPowerLimit = preset.CPUPeakPowerLimit, CPUCrossLoadingPowerLimit = preset.CPUCrossLoadingPowerLimit, CPUPL1Tau = preset.CPUPL1Tau, APUsPPTPowerLimit = preset.APUsPPTPowerLimit, CPUTemperatureLimit = preset.CPUTemperatureLimit, GPUPowerBoost = preset.GPUPowerBoost, GPUConfigurableTGP = preset.GPUConfigurableTGP, GPUTemperatureLimit = preset.GPUTemperatureLimit, GPUTotalProcessingPowerTargetOnAcOffsetFromBaseline = preset.GPUTotalProcessingPowerTargetOnAcOffsetFromBaseline, GPUToCPUDynamicBoost = preset.GPUToCPUDynamicBoost, FanTable = preset.FanTableInfo?.Table, FanFullSpeed = preset.FanFullSpeed, MinValueOffset = preset.MinValueOffset, MaxValueOffset = preset.MaxValueOffset, }); } settings.Store.ActivePresetId = activePresetId; settings.Store.Presets = presets; settings.SynchronizeStore(); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"State saved."); return Task.CompletedTask; } public abstract Task ApplyStateAsync(); public Task GetDefaultFanTableAsync() { var fanTable = new FanTable([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); return Task.FromResult(fanTable); } public abstract Task GetMinimumFanTableAsync(); public abstract Task> GetDefaultsInOtherPowerModesAsync(); public abstract Task RestoreDefaultsInOtherPowerModeAsync(PowerModeState state); protected abstract Task GetDefaultStateAsync(); protected void RaisePresetChanged(Guid presetId) => PresetChanged?.Invoke(this, presetId); protected async Task<(Guid, GodModeSettings.GodModeSettingsStore.Preset)> GetActivePresetAsync() { if (!IsValidStore(settings.Store)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Invalid store, generating default one."); var state = await GetStateAsync().ConfigureAwait(false); await SetStateAsync(state).ConfigureAwait(false); } var activePresetId = settings.Store.ActivePresetId; var presets = settings.Store.Presets; if (presets.TryGetValue(activePresetId, out var activePreset)) return (activePresetId, activePreset); throw new InvalidOperationException($"Preset with ID {activePresetId} not found"); } protected async Task IsValidFanTableAsync(FanTable fanTable) { var minimumFanTable = await GetMinimumFanTableAsync().ConfigureAwait(false); var minimum = minimumFanTable.GetTable(); return fanTable.GetTable().Where((t, i) => t < minimum[i] || t > 10u).IsEmpty(); } private static bool IsValidStore(GodModeSettings.GodModeSettingsStore store) => store.Presets.Count != 0 && store.Presets.ContainsKey(store.ActivePresetId); private async Task LoadStateFromStoreAsync(GodModeSettings.GodModeSettingsStore store, GodModePreset defaultState) { var states = new Dictionary(); foreach (var (id, preset) in store.Presets) { states.Add(id, new GodModePreset { Name = preset.Name, CPULongTermPowerLimit = CreateStepperValue(defaultState.CPULongTermPowerLimit, preset.CPULongTermPowerLimit, preset.MinValueOffset, preset.MaxValueOffset), CPUShortTermPowerLimit = CreateStepperValue(defaultState.CPUShortTermPowerLimit, preset.CPUShortTermPowerLimit, preset.MinValueOffset, preset.MaxValueOffset), CPUPeakPowerLimit = CreateStepperValue(defaultState.CPUPeakPowerLimit, preset.CPUPeakPowerLimit, preset.MinValueOffset, preset.MaxValueOffset), CPUCrossLoadingPowerLimit = CreateStepperValue(defaultState.CPUCrossLoadingPowerLimit, preset.CPUCrossLoadingPowerLimit, preset.MinValueOffset, preset.MaxValueOffset), CPUPL1Tau = CreateStepperValue(defaultState.CPUPL1Tau, preset.CPUPL1Tau, preset.MinValueOffset, preset.MaxValueOffset), APUsPPTPowerLimit = CreateStepperValue(defaultState.APUsPPTPowerLimit, preset.APUsPPTPowerLimit, preset.MinValueOffset, preset.MaxValueOffset), CPUTemperatureLimit = CreateStepperValue(defaultState.CPUTemperatureLimit, preset.CPUTemperatureLimit, preset.MinValueOffset, preset.MaxValueOffset), GPUPowerBoost = CreateStepperValue(defaultState.GPUPowerBoost, preset.GPUPowerBoost, preset.MinValueOffset, preset.MaxValueOffset), GPUConfigurableTGP = CreateStepperValue(defaultState.GPUConfigurableTGP, preset.GPUConfigurableTGP, preset.MinValueOffset, preset.MaxValueOffset), GPUTemperatureLimit = CreateStepperValue(defaultState.GPUTemperatureLimit, preset.GPUTemperatureLimit, preset.MinValueOffset, preset.MaxValueOffset), GPUTotalProcessingPowerTargetOnAcOffsetFromBaseline = CreateStepperValue(defaultState.GPUTotalProcessingPowerTargetOnAcOffsetFromBaseline, preset.GPUTotalProcessingPowerTargetOnAcOffsetFromBaseline, preset.MinValueOffset, preset.MaxValueOffset), GPUToCPUDynamicBoost = CreateStepperValue(defaultState.GPUToCPUDynamicBoost, preset.GPUToCPUDynamicBoost), FanTableInfo = await GetFanTableInfoAsync(preset, defaultState.FanTableInfo?.Data).ConfigureAwait(false), FanFullSpeed = preset.FanFullSpeed, MinValueOffset = preset.MinValueOffset ?? defaultState.MinValueOffset, MaxValueOffset = preset.MaxValueOffset ?? defaultState.MaxValueOffset }); } return new GodModeState { ActivePresetId = store.ActivePresetId, Presets = states.AsReadOnlyDictionary() }; } private static StepperValue? CreateStepperValue(StepperValue? state, StepperValue? store = null, int? minValueOffset = 0, int? maxValueOffset = 0) { if (state is not { } stateValue) return null; if (stateValue.Steps.Length > 0) { var value = store?.Value ?? stateValue.Value; var steps = stateValue.Steps; var defaultValue = stateValue.DefaultValue; if (!steps.Contains(value)) { var valueTemp = value; value = steps.MinBy(v => Math.Abs((long)v - valueTemp)); } return new(value, 0, 0, 0, steps, defaultValue); } if (stateValue.Step > 0) { var value = store?.Value ?? stateValue.Value; var min = Math.Max(0, stateValue.Min + (minValueOffset ?? 0)); var max = stateValue.Max + (maxValueOffset ?? 0); var step = stateValue.Step; var defaultValue = stateValue.DefaultValue; value = MathExtensions.RoundNearest(value, step); if (value < min || value > max) value = defaultValue ?? Math.Clamp(value, min, max); return new(value, min, max, step, [], defaultValue); } return null; } private async Task GetFanTableInfoAsync(GodModeSettings.GodModeSettingsStore.Preset preset, FanTableData[]? fanTableData) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Getting fan table info..."); if (fanTableData is null) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Fan table data is null"); return null; } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Fan table data retrieved: {fanTableData}"); var fanTable = preset.FanTable ?? await GetDefaultFanTableAsync().ConfigureAwait(false); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Fan table retrieved: {fanTable}"); if (!await IsValidFanTableAsync(fanTable).ConfigureAwait(false)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Fan table invalid, replacing with default..."); fanTable = await GetDefaultFanTableAsync().ConfigureAwait(false); } return new FanTableInfo(fanTableData, fanTable); } } ================================================ FILE: LenovoLegionToolkit.Lib/Controllers/GodMode/GodModeController.cs ================================================ using System; using System.Collections.Generic; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.Controllers.GodMode; public class GodModeController(GodModeControllerV1 controllerV1, GodModeControllerV2 controllerV2) : IGodModeController { private IGodModeController ControllerV1 => controllerV1; private IGodModeController ControllerV2 => controllerV2; public event EventHandler? PresetChanged { add { ControllerV1.PresetChanged += value; ControllerV2.PresetChanged += value; } remove { ControllerV1.PresetChanged -= value; ControllerV2.PresetChanged -= value; } } public async Task NeedsVantageDisabledAsync() { var controller = await GetControllerAsync().ConfigureAwait(false); return await controller.NeedsVantageDisabledAsync().ConfigureAwait(false); } public async Task NeedsLegionZoneDisabledAsync() { var controller = await GetControllerAsync().ConfigureAwait(false); return await controller.NeedsLegionZoneDisabledAsync().ConfigureAwait(false); } public async Task GetActivePresetIdAsync() { var controller = await GetControllerAsync().ConfigureAwait(false); return await controller.GetActivePresetIdAsync().ConfigureAwait(false); } public async Task GetActivePresetNameAsync() { var controller = await GetControllerAsync().ConfigureAwait(false); return await controller.GetActivePresetNameAsync().ConfigureAwait(false); } public async Task GetStateAsync() { var controller = await GetControllerAsync().ConfigureAwait(false); return await controller.GetStateAsync().ConfigureAwait(false); } public async Task SetStateAsync(GodModeState state) { var controller = await GetControllerAsync().ConfigureAwait(false); await controller.SetStateAsync(state).ConfigureAwait(false); } public async Task ApplyStateAsync() { var controller = await GetControllerAsync().ConfigureAwait(false); await controller.ApplyStateAsync().ConfigureAwait(false); } public async Task GetDefaultFanTableAsync() { var controller = await GetControllerAsync().ConfigureAwait(false); return await controller.GetDefaultFanTableAsync().ConfigureAwait(false); } public async Task GetMinimumFanTableAsync() { var controller = await GetControllerAsync().ConfigureAwait(false); return await controller.GetMinimumFanTableAsync().ConfigureAwait(false); } public async Task> GetDefaultsInOtherPowerModesAsync() { var controller = await GetControllerAsync().ConfigureAwait(false); return await controller.GetDefaultsInOtherPowerModesAsync().ConfigureAwait(false); } public async Task RestoreDefaultsInOtherPowerModeAsync(PowerModeState state) { var controller = await GetControllerAsync().ConfigureAwait(false); await controller.RestoreDefaultsInOtherPowerModeAsync(state).ConfigureAwait(false); } private async Task GetControllerAsync() { var mi = await Compatibility.GetMachineInformationAsync().ConfigureAwait(false); if (mi.Properties.SupportsGodModeV1) return controllerV1; if (mi.Properties.SupportsGodModeV2) return controllerV2; throw new InvalidOperationException("No supported version found"); } } ================================================ FILE: LenovoLegionToolkit.Lib/Controllers/GodMode/GodModeControllerV1.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Settings; using LenovoLegionToolkit.Lib.SoftwareDisabler; using LenovoLegionToolkit.Lib.System.Management; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.Controllers.GodMode; public class GodModeControllerV1( GodModeSettings settings, LegionZoneDisabler legionZoneDisabler) : AbstractGodModeController(settings) { public override Task NeedsVantageDisabledAsync() => Task.FromResult(false); public override Task NeedsLegionZoneDisabledAsync() => Task.FromResult(true); public override async Task ApplyStateAsync() { if (await legionZoneDisabler.GetStatusAsync().ConfigureAwait(false) == SoftwareStatus.Enabled) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Can't correctly apply state when Legion Zone is running."); return; } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Applying state..."); var (presetId, preset) = await GetActivePresetAsync().ConfigureAwait(false); var cpuLongTermPowerLimit = preset.CPULongTermPowerLimit; var cpuShortTermPowerLimit = preset.CPUShortTermPowerLimit; var cpuPeakPowerLimit = preset.CPUPeakPowerLimit; var cpuCrossLoadingPowerLimit = preset.CPUCrossLoadingPowerLimit; var apuSPPTPowerLimit = preset.APUsPPTPowerLimit; var cpuTemperatureLimit = preset.CPUTemperatureLimit; var gpuPowerBoost = preset.GPUPowerBoost; var gpuConfigurableTgp = preset.GPUConfigurableTGP; var gpuTemperatureLimit = preset.GPUTemperatureLimit; var fanTable = preset.FanTable ?? await GetDefaultFanTableAsync().ConfigureAwait(false); var fanFullSpeed = preset.FanFullSpeed ?? false; if (cpuLongTermPowerLimit is not null) { try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Applying CPU Long Term Power Limit: {cpuLongTermPowerLimit}"); await SetCPULongTermPowerLimitAsync(cpuLongTermPowerLimit.Value.Value).ConfigureAwait(false); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Apply failed. [setting=cpuLongTermPowerLimit]", ex); throw; } } if (cpuShortTermPowerLimit is not null) { try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Applying CPU Short Term Power Limit: {cpuShortTermPowerLimit}"); await SetCPUShortTermPowerLimitAsync(cpuShortTermPowerLimit.Value.Value).ConfigureAwait(false); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Apply failed. [setting=cpuShortTermPowerLimit]", ex); throw; } } if (cpuPeakPowerLimit is not null) { try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Applying CPU Peak Power Limit: {cpuPeakPowerLimit}"); await SetCPUPeakPowerLimitAsync(cpuPeakPowerLimit.Value.Value).ConfigureAwait(false); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Apply failed. [setting=cpuPeakPowerLimit]", ex); throw; } } if (cpuCrossLoadingPowerLimit is not null) { try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Applying CPU Cross Loading Power Limit: {cpuCrossLoadingPowerLimit}"); await SetCPUCrossLoadingPowerLimitAsync(cpuCrossLoadingPowerLimit.Value.Value).ConfigureAwait(false); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Apply failed. [setting=cpuCrossLoadingPowerLimit]", ex); throw; } } if (apuSPPTPowerLimit is not null) { try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Applying APU sPPT Power Limit: {apuSPPTPowerLimit}"); await SetAPUSPPTPowerLimitAsync(apuSPPTPowerLimit.Value.Value).ConfigureAwait(false); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Apply failed. [setting=apuSPPTPowerLimit]", ex); throw; } } if (cpuTemperatureLimit is not null) { try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Applying CPU Temperature Limit: {cpuTemperatureLimit}"); await SetCPUTemperatureLimitAsync(cpuTemperatureLimit.Value.Value).ConfigureAwait(false); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Apply failed. [setting=cpuTemperatureLimit]", ex); throw; } } if (gpuPowerBoost is not null) { try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Applying GPU Power Boost: {gpuPowerBoost}"); await SetGPUPowerBoostAsync(gpuPowerBoost.Value.Value).ConfigureAwait(false); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Apply failed. [setting=gpuPowerBoost]", ex); } } if (gpuConfigurableTgp is not null) { try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Applying GPU Configurable TGP: {gpuConfigurableTgp}"); await SetGPUConfigurableTGPAsync(gpuConfigurableTgp.Value.Value).ConfigureAwait(false); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Apply failed. [setting=gpuConfigurableTgp]", ex); } } if (gpuTemperatureLimit is not null) { try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Applying GPU Temperature Limit: {gpuTemperatureLimit}"); await SetGPUTemperatureLimitAsync(gpuTemperatureLimit.Value.Value).ConfigureAwait(false); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Apply failed. [setting=gpuTemperatureLimit]", ex); } } if (fanFullSpeed) { try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Applying Fan Full Speed {fanFullSpeed}..."); await SetFanFullSpeedAsync(fanFullSpeed).ConfigureAwait(false); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Apply failed. [setting=fanFullSpeed]", ex); throw; } } else { try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Making sure Fan Full Speed is false..."); await SetFanFullSpeedAsync(false).ConfigureAwait(false); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Apply failed. [setting=fanFullSpeed]", ex); throw; } try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Applying Fan Table {fanTable}..."); if (!await IsValidFanTableAsync(fanTable).ConfigureAwait(false)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Fan table invalid, replacing with default..."); fanTable = await GetDefaultFanTableAsync().ConfigureAwait(false); } await SetFanTable(fanTable).ConfigureAwait(false); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Apply failed. [setting=fanTable]", ex); throw; } } RaisePresetChanged(presetId); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"State applied. [name={preset.Name}, id={presetId}]"); } public override Task GetMinimumFanTableAsync() { var fanTable = new FanTable([0, 0, 0, 0, 0, 0, 0, 1, 3, 5]); return Task.FromResult(fanTable); } public override async Task> GetDefaultsInOtherPowerModesAsync() { try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Getting defaults in other power modes..."); var result = await GetDefaultValuesInDifferentModeAsync().ConfigureAwait(false); if (Log.Instance.IsTraceEnabled) { Log.Instance.Trace($"Defaults in other power modes retrieved:"); foreach (var (powerMode, defaults) in result) Log.Instance.Trace($" - {powerMode}: {defaults}"); } return result; } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to get defaults in other power modes.", ex); return []; } } public override async Task RestoreDefaultsInOtherPowerModeAsync(PowerModeState state) { try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Restoring defaults for {state}..."); var result = await GetDefaultValuesInDifferentModeAsync().ConfigureAwait(false); if (!result.TryGetValue(state, out var defaults)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Defaults for {state} not found. Skipping..."); return; } if (defaults.CPULongTermPowerLimit is not null) { try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Applying CPU Long Term Power Limit: {defaults.CPULongTermPowerLimit}"); await SetCPULongTermPowerLimitAsync(defaults.CPULongTermPowerLimit.Value).ConfigureAwait(false); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Apply failed. [setting=cpuLongTermPowerLimit]", ex); throw; } } if (defaults.CPUShortTermPowerLimit is not null) { try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Applying CPU Short Term Power Limit: {defaults.CPUShortTermPowerLimit}"); await SetCPUShortTermPowerLimitAsync(defaults.CPUShortTermPowerLimit.Value).ConfigureAwait(false); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Apply failed. [setting=cpuShortTermPowerLimit]", ex); throw; } } if (defaults.CPUPeakPowerLimit is not null) { try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Applying CPU Peak Power Limit: {defaults.CPUPeakPowerLimit}"); await SetCPUPeakPowerLimitAsync(defaults.CPUPeakPowerLimit.Value).ConfigureAwait(false); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Apply failed. [setting=cpuPeakPowerLimit]", ex); throw; } } if (defaults.CPUCrossLoadingPowerLimit is not null) { try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Applying CPU Cross Loading Power Limit: {defaults.CPUCrossLoadingPowerLimit}"); await SetCPUCrossLoadingPowerLimitAsync(defaults.CPUCrossLoadingPowerLimit.Value).ConfigureAwait(false); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Apply failed. [setting=cpuCrossLoadingPowerLimit]", ex); throw; } } if (defaults.APUsPPTPowerLimit is not null) { try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Applying APU sPPT Power Limit: {defaults.APUsPPTPowerLimit}"); await SetAPUSPPTPowerLimitAsync(defaults.APUsPPTPowerLimit.Value).ConfigureAwait(false); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Apply failed. [setting=apuSPPTPowerLimit]", ex); throw; } } if (defaults.CPUTemperatureLimit is not null) { try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Applying CPU Temperature Limit: {defaults.CPUTemperatureLimit}"); await SetCPUTemperatureLimitAsync(defaults.CPUTemperatureLimit.Value).ConfigureAwait(false); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Apply failed. [setting=cpuTemperatureLimit]", ex); throw; } } if (defaults.GPUPowerBoost is not null) { try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Applying GPU Power Boost: {defaults.GPUPowerBoost}"); await SetGPUPowerBoostAsync(defaults.GPUPowerBoost.Value).ConfigureAwait(false); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Apply failed. [setting=gpuPowerBoost]", ex); throw; } } if (defaults.GPUConfigurableTGP is not null) { try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Applying GPU Configurable TGP: {defaults.GPUConfigurableTGP}"); await SetGPUConfigurableTGPAsync(defaults.GPUConfigurableTGP.Value).ConfigureAwait(false); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Apply failed. [setting=gpuConfigurableTgp]", ex); throw; } } } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to restore defaults for {state}.", ex); } } protected override async Task GetDefaultStateAsync() { var fanTableData = await GetFanTableDataAsync().ConfigureAwait(false); var preset = new GodModePreset { Name = "Default", CPULongTermPowerLimit = await GetCPULongTermPowerLimitAsync().OrNullIfException().ConfigureAwait(false), CPUShortTermPowerLimit = await GetCPUShortTermPowerLimitAsync().OrNullIfException().ConfigureAwait(false), CPUPeakPowerLimit = await GetCPUPeakPowerLimitAsync().OrNullIfException().ConfigureAwait(false), CPUCrossLoadingPowerLimit = await GetCPUCrossLoadingPowerLimitAsync().OrNullIfException().ConfigureAwait(false), APUsPPTPowerLimit = await GetAPUSPPTPowerLimitAsync().OrNullIfException().ConfigureAwait(false), CPUTemperatureLimit = await GetCPUTemperatureLimitAsync().OrNullIfException().ConfigureAwait(false), GPUPowerBoost = await GetGPUPowerBoost().OrNullIfException().ConfigureAwait(false), GPUConfigurableTGP = await GetGPUConfigurableTGPAsync().OrNullIfException().ConfigureAwait(false), GPUTemperatureLimit = await GetGPUTemperatureLimitAsync().OrNullIfException().ConfigureAwait(false), FanTableInfo = fanTableData is null ? null : new FanTableInfo(fanTableData, await GetDefaultFanTableAsync().ConfigureAwait(false)), FanFullSpeed = await GetFanFullSpeedAsync().ConfigureAwait(false), MinValueOffset = 0, MaxValueOffset = 0 }; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Default state retrieved: {preset}"); return preset; } #region CPU Long Term Power Limit private static async Task GetCPULongTermPowerLimitAsync() { var defaultValue = await WMI.LenovoCpuMethod.CPUGetDefaultPowerLimitAsync().OrNullIfException().ConfigureAwait(false); var (value, min, max, step) = await WMI.LenovoCpuMethod.CPUGetLongTermPowerLimitAsync().ConfigureAwait(false); return new(value, min, max, step, [], defaultValue?.longTerm); } private static Task SetCPULongTermPowerLimitAsync(int value) => WMI.LenovoCpuMethod.CPUSetLongTermPowerLimitAsync(value); #endregion #region CPU Short Term Power Limit private static async Task GetCPUShortTermPowerLimitAsync() { var defaultValue = await WMI.LenovoCpuMethod.CPUGetDefaultPowerLimitAsync().OrNullIfException().ConfigureAwait(false); var (value, min, max, step) = await WMI.LenovoCpuMethod.CPUGetShortTermPowerLimitAsync().ConfigureAwait(false); return new(value, min, max, step, [], defaultValue?.shortTerm); } private static Task SetCPUShortTermPowerLimitAsync(int value) => WMI.LenovoCpuMethod.CPUSetShortTermPowerLimitAsync(value); #endregion #region CPU Peak Power Limit private static async Task GetCPUPeakPowerLimitAsync() { var (value, min, max, step, defaultValue) = await WMI.LenovoCpuMethod.CPUGetPeakPowerLimitAsync().ConfigureAwait(false); return new(value, min, max, step, [], defaultValue); } private static Task SetCPUPeakPowerLimitAsync(int value) => WMI.LenovoCpuMethod.CPUSetPeakPowerLimitAsync(value); #endregion #region CPU Cross Loading Power Limit private static async Task GetCPUCrossLoadingPowerLimitAsync() { var (value, min, max, step, defaultValue) = await WMI.LenovoCpuMethod.CPUGetCrossLoadingPowerLimitAsync().ConfigureAwait(false); return new(value, min, max, step, [], defaultValue); } private static Task SetCPUCrossLoadingPowerLimitAsync(int value) => WMI.LenovoCpuMethod.CPUSetCrossLoadingPowerLimitAsync(value); #endregion #region APU sPPT Power Limit private static async Task GetAPUSPPTPowerLimitAsync() { var (value, min, max, step, defaultValue) = await WMI.LenovoCpuMethod.GetAPUSPPTPowerLimitAsync().ConfigureAwait(false); return new(value, min, max, step, [], defaultValue); } private static Task SetAPUSPPTPowerLimitAsync(int value) => WMI.LenovoCpuMethod.SetAPUSPPTPowerLimitAsync(value); #endregion #region CPU Temperature Limit private static async Task GetCPUTemperatureLimitAsync() { var (value, min, max, step, defaultValue) = await WMI.LenovoCpuMethod.CPUGetTemperatureControlAsync().ConfigureAwait(false); return new(value, min, max, step, [], defaultValue); } private static Task SetCPUTemperatureLimitAsync(int value) => WMI.LenovoCpuMethod.CPUSetTemperatureControlAsync(value); #endregion #region GPU Configurable TGP private static async Task GetGPUConfigurableTGPAsync() { var defaultValue = await WMI.LenovoGpuMethod.GPUGetDefaultPPABcTGPPowerLimit().OrNullIfException().ConfigureAwait(false); var (value, min, max, step) = await WMI.LenovoGpuMethod.GPUGetCTGPPowerLimitAsync().ConfigureAwait(false); return new(value, min, max, step, [], defaultValue?.ctgp); } private static Task SetGPUConfigurableTGPAsync(int value) => WMI.LenovoGpuMethod.GPUSetCTGPPowerLimitAsync(value); #endregion #region GPU Power Boost private static async Task GetGPUPowerBoost() { var defaultValue = await WMI.LenovoGpuMethod.GPUGetDefaultPPABcTGPPowerLimit().OrNullIfException().ConfigureAwait(false); var (value, min, max, step) = await WMI.LenovoGpuMethod.GPUGetPPABPowerLimitAsync().ConfigureAwait(false); return new(value, min, max, step, [], defaultValue?.ppab); } private static Task SetGPUPowerBoostAsync(int value) => WMI.LenovoGpuMethod.GPUSetPPABPowerLimitAsync(value); #endregion #region GPU Temperature Limit private static async Task GetGPUTemperatureLimitAsync() { var (value, min, max, step, defaultValue) = await WMI.LenovoGpuMethod.GPUGetTemperatureLimitAsync().ConfigureAwait(false); return new(value, min, max, step, [], defaultValue); } private static Task SetGPUTemperatureLimitAsync(int value) => WMI.LenovoGpuMethod.GPUSetTemperatureLimitAsync(value); #endregion #region Fan Table private static async Task GetFanTableDataAsync() { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Reading fan table data..."); var data = await WMI.LenovoFanTableData.ReadAsync().ConfigureAwait(false); var fanTableData = data .Select(d => { var type = (d.fanId, d.sensorId) switch { (0, 3) => FanTableType.CPU, (1, 4) => FanTableType.GPU, (0, 0) => FanTableType.CPUSensor, _ => FanTableType.Unknown, }; return new FanTableData(type, d.fanId, d.sensorId, d.fanTableData, d.sensorTableData); }) .ToArray(); if (fanTableData.Length != 3) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Bad fan table length: {fanTableData}"); return null; } if (fanTableData.Count(ftd => ftd.FanSpeeds.Length == 10) != 3) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Bad fan table fan speeds length: {fanTableData}"); return null; } if (fanTableData.Count(ftd => ftd.Temps.Length == 10) != 3) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Bad fan table temps length: {fanTableData}"); return null; } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Fan table data: {fanTableData}"); return fanTableData; } private static Task SetFanTable(FanTable fanTable) => WMI.LenovoFanMethod.FanSetTableAsync(fanTable.GetBytes()); #endregion #region Fan Full Speed private static Task GetFanFullSpeedAsync() => WMI.LenovoFanMethod.FanGetFullSpeedAsync(); private static Task SetFanFullSpeedAsync(bool enabled) => WMI.LenovoFanMethod.FanSetFullSpeedAsync(enabled ? 1 : 0); #endregion #region Default values private async Task> GetDefaultValuesInDifferentModeAsync() { var defaultFanTableAsync = await GetDefaultFanTableAsync().ConfigureAwait(false); var result = await WMI.LenovoDefaultValueInDifferentModeData.ReadAsync().ConfigureAwait(false); return result.Select(d => { var powerMode = (PowerModeState)(d.Mode - 1); var defaults = new GodModeDefaults { CPULongTermPowerLimit = d.CPULongTermPowerLimit, CPUShortTermPowerLimit = d.CPUShortTermPowerLimit, CPUPeakPowerLimit = d.CPUPeakPowerLimit, CPUCrossLoadingPowerLimit = d.CPUCrossLoadingPowerLimit, APUsPPTPowerLimit = d.APUsPPTPowerLimit, CPUTemperatureLimit = d.CPUTemperatureLimit, GPUPowerBoost = d.GPUPowerBoost, GPUConfigurableTGP = d.GPUConfigurableTGP, GPUTemperatureLimit = d.GPUTemperatureLimit, FanTable = defaultFanTableAsync, FanFullSpeed = false }; return (powerMode, defaults); }) .Where(d => d.powerMode is PowerModeState.Quiet or PowerModeState.Balance or PowerModeState.Performance) .DistinctBy(d => d.powerMode) .ToDictionary(d => d.powerMode, d => d.defaults); } #endregion } ================================================ FILE: LenovoLegionToolkit.Lib/Controllers/GodMode/GodModeControllerV2.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Settings; using LenovoLegionToolkit.Lib.SoftwareDisabler; using LenovoLegionToolkit.Lib.System.Management; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.Controllers.GodMode; public class GodModeControllerV2( GodModeSettings settings, VantageDisabler vantageDisabler, LegionZoneDisabler legionZoneDisabler) : AbstractGodModeController(settings) { public override Task NeedsVantageDisabledAsync() => Task.FromResult(true); public override Task NeedsLegionZoneDisabledAsync() => Task.FromResult(true); public override async Task ApplyStateAsync() { if (await vantageDisabler.GetStatusAsync().ConfigureAwait(false) == SoftwareStatus.Enabled) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Can't correctly apply state when Vantage is running."); return; } if (await legionZoneDisabler.GetStatusAsync().ConfigureAwait(false) == SoftwareStatus.Enabled) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Can't correctly apply state when Legion Zone is running."); return; } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Applying state..."); var (presetId, preset) = await GetActivePresetAsync().ConfigureAwait(false); var settings = new Dictionary { { CapabilityID.CPULongTermPowerLimit, preset.CPULongTermPowerLimit }, { CapabilityID.CPUShortTermPowerLimit, preset.CPUShortTermPowerLimit }, { CapabilityID.CPUPeakPowerLimit, preset.CPUPeakPowerLimit }, { CapabilityID.CPUCrossLoadingPowerLimit, preset.CPUCrossLoadingPowerLimit }, { CapabilityID.CPUPL1Tau, preset.CPUPL1Tau }, { CapabilityID.APUsPPTPowerLimit, preset.APUsPPTPowerLimit }, { CapabilityID.CPUTemperatureLimit, preset.CPUTemperatureLimit }, { CapabilityID.GPUPowerBoost, preset.GPUPowerBoost }, { CapabilityID.GPUConfigurableTGP, preset.GPUConfigurableTGP }, { CapabilityID.GPUTemperatureLimit, preset.GPUTemperatureLimit }, { CapabilityID.GPUTotalProcessingPowerTargetOnAcOffsetFromBaseline, preset.GPUTotalProcessingPowerTargetOnAcOffsetFromBaseline }, { CapabilityID.GPUToCPUDynamicBoost, preset.GPUToCPUDynamicBoost }, }; var defaultPresets = await GetDefaultsInOtherPowerModesAsync().ConfigureAwait(false); var defaultPerformancePreset = defaultPresets.GetValueOrNull(PowerModeState.Performance); var defaultPerformanceSettings = new Dictionary { { CapabilityID.CPULongTermPowerLimit, defaultPerformancePreset?.CPULongTermPowerLimit }, { CapabilityID.CPUShortTermPowerLimit, defaultPerformancePreset?.CPUShortTermPowerLimit }, { CapabilityID.CPUPeakPowerLimit, defaultPerformancePreset?.CPUPeakPowerLimit }, { CapabilityID.CPUCrossLoadingPowerLimit, defaultPerformancePreset?.CPUCrossLoadingPowerLimit }, { CapabilityID.CPUPL1Tau, defaultPerformancePreset?.CPUPL1Tau }, { CapabilityID.APUsPPTPowerLimit, defaultPerformancePreset?.APUsPPTPowerLimit }, { CapabilityID.CPUTemperatureLimit, defaultPerformancePreset?.CPUTemperatureLimit }, { CapabilityID.GPUPowerBoost, defaultPerformancePreset?.GPUPowerBoost }, { CapabilityID.GPUConfigurableTGP, defaultPerformancePreset?.GPUConfigurableTGP }, { CapabilityID.GPUTemperatureLimit, defaultPerformancePreset?.GPUTemperatureLimit }, { CapabilityID.GPUTotalProcessingPowerTargetOnAcOffsetFromBaseline, defaultPerformancePreset?.GPUTotalProcessingPowerTargetOnAcOffsetFromBaseline }, { CapabilityID.GPUToCPUDynamicBoost, defaultPerformancePreset?.GPUToCPUDynamicBoost }, }; var failAllowedSettings = new[] { CapabilityID.GPUPowerBoost, CapabilityID.GPUConfigurableTGP, CapabilityID.GPUTemperatureLimit, CapabilityID.GPUTotalProcessingPowerTargetOnAcOffsetFromBaseline, CapabilityID.GPUToCPUDynamicBoost, }; var fanTable = preset.FanTable ?? await GetDefaultFanTableAsync().ConfigureAwait(false); var fanFullSpeed = preset.FanFullSpeed ?? false; foreach (var (id, value) in settings) { if (value.HasValue) { try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Applying {id}: {value}..."); await SetValueAsync(id, value.Value).ConfigureAwait(false); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to apply {id}. [value={value}]", ex); if (!failAllowedSettings.Contains(id)) throw; } } else if (defaultPerformanceSettings.GetValueOrDefault(id) is { } defaultPerformanceValue) { try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Applying default {id}: {defaultPerformanceValue}..."); await SetValueAsync(id, defaultPerformanceValue).ConfigureAwait(false); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to apply default {id}. [value={defaultPerformanceValue}]", ex); if (!failAllowedSettings.Contains(id)) throw; } } else { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to apply {id}, because neither value nor default value was available."); } } if (fanFullSpeed) { try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Applying Fan Full Speed {fanFullSpeed}..."); await SetFanFullSpeedAsync(fanFullSpeed).ConfigureAwait(false); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Apply failed. [setting=fanFullSpeed]", ex); throw; } } else { try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Making sure Fan Full Speed is false..."); await SetFanFullSpeedAsync(false).ConfigureAwait(false); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Apply failed. [setting=fanFullSpeed]", ex); throw; } try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Applying Fan Table {fanTable}..."); if (!await IsValidFanTableAsync(fanTable).ConfigureAwait(false)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Fan table invalid, replacing with default..."); fanTable = await GetDefaultFanTableAsync().ConfigureAwait(false); } await SetFanTable(fanTable).ConfigureAwait(false); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Apply failed. [setting=fanTable]", ex); throw; } } RaisePresetChanged(presetId); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"State applied. [name={preset.Name}, id={presetId}]"); } public override Task GetMinimumFanTableAsync() { var fanTable = new FanTable([1, 1, 1, 1, 1, 1, 1, 1, 3, 5]); return Task.FromResult(fanTable); } public override async Task> GetDefaultsInOtherPowerModesAsync() { try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Getting defaults in other power modes..."); var result = new Dictionary(); var allCapabilityData = await WMI.LenovoCapabilityData01.ReadAsync().ConfigureAwait(false); allCapabilityData = allCapabilityData.ToArray(); foreach (var powerMode in new[] { PowerModeState.Quiet, PowerModeState.Balance, PowerModeState.Performance }) { var defaults = new GodModeDefaults { CPULongTermPowerLimit = GetDefaultCapabilityIdValueInPowerMode(allCapabilityData, CapabilityID.CPULongTermPowerLimit, powerMode), CPUShortTermPowerLimit = GetDefaultCapabilityIdValueInPowerMode(allCapabilityData, CapabilityID.CPUShortTermPowerLimit, powerMode), CPUPeakPowerLimit = GetDefaultCapabilityIdValueInPowerMode(allCapabilityData, CapabilityID.CPUPeakPowerLimit, powerMode), CPUCrossLoadingPowerLimit = GetDefaultCapabilityIdValueInPowerMode(allCapabilityData, CapabilityID.CPUCrossLoadingPowerLimit, powerMode), CPUPL1Tau = GetDefaultCapabilityIdValueInPowerMode(allCapabilityData, CapabilityID.CPUPL1Tau, powerMode), APUsPPTPowerLimit = GetDefaultCapabilityIdValueInPowerMode(allCapabilityData, CapabilityID.APUsPPTPowerLimit, powerMode), CPUTemperatureLimit = GetDefaultCapabilityIdValueInPowerMode(allCapabilityData, CapabilityID.CPUTemperatureLimit, powerMode), GPUPowerBoost = GetDefaultCapabilityIdValueInPowerMode(allCapabilityData, CapabilityID.GPUPowerBoost, powerMode), GPUConfigurableTGP = GetDefaultCapabilityIdValueInPowerMode(allCapabilityData, CapabilityID.GPUConfigurableTGP, powerMode), GPUTemperatureLimit = GetDefaultCapabilityIdValueInPowerMode(allCapabilityData, CapabilityID.GPUTemperatureLimit, powerMode), GPUTotalProcessingPowerTargetOnAcOffsetFromBaseline = GetDefaultCapabilityIdValueInPowerMode(allCapabilityData, CapabilityID.GPUTotalProcessingPowerTargetOnAcOffsetFromBaseline, powerMode), GPUToCPUDynamicBoost = GetDefaultCapabilityIdValueInPowerMode(allCapabilityData, CapabilityID.GPUToCPUDynamicBoost, powerMode), FanTable = await GetDefaultFanTableAsync().ConfigureAwait(false), FanFullSpeed = false }; result[powerMode] = defaults; } if (Log.Instance.IsTraceEnabled) { Log.Instance.Trace($"Defaults in other power modes retrieved:"); foreach (var (powerMode, defaults) in result) Log.Instance.Trace($" - {powerMode}: {defaults}"); } return result; } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to get defaults in other power modes.", ex); return []; } } public override Task RestoreDefaultsInOtherPowerModeAsync(PowerModeState _) => Task.CompletedTask; protected override async Task GetDefaultStateAsync() { var allCapabilityData = await WMI.LenovoCapabilityData01.ReadAsync().ConfigureAwait(false); allCapabilityData = allCapabilityData.ToArray(); var capabilityData = allCapabilityData .Where(d => Enum.IsDefined(d.Id)) .ToArray(); var allDiscreteData = await WMI.LenovoDiscreteData.ReadAsync().ConfigureAwait(false); allDiscreteData = allDiscreteData.ToArray(); var discreteData = allDiscreteData .Where(d => Enum.IsDefined(d.Id)) .GroupBy(d => d.Id, d => d.Value, (id, values) => (id, values)) .ToDictionary(d => d.id, d => d.values.ToArray()); var stepperValues = new Dictionary(); foreach (var c in capabilityData) { var value = await GetValueAsync(c.Id).OrNullIfException().ConfigureAwait(false) ?? c.DefaultValue; var steps = discreteData.GetValueOrDefault(c.Id) ?? []; if (c.Step == 0 && steps.Length < 1) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Skipping {c.Id}... [idRaw={(int)c.Id:X}, defaultValue={c.DefaultValue}, min={c.Min}, max={c.Max}, step={c.Step}, steps={string.Join(", ", steps)}]"); continue; } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Creating StepperValue {c.Id}... [idRaw={(int)c.Id:X}, defaultValue={c.DefaultValue}, min={c.Min}, max={c.Max}, step={c.Step}, steps={string.Join(", ", steps)}]"); var stepperValue = new StepperValue(value, c.Min, c.Max, c.Step, steps, c.DefaultValue); stepperValues[c.Id] = stepperValue; } var fanTableData = await GetFanTableDataAsync().ConfigureAwait(false); var preset = new GodModePreset { Name = "Default", CPULongTermPowerLimit = stepperValues.GetValueOrNull(CapabilityID.CPULongTermPowerLimit), CPUShortTermPowerLimit = stepperValues.GetValueOrNull(CapabilityID.CPUShortTermPowerLimit), CPUPeakPowerLimit = stepperValues.GetValueOrNull(CapabilityID.CPUPeakPowerLimit), CPUCrossLoadingPowerLimit = stepperValues.GetValueOrNull(CapabilityID.CPUCrossLoadingPowerLimit), CPUPL1Tau = stepperValues.GetValueOrNull(CapabilityID.CPUPL1Tau), APUsPPTPowerLimit = stepperValues.GetValueOrNull(CapabilityID.APUsPPTPowerLimit), CPUTemperatureLimit = stepperValues.GetValueOrNull(CapabilityID.CPUTemperatureLimit), GPUPowerBoost = stepperValues.GetValueOrNull(CapabilityID.GPUPowerBoost), GPUConfigurableTGP = stepperValues.GetValueOrNull(CapabilityID.GPUConfigurableTGP), GPUTemperatureLimit = stepperValues.GetValueOrNull(CapabilityID.GPUTemperatureLimit), GPUTotalProcessingPowerTargetOnAcOffsetFromBaseline = stepperValues.GetValueOrNull(CapabilityID.GPUTotalProcessingPowerTargetOnAcOffsetFromBaseline), GPUToCPUDynamicBoost = stepperValues.GetValueOrNull(CapabilityID.GPUToCPUDynamicBoost), FanTableInfo = fanTableData is null ? null : new FanTableInfo(fanTableData, await GetDefaultFanTableAsync().ConfigureAwait(false)), FanFullSpeed = await GetFanFullSpeedAsync().ConfigureAwait(false), MinValueOffset = 0, MaxValueOffset = 0 }; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Default state retrieved: {preset}"); return preset; } private static CapabilityID AdjustCapabilityIdForPowerMode(CapabilityID id, PowerModeState powerMode) { var idRaw = (uint)id & 0xFFFF00FF; var powerModeRaw = ((uint)powerMode + 1) << 8; return (CapabilityID)(idRaw + powerModeRaw); } private static int? GetDefaultCapabilityIdValueInPowerMode(IEnumerable capabilities, CapabilityID id, PowerModeState powerMode) { var adjustedId = AdjustCapabilityIdForPowerMode(id, powerMode); var value = capabilities .Where(c => c.Id == adjustedId) .Select(c => c.DefaultValue) .DefaultIfEmpty(-1) .First(); return value < 0 ? null : value; } #region Get/Set Value private static Task GetValueAsync(CapabilityID id) { var idRaw = (uint)id & 0xFFFF00FF; return WMI.LenovoOtherMethod.GetFeatureValueAsync(idRaw); } private static Task SetValueAsync(CapabilityID id, StepperValue value) => SetValueAsync(id, value.Value); private static Task SetValueAsync(CapabilityID id, int value) { var idRaw = (uint)id & 0xFFFF00FF; return WMI.LenovoOtherMethod.SetFeatureValueAsync(idRaw, value); } #endregion #region Fan Table private static async Task GetFanTableDataAsync(PowerModeState powerModeState = PowerModeState.GodMode) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Reading fan table data..."); var data = await WMI.LenovoFanTableData.ReadAsync().ConfigureAwait(false); var fanTableData = data .Where(d => d.mode == (int)powerModeState + 1) .Select(d => { var type = (d.fanId, d.sensorId) switch { (1, 4) => FanTableType.CPU, (1, 1) => FanTableType.CPUSensor, (2, 5) => FanTableType.GPU, (3, 5) => FanTableType.GPU2, _ => FanTableType.Unknown, }; return new FanTableData(type, d.fanId, d.sensorId, d.fanTableData, d.sensorTableData); }) .ToArray(); var length = fanTableData.Where(ftd => ftd.Type != FanTableType.Unknown).Count(); if (fanTableData.Length != length) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Bad fan table length: {string.Join(", ", fanTableData)}"); return null; } if (fanTableData.Count(ftd => ftd.FanSpeeds.Length == 10) != length) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Bad fan table fan speeds length: {string.Join(", ", fanTableData)}"); return null; } if (fanTableData.Count(ftd => ftd.Temps.Length == 10) != length) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Bad fan table temps length: {string.Join(", ", fanTableData)}"); return null; } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Fan table data: {string.Join(", ", fanTableData)}"); return fanTableData; } private static Task SetFanTable(FanTable fanTable) => WMI.LenovoFanMethod.FanSetTableAsync(fanTable.GetBytes()); #endregion #region Fan Full Speed private static async Task GetFanFullSpeedAsync() { var value = await WMI.LenovoOtherMethod.GetFeatureValueAsync(CapabilityID.FanFullSpeed).ConfigureAwait(false); return value != 0; } private static Task SetFanFullSpeedAsync(bool enabled) => WMI.LenovoOtherMethod.SetFeatureValueAsync(CapabilityID.FanFullSpeed, enabled ? 1 : 0); #endregion } ================================================ FILE: LenovoLegionToolkit.Lib/Controllers/GodMode/IGodModeController.cs ================================================ using System; using System.Collections.Generic; using System.Threading.Tasks; namespace LenovoLegionToolkit.Lib.Controllers.GodMode; public interface IGodModeController { event EventHandler PresetChanged; Task NeedsVantageDisabledAsync(); Task NeedsLegionZoneDisabledAsync(); Task GetActivePresetIdAsync(); Task GetActivePresetNameAsync(); Task GetStateAsync(); Task SetStateAsync(GodModeState state); Task ApplyStateAsync(); Task GetDefaultFanTableAsync(); Task GetMinimumFanTableAsync(); Task> GetDefaultsInOtherPowerModesAsync(); Task RestoreDefaultsInOtherPowerModeAsync(PowerModeState state); } ================================================ FILE: LenovoLegionToolkit.Lib/Controllers/RGBKeyboardBacklightController.cs ================================================ // #define MOCK_RGB using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Settings; using LenovoLegionToolkit.Lib.SoftwareDisabler; using LenovoLegionToolkit.Lib.System; using LenovoLegionToolkit.Lib.System.Management; using LenovoLegionToolkit.Lib.Utils; using Microsoft.Win32.SafeHandles; using NeoSmart.AsyncLock; using Windows.Win32; namespace LenovoLegionToolkit.Lib.Controllers { public class RGBKeyboardBacklightController(RGBKeyboardSettings settings, VantageDisabler vantageDisabler) { private static readonly AsyncLock IoLock = new(); private SafeFileHandle? _deviceHandle; private SafeFileHandle? DeviceHandle { get { if (ForceDisable) return null; _deviceHandle ??= Devices.GetRGBKeyboard(); return _deviceHandle; } } public bool ForceDisable { get; set; } public Task IsSupportedAsync() { #if MOCK_RGB return Task.FromResult(true); #else return Task.FromResult(DeviceHandle is not null); #endif } public async Task SetLightControlOwnerAsync(bool enable, bool restorePreset = false) { using (await IoLock.LockAsync().ConfigureAwait(false)) { try { #if !MOCK_RGB _ = DeviceHandle ?? throw new InvalidOperationException("RGB Keyboard unsupported"); #endif await ThrowIfVantageEnabled().ConfigureAwait(false); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Taking ownership..."); #if !MOCK_RGB await WMI.LenovoGameZoneData.SetLightControlOwnerAsync(enable ? 1 : 0).ConfigureAwait(false); #endif if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Ownership set to {enable}, restoring profile..."); if (restorePreset) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Restoring preset..."); await SetCurrentPresetAsync().ConfigureAwait(false); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Restored preset"); } } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Can't take ownership.", ex); throw; } } } public async Task GetStateAsync() { using (await IoLock.LockAsync().ConfigureAwait(false)) { #if !MOCK_RGB _ = DeviceHandle ?? throw new InvalidOperationException("RGB Keyboard unsupported"); #endif await ThrowIfVantageEnabled().ConfigureAwait(false); return settings.Store.State; } } public async Task SetStateAsync(RGBKeyboardBacklightState state) { using (await IoLock.LockAsync().ConfigureAwait(false)) { #if !MOCK_RGB _ = DeviceHandle ?? throw new InvalidOperationException("RGB Keyboard unsupported"); #endif await ThrowIfVantageEnabled().ConfigureAwait(false); settings.Store.State = state; settings.SynchronizeStore(); var selectedPreset = state.SelectedPreset; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Selected preset: {selectedPreset}"); LENOVO_RGB_KEYBOARD_STATE str; if (selectedPreset == RGBKeyboardBacklightPreset.Off) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Creating off state."); str = CreateOffState(); } else { var presetDescription = state.Presets.GetValueOrDefault(selectedPreset, RGBKeyboardBacklightBacklightPresetDescription.Default); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Creating state: {presetDescription}"); str = Convert(presetDescription); } await SendToDevice(str).ConfigureAwait(false); } } public async Task SetPresetAsync(RGBKeyboardBacklightPreset preset) { using (await IoLock.LockAsync().ConfigureAwait(false)) { #if !MOCK_RGB _ = DeviceHandle ?? throw new InvalidOperationException("RGB Keyboard unsupported"); #endif await ThrowIfVantageEnabled().ConfigureAwait(false); var state = settings.Store.State; var presets = state.Presets; settings.Store.State = new(preset, presets); settings.SynchronizeStore(); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Preset is {preset}."); LENOVO_RGB_KEYBOARD_STATE str; if (preset == RGBKeyboardBacklightPreset.Off) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Creating off state."); str = CreateOffState(); } else { var presetDescription = state.Presets.GetValueOrDefault(preset, RGBKeyboardBacklightBacklightPresetDescription.Default); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Creating state: {presetDescription}"); str = Convert(presetDescription); } await SendToDevice(str).ConfigureAwait(false); } } public async Task SetNextPresetAsync() { using (await IoLock.LockAsync().ConfigureAwait(false)) { #if !MOCK_RGB _ = DeviceHandle ?? throw new InvalidOperationException("RGB Keyboard unsupported"); #endif await ThrowIfVantageEnabled().ConfigureAwait(false); var state = settings.Store.State; var newPreset = state.SelectedPreset.Next(); var presets = state.Presets; settings.Store.State = new(newPreset, presets); settings.SynchronizeStore(); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"New preset is {newPreset}."); LENOVO_RGB_KEYBOARD_STATE str; if (newPreset == RGBKeyboardBacklightPreset.Off) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Creating off state."); str = CreateOffState(); } else { var presetDescription = state.Presets.GetValueOrDefault(newPreset, RGBKeyboardBacklightBacklightPresetDescription.Default); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Creating state: {presetDescription}"); str = Convert(presetDescription); } await SendToDevice(str).ConfigureAwait(false); return newPreset; } } private async Task SetCurrentPresetAsync() { #if !MOCK_RGB _ = DeviceHandle ?? throw new InvalidOperationException("RGB Keyboard unsupported"); #endif await ThrowIfVantageEnabled().ConfigureAwait(false); var state = settings.Store.State; var preset = state.SelectedPreset; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Current preset is {preset}."); LENOVO_RGB_KEYBOARD_STATE str; if (preset == RGBKeyboardBacklightPreset.Off) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Creating off state."); str = CreateOffState(); } else { var presetDescription = state.Presets.GetValueOrDefault(preset, RGBKeyboardBacklightBacklightPresetDescription.Default); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Creating state: {presetDescription}"); str = Convert(presetDescription); } await SendToDevice(str).ConfigureAwait(false); } private async Task ThrowIfVantageEnabled() { var vantageStatus = await vantageDisabler.GetStatusAsync().ConfigureAwait(false); if (vantageStatus == SoftwareStatus.Enabled) throw new InvalidOperationException("Can't manage RGB keyboard with Vantage enabled"); } private unsafe Task SendToDevice(LENOVO_RGB_KEYBOARD_STATE str) => Task.Run(() => { #if !MOCK_RGB var handle = DeviceHandle ?? throw new InvalidOperationException("RGB Keyboard unsupported"); var ptr = IntPtr.Zero; try { var size = Marshal.SizeOf(); ptr = Marshal.AllocHGlobal(size); Marshal.StructureToPtr(str, ptr, false); if (!PInvoke.HidD_SetFeature(handle, ptr.ToPointer(), (uint)size)) PInvokeExtensions.ThrowIfWin32Error("HidD_SetFeature"); } finally { Marshal.FreeHGlobal(ptr); } #endif }); private static LENOVO_RGB_KEYBOARD_STATE CreateOffState() { return new() { Header = [0xCC, 0x16], Unused = new byte[13], Padding = 0, Effect = 0, WaveLTR = 0, WaveRTL = 0, Brightness = 0, Zone1Rgb = new byte[3], Zone2Rgb = new byte[3], Zone3Rgb = new byte[3], Zone4Rgb = new byte[3], }; } private static LENOVO_RGB_KEYBOARD_STATE Convert(RGBKeyboardBacklightBacklightPresetDescription preset) { var result = new LENOVO_RGB_KEYBOARD_STATE { Header = [0xCC, 0x16], Unused = new byte[13], Padding = 0x0, Zone1Rgb = [0xFF, 0xFF, 0xFF], Zone2Rgb = [0xFF, 0xFF, 0xFF], Zone3Rgb = [0xFF, 0xFF, 0xFF], Zone4Rgb = [0xFF, 0xFF, 0xFF], Effect = preset.Effect switch { RGBKeyboardBacklightEffect.Static => 1, RGBKeyboardBacklightEffect.Breath => 3, RGBKeyboardBacklightEffect.WaveRTL => 4, RGBKeyboardBacklightEffect.WaveLTR => 4, RGBKeyboardBacklightEffect.Smooth => 6, _ => 0 }, WaveRTL = (byte)(preset.Effect == RGBKeyboardBacklightEffect.WaveRTL ? 1 : 0), WaveLTR = (byte)(preset.Effect == RGBKeyboardBacklightEffect.WaveLTR ? 1 : 0), Brightness = preset.Brightness switch { RGBKeyboardBacklightBrightness.Low => 1, RGBKeyboardBacklightBrightness.High => 2, _ => 0 } }; if (preset.Effect != RGBKeyboardBacklightEffect.Static) { result.Speed = preset.Speed switch { RGBKeyboardBacklightSpeed.Slowest => 1, RGBKeyboardBacklightSpeed.Slow => 2, RGBKeyboardBacklightSpeed.Fast => 3, RGBKeyboardBacklightSpeed.Fastest => 4, _ => 0 }; } if (preset.Effect is RGBKeyboardBacklightEffect.Static or RGBKeyboardBacklightEffect.Breath) { result.Zone1Rgb = [preset.Zone1.R, preset.Zone1.G, preset.Zone1.B]; result.Zone2Rgb = [preset.Zone2.R, preset.Zone2.G, preset.Zone2.B]; result.Zone3Rgb = [preset.Zone3.R, preset.Zone3.G, preset.Zone3.B]; result.Zone4Rgb = [preset.Zone4.R, preset.Zone4.G, preset.Zone4.B]; } return result; } } } ================================================ FILE: LenovoLegionToolkit.Lib/Controllers/Sensors/AbstractSensorsController.cs ================================================ using System; using System.Linq; using System.Runtime.InteropServices; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.System; using LenovoLegionToolkit.Lib.System.Management; using LenovoLegionToolkit.Lib.Utils; using NvAPIWrapper.Native; using NvAPIWrapper.Native.GPU; using Windows.Win32; using Windows.Win32.System.Power; namespace LenovoLegionToolkit.Lib.Controllers.Sensors; public abstract class AbstractSensorsController(GPUController gpuController) : ISensorsController { private readonly struct GPUInfo( int utilization, int coreClock, int maxCoreClock, int memoryClock, int maxMemoryClock, int temperature, int maxTemperature) { public static readonly GPUInfo Empty = new(-1, -1, -1, -1, -1, -1, -1); public int Utilization { get; } = utilization; public int CoreClock { get; } = coreClock; public int MaxCoreClock { get; } = maxCoreClock; public int MemoryClock { get; } = memoryClock; public int MaxMemoryClock { get; } = maxMemoryClock; public int Temperature { get; } = temperature; public int MaxTemperature { get; } = maxTemperature; } private readonly SafePerformanceCounter _percentProcessorPerformanceCounter = new("Processor Information", "% Processor Performance", "_Total"); private readonly SafePerformanceCounter _percentProcessorUtilityCounter = new("Processor Information", "% Processor Utility", "_Total"); private int? _cpuBaseClockCache; private int? _cpuMaxCoreClockCache; private int? _cpuMaxFanSpeedCache; private int? _gpuMaxFanSpeedCache; public abstract Task IsSupportedAsync(); public Task PrepareAsync() { _percentProcessorPerformanceCounter.Reset(); _percentProcessorUtilityCounter.Reset(); return Task.CompletedTask; } public async Task GetDataAsync() { const int genericMaxUtilization = 100; const int genericMaxTemperature = 100; var cpuUtilization = GetCpuUtilization(genericMaxUtilization); var cpuMaxCoreClock = _cpuMaxCoreClockCache ??= await GetCpuMaxCoreClockAsync().ConfigureAwait(false); var cpuCoreClock = GetCpuCoreClock(); var cpuCurrentTemperature = await GetCpuCurrentTemperatureAsync().ConfigureAwait(false); var cpuCurrentFanSpeed = await GetCpuCurrentFanSpeedAsync().ConfigureAwait(false); var cpuMaxFanSpeed = _cpuMaxFanSpeedCache ??= await GetCpuMaxFanSpeedAsync().ConfigureAwait(false); var gpuInfo = await GetGPUInfoAsync().ConfigureAwait(false); var gpuCurrentTemperature = gpuInfo.Temperature >= 0 ? gpuInfo.Temperature : await GetGpuCurrentTemperatureAsync().ConfigureAwait(false); var gpuMaxTemperature = gpuInfo.MaxTemperature >= 0 ? gpuInfo.MaxTemperature : genericMaxTemperature; var gpuCurrentFanSpeed = await GetGpuCurrentFanSpeedAsync().ConfigureAwait(false); var gpuMaxFanSpeed = _gpuMaxFanSpeedCache ??= await GetGpuMaxFanSpeedAsync().ConfigureAwait(false); var cpu = new SensorData(cpuUtilization, genericMaxUtilization, cpuCoreClock, cpuMaxCoreClock, -1, -1, cpuCurrentTemperature, genericMaxTemperature, cpuCurrentFanSpeed, cpuMaxFanSpeed); var gpu = new SensorData(gpuInfo.Utilization, genericMaxUtilization, gpuInfo.CoreClock, gpuInfo.MaxCoreClock, gpuInfo.MemoryClock, gpuInfo.MaxMemoryClock, gpuCurrentTemperature, gpuMaxTemperature, gpuCurrentFanSpeed, gpuMaxFanSpeed); var result = new SensorsData(cpu, gpu); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Current data: {result} [type={GetType().Name}]"); return result; } public async Task<(int cpuFanSpeed, int gpuFanSpeed)> GetFanSpeedsAsync() { var cpuFanSpeed = await GetCpuCurrentFanSpeedAsync().ConfigureAwait(false); var gpuFanSpeed = await GetGpuCurrentFanSpeedAsync().ConfigureAwait(false); return (cpuFanSpeed, gpuFanSpeed); } protected abstract Task GetCpuCurrentTemperatureAsync(); protected abstract Task GetGpuCurrentTemperatureAsync(); protected abstract Task GetCpuCurrentFanSpeedAsync(); protected abstract Task GetGpuCurrentFanSpeedAsync(); protected abstract Task GetCpuMaxFanSpeedAsync(); protected abstract Task GetGpuMaxFanSpeedAsync(); private int GetCpuUtilization(int maxUtilization) { var result = (int)_percentProcessorUtilityCounter.NextValue(); if (result < 0) return -1; return Math.Min(result, maxUtilization); } private int GetCpuCoreClock() { var baseClock = _cpuBaseClockCache ??= GetCpuBaseClock(); var clock = (int)(baseClock * (_percentProcessorPerformanceCounter.NextValue() / 100f)); if (clock < 1) return -1; return clock; } private static unsafe int GetCpuBaseClock() { var ptr = IntPtr.Zero; try { PInvoke.GetSystemInfo(out var systemInfo); var numberOfProcessors = Math.Min(32, (int)systemInfo.dwNumberOfProcessors); var infoSize = Marshal.SizeOf(); var infosSize = numberOfProcessors * infoSize; ptr = Marshal.AllocHGlobal(infosSize); var result = PInvoke.CallNtPowerInformation(POWER_INFORMATION_LEVEL.ProcessorInformation, null, 0, ptr.ToPointer(), (uint)infosSize); if (result != 0) return 0; var infos = new PROCESSOR_POWER_INFORMATION[numberOfProcessors]; for (var i = 0; i < infos.Length; i++) infos[i] = Marshal.PtrToStructure(IntPtr.Add(ptr, i * infoSize)); return (int)infos.Select(p => p.MaxMhz).Max(); } finally { Marshal.FreeHGlobal(ptr); } } private static Task GetCpuMaxCoreClockAsync() => WMI.LenovoGameZoneData.GetCPUFrequencyAsync(); private async Task GetGPUInfoAsync() { if (gpuController.IsSupported()) await gpuController.StartAsync().ConfigureAwait(false); if (await gpuController.GetLastKnownStateAsync().ConfigureAwait(false) is GPUState.PoweredOff or GPUState.Unknown) return GPUInfo.Empty; try { NVAPI.Initialize(); var gpu = NVAPI.GetGPU(); if (gpu is null) return GPUInfo.Empty; var utilization = Math.Min(100, Math.Max(gpu.UsageInformation.GPU.Percentage, gpu.UsageInformation.VideoEngine.Percentage)); var currentCoreClock = (int)gpu.CurrentClockFrequencies.GraphicsClock.Frequency / 1000; var currentMemoryClock = (int)gpu.CurrentClockFrequencies.MemoryClock.Frequency / 1000; var maxCoreClock = (int)gpu.BoostClockFrequencies.GraphicsClock.Frequency / 1000; var maxMemoryClock = (int)gpu.BoostClockFrequencies.MemoryClock.Frequency / 1000; var states = GPUApi.GetPerformanceStates20(gpu.Handle); var maxCoreClockOffset = states.Clocks[PerformanceStateId.P0_3DPerformance][0].FrequencyDeltaInkHz.DeltaValue / 1000; var maxMemoryClockOffset = states.Clocks[PerformanceStateId.P0_3DPerformance][1].FrequencyDeltaInkHz.DeltaValue / 1000; var temperatureSensor = gpu.ThermalInformation.ThermalSensors.FirstOrDefault(); var currentTemperature = temperatureSensor?.CurrentTemperature ?? -1; var maxTemperature = temperatureSensor?.DefaultMaximumTemperature ?? -1; return new(utilization, currentCoreClock, maxCoreClock + maxCoreClockOffset, currentMemoryClock, maxMemoryClock + maxMemoryClockOffset, currentTemperature, maxTemperature); } catch { return GPUInfo.Empty; } finally { try { NVAPI.Unload(); } catch { /* Ignored */ } } } } ================================================ FILE: LenovoLegionToolkit.Lib/Controllers/Sensors/ISensorsController.cs ================================================ using System.Threading.Tasks; namespace LenovoLegionToolkit.Lib.Controllers.Sensors; public interface ISensorsController { Task IsSupportedAsync(); Task PrepareAsync(); Task GetDataAsync(); Task<(int cpuFanSpeed, int gpuFanSpeed)> GetFanSpeedsAsync(); } ================================================ FILE: LenovoLegionToolkit.Lib/Controllers/Sensors/SensorsController.cs ================================================ using System; using System.Threading.Tasks; namespace LenovoLegionToolkit.Lib.Controllers.Sensors; public class SensorsController( SensorsControllerV1 controllerV1, SensorsControllerV2 controllerV2, SensorsControllerV3 controllerV3) : ISensorsController { private ISensorsController? _controller; public async Task IsSupportedAsync() => await GetControllerAsync().ConfigureAwait(false) is not null; public async Task PrepareAsync() { var controller = await GetControllerAsync().ConfigureAwait(false) ?? throw new InvalidOperationException("No supported controller found"); await controller.PrepareAsync().ConfigureAwait(false); } public async Task GetDataAsync() { var controller = await GetControllerAsync().ConfigureAwait(false) ?? throw new InvalidOperationException("No supported controller found"); return await controller.GetDataAsync().ConfigureAwait(false); } public async Task<(int cpuFanSpeed, int gpuFanSpeed)> GetFanSpeedsAsync() { var controller = await GetControllerAsync().ConfigureAwait(false) ?? throw new InvalidOperationException("No supported controller found"); return await controller.GetFanSpeedsAsync().ConfigureAwait(false); } private async Task GetControllerAsync() { if (_controller is not null) return _controller; if (await controllerV3.IsSupportedAsync().ConfigureAwait(false)) return _controller = controllerV3; if (await controllerV2.IsSupportedAsync().ConfigureAwait(false)) return _controller = controllerV2; if (await controllerV1.IsSupportedAsync().ConfigureAwait(false)) return _controller = controllerV1; return null; } } ================================================ FILE: LenovoLegionToolkit.Lib/Controllers/Sensors/SensorsControllerV1.cs ================================================ using System.Threading.Tasks; using LenovoLegionToolkit.Lib.System.Management; namespace LenovoLegionToolkit.Lib.Controllers.Sensors; public class SensorsControllerV1(GPUController gpuController) : AbstractSensorsController(gpuController) { private const int CPU_SENSOR_ID = 3; private const int GPU_SENSOR_ID = 4; private const int CPU_FAN_ID = 0; private const int GPU_FAN_ID = 1; public override async Task IsSupportedAsync() { try { var result = await WMI.LenovoFanTableData.ExistsAsync(0, CPU_FAN_ID).ConfigureAwait(false); result &= await WMI.LenovoFanTableData.ExistsAsync(0, GPU_FAN_ID).ConfigureAwait(false); if (result) _ = await GetDataAsync().ConfigureAwait(false); return result; } catch { return false; } } protected override async Task GetCpuCurrentTemperatureAsync() { var t = await WMI.LenovoFanMethod.FanGetCurrentSensorTemperatureAsync(CPU_SENSOR_ID).ConfigureAwait(false); if (t < 1) return -1; return t; } protected override async Task GetGpuCurrentTemperatureAsync() { var t = await WMI.LenovoFanMethod.FanGetCurrentSensorTemperatureAsync(GPU_SENSOR_ID).ConfigureAwait(false); if (t < 1) return -1; return t; } protected override Task GetCpuCurrentFanSpeedAsync() => WMI.LenovoFanMethod.FanGetCurrentFanSpeedAsync(CPU_FAN_ID); protected override Task GetGpuCurrentFanSpeedAsync() => WMI.LenovoFanMethod.FanGetCurrentFanSpeedAsync(GPU_FAN_ID); protected override Task GetCpuMaxFanSpeedAsync() => WMI.LenovoFanMethod.GetDefaultFanMaxSpeedAsync(0, CPU_FAN_ID); protected override Task GetGpuMaxFanSpeedAsync() => WMI.LenovoFanMethod.GetDefaultFanMaxSpeedAsync(0, GPU_FAN_ID); } ================================================ FILE: LenovoLegionToolkit.Lib/Controllers/Sensors/SensorsControllerV2.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.System.Management; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.Controllers.Sensors; public class SensorsControllerV2(GPUController gpuController) : AbstractSensorsController(gpuController) { private const int CPU_SENSOR_ID = 3; private const int GPU_SENSOR_ID = 4; private const int CPU_FAN_ID = 0; private const int GPU_FAN_ID = 1; public override async Task IsSupportedAsync() { try { var result = await WMI.LenovoFanTableData.ExistsAsync(CPU_SENSOR_ID, CPU_FAN_ID).ConfigureAwait(false); result &= await WMI.LenovoFanTableData.ExistsAsync(GPU_SENSOR_ID, GPU_FAN_ID).ConfigureAwait(false); if (result) _ = await GetDataAsync().ConfigureAwait(false); return result; } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Error checking support. [type={GetType().Name}]", ex); return false; } } protected override async Task GetCpuCurrentTemperatureAsync() { var t = await WMI.LenovoFanMethod.FanGetCurrentSensorTemperatureAsync(CPU_SENSOR_ID).ConfigureAwait(false); if (t < 1) return -1; return t; } protected override async Task GetGpuCurrentTemperatureAsync() { var t = await WMI.LenovoFanMethod.FanGetCurrentSensorTemperatureAsync(GPU_SENSOR_ID).ConfigureAwait(false); if (t < 1) return -1; return t; } protected override Task GetCpuCurrentFanSpeedAsync() => WMI.LenovoFanMethod.FanGetCurrentFanSpeedAsync(CPU_FAN_ID); protected override Task GetGpuCurrentFanSpeedAsync() => WMI.LenovoFanMethod.FanGetCurrentFanSpeedAsync(GPU_FAN_ID); protected override Task GetCpuMaxFanSpeedAsync() => WMI.LenovoFanMethod.GetCurrentFanMaxSpeedAsync(CPU_SENSOR_ID, CPU_FAN_ID); protected override Task GetGpuMaxFanSpeedAsync() => WMI.LenovoFanMethod.GetCurrentFanMaxSpeedAsync(GPU_SENSOR_ID, GPU_FAN_ID); } ================================================ FILE: LenovoLegionToolkit.Lib/Controllers/Sensors/SensorsControllerV3.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.System.Management; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.Controllers.Sensors; public class SensorsControllerV3(GPUController gpuController) : AbstractSensorsController(gpuController) { private const int CPU_SENSOR_ID = 4; private const int GPU_SENSOR_ID = 5; private const int CPU_FAN_ID = 1; private const int GPU_FAN_ID = 2; public override async Task IsSupportedAsync() { try { var result = await WMI.LenovoFanTableData.ExistsAsync(CPU_SENSOR_ID, CPU_FAN_ID).ConfigureAwait(false); result &= await WMI.LenovoFanTableData.ExistsAsync(GPU_SENSOR_ID, GPU_FAN_ID).ConfigureAwait(false); if (result) _ = await GetDataAsync().ConfigureAwait(false); return result; } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Error checking support. [type={GetType().Name}]", ex); return false; } } protected override async Task GetCpuCurrentTemperatureAsync() { var value = await WMI.LenovoOtherMethod.GetFeatureValueAsync(CapabilityID.CpuCurrentTemperature).ConfigureAwait(false); return value < 1 ? -1 : value; } protected override async Task GetGpuCurrentTemperatureAsync() { var value = await WMI.LenovoOtherMethod.GetFeatureValueAsync(CapabilityID.GpuCurrentTemperature).ConfigureAwait(false); return value < 1 ? -1 : value; } protected override Task GetCpuCurrentFanSpeedAsync() => WMI.LenovoOtherMethod.GetFeatureValueAsync(CapabilityID.CpuCurrentFanSpeed); protected override Task GetGpuCurrentFanSpeedAsync() => WMI.LenovoOtherMethod.GetFeatureValueAsync(CapabilityID.GpuCurrentFanSpeed); protected override Task GetCpuMaxFanSpeedAsync() => WMI.LenovoFanMethod.GetCurrentFanMaxSpeedAsync(CPU_SENSOR_ID, CPU_FAN_ID); protected override Task GetGpuMaxFanSpeedAsync() => WMI.LenovoFanMethod.GetCurrentFanMaxSpeedAsync(GPU_SENSOR_ID, GPU_FAN_ID); } ================================================ FILE: LenovoLegionToolkit.Lib/Controllers/SmartFnLockController.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Features; using LenovoLegionToolkit.Lib.Settings; using LenovoLegionToolkit.Lib.Utils; using NeoSmart.AsyncLock; using Windows.Win32; using Windows.Win32.UI.Input.KeyboardAndMouse; using Windows.Win32.UI.WindowsAndMessaging; namespace LenovoLegionToolkit.Lib.Controllers; public class SmartFnLockController(FnLockFeature feature, ApplicationSettings settings) { private readonly AsyncLock _lock = new(); private bool _ctrlDepressed; private bool _shiftDepressed; private bool _altDepressed; private bool _restoreFnLock; public void OnKeyboardEvent(nuint wParam, KBDLLHOOKSTRUCT kbStruct) { if (settings.Store.SmartFnLockFlags == 0) return; Task.Run(async () => { try { using (await _lock.LockAsync().ConfigureAwait(false)) await OnKeyboardEventAsync(wParam, kbStruct).ConfigureAwait(false); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to handle keyboard event.", ex); } }); } private async Task OnKeyboardEventAsync(nuint wParam, KBDLLHOOKSTRUCT kbStruct) { if (IsModifierKeyPressed(wParam, kbStruct)) { if (_restoreFnLock) return; var state = await feature.GetStateAsync().ConfigureAwait(false); if (state == FnLockState.Off) return; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Disabling Fn Lock temporarily..."); await feature.SetStateAsync(FnLockState.Off).ConfigureAwait(false); _restoreFnLock = true; } else if (_restoreFnLock) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Re-enabling Fn Lock..."); await feature.SetStateAsync(FnLockState.On).ConfigureAwait(false); _restoreFnLock = false; } } private bool IsModifierKeyPressed(nuint wParam, KBDLLHOOKSTRUCT kbStruct) { var isKeyDown = wParam is PInvoke.WM_KEYDOWN or PInvoke.WM_SYSKEYDOWN; var vkKeyCode = (VIRTUAL_KEY)kbStruct.vkCode; if (vkKeyCode is VIRTUAL_KEY.VK_LCONTROL or VIRTUAL_KEY.VK_RCONTROL) _ctrlDepressed = isKeyDown; if (vkKeyCode is VIRTUAL_KEY.VK_LSHIFT or VIRTUAL_KEY.VK_RSHIFT) _shiftDepressed = isKeyDown; if (vkKeyCode is VIRTUAL_KEY.VK_LMENU or VIRTUAL_KEY.VK_RMENU) _altDepressed = isKeyDown; if (!_ctrlDepressed && !_shiftDepressed && !_altDepressed) return false; var result = false; var flags = settings.Store.SmartFnLockFlags; if (flags.HasFlag(ModifierKey.Ctrl)) result |= _ctrlDepressed; if (flags.HasFlag(ModifierKey.Shift)) result |= _shiftDepressed; if (flags.HasFlag(ModifierKey.Alt)) result |= _altDepressed; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Modifier key is depressed: {result} [ctrl={_ctrlDepressed}, shift={_shiftDepressed}, alt={_altDepressed}, flags={flags}]"); return result; } } ================================================ FILE: LenovoLegionToolkit.Lib/Controllers/SpectrumKeyboardBacklightController.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Listeners; using LenovoLegionToolkit.Lib.SoftwareDisabler; using LenovoLegionToolkit.Lib.System; using LenovoLegionToolkit.Lib.Utils; using Microsoft.Win32.SafeHandles; using NeoSmart.AsyncLock; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Windows.Win32; namespace LenovoLegionToolkit.Lib.Controllers; public class SpectrumKeyboardBacklightController { public interface IScreenCapture { void CaptureScreen(ref RGBColor[,] buffer, int width, int height, CancellationToken token); } private readonly struct KeyMap(int width, int height, ushort[,] keyCodes, ushort[] additionalKeyCodes) { public static readonly KeyMap Empty = new(0, 0, new ushort[0, 0], []); public readonly int Width = width; public readonly int Height = height; public readonly ushort[,] KeyCodes = keyCodes; public readonly ushort[] AdditionalKeyCodes = additionalKeyCodes; } private static readonly AsyncLock GetDeviceHandleLock = new(); private static readonly object IoLock = new(); private readonly TimeSpan _auroraRefreshInterval = TimeSpan.FromMilliseconds(60); private readonly SpecialKeyListener _listener; private readonly VantageDisabler _vantageDisabler; private readonly IScreenCapture _screenCapture; private SafeFileHandle? _deviceHandle; private CancellationTokenSource? _auroraRefreshCancellationTokenSource; private Task? _auroraRefreshTask; private readonly JsonSerializerSettings _jsonSerializerSettings = new() { Formatting = Formatting.Indented, TypeNameHandling = TypeNameHandling.Auto, ObjectCreationHandling = ObjectCreationHandling.Replace, Converters = [new StringEnumConverter()] }; public bool ForceDisable { get; set; } public SpectrumKeyboardBacklightController(SpecialKeyListener listener, VantageDisabler vantageDisabler, IScreenCapture screenCapture) { _listener = listener; _vantageDisabler = vantageDisabler; _screenCapture = screenCapture; _listener.Changed += Listener_Changed; } private async void Listener_Changed(object? sender, SpecialKeyListener.ChangedEventArgs e) { if (!await IsSupportedAsync().ConfigureAwait(false)) return; if (await _vantageDisabler.GetStatusAsync().ConfigureAwait(false) == SoftwareStatus.Enabled) return; switch (e.SpecialKey) { case SpecialKey.SpectrumPreset1 or SpecialKey.SpectrumPreset2 or SpecialKey.SpectrumPreset3 or SpecialKey.SpectrumPreset4 or SpecialKey.SpectrumPreset5 or SpecialKey.SpectrumPreset6: { await StartAuroraIfNeededAsync().ConfigureAwait(false); break; } } } public async Task IsSupportedAsync() => await GetDeviceHandleAsync().ConfigureAwait(false) is not null; public async Task<(SpectrumLayout, KeyboardLayout, HashSet)> GetKeyboardLayoutAsync() { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Checking keyboard layout..."); var (width, height, keys) = await ReadAllKeyCodesAsync().ConfigureAwait(false); var mi = await Compatibility.GetMachineInformationAsync().ConfigureAwait(false); var spectrumLayout = (width, height) switch { (22, 9) when mi.Properties.HasAlternativeFullSpectrumLayout => SpectrumLayout.FullAlternative, (22, 9) => SpectrumLayout.Full, (20, 8) => SpectrumLayout.KeyboardAndFront, _ => SpectrumLayout.KeyboardOnly // (20, 7) }; KeyboardLayout keyboardLayout; if (keys.Contains(0xA9)) keyboardLayout = KeyboardLayout.Jis; else if (keys.Contains(0xA8)) keyboardLayout = KeyboardLayout.Iso; else keyboardLayout = KeyboardLayout.Ansi; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Layout is {spectrumLayout}, {keyboardLayout}."); return (spectrumLayout, keyboardLayout, keys); } public async Task GetBrightnessAsync() { await ThrowIfVantageEnabled().ConfigureAwait(false); var handle = await GetDeviceHandleAsync().ConfigureAwait(false); if (handle is null) throw new InvalidOperationException(nameof(handle)); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Getting keyboard brightness..."); var input = new LENOVO_SPECTRUM_GET_BRIGHTNESS_REQUEST(); SetAndGetFeature(handle, input, out LENOVO_SPECTRUM_GET_BRIGHTNESS_RESPONSE output); var result = output.Brightness; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Keyboard brightness is {result}."); return result; } public async Task SetBrightnessAsync(int brightness) { await ThrowIfVantageEnabled().ConfigureAwait(false); var handle = await GetDeviceHandleAsync().ConfigureAwait(false); if (handle is null) throw new InvalidOperationException(nameof(handle)); if (brightness is < 0 or > 9) throw new InvalidOperationException("Brightness must be between 0 and 9"); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Setting keyboard brightness to: {brightness}."); var input = new LENOVO_SPECTRUM_SET_BRIGHTNESS_REQUEST((byte)brightness); SetFeature(handle, input); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Keyboard brightness set."); } public async Task GetLogoStatusAsync() { await ThrowIfVantageEnabled().ConfigureAwait(false); var handle = await GetDeviceHandleAsync().ConfigureAwait(false); if (handle is null) throw new InvalidOperationException(nameof(handle)); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Getting logo status..."); var input = new LENOVO_SPECTRUM_GET_LOGO_STATUS(); SetAndGetFeature(handle, input, out LENOVO_SPECTRUM_GET_LOGO_STATUS_RESPONSE output); var result = output.IsOn; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Logo status is {result}."); return result; } public async Task SetLogoStatusAsync(bool isOn) { await ThrowIfVantageEnabled().ConfigureAwait(false); var handle = await GetDeviceHandleAsync().ConfigureAwait(false); if (handle is null) throw new InvalidOperationException(nameof(handle)); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Setting logo status to: {isOn}."); var input = new LENOVO_SPECTRUM_SET_LOGO_STATUS_REQUEST(isOn); SetFeature(handle, input); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Logo status set."); } public async Task GetProfileAsync() { await ThrowIfVantageEnabled().ConfigureAwait(false); var handle = await GetDeviceHandleAsync().ConfigureAwait(false); if (handle is null) throw new InvalidOperationException(nameof(handle)); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Getting keyboard profile..."); var input = new LENOVO_SPECTRUM_GET_PROFILE_REQUEST(); SetAndGetFeature(handle, input, out LENOVO_SPECTRUM_GET_PROFILE_RESPONSE output); var result = output.Profile; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Keyboard profile is {result}."); return result; } public async Task SetProfileAsync(int profile) { await ThrowIfVantageEnabled().ConfigureAwait(false); var handle = await GetDeviceHandleAsync().ConfigureAwait(false); if (handle is null) throw new InvalidOperationException(nameof(handle)); await StopAuroraIfNeededAsync().ConfigureAwait(false); if (profile is < 0 or > 6) throw new InvalidOperationException("Profile must be between 0 and 6"); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Setting keyboard profile to {profile}..."); var input = new LENOVO_SPECTRUM_SET_PROFILE_REQUEST((byte)profile); SetFeature(handle, input); await Task.Delay(TimeSpan.FromMilliseconds(100)).ConfigureAwait(false); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Keyboard profile set to {profile}."); await StartAuroraIfNeededAsync(profile).ConfigureAwait(false); } public async Task SetProfileDefaultAsync(int profile) { await ThrowIfVantageEnabled().ConfigureAwait(false); var handle = await GetDeviceHandleAsync().ConfigureAwait(false); if (handle is null) throw new InvalidOperationException(nameof(handle)); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Setting keyboard profile {profile} to default..."); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Keyboard profile {profile} set to default."); var input = new LENOVO_SPECTRUM_SET_PROFILE_DEFAULT_REQUEST((byte)profile); SetFeature(handle, input); } public async Task SetProfileDescriptionAsync(int profile, SpectrumKeyboardBacklightEffect[] effects) { await ThrowIfVantageEnabled().ConfigureAwait(false); var handle = await GetDeviceHandleAsync().ConfigureAwait(false); if (handle is null) throw new InvalidOperationException(nameof(handle)); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Setting {effects.Length} effect to keyboard profile {profile}..."); effects = Compress(effects); var bytes = Convert(profile, effects).ToBytes(); SetFeature(handle, bytes); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Set {effects.Length} effect to keyboard profile {profile}."); await StartAuroraIfNeededAsync(profile).ConfigureAwait(false); } public async Task<(int Profile, SpectrumKeyboardBacklightEffect[] Effects)> GetProfileDescriptionAsync(int profile) { await ThrowIfVantageEnabled().ConfigureAwait(false); var handle = await GetDeviceHandleAsync().ConfigureAwait(false); if (handle is null) throw new InvalidOperationException(nameof(handle)); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Getting effects for keyboard profile {profile}..."); var input = new LENOVO_SPECTRUM_GET_EFFECT_REQUEST((byte)profile); SetAndGetFeature(handle, input, out var buffer, 960); var description = LENOVO_SPECTRUM_EFFECT_DESCRIPTION.FromBytes(buffer); var result = Convert(description); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Retrieved {result.Effects.Length} effects for keyboard profile {profile}..."); return result; } public async Task ImportProfileDescription(int profile, string jsonPath) { var json = await File.ReadAllTextAsync(jsonPath).ConfigureAwait(false); var effects = JsonConvert.DeserializeObject(json) ?? throw new InvalidOperationException("Couldn't deserialize effects"); await SetProfileDescriptionAsync(profile, effects).ConfigureAwait(false); } public async Task ExportProfileDescriptionAsync(int profile, string jsonPath) { var (_, effects) = await GetProfileDescriptionAsync(profile).ConfigureAwait(false); var json = JsonConvert.SerializeObject(effects, _jsonSerializerSettings); await File.WriteAllTextAsync(jsonPath, json).ConfigureAwait(false); } public async Task StartAuroraIfNeededAsync(int? profile = null) { await ThrowIfVantageEnabled().ConfigureAwait(false); await StopAuroraIfNeededAsync().ConfigureAwait(false); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Starting Aurora... [profile={profile}]"); profile ??= await GetProfileAsync().ConfigureAwait(false); var (_, effects) = await GetProfileDescriptionAsync(profile.Value).ConfigureAwait(false); if (!effects.Any(e => e.Type == SpectrumKeyboardBacklightEffectType.AuroraSync)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Aurora not needed. [profile={profile}]"); return false; } _auroraRefreshCancellationTokenSource = new(); var token = _auroraRefreshCancellationTokenSource.Token; _auroraRefreshTask = Task.Run(() => AuroraRefreshAsync(profile.Value, token), token); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Aurora started. [profile={profile}]"); return true; } public async Task StopAuroraIfNeededAsync() { await ThrowIfVantageEnabled().ConfigureAwait(false); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Stopping Aurora..."); if (_auroraRefreshCancellationTokenSource is not null) await _auroraRefreshCancellationTokenSource.CancelAsync().ConfigureAwait(false); if (_auroraRefreshTask is not null) await _auroraRefreshTask.ConfigureAwait(false); _auroraRefreshTask = null; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Aurora stopped."); } public async Task> GetStateAsync(bool skipVantageCheck = false) { if (!skipVantageCheck) await ThrowIfVantageEnabled().ConfigureAwait(false); var handle = await GetDeviceHandleAsync().ConfigureAwait(false); if (handle is null) throw new InvalidOperationException(nameof(handle)); GetFeature(handle, out LENOVO_SPECTRUM_STATE_RESPONSE state); var dict = new Dictionary(); foreach (var key in state.Data.Where(k => k.KeyCode > 0)) { var rgb = new RGBColor(key.Color.R, key.Color.G, key.Color.B); dict.TryAdd(key.KeyCode, rgb); } return dict; } private async Task ThrowIfVantageEnabled() { var vantageStatus = await _vantageDisabler.GetStatusAsync().ConfigureAwait(false); if (vantageStatus == SoftwareStatus.Enabled) throw new InvalidOperationException("Can't manage Spectrum keyboard with Vantage enabled"); } private async Task<(int Width, int Height, HashSet Keys)> ReadAllKeyCodesAsync() { var keyMap = await GetKeyMapAsync().ConfigureAwait(false); var keyCodes = new HashSet(keyMap.Width * keyMap.Height); foreach (var keyCode in keyMap.KeyCodes) if (keyCode > 0) keyCodes.Add(keyCode); foreach (var keyCode in keyMap.AdditionalKeyCodes) if (keyCode > 0) keyCodes.Add(keyCode); return (keyMap.Width, keyMap.Height, keyCodes); } private async Task GetKeyMapAsync() { try { var handle = await GetDeviceHandleAsync().ConfigureAwait(false); if (handle is null) return KeyMap.Empty; SetAndGetFeature(handle, new LENOVO_SPECTRUM_GET_KEY_COUNT_REQUEST(), out LENOVO_SPECTRUM_GET_KEY_COUNT_RESPONSE keyCountResponse); var width = keyCountResponse.KeysPerIndex; var height = keyCountResponse.Indexes; var keyCodes = new ushort[width, height]; var additionalKeyCodes = new ushort[width]; for (var y = 0; y < height; y++) { SetAndGetFeature(handle, new LENOVO_SPECTRUM_GET_KEY_PAGE_REQUEST((byte)y), out LENOVO_SPECTRUM_GET_KEY_PAGE_RESPONSE keyPageResponse); for (var x = 0; x < width; x++) keyCodes[x, y] = keyPageResponse.Items[x].KeyCode; } SetAndGetFeature(handle, new LENOVO_SPECTRUM_GET_KEY_PAGE_REQUEST(0, true), out LENOVO_SPECTRUM_GET_KEY_PAGE_RESPONSE secondaryKeyPageResponse); for (var x = 0; x < width; x++) additionalKeyCodes[x] = secondaryKeyPageResponse.Items[x].KeyCode; return new(width, height, keyCodes, additionalKeyCodes); } catch { return KeyMap.Empty; } } private async Task AuroraRefreshAsync(int profile, CancellationToken token) { try { await ThrowIfVantageEnabled().ConfigureAwait(false); var handle = await GetDeviceHandleAsync().ConfigureAwait(false); if (handle is null) throw new InvalidOperationException(nameof(handle)); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Aurora refresh starting..."); var keyMap = await GetKeyMapAsync().ConfigureAwait(false); var width = keyMap.Width; var height = keyMap.Height; var colorBuffer = new RGBColor[width, height]; SetFeature(handle, new LENOVO_SPECTRUM_AURORA_START_STOP_REQUEST(true, (byte)profile)); while (!token.IsCancellationRequested) { var delay = Task.Delay(_auroraRefreshInterval, token); try { _screenCapture.CaptureScreen(ref colorBuffer, width, height, token); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Screen capture failed. Delaying before next refresh...", ex); await Task.Delay(TimeSpan.FromSeconds(1), token).ConfigureAwait(false); } token.ThrowIfCancellationRequested(); var items = new List(width * height); var avgR = 0; var avgG = 0; var avgB = 0; for (var x = 0; x < width; x++) { for (var y = 0; y < height; y++) { var keyCode = keyMap.KeyCodes[x, y]; if (keyCode < 1) continue; var color = colorBuffer[x, y]; avgR += color.R; avgG += color.G; avgB += color.B; items.Add(new(keyCode, new(color.R, color.G, color.B))); } } avgR /= items.Count; avgG /= items.Count; avgB /= items.Count; for (var x = 0; x < width; x++) { var keyCode = keyMap.AdditionalKeyCodes[x]; if (keyCode < 1) continue; items.Add(new(keyCode, new((byte)avgR, (byte)avgB, (byte)avgG))); } token.ThrowIfCancellationRequested(); SetFeature(handle, new LENOVO_SPECTRUM_AURORA_SEND_BITMAP_REQUEST([.. items]).ToBytes()); await delay.ConfigureAwait(false); } } catch (OperationCanceledException) { } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Unexpected exception while refreshing Aurora.", ex); } finally { var handle = await GetDeviceHandleAsync().ConfigureAwait(false); if (handle is not null) { var currentProfile = await GetProfileAsync().ConfigureAwait(false); SetFeature(handle, new LENOVO_SPECTRUM_AURORA_START_STOP_REQUEST(false, (byte)currentProfile)); } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Aurora refresh stopped."); } } private async Task GetDeviceHandleAsync() { if (ForceDisable) return null; try { using (await GetDeviceHandleLock.LockAsync().ConfigureAwait(false)) { if (_deviceHandle is not null && IsReady(_deviceHandle)) return _deviceHandle; SafeFileHandle? newDeviceHandle = null; const int retries = 3; const int delay = 50; for (var i = 0; i < retries; i++) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Refreshing handle... [retry={i + 1}]"); var tempDeviceHandle = Devices.GetSpectrumRGBKeyboard(true); if (tempDeviceHandle is not null && IsReady(tempDeviceHandle)) { newDeviceHandle = tempDeviceHandle; break; } await Task.Delay(delay).ConfigureAwait(false); } if (newDeviceHandle is null) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Handle couldn't be refreshed."); return null; } SetAndGetFeature(newDeviceHandle, new LENOVO_SPECTRUM_GET_COMPATIBILITY_REQUEST(), out LENOVO_SPECTRUM_GET_COMPATIBILITY_RESPONSE res); if (!res.IsCompatible) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Handle not compatible."); return null; } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Handle refreshed."); _deviceHandle = newDeviceHandle; return newDeviceHandle; } } catch { return null; } } private static bool IsReady(SafeHandle handle) { try { var b = new byte[960]; b[0] = 7; SetFeature(handle, b); return true; } catch { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Keyboard not ready."); return false; } } private static void SetAndGetFeature(SafeHandle handle, TIn input, out TOut output) where TIn : notnull where TOut : struct { lock (IoLock) { SetFeature(handle, input); GetFeature(handle, out output); } } private static void SetAndGetFeature(SafeHandle handle, TIn input, out byte[] output, int size) where TIn : notnull { lock (IoLock) { SetFeature(handle, input); GetFeature(handle, out output, size); } } private static unsafe void SetFeature(SafeHandle handle, T str) where T : notnull { lock (IoLock) { var ptr = IntPtr.Zero; try { int size; if (str is byte[] bytes) { size = bytes.Length; ptr = Marshal.AllocHGlobal(size); Marshal.Copy(bytes, 0, ptr, size); } else { size = Marshal.SizeOf(); ptr = Marshal.AllocHGlobal(size); Marshal.StructureToPtr(str, ptr, false); } var result = PInvoke.HidD_SetFeature(handle, ptr.ToPointer(), (uint)size); if (!result) PInvokeExtensions.ThrowIfWin32Error(typeof(T).Name); } finally { Marshal.FreeHGlobal(ptr); } } } private static unsafe void GetFeature(SafeHandle handle, out T str) where T : struct { lock (IoLock) { var ptr = IntPtr.Zero; try { var size = Marshal.SizeOf(); ptr = Marshal.AllocHGlobal(size); Marshal.Copy(new byte[] { 7 }, 0, ptr, 1); var result = PInvoke.HidD_GetFeature(handle, ptr.ToPointer(), (uint)size); if (!result) PInvokeExtensions.ThrowIfWin32Error(typeof(T).Name); str = Marshal.PtrToStructure(ptr); } finally { Marshal.FreeHGlobal(ptr); } } } private static unsafe void GetFeature(SafeHandle handle, out byte[] bytes, int size) { lock (IoLock) { var ptr = IntPtr.Zero; try { ptr = Marshal.AllocHGlobal(size); Marshal.Copy(new byte[] { 7 }, 0, ptr, 1); var result = PInvoke.HidD_GetFeature(handle, ptr.ToPointer(), (uint)size); if (!result) PInvokeExtensions.ThrowIfWin32Error("bytes"); bytes = new byte[size]; Marshal.Copy(ptr, bytes, 0, size); } finally { Marshal.FreeHGlobal(ptr); } } } private static SpectrumKeyboardBacklightEffect[] Compress(SpectrumKeyboardBacklightEffect[] effects) { if (effects.Any(e => e.Type.IsAllLightsEffect())) return [effects.Last(e => e.Type.IsAllLightsEffect())]; var usedKeyCodes = new HashSet(); var newEffects = new List(); foreach (var effect in effects.Reverse()) { if (effect.Type.IsWholeKeyboardEffect() && usedKeyCodes.Intersect(effect.Keys).Any()) continue; var newKeyCodes = effect.Keys.Except(usedKeyCodes).ToArray(); foreach (var keyCode in newKeyCodes) usedKeyCodes.Add(keyCode); if (newKeyCodes.IsEmpty()) continue; var newEffect = new SpectrumKeyboardBacklightEffect(effect.Type, effect.Speed, effect.Direction, effect.ClockwiseDirection, effect.Colors, newKeyCodes); newEffects.Add(newEffect); } newEffects.Reverse(); return [.. newEffects]; } private static (int Profile, SpectrumKeyboardBacklightEffect[] Effects) Convert(LENOVO_SPECTRUM_EFFECT_DESCRIPTION description) { var profile = description.Profile; var effects = description.Effects.Select(Convert).ToArray(); return (profile, effects); } private static SpectrumKeyboardBacklightEffect Convert(LENOVO_SPECTRUM_EFFECT effect) { var effectType = effect.EffectHeader.EffectType switch { LENOVO_SPECTRUM_EFFECT_TYPE.Always => SpectrumKeyboardBacklightEffectType.Always, LENOVO_SPECTRUM_EFFECT_TYPE.LegionAuraSync => SpectrumKeyboardBacklightEffectType.AuroraSync, LENOVO_SPECTRUM_EFFECT_TYPE.AudioBounceLighting => SpectrumKeyboardBacklightEffectType.AudioBounce, LENOVO_SPECTRUM_EFFECT_TYPE.AudioRippleLighting => SpectrumKeyboardBacklightEffectType.AudioRipple, LENOVO_SPECTRUM_EFFECT_TYPE.ColorChange => SpectrumKeyboardBacklightEffectType.ColorChange, LENOVO_SPECTRUM_EFFECT_TYPE.ColorPulse => SpectrumKeyboardBacklightEffectType.ColorPulse, LENOVO_SPECTRUM_EFFECT_TYPE.ColorWave => SpectrumKeyboardBacklightEffectType.ColorWave, LENOVO_SPECTRUM_EFFECT_TYPE.Rain => SpectrumKeyboardBacklightEffectType.Rain, LENOVO_SPECTRUM_EFFECT_TYPE.ScrewRainbow => SpectrumKeyboardBacklightEffectType.RainbowScrew, LENOVO_SPECTRUM_EFFECT_TYPE.RainbowWave => SpectrumKeyboardBacklightEffectType.RainbowWave, LENOVO_SPECTRUM_EFFECT_TYPE.Ripple => SpectrumKeyboardBacklightEffectType.Ripple, LENOVO_SPECTRUM_EFFECT_TYPE.Smooth => SpectrumKeyboardBacklightEffectType.Smooth, LENOVO_SPECTRUM_EFFECT_TYPE.TypeLighting => SpectrumKeyboardBacklightEffectType.Type, _ => throw new ArgumentException(nameof(effect.EffectHeader.EffectType)) }; var speed = effect.EffectHeader.Speed switch { LENOVO_SPECTRUM_SPEED.Speed1 => SpectrumKeyboardBacklightSpeed.Speed1, LENOVO_SPECTRUM_SPEED.Speed2 => SpectrumKeyboardBacklightSpeed.Speed2, LENOVO_SPECTRUM_SPEED.Speed3 => SpectrumKeyboardBacklightSpeed.Speed3, _ => SpectrumKeyboardBacklightSpeed.None }; var direction = effect.EffectHeader.Direction switch { LENOVO_SPECTRUM_DIRECTION.LeftToRight => SpectrumKeyboardBacklightDirection.LeftToRight, LENOVO_SPECTRUM_DIRECTION.RightToLeft => SpectrumKeyboardBacklightDirection.RightToLeft, LENOVO_SPECTRUM_DIRECTION.BottomToTop => SpectrumKeyboardBacklightDirection.BottomToTop, LENOVO_SPECTRUM_DIRECTION.TopToBottom => SpectrumKeyboardBacklightDirection.TopToBottom, _ => SpectrumKeyboardBacklightDirection.None }; var clockwiseDirection = effect.EffectHeader.ClockwiseDirection switch { LENOVO_SPECTRUM_CLOCKWISE_DIRECTION.Clockwise => SpectrumKeyboardBacklightClockwiseDirection.Clockwise, LENOVO_SPECTRUM_CLOCKWISE_DIRECTION.CounterClockwise => SpectrumKeyboardBacklightClockwiseDirection.CounterClockwise, _ => SpectrumKeyboardBacklightClockwiseDirection.None }; var colors = effect.Colors.Select(c => new RGBColor(c.R, c.G, c.B)).ToArray(); var keys = effect.KeyCodes; if (effect.KeyCodes is [0x65]) keys = []; return new(effectType, speed, direction, clockwiseDirection, colors, keys); } private static LENOVO_SPECTRUM_EFFECT_DESCRIPTION Convert(int profile, SpectrumKeyboardBacklightEffect[] effects) { var header = new LENOVO_SPECTRUM_HEADER(LENOVO_SPECTRUM_OPERATION_TYPE.EffectChange, 0); // Size will be set on serialization var str = effects.Select((e, i) => Convert(i, e)).ToArray(); var result = new LENOVO_SPECTRUM_EFFECT_DESCRIPTION(header, (byte)profile, str); return result; } private static LENOVO_SPECTRUM_EFFECT Convert(int index, SpectrumKeyboardBacklightEffect effect) { var effectType = effect.Type switch { SpectrumKeyboardBacklightEffectType.Always => LENOVO_SPECTRUM_EFFECT_TYPE.Always, SpectrumKeyboardBacklightEffectType.AuroraSync => LENOVO_SPECTRUM_EFFECT_TYPE.LegionAuraSync, SpectrumKeyboardBacklightEffectType.AudioBounce => LENOVO_SPECTRUM_EFFECT_TYPE.AudioBounceLighting, SpectrumKeyboardBacklightEffectType.AudioRipple => LENOVO_SPECTRUM_EFFECT_TYPE.AudioRippleLighting, SpectrumKeyboardBacklightEffectType.ColorChange => LENOVO_SPECTRUM_EFFECT_TYPE.ColorChange, SpectrumKeyboardBacklightEffectType.ColorPulse => LENOVO_SPECTRUM_EFFECT_TYPE.ColorPulse, SpectrumKeyboardBacklightEffectType.ColorWave => LENOVO_SPECTRUM_EFFECT_TYPE.ColorWave, SpectrumKeyboardBacklightEffectType.Rain => LENOVO_SPECTRUM_EFFECT_TYPE.Rain, SpectrumKeyboardBacklightEffectType.RainbowScrew => LENOVO_SPECTRUM_EFFECT_TYPE.ScrewRainbow, SpectrumKeyboardBacklightEffectType.RainbowWave => LENOVO_SPECTRUM_EFFECT_TYPE.RainbowWave, SpectrumKeyboardBacklightEffectType.Ripple => LENOVO_SPECTRUM_EFFECT_TYPE.Ripple, SpectrumKeyboardBacklightEffectType.Smooth => LENOVO_SPECTRUM_EFFECT_TYPE.Smooth, SpectrumKeyboardBacklightEffectType.Type => LENOVO_SPECTRUM_EFFECT_TYPE.TypeLighting, _ => throw new ArgumentException(nameof(effect.Type)) }; var speed = effect.Speed switch { SpectrumKeyboardBacklightSpeed.Speed1 => LENOVO_SPECTRUM_SPEED.Speed1, SpectrumKeyboardBacklightSpeed.Speed2 => LENOVO_SPECTRUM_SPEED.Speed2, SpectrumKeyboardBacklightSpeed.Speed3 => LENOVO_SPECTRUM_SPEED.Speed3, _ => LENOVO_SPECTRUM_SPEED.None }; var direction = effect.Direction switch { SpectrumKeyboardBacklightDirection.LeftToRight => LENOVO_SPECTRUM_DIRECTION.LeftToRight, SpectrumKeyboardBacklightDirection.RightToLeft => LENOVO_SPECTRUM_DIRECTION.RightToLeft, SpectrumKeyboardBacklightDirection.BottomToTop => LENOVO_SPECTRUM_DIRECTION.BottomToTop, SpectrumKeyboardBacklightDirection.TopToBottom => LENOVO_SPECTRUM_DIRECTION.TopToBottom, _ => LENOVO_SPECTRUM_DIRECTION.None }; var clockwiseDirection = effect.ClockwiseDirection switch { SpectrumKeyboardBacklightClockwiseDirection.Clockwise => LENOVO_SPECTRUM_CLOCKWISE_DIRECTION.Clockwise, SpectrumKeyboardBacklightClockwiseDirection.CounterClockwise => LENOVO_SPECTRUM_CLOCKWISE_DIRECTION.CounterClockwise, _ => LENOVO_SPECTRUM_CLOCKWISE_DIRECTION.None }; var colorMode = effect.Type switch { SpectrumKeyboardBacklightEffectType.Always => LENOVO_SPECTRUM_COLOR_MODE.ColorList, SpectrumKeyboardBacklightEffectType.ColorChange when effect.Colors.Length != 0 => LENOVO_SPECTRUM_COLOR_MODE.ColorList, SpectrumKeyboardBacklightEffectType.ColorPulse when effect.Colors.Length != 0 => LENOVO_SPECTRUM_COLOR_MODE.ColorList, SpectrumKeyboardBacklightEffectType.ColorWave when effect.Colors.Length != 0 => LENOVO_SPECTRUM_COLOR_MODE.ColorList, SpectrumKeyboardBacklightEffectType.Rain when effect.Colors.Length != 0 => LENOVO_SPECTRUM_COLOR_MODE.ColorList, SpectrumKeyboardBacklightEffectType.Smooth when effect.Colors.Length != 0 => LENOVO_SPECTRUM_COLOR_MODE.ColorList, SpectrumKeyboardBacklightEffectType.Ripple when effect.Colors.Length != 0 => LENOVO_SPECTRUM_COLOR_MODE.ColorList, SpectrumKeyboardBacklightEffectType.Type when effect.Colors.Length != 0 => LENOVO_SPECTRUM_COLOR_MODE.ColorList, SpectrumKeyboardBacklightEffectType.ColorChange => LENOVO_SPECTRUM_COLOR_MODE.RandomColor, SpectrumKeyboardBacklightEffectType.ColorPulse => LENOVO_SPECTRUM_COLOR_MODE.RandomColor, SpectrumKeyboardBacklightEffectType.ColorWave => LENOVO_SPECTRUM_COLOR_MODE.RandomColor, SpectrumKeyboardBacklightEffectType.Rain => LENOVO_SPECTRUM_COLOR_MODE.RandomColor, SpectrumKeyboardBacklightEffectType.Smooth => LENOVO_SPECTRUM_COLOR_MODE.RandomColor, SpectrumKeyboardBacklightEffectType.Ripple => LENOVO_SPECTRUM_COLOR_MODE.RandomColor, SpectrumKeyboardBacklightEffectType.Type => LENOVO_SPECTRUM_COLOR_MODE.RandomColor, _ => LENOVO_SPECTRUM_COLOR_MODE.None }; var header = new LENOVO_SPECTRUM_EFFECT_HEADER(effectType, speed, direction, clockwiseDirection, colorMode); var colors = effect.Colors.Select(c => new LENOVO_SPECTRUM_COLOR(c.R, c.G, c.B)).ToArray(); var keys = effect.Type.IsAllLightsEffect() ? [0x65] : effect.Keys; var result = new LENOVO_SPECTRUM_EFFECT(header, index + 1, colors, keys); return result; } } ================================================ FILE: LenovoLegionToolkit.Lib/Controllers/WindowsPowerModeController.cs ================================================ using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Settings; using LenovoLegionToolkit.Lib.System; using LenovoLegionToolkit.Lib.Utils; using Windows.Win32; using Windows.Win32.Foundation; namespace LenovoLegionToolkit.Lib.Controllers; public partial class WindowsPowerModeController(ApplicationSettings settings, IMainThreadDispatcher mainThreadDispatcher) { private const string POWER_SCHEMES_HIVE = "HKEY_LOCAL_MACHINE"; private const string POWER_SCHEMES_SUBKEY = "SYSTEM\\CurrentControlSet\\Control\\Power\\User\\PowerSchemes"; private const string ACTIVE_OVERLAY_AC_POWER_SCHEME_KEY = "ActiveOverlayAcPowerScheme"; private const string ACTIVE_OVERLAY_DC_POWER_SCHEME_KEY = "ActiveOverlayDcPowerScheme"; private static readonly Guid DefaultPowerPlan = Guid.Parse("381b4222-f694-41f0-9685-ff5bb260df2e"); private static readonly Guid BestPowerEfficiency = Guid.Parse("961cc777-2547-4f9d-8174-7d86181b8a7a"); private static readonly Guid BestPerformance = Guid.Parse("ded574b5-45a0-4f42-8737-46345c09c238"); private readonly ThrottleLastDispatcher _dispatcher = new(TimeSpan.FromSeconds(2), nameof(WindowsPowerModeController)); public async Task SetPowerModeAsync(PowerModeState powerModeState) { if (settings.Store.PowerModeMappingMode is not PowerModeMappingMode.WindowsPowerMode) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Ignoring... [powerModeMappingMode={settings.Store.PowerModeMappingMode}]"); return; } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Activating... [powerModeState={powerModeState}]"); var powerMode = settings.Store.PowerModes.GetValueOrDefault(powerModeState, WindowsPowerMode.Balanced); var powerModeGuid = GuidForWindowsPowerMode(powerMode); if (Power.IsBatterySaverEnabled()) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Battery saver is on - will not set overlay scheme."); return; } await _dispatcher.DispatchAsync(() => { ActivateDefaultPowerPlanIfNeeded(); mainThreadDispatcher.Dispatch(() => { var result = PowerSetActiveOverlayScheme(powerModeGuid); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Overlay scheme set. [result={result}]"); }); try { UpdateRegistry(powerModeGuid); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to update registry.", ex); } return Task.CompletedTask; }).ConfigureAwait(false); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Power mode {powerMode} activated... [powerModeState={powerModeState}, powerModeGuid={powerModeGuid}]"); } private static void UpdateRegistry(Guid guid) { Registry.SetValue(POWER_SCHEMES_HIVE, POWER_SCHEMES_SUBKEY, ACTIVE_OVERLAY_AC_POWER_SCHEME_KEY, guid, true); Registry.SetValue(POWER_SCHEMES_HIVE, POWER_SCHEMES_SUBKEY, ACTIVE_OVERLAY_DC_POWER_SCHEME_KEY, guid, true); } private static Guid GuidForWindowsPowerMode(WindowsPowerMode windowsPowerMode) => windowsPowerMode switch { WindowsPowerMode.BestPowerEfficiency => BestPowerEfficiency, WindowsPowerMode.BestPerformance => BestPerformance, _ => Guid.Empty }; private static unsafe void ActivateDefaultPowerPlanIfNeeded() { if (PInvoke.PowerGetActiveScheme(null, out var guid) != WIN32_ERROR.ERROR_SUCCESS) PInvokeExtensions.ThrowIfWin32Error("PowerGetActiveScheme"); if (DefaultPowerPlan == *guid) return; if (PInvoke.PowerSetActiveScheme(null, DefaultPowerPlan) != WIN32_ERROR.ERROR_SUCCESS) PInvokeExtensions.ThrowIfWin32Error("PowerSetActiveScheme"); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Activated default power plan."); } [LibraryImport("powrprof.dll", EntryPoint = "PowerSetActiveOverlayScheme")] private static partial uint PowerSetActiveOverlayScheme(Guid guid); } ================================================ FILE: LenovoLegionToolkit.Lib/Controllers/WindowsPowerPlanController.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Settings; using LenovoLegionToolkit.Lib.SoftwareDisabler; using LenovoLegionToolkit.Lib.Utils; using Windows.Win32; using Windows.Win32.Foundation; using Windows.Win32.System.Power; namespace LenovoLegionToolkit.Lib.Controllers; public class WindowsPowerPlanController(ApplicationSettings settings, VantageDisabler vantageDisabler) { private static readonly Guid DefaultPowerPlan = Guid.Parse("381b4222-f694-41f0-9685-ff5bb260df2e"); public IEnumerable GetPowerPlans() { var activePowerPlanGuid = GetActivePowerPlanGuid(); foreach (var powerPlanGuid in GetPowerPlanGuids()) { var powerPlaneName = GetPowerPlanName(powerPlanGuid); yield return new WindowsPowerPlan(powerPlanGuid, powerPlaneName, powerPlanGuid == activePowerPlanGuid); } } public async Task SetPowerPlanAsync(PowerModeState powerModeState, bool alwaysActivateDefaults = false) { if (settings.Store.PowerModeMappingMode is not PowerModeMappingMode.WindowsPowerPlan) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Ignoring... [powerModeMappingMode={settings.Store.PowerModeMappingMode}]"); return; } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Activating... [powerModeState={powerModeState}, alwaysActivateDefaults={alwaysActivateDefaults}]"); var powerPlanId = settings.Store.PowerPlans.GetValueOrDefault(powerModeState); var isDefault = false; if (powerPlanId == Guid.Empty) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Power plan for power mode {powerModeState} was not found in settings"); powerPlanId = DefaultPowerPlan; isDefault = true; } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Power plan to be activated is {powerPlanId} [isDefault={isDefault}]"); if (!await ShouldSetPowerPlanAsync(alwaysActivateDefaults, isDefault).ConfigureAwait(false)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Power plan {powerPlanId} will not be activated [isDefault={isDefault}]"); return; } var powerPlans = GetPowerPlans().ToArray(); if (Log.Instance.IsTraceEnabled) { Log.Instance.Trace($"Available power plans:"); foreach (var powerPlan in powerPlans) Log.Instance.Trace($" - {powerPlan}"); } var powerPlanToActivate = powerPlans.FirstOrDefault(pp => pp.Guid == powerPlanId); if (powerPlanToActivate.Equals(default(WindowsPowerPlan))) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Power plan {powerPlanId} was not found"); return; } if (powerPlanToActivate.IsActive) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Power plan {powerPlanToActivate.Guid} is already active. [name={powerPlanToActivate.Name}]"); return; } SetActivePowerPlan(powerPlanToActivate.Guid); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Power plan {powerPlanToActivate.Guid} activated. [name={powerPlanToActivate.Name}]"); } public void SetPowerPlanParameter(WindowsPowerPlan windowsPowerPlan, Brightness brightness) { PInvoke.PowerWriteACValueIndex(NullSafeHandle.Null, windowsPowerPlan.Guid, PInvoke.GUID_VIDEO_SUBGROUP, PInvokeExtensions.DISPLAY_BRIGTHNESS_SETTING_GUID, brightness.Value); PInvoke.PowerWriteDCValueIndex(NullSafeHandle.Null, windowsPowerPlan.Guid, PInvoke.GUID_VIDEO_SUBGROUP, PInvokeExtensions.DISPLAY_BRIGTHNESS_SETTING_GUID, brightness.Value); } private async Task ShouldSetPowerPlanAsync(bool alwaysActivateDefaults, bool isDefault) { if (isDefault && alwaysActivateDefaults) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Power plan is default and always active defaults is set"); return true; } var status = await vantageDisabler.GetStatusAsync().ConfigureAwait(false); if (status is SoftwareStatus.NotFound or SoftwareStatus.Disabled) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Vantage is active [status={status}]"); return true; } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Criteria for activation not met [isDefault={isDefault}, alwaysActivateDefaults={alwaysActivateDefaults}, status={status}]"); return false; } private static unsafe List GetPowerPlanGuids() { var list = new List(); var bufferSize = (uint)Marshal.SizeOf(); var buffer = new byte[bufferSize]; fixed (byte* bufferPtr = buffer) { uint index = 0; while (PInvoke.PowerEnumerate(null, null, null, POWER_DATA_ACCESSOR.ACCESS_SCHEME, index, bufferPtr, ref bufferSize) == WIN32_ERROR.ERROR_SUCCESS) { list.Add(new Guid(buffer)); index++; } } return list; } private static unsafe string GetPowerPlanName(Guid powerPlanGuid) { var nameSize = 2048u; var namePtr = Marshal.AllocHGlobal((int)nameSize); try { if (PInvoke.PowerReadFriendlyName(null, powerPlanGuid, null, null, (byte*)namePtr.ToPointer(), ref nameSize) != WIN32_ERROR.ERROR_SUCCESS) PInvokeExtensions.ThrowIfWin32Error("PowerReadFriendlyName"); return Marshal.PtrToStringUni(namePtr) ?? string.Empty; } catch { return powerPlanGuid.ToString(); } finally { Marshal.FreeHGlobal(namePtr); } } private static unsafe Guid GetActivePowerPlanGuid() { if (PInvoke.PowerGetActiveScheme(null, out var guid) != WIN32_ERROR.ERROR_SUCCESS) PInvokeExtensions.ThrowIfWin32Error("PowerGetActiveScheme"); return *guid; } private static void SetActivePowerPlan(Guid powerPlanGuid) { if (PInvoke.PowerSetActiveScheme(null, powerPlanGuid) != WIN32_ERROR.ERROR_SUCCESS) PInvokeExtensions.ThrowIfWin32Error("PowerSetActiveScheme"); } } ================================================ FILE: LenovoLegionToolkit.Lib/Enums.cs ================================================ using System; using System.ComponentModel.DataAnnotations; using LenovoLegionToolkit.Lib.Resources; namespace LenovoLegionToolkit.Lib; public enum AlwaysOnUSBState { [Display(ResourceType = typeof(Resource), Name = "AlwaysOnUSBState_Off")] Off, [Display(ResourceType = typeof(Resource), Name = "AlwaysOnUSBState_OnWhenSleeping")] OnWhenSleeping, [Display(ResourceType = typeof(Resource), Name = "AlwaysOnUSBState_OnAlways")] OnAlways } public enum AutorunState { [Display(ResourceType = typeof(Resource), Name = "AutorunState_Enabled")] Enabled, [Display(ResourceType = typeof(Resource), Name = "AutorunState_EnabledDelayed")] EnabledDelayed, [Display(ResourceType = typeof(Resource), Name = "AutorunState_Disabled")] Disabled } public enum BatteryNightChargeState { [Display(ResourceType = typeof(Resource), Name = "BatteryNightChargeState_On")] On, [Display(ResourceType = typeof(Resource), Name = "BatteryNightChargeState_Off")] Off } public enum BatteryState { [Display(ResourceType = typeof(Resource), Name = "BatteryState_Conservation")] Conservation, [Display(ResourceType = typeof(Resource), Name = "BatteryState_Normal")] Normal, [Display(ResourceType = typeof(Resource), Name = "BatteryState_RapidCharge")] RapidCharge } public enum CapabilityID { IGPUMode = 0x00010000, FlipToStart = 0x00030000, NvidiaGPUDynamicDisplaySwitching = 0x00040000, AMDSmartShiftMode = 0x00050001, AMDSkinTemperatureTracking = 0x00050002, SupportedPowerModes = 0x00070000, LegionZoneSupportVersion = 0x00090000, GodModeFnQSwitchable = 0x00100000, OverDrive = 0x001A0000, AIChip = 0x000E0000, IGPUModeChangeStatus = 0x000F0000, CPUShortTermPowerLimit = 0x0101FF00, CPULongTermPowerLimit = 0x0102FF00, CPUPeakPowerLimit = 0x0103FF00, CPUTemperatureLimit = 0x0104FF00, APUsPPTPowerLimit = 0x0105FF00, CPUCrossLoadingPowerLimit = 0x0106FF00, CPUPL1Tau = 0x0107FF00, GPUPowerBoost = 0x0201FF00, GPUConfigurableTGP = 0x0202FF00, GPUTemperatureLimit = 0x0203FF00, GPUTotalProcessingPowerTargetOnAcOffsetFromBaseline = 0x0204FF00, GPUToCPUDynamicBoost = 0x020BFF00, GPUStatus = 0x02070000, GPUDidVid = 0x02090000, InstantBootAc = 0x03010001, InstantBootUsbPowerDelivery = 0x03010002, FanFullSpeed = 0x04020000, CpuCurrentFanSpeed = 0x04030001, GpuCurrentFanSpeed = 0x04030002, CpuCurrentTemperature = 0x05040000, GpuCurrentTemperature = 0x05050000 } [Flags] public enum DriverKey { FnF10 = 32, FnF4 = 256, FnF8 = 8192, FnSpace = 4096, } public enum FanTableType { Unknown, CPU, CPUSensor, GPU, GPU2 } public enum FlipToStartState { [Display(ResourceType = typeof(Resource), Name = "FlipToStartState_Off")] Off, [Display(ResourceType = typeof(Resource), Name = "FlipToStartState_On")] On } public enum FnLockState { [Display(ResourceType = typeof(Resource), Name = "FnLockState_Off")] Off, [Display(ResourceType = typeof(Resource), Name = "FnLockState_On")] On } public enum GPUState { Unknown, NvidiaGpuNotFound, MonitorConnected, Active, Inactive, PoweredOff } public enum GSyncState { Off, On } public enum HDRState { [Display(ResourceType = typeof(Resource), Name = "HDRState_Off")] Off, [Display(ResourceType = typeof(Resource), Name = "HDRState_On")] On } public enum HybridModeState { [Display(ResourceType = typeof(Resource), Name = "HybridModeState_On")] On, [Display(ResourceType = typeof(Resource), Name = "HybridModeState_OnIGPUOnly")] OnIGPUOnly, [Display(ResourceType = typeof(Resource), Name = "HybridModeState_OnAuto")] OnAuto, [Display(ResourceType = typeof(Resource), Name = "HybridModeState_Off")] Off } public enum IGPUModeState { Default, IGPUOnly, Auto } public enum InstantBootState { [Display(ResourceType = typeof(Resource), Name = "InstantBootState_Off")] Off, [Display(ResourceType = typeof(Resource), Name = "InstantBootState_AcAdapter")] AcAdapter, [Display(ResourceType = typeof(Resource), Name = "InstantBootState_UsbPowerDelivery")] UsbPowerDelivery, [Display(ResourceType = typeof(Resource), Name = "InstantBootState_AcAdapterAndUsbPowerDelivery")] AcAdapterAndUsbPowerDelivery } public enum KeyboardLayout { Ansi, Iso, Jis } public enum KnownFolder { Contacts, Downloads, Favorites, Links, SavedGames, SavedSearches } public enum LightingChangeState { Panel = 0, Ports = 1, } public enum MicrophoneState { [Display(ResourceType = typeof(Resource), Name = "MicrophoneState_Off")] Off, [Display(ResourceType = typeof(Resource), Name = "MicrophoneState_On")] On } [Flags] public enum ModifierKey { None = 0, [Display(ResourceType = typeof(Resource), Name = "ModifierKey_Shift")] Shift = 1, [Display(ResourceType = typeof(Resource), Name = "ModifierKey_Ctrl")] Ctrl = 2, [Display(ResourceType = typeof(Resource), Name = "ModifierKey_Alt")] Alt = 4 } public enum NativeWindowsMessage { LidOpened, LidClosed, MonitorOn, MonitorOff, DeviceConnected, DeviceDisconnected, MonitorConnected, MonitorDisconnected, ExternalMonitorConnected, ExternalMonitorDisconnected, OnDisplayDeviceArrival, BatterySaverEnabled } public enum NotificationDuration { [Display(ResourceType = typeof(Resource), Name = "NotificationDuration_Short")] Short, [Display(ResourceType = typeof(Resource), Name = "NotificationDuration_Normal")] Normal, [Display(ResourceType = typeof(Resource), Name = "NotificationDuration_Long")] Long } public enum NotificationType { ACAdapterConnected, ACAdapterConnectedLowWattage, ACAdapterDisconnected, AutomationNotification, CameraOn, CameraOff, CapsLockOn, CapsLockOff, FnLockOn, FnLockOff, MicrophoneOff, MicrophoneOn, NumLockOn, NumLockOff, PanelLogoLightingOn, PanelLogoLightingOff, PortLightingOn, PortLightingOff, PowerModeQuiet, PowerModeBalance, PowerModePerformance, PowerModeGodMode, RefreshRate, RGBKeyboardBacklightChanged, RGBKeyboardBacklightOff, SmartKeyDoublePress, SmartKeySinglePress, SpectrumBacklightChanged, SpectrumBacklightOff, SpectrumBacklightPresetChanged, TouchpadOn, TouchpadOff, UpdateAvailable, WhiteKeyboardBacklightChanged, WhiteKeyboardBacklightOff } public enum NotificationPosition { [Display(ResourceType = typeof(Resource), Name = "NotificationPosition_BottomRight")] BottomRight, [Display(ResourceType = typeof(Resource), Name = "NotificationPosition_BottomCenter")] BottomCenter, [Display(ResourceType = typeof(Resource), Name = "NotificationPosition_BottomLeft")] BottomLeft, [Display(ResourceType = typeof(Resource), Name = "NotificationPosition_CenterLeft")] CenterLeft, [Display(ResourceType = typeof(Resource), Name = "NotificationPosition_TopLeft")] TopLeft, [Display(ResourceType = typeof(Resource), Name = "NotificationPosition_TopCenter")] TopCenter, [Display(ResourceType = typeof(Resource), Name = "NotificationPosition_TopRight")] TopRight, [Display(ResourceType = typeof(Resource), Name = "NotificationPosition_CenterRight")] CenterRight, [Display(ResourceType = typeof(Resource), Name = "NotificationPosition_Center")] Center } public enum OneLevelWhiteKeyboardBacklightState { [Display(ResourceType = typeof(Resource), Name = "OneLevelWhiteKeyboardBacklightState_Off")] Off, [Display(ResourceType = typeof(Resource), Name = "OneLevelWhiteKeyboardBacklightState_On")] On } public enum OS { [Display(Name = "Windows 11")] Windows11, [Display(Name = "Windows 10")] Windows10, [Display(Name = "Windows 8")] Windows8, [Display(Name = "Windows 7")] Windows7 } public enum OverDriveState { [Display(ResourceType = typeof(Resource), Name = "OverdriveState_Off")] Off, [Display(ResourceType = typeof(Resource), Name = "OverdriveState_On")] On } public enum PanelLogoBacklightState { [Display(ResourceType = typeof(Resource), Name = "PanelLogoBacklightState_Off")] Off, [Display(ResourceType = typeof(Resource), Name = "PanelLogoBacklightState_On")] On } public enum PortsBacklightState { [Display(ResourceType = typeof(Resource), Name = "PortsBacklightState_Off")] Off, [Display(ResourceType = typeof(Resource), Name = "PortsBacklightState_On")] On } public enum PowerAdapterStatus { Connected, ConnectedLowWattage, Disconnected } public enum PowerModeMappingMode { [Display(ResourceType = typeof(Resource), Name = "PowerModeMappingMode_Disabled")] Disabled, [Display(ResourceType = typeof(Resource), Name = "PowerModeMappingMode_WindowsPowerMode")] WindowsPowerMode, [Display(ResourceType = typeof(Resource), Name = "PowerModeMappingMode_WindowsPowerPlan")] WindowsPowerPlan, } public enum PowerModeState { [Display(ResourceType = typeof(Resource), Name = "PowerModeState_Quiet")] Quiet, [Display(ResourceType = typeof(Resource), Name = "PowerModeState_Balance")] Balance, [Display(ResourceType = typeof(Resource), Name = "PowerModeState_Performance")] Performance, [Display(ResourceType = typeof(Resource), Name = "PowerModeState_GodMode")] GodMode = 254 } public enum PowerStateEvent { Unknown = -1, StatusChange, Suspend, Resume, } public enum ProcessEventInfoType { Started, Stopped } public enum RebootType { NotRequired = 0, Forced = 1, Requested = 3, ForcedPowerOff = 4, Delayed = 5 } public enum RGBKeyboardBacklightChanged; public enum RGBKeyboardBacklightBrightness { [Display(ResourceType = typeof(Resource), Name = "RGBKeyboardBacklightBrightness_Low")] Low, [Display(ResourceType = typeof(Resource), Name = "RGBKeyboardBacklightBrightness_High")] High } public enum RGBKeyboardBacklightEffect { [Display(ResourceType = typeof(Resource), Name = "RGBKeyboardBacklightEffect_Static")] Static, [Display(ResourceType = typeof(Resource), Name = "RGBKeyboardBacklightEffect_Breath")] Breath, [Display(ResourceType = typeof(Resource), Name = "RGBKeyboardBacklightEffect_Smooth")] Smooth, [Display(ResourceType = typeof(Resource), Name = "RGBKeyboardBacklightEffect_WaveRTL")] WaveRTL, [Display(ResourceType = typeof(Resource), Name = "RGBKeyboardBacklightEffect_WaveLTR")] WaveLTR } public enum RGBKeyboardBacklightPreset { [Display(ResourceType = typeof(Resource), Name = "RGBKeyboardBacklightPreset_Off")] Off = -1, [Display(ResourceType = typeof(Resource), Name = "RGBKeyboardBacklightPreset_One")] One = 0, [Display(ResourceType = typeof(Resource), Name = "RGBKeyboardBacklightPreset_Two")] Two = 1, [Display(ResourceType = typeof(Resource), Name = "RGBKeyboardBacklightPreset_Three")] Three = 2, [Display(ResourceType = typeof(Resource), Name = "RGBKeyboardBacklightPreset_Four")] Four = 3 } public enum RGBKeyboardBacklightSpeed { [Display(ResourceType = typeof(Resource), Name = "RGBKeyboardBacklightSpeed_Slowest")] Slowest, [Display(ResourceType = typeof(Resource), Name = "RGBKeyboardBacklightSpeed_Slow")] Slow, [Display(ResourceType = typeof(Resource), Name = "RGBKeyboardBacklightSpeed_Fast")] Fast, [Display(ResourceType = typeof(Resource), Name = "RGBKeyboardBacklightSpeed_Fastest")] Fastest } public enum SpeakerState { [Display(ResourceType = typeof(Resource), Name = "SpeakerState_Mute")] Mute, [Display(ResourceType = typeof(Resource), Name = "SpeakerState_Unmute")] Unmute } public enum SoftwareStatus { Enabled, Disabled, NotFound } public enum SpecialKey { FnF9 = 1, FnLockOn = 2, FnLockOff = 3, FnPrtSc = 4, FnPrtSc2 = 45, CameraOn = 12, CameraOff = 13, FnR = 16, FnR2 = 0x0041002A, SpectrumBacklightOff = 24, SpectrumBacklight1 = 25, SpectrumBacklight2 = 26, SpectrumBacklight3 = 38, SpectrumPreset1 = 32, SpectrumPreset2 = 33, SpectrumPreset3 = 34, SpectrumPreset4 = 35, SpectrumPreset5 = 36, SpectrumPreset6 = 37, FnN = 42, FnF4 = 62, FnF8 = 63, WhiteBacklightOff = 64, WhiteBacklight1 = 65, WhiteBacklight2 = 66 } public enum SpectrumKeyboardBacklightBrightness { [Display(ResourceType = typeof(Resource), Name = "SpectrumKeyboardBacklightBrightness_Off")] Off, [Display(ResourceType = typeof(Resource), Name = "SpectrumKeyboardBacklightBrightness_Low")] Low, [Display(ResourceType = typeof(Resource), Name = "SpectrumKeyboardBacklightBrightness_Medium")] Medium, [Display(ResourceType = typeof(Resource), Name = "SpectrumKeyboardBacklightBrightness_High")] High } public enum SpectrumKeyboardBacklightClockwiseDirection { None, [Display(ResourceType = typeof(Resource), Name = "SpectrumKeyboardBacklightDirection_Clockwise")] Clockwise, [Display(ResourceType = typeof(Resource), Name = "SpectrumKeyboardBacklightDirection_CounterClockwise")] CounterClockwise } public enum SpectrumKeyboardBacklightDirection { None, [Display(ResourceType = typeof(Resource), Name = "SpectrumKeyboardBacklightDirection_BottomToTop")] BottomToTop, [Display(ResourceType = typeof(Resource), Name = "SpectrumKeyboardBacklightDirection_TopToBottom")] TopToBottom, [Display(ResourceType = typeof(Resource), Name = "SpectrumKeyboardBacklightDirection_LeftToRight")] LeftToRight, [Display(ResourceType = typeof(Resource), Name = "SpectrumKeyboardBacklightDirection_RightToLeft")] RightToLeft } public enum SpectrumKeyboardBacklightEffectType { [Display(ResourceType = typeof(Resource), Name = "SpectrumKeyboardBacklightEffectType_Always")] Always, [Display(ResourceType = typeof(Resource), Name = "SpectrumKeyboardBacklightEffectType_RainbowScrew")] RainbowScrew, [Display(ResourceType = typeof(Resource), Name = "SpectrumKeyboardBacklightEffectType_RainbowWave")] RainbowWave, [Display(ResourceType = typeof(Resource), Name = "SpectrumKeyboardBacklightEffectType_ColorChange")] ColorChange, [Display(ResourceType = typeof(Resource), Name = "SpectrumKeyboardBacklightEffectType_ColorWave")] ColorWave, [Display(ResourceType = typeof(Resource), Name = "SpectrumKeyboardBacklightEffectType_ColorPulse")] ColorPulse, [Display(ResourceType = typeof(Resource), Name = "SpectrumKeyboardBacklightEffectType_Smooth")] Smooth, [Display(ResourceType = typeof(Resource), Name = "SpectrumKeyboardBacklightEffectType_Rain")] Rain, [Display(ResourceType = typeof(Resource), Name = "SpectrumKeyboardBacklightEffectType_Ripple")] Ripple, [Display(ResourceType = typeof(Resource), Name = "SpectrumKeyboardBacklightEffectType_Type")] Type, [Display(ResourceType = typeof(Resource), Name = "SpectrumKeyboardBacklightEffectType_AudioBounce")] AudioBounce, [Display(ResourceType = typeof(Resource), Name = "SpectrumKeyboardBacklightEffectType_AudioRipple")] AudioRipple, [Display(ResourceType = typeof(Resource), Name = "SpectrumKeyboardBacklightEffectType_AuroraSync")] AuroraSync } public enum SpectrumKeyboardBacklightSpeed { None, [Display(ResourceType = typeof(Resource), Name = "SpectrumKeyboardBacklightSpeed_Speed1")] Speed1, [Display(ResourceType = typeof(Resource), Name = "SpectrumKeyboardBacklightSpeed_Speed2")] Speed2, [Display(ResourceType = typeof(Resource), Name = "SpectrumKeyboardBacklightSpeed_Speed3")] Speed3 } public enum SpectrumLayout { KeyboardOnly, KeyboardAndFront, Full, FullAlternative } public enum Theme { [Display(ResourceType = typeof(Resource), Name = "Theme_System")] System, [Display(ResourceType = typeof(Resource), Name = "Theme_Light")] Light, [Display(ResourceType = typeof(Resource), Name = "Theme_Dark")] Dark } public enum AccentColorSource { [Display(ResourceType = typeof(Resource), Name = "AccentColorSource_System")] System, [Display(ResourceType = typeof(Resource), Name = "AccentColorSource_Custom")] Custom } public enum TemperatureUnit { C, F } public enum ThermalModeState { Unknown, Quiet, Balance, Performance, GodMode = 255 } public enum TouchpadLockState { [Display(ResourceType = typeof(Resource), Name = "TouchpadLockState_Off")] Off, [Display(ResourceType = typeof(Resource), Name = "TouchpadLockState_On")] On } public enum UpdateCheckFrequency { [Display(ResourceType = typeof(Resource), Name = "UpdateCheckFrequency_PerHour")] PerHour, [Display(ResourceType = typeof(Resource), Name = "UpdateCheckFrequency_PerThreeHours")] PerThreeHours, [Display(ResourceType = typeof(Resource), Name = "UpdateCheckFrequency_PerTwelveHours")] PerTwelveHours, [Display(ResourceType = typeof(Resource), Name = "UpdateCheckFrequency_PerDay")] PerDay, [Display(ResourceType = typeof(Resource), Name = "UpdateCheckFrequency_PerWeek")] PerWeek, [Display(ResourceType = typeof(Resource), Name = "UpdateCheckFrequency_PerMonth")] PerMonth } public enum UpdateCheckStatus { Success, RateLimitReached, Error } public enum WhiteKeyboardBacklightState { [Display(ResourceType = typeof(Resource), Name = "WhiteKeyboardBacklightState_Off")] Off, [Display(ResourceType = typeof(Resource), Name = "WhiteKeyboardBacklightState_Low")] Low, [Display(ResourceType = typeof(Resource), Name = "WhiteKeyboardBacklightState_High")] High } public enum WindowsPowerMode { [Display(Name = "Best power efficiency")] BestPowerEfficiency, [Display(Name = "Balanced")] Balanced, [Display(Name = "Best performance")] BestPerformance } public enum WinKeyState { [Display(ResourceType = typeof(Resource), Name = "WinKeyState_Off")] Off, [Display(ResourceType = typeof(Resource), Name = "WinKeyState_On")] On } public enum WinKeyChanged; ================================================ FILE: LenovoLegionToolkit.Lib/Extensions/AssemblyExtensions.cs ================================================ using System; using System.Globalization; using System.Reflection; namespace LenovoLegionToolkit.Lib.Extensions; public static class AssemblyExtensions { public static DateTime? GetBuildDateTime(this Assembly assembly) { const string buildVersionMetadataPrefix = "+build"; var attribute = assembly.GetCustomAttribute(); if (attribute?.InformationalVersion is null) return null; var value = attribute.InformationalVersion; var index = value.IndexOf(buildVersionMetadataPrefix, StringComparison.InvariantCultureIgnoreCase); if (index <= 0) return null; value = value[(index + buildVersionMetadataPrefix.Length)..]; // ReSharper disable once StringLiteralTypo if (DateTime.TryParseExact(value, "yyyyMMddHHmmss", CultureInfo.InvariantCulture, DateTimeStyles.None, out var result)) return result; return null; } public static string? GetBuildDateTimeString(this Assembly assembly) { // ReSharper disable once StringLiteralTypo return GetBuildDateTime(assembly)?.ToString("yyyyMMddHHmmss"); } } ================================================ FILE: LenovoLegionToolkit.Lib/Extensions/ContainerBuilderExtensions.cs ================================================ using Autofac; using Autofac.Builder; namespace LenovoLegionToolkit.Lib.Extensions; public static class ContainerBuilderExtensions { public static IRegistrationBuilder Register(this ContainerBuilder cb, bool selfOnly = false) where T : notnull { var registration = cb.RegisterType().AsSelf(); if (!selfOnly) registration = registration.AsImplementedInterfaces(); return registration.SingleInstance(); } } ================================================ FILE: LenovoLegionToolkit.Lib/Extensions/DateTimeExtensions.cs ================================================ using System; namespace LenovoLegionToolkit.Lib.Extensions; public static class DateTimeExtensions { public static DateTime UtcFrom(int hours, int minutes) { var now = DateTime.UtcNow; return new(now.Year, now.Month, now.Day, hours, minutes, 0, DateTimeKind.Utc); } public static DateTime LocalFrom(int hours, int minutes) { var now = DateTime.Now; return new(now.Year, now.Month, now.Day, hours, minutes, 0, DateTimeKind.Local); } public static DateTime UtcDayFrom(DayOfWeek targetDay, int hours, int minutes) { var now = DateTime.UtcNow; var date = new DateTime(now.Year, now.Month, now.Day, hours, minutes, now.Second, DateTimeKind.Utc); var daysUntilDayOfWeek = ((int)targetDay - (int)date.DayOfWeek + 7) % 7; return date.AddDays(daysUntilDayOfWeek); } public static DateTime LocalDayFrom(DayOfWeek targetDay, int hours, int minutes) { var now = DateTime.Now; var date = new DateTime(now.Year, now.Month, now.Day, hours, minutes, now.Second, DateTimeKind.Local); var daysUntilDayOfWeek = ((int)targetDay - (int)date.DayOfWeek + 7) % 7; return date.AddDays(daysUntilDayOfWeek); } } ================================================ FILE: LenovoLegionToolkit.Lib/Extensions/DictionaryExtensions.cs ================================================ using System.Collections.Generic; using System.Collections.ObjectModel; namespace LenovoLegionToolkit.Lib.Extensions; public static class DictionaryExtensions { public static ReadOnlyDictionary AsReadOnlyDictionary(this IDictionary source) where TKey : notnull { return new ReadOnlyDictionary(source); } public static void AddRange(this IDictionary source, IDictionary items) { foreach (var keyValuePair in items) source.Add(keyValuePair.Key, keyValuePair.Value); } public static TValue? GetValueOrNull(this IReadOnlyDictionary dictionary, TKey key) where TValue : struct { return !dictionary.TryGetValue(key, out var obj) ? null : obj; } } ================================================ FILE: LenovoLegionToolkit.Lib/Extensions/DisplayExtensions.cs ================================================ using System.Linq; using System.Runtime.InteropServices; using Windows.Win32; using Windows.Win32.Devices.Display; using WindowsDisplayAPI; using WindowsDisplayAPI.DisplayConfig; namespace LenovoLegionToolkit.Lib.Extensions; public static class DisplayExtensions { public static void SetSettingsUsingPathInfo(this Display display, DisplaySetting displaySetting) { // Use display path APIs to change internal display resolution & refresh rate. // Compared to Display.SetSettings(), these APIs can change the Active Signal Mode and not just the Desktop mode. // Setting 60Hz will change the Active Signal Mode to 60Hz instead of leaving it at the max refresh rate, // which lets the display consume less power for more battery life. var displaySource = display.ToPathDisplaySource(); var pathInfos = PathInfo.GetActivePaths(); for (var i = 0; i < pathInfos.Length; i++) { var pathInfo = pathInfos[i]; if (pathInfo.DisplaySource == displaySource) { var targetsInfo = pathInfo.TargetsInfo; var pathTargetInfos = targetsInfo .Select(targetInfo => new PathTargetInfo(targetInfo.DisplayTarget, new PathTargetSignalInfo(displaySetting, displaySetting.Resolution), targetInfo.Rotation, targetInfo.Scaling)) .ToArray(); pathInfos[i] = new PathInfo( pathInfo.DisplaySource, pathInfo.Position, displaySetting.Resolution, pathInfo.PixelFormat, pathTargetInfos ); } } PathInfo.ApplyPathInfos(pathInfos); } public static DisplayAdvancedColorInfo GetAdvancedColorInfo(this Display display) { var pathDisplayAdapter = display.Adapter.ToPathDisplayAdapter(); var pathDisplayTarget = display.ToPathDisplayTarget(); if (pathDisplayTarget is null || pathDisplayAdapter is null) return default; var getAdvancedColorInfo = new DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO(); getAdvancedColorInfo.header.type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO; getAdvancedColorInfo.header.size = (uint)Marshal.SizeOf(typeof(DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO)); getAdvancedColorInfo.header.adapterId.HighPart = pathDisplayAdapter.AdapterId.HighPart; getAdvancedColorInfo.header.adapterId.LowPart = pathDisplayAdapter.AdapterId.LowPart; getAdvancedColorInfo.header.id = pathDisplayTarget.TargetId; if (PInvoke.DisplayConfigGetDeviceInfo(ref getAdvancedColorInfo.header) != 0) PInvokeExtensions.ThrowIfWin32Error("GetAdvancedColorInfo"); return new(getAdvancedColorInfo.Anonymous.value.GetNthBit(0), getAdvancedColorInfo.Anonymous.value.GetNthBit(1), getAdvancedColorInfo.Anonymous.value.GetNthBit(2), getAdvancedColorInfo.Anonymous.value.GetNthBit(3)); } public static void SetAdvancedColorState(this Display display, bool state) { var pathDisplayAdapter = display.Adapter.ToPathDisplayAdapter(); var pathDisplayTarget = display.ToPathDisplayTarget(); if (pathDisplayTarget is null || pathDisplayAdapter is null) return; var setAdvancedColorState = new DISPLAYCONFIG_SET_ADVANCED_COLOR_STATE(); setAdvancedColorState.header.type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE; setAdvancedColorState.header.size = (uint)Marshal.SizeOf(); setAdvancedColorState.header.adapterId.HighPart = pathDisplayAdapter.AdapterId.HighPart; setAdvancedColorState.header.adapterId.LowPart = pathDisplayAdapter.AdapterId.LowPart; setAdvancedColorState.header.id = pathDisplayTarget.TargetId; setAdvancedColorState.Anonymous.value = setAdvancedColorState.Anonymous.value.SetNthBit(0, state); if (PInvoke.DisplayConfigSetDeviceInfo(setAdvancedColorState.header) != 0) PInvokeExtensions.ThrowIfWin32Error("SetAdvancedColorState"); } } ================================================ FILE: LenovoLegionToolkit.Lib/Extensions/DisplayPossibleSettingExtensions.cs ================================================ using System; using WindowsDisplayAPI; namespace LenovoLegionToolkit.Lib.Extensions; public static class DisplayPossibleSettingExtensions { public static bool IsTooSmall(this DisplayPossibleSetting dps) => Math.Max(dps.Resolution.Width, dps.Resolution.Height) < 1000; } ================================================ FILE: LenovoLegionToolkit.Lib/Extensions/DisplaySettingExtensions.cs ================================================ using WindowsDisplayAPI; namespace LenovoLegionToolkit.Lib.Extensions; public static class DisplaySettingExtensions { public static string ToExtendedString(this DisplaySetting displaySetting) { return $"{displaySetting.Resolution.Width}x{displaySetting.Resolution.Height}{(displaySetting.IsInterlaced ? "i" : "p")} @ {displaySetting.Frequency}Hz @ {displaySetting.ColorDepth} ({displaySetting.Position}, {displaySetting.Orientation}, {displaySetting.OutputScalingMode})"; } } ================================================ FILE: LenovoLegionToolkit.Lib/Extensions/EnumExtensions.cs ================================================ using System; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Reflection; namespace LenovoLegionToolkit.Lib.Extensions; public static class EnumExtensions { public static string GetDisplayName(this Enum enumValue) { var displayAttribute = enumValue.GetType() .GetMember(enumValue.ToString()) .First() .GetCustomAttributes(false) .OfType() .FirstOrDefault(); if (displayAttribute?.Name is null) return enumValue.ToString(); if (displayAttribute.ResourceType?.GetProperty(displayAttribute.Name, BindingFlags.Static | BindingFlags.Public)?.GetValue(null) is string str) return str; return displayAttribute.Name; } public static string GetFlagsDisplayName(this Enum enumValue, Enum? excluding = null) { var values = Enum.GetValues(enumValue.GetType()).Cast(); if (excluding is not null) values = values.Where(v => !v.Equals(excluding)); var names = values.Where(enumValue.HasFlag).Select(GetDisplayName); return string.Join(", ", names); } } ================================================ FILE: LenovoLegionToolkit.Lib/Extensions/EnumerableExtensions.cs ================================================ using System; using System.Collections.Generic; using System.Linq; namespace LenovoLegionToolkit.Lib.Extensions; public static class EnumerableExtensions { public static bool IsEmpty(this IEnumerable source) { if (source is ICollection collection) return collection.Count == 0; return !source.Any(); } public static void ForEach(this IEnumerable enumeration, Action action) { foreach (var item in enumeration) action(item); } public static IEnumerable> Split(this IEnumerable source, int size) { var enumerable = source as T[] ?? source.ToArray(); return enumerable .Select((_, i) => enumerable.Skip(i * size).Take(size)) .Where(a => a.Any()); } } ================================================ FILE: LenovoLegionToolkit.Lib/Extensions/HttpClientExtensions.cs ================================================ using System; using System.IO; using System.Net.Http; using System.Threading; using System.Threading.Tasks; namespace LenovoLegionToolkit.Lib.Extensions; public static class HttpClientExtensions { public static async Task DownloadAsync(this HttpClient client, string requestUri, Stream destination, IProgress? progress = null, CancellationToken cancellationToken = default) { using var response = await client.GetAsync(requestUri, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); var contentLength = response.Content.Headers.ContentLength; await using var download = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); if (progress is null || !contentLength.HasValue) { await download.CopyToAsync(destination, cancellationToken).ConfigureAwait(false); return; } progress.Report(0); var relativeProgress = new Progress(totalBytes => progress.Report((float)totalBytes / contentLength.Value)); await download.CopyToAsync(destination, 81920, relativeProgress, cancellationToken).ConfigureAwait(false); progress.Report(1); } } ================================================ FILE: LenovoLegionToolkit.Lib/Extensions/IntExtensions.cs ================================================ namespace LenovoLegionToolkit.Lib.Extensions; public static class IntExtensions { public static bool IsBitSet(this int value, int position) => (value & (1 << position)) != 0; } ================================================ FILE: LenovoLegionToolkit.Lib/Extensions/ListExtensions.cs ================================================ using System.Collections; namespace LenovoLegionToolkit.Lib.Extensions; public static class ListExtensions { public static object[] ToArray(this IList source) { var array = new object[source.Count]; source.CopyTo(array, 0); return array; } } ================================================ FILE: LenovoLegionToolkit.Lib/Extensions/LogoInfoFormatExtensions.cs ================================================ using System.Collections.Generic; using System.Drawing.Imaging; namespace LenovoLegionToolkit.Lib.Extensions; public static class LogoInfoFormatExtensions { public static IEnumerable ImageFormats(this BootLogoFormat format) { if (format.HasFlag(BootLogoFormat.Bmp)) yield return ImageFormat.Bmp; if (format.HasFlag(BootLogoFormat.Jpeg)) yield return ImageFormat.Jpeg; if (format.HasFlag(BootLogoFormat.Png)) yield return ImageFormat.Png; } public static IEnumerable ExtensionFilters(this BootLogoFormat format) { if (format.HasFlag(BootLogoFormat.Bmp)) yield return "*.bmp"; if (format.HasFlag(BootLogoFormat.Png)) yield return "*.png"; if (format.HasFlag(BootLogoFormat.Jpeg)) { yield return "*.jpeg"; yield return "*.jpg"; } } } ================================================ FILE: LenovoLegionToolkit.Lib/Extensions/ManagementObjectSearcherExtensions.cs ================================================ using System.Collections.Generic; using System.Linq; using System.Management; using System.Threading.Tasks; namespace LenovoLegionToolkit.Lib.Extensions; public static class ManagementObjectSearcherExtensions { public static Task> GetAsync(this ManagementObjectSearcher mos) => Task.Run(() => mos.Get().Cast()); } ================================================ FILE: LenovoLegionToolkit.Lib/Extensions/MathExtensions.cs ================================================ using System; namespace LenovoLegionToolkit.Lib.Extensions; public static class MathExtensions { public static int RoundNearest(int value, int factor) { return (int)Math.Round(value / (double)factor, MidpointRounding.AwayFromZero) * factor; } } ================================================ FILE: LenovoLegionToolkit.Lib/Extensions/OSExtensions.cs ================================================ using System; namespace LenovoLegionToolkit.Lib.Extensions; public static class OSExtensions { public static OS GetCurrent() { var os = Environment.OSVersion; if (os.Version >= new Version(10, 0, 22000, 0)) // Windows 11 return OS.Windows11; else if (os.Version >= new Version(10, 0, 0, 0)) // Windows 10 return OS.Windows10; else if (os.Version >= new Version(6, 2, 0, 0)) // Windows 8 return OS.Windows8; else if (os.Version >= new Version(6, 1, 0, 0)) // Windows 7 return OS.Windows7; else return OS.Windows11; } } ================================================ FILE: LenovoLegionToolkit.Lib/Extensions/PInvokeExtensions.cs ================================================ using System; using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; using Windows.Win32; namespace LenovoLegionToolkit.Lib.Extensions; // ReSharper disable InconsistentNaming // ReSharper disable IdentifierTypo public static class PInvokeExtensions { public enum CONSOLE_DISPLAY_STATE { Off = 0, On = 1, Dimmed = 2 } public const int ERROR_SUCCESS = 0; public const int ERROR_NO_MORE_ITEMS = 259; public const uint KF_FLAG_DEFAULT = 0; public const uint VARIABLE_ATTRIBUTE_BOOTSERVICE_ACCESS = 2; public const uint VARIABLE_ATTRIBUTE_NON_VOLATILE = 1; public const uint VARIABLE_ATTRIBUTE_RUNTIME_ACCESS = 7; public static readonly Guid DISPLAY_BRIGTHNESS_SETTING_GUID = Guid.Parse("aded5e82-b909-4619-9949-f5d71dac0bcb"); public static unsafe bool DeviceIoControl(SafeFileHandle hDevice, uint dwIoControlCode, TIn inVal, out TOut outVal) where TIn : struct where TOut : struct { var lpInBuffer = IntPtr.Zero; var lpOutBuffer = IntPtr.Zero; try { var nInBufferSize = Marshal.SizeOf(); var nOutBufferSize = Marshal.SizeOf(); lpInBuffer = Marshal.AllocHGlobal(nInBufferSize); lpOutBuffer = Marshal.AllocHGlobal(nOutBufferSize); Marshal.StructureToPtr(inVal, lpInBuffer, false); var ret = PInvoke.DeviceIoControl(hDevice, dwIoControlCode, lpInBuffer.ToPointer(), (uint)nInBufferSize, lpOutBuffer.ToPointer(), (uint)nOutBufferSize, null, null); outVal = ret ? Marshal.PtrToStructure(lpOutBuffer) : default; return ret; } finally { Marshal.FreeHGlobal(lpInBuffer); Marshal.FreeHGlobal(lpOutBuffer); } } public static void ThrowIfWin32Error(string description) { var errorCode = Marshal.GetLastWin32Error(); ThrowIfWin32Error(errorCode, description); } public static void ThrowIfWin32Error(int errorCode, string description) { if (errorCode != 0) throw Marshal.GetExceptionForHR(errorCode) ?? throw new Exception($"Unknown Win32 error code {errorCode} in {description}"); throw new Exception($"{description} failed but Win32 didn't catch an error"); } } ================================================ FILE: LenovoLegionToolkit.Lib/Extensions/PhyscialGPUExtensions.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using NvAPIWrapper.GPU; using NvAPIWrapper.Native; namespace LenovoLegionToolkit.Lib.Extensions; public static class NVAPIExtensions { private static readonly string[] Exclusions = [ "dwm.exe", "explorer.exe", ]; public static List GetActiveProcesses(PhysicalGPU gpu) { var processes = new List(); var apps = GPUApi.QueryActiveApps(gpu.Handle).Where(app => !Exclusions.Contains(app.ProcessName, StringComparer.InvariantCultureIgnoreCase)); foreach (var app in apps) { try { var process = Process.GetProcessById(app.ProcessId); processes.Add(process); } catch (ArgumentException) { } } return processes; } } ================================================ FILE: LenovoLegionToolkit.Lib/Extensions/ProcessExtensions.cs ================================================ using System.Diagnostics; using Windows.Win32; namespace LenovoLegionToolkit.Lib.Extensions; public static class ProcessExtensions { public static string? GetFileName(this Process process, int maxLength = 512) { var chars = new char[maxLength]; return PInvoke.K32GetModuleFileNameEx(process.SafeHandle, null, chars) == 0 ? null : chars.ToString(); } } ================================================ FILE: LenovoLegionToolkit.Lib/Extensions/PropertyDataCollectionExtensions.cs ================================================ using System.Management; namespace LenovoLegionToolkit.Lib.Extensions; public static class PropertyDataCollectionExtensions { public static bool Contains(this PropertyDataCollection pdc, string name) { // ReSharper disable once NotDisposedResource var enumerator = pdc.GetEnumerator(); while (enumerator.MoveNext()) if (enumerator.Current.Name == name) return true; return false; } } ================================================ FILE: LenovoLegionToolkit.Lib/Extensions/RGBKeyboardBacklightPresetExtensions.cs ================================================ namespace LenovoLegionToolkit.Lib.Extensions; public static class RGBKeyboardBacklightPresetExtensions { public static RGBKeyboardBacklightPreset Next(this RGBKeyboardBacklightPreset preset) => preset switch { RGBKeyboardBacklightPreset.Off => RGBKeyboardBacklightPreset.One, RGBKeyboardBacklightPreset.One => RGBKeyboardBacklightPreset.Two, RGBKeyboardBacklightPreset.Two => RGBKeyboardBacklightPreset.Three, RGBKeyboardBacklightPreset.Three => RGBKeyboardBacklightPreset.Four, _ => RGBKeyboardBacklightPreset.Off, }; } ================================================ FILE: LenovoLegionToolkit.Lib/Extensions/RegistrationBuilderExtensions.cs ================================================ using System; using Autofac; using Autofac.Builder; using LenovoLegionToolkit.Lib.Listeners; namespace LenovoLegionToolkit.Lib.Extensions; public static class RegistrationBuilderExtensions { public static void AutoActivateListener(this IRegistrationBuilder, ConcreteReflectionActivatorData, SingleRegistrationStyle> registration) where T : EventArgs { registration.OnActivating(e => e.Instance.StartAsync().AsValueTask()).AutoActivate(); } } ================================================ FILE: LenovoLegionToolkit.Lib/Extensions/ServiceControllerExtension.cs ================================================ using System.Runtime.InteropServices; using System.ServiceProcess; using Windows.Win32; using Windows.Win32.System.Services; namespace LenovoLegionToolkit.Lib.Extensions; internal static class ServiceControllerExtension { public static unsafe void ChangeStartMode(this ServiceController svc, bool enabled) { using var scManagerHandle = PInvoke.OpenSCManager(null as string, null, PInvoke.SC_MANAGER_ALL_ACCESS); if (scManagerHandle.IsInvalid) throw new ExternalException("Open Service Manager Error"); using var serviceHandle = PInvoke.OpenService(scManagerHandle, svc.ServiceName, PInvoke.SERVICE_CHANGE_CONFIG); if (serviceHandle.IsInvalid) throw new ExternalException("Open Service Error"); var result = PInvoke.ChangeServiceConfig(serviceHandle, (ENUM_SERVICE_TYPE)PInvoke.SERVICE_NO_CHANGE, enabled ? SERVICE_START_TYPE.SERVICE_AUTO_START : SERVICE_START_TYPE.SERVICE_DISABLED, SERVICE_ERROR.SERVICE_ERROR_NORMAL, null, null, null, null, null, null, null); if (result) return; PInvokeExtensions.ThrowIfWin32Error($"Could not change service: {svc.ServiceName}"); } } ================================================ FILE: LenovoLegionToolkit.Lib/Extensions/SpectrumKeyboardBacklightEffectTypeExtensions.cs ================================================ namespace LenovoLegionToolkit.Lib.Extensions; public static class SpectrumKeyboardBacklightEffectTypeExtensions { public static bool IsAllLightsEffect(this SpectrumKeyboardBacklightEffectType type) => type switch { SpectrumKeyboardBacklightEffectType.AudioBounce => true, SpectrumKeyboardBacklightEffectType.AudioRipple => true, SpectrumKeyboardBacklightEffectType.AuroraSync => true, _ => false }; public static bool IsWholeKeyboardEffect(this SpectrumKeyboardBacklightEffectType type) => type switch { SpectrumKeyboardBacklightEffectType.Type => true, SpectrumKeyboardBacklightEffectType.Ripple => true, _ => false }; } ================================================ FILE: LenovoLegionToolkit.Lib/Extensions/StreamExtensions.cs ================================================ using System; using System.IO; using System.Threading; using System.Threading.Tasks; namespace LenovoLegionToolkit.Lib.Extensions; // ReSharper disable LocalizableElement public static class StreamExtensions { public static async Task CopyToAsync(this Stream source, Stream destination, int bufferSize, IProgress? progress = null, CancellationToken cancellationToken = default) { if (!source.CanRead) throw new ArgumentException("Has to be readable", nameof(source)); if (!destination.CanWrite) throw new ArgumentException("Has to be writable", nameof(destination)); var buffer = new byte[bufferSize]; long totalBytesRead = 0; int bytesRead; while ((bytesRead = await source.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) != 0) { await destination.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken).ConfigureAwait(false); totalBytesRead += bytesRead; progress?.Report(totalBytesRead); } } } ================================================ FILE: LenovoLegionToolkit.Lib/Extensions/StringExtensions.cs ================================================ using System; namespace LenovoLegionToolkit.Lib.Extensions; public static class StringExtensions { public static string GetUntilOrEmpty(this string text, string stopAt) { if (string.IsNullOrWhiteSpace(text)) return string.Empty; var charLocation = text.IndexOf(stopAt, StringComparison.Ordinal); if (charLocation > 0) return text[..charLocation]; return string.Empty; } } ================================================ FILE: LenovoLegionToolkit.Lib/Extensions/TaskExtensions.cs ================================================ using System.Threading.Tasks; namespace LenovoLegionToolkit.Lib.Extensions; public static class TaskExtensions { public static ValueTask AsValueTask(this Task task) => new(task); public static Task OrNullIfException(this Task task) where T : struct { return task.ContinueWith(t => t.IsCompletedSuccessfully ? (T?)t.Result : null); } } ================================================ FILE: LenovoLegionToolkit.Lib/Extensions/TimeExtensions.cs ================================================ using System; namespace LenovoLegionToolkit.Lib.Extensions; public static class TimeExtensions { public static Time UtcNow { get { var utcNow = DateTime.UtcNow; return new(utcNow.Hour, utcNow.Minute); } } } ================================================ FILE: LenovoLegionToolkit.Lib/Extensions/UintExtensions.cs ================================================ using System; namespace LenovoLegionToolkit.Lib.Extensions; public static class UintExtensions { public static uint ReverseEndianness(this uint state) { var bytes = BitConverter.GetBytes(state); Array.Reverse(bytes, 0, bytes.Length); return BitConverter.ToUInt32(bytes, 0); } public static bool GetNthBit(this uint num, int n) => (num & (1 << n)) != 0; public static uint SetNthBit(this uint num, int n, bool state) => state ? num | (1U << n) : num & ~(1U << n); } ================================================ FILE: LenovoLegionToolkit.Lib/Extensions/VersionExtensions.cs ================================================ using System; namespace LenovoLegionToolkit.Lib.Extensions; public static class VersionExtensions { public static bool IsBeta(this Version version) => version switch { { Major: 0, Minor: 0, Build: 1, Revision: 0 } => true, { Build: 99 } => true, _ => false }; } ================================================ FILE: LenovoLegionToolkit.Lib/Features/AbstractCapabilityFeature.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.System.Management; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.Features; public abstract class AbstractCapabilityFeature(CapabilityID capabilityID) : IFeature where T : struct, Enum, IComparable, IConvertible { public async Task IsSupportedAsync() { try { var mi = await Compatibility.GetMachineInformationAsync().ConfigureAwait(false); return mi.Features.Source == MachineInformation.FeatureData.SourceType.CapabilityData && mi.Features[capabilityID]; } catch { return false; } } public Task GetAllStatesAsync() => Task.FromResult(Enum.GetValues()); public async Task GetStateAsync() { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Getting state... [feature={GetType().Name}]"); var value = await WMI.LenovoOtherMethod.GetFeatureValueAsync(capabilityID).ConfigureAwait(false); var result = (T)Enum.ToObject(typeof(T), value); if (!Enum.IsDefined(result)) throw new InvalidOperationException($"Undefined value received: {result} [type={typeof(T)}, feature={GetType().Name}]"); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"State is {result} [feature={GetType().Name}]"); return result; } public async Task SetStateAsync(T state) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Setting state to {state}... [feature={GetType().Name}]"); await WMI.LenovoOtherMethod.SetFeatureValueAsync(capabilityID, Convert.ToInt32(state)).ConfigureAwait(false); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Set state to {state} [feature={GetType().Name}]"); } } ================================================ FILE: LenovoLegionToolkit.Lib/Features/AbstractCompositeFeature.cs ================================================ using System; using System.Threading.Tasks; using NeoSmart.AsyncLock; namespace LenovoLegionToolkit.Lib.Features; public abstract class AbstractCompositeFeature(params IFeature[] features) : IFeature where T : struct { private readonly AsyncLock _lock = new(); private bool _resolved; private IFeature? _feature; public async Task IsSupportedAsync() { var feature = await ResolveInternalAsync().ConfigureAwait(false); if (feature is null) return false; return await feature.IsSupportedAsync().ConfigureAwait(false); } public async Task GetAllStatesAsync() { var feature = await ResolveInternalAsync().ConfigureAwait(false) ?? throw new InvalidOperationException($"No supported feature found [type={GetType().Name}"); return await feature.GetAllStatesAsync().ConfigureAwait(false); } public async Task GetStateAsync() { var feature = await ResolveInternalAsync().ConfigureAwait(false) ?? throw new InvalidOperationException($"No supported feature found [type={GetType().Name}"); return await feature.GetStateAsync().ConfigureAwait(false); } public async Task SetStateAsync(T state) { var feature = await ResolveInternalAsync().ConfigureAwait(false) ?? throw new InvalidOperationException($"No supported feature found [type={GetType().Name}"); await feature.SetStateAsync(state).ConfigureAwait(false); } protected virtual async Task?> ResolveAsync() { foreach (var feature in features) { if (!await feature.IsSupportedAsync().ConfigureAwait(false)) continue; return feature; } return null; } private async Task?> ResolveInternalAsync() { using (await _lock.LockAsync().ConfigureAwait(false)) { if (_resolved) return _feature; _feature = await ResolveAsync().ConfigureAwait(false); _resolved = true; return _feature; } } } ================================================ FILE: LenovoLegionToolkit.Lib/Features/AbstractDriverFeature.cs ================================================ using System; using System.Runtime.InteropServices; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Utils; using Microsoft.Win32.SafeHandles; namespace LenovoLegionToolkit.Lib.Features; public abstract class AbstractDriverFeature(Func driverHandleHandle, uint controlCode) : IFeature where T : struct, Enum, IComparable { protected readonly uint ControlCode = controlCode; protected readonly Func DriverHandle = driverHandleHandle; protected T LastState; public virtual async Task IsSupportedAsync() { try { _ = await GetStateAsync().ConfigureAwait(false); return true; } catch { return false; } } public Task GetAllStatesAsync() => Task.FromResult(Enum.GetValues()); public virtual async Task GetStateAsync() { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Getting state... [feature={GetType().Name}]"); var outBuffer = await SendCodeAsync(DriverHandle(), ControlCode, GetInBufferValue()).ConfigureAwait(false); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Buffer value: {outBuffer} [feature={GetType().Name}]"); var state = await FromInternalAsync(outBuffer).ConfigureAwait(false); LastState = state; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"State is {state} [feature={GetType().Name}]"); return state; } public virtual async Task SetStateAsync(T state) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Setting state to {state}... [feature={GetType().Name}]"); var codes = await ToInternalAsync(state).ConfigureAwait(false); foreach (var code in codes) await SendCodeAsync(DriverHandle(), ControlCode, code).ConfigureAwait(false); LastState = state; await VerifyStateSetAsync(state).ConfigureAwait(false); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"State set to {state} [feature={GetType().Name}]"); } protected abstract Task FromInternalAsync(uint state); protected abstract uint GetInBufferValue(); protected abstract Task ToInternalAsync(T state); protected Task SendCodeAsync(SafeFileHandle handle, uint controlCode, uint inBuffer) => Task.Run(() => { if (PInvokeExtensions.DeviceIoControl(handle, controlCode, inBuffer, out uint outBuffer)) return outBuffer; var error = Marshal.GetLastWin32Error(); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"DeviceIoControl returned 0, last error: {error} [feature={GetType().Name}]"); throw new InvalidOperationException($"DeviceIoControl returned 0, last error: {error}"); }); private async Task VerifyStateSetAsync(T state) { var retries = 0; var verified = false; while (retries < 10) { if (state.Equals(await GetStateAsync().ConfigureAwait(false))) { verified = true; break; } retries++; await Task.Delay(50).ConfigureAwait(false); } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Verify state {state} set {(verified ? "succeeded" : "failed")}. [feature={GetType().Name}]"); } } ================================================ FILE: LenovoLegionToolkit.Lib/Features/AbstractLenovoLightingFeature.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.System.Management; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.Features; public abstract class AbstractLenovoLightingFeature(int lightingID, int controlInterface, int type) : IFeature where T : struct, Enum, IComparable { public bool ForceDisable { get; set; } public virtual async Task IsSupportedAsync() { if (ForceDisable) return false; try { var mi = await Compatibility.GetMachineInformationAsync().ConfigureAwait(false); if (mi.Properties.IsExcludedFromLenovoLighting) return false; var isSupported = await WMI.LenovoLightingData.ExistsAsync(lightingID, controlInterface, type).ConfigureAwait(false); if (!isSupported) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Control interface not found [feature={GetType().Name}]"); return false; } _ = await GetStateAsync().ConfigureAwait(false); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Supported [feature={GetType().Name}]"); return true; } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to check support [feature={GetType().Name}]", ex); return false; } } public Task GetAllStatesAsync() => Task.FromResult(Enum.GetValues()); public async Task GetStateAsync() { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Getting state... [feature={GetType().Name}]"); var (stateType, level) = await WMI.LenovoLightingMethod.GetLightingCurrentStatusAsync(lightingID).ConfigureAwait(false); var result = FromInternal(stateType, level); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"State is {result} [feature={GetType().Name}]"); return result; } public async Task SetStateAsync(T state) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Setting state to {state}... [feature={GetType().Name}]"); var (stateType, level) = ToInternal(state); await WMI.LenovoLightingMethod.SetLightingCurrentStatusAsync(lightingID, stateType, level).ConfigureAwait(false); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Set state to {state} [feature={GetType().Name}]"); } protected abstract T FromInternal(int stateType, int level); protected abstract (int stateType, int level) ToInternal(T state); } ================================================ FILE: LenovoLegionToolkit.Lib/Features/AbstractUEFIFeature.cs ================================================ using System; using System.Runtime.InteropServices; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Utils; using Windows.Win32; namespace LenovoLegionToolkit.Lib.Features; public abstract class AbstractUEFIFeature(string guid, string scopeName, uint scopeAttribute) : IFeature where T : struct, Enum, IComparable { public async Task IsSupportedAsync() { try { _ = await GetStateAsync().ConfigureAwait(false); return true; } catch { return false; } } public Task GetAllStatesAsync() => Task.FromResult(Enum.GetValues()); public abstract Task GetStateAsync(); public abstract Task SetStateAsync(T state); protected unsafe Task ReadFromUefiAsync() where TS : struct => Task.Run(() => { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Reading from UEFI... [feature={GetType().Name}]"); var ptr = Marshal.AllocHGlobal(Marshal.SizeOf()); try { if (!TokenManipulator.AddPrivileges(TokenManipulator.SE_SYSTEM_ENVIRONMENT_PRIVILEGE)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Cannot set UEFI privileges [feature={GetType().Name}]"); throw new InvalidOperationException("Cannot set privileges UEFI"); } var ptrSize = (uint)Marshal.SizeOf(); if (PInvoke.GetFirmwareEnvironmentVariableEx(scopeName, guid, ptr.ToPointer(), ptrSize, null) != 0) { var result = Marshal.PtrToStructure(ptr); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Read from UEFI successful [feature={GetType().Name}]"); return result; } else { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Cannot read variable {scopeName} from UEFI [feature={GetType().Name}]"); throw new InvalidOperationException($"Cannot read variable {scopeName} from UEFI"); } } finally { Marshal.FreeHGlobal(ptr); TokenManipulator.RemovePrivileges(TokenManipulator.SE_SYSTEM_ENVIRONMENT_PRIVILEGE); } }); protected unsafe Task WriteToUefiAsync(TS structure) where TS : struct => Task.Run(() => { var ptr = Marshal.AllocHGlobal(Marshal.SizeOf()); try { if (!TokenManipulator.AddPrivileges(TokenManipulator.SE_SYSTEM_ENVIRONMENT_PRIVILEGE)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Cannot set UEFI privileges [feature={GetType().Name}]"); throw new InvalidOperationException("Cannot set UEFI privileges"); } Marshal.StructureToPtr(structure, ptr, false); var ptrSize = (uint)Marshal.SizeOf(); if (!PInvoke.SetFirmwareEnvironmentVariableEx(scopeName, guid, ptr.ToPointer(), ptrSize, scopeAttribute)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Cannot write variable {scopeName} to UEFI [feature={GetType().Name}]"); throw new InvalidOperationException($"Cannot write variable {scopeName} to UEFI"); } else { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"WriteAsync to UEFI successful [feature={GetType().Name}]"); } } finally { Marshal.FreeHGlobal(ptr); TokenManipulator.RemovePrivileges(TokenManipulator.SE_SYSTEM_ENVIRONMENT_PRIVILEGE); } }); } ================================================ FILE: LenovoLegionToolkit.Lib/Features/AbstractWmiFeature.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.Features; public abstract class AbstractWmiFeature(Func> getValue, Func setValue, Func>? isSupported = null, int offset = 0) : IFeature where T : struct, Enum, IComparable { public async Task IsSupportedAsync() { try { if (isSupported is null) return true; return await isSupported().ConfigureAwait(false) > 0; } catch { return false; } } public virtual Task GetAllStatesAsync() => Task.FromResult(Enum.GetValues()); public async Task GetStateAsync() { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Getting state... [feature={GetType().Name}]"); var internalResult = await getValue().ConfigureAwait(false); var result = FromInternal(internalResult); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"State is {result} [feature={GetType().Name}]"); return result; } public virtual async Task SetStateAsync(T state) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Setting state to {state}... [feature={GetType().Name}]"); await setValue(ToInternal(state)).ConfigureAwait(false); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Set state to {state} [feature={GetType().Name}]"); } private int ToInternal(T state) => (int)(object)state + offset; private T FromInternal(int state) => (T)(object)(state - offset); } ================================================ FILE: LenovoLegionToolkit.Lib/Features/AlwaysOnUsbFeature.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.System; namespace LenovoLegionToolkit.Lib.Features; public class AlwaysOnUSBFeature() : AbstractDriverFeature(Drivers.GetEnergy, Drivers.IOCTL_ENERGY_SETTINGS) { protected override uint GetInBufferValue() => 0x2; protected override Task ToInternalAsync(AlwaysOnUSBState state) { var result = state switch { AlwaysOnUSBState.Off => new uint[] { 0xB, 0x12 }, AlwaysOnUSBState.OnWhenSleeping => [0xA, 0x12], AlwaysOnUSBState.OnAlways => [0xA, 0x13], _ => throw new InvalidOperationException("Invalid state"), }; return Task.FromResult(result); } protected override Task FromInternalAsync(uint state) { state = state.ReverseEndianness(); if (state.GetNthBit(31)) // is on? { if (state.GetNthBit(23)) return Task.FromResult(AlwaysOnUSBState.OnAlways); return Task.FromResult(AlwaysOnUSBState.OnWhenSleeping); } return Task.FromResult(AlwaysOnUSBState.Off); } } ================================================ FILE: LenovoLegionToolkit.Lib/Features/BatteryFeature.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.System; namespace LenovoLegionToolkit.Lib.Features; public class BatteryFeature() : AbstractDriverFeature(Drivers.GetEnergy, Drivers.IOCTL_ENERGY_BATTERY_CHARGE_MODE) { private const string BATTERY_CHARGE_MODE_HIVE = "HKEY_CURRENT_USER"; private const string BATTERY_CHARGE_MODE_PATH = "Software\\Lenovo\\VantageService\\AddinData\\IdeaNotebookAddin"; private const string BATTERY_CHARGE_MODE_KEY = "BatteryChargeMode"; private const string BATTERY_CHARGE_MODE_NORMAL = "Normal"; private const string BATTERY_CHARGE_MODE_RAPID_CHARGE = "Quick"; private const string BATTERY_CHARGE_MODE_CONSERVATION = "Storage"; protected override uint GetInBufferValue() => 0xFF; protected override Task ToInternalAsync(BatteryState state) { var result = state switch { BatteryState.Conservation => LastState == BatteryState.RapidCharge ? new uint[] { 0x8, 0x3 } : [0x3], BatteryState.Normal => LastState == BatteryState.Conservation ? [0x5] : [0x8], BatteryState.RapidCharge => LastState == BatteryState.Conservation ? [0x5, 0x7] : [0x7], _ => throw new InvalidOperationException("Invalid state") }; return Task.FromResult(result); } protected override Task FromInternalAsync(uint state) { state = state.ReverseEndianness(); if (state.GetNthBit(17)) // Is charging? return Task.FromResult(state.GetNthBit(26) ? BatteryState.RapidCharge : BatteryState.Normal); if (state.GetNthBit(29)) return Task.FromResult(BatteryState.Conservation); throw new InvalidOperationException($"Unknown battery state: {state} [bits={Convert.ToString(state, 2)}]"); } public override async Task SetStateAsync(BatteryState state) { await base.SetStateAsync(state).ConfigureAwait(false); SetStateInRegistry(state); } public async Task EnsureCorrectBatteryModeIsSetAsync() { var state = GetStateFromRegistry(); if (!state.HasValue) return; if (await GetStateAsync().ConfigureAwait(false) == state.Value) return; await SetStateAsync(state.Value).ConfigureAwait(false); } private static BatteryState? GetStateFromRegistry() { var batteryModeString = Registry.GetValue(BATTERY_CHARGE_MODE_HIVE, BATTERY_CHARGE_MODE_PATH, BATTERY_CHARGE_MODE_KEY, string.Empty); return batteryModeString switch { BATTERY_CHARGE_MODE_NORMAL => BatteryState.Normal, BATTERY_CHARGE_MODE_RAPID_CHARGE => BatteryState.RapidCharge, BATTERY_CHARGE_MODE_CONSERVATION => BatteryState.Conservation, _ => null }; } private static void SetStateInRegistry(BatteryState state) { var batteryModeString = state switch { BatteryState.Normal => BATTERY_CHARGE_MODE_NORMAL, BatteryState.RapidCharge => BATTERY_CHARGE_MODE_RAPID_CHARGE, BatteryState.Conservation => BATTERY_CHARGE_MODE_CONSERVATION, _ => null }; if (batteryModeString is null) return; Registry.SetValue(BATTERY_CHARGE_MODE_HIVE, BATTERY_CHARGE_MODE_PATH, BATTERY_CHARGE_MODE_KEY, batteryModeString); } } ================================================ FILE: LenovoLegionToolkit.Lib/Features/BatteryNightChargeFeature.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.System; namespace LenovoLegionToolkit.Lib.Features; public class BatteryNightChargeFeature() : AbstractDriverFeature(Drivers.GetEnergy, Drivers.IOCTL_ENERGY_BATTERY_NIGHT_CHARGE) { protected override uint GetInBufferValue() => 0x11; protected override Task ToInternalAsync(BatteryNightChargeState state) { uint[] result = state switch { BatteryNightChargeState.On => [0x80000012u], BatteryNightChargeState.Off => [0x12u], _ => throw new InvalidOperationException("Invalid state") }; return Task.FromResult(result); } protected override Task FromInternalAsync(uint state) { if (state.GetNthBit(0)) return Task.FromResult(state.GetNthBit(4) ? BatteryNightChargeState.On : BatteryNightChargeState.Off); throw new InvalidOperationException($"Unknown battery night charge state: {state} [bits={Convert.ToString(state, 2)}]"); } } ================================================ FILE: LenovoLegionToolkit.Lib/Features/DpiScaleFeature.cs ================================================ using System; using System.Linq; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.System; using LenovoLegionToolkit.Lib.Utils; using WindowsDisplayAPI.Native.DisplayConfig; namespace LenovoLegionToolkit.Lib.Features; public class DpiScaleFeature : IFeature { public Task IsSupportedAsync() => Task.FromResult(true); public Task GetAllStatesAsync() { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Getting all DPI scales..."); var display = InternalDisplay.Get(); var pds = display?.ToPathDisplaySource(); if (pds is null) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Built in display not found"); return Task.FromResult(Array.Empty()); } var max = (int)pds.MaximumDPIScale; var result = Enum.GetValues() .Select(s => (int)s) .Where(s => s <= max) .OrderBy(s => s) .Select(s => new DpiScale(s)) .ToArray(); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Current DPI scale is {result}"); return Task.FromResult(result); } public Task GetStateAsync() { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Getting current DPI scale..."); var display = InternalDisplay.Get(); var pds = display?.ToPathDisplaySource(); if (pds is null) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Built in display not found"); return Task.FromResult(default(DpiScale)); } var result = (int)pds.CurrentDPIScale; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Current DPI scale is {result}"); return Task.FromResult(new DpiScale(result)); } public Task SetStateAsync(DpiScale state) { var display = InternalDisplay.Get(); var pds = display?.ToPathDisplaySource(); if (pds is null) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Built in display not found"); throw new InvalidOperationException("Built in display not found"); } if ((int)pds.CurrentDPIScale == state.Scale) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"DPI scale already set to {state.Scale}"); return Task.CompletedTask; } if (!Enum.IsDefined(typeof(DisplayConfigSourceDPIScale), (uint)state.Scale)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"DPI scale {state.Scale} not found"); return Task.CompletedTask; } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Setting DPI scale to {state.Scale}"); pds.CurrentDPIScale = (DisplayConfigSourceDPIScale)state.Scale; return Task.CompletedTask; } } ================================================ FILE: LenovoLegionToolkit.Lib/Features/FlipToStart/FlipToStartCapabilityFeature.cs ================================================ namespace LenovoLegionToolkit.Lib.Features.FlipToStart; public class FlipToStartCapabilityFeature() : AbstractCapabilityFeature(CapabilityID.FlipToStart); ================================================ FILE: LenovoLegionToolkit.Lib/Features/FlipToStart/FlipToStartFeature.cs ================================================ namespace LenovoLegionToolkit.Lib.Features.FlipToStart; public class FlipToStartFeature(FlipToStartCapabilityFeature feature1, FlipToStartUEFIFeature feature2) : AbstractCompositeFeature(feature1, feature2); ================================================ FILE: LenovoLegionToolkit.Lib/Features/FlipToStart/FlipToStartUEFIFeature.cs ================================================ using System.Runtime.InteropServices; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Extensions; namespace LenovoLegionToolkit.Lib.Features.FlipToStart; public class FlipToStartUEFIFeature() : AbstractUEFIFeature( "{D743491E-F484-4952-A87D-8D5DD189B70C}", "FBSWIF", PInvokeExtensions.VARIABLE_ATTRIBUTE_NON_VOLATILE | PInvokeExtensions.VARIABLE_ATTRIBUTE_BOOTSERVICE_ACCESS | PInvokeExtensions.VARIABLE_ATTRIBUTE_RUNTIME_ACCESS) { [StructLayout(LayoutKind.Sequential, Pack = 1)] private struct FlipToBootStruct { [MarshalAs(UnmanagedType.U1)] public byte FlipToBootEn; [MarshalAs(UnmanagedType.U1)] public byte Reserved1; [MarshalAs(UnmanagedType.U1)] public byte Reserved2; [MarshalAs(UnmanagedType.U1)] public byte Reserved3; } // ReSharper disable once StringLiteralTypo public override async Task GetStateAsync() { var result = await ReadFromUefiAsync().ConfigureAwait(false); return result.FlipToBootEn == 0 ? FlipToStartState.Off : FlipToStartState.On; } public override async Task SetStateAsync(FlipToStartState state) { var structure = new FlipToBootStruct { FlipToBootEn = state == FlipToStartState.On ? (byte)1 : (byte)0, Reserved1 = 0, Reserved2 = 0, Reserved3 = 0 }; await WriteToUefiAsync(structure).ConfigureAwait(false); } } ================================================ FILE: LenovoLegionToolkit.Lib/Features/FnLockFeature.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.System; namespace LenovoLegionToolkit.Lib.Features; public class FnLockFeature() : AbstractDriverFeature(Drivers.GetEnergy, Drivers.IOCTL_ENERGY_SETTINGS) { protected override uint GetInBufferValue() => 0x2; protected override Task ToInternalAsync(FnLockState state) { var lockOn = state switch { FnLockState.On => true, FnLockState.Off => false, _ => throw new InvalidOperationException("Invalid state"), }; var value = lockOn ? new uint[] { 0xE } : [0xF]; return Task.FromResult(value); } protected override Task FromInternalAsync(uint state) { var value = state.GetNthBit(10) ? FnLockState.On : FnLockState.Off; return Task.FromResult(value); } } ================================================ FILE: LenovoLegionToolkit.Lib/Features/HDRFeature.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.System; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.Features; public class HDRFeature : IFeature { public Task IsSupportedAsync() { try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Checking HDR support..."); var display = InternalDisplay.Get(); if (display is null) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Built in display not found"); return Task.FromResult(false); } var isSupported = display.GetAdvancedColorInfo().AdvancedColorSupported; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"HDR support: {isSupported}"); return Task.FromResult(isSupported); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to check HDR support", ex); return Task.FromResult(false); } } public Task IsHdrBlockedAsync() { var display = InternalDisplay.Get() ?? throw new InvalidOperationException("Built in display not found"); var result = display.GetAdvancedColorInfo().AdvancedColorForceDisabled; return Task.FromResult(result); } public Task GetAllStatesAsync() => Task.FromResult(Enum.GetValues()); public Task GetStateAsync() { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Getting current HDR state..."); var display = InternalDisplay.Get() ?? throw new InvalidOperationException("Built in display not found"); var result = display.GetAdvancedColorInfo().AdvancedColorEnabled ? HDRState.On : HDRState.Off; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"HDR is {result}"); return Task.FromResult(result); } public async Task SetStateAsync(HDRState state) { var currentState = await GetStateAsync().ConfigureAwait(false); if (currentState == state) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"HDR already set to {state}"); return; } var display = InternalDisplay.Get() ?? throw new InvalidOperationException("Built in display not found"); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Setting display HDR to {state}"); display.SetAdvancedColorState(state == HDRState.On); } } ================================================ FILE: LenovoLegionToolkit.Lib/Features/Hybrid/GSyncFeature.cs ================================================ using LenovoLegionToolkit.Lib.System.Management; namespace LenovoLegionToolkit.Lib.Features.Hybrid; public class GSyncFeature() : AbstractWmiFeature(WMI.LenovoGameZoneData.GetGSyncStatusAsync, WMI.LenovoGameZoneData.SetGSyncStatusAsync, WMI.LenovoGameZoneData.IsSupportGSyncAsync); ================================================ FILE: LenovoLegionToolkit.Lib/Features/Hybrid/HybridModeFeature.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Features.Hybrid.Notify; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.Features.Hybrid; public class HybridModeFeature(GSyncFeature gSyncFeature, IGPUModeFeature igpuModeFeature, DGPUNotify dgpuNotify) : IFeature { private readonly CancellationTokenSource _ensureDGPUEjectedIfNeededCancellationTokenSource = new(); public async Task IsSupportedAsync() { var mi = await Compatibility.GetMachineInformationAsync().ConfigureAwait(false); return mi.Properties.SupportsGSync || mi.Properties.SupportsIGPUMode; } public async Task GetAllStatesAsync() { var mi = await Compatibility.GetMachineInformationAsync().ConfigureAwait(false); return (mi.Properties.SupportsGSync, mi.Properties.SupportsIGPUMode) switch { (true, true) => [HybridModeState.On, HybridModeState.OnIGPUOnly, HybridModeState.OnAuto, HybridModeState.Off], (false, true) => [HybridModeState.On, HybridModeState.OnIGPUOnly, HybridModeState.OnAuto], (true, false) => [HybridModeState.On, HybridModeState.Off], _ => [] }; } public async Task GetStateAsync() { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Getting state..."); var gSyncSupported = await gSyncFeature.IsSupportedAsync().ConfigureAwait(false); var igpuModeSupported = await igpuModeFeature.IsSupportedAsync().ConfigureAwait(false); var gSync = GSyncState.Off; var igpuMode = IGPUModeState.Default; if (gSyncSupported) gSync = await gSyncFeature.GetStateAsync().ConfigureAwait(false); if (igpuModeSupported) igpuMode = await igpuModeFeature.GetStateAsync().ConfigureAwait(false); var state = Pack(gSync, igpuMode); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"State is {state} [gSync={gSync}, igpuMode={igpuMode}]"); return state; } public async Task SetStateAsync(HybridModeState state) { await _ensureDGPUEjectedIfNeededCancellationTokenSource.CancelAsync().ConfigureAwait(false); var (gSync, igpuMode) = Unpack(state); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Setting state to {state}... [gSync={gSync}, igpuMode={igpuMode}]"); var gSyncSupported = await gSyncFeature.IsSupportedAsync().ConfigureAwait(false); var igpuModeSupported = await igpuModeFeature.IsSupportedAsync().ConfigureAwait(false); var gSyncChanged = false; if (gSyncSupported && await gSyncFeature.GetStateAsync().ConfigureAwait(false) != gSync) { await gSyncFeature.SetStateAsync(gSync).ConfigureAwait(false); gSyncChanged = true; } if (igpuModeSupported && await igpuModeFeature.GetStateAsync().ConfigureAwait(false) != igpuMode) { try { await igpuModeFeature.SetStateAsync(igpuMode).ConfigureAwait(false); } catch (IGPUModeChangeException) { if (!gSyncChanged) throw; } finally { if (!gSyncChanged && igpuMode is IGPUModeState.Default or IGPUModeState.Auto) await dgpuNotify.NotifyLaterIfNeededAsync().ConfigureAwait(false); } } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"State set to {state} [gSync={gSync}, igpuMode={igpuMode}]"); } public async Task EnsureDGPUEjectedIfNeededAsync() { if (!await igpuModeFeature.IsSupportedAsync().ConfigureAwait(false) || !await dgpuNotify.IsSupportedAsync().ConfigureAwait(false)) return; _ = Task.Run(async () => { try { const int maxRetries = 5; const int delay = 5 * 1000; var retry = 1; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Will make sure that dGPU is ejected. [maxRetries={maxRetries}, delay={delay}ms]"); while (retry <= maxRetries) { await Task.Delay(delay).ConfigureAwait(false); if (_ensureDGPUEjectedIfNeededCancellationTokenSource.IsCancellationRequested) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Cancelled, aborting..."); break; } if (await igpuModeFeature.GetStateAsync().ConfigureAwait(false) != IGPUModeState.IGPUOnly) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Not in iGPU-only mode, aborting..."); break; } if (!await dgpuNotify.IsDGPUAvailableAsync().ConfigureAwait(false)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"dGPU already unavailable, aborting..."); break; } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Notifying dGPU... [retry={retry}, maxRetries={maxRetries}]"); await dgpuNotify.NotifyAsync(false).ConfigureAwait(false); retry++; } } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to ensure dGPU is ejected", ex); } }); } private static (GSyncState, IGPUModeState) Unpack(HybridModeState state) => state switch { HybridModeState.On => (GSyncState.Off, IGPUModeState.Default), HybridModeState.OnIGPUOnly => (GSyncState.Off, IGPUModeState.IGPUOnly), HybridModeState.OnAuto => (GSyncState.Off, IGPUModeState.Auto), HybridModeState.Off => (GSyncState.On, IGPUModeState.Default), _ => throw new InvalidOperationException("Invalid state"), }; private static HybridModeState Pack(GSyncState state1, IGPUModeState state2) => (state1, state2) switch { (GSyncState.Off, IGPUModeState.Default) => HybridModeState.On, (GSyncState.Off, IGPUModeState.IGPUOnly) => HybridModeState.OnIGPUOnly, (GSyncState.Off, IGPUModeState.Auto) => HybridModeState.OnAuto, (GSyncState.On, _) => HybridModeState.Off, _ => throw new InvalidOperationException("Invalid state"), }; } ================================================ FILE: LenovoLegionToolkit.Lib/Features/Hybrid/IGPUModeCapabilityFeature.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.System.Management; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.Features.Hybrid; public class IGPUModeCapabilityFeature : IFeature { public async Task IsSupportedAsync() { try { var mi = await Compatibility.GetMachineInformationAsync().ConfigureAwait(false); return mi is { Features.Source: MachineInformation.FeatureData.SourceType.CapabilityData, Properties.SupportsIGPUMode: true }; } catch { return false; } } public Task GetAllStatesAsync() => Task.FromResult(Enum.GetValues()); public async Task GetStateAsync() { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Getting state..."); var value = await WMI.LenovoOtherMethod.GetFeatureValueAsync(CapabilityID.IGPUMode).ConfigureAwait(false); var result = (IGPUModeState)value; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"State is {result}"); return result; } public async Task SetStateAsync(IGPUModeState state) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Setting state to {state}..."); await WMI.LenovoOtherMethod.SetFeatureValueAsync(CapabilityID.IGPUMode, (int)state).ConfigureAwait(false); if (await WMI.LenovoOtherMethod.GetFeatureValueAsync(CapabilityID.IGPUModeChangeStatus).ConfigureAwait(false) == 0) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Set state to {state}, but dGPU check failed."); throw new IGPUModeChangeException(state); } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Set state to {state}"); } } ================================================ FILE: LenovoLegionToolkit.Lib/Features/Hybrid/IGPUModeChangeException.cs ================================================ using System; namespace LenovoLegionToolkit.Lib.Features.Hybrid; public class IGPUModeChangeException(IGPUModeState igpuMode) : Exception { public IGPUModeState IGPUMode { get; } = igpuMode; } ================================================ FILE: LenovoLegionToolkit.Lib/Features/Hybrid/IGPUModeFeature.cs ================================================ using System.Threading.Tasks; namespace LenovoLegionToolkit.Lib.Features.Hybrid; public class IGPUModeFeature(IGPUModeGamezoneFeature feature1, IGPUModeCapabilityFeature feature2, IGPUModeFeatureFlagsFeature feature3) : AbstractCompositeFeature(feature1, feature2, feature3) { public bool ExperimentalGPUWorkingMode { get; set; } protected override async Task?> ResolveAsync() { if (!ExperimentalGPUWorkingMode) return await feature1.IsSupportedAsync().ConfigureAwait(false) ? feature1 : null; if (await feature2.IsSupportedAsync().ConfigureAwait(false)) return feature2; if (await feature3.IsSupportedAsync().ConfigureAwait(false)) return feature3; return null; } } ================================================ FILE: LenovoLegionToolkit.Lib/Features/Hybrid/IGPUModeFeatureFlagsFeature.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.System.Management; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.Features.Hybrid; public class IGPUModeFeatureFlagsFeature : IFeature { public async Task IsSupportedAsync() { try { var mi = await Compatibility.GetMachineInformationAsync().ConfigureAwait(false); return mi is { Features.Source: MachineInformation.FeatureData.SourceType.Flags, Properties.SupportsIGPUMode: true }; } catch { return false; } } public Task GetAllStatesAsync() => Task.FromResult(Enum.GetValues()); public async Task GetStateAsync() { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Getting state..."); var flags = await WMI.LenovoOtherMethod.GetDeviceCurrentSupportFeatureAsync().ConfigureAwait(false); var result = IGPUModeState.Default; if (flags.IsBitSet(0)) result = IGPUModeState.IGPUOnly; if (flags.IsBitSet(1)) result = IGPUModeState.Auto; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"State is {result}"); return result; } public async Task SetStateAsync(IGPUModeState state) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Setting state to {state}..."); var result = await WMI.LenovoOtherMethod.SetDeviceCurrentSupportFeatureAsync(1, (int)state).ConfigureAwait(false); if (result == 0) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Set state to {state}, but dGPU check failed."); throw new IGPUModeChangeException(state); } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Set state to {state}"); } } ================================================ FILE: LenovoLegionToolkit.Lib/Features/Hybrid/IGPUModeGamezoneFeature.cs ================================================ using LenovoLegionToolkit.Lib.System.Management; namespace LenovoLegionToolkit.Lib.Features.Hybrid; public class IGPUModeGamezoneFeature() : AbstractWmiFeature(WMI.LenovoGameZoneData.GetIGPUModeStatusAsync, WMI.LenovoGameZoneData.SetIGPUModeStatusAsync, WMI.LenovoGameZoneData.IsSupportIGPUModeAsync); ================================================ FILE: LenovoLegionToolkit.Lib/Features/Hybrid/Notify/AbstractDGPUNotify.cs ================================================ using System; using System.Runtime.InteropServices; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Utils; using Windows.Win32; using Windows.Win32.Devices.DeviceAndDriverInstallation; using Windows.Win32.Foundation; namespace LenovoLegionToolkit.Lib.Features.Hybrid.Notify; public abstract partial class AbstractDGPUNotify : IDGPUNotify { [GeneratedRegex("pci#ven_([0-9A-Fa-f]{4})|dev_([0-9A-Fa-f]{4})")] private static partial Regex HardwareIdRegex(); private readonly object _lock = new(); private CancellationTokenSource? _notifyLaterCancellationTokenSource; public event EventHandler? Notified; public abstract Task IsSupportedAsync(); public async Task IsDGPUAvailableAsync() { try { var dgpuHardwareId = await GetDGPUHardwareIdAsync().ConfigureAwait(false); var isAvailable = IsDGPUAvailable(dgpuHardwareId); return isAvailable; } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to notify.", ex); return false; } } public async Task NotifyAsync(bool publish = true) { lock (_lock) { _notifyLaterCancellationTokenSource?.Cancel(); _notifyLaterCancellationTokenSource = null; } try { var dgpuHardwareId = await GetDGPUHardwareIdAsync().ConfigureAwait(false); var isAvailable = IsDGPUAvailable(dgpuHardwareId); await NotifyDGPUStatusAsync(isAvailable).ConfigureAwait(false); if (publish) Notified?.Invoke(this, isAvailable); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Notified: {isAvailable}"); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to notify.", ex); } } public Task NotifyLaterIfNeededAsync() { CancellationToken token; lock (_lock) { _notifyLaterCancellationTokenSource?.Cancel(); _notifyLaterCancellationTokenSource = new(); token = _notifyLaterCancellationTokenSource.Token; } _ = Task.Delay(TimeSpan.FromSeconds(5), token) .ContinueWith(async t => { if (!t.IsCompletedSuccessfully) return; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Event not received, notifying anyway..."); await NotifyAsync(false).ConfigureAwait(false); }, token); return Task.CompletedTask; } protected abstract Task NotifyDGPUStatusAsync(bool state); protected abstract Task GetDGPUHardwareIdAsync(); private unsafe bool IsDGPUAvailable(HardwareId dgpuHardwareId) { if (dgpuHardwareId == HardwareId.Empty) return false; var guidDisplayDeviceArrival = PInvoke.GUID_DISPLAY_DEVICE_ARRIVAL; var deviceHandle = PInvoke.SetupDiGetClassDevs(guidDisplayDeviceArrival, null, HWND.Null, SETUP_DI_GET_CLASS_DEVS_FLAGS.DIGCF_DEVICEINTERFACE | SETUP_DI_GET_CLASS_DEVS_FLAGS.DIGCF_PRESENT | SETUP_DI_GET_CLASS_DEVS_FLAGS.DIGCF_PROFILE); uint index = 0; while (true) { var currentIndex = index; index++; var deviceInfoData = new SP_DEVINFO_DATA { cbSize = (uint)Marshal.SizeOf() }; var result1 = PInvoke.SetupDiEnumDeviceInfo(deviceHandle, currentIndex, ref deviceInfoData); if (!result1) { if (Marshal.GetLastWin32Error() == PInvokeExtensions.ERROR_NO_MORE_ITEMS) break; PInvokeExtensions.ThrowIfWin32Error("SetupDiEnumDeviceInfo"); } var deviceInterfaceData = new SP_DEVICE_INTERFACE_DATA { cbSize = (uint)Marshal.SizeOf() }; var result2 = PInvoke.SetupDiEnumDeviceInterfaces(deviceHandle, null, guidDisplayDeviceArrival, currentIndex, ref deviceInterfaceData); if (!result2) PInvokeExtensions.ThrowIfWin32Error("SetupDiEnumDeviceInterfaces"); var requiredSize = 0u; _ = PInvoke.SetupDiGetDeviceInterfaceDetail(deviceHandle, deviceInterfaceData, null, 0, &requiredSize, null); string devicePath; var output = IntPtr.Zero; try { output = Marshal.AllocHGlobal((int)requiredSize); var deviceDetailData = (SP_DEVICE_INTERFACE_DETAIL_DATA_W*)output.ToPointer(); deviceDetailData->cbSize = (uint)Marshal.SizeOf(); var result3 = PInvoke.SetupDiGetDeviceInterfaceDetail(deviceHandle, deviceInterfaceData, deviceDetailData, requiredSize, null, null); if (!result3) PInvokeExtensions.ThrowIfWin32Error("SetupDiGetDeviceInterfaceDetail"); fixed (char* e0Ptr = &deviceDetailData->DevicePath.e0) devicePath = new string(e0Ptr); } finally { Marshal.FreeHGlobal(output); } if (!devicePath.Contains(guidDisplayDeviceArrival.ToString())) continue; if (dgpuHardwareId != HardwareIdFromDevicePath(devicePath)) continue; if (PInvoke.CM_Get_DevNode_Status(out var status, out _, deviceInfoData.DevInst, 0) != 0) continue; if (status.HasFlag(CM_DEVNODE_STATUS_FLAGS.DN_HAS_PROBLEM)) continue; return true; } return false; } private static HardwareId HardwareIdFromDevicePath(string devicePath) { try { var matches = HardwareIdRegex().Matches(devicePath); if (matches.Count != 2) return default; var vendor = matches[0].Groups[1].Value; var device = matches[1].Groups[2].Value; return new(vendor, device); } catch { return default; } } } ================================================ FILE: LenovoLegionToolkit.Lib/Features/Hybrid/Notify/DGPUCapabilityNotify.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.System.Management; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.Features.Hybrid.Notify; public class DGPUCapabilityNotify : AbstractDGPUNotify { public override async Task IsSupportedAsync() { try { var mi = await Compatibility.GetMachineInformationAsync().ConfigureAwait(false); return mi is { Features.Source: MachineInformation.FeatureData.SourceType.CapabilityData, Properties.SupportsIGPUMode: true }; } catch { return false; } } protected override Task NotifyDGPUStatusAsync(bool state) => WMI.LenovoOtherMethod.SetFeatureValueAsync(CapabilityID.GPUStatus, state ? 1 : 0); protected override async Task GetDGPUHardwareIdAsync() { try { var value = await WMI.LenovoOtherMethod.GetFeatureValueAsync(CapabilityID.GPUDidVid).ConfigureAwait(false); var vendorId = value & 0xFFFF; var deviceId = value >> 16; return new HardwareId($"{vendorId:X}", $"{deviceId:X}"); } catch (Exception) { return HardwareId.Empty; } } } ================================================ FILE: LenovoLegionToolkit.Lib/Features/Hybrid/Notify/DGPUFeatureFlagsNotify.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.System.Management; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.Features.Hybrid.Notify; public class DGPUFeatureFlagsNotify : AbstractDGPUNotify { public override async Task IsSupportedAsync() { try { var mi = await Compatibility.GetMachineInformationAsync().ConfigureAwait(false); return mi is { Features.Source: MachineInformation.FeatureData.SourceType.Flags, Properties.SupportsIGPUMode: true }; } catch { return false; } } protected override Task NotifyDGPUStatusAsync(bool state) => WMI.LenovoOtherMethod.SetDGPUDeviceStatusAsync(state); protected override async Task GetDGPUHardwareIdAsync() { try { return await WMI.LenovoOtherMethod.GetDGPUDeviceDIDVIDAsync().ConfigureAwait(false); } catch (Exception) { return HardwareId.Empty; } } } ================================================ FILE: LenovoLegionToolkit.Lib/Features/Hybrid/Notify/DGPUGamezoneNotify.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.System.Management; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.Features.Hybrid.Notify; public class DGPUGamezoneNotify : AbstractDGPUNotify { public override async Task IsSupportedAsync() { try { var mi = await Compatibility.GetMachineInformationAsync().ConfigureAwait(false); return mi is { Properties.SupportsIGPUMode: true }; } catch { return false; } } protected override Task NotifyDGPUStatusAsync(bool state) => WMI.LenovoGameZoneData.NotifyDGPUStatusAsync(state ? 1 : 0); protected override async Task GetDGPUHardwareIdAsync() { try { return await WMI.LenovoGameZoneData.GetDGPUHWIdAsync().ConfigureAwait(false); } catch (Exception) { return HardwareId.Empty; } } } ================================================ FILE: LenovoLegionToolkit.Lib/Features/Hybrid/Notify/DGPUNotify.cs ================================================ using System; using System.Threading.Tasks; using NeoSmart.AsyncLock; namespace LenovoLegionToolkit.Lib.Features.Hybrid.Notify; public class DGPUNotify(DGPUGamezoneNotify gamezoneNotify, DGPUCapabilityNotify capabilityNotify, DGPUFeatureFlagsNotify featureFlagsNotify) : IDGPUNotify { private readonly AsyncLock _lock = new(); private bool _resolved; private IDGPUNotify? _dgpuNotify; private DGPUCapabilityNotify CapabilityNotify => capabilityNotify; private DGPUFeatureFlagsNotify FeatureFlagsNotify => featureFlagsNotify; private DGPUGamezoneNotify GamezoneNotify => gamezoneNotify; public bool ExperimentalGPUWorkingMode { get; set; } public event EventHandler? Notified { add { CapabilityNotify.Notified += value; FeatureFlagsNotify.Notified += value; GamezoneNotify.Notified += value; } remove { CapabilityNotify.Notified -= value; FeatureFlagsNotify.Notified -= value; GamezoneNotify.Notified -= value; } } public async Task IsSupportedAsync() { var dgpuNotify = await ResolveInternalAsync().ConfigureAwait(false); if (dgpuNotify is null) return false; return await dgpuNotify.IsSupportedAsync().ConfigureAwait(false); } public async Task IsDGPUAvailableAsync() { var dgpuNotify = await ResolveInternalAsync().ConfigureAwait(false) ?? throw new InvalidOperationException($"No supported feature found [type={GetType().Name}]"); return await dgpuNotify.IsDGPUAvailableAsync().ConfigureAwait(false); } public async Task NotifyAsync(bool publish = true) { var dgpuNotify = await ResolveInternalAsync().ConfigureAwait(false) ?? throw new InvalidOperationException($"No supported feature found [type={GetType().Name}]"); await dgpuNotify.NotifyAsync(publish).ConfigureAwait(false); } public async Task NotifyLaterIfNeededAsync() { var dgpuNotify = await ResolveInternalAsync().ConfigureAwait(false) ?? throw new InvalidOperationException($"No supported feature found [type={GetType().Name}]"); await dgpuNotify.NotifyLaterIfNeededAsync().ConfigureAwait(false); } private async Task ResolveAsync() { if (!ExperimentalGPUWorkingMode) return await GamezoneNotify.IsSupportedAsync().ConfigureAwait(false) ? GamezoneNotify : null; if (await CapabilityNotify.IsSupportedAsync().ConfigureAwait(false)) return CapabilityNotify; if (await FeatureFlagsNotify.IsSupportedAsync().ConfigureAwait(false)) return FeatureFlagsNotify; return null; } private async Task ResolveInternalAsync() { using (await _lock.LockAsync().ConfigureAwait(false)) { if (_resolved) return _dgpuNotify; _dgpuNotify = await ResolveAsync().ConfigureAwait(false); _resolved = true; return _dgpuNotify; } } } ================================================ FILE: LenovoLegionToolkit.Lib/Features/Hybrid/Notify/IDGPUNotify.cs ================================================ using System; using System.Threading.Tasks; namespace LenovoLegionToolkit.Lib.Features.Hybrid.Notify; public interface IDGPUNotify { public event EventHandler? Notified; Task IsSupportedAsync(); Task IsDGPUAvailableAsync(); Task NotifyAsync(bool publish = true); Task NotifyLaterIfNeededAsync(); } ================================================ FILE: LenovoLegionToolkit.Lib/Features/IFeature.cs ================================================ using System.Threading.Tasks; namespace LenovoLegionToolkit.Lib.Features; public interface IFeature where T : struct { Task IsSupportedAsync(); Task GetAllStatesAsync(); Task GetStateAsync(); Task SetStateAsync(T state); } ================================================ FILE: LenovoLegionToolkit.Lib/Features/InstantBoot/InstantBootCapabilityFeature.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.Features.InstantBoot; public class InstantBootCapabilityFeature : IFeature { private enum State { Off, On } private class InstantBootAcFeature() : AbstractCapabilityFeature(CapabilityID.InstantBootAc); private class InstantBootUsbPowerDeliveryFeature() : AbstractCapabilityFeature(CapabilityID.InstantBootUsbPowerDelivery); private readonly InstantBootAcFeature _ac = new(); private readonly InstantBootUsbPowerDeliveryFeature _usbPowerDelivery = new(); public async Task IsSupportedAsync() { return await _ac.IsSupportedAsync().ConfigureAwait(false) && await _usbPowerDelivery.IsSupportedAsync().ConfigureAwait(false); } public Task GetAllStatesAsync() => Task.FromResult(Enum.GetValues()); public async Task GetStateAsync() { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Getting state..."); var ac = await _ac.GetStateAsync().ConfigureAwait(false); var usbPowerDelivery = await _usbPowerDelivery.GetStateAsync().ConfigureAwait(false); var result = (ac, usbPowerDelivery) switch { (State.On, State.On) => InstantBootState.AcAdapterAndUsbPowerDelivery, (State.On, State.Off) => InstantBootState.AcAdapter, (State.Off, State.On) => InstantBootState.UsbPowerDelivery, _ => InstantBootState.Off }; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"State is {result}"); return result; } public async Task SetStateAsync(InstantBootState state) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Setting state to {state}..."); var (ac, usbPowerDelivery) = state switch { InstantBootState.AcAdapterAndUsbPowerDelivery => (State.On, State.On), InstantBootState.AcAdapter => (State.On, State.Off), InstantBootState.UsbPowerDelivery => (State.Off, State.On), _ => (State.Off, State.Off) }; await _ac.SetStateAsync(ac).ConfigureAwait(false); await _usbPowerDelivery.SetStateAsync(usbPowerDelivery).ConfigureAwait(false); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Set state to {state}"); } } ================================================ FILE: LenovoLegionToolkit.Lib/Features/InstantBoot/InstantBootFeature.cs ================================================ namespace LenovoLegionToolkit.Lib.Features.InstantBoot; public class InstantBootFeature(InstantBootCapabilityFeature feature1, InstantBootFeatureFlagsFeature feature2) : AbstractCompositeFeature(feature1, feature2); ================================================ FILE: LenovoLegionToolkit.Lib/Features/InstantBoot/InstantBootFeatureFlagsFeature.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.System.Management; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.Features.InstantBoot; public class InstantBootFeatureFlagsFeature : IFeature { private const int AC_INDEX = 5; private const int USB_POWER_DELIVERY_INDEX = 6; public async Task IsSupportedAsync() { try { var mi = await Compatibility.GetMachineInformationAsync().ConfigureAwait(false); return mi.Features.Source == MachineInformation.FeatureData.SourceType.Flags && mi.Features[CapabilityID.InstantBootAc] && mi.Features[CapabilityID.InstantBootUsbPowerDelivery]; } catch { return false; } } public Task GetAllStatesAsync() => Task.FromResult(Enum.GetValues()); public async Task GetStateAsync() { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Getting state..."); var flags = await WMI.LenovoOtherMethod.GetDeviceCurrentSupportFeatureAsync().ConfigureAwait(false); var acAdapter = flags.IsBitSet(AC_INDEX); var usbPowerDelivery = flags.IsBitSet(USB_POWER_DELIVERY_INDEX); var result = (acAdapter, usbPowerDelivery) switch { (true, true) => InstantBootState.AcAdapterAndUsbPowerDelivery, (true, false) => InstantBootState.AcAdapter, (false, true) => InstantBootState.UsbPowerDelivery, _ => InstantBootState.Off }; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"State is {result}"); return result; } public async Task SetStateAsync(InstantBootState state) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Setting state to {state}..."); var (acAdapter, usbPowerDelivery) = state switch { InstantBootState.AcAdapterAndUsbPowerDelivery => (1, 1), InstantBootState.AcAdapter => (1, 0), InstantBootState.UsbPowerDelivery => (0, 1), _ => (0, 0) }; await WMI.LenovoOtherMethod.SetDeviceCurrentSupportFeatureAsync(AC_INDEX, acAdapter).ConfigureAwait(false); await WMI.LenovoOtherMethod.SetDeviceCurrentSupportFeatureAsync(USB_POWER_DELIVERY_INDEX, usbPowerDelivery).ConfigureAwait(false); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Set state to {state}"); } } ================================================ FILE: LenovoLegionToolkit.Lib/Features/MicrophoneFeature.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Extensions; using NAudio.CoreAudioApi; namespace LenovoLegionToolkit.Lib.Features; public class MicrophoneFeature : IFeature { private readonly MMDeviceEnumerator _enumerator = new(); private IEnumerable AudioEndpointVolumes => _enumerator.EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.Active).Select(d => d.AudioEndpointVolume); public Task IsSupportedAsync() { try { var isSupported = AudioEndpointVolumes.Any(); return Task.FromResult(isSupported); } catch { return Task.FromResult(false); } } public Task GetAllStatesAsync() => Task.FromResult(Enum.GetValues()); public Task GetStateAsync() { var mute = AudioEndpointVolumes.Aggregate(true, (current, v) => current && v.Mute); var result = mute ? MicrophoneState.Off : MicrophoneState.On; return Task.FromResult(result); } public Task SetStateAsync(MicrophoneState state) { var mute = MicrophoneState.Off == state; AudioEndpointVolumes.ForEach(v => v.Mute = mute); return Task.CompletedTask; } } ================================================ FILE: LenovoLegionToolkit.Lib/Features/OneLevelWhiteKeyboardBacklightFeature.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.System; namespace LenovoLegionToolkit.Lib.Features; public class OneLevelWhiteKeyboardBacklightFeature() : AbstractDriverFeature(Drivers.GetEnergy, Drivers.IOCTL_ENERGY_SETTINGS) { public override async Task IsSupportedAsync() { try { var outBuffer = await SendCodeAsync(DriverHandle(), ControlCode, GetInBufferValue()).ConfigureAwait(false); var result = ((int)outBuffer & 16) == 16; return result; } catch { return false; } } protected override uint GetInBufferValue() => 0x2; protected override Task ToInternalAsync(OneLevelWhiteKeyboardBacklightState state) { var result = state switch { OneLevelWhiteKeyboardBacklightState.Off => new uint[] { 0x9 }, OneLevelWhiteKeyboardBacklightState.On => [0x8], _ => throw new InvalidOperationException("Invalid state"), }; return Task.FromResult(result); } protected override Task FromInternalAsync(uint state) { var isOn = ((int)state & 32) == 32; var result = isOn ? OneLevelWhiteKeyboardBacklightState.On : OneLevelWhiteKeyboardBacklightState.Off; return Task.FromResult(result); } } ================================================ FILE: LenovoLegionToolkit.Lib/Features/OverDrive/OverDriveCapabilityFeature.cs ================================================ namespace LenovoLegionToolkit.Lib.Features.OverDrive; public class OverDriveCapabilityFeature() : AbstractCapabilityFeature(CapabilityID.OverDrive); ================================================ FILE: LenovoLegionToolkit.Lib/Features/OverDrive/OverDriveFeature.cs ================================================ namespace LenovoLegionToolkit.Lib.Features.OverDrive; public class OverDriveFeature(OverDriveCapabilityFeature feature1, OverDriveGameZoneFeature feature2) : AbstractCompositeFeature(feature1, feature2); ================================================ FILE: LenovoLegionToolkit.Lib/Features/OverDrive/OverDriveGameZoneFeature.cs ================================================ using LenovoLegionToolkit.Lib.System.Management; namespace LenovoLegionToolkit.Lib.Features.OverDrive; public class OverDriveGameZoneFeature() : AbstractWmiFeature(WMI.LenovoGameZoneData.GetODStatusAsync, WMI.LenovoGameZoneData.SetODStatusAsync, WMI.LenovoGameZoneData.IsSupportODAsync); ================================================ FILE: LenovoLegionToolkit.Lib/Features/PanelLogo/PanelLogoBacklightFeature.cs ================================================ namespace LenovoLegionToolkit.Lib.Features.PanelLogo; public class PanelLogoBacklightFeature(PanelLogoLenovoLightingBacklightFeature feature1, PanelLogoSpectrumBacklightFeature feature2) : AbstractCompositeFeature(feature1, feature2); ================================================ FILE: LenovoLegionToolkit.Lib/Features/PanelLogo/PanelLogoLenovoLightingBacklightFeature.cs ================================================ using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.Features.PanelLogo; public class PanelLogoLenovoLightingBacklightFeature() : AbstractLenovoLightingFeature(3, 1, 0) { public override async Task IsSupportedAsync() { var mi = await Compatibility.GetMachineInformationAsync().ConfigureAwait(false); if (mi.Properties.IsExcludedFromPanelLogoLenovoLighting) return false; return await base.IsSupportedAsync().ConfigureAwait(false); } protected override PanelLogoBacklightState FromInternal(int stateType, int _) => (PanelLogoBacklightState)stateType; protected override (int stateType, int level) ToInternal(PanelLogoBacklightState state) => ((int)state, 0); } ================================================ FILE: LenovoLegionToolkit.Lib/Features/PanelLogo/PanelLogoSpectrumBacklightFeature.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Controllers; namespace LenovoLegionToolkit.Lib.Features.PanelLogo; public class PanelLogoSpectrumBacklightFeature(SpectrumKeyboardBacklightController controller) : IFeature { public async Task IsSupportedAsync() { var isSupported = await controller.IsSupportedAsync().ConfigureAwait(false); if (!isSupported) return false; var (layout, _, _) = await controller.GetKeyboardLayoutAsync().ConfigureAwait(false); return layout == SpectrumLayout.Full; } public Task GetAllStatesAsync() => Task.FromResult(Enum.GetValues()); public async Task GetStateAsync() => await controller.GetLogoStatusAsync().ConfigureAwait(false) ? PanelLogoBacklightState.On : PanelLogoBacklightState.Off; public Task SetStateAsync(PanelLogoBacklightState state) => controller.SetLogoStatusAsync(state == PanelLogoBacklightState.On); } ================================================ FILE: LenovoLegionToolkit.Lib/Features/PortsBacklightFeature.cs ================================================ namespace LenovoLegionToolkit.Lib.Features; public class PortsBacklightFeature() : AbstractLenovoLightingFeature(5, 1, 0) { protected override PortsBacklightState FromInternal(int stateType, int _) => (PortsBacklightState)stateType; protected override (int stateType, int level) ToInternal(PortsBacklightState state) => ((int)state, 0); } ================================================ FILE: LenovoLegionToolkit.Lib/Features/PowerModeFeature.cs ================================================ using System; using System.Linq; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Controllers; using LenovoLegionToolkit.Lib.Controllers.GodMode; using LenovoLegionToolkit.Lib.Listeners; using LenovoLegionToolkit.Lib.System; using LenovoLegionToolkit.Lib.System.Management; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.Features; public class PowerModeUnavailableWithoutACException(PowerModeState powerMode) : Exception { public PowerModeState PowerMode { get; } = powerMode; } public class PowerModeFeature( GodModeController godModeController, WindowsPowerModeController windowsPowerModeController, WindowsPowerPlanController windowsPowerPlanController, ThermalModeListener thermalModeListener, PowerModeListener powerModeListener) : AbstractWmiFeature(WMI.LenovoGameZoneData.GetSmartFanModeAsync, WMI.LenovoGameZoneData.SetSmartFanModeAsync, WMI.LenovoGameZoneData.IsSupportSmartFanAsync, 1) { public bool AllowAllPowerModesOnBattery { get; set; } public override async Task GetAllStatesAsync() { var mi = await Compatibility.GetMachineInformationAsync().ConfigureAwait(false); return mi.Properties.SupportsGodMode ? [PowerModeState.Quiet, PowerModeState.Balance, PowerModeState.Performance, PowerModeState.GodMode] : [PowerModeState.Quiet, PowerModeState.Balance, PowerModeState.Performance]; } public override async Task SetStateAsync(PowerModeState state) { var allStates = await GetAllStatesAsync().ConfigureAwait(false); if (!allStates.Contains(state)) throw new InvalidOperationException($"Unsupported power mode {state}"); if (state is PowerModeState.Performance or PowerModeState.GodMode && !AllowAllPowerModesOnBattery && await Power.IsPowerAdapterConnectedAsync().ConfigureAwait(false) is PowerAdapterStatus.Disconnected) throw new PowerModeUnavailableWithoutACException(state); var currentState = await GetStateAsync().ConfigureAwait(false); var mi = await Compatibility.GetMachineInformationAsync().ConfigureAwait(false); if (mi.Properties.HasQuietToPerformanceModeSwitchingBug && currentState == PowerModeState.Quiet && state == PowerModeState.Performance) { thermalModeListener.SuppressNext(); await base.SetStateAsync(PowerModeState.Balance).ConfigureAwait(false); await Task.Delay(TimeSpan.FromMilliseconds(500)).ConfigureAwait(false); } if (mi.Properties.HasGodModeToOtherModeSwitchingBug && currentState == PowerModeState.GodMode && state != PowerModeState.GodMode) { thermalModeListener.SuppressNext(); switch (state) { case PowerModeState.Quiet: await base.SetStateAsync(PowerModeState.Performance).ConfigureAwait(false); break; case PowerModeState.Balance: await base.SetStateAsync(PowerModeState.Quiet).ConfigureAwait(false); break; case PowerModeState.Performance: await base.SetStateAsync(PowerModeState.Balance).ConfigureAwait(false); break; } await Task.Delay(TimeSpan.FromMilliseconds(500)).ConfigureAwait(false); } thermalModeListener.SuppressNext(); await base.SetStateAsync(state).ConfigureAwait(false); await powerModeListener.NotifyAsync(state).ConfigureAwait(false); } public async Task EnsureCorrectWindowsPowerSettingsAreSetAsync() { var state = await GetStateAsync().ConfigureAwait(false); await windowsPowerModeController.SetPowerModeAsync(state).ConfigureAwait(false); await windowsPowerPlanController.SetPowerPlanAsync(state, true).ConfigureAwait(false); } public async Task EnsureGodModeStateIsAppliedAsync() { var state = await GetStateAsync().ConfigureAwait(false); if (state != PowerModeState.GodMode) return; await godModeController.ApplyStateAsync().ConfigureAwait(false); } } ================================================ FILE: LenovoLegionToolkit.Lib/Features/RefreshRateFeature.cs ================================================ using System; using System.Linq; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.System; using LenovoLegionToolkit.Lib.Utils; using WindowsDisplayAPI; using WindowsDisplayAPI.Native.DeviceContext; namespace LenovoLegionToolkit.Lib.Features; public class RefreshRateFeature : IFeature { public Task IsSupportedAsync() => Task.FromResult(true); public Task GetAllStatesAsync() { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Getting all refresh rates..."); var display = InternalDisplay.Get(); if (display is null) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Built in display not found"); return Task.FromResult(Array.Empty()); } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Built in display found: {display}"); var currentSettings = display.CurrentSetting; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Current built in display settings: {currentSettings.ToExtendedString()}"); var result = display.GetPossibleSettings() .Where(dps => Match(dps, currentSettings)) .Select(dps => dps.Frequency) .Distinct() .OrderBy(freq => freq) .Select(freq => new RefreshRate(freq)) .ToArray(); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Possible refresh rates are {string.Join(", ", result)}"); return Task.FromResult(result); } public Task GetStateAsync() { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Getting current refresh rate..."); var display = InternalDisplay.Get(); if (display is null) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Built in display not found"); return Task.FromResult(default(RefreshRate)); } var currentSettings = display.CurrentSetting; var result = new RefreshRate(currentSettings.Frequency); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Current refresh rate is {result} [currentSettings={currentSettings.ToExtendedString()}]"); return Task.FromResult(result); } public Task SetStateAsync(RefreshRate state) { var display = InternalDisplay.Get(); if (display is null) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Built in display not found"); throw new InvalidOperationException("Built in display not found"); } var currentSettings = display.CurrentSetting; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Current built in display settings: {currentSettings.ToExtendedString()}"); if (currentSettings.Frequency == state.Frequency) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Frequency already set to {state.Frequency}"); return Task.CompletedTask; } var possibleSettings = display.GetPossibleSettings(); var newSettings = possibleSettings .Where(dps => Match(dps, currentSettings)) .Where(dps => dps.Frequency == state.Frequency) .Select(dps => new DisplaySetting(dps, currentSettings.Position, currentSettings.Orientation, DisplayFixedOutput.Default)) .FirstOrDefault(); if (newSettings is not null) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Setting display to {newSettings.ToExtendedString()}..."); display.SetSettingsUsingPathInfo(newSettings); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Display set to {newSettings.ToExtendedString()}"); } else { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Could not find matching settings for frequency {state}"); } return Task.CompletedTask; } private static bool Match(DisplayPossibleSetting dps, DisplayPossibleSetting ds) { if (dps.IsTooSmall()) return false; var result = true; result &= dps.Resolution == ds.Resolution; result &= dps.ColorDepth == ds.ColorDepth; result &= dps.IsInterlaced == ds.IsInterlaced; return result; } } ================================================ FILE: LenovoLegionToolkit.Lib/Features/ResolutionFeature.cs ================================================ using System; using System.Linq; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.System; using LenovoLegionToolkit.Lib.Utils; using WindowsDisplayAPI; using WindowsDisplayAPI.Native.DeviceContext; namespace LenovoLegionToolkit.Lib.Features; public class ResolutionFeature : IFeature { public Task IsSupportedAsync() => Task.FromResult(true); public Task GetAllStatesAsync() { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Getting all resolutions..."); var display = InternalDisplay.Get(); if (display is null) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Built in display not found"); return Task.FromResult(Array.Empty()); } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Built in display found: {display}"); var currentSettings = display.CurrentSetting; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Current built in display settings: {currentSettings.ToExtendedString()}"); var result = display.GetPossibleSettings() .Where(dps => Match(dps, currentSettings)) .Select(dps => dps.Resolution) .Select(res => new Resolution(res)) .Distinct() .OrderByDescending(res => res) .ToArray(); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Possible resolutions are {string.Join(", ", result)}"); return Task.FromResult(result); } public Task GetStateAsync() { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Getting current resolution..."); var display = InternalDisplay.Get(); if (display is null) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Built in display not found"); return Task.FromResult(default(Resolution)); } var currentSettings = display.CurrentSetting; var result = new Resolution(currentSettings.Resolution); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Current resolution is {result} [currentSettings={currentSettings.ToExtendedString()}]"); return Task.FromResult(result); } public Task SetStateAsync(Resolution state) { var display = InternalDisplay.Get(); if (display is null) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Built in display not found"); throw new InvalidOperationException("Built in display not found"); } var currentSettings = display.CurrentSetting; if (currentSettings.Resolution == state) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Resolution already set to {state}"); return Task.CompletedTask; } var possibleSettings = display.GetPossibleSettings(); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Current built in display settings: {currentSettings.ToExtendedString()}"); var newSettings = possibleSettings .Where(dps => Match(dps, currentSettings)) .Where(dps => dps.Resolution == state) .Select(dps => new DisplaySetting(dps, currentSettings.Position, currentSettings.Orientation, DisplayFixedOutput.Default)) .FirstOrDefault(); if (newSettings is not null) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Setting display to {newSettings.ToExtendedString()}"); display.SetSettingsUsingPathInfo(newSettings); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Display set to {newSettings.ToExtendedString()}"); } else { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Could not find matching settings for resolution {state}"); } return Task.CompletedTask; } private static bool Match(DisplayPossibleSetting dps, DisplayPossibleSetting ds) { if (dps.IsTooSmall()) return false; var result = true; result &= dps.Frequency == ds.Frequency; result &= dps.ColorDepth == ds.ColorDepth; result &= dps.IsInterlaced == ds.IsInterlaced; return result; } } ================================================ FILE: LenovoLegionToolkit.Lib/Features/SpeakerFeature.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Extensions; using NAudio.CoreAudioApi; namespace LenovoLegionToolkit.Lib.Features; public class SpeakerFeature : IFeature { private readonly MMDeviceEnumerator _enumerator = new(); private IEnumerable AudioEndpointVolumes => _enumerator.EnumerateAudioEndPoints(DataFlow.Render, DeviceState.Active).Select(d => d.AudioEndpointVolume); public Task IsSupportedAsync() { try { var isSupported = AudioEndpointVolumes.Any(); return Task.FromResult(isSupported); } catch { return Task.FromResult(false); } } public Task GetAllStatesAsync() => Task.FromResult(Enum.GetValues()); public Task GetStateAsync() { var mute = AudioEndpointVolumes.Aggregate(true, (current, v) => current && v.Mute); var result = mute ? SpeakerState.Mute : SpeakerState.Unmute; return Task.FromResult(result); } public Task SetStateAsync(SpeakerState state) { var mute = SpeakerState.Mute == state; AudioEndpointVolumes.ForEach(v => v.Mute = mute); return Task.CompletedTask; } } ================================================ FILE: LenovoLegionToolkit.Lib/Features/TouchpadLockFeature.cs ================================================ using LenovoLegionToolkit.Lib.System.Management; namespace LenovoLegionToolkit.Lib.Features; public class TouchpadLockFeature() : AbstractWmiFeature(WMI.LenovoGameZoneData.GetTPStatusStatusAsync, WMI.LenovoGameZoneData.SetTPStatusAsync, WMI.LenovoGameZoneData.IsSupportDisableTPAsync); ================================================ FILE: LenovoLegionToolkit.Lib/Features/WhiteKeyboardBacklight/WhiteKeyboardBacklightFeature.cs ================================================ using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Controllers; namespace LenovoLegionToolkit.Lib.Features.WhiteKeyboardBacklight; public class WhiteKeyboardBacklightFeature(WhiteKeyboardLenovoLightingBacklightFeature feature1, WhiteKeyboardDriverBacklightFeature feature2, SpectrumKeyboardBacklightController spectrumController, RGBKeyboardBacklightController rgbController) : AbstractCompositeFeature(feature1, feature2) { protected override async Task?> ResolveAsync() { if (await spectrumController.IsSupportedAsync().ConfigureAwait(false) || await rgbController.IsSupportedAsync().ConfigureAwait(false)) return null; return await base.ResolveAsync().ConfigureAwait(false); } } ================================================ FILE: LenovoLegionToolkit.Lib/Features/WhiteKeyboardBacklight/WhiteKeyboardDriverBacklightFeature.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.System; namespace LenovoLegionToolkit.Lib.Features.WhiteKeyboardBacklight; public class WhiteKeyboardDriverBacklightFeature() : AbstractDriverFeature(Drivers.GetEnergy, Drivers.IOCTL_ENERGY_KEYBOARD) { public override async Task IsSupportedAsync() { try { var outBuffer = await SendCodeAsync(DriverHandle(), ControlCode, 0x1).ConfigureAwait(false); outBuffer >>= 1; return outBuffer == 0x2; } catch { return false; } } protected override uint GetInBufferValue() => 0x22; protected override Task ToInternalAsync(WhiteKeyboardBacklightState state) { var result = state switch { WhiteKeyboardBacklightState.Off => new uint[] { 0x00023 }, WhiteKeyboardBacklightState.Low => [0x10023], WhiteKeyboardBacklightState.High => [0x20023], _ => throw new InvalidOperationException("Invalid state"), }; return Task.FromResult(result); } protected override Task FromInternalAsync(uint state) { var result = state switch { 0x1 => WhiteKeyboardBacklightState.Off, 0x3 => WhiteKeyboardBacklightState.Low, 0x5 => WhiteKeyboardBacklightState.High, _ => throw new InvalidOperationException("Invalid state"), }; return Task.FromResult(result); } } ================================================ FILE: LenovoLegionToolkit.Lib/Features/WhiteKeyboardBacklight/WhiteKeyboardLenovoLightingBacklightFeature.cs ================================================ namespace LenovoLegionToolkit.Lib.Features.WhiteKeyboardBacklight; public class WhiteKeyboardLenovoLightingBacklightFeature() : AbstractLenovoLightingFeature(0, 0, 1) { protected override WhiteKeyboardBacklightState FromInternal(int _, int level) => (WhiteKeyboardBacklightState)(level - 1); protected override (int stateType, int level) ToInternal(WhiteKeyboardBacklightState state) => (0, (int)(state + 1)); } ================================================ FILE: LenovoLegionToolkit.Lib/Features/WinKeyFeature.cs ================================================ using LenovoLegionToolkit.Lib.System.Management; namespace LenovoLegionToolkit.Lib.Features; public class WinKeyFeature() : AbstractWmiFeature(WMI.LenovoGameZoneData.GetWinKeyStatusAsync, WMI.LenovoGameZoneData.SetWinKeyStatusAsync, WMI.LenovoGameZoneData.IsSupportDisableWinKeyAsync); ================================================ FILE: LenovoLegionToolkit.Lib/GameDetection/EffectiveGameModeDetector.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Utils; using Windows.Win32; using Windows.Win32.System.Power; namespace LenovoLegionToolkit.Lib.GameDetection; internal unsafe class EffectiveGameModeDetector { private readonly EFFECTIVE_POWER_MODE_CALLBACK _callbackPointer; private IntPtr _handle; private bool? _lastState; public event EventHandler? Changed; public EffectiveGameModeDetector() { _callbackPointer = Callback; } public Task StartAsync() { var result = PInvoke.PowerRegisterForEffectivePowerModeNotifications(PInvoke.EFFECTIVE_POWER_MODE_V2, _callbackPointer, null, out var handle); if (result == 0) _handle = new IntPtr(handle); return Task.CompletedTask; } public Task StopAsync() { PInvoke.PowerUnregisterFromEffectivePowerModeNotifications(_handle.ToPointer()); _handle = IntPtr.Zero; return Task.CompletedTask; } private void Callback(EFFECTIVE_POWER_MODE mode, void* context) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Effective power mode is {mode}."); var state = mode == EFFECTIVE_POWER_MODE.EffectivePowerModeGameMode; _lastState ??= state; if (_lastState == state) return; _lastState = state; Changed?.Invoke(this, state); } } ================================================ FILE: LenovoLegionToolkit.Lib/GameDetection/GameConfigStoreDetector.cs ================================================ using System; using System.Collections.Generic; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.System; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.GameDetection; internal class GameConfigStoreDetector { private const string GAME_CONFIG_STORE_HIVE = "HKEY_CURRENT_USER"; private const string GAME_CONFIG_STORE_PATH = @"System\GameConfigStore\Children"; private const string MATCHED_EXE_FULL_PATH_KEY_NAME = "MatchedExeFullPath"; public class GameDetectedEventArgs(HashSet games) : EventArgs { public HashSet Games { get; } = games; } public event EventHandler? GamesDetected; private IAsyncDisposable? _listener; public Task StartAsync() { if (_listener is not null) return Task.CompletedTask; var lastPaths = GetDetectedGamePaths(); _listener = Registry.ObserveKey(GAME_CONFIG_STORE_HIVE, GAME_CONFIG_STORE_PATH, true, () => { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Registry changed..."); var newPaths = GetDetectedGamePaths(); if (!newPaths.SetEquals(lastPaths)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Detected games changed."); GamesDetected?.Invoke(this, new(newPaths)); } lastPaths = newPaths; }); return Task.CompletedTask; } public async Task StopAsync() { if (_listener is not null) await _listener.DisposeAsync().ConfigureAwait(false); _listener = null; } public static HashSet GetDetectedGamePaths() { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Detecting games..."); var result = new HashSet(); foreach (var subKey in Registry.GetSubKeys(GAME_CONFIG_STORE_HIVE, GAME_CONFIG_STORE_PATH)) { var exePath = Registry.GetValue(GAME_CONFIG_STORE_HIVE, subKey, MATCHED_EXE_FULL_PATH_KEY_NAME, string.Empty); if (string.IsNullOrEmpty(exePath)) continue; result.Add(ProcessInfo.FromPath(exePath)); } if (Log.Instance.IsTraceEnabled) { Log.Instance.Trace($"Detected games:"); foreach (var r in result) Log.Instance.Trace($" - {r}"); } return result; } } ================================================ FILE: LenovoLegionToolkit.Lib/GlobalSuppressions.cs ================================================ // This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage("Performance", "CA1822:Mark members as static")] [assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility")] ================================================ FILE: LenovoLegionToolkit.Lib/HttpClientFactory.cs ================================================ using System; using System.Net; using System.Net.Http; namespace LenovoLegionToolkit.Lib; public class HttpClientFactory { private Uri? _url; private string? _username; private string? _password; private bool _allowAllCerts; public HttpClientHandler CreateHandler() { var handler = new HttpClientHandler(); if (_url is not null) { handler.UseProxy = true; handler.Proxy = new WebProxy(_url) { UseDefaultCredentials = false, BypassProxyOnLocal = false, }; if (_username is not null && _password is not null) handler.DefaultProxyCredentials = new NetworkCredential(_username, _password); if (_allowAllCerts) handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; } return handler; } public HttpClient Create() => new(CreateHandler(), true); public void SetProxy(Uri? url, string? username, string? password, bool allowAllCerts) { _url = url; _username = username; _password = password; _allowAllCerts = allowAllCerts; } } ================================================ FILE: LenovoLegionToolkit.Lib/Integrations/HWiNFOIntegration.cs ================================================ using System; using System.Globalization; using System.Threading; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Controllers.Sensors; using LenovoLegionToolkit.Lib.Settings; using LenovoLegionToolkit.Lib.System; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.Integrations; public class HWiNFOIntegration(SensorsController sensorController, IntegrationsSettings settings) { private const string CUSTOM_SENSOR_HIVE = "HKEY_CURRENT_USER"; private const string CUSTOM_SENSOR_PATH = @"Software\HWiNFO64\Sensors\Custom"; private const string CUSTOM_SENSOR_GROUP_NAME = "Lenovo Legion Toolkit"; private const string SENSOR_TYPE_FAN = "Fan"; private const string SENSOR_TYPE_TEMP = "Temp"; private const string CPU_FAN_SENSOR_NAME = "CPU Fan"; private const string GPU_FAN_SENSOR_NAME = "GPU Fan"; private const string BATTERY_TEMP_SENSOR_NAME = "Battery Temperature"; private readonly TimeSpan _refreshInterval = TimeSpan.FromSeconds(1); private CancellationTokenSource? _cts; private Task? _refreshTask; public async Task StartStopIfNeededAsync() { await StopAsync().ConfigureAwait(false); if (!settings.Store.HWiNFO) return; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Starting..."); _cts = new(); _refreshTask = RefreshLoopAsync(_cts.Token); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Started."); } public async Task StopAsync() { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Stopping..."); if (_cts is not null) await _cts.CancelAsync().ConfigureAwait(false); if (_refreshTask is not null) await _refreshTask.ConfigureAwait(false); ClearValues(); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Stopped."); } private async Task RefreshLoopAsync(CancellationToken token) { try { await SetSensorValuesAsync().ConfigureAwait(false); while (true) { await Task.Delay(_refreshInterval, token).ConfigureAwait(false); await SetSensorValuesAsync(false).ConfigureAwait(false); } } catch (OperationCanceledException) { } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to set values.", ex); } } private async Task SetSensorValuesAsync(bool firstRun = true) { var (cpuFanSpeed, gpuFanSpeed) = await sensorController.GetFanSpeedsAsync().ConfigureAwait(false); var batteryTemp = Battery.GetBatteryTemperatureC(); SetValue(SENSOR_TYPE_FAN, 0, CPU_FAN_SENSOR_NAME, cpuFanSpeed, firstRun); SetValue(SENSOR_TYPE_FAN, 1, GPU_FAN_SENSOR_NAME, gpuFanSpeed, firstRun); var batteryTempString = batteryTemp.HasValue ? batteryTemp.Value.ToString(new NumberFormatInfo { NumberDecimalSeparator = "." }) : string.Empty; SetValue(SENSOR_TYPE_TEMP, 0, BATTERY_TEMP_SENSOR_NAME, batteryTempString, firstRun); } private static void SetValue(string type, int index, string name, T value, bool firstRun) where T : notnull { Registry.SetValue(CUSTOM_SENSOR_HIVE, $@"{CUSTOM_SENSOR_PATH}\{CUSTOM_SENSOR_GROUP_NAME}\{type}{index}", "Value", value); if (!firstRun) return; Registry.SetValue(CUSTOM_SENSOR_HIVE, $@"{CUSTOM_SENSOR_PATH}\{CUSTOM_SENSOR_GROUP_NAME}\{type}{index}", "Name", name); } private static void ClearValues() { try { Registry.Delete(CUSTOM_SENSOR_HIVE, $@"{CUSTOM_SENSOR_PATH}\{CUSTOM_SENSOR_GROUP_NAME}"); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to clear values.", ex); } } } ================================================ FILE: LenovoLegionToolkit.Lib/Interfaces.cs ================================================ namespace LenovoLegionToolkit.Lib; public interface IDisplayName { string DisplayName { get; } } ================================================ FILE: LenovoLegionToolkit.Lib/IoCContainer.cs ================================================ using System; using Autofac; namespace LenovoLegionToolkit.Lib; public static class IoCContainer { private static readonly object Lock = new(); private static IContainer? _container; public static void Initialize(params Module[] modules) { lock (Lock) { if (_container is not null) throw new InvalidOperationException("IoCContainer already initialized"); var cb = new ContainerBuilder(); foreach (var module in modules) cb.RegisterModule(module); _container = cb.Build(); } } public static T Resolve() where T : notnull { lock (Lock) { if (_container is null) throw new InvalidOperationException($"IoCContainer must be initialized first [type={nameof(T)}]"); return _container.Resolve(); } } public static T? TryResolve() where T : class { lock (Lock) { if (_container is null) throw new InvalidOperationException($"IoCContainer must be initialized first [type={nameof(T)}]"); _ = _container.TryResolve(out T? value); return value; } } } ================================================ FILE: LenovoLegionToolkit.Lib/IoCModule.cs ================================================ using Autofac; using LenovoLegionToolkit.Lib.AutoListeners; using LenovoLegionToolkit.Lib.Controllers; using LenovoLegionToolkit.Lib.Controllers.GodMode; using LenovoLegionToolkit.Lib.Controllers.Sensors; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Features; using LenovoLegionToolkit.Lib.Features.FlipToStart; using LenovoLegionToolkit.Lib.Features.Hybrid; using LenovoLegionToolkit.Lib.Features.Hybrid.Notify; using LenovoLegionToolkit.Lib.Features.InstantBoot; using LenovoLegionToolkit.Lib.Features.OverDrive; using LenovoLegionToolkit.Lib.Features.PanelLogo; using LenovoLegionToolkit.Lib.Features.WhiteKeyboardBacklight; using LenovoLegionToolkit.Lib.Integrations; using LenovoLegionToolkit.Lib.Listeners; using LenovoLegionToolkit.Lib.PackageDownloader; using LenovoLegionToolkit.Lib.Services; using LenovoLegionToolkit.Lib.Settings; using LenovoLegionToolkit.Lib.SoftwareDisabler; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib; public class IoCModule : Module { protected override void Load(ContainerBuilder builder) { builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(true); builder.Register(true); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(true); builder.Register(true); builder.Register(true); builder.Register(); builder.Register(true); builder.Register(true); builder.Register(); builder.Register(); builder.Register(); builder.Register(true); builder.Register(true); builder.Register(); builder.Register(true); builder.Register(true); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(true); builder.Register(true); builder.Register(); builder.Register(); builder.Register(true); builder.Register(true); builder.Register(true); builder.Register().AutoActivateListener(); builder.Register().AutoActivateListener(); builder.Register().AutoActivateListener(); builder.Register().AutoActivateListener(); builder.Register().AutoActivateListener(); builder.Register().AutoActivateListener(); builder.Register().AutoActivateListener(); builder.Register().AutoActivateListener(); builder.Register().AutoActivateListener(); builder.Register().AutoActivateListener(); builder.Register().AutoActivateListener(); builder.Register().AutoActivateListener(); builder.Register().AutoActivateListener(); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(true); builder.Register(true); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(true); builder.Register(true); builder.Register(true); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(); builder.Register(); } } ================================================ FILE: LenovoLegionToolkit.Lib/LenovoLegionToolkit.Lib.csproj ================================================  net8.0-windows win-x64 x64 enable © 2024 Bartosz Cichecki true en true all runtime; build; native; contentfiles; analyzers; buildtransitive True True Resource.resx PublicResXFileCodeGenerator Resource.Designer.cs ================================================ FILE: LenovoLegionToolkit.Lib/LenovoLegionToolkit.Lib.csproj.DotSettings ================================================  Library ================================================ FILE: LenovoLegionToolkit.Lib/Listeners/AbstractEventLogListener.cs ================================================ using System; using System.Diagnostics.Eventing.Reader; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.Listeners; public abstract class AbstractEventLogListener : IListener { private readonly EventLogWatcher _watcher; public AbstractEventLogListener(string path, string query) { var eventLogQuery = new EventLogQuery(path, PathType.LogName, query); _watcher = new EventLogWatcher(eventLogQuery); _watcher.EventRecordWritten += Watcher_EventRecordWritten; } public event EventHandler? Changed; public Task StartAsync() { if (!_watcher.Enabled) _watcher.Enabled = true; return Task.CompletedTask; } public Task StopAsync() { _watcher.Enabled = false; return Task.CompletedTask; } protected abstract Task OnChangedAsync(); private async void Watcher_EventRecordWritten(object? sender, EventRecordWrittenEventArgs e) { try { await OnChangedAsync().ConfigureAwait(false); Changed?.Invoke(this, EventArgs.Empty); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to handle event. [listener={GetType().Name}]", ex); } } } ================================================ FILE: LenovoLegionToolkit.Lib/Listeners/AbstractWMIListener.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.Listeners; public abstract class AbstractWMIListener(Func, IDisposable> listen) : IListener where TEventArgs : EventArgs { private IDisposable? _disposable; public event EventHandler? Changed; public Task StartAsync() { try { if (_disposable is not null) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Already started. [listener={GetType().Name}]"); return Task.CompletedTask; } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Starting... [listener={GetType().Name}]"); _disposable = listen(Handler); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Couldn't start listener. [listener={GetType().Name}]", ex); } return Task.CompletedTask; } public Task StopAsync() { try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Stopping... [listener={GetType().Name}]"); _disposable?.Dispose(); _disposable = null; } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Couldn't stop listener. [listener={GetType().Name}]", ex); } return Task.CompletedTask; } protected abstract TValue GetValue(TRawValue value); protected abstract TEventArgs GetEventArgs(TValue value); protected abstract Task OnChangedAsync(TValue value); protected void RaiseChanged(TValue value) => Changed?.Invoke(this, GetEventArgs(value)); private async void Handler(TRawValue properties) { try { var value = GetValue(properties); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Event received. [value={value}, listener={GetType().Name}]"); await OnChangedAsync(value).ConfigureAwait(false); RaiseChanged(value); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to handle event. [listener={GetType().Name}]", ex); } } } ================================================ FILE: LenovoLegionToolkit.Lib/Listeners/DisplayBrightnessListener.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Controllers; using LenovoLegionToolkit.Lib.Settings; using LenovoLegionToolkit.Lib.System.Management; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.Listeners; public class DisplayBrightnessListener(WindowsPowerPlanController windowsPowerPlanController, ApplicationSettings settings) : AbstractWMIListener(WMI.WmiMonitorBrightnessEvent.Listen) { public class ChangedEventArgs(Brightness brightness) : EventArgs { public Brightness Brightness { get; } = brightness; } private readonly ThrottleLastDispatcher _dispatcher = new(TimeSpan.FromSeconds(2), nameof(DisplayBrightnessListener)); protected override Brightness GetValue(byte value) => new(value); protected override ChangedEventArgs GetEventArgs(Brightness value) => new(value); protected override async Task OnChangedAsync(Brightness value) => await SynchronizeBrightnessAsync(value).ConfigureAwait(false); private async Task SynchronizeBrightnessAsync(Brightness value) { if (!settings.Store.SynchronizeBrightnessToAllPowerPlans) return; await _dispatcher.DispatchAsync(() => { SetBrightnessForAllPowerPlans(value); return Task.CompletedTask; }).ConfigureAwait(false); } private void SetBrightnessForAllPowerPlans(Brightness brightness) { try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Setting brightness to {brightness.Value}..."); var powerPlans = windowsPowerPlanController.GetPowerPlans(); foreach (var powerPlan in powerPlans) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Modifying power plan {powerPlan.Name}... [powerPlan.Guid={powerPlan.Guid}, brightness={brightness.Value}]"); windowsPowerPlanController.SetPowerPlanParameter(powerPlan, brightness); } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Brightness set to {brightness.Value}."); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to set brightness to {brightness.Value}.", ex); } } } ================================================ FILE: LenovoLegionToolkit.Lib/Listeners/DisplayConfigurationListener.cs ================================================ using System; using System.Linq; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.System; using LenovoLegionToolkit.Lib.Utils; using Microsoft.Win32; namespace LenovoLegionToolkit.Lib.Listeners; public class DisplayConfigurationListener : IListener { public class ChangedEventArgs : EventArgs { public bool? HDR { get; init; } } private bool _started; public bool? IsHDROn { get; private set; } public event EventHandler? Changed; public Task StartAsync() { if (_started) return Task.CompletedTask; IsHDROn = GetHDRStatus(); SystemEvents.DisplaySettingsChanged += SystemEvents_DisplaySettingsChanged; _started = true; return Task.CompletedTask; } public Task StopAsync() { SystemEvents.DisplaySettingsChanged -= SystemEvents_DisplaySettingsChanged; _started = false; IsHDROn = null; return Task.CompletedTask; } private void SystemEvents_DisplaySettingsChanged(object? sender, EventArgs e) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Event received."); InternalDisplay.SetNeedsRefresh(); var previousIsHDROn = IsHDROn; IsHDROn = GetHDRStatus(); var changed = previousIsHDROn != IsHDROn; Changed?.Invoke(this, new() { HDR = changed ? IsHDROn : null }); } private static bool? GetHDRStatus() { try { return Displays.Get().FirstOrDefault()?.GetAdvancedColorInfo().AdvancedColorEnabled; } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to get HDR status. Assuming unavailable.", ex); return null; } } } ================================================ FILE: LenovoLegionToolkit.Lib/Listeners/DriverKeyListener.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Features; using LenovoLegionToolkit.Lib.Features.WhiteKeyboardBacklight; using LenovoLegionToolkit.Lib.Messaging; using LenovoLegionToolkit.Lib.Messaging.Messages; using LenovoLegionToolkit.Lib.SoftwareDisabler; using LenovoLegionToolkit.Lib.System; using LenovoLegionToolkit.Lib.Utils; using Windows.Win32; namespace LenovoLegionToolkit.Lib.Listeners; public class DriverKeyListener( FnKeysDisabler fnKeysDisabler, MicrophoneFeature microphoneFeature, TouchpadLockFeature touchpadLockFeature, WhiteKeyboardBacklightFeature whiteKeyboardBacklightFeature) : IListener { public class ChangedEventArgs(DriverKey driverKey) : EventArgs { public DriverKey DriverKey { get; } = driverKey; } public event EventHandler? Changed; private CancellationTokenSource? _cancellationTokenSource; private Task? _listenTask; public Task StartAsync() { if (_listenTask is not null) return Task.CompletedTask; _cancellationTokenSource = new(); _listenTask = Task.Run(() => HandlerAsync(_cancellationTokenSource.Token)); return Task.CompletedTask; } public async Task StopAsync() { if (_cancellationTokenSource is not null) await _cancellationTokenSource.CancelAsync().ConfigureAwait(false); _cancellationTokenSource = null; if (_listenTask is not null) await _listenTask; _listenTask = null; } private async Task HandlerAsync(CancellationToken token) { try { var resetEvent = new ManualResetEvent(false); var setHandleResult = BindListener(resetEvent); if (!setHandleResult) PInvokeExtensions.ThrowIfWin32Error("DeviceIoControl, setHandleResult"); GetValue(out _); // Clear register while (true) { WaitHandle.WaitAny([resetEvent, token.WaitHandle]); token.ThrowIfCancellationRequested(); if (await fnKeysDisabler.GetStatusAsync().ConfigureAwait(false) == SoftwareStatus.Enabled) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Ignoring, FnKeys are enabled."); resetEvent.Reset(); continue; } var getValueResult = GetValue(out var value); if (!getValueResult) PInvokeExtensions.ThrowIfWin32Error("DeviceIoControl, getValueResult"); var key = (DriverKey)value; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Event received. [key={key}, value={value}]"); await OnChangedAsync(key).ConfigureAwait(false); Changed?.Invoke(this, new(key)); resetEvent.Reset(); } } catch (OperationCanceledException) { } catch (ThreadAbortException) { } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Unknown error.", ex); } } private async Task OnChangedAsync(DriverKey value) { try { if (value.HasFlag(DriverKey.FnF4)) { if (await microphoneFeature.IsSupportedAsync().ConfigureAwait(false)) { switch (await microphoneFeature.GetStateAsync().ConfigureAwait(false)) { case MicrophoneState.On: await microphoneFeature.SetStateAsync(MicrophoneState.Off).ConfigureAwait(false); MessagingCenter.Publish(new NotificationMessage(NotificationType.MicrophoneOff)); break; case MicrophoneState.Off: await microphoneFeature.SetStateAsync(MicrophoneState.On).ConfigureAwait(false); MessagingCenter.Publish(new NotificationMessage(NotificationType.MicrophoneOn)); break; } } } if (value.HasFlag(DriverKey.FnF8)) AirplaneMode.Open(); if (value.HasFlag(DriverKey.FnF10)) { if (await touchpadLockFeature.IsSupportedAsync().ConfigureAwait(false)) { var status = await touchpadLockFeature.GetStateAsync().ConfigureAwait(false); MessagingCenter.Publish(status == TouchpadLockState.Off ? new NotificationMessage(NotificationType.TouchpadOn) : new NotificationMessage(NotificationType.TouchpadOff)); } } if (value.HasFlag(DriverKey.FnSpace)) { if (await whiteKeyboardBacklightFeature.IsSupportedAsync().ConfigureAwait(false)) { var state = await whiteKeyboardBacklightFeature.GetStateAsync().ConfigureAwait(false); MessagingCenter.Publish(state == WhiteKeyboardBacklightState.Off ? new NotificationMessage(NotificationType.WhiteKeyboardBacklightOff, state.GetDisplayName()) : new NotificationMessage(NotificationType.WhiteKeyboardBacklightChanged, state.GetDisplayName())); } } } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Couldn't handle key press. [value={value}]", ex); } } private static unsafe bool BindListener(WaitHandle waitHandle) { var handle = (uint)waitHandle.SafeWaitHandle.DangerousGetHandle(); return PInvoke.DeviceIoControl(Drivers.GetEnergy(), Drivers.IOCTL_KEY_WAIT_HANDLE, &handle, 16, null, 0, null, null); } private static unsafe bool GetValue(out uint value) { uint inBuff = 0; uint outBuff = 0; var result = PInvoke.DeviceIoControl(Drivers.GetEnergy(), Drivers.IOCTL_KEY_VALUE, &inBuff, 4, &outBuff, 4, null, null); value = outBuff; return result; } } ================================================ FILE: LenovoLegionToolkit.Lib/Listeners/IListener.cs ================================================ using System; using System.Threading.Tasks; namespace LenovoLegionToolkit.Lib.Listeners; public interface IListener where TEventArgs : EventArgs { event EventHandler? Changed; Task StartAsync(); Task StopAsync(); } ================================================ FILE: LenovoLegionToolkit.Lib/Listeners/INotifyingListener.cs ================================================ using System; using System.Threading.Tasks; namespace LenovoLegionToolkit.Lib.Listeners; public interface INotifyingListener : IListener where TEventArgs : EventArgs { Task NotifyAsync(TValue value); } ================================================ FILE: LenovoLegionToolkit.Lib/Listeners/LightingChangeListener.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Features; using LenovoLegionToolkit.Lib.Features.PanelLogo; using LenovoLegionToolkit.Lib.Messaging; using LenovoLegionToolkit.Lib.Messaging.Messages; using LenovoLegionToolkit.Lib.SoftwareDisabler; using LenovoLegionToolkit.Lib.System.Management; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.Listeners; public class LightingChangeListener( PanelLogoBacklightFeature panelLogoBacklightFeature, PortsBacklightFeature portsBacklightFeature, FnKeysDisabler fnKeysDisabler) : AbstractWMIListener(WMI.LenovoLightingEvent .Listen) { public class ChangedEventArgs(LightingChangeState state) : EventArgs { public LightingChangeState State { get; } = state; } protected override LightingChangeState GetValue(int value) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Event received. [value={value}]"); var result = (LightingChangeState)value; return result; } protected override ChangedEventArgs GetEventArgs(LightingChangeState value) => new(value); protected override async Task OnChangedAsync(LightingChangeState value) { try { if (await fnKeysDisabler.GetStatusAsync().ConfigureAwait(false) == SoftwareStatus.Enabled) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Ignoring, FnKeys are enabled."); return; } switch (value) { case LightingChangeState.Panel when await panelLogoBacklightFeature.IsSupportedAsync().ConfigureAwait(false): { var type = await panelLogoBacklightFeature.GetStateAsync().ConfigureAwait(false) == PanelLogoBacklightState.On ? NotificationType.PanelLogoLightingOn : NotificationType.PanelLogoLightingOff; MessagingCenter.Publish(new NotificationMessage(type)); break; } case LightingChangeState.Ports when await portsBacklightFeature.IsSupportedAsync().ConfigureAwait(false): { var type = await portsBacklightFeature.GetStateAsync().ConfigureAwait(false) == PortsBacklightState.On ? NotificationType.PortLightingOn : NotificationType.PortLightingOff; MessagingCenter.Publish(new NotificationMessage(type)); break; } } } catch { /* Ignored. */ } } } ================================================ FILE: LenovoLegionToolkit.Lib/Listeners/NativeWindowsMessageListener.cs ================================================ using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading.Tasks; using System.Windows.Forms; using LenovoLegionToolkit.Lib.Controllers; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Features; using LenovoLegionToolkit.Lib.Features.Hybrid.Notify; using LenovoLegionToolkit.Lib.Messaging; using LenovoLegionToolkit.Lib.Messaging.Messages; using LenovoLegionToolkit.Lib.System; using LenovoLegionToolkit.Lib.Utils; using Windows.Win32; using Windows.Win32.Foundation; using Windows.Win32.System.Power; using Windows.Win32.UI.Input.KeyboardAndMouse; using Windows.Win32.UI.WindowsAndMessaging; namespace LenovoLegionToolkit.Lib.Listeners; public class NativeWindowsMessageListener : NativeWindow, IListener { public class ChangedEventArgs(NativeWindowsMessage message, object? data = null) : EventArgs { public NativeWindowsMessage Message { get; } = message; public object? Data { get; } = data; } private readonly IMainThreadDispatcher _mainThreadDispatcher; private readonly DGPUNotify _dgpuNotify; private readonly SmartFnLockController _smartFnLockController; private readonly PowerModeFeature _powerModeFeature; private readonly HOOKPROC _kbProc; private readonly TaskCompletionSource _isMonitorOnTaskCompletionSource = new(); private readonly TaskCompletionSource _isLidOpenTaskCompletionSource = new(); private HDEVNOTIFY _deviceNotificationHandle; private HPOWERNOTIFY _consoleDisplayStateNotificationHandle; private HPOWERNOTIFY _lidSwitchStateChangeNotificationHandle; private HPOWERNOTIFY _powerSavingStateChangeNotificationHandle; private HHOOK _kbHook; public bool IsMonitorOn { get; private set; } public bool IsLidOpen { get; private set; } public event EventHandler? Changed; public NativeWindowsMessageListener(IMainThreadDispatcher mainThreadDispatcher, DGPUNotify dgpuNotify, SmartFnLockController smartFnLockController, PowerModeFeature powerModeFeature) { _mainThreadDispatcher = mainThreadDispatcher; _dgpuNotify = dgpuNotify; _smartFnLockController = smartFnLockController; _powerModeFeature = powerModeFeature; _kbProc = LowLevelKeyboardProc; } public async Task TurnOffMonitorAsync() { await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false); await _mainThreadDispatcher.DispatchAsync(() => { PInvoke.SendMessage(new HWND(Handle), PInvoke.WM_SYSCOMMAND, new WPARAM(PInvoke.SC_MONITORPOWER), new LPARAM(2)); return Task.CompletedTask; }).ConfigureAwait(false); } public Task StartAsync() => _mainThreadDispatcher.DispatchAsync(() => { CreateHandle(new CreateParams { Caption = "LenovoLegionToolkit_MessageWindow", Parent = new IntPtr(-3) }); _kbHook = PInvoke.SetWindowsHookEx(WINDOWS_HOOK_ID.WH_KEYBOARD_LL, _kbProc, HINSTANCE.Null, 0); _deviceNotificationHandle = RegisterDeviceNotification(Handle); _consoleDisplayStateNotificationHandle = RegisterPowerNotification(PInvoke.GUID_CONSOLE_DISPLAY_STATE); _lidSwitchStateChangeNotificationHandle = RegisterPowerNotification(PInvoke.GUID_LIDSWITCH_STATE_CHANGE); _powerSavingStateChangeNotificationHandle = RegisterPowerNotification(PInvoke.GUID_POWER_SAVING_STATUS); return WaitForInit(); }); public Task StopAsync() => _mainThreadDispatcher.DispatchAsync(() => { PInvoke.UnhookWindowsHookEx(_kbHook); PInvoke.UnregisterDeviceNotification(_deviceNotificationHandle); PInvoke.UnregisterPowerSettingNotification(_consoleDisplayStateNotificationHandle); PInvoke.UnregisterPowerSettingNotification(_lidSwitchStateChangeNotificationHandle); PInvoke.UnregisterPowerSettingNotification(_powerSavingStateChangeNotificationHandle); _kbHook = default; _deviceNotificationHandle = default; _consoleDisplayStateNotificationHandle = default; ReleaseHandle(); return Task.CompletedTask; }); protected override unsafe void WndProc(ref Message m) { if (m.Msg == PInvoke.WM_DEVICECHANGE && m.LParam != IntPtr.Zero) { ref var devBroadcastHdr = ref Unsafe.AsRef((void*)m.LParam); if (devBroadcastHdr.dbch_devicetype == DEV_BROADCAST_HDR_DEVICE_TYPE.DBT_DEVTYP_DEVICEINTERFACE) { ref var devBroadcastDeviceInterface = ref Unsafe.AsRef((void*)m.LParam); var length = ((int)devBroadcastDeviceInterface.dbcc_size - sizeof(DEV_BROADCAST_DEVICEINTERFACE_W)) / sizeof(char); var name = devBroadcastDeviceInterface.dbcc_name.AsSpan(length).ToString(); var state = (uint)m.WParam.ToInt32(); switch (state) { case PInvoke.DBT_DEVICEARRIVAL: { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Event received: Device Arrival [name={name}]"); OnDeviceConnected(name); break; } case PInvoke.DBT_DEVICEREMOVECOMPLETE: { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Event received: Device Removal Complete [name={name}]"); OnDeviceDisconnected(name); break; } } if (devBroadcastDeviceInterface.dbcc_classguid == PInvoke.GUID_DISPLAY_DEVICE_ARRIVAL) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Event received: Display Device Arrival"); OnDisplayDeviceArrival(); } if (devBroadcastDeviceInterface.dbcc_classguid == PInvoke.GUID_DEVINTERFACE_MONITOR) { var id = InternalDisplay.Get(); var isExternal = !name.Equals(id?.DevicePath, StringComparison.Ordinal); switch (state) { case PInvoke.DBT_DEVICEARRIVAL: { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Event received: Monitor Connected"); OnMonitorConnected(isExternal); break; } case PInvoke.DBT_DEVICEREMOVECOMPLETE: { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Event received: Monitor Disconnected"); OnMonitorDisconnected(isExternal); break; } } } } } if (m.Msg == PInvoke.WM_POWERBROADCAST && m.WParam == (IntPtr)PInvoke.PBT_POWERSETTINGCHANGE && m.LParam != IntPtr.Zero) { ref var str = ref Unsafe.AsRef((void*)m.LParam); if (str.PowerSetting == PInvoke.GUID_CONSOLE_DISPLAY_STATE) { var state = (PInvokeExtensions.CONSOLE_DISPLAY_STATE)str.Data[0]; switch (state) { case PInvokeExtensions.CONSOLE_DISPLAY_STATE.On: { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Event received: Monitor On"); OnMonitorOn(); break; } case PInvokeExtensions.CONSOLE_DISPLAY_STATE.Off: { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Event received: Monitor Off"); OnMonitorOff(); break; } } } if (str.PowerSetting == PInvoke.GUID_LIDSWITCH_STATE_CHANGE) { var isOpened = str.Data[0] != 0; if (isOpened) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Event received: Lid Opened"); OnLidOpened(); } else { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Event received: Lid Closed"); OnLidClosed(); } } if (str.PowerSetting == PInvoke.GUID_POWER_SAVING_STATUS && str.Data[0] == 0) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Event received: Battery Saver enabled"); OnBatterySaverEnabled(); } } base.WndProc(ref m); } private async Task WaitForInit() { var delayTask = Task.Delay(TimeSpan.FromSeconds(3)); var task = Task.WhenAll( _isMonitorOnTaskCompletionSource.Task, _isLidOpenTaskCompletionSource.Task ); var completed = await Task.WhenAny(task, delayTask).ConfigureAwait(false); if (completed == delayTask) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Delay expired, state might be inconsistent! [IsMonitorOn={IsMonitorOn}, IsLidOpen={IsLidOpen}]"); } } private void OnMonitorOn() { IsMonitorOn = true; _isMonitorOnTaskCompletionSource.TrySetResult(); RaiseChanged(NativeWindowsMessage.MonitorOn); } private void OnMonitorOff() { IsMonitorOn = false; _isMonitorOnTaskCompletionSource.TrySetResult(); RaiseChanged(NativeWindowsMessage.MonitorOff); } private void OnLidOpened() { IsLidOpen = true; _isLidOpenTaskCompletionSource.TrySetResult(); RaiseChanged(NativeWindowsMessage.LidOpened); } private void OnLidClosed() { IsLidOpen = false; _isLidOpenTaskCompletionSource.TrySetResult(); RaiseChanged(NativeWindowsMessage.LidClosed); } private void OnBatterySaverEnabled() { Task.Run(_powerModeFeature.EnsureCorrectWindowsPowerSettingsAreSetAsync); RaiseChanged(NativeWindowsMessage.BatterySaverEnabled); } private void OnDeviceConnected(string name) { RaiseChanged(NativeWindowsMessage.DeviceConnected, ConvertDeviceNameToDeviceInstanceId(name)); } private void OnDeviceDisconnected(string name) { RaiseChanged(NativeWindowsMessage.DeviceDisconnected, ConvertDeviceNameToDeviceInstanceId(name)); } private void OnMonitorConnected(bool isExternal) { RaiseChanged(NativeWindowsMessage.MonitorConnected); if (isExternal) RaiseChanged(NativeWindowsMessage.ExternalMonitorConnected); } private void OnMonitorDisconnected(bool isExternal) { RaiseChanged(NativeWindowsMessage.MonitorDisconnected); if (isExternal) RaiseChanged(NativeWindowsMessage.ExternalMonitorDisconnected); } private void OnDisplayDeviceArrival() { Task.Run(async () => { if (await _dgpuNotify.IsSupportedAsync().ConfigureAwait(false)) await _dgpuNotify.NotifyAsync().ConfigureAwait(false); }); RaiseChanged(NativeWindowsMessage.OnDisplayDeviceArrival); } private void RaiseChanged(NativeWindowsMessage message, object? data = null) => Changed?.Invoke(this, new ChangedEventArgs(message, data)); private unsafe LRESULT LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) { if (nCode != PInvoke.HC_ACTION) return PInvoke.CallNextHookEx(HHOOK.Null, nCode, wParam, lParam); ref var kbStruct = ref Unsafe.AsRef((void*)lParam.Value); _smartFnLockController.OnKeyboardEvent(wParam.Value, kbStruct); if (wParam.Value != PInvoke.WM_KEYUP) return PInvoke.CallNextHookEx(HHOOK.Null, nCode, wParam, lParam); if (kbStruct.vkCode == (ulong)VIRTUAL_KEY.VK_CAPITAL) { var isOn = (PInvoke.GetKeyState((int)VIRTUAL_KEY.VK_CAPITAL) & 0x1) != 0; var type = isOn ? NotificationType.CapsLockOn : NotificationType.CapsLockOff; MessagingCenter.Publish(new NotificationMessage(type)); } if (kbStruct.vkCode == (ulong)VIRTUAL_KEY.VK_NUMLOCK) { var isOn = (PInvoke.GetKeyState((int)VIRTUAL_KEY.VK_NUMLOCK) & 0x1) != 0; var type = isOn ? NotificationType.NumLockOn : NotificationType.NumLockOff; MessagingCenter.Publish(new NotificationMessage(type)); } return PInvoke.CallNextHookEx(HHOOK.Null, nCode, wParam, lParam); } private static unsafe HDEVNOTIFY RegisterDeviceNotification(IntPtr handle) { var ptr = IntPtr.Zero; try { var str = new DEV_BROADCAST_DEVICEINTERFACE_W(); str.dbcc_size = (uint)Marshal.SizeOf(str); str.dbcc_devicetype = (uint)DEV_BROADCAST_HDR_DEVICE_TYPE.DBT_DEVTYP_DEVICEINTERFACE; ptr = Marshal.AllocHGlobal(Marshal.SizeOf(str)); Marshal.StructureToPtr(str, ptr, true); return PInvoke.RegisterDeviceNotification(new HANDLE(handle), ptr.ToPointer(), REGISTER_NOTIFICATION_FLAGS.DEVICE_NOTIFY_WINDOW_HANDLE | REGISTER_NOTIFICATION_FLAGS.DEVICE_NOTIFY_ALL_INTERFACE_CLASSES); } finally { Marshal.FreeHGlobal(ptr); } } private unsafe HPOWERNOTIFY RegisterPowerNotification(Guid guid) { return PInvoke.RegisterPowerSettingNotification(new HANDLE(Handle), &guid, 0); } private static string? ConvertDeviceNameToDeviceInstanceId(string name) { var parts = name.Split('#'); if (parts.Length < 3) return null; var part1 = parts[0].TrimStart('\\', '?'); var part2 = parts[1].Replace('#', '\\'); var part3 = parts[2]; return $@"{part1}\{part2}\{part3}".ToUpperInvariant(); } } ================================================ FILE: LenovoLegionToolkit.Lib/Listeners/PowerModeListener.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Controllers; using LenovoLegionToolkit.Lib.Controllers.GodMode; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Messaging; using LenovoLegionToolkit.Lib.Messaging.Messages; using LenovoLegionToolkit.Lib.System.Management; namespace LenovoLegionToolkit.Lib.Listeners; public class PowerModeListener( GodModeController godModeController, WindowsPowerModeController windowsPowerModeController, WindowsPowerPlanController windowsPowerPlanController) : AbstractWMIListener(WMI.LenovoGameZoneSmartFanModeEvent.Listen), INotifyingListener { public class ChangedEventArgs(PowerModeState state) : EventArgs { public PowerModeState State { get; } = state; } protected override PowerModeState GetValue(int value) { var result = (PowerModeState)(value - 1); return result; } protected override ChangedEventArgs GetEventArgs(PowerModeState value) => new(value); protected override async Task OnChangedAsync(PowerModeState value) { await ChangeDependenciesAsync(value).ConfigureAwait(false); PublishNotification(value); } public async Task NotifyAsync(PowerModeState value) { await ChangeDependenciesAsync(value).ConfigureAwait(false); RaiseChanged(value); } private async Task ChangeDependenciesAsync(PowerModeState value) { if (value is PowerModeState.GodMode) await godModeController.ApplyStateAsync().ConfigureAwait(false); await windowsPowerModeController.SetPowerModeAsync(value).ConfigureAwait(false); await windowsPowerPlanController.SetPowerPlanAsync(value).ConfigureAwait(false); } private static void PublishNotification(PowerModeState value) { switch (value) { case PowerModeState.Quiet: MessagingCenter.Publish(new NotificationMessage(NotificationType.PowerModeQuiet, value.GetDisplayName())); break; case PowerModeState.Balance: MessagingCenter.Publish(new NotificationMessage(NotificationType.PowerModeBalance, value.GetDisplayName())); break; case PowerModeState.Performance: MessagingCenter.Publish(new NotificationMessage(NotificationType.PowerModePerformance, value.GetDisplayName())); break; case PowerModeState.GodMode: MessagingCenter.Publish(new NotificationMessage(NotificationType.PowerModeGodMode, value.GetDisplayName())); break; } } } ================================================ FILE: LenovoLegionToolkit.Lib/Listeners/PowerStateListener.cs ================================================ using System; using System.Runtime.InteropServices; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Controllers; using LenovoLegionToolkit.Lib.Features; using LenovoLegionToolkit.Lib.Features.Hybrid.Notify; using LenovoLegionToolkit.Lib.Messaging; using LenovoLegionToolkit.Lib.Messaging.Messages; using LenovoLegionToolkit.Lib.System; using LenovoLegionToolkit.Lib.Utils; using Microsoft.Win32; using Windows.Win32; using Windows.Win32.Foundation; using Windows.Win32.System.Power; using Windows.Win32.UI.WindowsAndMessaging; namespace LenovoLegionToolkit.Lib.Listeners; public class PowerStateListener : IListener { public class ChangedEventArgs(PowerStateEvent powerStateEvent, bool powerAdapterStateChanged) : EventArgs { public PowerStateEvent PowerStateEvent { get; } = powerStateEvent; public bool PowerAdapterStateChanged { get; } = powerAdapterStateChanged; } private readonly SafeHandle _recipientHandle; // ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable private readonly PDEVICE_NOTIFY_CALLBACK_ROUTINE _callback; private readonly PowerModeFeature _powerModeFeature; private readonly BatteryFeature _batteryFeature; private readonly DGPUNotify _dgpuNotify; private readonly RGBKeyboardBacklightController _rgbController; private bool _started; private HPOWERNOTIFY _handle; private PowerAdapterStatus? _lastPowerAdapterState; public event EventHandler? Changed; public unsafe PowerStateListener(PowerModeFeature powerModeFeature, BatteryFeature batteryFeature, DGPUNotify dgpuNotify, RGBKeyboardBacklightController rgbController) { _powerModeFeature = powerModeFeature; _batteryFeature = batteryFeature; _dgpuNotify = dgpuNotify; _rgbController = rgbController; _callback = Callback; _recipientHandle = new StructSafeHandle(new DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS { Callback = _callback, Context = null, }); } public async Task StartAsync() { if (_started) return; _lastPowerAdapterState = await Power.IsPowerAdapterConnectedAsync().ConfigureAwait(false); SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged; RegisterSuspendResumeNotification(); _started = true; } public Task StopAsync() { SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged; UnRegisterSuspendResumeNotification(); _started = false; return Task.CompletedTask; } private async void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Event received: {e.Mode}"); var powerMode = e.Mode switch { PowerModes.StatusChange => PowerStateEvent.StatusChange, PowerModes.Resume => PowerStateEvent.Resume, PowerModes.Suspend => PowerStateEvent.Suspend, _ => PowerStateEvent.Unknown }; if (powerMode is PowerStateEvent.Unknown) return; await HandleAsync(powerMode).ConfigureAwait(false); } private unsafe uint Callback(void* context, uint type, void* setting) { _ = Task.Run(() => CallbackAsync(type)); return (uint)WIN32_ERROR.ERROR_SUCCESS; } private async Task CallbackAsync(uint type) { var mi = await Compatibility.GetMachineInformationAsync().ConfigureAwait(false); if (!mi.Properties.SupportsAlwaysOnAc.status) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Ignoring, AO AC not enabled..."); return; } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Event value: {type}"); var powerMode = type switch { PInvoke.PBT_APMRESUMEAUTOMATIC => PowerStateEvent.Resume, _ => PowerStateEvent.Unknown }; if (powerMode is not PowerStateEvent.Resume) return; await HandleAsync(powerMode).ConfigureAwait(false); } private async Task HandleAsync(PowerStateEvent powerStateEvent) { var powerAdapterState = await Power.IsPowerAdapterConnectedAsync().ConfigureAwait(false); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Handle {powerStateEvent}. [newState={powerAdapterState}]"); if (powerStateEvent is PowerStateEvent.Resume) { _ = Task.Run(async () => { if (await _batteryFeature.IsSupportedAsync().ConfigureAwait(false)) await _batteryFeature.EnsureCorrectBatteryModeIsSetAsync().ConfigureAwait(false); if (await _rgbController.IsSupportedAsync().ConfigureAwait(false)) await _rgbController.SetLightControlOwnerAsync(true, true).ConfigureAwait(false); if (await _powerModeFeature.IsSupportedAsync().ConfigureAwait(false)) { await _powerModeFeature.EnsureCorrectWindowsPowerSettingsAreSetAsync().ConfigureAwait(false); await _powerModeFeature.EnsureGodModeStateIsAppliedAsync().ConfigureAwait(false); } if (await _dgpuNotify.IsSupportedAsync().ConfigureAwait(false)) { await Task.Delay(TimeSpan.FromSeconds(5)).ConfigureAwait(false); await _dgpuNotify.NotifyAsync().ConfigureAwait(false); } }); } if (powerStateEvent is PowerStateEvent.StatusChange && powerAdapterState is PowerAdapterStatus.Connected) { _ = Task.Run(async () => { if (await _powerModeFeature.IsSupportedAsync().ConfigureAwait(false)) await _powerModeFeature.EnsureGodModeStateIsAppliedAsync().ConfigureAwait(false); if (await _dgpuNotify.IsSupportedAsync().ConfigureAwait(false)) { await Task.Delay(TimeSpan.FromSeconds(5)).ConfigureAwait(false); await _dgpuNotify.NotifyAsync().ConfigureAwait(false); } }); } var powerAdapterStateChanged = powerAdapterState != _lastPowerAdapterState; _lastPowerAdapterState = powerAdapterState; if (powerStateEvent is PowerStateEvent.Suspend or PowerStateEvent.Unknown) return; if (powerAdapterStateChanged) Notify(powerAdapterState); Changed?.Invoke(this, new(powerStateEvent, powerAdapterStateChanged)); } private unsafe void RegisterSuspendResumeNotification() { _handle = PInvoke.PowerRegisterSuspendResumeNotification(REGISTER_NOTIFICATION_FLAGS.DEVICE_NOTIFY_CALLBACK, _recipientHandle, out var handle) == WIN32_ERROR.ERROR_SUCCESS ? new HPOWERNOTIFY(new IntPtr(handle)) : HPOWERNOTIFY.Null; } private void UnRegisterSuspendResumeNotification() { PInvoke.PowerUnregisterSuspendResumeNotification(_handle); _handle = HPOWERNOTIFY.Null; } private static void Notify(PowerAdapterStatus newState) { switch (newState) { case PowerAdapterStatus.Connected: MessagingCenter.Publish(new NotificationMessage(NotificationType.ACAdapterConnected)); break; case PowerAdapterStatus.ConnectedLowWattage: MessagingCenter.Publish(new NotificationMessage(NotificationType.ACAdapterConnectedLowWattage)); break; case PowerAdapterStatus.Disconnected: MessagingCenter.Publish(new NotificationMessage(NotificationType.ACAdapterDisconnected)); break; } } } ================================================ FILE: LenovoLegionToolkit.Lib/Listeners/RGBKeyboardBacklightListener.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Controllers; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Messaging; using LenovoLegionToolkit.Lib.Messaging.Messages; using LenovoLegionToolkit.Lib.System.Management; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.Listeners; public class RGBKeyboardBacklightListener(RGBKeyboardBacklightController controller) : AbstractWMIListener(WMI.LenovoGameZoneLightProfileChangeEvent.Listen) { protected override RGBKeyboardBacklightChanged GetValue(int value) => default; protected override EventArgs GetEventArgs(RGBKeyboardBacklightChanged value) => EventArgs.Empty; protected override async Task OnChangedAsync(RGBKeyboardBacklightChanged value) { try { if (!await controller.IsSupportedAsync().ConfigureAwait(false)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Not supported."); return; } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Taking ownership..."); await controller.SetLightControlOwnerAsync(true).ConfigureAwait(false); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Setting next preset set..."); var preset = await controller.SetNextPresetAsync().ConfigureAwait(false); MessagingCenter.Publish(preset == RGBKeyboardBacklightPreset.Off ? new NotificationMessage(NotificationType.RGBKeyboardBacklightOff, preset.GetDisplayName()) : new NotificationMessage(NotificationType.RGBKeyboardBacklightChanged, preset.GetDisplayName())); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Next preset set"); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to set next keyboard backlight preset.", ex); } } } ================================================ FILE: LenovoLegionToolkit.Lib/Listeners/SessionLockUnlockListener.cs ================================================ using System; using System.Runtime.InteropServices; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Utils; using Microsoft.Win32; using Windows.Win32; using Windows.Win32.Foundation; using Windows.Win32.System.RemoteDesktop; namespace LenovoLegionToolkit.Lib.Listeners; public class SessionLockUnlockListener : IListener { public class ChangedEventArgs(bool locked) : EventArgs { public bool Locked { get; } = locked; } public event EventHandler? Changed; public bool? IsLocked { get; private set; } public Task StartAsync() { SystemEvents.SessionSwitch += SystemEvents_SessionSwitch; return Task.CompletedTask; } public Task StopAsync() { SystemEvents.SessionSwitch -= SystemEvents_SessionSwitch; return Task.CompletedTask; } private void SystemEvents_SessionSwitch(object sender, SessionSwitchEventArgs e) { if (e.Reason != SessionSwitchReason.SessionLock && e.Reason != SessionSwitchReason.SessionUnlock) return; var flags = GetActiveConsoleSessionFlags(); if (flags == PInvoke.WTS_SESSIONSTATE_UNKNOWN) { IsLocked = null; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Unknown error occured when getting active console session flags."); return; } var locked = (flags == PInvoke.WTS_SESSIONSTATE_LOCK); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Session lock unlock state switched. [locked={locked}]"); IsLocked = locked; Changed?.Invoke(this, new(locked)); } private static unsafe uint GetActiveConsoleSessionFlags() { const WTS_INFO_CLASS infoClass = WTS_INFO_CLASS.WTSSessionInfoEx; var sessionFlags = PInvoke.WTS_SESSIONSTATE_UNKNOWN; var dwSessionId = PInvoke.WTSGetActiveConsoleSessionId(); if (!PInvoke.WTSQuerySessionInformation(HANDLE.WTS_CURRENT_SERVER_HANDLE, dwSessionId, infoClass, out var ppBuffer, out var pBytesReturned)) return sessionFlags; if (pBytesReturned > 0) { var info = Marshal.PtrToStructure((IntPtr)ppBuffer.Value); if (info.Level == 1) sessionFlags = (uint)info.Data.WTSInfoExLevel1.SessionFlags; } PInvoke.WTSFreeMemory(ppBuffer); return sessionFlags; } } ================================================ FILE: LenovoLegionToolkit.Lib/Listeners/SpecialKeyListener.cs ================================================ using System; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Features; using LenovoLegionToolkit.Lib.Messaging; using LenovoLegionToolkit.Lib.Messaging.Messages; using LenovoLegionToolkit.Lib.Settings; using LenovoLegionToolkit.Lib.SoftwareDisabler; using LenovoLegionToolkit.Lib.System; using LenovoLegionToolkit.Lib.System.Management; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.Listeners; public class SpecialKeyListener( ApplicationSettings settings, FnKeysDisabler fnKeysDisabler, RefreshRateFeature feature, MicrophoneFeature microphoneFeature) : AbstractWMIListener(WMI.LenovoUtilityEvent.Listen) { public class ChangedEventArgs(SpecialKey specialKey) : EventArgs { public SpecialKey SpecialKey { get; } = specialKey; } private readonly ThrottleFirstDispatcher _refreshRateDispatcher = new(TimeSpan.FromSeconds(2), nameof(SpecialKeyListener)); protected override SpecialKey GetValue(int value) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Event received. [value={value}]"); var result = (SpecialKey)value; return result; } protected override ChangedEventArgs GetEventArgs(SpecialKey value) => new(value); protected override async Task OnChangedAsync(SpecialKey value) { try { if (await fnKeysDisabler.GetStatusAsync().ConfigureAwait(false) == SoftwareStatus.Enabled) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Ignoring, FnKeys are enabled."); return; } switch (value) { case SpecialKey.CameraOn or SpecialKey.CameraOff: NotifyCameraState(value); break; case SpecialKey.FnLockOn or SpecialKey.FnLockOff: NotifyFnLockState(value); break; case SpecialKey.FnR or SpecialKey.FnR2: await ToggleRefreshRateAsync().ConfigureAwait(false); break; case SpecialKey.FnPrtSc or SpecialKey.FnPrtSc2: OpenSnippingTool(); break; case SpecialKey.SpectrumBacklightOff: NotifySpectrumBacklight(SpectrumKeyboardBacklightBrightness.Off); break; case SpecialKey.SpectrumBacklight1: NotifySpectrumBacklight(SpectrumKeyboardBacklightBrightness.Low); break; case SpecialKey.SpectrumBacklight2: NotifySpectrumBacklight(SpectrumKeyboardBacklightBrightness.Medium); break; case SpecialKey.SpectrumBacklight3: NotifySpectrumBacklight(SpectrumKeyboardBacklightBrightness.High); break; case SpecialKey.SpectrumPreset1: NotifySpectrumPreset(1); break; case SpecialKey.SpectrumPreset2: NotifySpectrumPreset(2); break; case SpecialKey.SpectrumPreset3: NotifySpectrumPreset(3); break; case SpecialKey.SpectrumPreset4: NotifySpectrumPreset(4); break; case SpecialKey.SpectrumPreset5: NotifySpectrumPreset(5); break; case SpecialKey.SpectrumPreset6: NotifySpectrumPreset(6); break; case SpecialKey.FnF4: await ToggleMicrophoneAsync().ConfigureAwait(false); break; case SpecialKey.FnF8: OpenAirplaneModeSettings(); break; case SpecialKey.WhiteBacklightOff: NotifyWhiteBacklight(WhiteKeyboardBacklightState.Off); break; case SpecialKey.WhiteBacklight1: NotifyWhiteBacklight(WhiteKeyboardBacklightState.Low); break; case SpecialKey.WhiteBacklight2: NotifyWhiteBacklight(WhiteKeyboardBacklightState.High); break; } } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to handle key. [key={value}, value={(int)value}]", ex); } } private static void NotifyCameraState(SpecialKey value) { switch (value) { case SpecialKey.CameraOn: MessagingCenter.Publish(new NotificationMessage(NotificationType.CameraOn)); break; case SpecialKey.CameraOff: MessagingCenter.Publish(new NotificationMessage(NotificationType.CameraOff)); break; } } private static void NotifyFnLockState(SpecialKey value) { switch (value) { case SpecialKey.FnLockOn: MessagingCenter.Publish(new NotificationMessage(NotificationType.FnLockOn)); break; case SpecialKey.FnLockOff: MessagingCenter.Publish(new NotificationMessage(NotificationType.FnLockOff)); break; } } private Task ToggleRefreshRateAsync() => _refreshRateDispatcher.DispatchAsync(async () => { try { if (!await feature.IsSupportedAsync().ConfigureAwait(false)) return; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Switch refresh rate after Fn+R..."); var all = await feature.GetAllStatesAsync().ConfigureAwait(false); var current = await feature.GetStateAsync().ConfigureAwait(false); var excluded = settings.Store.ExcludedRefreshRates; var filtered = all.Except(excluded).ToArray(); if (Log.Instance.IsTraceEnabled) { Log.Instance.Trace($"Refresh rates: [all={string.Join(", ", all.Select(r => r.Frequency))}]"); Log.Instance.Trace($" - All: {string.Join(", ", all.Select(r => r.Frequency))}"); Log.Instance.Trace($" - Excluded: {string.Join(", ", excluded.Select(r => r.Frequency))}"); Log.Instance.Trace($" - Filtered: {string.Join(", ", filtered.Select(r => r.Frequency))}"); } if (filtered.Length < 2) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Can't switch refresh rate after Fn+R when there is less than 2 available."); return; } var currentIndex = Array.IndexOf(filtered, current); var newIndex = currentIndex + 1; if (newIndex >= filtered.Length) newIndex = 0; var next = filtered[newIndex]; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Switching refresh rate after Fn+R to {next}..."); await feature.SetStateAsync(next).ConfigureAwait(false); _ = Task.Delay(TimeSpan.FromSeconds(1)).ContinueWith(_ => { MessagingCenter.Publish(new NotificationMessage(NotificationType.RefreshRate, next.DisplayName)); }); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Switched refresh rate after Fn+R to {next}."); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to switch refresh rate after Fn+R.", ex); } }); private static void OpenSnippingTool() { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Starting snipping tool.."); Process.Start("explorer", "ms-screenclip:"); } private static void NotifySpectrumBacklight(SpectrumKeyboardBacklightBrightness value) { var type = value is SpectrumKeyboardBacklightBrightness.Off ? NotificationType.SpectrumBacklightOff : NotificationType.SpectrumBacklightChanged; MessagingCenter.Publish(new NotificationMessage(type, value)); } private static void NotifySpectrumPreset(int value) => MessagingCenter.Publish(new NotificationMessage(NotificationType.SpectrumBacklightPresetChanged, value)); private async Task ToggleMicrophoneAsync() { if (!await microphoneFeature.IsSupportedAsync().ConfigureAwait(false)) return; switch (await microphoneFeature.GetStateAsync().ConfigureAwait(false)) { case MicrophoneState.On: await microphoneFeature.SetStateAsync(MicrophoneState.Off).ConfigureAwait(false); MessagingCenter.Publish(new NotificationMessage(NotificationType.MicrophoneOff)); break; case MicrophoneState.Off: await microphoneFeature.SetStateAsync(MicrophoneState.On).ConfigureAwait(false); MessagingCenter.Publish(new NotificationMessage(NotificationType.MicrophoneOn)); break; } } private static void OpenAirplaneModeSettings() => AirplaneMode.Open(); private static void NotifyWhiteBacklight(WhiteKeyboardBacklightState value) { var type = value is WhiteKeyboardBacklightState.Off ? NotificationType.WhiteKeyboardBacklightOff : NotificationType.WhiteKeyboardBacklightChanged; MessagingCenter.Publish(new NotificationMessage(type, value)); } } ================================================ FILE: LenovoLegionToolkit.Lib/Listeners/SystemThemeListener.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.System; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.Listeners; public class SystemThemeListener : IListener { public event EventHandler? Changed; private IDisposable? _darkModeListener; private IDisposable? _colorizationColorListener; private RGBColor? _currentRegColor; private bool _started; public Task StartAsync() { if (_started) return Task.CompletedTask; _darkModeListener = SystemTheme.GetDarkModeListener(OnDarkModeChanged); _colorizationColorListener = SystemTheme.GetColorizationColorListener(OnColorizationColorChanged); _started = true; return Task.CompletedTask; } private void OnDarkModeChanged() { Changed?.Invoke(this, EventArgs.Empty); } private void OnColorizationColorChanged() { try { var color = SystemTheme.GetColorizationColor(); // Ignore alpha channel transition events if (color.Equals(_currentRegColor)) return; _currentRegColor = color; Changed?.Invoke(this, EventArgs.Empty); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to notify on accent color change.", ex); } } public Task StopAsync() { _darkModeListener?.Dispose(); _colorizationColorListener?.Dispose(); _started = false; return Task.CompletedTask; } } ================================================ FILE: LenovoLegionToolkit.Lib/Listeners/ThermalModeListener.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Controllers; using LenovoLegionToolkit.Lib.System.Management; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.Listeners; public class ThermalModeListener( WindowsPowerModeController windowsPowerModeController, WindowsPowerPlanController windowsPowerPlanController) : AbstractWMIListener(WMI.LenovoGameZoneThermalModeEvent.Listen) { public class ChangedEventArgs(ThermalModeState state) : EventArgs { public ThermalModeState State { get; } = state; } private readonly ThreadSafeCounter _suppressCounter = new(); protected override ThermalModeState GetValue(int value) { var state = (ThermalModeState)value; if (!Enum.IsDefined(state)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Unknown value received: {value}"); state = ThermalModeState.Unknown; } return state; } protected override ChangedEventArgs GetEventArgs(ThermalModeState value) => new(value); protected override async Task OnChangedAsync(ThermalModeState state) { if (!_suppressCounter.Decrement()) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Suppressed."); return; } if (state == ThermalModeState.Unknown) return; var powerModeState = state switch { ThermalModeState.Quiet => PowerModeState.Quiet, ThermalModeState.Balance => PowerModeState.Balance, ThermalModeState.Performance => PowerModeState.Performance, ThermalModeState.GodMode => PowerModeState.GodMode, _ => throw new ArgumentOutOfRangeException(nameof(state), state, null) }; await windowsPowerModeController.SetPowerModeAsync(powerModeState).ConfigureAwait(false); await windowsPowerPlanController.SetPowerPlanAsync(powerModeState).ConfigureAwait(false); } public void SuppressNext() { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Suppressing next..."); _suppressCounter.Increment(); } } ================================================ FILE: LenovoLegionToolkit.Lib/Listeners/WinKeyListener.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.System.Management; namespace LenovoLegionToolkit.Lib.Listeners; public class WinKeyListener() : AbstractWMIListener(WMI.LenovoGameZoneKeyLockStatusEvent.Listen) { protected override WinKeyChanged GetValue(int value) => default; protected override EventArgs GetEventArgs(WinKeyChanged value) => EventArgs.Empty; protected override Task OnChangedAsync(WinKeyChanged value) => Task.CompletedTask; } ================================================ FILE: LenovoLegionToolkit.Lib/Messaging/Messages/FeatureStateMessage.cs ================================================ namespace LenovoLegionToolkit.Lib.Messaging.Messages; public readonly struct FeatureStateMessage(T state) : IMessage { public T State { get; } = state; } ================================================ FILE: LenovoLegionToolkit.Lib/Messaging/Messages/IMessage.cs ================================================ namespace LenovoLegionToolkit.Lib.Messaging.Messages; public interface IMessage; ================================================ FILE: LenovoLegionToolkit.Lib/Messaging/Messages/NotificationMessage.cs ================================================ namespace LenovoLegionToolkit.Lib.Messaging.Messages; public readonly struct NotificationMessage(NotificationType type, params object[] args) : IMessage { public NotificationType Type { get; } = type; public object[] Args { get; } = args; public override string ToString() => $@"{nameof(Type)}: {Type}, {nameof(Args)}: [{string.Join(", ", Args)}]"; } ================================================ FILE: LenovoLegionToolkit.Lib/Messaging/Messages/RGBKeyboardBacklightChangedMessage.cs ================================================ namespace LenovoLegionToolkit.Lib.Messaging.Messages; public class RGBKeyboardBacklightChangedMessage : IMessage; ================================================ FILE: LenovoLegionToolkit.Lib/Messaging/Messages/SpectrumBacklightChangedMessage.cs ================================================ namespace LenovoLegionToolkit.Lib.Messaging.Messages; public class SpectrumBacklightChangedMessage : IMessage; ================================================ FILE: LenovoLegionToolkit.Lib/Messaging/MessagingCenter.cs ================================================ using System; using LenovoLegionToolkit.Lib.Messaging.Messages; using PubSub; namespace LenovoLegionToolkit.Lib.Messaging; public static class MessagingCenter { public static void Publish(T data) where T : IMessage => Hub.Default.Publish(data); public static void Subscribe(object subscriber, Action handler) where T : IMessage => Hub.Default.Subscribe(subscriber, handler); public static void Subscribe(object subscriber, Action handler) where T : IMessage => Hub.Default.Subscribe(subscriber, _ => handler()); } ================================================ FILE: LenovoLegionToolkit.Lib/Native.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; namespace LenovoLegionToolkit.Lib; // ReSharper disable InconsistentNaming // ReSharper disable PrivateFieldCanBeConvertedToLocalVariable // ReSharper disable CollectionNeverQueried.Local #region Battery [StructLayout(LayoutKind.Sequential)] internal readonly struct LENOVO_BATTERY_INFORMATION { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 13)] private readonly byte[] Bytes1; public readonly ushort Temperature; public readonly ushort ManufactureDate; public readonly ushort FirstUseDate; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] private readonly byte[] Bytes2; } #endregion #region RGB Keyboard [StructLayout(LayoutKind.Sequential, Pack = 1)] internal struct LENOVO_RGB_KEYBOARD_STATE { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] public byte[] Header; public byte Effect; public byte Speed; public byte Brightness; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] public byte[] Zone1Rgb; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] public byte[] Zone2Rgb; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] public byte[] Zone3Rgb; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] public byte[] Zone4Rgb; public byte Padding; public byte WaveLTR; public byte WaveRTL; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 13)] public byte[] Unused; } #endregion #region Spectrum Keyboard internal enum LENOVO_SPECTRUM_OPERATION_TYPE : byte { Compatibility = 0xD1, KeyCount = 0xC4, KeyPage = 0xC5, ProfileChange = 0xC8, ProfileDefault = 0xC9, Profile = 0xCA, EffectChange = 0xCB, Effect = 0xCC, GetBrightness = 0xCD, Brightness = 0xCE, AuroraStartStop = 0xD0, AuroraSendBitmap = 0xA1, GetLogoStatus = 0xA5, LogoStatus = 0xA6 } internal enum LENOVO_SPECTRUM_EFFECT_TYPE : byte { ScrewRainbow = 1, RainbowWave = 2, ColorChange = 3, ColorPulse = 4, ColorWave = 5, Smooth = 6, Rain = 7, Ripple = 8, AudioBounceLighting = 9, AudioRippleLighting = 10, Always = 11, TypeLighting = 12, LegionAuraSync = 13, } internal enum LENOVO_SPECTRUM_COLOR_MODE : byte { None = 0, RandomColor = 1, ColorList = 2 } internal enum LENOVO_SPECTRUM_SPEED : byte { None = 0, Speed1 = 1, Speed2 = 2, Speed3 = 3 } internal enum LENOVO_SPECTRUM_CLOCKWISE_DIRECTION : byte { None = 0, Clockwise = 1, CounterClockwise = 2 } internal enum LENOVO_SPECTRUM_DIRECTION : byte { None = 0, BottomToTop = 1, TopToBottom = 2, RightToLeft = 3, LeftToRight = 4 } [StructLayout(LayoutKind.Sequential)] internal readonly struct LENOVO_SPECTRUM_COLOR(byte r, byte g, byte b) { public readonly byte R = r; public readonly byte G = g; public readonly byte B = b; } [StructLayout(LayoutKind.Sequential, Pack = 1)] internal struct LENOVO_SPECTRUM_KEY_STATE { public readonly ushort KeyCode; public LENOVO_SPECTRUM_COLOR Color; } [StructLayout(LayoutKind.Sequential, Pack = 1)] internal readonly struct LENOVO_SPECTRUM_KEY_PAGE_ITEM { private readonly byte Index; public readonly ushort KeyCode; } [StructLayout(LayoutKind.Sequential)] internal readonly struct LENOVO_SPECTRUM_HEADER(LENOVO_SPECTRUM_OPERATION_TYPE type, int size) { public readonly byte Head = 7; public readonly LENOVO_SPECTRUM_OPERATION_TYPE Type = type; public readonly byte Size = (byte)(size % 256); public readonly byte Tail = 3; } [StructLayout(LayoutKind.Sequential)] internal readonly struct LENOVO_SPECTRUM_EFFECT_HEADER( LENOVO_SPECTRUM_EFFECT_TYPE effectType, LENOVO_SPECTRUM_SPEED speed, LENOVO_SPECTRUM_DIRECTION direction, LENOVO_SPECTRUM_CLOCKWISE_DIRECTION clockwiseDirection, LENOVO_SPECTRUM_COLOR_MODE colorMode) { public readonly byte Head = 0x6; public readonly byte Unknown1 = 0x1; public readonly LENOVO_SPECTRUM_EFFECT_TYPE EffectType = effectType; public readonly byte Unknown2 = 0x2; public readonly LENOVO_SPECTRUM_SPEED Speed = speed; public readonly byte Unknown3 = 0x3; public readonly LENOVO_SPECTRUM_CLOCKWISE_DIRECTION ClockwiseDirection = clockwiseDirection; public readonly byte Unknown5 = 0x4; public readonly LENOVO_SPECTRUM_DIRECTION Direction = direction; public readonly byte Unknown6 = 0x5; public readonly LENOVO_SPECTRUM_COLOR_MODE ColorMode = colorMode; public readonly byte Unknown7 = 0x6; public readonly byte Tail = 0x0; } [StructLayout(LayoutKind.Sequential, Pack = 1)] internal readonly struct LENOVO_SPECTRUM_EFFECT( LENOVO_SPECTRUM_EFFECT_HEADER effectHeader, int effectNo, LENOVO_SPECTRUM_COLOR[] colors, ushort[] keyCodes) { public readonly byte EffectNo = (byte)effectNo; public readonly LENOVO_SPECTRUM_EFFECT_HEADER EffectHeader = effectHeader; public readonly byte NumberOfColors = (byte)colors.Length; public readonly LENOVO_SPECTRUM_COLOR[] Colors = colors; public readonly byte NumberOfKeys = (byte)keyCodes.Length; public readonly ushort[] KeyCodes = keyCodes; } [StructLayout(LayoutKind.Sequential, Pack = 1)] internal readonly struct LENOVO_SPECTRUM_AURORA_ITEM(ushort keyCode, LENOVO_SPECTRUM_COLOR color) { public readonly ushort KeyCode = keyCode; public readonly LENOVO_SPECTRUM_COLOR Color = color; } [StructLayout(LayoutKind.Sequential, Size = 960)] internal readonly struct LENOVO_SPECTRUM_GET_COMPATIBILITY_REQUEST() { private readonly LENOVO_SPECTRUM_HEADER Header = new(LENOVO_SPECTRUM_OPERATION_TYPE.Compatibility, 0xC0); } [StructLayout(LayoutKind.Sequential, Size = 960)] internal readonly struct LENOVO_SPECTRUM_GET_COMPATIBILITY_RESPONSE { private readonly byte ReportId; private readonly LENOVO_SPECTRUM_OPERATION_TYPE Type; private readonly byte Length; private readonly byte Unknown1; private readonly byte Compatibility; public bool IsCompatible => Compatibility == 0; } [StructLayout(LayoutKind.Sequential, Size = 960)] internal readonly struct LENOVO_SPECTRUM_GET_KEY_COUNT_REQUEST() { private readonly LENOVO_SPECTRUM_HEADER Header = new(LENOVO_SPECTRUM_OPERATION_TYPE.KeyCount, 0xC0); private readonly byte Parameter = 7; } [StructLayout(LayoutKind.Sequential, Size = 960)] internal readonly struct LENOVO_SPECTRUM_GET_KEY_COUNT_RESPONSE { private readonly byte ReportId; private readonly LENOVO_SPECTRUM_OPERATION_TYPE Type; private readonly byte Length; private readonly byte Unknown1; private readonly byte Parameter; public readonly byte Indexes; public readonly byte KeysPerIndex; } [StructLayout(LayoutKind.Sequential, Size = 960)] internal readonly struct LENOVO_SPECTRUM_GET_KEY_PAGE_REQUEST(byte index, bool secondary = false) { private readonly LENOVO_SPECTRUM_HEADER Header = new(LENOVO_SPECTRUM_OPERATION_TYPE.KeyPage, 0xC0); private readonly byte Parameter = secondary ? (byte)8 : (byte)7; private readonly byte Index = index; } [StructLayout(LayoutKind.Sequential, Size = 960)] internal readonly struct LENOVO_SPECTRUM_GET_KEY_PAGE_RESPONSE { private readonly byte ReportId; private readonly LENOVO_SPECTRUM_OPERATION_TYPE Type; private readonly byte Length; private readonly byte Unknown1; private readonly byte Parameter; private readonly byte Index; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] public readonly LENOVO_SPECTRUM_KEY_PAGE_ITEM[] Items; } [StructLayout(LayoutKind.Sequential, Size = 960)] internal readonly struct LENOVO_SPECTRUM_GET_BRIGHTNESS_REQUEST() { private readonly LENOVO_SPECTRUM_HEADER Header = new(LENOVO_SPECTRUM_OPERATION_TYPE.GetBrightness, 0xC0); } [StructLayout(LayoutKind.Sequential, Size = 960)] internal readonly struct LENOVO_SPECTRUM_GET_BRIGHTNESS_RESPONSE { private readonly byte ReportId; private readonly LENOVO_SPECTRUM_OPERATION_TYPE Type; private readonly byte Length; private readonly byte Unknown1; public readonly byte Brightness; } [StructLayout(LayoutKind.Sequential, Size = 960)] internal readonly struct LENOVO_SPECTRUM_SET_BRIGHTNESS_REQUEST(byte brightness) { private readonly LENOVO_SPECTRUM_HEADER Header = new(LENOVO_SPECTRUM_OPERATION_TYPE.Brightness, 0xC0); private readonly byte Brightness = brightness; } [StructLayout(LayoutKind.Sequential, Size = 960)] internal readonly struct LENOVO_SPECTRUM_GET_LOGO_STATUS() { private readonly LENOVO_SPECTRUM_HEADER Header = new(LENOVO_SPECTRUM_OPERATION_TYPE.GetLogoStatus, 0xC0); } [StructLayout(LayoutKind.Sequential, Size = 960)] internal readonly struct LENOVO_SPECTRUM_GET_LOGO_STATUS_RESPONSE { private readonly byte ReportId; private readonly LENOVO_SPECTRUM_OPERATION_TYPE Type; private readonly byte Length; private readonly byte Unknown1; private readonly byte Status; public bool IsOn => Status == 1; } [StructLayout(LayoutKind.Sequential, Size = 960)] internal readonly struct LENOVO_SPECTRUM_SET_LOGO_STATUS_REQUEST(bool isOn) { private readonly LENOVO_SPECTRUM_HEADER Header = new(LENOVO_SPECTRUM_OPERATION_TYPE.LogoStatus, 0xC0); private readonly byte Status = (byte)(isOn ? 1 : 0); } [StructLayout(LayoutKind.Sequential, Size = 960)] internal readonly struct LENOVO_SPECTRUM_GET_PROFILE_REQUEST() { private readonly LENOVO_SPECTRUM_HEADER Header = new(LENOVO_SPECTRUM_OPERATION_TYPE.Profile, 0xC0); } [StructLayout(LayoutKind.Sequential, Size = 960)] internal readonly struct LENOVO_SPECTRUM_GET_PROFILE_RESPONSE { private readonly byte ReportId; private readonly LENOVO_SPECTRUM_OPERATION_TYPE Type; private readonly byte Length; private readonly byte Unknown1; public readonly byte Profile; } [StructLayout(LayoutKind.Sequential, Size = 960)] internal readonly struct LENOVO_SPECTRUM_SET_PROFILE_REQUEST(byte profile) { private readonly LENOVO_SPECTRUM_HEADER Header = new(LENOVO_SPECTRUM_OPERATION_TYPE.ProfileChange, 0xC0); private readonly byte Profile = profile; } [StructLayout(LayoutKind.Sequential, Size = 960)] internal readonly struct LENOVO_SPECTRUM_SET_PROFILE_DEFAULT_REQUEST(byte profile) { private readonly LENOVO_SPECTRUM_HEADER Header = new(LENOVO_SPECTRUM_OPERATION_TYPE.ProfileDefault, 0xC0); private readonly byte Profile = profile; } [StructLayout(LayoutKind.Sequential, Size = 960)] internal readonly struct LENOVO_SPECTRUM_GET_EFFECT_REQUEST(byte profile) { private readonly LENOVO_SPECTRUM_HEADER Header = new(LENOVO_SPECTRUM_OPERATION_TYPE.Effect, 0xC0); private readonly byte Profile = profile; } internal readonly struct LENOVO_SPECTRUM_EFFECT_DESCRIPTION( LENOVO_SPECTRUM_HEADER header, byte profile, LENOVO_SPECTRUM_EFFECT[] effects) { public readonly byte Profile = profile; private readonly byte Unknown1 = 1; private readonly byte Unknown2 = 1; public readonly LENOVO_SPECTRUM_EFFECT[] Effects = effects; public static LENOVO_SPECTRUM_EFFECT_DESCRIPTION FromBytes(byte[] bytes) { using var ms = new MemoryStream(bytes); using var br = new BinaryReader(ms); _ = br.ReadByte(); var type = (LENOVO_SPECTRUM_OPERATION_TYPE)br.ReadByte(); var size = br.ReadByte(); _ = br.ReadByte(); var header = new LENOVO_SPECTRUM_HEADER(type, size); var profile = br.ReadByte(); _ = br.ReadByte(); _ = br.ReadByte(); var effects = new List(); var lastEffectNo = 1; while (true) { var effectNo = br.ReadByte(); if (effectNo < lastEffectNo) break; lastEffectNo = effectNo; _ = br.ReadByte(); _ = br.ReadByte(); var effectType = (LENOVO_SPECTRUM_EFFECT_TYPE)br.ReadByte(); _ = br.ReadByte(); var speed = (LENOVO_SPECTRUM_SPEED)br.ReadByte(); _ = br.ReadByte(); var clockwiseDirection = (LENOVO_SPECTRUM_CLOCKWISE_DIRECTION)br.ReadByte(); _ = br.ReadByte(); var direction = (LENOVO_SPECTRUM_DIRECTION)br.ReadByte(); _ = br.ReadByte(); var colorMode = (LENOVO_SPECTRUM_COLOR_MODE)br.ReadByte(); _ = br.ReadByte(); _ = br.ReadByte(); var effectHeader = new LENOVO_SPECTRUM_EFFECT_HEADER(effectType, speed, direction, clockwiseDirection, colorMode); var colors = new List(); var keyCodes = new List(); var noOfColors = br.ReadByte(); for (var i = 0; i < noOfColors; i++) { var r = br.ReadByte(); var g = br.ReadByte(); var b = br.ReadByte(); colors.Add(new(r, g, b)); } var noOfKeys = br.ReadByte(); for (var i = 0; i < noOfKeys; i++) { var keyCode = br.ReadUInt16(); keyCodes.Add(keyCode); } effects.Add(new(effectHeader, effectNo, [.. colors], [.. keyCodes])); } return new(header, profile, [.. effects]); } public byte[] ToBytes() { using var ms = new MemoryStream(new byte[960]); using var bf = new BinaryWriter(ms); bf.Write(header.Head); bf.Write((byte)header.Type); bf.Write(header.Size); bf.Write(header.Tail); bf.Write(Profile); bf.Write(Unknown1); bf.Write(Unknown2); foreach (var effect in Effects) { bf.Write(effect.EffectNo); bf.Write(effect.EffectHeader.Head); bf.Write(effect.EffectHeader.Unknown1); bf.Write((byte)effect.EffectHeader.EffectType); bf.Write(effect.EffectHeader.Unknown2); bf.Write((byte)effect.EffectHeader.Speed); bf.Write(effect.EffectHeader.Unknown3); bf.Write((byte)effect.EffectHeader.ClockwiseDirection); bf.Write(effect.EffectHeader.Unknown5); bf.Write((byte)effect.EffectHeader.Direction); bf.Write(effect.EffectHeader.Unknown6); bf.Write((byte)effect.EffectHeader.ColorMode); bf.Write(effect.EffectHeader.Unknown7); bf.Write(effect.EffectHeader.Tail); bf.Write(effect.NumberOfColors); foreach (var color in effect.Colors) { bf.Write(color.R); bf.Write(color.G); bf.Write(color.B); } bf.Write(effect.NumberOfKeys); foreach (var keyCode in effect.KeyCodes) { bf.Write(keyCode); } } var position = ms.Position; bf.Seek(2, SeekOrigin.Begin); bf.Write((byte)(position % 255)); return ms.ToArray(); } } [StructLayout(LayoutKind.Sequential, Size = 960)] internal readonly struct LENOVO_SPECTRUM_AURORA_START_STOP_REQUEST(bool start, byte profile) { private readonly LENOVO_SPECTRUM_HEADER Header = new(LENOVO_SPECTRUM_OPERATION_TYPE.AuroraStartStop, 0xC0); private readonly byte StartStop = start ? (byte)1 : (byte)2; private readonly byte Profile = profile; } internal readonly struct LENOVO_SPECTRUM_AURORA_SEND_BITMAP_REQUEST(LENOVO_SPECTRUM_AURORA_ITEM[] items) { private readonly LENOVO_SPECTRUM_HEADER Header = new(LENOVO_SPECTRUM_OPERATION_TYPE.AuroraSendBitmap, 0xC0); public byte[] ToBytes() { using var ms = new MemoryStream(new byte[960]); using var bf = new BinaryWriter(ms); bf.Write(Header.Head); bf.Write((byte)Header.Type); bf.Write(Header.Size); bf.Write(Header.Tail); foreach (var item in items) { bf.Write(item.KeyCode); bf.Write(item.Color.R); bf.Write(item.Color.G); bf.Write(item.Color.B); } return ms.ToArray(); } } [StructLayout(LayoutKind.Sequential, Size = 960)] internal readonly struct LENOVO_SPECTRUM_STATE_RESPONSE { private readonly byte ReportId; private readonly LENOVO_SPECTRUM_OPERATION_TYPE Type; private readonly byte Length; private readonly byte Unknown1; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 190)] public readonly LENOVO_SPECTRUM_KEY_STATE[] Data; } #endregion #region Boot Logo [Flags] public enum BootLogoFormat : byte { Jpeg = 0x1, Bmp = 0x10, Png = 0x20, } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct BootLogoInfo { public byte Enabled; public readonly int SupportedWidth; public readonly int SupportedHeight; public readonly BootLogoFormat SupportedFormat; } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct BootLogoChecksum { private readonly int Unused1; public uint Crc; private readonly int Unused2; private readonly int Unused3; private readonly int Unused4; private readonly int Unused5; private readonly int Unused6; private readonly int Unused7; private readonly int Unused8; private readonly int Unused9; } #endregion ================================================ FILE: LenovoLegionToolkit.Lib/NativeMethods.json ================================================ { "public": true } ================================================ FILE: LenovoLegionToolkit.Lib/NativeMethods.txt ================================================ HWND_BROADCAST WM_CLOSE WM_COMMAND WM_DESTROY WM_DEVICECHANGE WM_KEYDOWN WM_KEYUP WM_SYSKEYDOWN WM_SYSKEYUP WM_LBUTTONUP WM_POWERBROADCAST WM_SETTINGCHANGE WM_SYSCOMMAND WM_RBUTTONUP WM_USER WM_LBUTTONDOWN WM_LBUTTONUP WM_RBUTTONDOWN WM_RBUTTONUP WM_MBUTTONDOWN WM_MBUTTONUP WM_XBUTTONDOWN WM_XBUTTONUP WM_MOUSEWHEEL WM_MOUSEHWHEEL WM_MOUSEMOVE NIN_POPUPCLOSE NIN_POPUPOPEN VIRTUAL_KEY SC_MONITORPOWER DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS POWERBROADCAST_SETTING PBT_POWERSETTINGCHANGE PBT_APMRESUMEAUTOMATIC HC_ACTION FILE_ACCESS_RIGHTS KBDLLHOOKSTRUCT MSLLHOOKSTRUCT BATTERY_INFORMATION BATTERY_QUERY_INFORMATION BATTERY_STATUS BATTERY_WAIT_STATUS DEV_BROADCAST_DEVICEINTERFACE_W DEV_BROADCAST_HDR GUID_CONSOLE_DISPLAY_STATE GUID_DEVCLASS_BATTERY GUID_DISPLAY_DEVICE_ARRIVAL GUID_DEVINTERFACE_MONITOR GUID_LIDSWITCH_STATE_CHANGE GUID_VIDEO_SUBGROUP GUID_POWER_SAVING_STATUS DBT_DEVICEARRIVAL DBT_DEVICEREMOVECOMPLETE IOCTL_BATTERY_QUERY_INFORMATION IOCTL_BATTERY_QUERY_STATUS IOCTL_BATTERY_QUERY_TAG PROCESSOR_POWER_INFORMATION SC_MANAGER_ALL_ACCESS SERVICE_CHANGE_CONFIG SERVICE_NO_CHANGE DEVPKEY_NAME DEVPKEY_Device_ClassGuid DEVPKEY_Device_DeviceDesc DEVPKEY_Device_InstanceId DEVPKEY_Device_BusReportedDeviceDesc DEVPKEY_Device_Capabilities CM_DEVCAP DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY DISPLAYCONFIG_TARGET_DEVICE_NAME DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO DISPLAYCONFIG_SET_ADVANCED_COLOR_STATE LOCALE_SSHORTDATE SYSTEM_POWER_CAPABILITIES EFFECTIVE_POWER_MODE_V2 NOTIFYICONDATAW WLAN_CONNECTION_NOTIFICATION_DATA PROCESS_POWER_THROTTLING_STATE PROCESS_POWER_THROTTLING_CURRENT_VERSION PROCESS_POWER_THROTTLING_EXECUTION_SPEED GetCurrentProcess SetProcessInformation SetPriorityClass GetSystemInfo CallNtPowerInformation GetLocaleInfoEx CreateFile DeviceIoControl GetSystemPowerStatus SetupDiEnumDeviceInfo SetupDiEnumDeviceInterfaces SetupDiGetClassDevs SetupDiGetDeviceInterfaceDetail SetupDiGetDeviceProperty SetupDiGetDeviceInstanceId SetupDiGetClassDevs SetupDiClassNameFromGuid SetupDiCreateDeviceInfoList SetupDiOpenDeviceInterface HidD_FreePreparsedData HidD_GetAttributes HidD_GetFeature HidD_GetHidGuid HidD_GetPreparsedData HidD_SetFeature HidP_GetCaps CM_Get_DevNode_Status ChangeServiceConfig CloseServiceHandle OpenSCManager OpenService SHGetKnownFolderPath GetKeyState GetFirmwareEnvironmentVariableEx SetFirmwareEnvironmentVariableEx AdjustTokenPrivileges GetCurrentProcess LookupPrivilegeValue OpenProcessToken RegisterDeviceNotification UnregisterDeviceNotification RegisterPowerSettingNotification UnregisterPowerSettingNotification CallNextHookEx GetForegroundWindow SetForegroundWindow GetDesktopWindow GetShellWindow GetWindowRect SetWindowsHookEx GetWindowThreadProcessId UnhookWindowsHookEx DisplayConfigGetDeviceInfo DisplayConfigSetDeviceInfo MonitorFromPoint GetMonitorInfoW GetDpiForMonitor RegNotifyChangeKeyValue SendInput SendMessage SendNotifyMessage CallNtPowerInformation K32GetModuleFileNameExW PowerEnumerate PowerReadFriendlyName PowerGetActiveScheme PowerSetActiveScheme PowerWriteACValueIndex PowerWriteDCValueIndex PowerRegisterForEffectivePowerModeNotifications PowerUnregisterFromEffectivePowerModeNotifications PowerRegisterSuspendResumeNotification PowerUnregisterSuspendResumeNotification Shell_NotifyIcon RegisterWindowMessage WlanOpenHandle WlanCloseHandle WlanRegisterNotification MONITORINFOF_PRIMARY AC_SRC_ALPHA AC_SRC_OVER CreateCompatibleDC DeleteDC DeleteObject GetDC GetWindowRect ReleaseDC SelectObject SetWindowPos ShowWindow UpdateLayeredWindow WINDOW_STYLE WINDOW_EX_STYLE WTSINFOEXW WTS_INFO_CLASS WTS_SESSIONSTATE_UNKNOWN WTSGetActiveConsoleSessionId WTSQuerySessionInformation WTS_CURRENT_SERVER_HANDLE WTSFreeMemory WTS_SESSIONSTATE_LOCK WTS_SESSIONSTATE_UNLOCK ================================================ FILE: LenovoLegionToolkit.Lib/PackageDownloader/AbstractPackageDownloader.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http; using System.Security.Cryptography; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.PackageDownloader; public abstract class AbstractPackageDownloader(HttpClientFactory httpClientFactory) : IPackageDownloader { protected HttpClientFactory HttpClientFactory => httpClientFactory; public abstract Task> GetPackagesAsync(string machineType, OS os, IProgress? progress = null, CancellationToken token = default); public async Task DownloadPackageFileAsync(Package package, string location, IProgress? progress = null, CancellationToken token = default) { using var httpClient = httpClientFactory.Create(); var tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); await using (var fileStream = File.OpenWrite(tempPath)) await httpClient.DownloadAsync(package.FileLocation, fileStream, progress, token).ConfigureAwait(false); await TryValidateChecksum(package, tempPath, httpClient, token).ConfigureAwait(false); var filename = SanitizeFileName(package.Title) + " - " + package.FileName; var finalPath = Path.Combine(location, filename); File.Move(tempPath, finalPath, true); return finalPath; } private static async Task TryValidateChecksum(Package package, string tempPath, HttpClient httpClient, CancellationToken token) { await using var fileStream = File.OpenRead(tempPath); using var managedSha256 = SHA256.Create(); var fileSha256Bytes = await managedSha256.ComputeHashAsync(fileStream, token).ConfigureAwait(false); var fileSha256 = fileSha256Bytes.Aggregate(string.Empty, (current, b) => current + b.ToString("X2")); if (!string.IsNullOrEmpty(package.FileCrc) && fileSha256.Equals(package.FileCrc, StringComparison.InvariantCultureIgnoreCase)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Package file checksum match. [fileName={package.FileName}, fileLocation={package.FileLocation}, fileCrc={package.FileCrc}]"); return; } try { var externalSha256 = await httpClient.GetStringAsync($"{package.FileLocation}.sha256", token).ConfigureAwait(false); if (fileSha256.Equals(externalSha256, StringComparison.InvariantCultureIgnoreCase)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"External file checksum match. [fileName={package.FileName}, fileLocation={package.FileLocation}, fileCrc={package.FileCrc}]"); return; } } catch (HttpRequestException ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"External file checksum not found. [statusCode={ex.StatusCode}, fileName={package.FileName}, fileLocation={package.FileLocation}, fileCrc={package.FileCrc}]"); } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"File checksum mismatch. [fileName={package.FileName}, fileLocation={package.FileLocation}]"); throw new InvalidDataException("File checksum mismatch"); } private static string SanitizeFileName(string name) { var invalidChars = Regex.Escape(new string(Path.GetInvalidFileNameChars())); var invalidRegStr = string.Format(@"([{0}]*\.+$)|([{0}]+)", invalidChars); return Regex.Replace(name, invalidRegStr, "_"); } } ================================================ FILE: LenovoLegionToolkit.Lib/PackageDownloader/Detectors/Rules/AndPackageRule.cs ================================================ using System.Collections.Generic; using System.Net.Http; using System.Threading; using System.Threading.Tasks; namespace LenovoLegionToolkit.Lib.PackageDownloader.Detectors.Rules; internal readonly struct AndPackageRule : IPackageRule { private IEnumerable Rules { get; init; } public static bool TryCreate(IEnumerable rules, out AndPackageRule value) { value = new AndPackageRule { Rules = rules }; return true; } public async Task CheckDependenciesSatisfiedAsync(List driverInfoCache, HttpClient httpClient, CancellationToken token) { foreach (var rule in Rules) { if (!await rule.CheckDependenciesSatisfiedAsync(driverInfoCache, httpClient, token).ConfigureAwait(false)) return false; } return true; } public async Task DetectInstallNeededAsync(List driverInfoCache, HttpClient httpClient, CancellationToken token) { foreach (var rule in Rules) { if (!await rule.DetectInstallNeededAsync(driverInfoCache, httpClient, token).ConfigureAwait(false)) return false; } return true; } } ================================================ FILE: LenovoLegionToolkit.Lib/PackageDownloader/Detectors/Rules/BiosPackageRule.cs ================================================ using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Xml; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.PackageDownloader.Detectors.Rules; internal readonly partial struct BiosPackageRule : IPackageRule { [GeneratedRegex("^[A-Z0-9]{4}")] private static partial Regex PrefixRegex(); [GeneratedRegex("[0-9]{2}")] private static partial Regex VersionRegex(); private string[] Levels { get; init; } public static bool TryCreate(XmlNode? node, out BiosPackageRule value) { var levels = node?.SelectNodes("Level")? .OfType() .Select(n => n.InnerText) .ToArray() ?? []; if (levels.IsEmpty()) { value = default; return false; } value = new BiosPackageRule { Levels = levels }; return true; } public async Task CheckDependenciesSatisfiedAsync(List driverInfoCache, HttpClient httpClient, CancellationToken token) { var mi = await Compatibility.GetMachineInformationAsync().ConfigureAwait(false); var currentBios = mi.BiosVersion; var result = Levels.Any((global::System.Func)(level => { var levelPrefix = PrefixRegex().Match(level).Value; var levelVersion = int.Parse(VersionRegex().Match(level).Value); return currentBios.HasValue && levelPrefix == currentBios.Value.Prefix && levelVersion == currentBios.Value.Version; })); return result; } public async Task DetectInstallNeededAsync(List _1, HttpClient _2, CancellationToken _3) { var mi = await Compatibility.GetMachineInformationAsync().ConfigureAwait(false); var currentBios = mi.BiosVersion; var result = Levels.All((global::System.Func)(level => { var levelPrefix = PrefixRegex().Match(level).Value; var levelVersion = int.Parse(VersionRegex().Match(level).Value); return currentBios.HasValue && levelPrefix == currentBios.Value.Prefix && levelVersion > currentBios.Value.Version; })); return result; } } ================================================ FILE: LenovoLegionToolkit.Lib/PackageDownloader/Detectors/Rules/CpuAddressWidthPackageRule.cs ================================================ using System.Collections.Generic; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using System.Xml; using LenovoLegionToolkit.Lib.System.Management; namespace LenovoLegionToolkit.Lib.PackageDownloader.Detectors.Rules; internal readonly struct CpuAddressWidthPackageRule : IPackageRule { private int Value { get; init; } public static bool TryCreate(XmlNode? node, out CpuAddressWidthPackageRule value) { var addressWidthString = node?.SelectSingleNode("AddressWidth")?.InnerText; if (addressWidthString is null || !int.TryParse(addressWidthString, out var addressWidth)) { value = default; return false; } value = new CpuAddressWidthPackageRule { Value = addressWidth }; return true; } public Task CheckDependenciesSatisfiedAsync(List _1, HttpClient _2, CancellationToken _3) => CheckCpuAddressWidthAsync(); public Task DetectInstallNeededAsync(List _1, HttpClient _2, CancellationToken _3) => CheckCpuAddressWidthAsync(); private async Task CheckCpuAddressWidthAsync() { var addressWidth = await WMI.Win32.Processor.GetAddressWidthAsync().ConfigureAwait(false); var result = Value == addressWidth; return result; } } ================================================ FILE: LenovoLegionToolkit.Lib/PackageDownloader/Detectors/Rules/DriverPackageRule.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using System.Xml; using LenovoLegionToolkit.Lib.Extensions; namespace LenovoLegionToolkit.Lib.PackageDownloader.Detectors.Rules; internal readonly struct DriverPackageRule : IPackageRule { private string[] HardwareIds { get; init; } private DateTime? Date { get; init; } private Version? Version { get; init; } public static bool TryCreate(XmlNode? node, out DriverPackageRule value) { var hardwareIds = node?.SelectNodes("HardwareID")? .OfType() .Select(n => n.InnerText) .ToArray() ?? []; var dateString = node?.SelectSingleNode("Date")?.InnerText; var versionString = node?.SelectSingleNode("Version")?.InnerText; DateTime? date = null; if (DateTime.TryParse(dateString, out var d)) date = d; Version? version = null; if (Version.TryParse(RemoveNonVersionCharacters(versionString), out var v)) version = v; if (hardwareIds.IsEmpty()) { value = default; return false; } value = new DriverPackageRule { HardwareIds = hardwareIds, Date = date, Version = version }; return true; } public Task CheckDependenciesSatisfiedAsync(List driverInfoCache, HttpClient _1, CancellationToken _2) { var driverInfo = FindMatchingDriverInfo(HardwareIds, driverInfoCache); if (string.IsNullOrEmpty(driverInfo.HardwareId)) return Task.FromResult(false); var result = !VerifyByDateVersion(driverInfo); return Task.FromResult(result); } public Task DetectInstallNeededAsync(List driverInfoCache, HttpClient _1, CancellationToken _2) { var driverInfo = FindMatchingDriverInfo(HardwareIds, driverInfoCache); if (string.IsNullOrEmpty(driverInfo.HardwareId)) return Task.FromResult(false); var result = VerifyByDateVersion(driverInfo); return Task.FromResult(result); } private bool VerifyByDateVersion(DriverInfo driverInfo) { if (Date is not null && driverInfo.Date is not null && Version is not null && driverInfo.Version is not null) { if (Date < driverInfo.Date) return false; return Version > driverInfo.Version; } if (Version is not null && driverInfo.Version is not null) { var result = Version > driverInfo.Version; return result; } return false; } private static string RemoveNonVersionCharacters(string? versionString) { var arr = versionString?.ToCharArray() ?? []; arr = Array.FindAll(arr, c => char.IsDigit(c) || c == '.'); return new string(arr); } private static DriverInfo FindMatchingDriverInfo(IEnumerable hardwareIds, IEnumerable driverInfoCache) { return driverInfoCache.FirstOrDefault(di => hardwareIds.Any(hardwareId => { var result = di.DeviceId.StartsWith(hardwareId, StringComparison.InvariantCultureIgnoreCase); result |= di.HardwareId.StartsWith(hardwareId, StringComparison.InvariantCultureIgnoreCase); return result; })); } } ================================================ FILE: LenovoLegionToolkit.Lib/PackageDownloader/Detectors/Rules/ExternalDetectionRule.cs ================================================ using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using System.Xml; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.System; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.PackageDownloader.Detectors.Rules; internal readonly struct ExternalDetectionRule : IPackageRule { private const string TEMP_FOLDER_SUB_FOLDER = "external_package_detection"; private int[] ReturnCodes { get; init; } private string Command { get; init; } private string Url { get; init; } private string FileName { get; init; } private string PackageName { get; init; } public static bool TryCreate(XmlNode? node, XmlDocument document, string baseLocation, out ExternalDetectionRule value) { var command = node?.InnerText; var returnCodes = node?.Attributes?.OfType() .FirstOrDefault(a => a.Name == "rc")? .InnerText .Split(",") .Select(s => int.TryParse(s, out var result) ? result : -1) .Where(i => i >= 0) .Distinct() .ToArray() ?? []; var externalFile = document.SelectSingleNode("/Package/Files/External/File/Name")?.InnerText; var packageName = document.SelectSingleNode("/Package/@id")?.InnerText; if (command is null || returnCodes.IsEmpty() || externalFile is null || packageName is null) { value = default; return false; } value = new ExternalDetectionRule { Command = command, ReturnCodes = returnCodes, Url = $"{baseLocation}/{externalFile}", FileName = externalFile, PackageName = packageName }; return true; } public Task CheckDependenciesSatisfiedAsync(List _, HttpClient httpClient, CancellationToken token) => CheckExternalDependency(httpClient, token); public Task DetectInstallNeededAsync(List _, HttpClient httpClient, CancellationToken token) => CheckExternalDependency(httpClient, token); private async Task CheckExternalDependency(HttpClient httpClient, CancellationToken token) { var packagePath = Path.Combine(Folders.Temp, TEMP_FOLDER_SUB_FOLDER, PackageName); var filePath = Path.Combine(packagePath, FileName); if (!Directory.Exists(packagePath)) Directory.CreateDirectory(packagePath); if (!File.Exists(filePath)) { await using var fileStream = File.OpenWrite(filePath); await httpClient.DownloadAsync(Url, fileStream, null, token).ConfigureAwait(false); } var executable = Command.Split(' ').FirstOrDefault(); var arguments = string.Join(' ', Command.Split(' ').Skip(1)); if (executable is null) return false; // ReSharper disable StringLiteralTypo if (executable.Contains("%PACKAGEPATH%")) executable = executable.Replace("%PACKAGEPATH%", packagePath); // ReSharper restore StringLiteralTypo if (!executable.Contains('\\')) executable = Path.Combine(packagePath, executable); var (exitCode, _) = await CMD.RunAsync(executable, arguments, token: token).ConfigureAwait(false); var result = ReturnCodes.Contains(exitCode); return result; } } ================================================ FILE: LenovoLegionToolkit.Lib/PackageDownloader/Detectors/Rules/IPackageRule.cs ================================================ using System.Collections.Generic; using System.Net.Http; using System.Threading; using System.Threading.Tasks; namespace LenovoLegionToolkit.Lib.PackageDownloader.Detectors.Rules; public interface IPackageRule { Task CheckDependenciesSatisfiedAsync(List driverInfoCache, HttpClient httpClient, CancellationToken token); Task DetectInstallNeededAsync(List driverInfoCache, HttpClient httpClient, CancellationToken token); } ================================================ FILE: LenovoLegionToolkit.Lib/PackageDownloader/Detectors/Rules/NotPackageRule.cs ================================================ using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; namespace LenovoLegionToolkit.Lib.PackageDownloader.Detectors.Rules; internal readonly struct NotPackageRule : IPackageRule { private IPackageRule Rule { get; init; } public static bool TryCreate(IEnumerable rules, out NotPackageRule value) { var rule = rules.FirstOrDefault(); if (rule is null) { value = default; return false; } value = new NotPackageRule { Rule = rule }; return true; } public async Task CheckDependenciesSatisfiedAsync(List driverInfoCache, HttpClient httpClient, CancellationToken token) { var result = !await Rule.CheckDependenciesSatisfiedAsync(driverInfoCache, httpClient, token).ConfigureAwait(false); return result; } public async Task DetectInstallNeededAsync(List driverInfoCache, HttpClient httpClient, CancellationToken token) { var result = !await Rule.DetectInstallNeededAsync(driverInfoCache, httpClient, token).ConfigureAwait(false); return result; } } ================================================ FILE: LenovoLegionToolkit.Lib/PackageDownloader/Detectors/Rules/OrPackageRule.cs ================================================ using System.Collections.Generic; using System.Net.Http; using System.Threading; using System.Threading.Tasks; namespace LenovoLegionToolkit.Lib.PackageDownloader.Detectors.Rules; internal readonly struct OrPackageRule : IPackageRule { private IEnumerable Rules { get; init; } public static bool TryCreate(IEnumerable rules, out OrPackageRule value) { value = new OrPackageRule { Rules = rules }; return true; } public async Task CheckDependenciesSatisfiedAsync(List driverInfoCache, HttpClient httpClient, CancellationToken token) { foreach (var rule in Rules) { if (await rule.CheckDependenciesSatisfiedAsync(driverInfoCache, httpClient, token).ConfigureAwait(false)) return true; } return false; } public async Task DetectInstallNeededAsync(List driverInfoCache, HttpClient httpClient, CancellationToken token) { foreach (var rule in Rules) { if (await rule.DetectInstallNeededAsync(driverInfoCache, httpClient, token).ConfigureAwait(false)) return true; } return false; } } ================================================ FILE: LenovoLegionToolkit.Lib/PackageDownloader/Detectors/Rules/OsPackageRule.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using System.Xml; using LenovoLegionToolkit.Lib.Extensions; namespace LenovoLegionToolkit.Lib.PackageDownloader.Detectors.Rules; internal readonly struct OsPackageRule : IPackageRule { private string[] Oses { get; init; } public static bool TryCreate(XmlNode? node, out OsPackageRule value) { var oses = node?.SelectNodes("OS")? .OfType() .Select(n => n.InnerText) .ToArray() ?? []; if (oses.IsEmpty()) { value = default; return false; } value = new OsPackageRule { Oses = oses }; return true; } public Task CheckDependenciesSatisfiedAsync(List _1, HttpClient _2, CancellationToken _3) => OsVersionMatch(); public Task DetectInstallNeededAsync(List _1, HttpClient _2, CancellationToken _3) => OsVersionMatch(); private Task OsVersionMatch() { var currentOs = OSExtensions.GetCurrent(); var result = Oses.Any(os => { switch (currentOs) { case OS.Windows11 when os.StartsWith("win11", StringComparison.InvariantCultureIgnoreCase): case OS.Windows10 when os.StartsWith("win10", StringComparison.InvariantCultureIgnoreCase): case OS.Windows8 when os.StartsWith("win8", StringComparison.InvariantCultureIgnoreCase): case OS.Windows7 when os.StartsWith("win7", StringComparison.InvariantCultureIgnoreCase): return true; default: return false; } }); return Task.FromResult(result); } } ================================================ FILE: LenovoLegionToolkit.Lib/PackageDownloader/Detectors/Rules/PnPIdPackageRule.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using System.Xml; namespace LenovoLegionToolkit.Lib.PackageDownloader.Detectors.Rules; internal readonly struct PnPIdPackageRule : IPackageRule { private string HardwareId { get; init; } public static bool TryCreate(XmlNode? node, out PnPIdPackageRule value) { var hardwareId = node?.InnerText; if (hardwareId is null) { value = default; return false; } value = new() { HardwareId = hardwareId }; return true; } public Task CheckDependenciesSatisfiedAsync(List driverInfoCache, HttpClient _1, CancellationToken _2) { var result = MatchingDriverInfoExists(HardwareId, driverInfoCache); return Task.FromResult(result); } public Task DetectInstallNeededAsync(List driverInfoCache, HttpClient _1, CancellationToken _2) { var result = MatchingDriverInfoExists(HardwareId, driverInfoCache); return Task.FromResult(result); } private static bool MatchingDriverInfoExists(string hardwareId, IEnumerable driverInfoCache) { return driverInfoCache.Any(di => { var result = di.DeviceId.StartsWith(hardwareId, StringComparison.InvariantCultureIgnoreCase); result |= di.HardwareId.StartsWith(hardwareId, StringComparison.InvariantCultureIgnoreCase); return result; }); } } ================================================ FILE: LenovoLegionToolkit.Lib/PackageDownloader/Detectors/Rules/RegistryKeyPackageRule.cs ================================================ using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using System.Xml; using LenovoLegionToolkit.Lib.System; namespace LenovoLegionToolkit.Lib.PackageDownloader.Detectors.Rules; internal readonly struct RegistryKeyPackageRule : IPackageRule { private string Key { get; init; } public static bool TryCreate(XmlNode? node, out RegistryKeyPackageRule value) { var key = node?.SelectSingleNode("Key")?.InnerText; if (key is null) { value = default; return false; } value = new RegistryKeyPackageRule { Key = key }; return true; } public Task CheckDependenciesSatisfiedAsync(List _1, HttpClient _2, CancellationToken _3) => KeyExists(); public Task DetectInstallNeededAsync(List _1, HttpClient _2, CancellationToken _3) => KeyExists(); private Task KeyExists() { var hive = Key.Split('\\').FirstOrDefault(); var path = string.Join('\\', Key.Split('\\').Skip(1)); if (hive is null || string.IsNullOrEmpty(path)) return Task.FromResult(false); var result = Registry.KeyExists(hive, path); return Task.FromResult(result); } } ================================================ FILE: LenovoLegionToolkit.Lib/PackageDownloader/Detectors/Rules/RegistryKeyValuePackageRule.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using System.Xml; using LenovoLegionToolkit.Lib.System; namespace LenovoLegionToolkit.Lib.PackageDownloader.Detectors.Rules; internal readonly struct RegistryKeyValuePackageRule : IPackageRule { private string Key { get; init; } private string KeyName { get; init; } private Version Version { get; init; } public static bool TryCreate(XmlNode? node, out RegistryKeyValuePackageRule value) { var key = node?.SelectSingleNode("Key")?.InnerText; var keyName = node?.SelectSingleNode("KeyName")?.InnerText; var versionString = node?.SelectSingleNode("Version")?.InnerText; Version? version = null; if (Version.TryParse(RemoveNonVersionCharacters(versionString), out var v)) version = v; if (key is null || keyName is null || version is null) { value = default; return false; } value = new RegistryKeyValuePackageRule { Key = key, KeyName = keyName, Version = version }; return true; } public Task CheckDependenciesSatisfiedAsync(List _1, HttpClient _2, CancellationToken _3) { var hive = Key.Split('\\').FirstOrDefault(); var path = string.Join('\\', Key.Split('\\').Skip(1)); if (hive is null || string.IsNullOrEmpty(path)) return Task.FromResult(false); var keyExists = Registry.ValueExists(hive, path, KeyName); if (!keyExists) return Task.FromResult(true); var versionString = Registry.GetValue(hive, path, KeyName, string.Empty); if (!Version.TryParse(versionString, out var version)) return Task.FromResult(false); var result = Version <= version; return Task.FromResult(result); } public Task DetectInstallNeededAsync(List _1, HttpClient _2, CancellationToken _3) { var hive = Key.Split('\\').FirstOrDefault(); var path = string.Join('\\', Key.Split('\\').Skip(1)); if (hive is null || string.IsNullOrEmpty(path)) return Task.FromResult(false); var keyExists = Registry.ValueExists(hive, path, KeyName); if (!keyExists) return Task.FromResult(false); var versionString = Registry.GetValue(hive, path, KeyName, string.Empty); if (versionString.Any(char.IsWhiteSpace)) versionString = versionString.Split(null).FirstOrDefault(versionString); if (!Version.TryParse(versionString, out var version)) return Task.FromResult(false); var result = Version > version; return Task.FromResult(result); } private static string RemoveNonVersionCharacters(string? versionString) { var arr = versionString?.ToCharArray() ?? []; arr = Array.FindAll(arr, c => char.IsDigit(c) || c == '.'); return new string(arr); } } ================================================ FILE: LenovoLegionToolkit.Lib/PackageDownloader/Detectors/Rules/WindowsBuildVersionPackageRule.cs ================================================ using System; using System.Collections.Generic; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using System.Xml; using LenovoLegionToolkit.Lib.System.Management; namespace LenovoLegionToolkit.Lib.PackageDownloader.Detectors.Rules; internal readonly struct WindowsBuildVersionPackageRule : IPackageRule { private int Version { get; init; } public static bool TryCreate(XmlNode? node, out WindowsBuildVersionPackageRule value) { var versionString = node?.SelectSingleNode("AddressWidth")?.InnerText; if (versionString is null || !int.TryParse(RemoveNonVersionCharacters(versionString), out var version)) { value = default; return false; } value = new WindowsBuildVersionPackageRule { Version = version }; return true; } public Task CheckDependenciesSatisfiedAsync(List _1, HttpClient _2, CancellationToken _3) => CheckBuildNumberAsync(); public Task DetectInstallNeededAsync(List _1, HttpClient _2, CancellationToken _3) => CheckBuildNumberAsync(); private async Task CheckBuildNumberAsync() { var buildNumberString = await WMI.Win32.OperatingSystem.GetBuildNumberAsync().ConfigureAwait(false); var buildNumber = int.TryParse(buildNumberString, out var bn) ? bn : 0; var result = Version <= buildNumber; return result; } private static string RemoveNonVersionCharacters(string? versionString) { var arr = versionString?.ToCharArray() ?? []; arr = Array.FindAll(arr, c => char.IsDigit(c) || c == '.'); return new string(arr); } } ================================================ FILE: LenovoLegionToolkit.Lib/PackageDownloader/Detectors/VantagePackageUpdateDetector.cs ================================================ using System.Collections.Generic; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using System.Xml; using LenovoLegionToolkit.Lib.PackageDownloader.Detectors.Rules; using LenovoLegionToolkit.Lib.System.Management; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.PackageDownloader.Detectors; internal class VantagePackageUpdateDetector { private readonly List _driverInfoCache = []; public async Task BuildDriverInfoCache() { var driverInfo = await WMI.Win32.PnpSignedDriver.ReadAsync().ConfigureAwait(false); _driverInfoCache.Clear(); _driverInfoCache.AddRange(driverInfo); } public async Task DetectAsync(HttpClient httpClient, XmlDocument document, string baseLocation, CancellationToken token) { var dependenciesSatisfied = await CheckDependenciesSatisfiedAsync(httpClient, document, baseLocation, token).ConfigureAwait(false); if (!dependenciesSatisfied) return false; return await DetectInstallAsync(httpClient, document, baseLocation, token).ConfigureAwait(false); } private async Task CheckDependenciesSatisfiedAsync(HttpClient httpClient, XmlDocument document, string baseLocation, CancellationToken token) { var node = document.SelectSingleNode("/Package/Dependencies"); if (node is null || !node.HasChildNodes) return false; var rules = CreateRules(node, document, baseLocation); if (!AndPackageRule.TryCreate(rules, out var rule)) return false; var result = await rule.CheckDependenciesSatisfiedAsync(_driverInfoCache, httpClient, token).ConfigureAwait(false); return result; } private async Task DetectInstallAsync(HttpClient httpClient, XmlDocument document, string baseLocation, CancellationToken token) { var node = document.SelectSingleNode("/Package/DetectInstall"); if (node is null || !node.HasChildNodes) return true; var rules = CreateRules(node, document, baseLocation); if (!AndPackageRule.TryCreate(rules, out var rule)) return false; var result = await rule.DetectInstallNeededAsync(_driverInfoCache, httpClient, token).ConfigureAwait(false); return result; } private static IEnumerable CreateRules(XmlNode? node, XmlDocument document, string baseLocation) { if (node is null) yield break; for (var i = 0; i < node.ChildNodes.Count; i++) { var childNode = node.ChildNodes[i]; if (childNode is null) continue; switch (childNode.Name) { case "Or": { var rules = CreateRules(childNode, document, baseLocation); if (OrPackageRule.TryCreate(rules, out var value)) yield return value; break; } case "And": { var rules = CreateRules(childNode, document, baseLocation); if (AndPackageRule.TryCreate(rules, out var value)) yield return value; break; } case "Not": { var rules = CreateRules(childNode, document, baseLocation); if (NotPackageRule.TryCreate(rules, out var value)) yield return value; break; } case "_OS": { if (OsPackageRule.TryCreate(childNode, out var value)) yield return value; break; } case "_WindowsBuildVersion": { if (WindowsBuildVersionPackageRule.TryCreate(childNode, out var value)) yield return value; break; } case "_CPUAddressWidth": { if (CpuAddressWidthPackageRule.TryCreate(childNode, out var value)) yield return value; break; } case "_ExternalDetection": { if (ExternalDetectionRule.TryCreate(childNode, document, baseLocation, out var value)) yield return value; break; } case "_Driver": { if (DriverPackageRule.TryCreate(childNode, out var value)) yield return value; break; } case "_PnPID": { if (PnPIdPackageRule.TryCreate(childNode, out var value)) yield return value; break; } case "_Bios": { if (BiosPackageRule.TryCreate(childNode, out var value)) yield return value; break; } case "_RegistryKey": { if (RegistryKeyPackageRule.TryCreate(childNode, out var value)) yield return value; break; } case "_RegistryKeyValue": { if (RegistryKeyValuePackageRule.TryCreate(childNode, out var value)) yield return value; break; } default: { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Unknown rule: {childNode.Name}."); break; } } } } } ================================================ FILE: LenovoLegionToolkit.Lib/PackageDownloader/IPackageDownloader.cs ================================================ using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; namespace LenovoLegionToolkit.Lib.PackageDownloader; public interface IPackageDownloader { Task DownloadPackageFileAsync(Package package, string location, IProgress? progress = null, CancellationToken token = default); Task> GetPackagesAsync(string machineType, OS os, IProgress? progress = null, CancellationToken token = default); } ================================================ FILE: LenovoLegionToolkit.Lib/PackageDownloader/PCSupportPackageDownloader.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text.Json.Nodes; using System.Threading; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Extensions; namespace LenovoLegionToolkit.Lib.PackageDownloader; public class PCSupportPackageDownloader(HttpClientFactory httpClientFactory) : AbstractPackageDownloader(httpClientFactory) { private const string CATALOG_BASE_URL = "https://pcsupport.lenovo.com/us/en/api/v4/downloads/drivers?productId="; public override async Task> GetPackagesAsync(string machineType, OS os, IProgress? progress = null, CancellationToken token = default) { var osString = os switch { OS.Windows11 => "Windows 11", OS.Windows10 => "Windows 10", OS.Windows8 => "Windows 8", OS.Windows7 => "Windows 7", _ => throw new InvalidOperationException(nameof(os)), }; using var httpClient = HttpClientFactory.Create(); httpClient.DefaultRequestHeaders.Referrer = new Uri("https://pcsupport.lenovo.com/"); progress?.Report(0); var catalogJson = await httpClient.GetStringAsync($"{CATALOG_BASE_URL}{machineType}", token).ConfigureAwait(false); var catalogJsonNode = JsonNode.Parse(catalogJson); var downloadsNode = catalogJsonNode?["body"]?["DownloadItems"]?.AsArray(); if (downloadsNode is null) return []; var packages = new List(); foreach (var downloadNode in downloadsNode) { if (!IsCompatible(downloadNode, osString)) continue; var package = ParsePackage(downloadNode!); if (package is null) continue; packages.Add(package.Value); } return packages; } private static Package? ParsePackage(JsonNode downloadNode) { var id = downloadNode["ID"]!.ToJsonString(); var category = downloadNode["Category"]!["Name"]!.ToString(); var title = downloadNode["Title"]!.ToString(); var description = downloadNode["Summary"]!.ToString(); var version = downloadNode["SummaryInfo"]!["Version"]!.ToString(); var filesNode = downloadNode["Files"]!.AsArray(); var mainFileNode = filesNode.FirstOrDefault(n => n!["TypeString"]!.ToString().Equals("exe", StringComparison.InvariantCultureIgnoreCase)) ?? filesNode.FirstOrDefault(n => n!["TypeString"]!.ToString().Equals("zip", StringComparison.InvariantCultureIgnoreCase)) ?? filesNode.FirstOrDefault(); if (mainFileNode is null) return null; var fileLocation = mainFileNode["URL"]!.ToString(); var fileName = new Uri(fileLocation).Segments.LastOrDefault("file"); var fileSize = mainFileNode["Size"]!.ToString(); var fileCrc = mainFileNode["SHA256"]?.ToString(); var releaseDateUnix = long.Parse(mainFileNode["Date"]!["Unix"]!.ToString()); var releaseDate = DateTimeOffset.FromUnixTimeMilliseconds(releaseDateUnix).DateTime; var readmeFileNode = filesNode.FirstOrDefault(n => n!["TypeString"]!.ToString().Equals("txt readme", StringComparison.InvariantCultureIgnoreCase)) ?? filesNode.FirstOrDefault(n => n!["TypeString"]!.ToString().Equals("html", StringComparison.InvariantCultureIgnoreCase)); var readme = readmeFileNode?["URL"]?.ToString(); return new() { Id = id, Title = title, Description = title == description ? string.Empty : description, Version = version, Category = category, FileName = fileName, FileSize = fileSize, FileCrc = fileCrc, ReleaseDate = releaseDate, Readme = readme, FileLocation = fileLocation, }; } private static bool IsCompatible(JsonNode? downloadNode, string osString) { var operatingSystems = downloadNode?["OperatingSystemKeys"]?.AsArray(); if (operatingSystems is null || operatingSystems.IsEmpty()) return true; foreach (var operatingSystem in operatingSystems) if (operatingSystem is not null && operatingSystem.ToString().StartsWith(osString, StringComparison.CurrentCultureIgnoreCase)) return true; return false; } } ================================================ FILE: LenovoLegionToolkit.Lib/PackageDownloader/PackageDownloaderFactory.cs ================================================ using System; namespace LenovoLegionToolkit.Lib.PackageDownloader; public class PackageDownloaderFactory( PCSupportPackageDownloader pcSupportPackageDownloader, VantagePackageDownloader vantagePackageDownloader) { public enum Type { PCSupport, Vantage, } public IPackageDownloader GetInstance(Type type) => type switch { Type.PCSupport => pcSupportPackageDownloader, Type.Vantage => vantagePackageDownloader, _ => throw new InvalidOperationException(nameof(type)), }; } ================================================ FILE: LenovoLegionToolkit.Lib/PackageDownloader/UpdateCatalogNotFoundException.cs ================================================ using System; namespace LenovoLegionToolkit.Lib.PackageDownloader; public class UpdateCatalogNotFoundException(string? message, Exception? ex) : Exception(message, ex); ================================================ FILE: LenovoLegionToolkit.Lib/PackageDownloader/VantagePackageDownloader.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using System.Xml; using LenovoLegionToolkit.Lib.PackageDownloader.Detectors; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.PackageDownloader; public class VantagePackageDownloader(HttpClientFactory httpClientFactory) : AbstractPackageDownloader(httpClientFactory) { private readonly struct PackageDefinition(string location, string category) { public string Location { get; } = location; public string Category { get; } = category; } private const string CATALOG_BASE_URL = "https://download.lenovo.com/catalog/"; public override async Task> GetPackagesAsync(string machineType, OS os, IProgress? progress = null, CancellationToken token = default) { progress?.Report(0); var osString = os switch { OS.Windows11 => "win11", OS.Windows10 => "win10", OS.Windows8 => "win8", OS.Windows7 => "win7", _ => throw new ArgumentOutOfRangeException(nameof(os), os, null) }; using var httpClient = HttpClientFactory.Create(); var packageDefinitions = await GetPackageDefinitionsAsync(httpClient, $"{CATALOG_BASE_URL}/{machineType}_{osString}.xml", token).ConfigureAwait(false); var updateDetector = new VantagePackageUpdateDetector(); await updateDetector.BuildDriverInfoCache().ConfigureAwait(false); var count = 0; var totalCount = packageDefinitions.Count; var packages = new List(); foreach (var packageDefinition in packageDefinitions) { var package = await GetPackage(httpClient, updateDetector, packageDefinition, token).ConfigureAwait(false); packages.Add(package); count++; // ReSharper disable once PossibleLossOfFraction progress?.Report(count * 100 / totalCount); } return packages; } private static async Task> GetPackageDefinitionsAsync(HttpClient httpClient, string location, CancellationToken token) { string catalogString; try { catalogString = await httpClient.GetStringAsync(location, token).ConfigureAwait(false); } catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { throw new UpdateCatalogNotFoundException(ex.Message, ex); } var document = new XmlDocument(); document.LoadXml(catalogString); var packageNodes = document.SelectNodes("/packages/package"); if (packageNodes is null) return []; var packageDefinitions = new List(); foreach (var packageNode in packageNodes.OfType()) { token.ThrowIfCancellationRequested(); var pLocation = packageNode.SelectSingleNode("location")?.InnerText; var pCategory = packageNode.SelectSingleNode("category")?.InnerText; if (string.IsNullOrWhiteSpace(pLocation) || string.IsNullOrWhiteSpace(pCategory)) continue; packageDefinitions.Add(new(pLocation, pCategory)); } return packageDefinitions; } private static async Task GetPackage(HttpClient httpClient, VantagePackageUpdateDetector updateDetector, PackageDefinition packageDefinition, CancellationToken token) { var location = packageDefinition.Location; var baseLocation = location.Remove(location.LastIndexOf("/", StringComparison.InvariantCultureIgnoreCase)); var packageString = await httpClient.GetStringAsync(location, token).ConfigureAwait(false); var document = new XmlDocument(); document.LoadXml(packageString); var id = document.SelectSingleNode("/Package/@id")!.InnerText; var title = document.SelectSingleNode("/Package/Title/Desc")!.InnerText; var version = document.SelectSingleNode("/Package/@version")!.InnerText; var fileName = document.SelectSingleNode("/Package/Files/Installer/File/Name")!.InnerText; var fileCrc = document.SelectSingleNode("/Package/Files/Installer/File/CRC")?.InnerText; var fileSizeBytes = int.Parse(document.SelectSingleNode("/Package/Files/Installer/File/Size")!.InnerText); var fileSize = $"{fileSizeBytes / 1024.0 / 1024.0:0.00} MB"; var releaseDateString = document.SelectSingleNode("/Package/ReleaseDate")!.InnerText; var releaseDate = DateTime.Parse(releaseDateString); var readmeName = document.SelectSingleNode("/Package/Files/Readme/File/Name")?.InnerText; var readme = $"{baseLocation}/{readmeName}"; var fileLocation = $"{baseLocation}/{fileName}"; var rebootString = document.SelectSingleNode("/Package/Reboot/@type")!.InnerText; var reboot = int.TryParse(rebootString, out var rebootInt) ? (RebootType)rebootInt : RebootType.NotRequired; var isUpdate = false; try { isUpdate = await updateDetector.DetectAsync(httpClient, document, baseLocation, token) .ConfigureAwait(false); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Couldn't detect update for package {id}. [title={title}, location={location}]", ex); } return new() { Id = id, Title = title, Description = string.Empty, Version = version, Category = packageDefinition.Category, FileName = fileName, FileSize = fileSize, FileCrc = fileCrc, ReleaseDate = releaseDate, Readme = readme, FileLocation = fileLocation, IsUpdate = isUpdate, Reboot = reboot }; } } ================================================ FILE: LenovoLegionToolkit.Lib/Resources/Resource.Designer.cs ================================================ //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace LenovoLegionToolkit.Lib.Resources { using System; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class Resource { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Resource() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] public static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LenovoLegionToolkit.Lib.Resources.Resource", typeof(Resource).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] public static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Looks up a localized string similar to Custom. /// public static string AccentColorSource_Custom { get { return ResourceManager.GetString("AccentColorSource_Custom", resourceCulture); } } /// /// Looks up a localized string similar to System. /// public static string AccentColorSource_System { get { return ResourceManager.GetString("AccentColorSource_System", resourceCulture); } } /// /// Looks up a localized string similar to Off. /// public static string AlwaysOnUSBState_Off { get { return ResourceManager.GetString("AlwaysOnUSBState_Off", resourceCulture); } } /// /// Looks up a localized string similar to On, always. /// public static string AlwaysOnUSBState_OnAlways { get { return ResourceManager.GetString("AlwaysOnUSBState_OnAlways", resourceCulture); } } /// /// Looks up a localized string similar to On, when sleeping. /// public static string AlwaysOnUSBState_OnWhenSleeping { get { return ResourceManager.GetString("AlwaysOnUSBState_OnWhenSleeping", resourceCulture); } } /// /// Looks up a localized string similar to Disabled. /// public static string AutorunState_Disabled { get { return ResourceManager.GetString("AutorunState_Disabled", resourceCulture); } } /// /// Looks up a localized string similar to Enabled. /// public static string AutorunState_Enabled { get { return ResourceManager.GetString("AutorunState_Enabled", resourceCulture); } } /// /// Looks up a localized string similar to Enabled, delayed. /// public static string AutorunState_EnabledDelayed { get { return ResourceManager.GetString("AutorunState_EnabledDelayed", resourceCulture); } } /// /// Looks up a localized string similar to Off. /// public static string BatteryNightChargeState_Off { get { return ResourceManager.GetString("BatteryNightChargeState_Off", resourceCulture); } } /// /// Looks up a localized string similar to On. /// public static string BatteryNightChargeState_On { get { return ResourceManager.GetString("BatteryNightChargeState_On", resourceCulture); } } /// /// Looks up a localized string similar to Conservation. /// public static string BatteryState_Conservation { get { return ResourceManager.GetString("BatteryState_Conservation", resourceCulture); } } /// /// Looks up a localized string similar to Normal. /// public static string BatteryState_Normal { get { return ResourceManager.GetString("BatteryState_Normal", resourceCulture); } } /// /// Looks up a localized string similar to Rapid Charge. /// public static string BatteryState_RapidCharge { get { return ResourceManager.GetString("BatteryState_RapidCharge", resourceCulture); } } /// /// Looks up a localized string similar to Off. /// public static string FlipToStartState_Off { get { return ResourceManager.GetString("FlipToStartState_Off", resourceCulture); } } /// /// Looks up a localized string similar to On. /// public static string FlipToStartState_On { get { return ResourceManager.GetString("FlipToStartState_On", resourceCulture); } } /// /// Looks up a localized string similar to Off. /// public static string FnLockState_Off { get { return ResourceManager.GetString("FnLockState_Off", resourceCulture); } } /// /// Looks up a localized string similar to On. /// public static string FnLockState_On { get { return ResourceManager.GetString("FnLockState_On", resourceCulture); } } /// /// Looks up a localized string similar to Powered Off. /// public static string GPUController_PoweredOff { get { return ResourceManager.GetString("GPUController_PoweredOff", resourceCulture); } } /// /// Looks up a localized string similar to Powered On. /// public static string GPUController_PoweredOn { get { return ResourceManager.GetString("GPUController_PoweredOn", resourceCulture); } } /// /// Looks up a localized string similar to Off. /// public static string HDRState_Off { get { return ResourceManager.GetString("HDRState_Off", resourceCulture); } } /// /// Looks up a localized string similar to On. /// public static string HDRState_On { get { return ResourceManager.GetString("HDRState_On", resourceCulture); } } /// /// Looks up a localized string similar to dGPU. /// public static string HybridModeState_Off { get { return ResourceManager.GetString("HybridModeState_Off", resourceCulture); } } /// /// Looks up a localized string similar to Hybrid. /// public static string HybridModeState_On { get { return ResourceManager.GetString("HybridModeState_On", resourceCulture); } } /// /// Looks up a localized string similar to Hybrid-Auto. /// public static string HybridModeState_OnAuto { get { return ResourceManager.GetString("HybridModeState_OnAuto", resourceCulture); } } /// /// Looks up a localized string similar to Hybrid-iGPU. /// public static string HybridModeState_OnIGPUOnly { get { return ResourceManager.GetString("HybridModeState_OnIGPUOnly", resourceCulture); } } /// /// Looks up a localized string similar to AC Adapter. /// public static string InstantBootState_AcAdapter { get { return ResourceManager.GetString("InstantBootState_AcAdapter", resourceCulture); } } /// /// Looks up a localized string similar to AC and USB PD. /// public static string InstantBootState_AcAdapterAndUsbPowerDelivery { get { return ResourceManager.GetString("InstantBootState_AcAdapterAndUsbPowerDelivery", resourceCulture); } } /// /// Looks up a localized string similar to Off. /// public static string InstantBootState_Off { get { return ResourceManager.GetString("InstantBootState_Off", resourceCulture); } } /// /// Looks up a localized string similar to USB Power Delivery. /// public static string InstantBootState_UsbPowerDelivery { get { return ResourceManager.GetString("InstantBootState_UsbPowerDelivery", resourceCulture); } } /// /// Looks up a localized string similar to Off. /// public static string MicrophoneState_Off { get { return ResourceManager.GetString("MicrophoneState_Off", resourceCulture); } } /// /// Looks up a localized string similar to On. /// public static string MicrophoneState_On { get { return ResourceManager.GetString("MicrophoneState_On", resourceCulture); } } /// /// Looks up a localized string similar to Alt. /// public static string ModifierKey_Alt { get { return ResourceManager.GetString("ModifierKey_Alt", resourceCulture); } } /// /// Looks up a localized string similar to Ctrl. /// public static string ModifierKey_Ctrl { get { return ResourceManager.GetString("ModifierKey_Ctrl", resourceCulture); } } /// /// Looks up a localized string similar to Shift. /// public static string ModifierKey_Shift { get { return ResourceManager.GetString("ModifierKey_Shift", resourceCulture); } } /// /// Looks up a localized string similar to Long. /// public static string NotificationDuration_Long { get { return ResourceManager.GetString("NotificationDuration_Long", resourceCulture); } } /// /// Looks up a localized string similar to Normal. /// public static string NotificationDuration_Normal { get { return ResourceManager.GetString("NotificationDuration_Normal", resourceCulture); } } /// /// Looks up a localized string similar to Short. /// public static string NotificationDuration_Short { get { return ResourceManager.GetString("NotificationDuration_Short", resourceCulture); } } /// /// Looks up a localized string similar to Bottom center. /// public static string NotificationPosition_BottomCenter { get { return ResourceManager.GetString("NotificationPosition_BottomCenter", resourceCulture); } } /// /// Looks up a localized string similar to Bottom left. /// public static string NotificationPosition_BottomLeft { get { return ResourceManager.GetString("NotificationPosition_BottomLeft", resourceCulture); } } /// /// Looks up a localized string similar to Bottom right. /// public static string NotificationPosition_BottomRight { get { return ResourceManager.GetString("NotificationPosition_BottomRight", resourceCulture); } } /// /// Looks up a localized string similar to Center. /// public static string NotificationPosition_Center { get { return ResourceManager.GetString("NotificationPosition_Center", resourceCulture); } } /// /// Looks up a localized string similar to Center left. /// public static string NotificationPosition_CenterLeft { get { return ResourceManager.GetString("NotificationPosition_CenterLeft", resourceCulture); } } /// /// Looks up a localized string similar to Center right. /// public static string NotificationPosition_CenterRight { get { return ResourceManager.GetString("NotificationPosition_CenterRight", resourceCulture); } } /// /// Looks up a localized string similar to Top center. /// public static string NotificationPosition_TopCenter { get { return ResourceManager.GetString("NotificationPosition_TopCenter", resourceCulture); } } /// /// Looks up a localized string similar to Top left. /// public static string NotificationPosition_TopLeft { get { return ResourceManager.GetString("NotificationPosition_TopLeft", resourceCulture); } } /// /// Looks up a localized string similar to Top right. /// public static string NotificationPosition_TopRight { get { return ResourceManager.GetString("NotificationPosition_TopRight", resourceCulture); } } /// /// Looks up a localized string similar to Off. /// public static string OneLevelWhiteKeyboardBacklightState_Off { get { return ResourceManager.GetString("OneLevelWhiteKeyboardBacklightState_Off", resourceCulture); } } /// /// Looks up a localized string similar to On. /// public static string OneLevelWhiteKeyboardBacklightState_On { get { return ResourceManager.GetString("OneLevelWhiteKeyboardBacklightState_On", resourceCulture); } } /// /// Looks up a localized string similar to Off. /// public static string OverdriveState_Off { get { return ResourceManager.GetString("OverdriveState_Off", resourceCulture); } } /// /// Looks up a localized string similar to On. /// public static string OverdriveState_On { get { return ResourceManager.GetString("OverdriveState_On", resourceCulture); } } /// /// Looks up a localized string similar to Off. /// public static string PanelLogoBacklightState_Off { get { return ResourceManager.GetString("PanelLogoBacklightState_Off", resourceCulture); } } /// /// Looks up a localized string similar to On. /// public static string PanelLogoBacklightState_On { get { return ResourceManager.GetString("PanelLogoBacklightState_On", resourceCulture); } } /// /// Looks up a localized string similar to Off. /// public static string PortsBacklightState_Off { get { return ResourceManager.GetString("PortsBacklightState_Off", resourceCulture); } } /// /// Looks up a localized string similar to On. /// public static string PortsBacklightState_On { get { return ResourceManager.GetString("PortsBacklightState_On", resourceCulture); } } /// /// Looks up a localized string similar to Disabled. /// public static string PowerModeMappingMode_Disabled { get { return ResourceManager.GetString("PowerModeMappingMode_Disabled", resourceCulture); } } /// /// Looks up a localized string similar to Windows Power Mode. /// public static string PowerModeMappingMode_WindowsPowerMode { get { return ResourceManager.GetString("PowerModeMappingMode_WindowsPowerMode", resourceCulture); } } /// /// Looks up a localized string similar to Windows Power Plan. /// public static string PowerModeMappingMode_WindowsPowerPlan { get { return ResourceManager.GetString("PowerModeMappingMode_WindowsPowerPlan", resourceCulture); } } /// /// Looks up a localized string similar to Balance. /// public static string PowerModeState_Balance { get { return ResourceManager.GetString("PowerModeState_Balance", resourceCulture); } } /// /// Looks up a localized string similar to Custom. /// public static string PowerModeState_GodMode { get { return ResourceManager.GetString("PowerModeState_GodMode", resourceCulture); } } /// /// Looks up a localized string similar to Performance. /// public static string PowerModeState_Performance { get { return ResourceManager.GetString("PowerModeState_Performance", resourceCulture); } } /// /// Looks up a localized string similar to Quiet. /// public static string PowerModeState_Quiet { get { return ResourceManager.GetString("PowerModeState_Quiet", resourceCulture); } } /// /// Looks up a localized string similar to High. /// public static string RGBKeyboardBacklightBrightness_High { get { return ResourceManager.GetString("RGBKeyboardBacklightBrightness_High", resourceCulture); } } /// /// Looks up a localized string similar to Low. /// public static string RGBKeyboardBacklightBrightness_Low { get { return ResourceManager.GetString("RGBKeyboardBacklightBrightness_Low", resourceCulture); } } /// /// Looks up a localized string similar to Breath. /// public static string RGBKeyboardBacklightEffect_Breath { get { return ResourceManager.GetString("RGBKeyboardBacklightEffect_Breath", resourceCulture); } } /// /// Looks up a localized string similar to Smooth. /// public static string RGBKeyboardBacklightEffect_Smooth { get { return ResourceManager.GetString("RGBKeyboardBacklightEffect_Smooth", resourceCulture); } } /// /// Looks up a localized string similar to Static. /// public static string RGBKeyboardBacklightEffect_Static { get { return ResourceManager.GetString("RGBKeyboardBacklightEffect_Static", resourceCulture); } } /// /// Looks up a localized string similar to Wave Left. /// public static string RGBKeyboardBacklightEffect_WaveLTR { get { return ResourceManager.GetString("RGBKeyboardBacklightEffect_WaveLTR", resourceCulture); } } /// /// Looks up a localized string similar to Wave Right. /// public static string RGBKeyboardBacklightEffect_WaveRTL { get { return ResourceManager.GetString("RGBKeyboardBacklightEffect_WaveRTL", resourceCulture); } } /// /// Looks up a localized string similar to Preset 4. /// public static string RGBKeyboardBacklightPreset_Four { get { return ResourceManager.GetString("RGBKeyboardBacklightPreset_Four", resourceCulture); } } /// /// Looks up a localized string similar to Off. /// public static string RGBKeyboardBacklightPreset_Off { get { return ResourceManager.GetString("RGBKeyboardBacklightPreset_Off", resourceCulture); } } /// /// Looks up a localized string similar to Preset 1. /// public static string RGBKeyboardBacklightPreset_One { get { return ResourceManager.GetString("RGBKeyboardBacklightPreset_One", resourceCulture); } } /// /// Looks up a localized string similar to Preset 3. /// public static string RGBKeyboardBacklightPreset_Three { get { return ResourceManager.GetString("RGBKeyboardBacklightPreset_Three", resourceCulture); } } /// /// Looks up a localized string similar to Preset 2. /// public static string RGBKeyboardBacklightPreset_Two { get { return ResourceManager.GetString("RGBKeyboardBacklightPreset_Two", resourceCulture); } } /// /// Looks up a localized string similar to Fast. /// public static string RGBKeyboardBacklightSpeed_Fast { get { return ResourceManager.GetString("RGBKeyboardBacklightSpeed_Fast", resourceCulture); } } /// /// Looks up a localized string similar to Fastest. /// public static string RGBKeyboardBacklightSpeed_Fastest { get { return ResourceManager.GetString("RGBKeyboardBacklightSpeed_Fastest", resourceCulture); } } /// /// Looks up a localized string similar to Slow. /// public static string RGBKeyboardBacklightSpeed_Slow { get { return ResourceManager.GetString("RGBKeyboardBacklightSpeed_Slow", resourceCulture); } } /// /// Looks up a localized string similar to Slowest. /// public static string RGBKeyboardBacklightSpeed_Slowest { get { return ResourceManager.GetString("RGBKeyboardBacklightSpeed_Slowest", resourceCulture); } } /// /// Looks up a localized string similar to Mute. /// public static string SpeakerState_Mute { get { return ResourceManager.GetString("SpeakerState_Mute", resourceCulture); } } /// /// Looks up a localized string similar to Unmute. /// public static string SpeakerState_Unmute { get { return ResourceManager.GetString("SpeakerState_Unmute", resourceCulture); } } /// /// Looks up a localized string similar to High. /// public static string SpectrumKeyboardBacklightBrightness_High { get { return ResourceManager.GetString("SpectrumKeyboardBacklightBrightness_High", resourceCulture); } } /// /// Looks up a localized string similar to Low. /// public static string SpectrumKeyboardBacklightBrightness_Low { get { return ResourceManager.GetString("SpectrumKeyboardBacklightBrightness_Low", resourceCulture); } } /// /// Looks up a localized string similar to Medium. /// public static string SpectrumKeyboardBacklightBrightness_Medium { get { return ResourceManager.GetString("SpectrumKeyboardBacklightBrightness_Medium", resourceCulture); } } /// /// Looks up a localized string similar to Off. /// public static string SpectrumKeyboardBacklightBrightness_Off { get { return ResourceManager.GetString("SpectrumKeyboardBacklightBrightness_Off", resourceCulture); } } /// /// Looks up a localized string similar to Bottom to Top. /// public static string SpectrumKeyboardBacklightDirection_BottomToTop { get { return ResourceManager.GetString("SpectrumKeyboardBacklightDirection_BottomToTop", resourceCulture); } } /// /// Looks up a localized string similar to Clockwise. /// public static string SpectrumKeyboardBacklightDirection_Clockwise { get { return ResourceManager.GetString("SpectrumKeyboardBacklightDirection_Clockwise", resourceCulture); } } /// /// Looks up a localized string similar to Counter Clockwise. /// public static string SpectrumKeyboardBacklightDirection_CounterClockwise { get { return ResourceManager.GetString("SpectrumKeyboardBacklightDirection_CounterClockwise", resourceCulture); } } /// /// Looks up a localized string similar to Left to Right. /// public static string SpectrumKeyboardBacklightDirection_LeftToRight { get { return ResourceManager.GetString("SpectrumKeyboardBacklightDirection_LeftToRight", resourceCulture); } } /// /// Looks up a localized string similar to Right to Left. /// public static string SpectrumKeyboardBacklightDirection_RightToLeft { get { return ResourceManager.GetString("SpectrumKeyboardBacklightDirection_RightToLeft", resourceCulture); } } /// /// Looks up a localized string similar to Top to Bottom. /// public static string SpectrumKeyboardBacklightDirection_TopToBottom { get { return ResourceManager.GetString("SpectrumKeyboardBacklightDirection_TopToBottom", resourceCulture); } } /// /// Looks up a localized string similar to Always. /// public static string SpectrumKeyboardBacklightEffectType_Always { get { return ResourceManager.GetString("SpectrumKeyboardBacklightEffectType_Always", resourceCulture); } } /// /// Looks up a localized string similar to Audio Bounce. /// public static string SpectrumKeyboardBacklightEffectType_AudioBounce { get { return ResourceManager.GetString("SpectrumKeyboardBacklightEffectType_AudioBounce", resourceCulture); } } /// /// Looks up a localized string similar to Audio Ripple. /// public static string SpectrumKeyboardBacklightEffectType_AudioRipple { get { return ResourceManager.GetString("SpectrumKeyboardBacklightEffectType_AudioRipple", resourceCulture); } } /// /// Looks up a localized string similar to Aurora Sync. /// public static string SpectrumKeyboardBacklightEffectType_AuroraSync { get { return ResourceManager.GetString("SpectrumKeyboardBacklightEffectType_AuroraSync", resourceCulture); } } /// /// Looks up a localized string similar to Color Change. /// public static string SpectrumKeyboardBacklightEffectType_ColorChange { get { return ResourceManager.GetString("SpectrumKeyboardBacklightEffectType_ColorChange", resourceCulture); } } /// /// Looks up a localized string similar to Color Pulse. /// public static string SpectrumKeyboardBacklightEffectType_ColorPulse { get { return ResourceManager.GetString("SpectrumKeyboardBacklightEffectType_ColorPulse", resourceCulture); } } /// /// Looks up a localized string similar to Color Wave. /// public static string SpectrumKeyboardBacklightEffectType_ColorWave { get { return ResourceManager.GetString("SpectrumKeyboardBacklightEffectType_ColorWave", resourceCulture); } } /// /// Looks up a localized string similar to Rain. /// public static string SpectrumKeyboardBacklightEffectType_Rain { get { return ResourceManager.GetString("SpectrumKeyboardBacklightEffectType_Rain", resourceCulture); } } /// /// Looks up a localized string similar to Rainbow Screw. /// public static string SpectrumKeyboardBacklightEffectType_RainbowScrew { get { return ResourceManager.GetString("SpectrumKeyboardBacklightEffectType_RainbowScrew", resourceCulture); } } /// /// Looks up a localized string similar to Rainbow Wave. /// public static string SpectrumKeyboardBacklightEffectType_RainbowWave { get { return ResourceManager.GetString("SpectrumKeyboardBacklightEffectType_RainbowWave", resourceCulture); } } /// /// Looks up a localized string similar to Ripple. /// public static string SpectrumKeyboardBacklightEffectType_Ripple { get { return ResourceManager.GetString("SpectrumKeyboardBacklightEffectType_Ripple", resourceCulture); } } /// /// Looks up a localized string similar to Smooth. /// public static string SpectrumKeyboardBacklightEffectType_Smooth { get { return ResourceManager.GetString("SpectrumKeyboardBacklightEffectType_Smooth", resourceCulture); } } /// /// Looks up a localized string similar to Type. /// public static string SpectrumKeyboardBacklightEffectType_Type { get { return ResourceManager.GetString("SpectrumKeyboardBacklightEffectType_Type", resourceCulture); } } /// /// Looks up a localized string similar to Slow. /// public static string SpectrumKeyboardBacklightSpeed_Speed1 { get { return ResourceManager.GetString("SpectrumKeyboardBacklightSpeed_Speed1", resourceCulture); } } /// /// Looks up a localized string similar to Medium. /// public static string SpectrumKeyboardBacklightSpeed_Speed2 { get { return ResourceManager.GetString("SpectrumKeyboardBacklightSpeed_Speed2", resourceCulture); } } /// /// Looks up a localized string similar to Fast. /// public static string SpectrumKeyboardBacklightSpeed_Speed3 { get { return ResourceManager.GetString("SpectrumKeyboardBacklightSpeed_Speed3", resourceCulture); } } /// /// Looks up a localized string similar to Dark. /// public static string Theme_Dark { get { return ResourceManager.GetString("Theme_Dark", resourceCulture); } } /// /// Looks up a localized string similar to Light. /// public static string Theme_Light { get { return ResourceManager.GetString("Theme_Light", resourceCulture); } } /// /// Looks up a localized string similar to System. /// public static string Theme_System { get { return ResourceManager.GetString("Theme_System", resourceCulture); } } /// /// Looks up a localized string similar to Off. /// public static string TouchpadLockState_Off { get { return ResourceManager.GetString("TouchpadLockState_Off", resourceCulture); } } /// /// Looks up a localized string similar to On. /// public static string TouchpadLockState_On { get { return ResourceManager.GetString("TouchpadLockState_On", resourceCulture); } } /// /// Looks up a localized string similar to Daily. /// public static string UpdateCheckFrequency_PerDay { get { return ResourceManager.GetString("UpdateCheckFrequency_PerDay", resourceCulture); } } /// /// Looks up a localized string similar to Every hour. /// public static string UpdateCheckFrequency_PerHour { get { return ResourceManager.GetString("UpdateCheckFrequency_PerHour", resourceCulture); } } /// /// Looks up a localized string similar to Monthly. /// public static string UpdateCheckFrequency_PerMonth { get { return ResourceManager.GetString("UpdateCheckFrequency_PerMonth", resourceCulture); } } /// /// Looks up a localized string similar to Every 3 hours. /// public static string UpdateCheckFrequency_PerThreeHours { get { return ResourceManager.GetString("UpdateCheckFrequency_PerThreeHours", resourceCulture); } } /// /// Looks up a localized string similar to Every 12 hours. /// public static string UpdateCheckFrequency_PerTwelveHours { get { return ResourceManager.GetString("UpdateCheckFrequency_PerTwelveHours", resourceCulture); } } /// /// Looks up a localized string similar to Weekly. /// public static string UpdateCheckFrequency_PerWeek { get { return ResourceManager.GetString("UpdateCheckFrequency_PerWeek", resourceCulture); } } /// /// Looks up a localized string similar to High. /// public static string WhiteKeyboardBacklightState_High { get { return ResourceManager.GetString("WhiteKeyboardBacklightState_High", resourceCulture); } } /// /// Looks up a localized string similar to Low. /// public static string WhiteKeyboardBacklightState_Low { get { return ResourceManager.GetString("WhiteKeyboardBacklightState_Low", resourceCulture); } } /// /// Looks up a localized string similar to Off. /// public static string WhiteKeyboardBacklightState_Off { get { return ResourceManager.GetString("WhiteKeyboardBacklightState_Off", resourceCulture); } } /// /// Looks up a localized string similar to Off. /// public static string WinKeyState_Off { get { return ResourceManager.GetString("WinKeyState_Off", resourceCulture); } } /// /// Looks up a localized string similar to On. /// public static string WinKeyState_On { get { return ResourceManager.GetString("WinKeyState_On", resourceCulture); } } } } ================================================ FILE: LenovoLegionToolkit.Lib/Resources/Resource.ar.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 مُخصّص النظام مغلق يعمل دائما يعمل أثناء النوم فقط غير مُفعّل مُفعّل مُفعّل, متأخر حفظ البطارية طبيعى الشحن السريع إيقاف Informs the user that Flip to Start is or will be turned off. مفعل Informs the user that Flip to Start is or will be turned on. إيقاف Informs the user that Fn Lock is or will be turned off. مفعل Informs the user that Fn Lock is or will be turned on. مغلق يعمل إيقاف Informs the user that HDR is or will be turned off. مفعل Informs the user that HDR is or will be turned on. كرت الشاشة الخارجى الوضع الهجين الوضع الهيجن التلقائى الوضع الهجين للكرت الداخلى إيقاف Informs the user that the microphone is or will be turned off. مفعل Informs the user that the microphone is or will be turned on. مركز القاعدة أو القاع شمال القاعدة أو القاع يمين القاعدة أو القاع المركز أو الوَسَط شمال المركز أو شمال الوَسَط يمين المركز أو يمين الوَسَط وَسَط القمة شمال القمة يمين القمة إيقاف Informs the user that One Level White Keyboard Backlight is or will be turned off. مفعل Informs the user that One Level White Keyboard Backlight is or will be turned on. إيقاف Informs the user that Overdrive is or will be turned off. مفعل Informs the user that Overdrive is or will be turned on. متزن مخصص الأداء هادء سريع الأسرع بطىء الأبطأ عالي الوضوح منخفض الوضوح سكون ناعم ثابت يسار الموجة يمين الموجة مغلق الوضع 1 الوضع 2 الوضع 3 عالي الوضوح منخفض الوضوح متوسط الوضوح غلق من القاع إلى القمة اتجاه عقارب الساعة تدوير في اتجاه عقارب الساعة من اليسار إلى اليمين من اليمين إلى اليسار من الأعلى إلى الأسفل دائماً صدى الصوت تموج الصوت مزامنة اورورا تغيير اللون تذبذب اللون موجة اللون ماطر خط قوس قزح دقيق موجة قوس قزح متموج ناعم نوع بطىء متوسط سريع داكن فاتح النظام إيقاف Informs the user that Touchpad Lock is or will be turned off. مفعل Informs the user that Touchpad Lock is or will be turned on. مرتفع منخفض مغلق إيقاف Informs the user that WinLock is or will be turned on. مفعل Informs the user that WinLock is or will be turned on. إيقاف مفعل مفعل إيقاف إيقاف محول التيار توصيل الطاقة عبر USB محول التيار و توصيل الطاقة عبر USB طويلة عادية قصيرة Shift Ctrl Alt مفعل إيقاف كتم إلغاء الكتم معطل وضع طاقة ويندوز خطة طاقة ويندوز الإعداد 4 يوميا كل ساعة شهريا كل 3 ساعات كل 12 ساعة أسبوعيا ================================================ FILE: LenovoLegionToolkit.Lib/Resources/Resource.bg.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Избран от потребителя Системен Изключен Включен, винаги Включен, в спящ режим Деактивиран Активиран Активиран, със закъснение Удължаване на живота Обикновен Бързо зареждане Изключено Informs the user that Flip to Start is or will be turned off. Включено Informs the user that Flip to Start is or will be turned on. Изключено Informs the user that Fn Lock is or will be turned off. Включено Informs the user that Fn Lock is or will be turned on. Изключен Включен Изключен Informs the user that HDR is or will be turned off. Включен Informs the user that HDR is or will be turned on. Допълнителна видео карта Хибриден Хибриден - автоматичен Hybrid-iGPU Изключен Informs the user that the microphone is or will be turned off. Включен Informs the user that the microphone is or will be turned on. Долу в средата Долу вляво Долу вдясно По средата Вляво от средата Вдясно от средата Горе в средата Горе вляво Горе вдясно Изключена Informs the user that One Level White Keyboard Backlight is or will be turned off. Включена Informs the user that One Level White Keyboard Backlight is or will be turned on. Изключен Informs the user that Overdrive is or will be turned off. Включен Informs the user that Overdrive is or will be turned on. Балансиран Избран от потребителя Производителен Тих Бърза Най-бърза Бавна Най-бавна Висока Ниска Дишане Гладък Статичен Вълня наляво Вълна надясно Изключена Предварително зададена 1 Предварително зададена 2 Предварително зададена 3 Висока Ниска Средна Изключена От долу нагоре По часовниковата стрелка Обратно на часовниковата стрелка От ляво надясно От дясно наляво От горе надолу Винаги Аудио отскачане Аудио кръгове Aurora Sync Сменящ се цвят Пулсиращ цвят Цветна вълна Дъжд Rainbow Screw Вълна от дъга Водни кръгове Гладък Вид Бавен Среден Бърз Тъмна Светла Системна Изключено Informs the user that Touchpad Lock is or will be turned off. Включено Informs the user that Touchpad Lock is or will be turned on. Висока Ниска Изключена Изключено Informs the user that WinLock is or will be turned on. Включено Informs the user that WinLock is or will be turned on. Изключена Включена Включена Изключена Изключено Зарядно USB Захранване с Висока Мощност Зарядно и USB Захранване с Висока Мощност Дълга Нормална Къса Shift Ctrl Alt Включено Изключено ================================================ FILE: LenovoLegionToolkit.Lib/Resources/Resource.bs.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Isključeno Upaljeno, uvijek Upaljeno, na spavanju Konzervacija Normalno Brzo punjenje Isključeno Uključeno Diskretna grafička Hibridni režim Hibridno-Auto Hibridno-iGPU Balansirano Prilagođen Performanse Tiho Brzo Najbrže Sporo Najsporije Visoko Nisko Disanje Glatko Statično Talas lijevo Talas desno Ugašeno Šablon 1 Šablon 3 Šablon 2 Tamna Svijetla Sistemska Visoko Nisko Isključeno ================================================ FILE: LenovoLegionToolkit.Lib/Resources/Resource.ca.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: LenovoLegionToolkit.Lib/Resources/Resource.cs.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Vlastní Systém Vypnut Vždy zapnut Zapnut, když spí Zakázán Povolen Povolen, opožděn Konzervativní Normální Rapidní nabíjení Vypnut Zapnut dGPU Hybrid Hybrid-Auto Hybrid-iGPU Dole uprostřed Vlevo dole Vpravo dole Střed Uprostřed vlevo Uprostřed vpravo Nahoře uprostřed Nahoře vlevo Nahoře vpravo Vyvážený Vlastní Výkon Tichý Rychlý Nejrychlejší Pomalý Nejpomalejší Vysoký Nízký Dech Hladký Statický Vlna vlevo Vlna vpravo Vypnut Předvolba 1 Předvolba 3 Předvolba 2 Vysoký Nízký Střední Vypnut Ze spoda nahoru Pravotočivě Levotočivě Zleva doprava Zprava doleva Od shora dolů Vždy Audio efekt zleva Audio efekt ze středu Synchronizace Aurora Barevná změna Barevný pulz Barevná vlna Déšť Duhový kolotoč Duhová vlna Vlna Hladký Typ Pomalý Střední Rychlý Tmavý Světlý Systém Vysoký Nízký Vypnut ================================================ FILE: LenovoLegionToolkit.Lib/Resources/Resource.de.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Benutzerdefiniert System Aus An, immer An, wenn schlafend Deaktiviert Aktiviert Aktiviert, verzögert Erhaltung Normal Schnellladen Aus Informs the user that Flip to Start is or will be turned off. An Informs the user that Flip to Start is or will be turned on. Aus Informs the user that Fn Lock is or will be turned off. An Informs the user that Fn Lock is or will be turned on. Ausgeschaltet Eingeschaltet Aus Informs the user that HDR is or will be turned off. An Informs the user that HDR is or will be turned on. dGPU Hybrid Hybrid-Auto Hybrid-iGPU Aus Informs the user that the microphone is or will be turned off. An Informs the user that the microphone is or will be turned on. Unten mittig Unten links Unten rechts Mittig Mittig links Mittig rechts Oben mittig Oben links Oben rechts Aus Informs the user that One Level White Keyboard Backlight is or will be turned off. An Informs the user that One Level White Keyboard Backlight is or will be turned on. Aus Informs the user that Overdrive is or will be turned off. An Informs the user that Overdrive is or will be turned on. Balance Benutzerdefiniert Performance Quiet Schnell Am schnellsten Langsam Am langsamsten Hoch Niedrig Atmen Sanft Statisch Welle links Welle rechts Aus Voreinstellung 1 Voreinstellung 3 Voreinstellung 2 Hoch Niedrig Mittel Aus Von unten nach oben Im Uhrzeigersinn Gegen den Uhrzeigersinn Von links nach rechts Von rechts nach links Von oben nach unten Immer Audio-Spektrum Audio-Wellen Aurora Sync Farbwechsel Farbimpuls Farbwelle Regen Regenbogen-Schraube Regenbogenwelle Kräuseln Sanft Tippen Langsam Mittel Schnell Dunkel Hell System Aus Informs the user that Touchpad Lock is or will be turned off. An Informs the user that Touchpad Lock is or will be turned on. Hoch Niedrig Aus Aus Informs the user that WinLock is or will be turned on. An Informs the user that WinLock is or will be turned on. Aus An An Aus Aus Netzadapter USB Power Delivery Netzadapter und USB-PD Lang Normal Kurz Umschalt Strg Alt An Aus Stummschaltung Stummschaltung aufheben Deaktiviert Windows Leistungsmodus Windows Energiesparplan Vorlage 4 Täglich Jede Stunde Monatlich Alle 3 Stunden Alle 12 Stunden Wöchentlich ================================================ FILE: LenovoLegionToolkit.Lib/Resources/Resource.el.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Προσαρμοσμένο Συστήματος Ανενεργό Πάντα ενεργό Ενεργό όταν είναι σε αδράνεια Ανενεργό Ενεργό Ενεργό, με καθυστέρηση Διατήρηση Κανονική Ταχεία Φόρτιση Ανενεργό Informs the user that Flip to Start is or will be turned off. Ενεργό Informs the user that Flip to Start is or will be turned on. Ανενεργό Informs the user that Fn Lock is or will be turned off. Ενεργό Informs the user that Fn Lock is or will be turned on. Απενεργοποίηση Ενεργοποίηση Ανενεργό Informs the user that HDR is or will be turned off. Ενεργό Informs the user that HDR is or will be turned on. dGPU Υβριδική Υβριδική-Αυτόματη Υβριδική-iGPU Ανενεργό Informs the user that the microphone is or will be turned off. Ενεργό Informs the user that the microphone is or will be turned on. Κάτω κέντρο Κάτω αριστερά Κάτω δεξιά Κέντρο Κέντρο αριστερά Κέντρο δεξιά Πάνω κέντρο Πάνω αριστερά Πάνω δεξιά Ανενεργό Informs the user that One Level White Keyboard Backlight is or will be turned off. Ενεργό Informs the user that One Level White Keyboard Backlight is or will be turned on. Ανενεργή Informs the user that Overdrive is or will be turned off. Ενεργή Informs the user that Overdrive is or will be turned on. Ισορροπημένη Προσαρμοσμένη Υψηλή απόδοση Αθόρυβη Γρήγορη Ταχύτατη Αργή Πολύ αργή Υψηλή Χαμηλή Αναπνοή Ομαλό Στατικό Κύμα Αριστερά Κύμα Δεξιά Ανενεργό Προεπιλογή 1 Προεπιλογή 3 Προεπιλογή 2 Υψηλή Χαμηλή Μέτρια Ανενεργή Από Κάτω προς τα Πάνω Δεξιόστροφα Αριστερόστροφα Αριστερά προς Δεξιά Δεξιά προς Αριστερά Από Πάνω προς τα Κάτω Πάντα Αναπήδηση Ήχου Κυματισμός Ήχου Συγχρονισμός Aurora Εναλλαγή Χρωμάτων Παλμός Χρωμάτων Κυματισμός Χρωμάτων Βροχή Κυκλικό Ουράνιο Τόξο Κυματισμός Ουράνιου Τόξου Κυματισμός Ομαλή Τύπος Αργή Μέτρια Γρήγορη Σκοτεινό Φωτεινό Συστήματος Ανενεργό Informs the user that Touchpad Lock is or will be turned off. Ενεργό Informs the user that Touchpad Lock is or will be turned on. Υψηλό Χαμηλό Ανενεργό Ανενεργό Informs the user that WinLock is or will be turned on. Ενεργό Informs the user that WinLock is or will be turned on. Ανενεργός Ενεργός Ενεργός Ανενεργός Ανενεργή Φορτιστής Ρεύματος USB Power Delivery Ρεύμα και USB PD Μεγάλη Κανονική Μικρή Shift Ctrl Alt Ενεργή Ανενεργή Σίγαση Τέλος σίγασης Απενεργοποιημένο Λειτουργία Ισχύος Windows Πλάνο Τροφοδοσίας Windows Προεπιλογή 4 Καθημερινά Ωριαία Μηνιαία Κάθε 3 ώρες Κάθε 12 ώρες Εβδομαδιαία ================================================ FILE: LenovoLegionToolkit.Lib/Resources/Resource.es.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Personalizado Sistema Apagado Siempre encendido Encendido, al suspender Deshabilitado Activado Activado, demorado Conservador Normal Carga rápida Apagado Informs the user that Flip to Start is or will be turned off. Encendido Informs the user that Flip to Start is or will be turned on. Apagado Informs the user that Fn Lock is or will be turned off. Encendido Informs the user that Fn Lock is or will be turned on. Desactivado Activado Apagado Informs the user that HDR is or will be turned off. Encendido Informs the user that HDR is or will be turned on. dGPU Híbrido Híbrido-automático Híbrido-iGPU Apagado Informs the user that the microphone is or will be turned off. Encendido Informs the user that the microphone is or will be turned on. Abajo en el centro Abajo a la izquierda Abajo a la derecha Centrado Centro izquierda Centro derecha Arriba en el centro Arriba a la izquierda Arriba a la derecha Apagado Informs the user that One Level White Keyboard Backlight is or will be turned off. Encendido Informs the user that One Level White Keyboard Backlight is or will be turned on. Apagado Informs the user that Overdrive is or will be turned off. Encendido Informs the user that Overdrive is or will be turned on. Equilibrado Personalizado Rendimiento Tranquilo Rápido Más rápido Lento Más lento Alto Bajo Respiración Suave Estático Ola (desde la izquierda) A la derecha Apagado Predefinido 1 Predefinido 3 Predefinido 2 Alto Bajo Medio Apagado De abajo a arriba En el sentido de las agujas del reloj En el sentido contrario a las agujas del reloj De izquierda a derecha De derecha a izquierda De arriba a abajo Siempre Retorno de audio Oscilación de audio Aurora Sync Cambio de color Emisión de color Ola de Color Lluvia Rosca de arco iris Onda de arco iris Ondulación Suave Tipo Lento Medio Rápido Oscuro Claro Sistema Apagado Informs the user that Touchpad Lock is or will be turned off. Encendido Informs the user that Touchpad Lock is or will be turned on. Alta Baja Apagado Apagado Informs the user that WinLock is or will be turned on. Encendido Informs the user that WinLock is or will be turned on. Apagado Encendido Encendido Apagado Apagado Adaptador de corriente alterna Salida de alimentación USB DP de CA y USB Larga Normal Corta Mayúsculas Ctrl Alt Encendido Apagado Silenciar Activar sonido Deshabilitado Modo de energía de Windows Plan de energía de Windows Predefinido 4 Diariamente Cada hora Mensualmente Cada 3 horas Cada 12 horas Semanalmente ================================================ FILE: LenovoLegionToolkit.Lib/Resources/Resource.fr.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Personnalisé Système Éteint Allumé, toujours Allumé, en veille Désactivé Activé Activé, avec délai Conservation Normal Charge rapide Off Informs the user that Flip to Start is or will be turned off. Activé Informs the user that Flip to Start is or will be turned on. Désactiver Informs the user that Fn Lock is or will be turned off. On Informs the user that Fn Lock is or will be turned on. Désactiver Activé Off Informs the user that HDR is or will be turned off. On Informs the user that HDR is or will be turned on. dGPU Hybride Hybride-Auto Hybride-iGPU Off Informs the user that the microphone is or will be turned off. On Informs the user that the microphone is or will be turned on. En bas au centre En bas à gauche En bas à droite Centre Au centre à gauche Au centre à droite En haut au centre En haut à gauche En haut à droite Off Informs the user that One Level White Keyboard Backlight is or will be turned off. On Informs the user that One Level White Keyboard Backlight is or will be turned on. Off Informs the user that Overdrive is or will be turned off. On Informs the user that Overdrive is or will be turned on. Équilibré Personnalisé Performance Silencieux Rapide Très rapide Lent Très lent Élevée Faible Respiration Lisse Fixe Vague gauche Vague droite Off Prédéfini 1 Prédéfini 3 Prédéfini 2 Haut Faible Intermédiaire Off De bas en haut Sens horaire Sens anti-horaire De Gauche à Droite De Droite à Gauche De Haut en Bas Toujours Rebond audio Ondulation audio Synchronisation avec Aurora Sync Changement de couleur Pulsation Vague Pluie Rotation arc-en-ciel Vague arc-en-ciel Ondulation Doux Taper Lent Moyen Rapide Sombre Clair Système Off Informs the user that Touchpad Lock is or will be turned off. On Informs the user that Touchpad Lock is or will be turned on. Élevé Faible Éteint Off Informs the user that WinLock is or will be turned on. On Informs the user that WinLock is or will be turned on. Off On On Off Off Adaptateur secteur USB Power Delivery Secteur et USB PD Longue Normale Courte Maj Ctrl Alt Activé Désactivé Muet Réactiver le son Désactivé Mode de gestion de l'alimentation Windows Plan d'alimentation Windows Pré-réglage 4 Tous les jours Toutes les heures Tous les mois Toutes les 3 heures Toutes les 12 heures Toutes les semaines ================================================ FILE: LenovoLegionToolkit.Lib/Resources/Resource.hu.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Egyéni Rendszer Kikapcsolva Mindig bekapcsolva Bekapcsolva, alvó állapot esetén Letiltva Engedélyezve Bekapcsolva, késleltetve Konzerváló mód Normál Gyorstöltés Ki Informs the user that Flip to Start is or will be turned off. Be Informs the user that Flip to Start is or will be turned on. Ki Informs the user that Fn Lock is or will be turned off. Be Informs the user that Fn Lock is or will be turned on. Kikapcsolva Bekapcsolva Ki Informs the user that HDR is or will be turned off. Be Informs the user that HDR is or will be turned on. Dedikált videókártya Hibrid Hibrid-Automata Hibrid-Integrált videókártya Ki Informs the user that the microphone is or will be turned off. Be Informs the user that the microphone is or will be turned on. Középen alul Bal alul Jobb alul Középen Középen balra Középen jobbra Középen fent Bal fent Jobb fent Ki Informs the user that One Level White Keyboard Backlight is or will be turned off. Be Informs the user that One Level White Keyboard Backlight is or will be turned on. Ki Informs the user that Overdrive is or will be turned off. Be Informs the user that Overdrive is or will be turned on. Kiegyensúlyozott Egyéni Teljesítménycentrikus Csendes Gyors Leggyorsabb Lassú Leglassabb Magas Alacsony Lélegző Sima Állandó Hullám (balra) Hullám (jobbra) Kikapcsolva Előre beállított 1 Előre beállított 3 Előre beállított 2 Magas Alacsony Közepes Kikapcsolva Lentről felfelé Óramutató járásával megegyező Óramutató járásával ellentétes Balról jobbra Jobbról balra Fentről lefelé Mindig Audio Bounce Audio hullámzás Aurora szinkronizáció Színváltós Szín pulzálás Színhullám Eső Szivárvány csavar Szivárvány hullám Hullámzás Sima Típus Lassú Közepes Gyors Sötét Világos Rendszer Ki Informs the user that Touchpad Lock is or will be turned off. Be Informs the user that Touchpad Lock is or will be turned on. Magas Alacsony Kikapcsolva Ki Informs the user that WinLock is or will be turned on. Be Informs the user that WinLock is or will be turned on. Ki Be Be Ki Ki Váltóáramú adapter USB energiaellátás (PD) AC és USB PD Hosszú Normál Rövid Shift Ctrl Alt Be Ki Némítás Némítás feloldása Letiltva Windows energiagazdálkodási mód Windows Energiaséma Előre beállított 4 Naponta Óránként Havonta 3 óránként 12 óránként Hetente ================================================ FILE: LenovoLegionToolkit.Lib/Resources/Resource.it.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Personalizzato Sistema Non attivo Acceso, sempre Acceso, anche in sospensione Disattivato Attivato Abilitato, con ritardo Conservazione Normale Ricarica Rapida Spento Informs the user that Flip to Start is or will be turned off. Attivo Informs the user that Flip to Start is or will be turned on. Spento Informs the user that Fn Lock is or will be turned off. Attivo Informs the user that Fn Lock is or will be turned on. Spento Acceso Spento Informs the user that HDR is or will be turned off. Attivo Informs the user that HDR is or will be turned on. dGPU Ibrido Ibrido-Automatico Hybrid-iGPU Spento Informs the user that the microphone is or will be turned off. Attivo Informs the user that the microphone is or will be turned on. In basso al centro In basso a sinistra In basso a destra Centro Centro sinistra Centro destra In alto al centro In alto a sinistra In alto a destra Spento Informs the user that One Level White Keyboard Backlight is or will be turned off. Attivo Informs the user that One Level White Keyboard Backlight is or will be turned on. Spento Informs the user that Overdrive is or will be turned off. Attivo Informs the user that Overdrive is or will be turned on. Bilanciato Personalizzato Alte prestazioni Silenzioso Veloce Velocissimo Lento Lentissimo Alto Basso Respiro Morbido Statico Onda (Sinistra) Onda (Destra) Spento Tema 1 Tema 3 Tema 2 Alto Basso Medio Spento Dal basso verso l'alto Senso orario Senso anti-orario Da sinistra a destra Da destra a sinistra Dall'alto verso il basso Sempre Audio Bounce Audio Ripple Aurora Sync Color Change Color Pulse Color Wave Pioggia Rainbow Screw Rainbow Wave Ripple Morbido Tipo Lento Medio Veloce Scuro Chiaro Sistema Spento Informs the user that Touchpad Lock is or will be turned off. Attivo Informs the user that Touchpad Lock is or will be turned on. Alto Basso Spento Spento Informs the user that WinLock is or will be turned on. Attivo Informs the user that WinLock is or will be turned on. USB Power Delivery AC and USB PD ================================================ FILE: LenovoLegionToolkit.Lib/Resources/Resource.ja.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 カスタム システム オフ 常時オン スリープ時オン 無効 有効 有効(遅延) 節電 通常 急速充電 オフ Informs the user that Flip to Start is or will be turned off. オン Informs the user that Flip to Start is or will be turned on. オフ Informs the user that Fn Lock is or will be turned off. オン Informs the user that Fn Lock is or will be turned on. 電源オフ 電源オン オフ Informs the user that HDR is or will be turned off. オン Informs the user that HDR is or will be turned on. dGPU ハイブリッド ハイブリッド - 自動 ハイブリッド - iGPU オフ Informs the user that the microphone is or will be turned off. オン Informs the user that the microphone is or will be turned on. 中央下部 左下部 右下部 中央 左中央 右中央 中央上部 左上部 右上部 オフ Informs the user that One Level White Keyboard Backlight is or will be turned off. オン Informs the user that One Level White Keyboard Backlight is or will be turned on. オフ Informs the user that Overdrive is or will be turned off. オン Informs the user that Overdrive is or will be turned on. バランス カスタム パフォーマンス 静音 高速 超高速 低速 超低速 明るい 暗い 点滅 グラデーション 静的 ウェーブ(左へ) ウェーブ(右へ) オフ プリセット 1 プリセット 3 プリセット 2 明るい 暗い 普通 オフ 下から上へ 時計回り 反時計回り 左から右へ 右から左へ 上から下へ 常時 色を変更 グラデーション タイプ 遅い 普通 速い ダーク ライト システム オフ Informs the user that Touchpad Lock is or will be turned off. オン Informs the user that Touchpad Lock is or will be turned on. 明るい 暗い オフ オフ Informs the user that WinLock is or will be turned on. オン Informs the user that WinLock is or will be turned on. オフ オン オン オフ オフ ACアダプタ USB Power Delivery AC と USB PD 長い 普通 短い Shift Ctrl Alt オン オフ ミュート ミュート解除 無効 Windows電源モード Windows電源プラン プリセット 4 毎日 毎時 毎月 3時間毎 12時間毎 毎週 ================================================ FILE: LenovoLegionToolkit.Lib/Resources/Resource.ko.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: LenovoLegionToolkit.Lib/Resources/Resource.lv.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Pielāgots Sistēma Izslēgts Ieslēgts, vienmēr Ieslēgts, miega režīmā Atspējots Iespējots Iespējots, aizkavēts Saudzēšana Normāls Ātrā Uzlāde Atspējots Informs the user that Flip to Start is or will be turned off. Iespējots Informs the user that Flip to Start is or will be turned on. Atspējots Informs the user that Fn Lock is or will be turned off. Iespējots Informs the user that Fn Lock is or will be turned on. Izslēgts Ieslēgts Atspējots Informs the user that HDR is or will be turned off. Iespējots Informs the user that HDR is or will be turned on. dGPU Hibrīds Hibrīds-Auto Hibrīds-iGPU Atspējots Informs the user that the microphone is or will be turned off. Iespējots Informs the user that the microphone is or will be turned on. Centrēts apakšā Apakšā pa kreisi Apakšā pa labi Centrā Centrā pa kreisi Centrā pa labi Augšējais centrs Augšā pa kreisi Augšā pa labi Atspējots Informs the user that One Level White Keyboard Backlight is or will be turned off. Iespējots Informs the user that One Level White Keyboard Backlight is or will be turned on. Atspējots Informs the user that Overdrive is or will be turned off. Iespējots Informs the user that Overdrive is or will be turned on. Līdzsvarots Pielāgots Veiktspēja Kluss Ātrs Ātrākais Lēns Lēnākais Augsts Zems Elpojošs Vienmērīgs Nekustīgs Vilnis pa Kreisi Vilnis pa Labi Atspējots Priekšiestatījums 1 Priekšiestatījums 3 Priekšiestatījums 2 Augsts Zems Vidējs Atspējots No Apakšas uz Augšu Pulksteņrādītāja virzienā Pret pulksteņrādītāja virzienu No kreisās uz labo No Labās uz Kreiso No Augšas uz Leju Vienmēr Audio Atlēciens Audio Svārstība Aurora Sync Krāsu Maiņa Krāsu Pulsācija Krāsu Vilnis Lietus Varavīksnes Skrūve Varavīksnes Vilnis Svārstība Vienmērīgs Veids Lēns Vidējs Ātrs Tumšs Gaišs Sistēma Atspējots Informs the user that Touchpad Lock is or will be turned off. Iespējots Informs the user that Touchpad Lock is or will be turned on. Augsts Zems Atspējots Atspējots Informs the user that WinLock is or will be turned on. Iespējots Informs the user that WinLock is or will be turned on. Atspējots Iespējots Iespējots Atspējots Atspējots Maiņstrāvas Adapteris USB Power Delivery Maiņstrāva un USB PD Ilgi Normāli Īsi ================================================ FILE: LenovoLegionToolkit.Lib/Resources/Resource.nl-nl.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Aangepast Systeem Uit Aan, altijd Aan, wanneer in slaapstand Uitgeschakeld Ingeschakeld Ingeschakeld, vertraagd Behoudmodus Normaal Snelladen Uit Informs the user that Flip to Start is or will be turned off. Aan Informs the user that Flip to Start is or will be turned on. Uit Informs the user that Fn Lock is or will be turned off. Aan Informs the user that Fn Lock is or will be turned on. Uitgeschakeld Ingeschakeld Uit Informs the user that HDR is or will be turned off. Aan Informs the user that HDR is or will be turned on. dGPU Hybride Hybride-Auto Hybride-iGPU Uit Informs the user that the microphone is or will be turned off. Aan Informs the user that the microphone is or will be turned on. Onder midden Linksonder Rechtsonder Centreren Linksmidden Rechtsmidden Middenboven Linksboven Rechtsboven Uit Informs the user that One Level White Keyboard Backlight is or will be turned off. Aan Informs the user that One Level White Keyboard Backlight is or will be turned on. Uit Informs the user that Overdrive is or will be turned off. Aan Informs the user that Overdrive is or will be turned on. Gebalanceerd Aangepast Prestatie Stil Snel Snelste Langzaam Langzaamst Hoog Laag Adem Vloeiend Statisch Golf (Links) Golf (Rechts) Uit Voorinstelling 1 Voorinstelling 3 Voorinstelling 2 Hoog Laag Middel Uit Beneden naar boven Met De Klok Mee Tegen De Klok in Links naar rechts Rechts naar links Boven naar Beneden Altijd Audio Bounce Audio Ripple Aurora Sync Kleur verandering Kleur puls Kleuren golf Regen Regenboog schroef Regenboog golf Golf Vloeiend Type Traag Middel Snel Donker Licht Systeem Uit Informs the user that Touchpad Lock is or will be turned off. Aan Informs the user that Touchpad Lock is or will be turned on. Hoog Laag Uit Uit Informs the user that WinLock is or will be turned on. Aan Informs the user that WinLock is or will be turned on. Uit Aan Aan Uit Uit Oplader USB-PD Oplader Oplader en USB-PD Lang Gemiddeld Kort Shift Ctrl Alt Aan Uit Volume dempen Dempen van volume opheffen Uitgeschakeld Windows Energiemodus Windows Energiebeheerschema Voorinstelling 4 Dagelijks Elk uur Maandelijks Elke 3 uur Elke 12 uur Wekelijks ================================================ FILE: LenovoLegionToolkit.Lib/Resources/Resource.pl.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Niestandardowy System Wyłączone Włączone, zawsze Włączone w trakcie uśpienia Wyłączone Włączone Włączone (z opóźnieniem) Oszczędzanie żywotności Normalny Szybkie ładowanie Wyłączone Informs the user that Flip to Start is or will be turned off. Włączone Informs the user that Flip to Start is or will be turned on. Wyłączone Informs the user that Fn Lock is or will be turned off. Włączone Informs the user that Fn Lock is or will be turned on. Wyłączony Włączony Wyłączone Informs the user that HDR is or will be turned off. Włączone Informs the user that HDR is or will be turned on. dGPU Hybrydowy Hybrydowy-Auto Hybrydowy-iGPU Wyłączone Informs the user that the microphone is or will be turned off. Włączone Informs the user that the microphone is or will be turned on. Wyśrodkowany na dole Dolny lewy róg Prawy dolny róg Wyśrodkowany Wyśrodkowane po lewej Wyśrodkowane po prawej Wyśrodkowany na górze Lewy górny róg Prawy górny róg Wyłączone Informs the user that One Level White Keyboard Backlight is or will be turned off. Włączone Informs the user that One Level White Keyboard Backlight is or will be turned on. Wyłączone Informs the user that Overdrive is or will be turned off. Włączone Informs the user that Overdrive is or will be turned on. Zrównoważony Niestandardowy Wydajny Cichy Szybko Najszybszy Wolny Najwolniejszy Wysoki Niski Oddech Łagodny Statyczny Fala z lewej Fala z prawej Wyłączone Ustawienie 1 Ustawienie 3 Ustawienie 2 Wysoki Niski Średni Wyłączone Od dołu do góry Zgodny z ruchem wskazówek zegara Odwrotnie do ruchu wskazówek zegara Z lewej do prawej Od prawej do lewej Od góry do dołu Zawsze Podbicie Audio Fala AuroraSync Zmiana kolorów Pulsujące kolory Falujące kolory Deszcz Deszcz skręcony Deszczowa fala Fala Łagodny Rodzaj Wolny Średni Szybki Ciemny Jasny System Wyłączone Informs the user that Touchpad Lock is or will be turned off. Włączone Informs the user that Touchpad Lock is or will be turned on. Wysoki Niski Wyłączone Wyłączone Informs the user that WinLock is or will be turned on. Włączone Informs the user that WinLock is or will be turned on. Wyłączone Włączone Włączone Wyłączone Wyłączone Zasilacz sieciowy USB Power Delivery Sieciowe i PD Długi Normalny Krótki Shift Ctrl Alt Włączone Wyłączone Wyciszenie Wyłącz wyciszenie Wyłączone Tryb zasilania systemu Windows Plan zasilania systemu Windows Ustawienie 4 Codziennie Co godzinę Co miesiąc Co 3 godziny Co 12 godzin Co tydzień ================================================ FILE: LenovoLegionToolkit.Lib/Resources/Resource.pt-br.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Personalizado Sistema Desligado Sempre Ativo Ativado, quando em suspensão Desativado Ativado Ativado, com atraso Conservador Normal Carga Rápida Desligado Informs the user that Flip to Start is or will be turned off. Ativado Informs the user that Flip to Start is or will be turned on. Desligado Informs the user that Fn Lock is or will be turned off. Ativado Informs the user that Fn Lock is or will be turned on. Desligado Ligado Desligado Informs the user that HDR is or will be turned off. Ativado Informs the user that HDR is or will be turned on. GPU Dedicada Modo Híbrido Modo Híbrido Automático Somente GPU Integrada Desligado Informs the user that the microphone is or will be turned off. Ativado Informs the user that the microphone is or will be turned on. Inferior centralizado Inferior Esquerdo Inferior Direito Centralizar Centro à esquerda Centro à direita Centro superior Superior Esquerdo Superior direito Desligado Informs the user that One Level White Keyboard Backlight is or will be turned off. Ativado Informs the user that One Level White Keyboard Backlight is or will be turned on. Desligado Informs the user that Overdrive is or will be turned off. Ativado Informs the user that Overdrive is or will be turned on. Equilibrado Personalizado Performance Silencioso Rápido Super-rápido Lento Muito lenta Alta Baixa Respiração Suave Estático Onda (para esquerda) Onda (para direita) Desligado Predefinição 1 Predefinição 3 Predefinição 2 Alto Baixo Médio Desligado De baixo para cima Sentido horário Sentido Anti-horário Da esquerda para a direita Da direita para a esquerda De cima para baixo Sempre Spectro de Áudio Onda de Áudio Aurora Sync Mudança de Cor Cores Pulsantes Onda de Cor Chuva Parafuso de Arco-íris Onda de Arco-íris Onda Suave Tipo Lento Médio Rápido Escuro Claro Sistema Desligado Informs the user that Touchpad Lock is or will be turned off. Ativado Informs the user that Touchpad Lock is or will be turned on. Alto Baixo Desligado Desligado Informs the user that WinLock is or will be turned on. Ativado Informs the user that WinLock is or will be turned on. Desligado Ativado Ativado Desligado Desligado Carregador Carregamento por USB Carregamento por Carregador e USB Longa Normal Curta Shift Ctrl Alt Ativado Desativado Mutar Ativar som Desativado Planos de Energia do Windows Planos de Energia do Windows Perfil 4 Diariamente A cada hora Mensalmente A cada 3 horas A cada 12 horas Semanalmente ================================================ FILE: LenovoLegionToolkit.Lib/Resources/Resource.pt.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Personalizado Sistema Desligado Sempre ligado Ligado, quando suspenso Desativado Ativado Ativado com atraso Conservação Normal Carregamento rápido Desligado Informs the user that Flip to Start is or will be turned off. Ligado Informs the user that Flip to Start is or will be turned on. Desligado Informs the user that Fn Lock is or will be turned off. Ligado Informs the user that Fn Lock is or will be turned on. Desligada Ligada Desligado Informs the user that HDR is or will be turned off. Ligado Informs the user that HDR is or will be turned on. dGPU Híbrido Híbrido-automático Híbrido-iGPU Desligado Informs the user that the microphone is or will be turned off. Ligado Informs the user that the microphone is or will be turned on. Centro inferior Canto inferior esquerdo Canto inferior direito Centro Centro à esquerda Centro à direita Centro superior Centro inferior Canto superior direito Desligada Informs the user that One Level White Keyboard Backlight is or will be turned off. Ligada Informs the user that One Level White Keyboard Backlight is or will be turned on. Desligado Informs the user that Overdrive is or will be turned off. Ligado Informs the user that Overdrive is or will be turned on. Equilibrado Personalizado Performance Silencioso Rápido Rapidíssimo Lento Lentíssimo Alta Baixo Respiração Suave Estático Onda para a esquerda Onda para a direita Desligado Predefinição 1 Predefinição 3 Predefinição 2 Alto Baixo Médio Desligado Baixo para Cima Sentido dos ponteiros do relógio Sentido oposto aos ponteiros do relógio Esquerda para Direita Direita para Esquerda Cima para Baixo Sempre Audio Bounce Audio Ripple Aurora Sync Mudança de cor Pulsação de cor Onda de cor Chuva Rainbow Screw Ondulação arco-íris Ondulação Suave Tipo Lento Médio Rápido Escuro Claro Sistema Desligado Informs the user that Touchpad Lock is or will be turned off. Ligado Informs the user that Touchpad Lock is or will be turned on. Alta Baixa Desligada Desligada Informs the user that WinLock is or will be turned on. Ligada Informs the user that WinLock is or will be turned on. Desligada Ligada Ligado Desligada Desligado Carregador Carregador USB PD Carregador e USB PD Longa Normal Curta Shift Ctrl Alt Ligado Desligado Silenciar Ativar som Desativado Modo de Energia do Windows Esquemas de Energia do Windows Predefinição 4 Diariamente A cada hora Mensalmente A cada 3 horas A cada 12 horas Semanalmente ================================================ FILE: LenovoLegionToolkit.Lib/Resources/Resource.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Custom System Off On, always On, when sleeping Disabled Enabled Enabled, delayed Conservation Normal Rapid Charge Off Informs the user that Flip to Start is or will be turned off. On Informs the user that Flip to Start is or will be turned on. Off Informs the user that Fn Lock is or will be turned off. On Informs the user that Fn Lock is or will be turned on. Powered Off Powered On Off Informs the user that HDR is or will be turned off. On Informs the user that HDR is or will be turned on. dGPU Hybrid Hybrid-Auto Hybrid-iGPU Off Informs the user that the microphone is or will be turned off. On Informs the user that the microphone is or will be turned on. Bottom center Bottom left Bottom right Center Center left Center right Top center Top left Top right Off Informs the user that One Level White Keyboard Backlight is or will be turned off. On Informs the user that One Level White Keyboard Backlight is or will be turned on. Off Informs the user that Overdrive is or will be turned off. On Informs the user that Overdrive is or will be turned on. Balance Custom Performance Quiet Fast Fastest Slow Slowest High Low Breath Smooth Static Wave Left Wave Right Off Preset 1 Preset 3 Preset 2 High Low Medium Off Bottom to Top Clockwise Counter Clockwise Left to Right Right to Left Top to Bottom Always Audio Bounce Audio Ripple Aurora Sync Color Change Color Pulse Color Wave Rain Rainbow Screw Rainbow Wave Ripple Smooth Type Slow Medium Fast Dark Light System Off Informs the user that Touchpad Lock is or will be turned off. On Informs the user that Touchpad Lock is or will be turned on. High Low Off Off Informs the user that WinLock is or will be turned on. On Informs the user that WinLock is or will be turned on. Off On On Off Off AC Adapter USB Power Delivery AC and USB PD Long Normal Short Shift Ctrl Alt On Off Mute Unmute Disabled Windows Power Mode Windows Power Plan Preset 4 Daily Every hour Monthly Every 3 hours Every 12 hours Weekly ================================================ FILE: LenovoLegionToolkit.Lib/Resources/Resource.ro.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Personalizat Sistem Dezactivat Activat, tot timpul Activat, doar in mod repaus Dezactivat Activat Activat, cu întârziere Conservare Normal Încărcare rapidă Oprit Pornit dGPU Hibrid Hibrid-Auto Hibrid-iGPU Mijloc jos Stânga jos Dreapta jos Centru Stânga mijloc Dreapta mijloc Mijloc sus Stânga sus Dreapta sus Echilibrat Personalizat Performant Liniștit Rapid Cel mai rapid Lent Cel mai lent Ridicată Scăzută Respirație Tranziție lină culori Static Val culori spre stânga Val culori spre dreapta Oprit Profil 1 Profil 3 Profil 2 Ridicată Scăzută Mediu Oprit De jos în sus În sensul acelor de ceas În sens invers acelor de ceas De la stânga la dreapta De la dreapta la stânga De sus în jos Întotdeauna Audio Bounce Audio Ripple Aurora Sync Schimbare culoare Puls de culoare Val de culoare Ploaie Curcubeu în spirală Curcubeu în valuri Ripple Tranziție lină culori Tip Lent Mediu Rapid Întunecat Luminos Sistem Ridicată Scăzută Oprit ================================================ FILE: LenovoLegionToolkit.Lib/Resources/Resource.ru.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Пользовательский Система Выкл. Вкл. всегда Вкл. в спящем режиме Отключено Включено Включено с задержкой Консервация Обычный Быстрая зарядка Откл. Informs the user that Flip to Start is or will be turned off. Вкл. Informs the user that Flip to Start is or will be turned on. Откл. Informs the user that Fn Lock is or will be turned off. Вкл. Informs the user that Fn Lock is or will be turned on. Отключен Задействован Откл. Informs the user that HDR is or will be turned off. Вкл. Informs the user that HDR is or will be turned on. Дискретная видеокарта Гибридный режим Hybrid-Auto Hybrid-iGPU Откл. Informs the user that the microphone is or will be turned off. Вкл. Informs the user that the microphone is or will be turned on. Внизу по центру Внизу слева Внизу справа Центр По центру слева По центру справа Вверху по центру Вверху слева Вверху справа Откл. Informs the user that One Level White Keyboard Backlight is or will be turned off. Вкл. Informs the user that One Level White Keyboard Backlight is or will be turned on. Откл. Informs the user that Overdrive is or will be turned off. Вкл. Informs the user that Overdrive is or will be turned on. Сбалансированный Пользовательский Макс. производительность Тихий режим Быстро Самая быстрая Медленно Самая медленная Высокая Низкая Дыхание Плавный Статичный Волна (влево) Волна (вправо) Откл. Пресет 1 Пресет 2 Пресет 3 Высокая Низкая Средняя Откл. Снизу вверх По часовой стрелке Против часовой стрелки Слева направо Справа налево Сверху вниз Всегда Аудиоотскок Звуковой пульс Aurora Sync Изменение цвета Цветовой импульс Цветовая волна Дождь Радужный винт Радужная волна Волна Плавный Набор текста Медленно Средняя Быстрая Темная Светлая Системная тема Откл. Informs the user that Touchpad Lock is or will be turned off. Вкл. Informs the user that Touchpad Lock is or will be turned on. Высокая Низкая Откл. Откл. Informs the user that WinLock is or will be turned on. Вкл. Informs the user that WinLock is or will be turned on. Откл. Вкл. Вкл. Откл. Откл. Сетевой адаптер Питание от USB Сетевой адаптер и питание по USB Долго По умолчанию Короткая Shift Ctrl Alt Вкл. Откл. Выкл. звук Вкл. звук Отключен Схема питания Windows Режим питания Windows Пресет 4 Ежедневно Каждый час Ежемесячно Каждые 3 часа Каждые 12 часов Еженедельно ================================================ FILE: LenovoLegionToolkit.Lib/Resources/Resource.sk.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Vlastný Systém Vypnuté Vždy zapnuté Zapnuté v spánku Zakázaný Povolený Povolený, oneskorený Šetrenie Normálny Rýchle nabíjanie Vypnutá Zapnutá dGPU Hybrid Hybrid-Auto Hybrid-iGPU Dole v strede Vľavo dole Vpravo dole Uprostred Uprostred vľavo Uprostred vpravo Hore v strede Vľavo hore Vpravo hore Vyvážený Vlastný Výkonný Tichý Rýchla Najrýchlejšia Pomalá Najpomalšia Vysoký Nízky Dych Hladký Statický Vlna vľavo Vlna vpravo Vypnutý Predvoľba 1 Predvoľba 3 Predvoľba 2 Vysoký Nízky Stredný Vypnutý Zdola hore V smere hodinových ručičiek V protismeru hodinových ručičiek Zľava doprava Sprava do ľava Zhora dole Vždy Zvukové skoky Zvukové zvlnenie Synch. Aurora Farebná zmena Farebný pulz Farebné vlny Dážď Dúhový kolotoč Dúhová vlna Zvlnenie Hladké Typ Pomalá Stredná Rýchla Tmavý Svetlý Systém Silné Slabé Vypnuté ================================================ FILE: LenovoLegionToolkit.Lib/Resources/Resource.tr.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Özel Sistem Kapalı Her zaman açık Uykuda açık Devre dışı Etkin Etkin, gecikmeli Pil Korumalı Normal Hızlı Şarj Kapalı Informs the user that Flip to Start is or will be turned off. Açık Informs the user that Flip to Start is or will be turned on. Kapalı Informs the user that Fn Lock is or will be turned off. Açık Informs the user that Fn Lock is or will be turned on. Güç Kapatıldı Güç Açık Kapalı Informs the user that HDR is or will be turned off. Açık Informs the user that HDR is or will be turned on. dGPU Hibrit Hibrit-Otomatik Hibrit-iGPU Kapalı Informs the user that the microphone is or will be turned off. Açık Informs the user that the microphone is or will be turned on. Alt orta Sol alt Sağ alt Orta Sol orta Sağ orta Üst orta Sol üst Sağ üst Kapalı Informs the user that One Level White Keyboard Backlight is or will be turned off. Açık Informs the user that One Level White Keyboard Backlight is or will be turned on. Kapalı Informs the user that Overdrive is or will be turned off. Açık Informs the user that Overdrive is or will be turned on. Denge Özel Performans Sessiz Hızlı En Hızlı Yavaş En yavaş Yüksek Düşük Esinti Akıcı Sabit Soldan Dalga Sağdan Dalga Kapalı Ön ayar 1 Ön ayar 3 Ön ayar 2 Yüksek Düşük Orta Kapalı Aşağıdan Yukarıya Saat Yönünde Saat Yönünün Tersi Soldan Sağa Sağdan Sola Yukarıdan Aşağıya Her Zaman Ses Zıplaması Ses Dalgalanması Aurora Sync Renk Değiştir Renk Titreşimi Renk Dalgası Yağmur Gökkuşağı Döngüsü Gökkuşağı Dalgası Dalgalanma Akıcı Tür Yavaş Orta Hızlı Koyu Açık Sistem Kapalı Informs the user that Touchpad Lock is or will be turned off. Açık Informs the user that Touchpad Lock is or will be turned on. Yüksek Düşük Kapalı Kapalı Informs the user that WinLock is or will be turned on. Açık Informs the user that WinLock is or will be turned on. Kapalı Açık Açık Kapalı Kapalı AC Adaptör USB Güç Dağıtımı AC ve USB GD Uzun Normal Kısa Shift Ctrl Alt Açık Kapalı Sustur Sesi aç Devre Dışı Windows Güç Modu Windows Güç Planı Ön ayar 4 Günlük Her saat Aylık Her 3 saatte Her 12 saatte Haftalık ================================================ FILE: LenovoLegionToolkit.Lib/Resources/Resource.uk.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Користувальницький Системний Вимкнено Завжди Під час сну Вимкнено Увімкнено Увімкнено із затримкою Збереження Звичайний Швидка зарядка Вимкнено Informs the user that Flip to Start is or will be turned off. Увімкнено Informs the user that Flip to Start is or will be turned on. Вимкнено Informs the user that Fn Lock is or will be turned off. Увімкнено Informs the user that Fn Lock is or will be turned on. Вимкнений Увімкнений Вимкнено Informs the user that HDR is or will be turned off. Увімкнено Informs the user that HDR is or will be turned on. Дискр. ГП Гібридний Гібридний-Авто Гібридний-іГП Вимкнений Informs the user that the microphone is or will be turned off. Увімкнений Informs the user that the microphone is or will be turned on. Внизу по центру Внизу зліва Внизу справа Центр По центру ліворуч По центру праворуч Вгорі по центру Вгорі зліва Вгорі справа Вимкнений Informs the user that One Level White Keyboard Backlight is or will be turned off. Увімкнений Informs the user that One Level White Keyboard Backlight is or will be turned on. Вимкнений Informs the user that Overdrive is or will be turned off. Увімкнений Informs the user that Overdrive is or will be turned on. Збалансований Користувальницький Продуктивність Тихий Швидка Найшвидша Повільна Найповільніша Яскрава Тьмяна Дихання Плавний Статичний Хвиля Ліворуч Хвиля Праворуч Вимкнена Шаблон 1 Шаблон 3 Шаблон 2 Яскрава Тьмяна Середня Вимкнена Знизу вгору За годинниковою стрілкою Проти годинникової стрілки Зліва направо Справа наліво Згори вниз Постійно Звуковий відскок Звукова пульсація Aurora Sync Зміна кольору Пульсація кольору Колірні хвилі Дощ Райдужний гвинт Райдужні хвилі Пульсація Плавна Тип Повільна Середня Швидка Темна Світла Системна Вимкнений Informs the user that Touchpad Lock is or will be turned off. Увімкнений Informs the user that Touchpad Lock is or will be turned on. Яскрава Тьмяна Вимк. Вимкнена Informs the user that WinLock is or will be turned on. Увімкнена Informs the user that WinLock is or will be turned on. Вимкнені Увімкнені Увімкнено Вимкнено Вимкнено Від мережі Від USB Power Delivery Від мережі та USB PD Довга Звичайна Коротка Shift Ctrl Alt Увімкнене Вимкнене Вимкнути звук Увімкнути звук Вимкнений Режим живлення Windows План живлення Windows Шаблон 4 Щоденно Щогодини Щомісяця Що 3 години Що 12 годин Щотижня ================================================ FILE: LenovoLegionToolkit.Lib/Resources/Resource.uz-latn-uz.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Arnawlı Sistema Òshik Hámiyshe qosıwlı Qosıwlı, uyqı rejiminde Óshirilgen Iske túsirilgen Irkilis penen iske túsirilgen Saqlaw Ádettegidey Tez zaryadlaw Óshirilgen Informs the user that Flip to Start is or will be turned off. Janıq Informs the user that Flip to Start is or will be turned on. Óshirilgen Informs the user that Fn Lock is or will be turned off. Janıq Informs the user that Fn Lock is or will be turned on. Óshirilgen Qosılǵan Óshirilgen Informs the user that HDR is or will be turned off. Janıq Informs the user that HDR is or will be turned on. Bólekli videokarta Gibrid rejimi Gibrid-Avto Gibrid-iGPU Óshirilgen Informs the user that the microphone is or will be turned off. Janıq Informs the user that the microphone is or will be turned on. Tómende orayda Tómen shep Tómen oń Oray Orayda shepte Orayda ońda Joqarıda orayda Joqarıda shepte Joqarıda ońda Óshirilgen Informs the user that One Level White Keyboard Backlight is or will be turned off. Janıq Informs the user that One Level White Keyboard Backlight is or will be turned on. Óshirilgen Informs the user that Overdrive is or will be turned off. Janıq Informs the user that Overdrive is or will be turned on. Teńlestirilgen Arnawlı Maksimal ónimdarlıq Tınısh rejim Tez Júdá tez Ásten Júdá ásten Joqarı Pás Nápes Tegis Háreketsiz Tolqın shepke Tolqın ońǵa Óshik Sazlawlar jıyındısı 1 Sazlawlar jıyındısı 3 Sazlawlar jıyındısı 2 Joqarı Pás Ortasha Óshik Tómennen joqarıǵa Saat jónelisi boyınsha Saat jónelisine qarsı Shepten ońǵa Ońnan shepke Joqarıdan tómenge Hámiyshe Shawqım Dawıs tolqını Aurora Sync Reńdi ózgertiw Reń pulsi Reń tolqını Jawın Reńli vint Ayqulaq tolqın Tolqın Tegis Túri Áste Ortasha Tez Qarańǵı Jaqtı Sistema teması Óshirilgen Informs the user that Touchpad Lock is or will be turned off. Janıq Informs the user that Touchpad Lock is or will be turned on. Joqarı Pás Óshirilgen Óshirilgen Informs the user that WinLock is or will be turned on. Janıq Informs the user that WinLock is or will be turned on. Óshirilgen Janıq Janıq Óshirilgen Óshirilgen AC Adapteri USB Quwat Jetkiziw AC hám USB PD Uzaq Ádettegidey Qısqa Shift Ctrl Alt Janıq Óshik ================================================ FILE: LenovoLegionToolkit.Lib/Resources/Resource.vi.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Tùy Chỉnh Hệ thống Tắt Luôn Bật Bật, Chỉ khi Ngủ Tắt Bật Bật với độ trễ Bảo vệ pin Bình thường Sạc Nhanh Tắt Informs the user that Flip to Start is or will be turned off. Bật Informs the user that Flip to Start is or will be turned on. Tắt Informs the user that Fn Lock is or will be turned off. Bật Informs the user that Fn Lock is or will be turned on. Ngắt kích hoạt Kích hoạt Tắt Informs the user that HDR is or will be turned off. Bật Informs the user that HDR is or will be turned on. GPU rời Hybrid Hybrid- Tự động Hybrid- chỉ GPU tích hợp Tắt Informs the user that the microphone is or will be turned off. Bật Informs the user that the microphone is or will be turned on. Ở dưới chính giữa Góc dưới bên trái Góc dưới bên phải Ở giữa Ở giữa bên trái Ở giữa bên phải Ở trên chính giữa Góc trên bên trái Góc trên bên phải Tắt Informs the user that One Level White Keyboard Backlight is or will be turned off. Bật Informs the user that One Level White Keyboard Backlight is or will be turned on. Tắt Informs the user that Overdrive is or will be turned off. Bật Informs the user that Overdrive is or will be turned on. Cân Bằng Tùy Chỉnh Hiệu Suất Im Lặng Nhanh Nhanh nhất Chậm Chậm nhất Cao Thấp Thở Mượt Tĩnh Sóng, bên trái Sóng, bên phải Tắt Thiết lập 1 Thiết lập 3 Thiết lập 2 Cao Thấp Trung bình Tắt Từ dưới lên trên Xuôi chiều kim đồng hồ Ngược chiều kim đồng hồ Trái sang phải Phải sang trái Từ trên xuống dưới Luôn luôn Audio Bounce Audio Ripple Aurora Sync Đổi màu Nhịp điệu Sóng 7 màu Mưa Xoáy cầu vồng Sóng cầu vồng Gợn sóng Mượt Chậm Trung bình Nhanh Tối Sáng Hệ thống Tắt Informs the user that Touchpad Lock is or will be turned off. Bật Informs the user that Touchpad Lock is or will be turned on. Cao Thấp Tắt Tắt Informs the user that WinLock is or will be turned on. Bật Informs the user that WinLock is or will be turned on. Tắt Bật Bật Tắt Tắt Bộ sạc USB Power Delivery AC và USB PD Dài Bình thường Ngắn Shift Ctrl Alt Bật Tắt Tắt tiếng Bật tiếng Tắt Chế độ nguồn Power Plan Thiết lập 4 Hàng ngày Mỗi giờ Hàng tháng Mỗi 3 tiếng Mỗi 12 tiếng Hàng tuần ================================================ FILE: LenovoLegionToolkit.Lib/Resources/Resource.zh-hans.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 自定义 跟随系统 关闭 保持开启 仅在睡眠时 关闭 开启 延迟开启 养护 常规 快充 Informs the user that Flip to Start is or will be turned off. Informs the user that Flip to Start is or will be turned on. Informs the user that Fn Lock is or will be turned off. Informs the user that Fn Lock is or will be turned on. 关闭 启用 Informs the user that HDR is or will be turned off. Informs the user that HDR is or will be turned on. 独显直连模式 混合模式 混合自动模式 混合核显模式 Informs the user that the microphone is or will be turned off. Informs the user that the microphone is or will be turned on. 底部居中 左下角 右下角 居中 中间左侧 中间右侧 顶部居中 左上角 右上角 Informs the user that One Level White Keyboard Backlight is or will be turned off. Informs the user that One Level White Keyboard Backlight is or will be turned on. Informs the user that Overdrive is or will be turned off. Informs the user that Overdrive is or will be turned on. 均衡模式 自定义模式 野兽模式 安静模式 最快 最慢 高亮度 低亮度 呼吸 平滑 静态 从左向右 从右向左 关闭 预设 1 预设 3 预设 2 从下到上 顺时针 逆时针 从左到右 从右到左 从上到下 照明 音律跳动 音律涟漪 屏光同步 光谱循环 色彩脉冲 波浪 雨滴 螺旋彩虹 彩虹波 涟漪 晴朗 键入 深色模式 浅色模式 跟随系统 Informs the user that Touchpad Lock is or will be turned off. Informs the user that Touchpad Lock is or will be turned on. 高亮度 低亮度 关闭 Informs the user that WinLock is or will be turned on. Informs the user that WinLock is or will be turned on. 关闭 方形电源口 USB PD 充电口 方形电源口与 PD 充电口 较长 默认 较短 Shift Ctrl Alt 静音 解除静音 禁用 Windows 电源模式 Windows 电源计划 预设 4 每日 每小时 每月 每三小时 每十二小时 每周 ================================================ FILE: LenovoLegionToolkit.Lib/Resources/Resource.zh-hant.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 客製化 跟隨系統 關閉 保持開啟 僅限睡眠時 停用 啟用 延遲開啟 保養 一般 快速充電 關閉 Informs the user that Flip to Start is or will be turned off. 開啟 Informs the user that Flip to Start is or will be turned on. 關閉 Informs the user that Fn Lock is or will be turned off. 開啟 Informs the user that Fn Lock is or will be turned on. 關機 已啟動 關閉 Informs the user that HDR is or will be turned off. 開啟 Informs the user that HDR is or will be turned on. 獨立顯示卡直接連接模式(dGPU) 混合模式(Hybrid) 混合自動模式(Hybrid-Auto) 混合內建顯示卡模式(Hybrid-iGPU) 關閉 Informs the user that the microphone is or will be turned off. 開啟 Informs the user that the microphone is or will be turned on. 底部置中 左下角 右下角 置中 置中左側 置中右側 頂部置中 左上角 右上角 關閉 Informs the user that One Level White Keyboard Backlight is or will be turned off. 開啟 Informs the user that One Level White Keyboard Backlight is or will be turned on. 關閉 Informs the user that Overdrive is or will be turned off. 開啟 Informs the user that Overdrive is or will be turned on. 平衡模式 客製化 效能模式 靜音模式 最快 最慢 呼吸 平滑 靜態 從左向右波浪 從右向左波浪 關閉 預設 1 預設 3 預設 2 關閉 從底部到頂部 順時針 逆時針 從左到右 從又到左 從頂部到底部 照明 韻律跳動 韻律漣漪 Aurora Sync(顯示器燈光同步) 光譜循環 色彩脈衝 色彩波浪 雨滴 螺旋彩虹 彩虹波浪 漣漪 平滑 輸入 深色模式 淺色模式 跟隨系統 關閉 Informs the user that Touchpad Lock is or will be turned off. 開啟 Informs the user that Touchpad Lock is or will be turned on. 高亮度 低亮度 關閉 關閉 Informs the user that WinLock is or will be turned on. 開啟 Informs the user that WinLock is or will be turned on. 關閉 開啟 開啟 關閉 關閉 AC 方形電源孔 USB PD 充電孔 AC 方形電源孔和 USB PD 電源孔 一般 Shift Ctrl Alt 開啟 關閉 靜音 解除靜音 已停用 Windows 電源模式 Windows 電源計劃 預設 4 每天 每小時 每月 每 3 小時 每 12 小時 每週 ================================================ FILE: LenovoLegionToolkit.Lib/Services/BatteryDischargeRateMonitorService.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.System; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.Services; public class BatteryDischargeRateMonitorService { private CancellationTokenSource? _cts; private Task? _refreshTask; public async Task StartStopIfNeededAsync() { await StopAsync().ConfigureAwait(false); if (_refreshTask != null) return; if (_cts is not null) await _cts.CancelAsync().ConfigureAwait(false); _cts = new CancellationTokenSource(); var token = _cts.Token; _refreshTask = Task.Run(async () => { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Battery monitoring service started..."); while (!token.IsCancellationRequested) { try { Battery.SetMinMaxDischargeRate(); await Task.Delay(TimeSpan.FromSeconds(3), token).ConfigureAwait(false); } catch (OperationCanceledException) { } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Battery monitoring service failed.", ex); } } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Battery monitoring service stopped."); }, token); } public async Task StopAsync() { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Stopping..."); if (_cts is not null) await _cts.CancelAsync().ConfigureAwait(false); _cts = null; if (_refreshTask is not null) await _refreshTask.ConfigureAwait(false); _refreshTask = null; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Stopped."); } } ================================================ FILE: LenovoLegionToolkit.Lib/Settings/AbstractSettings.cs ================================================ using System; using System.IO; using LenovoLegionToolkit.Lib.Utils; using Newtonsoft.Json; using Newtonsoft.Json.Converters; namespace LenovoLegionToolkit.Lib.Settings; public abstract class AbstractSettings where T : class, new() { protected readonly JsonSerializerSettings JsonSerializerSettings; private readonly string _settingsStorePath; private readonly string _fileName; protected virtual T Default => new(); public T Store => _store ??= LoadStore() ?? Default; private T? _store; protected AbstractSettings(string filename) { JsonSerializerSettings = new() { Formatting = Formatting.Indented, TypeNameHandling = TypeNameHandling.Auto, ObjectCreationHandling = ObjectCreationHandling.Replace, Converters = { new StringEnumConverter() } }; _fileName = filename; _settingsStorePath = Path.Combine(Folders.AppData, _fileName); } public void SynchronizeStore() { var settingsSerialized = JsonConvert.SerializeObject(_store, JsonSerializerSettings); File.WriteAllText(_settingsStorePath, settingsSerialized); } public virtual T? LoadStore() { T? store = null; try { var settingsSerialized = File.ReadAllText(_settingsStorePath); store = JsonConvert.DeserializeObject(settingsSerialized, JsonSerializerSettings); if (store is null) TryBackup(); } catch { TryBackup(); } return store; } private void TryBackup() { try { if (!File.Exists(_settingsStorePath)) return; var backupFileName = $"{Path.GetFileNameWithoutExtension(_fileName)}_backup_{DateTime.UtcNow:yyyyMMddHHmmss}{Path.GetExtension(_fileName)}"; var backupFilePath = Path.Combine(Folders.AppData, backupFileName); File.Copy(_settingsStorePath, backupFilePath); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Unable to create backup for {_fileName}", ex); } } } ================================================ FILE: LenovoLegionToolkit.Lib/Settings/ApplicationSettings.cs ================================================ using System; using System.Collections.Generic; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Settings; public class ApplicationSettings : AbstractSettings { public class Notifications { public bool UpdateAvailable { get; set; } = true; public bool CapsNumLock { get; set; } public bool FnLock { get; set; } public bool TouchpadLock { get; set; } = true; public bool KeyboardBacklight { get; set; } = true; public bool CameraLock { get; set; } = true; public bool Microphone { get; set; } = true; public bool PowerMode { get; set; } public bool RefreshRate { get; set; } = true; public bool ACAdapter { get; set; } public bool SmartKey { get; set; } public bool AutomationNotification { get; set; } = true; } public class ApplicationSettingsStore { public Theme Theme { get; set; } public RGBColor? AccentColor { get; set; } public AccentColorSource AccentColorSource { get; set; } public PowerModeMappingMode PowerModeMappingMode { get; set; } = PowerModeMappingMode.WindowsPowerMode; public Dictionary PowerPlans { get; set; } = []; public Dictionary PowerModes { get; set; } = []; public bool MinimizeToTray { get; set; } = true; public bool MinimizeOnClose { get; set; } public WindowSize? WindowSize { get; set; } public bool DontShowNotifications { get; set; } public NotificationPosition NotificationPosition { get; set; } = NotificationPosition.BottomCenter; public NotificationDuration NotificationDuration { get; set; } = NotificationDuration.Normal; public bool NotificationAlwaysOnTop { get; set; } public bool NotificationOnAllScreens { get; set; } public Notifications Notifications { get; set; } = new(); public TemperatureUnit TemperatureUnit { get; set; } public List ExcludedRefreshRates { get; set; } = []; public WarrantyInfo? WarrantyInfo { get; set; } public Guid? SmartKeySinglePressActionId { get; set; } public Guid? SmartKeyDoublePressActionId { get; set; } public List SmartKeySinglePressActionList { get; set; } = []; public List SmartKeyDoublePressActionList { get; set; } = []; public bool SynchronizeBrightnessToAllPowerPlans { get; set; } public ModifierKey SmartFnLockFlags { get; set; } public bool ResetBatteryOnSinceTimerOnReboot { get; set; } } public ApplicationSettings() : base("settings.json") { JsonSerializerSettings.Converters.Add(new LegacyPowerPlanInstanceIdToGuidConverter()); } } internal class LegacyPowerPlanInstanceIdToGuidConverter : JsonConverter // Introduced in 2.12.0 { public override bool CanWrite => false; public override bool CanConvert(Type objectType) => objectType == typeof(Guid); public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) => throw new InvalidOperationException(); public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) { var value = reader.Value?.ToString() ?? string.Empty; const string prefix = "Microsoft:PowerPlan\\{"; const string suffix = "}"; var prefixIndex = value.Contains(prefix, StringComparison.InvariantCulture); var suffixIndex = value.IndexOf(suffix, StringComparison.InvariantCulture); if (prefixIndex && suffixIndex > 0) { value = value[..suffixIndex]; value = value[prefix.Length..]; } return Guid.Parse(value); } } ================================================ FILE: LenovoLegionToolkit.Lib/Settings/BalanceModeSettings.cs ================================================ using static LenovoLegionToolkit.Lib.Settings.BalanceModeSettings; namespace LenovoLegionToolkit.Lib.Settings; public class BalanceModeSettings() : AbstractSettings("balancemode.json") { public class BalanceModeSettingsStore { public bool AIModeEnabled { get; set; } } // ReSharper disable once StringLiteralTypo } ================================================ FILE: LenovoLegionToolkit.Lib/Settings/GPUOverclockSettings.cs ================================================ using static LenovoLegionToolkit.Lib.Settings.GPUOverclockSettings; namespace LenovoLegionToolkit.Lib.Settings; public class GPUOverclockSettings() : AbstractSettings("gpu_oc.json") { public class GPUOverclockSettingsStore { public bool Enabled { get; set; } public GPUOverclockInfo Info { get; set; } = GPUOverclockInfo.Zero; } } ================================================ FILE: LenovoLegionToolkit.Lib/Settings/GodModeSettings.cs ================================================ using System; using System.Collections.Generic; namespace LenovoLegionToolkit.Lib.Settings; public class GodModeSettings() : AbstractSettings("godmode.json") { public class GodModeSettingsStore { public class Preset { public string Name { get; init; } = string.Empty; public StepperValue? CPULongTermPowerLimit { get; init; } public StepperValue? CPUShortTermPowerLimit { get; init; } public StepperValue? CPUPeakPowerLimit { get; init; } public StepperValue? CPUCrossLoadingPowerLimit { get; init; } public StepperValue? CPUPL1Tau { get; init; } public StepperValue? APUsPPTPowerLimit { get; init; } public StepperValue? CPUTemperatureLimit { get; init; } public StepperValue? GPUPowerBoost { get; init; } public StepperValue? GPUConfigurableTGP { get; init; } public StepperValue? GPUTemperatureLimit { get; init; } public StepperValue? GPUTotalProcessingPowerTargetOnAcOffsetFromBaseline { get; init; } public StepperValue? GPUToCPUDynamicBoost { get; init; } public FanTable? FanTable { get; init; } public bool? FanFullSpeed { get; init; } public int? MinValueOffset { get; init; } public int? MaxValueOffset { get; init; } } public Guid ActivePresetId { get; set; } public Dictionary Presets { get; set; } = []; } // ReSharper disable once StringLiteralTypo } ================================================ FILE: LenovoLegionToolkit.Lib/Settings/IntegrationsSettings.cs ================================================ namespace LenovoLegionToolkit.Lib.Settings; public class IntegrationsSettings() : AbstractSettings("integrations.json") { public class IntegrationsSettingsStore { public bool HWiNFO { get; set; } public bool CLI { get; set; } } } ================================================ FILE: LenovoLegionToolkit.Lib/Settings/PackageDownloaderSettings.cs ================================================ using System.Collections.Generic; namespace LenovoLegionToolkit.Lib.Settings; public class PackageDownloaderSettings() : AbstractSettings("package_downloader.json") { public class PackageDownloaderSettingsStore { public string? DownloadPath { get; set; } public bool OnlyShowUpdates { get; set; } public HashSet HiddenPackages { get; set; } = []; } } ================================================ FILE: LenovoLegionToolkit.Lib/Settings/RGBKeyboardSettings.cs ================================================ using System.Collections.Generic; namespace LenovoLegionToolkit.Lib.Settings; public class RGBKeyboardSettings() : AbstractSettings("rgb_keyboard.json") { public class RGBKeyboardSettingsStore { public RGBKeyboardBacklightState State { get; set; } } protected override RGBKeyboardSettingsStore Default => new() { State = new(RGBKeyboardBacklightPreset.Off, new Dictionary { { RGBKeyboardBacklightPreset.One, new(RGBKeyboardBacklightEffect.Static, RGBKeyboardBacklightSpeed.Slowest, RGBKeyboardBacklightBrightness.Low, RGBColor.Green, RGBColor.Teal, RGBColor.Purple, RGBColor.Pink) }, { RGBKeyboardBacklightPreset.Two, new(RGBKeyboardBacklightEffect.Static, RGBKeyboardBacklightSpeed.Slowest, RGBKeyboardBacklightBrightness.Low, RGBColor.Red, RGBColor.Red, RGBColor.Red, RGBColor.Red) }, { RGBKeyboardBacklightPreset.Three, new(RGBKeyboardBacklightEffect.Breath, RGBKeyboardBacklightSpeed.Slowest, RGBKeyboardBacklightBrightness.Low, RGBColor.White,RGBColor.White,RGBColor.White,RGBColor.White) }, { RGBKeyboardBacklightPreset.Four, new(RGBKeyboardBacklightEffect.Smooth, RGBKeyboardBacklightSpeed.Slowest, RGBKeyboardBacklightBrightness.Low, RGBColor.White,RGBColor.White,RGBColor.White,RGBColor.White) }, }), }; } ================================================ FILE: LenovoLegionToolkit.Lib/Settings/SpectrumKeyboardSettings.cs ================================================ namespace LenovoLegionToolkit.Lib.Settings; public class SpectrumKeyboardSettings() : AbstractSettings("spectrum_keyboard.json") { public class SpectrumKeyboardSettingsStore { public KeyboardLayout? KeyboardLayout { get; set; } } } ================================================ FILE: LenovoLegionToolkit.Lib/Settings/SunriseSunsetSettings.cs ================================================ using System; namespace LenovoLegionToolkit.Lib.Settings; public class SunriseSunsetSettings() : AbstractSettings("sunrise_sunset.json") { public class SunriseSunsetSettingsStore { public DateTime? LastCheckDateTime { get; set; } public Time? Sunrise { get; set; } public Time? Sunset { get; set; } } } ================================================ FILE: LenovoLegionToolkit.Lib/Settings/UpdateCheckSettings.cs ================================================ using System; namespace LenovoLegionToolkit.Lib.Settings; public class UpdateCheckSettings() : AbstractSettings("update_check.json") { public class UpdateCheckSettingsStore { public DateTime? LastUpdateCheckDateTime { get; set; } public UpdateCheckFrequency UpdateCheckFrequency { get; set; } } protected override UpdateCheckSettingsStore Default => new() { LastUpdateCheckDateTime = null, UpdateCheckFrequency = UpdateCheckFrequency.PerDay }; } ================================================ FILE: LenovoLegionToolkit.Lib/SoftwareDisabler/AbstractSoftwareDisabler.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.ServiceProcess; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Utils; using TaskService = Microsoft.Win32.TaskScheduler.TaskService; namespace LenovoLegionToolkit.Lib.SoftwareDisabler; public class SoftwareDisablerException(string message, Exception innerException) : Exception(message, innerException); public abstract class AbstractSoftwareDisabler { public class AbstractSoftwareDisablerEventArgs : EventArgs { public SoftwareStatus Status { get; init; } } protected abstract IEnumerable ScheduledTasksPaths { get; } protected abstract IEnumerable ServiceNames { get; } protected abstract IEnumerable ProcessNames { get; } public event EventHandler? OnRefreshed; public Task GetStatusAsync() => Task.Run(() => { bool isEnabled; bool isInstalled; try { var services = RunningServices().ToArray(); var processes = RunningProcesses().ToArray(); if (Log.Instance.IsTraceEnabled) { Log.Instance.Trace($"Running services count: {services.Length}. [type={GetType().Name}, services={string.Join(",", services)}]"); Log.Instance.Trace($"Running processes count: {processes.Length}. [type={GetType().Name}, processes={string.Join(",", processes)}]"); } isEnabled = services.Length != 0 || processes.Length != 0; isInstalled = IsInstalled(); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Exception while getting status. [type={GetType().Name}]", ex); isEnabled = false; isInstalled = false; } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Status: {isEnabled},{isInstalled} [type={GetType().Name}]"); SoftwareStatus status; if (isEnabled) status = SoftwareStatus.Enabled; else if (!isInstalled) status = SoftwareStatus.NotFound; else status = SoftwareStatus.Disabled; OnRefreshed?.Invoke(this, new() { Status = status }); return status; }); public virtual Task EnableAsync() => Task.Run(async () => { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Enabling... [type={GetType().Name}]"); SetScheduledTasksEnabled(true); SetServicesEnabled(true); _ = await GetStatusAsync().ConfigureAwait(false); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Enabled [type={GetType().Name}]"); }); public virtual Task DisableAsync() => Task.Run(async () => { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Disabling... [type={GetType().Name}]"); SetScheduledTasksEnabled(false); SetServicesEnabled(false); await KillProcessesAsync().ConfigureAwait(false); _ = await GetStatusAsync().ConfigureAwait(false); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Disabled [type={GetType().Name}]"); }); private bool IsInstalled() => ServiceController.GetServices().Any(s => ServiceNames.Contains(s.ServiceName)); private IEnumerable RunningServices() { var services = ServiceController.GetServices(); return ServiceNames.Where(s => IsServiceEnabled(s, services)); } protected virtual IEnumerable RunningProcesses() { foreach (var process in Process.GetProcesses()) { foreach (var processName in ProcessNames) { var name = string.Empty; try { name = process.ProcessName; if (!name.StartsWith(processName, StringComparison.InvariantCultureIgnoreCase)) continue; } catch { /* Ignored. */ } if (!string.IsNullOrEmpty(name)) yield return name; } } } private static bool IsServiceEnabled(string serviceName, IEnumerable services) { try { var service = services.FirstOrDefault(s => s.ServiceName == serviceName); if (service is null) return false; return service.Status is not ServiceControllerStatus.Stopped; } catch (InvalidOperationException) { return false; } } private void SetScheduledTasksEnabled(bool enabled) { var taskService = TaskService.Instance; foreach (var path in ScheduledTasksPaths) SetTasksInFolderEnabled(taskService, path, enabled); } private void SetTasksInFolderEnabled(TaskService taskService, string path, bool enabled) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Setting tasks in folder {path} to {enabled}. [type={GetType().Name}]"); var folder = taskService.GetFolder(path); if (folder is null) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Folder not found [path={path}, type={GetType().Name}]]"); return; } foreach (var task in folder.Tasks.ToArray()) { task.Definition.Settings.Enabled = enabled; try { task.RegisterChanges(); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to register changes on task {task.Name} in {task.Path}.", ex); throw new SoftwareDisablerException($"Failed to register changes on task {task.Name} in {task.Path} [type={GetType().Name}]", ex); } } } private void SetServicesEnabled(bool enabled) { foreach (var serviceName in ServiceNames) SetServiceEnabled(serviceName, enabled); } private void SetServiceEnabled(string serviceName, bool enabled) { try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Setting service {serviceName} to {enabled}. [type={GetType().Name}]"); if (!ServiceController.GetServices().Any(s => s.ServiceName == serviceName)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Service {serviceName} not found. [type={GetType().Name}]"); return; } var service = new ServiceController(serviceName); try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Changing service {serviceName} start mode to {enabled}. [type={GetType().Name}]"); service.ChangeStartMode(enabled); if (enabled) { if (service.Status != ServiceControllerStatus.Running) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Starting service {serviceName}... [type={GetType().Name}]"); service.Start(); service.WaitForStatus(ServiceControllerStatus.Running); } else { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Will not start service {serviceName}. [status={service.Status}, type={GetType().Name}]]"); } } else { if (service.CanStop) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Stopping service {serviceName}... [type={GetType().Name}]"); service.Stop(); service.WaitForStatus(ServiceControllerStatus.Stopped); } else { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Will not stop service {serviceName}. [status={service.Status}, canStop={service.CanStop}, type={GetType().Name}]]"); } } } finally { service.Close(); } } catch (InvalidOperationException ex) when (ex.InnerException is Win32Exception { NativeErrorCode: 1060 }) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Service {serviceName} could not be set to {enabled}"); throw new SoftwareDisablerException(serviceName, ex); } } protected virtual async Task KillProcessesAsync() { foreach (var process in Process.GetProcesses()) foreach (var processName in ProcessNames) { try { if (process.ProcessName.StartsWith(processName, StringComparison.InvariantCultureIgnoreCase)) { process.Kill(true); await process.WaitForExitAsync().ConfigureAwait(false); } } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Couldn't kill process.", ex); } } } } ================================================ FILE: LenovoLegionToolkit.Lib/SoftwareDisabler/FnKeysDisabler.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.System; namespace LenovoLegionToolkit.Lib.SoftwareDisabler; public class FnKeysDisabler : AbstractSoftwareDisabler { protected override IEnumerable ScheduledTasksPaths => []; protected override IEnumerable ServiceNames => ["LenovoFnAndFunctionKeys"]; protected override IEnumerable ProcessNames => ["LenovoUtilityUI", "LenovoUtilityService", "LenovoSmartKey"]; public override async Task EnableAsync() { await base.EnableAsync().ConfigureAwait(false); SetUwpStartup("LenovoUtility", "LenovoUtilityID", true); } public override async Task DisableAsync() { await base.DisableAsync().ConfigureAwait(false); SetUwpStartup("LenovoUtility", "LenovoUtilityID", false); } protected override IEnumerable RunningProcesses() { var result = base.RunningProcesses().ToList(); try { foreach (var process in Process.GetProcessesByName("utility")) { var description = process.MainModule?.FileVersionInfo.FileDescription; if (description is null) continue; if (description.Equals("Lenovo Hotkeys", StringComparison.InvariantCultureIgnoreCase)) result.Add(process.ProcessName); } } catch { /* Ignored. */ } return result; } protected override async Task KillProcessesAsync() { await base.KillProcessesAsync().ConfigureAwait(false); try { foreach (var process in Process.GetProcessesByName("utility")) { var description = process.MainModule?.FileVersionInfo.FileDescription; if (description is null) continue; if (!description.Equals("Lenovo Hotkeys", StringComparison.InvariantCultureIgnoreCase)) continue; process.Kill(); await process.WaitForExitAsync().ConfigureAwait(false); } } catch { /* Ignored. */ } } private static void SetUwpStartup(string appPattern, string subKeyName, bool enabled) { const string hive = "HKEY_CURRENT_USER"; const string subKey = @"Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\SystemAppData"; const string valueName = "State"; var startupKey = Registry.GetSubKeys(hive, subKey).FirstOrDefault(s => s.Contains(appPattern, StringComparison.CurrentCultureIgnoreCase)); if (startupKey is null) return; startupKey = Path.Combine(startupKey, subKeyName); Registry.SetValue(hive, startupKey, valueName, enabled ? 0x2 : 0x1); } } ================================================ FILE: LenovoLegionToolkit.Lib/SoftwareDisabler/LegionZoneDisabler.cs ================================================ using System.Collections.Generic; namespace LenovoLegionToolkit.Lib.SoftwareDisabler; public class LegionZoneDisabler : AbstractSoftwareDisabler { protected override IEnumerable ScheduledTasksPaths => []; protected override IEnumerable ServiceNames => ["LZService"]; protected override IEnumerable ProcessNames => ["LegionZone", "LZTray"]; } ================================================ FILE: LenovoLegionToolkit.Lib/SoftwareDisabler/VantageDisabler.cs ================================================ using System.Collections.Generic; namespace LenovoLegionToolkit.Lib.SoftwareDisabler; public class VantageDisabler : AbstractSoftwareDisabler { protected override IEnumerable ScheduledTasksPaths => [ "Lenovo\\BatteryGauge", "Lenovo\\ImController", "Lenovo\\ImController\\Plugins", "Lenovo\\ImController\\TimeBasedEvents", "Lenovo\\UDC", "Lenovo\\Vantage", "Lenovo\\Vantage\\Schedule" ]; protected override IEnumerable ServiceNames => [ "ImControllerService", "LenovoVantageService" ]; protected override IEnumerable ProcessNames => [ "LenovoVantage", "Lenovo.Modern.ImController" ]; } ================================================ FILE: LenovoLegionToolkit.Lib/Structs.cs ================================================ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Drawing; using System.IO; using System.Linq; using System.Text; using LenovoLegionToolkit.Lib.Extensions; using Newtonsoft.Json; using Octokit; namespace LenovoLegionToolkit.Lib; public readonly struct BatteryInformation( bool isCharging, int batteryPercentage, int batteryLifeRemaining, int fullBatteryLifeRemaining, int dischargeRate, int minDischargeRate, int maxDischargeRate, int estimateChargeRemaining, int designCapacity, int fullChargeCapacity, int cycleCount, bool isLowBattery, double? batteryTemperatureC, DateTime? manufactureDate, DateTime? firstUseDate) { public bool IsCharging { get; } = isCharging; public int BatteryPercentage { get; } = batteryPercentage; public int BatteryLifeRemaining { get; } = batteryLifeRemaining; public int FullBatteryLifeRemaining { get; init; } = fullBatteryLifeRemaining; public int DischargeRate { get; } = dischargeRate; public int MinDischargeRate { get; } = minDischargeRate; public int MaxDischargeRate { get; } = maxDischargeRate; public int EstimateChargeRemaining { get; } = estimateChargeRemaining; public int DesignCapacity { get; } = designCapacity; public int FullChargeCapacity { get; } = fullChargeCapacity; public int CycleCount { get; } = cycleCount; public bool IsLowBattery { get; } = isLowBattery; public double? BatteryTemperatureC { get; } = batteryTemperatureC; public DateTime? ManufactureDate { get; } = manufactureDate; public DateTime? FirstUseDate { get; } = firstUseDate; public double BatteryHealth => DesignCapacity > 0 ? Math.Round((double)FullChargeCapacity / DesignCapacity * 100.0, 2, MidpointRounding.AwayFromZero) : 0.0; } public readonly struct BiosVersion(string prefix, int? version) { public string Prefix { get; } = prefix; public int? Version { get; } = version; public bool IsHigherOrEqualThan(BiosVersion other) { if (!Prefix.Equals(other.Prefix, StringComparison.InvariantCultureIgnoreCase)) return false; if (Version is null || other.Version is null) return true; return Version >= other.Version; } public bool IsLowerThan(BiosVersion other) { if (!Prefix.Equals(other.Prefix, StringComparison.InvariantCultureIgnoreCase)) return false; if (Version is null || other.Version is null) return true; return Version < other.Version; } public override string ToString() => $"{nameof(Prefix)}: {Prefix}, {nameof(Version)}: {Version}"; } public readonly struct Brightness(byte value) { public byte Value { get; } = value; } public readonly struct DiscreteCapability(CapabilityID id, int value) { public CapabilityID Id { get; } = id; public int Value { get; } = value; } public readonly struct DisplayAdvancedColorInfo(bool advancedColorSupported, bool advancedColorEnabled, bool wideColorEnforced, bool advancedColorForceDisabled) { public bool AdvancedColorSupported { get; } = advancedColorSupported; public bool AdvancedColorEnabled { get; } = advancedColorEnabled; public bool WideColorEnforced { get; } = wideColorEnforced; public bool AdvancedColorForceDisabled { get; } = advancedColorForceDisabled; } public struct Device( string name, string description, string busReportedDeviceDescription, string deviceInstanceId, Guid classGuid, string className, bool isRemovable, bool isDisconnected) { public string Name { get; } = name; public string Description { get; } = description; public string BusReportedDeviceDescription { get; } = busReportedDeviceDescription; public string DeviceInstanceId { get; } = deviceInstanceId; public Guid ClassGuid { get; } = classGuid; public string ClassName { get; } = className; public bool IsRemovable { get; } = isRemovable; public bool IsDisconnected { get; } = isDisconnected; private string? _index; public string Index { get { _index ??= new StringBuilder() .Append(ClassName) .Append(ClassGuid) .Append(BusReportedDeviceDescription) .Append(Description) .Append(Name) .Append(DeviceInstanceId) .ToString(); return _index; } } } public readonly struct DriverInfo(string deviceId, string hardwareId, Version? version, DateTime? date) { public string DeviceId { get; } = deviceId; public string HardwareId { get; } = hardwareId; public Version? Version { get; } = version; public DateTime? Date { get; } = date; } public readonly struct FanTableData(FanTableType type, byte fanId, byte sensorId, ushort[] fanSpeeds, ushort[] temps) { public FanTableType Type { get; } = type; public byte FanId { get; } = fanId; public byte SensorId { get; } = sensorId; public ushort[] FanSpeeds { get; } = fanSpeeds; public ushort[] Temps { get; } = temps; public override string ToString() => $"{nameof(Type)}: {Type}," + $" {nameof(FanId)}: {FanId}," + $" {nameof(SensorId)}: {SensorId}," + $" {nameof(FanSpeeds)}: [{string.Join(", ", FanSpeeds)}]," + $" {nameof(Temps)}: [{string.Join(", ", Temps)}]"; } public readonly struct FanTable { // ReSharper disable AutoPropertyCanBeMadeGetOnly.Global // ReSharper disable MemberCanBePrivate.Global // ReSharper disable IdentifierTypo // ReSharper disable InconsistentNaming public byte FSTM { get; init; } public byte FSID { get; init; } public uint FSTL { get; init; } public ushort FSS0 { get; init; } public ushort FSS1 { get; init; } public ushort FSS2 { get; init; } public ushort FSS3 { get; init; } public ushort FSS4 { get; init; } public ushort FSS5 { get; init; } public ushort FSS6 { get; init; } public ushort FSS7 { get; init; } public ushort FSS8 { get; init; } public ushort FSS9 { get; init; } // ReSharper restore AutoPropertyCanBeMadeGetOnly.Global // ReSharper restore MemberCanBePrivate.Global // ReSharper restore IdentifierTypo // ReSharper restore InconsistentNaming public FanTable(ushort[] fanTable) { if (fanTable.Length != 10) // ReSharper disable once LocalizableElement throw new ArgumentException("Fan table length must be 10", nameof(fanTable)); FSTM = 1; FSID = 0; FSTL = 0; FSS0 = fanTable[0]; FSS1 = fanTable[1]; FSS2 = fanTable[2]; FSS3 = fanTable[3]; FSS4 = fanTable[4]; FSS5 = fanTable[5]; FSS6 = fanTable[6]; FSS7 = fanTable[7]; FSS8 = fanTable[8]; FSS9 = fanTable[9]; } public ushort[] GetTable() => [FSS0, FSS1, FSS2, FSS3, FSS4, FSS5, FSS6, FSS7, FSS8, FSS9]; public byte[] GetBytes() { using var ms = new MemoryStream(new byte[64]); ms.WriteByte(FSTM); ms.WriteByte(FSID); ms.Write(BitConverter.GetBytes(FSTL)); ms.Write(BitConverter.GetBytes(FSS0)); ms.Write(BitConverter.GetBytes(FSS1)); ms.Write(BitConverter.GetBytes(FSS2)); ms.Write(BitConverter.GetBytes(FSS3)); ms.Write(BitConverter.GetBytes(FSS4)); ms.Write(BitConverter.GetBytes(FSS5)); ms.Write(BitConverter.GetBytes(FSS6)); ms.Write(BitConverter.GetBytes(FSS7)); ms.Write(BitConverter.GetBytes(FSS8)); ms.Write(BitConverter.GetBytes(FSS9)); return ms.ToArray(); } public override string ToString() => $"{nameof(FSTM)}: {FSTM}," + $" {nameof(FSID)}: {FSID}," + $" {nameof(FSTL)}: {FSTL}," + $" {nameof(FSS0)}: {FSS0}," + $" {nameof(FSS1)}: {FSS1}," + $" {nameof(FSS2)}: {FSS2}," + $" {nameof(FSS3)}: {FSS3}," + $" {nameof(FSS4)}: {FSS4}," + $" {nameof(FSS5)}: {FSS5}," + $" {nameof(FSS6)}: {FSS6}," + $" {nameof(FSS7)}: {FSS7}," + $" {nameof(FSS8)}: {FSS8}," + $" {nameof(FSS9)}: {FSS9}"; } public readonly struct FanTableInfo(FanTableData[] data, FanTable table) { public FanTableData[] Data { get; } = data; public FanTable Table { get; } = table; public override string ToString() => $"{nameof(Data)}: [{string.Join(", ", Data)}]," + $" {nameof(Table)}: {Table}"; } public readonly struct GPUOverclockInfo(int coreDeltaMhz, int memoryDeltaMhz) { public static readonly GPUOverclockInfo Zero = new(); public int CoreDeltaMhz { get; } = coreDeltaMhz; public int MemoryDeltaMhz { get; } = memoryDeltaMhz; #region Equality public override bool Equals(object? obj) => obj is GPUOverclockInfo other && CoreDeltaMhz == other.CoreDeltaMhz && MemoryDeltaMhz == other.MemoryDeltaMhz; public override int GetHashCode() => HashCode.Combine(CoreDeltaMhz, MemoryDeltaMhz); public static bool operator ==(GPUOverclockInfo left, GPUOverclockInfo right) => left.Equals(right); public static bool operator !=(GPUOverclockInfo left, GPUOverclockInfo right) => !left.Equals(right); #endregion public override string ToString() => $"{nameof(CoreDeltaMhz)}: {CoreDeltaMhz}, {nameof(MemoryDeltaMhz)}: {MemoryDeltaMhz}"; } public readonly struct GodModeDefaults { public int? CPULongTermPowerLimit { get; init; } public int? CPUShortTermPowerLimit { get; init; } public int? CPUPeakPowerLimit { get; init; } public int? CPUCrossLoadingPowerLimit { get; init; } public int? CPUPL1Tau { get; init; } public int? APUsPPTPowerLimit { get; init; } public int? CPUTemperatureLimit { get; init; } public int? GPUPowerBoost { get; init; } public int? GPUConfigurableTGP { get; init; } public int? GPUTemperatureLimit { get; init; } public int? GPUTotalProcessingPowerTargetOnAcOffsetFromBaseline { get; init; } public int? GPUToCPUDynamicBoost { get; init; } public FanTable? FanTable { get; init; } public bool? FanFullSpeed { get; init; } public override string ToString() => $"{nameof(CPULongTermPowerLimit)}: {CPULongTermPowerLimit}," + $" {nameof(CPUShortTermPowerLimit)}: {CPUShortTermPowerLimit}," + $" {nameof(CPUPeakPowerLimit)}: {CPUPeakPowerLimit}," + $" {nameof(CPUCrossLoadingPowerLimit)}: {CPUCrossLoadingPowerLimit}," + $" {nameof(CPUPL1Tau)}: {CPUPL1Tau}," + $" {nameof(APUsPPTPowerLimit)}: {APUsPPTPowerLimit}," + $" {nameof(CPUTemperatureLimit)}: {CPUTemperatureLimit}," + $" {nameof(GPUPowerBoost)}: {GPUPowerBoost}," + $" {nameof(GPUConfigurableTGP)}: {GPUConfigurableTGP}," + $" {nameof(GPUTemperatureLimit)}: {GPUTemperatureLimit}," + $" {nameof(GPUTotalProcessingPowerTargetOnAcOffsetFromBaseline)}: {GPUTotalProcessingPowerTargetOnAcOffsetFromBaseline}," + $" {nameof(GPUToCPUDynamicBoost)}: {GPUToCPUDynamicBoost}," + $" {nameof(FanTable)}: {FanTable}," + $" {nameof(FanFullSpeed)}: {FanFullSpeed}"; } public readonly struct GodModeState { public Guid ActivePresetId { get; init; } public ReadOnlyDictionary Presets { get; init; } } public readonly struct GodModePreset { public string Name { get; init; } public StepperValue? CPULongTermPowerLimit { get; init; } public StepperValue? CPUShortTermPowerLimit { get; init; } public StepperValue? CPUPeakPowerLimit { get; init; } public StepperValue? CPUCrossLoadingPowerLimit { get; init; } public StepperValue? CPUPL1Tau { get; init; } public StepperValue? APUsPPTPowerLimit { get; init; } public StepperValue? CPUTemperatureLimit { get; init; } public StepperValue? GPUPowerBoost { get; init; } public StepperValue? GPUConfigurableTGP { get; init; } public StepperValue? GPUTemperatureLimit { get; init; } public StepperValue? GPUTotalProcessingPowerTargetOnAcOffsetFromBaseline { get; init; } public StepperValue? GPUToCPUDynamicBoost { get; init; } public FanTableInfo? FanTableInfo { get; init; } public bool? FanFullSpeed { get; init; } public int? MinValueOffset { get; init; } public int? MaxValueOffset { get; init; } public override string ToString() => $"{nameof(Name)}: {Name}," + $" {nameof(CPULongTermPowerLimit)}: {CPULongTermPowerLimit}," + $" {nameof(CPUShortTermPowerLimit)}: {CPUShortTermPowerLimit}," + $" {nameof(CPUPeakPowerLimit)}: {CPUPeakPowerLimit}," + $" {nameof(CPUCrossLoadingPowerLimit)}: {CPUCrossLoadingPowerLimit}," + $" {nameof(CPUPL1Tau)}: {CPUPL1Tau}," + $" {nameof(APUsPPTPowerLimit)}: {APUsPPTPowerLimit}," + $" {nameof(CPUTemperatureLimit)}: {CPUTemperatureLimit}," + $" {nameof(GPUPowerBoost)}: {GPUPowerBoost}," + $" {nameof(GPUConfigurableTGP)}: {GPUConfigurableTGP}," + $" {nameof(GPUTemperatureLimit)}: {GPUTemperatureLimit}," + $" {nameof(GPUTotalProcessingPowerTargetOnAcOffsetFromBaseline)}: {GPUTotalProcessingPowerTargetOnAcOffsetFromBaseline}," + $" {nameof(GPUToCPUDynamicBoost)}: {GPUToCPUDynamicBoost}," + $" {nameof(FanTableInfo)}: {FanTableInfo}," + $" {nameof(FanFullSpeed)}: {FanFullSpeed}," + $" {nameof(MinValueOffset)}: {MinValueOffset}," + $" {nameof(MaxValueOffset)}: {MaxValueOffset}"; } public readonly struct GPUStatus(GPUState state, string? performanceState, List processes) { public GPUState State { get; } = state; public string? PerformanceState { get; } = performanceState; public List Processes { get; } = processes; public int ProcessCount => Processes.Count; } public readonly struct HardwareId(string vendor, string device) { public static readonly HardwareId Empty = new(); public string Vendor { get; } = vendor; public string Device { get; } = device; #region Equality public override bool Equals(object? obj) { if (obj is not HardwareId other) return false; if (!Vendor.Equals(other.Vendor, StringComparison.InvariantCultureIgnoreCase)) return false; if (!Device.Equals(other.Device, StringComparison.InvariantCultureIgnoreCase)) return false; return true; } public override int GetHashCode() => HashCode.Combine(Vendor, Device); public static bool operator ==(HardwareId left, HardwareId right) => left.Equals(right); public static bool operator !=(HardwareId left, HardwareId right) => !left.Equals(right); #endregion } public readonly struct MachineInformation { public readonly struct FeatureData(FeatureData.SourceType sourceType, IEnumerable capabilities) { public static readonly FeatureData Unknown = new(SourceType.Unknown); public enum SourceType { Unknown, Flags, CapabilityData } private readonly HashSet _capabilities = [.. capabilities]; public SourceType Source { get; } = sourceType; public IEnumerable All => _capabilities.Order().AsEnumerable(); public FeatureData(SourceType sourceType) : this(sourceType, []) { } public bool this[CapabilityID key] { get => _capabilities.Contains(key); init { if (value) _capabilities.Add(key); else _capabilities.Remove(key); } } } public readonly struct PropertyData { public bool SupportsGodMode => SupportsGodModeV1 || SupportsGodModeV2; public (bool status, bool connectivity) SupportsAlwaysOnAc { get; init; } public bool SupportsGodModeV1 { get; init; } public bool SupportsGodModeV2 { get; init; } public bool SupportsGSync { get; init; } public bool SupportsIGPUMode { get; init; } public bool SupportsAIMode { get; init; } public bool SupportBootLogoChange { get; init; } public bool HasQuietToPerformanceModeSwitchingBug { get; init; } public bool HasGodModeToOtherModeSwitchingBug { get; init; } public bool IsExcludedFromLenovoLighting { get; init; } public bool IsExcludedFromPanelLogoLenovoLighting { get; init; } public bool HasAlternativeFullSpectrumLayout { get; init; } } public string Vendor { get; init; } public string MachineType { get; init; } public string Model { get; init; } public string SerialNumber { get; init; } public BiosVersion? BiosVersion { get; init; } public string? BiosVersionRaw { get; init; } public PowerModeState[] SupportedPowerModes { get; init; } public int SmartFanVersion { get; init; } public int LegionZoneVersion { get; init; } public FeatureData Features { get; init; } public PropertyData Properties { get; init; } } public struct Package { public string Id { get; init; } public string Title { get; init; } public string Description { get; init; } public string Version { get; init; } public string Category { get; init; } public string FileName { get; init; } public string FileSize { get; init; } public string? FileCrc { get; init; } public DateTime ReleaseDate { get; init; } public string? Readme { get; init; } public string FileLocation { get; init; } public bool IsUpdate { get; init; } public RebootType Reboot { get; init; } private string? _index; public string Index { get { _index ??= new StringBuilder() .Append(Title) .Append(Description) .Append(Version) .Append(Category) .Append(FileName) .ToString(); return _index; } } } public readonly struct WindowsPowerPlan(Guid guid, string name, bool isActive) { public Guid Guid { get; } = guid; public string Name { get; } = name; public bool IsActive { get; } = isActive; public override string ToString() => $"{nameof(Guid)}: {Guid}, {nameof(Name)}: {Name}, {nameof(IsActive)}: {IsActive}"; #region Equality public override bool Equals(object? obj) => obj is WindowsPowerPlan other && Guid.Equals(other.Guid); public override int GetHashCode() => Guid.GetHashCode(); public static bool operator ==(WindowsPowerPlan left, WindowsPowerPlan right) => left.Equals(right); public static bool operator !=(WindowsPowerPlan left, WindowsPowerPlan right) => !left.Equals(right); #endregion } [method: JsonConstructor] public readonly struct ProcessInfo(string name, string? executablePath) : IComparable { public static ProcessInfo FromPath(string path) => new(Path.GetFileNameWithoutExtension(path), path); public string Name { get; } = name; public string? ExecutablePath { get; } = executablePath; public override string ToString() => $"{nameof(Name)}: {Name}, {nameof(ExecutablePath)}: {ExecutablePath}"; #region Equality public int CompareTo(object? obj) { var other = obj is null ? default : (ProcessInfo)obj; var result = string.Compare(Name, other.Name, StringComparison.InvariantCultureIgnoreCase); return result != 0 ? result : string.Compare(ExecutablePath, other.ExecutablePath, StringComparison.InvariantCultureIgnoreCase); } public override bool Equals(object? obj) => obj is ProcessInfo info && Name == info.Name && ExecutablePath == info.ExecutablePath; public override int GetHashCode() => HashCode.Combine(Name, ExecutablePath); public static bool operator ==(ProcessInfo left, ProcessInfo right) => left.Equals(right); public static bool operator !=(ProcessInfo left, ProcessInfo right) => !(left == right); public static bool operator <(ProcessInfo left, ProcessInfo right) => left.CompareTo(right) < 0; public static bool operator <=(ProcessInfo left, ProcessInfo right) => left.CompareTo(right) <= 0; public static bool operator >(ProcessInfo left, ProcessInfo right) => left.CompareTo(right) > 0; public static bool operator >=(ProcessInfo left, ProcessInfo right) => left.CompareTo(right) >= 0; #endregion } public readonly struct RangeCapability(CapabilityID id, int defaultValue, int min, int max, int step) { public CapabilityID Id { get; } = id; public int DefaultValue { get; } = defaultValue; public int Min { get; } = min; public int Max { get; } = max; public int Step { get; } = step; } [method: JsonConstructor] public readonly struct RGBColor(byte r, byte g, byte b) { public static readonly RGBColor Green = new(142, 255, 0); public static readonly RGBColor Pink = new(186, 0, 255); public static readonly RGBColor Purple = new(101, 0, 255); public static readonly RGBColor Red = new(255, 0, 0); public static readonly RGBColor Teal = new(0, 212, 255); public static readonly RGBColor White = new(255, 255, 255); public byte R { get; } = r; public byte G { get; } = g; public byte B { get; } = b; #region Equality public override bool Equals(object? obj) { return obj is RGBColor color && R == color.R && G == color.G && B == color.B; } public override int GetHashCode() => (R, G, B).GetHashCode(); public static bool operator ==(RGBColor left, RGBColor right) => left.Equals(right); public static bool operator !=(RGBColor left, RGBColor right) => !left.Equals(right); #endregion public override string ToString() => $"{nameof(R)}: {R}, {nameof(G)}: {G}, {nameof(B)}: {B}"; } [method: JsonConstructor] public readonly struct RGBKeyboardBacklightBacklightPresetDescription( RGBKeyboardBacklightEffect effect, RGBKeyboardBacklightSpeed speed, RGBKeyboardBacklightBrightness brightness, RGBColor zone1, RGBColor zone2, RGBColor zone3, RGBColor zone4) { public static readonly RGBKeyboardBacklightBacklightPresetDescription Default = new(RGBKeyboardBacklightEffect.Static, RGBKeyboardBacklightSpeed.Slowest, RGBKeyboardBacklightBrightness.High, RGBColor.White, RGBColor.White, RGBColor.White, RGBColor.White); public RGBKeyboardBacklightEffect Effect { get; } = effect; public RGBKeyboardBacklightSpeed Speed { get; } = speed; public RGBKeyboardBacklightBrightness Brightness { get; } = brightness; public RGBColor Zone1 { get; } = zone1; public RGBColor Zone2 { get; } = zone2; public RGBColor Zone3 { get; } = zone3; public RGBColor Zone4 { get; } = zone4; #region Equality public override bool Equals(object? obj) { return obj is RGBKeyboardBacklightBacklightPresetDescription settings && Effect == settings.Effect && Speed == settings.Speed && Brightness == settings.Brightness && Zone1.Equals(settings.Zone1) && Zone2.Equals(settings.Zone2) && Zone3.Equals(settings.Zone3) && Zone4.Equals(settings.Zone4); } public override int GetHashCode() => HashCode.Combine(Effect, Speed, Brightness, Zone1, Zone2, Zone3, Zone4); public static bool operator ==(RGBKeyboardBacklightBacklightPresetDescription left, RGBKeyboardBacklightBacklightPresetDescription right) => left.Equals(right); public static bool operator !=(RGBKeyboardBacklightBacklightPresetDescription left, RGBKeyboardBacklightBacklightPresetDescription right) => !(left == right); #endregion public override string ToString() => $"{nameof(Effect)}: {Effect}," + $" {nameof(Speed)}: {Speed}," + $" {nameof(Brightness)}: {Brightness}," + $" {nameof(Zone1)}: {Zone1}," + $" {nameof(Zone2)}: {Zone2}," + $" {nameof(Zone3)}: {Zone3}," + $" {nameof(Zone4)}: {Zone4}"; } [method: JsonConstructor] public readonly struct RGBKeyboardBacklightState( RGBKeyboardBacklightPreset selectedPreset, Dictionary presets) { public RGBKeyboardBacklightPreset SelectedPreset { get; } = selectedPreset; public Dictionary Presets { get; } = presets; } public readonly struct SensorData( int utilization, int maxUtilization, int coreClock, int maxCoreClock, int memoryClock, int maxMemoryClock, int temperature, int maxTemperature, int fanSpeed, int maxFanSpeed) { public static readonly SensorData Empty = new(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1); public int Utilization { get; } = utilization; public int MaxUtilization { get; } = maxUtilization; public int CoreClock { get; } = coreClock; public int MaxCoreClock { get; } = maxCoreClock; public int MemoryClock { get; } = memoryClock; public int MaxMemoryClock { get; } = maxMemoryClock; public int Temperature { get; } = temperature; public int MaxTemperature { get; } = maxTemperature; public int FanSpeed { get; } = fanSpeed; public int MaxFanSpeed { get; } = maxFanSpeed; public override string ToString() => $"{nameof(Utilization)}: {Utilization}," + $" {nameof(MaxUtilization)}: {MaxUtilization}," + $" {nameof(CoreClock)}: {CoreClock}," + $" {nameof(MaxCoreClock)}: {MaxCoreClock}," + $" {nameof(MemoryClock)}: {MemoryClock}," + $" {nameof(MaxMemoryClock)}: {MaxMemoryClock}," + $" {nameof(Temperature)}: {Temperature}," + $" {nameof(MaxTemperature)}: {MaxTemperature}," + $" {nameof(FanSpeed)}: {FanSpeed}," + $" {nameof(MaxFanSpeed)}: {MaxFanSpeed}"; } public readonly struct SensorsData(SensorData cpu, SensorData gpu) { public static readonly SensorsData Empty = new(SensorData.Empty, SensorData.Empty); public SensorData CPU { get; } = cpu; public SensorData GPU { get; } = gpu; public override string ToString() => $"{nameof(CPU)}: {CPU}, {nameof(GPU)}: {GPU}"; } [method: JsonConstructor] public readonly struct DpiScale(int scale) : IDisplayName, IEquatable { public int Scale { get; } = scale; [JsonIgnore] public string DisplayName => $"{Scale}%"; #region Equality public override bool Equals(object? obj) => obj is DpiScale rate && Equals(rate); public bool Equals(DpiScale other) => Scale == other.Scale; public override int GetHashCode() => HashCode.Combine(Scale); public static bool operator ==(DpiScale left, DpiScale right) => left.Equals(right); public static bool operator !=(DpiScale left, DpiScale right) => !(left == right); #endregion } [method: JsonConstructor] public readonly struct RefreshRate(int frequency) : IDisplayName, IEquatable { public int Frequency { get; } = frequency; [JsonIgnore] public string DisplayName => $"{Frequency} Hz"; public override string ToString() => $"{Frequency}Hz"; #region Equality public override bool Equals(object? obj) => obj is RefreshRate rate && Equals(rate); public bool Equals(RefreshRate other) => Frequency == other.Frequency; public override int GetHashCode() => HashCode.Combine(Frequency); public static bool operator ==(RefreshRate left, RefreshRate right) => left.Equals(right); public static bool operator !=(RefreshRate left, RefreshRate right) => !(left == right); #endregion } [method: JsonConstructor] public readonly struct Resolution(int width, int height) : IDisplayName, IEquatable, IComparable { [JsonProperty] public int Width { get; } = width; [JsonProperty] public int Height { get; } = height; [JsonIgnore] public string DisplayName => $"{Width} × {Height}"; public Resolution(Size size) : this(size.Width, size.Height) { } public override string ToString() => $"{Width}x{Height}"; public int CompareTo(Resolution other) { var widthComparison = Width.CompareTo(other.Width); return widthComparison != 0 ? widthComparison : Height.CompareTo(other.Height); } #region Conversion public static explicit operator Resolution(Size value) => new(value); public static implicit operator Size(Resolution data) => new(data.Width, data.Height); #endregion #region Equality public override bool Equals(object? obj) => obj is Resolution other && Equals(other); public bool Equals(Resolution other) => Width == other.Width && Height == other.Height; public override int GetHashCode() => HashCode.Combine(Width, Height); public static bool operator ==(Resolution left, Resolution right) => left.Equals(right); public static bool operator !=(Resolution left, Resolution right) => !(left == right); #endregion } public readonly struct SpectrumKeyboardBacklightEffect( SpectrumKeyboardBacklightEffectType type, SpectrumKeyboardBacklightSpeed speed, SpectrumKeyboardBacklightDirection direction, SpectrumKeyboardBacklightClockwiseDirection clockwiseDirection, RGBColor[] colors, ushort[] keys) { public SpectrumKeyboardBacklightEffectType Type { get; } = type; public SpectrumKeyboardBacklightSpeed Speed { get; } = speed; public SpectrumKeyboardBacklightDirection Direction { get; } = direction; public SpectrumKeyboardBacklightClockwiseDirection ClockwiseDirection { get; } = clockwiseDirection; public RGBColor[] Colors { get; } = colors; public ushort[] Keys { get; } = type.IsAllLightsEffect() ? [] : keys; } public readonly struct StepperValue(int value, int min, int max, int step, int[] steps, int? defaultValue) { public int Value { get; } = value; public int Min { get; } = min; public int Max { get; } = max; public int Step { get; } = step; public int[] Steps { get; } = steps; public int? DefaultValue { get; } = defaultValue; public StepperValue WithValue(int value) => new(value, Min, Max, Step, Steps, DefaultValue); public override string ToString() => $"{nameof(Value)}: {Value}," + $" {nameof(Min)}: {Min}," + $" {nameof(Max)}: {Max}," + $" {nameof(Step)}: {Step}," + $" {nameof(Steps)}: [{string.Join(", ", Steps)}]," + $" {nameof(DefaultValue)} : {DefaultValue}"; } public readonly struct Time(int hour, int minute) { public int Hour { get; } = hour; public int Minute { get; } = minute; #region Equality public override bool Equals(object? obj) => obj is Time time && Hour == time.Hour && Minute == time.Minute; public override int GetHashCode() => HashCode.Combine(Hour, Minute); public static bool operator ==(Time left, Time right) => left.Equals(right); public static bool operator !=(Time left, Time right) => !(left == right); #endregion } public readonly struct Update(Release release) { public Version Version { get; } = Version.Parse(release.TagName); public string Title { get; } = release.Name; public string Description { get; } = release.Body; public DateTimeOffset Date { get; } = release.PublishedAt ?? release.CreatedAt; public string? Url { get; } = release.Assets .Where(ra => ra.Name.EndsWith("setup.exe", StringComparison.InvariantCultureIgnoreCase)) .Select(ra => ra.BrowserDownloadUrl) .FirstOrDefault(); #region Equality public override bool Equals(object? obj) => obj is Update other && Version.Equals(other.Version); public override int GetHashCode() => Version.GetHashCode(); public static bool operator ==(Update left, Update right) => left.Equals(right); public static bool operator !=(Update left, Update right) => !left.Equals(right); #endregion } public readonly struct WarrantyInfo(DateTime? start, DateTime? end, Uri? link) { public DateTime? Start { get; } = start; public DateTime? End { get; } = end; public Uri? Link { get; } = link; } public readonly struct WindowSize(double width, double height) { public double Width { get; } = width; public double Height { get; } = height; } ================================================ FILE: LenovoLegionToolkit.Lib/System/AirplaneMode.cs ================================================ using System.Diagnostics; namespace LenovoLegionToolkit.Lib.System; public static class AirplaneMode { public static void Open() { Process.Start(new ProcessStartInfo { FileName = "cmd", Arguments = "/c \"start ms-settings:network-airplanemode\"", UseShellExecute = true, CreateNoWindow = true, WindowStyle = ProcessWindowStyle.Hidden, }); } } ================================================ FILE: LenovoLegionToolkit.Lib/System/Autorun.cs ================================================ using System; using System.Diagnostics; using System.Linq; using System.Security.Principal; using LenovoLegionToolkit.Lib.Utils; using Microsoft.Win32.TaskScheduler; namespace LenovoLegionToolkit.Lib.System; public static class Autorun { private const string TASK_NAME = "LenovoLegionToolkit_Autorun_6efcc882-924c-4cbc-8fec-f45c25696f98"; public static AutorunState State { get { var task = TaskService.Instance.GetTask(TASK_NAME); if (task is null) return AutorunState.Disabled; var delayed = task.Definition.Triggers.OfType().FirstOrDefault()?.Delay > TimeSpan.Zero; return delayed ? AutorunState.EnabledDelayed : AutorunState.Enabled; } } public static void Validate() { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Validating autorun..."); var currentTask = TaskService.Instance.GetTask(TASK_NAME); if (currentTask is null) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Autorun is not enabled."); return; } var mainModule = Process.GetCurrentProcess().MainModule; if (mainModule is null) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Main module is null."); return; } var fileVersion = mainModule.FileVersionInfo.FileVersion; if (fileVersion is null) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"File version is null."); return; } if (currentTask.Definition.Data == fileVersion) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Autorun settings seems to be fine."); return; } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Enabling autorun again..."); var delayed = currentTask.Definition.Triggers.OfType().FirstOrDefault()?.Delay > TimeSpan.Zero; Enable(delayed); } public static void Set(AutorunState state) { if (state == AutorunState.Disabled) Disable(); else Enable(state == AutorunState.EnabledDelayed); } private static void Enable(bool delayed) { Disable(); var mainModule = Process.GetCurrentProcess().MainModule ?? throw new InvalidOperationException("Main Module cannot be null"); var filename = mainModule.FileName ?? throw new InvalidOperationException("Current process file name cannot be null"); var fileVersion = mainModule.FileVersionInfo.FileVersion ?? throw new InvalidOperationException("Current process file version cannot be null"); var currentUser = WindowsIdentity.GetCurrent().Name; var ts = TaskService.Instance; var td = ts.NewTask(); td.Data = fileVersion; td.Principal.UserId = currentUser; td.Principal.RunLevel = TaskRunLevel.Highest; td.Triggers.Add(new LogonTrigger { UserId = currentUser, Delay = new TimeSpan(0, 0, delayed ? 30 : 0) }); td.Actions.Add($"\"{filename}\"", "--minimized"); td.Settings.DisallowStartIfOnBatteries = false; td.Settings.StopIfGoingOnBatteries = false; td.Settings.ExecutionTimeLimit = TimeSpan.Zero; ts.RootFolder.RegisterTaskDefinition(TASK_NAME, td); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Autorun enabled"); } private static void Disable() { try { TaskService.Instance.RootFolder.DeleteTask(TASK_NAME); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Autorun disabled"); } catch { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Autorun was not enabled"); } } } ================================================ FILE: LenovoLegionToolkit.Lib/System/Battery.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics.Eventing.Reader; using System.Linq; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Settings; using LenovoLegionToolkit.Lib.Utils; using Windows.Win32; using Windows.Win32.System.Power; namespace LenovoLegionToolkit.Lib.System; public static class Battery { private static readonly ApplicationSettings Settings = IoCContainer.Resolve(); private static int MinDischargeRate { get; set; } = int.MaxValue; private static int MaxDischargeRate { get; set; } public static void SetMinMaxDischargeRate(BATTERY_STATUS? status = null) { if (!status.HasValue) { var batteryTag = GetBatteryTag(); status = GetBatteryStatus(batteryTag); } if (status.Value.Rate == 0 || (status.Value.Rate > 0 && (MinDischargeRate < 0 || MaxDischargeRate < 0)) || (status.Value.Rate < 0 && (MinDischargeRate > 0 || MaxDischargeRate > 0))) { MinDischargeRate = int.MaxValue; MaxDischargeRate = 0; } if (status.Value.Rate != 0) { if (Math.Abs(status.Value.Rate) < Math.Abs(MinDischargeRate)) MinDischargeRate = status.Value.Rate; if (Math.Abs(status.Value.Rate) > Math.Abs(MaxDischargeRate)) MaxDischargeRate = status.Value.Rate; } } public static BatteryInformation GetBatteryInformation() { var powerStatus = GetSystemPowerStatus(); var batteryTag = GetBatteryTag(); var information = GetBatteryInformation(batteryTag); var status = GetBatteryStatus(batteryTag); double? temperatureC = null; DateTime? manufactureDate = null; DateTime? firstUseDate = null; SetMinMaxDischargeRate(status); try { var lenovoBatteryInformation = FindLenovoBatteryInformation(); if (lenovoBatteryInformation.HasValue) { temperatureC = DecodeTemperatureC(lenovoBatteryInformation.Value.Temperature); manufactureDate = DecodeDateTime(lenovoBatteryInformation.Value.ManufactureDate); firstUseDate = DecodeDateTime(lenovoBatteryInformation.Value.FirstUseDate); } } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to get temperature of battery.", ex); } return new(powerStatus.ACLineStatus == 1, powerStatus.BatteryLifePercent, (int)powerStatus.BatteryLifeTime, (int)powerStatus.BatteryFullLifeTime, status.Rate, (status.Rate == 0) ? 0 : MinDischargeRate, MaxDischargeRate, (int)status.Capacity, (int)information.DesignedCapacity, (int)information.FullChargedCapacity, (int)information.CycleCount, powerStatus.ACLineStatus == 0 && information.DefaultAlert2 >= status.Capacity, temperatureC, manufactureDate, firstUseDate); } public static double? GetBatteryTemperatureC() { try { var lenovoBatteryInformation = FindLenovoBatteryInformation(); return lenovoBatteryInformation.HasValue ? DecodeTemperatureC(lenovoBatteryInformation.Value.Temperature) : null; } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to get temperature of battery.", ex); return null; } } public static DateTime? GetOnBatterySince() { try { var resetOnReboot = Settings.Store.ResetBatteryOnSinceTimerOnReboot; var lastRebootTime = DateTime.Now - TimeSpan.FromMilliseconds(Environment.TickCount); var logs = new List<(DateTime Date, bool IsACOnline)>(); var query = new EventLogQuery("System", PathType.LogName, "*[System[EventID=105]]"); using var logReader = new EventLogReader(query); using var propertySelector = new EventLogPropertySelector(["Event/EventData/Data[@Name='AcOnline']"]); while (logReader.ReadEvent() is EventLogRecord record) { var date = record.TimeCreated; var isAcOnline = record.GetPropertyValues(propertySelector)[0] as bool?; if (date is null || isAcOnline is null) continue; if (resetOnReboot && date < lastRebootTime) continue; logs.Add((date.Value, isAcOnline.Value)); } if (logs.Count < 1) return null; logs.Reverse(); var (dateTime, _) = logs .TakeWhile(log => log.IsACOnline != true) .LastOrDefault(); return dateTime; } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to get event.", ex); } return null; } private static SYSTEM_POWER_STATUS GetSystemPowerStatus() { var result = PInvoke.GetSystemPowerStatus(out var sps); if (!result) PInvokeExtensions.ThrowIfWin32Error("GetSystemPowerStatus"); return sps; } private static uint GetBatteryTag() { var result = PInvokeExtensions.DeviceIoControl(Devices.GetBattery(), PInvoke.IOCTL_BATTERY_QUERY_TAG, 0u, out uint tag); if (!result) PInvokeExtensions.ThrowIfWin32Error("DeviceIoControl, IOCTL_BATTERY_QUERY_TAG"); return tag; } private static BATTERY_INFORMATION GetBatteryInformation(uint batteryTag) { var queryInformation = new BATTERY_QUERY_INFORMATION { BatteryTag = batteryTag, InformationLevel = BATTERY_QUERY_INFORMATION_LEVEL.BatteryInformation, }; var result = PInvokeExtensions.DeviceIoControl(Devices.GetBattery(), PInvoke.IOCTL_BATTERY_QUERY_INFORMATION, queryInformation, out BATTERY_INFORMATION bi); if (!result) PInvokeExtensions.ThrowIfWin32Error("DeviceIoControl, IOCTL_BATTERY_QUERY_INFORMATION"); return bi; } private static BATTERY_STATUS GetBatteryStatus(uint batteryTag) { var waitStatus = new BATTERY_WAIT_STATUS { BatteryTag = batteryTag, }; var result = PInvokeExtensions.DeviceIoControl(Devices.GetBattery(), PInvoke.IOCTL_BATTERY_QUERY_STATUS, waitStatus, out BATTERY_STATUS s); if (!result) PInvokeExtensions.ThrowIfWin32Error("DeviceIoControl, IOCTL_BATTERY_QUERY_STATUS"); return s; } private static LENOVO_BATTERY_INFORMATION? FindLenovoBatteryInformation() { for (uint index = 0; index < 3; index++) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Checking battery data at index {index}..."); var info = GetLenovoBatteryInformation(index); if (info.Temperature is ushort.MinValue or ushort.MaxValue) continue; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Battery data found at index {index}."); return info; } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Battery data not found."); return null; } private static LENOVO_BATTERY_INFORMATION GetLenovoBatteryInformation(uint index) { var result = PInvokeExtensions.DeviceIoControl(Drivers.GetEnergy(), Drivers.IOCTL_ENERGY_BATTERY_INFORMATION, index, out LENOVO_BATTERY_INFORMATION bi); if (!result) PInvokeExtensions.ThrowIfWin32Error("DeviceIoControl, 0x83102138"); return bi; } private static DateTime? DecodeDateTime(ushort s) { try { if (s < 1) return null; var date = new DateTime((s >> 9) + 1980, (s >> 5) & 15, (s & 31), 0, 0, 0, DateTimeKind.Unspecified); if (date.Year is < 2018 or > 2030) return null; return date; } catch { return null; } } private static double? DecodeTemperatureC(ushort s) { var value = (s - 2731.6) / 10.0; if (value < 0) return null; return value; } } ================================================ FILE: LenovoLegionToolkit.Lib/System/BootLogo.cs ================================================ using System; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Utils; using Windows.Win32; namespace LenovoLegionToolkit.Lib.System; public class CantSetUEFIPrivilegeException : Exception; public class CantMountUEFIPartitionException : Exception; public class NotEnoughSpaceOnUEFIPartitionException : Exception; public class InvalidBootLogoImageFormatException : Exception; public class InvalidBootLogoImageSizeException : Exception; public static class BootLogo { private const string LBLDESP = "LBLDESP"; private const string LBLDESP_GUID = "{871455D0-5576-4FB8-9865-AF0824463B9E}"; private const string LBLDVC = "LBLDVC"; private const string LBLDVC_GUID = "{871455D1-5576-4FB8-9865-AF0824463C9F}"; private const uint SCOPE_ATTR = PInvokeExtensions.VARIABLE_ATTRIBUTE_NON_VOLATILE | PInvokeExtensions.VARIABLE_ATTRIBUTE_BOOTSERVICE_ACCESS | PInvokeExtensions.VARIABLE_ATTRIBUTE_RUNTIME_ACCESS; public static async Task IsSupportedAsync() { try { var mi = await Compatibility.GetMachineInformationAsync().ConfigureAwait(false); if (!mi.Properties.SupportBootLogoChange) return false; _ = GetInfo(); _ = GetChecksum(); return true; } catch { return false; } } public static (bool, Resolution, ImageFormat[], string[]) GetStatus() { var info = GetInfo(); return (info.Enabled == 1, new(info.SupportedWidth, info.SupportedHeight), info.SupportedFormat.ImageFormats().ToArray(), info.SupportedFormat.ExtensionFilters().ToArray()); } public static async Task EnableAsync(string sourcePath) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Enabling logo... [sourcePath={sourcePath}]"); var info = GetInfo(); ThrowIfImageInvalid(info, sourcePath); await DeleteMyLogoAsync().ConfigureAwait(false); var crc = await CopyMyLogoAsync(info, sourcePath).ConfigureAwait(false); SetChecksum(crc); SetInfo(info with { Enabled = 1 }); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Enabled logo. [sourcePath={sourcePath}]"); } public static async Task DisableAsync() { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Disabling logo..."); await DeleteMyLogoAsync().ConfigureAwait(false); SetChecksum(0); SetInfo(GetInfo() with { Enabled = 0 }); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Disabled logo."); } private static unsafe BootLogoInfo GetInfo() { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Getting info..."); var ptr = Marshal.AllocHGlobal(Marshal.SizeOf()); try { if (!TokenManipulator.AddPrivileges(TokenManipulator.SE_SYSTEM_ENVIRONMENT_PRIVILEGE)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Cannot set UEFI privileges."); throw new CantSetUEFIPrivilegeException(); } var ptrSize = (uint)Marshal.SizeOf(); var size = PInvoke.GetFirmwareEnvironmentVariableEx(LBLDESP, LBLDESP_GUID, ptr.ToPointer(), ptrSize, null); if (size != ptrSize) PInvokeExtensions.ThrowIfWin32Error("GetFirmwareEnvironmentVariableEx"); var str = Marshal.PtrToStructure(ptr); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Retrieved info. [enabled={str.Enabled}, supportedWidth={str.SupportedWidth}, supportedHeight={str.SupportedHeight}, supportedFormat={(int)str.SupportedFormat}]"); return str; } finally { Marshal.FreeHGlobal(ptr); TokenManipulator.RemovePrivileges(TokenManipulator.SE_SYSTEM_ENVIRONMENT_PRIVILEGE); } } private static unsafe void SetInfo(BootLogoInfo info) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Setting info... [enabled={info.Enabled}, supportedWidth={info.SupportedWidth}, supportedHeight={info.SupportedHeight}, supportedFormat={(int)info.SupportedFormat}]"); var ptr = Marshal.AllocHGlobal(Marshal.SizeOf()); try { if (!TokenManipulator.AddPrivileges(TokenManipulator.SE_SYSTEM_ENVIRONMENT_PRIVILEGE)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Cannot set UEFI privileges."); throw new CantSetUEFIPrivilegeException(); } Marshal.StructureToPtr(info, ptr, false); var ptrSize = (uint)Marshal.SizeOf(); if (!PInvoke.SetFirmwareEnvironmentVariableEx(LBLDESP, LBLDESP_GUID, ptr.ToPointer(), ptrSize, SCOPE_ATTR)) PInvokeExtensions.ThrowIfWin32Error("SetFirmwareEnvironmentVariableEx"); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Info set. [enabled={info.Enabled}, supportedWidth={info.SupportedWidth}, supportedHeight={info.SupportedHeight}, supportedFormat={(int)info.SupportedFormat}]"); } finally { Marshal.FreeHGlobal(ptr); TokenManipulator.RemovePrivileges(TokenManipulator.SE_SYSTEM_ENVIRONMENT_PRIVILEGE); } } private static unsafe uint GetChecksum() { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Getting checksum..."); var ptr = Marshal.AllocHGlobal(Marshal.SizeOf()); try { if (!TokenManipulator.AddPrivileges(TokenManipulator.SE_SYSTEM_ENVIRONMENT_PRIVILEGE)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Cannot set UEFI privileges."); throw new CantSetUEFIPrivilegeException(); } var ptrSize = (uint)Marshal.SizeOf(); var size = PInvoke.GetFirmwareEnvironmentVariableEx(LBLDVC, LBLDVC_GUID, ptr.ToPointer(), ptrSize, null); if (size != ptrSize) PInvokeExtensions.ThrowIfWin32Error("GetFirmwareEnvironmentVariableEx"); var checksum = Marshal.PtrToStructure(ptr).Crc; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Retrieved checksum. [checksum={checksum:X2}]"); return checksum; } finally { Marshal.FreeHGlobal(ptr); TokenManipulator.RemovePrivileges(TokenManipulator.SE_SYSTEM_ENVIRONMENT_PRIVILEGE); } } private static unsafe void SetChecksum(uint checksum) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Setting checksum... [checksum={checksum:X2}]"); var str = new BootLogoChecksum { Crc = checksum }; var ptr = Marshal.AllocHGlobal(Marshal.SizeOf()); try { if (!TokenManipulator.AddPrivileges(TokenManipulator.SE_SYSTEM_ENVIRONMENT_PRIVILEGE)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Cannot set UEFI privileges."); throw new CantSetUEFIPrivilegeException(); } Marshal.StructureToPtr(str, ptr, false); var ptrSize = (uint)Marshal.SizeOf(); if (!PInvoke.SetFirmwareEnvironmentVariableEx(LBLDVC, LBLDVC_GUID, ptr.ToPointer(), ptrSize, SCOPE_ATTR)) PInvokeExtensions.ThrowIfWin32Error("SetFirmwareEnvironmentVariableEx"); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Checksum set. [checksum={checksum:X2}]"); } finally { Marshal.FreeHGlobal(ptr); TokenManipulator.RemovePrivileges(TokenManipulator.SE_SYSTEM_ENVIRONMENT_PRIVILEGE); } } private static async Task CopyMyLogoAsync(BootLogoInfo info, string sourcePath) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Copying logo... [sourcePath={sourcePath}, enabled={info.Enabled}, supportedWidth={info.SupportedWidth}, supportedHeight={info.SupportedHeight}, supportedFormat={(int)info.SupportedFormat}]"); char? drive = null; try { drive = await MountEfiPartitionAsync().ConfigureAwait(false); if (!drive.HasValue) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Cannot mount EFI partition."); throw new CantMountUEFIPartitionException(); } if (new DriveInfo($"{drive}:").AvailableFreeSpace < new FileInfo(sourcePath).Length) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Not enough free space on EFI partition."); throw new NotEnoughSpaceOnUEFIPartitionException(); } var destinationDirectory = Path.Combine($"{drive}:", "EFI", "Lenovo", "Logo"); var filename = $"mylogo_{info.SupportedWidth}x{info.SupportedHeight}{Path.GetExtension(sourcePath)}"; var destinationPath = Path.Combine(destinationDirectory, filename); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Destination path: {destinationPath}"); Directory.CreateDirectory(destinationDirectory); File.Copy(sourcePath, destinationPath, true); var checksum = Crc32Adler.Calculate(destinationPath); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Logo copied. [checksum={checksum:X2}]"); return checksum; } finally { if (drive.HasValue) await UnMountEfiPartitionAsync(drive.Value).ConfigureAwait(false); } } private static async Task DeleteMyLogoAsync() { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Deleting logos..."); char? drive = null; try { drive = await MountEfiPartitionAsync().ConfigureAwait(false); if (!drive.HasValue) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Cannot mount EFI partition"); throw new CantMountUEFIPartitionException(); } var directoryPath = $@"{drive}:\EFI\Lenovo\Logo"; if (!Directory.Exists(directoryPath)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"No logos to delete."); return; } var files = Directory.EnumerateFiles(directoryPath, "mylogo*").ToArray(); files.ForEach(File.Delete); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Logos deleted. [count={files.Length}]"); } finally { if (drive.HasValue) await UnMountEfiPartitionAsync(drive.Value).ConfigureAwait(false); } } private static void ThrowIfImageInvalid(BootLogoInfo info, string sourcePath) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Validating image... [sourcePath={sourcePath}, sourcePath={sourcePath}, enabled={info.Enabled}, supportedWidth={info.SupportedWidth}, supportedHeight={info.SupportedHeight}, supportedFormat={(int)info.SupportedFormat}]"); using var image = Image.FromFile(sourcePath); if (info.SupportedWidth != image.Width || info.SupportedHeight != image.Height) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Invalid image size."); throw new InvalidBootLogoImageSizeException(); } if (!info.SupportedFormat.ImageFormats().Contains(image.RawFormat)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Invalid image format."); throw new InvalidBootLogoImageFormatException(); } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Image valid. [sourcePath={sourcePath}, sourcePath={sourcePath}, enabled={info.Enabled}, supportedWidth={info.SupportedWidth}, supportedHeight={info.SupportedHeight}, supportedFormat={(int)info.SupportedFormat}]"); } private static char GetUnusedDriveLetter() { // ReSharper disable once StringLiteralTypo var letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray(); var usedLetters = DriveInfo.GetDrives().Select(di => di.Name.First()).ToArray(); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Used drive letters: {string.Join(",", usedLetters)}"); var letter = letters.Last(c => !usedLetters.Contains(c)); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Using '{letter}' letter."); return letter; } private static async Task MountEfiPartitionAsync() { var drive = GetUnusedDriveLetter(); var (result, _) = await CMD.RunAsync("mountvol", $"{drive}: /s").ConfigureAwait(false); if (result != 0) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to mount EFI partition at {drive}:"); return null; } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"EFI partition mounted at {drive}:"); return drive; } private static async Task UnMountEfiPartitionAsync(char letter) { var (result, _) = await CMD.RunAsync("mountvol", $"{letter}: /d").ConfigureAwait(false); if (result != 0) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to un-mount EFI partition from {letter}:"); } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"EFI partition un-mounted from {letter}:."); } } ================================================ FILE: LenovoLegionToolkit.Lib/System/CMD.cs ================================================ using System.Collections.Generic; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.System; public static class CMD { public static async Task<(int, string)> RunAsync(string file, string arguments, bool createNoWindow = true, bool waitForExit = true, Dictionary? environment = null, CancellationToken token = default) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Running... [file={file}, argument={arguments}, createNoWindow={createNoWindow}, waitForExit={waitForExit}, environment=[{(environment is null ? string.Empty : string.Join(",", environment))}]"); var cmd = new Process(); cmd.StartInfo.UseShellExecute = false; cmd.StartInfo.CreateNoWindow = createNoWindow; cmd.StartInfo.RedirectStandardOutput = createNoWindow; cmd.StartInfo.RedirectStandardError = createNoWindow; cmd.StartInfo.WindowStyle = createNoWindow ? ProcessWindowStyle.Hidden : ProcessWindowStyle.Normal; cmd.StartInfo.FileName = file; if (!string.IsNullOrWhiteSpace(arguments)) cmd.StartInfo.Arguments = arguments; if (environment is not null) { foreach (var (key, value) in environment) cmd.StartInfo.Environment[key] = value; } cmd.Start(); if (!waitForExit) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Ran [file={file}, argument={arguments}, createNoWindow={createNoWindow}, waitForExit={waitForExit}, environment=[{(environment is null ? string.Empty : string.Join(",", environment))}]"); return (-1, string.Empty); } await cmd.WaitForExitAsync(token).ConfigureAwait(false); var exitCode = cmd.ExitCode; var output = createNoWindow ? await cmd.StandardOutput.ReadToEndAsync(token).ConfigureAwait(false) : string.Empty; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Ran [file={file}, argument={arguments}, createNoWindow={createNoWindow}, waitForExit={waitForExit}, exitCode={exitCode} output={output}]"); return (exitCode, output); } } ================================================ FILE: LenovoLegionToolkit.Lib/System/Devices.cs ================================================ using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Utils; using Microsoft.Win32.SafeHandles; using Windows.Win32; using Windows.Win32.Devices.DeviceAndDriverInstallation; using Windows.Win32.Devices.HumanInterfaceDevice; using Windows.Win32.Devices.Properties; using Windows.Win32.Foundation; using Windows.Win32.Storage.FileSystem; namespace LenovoLegionToolkit.Lib.System; public static class Devices { private static readonly object Lock = new(); private static SafeFileHandle? _battery; private static SafeFileHandle? _rgbKeyboard; private static SafeFileHandle? _spectrumRgbKeyboard; #region All devices public static List GetAll() { using var deviceInfoSet = PInvoke.SetupDiGetClassDevs(null as Guid?, null, HWND.Null, SETUP_DI_GET_CLASS_DEVS_FLAGS.DIGCF_ALLCLASSES); if (deviceInfoSet.IsInvalid) return []; var devices = new List(); var index = 0u; while (true) { var currentIndex = index; index++; var deviceInfoData = new SP_DEVINFO_DATA { cbSize = (uint)Marshal.SizeOf() }; if (!PInvoke.SetupDiEnumDeviceInfo(deviceInfoSet, currentIndex, ref deviceInfoData)) { if (Marshal.GetLastWin32Error() == PInvokeExtensions.ERROR_NO_MORE_ITEMS) break; continue; } var (removable, isDisconnected) = GetFlags(deviceInfoData); if (isDisconnected) { var capabilities = (CM_DEVCAP)GetUInt32Property(deviceInfoSet, deviceInfoData, PInvoke.DEVPKEY_Device_Capabilities); removable = capabilities.HasFlag(CM_DEVCAP.CM_DEVCAP_REMOVABLE); } var name = GetStringProperty(deviceInfoSet, deviceInfoData, PInvoke.DEVPKEY_NAME); var description = GetStringProperty(deviceInfoSet, deviceInfoData, PInvoke.DEVPKEY_Device_DeviceDesc); var busReportedDeviceDescription = GetStringProperty(deviceInfoSet, deviceInfoData, PInvoke.DEVPKEY_Device_BusReportedDeviceDesc); var deviceInstanceId = GetStringProperty(deviceInfoSet, deviceInfoData, PInvoke.DEVPKEY_Device_InstanceId); var classGuid = GetGuidProperty(deviceInfoSet, deviceInfoData, PInvoke.DEVPKEY_Device_ClassGuid); var className = GetClassName(classGuid); if (deviceInstanceId.Contains("ROOT")) continue; devices.Add(new(name, description, busReportedDeviceDescription, deviceInstanceId, classGuid, className, removable, isDisconnected)); } return devices; } private static unsafe string GetClassName(Guid guid) { var requiredSize = 0u; PInvoke.SetupDiClassNameFromGuid(guid, Array.Empty(), &requiredSize); var chars = new char[requiredSize]; PInvoke.SetupDiClassNameFromGuid(guid, chars, null); return chars.ToString() ?? string.Empty; } private static unsafe string GetStringProperty(SetupDiDestroyDeviceInfoListSafeHandle deviceInfoSet, SP_DEVINFO_DATA deviceInfoData, DEVPROPKEY propertyKey) { var requiredSize = 0u; PInvoke.SetupDiGetDeviceProperty(deviceInfoSet, deviceInfoData, propertyKey, out var propertyType, null, &requiredSize, 0); if (propertyType == DEVPROPTYPE.DEVPROP_TYPE_EMPTY) return string.Empty; if (propertyType != DEVPROPTYPE.DEVPROP_TYPE_STRING) throw new InvalidOperationException("Device property is not a string"); var buffer = new byte[requiredSize]; var propertyBuffer = new Span(buffer); PInvoke.SetupDiGetDeviceProperty(deviceInfoSet, deviceInfoData, propertyKey, out _, propertyBuffer, null, 0); return Encoding.Unicode.GetString(buffer).TrimEnd('\0'); } private static unsafe uint GetUInt32Property(SetupDiDestroyDeviceInfoListSafeHandle deviceInfoSet, SP_DEVINFO_DATA deviceInfoData, DEVPROPKEY propertyKey) { var requiredSize = 0u; PInvoke.SetupDiGetDeviceProperty(deviceInfoSet, deviceInfoData, propertyKey, out var propertyType, null, &requiredSize, 0); if (propertyType == DEVPROPTYPE.DEVPROP_TYPE_EMPTY) return 0; if (propertyType != DEVPROPTYPE.DEVPROP_TYPE_UINT32) throw new InvalidOperationException("Device property is not a string"); var buffer = new byte[requiredSize]; var propertyBuffer = new Span(buffer); PInvoke.SetupDiGetDeviceProperty(deviceInfoSet, deviceInfoData, propertyKey, out _, propertyBuffer, null, 0); return BitConverter.ToUInt32(buffer); } private static unsafe Guid GetGuidProperty(SetupDiDestroyDeviceInfoListSafeHandle deviceInfoSet, SP_DEVINFO_DATA deviceInfoData, DEVPROPKEY propertyKey) { var requiredSize = 0u; PInvoke.SetupDiGetDeviceProperty(deviceInfoSet, deviceInfoData, propertyKey, out var propertyType, null, &requiredSize, 0); if (propertyType == DEVPROPTYPE.DEVPROP_TYPE_EMPTY) return Guid.Empty; if (propertyType != DEVPROPTYPE.DEVPROP_TYPE_GUID) throw new InvalidOperationException("Device property is not a GUID"); var buffer = new byte[requiredSize]; var propertyBuffer = new Span(buffer); PInvoke.SetupDiGetDeviceProperty(deviceInfoSet, deviceInfoData, propertyKey, out _, propertyBuffer, null, 0); return new Guid(buffer); } private static (bool noShow, bool disconnected) GetFlags(SP_DEVINFO_DATA deviceInfoData) { var result = PInvoke.CM_Get_DevNode_Status(out var flags, out _, deviceInfoData.DevInst, 0); return (flags.HasFlag(CM_DEVNODE_STATUS_FLAGS.DN_REMOVABLE), result == CONFIGRET.CR_NO_SUCH_DEVINST); } #endregion #region Battery public static unsafe SafeFileHandle GetBattery(bool forceRefresh = false) { if (_battery is not null && !forceRefresh) return _battery; lock (Lock) { if (_battery is not null && !forceRefresh) return _battery; var devClassBatteryGuid = PInvoke.GUID_DEVCLASS_BATTERY; using var deviceHandle = PInvoke.SetupDiGetClassDevs(devClassBatteryGuid, null, HWND.Null, SETUP_DI_GET_CLASS_DEVS_FLAGS.DIGCF_PRESENT | SETUP_DI_GET_CLASS_DEVS_FLAGS.DIGCF_DEVICEINTERFACE); // ReSharper disable once StringLiteralTypo if (deviceHandle.IsInvalid) PInvokeExtensions.ThrowIfWin32Error("SetupDiGetClassDevs"); var deviceInterfaceData = new SP_DEVICE_INTERFACE_DATA { cbSize = (uint)Marshal.SizeOf() }; var result1 = PInvoke.SetupDiEnumDeviceInterfaces(deviceHandle, null, devClassBatteryGuid, 0, ref deviceInterfaceData); if (!result1) PInvokeExtensions.ThrowIfWin32Error("SetupDiEnumDeviceInterfaces"); var requiredSize = 0u; _ = PInvoke.SetupDiGetDeviceInterfaceDetail(deviceHandle, deviceInterfaceData, null, 0, &requiredSize, null); string devicePath; var output = IntPtr.Zero; try { output = Marshal.AllocHGlobal((int)requiredSize); var deviceDetailData = (SP_DEVICE_INTERFACE_DETAIL_DATA_W*)output.ToPointer(); deviceDetailData->cbSize = (uint)Marshal.SizeOf(); var result3 = PInvoke.SetupDiGetDeviceInterfaceDetail(deviceHandle, deviceInterfaceData, deviceDetailData, requiredSize, null, null); if (!result3) PInvokeExtensions.ThrowIfWin32Error("SetupDiEnumDeviceInterfaces"); fixed (char* e0Ptr = &deviceDetailData->DevicePath.e0) devicePath = new string(e0Ptr); } finally { Marshal.FreeHGlobal(output); } var fileHandle = PInvoke.CreateFile(devicePath, (uint)FILE_ACCESS_RIGHTS.FILE_READ_DATA | (uint)FILE_ACCESS_RIGHTS.FILE_WRITE_DATA, FILE_SHARE_MODE.FILE_SHARE_READ | FILE_SHARE_MODE.FILE_SHARE_WRITE, null, FILE_CREATION_DISPOSITION.OPEN_EXISTING, FILE_FLAGS_AND_ATTRIBUTES.FILE_ATTRIBUTE_NORMAL, null); if (fileHandle.IsInvalid) PInvokeExtensions.ThrowIfWin32Error("CreateFile"); _battery = fileHandle; } return _battery; } #endregion #region Keyboard public static SafeFileHandle? GetRGBKeyboard(bool forceRefresh = false) { if (_rgbKeyboard is not null && !forceRefresh) return _rgbKeyboard; lock (Lock) { if (_rgbKeyboard is not null && !forceRefresh) return _rgbKeyboard; const ushort vendorId = 0x048D; const ushort productIdMasked = 0xC900; const ushort productIdMask = 0xFF00; const ushort descriptorLength = 0x21; _rgbKeyboard = FindHidDevice(vendorId, productIdMask, productIdMasked, descriptorLength); } return _rgbKeyboard; } public static SafeFileHandle? GetSpectrumRGBKeyboard(bool forceRefresh = false) { if (_spectrumRgbKeyboard is not null && !forceRefresh) return _spectrumRgbKeyboard; lock (Lock) { if (_spectrumRgbKeyboard is not null && !forceRefresh) return _spectrumRgbKeyboard; const ushort vendorId = 0x048D; const ushort productIdMasked = 0xC900; const ushort productIdMask = 0xFF00; const ushort descriptorLength = 0x03C0; _spectrumRgbKeyboard = FindHidDevice(vendorId, productIdMask, productIdMasked, descriptorLength); } return _spectrumRgbKeyboard; } private static unsafe SafeFileHandle? FindHidDevice(ushort vendorId, ushort productIdMask, ushort productIdMasked, ushort descriptorLength) { PInvoke.HidD_GetHidGuid(out var devClassHidGuid); using var deviceHandle = PInvoke.SetupDiGetClassDevs(devClassHidGuid, null, HWND.Null, SETUP_DI_GET_CLASS_DEVS_FLAGS.DIGCF_PRESENT | SETUP_DI_GET_CLASS_DEVS_FLAGS.DIGCF_DEVICEINTERFACE); uint index = 0; while (true) { var currentIndex = index; index++; var deviceInfoData = new SP_DEVINFO_DATA { cbSize = (uint)Marshal.SizeOf() }; var result1 = PInvoke.SetupDiEnumDeviceInfo(deviceHandle, currentIndex, ref deviceInfoData); if (!result1) { var errorCode = Marshal.GetLastWin32Error(); if (errorCode == PInvokeExtensions.ERROR_NO_MORE_ITEMS) break; PInvokeExtensions.ThrowIfWin32Error(errorCode, "SetupDiEnumDeviceInfo"); } var deviceInterfaceData = new SP_DEVICE_INTERFACE_DATA { cbSize = (uint)Marshal.SizeOf() }; var result2 = PInvoke.SetupDiEnumDeviceInterfaces(deviceHandle, null, devClassHidGuid, currentIndex, ref deviceInterfaceData); if (!result2) PInvokeExtensions.ThrowIfWin32Error("SetupDiEnumDeviceInterfaces"); var requiredSize = 0u; _ = PInvoke.SetupDiGetDeviceInterfaceDetail(deviceHandle, deviceInterfaceData, null, 0, &requiredSize, null); string devicePath; var output = IntPtr.Zero; try { output = Marshal.AllocHGlobal((int)requiredSize); var deviceDetailData = (SP_DEVICE_INTERFACE_DETAIL_DATA_W*)output.ToPointer(); deviceDetailData->cbSize = (uint)Marshal.SizeOf(); var result3 = PInvoke.SetupDiGetDeviceInterfaceDetail(deviceHandle, deviceInterfaceData, deviceDetailData, requiredSize, null, null); if (!result3) PInvokeExtensions.ThrowIfWin32Error("SetupDiEnumDeviceInterfaces"); fixed (char* e0Ptr = &deviceDetailData->DevicePath.e0) devicePath = new string(e0Ptr); } finally { Marshal.FreeHGlobal(output); } var fileHandle = PInvoke.CreateFile(devicePath, (uint)FILE_ACCESS_RIGHTS.FILE_READ_DATA | (uint)FILE_ACCESS_RIGHTS.FILE_WRITE_DATA, FILE_SHARE_MODE.FILE_SHARE_READ | FILE_SHARE_MODE.FILE_SHARE_WRITE, null, FILE_CREATION_DISPOSITION.OPEN_EXISTING, FILE_FLAGS_AND_ATTRIBUTES.FILE_ATTRIBUTE_NORMAL, null); if (!PInvoke.HidD_GetAttributes(fileHandle, out var hidAttributes)) continue; PHIDP_PREPARSED_DATA preParsedData = default; try { PInvoke.HidD_GetPreparsedData(fileHandle, out preParsedData); PInvoke.HidP_GetCaps(preParsedData, out var caps); if (hidAttributes.VendorID == vendorId && (hidAttributes.ProductID & productIdMask) == productIdMasked && caps.FeatureReportByteLength == descriptorLength) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Found device. [vendorId={hidAttributes.VendorID:X2}, productId={hidAttributes.ProductID:X2}, descriptorLength={caps.FeatureReportByteLength}]"); return fileHandle; } } finally { PInvoke.HidD_FreePreparsedData(preParsedData); } } return null; } #endregion } ================================================ FILE: LenovoLegionToolkit.Lib/System/Displays.cs ================================================ using System.Linq; using WindowsDisplayAPI; namespace LenovoLegionToolkit.Lib.System; public static class Displays { public static Display[] Get() => Display.GetDisplays().ToArray(); } ================================================ FILE: LenovoLegionToolkit.Lib/System/Drivers.cs ================================================ using System; using Microsoft.Win32.SafeHandles; using Windows.Win32; using Windows.Win32.Storage.FileSystem; namespace LenovoLegionToolkit.Lib.System; public static class Drivers { public const uint IOCTL_ENERGY_BATTERY_INFORMATION = 0x83102138; public const uint IOCTL_ENERGY_SETTINGS = 0x831020E8; public const uint IOCTL_ENERGY_BATTERY_CHARGE_MODE = 0x831020F8; public const uint IOCTL_ENERGY_BATTERY_NIGHT_CHARGE = 0x83102150; public const uint IOCTL_ENERGY_KEYBOARD = 0x83102144; public const uint IOCTL_KEY_WAIT_HANDLE = 0x831020D8; public const uint IOCTL_KEY_VALUE = 0x831020CC; private static readonly object Lock = new(); private static SafeFileHandle? _energy; public static SafeFileHandle GetEnergy() { if (_energy is not null) return _energy; lock (Lock) { if (_energy is not null) return _energy; var handle = PInvoke.CreateFile(@"\\.\EnergyDrv", (uint)FILE_ACCESS_RIGHTS.FILE_READ_DATA | (uint)FILE_ACCESS_RIGHTS.FILE_WRITE_DATA, FILE_SHARE_MODE.FILE_SHARE_READ | FILE_SHARE_MODE.FILE_SHARE_WRITE, null, FILE_CREATION_DISPOSITION.OPEN_EXISTING, FILE_FLAGS_AND_ATTRIBUTES.FILE_ATTRIBUTE_NORMAL, null); if (handle.IsInvalid) throw new InvalidOperationException("handle is invalid"); _energy = handle; } return _energy; } } ================================================ FILE: LenovoLegionToolkit.Lib/System/ExternalDisplays.cs ================================================ using System.Linq; using WindowsDisplayAPI; namespace LenovoLegionToolkit.Lib.System; public static class ExternalDisplays { public static Display[] Get() { var internalDisplay = InternalDisplay.Get(); var allDisplays = Display.GetDisplays(); return allDisplays.Where(d => d.DevicePath != internalDisplay?.DevicePath).ToArray(); } } ================================================ FILE: LenovoLegionToolkit.Lib/System/InternalDisplay.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Utils; using Windows.Win32; using Windows.Win32.Devices.Display; using Windows.Win32.Foundation; using WindowsDisplayAPI; using WindowsDisplayAPI.DisplayConfig; namespace LenovoLegionToolkit.Lib.System; public static class InternalDisplay { private readonly struct DisplayHolder { public static readonly DisplayHolder Empty = new(); private readonly Display? _display; private DisplayHolder(Display? display) => _display = display; public static implicit operator DisplayHolder(Display? s) => new(s); public static implicit operator Display?(DisplayHolder s) => s._display; } private static readonly object Lock = new(); private static DisplayHolder? _displayHolder; public static void SetNeedsRefresh() { lock (Lock) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Resetting holder..."); _displayHolder = null; } } public static Display? Get() { lock (Lock) { if (_displayHolder is not null) return _displayHolder; var displays = Display.GetDisplays().ToArray(); var internalDisplay = FindInternalDisplay(displays); if (internalDisplay is not null) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Found internal display: {internalDisplay}"); return (_displayHolder = internalDisplay); } var aoDisplay = FindInternalAdvancedOptimusDisplay(displays); if (aoDisplay is not null) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Found internal AO display: {aoDisplay}"); return (_displayHolder = aoDisplay); } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"No internal displays found."); return (_displayHolder = DisplayHolder.Empty); } } private static Display? FindInternalDisplay(IEnumerable displays) { return displays.Where(d => d.GetVideoOutputTechnology().IsInternalOutput()).FirstOrDefault(); } private static Display? FindInternalAdvancedOptimusDisplay(IEnumerable displays) { var exDpDisplays = displays.Where(di => di.GetVideoOutputTechnology().IsExternalDisplayPortOutput()).ToArray(); if (exDpDisplays.Length < 1) return null; var exDpDisplay = exDpDisplays[0]; var exDpPathDisplayTarget = exDpDisplay.ToPathDisplayTarget(); var exDpPortDisplayEdid = exDpPathDisplayTarget.EDIDManufactureId; var sameDeviceIsOnAnotherAdapter = DisplayAdapter.GetDisplayAdapters() .Where(da => da.DevicePath != exDpDisplay.Adapter.DevicePath) .SelectMany(da => da.GetDisplayDevices()) .Select(dd => dd.ToPathDisplayTarget()) .Any(pdt => pdt.EDIDManufactureId == exDpPortDisplayEdid && pdt.GetVideoOutputTechnology().IsInternalOutput()); return sameDeviceIsOnAnotherAdapter ? exDpDisplay : null; } private static DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY GetVideoOutputTechnology(this DisplayDevice displayDevice) { return GetVideoOutputTechnology(displayDevice.ToPathDisplayTarget()); } private static unsafe DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY GetVideoOutputTechnology(this PathDisplayTarget pathDisplayTarget) { var intPtr = IntPtr.Zero; try { var deviceName = new DISPLAYCONFIG_TARGET_DEVICE_NAME { header = new DISPLAYCONFIG_DEVICE_INFO_HEADER { type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME, id = pathDisplayTarget.TargetId, adapterId = new LUID { HighPart = pathDisplayTarget.Adapter.AdapterId.HighPart, LowPart = pathDisplayTarget.Adapter.AdapterId.LowPart, }, size = (uint)Marshal.SizeOf() } }; intPtr = Marshal.AllocHGlobal((int)deviceName.header.size); Marshal.StructureToPtr(deviceName, intPtr, false); var success = PInvoke.DisplayConfigGetDeviceInfo((DISPLAYCONFIG_DEVICE_INFO_HEADER*)intPtr.ToPointer()); if (success != PInvokeExtensions.ERROR_SUCCESS) PInvokeExtensions.ThrowIfWin32Error("DisplayConfigGetDeviceInfo"); var deviceNameResponse = Marshal.PtrToStructure(intPtr); return deviceNameResponse.outputTechnology; } catch { return DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY.DISPLAYCONFIG_OUTPUT_TECHNOLOGY_OTHER; } finally { Marshal.FreeHGlobal(intPtr); } } private static bool IsInternalOutput(this DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY outputTechnology) { var result = outputTechnology is DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY.DISPLAYCONFIG_OUTPUT_TECHNOLOGY_INTERNAL; result |= outputTechnology is DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY.DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EMBEDDED; return result; } private static bool IsExternalDisplayPortOutput(this DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY outputTechnology) { return outputTechnology is DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY.DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EXTERNAL; } } ================================================ FILE: LenovoLegionToolkit.Lib/System/KnownFolders.cs ================================================ using System; using System.Collections.Generic; using LenovoLegionToolkit.Lib.Extensions; using Windows.Win32; namespace LenovoLegionToolkit.Lib.System; public static class KnownFolders { private static readonly Dictionary Folders = new() { [KnownFolder.Contacts] = new("56784854-C6CB-462B-8169-88E350ACB882"), [KnownFolder.Downloads] = new("374DE290-123F-4565-9164-39C4925E467B"), [KnownFolder.Favorites] = new("1777F761-68AD-4D8A-87BD-30B759FA33DD"), [KnownFolder.Links] = new("BFB9D5E0-C6A9-404C-B2B2-AE6DB6AF4968"), [KnownFolder.SavedGames] = new("4C5C32FF-BB9D-43B0-B5B4-2D72E54EAAA4"), [KnownFolder.SavedSearches] = new("7D1D3A04-DEBB-4115-95CF-2F29DA2920DA") }; public static string GetPath(KnownFolder knownFolder) { if (!PInvoke.SHGetKnownFolderPath(Folders[knownFolder], PInvokeExtensions.KF_FLAG_DEFAULT, null, out var path).Succeeded) return string.Empty; return path.ToString() ?? string.Empty; } } ================================================ FILE: LenovoLegionToolkit.Lib/System/Management/WMI.LenovoCapabilityData00.cs ================================================ using System; using System.Collections.Generic; using System.Threading.Tasks; // ReSharper disable InconsistentNaming // ReSharper disable StringLiteralTypo namespace LenovoLegionToolkit.Lib.System.Management; public static partial class WMI { public static class LenovoCapabilityData00 { public static Task> ReadAsync() => WMI.ReadAsync("root\\WMI", $"SELECT * FROM LENOVO_CAPABILITY_DATA_00", pdc => (CapabilityID)Convert.ToInt32(pdc["IDs"].Value)); } } ================================================ FILE: LenovoLegionToolkit.Lib/System/Management/WMI.LenovoCapabilityData01.cs ================================================ using System; using System.Collections.Generic; using System.Threading.Tasks; // ReSharper disable InconsistentNaming // ReSharper disable StringLiteralTypo namespace LenovoLegionToolkit.Lib.System.Management; public static partial class WMI { public static class LenovoCapabilityData01 { public static Task> ReadAsync() => WMI.ReadAsync("root\\WMI", $"SELECT * FROM LENOVO_CAPABILITY_DATA_01", pdc => { var id = Convert.ToInt32(pdc["IDs"].Value); var defaultValue = Convert.ToInt32(pdc["DefaultValue"].Value); var min = Convert.ToInt32(pdc["MinValue"].Value); var max = Convert.ToInt32(pdc["MaxValue"].Value); var step = Convert.ToInt32(pdc["Step"].Value); return new RangeCapability((CapabilityID)id, defaultValue, min, max, step); }); } } ================================================ FILE: LenovoLegionToolkit.Lib/System/Management/WMI.LenovoCpuMethod.cs ================================================ using System; using System.Threading.Tasks; // ReSharper disable InconsistentNaming // ReSharper disable StringLiteralTypo namespace LenovoLegionToolkit.Lib.System.Management; public static partial class WMI { public static class LenovoCpuMethod { public static Task<(int longTerm, int shortTerm)> CPUGetDefaultPowerLimitAsync() => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_CPU_METHOD", "CPU_Get_Default_PowerLimit", [], pdc => { var longTerm = Convert.ToInt32(pdc["DefaultLongTermPowerlimit"].Value); var shortTerm = Convert.ToInt32(pdc["DefaultShortTermPowerlimit"].Value); return (longTerm, shortTerm); }); public static Task<(int value, int min, int max, int step)> CPUGetLongTermPowerLimitAsync() => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_CPU_METHOD", "CPU_Get_LongTerm_PowerLimit", [], pdc => { var value = Convert.ToInt32(pdc["CurrentLongTerm_PowerLimit"].Value); var min = Convert.ToInt32(pdc["MinLongTerm_PowerLimit"].Value); var max = Convert.ToInt32(pdc["MaxLongTerm_PowerLimit"].Value); var step = Convert.ToInt32(pdc["step"].Value); return (value, min, max, step); }); public static Task CPUSetLongTermPowerLimitAsync(int value) => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_CPU_METHOD", "CPU_Set_LongTerm_PowerLimit", new() { { "value", value } }); public static Task<(int value, int min, int max, int step)> CPUGetShortTermPowerLimitAsync() => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_CPU_METHOD", "CPU_Get_ShortTerm_PowerLimit", [], pdc => { var value = Convert.ToInt32(pdc["CurrentShortTerm_PowerLimit"].Value); var min = Convert.ToInt32(pdc["MinShortTerm_PowerLimit"].Value); var max = Convert.ToInt32(pdc["MaxShortTerm_PowerLimit"].Value); var step = Convert.ToInt32(pdc["step"].Value); return (value, min, max, step); }); public static Task CPUSetShortTermPowerLimitAsync(int value) => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_CPU_METHOD", "CPU_Set_ShortTerm_PowerLimit", new() { { "value", value } }); public static Task<(int value, int min, int max, int step, int defaultValue)> CPUGetPeakPowerLimitAsync() => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_CPU_METHOD", "CPU_Get_Peak_PowerLimit", [], pdc => { var value = Convert.ToInt32(pdc["CurrentPeakPowerLimit"].Value); var min = Convert.ToInt32(pdc["MinPeakPowerLimit"].Value); var max = Convert.ToInt32(pdc["MaxPeakPowerLimit"].Value); var step = Convert.ToInt32(pdc["step"].Value); var defaultValue = Convert.ToInt32(pdc["DefaultPeakPowerLimit"].Value); return (value, min, max, step, defaultValue); }); public static Task CPUSetPeakPowerLimitAsync(int value) => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_CPU_METHOD", "CPU_Set_Peak_PowerLimit", new() { { "CurrentPeakPowerLimit", value } }); public static Task<(int value, int min, int max, int step, int defaultValue)> CPUGetCrossLoadingPowerLimitAsync() => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_CPU_METHOD", "CPU_Get_Cross_Loading_PowerLimit", [], pdc => { var value = Convert.ToInt32(pdc["CurrentCpuCrossLoading"].Value); var min = Convert.ToInt32(pdc["MinCpuCrossLoading"].Value); var max = Convert.ToInt32(pdc["MaxCpuCrossLoading"].Value); var step = Convert.ToInt32(pdc["step"].Value); var defaultValue = Convert.ToInt32(pdc["DefaultCpuCrossLoading"].Value); return (value, min, max, step, defaultValue); }); public static Task CPUSetCrossLoadingPowerLimitAsync(int value) => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_CPU_METHOD", "CPU_Set_Cross_Loading_PowerLimit", new() { { "CurrentCpuCrossLoading", value } }); public static Task<(int value, int min, int max, int step, int defaultValue)> GetAPUSPPTPowerLimitAsync() => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_CPU_METHOD", "Get_APU_sPPT_PowerLimit", [], pdc => { var value = Convert.ToInt32(pdc["CurrenAPUsPPTPowerLimit"].Value); var min = Convert.ToInt32(pdc["MinAPUsPPTPowerLimit"].Value); var max = Convert.ToInt32(pdc["MaxAPUsPPTPowerLimit"].Value); var step = Convert.ToInt32(pdc["step"].Value); var defaultValue = Convert.ToInt32(pdc["DefaultAPUsPPTPowerLimit"].Value); return (value, min, max, step, defaultValue); }); public static Task SetAPUSPPTPowerLimitAsync(int value) => WMI.CallAsync("root\\WMI", $"SELECT * FROM LENOVO_CPU_METHOD", "Set_APU_sPPT_PowerLimit", new() { { "CurrentAPUsPPTPowerLimit", value } }); public static Task<(int value, int min, int max, int step, int defaultValue)> CPUGetTemperatureControlAsync() => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_CPU_METHOD", "CPU_Get_Temperature_Control", [], pdc => { var value = Convert.ToInt32(pdc["CurrentTemperatueControl"].Value); var min = Convert.ToInt32(pdc["MinTemperatueControl"].Value); var max = Convert.ToInt32(pdc["MaxTemperatueControl"].Value); var step = Convert.ToInt32(pdc["step"].Value); var defaultValue = Convert.ToInt32(pdc["DefaultTemperatueControl"].Value); return (value, min, max, step, defaultValue); }); public static Task CPUSetTemperatureControlAsync(int value) => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_CPU_METHOD", "CPU_Set_Temperature_Control", new() { { "CurrentTemperatureControl", value } }); } } ================================================ FILE: LenovoLegionToolkit.Lib/System/Management/WMI.LenovoDefaultValueInDifferentModeData.cs ================================================ using System; using System.Collections.Generic; using System.Threading.Tasks; // ReSharper disable InconsistentNaming // ReSharper disable StringLiteralTypo namespace LenovoLegionToolkit.Lib.System.Management; public static partial class WMI { public static class LenovoDefaultValueInDifferentModeData { public readonly struct Data( int mode, int cpuLongTermPowerLimit, int cpuShortTermPowerLimit, int cpuPeakPowerLimit, int cpuCrossLoadingPowerLimit, int apUsPPTPowerLimit, int cpuTemperatureLimit, int gpuPowerBoost, int gpuConfigurableTGP, int gpuTemperatureLimit) { public int Mode { get; } = mode; public int CPULongTermPowerLimit { get; } = cpuLongTermPowerLimit; public int CPUShortTermPowerLimit { get; } = cpuShortTermPowerLimit; public int CPUPeakPowerLimit { get; } = cpuPeakPowerLimit; public int CPUCrossLoadingPowerLimit { get; } = cpuCrossLoadingPowerLimit; public int APUsPPTPowerLimit { get; } = apUsPPTPowerLimit; public int CPUTemperatureLimit { get; } = cpuTemperatureLimit; public int GPUPowerBoost { get; } = gpuPowerBoost; public int GPUConfigurableTGP { get; } = gpuConfigurableTGP; public int GPUTemperatureLimit { get; } = gpuTemperatureLimit; } public static Task> ReadAsync() => WMI.ReadAsync("root\\WMI", $"SELECT * FROM LENOVO_DEFAULT_VALUE_IN_DIFFERENT_MODE_DATA ", pdc => new Data(Convert.ToInt32(pdc["mode"].Value), Convert.ToInt32(pdc["DefaultLongTermPowerlimit"].Value), Convert.ToInt32(pdc["DefaultShortTermPowerlimit"].Value), Convert.ToInt32(pdc["DefaultPeakPowerLimit"].Value), Convert.ToInt32(pdc["DefaultCpuCrossLoading"].Value), Convert.ToInt32(pdc["DefaultAPUsPPTPowerLimit"].Value), Convert.ToInt32(pdc["DefaultTemperatueControl"].Value), Convert.ToInt32(pdc["Default_PPAB_Powerlimit"].Value), Convert.ToInt32(pdc["Default_cTGP_Powerlimit"].Value), Convert.ToInt32(pdc["DefaultTemperatueLimit"].Value))); } } ================================================ FILE: LenovoLegionToolkit.Lib/System/Management/WMI.LenovoDiscreteData.cs ================================================ using System; using System.Collections.Generic; using System.Threading.Tasks; // ReSharper disable InconsistentNaming // ReSharper disable StringLiteralTypo namespace LenovoLegionToolkit.Lib.System.Management; public static partial class WMI { public static class LenovoDiscreteData { public static Task> ReadAsync() => WMI.ReadAsync("root\\WMI", $"SELECT * FROM LENOVO_DISCRETE_DATA", pdc => { var id = (CapabilityID)Convert.ToInt32(pdc["IDs"].Value); var value = Convert.ToInt32(pdc["Value"].Value); return new DiscreteCapability(id, value); }); } } ================================================ FILE: LenovoLegionToolkit.Lib/System/Management/WMI.LenovoFanMethod.cs ================================================ using System; using System.Linq; using System.Threading.Tasks; // ReSharper disable InconsistentNaming // ReSharper disable StringLiteralTypo namespace LenovoLegionToolkit.Lib.System.Management; public static partial class WMI { public static class LenovoFanMethod { public static Task FanSetTableAsync(byte[] fanTable) => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_FAN_METHOD", "Fan_Set_Table", new() { { "FanTable", fanTable } }); public static Task FanGetFullSpeedAsync() => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_FAN_METHOD", "Fan_Get_FullSpeed", [], pdc => (bool)pdc["Status"].Value); public static Task FanSetFullSpeedAsync(int status) => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_FAN_METHOD", "Fan_Set_FullSpeed", new() { { "Status", status } }); public static Task FanGetCurrentSensorTemperatureAsync(int sensorId) => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_FAN_METHOD", "Fan_GetCurrentSensorTemperature", new() { { "SensorID", sensorId } }, pdc => Convert.ToInt32(pdc["CurrentSensorTemperature"].Value)); public static Task FanGetCurrentFanSpeedAsync(int fanId) => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_FAN_METHOD", "Fan_GetCurrentFanSpeed", new() { { "FanID", fanId } }, pdc => Convert.ToInt32(pdc["CurrentFanSpeed"].Value)); public static async Task GetCurrentFanMaxSpeedAsync(int sensorId, int fanId) { var result = await ReadAsync("root\\WMI", $"SELECT * FROM LENOVO_FAN_TABLE_DATA WHERE Sensor_ID = {sensorId} AND Fan_Id = {fanId}", pdc => Convert.ToInt32(pdc["CurrentFanMaxSpeed"].Value)).ConfigureAwait(false); return result.Max(); } public static async Task GetDefaultFanMaxSpeedAsync(int sensorId, int fanID) { var result = await ReadAsync("root\\WMI", $"SELECT * FROM LENOVO_FAN_TABLE_DATA WHERE Sensor_ID = {sensorId} AND Fan_Id = {fanID}", pdc => Convert.ToInt32(pdc["DefaultFanMaxSpeed"].Value)).ConfigureAwait(false); return result.Max(); } } } ================================================ FILE: LenovoLegionToolkit.Lib/System/Management/WMI.LenovoFanTableData.cs ================================================ using System; using System.Collections.Generic; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Extensions; // ReSharper disable InconsistentNaming // ReSharper disable StringLiteralTypo namespace LenovoLegionToolkit.Lib.System.Management; public static partial class WMI { public static class LenovoFanTableData { public static Task ExistsAsync(int sensorId, int fanId) => WMI.ExistsAsync("root\\WMI", $"SELECT * FROM LENOVO_FAN_TABLE_DATA WHERE Sensor_ID = {sensorId} AND Fan_Id = {fanId}"); public static Task> ReadAsync() => WMI.ReadAsync("root\\WMI", $"SELECT * FROM LENOVO_FAN_TABLE_DATA", pdc => { var mode = pdc.Contains("Mode") ? Convert.ToInt32(pdc["Mode"].Value) : -1; var fanId = Convert.ToByte(pdc["Fan_Id"].Value); var sensorId = Convert.ToByte(pdc["Sensor_ID"].Value); var fanTableData = (ushort[]?)pdc["FanTable_Data"].Value ?? []; var sensorTableData = (ushort[]?)pdc["SensorTable_Data"].Value ?? []; return (mode, fanId, sensorId, fanTableData, sensorTableData); }); } } ================================================ FILE: LenovoLegionToolkit.Lib/System/Management/WMI.LenovoGameZoneData.cs ================================================ using System; using System.Text.RegularExpressions; using System.Threading.Tasks; // ReSharper disable InconsistentNaming // ReSharper disable StringLiteralTypo namespace LenovoLegionToolkit.Lib.System.Management; public static partial class WMI { public static partial class LenovoGameZoneData { [GeneratedRegex("PCIVEN_([0-9A-F]{4})|DEV_([0-9A-F]{4})")] private static partial Regex DGPUHWIdRegex(); public static Task ExistsAsync() => WMI.ExistsAsync("root\\WMI", $"SELECT * FROM LENOVO_GAMEZONE_DATA"); public static Task IsSupportSmartFanAsync() => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_GAMEZONE_DATA", "IsSupportSmartFan", [], pdc => Convert.ToInt32(pdc["Data"].Value)); public static Task GetSmartFanModeAsync() => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_GAMEZONE_DATA", "GetSmartFanMode", [], pdc => Convert.ToInt32(pdc["Data"].Value)); public static Task SetSmartFanModeAsync(int data) => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_GAMEZONE_DATA", "SetSmartFanMode", new() { { "Data", data } }); public static Task GetIntelligentSubModeAsync() => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_GAMEZONE_DATA", "GetIntelligentSubMode", [], pdc => Convert.ToInt32(pdc["Data"].Value)); public static Task SetIntelligentSubModeAsync(int data) => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_GAMEZONE_DATA", "SetIntelligentSubMode", new() { { "Data", data } }); public static Task IsSupportGSyncAsync() => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_GAMEZONE_DATA", "IsSupportGSync", [], pdc => Convert.ToInt32(pdc["Data"].Value)); public static Task GetGSyncStatusAsync() => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_GAMEZONE_DATA", "GetGSyncStatus", [], pdc => Convert.ToInt32(pdc["Data"].Value)); public static Task SetGSyncStatusAsync(int data) => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_GAMEZONE_DATA", "SetGSyncStatus", new() { { "Data", data } }); public static Task IsSupportIGPUModeAsync() => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_GAMEZONE_DATA", "IsSupportIGPUMode", [], pdc => Convert.ToInt32(pdc["Data"].Value)); public static Task GetIGPUModeStatusAsync() => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_GAMEZONE_DATA", "GetIGPUModeStatus", [], pdc => Convert.ToInt32(pdc["Data"].Value)); public static Task SetIGPUModeStatusAsync(int mode) => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_GAMEZONE_DATA", "SetIGPUModeStatus", new() { { "mode", mode } }); public static Task NotifyDGPUStatusAsync(int status) => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_GAMEZONE_DATA", "NotifyDGPUStatus", new() { { "Status", status } }); public static Task GetDGPUHWIdAsync() => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_GAMEZONE_DATA", "GetDGPUHWId", [], pdc => { var id = pdc["Data"].Value.ToString(); if (id is null) return HardwareId.Empty; try { var matches = DGPUHWIdRegex().Matches(id); if (matches.Count != 2) return HardwareId.Empty; var vendor = matches[0].Groups[1].Value; var device = matches[1].Groups[2].Value; return new HardwareId(vendor, device); } catch { return HardwareId.Empty; } }); public static Task IsSupportGpuOCAsync() => CallAsync("ROOT\\WMI", $"SELECT * FROM LENOVO_GAMEZONE_DATA", "IsSupportGpuOC", [], pdc => Convert.ToInt32(pdc["Data"].Value)); public static Task IsSupportDisableTPAsync() => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_GAMEZONE_DATA", "IsSupportDisableTP", [], pdc => Convert.ToInt32(pdc["Data"].Value)); public static Task GetTPStatusStatusAsync() => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_GAMEZONE_DATA", "GetTPStatus", [], pdc => Convert.ToInt32(pdc["Data"].Value)); public static Task SetTPStatusAsync(int data) => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_GAMEZONE_DATA", "SetTPStatus", new() { { "Data", data } }); public static Task IsSupportDisableWinKeyAsync() => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_GAMEZONE_DATA", "IsSupportDisableWinKey", [], pdc => Convert.ToInt32(pdc["Data"].Value)); public static Task GetWinKeyStatusAsync() => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_GAMEZONE_DATA", "GetWinKeyStatus", [], pdc => Convert.ToInt32(pdc["Data"].Value)); public static Task SetWinKeyStatusAsync(int data) => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_GAMEZONE_DATA", "SetWinKeyStatus", new() { { "Data", data } }); public static Task IsSupportODAsync() => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_GAMEZONE_DATA", "IsSupportOD", [], pdc => Convert.ToInt32(pdc["Data"].Value)); public static Task GetODStatusAsync() => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_GAMEZONE_DATA", "GetODStatus", [], pdc => Convert.ToInt32(pdc["Data"].Value)); public static Task SetODStatusAsync(int data) => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_GAMEZONE_DATA", "SetODStatus", new() { { "Data", data } }); public static Task SetLightControlOwnerAsync(int data) => CallAsync("ROOT\\WMI", $"SELECT * FROM LENOVO_GAMEZONE_DATA", "SetLightControlOwner", new() { { "Data", data } }); public static Task IsACFitForOCAsync() => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_GAMEZONE_DATA", "IsACFitForOC", [], pdc => Convert.ToInt32(pdc["Data"].Value)); public static Task GetPowerChargeModeAsync() => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_GAMEZONE_DATA", "GetPowerChargeMode", [], pdc => Convert.ToInt32(pdc["Data"].Value)); public static Task GetCPUFrequencyAsync() => WMI.CallAsync("root\\WMI", $"SELECT * FROM LENOVO_GAMEZONE_DATA", "GetCPUFrequency", [], pdc => { var value = Convert.ToInt32(pdc["Data"].Value); var low = value & 0xFFFF; var high = value >> 16; return Math.Max(low, high); }); } } ================================================ FILE: LenovoLegionToolkit.Lib/System/Management/WMI.LenovoGameZoneKeyLockStatusEvent.cs ================================================ using System; // ReSharper disable InconsistentNaming // ReSharper disable StringLiteralTypo namespace LenovoLegionToolkit.Lib.System.Management; public static partial class WMI { public static class LenovoGameZoneKeyLockStatusEvent { public static IDisposable Listen(Action handler) => WMI.Listen("root\\WMI", $"SELECT * FROM LENOVO_GAMEZONE_KEYLOCK_STATUS_EVENT", pdc => { var value = Convert.ToInt32(pdc["KeyLockState"].Value); handler(value); }); } } ================================================ FILE: LenovoLegionToolkit.Lib/System/Management/WMI.LenovoGameZoneLightProfileChangeEvent.cs ================================================ using System; // ReSharper disable InconsistentNaming // ReSharper disable StringLiteralTypo namespace LenovoLegionToolkit.Lib.System.Management; public static partial class WMI { public static class LenovoGameZoneLightProfileChangeEvent { public static IDisposable Listen(Action handler) => WMI.Listen("root\\WMI", $"SELECT * FROM LENOVO_GAMEZONE_LIGHT_PROFILE_CHANGE_EVENT", pdc => { var value = Convert.ToInt32(pdc["EventId"].Value); handler(value); }); } } ================================================ FILE: LenovoLegionToolkit.Lib/System/Management/WMI.LenovoGameZoneSmartFanModeEvent.cs ================================================ using System; // ReSharper disable InconsistentNaming // ReSharper disable StringLiteralTypo namespace LenovoLegionToolkit.Lib.System.Management; public static partial class WMI { public static class LenovoGameZoneSmartFanModeEvent { public static IDisposable Listen(Action handler) => WMI.Listen("root\\WMI", $"SELECT * FROM LENOVO_GAMEZONE_SMART_FAN_MODE_EVENT", pdc => { var value = Convert.ToInt32(pdc["mode"].Value); handler(value); }); } } ================================================ FILE: LenovoLegionToolkit.Lib/System/Management/WMI.LenovoGameZoneThermalModeEvent.cs ================================================ using System; // ReSharper disable InconsistentNaming // ReSharper disable StringLiteralTypo namespace LenovoLegionToolkit.Lib.System.Management; public static partial class WMI { public static class LenovoGameZoneThermalModeEvent { public static IDisposable Listen(Action handler) => WMI.Listen("root\\WMI", $"SELECT * FROM LENOVO_GAMEZONE_THERMAL_MODE_EVENT", pdc => { var value = Convert.ToInt32(pdc["mode"].Value); handler(value); }); } } ================================================ FILE: LenovoLegionToolkit.Lib/System/Management/WMI.LenovoGpuMethod.cs ================================================  // ReSharper disable InconsistentNaming // ReSharper disable StringLiteralTypo using System; using System.Threading.Tasks; namespace LenovoLegionToolkit.Lib.System.Management; public static partial class WMI { public static class LenovoGpuMethod { public static Task<(int ctgp, int ppab)> GPUGetDefaultPPABcTGPPowerLimit() => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_GPU_METHOD", "GPU_Get_Default_PPAB_cTGP_PowerLimit", [], pdc => { var ctgp = Convert.ToInt32(pdc["Default_cTGP_Powerlimit"].Value); var ppab = Convert.ToInt32(pdc["Default_PPAB_Powerlimit"].Value); return (ctgp, ppab); }); public static Task<(int value, int min, int max, int step)> GPUGetCTGPPowerLimitAsync() => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_GPU_METHOD", "GPU_Get_cTGP_PowerLimit", [], pdc => { var value = Convert.ToInt32(pdc["Current_cTGP_PowerLimit"].Value); var min = Convert.ToInt32(pdc["Min_cTGP_PowerLimit"].Value); var max = Convert.ToInt32(pdc["Max_cTGP_PowerLimit"].Value); var step = Convert.ToInt32(pdc["step"].Value); return (value, min, max, step); }); public static Task GPUSetCTGPPowerLimitAsync(int value) => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_GPU_METHOD", "GPU_Set_cTGP_PowerLimit", new() { { "value", value } }); public static Task<(int value, int min, int max, int step)> GPUGetPPABPowerLimitAsync() => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_GPU_METHOD", "GPU_Get_PPAB_PowerLimit", [], pdc => { var value = Convert.ToInt32(pdc["CurrentPPAB_PowerLimit"].Value); var min = Convert.ToInt32(pdc["MinPPAB_PowerLimit"].Value); var max = Convert.ToInt32(pdc["MaxPPAB_PowerLimit"].Value); var step = Convert.ToInt32(pdc["step"].Value); return (value, min, max, step); }); public static Task GPUSetPPABPowerLimitAsync(int value) => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_GPU_METHOD", "GPU_Set_PPAB_PowerLimit", new() { { "value", value } }); public static Task<(int value, int min, int max, int step, int defaultValue)> GPUGetTemperatureLimitAsync() => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_GPU_METHOD", "GPU_Get_Temperature_Limit", [], pdc => { var value = Convert.ToInt32(pdc["CurrentTemperatueLimit"].Value); var min = Convert.ToInt32(pdc["MinTemperatueLimit"].Value); var max = Convert.ToInt32(pdc["MaxTemperatueLimit"].Value); var step = Convert.ToInt32(pdc["step"].Value); var defaultValue = Convert.ToInt32(pdc["DefaultTemperatueLimit"].Value); return (value, min, max, step, defaultValue); }); public static Task GPUSetTemperatureLimitAsync(int value) => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_GPU_METHOD", "GPU_Set_Temperature_Limit", new() { { "CurrentTemperatureLimit", value } }); } } ================================================ FILE: LenovoLegionToolkit.Lib/System/Management/WMI.LenovoIntelligentOPList.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; // ReSharper disable InconsistentNaming // ReSharper disable StringLiteralTypo namespace LenovoLegionToolkit.Lib.System.Management; public static partial class WMI { public static class LenovoIntelligentOPList { public static async Task> ReadAsync() { var result = await WMI.ReadAsync("root\\WMI", $"SELECT * FROM LENOVO_INTELLIGENT_OP_LIST", pdc => { var processName = Convert.ToString(pdc["processname"].Value); var mode = Convert.ToInt32(pdc["mode"].Value); return (processName, mode); }).ConfigureAwait(false); return result .OfType<(string, int)>() .DistinctBy(sm => sm.Item1) .ToDictionary(sm => sm.Item1, sm => sm.Item2); } } } ================================================ FILE: LenovoLegionToolkit.Lib/System/Management/WMI.LenovoLightingData.cs ================================================ using System.Threading.Tasks; // ReSharper disable InconsistentNaming // ReSharper disable StringLiteralTypo namespace LenovoLegionToolkit.Lib.System.Management; public static partial class WMI { public static class LenovoLightingData { public static Task ExistsAsync(int lightingId, int controlInterface, int type) => WMI.ExistsAsync("root\\WMI", $"SELECT * FROM LENOVO_LIGHTING_DATA WHERE Lighting_ID = {lightingId} AND Control_Interface = {controlInterface} AND Lighting_Type = {type}"); } } ================================================ FILE: LenovoLegionToolkit.Lib/System/Management/WMI.LenovoLightingEvent.cs ================================================ using System; // ReSharper disable InconsistentNaming // ReSharper disable StringLiteralTypo namespace LenovoLegionToolkit.Lib.System.Management; public static partial class WMI { public static class LenovoLightingEvent { public static IDisposable Listen(Action handler) => WMI.Listen("root\\WMI", $"SELECT * FROM LENOVO_LIGHTING_EVENT", pdc => { var value = Convert.ToInt32(pdc["Key_ID"].Value); handler(value); }); } } ================================================ FILE: LenovoLegionToolkit.Lib/System/Management/WMI.LenovoLightingMethod.cs ================================================ using System; using System.Threading.Tasks; // ReSharper disable InconsistentNaming // ReSharper disable StringLiteralTypo namespace LenovoLegionToolkit.Lib.System.Management; public static partial class WMI { public static class LenovoLightingMethod { public static Task<(int stateType, int level)> GetLightingCurrentStatusAsync(int lightingId) => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_LIGHTING_METHOD", "Get_Lighting_Current_Status", new() { { "Lighting_ID", lightingId } }, pdc => { var stateType = Convert.ToInt32(pdc["Current_State_Type"].Value); var level = Convert.ToInt32(pdc["Current_Brightness_Level"].Value); return (stateType, level); }); public static Task SetLightingCurrentStatusAsync(int lightingId, int stateType, int level) => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_LIGHTING_METHOD", "Set_Lighting_Current_Status", new() { { "Lighting_ID", lightingId }, { "Current_State_Type", stateType }, { "Current_Brightness_Level", level } }); } } ================================================ FILE: LenovoLegionToolkit.Lib/System/Management/WMI.LenovoOtherMethod.cs ================================================ using System; using System.Threading.Tasks; // ReSharper disable IdentifierTypo // ReSharper disable InconsistentNaming // ReSharper disable StringLiteralTypo namespace LenovoLegionToolkit.Lib.System.Management; public static partial class WMI { public static class LenovoOtherMethod { public static Task GetSupportThermalModeAsync() => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_OTHER_METHOD", "GetSupportThermalMode", [], pdc => Convert.ToInt32(pdc["mode"].Value)); public static Task GetSupportLegionZoneVersionAsync() => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_OTHER_METHOD", "Get_Support_LegionZone_Version", [], pdc => Convert.ToInt32(pdc["Version"].Value)); public static Task GetLegionDeviceSupportFeatureAsync() => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_OTHER_METHOD", "Get_Legion_Device_Support_Feature", [], pdc => Convert.ToInt32(pdc["Status"].Value)); public static Task GetDeviceCurrentSupportFeatureAsync() => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_OTHER_METHOD", "Get_Device_Current_Support_Feature", [], pdc => Convert.ToInt32(pdc["Flag"].Value)); public static Task SetDeviceCurrentSupportFeatureAsync(int functionId, int value) => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_OTHER_METHOD", "Set_Device_Current_Support_Feature", new() { { "FunctionID", functionId }, { "value", value } }, pdc => Convert.ToInt32(pdc["ret"].Value)); public static Task SetDGPUDeviceStatusAsync(bool status) => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_OTHER_METHOD", "Set_DGPU_Device_Status", new() { { "Status", status ? 1 : 0 } }); public static Task GetDGPUDeviceDIDVIDAsync() => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_OTHER_METHOD", "Get_DGPU_Device_DIDVID", [], pdc => { var id = Convert.ToInt32(pdc["DGPU_ID"].Value); var vendorId = id & 0xFFFF; var deviceId = id >> 16; return new HardwareId($"{vendorId:X}", $"{deviceId:X}"); }); public static Task GetFeatureValueAsync(CapabilityID id) => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_OTHER_METHOD", "GetFeatureValue", new() { { "IDs", (int)id } }, pdc => Convert.ToInt32(pdc["Value"].Value)); public static Task SetFeatureValueAsync(CapabilityID id, int value) => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_OTHER_METHOD", "SetFeatureValue", new() { { "IDs", (int)id }, { "value", value } }); public static Task GetFeatureValueAsync(uint idRaw) => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_OTHER_METHOD", "GetFeatureValue", new() { { "IDs", idRaw } }, pdc => Convert.ToInt32(pdc["Value"].Value)); public static Task SetFeatureValueAsync(uint idRaw, int value) => CallAsync("root\\WMI", $"SELECT * FROM LENOVO_OTHER_METHOD", "SetFeatureValue", new() { { "IDs", idRaw }, { "value", value } }); } } ================================================ FILE: LenovoLegionToolkit.Lib/System/Management/WMI.LenovoUtilityEvent.cs ================================================ using System; // ReSharper disable InconsistentNaming // ReSharper disable StringLiteralTypo namespace LenovoLegionToolkit.Lib.System.Management; public static partial class WMI { public static class LenovoUtilityEvent { public static IDisposable Listen(Action handler) => WMI.Listen("root\\WMI", $"SELECT * FROM LENOVO_UTILITY_EVENT", pdc => { var value = Convert.ToInt32(pdc["PressTypeDataVal"].Value); handler(value); }); } } ================================================ FILE: LenovoLegionToolkit.Lib/System/Management/WMI.Win32.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Management; using System.Threading.Tasks; // ReSharper disable InconsistentNaming // ReSharper disable StringLiteralTypo namespace LenovoLegionToolkit.Lib.System.Management; public static partial class WMI { public static class Win32 { public static class ProcessStartTrace { public static IDisposable Listen(Action handler) => WMI.Listen("root\\CIMV2", $"SELECT * FROM Win32_ProcessStartTrace", pdc => { var processId = Convert.ToInt32(pdc["ProcessID"].Value); var processName = (string)pdc["ProcessName"].Value; handler(processId, Path.GetFileNameWithoutExtension(processName)); }); } public static class ProcessStopTrace { public static IDisposable Listen(Action handler) => WMI.Listen("root\\CIMV2", $"SELECT * FROM Win32_ProcessStopTrace", pdc => { var processId = Convert.ToInt32(pdc["ProcessID"].Value); var processName = (string)pdc["ProcessName"].Value; handler(processId, Path.GetFileNameWithoutExtension(processName)); }); } public static class ComputerSystemProduct { public static async Task<(string vendor, string name, string version, string identifyingNumber)> ReadAsync() { var result = await WMI.ReadAsync("root\\CIMV2", $"SELECT * FROM Win32_ComputerSystemProduct", pdc => { var vendor = (string)pdc["Vendor"].Value; var name = (string)pdc["Name"].Value; var version = (string)pdc["Version"].Value; var identifyingNumber = (string)pdc["IdentifyingNumber"].Value; return (vendor, name, version, identifyingNumber); }).ConfigureAwait(false); return result.First(); } } public static class Processor { public static async Task GetAddressWidthAsync() { var result = await ReadAsync("root\\CIMV2", $"SELECT * FROM Win32_Processor", pdc => Convert.ToInt32(pdc["AddressWidth"].Value)).ConfigureAwait(false); return result.First(); } } public static class OperatingSystem { public static async Task GetBuildNumberAsync() { var result = await ReadAsync("root\\CIMV2", $"SELECT * FROM Win32_OperatingSystem", pdc => (string)pdc["BuildNumber"].Value).ConfigureAwait(false); return result.First(); } } public static class PnpEntity { public static async Task GetDeviceIDAsync(string pnpDeviceIdPart) { var results = await ReadAsync("root\\CIMV2", $"SELECT * FROM Win32_PnpEntity WHERE DeviceID LIKE '{pnpDeviceIdPart}%'", pdc => (string)pdc["DeviceID"].Value).ConfigureAwait(false); return results.FirstOrDefault(); } } public static class PnpSignedDriver { public static Task> ReadAsync() => WMI.ReadAsync("root\\CIMV2", $"SELECT * FROM Win32_PnPSignedDriver", pdc => { var deviceId = pdc["DeviceID"].Value as string ?? string.Empty; var hardwareId = pdc["HardWareId"].Value as string ?? string.Empty; var driverVersionString = pdc["DriverVersion"].Value as string; var driverDateString = pdc["DriverDate"].Value as string; Version? driverVersion = null; if (Version.TryParse(driverVersionString, out var v)) driverVersion = v; DateTime? driverDate = null; if (driverDateString is not null) driverDate = ManagementDateTimeConverter.ToDateTime(driverDateString).Date; return new DriverInfo(deviceId, hardwareId, driverVersion, driverDate); }); } } } ================================================ FILE: LenovoLegionToolkit.Lib/System/Management/WMI.WmiMonitorBrightnessEvent.cs ================================================ using System; namespace LenovoLegionToolkit.Lib.System.Management; public static partial class WMI { public static class WmiMonitorBrightnessEvent { public static IDisposable Listen(Action handler) => WMI.Listen("root\\WMI", $"SELECT * FROM WmiMonitorBrightnessEvent", pdc => { var value = Convert.ToByte(pdc["Brightness"].Value); handler(value); }); } } ================================================ FILE: LenovoLegionToolkit.Lib/System/Management/WMI.WmiMonitorBrightnessMethods.cs ================================================ using System.Threading.Tasks; // ReSharper disable InconsistentNaming // ReSharper disable StringLiteralTypo namespace LenovoLegionToolkit.Lib.System.Management; public static partial class WMI { public static class WmiMonitorBrightnessMethods { public static Task WmiSetBrightness(int brightness, int timeout) => CallAsync("root\\WMI", $"SELECT * FROM WmiMonitorBrightnessMethods", "WmiSetBrightness", new() { { "Brightness", brightness }, { "Timeout", timeout } }); } } ================================================ FILE: LenovoLegionToolkit.Lib/System/Management/WMI.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Management; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.Lib.System.Management; public static partial class WMI { private static async Task ExistsAsync(string scope, FormattableString query) { try { var queryFormatted = query.ToString(WMIPropertyValueFormatter.Instance); var mos = new ManagementObjectSearcher(scope, queryFormatted); var managementObjects = await mos.GetAsync().ConfigureAwait(false); return managementObjects.Any(); } catch { return false; } } private static LambdaDisposable Listen(string scope, FormattableString query, Action handler) { var queryFormatted = query.ToString(WMIPropertyValueFormatter.Instance); var watcher = new ManagementEventWatcher(scope, queryFormatted); watcher.EventArrived += (_, e) => handler(e.NewEvent.Properties); watcher.Start(); return new LambdaDisposable(() => { watcher.Stop(); watcher.Dispose(); }); } private static async Task> ReadAsync(string scope, FormattableString query, Func converter) { try { var queryFormatted = query.ToString(WMIPropertyValueFormatter.Instance); var mos = new ManagementObjectSearcher(scope, queryFormatted); var managementObjects = await mos.GetAsync().ConfigureAwait(false); var result = managementObjects.Select(mo => mo.Properties).Select(converter); return result; } catch (ManagementException ex) { throw new ManagementException($"Read failed: {ex.Message} [scope={scope}, query={query}]", ex); } } private static async Task CallAsync(string scope, FormattableString query, string methodName, Dictionary methodParams) { try { var queryFormatted = query.ToString(WMIPropertyValueFormatter.Instance); var mos = new ManagementObjectSearcher(scope, queryFormatted); var managementObjects = await mos.GetAsync().ConfigureAwait(false); var managementObject = managementObjects.FirstOrDefault() ?? throw new InvalidOperationException("No results in query"); var mo = (ManagementObject)managementObject; var methodParamsObject = mo.GetMethodParameters(methodName); foreach (var pair in methodParams) methodParamsObject[pair.Key] = pair.Value; mo.InvokeMethod(methodName, methodParamsObject, new InvokeMethodOptions()); } catch (ManagementException ex) { throw new ManagementException($"Call failed: {ex.Message} [scope={scope}, query={query}, methodName={methodName}]", ex); } } private static async Task CallAsync(string scope, FormattableString query, string methodName, Dictionary methodParams, Func converter) { try { var queryFormatted = query.ToString(WMIPropertyValueFormatter.Instance); var mos = new ManagementObjectSearcher(scope, queryFormatted); var managementObjects = await mos.GetAsync().ConfigureAwait(false); var managementObject = managementObjects.FirstOrDefault() ?? throw new InvalidOperationException("No results in query"); var mo = (ManagementObject)managementObject; var methodParamsObject = mo.GetMethodParameters(methodName); foreach (var pair in methodParams) methodParamsObject[pair.Key] = pair.Value; var resultProperties = mo.InvokeMethod(methodName, methodParamsObject, new InvokeMethodOptions()); var result = converter(resultProperties.Properties); return result; } catch (ManagementException ex) { throw new ManagementException($"Call failed: {ex.Message}. [scope={scope}, query={query}, methodName={methodName}]", ex); } } private class WMIPropertyValueFormatter : IFormatProvider, ICustomFormatter { public static readonly WMIPropertyValueFormatter Instance = new(); private WMIPropertyValueFormatter() { } public object GetFormat(Type? formatType) { if (formatType == typeof(ICustomFormatter)) return this; throw new InvalidOperationException("Invalid type of formatted"); } public string Format(string? format, object? arg, IFormatProvider? formatProvider) { var stringArg = arg?.ToString()?.Replace("\\", "\\\\"); return stringArg ?? string.Empty; } } } ================================================ FILE: LenovoLegionToolkit.Lib/System/NVAPI.cs ================================================ using System.Collections.Generic; using System.Linq; using NvAPIWrapper; using NvAPIWrapper.Display; using NvAPIWrapper.GPU; using NvAPIWrapper.Native.Exceptions; using NvAPIWrapper.Native.GPU; namespace LenovoLegionToolkit.Lib.System; internal static class NVAPI { public static void Initialize() => NVIDIA.Initialize(); public static void Unload() => NVIDIA.Unload(); public static PhysicalGPU? GetGPU() { try { return PhysicalGPU.GetPhysicalGPUs().FirstOrDefault(gpu => gpu.SystemType == SystemType.Laptop); } catch (NVIDIAApiException) { return null; } } public static bool IsDisplayConnected(PhysicalGPU gpu) { try { return Display.GetDisplays().Any(d => d.PhysicalGPUs.Contains(gpu, PhysicalGPUEqualityComparer.Instance)); } catch (NVIDIAApiException) { return false; } } public static string? GetGPUId(PhysicalGPU gpu) { try { return gpu.BusInformation.PCIIdentifiers.ToString(); } catch (NVIDIAApiException) { return null; } } private class PhysicalGPUEqualityComparer : IEqualityComparer { public static readonly PhysicalGPUEqualityComparer Instance = new(); private PhysicalGPUEqualityComparer() { } public bool Equals(PhysicalGPU? x, PhysicalGPU? y) => x?.GPUId == y?.GPUId; public int GetHashCode(PhysicalGPU obj) => obj.GPUId.GetHashCode(); } } ================================================ FILE: LenovoLegionToolkit.Lib/System/Power.cs ================================================ using System.Threading.Tasks; using LenovoLegionToolkit.Lib.System.Management; using LenovoLegionToolkit.Lib.Utils; using Windows.Win32; namespace LenovoLegionToolkit.Lib.System; public static class Power { public static async Task IsPowerAdapterConnectedAsync() { if (!PInvoke.GetSystemPowerStatus(out var sps)) return PowerAdapterStatus.Connected; var adapterConnected = sps.ACLineStatus == 1; var acFitForOc = await IsAcFitForOc().ConfigureAwait(false) ?? true; var chargingNormally = await IsChargingNormally().ConfigureAwait(false) ?? true; return (adapterConnected, acFitForOc && chargingNormally) switch { (true, false) => PowerAdapterStatus.ConnectedLowWattage, (true, _) => PowerAdapterStatus.Connected, (false, _) => PowerAdapterStatus.Disconnected, }; } public static bool IsBatterySaverEnabled() { if (!PInvoke.GetSystemPowerStatus(out var sps)) return false; return sps.SystemStatusFlag == 1; } public static async Task RestartAsync() { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Restarting..."); await CMD.RunAsync("shutdown", "/r /t 0").ConfigureAwait(false); } private static async Task IsAcFitForOc() { try { var result = await WMI.LenovoGameZoneData.IsACFitForOCAsync().ConfigureAwait(false); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Mode = {result}"); return result == 1; } catch { return null; } } private static async Task IsChargingNormally() { try { var result = await WMI.LenovoGameZoneData.GetPowerChargeModeAsync().ConfigureAwait(false); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Mode = {result}"); return result == 1; } catch { return null; } } } ================================================ FILE: LenovoLegionToolkit.Lib/System/Registry.cs ================================================ using System; using System.IO; using System.Linq; using System.Management; using System.Security.AccessControl; using System.Security.Principal; using System.Threading; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Utils; using Microsoft.Win32; using Windows.Win32; using Windows.Win32.Foundation; using Windows.Win32.System.Registry; // ReSharper disable StringLiteralTypo namespace LenovoLegionToolkit.Lib.System; public static class Registry { public static IAsyncDisposable ObserveKey(string hive, string subKey, bool includeSubtreeChanges, Action handler) { var cancellationTokenSource = new CancellationTokenSource(); var task = Task.Run(() => Handler(cancellationTokenSource.Token), cancellationTokenSource.Token); return new LambdaAsyncDisposable(async () => { await cancellationTokenSource.CancelAsync().ConfigureAwait(false); await task.ConfigureAwait(false); }); void Handler(CancellationToken token) { try { using var baseKey = GetBaseKey(hive); using var key = baseKey.OpenSubKey(subKey) ?? throw new InvalidOperationException($"Key {subKey} could not be opened"); var resetEvent = new ManualResetEvent(false); while (true) { var regNotifyChangeKeyValueResult = PInvoke.RegNotifyChangeKeyValue(key.Handle, includeSubtreeChanges, REG_NOTIFY_FILTER.REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_FILTER.REG_NOTIFY_THREAD_AGNOSTIC, resetEvent.SafeWaitHandle, true); if (regNotifyChangeKeyValueResult != WIN32_ERROR.NO_ERROR) PInvokeExtensions.ThrowIfWin32Error("RegNotifyChangeKeyValue"); WaitHandle.WaitAny([resetEvent, token.WaitHandle]); token.ThrowIfCancellationRequested(); handler(); resetEvent.Reset(); } } catch (OperationCanceledException) { } catch (ThreadAbortException) { } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Unknown error.", ex); } } } public static IDisposable ObserveValue(string hive, string path, string valueName, Action handler) { if (hive is "HKEY_CURRENT_USER" or "HKCU") hive = WindowsIdentity.GetCurrent().User?.Value ?? throw new InvalidOperationException("Current user value is null"); var pathFormatted = @$"SELECT * FROM RegistryValueChangeEvent WHERE Hive = 'HKEY_USERS' AND KeyPath = '{hive}\\{path.Replace(@"\", @"\\")}' AND ValueName = '{valueName}'"; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Starting listener... [hive={hive}, pathFormatted={pathFormatted}, key={valueName}]"); var watcher = new ManagementEventWatcher(pathFormatted); watcher.EventArrived += (_, e) => { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Event arrived [classPath={e.NewEvent.ClassPath}, hive={hive}, pathFormatted={pathFormatted}, key={valueName}]"); handler(); }; watcher.Start(); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Started listener [hive={hive}, pathFormatted={pathFormatted}, key={valueName}]"); return watcher; } public static bool KeyExists(string hive, string subKey) { try { using var baseKey = GetBaseKey(hive); using var registryKey = baseKey.OpenSubKey(subKey); return registryKey is not null; } catch { return false; } } public static bool ValueExists(string hive, string subKey, string valueName) { try { var keyName = Path.Combine(hive, subKey); var value = Microsoft.Win32.Registry.GetValue(keyName, valueName, null); return value is not null; } catch { return false; } } public static string[] GetSubKeys(string hive, string subKey) { using var baseKey = GetBaseKey(hive); return baseKey.OpenSubKey(subKey)?.GetSubKeyNames().Select(s => Path.Combine(subKey, s)).ToArray() ?? []; } public static T GetValue(string hive, string subKey, string valueName, T defaultValue, bool doNotExpand = false) { using var baseKey = GetBaseKey(hive); var value = baseKey.OpenSubKey(subKey)?.GetValue(valueName, defaultValue, doNotExpand ? RegistryValueOptions.DoNotExpandEnvironmentNames : RegistryValueOptions.None); if (value is not T t) return defaultValue; return t; } public static void SetValue(string hive, string subKey, string valueName, T value, bool fixPermissions = false, RegistryValueKind valueKind = RegistryValueKind.Unknown) where T : notnull { try { Microsoft.Win32.Registry.SetValue(@$"{hive}\{subKey}", valueName, value, valueKind); } catch (UnauthorizedAccessException) { if (fixPermissions && AddPermissions(hive, subKey)) SetValue(hive, subKey, valueName, value, false, valueKind); else throw; } } public static void Delete(string hive, string subKey) { using var baseKey = GetBaseKey(hive); using var key = baseKey.OpenSubKey(subKey); if (key is null) return; baseKey.DeleteSubKeyTree(subKey); } private static bool AddPermissions(string hive, string subKey) { IdentityReference? originalOwner = null; try { var current = WindowsIdentity.GetCurrent(); // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract if (current is null) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Could not get current user."); return false; } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Attempting to add permissions to {hive}\\{subKey} for {current.Name}..."); var user = current.User; if (user is null) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Could not get current security identifier of user {current.Name}."); return false; } if (!TakeOwnership(hive, subKey, user, out originalOwner)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Could not take ownership of {hive}\\{subKey}. [user={user}, originalOwner={originalOwner}]"); return false; } using var baseKey = GetBaseKey(hive); using var key = baseKey.OpenSubKey(subKey, RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.ChangePermissions | RegistryRights.ReadKey); if (key is null) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to open key {hive}\\{subKey} for {current.Name}."); return false; } var accessControl = key.GetAccessControl(); const RegistryRights rights = RegistryRights.FullControl; const AccessControlType type = AccessControlType.Allow; accessControl.AddAccessRule(new(user, rights, type)); key.SetAccessControl(accessControl); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Permissions added on {hive}\\{subKey} for {current.Name}. [rights={rights}, type={type}]"); return true; } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to add permissions for {hive}\\{subKey}.", ex); throw; } finally { if (originalOwner is not null) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Restoring ownership of {hive}\\{subKey} to {originalOwner}..."); if (TakeOwnership(hive, subKey, originalOwner, out _)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Ownership of {hive}\\{subKey} restored to {originalOwner}."); } else { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Ownership of {hive}\\{subKey} NOT restored {originalOwner}."); } } } } private static bool TakeOwnership(string hive, string subKey, IdentityReference reference, out IdentityReference? previousIdentityReference) { previousIdentityReference = null; try { if (!TokenManipulator.AddPrivileges(TokenManipulator.SE_BACKUP_PRIVILEGE, TokenManipulator.SE_RESTORE_PRIVILEGE, TokenManipulator.SE_TAKE_OWNERSHIP_PRIVILEGE)) return false; using var baseKey = GetBaseKey(hive); using var key = baseKey.OpenSubKey(subKey, RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.TakeOwnership); if (key is null) return false; var accessControl = key.GetAccessControl(); previousIdentityReference = accessControl.GetOwner(typeof(NTAccount)); if (previousIdentityReference is null) return false; accessControl.SetOwner(reference); key.SetAccessControl(accessControl); return true; } finally { _ = TokenManipulator.RemovePrivileges(TokenManipulator.SE_BACKUP_PRIVILEGE, TokenManipulator.SE_RESTORE_PRIVILEGE, TokenManipulator.SE_TAKE_OWNERSHIP_PRIVILEGE); } } private static RegistryKey GetBaseKey(string hive) => hive switch { "HKLM" or "HKEY_LOCAL_MACHINE" => Microsoft.Win32.Registry.LocalMachine, "HKCU" or "HKEY_CURRENT_USER" => Microsoft.Win32.Registry.CurrentUser, "HKU" or "HKEY_USERS" => Microsoft.Win32.Registry.Users, "HKCR" or "HKEY_CLASSES_ROOT " => Microsoft.Win32.Registry.ClassesRoot, "HKCC" or "HKEY_CURRENT_CONFIG " => Microsoft.Win32.Registry.CurrentConfig, _ => throw new ArgumentException(@"Unknown hive.", nameof(hive)) }; } ================================================ FILE: LenovoLegionToolkit.Lib/System/SystemPath.cs ================================================ using System; using System.Linq; using LenovoLegionToolkit.Lib.Utils; using Microsoft.Win32; using Windows.Win32; using Windows.Win32.Foundation; namespace LenovoLegionToolkit.Lib.System; public static class SystemPath { private static string CLIPath => Folders.Program; public static bool HasCLI() { return Registry.GetValue("HKEY_CURRENT_USER", "Environment", "PATH", string.Empty, true) .Split(';') .Contains(CLIPath); } public static void SetCLI(bool enabled) { var value = Registry.GetValue("HKEY_CURRENT_USER", "Environment", "PATH", string.Empty, true) .Split(';') .ToList(); if (enabled) { if (value.Contains(CLIPath)) return; value.Add(CLIPath); } else { value.Remove(CLIPath); } Registry.SetValue("HKEY_CURRENT_USER", "Environment", "PATH", string.Join(';', value), valueKind: RegistryValueKind.ExpandString); Notify(); } private static unsafe void Notify() { const string environment = "Environment"; fixed (void* ptr = environment) { PInvoke.SendNotifyMessage(HWND.HWND_BROADCAST, PInvoke.WM_SETTINGCHANGE, 0, new IntPtr(ptr)); } } } ================================================ FILE: LenovoLegionToolkit.Lib/System/SystemTheme.cs ================================================ using System; using System.ComponentModel; using System.Runtime.InteropServices; namespace LenovoLegionToolkit.Lib.System; public static partial class SystemTheme { private const string REGISTRY_HIVE = "HKEY_CURRENT_USER"; private const string PERSONALIZE_REGISTRY_PATH = @"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"; private const string APPS_USE_LIGHT_THEME_REGISTRY_KEY = "AppsUseLightTheme"; private const string DWM_REGISTRY_PATH = @"Software\Microsoft\Windows\DWM"; private const string DWM_COLORIZATION_COLOR_REGISTRY_KEY = "ColorizationColor"; public static bool IsDarkMode() { var registryValue = Registry.GetValue(REGISTRY_HIVE, PERSONALIZE_REGISTRY_PATH, APPS_USE_LIGHT_THEME_REGISTRY_KEY, -1); if (registryValue == -1) throw new InvalidOperationException($"Couldn't read the {APPS_USE_LIGHT_THEME_REGISTRY_KEY} setting"); return registryValue == 0; } public static RGBColor GetColorizationColor() { var registryValue = Registry.GetValue(REGISTRY_HIVE, DWM_REGISTRY_PATH, DWM_COLORIZATION_COLOR_REGISTRY_KEY, -1); if (registryValue == -1) throw new InvalidOperationException($"Couldn't read the {DWM_COLORIZATION_COLOR_REGISTRY_KEY} setting"); var bytes = BitConverter.GetBytes(registryValue); return new(bytes[2], bytes[1], bytes[0]); } public static RGBColor GetAccentColor() { var colorName = IsDarkMode() ? "SystemAccentLight2" : "SystemAccentDark1"; return GetUxThemeImmersiveColor(colorName); } private static RGBColor GetUxThemeImmersiveColor(string name) { var colorType = GetImmersiveColorTypeFromName("Immersive" + name); if (colorType == 0xFFFFFFFF) throw new Win32Exception($"Couldn't get color \"{name}\""); var activeColorSet = GetImmersiveUserColorSetPreference(false, false); var nativeColor = GetImmersiveColorFromColorSetEx(activeColorSet, colorType, false, 0); var r = (byte)((0x000000FF & nativeColor) >> 0); var g = (byte)((0x0000FF00 & nativeColor) >> 8); var b = (byte)((0x00FF0000 & nativeColor) >> 16); return new(r, g, b); } internal static IDisposable GetDarkModeListener(Action callback) { return Registry.ObserveValue(REGISTRY_HIVE, PERSONALIZE_REGISTRY_PATH, APPS_USE_LIGHT_THEME_REGISTRY_KEY, callback); } internal static IDisposable GetColorizationColorListener(Action callback) { return Registry.ObserveValue(REGISTRY_HIVE, DWM_REGISTRY_PATH, DWM_COLORIZATION_COLOR_REGISTRY_KEY, callback); } #region Native // ReSharper disable StringLiteralTypo [LibraryImport("uxtheme.dll", EntryPoint = "#95A")] private static partial uint GetImmersiveColorFromColorSetEx(uint immersiveColorSet, uint immersiveColorType, [MarshalAs(UnmanagedType.Bool)] bool ignoreHighContrast, uint highContrastCacheMode); [LibraryImport("uxtheme.dll", EntryPoint = "#96W", StringMarshalling = StringMarshalling.Utf16)] private static partial uint GetImmersiveColorTypeFromName(string name); [LibraryImport("uxtheme.dll", EntryPoint = "#98A")] private static partial uint GetImmersiveUserColorSetPreference([MarshalAs(UnmanagedType.Bool)] bool forceCheckRegistry, [MarshalAs(UnmanagedType.Bool)] bool skipCheckOnFail); // ReSharper restore StringLiteralTypo #endregion } ================================================ FILE: LenovoLegionToolkit.Lib/System/WiFi.cs ================================================ using System; using System.Linq; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Utils; using ManagedNativeWifi; namespace LenovoLegionToolkit.Lib.System; public static class WiFi { public static void TurnOn() { try { NativeWifi.EnumerateInterfaces() .ForEach(i => NativeWifi.TurnOnInterfaceRadio(i.Id)); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to turn on WiFi.", ex); } } public static void TurnOff() { try { NativeWifi.EnumerateInterfaces() .ForEach(i => NativeWifi.TurnOffInterfaceRadio(i.Id)); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to turn off WiFi.", ex); } } public static string? GetConnectedNetworkSsid() { return NativeWifi.EnumerateConnectedNetworkSsids() .Select(c => c.ToString()) .FirstOrDefault(); } } ================================================ FILE: LenovoLegionToolkit.Lib/Utils/Compatibility.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text.RegularExpressions; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.System; using LenovoLegionToolkit.Lib.System.Management; using Windows.Win32; using Windows.Win32.Foundation; using Windows.Win32.System.Power; // ReSharper disable StringLiteralTypo namespace LenovoLegionToolkit.Lib.Utils; public static partial class Compatibility { [GeneratedRegex("^[A-Z0-9]{4}")] private static partial Regex BiosPrefixRegex(); [GeneratedRegex("[0-9]{2}")] private static partial Regex BiosVersionRegex(); private const string ALLOWED_VENDOR = "LENOVO"; private static readonly string[] AllowedModelsPrefix = [ // Worldwide variants "17ACH", "17ARH", "17ITH", "17IMH", "16ACH", "16AHP", "16APH", "16ARH", "16ARP", "16ARX", "16IAH", "16IAX", "16IRH", "16IRX", "16ITH", "15ACH", "15AHP", "15APH", "15ARH", "15ARP", "15IAH", "15IAX", "15IHU", "15IMH", "15IRH", "15ITH", "14APH", "14IRP", // Chinese variants "G5000", "R9000", "R7000", "Y9000", "Y7000", // Limited compatibility "17IR", "15IR", "15IC", "15IK" ]; private static MachineInformation? _machineInformation; public static Task CheckBasicCompatibilityAsync() => WMI.LenovoGameZoneData.ExistsAsync(); public static async Task<(bool isCompatible, MachineInformation machineInformation)> IsCompatibleAsync() { var mi = await GetMachineInformationAsync().ConfigureAwait(false); if (!await CheckBasicCompatibilityAsync().ConfigureAwait(false)) return (false, mi); if (!mi.Vendor.Equals(ALLOWED_VENDOR, StringComparison.InvariantCultureIgnoreCase)) return (false, mi); foreach (var allowedModel in AllowedModelsPrefix) if (mi.Model.Contains(allowedModel, StringComparison.InvariantCultureIgnoreCase)) return (true, mi); return (false, mi); } public static async Task GetMachineInformationAsync() { if (_machineInformation.HasValue) return _machineInformation.Value; var (vendor, machineType, model, serialNumber) = await GetModelDataAsync().ConfigureAwait(false); var (biosVersion, biosVersionRaw) = GetBIOSVersion(); var supportedPowerModes = (await GetSupportedPowerModesAsync().ConfigureAwait(false)).ToArray(); var smartFanVersion = await GetSmartFanVersionAsync().ConfigureAwait(false); var legionZoneVersion = await GetLegionZoneVersionAsync().ConfigureAwait(false); var features = await GetFeaturesAsync().ConfigureAwait(false); var machineInformation = new MachineInformation { Vendor = vendor, MachineType = machineType, Model = model, SerialNumber = serialNumber, BiosVersion = biosVersion, BiosVersionRaw = biosVersionRaw, SupportedPowerModes = supportedPowerModes, SmartFanVersion = smartFanVersion, LegionZoneVersion = legionZoneVersion, Features = features, Properties = new() { SupportsAlwaysOnAc = GetAlwaysOnAcStatus(), SupportsGodModeV1 = GetSupportsGodModeV1(supportedPowerModes, smartFanVersion, legionZoneVersion, biosVersion), SupportsGodModeV2 = GetSupportsGodModeV2(supportedPowerModes, smartFanVersion, legionZoneVersion), SupportsGSync = await GetSupportsGSyncAsync().ConfigureAwait(false), SupportsIGPUMode = await GetSupportsIGPUModeAsync().ConfigureAwait(false), SupportsAIMode = await GetSupportsAIModeAsync().ConfigureAwait(false), SupportBootLogoChange = GetSupportBootLogoChange(smartFanVersion), HasQuietToPerformanceModeSwitchingBug = GetHasQuietToPerformanceModeSwitchingBug(biosVersion), HasGodModeToOtherModeSwitchingBug = GetHasGodModeToOtherModeSwitchingBug(biosVersion), IsExcludedFromLenovoLighting = GetIsExcludedFromLenovoLighting(biosVersion), IsExcludedFromPanelLogoLenovoLighting = GetIsExcludedFromPanelLenovoLighting(machineType, model), HasAlternativeFullSpectrumLayout = GetHasAlternativeFullSpectrumLayout(machineType), } }; if (Log.Instance.IsTraceEnabled) { Log.Instance.Trace($"Retrieved machine information:"); Log.Instance.Trace($" * Vendor: '{machineInformation.Vendor}'"); Log.Instance.Trace($" * Machine Type: '{machineInformation.MachineType}'"); Log.Instance.Trace($" * Model: '{machineInformation.Model}'"); Log.Instance.Trace($" * BIOS: '{machineInformation.BiosVersion}' [{machineInformation.BiosVersionRaw}]"); Log.Instance.Trace($" * SupportedPowerModes: '{string.Join(",", machineInformation.SupportedPowerModes)}'"); Log.Instance.Trace($" * SmartFanVersion: '{machineInformation.SmartFanVersion}'"); Log.Instance.Trace($" * LegionZoneVersion: '{machineInformation.LegionZoneVersion}'"); Log.Instance.Trace($" * Features: {machineInformation.Features.Source}:{string.Join(',', machineInformation.Features.All)}"); Log.Instance.Trace($" * Properties:"); Log.Instance.Trace($" * SupportsAlwaysOnAc: '{machineInformation.Properties.SupportsAlwaysOnAc.status}, {machineInformation.Properties.SupportsAlwaysOnAc.connectivity}'"); Log.Instance.Trace($" * SupportsGodModeV1: '{machineInformation.Properties.SupportsGodModeV1}'"); Log.Instance.Trace($" * SupportsGodModeV2: '{machineInformation.Properties.SupportsGodModeV2}'"); Log.Instance.Trace($" * SupportsGSync: '{machineInformation.Properties.SupportsGSync}'"); Log.Instance.Trace($" * SupportsIGPUMode: '{machineInformation.Properties.SupportsIGPUMode}'"); Log.Instance.Trace($" * SupportsAIMode: '{machineInformation.Properties.SupportsAIMode}'"); Log.Instance.Trace($" * SupportBootLogoChange: '{machineInformation.Properties.SupportBootLogoChange}'"); Log.Instance.Trace($" * HasQuietToPerformanceModeSwitchingBug: '{machineInformation.Properties.HasQuietToPerformanceModeSwitchingBug}'"); Log.Instance.Trace($" * HasGodModeToOtherModeSwitchingBug: '{machineInformation.Properties.HasGodModeToOtherModeSwitchingBug}'"); Log.Instance.Trace($" * IsExcludedFromLenovoLighting: '{machineInformation.Properties.IsExcludedFromLenovoLighting}'"); Log.Instance.Trace($" * IsExcludedFromPanelLogoLenovoLighting: '{machineInformation.Properties.IsExcludedFromPanelLogoLenovoLighting}'"); Log.Instance.Trace($" * HasAlternativeFullSpectrumLayout: '{machineInformation.Properties.HasAlternativeFullSpectrumLayout}'"); } return (_machineInformation = machineInformation).Value; } private static Task<(string, string, string, string)> GetModelDataAsync() => WMI.Win32.ComputerSystemProduct.ReadAsync(); private static (BiosVersion?, string?) GetBIOSVersion() { var result = Registry.GetValue("HKEY_LOCAL_MACHINE", "HARDWARE\\DESCRIPTION\\System\\BIOS", "BIOSVersion", string.Empty).Trim(); var prefixRegex = BiosPrefixRegex(); var versionRegex = BiosVersionRegex(); var prefix = prefixRegex.Match(result).Value; var versionString = versionRegex.Match(result).Value; if (!int.TryParse(versionRegex.Match(versionString).Value, out var version)) return (null, null); return (new(prefix, version), result); } private static async Task GetFeaturesAsync() { try { var capabilities = await WMI.LenovoCapabilityData00.ReadAsync().ConfigureAwait(false); return new(MachineInformation.FeatureData.SourceType.CapabilityData, capabilities); } catch { /* Ignored. */ } try { var featureFlags = await WMI.LenovoOtherMethod.GetLegionDeviceSupportFeatureAsync().ConfigureAwait(false); return new(MachineInformation.FeatureData.SourceType.Flags) { [CapabilityID.IGPUMode] = featureFlags.IsBitSet(0), [CapabilityID.NvidiaGPUDynamicDisplaySwitching] = featureFlags.IsBitSet(4), [CapabilityID.InstantBootAc] = featureFlags.IsBitSet(5), [CapabilityID.InstantBootUsbPowerDelivery] = featureFlags.IsBitSet(6), [CapabilityID.AMDSmartShiftMode] = featureFlags.IsBitSet(7), [CapabilityID.AMDSkinTemperatureTracking] = featureFlags.IsBitSet(8), [CapabilityID.FlipToStart] = true, [CapabilityID.OverDrive] = true }; } catch { /* Ignored. */ } return MachineInformation.FeatureData.Unknown; } private static async Task> GetSupportedPowerModesAsync() { try { var powerModes = new List(); var value = await WMI.LenovoOtherMethod.GetFeatureValueAsync(CapabilityID.SupportedPowerModes).ConfigureAwait(false); if (value.IsBitSet(0)) powerModes.Add(PowerModeState.Quiet); if (value.IsBitSet(1)) powerModes.Add(PowerModeState.Balance); if (value.IsBitSet(2)) powerModes.Add(PowerModeState.Performance); if (value.IsBitSet(16)) powerModes.Add(PowerModeState.GodMode); return powerModes; } catch { /* Ignored. */ } try { var powerModes = new List(); var result = await WMI.LenovoOtherMethod.GetSupportThermalModeAsync().ConfigureAwait(false); if (result.IsBitSet(0)) powerModes.Add(PowerModeState.Quiet); if (result.IsBitSet(1)) powerModes.Add(PowerModeState.Balance); if (result.IsBitSet(2)) powerModes.Add(PowerModeState.Performance); if (result.IsBitSet(16)) powerModes.Add(PowerModeState.GodMode); return powerModes; } catch { /* Ignored. */ } return []; } private static async Task GetSmartFanVersionAsync() { try { return await WMI.LenovoGameZoneData.IsSupportSmartFanAsync().ConfigureAwait(false); } catch { /* Ignored. */ } return -1; } private static async Task GetLegionZoneVersionAsync() { try { return await WMI.LenovoOtherMethod.GetFeatureValueAsync(CapabilityID.LegionZoneSupportVersion).ConfigureAwait(false); } catch { /* Ignored. */ } try { return await WMI.LenovoOtherMethod.GetSupportLegionZoneVersionAsync().ConfigureAwait(false); } catch { /* Ignored. */ } return -1; } private static unsafe (bool status, bool connectivity) GetAlwaysOnAcStatus() { var capabilities = new SYSTEM_POWER_CAPABILITIES(); var result = PInvoke.CallNtPowerInformation(POWER_INFORMATION_LEVEL.SystemPowerCapabilities, null, 0, &capabilities, (uint)Marshal.SizeOf()); if (result.SeverityCode == NTSTATUS.Severity.Success) return (false, false); return (capabilities.AoAc, capabilities.AoAcConnectivitySupported); } private static bool GetSupportsGodModeV1(IEnumerable supportedPowerModes, int smartFanVersion, int legionZoneVersion, BiosVersion? biosVersion) { if (!supportedPowerModes.Contains(PowerModeState.GodMode)) return false; var affectedBiosVersions = new BiosVersion[] { new("G9CN", 24), new("GKCN", 46), new("H1CN", 39), new("HACN", 31), new("HHCN", 20) }; if (affectedBiosVersions.Any(bv => biosVersion?.IsLowerThan(bv) ?? false)) return false; return smartFanVersion is 4 or 5 || legionZoneVersion is 1 or 2; } private static bool GetSupportsGodModeV2(IEnumerable supportedPowerModes, int smartFanVersion, int legionZoneVersion) { if (!supportedPowerModes.Contains(PowerModeState.GodMode)) return false; return smartFanVersion is 6 or 7 || legionZoneVersion is 3 or 4; } private static async Task GetSupportsGSyncAsync() { try { return await WMI.LenovoGameZoneData.IsSupportGSyncAsync().ConfigureAwait(false) > 0; } catch { return false; } } private static async Task GetSupportsIGPUModeAsync() { try { return await WMI.LenovoGameZoneData.IsSupportIGPUModeAsync().ConfigureAwait(false) > 0; } catch { return false; } } private static async Task GetSupportsAIModeAsync() { try { await WMI.LenovoGameZoneData.GetIntelligentSubModeAsync().ConfigureAwait(false); return true; } catch { return false; } } private static bool GetSupportBootLogoChange(int smartFanVersion) => smartFanVersion < 8; private static bool GetHasQuietToPerformanceModeSwitchingBug(BiosVersion? biosVersion) { var affectedBiosVersions = new BiosVersion[] { new("J2CN", null) }; return affectedBiosVersions.Any(bv => biosVersion?.IsHigherOrEqualThan(bv) ?? false); } private static bool GetHasGodModeToOtherModeSwitchingBug(BiosVersion? biosVersion) { var affectedBiosVersions = new BiosVersion[] { new("K1CN", null) }; return affectedBiosVersions.Any(bv => biosVersion?.IsHigherOrEqualThan(bv) ?? false); } private static bool GetIsExcludedFromLenovoLighting(BiosVersion? biosVersion) { var affectedBiosVersions = new BiosVersion[] { new("GKCN", 54) }; return affectedBiosVersions.Any(bv => biosVersion?.IsLowerThan(bv) ?? false); } private static bool GetIsExcludedFromPanelLenovoLighting(string machineType, string model) { (string machineType, string model)[] excludedModels = [ ("82JH", "15ITH6H"), ("82JK", "15ITH6"), ("82JM", "17ITH6H"), ("82JN", "17ITH6"), ("82JU", "15ACH6H"), ("82JW", "15ACH6"), ("82JY", "17ACH6H"), ("82K0", "17ACH6"), ("82K1", "15IHU6"), ("82K2", "15ACH6"), ("82NW", "15ACH6A") ]; return excludedModels.Where(m => { var result = machineType.Contains(m.machineType); result &= model.Contains(m.model); return result; }).Any(); } private static bool GetHasAlternativeFullSpectrumLayout(string machineType) { var machineTypes = new[] { "83G0", // Gen 9 "83AG" // Gen 8 }; return machineTypes.Contains(machineType); } } ================================================ FILE: LenovoLegionToolkit.Lib/Utils/Crc32Adler.cs ================================================ using System.Collections.Generic; using System.IO; using System.Linq; namespace LenovoLegionToolkit.Lib.Utils; public static class Crc32Adler { private static readonly uint[] LookUpTable = [ 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D ]; public static uint Calculate(string path, int length = 512) { var buffer = new byte[length]; using var fs = File.Open(path, FileMode.Open); _ = fs.Read(buffer); return Calculate(buffer); } private static uint Calculate(IEnumerable data) => ~data.Aggregate(0xFFFFFFFF, (current, d) => LookUpTable[(current ^ d) & 0xFF] ^ (current >> 8)); } ================================================ FILE: LenovoLegionToolkit.Lib/Utils/Folders.cs ================================================ using System; using System.IO; namespace LenovoLegionToolkit.Lib.Utils; public static class Folders { public static string Program => AppDomain.CurrentDomain.SetupInformation.ApplicationBase ?? string.Empty; public static string AppData { get { var appData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); var folderPath = Path.Combine(appData, "LenovoLegionToolkit"); Directory.CreateDirectory(folderPath); return folderPath; } } public static string Temp { get { var appData = Path.GetTempPath(); var folderPath = Path.Combine(appData, "LenovoLegionToolkit"); Directory.CreateDirectory(folderPath); return folderPath; } } } ================================================ FILE: LenovoLegionToolkit.Lib/Utils/IMainThreadDispatcher.cs ================================================ using System; using System.Threading.Tasks; namespace LenovoLegionToolkit.Lib.Utils; public interface IMainThreadDispatcher { void Dispatch(Action callback); Task DispatchAsync(Func callback); } ================================================ FILE: LenovoLegionToolkit.Lib/Utils/LambdaAsyncDisposable.cs ================================================ using System; using System.Threading.Tasks; namespace LenovoLegionToolkit.Lib.Utils; public class LambdaAsyncDisposable(Func action) : IAsyncDisposable { public async ValueTask DisposeAsync() { GC.SuppressFinalize(this); await action().ConfigureAwait(false); } } ================================================ FILE: LenovoLegionToolkit.Lib/Utils/LambdaDisposable.cs ================================================ using System; namespace LenovoLegionToolkit.Lib.Utils; public class LambdaDisposable(Action action) : IDisposable { public void Dispose() { GC.SuppressFinalize(this); action(); } } ================================================ FILE: LenovoLegionToolkit.Lib/Utils/Log.cs ================================================ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Runtime.CompilerServices; using System.Text; namespace LenovoLegionToolkit.Lib.Utils; public class Log { private static Log? _instance; public static Log Instance { get { _instance ??= new Log(); return _instance; } } private readonly object _lock = new(); private readonly string _folderPath; private readonly string _logPath; public bool IsTraceEnabled { get; set; } public string LogPath => _logPath; private Log() { _folderPath = Path.Combine(Folders.AppData, "log"); Directory.CreateDirectory(_folderPath); _logPath = Path.Combine(_folderPath, $"log_{DateTime.UtcNow:yyyy_MM_dd_HH_mm_ss}.txt"); } public void ErrorReport(string header, Exception ex) { var errorReportPath = Path.Combine(_folderPath, $"error_{DateTime.UtcNow:yyyy_MM_dd_HH_mm_ss}.txt"); File.AppendAllLines(errorReportPath, [header, Serialize(ex)]); } public void Trace(FormattableString message, Exception? ex = null, [CallerFilePath] string? file = null, [CallerLineNumber] int lineNumber = -1, [CallerMemberName] string? caller = null) { if (!IsTraceEnabled) return; LogInternal(_logPath, message, ex, file, lineNumber, caller); } private void LogInternal(string path, FormattableString message, Exception? ex, string? file, int lineNumber, string? caller) { lock (_lock) { var lines = new List { $"[{DateTime.UtcNow:dd/MM/yyyy HH:mm:ss.fff}] [{Environment.CurrentManagedThreadId}] [{Path.GetFileName(file)}#{lineNumber}:{caller}] {message}" }; if (ex is not null) lines.Add(Serialize(ex)); #if DEBUG foreach (var line in lines) Debug.WriteLine(line); #endif File.AppendAllLines(path, lines); } } private static string Serialize(Exception ex) => new StringBuilder() .AppendLine("=== Exception ===") .AppendLine(ex.ToString()) .AppendLine() .AppendLine("=== Exception demystified ===") .AppendLine(ex.ToStringDemystified()) .ToString(); } ================================================ FILE: LenovoLegionToolkit.Lib/Utils/NullSafeHandle.cs ================================================ using System; using System.Runtime.InteropServices; namespace LenovoLegionToolkit.Lib.Utils; public sealed class NullSafeHandle() : SafeHandle(IntPtr.Zero, true) { public static readonly NullSafeHandle Null = new(); public override bool IsInvalid => false; protected override bool ReleaseHandle() => true; } ================================================ FILE: LenovoLegionToolkit.Lib/Utils/RetryHelper.cs ================================================ using System; using System.Runtime.CompilerServices; using System.Threading.Tasks; namespace LenovoLegionToolkit.Lib.Utils; public class MaximumRetriesReachedException : Exception; public static class RetryHelper { public static async Task RetryAsync(Func action, int? maximumRetries = null, TimeSpan? timeout = null, Func? matchingException = null, [CallerMemberName] string? tag = null) { maximumRetries ??= 3; timeout ??= TimeSpan.Zero; matchingException ??= (_) => true; var retries = 0; var success = false; while (!success) { try { if (retries >= maximumRetries) throw new MaximumRetriesReachedException(); await action().ConfigureAwait(false); success = true; } catch (Exception ex) { if (!matchingException(ex)) throw; retries++; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Retrying {retries}/{maximumRetries}... [tag={tag}]"); await Task.Delay(timeout.Value).ConfigureAwait(false); } } } } ================================================ FILE: LenovoLegionToolkit.Lib/Utils/SafePerformanceCounter.cs ================================================ using System; using System.Diagnostics; namespace LenovoLegionToolkit.Lib.Utils; public class SafePerformanceCounter(string categoryName, string counterName, string instanceName) { private PerformanceCounter? _performanceCounter; public float NextValue() { try { TryCreateIfNeeded(); return _performanceCounter?.NextValue() ?? 0f; } catch { return 0f; } } public void Reset() { _performanceCounter = null; _ = NextValue(); } private void TryCreateIfNeeded() { if (_performanceCounter is not null) return; try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Creating performance counter. [categoryName={categoryName}, counterName={counterName}, instanceName={instanceName}]"); _performanceCounter = new(categoryName, counterName, instanceName); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to create performance counter. [categoryName={categoryName}, counterName={counterName}, instanceName={instanceName}]", ex); _performanceCounter = null; } } } ================================================ FILE: LenovoLegionToolkit.Lib/Utils/StructSafeHandle.cs ================================================ using System; using System.Runtime.InteropServices; namespace LenovoLegionToolkit.Lib.Utils; public sealed class StructSafeHandle : SafeHandle where T : struct { private readonly IntPtr _ptr; public StructSafeHandle(T str) : base(IntPtr.Zero, true) { var size = Marshal.SizeOf(typeof(T)); var ptr = Marshal.AllocHGlobal(size); Marshal.StructureToPtr(str, ptr, false); SetHandle(ptr); _ptr = ptr; } public override bool IsInvalid => handle == IntPtr.Zero; protected override bool ReleaseHandle() { Marshal.FreeHGlobal(_ptr); return true; } } ================================================ FILE: LenovoLegionToolkit.Lib/Utils/SunriseSunset.cs ================================================ using System; using System.Text.Json.Nodes; using System.Threading; using System.Threading.Tasks; using CoordinateSharp; using LenovoLegionToolkit.Lib.Settings; namespace LenovoLegionToolkit.Lib.Utils; public class SunriseSunset(SunriseSunsetSettings settings, HttpClientFactory httpClientFactory) { public async Task<(Time?, Time?)> GetSunriseSunsetAsync(CancellationToken token = default) { var (sunrise, sunset) = (settings.Store.Sunrise, settings.Store.Sunset); if (settings.Store.LastCheckDateTime == DateTime.Today && sunrise is not null && sunset is not null) return (sunrise, sunset); var coordinate = await GetGeoLocationAsync(token).ConfigureAwait(false); if (coordinate is null) return (null, null); (sunrise, sunset) = CalculateSunriseSunset(coordinate); settings.Store.LastCheckDateTime = DateTime.UtcNow; settings.Store.Sunrise = sunrise; settings.Store.Sunset = sunset; settings.SynchronizeStore(); return (sunrise, sunset); } private async Task GetGeoLocationAsync(CancellationToken token) { try { using var httpClient = httpClientFactory.Create(); var responseJson = await httpClient.GetStringAsync("http://ip-api.com/json?fields=lat,lon", token).ConfigureAwait(false); var responseJsonNode = JsonNode.Parse(responseJson); if (responseJsonNode is not null && double.TryParse(responseJsonNode["lat"]?.ToString(), out var lat) && double.TryParse(responseJsonNode["lon"]?.ToString(), out var lon)) return new Coordinate(lat, lon, DateTime.UtcNow); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to get geolocation.", ex); } return null; } private static (Time?, Time?) CalculateSunriseSunset(Coordinate coordinate) { var sunrise = coordinate.CelestialInfo.SunRise; var sunset = coordinate.CelestialInfo.SunSet; if (sunrise is null || sunset is null) return (null, null); return (new Time(sunrise.Value.Hour, sunrise.Value.Minute), new Time(sunset.Value.Hour, sunset.Value.Minute)); } } ================================================ FILE: LenovoLegionToolkit.Lib/Utils/ThreadSafeBool.cs ================================================ using System.Threading; namespace LenovoLegionToolkit.Lib.Utils; public class ThreadSafeBool { private int _threadSafeBoolBackValue; public bool Value { get => Interlocked.CompareExchange(ref _threadSafeBoolBackValue, 1, 1) == 1; set { if (value) Interlocked.CompareExchange(ref _threadSafeBoolBackValue, 1, 0); else Interlocked.CompareExchange(ref _threadSafeBoolBackValue, 0, 1); } } } ================================================ FILE: LenovoLegionToolkit.Lib/Utils/ThreadSafeCounter.cs ================================================ using System; namespace LenovoLegionToolkit.Lib.Utils; public class ThreadSafeCounter { private readonly object _lock = new(); private int _counter; public bool Decrement() { lock (_lock) { var value = _counter < 1; _counter = Math.Max(0, _counter - 1); return value; } } public void Increment() { lock (_lock) { _counter++; } } } ================================================ FILE: LenovoLegionToolkit.Lib/Utils/ThrottleFirstDispatcher.cs ================================================ using System; using System.Threading.Tasks; using NeoSmart.AsyncLock; namespace LenovoLegionToolkit.Lib.Utils; public class ThrottleFirstDispatcher(TimeSpan interval, string? tag = null) { private readonly AsyncLock _lock = new(); private DateTime _lastEvent = DateTime.MinValue; public async Task DispatchAsync(Func task) { using (await _lock.LockAsync().ConfigureAwait(false)) { var diff = DateTime.UtcNow - _lastEvent; if (diff < interval) { if (tag is not null && Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Throttling... [tag={tag}, diff={diff.TotalMilliseconds}ms]"); return; } if (tag is not null && Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Allowing... [tag={tag}, diff={diff.TotalMilliseconds}ms]"); await task().ConfigureAwait(false); _lastEvent = DateTime.UtcNow; } } } ================================================ FILE: LenovoLegionToolkit.Lib/Utils/ThrottleLastDispatcher.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; namespace LenovoLegionToolkit.Lib.Utils; public class ThrottleLastDispatcher(TimeSpan interval, string? tag = null) { private CancellationTokenSource? _cancellationTokenSource; public async Task DispatchAsync(Func task) { try { if (_cancellationTokenSource is not null) await _cancellationTokenSource.CancelAsync().ConfigureAwait(false); _cancellationTokenSource = new(); var token = _cancellationTokenSource.Token; await Task.Delay(interval, token).ConfigureAwait(false); token.ThrowIfCancellationRequested(); if (tag is not null && Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Allowing... [tag={tag}]"); await task().ConfigureAwait(false); } catch (OperationCanceledException) { if (tag is not null && Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Throttling... [tag={tag}]"); } } } ================================================ FILE: LenovoLegionToolkit.Lib/Utils/TokenManipulator.cs ================================================ using System.Linq; using Microsoft.Win32.SafeHandles; using Windows.Win32; using Windows.Win32.Security; namespace LenovoLegionToolkit.Lib.Utils; public static class TokenManipulator { public const string SE_BACKUP_PRIVILEGE = "SeBackupPrivilege"; public const string SE_RESTORE_PRIVILEGE = "SeRestorePrivilege"; public const string SE_TAKE_OWNERSHIP_PRIVILEGE = "SeTakeOwnershipPrivilege"; public const string SE_SYSTEM_ENVIRONMENT_PRIVILEGE = "SeSystemEnvironmentPrivilege"; public static bool AddPrivileges(params string[] privileges) => AdjustPrivileges(privileges, true); public static bool RemovePrivileges(params string[] privileges) => AdjustPrivileges(privileges, true); private static bool AdjustPrivileges(string[] privileges, bool enable) { SafeProcessHandle? safeHandle = null; SafeFileHandle? safeTokenHandle = null; try { safeHandle = new SafeProcessHandle(PInvoke.GetCurrentProcess(), true); if (!PInvoke.OpenProcessToken(safeHandle, TOKEN_ACCESS_MASK.TOKEN_ADJUST_PRIVILEGES | TOKEN_ACCESS_MASK.TOKEN_QUERY, out safeTokenHandle) && safeTokenHandle is null) return false; return privileges.All(p => AdjustPrivilege(safeTokenHandle, p, enable)); } finally { safeTokenHandle?.Dispose(); safeHandle?.Dispose(); } } private static unsafe bool AdjustPrivilege(SafeFileHandle safeTokenHandle, string privilegeName, bool enable) { PInvoke.LookupPrivilegeValue(null, privilegeName, out var luid); var state = new TOKEN_PRIVILEGES { PrivilegeCount = 1 }; state.Privileges[0] = new LUID_AND_ATTRIBUTES { Luid = luid, Attributes = enable ? TOKEN_PRIVILEGES_ATTRIBUTES.SE_PRIVILEGE_ENABLED : 0 }; return PInvoke.AdjustTokenPrivileges(safeTokenHandle, false, &state, 0, null, null); } } ================================================ FILE: LenovoLegionToolkit.Lib/Utils/UpdateChecker.cs ================================================ using System; using System.IO; using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Settings; using NeoSmart.AsyncLock; using Octokit; using Octokit.Internal; namespace LenovoLegionToolkit.Lib.Utils; public class UpdateChecker { private readonly HttpClientFactory _httpClientFactory; private readonly UpdateCheckSettings _updateCheckSettings = IoCContainer.Resolve(); private readonly AsyncLock _updateSemaphore = new(); private DateTime _lastUpdate; private TimeSpan _minimumTimeSpanForRefresh; private Update[] _updates = []; public bool Disable { get; set; } public UpdateCheckStatus Status { get; set; } public UpdateChecker(HttpClientFactory httpClientFactory) { _httpClientFactory = httpClientFactory; UpdateMinimumTimeSpanForRefresh(); _lastUpdate = _updateCheckSettings.Store.LastUpdateCheckDateTime ?? DateTime.MinValue; } public async Task CheckAsync(bool forceCheck) { using (await _updateSemaphore.LockAsync().ConfigureAwait(false)) { if (Disable) { _lastUpdate = DateTime.UtcNow; _updates = []; return null; } try { var timeSpanSinceLastUpdate = DateTime.UtcNow - _lastUpdate; var shouldCheck = timeSpanSinceLastUpdate > _minimumTimeSpanForRefresh; if (!forceCheck && !shouldCheck) return _updates.Length != 0 ? _updates.First().Version : null; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Checking..."); var adapter = new HttpClientAdapter(_httpClientFactory.CreateHandler); var productInformation = new ProductHeaderValue("LenovoLegionToolkit-UpdateChecker"); var connection = new Connection(productInformation, adapter); var githubClient = new GitHubClient(connection); var releases = await githubClient.Repository.Release.GetAll("BartoszCichecki", "LenovoLegionToolkit", new ApiOptions { PageSize = 5 }).ConfigureAwait(false); var thisReleaseVersion = Assembly.GetEntryAssembly()?.GetName().Version; var thisBuildDate = Assembly.GetEntryAssembly()?.GetBuildDateTime() ?? new DateTime(2000, 1, 1); var updates = releases .Where(r => !r.Draft) .Where(r => !r.Prerelease) .Where(r => (r.PublishedAt ?? r.CreatedAt).UtcDateTime >= thisBuildDate) .Select(r => new Update(r)) .Where(r => r.Version > thisReleaseVersion) .OrderByDescending(r => r.Version) .ToArray(); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Checked [updates.Length={updates.Length}]"); _updates = updates; Status = UpdateCheckStatus.Success; return _updates.Length != 0 ? _updates.First().Version : null; } catch (RateLimitExceededException ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Reached API Rate Limitation.", ex); Status = UpdateCheckStatus.RateLimitReached; return null; } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Error checking for updates.", ex); Status = UpdateCheckStatus.Error; return null; } finally { _lastUpdate = DateTime.UtcNow; _updateCheckSettings.Store.LastUpdateCheckDateTime = _lastUpdate; _updateCheckSettings.SynchronizeStore(); } } } public async Task GetUpdatesAsync() { using (await _updateSemaphore.LockAsync().ConfigureAwait(false)) return _updates; } public async Task DownloadLatestUpdateAsync(IProgress? progress = null, CancellationToken cancellationToken = default) { using (await _updateSemaphore.LockAsync(cancellationToken).ConfigureAwait(false)) { var tempPath = Path.Combine(Folders.Temp, $"LenovoLegionToolkitSetup_{Guid.NewGuid()}.exe"); var latestUpdate = _updates.OrderByDescending(u => u.Version).FirstOrDefault(); if (latestUpdate.Equals(default)) throw new InvalidOperationException("No updates available"); if (latestUpdate.Url is null) throw new InvalidOperationException("Setup file URL could not be found"); await using var fileStream = File.OpenWrite(tempPath); using var httpClient = _httpClientFactory.Create(); await httpClient.DownloadAsync(latestUpdate.Url, fileStream, progress, cancellationToken).ConfigureAwait(false); return tempPath; } } public void UpdateMinimumTimeSpanForRefresh() => _minimumTimeSpanForRefresh = _updateCheckSettings.Store.UpdateCheckFrequency switch { UpdateCheckFrequency.PerHour => TimeSpan.FromHours(1), UpdateCheckFrequency.PerThreeHours => TimeSpan.FromHours(3), UpdateCheckFrequency.PerTwelveHours => TimeSpan.FromHours(13), UpdateCheckFrequency.PerDay => TimeSpan.FromDays(1), UpdateCheckFrequency.PerWeek => TimeSpan.FromDays(7), UpdateCheckFrequency.PerMonth => TimeSpan.FromDays(30), _ => throw new ArgumentException(nameof(_updateCheckSettings.Store.UpdateCheckFrequency)) }; } ================================================ FILE: LenovoLegionToolkit.Lib/Utils/WarrantyChecker.cs ================================================ using System; using System.Linq; using System.Net.Http; using System.Net.Http.Json; using System.Text.Json.Nodes; using System.Threading; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Settings; namespace LenovoLegionToolkit.Lib.Utils; public class WarrantyChecker(ApplicationSettings settings, HttpClientFactory httpClientFactory) { public async Task GetWarrantyInfo(MachineInformation machineInformation, bool forceRefresh = false, CancellationToken token = default) { if (!forceRefresh && settings.Store.WarrantyInfo.HasValue) return settings.Store.WarrantyInfo.Value; using var httpClient = httpClientFactory.Create(); var warrantyInfo = await GetStandardWarrantyInfo(httpClient, machineInformation, token).ConfigureAwait(false); settings.Store.WarrantyInfo = warrantyInfo; settings.SynchronizeStore(); return warrantyInfo; } private static async Task GetStandardWarrantyInfo(HttpClient httpClient, MachineInformation machineInformation, CancellationToken token) { var content = JsonContent.Create(new { serialNumber = machineInformation.SerialNumber, machineType = machineInformation.MachineType }); var response = await httpClient.PostAsync("https://pcsupport.lenovo.com/dk/en/api/v4/upsell/redport/getIbaseInfo", content, token).ConfigureAwait(false); var responseContent = await response.Content.ReadAsStringAsync(token).ConfigureAwait(false); var node = JsonNode.Parse(responseContent); if (node is null || node["code"]?.GetValue() != 0) return null; var baseWarranties = node["data"]?["baseWarranties"]?.AsArray() ?? []; var upgradeWarranties = node["data"]?["upgradeWarranties"]?.AsArray() ?? []; var startDate = baseWarranties.Concat(upgradeWarranties) .Select(n => n?["startDate"]) .Where(n => n is not null) .Select(n => DateTime.Parse(n!.ToString())) .Min(); var endDate = baseWarranties.Concat(upgradeWarranties) .Select(n => n?["endDate"]) .Where(n => n is not null) .Select(n => DateTime.Parse(n!.ToString())) .Max(); var productString = await httpClient.GetStringAsync($"https://pcsupport.lenovo.com/dk/en/api/v4/mse/getproducts?productId={machineInformation.SerialNumber}", token).ConfigureAwait(false); var productNode = JsonNode.Parse(productString); var firstProductNode = (productNode as JsonArray)?.FirstOrDefault(); var id = firstProductNode?["Id"]; var link = id is null ? null : new Uri($"https://pcsupport.lenovo.com/products/{id}"); return new WarrantyInfo(startDate, endDate, link); } } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/AutomationContext.cs ================================================ namespace LenovoLegionToolkit.Lib.Automation; public class AutomationContext { public string? LastRunOutput { get; set; } } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/AutomationEnvironment.cs ================================================ using System; using System.Collections.Generic; using System.Linq; namespace LenovoLegionToolkit.Lib.Automation; public class AutomationEnvironment { private const string AC_ADAPTER_CONNECTED = "LLT_IS_AC_ADAPTER_CONNECTED"; private const string LOW_POWER_AC_ADAPTER = "LLT_IS_AC_ADAPTER_LOW_POWER"; private const string DISPLAY_ON = "LLT_IS_DISPLAY_ON"; private const string EXTERNAL_DISPLAY_CONNECTED = "LLT_IS_EXTERNAL_DISPLAY_CONNECTED"; private const string GAME_RUNNING = "LLT_IS_GAME_RUNNING"; private const string HDR_ON = "LLT_IS_HDR_ON"; private const string LID_OPEN = "LLT_IS_LID_OPEN"; private const string STARTUP = "LLT_STARTUP"; private const string RESUME = "LLT_RESUME"; private const string POWER_MODE = "LLT_POWER_MODE"; private const string POWER_MODE_NAME = "LLT_POWER_MODE_NAME"; private const string PROCESSES_STARTED = "LLT_PROCESSES_STARTED"; private const string PROCESSES = "LLT_PROCESSES"; private const string DEVICE_CONNECTED = "LLT_DEVICE_CONNECTED"; private const string DEVICE_INSTANCE_IDS = "LLT_DEVICE_INSTANCE_IDS"; private const string IS_SUNSET = "LLT_IS_SUNSET"; private const string IS_SUNRISE = "LLT_IS_SUNRISE"; private const string TIME = "LLT_TIME"; private const string DAYS = "LLT_DAYS"; private const string PERIOD = "LLT_PERIOD"; private const string USER_ACTIVE = "LLT_IS_USER_ACTIVE"; private const string WIFI_CONNECTED = "LLT_WIFI_CONNECTED"; private const string WIFI_SSID = "LLT_WIFI_SSID"; private const string SESSION_LOCKED = "LLT_SESSION_LOCKED"; private const string VALUE_TRUE = "TRUE"; private const string VALUE_FALSE = "FALSE"; public bool AcAdapterConnected { set => _dictionary[AC_ADAPTER_CONNECTED] = value ? VALUE_TRUE : VALUE_FALSE; } public bool LowPowerAcAdapter { set => _dictionary[LOW_POWER_AC_ADAPTER] = value ? VALUE_TRUE : VALUE_FALSE; } public bool DisplayOn { set => _dictionary[DISPLAY_ON] = value ? VALUE_TRUE : VALUE_FALSE; } public bool ExternalDisplayConnected { set => _dictionary[EXTERNAL_DISPLAY_CONNECTED] = value ? VALUE_TRUE : VALUE_FALSE; } public bool GameRunning { set => _dictionary[GAME_RUNNING] = value ? VALUE_TRUE : VALUE_FALSE; } public bool HDROn { set => _dictionary[HDR_ON] = value ? VALUE_TRUE : VALUE_FALSE; } public bool LidOpen { set => _dictionary[LID_OPEN] = value ? VALUE_TRUE : VALUE_FALSE; } public bool Startup { set => _dictionary[STARTUP] = value ? VALUE_TRUE : VALUE_FALSE; } public bool Resume { set => _dictionary[RESUME] = value ? VALUE_TRUE : VALUE_FALSE; } public PowerModeState PowerMode { set { _dictionary[POWER_MODE] = value switch { PowerModeState.Quiet => "1", PowerModeState.Balance => "2", PowerModeState.Performance => "3", PowerModeState.GodMode => "255", _ => string.Empty }; _dictionary[POWER_MODE_NAME] = value switch { PowerModeState.Quiet => "QUIET", PowerModeState.Balance => "BALANCE", PowerModeState.Performance => "PERFORMANCE", PowerModeState.GodMode => "CUSTOM", _ => string.Empty }; } } public bool ProcessesStarted { set => _dictionary[PROCESSES_STARTED] = value ? VALUE_TRUE : VALUE_FALSE; } public ProcessInfo[] Processes { set => _dictionary[PROCESSES] = string.Join(",", value.Select(p => p.Name)); } public bool DeviceConnected { set => _dictionary[DEVICE_CONNECTED] = value ? VALUE_TRUE : VALUE_FALSE; } public string[] DeviceInstanceIds { set => _dictionary[DEVICE_INSTANCE_IDS] = string.Join(",", value); } public bool IsSunset { set => _dictionary[IS_SUNSET] = value ? VALUE_TRUE : VALUE_FALSE; } public bool IsSunrise { set => _dictionary[IS_SUNRISE] = value ? VALUE_TRUE : VALUE_FALSE; } public Time? Time { set => _dictionary[TIME] = value is null ? null : $"{value.Value.Hour}:{value.Value.Minute}"; } public DayOfWeek[] Days { set => _dictionary[DAYS] = value.Length < 1 ? null : string.Join(",", value.Select(v => v.ToString().ToUpperInvariant())); } public TimeSpan Period { set => _dictionary[PERIOD] = $"{(int)value.TotalSeconds}"; } public bool UserActive { set => _dictionary[USER_ACTIVE] = value ? VALUE_TRUE : VALUE_FALSE; } public bool WiFiConnected { set => _dictionary[WIFI_CONNECTED] = value ? VALUE_TRUE : VALUE_FALSE; } public string? WiFiSsid { set => _dictionary[WIFI_SSID] = value; } public bool SessionLocked { set => _dictionary[SESSION_LOCKED] = value ? VALUE_TRUE : VALUE_FALSE; } public Dictionary Dictionary => new(_dictionary); private readonly Dictionary _dictionary = []; } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/AutomationProcessor.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.AutoListeners; using LenovoLegionToolkit.Lib.Automation.Pipeline; using LenovoLegionToolkit.Lib.Automation.Pipeline.Triggers; using LenovoLegionToolkit.Lib.Automation.Utils; using LenovoLegionToolkit.Lib.Controllers.GodMode; using LenovoLegionToolkit.Lib.Listeners; using LenovoLegionToolkit.Lib.Utils; using NeoSmart.AsyncLock; namespace LenovoLegionToolkit.Lib.Automation; public class AutomationProcessor( AutomationSettings settings, DisplayConfigurationListener displayConfigurationListener, NativeWindowsMessageListener nativeWindowsMessageListener, PowerStateListener powerStateListener, PowerModeListener powerModeListener, GodModeController godModeController, GameAutoListener gameAutoListener, ProcessAutoListener processAutoListener, SessionLockUnlockListener sessionLockUnlockListener, TimeAutoListener timeAutoListener, UserInactivityAutoListener userInactivityAutoListener, WiFiAutoListener wifiAutoListener) { private readonly AsyncLock _ioLock = new(); private readonly AsyncLock _runLock = new(); private List _pipelines = []; private CancellationTokenSource? _cts; public bool IsEnabled => settings.Store.IsEnabled; public event EventHandler>? PipelinesChanged; #region Initialization / pipeline reloading public async Task InitializeAsync() { using (await _ioLock.LockAsync().ConfigureAwait(false)) { displayConfigurationListener.Changed += DisplayConfigurationListener_Changed; nativeWindowsMessageListener.Changed += NativeWindowsMessageListener_Changed; powerStateListener.Changed += PowerStateListener_Changed; powerModeListener.Changed += PowerModeListener_Changed; godModeController.PresetChanged += GodModeController_PresetChanged; sessionLockUnlockListener.Changed += SessionLockUnlockListener_Changed; _pipelines = [.. settings.Store.Pipelines]; RaisePipelinesChanged(); await UpdateListenersAsync().ConfigureAwait(false); } } public async Task SetEnabledAsync(bool enabled) { using (await _ioLock.LockAsync().ConfigureAwait(false)) { settings.Store.IsEnabled = enabled; settings.SynchronizeStore(); await UpdateListenersAsync().ConfigureAwait(false); } } public async Task ReloadPipelinesAsync(List pipelines) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Pipelines reload pending..."); using (await _ioLock.LockAsync().ConfigureAwait(false)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Pipelines reloading..."); _pipelines = pipelines.Select(p => p.DeepCopy()).ToList(); settings.Store.Pipelines = pipelines; settings.SynchronizeStore(); RaisePipelinesChanged(); await UpdateListenersAsync().ConfigureAwait(false); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Pipelines reloaded."); } } public async Task> GetPipelinesAsync() { using (await _ioLock.LockAsync().ConfigureAwait(false)) return _pipelines.Select(p => p.DeepCopy()).ToList(); } #endregion #region Run public void RunOnStartup() { if (!IsEnabled) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Not enabled. Pipeline run on startup ignored."); return; } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Pipeline run on startup pending..."); Task.Run(() => ProcessEvent(new StartupAutomationEvent())); } public async Task RunNowAsync(AutomationPipeline pipeline) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Pipeline run now pending..."); using (await _runLock.LockAsync().ConfigureAwait(false)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Pipeline run starting..."); try { List pipelines; using (await _ioLock.LockAsync().ConfigureAwait(false)) pipelines = _pipelines.ToList(); var otherPipelines = pipelines.Where(p => p.Id != pipeline.Id).ToList(); await pipeline.DeepCopy().RunAsync(otherPipelines).ConfigureAwait(false); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Pipeline run finished successfully."); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Pipeline run failed.", ex); throw; } } } public async Task RunNowAsync(Guid pipelineId) { AutomationPipeline? pipeline; using (await _ioLock.LockAsync().ConfigureAwait(false)) pipeline = _pipelines.Where(p => p.Trigger is null).FirstOrDefault(p => p.Id == pipelineId); if (pipeline is null) return; await RunNowAsync(pipeline).ConfigureAwait(false); } private async Task RunAsync(IAutomationEvent automationEvent) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Run pending..."); using (await _runLock.LockAsync().ConfigureAwait(false)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Run starting..."); if (_cts is not null) await _cts.CancelAsync().ConfigureAwait(false); if (!IsEnabled) return; List pipelines; using (await _ioLock.LockAsync().ConfigureAwait(false)) pipelines = _pipelines.ToList(); _cts = new CancellationTokenSource(); var ct = _cts.Token; foreach (var pipeline in pipelines) { if (ct.IsCancellationRequested) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Run interrupted."); break; } try { if (pipeline.Trigger is null || !await pipeline.Trigger.IsMatchingEvent(automationEvent).ConfigureAwait(false)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Pipeline triggers not satisfied. [name={pipeline.Name}, trigger={pipeline.Trigger}, steps.Count={pipeline.Steps.Count}]"); continue; } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Running pipeline... [name={pipeline.Name}, trigger={pipeline.Trigger}, steps.Count={pipeline.Steps.Count}]"); var otherPipelines = pipelines.Where(p => p.Id != pipeline.Id).ToList(); await pipeline.RunAsync(otherPipelines, ct).ConfigureAwait(false); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Pipeline completed successfully. [name={pipeline.Name}, trigger={pipeline.Trigger}]"); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Pipeline run failed. [name={pipeline.Name}, trigger={pipeline.Trigger}]", ex); } if (pipeline.IsExclusive) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Pipeline is exclusive. Breaking. [name={pipeline.Name}, trigger={pipeline.Trigger}, steps.Count={pipeline.Steps.Count}]"); break; } } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Run finished successfully."); } } #endregion #region Listeners private async void DisplayConfigurationListener_Changed(object? sender, DisplayConfigurationListener.ChangedEventArgs args) { var e = new HDRAutomationEvent(args.HDR); await ProcessEvent(e).ConfigureAwait(false); } private async void NativeWindowsMessageListener_Changed(object? sender, NativeWindowsMessageListener.ChangedEventArgs args) { var e = new NativeWindowsMessageEvent(args.Message, args.Data); await ProcessEvent(e).ConfigureAwait(false); } private async void PowerStateListener_Changed(object? sender, PowerStateListener.ChangedEventArgs args) { var e = new PowerStateAutomationEvent(args.PowerStateEvent, args.PowerAdapterStateChanged); await ProcessEvent(e).ConfigureAwait(false); } private async void PowerModeListener_Changed(object? sender, PowerModeListener.ChangedEventArgs args) { var e = new PowerModeAutomationEvent(args.State); await ProcessEvent(e).ConfigureAwait(false); } private async void GodModeController_PresetChanged(object? sender, Guid presetId) { var e = new CustomModePresetAutomationEvent(presetId); await ProcessEvent(e).ConfigureAwait(false); } private async void GameAutoListener_Changed(object? sender, GameAutoListener.ChangedEventArgs args) { var e = new GameAutomationEvent(args.Running); await ProcessEvent(e).ConfigureAwait(false); } private async void ProcessAutoListener_Changed(object? sender, ProcessAutoListener.ChangedEventArgs args) { var e = new ProcessAutomationEvent(args.Type, args.ProcessInfo); await ProcessEvent(e).ConfigureAwait(false); } private async void SessionLockUnlockListener_Changed(object? sender, SessionLockUnlockListener.ChangedEventArgs args) { var e = new SessionLockUnlockAutomationEvent(args.Locked); await ProcessEvent(e).ConfigureAwait(false); } private async void TimeAutoListener_Changed(object? sender, TimeAutoListener.ChangedEventArgs args) { var e = new TimeAutomationEvent(args.Time, args.Day); await ProcessEvent(e).ConfigureAwait(false); } private async void UserInactivityAutoListener_Changed(object? sender, UserInactivityAutoListener.ChangedEventArgs args) { var e = new UserInactivityAutomationEvent(args.TimerResolution * args.TickCount); await ProcessEvent(e).ConfigureAwait(false); } private async void WiFiAutoListener_Changed(object? sender, WiFiAutoListener.ChangedEventArgs args) { var e = new WiFiAutomationEvent(args.IsConnected, args.Ssid); await ProcessEvent(e).ConfigureAwait(false); } #endregion #region Event processing private async Task ProcessEvent(IAutomationEvent e) { var potentialMatch = _pipelines.SelectMany(p => p.AllTriggers) .Select(async t => await t.IsMatchingEvent(e).ConfigureAwait(false)) .Select(t => t.Result) .Where(t => t) .Any(); if (!potentialMatch) return; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Processing event {e}... [type={e.GetType().Name}]"); await RunAsync(e).ConfigureAwait(false); } #endregion #region Helper methods private async Task UpdateListenersAsync() { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Stopping listeners..."); await gameAutoListener.UnsubscribeChangedAsync(GameAutoListener_Changed).ConfigureAwait(false); await processAutoListener.UnsubscribeChangedAsync(ProcessAutoListener_Changed).ConfigureAwait(false); await timeAutoListener.UnsubscribeChangedAsync(TimeAutoListener_Changed).ConfigureAwait(false); await userInactivityAutoListener.UnsubscribeChangedAsync(UserInactivityAutoListener_Changed).ConfigureAwait(false); await wifiAutoListener.UnsubscribeChangedAsync(WiFiAutoListener_Changed).ConfigureAwait(false); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Stopped listeners..."); if (!IsEnabled) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Not enabled. Will not start listeners."); return; } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Starting listeners..."); var triggers = _pipelines.SelectMany(p => p.AllTriggers).ToArray(); if (triggers.OfType().Any()) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Starting game listener..."); await gameAutoListener.SubscribeChangedAsync(GameAutoListener_Changed).ConfigureAwait(false); } if (triggers.OfType().Any()) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Starting process listener..."); await processAutoListener.SubscribeChangedAsync(ProcessAutoListener_Changed).ConfigureAwait(false); } if (triggers.OfType().Any() || triggers.OfType().Any()) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Starting time listener..."); await timeAutoListener.SubscribeChangedAsync(TimeAutoListener_Changed).ConfigureAwait(false); } if (triggers.OfType().Any()) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Starting user inactivity listener..."); await userInactivityAutoListener.SubscribeChangedAsync(UserInactivityAutoListener_Changed).ConfigureAwait(false); } if (triggers.OfType().Any() || triggers.OfType().Any()) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Starting WiFi listener..."); await wifiAutoListener.SubscribeChangedAsync(WiFiAutoListener_Changed).ConfigureAwait(false); } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Started relevant listeners."); } private void RaisePipelinesChanged() { PipelinesChanged?.Invoke(this, _pipelines.Select(p => p.DeepCopy()).ToList()); } #endregion } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Enums.cs ================================================ using System.ComponentModel.DataAnnotations; using LenovoLegionToolkit.Lib.Automation.Resources; namespace LenovoLegionToolkit.Lib.Automation; public enum DeactivateGPUAutomationStepState { [Display(ResourceType = typeof(Resource), Name = "DeactivateGPUAutomationStepState_KillApps")] KillApps, [Display(ResourceType = typeof(Resource), Name = "DeactivateGPUAutomationStepState_RestartGPU")] RestartGPU, } public enum MacroAutomationStepState { [Display(ResourceType = typeof(Resource), Name = "MacroAutomationStepState_Off")] Off, [Display(ResourceType = typeof(Resource), Name = "MacroAutomationStepState_On")] On } public enum OverclockDiscreteGPUAutomationStepState { [Display(ResourceType = typeof(Resource), Name = "OverclockDiscreteGPUAutomationStepState_Off")] Off, [Display(ResourceType = typeof(Resource), Name = "OverclockDiscreteGPUAutomationStepState_On")] On } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/GlobalSuppressions.cs ================================================ // This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage("Performance", "CA1822:Mark members as static")] [assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility")] ================================================ FILE: LenovoLegionToolkit.Lib.Automation/IAutomationEvent.cs ================================================ using System; namespace LenovoLegionToolkit.Lib.Automation; public interface IAutomationEvent; public readonly struct HDRAutomationEvent(bool? isHDROn) : IAutomationEvent { public bool? IsHDROn { get; } = isHDROn; } public readonly struct NativeWindowsMessageEvent(NativeWindowsMessage message, object? data) : IAutomationEvent { public NativeWindowsMessage Message { get; } = message; public object? Data { get; } = data; } public struct StartupAutomationEvent : IAutomationEvent; public readonly struct PowerStateAutomationEvent(PowerStateEvent powerStateEvent, bool powerAdapterStateChanged) : IAutomationEvent { public PowerStateEvent PowerStateEvent { get; } = powerStateEvent; public bool PowerAdapterStateChanged { get; } = powerAdapterStateChanged; } public readonly struct PowerModeAutomationEvent(PowerModeState powerModeState) : IAutomationEvent { public PowerModeState PowerModeState { get; } = powerModeState; } public readonly struct CustomModePresetAutomationEvent(Guid id) : IAutomationEvent { public Guid Id { get; } = id; } public readonly struct GameAutomationEvent(bool running) : IAutomationEvent { public bool Running { get; } = running; } public readonly struct ProcessAutomationEvent(ProcessEventInfoType type, ProcessInfo processInfo) : IAutomationEvent { public ProcessEventInfoType Type { get; } = type; public ProcessInfo ProcessInfo { get; } = processInfo; } public readonly struct SessionLockUnlockAutomationEvent(bool locked) : IAutomationEvent { public bool Locked { get; } = locked; } public readonly struct TimeAutomationEvent(Time time, DayOfWeek day) : IAutomationEvent { public Time Time { get; } = time; public DayOfWeek Day { get; } = day; } public readonly struct UserInactivityAutomationEvent(TimeSpan inactivityTimeSpan) : IAutomationEvent { public TimeSpan InactivityTimeSpan { get; } = inactivityTimeSpan; } public readonly struct WiFiAutomationEvent(bool isConnected, string? ssid) : IAutomationEvent { public bool IsConnected { get; } = isConnected; public string? Ssid { get; } = ssid; } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/IoCModule.cs ================================================ using Autofac; using LenovoLegionToolkit.Lib.Automation.Utils; using LenovoLegionToolkit.Lib.Extensions; namespace LenovoLegionToolkit.Lib.Automation; public class IoCModule : Module { protected override void Load(ContainerBuilder builder) { builder.Register(); builder.Register(); } } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/LenovoLegionToolkit.Lib.Automation.csproj ================================================ net8.0-windows win-x64 x64 enable © 2024 Bartosz Cichecki true en True True Resource.resx PublicResXFileCodeGenerator Resource.Designer.cs ================================================ FILE: LenovoLegionToolkit.Lib.Automation/LenovoLegionToolkit.Lib.Automation.csproj.DotSettings ================================================  Library ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Pipeline/AutomationPipeline.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Automation.Pipeline.Triggers; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Utils; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Pipeline; public class AutomationPipeline { public Guid Id { get; init; } = Guid.NewGuid(); public string? IconName { get; set; } public string? Name { get; set; } public IAutomationPipelineTrigger? Trigger { get; set; } public List Steps { get; init; } = []; public bool IsExclusive { get; init; } = true; [JsonIgnore] public IEnumerable AllTriggers { get { if (Trigger is not null && Trigger is not ICompositeAutomationPipelineTrigger) yield return Trigger; if (Trigger is ICompositeAutomationPipelineTrigger compositeTrigger) foreach (var trigger in compositeTrigger.Triggers) yield return trigger; } } public AutomationPipeline() { } public AutomationPipeline(string name) => Name = name; public AutomationPipeline(IAutomationPipelineTrigger trigger) => Trigger = trigger; internal async Task RunAsync(List otherPipelines, CancellationToken token = default) { if (token.IsCancellationRequested) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Pipeline interrupted."); return; } var context = new AutomationContext(); var environment = new AutomationEnvironment(); var stepExceptions = new List(); AllTriggers.ForEach(t => t.UpdateEnvironment(environment)); foreach (var step in GetAllSteps(otherPipelines)) { if (token.IsCancellationRequested) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Pipeline interrupted."); break; } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Running step... [type={step.GetType().Name}]"); try { await step.RunAsync(context, environment, token).ConfigureAwait(false); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Step run failed. [name={step.GetType().Name}]", ex); stepExceptions.Add(ex); } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Step completed successfully. [type={step.GetType().Name}]"); } if (stepExceptions.Count != 0) throw new AggregateException(stepExceptions); } private IEnumerable GetAllSteps(List pipelines) { foreach (var step in Steps) { if (step is QuickActionAutomationStep qas) { var matchingPipeline = pipelines.FirstOrDefault(p => p.Id != Id && p.Id == qas.PipelineId && p.AllTriggers.IsEmpty()); if (matchingPipeline is null) continue; foreach (var matchingPipelineStep in matchingPipeline.GetAllSteps(pipelines)) yield return matchingPipelineStep; } yield return step; } } public AutomationPipeline DeepCopy() => new() { Id = Id, IconName = IconName, Name = Name, Trigger = Trigger?.DeepCopy(), Steps = Steps.Select(s => s.DeepCopy()).ToList(), IsExclusive = IsExclusive, }; } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Pipeline/Triggers/ACAdapterConnectedAutomationPipelineTrigger.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Automation.Resources; using LenovoLegionToolkit.Lib.System; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Pipeline.Triggers; public class ACAdapterConnectedAutomationPipelineTrigger : IPowerStateAutomationPipelineTrigger { [JsonIgnore] public string DisplayName => Resource.ACAdapterConnectedAutomationPipelineTrigger_DisplayName; public async Task IsMatchingEvent(IAutomationEvent automationEvent) { if (automationEvent is not (PowerStateAutomationEvent { PowerStateEvent: PowerStateEvent.StatusChange, PowerAdapterStateChanged: true } or StartupAutomationEvent)) return false; var status = await Power.IsPowerAdapterConnectedAsync().ConfigureAwait(false); return status == PowerAdapterStatus.Connected; } public async Task IsMatchingState() { var status = await Power.IsPowerAdapterConnectedAsync().ConfigureAwait(false); return status == PowerAdapterStatus.Connected; } public void UpdateEnvironment(AutomationEnvironment environment) => environment.AcAdapterConnected = true; public IAutomationPipelineTrigger DeepCopy() => new ACAdapterConnectedAutomationPipelineTrigger(); public override bool Equals(object? obj) => obj is ACAdapterConnectedAutomationPipelineTrigger; public override int GetHashCode() => HashCode.Combine(DisplayName); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Pipeline/Triggers/ACAdapterDisconnectedAutomationPipelineTrigger.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Automation.Resources; using LenovoLegionToolkit.Lib.System; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Pipeline.Triggers; public class ACAdapterDisconnectedAutomationPipelineTrigger : IPowerStateAutomationPipelineTrigger { [JsonIgnore] public string DisplayName => Resource.ACAdapterDisconnectedAutomationPipelineTrigger_DisplayName; public async Task IsMatchingEvent(IAutomationEvent automationEvent) { if (automationEvent is not (PowerStateAutomationEvent { PowerStateEvent: PowerStateEvent.StatusChange, PowerAdapterStateChanged: true } or StartupAutomationEvent)) return false; var status = await Power.IsPowerAdapterConnectedAsync().ConfigureAwait(false); return status == PowerAdapterStatus.Disconnected; } public async Task IsMatchingState() { var status = await Power.IsPowerAdapterConnectedAsync().ConfigureAwait(false); return status == PowerAdapterStatus.Disconnected; } public void UpdateEnvironment(AutomationEnvironment environment) => environment.AcAdapterConnected = false; public IAutomationPipelineTrigger DeepCopy() => new ACAdapterDisconnectedAutomationPipelineTrigger(); public override bool Equals(object? obj) => obj is ACAdapterDisconnectedAutomationPipelineTrigger; public override int GetHashCode() => HashCode.Combine(DisplayName); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Pipeline/Triggers/AndAutomationPipelineTrigger.cs ================================================ using System; using System.Linq; using System.Threading.Tasks; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Pipeline.Triggers; [method: JsonConstructor] public class AndAutomationPipelineTrigger(IAutomationPipelineTrigger[] triggers) : ICompositeAutomationPipelineTrigger { public string DisplayName => string.Join(Environment.NewLine, Triggers.Select(t => t.DisplayName)); public IAutomationPipelineTrigger[] Triggers { get; } = triggers; public async Task IsMatchingEvent(IAutomationEvent automationEvent) { foreach (var trigger in Triggers) { if (!await trigger.IsMatchingEvent(automationEvent).ConfigureAwait(false)) continue; foreach (var otherTrigger in Triggers.Where(t => !ReferenceEquals(t, trigger))) { if (!await otherTrigger.IsMatchingState().ConfigureAwait(false)) return false; } return true; } return false; } public Task IsMatchingState() => Task.FromResult(false); public void UpdateEnvironment(AutomationEnvironment environment) { foreach (var trigger in Triggers) trigger.UpdateEnvironment(environment); } public IAutomationPipelineTrigger DeepCopy() => new AndAutomationPipelineTrigger(Triggers); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Pipeline/Triggers/DeviceConnectedAutomationPipelineTrigger.cs ================================================ using System; using System.Linq; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Automation.Resources; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.System; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Pipeline.Triggers; [method: JsonConstructor] public class DeviceConnectedAutomationPipelineTrigger(string[]? instanceIds) : IDeviceAutomationPipelineTrigger { [JsonIgnore] public string DisplayName => Resource.DeviceConnectedAutomationPipelineTrigger_DisplayName; public string[] InstanceIds { get; } = instanceIds ?? []; public Task IsMatchingEvent(IAutomationEvent automationEvent) { if (automationEvent is not NativeWindowsMessageEvent { Message: NativeWindowsMessage.DeviceConnected, Data: string deviceInstanceId }) return Task.FromResult(false); return Task.FromResult(InstanceIds.Contains(deviceInstanceId)); } public Task IsMatchingState() { var result = Devices.GetAll() .Where(d => !d.IsDisconnected) .Select(d => d.DeviceInstanceId) .Intersect(InstanceIds) .Any(); return Task.FromResult(result); } public void UpdateEnvironment(AutomationEnvironment environment) { environment.DeviceConnected = true; environment.DeviceInstanceIds = InstanceIds; } public IAutomationPipelineTrigger DeepCopy() => new DeviceConnectedAutomationPipelineTrigger(InstanceIds); public IDeviceAutomationPipelineTrigger DeepCopy(string[] instanceIds) => new DeviceConnectedAutomationPipelineTrigger(instanceIds); public override bool Equals(object? obj) => obj is DeviceConnectedAutomationPipelineTrigger t && InstanceIds.SequenceEqual(t.InstanceIds); public override int GetHashCode() { var hc = new HashCode(); InstanceIds.ForEach(id => hc.Add(id)); return hc.ToHashCode(); } public override string ToString() => $"{nameof(InstanceIds)}: {string.Join(", ", InstanceIds)}"; } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Pipeline/Triggers/DeviceDisconnectedAutomationPipelineTrigger.cs ================================================ using System; using System.Linq; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Automation.Resources; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.System; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Pipeline.Triggers; [method: JsonConstructor] public class DeviceDisconnectedAutomationPipelineTrigger(string[]? instanceIds) : IDeviceAutomationPipelineTrigger { [JsonIgnore] public string DisplayName => Resource.DeviceDisconnectedAutomationPipelineTrigger_DisplayName; public string[] InstanceIds { get; } = instanceIds ?? []; public Task IsMatchingEvent(IAutomationEvent automationEvent) { if (automationEvent is not NativeWindowsMessageEvent { Message: NativeWindowsMessage.DeviceDisconnected, Data: string deviceInstanceId }) return Task.FromResult(false); return Task.FromResult(InstanceIds.Contains(deviceInstanceId)); } public Task IsMatchingState() { var result = Devices.GetAll() .Where(d => !d.IsDisconnected) .Select(d => d.DeviceInstanceId) .Intersect(InstanceIds) .IsEmpty(); return Task.FromResult(result); } public void UpdateEnvironment(AutomationEnvironment environment) { environment.DeviceConnected = false; environment.DeviceInstanceIds = InstanceIds; } public IAutomationPipelineTrigger DeepCopy() => new DeviceDisconnectedAutomationPipelineTrigger(InstanceIds); public IDeviceAutomationPipelineTrigger DeepCopy(string[] instanceIds) => new DeviceDisconnectedAutomationPipelineTrigger(instanceIds); public override bool Equals(object? obj) => obj is DeviceDisconnectedAutomationPipelineTrigger t && InstanceIds.SequenceEqual(t.InstanceIds); public override int GetHashCode() { var hc = new HashCode(); InstanceIds.ForEach(id => hc.Add(id)); return hc.ToHashCode(); } public override string ToString() => $"{nameof(InstanceIds)}: {string.Join(", ", InstanceIds)}"; } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Pipeline/Triggers/DisplayOffAutomationPipelineTrigger.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Automation.Resources; using LenovoLegionToolkit.Lib.Listeners; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Pipeline.Triggers; public class DisplayOffAutomationPipelineTrigger : INativeWindowsMessagePipelineTrigger, IDisallowDuplicatesAutomationPipelineTrigger { [JsonIgnore] public string DisplayName => Resource.DisplayOffAutomationPipelineTrigger_DisplayName; public Task IsMatchingEvent(IAutomationEvent automationEvent) { var result = automationEvent is NativeWindowsMessageEvent { Message: NativeWindowsMessage.MonitorOff }; return Task.FromResult(result); } public Task IsMatchingState() { var listener = IoCContainer.Resolve(); var result = !listener.IsMonitorOn; return Task.FromResult(result); } public void UpdateEnvironment(AutomationEnvironment environment) => environment.DisplayOn = false; public IAutomationPipelineTrigger DeepCopy() => new DisplayOffAutomationPipelineTrigger(); public override bool Equals(object? obj) => obj is DisplayOffAutomationPipelineTrigger; public override int GetHashCode() => HashCode.Combine(DisplayName); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Pipeline/Triggers/DisplayOnAutomationPipelineTrigger.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Automation.Resources; using LenovoLegionToolkit.Lib.Listeners; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Pipeline.Triggers; public class DisplayOnAutomationPipelineTrigger : INativeWindowsMessagePipelineTrigger, IDisallowDuplicatesAutomationPipelineTrigger { [JsonIgnore] public string DisplayName => Resource.DisplayOnAutomationPipelineTrigger_DisplayName; public Task IsMatchingEvent(IAutomationEvent automationEvent) { var result = automationEvent is NativeWindowsMessageEvent { Message: NativeWindowsMessage.MonitorOn }; return Task.FromResult(result); } public Task IsMatchingState() { var listener = IoCContainer.Resolve(); var result = listener.IsMonitorOn; return Task.FromResult(result); } public void UpdateEnvironment(AutomationEnvironment environment) => environment.DisplayOn = true; public IAutomationPipelineTrigger DeepCopy() => new DisplayOnAutomationPipelineTrigger(); public override bool Equals(object? obj) => obj is DisplayOnAutomationPipelineTrigger; public override int GetHashCode() => HashCode.Combine(DisplayName); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Pipeline/Triggers/ExternalDisplayConnectedAutomationPipelineTrigger.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Automation.Resources; using LenovoLegionToolkit.Lib.System; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Pipeline.Triggers; public class ExternalDisplayConnectedAutomationPipelineTrigger : INativeWindowsMessagePipelineTrigger, IDisallowDuplicatesAutomationPipelineTrigger { [JsonIgnore] public string DisplayName => Resource.ExternalDisplayConnectedAutomationPipelineTrigger_DisplayName; public Task IsMatchingEvent(IAutomationEvent automationEvent) { var result = automationEvent is NativeWindowsMessageEvent { Message: NativeWindowsMessage.ExternalMonitorConnected }; return Task.FromResult(result); } public Task IsMatchingState() { var result = ExternalDisplays.Get().Length > 0; return Task.FromResult(result); } public void UpdateEnvironment(AutomationEnvironment environment) => environment.ExternalDisplayConnected = true; public IAutomationPipelineTrigger DeepCopy() => new ExternalDisplayConnectedAutomationPipelineTrigger(); public override bool Equals(object? obj) => obj is ExternalDisplayConnectedAutomationPipelineTrigger; public override int GetHashCode() => HashCode.Combine(DisplayName); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Pipeline/Triggers/ExternalDisplayDisconnectedAutomationPipelineTrigger.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Automation.Resources; using LenovoLegionToolkit.Lib.System; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Pipeline.Triggers; public class ExternalDisplayDisconnectedAutomationPipelineTrigger : INativeWindowsMessagePipelineTrigger, IDisallowDuplicatesAutomationPipelineTrigger { [JsonIgnore] public string DisplayName => Resource.ExternalDisplayDisconnectedAutomationPipelineTrigger_DisplayName; public Task IsMatchingEvent(IAutomationEvent automationEvent) { var result = automationEvent is NativeWindowsMessageEvent { Message: NativeWindowsMessage.ExternalMonitorDisconnected }; return Task.FromResult(result); } public Task IsMatchingState() { var result = ExternalDisplays.Get().Length < 1; return Task.FromResult(result); } public void UpdateEnvironment(AutomationEnvironment environment) => environment.ExternalDisplayConnected = false; public IAutomationPipelineTrigger DeepCopy() => new ExternalDisplayDisconnectedAutomationPipelineTrigger(); public override bool Equals(object? obj) => obj is ExternalDisplayDisconnectedAutomationPipelineTrigger; public override int GetHashCode() => HashCode.Combine(DisplayName); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Pipeline/Triggers/GamesAreRunningAutomationPipelineTrigger.cs ================================================ using System.Threading.Tasks; using LenovoLegionToolkit.Lib.AutoListeners; using LenovoLegionToolkit.Lib.Automation.Resources; namespace LenovoLegionToolkit.Lib.Automation.Pipeline.Triggers; public class GamesAreRunningAutomationPipelineTrigger : IGameAutomationPipelineTrigger { public string DisplayName => Resource.GamesAreRunningAutomationPipelineTrigger_DisplayName; public Task IsMatchingEvent(IAutomationEvent automationEvent) { var result = automationEvent is GameAutomationEvent { Running: true }; return Task.FromResult(result); } public Task IsMatchingState() { var listener = IoCContainer.Resolve(); var result = listener.AreGamesRunning(); return Task.FromResult(result); } public void UpdateEnvironment(AutomationEnvironment environment) => environment.GameRunning = true; public IAutomationPipelineTrigger DeepCopy() => new GamesAreRunningAutomationPipelineTrigger(); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Pipeline/Triggers/GamesStopAutomationPipelineTrigger.cs ================================================ using System.Threading.Tasks; using LenovoLegionToolkit.Lib.AutoListeners; using LenovoLegionToolkit.Lib.Automation.Resources; namespace LenovoLegionToolkit.Lib.Automation.Pipeline.Triggers; public class GamesStopAutomationPipelineTrigger : IGameAutomationPipelineTrigger { public string DisplayName => Resource.GamesStopAutomationPipelineTrigger_DisplayName; public Task IsMatchingEvent(IAutomationEvent automationEvent) { var result = automationEvent is GameAutomationEvent { Running: false }; return Task.FromResult(result); } public Task IsMatchingState() { var listener = IoCContainer.Resolve(); var result = !listener.AreGamesRunning(); return Task.FromResult(result); } public void UpdateEnvironment(AutomationEnvironment environment) => environment.GameRunning = false; public IAutomationPipelineTrigger DeepCopy() => new GamesStopAutomationPipelineTrigger(); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Pipeline/Triggers/GodModePresetChangedAutomationPipelineTrigger.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Automation.Resources; using LenovoLegionToolkit.Lib.Controllers.GodMode; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Pipeline.Triggers; [method: JsonConstructor] public class GodModePresetChangedAutomationPipelineTrigger(Guid presetId) : IGodModePresetChangedAutomationPipelineTrigger { [JsonIgnore] public string DisplayName => Resource.GodModePresetChangedAutomationPipelineTrigger_DisplayName; public Guid PresetId { get; } = presetId; public Task IsMatchingEvent(IAutomationEvent automationEvent) { if (automationEvent is not CustomModePresetAutomationEvent e) return Task.FromResult(false); return Task.FromResult(e.Id == PresetId); } public async Task IsMatchingState() { var controller = IoCContainer.Resolve(); return PresetId == await controller.GetActivePresetIdAsync().ConfigureAwait(false); } public void UpdateEnvironment(AutomationEnvironment environment) { /* Ignored */ } public IAutomationPipelineTrigger DeepCopy() => new GodModePresetChangedAutomationPipelineTrigger(PresetId); public IGodModePresetChangedAutomationPipelineTrigger DeepCopy(Guid presetId) => new GodModePresetChangedAutomationPipelineTrigger(presetId); public override bool Equals(object? obj) { return obj is GodModePresetChangedAutomationPipelineTrigger t && PresetId == t.PresetId; } public override int GetHashCode() => HashCode.Combine(PresetId); public override string ToString() => $"{nameof(PresetId)}: {PresetId}"; } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Pipeline/Triggers/HDROffAutomationPipelineTrigger.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Automation.Resources; using LenovoLegionToolkit.Lib.Listeners; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Pipeline.Triggers; public class HDROffAutomationPipelineTrigger : IHDRPipelineTrigger { [JsonIgnore] public string DisplayName => Resource.HDROffAutomationPipelineTrigger_DisplayName; public Task IsMatchingEvent(IAutomationEvent automationEvent) { var result = automationEvent is HDRAutomationEvent { IsHDROn: false }; return Task.FromResult(result); } public Task IsMatchingState() { var listener = IoCContainer.Resolve(); var result = listener.IsHDROn; return Task.FromResult(result.HasValue && !result.Value); } public void UpdateEnvironment(AutomationEnvironment environment) => environment.HDROn = false; public IAutomationPipelineTrigger DeepCopy() => new HDROffAutomationPipelineTrigger(); public override bool Equals(object? obj) => obj is HDROffAutomationPipelineTrigger; public override int GetHashCode() => HashCode.Combine(DisplayName); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Pipeline/Triggers/HDROnAutomationPipelineTrigger.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Automation.Resources; using LenovoLegionToolkit.Lib.Listeners; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Pipeline.Triggers; public class HDROnAutomationPipelineTrigger : IHDRPipelineTrigger { [JsonIgnore] public string DisplayName => Resource.HDROnAutomationPipelineTrigger_DisplayName; public Task IsMatchingEvent(IAutomationEvent automationEvent) { var result = automationEvent is HDRAutomationEvent { IsHDROn: true }; return Task.FromResult(result); } public Task IsMatchingState() { var listener = IoCContainer.Resolve(); var result = listener.IsHDROn; return Task.FromResult(result.HasValue && result.Value); } public void UpdateEnvironment(AutomationEnvironment environment) => environment.HDROn = true; public IAutomationPipelineTrigger DeepCopy() => new HDROnAutomationPipelineTrigger(); public override bool Equals(object? obj) => obj is HDROnAutomationPipelineTrigger; public override int GetHashCode() => HashCode.Combine(DisplayName); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Pipeline/Triggers/IAutomationPipelineTrigger.cs ================================================ using System; using System.Threading.Tasks; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Pipeline.Triggers; public interface IAutomationPipelineTrigger { [JsonIgnore] string DisplayName { get; } Task IsMatchingEvent(IAutomationEvent automationEvent); Task IsMatchingState(); void UpdateEnvironment(AutomationEnvironment environment); IAutomationPipelineTrigger DeepCopy(); } public interface IDisallowDuplicatesAutomationPipelineTrigger : IAutomationPipelineTrigger; public interface ICompositeAutomationPipelineTrigger : IAutomationPipelineTrigger { public IAutomationPipelineTrigger[] Triggers { get; } } public interface IHDRPipelineTrigger : IDisallowDuplicatesAutomationPipelineTrigger; public interface INativeWindowsMessagePipelineTrigger : IAutomationPipelineTrigger; public interface IDeviceAutomationPipelineTrigger : INativeWindowsMessagePipelineTrigger { string[] InstanceIds { get; } IDeviceAutomationPipelineTrigger DeepCopy(string[] instanceIds); } public interface IOnStartupAutomationPipelineTrigger : IDisallowDuplicatesAutomationPipelineTrigger; public interface IOnResumeAutomationPipelineTrigger : IDisallowDuplicatesAutomationPipelineTrigger; public interface IPowerStateAutomationPipelineTrigger : IDisallowDuplicatesAutomationPipelineTrigger; public interface IPowerModeAutomationPipelineTrigger : IAutomationPipelineTrigger { PowerModeState PowerModeState { get; } IPowerModeAutomationPipelineTrigger DeepCopy(PowerModeState powerModeState); } public interface IGodModePresetChangedAutomationPipelineTrigger : IAutomationPipelineTrigger { Guid PresetId { get; } IGodModePresetChangedAutomationPipelineTrigger DeepCopy(Guid powerModeState); } public interface IGameAutomationPipelineTrigger : IDisallowDuplicatesAutomationPipelineTrigger; public interface IProcessesAutomationPipelineTrigger : IAutomationPipelineTrigger { ProcessInfo[] Processes { get; } IProcessesAutomationPipelineTrigger DeepCopy(ProcessInfo[] processes); } public interface ISessionLockPipelineTrigger : IDisallowDuplicatesAutomationPipelineTrigger; public interface ISessionUnlockPipelineTrigger : IDisallowDuplicatesAutomationPipelineTrigger; public interface ITimeAutomationPipelineTrigger : IAutomationPipelineTrigger { bool IsSunrise { get; } bool IsSunset { get; } Time? Time { get; } DayOfWeek[] Days { get; } ITimeAutomationPipelineTrigger DeepCopy(bool isSunrise, bool isSunset, Time? time, DayOfWeek[] day); } public interface IUserInactivityPipelineTrigger : IAutomationPipelineTrigger { TimeSpan InactivityTimeSpan { get; } IUserInactivityPipelineTrigger DeepCopy(TimeSpan timeSpan); } public interface IWiFiConnectedPipelineTrigger : IAutomationPipelineTrigger { string[] Ssids { get; } IWiFiConnectedPipelineTrigger DeepCopy(string[] ssids); } public interface IWiFiDisconnectedPipelineTrigger : IDisallowDuplicatesAutomationPipelineTrigger; public interface IPeriodicAutomationPipelineTrigger : IAutomationPipelineTrigger { public TimeSpan Period { get; } IPeriodicAutomationPipelineTrigger DeepCopy(TimeSpan period); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Pipeline/Triggers/LidClosedAutomationPipelineTrigger.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Automation.Resources; using LenovoLegionToolkit.Lib.Listeners; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Pipeline.Triggers; public class LidClosedAutomationPipelineTrigger : INativeWindowsMessagePipelineTrigger, IDisallowDuplicatesAutomationPipelineTrigger { [JsonIgnore] public string DisplayName => Resource.LidClosedAutomationPipelineTrigger_DisplayName; public Task IsMatchingEvent(IAutomationEvent automationEvent) { var result = automationEvent is NativeWindowsMessageEvent { Message: NativeWindowsMessage.LidClosed }; return Task.FromResult(result); } public Task IsMatchingState() { var listener = IoCContainer.Resolve(); var result = !listener.IsLidOpen; return Task.FromResult(result); } public void UpdateEnvironment(AutomationEnvironment environment) => environment.LidOpen = false; public IAutomationPipelineTrigger DeepCopy() => new LidClosedAutomationPipelineTrigger(); public override bool Equals(object? obj) => obj is LidClosedAutomationPipelineTrigger; public override int GetHashCode() => HashCode.Combine(DisplayName); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Pipeline/Triggers/LidOpenedAutomationPipelineTrigger.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Automation.Resources; using LenovoLegionToolkit.Lib.Listeners; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Pipeline.Triggers; public class LidOpenedAutomationPipelineTrigger : INativeWindowsMessagePipelineTrigger, IDisallowDuplicatesAutomationPipelineTrigger { [JsonIgnore] public string DisplayName => Resource.LidOpenedAutomationPipelineTrigger_DisplayName; public Task IsMatchingEvent(IAutomationEvent automationEvent) { var result = automationEvent is NativeWindowsMessageEvent { Message: NativeWindowsMessage.LidOpened }; return Task.FromResult(result); } public Task IsMatchingState() { var listener = IoCContainer.Resolve(); var result = listener.IsLidOpen; return Task.FromResult(result); } public void UpdateEnvironment(AutomationEnvironment environment) => environment.LidOpen = true; public IAutomationPipelineTrigger DeepCopy() => new LidOpenedAutomationPipelineTrigger(); public override bool Equals(object? obj) => obj is LidOpenedAutomationPipelineTrigger; public override int GetHashCode() => HashCode.Combine(DisplayName); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Pipeline/Triggers/LowWattageACAdapterConnectedAutomationPipelineTrigger.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Automation.Resources; using LenovoLegionToolkit.Lib.System; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Pipeline.Triggers; public class LowWattageACAdapterConnectedAutomationPipelineTrigger : IPowerStateAutomationPipelineTrigger { [JsonIgnore] public string DisplayName => Resource.LowWattageACAdapterConnectedAutomationPipelineTrigger_DisplayName; public async Task IsMatchingEvent(IAutomationEvent automationEvent) { if (automationEvent is not (PowerStateAutomationEvent or StartupAutomationEvent)) return false; var result = await Power.IsPowerAdapterConnectedAsync().ConfigureAwait(false); return result == PowerAdapterStatus.ConnectedLowWattage; } public async Task IsMatchingState() { var result = await Power.IsPowerAdapterConnectedAsync().ConfigureAwait(false); return result == PowerAdapterStatus.ConnectedLowWattage; } public void UpdateEnvironment(AutomationEnvironment environment) { environment.AcAdapterConnected = true; environment.LowPowerAcAdapter = true; } public IAutomationPipelineTrigger DeepCopy() => new LowWattageACAdapterConnectedAutomationPipelineTrigger(); public override bool Equals(object? obj) => obj is LowWattageACAdapterConnectedAutomationPipelineTrigger; public override int GetHashCode() => HashCode.Combine(DisplayName); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Pipeline/Triggers/OnResumeAutomationPipelineTrigger.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Automation.Resources; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Pipeline.Triggers; public class OnResumeAutomationPipelineTrigger : IOnResumeAutomationPipelineTrigger { [JsonIgnore] public string DisplayName => Resource.OnResumeAutomationPipelineTrigger_DisplayName; public Task IsMatchingEvent(IAutomationEvent automationEvent) { return Task.FromResult(automationEvent is PowerStateAutomationEvent { PowerStateEvent: PowerStateEvent.Resume, PowerAdapterStateChanged: false }); } public Task IsMatchingState() => Task.FromResult(false); public void UpdateEnvironment(AutomationEnvironment environment) => environment.Resume = true; public IAutomationPipelineTrigger DeepCopy() => new OnResumeAutomationPipelineTrigger(); public override bool Equals(object? obj) => obj is OnResumeAutomationPipelineTrigger; public override int GetHashCode() => HashCode.Combine(DisplayName); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Pipeline/Triggers/OnStartupAutomationPipelineTrigger.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Automation.Resources; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Pipeline.Triggers; public class OnStartupAutomationPipelineTrigger : IOnStartupAutomationPipelineTrigger { [JsonIgnore] public string DisplayName => Resource.OnStartupAutomationPipelineTrigger_DisplayName; public Task IsMatchingEvent(IAutomationEvent automationEvent) { return Task.FromResult(automationEvent is StartupAutomationEvent); } public Task IsMatchingState() => Task.FromResult(false); public void UpdateEnvironment(AutomationEnvironment environment) => environment.Startup = true; public IAutomationPipelineTrigger DeepCopy() => new OnStartupAutomationPipelineTrigger(); public override bool Equals(object? obj) => obj is OnStartupAutomationPipelineTrigger; public override int GetHashCode() => HashCode.Combine(DisplayName); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Pipeline/Triggers/PeriodicAutomationPipelineTrigger.cs ================================================ using System; using System.Text.Json.Serialization; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Automation.Resources; namespace LenovoLegionToolkit.Lib.Automation.Pipeline.Triggers; [method: JsonConstructor] public class PeriodicAutomationPipelineTrigger(TimeSpan period) : IPeriodicAutomationPipelineTrigger { public string DisplayName => Resource.PeriodicActionPipelineTrigger_DisplayName; public TimeSpan Period { get; } = period; public Task IsMatchingEvent(IAutomationEvent automationEvent) { return automationEvent is not TimeAutomationEvent ? Task.FromResult(false) : IsMatching(); } public Task IsMatchingState() => IsMatching(); public IAutomationPipelineTrigger DeepCopy() => new PeriodicAutomationPipelineTrigger(Period); public IPeriodicAutomationPipelineTrigger DeepCopy(TimeSpan period) => new PeriodicAutomationPipelineTrigger(period); private Task IsMatching() { var currentDayMinutes = (int)DateTime.Now.TimeOfDay.TotalMinutes; var isPeriod = currentDayMinutes % Period.TotalMinutes == 0; return Task.FromResult(isPeriod); } public void UpdateEnvironment(AutomationEnvironment environment) => environment.Period = Period; } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Pipeline/Triggers/PowerModeAutomationPipelineTrigger.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Automation.Resources; using LenovoLegionToolkit.Lib.Features; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Pipeline.Triggers; [method: JsonConstructor] public class PowerModeAutomationPipelineTrigger(PowerModeState powerModeState) : IPowerModeAutomationPipelineTrigger { public string DisplayName => Resource.PowerModeAutomationPipelineTrigger_DisplayName; public PowerModeState PowerModeState { get; } = powerModeState; public Task IsMatchingEvent(IAutomationEvent automationEvent) { if (automationEvent is not PowerModeAutomationEvent e) return Task.FromResult(false); var result = e.PowerModeState == PowerModeState; return Task.FromResult(result); } public async Task IsMatchingState() { var feature = IoCContainer.Resolve(); return await feature.GetStateAsync().ConfigureAwait(false) == PowerModeState; } public void UpdateEnvironment(AutomationEnvironment environment) => environment.PowerMode = PowerModeState; public IAutomationPipelineTrigger DeepCopy() => new PowerModeAutomationPipelineTrigger(PowerModeState); public IPowerModeAutomationPipelineTrigger DeepCopy(PowerModeState powerModeState) => new PowerModeAutomationPipelineTrigger(powerModeState); public override bool Equals(object? obj) { return obj is PowerModeAutomationPipelineTrigger t && PowerModeState == t.PowerModeState; } public override int GetHashCode() => HashCode.Combine(PowerModeState); public override string ToString() => $"{nameof(PowerModeState)}: {PowerModeState}"; } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Pipeline/Triggers/ProcessesAreRunningAutomationPipelineTrigger.cs ================================================ using System; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Automation.Resources; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Utils; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Pipeline.Triggers; [method: JsonConstructor] public class ProcessesAreRunningAutomationPipelineTrigger(ProcessInfo[]? processes) : IProcessesAutomationPipelineTrigger { public string DisplayName => Resource.ProcessesAreRunningAutomationPipelineTrigger_DisplayName; public ProcessInfo[] Processes { get; } = processes ?? []; public Task IsMatchingEvent(IAutomationEvent automationEvent) { if (automationEvent is not ProcessAutomationEvent { Type: ProcessEventInfoType.Started } e) return Task.FromResult(false); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Checking for {e.ProcessInfo.Name}... [processes={string.Join(",", Processes.Select(p => p.Name))}]"); if (!Processes.Contains(e.ProcessInfo) && !Processes.Select(p => p.Name).Contains(e.ProcessInfo.Name)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Process name {e.ProcessInfo.Name} not in the list."); return Task.FromResult(false); } var result = Processes.SelectMany(p => Process.GetProcessesByName(p.Name)).Any(); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Process name {e.ProcessInfo.Name} found in process list: {result}."); return Task.FromResult(result); } public Task IsMatchingState() { var result = Processes.SelectMany(p => Process.GetProcessesByName(p.Name)).Any(); return Task.FromResult(result); } public void UpdateEnvironment(AutomationEnvironment environment) { environment.ProcessesStarted = true; environment.Processes = Processes; } public IAutomationPipelineTrigger DeepCopy() => new ProcessesAreRunningAutomationPipelineTrigger(Processes); public IProcessesAutomationPipelineTrigger DeepCopy(ProcessInfo[] processes) => new ProcessesAreRunningAutomationPipelineTrigger(processes); public override bool Equals(object? obj) { return obj is ProcessesAreRunningAutomationPipelineTrigger t && Processes.SequenceEqual(t.Processes); } public override int GetHashCode() { var hc = new HashCode(); Processes.ForEach(p => hc.Add(p)); return hc.ToHashCode(); } public override string ToString() => $"{nameof(Processes)}: {string.Join(", ", Processes)}"; } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Pipeline/Triggers/ProcessesStopRunningAutomationPipelineTrigger.cs ================================================ using System; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Automation.Resources; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Utils; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Pipeline.Triggers; [method: JsonConstructor] public class ProcessesStopRunningAutomationPipelineTrigger(ProcessInfo[]? processes) : IProcessesAutomationPipelineTrigger { public string DisplayName => Resource.ProcessesStopRunningAutomationPipelineTrigger_DisplayName; public ProcessInfo[] Processes { get; } = processes ?? []; public Task IsMatchingEvent(IAutomationEvent automationEvent) { if (automationEvent is not ProcessAutomationEvent { Type: ProcessEventInfoType.Stopped } e) return Task.FromResult(false); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Checking for {e.ProcessInfo.Name}... [processes={string.Join(", ", Processes.Select(p => p.Name))}]"); if (!Processes.Contains(e.ProcessInfo) && !Processes.Select(p => p.Name).Contains(e.ProcessInfo.Name)) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Process name {e.ProcessInfo.Name} not in the list."); return Task.FromResult(false); } var result = Processes.SelectMany(p => Process.GetProcessesByName(p.Name)).IsEmpty(); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Process name {e.ProcessInfo.Name} found in process list: {!result}."); return Task.FromResult(result); } public Task IsMatchingState() { var result = Processes.SelectMany(p => Process.GetProcessesByName(p.Name)).IsEmpty(); return Task.FromResult(result); } public void UpdateEnvironment(AutomationEnvironment environment) { environment.ProcessesStarted = false; environment.Processes = Processes; } public IAutomationPipelineTrigger DeepCopy() => new ProcessesStopRunningAutomationPipelineTrigger(Processes); public IProcessesAutomationPipelineTrigger DeepCopy(ProcessInfo[] processes) => new ProcessesStopRunningAutomationPipelineTrigger(processes); public override bool Equals(object? obj) { return obj is ProcessesStopRunningAutomationPipelineTrigger t && Processes.SequenceEqual(t.Processes); } public override int GetHashCode() { var hc = new HashCode(); Processes.ForEach(p => hc.Add(p)); return hc.ToHashCode(); } public override string ToString() => $"{nameof(Processes)}: {string.Join(", ", Processes)}"; } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Pipeline/Triggers/SessionLockAutomationPipelineTrigger.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Automation.Resources; using LenovoLegionToolkit.Lib.Listeners; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Pipeline.Triggers; public class SessionLockAutomationPipelineTrigger : ISessionLockPipelineTrigger { [JsonIgnore] public string DisplayName => Resource.SessionLockAutomationPipelineTrigger_DisplayName; public Task IsMatchingEvent(IAutomationEvent automationEvent) { var result = automationEvent is SessionLockUnlockAutomationEvent { Locked: true }; return Task.FromResult(result); } public Task IsMatchingState() { var listener = IoCContainer.Resolve(); var result = listener.IsLocked; return Task.FromResult(result.HasValue && result.Value); } public void UpdateEnvironment(AutomationEnvironment environment) => environment.SessionLocked = true; public IAutomationPipelineTrigger DeepCopy() => new SessionLockAutomationPipelineTrigger(); public override bool Equals(object? obj) => obj is SessionLockAutomationPipelineTrigger; public override int GetHashCode() => HashCode.Combine(DisplayName); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Pipeline/Triggers/SessionUnlockAutomationPipelineTrigger.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Automation.Resources; using LenovoLegionToolkit.Lib.Listeners; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Pipeline.Triggers; public class SessionUnlockAutomationPipelineTrigger : ISessionUnlockPipelineTrigger { [JsonIgnore] public string DisplayName => Resource.SessionUnlockAutomationPipelineTrigger_DisplayName; public Task IsMatchingEvent(IAutomationEvent automationEvent) { var result = automationEvent is SessionLockUnlockAutomationEvent { Locked: false }; return Task.FromResult(result); } public Task IsMatchingState() { var listener = IoCContainer.Resolve(); var result = listener.IsLocked; return Task.FromResult(result.HasValue && !result.Value); } public void UpdateEnvironment(AutomationEnvironment environment) => environment.SessionLocked = false; public IAutomationPipelineTrigger DeepCopy() => new SessionUnlockAutomationPipelineTrigger(); public override bool Equals(object? obj) => obj is SessionUnlockAutomationPipelineTrigger; public override int GetHashCode() => HashCode.Combine(DisplayName); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Pipeline/Triggers/TimeAutomationPipelineTrigger.cs ================================================ using System; using System.Linq; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Automation.Resources; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Utils; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Pipeline.Triggers; [method: JsonConstructor] public class TimeAutomationPipelineTrigger(bool isSunrise, bool isSunset, Time? time, DayOfWeek[]? days) : ITimeAutomationPipelineTrigger { public bool IsSunrise { get; } = isSunrise; public bool IsSunset { get; } = isSunset; public Time? Time { get; } = time; public DayOfWeek[] Days { get; } = days ?? []; public string DisplayName => Resource.TimeAutomationPipelineTrigger_DisplayName; private readonly SunriseSunset _sunriseSunset = IoCContainer.Resolve(); public async Task IsMatchingEvent(IAutomationEvent automationEvent) { if (automationEvent is not TimeAutomationEvent e) return false; return await IsMatching(e.Time, e.Day).ConfigureAwait(false); } public async Task IsMatchingState() { var now = DateTime.UtcNow; var time = new Time(now.Hour, now.Minute); var day = now.DayOfWeek; return await IsMatching(time, day).ConfigureAwait(false); } public void UpdateEnvironment(AutomationEnvironment environment) { environment.IsSunset = IsSunset; environment.IsSunrise = IsSunrise; environment.Time = Time; environment.Days = Days; } private async Task IsMatching(Time time, DayOfWeek dayOfWeek) { if (Time == time && (Days.IsEmpty() || Days.Contains(dayOfWeek))) return true; var (sunrise, sunset) = await _sunriseSunset.GetSunriseSunsetAsync().ConfigureAwait(false); if (IsSunrise && sunrise == time) return true; if (IsSunset && sunset == time) return true; return false; } public IAutomationPipelineTrigger DeepCopy() => new TimeAutomationPipelineTrigger(IsSunrise, IsSunset, Time, Days); public ITimeAutomationPipelineTrigger DeepCopy(bool isSunrise, bool isSunset, Time? time, DayOfWeek[] days) => new TimeAutomationPipelineTrigger(isSunrise, isSunset, time, days); public override bool Equals(object? obj) { return obj is TimeAutomationPipelineTrigger t && IsSunrise == t.IsSunrise && IsSunset == t.IsSunset && Time == t.Time && Days == t.Days; } public override int GetHashCode() => HashCode.Combine(IsSunrise, IsSunset, Time, Days); public override string ToString() => $"{nameof(IsSunrise)}: {IsSunrise}," + $" {nameof(IsSunset)}: {IsSunset}," + $" {nameof(Time)}: {Time}," + $" {nameof(Days)}: {Days}"; } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Pipeline/Triggers/UserInactivityAutomationPipelineTrigger.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.AutoListeners; using LenovoLegionToolkit.Lib.Automation.Resources; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Pipeline.Triggers; [method: JsonConstructor] public class UserInactivityAutomationPipelineTrigger(TimeSpan inactivityTimeSpan) : IUserInactivityPipelineTrigger { public string DisplayName => InactivityTimeSpan == TimeSpan.Zero ? Resource.UserInactivityAutomationPipelineTrigger_DisplayName_Zero : Resource.UserInactivityAutomationPipelineTrigger_DisplayName; public TimeSpan InactivityTimeSpan { get; } = inactivityTimeSpan; public Task IsMatchingEvent(IAutomationEvent automationEvent) { if (automationEvent is not UserInactivityAutomationEvent e) return Task.FromResult(false); var result = InactivityTimeSpan == e.InactivityTimeSpan; return Task.FromResult(result); } public Task IsMatchingState() { var listener = IoCContainer.Resolve(); var result = InactivityTimeSpan == listener.InactivityTimeSpan; return Task.FromResult(result); } public void UpdateEnvironment(AutomationEnvironment environment) => environment.UserActive = InactivityTimeSpan == TimeSpan.Zero; public IAutomationPipelineTrigger DeepCopy() => new UserInactivityAutomationPipelineTrigger(InactivityTimeSpan); public IUserInactivityPipelineTrigger DeepCopy(TimeSpan timeSpan) => new UserInactivityAutomationPipelineTrigger(timeSpan); public override bool Equals(object? obj) => obj is UserInactivityAutomationPipelineTrigger t && InactivityTimeSpan == t.InactivityTimeSpan; public override int GetHashCode() => InactivityTimeSpan.GetHashCode(); public override string ToString() => $"{nameof(InactivityTimeSpan)}: {InactivityTimeSpan}"; } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Pipeline/Triggers/WiFiConnectedAutomationPipelineTrigger.cs ================================================ using System; using System.Linq; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Automation.Resources; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.System; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Pipeline.Triggers; [method: JsonConstructor] public class WiFiConnectedAutomationPipelineTrigger(string[] ssids) : IWiFiConnectedPipelineTrigger { public string DisplayName => Resource.WiFiConnectedAutomationPipelineTrigger_DisplayName; public string[] Ssids { get; } = ssids; public Task IsMatchingEvent(IAutomationEvent automationEvent) { if (automationEvent is not WiFiAutomationEvent { IsConnected: true } e) return Task.FromResult(false); return Task.FromResult(Ssids.IsEmpty() || Ssids.Contains(e.Ssid)); } public Task IsMatchingState() { var ssid = WiFi.GetConnectedNetworkSsid(); if (Ssids.IsEmpty() && ssid is not null) return Task.FromResult(true); return Task.FromResult(Ssids.Contains(ssid)); } public void UpdateEnvironment(AutomationEnvironment environment) { environment.WiFiConnected = true; environment.WiFiSsid = string.Join(",", Ssids); } public IAutomationPipelineTrigger DeepCopy() => new WiFiConnectedAutomationPipelineTrigger(Ssids); public IWiFiConnectedPipelineTrigger DeepCopy(string[] ssids) => new WiFiConnectedAutomationPipelineTrigger(ssids); public override bool Equals(object? obj) => obj is WiFiConnectedAutomationPipelineTrigger t && Ssids.SequenceEqual(t.Ssids); public override int GetHashCode() => HashCode.Combine(Ssids); public override string ToString() => $"{nameof(Ssids)}: {string.Join(",", Ssids)}"; } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Pipeline/Triggers/WiFiDisconnectedAutomationPipelineTrigger.cs ================================================ using System; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Automation.Resources; using LenovoLegionToolkit.Lib.System; namespace LenovoLegionToolkit.Lib.Automation.Pipeline.Triggers; public class WiFiDisconnectedAutomationPipelineTrigger : IWiFiDisconnectedPipelineTrigger { public string DisplayName => Resource.WiFiDisconnectedAutomationPipelineTrigger_DisplayName; public Task IsMatchingEvent(IAutomationEvent automationEvent) { var result = automationEvent is WiFiAutomationEvent { IsConnected: false }; return Task.FromResult(result); } public Task IsMatchingState() { var ssid = WiFi.GetConnectedNetworkSsid(); return Task.FromResult(ssid is null); } public void UpdateEnvironment(AutomationEnvironment environment) => environment.WiFiConnected = false; public IAutomationPipelineTrigger DeepCopy() => new WiFiDisconnectedAutomationPipelineTrigger(); public override bool Equals(object? obj) => obj is WiFiDisconnectedAutomationPipelineTrigger; public override int GetHashCode() => HashCode.Combine(DisplayName); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Resources/Resource.Designer.cs ================================================ //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace LenovoLegionToolkit.Lib.Automation.Resources { using System; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class Resource { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Resource() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] public static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LenovoLegionToolkit.Lib.Automation.Resources.Resource", typeof(Resource).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] public static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Looks up a localized string similar to When AC power adapter is connected. /// public static string ACAdapterConnectedAutomationPipelineTrigger_DisplayName { get { return ResourceManager.GetString("ACAdapterConnectedAutomationPipelineTrigger_DisplayName", resourceCulture); } } /// /// Looks up a localized string similar to When AC power adapter is disconnected. /// public static string ACAdapterDisconnectedAutomationPipelineTrigger_DisplayName { get { return ResourceManager.GetString("ACAdapterDisconnectedAutomationPipelineTrigger_DisplayName", resourceCulture); } } /// /// Looks up a localized string similar to Kill apps. /// public static string DeactivateGPUAutomationStepState_KillApps { get { return ResourceManager.GetString("DeactivateGPUAutomationStepState_KillApps", resourceCulture); } } /// /// Looks up a localized string similar to Restart GPU. /// public static string DeactivateGPUAutomationStepState_RestartGPU { get { return ResourceManager.GetString("DeactivateGPUAutomationStepState_RestartGPU", resourceCulture); } } /// /// Looks up a localized string similar to Deactivate GPU. /// public static string DeactivateGpuQuickAction_Title { get { return ResourceManager.GetString("DeactivateGpuQuickAction_Title", resourceCulture); } } /// /// Looks up a localized string similar to {0} second. /// public static string Delay_Second { get { return ResourceManager.GetString("Delay_Second", resourceCulture); } } /// /// Looks up a localized string similar to {0} seconds. /// public static string Delay_Second_Many { get { return ResourceManager.GetString("Delay_Second_Many", resourceCulture); } } /// /// Looks up a localized string similar to When device is connected. /// public static string DeviceConnectedAutomationPipelineTrigger_DisplayName { get { return ResourceManager.GetString("DeviceConnectedAutomationPipelineTrigger_DisplayName", resourceCulture); } } /// /// Looks up a localized string similar to When device is disconnected. /// public static string DeviceDisconnectedAutomationPipelineTrigger_DisplayName { get { return ResourceManager.GetString("DeviceDisconnectedAutomationPipelineTrigger_DisplayName", resourceCulture); } } /// /// Looks up a localized string similar to When displays turn off. /// public static string DisplayOffAutomationPipelineTrigger_DisplayName { get { return ResourceManager.GetString("DisplayOffAutomationPipelineTrigger_DisplayName", resourceCulture); } } /// /// Looks up a localized string similar to When displays turn on. /// public static string DisplayOnAutomationPipelineTrigger_DisplayName { get { return ResourceManager.GetString("DisplayOnAutomationPipelineTrigger_DisplayName", resourceCulture); } } /// /// Looks up a localized string similar to When external display is connected. /// public static string ExternalDisplayConnectedAutomationPipelineTrigger_DisplayName { get { return ResourceManager.GetString("ExternalDisplayConnectedAutomationPipelineTrigger_DisplayName", resourceCulture); } } /// /// Looks up a localized string similar to When external display is disconnected. /// public static string ExternalDisplayDisconnectedAutomationPipelineTrigger_DisplayName { get { return ResourceManager.GetString("ExternalDisplayDisconnectedAutomationPipelineTrigger_DisplayName", resourceCulture); } } /// /// Looks up a localized string similar to When game is running. /// public static string GamesAreRunningAutomationPipelineTrigger_DisplayName { get { return ResourceManager.GetString("GamesAreRunningAutomationPipelineTrigger_DisplayName", resourceCulture); } } /// /// Looks up a localized string similar to When game closes. /// public static string GamesStopAutomationPipelineTrigger_DisplayName { get { return ResourceManager.GetString("GamesStopAutomationPipelineTrigger_DisplayName", resourceCulture); } } /// /// Looks up a localized string similar to When Custom Mode preset changes. /// public static string GodModePresetChangedAutomationPipelineTrigger_DisplayName { get { return ResourceManager.GetString("GodModePresetChangedAutomationPipelineTrigger_DisplayName", resourceCulture); } } /// /// Looks up a localized string similar to When HDR turns off. /// public static string HDROffAutomationPipelineTrigger_DisplayName { get { return ResourceManager.GetString("HDROffAutomationPipelineTrigger_DisplayName", resourceCulture); } } /// /// Looks up a localized string similar to When HDR turns on. /// public static string HDROnAutomationPipelineTrigger_DisplayName { get { return ResourceManager.GetString("HDROnAutomationPipelineTrigger_DisplayName", resourceCulture); } } /// /// Looks up a localized string similar to Lid closed. /// public static string LidClosedAutomationPipelineTrigger_DisplayName { get { return ResourceManager.GetString("LidClosedAutomationPipelineTrigger_DisplayName", resourceCulture); } } /// /// Looks up a localized string similar to Lid opened. /// public static string LidOpenedAutomationPipelineTrigger_DisplayName { get { return ResourceManager.GetString("LidOpenedAutomationPipelineTrigger_DisplayName", resourceCulture); } } /// /// Looks up a localized string similar to When low wattage AC power adapter is connected. /// public static string LowWattageACAdapterConnectedAutomationPipelineTrigger_DisplayName { get { return ResourceManager.GetString("LowWattageACAdapterConnectedAutomationPipelineTrigger_DisplayName", resourceCulture); } } /// /// Looks up a localized string similar to Off. /// public static string MacroAutomationStepState_Off { get { return ResourceManager.GetString("MacroAutomationStepState_Off", resourceCulture); } } /// /// Looks up a localized string similar to On. /// public static string MacroAutomationStepState_On { get { return ResourceManager.GetString("MacroAutomationStepState_On", resourceCulture); } } /// /// Looks up a localized string similar to On resume. /// public static string OnResumeAutomationPipelineTrigger_DisplayName { get { return ResourceManager.GetString("OnResumeAutomationPipelineTrigger_DisplayName", resourceCulture); } } /// /// Looks up a localized string similar to On startup. /// public static string OnStartupAutomationPipelineTrigger_DisplayName { get { return ResourceManager.GetString("OnStartupAutomationPipelineTrigger_DisplayName", resourceCulture); } } /// /// Looks up a localized string similar to Off. /// public static string OverclockDiscreteGPUAutomationStepState_Off { get { return ResourceManager.GetString("OverclockDiscreteGPUAutomationStepState_Off", resourceCulture); } } /// /// Looks up a localized string similar to On. /// public static string OverclockDiscreteGPUAutomationStepState_On { get { return ResourceManager.GetString("OverclockDiscreteGPUAutomationStepState_On", resourceCulture); } } /// /// Looks up a localized string similar to Periodic action. /// public static string PeriodicActionPipelineTrigger_DisplayName { get { return ResourceManager.GetString("PeriodicActionPipelineTrigger_DisplayName", resourceCulture); } } /// /// Looks up a localized string similar to When Power Mode is changed. /// public static string PowerModeAutomationPipelineTrigger_DisplayName { get { return ResourceManager.GetString("PowerModeAutomationPipelineTrigger_DisplayName", resourceCulture); } } /// /// Looks up a localized string similar to When app starts. /// public static string ProcessesAreRunningAutomationPipelineTrigger_DisplayName { get { return ResourceManager.GetString("ProcessesAreRunningAutomationPipelineTrigger_DisplayName", resourceCulture); } } /// /// Looks up a localized string similar to When app closes. /// public static string ProcessesStopRunningAutomationPipelineTrigger_DisplayName { get { return ResourceManager.GetString("ProcessesStopRunningAutomationPipelineTrigger_DisplayName", resourceCulture); } } /// /// Looks up a localized string similar to Session locked. /// public static string SessionLockAutomationPipelineTrigger_DisplayName { get { return ResourceManager.GetString("SessionLockAutomationPipelineTrigger_DisplayName", resourceCulture); } } /// /// Looks up a localized string similar to Session unlocked. /// public static string SessionUnlockAutomationPipelineTrigger_DisplayName { get { return ResourceManager.GetString("SessionUnlockAutomationPipelineTrigger_DisplayName", resourceCulture); } } /// /// Looks up a localized string similar to At specified time. /// public static string TimeAutomationPipelineTrigger_DisplayName { get { return ResourceManager.GetString("TimeAutomationPipelineTrigger_DisplayName", resourceCulture); } } /// /// Looks up a localized string similar to When user becomes inactive. /// public static string UserInactivityAutomationPipelineTrigger_DisplayName { get { return ResourceManager.GetString("UserInactivityAutomationPipelineTrigger_DisplayName", resourceCulture); } } /// /// Looks up a localized string similar to When user becomes active. /// public static string UserInactivityAutomationPipelineTrigger_DisplayName_Zero { get { return ResourceManager.GetString("UserInactivityAutomationPipelineTrigger_DisplayName_Zero", resourceCulture); } } /// /// Looks up a localized string similar to When Wi-Fi is connected. /// public static string WiFiConnectedAutomationPipelineTrigger_DisplayName { get { return ResourceManager.GetString("WiFiConnectedAutomationPipelineTrigger_DisplayName", resourceCulture); } } /// /// Looks up a localized string similar to When Wi-Fi is disconnected. /// public static string WiFiDisconnectedAutomationPipelineTrigger_DisplayName { get { return ResourceManager.GetString("WiFiDisconnectedAutomationPipelineTrigger_DisplayName", resourceCulture); } } } } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Resources/Resource.ar.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 عند اتصال كابل الشحن عند ازالة كابل الشحن قتل التطبيقات أعادة تشغيل كرت الشاشة {0} ثوانى {0} ثانية عند اتصال كابل شحن ذو قوة كهربائية منخفضة عند بدء التشغيل عند تغير وضع الطاقة عندما يبدأ التطبيق عندما يغلق تطبيق عند أوقات محددة عند توصيل شاشة خارجية عند فصل الشاشة الخارجية عند تشغيل الشاشات عندما تنطفئ شاشات عندما تغلق اللعبة عندما تكون اللعبة قيد التشغيل عندما يصبح المستخدم نشطا عندما يصبح المستخدم غير نشط إلغاء تفعيل GPU The display name of the default Quick Action that is presented to the user upon first installation. إيقاف مفعل عند توصيل واي فاي عند انقطاع اتصال واي فاي إيقاف مفعل عند فصل الجهاز عند توصيل الجهاز ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Resources/Resource.bg.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 При свързване на зарядно При изваждане на зарядното Спри приложенията Рестартирай видео картата {0} секунда {0} секунди При свързване на зарядно с ниска мощност При стартиране При промяна в режима на захранване При отваряне на приложение При затваряне на приложение В определено време При свързване на външен дисплей При откачане на външен дисплей При включване на дисплеи При изключване на дисплеи При отваряне на капака При затваряне на капака При затваряне на игра По време на игра Когато потребителят стане активен Когато потребителят стане неактивен Деактивиране на видеокартата The display name of the default Quick Action that is presented to the user upon first installation. Изключен Включен При промяна в предварително зададената настройка на режима по избор Периодично действие The display name of the periodic automation action. При свързване към Wi-Fi мрежа При изключване на Wi-Fi мрежа При възобновяване ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Resources/Resource.bs.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Kad je punjač priključen Kad je punjač isključen Ugasi aplikacije Ponovo pokreni grafičku {0} sekunda {0} sekundi Kad je punjač manje snage priključen Pri pokretanju Kada je aplikacija ugašen U određeno vrijeme ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Resources/Resource.ca.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Resources/Resource.cs.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Když je AC nabíječka připojena Když je AC nabíječka odpojena Ukončit aplikace Restartovat GPU {0} sekund {0} sekund Když je připojena AC nabíječka s nízkým výkonem Při spuštění Když je změněn režim výkonu Při spuštění aplikace Když se aplikace ukončí V zadaný čas Při připojení externího monitoru Při odpojení externího monitoru Když se monitory zapnou Když se monitory vypnou Víko otevřeno Víko zavřeno Když se hra ukončí Když hra běží ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Resources/Resource.de.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Wenn Netzteil angeschlossen ist Wenn Netzteil nicht angeschlossen ist Anwendungen schließen GPU neustarten {0} Sekunde {0} Sekunden Wenn Netzteil mit niedriger Leistung angeschlossen ist Beim Start Wenn Energiemodus geändert wird Wenn die App startet Wenn Anwendung geschlossen wird Zur bestimmten Zeit Wenn ein externer Monitor angeschlossen wird Wenn ein externer Monitor getrennt wird Wenn Bildschirme eingeschaltet werden Wenn Bildschirme ausgeschaltet werden Deckel geöffnet Deckel geschlossen Wenn das Spiel beendet wird Wenn das Spiel ausgeführt wird Wenn ein Benutzer aktiv wird Wenn ein Benutzer inaktiv wird Deaktiviere GPU The display name of the default Quick Action that is presented to the user upon first installation. Aus An Wenn sich die Vorlage des benutzerdefinierten Modus ändert Regelmäßige Aktion The display name of the periodic automation action. Wenn eine WLAN-Verbindung besteht Wenn die WLAN-Verbindung getrennt wird Beim aufwachen Wenn HDR ausgeschaltet ist Wenn HDR eingeschaltet ist Aus An Wenn Gerät getrennt ist Wenn Gerät verbunden ist ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Resources/Resource.el.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Όταν ο φορτιστής είναι συνδεδεμένος Όταν ο φορτιστής είναι αποσυνδεδεμένος Τερματισμός εφαρμογών Επανεκκίνηση GPU {0} δευτερόλεπτο {0} δευτερόλεπτα Όταν είναι συνδεδεμένος φορτιστής χαμηλής ισχύος Κατά την εκκίνηση Όταν αλλάξει η Λειτουργία Ισχύος Όταν ξεκινά η εφαρμογή Όταν κλείνει η εφαρμογή Σε καθορισμένη ώρα Όταν συνδεθεί εξωτερική οθόνη Όταν αποσυνδεθεί εξωτερική οθόνη Όταν ανοίγουν οι οθόνες Όταν σβήνουν οι οθόνες Όταν ανοίγει το καπάκι Όταν κλείνει το καπάκι Όταν κλείνει το παιχνίδι Όταν εκτελείται το παιχνίδι Όταν ο χρήστης γίνεται ενεργός Όταν ο χρήστης γίνεται ανενεργός Απενεργοποίηση GPU The display name of the default Quick Action that is presented to the user upon first installation. Ανενεργό Ενεργό Όταν η προεπιλογή Προσαρμοσμένης Λειτουργίας αλλάζει Περιοδική δράση The display name of the periodic automation action. Όταν το Wi-Fi είναι συνδεδεμένο Όταν το Wi-Fi είναι αποσυνδεδεμένο Στη συνέχιση Όταν το HDR ενεργοποιείται Όταν το HDR απενεργοποιείται Ανενεργό Ενεργό Όταν η συσκευή είναι αποσυνδεδεμένη Όταν η συσκευή είναι συνδεδεμένη ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Resources/Resource.es.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Cuando el adaptador de corriente alterna está conectado Cuando el adaptador de corriente alterna está desconectado Terminar aplicaciones Reiniciar la GPU {0} segundo {0} segundos Al conectar el adaptador de corriente alterna a baja potencia Al iniciar Al cambiar el modo de alimentación Al iniciarse la aplicación Al cerrar la aplicación A la hora indicada Cuando se conecta una pantalla externa Cuando se desconecta una pantalla externa Al encender las pantallas Al apagar las pantallas Tapa abierta Tapa cerrada Al cerrar el juego Cuando el juego está en marcha Cuando el usuario está activo Cuando el usuario está inactivo Desactivar la GPU The display name of the default Quick Action that is presented to the user upon first installation. Apagado Encendido Al cambiar el predefinido del modo personalizado Acción periódica The display name of the periodic automation action. Si el Wi-Fi está conectado Si el Wi-Fi está desconectado En resumen Cuando se apaga HDR Cuando se enciende HDR Apagado Encendido Cuando el dispositivo está desconectado Cuando el dispositivo está conectado ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Resources/Resource.fr.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Lorsque le chargeur est connecté Lorsque le chargeur est déconnecté Tuer les applications Redémarrer le GPU {0} seconde {0} secondes Lorsqu'une faible puissance d'alimentation est connectée Au démarrage Lorsque le mode d’alimentation change Quand l'application démarre Lors de la fermeture d'une application À l’heure spécifiée Lorsque l'écran externe est connecté Lorsque l'écran externe est déconnecté Quand les écrans s'allument Quand les écrans s'éteignent Capot ouvert Capot fermé À la fermeture du jeu Pendant l'exécution du jeu Quand l'utilisateur devient actif Quand l'utilisateur devient inactif Désactiver le GPU The display name of the default Quick Action that is presented to the user upon first installation. Off On Quand le Mode Personnalisé change Action périodique The display name of the periodic automation action. Quand le Wifi est connecté Quand le Wifi est déconnecté À la reprise Lorsque le HDR est désactivé Lorsque le HDR est activé Désactivé Activé Lorsque l'appareil est déconnecté Lorsque l'appareil est connecté ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Resources/Resource.hu.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Ha a töltőadapter csatlakoztatva van Ha a töltőadapter nincs csatlakoztatva Alkalmazások kilövése Videókártya újraindítása {0} másodperc {0} másodperc Ha alacsony teljesítményű töltőadapter van csatlakoztatva Indításkor Amikor a Teljesítmény üzemmód változik Alkalmazás indításakor Alkalmazás bezárásakor Megadott időben Ha külső kijelző csatlakoztatásra kerül Ha külső kijelző lecsatlakoztatásra kerül Ha a kijelző bekapcsol Ha a kijelző kikapcsol Fedél felnyitva Fedél lehajtva Játék bezárásánál Játék futtatásánál Amikor a felhasználó aktívvá válik Amikor a felhasználó inaktívvá válik Videókártya kikapcsolása The display name of the default Quick Action that is presented to the user upon first installation. Ki Be Amikor az Egyéni üzemmód beállításai megváltoznak Rendszeres művelet The display name of the periodic automation action. Ha a Wi-Fi csatlakoztatva van Ha a Wi-Fi nincs csatlakoztatva Folytatásnál Amikor a HDR kikapcsol Amikor a HDR bekapcsol Ki Be Ha a készüléket leválasztották Ha a készülék csatlakoztatva van ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Resources/Resource.it.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Quando l'alimentatore è collegato Quando l'alimentatore è scollegato Chiudi applicazioni Riavvia GPU {0} secondo {0} secondi Quando è collegato un alimentatore di bassa potenza All'avvio Quando si cambia la modalità di alimentazione All'avvio dell'app Quando l'app si chiude All'ora specificata Quando il display esterno è collegato Quando il display esterno è scollegato Quando lo schermo si accende Quando lo schermo si spegne Coperchio aperto Coperchio chiuso Quando l'utente diventa inattivo Disattiva GPU The display name of the default Quick Action that is presented to the user upon first installation. ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Resources/Resource.ja.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ACアタブタが接続された時 ACアダプタが切断された時 アプリを強制終了 GPUを再起動 {0} 秒 {0} 秒 低出力なACアダプタが接続された時 起動した時 電源モードが変更された時 アプリケーションが起動した時 アプリケーションが終了した時 時間になったら 外部ディスプレイが接続された時 外部ディスプレイが切断された時 ディスプレイがオンになった時 ディスプレイがオフになった時 蓋が開いた時 蓋が閉じた時 ゲーム終了が終了した時 ゲームが起動した時 ユーザーがアクティブになった時 ユーザーが非アクティブになった時 GPUを非アクティブ化 The display name of the default Quick Action that is presented to the user upon first installation. オフ オン カスタムモードのプリセットが変更された時 定期的なアクション The display name of the periodic automation action. Wi-Fiが接続された時 Wi-Fiが切断された時 再開時 HDRがオフになった時 HDRがオンになった時 オフ オン デバイスが切断された時 デバイスが接続された時 ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Resources/Resource.ko.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Resources/Resource.lv.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Kad ir pievienots maiņstrāvas adapteris Kad ir atvienots maiņstrāvas adapteris Nokaut lietotnes Restartēt GPU {0} sekunde {0} sekundes Ja ir pievienots zemas jaudas maiņstrāvas adapteris Palaišanas laikā Ja tiek mainīts Enerģijas Režīms Kad aplikācija tiek palaista Kad aplikācija aizveras Noteiktā laikā Kad tiek pievienots papildus ekrāns Kad tiek atvienots papildus ekrāns Kad ekrāns ieslēdzas Kad ekrāns izslēdzas Vāks atveras Vāks aizveras Kad datorspēle aizveras Kad datorspēle ir atvērta Kad lietotājs kļūst aktīvs Kad lietotājs kļūst neaktīvs Deaktivizēt GPU The display name of the default Quick Action that is presented to the user upon first installation. Atspējots Iespējots Kad Pielāgotā Režīma priekšiestatījums izmainās ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Resources/Resource.nl-nl.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Wanneer de oplader is aangesloten Wanneer de oplader is losgekoppeld Apps stoppen GPU Opnieuw Opstarten {0} seconde {0} seconden Wanneer een wisselstroomadapter met laag wattage is aangesloten Bij Opstarten Wanneer de energiemodus wordt gewijzigd Bij starten van app Wanneer app afgesloten Op bepaalde tijd Wanneer een extern beeldscherm is aangesloten Wanneer een extern beeldscherm is losgekoppeld Wanneer de schermen ingeschakeld worden Wanneer de schermen uitgeschakeld worden Deksel geopend Deksel gesloten Wanneer spel afgesloten wordt Wanneer het spel draait Wanneer gebruiker actief wordt Wanneer gebruiker inactief wordt GPU uitschakelen The display name of the default Quick Action that is presented to the user upon first installation. Uit Aan Wanneer aangepaste modus wordt veranderd Periodieke actie The display name of the periodic automation action. Wanneer Wi-Fi verbinding is verbonden Wanneer Wi-Fi verbinding is verbroken Na het hervatten Wanneer HDR uitgeschakeld wordt Wanneer HDR ingeschakeld wordt Uit Aan Wanneer apparaat niet verbonden is Wanneer apparaat verbonden is ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Resources/Resource.pl.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Gdy zasilanie sieciowe jest podłączone Gdy zasilanie sieciowe jest odłączone Zamknij aplikacje Zrestartuj GPU {0} sekunda {0} sekund Gdy jest podłączony zasilacz o słabej mocy Przy uruchomieniu Gdy tryb zasilania ulegnie zmianie Przy uruchomieniu aplikacji Przy zamknięciu aplikacji We wskazanym czasie Gdy podłączono wyświetlacz zewnętrzny Gdy odłączono wyświetlacz zewnętrzny Gdy wyświetlacze zostaną włączone Gdy wyświetlacze zostaną wyłączone Pokrywa otwarta Pokrywa zamknięta Gdy gra się wyłączy Gdy gra jest uruchomiona Gdy użytkownik staje się aktywny Gdy użytkownik staje się nieaktywny Wyłącz GPU The display name of the default Quick Action that is presented to the user upon first installation. Wyłączone Włączone Gdy szablon trybu własnego ulegnie zmianie Akcja periodyczna The display name of the periodic automation action. Gdy połączono z Wi-Fi Gdy rozłączono z Wi-Fi Przy wybudzeniu Gdy HDR jest wyłączony Gdy HDR jest włączony Wyłączone Włączone Gdy odłączane jest urządzenie Gdy podłączane jest urządzenie ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Resources/Resource.pt-br.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Quando um carregador for conectado Quando o carregador for desconectado Fechar todas aplicações Reiniciar GPU {0} segundo {0} segundos Quando o carregador de baixa potência for conectado Ao iniciar Quando o plano de energia for mudado Quando um programa iniciar Quando um programa for fechado Em um horário específico Quando um monitor externo for conectado Quando um monitor externo for desconectado Quando a tela ligar Quando a tela desligar Tampa aberta Tampa fechada Quando um jogo fechar Quando um jogo estiver sendo executado Quando usuário ficar ativo Quando usuário ficar inativo Desativar GPU The display name of the default Quick Action that is presented to the user upon first installation. Desligado Ativado Quando uma Pré-definição de Modo Personalizado mudar Ação Periódica The display name of the periodic automation action. Quando o Wi-Fi estiver conectado Quando o Wi-Fi está desconectado Ao retomar Quando o HDR for desligado Quando o HDR for ligado Desativado Ativado Quando o dispositivo for desconectado Quando o dispositivo for conectado ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Resources/Resource.pt.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Quando o carregador for ligado Quando o carregador for desligado Fechar apps Reiniciar GPU {0} segundos {0} segundos Quando um carregador de baixa voltagem for ligado Ao ligar Quando o esquema de energia é alterado Quando uma aplicação inicia Quando a app fecha A uma hora específica Quando um monitor externo é ligado Quando um monitor externo é desligado Quando os ecrãs ligam Quando os ecrãs desligam Tampa é aberta Tampa fechada Quando um jogo fecha Quando um jogo está a rodar Quando o utilizador fica ativo Quando o utilizador fica inativo Desativar GPU The display name of the default Quick Action that is presented to the user upon first installation. Desligado Ligado Quando a predefinição do Modo Customizado muda Ação periódica The display name of the periodic automation action. Quando o Wi-Fi estiver conectado Quando o Wi-Fi estiver desconectado Ao continuar Quando o HDR for desligado Quando o HDR for ligado Desligado Ligado Quando o dispositivo estiver desconectado Quando o dispositivo estiver conectado ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Resources/Resource.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 When AC power adapter is connected When AC power adapter is disconnected Kill apps Restart GPU {0} second {0} seconds When low wattage AC power adapter is connected On startup When Power Mode is changed When app starts When app closes At specified time When external display is connected When external display is disconnected When displays turn on When displays turn off Lid opened Lid closed When game closes When game is running When user becomes active When user becomes inactive Deactivate GPU The display name of the default Quick Action that is presented to the user upon first installation. Off On When Custom Mode preset changes Periodic action The display name of the periodic automation action. When Wi-Fi is connected When Wi-Fi is disconnected On resume When HDR turns off When HDR turns on Off On When device is disconnected When device is connected Session locked Session unlocked ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Resources/Resource.ro.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Când alimentatorul este conectat Când alimentatorul este deconectat Închidere aplicații Repornire GPU {0} secundă {0} secunde Când alimentatorul de putere scăzută este conectat La pornire Când modul de putere este schimbat Când pornește aplicația Când o aplicație se închide La timpul specificat Când un afișaj extern este conectat Când un afișaj extern este deconectat Când afișajele se aprind Când afișajele se opresc Capac deschis Capac închis Când jocul se închide Când jocul rulează ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Resources/Resource.ru.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Когда блок питания подключен Когда блок питания отключен Завершить процессы Перезапустить графический драйвер {0} секунда {0} секунд Когда малопотребляющий блок питания подключен При запуске системы Когда измененен режима питания При запуске приложения При закрытии приложения В заданное время При подключении внешнего дисплея При отключении внешнего дисплея При включении дисплеев При отключении дисплеев Крышка ноутбука открыта Крышка ноутбука закрыта При закрытии игры При запущенной игре Когда пользователь проявляет активность Когда пользователь перестает проявлять активность Деактивировать GPU The display name of the default Quick Action that is presented to the user upon first installation. Откл. Вкл. Когда изменяется пресет пользовательского режима Периодическое действие The display name of the periodic automation action. Когда подключен Wi-Fi Когда отключен Wi-Fi При возобновлении При отключении HDR При включении HDR Откл. Вкл. При отсоединении устройства При соединении устройства ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Resources/Resource.sk.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Pri pripojení sieťového napájania Pri odpojení sieťového napájania Ukončiť aplikácie Reštartovať GPU {0} sekunda {0} sekúnd Pri pripojení nízkovýkonového sieťového napájania Pri štarte Pri zmene napájacieho režimu Pri štarte aplikácie Pri zatvorení aplikácie V určený čas Pri pripojení externého displeja Pri odpojení externého displeja Keď sa zapnú displeje Keď sa vypnú displeje Kryt otvorený Kryt zatvorený Pri zatvorení hry Keď je hra spustená Deaktivovať GPU The display name of the default Quick Action that is presented to the user upon first installation. ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Resources/Resource.tr.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 AC güç adaptörü bağlanınca AC güç adaptörü çıkarılınca Uygulamaları kapat GPU'yu yeniden başlat {0} saniye {0} saniye Düşük voltajlı AC güç adaptörü bağlandığında Başlangıçta Güç Modu değiştirildiğinde Uygulama başlatıldığında Uygulama kapandığında Belirli bir zamanda Harici ekran bağlandığında Harici ekran bağlantısı kesildiğinde Ekranlar açıldığında Ekranlar kapandığında Kapak açıldığında Kapak kapatıldığında Oyun kapandığında Oyun çalıştığında Kullanıcı aktif olduğunda Kullanıcı etkin olmadığında GPU Pasifleştir The display name of the default Quick Action that is presented to the user upon first installation. Kapalı Açık Özel mod ön ayarı değiştiğinde Süreli eylem The display name of the periodic automation action. Wi-Fi bağlandığında Wi-Fi bağlantısı kesildiğinde Özgeçmişte HDR kapatıldığında HDR açıldığında Kapalı Açık Cihaz bağlantısı kesildiğinde Cihaz bağlandığında ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Resources/Resource.uk.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Коли блок живлення під'єднаний Коли блок живлення від'єднаний Примусово закрити застосунки Перезапустити ГП {0} секунда {0} секунд Коли малопотужний блок живлення під'єднаний При запуску Коли режим потужності змінений При запуску застосунку Коли застосунок закрито У зазначений час Коли зовнішній дисплей підключений Коли зовнішній дисплей відключений Коли дисплей увімкнений Коли дисплей вимкнутий Кришка відкрита Кришка закрита Коли гра закривається Коли гра запущена Коли користувач стає активним Коли користувач стає неактивним Вимкнути графічний процесор The display name of the default Quick Action that is presented to the user upon first installation. Вимкнений Увімкнений Коли змінюється попереднє налаштування користувальницького режиму Періодична дія The display name of the periodic automation action. Коли під'єднано до Wi-Fi Коли від'єднано від Wi-Fi При поновленні Коли HDR вимкнено Коли HDR увімкнено Вимкнути Увімкнути Коли пристрій від'єднано Коли пристрій під'єднано ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Resources/Resource.uz-latn-uz.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Quwatlaw adapteri jalǵanǵanda Quwatlaw adapteri úzilgende Proceslerdi juwmaqlaw Grafikalıq drayverdı qayta iske qosıw {0} sekund {0} sekund Kem vattlı quwatlaw blogı jalǵanǵanda Iske túsirgende Quwatlılıq rejimi ózgergende Baǵdarlama iske túskende Baǵdarlama jabılǵanda Belgilengen waqıtta Sırtqı display qosılǵanda Sırtqı display óshirilgende Displeydi qosqanda Displeydi óshirgende Noutbuk qaqpaǵı ashıldı Noutbuk qaqpaǵı jawıldı Oyın jabılǵanda Oyın iske túsirilgende Paydalanıwshı online bolǵanda Paydalanıwshı óshik bolǵanında GIQQın isten alıw The display name of the default Quick Action that is presented to the user upon first installation. Óshik Janıq Kostyum rejimi aldınnan ornatılǵanda ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Resources/Resource.vi.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Khi kết nối với nguồn điện Khi ngắt kết nối nguồn điện Tắt app dùng GPU rời Khởi động lại GPU {0} giây {0} giây Khi kết nối với nguồn điện công suất thấp Khi khởi động Khi đổi chế độ hoạt động Khi chạy ứng dụng Khi ứng dụng được tắt đi Tại một thời điểm nhất định Khi kết nối với màn hình ngoài Khi ngắt kết nối với màn hình ngoài Khi bật màn hình Khi tắt màn hình Mở màn hình Gập màn hình Khi tắt game Khi game khởi chạy Khi người dùng có mặt Khi người dùng rời đi Ngắt GPU rời The display name of the default Quick Action that is presented to the user upon first installation. Tắt Bật Khi thay đổi thiết lập trong chế độ Tùy Chỉnh Hành động định kỳ The display name of the periodic automation action. Khi kết nối Wi-Fi Khi ngắt kết nối Wi-Fi Khi tạm dừng Khi HDR tắt Khi HDR bật Tắt Bật Khi ngắt kết nối thiết bị Khi kết nối thiết bị ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Resources/Resource.zh-hans.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 当电源适配器插入时 当电源适配器断开时 强制关闭应用 重启显卡 {0} 秒 {0} 秒 当较低功率电源适配器插入时 当开机时 当性能模式改变时 当应用程序启动时 当指定的应用关闭时 在特定的时间 当连接了外置屏幕后 当断开了外置屏幕后 当显示器打开时 当显示器关闭时 打开盖子时 合上盖子时 当关闭游戏后 当打开游戏时 当用户重新开始操作电脑后 当用户长时间未操作电脑后 强制休眠独显 The display name of the default Quick Action that is presented to the user upon first installation. 当自定义模式预设切换时 循环自动化 The display name of the periodic automation action. 当与 Wi-Fi 连接时 当与 Wi-Fi 断开连接时 当唤醒时 当 HDR 关闭时 当 HDR 开启时 当与设备断开连接时 当与设备连接时 ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Resources/Resource.zh-hant.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 當與電源供應器連接時 當與電源供應器中斷連接時 強制關閉應用程式 重新啟動 GPU {0} 秒 {0} 秒 當與較低功率電源供應器連接時 當啟動時 當電源模式變更時 當應用程式啟動時 當應用程式關閉後 指定時間 當與外接顯示器連接時 當與外接顯示器中斷連接時 當顯示器開啟時 當顯示器關閉時 開啟上蓋時 關閉上蓋時 當關閉遊戲後 當執行遊戲時 當使用者處於活動狀態 當使用者處於非活動狀態時 休眠 GPU The display name of the default Quick Action that is presented to the user upon first installation. 關閉 開啟 當客製化模式預設變更時 自動化循環 The display name of the periodic automation action. 當與 Wi-Fi 連接時 當與 Wi-Fi 中斷連接時 當喚醒時 當 HDR 關閉時 當 HDR 開啟時 關閉 開啟 當與裝置中斷連接時 當與裝置連接時 ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/AbstractFeatureAutomationStep.cs ================================================ using System.Threading; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Features; using LenovoLegionToolkit.Lib.Messaging; using LenovoLegionToolkit.Lib.Messaging.Messages; namespace LenovoLegionToolkit.Lib.Automation.Steps; public abstract class AbstractFeatureAutomationStep(T state) : IAutomationStep where T : struct { private readonly IFeature _feature = IoCContainer.Resolve>(); public T State { get; } = state; public Task IsSupportedAsync() => _feature.IsSupportedAsync(); public virtual async Task RunAsync(AutomationContext context, AutomationEnvironment environment, CancellationToken token) { var currentState = await _feature.GetStateAsync().ConfigureAwait(false); if (!State.Equals(currentState)) await _feature.SetStateAsync(State).ConfigureAwait(false); MessagingCenter.Publish(new FeatureStateMessage(State)); } public Task GetAllStatesAsync() => _feature.GetAllStatesAsync(); public abstract IAutomationStep DeepCopy(); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/AlwaysOnUsbAutomationStep.cs ================================================ using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Steps; [method: JsonConstructor] public class AlwaysOnUsbAutomationStep(AlwaysOnUSBState state) : AbstractFeatureAutomationStep(state) { public override IAutomationStep DeepCopy() => new AlwaysOnUsbAutomationStep(State); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/BatteryAutomationStep.cs ================================================ using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Steps; [method: JsonConstructor] public class BatteryAutomationStep(BatteryState state) : AbstractFeatureAutomationStep(state) { public override IAutomationStep DeepCopy() => new BatteryAutomationStep(State); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/BatteryNightChargeAutomationStep.cs ================================================ using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Steps; [method: JsonConstructor] public class BatteryNightChargeAutomationStep(BatteryNightChargeState state) : AbstractFeatureAutomationStep(state) { public override IAutomationStep DeepCopy() => new BatteryNightChargeAutomationStep(State); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/DeactivateGPUAutomationStep.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Controllers; namespace LenovoLegionToolkit.Lib.Automation.Steps; public class DeactivateGPUAutomationStep(DeactivateGPUAutomationStepState state) : IAutomationStep { private readonly GPUController _controller = IoCContainer.Resolve(); public DeactivateGPUAutomationStepState State { get; } = state; public Task GetAllStatesAsync() => Task.FromResult(Enum.GetValues()); public Task IsSupportedAsync() => Task.FromResult(_controller.IsSupported()); public async Task RunAsync(AutomationContext context, AutomationEnvironment environment, CancellationToken token) { if (!_controller.IsSupported()) return; var status = await _controller.RefreshNowAsync().ConfigureAwait(false); switch (State) { case DeactivateGPUAutomationStepState.KillApps when status.State is GPUState.Active: await _controller.KillGPUProcessesAsync().ConfigureAwait(false); break; case DeactivateGPUAutomationStepState.RestartGPU when status.State is GPUState.Active or GPUState.Inactive: await _controller.RestartGPUAsync().ConfigureAwait(false); break; } } IAutomationStep IAutomationStep.DeepCopy() => new DeactivateGPUAutomationStep(State); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/DelayAutomationStep.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Steps; [method: JsonConstructor] public class DelayAutomationStep(Delay state) : IAutomationStep { public Delay State { get; } = state; public Task IsSupportedAsync() => Task.FromResult(true); public Task GetAllStatesAsync() => Task.FromResult(new Delay[] { new(1), new(2), new(3), new(5) }); public IAutomationStep DeepCopy() => new DelayAutomationStep(State); public Task RunAsync(AutomationContext context, AutomationEnvironment environment, CancellationToken token) => Task.Delay(TimeSpan.FromSeconds(State.DelaySeconds), token); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/DisplayBrightnessAutomationStep.cs ================================================ using System.Threading; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Controllers; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Steps; [method: JsonConstructor] public class DisplayBrightnessAutomationStep(int brightness) : IAutomationStep { private readonly DisplayBrightnessController _controller = IoCContainer.Resolve(); public int Brightness { get; } = brightness; public Task IsSupportedAsync() => Task.FromResult(true); public Task RunAsync(AutomationContext context, AutomationEnvironment environment, CancellationToken token) { return _controller.SetBrightnessAsync(Brightness); } IAutomationStep IAutomationStep.DeepCopy() => new DisplayBrightnessAutomationStep(Brightness); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/DpiScaleAutomationStep.cs ================================================ using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Steps; [method: JsonConstructor] public class DpiScaleAutomationStep(DpiScale state) : AbstractFeatureAutomationStep(state) { public override IAutomationStep DeepCopy() => new DpiScaleAutomationStep(State); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/FlipToStartAutomationStep.cs ================================================ using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Steps; [method: JsonConstructor] public class FlipToStartAutomationStep(FlipToStartState state) : AbstractFeatureAutomationStep(state) { public override IAutomationStep DeepCopy() => new FlipToStartAutomationStep(State); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/FnLockAutomationStep.cs ================================================ using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Steps; [method: JsonConstructor] public class FnLockAutomationStep(FnLockState state) : AbstractFeatureAutomationStep(state) { public override IAutomationStep DeepCopy() => new FnLockAutomationStep(State); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/GodModePresetAutomationStep.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Controllers.GodMode; using LenovoLegionToolkit.Lib.Features; using LenovoLegionToolkit.Lib.Utils; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Steps; [method: JsonConstructor] public class GodModePresetAutomationStep(Guid presetId) : IAutomationStep { private readonly PowerModeFeature _feature = IoCContainer.Resolve(); private readonly GodModeController _controller = IoCContainer.Resolve(); public Guid PresetId { get; } = presetId; public async Task IsSupportedAsync() { var mi = await Compatibility.GetMachineInformationAsync().ConfigureAwait(false); return mi.Properties.SupportsGodMode; } public Task GetStateAsync() => _controller.GetStateAsync(); public async Task RunAsync(AutomationContext context, AutomationEnvironment environment, CancellationToken token) { var state = await _controller.GetStateAsync().ConfigureAwait(false); if (!state.Presets.ContainsKey(PresetId)) return; var newState = state with { ActivePresetId = PresetId }; await _controller.SetStateAsync(newState).ConfigureAwait(false); if (await _feature.GetStateAsync().ConfigureAwait(false) == PowerModeState.GodMode) await _controller.ApplyStateAsync().ConfigureAwait(false); } public IAutomationStep DeepCopy() => new GodModePresetAutomationStep(PresetId); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/HDRAutomationStep.cs ================================================ using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Steps; [method: JsonConstructor] public class HDRAutomationStep(HDRState state) : AbstractFeatureAutomationStep(state) { public override IAutomationStep DeepCopy() => new HDRAutomationStep(State); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/HybridModeAutomationStep.cs ================================================ using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Steps; [method: JsonConstructor] public class HybridModeAutomationStep(HybridModeState state) : AbstractFeatureAutomationStep(state) { public override IAutomationStep DeepCopy() => new HybridModeAutomationStep(State); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/IAutomationStep.cs ================================================ using System.Threading; using System.Threading.Tasks; namespace LenovoLegionToolkit.Lib.Automation.Steps; public interface IAutomationStep { Task IsSupportedAsync(); Task RunAsync(AutomationContext context, AutomationEnvironment environment, CancellationToken token); IAutomationStep DeepCopy(); } public interface IAutomationStep : IAutomationStep where T : struct { T State { get; } Task GetAllStatesAsync(); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/InstantBootAutomationStep.cs ================================================ using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Steps; [method: JsonConstructor] public class InstantBootAutomationStep(InstantBootState state) : AbstractFeatureAutomationStep(state) { public override IAutomationStep DeepCopy() => new InstantBootAutomationStep(State); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/MacroAutomationStep.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Macro; namespace LenovoLegionToolkit.Lib.Automation.Steps; public class MacroAutomationStep(MacroAutomationStepState state) : IAutomationStep { private readonly MacroController _controller = IoCContainer.Resolve(); public MacroAutomationStepState State { get; set; } = state; public Task IsSupportedAsync() => Task.FromResult(true); public Task GetAllStatesAsync() => Task.FromResult(Enum.GetValues()); public Task RunAsync(AutomationContext context, AutomationEnvironment environment, CancellationToken token) { _controller.SetEnabled(State is MacroAutomationStepState.On); return Task.CompletedTask; } public IAutomationStep DeepCopy() => new MacroAutomationStep(State); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/MicrophoneAutomationStep.cs ================================================ using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Steps; [method: JsonConstructor] public class MicrophoneAutomationStep(MicrophoneState state) : AbstractFeatureAutomationStep(state) { public override IAutomationStep DeepCopy() => new MicrophoneAutomationStep(State); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/NotificationAutomationStep.cs ================================================ using System.Threading; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Messaging; using LenovoLegionToolkit.Lib.Messaging.Messages; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Steps; [method: JsonConstructor] public class NotificationAutomationStep(string? text) : IAutomationStep { public string? Text { get; } = text; public Task IsSupportedAsync() => Task.FromResult(true); public Task RunAsync(AutomationContext context, AutomationEnvironment environment, CancellationToken token) { if (!string.IsNullOrWhiteSpace(Text)) { var text = Text.Replace("$RUN_OUTPUT$", context.LastRunOutput); MessagingCenter.Publish(new NotificationMessage(NotificationType.AutomationNotification, text)); } return Task.CompletedTask; } IAutomationStep IAutomationStep.DeepCopy() => new NotificationAutomationStep(Text); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/OneLevelWhiteKeyboardBacklightAutomationStep.cs ================================================ using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Steps; [method: JsonConstructor] public class OneLevelWhiteKeyboardBacklightAutomationStep(OneLevelWhiteKeyboardBacklightState state) : AbstractFeatureAutomationStep(state) { public override IAutomationStep DeepCopy() => new OneLevelWhiteKeyboardBacklightAutomationStep(State); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/OverDriveAutomationStep.cs ================================================ using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Steps; [method: JsonConstructor] public class OverDriveAutomationStep(OverDriveState state) : AbstractFeatureAutomationStep(state) { public override IAutomationStep DeepCopy() => new OverDriveAutomationStep(State); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/OverclockDiscreteGPUAutomationStep.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Controllers; namespace LenovoLegionToolkit.Lib.Automation.Steps; public class OverclockDiscreteGPUAutomationStep(OverclockDiscreteGPUAutomationStepState state) : IAutomationStep { private readonly GPUOverclockController _controller = IoCContainer.Resolve(); public OverclockDiscreteGPUAutomationStepState State { get; } = state; public Task GetAllStatesAsync() => Task.FromResult(Enum.GetValues()); public Task IsSupportedAsync() => _controller.IsSupportedAsync(); public async Task RunAsync(AutomationContext context, AutomationEnvironment environment, CancellationToken token) { if (!await _controller.IsSupportedAsync().ConfigureAwait(false)) return; var (_, info) = _controller.GetState(); switch (State) { case OverclockDiscreteGPUAutomationStepState.On: _controller.SaveState(true, info); break; case OverclockDiscreteGPUAutomationStepState.Off: _controller.SaveState(false, info); break; } await _controller.ApplyStateAsync(true).ConfigureAwait(false); } IAutomationStep IAutomationStep.DeepCopy() => new OverclockDiscreteGPUAutomationStep(State); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/PanelLogoBacklightAutomationStep.cs ================================================ using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Steps; [method: JsonConstructor] public class PanelLogoBacklightAutomationStep(PanelLogoBacklightState state) : AbstractFeatureAutomationStep(state) { public override IAutomationStep DeepCopy() => new PanelLogoBacklightAutomationStep(State); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/PlaySoundAutomationStep.cs ================================================ using System.Media; using System.Threading; using System.Threading.Tasks; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Steps; [method: JsonConstructor] public class PlaySoundAutomationStep(string? path) : IAutomationStep { public string? Path { get; } = path; public Task IsSupportedAsync() => Task.FromResult(true); public Task RunAsync(AutomationContext context, AutomationEnvironment environment, CancellationToken token) { if (Path is null) return Task.CompletedTask; return Task.Run(() => { var player = new SoundPlayer(Path); player.PlaySync(); }, token); } public IAutomationStep DeepCopy() => new PlaySoundAutomationStep(Path); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/PortsBacklightAutomationStep.cs ================================================ using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Steps; [method: JsonConstructor] public class PortsBacklightAutomationStep(PortsBacklightState state) : AbstractFeatureAutomationStep(state) { public override IAutomationStep DeepCopy() => new PortsBacklightAutomationStep(State); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/PowerModeAutomationStep.cs ================================================ using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Steps; [method: JsonConstructor] public class PowerModeAutomationStep(PowerModeState state) : AbstractFeatureAutomationStep(state) { public override IAutomationStep DeepCopy() => new PowerModeAutomationStep(State); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/QuickActionAutomationStep.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; namespace LenovoLegionToolkit.Lib.Automation.Steps; public class QuickActionAutomationStep(Guid? pipelineId) : IAutomationStep { public Guid? PipelineId { get; } = pipelineId; public Task IsSupportedAsync() => Task.FromResult(true); public Task RunAsync(AutomationContext context, AutomationEnvironment environment, CancellationToken token) => Task.CompletedTask; IAutomationStep IAutomationStep.DeepCopy() => new QuickActionAutomationStep(PipelineId); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/RGBKeyboardBacklightAutomationStep.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Controllers; using LenovoLegionToolkit.Lib.Messaging; using LenovoLegionToolkit.Lib.Messaging.Messages; namespace LenovoLegionToolkit.Lib.Automation.Steps; public class RGBKeyboardBacklightAutomationStep(RGBKeyboardBacklightPreset state) : IAutomationStep { private readonly RGBKeyboardBacklightController _controller = IoCContainer.Resolve(); public RGBKeyboardBacklightPreset State { get; } = state; public Task GetAllStatesAsync() => Task.FromResult(Enum.GetValues()); public Task IsSupportedAsync() => _controller.IsSupportedAsync(); public async Task RunAsync(AutomationContext context, AutomationEnvironment environment, CancellationToken token) { if (!await _controller.IsSupportedAsync().ConfigureAwait(false)) return; await _controller.SetLightControlOwnerAsync(true).ConfigureAwait(false); await _controller.SetPresetAsync(State).ConfigureAwait(false); MessagingCenter.Publish(new RGBKeyboardBacklightChangedMessage()); } IAutomationStep IAutomationStep.DeepCopy() => new RGBKeyboardBacklightAutomationStep(State); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/RefreshRateAutomationStep.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Utils; using Newtonsoft.Json; using WindowsDisplayAPI.Exceptions; namespace LenovoLegionToolkit.Lib.Automation.Steps; [method: JsonConstructor] public class RefreshRateAutomationStep(RefreshRate state) : AbstractFeatureAutomationStep(state) { public override Task RunAsync(AutomationContext context, AutomationEnvironment environment, CancellationToken token) { return RetryHelper.RetryAsync(() => base.RunAsync(context, environment, token), 5, TimeSpan.FromSeconds(1), ex => ex is ModeChangeException, nameof(RefreshRateAutomationStep)); } public override IAutomationStep DeepCopy() => new RefreshRateAutomationStep(State); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/ResolutionAutomationStep.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Utils; using Newtonsoft.Json; using WindowsDisplayAPI.Exceptions; namespace LenovoLegionToolkit.Lib.Automation.Steps; [method: JsonConstructor] public class ResolutionAutomationStep(Resolution state) : AbstractFeatureAutomationStep(state) { public override Task RunAsync(AutomationContext context, AutomationEnvironment environment, CancellationToken token) { return RetryHelper.RetryAsync(() => base.RunAsync(context, environment, token), 5, TimeSpan.FromSeconds(1), ex => ex is ModeChangeException, nameof(ResolutionAutomationStep)); } public override IAutomationStep DeepCopy() => new ResolutionAutomationStep(State); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/RunAutomationStep.cs ================================================ using System.Threading; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.System; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Steps; [method: JsonConstructor] public class RunAutomationStep(string? scriptPath, string? scriptArguments, bool? runSilently, bool? waitUntilFinished) : IAutomationStep { public string? ScriptPath { get; } = scriptPath; public string? ScriptArguments { get; } = scriptArguments; public bool RunSilently { get; } = runSilently ?? true; public bool WaitUntilFinished { get; } = waitUntilFinished ?? false; public Task IsSupportedAsync() => Task.FromResult(true); public async Task RunAsync(AutomationContext context, AutomationEnvironment environment, CancellationToken token) { if (string.IsNullOrWhiteSpace(ScriptPath)) return; var (_, output) = await CMD.RunAsync(ScriptPath, ScriptArguments ?? string.Empty, RunSilently, WaitUntilFinished, environment.Dictionary, token).ConfigureAwait(false); context.LastRunOutput = output.TrimEnd(); } IAutomationStep IAutomationStep.DeepCopy() => new RunAutomationStep(ScriptPath, ScriptArguments, RunSilently, WaitUntilFinished); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/SpeakerAutomationStep.cs ================================================ using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Steps; [method: JsonConstructor] public class SpeakerAutomationStep(SpeakerState state) : AbstractFeatureAutomationStep(state) { public override IAutomationStep DeepCopy() => new SpeakerAutomationStep(State); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/SpectrumKeyboardBacklightBrightnessAutomationStep.cs ================================================ using System; using System.Linq; using System.Threading; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Controllers; using LenovoLegionToolkit.Lib.Messaging; using LenovoLegionToolkit.Lib.Messaging.Messages; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Steps; [method: JsonConstructor] public class SpectrumKeyboardBacklightBrightnessAutomationStep(int state) : IAutomationStep { private readonly SpectrumKeyboardBacklightController _controller = IoCContainer.Resolve(); private readonly int[] _allStates = Enumerable.Range(0, 10).ToArray(); public int State { get; } = state; public Task GetAllStatesAsync() => Task.FromResult(_allStates); public Task IsSupportedAsync() => _controller.IsSupportedAsync(); public async Task RunAsync(AutomationContext context, AutomationEnvironment environment, CancellationToken token) { if (!await _controller.IsSupportedAsync().ConfigureAwait(false)) return; if (!_allStates.Contains(State)) throw new InvalidOperationException(nameof(State)); await _controller.SetBrightnessAsync(State).ConfigureAwait(false); MessagingCenter.Publish(new SpectrumBacklightChangedMessage()); } public IAutomationStep DeepCopy() => new SpectrumKeyboardBacklightBrightnessAutomationStep(State); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/SpectrumKeyboardBacklightImportProfileAutomationStep.cs ================================================ using System.Threading; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Controllers; using LenovoLegionToolkit.Lib.Messaging; using LenovoLegionToolkit.Lib.Messaging.Messages; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Steps; [method: JsonConstructor] public class SpectrumKeyboardBacklightImportProfileAutomationStep(string? path) : IAutomationStep { private readonly SpectrumKeyboardBacklightController _controller = IoCContainer.Resolve(); public string? Path { get; } = path; public Task IsSupportedAsync() => _controller.IsSupportedAsync(); public async Task RunAsync(AutomationContext context, AutomationEnvironment environment, CancellationToken token) { if (Path is null || !await _controller.IsSupportedAsync().ConfigureAwait(false)) return; var profile = await _controller.GetProfileAsync().ConfigureAwait(false); await _controller.ImportProfileDescription(profile, Path).ConfigureAwait(false); MessagingCenter.Publish(new SpectrumBacklightChangedMessage()); } IAutomationStep IAutomationStep.DeepCopy() => new SpectrumKeyboardBacklightImportProfileAutomationStep(Path); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/SpectrumKeyboardBacklightProfileAutomationStep.cs ================================================ using System; using System.Linq; using System.Threading; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Controllers; using LenovoLegionToolkit.Lib.Messaging; using LenovoLegionToolkit.Lib.Messaging.Messages; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Steps; [method: JsonConstructor] public class SpectrumKeyboardBacklightProfileAutomationStep(int state) : IAutomationStep { private readonly SpectrumKeyboardBacklightController _controller = IoCContainer.Resolve(); private readonly int[] _allStates = Enumerable.Range(1, 6).ToArray(); public int State { get; } = state; public Task GetAllStatesAsync() => Task.FromResult(_allStates); public Task IsSupportedAsync() => _controller.IsSupportedAsync(); public async Task RunAsync(AutomationContext context, AutomationEnvironment environment, CancellationToken token) { if (!await _controller.IsSupportedAsync().ConfigureAwait(false)) return; if (!_allStates.Contains(State)) throw new InvalidOperationException(nameof(State)); await _controller.SetProfileAsync(State).ConfigureAwait(false); MessagingCenter.Publish(new SpectrumBacklightChangedMessage()); } public IAutomationStep DeepCopy() => new SpectrumKeyboardBacklightProfileAutomationStep(State); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/TouchpadLockAutomationStep.cs ================================================ using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Steps; [method: JsonConstructor] public class TouchpadLockAutomationStep(TouchpadLockState state) : AbstractFeatureAutomationStep(state) { public override IAutomationStep DeepCopy() => new TouchpadLockAutomationStep(State); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/TurnOffMonitorsAutomationStep.cs ================================================ using System.Threading; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Listeners; namespace LenovoLegionToolkit.Lib.Automation.Steps; public class TurnOffMonitorsAutomationStep : IAutomationStep { private readonly NativeWindowsMessageListener _nativeWindowsMessageListener = IoCContainer.Resolve(); public Task IsSupportedAsync() => Task.FromResult(true); public Task RunAsync(AutomationContext context, AutomationEnvironment environment, CancellationToken token) => _nativeWindowsMessageListener.TurnOffMonitorAsync(); public IAutomationStep DeepCopy() => new TurnOffMonitorsAutomationStep(); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/TurnOffWiFiAutomationStep.cs ================================================ using System.Threading; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.System; namespace LenovoLegionToolkit.Lib.Automation.Steps; public class TurnOffWiFiAutomationStep : IAutomationStep { public Task IsSupportedAsync() => Task.FromResult(true); public Task RunAsync(AutomationContext context, AutomationEnvironment environment, CancellationToken token) { WiFi.TurnOff(); return Task.CompletedTask; } public IAutomationStep DeepCopy() => new TurnOffWiFiAutomationStep(); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/TurnOnWiFiAutomationStep.cs ================================================ using System.Threading; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.System; namespace LenovoLegionToolkit.Lib.Automation.Steps; public class TurnOnWiFiAutomationStep : IAutomationStep { public Task IsSupportedAsync() => Task.FromResult(true); public Task RunAsync(AutomationContext context, AutomationEnvironment environment, CancellationToken token) { WiFi.TurnOn(); return Task.CompletedTask; } public IAutomationStep DeepCopy() => new TurnOnWiFiAutomationStep(); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/WhiteKeyboardBacklightAutomationStep.cs ================================================ using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Steps; [method: JsonConstructor] public class WhiteKeyboardBacklightAutomationStep(WhiteKeyboardBacklightState state) : AbstractFeatureAutomationStep(state) { public override IAutomationStep DeepCopy() => new WhiteKeyboardBacklightAutomationStep(State); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Steps/WinKeyAutomationStep.cs ================================================ using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation.Steps; [method: JsonConstructor] public class WinKeyAutomationStep(WinKeyState state) : AbstractFeatureAutomationStep(state) { public override IAutomationStep DeepCopy() => new WinKeyAutomationStep(State); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Structs.cs ================================================ using LenovoLegionToolkit.Lib.Automation.Resources; using Newtonsoft.Json; namespace LenovoLegionToolkit.Lib.Automation; [method: JsonConstructor] public readonly struct Delay(int delaySeconds) : IDisplayName { public int DelaySeconds { get; } = delaySeconds; public string DisplayName => string.Format(DelaySeconds == 1 ? Resource.Delay_Second : Resource.Delay_Second_Many, DelaySeconds); } ================================================ FILE: LenovoLegionToolkit.Lib.Automation/Utils/AutomationSettings.cs ================================================ using System.Collections.Generic; using LenovoLegionToolkit.Lib.Automation.Pipeline; using LenovoLegionToolkit.Lib.Automation.Pipeline.Triggers; using LenovoLegionToolkit.Lib.Automation.Resources; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.Lib.Settings; namespace LenovoLegionToolkit.Lib.Automation.Utils; public class AutomationSettings() : AbstractSettings("automation.json") { public class AutomationSettingsStore { public bool IsEnabled { get; set; } public List Pipelines { get; set; } = []; } protected override AutomationSettingsStore Default => new() { Pipelines = { new AutomationPipeline { Trigger = new ACAdapterConnectedAutomationPipelineTrigger(), Steps = { new PowerModeAutomationStep(PowerModeState.Balance) }, }, new AutomationPipeline { Trigger = new ACAdapterDisconnectedAutomationPipelineTrigger(), Steps = { new PowerModeAutomationStep(PowerModeState.Quiet) }, }, new AutomationPipeline { Name = Resource.DeactivateGpuQuickAction_Title, Steps = { new DeactivateGPUAutomationStep(DeactivateGPUAutomationStepState.KillApps) }, }, }, }; } ================================================ FILE: LenovoLegionToolkit.Lib.Macro/Enums.cs ================================================ using System; using System.ComponentModel.DataAnnotations; using LenovoLegionToolkit.Lib.Macro.Resources; namespace LenovoLegionToolkit.Lib.Macro; public enum MacroDirection { Unknown, Down, Up, Wheel, HorizontalWheel, Move } [Flags] public enum MacroRecorderSettings { Keyboard = 1, Mouse = 2, Movement = 4 } public enum MacroSource { Unknown, [Display(ResourceType = typeof(Resource), Name = "MacroSource_Keyboard")] Keyboard, [Display(ResourceType = typeof(Resource), Name = "MacroSource_Mouse")] Mouse } ================================================ FILE: LenovoLegionToolkit.Lib.Macro/IoCModule.cs ================================================ using Autofac; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Macro.Utils; namespace LenovoLegionToolkit.Lib.Macro; public class IoCModule : Module { protected override void Load(ContainerBuilder builder) { builder.Register(); builder.Register(); } } ================================================ FILE: LenovoLegionToolkit.Lib.Macro/LenovoLegionToolkit.Lib.Macro.csproj ================================================  net8.0-windows win-x64 x64 enable © 2024 Bartosz Cichecki true en true True True Resource.resx PublicResXFileCodeGenerator Resource.Designer.cs ================================================ FILE: LenovoLegionToolkit.Lib.Macro/MacroController.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; using System.Threading.Tasks; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Macro.Utils; using Windows.Win32; using Windows.Win32.Foundation; using Windows.Win32.UI.WindowsAndMessaging; namespace LenovoLegionToolkit.Lib.Macro; public class MacroController { public class RecorderReceivedEventArgs : EventArgs { public MacroEvent MacroEvent { get; init; } } public class RecorderStoppedEventArgs : EventArgs { public bool Interrupted { get; init; } } private static readonly uint[] AllowedKeys = [0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69]; public static readonly int[] AllowedRepeatCounts = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; private readonly MacroRecorder _recorder = new(); private readonly MacroPlayer _player = new(); private readonly HOOKPROC _kbProc; private readonly MacroSettings _settings; private HHOOK _kbHook; public event EventHandler? RecorderReceived; public event EventHandler? RecorderStopped; public bool IsEnabled => _settings.Store.IsEnabled; public MacroController(MacroSettings settings) { _settings = settings; _kbProc = LowLevelKeyboardProc; _recorder.Received += Recorder_Received; _recorder.Stopped += Recorder_Stopped; } private void Recorder_Received(object? sender, MacroRecorder.ReceivedEventArgs e) => RecorderReceived?.Invoke(this, new() { MacroEvent = e.MacroEvent }); private void Recorder_Stopped(object? sender, MacroRecorder.StoppedEventArgs e) => RecorderStopped?.Invoke(this, new() { Interrupted = e.Interrupted }); public void SetEnabled(bool enabled) { _settings.Store.IsEnabled = enabled; _settings.SynchronizeStore(); } public Dictionary GetSequences() => _settings.Store.Sequences; public void SetSequences(Dictionary sequences) { CleanUp(ref sequences); _settings.Store.Sequences = sequences; _settings.SynchronizeStore(); } public void Start() { if (_kbHook != default) return; _kbHook = PInvoke.SetWindowsHookEx(WINDOWS_HOOK_ID.WH_KEYBOARD_LL, _kbProc, HINSTANCE.Null, 0); } public void StartRecording(MacroRecorderSettings settings = MacroRecorderSettings.Keyboard) => _recorder.StartRecording(settings); public void StopRecording() => _recorder.StopRecording(); private unsafe LRESULT LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) { if (nCode != PInvoke.HC_ACTION) return PInvoke.CallNextHookEx(HHOOK.Null, nCode, wParam, lParam); if (!IsEnabled) return PInvoke.CallNextHookEx(HHOOK.Null, nCode, wParam, lParam); ref var kbStruct = ref Unsafe.AsRef((void*)lParam.Value); _player.InterruptIfNeeded(kbStruct); var shouldRun = !_recorder.IsRecording; shouldRun &= kbStruct.flags == 0; shouldRun &= _settings.Store.Sequences.GetValueOrNull(new(MacroSource.Keyboard, kbStruct.vkCode))?.Events?.Length > 0; shouldRun &= AllowedKeys.Contains(kbStruct.vkCode); if (!shouldRun) return PInvoke.CallNextHookEx(HHOOK.Null, nCode, wParam, lParam); var sequence = _settings.Store.Sequences[new(MacroSource.Keyboard, kbStruct.vkCode)]; Play(sequence); return new LRESULT(96); } private void Play(MacroSequence sequence) => Task.Run(() => _player.StartPlayingAsync(sequence)); private static void CleanUp(ref Dictionary sequences) { sequences = ClearDownsWithoutUps(sequences); sequences = ClearEmptySequences(sequences); } private static Dictionary ClearEmptySequences(Dictionary sequences) { return sequences.Where(kv => kv.Value.Events?.Length > 0) .ToDictionary(kv => kv.Key, kv => kv.Value); } private static Dictionary ClearDownsWithoutUps(Dictionary sequences) { var result = new Dictionary(); foreach (var kv in sequences) result[kv.Key] = ClearDownsWithoutUps(kv.Value); return result; } private static MacroSequence ClearDownsWithoutUps(MacroSequence sequence) { var macroEvents = new List(sequence.Events ?? []); for (var i = macroEvents.Count - 1; i >= 0; i--) { var macroEvent = macroEvents[i]; switch (macroEvent.Direction) { case MacroDirection.Down: { var remove = true; for (var j = i; j < macroEvents.Count; j++) { if (macroEvents[j].Direction != MacroDirection.Up || macroEvents[j].Key != macroEvent.Key || macroEvents[j].Source != macroEvent.Source) continue; remove = false; break; } if (remove) macroEvents.RemoveAt(i); break; } case MacroDirection.Up: case MacroDirection.Unknown: default: continue; } } return sequence with { Events = [.. macroEvents] }; } } ================================================ FILE: LenovoLegionToolkit.Lib.Macro/Resources/Resource.Designer.cs ================================================ //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace LenovoLegionToolkit.Lib.Macro.Resources { using System; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class Resource { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Resource() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] public static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LenovoLegionToolkit.Lib.Macro.Resources.Resource", typeof(Resource).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] public static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Looks up a localized string similar to Keyboard. /// public static string MacroSource_Keyboard { get { return ResourceManager.GetString("MacroSource_Keyboard", resourceCulture); } } /// /// Looks up a localized string similar to Mouse. /// public static string MacroSource_Mouse { get { return ResourceManager.GetString("MacroSource_Mouse", resourceCulture); } } } } ================================================ FILE: LenovoLegionToolkit.Lib.Macro/Resources/Resource.ar.resx ================================================  text/microsoft-resx 1.3 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 لوحة المفاتيح فأرة ================================================ FILE: LenovoLegionToolkit.Lib.Macro/Resources/Resource.bg.resx ================================================  text/microsoft-resx 1.3 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: LenovoLegionToolkit.Lib.Macro/Resources/Resource.bs.resx ================================================  text/microsoft-resx 1.3 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: LenovoLegionToolkit.Lib.Macro/Resources/Resource.cs.resx ================================================  text/microsoft-resx 1.3 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: LenovoLegionToolkit.Lib.Macro/Resources/Resource.de.resx ================================================  text/microsoft-resx 1.3 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Tastatur Maus ================================================ FILE: LenovoLegionToolkit.Lib.Macro/Resources/Resource.el.resx ================================================  text/microsoft-resx 1.3 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Πληκτρολόγιο Ποντίκι ================================================ FILE: LenovoLegionToolkit.Lib.Macro/Resources/Resource.es.resx ================================================  text/microsoft-resx 1.3 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Teclado Ratón ================================================ FILE: LenovoLegionToolkit.Lib.Macro/Resources/Resource.fr.resx ================================================  text/microsoft-resx 1.3 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Clavier Souris ================================================ FILE: LenovoLegionToolkit.Lib.Macro/Resources/Resource.hu.resx ================================================  text/microsoft-resx 1.3 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Billentyűzet Egér ================================================ FILE: LenovoLegionToolkit.Lib.Macro/Resources/Resource.it.resx ================================================  text/microsoft-resx 1.3 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: LenovoLegionToolkit.Lib.Macro/Resources/Resource.ja.resx ================================================  text/microsoft-resx 1.3 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 キーボード マウス ================================================ FILE: LenovoLegionToolkit.Lib.Macro/Resources/Resource.ko.resx ================================================  text/microsoft-resx 1.3 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: LenovoLegionToolkit.Lib.Macro/Resources/Resource.lv.resx ================================================  text/microsoft-resx 1.3 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: LenovoLegionToolkit.Lib.Macro/Resources/Resource.nl-nl.resx ================================================  text/microsoft-resx 1.3 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Toetsenbord Muis ================================================ FILE: LenovoLegionToolkit.Lib.Macro/Resources/Resource.pl.resx ================================================  text/microsoft-resx 1.3 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Klawiatura Mysz ================================================ FILE: LenovoLegionToolkit.Lib.Macro/Resources/Resource.pt-br.resx ================================================  text/microsoft-resx 1.3 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Teclado Mouse ================================================ FILE: LenovoLegionToolkit.Lib.Macro/Resources/Resource.pt.resx ================================================  text/microsoft-resx 1.3 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Teclado Rato ================================================ FILE: LenovoLegionToolkit.Lib.Macro/Resources/Resource.resx ================================================  text/microsoft-resx 1.3 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Keyboard Mouse ================================================ FILE: LenovoLegionToolkit.Lib.Macro/Resources/Resource.ro.resx ================================================  text/microsoft-resx 1.3 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: LenovoLegionToolkit.Lib.Macro/Resources/Resource.ru.resx ================================================  text/microsoft-resx 1.3 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Клавиатура Мышь ================================================ FILE: LenovoLegionToolkit.Lib.Macro/Resources/Resource.sk.resx ================================================  text/microsoft-resx 1.3 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: LenovoLegionToolkit.Lib.Macro/Resources/Resource.tr.resx ================================================  text/microsoft-resx 1.3 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Klavye Fare ================================================ FILE: LenovoLegionToolkit.Lib.Macro/Resources/Resource.uk.resx ================================================  text/microsoft-resx 1.3 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Клавіатура Миша ================================================ FILE: LenovoLegionToolkit.Lib.Macro/Resources/Resource.uz-latn-uz.resx ================================================  text/microsoft-resx 1.3 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ================================================ FILE: LenovoLegionToolkit.Lib.Macro/Resources/Resource.vi.resx ================================================  text/microsoft-resx 1.3 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Bàn phím Chuột ================================================ FILE: LenovoLegionToolkit.Lib.Macro/Resources/Resource.zh-hans.resx ================================================  text/microsoft-resx 1.3 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 键盘 鼠标 ================================================ FILE: LenovoLegionToolkit.Lib.Macro/Resources/Resource.zh-hant.resx ================================================  text/microsoft-resx 1.3 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 鍵盤 滑鼠 ================================================ FILE: LenovoLegionToolkit.Lib.Macro/Structs.cs ================================================ using System; using System.ComponentModel; using System.Drawing; using LenovoLegionToolkit.Lib.Macro.Utils.TypeConverters; namespace LenovoLegionToolkit.Lib.Macro; public readonly struct MacroEvent { public MacroSource Source { get; init; } public MacroDirection Direction { get; init; } public uint Key { get; init; } public Point Point { get; init; } public TimeSpan Delay { get; init; } public bool IsUndefined() => Source == MacroSource.Unknown || Direction == MacroDirection.Unknown; public override string ToString() => $"{nameof(Source)}:{Source}, {nameof(Direction)}: {Direction}, {nameof(Key)}: {Key}, {nameof(Delay)}: {Delay}"; } [TypeConverter(typeof(MacroIdentifierTypeConverter))] public readonly struct MacroIdentifier(MacroSource source, ulong key) { public MacroSource Source { get; } = source; public ulong Key { get; } = key; #region Equality public override bool Equals(object? obj) => obj is MacroIdentifier other && Source == other.Source && Key == other.Key; public override int GetHashCode() => HashCode.Combine((int)Source, Key); public static bool operator ==(MacroIdentifier left, MacroIdentifier right) => left.Equals(right); public static bool operator !=(MacroIdentifier left, MacroIdentifier right) => !(left == right); #endregion } public readonly struct MacroSequence { public int RepeatCount { get; init; } public bool IgnoreDelays { get; init; } public bool InterruptOnOtherKey { get; init; } public MacroEvent[]? Events { get; init; } } ================================================ FILE: LenovoLegionToolkit.Lib.Macro/Utils/MacroPlayer.cs ================================================ using System; using System.Drawing; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Utils; using Windows.Win32; using Windows.Win32.UI.Input.KeyboardAndMouse; using Windows.Win32.UI.WindowsAndMessaging; namespace LenovoLegionToolkit.Lib.Macro.Utils; internal class MacroPlayer { private const int MAGIC_NUMBER = 1337; private readonly ThreadSafeBool _isPlayingInterruptableSequence = new(); private Task _playTask = Task.CompletedTask; private CancellationTokenSource _cancellationTokenSource = new(); public void InterruptIfNeeded(KBDLLHOOKSTRUCT kbStruct) { if (!_isPlayingInterruptableSequence.Value) return; if (kbStruct.flags != 0) return; if (kbStruct.dwExtraInfo == MAGIC_NUMBER) return; _cancellationTokenSource.Cancel(); } public async Task StartPlayingAsync(MacroSequence sequence) { await _cancellationTokenSource.CancelAsync(); try { await _playTask; } catch (OperationCanceledException) { } _cancellationTokenSource = new(); var token = _cancellationTokenSource.Token; _playTask = Task.Run(async () => { _isPlayingInterruptableSequence.Value = sequence.InterruptOnOtherKey; for (var i = 0; i < sequence.RepeatCount; i++) { foreach (var macroEvent in sequence.Events ?? []) { if (!sequence.IgnoreDelays) await Task.Delay(macroEvent.Delay, token).ConfigureAwait(false); token.ThrowIfCancellationRequested(); try { var input = ToInput(macroEvent, Screen.PrimaryScreen?.WorkingArea ?? Rectangle.Empty); var result = PInvoke.SendInput(MemoryMarshal.CreateSpan(ref input, 1), Marshal.SizeOf()); if (result == 0) PInvokeExtensions.ThrowIfWin32Error($"Failed to send input. Return code was {result}."); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to send input for event {macroEvent}", ex); } } } }, token); } private static INPUT ToInput(MacroEvent macroEvent, Rectangle screenArea) => macroEvent.Source switch { MacroSource.Keyboard => ToKeyboardInput(macroEvent), MacroSource.Mouse => ToMouseInput(macroEvent, screenArea), MacroSource.Unknown => throw new ArgumentException(null, nameof(macroEvent)), _ => throw new ArgumentOutOfRangeException(nameof(macroEvent)) }; private static INPUT ToKeyboardInput(MacroEvent macroEvent) => new() { type = INPUT_TYPE.INPUT_KEYBOARD, Anonymous = new INPUT._Anonymous_e__Union { ki = new KEYBDINPUT { wVk = (VIRTUAL_KEY)macroEvent.Key, dwFlags = macroEvent.Direction switch { MacroDirection.Up => KEYBD_EVENT_FLAGS.KEYEVENTF_KEYUP, _ => 0 }, dwExtraInfo = MAGIC_NUMBER } } }; private static INPUT ToMouseInput(MacroEvent macroEvent, Rectangle screenArea) => new() { type = INPUT_TYPE.INPUT_MOUSE, Anonymous = new INPUT._Anonymous_e__Union { mi = new MOUSEINPUT { dwFlags = (macroEvent.Direction, macroEvent.Key) switch { (MacroDirection.Up, 1) => MOUSE_EVENT_FLAGS.MOUSEEVENTF_LEFTUP, (MacroDirection.Down, 1) => MOUSE_EVENT_FLAGS.MOUSEEVENTF_LEFTDOWN, (MacroDirection.Up, 2) => MOUSE_EVENT_FLAGS.MOUSEEVENTF_RIGHTUP, (MacroDirection.Down, 2) => MOUSE_EVENT_FLAGS.MOUSEEVENTF_RIGHTDOWN, (MacroDirection.Up, 3) => MOUSE_EVENT_FLAGS.MOUSEEVENTF_MIDDLEUP, (MacroDirection.Down, 3) => MOUSE_EVENT_FLAGS.MOUSEEVENTF_MIDDLEDOWN, (MacroDirection.Up, > 0xFF) => MOUSE_EVENT_FLAGS.MOUSEEVENTF_XUP, (MacroDirection.Down, > 0xFF) => MOUSE_EVENT_FLAGS.MOUSEEVENTF_XDOWN, (MacroDirection.Wheel, _) => MOUSE_EVENT_FLAGS.MOUSEEVENTF_WHEEL, (MacroDirection.Move, _) => MOUSE_EVENT_FLAGS.MOUSEEVENTF_MOVE | MOUSE_EVENT_FLAGS.MOUSEEVENTF_ABSOLUTE, _ => 0 }, mouseData = (macroEvent.Direction, macroEvent.Key) switch { (MacroDirection.Up, >= 0xFF) => macroEvent.Key >> 16, (MacroDirection.Down, >= 0xFF) => macroEvent.Key >> 16, (MacroDirection.Wheel, _) => macroEvent.Key, _ => 0 }, dx = macroEvent.Direction switch { MacroDirection.Move => (int)(65535.0f * (macroEvent.Point.X / (float)screenArea.Width) + 0.5f), _ => 0 }, dy = macroEvent.Direction switch { MacroDirection.Move => (int)(65535.0f * (macroEvent.Point.Y / (float)screenArea.Height) + 0.5f), _ => 0 }, dwExtraInfo = MAGIC_NUMBER } } }; } ================================================ FILE: LenovoLegionToolkit.Lib.Macro/Utils/MacroRecorder.cs ================================================ using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Runtime.CompilerServices; using Microsoft.Win32; using Windows.Win32; using Windows.Win32.Foundation; using Windows.Win32.UI.Input.KeyboardAndMouse; using Windows.Win32.UI.WindowsAndMessaging; namespace LenovoLegionToolkit.Lib.Macro.Utils; internal class MacroRecorder { public class ReceivedEventArgs : EventArgs { public MacroEvent MacroEvent { get; init; } } public class StoppedEventArgs : EventArgs { public bool Interrupted { get; init; } } private class MacroEventEqualityComparer : IEqualityComparer { public bool Equals(MacroEvent x, MacroEvent y) => x.Source == y.Source && x.Key == y.Key; public int GetHashCode(MacroEvent obj) => HashCode.Combine(obj.Source, obj.Key); } private readonly HashSet _rolloverCache = new(new MacroEventEqualityComparer()); private readonly HOOKPROC _kbProc; private readonly HOOKPROC _mouseProc; private HHOOK _kbHook; private HHOOK _mouseHook; private TimeSpan _timeFromLastEvent; private bool _interrupted; private MacroRecorderSettings _settings; public bool IsRecording => _kbHook != HHOOK.Null && _mouseHook != HHOOK.Null; public event EventHandler? Received; public event EventHandler? Stopped; public MacroRecorder() { _kbProc = LowLevelKeyboardProc; _mouseProc = LowLevelMouseProc; } public void StartRecording(MacroRecorderSettings settings) { if (_kbHook != HHOOK.Null) return; _interrupted = false; _timeFromLastEvent = TimeSpan.Zero; _settings = settings; _kbHook = PInvoke.SetWindowsHookEx(WINDOWS_HOOK_ID.WH_KEYBOARD_LL, _kbProc, HINSTANCE.Null, 0); _mouseHook = PInvoke.SetWindowsHookEx(WINDOWS_HOOK_ID.WH_MOUSE_LL, _mouseProc, HINSTANCE.Null, 0); SystemEvents.SessionSwitch += SystemEvents_SessionSwitch; } public void StopRecording() { if (!IsRecording) return; var wasInterrupted = _interrupted; SystemEvents.SessionSwitch -= SystemEvents_SessionSwitch; PInvoke.UnhookWindowsHookEx(_kbHook); PInvoke.UnhookWindowsHookEx(_mouseHook); _kbHook = default; _mouseHook = default; _interrupted = false; _timeFromLastEvent = TimeSpan.Zero; _settings = default; Stopped?.Invoke(this, new() { Interrupted = wasInterrupted }); } private void SystemEvents_SessionSwitch(object sender, SessionSwitchEventArgs e) { var interrupt = new[] { SessionSwitchReason.ConsoleDisconnect, SessionSwitchReason.SessionLock, SessionSwitchReason.SessionLogoff, SessionSwitchReason.SessionRemoteControl, SessionSwitchReason.RemoteDisconnect }.Contains(e.Reason); if (!interrupt) return; _interrupted = true; StopRecording(); } private unsafe LRESULT LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) { if (nCode < 0) return PInvoke.CallNextHookEx(HHOOK.Null, nCode, wParam, lParam); var result = new LRESULT(69); ref var kbStruct = ref Unsafe.AsRef((void*)lParam.Value); var macroEvent = ConvertToMacroEvent(wParam, kbStruct, _timeFromLastEvent); if (macroEvent.IsUndefined()) return result; if (macroEvent.Direction == MacroDirection.Down) { if (macroEvent.Key == (ulong)VIRTUAL_KEY.VK_ESCAPE) { StopRecording(); return result; } if (_rolloverCache.Contains(macroEvent)) return result; } if (!_settings.HasFlag(MacroRecorderSettings.Keyboard)) return result; Received?.Invoke(this, new ReceivedEventArgs { MacroEvent = macroEvent }); _timeFromLastEvent = TimeSpan.FromMilliseconds(kbStruct.time); if (macroEvent.Direction == MacroDirection.Down) _rolloverCache.Add(macroEvent); else _rolloverCache.Remove(macroEvent); return result; } private unsafe LRESULT LowLevelMouseProc(int nCode, WPARAM wParam, LPARAM lParam) { if (nCode < 0) return PInvoke.CallNextHookEx(HHOOK.Null, nCode, wParam, lParam); ref var mouseStruct = ref Unsafe.AsRef((void*)lParam.Value); var macroEvent = ConvertToMacroEvent(wParam, mouseStruct, _timeFromLastEvent); if (macroEvent.IsUndefined()) return Result(); if (macroEvent.Direction == MacroDirection.Down && _rolloverCache.Contains(macroEvent)) return Result(); if (!_settings.HasFlag(MacroRecorderSettings.Mouse)) return Result(); if (macroEvent.Direction == MacroDirection.Move && !_settings.HasFlag(MacroRecorderSettings.Movement)) return Result(); Received?.Invoke(this, new ReceivedEventArgs { MacroEvent = macroEvent }); _timeFromLastEvent = TimeSpan.FromMilliseconds(mouseStruct.time); if (macroEvent.Direction == MacroDirection.Down) _rolloverCache.Add(macroEvent); else _rolloverCache.Remove(macroEvent); return Result(); LRESULT Result() => wParam == PInvoke.WM_MOUSEMOVE ? PInvoke.CallNextHookEx(HHOOK.Null, nCode, wParam, lParam) : new LRESULT(96); } private static MacroEvent ConvertToMacroEvent(WPARAM wParam, KBDLLHOOKSTRUCT kbStruct, TimeSpan timeFromLastEvent) { if (timeFromLastEvent == TimeSpan.Zero) timeFromLastEvent = TimeSpan.FromMilliseconds(kbStruct.time); var delay = TimeSpan.FromMilliseconds(kbStruct.time) - timeFromLastEvent; var macroEvent = new MacroEvent { Source = MacroSource.Keyboard, Direction = (uint)wParam switch { PInvoke.WM_KEYUP or PInvoke.WM_SYSKEYUP => MacroDirection.Up, PInvoke.WM_KEYDOWN or PInvoke.WM_SYSKEYDOWN => MacroDirection.Down, _ => MacroDirection.Unknown }, Key = kbStruct.vkCode, Delay = delay }; return macroEvent; } private static MacroEvent ConvertToMacroEvent(WPARAM wParam, MSLLHOOKSTRUCT mouseStruct, TimeSpan timeFromLastEvent) { if (timeFromLastEvent == TimeSpan.Zero) timeFromLastEvent = TimeSpan.FromMilliseconds(mouseStruct.time); var delay = TimeSpan.FromMilliseconds(mouseStruct.time) - timeFromLastEvent; var macroEvent = new MacroEvent { Source = MacroSource.Mouse, Direction = (uint)wParam switch { PInvoke.WM_LBUTTONUP or PInvoke.WM_RBUTTONUP or PInvoke.WM_MBUTTONUP or PInvoke.WM_XBUTTONUP => MacroDirection.Up, PInvoke.WM_LBUTTONDOWN or PInvoke.WM_RBUTTONDOWN or PInvoke.WM_MBUTTONDOWN or PInvoke.WM_XBUTTONDOWN => MacroDirection.Down, PInvoke.WM_MOUSEWHEEL => MacroDirection.Wheel, PInvoke.WM_MOUSEHWHEEL => MacroDirection.HorizontalWheel, PInvoke.WM_MOUSEMOVE => MacroDirection.Move, _ => MacroDirection.Unknown }, Key = (uint)wParam switch { PInvoke.WM_LBUTTONUP or PInvoke.WM_LBUTTONDOWN => 1, PInvoke.WM_RBUTTONUP or PInvoke.WM_RBUTTONDOWN => 2, PInvoke.WM_MBUTTONUP or PInvoke.WM_MBUTTONDOWN => 3, PInvoke.WM_XBUTTONUP or PInvoke.WM_XBUTTONDOWN => mouseStruct.mouseData, PInvoke.WM_MOUSEWHEEL => (uint)((int)mouseStruct.mouseData >> 16), PInvoke.WM_MOUSEHWHEEL => (uint)((int)mouseStruct.mouseData >> 16), _ => 0 }, Point = (uint)wParam switch { PInvoke.WM_MOUSEMOVE => mouseStruct.pt, _ => Point.Empty, }, Delay = delay }; return macroEvent; } } ================================================ FILE: LenovoLegionToolkit.Lib.Macro/Utils/MacroSettings.cs ================================================ using System.Collections.Generic; using LenovoLegionToolkit.Lib.Settings; namespace LenovoLegionToolkit.Lib.Macro.Utils; public class MacroSettings() : AbstractSettings("macro.json") { public class MacroSettingsStore { public bool IsEnabled { get; set; } public Dictionary Sequences { get; set; } = []; } protected override MacroSettingsStore Default => new(); } ================================================ FILE: LenovoLegionToolkit.Lib.Macro/Utils/TypeConverters/MacroIdentifierTypeConverter.cs ================================================ using System; using System.ComponentModel; using System.Globalization; namespace LenovoLegionToolkit.Lib.Macro.Utils.TypeConverters; public class MacroIdentifierTypeConverter : TypeConverter { public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) => sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) => destinationType == typeof(string) || base.CanConvertTo(context, destinationType); public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) { if (value is not string str) return base.ConvertFrom(context, culture, value); var split = str.Split(','); if (split.Length != 2) return base.ConvertFrom(context, culture, value); if (!Enum.TryParse(split[0], out var source)) return base.ConvertFrom(context, culture, value); if (!ulong.TryParse(split[1], out var key)) return base.ConvertFrom(context, culture, value); return new MacroIdentifier(source, key); } public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) { if (destinationType != typeof(string) || value is not MacroIdentifier macroIdentifier) return base.ConvertTo(context, culture, value, destinationType); return $"{macroIdentifier.Source},{macroIdentifier.Key}"; } } ================================================ FILE: LenovoLegionToolkit.SpectrumTester/LenovoLegionToolkit.SpectrumTester.csproj ================================================  net8.0-windows win-x64 x64 Exe SpectrumTester enable enable false false false true ================================================ FILE: LenovoLegionToolkit.SpectrumTester/Program.cs ================================================ using System.Runtime.InteropServices; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.System; using Windows.Win32; // ReSharper disable all Console.WriteLine(@"How to use: 1. Make sure Vantage and LLT is closed. 2. Set the keyboard brightness to maximum. When ready, press any key to continue... "); Console.ReadKey(); var device = Devices.GetSpectrumRGBKeyboard(); Console.WriteLine("Finding Spectrum keyboard..."); if (device is null) { Console.WriteLine("Spectrum not supported"); Console.ReadKey(); return; } Console.WriteLine("Spectrum keyboard found"); Console.WriteLine(); Console.WriteLine("Reading response for 0xD1..."); SetFeature(device, new LENOVO_SPECTRUM_GENERIC_REQUEST(LENOVO_SPECTRUM_OPERATION_TYPE.UnknownD1, 0, 0)); GetFeature(device, out LENOVO_SPECTRUM_GENERIC_RESPONSE resD1); Print(resD1.Bytes); Console.WriteLine(); Console.WriteLine("Reading response for 0xC6..."); SetFeature(device, new LENOVO_SPECTRUM_GENERIC_REQUEST(LENOVO_SPECTRUM_OPERATION_TYPE.UnknownC6, 0, 0)); GetFeature(device, out LENOVO_SPECTRUM_GENERIC_RESPONSE resC6); Print(resC6.Bytes); Console.WriteLine(); Console.WriteLine("Reading response for 0x04..."); SetFeature(device, new LENOVO_SPECTRUM_GENERIC_REQUEST(LENOVO_SPECTRUM_OPERATION_TYPE.Unknown04, 0, 0)); GetFeature(device, out LENOVO_SPECTRUM_GENERIC_RESPONSE res04); Print(res04.Bytes); Console.WriteLine(); Console.WriteLine("Reading response for 0xC7..."); SetFeature(device, new LENOVO_SPECTRUM_GENERIC_REQUEST(LENOVO_SPECTRUM_OPERATION_TYPE.UnknownC7, 0, 0)); GetFeature(device, out LENOVO_SPECTRUM_GENERIC_RESPONSE resC7); Print(resC7.Bytes); Console.WriteLine(); Console.WriteLine("Reading response for 0xC4 7..."); SetFeature(device, new LENOVO_SPECTRUM_GENERIC_REQUEST(LENOVO_SPECTRUM_OPERATION_TYPE.UnknownC4, 7, 0)); GetFeature(device, out LENOVO_SPECTRUM_GENERIC_RESPONSE resC47); Print(resC47.Bytes); Console.WriteLine(); Console.WriteLine("Reading response for 0xC4 8..."); SetFeature(device, new LENOVO_SPECTRUM_GENERIC_REQUEST(LENOVO_SPECTRUM_OPERATION_TYPE.UnknownC4, 8, 0)); GetFeature(device, out LENOVO_SPECTRUM_GENERIC_RESPONSE resC48); Print(resC48.Bytes); Console.WriteLine(); for (var i = 0; i < 10; i++) { Console.WriteLine($"Reading response for 0xC5 7 {i}..."); SetFeature(device, new LENOVO_SPECTRUM_GENERIC_REQUEST(LENOVO_SPECTRUM_OPERATION_TYPE.UnknownC5, 7, (byte)i)); GetFeature(device, out LENOVO_SPECTRUM_GENERIC_RESPONSE resC57); Print(resC57.Bytes); Console.WriteLine(); } Console.WriteLine($"Reading response for 0xC5 8..."); SetFeature(device, new LENOVO_SPECTRUM_GENERIC_REQUEST(LENOVO_SPECTRUM_OPERATION_TYPE.UnknownC5, 8, 0)); GetFeature(device, out LENOVO_SPECTRUM_GENERIC_RESPONSE resC58); Print(resC58.Bytes); Console.WriteLine(); Console.WriteLine(resD1.Bytes[4] == 0 ? "Keyboard is RGB." : "Not compatible."); var spectrumLayout = (resC47.Bytes[6], resC47.Bytes[5]) switch { (22, 9) => "Full", (20, 8) => "KeyboardAndFront", (20, 7) => "KeyboardOnly", _ => "Unknown" }; Console.WriteLine($"Layout is {spectrumLayout}."); Console.WriteLine("Reading config complete."); Console.WriteLine(); Console.ReadKey(); Console.WriteLine(@"*** TEST 1*** This program will now try to control brightness of the keyboard. Keyboard should turn off and then gradually increase brightness and turn off at the end. When ready, press any key to continue... "); var brightnessLevels = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; foreach (var level in brightnessLevels) { Console.WriteLine($"Setting brightness level to {level}..."); SetFeature(device, new LENOVO_SPECTRUM_GENERIC_REQUEST(LENOVO_SPECTRUM_OPERATION_TYPE.Brightness, level, 0)); await Task.Delay(250); } Console.WriteLine("Test complete, press any key to continue..."); Console.WriteLine(); Console.ReadKey(); Console.WriteLine(@"***TEST 2*** How to find a keycode for a specific key: 1. Open Vantage 2. Select the key that you want to find keycode for 3. Set the key to plain white (Hex: #FFFFFF, RGB: 255,255,255) 4. Make sure all other keys don't have any effect set (are off) 5. Set the keyboard brightness to maximum When ready, press any key to continue... "); Console.ReadKey(); Console.WriteLine("Reading white key keycodes..."); Console.WriteLine(); const int Iterations = 5; for (var i = 0; i < Iterations; i++) { GetFeature(device, out LENOVO_SPECTRUM_STATE state); var keys = state.Data .Where(kv => HasColor(kv.Color)) .Select(kv => kv.Key) .Select(k => "0x" + k.ToString("X4")) .Split(8); await Task.Delay(1000); if (keys.SelectMany(k => k).IsEmpty()) { Console.WriteLine($"[{i + 1}/{Iterations}] No keys found"); continue; } Console.WriteLine($"[{i + 1}/{Iterations}] Keys with color found:"); foreach (var line in keys) Console.WriteLine(" " + string.Join(", ", line)); } Console.WriteLine(); Console.WriteLine("Test complete."); Console.WriteLine("Press any key to exit..."); Console.ReadLine(); #region Methods void Print(byte[] bytes) { var length = bytes[2] + 4; foreach (var i in bytes.Take(length).Split(16)) Console.WriteLine(string.Join(" ", i.Select(i => $"{i:X2}"))); } bool HasColor(LENOVO_SPECTRUM_COLOR rgbColor) => rgbColor.Red == 255 && rgbColor.Green == 255 && rgbColor.Blue == 255; unsafe void SetFeature(SafeHandle handle, T str) where T : notnull { var ptr = IntPtr.Zero; try { int size; if (str is byte[] bytes) { size = bytes.Length; ptr = Marshal.AllocHGlobal(size); Marshal.Copy(bytes, 0, ptr, size); } else { size = Marshal.SizeOf(); ptr = Marshal.AllocHGlobal(size); Marshal.StructureToPtr(str, ptr, false); } var result = PInvoke.HidD_SetFeature(handle, ptr.ToPointer(), (uint)size); if (!result) PInvokeExtensions.ThrowIfWin32Error(typeof(T).Name); } finally { Marshal.FreeHGlobal(ptr); } } unsafe void GetFeature(SafeHandle handle, out T str) where T : struct { var ptr = IntPtr.Zero; try { var size = Marshal.SizeOf(); ptr = Marshal.AllocHGlobal(size); Marshal.Copy(new byte[] { 7 }, 0, ptr, 1); var result = PInvoke.HidD_GetFeature(handle, ptr.ToPointer(), (uint)size); if (!result) PInvokeExtensions.ThrowIfWin32Error(typeof(T).Name); str = Marshal.PtrToStructure(ptr); } finally { Marshal.FreeHGlobal(ptr); } } #endregion #region Structs [StructLayout(LayoutKind.Sequential)] internal struct LENOVO_SPECTRUM_HEADER(LENOVO_SPECTRUM_OPERATION_TYPE type, int size) { public byte Head = 7; public LENOVO_SPECTRUM_OPERATION_TYPE Type = type; public byte Size = (byte)(size % 256); public byte Tail = 3; } [StructLayout(LayoutKind.Sequential, Size = 960)] internal struct LENOVO_SPECTRUM_GENERIC_REQUEST(LENOVO_SPECTRUM_OPERATION_TYPE operation, byte value, byte value2) { public LENOVO_SPECTRUM_HEADER Header = new(operation, 0xC0); public byte Value = value; public byte Value2 = value2; } [StructLayout(LayoutKind.Sequential, Size = 960)] internal struct LENOVO_SPECTRUM_GENERIC_RESPONSE { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 960)] public byte[] Bytes; } internal enum LENOVO_SPECTRUM_OPERATION_TYPE : byte { ProfileSet1 = 0xC8, GetProfile = 0xCA, EffectChange = 0xCB, ProfileSet2 = 0xCC, GetBrightness = 0xCD, Brightness = 0xCE, AuroraSendBitmap = 0xA1, State = 0x03, Unknown04 = 0x04, UnknownC4 = 0xC4, UnknownC5 = 0xC5, UnknownC6 = 0xC6, UnknownC7 = 0xC7, UnknownD1 = 0xD1, } [StructLayout(LayoutKind.Sequential)] internal struct LENOVO_SPECTRUM_COLOR { public byte Red; public byte Green; public byte Blue; } [StructLayout(LayoutKind.Sequential, Pack = 1)] internal struct LENOVO_SPECTRUM_KEY_STATE { public ushort Key; public LENOVO_SPECTRUM_COLOR Color; } [StructLayout(LayoutKind.Sequential, Size = 960)] internal struct LENOVO_SPECTRUM_STATE { public byte ReportId; public LENOVO_SPECTRUM_OPERATION_TYPE Type; public byte Unknown2; public byte Unknown3; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 191)] public LENOVO_SPECTRUM_KEY_STATE[] Data; public byte Unknown4; } #endregion ================================================ FILE: LenovoLegionToolkit.WPF/App.xaml ================================================  ================================================ FILE: LenovoLegionToolkit.WPF/App.xaml.cs ================================================ #if !DEBUG using LenovoLegionToolkit.Lib.System; #endif using System; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Interop; using System.Windows.Media; using System.Windows.Threading; using LenovoLegionToolkit.Lib; using LenovoLegionToolkit.Lib.Automation; using LenovoLegionToolkit.Lib.Controllers; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Features; using LenovoLegionToolkit.Lib.Features.Hybrid; using LenovoLegionToolkit.Lib.Features.Hybrid.Notify; using LenovoLegionToolkit.Lib.Features.PanelLogo; using LenovoLegionToolkit.Lib.Features.WhiteKeyboardBacklight; using LenovoLegionToolkit.Lib.Integrations; using LenovoLegionToolkit.Lib.Listeners; using LenovoLegionToolkit.Lib.Macro; using LenovoLegionToolkit.Lib.Services; using LenovoLegionToolkit.Lib.SoftwareDisabler; using LenovoLegionToolkit.Lib.Utils; using LenovoLegionToolkit.WPF.CLI; using LenovoLegionToolkit.WPF.Extensions; using LenovoLegionToolkit.WPF.Pages; using LenovoLegionToolkit.WPF.Resources; using LenovoLegionToolkit.WPF.Utils; using LenovoLegionToolkit.WPF.Windows; using LenovoLegionToolkit.WPF.Windows.Utils; using Application = System.Windows.Application; using MessageBox = System.Windows.MessageBox; using WinFormsApp = System.Windows.Forms.Application; using WinFormsHighDpiMode = System.Windows.Forms.HighDpiMode; namespace LenovoLegionToolkit.WPF; public partial class App { private const string MUTEX_NAME = "LenovoLegionToolkit_Mutex_6efcc882-924c-4cbc-8fec-f45c25696f98"; private const string EVENT_NAME = "LenovoLegionToolkit_Event_6efcc882-924c-4cbc-8fec-f45c25696f98"; private Mutex? _singleInstanceMutex; private EventWaitHandle? _singleInstanceWaitHandle; public new static App Current => (App)Application.Current; private async void Application_Startup(object sender, StartupEventArgs e) { #if DEBUG if (Debugger.IsAttached) { Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName) .Where(p => p.Id != Environment.ProcessId) .ForEach(p => { p.Kill(); p.WaitForExit(); }); } #endif var flags = new Flags(e.Args); Log.Instance.IsTraceEnabled = flags.IsTraceEnabled; AppDomain.CurrentDomain.UnhandledException += AppDomain_UnhandledException; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Flags: {flags}"); EnsureSingleInstance(); await LocalizationHelper.SetLanguageAsync(true); if (!flags.SkipCompatibilityCheck) { try { if (!await CheckBasicCompatibilityAsync()) return; if (!await CheckCompatibilityAsync()) return; } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to check device compatibility", ex); MessageBox.Show(Resource.CompatibilityCheckError_Message, Resource.AppName, MessageBoxButton.OK, MessageBoxImage.Error); Shutdown(200); return; } } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Starting... [version={Assembly.GetEntryAssembly()?.GetName().Version}, build={Assembly.GetEntryAssembly()?.GetBuildDateTimeString()}, os={Environment.OSVersion}, dotnet={Environment.Version}]"); WinFormsApp.SetHighDpiMode(WinFormsHighDpiMode.PerMonitorV2); RenderOptions.ProcessRenderMode = RenderMode.SoftwareOnly; IoCContainer.Initialize( new Lib.IoCModule(), new Lib.Automation.IoCModule(), new Lib.Macro.IoCModule(), new IoCModule() ); IoCContainer.Resolve().SetProxy(flags.ProxyUrl, flags.ProxyUsername, flags.ProxyPassword, flags.ProxyAllowAllCerts); IoCContainer.Resolve().AllowAllPowerModesOnBattery = flags.AllowAllPowerModesOnBattery; IoCContainer.Resolve().ForceDisable = flags.ForceDisableRgbKeyboardSupport; IoCContainer.Resolve().ForceDisable = flags.ForceDisableSpectrumKeyboardSupport; IoCContainer.Resolve().ForceDisable = flags.ForceDisableLenovoLighting; IoCContainer.Resolve().ForceDisable = flags.ForceDisableLenovoLighting; IoCContainer.Resolve().ForceDisable = flags.ForceDisableLenovoLighting; IoCContainer.Resolve().ExperimentalGPUWorkingMode = flags.ExperimentalGPUWorkingMode; IoCContainer.Resolve().ExperimentalGPUWorkingMode = flags.ExperimentalGPUWorkingMode; IoCContainer.Resolve().Disable = flags.DisableUpdateChecker; AutomationPage.EnableHybridModeAutomation = flags.EnableHybridModeAutomation; await LogSoftwareStatusAsync(); await InitPowerModeFeatureAsync(); await InitBatteryFeatureAsync(); await InitRgbKeyboardControllerAsync(); await InitSpectrumKeyboardControllerAsync(); await InitGpuOverclockControllerAsync(); await InitHybridModeAsync(); await InitAutomationProcessorAsync(); InitMacroController(); await IoCContainer.Resolve().StartIfNeededAsync(); await IoCContainer.Resolve().StartStopIfNeededAsync(); await IoCContainer.Resolve().StartStopIfNeededAsync(); await IoCContainer.Resolve().StartStopIfNeededAsync(); #if !DEBUG Autorun.Validate(); #endif var mainWindow = new MainWindow { WindowStartupLocation = WindowStartupLocation.CenterScreen, TrayTooltipEnabled = !flags.DisableTrayTooltip, DisableConflictingSoftwareWarning = flags.DisableConflictingSoftwareWarning }; MainWindow = mainWindow; IoCContainer.Resolve().Apply(); if (flags.Minimized) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Sending MainWindow to tray..."); mainWindow.WindowState = WindowState.Minimized; mainWindow.Show(); mainWindow.SendToTray(); } else { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Showing MainWindow..."); mainWindow.Show(); } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Start up complete"); } private void Application_Exit(object sender, ExitEventArgs e) { _singleInstanceMutex?.Close(); } public void RestartMainWindow() { if (MainWindow is MainWindow mw) { mw.SuppressClosingEventHandler = true; mw.Close(); } var mainWindow = new MainWindow { WindowStartupLocation = WindowStartupLocation.CenterScreen }; MainWindow = mainWindow; mainWindow.Show(); } public async Task ShutdownAsync() { try { if (IoCContainer.TryResolve() is { } aiController) await aiController.StopAsync(); } catch { /* Ignored. */ } try { if (IoCContainer.TryResolve() is { } rgbKeyboardBacklightController) { if (await rgbKeyboardBacklightController.IsSupportedAsync()) await rgbKeyboardBacklightController.SetLightControlOwnerAsync(false); } } catch { /* Ignored. */ } try { if (IoCContainer.TryResolve() is { } spectrumKeyboardBacklightController) { if (await spectrumKeyboardBacklightController.IsSupportedAsync()) await spectrumKeyboardBacklightController.StopAuroraIfNeededAsync(); } } catch { /* Ignored. */ } try { if (IoCContainer.TryResolve() is { } nativeMessageWindowListener) { await nativeMessageWindowListener.StopAsync(); } } catch { /* Ignored. */ } try { if (IoCContainer.TryResolve() is { } sessionLockUnlockListener) { await sessionLockUnlockListener.StopAsync(); } } catch { /* Ignored. */ } try { if (IoCContainer.TryResolve() is { } hwinfoIntegration) { await hwinfoIntegration.StopAsync(); } } catch { /* Ignored. */ } try { if (IoCContainer.TryResolve() is { } ipcServer) { await ipcServer.StopAsync(); } } catch { /* Ignored. */ } try { if (IoCContainer.TryResolve() is { } batteryDischargeMon) { await batteryDischargeMon.StopAsync(); } } catch { /* Ignored. */ } Shutdown(); } private void AppDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { var exception = e.ExceptionObject as Exception; Log.Instance.ErrorReport("AppDomain_UnhandledException", exception ?? new Exception($"Unknown exception caught: {e.ExceptionObject}")); Log.Instance.Trace($"Unhandled exception occurred.", exception); MessageBox.Show(string.Format(Resource.UnexpectedException, exception?.ToStringDemystified() ?? "Unknown exception."), "Application Domain Error", MessageBoxButton.OK, MessageBoxImage.Error); Shutdown(100); } private void Application_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) { Log.Instance.ErrorReport("Application_DispatcherUnhandledException", e.Exception); Log.Instance.Trace($"Unhandled exception occurred.", e.Exception); MessageBox.Show(string.Format(Resource.UnexpectedException, e.Exception.ToStringDemystified()), "Application Error", MessageBoxButton.OK, MessageBoxImage.Error); Shutdown(101); } private async Task CheckBasicCompatibilityAsync() { var isCompatible = await Compatibility.CheckBasicCompatibilityAsync(); if (isCompatible) return true; MessageBox.Show(Resource.IncompatibleDevice_Message, Resource.AppName, MessageBoxButton.OK, MessageBoxImage.Error); Shutdown(201); return false; } private async Task CheckCompatibilityAsync() { var (isCompatible, mi) = await Compatibility.IsCompatibleAsync(); if (isCompatible) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Compatibility check passed. [Vendor={mi.Vendor}, Model={mi.Model}, MachineType={mi.MachineType}, BIOS={mi.BiosVersion}]"); return true; } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Incompatible system detected. [Vendor={mi.Vendor}, Model={mi.Model}, MachineType={mi.MachineType}, BIOS={mi.BiosVersion}]"); var unsupportedWindow = new UnsupportedWindow(mi); unsupportedWindow.Show(); var result = await unsupportedWindow.ShouldContinue; if (result) { Log.Instance.IsTraceEnabled = true; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Compatibility check OVERRIDE. [Vendor={mi.Vendor}, Model={mi.Model}, MachineType={mi.MachineType}, version={Assembly.GetEntryAssembly()?.GetName().Version}, build={Assembly.GetEntryAssembly()?.GetBuildDateTimeString() ?? string.Empty}]"); return true; } if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Shutting down... [Vendor={mi.Vendor}, Model={mi.Model}, MachineType={mi.MachineType}]"); Shutdown(202); return false; } private void EnsureSingleInstance() { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Checking for other instances..."); _singleInstanceMutex = new Mutex(true, MUTEX_NAME, out var isOwned); _singleInstanceWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset, EVENT_NAME); if (!isOwned) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Another instance running, closing..."); _singleInstanceWaitHandle.Set(); Shutdown(); return; } new Thread(() => { while (_singleInstanceWaitHandle.WaitOne()) { Current.Dispatcher.BeginInvoke(async () => { if (Current.MainWindow is { } window) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Another instance started, bringing this one to front instead..."); window.BringToForeground(); } else { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"!!! PANIC !!! This instance is missing main window. Shutting down."); await ShutdownAsync(); } }); } }) { IsBackground = true }.Start(); } private static async Task LogSoftwareStatusAsync() { if (!Log.Instance.IsTraceEnabled) return; var vantageStatus = await IoCContainer.Resolve().GetStatusAsync(); Log.Instance.Trace($"Vantage status: {vantageStatus}"); var legionZoneStatus = await IoCContainer.Resolve().GetStatusAsync(); Log.Instance.Trace($"LegionZone status: {legionZoneStatus}"); var fnKeysStatus = await IoCContainer.Resolve().GetStatusAsync(); Log.Instance.Trace($"FnKeys status: {fnKeysStatus}"); } private static async Task InitHybridModeAsync() { try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Initializing hybrid mode..."); var feature = IoCContainer.Resolve(); await feature.EnsureDGPUEjectedIfNeededAsync(); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Couldn't initialize hybrid mode.", ex); } } private static async Task InitAutomationProcessorAsync() { try { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Initializing automation processor..."); var automationProcessor = IoCContainer.Resolve(); await automationProcessor.InitializeAsync(); automationProcessor.RunOnStartup(); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Couldn't initialize automation processor.", ex); } } private static async Task InitPowerModeFeatureAsync() { try { var feature = IoCContainer.Resolve(); if (await feature.IsSupportedAsync()) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Ensuring god mode state is applied..."); await feature.EnsureGodModeStateIsAppliedAsync(); } } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Couldn't ensure god mode state.", ex); } try { var feature = IoCContainer.Resolve(); if (await feature.IsSupportedAsync()) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Ensuring correct power plan is set..."); await feature.EnsureCorrectWindowsPowerSettingsAreSetAsync(); } } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Couldn't ensure correct power plan.", ex); } } private static async Task InitBatteryFeatureAsync() { try { var feature = IoCContainer.Resolve(); if (await feature.IsSupportedAsync()) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Ensuring correct battery mode is set..."); await feature.EnsureCorrectBatteryModeIsSetAsync(); } } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Couldn't ensure correct battery mode.", ex); } } private static async Task InitRgbKeyboardControllerAsync() { try { var controller = IoCContainer.Resolve(); if (await controller.IsSupportedAsync()) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Setting light control owner and restoring preset..."); await controller.SetLightControlOwnerAsync(true, true); } else { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"RGB keyboard is not supported."); } } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Couldn't set light control owner or current preset.", ex); } } private static async Task InitSpectrumKeyboardControllerAsync() { try { var controller = IoCContainer.Resolve(); if (await controller.IsSupportedAsync()) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Starting Aurora if needed..."); var result = await controller.StartAuroraIfNeededAsync(); if (result) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Aurora started."); } else { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Aurora not needed."); } } else { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Spectrum keyboard is not supported."); } } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Couldn't start Aurora if needed.", ex); } } private static async Task InitGpuOverclockControllerAsync() { try { var controller = IoCContainer.Resolve(); if (await controller.IsSupportedAsync()) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Ensuring GPU overclock is applied..."); var result = await controller.EnsureOverclockIsAppliedAsync(); if (result) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"GPU overclock applied."); } else { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"GPU overclock not needed."); } } else { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"GPU overclock is not supported."); } } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Couldn't overclock GPU.", ex); } } private static void InitMacroController() { var controller = IoCContainer.Resolve(); controller.Start(); } } ================================================ FILE: LenovoLegionToolkit.WPF/Assets/AssetResources.Designer.cs ================================================ //------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ namespace LenovoLegionToolkit.WPF.Assets { using System; /// /// A strongly-typed resource class, for looking up localized strings, etc. /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class AssetResources { private static global::System.Resources.ResourceManager resourceMan; private static global::System.Globalization.CultureInfo resourceCulture; [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal AssetResources() { } /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LenovoLegionToolkit.WPF.Assets.AssetResources", typeof(AssetResources).Assembly); resourceMan = temp; } return resourceMan; } } /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } set { resourceCulture = value; } } /// /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). /// internal static System.Drawing.Icon icon { get { object obj = ResourceManager.GetObject("icon", resourceCulture); return ((System.Drawing.Icon)(obj)); } } } } ================================================ FILE: LenovoLegionToolkit.WPF/Assets/AssetResources.resx ================================================  text/microsoft-resx 2.0 System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 icon.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ================================================ FILE: LenovoLegionToolkit.WPF/Behaviors/ProgressBarAnimateBehavior.cs ================================================ using System; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Media.Animation; using Microsoft.Xaml.Behaviors; namespace LenovoLegionToolkit.WPF.Behaviors; public class ProgressBarAnimateBehavior : Behavior { private bool _isAnimating; protected override void OnAttached() { base.OnAttached(); AssociatedObject.ValueChanged += ProgressBar_ValueChanged; } protected override void OnDetaching() { base.OnDetaching(); AssociatedObject.ValueChanged -= ProgressBar_ValueChanged; } private void ProgressBar_ValueChanged(object sender, RoutedPropertyChangedEventArgs e) { if (sender is not ProgressBar progressBar) return; if (_isAnimating) return; _isAnimating = true; var doubleAnimation = new DoubleAnimation(e.OldValue, e.NewValue, new Duration(TimeSpan.FromMilliseconds(250)), FillBehavior.Stop); doubleAnimation.Completed += Completed; progressBar.BeginAnimation(RangeBase.ValueProperty, doubleAnimation); e.Handled = true; } private void Completed(object? sender, EventArgs e) => _isAnimating = false; } ================================================ FILE: LenovoLegionToolkit.WPF/CLI/Features/FeatureRegistration.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using LenovoLegionToolkit.Lib; using LenovoLegionToolkit.Lib.Features; namespace LenovoLegionToolkit.WPF.CLI.Features; public class FeatureRegistration(string name, Func? toStringConverter = null, Func? fromStringConverter = null) : IFeatureRegistration where T : struct { public string Name { get; } = name; private readonly Func> _feature = IoCContainer.Resolve>; public Task IsSupportedAsync() => _feature().IsSupportedAsync(); public async Task> GetValuesAsync() { var feature = _feature(); if (!await feature.IsSupportedAsync().ConfigureAwait(false)) throw new InvalidOperationException("Feature is not supported"); var states = await feature.GetAllStatesAsync().ConfigureAwait(false); return states.Select(s => toStringConverter?.Invoke(s) ?? s.ToString()?.ToLowerInvariant()).OfType(); } public async Task GetValueAsync() { var feature = _feature(); if (!await feature.IsSupportedAsync().ConfigureAwait(false)) throw new InvalidOperationException("Feature is not supported"); var state = await feature.GetStateAsync().ConfigureAwait(false); string result; if (toStringConverter is not null) { result = toStringConverter(state); } else { result = state.ToString()?.ToLowerInvariant() ?? throw new InvalidOperationException("Null return value"); } return result; } public async Task SetValueAsync(string value) { var feature = _feature(); if (!await feature.IsSupportedAsync().ConfigureAwait(false)) throw new InvalidOperationException("Feature is not supported"); var states = await feature.GetAllStatesAsync().ConfigureAwait(false); T state; if (fromStringConverter is not null) { state = fromStringConverter(value); } else { state = Enum.TryParse(value, true, out var s) ? s : throw new InvalidOperationException("State is not supported"); } if (!states.Contains(state)) throw new InvalidOperationException("State is not supported"); await feature.SetStateAsync(state).ConfigureAwait(false); } } ================================================ FILE: LenovoLegionToolkit.WPF/CLI/Features/FeatureRegistry.cs ================================================ using System; using System.Linq; using LenovoLegionToolkit.Lib; namespace LenovoLegionToolkit.WPF.CLI.Features; public static class FeatureRegistry { public static readonly IFeatureRegistration[] All = [ new FeatureRegistration("always-on-usb"), new FeatureRegistration("battery"), new FeatureRegistration("battery-night-charge"), new FeatureRegistration("flip-to-start"), new FeatureRegistration("fn-lock"), new FeatureRegistration("hdr"), new FeatureRegistration("hybrid-mode"), new FeatureRegistration("instant-boot"), new FeatureRegistration("microphone"), new FeatureRegistration("one-level-white-keyboard-backlight"), new FeatureRegistration("over-drive"), new FeatureRegistration("panel-logo-backlight"), new FeatureRegistration("ports-backlight"), new FeatureRegistration("power-mode"), new FeatureRegistration("refresh-rate", s => s.Frequency.ToString(), s => new(Convert.ToInt32(s))), new FeatureRegistration("resolution", s => $"{s.Width}x{s.Height}", s => { var split = s.Split('x'); return new(Convert.ToInt32(split.FirstOrDefault()), Convert.ToInt32(split.LastOrDefault())); }), new FeatureRegistration("speaker"), new FeatureRegistration("touchpad-lock"), new FeatureRegistration("win-key"), new FeatureRegistration("white-keyboard-backlight"), ]; } ================================================ FILE: LenovoLegionToolkit.WPF/CLI/Features/IFeatureRegistration.cs ================================================ using System.Collections.Generic; using System.Threading.Tasks; namespace LenovoLegionToolkit.WPF.CLI.Features; public interface IFeatureRegistration { string Name { get; } Task IsSupportedAsync(); Task> GetValuesAsync(); Task GetValueAsync(); Task SetValueAsync(string value); } ================================================ FILE: LenovoLegionToolkit.WPF/CLI/IpcServer.cs ================================================ using System; using System.Collections.Generic; using System.IO.Pipes; using System.Linq; using System.Security.AccessControl; using System.Security.Principal; using System.Threading; using System.Threading.Tasks; using LenovoLegionToolkit.CLI.Lib; using LenovoLegionToolkit.CLI.Lib.Extensions; using LenovoLegionToolkit.Lib; using LenovoLegionToolkit.Lib.Automation; using LenovoLegionToolkit.Lib.Controllers; using LenovoLegionToolkit.Lib.Messaging; using LenovoLegionToolkit.Lib.Messaging.Messages; using LenovoLegionToolkit.Lib.Settings; using LenovoLegionToolkit.Lib.Utils; using LenovoLegionToolkit.WPF.CLI.Features; namespace LenovoLegionToolkit.WPF.CLI; public class IpcServer( AutomationProcessor automationProcessor, SpectrumKeyboardBacklightController spectrumKeyboardBacklightController, RGBKeyboardBacklightController rgbKeyboardBacklightController, IntegrationsSettings settings ) { private CancellationTokenSource _cancellationTokenSource = new(); private Task _handler = Task.CompletedTask; public async Task StartStopIfNeededAsync() { await StopAsync().ConfigureAwait(false); if (!settings.Store.CLI) return; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Starting..."); _cancellationTokenSource = new(); var token = _cancellationTokenSource.Token; _handler = Task.Run(() => Handler(token), token); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Started"); } public async Task StopAsync() { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Stopping..."); await _cancellationTokenSource.CancelAsync(); await _handler; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Stopped"); } private async Task Handler(CancellationToken token) { try { var identity = new SecurityIdentifier(WellKnownSidType.AuthenticatedUserSid, null); var security = new PipeSecurity(); security.AddAccessRule(new(identity, PipeAccessRights.ReadWrite, AccessControlType.Allow)); await using var pipe = NamedPipeServerStreamAcl.Create(LenovoLegionToolkit.CLI.Lib.Constants.PIPE_NAME, PipeDirection.InOut, 1, PipeTransmissionMode.Message, PipeOptions.None, 0, 0, security); while (!token.IsCancellationRequested) { await pipe.WaitForConnectionAsync(token).ConfigureAwait(false); if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Connection received."); try { var req = await pipe.ReadObjectAsync(token).ConfigureAwait(false); if (req?.Operation is null) throw new IpcException("Failed to deserialize request"); var res = await HandleRequest(req).ConfigureAwait(false); await pipe.WriteObjectAsync(res, token).ConfigureAwait(false); } catch (OperationCanceledException) { throw; } catch (Exception ex) { var res = new IpcResponse { Success = false, Message = ex.Message }; await pipe.WriteObjectAsync(res, token).ConfigureAwait(false); } finally { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Disconnecting..."); pipe.Disconnect(); } } } catch (OperationCanceledException) { } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Unknown failure.", ex); } } private async Task HandleRequest(IpcRequest req) { string? message; switch (req.Operation) { case IpcRequest.OperationType.ListQuickActions: message = await ListQuickActionsAsync().ConfigureAwait(false); return new IpcResponse { Success = true, Message = message }; case IpcRequest.OperationType.QuickAction when req is { Name: not null }: await RunQuickActionAsync(req.Name).ConfigureAwait(false); return new IpcResponse { Success = true }; case IpcRequest.OperationType.ListFeatures: message = await ListFeaturesAsync(); return new IpcResponse { Success = true, Message = message }; case IpcRequest.OperationType.ListFeatureValues when req is { Name: not null }: message = await ListFeatureValuesAsync(req.Name); return new IpcResponse { Success = true, Message = message }; case IpcRequest.OperationType.GetFeatureValue when req is { Name: not null }: message = await GetFeatureValueAsync(req.Name); return new IpcResponse { Success = true, Message = message }; case IpcRequest.OperationType.SetFeatureValue when req is { Name: not null, Value: not null }: await SetFeatureValueAsync(req.Name, req.Value).ConfigureAwait(false); return new IpcResponse { Success = true }; case IpcRequest.OperationType.GetSpectrumProfile: message = await GetSpectrumProfileAsync(); return new IpcResponse { Success = true, Message = message }; case IpcRequest.OperationType.SetSpectrumProfile when req is { Value: not null }: await SetSpectrumProfileAsync(req.Value); return new IpcResponse { Success = true }; case IpcRequest.OperationType.GetSpectrumBrightness: message = await GetSpectrumBrightnessAsync(); return new IpcResponse { Success = true, Message = message }; case IpcRequest.OperationType.SetSpectrumBrightness when req is { Value: not null }: await SetSpectrumBrightnessAsync(req.Value); return new IpcResponse { Success = true }; case IpcRequest.OperationType.GetRGBPreset: message = await GetRGBPresetAsync(); return new IpcResponse { Success = true, Message = message }; case IpcRequest.OperationType.SetRGBPreset when req is { Value: not null }: await SetRGBPresetAsync(req.Value); return new IpcResponse { Success = true }; default: throw new IpcException("Invalid request"); } } private async Task ListQuickActionsAsync() { var pipelines = await automationProcessor.GetPipelinesAsync().ConfigureAwait(false); var quickActions = pipelines .Where(p => p.Trigger is null) .Select(p => p.Name); return string.Join('\n', quickActions); } private async Task RunQuickActionAsync(string name) { var pipelines = await automationProcessor.GetPipelinesAsync().ConfigureAwait(false); var quickAction = pipelines .Where(p => p.Trigger is null) .FirstOrDefault(p => p.Name == name) ?? throw new InvalidOperationException($"Quick Action \"{name}\" not found"); await automationProcessor.RunNowAsync(quickAction.Id); } private static async Task ListFeaturesAsync() { var features = new List(); foreach (var feature in FeatureRegistry.All) { if (await feature.IsSupportedAsync().ConfigureAwait(false)) features.Add(feature.Name); } return string.Join('\n', features); } private static async Task ListFeatureValuesAsync(string name) { var feature = FeatureRegistry.All.FirstOrDefault(f => f.Name == name) ?? throw new IpcException("Invalid feature"); var values = await feature.GetValuesAsync().ConfigureAwait(false); return string.Join('\n', values); } private static async Task GetFeatureValueAsync(string name) { var feature = FeatureRegistry.All.FirstOrDefault(f => f.Name == name) ?? throw new IpcException("Invalid feature"); return await feature.GetValueAsync().ConfigureAwait(false); } private static async Task SetFeatureValueAsync(string name, string value) { var feature = FeatureRegistry.All.FirstOrDefault(f => f.Name == name) ?? throw new IpcException("Invalid feature"); await feature.SetValueAsync(value).ConfigureAwait(false); } private async Task GetSpectrumProfileAsync() { if (!await spectrumKeyboardBacklightController.IsSupportedAsync().ConfigureAwait(false)) throw new InvalidOperationException("Spectrum is not supported"); var profile = await spectrumKeyboardBacklightController.GetProfileAsync().ConfigureAwait(false); return $"{profile}"; } private async Task SetSpectrumProfileAsync(string value) { if (!await spectrumKeyboardBacklightController.IsSupportedAsync().ConfigureAwait(false)) throw new InvalidOperationException("Spectrum is not supported"); await spectrumKeyboardBacklightController.SetProfileAsync(Convert.ToInt32(value)).ConfigureAwait(false); MessagingCenter.Publish(new SpectrumBacklightChangedMessage()); } private async Task GetSpectrumBrightnessAsync() { if (!await spectrumKeyboardBacklightController.IsSupportedAsync().ConfigureAwait(false)) throw new InvalidOperationException("Spectrum is not supported"); var profile = await spectrumKeyboardBacklightController.GetBrightnessAsync().ConfigureAwait(false); return $"{profile}"; } private async Task SetSpectrumBrightnessAsync(string value) { if (!await spectrumKeyboardBacklightController.IsSupportedAsync().ConfigureAwait(false)) throw new InvalidOperationException("Spectrum is not supported"); await spectrumKeyboardBacklightController.SetBrightnessAsync(Convert.ToInt32(value)).ConfigureAwait(false); MessagingCenter.Publish(new SpectrumBacklightChangedMessage()); } private async Task GetRGBPresetAsync() { if (!await rgbKeyboardBacklightController.IsSupportedAsync().ConfigureAwait(false)) throw new InvalidOperationException("RGB is not supported"); var state = await rgbKeyboardBacklightController.GetStateAsync().ConfigureAwait(false); return $"{(int)state.SelectedPreset + 1}"; } private async Task SetRGBPresetAsync(string value) { if (!await rgbKeyboardBacklightController.IsSupportedAsync().ConfigureAwait(false)) throw new InvalidOperationException("RGB is not supported"); var preset = (RGBKeyboardBacklightPreset)(Convert.ToInt32(value) - 1); if (!Enum.IsDefined(preset)) throw new InvalidOperationException("Invalid preset"); await rgbKeyboardBacklightController.SetLightControlOwnerAsync(true).ConfigureAwait(false); await rgbKeyboardBacklightController.SetPresetAsync(preset).ConfigureAwait(false); MessagingCenter.Publish(new RGBKeyboardBacklightChangedMessage()); } } ================================================ FILE: LenovoLegionToolkit.WPF/Constants.cs ================================================ using System; namespace LenovoLegionToolkit.WPF; public static class Constants { public static readonly Uri LatestReleaseUri = new("https://github.com/BartoszCichecki/LenovoLegionToolkit/releases/latest"); public static readonly Uri PayPalUri = new("https://www.paypal.com/donate/?hosted_button_id=22AZE2NBP3HTL"); } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/AbstractComboBoxFeatureCardControl.cs ================================================ using System; using System.Threading.Tasks; using System.Windows; using System.Windows.Automation; using System.Windows.Controls; using LenovoLegionToolkit.Lib; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Features; using LenovoLegionToolkit.Lib.Messaging; using LenovoLegionToolkit.Lib.Messaging.Messages; using LenovoLegionToolkit.Lib.Utils; using LenovoLegionToolkit.WPF.Controls.Custom; using LenovoLegionToolkit.WPF.Extensions; using Wpf.Ui.Common; namespace LenovoLegionToolkit.WPF.Controls; public abstract class AbstractComboBoxFeatureCardControl : AbstractRefreshingControl where T : struct { protected readonly IFeature Feature = IoCContainer.Resolve>(); private readonly CardControl _cardControl = new(); private readonly CardHeaderControl _cardHeaderControl = new(); private readonly ComboBox _comboBox = new(); protected SymbolRegular Icon { get => _cardControl.Icon; set => _cardControl.Icon = value; } protected string Title { get => _cardHeaderControl.Title; set { _cardHeaderControl.Title = value; AutomationProperties.SetName(_comboBox, value); } } protected string Subtitle { get => _cardHeaderControl.Subtitle; set => _cardHeaderControl.Subtitle = value; } protected string Warning { get => _cardHeaderControl.Warning; set => _cardHeaderControl.Warning = value; } protected AbstractComboBoxFeatureCardControl() => InitializeComponent(); private void InitializeComponent() { _comboBox.SelectionChanged += ComboBox_SelectionChanged; _comboBox.MinWidth = 165; _comboBox.Visibility = Visibility.Hidden; _comboBox.Margin = new(8, 0, 0, 0); _cardHeaderControl.Accessory = GetAccessory(_comboBox); _cardControl.Header = _cardHeaderControl; _cardControl.Margin = new(0, 0, 0, 8); Content = _cardControl; } private async void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { await OnStateChangeAsync(_comboBox, Feature, e.GetNewValue(), e.GetOldValue()); } protected bool TryGetSelectedItem(out T value) => _comboBox.TryGetSelectedItem(out value); protected int ItemsCount => _comboBox.Items.Count; protected virtual FrameworkElement GetAccessory(ComboBox comboBox) => comboBox; protected virtual string ComboBoxItemDisplayName(T value) => value switch { IDisplayName dn => dn.DisplayName, Enum e => e.GetDisplayName(), _ => value.ToString() ?? throw new InvalidOperationException("Unsupported type") }; protected override async Task OnRefreshAsync() { if (!await Feature.IsSupportedAsync()) throw new NotSupportedException(); var items = await Feature.GetAllStatesAsync(); var selectedItem = await Feature.GetStateAsync(); _comboBox.SetItems(items, selectedItem, ComboBoxItemDisplayName); _comboBox.IsEnabled = items.Length != 0; _comboBox.Visibility = Visibility.Visible; } protected override void OnFinishedLoading() { MessagingCenter.Subscribe>(this, () => Dispatcher.InvokeTask(async () => { if (!IsVisible) return; await RefreshAsync(); })); } protected virtual async Task OnStateChangeAsync(ComboBox comboBox, IFeature feature, T? newValue, T? oldValue) { var exceptionOccurred = false; try { if (IsRefreshing) return; _comboBox.IsEnabled = false; if (oldValue is null) return; if (!comboBox.TryGetSelectedItem(out T selectedState)) return; var currentState = await feature.GetStateAsync(); if (selectedState.Equals(currentState)) return; await feature.SetStateAsync(selectedState); } catch (Exception ex) { exceptionOccurred = true; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to change state. [feature={GetType().Name}]", ex); OnStateChangeException(ex); } finally { var delay = AdditionalStateChangeDelay(oldValue, newValue); if (delay > TimeSpan.Zero) await Task.Delay(delay); _comboBox.IsEnabled = true; } if (exceptionOccurred) await RefreshAsync(); } protected virtual void OnStateChangeException(Exception exception) { } protected virtual TimeSpan AdditionalStateChangeDelay(T? oldValue, T? newValue) => TimeSpan.Zero; } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/AbstractRefreshingControl.cs ================================================ using System; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using LenovoLegionToolkit.Lib.Utils; namespace LenovoLegionToolkit.WPF.Controls; public abstract class AbstractRefreshingControl : UserControl { private Task? _refreshTask; protected bool IsRefreshing => _refreshTask is not null; protected virtual bool DisablesWhileRefreshing => true; protected AbstractRefreshingControl() { IsEnabled = false; Loaded += RefreshingControl_Loaded; IsVisibleChanged += RefreshingControl_IsVisibleChanged; } private void RefreshingControl_Loaded(object sender, RoutedEventArgs e) { OnFinishedLoading(); } protected abstract void OnFinishedLoading(); private async void RefreshingControl_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) { if (IsVisible) await RefreshAsync(); } protected async Task RefreshAsync() { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Refreshing control... [feature={GetType().Name}]"); var exceptions = false; try { if (DisablesWhileRefreshing) IsEnabled = false; _refreshTask ??= OnRefreshAsync(); await _refreshTask; } catch (NotSupportedException) { exceptions = true; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Unsupported. [feature={GetType().Name}]"); } catch (Exception ex) { exceptions = true; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Exception when refreshing control. [feature={GetType().Name}]", ex); } finally { _refreshTask = null; if (exceptions) Visibility = Visibility.Collapsed; else IsEnabled = true; } } protected abstract Task OnRefreshAsync(); } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/AbstractToggleFeatureCardControl.cs ================================================ using System; using System.Threading.Tasks; using System.Windows; using System.Windows.Automation; using LenovoLegionToolkit.Lib; using LenovoLegionToolkit.Lib.Features; using LenovoLegionToolkit.Lib.Messaging; using LenovoLegionToolkit.Lib.Messaging.Messages; using LenovoLegionToolkit.Lib.Utils; using LenovoLegionToolkit.WPF.Extensions; using Wpf.Ui.Common; using Wpf.Ui.Controls; using CardControl = LenovoLegionToolkit.WPF.Controls.Custom.CardControl; namespace LenovoLegionToolkit.WPF.Controls; public abstract class AbstractToggleFeatureCardControl : AbstractRefreshingControl where T : struct { protected readonly IFeature Feature = IoCContainer.Resolve>(); private readonly CardControl _cardControl = new(); private readonly CardHeaderControl _cardHeaderControl = new(); private readonly ToggleSwitch _toggle = new(); protected SymbolRegular Icon { get => _cardControl.Icon; set => _cardControl.Icon = value; } protected string Title { get => _cardHeaderControl.Title; set { _cardHeaderControl.Title = value; AutomationProperties.SetName(_toggle, value); } } protected string Subtitle { get => _cardHeaderControl.Subtitle; set => _cardHeaderControl.Subtitle = value; } protected string Warning { get => _cardHeaderControl.Warning; set => _cardHeaderControl.Warning = value; } public bool IsToggleEnabled { get => _toggle.IsEnabled; set => _toggle.IsEnabled = value; } protected abstract T OnState { get; } protected abstract T OffState { get; } protected virtual TimeSpan AdditionalStateChangeDelay => TimeSpan.Zero; protected AbstractToggleFeatureCardControl() { InitializeComponent(); } private void InitializeComponent() { _toggle.Click += Toggle_Click; _toggle.Visibility = Visibility.Hidden; _toggle.Margin = new(8, 0, 0, 0); _cardHeaderControl.Accessory = _toggle; _cardControl.Header = _cardHeaderControl; _cardControl.Margin = new(0, 0, 0, 8); Content = _cardControl; } private async void Toggle_Click(object sender, RoutedEventArgs e) => await OnStateChange(_toggle, Feature); protected override async Task OnRefreshAsync() { if (!await Feature.IsSupportedAsync()) throw new NotSupportedException(); _toggle.IsChecked = OnState.Equals(await Feature.GetStateAsync()); _toggle.Visibility = Visibility.Visible; } protected override void OnFinishedLoading() { MessagingCenter.Subscribe>(this, () => Dispatcher.InvokeTask(async () => { if (!IsVisible) return; await RefreshAsync(); })); } protected virtual async Task OnStateChange(ToggleSwitch toggle, IFeature feature) { var exceptionOccurred = false; try { if (IsRefreshing || toggle.IsChecked is null) return; _toggle.IsEnabled = false; var state = toggle.IsChecked.Value ? OnState : OffState; if (state.Equals(await feature.GetStateAsync())) return; await feature.SetStateAsync(state); } catch (Exception ex) { exceptionOccurred = true; if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Failed to change state. [feature={GetType().Name}]", ex); OnStateChangeException(ex); } finally { if (AdditionalStateChangeDelay > TimeSpan.Zero) await Task.Delay(AdditionalStateChangeDelay); _toggle.IsEnabled = true; } if (exceptionOccurred) await RefreshAsync(); } protected virtual void OnStateChangeException(Exception exception) { } } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/AbstractAutomationStepControl.cs ================================================ using System; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.WPF.Resources; using Wpf.Ui.Common; using Button = Wpf.Ui.Controls.Button; using CardControl = LenovoLegionToolkit.WPF.Controls.Custom.CardControl; namespace LenovoLegionToolkit.WPF.Controls.Automation; public abstract class AbstractAutomationStepControl(T automationStep) : AbstractAutomationStepControl(automationStep) where T : IAutomationStep { protected new T AutomationStep => (T)base.AutomationStep; } public abstract class AbstractAutomationStepControl : UserControl { protected IAutomationStep AutomationStep { get; } private readonly CardControl _cardControl = new() { Margin = new(0, 0, 0, 8), }; private readonly CardHeaderControl _cardHeaderControl = new(); private readonly StackPanel _stackPanel = new() { Orientation = Orientation.Horizontal, }; private readonly Button _deleteButton = new() { Icon = SymbolRegular.Dismiss24, ToolTip = Resource.AbstractAutomationStepControl_Delete, MinWidth = 34, Height = 34, Margin = new(8, 0, 0, 0), }; public SymbolRegular Icon { get => _cardControl.Icon; set => _cardControl.Icon = value; } public string Title { get => _cardHeaderControl.Title; set => _cardHeaderControl.Title = value; } public string Subtitle { get => _cardHeaderControl.Subtitle; set => _cardHeaderControl.Subtitle = value; } public VerticalAlignment TitleVerticalAlignment { get => _cardHeaderControl.TitleVerticalAlignment; set => _cardHeaderControl.TitleVerticalAlignment = value; } public VerticalAlignment SubtitleVerticalAlignment { get => _cardHeaderControl.SubtitleVerticalAlignment; set => _cardHeaderControl.SubtitleVerticalAlignment = value; } public event EventHandler? Changed; public event EventHandler? Delete; protected AbstractAutomationStepControl(IAutomationStep automationStep) { AutomationStep = automationStep; InitializeComponent(); Loaded += RefreshingControl_Loaded; } private void InitializeComponent() { _deleteButton.Click += (_, _) => Delete?.Invoke(this, EventArgs.Empty); var control = GetCustomControl(); if (control is not null) _stackPanel.Children.Add(control); _stackPanel.Children.Add(_deleteButton); _cardHeaderControl.Accessory = _stackPanel; _cardControl.Header = _cardHeaderControl; Content = _cardControl; } private async void RefreshingControl_Loaded(object sender, RoutedEventArgs e) { await RefreshAsync(); OnFinishedLoading(); } public abstract IAutomationStep CreateAutomationStep(); protected abstract UIElement? GetCustomControl(); protected abstract void OnFinishedLoading(); protected abstract Task RefreshAsync(); protected void RaiseChanged() => Changed?.Invoke(this, EventArgs.Empty); } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/AbstractComboBoxAutomationStepControl.cs ================================================ using System; using System.Threading.Tasks; using System.Windows; using System.Windows.Automation; using System.Windows.Controls; using LenovoLegionToolkit.Lib; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.WPF.Extensions; namespace LenovoLegionToolkit.WPF.Controls.Automation; public abstract class AbstractComboBoxAutomationStepCardControl(IAutomationStep step) : AbstractAutomationStepControl>(step) where T : struct { private readonly ComboBox _comboBox = new() { MinWidth = 150, Visibility = Visibility.Hidden, Margin = new(8, 0, 0, 0) }; private T _state; protected override UIElement GetCustomControl() { _comboBox.SelectionChanged += ComboBox_SelectionChanged; return _comboBox; } private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (!_comboBox.TryGetSelectedItem(out T selectedState) || _state.Equals(selectedState)) return; _state = selectedState; RaiseChanged(); } public override IAutomationStep CreateAutomationStep() { var obj = Activator.CreateInstance(AutomationStep.GetType(), _state); if (obj is not IAutomationStep step) throw new InvalidOperationException("Couldn't create automation step"); return step; } protected virtual string ComboBoxItemDisplayName(T value) => value switch { IDisplayName dn => dn.DisplayName, Enum e => e.GetDisplayName(), _ => value.ToString() ?? throw new InvalidOperationException("Unsupported type") }; protected override async Task RefreshAsync() { AutomationProperties.SetName(_comboBox, Title); var items = await AutomationStep.GetAllStatesAsync(); var selectedItem = AutomationStep.State; _state = selectedItem; _comboBox.SetItems(items, selectedItem, ComboBoxItemDisplayName); _comboBox.IsEnabled = items.Length != 0; } protected override void OnFinishedLoading() => _comboBox.Visibility = Visibility.Visible; } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/AutomationPipelineControl.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using Humanizer; using LenovoLegionToolkit.Lib; using LenovoLegionToolkit.Lib.Automation; using LenovoLegionToolkit.Lib.Automation.Pipeline; using LenovoLegionToolkit.Lib.Automation.Pipeline.Triggers; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.Lib.Extensions; using LenovoLegionToolkit.Lib.Settings; using LenovoLegionToolkit.Lib.Utils; using LenovoLegionToolkit.WPF.Controls.Automation.Steps; using LenovoLegionToolkit.WPF.Extensions; using LenovoLegionToolkit.WPF.Resources; using LenovoLegionToolkit.WPF.Utils; using LenovoLegionToolkit.WPF.Windows.Automation; using Wpf.Ui.Common; using Button = Wpf.Ui.Controls.Button; using CardExpander = LenovoLegionToolkit.WPF.Controls.Custom.CardExpander; using MenuItem = Wpf.Ui.Controls.MenuItem; namespace LenovoLegionToolkit.WPF.Controls.Automation; public class AutomationPipelineControl : UserControl { private readonly TaskCompletionSource _initializedTaskCompletionSource = new(); private readonly AutomationProcessor _automationProcessor = IoCContainer.Resolve(); private readonly GodModeSettings _godModeSettings = IoCContainer.Resolve(); private readonly CardExpander _cardExpander = new() { Margin = new(0, 0, 0, 8), }; private readonly CardHeaderControl _cardHeaderControl = new(); private readonly StackPanel _stackPanel = new(); private readonly StackPanel _stepsStackPanel = new(); private readonly Grid _buttonsStackPanel = new() { Margin = new(0, 16, 0, 0), ColumnDefinitions = { new() { Width = new(1, GridUnitType.Star) }, new() { Width = GridLength.Auto }, new() { Width = GridLength.Auto }, new() { Width = GridLength.Auto }, } }; private readonly CheckBox _isExclusiveCheckBox = new() { HorizontalAlignment = HorizontalAlignment.Left, Content = Resource.AutomationPipelineControl_Exclusive, ToolTip = Resource.AutomationPipelineControl_Exclusive_ToolTip, MinWidth = 100, Margin = new(0, 0, 8, 0), }; private readonly Button _runNowButton = new() { Content = Resource.AutomationPipelineControl_RunNow, MinWidth = 100, Margin = new(0, 0, 8, 0), }; private readonly Button _addStepButton = new() { Content = Resource.AutomationPipelineControl_AddStep, MinWidth = 100, Margin = new(0, 0, 8, 0), }; private readonly Button _deletePipelineButton = new() { Content = Resource.Delete, MinWidth = 100, }; private readonly IAutomationStep[] _supportedAutomationSteps; public AutomationPipeline AutomationPipeline { get; } public event EventHandler? OnChanged; public event EventHandler? OnDelete; public Task InitializedTask => _initializedTaskCompletionSource.Task; public AutomationPipelineControl(AutomationPipeline automationPipeline, IAutomationStep[] supportedAutomationSteps) { AutomationPipeline = automationPipeline; _supportedAutomationSteps = supportedAutomationSteps; Initialized += AutomationPipelineControl_Initialized; } public AutomationPipeline CreateAutomationPipeline() => new() { Id = AutomationPipeline.Id, IconName = AutomationPipeline.IconName, Name = AutomationPipeline.Name, Trigger = AutomationPipeline.Trigger, Steps = _stepsStackPanel.Children.ToArray() .OfType() .Select(s => s.CreateAutomationStep()) .ToList(), IsExclusive = _isExclusiveCheckBox.IsChecked ?? false, }; public string? GetName() => AutomationPipeline.Name; public void SetName(string? name) { AutomationPipeline.Name = name; _cardHeaderControl.Title = GenerateHeader(); _cardHeaderControl.Subtitle = GenerateSubtitle(); _cardHeaderControl.SubtitleToolTip = _cardHeaderControl.Subtitle; OnChanged?.Invoke(this, EventArgs.Empty); } public void SetIcon(SymbolRegular? icon) { AutomationPipeline.IconName = icon.HasValue ? Enum.GetName(icon.Value) : null; _cardExpander.Icon = GenerateIcon(); OnChanged?.Invoke(this, EventArgs.Empty); } private async void AutomationPipelineControl_Initialized(object? sender, EventArgs e) { _cardExpander.Header = _cardHeaderControl; foreach (var step in AutomationPipeline.Steps) { var control = await GenerateStepControlAsync(step); _stepsStackPanel.Children.Add(control); } if (AutomationPipeline.Trigger is not null) { _isExclusiveCheckBox.IsChecked = AutomationPipeline.IsExclusive; _isExclusiveCheckBox.Checked += (_, _) => OnChanged?.Invoke(this, EventArgs.Empty); } else { _isExclusiveCheckBox.Visibility = Visibility.Hidden; } _runNowButton.Click += async (_, _) => await RunAsync(); _addStepButton.Click += async (_, _) => { var stepControls = new List(); foreach (var step in _supportedAutomationSteps) stepControls.Add(await GenerateStepControlAsync(step)); var window = new AddAutomationStepWindow(stepControls, AddStep) { Owner = Window.GetWindow(this) }; window.ShowDialog(); }; _deletePipelineButton.Click += (_, _) => OnDelete?.Invoke(this, EventArgs.Empty); Grid.SetColumn(_isExclusiveCheckBox, 0); Grid.SetColumn(_runNowButton, 1); Grid.SetColumn(_addStepButton, 2); Grid.SetColumn(_deletePipelineButton, 3); _buttonsStackPanel.Children.Add(_isExclusiveCheckBox); _buttonsStackPanel.Children.Add(_runNowButton); _buttonsStackPanel.Children.Add(_addStepButton); _buttonsStackPanel.Children.Add(_deletePipelineButton); _stackPanel.Children.Add(_stepsStackPanel); _stackPanel.Children.Add(_buttonsStackPanel); _cardExpander.Icon = GenerateIcon(); _cardHeaderControl.Title = GenerateHeader(); _cardHeaderControl.Subtitle = GenerateSubtitle(); _cardHeaderControl.Accessory = GenerateAccessory(); _cardHeaderControl.SubtitleToolTip = _cardHeaderControl.Subtitle; _cardExpander.Content = _stackPanel; Content = _cardExpander; _initializedTaskCompletionSource.TrySetResult(); } private async Task RunAsync() { try { _runNowButton.IsEnabled = false; _runNowButton.Content = Resource.AutomationPipelineControl_Running; var pipeline = CreateAutomationPipeline(); await _automationProcessor.RunNowAsync(pipeline); _runNowButton.Content = Resource.AutomationPipelineControl_RunNow; _runNowButton.IsEnabled = true; await SnackbarHelper.ShowAsync(Resource.AutomationPipelineControl_RunNow_Success_Title, Resource.AutomationPipelineControl_RunNow_Success_Message); } catch (Exception ex) { if (Log.Instance.IsTraceEnabled) Log.Instance.Trace($"Run now completed with errors", ex); _runNowButton.Content = Resource.AutomationPipelineControl_RunNow; _runNowButton.IsEnabled = true; await SnackbarHelper.ShowAsync(Resource.AutomationPipelineControl_RunNow_Error_Title, Resource.AutomationPipelineControl_RunNow_Error_Message); } } private SymbolRegular GenerateIcon() { if (AutomationPipeline.Trigger is not null) return SymbolRegular.Flow20; return Enum.TryParse(AutomationPipeline.IconName, out var icon) ? icon : SymbolRegular.Play24; } private string GenerateHeader() { if (!string.IsNullOrWhiteSpace(AutomationPipeline.Name)) return AutomationPipeline.Name; if (AutomationPipeline.Trigger is not null) return AutomationPipeline.Trigger.DisplayName; return Resource.AutomationPipelineControl_Unnamed; } private string GenerateSubtitle() { var stepsCount = _stepsStackPanel.Children.ToArray() .OfType() .Count(); var result = string.Format(stepsCount == 1 ? Resource.AutomationPipelineControl_Step : Resource.AutomationPipelineControl_Step_Many, stepsCount); if (!string.IsNullOrWhiteSpace(AutomationPipeline.Name) && AutomationPipeline.Trigger is not null) result += $" | {AutomationPipeline.Trigger.DisplayName}"; if (AutomationPipeline.Trigger is IPowerModeAutomationPipelineTrigger pm) result += $" | {Resource.AutomationPipelineControl_SubtitlePart_PowerMode}: {pm.PowerModeState.GetDisplayName()}"; if (AutomationPipeline.Trigger is IGodModePresetChangedAutomationPipelineTrigger gpt) { var name = _godModeSettings.Store.Presets.Where(kv => kv.Key == gpt.PresetId) .Select(kv => kv.Value.Name) .DefaultIfEmpty("-") .First(); result += $" | {Resource.AutomationPipelineControl_SubtitlePart_Preset}: {name}"; } if (AutomationPipeline.Trigger is IProcessesAutomationPipelineTrigger pt && pt.Processes.Length != 0) result += $" | {Resource.AutomationPipelineControl_SubtitlePart_Apps}: {string.Join(", ", pt.Processes.Select(p => p.Name))}"; if (AutomationPipeline.Trigger is ITimeAutomationPipelineTrigger tt) { if (tt.IsSunrise) result += $" | {Resource.AutomationPipelineControl_SubtitlePart_AtSunrise}"; if (tt.IsSunset) result += $" | {Resource.AutomationPipelineControl_SubtitlePart_AtSunset}"; if (tt.Time is not null) { var local = DateTimeExtensions.UtcFrom(tt.Time.Value.Hour, tt.Time.Value.Minute).ToLocalTime(); if (tt.Days.IsEmpty() || tt.Days.OrderBy(x => x).SequenceEqual(Enum.GetValues())) { result += $" | {string.Format(Resource.AutomationPipelineControl_SubtitlePart_AtTime, local.Hour, local.Minute)}"; } else { var localizedDayStrings = tt.Days.Select(day => Resource.Culture.DateTimeFormat.GetDayName(day)); result += $" | {string.Join(", ", localizedDayStrings)} {string.Format(Resource.AutomationPipelineControl_SubtitlePart_AtTime.ToLower(Resource.Culture), local.Hour, local.Minute)}"; } } } if (AutomationPipeline.Trigger is IUserInactivityPipelineTrigger ut && ut.InactivityTimeSpan > TimeSpan.Zero) result += $" | {string.Format(Resource.AutomationPipelineControl_SubtitlePart_After, ut.InactivityTimeSpan.Humanize(culture: Resource.Culture))}"; if (AutomationPipeline.Trigger is IWiFiConnectedPipelineTrigger wt && wt.Ssids.Length != 0) result += $" | {string.Join(",", wt.Ssids)}"; if (AutomationPipeline.Trigger is IPeriodicAutomationPipelineTrigger pet) result += $" | {Resource.PeriodicActionPipelineTriggerTabItemContent_PeriodMinutes}: {pet.Period.TotalMinutes}"; if (AutomationPipeline.Trigger is IDeviceAutomationPipelineTrigger dt && dt.InstanceIds.Length != 0) result += $" | {Resource.DevicePipelineTriggerTabItemContent_Devices}: {dt.InstanceIds.Length}"; return result; } private Button? GenerateAccessory() { var triggers = AutomationPipeline.AllTriggers .ToArray(); if (!AutomationPipelineTriggerConfigurationWindow.IsValid(triggers)) return null; var button = new Button { Content = Resource.AutomationPipelineControl_Configure, Margin = new(16, 0, 16, 0), MinWidth = 120, }; button.Click += (_, _) => { var window = new AutomationPipelineTriggerConfigurationWindow(triggers) { Owner = Window.GetWindow(this) }; window.OnSave += (_, e) => { AutomationPipeline.Trigger = e; _cardHeaderControl.Subtitle = GenerateSubtitle(); _cardHeaderControl.Accessory = GenerateAccessory(); _cardHeaderControl.SubtitleToolTip = _cardHeaderControl.Subtitle; OnChanged?.Invoke(this, EventArgs.Empty); }; window.ShowDialog(); }; return button; } private async Task GenerateStepControlAsync(IAutomationStep automationStep) { AbstractAutomationStepControl control = automationStep switch { AlwaysOnUsbAutomationStep s => new AlwaysOnUsbAutomationStepControl(s), BatteryAutomationStep s => new BatteryAutomationStepControl(s), BatteryNightChargeAutomationStep s => new BatteryNightChargeAutomationStepControl(s), DeactivateGPUAutomationStep s => new DeactivateGPUAutomationStepControl(s), DelayAutomationStep s => new DelayAutomationStepControl(s), DisplayBrightnessAutomationStep s => new DisplayBrightnessAutomationStepControl(s), DpiScaleAutomationStep s => new DpiScaleAutomationStepControl(s), FlipToStartAutomationStep s => new FlipToStartAutomationStepControl(s), FnLockAutomationStep s => new FnLockAutomationStepControl(s), GodModePresetAutomationStep s => new GodModePresetAutomationStepControl(s), HDRAutomationStep s => new HDRAutomationStepControl(s), HybridModeAutomationStep s => await HybridModeAutomationStepControlFactory.GetControlAsync(s), InstantBootAutomationStep s => new InstantBootAutomationStepControl(s), MacroAutomationStep s => new MacroAutomationStepControl(s), MicrophoneAutomationStep s => new MicrophoneAutomationStepControl(s), NotificationAutomationStep s => new NotificationAutomationStepControl(s), OneLevelWhiteKeyboardBacklightAutomationStep s => new OneLevelWhiteKeyboardBacklightAutomationStepControl(s), OverDriveAutomationStep s => new OverDriveAutomationStepControl(s), OverclockDiscreteGPUAutomationStep s => new OverclockDiscreteGPUAutomationStepControl(s), PanelLogoBacklightAutomationStep s => new PanelLogoBacklightAutomationStepControl(s), PlaySoundAutomationStep s => new PlaySoundAutomationStepControl(s), PortsBacklightAutomationStep s => new PortsBacklightAutomationStepControl(s), PowerModeAutomationStep s => new PowerModeAutomationStepControl(s), QuickActionAutomationStep s => new QuickActionAutomationStepControl(s), RefreshRateAutomationStep s => new RefreshRateAutomationStepControl(s), ResolutionAutomationStep s => new ResolutionAutomationStepControl(s), RGBKeyboardBacklightAutomationStep s => new RGBKeyboardBacklightAutomationStepControl(s), RunAutomationStep s => new RunAutomationStepControl(s), SpeakerAutomationStep s => new SpeakerAutomationStepControl(s), SpectrumKeyboardBacklightBrightnessAutomationStep s => new SpectrumKeyboardBacklightBrightnessAutomationStepControl(s), SpectrumKeyboardBacklightImportProfileAutomationStep s => new SpectrumKeyboardBacklightImportProfileAutomationStepControl(s), SpectrumKeyboardBacklightProfileAutomationStep s => new SpectrumKeyboardBacklightProfileAutomationStepControl(s), TurnOffMonitorsAutomationStep s => new TurnOffMonitorsAutomationStepControl(s), TurnOffWiFiAutomationStep s => new TurnOffWiFiAutomationStepControl(s), TurnOnWiFiAutomationStep s => new TurnOnWiFiAutomationStepControl(s), TouchpadLockAutomationStep s => new TouchpadLockAutomationStepControl(s), WhiteKeyboardBacklightAutomationStep s => new WhiteKeyboardBacklightAutomationStepControl(s), WinKeyAutomationStep s => new WinKeyAutomationStepControl(s), _ => throw new InvalidOperationException("Unknown step type"), }; control.MouseRightButtonUp += (_, e) => { ShowContextMenu(control); e.Handled = true; }; control.Changed += (_, _) => { OnChanged?.Invoke(this, EventArgs.Empty); }; control.Delete += (s, _) => { if (s is AbstractAutomationStepControl step) DeleteStep(step); }; return control; } private void ShowContextMenu(FrameworkElement control) { var menuItems = new List(); var index = _stepsStackPanel.Children.IndexOf(control); var maxIndex = _stepsStackPanel.Children.Count - 1; var moveUpMenuItem = new MenuItem { SymbolIcon = SymbolRegular.ArrowUp24, Header = Resource.MoveUp }; if (index > 0) moveUpMenuItem.Click += (_, _) => MoveStep(control, index - 1); else moveUpMenuItem.IsEnabled = false; menuItems.Add(moveUpMenuItem); var moveDownMenuItem = new MenuItem { SymbolIcon = SymbolRegular.ArrowDown24, Header = Resource.MoveDown }; if (index < maxIndex) moveDownMenuItem.Click += (_, _) => MoveStep(control, index + 1); else moveDownMenuItem.IsEnabled = false; menuItems.Add(moveDownMenuItem); control.ContextMenu = new(); control.ContextMenu.Items.AddRange(menuItems); control.ContextMenu.IsOpen = true; } private void MoveStep(UIElement control, int index) { _stepsStackPanel.Children.Remove(control); _stepsStackPanel.Children.Insert(index, control); OnChanged?.Invoke(this, EventArgs.Empty); } private void AddStep(AbstractAutomationStepControl control) { _stepsStackPanel.Children.Add(control); _cardHeaderControl.Subtitle = GenerateSubtitle(); _cardHeaderControl.SubtitleToolTip = _cardHeaderControl.Subtitle; control.Focus(); OnChanged?.Invoke(this, EventArgs.Empty); } private void DeleteStep(UIElement control) { _stepsStackPanel.Children.Remove(control); _cardHeaderControl.Subtitle = GenerateSubtitle(); _cardHeaderControl.SubtitleToolTip = _cardHeaderControl.Subtitle; OnChanged?.Invoke(this, EventArgs.Empty); } } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/AlwaysOnUsbAutomationStepControl.cs ================================================ using LenovoLegionToolkit.Lib; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.WPF.Resources; using Wpf.Ui.Common; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public class AlwaysOnUsbAutomationStepControl : AbstractComboBoxAutomationStepCardControl { public AlwaysOnUsbAutomationStepControl(IAutomationStep step) : base(step) { Icon = SymbolRegular.UsbStick24; Title = Resource.AlwaysOnUsbAutomationStepControl_Title; Subtitle = Resource.AlwaysOnUsbAutomationStepControl_Message; } } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/BatteryAutomationStepControl.cs ================================================ using LenovoLegionToolkit.Lib; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.WPF.Resources; using Wpf.Ui.Common; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public class BatteryAutomationStepControl : AbstractComboBoxAutomationStepCardControl { public BatteryAutomationStepControl(IAutomationStep step) : base(step) { Icon = SymbolRegular.BatteryCharge24; Title = Resource.BatteryAutomationStepControl_Title; Subtitle = Resource.BatteryAutomationStepControl_Message; } } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/BatteryNightChargeAutomationStepControl.cs ================================================ using LenovoLegionToolkit.Lib; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.WPF.Resources; using Wpf.Ui.Common; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public class BatteryNightChargeAutomationStepControl : AbstractComboBoxAutomationStepCardControl { public BatteryNightChargeAutomationStepControl(IAutomationStep step) : base(step) { Icon = SymbolRegular.WeatherMoon24; Title = Resource.BatteryNightChargeAutomationStepControl_Title; Subtitle = Resource.BatteryNightChargeAutomationStepControl_Message; } } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/DeactivateGPUAutomationStepControl.cs ================================================ using LenovoLegionToolkit.Lib.Automation; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.WPF.Resources; using Wpf.Ui.Common; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public class DeactivateGPUAutomationStepControl : AbstractComboBoxAutomationStepCardControl { public DeactivateGPUAutomationStepControl(DeactivateGPUAutomationStep step) : base(step) { Icon = SymbolRegular.DeveloperBoard24; Title = Resource.DeactivateGPUAutomationStepControl_Title; Subtitle = Resource.DeactivateGPUAutomationStepControl_Message; } } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/DelayAutomationStepControl.cs ================================================ using LenovoLegionToolkit.Lib.Automation; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.WPF.Resources; using Wpf.Ui.Common; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public class DelayAutomationStepControl : AbstractComboBoxAutomationStepCardControl { public DelayAutomationStepControl(IAutomationStep step) : base(step) { Icon = SymbolRegular.Clock24; Title = Resource.DelayAutomationStepControl_Title; Subtitle = Resource.DelayAutomationStepControl_Message; } } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/DisplayBrightnessAutomationStepControl.cs ================================================ using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.WPF.Resources; using Wpf.Ui.Common; using NumberBox = Wpf.Ui.Controls.NumberBox; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public class DisplayBrightnessAutomationStepControl : AbstractAutomationStepControl { private readonly NumberBox _brightness = new() { Width = 150, ClearButtonEnabled = false, MaxDecimalPlaces = 0, Minimum = 0, Maximum = 100, SmallChange = 5, LargeChange = 5 }; private readonly Grid _grid = new(); public DisplayBrightnessAutomationStepControl(DisplayBrightnessAutomationStep step) : base(step) { Icon = SymbolRegular.BrightnessHigh48; Title = Resource.DisplayBrightnessAutomationStepControl_Title; Subtitle = Resource.DisplayBrightnessAutomationStepControl_Message; } public override IAutomationStep CreateAutomationStep() => new DisplayBrightnessAutomationStep((int?)_brightness.Value ?? 0); protected override UIElement GetCustomControl() { _brightness.TextChanged += (_, _) => { if ((int?)_brightness.Value != AutomationStep.Brightness) RaiseChanged(); }; _grid.Children.Add(_brightness); return _grid; } protected override void OnFinishedLoading() { } protected override Task RefreshAsync() { _brightness.Value = AutomationStep.Brightness; return Task.CompletedTask; } } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/DpiScaleAutomationStepControl.cs ================================================ using System; using LenovoLegionToolkit.Lib; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.Lib.Listeners; using LenovoLegionToolkit.WPF.Resources; using LenovoLegionToolkit.WPF.Utils; using Wpf.Ui.Common; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public class DpiScaleAutomationStepControl : AbstractComboBoxAutomationStepCardControl { private readonly DisplayConfigurationListener _listener = IoCContainer.Resolve(); public DpiScaleAutomationStepControl(IAutomationStep step) : base(step) { Icon = SymbolRegular.TextFontSize24; Title = Resource.DpiScaleAutomationStepControl_Title; Subtitle = Resource.DpiScaleAutomationStepControl_Message; _listener.Changed += Listener_Changed; } protected override string ComboBoxItemDisplayName(DpiScale value) { var str = base.ComboBoxItemDisplayName(value); return LocalizationHelper.ForceLeftToRight(str); } private void Listener_Changed(object? sender, EventArgs e) => Dispatcher.Invoke(async () => { if (IsLoaded) await RefreshAsync(); }); } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/FlipToStartAutomationStepControl.cs ================================================ using LenovoLegionToolkit.Lib; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.WPF.Resources; using Wpf.Ui.Common; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public class FlipToStartAutomationStepControl : AbstractComboBoxAutomationStepCardControl { public FlipToStartAutomationStepControl(IAutomationStep step) : base(step) { Icon = SymbolRegular.Power24; Title = Resource.FlipToStartAutomationStepControl_Title; Subtitle = Resource.FlipToStartAutomationStepControl_Message; } } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/FnLockAutomationStepControl.cs ================================================ using LenovoLegionToolkit.Lib; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.WPF.Resources; using Wpf.Ui.Common; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public class FnLockAutomationStepControl : AbstractComboBoxAutomationStepCardControl { public FnLockAutomationStepControl(IAutomationStep step) : base(step) { Icon = SymbolRegular.Keyboard24; Title = Resource.FnLockAutomationStepControl_Title; Subtitle = Resource.FnLockAutomationStepControl_Message; } } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/GodModePresetAutomationStepControl.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using LenovoLegionToolkit.Lib; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.WPF.Extensions; using LenovoLegionToolkit.WPF.Resources; using Wpf.Ui.Common; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public class GodModePresetAutomationStepControl : AbstractAutomationStepControl { private readonly ComboBox _comboBox = new() { MinWidth = 150, Visibility = Visibility.Hidden, Margin = new(8, 0, 0, 0) }; public GodModePresetAutomationStepControl(GodModePresetAutomationStep step) : base(step) { Icon = SymbolRegular.Gauge24; Title = Resource.GodModePresetAutomationStepControl_Title; Subtitle = Resource.GodModePresetAutomationStepControl_Message; } public override IAutomationStep CreateAutomationStep() { var presetId = Guid.Empty; if (_comboBox.TryGetSelectedItem(out KeyValuePair value)) presetId = value.Key; return new GodModePresetAutomationStep(presetId); } protected override UIElement GetCustomControl() { _comboBox.SelectionChanged += ComboBox_SelectionChanged; return _comboBox; } private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) => RaiseChanged(); protected override async Task RefreshAsync() { var state = await AutomationStep.GetStateAsync(); var presets = state.Presets; var selectedPreset = presets.FirstOrDefault(kv => kv.Key == AutomationStep.PresetId); _comboBox.SetItems(presets, selectedPreset, kv => kv.Value.Name); _comboBox.IsEnabled = presets.Count != 0; } protected override void OnFinishedLoading() => _comboBox.Visibility = Visibility.Visible; } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/HDRAutomationStepControl.cs ================================================ using System; using LenovoLegionToolkit.Lib; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.Lib.Listeners; using LenovoLegionToolkit.WPF.Resources; using Wpf.Ui.Common; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public class HDRAutomationStepControl : AbstractComboBoxAutomationStepCardControl { private readonly DisplayConfigurationListener _listener = IoCContainer.Resolve(); public HDRAutomationStepControl(IAutomationStep step) : base(step) { Icon = SymbolRegular.Hdr24; Title = Resource.HDRAutomationStepControl_Title; Subtitle = Resource.HDRAutomationStepControl_Message; _listener.Changed += Listener_Changed; } private void Listener_Changed(object? sender, EventArgs e) => Dispatcher.Invoke(async () => { if (IsLoaded) await RefreshAsync(); }); } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/HybridModeAutomationStepControl.cs ================================================ using System.Threading.Tasks; using LenovoLegionToolkit.Lib; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.Lib.Utils; using LenovoLegionToolkit.WPF.Resources; using Wpf.Ui.Common; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public static class HybridModeAutomationStepControlFactory { public static async Task>> GetControlAsync(IAutomationStep step) { var mi = await Compatibility.GetMachineInformationAsync(); return mi.Properties.SupportsIGPUMode ? new ComboBoxHybridModeAutomationStepControl(step) : new ToggleHybridModeAutomationStepControl(step); } private class ComboBoxHybridModeAutomationStepControl : AbstractComboBoxAutomationStepCardControl { public ComboBoxHybridModeAutomationStepControl(IAutomationStep step) : base(step) { Icon = SymbolRegular.LeafOne24; Title = Resource.ComboBoxHybridModeAutomationStepControl_Title; Subtitle = Resource.ComboBoxHybridModeAutomationStepControl_Message; } } private class ToggleHybridModeAutomationStepControl : AbstractComboBoxAutomationStepCardControl { public ToggleHybridModeAutomationStepControl(IAutomationStep step) : base(step) { Icon = SymbolRegular.LeafOne24; Title = Resource.ToggleHybridModeAutomationStepControl_Title; Subtitle = Resource.ToggleHybridModeAutomationStepControl_Message; } } } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/InstantBootAutomationStepControl.cs ================================================ using LenovoLegionToolkit.Lib; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.WPF.Resources; using Wpf.Ui.Common; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; internal class InstantBootAutomationStepControl : AbstractComboBoxAutomationStepCardControl { public InstantBootAutomationStepControl(IAutomationStep step) : base(step) { Icon = SymbolRegular.PlugDisconnected24; Title = Resource.InstantBootAutomationStepControl_Title; Subtitle = Resource.InstantBootAutomationStepControl_Message; } } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/MacroAutomationStepControl.cs ================================================ using LenovoLegionToolkit.Lib.Automation; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.WPF.Resources; using Wpf.Ui.Common; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public class MacroAutomationStepControl : AbstractComboBoxAutomationStepCardControl { public MacroAutomationStepControl(IAutomationStep step) : base(step) { Icon = SymbolRegular.ReceiptPlay24; Title = Resource.MacroAutomationStepControl_Title; Subtitle = Resource.MacroAutomationStepControl_Message; } } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/MicrophoneAutomationStepControl.cs ================================================ using LenovoLegionToolkit.Lib; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.WPF.Resources; using Wpf.Ui.Common; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public class MicrophoneAutomationStepControl : AbstractComboBoxAutomationStepCardControl { public MicrophoneAutomationStepControl(IAutomationStep step) : base(step) { Icon = SymbolRegular.Mic24; Title = Resource.MicrophoneAutomationStepControl_Title; Subtitle = Resource.MicrophoneAutomationStepControl_Message; } } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/NotificationAutomationStepControl.cs ================================================ using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.WPF.Resources; using Wpf.Ui.Common; using TextBox = Wpf.Ui.Controls.TextBox; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public class NotificationAutomationStepControl : AbstractAutomationStepControl { private readonly TextBox _scriptPath = new() { PlaceholderText = Resource.NotificationAutomationStepControl_NotificationText, Width = 300 }; private readonly StackPanel _stackPanel = new(); public NotificationAutomationStepControl(NotificationAutomationStep step) : base(step) { Icon = SymbolRegular.Rocket24; Title = Resource.NotificationAutomationStepControl_Title; SizeChanged += RunAutomationStepControl_SizeChanged; } private void RunAutomationStepControl_SizeChanged(object sender, SizeChangedEventArgs e) { if (!e.WidthChanged) return; var newWidth = e.NewSize.Width / 3; _scriptPath.Width = newWidth; } public override IAutomationStep CreateAutomationStep() => new NotificationAutomationStep(_scriptPath.Text); protected override UIElement GetCustomControl() { _scriptPath.TextChanged += (_, _) => { if (_scriptPath.Text != AutomationStep.Text) RaiseChanged(); }; _stackPanel.Children.Add(_scriptPath); return _stackPanel; } protected override void OnFinishedLoading() { } protected override Task RefreshAsync() { _scriptPath.Text = AutomationStep.Text ?? string.Empty; return Task.CompletedTask; } } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/OneLevelWhiteKeyboardBacklightAutomationStepControl.cs ================================================ using LenovoLegionToolkit.Lib; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.WPF.Resources; using Wpf.Ui.Common; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public class OneLevelWhiteKeyboardBacklightAutomationStepControl : AbstractComboBoxAutomationStepCardControl { public OneLevelWhiteKeyboardBacklightAutomationStepControl(IAutomationStep step) : base(step) { Icon = SymbolRegular.Keyboard24; Title = Resource.OneLevelWhiteKeyboardBacklightAutomationStepControl_Title; Subtitle = Resource.OneLevelWhiteKeyboardBacklightAutomationStepControl_Message; } } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/OverDriveAutomationStepControl.cs ================================================ using LenovoLegionToolkit.Lib; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.WPF.Resources; using Wpf.Ui.Common; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public class OverDriveAutomationStepControl : AbstractComboBoxAutomationStepCardControl { public OverDriveAutomationStepControl(IAutomationStep step) : base(step) { Icon = SymbolRegular.TopSpeed24; Title = Resource.OverDriveAutomationStepControl_Title; Subtitle = Resource.OverDriveAutomationStepControl_Message; } } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/OverclockDiscreteGPUAutomationStepControl.cs ================================================ using LenovoLegionToolkit.Lib.Automation; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.WPF.Resources; using Wpf.Ui.Common; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public class OverclockDiscreteGPUAutomationStepControl : AbstractComboBoxAutomationStepCardControl { public OverclockDiscreteGPUAutomationStepControl(IAutomationStep step) : base(step) { Icon = SymbolRegular.DeveloperBoardLightning20; Title = Resource.OverclockDiscreteGPUAutomationStepControl_Title; Subtitle = Resource.OverclockDiscreteGPUAutomationStepControl_Message; } } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/PanelLogoBacklightAutomationStepControl.cs ================================================ using LenovoLegionToolkit.Lib; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.WPF.Resources; using Wpf.Ui.Common; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public class PanelLogoBacklightAutomationStepControl : AbstractComboBoxAutomationStepCardControl { public PanelLogoBacklightAutomationStepControl(IAutomationStep step) : base(step) { Icon = SymbolRegular.LightbulbCircle24; Title = Resource.PanelLogoBacklightAutomationStepControl_Title; Subtitle = Resource.PanelLogoBacklightAutomationStepControl_Message; } } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/PlaySoundAutomationStepControl.cs ================================================ using System.IO; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.WPF.Resources; using Microsoft.Win32; using Wpf.Ui.Common; using Button = Wpf.Ui.Controls.Button; using Orientation = System.Windows.Controls.Orientation; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public class PlaySoundAutomationStepControl : AbstractAutomationStepControl { private string? _path; private readonly TextBlock _titleTextBlock = new() { VerticalAlignment = VerticalAlignment.Center, }; private readonly Button _openButton = new() { Icon = SymbolRegular.Folder20, MinWidth = 34, Height = 34, Margin = new(8, 0, 0, 0) }; private readonly StackPanel _stackPanel = new() { Orientation = Orientation.Horizontal }; public PlaySoundAutomationStepControl(PlaySoundAutomationStep automationStep) : base(automationStep) { Icon = SymbolRegular.MusicNote2Play20; Title = Resource.PlaySoundAutomationStepControl_Title; Subtitle = Resource.PlaySoundAutomationStepControl_Message; } public override IAutomationStep CreateAutomationStep() { return new PlaySoundAutomationStep(_path); } protected override UIElement GetCustomControl() { _openButton.Click += (_, _) => { var ofd = new OpenFileDialog { Title = Resource.Import, InitialDirectory = @"C:\Windows\Media", CheckFileExists = true, }; var result = ofd.ShowDialog() ?? false; if (!result) return; _path = ofd.FileName; _titleTextBlock.Text = Path.GetFileName(_path); RaiseChanged(); }; _stackPanel.Children.Add(_titleTextBlock); _stackPanel.Children.Add(_openButton); return _stackPanel; } protected override void OnFinishedLoading() { } protected override Task RefreshAsync() { _path = AutomationStep.Path; _titleTextBlock.Text = Path.GetFileName(_path) ?? "-"; return Task.CompletedTask; } } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/PortsBacklightAutomationStepControl.cs ================================================ using LenovoLegionToolkit.Lib; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.WPF.Resources; using Wpf.Ui.Common; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public class PortsBacklightAutomationStepControl : AbstractComboBoxAutomationStepCardControl { public PortsBacklightAutomationStepControl(IAutomationStep step) : base(step) { Icon = SymbolRegular.UsbPlug24; Title = Resource.PortsBacklightAutomationStepControl_Title; Subtitle = Resource.PortsBacklightAutomationStepControl_Message; } } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/PowerModeAutomationStepControl.cs ================================================ using LenovoLegionToolkit.Lib; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.WPF.Resources; using Wpf.Ui.Common; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public class PowerModeAutomationStepControl : AbstractComboBoxAutomationStepCardControl { public PowerModeAutomationStepControl(IAutomationStep step) : base(step) { Icon = SymbolRegular.Gauge24; Title = Resource.PowerModeAutomationStepControl_Title; Subtitle = Resource.PowerModeAutomationStepControl_Message; } } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/QuickActionAutomationStepControl.cs ================================================ using System.Linq; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using LenovoLegionToolkit.Lib; using LenovoLegionToolkit.Lib.Automation; using LenovoLegionToolkit.Lib.Automation.Pipeline; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.WPF.Extensions; using LenovoLegionToolkit.WPF.Resources; using Wpf.Ui.Common; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public class QuickActionAutomationStepControl : AbstractAutomationStepControl { private readonly AutomationProcessor _processor = IoCContainer.Resolve(); private readonly ComboBox _comboBox = new() { MinWidth = 150 }; private readonly StackPanel _stackPanel = new(); public QuickActionAutomationStepControl(QuickActionAutomationStep step) : base(step) { Icon = SymbolRegular.Play24; Title = Resource.QuickActionAutomationStepControl_Title; Subtitle = Resource.QuickActionAutomationStepControl_Message; } public override IAutomationStep CreateAutomationStep() { return _comboBox.TryGetSelectedItem(out AutomationPipeline? pipeline) ? new QuickActionAutomationStep(pipeline?.Id) : new QuickActionAutomationStep(null); } protected override UIElement GetCustomControl() { _comboBox.SelectionChanged += (_, _) => { RaiseChanged(); }; _stackPanel.Children.Add(_comboBox); return _stackPanel; } protected override void OnFinishedLoading() { } protected override async Task RefreshAsync() { var pipelines = await _processor.GetPipelinesAsync(); var filteredPipelines = pipelines.Where(p => p.Trigger is null).ToArray(); var selectedPipeline = filteredPipelines.FirstOrDefault(p => p.Id == AutomationStep.PipelineId); _comboBox.SetItems(filteredPipelines, selectedPipeline, p => p?.Name ?? ""); } } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/RGBKeyboardBacklightAutomationStepControl.cs ================================================ using LenovoLegionToolkit.Lib; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.WPF.Resources; using Wpf.Ui.Common; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public class RGBKeyboardBacklightAutomationStepControl : AbstractComboBoxAutomationStepCardControl { public RGBKeyboardBacklightAutomationStepControl(IAutomationStep step) : base(step) { Icon = SymbolRegular.Keyboard24; Title = Resource.RGBKeyboardBacklightAutomationStepControl_Title; Subtitle = Resource.RGBKeyboardBacklightAutomationStepControl_Message; } } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/RefreshRateAutomationStepControl.cs ================================================ using System; using LenovoLegionToolkit.Lib; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.Lib.Listeners; using LenovoLegionToolkit.WPF.Resources; using LenovoLegionToolkit.WPF.Utils; using Wpf.Ui.Common; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public class RefreshRateAutomationStepControl : AbstractComboBoxAutomationStepCardControl { private readonly DisplayConfigurationListener _listener = IoCContainer.Resolve(); public RefreshRateAutomationStepControl(IAutomationStep step) : base(step) { Icon = SymbolRegular.DesktopPulse24; Title = Resource.RefreshRateAutomationStepControl_Title; Subtitle = Resource.RefreshRateAutomationStepControl_Message; _listener.Changed += Listener_Changed; } protected override string ComboBoxItemDisplayName(RefreshRate value) { var str = base.ComboBoxItemDisplayName(value); return LocalizationHelper.ForceLeftToRight(str); } private void Listener_Changed(object? sender, EventArgs e) => Dispatcher.Invoke(async () => { if (IsLoaded) await RefreshAsync(); }); } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/ResolutionAutomationStepControl.cs ================================================ using System; using LenovoLegionToolkit.Lib; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.Lib.Listeners; using LenovoLegionToolkit.WPF.Resources; using Wpf.Ui.Common; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public class ResolutionAutomationStepControl : AbstractComboBoxAutomationStepCardControl { private readonly DisplayConfigurationListener _listener = IoCContainer.Resolve(); public ResolutionAutomationStepControl(IAutomationStep step) : base(step) { Icon = SymbolRegular.ScaleFill24; Title = Resource.ResolutionAutomationStepControl_Title; Subtitle = Resource.ResolutionAutomationStepControl_Message; _listener.Changed += Listener_Changed; } private void Listener_Changed(object? sender, EventArgs e) => Dispatcher.Invoke(async () => { if (IsLoaded) await RefreshAsync(); }); } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/RunAutomationStepControl.cs ================================================ using System.Threading.Tasks; using System.Windows; using System.Windows.Automation; using System.Windows.Controls; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.WPF.Resources; using Wpf.Ui.Common; using TextBox = Wpf.Ui.Controls.TextBox; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public class RunAutomationStepControl : AbstractAutomationStepControl { private readonly TextBox _scriptPath = new() { PlaceholderText = Resource.RunAutomationStepControl_ExePath, Width = 300, Margin = new(0, 0, 0, 8), }; private readonly TextBox _scriptArguments = new() { PlaceholderText = Resource.RunAutomationStepControl_ExeArguments, Width = 300, Margin = new(0, 0, 0, 8), }; private readonly CheckBox _checkBoxProcessRunSilently = new() { Content = Resource.RunAutomationStepControl_ProcessRunSilently, ToolTip = Resource.RunAutomationStepControl_ProcessRunSilently_Description }; private readonly CheckBox _checkBoxProcessWaitUntilFinished = new() { Content = Resource.RunAutomationStepControl_ProcessWaitUntilFinished, ToolTip = Resource.RunAutomationStepControl_ProcessWaitUntilFinished_Description }; private readonly StackPanel _stackPanel = new(); public RunAutomationStepControl(RunAutomationStep step) : base(step) { Icon = SymbolRegular.WindowConsole20; Title = Resource.RunAutomationStepControl_Title; Subtitle = Resource.RunAutomationStepControl_Message; TitleVerticalAlignment = VerticalAlignment.Bottom; SubtitleVerticalAlignment = VerticalAlignment.Top; AutomationProperties.SetName(_scriptPath, Resource.RunAutomationStepControl_ExePath); AutomationProperties.SetName(_scriptArguments, Resource.RunAutomationStepControl_ExeArguments); AutomationProperties.SetName(_checkBoxProcessRunSilently, Resource.RunAutomationStepControl_ProcessRunSilently); AutomationProperties.SetName(_checkBoxProcessWaitUntilFinished, Resource.RunAutomationStepControl_ProcessWaitUntilFinished); SizeChanged += RunAutomationStepControl_SizeChanged; } private void RunAutomationStepControl_SizeChanged(object sender, SizeChangedEventArgs e) { if (!e.WidthChanged) return; var newWidth = e.NewSize.Width / 3; _scriptPath.Width = newWidth; _scriptArguments.Width = newWidth; } public override IAutomationStep CreateAutomationStep() { return new RunAutomationStep(_scriptPath.Text, _scriptArguments.Text, _checkBoxProcessRunSilently.IsChecked ?? true, _checkBoxProcessWaitUntilFinished.IsChecked ?? true); } protected override UIElement GetCustomControl() { _scriptPath.TextChanged += (_, _) => { if (_scriptPath.Text != AutomationStep.ScriptPath) RaiseChanged(); }; _scriptArguments.TextChanged += (_, _) => { if (_scriptArguments.Text != AutomationStep.ScriptArguments) RaiseChanged(); }; _checkBoxProcessRunSilently.Checked += (_, _) => { if (_checkBoxProcessRunSilently.IsChecked != AutomationStep.RunSilently) RaiseChanged(); }; _checkBoxProcessWaitUntilFinished.Checked += (_, _) => { if (_checkBoxProcessWaitUntilFinished.IsChecked != AutomationStep.WaitUntilFinished) RaiseChanged(); }; _checkBoxProcessRunSilently.Unchecked += (_, _) => { if (_checkBoxProcessRunSilently.IsChecked != AutomationStep.RunSilently) RaiseChanged(); }; _checkBoxProcessWaitUntilFinished.Unchecked += (_, _) => { if (_checkBoxProcessWaitUntilFinished.IsChecked != AutomationStep.WaitUntilFinished) RaiseChanged(); }; _stackPanel.Children.Add(_scriptPath); _stackPanel.Children.Add(_scriptArguments); _stackPanel.Children.Add(_checkBoxProcessRunSilently); _stackPanel.Children.Add(_checkBoxProcessWaitUntilFinished); return _stackPanel; } protected override void OnFinishedLoading() { } protected override Task RefreshAsync() { _scriptPath.Text = AutomationStep.ScriptPath ?? string.Empty; _scriptArguments.Text = AutomationStep.ScriptArguments ?? string.Empty; _checkBoxProcessRunSilently.IsChecked = AutomationStep.RunSilently; _checkBoxProcessWaitUntilFinished.IsChecked = AutomationStep.WaitUntilFinished; return Task.CompletedTask; } } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/SpeakerAutomationStepControl.cs ================================================ using LenovoLegionToolkit.Lib; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.WPF.Resources; using Wpf.Ui.Common; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public class SpeakerAutomationStepControl : AbstractComboBoxAutomationStepCardControl { public SpeakerAutomationStepControl(IAutomationStep step) : base(step) { Icon = SymbolRegular.Speaker224; Title = Resource.SpeakerAutomationStepControl_Title; Subtitle = Resource.SpeakerAutomationStepControl_Message; } } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/SpectrumKeyboardBacklightBrightnessAutomationStepControl.cs ================================================ using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.WPF.Resources; using Wpf.Ui.Common; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public class SpectrumKeyboardBacklightBrightnessAutomationStepControl : AbstractComboBoxAutomationStepCardControl { public SpectrumKeyboardBacklightBrightnessAutomationStepControl(IAutomationStep step) : base(step) { Icon = SymbolRegular.BrightnessHigh24; Title = Resource.SpectrumKeyboardBacklightBrightnessAutomationStepControl_Title; Subtitle = Resource.SpectrumKeyboardBacklightBrightnessAutomationStepControl_Message; } protected override string ComboBoxItemDisplayName(int value) { return value == 0 ? Resource.SpectrumKeyboardBacklightBrightnessAutomationStepControl_Off : base.ComboBoxItemDisplayName(value); } } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/SpectrumKeyboardBacklightImportProfileAutomationStepControl.cs ================================================ using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.WPF.Resources; using Microsoft.Win32; using Wpf.Ui.Common; using Button = Wpf.Ui.Controls.Button; using TextBox = Wpf.Ui.Controls.TextBox; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public class SpectrumKeyboardBacklightImportProfileAutomationStepControl : AbstractAutomationStepControl { private readonly TextBox _path = new() { PlaceholderText = Resource.SpectrumKeyboardBacklightImportProfileAutomationStepControl_Path, Width = 300 }; private readonly Button _openButton = new() { Icon = SymbolRegular.MoreHorizontal24, MinWidth = 34, Height = 34, Margin = new(8, 0, 0, 0) }; private readonly StackPanel _stackPanel = new() { Orientation = Orientation.Horizontal }; public SpectrumKeyboardBacklightImportProfileAutomationStepControl(SpectrumKeyboardBacklightImportProfileAutomationStep step) : base(step) { Icon = SymbolRegular.BrightnessHigh24; Title = Resource.SpectrumKeyboardBacklightImportProfileAutomationStepControl_Title; Subtitle = Resource.SpectrumKeyboardBacklightImportProfileAutomationStepControl_Message; SizeChanged += RunAutomationStepControl_SizeChanged; } private void RunAutomationStepControl_SizeChanged(object sender, SizeChangedEventArgs e) { if (!e.WidthChanged) return; var newWidth = e.NewSize.Width / 3; _path.Width = newWidth; } public override IAutomationStep CreateAutomationStep() => new SpectrumKeyboardBacklightImportProfileAutomationStep(_path.Text); protected override UIElement GetCustomControl() { _path.TextChanged += (_, _) => { if (_path.Text != AutomationStep.Path) RaiseChanged(); }; _openButton.Click += (_, _) => { var ofd = new OpenFileDialog { Title = Resource.Import, InitialDirectory = "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}", Filter = "Json Files (.json)|*.json", CheckFileExists = true, }; var result = ofd.ShowDialog() ?? false; if (!result) return; _path.Text = ofd.FileName; }; _stackPanel.Children.Add(_path); _stackPanel.Children.Add(_openButton); return _stackPanel; } protected override void OnFinishedLoading() { } protected override Task RefreshAsync() { _path.Text = AutomationStep.Path ?? string.Empty; return Task.CompletedTask; } } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/SpectrumKeyboardBacklightProfileAutomationStepControl.cs ================================================ using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.WPF.Resources; using Wpf.Ui.Common; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public class SpectrumKeyboardBacklightProfileAutomationStepControl : AbstractComboBoxAutomationStepCardControl { public SpectrumKeyboardBacklightProfileAutomationStepControl(IAutomationStep step) : base(step) { Icon = SymbolRegular.BrightnessHigh24; Title = Resource.SpectrumKeyboardBacklightProfileAutomationStepControl_Title; Subtitle = Resource.SpectrumKeyboardBacklightProfileAutomationStepControl_Message; } } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/TouchpadLockAutomationStepControl.cs ================================================ using LenovoLegionToolkit.Lib; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.WPF.Resources; using Wpf.Ui.Common; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public class TouchpadLockAutomationStepControl : AbstractComboBoxAutomationStepCardControl { public TouchpadLockAutomationStepControl(IAutomationStep step) : base(step) { Icon = SymbolRegular.Tablet24; Title = Resource.TouchpadLockAutomationStepControl_Title; Subtitle = Resource.TouchpadLockAutomationStepControl_Message; } } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/TurnOffMonitorsAutomationStepControl.cs ================================================ using System.Threading.Tasks; using System.Windows; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.WPF.Resources; using Wpf.Ui.Common; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public class TurnOffMonitorsAutomationStepControl : AbstractAutomationStepControl { public TurnOffMonitorsAutomationStepControl(TurnOffMonitorsAutomationStep automationStep) : base(automationStep) { Icon = SymbolRegular.Desktop24; Title = Resource.TurnOffMonitorsAutomationStepControl_Title; Subtitle = Resource.TurnOffMonitorsAutomationStepControl_Message; } public override IAutomationStep CreateAutomationStep() => new TurnOffMonitorsAutomationStep(); protected override UIElement? GetCustomControl() => null; protected override void OnFinishedLoading() { } protected override Task RefreshAsync() => Task.CompletedTask; } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/TurnOffWiFiAutomationStepControl.cs ================================================ using System.Threading.Tasks; using System.Windows; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.WPF.Resources; using Wpf.Ui.Common; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public class TurnOffWiFiAutomationStepControl : AbstractAutomationStepControl { public TurnOffWiFiAutomationStepControl(TurnOffWiFiAutomationStep automationStep) : base(automationStep) { Icon = SymbolRegular.WifiOff24; Title = Resource.TurnOffWiFiAutomationStepControl_Title; } public override IAutomationStep CreateAutomationStep() => new TurnOffWiFiAutomationStep(); protected override UIElement? GetCustomControl() => null; protected override void OnFinishedLoading() { } protected override Task RefreshAsync() => Task.CompletedTask; } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/TurnOnWiFiAutomationStepControl.cs ================================================ using System.Threading.Tasks; using System.Windows; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.WPF.Resources; using Wpf.Ui.Common; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public class TurnOnWiFiAutomationStepControl : AbstractAutomationStepControl { public TurnOnWiFiAutomationStepControl(TurnOnWiFiAutomationStep automationStep) : base(automationStep) { Icon = SymbolRegular.Wifi124; Title = Resource.TurnOnWiFiAutomationStepControl_Title; } public override IAutomationStep CreateAutomationStep() => new TurnOnWiFiAutomationStep(); protected override UIElement? GetCustomControl() => null; protected override void OnFinishedLoading() { } protected override Task RefreshAsync() => Task.CompletedTask; } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/WhiteKeyboardBacklightAutomationStepControl.cs ================================================ using LenovoLegionToolkit.Lib; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.WPF.Resources; using Wpf.Ui.Common; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public class WhiteKeyboardBacklightAutomationStepControl : AbstractComboBoxAutomationStepCardControl { public WhiteKeyboardBacklightAutomationStepControl(IAutomationStep step) : base(step) { Icon = SymbolRegular.Keyboard24; Title = Resource.WhiteKeyboardBacklightAutomationStepControl_Title; Subtitle = Resource.WhiteKeyboardBacklightAutomationStepControl_Message; } } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/Automation/Steps/WinKeyAutomationStepControl.cs ================================================ using LenovoLegionToolkit.Lib; using LenovoLegionToolkit.Lib.Automation.Steps; using LenovoLegionToolkit.WPF.Resources; using Wpf.Ui.Common; namespace LenovoLegionToolkit.WPF.Controls.Automation.Steps; public class WinKeyAutomationStepControl : AbstractComboBoxAutomationStepCardControl { public WinKeyAutomationStepControl(IAutomationStep step) : base(step) { Icon = SymbolRegular.Keyboard24; Title = Resource.WinKeyAutomationStepControl_Title; Subtitle = Resource.WinKeyAutomationStepControl_Message; } } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/CardHeaderControl.cs ================================================ using System; using System.Windows; using System.Windows.Automation; using System.Windows.Automation.Peers; using System.Windows.Controls; namespace LenovoLegionToolkit.WPF.Controls; public class CardHeaderControl : UserControl { private readonly TextBlock _titleTextBlock = new() { FontSize = 14, FontWeight = FontWeights.Medium, VerticalAlignment = VerticalAlignment.Center, TextTrimming = TextTrimming.CharacterEllipsis, }; private readonly TextBlock _subtitleTextBlock = new() { FontSize = 12, Margin = new(0, 4, 0, 0), TextWrapping = TextWrapping.Wrap, TextTrimming = TextTrimming.CharacterEllipsis, }; private readonly TextBlock _warningTextBlock = new() { FontSize = 12, Margin = new(0, 4, 0, 0), TextWrapping = TextWrapping.Wrap, TextTrimming = TextTrimming.CharacterEllipsis, }; private readonly StackPanel _stackPanel = new(); private readonly Grid _grid = new() { ColumnDefinitions = { new ColumnDefinition { Width = new(1, GridUnitType.Star) }, new ColumnDefinition { Width = GridLength.Auto }, }, RowDefinitions = { new RowDefinition { Height = GridLength.Auto }, new RowDefinition { Height = GridLength.Auto }, }, }; private UIElement? _accessory; public string Title { get => _titleTextBlock.Text; set { _titleTextBlock.Text = value; RefreshLayout(); } } public string Subtitle { get => _subtitleTextBlock.Text; set { _subtitleTextBlock.Text = value; RefreshLayout(); } } public VerticalAlignment TitleVerticalAlignment { get => _titleTextBlock.VerticalAlignment; set => _titleTextBlock.VerticalAlignment = value; } public VerticalAlignment SubtitleVerticalAlignment { get => _subtitleTextBlock.VerticalAlignment; set => _subtitleTextBlock.VerticalAlignment = value; } public string Warning { get => _warningTextBlock.Text; set { _warningTextBlock.Text = value; RefreshLayout(); } } public string? SubtitleToolTip { get => _subtitleTextBlock.ToolTip as string; set { _subtitleTextBlock.ToolTip = value; ToolTipService.SetIsEnabled(_subtitleTextBlock, value is not null); RefreshLayout(); } } public UIElement? Accessory { get => _accessory; set { if (_accessory is not null) _grid.Children.Remove(_accessory); _accessory = value; if (_accessory is not null) { Grid.SetColumn(_accessory, 1); Grid.SetRow(_accessory, 0); Grid.SetRowSpan(_accessory, 2); _grid.Children.Add(_accessory); } RefreshLayout(); } } protected override void OnInitialized(EventArgs e) { base.OnInitialized(e); Grid.SetColumn(_titleTextBlock, 0); Grid.SetColumn(_stackPanel, 0); Grid.SetRow(_titleTextBlock, 0); Grid.SetRow(_stackPanel, 1); _stackPanel.Children.Add(_subtitleTextBlock); _stackPanel.Children.Add(_warningTextBlock); _grid.Children.Add(_titleTextBlock); _grid.Children.Add(_stackPanel); Content = _grid; UpdateTextStyle(); IsEnabledChanged += (_, _) => UpdateTextStyle(); } protected override AutomationPeer OnCreateAutomationPeer() => new CardHeaderControlAutomationPeer(this); private void RefreshLayout() { if (string.IsNullOrWhiteSpace(Subtitle) && string.IsNullOrWhiteSpace(Warning)) Grid.SetRowSpan(_titleTextBlock, 2); else Grid.SetRowSpan(_titleTextBlock, 1); _subtitleTextBlock.Visibility = string.IsNullOrWhiteSpace(Subtitle) ? Visibility.Collapsed : Visibility.Visible; _warningTextBlock.Visibility = string.IsNullOrWhiteSpace(Warning) ? Visibility.Collapsed : Visibility.Visible; } private void UpdateTextStyle() { if (IsEnabled) { _titleTextBlock.SetResourceReference(ForegroundProperty, "TextFillColorPrimaryBrush"); _subtitleTextBlock.SetResourceReference(ForegroundProperty, "TextFillColorSecondaryBrush"); _warningTextBlock.SetResourceReference(ForegroundProperty, "SystemFillColorCautionBrush"); } else { _titleTextBlock.SetResourceReference(ForegroundProperty, "TextFillColorDisabledBrush"); _subtitleTextBlock.SetResourceReference(ForegroundProperty, "TextFillColorDisabledBrush"); _warningTextBlock.SetResourceReference(ForegroundProperty, "TextFillColorDisabledBrush"); } } private class CardHeaderControlAutomationPeer(CardHeaderControl owner) : FrameworkElementAutomationPeer(owner) { protected override string GetClassNameCore() => nameof(CardHeaderControl); protected override AutomationControlType GetAutomationControlTypeCore() => AutomationControlType.Pane; public override object? GetPattern(PatternInterface patternInterface) { if (patternInterface == PatternInterface.ItemContainer) return this; return base.GetPattern(patternInterface); } protected override string GetNameCore() { var result = base.GetNameCore() ?? string.Empty; if (result == string.Empty) result = AutomationProperties.GetName(owner); if (result == string.Empty && !string.IsNullOrWhiteSpace(owner._titleTextBlock.Text)) { result = owner._titleTextBlock.Text; if (!string.IsNullOrWhiteSpace(owner._subtitleTextBlock.Text)) result += $", {owner._subtitleTextBlock.Text}"; } return result; } } } ================================================ FILE: LenovoLegionToolkit.WPF/Controls/ColorPickerControl.xaml ================================================