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