Warning: That file was not part of the compilation database. It may have many parsing errors.

1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the plugins of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#if defined(WINVER) && WINVER < 0x0601
41# undef WINVER
42#endif
43#if !defined(WINVER)
44# define WINVER 0x0601 // Enable touch functions for MinGW
45#endif
46
47#include "qwindowswindow.h"
48#include "qwindowscontext.h"
49#if QT_CONFIG(draganddrop)
50# include "qwindowsdrag.h"
51#endif
52#include "qwindowsscreen.h"
53#include "qwindowsintegration.h"
54#include "qwindowsmenu.h"
55#include "qwindowsnativeinterface.h"
56#if QT_CONFIG(dynamicgl)
57# include "qwindowsglcontext.h"
58#else
59# include "qwindowsopenglcontext.h"
60#endif
61#include "qwindowsopengltester.h"
62#ifdef QT_NO_CURSOR
63# include "qwindowscursor.h"
64#endif
65
66#include <QtGui/qguiapplication.h>
67#include <QtGui/qscreen.h>
68#include <QtGui/qwindow.h>
69#include <QtGui/qregion.h>
70#include <QtGui/qopenglcontext.h>
71#include <private/qsystemlibrary_p.h>
72#include <private/qwindow_p.h> // QWINDOWSIZE_MAX
73#include <private/qguiapplication_p.h>
74#include <private/qhighdpiscaling_p.h>
75#include <qpa/qwindowsysteminterface.h>
76
77#include <QtCore/qdebug.h>
78#include <QtCore/qlibraryinfo.h>
79#include <QtCore/qoperatingsystemversion.h>
80
81#include <dwmapi.h>
82
83#if QT_CONFIG(vulkan)
84#include "qwindowsvulkaninstance.h"
85#endif
86
87QT_BEGIN_NAMESPACE
88
89using QWindowCreationContextPtr = QSharedPointer<QWindowCreationContext>;
90
91enum {
92 defaultWindowWidth = 160,
93 defaultWindowHeight = 160
94};
95
96Q_GUI_EXPORT HICON qt_pixmapToWinHICON(const QPixmap &);
97
98static QByteArray debugWinStyle(DWORD style)
99{
100 QByteArray rc = "0x";
101 rc += QByteArray::number(qulonglong(style), 16);
102 if (style & WS_POPUP)
103 rc += " WS_POPUP";
104 if (style & WS_CHILD)
105 rc += " WS_CHILD";
106 if (style & WS_OVERLAPPED)
107 rc += " WS_OVERLAPPED";
108 if (style & WS_CLIPSIBLINGS)
109 rc += " WS_CLIPSIBLINGS";
110 if (style & WS_CLIPCHILDREN)
111 rc += " WS_CLIPCHILDREN";
112 if (style & WS_THICKFRAME)
113 rc += " WS_THICKFRAME";
114 if (style & WS_DLGFRAME)
115 rc += " WS_DLGFRAME";
116 if (style & WS_SYSMENU)
117 rc += " WS_SYSMENU";
118 if (style & WS_MINIMIZEBOX)
119 rc += " WS_MINIMIZEBOX";
120 if (style & WS_MAXIMIZEBOX)
121 rc += " WS_MAXIMIZEBOX";
122 return rc;
123}
124
125static QByteArray debugWinExStyle(DWORD exStyle)
126{
127 QByteArray rc = "0x";
128 rc += QByteArray::number(qulonglong(exStyle), 16);
129 if (exStyle & WS_EX_TOOLWINDOW)
130 rc += " WS_EX_TOOLWINDOW";
131 if (exStyle & WS_EX_CONTEXTHELP)
132 rc += " WS_EX_CONTEXTHELP";
133 if (exStyle & WS_EX_LAYERED)
134 rc += " WS_EX_LAYERED";
135 if (exStyle & WS_EX_DLGMODALFRAME)
136 rc += " WS_EX_DLGMODALFRAME";
137 if (exStyle & WS_EX_LAYOUTRTL)
138 rc += " WS_EX_LAYOUTRTL";
139 if (exStyle & WS_EX_NOINHERITLAYOUT)
140 rc += " WS_EX_NOINHERITLAYOUT";
141 return rc;
142}
143
144static QByteArray debugWinSwpPos(UINT flags)
145{
146 QByteArray rc = "0x";
147 rc += QByteArray::number(flags, 16);
148 if (flags & SWP_FRAMECHANGED)
149 rc += " SWP_FRAMECHANGED";
150 if (flags & SWP_HIDEWINDOW)
151 rc += " SWP_HIDEWINDOW";
152 if (flags & SWP_NOACTIVATE)
153 rc += " SWP_NOACTIVATE";
154 if (flags & SWP_NOCOPYBITS)
155 rc += " SWP_NOCOPYBITS";
156 if (flags & SWP_NOMOVE)
157 rc += " SWP_NOMOVE";
158 if (flags & SWP_NOOWNERZORDER)
159 rc += " SWP_NOOWNERZORDER";
160 if (flags & SWP_NOREDRAW)
161 rc += " SWP_NOREDRAW";
162 if (flags & SWP_NOSENDCHANGING)
163 rc += " SWP_NOSENDCHANGING";
164 if (flags & SWP_NOSIZE)
165 rc += " SWP_NOSIZE";
166 if (flags & SWP_NOZORDER)
167 rc += " SWP_NOZORDER";
168 if (flags & SWP_SHOWWINDOW)
169 rc += " SWP_SHOWWINDOW";
170 return rc;
171}
172
173static inline QSize qSizeOfRect(const RECT &rect)
174{
175 return QSize(rect.right -rect.left, rect.bottom - rect.top);
176}
177
178static inline QRect qrectFromRECT(const RECT &rect)
179{
180 return QRect(QPoint(rect.left, rect.top), qSizeOfRect(rect));
181}
182
183static inline RECT RECTfromQRect(const QRect &rect)
184{
185 const int x = rect.left();
186 const int y = rect.top();
187 RECT result = { x, y, x + rect.width(), y + rect.height() };
188 return result;
189}
190
191
192#ifndef QT_NO_DEBUG_STREAM
193QDebug operator<<(QDebug d, const RECT &r)
194{
195 QDebugStateSaver saver(d);
196 d.nospace();
197 d << "RECT(left=" << r.left << ", top=" << r.top
198 << ", right=" << r.right << ", bottom=" << r.bottom
199 << " (" << r.right - r.left << 'x' << r.bottom - r.top << "))";
200 return d;
201}
202
203QDebug operator<<(QDebug d, const POINT &p)
204{
205 d << p.x << ',' << p.y;
206 return d;
207}
208
209QDebug operator<<(QDebug d, const WINDOWPOS &wp)
210{
211 QDebugStateSaver saver(d);
212 d.nospace();
213 d.noquote();
214 d << "WINDOWPOS(flags=" << debugWinSwpPos(wp.flags) << ", hwnd="
215 << wp.hwnd << ", hwndInsertAfter=" << wp.hwndInsertAfter << ", x=" << wp.x
216 << ", y=" << wp.y << ", cx=" << wp.cx << ", cy=" << wp.cy << ')';
217 return d;
218}
219
220QDebug operator<<(QDebug d, const NCCALCSIZE_PARAMS &p)
221{
222 QDebugStateSaver saver(d);
223 d.nospace();
224 d << "NCCALCSIZE_PARAMS(rgrc=[" << p.rgrc[0] << ' ' << p.rgrc[1] << ' '
225 << p.rgrc[2] << "], lppos=" << *p.lppos << ')';
226 return d;
227}
228
229QDebug operator<<(QDebug d, const MINMAXINFO &i)
230{
231 QDebugStateSaver saver(d);
232 d.nospace();
233 d << "MINMAXINFO maxSize=" << i.ptMaxSize.x << ','
234 << i.ptMaxSize.y << " maxpos=" << i.ptMaxPosition.x
235 << ',' << i.ptMaxPosition.y << " mintrack="
236 << i.ptMinTrackSize.x << ',' << i.ptMinTrackSize.y
237 << " maxtrack=" << i.ptMaxTrackSize.x << ',' << i.ptMaxTrackSize.y;
238 return d;
239}
240
241QDebug operator<<(QDebug d, const WINDOWPLACEMENT &wp)
242{
243 QDebugStateSaver saver(d);
244 d.nospace();
245 d.noquote();
246 d << "WINDOWPLACEMENT(flags=0x" << Qt::hex << wp.flags << Qt::dec << ", showCmd="
247 << wp.showCmd << ", ptMinPosition=" << wp.ptMinPosition << ", ptMaxPosition=" << wp.ptMaxPosition
248 << ", rcNormalPosition=" << wp.rcNormalPosition;
249 return d;
250}
251
252QDebug operator<<(QDebug d, const GUID &guid)
253{
254 QDebugStateSaver saver(d);
255 d.nospace();
256 d << '{' << Qt::hex << Qt::uppercasedigits << qSetPadChar(QLatin1Char('0'))
257 << qSetFieldWidth(8) << guid.Data1
258 << qSetFieldWidth(0) << '-' << qSetFieldWidth(4)
259 << guid.Data2 << qSetFieldWidth(0) << '-' << qSetFieldWidth(4)
260 << guid.Data3 << qSetFieldWidth(0) << '-' << qSetFieldWidth(4)
261 << qSetFieldWidth(2) << guid.Data4[0] << guid.Data4[1]
262 << qSetFieldWidth(0) << '-' << qSetFieldWidth(2);
263 for (int i = 2; i < 8; ++i)
264 d << guid.Data4[i];
265 d << qSetFieldWidth(0) << '}';
266 return d;
267}
268#endif // !QT_NO_DEBUG_STREAM
269
270static void formatBriefRectangle(QDebug &d, const QRect &r)
271{
272 d << r.width() << 'x' << r.height() << forcesign << r.x() << r.y() << noforcesign;
273}
274
275static void formatBriefMargins(QDebug &d, const QMargins &m)
276{
277 d << m.left() << ", " << m.top() << ", " << m.right() << ", " << m.bottom();
278}
279
280// QTBUG-43872, for windows that do not have WS_EX_TOOLWINDOW set, WINDOWPLACEMENT
281// is in workspace/available area coordinates.
282static QPoint windowPlacementOffset(HWND hwnd, const QPoint &point)
283{
284 if (GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_TOOLWINDOW)
285 return QPoint(0, 0);
286 const QWindowsScreenManager &screenManager = QWindowsContext::instance()->screenManager();
287 const QWindowsScreen *screen = screenManager.screens().size() == 1
288 ? screenManager.screens().constFirst() : screenManager.screenAtDp(point);
289 if (screen)
290 return screen->availableGeometry().topLeft() - screen->geometry().topLeft();
291 return QPoint(0, 0);
292}
293
294// Return the frame geometry relative to the parent
295// if there is one.
296static inline QRect frameGeometry(HWND hwnd, bool topLevel)
297{
298 RECT rect = { 0, 0, 0, 0 };
299 if (topLevel) {
300 WINDOWPLACEMENT windowPlacement;
301 windowPlacement.length = sizeof(WINDOWPLACEMENT);
302 GetWindowPlacement(hwnd, &windowPlacement);
303 if (windowPlacement.showCmd == SW_SHOWMINIMIZED) {
304 const QRect result = qrectFromRECT(windowPlacement.rcNormalPosition);
305 return result.translated(windowPlacementOffset(hwnd, result.topLeft()));
306 }
307 }
308 GetWindowRect(hwnd, &rect); // Screen coordinates.
309 const HWND parent = GetParent(hwnd);
310 if (parent && !topLevel) {
311 const int width = rect.right - rect.left;
312 const int height = rect.bottom - rect.top;
313 POINT leftTop = { rect.left, rect.top };
314 screenToClient(parent, &leftTop);
315 rect.left = leftTop.x;
316 rect.top = leftTop.y;
317 rect.right = leftTop.x + width;
318 rect.bottom = leftTop.y + height;
319 }
320 return qrectFromRECT(rect);
321}
322
323// Return the visibility of the Window (except full screen since it is not a window state).
324static QWindow::Visibility windowVisibility_sys(HWND hwnd)
325{
326 if (!IsWindowVisible(hwnd))
327 return QWindow::Hidden;
328 WINDOWPLACEMENT windowPlacement;
329 windowPlacement.length = sizeof(WINDOWPLACEMENT);
330 if (GetWindowPlacement(hwnd, &windowPlacement)) {
331 switch (windowPlacement.showCmd) {
332 case SW_SHOWMINIMIZED:
333 case SW_MINIMIZE:
334 case SW_FORCEMINIMIZE:
335 return QWindow::Minimized;
336 case SW_SHOWMAXIMIZED:
337 return QWindow::Maximized;
338 default:
339 break;
340 }
341 }
342 return QWindow::Windowed;
343}
344
345static inline bool windowIsAccelerated(const QWindow *w)
346{
347 switch (w->surfaceType()) {
348 case QSurface::OpenGLSurface:
349 return true;
350 case QSurface::RasterGLSurface:
351 return qt_window_private(const_cast<QWindow *>(w))->compositing;
352 case QSurface::VulkanSurface:
353 return true;
354 default:
355 return false;
356 }
357}
358
359static bool applyBlurBehindWindow(HWND hwnd)
360{
361 BOOL compositionEnabled;
362 if (DwmIsCompositionEnabled(&compositionEnabled) != S_OK)
363 return false;
364
365 DWM_BLURBEHIND blurBehind = {0, 0, nullptr, 0};
366
367 if (compositionEnabled) {
368 blurBehind.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
369 blurBehind.fEnable = TRUE;
370 blurBehind.hRgnBlur = CreateRectRgn(0, 0, -1, -1);
371 } else {
372 blurBehind.dwFlags = DWM_BB_ENABLE;
373 blurBehind.fEnable = FALSE;
374 }
375
376 const bool result = DwmEnableBlurBehindWindow(hwnd, &blurBehind) == S_OK;
377
378 if (blurBehind.hRgnBlur)
379 DeleteObject(blurBehind.hRgnBlur);
380
381 return result;
382}
383
384// from qwidget_win.cpp, pass flags separately in case they have been "autofixed".
385static bool shouldShowMaximizeButton(const QWindow *w, Qt::WindowFlags flags)
386{
387 if ((flags & Qt::MSWindowsFixedSizeDialogHint) || !(flags & Qt::WindowMaximizeButtonHint))
388 return false;
389 // if the user explicitly asked for the maximize button, we try to add
390 // it even if the window has fixed size.
391 return (flags & Qt::CustomizeWindowHint) ||
392 w->maximumSize() == QSize(QWINDOWSIZE_MAX, QWINDOWSIZE_MAX);
393}
394
395// Set the WS_EX_LAYERED flag on a HWND if required. This is required for
396// translucent backgrounds, not fully opaque windows and for
397// Qt::WindowTransparentForInput (in combination with WS_EX_TRANSPARENT).
398bool QWindowsWindow::setWindowLayered(HWND hwnd, Qt::WindowFlags flags, bool hasAlpha, qreal opacity)
399{
400 const LONG exStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
401 const bool needsLayered = (flags & Qt::WindowTransparentForInput)
402 || (hasAlpha && (flags & Qt::FramelessWindowHint)) || opacity < 1.0;
403 const bool isLayered = (exStyle & WS_EX_LAYERED);
404 if (needsLayered != isLayered) {
405 if (needsLayered) {
406 SetWindowLong(hwnd, GWL_EXSTYLE, exStyle | WS_EX_LAYERED);
407 } else {
408 SetWindowLong(hwnd, GWL_EXSTYLE, exStyle & ~WS_EX_LAYERED);
409 }
410 }
411 return needsLayered;
412}
413
414static void setWindowOpacity(HWND hwnd, Qt::WindowFlags flags, bool hasAlpha, bool accelerated, qreal level)
415{
416 if (QWindowsWindow::setWindowLayered(hwnd, flags, hasAlpha, level)) {
417 const BYTE alpha = BYTE(qRound(255.0 * level));
418 if (hasAlpha && !accelerated && (flags & Qt::FramelessWindowHint)) {
419 // Non-GL windows with alpha: Use blend function to update.
420 BLENDFUNCTION blend = {AC_SRC_OVER, 0, alpha, AC_SRC_ALPHA};
421 UpdateLayeredWindow(hwnd, nullptr, nullptr, nullptr, nullptr, nullptr, 0, &blend, ULW_ALPHA);
422 } else {
423 SetLayeredWindowAttributes(hwnd, 0, alpha, LWA_ALPHA);
424 }
425 } else if (IsWindowVisible(hwnd)) { // Repaint when switching from layered.
426 InvalidateRect(hwnd, nullptr, TRUE);
427 }
428}
429
430static inline void updateGLWindowSettings(const QWindow *w, HWND hwnd, Qt::WindowFlags flags, qreal opacity)
431{
432 const bool isAccelerated = windowIsAccelerated(w);
433 const bool hasAlpha = w->format().hasAlpha();
434
435 if (isAccelerated && hasAlpha)
436 applyBlurBehindWindow(hwnd);
437
438 setWindowOpacity(hwnd, flags, hasAlpha, isAccelerated, opacity);
439}
440
441/*!
442 Calculates the dimensions of the invisible borders within the
443 window frames in Windows 10, using an empirical expression that
444 reproduces the measured values for standard DPI settings.
445*/
446
447static QMargins invisibleMargins(QPoint screenPoint)
448{
449 if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10) {
450 POINT pt = {screenPoint.x(), screenPoint.y()};
451 if (HMONITOR hMonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONULL)) {
452 if (QWindowsContext::shcoredll.isValid()) {
453 UINT dpiX;
454 UINT dpiY;
455 if (SUCCEEDED(QWindowsContext::shcoredll.getDpiForMonitor(hMonitor, 0, &dpiX, &dpiY))) {
456 const qreal sc = (dpiX - 96) / 96.0;
457 const int gap = 7 + qRound(5*sc) - int(sc);
458 return QMargins(gap, 0, gap, gap);
459 }
460 }
461 }
462 }
463 return QMargins();
464}
465
466/*!
467 \class WindowCreationData
468 \brief Window creation code.
469
470 This struct gathers all information required to create a window.
471 Window creation is split in 3 steps:
472
473 \list
474 \li fromWindow() Gather all required information
475 \li create() Create the system handle.
476 \li initialize() Post creation initialization steps.
477 \endlist
478
479 The reason for this split is to also enable changing the QWindowFlags
480 by calling:
481
482 \list
483 \li fromWindow() Gather information and determine new system styles
484 \li applyWindowFlags() to apply the new window system styles.
485 \li initialize() Post creation initialization steps.
486 \endlist
487
488 Contains the window creation code formerly in qwidget_win.cpp.
489
490 \sa QWindowCreationContext
491 \internal
492 \ingroup qt-lighthouse-win
493*/
494
495struct WindowCreationData
496{
497 using WindowData = QWindowsWindowData;
498 enum Flags { ForceChild = 0x1, ForceTopLevel = 0x2 };
499
500 void fromWindow(const QWindow *w, const Qt::WindowFlags flags, unsigned creationFlags = 0);
501 inline WindowData create(const QWindow *w, const WindowData &data, QString title) const;
502 inline void applyWindowFlags(HWND hwnd) const;
503 void initialize(const QWindow *w, HWND h, bool frameChange, qreal opacityLevel) const;
504
505 Qt::WindowFlags flags;
506 HWND parentHandle = nullptr;
507 Qt::WindowType type = Qt::Widget;
508 unsigned style = 0;
509 unsigned exStyle = 0;
510 bool topLevel = false;
511 bool popup = false;
512 bool dialog = false;
513 bool tool = false;
514 bool embedded = false;
515 bool hasAlpha = false;
516};
517
518QDebug operator<<(QDebug debug, const WindowCreationData &d)
519{
520 QDebugStateSaver saver(debug);
521 debug.nospace();
522 debug.noquote();
523 debug << "WindowCreationData: " << d.flags
524 << "\n topLevel=" << d.topLevel;
525 if (d.parentHandle)
526 debug << " parent=" << d.parentHandle;
527 debug << " popup=" << d.popup << " dialog=" << d.dialog
528 << " embedded=" << d.embedded << " tool=" << d.tool
529 << "\n style=" << debugWinStyle(d.style);
530 if (d.exStyle)
531 debug << "\n exStyle=" << debugWinExStyle(d.exStyle);
532 return debug;
533}
534
535// Fix top level window flags in case only the type flags are passed.
536static inline void fixTopLevelWindowFlags(Qt::WindowFlags &flags)
537{
538 // Not supported on Windows, also do correction when it is set.
539 flags &= ~Qt::WindowFullscreenButtonHint;
540 switch (flags) {
541 case Qt::Window:
542 flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowMinimizeButtonHint
543 |Qt::WindowMaximizeButtonHint|Qt::WindowCloseButtonHint;
544 break;
545 case Qt::Dialog:
546 case Qt::Tool:
547 flags |= Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint;
548 break;
549 default:
550 break;
551 }
552 if ((flags & Qt::WindowType_Mask) == Qt::SplashScreen)
553 flags |= Qt::FramelessWindowHint;
554}
555
556static QScreen *screenForName(const QWindow *w, const QString &name)
557{
558 QScreen *winScreen = w ? w->screen() : QGuiApplication::primaryScreen();
559 if (winScreen && winScreen->name() != name) {
560 const auto screens = winScreen->virtualSiblings();
561 for (QScreen *screen : screens) {
562 if (screen->name() == name)
563 return screen;
564 }
565 }
566 return winScreen;
567}
568
569static QPoint calcPosition(const QWindow *w, const QWindowCreationContextPtr &context, const QMargins &invMargins)
570{
571 const QPoint orgPos(context->frameX - invMargins.left(), context->frameY - invMargins.top());
572
573 if (!w || (!w->isTopLevel() && w->surfaceType() != QWindow::OpenGLSurface))
574 return orgPos;
575
576 // Workaround for QTBUG-50371
577 const QScreen *screenForGL = QWindowsWindow::forcedScreenForGLWindow(w);
578 if (!screenForGL)
579 return orgPos;
580
581 const QPoint posFrame(context->frameX, context->frameY);
582 const QMargins margins = context->margins;
583 const QRect scrGeo = screenForGL->handle()->availableGeometry();
584
585 // Point is already in the required screen.
586 if (scrGeo.contains(orgPos))
587 return orgPos;
588
589 // If the visible part of the window is already in the
590 // required screen, just ignore the invisible offset.
591 if (scrGeo.contains(posFrame))
592 return posFrame;
593
594 // Find the original screen containing the coordinates.
595 const QList<QScreen *> screens = screenForGL->virtualSiblings();
596 const QScreen *orgScreen = nullptr;
597 for (QScreen *screen : screens) {
598 if (screen->handle()->availableGeometry().contains(posFrame)) {
599 orgScreen = screen;
600 break;
601 }
602 }
603 const QPoint ctPos = QPoint(qMax(scrGeo.left(), scrGeo.center().x()
604 + (margins.right() - margins.left() - context->frameWidth)/2),
605 qMax(scrGeo.top(), scrGeo.center().y()
606 + (margins.bottom() - margins.top() - context->frameHeight)/2));
607
608 // If initial coordinates were outside all screens, center the window on the required screen.
609 if (!orgScreen)
610 return ctPos;
611
612 const QRect orgGeo = orgScreen->handle()->availableGeometry();
613 const QRect orgFrame(QPoint(context->frameX, context->frameY),
614 QSize(context->frameWidth, context->frameHeight));
615
616 // Window would be centered on orgScreen. Center it on the required screen.
617 if (orgGeo.center() == (orgFrame - margins).center())
618 return ctPos;
619
620 // Transform the coordinates to map them into the required screen.
621 const QPoint newPos(scrGeo.left() + ((posFrame.x() - orgGeo.left()) * scrGeo.width()) / orgGeo.width(),
622 scrGeo.top() + ((posFrame.y() - orgGeo.top()) * scrGeo.height()) / orgGeo.height());
623 const QPoint newPosNoMargin(newPos.x() - invMargins.left(), newPos.y() - invMargins.top());
624
625 return scrGeo.contains(newPosNoMargin) ? newPosNoMargin : newPos;
626}
627
628void WindowCreationData::fromWindow(const QWindow *w, const Qt::WindowFlags flagsIn,
629 unsigned creationFlags)
630{
631 flags = flagsIn;
632
633 // Sometimes QWindow doesn't have a QWindow parent but does have a native parent window,
634 // e.g. in case of embedded ActiveQt servers. They should not be considered a top-level
635 // windows in such cases.
636 QVariant prop = w->property(QWindowsWindow::embeddedNativeParentHandleProperty);
637 if (prop.isValid()) {
638 embedded = true;
639 parentHandle = reinterpret_cast<HWND>(prop.value<WId>());
640 }
641
642 if (creationFlags & ForceChild) {
643 topLevel = false;
644 } else if (embedded) {
645 // Embedded native windows (for example Active X server windows) are by
646 // definition never toplevel, even though they do not have QWindow parents.
647 topLevel = false;
648 } else {
649 topLevel = (creationFlags & ForceTopLevel) ? true : w->isTopLevel();
650 }
651
652 if (topLevel)
653 fixTopLevelWindowFlags(flags);
654
655 type = static_cast<Qt::WindowType>(int(flags) & Qt::WindowType_Mask);
656 switch (type) {
657 case Qt::Dialog:
658 case Qt::Sheet:
659 dialog = true;
660 break;
661 case Qt::Drawer:
662 case Qt::Tool:
663 tool = true;
664 break;
665 case Qt::Popup:
666 popup = true;
667 break;
668 default:
669 break;
670 }
671 if ((flags & Qt::MSWindowsFixedSizeDialogHint))
672 dialog = true;
673
674 // This causes the title bar to drawn RTL and the close button
675 // to be left. Note that this causes:
676 // - All DCs created on the Window to have RTL layout (see SetLayout)
677 // - ClientToScreen() and ScreenToClient() to work in reverse as well.
678 // - Mouse event coordinates to be mirrored.
679 // - Positioning of child Windows.
680 if (QGuiApplication::layoutDirection() == Qt::RightToLeft
681 && (QWindowsIntegration::instance()->options() & QWindowsIntegration::RtlEnabled) != 0) {
682 exStyle |= WS_EX_LAYOUTRTL | WS_EX_NOINHERITLAYOUT;
683 }
684
685 // Parent: Use transient parent for top levels.
686 if (popup) {
687 flags |= Qt::WindowStaysOnTopHint; // a popup stays on top, no parent.
688 } else if (!embedded) {
689 if (const QWindow *parentWindow = topLevel ? w->transientParent() : w->parent())
690 parentHandle = QWindowsWindow::handleOf(parentWindow);
691 }
692
693 if (popup || (type == Qt::ToolTip) || (type == Qt::SplashScreen)) {
694 style = WS_POPUP;
695 } else if (topLevel) {
696 if (flags & Qt::FramelessWindowHint)
697 style = WS_POPUP; // no border
698 else if (flags & Qt::WindowTitleHint)
699 style = WS_OVERLAPPED;
700 else
701 style = 0;
702 } else {
703 style = WS_CHILD;
704 }
705
706 // if (!testAttribute(Qt::WA_PaintUnclipped))
707 // ### Commented out for now as it causes some problems, but
708 // this should be correct anyway, so dig some more into this
709#ifdef Q_FLATTEN_EXPOSE
710 if (windowIsOpenGL(w)) // a bit incorrect since the is-opengl status may change from false to true at any time later on
711 style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN; // see SetPixelFormat
712#else
713 style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN ;
714#endif
715 if (topLevel) {
716 if ((type == Qt::Window || dialog || tool)) {
717 if (!(flags & Qt::FramelessWindowHint)) {
718 style |= WS_POPUP;
719 if (flags & Qt::MSWindowsFixedSizeDialogHint) {
720 style |= WS_DLGFRAME;
721 } else {
722 style |= WS_THICKFRAME;
723 }
724 if (flags & Qt::WindowTitleHint)
725 style |= WS_CAPTION; // Contains WS_DLGFRAME
726 }
727 if (flags & Qt::WindowSystemMenuHint)
728 style |= WS_SYSMENU;
729 else if (dialog && (flags & Qt::WindowCloseButtonHint) && !(flags & Qt::FramelessWindowHint)) {
730 style |= WS_SYSMENU | WS_BORDER; // QTBUG-2027, dialogs without system menu.
731 exStyle |= WS_EX_DLGMODALFRAME;
732 }
733 if (flags & Qt::WindowMinimizeButtonHint)
734 style |= WS_MINIMIZEBOX;
735 if (shouldShowMaximizeButton(w, flags))
736 style |= WS_MAXIMIZEBOX;
737 if (tool)
738 exStyle |= WS_EX_TOOLWINDOW;
739 if (flags & Qt::WindowContextHelpButtonHint)
740 exStyle |= WS_EX_CONTEXTHELP;
741 } else {
742 exStyle |= WS_EX_TOOLWINDOW;
743 }
744
745 // make mouse events fall through this window
746 // NOTE: WS_EX_TRANSPARENT flag can make mouse inputs fall through a layered window
747 if (flagsIn & Qt::WindowTransparentForInput)
748 exStyle |= WS_EX_LAYERED | WS_EX_TRANSPARENT;
749 }
750}
751
752QWindowsWindowData
753 WindowCreationData::create(const QWindow *w, const WindowData &data, QString title) const
754{
755 WindowData result;
756 result.flags = flags;
757
758 const auto appinst = reinterpret_cast<HINSTANCE>(GetModuleHandle(nullptr));
759
760 const QString windowClassName = QWindowsContext::instance()->registerWindowClass(w);
761
762 const QRect rect = QPlatformWindow::initialGeometry(w, data.geometry, defaultWindowWidth, defaultWindowHeight);
763
764 if (title.isEmpty() && (result.flags & Qt::WindowTitleHint))
765 title = topLevel ? qAppName() : w->objectName();
766
767 const auto *titleUtf16 = reinterpret_cast<const wchar_t *>(title.utf16());
768 const auto *classNameUtf16 = reinterpret_cast<const wchar_t *>(windowClassName.utf16());
769
770 // Capture events before CreateWindowEx() returns. The context is cleared in
771 // the QWindowsWindow constructor.
772 const QWindowCreationContextPtr context(new QWindowCreationContext(w, data.geometry, rect, data.customMargins, style, exStyle));
773 QWindowsContext::instance()->setWindowCreationContext(context);
774
775 const bool hasFrame = (style & (WS_DLGFRAME | WS_THICKFRAME));
776 QMargins invMargins = topLevel && hasFrame && QWindowsGeometryHint::positionIncludesFrame(w)
777 ? invisibleMargins(QPoint(context->frameX, context->frameY)) : QMargins();
778
779 qCDebug(lcQpaWindows).nospace()
780 << "CreateWindowEx: " << w << " class=" << windowClassName << " title=" << title
781 << '\n' << *this << "\nrequested: " << rect << ": "
782 << context->frameWidth << 'x' << context->frameHeight
783 << '+' << context->frameX << '+' << context->frameY
784 << " custom margins: " << context->customMargins
785 << " invisible margins: " << invMargins;
786
787
788 QPoint pos = calcPosition(w, context, invMargins);
789
790 // Mirror the position when creating on a parent in RTL mode, ditto for the obtained geometry.
791 int mirrorParentWidth = 0;
792 if (!w->isTopLevel() && QWindowsBaseWindow::isRtlLayout(parentHandle)) {
793 RECT rect;
794 GetClientRect(parentHandle, &rect);
795 mirrorParentWidth = rect.right;
796 }
797 if (mirrorParentWidth != 0 && pos.x() != CW_USEDEFAULT && context->frameWidth != CW_USEDEFAULT)
798 pos.setX(mirrorParentWidth - context->frameWidth - pos.x());
799
800 result.hwnd = CreateWindowEx(exStyle, classNameUtf16, titleUtf16,
801 style,
802 pos.x(), pos.y(),
803 context->frameWidth, context->frameHeight,
804 parentHandle, nullptr, appinst, nullptr);
805 qCDebug(lcQpaWindows).nospace()
806 << "CreateWindowEx: returns " << w << ' ' << result.hwnd << " obtained geometry: "
807 << context->obtainedPos << context->obtainedSize << ' ' << context->margins;
808
809 if (!result.hwnd) {
810 qErrnoWarning("%s: CreateWindowEx failed", __FUNCTION__);
811 return result;
812 }
813
814 if (mirrorParentWidth != 0) {
815 context->obtainedPos.setX(mirrorParentWidth - context->obtainedSize.width()
816 - context->obtainedPos.x());
817 }
818
819 QRect obtainedGeometry(context->obtainedPos, context->obtainedSize);
820
821 result.geometry = obtainedGeometry;
822 result.fullFrameMargins = context->margins;
823 result.embedded = embedded;
824 result.hasFrame = hasFrame;
825 result.customMargins = context->customMargins;
826
827 return result;
828}
829
830void WindowCreationData::applyWindowFlags(HWND hwnd) const
831{
832 // Keep enabled and visible from the current style.
833 const LONG_PTR oldStyle = GetWindowLongPtr(hwnd, GWL_STYLE);
834 const LONG_PTR oldExStyle = GetWindowLongPtr(hwnd, GWL_EXSTYLE);
835
836 const LONG_PTR newStyle = style | (oldStyle & (WS_DISABLED|WS_VISIBLE));
837 if (oldStyle != newStyle)
838 SetWindowLongPtr(hwnd, GWL_STYLE, newStyle);
839 const LONG_PTR newExStyle = exStyle;
840 if (newExStyle != oldExStyle)
841 SetWindowLongPtr(hwnd, GWL_EXSTYLE, newExStyle);
842 qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << hwnd << *this
843 << "\n Style from " << debugWinStyle(DWORD(oldStyle)) << "\n to "
844 << debugWinStyle(DWORD(newStyle)) << "\n ExStyle from "
845 << debugWinExStyle(DWORD(oldExStyle)) << " to "
846 << debugWinExStyle(DWORD(newExStyle));
847}
848
849void WindowCreationData::initialize(const QWindow *w, HWND hwnd, bool frameChange, qreal opacityLevel) const
850{
851 if (!hwnd)
852 return;
853 UINT swpFlags = SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER;
854 if (frameChange)
855 swpFlags |= SWP_FRAMECHANGED;
856 if (topLevel) {
857 swpFlags |= SWP_NOACTIVATE;
858 if ((flags & Qt::WindowStaysOnTopHint) || (type == Qt::ToolTip)) {
859 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, swpFlags);
860 if (flags & Qt::WindowStaysOnBottomHint)
861 qWarning("QWidget: Incompatible window flags: the window can't be on top and on bottom at the same time");
862 } else if (flags & Qt::WindowStaysOnBottomHint) {
863 SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0, swpFlags);
864 } else if (frameChange) { // Force WM_NCCALCSIZE with wParam=1 in case of custom margins.
865 SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, swpFlags);
866 }
867 if (flags & (Qt::CustomizeWindowHint|Qt::WindowTitleHint)) {
868 HMENU systemMenu = GetSystemMenu(hwnd, FALSE);
869 if (flags & Qt::WindowCloseButtonHint)
870 EnableMenuItem(systemMenu, SC_CLOSE, MF_BYCOMMAND|MF_ENABLED);
871 else
872 EnableMenuItem(systemMenu, SC_CLOSE, MF_BYCOMMAND|MF_GRAYED);
873 }
874 updateGLWindowSettings(w, hwnd, flags, opacityLevel);
875 } else { // child.
876 SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, swpFlags);
877 }
878}
879
880
881// Scaling helpers for size constraints.
882static QSize toNativeSizeConstrained(QSize dip, const QWindow *w)
883{
884 if (QHighDpiScaling::isActive()) {
885 const qreal factor = QHighDpiScaling::factor(w);
886 if (!qFuzzyCompare(factor, qreal(1))) {
887 if (dip.width() > 0 && dip.width() < QWINDOWSIZE_MAX)
888 dip.setWidth(qRound(qreal(dip.width()) * factor));
889 if (dip.height() > 0 && dip.height() < QWINDOWSIZE_MAX)
890 dip.setHeight(qRound(qreal(dip.height()) * factor));
891 }
892 }
893 return dip;
894}
895
896/*!
897 \class QWindowsGeometryHint
898 \brief Stores geometry constraints and provides utility functions.
899
900 Geometry constraints ready to apply to a MINMAXINFO taking frame
901 into account.
902
903 \internal
904 \ingroup qt-lighthouse-win
905*/
906
907QMargins QWindowsGeometryHint::frameOnPrimaryScreen(DWORD style, DWORD exStyle)
908{
909 RECT rect = {0,0,0,0};
910 style &= ~DWORD(WS_OVERLAPPED); // Not permitted, see docs.
911 if (AdjustWindowRectEx(&rect, style, FALSE, exStyle) == FALSE)
912 qErrnoWarning("%s: AdjustWindowRectEx failed", __FUNCTION__);
913 const QMargins result(qAbs(rect.left), qAbs(rect.top),
914 qAbs(rect.right), qAbs(rect.bottom));
915 qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << " style="
916 << showbase << hex << style << " exStyle=" << exStyle << dec << noshowbase
917 << ' ' << rect << ' ' << result;
918 return result;
919}
920
921QMargins QWindowsGeometryHint::frameOnPrimaryScreen(HWND hwnd)
922{
923 return frameOnPrimaryScreen(DWORD(GetWindowLongPtr(hwnd, GWL_STYLE)),
924 DWORD(GetWindowLongPtr(hwnd, GWL_EXSTYLE)));
925}
926
927QMargins QWindowsGeometryHint::frame(DWORD style, DWORD exStyle, qreal dpi)
928{
929 if (QWindowsContext::user32dll.adjustWindowRectExForDpi == nullptr)
930 return frameOnPrimaryScreen(style, exStyle);
931 RECT rect = {0,0,0,0};
932 style &= ~DWORD(WS_OVERLAPPED); // Not permitted, see docs.
933 if (QWindowsContext::user32dll.adjustWindowRectExForDpi(&rect, style, FALSE, exStyle,
934 unsigned(qRound(dpi))) == FALSE) {
935 qErrnoWarning("%s: AdjustWindowRectExForDpi failed", __FUNCTION__);
936 }
937 const QMargins result(qAbs(rect.left), qAbs(rect.top),
938 qAbs(rect.right), qAbs(rect.bottom));
939 qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << " style="
940 << Qt::showbase << Qt::hex << style << " exStyle=" << exStyle << Qt::dec << Qt::noshowbase
941 << " dpi=" << dpi
942 << ' ' << rect << ' ' << result;
943 return result;
944}
945
946QMargins QWindowsGeometryHint::frame(HWND hwnd, DWORD style, DWORD exStyle)
947{
948 if (QWindowsScreenManager::isSingleScreen())
949 return frameOnPrimaryScreen(style, exStyle);
950 auto screenManager = QWindowsContext::instance()->screenManager();
951 auto screen = screenManager.screenForHwnd(hwnd);
952 if (!screen)
953 screen = screenManager.screens().value(0);
954 const auto dpi = screen ? screen->logicalDpi().first : qreal(96);
955 return frame(style, exStyle, dpi);
956}
957
958// For newly created windows.
959QMargins QWindowsGeometryHint::frame(const QWindow *w, const QRect &geometry,
960 DWORD style, DWORD exStyle)
961{
962 if (!w->isTopLevel() || w->flags().testFlag(Qt::FramelessWindowHint))
963 return {};
964 if (!QWindowsContext::user32dll.adjustWindowRectExForDpi
965 || QWindowsScreenManager::isSingleScreen()
966 || !QWindowsContext::shouldHaveNonClientDpiScaling(w)) {
967 return frameOnPrimaryScreen(style, exStyle);
968 }
969 qreal dpi = 96;
970 auto screenManager = QWindowsContext::instance()->screenManager();
971 auto screen = screenManager.screenAtDp(geometry.center());
972 if (!screen)
973 screen = screenManager.screens().value(0);
974 if (screen)
975 dpi = screen->logicalDpi().first;
976 return QWindowsGeometryHint::frame(style, exStyle, dpi);
977}
978
979bool QWindowsGeometryHint::handleCalculateSize(const QMargins &customMargins, const MSG &msg, LRESULT *result)
980{
981 // NCCALCSIZE_PARAMS structure if wParam==TRUE
982 if (!msg.wParam || customMargins.isNull())
983 return false;
984 *result = DefWindowProc(msg.hwnd, msg.message, msg.wParam, msg.lParam);
985 auto *ncp = reinterpret_cast<NCCALCSIZE_PARAMS *>(msg.lParam);
986 const RECT oldClientArea = ncp->rgrc[0];
987 ncp->rgrc[0].left += customMargins.left();
988 ncp->rgrc[0].top += customMargins.top();
989 ncp->rgrc[0].right -= customMargins.right();
990 ncp->rgrc[0].bottom -= customMargins.bottom();
991 result = nullptr;
992 qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << oldClientArea << '+' << customMargins << "-->"
993 << ncp->rgrc[0] << ' ' << ncp->rgrc[1] << ' ' << ncp->rgrc[2]
994 << ' ' << ncp->lppos->cx << ',' << ncp->lppos->cy;
995 return true;
996}
997
998void QWindowsGeometryHint::frameSizeConstraints(const QWindow *w, const QMargins &margins,
999 QSize *minimumSize, QSize *maximumSize)
1000{
1001 *minimumSize = toNativeSizeConstrained(w->minimumSize(), w);
1002 *maximumSize = toNativeSizeConstrained(w->maximumSize(), w);
1003
1004 const int maximumWidth = qMax(maximumSize->width(), minimumSize->width());
1005 const int maximumHeight = qMax(maximumSize->height(), minimumSize->height());
1006 const int frameWidth = margins.left() + margins.right();
1007 const int frameHeight = margins.top() + margins.bottom();
1008
1009 if (minimumSize->width() > 0)
1010 minimumSize->rwidth() += frameWidth;
1011 if (minimumSize->height() > 0)
1012 minimumSize->rheight() += frameHeight;
1013 if (maximumWidth < QWINDOWSIZE_MAX)
1014 maximumSize->setWidth(maximumWidth + frameWidth);
1015 if (maximumHeight < QWINDOWSIZE_MAX)
1016 maximumSize->setHeight(maximumHeight + frameHeight);
1017}
1018
1019void QWindowsGeometryHint::applyToMinMaxInfo(const QWindow *w,
1020 const QMargins &margins,
1021 MINMAXINFO *mmi)
1022{
1023 QSize minimumSize;
1024 QSize maximumSize;
1025 frameSizeConstraints(w, margins, &minimumSize, &maximumSize);
1026 qCDebug(lcQpaWindows).nospace() << '>' << __FUNCTION__ << '<' << " min="
1027 << minimumSize.width() << ',' << minimumSize.height()
1028 << " max=" << maximumSize.width() << ',' << maximumSize.height()
1029 << " margins=" << margins
1030 << " in " << *mmi;
1031
1032 if (minimumSize.width() > 0)
1033 mmi->ptMinTrackSize.x = minimumSize.width();
1034 if (minimumSize.height() > 0)
1035 mmi->ptMinTrackSize.y = minimumSize.height();
1036
1037 if (maximumSize.width() < QWINDOWSIZE_MAX)
1038 mmi->ptMaxTrackSize.x = maximumSize.width();
1039 if (maximumSize.height() < QWINDOWSIZE_MAX)
1040 mmi->ptMaxTrackSize.y = maximumSize.height();
1041 qCDebug(lcQpaWindows).nospace() << '<' << __FUNCTION__ << " out " << *mmi;
1042}
1043
1044bool QWindowsGeometryHint::positionIncludesFrame(const QWindow *w)
1045{
1046 return qt_window_private(const_cast<QWindow *>(w))->positionPolicy
1047 == QWindowPrivate::WindowFrameInclusive;
1048}
1049
1050/*!
1051 \class QWindowsBaseWindow
1052 \brief Base class for QWindowsForeignWindow, QWindowsWindow
1053
1054 The class provides some _sys() getters for querying window
1055 data from a HWND and some _sys() setters.
1056
1057 Derived classes wrapping foreign windows may use them directly
1058 to calculate geometry, margins, etc.
1059
1060 Derived classes representing windows created by Qt may defer
1061 expensive calculations until change notifications are received.
1062
1063 \since 5.6
1064 \internal
1065 \ingroup qt-lighthouse-win
1066*/
1067
1068bool QWindowsBaseWindow::isRtlLayout(HWND hwnd)
1069{
1070 return (GetWindowLongPtrW(hwnd, GWL_EXSTYLE) & WS_EX_LAYOUTRTL) != 0;
1071}
1072
1073QWindowsBaseWindow *QWindowsBaseWindow::baseWindowOf(const QWindow *w)
1074{
1075 if (w) {
1076 if (QPlatformWindow *pw = w->handle())
1077 return static_cast<QWindowsBaseWindow *>(pw);
1078 }
1079 return nullptr;
1080}
1081
1082HWND QWindowsBaseWindow::handleOf(const QWindow *w)
1083{
1084 const QWindowsBaseWindow *bw = QWindowsBaseWindow::baseWindowOf(w);
1085 return bw ? bw->handle() : HWND(nullptr);
1086}
1087
1088bool QWindowsBaseWindow::isTopLevel_sys() const
1089{
1090 const HWND parent = parentHwnd();
1091 return !parent || parent == GetDesktopWindow();
1092}
1093
1094QRect QWindowsBaseWindow::frameGeometry_sys() const
1095{
1096 return frameGeometry(handle(), isTopLevel());
1097}
1098
1099QRect QWindowsBaseWindow::geometry_sys() const
1100{
1101 return frameGeometry_sys().marginsRemoved(fullFrameMargins());
1102}
1103
1104QMargins QWindowsBaseWindow::frameMargins_sys() const
1105{
1106 return QWindowsGeometryHint::frame(handle(), style(), exStyle());
1107}
1108
1109void QWindowsBaseWindow::hide_sys() // Normal hide, do not activate other windows.
1110{
1111 SetWindowPos(handle(), nullptr , 0, 0, 0, 0,
1112 SWP_HIDEWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
1113}
1114
1115void QWindowsBaseWindow::raise_sys()
1116{
1117 qCDebug(lcQpaWindows) << __FUNCTION__ << this << window();
1118 const Qt::WindowType type = window()->type();
1119 if (type == Qt::Popup
1120 || type == Qt::SubWindow // Special case for QTBUG-63121: MDI subwindows with WindowStaysOnTopHint
1121 || !(window()->flags() & Qt::WindowStaysOnBottomHint)) {
1122 SetWindowPos(handle(), HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
1123 }
1124}
1125
1126void QWindowsBaseWindow::lower_sys()
1127{
1128 qCDebug(lcQpaWindows) << __FUNCTION__ << this << window();
1129 if (!(window()->flags() & Qt::WindowStaysOnTopHint))
1130 SetWindowPos(handle(), HWND_BOTTOM, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
1131}
1132
1133void QWindowsBaseWindow::setWindowTitle_sys(const QString &title)
1134{
1135 qCDebug(lcQpaWindows) << __FUNCTION__ << this << window() << title;
1136 SetWindowText(handle(), reinterpret_cast<const wchar_t *>(title.utf16()));
1137}
1138
1139QPoint QWindowsBaseWindow::mapToGlobal(const QPoint &pos) const
1140{
1141 return QWindowsGeometryHint::mapToGlobal(handle(), pos);
1142}
1143
1144QPoint QWindowsBaseWindow::mapFromGlobal(const QPoint &pos) const
1145{
1146 return QWindowsGeometryHint::mapFromGlobal(handle(), pos);
1147}
1148
1149/*!
1150 \class QWindowsDesktopWindow
1151 \brief Window wrapping GetDesktopWindow not allowing any manipulation.
1152 \since 5.6
1153 \internal
1154 \ingroup qt-lighthouse-win
1155*/
1156
1157/*!
1158 \class QWindowsForeignWindow
1159 \brief Window wrapping a foreign native window.
1160
1161 QWindowsForeignWindow stores a native HWND and implements getters for
1162 geometry, margins, etc. reparenting and geometry manipulation for use as a
1163 child window in Qt.
1164
1165 \since 5.6
1166 \internal
1167 \ingroup qt-lighthouse-win
1168*/
1169
1170QWindowsForeignWindow::QWindowsForeignWindow(QWindow *window, HWND hwnd)
1171 : QWindowsBaseWindow(window)
1172 , m_hwnd(hwnd)
1173 , m_topLevelStyle(0)
1174{
1175}
1176
1177void QWindowsForeignWindow::setParent(const QPlatformWindow *newParentWindow)
1178{
1179 const bool wasTopLevel = isTopLevel_sys();
1180 const HWND newParent = newParentWindow ? reinterpret_cast<HWND>(newParentWindow->winId()) : HWND(nullptr);
1181 const bool isTopLevel = !newParent;
1182 const DWORD oldStyle = style();
1183 qCDebug(lcQpaWindows) << __FUNCTION__ << window() << "newParent="
1184 << newParentWindow << newParent << "oldStyle=" << debugWinStyle(oldStyle);
1185 SetParent(m_hwnd, newParent);
1186 if (wasTopLevel != isTopLevel) { // Top level window flags need to be set/cleared manually.
1187 DWORD newStyle = oldStyle;
1188 if (isTopLevel) {
1189 newStyle = m_topLevelStyle;
1190 } else {
1191 m_topLevelStyle = oldStyle;
1192 newStyle &= ~(WS_OVERLAPPEDWINDOW | WS_POPUPWINDOW);
1193 newStyle |= WS_CHILD;
1194 }
1195 SetWindowLongPtr(m_hwnd, GWL_STYLE, newStyle);
1196 }
1197}
1198
1199void QWindowsForeignWindow::setVisible(bool visible)
1200{
1201 qCDebug(lcQpaWindows) << __FUNCTION__ << window() << visible;
1202 if (visible)
1203 ShowWindow(handle(), SW_SHOWNOACTIVATE);
1204 else
1205 hide_sys();
1206}
1207
1208/*!
1209 \class QWindowCreationContext
1210 \brief Active Context for creating windows.
1211
1212 There is a phase in window creation (WindowCreationData::create())
1213 in which events are sent before the system API CreateWindowEx() returns
1214 the handle. These cannot be handled by the platform window as the association
1215 of the unknown handle value to the window does not exist yet and as not
1216 to trigger recursive handle creation, etc.
1217
1218 In that phase, an instance of QWindowCreationContext is set on
1219 QWindowsContext.
1220
1221 QWindowCreationContext stores the information to answer the initial
1222 WM_GETMINMAXINFO and obtains the corrected size/position.
1223
1224 \sa WindowCreationData, QWindowsContext
1225 \internal
1226 \ingroup qt-lighthouse-win
1227*/
1228
1229QWindowCreationContext::QWindowCreationContext(const QWindow *w,
1230 const QRect &geometryIn, const QRect &geometry,
1231 const QMargins &cm,
1232 DWORD style, DWORD exStyle) :
1233 window(w),
1234 requestedGeometryIn(geometryIn),
1235 requestedGeometry(geometry),
1236 obtainedPos(geometryIn.topLeft()),
1237 obtainedSize(geometryIn.size()),
1238 margins(QWindowsGeometryHint::frame(w, geometry, style, exStyle)),
1239 customMargins(cm)
1240{
1241 // Geometry of toplevels does not consider window frames.
1242 // TODO: No concept of WA_wasMoved yet that would indicate a
1243 // CW_USEDEFAULT unless set. For now, assume that 0,0 means 'default'
1244 // for toplevels.
1245 if (geometry.isValid()
1246 || !qt_window_private(const_cast<QWindow *>(w))->resizeAutomatic) {
1247 frameX = geometry.x();
1248 frameY = geometry.y();
1249 const QMargins effectiveMargins = margins + customMargins;
1250 frameWidth = effectiveMargins.left() + geometry.width() + effectiveMargins.right();
1251 frameHeight = effectiveMargins.top() + geometry.height() + effectiveMargins.bottom();
1252 if (QWindowsMenuBar::menuBarOf(w) != nullptr) {
1253 menuHeight = GetSystemMetrics(SM_CYMENU);
1254 frameHeight += menuHeight;
1255 }
1256 const bool isDefaultPosition = !frameX && !frameY && w->isTopLevel();
1257 if (!QWindowsGeometryHint::positionIncludesFrame(w) && !isDefaultPosition) {
1258 frameX -= effectiveMargins.left();
1259 frameY -= effectiveMargins.top();
1260 }
1261 }
1262
1263 qCDebug(lcQpaWindows).nospace()
1264 << __FUNCTION__ << ' ' << w << ' ' << geometry
1265 << " pos incl. frame=" << QWindowsGeometryHint::positionIncludesFrame(w)
1266 << " frame=" << frameWidth << 'x' << frameHeight << '+'
1267 << frameX << '+' << frameY
1268 << " margins=" << margins << " custom margins=" << customMargins;
1269}
1270
1271void QWindowCreationContext::applyToMinMaxInfo(MINMAXINFO *mmi) const
1272{
1273 QWindowsGeometryHint::applyToMinMaxInfo(window, margins + customMargins, mmi);
1274}
1275
1276/*!
1277 \class QWindowsWindow
1278 \brief Raster or OpenGL Window.
1279
1280 \list
1281 \li Raster type: handleWmPaint() is implemented to
1282 to bitblt the image. The DC can be accessed
1283 via getDC/Relase DC, which has a special handling
1284 when within a paint event (in that case, the DC obtained
1285 from BeginPaint() is returned).
1286
1287 \li Open GL: The first time QWindowsGLContext accesses
1288 the handle, it sets up the pixelformat on the DC
1289 which in turn sets it on the window (see flag
1290 PixelFormatInitialized).
1291 handleWmPaint() is empty (although required).
1292 \endlist
1293
1294 \internal
1295 \ingroup qt-lighthouse-win
1296*/
1297
1298const char *QWindowsWindow::embeddedNativeParentHandleProperty = "_q_embedded_native_parent_handle";
1299const char *QWindowsWindow::hasBorderInFullScreenProperty = "_q_has_border_in_fullscreen";
1300bool QWindowsWindow::m_borderInFullScreenDefault = false;
1301
1302QWindowsWindow::QWindowsWindow(QWindow *aWindow, const QWindowsWindowData &data) :
1303 QWindowsBaseWindow(aWindow),
1304 m_data(data),
1305 m_cursor(new CursorHandle),
1306 m_format(aWindow->requestedFormat())
1307#if QT_CONFIG(vulkan)
1308 , m_vkSurface(VK_NULL_HANDLE)
1309#endif
1310{
1311 QWindowsContext::instance()->addWindow(m_data.hwnd, this);
1312 const Qt::WindowType type = aWindow->type();
1313 if (type == Qt::Desktop)
1314 return; // No further handling for Qt::Desktop
1315#ifndef QT_NO_OPENGL
1316 if (aWindow->surfaceType() == QWindow::OpenGLSurface) {
1317 if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL)
1318 setFlag(OpenGLSurface);
1319 else
1320 setFlag(OpenGL_ES2);
1321 }
1322#endif // QT_NO_OPENGL
1323#if QT_CONFIG(vulkan)
1324 if (aWindow->surfaceType() == QSurface::VulkanSurface)
1325 setFlag(VulkanSurface);
1326#endif
1327 updateDropSite(window()->isTopLevel());
1328
1329 registerTouchWindow();
1330 const qreal opacity = qt_window_private(aWindow)->opacity;
1331 if (!qFuzzyCompare(opacity, qreal(1.0)))
1332 setOpacity(opacity);
1333
1334 setMask(QHighDpi::toNativeLocalRegion(window()->mask(), window()));
1335
1336 if (aWindow->isTopLevel())
1337 setWindowIcon(aWindow->icon());
1338 if (m_borderInFullScreenDefault || aWindow->property(hasBorderInFullScreenProperty).toBool())
1339 setFlag(HasBorderInFullScreen);
1340 clearFlag(WithinCreate);
1341}
1342
1343QWindowsWindow::~QWindowsWindow()
1344{
1345 setFlag(WithinDestroy);
1346 if (testFlag(TouchRegistered))
1347 UnregisterTouchWindow(m_data.hwnd);
1348 destroyWindow();
1349 destroyIcon();
1350}
1351
1352void QWindowsWindow::initialize()
1353{
1354 // Clear the creation context as the window can be found in QWindowsContext's map.
1355 QWindowCreationContextPtr creationContext =
1356 QWindowsContext::instance()->setWindowCreationContext(QWindowCreationContextPtr());
1357
1358 QWindow *w = window();
1359 setWindowState(w->windowStates());
1360
1361 // Trigger geometry change (unless it has a special state in which case setWindowState()
1362 // will send the message) and screen change signals of QWindow.
1363 if (w->type() != Qt::Desktop) {
1364 const Qt::WindowState state = w->windowState();
1365 const QRect obtainedGeometry(creationContext->obtainedPos, creationContext->obtainedSize);
1366 if (state != Qt::WindowMaximized && state != Qt::WindowFullScreen
1367 && creationContext->requestedGeometryIn != obtainedGeometry) {
1368 QWindowSystemInterface::handleGeometryChange<QWindowSystemInterface::SynchronousDelivery>(w, obtainedGeometry);
1369 }
1370 QPlatformScreen *obtainedScreen = screenForGeometry(obtainedGeometry);
1371 if (obtainedScreen && screen() != obtainedScreen)
1372 QWindowSystemInterface::handleWindowScreenChanged<QWindowSystemInterface::SynchronousDelivery>(w, obtainedScreen->screen());
1373 }
1374}
1375
1376void QWindowsWindow::fireExpose(const QRegion &region, bool force)
1377{
1378 if (region.isEmpty() && !force)
1379 clearFlag(Exposed);
1380 else
1381 setFlag(Exposed);
1382 QWindowSystemInterface::handleExposeEvent(window(), region);
1383}
1384
1385void QWindowsWindow::destroyWindow()
1386{
1387 qCDebug(lcQpaWindows) << __FUNCTION__ << this << window() << m_data.hwnd;
1388 if (m_data.hwnd) { // Stop event dispatching before Window is destroyed.
1389 setFlag(WithinDestroy);
1390 // Clear any transient child relationships as Windows will otherwise destroy them (QTBUG-35499, QTBUG-36666)
1391 const auto tlw = QGuiApplication::topLevelWindows();
1392 for (QWindow *w : tlw) {
1393 if (w->transientParent() == window()) {
1394 if (QWindowsWindow *tw = QWindowsWindow::windowsWindowOf(w))
1395 tw->updateTransientParent();
1396 }
1397 }
1398 QWindowsContext *context = QWindowsContext::instance();
1399 if (context->windowUnderMouse() == window())
1400 context->clearWindowUnderMouse();
1401 if (hasMouseCapture())
1402 setMouseGrabEnabled(false);
1403 setDropSiteEnabled(false);
1404#if QT_CONFIG(vulkan)
1405 if (m_vkSurface) {
1406 QVulkanInstance *inst = window()->vulkanInstance();
1407 if (inst)
1408 static_cast<QWindowsVulkanInstance *>(inst->handle())->destroySurface(m_vkSurface);
1409 m_vkSurface = VK_NULL_HANDLE;
1410 }
1411#endif
1412#ifndef QT_NO_OPENGL
1413 if (m_surface) {
1414 if (QWindowsStaticOpenGLContext *staticOpenGLContext = QWindowsIntegration::staticOpenGLContext())
1415 staticOpenGLContext->destroyWindowSurface(m_surface);
1416 m_surface = nullptr;
1417 }
1418#endif
1419 DestroyWindow(m_data.hwnd);
1420 context->removeWindow(m_data.hwnd);
1421 m_data.hwnd = nullptr;
1422 }
1423}
1424
1425void QWindowsWindow::updateDropSite(bool topLevel)
1426{
1427 bool enabled = false;
1428 bool parentIsEmbedded = false;
1429
1430 if (!topLevel) {
1431 // if the parent window is a foreign window wrapped via QWindow::fromWinId, we need to enable the drop site
1432 // on the first child window
1433 const QWindow *parent = window()->parent();
1434 if (parent && parent->handle() && parent->handle()->isForeignWindow())
1435 parentIsEmbedded = true;
1436 }
1437
1438 if (topLevel || parentIsEmbedded) {
1439 switch (window()->type()) {
1440 case Qt::Window:
1441 case Qt::Dialog:
1442 case Qt::Sheet:
1443 case Qt::Drawer:
1444 case Qt::Popup:
1445 case Qt::Tool:
1446 enabled = true;
1447 break;
1448 default:
1449 break;
1450 }
1451 }
1452 setDropSiteEnabled(enabled);
1453}
1454
1455void QWindowsWindow::setDropSiteEnabled(bool dropEnabled)
1456{
1457 if (isDropSiteEnabled() == dropEnabled)
1458 return;
1459 qCDebug(lcQpaMime) << __FUNCTION__ << window() << dropEnabled;
1460#if QT_CONFIG(clipboard) && QT_CONFIG(draganddrop)
1461 if (dropEnabled) {
1462 Q_ASSERT(m_data.hwnd);
1463 m_dropTarget = new QWindowsOleDropTarget(window());
1464 RegisterDragDrop(m_data.hwnd, m_dropTarget);
1465 CoLockObjectExternal(m_dropTarget, true, true);
1466 } else {
1467 CoLockObjectExternal(m_dropTarget, false, true);
1468 m_dropTarget->Release();
1469 RevokeDragDrop(m_data.hwnd);
1470 m_dropTarget = nullptr;
1471 }
1472#endif // QT_CONFIG(clipboard) && QT_CONFIG(draganddrop)
1473}
1474
1475bool QWindowsWindow::m_screenForGLInitialized = false;
1476
1477void QWindowsWindow::displayChanged()
1478{
1479 m_screenForGLInitialized = false;
1480}
1481
1482void QWindowsWindow::settingsChanged()
1483{
1484 m_screenForGLInitialized = false;
1485}
1486
1487QScreen *QWindowsWindow::forcedScreenForGLWindow(const QWindow *w)
1488{
1489 static QString forceToScreen;
1490 if (!m_screenForGLInitialized) {
1491 forceToScreen = GpuDescription::detect().gpuSuitableScreen;
1492 m_screenForGLInitialized = true;
1493 }
1494 return forceToScreen.isEmpty() ? nullptr : screenForName(w, forceToScreen);
1495}
1496
1497// Returns topmost QWindowsWindow ancestor even if there are embedded windows in the chain.
1498// Returns this window if it is the topmost ancestor.
1499QWindow *QWindowsWindow::topLevelOf(QWindow *w)
1500{
1501 while (QWindow *parent = w->parent())
1502 w = parent;
1503
1504 if (const QPlatformWindow *handle = w->handle()) {
1505 const auto *ww = static_cast<const QWindowsWindow *>(handle);
1506 if (ww->isEmbedded()) {
1507 HWND parentHWND = GetAncestor(ww->handle(), GA_PARENT);
1508 const HWND desktopHwnd = GetDesktopWindow();
1509 const QWindowsContext *ctx = QWindowsContext::instance();
1510 while (parentHWND && parentHWND != desktopHwnd) {
1511 if (QWindowsWindow *ancestor = ctx->findPlatformWindow(parentHWND))
1512 return topLevelOf(ancestor->window());
1513 parentHWND = GetAncestor(parentHWND, GA_PARENT);
1514 }
1515 }
1516 }
1517 return w;
1518}
1519
1520QWindowsWindowData
1521 QWindowsWindowData::create(const QWindow *w,
1522 const QWindowsWindowData &parameters,
1523 const QString &title)
1524{
1525 WindowCreationData creationData;
1526 creationData.fromWindow(w, parameters.flags);
1527 QWindowsWindowData result = creationData.create(w, parameters, title);
1528 // Force WM_NCCALCSIZE (with wParam=1) via SWP_FRAMECHANGED for custom margin.
1529 creationData.initialize(w, result.hwnd, !parameters.customMargins.isNull(), 1);
1530 return result;
1531}
1532
1533void QWindowsWindow::setVisible(bool visible)
1534{
1535 const QWindow *win = window();
1536 qCDebug(lcQpaWindows) << __FUNCTION__ << this << win << m_data.hwnd << visible;
1537 if (m_data.hwnd) {
1538 if (visible) {
1539 show_sys();
1540
1541 // When the window is layered, we won't get WM_PAINT, and "we" are in control
1542 // over the rendering of the window
1543 // There is nobody waiting for this, so we don't need to flush afterwards.
1544 if (isLayered())
1545 fireExpose(QRect(0, 0, win->width(), win->height()));
1546 // QTBUG-44928, QTBUG-7386: This is to resolve the problem where popups are
1547 // opened from the system tray and not being implicitly activated
1548
1549 if (win->type() == Qt::Popup && !win->parent() && !QGuiApplication::focusWindow())
1550 SetForegroundWindow(m_data.hwnd);
1551 } else {
1552 if (hasMouseCapture())
1553 setMouseGrabEnabled(false);
1554 if (window()->flags() & Qt::Popup) // from QWidgetPrivate::hide_sys(), activate other
1555 ShowWindow(m_data.hwnd, SW_HIDE);
1556 else
1557 hide_sys();
1558 fireExpose(QRegion());
1559 }
1560 }
1561}
1562
1563bool QWindowsWindow::isVisible() const
1564{
1565 return m_data.hwnd && IsWindowVisible(m_data.hwnd);
1566}
1567
1568bool QWindowsWindow::isActive() const
1569{
1570 // Check for native windows or children of the active native window.
1571 if (const HWND activeHwnd = GetForegroundWindow())
1572 if (m_data.hwnd == activeHwnd || IsChild(activeHwnd, m_data.hwnd))
1573 return true;
1574 return false;
1575}
1576
1577bool QWindowsWindow::isAncestorOf(const QPlatformWindow *child) const
1578{
1579 const auto *childWindow = static_cast<const QWindowsWindow *>(child);
1580 return IsChild(m_data.hwnd, childWindow->handle());
1581}
1582
1583bool QWindowsWindow::isEmbedded() const
1584{
1585 return m_data.embedded;
1586}
1587
1588QPoint QWindowsWindow::mapToGlobal(const QPoint &pos) const
1589{
1590 return m_data.hwnd ? QWindowsGeometryHint::mapToGlobal(m_data.hwnd, pos) : pos;
1591}
1592
1593QPoint QWindowsWindow::mapFromGlobal(const QPoint &pos) const
1594{
1595 return m_data.hwnd ? QWindowsGeometryHint::mapFromGlobal(m_data.hwnd, pos) : pos;
1596}
1597
1598// Update the transient parent for a toplevel window. The concept does not
1599// really exist on Windows, the relationship is set by passing a parent along with !WS_CHILD
1600// to window creation or by setting the parent using GWL_HWNDPARENT (as opposed to
1601// SetParent, which would make it a real child).
1602
1603#ifndef GWL_HWNDPARENT
1604# define GWL_HWNDPARENT (-8)
1605#endif
1606
1607void QWindowsWindow::updateTransientParent() const
1608{
1609 if (window()->type() == Qt::Popup)
1610 return; // QTBUG-34503, // a popup stays on top, no parent, see also WindowCreationData::fromWindow().
1611 // Update transient parent.
1612 const HWND oldTransientParent = GetWindow(m_data.hwnd, GW_OWNER);
1613 HWND newTransientParent = nullptr;
1614 if (const QWindow *tp = window()->transientParent())
1615 if (const QWindowsWindow *tw = QWindowsWindow::windowsWindowOf(tp))
1616 if (!tw->testFlag(WithinDestroy)) // Prevent destruction by parent window (QTBUG-35499, QTBUG-36666)
1617 newTransientParent = tw->handle();
1618
1619 // QTSOLBUG-71: When using the MFC/winmigrate solution, it is possible that a child
1620 // window is found, which can cause issues with modality. Loop up to top level.
1621 while (newTransientParent && (GetWindowLongPtr(newTransientParent, GWL_STYLE) & WS_CHILD) != 0)
1622 newTransientParent = GetParent(newTransientParent);
1623
1624 if (newTransientParent != oldTransientParent)
1625 SetWindowLongPtr(m_data.hwnd, GWL_HWNDPARENT, LONG_PTR(newTransientParent));
1626}
1627
1628static inline bool testShowWithoutActivating(const QWindow *window)
1629{
1630 // QWidget-attribute Qt::WA_ShowWithoutActivating .
1631 const QVariant showWithoutActivating = window->property("_q_showWithoutActivating");
1632 return showWithoutActivating.isValid() && showWithoutActivating.toBool();
1633}
1634
1635static void setMinimizedGeometry(HWND hwnd, const QRect &r)
1636{
1637 WINDOWPLACEMENT windowPlacement;
1638 windowPlacement.length = sizeof(WINDOWPLACEMENT);
1639 if (GetWindowPlacement(hwnd, &windowPlacement)) {
1640 windowPlacement.showCmd = SW_SHOWMINIMIZED;
1641 windowPlacement.rcNormalPosition = RECTfromQRect(r);
1642 SetWindowPlacement(hwnd, &windowPlacement);
1643 }
1644}
1645
1646static void setRestoreMaximizedFlag(HWND hwnd, bool set = true)
1647{
1648 // Let Windows know that we need to restore as maximized
1649 WINDOWPLACEMENT windowPlacement;
1650 windowPlacement.length = sizeof(WINDOWPLACEMENT);
1651 if (GetWindowPlacement(hwnd, &windowPlacement)) {
1652 if (set)
1653 windowPlacement.flags |= WPF_RESTORETOMAXIMIZED;
1654 else
1655 windowPlacement.flags &= ~WPF_RESTORETOMAXIMIZED;
1656 SetWindowPlacement(hwnd, &windowPlacement);
1657 }
1658}
1659
1660// partially from QWidgetPrivate::show_sys()
1661void QWindowsWindow::show_sys() const
1662{
1663 int sm = SW_SHOWNORMAL;
1664 bool fakedMaximize = false;
1665 bool restoreMaximize = false;
1666 const QWindow *w = window();
1667 const Qt::WindowFlags flags = w->flags();
1668 const Qt::WindowType type = w->type();
1669 if (w->isTopLevel()) {
1670 const Qt::WindowStates state = w->windowStates();
1671 if (state & Qt::WindowMinimized) {
1672 sm = SW_SHOWMINIMIZED;
1673 if (!isVisible())
1674 sm = SW_SHOWMINNOACTIVE;
1675 if (state & Qt::WindowMaximized)
1676 restoreMaximize = true;
1677 } else {
1678 updateTransientParent();
1679 if (state & Qt::WindowMaximized) {
1680 sm = SW_SHOWMAXIMIZED;
1681 // Windows will not behave correctly when we try to maximize a window which does not
1682 // have minimize nor maximize buttons in the window frame. Windows would then ignore
1683 // non-available geometry, and rather maximize the widget to the full screen, minus the
1684 // window frame (caption). So, we do a trick here, by adding a maximize button before
1685 // maximizing the widget, and then remove the maximize button afterwards.
1686 if (flags & Qt::WindowTitleHint &&
1687 !(flags & (Qt::WindowMinMaxButtonsHint | Qt::FramelessWindowHint))) {
1688 fakedMaximize = TRUE;
1689 setStyle(style() | WS_MAXIMIZEBOX);
1690 }
1691 } // Qt::WindowMaximized
1692 } // !Qt::WindowMinimized
1693 }
1694 if (type == Qt::Popup || type == Qt::ToolTip || type == Qt::Tool || testShowWithoutActivating(w))
1695 sm = SW_SHOWNOACTIVATE;
1696
1697 if (w->windowStates() & Qt::WindowMaximized)
1698 setFlag(WithinMaximize); // QTBUG-8361
1699
1700 ShowWindow(m_data.hwnd, sm);
1701
1702 clearFlag(WithinMaximize);
1703
1704 if (fakedMaximize) {
1705 setStyle(style() & ~WS_MAXIMIZEBOX);
1706 SetWindowPos(m_data.hwnd, nullptr, 0, 0, 0, 0,
1707 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER
1708 | SWP_FRAMECHANGED);
1709 }
1710 if (restoreMaximize)
1711 setRestoreMaximizedFlag(m_data.hwnd);
1712}
1713
1714void QWindowsWindow::setParent(const QPlatformWindow *newParent)
1715{
1716 qCDebug(lcQpaWindows) << __FUNCTION__ << window() << newParent;
1717
1718 if (m_data.hwnd)
1719 setParent_sys(newParent);
1720}
1721
1722void QWindowsWindow::setParent_sys(const QPlatformWindow *parent)
1723{
1724 // Use GetAncestor instead of GetParent, as GetParent can return owner window for toplevels
1725 HWND oldParentHWND = parentHwnd();
1726 HWND newParentHWND = nullptr;
1727 if (parent) {
1728 const auto *parentW = static_cast<const QWindowsWindow *>(parent);
1729 newParentHWND = parentW->handle();
1730
1731 }
1732
1733 // NULL handle means desktop window, which also has its proper handle -> disambiguate
1734 HWND desktopHwnd = GetDesktopWindow();
1735 if (oldParentHWND == desktopHwnd)
1736 oldParentHWND = nullptr;
1737 if (newParentHWND == desktopHwnd)
1738 newParentHWND = nullptr;
1739
1740 if (newParentHWND != oldParentHWND) {
1741 const bool wasTopLevel = oldParentHWND == nullptr;
1742 const bool isTopLevel = newParentHWND == nullptr;
1743
1744 setFlag(WithinSetParent);
1745 SetParent(m_data.hwnd, newParentHWND);
1746 clearFlag(WithinSetParent);
1747
1748 // WS_CHILD/WS_POPUP must be manually set/cleared in addition
1749 // to dialog frames, etc (see SetParent() ) if the top level state changes.
1750 // Force toplevel state as QWindow::isTopLevel cannot be relied upon here.
1751 if (wasTopLevel != isTopLevel) {
1752 setDropSiteEnabled(false);
1753 setWindowFlags_sys(window()->flags(), unsigned(isTopLevel ? WindowCreationData::ForceTopLevel : WindowCreationData::ForceChild));
1754 updateDropSite(isTopLevel);
1755 }
1756 }
1757}
1758
1759void QWindowsWindow::handleHidden()
1760{
1761 fireExpose(QRegion());
1762}
1763
1764void QWindowsWindow::handleCompositionSettingsChanged()
1765{
1766 const QWindow *w = window();
1767 if ((w->surfaceType() == QWindow::OpenGLSurface || w->surfaceType() == QWindow::VulkanSurface)
1768 && w->format().hasAlpha()) {
1769 applyBlurBehindWindow(handle());
1770 }
1771}
1772
1773static QRect normalFrameGeometry(HWND hwnd)
1774{
1775 WINDOWPLACEMENT wp;
1776 wp.length = sizeof(WINDOWPLACEMENT);
1777 if (GetWindowPlacement(hwnd, &wp)) {
1778 const QRect result = qrectFromRECT(wp.rcNormalPosition);
1779 return result.translated(windowPlacementOffset(hwnd, result.topLeft()));
1780 }
1781 return QRect();
1782}
1783
1784QRect QWindowsWindow::normalGeometry() const
1785{
1786 // Check for fake 'fullscreen' mode.
1787 const bool fakeFullScreen =
1788 m_savedFrameGeometry.isValid() && (window()->windowStates() & Qt::WindowFullScreen);
1789 const QRect frame = fakeFullScreen ? m_savedFrameGeometry : normalFrameGeometry(m_data.hwnd);
1790 const QMargins margins = fakeFullScreen
1791 ? QWindowsGeometryHint::frame(handle(), m_savedStyle, 0)
1792 : fullFrameMargins();
1793 return frame.isValid() ? frame.marginsRemoved(margins) : frame;
1794}
1795
1796static QString msgUnableToSetGeometry(const QWindowsWindow *platformWindow,
1797 const QRect &requestedRect,
1798 const QRect &obtainedRect,
1799 const QMargins &fullMargins,
1800 const QMargins &customMargins)
1801{
1802 QString result;
1803 QDebug debug(&result);
1804 debug.nospace();
1805 debug.noquote();
1806 const auto window = platformWindow->window();
1807 debug << "Unable to set geometry ";
1808 formatBriefRectangle(debug, requestedRect);
1809 debug << " (frame: ";
1810 formatBriefRectangle(debug, requestedRect + fullMargins);
1811 debug << ") on " << window->metaObject()->className() << "/\""
1812 << window->objectName() << "\" on \"" << window->screen()->name()
1813 << "\". Resulting geometry: ";
1814 formatBriefRectangle(debug, obtainedRect);
1815 debug << " (frame: ";
1816 formatBriefRectangle(debug, obtainedRect + fullMargins);
1817 debug << ") margins: ";
1818 formatBriefMargins(debug, fullMargins);
1819 if (!customMargins.isNull()) {
1820 debug << " custom margin: ";
1821 formatBriefMargins(debug, customMargins);
1822 }
1823 const auto minimumSize = window->minimumSize();
1824 const bool hasMinimumSize = !minimumSize.isEmpty();
1825 if (hasMinimumSize)
1826 debug << " minimum size: " << minimumSize.width() << 'x' << minimumSize.height();
1827 const auto maximumSize = window->maximumSize();
1828 const bool hasMaximumSize = maximumSize.width() != QWINDOWSIZE_MAX || maximumSize.height() != QWINDOWSIZE_MAX;
1829 if (hasMaximumSize)
1830 debug << " maximum size: " << maximumSize.width() << 'x' << maximumSize.height();
1831 if (hasMinimumSize || hasMaximumSize) {
1832 MINMAXINFO minmaxInfo;
1833 memset(&minmaxInfo, 0, sizeof(minmaxInfo));
1834 platformWindow->getSizeHints(&minmaxInfo);
1835 debug << ' ' << minmaxInfo;
1836 }
1837 debug << ')';
1838 return result;
1839}
1840
1841void QWindowsWindow::setGeometry(const QRect &rectIn)
1842{
1843 QRect rect = rectIn;
1844 // This means it is a call from QWindow::setFramePosition() and
1845 // the coordinates include the frame (size is still the contents rectangle).
1846 if (QWindowsGeometryHint::positionIncludesFrame(window())) {
1847 const QMargins margins = frameMargins();
1848 rect.moveTopLeft(rect.topLeft() + QPoint(margins.left(), margins.top()));
1849 }
1850 if (m_windowState & Qt::WindowMinimized)
1851 m_data.geometry = rect; // Otherwise set by handleGeometryChange() triggered by event.
1852 if (m_data.hwnd) {
1853 // A ResizeEvent with resulting geometry will be sent. If we cannot
1854 // achieve that size (for example, window title minimal constraint),
1855 // notify and warn.
1856 setFlag(WithinSetGeometry);
1857 setGeometry_sys(rect);
1858 clearFlag(WithinSetGeometry);
1859 if (m_data.geometry != rect && (isVisible() || QLibraryInfo::isDebugBuild())) {
1860 const auto warning =
1861 msgUnableToSetGeometry(this, rectIn, m_data.geometry,
1862 m_data.fullFrameMargins, m_data.customMargins);
1863 qWarning("%s: %s", __FUNCTION__, qPrintable(warning));
1864 }
1865 } else {
1866 QPlatformWindow::setGeometry(rect);
1867 }
1868}
1869
1870void QWindowsWindow::handleMoved()
1871{
1872 // Minimize/Set parent can send nonsensical move events.
1873 if (!IsIconic(m_data.hwnd) && !testFlag(WithinSetParent))
1874 handleGeometryChange();
1875}
1876
1877void QWindowsWindow::handleResized(int wParam)
1878{
1879 switch (wParam) {
1880 case SIZE_MAXHIDE: // Some other window affected.
1881 case SIZE_MAXSHOW:
1882 return;
1883 case SIZE_MINIMIZED: // QTBUG-53577, prevent state change events during programmatic state change
1884 if (!testFlag(WithinSetStyle) && !testFlag(WithinSetGeometry))
1885 handleWindowStateChange(m_windowState | Qt::WindowMinimized);
1886 return;
1887 case SIZE_MAXIMIZED:
1888 if (!testFlag(WithinSetStyle) && !testFlag(WithinSetGeometry))
1889 handleWindowStateChange(Qt::WindowMaximized | (isFullScreen_sys() ? Qt::WindowFullScreen
1890 : Qt::WindowNoState));
1891 handleGeometryChange();
1892 break;
1893 case SIZE_RESTORED:
1894 if (!testFlag(WithinSetStyle) && !testFlag(WithinSetGeometry)) {
1895 if (isFullScreen_sys())
1896 handleWindowStateChange(
1897 Qt::WindowFullScreen
1898 | (testFlag(MaximizeToFullScreen) ? Qt::WindowMaximized : Qt::WindowNoState));
1899 else if (m_windowState != Qt::WindowNoState && !testFlag(MaximizeToFullScreen))
1900 handleWindowStateChange(Qt::WindowNoState);
1901 }
1902 handleGeometryChange();
1903 break;
1904 }
1905}
1906
1907static inline bool equalDpi(const QDpi &d1, const QDpi &d2)
1908{
1909 return qFuzzyCompare(d1.first, d2.first) && qFuzzyCompare(d1.second, d2.second);
1910}
1911
1912void QWindowsWindow::checkForScreenChanged(ScreenChangeMode mode)
1913{
1914 if (parent() || QWindowsScreenManager::isSingleScreen())
1915 return;
1916
1917 QPlatformScreen *currentScreen = screen();
1918 const QWindowsScreen *newScreen =
1919 QWindowsContext::instance()->screenManager().screenForHwnd(m_data.hwnd);
1920 if (newScreen == nullptr || newScreen == currentScreen)
1921 return;
1922 // For screens with different DPI: postpone until WM_DPICHANGE
1923 if (mode == FromGeometryChange
1924 && !equalDpi(currentScreen->logicalDpi(), newScreen->logicalDpi())) {
1925 return;
1926 }
1927 qCDebug(lcQpaWindows).noquote().nospace() << __FUNCTION__
1928 << ' ' << window() << " \"" << currentScreen->name()
1929 << "\"->\"" << newScreen->name() << '"';
1930 if (mode == FromGeometryChange)
1931 setFlag(SynchronousGeometryChangeEvent);
1932 updateFullFrameMargins();
1933 QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->screen());
1934}
1935
1936void QWindowsWindow::handleGeometryChange()
1937{
1938 const QRect previousGeometry = m_data.geometry;
1939 m_data.geometry = geometry_sys();
1940 if (testFlag(WithinDpiChanged))
1941 return; // QGuiApplication will send resize
1942 QWindowSystemInterface::handleGeometryChange(window(), m_data.geometry);
1943 // QTBUG-32121: OpenGL/normal windows (with exception of ANGLE) do not receive
1944 // expose events when shrinking, synthesize.
1945 if (!testFlag(OpenGL_ES2) && isExposed()
1946 && m_data.geometry.size() != previousGeometry.size() // Exclude plain move
1947 // One dimension grew -> Windows will send expose, no need to synthesize.
1948 && !(m_data.geometry.width() > previousGeometry.width() || m_data.geometry.height() > previousGeometry.height())) {
1949 fireExpose(QRect(QPoint(0, 0), m_data.geometry.size()), true);
1950 }
1951
1952 const bool wasSync = testFlag(SynchronousGeometryChangeEvent);
1953 checkForScreenChanged();
1954
1955 if (testFlag(SynchronousGeometryChangeEvent))
1956 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
1957
1958 if (!wasSync)
1959 clearFlag(SynchronousGeometryChangeEvent);
1960 qCDebug(lcQpaEvents) << __FUNCTION__ << this << window() << m_data.geometry;
1961}
1962
1963void QWindowsBaseWindow::setGeometry_sys(const QRect &rect) const
1964{
1965 const QMargins margins = fullFrameMargins();
1966 const QRect frameGeometry = rect + margins;
1967
1968 qCDebug(lcQpaWindows) << '>' << __FUNCTION__ << window()
1969 << "\n from " << geometry_sys() << " frame: "
1970 << margins << " to " <<rect
1971 << " new frame: " << frameGeometry;
1972
1973 bool result = false;
1974 const HWND hwnd = handle();
1975 WINDOWPLACEMENT windowPlacement;
1976 windowPlacement.length = sizeof(WINDOWPLACEMENT);
1977 GetWindowPlacement(hwnd, &windowPlacement);
1978 // If the window is hidden and in maximized state or minimized, instead of moving the
1979 // window, set the normal position of the window.
1980 if ((windowPlacement.showCmd == SW_MAXIMIZE && !IsWindowVisible(hwnd))
1981 || windowPlacement.showCmd == SW_SHOWMINIMIZED) {
1982 windowPlacement.rcNormalPosition =
1983 RECTfromQRect(frameGeometry.translated(-windowPlacementOffset(hwnd, frameGeometry.topLeft())));
1984 windowPlacement.showCmd = windowPlacement.showCmd == SW_SHOWMINIMIZED ? SW_SHOWMINIMIZED : SW_HIDE;
1985 result = SetWindowPlacement(hwnd, &windowPlacement);
1986 } else {
1987 int x = frameGeometry.x();
1988 if (!window()->isTopLevel()) {
1989 const HWND parentHandle = GetParent(hwnd);
1990 if (isRtlLayout(parentHandle)) {
1991 RECT rect;
1992 GetClientRect(parentHandle, &rect);
1993 x = rect.right - frameGeometry.width() - x;
1994 }
1995 }
1996 result = MoveWindow(hwnd, x, frameGeometry.y(),
1997 frameGeometry.width(), frameGeometry.height(), true);
1998 }
1999 qCDebug(lcQpaWindows) << '<' << __FUNCTION__ << window()
2000 << "\n resulting " << result << geometry_sys();
2001}
2002
2003/*!
2004 Allocates a HDC for the window or returns the temporary one
2005 obtained from WinAPI BeginPaint within a WM_PAINT event.
2006
2007 \sa releaseDC()
2008*/
2009
2010HDC QWindowsWindow::getDC()
2011{
2012 if (!m_hdc) {
2013 m_hdc = GetDC(handle());
2014 if (QGuiApplication::layoutDirection() == Qt::RightToLeft)
2015 SetLayout(m_hdc, 0); // Clear RTL layout
2016 }
2017 return m_hdc;
2018}
2019
2020/*!
2021 Relases the HDC for the window or does nothing in
2022 case it was obtained from WinAPI BeginPaint within a WM_PAINT event.
2023
2024 \sa getDC()
2025*/
2026
2027void QWindowsWindow::releaseDC()
2028{
2029 if (m_hdc) {
2030 ReleaseDC(handle(), m_hdc);
2031 m_hdc = nullptr;
2032 }
2033}
2034
2035static inline bool dwmIsCompositionEnabled()
2036{
2037 BOOL dWmCompositionEnabled = FALSE;
2038 return SUCCEEDED(DwmIsCompositionEnabled(&dWmCompositionEnabled)) && dWmCompositionEnabled == TRUE;
2039}
2040
2041static inline bool isSoftwareGl()
2042{
2043#if QT_CONFIG(dynamicgl)
2044 return QOpenGLStaticContext::opengl32.moduleIsNotOpengl32()
2045 && QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL;
2046#else
2047 return false;
2048#endif // dynamicgl
2049}
2050
2051bool QWindowsWindow::handleWmPaint(HWND hwnd, UINT message,
2052 WPARAM, LPARAM)
2053{
2054 if (message == WM_ERASEBKGND) // Backing store - ignored.
2055 return true;
2056 // QTBUG-75455: Suppress WM_PAINT sent to invisible windows when setting WS_EX_LAYERED
2057 if (!window()->isVisible() && (GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED) != 0)
2058 return false;
2059 // Ignore invalid update bounding rectangles
2060 RECT updateRect;
2061 if (!GetUpdateRect(m_data.hwnd, &updateRect, FALSE))
2062 return false;
2063 PAINTSTRUCT ps;
2064
2065 // GL software rendering (QTBUG-58178) and Windows 7/Aero off with some AMD cards
2066 // (QTBUG-60527) need InvalidateRect() to suppress artifacts while resizing.
2067 if (testFlag(OpenGLSurface) && (isSoftwareGl() || !dwmIsCompositionEnabled()))
2068 InvalidateRect(hwnd, nullptr, false);
2069
2070 BeginPaint(hwnd, &ps);
2071
2072 // Observed painting problems with Aero style disabled (QTBUG-7865).
2073 if (Q_UNLIKELY(!dwmIsCompositionEnabled())
2074 && ((testFlag(OpenGLSurface) && testFlag(OpenGLDoubleBuffered)) || testFlag(VulkanSurface)))
2075 {
2076 SelectClipRgn(ps.hdc, nullptr);
2077 }
2078
2079 // If the a window is obscured by another window (such as a child window)
2080 // we still need to send isExposed=true, for compatibility.
2081 // Our tests depend on it.
2082 fireExpose(QRegion(qrectFromRECT(ps.rcPaint)), true);
2083 if (qSizeOfRect(updateRect) == m_data.geometry.size() && !QWindowsContext::instance()->asyncExpose())
2084 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
2085
2086 EndPaint(hwnd, &ps);
2087 return true;
2088}
2089
2090void QWindowsWindow::setWindowTitle(const QString &title)
2091{
2092 setWindowTitle_sys(QWindowsWindow::formatWindowTitle(title));
2093}
2094
2095void QWindowsWindow::setWindowFlags(Qt::WindowFlags flags)
2096{
2097 qCDebug(lcQpaWindows) << '>' << __FUNCTION__ << this << window() << "\n from: "
2098 << m_data.flags << "\n to: " << flags;
2099 const QRect oldGeometry = geometry();
2100 if (m_data.flags != flags) {
2101 m_data.flags = flags;
2102 if (m_data.hwnd) {
2103 m_data = setWindowFlags_sys(flags);
2104 updateDropSite(window()->isTopLevel());
2105 }
2106 }
2107 // When switching to a frameless window, geometry
2108 // may change without a WM_MOVE. Report change manually.
2109 // Do not send synchronously as not to clobber the widget
2110 // geometry in a sequence of setting flags and geometry.
2111 const QRect newGeometry = geometry_sys();
2112 if (oldGeometry != newGeometry)
2113 handleGeometryChange();
2114
2115 qCDebug(lcQpaWindows) << '<' << __FUNCTION__ << "\n returns: "
2116 << m_data.flags << " geometry " << oldGeometry << "->" << newGeometry;
2117}
2118
2119QWindowsWindowData QWindowsWindow::setWindowFlags_sys(Qt::WindowFlags wt,
2120 unsigned flags) const
2121{
2122 WindowCreationData creationData;
2123 creationData.fromWindow(window(), wt, flags);
2124 creationData.applyWindowFlags(m_data.hwnd);
2125 creationData.initialize(window(), m_data.hwnd, true, m_opacity);
2126
2127 QWindowsWindowData result = m_data;
2128 result.flags = creationData.flags;
2129 result.embedded = creationData.embedded;
2130 return result;
2131}
2132
2133void QWindowsWindow::handleWindowStateChange(Qt::WindowStates state)
2134{
2135 qCDebug(lcQpaWindows) << __FUNCTION__ << this << window()
2136 << "\n from " << m_windowState << " to " << state;
2137 m_windowState = state;
2138 QWindowSystemInterface::handleWindowStateChanged(window(), state);
2139 if (state & Qt::WindowMinimized) {
2140 handleHidden();
2141 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents); // Tell QQuickWindow to stop rendering now.
2142 } else {
2143 // QTBUG-17548: We send expose events when receiving WM_Paint, but for
2144 // layered windows and transient children, we won't receive any WM_Paint.
2145 QWindow *w = window();
2146 bool exposeEventsSent = false;
2147 if (isLayered()) {
2148 fireExpose(QRegion(0, 0, w->width(), w->height()));
2149 exposeEventsSent = true;
2150 }
2151 const QWindowList allWindows = QGuiApplication::allWindows();
2152 for (QWindow *child : allWindows) {
2153 if (child != w && child->isVisible() && child->transientParent() == w) {
2154 QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(child);
2155 if (platformWindow && platformWindow->isLayered()) {
2156 platformWindow->fireExpose(QRegion(0, 0, child->width(), child->height()));
2157 exposeEventsSent = true;
2158 }
2159 }
2160 }
2161 if (exposeEventsSent && !QWindowsContext::instance()->asyncExpose())
2162 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
2163 }
2164}
2165
2166void QWindowsWindow::setWindowState(Qt::WindowStates state)
2167{
2168 if (m_data.hwnd) {
2169 setWindowState_sys(state);
2170 m_windowState = state;
2171 }
2172}
2173
2174bool QWindowsWindow::isFullScreen_sys() const
2175{
2176 const QWindow *w = window();
2177 if (!w->isTopLevel())
2178 return false;
2179 QRect geometry = geometry_sys();
2180 if (testFlag(HasBorderInFullScreen))
2181 geometry += QMargins(1, 1, 1, 1);
2182 QPlatformScreen *screen = screenForGeometry(geometry);
2183 return screen && geometry == screen->geometry();
2184}
2185
2186/*!
2187 \brief Change the window state.
2188
2189 \note Window frames change when maximized;
2190 the top margin shrinks somewhat but that cannot be obtained using
2191 AdjustWindowRectEx().
2192
2193 \note Some calls to SetWindowLong require a subsequent call
2194 to ShowWindow.
2195*/
2196
2197void QWindowsWindow::setWindowState_sys(Qt::WindowStates newState)
2198{
2199 const Qt::WindowStates oldState = m_windowState;
2200 if (oldState == newState)
2201 return;
2202 qCDebug(lcQpaWindows) << '>' << __FUNCTION__ << this << window()
2203 << " from " << oldState << " to " << newState;
2204
2205 const bool visible = isVisible();
2206 auto stateChange = oldState ^ newState;
2207
2208 if (stateChange & Qt::WindowFullScreen) {
2209 if (newState & Qt::WindowFullScreen) {
2210#ifndef Q_FLATTEN_EXPOSE
2211 UINT newStyle = WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_POPUP;
2212#else
2213 UINT newStyle = WS_POPUP;
2214#endif
2215 // Save geometry and style to be restored when fullscreen
2216 // is turned off again, since on Windows, it is not a real
2217 // Window state but emulated by changing geometry and style.
2218 if (!m_savedStyle) {
2219 m_savedStyle = style();
2220 if ((oldState & Qt::WindowMinimized) || (oldState & Qt::WindowMaximized)) {
2221 const QRect nf = normalFrameGeometry(m_data.hwnd);
2222 if (nf.isValid())
2223 m_savedFrameGeometry = nf;
2224 } else {
2225 m_savedFrameGeometry = frameGeometry_sys();
2226 }
2227 }
2228 if (newState & Qt::WindowMaximized)
2229 setFlag(MaximizeToFullScreen);
2230 if (m_savedStyle & WS_SYSMENU)
2231 newStyle |= WS_SYSMENU;
2232 if (visible)
2233 newStyle |= WS_VISIBLE;
2234 if (testFlag(HasBorderInFullScreen))
2235 newStyle |= WS_BORDER;
2236 setStyle(newStyle);
2237 // Use geometry of QWindow::screen() within creation or the virtual screen the
2238 // window is in (QTBUG-31166, QTBUG-30724).
2239 const QScreen *screen = window()->screen();
2240 if (!screen)
2241 screen = QGuiApplication::primaryScreen();
2242 const QRect r = screen ? QHighDpi::toNativePixels(screen->geometry(), window()) : m_savedFrameGeometry;
2243
2244 if (newState & Qt::WindowMinimized) {
2245 setMinimizedGeometry(m_data.hwnd, r);
2246 if (stateChange & Qt::WindowMaximized)
2247 setRestoreMaximizedFlag(m_data.hwnd, newState & Qt::WindowMaximized);
2248 } else {
2249 const UINT swpf = SWP_FRAMECHANGED | SWP_NOACTIVATE;
2250 const bool wasSync = testFlag(SynchronousGeometryChangeEvent);
2251 setFlag(SynchronousGeometryChangeEvent);
2252 SetWindowPos(m_data.hwnd, HWND_TOP, r.left(), r.top(), r.width(), r.height(), swpf);
2253 if (!wasSync)
2254 clearFlag(SynchronousGeometryChangeEvent);
2255 clearFlag(MaximizeToFullScreen);
2256 QWindowSystemInterface::handleGeometryChange(window(), r);
2257 QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents);
2258 }
2259 } else {
2260 // Restore saved state.
2261 unsigned newStyle = m_savedStyle ? m_savedStyle : style();
2262 if (visible)
2263 newStyle |= WS_VISIBLE;
2264 setStyle(newStyle);
2265
2266 const QScreen *screen = window()->screen();
2267 if (!screen)
2268 screen = QGuiApplication::primaryScreen();
2269 // That area of the virtual desktop might not be covered by a screen anymore.
2270 if (!screen->geometry().intersects(m_savedFrameGeometry))
2271 m_savedFrameGeometry.moveTo(screen->geometry().topLeft());
2272
2273 if (newState & Qt::WindowMinimized) {
2274 setMinimizedGeometry(m_data.hwnd, m_savedFrameGeometry);
2275 if (stateChange & Qt::WindowMaximized)
2276 setRestoreMaximizedFlag(m_data.hwnd, newState & Qt::WindowMaximized);
2277 } else {
2278 UINT swpf = SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOACTIVATE;
2279 if (!m_savedFrameGeometry.isValid())
2280 swpf |= SWP_NOSIZE | SWP_NOMOVE;
2281 const bool wasSync = testFlag(SynchronousGeometryChangeEvent);
2282 setFlag(SynchronousGeometryChangeEvent);
2283 // After maximized/fullscreen; the window can be in a maximized state. Clear
2284 // it before applying the normal geometry.
2285 if (windowVisibility_sys(m_data.hwnd) == QWindow::Maximized)
2286 ShowWindow(m_data.hwnd, SW_SHOWNOACTIVATE);
2287 SetWindowPos(m_data.hwnd, nullptr, m_savedFrameGeometry.x(), m_savedFrameGeometry.y(),
2288 m_savedFrameGeometry.width(), m_savedFrameGeometry.height(), swpf);
2289 if (!wasSync)
2290 clearFlag(SynchronousGeometryChangeEvent);
2291 // preserve maximized state
2292 if (visible) {
2293 setFlag(WithinMaximize);
2294 ShowWindow(m_data.hwnd,
2295 (newState & Qt::WindowMaximized) ? SW_MAXIMIZE : SW_SHOWNA);
2296 clearFlag(WithinMaximize);
2297 }
2298 }
2299 m_savedStyle = 0;
2300 m_savedFrameGeometry = QRect();
2301 }
2302 } else if ((oldState & Qt::WindowMaximized) != (newState & Qt::WindowMaximized)) {
2303 if (visible && !(newState & Qt::WindowMinimized)) {
2304 setFlag(WithinMaximize);
2305 if (newState & Qt::WindowFullScreen)
2306 setFlag(MaximizeToFullScreen);
2307 ShowWindow(m_data.hwnd,
2308 (newState & Qt::WindowMaximized) ? SW_MAXIMIZE : SW_SHOWNOACTIVATE);
2309 clearFlag(WithinMaximize);
2310 clearFlag(MaximizeToFullScreen);
2311 } else if (visible && (oldState & newState & Qt::WindowMinimized)) {
2312 // change of the maximized state while keeping minimized
2313 setRestoreMaximizedFlag(m_data.hwnd, newState & Qt::WindowMaximized);
2314 }
2315 }
2316
2317 if (stateChange & Qt::WindowMinimized) {
2318 if (visible) {
2319 ShowWindow(m_data.hwnd,
2320 (newState & Qt::WindowMinimized) ? SW_MINIMIZE :
2321 (newState & Qt::WindowMaximized) ? SW_MAXIMIZE : SW_SHOWNORMAL);
2322 if ((newState & Qt::WindowMinimized) && (stateChange & Qt::WindowMaximized))
2323 setRestoreMaximizedFlag(m_data.hwnd, newState & Qt::WindowMaximized);
2324 }
2325 }
2326 qCDebug(lcQpaWindows) << '<' << __FUNCTION__ << this << window() << newState;
2327}
2328
2329void QWindowsWindow::setStyle(unsigned s) const
2330{
2331 qCDebug(lcQpaWindows) << __FUNCTION__ << this << window() << debugWinStyle(s);
2332 setFlag(WithinSetStyle);
2333 SetWindowLongPtr(m_data.hwnd, GWL_STYLE, s);
2334 clearFlag(WithinSetStyle);
2335}
2336
2337void QWindowsWindow::setExStyle(unsigned s) const
2338{
2339 qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << ' ' << this << ' ' << window()
2340 << " 0x" << QByteArray::number(s, 16);
2341 SetWindowLongPtr(m_data.hwnd, GWL_EXSTYLE, s);
2342}
2343
2344bool QWindowsWindow::windowEvent(QEvent *event)
2345{
2346 switch (event->type()) {
2347 case QEvent::WindowBlocked: // Blocked by another modal window.
2348 setEnabled(false);
2349 setFlag(BlockedByModal);
2350 if (hasMouseCapture())
2351 ReleaseCapture();
2352 break;
2353 case QEvent::WindowUnblocked:
2354 setEnabled(true);
2355 clearFlag(BlockedByModal);
2356 break;
2357 default:
2358 break;
2359 }
2360
2361 return QPlatformWindow::windowEvent(event);
2362}
2363
2364void QWindowsWindow::propagateSizeHints()
2365{
2366 qCDebug(lcQpaWindows) << __FUNCTION__ << this << window();
2367}
2368
2369bool QWindowsWindow::handleGeometryChangingMessage(MSG *message, const QWindow *qWindow, const QMargins &margins)
2370{
2371 auto *windowPos = reinterpret_cast<WINDOWPOS *>(message->lParam);
2372 if ((windowPos->flags & SWP_NOZORDER) == 0) {
2373 if (QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(qWindow)) {
2374 QWindow *parentWindow = qWindow->parent();
2375 HWND parentHWND = GetAncestor(windowPos->hwnd, GA_PARENT);
2376 HWND desktopHWND = GetDesktopWindow();
2377 platformWindow->m_data.embedded = !parentWindow && parentHWND && (parentHWND != desktopHWND);
2378 }
2379 if (qWindow->flags().testFlag(Qt::WindowStaysOnBottomHint))
2380 windowPos->hwndInsertAfter = HWND_BOTTOM;
2381 }
2382 if (!qWindow->isTopLevel()) // Implement hasHeightForWidth().
2383 return false;
2384 if ((windowPos->flags & (SWP_NOCOPYBITS | SWP_NOSIZE)))
2385 return false;
2386 const QRect suggestedFrameGeometry(windowPos->x, windowPos->y,
2387 windowPos->cx, windowPos->cy);
2388 const QRect suggestedGeometry = suggestedFrameGeometry - margins;
2389 const QRectF correctedGeometryF = QPlatformWindow::closestAcceptableGeometry(qWindow, suggestedGeometry);
2390 if (!correctedGeometryF.isValid())
2391 return false;
2392 const QRect correctedFrameGeometry = correctedGeometryF.toRect() + margins;
2393 if (correctedFrameGeometry == suggestedFrameGeometry)
2394 return false;
2395 windowPos->x = correctedFrameGeometry.left();
2396 windowPos->y = correctedFrameGeometry.top();
2397 windowPos->cx = correctedFrameGeometry.width();
2398 windowPos->cy = correctedFrameGeometry.height();
2399 return true;
2400}
2401
2402bool QWindowsWindow::handleGeometryChanging(MSG *message) const
2403{
2404 const QMargins margins = window()->isTopLevel() ? fullFrameMargins() : QMargins();
2405 return QWindowsWindow::handleGeometryChangingMessage(message, window(), margins);
2406}
2407
2408void QWindowsWindow::setFullFrameMargins(const QMargins &newMargins)
2409{
2410 if (m_data.fullFrameMargins != newMargins) {
2411 qCDebug(lcQpaWindows) << __FUNCTION__ << window() << m_data.fullFrameMargins << "->" << newMargins;
2412 m_data.fullFrameMargins = newMargins;
2413 }
2414}
2415
2416void QWindowsWindow::updateFullFrameMargins()
2417{
2418 // Normally obtained from WM_NCCALCSIZE
2419 const auto systemMargins = testFlag(DisableNonClientScaling)
2420 ? QWindowsGeometryHint::frameOnPrimaryScreen(m_data.hwnd)
2421 : frameMargins_sys();
2422 setFullFrameMargins(systemMargins + m_data.customMargins);
2423}
2424
2425QMargins QWindowsWindow::frameMargins() const
2426{
2427 QMargins result = fullFrameMargins();
2428 if (isTopLevel() && m_data.hasFrame)
2429 result -= invisibleMargins(geometry().topLeft());
2430 return result;
2431}
2432
2433QMargins QWindowsWindow::fullFrameMargins() const
2434{
2435 return m_data.fullFrameMargins;
2436}
2437
2438void QWindowsWindow::setOpacity(qreal level)
2439{
2440 qCDebug(lcQpaWindows) << __FUNCTION__ << level;
2441 if (!qFuzzyCompare(m_opacity, level)) {
2442 m_opacity = level;
2443 if (m_data.hwnd)
2444 setWindowOpacity(m_data.hwnd, m_data.flags,
2445 window()->format().hasAlpha(), testFlag(OpenGLSurface) || testFlag(VulkanSurface),
2446 level);
2447 }
2448}
2449
2450static inline HRGN createRectRegion(const QRect &r)
2451{
2452 return CreateRectRgn(r.left(), r.top(), r.x() + r.width(), r.y() + r.height());
2453}
2454
2455static inline void addRectToWinRegion(const QRect &rect, HRGN *winRegion)
2456{
2457 if (const HRGN rectRegion = createRectRegion(rect)) {
2458 HRGN result = CreateRectRgn(0, 0, 0, 0);
2459 if (CombineRgn(result, *winRegion, rectRegion, RGN_OR)) {
2460 DeleteObject(*winRegion);
2461 *winRegion = result;
2462 }
2463 DeleteObject(rectRegion);
2464 }
2465}
2466
2467static HRGN qRegionToWinRegion(const QRegion &region)
2468{
2469 auto it = region.begin();
2470 const auto end = region.end();
2471 if (it == end)
2472 return nullptr;
2473 HRGN hRegion = createRectRegion(*it);
2474 while (++it != end)
2475 addRectToWinRegion(*it, &hRegion);
2476 return hRegion;
2477}
2478
2479void QWindowsWindow::setMask(const QRegion &region)
2480{
2481 if (region.isEmpty()) {
2482 SetWindowRgn(m_data.hwnd, nullptr, true);
2483 return;
2484 }
2485 const HRGN winRegion = qRegionToWinRegion(region);
2486
2487 // Mask is in client area coordinates, so offset it in case we have a frame
2488 if (window()->isTopLevel()) {
2489 const QMargins margins = fullFrameMargins();
2490 OffsetRgn(winRegion, margins.left(), margins.top());
2491 }
2492
2493 // SetWindowRgn takes ownership.
2494 if (!SetWindowRgn(m_data.hwnd, winRegion, true))
2495 DeleteObject(winRegion);
2496}
2497
2498void QWindowsWindow::requestActivateWindow()
2499{
2500 qCDebug(lcQpaWindows) << __FUNCTION__ << this << window();
2501 // 'Active' state handling is based in focus since it needs to work for
2502 // child windows as well.
2503 if (m_data.hwnd) {
2504 const DWORD currentThread = GetCurrentThreadId();
2505 bool attached = false;
2506 DWORD foregroundThread = 0;
2507
2508 // QTBUG-14062, QTBUG-37435: Windows normally only flashes the taskbar entry
2509 // when activating windows of inactive applications. Attach to the input of the
2510 // currently active window while setting the foreground window to always activate
2511 // the window when desired.
2512 if (QGuiApplication::applicationState() != Qt::ApplicationActive
2513 && QWindowsNativeInterface::windowActivationBehavior() == QWindowsWindowFunctions::AlwaysActivateWindow) {
2514 if (const HWND foregroundWindow = GetForegroundWindow()) {
2515 foregroundThread = GetWindowThreadProcessId(foregroundWindow, nullptr);
2516 if (foregroundThread && foregroundThread != currentThread)
2517 attached = AttachThreadInput(foregroundThread, currentThread, TRUE) == TRUE;
2518 if (attached) {
2519 if (!window()->flags().testFlag(Qt::WindowStaysOnBottomHint)
2520 && !window()->flags().testFlag(Qt::WindowStaysOnTopHint)
2521 && window()->type() != Qt::ToolTip) {
2522 const UINT swpFlags = SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER;
2523 SetWindowPos(m_data.hwnd, HWND_TOPMOST, 0, 0, 0, 0, swpFlags);
2524 SetWindowPos(m_data.hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, swpFlags);
2525 }
2526 }
2527 }
2528 }
2529 SetForegroundWindow(m_data.hwnd);
2530 SetFocus(m_data.hwnd);
2531 if (attached)
2532 AttachThreadInput(foregroundThread, currentThread, FALSE);
2533 }
2534}
2535
2536bool QWindowsWindow::setKeyboardGrabEnabled(bool grab)
2537{
2538 if (!m_data.hwnd) {
2539 qWarning("%s: No handle", __FUNCTION__);
2540 return false;
2541 }
2542 qCDebug(lcQpaWindows) << __FUNCTION__ << this << window() << grab;
2543
2544 QWindowsContext *context = QWindowsContext::instance();
2545 if (grab) {
2546 context->setKeyGrabber(window());
2547 } else {
2548 if (context->keyGrabber() == window())
2549 context->setKeyGrabber(nullptr);
2550 }
2551 return true;
2552}
2553
2554bool QWindowsWindow::setMouseGrabEnabled(bool grab)
2555{
2556 qCDebug(lcQpaWindows) << __FUNCTION__ << window() << grab;
2557 if (!m_data.hwnd) {
2558 qWarning("%s: No handle", __FUNCTION__);
2559 return false;
2560 }
2561 if (!isVisible() && grab) {
2562 qWarning("%s: Not setting mouse grab for invisible window %s/'%s'",
2563 __FUNCTION__, window()->metaObject()->className(),
2564 qPrintable(window()->objectName()));
2565 return false;
2566 }
2567 // release grab or an explicit grab overriding autocapture: Clear flag.
2568 clearFlag(QWindowsWindow::AutoMouseCapture);
2569 if (hasMouseCapture() != grab) {
2570 if (grab) {
2571 SetCapture(m_data.hwnd);
2572 } else {
2573 ReleaseCapture();
2574 }
2575 }
2576 return grab;
2577}
2578
2579static inline DWORD cornerToWinOrientation(Qt::Corner corner)
2580{
2581 switch (corner) {
2582 case Qt::TopLeftCorner:
2583 return 0xf004; // SZ_SIZETOPLEFT;
2584 case Qt::TopRightCorner:
2585 return 0xf005; // SZ_SIZETOPRIGHT
2586 case Qt::BottomLeftCorner:
2587 return 0xf007; // SZ_SIZEBOTTOMLEFT
2588 case Qt::BottomRightCorner:
2589 return 0xf008; // SZ_SIZEBOTTOMRIGHT
2590 }
2591 return 0;
2592}
2593
2594bool QWindowsWindow::startSystemResize(const QPoint &, Qt::Corner corner)
2595{
2596 if (!GetSystemMenu(m_data.hwnd, FALSE))
2597 return false;
2598
2599 ReleaseCapture();
2600 PostMessage(m_data.hwnd, WM_SYSCOMMAND, cornerToWinOrientation(corner), 0);
2601 setFlag(SizeGripOperation);
2602 return true;
2603}
2604
2605bool QWindowsWindow::startSystemMove(const QPoint &)
2606{
2607 if (!GetSystemMenu(m_data.hwnd, FALSE))
2608 return false;
2609
2610 ReleaseCapture();
2611 PostMessage(m_data.hwnd, WM_SYSCOMMAND, 0xF012 /*SC_DRAGMOVE*/, 0);
2612 return true;
2613}
2614
2615void QWindowsWindow::setFrameStrutEventsEnabled(bool enabled)
2616{
2617 if (enabled) {
2618 setFlag(FrameStrutEventsEnabled);
2619 } else {
2620 clearFlag(FrameStrutEventsEnabled);
2621 }
2622}
2623
2624static int getBorderWidth(const QPlatformScreen *screen)
2625{
2626 NONCLIENTMETRICS ncm;
2627 QWindowsContext::nonClientMetricsForScreen(&ncm, screen);
2628 return ncm.iBorderWidth + ncm.iPaddedBorderWidth + 2;
2629}
2630
2631void QWindowsWindow::getSizeHints(MINMAXINFO *mmi) const
2632{
2633 // We don't apply the min/max size hint as we change the dpi, because we did not adjust the
2634 // QScreen of the window yet so we don't have the min/max with the right ratio
2635 if (!testFlag(QWindowsWindow::WithinDpiChanged))
2636 QWindowsGeometryHint::applyToMinMaxInfo(window(), fullFrameMargins(), mmi);
2637
2638 // This block fixes QTBUG-8361, QTBUG-4362: Frameless/title-less windows shouldn't cover the
2639 // taskbar when maximized
2640 if ((testFlag(WithinMaximize) || window()->windowStates().testFlag(Qt::WindowMinimized))
2641 && (m_data.flags.testFlag(Qt::FramelessWindowHint)
2642 || (m_data.flags.testFlag(Qt::CustomizeWindowHint) && !m_data.flags.testFlag(Qt::WindowTitleHint)))) {
2643 const QScreen *screen = window()->screen();
2644
2645 // Documentation of MINMAXINFO states that it will only work for the primary screen
2646 if (screen && screen == QGuiApplication::primaryScreen()) {
2647 const QRect availableGeometry = QHighDpi::toNativePixels(screen->availableGeometry(), screen);
2648 mmi->ptMaxSize.y = availableGeometry.height();
2649
2650 // Width, because you can have the taskbar on the sides too.
2651 mmi->ptMaxSize.x = availableGeometry.width();
2652
2653 // If you have the taskbar on top, or on the left you don't want it at (0,0):
2654 mmi->ptMaxPosition.x = availableGeometry.x();
2655 mmi->ptMaxPosition.y = availableGeometry.y();
2656 if (!m_data.flags.testFlag(Qt::FramelessWindowHint)) {
2657 const int borderWidth = getBorderWidth(screen->handle());
2658 mmi->ptMaxSize.x += borderWidth * 2;
2659 mmi->ptMaxSize.y += borderWidth * 2;
2660 mmi->ptMaxTrackSize = mmi->ptMaxSize;
2661 mmi->ptMaxPosition.x -= borderWidth;
2662 mmi->ptMaxPosition.y -= borderWidth;
2663 }
2664 } else if (!screen){
2665 qWarning("window()->screen() returned a null screen");
2666 }
2667 }
2668
2669 qCDebug(lcQpaWindows) << __FUNCTION__ << window() << *mmi;
2670}
2671
2672bool QWindowsWindow::handleNonClientHitTest(const QPoint &globalPos, LRESULT *result) const
2673{
2674 // QTBUG-32663, suppress resize cursor for fixed size windows.
2675 const QWindow *w = window();
2676 if (!w->isTopLevel() // Task 105852, minimized windows need to respond to user input.
2677 || !(m_windowState & ~Qt::WindowActive)
2678 || (m_data.flags & Qt::FramelessWindowHint)) {
2679 return false;
2680 }
2681 const QSize minimumSize = w->minimumSize();
2682 if (minimumSize.isEmpty())
2683 return false;
2684 const QSize maximumSize = w->maximumSize();
2685 const bool fixedWidth = minimumSize.width() == maximumSize.width();
2686 const bool fixedHeight = minimumSize.height() == maximumSize.height();
2687 if (!fixedWidth && !fixedHeight)
2688 return false;
2689 const QPoint localPos = w->mapFromGlobal(QHighDpi::fromNativePixels(globalPos, w));
2690 const QSize size = w->size();
2691 if (fixedHeight) {
2692 if (localPos.y() >= size.height()) {
2693 *result = HTBORDER; // Unspecified border, no resize cursor.
2694 return true;
2695 }
2696 if (localPos.y() < 0) {
2697 const QMargins margins = frameMargins();
2698 const int topResizeBarPos = margins.left() - margins.top();
2699 if (localPos.y() < topResizeBarPos) {
2700 *result = HTCAPTION; // Extend caption over top resize bar, let's user move the window.
2701 return true;
2702 }
2703 }
2704 }
2705 if (fixedWidth && (localPos.x() < 0 || localPos.x() >= size.width())) {
2706 *result = HTBORDER; // Unspecified border, no resize cursor.
2707 return true;
2708 }
2709 return false;
2710}
2711
2712#ifndef QT_NO_CURSOR
2713// Return the default cursor (Arrow) from QWindowsCursor's cache.
2714static inline CursorHandlePtr defaultCursor(const QWindow *w)
2715{
2716 if (QScreen *screen = w->screen())
2717 if (const QPlatformScreen *platformScreen = screen->handle())
2718 if (QPlatformCursor *cursor = platformScreen->cursor())
2719 return static_cast<QWindowsCursor *>(cursor)->standardWindowCursor(Qt::ArrowCursor);
2720 return CursorHandlePtr(new CursorHandle(QWindowsCursor::createCursorFromShape(Qt::ArrowCursor)));
2721}
2722
2723// Check whether to apply a new cursor. Either the window in question is
2724// currently under mouse, or it is the parent of the window under mouse and
2725// there is no other window with an explicitly set cursor in-between.
2726static inline bool applyNewCursor(const QWindow *w)
2727{
2728 const QWindow *underMouse = QWindowsContext::instance()->windowUnderMouse();
2729 if (underMouse == w)
2730 return true;
2731 for (const QWindow *p = underMouse; p ; p = p->parent()) {
2732 if (p == w)
2733 return true;
2734 const QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(p);
2735 if (platformWindow && !platformWindow->cursor()->isNull())
2736 return false;
2737 }
2738 return false;
2739}
2740#endif // !QT_NO_CURSOR
2741
2742/*!
2743 \brief Applies to cursor property set on the window to the global cursor.
2744
2745 \sa QWindowsCursor
2746*/
2747
2748void QWindowsWindow::applyCursor()
2749{
2750 if (QWindowsCursor::hasOverrideCursor()) {
2751 if (isTopLevel())
2752 QWindowsCursor::enforceOverrideCursor();
2753 return;
2754 }
2755#ifndef QT_NO_CURSOR
2756 if (m_cursor->isNull()) { // Recurse up to parent with non-null cursor. Set default for toplevel.
2757 if (const QWindow *p = window()->parent()) {
2758 if (QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(p))
2759 platformWindow->applyCursor();
2760 } else {
2761 SetCursor(defaultCursor(window())->handle());
2762 }
2763 } else {
2764 SetCursor(m_cursor->handle());
2765 }
2766#endif
2767}
2768
2769void QWindowsWindow::setCursor(const CursorHandlePtr &c)
2770{
2771#ifndef QT_NO_CURSOR
2772 if (c->handle() != m_cursor->handle()) {
2773 const bool apply = applyNewCursor(window());
2774 qCDebug(lcQpaWindows) << window() << __FUNCTION__
2775 << c->handle() << " doApply=" << apply;
2776 m_cursor = c;
2777 if (apply)
2778 applyCursor();
2779 }
2780#endif
2781}
2782
2783void QWindowsWindow::setAlertState(bool enabled)
2784{
2785 if (isAlertState() == enabled)
2786 return;
2787 if (enabled) {
2788 alertWindow(0);
2789 setFlag(AlertState);
2790 } else {
2791 stopAlertWindow();
2792 clearFlag(AlertState);
2793 }
2794}
2795
2796void QWindowsWindow::alertWindow(int durationMs)
2797{
2798 UINT timeOutMs = GetCaretBlinkTime();
2799 if (!timeOutMs || timeOutMs == INFINITE)
2800 timeOutMs = 250;
2801
2802 FLASHWINFO info;
2803 info.cbSize = sizeof(info);
2804 info.hwnd = m_data.hwnd;
2805 info.dwFlags = FLASHW_TRAY;
2806 info.dwTimeout = timeOutMs;
2807 info.uCount = durationMs == 0 ? 10 : UINT(durationMs) / timeOutMs;
2808 FlashWindowEx(&info);
2809}
2810
2811void QWindowsWindow::stopAlertWindow()
2812{
2813 FLASHWINFO info;
2814 info.cbSize = sizeof(info);
2815 info.hwnd = m_data.hwnd;
2816 info.dwFlags = FLASHW_STOP;
2817 info.dwTimeout = 0;
2818 info.uCount = 0;
2819 FlashWindowEx(&info);
2820}
2821
2822bool QWindowsWindow::isEnabled() const
2823{
2824 return (style() & WS_DISABLED) == 0;
2825}
2826
2827void QWindowsWindow::setEnabled(bool enabled)
2828{
2829 const unsigned oldStyle = style();
2830 unsigned newStyle = oldStyle;
2831 if (enabled) {
2832 newStyle &= ~WS_DISABLED;
2833 } else {
2834 newStyle |= WS_DISABLED;
2835 }
2836 if (newStyle != oldStyle)
2837 setStyle(newStyle);
2838}
2839
2840static HICON createHIcon(const QIcon &icon, int xSize, int ySize)
2841{
2842 if (!icon.isNull()) {
2843 const QPixmap pm = icon.pixmap(icon.actualSize(QSize(xSize, ySize)));
2844 if (!pm.isNull())
2845 return qt_pixmapToWinHICON(pm);
2846 }
2847 return nullptr;
2848}
2849
2850void QWindowsWindow::setWindowIcon(const QIcon &icon)
2851{
2852 if (m_data.hwnd) {
2853 destroyIcon();
2854
2855 m_iconSmall = createHIcon(icon, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
2856 m_iconBig = createHIcon(icon, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON));
2857
2858 if (m_iconBig) {
2859 SendMessage(m_data.hwnd, WM_SETICON, 0 /* ICON_SMALL */, LPARAM(m_iconSmall));
2860 SendMessage(m_data.hwnd, WM_SETICON, 1 /* ICON_BIG */, LPARAM(m_iconBig));
2861 } else {
2862 SendMessage(m_data.hwnd, WM_SETICON, 0 /* ICON_SMALL */, LPARAM(m_iconSmall));
2863 SendMessage(m_data.hwnd, WM_SETICON, 1 /* ICON_BIG */, LPARAM(m_iconSmall));
2864 }
2865 }
2866}
2867
2868bool QWindowsWindow::isTopLevel() const
2869{
2870 return window()->isTopLevel() && !m_data.embedded;
2871}
2872
2873QWindowsMenuBar *QWindowsWindow::menuBar() const
2874{
2875 return m_menuBar.data();
2876}
2877
2878void QWindowsWindow::setMenuBar(QWindowsMenuBar *mb)
2879{
2880 m_menuBar = mb;
2881}
2882
2883/*!
2884 \brief Sets custom margins to be added to the default margins determined by
2885 the windows style in the handling of the WM_NCCALCSIZE message.
2886
2887 This is currently used to give the Aero-style QWizard a smaller top margin.
2888 The property can be set using QPlatformNativeInterface::setWindowProperty() or,
2889 before platform window creation, by setting a dynamic property
2890 on the QWindow (see QWindowsIntegration::createPlatformWindow()).
2891*/
2892
2893void QWindowsWindow::setCustomMargins(const QMargins &newCustomMargins)
2894{
2895 if (newCustomMargins != m_data.customMargins) {
2896 const QMargins oldCustomMargins = m_data.customMargins;
2897 m_data.customMargins = newCustomMargins;
2898 // Re-trigger WM_NCALCSIZE with wParam=1 by passing SWP_FRAMECHANGED
2899 const QRect currentFrameGeometry = frameGeometry_sys();
2900 const QPoint topLeft = currentFrameGeometry.topLeft();
2901 QRect newFrame = currentFrameGeometry.marginsRemoved(oldCustomMargins) + m_data.customMargins;
2902 newFrame.moveTo(topLeft);
2903 qCDebug(lcQpaWindows) << __FUNCTION__ << oldCustomMargins << "->" << newCustomMargins
2904 << currentFrameGeometry << "->" << newFrame;
2905 SetWindowPos(m_data.hwnd, nullptr, newFrame.x(), newFrame.y(), newFrame.width(), newFrame.height(), SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE);
2906 }
2907}
2908
2909void *QWindowsWindow::surface(void *nativeConfig, int *err)
2910{
2911#if QT_CONFIG(vulkan)
2912 Q_UNUSED(nativeConfig);
2913 Q_UNUSED(err);
2914 if (window()->surfaceType() == QSurface::VulkanSurface) {
2915 if (!m_vkSurface) {
2916 QVulkanInstance *inst = window()->vulkanInstance();
2917 if (inst)
2918 m_vkSurface = static_cast<QWindowsVulkanInstance *>(inst->handle())->createSurface(handle());
2919 else
2920 qWarning("Attempted to create Vulkan surface without an instance; was QWindow::setVulkanInstance() called?");
2921 }
2922 // Different semantics for VkSurfaces: the return value is the address,
2923 // not the value, given that this is a 64-bit handle even on x86.
2924 return &m_vkSurface;
2925 }
2926#elif defined(QT_NO_OPENGL)
2927 Q_UNUSED(err)
2928 Q_UNUSED(nativeConfig)
2929 return 0;
2930#endif
2931#ifndef QT_NO_OPENGL
2932 if (!m_surface) {
2933 if (QWindowsStaticOpenGLContext *staticOpenGLContext = QWindowsIntegration::staticOpenGLContext())
2934 m_surface = staticOpenGLContext->createWindowSurface(m_data.hwnd, nativeConfig, err);
2935 }
2936
2937 return m_surface;
2938#endif
2939}
2940
2941void QWindowsWindow::invalidateSurface()
2942{
2943#if QT_CONFIG(vulkan)
2944 if (m_vkSurface) {
2945 QVulkanInstance *inst = window()->vulkanInstance();
2946 if (inst)
2947 static_cast<QWindowsVulkanInstance *>(inst->handle())->destroySurface(m_vkSurface);
2948 m_vkSurface = VK_NULL_HANDLE;
2949 }
2950#endif
2951#ifndef QT_NO_OPENGL
2952 if (m_surface) {
2953 if (QWindowsStaticOpenGLContext *staticOpenGLContext = QWindowsIntegration::staticOpenGLContext())
2954 staticOpenGLContext->destroyWindowSurface(m_surface);
2955 m_surface = nullptr;
2956 }
2957#endif // QT_NO_OPENGL
2958}
2959
2960void QWindowsWindow::setTouchWindowTouchTypeStatic(QWindow *window, QWindowsWindowFunctions::TouchWindowTouchTypes touchTypes)
2961{
2962 if (!window->handle())
2963 return;
2964 static_cast<QWindowsWindow *>(window->handle())->registerTouchWindow(touchTypes);
2965}
2966
2967void QWindowsWindow::registerTouchWindow(QWindowsWindowFunctions::TouchWindowTouchTypes touchTypes)
2968{
2969 if ((QWindowsContext::instance()->systemInfo() & QWindowsContext::SI_SupportsTouch)
2970 && !testFlag(TouchRegistered)) {
2971 ULONG touchFlags = 0;
2972 const bool ret = IsTouchWindow(m_data.hwnd, &touchFlags);
2973 // Return if it is not a touch window or the flags are already set by a hook
2974 // such as HCBT_CREATEWND
2975 if (ret || touchFlags != 0)
2976 return;
2977 if (RegisterTouchWindow(m_data.hwnd, ULONG(touchTypes)))
2978 setFlag(TouchRegistered);
2979 else
2980 qErrnoWarning("RegisterTouchWindow() failed for window '%s'.", qPrintable(window()->objectName()));
2981 }
2982}
2983
2984void QWindowsWindow::aboutToMakeCurrent()
2985{
2986#ifndef QT_NO_OPENGL
2987 // For RasterGLSurface windows, that become OpenGL windows dynamically, it might be
2988 // time to set up some GL specifics. This is particularly important for layered
2989 // windows (WS_EX_LAYERED due to alpha > 0).
2990 const bool isCompositing = qt_window_private(window())->compositing;
2991 if (isCompositing != testFlag(Compositing)) {
2992 if (isCompositing)
2993 setFlag(Compositing);
2994 else
2995 clearFlag(Compositing);
2996
2997 updateGLWindowSettings(window(), m_data.hwnd, m_data.flags, m_opacity);
2998 }
2999#endif
3000}
3001
3002void QWindowsWindow::setHasBorderInFullScreenStatic(QWindow *window, bool border)
3003{
3004 if (QPlatformWindow *handle = window->handle())
3005 static_cast<QWindowsWindow *>(handle)->setHasBorderInFullScreen(border);
3006 else
3007 window->setProperty(hasBorderInFullScreenProperty, QVariant(border));
3008}
3009
3010void QWindowsWindow::setHasBorderInFullScreenDefault(bool border)
3011{
3012 m_borderInFullScreenDefault = border;
3013}
3014
3015void QWindowsWindow::setHasBorderInFullScreen(bool border)
3016{
3017 if (testFlag(HasBorderInFullScreen) == border)
3018 return;
3019 if (border)
3020 setFlag(HasBorderInFullScreen);
3021 else
3022 clearFlag(HasBorderInFullScreen);
3023 // Directly apply the flag in case we are fullscreen.
3024 if (m_windowState == Qt::WindowFullScreen) {
3025 LONG_PTR style = GetWindowLongPtr(handle(), GWL_STYLE);
3026 if (border)
3027 style |= WS_BORDER;
3028 else
3029 style &= ~WS_BORDER;
3030 SetWindowLongPtr(handle(), GWL_STYLE, style);
3031 }
3032}
3033
3034QString QWindowsWindow::formatWindowTitle(const QString &title)
3035{
3036 return QPlatformWindow::formatWindowTitle(title, QStringLiteral(" - "));
3037}
3038
3039QT_END_NAMESPACE
3040

Warning: That file was not part of the compilation database. It may have many parsing errors.