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