From ecd08c3cd520b8269396809996d2bf496a3c8901 Mon Sep 17 00:00:00 2001 From: Sine Striker <trueful@163.com> Date: ćšć, 29 2æ 2024 03:26:45 +0800 Subject: [PATCH] minor tweaks --- src/widgets/widgetwindowagent_win.cpp | 2 examples/mainwindow/CMakeLists.txt | 9 -- qmsetup | 2 src/quick/quickwindowagent_win.cpp | 65 ++++++++++++--------- src/core/shared/windows10borderhandler_p.h | 13 ++++ README.md | 6 ++ src/core/contexts/win32windowcontext.cpp | 83 ++++++++++++++------------- 7 files changed, 104 insertions(+), 76 deletions(-) diff --git a/README.md b/README.md index 39a33fa..b47a9a7 100644 --- a/README.md +++ b/README.md @@ -255,6 +255,12 @@ + QWindowKit Internals [TODO] + [FramelessHelper Related](docs/framelesshelper-related.md) + +### Vulnerabilities + ++ Once you have made the window frameless, it will not be able to switch back to the system border. ++ There must not be any child widget with `Qt::WA_NativeWindow` property enabled, otherwise the native features and display may be abnormal. Therefore, do not set any widget that has called `QWidget::winId()` or `QWidget::setAttribute(Qt::WA_NativeWindow)` as a descendant of a frameless window. If you really need to move widgets between different windows, make sure that the widget is not a top-level window and wrap it with a frameless container. + ## TODO + Fix 5.15 window abnormal behavior diff --git a/examples/mainwindow/CMakeLists.txt b/examples/mainwindow/CMakeLists.txt index c74e902..bd16470 100644 --- a/examples/mainwindow/CMakeLists.txt +++ b/examples/mainwindow/CMakeLists.txt @@ -4,11 +4,6 @@ qwk_add_example(${PROJECT_NAME} SOURCES ${_src} mainwindow.qrc ../shared/resources/shared.qrc - QT_LINKS Core Gui Widgets # WebEngineWidgets + QT_LINKS Core Gui Widgets # MultimediaWidgets LINKS QWKWidgets WidgetFrame -) - -set_target_properties(${PROJECT_NAME} PROPERTIES - CXX_STANDARD 17 - CXX_STANDARD_REQUIRED TRUE -) +) \ No newline at end of file diff --git a/qmsetup b/qmsetup index 961f074..513c04b 160000 --- a/qmsetup +++ b/qmsetup @@ -1 +1 @@ -Subproject commit 961f074a5bc9e6cfd0ecaf790be2734d08702a9f +Subproject commit 513c04beb24f77813fedf96c8c9d215f41646a00 diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp index 4fda1eb..811fd44 100644 --- a/src/core/contexts/win32windowcontext.cpp +++ b/src/core/contexts/win32windowcontext.cpp @@ -33,14 +33,6 @@ #include "qwkglobal_p.h" #include "qwkwindowsextra_p.h" -// https://github.com/qt/qtbase/blob/6.6.1/src/plugins/platforms/windows/qwindowswindow.cpp#L2791 -// https://github.com/qt/qtbase/blob/6.6.1/src/plugins/platforms/windows/qwindowswindow.cpp#L3321 -// This issue exists only in Qt 6.6.1, and was introduced by QTBUG-113736 patch and fixed by -// QTBUG-117704 patch. -#if !QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDERS) && QT_VERSION == QT_VERSION_CHECK(6, 6, 1) -# define QTBUG_113736_WORKAROUND -#endif - namespace QWK { // The thickness of an auto-hide taskbar in pixels. @@ -490,17 +482,38 @@ return false; } - // https://github.com/qt/qtbase/blob/e26a87f1ecc40bc8c6aa5b889fce67410a57a702/src/plugins/platforms/windows/qwindowscontext.cpp#L1546 - // Qt needs to refer to the WM_NCCALCSIZE message data that hasn't been processed, so we - // have to process it after Qt acquires the initial data. auto msg = static_cast<const MSG *>(message); - if (msg->message == WM_NCCALCSIZE && lastMessageContext) { - LRESULT res; - if (lastMessageContext->nonClientCalcSizeHandler(msg->hwnd, msg->message, - msg->wParam, msg->lParam, &res)) { - *result = decltype(*result)(res); - return true; + switch (msg->message) { + case WM_NCCALCSIZE: { + // https://github.com/qt/qtbase/blob/e26a87f1ecc40bc8c6aa5b889fce67410a57a702/src/plugins/platforms/windows/qwindowscontext.cpp#L1546 + // Qt needs to refer to the WM_NCCALCSIZE message data that hasn't been + // processed, so we have to process it after Qt acquires the initial data. + if (lastMessageContext) { + LRESULT res; + if (lastMessageContext->nonClientCalcSizeHandler( + msg->hwnd, msg->message, msg->wParam, msg->lParam, &res)) { + *result = decltype(*result)(res); + return true; + } + } + break; } + + // case WM_NCHITTEST: { + // // The child window must return HTTRANSPARENT when processing + // WM_NCHITTEST for + // // the parent window to receive WM_NCHITTEST. + // if (!lastMessageContext) { + // auto rootHWnd = ::GetAncestor(msg->hwnd, GA_ROOT); + // if (rootHWnd != msg->hwnd) { + // if (auto ctx = g_wndProcHash->value(rootHWnd)) { + // *result = HTTRANSPARENT; + // return true; + // } + // } + // } + // break; + // } } return false; } @@ -589,13 +602,15 @@ return ::DefWindowProcW(hWnd, message, wParam, lParam); } + WindowsNativeEventFilter::lastMessageContext = ctx; + const auto &contextCleaner = qScopeGuard([]() { + WindowsNativeEventFilter::lastMessageContext = nullptr; // + }); + // Since Qt does the necessary processing of the WM_NCCALCSIZE message, we need to // forward it right away and process it in our native event filter. if (message == WM_NCCALCSIZE) { - WindowsNativeEventFilter::lastMessageContext = ctx; - LRESULT result = ::CallWindowProcW(g_qtWindowProc, hWnd, message, wParam, lParam); - WindowsNativeEventFilter::lastMessageContext = nullptr; - return result; + return ::CallWindowProcW(g_qtWindowProc, hWnd, message, wParam, lParam); } // Try hooked procedure and save result @@ -613,20 +628,10 @@ } static inline void addManagedWindow(QWindow *window, HWND hWnd, Win32WindowContext *ctx) { -#ifndef QTBUG_113736_WORKAROUND - const auto margins = [](HWND hWnd) -> QMargins { - const auto titleBarHeight = int(getTitleBarHeight(hWnd)); - if (isSystemBorderEnabled()) { - return {0, -titleBarHeight, 0, 0}; - } else { - const auto frameSize = int(getResizeBorderThickness(hWnd)); - return {-frameSize, -titleBarHeight, -frameSize, -frameSize}; - } - }(hWnd); - - // Inform Qt we want and have set custom margins - setInternalWindowFrameMargins(window, margins); -#endif + if (isSystemBorderEnabled()) { + // Inform Qt we want and have set custom margins + setInternalWindowFrameMargins(window, QMargins(0, -int(getTitleBarHeight(hWnd)), 0, 0)); + } // Store original window proc if (!g_qtWindowProc) { @@ -817,10 +822,10 @@ mouseLeaveBlocked = false; lastHitTestResult = WindowPart::Outside; -#ifdef QTBUG_113736_WORKAROUND - m_delegate->setWindowFlags(m_host, - m_delegate->getWindowFlags(m_host) | Qt::FramelessWindowHint); -#endif + if (!isSystemBorderEnabled()) { + m_delegate->setWindowFlags(m_host, m_delegate->getWindowFlags(m_host) | + Qt::FramelessWindowHint); + } // If the original window id is valid, remove all resources related if (oldWinId) { diff --git a/src/core/shared/windows10borderhandler_p.h b/src/core/shared/windows10borderhandler_p.h index 7fc9760..cf530e8 100644 --- a/src/core/shared/windows10borderhandler_p.h +++ b/src/core/shared/windows10borderhandler_p.h @@ -1,6 +1,19 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + #ifndef WINDOWS10BORDERHANDLER_P_H #define WINDOWS10BORDERHANDLER_P_H +// +// W A R N I N G !!! +// ----------------- +// +// This file is not part of the QWindowKit API. It is used purely as an +// implementation detail. This header file may change from version to +// version without notice, or may even be removed. +// + #include <QtGui/QWindow> #include <QtGui/QMouseEvent> diff --git a/src/quick/quickwindowagent_win.cpp b/src/quick/quickwindowagent_win.cpp index 011dae0..0ef0d60 100644 --- a/src/quick/quickwindowagent_win.cpp +++ b/src/quick/quickwindowagent_win.cpp @@ -14,8 +14,6 @@ #if QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDERS) - class DeferredDeleteHook; - class BorderItem : public QQuickPaintedItem, public Windows10BorderHandler { public: explicit BorderItem(QQuickItem *parent, AbstractWindowContext *context); @@ -30,29 +28,16 @@ protected: bool sharedEventFilter(QObject *obj, QEvent *event) override; - std::unique_ptr<DeferredDeleteHook> paintHook; - }; +# if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + volatile bool needPaint = false; - class DeferredDeleteHook : public QObject { - public: - DeferredDeleteHook(BorderItem *item) : item(item) { - } - - // Override deferred delete handler - bool event(QEvent *event) override { - if (event->type() == QEvent::DeferredDelete) { - item->drawBorder(); - return true; - } - return QObject::event(event); - } - - BorderItem *item; + private: + void _q_afterSynchronizing(); +# endif }; BorderItem::BorderItem(QQuickItem *parent, AbstractWindowContext *context) - : QQuickPaintedItem(parent), Windows10BorderHandler(context), - paintHook(std::make_unique<DeferredDeleteHook>(this)) { + : QQuickPaintedItem(parent), Windows10BorderHandler(context) { setAntialiasing(true); // We need anti-aliasing to give us better result. setFillColor({}); // Will improve the performance a little bit. setOpaquePainting(true); // Will also improve the performance, we don't draw @@ -66,6 +51,11 @@ setZ(std::numeric_limits<qreal>::max()); // Make sure our fake border always above // everything in the window. + +# if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + connect(window(), &QQuickWindow::afterSynchronizing, this, + &BorderItem::_q_afterSynchronizing, Qt::DirectConnection); +# endif // First update if (context->windowId()) { @@ -84,12 +74,23 @@ void BorderItem::paint(QPainter *painter) { Q_UNUSED(painter) - // https://github.com/qt/qtdeclarative/blob/cc04afbb382fd4b1f65173d71f44d3372c47b0e1/src/quick/scenegraph/qsgthreadedrenderloop.cpp#L551 - // https://github.com/qt/qtdeclarative/blob/cc04afbb382fd4b1f65173d71f44d3372c47b0e1/src/quick/scenegraph/qsgthreadedrenderloop.cpp#L561 - // QCoreApplication must process all DeferredDelay events right away after rendering, we can - // hook it by overriding a specifiy QObject's event handler and draw the native border in - // it. - paintHook->deleteLater(); +# if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + if (auto api = window()->rendererInterface()->graphicsApi(); + !(api == QSGRendererInterface::Direct3D11 || api == QSGRendererInterface::Direct3D12)) { +# endif + QRect rect(QPoint(0, 0), size().toSize()); + QRegion region(rect); + void *args[] = { + painter, + &rect, + ®ion, + }; + ctx->virtual_hook(AbstractWindowContext::DrawWindows10BorderHook, args); +# if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + } else { + needPaint = true; + } +# endif } void BorderItem::itemChange(ItemChange change, const ItemChangeData &data) { @@ -111,7 +112,6 @@ switch (event->type()) { case QEvent::WindowStateChange: { updateGeometry(); - break; } default: break; @@ -119,6 +119,15 @@ return Windows10BorderHandler::sharedEventFilter(obj, event); } +# if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + void BorderItem::_q_afterSynchronizing() { + if (needPaint) { + needPaint = false; + drawBorder(); + } + } +# endif + void QuickWindowAgentPrivate::setupWindows10BorderWorkaround() { // Install painting hook auto ctx = context.get(); diff --git a/src/widgets/widgetwindowagent_win.cpp b/src/widgets/widgetwindowagent_win.cpp index 13af840..706e865 100644 --- a/src/widgets/widgetwindowagent_win.cpp +++ b/src/widgets/widgetwindowagent_win.cpp @@ -84,7 +84,6 @@ bool sharedEventFilter(QObject *obj, QEvent *event) override { Q_UNUSED(obj) - auto window = widget->windowHandle(); switch (event->type()) { case QEvent::Expose: { // Qt will absolutely send a QExposeEvent or QResizeEvent to the QWindow when it @@ -95,6 +94,7 @@ // Since a QExposeEvent will be sent immediately after the QResizeEvent, we can // simply ignore it. auto ee = static_cast<QExposeEvent *>(event); + auto window = widget->windowHandle(); if (window->isExposed() && isNormalWindow() && !ee->region().isNull()) { forwardEventToWindowAndDraw(window, event); return true; -- Gitblit v1.9.1