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