Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqnxwindow.cpp
Go to the documentation of this file.
1// Copyright (C) 2011 - 2013 BlackBerry Limited. All rights reserved.
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#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses
6
7#include "qqnxglobal.h"
8
9#include "qqnxwindow.h"
11#include "qqnxscreen.h"
12#include "qqnxlgmon.h"
14
15#include <QUuid>
16
17#include <QtGui/QWindow>
18#include <qpa/qwindowsysteminterface.h>
19
20#include "private/qguiapplication_p.h"
21#include "private/qhighdpiscaling_p.h"
22
23#include <QtCore/QDebug>
24
25#include <errno.h>
26
28
29Q_LOGGING_CATEGORY(lcQpaWindow, "qt.qpa.window");
30
31#define DECLARE_DEBUG_VAR(variable)
32 static bool debug_ ## variable()
33 { static bool value = qgetenv("QNX_SCREEN_DEBUG").contains(QT_STRINGIFY(variable)); return value; }
37DECLARE_DEBUG_VAR(updates)
38DECLARE_DEBUG_VAR(cpu_time)
39DECLARE_DEBUG_VAR(gpu_time)
40DECLARE_DEBUG_VAR(statistics)
41#undef DECLARE_DEBUG_VAR
42
43/*!
44 \class QQnxWindow
45 \brief The QQnxWindow is the base class of the various classes used as instances of
46 QPlatformWindow in the QNX QPA plugin.
47
48 The standard properties and methods available in Qt are not a perfect match for the
49 features provided by the QNX screen service. While for the majority of applications
50 the default behavior suffices, some circumstances require greater control over the
51 interaction with screen.
52
53 \section1 Window Types
54
55 The QNX QPA plugin can operate in two modes, with or without a root window. The
56 selection of mode is made via the \e rootwindow and \e no-rootwindow options to the
57 plugin. The default mode is rootwindow for BlackBerry builds and no-rootwindow for
58 non-BlackBerry builds.
59
60 Windows with parents are always created as child windows, the difference in the modes
61 is in the treatment of parentless windows. In no-rootwindow mode, these windows are
62 created as application windows while in rootwindow mode, the first window on a screen
63 is created as an application window while subsequent windows are created as child
64 windows. The only exception to this is any window of type Qt::CoverWindow;
65 these are created as application windows, but will never become the root window,
66 even if they are the first window created.
67
68 It is also possible to create a parentless child window. These may be useful to
69 create windows that are parented by windows from other processes. To do this, you
70 attach a dynamic property \e qnxInitialWindowGroup to the QWindow though this must be done
71 prior to the platform window class (this class) being created which typically happens
72 when the window is made visible. When the window is created in QML, it is acceptable
73 to have the \e visible property hardcoded to true so long as the qnxInitialWindowGroup
74 is also set.
75
76 \section1 Joining Window Groups
77
78 Window groups may be joined in a number of ways, some are automatic based on
79 predefined rules though an application is also able to provide explicit control.
80
81 A QWindow that has a parent will join its parent's window group. When rootwindow mode
82 is in effect, all but the first parentless window on a screen will be child windows
83 and join the window group of the first parentless window, the root window.
84
85 If a QWindow has a valid dynamic property called \e qnxInitialWindowGroup at the time the
86 QQnxWindow is created, the window will be created as a child window and, if the
87 qnxInitialWindowGroup property is a non-empty string, an attempt will be made to join that
88 window group. This has an effect only when the QQnxWindow is created, subsequent
89 changes to this property are ignored. Setting the property to an empty string
90 provides a means to create 'top level' child windows without automatically joining
91 any group. Typically when this property is used \e qnxWindowId should be used as well
92 so that the process that owns the window group being joined has some means to
93 identify the window.
94
95 At any point following the creation of the QQnxWindow object, an application can
96 change the window group it has joined. This is done by using the \e
97 setWindowProperty function of the native interface to set the \e qnxWindowGroup property
98 to the desired value, for example:
99
100 \snippet code/src_plugins_platforms_qnx_qqnxwindow.cpp 0
101
102 To leave the current window group, one passes a null value for the property value,
103 for example:
104
105 \snippet code/src_plugins_platforms_qnx_qqnxwindow.cpp 1
106
107 \section1 Window Id
108
109 The screen window id string property can be set on a window by assigning the desired
110 value to a dynamic property \e qnxWindowId on the QWindow prior to the QQnxWindow having
111 been created. This is often wanted when one joins a window group belonging to a
112 different process.
113
114*/
115QQnxWindow::QQnxWindow(QWindow *window, screen_context_t context, bool needRootWindow)
118 m_window(0),
119 m_screen(0),
120 m_parentWindow(0),
121 m_visible(false),
122 m_exposed(true),
123 m_foreign(false),
125 m_firstActivateHandled(false),
126 m_desktopNotify(0)
127{
128 qCDebug(lcQpaWindow) << "window =" << window << ", size =" << window->size();
129
130 QQnxScreen *platformScreen = static_cast<QQnxScreen *>(window->screen()->handle());
131
132 // If a qnxInitialWindowGroup property is set on the window we'll take this as an
133 // indication that we want to create a child window and join that window group.
134 QVariant windowGroup = window->property("qnxInitialWindowGroup");
135 if (!windowGroup.isValid())
136 windowGroup = window->property("_q_platform_qnxParentGroup");
137
138 if (window->type() == Qt::CoverWindow) {
139 // Cover windows have to be top level to be accessible to window delegate (i.e. navigator)
140 // Desktop windows also need to be toplevel because they are not
141 // supposed to be part of the window hierarchy tree
142 m_isTopLevel = true;
143 } else if (parent() || windowGroup.isValid()) {
144 // If we have a parent we are a child window. Sometimes we have to be a child even if we
145 // don't have a parent e.g. our parent might be in a different process.
146 m_isTopLevel = false;
147 } else {
148 // We're parentless. If we're not using a root window, we'll always be a top-level window
149 // otherwise only the first window is.
150 m_isTopLevel = !needRootWindow || !platformScreen->rootWindow();
151 }
152
153 QVariant type = window->property("_q_platform_qnxWindowType");
154 if (type.isValid() && type.canConvert<int>()) {
156 screen_create_window_type(&m_window, m_screenContext, type.value<int>()),
157 "Could not create window");
158 } else if (m_isTopLevel) {
159 Q_SCREEN_CRITICALERROR(screen_create_window(&m_window, m_screenContext),
160 "Could not create top level window"); // Creates an application window
161 if (window->type() != Qt::CoverWindow) {
162 if (needRootWindow)
163 platformScreen->setRootWindow(this);
164 }
165 } else {
167 screen_create_window_type(&m_window, m_screenContext, SCREEN_CHILD_WINDOW),
168 "Could not create child window");
169 }
170
171 createWindowGroup();
172
173 // If the window has a qnxWindowId property, set this as the string id property. This generally
174 // needs to be done prior to joining any group as it might be used by the owner of the
175 // group to identify the window.
176 QVariant windowId = window->property("qnxWindowId");
177 if (!windowId.isValid())
178 windowId = window->property("_q_platform_qnxWindowId");
179 if (windowId.isValid() && windowId.canConvert<QByteArray>()) {
180 QByteArray id = windowId.toByteArray();
181 Q_SCREEN_CHECKERROR(screen_set_window_property_cv(m_window, SCREEN_PROPERTY_ID_STRING,
182 id.size(), id), "Failed to set id");
183 }
184
185 // If a window group has been provided join it now. If it's an empty string that's OK too,
186 // it'll cause us not to join a group (the app will presumably join at some future time).
187 if (windowGroup.isValid() && windowGroup.canConvert<QByteArray>())
188 joinWindowGroup(windowGroup.toByteArray());
189
190 QVariant pipelineValue = window->property("_q_platform_qnxPipeline");
191 if (pipelineValue.isValid()) {
192 bool ok = false;
193 int pipeline = pipelineValue.toInt(&ok);
194 if (ok) {
195 qCDebug(lcQpaWindow) << "Set pipeline value to" << pipeline;
196
198 screen_set_window_property_iv(m_window, SCREEN_PROPERTY_PIPELINE, &pipeline),
199 "Failed to set window pipeline");
200 } else {
201 qCDebug(lcQpaWindow) << "Invalid pipeline value:" << pipelineValue;
202 }
203 }
204
205 // QNX desktop integration.
207 // Determine if the window needs a frame.
208 switch (window->type()) {
209 case Qt::Popup:
210 case Qt::ToolTip:
211 m_desktopNotify = DesktopNotifyPosition | DesktopNotifyVisible;
212 break;
213
214 default:
215 m_desktopNotify = DesktopNotifyTitle | DesktopNotifyVisible;
216 break;
217 }
218
219 // Wait for the window manager to acknowledge the window's creation.
220 // The call returns immediately if there is no window manager.
221 screen_manage_window(m_window,
222 (m_desktopNotify & DesktopNotifyTitle) ? "Frame=Y" : "Frame=N");
223 }
224
225 int debug = 0;
226 if (Q_UNLIKELY(debug_fps())) {
227 debug |= SCREEN_DEBUG_GRAPH_FPS;
228 }
229 if (Q_UNLIKELY(debug_posts())) {
230 debug |= SCREEN_DEBUG_GRAPH_POSTS;
231 }
232 if (Q_UNLIKELY(debug_blits())) {
233 debug |= SCREEN_DEBUG_GRAPH_BLITS;
234 }
235 if (Q_UNLIKELY(debug_updates())) {
236 debug |= SCREEN_DEBUG_GRAPH_UPDATES;
237 }
238 if (Q_UNLIKELY(debug_cpu_time())) {
239 debug |= SCREEN_DEBUG_GRAPH_CPU_TIME;
240 }
241 if (Q_UNLIKELY(debug_gpu_time())) {
242 debug |= SCREEN_DEBUG_GRAPH_GPU_TIME;
243 }
244 if (Q_UNLIKELY(debug_statistics())) {
245 debug = SCREEN_DEBUG_STATISTICS;
246 }
247
248 if (debug > 0) {
249 Q_SCREEN_CHECKERROR(screen_set_window_property_iv(nativeHandle(), SCREEN_PROPERTY_DEBUG, &debug),
250 "Could not set SCREEN_PROPERTY_DEBUG");
251 qCDebug(lcQpaWindow) << "window SCREEN_PROPERTY_DEBUG= " << debug;
252 }
253}
254
255QQnxWindow::QQnxWindow(QWindow *window, screen_context_t context, screen_window_t screenWindow)
259 , m_screen(0)
260 , m_parentWindow(0)
261 , m_visible(false)
262 , m_exposed(true)
263 , m_foreign(true)
265 , m_parentGroupName(256, 0)
266 , m_isTopLevel(false)
267{
268 qCDebug(lcQpaWindow) << "window =" << window << ", size =" << window->size();
269
270 collectWindowGroup();
271
272 screen_get_window_property_cv(m_window,
273 SCREEN_PROPERTY_PARENT,
274 m_parentGroupName.size(),
275 m_parentGroupName.data());
276 m_parentGroupName.resize(strlen(m_parentGroupName.constData()));
277
278 // If a window group has been provided join it now. If it's an empty string that's OK too,
279 // it'll cause us not to join a group (the app will presumably join at some future time).
280 QVariant parentGroup = window->property("qnxInitialWindowGroup");
281 if (!parentGroup.isValid())
282 parentGroup = window->property("_q_platform_qnxParentGroup");
283 if (parentGroup.isValid() && parentGroup.canConvert<QByteArray>())
284 joinWindowGroup(parentGroup.toByteArray());
285}
286
288{
289 qCDebug(lcQpaWindow) << "window =" << window();
290 *m_alive = false;
291
292 // Qt should have already deleted the children before deleting the parent.
293 Q_ASSERT(m_childWindows.size() == 0);
294
295 // Remove from plugin's window mapper
296 QQnxIntegration::instance()->removeWindow(m_window);
297 if (m_postEventRegistered)
298 QQnxIntegration::instance()->m_screenEventThread->unregisterUpdateNotification(m_window);
299
300 // Remove from parent's Hierarchy.
301 removeFromParent();
302 if (m_screen)
303 m_screen->updateHierarchy();
304
305 // Cleanup QNX window and its buffers
306 // Foreign windows are cleaned up externally after the CLOSE event has been handled.
307 if (m_foreign)
308 removeContextPermission();
309 else
310 screen_destroy_window(m_window);
311}
312
313void QQnxWindow::setGeometry(const QRect &rect)
314{
315 QRect newGeometry = rect;
316 if (shouldMakeFullScreen())
317 newGeometry = screen()->geometry();
318
319 setGeometryHelper(newGeometry);
320
321 if (isExposed())
322 QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), newGeometry.size()));
323}
324
325void QQnxWindow::setGeometryHelper(const QRect &rect)
326{
327 qCDebug(lcQpaWindow) << "window =" << window()
328 << ", (" << rect.x() << "," << rect.y()
329 << "," << rect.width() << "," << rect.height() << ")";
330
331 // Call base class method
332 QPlatformWindow::setGeometry(rect);
333
334 int val[2];
335
336 // Set window geometry equal to widget geometry
337 if (m_desktopNotify & DesktopNotifyPosition) {
338 notifyManager(QString::asprintf("Pos=%d,%d", rect.x(), rect.y()));
339 } else {
340 val[0] = rect.x();
341 val[1] = rect.y();
342 Q_SCREEN_CHECKERROR(screen_set_window_property_iv(m_window, SCREEN_PROPERTY_POSITION, val),
343 "Failed to set window position");
344 }
345
346 val[0] = rect.width();
347 val[1] = rect.height();
348 Q_SCREEN_CHECKERROR(screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SIZE, val),
349 "Failed to set window size");
350
351 // Set viewport size equal to window size
352 Q_SCREEN_CHECKERROR(screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SOURCE_SIZE, val),
353 "Failed to set window source size");
354
355 screen_flush_context(m_screenContext, 0);
356
357 QWindowSystemInterface::handleGeometryChange(window(), rect);
358}
359
360void QQnxWindow::setVisible(bool visible)
361{
362 qCDebug(lcQpaWindow) << "window =" << window() << "visible =" << visible;
363
364 if (m_visible == visible)
365 return;
366
367 // The first time through we join a window group if appropriate.
368 if (m_parentGroupName.isNull() && !m_isTopLevel) {
369 joinWindowGroup(parent() ? static_cast<QQnxWindow*>(parent())->groupName()
370 : QByteArray(m_screen->windowGroupName()));
371 }
372
373 m_visible = visible;
374
375 QQnxWindow *root = this;
376 while (root->m_parentWindow)
377 root = root->m_parentWindow;
378
379 root->updateVisibility(root->m_visible);
380
381 const QSize windowSize = QHighDpi::toNativePixels(window()->geometry().size(), window());
382 QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), windowSize));
383
384 if (visible) {
385 applyWindowState();
386
387 // Activate the window when shown, similar to how eglfs handles
388 // window activation through its compositor. Without a window manager,
389 // the platform must proactively activate windows on show.
390 if (m_isTopLevel && !showWithoutActivating())
392 } else {
393 if (showWithoutActivating() && focusable() && m_firstActivateHandled) {
394 m_firstActivateHandled = false;
395 int val = SCREEN_SENSITIVITY_NO_FOCUS;
397 screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SENSITIVITY, &val),
398 "Failed to set window sensitivity");
399 }
400
401 // Flush the context, otherwise it won't disappear immediately
402 screen_flush_context(m_screenContext, 0);
403
404 // When hiding the focused window, activate the next appropriate window.
405 // Similar to eglfs compositor behavior on window removal.
406 if (m_isTopLevel && window() == QGuiApplication::focusWindow()) {
407 QWindow *nextFocus = QGuiApplication::modalWindow();
408 if (!nextFocus) {
409 // Find the topmost visible window to activate
410 const auto windows = QGuiApplication::topLevelWindows();
411 for (auto it = windows.rbegin(); it != windows.rend(); ++it) {
412 if (*it != window() && (*it)->isVisible() && (*it)->handle()) {
413 nextFocus = *it;
414 break;
415 }
416 }
417 }
418 if (nextFocus)
419 nextFocus->requestActivate();
420 else
421 QWindowSystemInterface::handleFocusWindowChanged(nullptr);
422 }
423 }
424}
425
426void QQnxWindow::updateVisibility(bool parentVisible)
427{
428 qCDebug(lcQpaWindow) << "parentVisible =" << parentVisible << "window =" << window();
429 // Set window visibility
430 int val = (m_visible && parentVisible) ? 1 : 0;
431 if (m_desktopNotify & DesktopNotifyVisible) {
432 notifyManager(QString("Visible=") + (val ? "Y" : "N"));
433 } else {
434 Q_SCREEN_CHECKERROR(screen_set_window_property_iv(m_window, SCREEN_PROPERTY_VISIBLE, &val),
435 "Failed to set window visibility");
436 }
437
438 Q_FOREACH (QQnxWindow *childWindow, m_childWindows)
439 childWindow->updateVisibility(m_visible && parentVisible);
440}
441
442void QQnxWindow::setOpacity(qreal level)
443{
444 qCDebug(lcQpaWindow) << "window =" << window() << "opacity =" << level;
445 // Set window global alpha
446 int val = (int)(level * 255);
447 Q_SCREEN_CHECKERROR(screen_set_window_property_iv(m_window, SCREEN_PROPERTY_GLOBAL_ALPHA, &val),
448 "Failed to set global alpha");
449
450 screen_flush_context(m_screenContext, 0);
451}
452
453void QQnxWindow::setExposed(bool exposed)
454{
455 qCDebug(lcQpaWindow) << "window =" << window() << "expose =" << exposed;
456
457 if (m_exposed != exposed) {
458 m_exposed = exposed;
459 const QSize windowSize = QHighDpi::toNativePixels(window()->geometry().size(), window());
460 QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), windowSize));
461 }
462}
463
465{
466 return m_visible && m_exposed;
467}
468
469void QQnxWindow::setBufferSize(const QSize &size)
470{
471 qCDebug(lcQpaWindow) << "window =" << window() << "size =" << size;
472
473 // libscreen fails when creating empty buffers
474 const QSize nonEmptySize = size.isEmpty() ? QSize(1, 1) : size;
475 int format = pixelFormat();
476
477 if (nonEmptySize == m_bufferSize || format == -1)
478 return;
479
481 screen_set_window_property_iv(m_window, SCREEN_PROPERTY_FORMAT, &format),
482 "Failed to set window format");
483
484 int val[2] = { nonEmptySize.width(), nonEmptySize.height() };
485 Q_SCREEN_CHECKERROR(screen_set_window_property_iv(m_window, SCREEN_PROPERTY_BUFFER_SIZE, val),
486 "Failed to set window buffer size");
487
488 if (m_bufferSize.isValid()) {
489 m_bufferSize = nonEmptySize;
491 return;
492 }
493
494 Q_SCREEN_CRITICALERROR(screen_create_window_buffers(m_window, MAX_BUFFER_COUNT),
495 "Failed to create window buffers");
496
497 // check if there are any buffers available
498 int bufferCount = 0;
500 screen_get_window_property_iv(m_window, SCREEN_PROPERTY_RENDER_BUFFER_COUNT, &bufferCount),
501 "Failed to query render buffer count");
502
503 if (Q_UNLIKELY(bufferCount != MAX_BUFFER_COUNT)) {
504 qFatal("QQnxWindow: invalid buffer count. Expected = %d, got = %d.",
505 MAX_BUFFER_COUNT, bufferCount);
506 }
507
508 // Set the transparency. According to QNX technical support, setting the window
509 // transparency property should always be done *after* creating the window
510 // buffers in order to guarantee the property is paid attention to.
511 if (size.isEmpty()) {
512 // We can't create 0x0 buffers and instead make them 1x1. But to allow these windows to
513 // still be 'visible' (thus allowing their children to be visible), we need to allow
514 // them to be posted but still not show up.
515 val[0] = SCREEN_TRANSPARENCY_DISCARD;
516 } else if (window()->requestedFormat().alphaBufferSize() == 0) {
517 // To avoid overhead in the composition manager, disable blending
518 // when the underlying window buffer doesn't have an alpha channel.
519 val[0] = SCREEN_TRANSPARENCY_NONE;
520 } else {
521 // Normal alpha blending. This doesn't commit us to translucency; the
522 // normal backfill during the painting will contain a fully opaque
523 // alpha channel unless the user explicitly intervenes to make something
524 // transparent.
525 val[0] = SCREEN_TRANSPARENCY_SOURCE_OVER;
526 }
527
528 Q_SCREEN_CHECKERROR(screen_set_window_property_iv(m_window, SCREEN_PROPERTY_TRANSPARENCY, val),
529 "Failed to set window transparency");
530
531 // Cache new buffer size
532 m_bufferSize = nonEmptySize;
534}
535
536void QQnxWindow::setScreen(QQnxScreen *platformScreen)
537{
538 qCDebug(lcQpaWindow) << "window =" << window() << "platformScreen =" << platformScreen;
539
540 if (platformScreen == 0) { // The screen has been destroyed
541 m_screen = 0;
542 Q_FOREACH (QQnxWindow *childWindow, m_childWindows) {
543 childWindow->setScreen(0);
544 }
545 return;
546 }
547
548 if (m_screen == platformScreen)
549 return;
550
551 if (m_screen) {
552 qCDebug(lcQpaWindow) << "Moving window to different screen";
553 m_screen->removeWindow(this);
554
556 screen_leave_window_group(m_window);
557 }
558 }
559
560 m_screen = platformScreen;
561 if (!m_parentWindow) {
562 platformScreen->addWindow(this);
563 }
564 if (m_isTopLevel) {
565 // Move window to proper screen/display
566 screen_display_t display = platformScreen->nativeDisplay();
568 screen_set_window_property_pv(m_window, SCREEN_PROPERTY_DISPLAY, (void **)&display),
569 "Failed to set window display");
570 } else {
571 Q_FOREACH (QQnxWindow *childWindow, m_childWindows) {
572 // Only subwindows and tooltips need necessarily be moved to another display with the window.
573 if (window()->type() == Qt::SubWindow || window()->type() == Qt::ToolTip)
574 childWindow->setScreen(platformScreen);
575 }
576 }
577
578 m_screen->updateHierarchy();
579}
580
581void QQnxWindow::removeFromParent()
582{
583 qCDebug(lcQpaWindow) << Q_FUNC_INFO << "window =" << window();
584 // Remove from old Hierarchy position
585 if (m_parentWindow) {
586 if (Q_UNLIKELY(!m_parentWindow->m_childWindows.removeAll(this)))
587 qFatal("QQnxWindow: Window Hierarchy broken; window has parent, but parent hasn't got child.");
588 else
589 m_parentWindow = nullptr;
590 } else if (m_screen) {
591 m_screen->removeWindow(this);
592 }
593}
594
595void QQnxWindow::setParent(const QPlatformWindow *window)
596{
597 qCDebug(lcQpaWindow) << "window =" << this->window() << "platformWindow =" << window;
598 // Cast away the const, we need to modify the hierarchy.
599 QQnxWindow* const newParent = static_cast<QQnxWindow*>(const_cast<QPlatformWindow*>(window));
600
601 if (newParent == m_parentWindow)
602 return;
603
604 if (static_cast<QQnxScreen *>(screen())->rootWindow() == this) {
605 qWarning("Application window cannot be reparented");
606 return;
607 }
608
609 removeFromParent();
610 m_parentWindow = newParent;
611
612 // Add to new hierarchy position.
613 if (m_parentWindow) {
614 if (m_parentWindow->m_screen != m_screen)
615 setScreen(m_parentWindow->m_screen);
616
617 m_parentWindow->m_childWindows.push_back(this);
618 joinWindowGroup(m_parentWindow->groupName());
619 } else {
620 m_screen->addWindow(this);
621 joinWindowGroup(QByteArray());
622 }
623
624 m_screen->updateHierarchy();
625}
626
628{
629 qCDebug(lcQpaWindow) << Q_FUNC_INFO << "window =" << window();
630
631 if (m_parentWindow) {
632 m_parentWindow->m_childWindows.removeAll(this);
633 m_parentWindow->m_childWindows.push_back(this);
634 } else {
635 m_screen->raiseWindow(this);
636 }
637
638 m_screen->updateHierarchy();
639}
640
642{
643 qCDebug(lcQpaWindow) << Q_FUNC_INFO << "window =" << window();
644
645 if (m_parentWindow) {
646 m_parentWindow->m_childWindows.removeAll(this);
647 m_parentWindow->m_childWindows.push_front(this);
648 } else {
649 m_screen->lowerWindow(this);
650 }
651
652 m_screen->updateHierarchy();
653}
654
656{
657 QQnxWindow *focusWindow = nullptr;
658 if (QGuiApplication::focusWindow())
659 focusWindow = static_cast<QQnxWindow*>(QGuiApplication::focusWindow()->handle());
660
661 if (focusWindow == this)
662 return;
663
664 if (static_cast<QQnxScreen *>(screen())->rootWindow() == this ||
665 (focusWindow && findWindow(focusWindow->nativeHandle()))) {
666 // If the focus window is a child, we can just set the focus of our own window
667 // group to our window handle
668 setFocus(nativeHandle());
669 } else {
670 // In order to receive focus the parent's window group has to give focus to the
671 // child. If we have several hierarchy layers, we have to do that several times
672 QQnxWindow *currentWindow = this;
673 QList<QQnxWindow*> windowList;
674 while (currentWindow) {
675 auto platformScreen = static_cast<QQnxScreen *>(screen());
676 windowList.prepend(currentWindow);
677 // If we find the focus window, we don't have to go further
678 if (currentWindow == focusWindow)
679 break;
680
681 if (currentWindow->parent()){
682 currentWindow = static_cast<QQnxWindow*>(currentWindow->parent());
683 } else if (platformScreen->rootWindow() &&
684 platformScreen->rootWindow()->m_windowGroupName == currentWindow->m_parentGroupName) {
685 currentWindow = platformScreen->rootWindow();
686 } else {
687 currentWindow = nullptr;
688 }
689 }
690
691 // We have to apply the focus from parent to child windows
692 for (int i = 1; i < windowList.size(); ++i)
693 windowList.at(i-1)->setFocus(windowList.at(i)->nativeHandle());
694
695 windowList.last()->setFocus(windowList.constLast()->nativeHandle());
696 }
697
698 screen_flush_context(m_screenContext, 0);
699
700 // Proactively notify Qt about the focus change. The QNX screen service
701 // may not reliably send back a FOCUS property event after we set it.
702 // If a modal window is blocking the requested window, redirect focus
703 // to the blocking modal window instead.
704 QWindow *activateTarget = window();
705 QWindow *blockingWindow = nullptr;
706 if (QGuiApplicationPrivate::instance()->isWindowBlocked(activateTarget, &blockingWindow)
707 && blockingWindow) {
708 activateTarget = blockingWindow;
709 }
710 QWindowSystemInterface::handleFocusWindowChanged(activateTarget, Qt::ActiveWindowFocusReason);
711}
712
713void QQnxWindow::setFocus(screen_window_t newFocusWindow)
714{
715 screen_window_t temporaryFocusWindow = nullptr;
716
717 screen_group_t screenGroup = 0;
718 Q_SCREEN_CHECKERROR(screen_get_window_property_pv(nativeHandle(), SCREEN_PROPERTY_GROUP,
719 reinterpret_cast<void **>(&screenGroup)),
720 "Failed to retrieve window group");
721
722 if (showWithoutActivating() && focusable() && !m_firstActivateHandled) {
723 m_firstActivateHandled = true;
724 int val = SCREEN_SENSITIVITY_TEST;
726 screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SENSITIVITY, &val),
727 "Failed to set window sensitivity");
728
730 // For older versions of screen, the window may still have group
731 // focus even though it was marked NO_FOCUS when it was hidden.
732 // In that situation, focus has to be given to another window
733 // so that this window can take focus back from it.
734 screen_window_t oldFocusWindow = nullptr;
736 screen_get_group_property_pv(screenGroup, SCREEN_PROPERTY_FOCUS,
737 reinterpret_cast<void **>(&oldFocusWindow)),
738 "Failed to retrieve group focus");
739 if (newFocusWindow == oldFocusWindow) {
740 char groupName[256];
741 memset(groupName, 0, sizeof(groupName));
742 Q_SCREEN_CHECKERROR(screen_get_group_property_cv(screenGroup, SCREEN_PROPERTY_NAME,
743 sizeof(groupName) - 1, groupName),
744 "Failed to retrieve group name");
745
746 Q_SCREEN_CHECKERROR(screen_create_window_type(&temporaryFocusWindow,
747 m_screenContext, SCREEN_CHILD_WINDOW),
748 "Failed to create temporary focus window");
749 Q_SCREEN_CHECKERROR(screen_join_window_group(temporaryFocusWindow, groupName),
750 "Temporary focus window failed to join window group");
752 screen_set_group_property_pv(screenGroup, SCREEN_PROPERTY_FOCUS,
753 reinterpret_cast<void **>(&temporaryFocusWindow)),
754 "Temporary focus window failed to take focus");
755 screen_flush_context(m_screenContext, 0);
756 }
757#endif
758 }
759
760 Q_SCREEN_CHECKERROR(screen_set_group_property_pv(screenGroup, SCREEN_PROPERTY_FOCUS,
761 reinterpret_cast<void **>(&newFocusWindow)),
762 "Failed to set group focus");
763
764 screen_destroy_window(temporaryFocusWindow);
765}
766
767void QQnxWindow::setWindowState(Qt::WindowStates state)
768{
769 qCDebug(lcQpaWindow) << Q_FUNC_INFO << "state =" << state;
770
771 // Prevent two calls with Qt::WindowFullScreen from changing m_unmaximizedGeometry
772 if (m_windowState == state)
773 return;
774
775 m_windowState = state;
776
777 if (m_visible)
778 applyWindowState();
779}
780
782{
783 // nothing to do; silence base class warning
784 // qWindowDebug("ignored");
785}
786
788{
789 return m_screen;
790}
791
792QQnxWindow *QQnxWindow::findWindow(screen_window_t windowHandle)
793{
794 if (m_window == windowHandle)
795 return this;
796
797 Q_FOREACH (QQnxWindow *window, m_childWindows) {
798 QQnxWindow * const result = window->findWindow(windowHandle);
799 if (result)
800 return result;
801 }
802
803 return 0;
804}
805
807{
808 qWarning("Qt::WindowMinimized is not supported by this OS version");
809}
810
811void QQnxWindow::setRotation(int rotation)
812{
813 qCDebug(lcQpaWindow) << Q_FUNC_INFO << "angle =" << rotation;
815 screen_set_window_property_iv(m_window, SCREEN_PROPERTY_ROTATION, &rotation),
816 "Failed to set window rotation");
817}
818
820{
821 // Alpha channel is always pre-multiplied if present
822 int val = SCREEN_PRE_MULTIPLIED_ALPHA;
823 Q_SCREEN_CHECKERROR(screen_set_window_property_iv(m_window, SCREEN_PROPERTY_ALPHA_MODE, &val),
824 "Failed to set alpha mode");
825
826 // Set the window swap interval
827 val = 1;
829 screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SWAP_INTERVAL, &val),
830 "Failed to set swap interval");
831
832 if (showWithoutActivating() || !focusable()) {
833 // NO_FOCUS is temporary for showWithoutActivating (and pop-up) windows.
834 // Using NO_FOCUS ensures that screen doesn't activate the window because
835 // it was just created. Sensitivity will be changed to TEST when the
836 // window is clicked or touched.
837 val = SCREEN_SENSITIVITY_NO_FOCUS;
839 screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SENSITIVITY, &val),
840 "Failed to set window sensitivity");
841 }
842
843 QQnxScreen *platformScreen = static_cast<QQnxScreen *>(window()->screen()->handle());
844 setScreen(platformScreen);
845
846 if (window()->type() == Qt::CoverWindow)
847 m_exposed = false;
848
849 // Add window to plugin's window mapper
850 QQnxIntegration::instance()->addWindow(m_window, window());
851 m_postEventRegistered =
852 QQnxIntegration::instance()->m_screenEventThread->registerUpdateNotification(m_window);
853
854 // Qt never calls these setters after creating the window, so we need to do that ourselves here
855 setWindowState(window()->windowState());
856 setOpacity(window()->opacity());
857
858 if (window()->parent() && window()->parent()->handle())
859 setParent(window()->parent()->handle());
860
861 setGeometryHelper(shouldMakeFullScreen()
862 ? screen()->geometry()
863 : QHighDpi::toNativePixels(window()->geometry(), window()));
864}
865
866void QQnxWindow::collectWindowGroup()
867{
868 QByteArray groupName(256, 0);
869 Q_SCREEN_CHECKERROR(screen_get_window_property_cv(m_window,
870 SCREEN_PROPERTY_GROUP,
871 groupName.size(),
872 groupName.data()),
873 "Failed to retrieve window group");
874 groupName.resize(strlen(groupName.constData()));
875 m_windowGroupName = groupName;
876}
877
878void QQnxWindow::createWindowGroup()
879{
880 Q_SCREEN_CHECKERROR(screen_create_window_group(m_window, nullptr),
881 "Failed to create window group");
882
883 collectWindowGroup();
884}
885
886void QQnxWindow::joinWindowGroup(const QByteArray &groupName)
887{
888 bool changed = false;
889
890 qCDebug(lcQpaWindow) << Q_FUNC_INFO << "group:" << groupName;
891
892 // screen has this annoying habit of generating a CLOSE/CREATE when the owner context of
893 // the parent group moves a foreign window to another group that it also owns. The
894 // CLOSE/CREATE changes the identity of the foreign window. Usually, this is undesirable.
895 // To prevent this CLOSE/CREATE when changing the parent group, we temporarily add a
896 // context permission for the Qt context. screen won't send a CLOSE/CREATE when the
897 // context has some permission other than the PARENT permission. If there isn't a new
898 // group (the window has no parent), this context permission is left in place.
899
900 if (m_foreign && !m_parentGroupName.isEmpty())
901 addContextPermission();
902
903 if (!groupName.isEmpty()) {
904 if (groupName != m_parentGroupName) {
905 screen_join_window_group(m_window, groupName);
906 m_parentGroupName = groupName;
907 changed = true;
908 }
909 } else {
910 if (!m_parentGroupName.isEmpty()) {
911 screen_leave_window_group(m_window);
912 changed = true;
913 }
914 // By setting to an empty string we'll stop setVisible from trying to
915 // change our group, we want that to happen only if joinWindowGroup has
916 // never been called. This allows windows to be created that are not initially
917 // part of any group.
918 m_parentGroupName = "";
919 }
920
921 if (m_foreign && !groupName.isEmpty())
922 removeContextPermission();
923
924 if (changed)
925 screen_flush_context(m_screenContext, 0);
926}
927
928void QQnxWindow::updateZorder(int &topZorder)
929{
930 updateZorder(m_window, topZorder);
931
932 Q_FOREACH (QQnxWindow *childWindow, m_childWindows)
933 childWindow->updateZorder(topZorder);
934}
935
936void QQnxWindow::updateZorder(screen_window_t window, int &topZorder)
937{
938 Q_SCREEN_CHECKERROR(screen_set_window_property_iv(window, SCREEN_PROPERTY_ZORDER, &topZorder),
939 "Failed to set window z-order");
940 topZorder++;
941}
942
943void QQnxWindow::applyWindowState()
944{
945 if (m_windowState & Qt::WindowMinimized) {
947
948 if (m_unmaximizedGeometry.isValid())
949 setGeometry(m_unmaximizedGeometry);
950 else
951 setGeometry(m_screen->geometry());
952 } else if (m_windowState & (Qt::WindowMaximized | Qt::WindowFullScreen)) {
953 m_unmaximizedGeometry = geometry();
954 setGeometry(m_windowState & Qt::WindowFullScreen ? m_screen->geometry()
955 : m_screen->availableGeometry());
956 } else if (m_unmaximizedGeometry.isValid()) {
957 setGeometry(m_unmaximizedGeometry);
958 }
959}
960
962{
963 if (m_postEventRegistered) {
964 m_waitingForPost.storeRelease(1);
965 }
966
967 if (m_cover) {
968 m_cover->updateCover();
969 qqnxLgmonFramePosted(true); // for performance measurements
970 } else {
971 qqnxLgmonFramePosted(false); // for performance measurements
972 }
973}
974
976{
978 return false;
979
980 // In fullscreen application mode, all top-level windows are fullscreen,
981 // similar to how eglfs forces all native windows to screen geometry.
982 // Exclude cover windows — they are top-level but managed by the
983 // navigator and should retain their own geometry.
984 return m_isTopLevel && window()->type() != Qt::CoverWindow;
985}
986
987
989{
990 if (showWithoutActivating() && focusable() && !m_firstActivateHandled)
992}
993
994bool QQnxWindow::showWithoutActivating() const
995{
996 return (window()->flags() & Qt::Popup) == Qt::Popup
997 || window()->property("_q_showWithoutActivating").toBool();
998}
999
1000bool QQnxWindow::focusable() const
1001{
1002 return (window()->flags() & Qt::WindowDoesNotAcceptFocus) != Qt::WindowDoesNotAcceptFocus;
1003}
1004
1005void QQnxWindow::addContextPermission()
1006{
1007 QByteArray grantString("context:");
1008 grantString.append(QQnxIntegration::instance()->screenContextId());
1009 grantString.append(":rw-");
1010 screen_set_window_property_cv(m_window,
1011 SCREEN_PROPERTY_PERMISSIONS,
1012 grantString.length(),
1013 grantString.data());
1014}
1015
1016void QQnxWindow::setWindowTitle(const QString &title)
1017{
1018 if (m_desktopNotify & DesktopNotifyTitle) {
1019 QString titleStr = "Title=" + title;
1020 notifyManager(titleStr);
1021 }
1022}
1023
1025{
1026 Q_ASSERT(hasPendingUpdateRequest());
1027
1028 if (!m_postEventRegistered) {
1029 qCDebug(lcQpaWindow) << "requestUpdate: post event not registered, using fallback timer" << window();
1030 QPlatformWindow::requestUpdate();
1031 return;
1032 }
1033
1034 if (m_waitingForPost.loadAcquire()) {
1035 qCDebug(lcQpaWindow) << "requestUpdate: frame in-flight, deferring" << window();
1036 return;
1037 }
1038
1039 if (m_fallbackQueued.testAndSetRelaxed(0, 1)) {
1040 QSharedPointer<bool> alive = m_alive;
1041 QMetaObject::invokeMethod(window(), [this, alive]() {
1042 if (!*alive)
1043 return;
1044 m_fallbackQueued.storeRelaxed(0);
1045 if (m_waitingForPost.loadAcquire())
1046 return;
1047 if (hasPendingUpdateRequest())
1048 deliverUpdateRequest();
1049 }, Qt::QueuedConnection);
1050 }
1051}
1052
1054{
1055 if (!m_waitingForPost.testAndSetAcquire(1, 0)) {
1056 qCDebug(lcQpaWindow) << "handlePostEvent: duplicate/early pulse, ignoring" << window();
1057 return;
1058 }
1059
1060 qCDebug(lcQpaWindow) << "handlePostEvent: post pulse received" << window();
1061
1062 if (isExposed() && hasPendingUpdateRequest()) {
1063 qCDebug(lcQpaWindow) << "handlePostEvent: delivering update" << window();
1064 deliverUpdateRequest();
1065 }
1066}
1067
1068void QQnxWindow::notifyManager(const QString &msg)
1069{
1070 screen_event_t ev;
1071 screen_create_event(&ev);
1072
1073 std::string str = msg.toStdString();
1074 screen_set_event_property_iv(ev, SCREEN_PROPERTY_TYPE,
1075 (const int[]){ SCREEN_EVENT_MANAGER });
1076 screen_set_event_property_cv(ev, SCREEN_PROPERTY_USER_DATA, str.length(),
1077 str.c_str());
1078 screen_set_event_property_pv(ev, SCREEN_PROPERTY_WINDOW,
1079 reinterpret_cast<void **>(&m_window));
1080 screen_set_event_property_pv(ev, SCREEN_PROPERTY_CONTEXT,
1081 reinterpret_cast<void **>(&m_screenContext));
1082
1083 Q_SCREEN_CHECKERROR(screen_inject_event(NULL, ev),
1084 "Failed to send a message to the window manager");
1085}
1086
1087void QQnxWindow::removeContextPermission()
1088{
1089 QByteArray revokeString("context:");
1090 revokeString.append(QQnxIntegration::instance()->screenContextId());
1091 revokeString.append(":---");
1092 screen_set_window_property_cv(m_window,
1093 SCREEN_PROPERTY_PERMISSIONS,
1094 revokeString.length(),
1095 revokeString.data());
1096}
1097
1098QT_END_NAMESPACE
static QQnxIntegration * instance()
void lowerWindow(QQnxWindow *window)
void raiseWindow(QQnxWindow *window)
void removeWindow(QQnxWindow *child)
void addWindow(QQnxWindow *child)
void setRootWindow(QQnxWindow *)
QQnxWindow * rootWindow() const
void updateHierarchy()
The QQnxWindow is the base class of the various classes used as instances of QPlatformWindow in the Q...
Definition qqnxwindow.h:32
void windowPosted()
void setExposed(bool exposed)
void requestActivateWindow() override
Reimplement to let Qt be able to request activation/focus for a window.
virtual int pixelFormat() const =0
QQnxWindow(QWindow *window, screen_context_t context, screen_window_t screenWindow)
QQnxWindow * findWindow(screen_window_t windowHandle)
void setParent(const QPlatformWindow *window) override
This function is called to enable native child window in QPA.
bool shouldMakeFullScreen() const
void setVisible(bool visible) override
Reimplemented in subclasses to show the surface if visible is true, and hide it if visible is false.
virtual void resetBuffers()=0
bool isExposed() const override
Returns if this window is exposed in the windowing system.
void initWindow()
void raise() override
Reimplement to be able to let Qt raise windows to the top of the desktop.
QQnxWindow(QWindow *window, screen_context_t context, bool needRootWindow)
void joinWindowGroup(const QByteArray &groupName)
void minimize()
void propagateSizeHints() override
Reimplement to propagate the size hints of the QWindow.
void setWindowTitle(const QString &title)
Reimplement to set the window title to title.
void handlePostEvent()
void setScreen(QQnxScreen *platformScreen)
void setBufferSize(const QSize &size)
void setWindowState(Qt::WindowStates state) override
Requests setting the window state of this surface to type.
QPlatformScreen * screen() const override
Returns the platform screen handle corresponding to this platform window, or null if the window is no...
void setRotation(int rotation)
void handleActivationEvent()
void requestUpdate() override
Requests an QEvent::UpdateRequest event.
void lower() override
Reimplement to be able to let Qt lower windows to the bottom of the desktop.
void setGeometry(const QRect &rect) override
This function is called by Qt whenever a window is moved or resized using the QWindow API.
void setOpacity(qreal level) override
Reimplement to be able to let Qt set the opacity level of a window.
virtual ~QQnxWindow()
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")
#define Q_SCREEN_CRITICALERROR(x, message)
Definition qqnxglobal.h:20
#define Q_SCREEN_CHECKERROR(x, message)
Definition qqnxglobal.h:17
void qqnxLgmonFramePosted(bool)
Definition qqnxlgmon.h:36
#define _SCREEN_VERSION
Definition qqnxscreen.h:19
const int SCREEN_PROPERTY_FOCUS
Definition qqnxscreen.h:26
#define _SCREEN_MAKE_VERSION(major, minor, patch)
Definition qqnxscreen.h:18
#define DECLARE_DEBUG_VAR(variable)
#define MAX_BUFFER_COUNT
Definition qqnxwindow.h:25