Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qwindowswindow.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include <QtCore/qt_windows.h>
6#include <QtGui/qstylehints.h>
7
10#include "qwindowstheme.h"
11#if QT_CONFIG(draganddrop)
12# include "qwindowsdrag.h"
13#endif
14#include "qwindowsscreen.h"
16#include "qwindowsmenu.h"
18#if QT_CONFIG(dynamicgl)
19# include "qwindowsglcontext.h"
20#else
22#endif
24#ifdef QT_NO_CURSOR
25# include "qwindowscursor.h"
26#endif
29
30#include <QtGui/qguiapplication.h>
31#include <QtGui/qscreen.h>
32#include <QtGui/qwindow.h>
33#include <QtGui/qregion.h>
34#include <QtGui/qopenglcontext.h>
35#include <QtGui/qpainterpath.h>
36#include <QtGui/private/qwindowsthemecache_p.h>
37#include <private/qwindow_p.h> // QWINDOWSIZE_MAX
38#include <private/qguiapplication_p.h>
39#include <private/qhighdpiscaling_p.h>
40#include <qpa/qwindowsysteminterface.h>
41#include <private/qsystemlibrary_p.h>
42#include <private/qwinregistry_p.h>
43
44
45#include <QtCore/qdebug.h>
46#include <QtCore/qlibraryinfo.h>
47#include <QtCore/qoperatingsystemversion.h>
48
49#include <dwmapi.h>
50
51#if QT_CONFIG(vulkan)
52#include "qwindowsvulkaninstance.h"
53#endif
54
55#include <shellscalingapi.h>
56
57#include <private/qdxgivsyncservice_p.h>
58
59#ifndef GWL_HWNDPARENT
60# define GWL_HWNDPARENT (-8)
61#endif
62
64
65using namespace Qt::StringLiterals;
66
67using QWindowCreationContextPtr = QSharedPointer<QWindowCreationContext>;
68
69enum {
72};
73
74Q_GUI_EXPORT HICON qt_pixmapToWinHICON(const QPixmap &);
75
76static QByteArray debugWinStyle(DWORD style)
77{
78 QByteArray rc = "0x";
79 rc += QByteArray::number(qulonglong(style), 16);
80 if (style & WS_POPUP)
81 rc += " WS_POPUP";
82 if (style & WS_CHILD)
83 rc += " WS_CHILD";
84 if (style & WS_OVERLAPPED)
85 rc += " WS_OVERLAPPED";
86 if (style & WS_CLIPSIBLINGS)
87 rc += " WS_CLIPSIBLINGS";
88 if (style & WS_CLIPCHILDREN)
89 rc += " WS_CLIPCHILDREN";
90 if (style & WS_THICKFRAME)
91 rc += " WS_THICKFRAME";
92 if (style & WS_DLGFRAME)
93 rc += " WS_DLGFRAME";
94 if (style & WS_SYSMENU)
95 rc += " WS_SYSMENU";
96 if (style & WS_MINIMIZEBOX)
97 rc += " WS_MINIMIZEBOX";
98 if (style & WS_MAXIMIZEBOX)
99 rc += " WS_MAXIMIZEBOX";
100 if (style & WS_BORDER)
101 rc += " WS_BORDER";
102 if (style & WS_CAPTION)
103 rc += " WS_CAPTION";
104 if (style & WS_CHILDWINDOW)
105 rc += " WS_CHILDWINDOW";
106 if (style & WS_DISABLED)
107 rc += " WS_DISABLED";
108 if (style & WS_GROUP)
109 rc += " WS_GROUP";
110 if (style & WS_HSCROLL)
111 rc += " WS_HSCROLL";
112 if (style & WS_ICONIC)
113 rc += " WS_ICONIC";
114 if (style & WS_MAXIMIZE)
115 rc += " WS_MAXIMIZE";
116 if (style & WS_MINIMIZE)
117 rc += " WS_MINIMIZE";
118 if (style & WS_SIZEBOX)
119 rc += " WS_SIZEBOX";
120 if (style & WS_TABSTOP)
121 rc += " WS_TABSTOP";
122 if (style & WS_TILED)
123 rc += " WS_TILED";
124 if (style & WS_VISIBLE)
125 rc += " WS_VISIBLE";
126 if (style & WS_VSCROLL)
127 rc += " WS_VSCROLL";
128 return rc;
129}
130
131static QByteArray debugWinExStyle(DWORD exStyle)
132{
133 QByteArray rc = "0x";
134 rc += QByteArray::number(qulonglong(exStyle), 16);
135 if (exStyle & WS_EX_TOOLWINDOW)
136 rc += " WS_EX_TOOLWINDOW";
137 if (exStyle & WS_EX_CONTEXTHELP)
138 rc += " WS_EX_CONTEXTHELP";
139 if (exStyle & WS_EX_LAYERED)
140 rc += " WS_EX_LAYERED";
141 if (exStyle & WS_EX_DLGMODALFRAME)
142 rc += " WS_EX_DLGMODALFRAME";
143 if (exStyle & WS_EX_LAYOUTRTL)
144 rc += " WS_EX_LAYOUTRTL";
145 if (exStyle & WS_EX_NOINHERITLAYOUT)
146 rc += " WS_EX_NOINHERITLAYOUT";
147 if (exStyle & WS_EX_ACCEPTFILES)
148 rc += " WS_EX_ACCEPTFILES";
149 if (exStyle & WS_EX_APPWINDOW)
150 rc += " WS_EX_APPWINDOW";
151 if (exStyle & WS_EX_CLIENTEDGE)
152 rc += " WS_EX_CLIENTEDGE";
153 if (exStyle & WS_EX_COMPOSITED)
154 rc += " WS_EX_COMPOSITED";
155 if (exStyle & WS_EX_CONTROLPARENT)
156 rc += " WS_EX_CONTROLPARENT";
157 if (exStyle & WS_EX_LEFT)
158 rc += " WS_EX_LEFT";
159 if (exStyle & WS_EX_LEFTSCROLLBAR)
160 rc += " WS_EX_LEFTSCROLLBAR";
161 if (exStyle & WS_EX_LTRREADING)
162 rc += " WS_EX_LTRREADING";
163 if (exStyle & WS_EX_MDICHILD)
164 rc += " WS_EX_MDICHILD";
165 if (exStyle & WS_EX_NOACTIVATE)
166 rc += " WS_EX_NOACTIVATE";
167 if (exStyle & WS_EX_NOPARENTNOTIFY)
168 rc += " WS_EX_NOPARENTNOTIFY";
169 if (exStyle & WS_EX_NOREDIRECTIONBITMAP)
170 rc += " WS_EX_NOREDIRECTIONBITMAP";
171 if (exStyle & WS_EX_RIGHT)
172 rc += " WS_EX_RIGHT";
173 if (exStyle & WS_EX_RIGHTSCROLLBAR)
174 rc += " WS_EX_RIGHTSCROLLBAR";
175 if (exStyle & WS_EX_RTLREADING)
176 rc += " WS_EX_RTLREADING";
177 if (exStyle & WS_EX_STATICEDGE)
178 rc += " WS_EX_STATICEDGE";
179 if (exStyle & WS_EX_TOPMOST)
180 rc += " WS_EX_TOPMOST";
181 if (exStyle & WS_EX_TRANSPARENT)
182 rc += " WS_EX_TRANSPARENT";
183 if (exStyle & WS_EX_WINDOWEDGE)
184 rc += " WS_EX_WINDOWEDGE";
185 return rc;
186}
187
188static QByteArray debugWinSwpPos(UINT flags)
189{
190 QByteArray rc = "0x";
191 rc += QByteArray::number(flags, 16);
192 if (flags & SWP_FRAMECHANGED)
193 rc += " SWP_FRAMECHANGED";
194 if (flags & SWP_HIDEWINDOW)
195 rc += " SWP_HIDEWINDOW";
196 if (flags & SWP_NOACTIVATE)
197 rc += " SWP_NOACTIVATE";
198 if (flags & SWP_NOCOPYBITS)
199 rc += " SWP_NOCOPYBITS";
200 if (flags & SWP_NOMOVE)
201 rc += " SWP_NOMOVE";
202 if (flags & SWP_NOOWNERZORDER)
203 rc += " SWP_NOOWNERZORDER";
204 if (flags & SWP_NOREDRAW)
205 rc += " SWP_NOREDRAW";
206 if (flags & SWP_NOSENDCHANGING)
207 rc += " SWP_NOSENDCHANGING";
208 if (flags & SWP_NOSIZE)
209 rc += " SWP_NOSIZE";
210 if (flags & SWP_NOZORDER)
211 rc += " SWP_NOZORDER";
212 if (flags & SWP_SHOWWINDOW)
213 rc += " SWP_SHOWWINDOW";
214 if (flags & SWP_ASYNCWINDOWPOS)
215 rc += " SWP_ASYNCWINDOWPOS";
216 if (flags & SWP_DEFERERASE)
217 rc += " SWP_DEFERERASE";
218 if (flags & SWP_DRAWFRAME)
219 rc += " SWP_DRAWFRAME";
220 if (flags & SWP_NOREPOSITION)
221 rc += " SWP_NOREPOSITION";
222 return rc;
223}
224
225[[nodiscard]] static inline QByteArray debugWindowPlacementFlags(const UINT flags)
226{
227 QByteArray rc = "0x";
228 rc += QByteArray::number(flags, 16);
229 if (flags & WPF_SETMINPOSITION)
230 rc += " WPF_SETMINPOSITION";
231 if (flags & WPF_RESTORETOMAXIMIZED)
232 rc += " WPF_RESTORETOMAXIMIZED";
233 if (flags & WPF_ASYNCWINDOWPLACEMENT)
234 rc += " WPF_ASYNCWINDOWPLACEMENT";
235 return rc;
236}
237
238[[nodiscard]] static inline QByteArray debugShowWindowCmd(const UINT cmd)
239{
240 QByteArray rc = {};
241 rc += QByteArray::number(cmd);
242 if (cmd == SW_HIDE)
243 rc += " SW_HIDE";
244 if (cmd == SW_SHOWNORMAL)
245 rc += " SW_SHOWNORMAL";
246 if (cmd == SW_NORMAL)
247 rc += " SW_NORMAL";
248 if (cmd == SW_SHOWMINIMIZED)
249 rc += " SW_SHOWMINIMIZED";
250 if (cmd == SW_SHOWMAXIMIZED)
251 rc += " SW_SHOWMAXIMIZED";
252 if (cmd == SW_MAXIMIZE)
253 rc += " SW_MAXIMIZE";
254 if (cmd == SW_SHOWNOACTIVATE)
255 rc += " SW_SHOWNOACTIVATE";
256 if (cmd == SW_SHOW)
257 rc += " SW_SHOW";
258 if (cmd == SW_MINIMIZE)
259 rc += " SW_MINIMIZE";
260 if (cmd == SW_SHOWMINNOACTIVE)
261 rc += " SW_SHOWMINNOACTIVE";
262 if (cmd == SW_SHOWNA)
263 rc += " SW_SHOWNA";
264 if (cmd == SW_RESTORE)
265 rc += " SW_RESTORE";
266 if (cmd == SW_SHOWDEFAULT)
267 rc += " SW_SHOWDEFAULT";
268 if (cmd == SW_FORCEMINIMIZE)
269 rc += " SW_FORCEMINIMIZE";
270 return rc;
271}
272
273static inline QSize qSizeOfRect(const RECT &rect)
274{
275 return QSize(rect.right -rect.left, rect.bottom - rect.top);
276}
277
278static inline QRect qrectFromRECT(const RECT &rect)
279{
280 return QRect(QPoint(rect.left, rect.top), qSizeOfRect(rect));
281}
282
283static inline RECT RECTfromQRect(const QRect &rect)
284{
285 const int x = rect.left();
286 const int y = rect.top();
287 RECT result = { x, y, x + rect.width(), y + rect.height() };
288 return result;
289}
290
291#ifndef QT_NO_DEBUG_STREAM
292QDebug operator<<(QDebug d, const RECT &r)
293{
294 QDebugStateSaver saver(d);
295 d.nospace();
296 d << "RECT(left=" << r.left << ", top=" << r.top
297 << ", right=" << r.right << ", bottom=" << r.bottom
298 << " (" << r.right - r.left << 'x' << r.bottom - r.top << "))";
299 return d;
300}
301
302QDebug operator<<(QDebug d, const POINT &p)
303{
304 QDebugStateSaver saver(d);
305 d.nospace();
306 d << "POINT(x=" << p.x << ", y=" << p.y << ')';
307 return d;
308}
309
310QDebug operator<<(QDebug d, const WINDOWPOS &wp)
311{
312 QDebugStateSaver saver(d);
313 d.nospace();
314 d.noquote();
315 d << "WINDOWPOS(flags=" << debugWinSwpPos(wp.flags) << ", hwnd="
316 << wp.hwnd << ", hwndInsertAfter=" << wp.hwndInsertAfter << ", x=" << wp.x
317 << ", y=" << wp.y << ", cx=" << wp.cx << ", cy=" << wp.cy << ')';
318 return d;
319}
320
321QDebug operator<<(QDebug d, const NCCALCSIZE_PARAMS &p)
322{
323 QDebugStateSaver saver(d);
324 d.nospace();
325 d << "NCCALCSIZE_PARAMS(rgrc=[" << p.rgrc[0] << ", " << p.rgrc[1] << ", "
326 << p.rgrc[2] << "], lppos=" << *p.lppos << ')';
327 return d;
328}
329
330QDebug operator<<(QDebug d, const MINMAXINFO &i)
331{
332 QDebugStateSaver saver(d);
333 d.nospace();
334 d << "MINMAXINFO(maxSize=" << i.ptMaxSize << ", "
335 << "maxpos=" << i.ptMaxPosition << ", "
336 << "maxtrack=" << i.ptMaxTrackSize << ", "
337 << "mintrack=" << i.ptMinTrackSize << ')';
338 return d;
339}
340
341QDebug operator<<(QDebug d, const WINDOWPLACEMENT &wp)
342{
343 QDebugStateSaver saver(d);
344 d.nospace();
345 d.noquote();
346 d << "WINDOWPLACEMENT(flags=" << debugWindowPlacementFlags(wp.flags) << ", showCmd="
347 << debugShowWindowCmd(wp.showCmd) << ", ptMinPosition=" << wp.ptMinPosition
348 << ", ptMaxPosition=" << wp.ptMaxPosition << ", rcNormalPosition="
349 << wp.rcNormalPosition << ')';
350 return d;
351}
352
353QDebug operator<<(QDebug d, const GUID &guid)
354{
355 QDebugStateSaver saver(d);
356 d.nospace();
357 d << '{' << Qt::hex << Qt::uppercasedigits << qSetPadChar(u'0')
358 << qSetFieldWidth(8) << guid.Data1
359 << qSetFieldWidth(0) << '-' << qSetFieldWidth(4)
360 << guid.Data2 << qSetFieldWidth(0) << '-' << qSetFieldWidth(4)
361 << guid.Data3 << qSetFieldWidth(0) << '-' << qSetFieldWidth(4)
362 << qSetFieldWidth(2) << guid.Data4[0] << guid.Data4[1]
363 << qSetFieldWidth(0) << '-' << qSetFieldWidth(2);
364 for (int i = 2; i < 8; ++i)
365 d << guid.Data4[i];
366 d << qSetFieldWidth(0) << '}';
367 return d;
368}
369#endif // !QT_NO_DEBUG_STREAM
370
371static void formatBriefRectangle(QDebug &d, const QRect &r)
372{
373 d << r.width() << 'x' << r.height() << Qt::forcesign << r.x() << r.y() << Qt::noforcesign;
374}
375
376static void formatBriefMargins(QDebug &d, const QMargins &m)
377{
378 d << m.left() << ", " << m.top() << ", " << m.right() << ", " << m.bottom();
379}
380
381// QTBUG-43872, for windows that do not have WS_EX_TOOLWINDOW set, WINDOWPLACEMENT
382// is in workspace/available area coordinates.
383static QPoint windowPlacementOffset(HWND hwnd, const QPoint &point)
384{
385 if (GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_TOOLWINDOW)
386 return QPoint(0, 0);
388 const QWindowsScreen *screen = screenManager.screens().size() == 1
389 ? screenManager.screens().constFirst() : screenManager.screenAtDp(point);
390 if (screen)
391 return screen->availableGeometry().topLeft() - screen->geometry().topLeft();
392 return QPoint(0, 0);
393}
394
395// Return the frame geometry relative to the parent
396// if there is one.
397static inline QRect frameGeometry(HWND hwnd, bool topLevel)
398{
399 RECT rect = { 0, 0, 0, 0 };
400 if (topLevel) {
401 WINDOWPLACEMENT windowPlacement;
402 windowPlacement.length = sizeof(WINDOWPLACEMENT);
403 GetWindowPlacement(hwnd, &windowPlacement);
404 if (windowPlacement.showCmd == SW_SHOWMINIMIZED) {
405 const QRect result = qrectFromRECT(windowPlacement.rcNormalPosition);
406 return result.translated(windowPlacementOffset(hwnd, result.topLeft()));
407 }
408 }
409 GetWindowRect(hwnd, &rect); // Screen coordinates.
410 const HWND parent = GetParent(hwnd);
411 if (parent && !topLevel) {
412 const int width = rect.right - rect.left;
413 const int height = rect.bottom - rect.top;
414 POINT leftTop = { rect.left, rect.top };
415 screenToClient(parent, &leftTop);
416 rect.left = leftTop.x;
417 rect.top = leftTop.y;
418 rect.right = leftTop.x + width;
419 rect.bottom = leftTop.y + height;
420 }
421 return qrectFromRECT(rect);
422}
423
424// Return the visibility of the Window (except full screen since it is not a window state).
426{
427 if (!IsWindowVisible(hwnd))
428 return QWindow::Hidden;
429 WINDOWPLACEMENT windowPlacement;
430 windowPlacement.length = sizeof(WINDOWPLACEMENT);
431 if (GetWindowPlacement(hwnd, &windowPlacement)) {
432 switch (windowPlacement.showCmd) {
433 case SW_SHOWMINIMIZED:
434 case SW_MINIMIZE:
435 case SW_FORCEMINIMIZE:
436 return QWindow::Minimized;
437 case SW_SHOWMAXIMIZED:
438 return QWindow::Maximized;
439 default:
440 break;
441 }
442 }
443 return QWindow::Windowed;
444}
445
446static inline bool windowIsAccelerated(const QWindow *w)
447{
448 switch (w->surfaceType()) {
449 case QSurface::OpenGLSurface:
450 case QSurface::VulkanSurface:
451 case QSurface::Direct3DSurface:
452 return true;
453 default:
454 return false;
455 }
456}
457
458static bool applyBlurBehindWindow(HWND hwnd)
459{
460 DWM_BLURBEHIND blurBehind = {0, 0, nullptr, 0};
461
462 blurBehind.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
463 blurBehind.fEnable = TRUE;
464 blurBehind.hRgnBlur = CreateRectRgn(0, 0, -1, -1);
465
466 const bool result = DwmEnableBlurBehindWindow(hwnd, &blurBehind) == S_OK;
467
468 if (blurBehind.hRgnBlur)
469 DeleteObject(blurBehind.hRgnBlur);
470
471 return result;
472}
473
474// from qwidget_win.cpp, pass flags separately in case they have been "autofixed".
475static bool shouldShowMaximizeButton(const QWindow *w, Qt::WindowFlags flags)
476{
477 if ((flags & Qt::MSWindowsFixedSizeDialogHint) || !(flags & Qt::WindowMaximizeButtonHint))
478 return false;
479 // if the user explicitly asked for the maximize button, we try to add
480 // it even if the window has fixed size.
481 return (flags & Qt::CustomizeWindowHint) ||
482 w->maximumSize() == QSize(QWINDOWSIZE_MAX, QWINDOWSIZE_MAX);
483}
484
485bool QWindowsWindow::hasNoNativeFrame(HWND hwnd, Qt::WindowFlags flags)
486{
487 const LONG_PTR style = GetWindowLongPtr(hwnd, GWL_STYLE);
488 return (style & WS_CHILD) || (flags & Qt::FramelessWindowHint);
489}
490
491// Set the WS_EX_LAYERED flag on a HWND if required. This is required for
492// translucent backgrounds, not fully opaque windows and for
493// Qt::WindowTransparentForInput (in combination with WS_EX_TRANSPARENT).
494bool QWindowsWindow::setWindowLayered(HWND hwnd, Qt::WindowFlags flags, bool hasAlpha, qreal opacity)
495{
496 const LONG_PTR exStyle = GetWindowLongPtr(hwnd, GWL_EXSTYLE);
497 // Native children are frameless by nature, so check for that as well.
498 const bool needsLayered = (flags & Qt::WindowTransparentForInput)
499 || (hasAlpha && hasNoNativeFrame(hwnd, flags)) || opacity < 1.0;
500 const bool isLayered = (exStyle & WS_EX_LAYERED);
501 if (needsLayered != isLayered) {
502 if (needsLayered) {
503 SetWindowLongPtr(hwnd, GWL_EXSTYLE, exStyle | WS_EX_LAYERED);
504 } else {
505 SetWindowLongPtr(hwnd, GWL_EXSTYLE, exStyle & ~WS_EX_LAYERED);
506 }
507 }
508 return needsLayered;
509}
510
511static void setWindowOpacity(HWND hwnd, Qt::WindowFlags flags, bool hasAlpha, bool accelerated, qreal level)
512{
513 if (QWindowsWindow::setWindowLayered(hwnd, flags, hasAlpha, level)) {
514 const BYTE alpha = BYTE(qRound(255.0 * level));
515 if (hasAlpha && !accelerated && QWindowsWindow::hasNoNativeFrame(hwnd, flags)) {
516 // Non-GL windows with alpha: Use blend function to update.
517 BLENDFUNCTION blend = {AC_SRC_OVER, 0, alpha, AC_SRC_ALPHA};
518 UpdateLayeredWindow(hwnd, nullptr, nullptr, nullptr, nullptr, nullptr, 0, &blend, ULW_ALPHA);
519 } else {
520 SetLayeredWindowAttributes(hwnd, 0, alpha, LWA_ALPHA);
521 }
522 } else if (IsWindowVisible(hwnd)) { // Repaint when switching from layered.
523 InvalidateRect(hwnd, nullptr, TRUE);
524 }
525}
526
527[[nodiscard]] static inline int getResizeBorderThickness(const UINT dpi)
528{
529 // The width of the padded border will always be 0 if DWM composition is
530 // disabled, but since it will always be enabled and can't be programtically
531 // disabled from Windows 8, we are safe to go.
532 return GetSystemMetricsForDpi(SM_CXSIZEFRAME, dpi)
533 + GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi);
534}
535
536/*!
537 Calculates the dimensions of the invisible borders within the
538 window frames which only exist on Windows 10 and onwards.
539*/
540
542{
543 POINT pt = {screenPoint.x(), screenPoint.y()};
544 if (HMONITOR hMonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONULL)) {
545 UINT dpiX;
546 UINT dpiY;
547 if (SUCCEEDED(GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY))) {
548 const int gap = getResizeBorderThickness(dpiX);
549 return QMargins(gap, 0, gap, gap);
550 }
551 }
552 return QMargins();
553}
554
555[[nodiscard]] static inline QMargins invisibleMargins(const HWND hwnd)
556{
557 const UINT dpi = GetDpiForWindow(hwnd);
558 const int gap = getResizeBorderThickness(dpi);
559 return QMargins(gap, 0, gap, gap);
560}
561
562/*!
563 \class WindowCreationData
564 \brief Window creation code.
565
566 This struct gathers all information required to create a window.
567 Window creation is split in 3 steps:
568
569 \list
570 \li fromWindow() Gather all required information
571 \li create() Create the system handle.
572 \li initialize() Post creation initialization steps.
573 \endlist
574
575 The reason for this split is to also enable changing the QWindowFlags
576 by calling:
577
578 \list
579 \li fromWindow() Gather information and determine new system styles
580 \li applyWindowFlags() to apply the new window system styles.
581 \li initialize() Post creation initialization steps.
582 \endlist
583
584 Contains the window creation code formerly in qwidget_win.cpp.
585
586 \sa QWindowCreationContext
587 \internal
588*/
589
591{
593 enum Flags { ForceChild = 0x1, ForceTopLevel = 0x2 };
594
595 void fromWindow(const QWindow *w, const Qt::WindowFlags flags, unsigned creationFlags = 0);
596 inline WindowData create(const QWindow *w, const WindowData &data, QString title) const;
597 inline void applyWindowFlags(HWND hwnd) const;
598 void initialize(const QWindow *w, HWND h, bool frameChange, qreal opacityLevel) const;
599
603 unsigned style = 0;
604 unsigned exStyle = 0;
605 bool topLevel = false;
606 bool popup = false;
607 bool dialog = false;
608 bool tool = false;
609 bool embedded = false;
610 bool hasAlpha = false;
611};
612
613QDebug operator<<(QDebug debug, const WindowCreationData &d)
614{
615 QDebugStateSaver saver(debug);
616 debug.nospace();
617 debug.noquote();
618 debug << "WindowCreationData: " << d.flags
619 << "\n topLevel=" << d.topLevel;
620 if (d.parentHandle)
621 debug << " parent=" << d.parentHandle;
622 debug << " popup=" << d.popup << " dialog=" << d.dialog
623 << " embedded=" << d.embedded << " tool=" << d.tool
624 << "\n style=" << debugWinStyle(d.style);
625 if (d.exStyle)
626 debug << "\n exStyle=" << debugWinExStyle(d.exStyle);
627 return debug;
628}
629
630// Fix top level window flags in case only the type flags are passed.
631static inline void fixTopLevelWindowFlags(Qt::WindowFlags &flags)
632{
633 // Not supported on Windows, also do correction when it is set.
634 flags &= ~Qt::WindowFullscreenButtonHint;
635 switch (flags) {
636 case Qt::Window:
637 flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowMinimizeButtonHint
638 |Qt::WindowMaximizeButtonHint|Qt::WindowCloseButtonHint;
639 break;
640 case Qt::Dialog:
641 case Qt::Tool:
642 flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint;
643 break;
644 default:
645 break;
646 }
647 if ((flags & Qt::WindowType_Mask) == Qt::SplashScreen)
648 flags |= Qt::FramelessWindowHint;
649}
650
651static QScreen *screenForDeviceName(const QWindow *w, const QString &name)
652{
653 const auto getDeviceName = [](const QScreen *screen) -> QString {
654 if (const auto s = static_cast<const QWindowsScreen *>(screen->handle()))
655 return s->data().deviceName;
656 return {};
657 };
658 QScreen *winScreen = w ? w->screen() : QGuiApplication::primaryScreen();
659 if (winScreen && getDeviceName(winScreen) != name) {
660 const auto screens = winScreen->virtualSiblings();
661 for (QScreen *screen : screens) {
662 if (getDeviceName(screen) == name)
663 return screen;
664 }
665 }
666 return winScreen;
667}
668
669static QPoint calcPosition(const QWindow *w, const QWindowCreationContextPtr &context, const QMargins &invMargins)
670{
671 const QPoint orgPos(context->frameX - invMargins.left(), context->frameY - invMargins.top());
672
673 if (!w || w->type() != Qt::Window)
674 return orgPos;
675
676 // Workaround for QTBUG-50371
677 const QScreen *screenForGL = QWindowsWindow::forcedScreenForGLWindow(w);
678 if (!screenForGL)
679 return orgPos;
680
681 const QPoint posFrame(context->frameX, context->frameY);
682 const QMargins margins = context->margins;
683 const QRect scrGeo = screenForGL->handle()->availableGeometry();
684
685 // Point is already in the required screen.
686 if (scrGeo.contains(orgPos))
687 return orgPos;
688
689 // If the visible part of the window is already in the
690 // required screen, just ignore the invisible offset.
691 if (scrGeo.contains(posFrame))
692 return posFrame;
693
694 // Find the original screen containing the coordinates.
695 const auto screens = screenForGL->virtualSiblings();
696 const QScreen *orgScreen = nullptr;
697 for (QScreen *screen : screens) {
698 if (screen->handle()->availableGeometry().contains(posFrame)) {
699 orgScreen = screen;
700 break;
701 }
702 }
703 const QPoint ctPos = QPoint(qMax(scrGeo.left(), scrGeo.center().x()
704 + (margins.right() - margins.left() - context->frameWidth)/2),
705 qMax(scrGeo.top(), scrGeo.center().y()
706 + (margins.bottom() - margins.top() - context->frameHeight)/2));
707
708 // If initial coordinates were outside all screens, center the window on the required screen.
709 if (!orgScreen)
710 return ctPos;
711
712 const QRect orgGeo = orgScreen->handle()->availableGeometry();
713 const QRect orgFrame(QPoint(context->frameX, context->frameY),
714 QSize(context->frameWidth, context->frameHeight));
715
716 // Window would be centered on orgScreen. Center it on the required screen.
717 if (orgGeo.center() == (orgFrame - margins).center())
718 return ctPos;
719
720 // Transform the coordinates to map them into the required screen.
721 const QPoint newPos(scrGeo.left() + ((posFrame.x() - orgGeo.left()) * scrGeo.width()) / orgGeo.width(),
722 scrGeo.top() + ((posFrame.y() - orgGeo.top()) * scrGeo.height()) / orgGeo.height());
723 const QPoint newPosNoMargin(newPos.x() - invMargins.left(), newPos.y() - invMargins.top());
724
725 return scrGeo.contains(newPosNoMargin) ? newPosNoMargin : newPos;
726}
727
728void WindowCreationData::fromWindow(const QWindow *w, const Qt::WindowFlags flagsIn,
729 unsigned creationFlags)
730{
731 flags = flagsIn;
732
733 // Sometimes QWindow doesn't have a QWindow parent but does have a native parent window,
734 // e.g. in case of embedded ActiveQt servers. They should not be considered a top-level
735 // windows in such cases.
736 QVariant prop = w->property(QWindowsWindow::embeddedNativeParentHandleProperty);
737 if (prop.isValid()) {
738 embedded = true;
739 parentHandle = reinterpret_cast<HWND>(prop.value<WId>());
740 }
741
742 if (creationFlags & ForceChild) {
743 topLevel = false;
744 } else if (embedded) {
745 // Embedded native windows (for example Active X server windows) are by
746 // definition never toplevel, even though they do not have QWindow parents.
747 topLevel = false;
748 } else {
749 topLevel = (creationFlags & ForceTopLevel) ? true : w->isTopLevel();
750 }
751
752 if (topLevel)
753 fixTopLevelWindowFlags(flags);
754
755 type = static_cast<Qt::WindowType>(int(flags) & Qt::WindowType_Mask);
756 switch (type) {
757 case Qt::Dialog:
758 case Qt::Sheet:
759 dialog = true;
760 break;
761 case Qt::Drawer:
762 case Qt::Tool:
763 tool = true;
764 break;
765 case Qt::Popup:
766 popup = true;
767 break;
768 default:
769 break;
770 }
771 if ((flags & Qt::MSWindowsFixedSizeDialogHint))
772 dialog = true;
773
774 // This causes the title bar to drawn RTL and the close button
775 // to be left. Note that this causes:
776 // - All DCs created on the Window to have RTL layout (see SetLayout)
777 // - ClientToScreen() and ScreenToClient() to work in reverse as well.
778 // - Mouse event coordinates to be mirrored.
779 // - Positioning of child Windows.
780 if (QGuiApplication::layoutDirection() == Qt::RightToLeft
781 && (QWindowsIntegration::instance()->options() & QWindowsIntegration::RtlEnabled) != 0) {
782 exStyle |= WS_EX_LAYOUTRTL | WS_EX_NOINHERITLAYOUT;
783 }
784
785 // Parent: Use transient parent for top levels.
786 if (popup) {
787 flags |= Qt::WindowStaysOnTopHint; // a popup stays on top, no parent.
788 } else if (!embedded) {
789 if (const QWindow *parentWindow = topLevel ? w->transientParent() : w->parent())
790 parentHandle = QWindowsWindow::handleOf(parentWindow);
791 }
792
793 if (popup || (type == Qt::ToolTip) || (type == Qt::SplashScreen)) {
794 style = WS_POPUP;
795 } else if (topLevel) {
796 if (flags & Qt::FramelessWindowHint)
797 style = WS_POPUP; // no border
798 else if (flags & Qt::WindowTitleHint)
799 style = WS_OVERLAPPED;
800 else
801 style = 0;
802 } else {
803 style = WS_CHILD;
804 }
805
806 style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN ;
807
808 if (flags & Qt::WindowDoesNotAcceptFocus)
809 exStyle |= WS_EX_NOACTIVATE;
810
811 if (topLevel) {
812 if ((type == Qt::Window || dialog || tool)) {
813 if (!(flags & Qt::FramelessWindowHint)) {
814 style |= WS_POPUP;
815 if (flags & Qt::MSWindowsFixedSizeDialogHint) {
816 style |= WS_DLGFRAME;
817 } else {
818 style |= WS_THICKFRAME;
819 }
820 if (flags & Qt::WindowTitleHint)
821 style |= WS_CAPTION; // Contains WS_DLGFRAME
822 }
823 if (flags & Qt::WindowSystemMenuHint)
824 style |= WS_SYSMENU;
825 else if (dialog && (flags & Qt::WindowCloseButtonHint) && !(flags & Qt::FramelessWindowHint)) {
826 style |= WS_SYSMENU | WS_BORDER; // QTBUG-2027, dialogs without system menu.
827 exStyle |= WS_EX_DLGMODALFRAME;
828 }
829 const bool showMinimizeButton = flags & Qt::WindowMinimizeButtonHint;
830 if (showMinimizeButton)
831 style |= WS_MINIMIZEBOX;
832 const bool showMaximizeButton = shouldShowMaximizeButton(w, flags);
833 if (showMaximizeButton)
834 style |= WS_MAXIMIZEBOX;
835 if (showMinimizeButton || showMaximizeButton)
836 style |= WS_SYSMENU;
837 if (tool)
838 exStyle |= WS_EX_TOOLWINDOW;
839 if ((flags & Qt::WindowContextHelpButtonHint) && !showMinimizeButton
840 && !showMaximizeButton)
841 exStyle |= WS_EX_CONTEXTHELP;
842 } else {
843 exStyle |= WS_EX_TOOLWINDOW;
844 }
845
846 // make mouse events fall through this window
847 // NOTE: WS_EX_TRANSPARENT flag can make mouse inputs fall through a layered window
848 if (flagsIn & Qt::WindowTransparentForInput)
849 exStyle |= WS_EX_TRANSPARENT;
850
851 // Currently only compatible with D3D surfaces, use it with care.
852 if (qEnvironmentVariableIntValue("QT_QPA_DISABLE_REDIRECTION_SURFACE"))
853 exStyle |= WS_EX_NOREDIRECTIONBITMAP;
854 }
855}
856
857static inline bool shouldApplyDarkFrame(const QWindow *w)
858{
859 if (!w->isTopLevel() || w->flags().testFlag(Qt::FramelessWindowHint))
860 return false;
861
862 // the user of the application has explicitly opted out of dark frames
863 if (!QWindowsIntegration::instance()->darkModeHandling().testFlag(QWindowsApplication::DarkModeWindowFrames))
864 return false;
865
866 // if the application supports a dark border, and the palette is dark (window background color
867 // is darker than the text), then turn dark-border support on, otherwise use a light border.
868 auto *dWindow = QWindowPrivate::get(const_cast<QWindow*>(w));
869 const QPalette windowPal = dWindow->windowPalette();
870 return windowPal.color(QPalette::WindowText).lightness()
871 > windowPal.color(QPalette::Window).lightness();
872}
873
874static inline int getTitleBarHeight_sys(const UINT dpi)
875{
876 // According to MS design manual, it should be 32px when DPI is 96.
877 return getResizeBorderThickness(dpi) +
878 ::GetSystemMetricsForDpi(SM_CYCAPTION, dpi);
879}
880
882 WindowCreationData::create(const QWindow *w, const WindowData &data, QString title) const
883{
884 WindowData result;
885 result.flags = flags;
886
887 const auto appinst = reinterpret_cast<HINSTANCE>(GetModuleHandle(nullptr));
888
889 const QString windowClassName = QWindowsWindowClassRegistry::instance()->registerWindowClass(w);
890
891 QWindowsWindowClassDescription windowTitlebarDescription;
892 windowTitlebarDescription.name = "_q_titlebar"_L1;
893 windowTitlebarDescription.style = CS_VREDRAW | CS_HREDRAW;
894 windowTitlebarDescription.shouldAddPrefix = false;
895 const QString windowTitlebarName = QWindowsWindowClassRegistry::instance()->registerWindowClass(windowTitlebarDescription);
896
897 const QScreen *screen{};
898 const QRect rect = QPlatformWindow::initialGeometry(w, data.geometry,
899 defaultWindowWidth, defaultWindowHeight,
900 &screen);
901
902 if (title.isEmpty() && (result.flags & Qt::WindowTitleHint))
903 title = topLevel ? qAppName() : w->objectName();
904
905 const auto *titleUtf16 = reinterpret_cast<const wchar_t *>(title.utf16());
906 const auto *classNameUtf16 = reinterpret_cast<const wchar_t *>(windowClassName.utf16());
907 const auto *classTitleBarNameUtf16 = reinterpret_cast<const wchar_t *>(windowTitlebarName.utf16());
908
909 // Capture events before CreateWindowEx() returns. The context is cleared in
910 // the QWindowsWindow constructor.
911 const QWindowCreationContextPtr context(new QWindowCreationContext(w, screen, data.geometry,
912 rect, data.customMargins,
913 style, exStyle));
914 QWindowsContext::instance()->setWindowCreationContext(context);
915
916 const bool hasFrame = (style & (WS_DLGFRAME | WS_THICKFRAME))
917 && !(result.flags & Qt::FramelessWindowHint);
918 QMargins invMargins = topLevel && hasFrame && QWindowsGeometryHint::positionIncludesFrame(w)
919 ? invisibleMargins(QPoint(context->frameX, context->frameY)) : QMargins();
920
921 qCDebug(lcQpaWindow).nospace()
922 << "CreateWindowEx: " << w << " class=" << windowClassName << " title=" << title
923 << '\n' << *this << "\nrequested: " << rect << ": "
924 << context->frameWidth << 'x' << context->frameHeight
925 << '+' << context->frameX << '+' << context->frameY
926 << " custom margins: " << context->customMargins
927 << " invisible margins: " << invMargins;
928
929
930 QPoint pos = calcPosition(w, context, invMargins);
931
932 // Mirror the position when creating on a parent in RTL mode, ditto for the obtained geometry.
933 int mirrorParentWidth = 0;
934 if (!w->isTopLevel() && QWindowsBaseWindow::isRtlLayout(parentHandle)) {
935 RECT rect;
936 GetClientRect(parentHandle, &rect);
937 mirrorParentWidth = rect.right;
938 }
939 if (mirrorParentWidth != 0 && pos.x() != CW_USEDEFAULT && context->frameWidth != CW_USEDEFAULT)
940 pos.setX(mirrorParentWidth - context->frameWidth - pos.x());
941
942 result.hwnd = CreateWindowEx(exStyle, classNameUtf16, titleUtf16,
943 style,
944 pos.x(), pos.y(),
945 context->frameWidth, context->frameHeight,
946 parentHandle, nullptr, appinst, nullptr);
947
948 const UINT dpi = ::GetDpiForWindow(result.hwnd);
949 const int titleBarHeight = getTitleBarHeight_sys(dpi);
950 result.hwndTitlebar = CreateWindowEx(WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_NOACTIVATE,
951 classTitleBarNameUtf16, classTitleBarNameUtf16,
952 0, 0, 0,
953 context->frameWidth, titleBarHeight,
954 nullptr, nullptr, appinst, nullptr);
955
956 qCDebug(lcQpaWindow).nospace()
957 << "CreateWindowEx: returns " << w << ' ' << result.hwnd << " obtained geometry: "
958 << context->obtainedPos << context->obtainedSize << ' ' << context->margins;
959
960 if (!result.hwnd) {
961 qErrnoWarning("%s: CreateWindowEx failed", __FUNCTION__);
962 return result;
963 }
964
965 if (QWindowsTheme::instance()->colorScheme() == Qt::ColorScheme::Dark && shouldApplyDarkFrame(w))
966 QWindowsWindow::setDarkBorderToWindow(result.hwnd, true);
967
968 if (mirrorParentWidth != 0) {
969 context->obtainedPos.setX(mirrorParentWidth - context->obtainedSize.width()
970 - context->obtainedPos.x());
971 }
972
973 QRect obtainedGeometry(context->obtainedPos, context->obtainedSize);
974
975 result.geometry = obtainedGeometry;
976 result.restoreGeometry = frameGeometry(result.hwnd, topLevel);
977 result.fullFrameMargins = context->margins;
978 result.embedded = embedded;
979 result.hasFrame = hasFrame;
980 result.customMargins = context->customMargins;
981
982 return result;
983}
984
986{
987 // Keep enabled and visible from the current style.
988 const LONG_PTR oldStyle = GetWindowLongPtr(hwnd, GWL_STYLE);
989 const LONG_PTR oldExStyle = GetWindowLongPtr(hwnd, GWL_EXSTYLE);
990
991 const LONG_PTR newStyle = style | (oldStyle & (WS_DISABLED|WS_VISIBLE));
992 if (oldStyle != newStyle)
993 SetWindowLongPtr(hwnd, GWL_STYLE, newStyle);
994 const LONG_PTR newExStyle = exStyle;
995 if (newExStyle != oldExStyle)
996 SetWindowLongPtr(hwnd, GWL_EXSTYLE, newExStyle);
997 qCDebug(lcQpaWindow).nospace() << __FUNCTION__ << hwnd << *this
998 << "\n Style from " << debugWinStyle(DWORD(oldStyle)) << "\n to "
999 << debugWinStyle(DWORD(newStyle)) << "\n ExStyle from "
1000 << debugWinExStyle(DWORD(oldExStyle)) << " to "
1001 << debugWinExStyle(DWORD(newExStyle));
1002}
1003
1004void WindowCreationData::initialize(const QWindow *w, HWND hwnd, bool frameChange, qreal opacityLevel) const
1005{
1006 if (!hwnd)
1007 return;
1008 UINT swpFlags = SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER;
1009 if (frameChange)
1010 swpFlags |= SWP_FRAMECHANGED;
1011 if (topLevel) {
1012 swpFlags |= SWP_NOACTIVATE;
1013 if ((flags & Qt::WindowStaysOnTopHint) || (type == Qt::ToolTip)) {
1014 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, swpFlags);
1015 if (flags & Qt::WindowStaysOnBottomHint)
1016 qWarning("QWidget: Incompatible window flags: the window can't be on top and on bottom at the same time");
1017 } else if (flags & Qt::WindowStaysOnBottomHint) {
1018 SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0, swpFlags);
1019 } else if (frameChange) { // Force WM_NCCALCSIZE with wParam=1 in case of custom margins.
1020 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, swpFlags);
1021 }
1022 if (flags & (Qt::CustomizeWindowHint|Qt::WindowTitleHint)) {
1023 HMENU systemMenu = GetSystemMenu(hwnd, FALSE);
1024 if (flags & Qt::WindowCloseButtonHint)
1025 EnableMenuItem(systemMenu, SC_CLOSE, MF_BYCOMMAND|MF_ENABLED);
1026 else
1027 EnableMenuItem(systemMenu, SC_CLOSE, MF_BYCOMMAND|MF_GRAYED);
1028 }
1029 if (flags & Qt::ExpandedClientAreaHint) { // Gives us the rounded corners looks and the frame shadow
1030 MARGINS margins = { -1, -1, -1, -1 };
1031 DwmExtendFrameIntoClientArea(hwnd, &margins);
1032 } else {
1033 MARGINS margins = { 0, 0, 0, 0 };
1034 DwmExtendFrameIntoClientArea(hwnd, &margins);
1035 }
1036 } else { // child.
1037 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, swpFlags);
1038 }
1039
1040 const bool isAccelerated = windowIsAccelerated(w);
1041 const bool hasAlpha = w->format().hasAlpha();
1042 if (isAccelerated && hasAlpha)
1043 applyBlurBehindWindow(hwnd);
1044 setWindowOpacity(hwnd, flags, hasAlpha, isAccelerated, opacityLevel);
1045}
1046
1047
1048// Scaling helpers for size constraints.
1049static QSize toNativeSizeConstrained(QSize dip, const QScreen *s)
1050{
1051 if (QHighDpiScaling::isActive()) {
1052 const qreal factor = QHighDpiScaling::factor(s);
1053 if (!qFuzzyCompare(factor, qreal(1))) {
1054 if (dip.width() > 0 && dip.width() < QWINDOWSIZE_MAX)
1055 dip.setWidth(qRound(qreal(dip.width()) * factor));
1056 if (dip.height() > 0 && dip.height() < QWINDOWSIZE_MAX)
1057 dip.setHeight(qRound(qreal(dip.height()) * factor));
1058 }
1059 }
1060 return dip;
1061}
1062
1063// Helper for checking if frame adjustment needs to be skipped
1064// NOTE: Unmaximized frameless windows will skip margins calculation
1065static bool shouldOmitFrameAdjustment(const Qt::WindowFlags flags, DWORD style)
1066{
1067 return flags.testFlag(Qt::FramelessWindowHint) && !(style & WS_MAXIMIZE);
1068}
1069
1070// Helper for checking if frame adjustment needs to be skipped
1071// NOTE: Unmaximized frameless windows will skip margins calculation
1072static bool shouldOmitFrameAdjustment(const Qt::WindowFlags flags, HWND hwnd)
1073{
1074 DWORD style = hwnd != nullptr ? DWORD(GetWindowLongPtr(hwnd, GWL_STYLE)) : 0;
1075 return flags.testFlag(Qt::FramelessWindowHint) && !(style & WS_MAXIMIZE);
1076}
1077
1078/*!
1079 \class QWindowsGeometryHint
1080 \brief Stores geometry constraints and provides utility functions.
1081
1082 Geometry constraints ready to apply to a MINMAXINFO taking frame
1083 into account.
1084
1085 \internal
1086*/
1087
1088QMargins QWindowsGeometryHint::frameOnPrimaryScreen(const QWindow *w, DWORD style, DWORD exStyle)
1089{
1090 if (!w->isTopLevel() || shouldOmitFrameAdjustment(w->flags(), style))
1091 return {};
1092 RECT rect = {0,0,0,0};
1093 style &= ~DWORD(WS_OVERLAPPED); // Not permitted, see docs.
1094 if (AdjustWindowRectEx(&rect, style, FALSE, exStyle) == FALSE)
1095 qErrnoWarning("%s: AdjustWindowRectEx failed", __FUNCTION__);
1096 const QMargins result(qAbs(rect.left), qAbs(rect.top),
1097 qAbs(rect.right), qAbs(rect.bottom));
1098 qCDebug(lcQpaWindow).nospace() << __FUNCTION__ << " style="
1099 << Qt::showbase << Qt::hex << style << " exStyle=" << exStyle << Qt::dec << Qt::noshowbase
1100 << ' ' << rect << ' ' << result;
1101 return result;
1102}
1103
1104QMargins QWindowsGeometryHint::frameOnPrimaryScreen(const QWindow *w, HWND hwnd)
1105{
1106 return frameOnPrimaryScreen(w, DWORD(GetWindowLongPtr(hwnd, GWL_STYLE)),
1107 DWORD(GetWindowLongPtr(hwnd, GWL_EXSTYLE)));
1108}
1109
1110QMargins QWindowsGeometryHint::frame(const QWindow *w, DWORD style, DWORD exStyle, qreal dpi)
1111{
1112 if (!w->isTopLevel() || shouldOmitFrameAdjustment(w->flags(), style))
1113 return {};
1114 RECT rect = {0,0,0,0};
1115 style &= ~DWORD(WS_OVERLAPPED); // Not permitted, see docs.
1116 if (AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle, unsigned(qRound(dpi))) == FALSE) {
1117 qErrnoWarning("%s: AdjustWindowRectExForDpi failed", __FUNCTION__);
1118 }
1119 const QMargins result(qAbs(rect.left), qAbs(rect.top),
1120 qAbs(rect.right), qAbs(rect.bottom));
1121 qCDebug(lcQpaWindow).nospace() << __FUNCTION__ << " style="
1122 << Qt::showbase << Qt::hex << style << " exStyle=" << exStyle << Qt::dec << Qt::noshowbase
1123 << " dpi=" << dpi
1124 << ' ' << rect << ' ' << result;
1125 return result;
1126}
1127
1128QMargins QWindowsGeometryHint::frame(const QWindow *w, HWND hwnd, DWORD style, DWORD exStyle)
1129{
1130 if (!w->isTopLevel() || shouldOmitFrameAdjustment(w->flags(), style))
1131 return {};
1132 if (QWindowsScreenManager::isSingleScreen())
1133 return frameOnPrimaryScreen(w, style, exStyle);
1135 auto screen = screenManager.screenForHwnd(hwnd);
1136 if (!screen)
1137 screen = screenManager.screens().value(0);
1138 const auto dpi = screen ? screen->logicalDpi().first : qreal(96);
1139 return frame(w, style, exStyle, dpi);
1140}
1141
1142QMargins QWindowsGeometryHint::frame(const QWindow *w, HWND hwnd)
1143{
1144 return frame(w, hwnd, DWORD(GetWindowLongPtr(hwnd, GWL_STYLE)),
1145 DWORD(GetWindowLongPtr(hwnd, GWL_EXSTYLE)));
1146}
1147
1148// For newly created windows.
1149QMargins QWindowsGeometryHint::frame(const QWindow *w, const QRect &geometry,
1150 DWORD style, DWORD exStyle)
1151{
1152 if (!w->isTopLevel() || shouldOmitFrameAdjustment(w->flags(), style))
1153 return {};
1155 || !QWindowsContext::shouldHaveNonClientDpiScaling(w)) {
1156 return frameOnPrimaryScreen(w, style, exStyle);
1157 }
1158 qreal dpi = 96;
1160 auto screen = screenManager.screenAtDp(geometry.center());
1161 if (!screen)
1162 screen = screenManager.screens().value(0);
1163 if (screen)
1164 dpi = screen->logicalDpi().first;
1165 return QWindowsGeometryHint::frame(w, style, exStyle, dpi);
1166}
1167
1168bool QWindowsGeometryHint::handleCalculateSize(const QWindow *window, const QMargins &customMargins, const MSG &msg, LRESULT *result)
1169{
1170 // Prevent adding any border for frameless window
1171 if (msg.wParam && window->flags() & Qt::FramelessWindowHint) {
1172 *result = 0;
1173 return true;
1174 }
1175 const QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(window);
1176 // In case the platformwindow was not yet created, use the initial windowflags provided by the user.
1177 const bool clientAreaExpanded = platformWindow != nullptr ? platformWindow->isClientAreaExpanded() : window->flags() & Qt::ExpandedClientAreaHint;
1178 // Return 0 to remove the window's border
1179 if (msg.wParam && clientAreaExpanded) {
1180 // Prevent content from being cutoff by border for maximized, but not fullscreened windows.
1181 const bool maximized = IsZoomed(msg.hwnd) && window->visibility() != QWindow::FullScreen;
1182 auto *ncp = reinterpret_cast<NCCALCSIZE_PARAMS *>(msg.lParam);
1183 RECT *clientArea = &ncp->rgrc[0];
1184 const int border = getResizeBorderThickness(96);
1185 if (maximized)
1186 clientArea->top += border;
1187 clientArea->bottom -= border;
1188 clientArea->left += border;
1189 clientArea->right -= border;
1190 *result = 0;
1191 return true;
1192 }
1193 // NCCALCSIZE_PARAMS structure if wParam==TRUE
1194 if (!msg.wParam || customMargins.isNull())
1195 return false;
1196 *result = DefWindowProc(msg.hwnd, msg.message, msg.wParam, msg.lParam);
1197 auto *ncp = reinterpret_cast<NCCALCSIZE_PARAMS *>(msg.lParam);
1198 const RECT oldClientArea = ncp->rgrc[0];
1199 ncp->rgrc[0].left += customMargins.left();
1200 ncp->rgrc[0].top += customMargins.top();
1201 ncp->rgrc[0].right -= customMargins.right();
1202 ncp->rgrc[0].bottom -= customMargins.bottom();
1203 result = nullptr;
1204 qCDebug(lcQpaWindow).nospace() << __FUNCTION__ << oldClientArea << '+' << customMargins << "-->"
1205 << ncp->rgrc[0] << ' ' << ncp->rgrc[1] << ' ' << ncp->rgrc[2]
1206 << ' ' << ncp->lppos->cx << ',' << ncp->lppos->cy;
1207 return true;
1208}
1209
1210void QWindowsGeometryHint::frameSizeConstraints(const QWindow *w, const QScreen *screen,
1211 const QMargins &margins,
1212 QSize *minimumSize, QSize *maximumSize)
1213{
1214 *minimumSize = toNativeSizeConstrained(w->minimumSize(), screen);
1215 *maximumSize = toNativeSizeConstrained(w->maximumSize(), screen);
1216
1217 const int maximumWidth = qMax(maximumSize->width(), minimumSize->width());
1218 const int maximumHeight = qMax(maximumSize->height(), minimumSize->height());
1219 const int frameWidth = margins.left() + margins.right();
1220 const int frameHeight = margins.top() + margins.bottom();
1221
1222 if (minimumSize->width() > 0)
1223 minimumSize->rwidth() += frameWidth;
1224 if (minimumSize->height() > 0)
1225 minimumSize->rheight() += frameHeight;
1226 if (maximumWidth < QWINDOWSIZE_MAX)
1227 maximumSize->setWidth(maximumWidth + frameWidth);
1228 if (maximumHeight < QWINDOWSIZE_MAX)
1229 maximumSize->setHeight(maximumHeight + frameHeight);
1230}
1231
1232void QWindowsGeometryHint::applyToMinMaxInfo(const QWindow *w,
1233 const QScreen *screen,
1234 const QMargins &margins,
1235 MINMAXINFO *mmi)
1236{
1237 QSize minimumSize;
1238 QSize maximumSize;
1239 frameSizeConstraints(w, screen, margins, &minimumSize, &maximumSize);
1240 qCDebug(lcQpaWindow).nospace() << '>' << __FUNCTION__ << '<' << " min="
1241 << minimumSize.width() << ',' << minimumSize.height()
1242 << " max=" << maximumSize.width() << ',' << maximumSize.height()
1243 << " margins=" << margins
1244 << " in " << *mmi;
1245
1246 if (minimumSize.width() > 0)
1247 mmi->ptMinTrackSize.x = minimumSize.width();
1248 if (minimumSize.height() > 0)
1249 mmi->ptMinTrackSize.y = minimumSize.height();
1250
1251 if (maximumSize.width() < QWINDOWSIZE_MAX)
1252 mmi->ptMaxTrackSize.x = maximumSize.width();
1253 if (maximumSize.height() < QWINDOWSIZE_MAX)
1254 mmi->ptMaxTrackSize.y = maximumSize.height();
1255 qCDebug(lcQpaWindow).nospace() << '<' << __FUNCTION__ << " out " << *mmi;
1256}
1257
1258void QWindowsGeometryHint::applyToMinMaxInfo(const QWindow *w,
1259 const QMargins &margins,
1260 MINMAXINFO *mmi)
1261{
1262 applyToMinMaxInfo(w, w->screen(), margins, mmi);
1263}
1264
1265bool QWindowsGeometryHint::positionIncludesFrame(const QWindow *w)
1266{
1267 return qt_window_private(const_cast<QWindow *>(w))->positionPolicy
1268 == QWindowPrivate::WindowFrameInclusive;
1269}
1270
1271/*!
1272 \class QWindowsBaseWindow
1273 \brief Base class for QWindowsForeignWindow, QWindowsWindow
1274
1275 The class provides some _sys() getters for querying window
1276 data from a HWND and some _sys() setters.
1277
1278 Derived classes wrapping foreign windows may use them directly
1279 to calculate geometry, margins, etc.
1280
1281 Derived classes representing windows created by Qt may defer
1282 expensive calculations until change notifications are received.
1283
1284 \since 5.6
1285 \internal
1286*/
1287
1288bool QWindowsBaseWindow::isRtlLayout(HWND hwnd)
1289{
1290 return (GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYOUTRTL) != 0;
1291}
1292
1293QWindowsBaseWindow *QWindowsBaseWindow::baseWindowOf(const QWindow *w)
1294{
1295 if (w) {
1296 if (QPlatformWindow *pw = w->handle())
1297 return static_cast<QWindowsBaseWindow *>(pw);
1298 }
1299 return nullptr;
1300}
1301
1302HWND QWindowsBaseWindow::handleOf(const QWindow *w)
1303{
1304 const QWindowsBaseWindow *bw = QWindowsBaseWindow::baseWindowOf(w);
1305 return bw ? bw->handle() : HWND(nullptr);
1306}
1307
1309{
1310 const HWND parent = parentHwnd();
1311 return !parent || parent == GetDesktopWindow();
1312}
1313
1315{
1316 return frameGeometry(handle(), isTopLevel());
1317}
1318
1320{
1321 return frameGeometry_sys().marginsRemoved(fullFrameMargins());
1322}
1323
1325{
1326 return QWindowsGeometryHint::frame(window(), handle(), style(), exStyle());
1327}
1328
1331{
1332 ULONG touchFlags = 0;
1333 if (IsTouchWindow(handle(), &touchFlags) == FALSE)
1334 return {};
1335 TouchWindowTouchTypes result;
1336 if ((touchFlags & TWF_FINETOUCH) != 0)
1337 result.setFlag(TouchWindowTouchType::FineTouch);
1338 if ((touchFlags & TWF_WANTPALM) != 0)
1339 result.setFlag(TouchWindowTouchType::WantPalmTouch);
1340 return result;
1341}
1342
1343void QWindowsBaseWindow::hide_sys() // Normal hide, do not activate other windows.
1344{
1345 SetWindowPos(handle(), nullptr , 0, 0, 0, 0,
1346 SWP_HIDEWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
1347}
1348
1350{
1351 qCDebug(lcQpaWindow) << __FUNCTION__ << this << window();
1352 const Qt::WindowType type = window()->type();
1353 if (type == Qt::Popup
1354 || type == Qt::SubWindow // Special case for QTBUG-63121: MDI subwindows with WindowStaysOnTopHint
1355 || !(window()->flags() & Qt::WindowStaysOnBottomHint)) {
1356 SetWindowPos(handle(), HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
1357 }
1358}
1359
1361{
1362 qCDebug(lcQpaWindow) << __FUNCTION__ << this << window();
1363 if (!(window()->flags() & Qt::WindowStaysOnTopHint))
1364 SetWindowPos(handle(), HWND_BOTTOM, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
1365}
1366
1367void QWindowsBaseWindow::setWindowTitle_sys(const QString &title)
1368{
1369 qCDebug(lcQpaWindow) << __FUNCTION__ << this << window() << title;
1370 SetWindowText(handle(), reinterpret_cast<const wchar_t *>(title.utf16()));
1371}
1372
1373QPoint QWindowsBaseWindow::mapToGlobal(const QPoint &pos) const
1374{
1375 return QWindowsGeometryHint::mapToGlobal(handle(), pos);
1376}
1377
1378QPoint QWindowsBaseWindow::mapFromGlobal(const QPoint &pos) const
1379{
1380 return QWindowsGeometryHint::mapFromGlobal(handle(), pos);
1381}
1382
1384{
1385 Q_UNIMPLEMENTED();
1386}
1387
1389{
1390 Q_UNIMPLEMENTED();
1391 return false;
1392}
1393
1395{
1396 return {};
1397}
1398
1400{
1401 Q_UNIMPLEMENTED();
1402}
1403
1405{
1406 switch (event->type()) {
1407 case QEvent::ChildWindowAdded:
1408 if (!(GetWindowLongPtr(handle(), GWL_STYLE) & WS_CLIPCHILDREN)) {
1409 auto *childWindowEvent = static_cast<QChildWindowEvent*>(event);
1410 qWarning() << childWindowEvent->child() << "added as child to"
1411 << window() << "which does not have WS_CLIPCHILDREN set."
1412 << "This will result in drawing artifacts!";
1413 }
1414 break;
1415 default:
1416 break;
1417 }
1418
1419 return QPlatformWindow::windowEvent(event);
1420}
1421
1422/*!
1423 \class QWindowsDesktopWindow
1424 \brief Window wrapping GetDesktopWindow not allowing any manipulation.
1425 \since 5.6
1426 \internal
1427*/
1428
1429/*!
1430 \class QWindowsForeignWindow
1431 \brief Window wrapping a foreign native window.
1432
1433 QWindowsForeignWindow stores a native HWND and implements getters for
1434 geometry, margins, etc. reparenting and geometry manipulation for use as a
1435 child window in Qt.
1436
1437 \since 5.6
1438 \internal
1439*/
1440
1441QWindowsForeignWindow::QWindowsForeignWindow(QWindow *window, HWND hwnd)
1442 : QWindowsBaseWindow(window)
1443 , m_hwnd(hwnd)
1444 , m_topLevelStyle(0)
1445{
1446 if (QPlatformWindow::parent())
1447 setParent(QPlatformWindow::parent());
1448}
1449
1451{
1452 if (QPlatformWindow::parent())
1453 setParent(nullptr);
1454}
1455
1456void QWindowsForeignWindow::setParent(const QPlatformWindow *newParentWindow)
1457{
1458 const bool wasTopLevel = isTopLevel_sys();
1459 const HWND newParent = newParentWindow ? reinterpret_cast<HWND>(newParentWindow->winId()) : HWND(nullptr);
1460 const bool isTopLevel = !newParent;
1461 const DWORD oldStyle = style();
1462
1463 qCDebug(lcQpaWindow) << __FUNCTION__ << window() << "newParent="
1464 << newParentWindow << newParent << "oldStyle=" << debugWinStyle(oldStyle);
1465
1466 auto updateWindowFlags = [&] {
1467 // Top level window flags need to be set/cleared manually.
1468 DWORD newStyle = oldStyle;
1469 if (isTopLevel) {
1470 newStyle = m_topLevelStyle;
1471 } else {
1472 m_topLevelStyle = oldStyle;
1473 newStyle &= ~(WS_OVERLAPPEDWINDOW | WS_POPUPWINDOW);
1474 newStyle |= WS_CHILD;
1475 }
1476 SetWindowLongPtr(m_hwnd, GWL_STYLE, newStyle);
1477 };
1478
1479 if (wasTopLevel && !isTopLevel) {
1480 // Becoming a child window requires the style
1481 // flags to be updated before reparenting.
1482 updateWindowFlags();
1483 }
1484
1485 SetParent(m_hwnd, newParent);
1486
1487 if (!wasTopLevel && isTopLevel) {
1488 // Becoming a top level window requires the style
1489 // flags to be updated after reparenting.
1490 updateWindowFlags();
1491 }
1492}
1493
1495{
1496 qCDebug(lcQpaWindow) << __FUNCTION__ << window() << visible;
1497 if (visible)
1498 ShowWindow(handle(), SW_SHOWNOACTIVATE);
1499 else
1500 hide_sys();
1501}
1502
1503/*!
1504 \class QWindowCreationContext
1505 \brief Active Context for creating windows.
1506
1507 There is a phase in window creation (WindowCreationData::create())
1508 in which events are sent before the system API CreateWindowEx() returns
1509 the handle. These cannot be handled by the platform window as the association
1510 of the unknown handle value to the window does not exist yet and as not
1511 to trigger recursive handle creation, etc.
1512
1513 In that phase, an instance of QWindowCreationContext is set on
1514 QWindowsContext.
1515
1516 QWindowCreationContext stores the information to answer the initial
1517 WM_GETMINMAXINFO and obtains the corrected size/position.
1518
1519 \sa WindowCreationData, QWindowsContext
1520 \internal
1521*/
1522
1523QWindowCreationContext::QWindowCreationContext(const QWindow *w, const QScreen *s,
1524 const QRect &geometryIn, const QRect &geometry,
1525 const QMargins &cm,
1526 DWORD style, DWORD exStyle) :
1527 window(w),
1528 screen(s),
1529 requestedGeometryIn(geometryIn),
1530 requestedGeometry(geometry),
1531 obtainedPos(geometryIn.topLeft()),
1532 obtainedSize(geometryIn.size()),
1533 margins(QWindowsGeometryHint::frame(w, geometry, style, exStyle))
1534{
1535 // Geometry of toplevels does not consider window frames.
1536 // TODO: No concept of WA_wasMoved yet that would indicate a
1537 // CW_USEDEFAULT unless set. For now, assume that 0,0 means 'default'
1538 // for toplevels.
1539
1540 if (!(w->flags() & Qt::FramelessWindowHint))
1541 customMargins = cm;
1542
1543 if (geometry.isValid()
1544 || !qt_window_private(const_cast<QWindow *>(w))->resizeAutomatic) {
1545 frameX = geometry.x();
1546 frameY = geometry.y();
1547 const QMargins effectiveMargins = margins + customMargins;
1548 frameWidth = effectiveMargins.left() + geometry.width() + effectiveMargins.right();
1549 frameHeight = effectiveMargins.top() + geometry.height() + effectiveMargins.bottom();
1550 if (QWindowsMenuBar::menuBarOf(w) != nullptr) {
1551 menuHeight = GetSystemMetrics(SM_CYMENU);
1552 frameHeight += menuHeight;
1553 }
1554 const bool isDefaultPosition = !frameX && !frameY && w->isTopLevel();
1555 if (!QWindowsGeometryHint::positionIncludesFrame(w) && !isDefaultPosition) {
1556 frameX -= effectiveMargins.left();
1557 frameY -= effectiveMargins.top();
1558 }
1559 }
1560
1561 qCDebug(lcQpaWindow).nospace()
1562 << __FUNCTION__ << ' ' << w << ' ' << geometry
1563 << " pos incl. frame=" << QWindowsGeometryHint::positionIncludesFrame(w)
1564 << " frame=" << frameWidth << 'x' << frameHeight << '+'
1565 << frameX << '+' << frameY
1566 << " margins=" << margins << " custom margins=" << customMargins;
1567}
1568
1569void QWindowCreationContext::applyToMinMaxInfo(MINMAXINFO *mmi) const
1570{
1571 QWindowsGeometryHint::applyToMinMaxInfo(window, screen, margins + customMargins, mmi);
1572}
1573
1574/*!
1575 \class QWindowsWindow
1576 \brief Raster or OpenGL Window.
1577
1578 \list
1579 \li Raster type: handleWmPaint() is implemented to
1580 to bitblt the image. The DC can be accessed
1581 via getDC/releaseDC, which has special handling
1582 when within a paint event (in that case, the DC obtained
1583 from BeginPaint() is returned).
1584
1585 \li Open GL: The first time QWindowsGLContext accesses
1586 the handle, it sets up the pixelformat on the DC
1587 which in turn sets it on the window (see flag
1588 PixelFormatInitialized).
1589 handleWmPaint() is empty (although required).
1590 \endlist
1591
1592 \internal
1593*/
1594
1595const char *QWindowsWindow::embeddedNativeParentHandleProperty = "_q_embedded_native_parent_handle";
1596const char *QWindowsWindow::hasBorderInFullScreenProperty = "_q_has_border_in_fullscreen";
1597bool QWindowsWindow::m_borderInFullScreenDefault = false;
1598bool QWindowsWindow::m_inSetgeometry = false;
1599
1600QWindowsWindow::QWindowsWindow(QWindow *aWindow, const QWindowsWindowData &data) :
1601 QWindowsBaseWindow(aWindow),
1602 m_data(data),
1603 m_cursor(new CursorHandle)
1604#if QT_CONFIG(vulkan)
1605 , m_vkSurface(VK_NULL_HANDLE)
1606#endif
1607{
1608 QWindowsContext::instance()->addWindow(m_data.hwnd, this);
1609
1610 if (aWindow->flags().testFlags(Qt::ExpandedClientAreaHint)) {
1611 SetParent(m_data.hwndTitlebar, m_data.hwnd);
1612 ShowWindow(m_data.hwndTitlebar, SW_SHOW);
1613 }
1614
1615 if (aWindow->surfaceType() == QWindow::Direct3DSurface)
1617#if QT_CONFIG(opengl)
1618 if (aWindow->surfaceType() == QWindow::OpenGLSurface)
1619 setFlag(OpenGLSurface);
1620#endif
1621#if QT_CONFIG(vulkan)
1622 if (aWindow->surfaceType() == QSurface::VulkanSurface)
1623 setFlag(VulkanSurface);
1624#endif
1625 updateDropSite(window()->isTopLevel());
1626
1627 // Register touch unless if the flags are already set by a hook
1628 // such as HCBT_CREATEWND
1629 if (!touchWindowTouchTypes_sys().has_value())
1631
1632 const qreal opacity = qt_window_private(aWindow)->opacity;
1633 if (!qFuzzyCompare(opacity, qreal(1.0)))
1634 setOpacity(opacity);
1635
1636 setMask(QHighDpi::toNativeLocalRegion(window()->mask(), window()));
1637
1638 if (aWindow->isTopLevel())
1639 setWindowIcon(aWindow->icon());
1640 if (m_borderInFullScreenDefault || aWindow->property(hasBorderInFullScreenProperty).toBool())
1643}
1644
1646{
1647 if (m_vsyncServiceCallbackId != 0)
1648 QDxgiVSyncService::instance()->unregisterCallback(m_vsyncServiceCallbackId);
1650 QWindowsThemeCache::clearThemeCache(m_data.hwnd);
1652 UnregisterTouchWindow(m_data.hwnd);
1653 destroyWindow();
1654 destroyIcon();
1655}
1656
1658{
1659 // Clear the creation context as the window can be found in QWindowsContext's map.
1660 QWindowCreationContextPtr creationContext =
1661 QWindowsContext::instance()->setWindowCreationContext(QWindowCreationContextPtr());
1662
1663 QWindow *w = window();
1664 setWindowState(w->windowStates());
1665
1666 // Trigger geometry change (unless it has a special state in which case setWindowState()
1667 // will send the message) and screen change signals of QWindow.
1668 const Qt::WindowState state = w->windowState();
1669 const QRect obtainedGeometry(creationContext->obtainedPos, creationContext->obtainedSize);
1670 QPlatformScreen *obtainedScreen = screenForGeometry(obtainedGeometry);
1671 if (obtainedScreen && screen() != obtainedScreen)
1672 QWindowSystemInterface::handleWindowScreenChanged<QWindowSystemInterface::SynchronousDelivery>(w, obtainedScreen->screen());
1673 if (state != Qt::WindowMaximized && state != Qt::WindowFullScreen
1674 && creationContext->requestedGeometryIn != obtainedGeometry) {
1675 QWindowSystemInterface::handleGeometryChange<QWindowSystemInterface::SynchronousDelivery>(w, obtainedGeometry);
1676 }
1677 QWindowsWindow::setSavedDpi(GetDpiForWindow(handle()));
1678}
1679
1681{
1682 return window()->requestedFormat();
1683}
1684
1685void QWindowsWindow::fireExpose(const QRegion &region, bool force)
1686{
1687 if (region.isEmpty() && !force)
1689 else
1691 QWindowSystemInterface::handleExposeEvent(window(), region);
1692}
1693
1694void QWindowsWindow::fireFullExpose(bool force)
1695{
1696 fireExpose(QRect(QPoint(0, 0), m_data.geometry.size()), force);
1697}
1698
1699void QWindowsWindow::destroyWindow()
1700{
1701 qCDebug(lcQpaWindow) << __FUNCTION__ << this << window() << m_data.hwnd;
1702 if (m_data.hwnd) { // Stop event dispatching before Window is destroyed.
1704 // Clear any transient child relationships as Windows will otherwise destroy them (QTBUG-35499, QTBUG-36666)
1705 const auto tlw = QGuiApplication::topLevelWindows();
1706 for (QWindow *w : tlw) {
1707 if (w->transientParent() == window()) {
1708 if (QWindowsWindow *tw = QWindowsWindow::windowsWindowOf(w))
1709 tw->updateTransientParent();
1710 }
1711 }
1713 if (context->windowUnderMouse() == window())
1717 setDropSiteEnabled(false);
1718#if QT_CONFIG(vulkan)
1719 if (m_vkSurface) {
1720 QVulkanInstance *inst = window()->vulkanInstance();
1721 if (inst)
1722 static_cast<QWindowsVulkanInstance *>(inst->handle())->destroySurface(m_vkSurface);
1723 m_vkSurface = VK_NULL_HANDLE;
1724 }
1725#endif
1726#ifndef QT_NO_OPENGL
1727 if (m_surface) {
1729 staticOpenGLContext->destroyWindowSurface(m_surface);
1730 m_surface = nullptr;
1731 }
1732#endif
1733 DestroyWindow(m_data.hwndTitlebar);
1734 DestroyWindow(m_data.hwnd);
1735 context->removeWindow(m_data.hwnd);
1736 m_data.hwndTitlebar = nullptr;
1737 m_data.hwnd = nullptr;
1738 }
1739}
1740
1741void QWindowsWindow::updateDropSite(bool topLevel)
1742{
1743 bool enabled = false;
1744 bool parentIsEmbedded = false;
1745
1746 if (!topLevel) {
1747 // if the parent window is a foreign window wrapped via QWindow::fromWinId, we need to enable the drop site
1748 // on the first child window
1749 const QWindow *parent = window()->parent();
1750 if (parent && parent->handle() && parent->handle()->isForeignWindow())
1751 parentIsEmbedded = true;
1752 }
1753
1754 if (topLevel || parentIsEmbedded) {
1755 switch (window()->type()) {
1756 case Qt::Window:
1757 case Qt::Dialog:
1758 case Qt::Sheet:
1759 case Qt::Drawer:
1760 case Qt::Popup:
1761 case Qt::Tool:
1762 enabled = true;
1763 break;
1764 default:
1765 break;
1766 }
1767 }
1768 setDropSiteEnabled(enabled);
1769}
1770
1771void QWindowsWindow::setDropSiteEnabled(bool dropEnabled)
1772{
1773 if (isDropSiteEnabled() == dropEnabled)
1774 return;
1775 qCDebug(lcQpaMime) << __FUNCTION__ << window() << dropEnabled;
1776#if QT_CONFIG(clipboard) && QT_CONFIG(draganddrop)
1777 if (dropEnabled) {
1778 Q_ASSERT(m_data.hwnd);
1779 m_dropTarget = new QWindowsOleDropTarget(window());
1780 RegisterDragDrop(m_data.hwnd, m_dropTarget);
1781 CoLockObjectExternal(m_dropTarget, true, true);
1782 } else {
1783 CoLockObjectExternal(m_dropTarget, false, true);
1784 m_dropTarget->Release();
1785 RevokeDragDrop(m_data.hwnd);
1786 m_dropTarget = nullptr;
1787 }
1788#endif // QT_CONFIG(clipboard) && QT_CONFIG(draganddrop)
1789}
1790
1791bool QWindowsWindow::m_screenForGLInitialized = false;
1792
1794{
1795 m_screenForGLInitialized = false;
1796}
1797
1799{
1800 m_screenForGLInitialized = false;
1801}
1802
1803QScreen *QWindowsWindow::forcedScreenForGLWindow(const QWindow *w)
1804{
1805 static QString forceToScreen;
1806 if (!m_screenForGLInitialized) {
1807 forceToScreen = GpuDescription::detect().gpuSuitableScreen;
1808 m_screenForGLInitialized = true;
1809 }
1810 return forceToScreen.isEmpty() ? nullptr : screenForDeviceName(w, forceToScreen);
1811}
1812
1813// Returns topmost QWindowsWindow ancestor even if there are embedded windows in the chain.
1814// Returns this window if it is the topmost ancestor.
1815QWindow *QWindowsWindow::topLevelOf(QWindow *w)
1816{
1817 while (QWindow *parent = w->parent())
1818 w = parent;
1819
1820 if (const QPlatformWindow *handle = w->handle()) {
1821 const auto *ww = static_cast<const QWindowsWindow *>(handle);
1822 if (ww->isEmbedded()) {
1823 HWND parentHWND = GetAncestor(ww->handle(), GA_PARENT);
1824 const HWND desktopHwnd = GetDesktopWindow();
1826 while (parentHWND && parentHWND != desktopHwnd) {
1827 if (QWindowsWindow *ancestor = ctx->findPlatformWindow(parentHWND))
1828 return topLevelOf(ancestor->window());
1829 parentHWND = GetAncestor(parentHWND, GA_PARENT);
1830 }
1831 }
1832 }
1833 return w;
1834}
1835
1837 QWindowsWindowData::create(const QWindow *w,
1838 const QWindowsWindowData &parameters,
1839 const QString &title)
1840{
1841 WindowCreationData creationData;
1842 creationData.fromWindow(w, parameters.flags);
1843 QWindowsWindowData result = creationData.create(w, parameters, title);
1844 // Force WM_NCCALCSIZE (with wParam=1) via SWP_FRAMECHANGED for custom margin.
1845 creationData.initialize(w, result.hwnd, !parameters.customMargins.isNull(), 1);
1846 return result;
1847}
1848
1849void QWindowsWindow::setVisible(bool visible)
1850{
1851 const QWindow *win = window();
1852 qCDebug(lcQpaWindow) << __FUNCTION__ << this << win << m_data.hwnd << visible;
1853 if (m_data.hwnd) {
1854 if (visible) {
1855 show_sys();
1856
1857 // When the window is layered, we won't get WM_PAINT, and "we" are in control
1858 // over the rendering of the window
1859 // There is nobody waiting for this, so we don't need to flush afterwards.
1860 if (isLayered())
1861 fireFullExpose();
1862 // QTBUG-44928, QTBUG-7386: This is to resolve the problem where popups are
1863 // opened from the system tray and not being implicitly activated
1864
1865 if (win->type() == Qt::Popup && !win->parent() && !QGuiApplication::focusWindow())
1866 SetForegroundWindow(m_data.hwnd);
1867 } else {
1870 if (window()->flags() & Qt::Popup) // from QWidgetPrivate::hide_sys(), activate other
1871 ShowWindow(m_data.hwnd, SW_HIDE);
1872 else
1873 hide_sys();
1874 fireExpose(QRegion());
1875 }
1876 }
1877}
1878
1880{
1881 return m_data.hwnd && IsWindowVisible(m_data.hwnd);
1882}
1883
1885{
1886 // Check for native windows or children of the active native window.
1887 if (const HWND activeHwnd = GetForegroundWindow())
1888 if (m_data.hwnd == activeHwnd || IsChild(activeHwnd, m_data.hwnd))
1889 return true;
1890 return false;
1891}
1892
1893bool QWindowsWindow::isAncestorOf(const QPlatformWindow *child) const
1894{
1895 const auto *childWindow = static_cast<const QWindowsWindow *>(child);
1896 return IsChild(m_data.hwnd, childWindow->handle());
1897}
1898
1900{
1901 return m_data.embedded;
1902}
1903
1904QPoint QWindowsWindow::mapToGlobal(const QPoint &pos) const
1905{
1906 return m_data.hwnd ? QWindowsGeometryHint::mapToGlobal(m_data.hwnd, pos) : pos;
1907}
1908
1909QPoint QWindowsWindow::mapFromGlobal(const QPoint &pos) const
1910{
1911 return m_data.hwnd ? QWindowsGeometryHint::mapFromGlobal(m_data.hwnd, pos) : pos;
1912}
1913
1914// Update the transient parent for a toplevel window. The concept does not
1915// really exist on Windows, the relationship is set by passing a parent along with !WS_CHILD
1916// to window creation or by setting the parent using GWL_HWNDPARENT (as opposed to
1917// SetParent, which would make it a real child).
1918
1919void QWindowsWindow::updateTransientParent() const
1920{
1921 if (window()->type() == Qt::Popup)
1922 return; // QTBUG-34503, // a popup stays on top, no parent, see also WindowCreationData::fromWindow().
1923 // Update transient parent.
1924 const HWND oldTransientParent = GetWindow(m_data.hwnd, GW_OWNER);
1925 HWND newTransientParent = nullptr;
1926 if (const QWindow *tp = window()->transientParent()) {
1927 if (const QWindowsWindow *tw = QWindowsWindow::windowsWindowOf(tp)) {
1928 if (!tw->testFlag(WithinDestroy)) { // Prevent destruction by parent window (QTBUG-35499, QTBUG-36666)
1929 newTransientParent = tw->handle();
1930 }
1931 } else if (const QWindowsBaseWindow *tbw = QWindowsBaseWindow::baseWindowOf(tp)) {
1932 newTransientParent = tbw->handle();
1933 }
1934 }
1935
1936 // QTSOLBUG-71: When using the MFC/winmigrate solution, it is possible that a child
1937 // window is found, which can cause issues with modality. Loop up to top level.
1938 while (newTransientParent && (GetWindowLongPtr(newTransientParent, GWL_STYLE) & WS_CHILD) != 0)
1939 newTransientParent = GetParent(newTransientParent);
1940
1941 if (newTransientParent != oldTransientParent)
1942 SetWindowLongPtr(m_data.hwnd, GWL_HWNDPARENT, LONG_PTR(newTransientParent));
1943}
1944
1945static inline bool testShowWithoutActivating(const QWindow *window)
1946{
1947 // QWidget-attribute Qt::WA_ShowWithoutActivating .
1948 const QVariant showWithoutActivating = window->property("_q_showWithoutActivating");
1949 return showWithoutActivating.isValid() && showWithoutActivating.toBool();
1950}
1951
1952static void setMinimizedGeometry(HWND hwnd, const QRect &r)
1953{
1954 WINDOWPLACEMENT windowPlacement;
1955 windowPlacement.length = sizeof(WINDOWPLACEMENT);
1956 if (GetWindowPlacement(hwnd, &windowPlacement)) {
1957 windowPlacement.showCmd = SW_SHOWMINIMIZED;
1958 windowPlacement.rcNormalPosition = RECTfromQRect(r);
1959 SetWindowPlacement(hwnd, &windowPlacement);
1960 }
1961}
1962
1963static void setRestoreMaximizedFlag(HWND hwnd, bool set = true)
1964{
1965 // Let Windows know that we need to restore as maximized
1966 WINDOWPLACEMENT windowPlacement;
1967 windowPlacement.length = sizeof(WINDOWPLACEMENT);
1968 if (GetWindowPlacement(hwnd, &windowPlacement)) {
1969 if (set)
1970 windowPlacement.flags |= WPF_RESTORETOMAXIMIZED;
1971 else
1972 windowPlacement.flags &= ~WPF_RESTORETOMAXIMIZED;
1973 SetWindowPlacement(hwnd, &windowPlacement);
1974 }
1975}
1976
1977// partially from QWidgetPrivate::show_sys()
1978void QWindowsWindow::show_sys() const
1979{
1980 int sm = SW_SHOWNORMAL;
1981 bool fakedMaximize = false;
1982 bool restoreMaximize = false;
1983 const QWindow *w = window();
1984 const Qt::WindowFlags flags = w->flags();
1985 const Qt::WindowType type = w->type();
1986 if (w->isTopLevel()) {
1987 const Qt::WindowStates state = w->windowStates();
1988 if (state & Qt::WindowMinimized) {
1989 sm = SW_SHOWMINIMIZED;
1990 if (!isVisible())
1991 sm = SW_SHOWMINNOACTIVE;
1992 if (state & Qt::WindowMaximized)
1993 restoreMaximize = true;
1994 } else {
1995 updateTransientParent();
1996 if (state & Qt::WindowMaximized) {
1997 sm = SW_SHOWMAXIMIZED;
1998 // Windows will not behave correctly when we try to maximize a window which does not
1999 // have minimize nor maximize buttons in the window frame. Windows would then ignore
2000 // non-available geometry, and rather maximize the widget to the full screen, minus the
2001 // window frame (caption). So, we do a trick here, by adding a maximize button before
2002 // maximizing the widget, and then remove the maximize button afterwards.
2003 if (flags & Qt::WindowTitleHint &&
2004 !(flags & (Qt::WindowMinMaxButtonsHint | Qt::FramelessWindowHint))) {
2005 fakedMaximize = TRUE;
2006 setStyle(style() | WS_MAXIMIZEBOX);
2007 }
2008 } // Qt::WindowMaximized
2009 } // !Qt::WindowMinimized
2010 }
2011 if (type == Qt::Popup ||
2012 type == Qt::ToolTip ||
2013 type == Qt::Tool ||
2014 (flags & Qt::WindowDoesNotAcceptFocus) ||
2015 testShowWithoutActivating(w))
2016 sm = SW_SHOWNOACTIVATE;
2017
2018 if (w->windowStates() & Qt::WindowMaximized)
2019 setFlag(WithinMaximize); // QTBUG-8361
2020
2021 ShowWindow(m_data.hwnd, sm);
2022
2024
2025 if (fakedMaximize) {
2026 setStyle(style() & ~WS_MAXIMIZEBOX);
2027 SetWindowPos(m_data.hwnd, nullptr, 0, 0, 0, 0,
2028 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER
2029 | SWP_FRAMECHANGED);
2030 }
2031 if (restoreMaximize)
2032 setRestoreMaximizedFlag(m_data.hwnd);
2033}
2034
2035void QWindowsWindow::setParent(const QPlatformWindow *newParent)
2036{
2037 qCDebug(lcQpaWindow) << __FUNCTION__ << window() << newParent;
2038
2039 if (m_data.hwnd)
2040 setParent_sys(newParent);
2041}
2042
2043void QWindowsWindow::setParent_sys(const QPlatformWindow *parent)
2044{
2045 // Use GetAncestor instead of GetParent, as GetParent can return owner window for toplevels
2046 HWND oldParentHWND = parentHwnd();
2047 HWND newParentHWND = nullptr;
2048 if (parent) {
2049 const auto *parentW = static_cast<const QWindowsWindow *>(parent);
2050 newParentHWND = parentW->handle();
2051
2052 }
2053
2054 // NULL handle means desktop window, which also has its proper handle -> disambiguate
2055 HWND desktopHwnd = GetDesktopWindow();
2056 if (oldParentHWND == desktopHwnd)
2057 oldParentHWND = nullptr;
2058 if (newParentHWND == desktopHwnd)
2059 newParentHWND = nullptr;
2060
2061 if (newParentHWND != oldParentHWND) {
2062 const bool wasTopLevel = oldParentHWND == nullptr;
2063 const bool isTopLevel = newParentHWND == nullptr;
2064
2066 SetParent(m_data.hwnd, newParentHWND);
2068
2069 // WS_CHILD/WS_POPUP must be manually set/cleared in addition
2070 // to dialog frames, etc (see SetParent() ) if the top level state changes.
2071 // Force toplevel state as QWindow::isTopLevel cannot be relied upon here.
2072 if (wasTopLevel != isTopLevel) {
2073 setDropSiteEnabled(false);
2074 setWindowFlags_sys(window()->flags(), unsigned(isTopLevel ? WindowCreationData::ForceTopLevel : WindowCreationData::ForceChild));
2075 updateDropSite(isTopLevel);
2076 }
2077 }
2078}
2079
2081{
2082 fireExpose(QRegion());
2083}
2084
2086{
2087 const QWindow *w = window();
2088 if (windowIsAccelerated(w) && w->format().hasAlpha())
2089 applyBlurBehindWindow(handle());
2090}
2091
2093{
2094 return QHighDpiScaling::roundScaleFactor(qreal(dpi) / QWindowsScreen::baseDpi) /
2095 QHighDpiScaling::roundScaleFactor(qreal(savedDpi()) / QWindowsScreen::baseDpi);
2096}
2097
2098void QWindowsWindow::handleDpiScaledSize(WPARAM wParam, LPARAM lParam, LRESULT *result)
2099{
2100 // We want to keep QWindow's device independent size constant across the
2101 // DPI change. To accomplish this, scale QPlatformWindow's native size
2102 // by the change of DPI (e.g. 120 -> 144 = 1.2), also taking any scale
2103 // factor rounding into account. The win32 window size includes the margins;
2104 // add the margins for the new DPI to the window size.
2105 const UINT dpi = UINT(wParam);
2106 const qreal scale = dpiRelativeScale(dpi);
2107 const QMargins margins = fullFrameMargins();
2108 if (!(m_data.flags & Qt::FramelessWindowHint)) {
2109 // We need to update the custom margins to match the current DPI, because
2110 // we don't want our users manually hook into this message just to set a
2111 // new margin, but here we can't call setCustomMargins() directly, that
2112 // function will change the window geometry which conflicts with what we
2113 // are currently doing.
2114 m_data.customMargins *= scale;
2115 }
2116
2117 const QSize windowSize = (geometry().size() * scale).grownBy((margins * scale) + customMargins());
2118 SIZE *size = reinterpret_cast<SIZE *>(lParam);
2119 size->cx = windowSize.width();
2120 size->cy = windowSize.height();
2121 *result = true; // Inform Windows that we've set a size
2122}
2123
2124void QWindowsWindow::handleDpiChanged(HWND hwnd, WPARAM wParam, LPARAM lParam)
2125{
2126 const UINT dpi = HIWORD(wParam);
2127 const qreal scale = dpiRelativeScale(dpi);
2128 setSavedDpi(dpi);
2129
2130 QWindowsThemeCache::clearThemeCache(hwnd);
2131
2132 // Send screen change first, so that the new screen is set during any following resize
2133 const auto prcNewWindow = reinterpret_cast<const RECT *>(lParam);
2134 checkForScreenChanged(QWindowsWindow::FromDpiChange, !m_inSetgeometry ? prcNewWindow : nullptr);
2135
2136 if (!IsZoomed(hwnd))
2137 m_data.restoreGeometry.setSize(m_data.restoreGeometry.size() * scale);
2138
2139 // We get WM_DPICHANGED in one of two situations:
2140 //
2141 // 1. The DPI change is a "spontaneous" DPI change as a result of e.g.
2142 // the user dragging the window to a new screen. In this case Windows
2143 // first sends WM_GETDPISCALEDSIZE, where we set the new window size,
2144 // followed by this event where we apply the suggested window geometry
2145 // to the native window. This will make sure the window tracks the mouse
2146 // cursor during screen change, and also that the window size is scaled
2147 // according to the DPI change.
2148 //
2149 // 2. The DPI change is a result of a setGeometry() call. In this case
2150 // Qt has already scaled the window size for the new DPI. Further, Windows
2151 // does not call WM_GETDPISCALEDSIZE, and also applies its own scaling
2152 // to the already scaled window size. Since there is no need to set the
2153 // window geometry again, and the provided geometry is incorrect, we omit
2154 // making the SetWindowPos() call.
2155 if (!m_inSetgeometry) {
2157 SetWindowPos(hwnd, nullptr, prcNewWindow->left, prcNewWindow->top,
2158 prcNewWindow->right - prcNewWindow->left,
2159 prcNewWindow->bottom - prcNewWindow->top, SWP_NOZORDER | SWP_NOACTIVATE);
2160 // If the window does not have a frame, WM_MOVE and WM_SIZE won't be
2161 // called which prevents the content from being scaled appropriately
2162 // after a DPI change.
2163 if (shouldOmitFrameAdjustment(m_data.flags, m_data.hwnd))
2164 handleGeometryChange();
2165 }
2166
2167 // Re-apply mask now that we have a new DPI, which have resulted in
2168 // a new scale factor.
2169 setMask(QHighDpi::toNativeLocalRegion(window()->mask(), window()));
2170}
2171
2173{
2174 const UINT dpi = GetDpiForWindow(hwnd);
2175 const qreal scale = dpiRelativeScale(dpi);
2176 setSavedDpi(dpi);
2177
2178 checkForScreenChanged(QWindowsWindow::FromDpiChange);
2179
2180 // Child windows do not get WM_GETDPISCALEDSIZE messages to inform
2181 // Windows about the new size, so we need to manually scale them.
2182 QRect currentGeometry = geometry();
2183 QRect scaledGeometry = QRect(currentGeometry.topLeft() * scale, currentGeometry.size() * scale);
2184 setGeometry(scaledGeometry);
2185}
2186
2188{
2189 WINDOWPLACEMENT wp;
2190 wp.length = sizeof(WINDOWPLACEMENT);
2191 if (GetWindowPlacement(hwnd, &wp)) {
2192 const QRect result = qrectFromRECT(wp.rcNormalPosition);
2193 return result.translated(windowPlacementOffset(hwnd, result.topLeft()));
2194 }
2195 return QRect();
2196}
2197
2199{
2200 // Check for fake 'fullscreen' mode.
2201 const bool fakeFullScreen =
2202 m_savedFrameGeometry.isValid() && (window()->windowStates() & Qt::WindowFullScreen);
2203 const QRect frame = fakeFullScreen ? m_savedFrameGeometry : normalFrameGeometry(m_data.hwnd);
2204 const QMargins margins = fakeFullScreen
2205 ? QWindowsGeometryHint::frame(window(), handle(), m_savedStyle, 0)
2206 : fullFrameMargins();
2207 return frame.isValid() ? frame.marginsRemoved(margins) : frame;
2208}
2209
2211{
2212 if (m_data.flags.testFlags(Qt::ExpandedClientAreaHint)) {
2213 const int titleBarHeight = getTitleBarHeight_sys(96);
2214
2215 return QMargins(0, titleBarHeight, 0, 0);
2216 }
2217 return QMargins();
2218}
2219
2220static QString msgUnableToSetGeometry(const QWindowsWindow *platformWindow,
2221 const QRect &requestedRect,
2222 const QRect &obtainedRect,
2223 const QMargins &fullMargins,
2224 const QMargins &customMargins)
2225{
2226 QString result;
2227 QDebug debug(&result);
2228 debug.nospace();
2229 debug.noquote();
2230 const auto window = platformWindow->window();
2231 debug << "Unable to set geometry ";
2232 formatBriefRectangle(debug, requestedRect);
2233 debug << " (frame: ";
2234 formatBriefRectangle(debug, requestedRect + fullMargins);
2235 debug << ") on " << window->metaObject()->className() << "/\""
2236 << window->objectName() << "\" on \"" << window->screen()->name()
2237 << "\". Resulting geometry: ";
2238 formatBriefRectangle(debug, obtainedRect);
2239 debug << " (frame: ";
2240 formatBriefRectangle(debug, obtainedRect + fullMargins);
2241 debug << ") margins: ";
2242 formatBriefMargins(debug, fullMargins);
2243 if (!customMargins.isNull()) {
2244 debug << " custom margin: ";
2245 formatBriefMargins(debug, customMargins);
2246 }
2247 const auto minimumSize = window->minimumSize();
2248 const bool hasMinimumSize = !minimumSize.isEmpty();
2249 if (hasMinimumSize)
2250 debug << " minimum size: " << minimumSize.width() << 'x' << minimumSize.height();
2251 const auto maximumSize = window->maximumSize();
2252 const bool hasMaximumSize = maximumSize.width() != QWINDOWSIZE_MAX || maximumSize.height() != QWINDOWSIZE_MAX;
2253 if (hasMaximumSize)
2254 debug << " maximum size: " << maximumSize.width() << 'x' << maximumSize.height();
2255 if (hasMinimumSize || hasMaximumSize) {
2256 MINMAXINFO minmaxInfo;
2257 memset(&minmaxInfo, 0, sizeof(minmaxInfo));
2258 platformWindow->getSizeHints(&minmaxInfo);
2259 debug << ' ' << minmaxInfo;
2260 }
2261 debug << ')';
2262 return result;
2263}
2264
2265void QWindowsWindow::setGeometry(const QRect &rectIn)
2266{
2267 QScopedValueRollback b(m_inSetgeometry, true);
2268
2269 QRect rect = rectIn;
2270 // This means it is a call from QWindow::setFramePosition() and
2271 // the coordinates include the frame (size is still the contents rectangle).
2272 if (QWindowsGeometryHint::positionIncludesFrame(window())) {
2273 const QMargins margins = frameMargins();
2274 rect.moveTopLeft(rect.topLeft() + QPoint(margins.left(), margins.top()));
2275 }
2276 if (m_windowState & Qt::WindowMinimized)
2277 m_data.geometry = rect; // Otherwise set by handleGeometryChange() triggered by event.
2278 if (m_data.hwnd) {
2279 // A ResizeEvent with resulting geometry will be sent. If we cannot
2280 // achieve that size (for example, window title minimal constraint),
2281 // notify and warn.
2283 setGeometry_sys(rect);
2285 if (m_data.geometry != rect && (isVisible() || QLibraryInfo::isDebugBuild())) {
2286 const auto warning =
2287 msgUnableToSetGeometry(this, rectIn, m_data.geometry,
2288 fullFrameMargins(), customMargins());
2289 qWarning("%s: %s", __FUNCTION__, qPrintable(warning));
2290 }
2291 } else {
2292 QPlatformWindow::setGeometry(rect);
2293 }
2294}
2295
2297{
2298 // Minimize/Set parent can send nonsensical move events.
2299 if (!IsIconic(m_data.hwnd) && !testFlag(WithinSetParent))
2300 handleGeometryChange();
2301}
2302
2303void QWindowsWindow::handleResized(int wParam, LPARAM lParam)
2304{
2305 /* Prevents borderless windows from covering the taskbar when maximized. */
2306 if ((m_data.flags.testFlag(Qt::FramelessWindowHint)
2307 || (m_data.flags.testFlag(Qt::CustomizeWindowHint) && !m_data.flags.testFlag(Qt::WindowTitleHint)))
2308 && IsZoomed(m_data.hwnd)) {
2309 const int resizedWidth = LOWORD(lParam);
2310 const int resizedHeight = HIWORD(lParam);
2311
2312 const HMONITOR monitor = MonitorFromWindow(m_data.hwnd, MONITOR_DEFAULTTOPRIMARY);
2313 MONITORINFO monitorInfo = {};
2314 monitorInfo.cbSize = sizeof(MONITORINFO);
2315 GetMonitorInfoW(monitor, &monitorInfo);
2316
2317 int correctLeft = monitorInfo.rcMonitor.left;
2318 int correctTop = monitorInfo.rcMonitor.top;
2319 int correctWidth = monitorInfo.rcWork.right - monitorInfo.rcWork.left;
2320 int correctHeight = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top;
2321
2322 if (!m_data.flags.testFlag(Qt::FramelessWindowHint)) {
2323 const int borderWidth = invisibleMargins(m_data.hwnd).left();
2324 correctLeft -= borderWidth;
2325 correctTop -= borderWidth;
2326 correctWidth += borderWidth * 2;
2327 correctHeight += borderWidth * 2;
2328 }
2329
2330 if (resizedWidth != correctWidth || resizedHeight != correctHeight) {
2331 qCDebug(lcQpaWindow) << __FUNCTION__ << "correcting: " << resizedWidth << "x"
2332 << resizedHeight << " -> " << correctWidth << "x" << correctHeight;
2333 SetWindowPos(m_data.hwnd, nullptr, correctLeft, correctTop, correctWidth, correctHeight,
2334 SWP_NOZORDER | SWP_NOACTIVATE);
2335 }
2336 }
2337
2338 switch (wParam) {
2339 case SIZE_MAXHIDE: // Some other window affected.
2340 case SIZE_MAXSHOW:
2341 return;
2342 case SIZE_MINIMIZED: // QTBUG-53577, prevent state change events during programmatic state change
2343 if (!testFlag(WithinSetStyle) && !testFlag(WithinSetGeometry))
2344 handleWindowStateChange(m_windowState | Qt::WindowMinimized);
2345 return;
2346 case SIZE_MAXIMIZED:
2347 handleGeometryChange();
2348 if (!testFlag(WithinSetStyle) && !testFlag(WithinSetGeometry))
2349 handleWindowStateChange(Qt::WindowMaximized | (isFullScreen_sys() ? Qt::WindowFullScreen
2350 : Qt::WindowNoState));
2351 break;
2352 case SIZE_RESTORED:
2353 handleGeometryChange();
2355 if (isFullScreen_sys())
2356 handleWindowStateChange(
2357 Qt::WindowFullScreen
2358 | (testFlag(MaximizeToFullScreen) ? Qt::WindowMaximized : Qt::WindowNoState));
2359 else if (m_windowState != Qt::WindowNoState && !testFlag(MaximizeToFullScreen))
2360 handleWindowStateChange(Qt::WindowNoState);
2361 }
2362 break;
2363 }
2364}
2365
2366static inline bool equalDpi(const QDpi &d1, const QDpi &d2)
2367{
2368 return qFuzzyCompare(d1.first, d2.first) && qFuzzyCompare(d1.second, d2.second);
2369}
2370
2371void QWindowsWindow::checkForScreenChanged(ScreenChangeMode mode, const RECT *suggestedRect)
2372{
2373 if ((parent() && !parent()->isForeignWindow()) || QWindowsScreenManager::isSingleScreen())
2374 return;
2375
2376 QPlatformScreen *currentScreen = screen();
2377 auto topLevel = isTopLevel_sys() ? m_data.hwnd : GetAncestor(m_data.hwnd, GA_ROOT);
2378 const QWindowsScreen *newScreen = suggestedRect ?
2379 QWindowsContext::instance()->screenManager().screenForRect(suggestedRect) :
2380 QWindowsContext::instance()->screenManager().screenForHwnd(topLevel);
2381
2382 if (newScreen == nullptr || newScreen == currentScreen)
2383 return;
2384 // For screens with different DPI: postpone until WM_DPICHANGE
2385 // Check on currentScreen as it can be 0 when resuming a session (QTBUG-80436).
2386 const bool changingDpi = !equalDpi(QDpi(savedDpi(), savedDpi()), newScreen->logicalDpi());
2387 if (mode == FromGeometryChange && currentScreen != nullptr && changingDpi)
2388 return;
2389
2390 qCDebug(lcQpaWindow).noquote().nospace() << __FUNCTION__
2391 << ' ' << window() << " \"" << (currentScreen ? currentScreen->name() : QString())
2392 << "\"->\"" << newScreen->name() << '"';
2394 QWindowSystemInterface::handleWindowScreenChanged<QWindowSystemInterface::SynchronousDelivery>(window(), newScreen->screen());
2395}
2396
2397void QWindowsWindow::handleGeometryChange()
2398{
2399 const QRect previousGeometry = m_data.geometry;
2401 m_data.geometry = geometry_sys();
2402 QWindowSystemInterface::handleGeometryChange(window(), m_data.geometry);
2403 // QTBUG-32121: OpenGL/normal windows (with exception of ANGLE
2404 // which we no longer support in Qt 6) do not receive expose
2405 // events when shrinking, synthesize.
2406 if (isExposed()
2407 && m_data.geometry.size() != previousGeometry.size() // Exclude plain move
2408 // One dimension grew -> Windows will send expose, no need to synthesize.
2409 && !(m_data.geometry.width() > previousGeometry.width() || m_data.geometry.height() > previousGeometry.height())) {
2410 fireFullExpose(true);
2411 }
2412
2413 const bool wasSync = testFlag(SynchronousGeometryChangeEvent);
2414 checkForScreenChanged();
2415
2416 if (testFlag(SynchronousGeometryChangeEvent))
2417 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
2418
2421
2422 if (!wasSync)
2424 qCDebug(lcQpaEvents) << __FUNCTION__ << this << window() << m_data.geometry;
2425
2426 if (m_data.flags & Qt::ExpandedClientAreaHint) {
2427 const int titleBarHeight = getTitleBarHeight_sys(savedDpi());
2428 MoveWindow(m_data.hwndTitlebar, 0, 0, m_data.geometry.width(), titleBarHeight, true);
2429 }
2430}
2431
2432void QWindowsBaseWindow::setGeometry_sys(const QRect &rect) const
2433{
2434 const QMargins margins = fullFrameMargins();
2435 const QRect frameGeometry = rect + margins;
2436
2437 qCDebug(lcQpaWindow) << '>' << __FUNCTION__ << window()
2438 << "\n from " << geometry_sys() << " frame: "
2439 << margins << " to " <<rect
2440 << " new frame: " << frameGeometry;
2441
2442 bool result = false;
2443 const HWND hwnd = handle();
2444 WINDOWPLACEMENT windowPlacement;
2445 windowPlacement.length = sizeof(WINDOWPLACEMENT);
2446 GetWindowPlacement(hwnd, &windowPlacement);
2447 // If the window is hidden and in maximized state or minimized, instead of moving the
2448 // window, set the normal position of the window.
2449 if ((windowPlacement.showCmd == SW_MAXIMIZE && !IsWindowVisible(hwnd))
2450 || windowPlacement.showCmd == SW_SHOWMINIMIZED) {
2451 windowPlacement.rcNormalPosition =
2452 RECTfromQRect(frameGeometry.translated(-windowPlacementOffset(hwnd, frameGeometry.topLeft())));
2453 windowPlacement.showCmd = windowPlacement.showCmd == SW_SHOWMINIMIZED ? SW_SHOWMINIMIZED : SW_HIDE;
2454 result = SetWindowPlacement(hwnd, &windowPlacement);
2455 } else {
2456 int x = frameGeometry.x();
2457 if (!window()->isTopLevel()) {
2458 const HWND parentHandle = GetParent(hwnd);
2459 if (isRtlLayout(parentHandle)) {
2460 RECT rect;
2461 GetClientRect(parentHandle, &rect);
2462 x = rect.right - frameGeometry.width() - x;
2463 }
2464 }
2465 result = MoveWindow(hwnd, x, frameGeometry.y(),
2466 frameGeometry.width(), frameGeometry.height(), true);
2467 }
2468 qCDebug(lcQpaWindow) << '<' << __FUNCTION__ << window()
2469 << "\n resulting " << result << geometry_sys();
2470}
2471
2472/*!
2473 Allocates a HDC for the window or returns the temporary one
2474 obtained from WinAPI BeginPaint within a WM_PAINT event.
2475
2476 \sa releaseDC()
2477*/
2478
2480{
2481 if (!m_hdc) {
2482 m_hdc = GetDC(handle());
2483 if (QGuiApplication::layoutDirection() == Qt::RightToLeft)
2484 SetLayout(m_hdc, 0); // Clear RTL layout
2485 }
2486 return m_hdc;
2487}
2488
2489/*!
2490 Releases the HDC for the window or does nothing in
2491 case it was obtained from WinAPI BeginPaint within a WM_PAINT event.
2492
2493 \sa getDC()
2494*/
2495
2497{
2498 if (m_hdc) {
2499 ReleaseDC(handle(), m_hdc);
2500 m_hdc = nullptr;
2501 }
2502}
2503
2504static inline bool isSoftwareGl()
2505{
2506#if QT_CONFIG(dynamicgl)
2507 return QOpenGLStaticContext::opengl32.moduleIsNotOpengl32()
2508 && QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL;
2509#else
2510 return false;
2511#endif // dynamicgl
2512}
2513
2514bool QWindowsWindow::handleWmPaint(HWND hwnd, UINT message,
2515 WPARAM, LPARAM, LRESULT *result)
2516{
2517 if (message == WM_ERASEBKGND) { // Backing store - ignored.
2518 *result = 1;
2519 return true;
2520 }
2521 // QTBUG-75455: Suppress WM_PAINT sent to invisible windows when setting WS_EX_LAYERED
2522 if (!window()->isVisible() && (GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED) != 0)
2523 return false;
2524 // Ignore invalid update bounding rectangles
2525 if (!GetUpdateRect(m_data.hwnd, 0, FALSE))
2526 return false;
2527 PAINTSTRUCT ps;
2528
2529 // GL software rendering (QTBUG-58178) with some AMD cards
2530 // (QTBUG-60527) need InvalidateRect() to suppress artifacts while resizing.
2532 InvalidateRect(hwnd, nullptr, false);
2533
2534 BeginPaint(hwnd, &ps);
2535
2536 // If the a window is obscured by another window (such as a child window)
2537 // we still need to send isExposed=true, for compatibility.
2538 // Our tests depend on it.
2539 fireExpose(QRegion(qrectFromRECT(ps.rcPaint)), true);
2540 if (!QWindowsContext::instance()->asyncExpose())
2541 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
2542
2543 EndPaint(hwnd, &ps);
2544 return true;
2545}
2546
2547void QWindowsWindow::setWindowTitle(const QString &title)
2548{
2549 m_windowTitle = QWindowsWindow::formatWindowTitle(title);
2550 setWindowTitle_sys(m_windowTitle);
2551}
2552
2554{
2555 return m_windowTitle;
2556}
2557
2558void QWindowsWindow::setWindowFlags(Qt::WindowFlags flags)
2559{
2560 qCDebug(lcQpaWindow) << '>' << __FUNCTION__ << this << window() << "\n from: "
2561 << m_data.flags << "\n to: " << flags;
2562 const QRect oldGeometry = geometry();
2563 if (m_data.flags != flags) {
2564 m_data.flags = flags;
2565 if (m_data.hwnd) {
2566 m_data = setWindowFlags_sys(flags);
2567 updateDropSite(window()->isTopLevel());
2568 }
2569 }
2570 // When switching to a frameless window, geometry
2571 // may change without a WM_MOVE. Report change manually.
2572 // Do not send synchronously as not to clobber the widget
2573 // geometry in a sequence of setting flags and geometry.
2574 const QRect newGeometry = geometry_sys();
2575 if (oldGeometry != newGeometry)
2576 handleGeometryChange();
2577
2578 qCDebug(lcQpaWindow) << '<' << __FUNCTION__ << "\n returns: "
2579 << m_data.flags << " geometry " << oldGeometry << "->" << newGeometry;
2580}
2581
2582QWindowsWindowData QWindowsWindow::setWindowFlags_sys(Qt::WindowFlags wt,
2583 unsigned flags) const
2584{
2585 WindowCreationData creationData;
2586 creationData.fromWindow(window(), wt, flags);
2587 creationData.applyWindowFlags(m_data.hwnd);
2588 creationData.initialize(window(), m_data.hwnd, true, m_opacity);
2589
2590 if (creationData.flags.testFlag(Qt::ExpandedClientAreaHint)) {
2591 SetParent(m_data.hwndTitlebar, m_data.hwnd);
2592 ShowWindow(m_data.hwndTitlebar, SW_SHOW);
2593 } else {
2594 if (IsWindowVisible(m_data.hwndTitlebar)) {
2595 SetParent(m_data.hwndTitlebar, HWND_MESSAGE);
2596 ShowWindow(m_data.hwndTitlebar, SW_HIDE);
2597 }
2598 }
2599
2600 QWindowsWindowData result = m_data;
2601 result.flags = creationData.flags;
2602 result.embedded = creationData.embedded;
2603 result.hasFrame = (creationData.style & (WS_DLGFRAME | WS_THICKFRAME))
2604 && !(creationData.flags & Qt::FramelessWindowHint);
2605 return result;
2606}
2607
2608void QWindowsWindow::handleWindowStateChange(Qt::WindowStates state)
2609{
2610 qCDebug(lcQpaWindow) << __FUNCTION__ << this << window()
2611 << "\n from " << m_windowState << " to " << state;
2612 m_windowState = state;
2613 QWindowSystemInterface::handleWindowStateChanged(window(), state);
2614 if (state & Qt::WindowMinimized) {
2616 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents); // Tell QQuickWindow to stop rendering now.
2617 } else {
2619 if (state & Qt::WindowMaximized) {
2620 WINDOWPLACEMENT windowPlacement{};
2621 windowPlacement.length = sizeof(WINDOWPLACEMENT);
2622 GetWindowPlacement(m_data.hwnd, &windowPlacement);
2623 const RECT geometry = RECTfromQRect(m_data.restoreGeometry);
2624 windowPlacement.rcNormalPosition = geometry;
2625 correctWindowPlacement(windowPlacement);
2626
2627 // Even if the window is hidden, windowPlacement's showCmd is not SW_HIDE, so change it
2628 // manually to avoid unhiding a hidden window with the subsequent call to
2629 // SetWindowPlacement().
2630 if (!isVisible())
2631 windowPlacement.showCmd = SW_HIDE;
2632 SetWindowPlacement(m_data.hwnd, &windowPlacement);
2633 }
2634 // QTBUG-17548: We send expose events when receiving WM_Paint, but for
2635 // layered windows and transient children, we won't receive any WM_Paint.
2636 QWindow *w = window();
2637 bool exposeEventsSent = false;
2638 if (isLayered()) {
2639 fireFullExpose();
2640 exposeEventsSent = true;
2641 }
2642 const QWindowList allWindows = QGuiApplication::allWindows();
2643 for (QWindow *child : allWindows) {
2644 if (child != w && child->isVisible() && child->transientParent() == w) {
2645 QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(child);
2646 if (platformWindow && platformWindow->isLayered()) {
2647 platformWindow->fireFullExpose();
2648 exposeEventsSent = true;
2649 }
2650 }
2651 }
2652 if (exposeEventsSent && !QWindowsContext::instance()->asyncExpose())
2653 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
2654 }
2655}
2656
2657// Apply corrections to window placement in Windows 10
2658// Related to task bar on top or left.
2659
2661{
2662 return window()->maximumHeight() != QWINDOWSIZE_MAX;
2663}
2664
2666{
2667 return window()->maximumWidth() != QWINDOWSIZE_MAX;
2668}
2669
2671{
2673}
2674
2675void QWindowsWindow::correctWindowPlacement(WINDOWPLACEMENT &windowPlacement)
2676{
2677 static const auto windows11 = QOperatingSystemVersion::Windows11_21H2;
2678 static const bool isWindows10 = QOperatingSystemVersion::current() < windows11;
2679 if (!isWindows10)
2680 return;
2681
2682 // Correct normal position by placement offset on Windows 10
2683 // (where task bar can be on any side of the screen)
2684 const QPoint offset = windowPlacementOffset(m_data.hwnd, m_data.restoreGeometry.topLeft());
2685 windowPlacement.rcNormalPosition = RECTfromQRect(m_data.restoreGeometry.translated(-offset));
2686 qCDebug(lcQpaWindow) << "Corrected normal position by" << -offset;
2687
2688 // A bug in windows 10 grows
2689 // - ptMaxPosition.x by the task bar's width, if it's on the left
2690 // - ptMaxPosition.y by the task bar's height, if it's on the top
2691 // each time GetWindowPlacement() is called.
2692 // The offset of the screen's left edge (as per frameMargins_sys().left()) is ignored.
2693 // => Check for windows 10 and correct.
2694 if (hasMaximumSize()) {
2695 const QMargins margins = frameMargins_sys();
2696 const QPoint topLeft = window()->screen()->geometry().topLeft();
2697 windowPlacement.ptMaxPosition = POINT{ topLeft.x() - margins.left(), topLeft.y() };
2698 qCDebug(lcQpaWindow) << "Window has maximum size. Corrected topLeft by"
2699 << -margins.left();
2700
2701 // If there is a placement offset correct width/height unless restricted,
2702 // in order to fit window onto the screen.
2703 if (offset.x() > 0 && !hasMaximumWidth()) {
2704 const int adjust = offset.x() / window()->devicePixelRatio();
2705 window()->setWidth(window()->width() - adjust);
2706 qCDebug(lcQpaWindow) << "Width shortened by" << adjust << "logical pixels.";
2707 }
2708 if (offset.y() > 0 && !hasMaximumHeight()) {
2709 const int adjust = offset.y() / window()->devicePixelRatio();
2710 window()->setHeight(window()->height() - adjust);
2711 qCDebug(lcQpaWindow) << "Height shortened by" << adjust << "logical pixels.";
2712 }
2713 }
2714}
2715
2717{
2718 m_data.restoreGeometry = normalFrameGeometry(m_data.hwnd);
2719}
2720
2721void QWindowsWindow::setWindowState(Qt::WindowStates state)
2722{
2723 if (m_data.hwnd) {
2724 setWindowState_sys(state);
2725 m_windowState = state;
2726 }
2727}
2728
2729bool QWindowsWindow::isFullScreen_sys() const
2730{
2731 const QWindow *w = window();
2732 if (!w->isTopLevel())
2733 return false;
2734 QRect geometry = geometry_sys();
2736 geometry += QMargins(1, 1, 1, 1);
2737 QPlatformScreen *screen = screenForGeometry(geometry);
2738 return screen && geometry == screen->geometry();
2739}
2740
2741/*!
2742 \brief Change the window state.
2743
2744 \note Window frames change when maximized;
2745 the top margin shrinks somewhat but that cannot be obtained using
2746 AdjustWindowRectEx().
2747
2748 \note Some calls to SetWindowLong require a subsequent call
2749 to ShowWindow.
2750*/
2751
2752void QWindowsWindow::setWindowState_sys(Qt::WindowStates newState)
2753{
2754 const Qt::WindowStates oldState = m_windowState;
2755 if (oldState == newState)
2756 return;
2757 qCDebug(lcQpaWindow) << '>' << __FUNCTION__ << this << window()
2758 << " from " << oldState << " to " << newState;
2759
2760 const bool visible = isVisible();
2761 auto stateChange = oldState ^ newState;
2762
2763 if (stateChange & Qt::WindowFullScreen) {
2764 if (newState & Qt::WindowFullScreen) {
2765 UINT newStyle = WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_POPUP;
2766 // Save geometry and style to be restored when fullscreen
2767 // is turned off again, since on Windows, it is not a real
2768 // Window state but emulated by changing geometry and style.
2769 if (!m_savedStyle) {
2770 m_savedStyle = style();
2771 if ((oldState & Qt::WindowMinimized) || (oldState & Qt::WindowMaximized)) {
2772 const QRect nf = normalFrameGeometry(m_data.hwnd);
2773 if (nf.isValid())
2774 m_savedFrameGeometry = nf;
2775 } else {
2776 m_savedFrameGeometry = frameGeometry_sys();
2777 }
2778 }
2779 if (newState & Qt::WindowMaximized)
2781 if (m_savedStyle & WS_SYSMENU)
2782 newStyle |= WS_SYSMENU;
2783 if (visible)
2784 newStyle |= WS_VISIBLE;
2785 if (testFlag(HasBorderInFullScreen))
2786 newStyle |= WS_BORDER;
2787 setStyle(newStyle);
2788 const HMONITOR monitor = MonitorFromWindow(m_data.hwnd, MONITOR_DEFAULTTONEAREST);
2789 MONITORINFO monitorInfo = {};
2790 monitorInfo.cbSize = sizeof(MONITORINFO);
2791 GetMonitorInfoW(monitor, &monitorInfo);
2792 const QRect screenGeometry(monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.top,
2793 monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left,
2794 monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top);
2795 if (newState & Qt::WindowMinimized) {
2796 setMinimizedGeometry(m_data.hwnd, screenGeometry);
2797 if (stateChange & Qt::WindowMaximized)
2798 setRestoreMaximizedFlag(m_data.hwnd, newState & Qt::WindowMaximized);
2799 } else {
2800 const UINT swpf = SWP_FRAMECHANGED | SWP_NOACTIVATE;
2801 const bool wasSync = testFlag(SynchronousGeometryChangeEvent);
2803 SetWindowPos(m_data.hwnd, HWND_TOP, screenGeometry.left(), screenGeometry.top(), screenGeometry.width(), screenGeometry.height(), swpf);
2804 if (!wasSync)
2807 QWindowSystemInterface::handleGeometryChange(window(), screenGeometry);
2808 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
2809 }
2810 } else {
2811 // Restore saved state.
2812 unsigned newStyle = m_savedStyle ? m_savedStyle : style();
2813 if (visible)
2814 newStyle |= WS_VISIBLE;
2815 setStyle(newStyle);
2816
2817 const QScreen *screen = window()->screen();
2818 if (!screen)
2819 screen = QGuiApplication::primaryScreen();
2820 // That area of the virtual desktop might not be covered by a screen anymore.
2821 if (const auto platformScreen = screen->handle()) {
2822 if (!platformScreen->geometry().intersects(m_savedFrameGeometry))
2823 m_savedFrameGeometry.moveTo(platformScreen->geometry().topLeft());
2824 }
2825
2826 if (newState & Qt::WindowMinimized) {
2827 setMinimizedGeometry(m_data.hwnd, m_savedFrameGeometry);
2828 if (stateChange & Qt::WindowMaximized)
2829 setRestoreMaximizedFlag(m_data.hwnd, newState & Qt::WindowMaximized);
2830 } else {
2831 UINT swpf = SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOACTIVATE;
2832 if (!m_savedFrameGeometry.isValid())
2833 swpf |= SWP_NOSIZE | SWP_NOMOVE;
2834 const bool wasSync = testFlag(SynchronousGeometryChangeEvent);
2836 // After maximized/fullscreen; the window can be in a maximized state. Clear
2837 // it before applying the normal geometry.
2838 if (windowVisibility_sys(m_data.hwnd) == QWindow::Maximized)
2839 ShowWindow(m_data.hwnd, SW_SHOWNOACTIVATE);
2840 SetWindowPos(m_data.hwnd, nullptr, m_savedFrameGeometry.x(), m_savedFrameGeometry.y(),
2841 m_savedFrameGeometry.width(), m_savedFrameGeometry.height(), swpf);
2842 if (!wasSync)
2844 // preserve maximized state
2845 if (visible) {
2847 ShowWindow(m_data.hwnd,
2848 (newState & Qt::WindowMaximized) ? SW_MAXIMIZE : SW_SHOWNA);
2850 }
2851 }
2852 m_savedStyle = 0;
2853 m_savedFrameGeometry = QRect();
2854 }
2855 } else if ((oldState & Qt::WindowMaximized) != (newState & Qt::WindowMaximized)) {
2856 if (visible && !(newState & Qt::WindowMinimized)) {
2858 if (newState & Qt::WindowFullScreen)
2860 if (m_data.flags & Qt::FramelessWindowHint) {
2861 if (newState == Qt::WindowNoState) {
2862 const QRect &rect = m_savedFrameGeometry;
2863 MoveWindow(m_data.hwnd, rect.x(), rect.y(), rect.width(), rect.height(), true);
2864 } else {
2865 HMONITOR monitor = MonitorFromWindow(m_data.hwnd, MONITOR_DEFAULTTONEAREST);
2866 MONITORINFO monitorInfo = {};
2867 monitorInfo.cbSize = sizeof(MONITORINFO);
2868 GetMonitorInfo(monitor, &monitorInfo);
2869 const RECT &rect = monitorInfo.rcWork;
2870 m_savedFrameGeometry = geometry();
2871 MoveWindow(m_data.hwnd, rect.left, rect.top,
2872 rect.right - rect.left, rect.bottom - rect.top, true);
2873 }
2874 } else {
2875 ShowWindow(m_data.hwnd,
2876 (newState & Qt::WindowMaximized) ? SW_MAXIMIZE : SW_SHOWNOACTIVATE);
2877 }
2880 } else if (visible && (oldState & newState & Qt::WindowMinimized)) {
2881 // change of the maximized state while keeping minimized
2882 setRestoreMaximizedFlag(m_data.hwnd, newState & Qt::WindowMaximized);
2883 }
2884 }
2885
2886 if (stateChange & Qt::WindowMinimized) {
2887 if (visible) {
2888 ShowWindow(m_data.hwnd,
2889 (newState & Qt::WindowMinimized) ? SW_MINIMIZE :
2890 (newState & Qt::WindowMaximized) ? SW_MAXIMIZE : SW_SHOWNORMAL);
2891 if ((newState & Qt::WindowMinimized) && (stateChange & Qt::WindowMaximized))
2892 setRestoreMaximizedFlag(m_data.hwnd, newState & Qt::WindowMaximized);
2893 }
2894 }
2895 qCDebug(lcQpaWindow) << '<' << __FUNCTION__ << this << window() << newState;
2896}
2897
2898void QWindowsWindow::setStyle(unsigned s) const
2899{
2900 qCDebug(lcQpaWindow) << __FUNCTION__ << this << window() << debugWinStyle(s);
2902 SetWindowLongPtr(m_data.hwnd, GWL_STYLE, s);
2904}
2905
2906void QWindowsWindow::setExStyle(unsigned s) const
2907{
2908 qCDebug(lcQpaWindow) << __FUNCTION__ << this << window() << debugWinExStyle(s);
2909 SetWindowLongPtr(m_data.hwnd, GWL_EXSTYLE, s);
2910}
2911
2912bool QWindowsWindow::windowEvent(QEvent *event)
2913{
2914 switch (event->type()) {
2915 case QEvent::ApplicationPaletteChange:
2916 setDarkBorder(QWindowsTheme::instance()->colorScheme() == Qt::ColorScheme::Dark);
2917 break;
2918 case QEvent::WindowBlocked: // Blocked by another modal window.
2919 setEnabled(false);
2922 ReleaseCapture();
2923 break;
2924 case QEvent::WindowUnblocked:
2925 setEnabled(true);
2927 break;
2928 default:
2929 break;
2930 }
2931
2932 return QWindowsBaseWindow::windowEvent(event);
2933}
2934
2936{
2937 qCDebug(lcQpaWindow) << __FUNCTION__ << this << window();
2938}
2939
2940static bool isResize(const WINDOWPOS *windowPos)
2941{
2942 bool result = false;
2943 if ((windowPos->flags & SWP_NOSIZE) == 0) {
2944 RECT rect;
2945 GetWindowRect(windowPos->hwnd, &rect);
2946 result = rect.right - rect.left != windowPos->cx || rect.bottom - rect.top != windowPos->cy;
2947 }
2948 return result;
2949}
2950
2951bool QWindowsWindow::handleGeometryChangingMessage(MSG *message, const QWindow *qWindow, const QMargins &margins)
2952{
2953 auto *windowPos = reinterpret_cast<WINDOWPOS *>(message->lParam);
2954 const QRect suggestedFrameGeometry(windowPos->x, windowPos->y,
2955 windowPos->cx, windowPos->cy);
2956 const QRect suggestedGeometry = suggestedFrameGeometry - margins;
2957
2958 // Tell Windows to discard the entire contents of the client area, as re-using
2959 // parts of the client area would lead to jitter during resize.
2960 // Check the suggestedGeometry against the current one to only discard during
2961 // resize, and not a plain move. We also look for SWP_NOSIZE since that, too,
2962 // implies an identical size, and comparing QRects wouldn't work with null cx/cy
2963 if (isResize(windowPos))
2964 windowPos->flags |= SWP_NOCOPYBITS;
2965
2966 if ((windowPos->flags & SWP_NOZORDER) == 0) {
2967 if (QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(qWindow)) {
2968 QWindow *parentWindow = qWindow->parent();
2969 HWND parentHWND = GetAncestor(windowPos->hwnd, GA_PARENT);
2970 HWND desktopHWND = GetDesktopWindow();
2971 platformWindow->m_data.embedded = !parentWindow && parentHWND && (parentHWND != desktopHWND);
2972 }
2973 if (qWindow->flags().testFlag(Qt::WindowStaysOnBottomHint))
2974 windowPos->hwndInsertAfter = HWND_BOTTOM;
2975 }
2976 if (!qWindow->isTopLevel()) // Implement hasHeightForWidth().
2977 return false;
2978 if (windowPos->flags & SWP_NOSIZE)
2979 return false;
2980 const QRectF correctedGeometryF = QPlatformWindow::closestAcceptableGeometry(qWindow, suggestedGeometry);
2981 if (!correctedGeometryF.isValid())
2982 return false;
2983 const QRect correctedFrameGeometry = correctedGeometryF.toRect() + margins;
2984 if (correctedFrameGeometry == suggestedFrameGeometry)
2985 return false;
2986 windowPos->x = correctedFrameGeometry.left();
2987 windowPos->y = correctedFrameGeometry.top();
2988 windowPos->cx = correctedFrameGeometry.width();
2989 windowPos->cy = correctedFrameGeometry.height();
2990 return true;
2991}
2992
2994{
2995 const QMargins margins = window()->isTopLevel() ? fullFrameMargins() : QMargins();
2996 return QWindowsWindow::handleGeometryChangingMessage(message, window(), margins);
2997}
2998
2999void QWindowsWindow::setFullFrameMargins(const QMargins &newMargins)
3000{
3001 if (shouldOmitFrameAdjustment(m_data.flags, m_data.hwnd))
3002 return;
3003 if (m_data.fullFrameMargins != newMargins) {
3004 qCDebug(lcQpaWindow) << __FUNCTION__ << window() << m_data.fullFrameMargins << "->" << newMargins;
3005 m_data.fullFrameMargins = newMargins;
3006 }
3007}
3008
3010{
3011 // QTBUG-82580: If a native menu is present, force a WM_NCCALCSIZE.
3012 if (GetMenu(m_data.hwnd))
3013 QWindowsContext::forceNcCalcSize(m_data.hwnd);
3014 else
3015 calculateFullFrameMargins();
3016}
3017
3018void QWindowsWindow::calculateFullFrameMargins()
3019{
3020 if (shouldOmitFrameAdjustment(m_data.flags, m_data.hwnd))
3021 return;
3022
3023 // QTBUG-113736: systemMargins depends on AdjustWindowRectExForDpi. This doesn't take into
3024 // account possible external modifications to the titlebar, as with ExtendsContentIntoTitleBar()
3025 // from the Windows App SDK. We can fix this by comparing the WindowRect (which includes the
3026 // frame) to the ClientRect. If a 'typical' frame is detected, i.e. only the titlebar has been
3027 // modified, we can safely adjust the frame by deducting the bottom margin to the total Y
3028 // difference between the two rects, to get the actual size of the titlebar and prevent
3029 // unwanted client area slicing.
3030
3031 RECT windowRect{};
3032 RECT clientRect{};
3033 GetWindowRect(handle(), &windowRect);
3034 GetClientRect(handle(), &clientRect);
3035
3036 // QTBUG-117704 It is also possible that the user has manually removed the frame (for example
3037 // by handling WM_NCCALCSIZE). If that is the case, i.e., the client area and the window area
3038 // have identical sizes, we don't want to override the user-defined margins.
3039
3040 if (qrectFromRECT(windowRect).size() == qrectFromRECT(clientRect).size())
3041 return;
3042
3043 // Normally obtained from WM_NCCALCSIZE. This calculation only works
3044 // when no native menu is present.
3045 const auto systemMargins = testFlag(DisableNonClientScaling)
3046 ? QWindowsGeometryHint::frameOnPrimaryScreen(window(), m_data.hwnd)
3047 : frameMargins_sys();
3048 const int extendedClientAreaBorder = window()->flags().testFlag(Qt::ExpandedClientAreaHint) ? qRound(QHighDpiScaling::factor(window())) * 2 : 0;
3049 const QMargins actualMargins = systemMargins + customMargins() - extendedClientAreaBorder;
3050
3051 const int yDiff = (windowRect.bottom - windowRect.top) - (clientRect.bottom - clientRect.top);
3052 const bool typicalFrame = (actualMargins.left() == actualMargins.right())
3053 && (actualMargins.right() == actualMargins.bottom());
3054
3055 const QMargins adjustedMargins = typicalFrame ?
3056 QMargins(actualMargins.left(), (yDiff - actualMargins.bottom()),
3057 actualMargins.right(), actualMargins.bottom())
3058 : actualMargins;
3059
3060 setFullFrameMargins(adjustedMargins);
3061}
3062
3064{
3065 QMargins result = fullFrameMargins();
3066 if (isTopLevel() && m_data.hasFrame)
3067 result -= invisibleMargins(m_data.hwnd);
3068 return result;
3069}
3070
3072{
3073 if (shouldOmitFrameAdjustment(m_data.flags, m_data.hwnd))
3074 return {};
3075 return m_data.fullFrameMargins;
3076}
3077
3078void QWindowsWindow::setOpacity(qreal level)
3079{
3080 qCDebug(lcQpaWindow) << __FUNCTION__ << level;
3081 if (!qFuzzyCompare(m_opacity, level)) {
3082 m_opacity = level;
3083 if (m_data.hwnd)
3084 setWindowOpacity(m_data.hwnd, m_data.flags,
3086 level);
3087 }
3088}
3089
3090static inline HRGN createRectRegion(const QRect &r)
3091{
3092 return CreateRectRgn(r.left(), r.top(), r.x() + r.width(), r.y() + r.height());
3093}
3094
3095static inline void addRectToWinRegion(const QRect &rect, HRGN *winRegion)
3096{
3097 if (const HRGN rectRegion = createRectRegion(rect)) {
3098 HRGN result = CreateRectRgn(0, 0, 0, 0);
3099 if (CombineRgn(result, *winRegion, rectRegion, RGN_OR)) {
3100 DeleteObject(*winRegion);
3101 *winRegion = result;
3102 }
3103 DeleteObject(rectRegion);
3104 }
3105}
3106
3107static HRGN qRegionToWinRegion(const QRegion &region)
3108{
3109 auto it = region.begin();
3110 const auto end = region.end();
3111 if (it == end)
3112 return nullptr;
3113 HRGN hRegion = createRectRegion(*it);
3114 while (++it != end)
3115 addRectToWinRegion(*it, &hRegion);
3116 return hRegion;
3117}
3118
3119void QWindowsWindow::setMask(const QRegion &region)
3120{
3121 if (region.isEmpty()) {
3122 SetWindowRgn(m_data.hwnd, nullptr, true);
3123 return;
3124 }
3125 const HRGN winRegion = qRegionToWinRegion(region);
3126
3127 // Mask is in client area coordinates, so offset it in case we have a frame
3128 if (window()->isTopLevel()) {
3129 const QMargins margins = fullFrameMargins();
3130 OffsetRgn(winRegion, margins.left(), margins.top());
3131 }
3132
3133 // SetWindowRgn takes ownership.
3134 if (!SetWindowRgn(m_data.hwnd, winRegion, true))
3135 DeleteObject(winRegion);
3136}
3137
3139{
3140 qCDebug(lcQpaWindow) << __FUNCTION__ << this << window();
3141
3142 if (!m_data.hwnd)
3143 return;
3144
3145 const auto activationBehavior = QWindowsIntegration::instance()->windowActivationBehavior();
3146 if (QGuiApplication::applicationState() == Qt::ApplicationActive
3147 || activationBehavior != QWindowsApplication::AlwaysActivateWindow) {
3148 SetForegroundWindow(m_data.hwnd);
3149 SetFocus(m_data.hwnd);
3150 return;
3151 }
3152
3153 // Force activate this window. The following code will bring the window to the
3154 // foreground and activate it. If the window is hidden, it will show up. If
3155 // the window is minimized, it will restore to the previous position.
3156
3157 // But first we need some sanity checks.
3158 if (m_data.flags & Qt::WindowStaysOnBottomHint) {
3159 qCWarning(lcQpaWindow) <<
3160 "Windows with Qt::WindowStaysOnBottomHint can't be brought to the foreground.";
3161 return;
3162 }
3163 if (m_data.flags & Qt::WindowStaysOnTopHint) {
3164 qCWarning(lcQpaWindow) <<
3165 "Windows with Qt::WindowStaysOnTopHint will always be on the foreground.";
3166 return;
3167 }
3168 if (window()->type() == Qt::ToolTip) {
3169 qCWarning(lcQpaWindow) << "ToolTip windows should not be activated.";
3170 return;
3171 }
3172
3173 // We need to show the window first, otherwise we won't be able to bring it to front.
3174 if (!IsWindowVisible(m_data.hwnd))
3175 ShowWindow(m_data.hwnd, SW_SHOW);
3176
3177 if (IsIconic(m_data.hwnd)) {
3178 ShowWindow(m_data.hwnd, SW_RESTORE);
3179 // When the window is restored, it will always become the foreground window.
3180 // So return early here, we don't need the following code to bring it to front.
3181 return;
3182 }
3183
3184 // OK, our window is not minimized, so now we will try to bring it to front manually.
3185 const HWND oldForegroundWindow = GetForegroundWindow();
3186 if (!oldForegroundWindow) // It may be NULL, according to MS docs.
3187 return;
3188
3189 // First try to send a message to the current foreground window to check whether
3190 // it is currently hanging or not.
3191 if (SendMessageTimeoutW(oldForegroundWindow, WM_NULL, 0, 0,
3192 SMTO_BLOCK | SMTO_ABORTIFHUNG | SMTO_NOTIMEOUTIFNOTHUNG, 1000, nullptr) == 0) {
3193 qCWarning(lcQpaWindow) << "The foreground window hangs, can't activate current window.";
3194 return;
3195 }
3196
3197 const DWORD windowThreadProcessId = GetWindowThreadProcessId(oldForegroundWindow, nullptr);
3198 const DWORD currentThreadId = GetCurrentThreadId();
3199
3200 AttachThreadInput(windowThreadProcessId, currentThreadId, TRUE);
3201 const auto cleanup = qScopeGuard([windowThreadProcessId, currentThreadId](){
3202 AttachThreadInput(windowThreadProcessId, currentThreadId, FALSE);
3203 });
3204
3205 BringWindowToTop(m_data.hwnd);
3206
3207 // Activate the window too. This will force us to the virtual desktop this
3208 // window is on, if it's on another virtual desktop.
3209 SetActiveWindow(m_data.hwnd);
3210}
3211
3213{
3214 if (!m_data.hwnd) {
3215 qWarning("%s: No handle", __FUNCTION__);
3216 return false;
3217 }
3218 qCDebug(lcQpaWindow) << __FUNCTION__ << this << window() << grab;
3219
3221 if (grab) {
3222 context->setKeyGrabber(window());
3223 } else {
3224 if (context->keyGrabber() == window())
3225 context->setKeyGrabber(nullptr);
3226 }
3227 return true;
3228}
3229
3231{
3232 qCDebug(lcQpaWindow) << __FUNCTION__ << window() << grab;
3233 if (!m_data.hwnd) {
3234 qWarning("%s: No handle", __FUNCTION__);
3235 return false;
3236 }
3237 if (!isVisible() && grab) {
3238 qWarning("%s: Not setting mouse grab for invisible window %s/'%s'",
3239 __FUNCTION__, window()->metaObject()->className(),
3240 qPrintable(window()->objectName()));
3241 return false;
3242 }
3243 // release grab or an explicit grab overriding autocapture: Clear flag.
3245 if (hasMouseCapture() != grab) {
3246 if (grab) {
3247 SetCapture(m_data.hwnd);
3248 } else {
3249 ReleaseCapture();
3250 }
3251 }
3252 return grab;
3253}
3254
3255static inline DWORD edgesToWinOrientation(Qt::Edges edges)
3256{
3257 if (edges == Qt::LeftEdge)
3258 return 0xf001; // SC_SIZELEFT;
3259 else if (edges == (Qt::RightEdge))
3260 return 0xf002; // SC_SIZERIGHT
3261 else if (edges == (Qt::TopEdge))
3262 return 0xf003; // SC_SIZETOP
3263 else if (edges == (Qt::TopEdge | Qt::LeftEdge))
3264 return 0xf004; // SC_SIZETOPLEFT
3265 else if (edges == (Qt::TopEdge | Qt::RightEdge))
3266 return 0xf005; // SC_SIZETOPRIGHT
3267 else if (edges == (Qt::BottomEdge))
3268 return 0xf006; // SC_SIZEBOTTOM
3269 else if (edges == (Qt::BottomEdge | Qt::LeftEdge))
3270 return 0xf007; // SC_SIZEBOTTOMLEFT
3271 else if (edges == (Qt::BottomEdge | Qt::RightEdge))
3272 return 0xf008; // SC_SIZEBOTTOMRIGHT
3273
3274 return 0xf000; // SC_SIZE
3275}
3276
3277bool QWindowsWindow::startSystemResize(Qt::Edges edges)
3278{
3279 if (Q_UNLIKELY(window()->flags().testFlag(Qt::MSWindowsFixedSizeDialogHint)))
3280 return false;
3281
3282 ReleaseCapture();
3283 PostMessage(m_data.hwnd, WM_SYSCOMMAND, edgesToWinOrientation(edges), 0);
3285 return true;
3286}
3287
3289{
3290 ReleaseCapture();
3291 PostMessage(m_data.hwnd, WM_SYSCOMMAND, 0xF012 /*SC_DRAGMOVE*/, 0);
3292 return true;
3293}
3294
3296{
3297 if (enabled) {
3299 } else {
3301 }
3302}
3303
3304void QWindowsWindow::getSizeHints(MINMAXINFO *mmi) const
3305{
3306 QWindowsGeometryHint::applyToMinMaxInfo(window(), fullFrameMargins(), mmi);
3307 qCDebug(lcQpaWindow) << __FUNCTION__ << window() << *mmi;
3308}
3309
3310bool QWindowsWindow::handleNonClientHitTest(const QPoint &globalPos, LRESULT *result) const
3311{
3312 // QTBUG-32663, suppress resize cursor for fixed size windows.
3313 const QWindow *w = window();
3314 const QPoint localPos = w->mapFromGlobal(QHighDpi::fromNativePixels(globalPos, w));
3315 const QRect geom = geometry();
3316 static auto oldMouseButtonState = Qt::NoButton;
3317
3318 if (m_data.flags.testFlags(Qt::ExpandedClientAreaHint)) {
3319 bool isDefaultTitleBar = !w->flags().testFlag(Qt::CustomizeWindowHint);
3320 bool isCustomized = w->flags().testFlags(Qt::CustomizeWindowHint) && w->flags().testAnyFlags(Qt::WindowTitleHint|
3321 Qt::WindowMinimizeButtonHint|
3322 Qt::WindowMaximizeButtonHint|
3323 Qt::WindowCloseButtonHint);
3324 const int border = (IsZoomed(m_data.hwnd) || isFullScreen_sys()) ? 0 : getResizeBorderThickness(savedDpi());
3325 const int titleBarHeight = getTitleBarHeight_sys(savedDpi());
3326 const int titleButtonWidth = titleBarHeight * 1.5;
3327 const bool mouseButtonsSwapped = GetSystemMetrics(SM_SWAPBUTTON);
3328 auto mouseButtons = Qt::NoButton;
3329 if (mouseButtonsSwapped)
3330 mouseButtons = GetAsyncKeyState(VK_LBUTTON) != 0 ? Qt::RightButton : (GetAsyncKeyState(VK_RBUTTON) ? Qt::LeftButton : Qt::NoButton);
3331 else
3332 mouseButtons = GetAsyncKeyState(VK_LBUTTON) != 0 ? Qt::LeftButton : (GetAsyncKeyState(VK_RBUTTON) ? Qt::RightButton : Qt::NoButton);
3333
3334 *result = HTCLIENT;
3335 if (isCustomized || isDefaultTitleBar) {
3336 int buttons = 1;
3337
3338 if (globalPos.y() < geom.top() + titleBarHeight) {
3339 if (m_data.flags.testFlags(Qt::WindowCloseButtonHint) || isDefaultTitleBar) {
3340 if ((globalPos.x() > geom.right() - titleButtonWidth * buttons) && (globalPos.x() <= geom.right())) {
3341 if (mouseButtons == Qt::LeftButton)
3342 *result = HTCLOSE;
3343 }
3344 buttons++;
3345 } if (m_data.flags.testFlags(Qt::WindowMaximizeButtonHint) || isDefaultTitleBar) {
3346 if ((globalPos.x() > geom.right() - titleButtonWidth * buttons) && (globalPos.x() <= geom.right() - titleButtonWidth * (buttons-1))){
3347 if (mouseButtons == Qt::LeftButton) {
3348 if (IsZoomed(m_data.hwnd))
3349 *result = HTSIZE;
3350 else
3351 *result = HTMAXBUTTON;
3352 }
3353 }
3354 buttons++;
3355 } if (m_data.flags.testFlags(Qt::WindowMinimizeButtonHint) || isDefaultTitleBar) {
3356 if ((globalPos.x() > geom.right() - titleButtonWidth * buttons) && (globalPos.x() <= geom.right() - titleButtonWidth * (buttons-1))){
3357 if (mouseButtons == Qt::LeftButton)
3358 *result = HTMINBUTTON;
3359 }
3360 buttons++;
3361 } if ((isCustomized || isDefaultTitleBar) &&
3362 *result == HTCLIENT){
3363 QWindow* wnd = window();
3364 if (mouseButtons != oldMouseButtonState) {
3365 auto mouseEventType = mouseButtons == Qt::NoButton ? QEvent::MouseButtonRelease : QEvent::MouseButtonPress;
3366 auto mouseEventButtons = mouseEventType == QEvent::MouseButtonPress ? mouseButtons : oldMouseButtonState;
3367 bool accepted = QWindowSystemInterface::handleMouseEvent<QWindowSystemInterface::SynchronousDelivery>(wnd, QHighDpi::toNativeLocalPosition(localPos, w), globalPos, mouseEventButtons, mouseEventButtons, mouseEventType);
3368 if (!accepted && mouseButtons == Qt::RightButton)
3369 *result = HTSYSMENU;
3370 else if (!accepted && globalPos.y() < geom.top() + titleBarHeight)
3371 *result = HTCAPTION;
3372 }
3373 }
3374 }
3375 } else if (w->flags().testFlag(Qt::CustomizeWindowHint)) {
3376
3377 QWindow* wnd = window();
3378 if (mouseButtons != oldMouseButtonState) {
3379 auto mouseEventType = mouseButtons == Qt::NoButton ? QEvent::MouseButtonRelease : QEvent::MouseButtonPress;
3380 auto mouseEventButtons = mouseEventType == QEvent::MouseButtonPress ? mouseButtons : oldMouseButtonState;
3381 bool accepted = QWindowSystemInterface::handleMouseEvent<QWindowSystemInterface::SynchronousDelivery>(wnd, QHighDpi::toNativeLocalPosition(localPos, w), globalPos, mouseEventButtons, mouseEventButtons, mouseEventType);
3382 if (!accepted && mouseButtons == Qt::RightButton)
3383 *result = HTSYSMENU;
3384 else if (!accepted && globalPos.y() < geom.top() + titleBarHeight)
3385 *result = HTCAPTION;
3386 }
3387 }
3388 oldMouseButtonState = mouseButtons;
3389
3390 if (border != 0) {
3391 const bool left = (globalPos.x() >= geom.left()) && (globalPos.x() < geom.left() + border);
3392 const bool right = (globalPos.x() > geom.right() - border) && (globalPos.x() <= geom.right());
3393 const bool top = (globalPos.y() >= geom.top()) && (globalPos.y() < geom.top() + border);
3394 const bool bottom = (globalPos.y() > geom.bottom() - border) && (globalPos.y() <= geom.bottom());
3395
3396 if (left || right || top || bottom) {
3397 if (left)
3398 *result = top ? HTTOPLEFT : (bottom ? HTBOTTOMLEFT : HTLEFT);
3399 else if (right)
3400 *result = top ? HTTOPRIGHT : (bottom ? HTBOTTOMRIGHT : HTRIGHT);
3401 else
3402 *result = top ? HTTOP : HTBOTTOM;
3403 }
3404 }
3405
3406 switch (*result) {
3407 case HTCLOSE:
3408 const_cast<QWindow *>(w)->close();
3409 break;
3410 case HTMAXBUTTON:
3411 const_cast<QWindow *>(w)->showMaximized();
3412 break;
3413 case HTMINBUTTON:
3414 const_cast<QWindow *>(w)->showMinimized();
3415 break;
3416 case HTSIZE:
3417 const_cast<QWindow *>(w)->showNormal();
3418 break;
3419 case HTSYSMENU: {
3420 HWND hwnd = reinterpret_cast<HWND>(w->winId());
3421 HMENU sysMenu = GetSystemMenu(hwnd, false);
3422 TrackPopupMenu(sysMenu, 0, globalPos.x(), globalPos.y(), 0, hwnd, nullptr);
3423 }
3424 default:
3425 break;
3426 }
3427 return true;
3428 }
3429
3430 // QTBUG-32663, suppress resize cursor for fixed size windows.
3431 if (m_data.flags & Qt::ExpandedClientAreaHint) {
3432 const int border = (IsZoomed(m_data.hwnd) || isFullScreen_sys()) ? 0 : getResizeBorderThickness(savedDpi());
3433 if (border == 0) {
3434 *result = HTCLIENT;
3435 return true;
3436 }
3437 const QRect rect = geom;
3438 const bool left = (globalPos.x() >= rect.left()) && (globalPos.x() < rect.left() + border);
3439 const bool right = (globalPos.x() > rect.right() - border) && (globalPos.x() <= rect.right());
3440 const bool top = (globalPos.y() >= rect.top()) && (globalPos.y() < rect.top() + border);
3441 const bool bottom = (globalPos.y() > rect.bottom() - border) && (globalPos.y() <= rect.bottom());
3442
3443
3444 if (left || right || top || bottom) {
3445 if (left)
3446 *result = top ? HTTOPLEFT : (bottom ? HTBOTTOMLEFT : HTLEFT);
3447 else if (right)
3448 *result = top ? HTTOPRIGHT : (bottom ? HTBOTTOMRIGHT : HTRIGHT);
3449 else
3450 *result = top ? HTTOP : HTBOTTOM;
3451 } else {
3452 *result = HTCLIENT;
3453 }
3454 return true;
3455 }
3456
3457 // QTBUG-32663, suppress resize cursor for fixed size windows.
3458 if (!w->isTopLevel() // Task 105852, minimized windows need to respond to user input.
3459 || (m_windowState != Qt::WindowNoState)
3460 || !isActive()
3461 || (m_data.flags & Qt::FramelessWindowHint)) {
3462 return false;
3463 }
3464 const QSize minimumSize = w->minimumSize();
3465 if (minimumSize.isEmpty())
3466 return false;
3467 const QSize maximumSize = w->maximumSize();
3468 const bool fixedWidth = minimumSize.width() == maximumSize.width();
3469 const bool fixedHeight = minimumSize.height() == maximumSize.height();
3470 if (!fixedWidth && !fixedHeight)
3471 return false;
3472 const QSize size = w->size();
3473 if (fixedHeight) {
3474 if (localPos.y() >= size.height()) {
3475 *result = HTBORDER; // Unspecified border, no resize cursor.
3476 return true;
3477 }
3478 if (localPos.y() < 0) {
3479 const int topResizeBarPos = invisibleMargins(m_data.hwnd).left() - frameMargins().top();
3480 if (localPos.y() < topResizeBarPos) {
3481 *result = HTCAPTION; // Extend caption over top resize bar, let's user move the window.
3482 return true;
3483 }
3484 }
3485 }
3486 if (fixedWidth && (localPos.x() < 0 || localPos.x() >= size.width())) {
3487 *result = HTBORDER; // Unspecified border, no resize cursor.
3488 return true;
3489 }
3490 return false;
3491}
3492
3493bool QWindowsWindow::handleNonClientActivate(LRESULT *result) const
3494{
3495 // If this window is frameless we choose to consume the event,
3496 // since the default logic causes the window title to appear.
3497 // QTBUG-127116
3498 if (m_data.flags & Qt::FramelessWindowHint) {
3499 *result = true;
3500 return true;
3501 }
3502 return false;
3503}
3504
3506{
3507 HWND hwnd = m_data.hwndTitlebar;
3508 QWindow *wnd = window();
3509
3510 RECT windowRect;
3511 GetWindowRect(hwnd, &windowRect);
3512
3513 const int titleBarHeight = getTitleBarHeight_sys(savedDpi());
3514 const int titleButtonWidth = titleBarHeight * 1.5;
3515 const qreal factor = QHighDpiScaling::factor(wnd);
3516 const int windowWidth = windowRect.right - windowRect.left;
3517
3518 POINT localPos;
3519 GetCursorPos(&localPos);
3520 MapWindowPoints(HWND_DESKTOP, m_data.hwnd, &localPos, 1);
3521
3522 const bool isDarkmode = QWindowsIntegration::instance()->darkModeHandling().testFlags(QWindowsApplication::DarkModeWindowFrames) &&
3523 qApp->styleHints()->colorScheme() == Qt::ColorScheme::Dark;
3524 const bool isWindows11orAbove = QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows11;
3525
3526 const QBrush closeButtonBrush(QColor(0xC4, 0x2C, 0x1E, 255));
3527 const QBrush minMaxButtonBrush = QBrush(isDarkmode ? QColor(0xFF, 0xFF, 0xFF, 0x40) : QColor(0x00, 0x00, 0x00, 0x20));
3528 const QBrush titleBarBackgroundColor = QBrush(isDarkmode ? QColor(0x1F, 0x1F, 0x1F, 0xFF) : QColor(0xF3, 0xF3, 0xF3, 0xFF));
3529 const QPen textPen = QPen(isDarkmode ? QColor(0xFF, 0xFF, 0xFD, 0xFF) : QColor(0x00, 0x00, 0x00, 0xFF));
3530
3531 QImage image(windowWidth, titleBarHeight, QImage::Format_ARGB32);
3532 QPainter p(&image);
3533 p.setCompositionMode(QPainter::CompositionMode_Clear);
3534 p.fillRect(0, 0, windowWidth, titleBarHeight, Qt::transparent);
3535
3536 p.setCompositionMode(QPainter::CompositionMode_SourceOver);
3537
3538 p.setBrush(titleBarBackgroundColor);
3539 p.setPen(Qt::NoPen);
3540 if (!wnd->flags().testFlags(Qt::NoTitleBarBackgroundHint)) {
3541 QRect titleRect;
3542 titleRect.setWidth(windowWidth);
3543 titleRect.setHeight(titleBarHeight);
3544 p.drawRect(titleRect);
3545 }
3546
3547 if (wnd->flags().testFlags(Qt::WindowTitleHint | Qt::CustomizeWindowHint) || !wnd->flags().testFlag(Qt::CustomizeWindowHint)) {
3548 QRect titleRect;
3549 titleRect.setY(1);
3550 titleRect.setX(12);
3551 titleRect.setWidth(windowWidth);
3552 titleRect.setHeight(titleBarHeight);
3553
3554 titleRect.adjust(factor * 4, 0, 0, 0);
3555 QRect iconRect(titleRect.x(), titleRect.y() + factor * 8, factor * 16, factor * 16);
3556 if (wnd->icon().isNull()) {
3557 static QIcon defaultIcon;
3558 if (defaultIcon.isNull()) {
3559 const QImage defaultIconImage = QImage::fromHICON(LoadIcon(0, IDI_APPLICATION));
3560 defaultIcon = QIcon(QPixmap::fromImage(defaultIconImage));
3561 }
3562 defaultIcon.paint(&p, iconRect);
3563 } else {
3564 wnd->icon().paint(&p, iconRect);
3565 }
3566 titleRect.adjust(factor * 24, 0, 0, 0);
3567
3568 p.setPen(textPen);
3569 QFont titleFont = QWindowsIntegration::instance()->fontDatabase()->defaultFont();
3570 titleFont.setPointSize(factor * 9);
3571 titleFont.setWeight(QFont::Thin);
3572 titleFont.setHintingPreference(QFont::PreferFullHinting);
3573 p.setFont(titleFont);
3574 const QString title = wnd->title().isEmpty() ? qApp->applicationName() : wnd->title();
3575 p.drawText(titleRect, title, QTextOption(Qt::AlignVCenter));
3576 }
3577
3578 int buttons = 1;
3579 const QString assetFontName = isWindows11orAbove ? QStringLiteral("Segoe Fluent Icons") : QStringLiteral("Segoe MDL2 Assets");
3580 QFont assetFont = QFont(assetFontName, factor * 7);
3581 assetFont.setWeight(QFont::Thin);
3582 assetFont.setHintingPreference(QFont::PreferFullHinting);
3583 p.setFont(assetFont);
3584 p.setBrush(closeButtonBrush);
3585 p.setPen(Qt::NoPen);
3586 if (wnd->flags().testFlags(Qt::WindowCloseButtonHint | Qt::CustomizeWindowHint) || !wnd->flags().testFlag(Qt::CustomizeWindowHint)) {
3587 QRectF rect;
3588 rect.setY(1);
3589 rect.setX(windowWidth - titleButtonWidth * buttons);
3590 rect.setWidth(titleButtonWidth);
3591 rect.setHeight(titleBarHeight);
3592 if (localPos.x > (windowWidth - buttons * titleButtonWidth) &&
3593 localPos.x < (windowWidth - (buttons - 1) * titleButtonWidth) &&
3594 localPos.y > rect.y() && localPos.y < rect.y() + rect.height()) {
3595 p.drawRect(rect);
3596 const QPen closeButtonHoveredPen = QPen(QColor(0xFF, 0xFF, 0xFD, 0xFF));
3597 p.setPen(closeButtonHoveredPen);
3598 } else {
3599 p.setPen(textPen);
3600 }
3601 p.drawText(rect, QStringLiteral("\uE8BB"), QTextOption(Qt::AlignVCenter | Qt::AlignHCenter));
3602 buttons++;
3603 }
3604
3605 p.setBrush(minMaxButtonBrush);
3606 p.setPen(Qt::NoPen);
3607 if (wnd->flags().testFlags(Qt::WindowMaximizeButtonHint | Qt::CustomizeWindowHint) || !wnd->flags().testFlag(Qt::CustomizeWindowHint)) {
3608 QRectF rect;
3609 rect.setY(1);
3610 rect.setX(windowWidth - titleButtonWidth * buttons);
3611 rect.setWidth(titleButtonWidth);
3612 rect.setHeight(titleBarHeight);
3613 if (localPos.x > (windowWidth - buttons * titleButtonWidth) &&
3614 localPos.x < (windowWidth - (buttons - 1) * titleButtonWidth) &&
3615 localPos.y > rect.y() && localPos.y < rect.y() + rect.height()) {
3616 p.drawRect(rect);
3617 }
3618 p.setPen(textPen);
3619 p.drawText(rect,QStringLiteral("\uE922"), QTextOption(Qt::AlignVCenter | Qt::AlignHCenter));
3620 buttons++;
3621 }
3622
3623 p.setBrush(minMaxButtonBrush);
3624 p.setPen(Qt::NoPen);
3625 if (wnd->flags().testFlags(Qt::WindowMinimizeButtonHint | Qt::CustomizeWindowHint) || !wnd->flags().testFlag(Qt::CustomizeWindowHint)) {
3626 QRectF rect;
3627 rect.setY(1);
3628 rect.setX(windowWidth - titleButtonWidth * buttons);
3629 rect.setWidth(titleButtonWidth);
3630 rect.setHeight(titleBarHeight);
3631 if (localPos.x > (windowWidth - buttons * titleButtonWidth) &&
3632 localPos.x < (windowWidth - (buttons - 1) * titleButtonWidth) &&
3633 localPos.y > rect.y() && localPos.y < rect.y() + rect.height()) {
3634 p.drawRect(rect);
3635 }
3636 p.setPen(textPen);
3637 p.drawText(rect,QStringLiteral("\uE921"), QTextOption(Qt::AlignVCenter | Qt::AlignHCenter));
3638 buttons++;
3639 }
3640
3641 p.end();
3642
3643 HBITMAP bmp = image.toHBITMAP();
3644
3645 HDC hdc = GetDC(hwnd);
3646
3647 HDC memdc = CreateCompatibleDC(hdc);
3648 HGDIOBJ original = SelectObject(memdc, bmp);
3649
3650
3651 BLENDFUNCTION blend = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
3652 POINT ptLocation = { 0, 0 };
3653 SIZE szWnd = { windowWidth, titleBarHeight };
3654 POINT ptSrc = { 0, 0 };
3655 UpdateLayeredWindow(hwnd, hdc, &ptLocation, &szWnd, memdc, &ptSrc, 0, &blend, ULW_ALPHA);
3656 SelectObject(hdc, original);
3657
3658 DeleteObject(bmp);
3659 DeleteObject(memdc);
3660 ReleaseDC(hwnd,hdc);
3661}
3662
3663#ifndef QT_NO_CURSOR
3664// Return the default cursor (Arrow) from QWindowsCursor's cache.
3665static inline CursorHandlePtr defaultCursor(const QWindow *w)
3666{
3667 if (QScreen *screen = w->screen())
3668 if (const QPlatformScreen *platformScreen = screen->handle())
3669 if (QPlatformCursor *cursor = platformScreen->cursor())
3670 return static_cast<QWindowsCursor *>(cursor)->standardWindowCursor(Qt::ArrowCursor);
3671 return CursorHandlePtr(new CursorHandle(QWindowsCursor::createCursorFromShape(Qt::ArrowCursor)));
3672}
3673
3674// Check whether to apply a new cursor. Either the window in question is
3675// currently under mouse, or it is the parent of the window under mouse and
3676// there is no other window with an explicitly set cursor in-between.
3677static inline bool applyNewCursor(const QWindow *w)
3678{
3679 const QWindow *underMouse = QWindowsContext::instance()->windowUnderMouse();
3680 if (underMouse == w)
3681 return true;
3682 for (const QWindow *p = underMouse; p ; p = p->parent()) {
3683 if (p == w)
3684 return true;
3685 const QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(p);
3686 if (platformWindow && !platformWindow->cursor()->isNull())
3687 return false;
3688 }
3689 return false;
3690}
3691#endif // !QT_NO_CURSOR
3692
3693/*!
3694 \brief Applies to cursor property set on the window to the global cursor.
3695
3696 \sa QWindowsCursor
3697*/
3698
3700{
3702 if (isTopLevel())
3704 return;
3705 }
3706#ifndef QT_NO_CURSOR
3707 if (m_cursor->isNull()) { // Recurse up to parent with non-null cursor. Set default for toplevel.
3708 if (const QWindow *p = window()->parent()) {
3709 if (QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(p))
3710 platformWindow->applyCursor();
3711 } else {
3712 SetCursor(defaultCursor(window())->handle());
3713 }
3714 } else {
3715 SetCursor(m_cursor->handle());
3716 }
3717#endif
3718}
3719
3720void QWindowsWindow::setCursor(const CursorHandlePtr &c)
3721{
3722#ifndef QT_NO_CURSOR
3723 bool changed = c->handle() != m_cursor->handle();
3724 // QTBUG-98856: Cursors can get out of sync after restoring override
3725 // cursors on native windows. Force an update.
3728 changed = true;
3729 }
3730 if (changed) {
3731 const bool apply = applyNewCursor(window());
3732 qCDebug(lcQpaWindow) << window() << __FUNCTION__
3733 << c->handle() << " doApply=" << apply;
3734 m_cursor = c;
3735 if (apply)
3737 }
3738#endif
3739}
3740
3742{
3743 if (isAlertState() == enabled)
3744 return;
3745 if (enabled) {
3748 } else {
3751 }
3752}
3753
3754void QWindowsWindow::alertWindow(int durationMs)
3755{
3756 UINT timeOutMs = GetCaretBlinkTime();
3757 if (!timeOutMs || timeOutMs == INFINITE)
3758 timeOutMs = 250;
3759
3760 FLASHWINFO info;
3761 info.cbSize = sizeof(info);
3762 info.hwnd = m_data.hwnd;
3763 info.dwFlags = FLASHW_TRAY;
3764 info.dwTimeout = timeOutMs;
3765 info.uCount = durationMs == 0 ? 10 : UINT(durationMs) / timeOutMs;
3766 FlashWindowEx(&info);
3767}
3768
3770{
3771 FLASHWINFO info;
3772 info.cbSize = sizeof(info);
3773 info.hwnd = m_data.hwnd;
3774 info.dwFlags = FLASHW_STOP;
3775 info.dwTimeout = 0;
3776 info.uCount = 0;
3777 FlashWindowEx(&info);
3778}
3779
3781{
3782 return (style() & WS_DISABLED) == 0;
3783}
3784
3785void QWindowsWindow::setEnabled(bool enabled)
3786{
3787 const unsigned oldStyle = style();
3788 unsigned newStyle = oldStyle;
3789 if (enabled) {
3790 newStyle &= ~WS_DISABLED;
3791 } else {
3792 newStyle |= WS_DISABLED;
3793 }
3794 if (newStyle != oldStyle)
3795 setStyle(newStyle);
3796}
3797
3798static HICON createHIcon(const QIcon &icon, int xSize, int ySize)
3799{
3800 if (!icon.isNull()) {
3801 // QTBUG-90363, request DPR=1 for the title bar.
3802 const QPixmap pm = icon.pixmap(icon.actualSize(QSize(xSize, ySize)), 1);
3803 if (!pm.isNull())
3804 return qt_pixmapToWinHICON(pm);
3805 }
3806 return nullptr;
3807}
3808
3809void QWindowsWindow::setWindowIcon(const QIcon &icon)
3810{
3811 if (m_data.hwnd) {
3812 destroyIcon();
3813
3814 m_iconSmall = createHIcon(icon, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
3815 m_iconBig = createHIcon(icon, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON));
3816
3817 if (m_iconBig) {
3818 SendMessage(m_data.hwnd, WM_SETICON, 0 /* ICON_SMALL */, LPARAM(m_iconSmall));
3819 SendMessage(m_data.hwnd, WM_SETICON, 1 /* ICON_BIG */, LPARAM(m_iconBig));
3820 } else {
3821 SendMessage(m_data.hwnd, WM_SETICON, 0 /* ICON_SMALL */, LPARAM(m_iconSmall));
3822 SendMessage(m_data.hwnd, WM_SETICON, 1 /* ICON_BIG */, LPARAM(m_iconSmall));
3823 }
3824 }
3825}
3826
3828{
3829 return window()->isTopLevel() && !m_data.embedded;
3830}
3831
3832enum : WORD {
3835};
3836
3837bool QWindowsWindow::setDarkBorderToWindow(HWND hwnd, bool d)
3838{
3839 const BOOL darkBorder = d ? TRUE : FALSE;
3840 const bool ok =
3841 SUCCEEDED(DwmSetWindowAttribute(hwnd, DwmwaUseImmersiveDarkMode, &darkBorder, sizeof(darkBorder)))
3842 || SUCCEEDED(DwmSetWindowAttribute(hwnd, DwmwaUseImmersiveDarkModeBefore20h1, &darkBorder, sizeof(darkBorder)));
3843 if (!ok)
3844 qCWarning(lcQpaWindow, "%s: Unable to set %s window border.", __FUNCTION__, d ? "dark" : "light");
3845 return ok;
3846}
3847
3849{
3850 // respect explicit opt-out and incompatible palettes or styles
3851 d = d && shouldApplyDarkFrame(window());
3852
3853 setDarkBorderToWindow(m_data.hwnd, d);
3854}
3855
3857{
3858 return m_menuBar.data();
3859}
3860
3862{
3863 m_menuBar = mb;
3864}
3865
3867{
3868 if (m_data.flags & Qt::FramelessWindowHint)
3869 return {};
3870 return m_data.customMargins;
3871}
3872
3873/*!
3874 \brief Sets custom margins to be added to the default margins determined by
3875 the windows style in the handling of the WM_NCCALCSIZE message.
3876
3877 This is currently used to give the Aero-style QWizard a smaller top margin.
3878 The property can be set using QPlatformNativeInterface::setWindowProperty() or,
3879 before platform window creation, by setting a dynamic property
3880 on the QWindow (see QWindowsIntegration::createPlatformWindow()).
3881*/
3882
3883void QWindowsWindow::setCustomMargins(const QMargins &newCustomMargins)
3884{
3885 if (m_data.flags & Qt::FramelessWindowHint) {
3886 qCWarning(lcQpaWindow) << "You should not set custom margins for a frameless window.";
3887 return;
3888 }
3889 if (newCustomMargins != m_data.customMargins) {
3890 const QMargins oldCustomMargins = m_data.customMargins;
3891 m_data.customMargins = newCustomMargins;
3892 // Re-trigger WM_NCALCSIZE with wParam=1 by passing SWP_FRAMECHANGED
3893 const QRect currentFrameGeometry = frameGeometry_sys();
3894 const QPoint topLeft = currentFrameGeometry.topLeft();
3895 QRect newFrame = currentFrameGeometry.marginsRemoved(oldCustomMargins) + m_data.customMargins;
3896 newFrame.moveTo(topLeft);
3897 qCDebug(lcQpaWindow) << __FUNCTION__ << oldCustomMargins << "->" << newCustomMargins
3898 << currentFrameGeometry << "->" << newFrame;
3899 SetWindowPos(m_data.hwnd, nullptr, newFrame.x(), newFrame.y(), newFrame.width(), newFrame.height(), SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE);
3900 }
3901}
3902
3903void *QWindowsWindow::surface(void *nativeConfig, int *err)
3904{
3905#if QT_CONFIG(vulkan)
3906 Q_UNUSED(nativeConfig);
3907 Q_UNUSED(err);
3908 if (window()->surfaceType() == QSurface::VulkanSurface) {
3909 if (!m_vkSurface) {
3910 QVulkanInstance *inst = window()->vulkanInstance();
3911 if (inst)
3912 m_vkSurface = static_cast<QWindowsVulkanInstance *>(inst->handle())->createSurface(handle());
3913 else
3914 qWarning("Attempted to create Vulkan surface without an instance; was QWindow::setVulkanInstance() called?");
3915 }
3916 // Different semantics for VkSurfaces: the return value is the address,
3917 // not the value, given that this is a 64-bit handle even on x86.
3918 return &m_vkSurface;
3919 }
3920#elif defined(QT_NO_OPENGL)
3921 Q_UNUSED(err);
3922 Q_UNUSED(nativeConfig);
3923 return nullptr;
3924#endif
3925#ifndef QT_NO_OPENGL
3926 if (!m_surface) {
3928 m_surface = staticOpenGLContext->createWindowSurface(m_data.hwnd, nativeConfig, err);
3929 }
3930
3931 return m_surface;
3932#else
3933 return nullptr;
3934#endif
3935}
3936
3938{
3939#if QT_CONFIG(vulkan)
3940 if (m_vkSurface) {
3941 QVulkanInstance *inst = window()->vulkanInstance();
3942 if (inst)
3943 static_cast<QWindowsVulkanInstance *>(inst->handle())->destroySurface(m_vkSurface);
3944 m_vkSurface = VK_NULL_HANDLE;
3945 }
3946#endif
3947#ifndef QT_NO_OPENGL
3948 if (m_surface) {
3950 staticOpenGLContext->destroyWindowSurface(m_surface);
3951 m_surface = nullptr;
3952 }
3953#endif // QT_NO_OPENGL
3954}
3955
3957{
3959 return;
3960
3961 // Initially register or re-register to change the flags
3962 const auto touchTypes = QWindowsIntegration::instance()->touchWindowTouchType();
3964 const auto currentTouchTypes = touchWindowTouchTypes_sys();
3965 if (currentTouchTypes.has_value() && currentTouchTypes.value() == touchTypes)
3966 return;
3967 }
3968
3969 ULONG touchFlags = 0;
3970 if (touchTypes.testFlag(TouchWindowTouchType::FineTouch))
3971 touchFlags |= TWF_FINETOUCH;
3972 if (touchTypes.testFlag(TouchWindowTouchType::WantPalmTouch))
3973 touchFlags |= TWF_WANTPALM;
3974 if (RegisterTouchWindow(m_data.hwnd, touchFlags))
3976 else
3977 qErrnoWarning("RegisterTouchWindow() failed for window '%s'.", qPrintable(window()->objectName()));
3978}
3979
3980void QWindowsWindow::setHasBorderInFullScreenStatic(QWindow *window, bool border)
3981{
3982 if (QPlatformWindow *handle = window->handle())
3983 static_cast<QWindowsWindow *>(handle)->setHasBorderInFullScreen(border);
3984 else
3985 window->setProperty(hasBorderInFullScreenProperty, QVariant(border));
3986}
3987
3989{
3990 m_borderInFullScreenDefault = border;
3991}
3992
3997
3999{
4000 if (hasBorderInFullScreen() == border)
4001 return;
4002 if (border)
4004 else
4006 // Directly apply the flag in case we are fullscreen.
4007 if (m_windowState == Qt::WindowFullScreen) {
4008 LONG_PTR style = GetWindowLongPtr(handle(), GWL_STYLE);
4009 if (border)
4010 style |= WS_BORDER;
4011 else
4012 style &= ~WS_BORDER;
4013 SetWindowLongPtr(handle(), GWL_STYLE, style);
4014 }
4015}
4016
4017QString QWindowsWindow::formatWindowTitle(const QString &title)
4018{
4019 return QPlatformWindow::formatWindowTitle(title, QStringLiteral(" - "));
4020}
4021
4023{
4024 enum UpdateState {
4025 Ready = 0,
4026 Requested = 1,
4027 Posted = 2
4028 };
4029 QWindow *w = window();
4030 QDxgiVSyncService *vs = QDxgiVSyncService::instance();
4031 if (vs->supportsWindow(w)) {
4032 if (m_vsyncServiceCallbackId == 0) {
4033 m_vsyncServiceCallbackId = vs->registerCallback([this, w](const QDxgiVSyncService::CallbackWindowList &windowList, qint64) {
4034 if (windowList.contains(w)) {
4035 // Make sure we only post one event at a time. If the state
4036 // isn't Requested, it means there either isn't a pending
4037 // request or we are waiting for the event loop to process
4038 // the Posted event on the GUI thread.
4039 if (m_vsyncUpdatePending.testAndSetAcquire(UpdateState::Requested, UpdateState::Posted)) {
4040 QWindowsWindow *oldSelf = this;
4041 qsizetype oldCallbackId = m_vsyncServiceCallbackId;
4042 QMetaObject::invokeMethod(w, [w, oldSelf, oldCallbackId] {
4043 // 'oldSelf' is only used for comparison, don't access it directly!
4044 auto *self = static_cast<QWindowsWindow *>(w->handle());
4045 // NOTE: In the off chance that the window got destroyed and recreated with the
4046 // same address, we also check that the callback id is the same.
4047 if (self && self == oldSelf && self->m_vsyncServiceCallbackId == oldCallbackId) {
4048 // The platform window is still alive
4049 self->m_vsyncUpdatePending.storeRelease(UpdateState::Ready);
4050 self->deliverUpdateRequest();
4051 }
4052 });
4053 }
4054 }
4055 });
4056 }
4057 m_vsyncUpdatePending.testAndSetRelease(UpdateState::Ready, UpdateState::Requested);
4058 } else {
4059 QPlatformWindow::requestUpdate();
4060 }
4061}
4062
4063QT_END_NAMESPACE
\inmodule QtCore\reentrant
Definition qpoint.h:30
Base class for QWindowsForeignWindow, QWindowsWindow.
QMargins frameMargins_sys() const
bool isTopLevel_sys() const
void setHasBorderInFullScreen(bool border) override
QMargins customMargins() const override
bool windowEvent(QEvent *event) override
Reimplement this method to be able to do any platform specific event handling.
bool hasBorderInFullScreen() const override
bool hasMaximumWidth() const
unsigned style() const
std::optional< TouchWindowTouchTypes > touchWindowTouchTypes_sys() const
bool hasMaximumHeight() const
QRect frameGeometry_sys() const
QRect geometry_sys() const
void setCustomMargins(const QMargins &margins) override
void setGeometry_sys(const QRect &rect) const
bool hasMaximumSize() const
void setWindowTitle_sys(const QString &title)
Singleton container for all relevant information.
QWindowsScreenManager & screenManager()
unsigned systemInfo() const
static QWindowsContext * instance()
Platform cursor implementation.
static bool hasOverrideCursor()
static void enforceOverrideCursor()
Window wrapping a foreign native window.
void setVisible(bool visible) override
Reimplemented in subclasses to show the surface if visible is true, and hide it if visible is false.
static QWindowsStaticOpenGLContext * staticOpenGLContext()
static QWindowsIntegration * instance()
Windows native menu bar.
static QWindowsMenuBar * menuBarOf(const QWindow *notYetCreatedWindow)
Manages a list of QWindowsScreen.
const QWindowsScreen * screenAtDp(const QPoint &p) const
Windows screen.
virtual void * createWindowSurface(void *, void *, int *)
virtual void destroyWindowSurface(void *)
static QWindowsWindowClassRegistry * instance()
Raster or OpenGL Window.
void alertWindow(int durationMs=0)
void setWindowFlags(Qt::WindowFlags flags) override
Requests setting the window flags of this surface to flags.
void setCustomMargins(const QMargins &m) override
Sets custom margins to be added to the default margins determined by the windows style in the handlin...
void setMenuBar(QWindowsMenuBar *mb)
void invalidateSurface() override
Invalidates the window's surface by releasing its surface buffers.
HDC getDC()
Allocates a HDC for the window or returns the temporary one obtained from WinAPI BeginPaint within a ...
QMargins fullFrameMargins() const override
void checkForScreenChanged(ScreenChangeMode mode=FromGeometryChange, const RECT *suggestedRect=nullptr)
void initialize() override
Called as part of QWindow::create(), after constructing the window.
void handleDpiChangedAfterParent(HWND hwnd)
void requestUpdate() override
Requests an QEvent::UpdateRequest event.
bool testFlag(unsigned f) const
void setFlag(unsigned f) const
void clearFlag(unsigned f) const
void setFrameStrutEventsEnabled(bool enabled) override
Reimplement this method to set whether frame strut events should be sent to enabled.
static void settingsChanged()
void getSizeHints(MINMAXINFO *mmi) const
static void setHasBorderInFullScreenDefault(bool border)
~QWindowsWindow() override
bool handleWmPaint(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result)
bool setMouseGrabEnabled(bool grab) override
void propagateSizeHints() override
Reimplement to propagate the size hints of the QWindow.
void applyCursor()
Applies to cursor property set on the window to the global cursor.
bool handleGeometryChanging(MSG *message) const
bool isActive() const override
Returns true if the window should appear active from a style perspective.
void setStyle(unsigned s) const
QString windowTitle() const override
Reimplement to return the actual window title used in the underlying windowing system unless the titl...
static void displayChanged()
bool setKeyboardGrabEnabled(bool grab) override
bool windowEvent(QEvent *event) override
Reimplement this method to be able to do any platform specific event handling.
void setGeometry(const QRect &rect) override
This function is called by Qt whenever a window is moved or resized using the QWindow API.
void setEnabled(bool enabled)
void setVisible(bool visible) override
Reimplemented in subclasses to show the surface if visible is true, and hide it if visible is false.
QSurfaceFormat format() const override
Returns the actual surface format of the window.
static const char * embeddedNativeParentHandleProperty
QWindowsMenuBar * menuBar() const
bool isLayered() const
void handleDpiChanged(HWND hwnd, WPARAM wParam, LPARAM lParam)
QMargins frameMargins() const override
void setWindowIcon(const QIcon &icon) override
Reimplement to set the window icon to icon.
void handleResized(int wParam, LPARAM lParam)
void setOpacity(qreal level) override
Reimplement to be able to let Qt set the opacity level of a window.
bool isEmbedded() const override
Returns true if the window is a child of a non-Qt window.
QMargins customMargins() const override
bool handleNonClientActivate(LRESULT *result) const
bool isEnabled() const
QRect normalGeometry() const override
Returns the geometry of a window in 'normal' state (neither maximized, fullscreen nor minimized) for ...
void setExStyle(unsigned s) const
qreal dpiRelativeScale(const UINT dpi) const
QMargins safeAreaMargins() const override
The safe area margins of a window represent the area that is safe to place content within,...
void setSavedDpi(int dpi)
void requestActivateWindow() override
Reimplement to let Qt be able to request activation/focus for a window.
void handleDpiScaledSize(WPARAM wParam, LPARAM lParam, LRESULT *result)
@ WithinSetParent
Automatic mouse capture on button press.
static const char * hasBorderInFullScreenProperty
bool startSystemMove() override
Reimplement this method to start a system move operation if the system supports it and return true to...
bool isTopLevel() const override
void releaseDC()
Releases the HDC for the window or does nothing in case it was obtained from WinAPI BeginPaint within...
void setWindowState(Qt::WindowStates state) override
Requests setting the window state of this surface to type.
void setCursor(const CursorHandlePtr &c)
bool hasBorderInFullScreen() const override
bool hasMouseCapture() const
void * surface(void *nativeConfig, int *err)
void setHasBorderInFullScreen(bool border) override
bool isVisible() const
void handleCompositionSettingsChanged()
void setWindowTitle(const QString &title) override
Reimplement to set the window title to title.
void setDarkBorder(bool d)
bool startSystemResize(Qt::Edges edges) override
Reimplement this method to start a system resize operation if the system supports it and return true ...
void setFullFrameMargins(const QMargins &newMargins)
void setMask(const QRegion &region) override
Reimplement to be able to let Qt set the mask of a window.
void setAlertState(bool enabled) override
Reimplement this method to set whether the window demands attention (for example, by flashing the tas...
bool isAlertState() const override
Reimplement this method return whether the window is in an alert state.
Combined button and popup list for selecting options.
@ defaultWindowHeight
Definition qioswindow.mm:35
@ defaultWindowWidth
Definition qioswindow.mm:34
static QSize toNativeSizeConstrained(QSize dip, const QScreen *s)
static void fixTopLevelWindowFlags(Qt::WindowFlags &flags)
static QString msgUnableToSetGeometry(const QWindowsWindow *platformWindow, const QRect &requestedRect, const QRect &obtainedRect, const QMargins &fullMargins, const QMargins &customMargins)
static bool shouldOmitFrameAdjustment(const Qt::WindowFlags flags, DWORD style)
static QRect normalFrameGeometry(HWND hwnd)
static void formatBriefRectangle(QDebug &d, const QRect &r)
static bool testShowWithoutActivating(const QWindow *window)
static bool shouldShowMaximizeButton(const QWindow *w, Qt::WindowFlags flags)
static void setMinimizedGeometry(HWND hwnd, const QRect &r)
static bool applyNewCursor(const QWindow *w)
static bool isSoftwareGl()
static HICON createHIcon(const QIcon &icon, int xSize, int ySize)
static void addRectToWinRegion(const QRect &rect, HRGN *winRegion)
static QByteArray debugWinSwpPos(UINT flags)
static HRGN createRectRegion(const QRect &r)
static QByteArray debugWinStyle(DWORD style)
static QScreen * screenForDeviceName(const QWindow *w, const QString &name)
static QPoint windowPlacementOffset(HWND hwnd, const QPoint &point)
static bool applyBlurBehindWindow(HWND hwnd)
static void setRestoreMaximizedFlag(HWND hwnd, bool set=true)
static RECT RECTfromQRect(const QRect &rect)
static QMargins invisibleMargins(const HWND hwnd)
static QRect frameGeometry(HWND hwnd, bool topLevel)
static void setWindowOpacity(HWND hwnd, Qt::WindowFlags flags, bool hasAlpha, bool accelerated, qreal level)
static QMargins invisibleMargins(QPoint screenPoint)
Calculates the dimensions of the invisible borders within the window frames which only exist on Windo...
static int getResizeBorderThickness(const UINT dpi)
static bool windowIsAccelerated(const QWindow *w)
static bool equalDpi(const QDpi &d1, const QDpi &d2)
static bool isResize(const WINDOWPOS *windowPos)
static void formatBriefMargins(QDebug &d, const QMargins &m)
static int getTitleBarHeight_sys(const UINT dpi)
static QByteArray debugWindowPlacementFlags(const UINT flags)
static QSize qSizeOfRect(const RECT &rect)
static DWORD edgesToWinOrientation(Qt::Edges edges)
static bool shouldApplyDarkFrame(const QWindow *w)
static QByteArray debugWinExStyle(DWORD exStyle)
static CursorHandlePtr defaultCursor(const QWindow *w)
static QByteArray debugShowWindowCmd(const UINT cmd)
#define GWL_HWNDPARENT
static HRGN qRegionToWinRegion(const QRegion &region)
static QPoint calcPosition(const QWindow *w, const QWindowCreationContextPtr &context, const QMargins &invMargins)
@ DwmwaUseImmersiveDarkMode
@ DwmwaUseImmersiveDarkModeBefore20h1
static QRect qrectFromRECT(const RECT &rect)
static QWindow::Visibility windowVisibility_sys(HWND hwnd)
static GpuDescription detect()
Active Context for creating windows.
void applyToMinMaxInfo(MINMAXINFO *mmi) const
Stores geometry constraints and provides utility functions.
Window creation code.
void applyWindowFlags(HWND hwnd) const
void initialize(const QWindow *w, HWND h, bool frameChange, qreal opacityLevel) const
QWindowsWindowData WindowData
WindowData create(const QWindow *w, const WindowData &data, QString title) const
void fromWindow(const QWindow *w, const Qt::WindowFlags flags, unsigned creationFlags=0)
Qt::WindowFlags flags