1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the plugins of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
42 #include "qeglfscursor.h"
43 #include <QtGui/qwindowsysteminterface.h>
44 #include <QtGui/QOpenGLContext>
45 #include <QtCore/QJsonDocument>
46 #include <QtCore/QJsonArray>
47 #include <QtCore/QJsonObject>
52 QEglFSCursor::QEglFSCursor(QEglFSScreen *screen)
53 : m_screen(screen), m_program(0), m_vertexCoordEntry(0), m_textureCoordEntry(0), m_textureEntry(0)
57 // initialize the cursor
58 QCursor cursor(Qt::ArrowCursor);
59 setCurrentCursor(&cursor);
62 QEglFSCursor::~QEglFSCursor()
64 if (QOpenGLContext::currentContext()) {
65 glDeleteProgram(m_program);
66 glDeleteTextures(1, &m_cursor.customCursorTexture);
67 glDeleteTextures(1, &m_cursorAtlas.texture);
71 static GLuint createShader(GLenum shaderType, const char *program)
73 GLuint shader = glCreateShader(shaderType);
74 glShaderSource(shader, 1 /* count */, &program, NULL /* lengths */);
75 glCompileShader(shader);
77 glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
78 if (status == GL_TRUE)
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);
90 static GLuint createProgram(GLuint vshader, GLuint fshader)
92 GLuint program = glCreateProgram();
93 glAttachShader(program, vshader);
94 glAttachShader(program, fshader);
95 glLinkProgram(program);
97 glGetProgramiv(program, GL_LINK_STATUS, &status);
98 if (status == GL_TRUE)
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);
110 void QEglFSCursor::createShaderPrograms()
112 static const char *textureVertexProgram =
113 "attribute highp vec2 vertexCoordEntry;\n"
114 "attribute highp vec2 textureCoordEntry;\n"
115 "varying highp vec2 textureCoord;\n"
117 " textureCoord = textureCoordEntry;\n"
118 " gl_Position = vec4(vertexCoordEntry, 1.0, 1.0);\n"
121 static const char *textureFragmentProgram =
122 "uniform sampler2D texture;\n"
123 "varying highp vec2 textureCoord;\n"
125 " gl_FragColor = texture2D(texture, textureCoord).bgra;\n"
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);
134 m_vertexCoordEntry = glGetAttribLocation(m_program, "vertexCoordEntry");
135 m_textureCoordEntry = glGetAttribLocation(m_program, "textureCoordEntry");
136 m_textureEntry = glGetUniformLocation(m_program, "texture");
139 void QEglFSCursor::createCursorTexture(uint *texture, const QImage &image)
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);
149 glTexImage2D(GL_TEXTURE_2D, 0 /* level */, GL_RGBA, image.width(), image.height(), 0 /* border */,
150 GL_RGBA, GL_UNSIGNED_BYTE, image.constBits());
153 void QEglFSCursor::initCursorAtlas()
155 static QByteArray json = qgetenv("QT_QPA_EGLFS_CURSOR");
157 json = ":/cursor.json";
160 file.open(QFile::ReadOnly);
161 QJsonDocument doc = QJsonDocument::fromJson(file.readAll());
162 QJsonObject object = doc.object();
164 QString atlas = object.value("image").toString();
165 Q_ASSERT(!atlas.isEmpty());
167 const int cursorsPerRow = object.value("cursorsPerRow").toDouble();
168 Q_ASSERT(cursorsPerRow);
169 m_cursorAtlas.cursorsPerRow = cursorsPerRow;
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;
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;
186 void QEglFSCursor::changeCursor(QCursor *cursor, QWindow *window)
189 const QRect oldCursorRect = cursorRect();
190 if (setCurrentCursor(cursor))
191 update(oldCursorRect | cursorRect());
194 bool QEglFSCursor::setCurrentCursor(QCursor *cursor)
196 if (m_cursor.shape == cursor->shape() && cursor->shape() != Qt::BitmapCursor)
199 if (m_cursor.shape == Qt::BitmapCursor) {
200 m_cursor.customCursorImage = QImage(); // in case render() never uploaded it
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),
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);
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;
225 void QEglFSCursor::update(const QRegion &rgn)
227 QWindowSystemInterface::handleSynchronousExposeEvent(m_screen->topLevelAt(m_cursor.pos), rgn);
230 QRect QEglFSCursor::cursorRect() const
232 return QRect(m_cursor.pos - m_cursor.hotSpot, m_cursor.size);
235 QPoint QEglFSCursor::pos() const
240 void QEglFSCursor::setPos(const QPoint &pos)
242 const QRect oldCursorRect = cursorRect();
244 update(oldCursorRect | cursorRect());
247 void QEglFSCursor::pointerEvent(const QMouseEvent &event)
249 if (event.type() != QEvent::MouseMove)
251 const QRect oldCursorRect = cursorRect();
252 m_cursor.pos = event.pos();
253 update(oldCursorRect | cursorRect());
256 void QEglFSCursor::paintOnScreen()
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));
269 void QEglFSCursor::draw(const QRectF &r)
272 // one time initialization
273 createShaderPrograms();
275 if (!m_cursorAtlas.texture) {
276 createCursorTexture(&m_cursorAtlas.texture, m_cursorAtlas.image);
277 m_cursorAtlas.image = QImage();
279 if (m_cursor.shape != Qt::BitmapCursor)
280 m_cursor.texture = m_cursorAtlas.texture;
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();
291 Q_ASSERT(m_cursor.texture);
293 glUseProgram(m_program);
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[] = {
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[] = {
317 glBindTexture(GL_TEXTURE_2D, m_cursor.texture);
319 glEnableVertexAttribArray(m_vertexCoordEntry);
320 glEnableVertexAttribArray(m_textureCoordEntry);
322 glVertexAttribPointer(m_vertexCoordEntry, 2, GL_FLOAT, GL_FALSE, 0, cursorCoordinates);
323 glVertexAttribPointer(m_textureCoordEntry, 2, GL_FLOAT, GL_FALSE, 0, textureCoordinates);
325 glUniform1f(m_textureEntry, 0);
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);
333 glBindTexture(GL_TEXTURE_2D, 0);
334 glDisableVertexAttribArray(m_vertexCoordEntry);
335 glDisableVertexAttribArray(m_textureCoordEntry);