1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #if defined(WINVER) && WINVER < 0x0601
41 #  undef WINVER
42 #endif
43 #if !defined(WINVER)
44 #  define WINVER 0x0601 // Enable touch functions for MinGW
45 #endif
46 
47 #include "qwindowswindow.h"
48 #include "qwindowscontext.h"
49 #if QT_CONFIG(draganddrop)
50 #  include "qwindowsdrag.h"
51 #endif
52 #include "qwindowsscreen.h"
53 #include "qwindowsintegration.h"
54 #include "qwindowsmenu.h"
55 #include "qwindowsnativeinterface.h"
56 #if QT_CONFIG(dynamicgl)
57 #  include "qwindowsglcontext.h"
58 #else
59 #  include "qwindowsopenglcontext.h"
60 #endif
61 #include "qwindowsopengltester.h"
62 #ifdef QT_NO_CURSOR
63 #  include "qwindowscursor.h"
64 #endif
65 
66 #include <QtGui/qguiapplication.h>
67 #include <QtGui/qscreen.h>
68 #include <QtGui/qwindow.h>
69 #include <QtGui/qregion.h>
70 #include <QtGui/qopenglcontext.h>
71 #include <private/qsystemlibrary_p.h>
72 #include <private/qwindow_p.h> // QWINDOWSIZE_MAX
73 #include <private/qguiapplication_p.h>
74 #include <private/qhighdpiscaling_p.h>
75 #include <qpa/qwindowsysteminterface.h>
76 
77 #include <QtCore/qdebug.h>
78 #include <QtCore/qlibraryinfo.h>
79 #include <QtCore/qoperatingsystemversion.h>
80 
81 #include <dwmapi.h>
82 
83 #if QT_CONFIG(vulkan)
84 #include "qwindowsvulkaninstance.h"
85 #endif
86 
87 QT_BEGIN_NAMESPACE
88 
89 using QWindowCreationContextPtr = QSharedPointer<QWindowCreationContext>;
90 
91 enum {
92     defaultWindowWidth = 160,
93     defaultWindowHeight = 160
94 };
95 
96 Q_GUI_EXPORT HICON qt_pixmapToWinHICON(const QPixmap &);
97 
debugWinStyle(DWORD style)98 static QByteArray debugWinStyle(DWORD style)
99 {
100     QByteArray rc = "0x";
101     rc += QByteArray::number(qulonglong(style), 16);
102     if (style & WS_POPUP)
103         rc += " WS_POPUP";
104     if (style & WS_CHILD)
105         rc += " WS_CHILD";
106     if (style & WS_OVERLAPPED)
107         rc += " WS_OVERLAPPED";
108     if (style & WS_CLIPSIBLINGS)
109         rc += " WS_CLIPSIBLINGS";
110     if (style & WS_CLIPCHILDREN)
111         rc += " WS_CLIPCHILDREN";
112     if (style & WS_THICKFRAME)
113         rc += " WS_THICKFRAME";
114     if (style & WS_DLGFRAME)
115         rc += " WS_DLGFRAME";
116     if (style & WS_SYSMENU)
117         rc += " WS_SYSMENU";
118     if (style & WS_MINIMIZEBOX)
119         rc += " WS_MINIMIZEBOX";
120     if (style & WS_MAXIMIZEBOX)
121         rc += " WS_MAXIMIZEBOX";
122     return rc;
123 }
124 
debugWinExStyle(DWORD exStyle)125 static QByteArray debugWinExStyle(DWORD exStyle)
126 {
127     QByteArray rc = "0x";
128     rc += QByteArray::number(qulonglong(exStyle), 16);
129     if (exStyle & WS_EX_TOOLWINDOW)
130         rc += " WS_EX_TOOLWINDOW";
131     if (exStyle & WS_EX_CONTEXTHELP)
132         rc += " WS_EX_CONTEXTHELP";
133     if (exStyle & WS_EX_LAYERED)
134         rc += " WS_EX_LAYERED";
135     if (exStyle & WS_EX_DLGMODALFRAME)
136         rc += " WS_EX_DLGMODALFRAME";
137     if (exStyle & WS_EX_LAYOUTRTL)
138         rc += " WS_EX_LAYOUTRTL";
139     if (exStyle & WS_EX_NOINHERITLAYOUT)
140         rc += " WS_EX_NOINHERITLAYOUT";
141     return rc;
142 }
143 
debugWinSwpPos(UINT flags)144 static QByteArray debugWinSwpPos(UINT flags)
145 {
146     QByteArray rc = "0x";
147     rc += QByteArray::number(flags, 16);
148     if (flags & SWP_FRAMECHANGED)
149         rc += " SWP_FRAMECHANGED";
150     if (flags & SWP_HIDEWINDOW)
151         rc += " SWP_HIDEWINDOW";
152     if (flags & SWP_NOACTIVATE)
153         rc += " SWP_NOACTIVATE";
154     if (flags & SWP_NOCOPYBITS)
155         rc += " SWP_NOCOPYBITS";
156     if (flags & SWP_NOMOVE)
157         rc += " SWP_NOMOVE";
158     if (flags & SWP_NOOWNERZORDER)
159         rc += " SWP_NOOWNERZORDER";
160     if (flags & SWP_NOREDRAW)
161         rc += " SWP_NOREDRAW";
162     if (flags & SWP_NOSENDCHANGING)
163         rc += " SWP_NOSENDCHANGING";
164     if (flags & SWP_NOSIZE)
165         rc += " SWP_NOSIZE";
166     if (flags & SWP_NOZORDER)
167         rc += " SWP_NOZORDER";
168     if (flags & SWP_SHOWWINDOW)
169         rc += " SWP_SHOWWINDOW";
170     return rc;
171 }
172 
qSizeOfRect(const RECT & rect)173 static inline QSize qSizeOfRect(const RECT &rect)
174 {
175     return QSize(rect.right -rect.left, rect.bottom - rect.top);
176 }
177 
qrectFromRECT(const RECT & rect)178 static inline QRect qrectFromRECT(const RECT &rect)
179 {
180     return QRect(QPoint(rect.left, rect.top), qSizeOfRect(rect));
181 }
182 
RECTfromQRect(const QRect & rect)183 static inline RECT RECTfromQRect(const QRect &rect)
184 {
185     const int x = rect.left();
186     const int y = rect.top();
187     RECT result = { x, y, x + rect.width(), y + rect.height() };
188     return result;
189 }
190 
191 
192 #ifndef QT_NO_DEBUG_STREAM
operator <<(QDebug d,const RECT & r)193 QDebug operator<<(QDebug d, const RECT &r)
194 {
195     QDebugStateSaver saver(d);
196     d.nospace();
197     d << "RECT(left=" << r.left << ", top=" << r.top
198         << ", right=" << r.right << ", bottom=" << r.bottom
199         << " (" << r.right - r.left << 'x' << r.bottom - r.top << "))";
200     return d;
201 }
202 
operator <<(QDebug d,const POINT & p)203 QDebug operator<<(QDebug d, const POINT &p)
204 {
205     d << p.x << ',' << p.y;
206     return d;
207 }
208 
operator <<(QDebug d,const WINDOWPOS & wp)209 QDebug operator<<(QDebug d, const WINDOWPOS &wp)
210 {
211     QDebugStateSaver saver(d);
212     d.nospace();
213     d.noquote();
214     d <<  "WINDOWPOS(flags=" << debugWinSwpPos(wp.flags) << ", hwnd="
215        << wp.hwnd << ", hwndInsertAfter=" << wp.hwndInsertAfter << ", x=" << wp.x
216        <<  ", y=" << wp.y << ", cx=" << wp.cx <<  ", cy=" << wp.cy << ')';
217     return d;
218 }
219 
operator <<(QDebug d,const NCCALCSIZE_PARAMS & p)220 QDebug operator<<(QDebug d, const NCCALCSIZE_PARAMS &p)
221 {
222     QDebugStateSaver saver(d);
223     d.nospace();
224     d << "NCCALCSIZE_PARAMS(rgrc=[" << p.rgrc[0] << ' ' << p.rgrc[1] << ' '
225         << p.rgrc[2] << "], lppos=" << *p.lppos << ')';
226     return d;
227 }
228 
operator <<(QDebug d,const MINMAXINFO & i)229 QDebug operator<<(QDebug d, const MINMAXINFO &i)
230 {
231     QDebugStateSaver saver(d);
232     d.nospace();
233     d << "MINMAXINFO maxSize=" << i.ptMaxSize.x << ','
234         << i.ptMaxSize.y << " maxpos=" << i.ptMaxPosition.x
235         << ',' << i.ptMaxPosition.y << " mintrack="
236         << i.ptMinTrackSize.x << ',' << i.ptMinTrackSize.y
237         << " maxtrack=" << i.ptMaxTrackSize.x << ',' << i.ptMaxTrackSize.y;
238     return d;
239 }
240 
operator <<(QDebug d,const WINDOWPLACEMENT & wp)241 QDebug operator<<(QDebug d, const WINDOWPLACEMENT &wp)
242 {
243     QDebugStateSaver saver(d);
244     d.nospace();
245     d.noquote();
246     d <<  "WINDOWPLACEMENT(flags=0x" << Qt::hex << wp.flags << Qt::dec << ", showCmd="
247         << wp.showCmd << ", ptMinPosition=" << wp.ptMinPosition << ", ptMaxPosition=" << wp.ptMaxPosition
248         << ", rcNormalPosition=" << wp.rcNormalPosition;
249     return d;
250 }
251 
operator <<(QDebug d,const GUID & guid)252 QDebug operator<<(QDebug d, const GUID &guid)
253 {
254     QDebugStateSaver saver(d);
255     d.nospace();
256     d << '{' << Qt::hex << Qt::uppercasedigits << qSetPadChar(u'0')
257       << qSetFieldWidth(8) << guid.Data1
258       << qSetFieldWidth(0) << '-' << qSetFieldWidth(4)
259       << guid.Data2 << qSetFieldWidth(0) << '-' << qSetFieldWidth(4)
260       << guid.Data3 << qSetFieldWidth(0) << '-' << qSetFieldWidth(4)
261       << qSetFieldWidth(2) << guid.Data4[0] << guid.Data4[1]
262       << qSetFieldWidth(0) << '-' << qSetFieldWidth(2);
263     for (int i = 2; i < 8; ++i)
264         d << guid.Data4[i];
265     d << qSetFieldWidth(0) << '}';
266     return d;
267 }
268 #endif // !QT_NO_DEBUG_STREAM
269 
formatBriefRectangle(QDebug & d,const QRect & r)270 static void formatBriefRectangle(QDebug &d, const QRect &r)
271 {
272     d << r.width() << 'x' << r.height() << Qt::forcesign << r.x() << r.y() << Qt::noforcesign;
273 }
274 
formatBriefMargins(QDebug & d,const QMargins & m)275 static void formatBriefMargins(QDebug &d, const QMargins &m)
276 {
277     d << m.left() << ", " << m.top() << ", " << m.right() << ", " << m.bottom();
278 }
279 
280 // QTBUG-43872, for windows that do not have WS_EX_TOOLWINDOW set, WINDOWPLACEMENT
281 // is in workspace/available area coordinates.
windowPlacementOffset(HWND hwnd,const QPoint & point)282 static QPoint windowPlacementOffset(HWND hwnd, const QPoint &point)
283 {
284     if (GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_TOOLWINDOW)
285         return QPoint(0, 0);
286     const QWindowsScreenManager &screenManager = QWindowsContext::instance()->screenManager();
287     const QWindowsScreen *screen = screenManager.screens().size() == 1
288         ? screenManager.screens().constFirst() : screenManager.screenAtDp(point);
289     if (screen)
290         return screen->availableGeometry().topLeft() - screen->geometry().topLeft();
291     return QPoint(0, 0);
292 }
293 
294 // Return the frame geometry relative to the parent
295 // if there is one.
frameGeometry(HWND hwnd,bool topLevel)296 static inline QRect frameGeometry(HWND hwnd, bool topLevel)
297 {
298     RECT rect = { 0, 0, 0, 0 };
299     if (topLevel) {
300         WINDOWPLACEMENT windowPlacement;
301         windowPlacement.length = sizeof(WINDOWPLACEMENT);
302         GetWindowPlacement(hwnd, &windowPlacement);
303         if (windowPlacement.showCmd == SW_SHOWMINIMIZED) {
304             const QRect result = qrectFromRECT(windowPlacement.rcNormalPosition);
305             return result.translated(windowPlacementOffset(hwnd, result.topLeft()));
306         }
307     }
308     GetWindowRect(hwnd, &rect); // Screen coordinates.
309     const HWND parent = GetParent(hwnd);
310     if (parent && !topLevel) {
311         const int width = rect.right - rect.left;
312         const int height = rect.bottom - rect.top;
313         POINT leftTop = { rect.left, rect.top };
314         screenToClient(parent, &leftTop);
315         rect.left = leftTop.x;
316         rect.top = leftTop.y;
317         rect.right = leftTop.x + width;
318         rect.bottom = leftTop.y + height;
319     }
320     return qrectFromRECT(rect);
321 }
322 
323 // Return the visibility of the Window (except full screen since it is not a window state).
windowVisibility_sys(HWND hwnd)324 static QWindow::Visibility windowVisibility_sys(HWND hwnd)
325 {
326     if (!IsWindowVisible(hwnd))
327         return QWindow::Hidden;
328     WINDOWPLACEMENT windowPlacement;
329     windowPlacement.length = sizeof(WINDOWPLACEMENT);
330     if (GetWindowPlacement(hwnd, &windowPlacement)) {
331         switch (windowPlacement.showCmd) {
332         case SW_SHOWMINIMIZED:
333         case SW_MINIMIZE:
334         case SW_FORCEMINIMIZE:
335             return QWindow::Minimized;
336         case SW_SHOWMAXIMIZED:
337             return QWindow::Maximized;
338         default:
339             break;
340         }
341     }
342     return QWindow::Windowed;
343 }
344 
windowIsAccelerated(const QWindow * w)345 static inline bool windowIsAccelerated(const QWindow *w)
346 {
347     switch (w->surfaceType()) {
348     case QSurface::OpenGLSurface:
349         return true;
350     case QSurface::RasterGLSurface:
351         return qt_window_private(const_cast<QWindow *>(w))->compositing;
352     case QSurface::VulkanSurface:
353         return true;
354     default:
355         return false;
356     }
357 }
358 
applyBlurBehindWindow(HWND hwnd)359 static bool applyBlurBehindWindow(HWND hwnd)
360 {
361     BOOL compositionEnabled;
362     if (DwmIsCompositionEnabled(&compositionEnabled) != S_OK)
363         return false;
364 
365     DWM_BLURBEHIND blurBehind = {0, 0, nullptr, 0};
366 
367     if (compositionEnabled) {
368         blurBehind.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
369         blurBehind.fEnable = TRUE;
370         blurBehind.hRgnBlur = CreateRectRgn(0, 0, -1, -1);
371     } else {
372         blurBehind.dwFlags = DWM_BB_ENABLE;
373         blurBehind.fEnable = FALSE;
374     }
375 
376     const bool result = DwmEnableBlurBehindWindow(hwnd, &blurBehind) == S_OK;
377 
378     if (blurBehind.hRgnBlur)
379         DeleteObject(blurBehind.hRgnBlur);
380 
381     return result;
382 }
383 
384 // from qwidget_win.cpp, pass flags separately in case they have been "autofixed".
shouldShowMaximizeButton(const QWindow * w,Qt::WindowFlags flags)385 static bool shouldShowMaximizeButton(const QWindow *w, Qt::WindowFlags flags)
386 {
387     if ((flags & Qt::MSWindowsFixedSizeDialogHint) || !(flags & Qt::WindowMaximizeButtonHint))
388         return false;
389     // if the user explicitly asked for the maximize button, we try to add
390     // it even if the window has fixed size.
391     return (flags & Qt::CustomizeWindowHint) ||
392         w->maximumSize() == QSize(QWINDOWSIZE_MAX, QWINDOWSIZE_MAX);
393 }
394 
395 // Set the WS_EX_LAYERED flag on a HWND if required. This is required for
396 // translucent backgrounds, not fully opaque windows and for
397 // Qt::WindowTransparentForInput (in combination with WS_EX_TRANSPARENT).
setWindowLayered(HWND hwnd,Qt::WindowFlags flags,bool hasAlpha,qreal opacity)398 bool QWindowsWindow::setWindowLayered(HWND hwnd, Qt::WindowFlags flags, bool hasAlpha, qreal opacity)
399 {
400     const LONG exStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
401     const bool needsLayered = (flags & Qt::WindowTransparentForInput)
402         || (hasAlpha && (flags & Qt::FramelessWindowHint)) || opacity < 1.0;
403     const bool isLayered = (exStyle & WS_EX_LAYERED);
404     if (needsLayered != isLayered) {
405         if (needsLayered) {
406             SetWindowLong(hwnd, GWL_EXSTYLE, exStyle | WS_EX_LAYERED);
407         } else {
408             SetWindowLong(hwnd, GWL_EXSTYLE, exStyle & ~WS_EX_LAYERED);
409         }
410     }
411     return needsLayered;
412 }
413 
setWindowOpacity(HWND hwnd,Qt::WindowFlags flags,bool hasAlpha,bool accelerated,qreal level)414 static void setWindowOpacity(HWND hwnd, Qt::WindowFlags flags, bool hasAlpha, bool accelerated, qreal level)
415 {
416     if (QWindowsWindow::setWindowLayered(hwnd, flags, hasAlpha, level)) {
417         const BYTE alpha = BYTE(qRound(255.0 * level));
418         if (hasAlpha && !accelerated && (flags & Qt::FramelessWindowHint)) {
419             // Non-GL windows with alpha: Use blend function to update.
420             BLENDFUNCTION blend = {AC_SRC_OVER, 0, alpha, AC_SRC_ALPHA};
421             UpdateLayeredWindow(hwnd, nullptr, nullptr, nullptr, nullptr, nullptr, 0, &blend, ULW_ALPHA);
422         } else {
423             SetLayeredWindowAttributes(hwnd, 0, alpha, LWA_ALPHA);
424         }
425     } else if (IsWindowVisible(hwnd)) { // Repaint when switching from layered.
426         InvalidateRect(hwnd, nullptr, TRUE);
427     }
428 }
429 
updateGLWindowSettings(const QWindow * w,HWND hwnd,Qt::WindowFlags flags,qreal opacity)430 static inline void updateGLWindowSettings(const QWindow *w, HWND hwnd, Qt::WindowFlags flags, qreal opacity)
431 {
432     const bool isAccelerated = windowIsAccelerated(w);
433     const bool hasAlpha = w->format().hasAlpha();
434 
435     if (isAccelerated && hasAlpha)
436         applyBlurBehindWindow(hwnd);
437 
438     setWindowOpacity(hwnd, flags, hasAlpha, isAccelerated, opacity);
439 }
440 
441 /*!
442     Calculates the dimensions of the invisible borders within the
443     window frames in Windows 10, using an empirical expression that
444     reproduces the measured values for standard DPI settings.
445 */
446 
invisibleMargins(QPoint screenPoint)447 static QMargins invisibleMargins(QPoint screenPoint)
448 {
449     if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10) {
450         POINT pt = {screenPoint.x(), screenPoint.y()};
451         if (HMONITOR hMonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONULL)) {
452             if (QWindowsContext::shcoredll.isValid()) {
453                 UINT dpiX;
454                 UINT dpiY;
455                 if (SUCCEEDED(QWindowsContext::shcoredll.getDpiForMonitor(hMonitor, 0, &dpiX, &dpiY))) {
456                     const qreal sc = (dpiX - 96) / 96.0;
457                     const int gap = 7 + qRound(5*sc) - int(sc);
458                     return QMargins(gap, 0, gap, gap);
459                 }
460             }
461         }
462     }
463     return QMargins();
464 }
465 
466 /*!
467     \class WindowCreationData
468     \brief Window creation code.
469 
470     This struct gathers all information required to create a window.
471     Window creation is split in 3 steps:
472 
473     \list
474     \li fromWindow() Gather all required information
475     \li create() Create the system handle.
476     \li initialize() Post creation initialization steps.
477     \endlist
478 
479     The reason for this split is to also enable changing the QWindowFlags
480     by calling:
481 
482     \list
483     \li fromWindow() Gather information and determine new system styles
484     \li applyWindowFlags() to apply the new window system styles.
485     \li initialize() Post creation initialization steps.
486     \endlist
487 
488     Contains the window creation code formerly in qwidget_win.cpp.
489 
490     \sa QWindowCreationContext
491     \internal
492 */
493 
494 struct WindowCreationData
495 {
496     using WindowData = QWindowsWindowData;
497     enum Flags { ForceChild = 0x1, ForceTopLevel = 0x2 };
498 
499     void fromWindow(const QWindow *w, const Qt::WindowFlags flags, unsigned creationFlags = 0);
500     inline WindowData create(const QWindow *w, const WindowData &data, QString title) const;
501     inline void applyWindowFlags(HWND hwnd) const;
502     void initialize(const QWindow *w, HWND h, bool frameChange, qreal opacityLevel) const;
503 
504     Qt::WindowFlags flags;
505     HWND parentHandle = nullptr;
506     Qt::WindowType type = Qt::Widget;
507     unsigned style = 0;
508     unsigned exStyle = 0;
509     bool topLevel = false;
510     bool popup = false;
511     bool dialog = false;
512     bool tool = false;
513     bool embedded = false;
514     bool hasAlpha = false;
515 };
516 
operator <<(QDebug debug,const WindowCreationData & d)517 QDebug operator<<(QDebug debug, const WindowCreationData &d)
518 {
519     QDebugStateSaver saver(debug);
520     debug.nospace();
521     debug.noquote();
522     debug << "WindowCreationData: " << d.flags
523         << "\n  topLevel=" << d.topLevel;
524      if (d.parentHandle)
525          debug << " parent=" << d.parentHandle;
526      debug << " popup=" << d.popup << " dialog=" << d.dialog
527         << " embedded=" << d.embedded << " tool=" << d.tool
528         << "\n  style=" << debugWinStyle(d.style);
529     if (d.exStyle)
530         debug << "\n  exStyle=" << debugWinExStyle(d.exStyle);
531     return debug;
532 }
533 
534 // Fix top level window flags in case only the type flags are passed.
fixTopLevelWindowFlags(Qt::WindowFlags & flags)535 static inline void fixTopLevelWindowFlags(Qt::WindowFlags &flags)
536 {
537     // Not supported on Windows, also do correction when it is set.
538     flags &= ~Qt::WindowFullscreenButtonHint;
539     switch (flags) {
540     case Qt::Window:
541         flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowMinimizeButtonHint
542               |Qt::WindowMaximizeButtonHint|Qt::WindowCloseButtonHint;
543         break;
544     case Qt::Dialog:
545     case Qt::Tool:
546         flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint;
547         break;
548     default:
549         break;
550     }
551     if ((flags & Qt::WindowType_Mask) == Qt::SplashScreen)
552         flags |= Qt::FramelessWindowHint;
553 }
554 
screenForName(const QWindow * w,const QString & name)555 static QScreen *screenForName(const QWindow *w, const QString &name)
556 {
557     QScreen *winScreen = w ? w->screen() : QGuiApplication::primaryScreen();
558     if (winScreen && winScreen->name() != name) {
559         const auto screens = winScreen->virtualSiblings();
560         for (QScreen *screen : screens) {
561             if (screen->name() == name)
562                 return screen;
563         }
564     }
565     return winScreen;
566 }
567 
calcPosition(const QWindow * w,const QWindowCreationContextPtr & context,const QMargins & invMargins)568 static QPoint calcPosition(const QWindow *w, const QWindowCreationContextPtr &context, const QMargins &invMargins)
569 {
570     const QPoint orgPos(context->frameX - invMargins.left(), context->frameY - invMargins.top());
571 
572     if (!w || (!w->isTopLevel() && w->surfaceType() != QWindow::OpenGLSurface))
573         return orgPos;
574 
575     // Workaround for QTBUG-50371
576     const QScreen *screenForGL = QWindowsWindow::forcedScreenForGLWindow(w);
577     if (!screenForGL)
578         return orgPos;
579 
580     const QPoint posFrame(context->frameX, context->frameY);
581     const QMargins margins = context->margins;
582     const QRect scrGeo = screenForGL->handle()->availableGeometry();
583 
584     // Point is already in the required screen.
585     if (scrGeo.contains(orgPos))
586         return orgPos;
587 
588     // If the visible part of the window is already in the
589     // required screen, just ignore the invisible offset.
590     if (scrGeo.contains(posFrame))
591         return posFrame;
592 
593     // Find the original screen containing the coordinates.
594     const auto screens = screenForGL->virtualSiblings();
595     const QScreen *orgScreen = nullptr;
596     for (QScreen *screen : screens) {
597         if (screen->handle()->availableGeometry().contains(posFrame)) {
598             orgScreen = screen;
599             break;
600         }
601     }
602     const QPoint ctPos = QPoint(qMax(scrGeo.left(), scrGeo.center().x()
603                                      + (margins.right() - margins.left() - context->frameWidth)/2),
604                                 qMax(scrGeo.top(), scrGeo.center().y()
605                                      + (margins.bottom() - margins.top() - context->frameHeight)/2));
606 
607     // If initial coordinates were outside all screens, center the window on the required screen.
608     if (!orgScreen)
609         return ctPos;
610 
611     const QRect orgGeo = orgScreen->handle()->availableGeometry();
612     const QRect orgFrame(QPoint(context->frameX, context->frameY),
613                          QSize(context->frameWidth, context->frameHeight));
614 
615     // Window would be centered on orgScreen. Center it on the required screen.
616     if (orgGeo.center() == (orgFrame - margins).center())
617         return ctPos;
618 
619     // Transform the coordinates to map them into the required screen.
620     const QPoint newPos(scrGeo.left() + ((posFrame.x() - orgGeo.left()) * scrGeo.width()) / orgGeo.width(),
621                         scrGeo.top() + ((posFrame.y() - orgGeo.top()) * scrGeo.height()) / orgGeo.height());
622     const QPoint newPosNoMargin(newPos.x() - invMargins.left(), newPos.y() - invMargins.top());
623 
624     return scrGeo.contains(newPosNoMargin) ? newPosNoMargin : newPos;
625 }
626 
fromWindow(const QWindow * w,const Qt::WindowFlags flagsIn,unsigned creationFlags)627 void WindowCreationData::fromWindow(const QWindow *w, const Qt::WindowFlags flagsIn,
628                                     unsigned creationFlags)
629 {
630     flags = flagsIn;
631 
632     // Sometimes QWindow doesn't have a QWindow parent but does have a native parent window,
633     // e.g. in case of embedded ActiveQt servers. They should not be considered a top-level
634     // windows in such cases.
635     QVariant prop = w->property(QWindowsWindow::embeddedNativeParentHandleProperty);
636     if (prop.isValid()) {
637         embedded = true;
638         parentHandle = reinterpret_cast<HWND>(prop.value<WId>());
639     }
640 
641     if (creationFlags & ForceChild) {
642         topLevel = false;
643     } else if (embedded) {
644         // Embedded native windows (for example Active X server windows) are by
645         // definition never toplevel, even though they do not have QWindow parents.
646         topLevel = false;
647     } else {
648         topLevel = (creationFlags & ForceTopLevel) ? true : w->isTopLevel();
649     }
650 
651     if (topLevel)
652         fixTopLevelWindowFlags(flags);
653 
654     type = static_cast<Qt::WindowType>(int(flags) & Qt::WindowType_Mask);
655     switch (type) {
656     case Qt::Dialog:
657     case Qt::Sheet:
658         dialog = true;
659         break;
660     case Qt::Drawer:
661     case Qt::Tool:
662         tool = true;
663         break;
664     case Qt::Popup:
665         popup = true;
666         break;
667     default:
668         break;
669     }
670     if ((flags & Qt::MSWindowsFixedSizeDialogHint))
671         dialog = true;
672 
673     // This causes the title bar to drawn RTL and the close button
674     // to be left. Note that this causes:
675     // - All DCs created on the Window to have RTL layout (see SetLayout)
676     // - ClientToScreen() and ScreenToClient() to work in reverse as well.
677     // - Mouse event coordinates to be mirrored.
678     // - Positioning of child Windows.
679     if (QGuiApplication::layoutDirection() == Qt::RightToLeft
680         && (QWindowsIntegration::instance()->options() & QWindowsIntegration::RtlEnabled) != 0) {
681         exStyle |= WS_EX_LAYOUTRTL | WS_EX_NOINHERITLAYOUT;
682     }
683 
684     // Parent: Use transient parent for top levels.
685     if (popup) {
686         flags |= Qt::WindowStaysOnTopHint; // a popup stays on top, no parent.
687     } else if (!embedded) {
688         if (const QWindow *parentWindow = topLevel ? w->transientParent() : w->parent())
689             parentHandle = QWindowsWindow::handleOf(parentWindow);
690     }
691 
692     if (popup || (type == Qt::ToolTip) || (type == Qt::SplashScreen)) {
693         style = WS_POPUP;
694     } else if (topLevel) {
695         if (flags & Qt::FramelessWindowHint)
696             style = WS_POPUP;                // no border
697         else if (flags & Qt::WindowTitleHint)
698             style = WS_OVERLAPPED;
699         else
700             style = 0;
701     } else {
702         style = WS_CHILD;
703     }
704 
705         // if (!testAttribute(Qt::WA_PaintUnclipped))
706         // ### Commented out for now as it causes some problems, but
707         // this should be correct anyway, so dig some more into this
708 #ifdef Q_FLATTEN_EXPOSE
709         if (windowIsOpenGL(w)) // a bit incorrect since the is-opengl status may change from false to true at any time later on
710             style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN; // see SetPixelFormat
711 #else
712         style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN ;
713 #endif
714         if (topLevel) {
715             if ((type == Qt::Window || dialog || tool)) {
716                 if (!(flags & Qt::FramelessWindowHint)) {
717                     style |= WS_POPUP;
718                     if (flags & Qt::MSWindowsFixedSizeDialogHint) {
719                         style |= WS_DLGFRAME;
720                     } else {
721                         style |= WS_THICKFRAME;
722                     }
723                     if (flags & Qt::WindowTitleHint)
724                         style |= WS_CAPTION; // Contains WS_DLGFRAME
725                 }
726                 if (flags & Qt::WindowSystemMenuHint)
727                     style |= WS_SYSMENU;
728                 else if (dialog && (flags & Qt::WindowCloseButtonHint) && !(flags & Qt::FramelessWindowHint)) {
729                     style |= WS_SYSMENU | WS_BORDER; // QTBUG-2027, dialogs without system menu.
730                     exStyle |= WS_EX_DLGMODALFRAME;
731                 }
732                 if (flags & Qt::WindowMinimizeButtonHint)
733                     style |= WS_MINIMIZEBOX;
734                 if (shouldShowMaximizeButton(w, flags))
735                     style |= WS_MAXIMIZEBOX;
736                 if (tool)
737                     exStyle |= WS_EX_TOOLWINDOW;
738                 if (flags & Qt::WindowContextHelpButtonHint)
739                     exStyle |= WS_EX_CONTEXTHELP;
740             } else {
741                  exStyle |= WS_EX_TOOLWINDOW;
742             }
743 
744             // make mouse events fall through this window
745             // NOTE: WS_EX_TRANSPARENT flag can make mouse inputs fall through a layered window
746             if (flagsIn & Qt::WindowTransparentForInput)
747                 exStyle |= WS_EX_LAYERED | WS_EX_TRANSPARENT;
748     }
749 }
750 
shouldApplyDarkFrame(const QWindow * w)751 static inline bool shouldApplyDarkFrame(const QWindow *w)
752 {
753     return w->isTopLevel() && !w->flags().testFlag(Qt::FramelessWindowHint);
754 }
755 
756 QWindowsWindowData
create(const QWindow * w,const WindowData & data,QString title) const757     WindowCreationData::create(const QWindow *w, const WindowData &data, QString title) const
758 {
759     WindowData result;
760     result.flags = flags;
761 
762     const auto appinst = reinterpret_cast<HINSTANCE>(GetModuleHandle(nullptr));
763 
764     const QString windowClassName = QWindowsContext::instance()->registerWindowClass(w);
765 
766     const QScreen *screen{};
767     const QRect rect = QPlatformWindow::initialGeometry(w, data.geometry,
768                                                         defaultWindowWidth, defaultWindowHeight,
769                                                         &screen);
770 
771     if (title.isEmpty() && (result.flags & Qt::WindowTitleHint))
772         title = topLevel ? qAppName() : w->objectName();
773 
774     const auto *titleUtf16 = reinterpret_cast<const wchar_t *>(title.utf16());
775     const auto *classNameUtf16 = reinterpret_cast<const wchar_t *>(windowClassName.utf16());
776 
777     // Capture events before CreateWindowEx() returns. The context is cleared in
778     // the QWindowsWindow constructor.
779     const QWindowCreationContextPtr context(new QWindowCreationContext(w, screen, data.geometry,
780                                                                        rect, data.customMargins,
781                                                                        style, exStyle));
782     QWindowsContext::instance()->setWindowCreationContext(context);
783 
784     const bool hasFrame = (style & (WS_DLGFRAME | WS_THICKFRAME));
785     QMargins invMargins = topLevel && hasFrame && QWindowsGeometryHint::positionIncludesFrame(w)
786             ? invisibleMargins(QPoint(context->frameX, context->frameY)) : QMargins();
787 
788     qCDebug(lcQpaWindows).nospace()
789         << "CreateWindowEx: " << w << " class=" << windowClassName << " title=" << title
790         << '\n' << *this << "\nrequested: " << rect << ": "
791         << context->frameWidth << 'x' <<  context->frameHeight
792         << '+' << context->frameX << '+' << context->frameY
793         << " custom margins: " << context->customMargins
794         << " invisible margins: " << invMargins;
795 
796 
797     QPoint pos = calcPosition(w, context, invMargins);
798 
799     // Mirror the position when creating on a parent in RTL mode, ditto for the obtained geometry.
800     int mirrorParentWidth = 0;
801     if (!w->isTopLevel() && QWindowsBaseWindow::isRtlLayout(parentHandle)) {
802         RECT rect;
803         GetClientRect(parentHandle, &rect);
804         mirrorParentWidth = rect.right;
805     }
806     if (mirrorParentWidth != 0 && pos.x() != CW_USEDEFAULT && context->frameWidth != CW_USEDEFAULT)
807         pos.setX(mirrorParentWidth - context->frameWidth - pos.x());
808 
809     result.hwnd = CreateWindowEx(exStyle, classNameUtf16, titleUtf16,
810                                  style,
811                                  pos.x(), pos.y(),
812                                  context->frameWidth, context->frameHeight,
813                                  parentHandle, nullptr, appinst, nullptr);
814     qCDebug(lcQpaWindows).nospace()
815         << "CreateWindowEx: returns " << w << ' ' << result.hwnd << " obtained geometry: "
816         << context->obtainedPos << context->obtainedSize << ' ' << context->margins;
817 
818     if (!result.hwnd) {
819         qErrnoWarning("%s: CreateWindowEx failed", __FUNCTION__);
820         return result;
821     }
822 
823     if (QWindowsContext::isDarkMode()
824         && (QWindowsIntegration::instance()->options() & QWindowsIntegration::DarkModeWindowFrames) != 0
825         && shouldApplyDarkFrame(w)) {
826         QWindowsWindow::setDarkBorderToWindow(result.hwnd, true);
827     }
828 
829     if (mirrorParentWidth != 0) {
830         context->obtainedPos.setX(mirrorParentWidth - context->obtainedSize.width()
831                                   -  context->obtainedPos.x());
832     }
833 
834     QRect obtainedGeometry(context->obtainedPos, context->obtainedSize);
835 
836     result.geometry = obtainedGeometry;
837     result.fullFrameMargins = context->margins;
838     result.embedded = embedded;
839     result.hasFrame = hasFrame;
840     result.customMargins = context->customMargins;
841 
842     return result;
843 }
844 
applyWindowFlags(HWND hwnd) const845 void WindowCreationData::applyWindowFlags(HWND hwnd) const
846 {
847     // Keep enabled and visible from the current style.
848     const LONG_PTR oldStyle = GetWindowLongPtr(hwnd, GWL_STYLE);
849     const LONG_PTR oldExStyle = GetWindowLongPtr(hwnd, GWL_EXSTYLE);
850 
851     const LONG_PTR newStyle = style | (oldStyle & (WS_DISABLED|WS_VISIBLE));
852     if (oldStyle != newStyle)
853         SetWindowLongPtr(hwnd, GWL_STYLE, newStyle);
854     const LONG_PTR newExStyle = exStyle;
855     if (newExStyle != oldExStyle)
856         SetWindowLongPtr(hwnd, GWL_EXSTYLE, newExStyle);
857     qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << hwnd << *this
858         << "\n    Style from " << debugWinStyle(DWORD(oldStyle)) << "\n    to "
859         << debugWinStyle(DWORD(newStyle)) << "\n    ExStyle from "
860         << debugWinExStyle(DWORD(oldExStyle)) << " to "
861         << debugWinExStyle(DWORD(newExStyle));
862 }
863 
initialize(const QWindow * w,HWND hwnd,bool frameChange,qreal opacityLevel) const864 void WindowCreationData::initialize(const QWindow *w, HWND hwnd, bool frameChange, qreal opacityLevel) const
865 {
866     if (!hwnd)
867         return;
868     UINT swpFlags = SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER;
869     if (frameChange)
870         swpFlags |= SWP_FRAMECHANGED;
871     if (topLevel) {
872         swpFlags |= SWP_NOACTIVATE;
873         if ((flags & Qt::WindowStaysOnTopHint) || (type == Qt::ToolTip)) {
874             SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, swpFlags);
875             if (flags & Qt::WindowStaysOnBottomHint)
876                 qWarning("QWidget: Incompatible window flags: the window can't be on top and on bottom at the same time");
877         } else if (flags & Qt::WindowStaysOnBottomHint) {
878             SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0, swpFlags);
879         } else if (frameChange) { // Force WM_NCCALCSIZE with wParam=1 in case of custom margins.
880             SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, swpFlags);
881         }
882         if (flags & (Qt::CustomizeWindowHint|Qt::WindowTitleHint)) {
883             HMENU systemMenu = GetSystemMenu(hwnd, FALSE);
884             if (flags & Qt::WindowCloseButtonHint)
885                 EnableMenuItem(systemMenu, SC_CLOSE, MF_BYCOMMAND|MF_ENABLED);
886             else
887                 EnableMenuItem(systemMenu, SC_CLOSE, MF_BYCOMMAND|MF_GRAYED);
888         }
889         updateGLWindowSettings(w, hwnd, flags, opacityLevel);
890     } else { // child.
891         SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, swpFlags);
892     }
893 }
894 
895 
896 // Scaling helpers for size constraints.
toNativeSizeConstrained(QSize dip,const QScreen * s)897 static QSize toNativeSizeConstrained(QSize dip, const QScreen *s)
898 {
899     if (QHighDpiScaling::isActive()) {
900         const qreal factor = QHighDpiScaling::factor(s);
901         if (!qFuzzyCompare(factor, qreal(1))) {
902             if (dip.width() > 0 && dip.width() < QWINDOWSIZE_MAX)
903                 dip.setWidth(qRound(qreal(dip.width()) * factor));
904             if (dip.height() > 0 && dip.height() < QWINDOWSIZE_MAX)
905                 dip.setHeight(qRound(qreal(dip.height()) * factor));
906         }
907     }
908     return dip;
909 }
910 
911 /*!
912     \class QWindowsGeometryHint
913     \brief Stores geometry constraints and provides utility functions.
914 
915     Geometry constraints ready to apply to a MINMAXINFO taking frame
916     into account.
917 
918     \internal
919 */
920 
frameOnPrimaryScreen(DWORD style,DWORD exStyle)921 QMargins QWindowsGeometryHint::frameOnPrimaryScreen(DWORD style, DWORD exStyle)
922 {
923     RECT rect = {0,0,0,0};
924     style &= ~DWORD(WS_OVERLAPPED); // Not permitted, see docs.
925     if (AdjustWindowRectEx(&rect, style, FALSE, exStyle) == FALSE)
926         qErrnoWarning("%s: AdjustWindowRectEx failed", __FUNCTION__);
927     const QMargins result(qAbs(rect.left), qAbs(rect.top),
928                           qAbs(rect.right), qAbs(rect.bottom));
929     qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << " style="
930         << Qt::showbase << Qt::hex << style << " exStyle=" << exStyle << Qt::dec << Qt::noshowbase
931         << ' ' << rect << ' ' << result;
932     return result;
933 }
934 
frameOnPrimaryScreen(HWND hwnd)935 QMargins QWindowsGeometryHint::frameOnPrimaryScreen(HWND hwnd)
936 {
937     return frameOnPrimaryScreen(DWORD(GetWindowLongPtr(hwnd, GWL_STYLE)),
938                                 DWORD(GetWindowLongPtr(hwnd, GWL_EXSTYLE)));
939 }
940 
frame(DWORD style,DWORD exStyle,qreal dpi)941 QMargins QWindowsGeometryHint::frame(DWORD style, DWORD exStyle, qreal dpi)
942 {
943     if (QWindowsContext::user32dll.adjustWindowRectExForDpi == nullptr)
944         return frameOnPrimaryScreen(style, exStyle);
945     RECT rect = {0,0,0,0};
946     style &= ~DWORD(WS_OVERLAPPED); // Not permitted, see docs.
947     if (QWindowsContext::user32dll.adjustWindowRectExForDpi(&rect, style, FALSE, exStyle,
948                                                             unsigned(qRound(dpi))) == FALSE) {
949         qErrnoWarning("%s: AdjustWindowRectExForDpi failed", __FUNCTION__);
950     }
951     const QMargins result(qAbs(rect.left), qAbs(rect.top),
952                           qAbs(rect.right), qAbs(rect.bottom));
953     qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << " style="
954         << Qt::showbase << Qt::hex << style << " exStyle=" << exStyle << Qt::dec << Qt::noshowbase
955         << " dpi=" << dpi
956         << ' ' << rect << ' ' << result;
957     return result;
958 }
959 
frame(HWND hwnd,DWORD style,DWORD exStyle)960 QMargins QWindowsGeometryHint::frame(HWND hwnd, DWORD style, DWORD exStyle)
961 {
962     if (QWindowsScreenManager::isSingleScreen())
963         return frameOnPrimaryScreen(style, exStyle);
964     auto screenManager = QWindowsContext::instance()->screenManager();
965     auto screen = screenManager.screenForHwnd(hwnd);
966     if (!screen)
967         screen = screenManager.screens().value(0);
968     const auto dpi = screen ? screen->logicalDpi().first : qreal(96);
969     return frame(style, exStyle, dpi);
970 }
971 
972 // For newly created windows.
frame(const QWindow * w,const QRect & geometry,DWORD style,DWORD exStyle)973 QMargins QWindowsGeometryHint::frame(const QWindow *w, const QRect &geometry,
974                                      DWORD style, DWORD exStyle)
975 {
976     if (!w->isTopLevel() || w->flags().testFlag(Qt::FramelessWindowHint))
977         return {};
978     if (!QWindowsContext::user32dll.adjustWindowRectExForDpi
979         || QWindowsScreenManager::isSingleScreen()
980         || !QWindowsContext::shouldHaveNonClientDpiScaling(w)) {
981         return frameOnPrimaryScreen(style, exStyle);
982     }
983     qreal dpi = 96;
984     auto screenManager = QWindowsContext::instance()->screenManager();
985     auto screen = screenManager.screenAtDp(geometry.center());
986     if (!screen)
987         screen = screenManager.screens().value(0);
988     if (screen)
989         dpi = screen->logicalDpi().first;
990     return QWindowsGeometryHint::frame(style, exStyle, dpi);
991 }
992 
handleCalculateSize(const QMargins & customMargins,const MSG & msg,LRESULT * result)993 bool QWindowsGeometryHint::handleCalculateSize(const QMargins &customMargins, const MSG &msg, LRESULT *result)
994 {
995     // NCCALCSIZE_PARAMS structure if wParam==TRUE
996     if (!msg.wParam || customMargins.isNull())
997         return false;
998     *result = DefWindowProc(msg.hwnd, msg.message, msg.wParam, msg.lParam);
999     auto *ncp = reinterpret_cast<NCCALCSIZE_PARAMS *>(msg.lParam);
1000     const RECT oldClientArea = ncp->rgrc[0];
1001     ncp->rgrc[0].left += customMargins.left();
1002     ncp->rgrc[0].top += customMargins.top();
1003     ncp->rgrc[0].right -= customMargins.right();
1004     ncp->rgrc[0].bottom -= customMargins.bottom();
1005     result = nullptr;
1006     qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << oldClientArea << '+' << customMargins << "-->"
1007         << ncp->rgrc[0] << ' ' << ncp->rgrc[1] << ' ' << ncp->rgrc[2]
1008         << ' ' << ncp->lppos->cx << ',' << ncp->lppos->cy;
1009     return true;
1010 }
1011 
frameSizeConstraints(const QWindow * w,const QScreen * screen,const QMargins & margins,QSize * minimumSize,QSize * maximumSize)1012 void QWindowsGeometryHint::frameSizeConstraints(const QWindow *w, const QScreen *screen,
1013                                                 const QMargins &margins,
1014                                                 QSize *minimumSize, QSize *maximumSize)
1015 {
1016     *minimumSize = toNativeSizeConstrained(w->minimumSize(), screen);
1017     *maximumSize = toNativeSizeConstrained(w->maximumSize(), screen);
1018 
1019     const int maximumWidth = qMax(maximumSize->width(), minimumSize->width());
1020     const int maximumHeight = qMax(maximumSize->height(), minimumSize->height());
1021     const int frameWidth = margins.left() + margins.right();
1022     const int frameHeight = margins.top() + margins.bottom();
1023 
1024     if (minimumSize->width() > 0)
1025         minimumSize->rwidth() += frameWidth;
1026     if (minimumSize->height() > 0)
1027         minimumSize->rheight() += frameHeight;
1028     if (maximumWidth < QWINDOWSIZE_MAX)
1029         maximumSize->setWidth(maximumWidth + frameWidth);
1030     if (maximumHeight < QWINDOWSIZE_MAX)
1031         maximumSize->setHeight(maximumHeight + frameHeight);
1032 }
1033 
applyToMinMaxInfo(const QWindow * w,const QScreen * screen,const QMargins & margins,MINMAXINFO * mmi)1034 void QWindowsGeometryHint::applyToMinMaxInfo(const QWindow *w,
1035                                              const QScreen *screen,
1036                                              const QMargins &margins,
1037                                              MINMAXINFO *mmi)
1038 {
1039     QSize minimumSize;
1040     QSize maximumSize;
1041     frameSizeConstraints(w, screen, margins, &minimumSize, &maximumSize);
1042     qCDebug(lcQpaWindows).nospace() << '>' << __FUNCTION__ << '<' << " min="
1043         << minimumSize.width() << ',' << minimumSize.height()
1044         << " max=" << maximumSize.width() << ',' << maximumSize.height()
1045         << " margins=" << margins
1046         << " in " << *mmi;
1047 
1048     if (minimumSize.width() > 0)
1049         mmi->ptMinTrackSize.x = minimumSize.width();
1050     if (minimumSize.height() > 0)
1051         mmi->ptMinTrackSize.y = minimumSize.height();
1052 
1053     if (maximumSize.width() < QWINDOWSIZE_MAX)
1054         mmi->ptMaxTrackSize.x = maximumSize.width();
1055     if (maximumSize.height() < QWINDOWSIZE_MAX)
1056         mmi->ptMaxTrackSize.y = maximumSize.height();
1057     qCDebug(lcQpaWindows).nospace() << '<' << __FUNCTION__ << " out " << *mmi;
1058 }
1059 
applyToMinMaxInfo(const QWindow * w,const QMargins & margins,MINMAXINFO * mmi)1060 void QWindowsGeometryHint::applyToMinMaxInfo(const QWindow *w,
1061                                              const QMargins &margins,
1062                                              MINMAXINFO *mmi)
1063 {
1064     applyToMinMaxInfo(w, w->screen(), margins, mmi);
1065 }
1066 
positionIncludesFrame(const QWindow * w)1067 bool QWindowsGeometryHint::positionIncludesFrame(const QWindow *w)
1068 {
1069     return qt_window_private(const_cast<QWindow *>(w))->positionPolicy
1070            == QWindowPrivate::WindowFrameInclusive;
1071 }
1072 
1073 /*!
1074     \class QWindowsBaseWindow
1075     \brief Base class for QWindowsForeignWindow, QWindowsWindow
1076 
1077     The class provides some _sys() getters for querying window
1078     data from a HWND and some _sys() setters.
1079 
1080     Derived classes wrapping foreign windows may use them directly
1081     to calculate geometry, margins, etc.
1082 
1083     Derived classes representing windows created by Qt may defer
1084     expensive calculations until change notifications are received.
1085 
1086     \since 5.6
1087     \internal
1088 */
1089 
isRtlLayout(HWND hwnd)1090 bool QWindowsBaseWindow::isRtlLayout(HWND hwnd)
1091 {
1092     return (GetWindowLongPtrW(hwnd, GWL_EXSTYLE) & WS_EX_LAYOUTRTL) != 0;
1093 }
1094 
baseWindowOf(const QWindow * w)1095 QWindowsBaseWindow *QWindowsBaseWindow::baseWindowOf(const QWindow *w)
1096 {
1097     if (w) {
1098         if (QPlatformWindow *pw = w->handle())
1099             return static_cast<QWindowsBaseWindow *>(pw);
1100     }
1101     return nullptr;
1102 }
1103 
handleOf(const QWindow * w)1104 HWND QWindowsBaseWindow::handleOf(const QWindow *w)
1105 {
1106     const QWindowsBaseWindow *bw = QWindowsBaseWindow::baseWindowOf(w);
1107     return bw ? bw->handle() : HWND(nullptr);
1108 }
1109 
isTopLevel_sys() const1110 bool QWindowsBaseWindow::isTopLevel_sys() const
1111 {
1112     const HWND parent = parentHwnd();
1113     return !parent || parent == GetDesktopWindow();
1114 }
1115 
frameGeometry_sys() const1116 QRect QWindowsBaseWindow::frameGeometry_sys() const
1117 {
1118     return frameGeometry(handle(), isTopLevel());
1119 }
1120 
geometry_sys() const1121 QRect QWindowsBaseWindow::geometry_sys() const
1122 {
1123     return frameGeometry_sys().marginsRemoved(fullFrameMargins());
1124 }
1125 
frameMargins_sys() const1126 QMargins QWindowsBaseWindow::frameMargins_sys() const
1127 {
1128     return QWindowsGeometryHint::frame(handle(), style(), exStyle());
1129 }
1130 
hide_sys()1131 void QWindowsBaseWindow::hide_sys() // Normal hide, do not activate other windows.
1132 {
1133     SetWindowPos(handle(), nullptr , 0, 0, 0, 0,
1134                  SWP_HIDEWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
1135 }
1136 
raise_sys()1137 void QWindowsBaseWindow::raise_sys()
1138 {
1139     qCDebug(lcQpaWindows) << __FUNCTION__ << this << window();
1140     const Qt::WindowType type = window()->type();
1141     if (type == Qt::Popup
1142         || type == Qt::SubWindow // Special case for QTBUG-63121: MDI subwindows with WindowStaysOnTopHint
1143         || !(window()->flags() & Qt::WindowStaysOnBottomHint)) {
1144         SetWindowPos(handle(), HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
1145     }
1146 }
1147 
lower_sys()1148 void QWindowsBaseWindow::lower_sys()
1149 {
1150     qCDebug(lcQpaWindows) << __FUNCTION__ << this << window();
1151     if (!(window()->flags() & Qt::WindowStaysOnTopHint))
1152         SetWindowPos(handle(), HWND_BOTTOM, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
1153 }
1154 
setWindowTitle_sys(const QString & title)1155 void QWindowsBaseWindow::setWindowTitle_sys(const QString &title)
1156 {
1157     qCDebug(lcQpaWindows) << __FUNCTION__ << this << window() << title;
1158     SetWindowText(handle(), reinterpret_cast<const wchar_t *>(title.utf16()));
1159 }
1160 
mapToGlobal(const QPoint & pos) const1161 QPoint QWindowsBaseWindow::mapToGlobal(const QPoint &pos) const
1162 {
1163     return QWindowsGeometryHint::mapToGlobal(handle(), pos);
1164 }
1165 
mapFromGlobal(const QPoint & pos) const1166 QPoint QWindowsBaseWindow::mapFromGlobal(const QPoint &pos) const
1167 {
1168     return QWindowsGeometryHint::mapFromGlobal(handle(), pos);
1169 }
1170 
1171 /*!
1172     \class QWindowsDesktopWindow
1173     \brief Window wrapping GetDesktopWindow not allowing any manipulation.
1174     \since 5.6
1175     \internal
1176 */
1177 
1178 /*!
1179     \class QWindowsForeignWindow
1180     \brief Window wrapping a foreign native window.
1181 
1182     QWindowsForeignWindow stores a native HWND and implements getters for
1183     geometry, margins, etc. reparenting and geometry manipulation for use as a
1184     child window in Qt.
1185 
1186     \since 5.6
1187     \internal
1188 */
1189 
QWindowsForeignWindow(QWindow * window,HWND hwnd)1190 QWindowsForeignWindow::QWindowsForeignWindow(QWindow *window, HWND hwnd)
1191     : QWindowsBaseWindow(window)
1192     , m_hwnd(hwnd)
1193     , m_topLevelStyle(0)
1194 {
1195 }
1196 
setParent(const QPlatformWindow * newParentWindow)1197 void QWindowsForeignWindow::setParent(const QPlatformWindow *newParentWindow)
1198 {
1199     const bool wasTopLevel = isTopLevel_sys();
1200     const HWND newParent = newParentWindow ? reinterpret_cast<HWND>(newParentWindow->winId()) : HWND(nullptr);
1201     const bool isTopLevel = !newParent;
1202     const DWORD oldStyle = style();
1203     qCDebug(lcQpaWindows) << __FUNCTION__ << window() << "newParent="
1204         << newParentWindow << newParent << "oldStyle=" << debugWinStyle(oldStyle);
1205     SetParent(m_hwnd, newParent);
1206     if (wasTopLevel != isTopLevel) { // Top level window flags need to be set/cleared manually.
1207         DWORD newStyle = oldStyle;
1208         if (isTopLevel) {
1209             newStyle = m_topLevelStyle;
1210         } else {
1211             m_topLevelStyle = oldStyle;
1212             newStyle &= ~(WS_OVERLAPPEDWINDOW | WS_POPUPWINDOW);
1213             newStyle |= WS_CHILD;
1214         }
1215         SetWindowLongPtr(m_hwnd, GWL_STYLE, newStyle);
1216     }
1217 }
1218 
setVisible(bool visible)1219 void QWindowsForeignWindow::setVisible(bool visible)
1220 {
1221     qCDebug(lcQpaWindows) << __FUNCTION__ << window() << visible;
1222     if (visible)
1223         ShowWindow(handle(), SW_SHOWNOACTIVATE);
1224     else
1225         hide_sys();
1226 }
1227 
1228 /*!
1229     \class QWindowCreationContext
1230     \brief Active Context for creating windows.
1231 
1232     There is a phase in window creation (WindowCreationData::create())
1233     in which events are sent before the system API CreateWindowEx() returns
1234     the handle. These cannot be handled by the platform window as the association
1235     of the unknown handle value to the window does not exist yet and as not
1236     to trigger recursive handle creation, etc.
1237 
1238     In that phase, an instance of  QWindowCreationContext is set on
1239     QWindowsContext.
1240 
1241     QWindowCreationContext stores the information to answer the initial
1242     WM_GETMINMAXINFO and obtains the corrected size/position.
1243 
1244     \sa WindowCreationData, QWindowsContext
1245     \internal
1246 */
1247 
QWindowCreationContext(const QWindow * w,const QScreen * s,const QRect & geometryIn,const QRect & geometry,const QMargins & cm,DWORD style,DWORD exStyle)1248 QWindowCreationContext::QWindowCreationContext(const QWindow *w, const QScreen *s,
1249                                                const QRect &geometryIn, const QRect &geometry,
1250                                                const QMargins &cm,
1251                                                DWORD style, DWORD exStyle) :
1252     window(w),
1253     screen(s),
1254     requestedGeometryIn(geometryIn),
1255     requestedGeometry(geometry),
1256     obtainedPos(geometryIn.topLeft()),
1257     obtainedSize(geometryIn.size()),
1258     margins(QWindowsGeometryHint::frame(w, geometry, style, exStyle)),
1259     customMargins(cm)
1260 {
1261     // Geometry of toplevels does not consider window frames.
1262     // TODO: No concept of WA_wasMoved yet that would indicate a
1263     // CW_USEDEFAULT unless set. For now, assume that 0,0 means 'default'
1264     // for toplevels.
1265     if (geometry.isValid()
1266         || !qt_window_private(const_cast<QWindow *>(w))->resizeAutomatic) {
1267         frameX = geometry.x();
1268         frameY = geometry.y();
1269         const QMargins effectiveMargins = margins + customMargins;
1270         frameWidth = effectiveMargins.left() + geometry.width() + effectiveMargins.right();
1271         frameHeight = effectiveMargins.top() + geometry.height() + effectiveMargins.bottom();
1272         if (QWindowsMenuBar::menuBarOf(w) != nullptr) {
1273             menuHeight = GetSystemMetrics(SM_CYMENU);
1274             frameHeight += menuHeight;
1275         }
1276         const bool isDefaultPosition = !frameX && !frameY && w->isTopLevel();
1277         if (!QWindowsGeometryHint::positionIncludesFrame(w) && !isDefaultPosition) {
1278             frameX -= effectiveMargins.left();
1279             frameY -= effectiveMargins.top();
1280         }
1281     }
1282 
1283     qCDebug(lcQpaWindows).nospace()
1284         << __FUNCTION__ << ' ' << w << ' ' << geometry
1285         << " pos incl. frame=" << QWindowsGeometryHint::positionIncludesFrame(w)
1286         << " frame=" << frameWidth << 'x' << frameHeight << '+'
1287         << frameX << '+' << frameY
1288         << " margins=" << margins << " custom margins=" << customMargins;
1289 }
1290 
applyToMinMaxInfo(MINMAXINFO * mmi) const1291 void QWindowCreationContext::applyToMinMaxInfo(MINMAXINFO *mmi) const
1292 {
1293     QWindowsGeometryHint::applyToMinMaxInfo(window, screen, margins + customMargins, mmi);
1294 }
1295 
1296 /*!
1297     \class QWindowsWindow
1298     \brief Raster or OpenGL Window.
1299 
1300     \list
1301     \li Raster type: handleWmPaint() is implemented to
1302        to bitblt the image. The DC can be accessed
1303        via getDC/Relase DC, which has a special handling
1304        when within a paint event (in that case, the DC obtained
1305        from BeginPaint() is returned).
1306 
1307     \li Open GL: The first time QWindowsGLContext accesses
1308        the handle, it sets up the pixelformat on the DC
1309        which in turn sets it on the window (see flag
1310        PixelFormatInitialized).
1311        handleWmPaint() is empty (although required).
1312     \endlist
1313 
1314     \internal
1315 */
1316 
1317 const char *QWindowsWindow::embeddedNativeParentHandleProperty = "_q_embedded_native_parent_handle";
1318 const char *QWindowsWindow::hasBorderInFullScreenProperty = "_q_has_border_in_fullscreen";
1319 bool QWindowsWindow::m_borderInFullScreenDefault = false;
1320 
QWindowsWindow(QWindow * aWindow,const QWindowsWindowData & data)1321 QWindowsWindow::QWindowsWindow(QWindow *aWindow, const QWindowsWindowData &data) :
1322     QWindowsBaseWindow(aWindow),
1323     m_data(data),
1324     m_cursor(new CursorHandle)
1325 #if QT_CONFIG(vulkan)
1326   , m_vkSurface(VK_NULL_HANDLE)
1327 #endif
1328 {
1329     QWindowsContext::instance()->addWindow(m_data.hwnd, this);
1330     const Qt::WindowType type = aWindow->type();
1331     if (type == Qt::Desktop)
1332         return; // No further handling for Qt::Desktop
1333 #ifndef QT_NO_OPENGL
1334     if (aWindow->surfaceType() == QWindow::OpenGLSurface) {
1335         if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL)
1336             setFlag(OpenGLSurface);
1337         else
1338             setFlag(OpenGL_ES2);
1339     }
1340 #endif // QT_NO_OPENGL
1341 #if QT_CONFIG(vulkan)
1342     if (aWindow->surfaceType() == QSurface::VulkanSurface)
1343         setFlag(VulkanSurface);
1344 #endif
1345     updateDropSite(window()->isTopLevel());
1346 
1347     registerTouchWindow();
1348     const qreal opacity = qt_window_private(aWindow)->opacity;
1349     if (!qFuzzyCompare(opacity, qreal(1.0)))
1350         setOpacity(opacity);
1351 
1352     setMask(QHighDpi::toNativeLocalRegion(window()->mask(), window()));
1353 
1354     if (aWindow->isTopLevel())
1355         setWindowIcon(aWindow->icon());
1356     if (m_borderInFullScreenDefault || aWindow->property(hasBorderInFullScreenProperty).toBool())
1357         setFlag(HasBorderInFullScreen);
1358     clearFlag(WithinCreate);
1359 }
1360 
~QWindowsWindow()1361 QWindowsWindow::~QWindowsWindow()
1362 {
1363     setFlag(WithinDestroy);
1364     if (testFlag(TouchRegistered))
1365         UnregisterTouchWindow(m_data.hwnd);
1366     destroyWindow();
1367     destroyIcon();
1368 }
1369 
initialize()1370 void QWindowsWindow::initialize()
1371 {
1372     // Clear the creation context as the window can be found in QWindowsContext's map.
1373     QWindowCreationContextPtr creationContext =
1374         QWindowsContext::instance()->setWindowCreationContext(QWindowCreationContextPtr());
1375 
1376     QWindow *w = window();
1377     setWindowState(w->windowStates());
1378 
1379     // Trigger geometry change (unless it has a special state in which case setWindowState()
1380     // will send the message) and screen change signals of QWindow.
1381     if (w->type() != Qt::Desktop) {
1382         const Qt::WindowState state = w->windowState();
1383         const QRect obtainedGeometry(creationContext->obtainedPos, creationContext->obtainedSize);
1384         if (state != Qt::WindowMaximized && state != Qt::WindowFullScreen
1385             && creationContext->requestedGeometryIn != obtainedGeometry) {
1386             QWindowSystemInterface::handleGeometryChange<QWindowSystemInterface::SynchronousDelivery>(w, obtainedGeometry);
1387         }
1388         QPlatformScreen *obtainedScreen = screenForGeometry(obtainedGeometry);
1389         if (obtainedScreen && screen() != obtainedScreen)
1390             QWindowSystemInterface::handleWindowScreenChanged<QWindowSystemInterface::SynchronousDelivery>(w, obtainedScreen->screen());
1391     }
1392 }
1393 
format() const1394 QSurfaceFormat QWindowsWindow::format() const
1395 {
1396     return window()->requestedFormat();
1397 }
1398 
fireExpose(const QRegion & region,bool force)1399 void QWindowsWindow::fireExpose(const QRegion &region, bool force)
1400 {
1401     if (region.isEmpty() && !force)
1402         clearFlag(Exposed);
1403     else
1404         setFlag(Exposed);
1405     QWindowSystemInterface::handleExposeEvent(window(), region);
1406 }
1407 
fireFullExpose(bool force)1408 void QWindowsWindow::fireFullExpose(bool force)
1409 {
1410     fireExpose(QRect(QPoint(0, 0), m_data.geometry.size()), force);
1411 }
1412 
destroyWindow()1413 void QWindowsWindow::destroyWindow()
1414 {
1415     qCDebug(lcQpaWindows) << __FUNCTION__ << this << window() << m_data.hwnd;
1416     if (m_data.hwnd) { // Stop event dispatching before Window is destroyed.
1417         setFlag(WithinDestroy);
1418         // Clear any transient child relationships as Windows will otherwise destroy them (QTBUG-35499, QTBUG-36666)
1419         const auto tlw = QGuiApplication::topLevelWindows();
1420         for (QWindow *w : tlw) {
1421             if (w->transientParent() == window()) {
1422                 if (QWindowsWindow *tw = QWindowsWindow::windowsWindowOf(w))
1423                     tw->updateTransientParent();
1424             }
1425         }
1426         QWindowsContext *context = QWindowsContext::instance();
1427         if (context->windowUnderMouse() == window())
1428             context->clearWindowUnderMouse();
1429         if (hasMouseCapture())
1430             setMouseGrabEnabled(false);
1431         setDropSiteEnabled(false);
1432 #if QT_CONFIG(vulkan)
1433         if (m_vkSurface) {
1434             QVulkanInstance *inst = window()->vulkanInstance();
1435             if (inst)
1436                 static_cast<QWindowsVulkanInstance *>(inst->handle())->destroySurface(m_vkSurface);
1437             m_vkSurface = VK_NULL_HANDLE;
1438         }
1439 #endif
1440 #ifndef QT_NO_OPENGL
1441         if (m_surface) {
1442             if (QWindowsStaticOpenGLContext *staticOpenGLContext = QWindowsIntegration::staticOpenGLContext())
1443                 staticOpenGLContext->destroyWindowSurface(m_surface);
1444             m_surface = nullptr;
1445         }
1446 #endif
1447         DestroyWindow(m_data.hwnd);
1448         context->removeWindow(m_data.hwnd);
1449         m_data.hwnd = nullptr;
1450     }
1451 }
1452 
updateDropSite(bool topLevel)1453 void QWindowsWindow::updateDropSite(bool topLevel)
1454 {
1455     bool enabled = false;
1456     bool parentIsEmbedded = false;
1457 
1458     if (!topLevel) {
1459         // if the parent window is a foreign window wrapped via QWindow::fromWinId, we need to enable the drop site
1460         // on the first child window
1461         const QWindow *parent = window()->parent();
1462         if (parent && parent->handle() && parent->handle()->isForeignWindow())
1463             parentIsEmbedded = true;
1464     }
1465 
1466     if (topLevel || parentIsEmbedded) {
1467         switch (window()->type()) {
1468         case Qt::Window:
1469         case Qt::Dialog:
1470         case Qt::Sheet:
1471         case Qt::Drawer:
1472         case Qt::Popup:
1473         case Qt::Tool:
1474             enabled = true;
1475             break;
1476         default:
1477             break;
1478         }
1479     }
1480     setDropSiteEnabled(enabled);
1481 }
1482 
setDropSiteEnabled(bool dropEnabled)1483 void QWindowsWindow::setDropSiteEnabled(bool dropEnabled)
1484 {
1485     if (isDropSiteEnabled() == dropEnabled)
1486         return;
1487     qCDebug(lcQpaMime) << __FUNCTION__ << window() << dropEnabled;
1488 #if QT_CONFIG(clipboard) && QT_CONFIG(draganddrop)
1489     if (dropEnabled) {
1490         Q_ASSERT(m_data.hwnd);
1491         m_dropTarget = new QWindowsOleDropTarget(window());
1492         RegisterDragDrop(m_data.hwnd, m_dropTarget);
1493         CoLockObjectExternal(m_dropTarget, true, true);
1494     } else {
1495         CoLockObjectExternal(m_dropTarget, false, true);
1496         m_dropTarget->Release();
1497         RevokeDragDrop(m_data.hwnd);
1498         m_dropTarget = nullptr;
1499     }
1500 #endif // QT_CONFIG(clipboard) && QT_CONFIG(draganddrop)
1501 }
1502 
1503 bool QWindowsWindow::m_screenForGLInitialized = false;
1504 
displayChanged()1505 void QWindowsWindow::displayChanged()
1506 {
1507     m_screenForGLInitialized = false;
1508 }
1509 
settingsChanged()1510 void QWindowsWindow::settingsChanged()
1511 {
1512     m_screenForGLInitialized = false;
1513 }
1514 
forcedScreenForGLWindow(const QWindow * w)1515 QScreen *QWindowsWindow::forcedScreenForGLWindow(const QWindow *w)
1516 {
1517     static QString forceToScreen;
1518     if (!m_screenForGLInitialized) {
1519         forceToScreen = GpuDescription::detect().gpuSuitableScreen;
1520         m_screenForGLInitialized = true;
1521     }
1522     return forceToScreen.isEmpty() ? nullptr : screenForName(w, forceToScreen);
1523 }
1524 
1525 // Returns topmost QWindowsWindow ancestor even if there are embedded windows in the chain.
1526 // Returns this window if it is the topmost ancestor.
topLevelOf(QWindow * w)1527 QWindow *QWindowsWindow::topLevelOf(QWindow *w)
1528 {
1529     while (QWindow *parent = w->parent())
1530         w = parent;
1531 
1532     if (const QPlatformWindow *handle = w->handle()) {
1533         const auto *ww = static_cast<const QWindowsWindow *>(handle);
1534         if (ww->isEmbedded()) {
1535             HWND parentHWND = GetAncestor(ww->handle(), GA_PARENT);
1536             const HWND desktopHwnd = GetDesktopWindow();
1537             const QWindowsContext *ctx = QWindowsContext::instance();
1538             while (parentHWND && parentHWND != desktopHwnd) {
1539                 if (QWindowsWindow *ancestor = ctx->findPlatformWindow(parentHWND))
1540                     return topLevelOf(ancestor->window());
1541                 parentHWND = GetAncestor(parentHWND, GA_PARENT);
1542             }
1543         }
1544     }
1545     return w;
1546 }
1547 
1548 QWindowsWindowData
create(const QWindow * w,const QWindowsWindowData & parameters,const QString & title)1549     QWindowsWindowData::create(const QWindow *w,
1550                                        const QWindowsWindowData &parameters,
1551                                        const QString &title)
1552 {
1553     WindowCreationData creationData;
1554     creationData.fromWindow(w, parameters.flags);
1555     QWindowsWindowData result = creationData.create(w, parameters, title);
1556     // Force WM_NCCALCSIZE (with wParam=1) via SWP_FRAMECHANGED for custom margin.
1557     creationData.initialize(w, result.hwnd, !parameters.customMargins.isNull(), 1);
1558     return result;
1559 }
1560 
setVisible(bool visible)1561 void QWindowsWindow::setVisible(bool visible)
1562 {
1563     const QWindow *win = window();
1564     qCDebug(lcQpaWindows) << __FUNCTION__ << this << win << m_data.hwnd << visible;
1565     if (m_data.hwnd) {
1566         if (visible) {
1567             show_sys();
1568 
1569             // When the window is layered, we won't get WM_PAINT, and "we" are in control
1570             // over the rendering of the window
1571             // There is nobody waiting for this, so we don't need to flush afterwards.
1572             if (isLayered())
1573                 fireFullExpose();
1574             // QTBUG-44928, QTBUG-7386: This is to resolve the problem where popups are
1575             // opened from the system tray and not being implicitly activated
1576 
1577             if (win->type() == Qt::Popup && !win->parent() && !QGuiApplication::focusWindow())
1578                 SetForegroundWindow(m_data.hwnd);
1579         } else {
1580             if (hasMouseCapture())
1581                 setMouseGrabEnabled(false);
1582             if (window()->flags() & Qt::Popup) // from QWidgetPrivate::hide_sys(), activate other
1583                 ShowWindow(m_data.hwnd, SW_HIDE);
1584             else
1585                 hide_sys();
1586             fireExpose(QRegion());
1587         }
1588     }
1589 }
1590 
isVisible() const1591 bool QWindowsWindow::isVisible() const
1592 {
1593     return m_data.hwnd && IsWindowVisible(m_data.hwnd);
1594 }
1595 
isActive() const1596 bool QWindowsWindow::isActive() const
1597 {
1598     // Check for native windows or children of the active native window.
1599     if (const HWND activeHwnd = GetForegroundWindow())
1600         if (m_data.hwnd == activeHwnd || IsChild(activeHwnd, m_data.hwnd))
1601             return true;
1602     return false;
1603 }
1604 
isAncestorOf(const QPlatformWindow * child) const1605 bool QWindowsWindow::isAncestorOf(const QPlatformWindow *child) const
1606 {
1607     const auto *childWindow = static_cast<const QWindowsWindow *>(child);
1608     return IsChild(m_data.hwnd, childWindow->handle());
1609 }
1610 
isEmbedded() const1611 bool QWindowsWindow::isEmbedded() const
1612 {
1613     return m_data.embedded;
1614 }
1615 
mapToGlobal(const QPoint & pos) const1616 QPoint QWindowsWindow::mapToGlobal(const QPoint &pos) const
1617 {
1618     return m_data.hwnd ? QWindowsGeometryHint::mapToGlobal(m_data.hwnd, pos) : pos;
1619 }
1620 
mapFromGlobal(const QPoint & pos) const1621 QPoint QWindowsWindow::mapFromGlobal(const QPoint &pos) const
1622 {
1623     return m_data.hwnd ? QWindowsGeometryHint::mapFromGlobal(m_data.hwnd, pos) : pos;
1624 }
1625 
1626 // Update the transient parent for a toplevel window. The concept does not
1627 // really exist on Windows, the relationship is set by passing a parent along with !WS_CHILD
1628 // to window creation or by setting the parent using  GWL_HWNDPARENT (as opposed to
1629 // SetParent, which would make it a real child).
1630 
1631 #ifndef GWL_HWNDPARENT
1632 #    define GWL_HWNDPARENT (-8)
1633 #endif
1634 
updateTransientParent() const1635 void QWindowsWindow::updateTransientParent() const
1636 {
1637     if (window()->type() == Qt::Popup)
1638         return; // QTBUG-34503, // a popup stays on top, no parent, see also WindowCreationData::fromWindow().
1639     // Update transient parent.
1640     const HWND oldTransientParent = GetWindow(m_data.hwnd, GW_OWNER);
1641     HWND newTransientParent = nullptr;
1642     if (const QWindow *tp = window()->transientParent())
1643         if (const QWindowsWindow *tw = QWindowsWindow::windowsWindowOf(tp))
1644             if (!tw->testFlag(WithinDestroy)) // Prevent destruction by parent window (QTBUG-35499, QTBUG-36666)
1645                 newTransientParent = tw->handle();
1646 
1647     // QTSOLBUG-71: When using the MFC/winmigrate solution, it is possible that a child
1648     // window is found, which can cause issues with modality. Loop up to top level.
1649     while (newTransientParent && (GetWindowLongPtr(newTransientParent, GWL_STYLE) & WS_CHILD) != 0)
1650         newTransientParent = GetParent(newTransientParent);
1651 
1652     if (newTransientParent != oldTransientParent)
1653         SetWindowLongPtr(m_data.hwnd, GWL_HWNDPARENT, LONG_PTR(newTransientParent));
1654 }
1655 
testShowWithoutActivating(const QWindow * window)1656 static inline bool testShowWithoutActivating(const QWindow *window)
1657 {
1658     // QWidget-attribute Qt::WA_ShowWithoutActivating .
1659     const QVariant showWithoutActivating = window->property("_q_showWithoutActivating");
1660     return showWithoutActivating.isValid() && showWithoutActivating.toBool();
1661 }
1662 
setMinimizedGeometry(HWND hwnd,const QRect & r)1663 static void setMinimizedGeometry(HWND hwnd, const QRect &r)
1664 {
1665     WINDOWPLACEMENT windowPlacement;
1666     windowPlacement.length = sizeof(WINDOWPLACEMENT);
1667     if (GetWindowPlacement(hwnd, &windowPlacement)) {
1668         windowPlacement.showCmd = SW_SHOWMINIMIZED;
1669         windowPlacement.rcNormalPosition = RECTfromQRect(r);
1670         SetWindowPlacement(hwnd, &windowPlacement);
1671     }
1672 }
1673 
setRestoreMaximizedFlag(HWND hwnd,bool set=true)1674 static void setRestoreMaximizedFlag(HWND hwnd, bool set = true)
1675 {
1676     // Let Windows know that we need to restore as maximized
1677     WINDOWPLACEMENT windowPlacement;
1678     windowPlacement.length = sizeof(WINDOWPLACEMENT);
1679     if (GetWindowPlacement(hwnd, &windowPlacement)) {
1680         if (set)
1681             windowPlacement.flags |= WPF_RESTORETOMAXIMIZED;
1682         else
1683             windowPlacement.flags &= ~WPF_RESTORETOMAXIMIZED;
1684         SetWindowPlacement(hwnd, &windowPlacement);
1685     }
1686 }
1687 
1688 // partially from QWidgetPrivate::show_sys()
show_sys() const1689 void QWindowsWindow::show_sys() const
1690 {
1691     int sm = SW_SHOWNORMAL;
1692     bool fakedMaximize = false;
1693     bool restoreMaximize = false;
1694     const QWindow *w = window();
1695     const Qt::WindowFlags flags = w->flags();
1696     const Qt::WindowType type = w->type();
1697     if (w->isTopLevel()) {
1698         const Qt::WindowStates state = w->windowStates();
1699         if (state & Qt::WindowMinimized) {
1700             sm = SW_SHOWMINIMIZED;
1701             if (!isVisible())
1702                 sm = SW_SHOWMINNOACTIVE;
1703             if (state & Qt::WindowMaximized)
1704                 restoreMaximize = true;
1705         } else {
1706             updateTransientParent();
1707             if (state & Qt::WindowMaximized) {
1708                 sm = SW_SHOWMAXIMIZED;
1709                 // Windows will not behave correctly when we try to maximize a window which does not
1710                 // have minimize nor maximize buttons in the window frame. Windows would then ignore
1711                 // non-available geometry, and rather maximize the widget to the full screen, minus the
1712                 // window frame (caption). So, we do a trick here, by adding a maximize button before
1713                 // maximizing the widget, and then remove the maximize button afterwards.
1714                 if (flags & Qt::WindowTitleHint &&
1715                         !(flags & (Qt::WindowMinMaxButtonsHint | Qt::FramelessWindowHint))) {
1716                     fakedMaximize = TRUE;
1717                     setStyle(style() | WS_MAXIMIZEBOX);
1718                 }
1719             } // Qt::WindowMaximized
1720         } // !Qt::WindowMinimized
1721     }
1722     if (type == Qt::Popup || type == Qt::ToolTip || type == Qt::Tool || testShowWithoutActivating(w))
1723         sm = SW_SHOWNOACTIVATE;
1724 
1725     if (w->windowStates() & Qt::WindowMaximized)
1726         setFlag(WithinMaximize); // QTBUG-8361
1727 
1728     ShowWindow(m_data.hwnd, sm);
1729 
1730     clearFlag(WithinMaximize);
1731 
1732     if (fakedMaximize) {
1733         setStyle(style() & ~WS_MAXIMIZEBOX);
1734         SetWindowPos(m_data.hwnd, nullptr, 0, 0, 0, 0,
1735                      SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER
1736                      | SWP_FRAMECHANGED);
1737     }
1738     if (restoreMaximize)
1739         setRestoreMaximizedFlag(m_data.hwnd);
1740 }
1741 
setParent(const QPlatformWindow * newParent)1742 void QWindowsWindow::setParent(const QPlatformWindow *newParent)
1743 {
1744     qCDebug(lcQpaWindows) << __FUNCTION__ << window() << newParent;
1745 
1746     if (m_data.hwnd)
1747         setParent_sys(newParent);
1748 }
1749 
setParent_sys(const QPlatformWindow * parent)1750 void QWindowsWindow::setParent_sys(const QPlatformWindow *parent)
1751 {
1752     // Use GetAncestor instead of GetParent, as GetParent can return owner window for toplevels
1753     HWND oldParentHWND = parentHwnd();
1754     HWND newParentHWND = nullptr;
1755     if (parent) {
1756         const auto *parentW = static_cast<const QWindowsWindow *>(parent);
1757         newParentHWND = parentW->handle();
1758 
1759     }
1760 
1761     // NULL handle means desktop window, which also has its proper handle -> disambiguate
1762     HWND desktopHwnd = GetDesktopWindow();
1763     if (oldParentHWND == desktopHwnd)
1764         oldParentHWND = nullptr;
1765     if (newParentHWND == desktopHwnd)
1766         newParentHWND = nullptr;
1767 
1768     if (newParentHWND != oldParentHWND) {
1769         const bool wasTopLevel = oldParentHWND == nullptr;
1770         const bool isTopLevel = newParentHWND == nullptr;
1771 
1772         setFlag(WithinSetParent);
1773         SetParent(m_data.hwnd, newParentHWND);
1774         clearFlag(WithinSetParent);
1775 
1776         // WS_CHILD/WS_POPUP must be manually set/cleared in addition
1777         // to dialog frames, etc (see  SetParent() ) if the top level state changes.
1778         // Force toplevel state as QWindow::isTopLevel cannot be relied upon here.
1779         if (wasTopLevel != isTopLevel) {
1780             setDropSiteEnabled(false);
1781             setWindowFlags_sys(window()->flags(), unsigned(isTopLevel ? WindowCreationData::ForceTopLevel : WindowCreationData::ForceChild));
1782             updateDropSite(isTopLevel);
1783         }
1784     }
1785 }
1786 
handleHidden()1787 void QWindowsWindow::handleHidden()
1788 {
1789     fireExpose(QRegion());
1790 }
1791 
handleCompositionSettingsChanged()1792 void QWindowsWindow::handleCompositionSettingsChanged()
1793 {
1794     const QWindow *w = window();
1795     if ((w->surfaceType() == QWindow::OpenGLSurface || w->surfaceType() == QWindow::VulkanSurface)
1796             && w->format().hasAlpha()) {
1797         applyBlurBehindWindow(handle());
1798     }
1799 }
1800 
normalFrameGeometry(HWND hwnd)1801 static QRect normalFrameGeometry(HWND hwnd)
1802 {
1803     WINDOWPLACEMENT wp;
1804     wp.length = sizeof(WINDOWPLACEMENT);
1805     if (GetWindowPlacement(hwnd, &wp)) {
1806         const QRect result = qrectFromRECT(wp.rcNormalPosition);
1807         return result.translated(windowPlacementOffset(hwnd, result.topLeft()));
1808     }
1809     return QRect();
1810 }
1811 
normalGeometry() const1812 QRect QWindowsWindow::normalGeometry() const
1813 {
1814     // Check for fake 'fullscreen' mode.
1815     const bool fakeFullScreen =
1816         m_savedFrameGeometry.isValid() && (window()->windowStates() & Qt::WindowFullScreen);
1817     const QRect frame = fakeFullScreen ? m_savedFrameGeometry : normalFrameGeometry(m_data.hwnd);
1818     const QMargins margins = fakeFullScreen
1819         ? QWindowsGeometryHint::frame(handle(), m_savedStyle, 0)
1820         : fullFrameMargins();
1821     return frame.isValid() ? frame.marginsRemoved(margins) : frame;
1822 }
1823 
msgUnableToSetGeometry(const QWindowsWindow * platformWindow,const QRect & requestedRect,const QRect & obtainedRect,const QMargins & fullMargins,const QMargins & customMargins)1824 static QString msgUnableToSetGeometry(const QWindowsWindow *platformWindow,
1825                                       const QRect &requestedRect,
1826                                       const QRect &obtainedRect,
1827                                       const QMargins &fullMargins,
1828                                       const QMargins &customMargins)
1829 {
1830     QString result;
1831     QDebug debug(&result);
1832     debug.nospace();
1833     debug.noquote();
1834     const auto window = platformWindow->window();
1835     debug << "Unable to set geometry ";
1836     formatBriefRectangle(debug, requestedRect);
1837     debug << " (frame: ";
1838     formatBriefRectangle(debug, requestedRect + fullMargins);
1839     debug << ") on " << window->metaObject()->className() << "/\""
1840           << window->objectName() << "\" on \"" << window->screen()->name()
1841           << "\". Resulting geometry: ";
1842     formatBriefRectangle(debug, obtainedRect);
1843     debug << " (frame: ";
1844     formatBriefRectangle(debug, obtainedRect + fullMargins);
1845     debug << ") margins: ";
1846     formatBriefMargins(debug, fullMargins);
1847     if (!customMargins.isNull()) {
1848        debug << " custom margin: ";
1849        formatBriefMargins(debug, customMargins);
1850     }
1851     const auto minimumSize = window->minimumSize();
1852     const bool hasMinimumSize = !minimumSize.isEmpty();
1853     if (hasMinimumSize)
1854         debug << " minimum size: " << minimumSize.width() << 'x' << minimumSize.height();
1855     const auto maximumSize = window->maximumSize();
1856     const bool hasMaximumSize = maximumSize.width() != QWINDOWSIZE_MAX || maximumSize.height() != QWINDOWSIZE_MAX;
1857     if (hasMaximumSize)
1858         debug << " maximum size: " << maximumSize.width() << 'x' << maximumSize.height();
1859     if (hasMinimumSize || hasMaximumSize) {
1860         MINMAXINFO minmaxInfo;
1861         memset(&minmaxInfo, 0, sizeof(minmaxInfo));
1862         platformWindow->getSizeHints(&minmaxInfo);
1863         debug << ' ' << minmaxInfo;
1864     }
1865     debug << ')';
1866     return result;
1867 }
1868 
setGeometry(const QRect & rectIn)1869 void QWindowsWindow::setGeometry(const QRect &rectIn)
1870 {
1871     QRect rect = rectIn;
1872     // This means it is a call from QWindow::setFramePosition() and
1873     // the coordinates include the frame (size is still the contents rectangle).
1874     if (QWindowsGeometryHint::positionIncludesFrame(window())) {
1875         const QMargins margins = frameMargins();
1876         rect.moveTopLeft(rect.topLeft() + QPoint(margins.left(), margins.top()));
1877     }
1878     if (m_windowState & Qt::WindowMinimized)
1879         m_data.geometry = rect; // Otherwise set by handleGeometryChange() triggered by event.
1880     if (m_data.hwnd) {
1881         // A ResizeEvent with resulting geometry will be sent. If we cannot
1882         // achieve that size (for example, window title minimal constraint),
1883         // notify and warn.
1884         setFlag(WithinSetGeometry);
1885         setGeometry_sys(rect);
1886         clearFlag(WithinSetGeometry);
1887         if (m_data.geometry != rect && (isVisible() || QLibraryInfo::isDebugBuild())) {
1888             const auto warning =
1889                 msgUnableToSetGeometry(this, rectIn, m_data.geometry,
1890                                        m_data.fullFrameMargins, m_data.customMargins);
1891             qWarning("%s: %s", __FUNCTION__, qPrintable(warning));
1892         }
1893     } else {
1894         QPlatformWindow::setGeometry(rect);
1895     }
1896 }
1897 
handleMoved()1898 void QWindowsWindow::handleMoved()
1899 {
1900     // Minimize/Set parent can send nonsensical move events.
1901     if (!IsIconic(m_data.hwnd) && !testFlag(WithinSetParent))
1902         handleGeometryChange();
1903 }
1904 
handleResized(int wParam)1905 void QWindowsWindow::handleResized(int wParam)
1906 {
1907     switch (wParam) {
1908     case SIZE_MAXHIDE: // Some other window affected.
1909     case SIZE_MAXSHOW:
1910         return;
1911     case SIZE_MINIMIZED: // QTBUG-53577, prevent state change events during programmatic state change
1912         if (!testFlag(WithinSetStyle) && !testFlag(WithinSetGeometry))
1913             handleWindowStateChange(m_windowState | Qt::WindowMinimized);
1914         return;
1915     case SIZE_MAXIMIZED:
1916         handleGeometryChange();
1917         if (!testFlag(WithinSetStyle) && !testFlag(WithinSetGeometry))
1918             handleWindowStateChange(Qt::WindowMaximized | (isFullScreen_sys() ? Qt::WindowFullScreen
1919                                                                               : Qt::WindowNoState));
1920         break;
1921     case SIZE_RESTORED:
1922         handleGeometryChange();
1923         if (!testFlag(WithinSetStyle) && !testFlag(WithinSetGeometry)) {
1924             if (isFullScreen_sys())
1925                 handleWindowStateChange(
1926                     Qt::WindowFullScreen
1927                     | (testFlag(MaximizeToFullScreen) ? Qt::WindowMaximized : Qt::WindowNoState));
1928             else if (m_windowState != Qt::WindowNoState && !testFlag(MaximizeToFullScreen))
1929                 handleWindowStateChange(Qt::WindowNoState);
1930         }
1931         break;
1932     }
1933 }
1934 
equalDpi(const QDpi & d1,const QDpi & d2)1935 static inline bool equalDpi(const QDpi &d1, const QDpi &d2)
1936 {
1937     return qFuzzyCompare(d1.first, d2.first) && qFuzzyCompare(d1.second, d2.second);
1938 }
1939 
checkForScreenChanged(ScreenChangeMode mode)1940 void QWindowsWindow::checkForScreenChanged(ScreenChangeMode mode)
1941 {
1942     if (parent() || QWindowsScreenManager::isSingleScreen())
1943         return;
1944 
1945     QPlatformScreen *currentScreen = screen();
1946     const QWindowsScreen *newScreen =
1947         QWindowsContext::instance()->screenManager().screenForHwnd(m_data.hwnd);
1948     if (newScreen == nullptr || newScreen == currentScreen)
1949         return;
1950     // For screens with different DPI: postpone until WM_DPICHANGE
1951     // Check on currentScreen as it can be 0 when resuming a session (QTBUG-80436).
1952     if (mode == FromGeometryChange && currentScreen != nullptr
1953         && !equalDpi(currentScreen->logicalDpi(), newScreen->logicalDpi())) {
1954         return;
1955     }
1956     qCDebug(lcQpaWindows).noquote().nospace() << __FUNCTION__
1957         << ' ' << window() << " \"" << (currentScreen ? currentScreen->name() : QString())
1958         << "\"->\"" << newScreen->name() << '"';
1959     updateFullFrameMargins();
1960     QWindowSystemInterface::handleWindowScreenChanged<QWindowSystemInterface::SynchronousDelivery>(window(), newScreen->screen());
1961 }
1962 
handleGeometryChange()1963 void QWindowsWindow::handleGeometryChange()
1964 {
1965     const QRect previousGeometry = m_data.geometry;
1966     m_data.geometry = geometry_sys();
1967     if (testFlag(WithinDpiChanged)
1968         && QWindowsContext::instance()->screenManager().screenForHwnd(m_data.hwnd) != screen()) {
1969         return; // QGuiApplication will send resize when screen actually changes
1970     }
1971     QWindowSystemInterface::handleGeometryChange(window(), m_data.geometry);
1972     // QTBUG-32121: OpenGL/normal windows (with exception of ANGLE) do not receive
1973     // expose events when shrinking, synthesize.
1974     if (!testFlag(OpenGL_ES2) && isExposed()
1975         && m_data.geometry.size() != previousGeometry.size() // Exclude plain move
1976         // One dimension grew -> Windows will send expose, no need to synthesize.
1977         && !(m_data.geometry.width() > previousGeometry.width() || m_data.geometry.height() > previousGeometry.height())) {
1978         fireFullExpose(true);
1979     }
1980 
1981     const bool wasSync = testFlag(SynchronousGeometryChangeEvent);
1982     checkForScreenChanged();
1983 
1984     if (testFlag(SynchronousGeometryChangeEvent))
1985         QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
1986 
1987     if (!wasSync)
1988         clearFlag(SynchronousGeometryChangeEvent);
1989     qCDebug(lcQpaEvents) << __FUNCTION__ << this << window() << m_data.geometry;
1990 }
1991 
setGeometry_sys(const QRect & rect) const1992 void QWindowsBaseWindow::setGeometry_sys(const QRect &rect) const
1993 {
1994     const QMargins margins = fullFrameMargins();
1995     const QRect frameGeometry = rect + margins;
1996 
1997     qCDebug(lcQpaWindows) << '>' << __FUNCTION__ << window()
1998         << "\n from " << geometry_sys() << " frame: "
1999         << margins << " to " <<rect
2000         << " new frame: " << frameGeometry;
2001 
2002     bool result = false;
2003     const HWND hwnd = handle();
2004     WINDOWPLACEMENT windowPlacement;
2005     windowPlacement.length = sizeof(WINDOWPLACEMENT);
2006     GetWindowPlacement(hwnd, &windowPlacement);
2007     // If the window is hidden and in maximized state or minimized, instead of moving the
2008     // window, set the normal position of the window.
2009     if ((windowPlacement.showCmd == SW_MAXIMIZE && !IsWindowVisible(hwnd))
2010         || windowPlacement.showCmd == SW_SHOWMINIMIZED) {
2011         windowPlacement.rcNormalPosition =
2012             RECTfromQRect(frameGeometry.translated(-windowPlacementOffset(hwnd, frameGeometry.topLeft())));
2013         windowPlacement.showCmd = windowPlacement.showCmd == SW_SHOWMINIMIZED ? SW_SHOWMINIMIZED : SW_HIDE;
2014         result = SetWindowPlacement(hwnd, &windowPlacement);
2015     } else {
2016         int x = frameGeometry.x();
2017         if (!window()->isTopLevel()) {
2018             const HWND parentHandle = GetParent(hwnd);
2019             if (isRtlLayout(parentHandle)) {
2020                 RECT rect;
2021                 GetClientRect(parentHandle, &rect);
2022                 x = rect.right - frameGeometry.width() - x;
2023             }
2024         }
2025         result = MoveWindow(hwnd, x, frameGeometry.y(),
2026                             frameGeometry.width(), frameGeometry.height(), true);
2027     }
2028     qCDebug(lcQpaWindows) << '<' << __FUNCTION__ << window()
2029         << "\n resulting " << result << geometry_sys();
2030 }
2031 
2032 /*!
2033     Allocates a HDC for the window or returns the temporary one
2034     obtained from WinAPI BeginPaint within a WM_PAINT event.
2035 
2036     \sa releaseDC()
2037 */
2038 
getDC()2039 HDC QWindowsWindow::getDC()
2040 {
2041     if (!m_hdc) {
2042         m_hdc = GetDC(handle());
2043         if (QGuiApplication::layoutDirection() == Qt::RightToLeft)
2044             SetLayout(m_hdc, 0); // Clear RTL layout
2045     }
2046     return m_hdc;
2047 }
2048 
2049 /*!
2050     Relases the HDC for the window or does nothing in
2051     case it was obtained from WinAPI BeginPaint within a WM_PAINT event.
2052 
2053     \sa getDC()
2054 */
2055 
releaseDC()2056 void QWindowsWindow::releaseDC()
2057 {
2058     if (m_hdc) {
2059         ReleaseDC(handle(), m_hdc);
2060         m_hdc = nullptr;
2061     }
2062 }
2063 
dwmIsCompositionEnabled()2064 static inline bool dwmIsCompositionEnabled()
2065 {
2066     BOOL dWmCompositionEnabled = FALSE;
2067     return SUCCEEDED(DwmIsCompositionEnabled(&dWmCompositionEnabled)) && dWmCompositionEnabled == TRUE;
2068 }
2069 
isSoftwareGl()2070 static inline bool isSoftwareGl()
2071 {
2072 #if QT_CONFIG(dynamicgl)
2073     return QOpenGLStaticContext::opengl32.moduleIsNotOpengl32()
2074         && QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL;
2075 #else
2076     return false;
2077 #endif // dynamicgl
2078 }
2079 
handleWmPaint(HWND hwnd,UINT message,WPARAM,LPARAM)2080 bool QWindowsWindow::handleWmPaint(HWND hwnd, UINT message,
2081                                          WPARAM, LPARAM)
2082 {
2083     if (message == WM_ERASEBKGND) // Backing store - ignored.
2084         return true;
2085     // QTBUG-75455: Suppress WM_PAINT sent to invisible windows when setting WS_EX_LAYERED
2086     if (!window()->isVisible() && (GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED) != 0)
2087         return false;
2088     // Ignore invalid update bounding rectangles
2089     RECT updateRect;
2090     if (!GetUpdateRect(m_data.hwnd, &updateRect, FALSE))
2091         return false;
2092     PAINTSTRUCT ps;
2093 
2094     // GL software rendering (QTBUG-58178) and Windows 7/Aero off with some AMD cards
2095     // (QTBUG-60527) need InvalidateRect() to suppress artifacts while resizing.
2096     if (testFlag(OpenGLSurface) && (isSoftwareGl() || !dwmIsCompositionEnabled()))
2097         InvalidateRect(hwnd, nullptr, false);
2098 
2099     BeginPaint(hwnd, &ps);
2100 
2101     // Observed painting problems with Aero style disabled (QTBUG-7865).
2102     if (Q_UNLIKELY(!dwmIsCompositionEnabled())
2103             && ((testFlag(OpenGLSurface) && testFlag(OpenGLDoubleBuffered)) || testFlag(VulkanSurface)))
2104     {
2105         SelectClipRgn(ps.hdc, nullptr);
2106     }
2107 
2108     // If the a window is obscured by another window (such as a child window)
2109     // we still need to send isExposed=true, for compatibility.
2110     // Our tests depend on it.
2111     fireExpose(QRegion(qrectFromRECT(ps.rcPaint)), true);
2112     if (qSizeOfRect(updateRect) == m_data.geometry.size() && !QWindowsContext::instance()->asyncExpose())
2113         QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
2114 
2115     EndPaint(hwnd, &ps);
2116     return true;
2117 }
2118 
setWindowTitle(const QString & title)2119 void QWindowsWindow::setWindowTitle(const QString &title)
2120 {
2121     setWindowTitle_sys(QWindowsWindow::formatWindowTitle(title));
2122 }
2123 
setWindowFlags(Qt::WindowFlags flags)2124 void QWindowsWindow::setWindowFlags(Qt::WindowFlags flags)
2125 {
2126     qCDebug(lcQpaWindows) << '>' << __FUNCTION__ << this << window() << "\n    from: "
2127         << m_data.flags << "\n    to: " << flags;
2128     const QRect oldGeometry = geometry();
2129     if (m_data.flags != flags) {
2130         m_data.flags = flags;
2131         if (m_data.hwnd) {
2132             m_data = setWindowFlags_sys(flags);
2133             updateDropSite(window()->isTopLevel());
2134         }
2135     }
2136     // When switching to a frameless window, geometry
2137     // may change without a WM_MOVE. Report change manually.
2138     // Do not send synchronously as not to clobber the widget
2139     // geometry in a sequence of setting flags and geometry.
2140     const QRect newGeometry = geometry_sys();
2141     if (oldGeometry != newGeometry)
2142         handleGeometryChange();
2143 
2144     qCDebug(lcQpaWindows) << '<' << __FUNCTION__ << "\n    returns: "
2145         << m_data.flags << " geometry " << oldGeometry << "->" << newGeometry;
2146 }
2147 
setWindowFlags_sys(Qt::WindowFlags wt,unsigned flags) const2148 QWindowsWindowData QWindowsWindow::setWindowFlags_sys(Qt::WindowFlags wt,
2149                                                               unsigned flags) const
2150 {
2151     WindowCreationData creationData;
2152     creationData.fromWindow(window(), wt, flags);
2153     creationData.applyWindowFlags(m_data.hwnd);
2154     creationData.initialize(window(), m_data.hwnd, true, m_opacity);
2155 
2156     QWindowsWindowData result = m_data;
2157     result.flags = creationData.flags;
2158     result.embedded = creationData.embedded;
2159     return result;
2160 }
2161 
handleWindowStateChange(Qt::WindowStates state)2162 void QWindowsWindow::handleWindowStateChange(Qt::WindowStates state)
2163 {
2164     qCDebug(lcQpaWindows) << __FUNCTION__ << this << window()
2165                  << "\n    from " << m_windowState << " to " << state;
2166     m_windowState = state;
2167     QWindowSystemInterface::handleWindowStateChanged(window(), state);
2168     if (state & Qt::WindowMinimized) {
2169         handleHidden();
2170         QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents); // Tell QQuickWindow to stop rendering now.
2171     } else {
2172         // QTBUG-17548: We send expose events when receiving WM_Paint, but for
2173         // layered windows and transient children, we won't receive any WM_Paint.
2174         QWindow *w = window();
2175         bool exposeEventsSent = false;
2176         if (isLayered()) {
2177             fireFullExpose();
2178             exposeEventsSent = true;
2179         }
2180         const QWindowList allWindows = QGuiApplication::allWindows();
2181         for (QWindow *child : allWindows) {
2182             if (child != w && child->isVisible() && child->transientParent() == w) {
2183                 QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(child);
2184                 if (platformWindow && platformWindow->isLayered()) {
2185                     platformWindow->fireFullExpose();
2186                     exposeEventsSent = true;
2187                 }
2188             }
2189         }
2190         if (exposeEventsSent && !QWindowsContext::instance()->asyncExpose())
2191             QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
2192     }
2193 }
2194 
setWindowState(Qt::WindowStates state)2195 void QWindowsWindow::setWindowState(Qt::WindowStates state)
2196 {
2197     if (m_data.hwnd) {
2198         setWindowState_sys(state);
2199         m_windowState = state;
2200     }
2201 }
2202 
isFullScreen_sys() const2203 bool QWindowsWindow::isFullScreen_sys() const
2204 {
2205     const QWindow *w = window();
2206     if (!w->isTopLevel())
2207         return false;
2208     QRect geometry = geometry_sys();
2209     if (testFlag(HasBorderInFullScreen))
2210         geometry += QMargins(1, 1, 1, 1);
2211     QPlatformScreen *screen = screenForGeometry(geometry);
2212     return screen && geometry == screen->geometry();
2213 }
2214 
2215 /*!
2216     \brief Change the window state.
2217 
2218     \note Window frames change when maximized;
2219     the top margin shrinks somewhat but that cannot be obtained using
2220     AdjustWindowRectEx().
2221 
2222     \note Some calls to SetWindowLong require a subsequent call
2223     to ShowWindow.
2224 */
2225 
setWindowState_sys(Qt::WindowStates newState)2226 void QWindowsWindow::setWindowState_sys(Qt::WindowStates newState)
2227 {
2228     const Qt::WindowStates oldState = m_windowState;
2229     if (oldState == newState)
2230         return;
2231     qCDebug(lcQpaWindows) << '>' << __FUNCTION__ << this << window()
2232         << " from " << oldState << " to " << newState;
2233 
2234     const bool visible = isVisible();
2235     auto stateChange = oldState ^ newState;
2236 
2237     if (stateChange & Qt::WindowFullScreen) {
2238         if (newState & Qt::WindowFullScreen) {
2239 #ifndef Q_FLATTEN_EXPOSE
2240             UINT newStyle = WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_POPUP;
2241 #else
2242             UINT newStyle = WS_POPUP;
2243 #endif
2244             // Save geometry and style to be restored when fullscreen
2245             // is turned off again, since on Windows, it is not a real
2246             // Window state but emulated by changing geometry and style.
2247             if (!m_savedStyle) {
2248                 m_savedStyle = style();
2249                 if ((oldState & Qt::WindowMinimized) || (oldState & Qt::WindowMaximized)) {
2250                     const QRect nf = normalFrameGeometry(m_data.hwnd);
2251                     if (nf.isValid())
2252                         m_savedFrameGeometry = nf;
2253                 } else {
2254                     m_savedFrameGeometry = frameGeometry_sys();
2255                 }
2256             }
2257             if (newState & Qt::WindowMaximized)
2258                 setFlag(MaximizeToFullScreen);
2259             if (m_savedStyle & WS_SYSMENU)
2260                 newStyle |= WS_SYSMENU;
2261             if (visible)
2262                 newStyle |= WS_VISIBLE;
2263             if (testFlag(HasBorderInFullScreen))
2264                 newStyle |= WS_BORDER;
2265             setStyle(newStyle);
2266             // Use geometry of QWindow::screen() within creation or the virtual screen the
2267             // window is in (QTBUG-31166, QTBUG-30724).
2268             const QScreen *screen = window()->screen();
2269             if (!screen)
2270                 screen = QGuiApplication::primaryScreen();
2271             const QRect r = screen ? QHighDpi::toNativePixels(screen->geometry(), window()) : m_savedFrameGeometry;
2272 
2273             if (newState & Qt::WindowMinimized) {
2274                 setMinimizedGeometry(m_data.hwnd, r);
2275                 if (stateChange & Qt::WindowMaximized)
2276                     setRestoreMaximizedFlag(m_data.hwnd, newState & Qt::WindowMaximized);
2277             } else {
2278                 const UINT swpf = SWP_FRAMECHANGED | SWP_NOACTIVATE;
2279                 const bool wasSync = testFlag(SynchronousGeometryChangeEvent);
2280                 setFlag(SynchronousGeometryChangeEvent);
2281                 SetWindowPos(m_data.hwnd, HWND_TOP, r.left(), r.top(), r.width(), r.height(), swpf);
2282                 if (!wasSync)
2283                     clearFlag(SynchronousGeometryChangeEvent);
2284                 clearFlag(MaximizeToFullScreen);
2285                 QWindowSystemInterface::handleGeometryChange(window(), r);
2286                 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
2287             }
2288         } else {
2289             // Restore saved state.
2290             unsigned newStyle = m_savedStyle ? m_savedStyle : style();
2291             if (visible)
2292                 newStyle |= WS_VISIBLE;
2293             setStyle(newStyle);
2294 
2295             const QScreen *screen = window()->screen();
2296             if (!screen)
2297                 screen = QGuiApplication::primaryScreen();
2298             // That area of the virtual desktop might not be covered by a screen anymore.
2299             if (const auto platformScreen = screen->handle()) {
2300                 if (!platformScreen->geometry().intersects(m_savedFrameGeometry))
2301                     m_savedFrameGeometry.moveTo(platformScreen->geometry().topLeft());
2302             }
2303 
2304             if (newState & Qt::WindowMinimized) {
2305                 setMinimizedGeometry(m_data.hwnd, m_savedFrameGeometry);
2306                 if (stateChange & Qt::WindowMaximized)
2307                     setRestoreMaximizedFlag(m_data.hwnd, newState & Qt::WindowMaximized);
2308             } else {
2309                 UINT swpf = SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOACTIVATE;
2310                 if (!m_savedFrameGeometry.isValid())
2311                     swpf |= SWP_NOSIZE | SWP_NOMOVE;
2312                 const bool wasSync = testFlag(SynchronousGeometryChangeEvent);
2313                 setFlag(SynchronousGeometryChangeEvent);
2314                 // After maximized/fullscreen; the window can be in a maximized state. Clear
2315                 // it before applying the normal geometry.
2316                 if (windowVisibility_sys(m_data.hwnd) == QWindow::Maximized)
2317                     ShowWindow(m_data.hwnd, SW_SHOWNOACTIVATE);
2318                 SetWindowPos(m_data.hwnd, nullptr, m_savedFrameGeometry.x(), m_savedFrameGeometry.y(),
2319                              m_savedFrameGeometry.width(), m_savedFrameGeometry.height(), swpf);
2320                 if (!wasSync)
2321                     clearFlag(SynchronousGeometryChangeEvent);
2322                 // preserve maximized state
2323                 if (visible) {
2324                     setFlag(WithinMaximize);
2325                     ShowWindow(m_data.hwnd,
2326                                (newState & Qt::WindowMaximized) ? SW_MAXIMIZE : SW_SHOWNA);
2327                     clearFlag(WithinMaximize);
2328                 }
2329             }
2330             m_savedStyle = 0;
2331             m_savedFrameGeometry = QRect();
2332         }
2333     } else if ((oldState & Qt::WindowMaximized) != (newState & Qt::WindowMaximized)) {
2334         if (visible && !(newState & Qt::WindowMinimized)) {
2335             setFlag(WithinMaximize);
2336             if (newState & Qt::WindowFullScreen)
2337                 setFlag(MaximizeToFullScreen);
2338             ShowWindow(m_data.hwnd,
2339                        (newState & Qt::WindowMaximized) ? SW_MAXIMIZE : SW_SHOWNOACTIVATE);
2340             clearFlag(WithinMaximize);
2341             clearFlag(MaximizeToFullScreen);
2342         } else if (visible && (oldState & newState & Qt::WindowMinimized)) {
2343             // change of the maximized state while keeping minimized
2344             setRestoreMaximizedFlag(m_data.hwnd, newState & Qt::WindowMaximized);
2345         }
2346     }
2347 
2348     if (stateChange & Qt::WindowMinimized) {
2349         if (visible) {
2350             ShowWindow(m_data.hwnd,
2351                        (newState & Qt::WindowMinimized) ? SW_MINIMIZE :
2352                        (newState & Qt::WindowMaximized) ? SW_MAXIMIZE : SW_SHOWNORMAL);
2353             if ((newState & Qt::WindowMinimized) && (stateChange & Qt::WindowMaximized))
2354                 setRestoreMaximizedFlag(m_data.hwnd, newState & Qt::WindowMaximized);
2355         }
2356     }
2357     qCDebug(lcQpaWindows) << '<' << __FUNCTION__ << this << window() << newState;
2358 }
2359 
setStyle(unsigned s) const2360 void QWindowsWindow::setStyle(unsigned s) const
2361 {
2362     qCDebug(lcQpaWindows) << __FUNCTION__ << this << window() << debugWinStyle(s);
2363     setFlag(WithinSetStyle);
2364     SetWindowLongPtr(m_data.hwnd, GWL_STYLE, s);
2365     clearFlag(WithinSetStyle);
2366 }
2367 
setExStyle(unsigned s) const2368 void QWindowsWindow::setExStyle(unsigned s) const
2369 {
2370     qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << ' ' << this << ' ' << window()
2371         << " 0x" << QByteArray::number(s, 16);
2372     SetWindowLongPtr(m_data.hwnd, GWL_EXSTYLE, s);
2373 }
2374 
windowEvent(QEvent * event)2375 bool QWindowsWindow::windowEvent(QEvent *event)
2376 {
2377     switch (event->type()) {
2378     case QEvent::WindowBlocked: // Blocked by another modal window.
2379         setEnabled(false);
2380         setFlag(BlockedByModal);
2381         if (hasMouseCapture())
2382             ReleaseCapture();
2383         break;
2384     case QEvent::WindowUnblocked:
2385         setEnabled(true);
2386         clearFlag(BlockedByModal);
2387         break;
2388     default:
2389         break;
2390     }
2391 
2392     return QPlatformWindow::windowEvent(event);
2393 }
2394 
propagateSizeHints()2395 void QWindowsWindow::propagateSizeHints()
2396 {
2397     qCDebug(lcQpaWindows) << __FUNCTION__ << this << window();
2398 }
2399 
handleGeometryChangingMessage(MSG * message,const QWindow * qWindow,const QMargins & margins)2400 bool QWindowsWindow::handleGeometryChangingMessage(MSG *message, const QWindow *qWindow, const QMargins &margins)
2401 {
2402     auto *windowPos = reinterpret_cast<WINDOWPOS *>(message->lParam);
2403     if ((windowPos->flags & SWP_NOZORDER) == 0) {
2404         if (QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(qWindow)) {
2405             QWindow *parentWindow = qWindow->parent();
2406             HWND parentHWND = GetAncestor(windowPos->hwnd, GA_PARENT);
2407             HWND desktopHWND = GetDesktopWindow();
2408             platformWindow->m_data.embedded = !parentWindow && parentHWND && (parentHWND != desktopHWND);
2409         }
2410         if (qWindow->flags().testFlag(Qt::WindowStaysOnBottomHint))
2411             windowPos->hwndInsertAfter = HWND_BOTTOM;
2412     }
2413     if (!qWindow->isTopLevel()) // Implement hasHeightForWidth().
2414         return false;
2415     if ((windowPos->flags & (SWP_NOCOPYBITS | SWP_NOSIZE)))
2416         return false;
2417     const QRect suggestedFrameGeometry(windowPos->x, windowPos->y,
2418                                        windowPos->cx, windowPos->cy);
2419     const QRect suggestedGeometry = suggestedFrameGeometry - margins;
2420     const QRectF correctedGeometryF = QPlatformWindow::closestAcceptableGeometry(qWindow, suggestedGeometry);
2421     if (!correctedGeometryF.isValid())
2422         return false;
2423     const QRect correctedFrameGeometry = correctedGeometryF.toRect() + margins;
2424     if (correctedFrameGeometry == suggestedFrameGeometry)
2425         return false;
2426     windowPos->x = correctedFrameGeometry.left();
2427     windowPos->y = correctedFrameGeometry.top();
2428     windowPos->cx = correctedFrameGeometry.width();
2429     windowPos->cy = correctedFrameGeometry.height();
2430     return true;
2431 }
2432 
handleGeometryChanging(MSG * message) const2433 bool QWindowsWindow::handleGeometryChanging(MSG *message) const
2434 {
2435     const QMargins margins = window()->isTopLevel() ? fullFrameMargins() : QMargins();
2436     return QWindowsWindow::handleGeometryChangingMessage(message, window(), margins);
2437 }
2438 
setFullFrameMargins(const QMargins & newMargins)2439 void QWindowsWindow::setFullFrameMargins(const QMargins &newMargins)
2440 {
2441     if (m_data.fullFrameMargins != newMargins) {
2442         qCDebug(lcQpaWindows) << __FUNCTION__ << window() <<  m_data.fullFrameMargins  << "->" << newMargins;
2443         m_data.fullFrameMargins = newMargins;
2444     }
2445 }
2446 
updateFullFrameMargins()2447 void QWindowsWindow::updateFullFrameMargins()
2448 {
2449     // QTBUG-82580: If a native menu is present, force a WM_NCCALCSIZE.
2450     if (GetMenu(m_data.hwnd))
2451         QWindowsContext::forceNcCalcSize(m_data.hwnd);
2452     else
2453         calculateFullFrameMargins();
2454 }
2455 
calculateFullFrameMargins()2456 void QWindowsWindow::calculateFullFrameMargins()
2457 {
2458     // Normally obtained from WM_NCCALCSIZE. This calculation only works
2459     // when no native menu is present.
2460     const auto systemMargins = testFlag(DisableNonClientScaling)
2461         ? QWindowsGeometryHint::frameOnPrimaryScreen(m_data.hwnd)
2462         : frameMargins_sys();
2463     setFullFrameMargins(systemMargins + m_data.customMargins);
2464 }
2465 
frameMargins() const2466 QMargins QWindowsWindow::frameMargins() const
2467 {
2468     QMargins result = fullFrameMargins();
2469     if (isTopLevel() && m_data.hasFrame)
2470         result -= invisibleMargins(geometry().topLeft());
2471     return result;
2472 }
2473 
fullFrameMargins() const2474 QMargins QWindowsWindow::fullFrameMargins() const
2475 {
2476     return m_data.fullFrameMargins;
2477 }
2478 
setOpacity(qreal level)2479 void QWindowsWindow::setOpacity(qreal level)
2480 {
2481     qCDebug(lcQpaWindows) << __FUNCTION__ << level;
2482     if (!qFuzzyCompare(m_opacity, level)) {
2483         m_opacity = level;
2484         if (m_data.hwnd)
2485             setWindowOpacity(m_data.hwnd, m_data.flags,
2486                              window()->format().hasAlpha(), testFlag(OpenGLSurface) || testFlag(VulkanSurface),
2487                              level);
2488     }
2489 }
2490 
createRectRegion(const QRect & r)2491 static inline HRGN createRectRegion(const QRect &r)
2492 {
2493     return CreateRectRgn(r.left(), r.top(), r.x() + r.width(), r.y() + r.height());
2494 }
2495 
addRectToWinRegion(const QRect & rect,HRGN * winRegion)2496 static inline void addRectToWinRegion(const QRect &rect, HRGN *winRegion)
2497 {
2498     if (const HRGN rectRegion = createRectRegion(rect)) {
2499         HRGN result = CreateRectRgn(0, 0, 0, 0);
2500         if (CombineRgn(result, *winRegion, rectRegion, RGN_OR)) {
2501             DeleteObject(*winRegion);
2502             *winRegion = result;
2503         }
2504         DeleteObject(rectRegion);
2505     }
2506 }
2507 
qRegionToWinRegion(const QRegion & region)2508 static HRGN qRegionToWinRegion(const QRegion &region)
2509 {
2510     auto it = region.begin();
2511     const auto end = region.end();
2512     if (it == end)
2513         return nullptr;
2514     HRGN hRegion = createRectRegion(*it);
2515     while (++it != end)
2516         addRectToWinRegion(*it, &hRegion);
2517     return hRegion;
2518 }
2519 
setMask(const QRegion & region)2520 void QWindowsWindow::setMask(const QRegion &region)
2521 {
2522     if (region.isEmpty()) {
2523          SetWindowRgn(m_data.hwnd, nullptr, true);
2524          return;
2525     }
2526     const HRGN winRegion = qRegionToWinRegion(region);
2527 
2528     // Mask is in client area coordinates, so offset it in case we have a frame
2529     if (window()->isTopLevel()) {
2530         const QMargins margins = fullFrameMargins();
2531         OffsetRgn(winRegion, margins.left(), margins.top());
2532     }
2533 
2534     // SetWindowRgn takes ownership.
2535     if (!SetWindowRgn(m_data.hwnd, winRegion, true))
2536         DeleteObject(winRegion);
2537 }
2538 
requestActivateWindow()2539 void QWindowsWindow::requestActivateWindow()
2540 {
2541     qCDebug(lcQpaWindows) << __FUNCTION__ << this << window();
2542     // 'Active' state handling is based in focus since it needs to work for
2543     // child windows as well.
2544     if (m_data.hwnd) {
2545         const DWORD currentThread = GetCurrentThreadId();
2546         bool attached = false;
2547         DWORD foregroundThread = 0;
2548 
2549         // QTBUG-14062, QTBUG-37435: Windows normally only flashes the taskbar entry
2550         // when activating windows of inactive applications. Attach to the input of the
2551         // currently active window while setting the foreground window to always activate
2552         // the window when desired.
2553         if (QGuiApplication::applicationState() != Qt::ApplicationActive
2554             && QWindowsNativeInterface::windowActivationBehavior() == QWindowsWindowFunctions::AlwaysActivateWindow) {
2555             if (const HWND foregroundWindow = GetForegroundWindow()) {
2556                 foregroundThread = GetWindowThreadProcessId(foregroundWindow, nullptr);
2557                 if (foregroundThread && foregroundThread != currentThread)
2558                     attached = AttachThreadInput(foregroundThread, currentThread, TRUE) == TRUE;
2559                 if (attached) {
2560                     if (!window()->flags().testFlag(Qt::WindowStaysOnBottomHint)
2561                         && !window()->flags().testFlag(Qt::WindowStaysOnTopHint)
2562                         && window()->type() != Qt::ToolTip) {
2563                         const UINT swpFlags = SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER;
2564                         SetWindowPos(m_data.hwnd, HWND_TOPMOST, 0, 0, 0, 0, swpFlags);
2565                         SetWindowPos(m_data.hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, swpFlags);
2566                     }
2567                 }
2568             }
2569         }
2570         SetForegroundWindow(m_data.hwnd);
2571         SetFocus(m_data.hwnd);
2572         if (attached)
2573             AttachThreadInput(foregroundThread, currentThread, FALSE);
2574     }
2575 }
2576 
setKeyboardGrabEnabled(bool grab)2577 bool QWindowsWindow::setKeyboardGrabEnabled(bool grab)
2578 {
2579     if (!m_data.hwnd) {
2580         qWarning("%s: No handle", __FUNCTION__);
2581         return false;
2582     }
2583     qCDebug(lcQpaWindows) << __FUNCTION__ << this << window() << grab;
2584 
2585     QWindowsContext *context = QWindowsContext::instance();
2586     if (grab) {
2587         context->setKeyGrabber(window());
2588     } else {
2589         if (context->keyGrabber() == window())
2590             context->setKeyGrabber(nullptr);
2591     }
2592     return true;
2593 }
2594 
setMouseGrabEnabled(bool grab)2595 bool QWindowsWindow::setMouseGrabEnabled(bool grab)
2596 {
2597     qCDebug(lcQpaWindows) << __FUNCTION__ << window() << grab;
2598     if (!m_data.hwnd) {
2599         qWarning("%s: No handle", __FUNCTION__);
2600         return false;
2601     }
2602     if (!isVisible() && grab) {
2603         qWarning("%s: Not setting mouse grab for invisible window %s/'%s'",
2604                  __FUNCTION__, window()->metaObject()->className(),
2605                  qPrintable(window()->objectName()));
2606         return false;
2607     }
2608     // release grab or an explicit grab overriding autocapture: Clear flag.
2609     clearFlag(QWindowsWindow::AutoMouseCapture);
2610     if (hasMouseCapture() != grab) {
2611         if (grab) {
2612             SetCapture(m_data.hwnd);
2613         } else {
2614             ReleaseCapture();
2615         }
2616     }
2617     return grab;
2618 }
2619 
edgesToWinOrientation(Qt::Edges edges)2620 static inline DWORD edgesToWinOrientation(Qt::Edges edges)
2621 {
2622     if (edges == Qt::LeftEdge)
2623         return 0xf001; // SC_SIZELEFT;
2624     else if (edges == (Qt::RightEdge))
2625         return 0xf002; // SC_SIZERIGHT
2626     else if (edges == (Qt::TopEdge))
2627         return 0xf003; // SC_SIZETOP
2628     else if (edges == (Qt::TopEdge | Qt::LeftEdge))
2629         return 0xf004; // SC_SIZETOPLEFT
2630     else if (edges == (Qt::TopEdge | Qt::RightEdge))
2631         return 0xf005; // SC_SIZETOPRIGHT
2632     else if (edges == (Qt::BottomEdge))
2633         return 0xf006; // SC_SIZEBOTTOM
2634     else if (edges == (Qt::BottomEdge | Qt::LeftEdge))
2635         return 0xf007; // SC_SIZEBOTTOMLEFT
2636     else if (edges == (Qt::BottomEdge | Qt::RightEdge))
2637         return 0xf008; // SC_SIZEBOTTOMRIGHT
2638 
2639     return 0xf000; // SC_SIZE
2640 }
2641 
startSystemResize(Qt::Edges edges)2642 bool QWindowsWindow::startSystemResize(Qt::Edges edges)
2643 {
2644     if (Q_UNLIKELY(window()->flags().testFlag(Qt::MSWindowsFixedSizeDialogHint)))
2645         return false;
2646 
2647     ReleaseCapture();
2648     PostMessage(m_data.hwnd, WM_SYSCOMMAND, edgesToWinOrientation(edges), 0);
2649     setFlag(SizeGripOperation);
2650     return true;
2651 }
2652 
startSystemMove()2653 bool QWindowsWindow::startSystemMove()
2654 {
2655     ReleaseCapture();
2656     PostMessage(m_data.hwnd, WM_SYSCOMMAND, 0xF012 /*SC_DRAGMOVE*/, 0);
2657     return true;
2658 }
2659 
setFrameStrutEventsEnabled(bool enabled)2660 void QWindowsWindow::setFrameStrutEventsEnabled(bool enabled)
2661 {
2662     if (enabled) {
2663         setFlag(FrameStrutEventsEnabled);
2664     } else {
2665         clearFlag(FrameStrutEventsEnabled);
2666     }
2667 }
2668 
getBorderWidth(const QPlatformScreen * screen)2669 static int getBorderWidth(const QPlatformScreen *screen)
2670 {
2671     NONCLIENTMETRICS ncm;
2672     QWindowsContext::nonClientMetricsForScreen(&ncm, screen);
2673     return ncm.iBorderWidth + ncm.iPaddedBorderWidth + 2;
2674 }
2675 
getSizeHints(MINMAXINFO * mmi) const2676 void QWindowsWindow::getSizeHints(MINMAXINFO *mmi) const
2677 {
2678     // We don't apply the min/max size hint as we change the dpi, because we did not adjust the
2679     // QScreen of the window yet so we don't have the min/max with the right ratio
2680     if (!testFlag(QWindowsWindow::WithinDpiChanged))
2681         QWindowsGeometryHint::applyToMinMaxInfo(window(), fullFrameMargins(), mmi);
2682 
2683     // This block fixes QTBUG-8361, QTBUG-4362: Frameless/title-less windows shouldn't cover the
2684     // taskbar when maximized
2685     if ((testFlag(WithinMaximize) || window()->windowStates().testFlag(Qt::WindowMinimized))
2686         && (m_data.flags.testFlag(Qt::FramelessWindowHint)
2687             || (m_data.flags.testFlag(Qt::CustomizeWindowHint) && !m_data.flags.testFlag(Qt::WindowTitleHint)))) {
2688         const QScreen *screen = window()->screen();
2689 
2690         // Documentation of MINMAXINFO states that it will only work for the primary screen
2691         if (screen && screen == QGuiApplication::primaryScreen()) {
2692             const QRect availableGeometry = QHighDpi::toNativePixels(screen->availableGeometry(), screen);
2693             mmi->ptMaxSize.y = availableGeometry.height();
2694 
2695             // Width, because you can have the taskbar on the sides too.
2696             mmi->ptMaxSize.x = availableGeometry.width();
2697 
2698             // If you have the taskbar on top, or on the left you don't want it at (0,0):
2699             mmi->ptMaxPosition.x = availableGeometry.x();
2700             mmi->ptMaxPosition.y = availableGeometry.y();
2701             if (!m_data.flags.testFlag(Qt::FramelessWindowHint)) {
2702                 const int borderWidth = getBorderWidth(screen->handle());
2703                 mmi->ptMaxSize.x += borderWidth * 2;
2704                 mmi->ptMaxSize.y += borderWidth * 2;
2705                 mmi->ptMaxTrackSize = mmi->ptMaxSize;
2706                 mmi->ptMaxPosition.x -= borderWidth;
2707                 mmi->ptMaxPosition.y -= borderWidth;
2708             }
2709         } else if (!screen){
2710             qWarning("window()->screen() returned a null screen");
2711         }
2712     }
2713 
2714     qCDebug(lcQpaWindows) << __FUNCTION__ << window() << *mmi;
2715 }
2716 
handleNonClientHitTest(const QPoint & globalPos,LRESULT * result) const2717 bool QWindowsWindow::handleNonClientHitTest(const QPoint &globalPos, LRESULT *result) const
2718 {
2719     // QTBUG-32663, suppress resize cursor for fixed size windows.
2720     const QWindow *w = window();
2721     if (!w->isTopLevel() // Task 105852, minimized windows need to respond to user input.
2722         || (m_windowState != Qt::WindowNoState)
2723         || !isActive()
2724         || (m_data.flags & Qt::FramelessWindowHint)) {
2725         return false;
2726     }
2727     const QSize minimumSize = w->minimumSize();
2728     if (minimumSize.isEmpty())
2729         return false;
2730     const QSize maximumSize = w->maximumSize();
2731     const bool fixedWidth = minimumSize.width() == maximumSize.width();
2732     const bool fixedHeight = minimumSize.height() == maximumSize.height();
2733     if (!fixedWidth && !fixedHeight)
2734         return false;
2735     const QPoint localPos = w->mapFromGlobal(QHighDpi::fromNativePixels(globalPos, w));
2736     const QSize size = w->size();
2737     if (fixedHeight) {
2738         if (localPos.y() >= size.height()) {
2739             *result = HTBORDER; // Unspecified border, no resize cursor.
2740             return true;
2741         }
2742         if (localPos.y() < 0) {
2743             // We want to return HTCAPTION/true only over the outer sizing frame, not the entire title bar,
2744             // otherwise the title bar buttons (close, etc.) become unresponsive on Windows 7 (QTBUG-78262).
2745             // However, neither frameMargins() nor GetSystemMetrics(SM_CYSIZEFRAME), etc., give the correct
2746             // sizing frame height in all Windows versions/scales. This empirical constant seems to work, though.
2747             const int sizingHeight = 9;
2748             const int topResizeBarPos = sizingHeight - frameMargins().top();
2749             if (localPos.y() < topResizeBarPos) {
2750                 *result = HTCAPTION; // Extend caption over top resize bar, let's user move the window.
2751                 return true;
2752             }
2753         }
2754     }
2755     if (fixedWidth && (localPos.x() < 0 || localPos.x() >= size.width())) {
2756         *result = HTBORDER; // Unspecified border, no resize cursor.
2757         return true;
2758     }
2759     return false;
2760 }
2761 
2762 #ifndef QT_NO_CURSOR
2763 // Return the default cursor (Arrow) from QWindowsCursor's cache.
defaultCursor(const QWindow * w)2764 static inline CursorHandlePtr defaultCursor(const QWindow *w)
2765 {
2766     if (QScreen *screen = w->screen())
2767         if (const QPlatformScreen *platformScreen = screen->handle())
2768             if (QPlatformCursor *cursor = platformScreen->cursor())
2769                 return static_cast<QWindowsCursor *>(cursor)->standardWindowCursor(Qt::ArrowCursor);
2770     return CursorHandlePtr(new CursorHandle(QWindowsCursor::createCursorFromShape(Qt::ArrowCursor)));
2771 }
2772 
2773 // Check whether to apply a new cursor. Either the window in question is
2774 // currently under mouse, or it is the parent of the window under mouse and
2775 // there is no other window with an explicitly set cursor in-between.
applyNewCursor(const QWindow * w)2776 static inline bool applyNewCursor(const QWindow *w)
2777 {
2778     const QWindow *underMouse = QWindowsContext::instance()->windowUnderMouse();
2779     if (underMouse == w)
2780         return true;
2781     for (const QWindow *p = underMouse; p ; p = p->parent()) {
2782         if (p == w)
2783             return true;
2784         const QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(p);
2785         if (platformWindow && !platformWindow->cursor()->isNull())
2786             return false;
2787     }
2788     return false;
2789 }
2790 #endif // !QT_NO_CURSOR
2791 
2792 /*!
2793     \brief Applies to cursor property set on the window to the global cursor.
2794 
2795     \sa QWindowsCursor
2796 */
2797 
applyCursor()2798 void QWindowsWindow::applyCursor()
2799 {
2800     if (QWindowsCursor::hasOverrideCursor()) {
2801         if (isTopLevel())
2802             QWindowsCursor::enforceOverrideCursor();
2803         return;
2804     }
2805 #ifndef QT_NO_CURSOR
2806     if (m_cursor->isNull()) { // Recurse up to parent with non-null cursor. Set default for toplevel.
2807         if (const QWindow *p = window()->parent()) {
2808             if (QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(p))
2809                 platformWindow->applyCursor();
2810         } else {
2811             SetCursor(defaultCursor(window())->handle());
2812         }
2813     } else {
2814         SetCursor(m_cursor->handle());
2815     }
2816 #endif
2817 }
2818 
setCursor(const CursorHandlePtr & c)2819 void QWindowsWindow::setCursor(const CursorHandlePtr &c)
2820 {
2821 #ifndef QT_NO_CURSOR
2822     if (c->handle() != m_cursor->handle()) {
2823         const bool apply = applyNewCursor(window());
2824         qCDebug(lcQpaWindows) << window() << __FUNCTION__
2825             << c->handle() << " doApply=" << apply;
2826         m_cursor = c;
2827         if (apply)
2828             applyCursor();
2829     }
2830 #endif
2831 }
2832 
setAlertState(bool enabled)2833 void QWindowsWindow::setAlertState(bool enabled)
2834 {
2835     if (isAlertState() == enabled)
2836         return;
2837     if (enabled) {
2838         alertWindow(0);
2839         setFlag(AlertState);
2840     } else {
2841         stopAlertWindow();
2842         clearFlag(AlertState);
2843     }
2844 }
2845 
alertWindow(int durationMs)2846 void QWindowsWindow::alertWindow(int durationMs)
2847 {
2848     UINT timeOutMs = GetCaretBlinkTime();
2849     if (!timeOutMs || timeOutMs == INFINITE)
2850         timeOutMs = 250;
2851 
2852     FLASHWINFO info;
2853     info.cbSize = sizeof(info);
2854     info.hwnd = m_data.hwnd;
2855     info.dwFlags = FLASHW_TRAY;
2856     info.dwTimeout = timeOutMs;
2857     info.uCount = durationMs == 0 ? 10 : UINT(durationMs) / timeOutMs;
2858     FlashWindowEx(&info);
2859 }
2860 
stopAlertWindow()2861 void QWindowsWindow::stopAlertWindow()
2862 {
2863     FLASHWINFO info;
2864     info.cbSize = sizeof(info);
2865     info.hwnd = m_data.hwnd;
2866     info.dwFlags = FLASHW_STOP;
2867     info.dwTimeout = 0;
2868     info.uCount = 0;
2869     FlashWindowEx(&info);
2870 }
2871 
isEnabled() const2872 bool QWindowsWindow::isEnabled() const
2873 {
2874     return (style() & WS_DISABLED) == 0;
2875 }
2876 
setEnabled(bool enabled)2877 void QWindowsWindow::setEnabled(bool enabled)
2878 {
2879     const unsigned oldStyle = style();
2880     unsigned newStyle = oldStyle;
2881     if (enabled) {
2882         newStyle &= ~WS_DISABLED;
2883     } else {
2884         newStyle |= WS_DISABLED;
2885     }
2886     if (newStyle != oldStyle)
2887         setStyle(newStyle);
2888 }
2889 
createHIcon(const QIcon & icon,int xSize,int ySize)2890 static HICON createHIcon(const QIcon &icon, int xSize, int ySize)
2891 {
2892     if (!icon.isNull()) {
2893         const QPixmap pm = icon.pixmap(icon.actualSize(QSize(xSize, ySize)));
2894         if (!pm.isNull())
2895             return qt_pixmapToWinHICON(pm);
2896     }
2897     return nullptr;
2898 }
2899 
setWindowIcon(const QIcon & icon)2900 void QWindowsWindow::setWindowIcon(const QIcon &icon)
2901 {
2902     if (m_data.hwnd) {
2903         destroyIcon();
2904 
2905         m_iconSmall = createHIcon(icon, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
2906         m_iconBig = createHIcon(icon, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON));
2907 
2908         if (m_iconBig) {
2909             SendMessage(m_data.hwnd, WM_SETICON, 0 /* ICON_SMALL */, LPARAM(m_iconSmall));
2910             SendMessage(m_data.hwnd, WM_SETICON, 1 /* ICON_BIG */, LPARAM(m_iconBig));
2911         } else {
2912             SendMessage(m_data.hwnd, WM_SETICON, 0 /* ICON_SMALL */, LPARAM(m_iconSmall));
2913             SendMessage(m_data.hwnd, WM_SETICON, 1 /* ICON_BIG */, LPARAM(m_iconSmall));
2914         }
2915     }
2916 }
2917 
isTopLevel() const2918 bool QWindowsWindow::isTopLevel() const
2919 {
2920     return window()->isTopLevel() && !m_data.embedded;
2921 }
2922 
2923 enum : WORD {
2924     DwmwaUseImmersiveDarkMode = 20,
2925     DwmwaUseImmersiveDarkModeBefore20h1 = 19
2926 };
2927 
queryDarkBorder(HWND hwnd)2928 static bool queryDarkBorder(HWND hwnd)
2929 {
2930     BOOL result = FALSE;
2931     const bool ok =
2932         SUCCEEDED(DwmGetWindowAttribute(hwnd, DwmwaUseImmersiveDarkMode, &result, sizeof(result)))
2933         || SUCCEEDED(DwmGetWindowAttribute(hwnd, DwmwaUseImmersiveDarkModeBefore20h1, &result, sizeof(result)));
2934     if (!ok)
2935         qWarning("%s: Unable to retrieve dark window border setting.", __FUNCTION__);
2936     return result == TRUE;
2937 }
2938 
setDarkBorderToWindow(HWND hwnd,bool d)2939 bool QWindowsWindow::setDarkBorderToWindow(HWND hwnd, bool d)
2940 {
2941     const BOOL darkBorder = d ? TRUE : FALSE;
2942     const bool ok =
2943         SUCCEEDED(DwmSetWindowAttribute(hwnd, DwmwaUseImmersiveDarkMode, &darkBorder, sizeof(darkBorder)))
2944         || SUCCEEDED(DwmSetWindowAttribute(hwnd, DwmwaUseImmersiveDarkModeBefore20h1, &darkBorder, sizeof(darkBorder)));
2945     if (!ok)
2946         qWarning("%s: Unable to set dark window border.", __FUNCTION__);
2947     return ok;
2948 }
2949 
setDarkBorder(bool d)2950 void QWindowsWindow::setDarkBorder(bool d)
2951 {
2952     if (shouldApplyDarkFrame(window()) && queryDarkBorder(m_data.hwnd) != d)
2953         setDarkBorderToWindow(m_data.hwnd, d);
2954 }
2955 
menuBar() const2956 QWindowsMenuBar *QWindowsWindow::menuBar() const
2957 {
2958     return m_menuBar.data();
2959 }
2960 
setMenuBar(QWindowsMenuBar * mb)2961 void QWindowsWindow::setMenuBar(QWindowsMenuBar *mb)
2962 {
2963     m_menuBar = mb;
2964 }
2965 
2966 /*!
2967     \brief Sets custom margins to be added to the default margins determined by
2968     the windows style in the handling of the WM_NCCALCSIZE message.
2969 
2970     This is currently used to give the Aero-style QWizard a smaller top margin.
2971     The property can be set using QPlatformNativeInterface::setWindowProperty() or,
2972     before platform window creation, by setting a dynamic property
2973     on the QWindow (see QWindowsIntegration::createPlatformWindow()).
2974 */
2975 
setCustomMargins(const QMargins & newCustomMargins)2976 void QWindowsWindow::setCustomMargins(const QMargins &newCustomMargins)
2977 {
2978     if (newCustomMargins != m_data.customMargins) {
2979         const QMargins oldCustomMargins = m_data.customMargins;
2980         m_data.customMargins = newCustomMargins;
2981          // Re-trigger WM_NCALCSIZE with wParam=1 by passing SWP_FRAMECHANGED
2982         const QRect currentFrameGeometry = frameGeometry_sys();
2983         const QPoint topLeft = currentFrameGeometry.topLeft();
2984         QRect newFrame = currentFrameGeometry.marginsRemoved(oldCustomMargins) + m_data.customMargins;
2985         newFrame.moveTo(topLeft);
2986         qCDebug(lcQpaWindows) << __FUNCTION__ << oldCustomMargins << "->" << newCustomMargins
2987             << currentFrameGeometry << "->" << newFrame;
2988         SetWindowPos(m_data.hwnd, nullptr, newFrame.x(), newFrame.y(), newFrame.width(), newFrame.height(), SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE);
2989     }
2990 }
2991 
surface(void * nativeConfig,int * err)2992 void *QWindowsWindow::surface(void *nativeConfig, int *err)
2993 {
2994 #if QT_CONFIG(vulkan)
2995     Q_UNUSED(nativeConfig);
2996     Q_UNUSED(err);
2997     if (window()->surfaceType() == QSurface::VulkanSurface) {
2998         if (!m_vkSurface) {
2999             QVulkanInstance *inst = window()->vulkanInstance();
3000             if (inst)
3001                 m_vkSurface = static_cast<QWindowsVulkanInstance *>(inst->handle())->createSurface(handle());
3002             else
3003                 qWarning("Attempted to create Vulkan surface without an instance; was QWindow::setVulkanInstance() called?");
3004         }
3005         // Different semantics for VkSurfaces: the return value is the address,
3006         // not the value, given that this is a 64-bit handle even on x86.
3007         return &m_vkSurface;
3008     }
3009 #elif defined(QT_NO_OPENGL)
3010     Q_UNUSED(err)
3011     Q_UNUSED(nativeConfig)
3012     return 0;
3013 #endif
3014 #ifndef QT_NO_OPENGL
3015     if (!m_surface) {
3016         if (QWindowsStaticOpenGLContext *staticOpenGLContext = QWindowsIntegration::staticOpenGLContext())
3017             m_surface = staticOpenGLContext->createWindowSurface(m_data.hwnd, nativeConfig, err);
3018     }
3019 
3020     return m_surface;
3021 #endif
3022 }
3023 
invalidateSurface()3024 void QWindowsWindow::invalidateSurface()
3025 {
3026 #if QT_CONFIG(vulkan)
3027     if (m_vkSurface) {
3028         QVulkanInstance *inst = window()->vulkanInstance();
3029         if (inst)
3030             static_cast<QWindowsVulkanInstance *>(inst->handle())->destroySurface(m_vkSurface);
3031         m_vkSurface = VK_NULL_HANDLE;
3032     }
3033 #endif
3034 #ifndef QT_NO_OPENGL
3035     if (m_surface) {
3036         if (QWindowsStaticOpenGLContext *staticOpenGLContext = QWindowsIntegration::staticOpenGLContext())
3037             staticOpenGLContext->destroyWindowSurface(m_surface);
3038         m_surface = nullptr;
3039     }
3040 #endif // QT_NO_OPENGL
3041 }
3042 
setTouchWindowTouchTypeStatic(QWindow * window,QWindowsWindowFunctions::TouchWindowTouchTypes touchTypes)3043 void QWindowsWindow::setTouchWindowTouchTypeStatic(QWindow *window, QWindowsWindowFunctions::TouchWindowTouchTypes touchTypes)
3044 {
3045     if (!window->handle())
3046         return;
3047     static_cast<QWindowsWindow *>(window->handle())->registerTouchWindow(touchTypes);
3048 }
3049 
registerTouchWindow(QWindowsWindowFunctions::TouchWindowTouchTypes touchTypes)3050 void QWindowsWindow::registerTouchWindow(QWindowsWindowFunctions::TouchWindowTouchTypes touchTypes)
3051 {
3052     if ((QWindowsContext::instance()->systemInfo() & QWindowsContext::SI_SupportsTouch)
3053         && !testFlag(TouchRegistered)) {
3054         ULONG touchFlags = 0;
3055         const bool ret = IsTouchWindow(m_data.hwnd, &touchFlags);
3056         // Return if it is not a touch window or the flags are already set by a hook
3057         // such as HCBT_CREATEWND
3058         if (ret || touchFlags != 0)
3059             return;
3060         if (RegisterTouchWindow(m_data.hwnd, ULONG(touchTypes)))
3061             setFlag(TouchRegistered);
3062         else
3063             qErrnoWarning("RegisterTouchWindow() failed for window '%s'.", qPrintable(window()->objectName()));
3064     }
3065 }
3066 
aboutToMakeCurrent()3067 void QWindowsWindow::aboutToMakeCurrent()
3068 {
3069 #ifndef QT_NO_OPENGL
3070     // For RasterGLSurface windows, that become OpenGL windows dynamically, it might be
3071     // time to set up some GL specifics.  This is particularly important for layered
3072     // windows (WS_EX_LAYERED due to alpha > 0).
3073     const bool isCompositing = qt_window_private(window())->compositing;
3074     if (isCompositing != testFlag(Compositing)) {
3075         if (isCompositing)
3076             setFlag(Compositing);
3077         else
3078             clearFlag(Compositing);
3079 
3080         updateGLWindowSettings(window(), m_data.hwnd, m_data.flags, m_opacity);
3081     }
3082 #endif
3083 }
3084 
setHasBorderInFullScreenStatic(QWindow * window,bool border)3085 void QWindowsWindow::setHasBorderInFullScreenStatic(QWindow *window, bool border)
3086 {
3087     if (QPlatformWindow *handle = window->handle())
3088         static_cast<QWindowsWindow *>(handle)->setHasBorderInFullScreen(border);
3089     else
3090         window->setProperty(hasBorderInFullScreenProperty, QVariant(border));
3091 }
3092 
setHasBorderInFullScreenDefault(bool border)3093 void QWindowsWindow::setHasBorderInFullScreenDefault(bool border)
3094 {
3095     m_borderInFullScreenDefault = border;
3096 }
3097 
setHasBorderInFullScreen(bool border)3098 void QWindowsWindow::setHasBorderInFullScreen(bool border)
3099 {
3100     if (testFlag(HasBorderInFullScreen) == border)
3101         return;
3102     if (border)
3103         setFlag(HasBorderInFullScreen);
3104     else
3105         clearFlag(HasBorderInFullScreen);
3106     // Directly apply the flag in case we are fullscreen.
3107     if (m_windowState == Qt::WindowFullScreen) {
3108         LONG_PTR style = GetWindowLongPtr(handle(), GWL_STYLE);
3109         if (border)
3110             style |= WS_BORDER;
3111         else
3112             style &= ~WS_BORDER;
3113         SetWindowLongPtr(handle(), GWL_STYLE, style);
3114     }
3115 }
3116 
formatWindowTitle(const QString & title)3117 QString QWindowsWindow::formatWindowTitle(const QString &title)
3118 {
3119     return QPlatformWindow::formatWindowTitle(title, QStringLiteral(" - "));
3120 }
3121 
3122 QT_END_NAMESPACE
3123