win: fix the multi monitor window expand issue

#141
#184

Signed-off-by: Yuhang Zhao <2546789017@qq.com>
This commit is contained in:
Yuhang Zhao 2022-11-22 15:03:11 +08:00
parent 3961ecb505
commit ebd3d6573f
6 changed files with 114 additions and 61 deletions

View File

@ -415,10 +415,10 @@ Q_ENUM_NS(WindowCornerStyle)
struct VersionNumber struct VersionNumber
{ {
int major = 0; const int major = 0;
int minor = 0; const int minor = 0;
int patch = 0; const int patch = 0;
int tweak = 0; const int tweak = 0;
[[nodiscard]] friend constexpr bool operator==(const VersionNumber &lhs, const VersionNumber &rhs) noexcept [[nodiscard]] friend constexpr bool operator==(const VersionNumber &lhs, const VersionNumber &rhs) noexcept
{ {
@ -571,39 +571,6 @@ struct SystemParameters
} }
}; };
#ifdef Q_OS_WINDOWS
[[maybe_unused]] inline constexpr const VersionNumber WindowsVersions[] =
{
{ 5, 0, 2195}, // Windows 2000
{ 5, 1, 2600}, // Windows XP
{ 5, 2, 3790}, // Windows XP x64 Edition or Windows Server 2003
{ 6, 0, 6000}, // Windows Vista
{ 6, 0, 6001}, // Windows Vista with Service Pack 1 or Windows Server 2008
{ 6, 0, 6002}, // Windows Vista with Service Pack 2
{ 6, 1, 7600}, // Windows 7 or Windows Server 2008 R2
{ 6, 1, 7601}, // Windows 7 with Service Pack 1 or Windows Server 2008 R2 with Service Pack 1
{ 6, 2, 9200}, // Windows 8 or Windows Server 2012
{ 6, 3, 9200}, // Windows 8.1 or Windows Server 2012 R2
{ 6, 3, 9600}, // Windows 8.1 with Update 1
{10, 0, 10240}, // Windows 10 Version 1507 (TH1)
{10, 0, 10586}, // Windows 10 Version 1511 (November Update) (TH2)
{10, 0, 14393}, // Windows 10 Version 1607 (Anniversary Update) (RS1) or Windows Server 2016
{10, 0, 15063}, // Windows 10 Version 1703 (Creators Update) (RS2)
{10, 0, 16299}, // Windows 10 Version 1709 (Fall Creators Update) (RS3)
{10, 0, 17134}, // Windows 10 Version 1803 (April 2018 Update) (RS4)
{10, 0, 17763}, // Windows 10 Version 1809 (October 2018 Update) (RS5) or Windows Server 2019
{10, 0, 18362}, // Windows 10 Version 1903 (May 2019 Update) (19H1)
{10, 0, 18363}, // Windows 10 Version 1909 (November 2019 Update) (19H2)
{10, 0, 19041}, // Windows 10 Version 2004 (May 2020 Update) (20H1)
{10, 0, 19042}, // Windows 10 Version 20H2 (October 2020 Update) (20H2)
{10, 0, 19043}, // Windows 10 Version 21H1 (May 2021 Update) (21H1)
{10, 0, 19044}, // Windows 10 Version 21H2 (November 2021 Update) (21H2)
{10, 0, 19045}, // Windows 10 Version 22H2 (October 2022 Update) (22H2)
{10, 0, 22000}, // Windows 11 Version 21H2 (21H2)
{10, 0, 22621} // Windows 11 Version 22H2 (October 2022 Update) (22H2)
};
#endif // Q_OS_WINDOWS
struct VersionInfo struct VersionInfo
{ {
const int version = 0; const int version = 0;
@ -615,6 +582,45 @@ struct VersionInfo
const bool isStatic = false; const bool isStatic = false;
}; };
struct Dpi
{
const quint32 x = 0;
const quint32 y = 0;
};
#ifdef Q_OS_WINDOWS
[[maybe_unused]] inline constexpr const VersionNumber WindowsVersions[] =
{
{ 5, 0, 2195}, // Windows 2000
{ 5, 1, 2600}, // Windows XP
{ 5, 2, 3790}, // Windows XP x64 Edition or Windows Server 2003
{ 6, 0, 6000}, // Windows Vista
{ 6, 0, 6001}, // Windows Vista with Service Pack 1 or Windows Server 2008
{ 6, 0, 6002}, // Windows Vista with Service Pack 2
{ 6, 1, 7600}, // Windows 7 or Windows Server 2008 R2
{ 6, 1, 7601}, // Windows 7 with Service Pack 1 or Windows Server 2008 R2 with Service Pack 1
{ 6, 2, 9200}, // Windows 8 or Windows Server 2012
{ 6, 3, 9200}, // Windows 8.1 or Windows Server 2012 R2
{ 6, 3, 9600}, // Windows 8.1 with Update 1
{10, 0, 10240}, // Windows 10 Version 1507 (TH1)
{10, 0, 10586}, // Windows 10 Version 1511 (November Update) (TH2)
{10, 0, 14393}, // Windows 10 Version 1607 (Anniversary Update) (RS1) or Windows Server 2016
{10, 0, 15063}, // Windows 10 Version 1703 (Creators Update) (RS2)
{10, 0, 16299}, // Windows 10 Version 1709 (Fall Creators Update) (RS3)
{10, 0, 17134}, // Windows 10 Version 1803 (April 2018 Update) (RS4)
{10, 0, 17763}, // Windows 10 Version 1809 (October 2018 Update) (RS5) or Windows Server 2019
{10, 0, 18362}, // Windows 10 Version 1903 (May 2019 Update) (19H1)
{10, 0, 18363}, // Windows 10 Version 1909 (November 2019 Update) (19H2)
{10, 0, 19041}, // Windows 10 Version 2004 (May 2020 Update) (20H1)
{10, 0, 19042}, // Windows 10 Version 20H2 (October 2020 Update) (20H2)
{10, 0, 19043}, // Windows 10 Version 21H1 (May 2021 Update) (21H1)
{10, 0, 19044}, // Windows 10 Version 21H2 (November 2021 Update) (21H2)
{10, 0, 19045}, // Windows 10 Version 22H2 (October 2022 Update) (22H2)
{10, 0, 22000}, // Windows 11 Version 21H2 (21H2)
{10, 0, 22621} // Windows 11 Version 22H2 (October 2022 Update) (22H2)
};
#endif // Q_OS_WINDOWS
} // namespace Global } // namespace Global
namespace FramelessHelper::Core namespace FramelessHelper::Core
@ -633,3 +639,10 @@ FRAMELESSHELPER_END_NAMESPACE
Q_DECLARE_METATYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::VersionNumber) Q_DECLARE_METATYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::VersionNumber)
Q_DECLARE_METATYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::SystemParameters) Q_DECLARE_METATYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::SystemParameters)
Q_DECLARE_METATYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::VersionInfo) Q_DECLARE_METATYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::VersionInfo)
Q_DECLARE_METATYPE(FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::Dpi);
#ifndef QT_NO_DEBUG_STREAM
FRAMELESSHELPER_CORE_API QDebug operator<<(QDebug, const FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::VersionNumber &);
FRAMELESSHELPER_CORE_API QDebug operator<<(QDebug, const FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::VersionInfo &);
FRAMELESSHELPER_CORE_API QDebug operator<<(QDebug, const FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::Dpi &);
#endif // QT_NO_DEBUG_STREAM

View File

@ -92,7 +92,7 @@ FRAMELESSHELPER_CORE_API void showSystemMenu(
[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getResizeBorderThickness(const WId windowId, [[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getResizeBorderThickness(const WId windowId,
const bool horizontal, const bool horizontal,
const bool scaled); const bool scaled);
[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getCaptionHeight(const WId windowId, const bool scaled); [[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getCaptionBarHeight(const WId windowId, const bool scaled);
[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getTitleBarHeight(const WId windowId, const bool scaled); [[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getTitleBarHeight(const WId windowId, const bool scaled);
[[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getFrameBorderThickness(const WId windowId, [[nodiscard]] FRAMELESSHELPER_CORE_API quint32 getFrameBorderThickness(const WId windowId,
const bool scaled); const bool scaled);

View File

@ -531,8 +531,8 @@ void FramelessHelperWin::addWindow(const SystemParameters &params)
qApp->installNativeEventFilter(g_win32Helper()->nativeEventFilter.data()); qApp->installNativeEventFilter(g_win32Helper()->nativeEventFilter.data());
} }
g_win32Helper()->mutex.unlock(); g_win32Helper()->mutex.unlock();
DEBUG.nospace().noquote() << "The DPI of window " << hwnd2str(windowId) << " is: QDpi(" const Dpi dpi = {Utils::getWindowDpi(windowId, true), Utils::getWindowDpi(windowId, false)};
<< Utils::getWindowDpi(windowId, true) << ", " << Utils::getWindowDpi(windowId, false) << ")."; DEBUG.noquote() << "The DPI of window" << hwnd2str(windowId) << "is" << dpi;
// Some Qt internals have to be corrected. // Some Qt internals have to be corrected.
Utils::maybeFixupQtInternals(windowId); Utils::maybeFixupQtInternals(windowId);
// Qt maintains a frame margin internally, we need to update it accordingly // Qt maintains a frame margin internally, we need to update it accordingly
@ -1122,22 +1122,30 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
} break; } break;
#endif #endif
case WM_DPICHANGED: { case WM_DPICHANGED: {
const UINT dpiX = LOWORD(wParam); const Dpi dpi = {UINT(LOWORD(wParam)), UINT(HIWORD(wParam))};
const UINT dpiY = HIWORD(wParam); DEBUG.noquote() << "New DPI for window" << hwnd2str(hWnd) << "is" << dpi;
DEBUG.nospace().noquote() << "New DPI for window "
<< hwnd2str(hWnd) << ": QDpi(" << dpiX << ", " << dpiY << ").";
// Sync the internal window frame margins with the latest DPI. // Sync the internal window frame margins with the latest DPI.
Utils::updateInternalWindowFrameMargins(data.params.getWindowHandle(), true); Utils::updateInternalWindowFrameMargins(data.params.getWindowHandle(), true);
// Here we need a little delay because event filters are processed before // Here we need a little delay because event filters are processed before
// Qt's own window message handlers. // Qt's own window message handlers.
QTimer::singleShot(50, [data](){ // Copy "data" intentionally, otherwise it'll go out of scope when Qt finally use it. QTimer::singleShot(0, qApp, [data, windowId](){ // Copy the variables intentionally, otherwise they'll go out of scope when Qt finally use it.
// For some unknown reason, Qt sometimes won't re-paint the window contents after // For some unknown reason, Qt sometimes won't re-paint the window contents after
// the DPI changes, and in my experiments the controls should be moved to our // the DPI changes, and in my experiments the controls should be moved to our
// desired geometry already, the only issue is we don't get the updated appearance // desired geometry already, the only issue is we don't get the updated appearance
// of our window. And we can workaround this issue by simply triggering a resize // of our window. And we can workaround this issue by simply triggering a resize
// event manually. There's no need to increase/decrease the window size and then // event manually.
// change it back, just give Qt our current window size is sufficient enough. // For some reason the window will always expand for some pixels after the DPI
data.params.setWindowSize(data.params.getWindowSize()); // changes, and normal Qt windows don't have this issue. It's probably caused
// by the custom margins we set, QPA may calculate a wrong frame size. It will
// always add a title bar height to the window, while it should not due to we
// have hide the title bar. For now we can simply workaround it by restoring
// to the original window size after the DPI change, but it's not ideal. Maybe
// we should reset the custom margins first and then restore it instead, but
// sadly this solution doesn't work as expected. Still need investigating.
const int titleBarHeight = Utils::getTitleBarHeight(windowId, false);
const QSize expandedSize = data.params.getWindowSize();
const QSize correctedSize = {expandedSize.width(), expandedSize.height() - titleBarHeight};
data.params.setWindowSize(correctedSize);
}); });
} break; } break;
case WM_DWMCOMPOSITIONCHANGED: { case WM_DWMCOMPOSITIONCHANGED: {

View File

@ -46,7 +46,7 @@
#endif #endif
#include <QtCore/qmutex.h> #include <QtCore/qmutex.h>
#include <QtCore/qiodevice.h> #include <QtCore/qiodevice.h>
#include <QtGui/qguiapplication.h> #include <QtCore/qcoreapplication.h>
#ifndef COMPILER_STRING #ifndef COMPILER_STRING
# ifdef Q_CC_CLANG // Must be before GNU, because Clang claims to be GNU too. # ifdef Q_CC_CLANG // Must be before GNU, because Clang claims to be GNU too.
@ -72,6 +72,46 @@
# endif # endif
#endif #endif
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug d, const FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::VersionNumber &ver)
{
const QDebugStateSaver saver(d);
d.nospace().noquote() << "VersionNumber("
<< ver.major << ", "
<< ver.minor << ", "
<< ver.patch << ", "
<< ver.tweak << ')';
return d;
}
QDebug operator<<(QDebug d, const FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::VersionInfo &ver)
{
const QDebugStateSaver saver(d);
int major = 0, minor = 0, patch = 0, tweak = 0;
FRAMELESSHELPER_EXTRACT_VERSION(ver.version, major, minor, patch, tweak)
const auto ver_num = FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::VersionNumber{major, minor, patch, tweak};
d.nospace().noquote() << "VersionInfo("
<< "version number: " << ver_num << ", "
<< "version string: " << ver.version_str << ", "
<< "commit hash: " << ver.commit << ", "
<< "compiler: " << ver.compiler << ", "
<< "debug build: " << ver.isDebug << ", "
<< "static build: " << ver.isStatic << ')';
return d;
}
QDebug operator<<(QDebug d, const FRAMELESSHELPER_PREPEND_NAMESPACE(Global)::Dpi &dpi)
{
const QDebugStateSaver saver(d);
const qreal scaleFactor = (qreal(dpi.x) / qreal(96));
d.nospace().noquote() << "Dpi("
<< "x: " << dpi.x << ", "
<< "y: " << dpi.y << ", "
<< "scale factor: " << scaleFactor << ')';
return d;
}
#endif // QT_NO_DEBUG_STREAM
FRAMELESSHELPER_BEGIN_NAMESPACE FRAMELESSHELPER_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcCoreGlobal, "wangwenx190.framelesshelper.core.global") Q_LOGGING_CATEGORY(lcCoreGlobal, "wangwenx190.framelesshelper.core.global")
@ -172,15 +212,6 @@ void initialize()
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif #endif
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
// Non-integer scale factors will cause Qt have some painting defects
// for both Qt Widgets and Qt Quick applications, and it's still not
// totally fixed till now (Qt 6.5), so we round the scale factors to
// get a better looking. Non-integer scale factors will also cause
// flicker and jitter during window resizing.
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Round);
#endif
qRegisterMetaType<Option>(); qRegisterMetaType<Option>();
qRegisterMetaType<SystemTheme>(); qRegisterMetaType<SystemTheme>();
qRegisterMetaType<SystemButtonType>(); qRegisterMetaType<SystemButtonType>();
@ -206,6 +237,7 @@ void initialize()
qRegisterMetaType<VersionNumber>(); qRegisterMetaType<VersionNumber>();
qRegisterMetaType<SystemParameters>(); qRegisterMetaType<SystemParameters>();
qRegisterMetaType<VersionInfo>(); qRegisterMetaType<VersionInfo>();
qRegisterMetaType<Dpi>();
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
qRegisterMetaType<FramelessManager>(); qRegisterMetaType<FramelessManager>();
# ifdef Q_OS_WINDOWS # ifdef Q_OS_WINDOWS

View File

@ -220,7 +220,7 @@ void FramelessManagerPrivate::addWindow(const SystemParameters &params)
Q_UNUSED(screen); Q_UNUSED(screen);
// Add a little delay here, make sure it happens after Qt has processed the window // Add a little delay here, make sure it happens after Qt has processed the window
// messages. // messages.
QTimer::singleShot(50, window, [windowId, window](){ QTimer::singleShot(0, window, [windowId, window](){
// Force a WM_NCCALCSIZE event to inform Windows about our custom window frame, // Force a WM_NCCALCSIZE event to inform Windows about our custom window frame,
// this is only necessary when the window is being moved cross monitors. // this is only necessary when the window is being moved cross monitors.
Utils::triggerFrameChange(windowId); Utils::triggerFrameChange(windowId);

View File

@ -1735,7 +1735,7 @@ quint32 Utils::getResizeBorderThickness(const WId windowId, const bool horizonta
} }
} }
quint32 Utils::getCaptionHeight(const WId windowId, const bool scaled) quint32 Utils::getCaptionBarHeight(const WId windowId, const bool scaled)
{ {
Q_ASSERT(windowId); Q_ASSERT(windowId);
if (!windowId) { if (!windowId) {
@ -1750,7 +1750,7 @@ quint32 Utils::getTitleBarHeight(const WId windowId, const bool scaled)
if (!windowId) { if (!windowId) {
return 0; return 0;
} }
return (getCaptionHeight(windowId, scaled) + getResizeBorderThickness(windowId, false, scaled)); return (getCaptionBarHeight(windowId, scaled) + getResizeBorderThickness(windowId, false, scaled));
} }
quint32 Utils::getFrameBorderThickness(const WId windowId, const bool scaled) quint32 Utils::getFrameBorderThickness(const WId windowId, const bool scaled)