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 <QDebug>
42 #include <QTime>
43
44 #include <qpa/qwindowsysteminterface.h>
45
46 #include "qandroidplatformscreen.h"
47 #include "qandroidplatformbackingstore.h"
48 #include "qandroidplatformintegration.h"
49 #include "qandroidplatformwindow.h"
50 #include "androidjnimain.h"
51 #include "androidjnimenu.h"
52 #include "androiddeadlockprotector.h"
53
54 #include <android/bitmap.h>
55 #include <android/native_window_jni.h>
56 #include <qguiapplication.h>
57
58 #include <QtGui/QGuiApplication>
59 #include <QtGui/QWindow>
60 #include <QtGui/private/qwindow_p.h>
61
62 #include <vector>
63
64 QT_BEGIN_NAMESPACE
65
66 #ifdef QANDROIDPLATFORMSCREEN_DEBUG
67 class ScopedProfiler
68 {
69 public:
ScopedProfiler(const QString & msg)70 ScopedProfiler(const QString &msg)
71 {
72 m_msg = msg;
73 m_timer.start();
74 }
~ScopedProfiler()75 ~ScopedProfiler()
76 {
77 qDebug() << m_msg << m_timer.elapsed();
78 }
79
80 private:
81 QTime m_timer;
82 QString m_msg;
83 };
84
85 # define PROFILE_SCOPE ScopedProfiler ___sp___(__func__)
86 #else
87 # define PROFILE_SCOPE
88 #endif
89
QAndroidPlatformScreen()90 QAndroidPlatformScreen::QAndroidPlatformScreen()
91 : QObject(), QPlatformScreen()
92 {
93 m_availableGeometry = QRect(0, 0, QAndroidPlatformIntegration::m_defaultGeometryWidth, QAndroidPlatformIntegration::m_defaultGeometryHeight);
94 m_size = QSize(QAndroidPlatformIntegration::m_defaultScreenWidth, QAndroidPlatformIntegration::m_defaultScreenHeight);
95 // Raster only apps should set QT_ANDROID_RASTER_IMAGE_DEPTH to 16
96 // is way much faster than 32
97 if (qEnvironmentVariableIntValue("QT_ANDROID_RASTER_IMAGE_DEPTH") == 16) {
98 m_format = QImage::Format_RGB16;
99 m_depth = 16;
100 } else {
101 m_format = QImage::Format_ARGB32_Premultiplied;
102 m_depth = 32;
103 }
104 m_physicalSize.setHeight(QAndroidPlatformIntegration::m_defaultPhysicalSizeHeight);
105 m_physicalSize.setWidth(QAndroidPlatformIntegration::m_defaultPhysicalSizeWidth);
106 connect(qGuiApp, &QGuiApplication::applicationStateChanged, this, &QAndroidPlatformScreen::applicationStateChanged);
107 }
108
~QAndroidPlatformScreen()109 QAndroidPlatformScreen::~QAndroidPlatformScreen()
110 {
111 if (m_id != -1) {
112 QtAndroid::destroySurface(m_id);
113 m_surfaceWaitCondition.wakeOne();
114 releaseSurface();
115 }
116 }
117
topWindow() const118 QWindow *QAndroidPlatformScreen::topWindow() const
119 {
120 for (QAndroidPlatformWindow *w : m_windowStack) {
121 if (w->window()->type() == Qt::Window ||
122 w->window()->type() == Qt::Popup ||
123 w->window()->type() == Qt::Dialog) {
124 return w->window();
125 }
126 }
127 return 0;
128 }
129
topLevelAt(const QPoint & p) const130 QWindow *QAndroidPlatformScreen::topLevelAt(const QPoint &p) const
131 {
132 for (QAndroidPlatformWindow *w : m_windowStack) {
133 if (w->geometry().contains(p, false) && w->window()->isVisible())
134 return w->window();
135 }
136 return 0;
137 }
138
event(QEvent * event)139 bool QAndroidPlatformScreen::event(QEvent *event)
140 {
141 if (event->type() == QEvent::UpdateRequest) {
142 doRedraw();
143 m_updatePending = false;
144 return true;
145 }
146 return QObject::event(event);
147 }
148
addWindow(QAndroidPlatformWindow * window)149 void QAndroidPlatformScreen::addWindow(QAndroidPlatformWindow *window)
150 {
151 if (window->parent() && window->isRaster())
152 return;
153
154 Q_ASSERT(!m_windowStack.contains(window));
155 m_windowStack.prepend(window);
156 if (window->isRaster()) {
157 m_rasterSurfaces.ref();
158 setDirty(window->geometry());
159 }
160
161 QWindow *w = topWindow();
162 QWindowSystemInterface::handleWindowActivated(w);
163 topWindowChanged(w);
164 }
165
removeWindow(QAndroidPlatformWindow * window)166 void QAndroidPlatformScreen::removeWindow(QAndroidPlatformWindow *window)
167 {
168 if (window->parent() && window->isRaster())
169 return;
170
171
172 Q_ASSERT(m_windowStack.contains(window));
173 m_windowStack.removeOne(window);
174 Q_ASSERT(!m_windowStack.contains(window));
175
176 if (window->isRaster()) {
177 m_rasterSurfaces.deref();
178 setDirty(window->geometry());
179 }
180
181 QWindow *w = topWindow();
182 QWindowSystemInterface::handleWindowActivated(w);
183 topWindowChanged(w);
184 }
185
raise(QAndroidPlatformWindow * window)186 void QAndroidPlatformScreen::raise(QAndroidPlatformWindow *window)
187 {
188 if (window->parent() && window->isRaster())
189 return;
190
191 int index = m_windowStack.indexOf(window);
192 if (index <= 0)
193 return;
194 m_windowStack.move(index, 0);
195 if (window->isRaster()) {
196 setDirty(window->geometry());
197 }
198 QWindow *w = topWindow();
199 QWindowSystemInterface::handleWindowActivated(w);
200 topWindowChanged(w);
201 }
202
lower(QAndroidPlatformWindow * window)203 void QAndroidPlatformScreen::lower(QAndroidPlatformWindow *window)
204 {
205 if (window->parent() && window->isRaster())
206 return;
207
208 int index = m_windowStack.indexOf(window);
209 if (index == -1 || index == (m_windowStack.size() - 1))
210 return;
211 m_windowStack.move(index, m_windowStack.size() - 1);
212 if (window->isRaster()) {
213 setDirty(window->geometry());
214 }
215 QWindow *w = topWindow();
216 QWindowSystemInterface::handleWindowActivated(w);
217 topWindowChanged(w);
218 }
219
scheduleUpdate()220 void QAndroidPlatformScreen::scheduleUpdate()
221 {
222 if (!m_updatePending) {
223 m_updatePending = true;
224 QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest));
225 }
226 }
227
setDirty(const QRect & rect)228 void QAndroidPlatformScreen::setDirty(const QRect &rect)
229 {
230 QRect intersection = rect.intersected(m_availableGeometry);
231 m_dirtyRect |= intersection;
232 scheduleUpdate();
233 }
234
setPhysicalSize(const QSize & size)235 void QAndroidPlatformScreen::setPhysicalSize(const QSize &size)
236 {
237 m_physicalSize = size;
238 }
239
setSize(const QSize & size)240 void QAndroidPlatformScreen::setSize(const QSize &size)
241 {
242 m_size = size;
243 QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), geometry(), availableGeometry());
244 }
245
setAvailableGeometry(const QRect & rect)246 void QAndroidPlatformScreen::setAvailableGeometry(const QRect &rect)
247 {
248 QMutexLocker lock(&m_surfaceMutex);
249 if (m_availableGeometry == rect)
250 return;
251
252 QRect oldGeometry = m_availableGeometry;
253
254 m_availableGeometry = rect;
255 QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), geometry(), availableGeometry());
256 resizeMaximizedWindows();
257
258 if (oldGeometry.width() == 0 && oldGeometry.height() == 0 && rect.width() > 0 && rect.height() > 0) {
259 QList<QWindow *> windows = QGuiApplication::allWindows();
260 for (int i = 0; i < windows.size(); ++i) {
261 QWindow *w = windows.at(i);
262 if (w->handle()) {
263 QRect geometry = w->handle()->geometry();
264 if (geometry.width() > 0 && geometry.height() > 0)
265 QWindowSystemInterface::handleExposeEvent(w, QRect(QPoint(0, 0), geometry.size()));
266 }
267 }
268 }
269
270 if (m_id != -1) {
271 releaseSurface();
272 QtAndroid::setSurfaceGeometry(m_id, rect);
273 }
274 }
275
applicationStateChanged(Qt::ApplicationState state)276 void QAndroidPlatformScreen::applicationStateChanged(Qt::ApplicationState state)
277 {
278 for (QAndroidPlatformWindow *w : qAsConst(m_windowStack))
279 w->applicationStateChanged(state);
280
281 if (state <= Qt::ApplicationHidden) {
282 lockSurface();
283 QtAndroid::destroySurface(m_id);
284 m_id = -1;
285 releaseSurface();
286 unlockSurface();
287 }
288 }
289
topWindowChanged(QWindow * w)290 void QAndroidPlatformScreen::topWindowChanged(QWindow *w)
291 {
292 QtAndroidMenu::setActiveTopLevelWindow(w);
293
294 if (w != 0) {
295 QAndroidPlatformWindow *platformWindow = static_cast<QAndroidPlatformWindow *>(w->handle());
296 if (platformWindow != 0)
297 platformWindow->updateStatusBarVisibility();
298 }
299 }
300
rasterSurfaces()301 int QAndroidPlatformScreen::rasterSurfaces()
302 {
303 return m_rasterSurfaces;
304 }
305
doRedraw()306 void QAndroidPlatformScreen::doRedraw()
307 {
308 PROFILE_SCOPE;
309 if (!QtAndroid::activity())
310 return;
311
312 if (m_dirtyRect.isEmpty())
313 return;
314
315 // Stop if there are no visible raster windows. If we only have RasterGLSurface
316 // windows that have renderToTexture children (i.e. they need the OpenGL path) then
317 // we do not need an overlay surface.
318 bool hasVisibleRasterWindows = false;
319 for (QAndroidPlatformWindow *window : qAsConst(m_windowStack)) {
320 if (window->window()->isVisible() && window->isRaster() && !qt_window_private(window->window())->compositing) {
321 hasVisibleRasterWindows = true;
322 break;
323 }
324 }
325 if (!hasVisibleRasterWindows) {
326 lockSurface();
327 if (m_id != -1) {
328 QtAndroid::destroySurface(m_id);
329 releaseSurface();
330 m_id = -1;
331 }
332 unlockSurface();
333 return;
334 }
335 QMutexLocker lock(&m_surfaceMutex);
336 if (m_id == -1 && m_rasterSurfaces) {
337 m_id = QtAndroid::createSurface(this, m_availableGeometry, true, m_depth);
338 AndroidDeadlockProtector protector;
339 if (!protector.acquire())
340 return;
341 m_surfaceWaitCondition.wait(&m_surfaceMutex);
342 }
343
344 if (!m_nativeSurface)
345 return;
346
347 ANativeWindow_Buffer nativeWindowBuffer;
348 ARect nativeWindowRect;
349 nativeWindowRect.top = m_dirtyRect.top();
350 nativeWindowRect.left = m_dirtyRect.left();
351 nativeWindowRect.bottom = m_dirtyRect.bottom() + 1; // for some reason that I don't understand the QRect bottom needs to +1 to be the same with ARect bottom
352 nativeWindowRect.right = m_dirtyRect.right() + 1; // same for the right
353
354 int ret;
355 if ((ret = ANativeWindow_lock(m_nativeSurface, &nativeWindowBuffer, &nativeWindowRect)) < 0) {
356 qWarning() << "ANativeWindow_lock() failed! error=" << ret;
357 return;
358 }
359
360 int bpp = 4;
361 QImage::Format format = QImage::Format_RGBA8888_Premultiplied;
362 if (nativeWindowBuffer.format == WINDOW_FORMAT_RGB_565) {
363 bpp = 2;
364 format = QImage::Format_RGB16;
365 }
366
367 QImage screenImage(reinterpret_cast<uchar *>(nativeWindowBuffer.bits)
368 , nativeWindowBuffer.width, nativeWindowBuffer.height
369 , nativeWindowBuffer.stride * bpp , format);
370
371 QPainter compositePainter(&screenImage);
372 compositePainter.setCompositionMode(QPainter::CompositionMode_Source);
373
374 QRegion visibleRegion(m_dirtyRect);
375 for (QAndroidPlatformWindow *window : qAsConst(m_windowStack)) {
376 if (!window->window()->isVisible()
377 || qt_window_private(window->window())->compositing
378 || !window->isRaster())
379 continue;
380
381 for (const QRect &rect : std::vector<QRect>(visibleRegion.begin(), visibleRegion.end())) {
382 QRect targetRect = window->geometry();
383 targetRect &= rect;
384
385 if (targetRect.isNull())
386 continue;
387
388 visibleRegion -= targetRect;
389 QRect windowRect = targetRect.translated(-window->geometry().topLeft());
390 QAndroidPlatformBackingStore *backingStore = static_cast<QAndroidPlatformWindow *>(window)->backingStore();
391 if (backingStore)
392 compositePainter.drawImage(targetRect.topLeft(), backingStore->toImage(), windowRect);
393 }
394 }
395
396 for (const QRect &rect : visibleRegion)
397 compositePainter.fillRect(rect, QColor(Qt::transparent));
398
399 ret = ANativeWindow_unlockAndPost(m_nativeSurface);
400 if (ret >= 0)
401 m_dirtyRect = QRect();
402 }
403
404 static const int androidLogicalDpi = 72;
405
logicalDpi() const406 QDpi QAndroidPlatformScreen::logicalDpi() const
407 {
408 qreal lDpi = QtAndroid::scaledDensity() * androidLogicalDpi;
409 return QDpi(lDpi, lDpi);
410 }
411
logicalBaseDpi() const412 QDpi QAndroidPlatformScreen::logicalBaseDpi() const
413 {
414 return QDpi(androidLogicalDpi, androidLogicalDpi);
415 }
416
orientation() const417 Qt::ScreenOrientation QAndroidPlatformScreen::orientation() const
418 {
419 return QAndroidPlatformIntegration::m_orientation;
420 }
421
nativeOrientation() const422 Qt::ScreenOrientation QAndroidPlatformScreen::nativeOrientation() const
423 {
424 return QAndroidPlatformIntegration::m_nativeOrientation;
425 }
426
surfaceChanged(JNIEnv * env,jobject surface,int w,int h)427 void QAndroidPlatformScreen::surfaceChanged(JNIEnv *env, jobject surface, int w, int h)
428 {
429 lockSurface();
430 if (surface && w > 0 && h > 0) {
431 releaseSurface();
432 m_nativeSurface = ANativeWindow_fromSurface(env, surface);
433 QMetaObject::invokeMethod(this, "setDirty", Qt::QueuedConnection, Q_ARG(QRect, QRect(0, 0, w, h)));
434 } else {
435 releaseSurface();
436 }
437 unlockSurface();
438 m_surfaceWaitCondition.wakeOne();
439 }
440
releaseSurface()441 void QAndroidPlatformScreen::releaseSurface()
442 {
443 if (m_nativeSurface) {
444 ANativeWindow_release(m_nativeSurface);
445 m_nativeSurface = 0;
446 }
447 }
448
449 QT_END_NAMESPACE
450