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 m_data = setWindowFlags_sys(window()->flags(), unsigned(isTopLevel ? WindowCreationData::ForceTopLevel : WindowCreationData::ForceChild));
2075 // Update frame margins for the new top-level state.
2076 // Child windows have no frame/titlebar, so margins must be zero.
2077 // Top-level windows need frame margins recalculated.
2078 if (isTopLevel)
2080 else
2081 m_data.fullFrameMargins = {};
2082 updateDropSite(isTopLevel);
2083 }
2084 }
2085}
2086
2088{
2089 fireExpose(QRegion());
2090}
2091
2093{
2094 const QWindow *w = window();
2095 if (windowIsAccelerated(w) && w->format().hasAlpha())
2096 applyBlurBehindWindow(handle());
2097}
2098
2100{
2101 return QHighDpiScaling::roundScaleFactor(qreal(dpi) / QWindowsScreen::baseDpi) /
2102 QHighDpiScaling::roundScaleFactor(qreal(savedDpi()) / QWindowsScreen::baseDpi);
2103}
2104
2105void QWindowsWindow::handleDpiScaledSize(WPARAM wParam, LPARAM lParam, LRESULT *result)
2106{
2107 // We want to keep QWindow's device independent size constant across the
2108 // DPI change. To accomplish this, scale QPlatformWindow's native size
2109 // by the change of DPI (e.g. 120 -> 144 = 1.2), also taking any scale
2110 // factor rounding into account. The win32 window size includes the margins;
2111 // add the margins for the new DPI to the window size.
2112 const UINT dpi = UINT(wParam);
2113 const qreal scale = dpiRelativeScale(dpi);
2114 const QMargins margins = fullFrameMargins();
2115 if (!(m_data.flags & Qt::FramelessWindowHint)) {
2116 // We need to update the custom margins to match the current DPI, because
2117 // we don't want our users manually hook into this message just to set a
2118 // new margin, but here we can't call setCustomMargins() directly, that
2119 // function will change the window geometry which conflicts with what we
2120 // are currently doing.
2121 m_data.customMargins *= scale;
2122 }
2123
2124 const QSize windowSize = (geometry().size() * scale).grownBy((margins * scale) + customMargins());
2125 SIZE *size = reinterpret_cast<SIZE *>(lParam);
2126 size->cx = windowSize.width();
2127 size->cy = windowSize.height();
2128 *result = true; // Inform Windows that we've set a size
2129}
2130
2131void QWindowsWindow::handleDpiChanged(HWND hwnd, WPARAM wParam, LPARAM lParam)
2132{
2133 const UINT dpi = HIWORD(wParam);
2134 const qreal scale = dpiRelativeScale(dpi);
2135 setSavedDpi(dpi);
2136
2137 QWindowsThemeCache::clearThemeCache(hwnd);
2138
2139 // Send screen change first, so that the new screen is set during any following resize
2140 const auto prcNewWindow = reinterpret_cast<const RECT *>(lParam);
2141 checkForScreenChanged(QWindowsWindow::FromDpiChange, !m_inSetgeometry ? prcNewWindow : nullptr);
2142
2143 if (!IsZoomed(hwnd))
2144 m_data.restoreGeometry.setSize(m_data.restoreGeometry.size() * scale);
2145
2146 // We get WM_DPICHANGED in one of two situations:
2147 //
2148 // 1. The DPI change is a "spontaneous" DPI change as a result of e.g.
2149 // the user dragging the window to a new screen. In this case Windows
2150 // first sends WM_GETDPISCALEDSIZE, where we set the new window size,
2151 // followed by this event where we apply the suggested window geometry
2152 // to the native window. This will make sure the window tracks the mouse
2153 // cursor during screen change, and also that the window size is scaled
2154 // according to the DPI change.
2155 //
2156 // 2. The DPI change is a result of a setGeometry() call. In this case
2157 // Qt has already scaled the window size for the new DPI. Further, Windows
2158 // does not call WM_GETDPISCALEDSIZE, and also applies its own scaling
2159 // to the already scaled window size. Since there is no need to set the
2160 // window geometry again, and the provided geometry is incorrect, we omit
2161 // making the SetWindowPos() call.
2162 if (!m_inSetgeometry) {
2164 SetWindowPos(hwnd, nullptr, prcNewWindow->left, prcNewWindow->top,
2165 prcNewWindow->right - prcNewWindow->left,
2166 prcNewWindow->bottom - prcNewWindow->top, SWP_NOZORDER | SWP_NOACTIVATE);
2167 // If the window does not have a frame, WM_MOVE and WM_SIZE won't be
2168 // called which prevents the content from being scaled appropriately
2169 // after a DPI change.
2170 if (shouldOmitFrameAdjustment(m_data.flags, m_data.hwnd))
2171 handleGeometryChange();
2172 }
2173
2174 // Re-apply mask now that we have a new DPI, which have resulted in
2175 // a new scale factor.
2176 setMask(QHighDpi::toNativeLocalRegion(window()->mask(), window()));
2177}
2178
2180{
2181 const UINT dpi = GetDpiForWindow(hwnd);
2182 const qreal scale = dpiRelativeScale(dpi);
2183 setSavedDpi(dpi);
2184
2185 checkForScreenChanged(QWindowsWindow::FromDpiChange);
2186
2187 // Child windows do not get WM_GETDPISCALEDSIZE messages to inform
2188 // Windows about the new size, so we need to manually scale them.
2189 QRect currentGeometry = geometry();
2190 QRect scaledGeometry = QRect(currentGeometry.topLeft() * scale, currentGeometry.size() * scale);
2191 setGeometry(scaledGeometry);
2192}
2193
2195{
2196 WINDOWPLACEMENT wp;
2197 wp.length = sizeof(WINDOWPLACEMENT);
2198 if (GetWindowPlacement(hwnd, &wp)) {
2199 const QRect result = qrectFromRECT(wp.rcNormalPosition);
2200 return result.translated(windowPlacementOffset(hwnd, result.topLeft()));
2201 }
2202 return QRect();
2203}
2204
2206{
2207 // Check for fake 'fullscreen' mode.
2208 const bool fakeFullScreen =
2209 m_savedFrameGeometry.isValid() && (window()->windowStates() & Qt::WindowFullScreen);
2210 const QRect frame = fakeFullScreen ? m_savedFrameGeometry : normalFrameGeometry(m_data.hwnd);
2211 const QMargins margins = fakeFullScreen
2212 ? QWindowsGeometryHint::frame(window(), handle(), m_savedStyle, 0)
2213 : fullFrameMargins();
2214 return frame.isValid() ? frame.marginsRemoved(margins) : frame;
2215}
2216
2218{
2219 if (m_data.flags.testFlags(Qt::ExpandedClientAreaHint)) {
2220 const int titleBarHeight = getTitleBarHeight_sys(96);
2221
2222 return QMargins(0, titleBarHeight, 0, 0);
2223 }
2224 return QMargins();
2225}
2226
2227static QString msgUnableToSetGeometry(const QWindowsWindow *platformWindow,
2228 const QRect &requestedRect,
2229 const QRect &obtainedRect,
2230 const QMargins &fullMargins,
2231 const QMargins &customMargins)
2232{
2233 QString result;
2234 QDebug debug(&result);
2235 debug.nospace();
2236 debug.noquote();
2237 const auto window = platformWindow->window();
2238 debug << "Unable to set geometry ";
2239 formatBriefRectangle(debug, requestedRect);
2240 debug << " (frame: ";
2241 formatBriefRectangle(debug, requestedRect + fullMargins);
2242 debug << ") on " << window->metaObject()->className() << "/\""
2243 << window->objectName() << "\" on \"" << window->screen()->name()
2244 << "\". Resulting geometry: ";
2245 formatBriefRectangle(debug, obtainedRect);
2246 debug << " (frame: ";
2247 formatBriefRectangle(debug, obtainedRect + fullMargins);
2248 debug << ") margins: ";
2249 formatBriefMargins(debug, fullMargins);
2250 if (!customMargins.isNull()) {
2251 debug << " custom margin: ";
2252 formatBriefMargins(debug, customMargins);
2253 }
2254 const auto minimumSize = window->minimumSize();
2255 const bool hasMinimumSize = !minimumSize.isEmpty();
2256 if (hasMinimumSize)
2257 debug << " minimum size: " << minimumSize.width() << 'x' << minimumSize.height();
2258 const auto maximumSize = window->maximumSize();
2259 const bool hasMaximumSize = maximumSize.width() != QWINDOWSIZE_MAX || maximumSize.height() != QWINDOWSIZE_MAX;
2260 if (hasMaximumSize)
2261 debug << " maximum size: " << maximumSize.width() << 'x' << maximumSize.height();
2262 if (hasMinimumSize || hasMaximumSize) {
2263 MINMAXINFO minmaxInfo;
2264 memset(&minmaxInfo, 0, sizeof(minmaxInfo));
2265 platformWindow->getSizeHints(&minmaxInfo);
2266 debug << ' ' << minmaxInfo;
2267 }
2268 debug << ')';
2269 return result;
2270}
2271
2272void QWindowsWindow::setGeometry(const QRect &rectIn)
2273{
2274 QScopedValueRollback b(m_inSetgeometry, true);
2275
2276 QRect rect = rectIn;
2277 // This means it is a call from QWindow::setFramePosition() and
2278 // the coordinates include the frame (size is still the contents rectangle).
2279 if (QWindowsGeometryHint::positionIncludesFrame(window())) {
2280 const QMargins margins = frameMargins();
2281 rect.moveTopLeft(rect.topLeft() + QPoint(margins.left(), margins.top()));
2282 }
2283 if (m_windowState & Qt::WindowMinimized)
2284 m_data.geometry = rect; // Otherwise set by handleGeometryChange() triggered by event.
2285 if (m_data.hwnd) {
2286 // A ResizeEvent with resulting geometry will be sent. If we cannot
2287 // achieve that size (for example, window title minimal constraint),
2288 // notify and warn.
2290 setGeometry_sys(rect);
2292 if (m_data.geometry != rect && (isVisible() || QLibraryInfo::isDebugBuild())) {
2293 const auto warning =
2294 msgUnableToSetGeometry(this, rectIn, m_data.geometry,
2295 fullFrameMargins(), customMargins());
2296 qWarning("%s: %s", __FUNCTION__, qPrintable(warning));
2297 }
2298 } else {
2299 QPlatformWindow::setGeometry(rect);
2300 }
2301}
2302
2304{
2305 // Minimize/Set parent can send nonsensical move events.
2306 if (!IsIconic(m_data.hwnd) && !testFlag(WithinSetParent))
2307 handleGeometryChange();
2308}
2309
2310void QWindowsWindow::handleResized(int wParam, LPARAM lParam)
2311{
2312 /* Prevents borderless windows from covering the taskbar when maximized. */
2313 if ((m_data.flags.testFlag(Qt::FramelessWindowHint)
2314 || (m_data.flags.testFlag(Qt::CustomizeWindowHint) && !m_data.flags.testFlag(Qt::WindowTitleHint)))
2315 && IsZoomed(m_data.hwnd)) {
2316 const int resizedWidth = LOWORD(lParam);
2317 const int resizedHeight = HIWORD(lParam);
2318
2319 const HMONITOR monitor = MonitorFromWindow(m_data.hwnd, MONITOR_DEFAULTTOPRIMARY);
2320 MONITORINFO monitorInfo = {};
2321 monitorInfo.cbSize = sizeof(MONITORINFO);
2322 GetMonitorInfoW(monitor, &monitorInfo);
2323
2324 int correctLeft = monitorInfo.rcMonitor.left;
2325 int correctTop = monitorInfo.rcMonitor.top;
2326 int correctWidth = monitorInfo.rcWork.right - monitorInfo.rcWork.left;
2327 int correctHeight = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top;
2328
2329 if (!m_data.flags.testFlag(Qt::FramelessWindowHint)) {
2330 const int borderWidth = invisibleMargins(m_data.hwnd).left();
2331 correctLeft -= borderWidth;
2332 correctTop -= borderWidth;
2333 correctWidth += borderWidth * 2;
2334 correctHeight += borderWidth * 2;
2335 }
2336
2337 if (resizedWidth != correctWidth || resizedHeight != correctHeight) {
2338 qCDebug(lcQpaWindow) << __FUNCTION__ << "correcting: " << resizedWidth << "x"
2339 << resizedHeight << " -> " << correctWidth << "x" << correctHeight;
2340 SetWindowPos(m_data.hwnd, nullptr, correctLeft, correctTop, correctWidth, correctHeight,
2341 SWP_NOZORDER | SWP_NOACTIVATE);
2342 }
2343 }
2344
2345 switch (wParam) {
2346 case SIZE_MAXHIDE: // Some other window affected.
2347 case SIZE_MAXSHOW:
2348 return;
2349 case SIZE_MINIMIZED: // QTBUG-53577, prevent state change events during programmatic state change
2350 if (!testFlag(WithinSetStyle) && !testFlag(WithinSetGeometry))
2351 handleWindowStateChange(m_windowState | Qt::WindowMinimized);
2352 return;
2353 case SIZE_MAXIMIZED:
2354 handleGeometryChange();
2355 updateRestoreGeometry(); // we might be in ResizeMoveActive mode but changed the screen
2356 if (!testFlag(WithinSetStyle) && !testFlag(WithinSetGeometry))
2357 handleWindowStateChange(Qt::WindowMaximized | (isFullScreen_sys() ? Qt::WindowFullScreen
2358 : Qt::WindowNoState));
2359 break;
2360 case SIZE_RESTORED:
2361 handleGeometryChange();
2363 if (isFullScreen_sys())
2364 handleWindowStateChange(
2365 Qt::WindowFullScreen
2366 | (testFlag(MaximizeToFullScreen) ? Qt::WindowMaximized : Qt::WindowNoState));
2367 else if (m_windowState != Qt::WindowNoState && !testFlag(MaximizeToFullScreen))
2368 handleWindowStateChange(Qt::WindowNoState);
2369 }
2370 break;
2371 }
2372}
2373
2374static inline bool equalDpi(const QDpi &d1, const QDpi &d2)
2375{
2376 return qFuzzyCompare(d1.first, d2.first) && qFuzzyCompare(d1.second, d2.second);
2377}
2378
2379void QWindowsWindow::checkForScreenChanged(ScreenChangeMode mode, const RECT *suggestedRect)
2380{
2381 if ((parent() && !parent()->isForeignWindow()) || QWindowsScreenManager::isSingleScreen())
2382 return;
2383
2384 QPlatformScreen *currentScreen = screen();
2385 auto topLevel = isTopLevel_sys() ? m_data.hwnd : GetAncestor(m_data.hwnd, GA_ROOT);
2386 const QWindowsScreen *newScreen = suggestedRect ?
2387 QWindowsContext::instance()->screenManager().screenForRect(suggestedRect) :
2388 QWindowsContext::instance()->screenManager().screenForHwnd(topLevel);
2389
2390 if (newScreen == nullptr || newScreen == currentScreen)
2391 return;
2392 // For screens with different DPI: postpone until WM_DPICHANGE
2393 // Check on currentScreen as it can be 0 when resuming a session (QTBUG-80436).
2394 const bool changingDpi = !equalDpi(QDpi(savedDpi(), savedDpi()), newScreen->logicalDpi());
2395 if (mode == FromGeometryChange && currentScreen != nullptr && changingDpi)
2396 return;
2397
2398 qCDebug(lcQpaWindow).noquote().nospace() << __FUNCTION__
2399 << ' ' << window() << " \"" << (currentScreen ? currentScreen->name() : QString())
2400 << "\"->\"" << newScreen->name() << '"';
2402 QWindowSystemInterface::handleWindowScreenChanged<QWindowSystemInterface::SynchronousDelivery>(window(), newScreen->screen());
2403}
2404
2405void QWindowsWindow::handleGeometryChange()
2406{
2407 const QRect previousGeometry = m_data.geometry;
2409 m_data.geometry = geometry_sys();
2410 // Avoid superfluous geometry change events, possibly triggered by window
2411 // customization frameworks.
2412 if (m_data.geometry == previousGeometry)
2413 return;
2414 QWindowSystemInterface::handleGeometryChange(window(), m_data.geometry);
2415 // QTBUG-32121: OpenGL/normal windows (with exception of ANGLE
2416 // which we no longer support in Qt 6) do not receive expose
2417 // events when shrinking, synthesize.
2418 if (isExposed()
2419 && m_data.geometry.size() != previousGeometry.size() // Exclude plain move
2420 // One dimension grew -> Windows will send expose, no need to synthesize.
2421 && !(m_data.geometry.width() > previousGeometry.width() || m_data.geometry.height() > previousGeometry.height())) {
2422 fireFullExpose(true);
2423 }
2424
2425 const bool wasSync = testFlag(SynchronousGeometryChangeEvent);
2426 checkForScreenChanged();
2427
2428 if (testFlag(SynchronousGeometryChangeEvent))
2429 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
2430
2433
2434 if (!wasSync)
2436 qCDebug(lcQpaEvents) << __FUNCTION__ << this << window() << m_data.geometry;
2437
2438 if (m_data.flags & Qt::ExpandedClientAreaHint) {
2439 const int titleBarHeight = getTitleBarHeight_sys(savedDpi());
2440 MoveWindow(m_data.hwndTitlebar, 0, 0, m_data.geometry.width(), titleBarHeight, true);
2441 }
2442}
2443
2444void QWindowsBaseWindow::setGeometry_sys(const QRect &rect) const
2445{
2446 const QMargins margins = fullFrameMargins();
2447 const QRect frameGeometry = rect + margins;
2448
2449 qCDebug(lcQpaWindow) << '>' << __FUNCTION__ << window()
2450 << "\n from " << geometry_sys() << " frame: "
2451 << margins << " to " <<rect
2452 << " new frame: " << frameGeometry;
2453
2454 bool result = false;
2455 const HWND hwnd = handle();
2456 WINDOWPLACEMENT windowPlacement;
2457 windowPlacement.length = sizeof(WINDOWPLACEMENT);
2458 GetWindowPlacement(hwnd, &windowPlacement);
2459 // If the window is hidden and in maximized state or minimized, instead of moving the
2460 // window, set the normal position of the window.
2461 if ((windowPlacement.showCmd == SW_MAXIMIZE && !IsWindowVisible(hwnd))
2462 || windowPlacement.showCmd == SW_SHOWMINIMIZED) {
2463 windowPlacement.rcNormalPosition =
2464 RECTfromQRect(frameGeometry.translated(-windowPlacementOffset(hwnd, frameGeometry.topLeft())));
2465 windowPlacement.showCmd = windowPlacement.showCmd == SW_SHOWMINIMIZED ? SW_SHOWMINIMIZED : SW_HIDE;
2466 result = SetWindowPlacement(hwnd, &windowPlacement);
2467 } else {
2468 int x = frameGeometry.x();
2469 if (!window()->isTopLevel()) {
2470 const HWND parentHandle = GetParent(hwnd);
2471 if (isRtlLayout(parentHandle)) {
2472 RECT rect;
2473 GetClientRect(parentHandle, &rect);
2474 x = rect.right - frameGeometry.width() - x;
2475 }
2476 }
2477 result = MoveWindow(hwnd, x, frameGeometry.y(),
2478 frameGeometry.width(), frameGeometry.height(), true);
2479 }
2480 qCDebug(lcQpaWindow) << '<' << __FUNCTION__ << window()
2481 << "\n resulting " << result << geometry_sys();
2482}
2483
2484/*!
2485 Allocates a HDC for the window or returns the temporary one
2486 obtained from WinAPI BeginPaint within a WM_PAINT event.
2487
2488 \sa releaseDC()
2489*/
2490
2492{
2493 if (!m_hdc) {
2494 m_hdc = GetDC(handle());
2495 if (QGuiApplication::layoutDirection() == Qt::RightToLeft)
2496 SetLayout(m_hdc, 0); // Clear RTL layout
2497 }
2498 return m_hdc;
2499}
2500
2501/*!
2502 Releases the HDC for the window or does nothing in
2503 case it was obtained from WinAPI BeginPaint within a WM_PAINT event.
2504
2505 \sa getDC()
2506*/
2507
2509{
2510 if (m_hdc) {
2511 ReleaseDC(handle(), m_hdc);
2512 m_hdc = nullptr;
2513 }
2514}
2515
2516static inline bool isSoftwareGl()
2517{
2518#if QT_CONFIG(dynamicgl)
2519 return QOpenGLStaticContext::opengl32.moduleIsNotOpengl32()
2520 && QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL;
2521#else
2522 return false;
2523#endif // dynamicgl
2524}
2525
2526bool QWindowsWindow::handleWmPaint(HWND hwnd, UINT message,
2527 WPARAM, LPARAM, LRESULT *result)
2528{
2529 if (message == WM_ERASEBKGND) { // Backing store - ignored.
2530 *result = 1;
2531 return true;
2532 }
2533 // QTBUG-75455: Suppress WM_PAINT sent to invisible windows when setting WS_EX_LAYERED
2534 if (!window()->isVisible() && (GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED) != 0)
2535 return false;
2536 // Ignore invalid update bounding rectangles
2537 if (!GetUpdateRect(m_data.hwnd, 0, FALSE))
2538 return false;
2539 PAINTSTRUCT ps;
2540
2541 // GL software rendering (QTBUG-58178) with some AMD cards
2542 // (QTBUG-60527) need InvalidateRect() to suppress artifacts while resizing.
2544 InvalidateRect(hwnd, nullptr, false);
2545
2546 BeginPaint(hwnd, &ps);
2547
2548 // If the a window is obscured by another window (such as a child window)
2549 // we still need to send isExposed=true, for compatibility.
2550 // Our tests depend on it.
2551 fireExpose(QRegion(qrectFromRECT(ps.rcPaint)), true);
2552 if (!QWindowsContext::instance()->asyncExpose())
2553 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
2554
2555 EndPaint(hwnd, &ps);
2556 return true;
2557}
2558
2559void QWindowsWindow::setWindowTitle(const QString &title)
2560{
2561 m_windowTitle = QWindowsWindow::formatWindowTitle(title);
2562 setWindowTitle_sys(m_windowTitle);
2563}
2564
2566{
2567 return m_windowTitle;
2568}
2569
2570void QWindowsWindow::setWindowFlags(Qt::WindowFlags flags)
2571{
2572 qCDebug(lcQpaWindow) << '>' << __FUNCTION__ << this << window() << "\n from: "
2573 << m_data.flags << "\n to: " << flags;
2574 const QRect oldGeometry = geometry();
2575 if (m_data.flags != flags) {
2576 m_data.flags = flags;
2577 if (m_data.hwnd) {
2578 m_data = setWindowFlags_sys(flags);
2579 updateDropSite(window()->isTopLevel());
2580 }
2581 }
2582 // When switching to a frameless window, geometry
2583 // may change without a WM_MOVE. Report change manually.
2584 // Do not send synchronously as not to clobber the widget
2585 // geometry in a sequence of setting flags and geometry.
2586 const QRect newGeometry = geometry_sys();
2587 if (oldGeometry != newGeometry)
2588 handleGeometryChange();
2589
2590 qCDebug(lcQpaWindow) << '<' << __FUNCTION__ << "\n returns: "
2591 << m_data.flags << " geometry " << oldGeometry << "->" << newGeometry;
2592}
2593
2594QWindowsWindowData QWindowsWindow::setWindowFlags_sys(Qt::WindowFlags wt,
2595 unsigned flags) const
2596{
2597 WindowCreationData creationData;
2598 creationData.fromWindow(window(), wt, flags);
2599 creationData.applyWindowFlags(m_data.hwnd);
2600 creationData.initialize(window(), m_data.hwnd, true, m_opacity);
2601
2602 if (creationData.flags.testFlag(Qt::ExpandedClientAreaHint)) {
2603 SetParent(m_data.hwndTitlebar, m_data.hwnd);
2604 ShowWindow(m_data.hwndTitlebar, SW_SHOW);
2605 } else {
2606 if (IsWindowVisible(m_data.hwndTitlebar)) {
2607 SetParent(m_data.hwndTitlebar, HWND_MESSAGE);
2608 ShowWindow(m_data.hwndTitlebar, SW_HIDE);
2609 }
2610 }
2611
2612 QWindowsWindowData result = m_data;
2613 result.flags = creationData.flags;
2614 result.embedded = creationData.embedded;
2615 result.hasFrame = (creationData.style & (WS_DLGFRAME | WS_THICKFRAME))
2616 && !(creationData.flags & Qt::FramelessWindowHint);
2617 return result;
2618}
2619
2620void QWindowsWindow::handleWindowStateChange(Qt::WindowStates state)
2621{
2622 qCDebug(lcQpaWindow) << __FUNCTION__ << this << window()
2623 << "\n from " << m_windowState << " to " << state;
2624 m_windowState = state;
2625 QWindowSystemInterface::handleWindowStateChanged(window(), state);
2626 if (state & Qt::WindowMinimized) {
2628 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents); // Tell QQuickWindow to stop rendering now.
2629 } else {
2631 if (state & Qt::WindowMaximized) {
2632 WINDOWPLACEMENT windowPlacement{};
2633 windowPlacement.length = sizeof(WINDOWPLACEMENT);
2634 GetWindowPlacement(m_data.hwnd, &windowPlacement);
2635 const RECT geometry = RECTfromQRect(m_data.restoreGeometry);
2636 windowPlacement.rcNormalPosition = geometry;
2637 correctWindowPlacement(windowPlacement);
2638
2639 // Even if the window is hidden, windowPlacement's showCmd is not SW_HIDE, so change it
2640 // manually to avoid unhiding a hidden window with the subsequent call to
2641 // SetWindowPlacement().
2642 if (!isVisible())
2643 windowPlacement.showCmd = SW_HIDE;
2644 SetWindowPlacement(m_data.hwnd, &windowPlacement);
2645 }
2646 // QTBUG-17548: We send expose events when receiving WM_Paint, but for
2647 // layered windows and transient children, we won't receive any WM_Paint.
2648 QWindow *w = window();
2649 bool exposeEventsSent = false;
2650 if (isLayered()) {
2651 fireFullExpose();
2652 exposeEventsSent = true;
2653 }
2654 const QWindowList allWindows = QGuiApplication::allWindows();
2655 for (QWindow *child : allWindows) {
2656 if (child != w && child->isVisible() && child->transientParent() == w) {
2657 QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(child);
2658 if (platformWindow && platformWindow->isLayered()) {
2659 platformWindow->fireFullExpose();
2660 exposeEventsSent = true;
2661 }
2662 }
2663 }
2664 if (exposeEventsSent && !QWindowsContext::instance()->asyncExpose())
2665 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
2666 }
2667}
2668
2669// Apply corrections to window placement in Windows 10
2670// Related to task bar on top or left.
2671
2673{
2674 return window()->maximumHeight() != QWINDOWSIZE_MAX;
2675}
2676
2678{
2679 return window()->maximumWidth() != QWINDOWSIZE_MAX;
2680}
2681
2683{
2685}
2686
2687void QWindowsWindow::correctWindowPlacement(WINDOWPLACEMENT &windowPlacement)
2688{
2689 static const auto windows11 = QOperatingSystemVersion::Windows11_21H2;
2690 static const bool isWindows10 = QOperatingSystemVersion::current() < windows11;
2691 if (!isWindows10)
2692 return;
2693
2694 // Correct normal position by placement offset on Windows 10
2695 // (where task bar can be on any side of the screen)
2696 const QPoint offset = windowPlacementOffset(m_data.hwnd, m_data.restoreGeometry.topLeft());
2697 windowPlacement.rcNormalPosition = RECTfromQRect(m_data.restoreGeometry.translated(-offset));
2698 qCDebug(lcQpaWindow) << "Corrected normal position by" << -offset;
2699
2700 // A bug in windows 10 grows
2701 // - ptMaxPosition.x by the task bar's width, if it's on the left
2702 // - ptMaxPosition.y by the task bar's height, if it's on the top
2703 // each time GetWindowPlacement() is called.
2704 // The offset of the screen's left edge (as per frameMargins_sys().left()) is ignored.
2705 // => Check for windows 10 and correct.
2706 if (hasMaximumSize()) {
2707 const QMargins margins = frameMargins_sys();
2708 const QPoint topLeft = window()->screen()->geometry().topLeft();
2709 windowPlacement.ptMaxPosition = POINT{ topLeft.x() - margins.left(), topLeft.y() };
2710 qCDebug(lcQpaWindow) << "Window has maximum size. Corrected topLeft by"
2711 << -margins.left();
2712
2713 // If there is a placement offset correct width/height unless restricted,
2714 // in order to fit window onto the screen.
2715 if (offset.x() > 0 && !hasMaximumWidth()) {
2716 const int adjust = offset.x() / window()->devicePixelRatio();
2717 window()->setWidth(window()->width() - adjust);
2718 qCDebug(lcQpaWindow) << "Width shortened by" << adjust << "logical pixels.";
2719 }
2720 if (offset.y() > 0 && !hasMaximumHeight()) {
2721 const int adjust = offset.y() / window()->devicePixelRatio();
2722 window()->setHeight(window()->height() - adjust);
2723 qCDebug(lcQpaWindow) << "Height shortened by" << adjust << "logical pixels.";
2724 }
2725 }
2726}
2727
2729{
2730 m_data.restoreGeometry = normalFrameGeometry(m_data.hwnd);
2731}
2732
2733void QWindowsWindow::setWindowState(Qt::WindowStates state)
2734{
2735 if (m_data.hwnd) {
2736 setWindowState_sys(state);
2737 m_windowState = state;
2738 }
2739}
2740
2741bool QWindowsWindow::isFullScreen_sys() const
2742{
2743 const QWindow *w = window();
2744 if (!w->isTopLevel())
2745 return false;
2746 QRect geometry = geometry_sys();
2748 geometry += QMargins(1, 1, 1, 1);
2749 QPlatformScreen *screen = screenForGeometry(geometry);
2750 return screen && geometry == screen->geometry();
2751}
2752
2753/*!
2754 \brief Change the window state.
2755
2756 \note Window frames change when maximized;
2757 the top margin shrinks somewhat but that cannot be obtained using
2758 AdjustWindowRectEx().
2759
2760 \note Some calls to SetWindowLong require a subsequent call
2761 to ShowWindow.
2762*/
2763
2764void QWindowsWindow::setWindowState_sys(Qt::WindowStates newState)
2765{
2766 const Qt::WindowStates oldState = m_windowState;
2767 if (oldState == newState)
2768 return;
2769 qCDebug(lcQpaWindow) << '>' << __FUNCTION__ << this << window()
2770 << " from " << oldState << " to " << newState;
2771
2772 const bool visible = isVisible();
2773 auto stateChange = oldState ^ newState;
2774
2775 if (stateChange & Qt::WindowFullScreen) {
2776 if (newState & Qt::WindowFullScreen) {
2777 UINT newStyle = WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_POPUP;
2778 // Save geometry and style to be restored when fullscreen
2779 // is turned off again, since on Windows, it is not a real
2780 // Window state but emulated by changing geometry and style.
2781 if (!m_savedStyle) {
2782 m_savedStyle = style();
2783 if ((oldState & Qt::WindowMinimized) || (oldState & Qt::WindowMaximized)) {
2784 const QRect nf = normalFrameGeometry(m_data.hwnd);
2785 if (nf.isValid())
2786 m_savedFrameGeometry = nf;
2787 } else {
2788 m_savedFrameGeometry = frameGeometry_sys();
2789 }
2790 }
2791 if (newState & Qt::WindowMaximized)
2793 if (m_savedStyle & WS_SYSMENU)
2794 newStyle |= WS_SYSMENU;
2795 if (visible)
2796 newStyle |= WS_VISIBLE;
2797 if (testFlag(HasBorderInFullScreen))
2798 newStyle |= WS_BORDER;
2799 setStyle(newStyle);
2800 const HMONITOR monitor = MonitorFromWindow(m_data.hwnd, MONITOR_DEFAULTTONEAREST);
2801 MONITORINFO monitorInfo = {};
2802 monitorInfo.cbSize = sizeof(MONITORINFO);
2803 GetMonitorInfoW(monitor, &monitorInfo);
2804 const QRect screenGeometry(monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.top,
2805 monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left,
2806 monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top);
2807 if (newState & Qt::WindowMinimized) {
2808 setMinimizedGeometry(m_data.hwnd, screenGeometry);
2809 if (stateChange & Qt::WindowMaximized)
2810 setRestoreMaximizedFlag(m_data.hwnd, newState & Qt::WindowMaximized);
2811 } else {
2812 const UINT swpf = SWP_FRAMECHANGED | SWP_NOACTIVATE;
2813 const bool wasSync = testFlag(SynchronousGeometryChangeEvent);
2815 SetWindowPos(m_data.hwnd, HWND_TOP, screenGeometry.left(), screenGeometry.top(), screenGeometry.width(), screenGeometry.height(), swpf);
2816 if (!wasSync)
2819 QWindowSystemInterface::handleGeometryChange(window(), screenGeometry);
2820 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
2821 }
2822 } else {
2823 // Restore saved state.
2824 unsigned newStyle = m_savedStyle ? m_savedStyle : style();
2825 if (visible)
2826 newStyle |= WS_VISIBLE;
2827 setStyle(newStyle);
2828
2829 const QScreen *screen = window()->screen();
2830 if (!screen)
2831 screen = QGuiApplication::primaryScreen();
2832 // That area of the virtual desktop might not be covered by a screen anymore.
2833 if (const auto platformScreen = screen->handle()) {
2834 if (!platformScreen->geometry().intersects(m_savedFrameGeometry))
2835 m_savedFrameGeometry.moveTo(platformScreen->geometry().topLeft());
2836 }
2837
2838 if (newState & Qt::WindowMinimized) {
2839 setMinimizedGeometry(m_data.hwnd, m_savedFrameGeometry);
2840 if (stateChange & Qt::WindowMaximized)
2841 setRestoreMaximizedFlag(m_data.hwnd, newState & Qt::WindowMaximized);
2842 } else {
2843 UINT swpf = SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOACTIVATE;
2844 if (!m_savedFrameGeometry.isValid())
2845 swpf |= SWP_NOSIZE | SWP_NOMOVE;
2846 const bool wasSync = testFlag(SynchronousGeometryChangeEvent);
2848 // After maximized/fullscreen; the window can be in a maximized state. Clear
2849 // it before applying the normal geometry.
2850 if (windowVisibility_sys(m_data.hwnd) == QWindow::Maximized)
2851 ShowWindow(m_data.hwnd, SW_SHOWNOACTIVATE);
2852 SetWindowPos(m_data.hwnd, nullptr, m_savedFrameGeometry.x(), m_savedFrameGeometry.y(),
2853 m_savedFrameGeometry.width(), m_savedFrameGeometry.height(), swpf);
2854 if (!wasSync)
2856 // preserve maximized state
2857 if (visible) {
2859 ShowWindow(m_data.hwnd,
2860 (newState & Qt::WindowMaximized) ? SW_MAXIMIZE : SW_SHOWNA);
2862 }
2863 }
2864 m_savedStyle = 0;
2865 m_savedFrameGeometry = QRect();
2866 }
2867 } else if ((oldState & Qt::WindowMaximized) != (newState & Qt::WindowMaximized)) {
2868 if (visible && !(newState & Qt::WindowMinimized)) {
2870 if (newState & Qt::WindowFullScreen)
2872 if (m_data.flags & Qt::FramelessWindowHint) {
2873 if (newState == Qt::WindowNoState) {
2874 const QRect &rect = m_savedFrameGeometry;
2875 MoveWindow(m_data.hwnd, rect.x(), rect.y(), rect.width(), rect.height(), true);
2876 } else {
2877 HMONITOR monitor = MonitorFromWindow(m_data.hwnd, MONITOR_DEFAULTTONEAREST);
2878 MONITORINFO monitorInfo = {};
2879 monitorInfo.cbSize = sizeof(MONITORINFO);
2880 GetMonitorInfo(monitor, &monitorInfo);
2881 const RECT &rect = monitorInfo.rcWork;
2882 m_savedFrameGeometry = geometry();
2883 MoveWindow(m_data.hwnd, rect.left, rect.top,
2884 rect.right - rect.left, rect.bottom - rect.top, true);
2885 }
2886 } else {
2887 ShowWindow(m_data.hwnd,
2888 (newState & Qt::WindowMaximized) ? SW_MAXIMIZE : SW_SHOWNOACTIVATE);
2889 }
2892 } else if (visible && (oldState & newState & Qt::WindowMinimized)) {
2893 // change of the maximized state while keeping minimized
2894 setRestoreMaximizedFlag(m_data.hwnd, newState & Qt::WindowMaximized);
2895 }
2896 }
2897
2898 if (stateChange & Qt::WindowMinimized) {
2899 if (visible) {
2900 ShowWindow(m_data.hwnd,
2901 (newState & Qt::WindowMinimized) ? SW_MINIMIZE :
2902 (newState & Qt::WindowMaximized) ? SW_MAXIMIZE : SW_SHOWNORMAL);
2903 if ((newState & Qt::WindowMinimized) && (stateChange & Qt::WindowMaximized))
2904 setRestoreMaximizedFlag(m_data.hwnd, newState & Qt::WindowMaximized);
2905 }
2906 }
2907 qCDebug(lcQpaWindow) << '<' << __FUNCTION__ << this << window() << newState;
2908}
2909
2910void QWindowsWindow::setStyle(unsigned s) const
2911{
2912 qCDebug(lcQpaWindow) << __FUNCTION__ << this << window() << debugWinStyle(s);
2914 SetWindowLongPtr(m_data.hwnd, GWL_STYLE, s);
2916}
2917
2918void QWindowsWindow::setExStyle(unsigned s) const
2919{
2920 qCDebug(lcQpaWindow) << __FUNCTION__ << this << window() << debugWinExStyle(s);
2921 SetWindowLongPtr(m_data.hwnd, GWL_EXSTYLE, s);
2922}
2923
2924bool QWindowsWindow::windowEvent(QEvent *event)
2925{
2926 switch (event->type()) {
2927 case QEvent::ApplicationPaletteChange:
2928 setDarkBorder(QWindowsTheme::instance()->colorScheme() == Qt::ColorScheme::Dark);
2929 break;
2930 case QEvent::WindowBlocked: // Blocked by another modal window.
2931 setEnabled(false);
2934 ReleaseCapture();
2935 break;
2936 case QEvent::WindowUnblocked:
2937 setEnabled(true);
2939 break;
2940 default:
2941 break;
2942 }
2943
2944 return QWindowsBaseWindow::windowEvent(event);
2945}
2946
2948{
2949 qCDebug(lcQpaWindow) << __FUNCTION__ << this << window();
2950}
2951
2952static bool isResize(const WINDOWPOS *windowPos)
2953{
2954 bool result = false;
2955 if ((windowPos->flags & SWP_NOSIZE) == 0) {
2956 RECT rect;
2957 GetWindowRect(windowPos->hwnd, &rect);
2958 result = rect.right - rect.left != windowPos->cx || rect.bottom - rect.top != windowPos->cy;
2959 }
2960 return result;
2961}
2962
2963bool QWindowsWindow::handleGeometryChangingMessage(MSG *message, const QWindow *qWindow, const QMargins &margins)
2964{
2965 auto *windowPos = reinterpret_cast<WINDOWPOS *>(message->lParam);
2966 const QRect suggestedFrameGeometry(windowPos->x, windowPos->y,
2967 windowPos->cx, windowPos->cy);
2968 const QRect suggestedGeometry = suggestedFrameGeometry - margins;
2969
2970 // Tell Windows to discard the entire contents of the client area, as re-using
2971 // parts of the client area would lead to jitter during resize.
2972 // Check the suggestedGeometry against the current one to only discard during
2973 // resize, and not a plain move. We also look for SWP_NOSIZE since that, too,
2974 // implies an identical size, and comparing QRects wouldn't work with null cx/cy
2975 if (isResize(windowPos))
2976 windowPos->flags |= SWP_NOCOPYBITS;
2977
2978 if ((windowPos->flags & SWP_NOZORDER) == 0) {
2979 if (QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(qWindow)) {
2980 QWindow *parentWindow = qWindow->parent();
2981 HWND parentHWND = GetAncestor(windowPos->hwnd, GA_PARENT);
2982 HWND desktopHWND = GetDesktopWindow();
2983 platformWindow->m_data.embedded = !parentWindow && parentHWND && (parentHWND != desktopHWND);
2984 }
2985 if (qWindow->flags().testFlag(Qt::WindowStaysOnBottomHint))
2986 windowPos->hwndInsertAfter = HWND_BOTTOM;
2987 }
2988 if (!qWindow->isTopLevel()) // Implement hasHeightForWidth().
2989 return false;
2990 if (windowPos->flags & SWP_NOSIZE)
2991 return false;
2992 const QRectF correctedGeometryF = QPlatformWindow::closestAcceptableGeometry(qWindow, suggestedGeometry);
2993 if (!correctedGeometryF.isValid())
2994 return false;
2995 const QRect correctedFrameGeometry = correctedGeometryF.toRect() + margins;
2996 if (correctedFrameGeometry == suggestedFrameGeometry)
2997 return false;
2998 windowPos->x = correctedFrameGeometry.left();
2999 windowPos->y = correctedFrameGeometry.top();
3000 windowPos->cx = correctedFrameGeometry.width();
3001 windowPos->cy = correctedFrameGeometry.height();
3002 return true;
3003}
3004
3006{
3007 const QMargins margins = window()->isTopLevel() ? fullFrameMargins() : QMargins();
3008 return QWindowsWindow::handleGeometryChangingMessage(message, window(), margins);
3009}
3010
3011void QWindowsWindow::setFullFrameMargins(const QMargins &newMargins)
3012{
3013 if (shouldOmitFrameAdjustment(m_data.flags, m_data.hwnd))
3014 return;
3015 if (m_data.fullFrameMargins != newMargins) {
3016 qCDebug(lcQpaWindow) << __FUNCTION__ << window() << m_data.fullFrameMargins << "->" << newMargins;
3017 m_data.fullFrameMargins = newMargins;
3018 }
3019}
3020
3022{
3023 // QTBUG-82580: If a native menu is present, force a WM_NCCALCSIZE.
3024 if (GetMenu(m_data.hwnd))
3025 QWindowsContext::forceNcCalcSize(m_data.hwnd);
3026 else
3027 calculateFullFrameMargins();
3028}
3029
3030void QWindowsWindow::calculateFullFrameMargins()
3031{
3032 if (shouldOmitFrameAdjustment(m_data.flags, m_data.hwnd))
3033 return;
3034
3035 // QTBUG-113736: systemMargins depends on AdjustWindowRectExForDpi. This doesn't take into
3036 // account possible external modifications to the titlebar, as with ExtendsContentIntoTitleBar()
3037 // from the Windows App SDK. We can fix this by comparing the WindowRect (which includes the
3038 // frame) to the ClientRect. If a 'typical' frame is detected, i.e. only the titlebar has been
3039 // modified, we can safely adjust the frame by deducting the bottom margin to the total Y
3040 // difference between the two rects, to get the actual size of the titlebar and prevent
3041 // unwanted client area slicing.
3042
3043 RECT windowRect{};
3044 RECT clientRect{};
3045 GetWindowRect(handle(), &windowRect);
3046 GetClientRect(handle(), &clientRect);
3047
3048 // QTBUG-117704 It is also possible that the user has manually removed the frame (for example
3049 // by handling WM_NCCALCSIZE). If that is the case, i.e., the client area and the window area
3050 // have identical sizes, we don't want to override the user-defined margins.
3051
3052 if (qSizeOfRect(windowRect) == qSizeOfRect(clientRect))
3053 return;
3054
3055 // Normally obtained from WM_NCCALCSIZE. This calculation only works
3056 // when no native menu is present.
3057 const auto systemMargins = testFlag(DisableNonClientScaling)
3058 ? QWindowsGeometryHint::frameOnPrimaryScreen(window(), m_data.hwnd)
3059 : frameMargins_sys();
3060 const int extendedClientAreaBorder = window()->flags().testFlag(Qt::ExpandedClientAreaHint) ? qRound(QHighDpiScaling::factor(window())) * 2 : 0;
3061 const QMargins actualMargins = systemMargins + customMargins() - extendedClientAreaBorder;
3062
3063 const int yDiff = (windowRect.bottom - windowRect.top) - (clientRect.bottom - clientRect.top);
3064 const bool typicalFrame = (actualMargins.left() == actualMargins.right())
3065 && (actualMargins.right() == actualMargins.bottom());
3066
3067 const QMargins adjustedMargins = typicalFrame ?
3068 QMargins(actualMargins.left(), (yDiff - actualMargins.bottom()),
3069 actualMargins.right(), actualMargins.bottom())
3070 : actualMargins;
3071
3072 setFullFrameMargins(adjustedMargins);
3073}
3074
3076{
3077 QMargins result = fullFrameMargins();
3078 if (isTopLevel() && m_data.hasFrame)
3079 result -= invisibleMargins(m_data.hwnd);
3080 return result;
3081}
3082
3084{
3085 if (shouldOmitFrameAdjustment(m_data.flags, m_data.hwnd))
3086 return {};
3087 return m_data.fullFrameMargins;
3088}
3089
3090void QWindowsWindow::setOpacity(qreal level)
3091{
3092 qCDebug(lcQpaWindow) << __FUNCTION__ << level;
3093 if (!qFuzzyCompare(m_opacity, level)) {
3094 m_opacity = level;
3095 if (m_data.hwnd)
3096 setWindowOpacity(m_data.hwnd, m_data.flags,
3098 level);
3099 }
3100}
3101
3102static inline HRGN createRectRegion(const QRect &r)
3103{
3104 return CreateRectRgn(r.left(), r.top(), r.x() + r.width(), r.y() + r.height());
3105}
3106
3107static inline void addRectToWinRegion(const QRect &rect, HRGN *winRegion)
3108{
3109 if (const HRGN rectRegion = createRectRegion(rect)) {
3110 HRGN result = CreateRectRgn(0, 0, 0, 0);
3111 if (CombineRgn(result, *winRegion, rectRegion, RGN_OR)) {
3112 DeleteObject(*winRegion);
3113 *winRegion = result;
3114 }
3115 DeleteObject(rectRegion);
3116 }
3117}
3118
3119static HRGN qRegionToWinRegion(const QRegion &region)
3120{
3121 auto it = region.begin();
3122 const auto end = region.end();
3123 if (it == end)
3124 return nullptr;
3125 HRGN hRegion = createRectRegion(*it);
3126 while (++it != end)
3127 addRectToWinRegion(*it, &hRegion);
3128 return hRegion;
3129}
3130
3131void QWindowsWindow::setMask(const QRegion &region)
3132{
3133 if (region.isEmpty()) {
3134 SetWindowRgn(m_data.hwnd, nullptr, true);
3135 return;
3136 }
3137 const HRGN winRegion = qRegionToWinRegion(region);
3138
3139 // Mask is in client area coordinates, so offset it in case we have a frame
3140 if (window()->isTopLevel()) {
3141 const QMargins margins = fullFrameMargins();
3142 OffsetRgn(winRegion, margins.left(), margins.top());
3143 }
3144
3145 // SetWindowRgn takes ownership.
3146 if (!SetWindowRgn(m_data.hwnd, winRegion, true))
3147 DeleteObject(winRegion);
3148}
3149
3151{
3152 qCDebug(lcQpaWindow) << __FUNCTION__ << this << window();
3153
3154 if (!m_data.hwnd)
3155 return;
3156
3157 const auto activationBehavior = QWindowsIntegration::instance()->windowActivationBehavior();
3158 if (QGuiApplication::applicationState() == Qt::ApplicationActive
3159 || activationBehavior != QWindowsApplication::AlwaysActivateWindow) {
3160 SetForegroundWindow(m_data.hwnd);
3161 SetFocus(m_data.hwnd);
3162 return;
3163 }
3164
3165 // Force activate this window. The following code will bring the window to the
3166 // foreground and activate it. If the window is hidden, it will show up. If
3167 // the window is minimized, it will restore to the previous position.
3168
3169 // But first we need some sanity checks.
3170 if (m_data.flags & Qt::WindowStaysOnBottomHint) {
3171 qCWarning(lcQpaWindow) <<
3172 "Windows with Qt::WindowStaysOnBottomHint can't be brought to the foreground.";
3173 return;
3174 }
3175 if (m_data.flags & Qt::WindowStaysOnTopHint) {
3176 qCWarning(lcQpaWindow) <<
3177 "Windows with Qt::WindowStaysOnTopHint will always be on the foreground.";
3178 return;
3179 }
3180 if (window()->type() == Qt::ToolTip) {
3181 qCWarning(lcQpaWindow) << "ToolTip windows should not be activated.";
3182 return;
3183 }
3184
3185 // We need to show the window first, otherwise we won't be able to bring it to front.
3186 if (!IsWindowVisible(m_data.hwnd))
3187 ShowWindow(m_data.hwnd, SW_SHOW);
3188
3189 if (IsIconic(m_data.hwnd)) {
3190 ShowWindow(m_data.hwnd, SW_RESTORE);
3191 // When the window is restored, it will always become the foreground window.
3192 // So return early here, we don't need the following code to bring it to front.
3193 return;
3194 }
3195
3196 // OK, our window is not minimized, so now we will try to bring it to front manually.
3197 const HWND oldForegroundWindow = GetForegroundWindow();
3198 if (!oldForegroundWindow) // It may be NULL, according to MS docs.
3199 return;
3200
3201 // First try to send a message to the current foreground window to check whether
3202 // it is currently hanging or not.
3203 if (SendMessageTimeoutW(oldForegroundWindow, WM_NULL, 0, 0,
3204 SMTO_BLOCK | SMTO_ABORTIFHUNG | SMTO_NOTIMEOUTIFNOTHUNG, 1000, nullptr) == 0) {
3205 qCWarning(lcQpaWindow) << "The foreground window hangs, can't activate current window.";
3206 return;
3207 }
3208
3209 const DWORD windowThreadProcessId = GetWindowThreadProcessId(oldForegroundWindow, nullptr);
3210 const DWORD currentThreadId = GetCurrentThreadId();
3211
3212 AttachThreadInput(windowThreadProcessId, currentThreadId, TRUE);
3213 const auto cleanup = qScopeGuard([windowThreadProcessId, currentThreadId](){
3214 AttachThreadInput(windowThreadProcessId, currentThreadId, FALSE);
3215 });
3216
3217 BringWindowToTop(m_data.hwnd);
3218
3219 // Activate the window too. This will force us to the virtual desktop this
3220 // window is on, if it's on another virtual desktop.
3221 SetActiveWindow(m_data.hwnd);
3222}
3223
3225{
3226 if (!m_data.hwnd) {
3227 qWarning("%s: No handle", __FUNCTION__);
3228 return false;
3229 }
3230 qCDebug(lcQpaWindow) << __FUNCTION__ << this << window() << grab;
3231
3233 if (grab) {
3234 context->setKeyGrabber(window());
3235 } else {
3236 if (context->keyGrabber() == window())
3237 context->setKeyGrabber(nullptr);
3238 }
3239 return true;
3240}
3241
3243{
3244 qCDebug(lcQpaWindow) << __FUNCTION__ << window() << grab;
3245 if (!m_data.hwnd) {
3246 qWarning("%s: No handle", __FUNCTION__);
3247 return false;
3248 }
3249 if (!isVisible() && grab) {
3250 qWarning("%s: Not setting mouse grab for invisible window %s/'%s'",
3251 __FUNCTION__, window()->metaObject()->className(),
3252 qPrintable(window()->objectName()));
3253 return false;
3254 }
3255 // release grab or an explicit grab overriding autocapture: Clear flag.
3257 if (hasMouseCapture() != grab) {
3258 if (grab) {
3259 SetCapture(m_data.hwnd);
3260 } else {
3261 ReleaseCapture();
3262 }
3263 }
3264 return grab;
3265}
3266
3267static inline DWORD edgesToWinOrientation(Qt::Edges edges)
3268{
3269 if (edges == Qt::LeftEdge)
3270 return 0xf001; // SC_SIZELEFT;
3271 else if (edges == (Qt::RightEdge))
3272 return 0xf002; // SC_SIZERIGHT
3273 else if (edges == (Qt::TopEdge))
3274 return 0xf003; // SC_SIZETOP
3275 else if (edges == (Qt::TopEdge | Qt::LeftEdge))
3276 return 0xf004; // SC_SIZETOPLEFT
3277 else if (edges == (Qt::TopEdge | Qt::RightEdge))
3278 return 0xf005; // SC_SIZETOPRIGHT
3279 else if (edges == (Qt::BottomEdge))
3280 return 0xf006; // SC_SIZEBOTTOM
3281 else if (edges == (Qt::BottomEdge | Qt::LeftEdge))
3282 return 0xf007; // SC_SIZEBOTTOMLEFT
3283 else if (edges == (Qt::BottomEdge | Qt::RightEdge))
3284 return 0xf008; // SC_SIZEBOTTOMRIGHT
3285
3286 return 0xf000; // SC_SIZE
3287}
3288
3289bool QWindowsWindow::startSystemResize(Qt::Edges edges)
3290{
3291 if (Q_UNLIKELY(window()->flags().testFlag(Qt::MSWindowsFixedSizeDialogHint)))
3292 return false;
3293
3294 ReleaseCapture();
3295 PostMessage(m_data.hwnd, WM_SYSCOMMAND, edgesToWinOrientation(edges), 0);
3297 return true;
3298}
3299
3301{
3302 ReleaseCapture();
3303 PostMessage(m_data.hwnd, WM_SYSCOMMAND, 0xF012 /*SC_DRAGMOVE*/, 0);
3304 return true;
3305}
3306
3308{
3309 if (enabled) {
3311 } else {
3313 }
3314}
3315
3316void QWindowsWindow::getSizeHints(MINMAXINFO *mmi) const
3317{
3318 QWindowsGeometryHint::applyToMinMaxInfo(window(), fullFrameMargins(), mmi);
3319 qCDebug(lcQpaWindow) << __FUNCTION__ << window() << *mmi;
3320}
3321
3322bool QWindowsWindow::handleNonClientHitTest(const QPoint &globalPos, LRESULT *result) const
3323{
3324 // QTBUG-32663, suppress resize cursor for fixed size windows.
3325 const QWindow *w = window();
3326 const QPoint localPos = w->mapFromGlobal(QHighDpi::fromNativePixels(globalPos, w));
3327 const QRect geom = geometry();
3328 static auto oldMouseButtonState = Qt::NoButton;
3329
3330 if (m_data.flags.testFlags(Qt::ExpandedClientAreaHint)) {
3331 bool isDefaultTitleBar = !w->flags().testFlag(Qt::CustomizeWindowHint);
3332 bool isCustomized = w->flags().testFlags(Qt::CustomizeWindowHint) && w->flags().testAnyFlags(Qt::WindowTitleHint|
3333 Qt::WindowMinimizeButtonHint|
3334 Qt::WindowMaximizeButtonHint|
3335 Qt::WindowCloseButtonHint);
3336 const int border = (IsZoomed(m_data.hwnd) || isFullScreen_sys()) ? 0 : getResizeBorderThickness(savedDpi());
3337 const int titleBarHeight = getTitleBarHeight_sys(savedDpi());
3338 const int titleButtonWidth = titleBarHeight * 1.5;
3339 const bool mouseButtonsSwapped = GetSystemMetrics(SM_SWAPBUTTON);
3340 auto mouseButtons = Qt::NoButton;
3341 if (mouseButtonsSwapped)
3342 mouseButtons = GetAsyncKeyState(VK_LBUTTON) != 0 ? Qt::RightButton : (GetAsyncKeyState(VK_RBUTTON) ? Qt::LeftButton : Qt::NoButton);
3343 else
3344 mouseButtons = GetAsyncKeyState(VK_LBUTTON) != 0 ? Qt::LeftButton : (GetAsyncKeyState(VK_RBUTTON) ? Qt::RightButton : Qt::NoButton);
3345
3346 *result = HTCLIENT;
3347 if (isCustomized || isDefaultTitleBar) {
3348 int buttons = 1;
3349
3350 if (globalPos.y() < geom.top() + titleBarHeight) {
3351 if (m_data.flags.testFlags(Qt::WindowCloseButtonHint) || isDefaultTitleBar) {
3352 if ((globalPos.x() > geom.right() - titleButtonWidth * buttons) && (globalPos.x() <= geom.right())) {
3353 if (mouseButtons == Qt::LeftButton)
3354 *result = HTCLOSE;
3355 }
3356 buttons++;
3357 } if (m_data.flags.testFlags(Qt::WindowMaximizeButtonHint) || isDefaultTitleBar) {
3358 if ((globalPos.x() > geom.right() - titleButtonWidth * buttons) && (globalPos.x() <= geom.right() - titleButtonWidth * (buttons-1))){
3359 if (mouseButtons == Qt::LeftButton) {
3360 if (IsZoomed(m_data.hwnd))
3361 *result = HTSIZE;
3362 else
3363 *result = HTMAXBUTTON;
3364 }
3365 }
3366 buttons++;
3367 } if (m_data.flags.testFlags(Qt::WindowMinimizeButtonHint) || isDefaultTitleBar) {
3368 if ((globalPos.x() > geom.right() - titleButtonWidth * buttons) && (globalPos.x() <= geom.right() - titleButtonWidth * (buttons-1))){
3369 if (mouseButtons == Qt::LeftButton)
3370 *result = HTMINBUTTON;
3371 }
3372 buttons++;
3373 } if ((isCustomized || isDefaultTitleBar) &&
3374 *result == HTCLIENT){
3375 QWindow* wnd = window();
3376 if (mouseButtons != oldMouseButtonState) {
3377 auto mouseEventType = mouseButtons == Qt::NoButton ? QEvent::MouseButtonRelease : QEvent::MouseButtonPress;
3378 auto mouseEventButtons = mouseEventType == QEvent::MouseButtonPress ? mouseButtons : oldMouseButtonState;
3379 bool accepted = QWindowSystemInterface::handleMouseEvent<QWindowSystemInterface::SynchronousDelivery>(wnd, QHighDpi::toNativeLocalPosition(localPos, w), globalPos, mouseEventButtons, mouseEventButtons, mouseEventType);
3380 if (!accepted && mouseButtons == Qt::RightButton)
3381 *result = HTSYSMENU;
3382 else if (!accepted && globalPos.y() < geom.top() + titleBarHeight)
3383 *result = HTCAPTION;
3384 }
3385 }
3386 }
3387 } else if (w->flags().testFlag(Qt::CustomizeWindowHint)) {
3388
3389 QWindow* wnd = window();
3390 if (mouseButtons != oldMouseButtonState) {
3391 auto mouseEventType = mouseButtons == Qt::NoButton ? QEvent::MouseButtonRelease : QEvent::MouseButtonPress;
3392 auto mouseEventButtons = mouseEventType == QEvent::MouseButtonPress ? mouseButtons : oldMouseButtonState;
3393 bool accepted = QWindowSystemInterface::handleMouseEvent<QWindowSystemInterface::SynchronousDelivery>(wnd, QHighDpi::toNativeLocalPosition(localPos, w), globalPos, mouseEventButtons, mouseEventButtons, mouseEventType);
3394 if (!accepted && mouseButtons == Qt::RightButton)
3395 *result = HTSYSMENU;
3396 else if (!accepted && globalPos.y() < geom.top() + titleBarHeight)
3397 *result = HTCAPTION;
3398 }
3399 }
3400 oldMouseButtonState = mouseButtons;
3401
3402 if (border != 0) {
3403 const bool left = (globalPos.x() >= geom.left()) && (globalPos.x() < geom.left() + border);
3404 const bool right = (globalPos.x() > geom.right() - border) && (globalPos.x() <= geom.right());
3405 const bool top = (globalPos.y() >= geom.top()) && (globalPos.y() < geom.top() + border);
3406 const bool bottom = (globalPos.y() > geom.bottom() - border) && (globalPos.y() <= geom.bottom());
3407
3408 if (left || right || top || bottom) {
3409 if (left)
3410 *result = top ? HTTOPLEFT : (bottom ? HTBOTTOMLEFT : HTLEFT);
3411 else if (right)
3412 *result = top ? HTTOPRIGHT : (bottom ? HTBOTTOMRIGHT : HTRIGHT);
3413 else
3414 *result = top ? HTTOP : HTBOTTOM;
3415 }
3416 }
3417
3418 switch (*result) {
3419 case HTCLOSE:
3420 const_cast<QWindow *>(w)->close();
3421 break;
3422 case HTMAXBUTTON:
3423 const_cast<QWindow *>(w)->showMaximized();
3424 break;
3425 case HTMINBUTTON:
3426 const_cast<QWindow *>(w)->showMinimized();
3427 break;
3428 case HTSIZE:
3429 const_cast<QWindow *>(w)->showNormal();
3430 break;
3431 case HTSYSMENU: {
3432 HWND hwnd = reinterpret_cast<HWND>(w->winId());
3433 HMENU sysMenu = GetSystemMenu(hwnd, false);
3434 TrackPopupMenu(sysMenu, 0, globalPos.x(), globalPos.y(), 0, hwnd, nullptr);
3435 }
3436 default:
3437 break;
3438 }
3439 return true;
3440 }
3441
3442 // QTBUG-32663, suppress resize cursor for fixed size windows.
3443 if (m_data.flags & Qt::ExpandedClientAreaHint) {
3444 const int border = (IsZoomed(m_data.hwnd) || isFullScreen_sys()) ? 0 : getResizeBorderThickness(savedDpi());
3445 if (border == 0) {
3446 *result = HTCLIENT;
3447 return true;
3448 }
3449 const QRect rect = geom;
3450 const bool left = (globalPos.x() >= rect.left()) && (globalPos.x() < rect.left() + border);
3451 const bool right = (globalPos.x() > rect.right() - border) && (globalPos.x() <= rect.right());
3452 const bool top = (globalPos.y() >= rect.top()) && (globalPos.y() < rect.top() + border);
3453 const bool bottom = (globalPos.y() > rect.bottom() - border) && (globalPos.y() <= rect.bottom());
3454
3455
3456 if (left || right || top || bottom) {
3457 if (left)
3458 *result = top ? HTTOPLEFT : (bottom ? HTBOTTOMLEFT : HTLEFT);
3459 else if (right)
3460 *result = top ? HTTOPRIGHT : (bottom ? HTBOTTOMRIGHT : HTRIGHT);
3461 else
3462 *result = top ? HTTOP : HTBOTTOM;
3463 } else {
3464 *result = HTCLIENT;
3465 }
3466 return true;
3467 }
3468
3469 // QTBUG-32663, suppress resize cursor for fixed size windows.
3470 if (!w->isTopLevel() // Task 105852, minimized windows need to respond to user input.
3471 || (m_windowState != Qt::WindowNoState)
3472 || !isActive()
3473 || (m_data.flags & Qt::FramelessWindowHint)) {
3474 return false;
3475 }
3476 const QSize minimumSize = w->minimumSize();
3477 if (minimumSize.isEmpty())
3478 return false;
3479 const QSize maximumSize = w->maximumSize();
3480 const bool fixedWidth = minimumSize.width() == maximumSize.width();
3481 const bool fixedHeight = minimumSize.height() == maximumSize.height();
3482 if (!fixedWidth && !fixedHeight)
3483 return false;
3484 const QSize size = w->size();
3485 if (fixedHeight) {
3486 if (localPos.y() >= size.height()) {
3487 *result = HTBORDER; // Unspecified border, no resize cursor.
3488 return true;
3489 }
3490 if (localPos.y() < 0) {
3491 const int topResizeBarPos = invisibleMargins(m_data.hwnd).left() - frameMargins().top();
3492 if (localPos.y() < topResizeBarPos) {
3493 *result = HTCAPTION; // Extend caption over top resize bar, let's user move the window.
3494 return true;
3495 }
3496 }
3497 }
3498 if (fixedWidth && (localPos.x() < 0 || localPos.x() >= size.width())) {
3499 *result = HTBORDER; // Unspecified border, no resize cursor.
3500 return true;
3501 }
3502 return false;
3503}
3504
3505bool QWindowsWindow::handleNonClientActivate(LRESULT *result) const
3506{
3507 // If this window is frameless we choose to consume the event,
3508 // since the default logic causes the window title to appear.
3509 // QTBUG-127116
3510 if (m_data.flags & Qt::FramelessWindowHint) {
3511 *result = true;
3512 return true;
3513 }
3514 return false;
3515}
3516
3518{
3519 HWND hwnd = m_data.hwndTitlebar;
3520 QWindow *wnd = window();
3521
3522 RECT windowRect;
3523 GetWindowRect(hwnd, &windowRect);
3524
3525 const int titleBarHeight = getTitleBarHeight_sys(savedDpi());
3526 const int titleButtonWidth = titleBarHeight * 1.5;
3527 const qreal factor = QHighDpiScaling::factor(wnd);
3528 const int windowWidth = windowRect.right - windowRect.left;
3529
3530 POINT localPos;
3531 GetCursorPos(&localPos);
3532 MapWindowPoints(HWND_DESKTOP, m_data.hwnd, &localPos, 1);
3533
3534 const bool isDarkmode = QWindowsIntegration::instance()->darkModeHandling().testFlags(QWindowsApplication::DarkModeWindowFrames) &&
3535 qApp->styleHints()->colorScheme() == Qt::ColorScheme::Dark;
3536 const bool isWindows11orAbove = QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows11;
3537
3538 const QBrush closeButtonBrush(QColor(0xC4, 0x2C, 0x1E, 255));
3539 const QBrush minMaxButtonBrush = QBrush(isDarkmode ? QColor(0xFF, 0xFF, 0xFF, 0x40) : QColor(0x00, 0x00, 0x00, 0x20));
3540 const QBrush titleBarBackgroundColor = QBrush(isDarkmode ? QColor(0x1F, 0x1F, 0x1F, 0xFF) : QColor(0xF3, 0xF3, 0xF3, 0xFF));
3541 const QPen textPen = QPen(isDarkmode ? QColor(0xFF, 0xFF, 0xFD, 0xFF) : QColor(0x00, 0x00, 0x00, 0xFF));
3542
3543 QImage image(windowWidth, titleBarHeight, QImage::Format_ARGB32);
3544 QPainter p(&image);
3545 p.setCompositionMode(QPainter::CompositionMode_Clear);
3546 p.fillRect(0, 0, windowWidth, titleBarHeight, Qt::transparent);
3547
3548 p.setCompositionMode(QPainter::CompositionMode_SourceOver);
3549
3550 p.setBrush(titleBarBackgroundColor);
3551 p.setPen(Qt::NoPen);
3552 if (!wnd->flags().testFlags(Qt::NoTitleBarBackgroundHint)) {
3553 QRect titleRect;
3554 titleRect.setWidth(windowWidth);
3555 titleRect.setHeight(titleBarHeight);
3556 p.drawRect(titleRect);
3557 }
3558
3559 if (wnd->flags().testFlags(Qt::WindowTitleHint | Qt::CustomizeWindowHint) || !wnd->flags().testFlag(Qt::CustomizeWindowHint)) {
3560 QRect titleRect;
3561 titleRect.setY(1);
3562 titleRect.setX(12);
3563 titleRect.setWidth(windowWidth);
3564 titleRect.setHeight(titleBarHeight);
3565
3566 titleRect.adjust(factor * 4, 0, 0, 0);
3567 QRect iconRect(titleRect.x(), titleRect.y() + factor * 8, factor * 16, factor * 16);
3568 if (wnd->icon().isNull()) {
3569 static QIcon defaultIcon;
3570 if (defaultIcon.isNull()) {
3571 const QImage defaultIconImage = QImage::fromHICON(LoadIcon(0, IDI_APPLICATION));
3572 defaultIcon = QIcon(QPixmap::fromImage(defaultIconImage));
3573 }
3574 defaultIcon.paint(&p, iconRect);
3575 } else {
3576 wnd->icon().paint(&p, iconRect);
3577 }
3578 titleRect.adjust(factor * 24, 0, 0, 0);
3579
3580 p.setPen(textPen);
3581 QFont titleFont = QWindowsIntegration::instance()->fontDatabase()->defaultFont();
3582 titleFont.setPointSize(factor * 9);
3583 titleFont.setWeight(QFont::Thin);
3584 titleFont.setHintingPreference(QFont::PreferFullHinting);
3585 p.setFont(titleFont);
3586 const QString title = wnd->title().isEmpty() ? qApp->applicationName() : wnd->title();
3587 p.drawText(titleRect, title, QTextOption(Qt::AlignVCenter));
3588 }
3589
3590 int buttons = 1;
3591 const QString assetFontName = isWindows11orAbove ? QStringLiteral("Segoe Fluent Icons") : QStringLiteral("Segoe MDL2 Assets");
3592 QFont assetFont = QFont(assetFontName, factor * 7);
3593 assetFont.setWeight(QFont::Thin);
3594 assetFont.setHintingPreference(QFont::PreferFullHinting);
3595 p.setFont(assetFont);
3596 p.setBrush(closeButtonBrush);
3597 p.setPen(Qt::NoPen);
3598 if (wnd->flags().testFlags(Qt::WindowCloseButtonHint | Qt::CustomizeWindowHint) || !wnd->flags().testFlag(Qt::CustomizeWindowHint)) {
3599 QRectF rect;
3600 rect.setY(1);
3601 rect.setX(windowWidth - titleButtonWidth * buttons);
3602 rect.setWidth(titleButtonWidth);
3603 rect.setHeight(titleBarHeight);
3604 if (localPos.x > (windowWidth - buttons * titleButtonWidth) &&
3605 localPos.x < (windowWidth - (buttons - 1) * titleButtonWidth) &&
3606 localPos.y > rect.y() && localPos.y < rect.y() + rect.height()) {
3607 p.drawRect(rect);
3608 const QPen closeButtonHoveredPen = QPen(QColor(0xFF, 0xFF, 0xFD, 0xFF));
3609 p.setPen(closeButtonHoveredPen);
3610 } else {
3611 p.setPen(textPen);
3612 }
3613 p.drawText(rect, QStringLiteral("\uE8BB"), QTextOption(Qt::AlignVCenter | Qt::AlignHCenter));
3614 buttons++;
3615 }
3616
3617 p.setBrush(minMaxButtonBrush);
3618 p.setPen(Qt::NoPen);
3619 if (wnd->flags().testFlags(Qt::WindowMaximizeButtonHint | Qt::CustomizeWindowHint) || !wnd->flags().testFlag(Qt::CustomizeWindowHint)) {
3620 QRectF rect;
3621 rect.setY(1);
3622 rect.setX(windowWidth - titleButtonWidth * buttons);
3623 rect.setWidth(titleButtonWidth);
3624 rect.setHeight(titleBarHeight);
3625 if (localPos.x > (windowWidth - buttons * titleButtonWidth) &&
3626 localPos.x < (windowWidth - (buttons - 1) * titleButtonWidth) &&
3627 localPos.y > rect.y() && localPos.y < rect.y() + rect.height()) {
3628 p.drawRect(rect);
3629 }
3630 p.setPen(textPen);
3631 p.drawText(rect,QStringLiteral("\uE922"), QTextOption(Qt::AlignVCenter | Qt::AlignHCenter));
3632 buttons++;
3633 }
3634
3635 p.setBrush(minMaxButtonBrush);
3636 p.setPen(Qt::NoPen);
3637 if (wnd->flags().testFlags(Qt::WindowMinimizeButtonHint | Qt::CustomizeWindowHint) || !wnd->flags().testFlag(Qt::CustomizeWindowHint)) {
3638 QRectF rect;
3639 rect.setY(1);
3640 rect.setX(windowWidth - titleButtonWidth * buttons);
3641 rect.setWidth(titleButtonWidth);
3642 rect.setHeight(titleBarHeight);
3643 if (localPos.x > (windowWidth - buttons * titleButtonWidth) &&
3644 localPos.x < (windowWidth - (buttons - 1) * titleButtonWidth) &&
3645 localPos.y > rect.y() && localPos.y < rect.y() + rect.height()) {
3646 p.drawRect(rect);
3647 }
3648 p.setPen(textPen);
3649 p.drawText(rect,QStringLiteral("\uE921"), QTextOption(Qt::AlignVCenter | Qt::AlignHCenter));
3650 buttons++;
3651 }
3652
3653 p.end();
3654
3655 HBITMAP bmp = image.toHBITMAP();
3656
3657 HDC hdc = GetDC(hwnd);
3658
3659 HDC memdc = CreateCompatibleDC(hdc);
3660 HGDIOBJ original = SelectObject(memdc, bmp);
3661
3662
3663 BLENDFUNCTION blend = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
3664 POINT ptLocation = { 0, 0 };
3665 SIZE szWnd = { windowWidth, titleBarHeight };
3666 POINT ptSrc = { 0, 0 };
3667 UpdateLayeredWindow(hwnd, hdc, &ptLocation, &szWnd, memdc, &ptSrc, 0, &blend, ULW_ALPHA);
3668 SelectObject(hdc, original);
3669
3670 DeleteObject(bmp);
3671 DeleteObject(memdc);
3672 ReleaseDC(hwnd,hdc);
3673}
3674
3675#ifndef QT_NO_CURSOR
3676// Return the default cursor (Arrow) from QWindowsCursor's cache.
3677static inline CursorHandlePtr defaultCursor(const QWindow *w)
3678{
3679 if (QScreen *screen = w->screen())
3680 if (const QPlatformScreen *platformScreen = screen->handle())
3681 if (QPlatformCursor *cursor = platformScreen->cursor())
3682 return static_cast<QWindowsCursor *>(cursor)->standardWindowCursor(Qt::ArrowCursor);
3683 return CursorHandlePtr(new CursorHandle(QWindowsCursor::createCursorFromShape(Qt::ArrowCursor)));
3684}
3685
3686// Check whether to apply a new cursor. Either the window in question is
3687// currently under mouse, or it is the parent of the window under mouse and
3688// there is no other window with an explicitly set cursor in-between.
3689static inline bool applyNewCursor(const QWindow *w)
3690{
3691 const QWindow *underMouse = QWindowsContext::instance()->windowUnderMouse();
3692 if (underMouse == w)
3693 return true;
3694 for (const QWindow *p = underMouse; p ; p = p->parent()) {
3695 if (p == w)
3696 return true;
3697 const QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(p);
3698 if (platformWindow && !platformWindow->cursor()->isNull())
3699 return false;
3700 }
3701 return false;
3702}
3703#endif // !QT_NO_CURSOR
3704
3705/*!
3706 \brief Applies to cursor property set on the window to the global cursor.
3707
3708 \sa QWindowsCursor
3709*/
3710
3712{
3714 if (isTopLevel())
3716 return;
3717 }
3718#ifndef QT_NO_CURSOR
3719 if (m_cursor->isNull()) { // Recurse up to parent with non-null cursor. Set default for toplevel.
3720 if (const QWindow *p = window()->parent()) {
3721 if (QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(p))
3722 platformWindow->applyCursor();
3723 } else {
3724 SetCursor(defaultCursor(window())->handle());
3725 }
3726 } else {
3727 SetCursor(m_cursor->handle());
3728 }
3729#endif
3730}
3731
3732void QWindowsWindow::setCursor(const CursorHandlePtr &c)
3733{
3734#ifndef QT_NO_CURSOR
3735 bool changed = c->handle() != m_cursor->handle();
3736 // QTBUG-98856: Cursors can get out of sync after restoring override
3737 // cursors on native windows. Force an update.
3740 changed = true;
3741 }
3742 if (changed) {
3743 const bool apply = applyNewCursor(window());
3744 qCDebug(lcQpaWindow) << window() << __FUNCTION__
3745 << c->handle() << " doApply=" << apply;
3746 m_cursor = c;
3747 if (apply)
3749 }
3750#endif
3751}
3752
3754{
3755 if (isAlertState() == enabled)
3756 return;
3757 if (enabled) {
3760 } else {
3763 }
3764}
3765
3766void QWindowsWindow::alertWindow(int durationMs)
3767{
3768 UINT timeOutMs = GetCaretBlinkTime();
3769 if (!timeOutMs || timeOutMs == INFINITE)
3770 timeOutMs = 250;
3771
3772 FLASHWINFO info;
3773 info.cbSize = sizeof(info);
3774 info.hwnd = m_data.hwnd;
3775 info.dwFlags = FLASHW_TRAY;
3776 info.dwTimeout = timeOutMs;
3777 info.uCount = durationMs == 0 ? 10 : UINT(durationMs) / timeOutMs;
3778 FlashWindowEx(&info);
3779}
3780
3782{
3783 FLASHWINFO info;
3784 info.cbSize = sizeof(info);
3785 info.hwnd = m_data.hwnd;
3786 info.dwFlags = FLASHW_STOP;
3787 info.dwTimeout = 0;
3788 info.uCount = 0;
3789 FlashWindowEx(&info);
3790}
3791
3793{
3794 return (style() & WS_DISABLED) == 0;
3795}
3796
3797void QWindowsWindow::setEnabled(bool enabled)
3798{
3799 const unsigned oldStyle = style();
3800 unsigned newStyle = oldStyle;
3801 if (enabled) {
3802 newStyle &= ~WS_DISABLED;
3803 } else {
3804 newStyle |= WS_DISABLED;
3805 }
3806 if (newStyle != oldStyle)
3807 setStyle(newStyle);
3808}
3809
3810static HICON createHIcon(const QIcon &icon, int xSize, int ySize)
3811{
3812 if (!icon.isNull()) {
3813 // QTBUG-90363, request DPR=1 for the title bar.
3814 const QPixmap pm = icon.pixmap(icon.actualSize(QSize(xSize, ySize)), 1);
3815 if (!pm.isNull())
3816 return qt_pixmapToWinHICON(pm);
3817 }
3818 return nullptr;
3819}
3820
3821void QWindowsWindow::setWindowIcon(const QIcon &icon)
3822{
3823 if (m_data.hwnd) {
3824 destroyIcon();
3825
3826 m_iconSmall = createHIcon(icon, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
3827 m_iconBig = createHIcon(icon, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON));
3828
3829 if (m_iconBig) {
3830 SendMessage(m_data.hwnd, WM_SETICON, 0 /* ICON_SMALL */, LPARAM(m_iconSmall));
3831 SendMessage(m_data.hwnd, WM_SETICON, 1 /* ICON_BIG */, LPARAM(m_iconBig));
3832 } else {
3833 SendMessage(m_data.hwnd, WM_SETICON, 0 /* ICON_SMALL */, LPARAM(m_iconSmall));
3834 SendMessage(m_data.hwnd, WM_SETICON, 1 /* ICON_BIG */, LPARAM(m_iconSmall));
3835 }
3836 }
3837}
3838
3840{
3841 return window()->isTopLevel() && !m_data.embedded;
3842}
3843
3844enum : WORD {
3847};
3848
3849bool QWindowsWindow::setDarkBorderToWindow(HWND hwnd, bool d)
3850{
3851 const BOOL darkBorder = d ? TRUE : FALSE;
3852 const bool ok =
3853 SUCCEEDED(DwmSetWindowAttribute(hwnd, DwmwaUseImmersiveDarkMode, &darkBorder, sizeof(darkBorder)))
3854 || SUCCEEDED(DwmSetWindowAttribute(hwnd, DwmwaUseImmersiveDarkModeBefore20h1, &darkBorder, sizeof(darkBorder)));
3855 if (!ok)
3856 qCWarning(lcQpaWindow, "%s: Unable to set %s window border.", __FUNCTION__, d ? "dark" : "light");
3857 return ok;
3858}
3859
3861{
3862 // respect explicit opt-out and incompatible palettes or styles
3863 d = d && shouldApplyDarkFrame(window());
3864
3865 setDarkBorderToWindow(m_data.hwnd, d);
3866}
3867
3869{
3870 return m_menuBar.data();
3871}
3872
3874{
3875 m_menuBar = mb;
3876}
3877
3879{
3880 if (m_data.flags & Qt::FramelessWindowHint)
3881 return {};
3882 return m_data.customMargins;
3883}
3884
3885/*!
3886 \brief Sets custom margins to be added to the default margins determined by
3887 the windows style in the handling of the WM_NCCALCSIZE message.
3888
3889 This is currently used to give the Aero-style QWizard a smaller top margin.
3890 The property can be set using QPlatformNativeInterface::setWindowProperty() or,
3891 before platform window creation, by setting a dynamic property
3892 on the QWindow (see QWindowsIntegration::createPlatformWindow()).
3893*/
3894
3895void QWindowsWindow::setCustomMargins(const QMargins &newCustomMargins)
3896{
3897 if (m_data.flags & Qt::FramelessWindowHint) {
3898 qCWarning(lcQpaWindow) << "You should not set custom margins for a frameless window.";
3899 return;
3900 }
3901 if (newCustomMargins != m_data.customMargins) {
3902 const QMargins oldCustomMargins = m_data.customMargins;
3903 m_data.customMargins = newCustomMargins;
3904 // Re-trigger WM_NCALCSIZE with wParam=1 by passing SWP_FRAMECHANGED
3905 const QRect currentFrameGeometry = frameGeometry_sys();
3906 const QPoint topLeft = currentFrameGeometry.topLeft();
3907 QRect newFrame = currentFrameGeometry.marginsRemoved(oldCustomMargins) + m_data.customMargins;
3908 newFrame.moveTo(topLeft);
3909 qCDebug(lcQpaWindow) << __FUNCTION__ << oldCustomMargins << "->" << newCustomMargins
3910 << currentFrameGeometry << "->" << newFrame;
3911 SetWindowPos(m_data.hwnd, nullptr, newFrame.x(), newFrame.y(), newFrame.width(), newFrame.height(), SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE);
3912 }
3913}
3914
3915void *QWindowsWindow::surface(void *nativeConfig, int *err)
3916{
3917#if QT_CONFIG(vulkan)
3918 Q_UNUSED(nativeConfig);
3919 Q_UNUSED(err);
3920 if (window()->surfaceType() == QSurface::VulkanSurface) {
3921 if (!m_vkSurface) {
3922 QVulkanInstance *inst = window()->vulkanInstance();
3923 if (inst)
3924 m_vkSurface = static_cast<QWindowsVulkanInstance *>(inst->handle())->createSurface(handle());
3925 else
3926 qWarning("Attempted to create Vulkan surface without an instance; was QWindow::setVulkanInstance() called?");
3927 }
3928 // Different semantics for VkSurfaces: the return value is the address,
3929 // not the value, given that this is a 64-bit handle even on x86.
3930 return &m_vkSurface;
3931 }
3932#elif defined(QT_NO_OPENGL)
3933 Q_UNUSED(err);
3934 Q_UNUSED(nativeConfig);
3935 return nullptr;
3936#endif
3937#ifndef QT_NO_OPENGL
3938 if (!m_surface) {
3940 m_surface = staticOpenGLContext->createWindowSurface(m_data.hwnd, nativeConfig, err);
3941 }
3942
3943 return m_surface;
3944#else
3945 return nullptr;
3946#endif
3947}
3948
3950{
3951#if QT_CONFIG(vulkan)
3952 if (m_vkSurface) {
3953 QVulkanInstance *inst = window()->vulkanInstance();
3954 if (inst)
3955 static_cast<QWindowsVulkanInstance *>(inst->handle())->destroySurface(m_vkSurface);
3956 m_vkSurface = VK_NULL_HANDLE;
3957 }
3958#endif
3959#ifndef QT_NO_OPENGL
3960 if (m_surface) {
3962 staticOpenGLContext->destroyWindowSurface(m_surface);
3963 m_surface = nullptr;
3964 }
3965#endif // QT_NO_OPENGL
3966}
3967
3969{
3971 return;
3972
3973 // Initially register or re-register to change the flags
3974 const auto touchTypes = QWindowsIntegration::instance()->touchWindowTouchType();
3976 const auto currentTouchTypes = touchWindowTouchTypes_sys();
3977 if (currentTouchTypes.has_value() && currentTouchTypes.value() == touchTypes)
3978 return;
3979 }
3980
3981 ULONG touchFlags = 0;
3982 if (touchTypes.testFlag(TouchWindowTouchType::FineTouch))
3983 touchFlags |= TWF_FINETOUCH;
3984 if (touchTypes.testFlag(TouchWindowTouchType::WantPalmTouch))
3985 touchFlags |= TWF_WANTPALM;
3986 if (RegisterTouchWindow(m_data.hwnd, touchFlags))
3988 else
3989 qErrnoWarning("RegisterTouchWindow() failed for window '%s'.", qPrintable(window()->objectName()));
3990}
3991
3992void QWindowsWindow::setHasBorderInFullScreenStatic(QWindow *window, bool border)
3993{
3994 if (QPlatformWindow *handle = window->handle())
3995 static_cast<QWindowsWindow *>(handle)->setHasBorderInFullScreen(border);
3996 else
3997 window->setProperty(hasBorderInFullScreenProperty, QVariant(border));
3998}
3999
4001{
4002 m_borderInFullScreenDefault = border;
4003}
4004
4009
4011{
4012 if (hasBorderInFullScreen() == border)
4013 return;
4014 if (border)
4016 else
4018 // Directly apply the flag in case we are fullscreen.
4019 if (m_windowState == Qt::WindowFullScreen) {
4020 LONG_PTR style = GetWindowLongPtr(handle(), GWL_STYLE);
4021 if (border)
4022 style |= WS_BORDER;
4023 else
4024 style &= ~WS_BORDER;
4025 SetWindowLongPtr(handle(), GWL_STYLE, style);
4026 }
4027}
4028
4029QString QWindowsWindow::formatWindowTitle(const QString &title)
4030{
4031 return QPlatformWindow::formatWindowTitle(title, QStringLiteral(" - "));
4032}
4033
4035{
4036 enum UpdateState {
4037 Ready = 0,
4038 Requested = 1,
4039 Posted = 2
4040 };
4041 QWindow *w = window();
4042 QDxgiVSyncService *vs = QDxgiVSyncService::instance();
4043 if (vs->supportsWindow(w)) {
4044 if (m_vsyncServiceCallbackId == 0) {
4045 m_vsyncServiceCallbackId = vs->registerCallback([this, w](const QDxgiVSyncService::CallbackWindowList &windowList, qint64) {
4046 if (windowList.contains(w)) {
4047 // Make sure we only post one event at a time. If the state
4048 // isn't Requested, it means there either isn't a pending
4049 // request or we are waiting for the event loop to process
4050 // the Posted event on the GUI thread.
4051 if (m_vsyncUpdatePending.testAndSetAcquire(UpdateState::Requested, UpdateState::Posted)) {
4052 QWindowsWindow *oldSelf = this;
4053 qsizetype oldCallbackId = m_vsyncServiceCallbackId;
4054 QMetaObject::invokeMethod(w, [w, oldSelf, oldCallbackId] {
4055 // 'oldSelf' is only used for comparison, don't access it directly!
4056 auto *self = static_cast<QWindowsWindow *>(w->handle());
4057 // NOTE: In the off chance that the window got destroyed and recreated with the
4058 // same address, we also check that the callback id is the same.
4059 if (self && self == oldSelf && self->m_vsyncServiceCallbackId == oldCallbackId) {
4060 // The platform window is still alive
4061 self->m_vsyncUpdatePending.storeRelease(UpdateState::Ready);
4062 self->deliverUpdateRequest();
4063 }
4064 });
4065 }
4066 }
4067 });
4068 }
4069 m_vsyncUpdatePending.testAndSetRelease(UpdateState::Ready, UpdateState::Requested);
4070 } else {
4071 QPlatformWindow::requestUpdate();
4072 }
4073}
4074
4075QT_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