mirror of
https://github.com/Blinue/Magpie.git
synced 2026-06-24 02:04:10 +00:00
优化自动缩放机制 (#1227)
* fix: 重复缩放时不显示错误消息 * fix: 减小 ScalingService 和 ScalingRuntime 状态不一致的窗口期 * fix: 避免自动缩放和最小化窗口还原机制冲突 * chore: 修复 clang 编译警告 * refactor: ScalingRuntime::SwitchScalingState 重命名为 ScalingRuntime::ToggleScaling * fix: 恢复检查自动缩放 * feat: 自动缩放可以终止当前缩放 * chore: 添加注释 * feat: 自动缩放不再等待最小化和不可见的窗口 现已支持直接缩放这类窗口,由 ScalingRuntime 等待
This commit is contained in:
parent
96a1c7287a
commit
ea107ac9c3
9 changed files with 90 additions and 90 deletions
|
|
@ -13,7 +13,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Magpie", "src\Magpie\Magpie
|
|||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{00AB63C3-0CD3-4944-B8E6-58C86138618D}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.editorconfig = .editorconfig
|
||||
src\BuildOptions.props = src\BuildOptions.props
|
||||
src\Common.Post.props = src\Common.Post.props
|
||||
src\Common.Pre.props = src\Common.Pre.props
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ bool AdaptivePresenter::_Initialize(HWND hwndAttach) noexcept {
|
|||
};
|
||||
|
||||
ID3D11Device5* d3dDevice = _deviceResources->GetD3DDevice();
|
||||
winrt::com_ptr<IDXGISwapChain1> dxgiSwapChain = nullptr;
|
||||
winrt::com_ptr<IDXGISwapChain1> dxgiSwapChain;
|
||||
HRESULT hr = _deviceResources->GetDXGIFactory()->CreateSwapChainForHwnd(
|
||||
d3dDevice,
|
||||
hwndAttach,
|
||||
|
|
|
|||
|
|
@ -824,7 +824,7 @@ bool OverlayDrawer::_DrawToolbar(uint32_t fps) noexcept {
|
|||
isWindowedMode ? L"Overlay_Toolbar_SwitchToFullscreen" : L"Overlay_Toolbar_SwitchToWindowed");
|
||||
if (drawButton(icon, switchScalingStr.c_str())) {
|
||||
ScalingWindow::Dispatcher().TryEnqueue([]() {
|
||||
ScalingWindow::Get().SwitchScalingState(!ScalingWindow::Get().Options().IsWindowedMode());
|
||||
ScalingWindow::Get().ToggleScaling(!ScalingWindow::Get().Options().IsWindowedMode());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,8 +14,6 @@ ScalingRuntime::ScalingRuntime() : _scalingThread(&ScalingRuntime::_ScalingThrea
|
|||
}
|
||||
|
||||
ScalingRuntime::~ScalingRuntime() {
|
||||
Stop();
|
||||
|
||||
if (_scalingThread.joinable()) {
|
||||
const HANDLE hScalingThread = _scalingThread.native_handle();
|
||||
|
||||
|
|
@ -50,27 +48,30 @@ ScalingRuntime::~ScalingRuntime() {
|
|||
}
|
||||
}
|
||||
|
||||
bool ScalingRuntime::Start(HWND hwndSrc, ScalingOptions&& options) {
|
||||
bool ScalingRuntime::Start(HWND hwndSrc, ScalingOptions&& options, bool force) {
|
||||
assert(!options.screenshotsDir.empty() && options.showToast && options.showError && options.save);
|
||||
|
||||
_Dispatcher().TryEnqueue([this, hwndSrc, options(std::move(options))]() mutable {
|
||||
_Dispatcher().TryEnqueue([this, hwndSrc, options(std::move(options)), force]() mutable {
|
||||
ScalingWindow& scalingWindow = ScalingWindow::Get();
|
||||
if (scalingWindow.IsSrcRepositioning()) {
|
||||
scalingWindow.CleanAfterSrcRepositioned();
|
||||
// 如果正在缩放且 force 为假则忽略
|
||||
if (scalingWindow && !force) {
|
||||
return;
|
||||
}
|
||||
|
||||
scalingWindow.Stop();
|
||||
|
||||
// 初始化时视为处于缩放状态
|
||||
_IsScaling(true);
|
||||
_State(ScalingState::Scaling);
|
||||
scalingWindow.Start(hwndSrc, std::move(options));
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScalingRuntime::SwitchScalingState(bool isWindowedMode) {
|
||||
void ScalingRuntime::ToggleScaling(bool isWindowedMode) {
|
||||
_Dispatcher().TryEnqueue([isWindowedMode]() {
|
||||
if (ScalingWindow& scalingWindow = ScalingWindow::Get()) {
|
||||
scalingWindow.SwitchScalingState(isWindowedMode);
|
||||
scalingWindow.ToggleScaling(isWindowedMode);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
|
@ -161,7 +162,6 @@ void ScalingRuntime::_ScalingThreadProc() noexcept {
|
|||
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
|
||||
if (msg.message == WM_QUIT) {
|
||||
scalingWindow.Stop();
|
||||
_IsScaling(false);
|
||||
return;
|
||||
} else if (msg.message == CommonSharedConstants::WM_FRONTEND_RENDER &&
|
||||
msg.hwnd == scalingWindow.Handle()) {
|
||||
|
|
@ -172,9 +172,9 @@ void ScalingRuntime::_ScalingThreadProc() noexcept {
|
|||
DispatchMessage(&msg);
|
||||
}
|
||||
|
||||
_IsScaling(scalingWindow);
|
||||
|
||||
if (scalingWindow) {
|
||||
_State(ScalingState::Scaling);
|
||||
|
||||
const auto now = steady_clock::now();
|
||||
// 限制检测光标移动的频率
|
||||
const milliseconds timeout(scalingWindow.Options().Is3DGameMode() ? 8 : 2);
|
||||
|
|
@ -196,16 +196,20 @@ void ScalingRuntime::_ScalingThreadProc() noexcept {
|
|||
if (repositioning.has_value()) {
|
||||
if (*repositioning) {
|
||||
// 等待调整完成
|
||||
_State(ScalingState::Waiting);
|
||||
MsgWaitForMultipleObjectsEx(0, nullptr, 10, QS_ALLINPUT, MWMO_INPUTAVAILABLE);
|
||||
} else {
|
||||
// 重新缩放
|
||||
// 重新缩放。初始化时视为处于缩放状态
|
||||
_State(ScalingState::Scaling);
|
||||
ScalingWindow::Get().RestartAfterSrcRepositioned();
|
||||
}
|
||||
} else {
|
||||
// 取消缩放
|
||||
ScalingWindow::Get().CleanAfterSrcRepositioned();
|
||||
_State(ScalingState::Idle);
|
||||
}
|
||||
} else {
|
||||
_State(ScalingState::Idle);
|
||||
lastRenderTime = {};
|
||||
WaitMessage();
|
||||
}
|
||||
|
|
@ -221,9 +225,9 @@ const winrt::DispatcherQueue& ScalingRuntime::_Dispatcher() noexcept {
|
|||
return _dispatcher;
|
||||
}
|
||||
|
||||
void ScalingRuntime::_IsScaling(bool value) {
|
||||
if (_isScaling.exchange(value, std::memory_order_relaxed) != value) {
|
||||
IsScalingChanged.Invoke(value);
|
||||
void ScalingRuntime::_State(ScalingState value) {
|
||||
if (_state.exchange(value, std::memory_order_relaxed) != value) {
|
||||
StateChanged.Invoke(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -322,10 +322,7 @@ ScalingError ScalingWindow::_StartImpl(HWND hwndSrc) noexcept {
|
|||
}
|
||||
|
||||
void ScalingWindow::Start(HWND hwndSrc, ScalingOptions&& options) noexcept {
|
||||
if (Handle()) {
|
||||
options.showError(hwndSrc, ScalingError::ScalingFailedGeneral);
|
||||
return;
|
||||
}
|
||||
assert(!Handle());
|
||||
|
||||
options.Log();
|
||||
// 缩放结束后失效
|
||||
|
|
@ -345,7 +342,7 @@ void ScalingWindow::Stop() noexcept {
|
|||
CleanAfterSrcRepositioned();
|
||||
}
|
||||
|
||||
void ScalingWindow::SwitchScalingState(bool isWindowedMode) noexcept {
|
||||
void ScalingWindow::ToggleScaling(bool isWindowedMode) noexcept {
|
||||
assert(Handle());
|
||||
|
||||
if (_options.IsWindowedMode() == isWindowedMode || !_srcTracker.IsFocused()) {
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ public:
|
|||
|
||||
void Stop() noexcept;
|
||||
|
||||
void SwitchScalingState(bool isWindowedMode) noexcept;
|
||||
void ToggleScaling(bool isWindowedMode) noexcept;
|
||||
|
||||
void SwitchToolbarState() noexcept;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,25 +3,31 @@
|
|||
|
||||
namespace Magpie {
|
||||
|
||||
enum class ScalingState {
|
||||
Idle,
|
||||
Scaling,
|
||||
Waiting
|
||||
};
|
||||
|
||||
class ScalingRuntime {
|
||||
public:
|
||||
ScalingRuntime();
|
||||
~ScalingRuntime();
|
||||
|
||||
bool Start(HWND hwndSrc, struct ScalingOptions&& options);
|
||||
bool Start(HWND hwndSrc, struct ScalingOptions&& options, bool force);
|
||||
|
||||
void SwitchScalingState(bool isWindowedMode);
|
||||
void ToggleScaling(bool isWindowedMode);
|
||||
|
||||
void SwitchToolbarState();
|
||||
|
||||
void Stop();
|
||||
|
||||
bool IsScaling() const noexcept {
|
||||
return _isScaling.load(std::memory_order_relaxed);
|
||||
ScalingState State() const noexcept {
|
||||
return _state.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
// 调用者应处理线程同步
|
||||
MultithreadEvent<bool> IsScalingChanged;
|
||||
MultithreadEvent<ScalingState> StateChanged;
|
||||
|
||||
private:
|
||||
void _ScalingThreadProc() noexcept;
|
||||
|
|
@ -29,7 +35,7 @@ private:
|
|||
// 确保 _dispatcher 完成初始化
|
||||
const winrt::DispatcherQueue& _Dispatcher() noexcept;
|
||||
|
||||
void _IsScaling(bool value);
|
||||
void _State(ScalingState value);
|
||||
|
||||
std::thread _scalingThread;
|
||||
|
||||
|
|
@ -38,7 +44,7 @@ private:
|
|||
// 只能在主线程访问,省下检查 _dispatcherInitialized 的开销
|
||||
bool _dispatcherInitializedCache = false;
|
||||
|
||||
std::atomic<bool> _isScaling = false;
|
||||
std::atomic<ScalingState> _state = ScalingState::Idle;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
#include "ProfileService.h"
|
||||
#include "ScalingMode.h"
|
||||
#include "ScalingModesService.h"
|
||||
#include "ScalingRuntime.h"
|
||||
#include "ScalingService.h"
|
||||
#include "ShortcutService.h"
|
||||
#include "ToastService.h"
|
||||
|
|
@ -31,9 +30,9 @@ ScalingService& ScalingService::Get() noexcept {
|
|||
ScalingService::~ScalingService() {}
|
||||
|
||||
void ScalingService::Initialize() {
|
||||
_scalingRuntime = std::make_unique<ScalingRuntime>();
|
||||
_scalingRuntime->IsScalingChanged(
|
||||
std::bind_front(&ScalingService::_ScalingRuntime_IsScalingChanged, this));
|
||||
_scalingRuntime.emplace();
|
||||
_scalingRuntime->StateChanged(
|
||||
std::bind_front(&ScalingService::_ScalingRuntime_StateChanged, this));
|
||||
|
||||
_countDownTimer.Interval(25ms);
|
||||
_countDownTimer.Tick({ this, &ScalingService::_CountDownTimer_Tick });
|
||||
|
|
@ -51,11 +50,14 @@ void ScalingService::Initialize() {
|
|||
}
|
||||
|
||||
void ScalingService::Uninitialize() {
|
||||
if (!_checkForegroundTimer) {
|
||||
if (!_scalingRuntime) {
|
||||
return;
|
||||
}
|
||||
|
||||
_checkForegroundTimer.Cancel();
|
||||
if (_checkForegroundTimer) {
|
||||
_checkForegroundTimer.Cancel();
|
||||
}
|
||||
|
||||
_countDownTimer.Stop();
|
||||
_scalingRuntime.reset();
|
||||
|
||||
|
|
@ -97,7 +99,8 @@ double ScalingService::SecondsLeft() const noexcept {
|
|||
}
|
||||
|
||||
bool ScalingService::IsScaling() const noexcept {
|
||||
return _scalingRuntime->IsScaling();
|
||||
// 等待状态视为未缩放
|
||||
return _scalingRuntime->State() == ScalingState::Scaling;
|
||||
}
|
||||
|
||||
void ScalingService::CheckForeground() {
|
||||
|
|
@ -112,8 +115,8 @@ void ScalingService::_ShortcutService_ShortcutPressed(ShortcutAction action) {
|
|||
{
|
||||
const bool isWindowdMode = action == ShortcutAction::WindowedModeScale;
|
||||
|
||||
if (_scalingRuntime->IsScaling()) {
|
||||
_scalingRuntime->SwitchScalingState(isWindowdMode);
|
||||
if (_scalingRuntime->State() == ScalingState::Scaling) {
|
||||
_scalingRuntime->ToggleScaling(isWindowdMode);
|
||||
} else {
|
||||
_ScaleForegroundWindow(isWindowdMode);
|
||||
}
|
||||
|
|
@ -122,10 +125,7 @@ void ScalingService::_ShortcutService_ShortcutPressed(ShortcutAction action) {
|
|||
}
|
||||
case ShortcutAction::Toolbar:
|
||||
{
|
||||
if (_scalingRuntime->IsScaling()) {
|
||||
_scalingRuntime->SwitchToolbarState();
|
||||
return;
|
||||
}
|
||||
_scalingRuntime->SwitchToolbarState();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
|
@ -211,17 +211,6 @@ static void ShowError(HWND hWnd, ScalingError error) noexcept {
|
|||
}
|
||||
|
||||
static bool IsReadyForScaling(HWND hwndFore) noexcept {
|
||||
// GH#538
|
||||
// 窗口还原过程中存在中间状态:虽然已经成为前台窗口,但仍是最小化的
|
||||
if (Win32Helper::GetWindowShowCmd(hwndFore) == SW_SHOWMINIMIZED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// OS 允许不可见的窗口成为前台窗口,应等待窗口显示
|
||||
if (!IsWindowVisible(hwndFore)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// GH#1148
|
||||
// 有些游戏刚启动时将窗口创建在屏幕外,初始化完成后再移到屏幕内
|
||||
if (!MonitorFromWindow(hwndFore, MONITOR_DEFAULTTONULL)) {
|
||||
|
|
@ -234,52 +223,56 @@ static bool IsReadyForScaling(HWND hwndFore) noexcept {
|
|||
}
|
||||
|
||||
fire_and_forget ScalingService::_CheckForegroundTimer_Tick(ThreadPoolTimer const& timer) {
|
||||
// ThreadPoolTimer 是异步的,Uninitialize 后仍可能执行
|
||||
if (!_scalingRuntime || _scalingRuntime->IsScaling()) {
|
||||
co_return;
|
||||
}
|
||||
|
||||
if (timer) {
|
||||
// ThreadPoolTimer 在后台线程触发
|
||||
co_await App::Get().Dispatcher();
|
||||
}
|
||||
|
||||
// ThreadPoolTimer 是异步的,Uninitialize 后仍可能执行
|
||||
if (!_scalingRuntime) {
|
||||
co_return;
|
||||
}
|
||||
|
||||
const HWND hwndFore = GetForegroundWindow();
|
||||
if (!hwndFore || hwndFore == _hwndChecked) {
|
||||
co_return;
|
||||
}
|
||||
|
||||
// 检查自动缩放
|
||||
if (const Profile* profile = ProfileService::Get().GetProfileForWindow(hwndFore, true)) {
|
||||
// 如果窗口处于某种中间状态则跳过此次检查
|
||||
if (!IsReadyForScaling(hwndFore)) {
|
||||
co_return;
|
||||
}
|
||||
// 检查 _hwndCurSrc 使得缩放或等待状态下避免再次缩放源窗口
|
||||
if (hwndFore != _hwndCurSrc) {
|
||||
// 检查自动缩放
|
||||
if (const Profile* profile = ProfileService::Get().GetProfileForWindow(hwndFore, true)) {
|
||||
// 如果窗口处于某种中间状态则跳过此次检查
|
||||
if (!IsReadyForScaling(hwndFore)) {
|
||||
co_return;
|
||||
}
|
||||
|
||||
_StartScale(hwndFore, *profile, profile->autoScale == AutoScale::Windowed);
|
||||
// 自动缩放可以终止当前缩放
|
||||
_StartScale(hwndFore, *profile, profile->autoScale == AutoScale::Windowed, true);
|
||||
}
|
||||
}
|
||||
|
||||
// 避免重复检查
|
||||
_hwndChecked = hwndFore;
|
||||
}
|
||||
|
||||
void ScalingService::_ScalingRuntime_IsScalingChanged(bool value) {
|
||||
void ScalingService::_ScalingRuntime_StateChanged(ScalingState value) {
|
||||
App::Get().Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [this, value]() {
|
||||
if (value) {
|
||||
if (value == ScalingState::Scaling) {
|
||||
StopTimer();
|
||||
} else {
|
||||
} else if (value == ScalingState::Idle) {
|
||||
// 缩放结束后源窗口位于前台则不要检查自动缩放,用户可能刚通过快捷键或
|
||||
// 工具栏终止缩放。_CheckForegroundTimer_Tick 也实现了类似功能,但它
|
||||
// 的触发频率较低,容易错过时机。
|
||||
if (GetForegroundWindow() == _hwndCurSrc) {
|
||||
// 退出全屏后如果前台窗口不变视为通过热键退出
|
||||
_hwndChecked = _hwndCurSrc;
|
||||
}
|
||||
|
||||
// 缩放结束后清空 _hwndCurSrc,等待状态下则保留
|
||||
_hwndCurSrc = NULL;
|
||||
|
||||
// 立即检查前台窗口
|
||||
_CheckForegroundTimer_Tick(nullptr);
|
||||
}
|
||||
|
||||
IsScalingChanged.Invoke(value);
|
||||
IsScalingChanged.Invoke(value == ScalingState::Scaling);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -290,25 +283,25 @@ void ScalingService::_ScaleForegroundWindow(bool windowedMode) {
|
|||
}
|
||||
|
||||
const Profile& profile = *ProfileService::Get().GetProfileForWindow(hWnd, false);
|
||||
_StartScale(hWnd, profile, windowedMode);
|
||||
_StartScale(hWnd, profile, windowedMode, false);
|
||||
}
|
||||
|
||||
void ScalingService::_StartScale(HWND hWnd, const Profile& profile, bool windowedMode) {
|
||||
void ScalingService::_StartScale(HWND hWnd, const Profile& profile, bool windowedMode, bool force) {
|
||||
assert(hWnd);
|
||||
|
||||
if (_scalingRuntime->IsScaling()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ScalingError error = _StartScaleImpl(hWnd, profile, windowedMode);
|
||||
const ScalingError error = _StartScaleImpl(hWnd, profile, windowedMode, force);
|
||||
if (error != ScalingError::NoError) {
|
||||
ShowError(hWnd, error);
|
||||
}
|
||||
}
|
||||
|
||||
ScalingError ScalingService::_StartScaleImpl(HWND hWnd, const Profile& profile, bool windowedMode) {
|
||||
ScalingError ScalingService::_StartScaleImpl(HWND hWnd, const Profile& profile, bool windowedMode, bool force) {
|
||||
// ScalingRuntime::Start 会检查是否正在缩放,这里提前检查以避免无效操作
|
||||
if (!force && _scalingRuntime->State() == ScalingState::Scaling) {
|
||||
return ScalingError::NoError;
|
||||
}
|
||||
|
||||
if (WindowHelper::IsForbiddenSystemWindow(hWnd)) {
|
||||
// 不显示错误
|
||||
return ScalingError::NoError;
|
||||
}
|
||||
|
||||
|
|
@ -473,7 +466,7 @@ ScalingError ScalingService::_StartScaleImpl(HWND hWnd, const Profile& profile,
|
|||
);
|
||||
};
|
||||
|
||||
if (!_scalingRuntime->Start(hWnd, std::move(options))) {
|
||||
if (!_scalingRuntime->Start(hWnd, std::move(options), force)) {
|
||||
return ScalingError::ScalingFailedGeneral;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
#include "Event.h"
|
||||
#include "ScalingRuntime.h"
|
||||
#include <winrt/Magpie.h>
|
||||
#include <winrt/Windows.System.Threading.h>
|
||||
|
||||
|
|
@ -56,15 +57,15 @@ private:
|
|||
|
||||
winrt::fire_and_forget _CheckForegroundTimer_Tick(winrt::Threading::ThreadPoolTimer const& timer);
|
||||
|
||||
void _ScalingRuntime_IsScalingChanged(bool isRunning);
|
||||
void _ScalingRuntime_StateChanged(ScalingState value);
|
||||
|
||||
void _ScaleForegroundWindow(bool windowedMode);
|
||||
|
||||
void _StartScale(HWND hWnd, const Profile& profile, bool windowedMode);
|
||||
void _StartScale(HWND hWnd, const Profile& profile, bool windowedMode, bool force);
|
||||
|
||||
ScalingError _StartScaleImpl(HWND hWnd, const Profile& profile, bool windowedMode);
|
||||
ScalingError _StartScaleImpl(HWND hWnd, const Profile& profile, bool windowedMode, bool force);
|
||||
|
||||
std::unique_ptr<ScalingRuntime> _scalingRuntime;
|
||||
std::optional<ScalingRuntime> _scalingRuntime;
|
||||
|
||||
winrt::DispatcherTimer _countDownTimer;
|
||||
// DispatcherTimer 在不显示主窗口时可能停滞,因此使用 ThreadPoolTimer
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue