1 /***************************************************************************
2 **
3 ** Copyright (C) 2013 - 2014 BlackBerry Limited. All rights reserved.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qqnxglobal.h"
41 
42 #include "qqnxrasterwindow.h"
43 #include "qqnxscreen.h"
44 
45 #include <QDebug>
46 
47 #include <errno.h>
48 
49 #if defined(QQNXRASTERWINDOW_DEBUG)
50 #define qRasterWindowDebug qDebug
51 #else
52 #define qRasterWindowDebug QT_NO_QDEBUG_MACRO
53 #endif
54 
55 QT_BEGIN_NAMESPACE
56 
QQnxRasterWindow(QWindow * window,screen_context_t context,bool needRootWindow)57 QQnxRasterWindow::QQnxRasterWindow(QWindow *window, screen_context_t context, bool needRootWindow) :
58     QQnxWindow(window, context, needRootWindow),
59     m_currentBufferIndex(-1),
60     m_previousBufferIndex(-1)
61 {
62     initWindow();
63 
64     // Set window usage
65     if (window->type() == Qt::Desktop)
66         return;
67 
68     const int val = SCREEN_USAGE_NATIVE | SCREEN_USAGE_READ | SCREEN_USAGE_WRITE;
69     const int result = screen_set_window_property_iv(nativeHandle(), SCREEN_PROPERTY_USAGE, &val);
70     if (Q_UNLIKELY(result != 0))
71         qFatal("QQnxRasterWindow: failed to set window alpha usage, errno=%d", errno);
72 }
73 
post(const QRegion & dirty)74 void QQnxRasterWindow::post(const QRegion &dirty)
75 {
76     // How double-buffering works
77     // --------------------------
78     //
79     // The are two buffers, the previous one and the current one.
80     // The previous buffer always contains the complete, full image of the whole window when it
81     // was last posted.
82     // The current buffer starts with the complete, full image of the second to last posting
83     // of the window.
84     //
85     // During painting, Qt paints on the current buffer. Thus, when Qt has finished painting, the
86     // current buffer contains the second to last image plus the newly painted regions.
87     // Since the second to last image is too old, we copy over the image from the previous buffer, but
88     // only for those regions that Qt didn't paint (because that would overwrite what Qt has just
89     // painted). This is the copyPreviousToCurrent() call below.
90     //
91     // After the call to copyPreviousToCurrent(), the current buffer contains the complete, full image of the
92     // whole window in its current state, and we call screen_post_window() to make the new buffer
93     // available to libscreen (called "posting"). There, only the regions that Qt painted on are
94     // posted, as nothing else has changed.
95     //
96     // After that, the previous and the current buffers are swapped, and the whole cycle starts anew.
97 
98     // Check if render buffer exists and something was rendered
99     if (m_currentBufferIndex != -1 && !dirty.isEmpty()) {
100         qRasterWindowDebug() << "window =" << window();
101         QQnxBuffer &currentBuffer = m_buffers[m_currentBufferIndex];
102 
103         // Copy unmodified region from old render buffer to new render buffer;
104         // required to allow partial updates
105         QRegion preserve = m_previousDirty - dirty - m_scrolled;
106         blitPreviousToCurrent(preserve, 0, 0);
107 
108         // Calculate region that changed
109         QRegion modified = preserve + dirty + m_scrolled;
110         QRect rect = modified.boundingRect();
111         int dirtyRect[4] = { rect.x(), rect.y(), rect.x() + rect.width(), rect.y() + rect.height() };
112 
113         // Update the display with contents of render buffer
114         Q_SCREEN_CHECKERROR(
115                 screen_post_window(nativeHandle(), currentBuffer.nativeBuffer(), 1, dirtyRect, 0),
116                 "Failed to post window");
117 
118         // Advance to next nender buffer
119         m_previousBufferIndex = m_currentBufferIndex++;
120         if (m_currentBufferIndex >= MAX_BUFFER_COUNT)
121             m_currentBufferIndex = 0;
122 
123         // Save modified region and clear scrolled region
124         m_previousDirty = dirty;
125         m_scrolled = QRegion();
126 
127         windowPosted();
128     }
129 }
130 
scroll(const QRegion & region,int dx,int dy,bool flush)131 void QQnxRasterWindow::scroll(const QRegion &region, int dx, int dy, bool flush)
132 {
133     qRasterWindowDebug() << "window =" << window();
134     blitPreviousToCurrent(region, dx, dy, flush);
135     m_scrolled += region;
136 }
137 
renderBuffer()138 QQnxBuffer &QQnxRasterWindow::renderBuffer()
139 {
140     qRasterWindowDebug() << "window =" << window();
141 
142     // Check if render buffer is invalid
143     if (m_currentBufferIndex == -1) {
144         auto platformScreen = static_cast<QQnxScreen *>(screen());
145         // Get all buffers available for rendering
146         screen_buffer_t buffers[MAX_BUFFER_COUNT];
147         const int result = screen_get_window_property_pv(nativeHandle(), SCREEN_PROPERTY_RENDER_BUFFERS,
148                                                    (void **)buffers);
149         Q_SCREEN_CRITICALERROR(result, "Failed to query window buffers");
150 
151         // Wrap each buffer and clear
152         for (int i = 0; i < MAX_BUFFER_COUNT; ++i) {
153             m_buffers[i] = QQnxBuffer(buffers[i]);
154 
155             // Clear Buffer
156             int bg[] = { SCREEN_BLIT_COLOR, 0x00000000, SCREEN_BLIT_END };
157             Q_SCREEN_CHECKERROR(screen_fill(platformScreen->nativeContext(), buffers[i], bg),
158                                 "Failed to clear window buffer");
159         }
160 
161         Q_SCREEN_CHECKERROR(screen_flush_blits(platformScreen->nativeContext(), SCREEN_WAIT_IDLE),
162                             "Failed to flush blits");
163 
164         // Use the first available render buffer
165         m_currentBufferIndex = 0;
166         m_previousBufferIndex = -1;
167     }
168 
169     return m_buffers[m_currentBufferIndex];
170 }
171 
setParent(const QPlatformWindow * wnd)172 void QQnxRasterWindow::setParent(const QPlatformWindow *wnd)
173 {
174     QQnxWindow::setParent(wnd);
175     adjustBufferSize();
176 }
177 
adjustBufferSize()178 void QQnxRasterWindow::adjustBufferSize()
179 {
180     // When having a raster window we don't need any buffers, since
181     // Qt will draw to the parent TLW backing store.
182     const QSize windowSize = window()->parent() ? QSize(0,0) : window()->size();
183     if (windowSize != bufferSize())
184         setBufferSize(windowSize);
185 }
186 
pixelFormat() const187 int QQnxRasterWindow::pixelFormat() const
188 {
189     return static_cast<QQnxScreen *>(screen())->nativeFormat();
190 }
191 
resetBuffers()192 void QQnxRasterWindow::resetBuffers()
193 {
194     // Buffers were destroyed; reacquire them
195     m_currentBufferIndex = -1;
196     m_previousDirty = QRegion();
197     m_scrolled = QRegion();
198     if (window()->parent() && bufferSize() == QSize(1,1)) {
199         // If we have a parent then we're not really rendering.  But if we don't render we'll
200         // be invisible and any children won't show up.  This should be harmless since we're
201         // rendering into a 1x1 window that has transparency set to discard.
202         renderBuffer();
203         post(QRegion(0,0,1,1));
204     }
205 }
206 
blitPreviousToCurrent(const QRegion & region,int dx,int dy,bool flush)207 void QQnxRasterWindow::blitPreviousToCurrent(const QRegion &region, int dx, int dy, bool flush)
208 {
209     qRasterWindowDebug() << "window =" << window();
210 
211     // Abort if previous buffer is invalid or if nothing to copy
212     if (m_previousBufferIndex == -1 || region.isEmpty())
213         return;
214 
215     QQnxBuffer &currentBuffer = m_buffers[m_currentBufferIndex];
216     QQnxBuffer &previousBuffer = m_buffers[m_previousBufferIndex];
217 
218     // Break down region into non-overlapping rectangles
219     for (auto rit = region.rbegin(), rend = region.rend(); rit != rend; ++rit) {
220         // Clip rectangle to bounds of target
221         const QRect rect = rit->intersected(currentBuffer.rect());
222 
223         if (rect.isEmpty())
224             continue;
225 
226         // Setup blit operation
227         int attribs[] = { SCREEN_BLIT_SOURCE_X, rect.x(),
228                           SCREEN_BLIT_SOURCE_Y, rect.y(),
229                           SCREEN_BLIT_SOURCE_WIDTH, rect.width(),
230                           SCREEN_BLIT_SOURCE_HEIGHT, rect.height(),
231                           SCREEN_BLIT_DESTINATION_X, rect.x() + dx,
232                           SCREEN_BLIT_DESTINATION_Y, rect.y() + dy,
233                           SCREEN_BLIT_DESTINATION_WIDTH, rect.width(),
234                           SCREEN_BLIT_DESTINATION_HEIGHT, rect.height(),
235                           SCREEN_BLIT_END };
236 
237         // Queue blit operation
238         Q_SCREEN_CHECKERROR(screen_blit(m_screenContext, currentBuffer.nativeBuffer(),
239                                        previousBuffer.nativeBuffer(), attribs),
240                             "Failed to blit buffers");
241     }
242 
243     // Check if flush requested
244     if (flush) {
245         // Wait for all blits to complete
246         Q_SCREEN_CHECKERROR(screen_flush_blits(m_screenContext, SCREEN_WAIT_IDLE),
247                             "Failed to flush blits");
248 
249         // Buffer was modified outside the CPU
250         currentBuffer.invalidateInCache();
251     }
252 }
253 
254 QT_END_NAMESPACE
255