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