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 QtGui module 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 | #include "qplatformwindow.h" |
41 | #include "qplatformwindow_p.h" |
42 | #include "qplatformscreen.h" |
43 | |
44 | #include <private/qguiapplication_p.h> |
45 | #include <qpa/qwindowsysteminterface.h> |
46 | #include <QtGui/qwindow.h> |
47 | #include <QtGui/qscreen.h> |
48 | #include <private/qhighdpiscaling_p.h> |
49 | #include <private/qwindow_p.h> |
50 | |
51 | |
52 | QT_BEGIN_NAMESPACE |
53 | |
54 | /*! |
55 | Constructs a platform window with the given top level window. |
56 | */ |
57 | |
58 | QPlatformWindow::QPlatformWindow(QWindow *window) |
59 | : QPlatformSurface(window) |
60 | , d_ptr(new QPlatformWindowPrivate) |
61 | { |
62 | Q_D(QPlatformWindow); |
63 | d->rect = QHighDpi::toNativePixels(window->geometry(), window); |
64 | } |
65 | |
66 | /*! |
67 | Virtual destructor does not delete its top level window. |
68 | */ |
69 | QPlatformWindow::~QPlatformWindow() |
70 | { |
71 | } |
72 | |
73 | /*! |
74 | Called as part of QWindow::create(), after constructing |
75 | the window. Platforms should prefer to do initialization |
76 | here instead of in the constructor, as the platform window |
77 | object will be fully constructed, and associated to the |
78 | corresponding QWindow, allowing synchronous event delivery. |
79 | */ |
80 | void QPlatformWindow::initialize() |
81 | { |
82 | } |
83 | |
84 | /*! |
85 | Returns the window which belongs to the QPlatformWindow |
86 | */ |
87 | QWindow *QPlatformWindow::window() const |
88 | { |
89 | return static_cast<QWindow *>(m_surface); |
90 | } |
91 | |
92 | /*! |
93 | Returns the parent platform window (or \nullptr if orphan). |
94 | */ |
95 | QPlatformWindow *QPlatformWindow::parent() const |
96 | { |
97 | return window()->parent() ? window()->parent()->handle() : nullptr; |
98 | } |
99 | |
100 | /*! |
101 | Returns the platform screen handle corresponding to this platform window, |
102 | or null if the window is not associated with a screen. |
103 | */ |
104 | QPlatformScreen *QPlatformWindow::screen() const |
105 | { |
106 | QScreen *scr = window()->screen(); |
107 | return scr ? scr->handle() : nullptr; |
108 | } |
109 | |
110 | /*! |
111 | Returns the actual surface format of the window. |
112 | */ |
113 | QSurfaceFormat QPlatformWindow::format() const |
114 | { |
115 | return QSurfaceFormat(); |
116 | } |
117 | |
118 | /*! |
119 | This function is called by Qt whenever a window is moved or resized using the QWindow API. |
120 | |
121 | Unless you also override QPlatformWindow::geometry(), you need to call the baseclass |
122 | implementation of this function in any override of QPlatformWindow::setGeometry(), as |
123 | QWindow::geometry() is expected to report back the set geometry until a confirmation |
124 | (or rejection) of the new geometry comes back from the window manager and is reported |
125 | via QWindowSystemInterface::handleGeometryChange(). |
126 | |
127 | Window move/resizes can also be triggered spontaneously by the window manager, or as a |
128 | response to an earlier requested move/resize via the Qt APIs. There is no need to call |
129 | this function from the window manager callback, instead call |
130 | QWindowSystemInterface::handleGeometryChange(). |
131 | |
132 | The position(x, y) part of the rect might be inclusive or exclusive of the window frame |
133 | as returned by frameMargins(). You can detect this in the plugin by checking |
134 | qt_window_private(window())->positionPolicy. |
135 | */ |
136 | void QPlatformWindow::setGeometry(const QRect &rect) |
137 | { |
138 | Q_D(QPlatformWindow); |
139 | d->rect = rect; |
140 | } |
141 | |
142 | /*! |
143 | Returns the current geometry of a window |
144 | */ |
145 | QRect QPlatformWindow::geometry() const |
146 | { |
147 | Q_D(const QPlatformWindow); |
148 | return d->rect; |
149 | } |
150 | |
151 | /*! |
152 | Returns the geometry of a window in 'normal' state |
153 | (neither maximized, fullscreen nor minimized) for saving geometries to |
154 | application settings. |
155 | |
156 | \since 5.3 |
157 | */ |
158 | QRect QPlatformWindow::normalGeometry() const |
159 | { |
160 | return QRect(); |
161 | } |
162 | |
163 | QMargins QPlatformWindow::frameMargins() const |
164 | { |
165 | return QMargins(); |
166 | } |
167 | |
168 | /*! |
169 | The safe area margins of a window represent the area that is safe to |
170 | place content within, without intersecting areas of the screen where |
171 | system UI is placed, or where a screen bezel may cover the content. |
172 | */ |
173 | QMargins QPlatformWindow::safeAreaMargins() const |
174 | { |
175 | return QMargins(); |
176 | } |
177 | |
178 | /*! |
179 | Reimplemented in subclasses to show the surface |
180 | if \a visible is \c true, and hide it if \a visible is \c false. |
181 | |
182 | The default implementation sends a synchronous expose event. |
183 | */ |
184 | void QPlatformWindow::setVisible(bool visible) |
185 | { |
186 | Q_UNUSED(visible); |
187 | QRect rect(QPoint(), geometry().size()); |
188 | QWindowSystemInterface::handleExposeEvent(window(), rect); |
189 | QWindowSystemInterface::flushWindowSystemEvents(); |
190 | } |
191 | |
192 | /*! |
193 | Requests setting the window flags of this surface |
194 | to \a flags. |
195 | */ |
196 | void QPlatformWindow::setWindowFlags(Qt::WindowFlags flags) |
197 | { |
198 | Q_UNUSED(flags); |
199 | } |
200 | |
201 | /*! |
202 | Returns if this window is exposed in the windowing system. |
203 | |
204 | An exposeEvent() is sent every time this value changes. |
205 | */ |
206 | |
207 | bool QPlatformWindow::isExposed() const |
208 | { |
209 | return window()->isVisible(); |
210 | } |
211 | |
212 | /*! |
213 | Returns \c true if the window should appear active from a style perspective. |
214 | |
215 | This function can make platform-specific isActive checks, such as checking |
216 | if the QWindow is embedded in an active native window. |
217 | */ |
218 | bool QPlatformWindow::isActive() const |
219 | { |
220 | return false; |
221 | } |
222 | |
223 | /*! |
224 | Returns \c true if the window is an ancestor of the given \a child. |
225 | |
226 | Platform overrides should iterate the native window hierarchy of the child, |
227 | to ensure that ancestary is reflected even with native windows in the window |
228 | hierarchy. |
229 | */ |
230 | bool QPlatformWindow::isAncestorOf(const QPlatformWindow *child) const |
231 | { |
232 | for (const QPlatformWindow *parent = child->parent(); parent; parent = child->parent()) { |
233 | if (parent == this) |
234 | return true; |
235 | } |
236 | |
237 | return false; |
238 | } |
239 | |
240 | /*! |
241 | Returns \c true if the window is a child of a non-Qt window. |
242 | |
243 | A embedded window has no parent platform window as reflected |
244 | though parent(), but will have a native parent window. |
245 | */ |
246 | bool QPlatformWindow::isEmbedded() const |
247 | { |
248 | return false; |
249 | } |
250 | |
251 | /*! |
252 | Translates the window coordinate \a pos to global screen |
253 | coordinates using native methods. This is required for embedded windows, |
254 | where the topmost QWindow coordinates are not global screen coordinates. |
255 | |
256 | Returns \a pos if there is no platform specific implementation. |
257 | */ |
258 | QPoint QPlatformWindow::mapToGlobal(const QPoint &pos) const |
259 | { |
260 | const QPlatformWindow *p = this; |
261 | QPoint result = pos; |
262 | while (p) { |
263 | result += p->geometry().topLeft(); |
264 | p = p->parent(); |
265 | } |
266 | return result; |
267 | } |
268 | |
269 | QPointF QPlatformWindow::mapToGlobalF(const QPointF &pos) const |
270 | { |
271 | const QPoint posPt = pos.toPoint(); |
272 | const QPointF delta = pos - posPt; |
273 | return mapToGlobal(posPt) + delta; |
274 | } |
275 | |
276 | QPointF QPlatformWindow::mapFromGlobalF(const QPointF &pos) const |
277 | { |
278 | const QPoint posPt = pos.toPoint(); |
279 | const QPointF delta = pos - posPt; |
280 | return mapFromGlobal(posPt) + delta; |
281 | } |
282 | |
283 | /*! |
284 | Translates the global screen coordinate \a pos to window |
285 | coordinates using native methods. This is required for embedded windows, |
286 | where the topmost QWindow coordinates are not global screen coordinates. |
287 | |
288 | Returns \a pos if there is no platform specific implementation. |
289 | */ |
290 | QPoint QPlatformWindow::mapFromGlobal(const QPoint &pos) const |
291 | { |
292 | const QPlatformWindow *p = this; |
293 | QPoint result = pos; |
294 | while (p) { |
295 | result -= p->geometry().topLeft(); |
296 | p = p->parent(); |
297 | } |
298 | return result; |
299 | } |
300 | |
301 | /*! |
302 | Requests setting the window state of this surface |
303 | to \a type. |
304 | |
305 | Qt::WindowActive can be ignored. |
306 | */ |
307 | void QPlatformWindow::setWindowState(Qt::WindowStates) |
308 | { |
309 | } |
310 | |
311 | /*! |
312 | Reimplement in subclasses to return a handle to the native window |
313 | */ |
314 | WId QPlatformWindow::winId() const |
315 | { |
316 | // Return anything but 0. Returning 0 would cause havoc with QWidgets on |
317 | // very basic platform plugins that do not reimplement this function, |
318 | // because the top-level widget's internalWinId() would always be 0 which |
319 | // would mean top-levels are never treated as native. |
320 | return WId(1); |
321 | } |
322 | |
323 | //jl: It would be useful to have a property on the platform window which indicated if the sub-class |
324 | // supported the setParent. If not, then geometry would be in screen coordinates. |
325 | /*! |
326 | This function is called to enable native child window in QPA. It is common not to support this |
327 | feature in Window systems, but can be faked. When this function is called all geometry of this |
328 | platform window will be relative to the parent. |
329 | */ |
330 | void QPlatformWindow::setParent(const QPlatformWindow *parent) |
331 | { |
332 | Q_UNUSED(parent); |
333 | qWarning("This plugin does not support setParent!" ); |
334 | } |
335 | |
336 | /*! |
337 | Reimplement to set the window title to \a title. |
338 | |
339 | The implementation might want to append the application display name to |
340 | the window title, like Windows and Linux do. |
341 | |
342 | \sa QGuiApplication::applicationDisplayName() |
343 | */ |
344 | void QPlatformWindow::setWindowTitle(const QString &title) { Q_UNUSED(title); } |
345 | |
346 | /*! |
347 | Reimplement to set the window file path to \a filePath |
348 | */ |
349 | void QPlatformWindow::setWindowFilePath(const QString &filePath) { Q_UNUSED(filePath); } |
350 | |
351 | /*! |
352 | Reimplement to set the window icon to \a icon |
353 | */ |
354 | void QPlatformWindow::setWindowIcon(const QIcon &icon) { Q_UNUSED(icon); } |
355 | |
356 | /*! |
357 | Reimplement to let the platform handle non-spontaneous window close. |
358 | |
359 | When reimplementing make sure to call the base class implementation |
360 | or QWindowSystemInterface::handleCloseEvent(), which will prompt the |
361 | user to accept the window close (if needed) and then close the QWindow. |
362 | */ |
363 | bool QPlatformWindow::close() |
364 | { |
365 | return QWindowSystemInterface::handleCloseEvent<QWindowSystemInterface::SynchronousDelivery>(window()); |
366 | } |
367 | |
368 | /*! |
369 | Reimplement to be able to let Qt raise windows to the top of the desktop |
370 | */ |
371 | void QPlatformWindow::raise() { qWarning("This plugin does not support raise()" ); } |
372 | |
373 | /*! |
374 | Reimplement to be able to let Qt lower windows to the bottom of the desktop |
375 | */ |
376 | void QPlatformWindow::lower() { qWarning("This plugin does not support lower()" ); } |
377 | |
378 | /*! |
379 | Reimplement to propagate the size hints of the QWindow. |
380 | |
381 | The size hints include QWindow::minimumSize(), QWindow::maximumSize(), |
382 | QWindow::sizeIncrement(), and QWindow::baseSize(). |
383 | */ |
384 | void QPlatformWindow::propagateSizeHints() {qWarning("This plugin does not support propagateSizeHints()" ); } |
385 | |
386 | /*! |
387 | Reimplement to be able to let Qt set the opacity level of a window |
388 | */ |
389 | void QPlatformWindow::setOpacity(qreal level) |
390 | { |
391 | Q_UNUSED(level); |
392 | qWarning("This plugin does not support setting window opacity" ); |
393 | } |
394 | |
395 | /*! |
396 | Reimplement to be able to let Qt set the mask of a window |
397 | */ |
398 | |
399 | void QPlatformWindow::setMask(const QRegion ®ion) |
400 | { |
401 | Q_UNUSED(region); |
402 | qWarning("This plugin does not support setting window masks" ); |
403 | } |
404 | |
405 | /*! |
406 | Reimplement to let Qt be able to request activation/focus for a window |
407 | |
408 | Some window systems will probably not have callbacks for this functionality, |
409 | and then calling QWindowSystemInterface::handleWindowActivated(QWindow *w) |
410 | would be sufficient. |
411 | |
412 | If the window system has some event handling/callbacks then call |
413 | QWindowSystemInterface::handleWindowActivated(QWindow *w) when the window system |
414 | gives the notification. |
415 | |
416 | Default implementation calls QWindowSystem::handleWindowActivated(QWindow *w) |
417 | */ |
418 | void QPlatformWindow::requestActivateWindow() |
419 | { |
420 | QWindowSystemInterface::handleWindowActivated(window()); |
421 | } |
422 | |
423 | /*! |
424 | Handle changes to the orientation of the platform window's contents. |
425 | |
426 | This is a hint to the window manager in case it needs to display |
427 | additional content like popups, dialogs, status bars, or similar |
428 | in relation to the window. |
429 | |
430 | \sa QWindow::reportContentOrientationChange() |
431 | */ |
432 | void QPlatformWindow::handleContentOrientationChange(Qt::ScreenOrientation orientation) |
433 | { |
434 | Q_UNUSED(orientation); |
435 | } |
436 | |
437 | /*! |
438 | Reimplement this function in subclass to return the device pixel ratio |
439 | for the window. This is the ratio between physical pixels |
440 | and device-independent pixels. |
441 | |
442 | \sa QPlatformWindow::devicePixelRatio(); |
443 | */ |
444 | qreal QPlatformWindow::devicePixelRatio() const |
445 | { |
446 | return 1.0; |
447 | } |
448 | |
449 | bool QPlatformWindow::setKeyboardGrabEnabled(bool grab) |
450 | { |
451 | Q_UNUSED(grab); |
452 | qWarning("This plugin does not support grabbing the keyboard" ); |
453 | return false; |
454 | } |
455 | |
456 | bool QPlatformWindow::setMouseGrabEnabled(bool grab) |
457 | { |
458 | Q_UNUSED(grab); |
459 | qWarning("This plugin does not support grabbing the mouse" ); |
460 | return false; |
461 | } |
462 | |
463 | /*! |
464 | Reimplement to be able to let Qt indicate that the window has been |
465 | modified. Return true if the native window supports setting the modified |
466 | flag, false otherwise. |
467 | */ |
468 | bool QPlatformWindow::setWindowModified(bool modified) |
469 | { |
470 | Q_UNUSED(modified); |
471 | return false; |
472 | } |
473 | |
474 | /*! |
475 | Reimplement this method to be able to do any platform specific event |
476 | handling. All non-synthetic events for window() are passed to this |
477 | function before being sent to QWindow::event(). |
478 | |
479 | Return true if the event should not be passed on to the QWindow. |
480 | |
481 | Subclasses should always call the base class implementation. |
482 | */ |
483 | bool QPlatformWindow::windowEvent(QEvent *event) |
484 | { |
485 | Q_D(QPlatformWindow); |
486 | |
487 | if (event->type() == QEvent::Timer) { |
488 | if (static_cast<QTimerEvent *>(event)->timerId() == d->updateTimer.timerId()) { |
489 | d->updateTimer.stop(); |
490 | deliverUpdateRequest(); |
491 | return true; |
492 | } |
493 | } |
494 | |
495 | return false; |
496 | } |
497 | |
498 | /*! |
499 | Reimplement this method to start a system resize operation if |
500 | the system supports it and return true to indicate success. |
501 | |
502 | The default implementation is empty and does nothing with \a edges. |
503 | |
504 | \since 5.15 |
505 | */ |
506 | |
507 | bool QPlatformWindow::startSystemResize(Qt::Edges edges) |
508 | { |
509 | Q_UNUSED(edges); |
510 | return false; |
511 | } |
512 | |
513 | /*! |
514 | Reimplement this method to start a system move operation if |
515 | the system supports it and return true to indicate success. |
516 | |
517 | The default implementation is empty and does nothing. |
518 | |
519 | \since 5.15 |
520 | */ |
521 | |
522 | bool QPlatformWindow::startSystemMove() |
523 | { |
524 | return false; |
525 | } |
526 | |
527 | /*! |
528 | Reimplement this method to set whether frame strut events |
529 | should be sent to \a enabled. |
530 | |
531 | \sa frameStrutEventsEnabled |
532 | */ |
533 | |
534 | void QPlatformWindow::setFrameStrutEventsEnabled(bool enabled) |
535 | { |
536 | Q_UNUSED(enabled); // Do not warn as widgets enable it by default causing warnings with XCB. |
537 | } |
538 | |
539 | /*! |
540 | Reimplement this method to return whether |
541 | frame strut events are enabled. |
542 | */ |
543 | |
544 | bool QPlatformWindow::frameStrutEventsEnabled() const |
545 | { |
546 | return false; |
547 | } |
548 | |
549 | /*! |
550 | Call this method to put together a window title composed of |
551 | \a title |
552 | \a separator |
553 | the application display name |
554 | |
555 | If the display name isn't set, and the title is empty, the raw app name is used. |
556 | */ |
557 | QString QPlatformWindow::formatWindowTitle(const QString &title, const QString &separator) |
558 | { |
559 | QString fullTitle = title; |
560 | if (QGuiApplicationPrivate::displayName && !title.endsWith(*QGuiApplicationPrivate::displayName)) { |
561 | // Append display name, if set. |
562 | if (!fullTitle.isEmpty()) |
563 | fullTitle += separator; |
564 | fullTitle += *QGuiApplicationPrivate::displayName; |
565 | } else if (fullTitle.isEmpty()) { |
566 | // Don't let the window title be completely empty, use the app name as fallback. |
567 | fullTitle = QCoreApplication::applicationName(); |
568 | } |
569 | return fullTitle; |
570 | } |
571 | |
572 | /*! |
573 | Helper function for finding the new screen for \a newGeometry in response to |
574 | a geometry changed event. Returns the new screen if the window was moved to |
575 | another virtual sibling. If the screen changes, the platform plugin should call |
576 | QWindowSystemInterface::handleWindowScreenChanged(). |
577 | \note: The current screen will always be returned for child windows since |
578 | they should never signal screen changes. |
579 | |
580 | \since 5.4 |
581 | \sa QWindowSystemInterface::handleWindowScreenChanged() |
582 | */ |
583 | QPlatformScreen *QPlatformWindow::screenForGeometry(const QRect &newGeometry) const |
584 | { |
585 | QPlatformScreen *currentScreen = screen(); |
586 | QPlatformScreen *fallback = currentScreen; |
587 | // QRect::center can return a value outside the rectangle if it's empty. |
588 | // Apply mapToGlobal() in case it is a foreign/embedded window. |
589 | QPoint center = newGeometry.isEmpty() ? newGeometry.topLeft() : newGeometry.center(); |
590 | if (isForeignWindow()) |
591 | center = mapToGlobal(center - newGeometry.topLeft()); |
592 | |
593 | if (!parent() && currentScreen && !currentScreen->geometry().contains(center)) { |
594 | const auto screens = currentScreen->virtualSiblings(); |
595 | for (QPlatformScreen *screen : screens) { |
596 | const QRect screenGeometry = screen->geometry(); |
597 | if (screenGeometry.contains(center)) |
598 | return screen; |
599 | if (screenGeometry.intersects(newGeometry)) |
600 | fallback = screen; |
601 | } |
602 | } |
603 | return fallback; |
604 | } |
605 | |
606 | /*! |
607 | Returns a size with both dimensions bounded to [0, QWINDOWSIZE_MAX] |
608 | */ |
609 | QSize QPlatformWindow::constrainWindowSize(const QSize &size) |
610 | { |
611 | return size.expandedTo(QSize(0, 0)).boundedTo(QSize(QWINDOWSIZE_MAX, QWINDOWSIZE_MAX)); |
612 | } |
613 | |
614 | /*! |
615 | Reimplement this method to set whether the window demands attention |
616 | (for example, by flashing the taskbar icon) depending on \a enabled. |
617 | |
618 | \sa isAlertState() |
619 | \since 5.1 |
620 | */ |
621 | |
622 | void QPlatformWindow::setAlertState(bool enable) |
623 | { |
624 | Q_UNUSED(enable); |
625 | } |
626 | |
627 | /*! |
628 | Reimplement this method return whether the window is in |
629 | an alert state. |
630 | |
631 | \sa setAlertState() |
632 | \since 5.1 |
633 | */ |
634 | |
635 | bool QPlatformWindow::isAlertState() const |
636 | { |
637 | return false; |
638 | } |
639 | |
640 | // Return the effective screen for the initial geometry of a window. In a |
641 | // multimonitor-setup, try to find the right screen by checking the transient |
642 | // parent or the mouse cursor for parentless windows (cf QTBUG-34204, |
643 | // QDialog::adjustPosition()), unless a non-primary screen has been set, |
644 | // in which case we try to respect that. |
645 | static inline const QScreen *effectiveScreen(const QWindow *window) |
646 | { |
647 | if (!window) |
648 | return QGuiApplication::primaryScreen(); |
649 | const QScreen *screen = window->screen(); |
650 | if (!screen) |
651 | return QGuiApplication::primaryScreen(); |
652 | if (screen != QGuiApplication::primaryScreen()) |
653 | return screen; |
654 | #ifndef QT_NO_CURSOR |
655 | const QList<QScreen *> siblings = screen->virtualSiblings(); |
656 | if (siblings.size() > 1) { |
657 | const QPoint referencePoint = window->transientParent() ? window->transientParent()->geometry().center() : QCursor::pos(); |
658 | for (const QScreen *sibling : siblings) { |
659 | if (sibling->geometry().contains(referencePoint)) |
660 | return sibling; |
661 | } |
662 | } |
663 | #endif |
664 | return screen; |
665 | } |
666 | |
667 | /*! |
668 | Invalidates the window's surface by releasing its surface buffers. |
669 | |
670 | Many platforms do not support releasing the surface memory, |
671 | and the default implementation does nothing. |
672 | |
673 | The platform window is expected to recreate the surface again if |
674 | it is needed. For instance, if an OpenGL context is made current |
675 | on this window. |
676 | */ |
677 | void QPlatformWindow::invalidateSurface() |
678 | { |
679 | } |
680 | |
681 | static QSize fixInitialSize(QSize size, const QWindow *w, |
682 | int defaultWidth, int defaultHeight) |
683 | { |
684 | if (size.width() == 0) { |
685 | const int minWidth = w->minimumWidth(); |
686 | size.setWidth(minWidth > 0 ? minWidth : defaultWidth); |
687 | } |
688 | if (size.height() == 0) { |
689 | const int minHeight = w->minimumHeight(); |
690 | size.setHeight(minHeight > 0 ? minHeight : defaultHeight); |
691 | } |
692 | return size; |
693 | } |
694 | |
695 | /*! |
696 | Helper function to get initial geometry on windowing systems which do not |
697 | do smart positioning and also do not provide a means of centering a |
698 | transient window w.r.t. its parent. For example this is useful on Windows |
699 | and MacOS but not X11, because an X11 window manager typically tries to |
700 | layout new windows to optimize usage of the available desktop space. |
701 | However if the given window already has geometry which the application has |
702 | initialized, it takes priority. |
703 | */ |
704 | QRect QPlatformWindow::initialGeometry(const QWindow *w, const QRect &initialGeometry, |
705 | int defaultWidth, int defaultHeight, |
706 | const QScreen **resultingScreenReturn) |
707 | { |
708 | if (resultingScreenReturn) |
709 | *resultingScreenReturn = w->screen(); |
710 | if (!w->isTopLevel()) { |
711 | const qreal factor = QHighDpiScaling::factor(w); |
712 | const QSize size = fixInitialSize(QHighDpi::fromNative(initialGeometry.size(), factor), |
713 | w, defaultWidth, defaultHeight); |
714 | return QRect(initialGeometry.topLeft(), QHighDpi::toNative(size, factor)); |
715 | } |
716 | const auto *wp = qt_window_private(const_cast<QWindow*>(w)); |
717 | const bool position = wp->positionAutomatic && w->type() != Qt::Popup; |
718 | if (!position && !wp->resizeAutomatic) |
719 | return initialGeometry; |
720 | const QScreen *screen = wp->positionAutomatic |
721 | ? effectiveScreen(w) |
722 | : QGuiApplication::screenAt(initialGeometry.center()); |
723 | if (!screen) |
724 | return initialGeometry; |
725 | if (resultingScreenReturn) |
726 | *resultingScreenReturn = screen; |
727 | // initialGeometry refers to window's screen |
728 | QRect rect(QHighDpi::fromNativePixels(initialGeometry, w)); |
729 | if (wp->resizeAutomatic) |
730 | rect.setSize(fixInitialSize(rect.size(), w, defaultWidth, defaultHeight)); |
731 | if (position) { |
732 | const QRect availableGeometry = screen->availableGeometry(); |
733 | // Center unless the geometry ( + unknown window frame) is too large for the screen). |
734 | if (rect.height() < (availableGeometry.height() * 8) / 9 |
735 | && rect.width() < (availableGeometry.width() * 8) / 9) { |
736 | const QWindow *tp = w->transientParent(); |
737 | if (tp) { |
738 | // A transient window should be centered w.r.t. its transient parent. |
739 | rect.moveCenter(tp->geometry().center()); |
740 | } else { |
741 | // Center the window on the screen. (Only applicable on platforms |
742 | // which do not provide a better way.) |
743 | rect.moveCenter(availableGeometry.center()); |
744 | } |
745 | } |
746 | } |
747 | return QHighDpi::toNativePixels(rect, screen); |
748 | } |
749 | |
750 | /*! |
751 | Requests an QEvent::UpdateRequest event. The event will be |
752 | delivered to the QWindow. |
753 | |
754 | QPlatformWindow subclasses can re-implement this function to |
755 | provide display refresh synchronized updates. The event |
756 | should be delivered using QPlatformWindow::deliverUpdateRequest() |
757 | to not get out of sync with the internal state of QWindow. |
758 | |
759 | The default implementation posts an UpdateRequest event to the |
760 | window after 5 ms. The additional time is there to give the event |
761 | loop a bit of idle time to gather system events. |
762 | |
763 | */ |
764 | void QPlatformWindow::requestUpdate() |
765 | { |
766 | Q_D(QPlatformWindow); |
767 | |
768 | static int updateInterval = []() { |
769 | bool ok = false; |
770 | int customUpdateInterval = qEnvironmentVariableIntValue("QT_QPA_UPDATE_IDLE_TIME" , &ok); |
771 | return ok ? customUpdateInterval : 5; |
772 | }(); |
773 | |
774 | Q_ASSERT(!d->updateTimer.isActive()); |
775 | d->updateTimer.start(updateInterval, Qt::PreciseTimer, window()); |
776 | } |
777 | |
778 | /*! |
779 | Returns true if the window has a pending update request. |
780 | |
781 | \sa requestUpdate(), deliverUpdateRequest() |
782 | */ |
783 | bool QPlatformWindow::hasPendingUpdateRequest() const |
784 | { |
785 | return qt_window_private(window())->updateRequestPending; |
786 | } |
787 | |
788 | /*! |
789 | Delivers an QEvent::UpdateRequest event to the window. |
790 | |
791 | QPlatformWindow subclasses can re-implement this function to |
792 | provide e.g. logging or tracing of the delivery, but should |
793 | always call the base class function. |
794 | */ |
795 | void QPlatformWindow::deliverUpdateRequest() |
796 | { |
797 | Q_ASSERT(hasPendingUpdateRequest()); |
798 | |
799 | QWindow *w = window(); |
800 | QWindowPrivate *wp = qt_window_private(w); |
801 | wp->updateRequestPending = false; |
802 | QEvent request(QEvent::UpdateRequest); |
803 | QCoreApplication::sendEvent(w, &request); |
804 | } |
805 | |
806 | /*! |
807 | Returns the QWindow minimum size. |
808 | */ |
809 | QSize QPlatformWindow::windowMinimumSize() const |
810 | { |
811 | return constrainWindowSize(QHighDpi::toNativePixels(window()->minimumSize(), window())); |
812 | } |
813 | |
814 | /*! |
815 | Returns the QWindow maximum size. |
816 | */ |
817 | QSize QPlatformWindow::windowMaximumSize() const |
818 | { |
819 | return constrainWindowSize(QHighDpi::toNativePixels(window()->maximumSize(), window())); |
820 | } |
821 | |
822 | /*! |
823 | Returns the QWindow base size. |
824 | */ |
825 | QSize QPlatformWindow::windowBaseSize() const |
826 | { |
827 | return QHighDpi::toNativePixels(window()->baseSize(), window()); |
828 | } |
829 | |
830 | /*! |
831 | Returns the QWindow size increment. |
832 | */ |
833 | QSize QPlatformWindow::windowSizeIncrement() const |
834 | { |
835 | QSize increment = window()->sizeIncrement(); |
836 | if (!QHighDpiScaling::isActive()) |
837 | return increment; |
838 | |
839 | // Normalize the increment. If not set the increment can be |
840 | // (-1, -1) or (0, 0). Make that (1, 1) which is scalable. |
841 | if (increment.isEmpty()) |
842 | increment = QSize(1, 1); |
843 | |
844 | return QHighDpi::toNativePixels(increment, window()); |
845 | } |
846 | |
847 | /*! |
848 | Returns the QWindow geometry. |
849 | */ |
850 | QRect QPlatformWindow::windowGeometry() const |
851 | { |
852 | return QHighDpi::toNativeWindowGeometry(window()->geometry(), window()); |
853 | } |
854 | |
855 | /*! |
856 | Returns the QWindow frame geometry. |
857 | */ |
858 | QRect QPlatformWindow::windowFrameGeometry() const |
859 | { |
860 | return QHighDpi::toNativeWindowGeometry(window()->frameGeometry(), window()); |
861 | } |
862 | |
863 | /*! |
864 | Returns the closest acceptable geometry for a given geometry before |
865 | a resize/move event for platforms that support it, for example to |
866 | implement heightForWidth(). |
867 | */ |
868 | |
869 | QRectF QPlatformWindow::closestAcceptableGeometry(const QWindow *qWindow, const QRectF &nativeRect) |
870 | { |
871 | const QRectF rectF = QHighDpi::fromNativeWindowGeometry(nativeRect, qWindow); |
872 | const QRectF correctedGeometryF = qt_window_private(const_cast<QWindow *>(qWindow))->closestAcceptableGeometry(rectF); |
873 | return !correctedGeometryF.isEmpty() && rectF != correctedGeometryF |
874 | ? QHighDpi::toNativeWindowGeometry(correctedGeometryF, qWindow) : nativeRect; |
875 | } |
876 | |
877 | QRectF QPlatformWindow::windowClosestAcceptableGeometry(const QRectF &nativeRect) const |
878 | { |
879 | return QPlatformWindow::closestAcceptableGeometry(window(), nativeRect); |
880 | } |
881 | |
882 | /*! |
883 | \class QPlatformWindow |
884 | \since 4.8 |
885 | \internal |
886 | \preliminary |
887 | \ingroup qpa |
888 | |
889 | \brief The QPlatformWindow class provides an abstraction for top-level windows. |
890 | |
891 | The QPlatformWindow abstraction is used by QWindow for all its top level windows. It is being |
892 | created by calling the createPlatformWindow function in the loaded QPlatformIntegration |
893 | instance. |
894 | |
895 | QPlatformWindow is used to signal to the windowing system, how Qt perceives its frame. |
896 | However, it is not concerned with how Qt renders into the window it represents. |
897 | |
898 | Visible QWindows will always have a QPlatformWindow. However, it is not necessary for |
899 | all windows to have a QBackingStore. This is the case for QOpenGLWindow. And could be the case for |
900 | windows where some third party renders into it. |
901 | |
902 | The platform specific window handle can be retrieved by the winId function. |
903 | |
904 | QPlatformWindow is also the way QPA defines how native child windows should be supported |
905 | through the setParent function. |
906 | |
907 | \section1 Implementation Aspects |
908 | |
909 | \list 1 |
910 | \li Mouse grab: Qt expects windows to automatically grab the mouse if the user presses |
911 | a button until the button is released. |
912 | Automatic grab should be released if some window is explicitly grabbed. |
913 | \li Enter/Leave events: If there is a window explicitly grabbing mouse events |
914 | (\c{setMouseGrabEnabled()}), enter and leave events should only be sent to the |
915 | grabbing window when mouse cursor passes over the grabbing window boundary. |
916 | Other windows will not receive enter or leave events while the grab is active. |
917 | While an automatic mouse grab caused by a mouse button press is active, no window |
918 | will receive enter or leave events. When the last mouse button is released, the |
919 | autograbbing window will receive leave event if mouse cursor is no longer within |
920 | the window boundary. |
921 | When any grab starts, the window under cursor will receive a leave event unless |
922 | it is the grabbing window. |
923 | When any grab ends, the window under cursor will receive an enter event unless it |
924 | was the grabbing window. |
925 | \li Window positioning: When calling \c{QWindow::setFramePosition()}, the flag |
926 | \c{QWindowPrivate::positionPolicy} is set to \c{QWindowPrivate::WindowFrameInclusive}. |
927 | This means the position includes the window frame, whose size is at this point |
928 | unknown and the geometry's topleft point is the position of the window frame. |
929 | \endlist |
930 | |
931 | Apart from the auto-tests (\c{tests/auto/gui/kernel/qwindow}, |
932 | \c{tests/auto/gui/kernel/qguiapplication} and \c{tests/auto/widgets/kernel/qwidget}), |
933 | there are a number of manual tests and examples that can help testing a platform plugin: |
934 | |
935 | \list 1 |
936 | \li \c{examples/qpa/windows}: Basic \c{QWindow} creation. |
937 | \li \c{examples/opengl/hellowindow}: Basic Open GL windows. |
938 | \li \c{tests/manual/windowflags}: Tests setting the window flags. |
939 | \li \c{tests/manual/windowgeometry} Tests setting the window geometry. |
940 | \li \c{tests/manual/windowmodality} Tests setting the window modality. |
941 | \li \c{tests/manual/widgetgrab} Tests mouse grab and dialogs. |
942 | \endlist |
943 | |
944 | \sa QBackingStore, QWindow |
945 | */ |
946 | |
947 | QT_END_NAMESPACE |
948 | |