1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
4 ** Copyright (C) 2016 The Qt Company Ltd.
5 ** Contact: https://www.qt.io/licensing/
6 **
7 ** This file is part of the plugins of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** Commercial License Usage
11 ** Licensees holding valid commercial Qt licenses may use this file in
12 ** accordance with the commercial license agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and The Qt Company. For licensing terms
15 ** and conditions see https://www.qt.io/terms-conditions. For further
16 ** information use the contact form at https://www.qt.io/contact-us.
17 **
18 ** GNU Lesser General Public License Usage
19 ** Alternatively, this file may be used under the terms of the GNU Lesser
20 ** General Public License version 3 as published by the Free Software
21 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
22 ** packaging of this file. Please review the following information to
23 ** ensure the GNU Lesser General Public License version 3 requirements
24 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25 **
26 ** GNU General Public License Usage
27 ** Alternatively, this file may be used under the terms of the GNU
28 ** General Public License version 2.0 or (at your option) the GNU General
29 ** Public license version 3 or any later version approved by the KDE Free
30 ** Qt Foundation. The licenses are as published by the Free Software
31 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32 ** included in the packaging of this file. Please review the following
33 ** information to ensure the GNU General Public License requirements will
34 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35 ** https://www.gnu.org/licenses/gpl-3.0.html.
36 **
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40
41 #include "qandroidplatformopenglwindow.h"
42
43 #include "qandroidplatformscreen.h"
44 #include "androidjnimain.h"
45 #include "qandroideventdispatcher.h"
46 #include "androiddeadlockprotector.h"
47
48 #include <QSurfaceFormat>
49 #include <QtGui/private/qwindow_p.h>
50 #include <QtGui/qguiapplication.h>
51
52 #include <qpa/qwindowsysteminterface.h>
53 #include <qpa/qplatformscreen.h>
54 #include <QtEglSupport/private/qeglconvenience_p.h>
55 #include <android/native_window.h>
56 #include <android/native_window_jni.h>
57
58 QT_BEGIN_NAMESPACE
59
QAndroidPlatformOpenGLWindow(QWindow * window,EGLDisplay display)60 QAndroidPlatformOpenGLWindow::QAndroidPlatformOpenGLWindow(QWindow *window, EGLDisplay display)
61 :QAndroidPlatformWindow(window), m_eglDisplay(display)
62 {
63 }
64
~QAndroidPlatformOpenGLWindow()65 QAndroidPlatformOpenGLWindow::~QAndroidPlatformOpenGLWindow()
66 {
67 m_surfaceWaitCondition.wakeOne();
68 lockSurface();
69 if (m_nativeSurfaceId != -1)
70 QtAndroid::destroySurface(m_nativeSurfaceId);
71 clearEgl();
72 unlockSurface();
73 }
74
repaint(const QRegion & region)75 void QAndroidPlatformOpenGLWindow::repaint(const QRegion ®ion)
76 {
77 // This is only for real raster top-level windows. Stop in all other cases.
78 if ((window()->surfaceType() == QSurface::RasterGLSurface && qt_window_private(window())->compositing)
79 || window()->surfaceType() == QSurface::OpenGLSurface
80 || QAndroidPlatformWindow::parent())
81 return;
82
83 QRect currentGeometry = geometry();
84
85 QRect dirtyClient = region.boundingRect();
86 QRect dirtyRegion(currentGeometry.left() + dirtyClient.left(),
87 currentGeometry.top() + dirtyClient.top(),
88 dirtyClient.width(),
89 dirtyClient.height());
90 QRect mOldGeometryLocal = m_oldGeometry;
91 m_oldGeometry = currentGeometry;
92 // If this is a move, redraw the previous location
93 if (mOldGeometryLocal != currentGeometry)
94 platformScreen()->setDirty(mOldGeometryLocal);
95 platformScreen()->setDirty(dirtyRegion);
96 }
97
setGeometry(const QRect & rect)98 void QAndroidPlatformOpenGLWindow::setGeometry(const QRect &rect)
99 {
100 if (rect == geometry())
101 return;
102
103 m_oldGeometry = geometry();
104
105 QAndroidPlatformWindow::setGeometry(rect);
106 if (m_nativeSurfaceId != -1)
107 QtAndroid::setSurfaceGeometry(m_nativeSurfaceId, rect);
108
109 QRect availableGeometry = screen()->availableGeometry();
110 if (rect.width() > 0
111 && rect.height() > 0
112 && availableGeometry.width() > 0
113 && availableGeometry.height() > 0) {
114 QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), rect.size()));
115 }
116
117 if (rect.topLeft() != m_oldGeometry.topLeft())
118 repaint(QRegion(rect));
119 }
120
eglSurface(EGLConfig config)121 EGLSurface QAndroidPlatformOpenGLWindow::eglSurface(EGLConfig config)
122 {
123 if (QAndroidEventDispatcherStopper::stopped() || QGuiApplication::applicationState() == Qt::ApplicationSuspended)
124 return m_eglSurface;
125
126 QMutexLocker lock(&m_surfaceMutex);
127
128 if (m_nativeSurfaceId == -1) {
129 AndroidDeadlockProtector protector;
130 if (!protector.acquire())
131 return m_eglSurface;
132
133 const bool windowStaysOnTop = bool(window()->flags() & Qt::WindowStaysOnTopHint);
134 m_nativeSurfaceId = QtAndroid::createSurface(this, geometry(), windowStaysOnTop, 32);
135 m_surfaceWaitCondition.wait(&m_surfaceMutex);
136 }
137
138 if (m_eglSurface == EGL_NO_SURFACE) {
139 m_surfaceMutex.unlock();
140 checkNativeSurface(config);
141 m_surfaceMutex.lock();
142 }
143 return m_eglSurface;
144 }
145
checkNativeSurface(EGLConfig config)146 bool QAndroidPlatformOpenGLWindow::checkNativeSurface(EGLConfig config)
147 {
148 QMutexLocker lock(&m_surfaceMutex);
149 if (m_nativeSurfaceId == -1 || !m_androidSurfaceObject.isValid())
150 return false; // makeCurrent is NOT needed.
151
152 createEgl(config);
153
154 // we've create another surface, the window should be repainted
155 QRect availableGeometry = screen()->availableGeometry();
156 if (geometry().width() > 0 && geometry().height() > 0 && availableGeometry.width() > 0 && availableGeometry.height() > 0)
157 QWindowSystemInterface::handleExposeEvent(window(), QRegion(QRect(QPoint(), geometry().size())));
158 return true; // makeCurrent is needed!
159 }
160
applicationStateChanged(Qt::ApplicationState state)161 void QAndroidPlatformOpenGLWindow::applicationStateChanged(Qt::ApplicationState state)
162 {
163 QAndroidPlatformWindow::applicationStateChanged(state);
164 if (state <= Qt::ApplicationHidden) {
165 lockSurface();
166 if (m_nativeSurfaceId != -1) {
167 QtAndroid::destroySurface(m_nativeSurfaceId);
168 m_nativeSurfaceId = -1;
169 }
170 clearEgl();
171 unlockSurface();
172 }
173 }
174
createEgl(EGLConfig config)175 void QAndroidPlatformOpenGLWindow::createEgl(EGLConfig config)
176 {
177 clearEgl();
178 QJNIEnvironmentPrivate env;
179 m_nativeWindow = ANativeWindow_fromSurface(env, m_androidSurfaceObject.object());
180 m_androidSurfaceObject = QJNIObjectPrivate();
181 m_eglSurface = eglCreateWindowSurface(m_eglDisplay, config, m_nativeWindow, NULL);
182 m_format = q_glFormatFromConfig(m_eglDisplay, config, window()->requestedFormat());
183 if (Q_UNLIKELY(m_eglSurface == EGL_NO_SURFACE)) {
184 EGLint error = eglGetError();
185 eglTerminate(m_eglDisplay);
186 qFatal("EGL Error : Could not create the egl surface: error = 0x%x\n", error);
187 }
188 }
189
format() const190 QSurfaceFormat QAndroidPlatformOpenGLWindow::format() const
191 {
192 if (m_nativeWindow == 0)
193 return window()->requestedFormat();
194 else
195 return m_format;
196 }
197
clearEgl()198 void QAndroidPlatformOpenGLWindow::clearEgl()
199 {
200 if (m_eglSurface != EGL_NO_SURFACE) {
201 eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
202 eglDestroySurface(m_eglDisplay, m_eglSurface);
203 m_eglSurface = EGL_NO_SURFACE;
204 }
205
206 if (m_nativeWindow) {
207 ANativeWindow_release(m_nativeWindow);
208 m_nativeWindow = 0;
209 }
210 }
211
surfaceChanged(JNIEnv * jniEnv,jobject surface,int w,int h)212 void QAndroidPlatformOpenGLWindow::surfaceChanged(JNIEnv *jniEnv, jobject surface, int w, int h)
213 {
214 Q_UNUSED(jniEnv);
215 Q_UNUSED(w);
216 Q_UNUSED(h);
217
218 lockSurface();
219 m_androidSurfaceObject = surface;
220 if (surface) // wait until we have a valid surface to draw into
221 m_surfaceWaitCondition.wakeOne();
222 unlockSurface();
223
224 if (surface) {
225 // repaint the window, when we have a valid surface
226 QRect availableGeometry = screen()->availableGeometry();
227 if (geometry().width() > 0 && geometry().height() > 0 && availableGeometry.width() > 0 && availableGeometry.height() > 0)
228 QWindowSystemInterface::handleExposeEvent(window(), QRegion(QRect(QPoint(), geometry().size())));
229 }
230 }
231
232 QT_END_NAMESPACE
233