/*************************************************************************** ** ** Copyright (C) 2011 - 2012 Research In Motion ** Contact: http://www.qt-project.org/legal ** ** 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 Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qqnxscreen.h" #include "qqnxwindow.h" #include "qqnxcursor.h" #include #include #include #include #ifdef 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 QT_BEGIN_NAMESPACE static QSize determineScreenSize(screen_display_t display, bool primaryScreen) { int val[2]; errno = 0; const int result = screen_get_display_property_iv(display, SCREEN_PROPERTY_PHYSICAL_SIZE, val); if (result != 0) { qFatal("QQnxScreen: failed to query display physical size, errno=%d", errno); return QSize(150, 90); } if (val[0] > 0 && val[1] > 0) return QSize(val[0], val[1]); qWarning("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 QStringList envPhySizeStrList = envPhySizeStr.split(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) { qFatal("QQnxScreen: The value of QQNX_PHYSICAL_SCREEN_SIZE must be in the format \"width,height\" in mm, with width, height > 0. 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) qFatal("QQnxScreen: QQNX_PHYSICAL_SCREEN_SIZE variable not set. Could not determine physical screen size."); return QSize(150, 90); #endif } QQnxScreen::QQnxScreen(screen_context_t screenContext, screen_display_t display, bool primaryScreen) : m_screenContext(screenContext), m_display(display), m_rootWindow(), m_primaryScreen(primaryScreen), m_posted(false), m_keyboardHeight(0), m_nativeOrientation(Qt::PrimaryOrientation), m_platformContext(0), m_cursor(new QQnxCursor()) { qScreenDebug() << Q_FUNC_INFO; // Cache initial orientation of this display errno = 0; int result = screen_get_display_property_iv(m_display, SCREEN_PROPERTY_ROTATION, &m_initialRotation); if (result != 0) { qFatal("QQnxScreen: failed to query display rotation, errno=%d", errno); } m_currentRotation = m_initialRotation; // Cache size of this display in pixels errno = 0; int val[2]; result = screen_get_display_property_iv(m_display, SCREEN_PROPERTY_SIZE, val); if (result != 0) { qFatal("QQnxScreen: failed to query display size, errno=%d", errno); } m_currentGeometry = m_initialGeometry = QRect(0, 0, val[0], val[1]); // 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(); // We only create the root window if we are the primary display. if (primaryScreen) m_rootWindow = QSharedPointer(new QQnxRootWindow(this)); } QQnxScreen::~QQnxScreen() { qScreenDebug() << Q_FUNC_INFO; delete m_cursor; } static int defaultDepth() { qScreenDebug() << Q_FUNC_INFO; 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 = qgetenv("QQNX_DISPLAY_DEPTH").toInt(); if (defaultDepth != 16 && defaultDepth != 32) { defaultDepth = 32; } } return defaultDepth; } QRect QQnxScreen::availableGeometry() const { qScreenDebug() << Q_FUNC_INFO; // 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(&displayMode)); if (result != 0) { qWarning("QQnxScreen: Failed to query screen mode. Using default value of 60Hz"); return 60.0; } qScreenDebug() << Q_FUNC_INFO << "screen mode:" << endl << " width =" << displayMode.width << endl << " height =" << displayMode.height << endl << " refresh =" << displayMode.refresh << endl << " interlaced =" << displayMode.interlaced; return static_cast(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() << Q_FUNC_INFO << "orientation =" << orient; return orient; } /*! 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() << Q_FUNC_INFO << "orientation =" << rotation; // Check if rotation changed if (m_currentRotation != rotation) { // Update rotation of root window if (m_rootWindow) m_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() << Q_FUNC_INFO << "resize, size =" << m_currentGeometry.size(); if (m_rootWindow) m_rootWindow->resize(m_currentGeometry.size()); if (m_primaryScreen) resizeWindows(previousScreenGeometry); } else { // TODO: Find one global place to flush display updates // Force immediate display update if no geometry changes required if (m_rootWindow) m_rootWindow->flush(); } // 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); } // 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(w->geometry().topLeft().x()) / previousScreenGeometry.width(); const qreal relativeY = static_cast(w->geometry().topLeft().y()) / previousScreenGeometry.height(); const qreal relativeWidth = static_cast(w->geometry().width()) / previousScreenGeometry.width(); const qreal relativeHeight = static_cast(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(w->geometry().center().x()) / previousScreenGeometry.width(); const qreal relativeCenterY = static_cast(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) { Q_FOREACH (QQnxWindow *window, m_childWindows) { QQnxWindow * const result = window->findWindow(windowHandle); if (result) return result; } return 0; } void QQnxScreen::addWindow(QQnxWindow *window) { qScreenDebug() << Q_FUNC_INFO << "window =" << window; if (m_childWindows.contains(window)) return; // 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() << Q_FUNC_INFO << "window =" << window; const int numWindowsRemoved = m_childWindows.removeAll(window); if (numWindowsRemoved > 0) updateHierarchy(); } void QQnxScreen::raiseWindow(QQnxWindow *window) { qScreenDebug() << Q_FUNC_INFO << "window =" << window; removeWindow(window); m_childWindows.push_back(window); updateHierarchy(); } void QQnxScreen::lowerWindow(QQnxWindow *window) { qScreenDebug() << Q_FUNC_INFO << "window =" << window; removeWindow(window); m_childWindows.push_front(window); updateHierarchy(); } void QQnxScreen::updateHierarchy() { qScreenDebug() << Q_FUNC_INFO; QList::const_iterator it; int topZorder = 1; // root window is z-order 0, all "top" level windows are "above" it for (it = m_childWindows.constBegin(); it != m_childWindows.constEnd(); ++it) (*it)->updateZorder(topZorder); topZorder++; Q_FOREACH (screen_window_t overlay, m_overlays) { // Do nothing when this fails. This can happen if we have stale windows in mOverlays, // which in turn can happen because a window was removed but we didn't get a notification // yet. 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::onWindowPost(QQnxWindow *window) { qScreenDebug() << Q_FUNC_INFO; Q_UNUSED(window) // post app window (so navigator will show it) after first child window // has posted; this only needs to happen once as the app window's content // never changes if (!m_posted && m_rootWindow) { m_rootWindow->post(); m_posted = true; } } QPlatformCursor * QQnxScreen::cursor() const { return m_cursor; } void QQnxScreen::keyboardHeightChanged(int height) { if (height == m_keyboardHeight) return; m_keyboardHeight = height; QWindowSystemInterface::handleScreenAvailableGeometryChange(screen(), availableGeometry()); } void QQnxScreen::addOverlayWindow(screen_window_t window) { m_overlays.append(window); updateHierarchy(); } void QQnxScreen::removeOverlayWindow(screen_window_t window) { const int numOverlaysRemoved = m_overlays.removeAll(window); if (numOverlaysRemoved > 0) updateHierarchy(); } void QQnxScreen::newWindowCreated(void *window) { Q_ASSERT(thread() == QThread::currentThread()); const screen_window_t windowHandle = reinterpret_cast(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; } 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 here. if (!findWindow(windowHandle)) addOverlayWindow(windowHandle); } } void QQnxScreen::windowClosed(void *window) { Q_ASSERT(thread() == QThread::currentThread()); const screen_window_t windowHandle = reinterpret_cast(window); removeOverlayWindow(windowHandle); } void QQnxScreen::activateWindowGroup(const QByteArray &id) { qScreenDebug() << Q_FUNC_INFO; if (!rootWindow() || id != rootWindow()->groupName()) return; if (!m_childWindows.isEmpty()) { // We're picking up the last window of the list here // because this list is ordered by stacking order. // Last window is effectively the one on top. QWindow * const window = m_childWindows.last()->window(); QWindowSystemInterface::handleWindowActivated(window); } } void QQnxScreen::deactivateWindowGroup(const QByteArray &id) { qScreenDebug() << Q_FUNC_INFO; if (!rootWindow() || id != rootWindow()->groupName()) return; QWindowSystemInterface::handleWindowActivated(0); } QT_END_NAMESPACE