text: /***************************************************************************
  **
  ** Copyright (C) 2013 - 2014 BlackBerry Limited. All rights reserved.
  ** Contact: http://www.qt.io/licensing/
  **
  ** This file is part of the plugins of the Qt Toolkit.
  **
  ** $QT_BEGIN_LICENSE:LGPL21$
  ** 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 http://www.qt.io/terms-conditions. For further
  ** information use the contact form at http://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 2.1 or version 3 as published by the Free
  ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
  ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
  ** following information to ensure the GNU Lesser General Public License
  ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
  ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
  **
  ** As a special exception, The Qt Company gives you certain additional
  ** rights. These rights are described in The Qt Company LGPL Exception
  ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
  **
  ** $QT_END_LICENSE$
  **
  ****************************************************************************/
  
  #include "qqnxglobal.h"
  
  #include "qqnxrasterwindow.h"
  #include "qqnxscreen.h"
  
  #include <QDebug>
  
  #include <errno.h>
  
  #if defined(QQNXRASTERWINDOW_DEBUG)
  #define qRasterWindowDebug qDebug
  #else
  #define qRasterWindowDebug QT_NO_QDEBUG_MACRO
  #endif
  
  QT_BEGIN_NAMESPACE
  
  QQnxRasterWindow::QQnxRasterWindow(QWindow *window, screen_context_t context, bool needRootWindow) :
      QQnxWindow(window, context, needRootWindow),
      m_currentBufferIndex(-1),
      m_previousBufferIndex(-1)
  {
      initWindow();
  
      // Set window usage
      if (window->type() == Qt::Desktop)
          return;
  
      const int val = SCREEN_USAGE_NATIVE | SCREEN_USAGE_READ | SCREEN_USAGE_WRITE;
      const int result = screen_set_window_property_iv(nativeHandle(), SCREEN_PROPERTY_USAGE, &val);
      if (result != 0)
          qFatal("QQnxRasterWindow: failed to set window alpha usage, errno=%d", errno);
  }
  
  void QQnxRasterWindow::post(const QRegion &dirty)
  {
      // How double-buffering works
      // --------------------------
      //
      // The are two buffers, the previous one and the current one.
      // The previous buffer always contains the complete, full image of the whole window when it
      // was last posted.
      // The current buffer starts with the complete, full image of the second to last posting
      // of the window.
      //
      // During painting, Qt paints on the current buffer. Thus, when Qt has finished painting, the
      // current buffer contains the second to last image plus the newly painted regions.
      // Since the second to last image is too old, we copy over the image from the previous buffer, but
      // only for those regions that Qt didn't paint (because that would overwrite what Qt has just
      // painted). This is the copyPreviousToCurrent() call below.
      //
      // After the call to copyPreviousToCurrent(), the current buffer contains the complete, full image of the
      // whole window in its current state, and we call screen_post_window() to make the new buffer
      // available to libscreen (called "posting"). There, only the regions that Qt painted on are
      // posted, as nothing else has changed.
      //
      // After that, the previous and the current buffers are swapped, and the whole cycle starts anew.
  
      // Check if render buffer exists and something was rendered
      if (m_currentBufferIndex != -1 && !dirty.isEmpty()) {
          qRasterWindowDebug() << "window =" << window();
          QQnxBuffer &currentBuffer = 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;
          blitPreviousToCurrent(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
          Q_SCREEN_CHECKERROR(
                  screen_post_window(nativeHandle(), currentBuffer.nativeBuffer(), 1, dirtyRect, 0),
                  "Failed to post window");
  
          // 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();
  
          windowPosted();
      }
  }
  
  void QQnxRasterWindow::scroll(const QRegion &region, int dx, int dy, bool flush)
  {
      qRasterWindowDebug() << "window =" << window();
      blitPreviousToCurrent(region, dx, dy, flush);
      m_scrolled += region;
  }
  
  QQnxBuffer &QQnxRasterWindow::renderBuffer()
  {
      qRasterWindowDebug() << "window =" << window();
  
      // Check if render buffer is invalid
      if (m_currentBufferIndex == -1) {
          // Get all buffers available for rendering
          screen_buffer_t buffers[MAX_BUFFER_COUNT];
          const int result = screen_get_window_property_pv(nativeHandle(), SCREEN_PROPERTY_RENDER_BUFFERS,
                                                     (void **)buffers);
          Q_SCREEN_CRITICALERROR(result, "Failed to query window buffers");
  
          // Wrap each buffer and clear
          for (int i = 0; i < MAX_BUFFER_COUNT; ++i) {
              m_buffers[i] = QQnxBuffer(buffers[i]);
  
              // Clear Buffer
              int bg[] = { SCREEN_BLIT_COLOR, 0x00000000, SCREEN_BLIT_END };
              Q_SCREEN_CHECKERROR(screen_fill(screen()->nativeContext(), buffers[i], bg),
                                  "Failed to clear window buffer");
          }
  
          Q_SCREEN_CHECKERROR(screen_flush_blits(screen()->nativeContext(), 0),
                              "Failed to flush blits");
  
          // Use the first available render buffer
          m_currentBufferIndex = 0;
          m_previousBufferIndex = -1;
      }
  
      return m_buffers[m_currentBufferIndex];
  }
  
  void QQnxRasterWindow::setParent(const QPlatformWindow *wnd)
  {
      QQnxWindow::setParent(wnd);
      adjustBufferSize();
  }
  
  void QQnxRasterWindow::adjustBufferSize()
  {
      // When having a raster window we don't need any buffers, since
      // Qt will draw to the parent TLW backing store.
      const QSize windowSize = window()->parent() ? QSize(1,1) : window()->size();
      if (windowSize != bufferSize())
          setBufferSize(windowSize);
  }
  
  int QQnxRasterWindow::pixelFormat() const
  {
      return screen()->nativeFormat();
  }
  
  void QQnxRasterWindow::resetBuffers()
  {
      // Buffers were destroyed; reacquire them
      m_currentBufferIndex = -1;
      m_previousDirty = QRegion();
      m_scrolled = QRegion();
  }
  
  void QQnxRasterWindow::blitPreviousToCurrent(const QRegion &region, int dx, int dy, bool flush)
  {
      qRasterWindowDebug() << "window =" << window();
  
      // Abort if previous buffer is invalid or if nothing to copy
      if (m_previousBufferIndex == -1 || region.isEmpty())
          return;
  
      QQnxBuffer &currentBuffer = m_buffers[m_currentBufferIndex];
      QQnxBuffer &previousBuffer = m_buffers[m_previousBufferIndex];
  
      // Break down region into non-overlapping rectangles
      const QVector<QRect> rects = region.rects();
      for (int i = rects.size() - 1; i >= 0; i--) {
          // Clip rectangle to bounds of target
          const 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
          Q_SCREEN_CHECKERROR(screen_blit(m_screenContext, currentBuffer.nativeBuffer(),
                                         previousBuffer.nativeBuffer(), attribs),
                              "Failed to blit buffers");
      }
  
      // Check if flush requested
      if (flush) {
          // Wait for all blits to complete
          Q_SCREEN_CHECKERROR(screen_flush_blits(m_screenContext, SCREEN_WAIT_IDLE),
                              "Failed to flush blits");
  
          // Buffer was modified outside the CPU
          currentBuffer.invalidateInCache();
      }
  }
  
  QT_END_NAMESPACE
  