Android: Don't crash when displaying multiple top-levels

While the raster platform plugin supports multiple top
level windows, this is not supported on the GL plugin,
so if you use GL or QtQuick2 in your app and use several
top levels, the app would crash with an error message.

A problem is that the top-level SurfaceView is a special
overlay View and does not support being stacked in a
layout. So instead, we let all windows share the same
GL surface and draw on top of each other. This works
fine for simple use cases.

We implement a new platform capability to make sure no
top level windows (even combobox popups and dialogs)
get non-fullscreen geometries. That has never
worked properly with the eglfs plugin.

Task-number: QTBUG-30473
Change-Id: Ia1438019638fc739cc93ffe79b46b81631254df2
Reviewed-by: Paul Olav Tvete <paul.tvete@digia.com>
This commit is contained in:
Eskil Abrahamsen Blomfeldt 2013-04-15 11:23:04 +02:00 committed by The Qt Project
parent 1770f25857
commit eb95685556
10 changed files with 128 additions and 37 deletions

View File

@ -210,6 +210,11 @@ QPlatformServices *QPlatformIntegration::services() const
\value ForeignWindows The platform allows creating QWindows which represent
native windows created by other processes or anyway created by using native
libraries.
\value NonFullScreenWindows The platform supports top-level windows which do not
fill the screen. The default implementation returns true. Returning false for
this will cause all windows, including dialogs and popups, to be resized to fill the
screen.
*/
@ -227,8 +232,7 @@ QPlatformServices *QPlatformIntegration::services() const
bool QPlatformIntegration::hasCapability(Capability cap) const
{
Q_UNUSED(cap);
return false;
return cap == NonFullScreenWindows;
}
QPlatformPixmap *QPlatformIntegration::createPlatformPixmap(QPlatformPixmap::PixelType type) const

View File

@ -90,7 +90,8 @@ public:
WindowMasks,
MultipleWindows,
ApplicationState,
ForeignWindows
ForeignWindows,
NonFullScreenWindows
};
virtual ~QPlatformIntegration() { }

View File

@ -559,7 +559,6 @@ static void setSurface(JNIEnv *env, jobject /*thiz*/, jobject jSurface)
m_surfaceMutex.unlock();
m_androidPlatformIntegration->surfaceChanged();
} else if (m_androidPlatformIntegration && sameNativeWindow) {
QAndroidOpenGLPlatformWindow *window = m_androidPlatformIntegration->primaryWindow();
QPlatformScreen *screen = m_androidPlatformIntegration->screen();
QSize size = QtAndroid::nativeWindowSize();
@ -567,13 +566,19 @@ static void setSurface(JNIEnv *env, jobject /*thiz*/, jobject jSurface)
QWindowSystemInterface::handleScreenAvailableGeometryChange(screen->screen(), geometry);
QWindowSystemInterface::handleScreenGeometryChange(screen->screen(), geometry);
if (window != 0) {
window->lock();
window->scheduleResize(size);
// Resize all top level windows, since they share the same surface
foreach (QWindow *w, QGuiApplication::topLevelWindows()) {
QAndroidOpenGLPlatformWindow *window =
static_cast<QAndroidOpenGLPlatformWindow *>(w->handle());
QWindowSystemInterface::handleExposeEvent(window->window(),
QRegion(window->window()->geometry()));
window->unlock();
if (window != 0) {
window->lock();
window->scheduleResize(size);
QWindowSystemInterface::handleExposeEvent(window->window(),
QRegion(window->window()->geometry()));
window->unlock();
}
}
m_surfaceMutex.unlock();

View File

@ -62,16 +62,16 @@ void QAndroidOpenGLContext::swapBuffers(QPlatformSurface *surface)
{
QEglFSContext::swapBuffers(surface);
QAndroidOpenGLPlatformWindow *primaryWindow = m_platformIntegration->primaryWindow();
if (primaryWindow == surface) {
primaryWindow->lock();
QSize size = primaryWindow->scheduledResize();
if (surface->surface()->surfaceClass() == QSurface::Window) {
QAndroidOpenGLPlatformWindow *window = static_cast<QAndroidOpenGLPlatformWindow *>(surface);
window->lock();
QSize size = window->scheduledResize();
if (size.isValid()) {
QRect geometry(QPoint(0, 0), size);
primaryWindow->setGeometry(geometry);
primaryWindow->scheduleResize(QSize());
window->setGeometry(geometry);
window->scheduleResize(QSize());
}
primaryWindow->unlock();
window->unlock();
}
}

View File

@ -45,11 +45,21 @@
QT_BEGIN_NAMESPACE
EGLSurface QAndroidOpenGLPlatformWindow::m_staticSurface = 0;
EGLNativeWindowType QAndroidOpenGLPlatformWindow::m_staticNativeWindow = 0;
QReadWriteLock QAndroidOpenGLPlatformWindow::m_staticSurfaceLock;
QBasicAtomicInt QAndroidOpenGLPlatformWindow::m_referenceCount = Q_BASIC_ATOMIC_INITIALIZER(0);
QAndroidOpenGLPlatformWindow::QAndroidOpenGLPlatformWindow(QWindow *window)
: QEglFSWindow(window)
{
}
QAndroidOpenGLPlatformWindow::~QAndroidOpenGLPlatformWindow()
{
destroy();
}
bool QAndroidOpenGLPlatformWindow::isExposed() const
{
return QtAndroid::nativeWindow(false) != 0 && QEglFSWindow::isExposed();
@ -60,11 +70,57 @@ void QAndroidOpenGLPlatformWindow::invalidateSurface()
QWindowSystemInterface::handleExposeEvent(window(), QRegion()); // Obscure event
QWindowSystemInterface::flushWindowSystemEvents();
QEglFSWindow::invalidateSurface();
m_window = 0;
m_surface = 0;
if (!m_referenceCount.deref()){
QWriteLocker locker(&m_staticSurfaceLock);
EGLDisplay display = (static_cast<QEglFSScreen *>(window()->screen()->handle()))->display();
eglDestroySurface(display, m_staticSurface);
m_staticSurface = 0;
m_staticNativeWindow = 0;
}
}
void QAndroidOpenGLPlatformWindow::resetSurface()
{
QEglFSWindow::resetSurface();
m_referenceCount.ref();
if (m_staticSurface == 0) {
QWriteLocker locker(&m_staticSurfaceLock);
QEglFSWindow::resetSurface();
m_staticSurface = m_surface;
m_staticNativeWindow = m_window;
} else {
QReadLocker locker(&m_staticSurfaceLock);
Q_ASSERT(m_staticSurface != m_surface);
m_window = m_staticNativeWindow;
m_surface = m_staticSurface;
}
QWindowSystemInterface::handleExposeEvent(window(), QRegion(geometry())); // Expose event
QWindowSystemInterface::flushWindowSystemEvents();
}
void QAndroidOpenGLPlatformWindow::destroy()
{
if (!m_referenceCount.deref()) {
QEglFSWindow::destroy();
} else {
m_window = 0;
m_surface = 0;
}
}
void QAndroidOpenGLPlatformWindow::raise()
{
}
void QAndroidOpenGLPlatformWindow::setVisible(bool visible)
{
QEglFSWindow::setVisible(visible);
QWindowSystemInterface::handleExposeEvent(window(), QRegion(geometry())); // Expose event
QWindowSystemInterface::flushWindowSystemEvents();
}

View File

@ -44,6 +44,7 @@
#include "qeglfswindow.h"
#include <QtCore/qmutex.h>
#include <QtCore/qreadwritelock.h>
QT_BEGIN_NAMESPACE
@ -51,6 +52,7 @@ class QAndroidOpenGLPlatformWindow : public QEglFSWindow
{
public:
QAndroidOpenGLPlatformWindow(QWindow *window);
~QAndroidOpenGLPlatformWindow();
QSize scheduledResize() const { return m_scheduledResize; }
void scheduleResize(const QSize &size) { m_scheduledResize = size; }
@ -60,12 +62,23 @@ public:
bool isExposed() const;
void raise();
void invalidateSurface();
void resetSurface();
void setVisible(bool visible);
void destroy();
private:
QSize m_scheduledResize;
QMutex m_lock;
static QReadWriteLock m_staticSurfaceLock;
static EGLSurface m_staticSurface;
static EGLNativeWindowType m_staticNativeWindow;
static QBasicAtomicInt m_referenceCount;
};
QT_END_NAMESPACE

View File

@ -96,6 +96,7 @@ QDpi QEglFSAndroidHooks::logicalDpi() const
EGLNativeWindowType QEglFSAndroidHooks::createNativeWindow(const QSize &size, const QSurfaceFormat &format)
{
Q_UNUSED(size);
ANativeWindow *window = QtAndroid::nativeWindow();
if (window != 0)
ANativeWindow_acquire(window);

View File

@ -43,6 +43,7 @@
#include "qabstracteventdispatcher.h"
#include "androidjnimain.h"
#include <QtGui/private/qpixmap_raster_p.h>
#include <QtGui/qguiapplication.h>
#include <qpa/qwindowsysteminterface.h>
#include <QThread>
#include <qpa/qplatformwindow.h>
@ -85,9 +86,6 @@ void *QAndroidPlatformNativeInterface::nativeResourceForIntegration(const QByteA
QAndroidPlatformIntegration::QAndroidPlatformIntegration(const QStringList &paramList)
: m_touchDevice(0)
#ifdef ANDROID_PLUGIN_OPENGL
, m_primaryWindow(0)
#endif
{
Q_UNUSED(paramList);
@ -116,6 +114,7 @@ bool QAndroidPlatformIntegration::hasCapability(Capability cap) const
{
switch (cap) {
case ThreadedPixmaps: return true;
case NonFullScreenWindows: return false;
default:
#ifndef ANDROID_PLUGIN_OPENGL
return QPlatformIntegration::hasCapability(cap);
@ -143,28 +142,32 @@ QAbstractEventDispatcher *QAndroidPlatformIntegration::guiThreadEventDispatcher(
#else // !ANDROID_PLUGIN_OPENGL
QPlatformWindow *QAndroidPlatformIntegration::createPlatformWindow(QWindow *window) const
{
if (m_primaryWindow != 0) {
qWarning("QAndroidPlatformIntegration::createPlatformWindow: Unsupported case: More than "
"one top-level window created.");
}
m_primaryWindow = new QAndroidOpenGLPlatformWindow(window);
m_primaryWindow->requestActivateWindow();
QAndroidOpenGLPlatformWindow *platformWindow = new QAndroidOpenGLPlatformWindow(window);
platformWindow->create();
platformWindow->requestActivateWindow();
QtAndroidMenu::setActiveTopLevelWindow(window);
return m_primaryWindow;
return platformWindow;
}
void QAndroidPlatformIntegration::invalidateNativeSurface()
{
if (m_primaryWindow != 0)
m_primaryWindow->invalidateSurface();
foreach (QWindow *w, QGuiApplication::topLevelWindows()) {
QAndroidOpenGLPlatformWindow *window =
static_cast<QAndroidOpenGLPlatformWindow *>(w->handle());
if (window != 0)
window->invalidateSurface();
}
}
void QAndroidPlatformIntegration::surfaceChanged()
{
if (m_primaryWindow != 0)
m_primaryWindow->resetSurface();
foreach (QWindow *w, QGuiApplication::topLevelWindows()) {
QAndroidOpenGLPlatformWindow *window =
static_cast<QAndroidOpenGLPlatformWindow *>(w->handle());
if (window != 0)
window->resetSurface();
}
}
QPlatformOpenGLContext *QAndroidPlatformIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const

View File

@ -95,7 +95,6 @@ public:
QPlatformWindow *createPlatformWindow(QWindow *window) const;
void invalidateNativeSurface();
void surfaceChanged();
QAndroidOpenGLPlatformWindow *primaryWindow() const { return m_primaryWindow; }
QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const;
#endif
@ -138,8 +137,6 @@ private:
#ifndef ANDROID_PLUGIN_OPENGL
QAbstractEventDispatcher *m_eventDispatcher;
QAndroidPlatformScreen *m_primaryScreen;
#else
mutable QAndroidOpenGLPlatformWindow *m_primaryWindow;
#endif
QThread *m_mainThread;

View File

@ -52,6 +52,7 @@
#include <qpa/qplatformopenglcontext.h>
#include <qpa/qplatformintegration.h>
#include "QtGui/private/qwindow_p.h"
#include "QtGui/private/qguiapplication_p.h"
#include <qpa/qplatformcursor.h>
#include <QtGui/QGuiApplication>
@ -675,6 +676,16 @@ void QWidgetPrivate::setGeometry_sys(int x, int y, int w, int h, bool isMove)
h = qMax(h,extra->minh);
}
if (q->isWindow() && q->windowHandle()) {
QPlatformIntegration *integration = QGuiApplicationPrivate::platformIntegration();
if (!integration->hasCapability(QPlatformIntegration::NonFullScreenWindows)) {
x = 0;
y = 0;
w = q->windowHandle()->width();
h = q->windowHandle()->height();
}
}
QPoint oldp = q->geometry().topLeft();
QSize olds = q->size();
QRect r(x, y, w, h);
@ -720,7 +731,7 @@ void QWidgetPrivate::setGeometry_sys(int x, int y, int w, int h, bool isMove)
q->windowHandle()->setGeometry(QRect(posInNativeParent,r.size()));
}
const QWidgetBackingStore *bs = maybeBackingStore();
if (bs->store) {
if (bs && bs->store) {
if (isResize)
bs->store->resize(r.size());
}