win: fix multi-monitor bug, take 3
#141 #184 Signed-off-by: Yuhang Zhao <2546789017@qq.com>
This commit is contained in:
parent
ff5b171566
commit
a26df61c33
|
@ -407,10 +407,10 @@ Q_ENUM_NS(WindowCornerStyle)
|
|||
|
||||
struct VersionNumber
|
||||
{
|
||||
const int major = 0;
|
||||
const int minor = 0;
|
||||
const int patch = 0;
|
||||
const int tweak = 0;
|
||||
int major = 0;
|
||||
int minor = 0;
|
||||
int patch = 0;
|
||||
int tweak = 0;
|
||||
|
||||
[[nodiscard]] friend constexpr bool operator==(const VersionNumber &lhs, const VersionNumber &rhs) noexcept
|
||||
{
|
||||
|
@ -565,19 +565,19 @@ struct SystemParameters
|
|||
|
||||
struct VersionInfo
|
||||
{
|
||||
const int version = 0;
|
||||
int version = 0;
|
||||
const char *version_str = nullptr;
|
||||
const char *commit = nullptr;
|
||||
const char *compileDateTime = nullptr;
|
||||
const char *compiler = nullptr;
|
||||
const bool isDebug = false;
|
||||
const bool isStatic = false;
|
||||
bool isDebug = false;
|
||||
bool isStatic = false;
|
||||
};
|
||||
|
||||
struct Dpi
|
||||
{
|
||||
const quint32 x = 0;
|
||||
const quint32 y = 0;
|
||||
quint32 x = 0;
|
||||
quint32 y = 0;
|
||||
};
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
|
|
|
@ -68,6 +68,7 @@ FRAMELESSHELPER_CORE_API void moveWindowToDesktopCenter(
|
|||
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isBlurBehindWindowSupported();
|
||||
FRAMELESSHELPER_CORE_API void registerThemeChangeNotification();
|
||||
[[nodiscard]] FRAMELESSHELPER_CORE_API QColor getFrameBorderColor(const bool active);
|
||||
[[nodiscard]] FRAMELESSHELPER_CORE_API qreal roundScaleFactor(const qreal factor);
|
||||
|
||||
#ifdef Q_OS_WINDOWS
|
||||
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isWindowsVersionOrGreater(const Global::WindowsVersion version);
|
||||
|
@ -89,11 +90,16 @@ FRAMELESSHELPER_CORE_API void showSystemMenu(
|
|||
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isHighContrastModeEnabled();
|
||||
[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getPrimaryScreenDpi(const bool horizontal);
|
||||
[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getWindowDpi(const WId windowId, const bool horizontal);
|
||||
[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getResizeBorderThicknessForDpi
|
||||
(const bool horizontal, const quint32 dpi);
|
||||
[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getResizeBorderThickness(const WId windowId,
|
||||
const bool horizontal,
|
||||
const bool scaled);
|
||||
[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getCaptionBarHeightForDpi(const quint32 dpi);
|
||||
[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getCaptionBarHeight(const WId windowId, const bool scaled);
|
||||
[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getTitleBarHeightForDpi(const quint32 dpi);
|
||||
[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getTitleBarHeight(const WId windowId, const bool scaled);
|
||||
[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getFrameBorderThicknessForDpi(const quint32 dpi);
|
||||
[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getFrameBorderThickness(const WId windowId,
|
||||
const bool scaled);
|
||||
FRAMELESSHELPER_CORE_API void maybeFixupQtInternals(const WId windowId);
|
||||
|
|
|
@ -95,6 +95,7 @@ struct Win32HelperData
|
|||
SystemParameters params = {};
|
||||
bool trackingMouse = false;
|
||||
WId fallbackTitleBarWindowId = 0;
|
||||
Dpi dpi = {};
|
||||
};
|
||||
|
||||
struct Win32Helper
|
||||
|
@ -521,14 +522,14 @@ void FramelessHelperWin::addWindow(const SystemParameters ¶ms)
|
|||
}
|
||||
Win32HelperData data = {};
|
||||
data.params = params;
|
||||
data.dpi = {Utils::getWindowDpi(windowId, true), Utils::getWindowDpi(windowId, false)};
|
||||
g_win32Helper()->data.insert(windowId, data);
|
||||
if (g_win32Helper()->nativeEventFilter.isNull()) {
|
||||
g_win32Helper()->nativeEventFilter.reset(new FramelessHelperWin);
|
||||
qApp->installNativeEventFilter(g_win32Helper()->nativeEventFilter.data());
|
||||
}
|
||||
g_win32Helper()->mutex.unlock();
|
||||
const Dpi dpi = {Utils::getWindowDpi(windowId, true), Utils::getWindowDpi(windowId, false)};
|
||||
DEBUG.noquote() << "The DPI of window" << hwnd2str(windowId) << "is" << dpi;
|
||||
DEBUG.noquote() << "The DPI of window" << hwnd2str(windowId) << "is" << data.dpi;
|
||||
// Some Qt internals have to be corrected.
|
||||
Utils::maybeFixupQtInternals(windowId);
|
||||
// Qt maintains a frame margin internally, we need to update it accordingly
|
||||
|
@ -1123,28 +1124,56 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
|
|||
const auto windowPos = reinterpret_cast<LPWINDOWPOS>(lParam);
|
||||
windowPos->flags |= SWP_NOCOPYBITS;
|
||||
} break;
|
||||
#endif
|
||||
#if ((QT_VERSION <= QT_VERSION_CHECK(6, 2, 6)) || ((QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)) && (QT_VERSION <= QT_VERSION_CHECK(6, 4, 1))))
|
||||
case WM_GETDPISCALEDSIZE: {
|
||||
// QtBase commit 2cfca7fd1911cc82a22763152c04c65bc05bc19a introduced a bug
|
||||
// which caused the custom margins is ignored during the handling of the
|
||||
// WM_GETDPISCALEDSIZE message, it was shipped with Qt 6.2.1 ~ 6.2.6 & 6.3 ~ 6.4.1.
|
||||
// We workaround it by overriding the wrong handling directly.
|
||||
RECT clientRect = {};
|
||||
if (GetClientRect(hWnd, &clientRect) == FALSE) {
|
||||
WARNING << Utils::getSystemErrorMessage(kGetClientRect);
|
||||
*result = FALSE; // Use the default linear DPI scaling provided by Windows.
|
||||
return true; // Jump over Qt's wrong handling logic.
|
||||
}
|
||||
const QSizeF oldSize = {qreal(clientRect.right - clientRect.left), qreal(clientRect.bottom - clientRect.top)};
|
||||
const UINT oldDpi = data.dpi.x;
|
||||
static constexpr const auto defaultDpi = qreal(USER_DEFAULT_SCREEN_DPI);
|
||||
// We need to round the scale factor according to Qt's rounding policy.
|
||||
const qreal oldDpr = Utils::roundScaleFactor(qreal(oldDpi) / defaultDpi);
|
||||
const QSizeF unscaledSize = (oldSize / oldDpr);
|
||||
const auto newDpi = UINT(wParam);
|
||||
const qreal newDpr = Utils::roundScaleFactor(qreal(newDpi) / defaultDpi);
|
||||
const QSizeF newSize = (unscaledSize * newDpr);
|
||||
const auto suggestedSize = reinterpret_cast<LPSIZE>(lParam);
|
||||
suggestedSize->cx = qRound(newSize.width());
|
||||
suggestedSize->cy = qRound(newSize.height());
|
||||
// If the window frame is visible, we need to expand the suggested size, currently
|
||||
// it's pure client size, we need to add the frame size to it. Windows expects a
|
||||
// full window size, including the window frame.
|
||||
// If the window frame is not visible, the window size equals to the client size,
|
||||
// the suggested size doesn't need further adjustments.
|
||||
if (frameBorderVisible) {
|
||||
const int frameSizeX = Utils::getResizeBorderThicknessForDpi(true, newDpi);
|
||||
const int frameSizeY = Utils::getResizeBorderThicknessForDpi(false, newDpi);
|
||||
suggestedSize->cx += (frameSizeX * 2);
|
||||
suggestedSize->cy += frameSizeY;
|
||||
}
|
||||
*result = TRUE; // We have set our preferred window size, don't use the default linear DPI scaling.
|
||||
return true; // Jump over Qt's wrong handling logic.
|
||||
}
|
||||
#endif
|
||||
case WM_DPICHANGED: {
|
||||
const Dpi dpi = {UINT(LOWORD(wParam)), UINT(HIWORD(wParam))};
|
||||
DEBUG.noquote() << "New DPI for window" << hwnd2str(hWnd) << "is" << dpi;
|
||||
g_win32Helper()->mutex.lock();
|
||||
g_win32Helper()->data[windowId].dpi = dpi;
|
||||
g_win32Helper()->mutex.unlock();
|
||||
#if (QT_VERSION <= QT_VERSION_CHECK(6, 4, 1))
|
||||
const auto suggestedRect = *reinterpret_cast<LPRECT>(lParam);
|
||||
const int titleBarHeight = Utils::getTitleBarHeight(windowId, true);
|
||||
// We need to wait until Qt has handled this message, otherwise everything
|
||||
// we have done here will always be overwritten.
|
||||
QTimer::singleShot(0, qApp, [data, hWnd, titleBarHeight, suggestedRect](){ // Copy the variables intentionally, otherwise they'll go out of scope when Qt finally use them.
|
||||
// QtBase commit 2cfca7fd1911cc82a22763152c04c65bc05bc19a introduced a bug
|
||||
// which caused the custom margins is ignored while handling the DPI change
|
||||
// messages, it was shipped with Qt 6.2.1 ~ 6.2.6 & 6.3 ~ 6.4.1. We can
|
||||
// workaround it by manually shrink the window size after Qt handles WM_DPICHANGED.
|
||||
# if (((QT_VERSION >= QT_VERSION_CHECK(6, 2, 1)) && (QT_VERSION <= QT_VERSION_CHECK(6, 2, 6))) || (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)))
|
||||
const int width = (suggestedRect.right - suggestedRect.left);
|
||||
const int height = (suggestedRect.bottom - suggestedRect.top - titleBarHeight);
|
||||
static constexpr const UINT flags = (SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
|
||||
if (SetWindowPos(hWnd, nullptr, 0, 0, width, height, flags) == FALSE) {
|
||||
WARNING << Utils::getSystemErrorMessage(kSetWindowPos);
|
||||
}
|
||||
# endif // (((QT_VERSION >= QT_VERSION_CHECK(6, 2, 1)) && (QT_VERSION <= QT_VERSION_CHECK(6, 2, 6))) || (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)))
|
||||
QTimer::singleShot(0, qApp, [data](){ // Copy the variables intentionally, otherwise they'll go out of scope when Qt finally use them.
|
||||
// Sync the internal window frame margins with the latest DPI, otherwise
|
||||
// we will get wrong window sizes after the DPI change.
|
||||
Utils::updateInternalWindowFrameMargins(data.params.getWindowHandle(), true);
|
||||
|
|
|
@ -294,4 +294,36 @@ bool Utils::shouldAppsUseDarkMode()
|
|||
#endif
|
||||
}
|
||||
|
||||
qreal Utils::roundScaleFactor(const qreal factor)
|
||||
{
|
||||
Q_ASSERT(factor > 0);
|
||||
if (factor <= 0) {
|
||||
return 1;
|
||||
}
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
|
||||
static const auto policy = QGuiApplication::highDpiScaleFactorRoundingPolicy();
|
||||
switch (policy) {
|
||||
case Qt::HighDpiScaleFactorRoundingPolicy::Unset:
|
||||
# if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||
return factor;
|
||||
# else // (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
return qRound(factor);
|
||||
# endif // (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||
case Qt::HighDpiScaleFactorRoundingPolicy::Round:
|
||||
return qRound(factor);
|
||||
case Qt::HighDpiScaleFactorRoundingPolicy::Ceil:
|
||||
return qCeil(factor);
|
||||
case Qt::HighDpiScaleFactorRoundingPolicy::Floor:
|
||||
return qFloor(factor);
|
||||
case Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor:
|
||||
return (((factor - qreal(int(factor))) >= qreal(0.75)) ? qRound(factor) : qFloor(factor));
|
||||
case Qt::HighDpiScaleFactorRoundingPolicy::PassThrough:
|
||||
return factor;
|
||||
}
|
||||
return 1;
|
||||
#else // (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
|
||||
return qRound(factor);
|
||||
#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
|
||||
}
|
||||
|
||||
FRAMELESSHELPER_END_NAMESPACE
|
||||
|
|
|
@ -1018,6 +1018,22 @@ static inline void moveWindowToMonitor(const HWND hwnd, const MONITORINFOEXW &ac
|
|||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] static inline int getSystemMetrics2(const int index, const bool horizontal,
|
||||
const quint32 dpi)
|
||||
{
|
||||
Q_ASSERT(dpi != 0);
|
||||
if (dpi == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (const int result = _GetSystemMetricsForDpi2(index, dpi); result > 0) {
|
||||
return result;
|
||||
}
|
||||
static constexpr const auto defaultDpi = qreal(USER_DEFAULT_SCREEN_DPI);
|
||||
const qreal currentDpr = (qreal(Utils::getPrimaryScreenDpi(horizontal)) / defaultDpi);
|
||||
const qreal requestedDpr = (qreal(dpi) / defaultDpi);
|
||||
return qRound(qreal(GetSystemMetrics(index)) / currentDpr * requestedDpr);
|
||||
}
|
||||
|
||||
[[nodiscard]] static inline int getSystemMetrics2(const WId windowId, const int index,
|
||||
const bool horizontal, const bool scaled)
|
||||
{
|
||||
|
@ -1720,6 +1736,21 @@ quint32 Utils::getWindowDpi(const WId windowId, const bool horizontal)
|
|||
return getPrimaryScreenDpi(horizontal);
|
||||
}
|
||||
|
||||
quint32 Utils::getResizeBorderThicknessForDpi(const bool horizontal, const quint32 dpi)
|
||||
{
|
||||
Q_ASSERT(dpi != 0);
|
||||
if (dpi == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (horizontal) {
|
||||
return (getSystemMetrics2(SM_CXSIZEFRAME, true, dpi)
|
||||
+ getSystemMetrics2(SM_CXPADDEDBORDER, true, dpi));
|
||||
} else {
|
||||
return (getSystemMetrics2(SM_CYSIZEFRAME, false, dpi)
|
||||
+ getSystemMetrics2(SM_CYPADDEDBORDER, false, dpi));
|
||||
}
|
||||
}
|
||||
|
||||
quint32 Utils::getResizeBorderThickness(const WId windowId, const bool horizontal, const bool scaled)
|
||||
{
|
||||
Q_ASSERT(windowId);
|
||||
|
@ -1735,6 +1766,15 @@ quint32 Utils::getResizeBorderThickness(const WId windowId, const bool horizonta
|
|||
}
|
||||
}
|
||||
|
||||
quint32 Utils::getCaptionBarHeightForDpi(const quint32 dpi)
|
||||
{
|
||||
Q_ASSERT(dpi != 0);
|
||||
if (dpi == 0) {
|
||||
return 0;
|
||||
}
|
||||
return getSystemMetrics2(SM_CYCAPTION, false, dpi);
|
||||
}
|
||||
|
||||
quint32 Utils::getCaptionBarHeight(const WId windowId, const bool scaled)
|
||||
{
|
||||
Q_ASSERT(windowId);
|
||||
|
@ -1744,6 +1784,15 @@ quint32 Utils::getCaptionBarHeight(const WId windowId, const bool scaled)
|
|||
return getSystemMetrics2(windowId, SM_CYCAPTION, false, scaled);
|
||||
}
|
||||
|
||||
quint32 Utils::getTitleBarHeightForDpi(const quint32 dpi)
|
||||
{
|
||||
Q_ASSERT(dpi != 0);
|
||||
if (dpi == 0) {
|
||||
return 0;
|
||||
}
|
||||
return (getCaptionBarHeightForDpi(dpi) + getResizeBorderThicknessForDpi(false, dpi));
|
||||
}
|
||||
|
||||
quint32 Utils::getTitleBarHeight(const WId windowId, const bool scaled)
|
||||
{
|
||||
Q_ASSERT(windowId);
|
||||
|
@ -1753,6 +1802,20 @@ quint32 Utils::getTitleBarHeight(const WId windowId, const bool scaled)
|
|||
return (getCaptionBarHeight(windowId, scaled) + getResizeBorderThickness(windowId, false, scaled));
|
||||
}
|
||||
|
||||
quint32 Utils::getFrameBorderThicknessForDpi(const quint32 dpi)
|
||||
{
|
||||
Q_ASSERT(dpi != 0);
|
||||
if (dpi == 0) {
|
||||
return 0;
|
||||
}
|
||||
// There's no window frame border before Windows 10.
|
||||
if (!WindowsVersionHelper::isWin10OrGreater()) {
|
||||
return 0;
|
||||
}
|
||||
const qreal dpr = (qreal(dpi) / qreal(USER_DEFAULT_SCREEN_DPI));
|
||||
return qRound(qreal(kDefaultWindowFrameBorderThickness) * dpr);
|
||||
}
|
||||
|
||||
quint32 Utils::getFrameBorderThickness(const WId windowId, const bool scaled)
|
||||
{
|
||||
Q_ASSERT(windowId);
|
||||
|
|
Loading…
Reference in New Issue