/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the plugins of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qwindowswindow.h" #include "qwindowscontext.h" #include "qwindowsdrag.h" #include "qwindowsscreen.h" #include "qwindowsintegration.h" #include "qwindowsnativeinterface.h" #if QT_CONFIG(dynamicgl) # include "qwindowsglcontext.h" #else # include "qwindowsopenglcontext.h" #endif #ifdef QT_NO_CURSOR # include "qwindowscursor.h" #endif #include #include #include #include #include #include #include // QWINDOWSIZE_MAX #include #include #include #include #include QT_BEGIN_NAMESPACE enum { defaultWindowWidth = 160, defaultWindowHeight = 160 }; Q_GUI_EXPORT HICON qt_pixmapToWinHICON(const QPixmap &); static QByteArray debugWinStyle(DWORD style) { QByteArray rc = "0x"; rc += QByteArray::number(qulonglong(style), 16); if (style & WS_POPUP) rc += " WS_POPUP"; if (style & WS_CHILD) rc += " WS_CHILD"; if (style & WS_OVERLAPPED) rc += " WS_OVERLAPPED"; if (style & WS_CLIPSIBLINGS) rc += " WS_CLIPSIBLINGS"; if (style & WS_CLIPCHILDREN) rc += " WS_CLIPCHILDREN"; if (style & WS_THICKFRAME) rc += " WS_THICKFRAME"; if (style & WS_DLGFRAME) rc += " WS_DLGFRAME"; if (style & WS_SYSMENU) rc += " WS_SYSMENU"; if (style & WS_MINIMIZEBOX) rc += " WS_MINIMIZEBOX"; if (style & WS_MAXIMIZEBOX) rc += " WS_MAXIMIZEBOX"; return rc; } static QByteArray debugWinExStyle(DWORD exStyle) { QByteArray rc = "0x"; rc += QByteArray::number(qulonglong(exStyle), 16); if (exStyle & WS_EX_TOOLWINDOW) rc += " WS_EX_TOOLWINDOW"; if (exStyle & WS_EX_CONTEXTHELP) rc += " WS_EX_CONTEXTHELP"; if (exStyle & WS_EX_LAYERED) rc += " WS_EX_LAYERED"; if (exStyle & WS_EX_DLGMODALFRAME) rc += " WS_EX_DLGMODALFRAME"; return rc; } static QByteArray debugWinSwpPos(UINT flags) { QByteArray rc = "0x"; rc += QByteArray::number(flags, 16); if (flags & SWP_FRAMECHANGED) rc += " SWP_FRAMECHANGED"; if (flags & SWP_HIDEWINDOW) rc += " SWP_HIDEWINDOW"; if (flags & SWP_NOACTIVATE) rc += " SWP_NOACTIVATE"; if (flags & SWP_NOCOPYBITS) rc += " SWP_NOCOPYBITS"; if (flags & SWP_NOMOVE) rc += " SWP_NOMOVE"; if (flags & SWP_NOOWNERZORDER) rc += " SWP_NOOWNERZORDER"; if (flags & SWP_NOREDRAW) rc += " SWP_NOREDRAW"; if (flags & SWP_NOSENDCHANGING) rc += " SWP_NOSENDCHANGING"; if (flags & SWP_NOSIZE) rc += " SWP_NOSIZE"; if (flags & SWP_NOZORDER) rc += " SWP_NOZORDER"; if (flags & SWP_SHOWWINDOW) rc += " SWP_SHOWWINDOW"; return rc; } static inline QSize qSizeOfRect(const RECT &rect) { return QSize(rect.right -rect.left, rect.bottom - rect.top); } static inline QRect qrectFromRECT(const RECT &rect) { return QRect(QPoint(rect.left, rect.top), qSizeOfRect(rect)); } static inline RECT RECTfromQRect(const QRect &rect) { const int x = rect.left(); const int y = rect.top(); RECT result = { x, y, x + rect.width(), y + rect.height() }; return result; } #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug d, const RECT &r) { QDebugStateSaver saver(d); d.nospace(); d << "RECT(left=" << r.left << ", top=" << r.top << ", right=" << r.right << ", bottom=" << r.bottom << " (" << r.right - r.left << 'x' << r.bottom - r.top << "))"; return d; } QDebug operator<<(QDebug d, const POINT &p) { d << p.x << ',' << p.y; return d; } QDebug operator<<(QDebug d, const WINDOWPOS &wp) { QDebugStateSaver saver(d); d.nospace(); d.noquote(); d << "WINDOWPOS(flags=" << debugWinSwpPos(wp.flags) << ", hwnd=" << wp.hwnd << ", hwndInsertAfter=" << wp.hwndInsertAfter << ", x=" << wp.x << ", y=" << wp.y << ", cx=" << wp.cx << ", cy=" << wp.cy << ')'; return d; } QDebug operator<<(QDebug d, const NCCALCSIZE_PARAMS &p) { QDebugStateSaver saver(d); d.nospace(); d << "NCCALCSIZE_PARAMS(rgrc=[" << p.rgrc[0] << ' ' << p.rgrc[1] << ' ' << p.rgrc[2] << "], lppos=" << *p.lppos << ')'; return d; } QDebug operator<<(QDebug d, const MINMAXINFO &i) { QDebugStateSaver saver(d); d.nospace(); d << "MINMAXINFO maxSize=" << i.ptMaxSize.x << ',' << i.ptMaxSize.y << " maxpos=" << i.ptMaxPosition.x << ',' << i.ptMaxPosition.y << " mintrack=" << i.ptMinTrackSize.x << ',' << i.ptMinTrackSize.y << " maxtrack=" << i.ptMaxTrackSize.x << ',' << i.ptMaxTrackSize.y; return d; } QDebug operator<<(QDebug d, const WINDOWPLACEMENT &wp) { QDebugStateSaver saver(d); d.nospace(); d.noquote(); d << "WINDOWPLACEMENT(flags=0x" << hex << wp.flags << dec << ", showCmd=" << wp.showCmd << ", ptMinPosition=" << wp.ptMinPosition << ", ptMaxPosition=" << wp.ptMaxPosition << ", rcNormalPosition=" << wp.rcNormalPosition; return d; } #endif // !QT_NO_DEBUG_STREAM // QTBUG-43872, for windows that do not have WS_EX_TOOLWINDOW set, WINDOWPLACEMENT // is in workspace/available area coordinates. static QPoint windowPlacementOffset(HWND hwnd, const QPoint &point) { if (GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_TOOLWINDOW) return QPoint(0, 0); const QWindowsScreenManager &screenManager = QWindowsContext::instance()->screenManager(); const QWindowsScreen *screen = screenManager.screens().size() == 1 ? screenManager.screens().constFirst() : screenManager.screenAtDp(point); if (screen) return screen->availableGeometry().topLeft() - screen->geometry().topLeft(); return QPoint(0, 0); } // Return the frame geometry relative to the parent // if there is one. static inline QRect frameGeometry(HWND hwnd, bool topLevel) { RECT rect = { 0, 0, 0, 0 }; if (topLevel) { WINDOWPLACEMENT windowPlacement; windowPlacement.length = sizeof(WINDOWPLACEMENT); GetWindowPlacement(hwnd, &windowPlacement); if (windowPlacement.showCmd == SW_SHOWMINIMIZED) { const QRect result = qrectFromRECT(windowPlacement.rcNormalPosition); return result.translated(windowPlacementOffset(hwnd, result.topLeft())); } } GetWindowRect(hwnd, &rect); // Screen coordinates. const HWND parent = GetParent(hwnd); if (parent && !topLevel) { const int width = rect.right - rect.left; const int height = rect.bottom - rect.top; POINT leftTop = { rect.left, rect.top }; ScreenToClient(parent, &leftTop); rect.left = leftTop.x; rect.top = leftTop.y; rect.right = leftTop.x + width; rect.bottom = leftTop.y + height; } return qrectFromRECT(rect); } // Return the visibility of the Window (except full screen since it is not a window state). static QWindow::Visibility windowVisibility_sys(HWND hwnd) { if (!IsWindowVisible(hwnd)) return QWindow::Hidden; WINDOWPLACEMENT windowPlacement; windowPlacement.length = sizeof(WINDOWPLACEMENT); if (GetWindowPlacement(hwnd, &windowPlacement)) { switch (windowPlacement.showCmd) { case SW_SHOWMINIMIZED: case SW_MINIMIZE: case SW_FORCEMINIMIZE: return QWindow::Minimized; case SW_SHOWMAXIMIZED: return QWindow::Maximized; default: break; } } return QWindow::Windowed; } static inline bool windowIsOpenGL(const QWindow *w) { switch (w->surfaceType()) { case QSurface::OpenGLSurface: return true; case QSurface::RasterGLSurface: return qt_window_private(const_cast(w))->compositing; default: return false; } } static bool applyBlurBehindWindow(HWND hwnd) { BOOL compositionEnabled; if (DwmIsCompositionEnabled(&compositionEnabled) != S_OK) return false; DWM_BLURBEHIND blurBehind = {0, 0, 0, 0}; if (compositionEnabled) { blurBehind.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION; blurBehind.fEnable = TRUE; blurBehind.hRgnBlur = CreateRectRgn(0, 0, -1, -1); } else { blurBehind.dwFlags = DWM_BB_ENABLE; blurBehind.fEnable = FALSE; } const bool result = DwmEnableBlurBehindWindow(hwnd, &blurBehind) == S_OK; if (blurBehind.hRgnBlur) DeleteObject(blurBehind.hRgnBlur); return result; } // from qwidget_win.cpp, pass flags separately in case they have been "autofixed". static bool shouldShowMaximizeButton(const QWindow *w, Qt::WindowFlags flags) { if ((flags & Qt::MSWindowsFixedSizeDialogHint) || !(flags & Qt::WindowMaximizeButtonHint)) return false; // if the user explicitly asked for the maximize button, we try to add // it even if the window has fixed size. return (flags & Qt::CustomizeWindowHint) || w->maximumSize() == QSize(QWINDOWSIZE_MAX, QWINDOWSIZE_MAX); } // Set the WS_EX_LAYERED flag on a HWND if required. This is required for // translucent backgrounds, not fully opaque windows and for // Qt::WindowTransparentForInput (in combination with WS_EX_TRANSPARENT). bool QWindowsWindow::setWindowLayered(HWND hwnd, Qt::WindowFlags flags, bool hasAlpha, qreal opacity) { const LONG exStyle = GetWindowLong(hwnd, GWL_EXSTYLE); const bool needsLayered = (flags & Qt::WindowTransparentForInput) || (hasAlpha && (flags & Qt::FramelessWindowHint)) || opacity < 1.0; const bool isLayered = (exStyle & WS_EX_LAYERED); if (needsLayered != isLayered) { if (needsLayered) { SetWindowLong(hwnd, GWL_EXSTYLE, exStyle | WS_EX_LAYERED); } else { SetWindowLong(hwnd, GWL_EXSTYLE, exStyle & ~WS_EX_LAYERED); } } return needsLayered; } static void setWindowOpacity(HWND hwnd, Qt::WindowFlags flags, bool hasAlpha, bool openGL, qreal level) { if (QWindowsWindow::setWindowLayered(hwnd, flags, hasAlpha, level)) { const BYTE alpha = BYTE(qRound(255.0 * level)); if (hasAlpha && !openGL && (flags & Qt::FramelessWindowHint)) { // Non-GL windows with alpha: Use blend function to update. BLENDFUNCTION blend = {AC_SRC_OVER, 0, alpha, AC_SRC_ALPHA}; UpdateLayeredWindow(hwnd, NULL, NULL, NULL, NULL, NULL, 0, &blend, ULW_ALPHA); } else { SetLayeredWindowAttributes(hwnd, 0, alpha, LWA_ALPHA); } } else if (IsWindowVisible(hwnd)) { // Repaint when switching from layered. InvalidateRect(hwnd, NULL, TRUE); } } static inline void updateGLWindowSettings(const QWindow *w, HWND hwnd, Qt::WindowFlags flags, qreal opacity) { const bool isGL = windowIsOpenGL(w); const bool hasAlpha = w->format().hasAlpha(); if (isGL && hasAlpha) applyBlurBehindWindow(hwnd); setWindowOpacity(hwnd, flags, hasAlpha, isGL, opacity); } /*! \class WindowCreationData \brief Window creation code. This struct gathers all information required to create a window. Window creation is split in 3 steps: \list \li fromWindow() Gather all required information \li create() Create the system handle. \li initialize() Post creation initialization steps. \endlist The reason for this split is to also enable changing the QWindowFlags by calling: \list \li fromWindow() Gather information and determine new system styles \li applyWindowFlags() to apply the new window system styles. \li initialize() Post creation initialization steps. \endlist Contains the window creation code formerly in qwidget_win.cpp. \sa QWindowCreationContext \internal \ingroup qt-lighthouse-win */ struct WindowCreationData { typedef QWindowsWindowData WindowData; enum Flags { ForceChild = 0x1, ForceTopLevel = 0x2 }; WindowCreationData() : parentHandle(0), type(Qt::Widget), style(0), exStyle(0), topLevel(false), popup(false), dialog(false), tool(false), embedded(false), hasAlpha(false) {} void fromWindow(const QWindow *w, const Qt::WindowFlags flags, unsigned creationFlags = 0); inline WindowData create(const QWindow *w, const WindowData &data, QString title) const; inline void applyWindowFlags(HWND hwnd) const; void initialize(const QWindow *w, HWND h, bool frameChange, qreal opacityLevel) const; Qt::WindowFlags flags; HWND parentHandle; Qt::WindowType type; unsigned style; unsigned exStyle; bool topLevel; bool popup; bool dialog; bool tool; bool embedded; bool hasAlpha; }; QDebug operator<<(QDebug debug, const WindowCreationData &d) { QDebugStateSaver saver(debug); debug.nospace(); debug.noquote(); debug << "WindowCreationData: " << d.flags << "\n topLevel=" << d.topLevel; if (d.parentHandle) debug << " parent=" << d.parentHandle; debug << " popup=" << d.popup << " dialog=" << d.dialog << " embedded=" << d.embedded << " tool=" << d.tool << "\n style=" << debugWinStyle(d.style); if (d.exStyle) debug << "\n exStyle=" << debugWinExStyle(d.exStyle); return debug; } // Fix top level window flags in case only the type flags are passed. static inline void fixTopLevelWindowFlags(Qt::WindowFlags &flags) { // Not supported on Windows, also do correction when it is set. flags &= ~Qt::WindowFullscreenButtonHint; switch (flags) { case Qt::Window: flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowMinimizeButtonHint |Qt::WindowMaximizeButtonHint|Qt::WindowCloseButtonHint; break; case Qt::Dialog: case Qt::Tool: flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint; break; default: break; } if ((flags & Qt::WindowType_Mask) == Qt::SplashScreen) flags |= Qt::FramelessWindowHint; } void WindowCreationData::fromWindow(const QWindow *w, const Qt::WindowFlags flagsIn, unsigned creationFlags) { flags = flagsIn; // Sometimes QWindow doesn't have a QWindow parent but does have a native parent window, // e.g. in case of embedded ActiveQt servers. They should not be considered a top-level // windows in such cases. QVariant prop = w->property("_q_embedded_native_parent_handle"); if (prop.isValid()) { embedded = true; parentHandle = reinterpret_cast(prop.value()); } if (creationFlags & ForceChild) { topLevel = false; } else if (embedded) { // Embedded native windows (for example Active X server windows) are by // definition never toplevel, even though they do not have QWindow parents. topLevel = false; } else { topLevel = (creationFlags & ForceTopLevel) ? true : w->isTopLevel(); } if (topLevel) fixTopLevelWindowFlags(flags); type = static_cast(int(flags) & Qt::WindowType_Mask); switch (type) { case Qt::Dialog: case Qt::Sheet: dialog = true; break; case Qt::Drawer: case Qt::Tool: tool = true; break; case Qt::Popup: popup = true; break; default: break; } if ((flags & Qt::MSWindowsFixedSizeDialogHint)) dialog = true; // Parent: Use transient parent for top levels. if (popup) { flags |= Qt::WindowStaysOnTopHint; // a popup stays on top, no parent. } else if (!embedded) { if (const QWindow *parentWindow = topLevel ? w->transientParent() : w->parent()) parentHandle = QWindowsWindow::handleOf(parentWindow); } if (popup || (type == Qt::ToolTip) || (type == Qt::SplashScreen)) { style = WS_POPUP; } else if (topLevel) { if (flags & Qt::FramelessWindowHint) style = WS_POPUP; // no border else if (flags & Qt::WindowTitleHint) style = WS_OVERLAPPED; else style = 0; } else { style = WS_CHILD; } // if (!testAttribute(Qt::WA_PaintUnclipped)) // ### Commented out for now as it causes some problems, but // this should be correct anyway, so dig some more into this #ifdef Q_FLATTEN_EXPOSE if (windowIsOpenGL(w)) // a bit incorrect since the is-opengl status may change from false to true at any time later on style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN; // see SetPixelFormat #else style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN ; #endif if (topLevel) { if ((type == Qt::Window || dialog || tool)) { if (!(flags & Qt::FramelessWindowHint)) { style |= WS_POPUP; if (flags & Qt::MSWindowsFixedSizeDialogHint) { style |= WS_DLGFRAME; } else { style |= WS_THICKFRAME; } if (flags & Qt::WindowTitleHint) style |= WS_CAPTION; // Contains WS_DLGFRAME } if (flags & Qt::WindowSystemMenuHint) style |= WS_SYSMENU; else if (dialog && (flags & Qt::WindowCloseButtonHint) && !(flags & Qt::FramelessWindowHint)) { style |= WS_SYSMENU | WS_BORDER; // QTBUG-2027, dialogs without system menu. exStyle |= WS_EX_DLGMODALFRAME; } if (flags & Qt::WindowMinimizeButtonHint) style |= WS_MINIMIZEBOX; if (shouldShowMaximizeButton(w, flags)) style |= WS_MAXIMIZEBOX; if (tool) exStyle |= WS_EX_TOOLWINDOW; if (flags & Qt::WindowContextHelpButtonHint) exStyle |= WS_EX_CONTEXTHELP; } else { exStyle |= WS_EX_TOOLWINDOW; } // make mouse events fall through this window // NOTE: WS_EX_TRANSPARENT flag can make mouse inputs fall through a layered window if (flagsIn & Qt::WindowTransparentForInput) exStyle |= WS_EX_LAYERED | WS_EX_TRANSPARENT; } } QWindowsWindowData WindowCreationData::create(const QWindow *w, const WindowData &data, QString title) const { typedef QSharedPointer QWindowCreationContextPtr; WindowData result; result.flags = flags; const HINSTANCE appinst = (HINSTANCE)GetModuleHandle(0); const QString windowClassName = QWindowsContext::instance()->registerWindowClass(w); const QRect rect = QPlatformWindow::initialGeometry(w, data.geometry, defaultWindowWidth, defaultWindowHeight); if (title.isEmpty() && (result.flags & Qt::WindowTitleHint)) title = topLevel ? qAppName() : w->objectName(); const wchar_t *titleUtf16 = reinterpret_cast(title.utf16()); const wchar_t *classNameUtf16 = reinterpret_cast(windowClassName.utf16()); // Capture events before CreateWindowEx() returns. The context is cleared in // the QWindowsWindow constructor. const QWindowCreationContextPtr context(new QWindowCreationContext(w, rect, data.customMargins, style, exStyle)); QWindowsContext::instance()->setWindowCreationContext(context); qCDebug(lcQpaWindows).nospace() << "CreateWindowEx: " << w << " class=" << windowClassName << " title=" << title << '\n' << *this << "\nrequested: " << rect << ": " << context->frameWidth << 'x' << context->frameHeight << '+' << context->frameX << '+' << context->frameY << " custom margins: " << context->customMargins; result.hwnd = CreateWindowEx(exStyle, classNameUtf16, titleUtf16, style, context->frameX, context->frameY, context->frameWidth, context->frameHeight, parentHandle, NULL, appinst, NULL); qCDebug(lcQpaWindows).nospace() << "CreateWindowEx: returns " << w << ' ' << result.hwnd << " obtained geometry: " << context->obtainedGeometry << ' ' << context->margins; if (!result.hwnd) { qErrnoWarning("%s: CreateWindowEx failed", __FUNCTION__); return result; } result.geometry = context->obtainedGeometry; result.frame = context->margins; result.embedded = embedded; result.customMargins = context->customMargins; return result; } void WindowCreationData::applyWindowFlags(HWND hwnd) const { // Keep enabled and visible from the current style. const LONG_PTR oldStyle = GetWindowLongPtr(hwnd, GWL_STYLE); const LONG_PTR oldExStyle = GetWindowLongPtr(hwnd, GWL_EXSTYLE); const LONG_PTR newStyle = style | (oldStyle & (WS_DISABLED|WS_VISIBLE)); if (oldStyle != newStyle) SetWindowLongPtr(hwnd, GWL_STYLE, newStyle); const LONG_PTR newExStyle = exStyle; if (newExStyle != oldExStyle) SetWindowLongPtr(hwnd, GWL_EXSTYLE, newExStyle); qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << hwnd << *this << "\n Style from " << debugWinStyle(DWORD(oldStyle)) << "\n to " << debugWinStyle(DWORD(newStyle)) << "\n ExStyle from " << debugWinExStyle(DWORD(oldExStyle)) << " to " << debugWinExStyle(DWORD(newExStyle)); } void WindowCreationData::initialize(const QWindow *w, HWND hwnd, bool frameChange, qreal opacityLevel) const { if (!hwnd) return; UINT swpFlags = SWP_NOMOVE | SWP_NOSIZE; if (frameChange) swpFlags |= SWP_FRAMECHANGED; if (topLevel) { swpFlags |= SWP_NOACTIVATE; if ((flags & Qt::WindowStaysOnTopHint) || (type == Qt::ToolTip)) { SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, swpFlags); if (flags & Qt::WindowStaysOnBottomHint) qWarning("QWidget: Incompatible window flags: the window can't be on top and on bottom at the same time"); } else if (flags & Qt::WindowStaysOnBottomHint) { SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0, swpFlags); } else if (frameChange) { // Force WM_NCCALCSIZE with wParam=1 in case of custom margins. SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, swpFlags); } if (flags & (Qt::CustomizeWindowHint|Qt::WindowTitleHint)) { HMENU systemMenu = GetSystemMenu(hwnd, FALSE); if (flags & Qt::WindowCloseButtonHint) EnableMenuItem(systemMenu, SC_CLOSE, MF_BYCOMMAND|MF_ENABLED); else EnableMenuItem(systemMenu, SC_CLOSE, MF_BYCOMMAND|MF_GRAYED); } updateGLWindowSettings(w, hwnd, flags, opacityLevel); } else { // child. SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, swpFlags); } } // Scaling helpers for size constraints. static QSize toNativeSizeConstrained(QSize dip, const QWindow *w) { if (QHighDpiScaling::isActive()) { const qreal factor = QHighDpiScaling::factor(w); if (dip.width() > 0 && dip.width() < QWINDOWSIZE_MAX) dip.rwidth() *= factor; if (dip.height() > 0 && dip.height() < QWINDOWSIZE_MAX) dip.rheight() *= factor; } return dip; } /*! \class QWindowsGeometryHint \brief Stores geometry constraints and provides utility functions. Geometry constraints ready to apply to a MINMAXINFO taking frame into account. \internal \ingroup qt-lighthouse-win */ QWindowsGeometryHint::QWindowsGeometryHint(const QWindow *w, const QMargins &cm) : minimumSize(toNativeSizeConstrained(w->minimumSize(), w)), maximumSize(toNativeSizeConstrained(w->maximumSize(), w)), customMargins(cm) { } bool QWindowsGeometryHint::validSize(const QSize &s) const { const int width = s.width(); const int height = s.height(); return width >= minimumSize.width() && width <= maximumSize.width() && height >= minimumSize.height() && height <= maximumSize.height(); } QMargins QWindowsGeometryHint::frame(DWORD style, DWORD exStyle) { RECT rect = {0,0,0,0}; style &= ~(WS_OVERLAPPED); // Not permitted, see docs. if (!AdjustWindowRectEx(&rect, style, FALSE, exStyle)) qErrnoWarning("%s: AdjustWindowRectEx failed", __FUNCTION__); const QMargins result(qAbs(rect.left), qAbs(rect.top), qAbs(rect.right), qAbs(rect.bottom)); qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << " style=" << showbase << hex << style << " exStyle=" << exStyle << dec << noshowbase << ' ' << rect << ' ' << result; return result; } bool QWindowsGeometryHint::handleCalculateSize(const QMargins &customMargins, const MSG &msg, LRESULT *result) { // NCCALCSIZE_PARAMS structure if wParam==TRUE if (!msg.wParam || customMargins.isNull()) return false; *result = DefWindowProc(msg.hwnd, msg.message, msg.wParam, msg.lParam); NCCALCSIZE_PARAMS *ncp = reinterpret_cast(msg.lParam); const RECT oldClientArea = ncp->rgrc[0]; ncp->rgrc[0].left += customMargins.left(); ncp->rgrc[0].top += customMargins.top(); ncp->rgrc[0].right -= customMargins.right(); ncp->rgrc[0].bottom -= customMargins.bottom(); result = 0; qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << oldClientArea << '+' << customMargins << "-->" << ncp->rgrc[0] << ' ' << ncp->rgrc[1] << ' ' << ncp->rgrc[2] << ' ' << ncp->lppos->cx << ',' << ncp->lppos->cy; return true; } void QWindowsGeometryHint::applyToMinMaxInfo(HWND hwnd, MINMAXINFO *mmi) const { return applyToMinMaxInfo(DWORD(GetWindowLong(hwnd, GWL_STYLE)), DWORD(GetWindowLong(hwnd, GWL_EXSTYLE)), mmi); } void QWindowsGeometryHint::applyToMinMaxInfo(DWORD style, DWORD exStyle, MINMAXINFO *mmi) const { qCDebug(lcQpaWindows).nospace() << '>' << __FUNCTION__ << '<' << " min=" << minimumSize.width() << ',' << minimumSize.height() << " max=" << maximumSize.width() << ',' << maximumSize.height() << " in " << *mmi; const QMargins margins = QWindowsGeometryHint::frame(style, exStyle); const int frameWidth = margins.left() + margins.right() + customMargins.left() + customMargins.right(); const int frameHeight = margins.top() + margins.bottom() + customMargins.top() + customMargins.bottom(); if (minimumSize.width() > 0) mmi->ptMinTrackSize.x = minimumSize.width() + frameWidth; if (minimumSize.height() > 0) mmi->ptMinTrackSize.y = minimumSize.height() + frameHeight; const int maximumWidth = qMax(maximumSize.width(), minimumSize.width()); const int maximumHeight = qMax(maximumSize.height(), minimumSize.height()); if (maximumWidth < QWINDOWSIZE_MAX) mmi->ptMaxTrackSize.x = maximumWidth + frameWidth; if (maximumHeight < QWINDOWSIZE_MAX) mmi->ptMaxTrackSize.y = maximumHeight + frameHeight; qCDebug(lcQpaWindows).nospace() << '<' << __FUNCTION__ << " frame=" << margins << ' ' << frameWidth << ',' << frameHeight << " out " << *mmi; } bool QWindowsGeometryHint::positionIncludesFrame(const QWindow *w) { return qt_window_private(const_cast(w))->positionPolicy == QWindowPrivate::WindowFrameInclusive; } /*! \class QWindowsBaseWindow \brief Base class for QWindowsForeignWindow, QWindowsWindow The class provides some _sys() getters for querying window data from a HWND and some _sys() setters. Derived classes wrapping foreign windows may use them directly to calculate geometry, margins, etc. Derived classes representing windows created by Qt may defer expensive calculations until change notifications are received. \since 5.6 \internal \ingroup qt-lighthouse-win */ QWindowsBaseWindow *QWindowsBaseWindow::baseWindowOf(const QWindow *w) { if (w) { if (QPlatformWindow *pw = w->handle()) return static_cast(pw); } return Q_NULLPTR; } HWND QWindowsBaseWindow::handleOf(const QWindow *w) { const QWindowsBaseWindow *bw = QWindowsBaseWindow::baseWindowOf(w); return bw ? bw->handle() : HWND(0); } bool QWindowsBaseWindow::isTopLevel_sys() const { const HWND parent = parentHwnd(); return !parent || parent == GetDesktopWindow(); } QRect QWindowsBaseWindow::frameGeometry_sys() const { return frameGeometry(handle(), isTopLevel()); } QRect QWindowsBaseWindow::geometry_sys() const { return frameGeometry_sys().marginsRemoved(frameMargins()); } QMargins QWindowsBaseWindow::frameMargins_sys() const { return QWindowsGeometryHint::frame(style(), exStyle()); } void QWindowsBaseWindow::hide_sys() // Normal hide, do not activate other windows. { SetWindowPos(handle(),0 , 0, 0, 0, 0, SWP_HIDEWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); } void QWindowsBaseWindow::raise_sys() { qCDebug(lcQpaWindows) << __FUNCTION__ << this << window(); if ((window()->flags() & (Qt::WindowStaysOnTopHint | Qt::WindowStaysOnBottomHint)) == 0) SetWindowPos(handle(), HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); } void QWindowsBaseWindow::lower_sys() { qCDebug(lcQpaWindows) << __FUNCTION__ << this << window(); if ((window()->flags() & (Qt::WindowStaysOnTopHint | Qt::WindowStaysOnBottomHint)) == 0) SetWindowPos(handle(), HWND_BOTTOM, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); } void QWindowsBaseWindow::setWindowTitle_sys(const QString &title) { qCDebug(lcQpaWindows) << __FUNCTION__ << this << window() << title; SetWindowText(handle(), reinterpret_cast(title.utf16())); } QPoint QWindowsBaseWindow::mapToGlobal(const QPoint &pos) const { return QWindowsGeometryHint::mapToGlobal(handle(), pos); } QPoint QWindowsBaseWindow::mapFromGlobal(const QPoint &pos) const { return QWindowsGeometryHint::mapFromGlobal(handle(), pos); } /*! \class QWindowsDesktopWindow \brief Window wrapping GetDesktopWindow not allowing any manipulation. \since 5.6 \internal \ingroup qt-lighthouse-win */ /*! \class QWindowsForeignWindow \brief Window wrapping a foreign native window. QWindowsForeignWindow stores a native HWND and implements getters for geometry, margins, etc. reparenting and geometry manipulation for use as a child window in Qt. \since 5.6 \internal \ingroup qt-lighthouse-win */ QWindowsForeignWindow::QWindowsForeignWindow(QWindow *window, HWND hwnd) : QWindowsBaseWindow(window) , m_hwnd(hwnd) , m_topLevelStyle(0) { } void QWindowsForeignWindow::setParent(const QPlatformWindow *newParentWindow) { const bool wasTopLevel = isTopLevel_sys(); const HWND newParent = newParentWindow ? reinterpret_cast(newParentWindow->winId()) : HWND(0); const bool isTopLevel = !newParent; const DWORD oldStyle = style(); qCDebug(lcQpaWindows) << __FUNCTION__ << window() << "newParent=" << newParentWindow << newParent << "oldStyle=" << debugWinStyle(oldStyle); SetParent(m_hwnd, newParent); if (wasTopLevel != isTopLevel) { // Top level window flags need to be set/cleared manually. DWORD newStyle = oldStyle; if (isTopLevel) { newStyle = m_topLevelStyle; } else { m_topLevelStyle = oldStyle; newStyle &= ~(WS_OVERLAPPEDWINDOW | WS_POPUPWINDOW); newStyle |= WS_CHILD; } SetWindowLongPtr(m_hwnd, GWL_STYLE, newStyle); } } void QWindowsForeignWindow::setVisible(bool visible) { qCDebug(lcQpaWindows) << __FUNCTION__ << window() << visible; if (visible) ShowWindow(handle(), SW_SHOWNOACTIVATE); else hide_sys(); } /*! \class QWindowCreationContext \brief Active Context for creating windows. There is a phase in window creation (WindowCreationData::create()) in which events are sent before the system API CreateWindowEx() returns the handle. These cannot be handled by the platform window as the association of the unknown handle value to the window does not exist yet and as not to trigger recursive handle creation, etc. In that phase, an instance of QWindowCreationContext is set on QWindowsContext. QWindowCreationContext stores the information to answer the initial WM_GETMINMAXINFO and obtains the corrected size/position. \sa WindowCreationData, QWindowsContext \internal \ingroup qt-lighthouse-win */ QWindowCreationContext::QWindowCreationContext(const QWindow *w, const QRect &geometry, const QMargins &cm, DWORD style_, DWORD exStyle_) : geometryHint(w, cm), window(w), style(style_), exStyle(exStyle_), requestedGeometry(geometry), obtainedGeometry(geometry), margins(QWindowsGeometryHint::frame(style, exStyle)), customMargins(cm) { // Geometry of toplevels does not consider window frames. // TODO: No concept of WA_wasMoved yet that would indicate a // CW_USEDEFAULT unless set. For now, assume that 0,0 means 'default' // for toplevels. if (geometry.isValid()) { frameX = geometry.x(); frameY = geometry.y(); const QMargins effectiveMargins = margins + customMargins; frameWidth = effectiveMargins.left() + geometry.width() + effectiveMargins.right(); frameHeight = effectiveMargins.top() + geometry.height() + effectiveMargins.bottom(); const bool isDefaultPosition = !frameX && !frameY && w->isTopLevel(); if (!QWindowsGeometryHint::positionIncludesFrame(w) && !isDefaultPosition) { frameX -= effectiveMargins.left(); frameY -= effectiveMargins.top(); } } qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << ' ' << w << ' ' << geometry << " pos incl. frame=" << QWindowsGeometryHint::positionIncludesFrame(w) << " frame=" << frameWidth << 'x' << frameHeight << '+' << frameX << '+' << frameY << " min=" << geometryHint.minimumSize << " max=" << geometryHint.maximumSize << " custom margins=" << customMargins; } /*! \class QWindowsWindow \brief Raster or OpenGL Window. \list \li Raster type: handleWmPaint() is implemented to to bitblt the image. The DC can be accessed via getDC/Relase DC, which has a special handling when within a paint event (in that case, the DC obtained from BeginPaint() is returned). \li Open GL: The first time QWindowsGLContext accesses the handle, it sets up the pixelformat on the DC which in turn sets it on the window (see flag PixelFormatInitialized). handleWmPaint() is empty (although required). \endlist \internal \ingroup qt-lighthouse-win */ QWindowsWindow::QWindowsWindow(QWindow *aWindow, const QWindowsWindowData &data) : QWindowsBaseWindow(aWindow), m_data(data), m_cursor(new CursorHandle), m_format(aWindow->requestedFormat()) { // Clear the creation context as the window can be found in QWindowsContext's map. QWindowsContext::instance()->setWindowCreationContext(QSharedPointer()); QWindowsContext::instance()->addWindow(m_data.hwnd, this); const Qt::WindowType type = aWindow->type(); if (type == Qt::Desktop) return; // No further handling for Qt::Desktop #ifndef QT_NO_OPENGL if (aWindow->surfaceType() == QWindow::OpenGLSurface) { if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) setFlag(OpenGLSurface); else setFlag(OpenGL_ES2); } #endif // QT_NO_OPENGL updateDropSite(window()->isTopLevel()); registerTouchWindow(); setWindowState(aWindow->windowState()); const qreal opacity = qt_window_private(aWindow)->opacity; if (!qFuzzyCompare(opacity, qreal(1.0))) setOpacity(opacity); if (aWindow->isTopLevel()) setWindowIcon(aWindow->icon()); clearFlag(WithinCreate); } QWindowsWindow::~QWindowsWindow() { setFlag(WithinDestroy); if (testFlag(TouchRegistered)) QWindowsContext::user32dll.unregisterTouchWindow(m_data.hwnd); destroyWindow(); destroyIcon(); } void QWindowsWindow::fireExpose(const QRegion ®ion, bool force) { if (region.isEmpty() && !force) clearFlag(Exposed); else setFlag(Exposed); QWindowSystemInterface::handleExposeEvent(window(), region); } static inline QWindow *findTransientChild(const QWindow *parent) { foreach (QWindow *w, QGuiApplication::topLevelWindows()) if (w->transientParent() == parent) return w; return 0; } void QWindowsWindow::destroyWindow() { qCDebug(lcQpaWindows) << __FUNCTION__ << this << window() << m_data.hwnd; if (m_data.hwnd) { // Stop event dispatching before Window is destroyed. setFlag(WithinDestroy); // Clear any transient child relationships as Windows will otherwise destroy them (QTBUG-35499, QTBUG-36666) if (QWindow *transientChild = findTransientChild(window())) if (QWindowsWindow *tw = QWindowsWindow::windowsWindowOf(transientChild)) tw->updateTransientParent(); QWindowsContext *context = QWindowsContext::instance(); if (context->windowUnderMouse() == window()) context->clearWindowUnderMouse(); if (hasMouseCapture()) setMouseGrabEnabled(false); setDropSiteEnabled(false); #ifndef QT_NO_OPENGL if (m_surface) { if (QWindowsStaticOpenGLContext *staticOpenGLContext = QWindowsIntegration::staticOpenGLContext()) staticOpenGLContext->destroyWindowSurface(m_surface); m_surface = 0; } #endif DestroyWindow(m_data.hwnd); context->removeWindow(m_data.hwnd); m_data.hwnd = 0; } } void QWindowsWindow::updateDropSite(bool topLevel) { bool enabled = false; bool parentIsEmbedded = false; if (!topLevel) { // if the parent window is a foreign window wrapped via QWindow::fromWinId, we need to enable the drop site // on the first child window const QWindow *parent = window()->parent(); if (parent && parent->handle() && parent->handle()->isForeignWindow()) parentIsEmbedded = true; } if (topLevel || parentIsEmbedded) { switch (window()->type()) { case Qt::Window: case Qt::Dialog: case Qt::Sheet: case Qt::Drawer: case Qt::Popup: case Qt::Tool: enabled = true; break; default: break; } } setDropSiteEnabled(enabled); } void QWindowsWindow::setDropSiteEnabled(bool dropEnabled) { if (isDropSiteEnabled() == dropEnabled) return; qCDebug(lcQpaMime) << __FUNCTION__ << window() << dropEnabled; #if !defined(QT_NO_CLIPBOARD) && !defined(QT_NO_DRAGANDDROP) if (dropEnabled) { Q_ASSERT(m_data.hwnd); m_dropTarget = new QWindowsOleDropTarget(window()); RegisterDragDrop(m_data.hwnd, m_dropTarget); CoLockObjectExternal(m_dropTarget, true, true); } else { CoLockObjectExternal(m_dropTarget, false, true); m_dropTarget->Release(); RevokeDragDrop(m_data.hwnd); m_dropTarget = 0; } #endif // !QT_NO_CLIPBOARD && !QT_NO_DRAGANDDROP } // Returns topmost QWindowsWindow ancestor even if there are embedded windows in the chain. // Returns this window if it is the topmost ancestor. QWindow *QWindowsWindow::topLevelOf(QWindow *w) { while (QWindow *parent = w->parent()) w = parent; if (const QPlatformWindow *handle = w->handle()) { const QWindowsWindow *ww = static_cast(handle); if (ww->isEmbedded()) { HWND parentHWND = GetAncestor(ww->handle(), GA_PARENT); const HWND desktopHwnd = GetDesktopWindow(); const QWindowsContext *ctx = QWindowsContext::instance(); while (parentHWND && parentHWND != desktopHwnd) { if (QWindowsWindow *ancestor = ctx->findPlatformWindow(parentHWND)) return topLevelOf(ancestor->window()); parentHWND = GetAncestor(parentHWND, GA_PARENT); } } } return w; } QWindowsWindowData QWindowsWindowData::create(const QWindow *w, const QWindowsWindowData ¶meters, const QString &title) { WindowCreationData creationData; creationData.fromWindow(w, parameters.flags); QWindowsWindowData result = creationData.create(w, parameters, title); // Force WM_NCCALCSIZE (with wParam=1) via SWP_FRAMECHANGED for custom margin. creationData.initialize(w, result.hwnd, !parameters.customMargins.isNull(), 1); return result; } void QWindowsWindow::setVisible(bool visible) { const QWindow *win = window(); qCDebug(lcQpaWindows) << __FUNCTION__ << this << win << m_data.hwnd << visible; if (m_data.hwnd) { if (visible) { show_sys(); // When the window is layered, we won't get WM_PAINT, and "we" are in control // over the rendering of the window // There is nobody waiting for this, so we don't need to flush afterwards. if (isLayered()) fireExpose(QRect(0, 0, win->width(), win->height())); // QTBUG-44928, QTBUG-7386: This is to resolve the problem where popups are // opened from the system tray and not being implicitly activated if (win->type() == Qt::Popup && !win->parent() && !QGuiApplication::focusWindow()) SetForegroundWindow(m_data.hwnd); } else { if (hasMouseCapture()) setMouseGrabEnabled(false); if (window()->flags() & Qt::Popup) // from QWidgetPrivate::hide_sys(), activate other ShowWindow(m_data.hwnd, SW_HIDE); else hide_sys(); fireExpose(QRegion()); } } } bool QWindowsWindow::isVisible() const { return m_data.hwnd && IsWindowVisible(m_data.hwnd); } bool QWindowsWindow::isActive() const { // Check for native windows or children of the active native window. if (const HWND activeHwnd = GetForegroundWindow()) if (m_data.hwnd == activeHwnd || IsChild(activeHwnd, m_data.hwnd)) return true; return false; } bool QWindowsWindow::isAncestorOf(const QPlatformWindow *child) const { const QWindowsWindow *childWindow = static_cast(child); return IsChild(m_data.hwnd, childWindow->handle()); } bool QWindowsWindow::isEmbedded() const { return m_data.embedded; } QPoint QWindowsWindow::mapToGlobal(const QPoint &pos) const { if (m_data.hwnd) return QWindowsGeometryHint::mapToGlobal(m_data.hwnd, pos); else return pos; } QPoint QWindowsWindow::mapFromGlobal(const QPoint &pos) const { if (m_data.hwnd) return QWindowsGeometryHint::mapFromGlobal(m_data.hwnd, pos); else return pos; } static inline HWND transientParentHwnd(HWND hwnd) { if (GetAncestor(hwnd, GA_PARENT) == GetDesktopWindow()) { const HWND rootOwnerHwnd = GetAncestor(hwnd, GA_ROOTOWNER); if (rootOwnerHwnd != hwnd) // May return itself for toplevels. return rootOwnerHwnd; } return 0; } // Update the transient parent for a toplevel window. The concept does not // really exist on Windows, the relationship is set by passing a parent along with !WS_CHILD // to window creation or by setting the parent using GWL_HWNDPARENT (as opposed to // SetParent, which would make it a real child). #ifndef GWL_HWNDPARENT # define GWL_HWNDPARENT (-8) #endif void QWindowsWindow::updateTransientParent() const { if (window()->type() == Qt::Popup) return; // QTBUG-34503, // a popup stays on top, no parent, see also WindowCreationData::fromWindow(). // Update transient parent. const HWND oldTransientParent = transientParentHwnd(m_data.hwnd); HWND newTransientParent = 0; if (const QWindow *tp = window()->transientParent()) if (const QWindowsWindow *tw = QWindowsWindow::windowsWindowOf(tp)) if (!tw->testFlag(WithinDestroy)) // Prevent destruction by parent window (QTBUG-35499, QTBUG-36666) newTransientParent = tw->handle(); // QTSOLBUG-71: When using the MFC/winmigrate solution, it is possible that a child // window is found, which can cause issues with modality. Loop up to top level. while (newTransientParent && (GetWindowLongPtr(newTransientParent, GWL_STYLE) & WS_CHILD) != 0) newTransientParent = GetParent(newTransientParent); if (newTransientParent != oldTransientParent) SetWindowLongPtr(m_data.hwnd, GWL_HWNDPARENT, LONG_PTR(newTransientParent)); } static inline bool testShowWithoutActivating(const QWindow *window) { // QWidget-attribute Qt::WA_ShowWithoutActivating . const QVariant showWithoutActivating = window->property("_q_showWithoutActivating"); return showWithoutActivating.isValid() && showWithoutActivating.toBool(); } // partially from QWidgetPrivate::show_sys() void QWindowsWindow::show_sys() const { int sm = SW_SHOWNORMAL; bool fakedMaximize = false; const QWindow *w = window(); const Qt::WindowFlags flags = w->flags(); const Qt::WindowType type = w->type(); if (w->isTopLevel()) { const Qt::WindowState state = w->windowState(); if (state & Qt::WindowMinimized) { sm = SW_SHOWMINIMIZED; if (!isVisible()) sm = SW_SHOWMINNOACTIVE; } else { updateTransientParent(); if (state & Qt::WindowMaximized) { sm = SW_SHOWMAXIMIZED; // Windows will not behave correctly when we try to maximize a window which does not // have minimize nor maximize buttons in the window frame. Windows would then ignore // non-available geometry, and rather maximize the widget to the full screen, minus the // window frame (caption). So, we do a trick here, by adding a maximize button before // maximizing the widget, and then remove the maximize button afterwards. if (flags & Qt::WindowTitleHint && !(flags & (Qt::WindowMinMaxButtonsHint | Qt::FramelessWindowHint))) { fakedMaximize = TRUE; setStyle(style() | WS_MAXIMIZEBOX); } } // Qt::WindowMaximized } // !Qt::WindowMinimized } if (type == Qt::Popup || type == Qt::ToolTip || type == Qt::Tool || testShowWithoutActivating(w)) sm = SW_SHOWNOACTIVATE; if (w->windowState() & Qt::WindowMaximized) setFlag(WithinMaximize); // QTBUG-8361 ShowWindow(m_data.hwnd, sm); clearFlag(WithinMaximize); if (fakedMaximize) { setStyle(style() & ~WS_MAXIMIZEBOX); SetWindowPos(m_data.hwnd, 0, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_FRAMECHANGED); } } void QWindowsWindow::setParent(const QPlatformWindow *newParent) { qCDebug(lcQpaWindows) << __FUNCTION__ << window() << newParent; if (m_data.hwnd) setParent_sys(newParent); } void QWindowsWindow::setParent_sys(const QPlatformWindow *parent) { // Use GetAncestor instead of GetParent, as GetParent can return owner window for toplevels HWND oldParentHWND = parentHwnd(); HWND newParentHWND = 0; if (parent) { const QWindowsWindow *parentW = static_cast(parent); newParentHWND = parentW->handle(); } // NULL handle means desktop window, which also has its proper handle -> disambiguate HWND desktopHwnd = GetDesktopWindow(); if (oldParentHWND == desktopHwnd) oldParentHWND = 0; if (newParentHWND == desktopHwnd) newParentHWND = 0; if (newParentHWND != oldParentHWND) { const bool wasTopLevel = oldParentHWND == 0; const bool isTopLevel = newParentHWND == 0; setFlag(WithinSetParent); SetParent(m_data.hwnd, newParentHWND); clearFlag(WithinSetParent); // WS_CHILD/WS_POPUP must be manually set/cleared in addition // to dialog frames, etc (see SetParent() ) if the top level state changes. // Force toplevel state as QWindow::isTopLevel cannot be relied upon here. if (wasTopLevel != isTopLevel) { setDropSiteEnabled(false); setWindowFlags_sys(window()->flags(), unsigned(isTopLevel ? WindowCreationData::ForceTopLevel : WindowCreationData::ForceChild)); updateDropSite(isTopLevel); } } } void QWindowsWindow::handleHidden() { fireExpose(QRegion()); } void QWindowsWindow::handleCompositionSettingsChanged() { const QWindow *w = window(); if (w->surfaceType() == QWindow::OpenGLSurface && w->format().hasAlpha()) applyBlurBehindWindow(handle()); } static QRect normalFrameGeometry(HWND hwnd) { WINDOWPLACEMENT wp; wp.length = sizeof(WINDOWPLACEMENT); if (GetWindowPlacement(hwnd, &wp)) { const QRect result = qrectFromRECT(wp.rcNormalPosition); return result.translated(windowPlacementOffset(hwnd, result.topLeft())); } return QRect(); } QRect QWindowsWindow::normalGeometry() const { // Check for fake 'fullscreen' mode. const bool fakeFullScreen = m_savedFrameGeometry.isValid() && window()->windowState() == Qt::WindowFullScreen; const QRect frame = fakeFullScreen ? m_savedFrameGeometry : normalFrameGeometry(m_data.hwnd); const QMargins margins = fakeFullScreen ? QWindowsGeometryHint::frame(m_savedStyle, 0) : frameMargins(); return frame.isValid() ? frame.marginsRemoved(margins) : frame; } void QWindowsWindow::setGeometry(const QRect &rectIn) { QRect rect = rectIn; // This means it is a call from QWindow::setFramePosition() and // the coordinates include the frame (size is still the contents rectangle). if (QWindowsGeometryHint::positionIncludesFrame(window())) { const QMargins margins = frameMargins(); rect.moveTopLeft(rect.topLeft() + QPoint(margins.left(), margins.top())); } if (m_windowState == Qt::WindowMinimized) m_data.geometry = rect; // Otherwise set by handleGeometryChange() triggered by event. if (m_data.hwnd) { // A ResizeEvent with resulting geometry will be sent. If we cannot // achieve that size (for example, window title minimal constraint), // notify and warn. setGeometry_sys(rect); if (m_data.geometry != rect) { qWarning("%s: Unable to set geometry %dx%d+%d+%d on %s/'%s'." " Resulting geometry: %dx%d+%d+%d " "(frame: %d, %d, %d, %d, custom margin: %d, %d, %d, %d" ", minimum size: %dx%d, maximum size: %dx%d).", __FUNCTION__, rect.width(), rect.height(), rect.x(), rect.y(), window()->metaObject()->className(), qPrintable(window()->objectName()), m_data.geometry.width(), m_data.geometry.height(), m_data.geometry.x(), m_data.geometry.y(), m_data.frame.left(), m_data.frame.top(), m_data.frame.right(), m_data.frame.bottom(), m_data.customMargins.left(), m_data.customMargins.top(), m_data.customMargins.right(), m_data.customMargins.bottom(), window()->minimumWidth(), window()->minimumHeight(), window()->maximumWidth(), window()->maximumHeight()); } } else { QPlatformWindow::setGeometry(rect); } } void QWindowsWindow::handleMoved() { // Minimize/Set parent can send nonsensical move events. if (!IsIconic(m_data.hwnd) && !testFlag(WithinSetParent)) handleGeometryChange(); } void QWindowsWindow::handleResized(int wParam) { switch (wParam) { case SIZE_MAXHIDE: // Some other window affected. case SIZE_MAXSHOW: return; case SIZE_MINIMIZED: // QTBUG-53577, prevent state change events during programmatic state change if (!testFlag(WithinSetStyle)) handleWindowStateChange(Qt::WindowMinimized); return; case SIZE_MAXIMIZED: if (!testFlag(WithinSetStyle)) handleWindowStateChange(Qt::WindowMaximized); handleGeometryChange(); break; case SIZE_RESTORED: if (!testFlag(WithinSetStyle)) { if (isFullScreen_sys()) handleWindowStateChange(Qt::WindowFullScreen); else if (m_windowState != Qt::WindowNoState && !testFlag(MaximizeToFullScreen)) handleWindowStateChange(Qt::WindowNoState); } handleGeometryChange(); break; } } void QWindowsWindow::handleGeometryChange() { const QRect previousGeometry = m_data.geometry; m_data.geometry = geometry_sys(); QPlatformWindow::setGeometry(m_data.geometry); QWindowSystemInterface::handleGeometryChange(window(), m_data.geometry); // QTBUG-32121: OpenGL/normal windows (with exception of ANGLE) do not receive // expose events when shrinking, synthesize. if (!testFlag(OpenGL_ES2) && isExposed() && m_data.geometry.size() != previousGeometry.size() // Exclude plain move // One dimension grew -> Windows will send expose, no need to synthesize. && !(m_data.geometry.width() > previousGeometry.width() || m_data.geometry.height() > previousGeometry.height())) { fireExpose(QRect(QPoint(0, 0), m_data.geometry.size()), true); } if (!parent() && previousGeometry.topLeft() != m_data.geometry.topLeft()) { HMONITOR hMonitor = MonitorFromWindow(m_data.hwnd, MONITOR_DEFAULTTONULL); QPlatformScreen *currentScreen = screen(); const auto screens = QWindowsContext::instance()->screenManager().screens(); auto newScreenIt = std::find_if(screens.begin(), screens.end(), [&](QWindowsScreen *s) { return s->data().hMonitor == hMonitor && s->data().flags & QWindowsScreenData::VirtualDesktop; }); if (newScreenIt != screens.end() && *newScreenIt != currentScreen) QWindowSystemInterface::handleWindowScreenChanged(window(), (*newScreenIt)->screen()); } if (testFlag(SynchronousGeometryChangeEvent)) QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents); qCDebug(lcQpaEvents) << __FUNCTION__ << this << window() << m_data.geometry; } void QWindowsBaseWindow::setGeometry_sys(const QRect &rect) const { const QMargins margins = frameMargins(); const QRect frameGeometry = rect + margins; qCDebug(lcQpaWindows) << '>' << __FUNCTION__ << window() << "\n from " << geometry_sys() << " frame: " << margins << " to " <