1 /***************************************************************************
2 **
3 ** Copyright (C) 2011 - 2013 BlackBerry Limited. All rights reserved.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39
40 #include "qqnxglobal.h"
41
42 #include "qqnxscreen.h"
43 #include "qqnxwindow.h"
44 #include "qqnxcursor.h"
45
46 #include <QtCore/QThread>
47 #include <QtCore/QDebug>
48 #include <qpa/qwindowsysteminterface.h>
49
50 #include <errno.h>
51
52 #if defined(QQNXSCREEN_DEBUG)
53 #define qScreenDebug qDebug
54 #else
55 #define qScreenDebug QT_NO_QDEBUG_MACRO
56 #endif
57
58 #if defined(QQNX_PHYSICAL_SCREEN_WIDTH) && QQNX_PHYSICAL_SCREEN_WIDTH > 0 \
59 && defined(QQNX_PHYSICAL_SCREEN_HEIGHT) && QQNX_PHYSICAL_SCREEN_HEIGHT > 0
60 #define QQNX_PHYSICAL_SCREEN_SIZE_DEFINED
61 #elif defined(QQNX_PHYSICAL_SCREEN_WIDTH) || defined(QQNX_PHYSICAL_SCREEN_HEIGHT)
62 #error Please define QQNX_PHYSICAL_SCREEN_WIDTH and QQNX_PHYSICAL_SCREEN_HEIGHT to values greater than zero
63 #endif
64
65 // The default z-order of a window (intended to be overlain) created by
66 // mmrender.
67 static const int MMRENDER_DEFAULT_ZORDER = -1;
68
69 // The maximum z-order at which a foreign window will be considered
70 // an underlay.
71 static const int MAX_UNDERLAY_ZORDER = MMRENDER_DEFAULT_ZORDER - 1;
72
73 QT_BEGIN_NAMESPACE
74
determineScreenSize(screen_display_t display,bool primaryScreen)75 static QSize determineScreenSize(screen_display_t display, bool primaryScreen) {
76 int val[2];
77
78 const int result = screen_get_display_property_iv(display, SCREEN_PROPERTY_PHYSICAL_SIZE, val);
79 Q_SCREEN_CHECKERROR(result, "Failed to query display physical size");
80 if (result != 0) {
81 return QSize(150, 90);
82 }
83
84 if (val[0] > 0 && val[1] > 0)
85 return QSize(val[0], val[1]);
86
87 qScreenDebug("QQnxScreen: screen_get_display_property_iv() reported an invalid "
88 "physical screen size (%dx%d). Falling back to QQNX_PHYSICAL_SCREEN_SIZE "
89 "environment variable.", val[0], val[1]);
90
91 const QString envPhySizeStr = qgetenv("QQNX_PHYSICAL_SCREEN_SIZE");
92 if (!envPhySizeStr.isEmpty()) {
93 const auto envPhySizeStrList = envPhySizeStr.splitRef(QLatin1Char(','));
94 const int envWidth = envPhySizeStrList.size() == 2 ? envPhySizeStrList[0].toInt() : -1;
95 const int envHeight = envPhySizeStrList.size() == 2 ? envPhySizeStrList[1].toInt() : -1;
96
97 if (envWidth <= 0 || envHeight <= 0) {
98 qWarning("QQnxScreen: The value of QQNX_PHYSICAL_SCREEN_SIZE must be in the format "
99 "\"width,height\" in mm, with width, height > 0. Defaulting to 150x90. "
100 "Example: QQNX_PHYSICAL_SCREEN_SIZE=150,90");
101 return QSize(150, 90);
102 }
103
104 return QSize(envWidth, envHeight);
105 }
106
107 #if defined(QQNX_PHYSICAL_SCREEN_SIZE_DEFINED)
108 const QSize defSize(QQNX_PHYSICAL_SCREEN_WIDTH, QQNX_PHYSICAL_SCREEN_HEIGHT);
109 qWarning("QQnxScreen: QQNX_PHYSICAL_SCREEN_SIZE variable not set. Falling back to defines "
110 "QQNX_PHYSICAL_SCREEN_WIDTH/QQNX_PHYSICAL_SCREEN_HEIGHT (%dx%d)",
111 defSize.width(), defSize.height());
112 return defSize;
113 #else
114 if (primaryScreen)
115 qWarning("QQnxScreen: QQNX_PHYSICAL_SCREEN_SIZE variable not set. "
116 "Could not determine physical screen size. Defaulting to 150x90.");
117 return QSize(150, 90);
118 #endif
119 }
120
findMultimediaWindow(const QList<QQnxWindow * > & windows,const QByteArray & mmWindowId)121 static QQnxWindow *findMultimediaWindow(const QList<QQnxWindow*> &windows,
122 const QByteArray &mmWindowId)
123 {
124 Q_FOREACH (QQnxWindow *sibling, windows) {
125 if (sibling->mmRendererWindowName() == mmWindowId)
126 return sibling;
127
128 QQnxWindow *mmWindow = findMultimediaWindow(sibling->children(), mmWindowId);
129
130 if (mmWindow)
131 return mmWindow;
132 }
133
134 return 0;
135 }
136
findMultimediaWindow(const QList<QQnxWindow * > & windows,screen_window_t mmWindowId)137 static QQnxWindow *findMultimediaWindow(const QList<QQnxWindow*> &windows,
138 screen_window_t mmWindowId)
139 {
140 Q_FOREACH (QQnxWindow *sibling, windows) {
141 if (sibling->mmRendererWindow() == mmWindowId)
142 return sibling;
143
144 QQnxWindow *mmWindow = findMultimediaWindow(sibling->children(), mmWindowId);
145
146 if (mmWindow)
147 return mmWindow;
148 }
149
150 return 0;
151 }
152
QQnxScreen(screen_context_t screenContext,screen_display_t display,bool primaryScreen)153 QQnxScreen::QQnxScreen(screen_context_t screenContext, screen_display_t display, bool primaryScreen)
154 : m_screenContext(screenContext),
155 m_display(display),
156 m_rootWindow(0),
157 m_primaryScreen(primaryScreen),
158 m_keyboardHeight(0),
159 m_nativeOrientation(Qt::PrimaryOrientation),
160 m_coverWindow(0),
161 m_cursor(new QQnxCursor())
162 {
163 qScreenDebug();
164 // Cache initial orientation of this display
165 int result = screen_get_display_property_iv(m_display, SCREEN_PROPERTY_ROTATION,
166 &m_initialRotation);
167 Q_SCREEN_CHECKERROR(result, "Failed to query display rotation");
168
169 m_currentRotation = m_initialRotation;
170
171 // Cache size of this display in pixels
172 int val[2];
173 Q_SCREEN_CRITICALERROR(screen_get_display_property_iv(m_display, SCREEN_PROPERTY_SIZE, val),
174 "Failed to query display size");
175
176 m_currentGeometry = m_initialGeometry = QRect(0, 0, val[0], val[1]);
177
178 char name[100];
179 Q_SCREEN_CHECKERROR(screen_get_display_property_cv(m_display, SCREEN_PROPERTY_ID_STRING, 100,
180 name), "Failed to query display name");
181 m_name = QString::fromUtf8(name);
182
183 // Cache size of this display in millimeters. We have to take care of the orientation.
184 // libscreen always reports the physical size dimensions as width and height in the
185 // native orientation. Contrary to this, QPlatformScreen::physicalSize() expects the
186 // returned dimensions to follow the current orientation.
187 const QSize screenSize = determineScreenSize(m_display, primaryScreen);
188
189 m_nativeOrientation = screenSize.width() >= screenSize.height() ? Qt::LandscapeOrientation : Qt::PortraitOrientation;
190
191 const int angle = screen()->angleBetween(m_nativeOrientation, orientation());
192 if (angle == 0 || angle == 180)
193 m_currentPhysicalSize = m_initialPhysicalSize = screenSize;
194 else
195 m_currentPhysicalSize = m_initialPhysicalSize = screenSize.transposed();
196 }
197
~QQnxScreen()198 QQnxScreen::~QQnxScreen()
199 {
200 qScreenDebug();
201 Q_FOREACH (QQnxWindow *childWindow, m_childWindows)
202 childWindow->setScreen(0);
203
204 if (m_coverWindow)
205 m_coverWindow->setScreen(0);
206
207 delete m_cursor;
208 }
209
grabWindow(WId window,int x,int y,int width,int height) const210 QPixmap QQnxScreen::grabWindow(WId window, int x, int y, int width, int height) const
211 {
212 QQnxWindow *qnxWin = findWindow(reinterpret_cast<screen_window_t>(window));
213 if (!qnxWin) {
214 qWarning("grabWindow: unknown window");
215 return QPixmap();
216 }
217
218 QRect bound = qnxWin->geometry();
219
220 if (width < 0)
221 width = bound.width();
222 if (height < 0)
223 height = bound.height();
224
225 bound &= QRect(x + bound.x(), y + bound.y(), width, height);
226
227 if (bound.width() <= 0 || bound.height() <= 0) {
228 qWarning("grabWindow: size is null");
229 return QPixmap();
230 }
231
232 // Create new context, only SCREEN_DISPLAY_MANAGER_CONTEXT can read from screen
233 screen_context_t context;
234 if (screen_create_context(&context, SCREEN_DISPLAY_MANAGER_CONTEXT)) {
235 if (errno == EPERM)
236 qWarning("grabWindow: root privileges required");
237 else
238 qWarning("grabWindow: cannot create context");
239 return QPixmap();
240 }
241
242 // Find corresponding display in SCREEN_DISPLAY_MANAGER_CONTEXT
243 int count = 0;
244 screen_display_t display = 0;
245 screen_get_context_property_iv(context, SCREEN_PROPERTY_DISPLAY_COUNT, &count);
246 if (count > 0) {
247 const size_t idLen = 30;
248 char matchId[idLen];
249 char id[idLen];
250 bool found = false;
251
252 screen_display_t *displays = static_cast<screen_display_t*>
253 (calloc(count, sizeof(screen_display_t)));
254 screen_get_context_property_pv(context, SCREEN_PROPERTY_DISPLAYS, (void **)displays);
255 screen_get_display_property_cv(m_display, SCREEN_PROPERTY_ID_STRING, idLen, matchId);
256
257 while (count && !found) {
258 --count;
259 screen_get_display_property_cv(displays[count], SCREEN_PROPERTY_ID_STRING, idLen, id);
260 found = !strncmp(id, matchId, idLen);
261 }
262
263 if (found)
264 display = displays[count];
265
266 free(displays);
267 }
268
269 // Create screen and Qt pixmap
270 screen_pixmap_t pixmap;
271 QPixmap result;
272 if (display && !screen_create_pixmap(&pixmap, context)) {
273 screen_buffer_t buffer;
274 void *pointer;
275 int stride;
276 const int rect[4] = { bound.x(), bound.y(), bound.width(), bound.height() };
277
278 int val = SCREEN_USAGE_READ | SCREEN_USAGE_NATIVE;
279 screen_set_pixmap_property_iv(pixmap, SCREEN_PROPERTY_USAGE, &val);
280 val = SCREEN_FORMAT_RGBA8888;
281 screen_set_pixmap_property_iv(pixmap, SCREEN_PROPERTY_FORMAT, &val);
282
283 int err = screen_set_pixmap_property_iv(pixmap, SCREEN_PROPERTY_BUFFER_SIZE, rect+2);
284 err = err || screen_create_pixmap_buffer(pixmap);
285 err = err || screen_get_pixmap_property_pv(pixmap, SCREEN_PROPERTY_RENDER_BUFFERS,
286 reinterpret_cast<void**>(&buffer));
287 err = err || screen_get_buffer_property_pv(buffer, SCREEN_PROPERTY_POINTER, &pointer);
288 err = err || screen_get_buffer_property_iv(buffer, SCREEN_PROPERTY_STRIDE, &stride);
289 err = err || screen_read_display(display, buffer, 1, rect, 0);
290
291 if (!err) {
292 const QImage img(static_cast<unsigned char*>(pointer),
293 bound.width(), bound.height(), stride, QImage::Format_ARGB32);
294 result = QPixmap::fromImage(img);
295 } else {
296 qWarning("grabWindow: capture error");
297 }
298 screen_destroy_pixmap(pixmap);
299 } else {
300 qWarning("grabWindow: display/pixmap error ");
301 }
302 screen_destroy_context(context);
303
304 return result;
305 }
306
defaultDepth()307 static int defaultDepth()
308 {
309 qScreenDebug();
310 static int defaultDepth = 0;
311 if (defaultDepth == 0) {
312 // check if display depth was specified in environment variable;
313 // use default value if no valid value found
314 defaultDepth = qEnvironmentVariableIntValue("QQNX_DISPLAY_DEPTH");
315 if (defaultDepth != 16 && defaultDepth != 32)
316 defaultDepth = 32;
317 }
318 return defaultDepth;
319 }
320
availableGeometry() const321 QRect QQnxScreen::availableGeometry() const
322 {
323 qScreenDebug();
324 // available geometry = total geometry - keyboard
325 return QRect(m_currentGeometry.x(), m_currentGeometry.y(),
326 m_currentGeometry.width(), m_currentGeometry.height() - m_keyboardHeight);
327 }
328
depth() const329 int QQnxScreen::depth() const
330 {
331 return defaultDepth();
332 }
333
refreshRate() const334 qreal QQnxScreen::refreshRate() const
335 {
336 screen_display_mode_t displayMode;
337 int result = screen_get_display_property_pv(m_display, SCREEN_PROPERTY_MODE, reinterpret_cast<void **>(&displayMode));
338 // Screen shouldn't really return 0 but it does so default to 60 or things break.
339 if (result != 0 || displayMode.refresh == 0) {
340 qWarning("QQnxScreen: Failed to query screen mode. Using default value of 60Hz");
341 return 60.0;
342 }
343 qScreenDebug("screen mode:\n"
344 " width = %u\n"
345 " height = %u\n"
346 " refresh = %u\n"
347 " interlaced = %u",
348 uint(displayMode.width), uint(displayMode.height), uint(displayMode.refresh), uint(displayMode.interlaced));
349 return static_cast<qreal>(displayMode.refresh);
350 }
351
nativeOrientation() const352 Qt::ScreenOrientation QQnxScreen::nativeOrientation() const
353 {
354 return m_nativeOrientation;
355 }
356
orientation() const357 Qt::ScreenOrientation QQnxScreen::orientation() const
358 {
359 Qt::ScreenOrientation orient;
360 if (m_nativeOrientation == Qt::LandscapeOrientation) {
361 // Landscape devices e.g. PlayBook
362 if (m_currentRotation == 0)
363 orient = Qt::LandscapeOrientation;
364 else if (m_currentRotation == 90)
365 orient = Qt::PortraitOrientation;
366 else if (m_currentRotation == 180)
367 orient = Qt::InvertedLandscapeOrientation;
368 else
369 orient = Qt::InvertedPortraitOrientation;
370 } else {
371 // Portrait devices e.g. Phones
372 // ###TODO Check these on an actual phone device
373 if (m_currentRotation == 0)
374 orient = Qt::PortraitOrientation;
375 else if (m_currentRotation == 90)
376 orient = Qt::LandscapeOrientation;
377 else if (m_currentRotation == 180)
378 orient = Qt::InvertedPortraitOrientation;
379 else
380 orient = Qt::InvertedLandscapeOrientation;
381 }
382 qScreenDebug() << "orientation =" << orient;
383 return orient;
384 }
385
topLevelAt(const QPoint & point) const386 QWindow *QQnxScreen::topLevelAt(const QPoint &point) const
387 {
388 for (auto it = m_childWindows.rbegin(), end = m_childWindows.rend(); it != end; ++it) {
389 QWindow *win = (*it)->window();
390 if (win->geometry().contains(point))
391 return win;
392 }
393 return 0;
394 }
395
396 /*!
397 Check if the supplied angles are perpendicular to each other.
398 */
isOrthogonal(int angle1,int angle2)399 static bool isOrthogonal(int angle1, int angle2)
400 {
401 return ((angle1 - angle2) % 180) != 0;
402 }
403
setRotation(int rotation)404 void QQnxScreen::setRotation(int rotation)
405 {
406 qScreenDebug("orientation = %d", rotation);
407 // Check if rotation changed
408 // We only want to rotate if we are the primary screen
409 if (m_currentRotation != rotation && isPrimaryScreen()) {
410 // Update rotation of root window
411 if (rootWindow())
412 rootWindow()->setRotation(rotation);
413
414 const QRect previousScreenGeometry = geometry();
415
416 // Swap dimensions if we've rotated 90 or 270 from initial orientation
417 if (isOrthogonal(m_initialRotation, rotation)) {
418 m_currentGeometry = QRect(0, 0, m_initialGeometry.height(), m_initialGeometry.width());
419 m_currentPhysicalSize = QSize(m_initialPhysicalSize.height(), m_initialPhysicalSize.width());
420 } else {
421 m_currentGeometry = QRect(0, 0, m_initialGeometry.width(), m_initialGeometry.height());
422 m_currentPhysicalSize = m_initialPhysicalSize;
423 }
424
425 // Resize root window if we've rotated 90 or 270 from previous orientation
426 if (isOrthogonal(m_currentRotation, rotation)) {
427 qScreenDebug() << "resize, size =" << m_currentGeometry.size();
428 if (rootWindow())
429 rootWindow()->setGeometry(QRect(QPoint(0,0), m_currentGeometry.size()));
430
431 resizeWindows(previousScreenGeometry);
432 } else {
433 // TODO: Find one global place to flush display updates
434 // Force immediate display update if no geometry changes required
435 screen_flush_context(nativeContext(), 0);
436 }
437
438 // Save new rotation
439 m_currentRotation = rotation;
440
441 // TODO: check if other screens are supposed to rotate as well and/or whether this depends
442 // on if clone mode is being used.
443 // Rotating only the primary screen is what we had in the navigator event handler before refactoring
444 if (m_primaryScreen) {
445 QWindowSystemInterface::handleScreenOrientationChange(screen(), orientation());
446 QWindowSystemInterface::handleScreenGeometryChange(screen(), m_currentGeometry, availableGeometry());
447 }
448
449 // Flush everything, so that the windows rotations are applied properly.
450 // Needed for non-maximized windows
451 screen_flush_context( m_screenContext, 0 );
452 }
453 }
454
455 /*!
456 Resize the given window proportionally to the screen geometry
457 */
resizeNativeWidgetWindow(QQnxWindow * w,const QRect & previousScreenGeometry) const458 void QQnxScreen::resizeNativeWidgetWindow(QQnxWindow *w, const QRect &previousScreenGeometry) const
459 {
460 const qreal relativeX = static_cast<qreal>(w->geometry().topLeft().x()) / previousScreenGeometry.width();
461 const qreal relativeY = static_cast<qreal>(w->geometry().topLeft().y()) / previousScreenGeometry.height();
462 const qreal relativeWidth = static_cast<qreal>(w->geometry().width()) / previousScreenGeometry.width();
463 const qreal relativeHeight = static_cast<qreal>(w->geometry().height()) / previousScreenGeometry.height();
464
465 const QRect windowGeometry(relativeX * geometry().width(), relativeY * geometry().height(),
466 relativeWidth * geometry().width(), relativeHeight * geometry().height());
467
468 w->setGeometry(windowGeometry);
469 }
470
471 /*!
472 Resize the given window to fit the screen geometry
473 */
resizeTopLevelWindow(QQnxWindow * w,const QRect & previousScreenGeometry) const474 void QQnxScreen::resizeTopLevelWindow(QQnxWindow *w, const QRect &previousScreenGeometry) const
475 {
476 QRect windowGeometry = w->geometry();
477
478 const qreal relativeCenterX = static_cast<qreal>(w->geometry().center().x()) / previousScreenGeometry.width();
479 const qreal relativeCenterY = static_cast<qreal>(w->geometry().center().y()) / previousScreenGeometry.height();
480 const QPoint newCenter(relativeCenterX * geometry().width(), relativeCenterY * geometry().height());
481
482 windowGeometry.moveCenter(newCenter);
483
484 // adjust center position in case the window
485 // is clipped
486 if (!geometry().contains(windowGeometry)) {
487 const int x1 = windowGeometry.x();
488 const int y1 = windowGeometry.y();
489 const int x2 = x1 + windowGeometry.width();
490 const int y2 = y1 + windowGeometry.height();
491
492 if (x1 < 0) {
493 const int centerX = qMin(qAbs(x1) + windowGeometry.center().x(),
494 geometry().center().x());
495
496 windowGeometry.moveCenter(QPoint(centerX, windowGeometry.center().y()));
497 }
498
499 if (y1 < 0) {
500 const int centerY = qMin(qAbs(y1) + windowGeometry.center().y(),
501 geometry().center().y());
502
503 windowGeometry.moveCenter(QPoint(windowGeometry.center().x(), centerY));
504 }
505
506 if (x2 > geometry().width()) {
507 const int centerX = qMax(windowGeometry.center().x() - (x2 - geometry().width()),
508 geometry().center().x());
509
510 windowGeometry.moveCenter(QPoint(centerX, windowGeometry.center().y()));
511 }
512
513 if (y2 > geometry().height()) {
514 const int centerY = qMax(windowGeometry.center().y() - (y2 - geometry().height()),
515 geometry().center().y());
516
517 windowGeometry.moveCenter(QPoint(windowGeometry.center().x(), centerY));
518 }
519 }
520
521 // at this point, if the window is still clipped,
522 // it means that it's too big to fit on the screen,
523 // so we need to proportionally shrink it
524 if (!geometry().contains(windowGeometry)) {
525 QSize newSize = windowGeometry.size();
526 newSize.scale(geometry().size(), Qt::KeepAspectRatio);
527 windowGeometry.setSize(newSize);
528
529 if (windowGeometry.x() < 0)
530 windowGeometry.moveCenter(QPoint(geometry().center().x(), windowGeometry.center().y()));
531
532 if (windowGeometry.y() < 0)
533 windowGeometry.moveCenter(QPoint(windowGeometry.center().x(), geometry().center().y()));
534 }
535
536 w->setGeometry(windowGeometry);
537 }
538
539 /*!
540 Adjust windows to the new screen geometry.
541 */
resizeWindows(const QRect & previousScreenGeometry)542 void QQnxScreen::resizeWindows(const QRect &previousScreenGeometry)
543 {
544 resizeMaximizedWindows();
545
546 Q_FOREACH (QQnxWindow *w, m_childWindows) {
547
548 if (w->window()->windowState() & Qt::WindowFullScreen || w->window()->windowState() & Qt::WindowMaximized)
549 continue;
550
551 if (w->parent()) {
552 // This is a native (non-alien) widget window
553 resizeNativeWidgetWindow(w, previousScreenGeometry);
554 } else {
555 // This is a toplevel window
556 resizeTopLevelWindow(w, previousScreenGeometry);
557 }
558 }
559 }
560
findWindow(screen_window_t windowHandle) const561 QQnxWindow *QQnxScreen::findWindow(screen_window_t windowHandle) const
562 {
563 Q_FOREACH (QQnxWindow *window, m_childWindows) {
564 QQnxWindow * const result = window->findWindow(windowHandle);
565 if (result)
566 return result;
567 }
568
569 return 0;
570 }
571
addWindow(QQnxWindow * window)572 void QQnxScreen::addWindow(QQnxWindow *window)
573 {
574 qScreenDebug() << "window =" << window;
575
576 if (m_childWindows.contains(window))
577 return;
578
579 if (window->window()->type() != Qt::CoverWindow) {
580 // Ensure that the desktop window is at the bottom of the zorder.
581 // If we do not do this then we may end up activating the desktop
582 // when the navigator service gets an event that our window group
583 // has been activated (see QQnxScreen::activateWindowGroup()).
584 // Such a situation would strangely break focus handling due to the
585 // invisible desktop widget window being layered on top of normal
586 // windows
587 if (window->window()->type() == Qt::Desktop)
588 m_childWindows.push_front(window);
589 else
590 m_childWindows.push_back(window);
591 updateHierarchy();
592 }
593 }
594
removeWindow(QQnxWindow * window)595 void QQnxScreen::removeWindow(QQnxWindow *window)
596 {
597 qScreenDebug() << "window =" << window;
598
599 if (window != m_coverWindow) {
600 const int numWindowsRemoved = m_childWindows.removeAll(window);
601 if (window == m_rootWindow) //We just removed the root window
602 m_rootWindow = 0; //TODO we need a new root window ;)
603 if (numWindowsRemoved > 0)
604 updateHierarchy();
605 } else {
606 m_coverWindow = 0;
607 }
608 }
609
raiseWindow(QQnxWindow * window)610 void QQnxScreen::raiseWindow(QQnxWindow *window)
611 {
612 qScreenDebug() << "window =" << window;
613
614 if (window != m_coverWindow) {
615 removeWindow(window);
616 m_childWindows.push_back(window);
617 }
618 }
619
lowerWindow(QQnxWindow * window)620 void QQnxScreen::lowerWindow(QQnxWindow *window)
621 {
622 qScreenDebug() << "window =" << window;
623
624 if (window != m_coverWindow) {
625 removeWindow(window);
626 m_childWindows.push_front(window);
627 }
628 }
629
updateHierarchy()630 void QQnxScreen::updateHierarchy()
631 {
632 qScreenDebug();
633
634 QList<QQnxWindow*>::const_iterator it;
635 int result;
636 int topZorder = 0;
637
638 errno = 0;
639 if (rootWindow()) {
640 result = screen_get_window_property_iv(rootWindow()->nativeHandle(), SCREEN_PROPERTY_ZORDER, &topZorder);
641 if (result != 0) { //This can happen if we use winId in QWidgets
642 topZorder = 10;
643 qWarning("QQnxScreen: failed to query root window z-order, errno=%d", errno);
644 }
645 } else {
646 topZorder = 0; //We do not need z ordering on the secondary screen, because only one window
647 //is supported there
648 }
649
650 topZorder++; // root window has the lowest z-order in the windowgroup
651
652 int underlayZorder = -1;
653 // Underlays sit immediately above the root window in the z-ordering
654 Q_FOREACH (screen_window_t underlay, m_underlays) {
655 // Do nothing when this fails. This can happen if we have stale windows in m_underlays,
656 // which in turn can happen because a window was removed but we didn't get a notification
657 // yet.
658 screen_set_window_property_iv(underlay, SCREEN_PROPERTY_ZORDER, &underlayZorder);
659 underlayZorder--;
660 }
661
662 // Normal Qt windows come next above the root window z-ordering
663 for (it = m_childWindows.constBegin(); it != m_childWindows.constEnd(); ++it)
664 (*it)->updateZorder(topZorder);
665
666 // Finally overlays sit above all else in the z-ordering
667 Q_FOREACH (screen_window_t overlay, m_overlays) {
668 // No error handling, see underlay logic above
669 screen_set_window_property_iv(overlay, SCREEN_PROPERTY_ZORDER, &topZorder);
670 topZorder++;
671 }
672
673 // After a hierarchy update, we need to force a flush on all screens.
674 // Right now, all screens share a context.
675 screen_flush_context(m_screenContext, 0);
676 }
677
adjustOrientation()678 void QQnxScreen::adjustOrientation()
679 {
680 if (!m_primaryScreen)
681 return;
682
683 bool ok = false;
684 const int rotation = qEnvironmentVariableIntValue("ORIENTATION", &ok);
685
686 if (ok)
687 setRotation(rotation);
688 }
689
cursor() const690 QPlatformCursor * QQnxScreen::cursor() const
691 {
692 return m_cursor;
693 }
694
keyboardHeightChanged(int height)695 void QQnxScreen::keyboardHeightChanged(int height)
696 {
697 if (height == m_keyboardHeight)
698 return;
699
700 m_keyboardHeight = height;
701
702 QWindowSystemInterface::handleScreenGeometryChange(screen(), geometry(), availableGeometry());
703 }
704
addOverlayWindow(screen_window_t window)705 void QQnxScreen::addOverlayWindow(screen_window_t window)
706 {
707 m_overlays.append(window);
708 updateHierarchy();
709 }
710
addUnderlayWindow(screen_window_t window)711 void QQnxScreen::addUnderlayWindow(screen_window_t window)
712 {
713 m_underlays.append(window);
714 updateHierarchy();
715 }
716
addMultimediaWindow(const QByteArray & id,screen_window_t window)717 void QQnxScreen::addMultimediaWindow(const QByteArray &id, screen_window_t window)
718 {
719 // find the QnxWindow this mmrenderer window is related to
720 QQnxWindow *mmWindow = findMultimediaWindow(m_childWindows, id);
721
722 if (!mmWindow)
723 return;
724
725 mmWindow->setMMRendererWindow(window);
726
727 updateHierarchy();
728 }
729
removeOverlayOrUnderlayWindow(screen_window_t window)730 void QQnxScreen::removeOverlayOrUnderlayWindow(screen_window_t window)
731 {
732 const int numRemoved = m_overlays.removeAll(window) + m_underlays.removeAll(window);
733 if (numRemoved > 0) {
734 updateHierarchy();
735 Q_EMIT foreignWindowClosed(window);
736 }
737 }
738
newWindowCreated(void * window)739 void QQnxScreen::newWindowCreated(void *window)
740 {
741 Q_ASSERT(thread() == QThread::currentThread());
742 const screen_window_t windowHandle = reinterpret_cast<screen_window_t>(window);
743 screen_display_t display = 0;
744 if (screen_get_window_property_pv(windowHandle, SCREEN_PROPERTY_DISPLAY, (void**)&display) != 0) {
745 qWarning("QQnx: Failed to get screen for window, errno=%d", errno);
746 return;
747 }
748
749 int zorder;
750 if (screen_get_window_property_iv(windowHandle, SCREEN_PROPERTY_ZORDER, &zorder) != 0) {
751 qWarning("QQnx: Failed to get z-order for window, errno=%d", errno);
752 zorder = 0;
753 }
754
755 char windowNameBuffer[256] = { 0 };
756 QByteArray windowName;
757
758 if (screen_get_window_property_cv(windowHandle, SCREEN_PROPERTY_ID_STRING,
759 sizeof(windowNameBuffer) - 1, windowNameBuffer) != 0) {
760 qWarning("QQnx: Failed to get id for window, errno=%d", errno);
761 }
762
763 windowName = QByteArray(windowNameBuffer);
764
765 if (display == nativeDisplay()) {
766 // A window was created on this screen. If we don't know about this window yet, it means
767 // it was not created by Qt, but by some foreign library like the multimedia renderer, which
768 // creates an overlay window when playing a video.
769 //
770 // Treat all foreign windows as overlays, underlays or as windows
771 // created by the BlackBerry QtMultimedia plugin.
772 //
773 // In the case of the BlackBerry QtMultimedia plugin, we need to
774 // "attach" the foreign created mmrenderer window to the correct
775 // platform window (usually the one belonging to QVideoWidget) to
776 // ensure proper z-ordering.
777 //
778 // Otherwise, assume that if a foreign window already has a Z-Order both negative and
779 // less than the default Z-Order installed by mmrender on windows it creates,
780 // the windows should be treated as an underlay. Otherwise, we treat it as an overlay.
781 if (!windowName.isEmpty() && windowName.startsWith("MmRendererVideoWindowControl")) {
782 addMultimediaWindow(windowName, windowHandle);
783 } else if (!findWindow(windowHandle)) {
784 if (zorder <= MAX_UNDERLAY_ZORDER)
785 addUnderlayWindow(windowHandle);
786 else
787 addOverlayWindow(windowHandle);
788 Q_EMIT foreignWindowCreated(windowHandle);
789 }
790 }
791 }
792
windowClosed(void * window)793 void QQnxScreen::windowClosed(void *window)
794 {
795 Q_ASSERT(thread() == QThread::currentThread());
796 const screen_window_t windowHandle = reinterpret_cast<screen_window_t>(window);
797
798 QQnxWindow *mmWindow = findMultimediaWindow(m_childWindows, windowHandle);
799
800 if (mmWindow)
801 mmWindow->clearMMRendererWindow();
802 else
803 removeOverlayOrUnderlayWindow(windowHandle);
804 }
805
windowGroupStateChanged(const QByteArray & id,Qt::WindowState state)806 void QQnxScreen::windowGroupStateChanged(const QByteArray &id, Qt::WindowState state)
807 {
808 qScreenDebug();
809
810 if (!rootWindow() || id != rootWindow()->groupName())
811 return;
812
813 QWindow * const window = rootWindow()->window();
814
815 if (!window)
816 return;
817
818 QWindowSystemInterface::handleWindowStateChanged(window, state);
819 }
820
activateWindowGroup(const QByteArray & id)821 void QQnxScreen::activateWindowGroup(const QByteArray &id)
822 {
823 qScreenDebug();
824
825 if (!rootWindow() || id != rootWindow()->groupName())
826 return;
827
828 QWindow * const window = rootWindow()->window();
829
830 if (!window)
831 return;
832
833 Q_FOREACH (QQnxWindow *childWindow, m_childWindows)
834 childWindow->setExposed(true);
835
836 if (m_coverWindow)
837 m_coverWindow->setExposed(false);
838 }
839
deactivateWindowGroup(const QByteArray & id)840 void QQnxScreen::deactivateWindowGroup(const QByteArray &id)
841 {
842 qScreenDebug();
843
844 if (!rootWindow() || id != rootWindow()->groupName())
845 return;
846
847 if (m_coverWindow)
848 m_coverWindow->setExposed(true);
849
850 Q_FOREACH (QQnxWindow *childWindow, m_childWindows)
851 childWindow->setExposed(false);
852 }
853
rootWindow() const854 QQnxWindow *QQnxScreen::rootWindow() const
855 {
856 return m_rootWindow;
857 }
858
setRootWindow(QQnxWindow * window)859 void QQnxScreen::setRootWindow(QQnxWindow *window)
860 {
861 // Optionally disable the screen power save
862 bool ok = false;
863 const int disablePowerSave = qEnvironmentVariableIntValue("QQNX_DISABLE_POWER_SAVE", &ok);
864 if (ok && disablePowerSave) {
865 const int mode = SCREEN_IDLE_MODE_KEEP_AWAKE;
866 int result = screen_set_window_property_iv(window->nativeHandle(), SCREEN_PROPERTY_IDLE_MODE, &mode);
867 if (result != 0)
868 qWarning("QQnxRootWindow: failed to disable power saving mode");
869 }
870 m_rootWindow = window;
871 }
872
873 QT_END_NAMESPACE
874