Android: Give raster windows their own surface, and flush via RHI

Refactored platform windows on Android so that all window
types, including raster windows, have their own surface to
draw on.

Raster windows now flush the backing-store via RHI/OpenGL.

As a drive by, update to newer JNI syntax where appropriate.

Task-number: QTBUG-116187
Change-Id: I3b764b7126abf53556750b0ccbb7d27efe007bc1
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
This commit is contained in:
Tinja Paavoseppä 2023-11-06 13:02:10 +02:00
parent 76b58be4b8
commit 2020ce5fd2
19 changed files with 108 additions and 531 deletions

View File

@ -377,7 +377,7 @@ class QtNative
// application methods
// surface methods
public static native void setSurface(int id, Object surface, int w, int h);
public static native void setSurface(int id, Object surface);
// surface methods
// window methods

View File

@ -50,13 +50,13 @@ class QtSurface extends SurfaceView implements SurfaceHolder.Callback
if (width < 1 || height < 1)
return;
QtNative.setSurface(getId(), holder.getSurface(), width, height);
QtNative.setSurface(getId(), holder.getSurface());
}
@Override
public void surfaceDestroyed(SurfaceHolder holder)
{
QtNative.setSurface(getId(), null, 0, 0);
QtNative.setSurface(getId(), null);
}
@Override

View File

@ -641,6 +641,8 @@ endif()
qt_internal_extend_target(Gui CONDITION ANDROID
SOURCES
platform/android/qandroidnativeinterface.cpp
painting/qrasterbackingstore.cpp painting/qrasterbackingstore_p.h
painting/qrhibackingstore.cpp painting/qrhibackingstore_p.h
)
qt_internal_extend_target(Gui CONDITION ANDROID AND (TEST_architecture_arch STREQUAL arm64 OR TEST_architecture_arch STREQUAL arm)

View File

@ -17,13 +17,11 @@ qt_internal_add_plugin(QAndroidIntegrationPlugin
androidjniinput.cpp androidjniinput.h
androidjnimain.cpp androidjnimain.h
androidjnimenu.cpp androidjnimenu.h
androidsurfaceclient.h
main.cpp
qandroidassetsfileenginehandler.cpp qandroidassetsfileenginehandler.h
qandroideventdispatcher.cpp qandroideventdispatcher.h
qandroidinputcontext.cpp qandroidinputcontext.h
qandroidplatformaccessibility.cpp qandroidplatformaccessibility.h
qandroidplatformbackingstore.cpp qandroidplatformbackingstore.h
qandroidplatformclipboard.cpp qandroidplatformclipboard.h
qandroidplatformdialoghelpers.cpp qandroidplatformdialoghelpers.h
qandroidplatformfiledialoghelper.cpp qandroidplatformfiledialoghelper.h

View File

@ -18,6 +18,7 @@
#include "qandroidplatformdialoghelpers.h"
#include "qandroidplatformintegration.h"
#include "qandroidplatformclipboard.h"
#include "qandroidplatformwindow.h"
#include <android/api-level.h>
#include <android/asset_manager_jni.h>
@ -69,7 +70,7 @@ static void *m_mainLibraryHnd = nullptr;
static QList<QByteArray> m_applicationParams;
static sem_t m_exitSemaphore, m_terminateSemaphore;
QHash<int, AndroidSurfaceClient *> m_surfaces;
QHash<int, QAndroidPlatformWindow *> m_surfaces;
Q_CONSTINIT static QBasicMutex m_surfacesMutex;
@ -323,7 +324,7 @@ namespace QtAndroid
return QJniObject::callStaticMethod<jint>("android/view/View", "generateViewId", "()I");
}
int createSurface(AndroidSurfaceClient *client, const QRect &geometry, bool onTop, int imageDepth)
int createSurface(QAndroidPlatformWindow *window, const QRect &geometry, bool onTop, int imageDepth)
{
QJniEnvironment env;
if (!env.jniEnv())
@ -331,7 +332,7 @@ namespace QtAndroid
m_surfacesMutex.lock();
jint surfaceId = generateViewId();
m_surfaces[surfaceId] = client;
m_surfaces[surfaceId] = window;
m_surfacesMutex.unlock();
jint x = 0, y = 0, w = -1, h = -1;
@ -594,8 +595,11 @@ static void terminateQt(JNIEnv *env, jclass /*clazz*/)
sem_post(&m_exitSemaphore);
}
static void setSurface(JNIEnv *env, jobject /*thiz*/, jint id, jobject jSurface, jint w, jint h)
static void setSurface(JNIEnv *env, jobject thiz, jint id, jobject jSurface)
{
Q_UNUSED(env);
Q_UNUSED(thiz);
QMutexLocker lock(&m_surfacesMutex);
const auto &it = m_surfaces.find(id);
if (it == m_surfaces.end())
@ -603,7 +607,7 @@ static void setSurface(JNIEnv *env, jobject /*thiz*/, jint id, jobject jSurface,
auto surfaceClient = it.value();
if (surfaceClient)
surfaceClient->surfaceChanged(env, jSurface, w, h);
surfaceClient->onSurfaceChanged(jSurface);
}
static void setDisplayMetrics(JNIEnv * /*env*/, jclass /*clazz*/, jint screenWidthPixels,
@ -658,10 +662,6 @@ static void updateWindow(JNIEnv */*env*/, jobject /*thiz*/)
QWindowSystemInterface::handleExposeEvent(w, QRegion(QRect(QPoint(), w->geometry().size())));
}
}
QAndroidPlatformScreen *screen = static_cast<QAndroidPlatformScreen *>(m_androidPlatformIntegration->screen());
if (screen->rasterSurfaces())
QMetaObject::invokeMethod(screen, "setDirty", Qt::QueuedConnection, Q_ARG(QRect,screen->geometry()));
}
static void updateApplicationState(JNIEnv */*env*/, jobject /*thiz*/, jint state)
@ -802,7 +802,7 @@ static JNINativeMethod methods[] = {
{ "quitQtCoreApplication", "()V", (void *)quitQtCoreApplication },
{ "terminateQt", "()V", (void *)terminateQt },
{ "waitForServiceSetup", "()V", (void *)waitForServiceSetup },
{ "setSurface", "(ILjava/lang/Object;II)V", (void *)setSurface },
{ "setSurface", "(ILjava/lang/Object;)V", (void *)setSurface },
{ "updateWindow", "()V", (void *)updateWindow },
{ "updateApplicationState", "(I)V", (void *)updateApplicationState },
{ "onActivityResult", "(IILandroid/content/Intent;)V", (void *)onActivityResult },

View File

@ -23,7 +23,7 @@ class QAndroidPlatformIntegration;
class QWidget;
class QString;
class QWindow;
class AndroidSurfaceClient;
class QAndroidPlatformWindow;
class QBasicMutex;
Q_DECLARE_JNI_CLASS(QtActivityDelegate, "org/qtproject/qt/android/QtActivityDelegate")
@ -37,7 +37,7 @@ namespace QtAndroid
void setAndroidPlatformIntegration(QAndroidPlatformIntegration *androidPlatformIntegration);
void setQtThread(QThread *thread);
int createSurface(AndroidSurfaceClient * client, const QRect &geometry, bool onTop, int imageDepth);
int createSurface(QAndroidPlatformWindow *window, const QRect &geometry, bool onTop, int imageDepth);
int insertNativeView(QtJniTypes::View view, const QRect &geometry);
void setViewVisibility(jobject view, bool visible);
void setSurfaceGeometry(int surfaceId, const QRect &geometry);

View File

@ -1,24 +0,0 @@
// Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef ANDROIDSURFACECLIENT_H
#define ANDROIDSURFACECLIENT_H
#include <QMutex>
#include <jni.h>
QT_BEGIN_NAMESPACE
class AndroidSurfaceClient
{
public:
virtual void surfaceChanged(JNIEnv *jniEnv, jobject surface, int w, int h) = 0;
void lockSurface() { m_surfaceMutex.lock(); }
void unlockSurface() { m_surfaceMutex.unlock(); }
protected:
QMutex m_surfaceMutex;
};
QT_END_NAMESPACE
#endif // ANDROIDSURFACECLIENT_H

View File

@ -1,48 +0,0 @@
// Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qandroidplatformbackingstore.h"
#include "qandroidplatformscreen.h"
#include "qandroidplatformwindow.h"
#include <qpa/qplatformscreen.h>
QT_BEGIN_NAMESPACE
QAndroidPlatformBackingStore::QAndroidPlatformBackingStore(QWindow *window)
: QPlatformBackingStore(window)
{
if (window->handle())
setBackingStore(window);
}
QPaintDevice *QAndroidPlatformBackingStore::paintDevice()
{
return &m_image;
}
void QAndroidPlatformBackingStore::flush(QWindow *window, const QRegion &region, const QPoint &offset)
{
Q_UNUSED(offset);
auto *platformWindow = static_cast<QAndroidPlatformWindow *>(window->handle());
if (!platformWindow->backingStore())
setBackingStore(window);
platformWindow->repaint(region);
}
void QAndroidPlatformBackingStore::resize(const QSize &size, const QRegion &staticContents)
{
Q_UNUSED(staticContents);
if (m_image.size() != size)
m_image = QImage(size, window()->screen()->handle()->format());
}
void QAndroidPlatformBackingStore::setBackingStore(QWindow *window)
{
(static_cast<QAndroidPlatformWindow *>(window->handle()))->setBackingStore(this);
}
QT_END_NAMESPACE

View File

@ -1,28 +0,0 @@
// Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDPLATFORMBACKINGSTORE_H
#define QANDROIDPLATFORMBACKINGSTORE_H
#include <qpa/qplatformbackingstore.h>
#include <qpa/qwindowsysteminterface.h>
QT_BEGIN_NAMESPACE
class QAndroidPlatformBackingStore : public QPlatformBackingStore
{
public:
explicit QAndroidPlatformBackingStore(QWindow *window);
QPaintDevice *paintDevice() override;
void flush(QWindow *window, const QRegion &region, const QPoint &offset) override;
void resize(const QSize &size, const QRegion &staticContents) override;
QImage toImage() const override { return m_image; }
void setBackingStore(QWindow *window);
protected:
QImage m_image;
};
QT_END_NAMESPACE
#endif // QANDROIDPLATFORMBACKINGSTORE_H

View File

@ -4,7 +4,6 @@
#ifndef QANDROIDPLATFORMFOREIGNWINDOW_H
#define QANDROIDPLATFORMFOREIGNWINDOW_H
#include "androidsurfaceclient.h"
#include "qandroidplatformwindow.h"
#include <QtCore/QJniObject>

View File

@ -9,7 +9,6 @@
#include "qabstracteventdispatcher.h"
#include "qandroideventdispatcher.h"
#include "qandroidplatformaccessibility.h"
#include "qandroidplatformbackingstore.h"
#include "qandroidplatformclipboard.h"
#include "qandroidplatformfontdatabase.h"
#include "qandroidplatformforeignwindow.h"
@ -29,6 +28,7 @@
#include <QtGui/private/qeglpbuffer_p.h>
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/private/qoffscreensurface_p.h>
#include <QtGui/private/qrhibackingstore_p.h>
#include <qpa/qplatformoffscreensurface.h>
#include <qpa/qplatformwindow.h>
#include <qpa/qwindowsysteminterface.h>
@ -323,6 +323,9 @@ bool QAndroidPlatformIntegration::hasCapability(Capability cap) const
case RasterGLSurface: return QtAndroidPrivate::activity().isValid();
case TopStackedNativeChildWindows: return false;
case MaximizeUsingFullscreenGeometry: return true;
// FIXME QTBUG-118849 - we do not implement grabWindow() anymore, calling it will return
// a null QPixmap also for raster windows - for OpenGL windows this was always true
case ScreenWindowGrabbing: return false;
default:
return QPlatformIntegration::hasCapability(cap);
}
@ -333,7 +336,7 @@ QPlatformBackingStore *QAndroidPlatformIntegration::createPlatformBackingStore(Q
if (!QtAndroidPrivate::activity().isValid())
return nullptr;
return new QAndroidPlatformBackingStore(window);
return new QRhiBackingStore(window);
}
QPlatformOpenGLContext *QAndroidPlatformIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const

View File

@ -24,41 +24,19 @@ QT_BEGIN_NAMESPACE
QAndroidPlatformOpenGLWindow::QAndroidPlatformOpenGLWindow(QWindow *window, EGLDisplay display)
:QAndroidPlatformWindow(window), m_eglDisplay(display)
{
if (window->surfaceType() == QSurface::RasterSurface)
window->setSurfaceType(QSurface::OpenGLSurface);
}
QAndroidPlatformOpenGLWindow::~QAndroidPlatformOpenGLWindow()
{
m_surfaceWaitCondition.wakeOne();
lockSurface();
if (m_nativeSurfaceId != -1)
QtAndroid::destroySurface(m_nativeSurfaceId);
destroySurface();
clearEgl();
unlockSurface();
}
void QAndroidPlatformOpenGLWindow::repaint(const QRegion &region)
{
// This is only for real raster top-level windows. Stop in all other cases.
if ((window()->surfaceType() == QSurface::RasterGLSurface && qt_window_private(window())->compositing)
|| window()->surfaceType() == QSurface::OpenGLSurface
|| QAndroidPlatformWindow::parent())
return;
QRect currentGeometry = geometry();
QRect dirtyClient = region.boundingRect();
QRect dirtyRegion(currentGeometry.left() + dirtyClient.left(),
currentGeometry.top() + dirtyClient.top(),
dirtyClient.width(),
dirtyClient.height());
QRect mOldGeometryLocal = m_oldGeometry;
m_oldGeometry = currentGeometry;
// If this is a move, redraw the previous location
if (mOldGeometryLocal != currentGeometry)
platformScreen()->setDirty(mOldGeometryLocal);
platformScreen()->setDirty(dirtyRegion);
}
void QAndroidPlatformOpenGLWindow::setGeometry(const QRect &rect)
{
if (rect == geometry())
@ -67,8 +45,7 @@ void QAndroidPlatformOpenGLWindow::setGeometry(const QRect &rect)
m_oldGeometry = geometry();
QAndroidPlatformWindow::setGeometry(rect);
if (m_nativeSurfaceId != -1)
QtAndroid::setSurfaceGeometry(m_nativeSurfaceId, rect);
setSurfaceGeometry(rect);
QRect availableGeometry = screen()->availableGeometry();
if (rect.width() > 0
@ -77,9 +54,6 @@ void QAndroidPlatformOpenGLWindow::setGeometry(const QRect &rect)
&& availableGeometry.height() > 0) {
QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), rect.size()));
}
if (rect.topLeft() != m_oldGeometry.topLeft())
repaint(QRegion(rect));
}
EGLSurface QAndroidPlatformOpenGLWindow::eglSurface(EGLConfig config)
@ -94,8 +68,7 @@ EGLSurface QAndroidPlatformOpenGLWindow::eglSurface(EGLConfig config)
if (!protector.acquire())
return m_eglSurface;
const bool windowStaysOnTop = bool(window()->flags() & Qt::WindowStaysOnTopHint);
m_nativeSurfaceId = QtAndroid::createSurface(this, geometry(), windowStaysOnTop, 32);
createSurface();
m_surfaceWaitCondition.wait(&m_surfaceMutex);
}
@ -127,10 +100,7 @@ void QAndroidPlatformOpenGLWindow::applicationStateChanged(Qt::ApplicationState
QAndroidPlatformWindow::applicationStateChanged(state);
if (state <= Qt::ApplicationHidden) {
lockSurface();
if (m_nativeSurfaceId != -1) {
QtAndroid::destroySurface(m_nativeSurfaceId);
m_nativeSurfaceId = -1;
}
destroySurface();
clearEgl();
unlockSurface();
}
@ -173,24 +143,4 @@ void QAndroidPlatformOpenGLWindow::clearEgl()
}
}
void QAndroidPlatformOpenGLWindow::surfaceChanged(JNIEnv *jniEnv, jobject surface, int w, int h)
{
Q_UNUSED(jniEnv);
Q_UNUSED(w);
Q_UNUSED(h);
lockSurface();
m_androidSurfaceObject = surface;
if (surface) // wait until we have a valid surface to draw into
m_surfaceWaitCondition.wakeOne();
unlockSurface();
if (surface) {
// repaint the window, when we have a valid surface
QRect availableGeometry = screen()->availableGeometry();
if (geometry().width() > 0 && geometry().height() > 0 && availableGeometry.width() > 0 && availableGeometry.height() > 0)
QWindowSystemInterface::handleExposeEvent(window(), QRegion(QRect(QPoint(), geometry().size())));
}
}
QT_END_NAMESPACE

View File

@ -5,7 +5,6 @@
#ifndef QANDROIDPLATFORMOPENGLWINDOW_H
#define QANDROIDPLATFORMOPENGLWINDOW_H
#include "androidsurfaceclient.h"
#include "qandroidplatformwindow.h"
#include <QWaitCondition>
@ -16,7 +15,7 @@
QT_BEGIN_NAMESPACE
class QAndroidPlatformOpenGLWindow : public QAndroidPlatformWindow, public AndroidSurfaceClient
class QAndroidPlatformOpenGLWindow : public QAndroidPlatformWindow
{
public:
explicit QAndroidPlatformOpenGLWindow(QWindow *window, EGLDisplay display);
@ -30,10 +29,7 @@ public:
void applicationStateChanged(Qt::ApplicationState) override;
void repaint(const QRegion &region) override;
protected:
void surfaceChanged(JNIEnv *jniEnv, jobject surface, int w, int h) override;
void createEgl(EGLConfig config);
void clearEgl();
@ -42,9 +38,6 @@ private:
EGLSurface m_eglSurface = EGL_NO_SURFACE;
EGLNativeWindowType m_nativeWindow = nullptr;
int m_nativeSurfaceId = -1;
QJniObject m_androidSurfaceObject;
QWaitCondition m_surfaceWaitCondition;
QSurfaceFormat m_format;
QRect m_oldGeometry;
};

View File

@ -8,7 +8,6 @@
#include <qpa/qwindowsysteminterface.h>
#include "qandroidplatformscreen.h"
#include "qandroidplatformbackingstore.h"
#include "qandroidplatformintegration.h"
#include "qandroidplatformwindow.h"
#include "androidjnimain.h"
@ -131,11 +130,6 @@ QAndroidPlatformScreen::QAndroidPlatformScreen(const QJniObject &displayObject)
QAndroidPlatformScreen::~QAndroidPlatformScreen()
{
if (m_surfaceId != -1) {
QtAndroid::destroySurface(m_surfaceId);
m_surfaceWaitCondition.wakeOne();
releaseSurface();
}
}
QWindow *QAndroidPlatformScreen::topWindow() const
@ -159,16 +153,6 @@ QWindow *QAndroidPlatformScreen::topLevelAt(const QPoint &p) const
return 0;
}
bool QAndroidPlatformScreen::event(QEvent *event)
{
if (event->type() == QEvent::UpdateRequest) {
doRedraw();
m_updatePending = false;
return true;
}
return QObject::event(event);
}
void QAndroidPlatformScreen::addWindow(QAndroidPlatformWindow *window)
{
if (window->parent() && window->isRaster())
@ -178,10 +162,6 @@ void QAndroidPlatformScreen::addWindow(QAndroidPlatformWindow *window)
return;
m_windowStack.prepend(window);
if (window->isRaster()) {
m_rasterSurfaces.ref();
setDirty(window->geometry());
}
QWindow *w = topWindow();
QWindowSystemInterface::handleFocusWindowChanged(w, Qt::ActiveWindowFocusReason);
@ -198,11 +178,6 @@ void QAndroidPlatformScreen::removeWindow(QAndroidPlatformWindow *window)
if (m_windowStack.contains(window))
qWarning() << "Failed to remove window";
if (window->isRaster()) {
m_rasterSurfaces.deref();
setDirty(window->geometry());
}
QWindow *w = topWindow();
QWindowSystemInterface::handleFocusWindowChanged(w, Qt::ActiveWindowFocusReason);
topWindowChanged(w);
@ -210,16 +185,11 @@ void QAndroidPlatformScreen::removeWindow(QAndroidPlatformWindow *window)
void QAndroidPlatformScreen::raise(QAndroidPlatformWindow *window)
{
if (window->parent() && window->isRaster())
return;
int index = m_windowStack.indexOf(window);
if (index <= 0)
return;
m_windowStack.move(index, 0);
if (window->isRaster()) {
setDirty(window->geometry());
}
QWindow *w = topWindow();
QWindowSystemInterface::handleFocusWindowChanged(w, Qt::ActiveWindowFocusReason);
topWindowChanged(w);
@ -227,36 +197,16 @@ void QAndroidPlatformScreen::raise(QAndroidPlatformWindow *window)
void QAndroidPlatformScreen::lower(QAndroidPlatformWindow *window)
{
if (window->parent() && window->isRaster())
return;
int index = m_windowStack.indexOf(window);
if (index == -1 || index == (m_windowStack.size() - 1))
return;
m_windowStack.move(index, m_windowStack.size() - 1);
if (window->isRaster()) {
setDirty(window->geometry());
}
QWindow *w = topWindow();
QWindowSystemInterface::handleFocusWindowChanged(w, Qt::ActiveWindowFocusReason);
topWindowChanged(w);
}
void QAndroidPlatformScreen::scheduleUpdate()
{
if (!m_updatePending) {
m_updatePending = true;
QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest));
}
}
void QAndroidPlatformScreen::setDirty(const QRect &rect)
{
QRect intersection = rect.intersected(m_availableGeometry);
m_dirtyRect |= intersection;
scheduleUpdate();
}
void QAndroidPlatformScreen::setPhysicalSize(const QSize &size)
{
m_physicalSize = size;
@ -306,7 +256,6 @@ void QAndroidPlatformScreen::setOrientation(Qt::ScreenOrientation orientation)
void QAndroidPlatformScreen::setAvailableGeometry(const QRect &rect)
{
QMutexLocker lock(&m_surfaceMutex);
if (m_availableGeometry == rect)
return;
@ -327,25 +276,12 @@ void QAndroidPlatformScreen::setAvailableGeometry(const QRect &rect)
}
}
}
if (m_surfaceId != -1) {
releaseSurface();
QtAndroid::setSurfaceGeometry(m_surfaceId, rect);
}
}
void QAndroidPlatformScreen::applicationStateChanged(Qt::ApplicationState state)
{
for (QAndroidPlatformWindow *w : std::as_const(m_windowStack))
w->applicationStateChanged(state);
if (state <= Qt::ApplicationHidden) {
lockSurface();
QtAndroid::destroySurface(m_surfaceId);
m_surfaceId = -1;
releaseSurface();
unlockSurface();
}
}
void QAndroidPlatformScreen::topWindowChanged(QWindow *w)
@ -359,133 +295,6 @@ void QAndroidPlatformScreen::topWindowChanged(QWindow *w)
}
}
int QAndroidPlatformScreen::rasterSurfaces()
{
return m_rasterSurfaces;
}
void QAndroidPlatformScreen::doRedraw(QImage* screenGrabImage)
{
PROFILE_SCOPE;
if (!QtAndroidPrivate::activity().isValid())
return;
if (m_dirtyRect.isEmpty())
return;
// Stop if there are no visible raster windows. If we only have RasterGLSurface
// windows that have renderToTexture children (i.e. they need the OpenGL path) then
// we do not need an overlay surface.
bool hasVisibleRasterWindows = false;
for (QAndroidPlatformWindow *window : std::as_const(m_windowStack)) {
if (window->window()->isVisible() && window->isRaster() && !qt_window_private(window->window())->compositing) {
hasVisibleRasterWindows = true;
break;
}
}
if (!hasVisibleRasterWindows) {
lockSurface();
if (m_surfaceId != -1) {
QtAndroid::destroySurface(m_surfaceId);
releaseSurface();
m_surfaceId = -1;
}
unlockSurface();
return;
}
QMutexLocker lock(&m_surfaceMutex);
if (m_surfaceId == -1 && m_rasterSurfaces) {
m_surfaceId = QtAndroid::createSurface(this, geometry(), true, m_depth);
AndroidDeadlockProtector protector;
if (!protector.acquire())
return;
m_surfaceWaitCondition.wait(&m_surfaceMutex);
}
if (!m_nativeSurface)
return;
ANativeWindow_Buffer nativeWindowBuffer;
ARect nativeWindowRect;
nativeWindowRect.top = m_dirtyRect.top();
nativeWindowRect.left = m_dirtyRect.left();
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
nativeWindowRect.right = m_dirtyRect.right() + 1; // same for the right
int ret;
if ((ret = ANativeWindow_lock(m_nativeSurface, &nativeWindowBuffer, &nativeWindowRect)) < 0) {
qWarning() << "ANativeWindow_lock() failed! error=" << ret;
return;
}
int bpp = 4;
if (nativeWindowBuffer.format == WINDOW_FORMAT_RGB_565) {
bpp = 2;
m_pixelFormat = QImage::Format_RGB16;
}
QImage screenImage(reinterpret_cast<uchar *>(nativeWindowBuffer.bits)
, nativeWindowBuffer.width, nativeWindowBuffer.height
, nativeWindowBuffer.stride * bpp , m_pixelFormat);
QPainter compositePainter(&screenImage);
compositePainter.setCompositionMode(QPainter::CompositionMode_Source);
QRegion visibleRegion(m_dirtyRect);
for (QAndroidPlatformWindow *window : std::as_const(m_windowStack)) {
if (!window->window()->isVisible()
|| qt_window_private(window->window())->compositing
|| !window->isRaster())
continue;
for (const QRect &rect : std::vector<QRect>(visibleRegion.begin(), visibleRegion.end())) {
QRect targetRect = window->geometry();
targetRect &= rect;
if (targetRect.isNull())
continue;
visibleRegion -= targetRect;
QRect windowRect = targetRect.translated(-window->geometry().topLeft());
QAndroidPlatformBackingStore *backingStore = static_cast<QAndroidPlatformWindow *>(window)->backingStore();
if (backingStore)
compositePainter.drawImage(targetRect.topLeft(), backingStore->toImage(), windowRect);
}
}
for (const QRect &rect : visibleRegion)
compositePainter.fillRect(rect, QColor(Qt::transparent));
ret = ANativeWindow_unlockAndPost(m_nativeSurface);
if (ret >= 0)
m_dirtyRect = QRect();
if (screenGrabImage) {
if (screenGrabImage->size() != screenImage.size()) {
uchar* bytes = static_cast<uchar*>(malloc(screenImage.height() * screenImage.bytesPerLine()));
*screenGrabImage = QImage(bytes, screenImage.width(), screenImage.height(),
screenImage.bytesPerLine(), m_pixelFormat,
[](void* ptr){ if (ptr) free (ptr);});
}
memcpy(screenGrabImage->bits(),
screenImage.bits(),
screenImage.bytesPerLine() * screenImage.height());
}
m_repaintOccurred = true;
}
QPixmap QAndroidPlatformScreen::doScreenShot(QRect grabRect)
{
if (!m_repaintOccurred)
return QPixmap::fromImage(m_lastScreenshot.copy(grabRect));
QRect tmp = m_dirtyRect;
m_dirtyRect = geometry();
doRedraw(&m_lastScreenshot);
m_dirtyRect = tmp;
m_repaintOccurred = false;
return QPixmap::fromImage(m_lastScreenshot.copy(grabRect));
}
static const int androidLogicalDpi = 72;
QDpi QAndroidPlatformScreen::logicalDpi() const
@ -508,67 +317,4 @@ Qt::ScreenOrientation QAndroidPlatformScreen::nativeOrientation() const
{
return QAndroidPlatformIntegration::m_nativeOrientation;
}
void QAndroidPlatformScreen::surfaceChanged(JNIEnv *env, jobject surface, int w, int h)
{
lockSurface();
if (surface && w > 0 && h > 0) {
releaseSurface();
m_nativeSurface = ANativeWindow_fromSurface(env, surface);
QMetaObject::invokeMethod(this, "setDirty", Qt::QueuedConnection, Q_ARG(QRect, QRect(0, 0, w, h)));
} else {
releaseSurface();
}
unlockSurface();
m_surfaceWaitCondition.wakeOne();
}
void QAndroidPlatformScreen::releaseSurface()
{
if (m_nativeSurface) {
ANativeWindow_release(m_nativeSurface);
m_nativeSurface = 0;
}
}
/*!
This function is called when Qt needs to be able to grab the content of a window.
Returns the content of the window specified with the WId handle within the boundaries of
QRect(x, y, width, height).
*/
QPixmap QAndroidPlatformScreen::grabWindow(WId window, int x, int y, int width, int height) const
{
QRectF screenshotRect(x, y, width, height);
QWindow* wnd = 0;
if (window)
{
const auto windowList = qApp->allWindows();
for (QWindow *w : windowList)
if (w->winId() == window) {
wnd = w;
break;
}
}
if (wnd) {
const qreal factor = logicalDpi().first / androidLogicalDpi; //HighDPI factor;
QRectF wndRect = wnd->geometry();
if (wnd->parent())
wndRect.moveTopLeft(wnd->parent()->mapToGlobal(wndRect.topLeft().toPoint()));
if (!qFuzzyCompare(factor, 1))
wndRect = QRectF(wndRect.left() * factor, wndRect.top() * factor,
wndRect.width() * factor, wndRect.height() * factor);
if (!screenshotRect.isEmpty()) {
screenshotRect.moveTopLeft(wndRect.topLeft() + screenshotRect.topLeft());
screenshotRect = screenshotRect.intersected(wndRect);
} else {
screenshotRect = wndRect;
}
} else {
screenshotRect = screenshotRect.isValid() ? screenshotRect : geometry();
}
return const_cast<QAndroidPlatformScreen *>(this)->doScreenShot(screenshotRect.toRect());
}
QT_END_NAMESPACE

View File

@ -5,8 +5,6 @@
#ifndef QANDROIDPLATFORMSCREEN_H
#define QANDROIDPLATFORMSCREEN_H
#include "androidsurfaceclient.h"
#include <QList>
#include <QPainter>
#include <QTimer>
@ -15,14 +13,12 @@
#include <qpa/qplatformscreen.h>
#include <qpa/qplatformscreen_p.h>
#include <android/native_window.h>
QT_BEGIN_NAMESPACE
class QAndroidPlatformWindow;
class QAndroidPlatformScreen: public QObject,
public QPlatformScreen, public AndroidSurfaceClient,
public QPlatformScreen,
public QNativeInterface::Private::QAndroidScreen
{
Q_OBJECT
@ -44,19 +40,15 @@ public:
inline QWindow *topWindow() const;
QWindow *topLevelAt(const QPoint & p) const override;
// compositor api
void addWindow(QAndroidPlatformWindow *window);
void removeWindow(QAndroidPlatformWindow *window);
void raise(QAndroidPlatformWindow *window);
void lower(QAndroidPlatformWindow *window);
void scheduleUpdate();
void topWindowChanged(QWindow *w);
int rasterSurfaces();
int displayId() const override;
public slots:
void setDirty(const QRect &rect);
void setPhysicalSize(const QSize &size);
void setAvailableGeometry(const QRect &rect);
void setSize(const QSize &size);
@ -66,12 +58,8 @@ public slots:
void setOrientation(Qt::ScreenOrientation orientation);
protected:
bool event(QEvent *event) override;
typedef QList<QAndroidPlatformWindow *> WindowStackType;
WindowStackType m_windowStack;
QRect m_dirtyRect;
bool m_updatePending = false;
QRect m_availableGeometry;
int m_depth;
@ -88,25 +76,9 @@ private:
QDpi logicalBaseDpi() const override;
Qt::ScreenOrientation orientation() const override;
Qt::ScreenOrientation nativeOrientation() const override;
QPixmap grabWindow(WId window, int x, int y, int width, int height) const override;
void surfaceChanged(JNIEnv *env, jobject surface, int w, int h) override;
void releaseSurface();
void applicationStateChanged(Qt::ApplicationState);
QPixmap doScreenShot(QRect grabRect = QRect());
private slots:
void doRedraw(QImage *screenGrabImage = nullptr);
private:
int m_surfaceId = -1;
QAtomicInt m_rasterSurfaces = 0;
ANativeWindow* m_nativeSurface = nullptr;
QWaitCondition m_surfaceWaitCondition;
QSize m_size;
QImage m_lastScreenshot;
QImage::Format m_pixelFormat = QImage::Format_RGBA8888_Premultiplied;
bool m_repaintOccurred = false;
};
QT_END_NAMESPACE

View File

@ -18,7 +18,6 @@ QT_BEGIN_NAMESPACE
QAndroidPlatformVulkanWindow::QAndroidPlatformVulkanWindow(QWindow *window)
: QAndroidPlatformWindow(window),
m_nativeSurfaceId(-1),
m_nativeWindow(nullptr),
m_vkSurface(0),
m_createVkSurface(nullptr),
@ -29,11 +28,7 @@ QAndroidPlatformVulkanWindow::QAndroidPlatformVulkanWindow(QWindow *window)
QAndroidPlatformVulkanWindow::~QAndroidPlatformVulkanWindow()
{
m_surfaceWaitCondition.wakeOne();
lockSurface();
if (m_nativeSurfaceId != -1)
QtAndroid::destroySurface(m_nativeSurfaceId);
clearSurface();
unlockSurface();
destroyAndClearSurface();
}
void QAndroidPlatformVulkanWindow::setGeometry(const QRect &rect)
@ -44,8 +39,7 @@ void QAndroidPlatformVulkanWindow::setGeometry(const QRect &rect)
m_oldGeometry = geometry();
QAndroidPlatformWindow::setGeometry(rect);
if (m_nativeSurfaceId != -1)
QtAndroid::setSurfaceGeometry(m_nativeSurfaceId, rect);
setSurfaceGeometry(rect);
QRect availableGeometry = screen()->availableGeometry();
if (rect.width() > 0
@ -54,22 +48,13 @@ void QAndroidPlatformVulkanWindow::setGeometry(const QRect &rect)
&& availableGeometry.height() > 0) {
QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), rect.size()));
}
if (rect.topLeft() != m_oldGeometry.topLeft())
repaint(QRegion(rect));
}
void QAndroidPlatformVulkanWindow::applicationStateChanged(Qt::ApplicationState state)
{
QAndroidPlatformWindow::applicationStateChanged(state);
if (state <= Qt::ApplicationHidden) {
lockSurface();
if (m_nativeSurfaceId != -1) {
QtAndroid::destroySurface(m_nativeSurfaceId);
m_nativeSurfaceId = -1;
}
clearSurface();
unlockSurface();
destroyAndClearSurface();
}
}
@ -91,27 +76,12 @@ void QAndroidPlatformVulkanWindow::clearSurface()
}
}
void QAndroidPlatformVulkanWindow::sendExpose()
void QAndroidPlatformVulkanWindow::destroyAndClearSurface()
{
QRect availableGeometry = screen()->availableGeometry();
if (geometry().width() > 0 && geometry().height() > 0 && availableGeometry.width() > 0 && availableGeometry.height() > 0)
QWindowSystemInterface::handleExposeEvent(window(), QRegion(QRect(QPoint(), geometry().size())));
}
void QAndroidPlatformVulkanWindow::surfaceChanged(JNIEnv *jniEnv, jobject surface, int w, int h)
{
Q_UNUSED(jniEnv);
Q_UNUSED(w);
Q_UNUSED(h);
lockSurface();
m_androidSurfaceObject = surface;
if (surface)
m_surfaceWaitCondition.wakeOne();
destroySurface();
clearSurface();
unlockSurface();
if (surface)
sendExpose();
}
VkSurfaceKHR *QAndroidPlatformVulkanWindow::vkSurface()
@ -128,8 +98,7 @@ VkSurfaceKHR *QAndroidPlatformVulkanWindow::vkSurface()
AndroidDeadlockProtector protector;
if (!protector.acquire())
return &m_vkSurface;
const bool windowStaysOnTop = bool(window()->flags() & Qt::WindowStaysOnTopHint);
m_nativeSurfaceId = QtAndroid::createSurface(this, geometry(), windowStaysOnTop, 32);
createSurface();
m_surfaceWaitCondition.wait(&m_surfaceMutex);
}

View File

@ -10,7 +10,6 @@
#define VK_USE_PLATFORM_ANDROID_KHR
#include "androidsurfaceclient.h"
#include "qandroidplatformvulkaninstance.h"
#include "qandroidplatformwindow.h"
@ -20,7 +19,7 @@
QT_BEGIN_NAMESPACE
class QAndroidPlatformVulkanWindow : public QAndroidPlatformWindow, public AndroidSurfaceClient
class QAndroidPlatformVulkanWindow : public QAndroidPlatformWindow
{
public:
explicit QAndroidPlatformVulkanWindow(QWindow *window);
@ -32,17 +31,11 @@ public:
VkSurfaceKHR *vkSurface();
protected:
void surfaceChanged(JNIEnv *jniEnv, jobject surface, int w, int h) override;
private:
void sendExpose();
void clearSurface();
void destroyAndClearSurface();
int m_nativeSurfaceId;
ANativeWindow *m_nativeWindow;
QJniObject m_androidSurfaceObject;
QWaitCondition m_surfaceWaitCondition;
QSurfaceFormat m_format;
QRect m_oldGeometry;
VkSurfaceKHR m_vkSurface;

View File

@ -17,10 +17,13 @@ QT_BEGIN_NAMESPACE
Q_CONSTINIT static QBasicAtomicInt winIdGenerator = Q_BASIC_ATOMIC_INITIALIZER(0);
QAndroidPlatformWindow::QAndroidPlatformWindow(QWindow *window)
: QPlatformWindow(window)
: QPlatformWindow(window), m_androidSurfaceObject(nullptr)
{
m_windowFlags = Qt::Widget;
m_windowState = Qt::WindowNoState;
// the surfaceType is overwritten in QAndroidPlatformOpenGLWindow ctor so let's save
// the fact that it's a raster window for now
m_isRaster = window->surfaceType() == QSurface::RasterSurface;
m_windowId = winIdGenerator.fetchAndAddRelaxed(1) + 1;
setWindowState(window->windowStates());
@ -171,4 +174,44 @@ void QAndroidPlatformWindow::applicationStateChanged(Qt::ApplicationState)
QWindowSystemInterface::flushWindowSystemEvents();
}
void QAndroidPlatformWindow::createSurface()
{
const bool windowStaysOnTop = bool(window()->flags() & Qt::WindowStaysOnTopHint);
m_nativeSurfaceId = QtAndroid::createSurface(this, geometry(), windowStaysOnTop, 32);
}
void QAndroidPlatformWindow::destroySurface()
{
if (m_nativeSurfaceId != -1) {
QtAndroid::destroySurface(m_nativeSurfaceId);
m_nativeSurfaceId = -1;
}
}
void QAndroidPlatformWindow::setSurfaceGeometry(const QRect &rect)
{
if (m_nativeSurfaceId != -1)
QtAndroid::setSurfaceGeometry(m_nativeSurfaceId, rect);
}
void QAndroidPlatformWindow::sendExpose()
{
QRect availableGeometry = screen()->availableGeometry();
if (!geometry().isNull() && !availableGeometry.isNull()) {
QWindowSystemInterface::handleExposeEvent(window(),
QRegion(QRect(QPoint(), geometry().size())));
}
}
void QAndroidPlatformWindow::onSurfaceChanged(QtJniTypes::Surface surface)
{
lockSurface();
m_androidSurfaceObject = surface;
if (m_androidSurfaceObject.isValid())
m_surfaceWaitCondition.wakeOne();
unlockSurface();
if (m_androidSurfaceObject.isValid())
sendExpose();
}
QT_END_NAMESPACE

View File

@ -7,11 +7,19 @@
#include <qobject.h>
#include <qrect.h>
#include <qpa/qplatformwindow.h>
#include <QtCore/qjnienvironment.h>
#include <QtCore/qjniobject.h>
#include <QtCore/qjnitypes.h>
#include <QtCore/qloggingcategory.h>
#include <QtCore/qmutex.h>
#include <QtCore/qwaitcondition.h>
#include <jni.h>
QT_BEGIN_NAMESPACE
Q_DECLARE_JNI_CLASS(Surface, "android/view/Surface")
class QAndroidPlatformScreen;
class QAndroidPlatformBackingStore;
class QAndroidPlatformWindow: public QPlatformWindow
{
@ -39,32 +47,33 @@ public:
void propagateSizeHints() override;
void requestActivateWindow() override;
void updateSystemUiVisibility();
inline bool isRaster() const {
if (isForeignWindow())
return false;
return window()->surfaceType() == QSurface::RasterSurface
|| window()->surfaceType() == QSurface::RasterGLSurface;
}
inline bool isRaster() const { return m_isRaster; }
bool isExposed() const override;
virtual void applicationStateChanged(Qt::ApplicationState);
void setBackingStore(QAndroidPlatformBackingStore *store) { m_backingStore = store; }
QAndroidPlatformBackingStore *backingStore() const { return m_backingStore; }
virtual void repaint(const QRegion &) { }
void onSurfaceChanged(QtJniTypes::Surface surface);
protected:
void setGeometry(const QRect &rect) override;
void lockSurface() { m_surfaceMutex.lock(); }
void unlockSurface() { m_surfaceMutex.unlock(); }
void createSurface();
void destroySurface();
void setSurfaceGeometry(const QRect &rect);
void sendExpose();
protected:
Qt::WindowFlags m_windowFlags;
Qt::WindowStates m_windowState;
bool m_isRaster;
WId m_windowId;
QAndroidPlatformBackingStore *m_backingStore = nullptr;
// The Android Surface, accessed from multiple threads, guarded by m_surfaceMutex
QtJniTypes::Surface m_androidSurfaceObject;
QWaitCondition m_surfaceWaitCondition;
int m_nativeSurfaceId = -1;
QMutex m_surfaceMutex;
};
QT_END_NAMESPACE