From dafd3128c2737897cac69827d9de42499e60920e Mon Sep 17 00:00:00 2001 From: Lightczx <1686188646@qq.com> Date: Sat, 11 May 2024 16:23:52 +0800 Subject: [PATCH] adjust lifecycle --- src/Snap.Hutao/Snap.Hutao/App.xaml.cs | 12 +- .../Snap.Hutao/Core/LifeCycle/Activation.cs | 36 ++--- .../CurrentWindowReferenceExtension.cs | 2 +- .../Snap.Hutao/Core/LifeCycle/IActivation.cs | 2 +- .../Snap.Hutao/Core/Setting/SettingKeys.cs | 1 + .../Snap.Hutao/Core/Shell/IJumpListInterop.cs | 16 --- .../Snap.Hutao/Core/Shell/JumpListInterop.cs | 36 ----- .../Windowing/HotKey/HotKeyCombination.cs | 24 ++-- .../Core/Windowing/HotKey/HotKeyController.cs | 97 -------------- .../Windowing/HotKey/HotKeyMessageWindow.cs | 92 +++++++++++++ .../Core/Windowing/HotKey/HotKeyOptions.cs | 112 +++++++++++++++- .../Windowing/HotKey/IHotKeyController.cs | 13 -- ...sSource.cs => IXamlWindowOptionsSource.cs} | 4 +- .../NotifyIcon/NotifyIconController.cs | 19 ++- .../NotifyIcon/NotifyIconMessageWindow.cs | 125 ++++++++++-------- .../Core/Windowing/WindowExtension.cs | 6 +- ...wController.cs => XamlWindowController.cs} | 12 +- ...onRudeHWND.cs => XamlWindowNonRudeHWND.cs} | 4 +- ...{WindowOptions.cs => XamlWindowOptions.cs} | 4 +- ...indowSubclass.cs => XamlWindowSubclass.cs} | 33 +---- src/Snap.Hutao/Snap.Hutao/GuideWindow.xaml.cs | 6 +- .../Snap.Hutao/LaunchGameWindow.xaml.cs | 6 +- src/Snap.Hutao/Snap.Hutao/MainWindow.xaml.cs | 6 +- src/Snap.Hutao/Snap.Hutao/Program.cs | 9 +- 24 files changed, 364 insertions(+), 313 deletions(-) delete mode 100644 src/Snap.Hutao/Snap.Hutao/Core/Shell/IJumpListInterop.cs delete mode 100644 src/Snap.Hutao/Snap.Hutao/Core/Shell/JumpListInterop.cs delete mode 100644 src/Snap.Hutao/Snap.Hutao/Core/Windowing/HotKey/HotKeyController.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Core/Windowing/HotKey/HotKeyMessageWindow.cs delete mode 100644 src/Snap.Hutao/Snap.Hutao/Core/Windowing/HotKey/IHotKeyController.cs rename src/Snap.Hutao/Snap.Hutao/Core/Windowing/{IWindowOptionsSource.cs => IXamlWindowOptionsSource.cs} (74%) rename src/Snap.Hutao/Snap.Hutao/Core/Windowing/{WindowController.cs => XamlWindowController.cs} (95%) rename src/Snap.Hutao/Snap.Hutao/Core/Windowing/{WindowNonRudeHWND.cs => XamlWindowNonRudeHWND.cs} (85%) rename src/Snap.Hutao/Snap.Hutao/Core/Windowing/{WindowOptions.cs => XamlWindowOptions.cs} (92%) rename src/Snap.Hutao/Snap.Hutao/Core/Windowing/{WindowSubclass.cs => XamlWindowSubclass.cs} (69%) diff --git a/src/Snap.Hutao/Snap.Hutao/App.xaml.cs b/src/Snap.Hutao/Snap.Hutao/App.xaml.cs index 9e040829..2af98f7a 100644 --- a/src/Snap.Hutao/Snap.Hutao/App.xaml.cs +++ b/src/Snap.Hutao/Snap.Hutao/App.xaml.cs @@ -8,7 +8,9 @@ using Snap.Hutao.Core.ExceptionService; using Snap.Hutao.Core.LifeCycle; using Snap.Hutao.Core.LifeCycle.InterProcess; using Snap.Hutao.Core.Logging; -using Snap.Hutao.Core.Shell; +using Snap.Hutao.Core.Setting; +using Snap.Hutao.Core.Windowing.HotKey; +using Snap.Hutao.Core.Windowing.NotifyIcon; using System.Diagnostics; namespace Snap.Hutao; @@ -42,6 +44,8 @@ public sealed partial class App : Application private readonly IActivation activation; private readonly ILogger logger; + private NotifyIconController? notifyIconController; + /// /// Initializes the singleton application object. /// @@ -75,11 +79,9 @@ public sealed partial class App : Application logger.LogColorizedInformation((ConsoleBanner, ConsoleColor.DarkYellow)); LogDiagnosticInformation(); - // manually invoke + // Manually invoke activation.Activate(HutaoActivationArguments.FromAppActivationArguments(activatedEventArgs)); - activation.Initialize(); - - serviceProvider.GetRequiredService().ConfigureAsync().SafeForget(); + activation.PostInitialization(); } catch { diff --git a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/Activation.cs b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/Activation.cs index beb3f261..4146482e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/Activation.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/Activation.cs @@ -5,6 +5,8 @@ using CommunityToolkit.WinUI.Notifications; using Microsoft.Extensions.Caching.Memory; using Snap.Hutao.Core.LifeCycle.InterProcess; using Snap.Hutao.Core.Setting; +using Snap.Hutao.Core.Windowing.HotKey; +using Snap.Hutao.Core.Windowing.NotifyIcon; using Snap.Hutao.Service.DailyNote; using Snap.Hutao.Service.Discord; using Snap.Hutao.Service.Hutao; @@ -22,7 +24,7 @@ namespace Snap.Hutao.Core.LifeCycle; [ConstructorGenerated] [Injection(InjectAs.Singleton, typeof(IActivation))] [SuppressMessage("", "CA1001")] -internal sealed partial class Activation : IActivation +internal sealed partial class Activation : IActivation, IDisposable { public const string Action = nameof(Action); public const string Uid = nameof(Uid); @@ -42,6 +44,8 @@ internal sealed partial class Activation : IActivation /// public void Activate(HutaoActivationArguments args) { + // Before activate, we try to redirect to the opened process in App, + // And we check if it's a toast activation. if (ToastNotificationManagerCompat.WasCurrentProcessToastActivated()) { return; @@ -51,10 +55,21 @@ internal sealed partial class Activation : IActivation } /// - public void Initialize() + public void PostInitialization() { serviceProvider.GetRequiredService().RunAsync().SafeForget(); ToastNotificationManagerCompat.OnActivated += NotificationActivate; + + serviceProvider.GetRequiredService().RegisterAll(); + if (LocalSetting.Get(SettingKeys.IsNotifyIconEnabled, true)) + { + _ = serviceProvider.GetRequiredService(); + } + } + + public void Dispose() + { + activateSemaphore.Dispose(); } private void NotificationActivate(ToastNotificationActivatedEventArgsCompat args) @@ -94,12 +109,6 @@ internal sealed partial class Activation : IActivation ArgumentNullException.ThrowIfNull(args.LaunchActivatedArguments); switch (args.LaunchActivatedArguments) { - case LaunchGame: - { - await HandleLaunchGameActionAsync().ConfigureAwait(false); - break; - } - default: { await HandleNormalLaunchActionAsync().ConfigureAwait(false); @@ -112,10 +121,9 @@ internal sealed partial class Activation : IActivation private async ValueTask HandleNormalLaunchActionAsync() { // Increase launch times - LocalSetting.Update(SettingKeys.LaunchTimes, 0, x => x + 1); + LocalSetting.Update(SettingKeys.LaunchTimes, 0, x => unchecked(x + 1)); - // If it's the first time launch, we show the guide window anyway. - // Otherwise, we check if there's any unfulfilled resource category present. + // If the guide is completed, we check if there's any unfulfilled resource category present. if (UnsafeLocalSetting.Get(SettingKeys.Major1Minor10Revision0GuideState, GuideState.Language) >= GuideState.StaticResourceBegin) { if (StaticResource.IsAnyUnfulfilledCategoryPresent()) @@ -124,6 +132,7 @@ internal sealed partial class Activation : IActivation } } + // If it's the first time launch, show the guide window anyway. if (UnsafeLocalSetting.Get(SettingKeys.Major1Minor10Revision0GuideState, GuideState.Language) < GuideState.Completed) { await taskContext.SwitchToMainThreadAsync(); @@ -158,10 +167,7 @@ internal sealed partial class Activation : IActivation hutaoUserServiceInitialization.InitializeInternalAsync().SafeForget(); } - serviceProvider - .GetRequiredService() - .SetNormalActivityAsync() - .SafeForget(); + serviceProvider.GetRequiredService().SetNormalActivityAsync().SafeForget(); } private async ValueTask HandleUrlActivationAsync(Uri uri, bool isRedirectTo) diff --git a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/CurrentWindowReferenceExtension.cs b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/CurrentWindowReferenceExtension.cs index ab424676..ef5fc374 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/CurrentWindowReferenceExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/CurrentWindowReferenceExtension.cs @@ -17,7 +17,7 @@ internal static class CurrentWindowReferenceExtension public static HWND GetWindowHandle(this ICurrentWindowReference reference) { - return reference.Window is IWindowOptionsSource optionsSource + return reference.Window is IXamlWindowOptionsSource optionsSource ? optionsSource.WindowOptions.Hwnd : WindowNative.GetWindowHandle(reference.Window); } diff --git a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/IActivation.cs b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/IActivation.cs index ca73609f..ad344e67 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/IActivation.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/LifeCycle/IActivation.cs @@ -10,5 +10,5 @@ internal interface IActivation { void Activate(HutaoActivationArguments args); - void Initialize(); + void PostInitialization(); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Setting/SettingKeys.cs b/src/Snap.Hutao/Snap.Hutao/Core/Setting/SettingKeys.cs index f848c10b..fbb40ce2 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Setting/SettingKeys.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Setting/SettingKeys.cs @@ -26,6 +26,7 @@ internal static class SettingKeys public const string StaticResourceImageArchive = "StaticResourceImageArchive"; public const string HotKeyMouseClickRepeatForever = "HotKeyMouseClickRepeatForever"; public const string IsAllocConsoleDebugModeEnabled = "IsAllocConsoleDebugModeEnabled2"; + public const string IsNotifyIconEnabled = "IsNotifyIconEnabled"; #endregion #region Passport diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Shell/IJumpListInterop.cs b/src/Snap.Hutao/Snap.Hutao/Core/Shell/IJumpListInterop.cs deleted file mode 100644 index 1da9460b..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Core/Shell/IJumpListInterop.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -namespace Snap.Hutao.Core.Shell; - -/// -/// 跳转列表交互 -/// -internal interface IJumpListInterop -{ - /// - /// 异步配置跳转列表 - /// - /// 任务 - ValueTask ConfigureAsync(); -} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Shell/JumpListInterop.cs b/src/Snap.Hutao/Snap.Hutao/Core/Shell/JumpListInterop.cs deleted file mode 100644 index 2e875dd4..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Core/Shell/JumpListInterop.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -using Snap.Hutao.Core.LifeCycle; -using Windows.UI.StartScreen; - -namespace Snap.Hutao.Core.Shell; - -/// -/// 跳转列表交互 -/// -[HighQuality] -[Injection(InjectAs.Transient, typeof(IJumpListInterop))] -internal sealed class JumpListInterop : IJumpListInterop -{ - /// - /// 异步配置跳转列表 - /// - /// 任务 - public async ValueTask ConfigureAsync() - { - if (JumpList.IsSupported()) - { - JumpList list = await JumpList.LoadCurrentAsync(); - - list.Items.Clear(); - - JumpListItem launchGameItem = JumpListItem.CreateWithArguments(Activation.LaunchGame, SH.CoreJumpListHelperLaunchGameItemDisplayName); - launchGameItem.Logo = "ms-appx:///Resource/Navigation/LaunchGame.png".ToUri(); - - list.Items.Add(launchGameItem); - - await list.SaveAsync(); - } - } -} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/HotKey/HotKeyCombination.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/HotKey/HotKeyCombination.cs index c1006677..fca1e093 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/HotKey/HotKeyCombination.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/HotKey/HotKeyCombination.cs @@ -17,10 +17,10 @@ namespace Snap.Hutao.Core.Windowing.HotKey; [SuppressMessage("", "SA1124")] internal sealed class HotKeyCombination : ObservableObject { - private readonly ICurrentWindowReference currentWindowReference; private readonly IInfoBarService infoBarService; private readonly RuntimeOptions runtimeOptions; + private readonly HWND hwnd; private readonly string settingKey; private readonly int hotKeyId; private readonly HotKeyParameter defaultHotKeyParameter; @@ -36,12 +36,12 @@ internal sealed class HotKeyCombination : ObservableObject private VirtualKey key; private bool isEnabled; - public HotKeyCombination(IServiceProvider serviceProvider, string settingKey, int hotKeyId, HOT_KEY_MODIFIERS defaultModifiers, VirtualKey defaultKey) + public HotKeyCombination(IServiceProvider serviceProvider, HWND hwnd, string settingKey, int hotKeyId, HOT_KEY_MODIFIERS defaultModifiers, VirtualKey defaultKey) { - currentWindowReference = serviceProvider.GetRequiredService(); infoBarService = serviceProvider.GetRequiredService(); runtimeOptions = serviceProvider.GetRequiredService(); + this.hwnd = hwnd; this.settingKey = settingKey; this.hotKeyId = hotKeyId; defaultHotKeyParameter = new(defaultModifiers, defaultKey); @@ -53,7 +53,7 @@ internal sealed class HotKeyCombination : ObservableObject HotKeyParameter actual = LocalSettingGetHotKeyParameter(); modifiers = actual.Modifiers; - InitializeModifiersComposeFields(); + InitializeModifiersCompositionFields(); key = actual.Key; keyNameValue = VirtualKeys.GetList().Single(v => v.Value == key); @@ -164,8 +164,8 @@ internal sealed class HotKeyCombination : ObservableObject _ = (value, registered) switch { - (true, false) => RegisterForCurrentWindow(), - (false, true) => UnregisterForCurrentWindow(), + (true, false) => Register(), + (false, true) => Unregister(), _ => false, }; } @@ -174,7 +174,7 @@ internal sealed class HotKeyCombination : ObservableObject public string DisplayName { get => ToString(); } - public bool RegisterForCurrentWindow() + public bool Register() { if (!runtimeOptions.IsElevated || !IsEnabled) { @@ -186,7 +186,6 @@ internal sealed class HotKeyCombination : ObservableObject return true; } - HWND hwnd = currentWindowReference.GetWindowHandle(); BOOL result = RegisterHotKey(hwnd, hotKeyId, Modifiers, (uint)Key); registered = result; @@ -198,7 +197,7 @@ internal sealed class HotKeyCombination : ObservableObject return result; } - public bool UnregisterForCurrentWindow() + public bool Unregister() { if (!runtimeOptions.IsElevated) { @@ -210,7 +209,6 @@ internal sealed class HotKeyCombination : ObservableObject return true; } - HWND hwnd = currentWindowReference.GetWindowHandle(); BOOL result = UnregisterHotKey(hwnd, hotKeyId); registered = !result; return result; @@ -272,7 +270,7 @@ internal sealed class HotKeyCombination : ObservableObject Modifiers = modifiers; } - private void InitializeModifiersComposeFields() + private void InitializeModifiersCompositionFields() { if (Modifiers.HasFlag(HOT_KEY_MODIFIERS.MOD_WIN)) { @@ -309,7 +307,7 @@ internal sealed class HotKeyCombination : ObservableObject HotKeyParameter current = new(Modifiers, Key); LocalSetting.Set(settingKey, *(int*)¤t); - UnregisterForCurrentWindow(); - RegisterForCurrentWindow(); + Unregister(); + Register(); } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/HotKey/HotKeyController.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/HotKey/HotKeyController.cs deleted file mode 100644 index fde8196b..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/HotKey/HotKeyController.cs +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -using Snap.Hutao.Win32.UI.Input.KeyboardAndMouse; -using System.Runtime.InteropServices; -using static Snap.Hutao.Win32.User32; - -namespace Snap.Hutao.Core.Windowing.HotKey; - -[SuppressMessage("", "CA1001")] -[Injection(InjectAs.Singleton, typeof(IHotKeyController))] -[ConstructorGenerated] -internal sealed partial class HotKeyController : IHotKeyController -{ - private static readonly WaitCallback RunMouseClickRepeatForever = MouseClickRepeatForever; - - private readonly object syncRoot = new(); - - private readonly HotKeyOptions hotKeyOptions; - - private volatile CancellationTokenSource? cancellationTokenSource; - - public void RegisterAll() - { - hotKeyOptions.MouseClickRepeatForeverKeyCombination.RegisterForCurrentWindow(); - } - - public void UnregisterAll() - { - hotKeyOptions.MouseClickRepeatForeverKeyCombination.UnregisterForCurrentWindow(); - } - - public void OnHotKeyPressed(in HotKeyParameter parameter) - { - if (parameter.Equals(hotKeyOptions.MouseClickRepeatForeverKeyCombination)) - { - ToggleMouseClickRepeatForever(); - } - } - - private static unsafe INPUT CreateInputForMouseEvent(MOUSE_EVENT_FLAGS flags) - { - INPUT input = default; - input.type = INPUT_TYPE.INPUT_MOUSE; - input.Anonymous.mi.dwFlags = flags; - return input; - } - - [SuppressMessage("", "SH007")] - private static unsafe void MouseClickRepeatForever(object? state) - { - CancellationToken token = (CancellationToken)state!; - - // We want to use this thread for a long time - while (!token.IsCancellationRequested) - { - INPUT[] inputs = - [ - CreateInputForMouseEvent(MOUSE_EVENT_FLAGS.MOUSEEVENTF_LEFTDOWN), - CreateInputForMouseEvent(MOUSE_EVENT_FLAGS.MOUSEEVENTF_LEFTUP), - ]; - - if (SendInput(inputs.AsSpan(), sizeof(INPUT)) is 0) - { - Marshal.ThrowExceptionForHR(Marshal.GetLastPInvokeError()); - } - - if (token.IsCancellationRequested) - { - return; - } - - Thread.Sleep(System.Random.Shared.Next(100, 150)); - } - } - - private void ToggleMouseClickRepeatForever() - { - lock (syncRoot) - { - if (hotKeyOptions.IsMouseClickRepeatForeverOn) - { - // Turn off - cancellationTokenSource?.Cancel(); - cancellationTokenSource = default; - hotKeyOptions.IsMouseClickRepeatForeverOn = false; - } - else - { - // Turn on - cancellationTokenSource = new(); - ThreadPool.QueueUserWorkItem(RunMouseClickRepeatForever, cancellationTokenSource.Token); - hotKeyOptions.IsMouseClickRepeatForeverOn = true; - } - } - } -} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/HotKey/HotKeyMessageWindow.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/HotKey/HotKeyMessageWindow.cs new file mode 100644 index 00000000..b58517d5 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/HotKey/HotKeyMessageWindow.cs @@ -0,0 +1,92 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Win32.Foundation; +using Snap.Hutao.Win32.UI.WindowsAndMessaging; +using System.Collections.Concurrent; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using static Snap.Hutao.Win32.ConstValues; +using static Snap.Hutao.Win32.User32; + +namespace Snap.Hutao.Core.Windowing.HotKey; + +internal sealed class HotKeyMessageWindow : IDisposable +{ + private const string WindowClassName = "SnapHutaoHotKeyMessageWindowClass"; + + private static readonly ConcurrentDictionary WindowTable = []; + + private bool isDisposed; + + public unsafe HotKeyMessageWindow() + { + ushort atom; + fixed (char* className = WindowClassName) + { + WNDCLASSW wc = new() + { + lpfnWndProc = WNDPROC.Create(&OnWindowProcedure), + lpszClassName = className, + }; + + atom = RegisterClassW(&wc); + } + + ArgumentOutOfRangeException.ThrowIfEqual(atom, 0); + + HWND = CreateWindowExW(0, WindowClassName, WindowClassName, 0, 0, 0, 0, 0, default, default, default, default); + + if (HWND == default) + { + Marshal.ThrowExceptionForHR(Marshal.GetLastPInvokeError()); + } + + WindowTable.TryAdd(HWND, this); + } + + ~HotKeyMessageWindow() + { + Dispose(); + } + + public Action? HotKeyPressed { get; set; } + + public HWND HWND { get; } + + public void Dispose() + { + if (isDisposed) + { + return; + } + + isDisposed = true; + + DestroyWindow(HWND); + WindowTable.TryRemove(HWND, out _); + + GC.SuppressFinalize(this); + } + + [SuppressMessage("", "SH002")] + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvStdcall)])] + private static unsafe LRESULT OnWindowProcedure(HWND hwnd, uint uMsg, WPARAM wParam, LPARAM lParam) + { + if (!WindowTable.TryGetValue(hwnd, out HotKeyMessageWindow? window)) + { + return DefWindowProcW(hwnd, uMsg, wParam, lParam); + } + + switch (uMsg) + { + case WM_HOTKEY: + window.HotKeyPressed?.Invoke(*(HotKeyParameter*)&lParam); + break; + default: + break; + } + + return DefWindowProcW(hwnd, uMsg, wParam, lParam); + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/HotKey/HotKeyOptions.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/HotKey/HotKeyOptions.cs index fe49fbaa..b3f676dd 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/HotKey/HotKeyOptions.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/HotKey/HotKeyOptions.cs @@ -4,19 +4,35 @@ using CommunityToolkit.Mvvm.ComponentModel; using Snap.Hutao.Core.Setting; using Snap.Hutao.Model; +using Snap.Hutao.Win32.UI.Input.KeyboardAndMouse; +using System.Runtime.InteropServices; using Windows.System; +using static Snap.Hutao.Win32.User32; namespace Snap.Hutao.Core.Windowing.HotKey; [Injection(InjectAs.Singleton)] -internal sealed partial class HotKeyOptions : ObservableObject +internal sealed partial class HotKeyOptions : ObservableObject, IDisposable { + private static readonly WaitCallback RunMouseClickRepeatForever = MouseClickRepeatForever; + + private readonly object syncRoot = new(); + private readonly HotKeyMessageWindow hotKeyMessageWindow; + + private volatile CancellationTokenSource? cancellationTokenSource; + + private bool isDisposed; private bool isMouseClickRepeatForeverOn; private HotKeyCombination mouseClickRepeatForeverKeyCombination; public HotKeyOptions(IServiceProvider serviceProvider) { - mouseClickRepeatForeverKeyCombination = new(serviceProvider, SettingKeys.HotKeyMouseClickRepeatForever, 100000, default, VirtualKey.F8); + hotKeyMessageWindow = new() + { + HotKeyPressed = OnHotKeyPressed, + }; + + mouseClickRepeatForeverKeyCombination = new(serviceProvider, hotKeyMessageWindow.HWND, SettingKeys.HotKeyMouseClickRepeatForever, 100000, default, VirtualKey.F8); } public List> VirtualKeys { get; } = HotKey.VirtualKeys.GetList(); @@ -32,4 +48,96 @@ internal sealed partial class HotKeyOptions : ObservableObject get => mouseClickRepeatForeverKeyCombination; set => SetProperty(ref mouseClickRepeatForeverKeyCombination, value); } + + public void Dispose() + { + if (isDisposed) + { + return; + } + + isDisposed = true; + + UnregisterAll(); + hotKeyMessageWindow.Dispose(); + cancellationTokenSource?.Dispose(); + + GC.SuppressFinalize(this); + } + + public void RegisterAll() + { + MouseClickRepeatForeverKeyCombination.Register(); + } + + private static unsafe INPUT CreateInputForMouseEvent(MOUSE_EVENT_FLAGS flags) + { + INPUT input = default; + input.type = INPUT_TYPE.INPUT_MOUSE; + input.Anonymous.mi.dwFlags = flags; + return input; + } + + [SuppressMessage("", "SH007")] + private static unsafe void MouseClickRepeatForever(object? state) + { + CancellationToken token = (CancellationToken)state!; + + // We want to use this thread for a long time + while (!token.IsCancellationRequested) + { + INPUT[] inputs = + [ + CreateInputForMouseEvent(MOUSE_EVENT_FLAGS.MOUSEEVENTF_LEFTDOWN), + CreateInputForMouseEvent(MOUSE_EVENT_FLAGS.MOUSEEVENTF_LEFTUP), + ]; + + if (SendInput(inputs.AsSpan(), sizeof(INPUT)) is 0) + { + Marshal.ThrowExceptionForHR(Marshal.GetLastPInvokeError()); + } + + if (token.IsCancellationRequested) + { + return; + } + + Thread.Sleep(System.Random.Shared.Next(100, 150)); + } + } + + private void UnregisterAll() + { + MouseClickRepeatForeverKeyCombination.Unregister(); + } + + [SuppressMessage("", "SH002")] + private void OnHotKeyPressed(HotKeyParameter parameter) + { + if (parameter.Equals(MouseClickRepeatForeverKeyCombination)) + { + ToggleMouseClickRepeatForever(); + } + } + + private void ToggleMouseClickRepeatForever() + { + lock (syncRoot) + { + if (IsMouseClickRepeatForeverOn) + { + // Turn off + cancellationTokenSource?.Cancel(); + cancellationTokenSource = default; + IsMouseClickRepeatForeverOn = false; + } + else + { + // Turn on + cancellationTokenSource = new(); + ThreadPool.QueueUserWorkItem(RunMouseClickRepeatForever, cancellationTokenSource.Token); + IsMouseClickRepeatForeverOn = true; + } + } + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/HotKey/IHotKeyController.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/HotKey/IHotKeyController.cs deleted file mode 100644 index c78fa472..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/HotKey/IHotKeyController.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -namespace Snap.Hutao.Core.Windowing.HotKey; - -internal interface IHotKeyController -{ - void OnHotKeyPressed(in HotKeyParameter parameter); - - void RegisterAll(); - - void UnregisterAll(); -} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/IWindowOptionsSource.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/IXamlWindowOptionsSource.cs similarity index 74% rename from src/Snap.Hutao/Snap.Hutao/Core/Windowing/IWindowOptionsSource.cs rename to src/Snap.Hutao/Snap.Hutao/Core/Windowing/IXamlWindowOptionsSource.cs index 5c878657..fa9a060b 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/IWindowOptionsSource.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/IXamlWindowOptionsSource.cs @@ -6,10 +6,10 @@ namespace Snap.Hutao.Core.Windowing; /// /// 为扩展窗体提供必要的选项 /// -internal interface IWindowOptionsSource +internal interface IXamlWindowOptionsSource { /// /// 窗体选项 /// - WindowOptions WindowOptions { get; } + XamlWindowOptions WindowOptions { get; } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/NotifyIcon/NotifyIconController.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/NotifyIcon/NotifyIconController.cs index eab29986..2dede31c 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/NotifyIcon/NotifyIconController.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/NotifyIcon/NotifyIconController.cs @@ -10,6 +10,7 @@ using static Snap.Hutao.Win32.ConstValues; namespace Snap.Hutao.Core.Windowing.NotifyIcon; +[Injection(InjectAs.Singleton)] internal sealed class NotifyIconController : IDisposable { private readonly NotifyIconMessageWindow messageWindow; @@ -17,13 +18,23 @@ internal sealed class NotifyIconController : IDisposable public NotifyIconController() { - messageWindow = new(); - - NotifyIconMethods.Delete(Id); - StorageFile iconFile = StorageFile.GetFileFromApplicationUriAsync("ms-appx:///Assets/Logo.ico".ToUri()).AsTask().GetAwaiter().GetResult(); icon = new(iconFile.Path); + messageWindow = new() + { + TaskbarCreated = window => + { + NotifyIconMethods.Delete(Id); + if (!NotifyIconMethods.Add(Id, window.HWND, "Snap Hutao", NotifyIconMessageWindow.WM_NOTIFYICON_CALLBACK, (HICON)icon.Handle)) + { + HutaoException.InvalidOperation("Failed to recreate NotifyIcon"); + } + }, + }; + + NotifyIconMethods.Delete(Id); + if (!NotifyIconMethods.Add(Id, messageWindow.HWND, "Snap Hutao", NotifyIconMessageWindow.WM_NOTIFYICON_CALLBACK, (HICON)icon.Handle)) { HutaoException.InvalidOperation("Failed to create NotifyIcon"); diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/NotifyIcon/NotifyIconMessageWindow.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/NotifyIcon/NotifyIconMessageWindow.cs index f6a27a4e..b6c0e585 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/NotifyIcon/NotifyIconMessageWindow.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/NotifyIcon/NotifyIconMessageWindow.cs @@ -1,6 +1,8 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using Microsoft.UI.Windowing; +using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Snap.Hutao.Win32.Foundation; using Snap.Hutao.Win32.UI.WindowsAndMessaging; @@ -19,12 +21,10 @@ internal sealed class NotifyIconMessageWindow : IDisposable public const uint WM_NOTIFYICON_CALLBACK = 0x444U; private const string WindowClassName = "SnapHutaoNotifyIconMessageWindowClass"; - public readonly HWND HWND; - private static readonly ConcurrentDictionary WindowTable = []; [SuppressMessage("", "SA1306")] - private uint WM_TASKBARCREATED; + private readonly uint WM_TASKBARCREATED; private bool isDisposed; @@ -62,6 +62,10 @@ internal sealed class NotifyIconMessageWindow : IDisposable Dispose(); } + public Action? TaskbarCreated { get; set; } + + public HWND HWND { get; } + public void Dispose() { if (isDisposed) @@ -81,59 +85,62 @@ internal sealed class NotifyIconMessageWindow : IDisposable [UnmanagedCallersOnly(CallConvs = [typeof(CallConvStdcall)])] private static unsafe LRESULT OnWindowProcedure(HWND hwnd, uint uMsg, WPARAM wParam, LPARAM lParam) { - if (WindowTable.TryGetValue(hwnd, out NotifyIconMessageWindow? window)) + if (!WindowTable.TryGetValue(hwnd, out NotifyIconMessageWindow? window)) { - if (uMsg == window.WM_TASKBARCREATED) - { - // TODO: Re-add the notify icon. - } + return DefWindowProcW(hwnd, uMsg, wParam, lParam); + } - // https://learn.microsoft.com/zh-cn/windows/win32/api/shellapi/ns-shellapi-notifyicondataw - if (uMsg is WM_NOTIFYICON_CALLBACK) - { - LPARAM2 lParam2 = *(LPARAM2*)&lParam; - WPARAM2 wParam2 = *(WPARAM2*)&wParam; + if (uMsg == window.WM_TASKBARCREATED) + { + // TODO: Re-add the notify icon. + window.TaskbarCreated?.Invoke(window); + } - switch (lParam2.Low) - { - case WM_MOUSEMOVE: - // X: wParam2.X Y: wParam2.Y Low: WM_MOUSEMOVE - break; - case NIN_SELECT: - // X: wParam2.X Y: wParam2.Y Low: NIN_SELECT - break; - case NIN_POPUPOPEN: - // X: wParam2.X Y: 0? Low: NIN_POPUPOPEN - break; - case NIN_POPUPCLOSE: - // X: wParam2.X Y: 0? Low: NIN_POPUPCLOSE - break; - case WM_LBUTTONDOWN: - case WM_LBUTTONUP: - break; - case WM_RBUTTONDOWN: - case WM_RBUTTONUP: - break; - case WM_CONTEXTMENU: - Debug.WriteLine($"[uMsg: 0x{uMsg:X8}] [X: {wParam2.X} Y: {wParam2.Y}] [Low: WM_CONTEXTMENU High: 0x{lParam2.High:X8}]"); - break; - default: - Debug.WriteLine($"[uMsg: 0x{uMsg:X8}] [X: {wParam2.X} Y: {wParam2.Y}] [Low: 0x{lParam2.Low:X8} High: 0x{lParam2.High:X8}]"); - break; - } - } - else + // https://learn.microsoft.com/zh-cn/windows/win32/api/shellapi/ns-shellapi-notifyicondataw + if (uMsg is WM_NOTIFYICON_CALLBACK) + { + LPARAM2 lParam2 = *(LPARAM2*)&lParam; + WPARAM2 wParam2 = *(WPARAM2*)&wParam; + + switch (lParam2.Low) { - switch (uMsg) - { - case WM_ACTIVATEAPP: - break; - case WM_DWMNCRENDERINGCHANGED: - break; - default: - Debug.WriteLine($"[uMsg: 0x{uMsg:X8}] [wParam: 0x{wParam.Value:X8}] [lParam: 0x{lParam.Value:X8}]"); - break; - } + case WM_MOUSEMOVE: + // X: wParam2.X Y: wParam2.Y Low: WM_MOUSEMOVE + break; + case NIN_SELECT: + // X: wParam2.X Y: wParam2.Y Low: NIN_SELECT + break; + case NIN_POPUPOPEN: + // X: wParam2.X Y: 0? Low: NIN_POPUPOPEN + break; + case NIN_POPUPCLOSE: + // X: wParam2.X Y: 0? Low: NIN_POPUPCLOSE + break; + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + break; + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + break; + case WM_CONTEXTMENU: + Debug.WriteLine($"[uMsg: 0x{uMsg:X8}] [X: {wParam2.X} Y: {wParam2.Y}] [Low: WM_CONTEXTMENU High: 0x{lParam2.High:X8}]"); + break; + default: + Debug.WriteLine($"[uMsg: 0x{uMsg:X8}] [X: {wParam2.X} Y: {wParam2.Y}] [Low: 0x{lParam2.Low:X8} High: 0x{lParam2.High:X8}]"); + break; + } + } + else + { + switch (uMsg) + { + case WM_ACTIVATEAPP: + break; + case WM_DWMNCRENDERINGCHANGED: + break; + default: + Debug.WriteLine($"[uMsg: 0x{uMsg:X8}] [wParam: 0x{wParam.Value:X8}] [lParam: 0x{lParam.Value:X8}]"); + break; } } @@ -151,4 +158,18 @@ internal sealed class NotifyIconMessageWindow : IDisposable public readonly ushort X; public readonly ushort Y; } +} + +internal sealed class NotifyIconXamlHostWindow : Window +{ + public NotifyIconXamlHostWindow() + { + Content = new Border(); + + OverlappedPresenter presenter = OverlappedPresenter.Create(); + presenter.SetBorderAndTitleBar(false, false); + presenter.IsAlwaysOnTop = true; + presenter.IsResizable = false; + AppWindow.SetPresenter(presenter); + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowExtension.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowExtension.cs index fec7d2f7..dfd5793d 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowExtension.cs @@ -12,12 +12,12 @@ namespace Snap.Hutao.Core.Windowing; internal static class WindowExtension { - private static readonly ConditionalWeakTable WindowControllers = []; + private static readonly ConditionalWeakTable WindowControllers = []; public static void InitializeController(this TWindow window, IServiceProvider serviceProvider) - where TWindow : Window, IWindowOptionsSource + where TWindow : Window, IXamlWindowOptionsSource { - WindowController windowController = new(window, window.WindowOptions, serviceProvider); + XamlWindowController windowController = new(window, window.WindowOptions, serviceProvider); WindowControllers.Add(window, windowController); } diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowController.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/XamlWindowController.cs similarity index 95% rename from src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowController.cs rename to src/Snap.Hutao/Snap.Hutao/Core/Windowing/XamlWindowController.cs index 2a6fad9f..5dcdd010 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowController.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/XamlWindowController.cs @@ -22,15 +22,15 @@ using static Snap.Hutao.Win32.User32; namespace Snap.Hutao.Core.Windowing; [SuppressMessage("", "CA1001")] -internal sealed class WindowController +internal sealed class XamlWindowController { private readonly Window window; - private readonly WindowOptions options; + private readonly XamlWindowOptions options; private readonly IServiceProvider serviceProvider; - private readonly WindowSubclass subclass; - private readonly WindowNonRudeHWND windowNonRudeHWND; + private readonly XamlWindowSubclass subclass; + private readonly XamlWindowNonRudeHWND windowNonRudeHWND; - public WindowController(Window window, in WindowOptions options, IServiceProvider serviceProvider) + public XamlWindowController(Window window, in XamlWindowOptions options, IServiceProvider serviceProvider) { this.window = window; this.options = options; @@ -38,7 +38,7 @@ internal sealed class WindowController // Window reference must be set before Window Subclass created serviceProvider.GetRequiredService().Window = window; - subclass = new(window, options, serviceProvider); + subclass = new(window, options); windowNonRudeHWND = new(options.Hwnd); InitializeCore(); diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowNonRudeHWND.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/XamlWindowNonRudeHWND.cs similarity index 85% rename from src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowNonRudeHWND.cs rename to src/Snap.Hutao/Snap.Hutao/Core/Windowing/XamlWindowNonRudeHWND.cs index 4d0d9a7f..6c993d32 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowNonRudeHWND.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/XamlWindowNonRudeHWND.cs @@ -6,13 +6,13 @@ using static Snap.Hutao.Win32.User32; namespace Snap.Hutao.Core.Windowing; -internal sealed class WindowNonRudeHWND : IDisposable +internal sealed class XamlWindowNonRudeHWND : IDisposable { // https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-itaskbarlist2-markfullscreenwindow#remarks private const string NonRudeHWND = "NonRudeHWND"; private readonly HWND hwnd; - public WindowNonRudeHWND(HWND hwnd) + public XamlWindowNonRudeHWND(HWND hwnd) { this.hwnd = hwnd; SetPropW(hwnd, NonRudeHWND, BOOL.TRUE); diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowOptions.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/XamlWindowOptions.cs similarity index 92% rename from src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowOptions.cs rename to src/Snap.Hutao/Snap.Hutao/Core/Windowing/XamlWindowOptions.cs index fa110de4..acd2e78b 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowOptions.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/XamlWindowOptions.cs @@ -13,7 +13,7 @@ namespace Snap.Hutao.Core.Windowing; /// /// Window 选项 /// -internal readonly struct WindowOptions +internal readonly struct XamlWindowOptions { /// /// 窗体句柄 @@ -43,7 +43,7 @@ internal readonly struct WindowOptions public readonly string? PersistRectKey; - public WindowOptions(Window window, FrameworkElement titleBar, SizeInt32 initSize, string? persistSize = default) + public XamlWindowOptions(Window window, FrameworkElement titleBar, SizeInt32 initSize, string? persistSize = default) { Hwnd = WindowNative.GetWindowHandle(window); InputNonClientPointerSource = InputNonClientPointerSource.GetForWindowId(window.AppWindow.Id); diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowSubclass.cs b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/XamlWindowSubclass.cs similarity index 69% rename from src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowSubclass.cs rename to src/Snap.Hutao/Snap.Hutao/Core/Windowing/XamlWindowSubclass.cs index bdad8855..e5d8b9bf 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Windowing/WindowSubclass.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Windowing/XamlWindowSubclass.cs @@ -3,7 +3,6 @@ using Microsoft.UI.Xaml; using Snap.Hutao.Core.Windowing.Backdrop; -using Snap.Hutao.Core.Windowing.HotKey; using Snap.Hutao.Win32; using Snap.Hutao.Win32.Foundation; using Snap.Hutao.Win32.UI.Shell; @@ -15,51 +14,35 @@ using static Snap.Hutao.Win32.ConstValues; namespace Snap.Hutao.Core.Windowing; -/// -/// 窗体子类管理器 -/// [HighQuality] -internal sealed class WindowSubclass : IDisposable +internal sealed class XamlWindowSubclass : IDisposable { private const int WindowSubclassId = 101; private readonly Window window; - private readonly WindowOptions options; - private readonly IServiceProvider serviceProvider; - private readonly IHotKeyController hotKeyController; + private readonly XamlWindowOptions options; // We have to explicitly hold a reference to SUBCLASSPROC private SUBCLASSPROC windowProc = default!; - private UnmanagedAccess unmanagedAccess = default!; + private UnmanagedAccess unmanagedAccess = default!; - public WindowSubclass(Window window, in WindowOptions options, IServiceProvider serviceProvider) + public XamlWindowSubclass(Window window, in XamlWindowOptions options) { this.window = window; this.options = options; - this.serviceProvider = serviceProvider; - - hotKeyController = serviceProvider.GetRequiredService(); } - /// - /// 尝试设置窗体子类 - /// - /// 是否设置成功 public unsafe bool Initialize() { windowProc = SUBCLASSPROC.Create(&OnSubclassProcedure); unmanagedAccess = UnmanagedAccess.Create(this); bool windowHooked = SetWindowSubclass(options.Hwnd, windowProc, WindowSubclassId, unmanagedAccess); - hotKeyController.RegisterAll(); return windowHooked; } - /// public void Dispose() { - hotKeyController.UnregisterAll(); - RemoveWindowSubclass(options.Hwnd, windowProc, WindowSubclassId); windowProc = default!; unmanagedAccess.Dispose(); @@ -69,7 +52,7 @@ internal sealed class WindowSubclass : IDisposable [UnmanagedCallersOnly(CallConvs = [typeof(CallConvStdcall)])] private static unsafe LRESULT OnSubclassProcedure(HWND hwnd, uint uMsg, WPARAM wParam, LPARAM lParam, nuint uIdSubclass, nuint dwRefData) { - WindowSubclass? state = UnmanagedAccess.Get(dwRefData); + XamlWindowSubclass? state = UnmanagedAccess.Get(dwRefData); ArgumentNullException.ThrowIfNull(state); switch (uMsg) @@ -90,12 +73,6 @@ internal sealed class WindowSubclass : IDisposable return default; } - case WM_HOTKEY: - { - state.hotKeyController.OnHotKeyPressed(*(HotKeyParameter*)&lParam); - break; - } - case WM_ERASEBKGND: { if (state.window.SystemBackdrop is IBackdropNeedEraseBackground) diff --git a/src/Snap.Hutao/Snap.Hutao/GuideWindow.xaml.cs b/src/Snap.Hutao/Snap.Hutao/GuideWindow.xaml.cs index edc52cdf..0fa446c5 100644 --- a/src/Snap.Hutao/Snap.Hutao/GuideWindow.xaml.cs +++ b/src/Snap.Hutao/Snap.Hutao/GuideWindow.xaml.cs @@ -12,7 +12,7 @@ namespace Snap.Hutao; /// 指引窗口 /// [Injection(InjectAs.Singleton)] -internal sealed partial class GuideWindow : Window, IWindowOptionsSource, IMinMaxInfoHandler +internal sealed partial class GuideWindow : Window, IXamlWindowOptionsSource, IMinMaxInfoHandler { private const int MinWidth = 1000; private const int MinHeight = 650; @@ -20,7 +20,7 @@ internal sealed partial class GuideWindow : Window, IWindowOptionsSource, IMinMa private const int MaxWidth = 1200; private const int MaxHeight = 800; - private readonly WindowOptions windowOptions; + private readonly XamlWindowOptions windowOptions; public GuideWindow(IServiceProvider serviceProvider) { @@ -29,7 +29,7 @@ internal sealed partial class GuideWindow : Window, IWindowOptionsSource, IMinMa this.InitializeController(serviceProvider); } - WindowOptions IWindowOptionsSource.WindowOptions { get => windowOptions; } + XamlWindowOptions IXamlWindowOptionsSource.WindowOptions { get => windowOptions; } public unsafe void HandleMinMaxInfo(ref MINMAXINFO info, double scalingFactor) { diff --git a/src/Snap.Hutao/Snap.Hutao/LaunchGameWindow.xaml.cs b/src/Snap.Hutao/Snap.Hutao/LaunchGameWindow.xaml.cs index e69d63a3..b1df5857 100644 --- a/src/Snap.Hutao/Snap.Hutao/LaunchGameWindow.xaml.cs +++ b/src/Snap.Hutao/Snap.Hutao/LaunchGameWindow.xaml.cs @@ -15,7 +15,7 @@ namespace Snap.Hutao; /// [HighQuality] [Injection(InjectAs.Singleton)] -internal sealed partial class LaunchGameWindow : Window, IDisposable, IWindowOptionsSource, IMinMaxInfoHandler +internal sealed partial class LaunchGameWindow : Window, IDisposable, IXamlWindowOptionsSource, IMinMaxInfoHandler { private const int MinWidth = 240; private const int MinHeight = 240; @@ -23,7 +23,7 @@ internal sealed partial class LaunchGameWindow : Window, IDisposable, IWindowOpt private const int MaxWidth = 320; private const int MaxHeight = 320; - private readonly WindowOptions windowOptions; + private readonly XamlWindowOptions windowOptions; private readonly IServiceScope scope; /// @@ -41,7 +41,7 @@ internal sealed partial class LaunchGameWindow : Window, IDisposable, IWindowOpt } /// - public WindowOptions WindowOptions { get => windowOptions; } + public XamlWindowOptions WindowOptions { get => windowOptions; } /// public void Dispose() diff --git a/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml.cs b/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml.cs index 6bc104fe..c0b0ccef 100644 --- a/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml.cs +++ b/src/Snap.Hutao/Snap.Hutao/MainWindow.xaml.cs @@ -14,12 +14,12 @@ namespace Snap.Hutao; [HighQuality] [Injection(InjectAs.Singleton)] [SuppressMessage("", "CA1001")] -internal sealed partial class MainWindow : Window, IWindowOptionsSource, IMinMaxInfoHandler +internal sealed partial class MainWindow : Window, IXamlWindowOptionsSource, IMinMaxInfoHandler { private const int MinWidth = 1000; private const int MinHeight = 600; - private readonly WindowOptions windowOptions; + private readonly XamlWindowOptions windowOptions; /// /// 构造一个新的主窗体 @@ -33,7 +33,7 @@ internal sealed partial class MainWindow : Window, IWindowOptionsSource, IMinMax } /// - public WindowOptions WindowOptions { get => windowOptions; } + public XamlWindowOptions WindowOptions { get => windowOptions; } /// public unsafe void HandleMinMaxInfo(ref MINMAXINFO pInfo, double scalingFactor) diff --git a/src/Snap.Hutao/Snap.Hutao/Program.cs b/src/Snap.Hutao/Snap.Hutao/Program.cs index 6242f63b..5b842457 100644 --- a/src/Snap.Hutao/Snap.Hutao/Program.cs +++ b/src/Snap.Hutao/Snap.Hutao/Program.cs @@ -50,12 +50,9 @@ public static partial class Program // By adding the using statement, we can dispose the injected services when we closing using (ServiceProvider serviceProvider = DependencyInjection.Initialize()) { - using (NotifyIconController notifyIconController = new()) - { - // In a Desktop app this runs a message pump internally, - // and does not return until the application shuts down. - Application.Start(AppInitializationCallback); - } + // In a Desktop app this runs a message pump internally, + // and does not return until the application shuts down. + Application.Start(AppInitializationCallback); } }