Add PLUGIN_CLASS_NAME to qtbase plugins
[profile/ivi/qtbase.git] / src / plugins / platforms / eglfs / qeglfscursor.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 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 Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qeglfscursor.h"
43 #include <qpa/qwindowsysteminterface.h>
44 #include <QtGui/QOpenGLContext>
45 #include <QtCore/QJsonDocument>
46 #include <QtCore/QJsonArray>
47 #include <QtCore/QJsonObject>
48 #include <QtDebug>
49
50 QT_BEGIN_NAMESPACE
51
52 QEglFSCursor::QEglFSCursor(QEglFSScreen *screen)
53     : m_screen(screen), m_program(0), m_vertexCoordEntry(0), m_textureCoordEntry(0), m_textureEntry(0)
54 {
55     initCursorAtlas();
56
57     // initialize the cursor
58 #ifndef QT_NO_CURSOR
59     QCursor cursor(Qt::ArrowCursor);
60     setCurrentCursor(&cursor);
61 #endif
62 }
63
64 QEglFSCursor::~QEglFSCursor()
65 {
66     if (QOpenGLContext::currentContext()) {
67         glDeleteProgram(m_program);
68         glDeleteTextures(1, &m_cursor.customCursorTexture);
69         glDeleteTextures(1, &m_cursorAtlas.texture);
70     }
71 }
72
73 static GLuint createShader(GLenum shaderType, const char *program)
74 {
75     GLuint shader = glCreateShader(shaderType);
76     glShaderSource(shader, 1 /* count */, &program, NULL /* lengths */);
77     glCompileShader(shader);
78     GLint status;
79     glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
80     if (status == GL_TRUE)
81         return shader;
82
83     GLint length;
84     glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
85     char *infoLog = new char[length];
86     glGetShaderInfoLog(shader, length, NULL, infoLog);
87     qDebug("%s shader compilation error: %s", shaderType == GL_VERTEX_SHADER ? "vertex" : "fragment", infoLog);
88     delete [] infoLog;
89     return 0;
90 }
91
92 static GLuint createProgram(GLuint vshader, GLuint fshader)
93 {
94     GLuint program = glCreateProgram();
95     glAttachShader(program, vshader);
96     glAttachShader(program, fshader);
97     glLinkProgram(program);
98     GLint status;
99     glGetProgramiv(program, GL_LINK_STATUS, &status);
100     if (status == GL_TRUE)
101         return program;
102
103     GLint length;
104     glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length);
105     char *infoLog = new char[length];
106     glGetProgramInfoLog(program, length, NULL, infoLog);
107     qDebug("program link error: %s", infoLog);
108     delete [] infoLog;
109     return 0;
110 }
111
112 void QEglFSCursor::createShaderPrograms()
113 {
114     static const char *textureVertexProgram =
115         "attribute highp vec2 vertexCoordEntry;\n"
116         "attribute highp vec2 textureCoordEntry;\n"
117         "varying highp vec2 textureCoord;\n"
118         "void main() {\n"
119         "   textureCoord = textureCoordEntry;\n"
120         "   gl_Position = vec4(vertexCoordEntry, 1.0, 1.0);\n"
121         "}\n";
122
123     static const char *textureFragmentProgram =
124         "uniform sampler2D texture;\n"
125         "varying highp vec2 textureCoord;\n"
126         "void main() {\n"
127         "   gl_FragColor = texture2D(texture, textureCoord).bgra;\n"
128         "}\n";
129
130     GLuint vertexShader = createShader(GL_VERTEX_SHADER, textureVertexProgram);
131     GLuint fragmentShader = createShader(GL_FRAGMENT_SHADER, textureFragmentProgram);
132     m_program = createProgram(vertexShader, fragmentShader);
133     glDeleteShader(vertexShader);
134     glDeleteShader(fragmentShader);
135
136     m_vertexCoordEntry = glGetAttribLocation(m_program, "vertexCoordEntry");
137     m_textureCoordEntry = glGetAttribLocation(m_program, "textureCoordEntry");
138     m_textureEntry = glGetUniformLocation(m_program, "texture");
139 }
140
141 void QEglFSCursor::createCursorTexture(uint *texture, const QImage &image)
142 {
143     if (!*texture)
144         glGenTextures(1, texture);
145     glBindTexture(GL_TEXTURE_2D, *texture);
146     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
147     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
148     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
149     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
150
151     glTexImage2D(GL_TEXTURE_2D, 0 /* level */, GL_RGBA, image.width(), image.height(), 0 /* border */,
152                  GL_RGBA, GL_UNSIGNED_BYTE, image.constBits());
153 }
154
155 void QEglFSCursor::initCursorAtlas()
156 {
157     static QByteArray json = qgetenv("QT_QPA_EGLFS_CURSOR");
158     if (json.isEmpty())
159         json = ":/cursor.json";
160
161     QFile file(json);
162     file.open(QFile::ReadOnly);
163     QJsonDocument doc = QJsonDocument::fromJson(file.readAll());
164     QJsonObject object = doc.object();
165
166     QString atlas = object.value("image").toString();
167     Q_ASSERT(!atlas.isEmpty());
168
169     const int cursorsPerRow = object.value("cursorsPerRow").toDouble();
170     Q_ASSERT(cursorsPerRow);
171     m_cursorAtlas.cursorsPerRow = cursorsPerRow;
172
173     const QJsonArray hotSpots = object.value("hotSpots").toArray();
174     Q_ASSERT(hotSpots.count() == Qt::LastCursor);
175     for (int i = 0; i < hotSpots.count(); i++) {
176         QPoint hotSpot(hotSpots[i].toArray()[0].toDouble(), hotSpots[i].toArray()[1].toDouble());
177         m_cursorAtlas.hotSpots << hotSpot;
178     }
179
180     QImage image = QImage(atlas).convertToFormat(QImage::Format_ARGB32_Premultiplied);
181     m_cursorAtlas.cursorWidth = image.width() / m_cursorAtlas.cursorsPerRow;
182     m_cursorAtlas.cursorHeight = image.height() / ((Qt::LastCursor + cursorsPerRow - 1) / cursorsPerRow);
183     m_cursorAtlas.width = image.width();
184     m_cursorAtlas.height = image.height();
185     m_cursorAtlas.image = image;
186 }
187
188 #ifndef QT_NO_CURSOR
189 void QEglFSCursor::changeCursor(QCursor *cursor, QWindow *window)
190 {
191     Q_UNUSED(window);
192     const QRect oldCursorRect = cursorRect();
193     if (setCurrentCursor(cursor))
194         update(oldCursorRect | cursorRect());
195 }
196
197 bool QEglFSCursor::setCurrentCursor(QCursor *cursor)
198 {
199     if (m_cursor.shape == cursor->shape() && cursor->shape() != Qt::BitmapCursor)
200         return false;
201
202     if (m_cursor.shape == Qt::BitmapCursor) {
203         m_cursor.customCursorImage = QImage(); // in case render() never uploaded it
204     }
205
206     m_cursor.shape = cursor->shape();
207     if (cursor->shape() != Qt::BitmapCursor) { // standard cursor
208         const float ws = (float)m_cursorAtlas.cursorWidth / m_cursorAtlas.width,
209                     hs = (float)m_cursorAtlas.cursorHeight / m_cursorAtlas.height;
210         m_cursor.textureRect = QRectF(ws * (m_cursor.shape % m_cursorAtlas.cursorsPerRow),
211                                       hs * (m_cursor.shape / m_cursorAtlas.cursorsPerRow),
212                                       ws, hs);
213         m_cursor.hotSpot = m_cursorAtlas.hotSpots[m_cursor.shape];
214         m_cursor.texture = m_cursorAtlas.texture;
215         m_cursor.size = QSize(m_cursorAtlas.cursorWidth, m_cursorAtlas.cursorHeight);
216     } else {
217         QImage image = cursor->pixmap().toImage();
218         m_cursor.textureRect = QRectF(0, 0, 1, 1);
219         m_cursor.hotSpot = cursor->hotSpot();
220         m_cursor.texture = 0; // will get updated in the next render()
221         m_cursor.size = image.size();
222         m_cursor.customCursorImage = image;
223     }
224
225     return true;
226 }
227 #endif
228
229 void QEglFSCursor::update(const QRegion &rgn)
230 {
231     QWindowSystemInterface::handleExposeEvent(m_screen->topLevelAt(m_cursor.pos), rgn);
232     QWindowSystemInterface::flushWindowSystemEvents();
233 }
234
235 QRect QEglFSCursor::cursorRect() const
236 {
237     return QRect(m_cursor.pos - m_cursor.hotSpot, m_cursor.size);
238 }
239
240 QPoint QEglFSCursor::pos() const
241 {
242     return m_cursor.pos;
243 }
244
245 void QEglFSCursor::setPos(const QPoint &pos)
246 {
247     const QRect oldCursorRect = cursorRect();
248     m_cursor.pos = pos;
249     update(oldCursorRect | cursorRect());
250 }
251
252 void QEglFSCursor::pointerEvent(const QMouseEvent &event)
253 {
254     if (event.type() != QEvent::MouseMove)
255         return;
256     const QRect oldCursorRect = cursorRect();
257     m_cursor.pos = event.pos();
258     update(oldCursorRect | cursorRect());
259 }
260
261 void QEglFSCursor::paintOnScreen()
262 {
263     const QRectF cr = cursorRect();
264     const QRect screenRect(m_screen->geometry());
265     const GLfloat x1 = 2 * (cr.left() / screenRect.width()) - 1;
266     const GLfloat x2 = 2 * (cr.right() / screenRect.width()) - 1;
267     const GLfloat y1 = 1 - (cr.top() / screenRect.height()) * 2;
268     const GLfloat y2 = 1 - (cr.bottom() / screenRect.height()) * 2;
269     QRectF r(QPointF(x1, y1), QPointF(x2, y2));
270
271     draw(r);
272 }
273
274 void QEglFSCursor::draw(const QRectF &r)
275 {
276     if (!m_program) {
277         // one time initialization
278         createShaderPrograms();
279
280         if (!m_cursorAtlas.texture) {
281             createCursorTexture(&m_cursorAtlas.texture, m_cursorAtlas.image);
282             m_cursorAtlas.image = QImage();
283
284             if (m_cursor.shape != Qt::BitmapCursor)
285                 m_cursor.texture = m_cursorAtlas.texture;
286         }
287     }
288
289     if (m_cursor.shape == Qt::BitmapCursor && !m_cursor.customCursorImage.isNull()) {
290         // upload the custom cursor
291         createCursorTexture(&m_cursor.customCursorTexture, m_cursor.customCursorImage);
292         m_cursor.texture = m_cursor.customCursorTexture;
293         m_cursor.customCursorImage = QImage();
294     }
295
296     Q_ASSERT(m_cursor.texture);
297
298     glUseProgram(m_program);
299
300     const GLfloat x1 = r.left();
301     const GLfloat x2 = r.right();
302     const GLfloat y1 = r.top();
303     const GLfloat y2 = r.bottom();
304     const GLfloat cursorCoordinates[] = {
305         x1, y2,
306         x2, y2,
307         x1, y1,
308         x2, y1
309     };
310
311     const GLfloat s1 = m_cursor.textureRect.left();
312     const GLfloat s2 = m_cursor.textureRect.right();
313     const GLfloat t1 = m_cursor.textureRect.top();
314     const GLfloat t2 = m_cursor.textureRect.bottom();
315     const GLfloat textureCoordinates[] = {
316         s1, t2,
317         s2, t2,
318         s1, t1,
319         s2, t1
320     };
321
322     glBindTexture(GL_TEXTURE_2D, m_cursor.texture);
323
324     glEnableVertexAttribArray(m_vertexCoordEntry);
325     glEnableVertexAttribArray(m_textureCoordEntry);
326
327     glVertexAttribPointer(m_vertexCoordEntry, 2, GL_FLOAT, GL_FALSE, 0, cursorCoordinates);
328     glVertexAttribPointer(m_textureCoordEntry, 2, GL_FLOAT, GL_FALSE, 0, textureCoordinates);
329
330     glUniform1f(m_textureEntry, 0);
331
332     glEnable(GL_BLEND);
333     glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
334     glDisable(GL_DEPTH_TEST); // disable depth testing to make sure cursor is always on top
335     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
336     glDisable(GL_BLEND);
337
338     glBindTexture(GL_TEXTURE_2D, 0);
339     glDisableVertexAttribArray(m_vertexCoordEntry);
340     glDisableVertexAttribArray(m_textureCoordEntry);
341
342     glUseProgram(0);
343 }
344
345 QT_END_NAMESPACE