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