| /*************************************************************************** |
| ** |
| ** Copyright (C) 2011 - 2013 BlackBerry Limited. All rights reserved. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the plugins of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qqnxglobal.h" |
| |
| #include "qqnxscreen.h" |
| #include "qqnxwindow.h" |
| #include "qqnxcursor.h" |
| |
| #include <QtCore/QThread> |
| #include <QtCore/QDebug> |
| #include <qpa/qwindowsysteminterface.h> |
| |
| #include <errno.h> |
| |
| #if defined(QQNXSCREEN_DEBUG) |
| #define qScreenDebug qDebug |
| #else |
| #define qScreenDebug QT_NO_QDEBUG_MACRO |
| #endif |
| |
| #if defined(QQNX_PHYSICAL_SCREEN_WIDTH) && QQNX_PHYSICAL_SCREEN_WIDTH > 0 \ |
| && defined(QQNX_PHYSICAL_SCREEN_HEIGHT) && QQNX_PHYSICAL_SCREEN_HEIGHT > 0 |
| #define QQNX_PHYSICAL_SCREEN_SIZE_DEFINED |
| #elif defined(QQNX_PHYSICAL_SCREEN_WIDTH) || defined(QQNX_PHYSICAL_SCREEN_HEIGHT) |
| #error Please define QQNX_PHYSICAL_SCREEN_WIDTH and QQNX_PHYSICAL_SCREEN_HEIGHT to values greater than zero |
| #endif |
| |
| // The default z-order of a window (intended to be overlain) created by |
| // mmrender. |
| static const int MMRENDER_DEFAULT_ZORDER = -1; |
| |
| // The maximum z-order at which a foreign window will be considered |
| // an underlay. |
| static const int MAX_UNDERLAY_ZORDER = MMRENDER_DEFAULT_ZORDER - 1; |
| |
| QT_BEGIN_NAMESPACE |
| |
| static QSize determineScreenSize(screen_display_t display, bool primaryScreen) { |
| int val[2]; |
| |
| const int result = screen_get_display_property_iv(display, SCREEN_PROPERTY_PHYSICAL_SIZE, val); |
| Q_SCREEN_CHECKERROR(result, "Failed to query display physical size"); |
| if (result != 0) { |
| return QSize(150, 90); |
| } |
| |
| if (val[0] > 0 && val[1] > 0) |
| return QSize(val[0], val[1]); |
| |
| qScreenDebug("QQnxScreen: screen_get_display_property_iv() reported an invalid " |
| "physical screen size (%dx%d). Falling back to QQNX_PHYSICAL_SCREEN_SIZE " |
| "environment variable.", val[0], val[1]); |
| |
| const QString envPhySizeStr = qgetenv("QQNX_PHYSICAL_SCREEN_SIZE"); |
| if (!envPhySizeStr.isEmpty()) { |
| const auto envPhySizeStrList = envPhySizeStr.splitRef(QLatin1Char(',')); |
| const int envWidth = envPhySizeStrList.size() == 2 ? envPhySizeStrList[0].toInt() : -1; |
| const int envHeight = envPhySizeStrList.size() == 2 ? envPhySizeStrList[1].toInt() : -1; |
| |
| if (envWidth <= 0 || envHeight <= 0) { |
| qWarning("QQnxScreen: The value of QQNX_PHYSICAL_SCREEN_SIZE must be in the format " |
| "\"width,height\" in mm, with width, height > 0. Defaulting to 150x90. " |
| "Example: QQNX_PHYSICAL_SCREEN_SIZE=150,90"); |
| return QSize(150, 90); |
| } |
| |
| return QSize(envWidth, envHeight); |
| } |
| |
| #if defined(QQNX_PHYSICAL_SCREEN_SIZE_DEFINED) |
| const QSize defSize(QQNX_PHYSICAL_SCREEN_WIDTH, QQNX_PHYSICAL_SCREEN_HEIGHT); |
| qWarning("QQnxScreen: QQNX_PHYSICAL_SCREEN_SIZE variable not set. Falling back to defines " |
| "QQNX_PHYSICAL_SCREEN_WIDTH/QQNX_PHYSICAL_SCREEN_HEIGHT (%dx%d)", |
| defSize.width(), defSize.height()); |
| return defSize; |
| #else |
| if (primaryScreen) |
| qWarning("QQnxScreen: QQNX_PHYSICAL_SCREEN_SIZE variable not set. " |
| "Could not determine physical screen size. Defaulting to 150x90."); |
| return QSize(150, 90); |
| #endif |
| } |
| |
| static QQnxWindow *findMultimediaWindow(const QList<QQnxWindow*> &windows, |
| const QByteArray &mmWindowId) |
| { |
| Q_FOREACH (QQnxWindow *sibling, windows) { |
| if (sibling->mmRendererWindowName() == mmWindowId) |
| return sibling; |
| |
| QQnxWindow *mmWindow = findMultimediaWindow(sibling->children(), mmWindowId); |
| |
| if (mmWindow) |
| return mmWindow; |
| } |
| |
| return 0; |
| } |
| |
| static QQnxWindow *findMultimediaWindow(const QList<QQnxWindow*> &windows, |
| screen_window_t mmWindowId) |
| { |
| Q_FOREACH (QQnxWindow *sibling, windows) { |
| if (sibling->mmRendererWindow() == mmWindowId) |
| return sibling; |
| |
| QQnxWindow *mmWindow = findMultimediaWindow(sibling->children(), mmWindowId); |
| |
| if (mmWindow) |
| return mmWindow; |
| } |
| |
| return 0; |
| } |
| |
| QQnxScreen::QQnxScreen(screen_context_t screenContext, screen_display_t display, bool primaryScreen) |
| : m_screenContext(screenContext), |
| m_display(display), |
| m_rootWindow(0), |
| m_primaryScreen(primaryScreen), |
| m_keyboardHeight(0), |
| m_nativeOrientation(Qt::PrimaryOrientation), |
| m_coverWindow(0), |
| m_cursor(new QQnxCursor()) |
| { |
| qScreenDebug(); |
| // Cache initial orientation of this display |
| int result = screen_get_display_property_iv(m_display, SCREEN_PROPERTY_ROTATION, |
| &m_initialRotation); |
| Q_SCREEN_CHECKERROR(result, "Failed to query display rotation"); |
| |
| m_currentRotation = m_initialRotation; |
| |
| // Cache size of this display in pixels |
| int val[2]; |
| Q_SCREEN_CRITICALERROR(screen_get_display_property_iv(m_display, SCREEN_PROPERTY_SIZE, val), |
| "Failed to query display size"); |
| |
| m_currentGeometry = m_initialGeometry = QRect(0, 0, val[0], val[1]); |
| |
| char name[100]; |
| Q_SCREEN_CHECKERROR(screen_get_display_property_cv(m_display, SCREEN_PROPERTY_ID_STRING, 100, |
| name), "Failed to query display name"); |
| m_name = QString::fromUtf8(name); |
| |
| // Cache size of this display in millimeters. We have to take care of the orientation. |
| // libscreen always reports the physical size dimensions as width and height in the |
| // native orientation. Contrary to this, QPlatformScreen::physicalSize() expects the |
| // returned dimensions to follow the current orientation. |
| const QSize screenSize = determineScreenSize(m_display, primaryScreen); |
| |
| m_nativeOrientation = screenSize.width() >= screenSize.height() ? Qt::LandscapeOrientation : Qt::PortraitOrientation; |
| |
| const int angle = screen()->angleBetween(m_nativeOrientation, orientation()); |
| if (angle == 0 || angle == 180) |
| m_currentPhysicalSize = m_initialPhysicalSize = screenSize; |
| else |
| m_currentPhysicalSize = m_initialPhysicalSize = screenSize.transposed(); |
| } |
| |
| QQnxScreen::~QQnxScreen() |
| { |
| qScreenDebug(); |
| Q_FOREACH (QQnxWindow *childWindow, m_childWindows) |
| childWindow->setScreen(0); |
| |
| if (m_coverWindow) |
| m_coverWindow->setScreen(0); |
| |
| delete m_cursor; |
| } |
| |
| QPixmap QQnxScreen::grabWindow(WId window, int x, int y, int width, int height) const |
| { |
| QQnxWindow *qnxWin = findWindow(reinterpret_cast<screen_window_t>(window)); |
| if (!qnxWin) { |
| qWarning("grabWindow: unknown window"); |
| return QPixmap(); |
| } |
| |
| QRect bound = qnxWin->geometry(); |
| |
| if (width < 0) |
| width = bound.width(); |
| if (height < 0) |
| height = bound.height(); |
| |
| bound &= QRect(x + bound.x(), y + bound.y(), width, height); |
| |
| if (bound.width() <= 0 || bound.height() <= 0) { |
| qWarning("grabWindow: size is null"); |
| return QPixmap(); |
| } |
| |
| // Create new context, only SCREEN_DISPLAY_MANAGER_CONTEXT can read from screen |
| screen_context_t context; |
| if (screen_create_context(&context, SCREEN_DISPLAY_MANAGER_CONTEXT)) { |
| if (errno == EPERM) |
| qWarning("grabWindow: root privileges required"); |
| else |
| qWarning("grabWindow: cannot create context"); |
| return QPixmap(); |
| } |
| |
| // Find corresponding display in SCREEN_DISPLAY_MANAGER_CONTEXT |
| int count = 0; |
| screen_display_t display = 0; |
| screen_get_context_property_iv(context, SCREEN_PROPERTY_DISPLAY_COUNT, &count); |
| if (count > 0) { |
| const size_t idLen = 30; |
| char matchId[idLen]; |
| char id[idLen]; |
| bool found = false; |
| |
| screen_display_t *displays = static_cast<screen_display_t*> |
| (calloc(count, sizeof(screen_display_t))); |
| screen_get_context_property_pv(context, SCREEN_PROPERTY_DISPLAYS, (void **)displays); |
| screen_get_display_property_cv(m_display, SCREEN_PROPERTY_ID_STRING, idLen, matchId); |
| |
| while (count && !found) { |
| --count; |
| screen_get_display_property_cv(displays[count], SCREEN_PROPERTY_ID_STRING, idLen, id); |
| found = !strncmp(id, matchId, idLen); |
| } |
| |
| if (found) |
| display = displays[count]; |
| |
| free(displays); |
| } |
| |
| // Create screen and Qt pixmap |
| screen_pixmap_t pixmap; |
| QPixmap result; |
| if (display && !screen_create_pixmap(&pixmap, context)) { |
| screen_buffer_t buffer; |
| void *pointer; |
| int stride; |
| const int rect[4] = { bound.x(), bound.y(), bound.width(), bound.height() }; |
| |
| int val = SCREEN_USAGE_READ | SCREEN_USAGE_NATIVE; |
| screen_set_pixmap_property_iv(pixmap, SCREEN_PROPERTY_USAGE, &val); |
| val = SCREEN_FORMAT_RGBA8888; |
| screen_set_pixmap_property_iv(pixmap, SCREEN_PROPERTY_FORMAT, &val); |
| |
| int err = screen_set_pixmap_property_iv(pixmap, SCREEN_PROPERTY_BUFFER_SIZE, rect+2); |
| err = err || screen_create_pixmap_buffer(pixmap); |
| err = err || screen_get_pixmap_property_pv(pixmap, SCREEN_PROPERTY_RENDER_BUFFERS, |
| reinterpret_cast<void**>(&buffer)); |
| err = err || screen_get_buffer_property_pv(buffer, SCREEN_PROPERTY_POINTER, &pointer); |
| err = err || screen_get_buffer_property_iv(buffer, SCREEN_PROPERTY_STRIDE, &stride); |
| err = err || screen_read_display(display, buffer, 1, rect, 0); |
| |
| if (!err) { |
| const QImage img(static_cast<unsigned char*>(pointer), |
| bound.width(), bound.height(), stride, QImage::Format_ARGB32); |
| result = QPixmap::fromImage(img); |
| } else { |
| qWarning("grabWindow: capture error"); |
| } |
| screen_destroy_pixmap(pixmap); |
| } else { |
| qWarning("grabWindow: display/pixmap error "); |
| } |
| screen_destroy_context(context); |
| |
| return result; |
| } |
| |
| static int defaultDepth() |
| { |
| qScreenDebug(); |
| static int defaultDepth = 0; |
| if (defaultDepth == 0) { |
| // check if display depth was specified in environment variable; |
| // use default value if no valid value found |
| defaultDepth = qEnvironmentVariableIntValue("QQNX_DISPLAY_DEPTH"); |
| if (defaultDepth != 16 && defaultDepth != 32) |
| defaultDepth = 32; |
| } |
| return defaultDepth; |
| } |
| |
| QRect QQnxScreen::availableGeometry() const |
| { |
| qScreenDebug(); |
| // available geometry = total geometry - keyboard |
| return QRect(m_currentGeometry.x(), m_currentGeometry.y(), |
| m_currentGeometry.width(), m_currentGeometry.height() - m_keyboardHeight); |
| } |
| |
| int QQnxScreen::depth() const |
| { |
| return defaultDepth(); |
| } |
| |
| qreal QQnxScreen::refreshRate() const |
| { |
| screen_display_mode_t displayMode; |
| int result = screen_get_display_property_pv(m_display, SCREEN_PROPERTY_MODE, reinterpret_cast<void **>(&displayMode)); |
| // Screen shouldn't really return 0 but it does so default to 60 or things break. |
| if (result != 0 || displayMode.refresh == 0) { |
| qWarning("QQnxScreen: Failed to query screen mode. Using default value of 60Hz"); |
| return 60.0; |
| } |
| qScreenDebug("screen mode:\n" |
| " width = %u\n" |
| " height = %u\n" |
| " refresh = %u\n" |
| " interlaced = %u", |
| uint(displayMode.width), uint(displayMode.height), uint(displayMode.refresh), uint(displayMode.interlaced)); |
| return static_cast<qreal>(displayMode.refresh); |
| } |
| |
| Qt::ScreenOrientation QQnxScreen::nativeOrientation() const |
| { |
| return m_nativeOrientation; |
| } |
| |
| Qt::ScreenOrientation QQnxScreen::orientation() const |
| { |
| Qt::ScreenOrientation orient; |
| if (m_nativeOrientation == Qt::LandscapeOrientation) { |
| // Landscape devices e.g. PlayBook |
| if (m_currentRotation == 0) |
| orient = Qt::LandscapeOrientation; |
| else if (m_currentRotation == 90) |
| orient = Qt::PortraitOrientation; |
| else if (m_currentRotation == 180) |
| orient = Qt::InvertedLandscapeOrientation; |
| else |
| orient = Qt::InvertedPortraitOrientation; |
| } else { |
| // Portrait devices e.g. Phones |
| // ###TODO Check these on an actual phone device |
| if (m_currentRotation == 0) |
| orient = Qt::PortraitOrientation; |
| else if (m_currentRotation == 90) |
| orient = Qt::LandscapeOrientation; |
| else if (m_currentRotation == 180) |
| orient = Qt::InvertedPortraitOrientation; |
| else |
| orient = Qt::InvertedLandscapeOrientation; |
| } |
| qScreenDebug() << "orientation =" << orient; |
| return orient; |
| } |
| |
| QWindow *QQnxScreen::topLevelAt(const QPoint &point) const |
| { |
| for (auto it = m_childWindows.rbegin(), end = m_childWindows.rend(); it != end; ++it) { |
| QWindow *win = (*it)->window(); |
| if (win->geometry().contains(point)) |
| return win; |
| } |
| return 0; |
| } |
| |
| /*! |
| Check if the supplied angles are perpendicular to each other. |
| */ |
| static bool isOrthogonal(int angle1, int angle2) |
| { |
| return ((angle1 - angle2) % 180) != 0; |
| } |
| |
| void QQnxScreen::setRotation(int rotation) |
| { |
| qScreenDebug("orientation = %d", rotation); |
| // Check if rotation changed |
| // We only want to rotate if we are the primary screen |
| if (m_currentRotation != rotation && isPrimaryScreen()) { |
| // Update rotation of root window |
| if (rootWindow()) |
| rootWindow()->setRotation(rotation); |
| |
| const QRect previousScreenGeometry = geometry(); |
| |
| // Swap dimensions if we've rotated 90 or 270 from initial orientation |
| if (isOrthogonal(m_initialRotation, rotation)) { |
| m_currentGeometry = QRect(0, 0, m_initialGeometry.height(), m_initialGeometry.width()); |
| m_currentPhysicalSize = QSize(m_initialPhysicalSize.height(), m_initialPhysicalSize.width()); |
| } else { |
| m_currentGeometry = QRect(0, 0, m_initialGeometry.width(), m_initialGeometry.height()); |
| m_currentPhysicalSize = m_initialPhysicalSize; |
| } |
| |
| // Resize root window if we've rotated 90 or 270 from previous orientation |
| if (isOrthogonal(m_currentRotation, rotation)) { |
| qScreenDebug() << "resize, size =" << m_currentGeometry.size(); |
| if (rootWindow()) |
| rootWindow()->setGeometry(QRect(QPoint(0,0), m_currentGeometry.size())); |
| |
| resizeWindows(previousScreenGeometry); |
| } else { |
| // TODO: Find one global place to flush display updates |
| // Force immediate display update if no geometry changes required |
| screen_flush_context(nativeContext(), 0); |
| } |
| |
| // Save new rotation |
| m_currentRotation = rotation; |
| |
| // TODO: check if other screens are supposed to rotate as well and/or whether this depends |
| // on if clone mode is being used. |
| // Rotating only the primary screen is what we had in the navigator event handler before refactoring |
| if (m_primaryScreen) { |
| QWindowSystemInterface::handleScreenOrientationChange(screen(), orientation()); |
| QWindowSystemInterface::handleScreenGeometryChange(screen(), m_currentGeometry, availableGeometry()); |
| } |
| |
| // Flush everything, so that the windows rotations are applied properly. |
| // Needed for non-maximized windows |
| screen_flush_context( m_screenContext, 0 ); |
| } |
| } |
| |
| /*! |
| Resize the given window proportionally to the screen geometry |
| */ |
| void QQnxScreen::resizeNativeWidgetWindow(QQnxWindow *w, const QRect &previousScreenGeometry) const |
| { |
| const qreal relativeX = static_cast<qreal>(w->geometry().topLeft().x()) / previousScreenGeometry.width(); |
| const qreal relativeY = static_cast<qreal>(w->geometry().topLeft().y()) / previousScreenGeometry.height(); |
| const qreal relativeWidth = static_cast<qreal>(w->geometry().width()) / previousScreenGeometry.width(); |
| const qreal relativeHeight = static_cast<qreal>(w->geometry().height()) / previousScreenGeometry.height(); |
| |
| const QRect windowGeometry(relativeX * geometry().width(), relativeY * geometry().height(), |
| relativeWidth * geometry().width(), relativeHeight * geometry().height()); |
| |
| w->setGeometry(windowGeometry); |
| } |
| |
| /*! |
| Resize the given window to fit the screen geometry |
| */ |
| void QQnxScreen::resizeTopLevelWindow(QQnxWindow *w, const QRect &previousScreenGeometry) const |
| { |
| QRect windowGeometry = w->geometry(); |
| |
| const qreal relativeCenterX = static_cast<qreal>(w->geometry().center().x()) / previousScreenGeometry.width(); |
| const qreal relativeCenterY = static_cast<qreal>(w->geometry().center().y()) / previousScreenGeometry.height(); |
| const QPoint newCenter(relativeCenterX * geometry().width(), relativeCenterY * geometry().height()); |
| |
| windowGeometry.moveCenter(newCenter); |
| |
| // adjust center position in case the window |
| // is clipped |
| if (!geometry().contains(windowGeometry)) { |
| const int x1 = windowGeometry.x(); |
| const int y1 = windowGeometry.y(); |
| const int x2 = x1 + windowGeometry.width(); |
| const int y2 = y1 + windowGeometry.height(); |
| |
| if (x1 < 0) { |
| const int centerX = qMin(qAbs(x1) + windowGeometry.center().x(), |
| geometry().center().x()); |
| |
| windowGeometry.moveCenter(QPoint(centerX, windowGeometry.center().y())); |
| } |
| |
| if (y1 < 0) { |
| const int centerY = qMin(qAbs(y1) + windowGeometry.center().y(), |
| geometry().center().y()); |
| |
| windowGeometry.moveCenter(QPoint(windowGeometry.center().x(), centerY)); |
| } |
| |
| if (x2 > geometry().width()) { |
| const int centerX = qMax(windowGeometry.center().x() - (x2 - geometry().width()), |
| geometry().center().x()); |
| |
| windowGeometry.moveCenter(QPoint(centerX, windowGeometry.center().y())); |
| } |
| |
| if (y2 > geometry().height()) { |
| const int centerY = qMax(windowGeometry.center().y() - (y2 - geometry().height()), |
| geometry().center().y()); |
| |
| windowGeometry.moveCenter(QPoint(windowGeometry.center().x(), centerY)); |
| } |
| } |
| |
| // at this point, if the window is still clipped, |
| // it means that it's too big to fit on the screen, |
| // so we need to proportionally shrink it |
| if (!geometry().contains(windowGeometry)) { |
| QSize newSize = windowGeometry.size(); |
| newSize.scale(geometry().size(), Qt::KeepAspectRatio); |
| windowGeometry.setSize(newSize); |
| |
| if (windowGeometry.x() < 0) |
| windowGeometry.moveCenter(QPoint(geometry().center().x(), windowGeometry.center().y())); |
| |
| if (windowGeometry.y() < 0) |
| windowGeometry.moveCenter(QPoint(windowGeometry.center().x(), geometry().center().y())); |
| } |
| |
| w->setGeometry(windowGeometry); |
| } |
| |
| /*! |
| Adjust windows to the new screen geometry. |
| */ |
| void QQnxScreen::resizeWindows(const QRect &previousScreenGeometry) |
| { |
| resizeMaximizedWindows(); |
| |
| Q_FOREACH (QQnxWindow *w, m_childWindows) { |
| |
| if (w->window()->windowState() & Qt::WindowFullScreen || w->window()->windowState() & Qt::WindowMaximized) |
| continue; |
| |
| if (w->parent()) { |
| // This is a native (non-alien) widget window |
| resizeNativeWidgetWindow(w, previousScreenGeometry); |
| } else { |
| // This is a toplevel window |
| resizeTopLevelWindow(w, previousScreenGeometry); |
| } |
| } |
| } |
| |
| QQnxWindow *QQnxScreen::findWindow(screen_window_t windowHandle) const |
| { |
| Q_FOREACH (QQnxWindow *window, m_childWindows) { |
| QQnxWindow * const result = window->findWindow(windowHandle); |
| if (result) |
| return result; |
| } |
| |
| return 0; |
| } |
| |
| void QQnxScreen::addWindow(QQnxWindow *window) |
| { |
| qScreenDebug() << "window =" << window; |
| |
| if (m_childWindows.contains(window)) |
| return; |
| |
| if (window->window()->type() != Qt::CoverWindow) { |
| // Ensure that the desktop window is at the bottom of the zorder. |
| // If we do not do this then we may end up activating the desktop |
| // when the navigator service gets an event that our window group |
| // has been activated (see QQnxScreen::activateWindowGroup()). |
| // Such a situation would strangely break focus handling due to the |
| // invisible desktop widget window being layered on top of normal |
| // windows |
| if (window->window()->type() == Qt::Desktop) |
| m_childWindows.push_front(window); |
| else |
| m_childWindows.push_back(window); |
| updateHierarchy(); |
| } |
| } |
| |
| void QQnxScreen::removeWindow(QQnxWindow *window) |
| { |
| qScreenDebug() << "window =" << window; |
| |
| if (window != m_coverWindow) { |
| const int numWindowsRemoved = m_childWindows.removeAll(window); |
| if (window == m_rootWindow) //We just removed the root window |
| m_rootWindow = 0; //TODO we need a new root window ;) |
| if (numWindowsRemoved > 0) |
| updateHierarchy(); |
| } else { |
| m_coverWindow = 0; |
| } |
| } |
| |
| void QQnxScreen::raiseWindow(QQnxWindow *window) |
| { |
| qScreenDebug() << "window =" << window; |
| |
| if (window != m_coverWindow) { |
| removeWindow(window); |
| m_childWindows.push_back(window); |
| } |
| } |
| |
| void QQnxScreen::lowerWindow(QQnxWindow *window) |
| { |
| qScreenDebug() << "window =" << window; |
| |
| if (window != m_coverWindow) { |
| removeWindow(window); |
| m_childWindows.push_front(window); |
| } |
| } |
| |
| void QQnxScreen::updateHierarchy() |
| { |
| qScreenDebug(); |
| |
| QList<QQnxWindow*>::const_iterator it; |
| int result; |
| int topZorder = 0; |
| |
| errno = 0; |
| if (rootWindow()) { |
| result = screen_get_window_property_iv(rootWindow()->nativeHandle(), SCREEN_PROPERTY_ZORDER, &topZorder); |
| if (result != 0) { //This can happen if we use winId in QWidgets |
| topZorder = 10; |
| qWarning("QQnxScreen: failed to query root window z-order, errno=%d", errno); |
| } |
| } else { |
| topZorder = 0; //We do not need z ordering on the secondary screen, because only one window |
| //is supported there |
| } |
| |
| topZorder++; // root window has the lowest z-order in the windowgroup |
| |
| int underlayZorder = -1; |
| // Underlays sit immediately above the root window in the z-ordering |
| Q_FOREACH (screen_window_t underlay, m_underlays) { |
| // Do nothing when this fails. This can happen if we have stale windows in m_underlays, |
| // which in turn can happen because a window was removed but we didn't get a notification |
| // yet. |
| screen_set_window_property_iv(underlay, SCREEN_PROPERTY_ZORDER, &underlayZorder); |
| underlayZorder--; |
| } |
| |
| // Normal Qt windows come next above the root window z-ordering |
| for (it = m_childWindows.constBegin(); it != m_childWindows.constEnd(); ++it) |
| (*it)->updateZorder(topZorder); |
| |
| // Finally overlays sit above all else in the z-ordering |
| Q_FOREACH (screen_window_t overlay, m_overlays) { |
| // No error handling, see underlay logic above |
| screen_set_window_property_iv(overlay, SCREEN_PROPERTY_ZORDER, &topZorder); |
| topZorder++; |
| } |
| |
| // After a hierarchy update, we need to force a flush on all screens. |
| // Right now, all screens share a context. |
| screen_flush_context(m_screenContext, 0); |
| } |
| |
| void QQnxScreen::adjustOrientation() |
| { |
| if (!m_primaryScreen) |
| return; |
| |
| bool ok = false; |
| const int rotation = qEnvironmentVariableIntValue("ORIENTATION", &ok); |
| |
| if (ok) |
| setRotation(rotation); |
| } |
| |
| QPlatformCursor * QQnxScreen::cursor() const |
| { |
| return m_cursor; |
| } |
| |
| void QQnxScreen::keyboardHeightChanged(int height) |
| { |
| if (height == m_keyboardHeight) |
| return; |
| |
| m_keyboardHeight = height; |
| |
| QWindowSystemInterface::handleScreenGeometryChange(screen(), geometry(), availableGeometry()); |
| } |
| |
| void QQnxScreen::addOverlayWindow(screen_window_t window) |
| { |
| m_overlays.append(window); |
| updateHierarchy(); |
| } |
| |
| void QQnxScreen::addUnderlayWindow(screen_window_t window) |
| { |
| m_underlays.append(window); |
| updateHierarchy(); |
| } |
| |
| void QQnxScreen::addMultimediaWindow(const QByteArray &id, screen_window_t window) |
| { |
| // find the QnxWindow this mmrenderer window is related to |
| QQnxWindow *mmWindow = findMultimediaWindow(m_childWindows, id); |
| |
| if (!mmWindow) |
| return; |
| |
| mmWindow->setMMRendererWindow(window); |
| |
| updateHierarchy(); |
| } |
| |
| void QQnxScreen::removeOverlayOrUnderlayWindow(screen_window_t window) |
| { |
| const int numRemoved = m_overlays.removeAll(window) + m_underlays.removeAll(window); |
| if (numRemoved > 0) { |
| updateHierarchy(); |
| Q_EMIT foreignWindowClosed(window); |
| } |
| } |
| |
| void QQnxScreen::newWindowCreated(void *window) |
| { |
| Q_ASSERT(thread() == QThread::currentThread()); |
| const screen_window_t windowHandle = reinterpret_cast<screen_window_t>(window); |
| screen_display_t display = 0; |
| if (screen_get_window_property_pv(windowHandle, SCREEN_PROPERTY_DISPLAY, (void**)&display) != 0) { |
| qWarning("QQnx: Failed to get screen for window, errno=%d", errno); |
| return; |
| } |
| |
| int zorder; |
| if (screen_get_window_property_iv(windowHandle, SCREEN_PROPERTY_ZORDER, &zorder) != 0) { |
| qWarning("QQnx: Failed to get z-order for window, errno=%d", errno); |
| zorder = 0; |
| } |
| |
| char windowNameBuffer[256] = { 0 }; |
| QByteArray windowName; |
| |
| if (screen_get_window_property_cv(windowHandle, SCREEN_PROPERTY_ID_STRING, |
| sizeof(windowNameBuffer) - 1, windowNameBuffer) != 0) { |
| qWarning("QQnx: Failed to get id for window, errno=%d", errno); |
| } |
| |
| windowName = QByteArray(windowNameBuffer); |
| |
| if (display == nativeDisplay()) { |
| // A window was created on this screen. If we don't know about this window yet, it means |
| // it was not created by Qt, but by some foreign library like the multimedia renderer, which |
| // creates an overlay window when playing a video. |
| // |
| // Treat all foreign windows as overlays, underlays or as windows |
| // created by the BlackBerry QtMultimedia plugin. |
| // |
| // In the case of the BlackBerry QtMultimedia plugin, we need to |
| // "attach" the foreign created mmrenderer window to the correct |
| // platform window (usually the one belonging to QVideoWidget) to |
| // ensure proper z-ordering. |
| // |
| // Otherwise, assume that if a foreign window already has a Z-Order both negative and |
| // less than the default Z-Order installed by mmrender on windows it creates, |
| // the windows should be treated as an underlay. Otherwise, we treat it as an overlay. |
| if (!windowName.isEmpty() && windowName.startsWith("MmRendererVideoWindowControl")) { |
| addMultimediaWindow(windowName, windowHandle); |
| } else if (!findWindow(windowHandle)) { |
| if (zorder <= MAX_UNDERLAY_ZORDER) |
| addUnderlayWindow(windowHandle); |
| else |
| addOverlayWindow(windowHandle); |
| Q_EMIT foreignWindowCreated(windowHandle); |
| } |
| } |
| } |
| |
| void QQnxScreen::windowClosed(void *window) |
| { |
| Q_ASSERT(thread() == QThread::currentThread()); |
| const screen_window_t windowHandle = reinterpret_cast<screen_window_t>(window); |
| |
| QQnxWindow *mmWindow = findMultimediaWindow(m_childWindows, windowHandle); |
| |
| if (mmWindow) |
| mmWindow->clearMMRendererWindow(); |
| else |
| removeOverlayOrUnderlayWindow(windowHandle); |
| } |
| |
| void QQnxScreen::windowGroupStateChanged(const QByteArray &id, Qt::WindowState state) |
| { |
| qScreenDebug(); |
| |
| if (!rootWindow() || id != rootWindow()->groupName()) |
| return; |
| |
| QWindow * const window = rootWindow()->window(); |
| |
| if (!window) |
| return; |
| |
| QWindowSystemInterface::handleWindowStateChanged(window, state); |
| } |
| |
| void QQnxScreen::activateWindowGroup(const QByteArray &id) |
| { |
| qScreenDebug(); |
| |
| if (!rootWindow() || id != rootWindow()->groupName()) |
| return; |
| |
| QWindow * const window = rootWindow()->window(); |
| |
| if (!window) |
| return; |
| |
| Q_FOREACH (QQnxWindow *childWindow, m_childWindows) |
| childWindow->setExposed(true); |
| |
| if (m_coverWindow) |
| m_coverWindow->setExposed(false); |
| } |
| |
| void QQnxScreen::deactivateWindowGroup(const QByteArray &id) |
| { |
| qScreenDebug(); |
| |
| if (!rootWindow() || id != rootWindow()->groupName()) |
| return; |
| |
| if (m_coverWindow) |
| m_coverWindow->setExposed(true); |
| |
| Q_FOREACH (QQnxWindow *childWindow, m_childWindows) |
| childWindow->setExposed(false); |
| } |
| |
| QQnxWindow *QQnxScreen::rootWindow() const |
| { |
| return m_rootWindow; |
| } |
| |
| void QQnxScreen::setRootWindow(QQnxWindow *window) |
| { |
| // Optionally disable the screen power save |
| bool ok = false; |
| const int disablePowerSave = qEnvironmentVariableIntValue("QQNX_DISABLE_POWER_SAVE", &ok); |
| if (ok && disablePowerSave) { |
| const int mode = SCREEN_IDLE_MODE_KEEP_AWAKE; |
| int result = screen_set_window_property_iv(window->nativeHandle(), SCREEN_PROPERTY_IDLE_MODE, &mode); |
| if (result != 0) |
| qWarning("QQnxRootWindow: failed to disable power saving mode"); |
| } |
| m_rootWindow = window; |
| } |
| |
| QT_END_NAMESPACE |