Don't crash when sending touch events.
[profile/ivi/qtwayland.git] / examples / qwindow-compositor / qwindowcompositor.cpp
1 #include "qwindowcompositor.h"
2 #include <QMouseEvent>
3 #include <QKeyEvent>
4 #include <QTouchEvent>
5
6 #include <QtCompositor/waylandinput.h>
7
8 QWindowCompositor::QWindowCompositor(QOpenGLWindow *window)
9     : WaylandCompositor(window)
10     , m_window(window)
11     , m_textureBlitter(0)
12     , m_renderScheduler(this)
13     , m_draggingWindow(0)
14     , m_dragKeyIsPressed(false)
15 {
16     enableSubSurfaceExtension();
17     m_window->makeCurrent();
18
19     initializeGLFunctions();
20
21     m_textureCache = new QOpenGLTextureCache(m_window->context());
22     m_textureBlitter = new TextureBlitter();
23     m_backgroundImage = QImage(QLatin1String(":/background.jpg"));
24     m_renderScheduler.setSingleShot(true);
25     connect(&m_renderScheduler,SIGNAL(timeout()),this,SLOT(render()));
26
27     glGenFramebuffers(1,&m_surface_fbo);
28
29     window->installEventFilter(this);
30
31     setRetainedSelectionEnabled(true);
32
33     m_renderScheduler.start(0);
34 }
35
36 void QWindowCompositor::surfaceDestroyed(QObject *object)
37 {
38     WaylandSurface *surface = static_cast<WaylandSurface *>(object);
39     m_surfaces.removeOne(surface);
40     if (defaultInputDevice()->keyboardFocus() == surface || !defaultInputDevice()->keyboardFocus()) // typically reset to 0 already in Compositor::surfaceDestroyed()
41         defaultInputDevice()->setKeyboardFocus(m_surfaces.isEmpty() ? 0 : m_surfaces.last());
42     m_renderScheduler.start(0);
43 }
44
45 void QWindowCompositor::surfaceMapped()
46 {
47     WaylandSurface *surface = qobject_cast<WaylandSurface *>(sender());
48     QPoint pos;
49     if (!m_surfaces.contains(surface)) {
50         uint px = 0;
51         uint py = 0;
52         if (!QCoreApplication::arguments().contains(QLatin1String("-stickytopleft"))) {
53             px = 1 + (qrand() % (m_window->width() - surface->size().width() - 2));
54             py = 1 + (qrand() % (m_window->height() - surface->size().height() - 2));
55         }
56         pos = QPoint(px, py);
57         surface->setPos(pos);
58     } else {
59         surface->setPos(window()->geometry().topLeft());
60         m_surfaces.removeOne(surface);
61     }
62     m_surfaces.append(surface);
63     defaultInputDevice()->setKeyboardFocus(surface);
64     m_renderScheduler.start(0);
65 }
66
67 void QWindowCompositor::surfaceDamaged(const QRect &rect)
68 {
69     WaylandSurface *surface = qobject_cast<WaylandSurface *>(sender());
70     surfaceDamaged(surface, rect);
71 }
72
73 void QWindowCompositor::surfaceDamaged(WaylandSurface *surface, const QRect &rect)
74 {
75     Q_UNUSED(surface)
76     Q_UNUSED(rect)
77     m_renderScheduler.start(0);
78 }
79
80 void QWindowCompositor::surfaceCreated(WaylandSurface *surface)
81 {
82     connect(surface, SIGNAL(destroyed(QObject *)), this, SLOT(surfaceDestroyed(QObject *)));
83     connect(surface, SIGNAL(mapped()), this, SLOT(surfaceMapped()));
84     connect(surface, SIGNAL(damaged(const QRect &)), this, SLOT(surfaceDamaged(const QRect &)));
85     m_renderScheduler.start(0);
86 }
87
88 QPointF QWindowCompositor::toSurface(WaylandSurface *surface, const QPointF &pos) const
89 {
90     return pos - surface->pos();
91 }
92
93 void QWindowCompositor::changeCursor(const QImage &image, int hotspotX, int hotspotY)
94 {
95     QCursor cursor(QPixmap::fromImage(image),hotspotX,hotspotY);
96     static bool cursroIsSet = false;
97     if (cursroIsSet) {
98         QGuiApplication::changeOverrideCursor(cursor);
99     } else {
100         QGuiApplication::setOverrideCursor(cursor);
101         cursroIsSet = true;
102     }
103 }
104
105 WaylandSurface *QWindowCompositor::surfaceAt(const QPoint &point, QPoint *local)
106 {
107     for (int i = m_surfaces.size() - 1; i >= 0; --i) {
108         WaylandSurface *surface = m_surfaces.at(i);
109         QRect geo(surface->pos().toPoint(),surface->size());
110         if (geo.contains(point)) {
111             if (local)
112                 *local = toSurface(surface, point).toPoint();
113             return surface;
114         }
115     }
116     return 0;
117 }
118
119 GLuint QWindowCompositor::composeSurface(WaylandSurface *surface)
120 {
121     GLuint texture = 0;
122
123     glBindFramebuffer(GL_FRAMEBUFFER, m_surface_fbo);
124
125     if (surface->type() == WaylandSurface::Shm) {
126         texture = m_textureCache->bindTexture(QOpenGLContext::currentContext(),surface->image());
127     } else {
128         texture = surface->texture(QOpenGLContext::currentContext());
129     }
130     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
131                            GL_TEXTURE_2D, texture, 0);
132     paintChildren(surface,surface);
133     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
134                            GL_TEXTURE_2D,0, 0);
135
136     glBindFramebuffer(GL_FRAMEBUFFER,0);
137     return texture;
138 }
139
140 void QWindowCompositor::paintChildren(WaylandSurface *surface, WaylandSurface *window) {
141
142     if (surface->subSurfaces().size() == 0)
143         return;
144
145     QLinkedListIterator<WaylandSurface *> i(surface->subSurfaces());
146     while (i.hasNext()) {
147         WaylandSurface *subSurface = i.next();
148         QPointF p = subSurface->mapTo(window,QPointF(0,0));
149         if (subSurface->size().isValid()) {
150             GLuint texture = 0;
151             if (subSurface->type() == WaylandSurface::Texture) {
152                 texture = subSurface->texture(QOpenGLContext::currentContext());
153             } else if (surface->type() == WaylandSurface::Shm ) {
154                 texture = m_textureCache->bindTexture(QOpenGLContext::currentContext(),surface->image());
155             }
156             QRect geo(p.toPoint(),subSurface->size());
157             m_textureBlitter->drawTexture(texture,geo,window->size(),0,window->isYInverted(),subSurface->isYInverted());
158         }
159         paintChildren(subSurface,window);
160     }
161 }
162
163
164 void QWindowCompositor::render()
165 {
166     m_window->makeCurrent();
167     m_backgroundTexture = m_textureCache->bindTexture(QOpenGLContext::currentContext(),m_backgroundImage);
168
169     m_textureBlitter->bind();
170     //Draw the background Image texture
171     int w = m_window->width();
172     int h = m_window->height();
173     QSize imageSize = m_backgroundImage.size();
174     for (int y = 0; y < h; y += imageSize.height()) {
175         for (int x = 0; x < w; x += imageSize.width()) {
176             m_textureBlitter->drawTexture(m_backgroundTexture,QRect(QPoint(x, y),imageSize),window()->size(), 0,true,true);
177         }
178     }
179
180     foreach (WaylandSurface *surface, m_surfaces) {
181         GLuint texture = composeSurface(surface);
182         QRect geo(surface->pos().toPoint(),surface->size());
183         m_textureBlitter->drawTexture(texture,geo,m_window->size(),0,false,surface->isYInverted());
184     }
185
186     m_textureBlitter->release();
187     frameFinished();
188     glFinish();
189
190     m_window->swapBuffers();
191 }
192
193 bool QWindowCompositor::eventFilter(QObject *obj, QEvent *event)
194 {
195     if (obj != m_window)
196         return false;
197
198     WaylandInputDevice *input = defaultInputDevice();
199
200     switch (event->type()) {
201     case QEvent::Expose:
202         m_renderScheduler.start(0);
203         break;
204     case QEvent::MouseButtonPress: {
205         QPoint local;
206         QMouseEvent *me = static_cast<QMouseEvent *>(event);
207         WaylandSurface *targetSurface = surfaceAt(me->pos(), &local);
208         if (targetSurface) {
209             if (m_dragKeyIsPressed) {
210                m_draggingWindow = targetSurface;
211                m_drag_diff = local;
212             } else {
213                 if (input->keyboardFocus() != targetSurface) {
214                     input->setKeyboardFocus(targetSurface);
215                     m_surfaces.removeOne(targetSurface);
216                     m_surfaces.append(targetSurface);
217                     m_renderScheduler.start(0);
218                 }
219                 input->sendMousePressEvent(me->button(),local,me->pos());
220             }
221         }
222         break;
223     }
224     case QEvent::MouseButtonRelease: {
225         WaylandSurface *targetSurface = input->mouseFocus();
226         if (m_draggingWindow) {
227             m_draggingWindow = 0;
228             m_drag_diff = QPoint();
229         } else  if (targetSurface) {
230             QMouseEvent *me = static_cast<QMouseEvent *>(event);
231             input->sendMouseReleaseEvent(me->button(),toSurface(targetSurface, me->pos()).toPoint(),me->pos());
232         }
233         break;
234     }
235     case QEvent::MouseMove: {
236         QMouseEvent *me = static_cast<QMouseEvent *>(event);
237         if (m_draggingWindow) {
238             m_draggingWindow->setPos(me->posF() - m_drag_diff);
239             m_renderScheduler.start(0);
240         } else {
241             QPoint local;
242             WaylandSurface *targetSurface = surfaceAt(me->pos(), &local);
243             input->sendMouseMoveEvent(targetSurface, local,me->pos());
244         }
245         break;
246     }
247     case QEvent::KeyPress: {
248         QKeyEvent *ke = static_cast<QKeyEvent *>(event);
249         if (ke->key() == Qt::Key_Meta || ke->key() == Qt::Key_Super_L) {
250             m_dragKeyIsPressed = true;
251         }
252         WaylandSurface *targetSurface = input->keyboardFocus();
253         if (targetSurface)
254             input->sendKeyPressEvent(ke->nativeScanCode());
255         break;
256     }
257     case QEvent::KeyRelease: {
258         QKeyEvent *ke = static_cast<QKeyEvent *>(event);
259         if (ke->key() == Qt::Key_Meta || ke->key() == Qt::Key_Super_L) {
260             m_dragKeyIsPressed = false;
261         }
262         WaylandSurface *targetSurface = input->keyboardFocus();
263         if (targetSurface)
264             input->sendKeyReleaseEvent(ke->nativeScanCode());
265         break;
266     }
267     case QEvent::TouchBegin:
268     case QEvent::TouchUpdate:
269     case QEvent::TouchEnd:
270     {
271         WaylandSurface *targetSurface = 0;
272         QTouchEvent *te = static_cast<QTouchEvent *>(event);
273         QList<QTouchEvent::TouchPoint> points = te->touchPoints();
274         QPoint pointPos;
275         if (!points.isEmpty()) {
276             pointPos = points.at(0).pos().toPoint();
277             targetSurface = surfaceAt(pointPos);
278         }
279         if (targetSurface && targetSurface != input->mouseFocus())
280             input->setMouseFocus(targetSurface, pointPos, pointPos);
281         if (input->mouseFocus())
282             input->sendFullTouchEvent(te);
283         break;
284     }
285     default:
286         break;
287     }
288     return false;
289 }