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