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