Avoid flicker when switching back to composited mode
[profile/ivi/qtwayland.git] / src / compositor / compositor_api / waylandsurfaceitem.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the Qt Compositor.
7 **
8 ** $QT_BEGIN_LICENSE:BSD$
9 ** You may use this file under the terms of the BSD license as follows:
10 **
11 ** "Redistribution and use in source and binary forms, with or without
12 ** modification, are permitted provided that the following conditions are
13 ** met:
14 **   * Redistributions of source code must retain the above copyright
15 **     notice, this list of conditions and the following disclaimer.
16 **   * Redistributions in binary form must reproduce the above copyright
17 **     notice, this list of conditions and the following disclaimer in
18 **     the documentation and/or other materials provided with the
19 **     distribution.
20 **   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
21 **     the names of its contributors may be used to endorse or promote
22 **     products derived from this software without specific prior written
23 **     permission.
24 **
25 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
36 **
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40
41 #include "waylandsurfaceitem.h"
42 #include "waylandsurface.h"
43 #include "waylandcompositor.h"
44 #include "waylandinput.h"
45
46 #include "wlsurface.h"
47 #include "wlextendedsurface.h"
48
49 #include <QtGui/QKeyEvent>
50 #include <QtGui/QGuiApplication>
51 #include <QtGui/QScreen>
52
53 #include <QtQuick/QSGSimpleTextureNode>
54 #include <QtQuick/QSGSimpleRectNode>
55 #include <QtQuick/QQuickCanvas>
56
57 class WaylandSurfaceTextureProvider : public QSGTextureProvider
58 {
59 public:
60     WaylandSurfaceTextureProvider() : t(0) { }
61
62     QSGTexture *texture() const {
63         if (t)
64             t->setFiltering(smooth ? QSGTexture::Linear : QSGTexture::Nearest);
65         return t;
66     }
67
68     QSGTexture *t;
69     bool smooth;
70 };
71
72 WaylandSurfaceItem::WaylandSurfaceItem(QQuickItem *parent)
73     : QQuickItem(parent)
74     , m_surface(0)
75     , m_texture(0)
76     , m_provider(0)
77     , m_paintEnabled(true)
78     , m_useTextureAlpha(false)
79     , m_clientRenderingEnabled(false)
80     , m_touchEventsEnabled(false)
81 {
82 }
83
84 WaylandSurfaceItem::WaylandSurfaceItem(WaylandSurface *surface, QQuickItem *parent)
85     : QQuickItem(parent)
86     , m_surface(0)
87     , m_texture(0)
88     , m_provider(0)
89     , m_paintEnabled(true)
90     , m_useTextureAlpha(false)
91     , m_clientRenderingEnabled(false)
92     , m_touchEventsEnabled(false)
93 {
94     init(surface);
95 }
96
97 void WaylandSurfaceItem::init(WaylandSurface *surface)
98 {
99     if (!surface)
100         return;
101
102     if (m_surface) {
103         m_surface->setSurfaceItem(0);
104     }
105
106     m_surface = surface;
107     m_surface->setSurfaceItem(this);
108     if (m_clientRenderingEnabled) {
109         m_surface->sendOnScreenVisibilityChange(m_clientRenderingEnabled);
110     }
111
112     setWidth(surface->size().width());
113     setHeight(surface->size().height());
114
115     setSmooth(true);
116     setFlag(ItemHasContents);
117     setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton);
118     setAcceptHoverEvents(true);
119     connect(surface, SIGNAL(mapped()), this, SLOT(surfaceMapped()));
120     connect(surface, SIGNAL(unmapped()), this, SLOT(surfaceUnmapped()));
121     connect(surface, SIGNAL(destroyed(QObject *)), this, SLOT(surfaceDestroyed(QObject *)));
122     connect(surface, SIGNAL(damaged(const QRect &)), this, SLOT(surfaceDamaged(const QRect &)));
123     connect(surface, SIGNAL(parentChanged(WaylandSurface*,WaylandSurface*)),
124             this, SLOT(parentChanged(WaylandSurface*,WaylandSurface*)));
125     connect(surface, SIGNAL(sizeChanged()), this, SLOT(updateSize()));
126     connect(surface, SIGNAL(posChanged()), this, SLOT(updatePosition()));
127
128     m_damaged = false;
129     m_yInverted = surface ? surface->isYInverted() : true;
130 }
131
132 WaylandSurfaceItem::~WaylandSurfaceItem()
133 {
134     if (m_surface) {
135         m_surface->setSurfaceItem(0);
136     }
137     m_texture->deleteLater();
138 }
139
140 void WaylandSurfaceItem::setSurface(WaylandSurface *surface)
141 {
142     if (surface == m_surface)
143         return;
144
145     init(surface);
146     emit surfaceChanged();
147 }
148
149 bool WaylandSurfaceItem::isYInverted() const
150 {
151     return m_yInverted;
152 }
153
154 QSGTextureProvider *WaylandSurfaceItem::textureProvider() const
155 {
156     if (!m_provider)
157         m_provider = new WaylandSurfaceTextureProvider();
158     return m_provider;
159 }
160
161 void WaylandSurfaceItem::mousePressEvent(QMouseEvent *event)
162 {
163     if (m_surface) {
164         WaylandInputDevice *inputDevice = m_surface->compositor()->defaultInputDevice();
165         if (inputDevice->mouseFocus() != m_surface)
166             inputDevice->setMouseFocus(m_surface, event->pos(), event->globalPos());
167         inputDevice->sendMousePressEvent(event->button(), toSurface(event->pos()));
168     }
169 }
170
171 void WaylandSurfaceItem::mouseMoveEvent(QMouseEvent *event)
172 {
173     if (m_surface){
174         WaylandInputDevice *inputDevice = m_surface->compositor()->defaultInputDevice();
175         inputDevice->sendMouseMoveEvent(m_surface, toSurface(event->pos()));
176     }
177 }
178
179 void WaylandSurfaceItem::mouseReleaseEvent(QMouseEvent *event)
180 {
181     if (m_surface){
182         WaylandInputDevice *inputDevice = m_surface->compositor()->defaultInputDevice();
183         inputDevice->sendMouseReleaseEvent(event->button(), toSurface(event->pos()));
184     }
185 }
186
187 void WaylandSurfaceItem::keyPressEvent(QKeyEvent *event)
188 {
189     if (m_surface && hasFocus()) {
190         WaylandInputDevice *inputDevice = m_surface->compositor()->defaultInputDevice();
191         inputDevice->sendKeyPressEvent(event->nativeScanCode());
192     }
193 }
194
195 void WaylandSurfaceItem::keyReleaseEvent(QKeyEvent *event)
196 {
197     if (m_surface && hasFocus()) {
198         WaylandInputDevice *inputDevice = m_surface->compositor()->defaultInputDevice();
199         inputDevice->sendKeyReleaseEvent(event->nativeScanCode());
200     }
201 }
202
203 void WaylandSurfaceItem::touchEvent(QTouchEvent *event)
204 {
205     if (m_touchEventsEnabled && m_surface) {
206         WaylandInputDevice *inputDevice = m_surface->compositor()->defaultInputDevice();
207         event->accept();
208         if (inputDevice->mouseFocus() != m_surface) {
209             QPoint pointPos;
210             QList<QTouchEvent::TouchPoint> points = event->touchPoints();
211             if (!points.isEmpty())
212                 pointPos = points.at(0).pos().toPoint();
213             inputDevice->setMouseFocus(m_surface, pointPos, pointPos);
214         }
215         inputDevice->sendFullTouchEvent(event);
216     } else {
217         event->ignore();
218     }
219 }
220
221 void WaylandSurfaceItem::takeFocus()
222 {
223     setFocus(true);
224
225     if (m_surface) {
226         WaylandInputDevice *inputDevice = m_surface->compositor()->defaultInputDevice();
227         inputDevice->setKeyboardFocus(m_surface);
228     }
229 }
230
231 QPoint WaylandSurfaceItem::toSurface(const QPointF &pos) const
232 {
233     return pos.toPoint();
234 }
235
236 void WaylandSurfaceItem::surfaceMapped()
237 {
238     setPaintEnabled(true);
239 }
240
241 void WaylandSurfaceItem::surfaceUnmapped()
242 {
243     setPaintEnabled(false);
244 }
245
246 void WaylandSurfaceItem::surfaceDestroyed(QObject *)
247 {
248     m_surface = 0;
249 }
250
251 void WaylandSurfaceItem::setDamagedFlag(bool on)
252 {
253     m_damaged = on;
254 }
255
256
257 void WaylandSurfaceItem::surfaceDamaged(const QRect &)
258 {
259     m_damaged = true;
260     if (m_surface) {
261         bool inverted = m_surface->isYInverted();
262         if (inverted  != m_yInverted) {
263             m_yInverted = inverted;
264             emit yInvertedChanged();
265         }
266     }
267     emit textureChanged();
268     update();
269 }
270
271 void WaylandSurfaceItem::parentChanged(WaylandSurface *newParent, WaylandSurface *oldParent)
272 {
273     Q_UNUSED(oldParent);
274
275     WaylandSurfaceItem *item = newParent? newParent->surfaceItem():0;
276     setParentItem(item);
277
278     if (newParent) {
279         setPaintEnabled(true);
280         setVisible(true);
281         setOpacity(1);
282         setEnabled(true);
283     }
284 }
285
286 void WaylandSurfaceItem::updateSize()
287 {
288     setSize(m_surface->size());
289 }
290
291 void WaylandSurfaceItem::updatePosition()
292 {
293     setPos(m_surface->pos());
294 }
295
296 bool WaylandSurfaceItem::paintEnabled() const
297 {
298     return m_paintEnabled;
299 }
300
301 void WaylandSurfaceItem::setPaintEnabled(bool enabled)
302 {
303     m_paintEnabled = enabled;
304     update();
305 }
306
307 class WaylandSurfaceNode : public QSGSimpleTextureNode
308 {
309 public:
310     WaylandSurfaceNode(WaylandSurfaceItem *item) : m_item(item) {
311         setFlag(UsePreprocess,true);
312     }
313     void preprocess() {
314         if (m_item->m_damaged)
315             m_item->updateNodeTexture(this);
316     }
317 private:
318     WaylandSurfaceItem *m_item;
319 };
320
321
322 void WaylandSurfaceItem::updateNodeTexture(WaylandSurfaceNode *node)
323 {
324     if (m_damaged) {
325         QSGTexture *oldTexture = m_texture;
326         if (m_surface->type() == WaylandSurface::Texture) {
327             QOpenGLContext *context = QOpenGLContext::currentContext();
328
329             QQuickCanvas::CreateTextureOptions opt = useTextureAlpha() ? QQuickCanvas::TextureHasAlphaChannel : QQuickCanvas::CreateTextureOptions(0);
330             m_texture = canvas()->createTextureFromId(m_surface->texture(context),
331                                                                           m_surface->size(),
332                                                                           opt);
333         } else {
334             m_texture = canvas()->createTextureFromImage(m_surface->image());
335         }
336
337         delete oldTexture;
338         m_damaged = false;
339     }
340
341     if (m_provider) {
342         m_provider->t = m_texture;
343         m_provider->smooth = smooth();
344     }
345
346     node->setTexture(m_texture);
347     node->setFiltering(smooth() ? QSGTexture::Linear : QSGTexture::Nearest);
348 }
349
350
351 QSGNode *WaylandSurfaceItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
352 {
353     if (!m_surface || !m_paintEnabled) {
354         delete oldNode;
355         return 0;
356     }
357
358     WaylandSurfaceNode *node = static_cast<WaylandSurfaceNode *>(oldNode);
359
360     if (!node) {
361         node = new WaylandSurfaceNode(this);
362     }
363
364     updateNodeTexture(node);
365
366     if (surface()->isYInverted()) {
367         node->setRect(0, height(), width(), -height());
368     } else {
369         node->setRect(0, 0, width(), height());
370     }
371
372     return node;
373 }
374
375 void WaylandSurfaceItem::setUseTextureAlpha(bool useTextureAlpha)
376 {
377     m_useTextureAlpha = useTextureAlpha;
378
379     if ((flags() & ItemHasContents) != 0) {
380         update();
381     }
382 }
383
384 void WaylandSurfaceItem::setClientRenderingEnabled(bool enabled)
385 {
386     if (m_clientRenderingEnabled != enabled) {
387         m_clientRenderingEnabled = enabled;
388
389         if (m_surface) {
390             m_surface->sendOnScreenVisibilityChange(enabled);
391         }
392
393         emit clientRenderingEnabledChanged();
394     }
395 }
396
397 void WaylandSurfaceItem::setTouchEventsEnabled(bool enabled)
398 {
399     if (m_touchEventsEnabled != enabled) {
400         m_touchEventsEnabled = enabled;
401         emit touchEventsEnabledChanged();
402     }
403 }