From eb95685556143eb71323742bfcdaa20541b01375 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Mon, 15 Apr 2013 11:23:04 +0200 Subject: 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 --- src/gui/kernel/qplatformintegration.cpp | 8 ++- src/gui/kernel/qplatformintegration.h | 3 +- .../platforms/android/src/androidjnimain.cpp | 19 ++++--- .../android/src/opengl/qandroidopenglcontext.cpp | 14 +++--- .../src/opengl/qandroidopenglplatformwindow.cpp | 58 +++++++++++++++++++++- .../src/opengl/qandroidopenglplatformwindow.h | 13 +++++ .../android/src/opengl/qeglfshooks_android.cpp | 1 + .../android/src/qandroidplatformintegration.cpp | 33 ++++++------ .../android/src/qandroidplatformintegration.h | 3 -- src/widgets/kernel/qwidget_qpa.cpp | 13 ++++- 10 files changed, 128 insertions(+), 37 deletions(-) diff --git a/src/gui/kernel/qplatformintegration.cpp b/src/gui/kernel/qplatformintegration.cpp index 70de75072c..8a0540efc2 100644 --- a/src/gui/kernel/qplatformintegration.cpp +++ b/src/gui/kernel/qplatformintegration.cpp @@ -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 diff --git a/src/gui/kernel/qplatformintegration.h b/src/gui/kernel/qplatformintegration.h index ddee6f05c8..bee2ba0caf 100644 --- a/src/gui/kernel/qplatformintegration.h +++ b/src/gui/kernel/qplatformintegration.h @@ -90,7 +90,8 @@ public: WindowMasks, MultipleWindows, ApplicationState, - ForeignWindows + ForeignWindows, + NonFullScreenWindows }; virtual ~QPlatformIntegration() { } diff --git a/src/plugins/platforms/android/src/androidjnimain.cpp b/src/plugins/platforms/android/src/androidjnimain.cpp index cd7fa25da7..162a8aa977 100644 --- a/src/plugins/platforms/android/src/androidjnimain.cpp +++ b/src/plugins/platforms/android/src/androidjnimain.cpp @@ -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(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(); diff --git a/src/plugins/platforms/android/src/opengl/qandroidopenglcontext.cpp b/src/plugins/platforms/android/src/opengl/qandroidopenglcontext.cpp index aa8ee57341..4d741807d0 100644 --- a/src/plugins/platforms/android/src/opengl/qandroidopenglcontext.cpp +++ b/src/plugins/platforms/android/src/opengl/qandroidopenglcontext.cpp @@ -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(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(); } } diff --git a/src/plugins/platforms/android/src/opengl/qandroidopenglplatformwindow.cpp b/src/plugins/platforms/android/src/opengl/qandroidopenglplatformwindow.cpp index 15c6559157..5362906e0e 100644 --- a/src/plugins/platforms/android/src/opengl/qandroidopenglplatformwindow.cpp +++ b/src/plugins/platforms/android/src/opengl/qandroidopenglplatformwindow.cpp @@ -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(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(); } diff --git a/src/plugins/platforms/android/src/opengl/qandroidopenglplatformwindow.h b/src/plugins/platforms/android/src/opengl/qandroidopenglplatformwindow.h index b835cb3246..36a110e1a8 100644 --- a/src/plugins/platforms/android/src/opengl/qandroidopenglplatformwindow.h +++ b/src/plugins/platforms/android/src/opengl/qandroidopenglplatformwindow.h @@ -44,6 +44,7 @@ #include "qeglfswindow.h" #include +#include 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 diff --git a/src/plugins/platforms/android/src/opengl/qeglfshooks_android.cpp b/src/plugins/platforms/android/src/opengl/qeglfshooks_android.cpp index 4734d47eb3..005758d83d 100644 --- a/src/plugins/platforms/android/src/opengl/qeglfshooks_android.cpp +++ b/src/plugins/platforms/android/src/opengl/qeglfshooks_android.cpp @@ -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); diff --git a/src/plugins/platforms/android/src/qandroidplatformintegration.cpp b/src/plugins/platforms/android/src/qandroidplatformintegration.cpp index cbd0f26835..3de6c47ad0 100644 --- a/src/plugins/platforms/android/src/qandroidplatformintegration.cpp +++ b/src/plugins/platforms/android/src/qandroidplatformintegration.cpp @@ -43,6 +43,7 @@ #include "qabstracteventdispatcher.h" #include "androidjnimain.h" #include +#include #include #include #include @@ -85,9 +86,6 @@ void *QAndroidPlatformNativeInterface::nativeResourceForIntegration(const QByteA QAndroidPlatformIntegration::QAndroidPlatformIntegration(const QStringList ¶mList) : 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(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(w->handle()); + if (window != 0) + window->resetSurface(); + } } QPlatformOpenGLContext *QAndroidPlatformIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const diff --git a/src/plugins/platforms/android/src/qandroidplatformintegration.h b/src/plugins/platforms/android/src/qandroidplatformintegration.h index 3f8cc5a809..8da9fb2ff4 100644 --- a/src/plugins/platforms/android/src/qandroidplatformintegration.h +++ b/src/plugins/platforms/android/src/qandroidplatformintegration.h @@ -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; diff --git a/src/widgets/kernel/qwidget_qpa.cpp b/src/widgets/kernel/qwidget_qpa.cpp index 8c31d4ad26..fdc72ee23c 100644 --- a/src/widgets/kernel/qwidget_qpa.cpp +++ b/src/widgets/kernel/qwidget_qpa.cpp @@ -52,6 +52,7 @@ #include #include #include "QtGui/private/qwindow_p.h" +#include "QtGui/private/qguiapplication_p.h" #include #include @@ -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()); } -- cgit v1.2.3