From 7e0a118bae932e266b58314a7ca2688e8091999b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=97=AD?= Date: Wed, 16 Oct 2024 14:11:42 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BD=BF=E6=B6=88=E6=81=AF=E7=AA=97?= =?UTF-8?q?=E5=8F=A3=E6=98=BE=E7=A4=BA=E5=9C=A8=E6=BA=90=E7=AA=97=E5=8F=A3?= =?UTF-8?q?=E4=B8=8A=E6=96=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Magpie.App/ScalingService.cpp | 18 +++++++++----- src/Magpie.App/ScalingService.h | 3 ++- src/Magpie.App/ToastPage.cpp | 2 +- src/Magpie.App/ToastPage.h | 2 +- src/Magpie.App/ToastPage.idl | 2 +- src/Magpie.App/ToastService.cpp | 40 +++++++++++++++++++++++++------ src/Magpie.App/ToastService.h | 7 ++++-- src/Shared/ScalingError.h | 21 ++++++++++++++++ src/Shared/Shared.vcxitems | 1 + 9 files changed, 77 insertions(+), 19 deletions(-) create mode 100644 src/Shared/ScalingError.h diff --git a/src/Magpie.App/ScalingService.cpp b/src/Magpie.App/ScalingService.cpp index 671747d14..13f7ab650 100644 --- a/src/Magpie.App/ScalingService.cpp +++ b/src/Magpie.App/ScalingService.cpp @@ -10,6 +10,7 @@ #include "EffectsService.h" #include #include "TouchHelper.h" +#include "ToastService.h" using namespace ::Magpie::Core; using namespace winrt; @@ -223,20 +224,26 @@ fire_and_forget ScalingService::_ScalingRuntime_IsRunningChanged(bool isRunning) IsRunningChanged.Invoke(isRunning); } -bool ScalingService::_StartScale(HWND hWnd, const Profile& profile) { +static void ShowError(HWND hWnd, ScalingError error) noexcept { + ToastService::Get().ShowMessageOnWindow(L"缩放模式无效", hWnd); + Logger::Get().Error(fmt::format("缩放失败\n\t错误码: {}", (int)error)); +} + +void ScalingService::_StartScale(HWND hWnd, const Profile& profile) { if (profile.scalingMode < 0) { - return false; + ShowError(hWnd, ScalingError::InvalidScalingMode); + return; } ScalingOptions options; options.effects = ScalingModesService::Get().GetScalingMode(profile.scalingMode).effects; if (options.effects.empty()) { - return false; + return; } else { for (EffectOption& effect : options.effects) { if (!EffectsService::Get().GetEffect(effect.name)) { // 存在无法解析的效果 - return false; + return; } } } @@ -245,7 +252,7 @@ bool ScalingService::_StartScale(HWND hWnd, const Profile& profile) { bool isTouchSupportEnabled; if (!TouchHelper::TryLaunchTouchHelper(isTouchSupportEnabled)) { Logger::Get().Error("TryLaunchTouchHelper 失败"); - return false; + return; } options.graphicsCard = profile.graphicsCard; @@ -316,7 +323,6 @@ bool ScalingService::_StartScale(HWND hWnd, const Profile& profile) { _isAutoScaling = profile.isAutoScale; _scalingRuntime->Start(hWnd, std::move(options)); _hwndCurSrc = hWnd; - return true; } void ScalingService::_ScaleForegroundWindow() { diff --git a/src/Magpie.App/ScalingService.h b/src/Magpie.App/ScalingService.h index ec8a0864f..80bbcd07a 100644 --- a/src/Magpie.App/ScalingService.h +++ b/src/Magpie.App/ScalingService.h @@ -1,6 +1,7 @@ #pragma once #include #include "WinRTUtils.h" +#include "ScalingError.h" namespace Magpie::Core { class ScalingRuntime; @@ -69,7 +70,7 @@ class ScalingService { fire_and_forget _ScalingRuntime_IsRunningChanged(bool isRunning); - bool _StartScale(HWND hWnd, const Profile& profile); + void _StartScale(HWND hWnd, const Profile& profile); void _ScaleForegroundWindow(); diff --git a/src/Magpie.App/ToastPage.cpp b/src/Magpie.App/ToastPage.cpp index 54e2a19ed..9521ab66b 100644 --- a/src/Magpie.App/ToastPage.cpp +++ b/src/Magpie.App/ToastPage.cpp @@ -9,7 +9,7 @@ using namespace Windows::UI::Xaml::Controls; namespace winrt::Magpie::App::implementation { -IAsyncAction ToastPage::ShowMessage(const hstring& message) { +fire_and_forget ToastPage::ShowMessage(const hstring& message) { // !!! HACK !!! // 重用 TeachingTip 有一个 bug: 前一个 Toast 正在消失时新的 Toast 不会显示。为了 // 规避它,我们每次都创建新的 TeachingTip,但要保留旧对象的引用,因为播放动画时销毁 diff --git a/src/Magpie.App/ToastPage.h b/src/Magpie.App/ToastPage.h index b667e1eaa..42ec83b09 100644 --- a/src/Magpie.App/ToastPage.h +++ b/src/Magpie.App/ToastPage.h @@ -4,7 +4,7 @@ namespace winrt::Magpie::App::implementation { struct ToastPage : ToastPageT { - IAsyncAction ShowMessage(const hstring& message); + fire_and_forget ShowMessage(const hstring& message); }; } diff --git a/src/Magpie.App/ToastPage.idl b/src/Magpie.App/ToastPage.idl index 6b131b5f4..e0b3a86e7 100644 --- a/src/Magpie.App/ToastPage.idl +++ b/src/Magpie.App/ToastPage.idl @@ -2,7 +2,7 @@ namespace Magpie.App { runtimeclass ToastPage : Windows.UI.Xaml.Controls.Page { ToastPage(); - Windows.Foundation.IAsyncAction ShowMessage(String message); + void ShowMessage(String message); // https://github.com/microsoft/microsoft-ui-xaml/issues/7579 void UnloadObject(Windows.UI.Xaml.DependencyObject object); diff --git a/src/Magpie.App/ToastService.cpp b/src/Magpie.App/ToastService.cpp index 6629aa069..e1fe5bc42 100644 --- a/src/Magpie.App/ToastService.cpp +++ b/src/Magpie.App/ToastService.cpp @@ -4,6 +4,7 @@ #include "Utils.h" #include #include +#include "Win32Utils.h" using namespace winrt; using namespace Windows::UI::Xaml::Controls; @@ -36,9 +37,34 @@ void ToastService::Uninitialize() noexcept { _toastThread.join(); } -void ToastService::ShowMessage(std::wstring_view message) noexcept { - _Dispatcher().TryRunAsync(CoreDispatcherPriority::Normal, [this, captured(std::wstring(message))]() { - _toastPage.ShowMessage(captured); +void ToastService::ShowMessageOnWindow(std::wstring_view message, HWND hWnd) noexcept { + _Dispatcher().TryRunAsync(CoreDispatcherPriority::Normal, [this, capturedMessage(std::wstring(message)), hWnd]() { + RECT frameRect; + if (!Win32Utils::GetWindowFrameRect(hWnd, frameRect)) { + return; + } + + // 更改所有者关系使弹窗始终在 hWnd 上方 + SetWindowLongPtr(_hwndToast, GWLP_HWNDPARENT, (LONG_PTR)hWnd); + // _hwndToast 的输入已被附加到了 hWnd 上,这是所有者窗口的默认行为,但我们不需要。 + // 见 https://devblogs.microsoft.com/oldnewthing/20130412-00/?p=4683 + AttachThreadInput( + GetCurrentThreadId(), + GetWindowThreadProcessId(hWnd, nullptr), + FALSE + ); + + SetWindowPos( + _hwndToast, + NULL, + (frameRect.left + frameRect.right) / 2, + (frameRect.top + frameRect.bottom * 4) / 5, + 0, + 0, + SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOREDRAW + ); + + _toastPage.ShowMessage(capturedMessage); }); } @@ -63,8 +89,8 @@ void ToastService::_ToastThreadProc() noexcept { // 创建窗口失败也应进入消息循环。Win10 中关闭任意线程的 DesktopWindowXamlSource 都会使主线程会崩溃, // 在程序退出前,xamlSource 不能析构。见 https://github.com/microsoft/terminal/pull/15397 - HWND hwndToast = CreateWindowEx( - WS_EX_TOPMOST | WS_EX_NOREDIRECTIONBITMAP | WS_EX_TOOLWINDOW, + _hwndToast = CreateWindowEx( + WS_EX_NOREDIRECTIONBITMAP | WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT, CommonSharedConstants::TOAST_WINDOW_CLASS_NAME, L"Toast", WS_POPUP | WS_VISIBLE, @@ -80,7 +106,7 @@ void ToastService::_ToastThreadProc() noexcept { com_ptr xamlSourceNative2 = xamlSource.try_as(); - xamlSourceNative2->AttachToWindow(hwndToast); + xamlSourceNative2->AttachToWindow(_hwndToast); HWND hwndXamlIsland; xamlSourceNative2->get_WindowHandle(&hwndXamlIsland); @@ -97,7 +123,7 @@ void ToastService::_ToastThreadProc() noexcept { MSG msg; while (GetMessage(&msg, nullptr, 0, 0)) { if (msg.message == CommonSharedConstants::WM_TOAST_QUIT) { - DestroyWindow(hwndToast); + DestroyWindow(_hwndToast); break; } diff --git a/src/Magpie.App/ToastService.h b/src/Magpie.App/ToastService.h index f1c7ea430..ee9d1a72a 100644 --- a/src/Magpie.App/ToastService.h +++ b/src/Magpie.App/ToastService.h @@ -17,7 +17,7 @@ class ToastService { void Uninitialize() noexcept; - void ShowMessage(std::wstring_view message) noexcept; + void ShowMessageOnWindow(std::wstring_view message, HWND hWnd) noexcept; private: ToastService() = default; @@ -29,11 +29,14 @@ class ToastService { std::thread _toastThread; - ToastPage _toastPage{ nullptr }; CoreDispatcher _dispatcher{ nullptr }; std::atomic _dispatcherInitialized = false; // 只能在主线程访问,省下检查 _dispatcherInitialized 的开销 bool _dispatcherInitializedCache = false; + + // 只能在 toast 线程访问 + ToastPage _toastPage{ nullptr }; + HWND _hwndToast = NULL; }; } diff --git a/src/Shared/ScalingError.h b/src/Shared/ScalingError.h new file mode 100644 index 000000000..43763b89a --- /dev/null +++ b/src/Shared/ScalingError.h @@ -0,0 +1,21 @@ +#pragma once + +enum class ScalingError { + ///////////////////////////////////// + // + // 先决条件错误 + // + ///////////////////////////////////// + + // 未配置缩放模式或者缩放模式不合法 + InvalidScalingMode, + + ///////////////////////////////////// + // + // 初始化错误 + // + ///////////////////////////////////// + + // 检测到其他缩放窗口。由于不支持多实例,这个错误不应该出现 + AlreadyScaling +}; diff --git a/src/Shared/Shared.vcxitems b/src/Shared/Shared.vcxitems index 2e7f28bd2..a4a930f4b 100644 --- a/src/Shared/Shared.vcxitems +++ b/src/Shared/Shared.vcxitems @@ -18,6 +18,7 @@ +