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