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