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 &region)
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