Yuhang Zhao
2023-12-06 d5cc7b515e8897c94815ebb2d8fe6da675846149
src/core/contexts/win32windowcontext.cpp
@@ -1,19 +1,30 @@
#include "win32windowcontext_p.h"
#include "qwkcoreglobal_p.h"
#include <optional>
#include <QtCore/QHash>
#include <QtCore/QAbstractNativeEventFilter>
#include <QtCore/QCoreApplication>
#include <QtCore/QOperatingSystemVersion>
#include <QtCore/QScopeGuard>
#include <QtGui/QGuiApplication>
#include <QtCore/private/qsystemlibrary_p.h>
#include <QtGui/private/qhighdpiscaling_p.h>
#include "qwkcoreglobal_p.h"
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
#  include <QtGui/private/qguiapplication_p.h>
#endif
#include <QtGui/qpa/qplatformwindow.h>
#if QT_VERSION < QT_VERSION_CHECK(6, 2, 0)
#  include <QtGui/qpa/qplatformnativeinterface.h>
#else
#  include <QtGui/qpa/qplatformwindow_p.h>
#endif
#include <shellscalingapi.h>
#include <dwmapi.h>
Q_DECLARE_METATYPE(QMargins)
namespace QWK {
@@ -25,8 +36,15 @@
    static WNDPROC g_qtWindowProc = nullptr; // Original Qt window proc function
    static struct QWK_Hook {
        QWK_Hook() {
            qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
        }
    } g_hook{};
    struct DynamicApis {
        decltype(&::DwmFlush) pDwmFlush = nullptr;
        decltype(&::DwmIsCompositionEnabled) pDwmIsCompositionEnabled = nullptr;
        decltype(&::GetDpiForWindow) pGetDpiForWindow = nullptr;
        decltype(&::GetSystemMetricsForDpi) pGetSystemMetricsForDpi = nullptr;
        decltype(&::GetDpiForMonitor) pGetDpiForMonitor = nullptr;
@@ -44,6 +62,8 @@
            QSystemLibrary dwmapi(QStringLiteral("dwmapi.dll"));
            pDwmFlush = reinterpret_cast<decltype(pDwmFlush)>(dwmapi.resolve("DwmFlush"));
            pDwmIsCompositionEnabled = reinterpret_cast<decltype(pDwmIsCompositionEnabled)>(
                dwmapi.resolve("DwmIsCompositionEnabled"));
        }
        ~DynamicApis() = default;
@@ -137,6 +157,12 @@
        return hwnd2str(reinterpret_cast<WId>(hwnd));
    }
    static inline bool isWin8OrGreater() {
        static const bool result =
            QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8;
        return result;
    }
    static inline bool isWin8Point1OrGreater() {
        static const bool result =
            QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8_1;
@@ -149,13 +175,35 @@
        return result;
    }
    static inline bool isDwmCompositionEnabled() {
        if (isWin8OrGreater()) {
            return true;
        }
        const DynamicApis &apis = DynamicApis::instance();
        if (!apis.pDwmIsCompositionEnabled) {
            return false;
        }
        BOOL enabled = FALSE;
        return SUCCEEDED(apis.pDwmIsCompositionEnabled(&enabled)) && enabled;
    }
    static inline void triggerFrameChange(HWND hwnd) {
        Q_ASSERT(hwnd);
        if (!hwnd) {
            return;
        }
        ::SetWindowPos(hwnd, nullptr, 0, 0, 0, 0,
                       SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER |
                           SWP_FRAMECHANGED);
    }
    static inline quint32 getDpiForWindow(HWND hwnd) {
        Q_ASSERT(hwnd);
        if (!hwnd) {
            return USER_DEFAULT_SCREEN_DPI;
            return 0;
        }
        const DynamicApis &apis = DynamicApis::instance();
        if (apis.pGetDpiForWindow) {         // Win10
        if (apis.pGetDpiForWindow) { // Win10
            return apis.pGetDpiForWindow(hwnd);
        } else if (apis.pGetDpiForMonitor) { // Win8.1
            HMONITOR monitor = ::MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
@@ -187,10 +235,59 @@
        }
    }
    static inline std::optional<MONITORINFOEXW> getMonitorForWindow(HWND hwnd) {
    static inline quint32 getTitleBarHeight(HWND hwnd) {
        Q_ASSERT(hwnd);
        if (!hwnd) {
            return std::nullopt;
            return 0;
        }
        const auto captionHeight = [hwnd]() -> int {
            const DynamicApis &apis = DynamicApis::instance();
            if (apis.pGetSystemMetricsForDpi) {
                const quint32 dpi = getDpiForWindow(hwnd);
                return apis.pGetSystemMetricsForDpi(SM_CYCAPTION, dpi);
            } else {
                return ::GetSystemMetrics(SM_CYCAPTION);
            }
        }();
        return captionHeight + getResizeBorderThickness(hwnd);
    }
    static inline void updateInternalWindowFrameMargins(HWND hwnd, QWindow *window) {
        Q_ASSERT(hwnd);
        Q_ASSERT(window);
        if (!hwnd || !window) {
            return;
        }
        const auto margins = [hwnd]() -> QMargins {
            const int titleBarHeight = getTitleBarHeight(hwnd);
            if (isWin10OrGreater()) {
                return {0, -titleBarHeight, 0, 0};
            } else {
                const int frameSize = getResizeBorderThickness(hwnd);
                return {-frameSize, -titleBarHeight, -frameSize, -frameSize};
            }
        }();
        const QVariant marginsVar = QVariant::fromValue(margins);
        window->setProperty("_q_windowsCustomMargins", marginsVar);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
        if (QPlatformWindow *platformWindow = window->handle()) {
            if (const auto ni = QGuiApplication::platformNativeInterface()) {
                ni->setWindowProperty(platformWindow, QStringLiteral("_q_windowsCustomMargins"),
                                      marginsVar);
            }
        }
#else
        if (const auto platformWindow =
                dynamic_cast<QNativeInterface::Private::QWindowsWindow *>(window->handle())) {
            platformWindow->setCustomMargins(margins);
        }
#endif
    }
    static inline MONITORINFOEXW getMonitorForWindow(HWND hwnd) {
        Q_ASSERT(hwnd);
        if (!hwnd) {
            return {};
        }
        // Use "MONITOR_DEFAULTTONEAREST" here so that we can still get the correct
        // monitor even if the window is minimized.
@@ -201,6 +298,20 @@
        return monitorInfo;
    };
    static inline void moveToDesktopCenter(HWND hwnd) {
        Q_ASSERT(hwnd);
        if (!hwnd) {
            return;
        }
        const auto monitorInfo = getMonitorForWindow(hwnd);
        RECT windowRect{};
        ::GetWindowRect(hwnd, &windowRect);
        const auto newX = (RECT_WIDTH(monitorInfo.rcMonitor) - RECT_WIDTH(windowRect)) / 2;
        const auto newY = (RECT_HEIGHT(monitorInfo.rcMonitor) - RECT_HEIGHT(windowRect)) / 2;
        ::SetWindowPos(hwnd, nullptr, newX, newY, 0, 0,
                       SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
    }
    static inline bool isFullScreen(HWND hwnd) {
        Q_ASSERT(hwnd);
        if (!hwnd) {
@@ -208,13 +319,11 @@
        }
        RECT windowRect{};
        ::GetWindowRect(hwnd, &windowRect);
        const std::optional<MONITORINFOEXW> mi = getMonitorForWindow(hwnd);
        // Compare to the full area of the screen, not the work area.
        return (windowRect == mi.value_or(MONITORINFOEXW{}).rcMonitor);
        return (windowRect == getMonitorForWindow(hwnd).rcMonitor);
    }
    static inline bool isWindowNoState(HWND hwnd)
    {
    static inline bool isWindowNoState(HWND hwnd) {
        Q_ASSERT(hwnd);
        if (!hwnd) {
            return false;
@@ -266,13 +375,13 @@
            case HTBORDER:
                return Win32WindowContext::FixedBorder;
            default:
                break;
                break; // unreachable
        }
        return Win32WindowContext::Outside;
    }
    static bool isValidWindow(HWND hWnd, bool checkVisible, bool checkTopLevel) {
        if (::IsWindow(hWnd) == FALSE) {
        if (!::IsWindow(hWnd)) {
            return false;
        }
        const LONG_PTR styles = ::GetWindowLongPtrW(hWnd, GWL_STYLE);
@@ -283,15 +392,15 @@
        if (exStyles & WS_EX_TOOLWINDOW) {
            return false;
        }
        RECT rect = {0, 0, 0, 0};
        if (::GetWindowRect(hWnd, &rect) == FALSE) {
        RECT rect{};
        if (!::GetWindowRect(hWnd, &rect)) {
            return false;
        }
        if ((rect.left >= rect.right) || (rect.top >= rect.bottom)) {
            return false;
        }
        if (checkVisible) {
            if (::IsWindowVisible(hWnd) == FALSE) {
            if (!::IsWindowVisible(hWnd)) {
                return false;
            }
        }
@@ -314,11 +423,14 @@
    public:
        bool nativeEventFilter(const QByteArray &eventType, void *message,
                               QT_NATIVE_EVENT_RESULT_TYPE *result) override {
            Q_UNUSED(eventType);
            // It has been observed that the pointer that Qt gives us is sometimes null on some
            // machines. We need to guard against it in such scenarios.
            if (!result) {
                return false;
            }
            if (lastMessageHandled) {
                *result = static_cast<QT_NATIVE_EVENT_RESULT_TYPE>(lastMessageResult);
                return true;
@@ -332,11 +444,11 @@
        static inline void install() {
            instance = new WindowsNativeEventFilter();
            qApp->installNativeEventFilter(instance);
            installNativeEventFilter(instance);
        }
        static inline void uninstall() {
            qApp->removeNativeEventFilter(instance);
            removeNativeEventFilter(instance);
            delete instance;
            instance = nullptr;
        }
@@ -419,13 +531,16 @@
        // TODO: Determine whether to show system menu
        // ...
        // Since Qt does the necessary processing of the message afterward, we still need to
        // Since Qt does the necessary processing of the WM_NCCALCSIZE afterward, we still need to
        // continue dispatching it.
        if (handled && message != WM_NCCALCSIZE) {
            return result;
        }
        return ::CallWindowProcW(g_qtWindowProc, hWnd, message, wParam, lParam);
    }
    Win32WindowContext::Win32WindowContext(QWindow *window, WindowItemDelegate *delegate)
        : AbstractWindowContext(window, delegate) {
    Win32WindowContext::Win32WindowContext(QObject *host, WindowItemDelegate *delegate)
        : AbstractWindowContext(host, delegate) {
    }
    Win32WindowContext::~Win32WindowContext() {
@@ -441,10 +556,16 @@
    }
    bool Win32WindowContext::setup() {
        auto winId = m_windowHandle->winId();
        if (!m_windowHandle) {
            return false;
        }
        // Install window hook
        auto winId = m_windowHandle->winId();
        auto hWnd = reinterpret_cast<HWND>(winId);
        // Inform Qt we want and have set custom margins
        updateInternalWindowFrameMargins(hWnd, m_windowHandle);
        // Store original window proc
        if (!g_qtWindowProc) {
@@ -514,7 +635,7 @@
        const auto &get = [](const int virtualKey) -> bool {
            return (::GetAsyncKeyState(virtualKey) < 0);
        };
        const bool buttonSwapped = (::GetSystemMetrics(SM_SWAPBUTTON) != FALSE);
        const bool buttonSwapped = ::GetSystemMetrics(SM_SWAPBUTTON);
        if (get(VK_LBUTTON)) {
            result |= (buttonSwapped ? MK_RBUTTON : MK_LBUTTON);
        }
@@ -657,9 +778,10 @@
                    // actually lost the hover state. So we filter out these superfluous mouse leave
                    // events here to avoid this issue.
                    DWORD dwScreenPos = ::GetMessagePos();
                    POINT screenPoint{GET_X_LPARAM(dwScreenPos), GET_Y_LPARAM(dwScreenPos)};
                    ::ScreenToClient(hWnd, &screenPoint);
                    QPoint qtScenePos =
                        fromNativeLocalPosition(m_windowHandle, QPoint(GET_X_LPARAM(dwScreenPos),
                                                                       GET_Y_LPARAM(dwScreenPos)));
                        fromNativeLocalPosition(m_windowHandle, {screenPoint.x, screenPoint.y});
                    auto dummy = CoreWindowAgent::Unknown;
                    if (isInSystemButtons(qtScenePos, &dummy)) {
                        // We must record whether the last WM_MOUSELEAVE was filtered, because if
@@ -784,6 +906,18 @@
    bool Win32WindowContext::customWindowHandler(HWND hWnd, UINT message, WPARAM wParam,
                                                 LPARAM lParam, LRESULT *result) {
        switch (message) {
            case WM_SHOWWINDOW: {
                if (!centered) {
                    // If wParam is TRUE, the window is being shown.
                    // If lParam is zero, the message was sent because of a call to the ShowWindow
                    // function.
                    if (wParam && lParam == 0) {
                        centered = true;
                        moveToDesktopCenter(hWnd);
                    }
                }
                break;
            }
            case WM_NCCALCSIZE: {
                // Windows是根据这个消息的返回值来设置窗口的客户区(窗口中真正显示的内容)
                // 和非客户区(标题栏、窗口边框、菜单栏和状态栏等Windows系统自行提供的部分
@@ -871,8 +1005,8 @@
                // simply return 0, which means "preserve the entire old client area
                // and align it with the upper-left corner of our new client area".
                const auto clientRect =
                    ((wParam == FALSE) ? reinterpret_cast<LPRECT>(lParam)
                                       : &(reinterpret_cast<LPNCCALCSIZE_PARAMS>(lParam))->rgrc[0]);
                    wParam ? &(reinterpret_cast<LPNCCALCSIZE_PARAMS>(lParam))->rgrc[0]
                           : reinterpret_cast<LPRECT>(lParam);
                if (isWin10OrGreater()) {
                    // Store the original top margin before the default window procedure applies the
                    // default frame.
@@ -938,9 +1072,7 @@
                        // we have to use another way to judge this if we are running
                        // on Windows 7 or Windows 8.
                        if (isWin8Point1OrGreater()) {
                            const std::optional<MONITORINFOEXW> monitorInfo =
                                getMonitorForWindow(hWnd);
                            const RECT monitorRect = monitorInfo.value().rcMonitor;
                            const RECT monitorRect = getMonitorForWindow(hWnd).rcMonitor;
                            // This helper can be used to determine if there's an
                            // auto-hide taskbar on the given edge of the monitor
                            // we're currently on.
@@ -1008,7 +1140,7 @@
                // of the upper-left non-client area. It's confirmed that this issue exists
                // from Windows 7 to Windows 10. Not tested on Windows 11 yet. Don't know
                // whether it exists on Windows XP to Windows Vista or not.
                *result = wParam == FALSE ? FALSE : WVR_REDRAW;
                *result = wParam ? WVR_REDRAW : FALSE;
                return true;
            }
            case WM_NCHITTEST: {
@@ -1087,45 +1219,50 @@
                // window can behave just like a normal Win32 window even if we now
                // doesn't have a title bar at all. Most importantly, the flicker and
                // jitter during window resizing is totally gone now. The disadvantage
                // is we have to draw a top frame border ourself. Previously I thought
                // is we have to draw a top frame border ourselves. Previously I thought
                // we have to do the black magic in WM_PAINT just like what Windows
                // Terminal does, however, later I found that if we choose a proper
                // color, our homemade top border can almost have exactly the same
                // appearance with the system's one.
                const auto nativeGlobalPos = POINT{ GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
                [[maybe_unused]] const auto &hitTestRecorder = qScopeGuard([this, result]() {
                    lastHitTestResult = getHitWindowPart(int(*result)); //
                });
                POINT nativeGlobalPos{GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
                POINT nativeLocalPos = nativeGlobalPos;
                ::ScreenToClient(hWnd, &nativeLocalPos);
                auto clientRect = RECT{ 0, 0, 0, 0 };
                RECT clientRect{0, 0, 0, 0};
                ::GetClientRect(hWnd, &clientRect);
                const auto clientWidth = RECT_WIDTH(clientRect);
                const auto clientHeight = RECT_HEIGHT(clientRect);
                auto clientWidth = RECT_WIDTH(clientRect);
                auto clientHeight = RECT_HEIGHT(clientRect);
                const QPoint qtScenePos = fromNativeLocalPosition(m_windowHandle, QPoint(nativeLocalPos.x, nativeLocalPos.y));
                QPoint qtScenePos = fromNativeLocalPosition(
                    m_windowHandle, QPoint(nativeLocalPos.x, nativeLocalPos.y));
                const bool isFixedSize = /*isWindowFixedSize()*/false; // ### FIXME
                const bool isTitleBar = isInTitleBarDraggableArea(qtScenePos);
                const bool dontOverrideCursor = false; // ### TODO
                bool isFixedSize = m_delegate->isHostSizeFixed(m_host);
                bool isTitleBar = isInTitleBarDraggableArea(qtScenePos);
                bool dontOverrideCursor = false; // ### TODO
                CoreWindowAgent::SystemButton sysButtonType = CoreWindowAgent::Unknown;
                if (!isFixedSize && isInSystemButtons(qtScenePos, &sysButtonType)) {
                    // Firstly, we set the hit test result to a default value to be able to detect whether we
                    // have changed it or not afterwards.
                    // Firstly, we set the hit test result to a default value to be able to detect
                    // whether we have changed it or not afterwards.
                    *result = HTNOWHERE;
                    // Even if the mouse is inside the chrome button area now, we should still allow the user
                    // to be able to resize the window with the top or right window border, this is also the
                    // normal behavior of a native Win32 window (but only when the window is not maximized/
                    // fullscreened/minimized, of course).
                    // Even if the mouse is inside the chrome button area now, we should still allow
                    // the user to be able to resize the window with the top or right window border,
                    // this is also the normal behavior of a native Win32 window (but only when the
                    // window is not maximized/fullscreen/minimized, of course).
                    if (isWindowNoState(hWnd)) {
                        static constexpr const int kBorderSize = 2;
                        const bool isTop = (nativeLocalPos.y <= kBorderSize);
                        const bool isRight = (nativeLocalPos.x >= (clientWidth - kBorderSize));
                        bool isTop = (nativeLocalPos.y <= kBorderSize);
                        bool isRight = (nativeLocalPos.x >= (clientWidth - kBorderSize));
                        if (isTop || isRight) {
                            if (dontOverrideCursor) {
                                // The user doesn't want the window to be resized, so we tell Windows we are
                                // in the client area so that the controls beneath the mouse cursor can still
                                // be hovered or clicked.
                                // The user doesn't want the window to be resized, so we tell
                                // Windows we are in the client area so that the controls beneath
                                // the mouse cursor can still be hovered or clicked.
                                *result = (isTitleBar ? HTCAPTION : HTCLIENT);
                            } else {
                                if (isTop && isRight) {
@@ -1139,8 +1276,9 @@
                        }
                    }
                    if (*result == HTNOWHERE) {
                        // OK, we are now really inside one of the chrome buttons, tell Windows the exact role of our button.
                        // The Snap Layout feature introduced in Windows 11 won't work without this.
                        // OK, we are now really inside one of the chrome buttons, tell Windows the
                        // exact role of our button. The Snap Layout feature introduced in Windows
                        // 11 won't work without this.
                        switch (sysButtonType) {
                            case CoreWindowAgent::WindowIcon:
                                *result = HTSYSMENU;
@@ -1159,33 +1297,38 @@
                                break;
                            case CoreWindowAgent::Unknown:
                                break;
                            default:
                                break; // unreachable
                        }
                    }
                    if (*result == HTNOWHERE) {
                        // OK, it seems we are not inside the window resize area, nor inside the chrome buttons,
                        // tell Windows we are in the client area to let Qt handle this event.
                        // OK, it seems we are not inside the window resize area, nor inside the
                        // chrome buttons, tell Windows we are in the client area to let Qt handle
                        // this event.
                        *result = HTCLIENT;
                    }
                    return true;
                }
                // OK, we are not inside any chrome buttons, try to find out which part of the window
                // are we hitting.
                // OK, we are not inside any chrome buttons, try to find out which part of the
                // window are we hitting.
                const bool max = IsMaximized(hWnd);
                const bool full = isFullScreen(hWnd);
                const int frameSize = getResizeBorderThickness(hWnd);
                const bool isTop = (nativeLocalPos.y < frameSize);
                bool max = IsMaximized(hWnd);
                bool full = isFullScreen(hWnd);
                int frameSize = getResizeBorderThickness(hWnd);
                bool isTop = (nativeLocalPos.y < frameSize);
                if (isWin10OrGreater()) {
                    // This will handle the left, right and bottom parts of the frame
                    // because we didn't change them.
                    const LRESULT originalHitTestResult = ::DefWindowProcW(hWnd, WM_NCHITTEST, 0, lParam);
                    LRESULT originalHitTestResult = ::DefWindowProcW(hWnd, WM_NCHITTEST, 0, lParam);
                    if (originalHitTestResult != HTCLIENT) {
                        // Even if the window is not resizable, we still can't return HTCLIENT here because
                        // when we enter this code path, it means the mouse cursor is outside the window,
                        // that is, the three transparent window resize area. Returning HTCLIENT will confuse
                        // Windows, we can't put our controls there anyway.
                        *result = ((isFixedSize || dontOverrideCursor) ? HTBORDER : originalHitTestResult);
                        // Even if the window is not resizable, we still can't return HTCLIENT here
                        // because when we enter this code path, it means the mouse cursor is
                        // outside the window, that is, the three transparent window resize area.
                        // Returning HTCLIENT will confuse Windows, we can't put our controls there
                        // anyway.
                        *result = ((isFixedSize || dontOverrideCursor) ? HTBORDER
                                                                       : originalHitTestResult);
                        return true;
                    }
                    if (full) {
@@ -1205,7 +1348,9 @@
                        // Return HTCLIENT instead of HTBORDER here, because the mouse is
                        // inside our homemade title bar now, return HTCLIENT to let our
                        // title bar can still capture mouse events.
                        *result = ((isFixedSize || dontOverrideCursor) ? (isTitleBar ? HTCAPTION : HTCLIENT) : HTTOP);
                        *result = ((isFixedSize || dontOverrideCursor)
                                       ? (isTitleBar ? HTCAPTION : HTCLIENT)
                                       : HTTOP);
                        return true;
                    }
                    if (isTitleBar) {
@@ -1213,6 +1358,7 @@
                        return true;
                    }
                    *result = HTCLIENT;
                    return true;
                } else {
                    if (full) {
                        *result = HTCLIENT;
@@ -1274,13 +1420,73 @@
                        return true;
                    }
                    *result = HTCLIENT;
                    return true;
                }
                return true;
            }
            default:
                break;
        }
        if (!isWin10OrGreater()) {
            switch (message) {
                case WM_NCUAHDRAWCAPTION:
                case WM_NCUAHDRAWFRAME: {
                    // These undocumented messages are sent to draw themed window
                    // borders. Block them to prevent drawing borders over the client
                    // area.
                    *result = FALSE;
                    return true;
                }
                case WM_NCPAINT: {
                    // 边框阴影处于非客户区的范围,因此如果直接阻止非客户区的绘制,会导致边框阴影丢失
                    if (!isDwmCompositionEnabled()) {
                        // Only block WM_NCPAINT when DWM composition is disabled. If
                        // it's blocked when DWM composition is enabled, the frame
                        // shadow won't be drawn.
                        *result = FALSE;
                        return true;
                    } else {
                        break;
                    }
                }
                case WM_NCACTIVATE: {
                    if (isDwmCompositionEnabled()) {
                        // DefWindowProc won't repaint the window border if lParam (normally a HRGN)
                        // is -1. See the following link's "lParam" section:
                        // https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-ncactivate
                        // Don't use "*result = 0" here, otherwise the window won't respond to the
                        // window activation state change.
                        *result = ::DefWindowProcW(hWnd, WM_NCACTIVATE, wParam, -1);
                    } else {
                        if (wParam) {
                            *result = FALSE;
                        } else {
                            *result = TRUE;
                        }
                    }
                    return true;
                }
                case WM_SETICON:
                case WM_SETTEXT: {
                    // Disable painting while these messages are handled to prevent them
                    // from drawing a window caption over the client area.
                    const auto oldStyle = static_cast<DWORD>(::GetWindowLongPtrW(hWnd, GWL_STYLE));
                    // Prevent Windows from drawing the default title bar by temporarily
                    // toggling the WS_VISIBLE style.
                    const DWORD newStyle = (oldStyle & ~WS_VISIBLE);
                    ::SetWindowLongPtrW(hWnd, GWL_STYLE, static_cast<LONG_PTR>(newStyle));
                    triggerFrameChange(hWnd);
                    const LRESULT originalResult = ::DefWindowProcW(hWnd, message, wParam, lParam);
                    ::SetWindowLongPtrW(hWnd, GWL_STYLE, static_cast<LONG_PTR>(oldStyle));
                    triggerFrameChange(hWnd);
                    *result = originalResult;
                    return true;
                }
                default:
                    break;
            }
        }
        return false;
    }
}
}