LenovoLegionToolkit
530 строк · 18.2 Кб
1#if !DEBUG2using LenovoLegionToolkit.Lib.System;3#endif4using System;5using System.Diagnostics;6using System.Linq;7using System.Reflection;8using System.Threading;9using System.Threading.Tasks;10using System.Windows;11using System.Windows.Interop;12using System.Windows.Media;13using System.Windows.Threading;14using LenovoLegionToolkit.Lib;15using LenovoLegionToolkit.Lib.Automation;16using LenovoLegionToolkit.Lib.Controllers;17using LenovoLegionToolkit.Lib.Extensions;18using LenovoLegionToolkit.Lib.Features;19using LenovoLegionToolkit.Lib.Features.Hybrid;20using LenovoLegionToolkit.Lib.Features.Hybrid.Notify;21using LenovoLegionToolkit.Lib.Features.PanelLogo;22using LenovoLegionToolkit.Lib.Features.WhiteKeyboardBacklight;23using LenovoLegionToolkit.Lib.Listeners;24using LenovoLegionToolkit.Lib.SoftwareDisabler;25using LenovoLegionToolkit.Lib.Utils;26using LenovoLegionToolkit.WPF.Extensions;27using LenovoLegionToolkit.WPF.Pages;28using LenovoLegionToolkit.WPF.Resources;29using LenovoLegionToolkit.WPF.Utils;30using LenovoLegionToolkit.WPF.Windows;31using LenovoLegionToolkit.WPF.Windows.Utils;32using WinFormsApp = System.Windows.Forms.Application;33using WinFormsHighDpiMode = System.Windows.Forms.HighDpiMode;34
35namespace LenovoLegionToolkit.WPF;36
37public partial class App38{
39private const string MUTEX_NAME = "LenovoLegionToolkit_Mutex_6efcc882-924c-4cbc-8fec-f45c25696f98";40private const string EVENT_NAME = "LenovoLegionToolkit_Event_6efcc882-924c-4cbc-8fec-f45c25696f98";41
42private Mutex? _singleInstanceMutex;43private EventWaitHandle? _singleInstanceWaitHandle;44
45public new static App Current => (App)Application.Current;46
47private async void Application_Startup(object sender, StartupEventArgs e)48{49#if DEBUG50if (Debugger.IsAttached)51{52Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName)53.Where(p => p.Id != Environment.ProcessId)54.ForEach(p =>55{56p.Kill();57p.WaitForExit();58});59}60#endif61
62var flags = new Flags(e.Args);63
64Log.Instance.IsTraceEnabled = flags.IsTraceEnabled;65
66AppDomain.CurrentDomain.UnhandledException += AppDomain_UnhandledException;67
68if (Log.Instance.IsTraceEnabled)69Log.Instance.Trace($"Flags: {flags}");70
71EnsureSingleInstance();72
73await LocalizationHelper.SetLanguageAsync(true);74
75if (!flags.SkipCompatibilityCheck)76{77await CheckBasicCompatibilityAsync();78await CheckCompatibilityAsync();79}80
81if (Log.Instance.IsTraceEnabled)82Log.Instance.Trace($"Starting... [version={Assembly.GetEntryAssembly()?.GetName().Version}, build={Assembly.GetEntryAssembly()?.GetBuildDateTimeString()}, os={Environment.OSVersion}, dotnet={Environment.Version}]");83
84WinFormsApp.SetHighDpiMode(WinFormsHighDpiMode.PerMonitorV2);85RenderOptions.ProcessRenderMode = RenderMode.SoftwareOnly;86
87IoCContainer.Initialize(88new Lib.IoCModule(),89new Lib.Automation.IoCModule(),90new IoCModule()91);92
93IoCContainer.Resolve<HttpClientFactory>().SetProxy(flags.ProxyUrl, flags.ProxyUsername, flags.ProxyPassword, flags.ProxyAllowAllCerts);94
95IoCContainer.Resolve<PowerModeFeature>().AllowAllPowerModesOnBattery = flags.AllowAllPowerModesOnBattery;96IoCContainer.Resolve<RGBKeyboardBacklightController>().ForceDisable = flags.ForceDisableRgbKeyboardSupport;97IoCContainer.Resolve<SpectrumKeyboardBacklightController>().ForceDisable = flags.ForceDisableSpectrumKeyboardSupport;98IoCContainer.Resolve<WhiteKeyboardLenovoLightingBacklightFeature>().ForceDisable = flags.ForceDisableLenovoLighting;99IoCContainer.Resolve<PanelLogoLenovoLightingBacklightFeature>().ForceDisable = flags.ForceDisableLenovoLighting;100IoCContainer.Resolve<PortsBacklightFeature>().ForceDisable = flags.ForceDisableLenovoLighting;101IoCContainer.Resolve<IGPUModeFeature>().ExperimentalGPUWorkingMode = flags.ExperimentalGPUWorkingMode;102IoCContainer.Resolve<DGPUNotify>().ExperimentalGPUWorkingMode = flags.ExperimentalGPUWorkingMode;103
104AutomationPage.EnableHybridModeAutomation = flags.EnableHybridModeAutomation;105
106await LogSoftwareStatusAsync();107await InitPowerModeFeatureAsync();108await InitBatteryFeatureAsync();109await InitRgbKeyboardControllerAsync();110await InitSpectrumKeyboardControllerAsync();111await InitGpuOverclockControllerAsync();112await InitAutomationProcessorAsync();113await InitHybridModeAsync();114
115await IoCContainer.Resolve<AIController>().StartIfNeededAsync();116
117#if !DEBUG118Autorun.Validate();119#endif120
121var mainWindow = new MainWindow122{123WindowStartupLocation = WindowStartupLocation.CenterScreen,124TrayTooltipEnabled = !flags.DisableTrayTooltip125};126MainWindow = mainWindow;127
128IoCContainer.Resolve<ThemeManager>().Apply();129
130if (flags.Minimized)131{132if (Log.Instance.IsTraceEnabled)133Log.Instance.Trace($"Sending MainWindow to tray...");134
135mainWindow.WindowState = WindowState.Minimized;136mainWindow.Show();137mainWindow.SendToTray();138}139else140{141if (Log.Instance.IsTraceEnabled)142Log.Instance.Trace($"Showing MainWindow...");143
144mainWindow.Show();145}146
147if (Log.Instance.IsTraceEnabled)148Log.Instance.Trace($"Start up complete");149}150
151private void Application_Exit(object sender, ExitEventArgs e)152{153_singleInstanceMutex?.Close();154}155
156public void RestartMainWindow()157{158if (MainWindow is MainWindow mw)159{160mw.SuppressClosingEventHandler = true;161mw.Close();162}163
164var mainWindow = new MainWindow165{166WindowStartupLocation = WindowStartupLocation.CenterScreen167};168MainWindow = mainWindow;169mainWindow.Show();170}171
172public async Task ShutdownAsync()173{174try175{176if (IoCContainer.TryResolve<AIController>() is { } aiController)177await aiController.StopAsync();178}179catch { /* Ignored. */ }180
181try182{183if (IoCContainer.TryResolve<RGBKeyboardBacklightController>() is { } rgbKeyboardBacklightController)184{185if (await rgbKeyboardBacklightController.IsSupportedAsync())186await rgbKeyboardBacklightController.SetLightControlOwnerAsync(false);187}188}189catch { /* Ignored. */ }190
191try192{193if (IoCContainer.TryResolve<SpectrumKeyboardBacklightController>() is { } spectrumKeyboardBacklightController)194{195if (await spectrumKeyboardBacklightController.IsSupportedAsync())196await spectrumKeyboardBacklightController.StopAuroraIfNeededAsync();197}198}199catch { /* Ignored. */ }200
201try202{203if (IoCContainer.TryResolve<NativeWindowsMessageListener>() is { } nativeMessageWindowListener)204{205await nativeMessageWindowListener.StopAsync();206}207}208catch { /* Ignored. */ }209
210
211Shutdown();212}213
214private void AppDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)215{216var exception = e.ExceptionObject as Exception;217
218Log.Instance.ErrorReport("AppDomain_UnhandledException", exception ?? new Exception($"Unknown exception caught: {e.ExceptionObject}"));219Log.Instance.Trace($"Unhandled exception occurred.", exception);220
221MessageBox.Show(string.Format(Resource.UnexpectedException, exception?.ToStringDemystified() ?? "Unknown exception.", Constants.ProjectUri),222"Application Domain Error",223MessageBoxButton.OK,224MessageBoxImage.Error);225Shutdown(1);226}227
228private void Application_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)229{230Log.Instance.ErrorReport("Application_DispatcherUnhandledException", e.Exception);231Log.Instance.Trace($"Unhandled exception occurred.", e.Exception);232
233MessageBox.Show(string.Format(Resource.UnexpectedException, e.Exception.ToStringDemystified(), Constants.ProjectUri),234"Application Error",235MessageBoxButton.OK,236MessageBoxImage.Error);237Shutdown(1);238}239
240private async Task CheckBasicCompatibilityAsync()241{242var isCompatible = await Compatibility.CheckBasicCompatibilityAsync();243if (isCompatible)244return;245
246MessageBox.Show(Resource.IncompatibleDevice_Message, Resource.IncompatibleDevice_Title, MessageBoxButton.OK, MessageBoxImage.Error);247
248Shutdown(99);249}250
251private async Task CheckCompatibilityAsync()252{253var (isCompatible, mi) = await Compatibility.IsCompatibleAsync();254if (isCompatible)255{256if (Log.Instance.IsTraceEnabled)257Log.Instance.Trace($"Compatibility check passed. [Vendor={mi.Vendor}, Model={mi.Model}, MachineType={mi.MachineType}, BIOS={mi.BiosVersion}]");258return;259}260
261if (Log.Instance.IsTraceEnabled)262Log.Instance.Trace($"Incompatible system detected. [Vendor={mi.Vendor}, Model={mi.Model}, MachineType={mi.MachineType}, BIOS={mi.BiosVersion}]");263
264var unsupportedWindow = new UnsupportedWindow(mi);265unsupportedWindow.Show();266
267var result = await unsupportedWindow.ShouldContinue;268if (result)269{270Log.Instance.IsTraceEnabled = true;271
272if (Log.Instance.IsTraceEnabled)273Log.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}]");274return;275}276
277if (Log.Instance.IsTraceEnabled)278Log.Instance.Trace($"Shutting down... [Vendor={mi.Vendor}, Model={mi.Model}, MachineType={mi.MachineType}]");279
280Shutdown(100);281}282
283private void EnsureSingleInstance()284{285if (Log.Instance.IsTraceEnabled)286Log.Instance.Trace($"Checking for other instances...");287
288_singleInstanceMutex = new Mutex(true, MUTEX_NAME, out var isOwned);289_singleInstanceWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset, EVENT_NAME);290
291if (!isOwned)292{293if (Log.Instance.IsTraceEnabled)294Log.Instance.Trace($"Another instance running, closing...");295
296_singleInstanceWaitHandle.Set();297Shutdown();298return;299}300
301new Thread(() =>302{303while (_singleInstanceWaitHandle.WaitOne())304{305Current.Dispatcher.BeginInvoke(async () =>306{307if (Current.MainWindow is { } window)308{309if (Log.Instance.IsTraceEnabled)310Log.Instance.Trace($"Another instance started, bringing this one to front instead...");311
312window.BringToForeground();313}314else315{316if (Log.Instance.IsTraceEnabled)317Log.Instance.Trace($"!!! PANIC !!! This instance is missing main window. Shutting down.");318
319await ShutdownAsync();320}321});322}323})324{325IsBackground = true326}.Start();327}328
329private static async Task LogSoftwareStatusAsync()330{331if (!Log.Instance.IsTraceEnabled)332return;333
334var vantageStatus = await IoCContainer.Resolve<VantageDisabler>().GetStatusAsync();335Log.Instance.Trace($"Vantage status: {vantageStatus}");336
337var legionZoneStatus = await IoCContainer.Resolve<LegionZoneDisabler>().GetStatusAsync();338Log.Instance.Trace($"LegionZone status: {legionZoneStatus}");339
340var fnKeysStatus = await IoCContainer.Resolve<FnKeysDisabler>().GetStatusAsync();341Log.Instance.Trace($"FnKeys status: {fnKeysStatus}");342}343
344private static async Task InitHybridModeAsync()345{346try347{348if (Log.Instance.IsTraceEnabled)349Log.Instance.Trace($"Initializing hybrid mode...");350
351var feature = IoCContainer.Resolve<HybridModeFeature>();352await feature.EnsureDGPUEjectedIfNeededAsync();353}354catch (Exception ex)355{356if (Log.Instance.IsTraceEnabled)357Log.Instance.Trace($"Couldn't initialize hybrid mode.", ex);358}359}360
361private static async Task InitAutomationProcessorAsync()362{363try364{365if (Log.Instance.IsTraceEnabled)366Log.Instance.Trace($"Initializing automation processor...");367
368var automationProcessor = IoCContainer.Resolve<AutomationProcessor>();369await automationProcessor.InitializeAsync();370automationProcessor.RunOnStartup();371}372catch (Exception ex)373{374if (Log.Instance.IsTraceEnabled)375Log.Instance.Trace($"Couldn't initialize automation processor.", ex);376}377}378
379private static async Task InitPowerModeFeatureAsync()380{381try382{383var feature = IoCContainer.Resolve<PowerModeFeature>();384if (await feature.IsSupportedAsync())385{386if (Log.Instance.IsTraceEnabled)387Log.Instance.Trace($"Ensuring god mode state is applied...");388
389await feature.EnsureGodModeStateIsAppliedAsync();390}391}392catch (Exception ex)393{394if (Log.Instance.IsTraceEnabled)395Log.Instance.Trace($"Couldn't ensure god mode state.", ex);396}397
398try399{400var feature = IoCContainer.Resolve<PowerModeFeature>();401if (await feature.IsSupportedAsync())402{403if (Log.Instance.IsTraceEnabled)404Log.Instance.Trace($"Ensuring correct power plan is set...");405
406await feature.EnsureCorrectPowerPlanIsSetAsync();407}408}409catch (Exception ex)410{411if (Log.Instance.IsTraceEnabled)412Log.Instance.Trace($"Couldn't ensure correct power plan.", ex);413}414}415
416private static async Task InitBatteryFeatureAsync()417{418try419{420var feature = IoCContainer.Resolve<BatteryFeature>();421if (await feature.IsSupportedAsync())422{423if (Log.Instance.IsTraceEnabled)424Log.Instance.Trace($"Ensuring correct battery mode is set...");425
426await feature.EnsureCorrectBatteryModeIsSetAsync();427}428}429catch (Exception ex)430{431if (Log.Instance.IsTraceEnabled)432Log.Instance.Trace($"Couldn't ensure correct battery mode.", ex);433}434}435
436private static async Task InitRgbKeyboardControllerAsync()437{438try439{440var controller = IoCContainer.Resolve<RGBKeyboardBacklightController>();441if (await controller.IsSupportedAsync())442{443if (Log.Instance.IsTraceEnabled)444Log.Instance.Trace($"Setting light control owner and restoring preset...");445
446await controller.SetLightControlOwnerAsync(true, true);447}448else449{450if (Log.Instance.IsTraceEnabled)451Log.Instance.Trace($"RGB keyboard is not supported.");452}453}454catch (Exception ex)455{456if (Log.Instance.IsTraceEnabled)457Log.Instance.Trace($"Couldn't set light control owner or current preset.", ex);458}459}460
461private static async Task InitSpectrumKeyboardControllerAsync()462{463try464{465var controller = IoCContainer.Resolve<SpectrumKeyboardBacklightController>();466if (await controller.IsSupportedAsync())467{468if (Log.Instance.IsTraceEnabled)469Log.Instance.Trace($"Starting Aurora if needed...");470
471var result = await controller.StartAuroraIfNeededAsync();472if (result)473{474if (Log.Instance.IsTraceEnabled)475Log.Instance.Trace($"Aurora started.");476}477else478{479if (Log.Instance.IsTraceEnabled)480Log.Instance.Trace($"Aurora not needed.");481}482}483else484{485if (Log.Instance.IsTraceEnabled)486Log.Instance.Trace($"Spectrum keyboard is not supported.");487}488}489catch (Exception ex)490{491if (Log.Instance.IsTraceEnabled)492Log.Instance.Trace($"Couldn't start Aurora if needed.", ex);493}494}495
496private static async Task InitGpuOverclockControllerAsync()497{498try499{500var controller = IoCContainer.Resolve<GPUOverclockController>();501if (await controller.IsSupportedAsync())502{503if (Log.Instance.IsTraceEnabled)504Log.Instance.Trace($"Ensuring GPU overclock is applied...");505
506var result = await controller.EnsureOverclockIsAppliedAsync().ConfigureAwait(false);507if (result)508{509if (Log.Instance.IsTraceEnabled)510Log.Instance.Trace($"GPU overclock applied.");511}512else513{514if (Log.Instance.IsTraceEnabled)515Log.Instance.Trace($"GPU overclock not needed.");516}517}518else519{520if (Log.Instance.IsTraceEnabled)521Log.Instance.Trace($"GPU overclock is not supported.");522}523}524catch (Exception ex)525{526if (Log.Instance.IsTraceEnabled)527Log.Instance.Trace($"Couldn't overclock GPU.", ex);528}529}530}
531