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