diff --git a/CMakeLists.txt b/CMakeLists.txt index a3dda83f..8f7857dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,12 +50,12 @@ option(FRAMELESSHELPER_NO_SUMMARY "Don't show CMake configure summary." ON) option(FRAMELESSHELPER_ENABLE_SPECTRE "Mitigate Spectre security vulnerabilities." OFF) option(FRAMELESSHELPER_ENABLE_EHCONTGUARD "MSVC only: Enable EH Continuation (EHCONT) Metadata." OFF) option(FRAMELESSHELPER_ENABLE_INTELCET "Enable Intel CET." OFF) -option(FRAMELESSHELPER_ENABLE_INTELJCC "Enable Intel JCC." OFF) +#option(FRAMELESSHELPER_ENABLE_INTELJCC "Enable Intel JCC." OFF) # Always enabled now. option(FRAMELESSHELPER_ENABLE_CFGUARD "Enable Control Flow Guard (CFG)." OFF) option(FRAMELESSHELPER_EXAMPLES_STANDALONE "Build the demo projects as standalone CMake projects." OFF) cmake_dependent_option(FRAMELESSHELPER_ENABLE_UNIVERSAL_BUILD "macOS only: build universal library/example for Mac." ON APPLE OFF) option(FRAMELESSHELPER_FORCE_LTO "Force enable LTO/LTCG even when building static libraries." OFF) -option(FRAMELESSHELPER_REPRODUCIBLE_OUTPUT "Don't update the build commit and date dynamically." ON) +#option(FRAMELESSHELPER_REPRODUCIBLE_OUTPUT "Don't update the build commit and date dynamically." ON) # Always enabled now. option(FRAMELESSHELPER_NO_WINDOW "Disable the pre-defined FramelessWindow wrapper class." OFF) option(FRAMELESSHELPER_NO_TITLEBAR "Disable the pre-defined StandardTitleBar control." OFF) option(FRAMELESSHELPER_NO_TRANSLATION "Don't bundle the I18N translations into the library." OFF) @@ -163,7 +163,7 @@ if(MINGW AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(FRAMELESSHELPER_ENABLE_SPECTRE OFF) set(FRAMELESSHELPER_ENABLE_EHCONTGUARD OFF) set(FRAMELESSHELPER_ENABLE_INTELCET OFF) - set(FRAMELESSHELPER_ENABLE_INTELJCC OFF) + #set(FRAMELESSHELPER_ENABLE_INTELJCC OFF) set(FRAMELESSHELPER_ENABLE_CFGUARD OFF) endif() @@ -299,12 +299,12 @@ if(NOT FRAMELESSHELPER_NO_SUMMARY) message("Mitigate Spectre security vulnerabilities: ${FRAMELESSHELPER_ENABLE_SPECTRE}") message("[MSVC] Enable EH Continuation (EHCONT) Metadata: ${FRAMELESSHELPER_ENABLE_EHCONTGUARD}") message("Enable Intel CET: ${FRAMELESSHELPER_ENABLE_INTELCET}") - message("Enable Intel JCC: ${FRAMELESSHELPER_ENABLE_INTELJCC}") + #message("Enable Intel JCC: ${FRAMELESSHELPER_ENABLE_INTELJCC}") message("Enable Control Flow Guard (CFG): ${FRAMELESSHELPER_ENABLE_CFGUARD}") message("Build standalone demo projects: ${FRAMELESSHELPER_EXAMPLES_STANDALONE}") message("[macOS]: Build universal library/example: ${FRAMELESSHELPER_ENABLE_UNIVERSAL_BUILD}") message("Force enable LTO: ${FRAMELESSHELPER_FORCE_LTO}") - message("Make output reproducible: ${FRAMELESSHELPER_REPRODUCIBLE_OUTPUT}") + #message("Make output reproducible: ${FRAMELESSHELPER_REPRODUCIBLE_OUTPUT}") message("Disable the FramelessWindow class (to reduce file size): ${FRAMELESSHELPER_NO_WINDOW}") message("Disable the StandardTitleBar class (to reduce file size): ${FRAMELESSHELPER_NO_TITLEBAR}") message("Don't embed the I18N resources (to reduce file size): ${FRAMELESSHELPER_NO_TRANSLATION}") diff --git a/include/FramelessHelper/Core/private/framelesshelpercore_global_p.h b/include/FramelessHelper/Core/private/framelesshelpercore_global_p.h index 4ce2607d..26ad76af 100644 --- a/include/FramelessHelper/Core/private/framelesshelpercore_global_p.h +++ b/include/FramelessHelper/Core/private/framelesshelpercore_global_p.h @@ -62,7 +62,7 @@ using GetPropertyCallback = std::function; using UnsetCursorCallback = std::function; using GetWidgetHandleCallback = std::function; -using ForceChildrenRepaintCallback = std::function; +using ForceChildrenRepaintCallback = std::function; using ResetQtGrabbedControlCallback = std::function; struct FRAMELESSHELPER_CORE_API FramelessCallbacks diff --git a/include/FramelessHelper/Quick/private/framelessquickhelper_p.h b/include/FramelessHelper/Quick/private/framelessquickhelper_p.h index 2cc17964..03cce3c3 100644 --- a/include/FramelessHelper/Quick/private/framelessquickhelper_p.h +++ b/include/FramelessHelper/Quick/private/framelessquickhelper_p.h @@ -25,6 +25,7 @@ #pragma once #include +#include #include QT_BEGIN_NAMESPACE @@ -67,7 +68,8 @@ class FRAMELESSHELPER_QUICK_API FramelessQuickHelperPrivate : public QObject Q_NODISCARD static FramelessQuickHelper *findOrCreateFramelessHelper(QObject *object); - void repaintAllChildren(const quint32 delay = 0) const; + void repaintAllChildren(); + Q_INVOKABLE void doRepaintAllChildren(); Q_NODISCARD quint32 readyWaitTime() const; void setReadyWaitTime(const quint32 time); @@ -84,6 +86,7 @@ class FRAMELESSHELPER_QUICK_API FramelessQuickHelperPrivate : public QObject std::optional extendIntoTitleBar = std::nullopt; bool qpaReady = false; quint32 qpaWaitTime = 0; + QTimer repaintTimer{}; }; FRAMELESSHELPER_END_NAMESPACE diff --git a/include/FramelessHelper/Widgets/private/framelesswidgetshelper_p.h b/include/FramelessHelper/Widgets/private/framelesswidgetshelper_p.h index 471e8bd9..bd4e7f1b 100644 --- a/include/FramelessHelper/Widgets/private/framelesswidgetshelper_p.h +++ b/include/FramelessHelper/Widgets/private/framelesswidgetshelper_p.h @@ -26,6 +26,7 @@ #include #include +#include #include FRAMELESSHELPER_BEGIN_NAMESPACE @@ -66,7 +67,8 @@ class FRAMELESSHELPER_WIDGETS_API FramelessWidgetsHelperPrivate : public QObject Q_NODISCARD static WidgetsSharedHelper *findOrCreateSharedHelper(QWidget *window); Q_NODISCARD static FramelessWidgetsHelper *findOrCreateFramelessHelper(QObject *object); - void repaintAllChildren(const quint32 delay = 0) const; + void repaintAllChildren(); + Q_INVOKABLE void doRepaintAllChildren(); Q_NODISCARD quint32 readyWaitTime() const; void setReadyWaitTime(const quint32 time); @@ -84,6 +86,7 @@ class FRAMELESSHELPER_WIDGETS_API FramelessWidgetsHelperPrivate : public QObject bool qpaReady = false; QSizePolicy savedSizePolicy = {}; quint32 qpaWaitTime = 0; + QTimer repaintTimer{}; }; FRAMELESSHELPER_END_NAMESPACE diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 139fe3d0..504ea00d 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -295,9 +295,6 @@ endif() if(FRAMELESSHELPER_ENABLE_INTELCET) list(APPEND __extra_flags INTELCET) endif() -if(FRAMELESSHELPER_ENABLE_INTELJCC) - list(APPEND __extra_flags INTELJCC) -endif() if(FRAMELESSHELPER_ENABLE_CFGUARD) list(APPEND __extra_flags CFGUARD) endif() diff --git a/src/core/framelesshelper_qt.cpp b/src/core/framelesshelper_qt.cpp index a9667fd3..25a0e1d9 100644 --- a/src/core/framelesshelper_qt.cpp +++ b/src/core/framelesshelper_qt.cpp @@ -222,7 +222,7 @@ bool FramelessHelperQt::eventFilter(QObject *object, QEvent *event) if (type == QEvent::ScreenChangeInternal) #endif // (QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)) { - data->callbacks->forceChildrenRepaint(500); + data->callbacks->forceChildrenRepaint(); return false; } const auto qWindow = qobject_cast(object); diff --git a/src/core/framelesshelper_win.cpp b/src/core/framelesshelper_win.cpp index 08826b65..0f5ea896 100644 --- a/src/core/framelesshelper_win.cpp +++ b/src/core/framelesshelper_win.cpp @@ -1154,7 +1154,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me data->restoreGeometry.setSize(Utils::rescaleSize(data->restoreGeometry.size(), oldDpi.x, newDpi.x)); } #endif // (QT_VERSION < QT_VERSION_CHECK(6, 5, 1)) - data->callbacks->forceChildrenRepaint(500); + data->callbacks->forceChildrenRepaint(); } break; case WM_DWMCOMPOSITIONCHANGED: // Re-apply the custom window frame if recovered from the basic theme. @@ -1202,7 +1202,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me break; } data->monitor = currentMonitor; - data->callbacks->forceChildrenRepaint(500); + data->callbacks->forceChildrenRepaint(); } break; case WM_SYSCOMMAND: { const WPARAM filteredWParam = (wParam & 0xFFF0); diff --git a/src/quick/CMakeLists.txt b/src/quick/CMakeLists.txt index 6b81b7ac..ef270e9c 100644 --- a/src/quick/CMakeLists.txt +++ b/src/quick/CMakeLists.txt @@ -275,9 +275,6 @@ endif() if(FRAMELESSHELPER_ENABLE_INTELCET) list(APPEND __extra_flags INTELCET) endif() -if(FRAMELESSHELPER_ENABLE_INTELJCC) - list(APPEND __extra_flags INTELJCC) -endif() if(FRAMELESSHELPER_ENABLE_CFGUARD) list(APPEND __extra_flags CFGUARD) endif() diff --git a/src/quick/framelessquickhelper.cpp b/src/quick/framelessquickhelper.cpp index c8c1b1e5..0ca49b34 100644 --- a/src/quick/framelessquickhelper.cpp +++ b/src/quick/framelessquickhelper.cpp @@ -38,7 +38,6 @@ #ifdef Q_OS_WINDOWS # include #endif // Q_OS_WINDOWS -#include #include #include #include @@ -73,6 +72,8 @@ FRAMELESSHELPER_BEGIN_NAMESPACE using namespace Global; +static constexpr const auto kRepaintTimerInterval = 500; + struct FramelessQuickHelperExtraData : public FramelessExtraData { QPointer titleBarItem = nullptr; @@ -137,6 +138,9 @@ FramelessQuickHelperPrivate::FramelessQuickHelperPrivate(FramelessQuickHelper *q return; } q_ptr = q; + repaintTimer.setTimerType(Qt::VeryCoarseTimer); + repaintTimer.setInterval(kRepaintTimerInterval); + connect(&repaintTimer, &QTimer::timeout, this, &FramelessQuickHelperPrivate::doRepaintAllChildren); // Workaround a MOC limitation: we can't emit a signal from the parent class. connect(q_ptr, &FramelessQuickHelper::windowChanged, q_ptr, &FramelessQuickHelper::windowChanged2); } @@ -212,7 +216,7 @@ void FramelessQuickHelperPrivate::attach() data->callbacks->setCursor = [window](const QCursor &cursor) -> void { window->setCursor(cursor); }; data->callbacks->unsetCursor = [window]() -> void { window->unsetCursor(); }; data->callbacks->getWidgetHandle = []() -> QObject * { return nullptr; }; - data->callbacks->forceChildrenRepaint = [this](const int delay) -> void { repaintAllChildren(delay); }; + data->callbacks->forceChildrenRepaint = [this]() -> void { repaintAllChildren(); }; data->callbacks->resetQtGrabbedControl = []() -> bool { return false; }; } @@ -398,48 +402,46 @@ FramelessQuickHelper *FramelessQuickHelperPrivate::findOrCreateFramelessHelper(Q return instance; } -void FramelessQuickHelperPrivate::repaintAllChildren(const quint32 delay) const +void FramelessQuickHelperPrivate::repaintAllChildren() +{ + repaintTimer.start(); +} + +void FramelessQuickHelperPrivate::doRepaintAllChildren() { Q_Q(const FramelessQuickHelper); - QQuickWindow * const window = q->window(); + QQuickWindow *window = q->window(); if (!window) { return; } - const auto update = [window, q]() -> void { #ifdef Q_OS_WINDOWS - // Sync the internal window frame margins with the latest DPI, otherwise - // we will get wrong window sizes after the DPI change. - std::ignore = Utils::updateInternalWindowFrameMargins(window, true); + // Sync the internal window frame margins with the latest DPI, otherwise + // we will get wrong window sizes after the DPI change. + std::ignore = Utils::updateInternalWindowFrameMargins(window, true); #endif // Q_OS_WINDOWS - // No need to repaint the window when it's hidden. - if (!window->isVisible()) { - return; - } - if (!((window->windowState() & (Qt::WindowMinimized | Qt::WindowMaximized | Qt::WindowFullScreen)) || q->isWindowFixedSize())) { - const QSize originalSize = window->size(); - static constexpr const auto margins = QMargins{ 10, 10, 10, 10 }; - window->resize(originalSize.shrunkBy(margins)); - window->resize(originalSize.grownBy(margins)); - window->resize(originalSize); - } - window->requestUpdate(); - const QList items = window->findChildren(); - if (items.isEmpty()) { - return; - } - for (auto &&item : std::as_const(items)) { - // Only items with the "QQuickItem::ItemHasContents" flag enabled are allowed to call "update()". - // And don't repaint the item if it's hidden. - if ((item->flags() & QQuickItem::ItemHasContents) && item->isVisible()) { - item->update(); - } + // No need to repaint the window when it's hidden. + if (!window->isVisible()) { + return; + } + if (!((window->windowState() & (Qt::WindowMinimized | Qt::WindowMaximized | Qt::WindowFullScreen)) || q->isWindowFixedSize())) { + const QSize originalSize = window->size(); + static constexpr const auto margins = QMargins{ 10, 10, 10, 10 }; + window->resize(originalSize.shrunkBy(margins)); + window->resize(originalSize.grownBy(margins)); + window->resize(originalSize); + } + window->requestUpdate(); +#if 0 // Calling QWindow::requestUpdate() should be enough. + const QList items = window->findChildren(); + for (auto &&item : std::as_const(items)) { + // Only items with the "QQuickItem::ItemHasContents" flag enabled are allowed to call "update()". + // And don't repaint the item if it's hidden. + if ((item->flags() & QQuickItem::ItemHasContents) && item->isVisible()) { + item->update(); } - }; - if (delay > 0) { - QTimer::singleShot(delay, this, update); - } else { - update(); } +#endif + repaintTimer.stop(); } quint32 FramelessQuickHelperPrivate::readyWaitTime() const diff --git a/src/widgets/CMakeLists.txt b/src/widgets/CMakeLists.txt index 73d88cc3..a23013ef 100644 --- a/src/widgets/CMakeLists.txt +++ b/src/widgets/CMakeLists.txt @@ -167,9 +167,6 @@ endif() if(FRAMELESSHELPER_ENABLE_INTELCET) list(APPEND __extra_flags INTELCET) endif() -if(FRAMELESSHELPER_ENABLE_INTELJCC) - list(APPEND __extra_flags INTELJCC) -endif() if(FRAMELESSHELPER_ENABLE_CFGUARD) list(APPEND __extra_flags CFGUARD) endif() diff --git a/src/widgets/framelesswidgetshelper.cpp b/src/widgets/framelesswidgetshelper.cpp index ea1b950e..e6866605 100644 --- a/src/widgets/framelesswidgetshelper.cpp +++ b/src/widgets/framelesswidgetshelper.cpp @@ -39,7 +39,6 @@ #include #include #include -#include #include #include #include @@ -72,6 +71,8 @@ FRAMELESSHELPER_BEGIN_NAMESPACE using namespace Global; +static constexpr const auto kRepaintTimerInterval = 500; + struct FramelessWidgetsHelperExtraData : public FramelessExtraData { QPointer titleBarWidget = nullptr; @@ -210,6 +211,9 @@ FramelessWidgetsHelperPrivate::FramelessWidgetsHelperPrivate(FramelessWidgetsHel return; } q_ptr = q; + repaintTimer.setTimerType(Qt::VeryCoarseTimer); + repaintTimer.setInterval(kRepaintTimerInterval); + connect(&repaintTimer, &QTimer::timeout, this, &FramelessWidgetsHelperPrivate::doRepaintAllChildren); } FramelessWidgetsHelperPrivate::~FramelessWidgetsHelperPrivate() = default; @@ -363,26 +367,22 @@ FramelessWidgetsHelper *FramelessWidgetsHelperPrivate::findOrCreateFramelessHelp return instance; } -void FramelessWidgetsHelperPrivate::repaintAllChildren(const quint32 delay) const +void FramelessWidgetsHelperPrivate::repaintAllChildren() +{ + repaintTimer.start(); +} + +void FramelessWidgetsHelperPrivate::doRepaintAllChildren() { if (!window) { return; } - const auto update = [this]() -> void { - forceWidgetRepaint(window); - const QList widgets = window->findChildren(); - if (widgets.isEmpty()) { - return; - } - for (auto &&widget : std::as_const(widgets)) { - forceWidgetRepaint(widget); - } - }; - if (delay > 0) { - QTimer::singleShot(delay, this, update); - } else { - update(); + forceWidgetRepaint(window); + const QList widgets = window->findChildren(); + for (auto &&widget : std::as_const(widgets)) { + forceWidgetRepaint(widget); } + repaintTimer.stop(); } quint32 FramelessWidgetsHelperPrivate::readyWaitTime() const @@ -460,7 +460,7 @@ void FramelessWidgetsHelperPrivate::attach() data->callbacks->setCursor = [this](const QCursor &cursor) -> void { window->setCursor(cursor); }; data->callbacks->unsetCursor = [this]() -> void { window->unsetCursor(); }; data->callbacks->getWidgetHandle = [this]() -> QObject * { return window; }; - data->callbacks->forceChildrenRepaint = [this](const int delay) -> void { repaintAllChildren(delay); }; + data->callbacks->forceChildrenRepaint = [this]() -> void { repaintAllChildren(); }; data->callbacks->resetQtGrabbedControl = []() -> bool { if (qt_button_down) { static constexpr const auto invalidPos = QPoint{ -99999, -99999 };