Change copyrights from Nokia to Digia
[profile/ivi/qtwayland.git] / examples / qwidget-compositor / main.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
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 Digia Plc and its Subsidiary(-ies) nor the names
21 **     of its contributors may be used to endorse or promote products derived
22 **     from this software without specific prior written permission.
23 **
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 "waylandcompositor.h"
42
43 #include "waylandsurface.h"
44 #include <QtCompositor/waylandinput.h>
45
46 #include <QApplication>
47 #include <QWidget>
48 #include <QTimer>
49 #include <QPainter>
50 #include <QMouseEvent>
51 #include <QtCore/QLinkedList>
52
53 #ifdef QT_COMPOSITOR_WAYLAND_GL
54 #include <QOpenGLContext>
55 #include <QGLWidget>
56 #include <QtGui/private/qopengltexturecache_p.h>
57 #include "textureblitter.h"
58 #include <QOpenGLFunctions>
59 #endif
60
61 #include <QDebug>
62
63 #ifdef QT_COMPOSITOR_WAYLAND_GL
64 class QWidgetCompositor : public QGLWidget, public WaylandCompositor
65 #else
66 class QWidgetCompositor : public QWidget, public WaylandCompositor
67 #endif
68 {
69     Q_OBJECT
70 public:
71     QWidgetCompositor()
72         : WaylandCompositor(windowHandle())
73 #ifdef QT_COMPOSITOR_WAYLAND_GL
74         , m_surfaceCompositorFbo(0)
75         , m_textureBlitter(0)
76         , m_textureCache(0)
77 #endif
78         , m_moveSurface(0)
79         , m_dragSourceSurface(0)
80     {
81         enableSubSurfaceExtension();
82         setMouseTracking(true);
83         setRetainedSelectionEnabled(true);
84         m_background = QImage(QLatin1String(":/background.jpg"));
85         //make sure we get the window id and create the glcontext
86         //so that clients can successfully initialize egl
87         winId();
88     }
89
90 private slots:
91     void surfaceDestroyed(QObject *object) {
92         WaylandSurface *surface = static_cast<WaylandSurface *>(object);
93         m_surfaces.removeAll(surface);
94         update();
95     }
96
97     void surfaceMapped() {
98         WaylandSurface *surface = qobject_cast<WaylandSurface *>(sender());
99         QPoint pos;
100         if (!m_surfaces.contains(surface)) {
101             uint px = 1 + (qrand() % (width() - surface->size().width() - 2));
102             uint py = 1 + (qrand() % (height() - surface->size().height() - 2));
103             pos = QPoint(px, py);
104             surface->setPos(pos);
105             m_surfaces.append(surface);
106         }
107
108         defaultInputDevice()->setKeyboardFocus(surface);
109         update();
110     }
111
112     void surfaceDamaged(const QRect &rect) {
113         WaylandSurface *surface = qobject_cast<WaylandSurface *>(sender());
114         surfaceDamaged(surface, rect);
115     }
116
117 protected:
118     void surfaceDamaged(WaylandSurface *surface, const QRect &rect)
119     {
120 #ifdef QT_COMPOSITOR_WAYLAND_GL
121         Q_UNUSED(surface);
122         Q_UNUSED(rect);
123         update();
124 #else
125         update(rect.translated(surface->geometry().topLeft()));
126 #endif
127     }
128
129     void surfaceCreated(WaylandSurface *surface) {
130         connect(surface, SIGNAL(destroyed(QObject *)), this, SLOT(surfaceDestroyed(QObject *)));
131         connect(surface, SIGNAL(mapped()), this, SLOT(surfaceMapped()));
132         connect(surface, SIGNAL(damaged(const QRect &)), this, SLOT(surfaceDamaged(const QRect &)));
133         update();
134     }
135
136 #ifdef QT_COMPOSITOR_WAYLAND_GL
137     GLuint composeSurface(WaylandSurface *surface) {
138         GLuint texture = 0;
139         QOpenGLFunctions *functions = QOpenGLContext::currentContext()->functions();
140
141         if (!m_surfaceCompositorFbo)
142             functions->glGenFramebuffers(1,&m_surfaceCompositorFbo);
143
144         functions->glBindFramebuffer(GL_FRAMEBUFFER, m_surfaceCompositorFbo);
145
146         if (surface->type() == WaylandSurface::Shm) {
147             texture = m_textureCache->bindTexture(context()->contextHandle(), surface->image());
148         } else {
149             texture = surface->texture(QOpenGLContext::currentContext());
150         }
151
152         functions->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
153                                            GL_TEXTURE_2D, texture, 0);
154         paintChildren(surface,surface);
155         functions->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
156                                            GL_TEXTURE_2D, 0, 0);
157
158         functions->glBindFramebuffer(GL_FRAMEBUFFER, 0);
159         return texture;
160     }
161
162     void paintChildren(WaylandSurface *surface, WaylandSurface *window) {
163
164         if (surface->subSurfaces().size() == 0)
165             return;
166
167         QLinkedListIterator<WaylandSurface *> i(surface->subSurfaces());
168         while (i.hasNext()) {
169             WaylandSurface *subSurface = i.next();
170             QPointF p = subSurface->mapTo(window,QPoint(0,0));
171             QSize size = subSurface->size();
172             if (size.isValid()) {
173                 GLuint texture = 0;
174                 if (subSurface->type() == WaylandSurface::Texture) {
175                     texture = subSurface->texture(QOpenGLContext::currentContext());
176                 } else if (surface->type() == WaylandSurface::Shm ) {
177                     texture = m_textureCache->bindTexture(context()->contextHandle(), surface->image());
178                 }
179                 m_textureBlitter->drawTexture(texture,QRect(p.toPoint(),size),window->size(),0,window->isYInverted(),subSurface->isYInverted());
180             }
181             paintChildren(subSurface,window);
182         }
183     }
184 #else //hmmm, this is actually untested :(
185     QImage composeSurface(WaylandSurface *surface)
186     {
187         Q_ASSER(surface->type() == WaylandSurface::Shm);
188         QImage img = surface->image();
189         QPainter p(&img);
190         paintChildren(surface,p,surface);
191
192         return img;
193     }
194
195     void paintChildren(WaylandSurface *surface, QPainter *painter, WaylandSurface *window) {
196         if (surface->subSurfaces().size() == 0)
197             return;
198
199         QLinkedListIterator<WaylandSurface *> i(surface->subSurfaces());
200         while (i.hasNext()) {
201             WaylandSurface *subSurface = i.next();
202             QPoint p = subSurface->mapTo(window,QPoint(0,0));
203             QRect geo = subSurface->geometry();
204             geo.moveTo(p);
205             if (geo.isValid()) {
206                 painter->drawImage(p,subSurface->image());
207             }
208             paintChildren(subSurface,painter,window);
209         }
210     }
211 #endif //QT_COMPOSITOR_WAYLAND_GL
212
213
214
215     void paintEvent(QPaintEvent *) {
216         QPainter p(this);
217
218         if (!m_background.isNull())
219             p.drawPixmap(rect(), m_backgroundScaled);
220
221 #ifdef QT_COMPOSITOR_WAYLAND_GL
222         if (!m_textureCache) {
223             m_textureCache = new QOpenGLTextureCache(context()->contextHandle());
224         }
225         if (!m_textureBlitter) {
226             m_textureBlitter = new TextureBlitter();
227         }
228         m_textureBlitter->bind();
229 #endif
230         for (int i = 0; i < m_surfaces.size(); ++i) {
231 #ifdef QT_COMPOSITOR_WAYLAND_GL
232             GLuint texture = composeSurface(m_surfaces.at(i));
233             WaylandSurface *surface = m_surfaces.at(i);
234             QRect geo(surface->pos().toPoint(),surface->size());
235             m_textureBlitter->drawTexture(texture,geo,size(),0,false,m_surfaces.at(i)->isYInverted());
236 #else
237             QImage img = composeSurface(m_surfaces.at(i));
238             p.drawImage(m_surfaces.at(i)->geometry().topLeft(),img);
239 #endif //QT_COMPOSITOR_WAYLAND_GL
240         }
241
242         if (!m_cursor.isNull())
243             p.drawImage(m_cursorPos - m_cursorHotspot, m_cursor);
244
245         frameFinished();
246
247 #ifdef QT_COMPOSITOR_WAYLAND_GL
248         //jl:FIX FIX FIX:)
249 //        update();
250         m_textureBlitter->release();
251 #endif
252     }
253
254     void resizeEvent(QResizeEvent *)
255     {
256         if (!m_background.isNull()) {
257             m_backgroundScaled = QPixmap::fromImage(m_background.scaled(size(),
258                 Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
259         }
260     }
261
262     void raise(WaylandSurface *surface) {
263         defaultInputDevice()->setKeyboardFocus(surface);
264         surfaceDamaged(surface, QRect(QPoint(), surface->size()));
265         m_surfaces.removeOne(surface);
266         m_surfaces.append(surface);
267     }
268
269     void mousePressEvent(QMouseEvent *e) {
270         m_cursorPos = e->pos();
271         if (!m_cursor.isNull())
272             update();
273         QPointF local;
274         if (WaylandSurface *surface = surfaceAt(e->pos(), &local)) {
275             raise(surface);
276             if (e->modifiers() & Qt::ControlModifier) {
277                 m_moveSurface = surface;
278                 m_moveOffset = local;
279             } else {
280                 defaultInputDevice()->sendMousePressEvent(e->button(), local.toPoint(),e->pos());
281             }
282         }
283     }
284
285     void mouseMoveEvent(QMouseEvent *e) {
286         m_cursorPos = e->pos();
287         if (!m_cursor.isNull())
288             update();
289         if (isDragging()) {
290             QPoint global = e->pos(); // "global" here means the window of the compositor
291             QPointF local;
292             WaylandSurface *surface = surfaceAt(e->pos(), &local);
293             if (surface) {
294                 if (!m_dragSourceSurface)
295                     m_dragSourceSurface = surface;
296                 if (m_dragSourceSurface == surface)
297                     m_lastDragSourcePos = local;
298                 raise(surface);
299             }
300             //this should go away when draggin is reimplemented
301             sendDragMoveEvent(global, local.toPoint(), surface);
302             return;
303         }
304         if (m_moveSurface) {
305             m_moveSurface->setPos(e->localPos() - m_moveOffset);
306             update();
307             return;
308         }
309         QPointF local;
310         if (surfaceAt(e->pos(), &local))
311             defaultInputDevice()->sendMouseMoveEvent(local.toPoint(),pos());
312     }
313
314     void mouseReleaseEvent(QMouseEvent *e) {
315         if (isDragging()) {
316             sendDragEndEvent();
317             if (m_dragSourceSurface) {
318                 // Must send a release event to the source too, no matter where the cursor is now.
319                 // This is a hack and should go away when we reimplement draging
320                 defaultInputDevice()->sendMouseReleaseEvent(e->button(), m_lastDragSourcePos.toPoint(), e->pos());
321                 m_dragSourceSurface = 0;
322             }
323         }
324         if (m_moveSurface) {
325             m_moveSurface = 0;
326             return;
327         }
328         QPointF local;
329         if (surfaceAt(e->pos(), &local))
330             defaultInputDevice()->sendMouseReleaseEvent(e->button(), local.toPoint(), e->pos());
331     }
332
333     void keyPressEvent(QKeyEvent *event)
334     {
335         defaultInputDevice()->sendKeyPressEvent(event->nativeScanCode());
336     }
337
338     void keyReleaseEvent(QKeyEvent *event)
339     {
340         defaultInputDevice()->sendKeyReleaseEvent(event->nativeScanCode());
341     }
342
343     WaylandSurface *surfaceAt(const QPointF &point, QPointF *local = 0) {
344         for (int i = m_surfaces.size() - 1; i >= 0; --i) {
345             WaylandSurface *surface = m_surfaces.at(i);
346             QRect geo(surface->pos().toPoint(),surface->size());
347             if (geo.contains(point.toPoint())) {
348                 if (local)
349                     *local = point - surface->pos();
350                 return surface;
351             }
352         }
353         return 0;
354     }
355
356     void changeCursor(const QImage &image, int hotspotX, int hotspotY) {
357         m_cursor = image;
358         m_cursorHotspot = QPoint(hotspotX, hotspotY);
359         update();
360     }
361
362 private:
363     QImage m_background;
364     QPixmap m_backgroundScaled;
365
366     QList<WaylandSurface *> m_surfaces;
367
368 #ifdef QT_COMPOSITOR_WAYLAND_GL
369     GLuint m_surfaceCompositorFbo;
370     TextureBlitter *m_textureBlitter;
371     QOpenGLTextureCache *m_textureCache;
372 #endif
373
374     WaylandSurface *m_moveSurface;
375     QPointF m_moveOffset;
376     WaylandSurface *m_dragSourceSurface;
377     QPointF m_lastDragSourcePos;
378
379     QImage m_cursor;
380     QPoint m_cursorPos;
381     QPoint m_cursorHotspot;
382
383     friend class TouchObserver;
384 };
385
386 int main(int argc, char *argv[])
387 {
388     QApplication app(argc, argv);
389
390     QWidgetCompositor compositor;
391     compositor.resize(800, 600);
392     compositor.show();
393
394 //    QTouchScreenHandlerThread t(QString(), new TouchObserver(&compositor));
395
396     return app.exec();
397 }
398
399 #include "main.moc"