mirror of
https://github.com/Blinue/Magpie.git
synced 2026-06-24 02:04:10 +00:00
feat: 源窗口隐藏会等待显示然后恢复缩放
This commit is contained in:
parent
7b71454b5d
commit
96a1c7287a
5 changed files with 87 additions and 69 deletions
|
|
@ -89,38 +89,39 @@ void ScalingRuntime::Stop() {
|
|||
});
|
||||
}
|
||||
|
||||
// 返回值:
|
||||
// -1: 应取消缩放
|
||||
// 0: 仍在调整中
|
||||
// 1: 调整完毕
|
||||
static int GetSrcRepositionState(HWND hwndSrc) noexcept {
|
||||
static std::optional<bool> IsSrcRepositioning(HWND hwndSrc) noexcept {
|
||||
if (!IsWindow(hwndSrc)) {
|
||||
return -1;
|
||||
Logger::Get().Info("源窗口已销毁");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// 窗口不可见或最小化则继续等待。注意 showCmd 不能准确判断窗口可见性,
|
||||
// 应使用 IsWindowVisible。
|
||||
if (!IsWindowVisible(hwndSrc)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Win32Helper::IsWindowHung(hwndSrc)) {
|
||||
return -1;
|
||||
Logger::Get().Info("源窗口已挂起");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const UINT showCmd = Win32Helper::GetWindowShowCmd(hwndSrc);
|
||||
if (showCmd == SW_HIDE) {
|
||||
return -1;
|
||||
} else if (showCmd == SW_SHOWMAXIMIZED) {
|
||||
if (showCmd == SW_SHOWMAXIMIZED) {
|
||||
// 窗口最大化则尝试缩放,失败会显示错误消息
|
||||
return 1;
|
||||
return false;
|
||||
} else if (showCmd == SW_SHOWMINIMIZED) {
|
||||
// 窗口最小化则继续等待
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 检查源窗口是否正在调整大小或移动
|
||||
GUITHREADINFO guiThreadInfo{ .cbSize = sizeof(GUITHREADINFO) };
|
||||
if (!GetGUIThreadInfo(GetWindowThreadProcessId(hwndSrc, nullptr), &guiThreadInfo)) {
|
||||
Logger::Get().Win32Error("GetGUIThreadInfo 失败");
|
||||
return -1;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return (guiThreadInfo.flags & GUI_INMOVESIZE) ? 0 : 1;
|
||||
return bool(guiThreadInfo.flags & GUI_INMOVESIZE);
|
||||
}
|
||||
|
||||
void ScalingRuntime::_ScalingThreadProc() noexcept {
|
||||
|
|
@ -162,7 +163,8 @@ void ScalingRuntime::_ScalingThreadProc() noexcept {
|
|||
scalingWindow.Stop();
|
||||
_IsScaling(false);
|
||||
return;
|
||||
} else if (msg.message == CommonSharedConstants::WM_FRONTEND_RENDER && msg.hwnd == scalingWindow.Handle()) {
|
||||
} else if (msg.message == CommonSharedConstants::WM_FRONTEND_RENDER &&
|
||||
msg.hwnd == scalingWindow.Handle()) {
|
||||
// 缩放窗口收到 WM_FRONTEND_RENDER 将执行渲染
|
||||
lastRenderTime = steady_clock::now();
|
||||
}
|
||||
|
|
@ -189,13 +191,16 @@ void ScalingRuntime::_ScalingThreadProc() noexcept {
|
|||
const DWORD restMs = DWORD((rest.count() + ratio - 1) / ratio);
|
||||
MsgWaitForMultipleObjectsEx(0, nullptr, restMs, QS_ALLINPUT, MWMO_INPUTAVAILABLE);
|
||||
} else if (scalingWindow.IsSrcRepositioning()) {
|
||||
const int state = GetSrcRepositionState(scalingWindow.SrcTracker().Handle());
|
||||
if (state == 0) {
|
||||
// 等待调整完成
|
||||
MsgWaitForMultipleObjectsEx(0, nullptr, 10, QS_ALLINPUT, MWMO_INPUTAVAILABLE);
|
||||
} else if (state == 1) {
|
||||
// 重新缩放
|
||||
ScalingWindow::Get().RestartAfterSrcRepositioned();
|
||||
std::optional<bool> repositioning =
|
||||
IsSrcRepositioning(scalingWindow.SrcTracker().Handle());
|
||||
if (repositioning.has_value()) {
|
||||
if (*repositioning) {
|
||||
// 等待调整完成
|
||||
MsgWaitForMultipleObjectsEx(0, nullptr, 10, QS_ALLINPUT, MWMO_INPUTAVAILABLE);
|
||||
} else {
|
||||
// 重新缩放
|
||||
ScalingWindow::Get().RestartAfterSrcRepositioned();
|
||||
}
|
||||
} else {
|
||||
// 取消缩放
|
||||
ScalingWindow::Get().CleanAfterSrcRepositioned();
|
||||
|
|
|
|||
|
|
@ -86,11 +86,20 @@ ScalingError ScalingWindow::_StartImpl(HWND hwndSrc) noexcept {
|
|||
|
||||
InitMessage();
|
||||
|
||||
if (ScalingError error = _srcTracker.Set(hwndSrc, _options); error != ScalingError::NoError) {
|
||||
bool isSrcInvisibleOrMinimized = false;
|
||||
if (ScalingError error = _srcTracker.Set(hwndSrc, _options, isSrcInvisibleOrMinimized);
|
||||
error != ScalingError::NoError
|
||||
) {
|
||||
Logger::Get().Error("初始化 SrcTracker 失败");
|
||||
return error;
|
||||
}
|
||||
|
||||
if (isSrcInvisibleOrMinimized || (_srcTracker.IsMoving() && !_options.IsWindowedMode())) {
|
||||
// 等待源窗口状态改变
|
||||
_isSrcRepositioning = true;
|
||||
return ScalingError::NoError;
|
||||
}
|
||||
|
||||
if (_srcTracker.IsZoomed()) {
|
||||
if (_options.IsWindowedMode()) {
|
||||
Logger::Get().Info("已最大化的窗口不支持窗口模式缩放");
|
||||
|
|
@ -101,11 +110,6 @@ ScalingError ScalingWindow::_StartImpl(HWND hwndSrc) noexcept {
|
|||
}
|
||||
}
|
||||
|
||||
if (_srcTracker.IsMoving() && !_options.IsWindowedMode()) {
|
||||
_isSrcRepositioning = true;
|
||||
return ScalingError::NoError;
|
||||
}
|
||||
|
||||
[[maybe_unused]] static Ignore _ = []() {
|
||||
WNDCLASSEXW wcex{
|
||||
.cbSize = sizeof(wcex),
|
||||
|
|
@ -1269,24 +1273,24 @@ bool ScalingWindow::_UpdateSrcState(
|
|||
return false;
|
||||
}
|
||||
|
||||
bool srcMinimized = false;
|
||||
bool isSrcInvisibleOrMinimized = false;
|
||||
bool srcRectChanged = false;
|
||||
bool srcSizeChanged = false;
|
||||
bool srcMovingChanged = false;
|
||||
if (!_srcTracker.UpdateState(hwndFore, _options.IsWindowedMode(), _isResizingOrMoving,
|
||||
srcFocusedChanged, srcOwnedWindowFocusedChanged,
|
||||
srcMinimized, srcRectChanged, srcSizeChanged, srcMovingChanged)) {
|
||||
isSrcInvisibleOrMinimized, srcFocusedChanged, srcOwnedWindowFocusedChanged,
|
||||
srcRectChanged, srcSizeChanged, srcMovingChanged)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (srcMinimized || srcSizeChanged || (!_options.IsWindowedMode() && srcRectChanged)) {
|
||||
if (isSrcInvisibleOrMinimized || srcSizeChanged || (!_options.IsWindowedMode() && srcRectChanged)) {
|
||||
// 不要立刻设置 _isSrcSizing,销毁窗口是异步的
|
||||
isSrcRepositioning = true;
|
||||
|
||||
if (srcSizeChanged) {
|
||||
// 源窗口大小改变则清除记忆
|
||||
_lastWindowedRendererWidth = 0;
|
||||
} else if (srcMinimized) {
|
||||
} else if (isSrcInvisibleOrMinimized) {
|
||||
if (_options.IsWindowedMode()) {
|
||||
_lastWindowedRendererWidth = _rendererRect.right - _rendererRect.left;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,9 @@ static bool IsWindowMoving(HWND hWnd) noexcept {
|
|||
}
|
||||
}
|
||||
|
||||
ScalingError SrcTracker::Set(HWND hWnd, const ScalingOptions& options) noexcept {
|
||||
ScalingError SrcTracker::Set(HWND hWnd, const ScalingOptions& options, bool& isInvisibleOrMinimized) noexcept {
|
||||
assert(!isInvisibleOrMinimized);
|
||||
|
||||
_hWnd = hWnd;
|
||||
|
||||
// 这里不检查源窗口是否挂起,将在创建缩放窗口前检查
|
||||
|
|
@ -57,11 +59,21 @@ ScalingError SrcTracker::Set(HWND hWnd, const ScalingOptions& options) noexcept
|
|||
return ScalingError::InvalidSourceWindow;
|
||||
}
|
||||
|
||||
// 不可见和最小化的窗口将等待源窗口状态改变,这里提前返回。注意 showCmd 不能准确
|
||||
// 判断窗口可见性,应使用 IsWindowVisible。
|
||||
if (!IsWindowVisible(_hWnd)) {
|
||||
Logger::Get().Error("不支持缩放隐藏的窗口");
|
||||
return ScalingError::InvalidSourceWindow;
|
||||
isInvisibleOrMinimized = true;
|
||||
return ScalingError::NoError;
|
||||
}
|
||||
|
||||
const UINT showCmd = Win32Helper::GetWindowShowCmd(hWnd);
|
||||
if (showCmd == SW_SHOWMINIMIZED) {
|
||||
isInvisibleOrMinimized = true;
|
||||
return ScalingError::NoError;
|
||||
}
|
||||
|
||||
_isMaximized = showCmd == SW_SHOWMAXIMIZED;
|
||||
|
||||
if (Win32Helper::GetWindowClassName(hWnd) == L"Ghost") {
|
||||
Logger::Get().Error("不支持缩放幽灵窗口");
|
||||
return ScalingError::InvalidSourceWindow;
|
||||
|
|
@ -110,14 +122,6 @@ ScalingError SrcTracker::Set(HWND hWnd, const ScalingOptions& options) noexcept
|
|||
return ScalingError::ScalingFailedGeneral;
|
||||
}
|
||||
|
||||
const UINT showCmd = Win32Helper::GetWindowShowCmd(hWnd);
|
||||
if (showCmd == SW_SHOWMINIMIZED) {
|
||||
Logger::Get().Error("不支持缩放最小化的窗口");
|
||||
return ScalingError::InvalidSourceWindow;
|
||||
}
|
||||
|
||||
_isMaximized = showCmd == SW_SHOWMAXIMIZED;
|
||||
|
||||
// 计算窗口样式
|
||||
BOOL hasBorder = TRUE;
|
||||
hr = DwmGetWindowAttribute(hWnd, DWMWA_NCRENDERING_ENABLED, &hasBorder, sizeof(hasBorder));
|
||||
|
|
@ -182,20 +186,23 @@ bool SrcTracker::UpdateState(
|
|||
HWND hwndFore,
|
||||
bool isWindowedMode,
|
||||
bool isResizingOrMoving,
|
||||
bool& isInvisibleOrMinimized,
|
||||
bool& focusedChanged,
|
||||
bool& ownedWindowFocusedChanged,
|
||||
bool& minimized,
|
||||
bool& rectChanged,
|
||||
bool& sizeChanged,
|
||||
bool& movingChanged
|
||||
) noexcept {
|
||||
assert(!focusedChanged && !ownedWindowFocusedChanged && !rectChanged && !sizeChanged && !movingChanged);
|
||||
assert(!isInvisibleOrMinimized && !focusedChanged && !ownedWindowFocusedChanged &&
|
||||
!rectChanged && !sizeChanged && !movingChanged);
|
||||
|
||||
if (!IsWindow(_hWnd)) {
|
||||
Logger::Get().Info("源窗口已销毁");
|
||||
return false;
|
||||
}
|
||||
|
||||
isInvisibleOrMinimized = !IsWindowVisible(_hWnd);
|
||||
|
||||
// Win32Helper::IsWindowHung 更准确,但它会向源窗口发送消息,比较耗时。
|
||||
// IsHungAppWindow 的另一个好处是它不如 Win32Helper::IsWindowHung 严
|
||||
// 格,因此即使源窗口挂起一段时间,只要用户不做额外的操作就不会结束缩放,
|
||||
|
|
@ -213,26 +220,30 @@ bool SrcTracker::UpdateState(
|
|||
ownedWindowFocusedChanged = _UpdateIsOwnedWindowFocused(hwndFore);
|
||||
|
||||
const bool oldMaximized = _isMaximized;
|
||||
const UINT showCmd = Win32Helper::GetWindowShowCmd(_hWnd);
|
||||
if (showCmd == SW_HIDE) {
|
||||
Logger::Get().Info("源窗口已隐藏");
|
||||
|
||||
WINDOWPLACEMENT wp{ sizeof(wp) };
|
||||
if (!GetWindowPlacement(_hWnd, &wp)) {
|
||||
Logger::Get().Win32Error("GetWindowPlacement 失败");
|
||||
return false;
|
||||
} else if (showCmd == SW_SHOWMINIMIZED) {
|
||||
Logger::Get().Info("源窗口已最小化");
|
||||
_isMaximized = false;
|
||||
minimized = true;
|
||||
} else {
|
||||
_isMaximized = showCmd == SW_SHOWMAXIMIZED;
|
||||
}
|
||||
|
||||
_isMaximized = wp.showCmd == SW_SHOWMAXIMIZED;
|
||||
|
||||
RECT curWindowRect;
|
||||
if (minimized) {
|
||||
WINDOWPLACEMENT wp{ sizeof(wp) };
|
||||
if (!GetWindowPlacement(_hWnd, &wp)) {
|
||||
Logger::Get().Win32Error("GetWindowPlacement 失败");
|
||||
if (wp.showCmd == SW_SHOWMINIMIZED) {
|
||||
isInvisibleOrMinimized = true;
|
||||
|
||||
// rcNormalPosition 使用工作区坐标,应转换为屏幕坐标
|
||||
HMONITOR hMon = MonitorFromWindow(_hWnd, MONITOR_DEFAULTTOPRIMARY);
|
||||
MONITORINFO mi{ sizeof(mi) };
|
||||
if (!GetMonitorInfo(hMon, &mi)) {
|
||||
Logger::Get().Win32Error("GetMonitorInfo 失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
curWindowRect = wp.rcNormalPosition;
|
||||
Win32Helper::OffsetRect(
|
||||
curWindowRect, mi.rcWork.left - mi.rcMonitor.left, mi.rcWork.top - mi.rcMonitor.top);
|
||||
} else {
|
||||
if (!GetWindowRect(_hWnd, &curWindowRect)) {
|
||||
Logger::Get().Win32Error("GetWindowRect 失败");
|
||||
|
|
@ -266,7 +277,7 @@ bool SrcTracker::UpdateState(
|
|||
|
||||
// 处理自己实现拖拽逻辑的窗口:将鼠标左键按下视为开始拖拽,释放视为拖拽结束。
|
||||
// 可能会有误判,但幸好后果不太严重。
|
||||
const bool isMoving = !minimized &&
|
||||
const bool isMoving = !isInvisibleOrMinimized &&
|
||||
(IsWindowMoving(_hWnd) || (rectChanged && IsPrimaryMouseButtonDown()));
|
||||
if (_isMoving != isMoving) {
|
||||
movingChanged = true;
|
||||
|
|
|
|||
|
|
@ -27,15 +27,15 @@ public:
|
|||
SrcTracker(const SrcTracker&) = delete;
|
||||
SrcTracker(SrcTracker&&) = delete;
|
||||
|
||||
ScalingError Set(HWND hWnd, const ScalingOptions& options) noexcept;
|
||||
ScalingError Set(HWND hWnd, const ScalingOptions& options, bool& isInvisibleOrMinimized) noexcept;
|
||||
|
||||
bool UpdateState(
|
||||
HWND hwndFore,
|
||||
bool isWindowedMode,
|
||||
bool isResizingOrMoving,
|
||||
bool& isInvisibleOrMinimized,
|
||||
bool& focusedChanged,
|
||||
bool& ownedWindowFocusedChanged,
|
||||
bool& minimized,
|
||||
bool& rectChanged,
|
||||
bool& sizeChanged,
|
||||
bool& movingChanged
|
||||
|
|
|
|||
|
|
@ -517,13 +517,11 @@ void AppSettings::_UpdateWindowPlacement() noexcept {
|
|||
return;
|
||||
}
|
||||
|
||||
const POINT workingAreaOffset = {
|
||||
mi.rcWork.left - mi.rcMonitor.left,
|
||||
mi.rcWork.top - mi.rcMonitor.top
|
||||
};
|
||||
const LONG workingAreaOffsetX = mi.rcWork.left - mi.rcMonitor.left;
|
||||
const LONG workingAreaOffsetY = mi.rcWork.top - mi.rcMonitor.top;
|
||||
_mainWindowCenter = {
|
||||
(wp.rcNormalPosition.left + wp.rcNormalPosition.right) / 2.0f + workingAreaOffset.x,
|
||||
(wp.rcNormalPosition.top + wp.rcNormalPosition.bottom) / 2.0f + workingAreaOffset.y,
|
||||
(wp.rcNormalPosition.left + wp.rcNormalPosition.right) / 2.0f + workingAreaOffsetX,
|
||||
(wp.rcNormalPosition.top + wp.rcNormalPosition.bottom) / 2.0f + workingAreaOffsetY,
|
||||
};
|
||||
|
||||
const float dpiFactor = GetDpiForWindow(hwndMain) / float(USER_DEFAULT_SCREEN_DPI);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue