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