diff options
Diffstat (limited to 'src/plugins/platforms/blackberry/qbbwindow.cpp')
-rw-r--r-- | src/plugins/platforms/blackberry/qbbwindow.cpp | 665 |
1 files changed, 665 insertions, 0 deletions
diff --git a/src/plugins/platforms/blackberry/qbbwindow.cpp b/src/plugins/platforms/blackberry/qbbwindow.cpp new file mode 100644 index 0000000000..bc9f112b01 --- /dev/null +++ b/src/plugins/platforms/blackberry/qbbwindow.cpp @@ -0,0 +1,665 @@ +/*************************************************************************** +** +** Copyright (C) 2011 - 2012 Research In Motion +** Contact: http://www.qt-project.org/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qbbwindow.h" +#include "qbbglcontext.h" +#include "qbbintegration.h" +#include "qbbscreen.h" + +#include <QtGui/QWindow> +#include <QtGui/QWindowSystemInterface> + +#include <QtCore/QDebug> + +#include <errno.h> + +QT_BEGIN_NAMESPACE + +QBBWindow::QBBWindow(QWindow *window, screen_context_t context) + : QPlatformWindow(window), + m_screenContext(context), + m_window(0), + m_currentBufferIndex(-1), + m_previousBufferIndex(-1), + m_platformOpenGLContext(0), + m_screen(0), + m_parentWindow(0), + m_visible(true) +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "window =" << window << ", size =" << window->size(); +#endif + int result; + + // Create child QNX window + errno = 0; + result = screen_create_window_type(&m_window, m_screenContext, SCREEN_CHILD_WINDOW); + if (result != 0) { + qFatal("QBBWindow: failed to create window, errno=%d", errno); + } + + // Set window buffer usage based on rendering API + int val; + QSurface::SurfaceType surfaceType = window->surfaceType(); + switch (surfaceType) { + case QSurface::RasterSurface: + val = SCREEN_USAGE_NATIVE | SCREEN_USAGE_READ | SCREEN_USAGE_WRITE; + break; + case QSurface::OpenGLSurface: + val = SCREEN_USAGE_OPENGL_ES2; + break; + default: + qFatal("QBBWindow: unsupported window API"); + break; + } + + errno = 0; + result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_USAGE, &val); + if (result != 0) { + qFatal("QBBWindow: failed to set window buffer usage, errno=%d", errno); + } + + // Alpha channel is always pre-multiplied if present + errno = 0; + val = SCREEN_PRE_MULTIPLIED_ALPHA; + result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_ALPHA_MODE, &val); + if (result != 0) { + qFatal("QBBWindow: failed to set window alpha mode, errno=%d", errno); + } + + // Make the window opaque + errno = 0; + val = SCREEN_TRANSPARENCY_NONE; + result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_TRANSPARENCY, &val); + if (result != 0) { + qFatal("QBBWindow: failed to set window transparency, errno=%d", errno); + } + + // Set the window swap interval + errno = 0; + val = 1; + result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SWAP_INTERVAL, &val); + if (result != 0) { + qFatal("QBBWindow: failed to set window swap interval, errno=%d", errno); + } + + // Assign the window to the primary display (this is the default specified by screen). + setScreen(QBBScreen::primaryDisplay()); + + // Add the window to the root of the hierarchy + QBBScreen::addWindow(this); + + // Add window to plugin's window mapper + QBBIntegration::addWindow(m_window, window); +} + +QBBWindow::~QBBWindow() +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "window =" << window(); +#endif + // Remove from plugin's window mapper + QBBIntegration::removeWindow(m_window); + + // Remove from parent's Hierarchy. + removeFromParent(); + QBBScreen::updateHierarchy(); + + // We shouldn't allow this case unless QT allows it. Does it? Or should we send the + // handleCloseEvent on all children when this window is deleted? + if (m_childWindows.size() > 0) + qFatal("QBBWindow: window destroyed before children!"); + + // Cleanup QNX window and its buffers + screen_destroy_window(m_window); +} + +void QBBWindow::setGeometry(const QRect &rect) +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "window =" << window() << ", (" << rect.x() << "," << rect.y() << "," << rect.width() << "," << rect.height() << ")"; +#endif + + QRect oldGeometry = geometry(); + + // Call base class method + QPlatformWindow::setGeometry(rect); + + // Set window geometry equal to widget geometry + errno = 0; + int val[2]; + val[0] = rect.x(); + val[1] = rect.y(); + int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_POSITION, val); + if (result != 0) { + qFatal("QBBWindow: failed to set window position, errno=%d", errno); + } + + errno = 0; + val[0] = rect.width(); + val[1] = rect.height(); + result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SIZE, val); + if (result != 0) { + qFatal("QBBWindow: failed to set window size, errno=%d", errno); + } + + // Set viewport size equal to window size + errno = 0; + result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SOURCE_SIZE, val); + if (result != 0) { + qFatal("QBBWindow: failed to set window source size, errno=%d", errno); + } + + // Now move all children. + QPoint offset; + if (!oldGeometry.isEmpty()) { + offset = rect.topLeft(); + offset -= oldGeometry.topLeft(); + + QList<QBBWindow*>::iterator it; + for (it = m_childWindows.begin(); it != m_childWindows.end(); it++) { + (*it)->offset(offset); + } + } +} + +void QBBWindow::offset(const QPoint &offset) +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "window =" << window(); +#endif + // Move self and then children. + QRect newGeometry = geometry(); + newGeometry.translate(offset); + + // Call the base class + QPlatformWindow::setGeometry(newGeometry); + + int val[2]; + + errno = 0; + val[0] = newGeometry.x(); + val[1] = newGeometry.y(); + int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_POSITION, val); + if (result != 0) { + qFatal("QBBWindow: failed to set window position, errno=%d", errno); + } + + QList<QBBWindow*>::iterator it; + for (it = m_childWindows.begin(); it != m_childWindows.end(); it++) { + (*it)->offset(offset); + } +} + +void QBBWindow::setVisible(bool visible) +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "window =" << window() << "visible =" << visible; +#endif + + m_visible = visible; + + QBBWindow *root = this; + while (root->m_parentWindow) + root = root->m_parentWindow; + + root->updateVisibility(root->m_visible); + + window()->requestActivateWindow(); +} + +void QBBWindow::updateVisibility(bool parentVisible) +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "parentVisible =" << parentVisible << "window =" << window(); +#endif + // Set window visibility + errno = 0; + int val = (m_visible && parentVisible) ? 1 : 0; + int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_VISIBLE, &val); + if (result != 0) { + qFatal("QBBWindow: failed to set window visibility, errno=%d", errno); + } + + QList<QBBWindow *>::iterator it; + for (it = m_childWindows.begin(); it != m_childWindows.end(); it++) { + (*it)->updateVisibility(m_visible && parentVisible); + } +} + +void QBBWindow::setOpacity(qreal level) +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "window =" << window() << "opacity =" << level; +#endif + // Set window global alpha + errno = 0; + int val = (int)(level * 255); + int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_GLOBAL_ALPHA, &val); + if (result != 0) { + qFatal("QBBWindow: failed to set window global alpha, errno=%d", errno); + } + + // TODO: How to handle children of this window? If we change all the visibilities, then + // the transparency will look wrong... +} + +void QBBWindow::setBufferSize(const QSize &size) +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "window =" << window() << "size =" << size; +#endif + // Set window buffer size + errno = 0; + int val[2] = { size.width(), size.height() }; + int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_BUFFER_SIZE, val); + if (result != 0) { + qFatal("QBBWindow: failed to set window buffer size, errno=%d", errno); + } + + // Create window buffers if they do not exist + if (!hasBuffers()) { + // Get pixel format from EGL config if using OpenGL; + // otherwise inherit pixel format of window's screen + if (m_platformOpenGLContext != 0) { + val[0] = platformWindowFormatToNativeFormat(m_platformOpenGLContext->format()); + } else { + val[0] = m_screen->nativeFormat(); + } + + errno = 0; + result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_FORMAT, val); + if (result != 0) { + qFatal("QBBWindow: failed to set window pixel format, errno=%d", errno); + } + + errno = 0; + result = screen_create_window_buffers(m_window, MAX_BUFFER_COUNT); + if (result != 0) { + qFatal("QBBWindow: failed to create window buffers, errno=%d", errno); + } + } + + // Cache new buffer size + m_bufferSize = size; + + // Buffers were destroyed; reacquire them + m_currentBufferIndex = -1; + m_previousDirty = QRegion(); + m_scrolled = QRegion(); +} + +QBBBuffer &QBBWindow::renderBuffer() +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "window =" << window(); +#endif + // Check if render buffer is invalid + if (m_currentBufferIndex == -1) { + // Get all buffers available for rendering + errno = 0; + screen_buffer_t buffers[MAX_BUFFER_COUNT]; + int result = screen_get_window_property_pv(m_window, SCREEN_PROPERTY_RENDER_BUFFERS, (void **)buffers); + if (result != 0) { + qFatal("QBBWindow: failed to query window buffers, errno=%d", errno); + } + + // Wrap each buffer + for (int i = 0; i < MAX_BUFFER_COUNT; ++i) { + m_buffers[i] = QBBBuffer(buffers[i]); + } + + // Use the first available render buffer + m_currentBufferIndex = 0; + m_previousBufferIndex = -1; + } + + return m_buffers[m_currentBufferIndex]; +} + +void QBBWindow::scroll(const QRegion ®ion, int dx, int dy, bool flush) +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "window =" << window(); +#endif + copyBack(region, dx, dy, flush); + m_scrolled += region; +} + +void QBBWindow::post(const QRegion &dirty) +{ + // Check if render buffer exists and something was rendered + if (m_currentBufferIndex != -1 && !dirty.isEmpty()) { +#if defined(QBBWINDOW_DEBUG) + qDebug() << "QBBWindow::post - window =" << window(); +#endif + QBBBuffer ¤tBuffer = m_buffers[m_currentBufferIndex]; + + // Copy unmodified region from old render buffer to new render buffer; + // required to allow partial updates + QRegion preserve = m_previousDirty - dirty - m_scrolled; + copyBack(preserve, 0, 0); + + // Calculate region that changed + QRegion modified = preserve + dirty + m_scrolled; + QRect rect = modified.boundingRect(); + int dirtyRect[4] = { rect.x(), rect.y(), rect.x() + rect.width(), rect.y() + rect.height() }; + + // Update the display with contents of render buffer + errno = 0; + int result = screen_post_window(m_window, currentBuffer.nativeBuffer(), 1, dirtyRect, 0); + if (result != 0) { + qFatal("QBBWindow: failed to post window buffer, errno=%d", errno); + } + + // Advance to next nender buffer + m_previousBufferIndex = m_currentBufferIndex++; + if (m_currentBufferIndex >= MAX_BUFFER_COUNT) { + m_currentBufferIndex = 0; + } + + // Save modified region and clear scrolled region + m_previousDirty = dirty; + m_scrolled = QRegion(); + + // Notify screen that window posted + if (m_screen != 0) { + m_screen->onWindowPost(this); + } + } +} + +void QBBWindow::setScreen(QBBScreen *platformScreen) +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "window =" << window() << "platformScreen =" << platformScreen; +#endif + + if (m_screen == platformScreen) + return; + + m_screen = platformScreen; + + // Move window to proper screen/display + errno = 0; + screen_display_t display = platformScreen->nativeDisplay(); + int result = screen_set_window_property_pv(m_window, SCREEN_PROPERTY_DISPLAY, (void **)&display); + if (result != 0) { + qFatal("QBBWindow: failed to set window display, errno=%d", errno); + } + + // Add window to display's window group + errno = 0; + result = screen_join_window_group(m_window, platformScreen->windowGroupName()); + if (result != 0) { + qFatal("QBBWindow: failed to join window group, errno=%d", errno); + } + + QList<QBBWindow*>::iterator it; + for (it = m_childWindows.begin(); it != m_childWindows.end(); it++) { + // Only subwindows and tooltips need necessarily be moved to another display with the window. + if ((window()->windowType() & Qt::WindowType_Mask) == Qt::SubWindow || + (window()->windowType() & Qt::WindowType_Mask) == Qt::ToolTip) + (*it)->setScreen(platformScreen); + } + + QBBScreen::updateHierarchy(); +} + +void QBBWindow::removeFromParent() +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "window =" << window(); +#endif + // Remove from old Hierarchy position + if (m_parentWindow) { + if (m_parentWindow->m_childWindows.removeAll(this)) + m_parentWindow = 0; + else + qFatal("QBBWindow: Window Hierarchy broken; window has parent, but parent hasn't got child."); + } else { + QBBScreen::removeWindow(this); + } +} + +void QBBWindow::setParent(const QPlatformWindow *window) +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "window =" << this->window() << "platformWindow =" << window; +#endif + // Cast away the const, we need to modify the hierarchy. + QBBWindow *newParent = 0; + + if (window) + newParent = static_cast<QBBWindow*>((QPlatformWindow *)window); + + if (newParent == m_parentWindow) + return; + + removeFromParent(); + m_parentWindow = newParent; + + // Add to new hierarchy position. + if (m_parentWindow) { + if (m_parentWindow->m_screen != m_screen) + setScreen(m_parentWindow->m_screen); + + m_parentWindow->m_childWindows.push_back(this); + } else { + QBBScreen::addWindow(this); + } + + QBBScreen::updateHierarchy(); +} + +void QBBWindow::raise() +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "window =" << window(); +#endif + + QBBWindow *oldParent = m_parentWindow; + if (oldParent) { + removeFromParent(); + oldParent->m_childWindows.push_back(this); + } else { + QBBScreen::raiseWindow(this); + } + + QBBScreen::updateHierarchy(); +} + +void QBBWindow::lower() +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "window =" << window(); +#endif + + QBBWindow *oldParent = m_parentWindow; + if (oldParent) { + removeFromParent(); + oldParent->m_childWindows.push_front(this); + } else { + QBBScreen::lowerWindow(this); + } + + QBBScreen::updateHierarchy(); +} + +void QBBWindow::requestActivateWindow() +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "window =" << window(); +#endif + + // TODO: Tell screen to set keyboard focus to this window. + + // Notify that we gained focus. + gainedFocus(); +} + +void QBBWindow::gainedFocus() +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "window =" << window(); +#endif + + // Got focus + QWindowSystemInterface::handleWindowActivated(window()); +} + +void QBBWindow::setPlatformOpenGLContext(QBBGLContext *platformOpenGLContext) +{ + // This function does not take ownership of the platform gl context. + // It is owned by the frontend QOpenGLContext + m_platformOpenGLContext = platformOpenGLContext; +} + +void QBBWindow::updateZorder(int &topZorder) +{ + errno = 0; + int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_ZORDER, &topZorder); + topZorder++; + + if (result != 0) + qFatal("QBBWindow: failed to set window z-order=%d, errno=%d, mWindow=%p", topZorder, errno, m_window); + + QList<QBBWindow*>::const_iterator it; + + for (it = m_childWindows.begin(); it != m_childWindows.end(); it++) + (*it)->updateZorder(topZorder); +} + +void QBBWindow::copyBack(const QRegion ®ion, int dx, int dy, bool flush) +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO << "window =" << window(); +#endif + int result; + + // Abort if previous buffer is invalid + if (m_previousBufferIndex == -1) { + return; + } + + // Abort if nothing to copy + if (region.isEmpty()) { + return; + } + + QBBBuffer ¤tBuffer = m_buffers[m_currentBufferIndex]; + QBBBuffer &previousBuffer = m_buffers[m_previousBufferIndex]; + + // Break down region into non-overlapping rectangles + QVector<QRect> rects = region.rects(); + for (int i = rects.size() - 1; i >= 0; i--) { + // Clip rectangle to bounds of target + QRect rect = rects[i].intersected( currentBuffer.rect() ); + + if (rect.isEmpty()) + continue; + + // Setup blit operation + int attribs[] = { SCREEN_BLIT_SOURCE_X, rect.x(), + SCREEN_BLIT_SOURCE_Y, rect.y(), + SCREEN_BLIT_SOURCE_WIDTH, rect.width(), + SCREEN_BLIT_SOURCE_HEIGHT, rect.height(), + SCREEN_BLIT_DESTINATION_X, rect.x() + dx, + SCREEN_BLIT_DESTINATION_Y, rect.y() + dy, + SCREEN_BLIT_DESTINATION_WIDTH, rect.width(), + SCREEN_BLIT_DESTINATION_HEIGHT, rect.height(), + SCREEN_BLIT_END }; + + // Queue blit operation + errno = 0; + result = screen_blit(m_screenContext, currentBuffer.nativeBuffer(), previousBuffer.nativeBuffer(), attribs); + if (result != 0) { + qFatal("QBBWindow: failed to blit buffers, errno=%d", errno); + } + } + + // Check if flush requested + if (flush) { + // Wait for all blits to complete + errno = 0; + result = screen_flush_blits(m_screenContext, SCREEN_WAIT_IDLE); + if (result != 0) { + qFatal("QBBWindow: failed to flush blits, errno=%d", errno); + } + + // Buffer was modified outside the CPU + currentBuffer.invalidateInCache(); + } +} + +int QBBWindow::platformWindowFormatToNativeFormat(const QSurfaceFormat &format) +{ +#if defined(QBBWINDOW_DEBUG) + qDebug() << Q_FUNC_INFO; +#endif + // Extract size of colour channels from window format + int redSize = format.redBufferSize(); + if (redSize == -1) { + qFatal("QBBWindow: red size not defined"); + } + + int greenSize = format.greenBufferSize(); + if (greenSize == -1) { + qFatal("QBBWindow: green size not defined"); + } + + int blueSize = format.blueBufferSize(); + if (blueSize == -1) { + qFatal("QBBWindow: blue size not defined"); + } + + // select matching native format + if (redSize == 5 && greenSize == 6 && blueSize == 5) { + return SCREEN_FORMAT_RGB565; + } else if (redSize == 8 && greenSize == 8 && blueSize == 8) { + return SCREEN_FORMAT_RGBA8888; + } else { + qFatal("QBBWindow: unsupported pixel format"); + return 0; + } +} + +QT_END_NAMESPACE |