1 /***************************************************************************
3 ** Copyright (C) 2011 - 2012 Research In Motion
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the plugins of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qqnxwindow.h"
44 #include "qqnxglcontext.h"
46 #include "qqnxintegration.h"
47 #include "qqnxscreen.h"
49 #include <QtGui/QWindow>
50 #include <QtGui/qwindowsysteminterface.h>
52 #include <QtCore/QDebug>
56 #ifdef QQNXWINDOW_DEBUG
57 #define qWindowDebug qDebug
59 #define qWindowDebug QT_NO_QDEBUG_MACRO
64 QQnxWindow::QQnxWindow(QWindow *window, screen_context_t context)
65 : QPlatformWindow(window),
66 m_screenContext(context),
68 m_currentBufferIndex(-1),
69 m_previousBufferIndex(-1),
71 m_platformOpenGLContext(0),
76 m_windowState(Qt::WindowNoState)
78 qWindowDebug() << Q_FUNC_INFO << "window =" << window << ", size =" << window->size();
81 // Create child QNX window
83 result = screen_create_window_type(&m_window, m_screenContext, SCREEN_CHILD_WINDOW);
85 qFatal("QQnxWindow: failed to create window, errno=%d", errno);
88 // Set window buffer usage based on rendering API
90 QSurface::SurfaceType surfaceType = window->surfaceType();
91 switch (surfaceType) {
92 case QSurface::RasterSurface:
93 val = SCREEN_USAGE_NATIVE | SCREEN_USAGE_READ | SCREEN_USAGE_WRITE;
95 case QSurface::OpenGLSurface:
96 val = SCREEN_USAGE_OPENGL_ES2;
99 qFatal("QQnxWindow: unsupported window API");
104 result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_USAGE, &val);
106 qFatal("QQnxWindow: failed to set window buffer usage, errno=%d", errno);
109 // Alpha channel is always pre-multiplied if present
111 val = SCREEN_PRE_MULTIPLIED_ALPHA;
112 result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_ALPHA_MODE, &val);
114 qFatal("QQnxWindow: failed to set window alpha mode, errno=%d", errno);
117 // Make the window opaque
119 val = SCREEN_TRANSPARENCY_NONE;
120 result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_TRANSPARENCY, &val);
122 qFatal("QQnxWindow: failed to set window transparency, errno=%d", errno);
125 // Set the window swap interval
128 result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SWAP_INTERVAL, &val);
130 qFatal("QQnxWindow: failed to set window swap interval, errno=%d", errno);
133 setScreen(static_cast<QQnxScreen *>(window->screen()->handle()));
135 // Add window to plugin's window mapper
136 QQnxIntegration::addWindow(m_window, window);
138 // Qt never calls these setters after creating the window, so we need to do that ourselves here
139 setWindowState(window->windowState());
140 if (window->parent())
141 setParent(window->parent()->handle());
142 setGeometryHelper(window->geometry());
143 setVisible(window->isVisible());
146 QQnxWindow::~QQnxWindow()
148 qWindowDebug() << Q_FUNC_INFO << "window =" << window();
149 // Remove from plugin's window mapper
150 QQnxIntegration::removeWindow(m_window);
152 // Remove from parent's Hierarchy.
154 m_screen->updateHierarchy();
156 // We shouldn't allow this case unless QT allows it. Does it? Or should we send the
157 // handleCloseEvent on all children when this window is deleted?
158 if (m_childWindows.size() > 0)
159 qFatal("QQnxWindow: window destroyed before children!");
161 // Cleanup QNX window and its buffers
162 screen_destroy_window(m_window);
165 void QQnxWindow::setGeometry(const QRect &rect)
167 const QRect oldGeometry = setGeometryHelper(rect);
169 // If this is an OpenGL window we need to request that the GL context updates
170 // the EGLsurface on which it is rendering. The surface will be recreated the
171 // next time QQnxGLContext::makeCurrent() is called.
173 // We want the setting of the atomic bool in the GL context to be atomic with
174 // setting m_requestedBufferSize and therefore extended the scope to include
176 const QMutexLocker locker(&m_mutex);
177 m_requestedBufferSize = rect.size();
178 if (m_platformOpenGLContext != 0 && bufferSize() != rect.size())
179 m_platformOpenGLContext->requestSurfaceChange();
182 // Send a geometry change event to Qt (triggers resizeEvent() in QWindow/QWidget)
183 QWindowSystemInterface::handleSynchronousGeometryChange(window(), rect);
185 // Now move all children.
186 if (!oldGeometry.isEmpty()) {
187 const QPoint offset = rect.topLeft() - oldGeometry.topLeft();
188 Q_FOREACH (QQnxWindow *childWindow, m_childWindows)
189 childWindow->setOffset(offset);
193 QRect QQnxWindow::setGeometryHelper(const QRect &rect)
195 qWindowDebug() << Q_FUNC_INFO << "window =" << window()
196 << ", (" << rect.x() << "," << rect.y()
197 << "," << rect.width() << "," << rect.height() << ")";
199 // Call base class method
200 QRect oldGeometry = QPlatformWindow::geometry();
201 QPlatformWindow::setGeometry(rect);
203 // Set window geometry equal to widget geometry
208 int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_POSITION, val);
210 qFatal("QQnxWindow: failed to set window position, errno=%d", errno);
214 val[0] = rect.width();
215 val[1] = rect.height();
216 result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SIZE, val);
218 qFatal("QQnxWindow: failed to set window size, errno=%d", errno);
221 // Set viewport size equal to window size
223 result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SOURCE_SIZE, val);
225 qFatal("QQnxWindow: failed to set window source size, errno=%d", errno);
231 void QQnxWindow::setOffset(const QPoint &offset)
233 qWindowDebug() << Q_FUNC_INFO << "window =" << window();
234 // Move self and then children.
235 QRect newGeometry = geometry();
236 newGeometry.translate(offset);
238 // Call the base class
239 QPlatformWindow::setGeometry(newGeometry);
244 val[0] = newGeometry.x();
245 val[1] = newGeometry.y();
246 int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_POSITION, val);
248 qFatal("QQnxWindow: failed to set window position, errno=%d", errno);
251 Q_FOREACH (QQnxWindow *childWindow, m_childWindows)
252 childWindow->setOffset(offset);
255 void QQnxWindow::setVisible(bool visible)
257 qWindowDebug() << Q_FUNC_INFO << "window =" << window() << "visible =" << visible;
261 QQnxWindow *root = this;
262 while (root->m_parentWindow)
263 root = root->m_parentWindow;
265 root->updateVisibility(root->m_visible);
267 window()->requestActivateWindow();
269 if (window()->isTopLevel() && visible)
270 QWindowSystemInterface::handleExposeEvent(window(), window()->geometry());
273 void QQnxWindow::updateVisibility(bool parentVisible)
275 qWindowDebug() << Q_FUNC_INFO << "parentVisible =" << parentVisible << "window =" << window();
276 // Set window visibility
278 int val = (m_visible && parentVisible) ? 1 : 0;
279 int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_VISIBLE, &val);
281 qFatal("QQnxWindow: failed to set window visibility, errno=%d", errno);
284 Q_FOREACH (QQnxWindow *childWindow, m_childWindows)
285 childWindow->updateVisibility(m_visible && parentVisible);
288 void QQnxWindow::setOpacity(qreal level)
290 qWindowDebug() << Q_FUNC_INFO << "window =" << window() << "opacity =" << level;
291 // Set window global alpha
293 int val = (int)(level * 255);
294 int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_GLOBAL_ALPHA, &val);
296 qFatal("QQnxWindow: failed to set window global alpha, errno=%d", errno);
299 // TODO: How to handle children of this window? If we change all the visibilities, then
300 // the transparency will look wrong...
303 bool QQnxWindow::isExposed() const
308 QSize QQnxWindow::requestedBufferSize() const
310 const QMutexLocker locker(&m_mutex);
311 return m_requestedBufferSize;
314 void QQnxWindow::adjustBufferSize()
316 const QSize windowSize = window()->size();
317 if (windowSize != bufferSize())
318 setBufferSize(windowSize);
321 void QQnxWindow::setBufferSize(const QSize &size)
323 qWindowDebug() << Q_FUNC_INFO << "window =" << window() << "size =" << size;
325 // Set window buffer size
327 int val[2] = { size.width(), size.height() };
328 int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_BUFFER_SIZE, val);
330 qFatal("QQnxWindow: failed to set window buffer size, errno=%d", errno);
333 // Create window buffers if they do not exist
334 if (m_bufferSize.isEmpty()) {
336 // Get pixel format from EGL config if using OpenGL;
337 // otherwise inherit pixel format of window's screen
338 if (m_platformOpenGLContext != 0) {
339 val[0] = platformWindowFormatToNativeFormat(m_platformOpenGLContext->format());
341 val[0] = m_screen->nativeFormat();
346 result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_FORMAT, val);
348 qFatal("QQnxWindow: failed to set window pixel format, errno=%d", errno);
352 result = screen_create_window_buffers(m_window, MAX_BUFFER_COUNT);
354 qFatal("QQnxWindow: failed to create window buffers, errno=%d", errno);
358 // Cache new buffer size
361 // Buffers were destroyed; reacquire them
362 m_currentBufferIndex = -1;
363 m_previousDirty = QRegion();
364 m_scrolled = QRegion();
366 const QMutexLocker locker(&m_mutex);
367 m_requestedBufferSize = QSize();
370 QQnxBuffer &QQnxWindow::renderBuffer()
372 qWindowDebug() << Q_FUNC_INFO << "window =" << window();
374 // Check if render buffer is invalid
375 if (m_currentBufferIndex == -1) {
376 // Get all buffers available for rendering
378 screen_buffer_t buffers[MAX_BUFFER_COUNT];
379 int result = screen_get_window_property_pv(m_window, SCREEN_PROPERTY_RENDER_BUFFERS, (void **)buffers);
381 qFatal("QQnxWindow: failed to query window buffers, errno=%d", errno);
385 for (int i = 0; i < MAX_BUFFER_COUNT; ++i) {
386 m_buffers[i] = QQnxBuffer(buffers[i]);
389 // Use the first available render buffer
390 m_currentBufferIndex = 0;
391 m_previousBufferIndex = -1;
394 return m_buffers[m_currentBufferIndex];
397 void QQnxWindow::scroll(const QRegion ®ion, int dx, int dy, bool flush)
399 qWindowDebug() << Q_FUNC_INFO << "window =" << window();
400 blitPreviousToCurrent(region, dx, dy, flush);
401 m_scrolled += region;
404 void QQnxWindow::post(const QRegion &dirty)
406 // How double-buffering works
407 // --------------------------
409 // The are two buffers, the previous one and the current one.
410 // The previous buffer always contains the complete, full image of the whole window when it
412 // The current buffer starts with the complete, full image of the second to last posting
415 // During painting, Qt paints on the current buffer. Thus, when Qt has finished painting, the
416 // current buffer contains the second to last image plus the newly painted regions.
417 // Since the second to last image is too old, we copy over the image from the previous buffer, but
418 // only for those regions that Qt didn't paint (because that would overwrite what Qt has just
419 // painted). This is the copyPreviousToCurrent() call below.
421 // After the call to copyPreviousToCurrent(), the current buffer contains the complete, full image of the
422 // whole window in its current state, and we call screen_post_window() to make the new buffer
423 // available to libscreen (called "posting"). There, only the regions that Qt painted on are
424 // posted, as nothing else has changed.
426 // After that, the previous and the current buffers are swapped, and the whole cycle starts anew.
428 // Check if render buffer exists and something was rendered
429 if (m_currentBufferIndex != -1 && !dirty.isEmpty()) {
430 qWindowDebug() << Q_FUNC_INFO << "window =" << window();
431 QQnxBuffer ¤tBuffer = m_buffers[m_currentBufferIndex];
433 // Copy unmodified region from old render buffer to new render buffer;
434 // required to allow partial updates
435 QRegion preserve = m_previousDirty - dirty - m_scrolled;
436 blitPreviousToCurrent(preserve, 0, 0);
438 // Calculate region that changed
439 QRegion modified = preserve + dirty + m_scrolled;
440 QRect rect = modified.boundingRect();
441 int dirtyRect[4] = { rect.x(), rect.y(), rect.x() + rect.width(), rect.y() + rect.height() };
443 // Update the display with contents of render buffer
445 int result = screen_post_window(m_window, currentBuffer.nativeBuffer(), 1, dirtyRect, 0);
447 qFatal("QQnxWindow: failed to post window buffer, errno=%d", errno);
450 // Advance to next nender buffer
451 m_previousBufferIndex = m_currentBufferIndex++;
452 if (m_currentBufferIndex >= MAX_BUFFER_COUNT) {
453 m_currentBufferIndex = 0;
456 // Save modified region and clear scrolled region
457 m_previousDirty = dirty;
458 m_scrolled = QRegion();
460 // Notify screen that window posted
462 m_screen->onWindowPost(this);
467 void QQnxWindow::setScreen(QQnxScreen *platformScreen)
469 qWindowDebug() << Q_FUNC_INFO << "window =" << window() << "platformScreen =" << platformScreen;
471 if (m_screen == platformScreen)
475 m_screen->removeWindow(this);
476 platformScreen->addWindow(this);
477 m_screen = platformScreen;
479 // Move window to proper screen/display
481 screen_display_t display = platformScreen->nativeDisplay();
482 int result = screen_set_window_property_pv(m_window, SCREEN_PROPERTY_DISPLAY, (void **)&display);
484 qFatal("QQnxWindow: failed to set window display, errno=%d", errno);
487 // Add window to display's window group
489 result = screen_join_window_group(m_window, platformScreen->windowGroupName());
491 qFatal("QQnxWindow: failed to join window group, errno=%d", errno);
494 Q_FOREACH (QQnxWindow *childWindow, m_childWindows) {
495 // Only subwindows and tooltips need necessarily be moved to another display with the window.
496 if ((window()->windowType() & Qt::WindowType_Mask) == Qt::SubWindow ||
497 (window()->windowType() & Qt::WindowType_Mask) == Qt::ToolTip)
498 childWindow->setScreen(platformScreen);
501 m_screen->updateHierarchy();
504 void QQnxWindow::removeFromParent()
506 qWindowDebug() << Q_FUNC_INFO << "window =" << window();
507 // Remove from old Hierarchy position
508 if (m_parentWindow) {
509 if (m_parentWindow->m_childWindows.removeAll(this))
512 qFatal("QQnxWindow: Window Hierarchy broken; window has parent, but parent hasn't got child.");
514 m_screen->removeWindow(this);
518 void QQnxWindow::setParent(const QPlatformWindow *window)
520 qWindowDebug() << Q_FUNC_INFO << "window =" << this->window() << "platformWindow =" << window;
521 // Cast away the const, we need to modify the hierarchy.
522 QQnxWindow *newParent = 0;
525 newParent = static_cast<QQnxWindow*>((QPlatformWindow *)window);
527 if (newParent == m_parentWindow)
531 m_parentWindow = newParent;
533 // Add to new hierarchy position.
534 if (m_parentWindow) {
535 if (m_parentWindow->m_screen != m_screen)
536 setScreen(m_parentWindow->m_screen);
538 m_parentWindow->m_childWindows.push_back(this);
540 m_screen->addWindow(this);
543 m_screen->updateHierarchy();
546 void QQnxWindow::raise()
548 qWindowDebug() << Q_FUNC_INFO << "window =" << window();
550 QQnxWindow *oldParent = m_parentWindow;
553 oldParent->m_childWindows.push_back(this);
555 m_screen->raiseWindow(this);
558 m_screen->updateHierarchy();
561 void QQnxWindow::lower()
563 qWindowDebug() << Q_FUNC_INFO << "window =" << window();
565 QQnxWindow *oldParent = m_parentWindow;
568 oldParent->m_childWindows.push_front(this);
570 m_screen->lowerWindow(this);
573 m_screen->updateHierarchy();
576 void QQnxWindow::requestActivateWindow()
578 qWindowDebug() << Q_FUNC_INFO << "window =" << window();
580 // TODO: Tell screen to set keyboard focus to this window.
582 // Notify that we gained focus.
587 Qt::WindowState QQnxWindow::setWindowState(Qt::WindowState state)
589 qWindowDebug() << Q_FUNC_INFO << "state =" << state;
591 // Prevent two calls with Qt::WindowFullScreen from changing m_unmaximizedGeometry
592 if (m_windowState == state)
597 // WindowMinimized is not supported - navigator does not have an API to minimize a window
598 // WindowActive is not an accepted parameter according to the docs
599 case Qt::WindowMinimized:
600 case Qt::WindowActive:
601 return m_windowState;
603 case Qt::WindowMaximized:
604 case Qt::WindowFullScreen:
605 m_unmaximizedGeometry = geometry();
606 setGeometry(state == Qt::WindowMaximized ? m_screen->availableGeometry() : m_screen->geometry());
609 case Qt::WindowNoState:
610 if (m_unmaximizedGeometry.isValid())
611 setGeometry(m_unmaximizedGeometry);
615 m_windowState = state;
619 void QQnxWindow::gainedFocus()
621 qWindowDebug() << Q_FUNC_INFO << "window =" << window();
624 QWindowSystemInterface::handleWindowActivated(window());
628 void QQnxWindow::setPlatformOpenGLContext(QQnxGLContext *platformOpenGLContext)
630 // This function does not take ownership of the platform gl context.
631 // It is owned by the frontend QOpenGLContext
632 m_platformOpenGLContext = platformOpenGLContext;
636 QQnxWindow *QQnxWindow::findWindow(screen_window_t windowHandle)
638 if (m_window == windowHandle)
641 Q_FOREACH (QQnxWindow *window, m_childWindows) {
642 QQnxWindow * const result = window->findWindow(windowHandle);
650 void QQnxWindow::blitFrom(QQnxWindow *sourceWindow, const QPoint &sourceOffset, const QRegion &targetRegion)
652 if (!sourceWindow || sourceWindow->m_previousBufferIndex == -1 || targetRegion.isEmpty())
655 qWindowDebug() << Q_FUNC_INFO << window() << sourceWindow->window() << sourceOffset << targetRegion;
657 QQnxBuffer &sourceBuffer = sourceWindow->m_buffers[sourceWindow->m_previousBufferIndex];
658 QQnxBuffer &targetBuffer = renderBuffer();
660 blitHelper(sourceBuffer, targetBuffer, sourceOffset, QPoint(0, 0), targetRegion, true);
663 void QQnxWindow::updateZorder(int &topZorder)
666 int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_ZORDER, &topZorder);
670 qFatal("QQnxWindow: failed to set window z-order=%d, errno=%d, mWindow=%p", topZorder, errno, m_window);
672 Q_FOREACH (QQnxWindow *childWindow, m_childWindows)
673 childWindow->updateZorder(topZorder);
676 void QQnxWindow::blitHelper(QQnxBuffer &source, QQnxBuffer &target, const QPoint &sourceOffset,
677 const QPoint &targetOffset, const QRegion ®ion, bool flush)
679 // Break down region into non-overlapping rectangles
680 const QVector<QRect> rects = region.rects();
681 for (int i = rects.size() - 1; i >= 0; i--) {
682 // Clip rectangle to bounds of target
683 const QRect rect = rects[i].intersected(target.rect());
688 // Setup blit operation
689 int attribs[] = { SCREEN_BLIT_SOURCE_X, rect.x() + sourceOffset.x(),
690 SCREEN_BLIT_SOURCE_Y, rect.y() + sourceOffset.y(),
691 SCREEN_BLIT_SOURCE_WIDTH, rect.width(),
692 SCREEN_BLIT_SOURCE_HEIGHT, rect.height(),
693 SCREEN_BLIT_DESTINATION_X, rect.x() + targetOffset.x(),
694 SCREEN_BLIT_DESTINATION_Y, rect.y() + targetOffset.y(),
695 SCREEN_BLIT_DESTINATION_WIDTH, rect.width(),
696 SCREEN_BLIT_DESTINATION_HEIGHT, rect.height(),
699 // Queue blit operation
701 const int result = screen_blit(m_screenContext, target.nativeBuffer(),
702 source.nativeBuffer(), attribs);
704 qFatal("QQnxWindow: failed to blit buffers, errno=%d", errno);
707 // Check if flush requested
709 // Wait for all blits to complete
711 const int result = screen_flush_blits(m_screenContext, SCREEN_WAIT_IDLE);
713 qFatal("QQnxWindow: failed to flush blits, errno=%d", errno);
715 // Buffer was modified outside the CPU
716 target.invalidateInCache();
720 void QQnxWindow::blitPreviousToCurrent(const QRegion ®ion, int dx, int dy, bool flush)
722 qWindowDebug() << Q_FUNC_INFO << "window =" << window();
724 // Abort if previous buffer is invalid or if nothing to copy
725 if (m_previousBufferIndex == -1 || region.isEmpty())
728 QQnxBuffer ¤tBuffer = m_buffers[m_currentBufferIndex];
729 QQnxBuffer &previousBuffer = m_buffers[m_previousBufferIndex];
731 blitHelper(previousBuffer, currentBuffer, QPoint(0, 0), QPoint(dx, dy), region, flush);
734 int QQnxWindow::platformWindowFormatToNativeFormat(const QSurfaceFormat &format)
736 qWindowDebug() << Q_FUNC_INFO;
737 // Extract size of colour channels from window format
738 int redSize = format.redBufferSize();
740 qFatal("QQnxWindow: red size not defined");
743 int greenSize = format.greenBufferSize();
744 if (greenSize == -1) {
745 qFatal("QQnxWindow: green size not defined");
748 int blueSize = format.blueBufferSize();
749 if (blueSize == -1) {
750 qFatal("QQnxWindow: blue size not defined");
753 // select matching native format
754 if (redSize == 5 && greenSize == 6 && blueSize == 5) {
755 return SCREEN_FORMAT_RGB565;
756 } else if (redSize == 8 && greenSize == 8 && blueSize == 8) {
757 return SCREEN_FORMAT_RGBA8888;
759 qFatal("QQnxWindow: unsupported pixel format");