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