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