diff options
Diffstat (limited to 'src/plugins')
128 files changed, 2665 insertions, 1159 deletions
diff --git a/src/plugins/accessible/widgets/complexwidgets.cpp b/src/plugins/accessible/widgets/complexwidgets.cpp index c0d62ccc16..b1df6d816a 100644 --- a/src/plugins/accessible/widgets/complexwidgets.cpp +++ b/src/plugins/accessible/widgets/complexwidgets.cpp @@ -221,41 +221,6 @@ QString QAccessibleTabBar::text(QAccessible::Text t) const return QString(); } -/*! - Selects the item with index \a child if \a on is true; otherwise - unselects it. If \a extend is true and the selection mode is not - \c Single and there is an existing selection, the selection is - extended to include all the items from the existing selection up - to and including the item with index \a child. Returns \c true if a - selection was made or extended; otherwise returns \c false. - - \sa selection(), clearSelection() -*/ -bool QAccessibleTabBar::setSelected(int child, bool on, bool extend) -{ - if (!child || !on || extend || child > tabBar()->count()) - return false; - - if (!tabBar()->isTabEnabled(child - 1)) - return false; - tabBar()->setCurrentIndex(child - 1); - return true; -} - -/*! - Returns a (possibly empty) list of indexes of the items selected - in the list box. - - \sa setSelected(), clearSelection() -*/ -QVector<int> QAccessibleTabBar::selection() const -{ - QVector<int> array; - if (tabBar()->currentIndex() != -1) - array +=tabBar()->currentIndex() + 1; - return array; -} - #endif // QT_NO_TABBAR #ifndef QT_NO_COMBOBOX diff --git a/src/plugins/accessible/widgets/complexwidgets.h b/src/plugins/accessible/widgets/complexwidgets.h index 164bc5b6d9..00186282f3 100644 --- a/src/plugins/accessible/widgets/complexwidgets.h +++ b/src/plugins/accessible/widgets/complexwidgets.h @@ -105,9 +105,6 @@ public: int childCount() const Q_DECL_OVERRIDE; QString text(QAccessible::Text t) const Q_DECL_OVERRIDE; - bool setSelected(int child, bool on, bool extend); - QVector<int> selection() const; - QAccessibleInterface* child(int index) const Q_DECL_OVERRIDE; int indexOfChild(const QAccessibleInterface *child) const Q_DECL_OVERRIDE; diff --git a/src/plugins/platforminputcontexts/compose/generator/qtablegenerator.cpp b/src/plugins/platforminputcontexts/compose/generator/qtablegenerator.cpp index 459a450222..3d1b281620 100644 --- a/src/plugins/platforminputcontexts/compose/generator/qtablegenerator.cpp +++ b/src/plugins/platforminputcontexts/compose/generator/qtablegenerator.cpp @@ -101,7 +101,6 @@ void TableGenerator::findComposeFile() qDebug() << "Using Compose file from: " << composeFile; #endif } - // check if user’s home directory has a file named .XCompose if (!found && cleanState()) { QString composeFile = qgetenv("HOME") + QStringLiteral("/.XCompose"); @@ -112,10 +111,12 @@ void TableGenerator::findComposeFile() qDebug() << "Using Compose file from: " << composeFile; #endif } - // check for the system provided compose files if (!found && cleanState()) { - QString table = readLocaleMappings(locale().toUpper().toUtf8()); + QByteArray loc = locale().toUpper().toUtf8(); + QString table = readLocaleMappings(loc); + if (table.isEmpty()) + table = readLocaleMappings(readLocaleAliases(loc)); if (cleanState()) { if (table.isEmpty()) @@ -178,8 +179,11 @@ QString TableGenerator::locale() const QString TableGenerator::readLocaleMappings(const QByteArray &locale) { - QFile mappings(systemComposeDir() + QLatin1String("/compose.dir")); QString file; + if (locale.isEmpty()) + return file; + + QFile mappings(systemComposeDir() + QLatin1String("/compose.dir")); if (mappings.open(QIODevice::ReadOnly)) { const int localeNameLength = locale.size(); const char * const localeData = locale.constData(); @@ -209,9 +213,8 @@ QString TableGenerator::readLocaleMappings(const QByteArray &locale) while (*line && *line != ' ' && *line != '\t' && *line != '\n') ++line; *line = '\0'; - if (localeNameLength == (line - lc) && !strncasecmp(lc, localeData, line - lc)) { - file = QString::fromUtf8(l, composeFileNameEnd - l); + file = QString::fromLocal8Bit(l, composeFileNameEnd - l); break; } } @@ -221,6 +224,47 @@ QString TableGenerator::readLocaleMappings(const QByteArray &locale) return file; } +QByteArray TableGenerator::readLocaleAliases(const QByteArray &locale) +{ + QFile aliases(systemComposeDir() + QLatin1String("/locale.alias")); + QByteArray fullLocaleName; + if (aliases.exists()) { + aliases.open(QIODevice::ReadOnly); + while (!aliases.atEnd()) { + char l[1024]; + int read = aliases.readLine(l, sizeof(l)); + char *line = l; + if (read && ((*line >= 'a' && *line <= 'z') || + (*line >= 'A' && *line <= 'Z'))) { + const char *alias = line; + while (*line && *line != ':' && *line != ' ' && *line != '\t') + ++line; + if (!*line) + continue; + *line = 0; + if (locale.size() == (line - alias) + && !strncasecmp(alias, locale.constData(), line - alias)) { + // found a match for alias, read the real locale name + ++line; + while (*line && (*line == ' ' || *line == '\t')) + ++line; + const char *fullName = line; + while (*line && *line != ' ' && *line != '\t' && *line != '\n') + ++line; + *line = 0; + fullLocaleName = fullName; +#ifdef DEBUG_GENERATOR + qDebug() << "Alias for: " << alias << "is: " << fullLocaleName; + break; +#endif + } + } + } + aliases.close(); + } + return fullLocaleName; +} + bool TableGenerator::processFile(QString composeFileName) { QFile composeFile(composeFileName); @@ -255,7 +299,7 @@ void TableGenerator::parseComposeFile(QFile *composeFile) if (*line == '<') parseKeySequence(line); else if (!strncmp(line, "include", 7)) - parseIncludeInstruction(QString::fromUtf8(line)); + parseIncludeInstruction(QString::fromLocal8Bit(line)); } composeFile->close(); @@ -309,7 +353,7 @@ ushort TableGenerator::keysymToUtf8(quint32 sym) qDebug() << QString("keysym - 0x%1 : utf8 - %2").arg(QString::number(sym, 16)) .arg(codec->toUnicode(chars)); #endif - return QString::fromUtf8(chars).at(0).unicode(); + return QString::fromLocal8Bit(chars).at(0).unicode(); } static inline int fromBase8(const char *s, const char *end) @@ -378,13 +422,13 @@ void TableGenerator::parseKeySequence(char *line) // handle direct text encoded in the locale if (*composeValue == '\\') ++composeValue; - elem.value = QString::fromUtf8(composeValue).at(0).unicode(); + elem.value = QString::fromLocal8Bit(composeValue).at(0).unicode(); ++composeValue; } #ifdef DEBUG_GENERATOR // find the comment - elem.comment = QString::fromUtf8(composeValueEnd + 1).trimmed(); + elem.comment = QString::fromLocal8Bit(composeValueEnd + 1).trimmed(); #endif // find the key sequence and convert to X11 keysym diff --git a/src/plugins/platforminputcontexts/compose/generator/qtablegenerator.h b/src/plugins/platforminputcontexts/compose/generator/qtablegenerator.h index aa65b7b895..248c09f3ea 100644 --- a/src/plugins/platforminputcontexts/compose/generator/qtablegenerator.h +++ b/src/plugins/platforminputcontexts/compose/generator/qtablegenerator.h @@ -122,6 +122,7 @@ protected: ushort keysymToUtf8(quint32 sym); QString readLocaleMappings(const QByteArray &locale); + QByteArray readLocaleAliases(const QByteArray &locale); void initPossibleLocations(); bool cleanState() const { return ((m_state & NoErrors) == NoErrors); } QString locale() const; diff --git a/src/plugins/platforms/android/opengl/opengl.pro b/src/plugins/platforms/android/opengl/opengl.pro index 301c8e6e4c..ea050ca3a0 100644 --- a/src/plugins/platforms/android/opengl/opengl.pro +++ b/src/plugins/platforms/android/opengl/opengl.pro @@ -20,11 +20,13 @@ INCLUDEPATH += $$PWD/../src/opengl/ HEADERS += \ $$PWD/../src/opengl/qandroidopenglcontext.h \ - $$PWD/../src/opengl/qandroidopenglplatformwindow.h + $$PWD/../src/opengl/qandroidopenglplatformwindow.h \ + $$PWD/../src/opengl/qandroidopenglplatformscreen.h SOURCES += \ $$PWD/../src/opengl/qandroidopenglcontext.cpp \ - $$PWD/../src/opengl/qandroidopenglplatformwindow.cpp + $$PWD/../src/opengl/qandroidopenglplatformwindow.cpp \ + $$PWD/../src/opengl/qandroidopenglplatformscreen.cpp include($$PWD/../../eglfs/eglfs.pri) include($$PWD/../src/src.pri) diff --git a/src/plugins/platforms/android/src/androidjniaccessibility.cpp b/src/plugins/platforms/android/src/androidjniaccessibility.cpp index a27d9f5aed..b987c49c9c 100644 --- a/src/plugins/platforms/android/src/androidjniaccessibility.cpp +++ b/src/plugins/platforms/android/src/androidjniaccessibility.cpp @@ -46,7 +46,7 @@ #include "qguiapplication.h" #include "qwindow.h" #include "qrect.h" -#include "private/qaccessible2_p.h" +#include "QtGui/qaccessible.h" #include "qdebug.h" diff --git a/src/plugins/platforms/android/src/androidjniinput.cpp b/src/plugins/platforms/android/src/androidjniinput.cpp index 30d4e69afe..8ce95532d3 100644 --- a/src/plugins/platforms/android/src/androidjniinput.cpp +++ b/src/plugins/platforms/android/src/androidjniinput.cpp @@ -47,6 +47,10 @@ #include <QTouchEvent> #include <QPointer> +#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL +# include <QDebug> +#endif + using namespace QtAndroid; namespace QtAndroidInput @@ -63,12 +67,32 @@ namespace QtAndroidInput static QPointer<QWindow> m_mouseGrabber; + static int m_lastCursorPos = -1; + void updateSelection(int selStart, int selEnd, int candidatesStart, int candidatesEnd) { AttachedJNIEnv env; if (!env.jniEnv) return; +#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL + qDebug() << ">>> UPDATESELECTION" << selStart << selEnd << candidatesStart << candidatesEnd; +#endif + if (candidatesStart == -1 && candidatesEnd == -1 && selStart == selEnd) { + // Qt only gives us position inside the block, so if we move to the + // same position in another block, the Android keyboard will believe + // we have not changed position, and be terribly confused. + if (selStart == m_lastCursorPos) { +#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL + qDebug() << ">>> FAKEUPDATESELECTION" << selStart+1; +#endif + env.jniEnv->CallStaticVoidMethod(applicationClass(), m_updateSelectionMethodID, + selStart+1, selEnd+1, candidatesStart, candidatesEnd); + } + m_lastCursorPos = selStart; + } else { + m_lastCursorPos = -1; + } env.jniEnv->CallStaticVoidMethod(applicationClass(), m_updateSelectionMethodID, selStart, selEnd, candidatesStart, candidatesEnd); } @@ -86,6 +110,9 @@ namespace QtAndroidInput width, height, inputHints); +#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL + qDebug() << "@@@ SHOWSOFTWAREKEYBOARD" << left << top << width << height << inputHints; +#endif } void resetSoftwareKeyboard() @@ -95,6 +122,9 @@ namespace QtAndroidInput return; env.jniEnv->CallStaticVoidMethod(applicationClass(), m_resetSoftwareKeyboardMethodID); +#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL + qDebug() << "@@@ RESETSOFTWAREKEYBOARD"; +#endif } void hideSoftwareKeyboard() @@ -104,6 +134,9 @@ namespace QtAndroidInput return; env.jniEnv->CallStaticVoidMethod(applicationClass(), m_hideSoftwareKeyboardMethodID); +#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL + qDebug() << "@@@ HIDESOFTWAREKEYBOARD"; +#endif } bool isSoftwareKeyboardVisible() @@ -112,7 +145,11 @@ namespace QtAndroidInput if (!env.jniEnv) return false; - return env.jniEnv->CallStaticBooleanMethod(applicationClass(), m_isSoftwareKeyboardVisibleMethodID); + bool visibility = env.jniEnv->CallStaticBooleanMethod(applicationClass(), m_isSoftwareKeyboardVisibleMethodID); +#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL + qDebug() << "@@@ ISSOFTWAREKEYBOARDVISIBLE" << visibility; +#endif + return visibility; } @@ -511,6 +548,15 @@ namespace QtAndroidInput false); } + static void keyboardVisibilityChanged(JNIEnv */*env*/, jobject /*thiz*/, jboolean /*visibility*/) + { + QAndroidInputContext *inputContext = QAndroidInputContext::androidInputContext(); + if (inputContext) + inputContext->emitInputPanelVisibleChanged(); +#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL + qDebug() << "@@@ KEYBOARDVISIBILITYCHANGED" << inputContext; +#endif + } static JNINativeMethod methods[] = { {"touchBegin","(I)V",(void*)touchBegin}, @@ -521,7 +567,8 @@ namespace QtAndroidInput {"mouseMove", "(III)V", (void *)mouseMove}, {"longPress", "(III)V", (void *)longPress}, {"keyDown", "(III)V", (void *)keyDown}, - {"keyUp", "(III)V", (void *)keyUp} + {"keyUp", "(III)V", (void *)keyUp}, + {"keyboardVisibilityChanged", "(Z)V", (void *)keyboardVisibilityChanged} }; #define GET_AND_CHECK_STATIC_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) \ diff --git a/src/plugins/platforms/android/src/androidjnimain.cpp b/src/plugins/platforms/android/src/androidjnimain.cpp index 5c9ca798a8..3064e5d4e2 100644 --- a/src/plugins/platforms/android/src/androidjnimain.cpp +++ b/src/plugins/platforms/android/src/androidjnimain.cpp @@ -40,6 +40,7 @@ ** ****************************************************************************/ +#include <QtGui/private/qguiapplication_p.h> #include <dlfcn.h> #include <pthread.h> @@ -59,6 +60,7 @@ #include "androidjniinput.h" #include "androidjniclipboard.h" #include "androidjnimenu.h" +#include "qandroidplatformdialoghelpers.h" #include "qandroidplatformintegration.h" #include <QtWidgets/QApplication> @@ -97,6 +99,9 @@ static jmethodID m_createBitmapMethodID = 0; static jobject m_ARGB_8888_BitmapConfigValue = 0; static jobject m_RGB_565_BitmapConfigValue = 0; +jmethodID m_setFullScreenMethodID = 0; +static bool m_statusBarShowing = true; + static jclass m_bitmapDrawableClass = 0; static jmethodID m_bitmapDrawableConstructorMethodID = 0; @@ -111,8 +116,6 @@ static jobject m_surface = NULL; static EGLNativeWindowType m_nativeWindow = 0; static QSemaphore m_waitForWindowSemaphore; static bool m_waitForWindow = false; - -static jfieldID m_surfaceFieldID = 0; #endif @@ -247,17 +250,6 @@ namespace QtAndroid m_surfaceMutex.unlock(); return m_nativeWindow; } - - QSize nativeWindowSize() - { - if (m_nativeWindow == 0) - return QAndroidPlatformIntegration::defaultDesktopSize(); - - int width = ANativeWindow_getWidth(m_nativeWindow); - int height = ANativeWindow_getHeight(m_nativeWindow); - - return QSize(width, height); - } #endif void setAndroidPlatformIntegration(QAndroidPlatformIntegration *androidPlatformIntegration) @@ -323,6 +315,36 @@ namespace QtAndroid return m_activityObject; } + void showStatusBar() + { + if (m_statusBarShowing) + return; + + QtAndroid::AttachedJNIEnv env; + if (env.jniEnv == 0) { + qWarning("Failed to get JNI Environment."); + return; + } + + env.jniEnv->CallStaticVoidMethod(m_applicationClass, m_setFullScreenMethodID, false); + m_statusBarShowing = true; + } + + void hideStatusBar() + { + if (!m_statusBarShowing) + return; + + QtAndroid::AttachedJNIEnv env; + if (env.jniEnv == 0) { + qWarning("Failed to get JNI Environment."); + return; + } + + env.jniEnv->CallStaticVoidMethod(m_applicationClass, m_setFullScreenMethodID, true); + m_statusBarShowing = false; + } + void setApplicationActive() { if (m_activityActive) @@ -564,12 +586,16 @@ static void setSurface(JNIEnv *env, jobject /*thiz*/, jobject jSurface) m_waitForWindowSemaphore.release(); if (m_androidPlatformIntegration) { - QSize size = QtAndroid::nativeWindowSize(); + // Use the desktop size. + // On some devices, the getters for the native window size gives wrong values + QSize size = QAndroidPlatformIntegration::defaultDesktopSize(); QPlatformScreen *screen = m_androidPlatformIntegration->screen(); QRect geometry(QPoint(0, 0), size); - QWindowSystemInterface::handleScreenAvailableGeometryChange(screen->screen(), geometry); - QWindowSystemInterface::handleScreenGeometryChange(screen->screen(), geometry); + if (screen) { + QWindowSystemInterface::handleScreenAvailableGeometryChange(screen->screen(), geometry); + QWindowSystemInterface::handleScreenGeometryChange(screen->screen(), geometry); + } if (!sameNativeWindow) { m_surfaceMutex.unlock(); @@ -666,7 +692,7 @@ static void updateApplicationState(JNIEnv */*env*/, jobject /*thiz*/, jint state { m_activityActive = (state == Qt::ApplicationActive); - if (!m_androidPlatformIntegration) + if (!m_androidPlatformIntegration || !QGuiApplicationPrivate::platformIntegration()) return; QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationState(state)); @@ -762,6 +788,7 @@ static int registerNatives(JNIEnv *env) jclass clazz; FIND_AND_CHECK_CLASS("org/qtproject/qt5/android/QtNative"); m_applicationClass = static_cast<jclass>(env->NewGlobalRef(clazz)); + GET_AND_CHECK_STATIC_METHOD(m_setFullScreenMethodID, m_applicationClass, "setFullScreen", "(Z)V"); if (env->RegisterNatives(m_applicationClass, methods, sizeof(methods) / sizeof(methods[0])) < 0) { __android_log_print(ANDROID_LOG_FATAL,"Qt", "RegisterNatives failed"); @@ -770,11 +797,6 @@ static int registerNatives(JNIEnv *env) GET_AND_CHECK_STATIC_METHOD(m_redrawSurfaceMethodID, m_applicationClass, "redrawSurface", "(IIII)V"); -#ifdef ANDROID_PLUGIN_OPENGL - FIND_AND_CHECK_CLASS("android/view/Surface"); - GET_AND_CHECK_FIELD(m_surfaceFieldID, clazz, "mNativeSurface", "I"); -#endif - jmethodID methodID; GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "activity", "()Landroid/app/Activity;"); jobject activityObject = env->CallStaticObjectMethod(m_applicationClass, methodID); @@ -814,6 +836,15 @@ static int registerNatives(JNIEnv *env) return JNI_TRUE; } +jint androidApiLevel(JNIEnv *env) +{ + jclass clazz; + FIND_AND_CHECK_CLASS("android/os/Build$VERSION"); + jfieldID fieldId; + GET_AND_CHECK_STATIC_FIELD(fieldId, clazz, "SDK_INT", "I"); + return env->GetStaticIntField(clazz, fieldId); +} + Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void */*reserved*/) { typedef union { @@ -836,11 +867,18 @@ Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void */*reserved*/) || !QtAndroidInput::registerNatives(env) || !QtAndroidClipboard::registerNatives(env) || !QtAndroidMenu::registerNatives(env) - || !QtAndroidAccessibility::registerNatives(env)) { + || !QtAndroidAccessibility::registerNatives(env) + || !QtAndroidDialogHelpers::registerNatives(env)) { __android_log_print(ANDROID_LOG_FATAL, "Qt", "registerNatives failed"); return -1; } + jint apiLevel = androidApiLevel(env); + if (apiLevel >= 16 && !QtAndroidAccessibility::registerNatives(env)) { + __android_log_print(ANDROID_LOG_FATAL, "Qt A11y", "registerNatives failed"); + return -1; + } + m_javaVM = vm; return JNI_VERSION_1_4; } diff --git a/src/plugins/platforms/android/src/androidjnimain.h b/src/plugins/platforms/android/src/androidjnimain.h index 9a3d8a9607..11d3573404 100644 --- a/src/plugins/platforms/android/src/androidjnimain.h +++ b/src/plugins/platforms/android/src/androidjnimain.h @@ -69,13 +69,10 @@ namespace QtAndroid void setAndroidPlatformIntegration(QAndroidPlatformIntegration *androidPlatformIntegration); void setQtThread(QThread *thread); - void setFullScreen(QWidget *widget); - #ifndef ANDROID_PLUGIN_OPENGL void flushImage(const QPoint &pos, const QImage &image, const QRect &rect); #else EGLNativeWindowType nativeWindow(bool waitToCreate = true); - QSize nativeWindowSize(); #endif QWindow *topLevelWindowAt(const QPoint &globalPos); @@ -90,6 +87,9 @@ namespace QtAndroid void setApplicationActive(); + void showStatusBar(); + void hideStatusBar(); + jobject createBitmap(QImage img, JNIEnv *env = 0); jobject createBitmapDrawable(jobject bitmap, JNIEnv *env = 0); diff --git a/src/plugins/platforms/android/src/androidjnimenu.cpp b/src/plugins/platforms/android/src/androidjnimenu.cpp index bb180347c1..293af2b9cd 100644 --- a/src/plugins/platforms/android/src/androidjnimenu.cpp +++ b/src/plugins/platforms/android/src/androidjnimenu.cpp @@ -41,13 +41,14 @@ #include "androidjnimenu.h" #include "androidjnimain.h" -#include <qmutex.h> -#include <qset.h> -#include <qqueue.h> -#include <android/log.h> #include "qandroidplatformmenubar.h" #include "qandroidplatformmenu.h" -#include <qandroidplatformmenuitem.h> +#include "qandroidplatformmenuitem.h" + +#include <QMutex> +#include <QSet> +#include <QQueue> +#include <QWindow> using namespace QtAndroid; @@ -141,18 +142,17 @@ namespace QtAndroidMenu void setActiveTopLevelWindow(QWindow *window) { + Qt::WindowFlags flags = window ? window->flags() : Qt::WindowFlags(); + bool isNonRegularWindow = flags & (Qt::Desktop | Qt::Popup | Qt::Dialog | Qt::Sheet) & ~Qt::Window; + if (isNonRegularWindow) + return; + QMutexLocker lock(&menuBarMutex); if (activeTopLevelWindow == window) return; visibleMenuBar = 0; activeTopLevelWindow = window; -#ifdef ANDROID_PLUGIN_OPENGL - //only one toplevel window, so the menu bar always belongs to us - if (menuBars.size() == 1) { - visibleMenuBar = *menuBars.constBegin(); //since QSet doesn't have first() - } else -#endif foreach (QAndroidPlatformMenuBar *menuBar, menuBars) { if (menuBar->parentWindow() == window) { visibleMenuBar = menuBar; @@ -173,8 +173,10 @@ namespace QtAndroidMenu { QMutexLocker lock(&menuBarMutex); menuBars.remove(menuBar); - if (visibleMenuBar == menuBar) + if (visibleMenuBar == menuBar) { + visibleMenuBar = 0; resetMenuBar(); + } } static QString removeAmpersandEscapes(QString s) @@ -197,16 +199,18 @@ namespace QtAndroidMenu env->CallObjectMethod(menuItem, setCheckedMenuItemMethodID, checked); env->CallObjectMethod(menuItem, setEnabledMenuItemMethodID, enabled); - if (!icon.isNull()) { + if (!icon.isNull()) { // isNull() only checks the d pointer, not the actual image data. int sz = qMax(36, qgetenv("QT_ANDROID_APP_ICON_SIZE").toInt()); QImage img = icon.pixmap(QSize(sz,sz), enabled ? QIcon::Normal : QIcon::Disabled, QIcon::On).toImage(); - env->CallObjectMethod(menuItem, - setIconMenuItemMethodID, - createBitmapDrawable(createBitmap(img, env), env)); + if (!img.isNull()) { // Make sure we have a valid image. + env->CallObjectMethod(menuItem, + setIconMenuItemMethodID, + createBitmapDrawable(createBitmap(img, env), env)); + } } env->CallObjectMethod(menuItem, setVisibleMenuItemMethodID, visible); diff --git a/src/plugins/platforms/android/src/opengl/qandroidopenglcontext.cpp b/src/plugins/platforms/android/src/opengl/qandroidopenglcontext.cpp index 9d6d4003f7..6431914812 100644 --- a/src/plugins/platforms/android/src/opengl/qandroidopenglcontext.cpp +++ b/src/plugins/platforms/android/src/opengl/qandroidopenglcontext.cpp @@ -71,6 +71,8 @@ void QAndroidOpenGLContext::swapBuffers(QPlatformSurface *surface) if (size.isValid()) { QRect geometry(QPoint(0, 0), size); window->setGeometry(geometry); + QWindowSystemInterface::handleGeometryChange(window->window(), geometry); + QWindowSystemInterface::handleExposeEvent(window->window(), QRegion(geometry)); window->scheduleResize(QSize()); } window->unlock(); diff --git a/src/plugins/platforms/android/src/opengl/qandroidopenglplatformscreen.cpp b/src/plugins/platforms/android/src/opengl/qandroidopenglplatformscreen.cpp new file mode 100644 index 0000000000..de4075feff --- /dev/null +++ b/src/plugins/platforms/android/src/opengl/qandroidopenglplatformscreen.cpp @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qandroidopenglplatformscreen.h" +#include "qandroidopenglplatformwindow.h" +#include "androidjnimenu.h" + +QT_BEGIN_NAMESPACE + +QAndroidOpenGLPlatformScreen::QAndroidOpenGLPlatformScreen(EGLDisplay display) + : QEglFSScreen(display) +{ +} + +void QAndroidOpenGLPlatformScreen::topWindowChanged(QPlatformWindow *window) +{ + QtAndroidMenu::setActiveTopLevelWindow(window->window()); + QAndroidOpenGLPlatformWindow *platformWindow = static_cast<QAndroidOpenGLPlatformWindow *>(window); + if (platformWindow != 0) + platformWindow->updateStatusBarVisibility(); +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/qnx/qqnxrootwindow.h b/src/plugins/platforms/android/src/opengl/qandroidopenglplatformscreen.h index ea7c7faace..e9251592aa 100644 --- a/src/plugins/platforms/qnx/qqnxrootwindow.h +++ b/src/plugins/platforms/android/src/opengl/qandroidopenglplatformscreen.h @@ -1,6 +1,6 @@ -/*************************************************************************** +/**************************************************************************** ** -** Copyright (C) 2011 - 2012 Research In Motion +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the plugins of the Qt Toolkit. @@ -39,47 +39,22 @@ ** ****************************************************************************/ -#ifndef QQNXROOTWINDOW_H -#define QQNXROOTWINDOW_H +#ifndef QANDROIDOPENGLPLATFORMSCREEN_H +#define QANDROIDOPENGLPLATFORMSCREEN_H -#include <QtCore/QByteArray> -#include <QtCore/QRect> - -#include <screen/screen.h> +#include "qeglfsscreen.h" QT_BEGIN_NAMESPACE -class QQnxScreen; - -class QQnxRootWindow +class QAndroidOpenGLPlatformScreen : public QEglFSScreen { public: - QQnxRootWindow(const QQnxScreen *screen); - ~QQnxRootWindow(); - - screen_window_t nativeHandle() const { return m_window; } - - void post() const; - void flush() const; - - void setRotation(int rotation); - - void resize(const QSize &size); - - void makeTranslucent(); - - QByteArray groupName() const { return m_windowGroupName; } - -private: - void createWindowGroup(); - - const QQnxScreen *m_screen; - screen_window_t m_window; - QByteArray m_windowGroupName; + QAndroidOpenGLPlatformScreen(EGLDisplay display); - bool m_translucent; +protected: + void topWindowChanged(QPlatformWindow *window); }; QT_END_NAMESPACE -#endif // QQNXROOTWINDOW_H +#endif // QANDROIDOPENGLPLATFORMSCREEN_H diff --git a/src/plugins/platforms/android/src/opengl/qandroidopenglplatformwindow.cpp b/src/plugins/platforms/android/src/opengl/qandroidopenglplatformwindow.cpp index 4934047af9..6ed805174b 100644 --- a/src/plugins/platforms/android/src/opengl/qandroidopenglplatformwindow.cpp +++ b/src/plugins/platforms/android/src/opengl/qandroidopenglplatformwindow.cpp @@ -41,6 +41,7 @@ #include "qandroidopenglplatformwindow.h" #include "androidjnimain.h" +#include "qandroidplatformintegration.h" #include <qpa/qwindowsysteminterface.h> QT_BEGIN_NAMESPACE @@ -52,6 +53,7 @@ QBasicAtomicInt QAndroidOpenGLPlatformWindow::m_referenceCount = Q_BASIC_ATOMIC_ QAndroidOpenGLPlatformWindow::QAndroidOpenGLPlatformWindow(QWindow *window) : QEglFSWindow(window) + , m_state(Qt::WindowNoState) { } @@ -110,7 +112,9 @@ void QAndroidOpenGLPlatformWindow::resetSurface() { lock(); - scheduleResize(QtAndroid::nativeWindowSize()); + // Use the desktop size. + // On some devices, the getters for the native window size gives wrong values + scheduleResize(QAndroidPlatformIntegration::defaultDesktopSize()); QWindowSystemInterface::handleExposeEvent(window(), QRegion(geometry())); // Expose event unlock(); } @@ -128,12 +132,38 @@ void QAndroidOpenGLPlatformWindow::destroy() } } +void QAndroidOpenGLPlatformWindow::updateStatusBarVisibility() +{ + Qt::WindowFlags flags = window()->flags(); + bool isNonRegularWindow = flags & (Qt::Popup | Qt::Dialog | Qt::Sheet) & ~Qt::Window; + if (!isNonRegularWindow) { + if (m_state & Qt::WindowFullScreen) + QtAndroid::hideStatusBar(); + else if (m_state & Qt::WindowMaximized) + QtAndroid::showStatusBar(); + } +} + void QAndroidOpenGLPlatformWindow::raise() { + updateStatusBarVisibility(); +} + +void QAndroidOpenGLPlatformWindow::setWindowState(Qt::WindowState state) +{ + if (m_state == state) + return; + + m_state = state; + if (window()->isVisible()) + updateStatusBarVisibility(); } void QAndroidOpenGLPlatformWindow::setVisible(bool visible) { + if (visible) + updateStatusBarVisibility(); + QEglFSWindow::setVisible(visible); // The Android Activity is activated before Qt is initialized, causing the application state to diff --git a/src/plugins/platforms/android/src/opengl/qandroidopenglplatformwindow.h b/src/plugins/platforms/android/src/opengl/qandroidopenglplatformwindow.h index 9a25957ccd..e4ff0444d4 100644 --- a/src/plugins/platforms/android/src/opengl/qandroidopenglplatformwindow.h +++ b/src/plugins/platforms/android/src/opengl/qandroidopenglplatformwindow.h @@ -66,16 +66,19 @@ public: void invalidateSurface(); void resetSurface(); + void setWindowState(Qt::WindowState state); void setVisible(bool visible); void destroy(); static void updateStaticNativeWindow(); + void updateStatusBarVisibility(); private: QSize m_scheduledResize; QMutex m_lock; + Qt::WindowState m_state; static QReadWriteLock m_staticSurfaceLock; static EGLSurface m_staticSurface; diff --git a/src/plugins/platforms/android/src/opengl/qeglfshooks_android.cpp b/src/plugins/platforms/android/src/opengl/qeglfshooks_android.cpp index 338966eb40..278cd553f4 100644 --- a/src/plugins/platforms/android/src/opengl/qeglfshooks_android.cpp +++ b/src/plugins/platforms/android/src/opengl/qeglfshooks_android.cpp @@ -82,7 +82,7 @@ EGLNativeDisplayType QEglFSAndroidHooks::platformDisplay() const QSize QEglFSAndroidHooks::screenSize() const { - return QtAndroid::nativeWindowSize(); + return QAndroidPlatformIntegration::defaultDesktopSize(); } QSizeF QEglFSAndroidHooks::physicalScreenSize() const @@ -120,7 +120,8 @@ EGLNativeWindowType QEglFSAndroidHooks::createNativeWindow(QPlatformWindow *plat void QEglFSAndroidHooks::destroyNativeWindow(EGLNativeWindowType window) { - ANativeWindow_release(window); + if (window != 0) + ANativeWindow_release(window); } bool QEglFSAndroidHooks::hasCapability(QPlatformIntegration::Capability capability) const diff --git a/src/plugins/platforms/android/src/qandroidassetsfileenginehandler.cpp b/src/plugins/platforms/android/src/qandroidassetsfileenginehandler.cpp index f3cb2586cc..95844fc649 100644 --- a/src/plugins/platforms/android/src/qandroidassetsfileenginehandler.cpp +++ b/src/plugins/platforms/android/src/qandroidassetsfileenginehandler.cpp @@ -43,20 +43,32 @@ #include "androidjnimain.h" #include <QCoreApplication> +#include <QVector> + +typedef QVector<QString> FilesList; + +struct AndroidAssetDir +{ + AndroidAssetDir(AAssetDir* ad) + { + const char *fileName; + while ((fileName = AAssetDir_getNextFileName(ad))) + m_items.push_back(QString::fromUtf8(fileName)); + AAssetDir_close(ad); + } + FilesList m_items; +}; class AndroidAbstractFileEngineIterator: public QAbstractFileEngineIterator { public: AndroidAbstractFileEngineIterator(QDir::Filters filters, const QStringList &nameFilters, - AAssetDir *asset, + QSharedPointer<AndroidAssetDir> asset, const QString &path) : QAbstractFileEngineIterator(filters, nameFilters) { - AAssetDir_rewind(asset); - const char *fileName; - while ((fileName = AAssetDir_getNextFileName(asset))) - m_items << fileName; + m_items = asset->m_items; m_index = -1; m_path = path; } @@ -93,7 +105,7 @@ public: private: QString m_path; - QStringList m_items; + FilesList m_items; int m_index; }; @@ -102,12 +114,11 @@ class AndroidAbstractFileEngine: public QAbstractFileEngine public: explicit AndroidAbstractFileEngine(AAsset *asset, const QString &fileName) { - m_assetDir = 0; m_assetFile = asset; m_fileName = fileName; } - explicit AndroidAbstractFileEngine(AAssetDir *asset, const QString &fileName) + explicit AndroidAbstractFileEngine(QSharedPointer<AndroidAssetDir> asset, const QString &fileName) { m_assetFile = 0; m_assetDir = asset; @@ -119,8 +130,6 @@ public: ~AndroidAbstractFileEngine() { close(); - if (m_assetDir) - AAssetDir_close(m_assetDir); } virtual bool open(QIODevice::OpenMode openMode) @@ -188,7 +197,7 @@ public: FileFlags flags(ReadOwnerPerm|ReadUserPerm|ReadGroupPerm|ReadOtherPerm|ExistsFlag); if (m_assetFile) flags |= FileType; - if (m_assetDir) + if (!m_assetDir.isNull()) flags |= DirectoryType; return type & flags; @@ -233,19 +242,19 @@ public: virtual Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) { - if (m_assetDir) + if (!m_assetDir.isNull()) return new AndroidAbstractFileEngineIterator(filters, filterNames, m_assetDir, m_fileName); return 0; } private: AAsset *m_assetFile; - AAssetDir *m_assetDir; + QSharedPointer<AndroidAssetDir> m_assetDir; QString m_fileName; }; -AndroidAssetsFileEngineHandler::AndroidAssetsFileEngineHandler() +AndroidAssetsFileEngineHandler::AndroidAssetsFileEngineHandler():m_assetsCache(std::max(5, qgetenv("QT_ANDROID_MAX_ASSETS_CACHE_SIZE").toInt())) { m_assetManager = QtAndroid::assetManager(); } @@ -264,25 +273,37 @@ QAbstractFileEngine * AndroidAssetsFileEngineHandler::create(const QString &file int prefixSize=8; - m_path.clear(); + QByteArray path; if (!fileName.endsWith(QLatin1Char('/'))) { - m_path = fileName.toUtf8(); + path = fileName.toUtf8(); AAsset *asset = AAssetManager_open(m_assetManager, - m_path.constData() + prefixSize, + path.constData() + prefixSize, AASSET_MODE_BUFFER); if (asset) return new AndroidAbstractFileEngine(asset, fileName); } - if (!m_path.size()) - m_path = fileName.left(fileName.length() - 1).toUtf8(); - - AAssetDir *assetDir = AAssetManager_openDir(m_assetManager, m_path.constData() + prefixSize); - if (assetDir) { - if (AAssetDir_getNextFileName(assetDir)) - return new AndroidAbstractFileEngine(assetDir, fileName); - else - AAssetDir_close(assetDir); + if (!path.size()) + path = fileName.left(fileName.length() - 1).toUtf8(); + + m_assetsCacheMutext.lock(); + QSharedPointer<AndroidAssetDir> *aad = m_assetsCache.object(path); + m_assetsCacheMutext.unlock(); + if (!aad) { + AAssetDir *assetDir = AAssetManager_openDir(m_assetManager, path.constData() + prefixSize); + if (assetDir) { + if (AAssetDir_getNextFileName(assetDir)) { + aad = new QSharedPointer<AndroidAssetDir>(new AndroidAssetDir(assetDir)); + m_assetsCacheMutext.lock(); + m_assetsCache.insert(path, aad); + m_assetsCacheMutext.unlock(); + return new AndroidAbstractFileEngine(*aad, fileName); + } else { + AAssetDir_close(assetDir); + } + } + } else { + return new AndroidAbstractFileEngine(*aad, fileName); } return 0; } diff --git a/src/plugins/platforms/android/src/qandroidassetsfileenginehandler.h b/src/plugins/platforms/android/src/qandroidassetsfileenginehandler.h index 9bff6a012e..7bd560886c 100644 --- a/src/plugins/platforms/android/src/qandroidassetsfileenginehandler.h +++ b/src/plugins/platforms/android/src/qandroidassetsfileenginehandler.h @@ -43,8 +43,13 @@ #define QANDROIDASSETSFILEENGINEHANDLER_H #include <QtCore/private/qabstractfileengine_p.h> +#include <QCache> +#include <QMutex> +#include <QSharedPointer> + #include <android/asset_manager.h> +struct AndroidAssetDir; class AndroidAssetsFileEngineHandler: public QAbstractFileEngineHandler { public: @@ -54,7 +59,8 @@ public: private: AAssetManager *m_assetManager; - mutable QByteArray m_path; + mutable QCache<QByteArray, QSharedPointer<AndroidAssetDir>> m_assetsCache; + mutable QMutex m_assetsCacheMutext; }; #endif // QANDROIDASSETSFILEENGINEHANDLER_H diff --git a/src/plugins/platforms/android/src/qandroidinputcontext.cpp b/src/plugins/platforms/android/src/qandroidinputcontext.cpp index 386c8e006a..8556e8ebf1 100644 --- a/src/plugins/platforms/android/src/qandroidinputcontext.cpp +++ b/src/plugins/platforms/android/src/qandroidinputcontext.cpp @@ -381,6 +381,11 @@ QAndroidInputContext::~QAndroidInputContext() m_textFieldID = 0; } +QAndroidInputContext *QAndroidInputContext::androidInputContext() +{ + return m_androidInputContext; +} + void QAndroidInputContext::reset() { clear(); diff --git a/src/plugins/platforms/android/src/qandroidinputcontext.h b/src/plugins/platforms/android/src/qandroidinputcontext.h index d19dcc384b..041bd0dc49 100644 --- a/src/plugins/platforms/android/src/qandroidinputcontext.h +++ b/src/plugins/platforms/android/src/qandroidinputcontext.h @@ -80,6 +80,7 @@ public: public: QAndroidInputContext(); ~QAndroidInputContext(); + static QAndroidInputContext * androidInputContext(); bool isValid() const { return true; } void reset(); diff --git a/src/plugins/platforms/android/src/qandroidplatformdialoghelpers.cpp b/src/plugins/platforms/android/src/qandroidplatformdialoghelpers.cpp new file mode 100644 index 0000000000..f379402e18 --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidplatformdialoghelpers.cpp @@ -0,0 +1,204 @@ +/**************************************************************************** +** +** Copyright (C) 2013 BogDan Vatra <bogdan@kde.org> +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtWidgets/QApplication> +#include <QtWidgets/QStyle> +#include "qandroidplatformdialoghelpers.h" +#include "androidjnimain.h" + +namespace QtAndroidDialogHelpers { +static jclass g_messageDialogHelperClass = 0; + +QAndroidPlatformMessageDialogHelper::QAndroidPlatformMessageDialogHelper() + :m_buttonId(-1) + ,m_javaMessageDialog(g_messageDialogHelperClass, "(Landroid/app/Activity;)V", QtAndroid::activity()) + ,m_shown(false) +{ +} + +void QAndroidPlatformMessageDialogHelper::exec() +{ + if (!m_shown) + show(Qt::Dialog, Qt::ApplicationModal, 0); + m_loop.exec(); +} + +static QString standardButtonText(int sbutton) +{ + switch (sbutton) { + case QMessageDialogOptions::Ok: + return QAndroidPlatformMessageDialogHelper::tr("OK"); + case QMessageDialogOptions::Save: + return QAndroidPlatformMessageDialogHelper::tr("Save"); + case QMessageDialogOptions::Open: + return QAndroidPlatformMessageDialogHelper::tr("Open"); + case QMessageDialogOptions::Cancel: + return QAndroidPlatformMessageDialogHelper::tr("Cancel"); + case QMessageDialogOptions::Close: + return QAndroidPlatformMessageDialogHelper::tr("Close"); + case QMessageDialogOptions::Apply: + return QAndroidPlatformMessageDialogHelper::tr("Apply"); + case QMessageDialogOptions::Reset: + return QAndroidPlatformMessageDialogHelper::tr("Reset"); + case QMessageDialogOptions::Help: + return QAndroidPlatformMessageDialogHelper::tr("Help"); + case QMessageDialogOptions::Discard: + return QAndroidPlatformMessageDialogHelper::tr("Discard"); + case QMessageDialogOptions::Yes: + return QAndroidPlatformMessageDialogHelper::tr("Yes"); + case QMessageDialogOptions::YesToAll: + return QAndroidPlatformMessageDialogHelper::tr("Yes to All"); + case QMessageDialogOptions::No: + return QAndroidPlatformMessageDialogHelper::tr("No"); + case QMessageDialogOptions::NoToAll: + return QAndroidPlatformMessageDialogHelper::tr("No to All"); + case QMessageDialogOptions::SaveAll: + return QAndroidPlatformMessageDialogHelper::tr("Save All"); + case QMessageDialogOptions::Abort: + return QAndroidPlatformMessageDialogHelper::tr("Abort"); + case QMessageDialogOptions::Retry: + return QAndroidPlatformMessageDialogHelper::tr("Retry"); + case QMessageDialogOptions::Ignore: + return QAndroidPlatformMessageDialogHelper::tr("Ignore"); + case QMessageDialogOptions::RestoreDefaults: + return QAndroidPlatformMessageDialogHelper::tr("Restore Defaults"); + } // switch + return QString(); +} + +bool QAndroidPlatformMessageDialogHelper::show(Qt::WindowFlags windowFlags + , Qt::WindowModality windowModality + , QWindow *parent) +{ + Q_UNUSED(windowFlags) + Q_UNUSED(windowModality) + Q_UNUSED(parent) + QSharedPointer<QMessageDialogOptions> opt = options(); + if (!opt.data()) + return false; + + m_javaMessageDialog.callMethod<void>("setIcon", "(I)V", opt->icon()); + + QString str = opt->windowTitle(); + if (!str.isEmpty()) + m_javaMessageDialog.callMethod<void>("setTile", "(Ljava/lang/String;)V", QJNIObjectPrivate::fromString(str).object()); + + str = opt->text(); + if (!str.isEmpty()) + m_javaMessageDialog.callMethod<void>("setText", "(Ljava/lang/String;)V", QJNIObjectPrivate::fromString(str).object()); + + str = opt->informativeText(); + if (!str.isEmpty()) + m_javaMessageDialog.callMethod<void>("setInformativeText", "(Ljava/lang/String;)V", QJNIObjectPrivate::fromString(str).object()); + + str = opt->detailedText(); + if (!str.isEmpty()) + m_javaMessageDialog.callMethod<void>("setDetailedText", "(Ljava/lang/String;)V", QJNIObjectPrivate::fromString(str).object()); + + for (int i = QMessageDialogOptions::FirstButton; i < QMessageDialogOptions::LastButton; i<<=1) { + if ( opt->standardButtons() & i ) + m_javaMessageDialog.callMethod<void>("addButton", "(ILjava/lang/String;)V", i, QJNIObjectPrivate::fromString(standardButtonText(i)).object()); + } + + m_javaMessageDialog.callMethod<void>("show", "(J)V", jlong(static_cast<QObject*>(this))); + m_shown = true; + return true; +} + +void QAndroidPlatformMessageDialogHelper::hide() +{ + m_javaMessageDialog.callMethod<void>("hide", "()V"); + m_shown = false; +} + +void QAndroidPlatformMessageDialogHelper::dialogResult(int buttonID) +{ + m_buttonId = buttonID; + if (m_loop.isRunning()) + m_loop.exit(); + if (m_buttonId < 0) { + emit reject(); + return; + } + + QMessageDialogOptions::StandardButton standardButton = static_cast<QMessageDialogOptions::StandardButton>(buttonID); + QMessageDialogOptions::ButtonRole role = QMessageDialogOptions::buttonRole(standardButton); + emit clicked(standardButton, role); +} + +static void dialogResult(JNIEnv * /*env*/, jobject /*thiz*/, jlong handler, int buttonID) +{ + QObject *object = reinterpret_cast<QObject *>(handler); + QMetaObject::invokeMethod(object, "dialogResult", Qt::QueuedConnection, Q_ARG(int, buttonID)); +} + +static JNINativeMethod methods[] = { + {"dialogResult", "(JI)V", (void *)dialogResult} +}; + + +#define FIND_AND_CHECK_CLASS(CLASS_NAME) \ + clazz = env->FindClass(CLASS_NAME); \ + if (!clazz) { \ + __android_log_print(ANDROID_LOG_FATAL, QtAndroid::qtTagText(), QtAndroid::classErrorMsgFmt(), CLASS_NAME); \ + return false; \ + } + +bool registerNatives(JNIEnv *env) +{ + jclass clazz = QtAndroid::findClass("org/qtproject/qt5/android/QtMessageDialogHelper", env); + if (!clazz) { + __android_log_print(ANDROID_LOG_FATAL, QtAndroid::qtTagText(), QtAndroid::classErrorMsgFmt() + , "org/qtproject/qt5/android/QtMessageDialogHelper"); + return false; + } + g_messageDialogHelperClass = static_cast<jclass>(env->NewGlobalRef(clazz)); + FIND_AND_CHECK_CLASS("org/qtproject/qt5/android/QtNativeDialogHelper"); + jclass appClass = static_cast<jclass>(env->NewGlobalRef(clazz)); + + if (env->RegisterNatives(appClass, methods, sizeof(methods) / sizeof(methods[0])) < 0) { + __android_log_print(ANDROID_LOG_FATAL, "Qt", "RegisterNatives failed"); + return false; + } + + return true; +} +} diff --git a/src/plugins/platforms/android/src/qandroidplatformdialoghelpers.h b/src/plugins/platforms/android/src/qandroidplatformdialoghelpers.h new file mode 100644 index 0000000000..88ec91d936 --- /dev/null +++ b/src/plugins/platforms/android/src/qandroidplatformdialoghelpers.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2013 BogDan Vatra <bogdan@kde.org> +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QANDROIDPLATFORMDIALOGHELPERS_H +#define QANDROIDPLATFORMDIALOGHELPERS_H +#include <jni.h> +#include <qpa/qplatformdialoghelper.h> +#include <QEventLoop> +#include <private/qjni_p.h> + +namespace QtAndroidDialogHelpers { + +class QAndroidPlatformMessageDialogHelper: public QPlatformMessageDialogHelper +{ + Q_OBJECT +public: + QAndroidPlatformMessageDialogHelper(); + void exec(); + bool show(Qt::WindowFlags windowFlags, + Qt::WindowModality windowModality, + QWindow *parent); + void hide(); + +public slots: + void dialogResult(int buttonID); + +private: + int m_buttonId; + QEventLoop m_loop; + QJNIObjectPrivate m_javaMessageDialog; + bool m_shown; +}; + + +bool registerNatives(JNIEnv *env); + +} + +#endif // QANDROIDPLATFORMDIALOGHELPERS_H diff --git a/src/plugins/platforms/android/src/qandroidplatformintegration.cpp b/src/plugins/platforms/android/src/qandroidplatformintegration.cpp index a3db421de9..ae3e257d3c 100644 --- a/src/plugins/platforms/android/src/qandroidplatformintegration.cpp +++ b/src/plugins/platforms/android/src/qandroidplatformintegration.cpp @@ -61,6 +61,7 @@ # include "androidjnimenu.h" # include "qandroidopenglcontext.h" # include "qandroidopenglplatformwindow.h" +# include "qandroidopenglplatformscreen.h" # include "qeglfshooks.h" # include <QtGui/qopenglcontext.h> #endif @@ -123,7 +124,6 @@ bool QAndroidPlatformIntegration::hasCapability(Capability cap) const switch (cap) { case ThreadedPixmaps: return true; case ApplicationState: return true; - case NonFullScreenWindows: return false; case NativeWidgets: return false; default: #ifndef ANDROID_PLUGIN_OPENGL @@ -142,7 +142,10 @@ QPlatformBackingStore *QAndroidPlatformIntegration::createPlatformBackingStore(Q QPlatformWindow *QAndroidPlatformIntegration::createPlatformWindow(QWindow *window) const { - return new QAndroidPlatformWindow(window); + QAndroidPlatformWindow *platformWindow = new QAndroidPlatformWindow(window); + platformWindow->setWindowState(window->windowState()); + + return platformWindow; } QAbstractEventDispatcher *QAndroidPlatformIntegration::createEventDispatcher() const @@ -155,6 +158,7 @@ QPlatformWindow *QAndroidPlatformIntegration::createPlatformWindow(QWindow *wind QAndroidOpenGLPlatformWindow *platformWindow = new QAndroidOpenGLPlatformWindow(window); platformWindow->create(); platformWindow->requestActivateWindow(); + platformWindow->setWindowState(window->windowState()); QtAndroidMenu::setActiveTopLevelWindow(window); return platformWindow; @@ -231,13 +235,22 @@ QPlatformServices *QAndroidPlatformIntegration::services() const QVariant QAndroidPlatformIntegration::styleHint(StyleHint hint) const { switch (hint) { - case ShowIsFullScreen: + case ShowIsMaximized: return true; default: return QPlatformIntegration::styleHint(hint); } } +Qt::WindowState QAndroidPlatformIntegration::defaultWindowState(Qt::WindowFlags flags) const +{ + // Don't maximize dialogs on Android + if (flags & Qt::Dialog & ~Qt::Window) + return Qt::WindowNoState; + + return QPlatformIntegration::defaultWindowState(flags); +} + static const QLatin1String androidThemeName("android"); QStringList QAndroidPlatformIntegration::themeNames() const { @@ -308,6 +321,11 @@ void QAndroidPlatformIntegration::setDisplayMetrics(int width, int height) m_defaultPhysicalSizeHeight = height; } +QEglFSScreen *QAndroidPlatformIntegration::createScreen() const +{ + return new QAndroidOpenGLPlatformScreen(display()); +} + #endif void QAndroidPlatformIntegration::pauseApp() diff --git a/src/plugins/platforms/android/src/qandroidplatformintegration.h b/src/plugins/platforms/android/src/qandroidplatformintegration.h index 5ebdf9e65c..bd08ad694c 100644 --- a/src/plugins/platforms/android/src/qandroidplatformintegration.h +++ b/src/plugins/platforms/android/src/qandroidplatformintegration.h @@ -121,6 +121,7 @@ public: #endif QVariant styleHint(StyleHint hint) const; + Qt::WindowState defaultWindowState(Qt::WindowFlags flags) const Q_DECL_OVERRIDE; QStringList themeNames() const; QPlatformTheme *createPlatformTheme(const QString &name) const; @@ -140,6 +141,10 @@ public: QTouchDevice *touchDevice() const { return m_touchDevice; } void setTouchDevice(QTouchDevice *touchDevice) { m_touchDevice = touchDevice; } +#ifdef ANDROID_PLUGIN_OPENGL + QEglFSScreen *createScreen() const; +#endif + private: friend class QEglFSAndroidHooks; diff --git a/src/plugins/platforms/android/src/qandroidplatformmenu.cpp b/src/plugins/platforms/android/src/qandroidplatformmenu.cpp index 253c22a12f..1ecabb25e2 100644 --- a/src/plugins/platforms/android/src/qandroidplatformmenu.cpp +++ b/src/plugins/platforms/android/src/qandroidplatformmenu.cpp @@ -141,6 +141,15 @@ bool QAndroidPlatformMenu::isVisible() const return m_isVisible; } +void QAndroidPlatformMenu::showPopup(const QWindow *parentWindow, QPoint pos, const QPlatformMenuItem *item) +{ + Q_UNUSED(parentWindow); + Q_UNUSED(pos); + Q_UNUSED(item); + setVisible(true); + QtAndroidMenu::showContextMenu(this); +} + QPlatformMenuItem *QAndroidPlatformMenu::menuItemAt(int position) const { if (position < m_menuItems.size()) @@ -154,7 +163,6 @@ QPlatformMenuItem *QAndroidPlatformMenu::menuItemForTag(quintptr tag) const if (menuItem->tag() == tag) return menuItem; } - return 0; } diff --git a/src/plugins/platforms/android/src/qandroidplatformmenu.h b/src/plugins/platforms/android/src/qandroidplatformmenu.h index 20236cb636..305b64168a 100644 --- a/src/plugins/platforms/android/src/qandroidplatformmenu.h +++ b/src/plugins/platforms/android/src/qandroidplatformmenu.h @@ -71,6 +71,7 @@ public: bool isEnabled() const; void setVisible(bool visible); bool isVisible() const; + void showPopup(const QWindow *parentWindow, QPoint pos, const QPlatformMenuItem *item); QPlatformMenuItem *menuItemAt(int position) const; QPlatformMenuItem *menuItemForTag(quintptr tag) const; diff --git a/src/plugins/platforms/android/src/qandroidplatformmenubar.cpp b/src/plugins/platforms/android/src/qandroidplatformmenubar.cpp index ef1ac61356..134062fb32 100644 --- a/src/plugins/platforms/android/src/qandroidplatformmenubar.cpp +++ b/src/plugins/platforms/android/src/qandroidplatformmenubar.cpp @@ -79,6 +79,8 @@ void QAndroidPlatformMenuBar::syncMenu(QPlatformMenu *menu) void QAndroidPlatformMenuBar::handleReparent(QWindow *newParentWindow) { + if (m_parentWindow == newParentWindow) + return; m_parentWindow = newParentWindow; QtAndroidMenu::setMenuBar(this, newParentWindow); } diff --git a/src/plugins/platforms/android/src/qandroidplatformtheme.cpp b/src/plugins/platforms/android/src/qandroidplatformtheme.cpp index 79fbc440fc..308bb70faf 100644 --- a/src/plugins/platforms/android/src/qandroidplatformtheme.cpp +++ b/src/plugins/platforms/android/src/qandroidplatformtheme.cpp @@ -43,6 +43,7 @@ #include "qandroidplatformmenubar.h" #include "qandroidplatformmenu.h" #include "qandroidplatformmenuitem.h" +#include "qandroidplatformdialoghelpers.h" #include <QVariant> #include <QFileInfo> #include <qandroidplatformintegration.h> @@ -124,7 +125,7 @@ const QFont *QAndroidPlatformTheme::font(Font type) const return &(it.value()); // default in case the style has not set a font - static QFont systemFont("Roboto", 12.0 * 100 / 72); // keep default size the same after changing from 100 dpi to 72 dpi + static QFont systemFont("Roboto", 14.0 * 100 / 72); // keep default size the same after changing from 100 dpi to 72 dpi if (type == QPlatformTheme::SystemFont) return &systemFont; return 0; @@ -150,3 +151,20 @@ QVariant QAndroidPlatformTheme::themeHint(ThemeHint hint) const return QPlatformTheme::themeHint(hint); } } + +bool QAndroidPlatformTheme::usePlatformNativeDialog(QPlatformTheme::DialogType type) const +{ + if (type == MessageDialog) + return qgetenv("QT_USE_ANDROID_NATIVE_DIALOGS").toInt() == 1; + return false; +} + +QPlatformDialogHelper *QAndroidPlatformTheme::createPlatformDialogHelper(QPlatformTheme::DialogType type) const +{ + switch (type) { + case MessageDialog: + return new QtAndroidDialogHelpers::QAndroidPlatformMessageDialogHelper; + default: + return 0; + } +} diff --git a/src/plugins/platforms/android/src/qandroidplatformtheme.h b/src/plugins/platforms/android/src/qandroidplatformtheme.h index 15d2cb2000..ec259a9b0a 100644 --- a/src/plugins/platforms/android/src/qandroidplatformtheme.h +++ b/src/plugins/platforms/android/src/qandroidplatformtheme.h @@ -54,6 +54,9 @@ public: virtual const QPalette *palette(Palette type = SystemPalette) const; virtual const QFont *font(Font type = SystemFont) const; virtual QVariant themeHint(ThemeHint hint) const; + virtual bool usePlatformNativeDialog(DialogType type) const; + virtual QPlatformDialogHelper *createPlatformDialogHelper(DialogType type) const; + private: QAndroidPlatformNativeInterface * m_androidPlatformNativeInterface; diff --git a/src/plugins/platforms/android/src/raster/qandroidplatformscreen.cpp b/src/plugins/platforms/android/src/raster/qandroidplatformscreen.cpp index 0250a6122c..2e59c307c3 100644 --- a/src/plugins/platforms/android/src/raster/qandroidplatformscreen.cpp +++ b/src/plugins/platforms/android/src/raster/qandroidplatformscreen.cpp @@ -43,6 +43,7 @@ #include "qandroidplatformintegration.h" #include "androidjnimain.h" #include "androidjnimenu.h" +#include "qandroidplatformwindow.h" QAndroidPlatformScreen::QAndroidPlatformScreen():QFbScreen() { @@ -57,6 +58,12 @@ QAndroidPlatformScreen::QAndroidPlatformScreen():QFbScreen() void QAndroidPlatformScreen::topWindowChanged(QWindow *w) { QtAndroidMenu::setActiveTopLevelWindow(w); + + if (w != 0) { + QAndroidPlatformWindow *platformWindow = static_cast<QAndroidPlatformWindow *>(w->handle()); + if (platformWindow != 0) + platformWindow->updateStatusBarVisibility(); + } } QRegion QAndroidPlatformScreen::doRedraw() diff --git a/src/plugins/platforms/android/src/raster/qandroidplatformwindow.cpp b/src/plugins/platforms/android/src/raster/qandroidplatformwindow.cpp index f5fce0ae34..7ff18526d9 100644 --- a/src/plugins/platforms/android/src/raster/qandroidplatformwindow.cpp +++ b/src/plugins/platforms/android/src/raster/qandroidplatformwindow.cpp @@ -44,7 +44,8 @@ #include "androidjnimain.h" #include <qpa/qwindowsysteminterface.h> -QAndroidPlatformWindow::QAndroidPlatformWindow(QWindow *window) : QFbWindow(window) +QAndroidPlatformWindow::QAndroidPlatformWindow(QWindow *window) + : QFbWindow(window) { } @@ -58,8 +59,40 @@ void QAndroidPlatformWindow::propagateSizeHints() //shut up warning from default implementation } +void QAndroidPlatformWindow::updateStatusBarVisibility() +{ + Qt::WindowFlags flags = window()->flags(); + bool isNonRegularWindow = flags & (Qt::Popup | Qt::Dialog | Qt::Sheet) & ~Qt::Window; + if (!isNonRegularWindow) { + if (mWindowState & Qt::WindowFullScreen) + QtAndroid::hideStatusBar(); + else if (mWindowState & Qt::WindowMaximized) + QtAndroid::showStatusBar(); + } +} + +void QAndroidPlatformWindow::raise() +{ + updateStatusBarVisibility(); + QFbWindow::raise(); +} + +void QAndroidPlatformWindow::setWindowState(Qt::WindowState state) +{ + if (mWindowState == state) + return; + + if (window()->isVisible()) + updateStatusBarVisibility(); + + QFbWindow::setWindowState(state); +} + void QAndroidPlatformWindow::setVisible(bool visible) { + if (visible) + updateStatusBarVisibility(); + QFbWindow::setVisible(visible); // The Android Activity is activated before Qt is initialized, causing the application state to diff --git a/src/plugins/platforms/android/src/raster/qandroidplatformwindow.h b/src/plugins/platforms/android/src/raster/qandroidplatformwindow.h index 58e6451ea1..9e3f203201 100644 --- a/src/plugins/platforms/android/src/raster/qandroidplatformwindow.h +++ b/src/plugins/platforms/android/src/raster/qandroidplatformwindow.h @@ -52,11 +52,13 @@ public: void propagateSizeHints(); + void raise(); + void setWindowState(Qt::WindowState state); void setVisible(bool visible); + void updateStatusBarVisibility(); public slots: void setGeometry(const QRect &rect); - }; #endif // ANDROIDPLATFORMWINDOW_H diff --git a/src/plugins/platforms/android/src/src.pri b/src/plugins/platforms/android/src/src.pri index 6cc41c3e68..9b64e846f7 100644 --- a/src/plugins/platforms/android/src/src.pri +++ b/src/plugins/platforms/android/src/src.pri @@ -21,6 +21,7 @@ SOURCES += $$PWD/androidplatformplugin.cpp \ $$PWD/qandroidinputcontext.cpp \ $$PWD/qandroidplatformaccessibility.cpp \ $$PWD/qandroidplatformfontdatabase.cpp \ + $$PWD/qandroidplatformdialoghelpers.cpp \ $$PWD/qandroidplatformclipboard.cpp \ $$PWD/qandroidplatformtheme.cpp \ $$PWD/qandroidplatformmenubar.cpp \ @@ -41,6 +42,7 @@ HEADERS += $$PWD/qandroidplatformintegration.h \ $$PWD/qandroidplatformaccessibility.h \ $$PWD/qandroidplatformfontdatabase.h \ $$PWD/qandroidplatformclipboard.h \ + $$PWD/qandroidplatformdialoghelpers.h \ $$PWD/qandroidplatformtheme.h \ $$PWD/qandroidplatformmenubar.h \ $$PWD/qandroidplatformmenu.h \ diff --git a/src/plugins/platforms/cocoa/images/sizeallcursor.png b/src/plugins/platforms/cocoa/images/sizeallcursor.png Binary files differnew file mode 100644 index 0000000000..bb5381ba32 --- /dev/null +++ b/src/plugins/platforms/cocoa/images/sizeallcursor.png diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibility.mm b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm index e135f36e78..1371eb3658 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibility.mm +++ b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm @@ -40,8 +40,7 @@ ****************************************************************************/ #include "qcocoaaccessibility.h" #include "qcocoaaccessibilityelement.h" -#include <qaccessible.h> -#include <QtGui/private/qaccessible2_p.h> +#include <QtGui/qaccessible.h> #include <private/qcore_mac_p.h> QCocoaAccessibility::QCocoaAccessibility() @@ -56,12 +55,8 @@ QCocoaAccessibility::~QCocoaAccessibility() void QCocoaAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event) { - QObject *object = event->object(); - if (!object) - return; - - QAccessibleInterface *interface = event->accessibleInterface(); - if (!interface) + QAccessible::Id interfaceId = event->uniqueId(); + if (!interfaceId) return; switch (event->type()) { @@ -69,7 +64,7 @@ void QCocoaAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event) case QAccessible::TextInserted : case QAccessible::TextRemoved : case QAccessible::TextUpdated : { - QCocoaAccessibleElement *element = [QCocoaAccessibleElement createElementWithId : QAccessible::uniqueId(interface) parent : nil]; + QCocoaAccessibleElement *element = [QCocoaAccessibleElement createElementWithId : interfaceId parent : nil]; [element autorelease]; NSAccessibilityPostNotification(element, NSAccessibilityValueChangedNotification); break; } diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm index 7eae22f720..8e20a96a48 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm +++ b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm @@ -42,8 +42,7 @@ #include "qcocoaaccessibility.h" #include "qcocoahelpers.h" -#include <QAccessible> -#include <QtGui/private/qaccessible2_p.h> +#include <QtGui/qaccessible.h> #import <AppKit/NSAccessibility.h> diff --git a/src/plugins/platforms/cocoa/qcocoaapplication.mm b/src/plugins/platforms/cocoa/qcocoaapplication.mm index c293f4cd52..551a59823c 100644 --- a/src/plugins/platforms/cocoa/qcocoaapplication.mm +++ b/src/plugins/platforms/cocoa/qcocoaapplication.mm @@ -115,8 +115,8 @@ QT_USE_NAMESPACE QCocoaPostMessageArgs *args = reinterpret_cast<QCocoaPostMessageArgs *>(lower | (upper << 32)); // Special case for convenience: if the argument is an NSNumber, we unbox it directly. // Use NSValue instead if this behaviour is unwanted. - id a1 = ([args->arg1 isKindOfClass:[NSNumber class]]) ? (id)[args->arg1 intValue] : args->arg1; - id a2 = ([args->arg2 isKindOfClass:[NSNumber class]]) ? (id)[args->arg2 intValue] : args->arg2; + id a1 = ([args->arg1 isKindOfClass:[NSNumber class]]) ? (id)[args->arg1 longValue] : args->arg1; + id a2 = ([args->arg2 isKindOfClass:[NSNumber class]]) ? (id)[args->arg2 longValue] : args->arg2; switch (args->argCount) { case 0: [args->target performSelector:args->selector]; diff --git a/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm b/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm index d90d77ec1d..be2bab8ce7 100644 --- a/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm +++ b/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm @@ -82,6 +82,7 @@ static NSButton *macCreateButton(const char *text, NSView *superview) BOOL mDialogIsExecuting; BOOL mResultSet; }; +- (void)restoreOriginalContentView; - (void)relayout; - (void)updateQtColor; - (void)finishOffWithCode:(NSInteger)code; diff --git a/src/plugins/platforms/cocoa/qcocoacursor.h b/src/plugins/platforms/cocoa/qcocoacursor.h index dfa1fcff81..f332240724 100644 --- a/src/plugins/platforms/cocoa/qcocoacursor.h +++ b/src/plugins/platforms/cocoa/qcocoacursor.h @@ -55,12 +55,13 @@ public: QCocoaCursor(); ~QCocoaCursor(); - virtual void changeCursor(QCursor * widgetCursor, QWindow * widget); + virtual void changeCursor(QCursor *cursor, QWindow *window); virtual QPoint pos() const; virtual void setPos(const QPoint &position); private: QHash<Qt::CursorShape, NSCursor *> m_cursors; - NSCursor *createCursorData(QCursor *); + NSCursor *convertCursor(QCursor *cursor); + NSCursor *createCursorData(QCursor * cursor); NSCursor *createCursorFromBitmap(const QBitmap *bitmap, const QBitmap *mask, const QPoint hotspot = QPoint()); NSCursor *createCursorFromPixmap(const QPixmap pixmap, const QPoint hotspot = QPoint()); }; diff --git a/src/plugins/platforms/cocoa/qcocoacursor.mm b/src/plugins/platforms/cocoa/qcocoacursor.mm index e5b41e7a88..13f6423701 100644 --- a/src/plugins/platforms/cocoa/qcocoacursor.mm +++ b/src/plugins/platforms/cocoa/qcocoacursor.mm @@ -40,6 +40,7 @@ ****************************************************************************/ #include "qcocoacursor.h" +#include "qcocoawindow.h" #include "qcocoahelpers.h" #include "qcocoaautoreleasepool.h" @@ -63,82 +64,90 @@ QCocoaCursor::~QCocoaCursor() void QCocoaCursor::changeCursor(QCursor *cursor, QWindow *window) { - Q_UNUSED(window); + NSCursor * cocoaCursor = convertCursor(cursor); + if (QPlatformWindow * platformWindow = window->handle()) + static_cast<QCocoaWindow *>(platformWindow)->setWindowCursor(cocoaCursor); +} + +QPoint QCocoaCursor::pos() const +{ + return qt_mac_flipPoint([NSEvent mouseLocation]).toPoint(); +} + +void QCocoaCursor::setPos(const QPoint &position) +{ + CGPoint pos; + pos.x = position.x(); + pos.y = position.y(); + + CGEventRef e = CGEventCreateMouseEvent(0, kCGEventMouseMoved, pos, 0); + CGEventPost(kCGHIDEventTap, e); + CFRelease(e); +} + +NSCursor *QCocoaCursor::convertCursor(QCursor * cursor) +{ const Qt::CursorShape newShape = cursor ? cursor->shape() : Qt::ArrowCursor; + NSCursor *cocoaCursor; + // Check for a suitable built-in NSCursor first: switch (newShape) { case Qt::ArrowCursor: - [[NSCursor arrowCursor] set]; + cocoaCursor= [NSCursor arrowCursor]; break; case Qt::CrossCursor: - [[NSCursor crosshairCursor] set]; + cocoaCursor = [NSCursor crosshairCursor]; break; case Qt::IBeamCursor: - [[NSCursor IBeamCursor] set]; + cocoaCursor = [NSCursor IBeamCursor]; break; case Qt::WhatsThisCursor: //for now just use the pointing hand case Qt::PointingHandCursor: - [[NSCursor pointingHandCursor] set]; + cocoaCursor = [NSCursor pointingHandCursor]; break; case Qt::SplitVCursor: - [[NSCursor resizeUpDownCursor] set]; + cocoaCursor = [NSCursor resizeUpDownCursor]; break; case Qt::SplitHCursor: - [[NSCursor resizeLeftRightCursor] set]; + cocoaCursor = [NSCursor resizeLeftRightCursor]; break; case Qt::OpenHandCursor: - [[NSCursor openHandCursor] set]; + cocoaCursor = [NSCursor openHandCursor]; break; case Qt::ClosedHandCursor: - [[NSCursor closedHandCursor] set]; + cocoaCursor = [NSCursor closedHandCursor]; break; case Qt::DragMoveCursor: - [[NSCursor crosshairCursor] set]; + cocoaCursor = [NSCursor crosshairCursor]; break; case Qt::DragCopyCursor: - [[NSCursor crosshairCursor] set]; + cocoaCursor = [NSCursor crosshairCursor]; break; case Qt::DragLinkCursor: - [[NSCursor dragLinkCursor] set]; + cocoaCursor = [NSCursor dragLinkCursor]; break; default : { // No suitable OS cursor exist, use cursors provided // by Qt for the rest. Check for a cached cursor: - NSCursor *cocoaCursor = m_cursors.value(newShape); + cocoaCursor = m_cursors.value(newShape); if (cocoaCursor && cursor->shape() == Qt::BitmapCursor) { [cocoaCursor release]; cocoaCursor = 0; } if (cocoaCursor == 0) { cocoaCursor = createCursorData(cursor); - if (cocoaCursor == 0) { - [[NSCursor arrowCursor] set]; - return; - } + if (cocoaCursor == 0) + return [NSCursor arrowCursor]; + m_cursors.insert(newShape, cocoaCursor); } - [cocoaCursor set]; break; } } + return cocoaCursor; } -QPoint QCocoaCursor::pos() const -{ - return qt_mac_flipPoint([NSEvent mouseLocation]).toPoint(); -} - -void QCocoaCursor::setPos(const QPoint &position) -{ - CGPoint pos; - pos.x = position.x(); - pos.y = position.y(); - - CGEventRef e = CGEventCreateMouseEvent(0, kCGEventMouseMoved, pos, 0); - CGEventPost(kCGHIDEventTap, e); - CFRelease(e); -} // Creates an NSCursor for the given QCursor. NSCursor *QCocoaCursor::createCursorData(QCursor *cursor) @@ -218,8 +227,8 @@ NSCursor *QCocoaCursor::createCursorData(QCursor *cursor) return createCursorFromPixmap(pixmap, hotspot); break; } case Qt::SizeAllCursor: { - QPixmap pixmap = QPixmap(QLatin1String(":/qt-project.org/mac/cursors/images/pluscursor.png")); - return createCursorFromPixmap(pixmap, hotspot); + QPixmap pixmap = QPixmap(QLatin1String(":/qt-project.org/mac/cursors/images/sizeallcursor.png")); + return createCursorFromPixmap(pixmap, QPoint(8, 8)); break; } case Qt::BusyCursor: { QPixmap pixmap = QPixmap(QLatin1String(":/qt-project.org/mac/cursors/images/waitcursor.png")); diff --git a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm index b3bc4a8ebf..1ad833ee44 100644 --- a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm +++ b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm @@ -61,12 +61,12 @@ #include <stdlib.h> #include <qabstracteventdispatcher.h> #include "qcocoaautoreleasepool.h" -#include <QFileSystemWatcher> #include <QDir> #include <qpa/qplatformnativeinterface.h> #import <AppKit/NSSavePanel.h> +#import <CoreFoundation/CFNumber.h> QT_FORWARD_DECLARE_CLASS(QString) QT_FORWARD_DECLARE_CLASS(QStringList) @@ -74,30 +74,6 @@ QT_FORWARD_DECLARE_CLASS(QFileInfo) QT_FORWARD_DECLARE_CLASS(QWindow) QT_USE_NAMESPACE -class CachedEntries: public QObject { -public: - CachedEntries(QDir::Filters filters) : mFilters(filters) { - QObject::connect(&mFSWatcher, &QFileSystemWatcher::directoryChanged, this, &CachedEntries::updateDirCache); - } - QString directory() const { - const QStringList &dirs = mFSWatcher.directories(); - return (dirs.count() ? dirs[0] : QString()); - } - QStringList entries() const { - return mQDirFilterEntryList; - } - void updateDirCache(const QString &path) { - mFSWatcher.removePaths(mFSWatcher.directories()); - mFSWatcher.addPath(path); - mQDirFilterEntryList = QDir(path).entryList(mFilters); - } - -private: - QFileSystemWatcher mFSWatcher; - QStringList mQDirFilterEntryList; - QDir::Filters mFilters; -}; - typedef QSharedPointer<QFileDialogOptions> SharedPointerFileDialogOptions; @class QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate); @@ -117,7 +93,6 @@ typedef QSharedPointer<QFileDialogOptions> SharedPointerFileDialogOptions; int mReturnCode; SharedPointerFileDialogOptions mOptions; - CachedEntries *mCachedEntries; QString *mCurrentSelection; QStringList *mNameFilterDropDownList; QStringList *mSelectedNameFilter; @@ -164,7 +139,6 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSOpenSavePanelDelegate); [mSavePanel setDelegate:self]; mReturnCode = -1; mHelper = helper; - mCachedEntries = new CachedEntries(mOptions->filter()); mNameFilterDropDownList = new QStringList(mOptions->nameFilters()); QString selectedVisualNameFilter = mOptions->initiallySelectedNameFilter(); mSelectedNameFilter = new QStringList([self findStrippedFilterWithVisualFilterName:selectedVisualNameFilter]); @@ -197,7 +171,6 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSOpenSavePanelDelegate); - (void)dealloc { - delete mCachedEntries; delete mNameFilterDropDownList; delete mSelectedNameFilter; delete mCurrentSelection; @@ -308,6 +281,22 @@ static QString strippedText(QString s) }]; } +- (BOOL)isHiddenFile:(NSString *)filename isDir:(BOOL)isDir +{ + CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (CFStringRef)filename, kCFURLPOSIXPathStyle, isDir); + CFBooleanRef isHidden; + Boolean errorOrHidden = false; + if (!CFURLCopyResourcePropertyForKey(url, kCFURLIsHiddenKey, &isHidden, NULL)) { + errorOrHidden = true; + } else { + if (CFBooleanGetValue(isHidden)) + errorOrHidden = true; + CFRelease(isHidden); + } + CFRelease(url); + return errorOrHidden; +} + - (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename { Q_UNUSED(sender); @@ -316,8 +305,13 @@ static QString strippedText(QString s) return NO; // Always accept directories regardless of their names (unless it is a bundle): - BOOL isDir; - if ([[NSFileManager defaultManager] fileExistsAtPath:filename isDirectory:&isDir] && isDir) { + NSFileManager *fm = [NSFileManager defaultManager]; + NSDictionary *fileAttrs = [fm attributesOfItemAtPath:filename error:nil]; + if (!fileAttrs) + return NO; // Error accessing the file means 'no'. + NSString *fileType = [fileAttrs fileType]; + bool isDir = [fileType isEqualToString:NSFileTypeDirectory]; + if (isDir) { if ([mSavePanel treatsFilePackagesAsDirectories] == NO) { if ([[NSWorkspace sharedWorkspace] isFilePackageAtPath:filename] == NO) return YES; @@ -325,24 +319,35 @@ static QString strippedText(QString s) } QString qtFileName = QCFString::toQString(filename); - QFileInfo info(qtFileName.normalized(QString::NormalizationForm_C)); - QString path = info.absolutePath(); - if (mCachedEntries->directory() != path) { - mCachedEntries->updateDirCache(path); - } - // Check if the QDir filter accepts the file: - if (!mCachedEntries->entries().contains(info.fileName())) - return NO; - // No filter means accept everything - if (mSelectedNameFilter->isEmpty()) - return YES; + bool nameMatches = mSelectedNameFilter->isEmpty(); // Check if the current file name filter accepts the file: - for (int i=0; i<mSelectedNameFilter->size(); ++i) { + for (int i = 0; !nameMatches && i < mSelectedNameFilter->size(); ++i) { if (QDir::match(mSelectedNameFilter->at(i), qtFileName)) - return YES; + nameMatches = true; + } + if (!nameMatches) + return NO; + + QDir::Filters filter = mOptions->filter(); + if ((!(filter & (QDir::Dirs | QDir::AllDirs)) && isDir) + || (!(filter & QDir::Files) && [fileType isEqualToString:NSFileTypeRegular]) + || ((filter & QDir::NoSymLinks) && [fileType isEqualToString:NSFileTypeSymbolicLink])) + return NO; + + bool filterPermissions = ((filter & QDir::PermissionMask) + && (filter & QDir::PermissionMask) != QDir::PermissionMask); + if (filterPermissions) { + if ((!(filter & QDir::Readable) && [fm isReadableFileAtPath:filename]) + || (!(filter & QDir::Writable) && [fm isWritableFileAtPath:filename]) + || (!(filter & QDir::Executable) && [fm isExecutableFileAtPath:filename])) + return NO; } - return NO; + if (!(filter & QDir::Hidden) + && (qtFileName.startsWith(QLatin1Char('.')) || [self isHiddenFile:filename isDir:isDir])) + return NO; + + return YES; } - (NSString *)panel:(id)sender userEnteredFilename:(NSString *)filename confirmed:(BOOL)okFlag @@ -461,7 +466,7 @@ static QString strippedText(QString s) Q_UNUSED(sender); if (!mHelper) return; - if ([path isEqualToString:mCurrentDir]) + if (!(path && path.length) || [path isEqualToString:mCurrentDir]) return; [mCurrentDir release]; diff --git a/src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm b/src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm index 91fb52eb6d..dc22da0983 100644 --- a/src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm +++ b/src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm @@ -121,6 +121,7 @@ static QFont qfontForCocoaFont(NSFont *cocoaFont, const QFont &resolveFont) BOOL mDialogIsExecuting; BOOL mResultSet; }; +- (void)restoreOriginalContentView; - (void)relayout; - (void)relayoutToContentSize:(NSSize)frameSize; - (void)updateQtFont; diff --git a/src/plugins/platforms/cocoa/qcocoaglcontext.h b/src/plugins/platforms/cocoa/qcocoaglcontext.h index 29affb0e4a..e1d4b602c9 100644 --- a/src/plugins/platforms/cocoa/qcocoaglcontext.h +++ b/src/plugins/platforms/cocoa/qcocoaglcontext.h @@ -77,6 +77,7 @@ public: private: void setActiveWindow(QWindow *window); + void updateSurfaceFormat(); NSOpenGLContext *m_context; NSOpenGLContext *m_shareContext; diff --git a/src/plugins/platforms/cocoa/qcocoaglcontext.mm b/src/plugins/platforms/cocoa/qcocoaglcontext.mm index 3dee137038..144144338f 100644 --- a/src/plugins/platforms/cocoa/qcocoaglcontext.mm +++ b/src/plugins/platforms/cocoa/qcocoaglcontext.mm @@ -48,6 +48,74 @@ #import <Cocoa/Cocoa.h> +static inline QByteArray getGlString(GLenum param) +{ + if (const GLubyte *s = glGetString(param)) + return QByteArray(reinterpret_cast<const char*>(s)); + return QByteArray(); +} + +#if !defined(GL_CONTEXT_FLAGS) +#define GL_CONTEXT_FLAGS 0x821E +#endif + +#if !defined(GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT) +#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x0001 +#endif + +#if !defined(GL_CONTEXT_PROFILE_MASK) +#define GL_CONTEXT_PROFILE_MASK 0x9126 +#endif + +#if !defined(GL_CONTEXT_CORE_PROFILE_BIT) +#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001 +#endif + +#if !defined(GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) +#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002 +#endif + +static void updateFormatFromContext(QSurfaceFormat *format) +{ + Q_ASSERT(format); + + // Update the version, profile, and context bit of the format + int major = 0, minor = 0; + QByteArray versionString(getGlString(GL_VERSION)); + if (QPlatformOpenGLContext::parseOpenGLVersion(versionString, major, minor)) { + format->setMajorVersion(major); + format->setMinorVersion(minor); + } + + format->setProfile(QSurfaceFormat::NoProfile); + + Q_ASSERT(format->renderableType() == QSurfaceFormat::OpenGL); + if (format->version() < qMakePair(3, 0)) { + format->setOption(QSurfaceFormat::DeprecatedFunctions); + return; + } + + // Version 3.0 onwards - check if it includes deprecated functionality + GLint value = 0; + glGetIntegerv(GL_CONTEXT_FLAGS, &value); + if (!(value & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT)) + format->setOption(QSurfaceFormat::DeprecatedFunctions); + + // Debug context option not supported on OS X + + if (format->version() < qMakePair(3, 2)) + return; + + // Version 3.2 and newer have a profile + value = 0; + glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &value); + + if (value & GL_CONTEXT_CORE_PROFILE_BIT) + format->setProfile(QSurfaceFormat::CoreProfile); + else if (value & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) + format->setProfile(QSurfaceFormat::CompatibilityProfile); +} + QCocoaGLContext::QCocoaGLContext(const QSurfaceFormat &format, QPlatformOpenGLContext *share) : m_context(nil), m_shareContext(nil), @@ -82,6 +150,8 @@ QCocoaGLContext::QCocoaGLContext(const QSurfaceFormat &format, QPlatformOpenGLCo int zeroOpacity = 0; [m_context setValues:&zeroOpacity forParameter:NSOpenGLCPSurfaceOpacity]; } + + updateSurfaceFormat(); } QCocoaGLContext::~QCocoaGLContext() @@ -112,6 +182,8 @@ bool QCocoaGLContext::makeCurrent(QPlatformSurface *surface) QWindow *window = static_cast<QCocoaWindow *>(surface)->window(); setActiveWindow(window); + if (![m_context view]) + return false; [m_context makeCurrentContext]; update(); return true; @@ -135,6 +207,74 @@ void QCocoaGLContext::setActiveWindow(QWindow *window) [(QNSView *) cocoaWindow->contentView() setQCocoaGLContext:this]; } +void QCocoaGLContext::updateSurfaceFormat() +{ + // At present it is impossible to turn an option off on a QSurfaceFormat (see + // https://codereview.qt-project.org/#change,70599). So we have to populate + // the actual surface format from scratch + QSurfaceFormat requestedFormat = m_format; + m_format = QSurfaceFormat(); + m_format.setRenderableType(QSurfaceFormat::OpenGL); + + // CoreGL doesn't require a drawable to make the context current + CGLContextObj oldContext = CGLGetCurrentContext(); + CGLContextObj ctx = static_cast<CGLContextObj>([m_context CGLContextObj]); + CGLSetCurrentContext(ctx); + + // Get the data that OpenGL provides + updateFormatFromContext(&m_format); + + // Get the data contained within the pixel format + CGLPixelFormatObj cglPixelFormat = static_cast<CGLPixelFormatObj>(CGLGetPixelFormat(ctx)); + NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithCGLPixelFormatObj:cglPixelFormat]; + + int colorSize = -1; + [pixelFormat getValues:&colorSize forAttribute:NSOpenGLPFAColorSize forVirtualScreen:0]; + if (colorSize > 0) { + // This seems to return the total color buffer depth, including alpha + m_format.setRedBufferSize(colorSize / 4); + m_format.setGreenBufferSize(colorSize / 4); + m_format.setBlueBufferSize(colorSize / 4); + } + + // The pixel format always seems to return 8 for alpha. However, the framebuffer only + // seems to have alpha enabled if we requested it explicitly. I can't find any other + // attribute to check explicitly for this so we use our best guess for alpha. + int alphaSize = -1; + [pixelFormat getValues:&alphaSize forAttribute:NSOpenGLPFAAlphaSize forVirtualScreen:0]; + if (alphaSize > 0 && requestedFormat.alphaBufferSize() > 0) + m_format.setAlphaBufferSize(alphaSize); + + int depthSize = -1; + [pixelFormat getValues:&depthSize forAttribute:NSOpenGLPFADepthSize forVirtualScreen:0]; + if (depthSize > 0) + m_format.setDepthBufferSize(depthSize); + + int stencilSize = -1; + [pixelFormat getValues:&stencilSize forAttribute:NSOpenGLPFAStencilSize forVirtualScreen:0]; + if (stencilSize > 0) + m_format.setStencilBufferSize(stencilSize); + + int samples = -1; + [pixelFormat getValues:&samples forAttribute:NSOpenGLPFASamples forVirtualScreen:0]; + if (samples > 0) + m_format.setSamples(samples); + + int doubleBuffered = -1; + [pixelFormat getValues:&doubleBuffered forAttribute:NSOpenGLPFADoubleBuffer forVirtualScreen:0]; + m_format.setSwapBehavior(doubleBuffered == 1 ? QSurfaceFormat::DoubleBuffer : QSurfaceFormat::SingleBuffer); + + int steroBuffers = -1; + [pixelFormat getValues:&steroBuffers forAttribute:NSOpenGLPFAStereo forVirtualScreen:0]; + if (steroBuffers == 1) + m_format.setOption(QSurfaceFormat::StereoBuffers); + + [pixelFormat release]; + + // Restore the original context + CGLSetCurrentContext(oldContext); +} + void QCocoaGLContext::doneCurrent() { if (m_currentWindow && m_currentWindow.data()->handle()) diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm index 197a2058af..4a5696b35e 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.mm +++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm @@ -599,7 +599,17 @@ NSRect qt_mac_flipRect(const QRect &rect, QWindow *window) { QPlatformScreen *onScreen = QPlatformScreen::platformScreenForWindow(window); int flippedY = onScreen->geometry().height() - (rect.y() + rect.height()); - + QList<QScreen *> screens = QGuiApplication::screens(); + if (screens.size() > 1) { + int height = 0; + foreach (QScreen *scr, screens) + height = qMax(height, scr->size().height()); + int difference = height - onScreen->geometry().height(); + if (difference > 0) + flippedY += difference; + else + flippedY -= difference; + } // In case of automatic positioning, try to put as much of the window onscreen as possible. if (window->isTopLevel() && qt_window_private(const_cast<QWindow*>(window))->positionAutomatic && flippedY < 0) flippedY = onScreen->geometry().height() - onScreen->availableGeometry().height() - onScreen->availableGeometry().y(); diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index 2e42b9acda..0af635be6f 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.mm +++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm @@ -63,7 +63,6 @@ static void initResources() { - Q_INIT_RESOURCE_EXTERN(qcocoaresources) Q_INIT_RESOURCE(qcocoaresources); } diff --git a/src/plugins/platforms/cocoa/qcocoamenu.mm b/src/plugins/platforms/cocoa/qcocoamenu.mm index 14b8dee101..329c7a264a 100644 --- a/src/plugins/platforms/cocoa/qcocoamenu.mm +++ b/src/plugins/platforms/cocoa/qcocoamenu.mm @@ -84,6 +84,7 @@ static inline QCocoaMenuLoader *getMenuLoader() } - (id) initWithMenu:(QCocoaMenu*) m; +- (BOOL)hasShortcut:(NSMenu *)menu forKey:(NSString *)key forModifiers:(NSUInteger)modifier; @end @@ -465,17 +466,21 @@ void QCocoaMenu::showPopup(const QWindow *parentWindow, QPoint pos, const QPlatf nsPos.y = screen->availableVirtualSize().height() - nsPos.y; } - // Finally, we need to synthesize an event. - NSEvent *menuEvent = [NSEvent mouseEventWithType:NSRightMouseDown - location:nsPos - modifierFlags:0 - timestamp:0 - windowNumber:view ? view.window.windowNumber : 0 - context:nil - eventNumber:0 - clickCount:1 - pressure:1.0]; - [NSMenu popUpContextMenu:m_nativeMenu withEvent:menuEvent forView:view]; + if (view) { + // Finally, we need to synthesize an event. + NSEvent *menuEvent = [NSEvent mouseEventWithType:NSRightMouseDown + location:nsPos + modifierFlags:0 + timestamp:0 + windowNumber:view ? view.window.windowNumber : 0 + context:nil + eventNumber:0 + clickCount:1 + pressure:1.0]; + [NSMenu popUpContextMenu:m_nativeMenu withEvent:menuEvent forView:view]; + } else { + [m_nativeMenu popUpMenuPositioningItem:nsItem atLocation:nsPos inView:0]; + } } // The calls above block, and also swallow any mouse release event, diff --git a/src/plugins/platforms/cocoa/qcocoamenubar.mm b/src/plugins/platforms/cocoa/qcocoamenubar.mm index 0fea55ac68..5a8664747e 100644 --- a/src/plugins/platforms/cocoa/qcocoamenubar.mm +++ b/src/plugins/platforms/cocoa/qcocoamenubar.mm @@ -175,6 +175,7 @@ void QCocoaMenuBar::handleReparent(QWindow *newParentWindow) if (newParentWindow == NULL) { m_window = NULL; } else { + newParentWindow->create(); m_window = static_cast<QCocoaWindow*>(newParentWindow->handle()); m_window->setMenubar(this); } diff --git a/src/plugins/platforms/cocoa/qcocoamenuitem.mm b/src/plugins/platforms/cocoa/qcocoamenuitem.mm index 013f9931ff..3bba1ee1d5 100644 --- a/src/plugins/platforms/cocoa/qcocoamenuitem.mm +++ b/src/plugins/platforms/cocoa/qcocoamenuitem.mm @@ -338,6 +338,8 @@ QString QCocoaMenuItem::mergeText() return qt_mac_applicationmenu_string(4); } else if (m_native == [loader quitMenuItem]) { return qt_mac_applicationmenu_string(5).arg(qt_mac_applicationName()); + } else if (m_text.contains('\t')) { + return m_text.left(m_text.indexOf('\t')); } return m_text; } @@ -349,6 +351,8 @@ QKeySequence QCocoaMenuItem::mergeAccel() return QKeySequence(QKeySequence::Preferences); else if (m_native == [loader quitMenuItem]) return QKeySequence(QKeySequence::Quit); + else if (m_text.contains('\t')) + return QKeySequence(m_text.mid(m_text.indexOf('\t') + 1), QKeySequence::NativeText); return m_shortcut; } diff --git a/src/plugins/platforms/cocoa/qcocoaresources.qrc b/src/plugins/platforms/cocoa/qcocoaresources.qrc index 392300bb03..9e0640db7d 100644 --- a/src/plugins/platforms/cocoa/qcocoaresources.qrc +++ b/src/plugins/platforms/cocoa/qcocoaresources.qrc @@ -4,7 +4,7 @@ <file>images/forbiddencursor.png</file> <file>images/spincursor.png</file> <file>images/waitcursor.png</file> -<file>images/pluscursor.png</file> +<file>images/sizeallcursor.png</file> </qresource> <qresource prefix="/qt-project.org/mac/style"> <file>images/leopard-unified-toolbar-on.png</file> diff --git a/src/plugins/platforms/cocoa/qcocoawindow.h b/src/plugins/platforms/cocoa/qcocoawindow.h index 7f0f07e912..4f5a208f43 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.h +++ b/src/plugins/platforms/cocoa/qcocoawindow.h @@ -154,6 +154,8 @@ public: void setMenubar(QCocoaMenuBar *mb); QCocoaMenuBar *menubar() const; + void setWindowCursor(NSCursor *cursor); + void registerTouch(bool enable); qreal devicePixelRatio() const; @@ -190,11 +192,13 @@ public: // for QNSView Qt::WindowState m_synchedWindowState; Qt::WindowModality m_windowModality; QPointer<QWindow> m_activePopupWindow; - QPointer<QWindow> m_underMouseWindow; + QPointer<QWindow> m_enterLeaveTargetWindow; + bool m_windowUnderMouse; bool m_inConstructor; QCocoaGLContext *m_glContext; QCocoaMenuBar *m_menubar; + NSCursor *m_windowCursor; bool m_hasModalSession; bool m_frameStrutEventsEnabled; diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index 845cc1202f..4da47f4f1f 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -206,9 +206,11 @@ QCocoaWindow::QCocoaWindow(QWindow *tlw) , m_nsWindowDelegate(0) , m_synchedWindowState(Qt::WindowActive) , m_windowModality(Qt::NonModal) + , m_windowUnderMouse(false) , m_inConstructor(true) , m_glContext(0) , m_menubar(0) + , m_windowCursor(0) , m_hasModalSession(false) , m_frameStrutEventsEnabled(false) , m_isExposed(false) @@ -463,19 +465,19 @@ NSUInteger QCocoaWindow::windowStyleMask(Qt::WindowFlags flags) { Qt::WindowType type = static_cast<Qt::WindowType>(int(flags & Qt::WindowType_Mask)); NSInteger styleMask = NSBorderlessWindowMask; + if (flags & Qt::FramelessWindowHint) + return styleMask; if ((type & Qt::Popup) == Qt::Popup) { - if (!windowIsPopupType(type) && !(flags & Qt::FramelessWindowHint)) + if (!windowIsPopupType(type)) styleMask = (NSUtilityWindowMask | NSResizableWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSTitledWindowMask); } else { - // Filter flags for supported properties - flags &= Qt::WindowType_Mask | Qt::FramelessWindowHint | Qt::WindowTitleHint | - Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint | Qt::CustomizeWindowHint; - if (flags == Qt::Window) { + if (type == Qt::Window && !(flags & Qt::CustomizeWindowHint)) { styleMask = (NSResizableWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSTitledWindowMask); - } else if ((flags & Qt::Dialog) == Qt::Dialog) { + } else if (type == Qt::Dialog) { if (flags & Qt::CustomizeWindowHint) { - styleMask = NSResizableWindowMask; + if (flags & Qt::WindowMaximizeButtonHint) + styleMask = NSResizableWindowMask; if (flags & Qt::WindowTitleHint) styleMask |= NSTitledWindowMask; if (flags & Qt::WindowCloseButtonHint) @@ -485,8 +487,8 @@ NSUInteger QCocoaWindow::windowStyleMask(Qt::WindowFlags flags) } else { styleMask = NSResizableWindowMask | NSClosableWindowMask | NSTitledWindowMask; } - } else if (!(flags & Qt::FramelessWindowHint)) { - if ((flags & Qt::Dialog) || (flags & Qt::WindowMaximizeButtonHint)) + } else { + if (flags & Qt::WindowMaximizeButtonHint) styleMask |= NSResizableWindowMask; if (flags & Qt::WindowTitleHint) styleMask |= NSTitledWindowMask; @@ -711,7 +713,10 @@ WId QCocoaWindow::winId() const void QCocoaWindow::setParent(const QPlatformWindow *parentWindow) { // recreate the window for compatibility + bool unhideAfterRecreate = parentWindow && !m_contentViewIsToBeEmbedded && ![m_contentView isHidden]; recreateWindow(parentWindow); + if (unhideAfterRecreate) + [m_contentView setHidden:NO]; setCocoaGeometry(geometry()); } @@ -1030,6 +1035,23 @@ QCocoaMenuBar *QCocoaWindow::menubar() const return m_menubar; } +void QCocoaWindow::setWindowCursor(NSCursor *cursor) +{ + // This function is called (via QCocoaCursor) by Qt to set + // the cursor for this window. It can be called for a window + // that is not currenly under the mouse pointer (for example + // for a popup window.) Qt expects the set cursor to "stick": + // it should be accociated with the window until a different + // cursor is set. + + // Cocoa has different abstractions. We can set the cursor *now*: + if (m_windowUnderMouse) + [cursor set]; + // or we can set the cursor on mouse enter/leave using tracking + // areas. This is done in QNSView, save the cursor: + m_windowCursor = cursor; +} + void QCocoaWindow::registerTouch(bool enable) { m_registerTouchCount += enable ? 1 : -1; diff --git a/src/plugins/platforms/cocoa/qmacclipboard.mm b/src/plugins/platforms/cocoa/qmacclipboard.mm index 95143fd8ea..b5c50d676f 100644 --- a/src/plugins/platforms/cocoa/qmacclipboard.mm +++ b/src/plugins/platforms/cocoa/qmacclipboard.mm @@ -105,7 +105,8 @@ QMacPasteboard::~QMacPasteboard() for (int i = 0; i < promises.count(); ++i) { const Promise &promise = promises.at(i); QCFString flavor = QCFString(promise.convertor->flavorFor(promise.mime)); - promiseKeeper(paste, (PasteboardItemID)promise.itemId, flavor, this); + NSInteger pbItemId = promise.itemId; + promiseKeeper(paste, reinterpret_cast<PasteboardItemID>(pbItemId), flavor, this); } if (paste) @@ -311,9 +312,9 @@ QMacPasteboard::setMimeData(QMimeData *mime_src) int numItems = c->count(mime_src); for (int item = 0; item < numItems; ++item) { - const int itemID = item+1; //id starts at 1 + const NSInteger itemID = item+1; //id starts at 1 promises.append(QMacPasteboard::Promise(itemID, c, mimeType, mimeData, item)); - PasteboardPutItemFlavor(paste, (PasteboardItemID)itemID, QCFString(flavor), 0, kPasteboardFlavorNoFlags); + PasteboardPutItemFlavor(paste, reinterpret_cast<PasteboardItemID>(itemID), QCFString(flavor), 0, kPasteboardFlavorNoFlags); #ifdef DEBUG_PASTEBOARD qDebug(" - adding %d %s [%s] <%s> [%d]", itemID, qPrintable(mimeType), qPrintable(flavor), qPrintable(c->convertorName()), item); diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm index f90fc6b205..71c4de3b69 100644 --- a/src/plugins/platforms/cocoa/qnsview.mm +++ b/src/plugins/platforms/cocoa/qnsview.mm @@ -496,7 +496,7 @@ static QTouchDevice *touchDevice = 0; return YES; } -- (void)convertFromEvent:(NSEvent *)event toWindowPoint:(QPointF *)qtWindowPoint andScreenPoint:(QPointF *)qtScreenPoint +- (void)convertFromScreen:(NSPoint)mouseLocation toWindowPoint:(QPointF *)qtWindowPoint andScreenPoint:(QPointF *)qtScreenPoint { // Calculate the mouse position in the QWindow and Qt screen coordinate system, // starting from coordinates in the NSWindow coordinate system. @@ -516,23 +516,22 @@ static QTouchDevice *touchDevice = 0; // NSView and QWindow are equal coordinate systems: the QWindow covers the // entire NSView, and we've set the NSView's isFlipped property to true. - NSPoint nsWindowPoint = [event locationInWindow]; // NSWindow coordinates - - NSPoint nsViewPoint = [self convertPoint: nsWindowPoint fromView: nil]; // NSView/QWindow coordinates - *qtWindowPoint = QPointF(nsViewPoint.x, nsViewPoint.y); // NSView/QWindow coordinates - NSWindow *window = [self window]; + NSPoint nsWindowPoint; // Use convertRectToScreen if available (added in 10.7). #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 - if ([window respondsToSelector:@selector(convertRectToScreen:)]) { - NSRect screenRect = [window convertRectToScreen : NSMakeRect(nsWindowPoint.x, nsWindowPoint.y, 0, 0)]; // OS X screen coordinates - *qtScreenPoint = QPointF(screenRect.origin.x, qt_mac_flipYCoordinate(screenRect.origin.y)); // Qt screen coordinates + if ([window respondsToSelector:@selector(convertRectFromScreen:)]) { + NSRect windowRect = [window convertRectFromScreen:NSMakeRect(mouseLocation.x, mouseLocation.y, 1, 1)]; + nsWindowPoint = windowRect.origin; // NSWindow coordinates } else #endif { - NSPoint screenPoint = [window convertBaseToScreen : NSMakePoint(nsWindowPoint.x, nsWindowPoint.y)]; - *qtScreenPoint = QPointF(screenPoint.x, qt_mac_flipYCoordinate(screenPoint.y)); + nsWindowPoint = [window convertScreenToBase:mouseLocation]; // NSWindow coordinates } + NSPoint nsViewPoint = [self convertPoint: nsWindowPoint fromView: nil]; // NSView/QWindow coordinates + *qtWindowPoint = QPointF(nsViewPoint.x, nsViewPoint.y); // NSView/QWindow coordinates + + *qtScreenPoint = QPointF(mouseLocation.x, qt_mac_flipYCoordinate(mouseLocation.y)); // Qt screen coordinates } - (void)resetMouseButtons @@ -546,7 +545,7 @@ static QTouchDevice *touchDevice = 0; QPointF qtWindowPoint; QPointF qtScreenPoint; - [self convertFromEvent:theEvent toWindowPoint:&qtWindowPoint andScreenPoint:&qtScreenPoint]; + [self convertFromScreen:[NSEvent mouseLocation] toWindowPoint:&qtWindowPoint andScreenPoint:&qtScreenPoint]; ulong timestamp = [theEvent timestamp] * 1000; QCocoaDrag* nativeDrag = static_cast<QCocoaDrag *>(QGuiApplicationPrivate::platformIntegration()->drag()); @@ -672,7 +671,7 @@ static QTouchDevice *touchDevice = 0; // mouse moves delivered to it (Apple recommends keeping it OFF because there // is a performance hit). So it goes. NSUInteger trackingOptions = NSTrackingMouseEnteredAndExited | NSTrackingActiveInActiveApp - | NSTrackingInVisibleRect | NSTrackingMouseMoved; + | NSTrackingInVisibleRect | NSTrackingMouseMoved | NSTrackingCursorUpdate; NSTrackingArea *ta = [[[NSTrackingArea alloc] initWithRect:[self frame] options:trackingOptions owner:self @@ -681,6 +680,13 @@ static QTouchDevice *touchDevice = 0; [self addTrackingArea:ta]; } +-(void)cursorUpdate:(NSEvent *)theEvent +{ + Q_UNUSED(theEvent) + if (m_platformWindow->m_windowCursor) + [m_platformWindow->m_windowCursor set]; +} + - (void)mouseMoved:(NSEvent *)theEvent { if (m_window->flags() & Qt::WindowTransparentForInput) @@ -688,7 +694,7 @@ static QTouchDevice *touchDevice = 0; QPointF windowPoint; QPointF screenPoint; - [self convertFromEvent:theEvent toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; + [self convertFromScreen:[NSEvent mouseLocation] toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; QWindow *childWindow = m_platformWindow->childWindowAt(windowPoint.toPoint()); // Top-level windows generate enter-leave events for sub-windows. @@ -697,9 +703,9 @@ static QTouchDevice *touchDevice = 0; // handling mouseEnter and mouseLeave envents, since they are sent // individually to different views. if (m_platformWindow->m_nsWindow && childWindow) { - if (childWindow != m_platformWindow->m_underMouseWindow) { - QWindowSystemInterface::handleEnterLeaveEvent(childWindow, m_platformWindow->m_underMouseWindow, windowPoint, screenPoint); - m_platformWindow->m_underMouseWindow = childWindow; + if (childWindow != m_platformWindow->m_enterLeaveTargetWindow) { + QWindowSystemInterface::handleEnterLeaveEvent(childWindow, m_platformWindow->m_enterLeaveTargetWindow, windowPoint, screenPoint); + m_platformWindow->m_enterLeaveTargetWindow = childWindow; } } @@ -713,6 +719,8 @@ static QTouchDevice *touchDevice = 0; - (void)mouseEntered:(NSEvent *)theEvent { + m_platformWindow->m_windowUnderMouse = true; + if (m_window->flags() & Qt::WindowTransparentForInput) return [super mouseEntered:theEvent]; @@ -722,13 +730,15 @@ static QTouchDevice *touchDevice = 0; QPointF windowPoint; QPointF screenPoint; - [self convertFromEvent:theEvent toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; - m_platformWindow->m_underMouseWindow = m_platformWindow->childWindowAt(windowPoint.toPoint()); - QWindowSystemInterface::handleEnterEvent(m_platformWindow->m_underMouseWindow, windowPoint, screenPoint); + [self convertFromScreen:[NSEvent mouseLocation] toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; + m_platformWindow->m_enterLeaveTargetWindow = m_platformWindow->childWindowAt(windowPoint.toPoint()); + QWindowSystemInterface::handleEnterEvent(m_platformWindow->m_enterLeaveTargetWindow, windowPoint, screenPoint); } - (void)mouseExited:(NSEvent *)theEvent { + m_platformWindow->m_windowUnderMouse = false; + if (m_window->flags() & Qt::WindowTransparentForInput) return [super mouseExited:theEvent]; Q_UNUSED(theEvent); @@ -737,8 +747,8 @@ static QTouchDevice *touchDevice = 0; if (!m_platformWindow->m_nsWindow) return; - QWindowSystemInterface::handleLeaveEvent(m_platformWindow->m_underMouseWindow); - m_platformWindow->m_underMouseWindow = 0; + QWindowSystemInterface::handleLeaveEvent(m_platformWindow->m_enterLeaveTargetWindow); + m_platformWindow->m_enterLeaveTargetWindow = 0; } - (void)rightMouseDown:(NSEvent *)theEvent @@ -812,7 +822,7 @@ Q_GLOBAL_STATIC(QCocoaTabletDeviceDataHash, tabletDeviceDataHash) QPointF windowPoint; QPointF screenPoint; - [self convertFromEvent: theEvent toWindowPoint: &windowPoint andScreenPoint: &screenPoint]; + [self convertFromScreen:[NSEvent mouseLocation] toWindowPoint: &windowPoint andScreenPoint: &screenPoint]; uint deviceId = [theEvent deviceID]; if (!tabletDeviceDataHash->contains(deviceId)) { @@ -985,7 +995,7 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) const NSTimeInterval timestamp = [event timestamp]; QPointF windowPoint; QPointF screenPoint; - [self convertFromEvent:event toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; + [self convertFromScreen:[NSEvent mouseLocation] toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; QWindowSystemInterface::handleGestureEventWithRealValue(m_window, timestamp, Qt::ZoomNativeGesture, [event magnification], windowPoint, screenPoint); } @@ -1000,7 +1010,7 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) const NSTimeInterval timestamp = [event timestamp]; QPointF windowPoint; QPointF screenPoint; - [self convertFromEvent:event toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; + [self convertFromScreen:[NSEvent mouseLocation] toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; QWindowSystemInterface::handleGestureEventWithRealValue(m_window, timestamp, Qt::SmartZoomNativeGesture, zoomIn ? 1.0f : 0.0f, windowPoint, screenPoint); zoomIn = !zoomIn; @@ -1015,7 +1025,7 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) const NSTimeInterval timestamp = [event timestamp]; QPointF windowPoint; QPointF screenPoint; - [self convertFromEvent:event toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; + [self convertFromScreen:[NSEvent mouseLocation] toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; QWindowSystemInterface::handleGestureEventWithRealValue(m_window, timestamp, Qt::RotateNativeGesture, -[event rotation], windowPoint, screenPoint); } @@ -1028,7 +1038,7 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) const NSTimeInterval timestamp = [event timestamp]; QPointF windowPoint; QPointF screenPoint; - [self convertFromEvent:event toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; + [self convertFromScreen:[NSEvent mouseLocation] toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; qreal angle = 0.0f; if ([event deltaX] == 1) @@ -1052,7 +1062,7 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) const NSTimeInterval timestamp = [event timestamp]; QPointF windowPoint; QPointF screenPoint; - [self convertFromEvent:event toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; + [self convertFromScreen:[NSEvent mouseLocation] toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; QWindowSystemInterface::handleGestureEvent(m_window, timestamp, Qt::BeginNativeGesture, windowPoint, screenPoint); } @@ -1065,7 +1075,7 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) const NSTimeInterval timestamp = [event timestamp]; QPointF windowPoint; QPointF screenPoint; - [self convertFromEvent:event toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; + [self convertFromScreen:[NSEvent mouseLocation] toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; QWindowSystemInterface::handleGestureEvent(m_window, timestamp, Qt::EndNativeGesture, windowPoint, screenPoint); } @@ -1125,7 +1135,7 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) QPointF qt_windowPoint; QPointF qt_screenPoint; - [self convertFromEvent:theEvent toWindowPoint:&qt_windowPoint andScreenPoint:&qt_screenPoint]; + [self convertFromScreen:[NSEvent mouseLocation] toWindowPoint:&qt_windowPoint andScreenPoint:&qt_screenPoint]; NSTimeInterval timestamp = [theEvent timestamp]; ulong qt_timestamp = timestamp * 1000; diff --git a/src/plugins/platforms/cocoa/qnsviewaccessibility.mm b/src/plugins/platforms/cocoa/qnsviewaccessibility.mm index 86e5066fbb..e8f26aa8c4 100644 --- a/src/plugins/platforms/cocoa/qnsviewaccessibility.mm +++ b/src/plugins/platforms/cocoa/qnsviewaccessibility.mm @@ -47,7 +47,7 @@ #include "qcocoaaccessibilityelement.h" #include <qpa/qplatformintegration.h> -#include <QtGui/private/qaccessible2_p.h> +#include <QtGui/qaccessible.h> #include <QtCore/QDebug> #import <AppKit/NSAccessibility.h> diff --git a/src/plugins/platforms/eglfs/qeglfsbackingstore.cpp b/src/plugins/platforms/eglfs/qeglfsbackingstore.cpp index 8de8268616..03531916cf 100644 --- a/src/plugins/platforms/eglfs/qeglfsbackingstore.cpp +++ b/src/plugins/platforms/eglfs/qeglfsbackingstore.cpp @@ -113,7 +113,7 @@ void QEglFSBackingStore::flush(QWindow *window, const QRegion ®ion, const QPo #endif QEglFSWindow *rootWin = m_window->screen()->rootWindow(); - if (!rootWin) + if (!rootWin || !rootWin->isRaster()) return; m_window->create(); @@ -132,13 +132,14 @@ void QEglFSBackingStore::resize(const QSize &size, const QRegion &staticContents Q_UNUSED(staticContents); QEglFSWindow *rootWin = m_window->screen()->rootWindow(); - if (!rootWin) + if (!rootWin || !rootWin->isRaster()) return; m_image = QImage(size, QImage::Format_RGB32); m_window->create(); rootWin->screen()->rootContext()->makeCurrent(rootWin->window()); + initializeOpenGLFunctions(); if (m_texture) glDeleteTextures(1, &m_texture); diff --git a/src/plugins/platforms/eglfs/qeglfsbackingstore.h b/src/plugins/platforms/eglfs/qeglfsbackingstore.h index 535428aac1..9af856e8e7 100644 --- a/src/plugins/platforms/eglfs/qeglfsbackingstore.h +++ b/src/plugins/platforms/eglfs/qeglfsbackingstore.h @@ -43,6 +43,7 @@ #define QEGLFSBACKINGSTORE_H #include <qpa/qplatformbackingstore.h> +#include <QtGui/QOpenGLFunctions> #include <QImage> #include <QRegion> @@ -52,7 +53,7 @@ QT_BEGIN_NAMESPACE class QOpenGLPaintDevice; class QEglFSWindow; -class QEglFSBackingStore : public QPlatformBackingStore +class QEglFSBackingStore : public QPlatformBackingStore, public QOpenGLFunctions { public: QEglFSBackingStore(QWindow *window); diff --git a/src/plugins/platforms/eglfs/qeglfscompositor.cpp b/src/plugins/platforms/eglfs/qeglfscompositor.cpp index 9db43a57a1..845bb5b3b5 100644 --- a/src/plugins/platforms/eglfs/qeglfscompositor.cpp +++ b/src/plugins/platforms/eglfs/qeglfscompositor.cpp @@ -53,7 +53,8 @@ static QEglFSCompositor *compositor = 0; QEglFSCompositor::QEglFSCompositor() : m_screen(0), - m_program(0) + m_program(0), + m_initialized(false) { Q_ASSERT(!compositor); m_updateTimer.setSingleShot(true); @@ -86,6 +87,10 @@ void QEglFSCompositor::renderAll() Q_ASSERT(context); context->makeCurrent(rootWin->window()); + if (!m_initialized) { + initializeOpenGLFunctions(); + m_initialized = true; + } ensureProgram(); m_program->bind(); diff --git a/src/plugins/platforms/eglfs/qeglfscompositor.h b/src/plugins/platforms/eglfs/qeglfscompositor.h index ade2e06031..0d5daafa2c 100644 --- a/src/plugins/platforms/eglfs/qeglfscompositor.h +++ b/src/plugins/platforms/eglfs/qeglfscompositor.h @@ -42,7 +42,8 @@ #ifndef QEGLFSCOMPOSITOR_H #define QEGLFSCOMPOSITOR_H -#include <QTimer> +#include <QtCore/QTimer> +#include <QtGui/QOpenGLFunctions> QT_BEGIN_NAMESPACE @@ -50,7 +51,7 @@ class QEglFSScreen; class QEglFSWindow; class QOpenGLShaderProgram; -class QEglFSCompositor : public QObject +class QEglFSCompositor : public QObject, public QOpenGLFunctions { Q_OBJECT @@ -76,6 +77,7 @@ private: int m_vertexCoordEntry; int m_textureCoordEntry; int m_isRasterEntry; + bool m_initialized; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/qeglfscursor.cpp b/src/plugins/platforms/eglfs/qeglfscursor.cpp index c00e86de35..0066426769 100644 --- a/src/plugins/platforms/eglfs/qeglfscursor.cpp +++ b/src/plugins/platforms/eglfs/qeglfscursor.cpp @@ -79,7 +79,7 @@ void QEglFSCursor::resetResources() m_cursorAtlas.texture = 0; } -static GLuint createShader(GLenum shaderType, const char *program) +GLuint QEglFSCursor::createShader(GLenum shaderType, const char *program) { GLuint shader = glCreateShader(shaderType); glShaderSource(shader, 1 /* count */, &program, NULL /* lengths */); @@ -98,7 +98,7 @@ static GLuint createShader(GLenum shaderType, const char *program) return 0; } -static GLuint createProgram(GLuint vshader, GLuint fshader) +GLuint QEglFSCursor::createProgram(GLuint vshader, GLuint fshader) { GLuint program = glCreateProgram(); glAttachShader(program, vshader); @@ -286,6 +286,7 @@ void QEglFSCursor::draw(const QRectF &r) { if (!m_program) { // one time initialization + initializeOpenGLFunctions(); createShaderPrograms(); if (!m_cursorAtlas.texture) { diff --git a/src/plugins/platforms/eglfs/qeglfscursor.h b/src/plugins/platforms/eglfs/qeglfscursor.h index 51a34e041d..71ff73b8f3 100644 --- a/src/plugins/platforms/eglfs/qeglfscursor.h +++ b/src/plugins/platforms/eglfs/qeglfscursor.h @@ -43,15 +43,15 @@ #define QEGLFSCURSOR_H #include <qpa/qplatformcursor.h> +#include <QtGui/QOpenGLFunctions> #include "qeglfsscreen.h" -#include <GLES2/gl2.h> QT_BEGIN_NAMESPACE class QOpenGLShaderProgram; class QEglFSScreen; -class QEglFSCursor : public QPlatformCursor +class QEglFSCursor : public QPlatformCursor, public QOpenGLFunctions { public: QEglFSCursor(QEglFSScreen *screen); @@ -78,6 +78,9 @@ protected: void draw(const QRectF &rect); void update(const QRegion ®ion); + GLuint createShader(GLenum shaderType, const char *program); + GLuint createProgram(GLuint vshader, GLuint fshader); + QEglFSScreen *m_screen; // current cursor information diff --git a/src/plugins/platforms/eglfs/qeglfsintegration.cpp b/src/plugins/platforms/eglfs/qeglfsintegration.cpp index 67f0c35ed7..55a822b887 100644 --- a/src/plugins/platforms/eglfs/qeglfsintegration.cpp +++ b/src/plugins/platforms/eglfs/qeglfsintegration.cpp @@ -74,35 +74,20 @@ #include <EGL/egl.h> +static void initResources() +{ + Q_INIT_RESOURCE(cursor); +} + QT_BEGIN_NAMESPACE QEglFSIntegration::QEglFSIntegration() : mFontDb(new QGenericUnixFontDatabase) , mServices(new QGenericUnixServices) + , mScreen(0) , mInputContext(0) { - QEglFSHooks::hooks()->platformInit(); - - EGLint major, minor; - - if (!eglBindAPI(EGL_OPENGL_ES_API)) { - qWarning("Could not bind GL_ES API\n"); - qFatal("EGL error"); - } - - mDisplay = eglGetDisplay(QEglFSHooks::hooks() ? QEglFSHooks::hooks()->platformDisplay() : EGL_DEFAULT_DISPLAY); - if (mDisplay == EGL_NO_DISPLAY) { - qWarning("Could not open egl display\n"); - qFatal("EGL error"); - } - - if (!eglInitialize(mDisplay, &major, &minor)) { - qWarning("Could not initialize egl display\n"); - qFatal("EGL error"); - } - - mScreen = new QEglFSScreen(mDisplay); - screenAdded(mScreen); + initResources(); } QEglFSIntegration::~QEglFSIntegration() @@ -166,10 +151,39 @@ QAbstractEventDispatcher *QEglFSIntegration::createEventDispatcher() const void QEglFSIntegration::initialize() { + QEglFSHooks::hooks()->platformInit(); + + EGLint major, minor; + + if (!eglBindAPI(EGL_OPENGL_ES_API)) { + qWarning("Could not bind GL_ES API\n"); + qFatal("EGL error"); + } + + mDisplay = eglGetDisplay(QEglFSHooks::hooks() ? QEglFSHooks::hooks()->platformDisplay() : EGL_DEFAULT_DISPLAY); + if (mDisplay == EGL_NO_DISPLAY) { + qWarning("Could not open egl display\n"); + qFatal("EGL error"); + } + + if (!eglInitialize(mDisplay, &major, &minor)) { + qWarning("Could not initialize egl display\n"); + qFatal("EGL error"); + } + + mScreen = createScreen(); + screenAdded(mScreen); + mInputContext = QPlatformInputContextFactory::create(); + createInputHandlers(); } +QEglFSScreen *QEglFSIntegration::createScreen() const +{ + return new QEglFSScreen(mDisplay); +} + QVariant QEglFSIntegration::styleHint(QPlatformIntegration::StyleHint hint) const { switch (hint) diff --git a/src/plugins/platforms/eglfs/qeglfsintegration.h b/src/plugins/platforms/eglfs/qeglfsintegration.h index a6fcfc8427..f685eec2d4 100644 --- a/src/plugins/platforms/eglfs/qeglfsintegration.h +++ b/src/plugins/platforms/eglfs/qeglfsintegration.h @@ -86,6 +86,9 @@ public: QPlatformInputContext *inputContext() const { return mInputContext; } +protected: + virtual QEglFSScreen *createScreen() const; + private: void createInputHandlers(); diff --git a/src/plugins/platforms/eglfs/qeglfsscreen.cpp b/src/plugins/platforms/eglfs/qeglfsscreen.cpp index 3f92d60aa2..758b461b3f 100644 --- a/src/plugins/platforms/eglfs/qeglfsscreen.cpp +++ b/src/plugins/platforms/eglfs/qeglfsscreen.cpp @@ -126,26 +126,34 @@ void QEglFSScreen::setPrimarySurface(EGLSurface surface) void QEglFSScreen::addWindow(QEglFSWindow *window) { - if (!m_windows.contains(window)) + if (!m_windows.contains(window)) { m_windows.append(window); + topWindowChanged(window); + } } void QEglFSScreen::removeWindow(QEglFSWindow *window) { m_windows.removeOne(window); + if (!m_windows.isEmpty()) + topWindowChanged(m_windows.last()); } void QEglFSScreen::moveToTop(QEglFSWindow *window) { m_windows.removeOne(window); m_windows.append(window); + topWindowChanged(window); } void QEglFSScreen::changeWindowIndex(QEglFSWindow *window, int newIdx) { int idx = m_windows.indexOf(window); - if (idx != -1 && idx != newIdx) + if (idx != -1 && idx != newIdx) { m_windows.move(idx, newIdx); + if (newIdx == m_windows.size() - 1) + topWindowChanged(m_windows.last()); + } } QEglFSWindow *QEglFSScreen::rootWindow() @@ -157,4 +165,9 @@ QEglFSWindow *QEglFSScreen::rootWindow() return 0; } +void QEglFSScreen::topWindowChanged(QPlatformWindow *window) +{ + Q_UNUSED(window); +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/qeglfsscreen.h b/src/plugins/platforms/eglfs/qeglfsscreen.h index 578a6cf20d..11d66b7e0f 100644 --- a/src/plugins/platforms/eglfs/qeglfsscreen.h +++ b/src/plugins/platforms/eglfs/qeglfsscreen.h @@ -85,6 +85,7 @@ public: protected: void setPrimarySurface(EGLSurface surface); + virtual void topWindowChanged(QPlatformWindow *window); private: friend class QEglFSWindow; diff --git a/src/plugins/platforms/eglfs/qeglfswindow.cpp b/src/plugins/platforms/eglfs/qeglfswindow.cpp index d6f233b6c5..cc0e64a83b 100644 --- a/src/plugins/platforms/eglfs/qeglfswindow.cpp +++ b/src/plugins/platforms/eglfs/qeglfswindow.cpp @@ -210,15 +210,17 @@ void QEglFSWindow::setVisible(bool visible) void QEglFSWindow::setGeometry(const QRect &r) { QRect rect; - if (m_flags.testFlag(HasNativeWindow)) + bool forceFullscreen = m_flags.testFlag(HasNativeWindow); + if (forceFullscreen) rect = screen()->availableGeometry(); else rect = r; QPlatformWindow::setGeometry(rect); + // if we corrected the size, trigger a resize event if (rect != r) - QWindowSystemInterface::handleGeometryChange(window(), rect); + QWindowSystemInterface::handleGeometryChange(window(), rect, r); } QRect QEglFSWindow::geometry() const diff --git a/src/plugins/platforms/ios/qiosapplicationdelegate.h b/src/plugins/platforms/ios/qiosapplicationdelegate.h index bfe31af198..617b740d6e 100644 --- a/src/plugins/platforms/ios/qiosapplicationdelegate.h +++ b/src/plugins/platforms/ios/qiosapplicationdelegate.h @@ -47,6 +47,5 @@ @interface QIOSApplicationDelegate : UIResponder <UIApplicationDelegate> @property (strong, nonatomic) UIWindow *window; -@property (strong, nonatomic) QIOSViewController *qiosViewController; @end diff --git a/src/plugins/platforms/ios/qiosapplicationdelegate.mm b/src/plugins/platforms/ios/qiosapplicationdelegate.mm index e06d2b8840..cf702c82af 100644 --- a/src/plugins/platforms/ios/qiosapplicationdelegate.mm +++ b/src/plugins/platforms/ios/qiosapplicationdelegate.mm @@ -49,7 +49,6 @@ @implementation QIOSApplicationDelegate @synthesize window; -@synthesize qiosViewController; - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { @@ -57,21 +56,34 @@ Q_UNUSED(launchOptions); self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease]; - self.qiosViewController = [[[QIOSViewController alloc] init] autorelease]; - self.window.rootViewController = self.qiosViewController; + self.window.rootViewController = [[[QIOSViewController alloc] init] autorelease]; -#ifdef QT_DEBUG - self.window.backgroundColor = [UIColor cyanColor]; +#if QT_IOS_DEPLOYMENT_TARGET_BELOW(__IPHONE_7_0) + QSysInfo::MacVersion iosVersion = QSysInfo::MacintoshVersion; + + // We prefer to keep the root viewcontroller in fullscreen layout, so that + // we don't have to compensate for the viewcontroller position. This also + // gives us the same behavior on iOS 5/6 as on iOS 7, where full screen layout + // is the only way. + if (iosVersion < QSysInfo::MV_IOS_7_0) + self.window.rootViewController.wantsFullScreenLayout = YES; + + // Use translucent statusbar by default on iOS6 iPhones (unless the user changed + // the default in the Info.plist), so that windows placed under the stausbar are + // still visible, just like on iOS7. + if (iosVersion >= QSysInfo::MV_IOS_6_0 && iosVersion < QSysInfo::MV_IOS_7_0 + && [UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone + && [UIApplication sharedApplication].statusBarStyle == UIStatusBarStyleDefault) + [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackTranslucent]; #endif - [self.window makeKeyAndVisible]; + self.window.hidden = NO; return YES; } - (void)dealloc { - [qiosViewController release]; [window release]; [super dealloc]; } diff --git a/src/plugins/platforms/ios/qiosbackingstore.mm b/src/plugins/platforms/ios/qiosbackingstore.mm index 2dadc5672b..5ea5fbd8d1 100644 --- a/src/plugins/platforms/ios/qiosbackingstore.mm +++ b/src/plugins/platforms/ios/qiosbackingstore.mm @@ -56,6 +56,9 @@ QIOSBackingStore::QIOSBackingStore(QWindow *window) fmt.setDepthBufferSize(16); fmt.setStencilBufferSize(8); + // Needed to prevent QOpenGLContext::makeCurrent() from failing + window->setSurfaceType(QSurface::OpenGLSurface); + m_context->setFormat(fmt); m_context->setScreen(window->screen()); m_context->create(); @@ -69,9 +72,6 @@ QIOSBackingStore::~QIOSBackingStore() void QIOSBackingStore::beginPaint(const QRegion &) { - // Needed to prevent QOpenGLContext::makeCurrent() from failing - window()->setSurfaceType(QSurface::OpenGLSurface); - m_context->makeCurrent(window()); QIOSWindow *iosWindow = static_cast<QIOSWindow *>(window()->handle()); @@ -102,6 +102,8 @@ void QIOSBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoin // the child window overlaps a sibling window that's draws using a separate QOpenGLContext. return; } + + m_context->makeCurrent(window); m_context->swapBuffers(window); } @@ -115,7 +117,7 @@ void QIOSBackingStore::resize(const QSize &size, const QRegion &staticContents) // backing store and always keep the paint device's size in sync with the // window size in beginPaint(). - if (size != window()->size()) + if (size != window()->size() && !window()->inherits("QWidgetWindow")) qWarning() << "QIOSBackingStore needs to have the same size as its window"; } diff --git a/src/plugins/platforms/ios/qioscontext.h b/src/plugins/platforms/ios/qioscontext.h index 082ec4794c..c48a0251a9 100644 --- a/src/plugins/platforms/ios/qioscontext.h +++ b/src/plugins/platforms/ios/qioscontext.h @@ -48,6 +48,8 @@ QT_BEGIN_NAMESPACE +class QIOSWindow; + class QIOSContext : public QObject, public QPlatformOpenGLContext { Q_OBJECT @@ -66,10 +68,14 @@ public: GLuint defaultFramebufferObject(QPlatformSurface *) const; QFunctionPointer getProcAddress(const QByteArray &procName); + bool isSharing() const Q_DECL_OVERRIDE; + bool isValid() const Q_DECL_OVERRIDE; + private Q_SLOTS: void windowDestroyed(QObject *object); private: + QIOSContext *m_sharedContext; EAGLContext *m_eaglContext; QSurfaceFormat m_format; @@ -83,7 +89,7 @@ private: static void deleteBuffers(const FramebufferObject &framebufferObject); - mutable QHash<QWindow *, FramebufferObject> m_framebufferObjects; + mutable QHash<QIOSWindow *, FramebufferObject> m_framebufferObjects; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qioscontext.mm b/src/plugins/platforms/ios/qioscontext.mm index 0c4bee1ef0..7310d2904f 100644 --- a/src/plugins/platforms/ios/qioscontext.mm +++ b/src/plugins/platforms/ios/qioscontext.mm @@ -51,7 +51,10 @@ QIOSContext::QIOSContext(QOpenGLContext *context) : QPlatformOpenGLContext() - , m_eaglContext([[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]) + , m_sharedContext(static_cast<QIOSContext *>(context->shareHandle())) + , m_eaglContext([[EAGLContext alloc] + initWithAPI:kEAGLRenderingAPIOpenGLES2 + sharegroup:m_sharedContext ? [m_sharedContext->m_eaglContext sharegroup] : nil]) , m_format(context->format()) { m_format.setRenderableType(QSurfaceFormat::OpenGLES); @@ -110,7 +113,7 @@ void QIOSContext::swapBuffers(QPlatformSurface *surface) { Q_ASSERT(surface && surface->surface()->surfaceType() == QSurface::OpenGLSurface); Q_ASSERT(surface->surface()->surfaceClass() == QSurface::Window); - QWindow *window = static_cast<QWindow *>(surface->surface()); + QIOSWindow *window = static_cast<QIOSWindow *>(surface); Q_ASSERT(m_framebufferObjects.contains(window)); [EAGLContext setCurrentContext:m_eaglContext]; @@ -121,7 +124,7 @@ void QIOSContext::swapBuffers(QPlatformSurface *surface) GLuint QIOSContext::defaultFramebufferObject(QPlatformSurface *surface) const { Q_ASSERT(surface && surface->surface()->surfaceClass() == QSurface::Window); - QWindow *window = static_cast<QWindow *>(surface->surface()); + QIOSWindow *window = static_cast<QIOSWindow *>(surface); FramebufferObject &framebufferObject = m_framebufferObjects[window]; @@ -152,8 +155,7 @@ GLuint QIOSContext::defaultFramebufferObject(QPlatformSurface *surface) const } // Ensure that the FBO's buffers match the size of the layer - QIOSWindow *platformWindow = static_cast<QIOSWindow *>(surface); - UIView *view = reinterpret_cast<UIView *>(platformWindow->winId()); + UIView *view = reinterpret_cast<UIView *>(window->winId()); CAEAGLLayer *layer = static_cast<CAEAGLLayer *>(view.layer); if (framebufferObject.renderbufferWidth != (layer.frame.size.width * layer.contentsScale) || framebufferObject.renderbufferHeight != (layer.frame.size.height * layer.contentsScale)) { @@ -188,7 +190,7 @@ GLuint QIOSContext::defaultFramebufferObject(QPlatformSurface *surface) const void QIOSContext::windowDestroyed(QObject *object) { - QWindow *window = static_cast<QWindow *>(object); + QIOSWindow *window = static_cast<QIOSWindow *>(object); if (m_framebufferObjects.contains(window)) { EAGLContext *originalContext = [EAGLContext currentContext]; [EAGLContext setCurrentContext:m_eaglContext]; @@ -203,5 +205,15 @@ QFunctionPointer QIOSContext::getProcAddress(const QByteArray& functionName) return QFunctionPointer(dlsym(RTLD_DEFAULT, functionName.constData())); } +bool QIOSContext::isValid() const +{ + return m_eaglContext; +} + +bool QIOSContext::isSharing() const +{ + return m_sharedContext; +} + #include "moc_qioscontext.cpp" diff --git a/src/plugins/platforms/ios/qioseventdispatcher.h b/src/plugins/platforms/ios/qioseventdispatcher.h index f2272ecd68..5caa7f5d2d 100644 --- a/src/plugins/platforms/ios/qioseventdispatcher.h +++ b/src/plugins/platforms/ios/qioseventdispatcher.h @@ -54,11 +54,9 @@ public: explicit QIOSEventDispatcher(QObject *parent = 0); bool processEvents(QEventLoop::ProcessEventsFlags flags) Q_DECL_OVERRIDE; - void interrupt() Q_DECL_OVERRIDE; void handleRunLoopExit(CFRunLoopActivity activity); - void checkIfEventLoopShouldExit(); void interruptEventLoopExec(); private: diff --git a/src/plugins/platforms/ios/qioseventdispatcher.mm b/src/plugins/platforms/ios/qioseventdispatcher.mm index 3dd9c7ad9f..51eb10d385 100644 --- a/src/plugins/platforms/ios/qioseventdispatcher.mm +++ b/src/plugins/platforms/ios/qioseventdispatcher.mm @@ -446,6 +446,8 @@ bool __attribute__((returns_twice)) QIOSEventDispatcher::processEvents(QEventLoo if (!m_processEventCallsAfterExec && (flags & QEventLoop::EventLoopExec)) { ++m_processEventCallsAfterExec; + m_runLoopExitObserver.addToMode(kCFRunLoopCommonModes); + // We set a new jump point here that we can return to when the event loop // is asked to exit, so that we can return from QEventLoop::exec(). switch (setjmp(processEventExitJumpPoint)) { @@ -475,44 +477,18 @@ bool __attribute__((returns_twice)) QIOSEventDispatcher::processEvents(QEventLoo if (m_processEventCallsAfterExec) --m_processEventCallsAfterExec; - // If we're running with nested event loops and the application is quit, - // then the forwarded interrupt call will happen while our processEvent - // counter is still 2, and we won't detect that we're about to fall down - // to the root iOS run-loop. We do an extra check here to catch that case. - checkIfEventLoopShouldExit(); - return processedEvents; } -void QIOSEventDispatcher::interrupt() -{ - QEventDispatcherCoreFoundation::interrupt(); - - if (!rootLevelRunLoopIntegration()) - return; - - // If an interrupt happens as part of a non-nested event loop, that is, - // by processing an event or timer in the root iOS run-loop, we'll be - // able to detect it here. - checkIfEventLoopShouldExit(); -} - -void QIOSEventDispatcher::checkIfEventLoopShouldExit() -{ - if (m_processEventCallsAfterExec == 1) { - qEventDispatcherDebug() << "Hit root runloop level, watching for runloop exit"; - m_runLoopExitObserver.addToMode(kCFRunLoopCommonModes); - } -} - void QIOSEventDispatcher::handleRunLoopExit(CFRunLoopActivity activity) { Q_UNUSED(activity); Q_ASSERT(activity == kCFRunLoopExit); - m_runLoopExitObserver.removeFromMode(kCFRunLoopCommonModes); - - interruptEventLoopExec(); + if (m_processEventCallsAfterExec == 1 && !QThreadData::current()->eventLoops.top()->isRunning()) { + qEventDispatcherDebug() << "Root runloop level exited"; + interruptEventLoopExec(); + } } void QIOSEventDispatcher::interruptEventLoopExec() @@ -521,6 +497,8 @@ void QIOSEventDispatcher::interruptEventLoopExec() --m_processEventCallsAfterExec; + m_runLoopExitObserver.removeFromMode(kCFRunLoopCommonModes); + // We re-set applicationProcessEventsReturnPoint here so that future // calls to QEventLoop::exec() will end up back here after entering // processEvents, instead of back in didFinishLaunchingWithOptions. diff --git a/src/plugins/platforms/ios/qiosglobal.h b/src/plugins/platforms/ios/qiosglobal.h index fd328c9171..1c76d29389 100644 --- a/src/plugins/platforms/ios/qiosglobal.h +++ b/src/plugins/platforms/ios/qiosglobal.h @@ -52,12 +52,12 @@ QT_BEGIN_NAMESPACE class QPlatformScreen; bool isQtApplication(); -QIOSViewController *qiosViewController(); -CGRect toCGRect(const QRect &rect); -QRect fromCGRect(const CGRect &rect); -CGPoint toCGPoint(const QPoint &point); -QPoint fromCGPoint(const CGPoint &point); +CGRect toCGRect(const QRectF &rect); +QRectF fromCGRect(const CGRect &rect); +CGPoint toCGPoint(const QPointF &point); +QPointF fromCGPoint(const CGPoint &point); + Qt::ScreenOrientation toQtScreenOrientation(UIDeviceOrientation uiDeviceOrientation); UIDeviceOrientation fromQtScreenOrientation(Qt::ScreenOrientation qtOrientation); QRect fromPortraitToPrimary(const QRect &rect, QPlatformScreen *screen); diff --git a/src/plugins/platforms/ios/qiosglobal.mm b/src/plugins/platforms/ios/qiosglobal.mm index 9b8462a6cc..d749b8f514 100644 --- a/src/plugins/platforms/ios/qiosglobal.mm +++ b/src/plugins/platforms/ios/qiosglobal.mm @@ -58,35 +58,24 @@ bool isQtApplication() return isQt; } -QIOSViewController *qiosViewController() -{ - // If Qt controls the application, we have created a root view controller were we place top-level - // QWindows. Note that in a mixed native application, our view controller might later be removed or - // added as a child of another controller. To protect against that, we keep an explicit pointer to the - // view controller in cases where this is the controller we need to access. - static QIOSViewController *c = isQtApplication() ? - static_cast<QIOSApplicationDelegate *>([UIApplication sharedApplication].delegate).qiosViewController : nil; - return c; -} - -CGRect toCGRect(const QRect &rect) +CGRect toCGRect(const QRectF &rect) { return CGRectMake(rect.x(), rect.y(), rect.width(), rect.height()); } -QRect fromCGRect(const CGRect &rect) +QRectF fromCGRect(const CGRect &rect) { - return QRect(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height); + return QRectF(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height); } -CGPoint toCGPoint(const QPoint &point) +CGPoint toCGPoint(const QPointF &point) { return CGPointMake(point.x(), point.y()); } -QPoint fromCGPoint(const CGPoint &point) +QPointF fromCGPoint(const CGPoint &point) { - return QPoint(point.x, point.y); + return QPointF(point.x, point.y); } Qt::ScreenOrientation toQtScreenOrientation(UIDeviceOrientation uiDeviceOrientation) diff --git a/src/plugins/platforms/ios/qiosinputcontext.h b/src/plugins/platforms/ios/qiosinputcontext.h index 78c1b260e6..533ba686e1 100644 --- a/src/plugins/platforms/ios/qiosinputcontext.h +++ b/src/plugins/platforms/ios/qiosinputcontext.h @@ -44,6 +44,7 @@ #include <UIKit/UIKit.h> +#include <QtGui/qtransform.h> #include <qpa/qplatforminputcontext.h> QT_BEGIN_NAMESPACE @@ -60,13 +61,17 @@ public: void showInputPanel(); void hideInputPanel(); bool isInputPanelVisible() const; + void setFocusObject(QObject *object); - void focusViewChanged(UIView *view); + void focusWindowChanged(QWindow *focusWindow); + void scrollRootView(); private: QIOSKeyboardListener *m_keyboardListener; - UIView *m_focusView; + UIView<UIKeyInput> *m_focusView; + QTransform m_inputItemTransform; bool m_hasPendingHideRequest; + bool m_inSetFocusObject; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qiosinputcontext.mm b/src/plugins/platforms/ios/qiosinputcontext.mm index d430589037..0e43429015 100644 --- a/src/plugins/platforms/ios/qiosinputcontext.mm +++ b/src/plugins/platforms/ios/qiosinputcontext.mm @@ -48,7 +48,12 @@ @public QIOSInputContext *m_context; BOOL m_keyboardVisible; + BOOL m_keyboardVisibleAndDocked; QRectF m_keyboardRect; + QRectF m_keyboardEndRect; + NSTimeInterval m_duration; + UIViewAnimationCurve m_curve; + UIViewController *m_viewController; } @end @@ -60,8 +65,30 @@ if (self) { m_context = context; m_keyboardVisible = NO; - // After the keyboard became undockable (iOS5), UIKeyboardWillShow/UIKeyboardWillHide - // no longer works for all cases. So listen to keyboard frame changes instead: + m_keyboardVisibleAndDocked = NO; + m_duration = 0; + m_curve = UIViewAnimationCurveEaseOut; + m_viewController = 0; + + if (isQtApplication()) { + // Get the root view controller that is on the same screen as the keyboard: + for (UIWindow *uiWindow in [[UIApplication sharedApplication] windows]) { + if (uiWindow.screen == [UIScreen mainScreen]) { + m_viewController = [uiWindow.rootViewController retain]; + break; + } + } + Q_ASSERT(m_viewController); + } + + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(keyboardWillShow:) + name:@"UIKeyboardWillShowNotification" object:nil]; + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(keyboardWillHide:) + name:@"UIKeyboardWillHideNotification" object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidChangeFrame:) @@ -72,25 +99,68 @@ - (void) dealloc { + [m_viewController release]; + [[NSNotificationCenter defaultCenter] + removeObserver:self + name:@"UIKeyboardWillShowNotification" object:nil]; + [[NSNotificationCenter defaultCenter] + removeObserver:self + name:@"UIKeyboardWillHideNotification" object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:@"UIKeyboardDidChangeFrameNotification" object:nil]; [super dealloc]; } -- (void) keyboardDidChangeFrame:(NSNotification *)notification +- (QRectF) getKeyboardRect:(NSNotification *)notification { - CGRect frame; - [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&frame]; + // For Qt applications we rotate the keyboard rect to align with the screen + // orientation (which is the interface orientation of the root view controller). + // For hybrid apps we follow native behavior, and return the rect unmodified: + CGRect keyboardFrame = [[notification userInfo][UIKeyboardFrameEndUserInfoKey] CGRectValue]; + if (isQtApplication()) { + UIView *view = m_viewController.view; + return fromCGRect(CGRectOffset([view convertRect:keyboardFrame fromView:view.window], 0, -view.bounds.origin.y)); + } else { + return fromCGRect(keyboardFrame); + } +} - m_keyboardRect = fromPortraitToPrimary(fromCGRect(frame), QGuiApplication::primaryScreen()->handle()); +- (void) keyboardDidChangeFrame:(NSNotification *)notification +{ + m_keyboardRect = [self getKeyboardRect:notification]; m_context->emitKeyboardRectChanged(); - BOOL visible = CGRectIntersectsRect(frame, [UIScreen mainScreen].bounds); + BOOL visible = m_keyboardRect.intersects(fromCGRect([UIScreen mainScreen].bounds)); if (m_keyboardVisible != visible) { m_keyboardVisible = visible; m_context->emitInputPanelVisibleChanged(); } + + // If the keyboard was visible and docked from before, this is just a geometry + // change (normally caused by an orientation change). In that case, update scroll: + if (m_keyboardVisibleAndDocked) + m_context->scrollRootView(); +} + +- (void) keyboardWillShow:(NSNotification *)notification +{ + // Note that UIKeyboardWillShowNotification is only sendt when the keyboard is docked. + m_keyboardVisibleAndDocked = YES; + m_keyboardEndRect = [self getKeyboardRect:notification]; + if (!m_duration) { + m_duration = [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]; + m_curve = [notification.userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue] << 16; + } + m_context->scrollRootView(); +} + +- (void) keyboardWillHide:(NSNotification *)notification +{ + // Note that UIKeyboardWillHideNotification is also sendt when the keyboard is undocked. + m_keyboardVisibleAndDocked = NO; + m_keyboardEndRect = [self getKeyboardRect:notification]; + m_context->scrollRootView(); } @end @@ -100,7 +170,11 @@ QIOSInputContext::QIOSInputContext() , m_keyboardListener([[QIOSKeyboardListener alloc] initWithQIOSInputContext:this]) , m_focusView(0) , m_hasPendingHideRequest(false) + , m_inSetFocusObject(false) { + if (isQtApplication()) + connect(qGuiApp->inputMethod(), &QInputMethod::cursorRectangleChanged, this, &QIOSInputContext::scrollRootView); + connect(qGuiApp, &QGuiApplication::focusWindowChanged, this, &QIOSInputContext::focusWindowChanged); } QIOSInputContext::~QIOSInputContext() @@ -142,10 +216,76 @@ bool QIOSInputContext::isInputPanelVisible() const return m_keyboardListener->m_keyboardVisible; } -void QIOSInputContext::focusViewChanged(UIView *view) +void QIOSInputContext::setFocusObject(QObject *) +{ + m_inputItemTransform = qApp->inputMethod()->inputItemTransform(); + + if (!m_focusView || !m_focusView.isFirstResponder) + return; + + // Since m_focusView is the first responder, it means that the keyboard is open and we + // should update keyboard layout. But there seem to be no way to tell it to reread the + // UITextInputTraits from m_focusView. To work around that, we quickly resign first + // responder status just to reassign it again. To not remove the focusObject in the same + // go, we need to call the super implementation of resignFirstResponder. Since the call + // will cause a 'keyboardWillHide' notification to be sendt, we also block scrollRootView + // to avoid artifacts: + m_inSetFocusObject = true; + SEL sel = @selector(resignFirstResponder); + [[m_focusView superclass] instanceMethodForSelector:sel](m_focusView, sel); + m_inSetFocusObject = false; + [m_focusView becomeFirstResponder]; +} + +void QIOSInputContext::focusWindowChanged(QWindow *focusWindow) { + UIView<UIKeyInput> *view = reinterpret_cast<UIView<UIKeyInput> *>(focusWindow->handle()->winId()); if ([m_focusView isFirstResponder]) [view becomeFirstResponder]; [m_focusView release]; m_focusView = [view retain]; } + +void QIOSInputContext::scrollRootView() +{ + // Scroll the root view (screen) if: + // - our backend controls the root view controller on the main screen (no hybrid app) + // - the focus object is on the same screen as the keyboard. + // - the first responder is a QUIView, and not some other foreign UIView. + // - the keyboard is docked. Otherwise the user can move the keyboard instead. + // - the inputItem has not been moved/scrolled + if (!isQtApplication() || !m_focusView || m_inSetFocusObject) + return; + + if (m_inputItemTransform != qApp->inputMethod()->inputItemTransform()) { + // The inputItem has moved since the last scroll update. To avoid competing + // with the application where the cursor/inputItem should be, we bail: + return; + } + + UIView *view = m_keyboardListener->m_viewController.view; + qreal scrollTo = 0; + + if (m_focusView.isFirstResponder + && m_keyboardListener->m_keyboardVisibleAndDocked + && m_focusView.window == view.window) { + QRectF cursorRect = qGuiApp->inputMethod()->cursorRectangle(); + cursorRect.translate(qGuiApp->focusWindow()->geometry().topLeft()); + qreal keyboardY = m_keyboardListener->m_keyboardEndRect.y(); + int statusBarY = qGuiApp->primaryScreen()->availableGeometry().y(); + const int margin = 20; + + if (cursorRect.bottomLeft().y() > keyboardY - margin) + scrollTo = qMin(view.bounds.size.height - keyboardY, cursorRect.y() - statusBarY - margin); + } + + if (scrollTo != view.bounds.origin.y) { + // Scroll the view the same way a UIScrollView works: by changing bounds.origin: + CGRect newBounds = view.bounds; + newBounds.origin.y = scrollTo; + [UIView animateWithDuration:m_keyboardListener->m_duration delay:0 + options:m_keyboardListener->m_curve + animations:^{ view.bounds = newBounds; } + completion:0]; + } +} diff --git a/src/plugins/platforms/ios/qiosintegration.mm b/src/plugins/platforms/ios/qiosintegration.mm index dcad6121be..5c8f67bda2 100644 --- a/src/plugins/platforms/ios/qiosintegration.mm +++ b/src/plugins/platforms/ios/qiosintegration.mm @@ -101,7 +101,12 @@ QIOSIntegration::~QIOSIntegration() bool QIOSIntegration::hasCapability(Capability cap) const { switch (cap) { + case BufferQueueingOpenGL: + return true; case OpenGL: + case ThreadedOpenGL: + return true; + case ThreadedPixmaps: return true; case MultipleWindows: return true; @@ -155,7 +160,7 @@ QPlatformServices *QIOSIntegration::services() const QVariant QIOSIntegration::styleHint(StyleHint hint) const { switch (hint) { - case ShowIsFullScreen: + case ShowIsMaximized: return true; case SetFocusOnTouchRelease: return true; diff --git a/src/plugins/platforms/ios/qiosscreen.h b/src/plugins/platforms/ios/qiosscreen.h index 40c7a3ccf7..173bd11719 100644 --- a/src/plugins/platforms/ios/qiosscreen.h +++ b/src/plugins/platforms/ios/qiosscreen.h @@ -50,8 +50,10 @@ QT_BEGIN_NAMESPACE -class QIOSScreen : public QPlatformScreen +class QIOSScreen : public QObject, public QPlatformScreen { + Q_OBJECT + public: QIOSScreen(unsigned int screenIndex); ~QIOSScreen(); @@ -72,13 +74,18 @@ public: UIScreen *uiScreen() const; - void setPrimaryOrientation(Qt::ScreenOrientation orientation); + void updateProperties(); + void layoutWindows(); + +public slots: + void updateStatusBarVisibility(); private: UIScreen *m_uiScreen; QRect m_geometry; QRect m_availableGeometry; int m_depth; + uint m_unscaledDpi; QSizeF m_physicalSize; QIOSOrientationListener *m_orientationListener; }; diff --git a/src/plugins/platforms/ios/qiosscreen.mm b/src/plugins/platforms/ios/qiosscreen.mm index c28d8881bf..57522cb1a3 100644 --- a/src/plugins/platforms/ios/qiosscreen.mm +++ b/src/plugins/platforms/ios/qiosscreen.mm @@ -123,8 +123,6 @@ QIOSScreen::QIOSScreen(unsigned int screenIndex) , m_uiScreen([[UIScreen screens] objectAtIndex:qMin(NSUInteger(screenIndex), [[UIScreen screens] count] - 1)]) , m_orientationListener(0) { - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - QString deviceIdentifier = deviceModelIdentifier(); if (deviceIdentifier == QStringLiteral("iPhone2,1") /* iPhone 3GS */ @@ -134,32 +132,118 @@ QIOSScreen::QIOSScreen(unsigned int screenIndex) m_depth = 24; } - int unscaledDpi = 163; // Regular iPhone DPI if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad && !deviceIdentifier.contains(QRegularExpression("^iPad2,[567]$")) /* excluding iPad Mini */) { - unscaledDpi = 132; - }; + m_unscaledDpi = 132; + } else { + m_unscaledDpi = 163; // Regular iPhone DPI + } - CGRect bounds = [m_uiScreen bounds]; - m_geometry = QRect(bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.height); + connect(qGuiApp, &QGuiApplication::focusWindowChanged, this, &QIOSScreen::updateStatusBarVisibility); - CGRect frame = m_uiScreen.applicationFrame; - m_availableGeometry = QRect(frame.origin.x, frame.origin.y, frame.size.width, frame.size.height); + updateProperties(); +} - const qreal millimetersPerInch = 25.4; - m_physicalSize = QSizeF(m_geometry.size()) / unscaledDpi * millimetersPerInch; +QIOSScreen::~QIOSScreen() +{ + [m_orientationListener release]; +} - if (isQtApplication()) { - // When in a non-mixed environment, let QScreen follow the current interface orientation: - setPrimaryOrientation(toQtScreenOrientation(UIDeviceOrientation(qiosViewController().interfaceOrientation))); +void QIOSScreen::updateProperties() +{ + UIWindow *uiWindow = 0; + for (uiWindow in [[UIApplication sharedApplication] windows]) { + if (uiWindow.screen == m_uiScreen) + break; } - [pool release]; + bool inPortrait = UIInterfaceOrientationIsPortrait(uiWindow.rootViewController.interfaceOrientation); + QRect geometry = inPortrait ? fromCGRect(m_uiScreen.bounds).toRect() + : QRect(m_uiScreen.bounds.origin.x, m_uiScreen.bounds.origin.y, + m_uiScreen.bounds.size.height, m_uiScreen.bounds.size.width); + + if (geometry != m_geometry) { + m_geometry = geometry; + + const qreal millimetersPerInch = 25.4; + m_physicalSize = QSizeF(m_geometry.size()) / m_unscaledDpi * millimetersPerInch; + + QWindowSystemInterface::handleScreenGeometryChange(screen(), m_geometry); + } + + QRect availableGeometry = geometry; + + CGSize applicationFrameSize = m_uiScreen.applicationFrame.size; + int statusBarHeight = geometry.height() - (inPortrait ? applicationFrameSize.height : applicationFrameSize.width); + + availableGeometry.adjust(0, statusBarHeight, 0, 0); + + if (availableGeometry != m_availableGeometry) { + m_availableGeometry = availableGeometry; + QWindowSystemInterface::handleScreenAvailableGeometryChange(screen(), m_availableGeometry); + } + + if (screen()) + layoutWindows(); } -QIOSScreen::~QIOSScreen() +void QIOSScreen::updateStatusBarVisibility() { - [m_orientationListener release]; + QWindow *focusWindow = QGuiApplication::focusWindow(); + + // If we don't have a focus window we leave the status + // bar as is, so that the user can activate a new window + // with the same window state without the status bar jumping + // back and forth. + if (!focusWindow) + return; + + UIView *view = reinterpret_cast<UIView *>(focusWindow->handle()->winId()); +#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_7_0) + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_IOS_7_0) { + [view.viewController setNeedsStatusBarAppearanceUpdate]; + } else +#endif + { + bool wasHidden = [UIApplication sharedApplication].statusBarHidden; + QIOSViewController *viewController = static_cast<QIOSViewController *>(view.viewController); + [[UIApplication sharedApplication] + setStatusBarHidden:[viewController prefersStatusBarHidden] + withAnimation:UIStatusBarAnimationNone]; + + if ([UIApplication sharedApplication].statusBarHidden != wasHidden) + updateProperties(); + } +} + +void QIOSScreen::layoutWindows() +{ + QList<QWindow*> windows = QGuiApplication::topLevelWindows(); + + const QRect oldGeometry = screen()->geometry(); + const QRect oldAvailableGeometry = screen()->availableGeometry(); + const QRect newGeometry = geometry(); + const QRect newAvailableGeometry = availableGeometry(); + + for (int i = 0; i < windows.size(); ++i) { + QWindow *window = windows.at(i); + + if (platformScreenForWindow(window) != this) + continue; + + QIOSWindow *platformWindow = static_cast<QIOSWindow *>(window->handle()); + if (!platformWindow) + continue; + + // FIXME: Handle more complex cases of no-state and/or child windows when rotating + + if (window->windowState() & Qt::WindowFullScreen + || (window->windowState() & Qt::WindowNoState && window->geometry() == oldGeometry)) + platformWindow->applyGeometry(newGeometry); + else if (window->windowState() & Qt::WindowMaximized + || (window->windowState() & Qt::WindowNoState && window->geometry() == oldAvailableGeometry)) + platformWindow->applyGeometry(newAvailableGeometry); + } } QRect QIOSScreen::geometry() const @@ -199,7 +283,9 @@ qreal QIOSScreen::devicePixelRatio() const Qt::ScreenOrientation QIOSScreen::nativeOrientation() const { - return Qt::PortraitOrientation; + // A UIScreen stays in the native orientation, regardless of rotation + return m_uiScreen.bounds.size.width >= m_uiScreen.bounds.size.height ? + Qt::LandscapeOrientation : Qt::PortraitOrientation; } Qt::ScreenOrientation QIOSScreen::orientation() const @@ -217,31 +303,11 @@ void QIOSScreen::setOrientationUpdateMask(Qt::ScreenOrientations mask) } } -void QIOSScreen::setPrimaryOrientation(Qt::ScreenOrientation orientation) -{ - // Note that UIScreen never changes orientation, but QScreen should. To work around - // this, we let QIOSViewController call us whenever interface orientation changes, and - // use that as primary orientation. After all, the viewcontrollers geometry is what we - // place QWindows on top of. A problem with this approach is that QIOSViewController is - // not in use in a mixed environment, which results in no change to primary orientation. - // We see that as acceptable since Qt should most likely not interfere with orientation - // for that case anyway. - bool portrait = screen()->isPortrait(orientation); - if (portrait && m_geometry.width() < m_geometry.height()) - return; - - // Switching portrait/landscape means swapping width/height (and adjusting x/y): - m_geometry = QRect(0, 0, m_geometry.height(), m_geometry.width()); - m_physicalSize = QSizeF(m_physicalSize.height(), m_physicalSize.width()); - m_availableGeometry = fromPortraitToPrimary(fromCGRect(m_uiScreen.applicationFrame), this); - - QWindowSystemInterface::handleScreenGeometryChange(screen(), m_geometry); - QWindowSystemInterface::handleScreenAvailableGeometryChange(screen(), m_availableGeometry); -} - UIScreen *QIOSScreen::uiScreen() const { return m_uiScreen; } +#include "moc_qiosscreen.cpp" + QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qiosviewcontroller.h b/src/plugins/platforms/ios/qiosviewcontroller.h index d5a61cb3f4..a0017808d3 100644 --- a/src/plugins/platforms/ios/qiosviewcontroller.h +++ b/src/plugins/platforms/ios/qiosviewcontroller.h @@ -42,5 +42,6 @@ #import <UIKit/UIKit.h> @interface QIOSViewController : UIViewController +- (BOOL)prefersStatusBarHidden; @end diff --git a/src/plugins/platforms/ios/qiosviewcontroller.mm b/src/plugins/platforms/ios/qiosviewcontroller.mm index d315b49776..2e7e44d32c 100644 --- a/src/plugins/platforms/ios/qiosviewcontroller.mm +++ b/src/plugins/platforms/ios/qiosviewcontroller.mm @@ -42,20 +42,14 @@ #import "qiosviewcontroller.h" #include <QtGui/QGuiApplication> +#include <QtGui/QWindow> #include <QtGui/QScreen> #include "qiosscreen.h" #include "qiosglobal.h" +#include "qioswindow.h" @implementation QIOSViewController -- (void)viewDidLoad -{ -#ifdef QT_DEBUG - if (!self.nibName) - self.view.backgroundColor = [UIColor magentaColor]; -#endif -} - -(BOOL)shouldAutorotate { // Until a proper orientation and rotation API is in place, we always auto rotate. @@ -63,26 +57,56 @@ return YES; } +#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_6_0) -(NSUInteger)supportedInterfaceOrientations { // We need to tell iOS that we support all orientations in order to set // status bar orientation when application content orientation changes. return UIInterfaceOrientationMaskAll; } +#endif + +#if QT_IOS_DEPLOYMENT_TARGET_BELOW(__IPHONE_6_0) +-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + Q_UNUSED(interfaceOrientation); + return YES; +} +#endif -- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration +- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration { Q_UNUSED(duration); + Q_UNUSED(interfaceOrientation); if (!QCoreApplication::instance()) return; // FIXME: Store orientation for later (?) - Qt::ScreenOrientation orientation = toQtScreenOrientation(UIDeviceOrientation(toInterfaceOrientation)); - if (orientation == -1) - return; - QIOSScreen *qiosScreen = static_cast<QIOSScreen *>(QGuiApplication::primaryScreen()->handle()); - qiosScreen->setPrimaryOrientation(orientation); + qiosScreen->updateProperties(); +} + +#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_7_0) +- (UIStatusBarStyle)preferredStatusBarStyle +{ + // Since we don't place anything behind the status bare by default, we + // end up with a black area, so we have to enable the white text mode + // of the iOS7 statusbar. + return UIStatusBarStyleLightContent; + + // FIXME: Try to detect the content underneath the statusbar and choose + // an appropriate style, and/or expose Qt APIs to control the style. +} +#endif + +- (BOOL)prefersStatusBarHidden +{ + QWindow *focusWindow = QGuiApplication::focusWindow(); + if (!focusWindow) + return [UIApplication sharedApplication].statusBarHidden; + + QIOSWindow *topLevel = static_cast<QIOSWindow *>(focusWindow->handle())->topLevelWindow(); + return topLevel->window()->windowState() == Qt::WindowFullScreen; } @end diff --git a/src/plugins/platforms/ios/qioswindow.h b/src/plugins/platforms/ios/qioswindow.h index 20f0aa59b6..a5e122bda1 100644 --- a/src/plugins/platforms/ios/qioswindow.h +++ b/src/plugins/platforms/ios/qioswindow.h @@ -52,12 +52,17 @@ class QIOSWindow; @interface UIView (QIOS) @property(readonly) QWindow *qwindow; +@property(readonly) UIViewController *viewController; @end QT_BEGIN_NAMESPACE -class QIOSWindow : public QPlatformWindow +@class QUIView; + +class QIOSWindow : public QObject, public QPlatformWindow { + Q_OBJECT + public: explicit QIOSWindow(QWindow *window); ~QIOSWindow(); @@ -74,20 +79,21 @@ public: void requestActivateWindow(); qreal devicePixelRatio() const; - int effectiveWidth() const; - int effectiveHeight() const; bool setMouseGrabEnabled(bool grab) { return grab; } bool setKeyboardGrabEnabled(bool grab) { return grab; } WId winId() const { return WId(m_view); }; + QIOSWindow *topLevelWindow() const; + private: - UIView *m_view; + void applyGeometry(const QRect &rect); - QRect m_requestedGeometry; + QUIView *m_view; + + QRect m_normalGeometry; int m_windowLevel; - qreal m_devicePixelRatio; void raiseOrLower(bool raise); void updateWindowLevel(); @@ -95,6 +101,8 @@ private: inline Qt::WindowType windowType() { return static_cast<Qt::WindowType>(int(window()->flags() & Qt::WindowType_Mask)); } inline bool windowIsPopup() { return windowType() & Qt::Popup & ~Qt::Window; } + + friend class QIOSScreen; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qioswindow.mm b/src/plugins/platforms/ios/qioswindow.mm index dbeec5f5f2..0dd810bdf6 100644 --- a/src/plugins/platforms/ios/qioswindow.mm +++ b/src/plugins/platforms/ios/qioswindow.mm @@ -48,6 +48,7 @@ #include "qiosviewcontroller.h" #include "qiosintegration.h" #include <QtGui/private/qguiapplication_p.h> +#include <QtGui/private/qwindow_p.h> #include <qpa/qplatformintegration.h> #import <QuartzCore/CAEAGLLayer.h> @@ -57,7 +58,7 @@ #include <QtDebug> -@interface EAGLView : UIView <UIKeyInput> +@interface QUIView : UIView <UIKeyInput> { @public UITextAutocapitalizationType autocapitalizationType; @@ -82,7 +83,7 @@ @end -@implementation EAGLView +@implementation QUIView + (Class)layerClass { @@ -107,15 +108,7 @@ [NSNumber numberWithBool:YES], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil]; - // Set up text input - autocapitalizationType = UITextAutocapitalizationTypeNone; - autocorrectionType = UITextAutocorrectionTypeNo; - enablesReturnKeyAutomatically = NO; - keyboardAppearance = UIKeyboardAppearanceDefault; - keyboardType = UIKeyboardTypeDefault; - returnKeyType = UIReturnKeyDone; - secureTextEntry = NO; - m_nextTouchId = 0; + [self updateTextInputTraits]; if (isQtApplication()) self.hidden = YES; @@ -126,6 +119,41 @@ return self; } +- (void)willMoveToWindow:(UIWindow *)newWindow +{ + // UIKIt will normally set the scale factor of a view to match the corresponding + // screen scale factor, but views backed by CAEAGLLayers need to do this manually. + self.contentScaleFactor = newWindow && newWindow.screen ? + newWindow.screen.scale : [[UIScreen mainScreen] scale]; + + // FIXME: Allow the scale factor to be customized through QSurfaceFormat. +} + +- (void)didAddSubview:(UIView *)subview +{ + if ([subview isKindOfClass:[QUIView class]]) + self.clipsToBounds = YES; +} + +- (void)willRemoveSubview:(UIView *)subview +{ + for (UIView *view in self.subviews) { + if (view != subview && [view isKindOfClass:[QUIView class]]) + return; + } + + self.clipsToBounds = NO; +} + +- (void)setNeedsDisplay +{ + [super setNeedsDisplay]; + + // We didn't implement drawRect: so we have to manually + // mark the layer as needing display. + [self.layer setNeedsDisplay]; +} + - (void)layoutSubviews { // This method is the de facto way to know that view has been resized, @@ -138,21 +166,67 @@ qWarning() << m_qioswindow->window() << "is backed by a UIView that has a transform set. This is not supported."; - QRect geometry = fromCGRect(self.frame); - m_qioswindow->QPlatformWindow::setGeometry(geometry); - QWindowSystemInterface::handleGeometryChange(m_qioswindow->window(), geometry); + // The original geometry requested by setGeometry() might be different + // from what we end up with after applying window constraints. + QRect requestedGeometry = m_qioswindow->geometry(); + + QRect actualGeometry; + if (m_qioswindow->window()->isTopLevel()) { + UIWindow *uiWindow = self.window; + UIView *rootView = uiWindow.rootViewController.view; + CGRect rootViewPositionInRelationToRootViewController = + [rootView convertRect:uiWindow.bounds fromView:uiWindow]; + + actualGeometry = fromCGRect(CGRectOffset([self.superview convertRect:self.frame toView:rootView], + -rootViewPositionInRelationToRootViewController.origin.x, + -rootViewPositionInRelationToRootViewController.origin.y + + rootView.bounds.origin.y)).toRect(); + } else { + actualGeometry = fromCGRect(self.frame).toRect(); + } + + // Persist the actual/new geometry so that QWindow::geometry() can + // be queried on the resize event. + m_qioswindow->QPlatformWindow::setGeometry(actualGeometry); + + QRect previousGeometry = requestedGeometry != actualGeometry ? + requestedGeometry : qt_window_private(m_qioswindow->window())->geometry; - // If we have a new size here we need to resize the FBO's corresponding buffers, - // but we defer that to when the application calls makeCurrent. + QWindowSystemInterface::handleGeometryChange(m_qioswindow->window(), actualGeometry, previousGeometry); + QWindowSystemInterface::flushWindowSystemEvents(); + + if (actualGeometry.size() != previousGeometry.size()) { + // Trigger expose event on resize + [self setNeedsDisplay]; - [super layoutSubviews]; + // A new size means we also need to resize the FBO's corresponding buffers, + // but we defer that to when the application calls makeCurrent. + } +} + +- (void)displayLayer:(CALayer *)layer +{ + QRect geometry = fromCGRect(layer.frame).toRect(); + Q_ASSERT(m_qioswindow->geometry() == geometry); + Q_ASSERT(self.hidden == !m_qioswindow->window()->isVisible()); + + QRegion region = self.hidden ? QRegion() : QRect(QPoint(), geometry.size()); + QWindowSystemInterface::handleExposeEvent(m_qioswindow->window(), region); + QWindowSystemInterface::flushWindowSystemEvents(); } - (void)updateTouchList:(NSSet *)touches withState:(Qt::TouchPointState)state { - // We deliver touch events with global coordinates. But global in this respect means - // the coordinate system where this QWindow lives. And that is our superview. - CGSize parentSize = self.superview.frame.size; + // We deliver touch events in global coordinates. But global in this respect + // means the same coordinate system that we use for describing the geometry + // of the top level QWindow we're inside. And that would be the coordinate + // system of the superview of the UIView that backs that window: + QPlatformWindow *topLevel = m_qioswindow; + while (QPlatformWindow *topLevelParent = topLevel->parent()) + topLevel = topLevelParent; + UIView *rootView = reinterpret_cast<UIView *>(topLevel->winId()).superview; + CGSize rootViewSize = rootView.frame.size; + foreach (UITouch *uiTouch, m_activeTouches.keys()) { QWindowSystemInterface::TouchPoint &touchPoint = m_activeTouches[uiTouch]; if (![touches containsObject:uiTouch]) { @@ -160,9 +234,9 @@ } else { touchPoint.state = state; touchPoint.pressure = (state == Qt::TouchPointReleased) ? 0.0 : 1.0; - QPoint touchPos = fromCGPoint([uiTouch locationInView:self.superview]); + QPoint touchPos = fromCGPoint([uiTouch locationInView:rootView]).toPoint(); touchPoint.area = QRectF(touchPos, QSize(0, 0)); - touchPoint.normalPosition = QPointF(touchPos.x() / parentSize.width, touchPos.y() / parentSize.height); + touchPoint.normalPosition = QPointF(touchPos.x() / rootViewSize.width, touchPos.y() / rootViewSize.height); } } } @@ -218,15 +292,26 @@ - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { - Q_UNUSED(touches) // ### can a subset of the active touches be cancelled? + if (!touches && m_activeTouches.isEmpty()) + return; + + if (!touches) { + m_activeTouches.clear(); + } else { + for (UITouch *touch in touches) + m_activeTouches.remove(touch); + + Q_ASSERT_X(m_activeTouches.isEmpty(), Q_FUNC_INFO, + "Subset of active touches cancelled by UIKit"); + } - // Clear current touch points - m_activeTouches.clear(); m_nextTouchId = 0; + NSTimeInterval timestamp = event ? event.timestamp : [[NSProcessInfo processInfo] systemUptime]; + // Send cancel touch event synchronously QIOSIntegration *iosIntegration = static_cast<QIOSIntegration *>(QGuiApplicationPrivate::platformIntegration()); - QWindowSystemInterface::handleTouchCancelEvent(m_qioswindow->window(), ulong(event.timestamp * 1000), iosIntegration->touchDevice()); + QWindowSystemInterface::handleTouchCancelEvent(m_qioswindow->window(), ulong(timestamp * 1000), iosIntegration->touchDevice()); QWindowSystemInterface::flushWindowSystemEvents(); } @@ -250,13 +335,17 @@ // user cannot type. And since the keyboard will open when a view becomes // the first responder, it's now a good time to inform QPA that the QWindow // this view backs became active: + [self updateTextInputTraits]; QWindowSystemInterface::handleWindowActivated(m_qioswindow->window()); return [super becomeFirstResponder]; } - (BOOL)resignFirstResponder { - QWindowSystemInterface::handleWindowActivated(0); + // Resigning first responed status means that the virtual keyboard was closed, or + // some other view became first responder. In either case we clear the focus object to + // avoid blinking cursors in line edits etc: + static_cast<QWindowPrivate *>(QObjectPrivate::get(m_qioswindow->window()))->clearFocusObject(); return [super resignFirstResponder]; } @@ -269,8 +358,11 @@ { QString string = QString::fromUtf8([text UTF8String]); int key = 0; - if ([text isEqualToString:@"\n"]) + if ([text isEqualToString:@"\n"]) { key = (int)Qt::Key_Return; + if (self.returnKeyType == UIReturnKeyDone) + [self resignFirstResponder]; + } // Send key event to window system interface QWindowSystemInterface::handleKeyEvent( @@ -288,14 +380,65 @@ 0, QEvent::KeyRelease, (int)Qt::Key_Backspace, Qt::NoModifier); } +- (void)updateTextInputTraits +{ + // Ask the current focus object what kind of input it + // expects, and configure the keyboard appropriately: + QObject *focusObject = QGuiApplication::focusObject(); + if (!focusObject) + return; + QInputMethodQueryEvent queryEvent(Qt::ImEnabled | Qt::ImHints); + if (!QCoreApplication::sendEvent(focusObject, &queryEvent)) + return; + if (!queryEvent.value(Qt::ImEnabled).toBool()) + return; + + Qt::InputMethodHints hints = static_cast<Qt::InputMethodHints>(queryEvent.value(Qt::ImHints).toUInt()); + + self.returnKeyType = (hints & Qt::ImhMultiLine) ? UIReturnKeyDefault : UIReturnKeyDone; + self.secureTextEntry = BOOL(hints & Qt::ImhHiddenText); + self.autocorrectionType = (hints & Qt::ImhNoPredictiveText) ? + UITextAutocorrectionTypeNo : UITextAutocorrectionTypeDefault; + + if (hints & Qt::ImhUppercaseOnly) + self.autocapitalizationType = UITextAutocapitalizationTypeAllCharacters; + else if (hints & Qt::ImhNoAutoUppercase) + self.autocapitalizationType = UITextAutocapitalizationTypeNone; + else + self.autocapitalizationType = UITextAutocapitalizationTypeSentences; + + if (hints & Qt::ImhUrlCharactersOnly) + self.keyboardType = UIKeyboardTypeURL; + else if (hints & Qt::ImhEmailCharactersOnly) + self.keyboardType = UIKeyboardTypeEmailAddress; + else if (hints & Qt::ImhDigitsOnly) + self.keyboardType = UIKeyboardTypeNumberPad; + else if (hints & Qt::ImhFormattedNumbersOnly) + self.keyboardType = UIKeyboardTypeDecimalPad; + else if (hints & Qt::ImhDialableCharactersOnly) + self.keyboardType = UIKeyboardTypeNumberPad; + else + self.keyboardType = UIKeyboardTypeDefault; +} + @end @implementation UIView (QIOS) - (QWindow *)qwindow { - if ([self isKindOfClass:[EAGLView class]]) - return static_cast<EAGLView *>(self)->m_qioswindow->window(); + if ([self isKindOfClass:[QUIView class]]) + return static_cast<QUIView *>(self)->m_qioswindow->window(); + return nil; +} + +- (UIViewController *)viewController +{ + id responder = self; + while ((responder = [responder nextResponder])) { + if ([responder isKindOfClass:UIViewController.class]) + return responder; + } return nil; } @@ -305,26 +448,23 @@ QT_BEGIN_NAMESPACE QIOSWindow::QIOSWindow(QWindow *window) : QPlatformWindow(window) - , m_view([[EAGLView alloc] initWithQIOSWindow:this]) - , m_requestedGeometry(QPlatformWindow::geometry()) + , m_view([[QUIView alloc] initWithQIOSWindow:this]) + , m_normalGeometry(QPlatformWindow::geometry()) , m_windowLevel(0) - , m_devicePixelRatio(1.0) { - setParent(parent()); + setParent(QPlatformWindow::parent()); setWindowState(window->windowState()); - - // Retina support: get screen scale factor and set it in the content view. - // This will make framebufferObject() create a 2x frame buffer on retina - // displays. Also set m_devicePixelRatio which is used for scaling the - // paint device. - if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)] == YES) { - m_devicePixelRatio = [[UIScreen mainScreen] scale]; - [m_view setContentScaleFactor: m_devicePixelRatio]; - } } QIOSWindow::~QIOSWindow() { + // According to the UIResponder documentation, Cocoa Touch should react to system interruptions + // that "might cause the view to be removed from the window" by sending touchesCancelled, but in + // practice this doesn't seem to happen when removing the view from its superview. To ensure that + // Qt's internal state for touch and mouse handling is kept consistent, we therefor have to force + // cancellation of all touch events. + [m_view touchesCancelled:0 withEvent:0]; + [m_view removeFromSuperview]; [m_view release]; } @@ -337,8 +477,8 @@ bool QIOSWindow::blockedByModal() void QIOSWindow::setVisible(bool visible) { - QPlatformWindow::setVisible(visible); m_view.hidden = !visible; + [m_view setNeedsDisplay]; if (!isQtApplication()) return; @@ -356,9 +496,13 @@ void QIOSWindow::setVisible(bool visible) if (visible) { requestActivateWindow(); + + if (window()->isTopLevel()) + static_cast<QIOSScreen *>(screen())->updateStatusBarVisibility(); + } else { // Activate top-most visible QWindow: - NSArray *subviews = qiosViewController().view.subviews; + NSArray *subviews = m_view.viewController.view.subviews; for (int i = int(subviews.count) - 1; i >= 0; --i) { UIView *view = [subviews objectAtIndex:i]; if (!view.hidden) { @@ -373,38 +517,95 @@ void QIOSWindow::setVisible(bool visible) void QIOSWindow::setGeometry(const QRect &rect) { - // If the window is in fullscreen, just bookkeep the requested - // geometry in case the window goes into Qt::WindowNoState later: - m_requestedGeometry = rect; - if (window()->windowState() & (Qt::WindowMaximized | Qt::WindowFullScreen)) + m_normalGeometry = rect; + + if (window()->windowState() != Qt::WindowNoState) { + QPlatformWindow::setGeometry(rect); + + // The layout will realize the requested geometry was not applied, and + // send geometry-change events that match the actual geometry. + [m_view setNeedsLayout]; + + if (window()->inherits("QWidgetWindow")) { + // QWidget wrongly assumes that setGeometry resets the window + // state back to Qt::NoWindowState, so we need to inform it that + // that his is not the case by re-issuing the current window state. + QWindowSystemInterface::handleWindowStateChanged(window(), window()->windowState()); + + // It also needs to be told immediately that the geometry it requested + // did not apply, otherwise it will continue on as if it did, instead + // of waiting for a resize event. + [m_view layoutIfNeeded]; + } + return; + } + + applyGeometry(rect); +} + +void QIOSWindow::applyGeometry(const QRect &rect) +{ + // Geometry changes are asynchronous, but QWindow::geometry() is + // expected to report back the 'requested geometry' until we get + // a callback with the updated geometry from the window system. + // The baseclass takes care of persisting this for us. + QPlatformWindow::setGeometry(rect); + + if (window()->isTopLevel()) { + // The QWindow is in QScreen coordinates, which maps to a possibly rotated root-view-controller. + // Since the root-view-controller might be translated in relation to the UIWindow, we need to + // check specifically for that and compensate. Also check if the root view has been scrolled + // as a result of the keyboard being open. + UIWindow *uiWindow = m_view.window; + UIView *rootView = uiWindow.rootViewController.view; + CGRect rootViewPositionInRelationToRootViewController = + [rootView convertRect:uiWindow.bounds fromView:uiWindow]; + + m_view.frame = CGRectOffset([m_view.superview convertRect:toCGRect(rect) fromView:rootView], + rootViewPositionInRelationToRootViewController.origin.x, + rootViewPositionInRelationToRootViewController.origin.y + + rootView.bounds.origin.y); + } else { + // Easy, in parent's coordinates + m_view.frame = toCGRect(rect); + } + + // iOS will automatically trigger -[layoutSubviews:] for resize, + // but not for move, so we force it just in case. + [m_view setNeedsLayout]; - // Since we don't support transformations on the UIView, we can set the frame - // directly and let UIKit deal with translating that into bounds and center. - // Changing the size of the view will end up in a call to -[EAGLView layoutSubviews] - // which will update QWindowSystemInterface with the new size. - m_view.frame = toCGRect(rect); + if (window()->inherits("QWidgetWindow")) + [m_view layoutIfNeeded]; } void QIOSWindow::setWindowState(Qt::WindowState state) { - // FIXME: Figure out where or how we should disable/enable the statusbar. - // Perhaps setting QWindow to maximized should also mean that we'll show - // the statusbar, and vice versa for fullscreen? + // Update the QWindow representation straight away, so that + // we can update the statusbar visibility based on the new + // state before applying geometry changes. + qt_window_private(window())->windowState = state; + + if (window()->isTopLevel() && window()->isVisible() && window()->isActive()) + static_cast<QIOSScreen *>(screen())->updateStatusBarVisibility(); switch (state) { + case Qt::WindowNoState: + applyGeometry(m_normalGeometry); + break; case Qt::WindowMaximized: - case Qt::WindowFullScreen: { - // Since UIScreen does not take orientation into account when - // reporting geometry, we need to look at the top view instead: - CGSize fullscreenSize = m_view.window.rootViewController.view.bounds.size; - m_view.frame = CGRectMake(0, 0, fullscreenSize.width, fullscreenSize.height); - m_view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - break; } - default: - m_view.frame = toCGRect(m_requestedGeometry); - m_view.autoresizingMask = UIViewAutoresizingNone; + applyGeometry(screen()->availableGeometry()); + break; + case Qt::WindowFullScreen: + applyGeometry(screen()->geometry()); break; + case Qt::WindowMinimized: + applyGeometry(QRect()); + break; + case Qt::WindowActive: + Q_UNREACHABLE(); + default: + Q_UNREACHABLE(); } } @@ -414,8 +615,30 @@ void QIOSWindow::setParent(const QPlatformWindow *parentWindow) UIView *parentView = reinterpret_cast<UIView *>(parentWindow->winId()); [parentView addSubview:m_view]; } else if (isQtApplication()) { - [qiosViewController().view addSubview:m_view]; + for (UIWindow *uiWindow in [[UIApplication sharedApplication] windows]) { + if (uiWindow.screen == static_cast<QIOSScreen *>(screen())->uiScreen()) { + [uiWindow.rootViewController.view addSubview:m_view]; + break; + } + } + } +} + +QIOSWindow *QIOSWindow::topLevelWindow() const +{ + QWindow *window = this->window(); + while (window) { + QWindow *parent = window->parent(); + if (!parent) + parent = window->transientParent(); + + if (!parent) + break; + + window = parent; } + + return static_cast<QIOSWindow *>(window->handle()); } void QIOSWindow::requestActivateWindow() @@ -423,12 +646,15 @@ void QIOSWindow::requestActivateWindow() // Note that several windows can be active at the same time if they exist in the same // hierarchy (transient children). But only one window can be QGuiApplication::focusWindow(). // Dispite the name, 'requestActivateWindow' means raise and transfer focus to the window: - if (!window()->isTopLevel() || blockedByModal()) + if (blockedByModal()) return; - raise(); - QPlatformInputContext *context = QGuiApplicationPrivate::platformIntegration()->inputContext(); - static_cast<QIOSInputContext *>(context)->focusViewChanged(m_view); + [m_view.window makeKeyWindow]; + + if (window()->isTopLevel()) + raise(); + + QWindowSystemInterface::handleWindowActivated(window()); } void QIOSWindow::raiseOrLower(bool raise) @@ -444,7 +670,7 @@ void QIOSWindow::raiseOrLower(bool raise) for (int i = int(subviews.count) - 1; i >= 0; --i) { UIView *view = static_cast<UIView *>([subviews objectAtIndex:i]); - if (view.hidden || view == m_view) + if (view.hidden || view == m_view || !view.qwindow) continue; int level = static_cast<QIOSWindow *>(view.qwindow->handle())->m_windowLevel; if (m_windowLevel > level || (raise && m_windowLevel == level)) { @@ -491,17 +717,9 @@ void QIOSWindow::handleContentOrientationChange(Qt::ScreenOrientation orientatio qreal QIOSWindow::devicePixelRatio() const { - return m_devicePixelRatio; -} - -int QIOSWindow::effectiveWidth() const -{ - return geometry().width() * m_devicePixelRatio; + return m_view.contentScaleFactor; } -int QIOSWindow::effectiveHeight() const -{ - return geometry().height() * m_devicePixelRatio; -} +#include "moc_qioswindow.cpp" QT_END_NAMESPACE diff --git a/src/plugins/platforms/kms/qkmsbackingstore.cpp b/src/plugins/platforms/kms/qkmsbackingstore.cpp index 29395f3b4f..fa4ef847cd 100644 --- a/src/plugins/platforms/kms/qkmsbackingstore.cpp +++ b/src/plugins/platforms/kms/qkmsbackingstore.cpp @@ -52,6 +52,7 @@ QKmsBackingStore::QKmsBackingStore(QWindow *window) , m_context(new QOpenGLContext) , m_texture(0) , m_program(0) + , m_initialized(false) { m_context->setFormat(window->requestedFormat()); m_context->setScreen(window->screen()); @@ -85,6 +86,11 @@ void QKmsBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoin m_context->makeCurrent(window); + if (!m_initialized) { + initializeOpenGLFunctions(); + m_initialized = true; + } + if (!m_program) { static const char *textureVertexProgram = "attribute highp vec2 vertexCoordEntry;\n" diff --git a/src/plugins/platforms/kms/qkmsbackingstore.h b/src/plugins/platforms/kms/qkmsbackingstore.h index 34ea49a346..c5f6c81d18 100644 --- a/src/plugins/platforms/kms/qkmsbackingstore.h +++ b/src/plugins/platforms/kms/qkmsbackingstore.h @@ -43,6 +43,7 @@ #define QBACKINGSTORE_KMS_H #include <qpa/qplatformbackingstore.h> +#include <QtGui/QOpenGLFunctions> #include <QImage> QT_BEGIN_NAMESPACE @@ -50,7 +51,7 @@ QT_BEGIN_NAMESPACE class QOpenGLContext; class QOpenGLShaderProgram; -class QKmsBackingStore : public QPlatformBackingStore +class QKmsBackingStore : public QPlatformBackingStore, public QOpenGLFunctions { public: QKmsBackingStore(QWindow *window); @@ -69,6 +70,7 @@ private: uint m_texture; QOpenGLShaderProgram *m_program; QRegion m_dirty; + bool m_initialized; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/kms/qkmsscreen.h b/src/plugins/platforms/kms/qkmsscreen.h index a90d6fa051..f0c49ad11c 100644 --- a/src/plugins/platforms/kms/qkmsscreen.h +++ b/src/plugins/platforms/kms/qkmsscreen.h @@ -55,8 +55,7 @@ extern "C" { #include <EGL/egl.h> #include <EGL/eglext.h> -#include <GLES2/gl2.h> -#include <GLES2/gl2ext.h> +#include <QtGui/qopengl.h> #include <qpa/qplatformscreen.h> diff --git a/src/plugins/platforms/qnx/qnx.pro b/src/plugins/platforms/qnx/qnx.pro index e2c25add0b..100db54a5c 100644 --- a/src/plugins/platforms/qnx/qnx.pro +++ b/src/plugins/platforms/qnx/qnx.pro @@ -5,18 +5,7 @@ QT += platformsupport-private core-private gui-private # Uncomment this to build with support for IMF once it becomes available in the BBNDK #CONFIG += qqnx_imf -# Uncomment this to build with support for PPS based platform integration -#CONFIG += qqnx_pps - -CONFIG(blackberry) { - CONFIG += qqnx_pps - - # Uncomment following line to enable screen event - # handling through a dedicated thread. - # CONFIG += qqnx_screeneventthread -} else { - CONFIG += qqnx_screeneventthread -} +!blackberry:CONFIG += qqnx_screeneventthread # Uncomment these to enable debugging output for various aspects of the plugin #DEFINES += QQNXBPSEVENTFILTER_DEBUG @@ -33,7 +22,6 @@ CONFIG(blackberry) { #DEFINES += QQNXNAVIGATOREVENTNOTIFIER_DEBUG #DEFINES += QQNXNAVIGATOR_DEBUG #DEFINES += QQNXRASTERBACKINGSTORE_DEBUG -#DEFINES += QQNXROOTWINDOW_DEBUG #DEFINES += QQNXSCREENEVENTTHREAD_DEBUG #DEFINES += QQNXSCREENEVENT_DEBUG #DEFINES += QQNXSCREEN_DEBUG @@ -50,7 +38,6 @@ SOURCES = main.cpp \ qqnxscreen.cpp \ qqnxwindow.cpp \ qqnxrasterbackingstore.cpp \ - qqnxrootwindow.cpp \ qqnxscreeneventhandler.cpp \ qqnxnativeinterface.cpp \ qqnxnavigatoreventhandler.cpp \ @@ -67,7 +54,6 @@ HEADERS = main.h \ qqnxscreen.h \ qqnxwindow.h \ qqnxrasterbackingstore.h \ - qqnxrootwindow.h \ qqnxscreeneventhandler.h \ qqnxnativeinterface.h \ qqnxnavigatoreventhandler.h \ @@ -144,7 +130,8 @@ CONFIG(qqnx_pps) { qqnxclipboard.h \ qqnxbuttoneventnotifier.h - LIBS += -lpps -lclipboard + LIBS += -lpps + !contains(DEFINES, QT_NO_CLIPBOARD): LIBS += -lclipboard CONFIG(qqnx_imf) { DEFINES += QQNX_IMF diff --git a/src/plugins/platforms/qnx/qqnxeglwindow.cpp b/src/plugins/platforms/qnx/qqnxeglwindow.cpp index e8fcdd692b..b57227a60b 100644 --- a/src/plugins/platforms/qnx/qqnxeglwindow.cpp +++ b/src/plugins/platforms/qnx/qqnxeglwindow.cpp @@ -42,6 +42,7 @@ #include "qqnxeglwindow.h" #include "qqnxscreen.h" +#include "qqnxglcontext.h" #include <QDebug> @@ -55,14 +56,15 @@ QT_BEGIN_NAMESPACE -QQnxEglWindow::QQnxEglWindow(QWindow *window, screen_context_t context) : - QQnxWindow(window, context), - m_requestedBufferSize(window->geometry().size()), +QQnxEglWindow::QQnxEglWindow(QWindow *window, screen_context_t context, bool needRootWindow) : + QQnxWindow(window, context, needRootWindow), m_platformOpenGLContext(0), m_newSurfaceRequested(true), m_eglSurface(EGL_NO_SURFACE) { initWindow(); + m_requestedBufferSize = screen()->rootWindow() == this ? + screen()->geometry().size() : window->geometry().size(); } QQnxEglWindow::~QQnxEglWindow() @@ -98,8 +100,6 @@ void QQnxEglWindow::createEGLSurface() QQnxGLContext::checkEGLError("eglCreateWindowSurface"); qFatal("QQNX: failed to create EGL surface, err=%d", eglGetError()); } - - screen()->onWindowPost(0); } void QQnxEglWindow::destroyEGLSurface() @@ -146,6 +146,9 @@ EGLSurface QQnxEglWindow::getSurface() void QQnxEglWindow::setGeometry(const QRect &rect) { + //If this is the root window, it has to be shown fullscreen + const QRect &newGeometry = screen()->rootWindow() == this ? screen()->geometry() : rect; + //We need to request that the GL context updates // the EGLsurface on which it is rendering. { @@ -153,11 +156,11 @@ void QQnxEglWindow::setGeometry(const QRect &rect) // setting m_requestedBufferSize and therefore extended the scope to include // that test. const QMutexLocker locker(&m_mutex); - m_requestedBufferSize = rect.size(); - if (m_platformOpenGLContext != 0 && bufferSize() != rect.size()) + m_requestedBufferSize = newGeometry.size(); + if (m_platformOpenGLContext != 0 && bufferSize() != newGeometry.size()) m_newSurfaceRequested.testAndSetRelease(false, true); } - QQnxWindow::setGeometry(rect); + QQnxWindow::setGeometry(newGeometry); } QSize QQnxEglWindow::requestedBufferSize() const @@ -166,6 +169,13 @@ QSize QQnxEglWindow::requestedBufferSize() const return m_requestedBufferSize; } +void QQnxEglWindow::adjustBufferSize() +{ + const QSize windowSize = window()->size(); + if (windowSize != bufferSize()) + setBufferSize(windowSize); +} + void QQnxEglWindow::setPlatformOpenGLContext(QQnxGLContext *platformOpenGLContext) { // This function does not take ownership of the platform gl context. @@ -175,6 +185,9 @@ void QQnxEglWindow::setPlatformOpenGLContext(QQnxGLContext *platformOpenGLContex int QQnxEglWindow::pixelFormat() const { + if (!m_platformOpenGLContext) //The platform GL context was not set yet + return -1; + const QSurfaceFormat format = m_platformOpenGLContext->format(); // Extract size of color channels from window format const int redSize = format.redBufferSize(); diff --git a/src/plugins/platforms/qnx/qqnxeglwindow.h b/src/plugins/platforms/qnx/qqnxeglwindow.h index e7dae6a458..fc53afcd7a 100644 --- a/src/plugins/platforms/qnx/qqnxeglwindow.h +++ b/src/plugins/platforms/qnx/qqnxeglwindow.h @@ -43,8 +43,6 @@ #define QQNXEGLWINDOW_H #include "qqnxwindow.h" -#include "qqnxglcontext.h" - #include <QtCore/QMutex> QT_BEGIN_NAMESPACE @@ -54,7 +52,7 @@ class QQnxGLContext; class QQnxEglWindow : public QQnxWindow { public: - QQnxEglWindow(QWindow *window, screen_context_t context); + QQnxEglWindow(QWindow *window, screen_context_t context, bool needRootWindow); ~QQnxEglWindow(); void createEGLSurface(); @@ -70,7 +68,7 @@ public: // Called by QQnxGLContext::createSurface() QSize requestedBufferSize() const; - WindowType windowType() const Q_DECL_OVERRIDE { return EGL; } + void adjustBufferSize(); protected: int pixelFormat() const; diff --git a/src/plugins/platforms/qnx/qqnxglcontext.h b/src/plugins/platforms/qnx/qqnxglcontext.h index ff57861498..2b12657da9 100644 --- a/src/plugins/platforms/qnx/qqnxglcontext.h +++ b/src/plugins/platforms/qnx/qqnxglcontext.h @@ -75,6 +75,8 @@ public: static EGLDisplay getEglDisplay(); EGLConfig getEglConfig() const { return m_eglConfig;} + EGLContext getEglContext() const { return m_eglContext; } + private: //Can be static because different displays returne the same handle static EGLDisplay ms_eglDisplay; diff --git a/src/plugins/platforms/qnx/qqnxintegration.cpp b/src/plugins/platforms/qnx/qqnxintegration.cpp index 09054b9eb9..5695ef433a 100644 --- a/src/plugins/platforms/qnx/qqnxintegration.cpp +++ b/src/plugins/platforms/qnx/qqnxintegration.cpp @@ -122,6 +122,17 @@ static inline QQnxIntegration::Options parseOptions(const QStringList ¶mList if (!paramList.contains(QLatin1String("no-fullscreen"))) { options |= QQnxIntegration::FullScreenApplication; } + +// On Blackberry the first window is treated as a root window +#ifdef Q_OS_BLACKBERRY + if (!paramList.contains(QLatin1String("no-rootwindow"))) { + options |= QQnxIntegration::RootWindow; + } +#else + if (paramList.contains(QLatin1String("rootwindow"))) { + options |= QQnxIntegration::RootWindow; + } +#endif return options; } @@ -326,9 +337,10 @@ bool QQnxIntegration::hasCapability(QPlatformIntegration::Capability cap) const { qIntegrationDebug() << Q_FUNC_INFO; switch (cap) { + case MultipleWindows: case ThreadedPixmaps: return true; -#if defined(QT_OPENGL_ES) +#if !defined(QT_NO_OPENGL) case OpenGL: case ThreadedOpenGL: case BufferQueueingOpenGL: @@ -343,12 +355,13 @@ QPlatformWindow *QQnxIntegration::createPlatformWindow(QWindow *window) const { qIntegrationDebug() << Q_FUNC_INFO; QSurface::SurfaceType surfaceType = window->surfaceType(); + const bool needRootWindow = options() & RootWindow; switch (surfaceType) { case QSurface::RasterSurface: - return new QQnxRasterWindow(window, m_screenContext); + return new QQnxRasterWindow(window, m_screenContext, needRootWindow); #if !defined(QT_NO_OPENGL) case QSurface::OpenGLSurface: - return new QQnxEglWindow(window, m_screenContext); + return new QQnxEglWindow(window, m_screenContext, needRootWindow); #endif default: qFatal("QQnxWindow: unsupported window API"); diff --git a/src/plugins/platforms/qnx/qqnxintegration.h b/src/plugins/platforms/qnx/qqnxintegration.h index ab0d6a3156..8b5614fe4f 100644 --- a/src/plugins/platforms/qnx/qqnxintegration.h +++ b/src/plugins/platforms/qnx/qqnxintegration.h @@ -1,6 +1,6 @@ /*************************************************************************** ** -** Copyright (C) 2011 - 2012 Research In Motion +** Copyright (C) 2011 - 2013 BlackBerry Limited. All rights reserved. ** Contact: http://www.qt-project.org/legal ** ** This file is part of the plugins of the Qt Toolkit. @@ -84,7 +84,8 @@ class QQnxIntegration : public QPlatformIntegration public: enum Option { // Options to be passed on command line. NoOptions = 0x0, - FullScreenApplication = 0x1 + FullScreenApplication = 0x1, + RootWindow = 0x2 }; Q_DECLARE_FLAGS(Options, Option) explicit QQnxIntegration(const QStringList ¶mList); diff --git a/src/plugins/platforms/qnx/qqnxnativeinterface.cpp b/src/plugins/platforms/qnx/qqnxnativeinterface.cpp index 4dd3444832..6e7fc35a82 100644 --- a/src/plugins/platforms/qnx/qqnxnativeinterface.cpp +++ b/src/plugins/platforms/qnx/qqnxnativeinterface.cpp @@ -41,8 +41,11 @@ #include "qqnxnativeinterface.h" +#include "qqnxglcontext.h" #include "qqnxscreen.h" +#include "qqnxwindow.h" +#include <QtGui/QOpenGLContext> #include <QtGui/QScreen> #include <QtGui/QWindow> @@ -70,4 +73,20 @@ void *QQnxNativeInterface::nativeResourceForScreen(const QByteArray &resource, Q return 0; } +void *QQnxNativeInterface::nativeResourceForContext(const QByteArray &resource, QOpenGLContext *context) +{ + if (resource == "eglcontext" && context) + return static_cast<QQnxGLContext*>(context->handle())->getEglContext(); + + return 0; +} + +void QQnxNativeInterface::setWindowProperty(QPlatformWindow *window, const QString &name, const QVariant &value) +{ + if (name == QStringLiteral("mmRendererWindowName")) { + QQnxWindow *qnxWindow = static_cast<QQnxWindow*>(window); + qnxWindow->setMMRendererWindowName(value.toString()); + } +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/qnx/qqnxnativeinterface.h b/src/plugins/platforms/qnx/qqnxnativeinterface.h index 6692da2576..dfd386214e 100644 --- a/src/plugins/platforms/qnx/qqnxnativeinterface.h +++ b/src/plugins/platforms/qnx/qqnxnativeinterface.h @@ -51,6 +51,8 @@ class QQnxNativeInterface : public QPlatformNativeInterface public: void *nativeResourceForWindow(const QByteArray &resource, QWindow *window); void *nativeResourceForScreen(const QByteArray &resource, QScreen *screen); + void *nativeResourceForContext(const QByteArray &resource, QOpenGLContext *context); + void setWindowProperty(QPlatformWindow *window, const QString &name, const QVariant &value); }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/qnx/qqnxrasterwindow.cpp b/src/plugins/platforms/qnx/qqnxrasterwindow.cpp index 16ff9d7519..1f974b268d 100644 --- a/src/plugins/platforms/qnx/qqnxrasterwindow.cpp +++ b/src/plugins/platforms/qnx/qqnxrasterwindow.cpp @@ -54,8 +54,8 @@ QT_BEGIN_NAMESPACE -QQnxRasterWindow::QQnxRasterWindow(QWindow *window, screen_context_t context) : - QQnxWindow(window, context), +QQnxRasterWindow::QQnxRasterWindow(QWindow *window, screen_context_t context, bool needRootWindow) : + QQnxWindow(window, context, needRootWindow), m_currentBufferIndex(-1), m_previousBufferIndex(-1) { @@ -115,9 +115,6 @@ void QQnxRasterWindow::post(const QRegion &dirty) // Save modified region and clear scrolled region m_previousDirty = dirty; m_scrolled = QRegion(); - // Notify screen that window posted - if (screen() != 0) - screen()->onWindowPost(this); if (m_cover) m_cover->updateCover(); @@ -169,6 +166,15 @@ QQnxBuffer &QQnxRasterWindow::renderBuffer() return m_buffers[m_currentBufferIndex]; } +void QQnxRasterWindow::adjustBufferSize() +{ + // When having a raster window we don't need any buffers, since + // Qt will draw to the parent TLW backing store. + const QSize windowSize = m_parentWindow ? QSize(1,1) : window()->size(); + if (windowSize != bufferSize()) + setBufferSize(windowSize); +} + int QQnxRasterWindow::pixelFormat() const { return screen()->nativeFormat(); diff --git a/src/plugins/platforms/qnx/qqnxrasterwindow.h b/src/plugins/platforms/qnx/qqnxrasterwindow.h index 8bd42bc320..ad34b3ccf2 100644 --- a/src/plugins/platforms/qnx/qqnxrasterwindow.h +++ b/src/plugins/platforms/qnx/qqnxrasterwindow.h @@ -50,7 +50,7 @@ QT_BEGIN_NAMESPACE class QQnxRasterWindow : public QQnxWindow { public: - QQnxRasterWindow(QWindow *window, screen_context_t context); + QQnxRasterWindow(QWindow *window, screen_context_t context, bool needRootWindow); void post(const QRegion &dirty); @@ -60,7 +60,7 @@ public: bool hasBuffers() const { return !bufferSize().isEmpty(); } - WindowType windowType() const Q_DECL_OVERRIDE { return Raster; } + void adjustBufferSize(); protected: int pixelFormat() const; @@ -69,6 +69,9 @@ protected: // Copies content from the previous buffer (back buffer) to the current buffer (front buffer) void blitPreviousToCurrent(const QRegion ®ion, int dx, int dy, bool flush=false); + void blitHelper(QQnxBuffer &source, QQnxBuffer &target, const QPoint &sourceOffset, + const QPoint &targetOffset, const QRegion ®ion, bool flush = false); + private: QRegion m_previousDirty; QRegion m_scrolled; diff --git a/src/plugins/platforms/qnx/qqnxrootwindow.cpp b/src/plugins/platforms/qnx/qqnxrootwindow.cpp deleted file mode 100644 index b3f5c87176..0000000000 --- a/src/plugins/platforms/qnx/qqnxrootwindow.cpp +++ /dev/null @@ -1,268 +0,0 @@ -/*************************************************************************** -** -** Copyright (C) 2011 - 2012 Research In Motion -** Contact: http://www.qt-project.org/legal -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqnxrootwindow.h" - -#include "qqnxscreen.h" - -#include <QtCore/QUuid> -#include <QtCore/QDebug> - -#if defined(QQNXROOTWINDOW_DEBUG) -#define qRootWindowDebug qDebug -#else -#define qRootWindowDebug QT_NO_QDEBUG_MACRO -#endif - -#include <errno.h> -#include <unistd.h> - -static const int MAGIC_ZORDER_FOR_NO_NAV = 10; - -QQnxRootWindow::QQnxRootWindow(const QQnxScreen *screen) - : m_screen(screen), - m_window(0), - m_windowGroupName(), - m_translucent(false) -{ - qRootWindowDebug() << Q_FUNC_INFO; - // Create one top-level QNX window to act as a container for child windows - // since navigator only supports one application window - errno = 0; - int result = screen_create_window(&m_window, m_screen->nativeContext()); - int val[2]; - if (result != 0) - qFatal("QQnxRootWindow: failed to create window, errno=%d", errno); - - // Move window to proper display - errno = 0; - screen_display_t display = m_screen->nativeDisplay(); - result = screen_set_window_property_pv(m_window, SCREEN_PROPERTY_DISPLAY, (void **)&display); - if (result != 0) - qFatal("QQnxRootWindow: failed to set window display, errno=%d", errno); - - // Make sure window is above navigator but below keyboard if running as root - // since navigator won't automatically set our z-order in this case - if (getuid() == 0) { - errno = 0; - val[0] = MAGIC_ZORDER_FOR_NO_NAV; - result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_ZORDER, val); - if (result != 0) - qFatal("QQnxRootWindow: failed to set window z-order, errno=%d", errno); - } - - // Window won't be visible unless it has some buffers so make one dummy buffer that is 1x1 - errno = 0; - val[0] = SCREEN_USAGE_NATIVE; - result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_USAGE, val); - if (result != 0) - qFatal("QQnxRootWindow: failed to set window buffer usage, errno=%d", errno); - - errno = 0; - val[0] = m_screen->nativeFormat(); - result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_FORMAT, val); - if (result != 0) - qFatal("QQnxRootWindow: failed to set window pixel format, errno=%d", errno); - - errno = 0; - val[0] = 1; - val[1] = 1; - result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_BUFFER_SIZE, val); - if (result != 0) - qFatal("QQnxRootWindow: failed to set window buffer size, errno=%d", errno); - - errno = 0; - result = screen_create_window_buffers(m_window, 1); - if (result != 0) - qFatal("QQNX: failed to create window buffer, errno=%d", errno); - - // Window is always the size of the display - errno = 0; - QRect geometry = m_screen->geometry(); - val[0] = geometry.width(); - val[1] = geometry.height(); - result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SIZE, val); - if (result != 0) - qFatal("QQnxRootWindow: failed to set window size, errno=%d", errno); - - // Fill the window with solid black. Note that the LSB of the pixel value - // 0x00000000 just happens to be 0x00, so if and when this root window's - // alpha blending mode is changed from None to Source-Over, it will then - // be interpreted as transparent. - errno = 0; - val[0] = 0; - result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_COLOR, val); - if (result != 0) - qFatal("QQnxRootWindow: failed to set window colour, errno=%d", errno); - - // Make the window opaque - errno = 0; - val[0] = SCREEN_TRANSPARENCY_NONE; - result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_TRANSPARENCY, val); - if (result != 0) - qFatal("QQnxRootWindow: failed to set window transparency, errno=%d", errno); - - // Set the swap interval to 1 - errno = 0; - val[0] = 1; - result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SWAP_INTERVAL, val); - if (result != 0) - qFatal("QQnxRootWindow: failed to set window swap interval, errno=%d", errno); - - // Set viewport size equal to window size but move outside buffer so the fill colour is used exclusively - errno = 0; - val[0] = geometry.width(); - val[1] = geometry.height(); - result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SOURCE_SIZE, val); - if (result != 0) - qFatal("QQnxRootWindow: failed to set window source size, errno=%d", errno); - - errno = 0; - val[0] = 0; - val[1] = 0; - result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SOURCE_POSITION, val); - if (result != 0) - qFatal("QQnxRootWindow: failed to set window source position, errno=%d", errno); - - // Optionally disable the screen power save - bool ok = false; - const int disablePowerSave = qgetenv("QQNX_DISABLE_POWER_SAVE").toInt(&ok); - if (ok && disablePowerSave) { - const int mode = SCREEN_IDLE_MODE_KEEP_AWAKE; - result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_IDLE_MODE, &mode); - if (result != 0) - qWarning("QQnxRootWindow: failed to disable power saving mode"); - } - - createWindowGroup(); - - // Don't post yet. This will be lazily done from QQnxScreen upon first posting of - // a child window. Doing it now pre-emptively would create a flicker if one of - // the QWindow's about to be created sets its Qt::WA_TranslucentBackground flag - // and immediately triggers the buffer re-creation in makeTranslucent(). -} - -void QQnxRootWindow::makeTranslucent() -{ - if (m_translucent) - return; - - int result; - - errno = 0; - const int val = SCREEN_TRANSPARENCY_DISCARD; - result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_TRANSPARENCY, &val); - if (result != 0) { - qFatal("QQnxRootWindow: failed to set window transparency, errno=%d", errno); - } - - m_translucent = true; - post(); -} - -QQnxRootWindow::~QQnxRootWindow() -{ - // Cleanup top-level QNX window - screen_destroy_window(m_window); -} - -void QQnxRootWindow::post() const -{ - qRootWindowDebug() << Q_FUNC_INFO; - errno = 0; - screen_buffer_t buffer; - int result = screen_get_window_property_pv(m_window, SCREEN_PROPERTY_RENDER_BUFFERS, (void **)&buffer); - if (result != 0) - qFatal("QQnxRootWindow: failed to query window buffer, errno=%d", errno); - - errno = 0; - int dirtyRect[] = {0, 0, 1, 1}; - result = screen_post_window(m_window, buffer, 1, dirtyRect, 0); - if (result != 0) - qFatal("QQNX: failed to post window buffer, errno=%d", errno); -} - -void QQnxRootWindow::flush() const -{ - qRootWindowDebug() << Q_FUNC_INFO; - // Force immediate display update - errno = 0; - int result = screen_flush_context(m_screen->nativeContext(), 0); - if (result != 0) - qFatal("QQnxRootWindow: failed to flush context, errno=%d", errno); -} - -void QQnxRootWindow::setRotation(int rotation) -{ - qRootWindowDebug() << Q_FUNC_INFO << "angle =" << rotation; - errno = 0; - int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_ROTATION, &rotation); - if (result != 0) - qFatal("QQnxRootWindow: failed to set window rotation, errno=%d", errno); -} - -void QQnxRootWindow::resize(const QSize &size) -{ - errno = 0; - int val[] = {size.width(), size.height()}; - int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SIZE, val); - if (result != 0) - qFatal("QQnxRootWindow: failed to set window size, errno=%d", errno); - - errno = 0; - result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SOURCE_SIZE, val); - if (result != 0) - qFatal("QQnxRootWindow: failed to set window source size, errno=%d", errno); - - // NOTE: display will update when child windows relayout and repaint -} - -void QQnxRootWindow::createWindowGroup() -{ - // Generate a random window group name - m_windowGroupName = QUuid::createUuid().toString().toLatin1(); - - // Create window group so child windows can be parented by container window - errno = 0; - int result = screen_create_window_group(m_window, m_windowGroupName.constData()); - if (result != 0) - qFatal("QQnxRootWindow: failed to create app window group, errno=%d", errno); -} diff --git a/src/plugins/platforms/qnx/qqnxscreen.cpp b/src/plugins/platforms/qnx/qqnxscreen.cpp index dd8cf2131a..a09d6ce1f5 100644 --- a/src/plugins/platforms/qnx/qqnxscreen.cpp +++ b/src/plugins/platforms/qnx/qqnxscreen.cpp @@ -1,6 +1,6 @@ /*************************************************************************** ** -** Copyright (C) 2011 - 2012 Research In Motion +** Copyright (C) 2011 - 2013 BlackBerry Limited. All rights reserved. ** Contact: http://www.qt-project.org/legal ** ** This file is part of the plugins of the Qt Toolkit. @@ -119,11 +119,43 @@ static QSize determineScreenSize(screen_display_t display, bool primaryScreen) { #endif } +static QQnxWindow *findMultimediaWindow(const QList<QQnxWindow*> windows, + const QByteArray &mmWindowId) +{ + Q_FOREACH (QQnxWindow *sibling, windows) { + if (sibling->mmRendererWindowName() == mmWindowId) + return sibling; + + QQnxWindow *mmWindow = findMultimediaWindow(sibling->children(), mmWindowId); + + if (mmWindow) + return mmWindow; + } + + return 0; +} + +static QQnxWindow *findMultimediaWindow(const QList<QQnxWindow*> windows, + screen_window_t mmWindowId) +{ + Q_FOREACH (QQnxWindow *sibling, windows) { + if (sibling->mmRendererWindow() == mmWindowId) + return sibling; + + QQnxWindow *mmWindow = findMultimediaWindow(sibling->children(), mmWindowId); + + if (mmWindow) + return mmWindow; + } + + return 0; +} + QQnxScreen::QQnxScreen(screen_context_t screenContext, screen_display_t display, bool primaryScreen) : m_screenContext(screenContext), m_display(display), + m_rootWindow(0), m_primaryScreen(primaryScreen), - m_posted(false), m_keyboardHeight(0), m_nativeOrientation(Qt::PrimaryOrientation), m_coverWindow(0), @@ -260,7 +292,8 @@ void QQnxScreen::setRotation(int rotation) { qScreenDebug() << Q_FUNC_INFO << "orientation =" << rotation; // Check if rotation changed - if (m_currentRotation != rotation) { + // We only want to rotate if we are the primary screen + if (m_currentRotation != rotation && isPrimaryScreen()) { // Update rotation of root window if (rootWindow()) rootWindow()->setRotation(rotation); @@ -280,15 +313,13 @@ void QQnxScreen::setRotation(int rotation) if (isOrthogonal(m_currentRotation, rotation)) { qScreenDebug() << Q_FUNC_INFO << "resize, size =" << m_currentGeometry.size(); if (rootWindow()) - rootWindow()->resize(m_currentGeometry.size()); + rootWindow()->setGeometry(QRect(QPoint(0,0), m_currentGeometry.size())); - if (m_primaryScreen) - resizeWindows(previousScreenGeometry); + resizeWindows(previousScreenGeometry); } else { // TODO: Find one global place to flush display updates // Force immediate display update if no geometry changes required - if (rootWindow()) - rootWindow()->flush(); + screen_flush_context(nativeContext(), 0); } // Save new rotation @@ -458,6 +489,8 @@ void QQnxScreen::removeWindow(QQnxWindow *window) if (window != m_coverWindow) { const int numWindowsRemoved = m_childWindows.removeAll(window); + if (window == m_rootWindow) //We just removed the root window + m_rootWindow = 0; //TODO we need a new root window ;) if (numWindowsRemoved > 0) updateHierarchy(); } else { @@ -493,13 +526,15 @@ void QQnxScreen::updateHierarchy() QList<QQnxWindow*>::const_iterator it; int result; - int topZorder; + int topZorder = 0; errno = 0; - if (isPrimaryScreen()) { + if (rootWindow()) { result = screen_get_window_property_iv(rootWindow()->nativeHandle(), SCREEN_PROPERTY_ZORDER, &topZorder); - if (result != 0) - qFatal("QQnxScreen: failed to query root window z-order, errno=%d", errno); + if (result != 0) { //This can happen if we use winId in QWidgets + topZorder = 10; + qWarning("QQnxScreen: failed to query root window z-order, errno=%d", errno); + } } else { topZorder = 0; //We do not need z ordering on the secondary screen, because only one window //is supported there @@ -507,16 +542,17 @@ void QQnxScreen::updateHierarchy() topZorder++; // root window has the lowest z-order in the windowgroup + int underlayZorder = -1; // Underlays sit immediately above the root window in the z-ordering Q_FOREACH (screen_window_t underlay, m_underlays) { // Do nothing when this fails. This can happen if we have stale windows in m_underlays, // which in turn can happen because a window was removed but we didn't get a notification // yet. - screen_set_window_property_iv(underlay, SCREEN_PROPERTY_ZORDER, &topZorder); - topZorder++; + screen_set_window_property_iv(underlay, SCREEN_PROPERTY_ZORDER, &underlayZorder); + underlayZorder--; } - // Normal Qt windows come next above underlays in the z-ordering + // Normal Qt windows come next above the root window z-ordering for (it = m_childWindows.constBegin(); it != m_childWindows.constEnd(); ++it) (*it)->updateZorder(topZorder); @@ -532,20 +568,6 @@ void QQnxScreen::updateHierarchy() screen_flush_context( m_screenContext, 0 ); } -void QQnxScreen::onWindowPost(QQnxWindow *window) -{ - qScreenDebug() << Q_FUNC_INFO; - Q_UNUSED(window) - - // post app window (so navigator will show it) after first child window - // has posted; this only needs to happen once as the app window's content - // never changes - if (!m_posted && rootWindow()) { - rootWindow()->post(); - m_posted = true; - } -} - void QQnxScreen::adjustOrientation() { if (!m_primaryScreen) @@ -585,6 +607,19 @@ void QQnxScreen::addUnderlayWindow(screen_window_t window) updateHierarchy(); } +void QQnxScreen::addMultimediaWindow(const QByteArray &id, screen_window_t window) +{ + // find the QnxWindow this mmrenderer window is related to + QQnxWindow *mmWindow = findMultimediaWindow(m_childWindows, id); + + if (!mmWindow) + return; + + mmWindow->setMMRendererWindow(window); + + updateHierarchy(); +} + void QQnxScreen::removeOverlayOrUnderlayWindow(screen_window_t window) { const int numRemoved = m_overlays.removeAll(window) + m_underlays.removeAll(window); @@ -610,17 +645,35 @@ void QQnxScreen::newWindowCreated(void *window) zorder = 0; } + char windowNameBuffer[256] = { 0 }; + QByteArray windowName; + + if (screen_get_window_property_cv(windowHandle, SCREEN_PROPERTY_ID_STRING, + sizeof(windowNameBuffer) - 1, windowNameBuffer) != 0) { + qWarning("QQnx: Failed to get id for window, errno=%d", errno); + } + + windowName = QByteArray(windowNameBuffer); + if (display == nativeDisplay()) { // A window was created on this screen. If we don't know about this window yet, it means // it was not created by Qt, but by some foreign library like the multimedia renderer, which // creates an overlay window when playing a video. // - // Treat all foreign windows as overlays or underlays here. + // Treat all foreign windows as overlays, underlays or as windows + // created by the BlackBerry QtMultimedia plugin. + // + // In the case of the BlackBerry QtMultimedia plugin, we need to + // "attach" the foreign created mmrenderer window to the correct + // platform window (usually the one belonging to QVideoWidget) to + // ensure proper z-ordering. // - // Assume that if a foreign window already has a Z-Order both negative and + // Otherwise, assume that if a foreign window already has a Z-Order both negative and // less than the default Z-Order installed by mmrender on windows it creates, // the windows should be treated as an underlay. Otherwise, we treat it as an overlay. - if (!findWindow(windowHandle)) { + if (!windowName.isEmpty() && windowName.startsWith("BbVideoWindowControl")) { + addMultimediaWindow(windowName, windowHandle); + } else if (!findWindow(windowHandle)) { if (zorder <= MAX_UNDERLAY_ZORDER) addUnderlayWindow(windowHandle); else @@ -634,7 +687,13 @@ void QQnxScreen::windowClosed(void *window) { Q_ASSERT(thread() == QThread::currentThread()); const screen_window_t windowHandle = reinterpret_cast<screen_window_t>(window); - removeOverlayOrUnderlayWindow(windowHandle); + + QQnxWindow *mmWindow = findMultimediaWindow(m_childWindows, windowHandle); + + if (mmWindow) + mmWindow->clearMMRendererWindow(); + else + removeOverlayOrUnderlayWindow(windowHandle); } void QQnxScreen::windowGroupStateChanged(const QByteArray &id, Qt::WindowState state) @@ -644,7 +703,7 @@ void QQnxScreen::windowGroupStateChanged(const QByteArray &id, Qt::WindowState s if (!rootWindow() || id != rootWindow()->groupName()) return; - QWindow * const window = topMostChildWindow(); + QWindow * const window = rootWindow()->window(); if (!window) return; @@ -659,7 +718,7 @@ void QQnxScreen::activateWindowGroup(const QByteArray &id) if (!rootWindow() || id != rootWindow()->groupName()) return; - QWindow * const window = topMostChildWindow(); + QWindow * const window = rootWindow()->window(); if (!window) return; @@ -686,18 +745,28 @@ void QQnxScreen::deactivateWindowGroup(const QByteArray &id) Q_FOREACH (QQnxWindow *childWindow, m_childWindows) childWindow->setExposed(false); - QWindowSystemInterface::handleWindowActivated(0); + QWindowSystemInterface::handleWindowActivated(rootWindow()->window()); } -QSharedPointer<QQnxRootWindow> QQnxScreen::rootWindow() const +QQnxWindow *QQnxScreen::rootWindow() const { - // We only create the root window if we are the primary display. - if (m_primaryScreen && !m_rootWindow) - m_rootWindow = QSharedPointer<QQnxRootWindow>(new QQnxRootWindow(this)); - return m_rootWindow; } +void QQnxScreen::setRootWindow(QQnxWindow *window) +{ + // Optionally disable the screen power save + bool ok = false; + const int disablePowerSave = qgetenv("QQNX_DISABLE_POWER_SAVE").toInt(&ok); + if (ok && disablePowerSave) { + const int mode = SCREEN_IDLE_MODE_KEEP_AWAKE; + int result = screen_set_window_property_iv(window->nativeHandle(), SCREEN_PROPERTY_IDLE_MODE, &mode); + if (result != 0) + qWarning("QQnxRootWindow: failed to disable power saving mode"); + } + m_rootWindow = window; +} + QWindow * QQnxScreen::topMostChildWindow() const { if (!m_childWindows.isEmpty()) { diff --git a/src/plugins/platforms/qnx/qqnxscreen.h b/src/plugins/platforms/qnx/qqnxscreen.h index e11030ea0a..61c47e6c72 100644 --- a/src/plugins/platforms/qnx/qqnxscreen.h +++ b/src/plugins/platforms/qnx/qqnxscreen.h @@ -44,7 +44,7 @@ #include <qpa/qplatformscreen.h> -#include "qqnxrootwindow.h" +#include "qqnxwindow.h" #include <QtCore/QObject> #include <QtCore/QScopedPointer> @@ -91,10 +91,10 @@ public: void lowerWindow(QQnxWindow *window); void updateHierarchy(); - void onWindowPost(QQnxWindow *window); void adjustOrientation(); - QSharedPointer<QQnxRootWindow> rootWindow() const; + QQnxWindow *rootWindow() const; + void setRootWindow(QQnxWindow*); QPlatformCursor *cursor() const; @@ -119,15 +119,15 @@ private: void resizeWindows(const QRect &previousScreenGeometry); void addOverlayWindow(screen_window_t window); void addUnderlayWindow(screen_window_t window); + void addMultimediaWindow(const QByteArray &id, screen_window_t window); void removeOverlayOrUnderlayWindow(screen_window_t window); QWindow *topMostChildWindow() const; screen_context_t m_screenContext; screen_display_t m_display; - mutable QSharedPointer<QQnxRootWindow> m_rootWindow; + QQnxWindow *m_rootWindow; const bool m_primaryScreen; - bool m_posted; int m_initialRotation; int m_currentRotation; diff --git a/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp b/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp index a5d6db0985..37071d825c 100644 --- a/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp +++ b/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp @@ -40,6 +40,9 @@ ****************************************************************************/ #include "qqnxscreeneventhandler.h" +#if defined(QQNX_SCREENEVENTTHREAD) +#include "qqnxscreeneventthread.h" +#endif #include "qqnxintegration.h" #include "qqnxkeytranslator.h" #include "qqnxscreen.h" @@ -64,6 +67,9 @@ QQnxScreenEventHandler::QQnxScreenEventHandler(QQnxIntegration *integration) , m_lastButtonState(Qt::NoButton) , m_lastMouseWindow(0) , m_touchDevice(0) +#if defined(QQNX_SCREENEVENTTHREAD) + , m_eventThread(0) +#endif { // Create a touch device m_touchDevice = new QTouchDevice; @@ -136,6 +142,10 @@ bool QQnxScreenEventHandler::handleEvent(screen_event_t event, int qnxType) handleDisplayEvent(event); break; + case SCREEN_EVENT_PROPERTY: + handlePropertyEvent(event); + break; + default: // event ignored qScreenEventDebug() << Q_FUNC_INFO << "unknown event" << qnxType; @@ -189,6 +199,43 @@ void QQnxScreenEventHandler::injectKeyboardEvent(int flags, int sym, int modifie } } +#if defined(QQNX_SCREENEVENTTHREAD) +void QQnxScreenEventHandler::setScreenEventThread(QQnxScreenEventThread *eventThread) +{ + m_eventThread = eventThread; +} + +void QQnxScreenEventHandler::processEventsFromScreenThread() +{ + if (!m_eventThread) + return; + + QQnxScreenEventArray *events = m_eventThread->lock(); + + for (int i = 0; i < events->size(); ++i) { + screen_event_t event = events->at(i); + if (!event) + continue; + (*events)[i] = 0; + + m_eventThread->unlock(); + + long result = 0; + QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance(); + bool handled = dispatcher && dispatcher->filterNativeEvent(QByteArrayLiteral("screen_event_t"), event, &result); + if (!handled) + handleEvent(event); + screen_destroy_event(event); + + m_eventThread->lock(); + } + + events->clear(); + + m_eventThread->unlock(); +} +#endif + void QQnxScreenEventHandler::handleKeyboardEvent(screen_event_t event) { // get flags of key event @@ -412,11 +459,20 @@ void QQnxScreenEventHandler::handleTouchEvent(screen_event_t event, int qnxType) if (w) { // get size of screen which contains window QPlatformScreen *platformScreen = QPlatformScreen::platformScreenForWindow(w); - QSizeF screenSize = platformScreen->physicalSize(); + QSizeF screenSize = platformScreen->geometry().size(); // update cached position of current touch point - m_touchPoints[touchId].normalPosition = QPointF( static_cast<qreal>(pos[0]) / screenSize.width(), static_cast<qreal>(pos[1]) / screenSize.height() ); - m_touchPoints[touchId].area = QRectF( pos[0], pos[1], 0.0, 0.0 ); + m_touchPoints[touchId].normalPosition = + QPointF(static_cast<qreal>(pos[0]) / screenSize.width(), + static_cast<qreal>(pos[1]) / screenSize.height()); + + m_touchPoints[touchId].area = QRectF(w->geometry().left() + windowPos[0], + w->geometry().top() + windowPos[1], 0.0, 0.0); + QWindow *parent = w->parent(); + while (parent) { + m_touchPoints[touchId].area.translate(parent->geometry().topLeft()); + parent = parent->parent(); + } // determine event type and update state of current touch point QEvent::Type type = QEvent::None; @@ -451,8 +507,8 @@ void QQnxScreenEventHandler::handleTouchEvent(screen_event_t event, int qnxType) // inject event into Qt QWindowSystemInterface::handleTouchEvent(w, m_touchDevice, pointList); qScreenEventDebug() << Q_FUNC_INFO << "Qt touch, w =" << w - << ", p=(" << pos[0] << "," << pos[1] - << "), t=" << type; + << ", p=" << m_touchPoints[touchId].area.topLeft() + << ", t=" << type; } } } @@ -523,6 +579,48 @@ void QQnxScreenEventHandler::handleDisplayEvent(screen_event_t event) } } +void QQnxScreenEventHandler::handlePropertyEvent(screen_event_t event) +{ + errno = 0; + int objectType; + if (screen_get_event_property_iv(event, SCREEN_PROPERTY_OBJECT_TYPE, &objectType) != 0) + qFatal("QQNX: failed to query object type property, errno=%d", errno); + + if (objectType != SCREEN_OBJECT_TYPE_WINDOW) + return; + + errno = 0; + screen_window_t window = 0; + if (screen_get_event_property_pv(event, SCREEN_PROPERTY_WINDOW, (void**)&window) != 0) + qFatal("QQnx: failed to query window property, errno=%d", errno); + + errno = 0; + int property; + if (screen_get_event_property_iv(event, SCREEN_PROPERTY_NAME, &property) != 0) + qFatal("QQnx: failed to query window property, errno=%d", errno); + + switch (property) { + case SCREEN_PROPERTY_KEYBOARD_FOCUS: + handleKeyboardFocusPropertyEvent(window); + break; + default: + // event ignored + qScreenEventDebug() << Q_FUNC_INFO << "Ignore property event for property: " << property; + } +} + +void QQnxScreenEventHandler::handleKeyboardFocusPropertyEvent(screen_window_t window) +{ + errno = 0; + int focus = 0; + if (window && screen_get_window_property_iv(window, SCREEN_PROPERTY_KEYBOARD_FOCUS, &focus) != 0) + qFatal("QQnx: failed to query keyboard focus property, errno=%d", errno); + + QWindow *w = focus ? QQnxIntegration::window(window) : 0; + + QWindowSystemInterface::handleWindowActivated(w); +} + #include "moc_qqnxscreeneventhandler.cpp" QT_END_NAMESPACE diff --git a/src/plugins/platforms/qnx/qqnxscreeneventhandler.h b/src/plugins/platforms/qnx/qqnxscreeneventhandler.h index af0b086eb8..a7bcd449ee 100644 --- a/src/plugins/platforms/qnx/qqnxscreeneventhandler.h +++ b/src/plugins/platforms/qnx/qqnxscreeneventhandler.h @@ -50,6 +50,9 @@ QT_BEGIN_NAMESPACE class QQnxIntegration; class QQnxScreenEventFilter; +#if defined(QQNX_SCREENEVENTTHREAD) +class QQnxScreenEventThread; +#endif class QQnxScreenEventHandler : public QObject { @@ -65,10 +68,19 @@ public: static void injectKeyboardEvent(int flags, int sym, int mod, int scan, int cap); +#if defined(QQNX_SCREENEVENTTHREAD) + void setScreenEventThread(QQnxScreenEventThread *eventThread); +#endif + Q_SIGNALS: void newWindowCreated(void *window); void windowClosed(void *window); +#if defined(QQNX_SCREENEVENTTHREAD) +private Q_SLOTS: + void processEventsFromScreenThread(); +#endif + private: void handleKeyboardEvent(screen_event_t event); void handlePointerEvent(screen_event_t event); @@ -76,6 +88,8 @@ private: void handleCloseEvent(screen_event_t event); void handleCreateEvent(screen_event_t event); void handleDisplayEvent(screen_event_t event); + void handlePropertyEvent(screen_event_t event); + void handleKeyboardFocusPropertyEvent(screen_window_t window); private: enum { @@ -90,6 +104,9 @@ private: QTouchDevice *m_touchDevice; QWindowSystemInterface::TouchPoint m_touchPoints[MaximumTouchPoints]; QList<QQnxScreenEventFilter*> m_eventFilters; +#if defined(QQNX_SCREENEVENTTHREAD) + QQnxScreenEventThread *m_eventThread; +#endif }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/qnx/qqnxscreeneventthread.cpp b/src/plugins/platforms/qnx/qqnxscreeneventthread.cpp index f3f660bc03..25a597bab9 100644 --- a/src/plugins/platforms/qnx/qqnxscreeneventthread.cpp +++ b/src/plugins/platforms/qnx/qqnxscreeneventthread.cpp @@ -61,6 +61,9 @@ QQnxScreenEventThread::QQnxScreenEventThread(screen_context_t context, QQnxScree m_screenEventHandler(screenEventHandler), m_quit(false) { + screenEventHandler->setScreenEventThread(this); + connect(this, SIGNAL(eventPending()), screenEventHandler, SLOT(processEventsFromScreenThread()), Qt::QueuedConnection); + connect(this, SIGNAL(finished()), screenEventHandler, SLOT(processEventsFromScreenThread()), Qt::QueuedConnection); } QQnxScreenEventThread::~QQnxScreenEventThread() @@ -74,20 +77,31 @@ void QQnxScreenEventThread::injectKeyboardEvent(int flags, int sym, int mod, int QQnxScreenEventHandler::injectKeyboardEvent(flags, sym, mod, scan, cap); } -void QQnxScreenEventThread::run() +QQnxScreenEventArray *QQnxScreenEventThread::lock() { - screen_event_t event; + m_mutex.lock(); + return &m_events; +} - // create screen event - errno = 0; - int result = screen_create_event(&event); - if (result) - qFatal("QQNX: failed to create screen event, errno=%d", errno); +void QQnxScreenEventThread::unlock() +{ + m_mutex.unlock(); +} +void QQnxScreenEventThread::run() +{ qScreenEventThreadDebug() << Q_FUNC_INFO << "screen event thread started"; // loop indefinitely while (!m_quit) { + screen_event_t event; + + // create screen event + errno = 0; + int result = screen_create_event(&event); + if (result) + qFatal("QQNX: failed to create screen event, errno=%d", errno); + // block until screen event is available errno = 0; @@ -108,14 +122,22 @@ void QQnxScreenEventThread::run() qScreenEventThreadDebug() << Q_FUNC_INFO << "QNX user screen event"; m_quit = true; } else { - m_screenEventHandler->handleEvent(event, qnxType); + m_mutex.lock(); + m_events << event; + m_mutex.unlock(); + emit eventPending(); } } qScreenEventThreadDebug() << Q_FUNC_INFO << "screen event thread stopped"; // cleanup - screen_destroy_event(event); + m_mutex.lock(); + Q_FOREACH (screen_event_t event, m_events) { + screen_destroy_event(event); + } + m_events.clear(); + m_mutex.unlock(); } void QQnxScreenEventThread::shutdown() diff --git a/src/plugins/platforms/qnx/qqnxscreeneventthread.h b/src/plugins/platforms/qnx/qqnxscreeneventthread.h index 5e931819be..cbdb505b3b 100644 --- a/src/plugins/platforms/qnx/qqnxscreeneventthread.h +++ b/src/plugins/platforms/qnx/qqnxscreeneventthread.h @@ -43,6 +43,7 @@ #define QQNXSCREENEVENTTHREAD_H #include <QtCore/QThread> +#include <QtCore/QMutex> #include <screen/screen.h> @@ -50,21 +51,33 @@ QT_BEGIN_NAMESPACE class QQnxScreenEventHandler; +typedef QVarLengthArray<screen_event_t, 64> QQnxScreenEventArray; + class QQnxScreenEventThread : public QThread { + Q_OBJECT + public: QQnxScreenEventThread(screen_context_t context, QQnxScreenEventHandler *screenEventHandler); ~QQnxScreenEventThread(); static void injectKeyboardEvent(int flags, int sym, int mod, int scan, int cap); + QQnxScreenEventArray *lock(); + void unlock(); + protected: void run(); +Q_SIGNALS: + void eventPending(); + private: void shutdown(); screen_context_t m_screenContext; + QMutex m_mutex; + QQnxScreenEventArray m_events; QQnxScreenEventHandler *m_screenEventHandler; bool m_quit; }; diff --git a/src/plugins/platforms/qnx/qqnxwindow.cpp b/src/plugins/platforms/qnx/qqnxwindow.cpp index 3969a09098..b25c0b5b29 100644 --- a/src/plugins/platforms/qnx/qqnxwindow.cpp +++ b/src/plugins/platforms/qnx/qqnxwindow.cpp @@ -1,6 +1,6 @@ /*************************************************************************** ** -** Copyright (C) 2011 - 2012 Research In Motion +** Copyright (C) 2011 - 2013 BlackBerry Limited. All rights reserved. ** Contact: http://www.qt-project.org/legal ** ** This file is part of the plugins of the Qt Toolkit. @@ -40,15 +40,16 @@ ****************************************************************************/ #include "qqnxwindow.h" -#if !defined(QT_NO_OPENGL) -#include "qqnxglcontext.h" -#endif #include "qqnxintegration.h" #include "qqnxscreen.h" +#include <QUuid> + #include <QtGui/QWindow> #include <qpa/qwindowsysteminterface.h> +#include "private/qguiapplication_p.h" + #include <QtCore/QDebug> #if defined(Q_OS_BLACKBERRY) @@ -69,26 +70,36 @@ QT_BEGIN_NAMESPACE -QQnxWindow::QQnxWindow(QWindow *window, screen_context_t context) +QQnxWindow::QQnxWindow(QWindow *window, screen_context_t context, bool needRootWindow) : QPlatformWindow(window), m_screenContext(context), + m_parentWindow(0), m_window(0), m_screen(0), - m_parentWindow(0), m_visible(false), m_exposed(true), - m_windowState(Qt::WindowNoState) + m_windowState(Qt::WindowNoState), + m_mmRendererWindow(0) { qWindowDebug() << Q_FUNC_INFO << "window =" << window << ", size =" << window->size(); int result; - // Create child QNX window + QQnxScreen *platformScreen = static_cast<QQnxScreen *>(window->screen()->handle()); + + m_isTopLevel = ( needRootWindow && !platformScreen->rootWindow()) + || (!needRootWindow && !parent()) + || window->type() == Qt::CoverWindow; + errno = 0; - if (static_cast<QQnxScreen *>(window->screen()->handle())->isPrimaryScreen() - && window->type() != Qt::CoverWindow) { - result = screen_create_window_type(&m_window, m_screenContext, SCREEN_CHILD_WINDOW); + if (m_isTopLevel) { + result = screen_create_window(&m_window, m_screenContext); // Creates an application window + if (window->type() != Qt::CoverWindow) { + if (needRootWindow) + platformScreen->setRootWindow(this); + createWindowGroup(); + } } else { - result = screen_create_window(&m_window, m_screenContext); + result = screen_create_window_type(&m_window, m_screenContext, SCREEN_CHILD_WINDOW); } if (result != 0) qFatal("QQnxWindow: failed to create window, errno=%d", errno); @@ -115,20 +126,24 @@ QQnxWindow::~QQnxWindow() void QQnxWindow::setGeometry(const QRect &rect) { - const QRect oldGeometry = setGeometryHelper(rect); + QRect newGeometry = rect; + if (screen()->rootWindow() == this) //If this is the root window, it has to be shown fullscreen + newGeometry = screen()->geometry(); + + const QRect oldGeometry = setGeometryHelper(newGeometry); // Send a geometry change event to Qt (triggers resizeEvent() in QWindow/QWidget). // Calling flushWindowSystemEvents() here would flush input events which // could result in re-entering QQnxWindow::setGeometry() again. QWindowSystemInterface::setSynchronousWindowsSystemEvents(true); - QWindowSystemInterface::handleGeometryChange(window(), rect); - QWindowSystemInterface::handleExposeEvent(window(), rect); + QWindowSystemInterface::handleGeometryChange(window(), newGeometry); + QWindowSystemInterface::handleExposeEvent(window(), newGeometry); QWindowSystemInterface::setSynchronousWindowsSystemEvents(false); // Now move all children. if (!oldGeometry.isEmpty()) { - const QPoint offset = rect.topLeft() - oldGeometry.topLeft(); + const QPoint offset = newGeometry.topLeft() - oldGeometry.topLeft(); Q_FOREACH (QQnxWindow *childWindow, m_childWindows) childWindow->setOffset(offset); } @@ -262,16 +277,6 @@ bool QQnxWindow::isExposed() const return m_visible && m_exposed; } -void QQnxWindow::adjustBufferSize() -{ - if (m_parentWindow) - return; - - const QSize windowSize = window()->size(); - if (windowSize != bufferSize()) - setBufferSize(windowSize); -} - void QQnxWindow::setBufferSize(const QSize &size) { qWindowDebug() << Q_FUNC_INFO << "window =" << window() << "size =" << size; @@ -290,6 +295,8 @@ void QQnxWindow::setBufferSize(const QSize &size) // Create window buffers if they do not exist if (m_bufferSize.isEmpty()) { val[0] = pixelFormat(); + if (val[0] == -1) // The platform GL context was not set yet on the window, so we can't procede + return; errno = 0; result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_FORMAT, val); @@ -303,12 +310,6 @@ void QQnxWindow::setBufferSize(const QSize &size) qFatal("QQnxWindow: failed to create window buffers, errno=%d", errno); } - // If the child window has been configured for transparency, lazily create - // a full-screen buffer to back the root window. - if (window()->requestedFormat().hasAlpha()) { - m_screen->rootWindow()->makeTranslucent(); - } - // check if there are any buffers available int bufferCount = 0; result = screen_get_window_property_iv(m_window, SCREEN_PROPERTY_RENDER_BUFFER_COUNT, &bufferCount); @@ -333,6 +334,9 @@ void QQnxWindow::setScreen(QQnxScreen *platformScreen) if (platformScreen == 0) { // The screen has been destroyed m_screen = 0; + Q_FOREACH (QQnxWindow *childWindow, m_childWindows) { + childWindow->setScreen(0); + } return; } @@ -342,26 +346,36 @@ void QQnxWindow::setScreen(QQnxScreen *platformScreen) if (m_screen) { qWindowDebug() << Q_FUNC_INFO << "Moving window to different screen"; m_screen->removeWindow(this); - screen_leave_window_group(m_window); + QQnxIntegration *platformIntegration = static_cast<QQnxIntegration*>(QGuiApplicationPrivate::platformIntegration()); + + if ((platformIntegration->options() & QQnxIntegration::RootWindow)) { + screen_leave_window_group(m_window); + } } - platformScreen->addWindow(this); m_screen = platformScreen; - - // Move window to proper screen/display - errno = 0; - screen_display_t display = platformScreen->nativeDisplay(); - int result = screen_set_window_property_pv(m_window, SCREEN_PROPERTY_DISPLAY, (void **)&display); - if (result != 0) - qFatal("QQnxWindow: failed to set window display, errno=%d", errno); - - - if (m_screen->isPrimaryScreen() && window()->type() != Qt::CoverWindow) { - // Add window to display's window group + if (!m_parentWindow) { + platformScreen->addWindow(this); + } + if (m_isTopLevel) { + // Move window to proper screen/display errno = 0; - result = screen_join_window_group(m_window, platformScreen->windowGroupName()); + screen_display_t display = platformScreen->nativeDisplay(); + int result = screen_set_window_property_pv(m_window, SCREEN_PROPERTY_DISPLAY, (void **)&display); if (result != 0) - qFatal("QQnxWindow: failed to join window group, errno=%d", errno); + qFatal("QQnxWindow: failed to set window display, errno=%d", errno); + } else { + errno = 0; + int result; + if (!parent()) { + result = screen_join_window_group(m_window, platformScreen->windowGroupName()); + if (result != 0) + qFatal("QQnxWindow: failed to join window group, errno=%d", errno); + } else { + result = screen_join_window_group(m_window, static_cast<QQnxWindow*>(parent())->groupName().constData()); + if (result != 0) + qFatal("QQnxWindow: failed to join window group, errno=%d", errno); + } Q_FOREACH (QQnxWindow *childWindow, m_childWindows) { // Only subwindows and tooltips need necessarily be moved to another display with the window. @@ -405,20 +419,12 @@ void QQnxWindow::setParent(const QPlatformWindow *window) setScreen(m_parentWindow->m_screen); m_parentWindow->m_childWindows.push_back(this); - - // we don't need any buffers, since - // Qt will draw to the parent TLW - // backing store. - setBufferSize(QSize(1, 1)); } else { m_screen->addWindow(this); - - // recreate buffers, in case the - // window has been reparented and - // becomes a TLW - adjustBufferSize(); } + adjustBufferSize(); + m_screen->updateHierarchy(); } @@ -489,6 +495,22 @@ void QQnxWindow::gainedFocus() QWindowSystemInterface::handleWindowActivated(window()); } +void QQnxWindow::setMMRendererWindowName(const QString &name) +{ + m_mmRendererWindowName = name; +} + +void QQnxWindow::setMMRendererWindow(screen_window_t handle) +{ + m_mmRendererWindow = handle; +} + +void QQnxWindow::clearMMRendererWindow() +{ + m_mmRendererWindowName.clear(); + m_mmRendererWindow = 0; +} + QQnxWindow *QQnxWindow::findWindow(screen_window_t windowHandle) { if (m_window == windowHandle) @@ -524,6 +546,15 @@ void QQnxWindow::minimize() #endif } +void QQnxWindow::setRotation(int rotation) +{ + qWindowDebug() << Q_FUNC_INFO << "angle =" << rotation; + errno = 0; + int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_ROTATION, &rotation); + if (result != 0) + qFatal("QQnxRootWindow: failed to set window rotation, errno=%d", errno); +} + void QQnxWindow::initWindow() { // Alpha channel is always pre-multiplied if present @@ -577,23 +608,48 @@ void QQnxWindow::initWindow() setWindowState(window()->windowState()); if (window()->parent() && window()->parent()->handle()) setParent(window()->parent()->handle()); - setGeometryHelper(window()->geometry()); + + if (screen()->rootWindow() == this) { + setGeometryHelper(screen()->geometry()); + QWindowSystemInterface::handleGeometryChange(window(), screen()->geometry()); + } else { + setGeometryHelper(window()->geometry()); + } } +void QQnxWindow::createWindowGroup() +{ + // Generate a random window group name + m_windowGroupName = QUuid::createUuid().toString().toLatin1(); + + // Create window group so child windows can be parented by container window + errno = 0; + int result = screen_create_window_group(m_window, m_windowGroupName.constData()); + if (result != 0) + qFatal("QQnxRootWindow: failed to create app window group, errno=%d", errno); +} void QQnxWindow::updateZorder(int &topZorder) { - errno = 0; - int result = screen_set_window_property_iv(m_window, SCREEN_PROPERTY_ZORDER, &topZorder); - topZorder++; + updateZorder(m_window, topZorder); - if (result != 0) - qFatal("QQnxWindow: failed to set window z-order=%d, errno=%d, mWindow=%p", topZorder, errno, m_window); + if (m_mmRendererWindow) + updateZorder(m_mmRendererWindow, topZorder); Q_FOREACH (QQnxWindow *childWindow, m_childWindows) childWindow->updateZorder(topZorder); } +void QQnxWindow::updateZorder(screen_window_t window, int &topZorder) +{ + errno = 0; + int result = screen_set_window_property_iv(window, SCREEN_PROPERTY_ZORDER, &topZorder); + topZorder++; + + if (result != 0) + qFatal("QQnxWindow: failed to set window z-order=%d, errno=%d, mWindow=%p", topZorder, errno, window); +} + void QQnxWindow::applyWindowState() { switch (m_windowState) { diff --git a/src/plugins/platforms/qnx/qqnxwindow.h b/src/plugins/platforms/qnx/qqnxwindow.h index f96280848a..3c8070b0be 100644 --- a/src/plugins/platforms/qnx/qqnxwindow.h +++ b/src/plugins/platforms/qnx/qqnxwindow.h @@ -66,12 +66,7 @@ class QQnxWindow : public QPlatformWindow { friend class QQnxScreen; public: - enum WindowType { - EGL, - Raster - }; - - QQnxWindow(QWindow *window, screen_context_t context); + QQnxWindow(QWindow *window, screen_context_t context, bool needRootWindow); virtual ~QQnxWindow(); void setGeometry(const QRect &rect); @@ -83,7 +78,7 @@ public: WId winId() const { return (WId)m_window; } screen_window_t nativeHandle() const { return m_window; } - void adjustBufferSize(); + virtual void adjustBufferSize() = 0; void setBufferSize(const QSize &size); QSize bufferSize() const { return m_bufferSize; } @@ -99,6 +94,9 @@ public: void propagateSizeHints(); void gainedFocus(); + void setMMRendererWindowName(const QString &name); + void setMMRendererWindow(screen_window_t handle); + void clearMMRendererWindow(); QQnxScreen *screen() const { return m_screen; } const QList<QQnxWindow*>& children() const { return m_childWindows; } @@ -107,7 +105,14 @@ public: void minimize(); - virtual WindowType windowType() const = 0; + QString mmRendererWindowName() const { return m_mmRendererWindowName; } + + screen_window_t mmRendererWindow() const { return m_mmRendererWindow; } + + void setRotation(int rotation); + + QByteArray groupName() const { return m_windowGroupName; } + protected: virtual int pixelFormat() const = 0; virtual void resetBuffers() = 0; @@ -117,12 +122,16 @@ protected: screen_context_t m_screenContext; QScopedPointer<QQnxAbstractCover> m_cover; + QQnxWindow *m_parentWindow; + private: + void createWindowGroup(); QRect setGeometryHelper(const QRect &rect); void removeFromParent(); void setOffset(const QPoint &setOffset); void updateVisibility(bool parentVisible); void updateZorder(int &topZorder); + void updateZorder(screen_window_t window, int &zOrder); void applyWindowState(); screen_window_t m_window; @@ -130,11 +139,16 @@ private: QQnxScreen *m_screen; QList<QQnxWindow*> m_childWindows; - QQnxWindow *m_parentWindow; bool m_visible; bool m_exposed; QRect m_unmaximizedGeometry; Qt::WindowState m_windowState; + QString m_mmRendererWindowName; + screen_window_t m_mmRendererWindow; + + QByteArray m_windowGroupName; + + bool m_isTopLevel; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/accessible/iaccessible2.cpp b/src/plugins/platforms/windows/accessible/iaccessible2.cpp index 9170c774b4..66ed9d85dc 100644 --- a/src/plugins/platforms/windows/accessible/iaccessible2.cpp +++ b/src/plugins/platforms/windows/accessible/iaccessible2.cpp @@ -44,7 +44,7 @@ #include "iaccessible2.h" #include "qwindowsaccessibility.h" -#include <QtGui/private/qaccessible2_p.h> +#include <QtGui/qaccessible.h> #include <QtGui/qclipboard.h> #include <QtWidgets/qapplication.h> #include <QtCore/qdebug.h> diff --git a/src/plugins/platforms/windows/accessible/qwindowsaccessibility.cpp b/src/plugins/platforms/windows/accessible/qwindowsaccessibility.cpp index 63b4370dc2..885bc37cff 100644 --- a/src/plugins/platforms/windows/accessible/qwindowsaccessibility.cpp +++ b/src/plugins/platforms/windows/accessible/qwindowsaccessibility.cpp @@ -51,7 +51,6 @@ #include <QtCore/qpointer.h> #include <QtCore/qsettings.h> #include <QtGui/qaccessible.h> -#include <QtGui/private/qaccessible2_p.h> #include <QtGui/private/qguiapplication_p.h> #include <qpa/qplatformnativeinterface.h> #include <qpa/qplatformintegration.h> diff --git a/src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.cpp b/src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.cpp index 93592eb969..8bb7646258 100644 --- a/src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.cpp +++ b/src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.cpp @@ -54,7 +54,6 @@ #include <QtCore/qpair.h> #include <QtCore/qsettings.h> #include <QtGui/qaccessible.h> -#include <QtGui/private/qaccessible2_p.h> #include <QtGui/qguiapplication.h> #include <qpa/qplatformnativeinterface.h> #include <QtGui/qwindow.h> diff --git a/src/plugins/platforms/windows/qtwindowsglobal.h b/src/plugins/platforms/windows/qtwindowsglobal.h index f6ed9447ef..7b574b0a56 100644 --- a/src/plugins/platforms/windows/qtwindowsglobal.h +++ b/src/plugins/platforms/windows/qtwindowsglobal.h @@ -93,6 +93,7 @@ enum WindowsEventType // Simplify event types NonClientHitTest = NonClientEventFlag + 2, KeyEvent = KeyEventFlag + 1, KeyDownEvent = KeyEventFlag + KeyDownEventFlag + 1, + KeyboardLayoutChangeEvent = KeyEventFlag + 2, InputMethodKeyEvent = InputMethodEventFlag + KeyEventFlag + 1, InputMethodKeyDownEvent = InputMethodEventFlag + KeyEventFlag + KeyDownEventFlag + 1, ClipboardEvent = ClipboardEventFlag + 1, @@ -165,6 +166,10 @@ inline QtWindows::WindowsEventType windowsEventType(UINT message, WPARAM wParamI return QtWindows::InputMethodKeyEvent; case WM_IME_KEYDOWN: return QtWindows::InputMethodKeyDownEvent; +#ifdef WM_INPUTLANGCHANGE + case WM_INPUTLANGCHANGE: + return QtWindows::KeyboardLayoutChangeEvent; +#endif // WM_INPUTLANGCHANGE case WM_TOUCH: return QtWindows::TouchEvent; case WM_CHANGECBCHAIN: diff --git a/src/plugins/platforms/windows/qwindowsbackingstore.cpp b/src/plugins/platforms/windows/qwindowsbackingstore.cpp index 26205eb146..55e7b85d96 100644 --- a/src/plugins/platforms/windows/qwindowsbackingstore.cpp +++ b/src/plugins/platforms/windows/qwindowsbackingstore.cpp @@ -149,7 +149,23 @@ void QWindowsBackingStore::resize(const QSize &size, const QRegion ®ion) QImage::Format format = QWindowsNativeImage::systemFormat(); if (format == QImage::Format_RGB32 && rasterWindow()->window()->format().hasAlpha()) format = QImage::Format_ARGB32_Premultiplied; - m_image.reset(new QWindowsNativeImage(size.width(), size.height(), format)); + + QWindowsNativeImage *oldwni = m_image.data(); + QWindowsNativeImage *newwni = new QWindowsNativeImage(size.width(), size.height(), format); + + if (oldwni && !region.isEmpty()) { + const QImage &oldimg(oldwni->image()); + QImage &newimg(newwni->image()); + QRegion staticRegion(region); + staticRegion &= QRect(0, 0, oldimg.width(), oldimg.height()); + staticRegion &= QRect(0, 0, newimg.width(), newimg.height()); + QPainter painter(&newimg); + painter.setCompositionMode(QPainter::CompositionMode_Source); + foreach (const QRect &rect, staticRegion.rects()) + painter.drawImage(rect, oldimg, rect); + } + + m_image.reset(newwni); } } diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp index 85b03673ac..77cac647ba 100644 --- a/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -783,9 +783,15 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, case QtWindows::InputMethodCloseCandidateWindowEvent: // TODO: Release/regrab mouse if a popup has mouse grab. return false; - case QtWindows::ClipboardEvent: case QtWindows::DestroyEvent: - + if (!platformWindow->testFlag(QWindowsWindow::WithinDestroy)) { + qWarning() << "External WM_DESTROY received for " << platformWindow->window() + << ", parent: " << platformWindow->window()->parent() + << ", transient parent: " << platformWindow->window()->transientParent(); + } + return false; + case QtWindows::ClipboardEvent: + return false; case QtWindows::UnknownEvent: return false; case QtWindows::AccessibleObjectFromWindowRequest: @@ -842,8 +848,9 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, case QtWindows::KeyEvent: case QtWindows::InputMethodKeyEvent: case QtWindows::InputMethodKeyDownEvent: + case QtWindows::KeyboardLayoutChangeEvent: #if !defined(Q_OS_WINCE) && !defined(QT_NO_SESSIONMANAGER) - return platformSessionManager()->isInterractionBlocked() ? true : d->m_keyMapper.translateKeyEvent(platformWindow->window(), hwnd, msg, result); + return platformSessionManager()->isInteractionBlocked() ? true : d->m_keyMapper.translateKeyEvent(platformWindow->window(), hwnd, msg, result); #else return d->m_keyMapper.translateKeyEvent(platformWindow->window(), hwnd, msg, result); #endif @@ -867,7 +874,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, case QtWindows::NonClientMouseEvent: if (platformWindow->frameStrutEventsEnabled()) #if !defined(Q_OS_WINCE) && !defined(QT_NO_SESSIONMANAGER) - return platformSessionManager()->isInterractionBlocked() ? true : d->m_mouseHandler.translateMouseEvent(platformWindow->window(), hwnd, et, msg, result); + return platformSessionManager()->isInteractionBlocked() ? true : d->m_mouseHandler.translateMouseEvent(platformWindow->window(), hwnd, et, msg, result); #else return d->m_mouseHandler.translateMouseEvent(platformWindow->window(), hwnd, et, msg, result); #endif @@ -887,13 +894,13 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, case QtWindows::MouseEvent: case QtWindows::LeaveEvent: #if !defined(Q_OS_WINCE) && !defined(QT_NO_SESSIONMANAGER) - return platformSessionManager()->isInterractionBlocked() ? true : d->m_mouseHandler.translateMouseEvent(platformWindow->window(), hwnd, et, msg, result); + return platformSessionManager()->isInteractionBlocked() ? true : d->m_mouseHandler.translateMouseEvent(platformWindow->window(), hwnd, et, msg, result); #else return d->m_mouseHandler.translateMouseEvent(platformWindow->window(), hwnd, et, msg, result); #endif case QtWindows::TouchEvent: #if !defined(Q_OS_WINCE) && !defined(QT_NO_SESSIONMANAGER) - return platformSessionManager()->isInterractionBlocked() ? true : d->m_mouseHandler.translateTouchEvent(platformWindow->window(), hwnd, et, msg, result); + return platformSessionManager()->isInteractionBlocked() ? true : d->m_mouseHandler.translateTouchEvent(platformWindow->window(), hwnd, et, msg, result); #else return d->m_mouseHandler.translateTouchEvent(platformWindow->window(), hwnd, et, msg, result); #endif diff --git a/src/plugins/platforms/windows/qwindowsfontengine.cpp b/src/plugins/platforms/windows/qwindowsfontengine.cpp index ac57a1b396..6c928119b3 100644 --- a/src/plugins/platforms/windows/qwindowsfontengine.cpp +++ b/src/plugins/platforms/windows/qwindowsfontengine.cpp @@ -161,12 +161,18 @@ bool QWindowsFontEngine::hasCFFTable() const return GetFontData(hdc, MAKE_TAG('C', 'F', 'F', ' '), 0, 0, 0) != GDI_ERROR; } +bool QWindowsFontEngine::hasCMapTable() const +{ + HDC hdc = m_fontEngineData->hdc; + SelectObject(hdc, hfont); + return GetFontData(hdc, MAKE_TAG('c', 'm', 'a', 'p'), 0, 0, 0) != GDI_ERROR; +} + void QWindowsFontEngine::getCMap() { - ttf = (bool)(tm.tmPitchAndFamily & TMPF_TRUETYPE); + ttf = (bool)(tm.tmPitchAndFamily & TMPF_TRUETYPE) || hasCMapTable(); - // TMPF_TRUETYPE is not set for fonts with CFF tables - cffTable = !ttf && hasCFFTable(); + cffTable = hasCFFTable(); HDC hdc = m_fontEngineData->hdc; SelectObject(hdc, hfont); @@ -374,6 +380,7 @@ HGDIOBJ QWindowsFontEngine::selectDesignFont() const { LOGFONT f = m_logfont; f.lfHeight = unitsPerEm; + f.lfWidth = 0; HFONT designFont = CreateFontIndirect(&f); return SelectObject(m_fontEngineData->hdc, designFont); } diff --git a/src/plugins/platforms/windows/qwindowsfontengine.h b/src/plugins/platforms/windows/qwindowsfontengine.h index d783b6048c..acf84d270c 100644 --- a/src/plugins/platforms/windows/qwindowsfontengine.h +++ b/src/plugins/platforms/windows/qwindowsfontengine.h @@ -140,6 +140,7 @@ private: QWindowsNativeImage *drawGDIGlyph(HFONT font, glyph_t, int margin, const QTransform &xform, QImage::Format mask_format); bool hasCFFTable() const; + bool hasCMapTable() const; const QSharedPointer<QWindowsFontEngineData> m_fontEngineData; diff --git a/src/plugins/platforms/windows/qwindowskeymapper.cpp b/src/plugins/platforms/windows/qwindowskeymapper.cpp index 47c136991a..2743ef029d 100644 --- a/src/plugins/platforms/windows/qwindowskeymapper.cpp +++ b/src/plugins/platforms/windows/qwindowskeymapper.cpp @@ -86,6 +86,10 @@ QWindowsKeyMapper::~QWindowsKeyMapper() #define VK_OEM_3 0xC0 #endif +// We not only need the scancode itself but also the extended bit of key messages. Thus we need +// the additional bit when masking the scancode. +enum { scancodeBitmask = 0x1ff }; + // Key recorder ------------------------------------------------------------------------[ start ] -- struct KeyRecord { KeyRecord(int c, int a, int s, const QString &t) : code(c), ascii(a), state(s), text(t) {} @@ -97,6 +101,8 @@ struct KeyRecord { QString text; }; +// We need to record the pressed keys in order to decide, whether the key event is an autorepeat +// event. As soon as its state changes, the chain of autorepeat events will be broken. static const int QT_MAX_KEY_RECORDINGS = 64; // User has LOTS of fingers... struct KeyRecorder { @@ -503,12 +509,6 @@ static inline int toKeyOrUnicode(int vk, int scancode, unsigned char *kbdBuffer, return code == Qt::Key_unknown ? 0 : code; } -int qt_translateKeyCode(int vk) -{ - int code = winceKeyBend((vk < 0 || vk > 255) ? 0 : vk); - return code == Qt::Key_unknown ? 0 : code; -} - static inline int asciiToKeycode(char a, int state) { if (a >= 'a' && a <= 'z') @@ -554,12 +554,8 @@ void QWindowsKeyMapper::changeKeyboard() keyboardInputDirection = bidi ? Qt::RightToLeft : Qt::LeftToRight; } -void QWindowsKeyMapper::clearRecordedKeys() -{ - key_recorder.clearKeys(); -} - - +// Helper function that is used when obtaining the list of characters that can be produced by one key and +// every possible combination of modifiers inline void setKbdState(unsigned char *kbd, bool shift, bool ctrl, bool alt) { kbd[VK_LSHIFT ] = (shift ? 0x80 : 0); @@ -570,14 +566,18 @@ inline void setKbdState(unsigned char *kbd, bool shift, bool ctrl, bool alt) kbd[VK_MENU ] = (alt ? 0x80 : 0); } +// Adds the msg's key to keyLayout if it is not yet present there void QWindowsKeyMapper::updateKeyMap(const MSG &msg) { unsigned char kbdBuffer[256]; // Will hold the complete keyboard state GetKeyboardState(kbdBuffer); - quint32 scancode = (msg.lParam >> 16) & 0xfff; + const quint32 scancode = (msg.lParam >> 16) & scancodeBitmask; updatePossibleKeyCodes(kbdBuffer, scancode, msg.wParam); } +// Fills keyLayout for that vk_key. Values are all characters one can type using that key +// (in connection with every combination of modifiers) and whether these "characters" are +// dead keys. void QWindowsKeyMapper::updatePossibleKeyCodes(unsigned char *kbdBuffer, quint32 scancode, quint32 vk_key) { @@ -598,6 +598,10 @@ void QWindowsKeyMapper::updatePossibleKeyCodes(unsigned char *kbdBuffer, quint32 buffer[VK_RCONTROL] = 0; buffer[VK_LMENU ] = 0; // Use right Alt, since left Ctrl + right Alt is considered AltGraph + // keyLayout contains the actual characters which can be written using the vk_key together with the + // different modifiers. '2' together with shift will for example cause the character + // to be @ for a US key layout (thus keyLayout[vk_key].qtKey[1] will be @). In addition to that + // it stores whether the resulting key is a dead key as these keys have to be handled later. bool isDeadKey = false; keyLayout[vk_key].deadkeys = 0; keyLayout[vk_key].dirty = false; @@ -635,8 +639,9 @@ void QWindowsKeyMapper::updatePossibleKeyCodes(unsigned char *kbdBuffer, quint32 } keyLayout[vk_key].qtKey[8] = fallbackKey; - // If this vk_key a Dead Key - if (MapVirtualKey(vk_key, 2) & 0x80000000) { + // If one of the values inserted into the keyLayout above, can be considered a dead key, we have + // to run the workaround below. + if (keyLayout[vk_key].deadkeys) { // Push a Space, then the original key through the low-level ToAscii functions. // We do this because these functions (ToAscii / ToUnicode) will alter the internal state of // the keyboard driver By doing the following, we set the keyboard driver state back to what @@ -661,17 +666,6 @@ void QWindowsKeyMapper::updatePossibleKeyCodes(unsigned char *kbdBuffer, quint32 } } -bool QWindowsKeyMapper::isADeadKey(unsigned int vk_key, unsigned int modifiers) -{ - if ((vk_key < NumKeyboardLayoutItems) && keyLayout[vk_key].exists) { - for (size_t i = 0; i < NumMods; ++i) { - if (uint(ModsTbl[i]) == modifiers) - return bool(keyLayout[vk_key].deadkeys & 1<<i); - } - } - return false; -} - static inline QString messageKeyText(const MSG &msg) { const QChar ch = QChar((ushort)msg.wParam); @@ -742,12 +736,21 @@ bool QWindowsKeyMapper::translateKeyEvent(QWindow *widget, HWND hwnd, const MSG &msg, LRESULT *result) { *result = 0; + + // Reset layout map when system keyboard layout is changed + if (msg.message == WM_INPUTLANGCHANGE) { + deleteLayouts(); + return true; + } + + // Add this key to the keymap if it is not present yet. + updateKeyMap(msg); + MSG peekedMsg; // consume dead chars?(for example, typing '`','a' resulting in a-accent). if (PeekMessage(&peekedMsg, hwnd, 0, 0, PM_NOREMOVE) && peekedMsg.message == WM_DEADCHAR) return true; - if (msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN) - updateKeyMap(msg); + return translateKeyEventInternal(widget, msg, false); } @@ -755,9 +758,8 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, const MSG &ms { const int msgType = msg.message; - const quint32 scancode = (msg.lParam >> 16) & 0xfff; - const quint32 vk_key = MapVirtualKey(scancode, 1); - const bool isNumpad = (msg.wParam >= VK_NUMPAD0 && msg.wParam <= VK_NUMPAD9); + const quint32 scancode = (msg.lParam >> 16) & scancodeBitmask; + const quint32 vk_key = msg.wParam; quint32 nModifiers = 0; QWindow *receiver = m_keyGrabber ? m_keyGrabber : window; @@ -786,10 +788,6 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, const MSG &ms state |= (nModifiers & AltAny ? int(Qt::AltModifier) : 0); state |= (nModifiers & MetaAny ? int(Qt::MetaModifier) : 0); - // Now we know enough to either have MapVirtualKey or our own keymap tell us if it's a deadkey - const bool isDeadKey = isADeadKey(msg.wParam, state) - || MapVirtualKey(msg.wParam, 2) & 0x80000000; - // A multi-character key or a Input method character // not found by our look-ahead if (msgType == WM_CHAR || msgType == WM_IME_CHAR) { @@ -849,23 +847,12 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, const MSG &ms return true; // Translate VK_* (native) -> Key_* (Qt) keys - // If it's a dead key, we cannot use the toKeyOrUnicode() function, since that will change - // the internal state of the keyboard driver, resulting in that dead keys no longer works. - // ..also if we're typing numbers on the keypad, while holding down the Alt modifier. - int code = 0; - if (isNumpad && (nModifiers & AltAny)) { - code = winceKeyBend(msg.wParam); - } else if (!isDeadKey) { - // QTBUG-8764, QTBUG-10032 - // Can't call toKeyOrUnicode because that would call ToUnicode, and, if a dead key - // is pressed at the moment, Windows would NOT use it to compose a character for the next - // WM_CHAR event. - - // Instead, use MapVirtualKey, which will provide adequate values. - code = MapVirtualKey(msg.wParam, MAPVK_VK_TO_CHAR); - if (code < 0x20 || code == 0x7f) // The same logic as in toKeyOrUnicode() - code = winceKeyBend(msg.wParam); - } + int modifiersIndex = 0; + modifiersIndex |= (nModifiers & ShiftAny ? 0x1 : 0); + modifiersIndex |= (nModifiers & ControlAny ? 0x2 : 0); + modifiersIndex |= (nModifiers & AltAny ? 0x4 : 0); + + int code = keyLayout[vk_key].qtKey[modifiersIndex]; // Invert state logic: // If the key actually pressed is a modifier key, then we remove its modifier key from the diff --git a/src/plugins/platforms/windows/qwindowskeymapper.h b/src/plugins/platforms/windows/qwindowskeymapper.h index ce6ca23708..f7d33758a0 100644 --- a/src/plugins/platforms/windows/qwindowskeymapper.h +++ b/src/plugins/platforms/windows/qwindowskeymapper.h @@ -100,9 +100,7 @@ private: QLocale keyboardInputLocale; Qt::LayoutDirection keyboardInputDirection; - void clearRecordedKeys(); void updatePossibleKeyCodes(unsigned char *kbdBuffer, quint32 scancode, quint32 vk_key); - bool isADeadKey(unsigned int vk_key, unsigned int modifiers); void deleteLayouts(); QWindow *m_keyGrabber; diff --git a/src/plugins/platforms/windows/qwindowssessionmanager.cpp b/src/plugins/platforms/windows/qwindowssessionmanager.cpp index efdbb6b279..e86722f953 100644 --- a/src/plugins/platforms/windows/qwindowssessionmanager.cpp +++ b/src/plugins/platforms/windows/qwindowssessionmanager.cpp @@ -52,10 +52,6 @@ QWindowsSessionManager::QWindowsSessionManager(const QString &id, const QString { } -QWindowsSessionManager::~QWindowsSessionManager() -{ -} - bool QWindowsSessionManager::allowsInteraction() { m_blockUserInput = false; @@ -68,16 +64,6 @@ bool QWindowsSessionManager::allowsErrorInteraction() return true; } -void QWindowsSessionManager::blocksInteraction() -{ - m_blockUserInput = true; -} - -bool QWindowsSessionManager::isInterractionBlocked() const -{ - return m_blockUserInput; -} - void QWindowsSessionManager::release() { if (m_isActive) @@ -89,24 +75,4 @@ void QWindowsSessionManager::cancel() m_canceled = true; } -void QWindowsSessionManager::clearCancellation() -{ - m_canceled = false; -} - -bool QWindowsSessionManager::wasCanceled() const -{ - return m_canceled; -} - -void QWindowsSessionManager::setActive(bool active) -{ - m_isActive = active; -} - -bool QWindowsSessionManager::isActive() const -{ - return m_isActive; -} - QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowssessionmanager.h b/src/plugins/platforms/windows/qwindowssessionmanager.h index 3e21cbabec..0443635c35 100644 --- a/src/plugins/platforms/windows/qwindowssessionmanager.h +++ b/src/plugins/platforms/windows/qwindowssessionmanager.h @@ -60,22 +60,21 @@ class QWindowsSessionManager : public QPlatformSessionManager { public: explicit QWindowsSessionManager(const QString &id, const QString &key); - virtual ~QWindowsSessionManager(); bool allowsInteraction() Q_DECL_OVERRIDE; bool allowsErrorInteraction() Q_DECL_OVERRIDE; - void blocksInteraction(); - bool isInterractionBlocked() const; + void blocksInteraction() { m_blockUserInput = true; } + bool isInteractionBlocked() const { return m_blockUserInput; } void release() Q_DECL_OVERRIDE; void cancel() Q_DECL_OVERRIDE; - void clearCancellation(); - bool wasCanceled() const; + void clearCancellation() { m_canceled = false; } + bool wasCanceled() const { return m_canceled; } - void setActive(bool active); - bool isActive() const; + void setActive(bool active) { m_isActive = active; } + bool isActive() const { return m_isActive;} private: bool m_isActive; diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index 1909e0313b..58047124a1 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -305,7 +305,7 @@ bool QWindowsWindow::setWindowLayered(HWND hwnd, Qt::WindowFlags flags, bool has #endif // Q_OS_WINCE } -static void setWindowOpacity(HWND hwnd, Qt::WindowFlags flags, bool hasAlpha, qreal level) +static void setWindowOpacity(HWND hwnd, Qt::WindowFlags flags, bool hasAlpha, bool openGL, qreal level) { #ifdef Q_OS_WINCE // WINCE does not support that feature and microsoft explicitly warns to use those calls Q_UNUSED(hwnd); @@ -314,8 +314,8 @@ static void setWindowOpacity(HWND hwnd, Qt::WindowFlags flags, bool hasAlpha, qr Q_UNUSED(level); #else if (QWindowsWindow::setWindowLayered(hwnd, flags, hasAlpha, level)) { - if (hasAlpha && (flags & Qt::FramelessWindowHint)) { - // Windows with alpha: Use blend function to update. + if (hasAlpha && !openGL && (flags & Qt::FramelessWindowHint)) { + // Non-GL windows with alpha: Use blend function to update. BLENDFUNCTION blend = {AC_SRC_OVER, 0, (BYTE)(255.0 * level), AC_SRC_ALPHA}; QWindowsContext::user32dll.updateLayeredWindow(hwnd, NULL, NULL, NULL, NULL, NULL, 0, &blend, ULW_ALPHA); } else { @@ -661,7 +661,7 @@ void WindowCreationData::initialize(HWND hwnd, bool frameChange, qreal opacityLe EnableMenuItem(systemMenu, SC_CLOSE, MF_BYCOMMAND|MF_GRAYED); } - setWindowOpacity(hwnd, flags, hasAlpha, opacityLevel); + setWindowOpacity(hwnd, flags, hasAlpha, isGL, opacityLevel); } else { // child. SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, swpFlags); } @@ -929,6 +929,7 @@ QWindowsWindow::QWindowsWindow(QWindow *aWindow, const WindowData &data) : QWindowsWindow::~QWindowsWindow() { + setFlag(WithinDestroy); #ifndef Q_OS_WINCE if (testFlag(TouchRegistered)) QWindowsContext::user32dll.unregisterTouchWindow(m_data.hwnd); @@ -1122,6 +1123,8 @@ QPoint QWindowsWindow::mapFromGlobal(const QPoint &pos) const void QWindowsWindow::updateTransientParent() const { #ifndef Q_OS_WINCE + if (window()->type() == Qt::Popup) + return; // QTBUG-34503, // a popup stays on top, no parent, see also WindowCreationData::fromWindow(). // Update transient parent. const HWND oldTransientParent = GetAncestor(m_data.hwnd, GA_PARENT) == GetDesktopWindow() ? GetAncestor(m_data.hwnd, GA_ROOTOWNER) : HWND(0); @@ -1497,6 +1500,8 @@ void QWindowsWindow::handleWindowStateChange(Qt::WindowState state) handleHidden(); QWindowSystemInterface::flushWindowSystemEvents(); // Tell QQuickWindow to stop rendering now. break; + case Qt::WindowMaximized: + case Qt::WindowFullScreen: case Qt::WindowNoState: { // QTBUG-17548: We send expose events when receiving WM_Paint, but for // layered windows and transient children, we won't receive any WM_Paint. @@ -1548,7 +1553,7 @@ static const QScreen *effectiveScreen(const QWindow *w) bool QWindowsWindow::isFullScreen_sys() const { - return geometry_sys() == effectiveScreen(window())->geometry(); + return window()->isTopLevel() && geometry_sys() == effectiveScreen(window())->geometry(); } /*! @@ -1745,7 +1750,9 @@ void QWindowsWindow::setOpacity(qreal level) if (m_opacity != level) { m_opacity = level; if (m_data.hwnd) - setWindowOpacity(m_data.hwnd, m_data.flags, window()->format().hasAlpha(), level); + setWindowOpacity(m_data.hwnd, m_data.flags, + window()->format().hasAlpha(), testFlag(OpenGLSurface), + level); } } diff --git a/src/plugins/platforms/xcb/qglxintegration.cpp b/src/plugins/platforms/xcb/qglxintegration.cpp index 5fc5b4ba97..3f1c53b122 100644 --- a/src/plugins/platforms/xcb/qglxintegration.cpp +++ b/src/plugins/platforms/xcb/qglxintegration.cpp @@ -440,6 +440,86 @@ bool QGLXContext::isValid() const return m_context != 0; } +bool QGLXContext::m_queriedDummyContext = false; +bool QGLXContext::m_supportsThreading = true; + + +// If this list grows to any significant size, change it a +// proper string table and make the implementation below use +// binary search. +static const char *qglx_threadedgl_blacklist_renderer[] = { + "Chromium", // QTBUG-32225 (initialization fails) + "Mesa DRI Intel(R) Sandybridge Mobile", // QTBUG-34492 (flickering in fullscreen) + 0 +}; + +static const char *qglx_threadedgl_blacklist_vendor[] = { + "nouveau", // QTCREATORBUG-10875 (crash in creator) + 0 +}; + +void QGLXContext::queryDummyContext() +{ + if (m_queriedDummyContext) + return; + m_queriedDummyContext = true; + + static bool skip = qEnvironmentVariableIsSet("QT_OPENGL_NO_SANITY_CHECK"); + if (skip) + return; + + QOpenGLContext *oldContext = QOpenGLContext::currentContext(); + QSurface *oldSurface = 0; + if (oldContext) + oldSurface = oldContext->surface(); + + QScopedPointer<QSurface> surface; + const char *glxvendor = glXGetClientString(glXGetCurrentDisplay(), GLX_VENDOR); + if (glxvendor && !strcmp(glxvendor, "ATI")) { + QWindow *window = new QWindow; + window->resize(64, 64); + window->setSurfaceType(QSurface::OpenGLSurface); + window->create(); + surface.reset(window); + } else { + QOffscreenSurface *offSurface = new QOffscreenSurface; + offSurface->create(); + surface.reset(offSurface); + } + + QOpenGLContext context; + context.create(); + context.makeCurrent(surface.data()); + + m_supportsThreading = true; + + const char *renderer = (const char *) glGetString(GL_RENDERER); + for (int i = 0; qglx_threadedgl_blacklist_renderer[i]; ++i) { + if (strstr(renderer, qglx_threadedgl_blacklist_renderer[i]) != 0) { + m_supportsThreading = false; + break; + } + } + + const char *vendor = (const char *) glGetString(GL_VENDOR); + for (int i = 0; qglx_threadedgl_blacklist_vendor[i]; ++i) { + if (strstr(vendor, qglx_threadedgl_blacklist_vendor[i]) != 0) { + m_supportsThreading = false; + break; + } + } + + context.doneCurrent(); + if (oldContext && oldSurface) + oldContext->makeCurrent(oldSurface); +} + +bool QGLXContext::supportsThreading() +{ + if (!m_queriedDummyContext) + queryDummyContext(); + return m_supportsThreading; +} QGLXPbuffer::QGLXPbuffer(QOffscreenSurface *offscreenSurface) : QPlatformOffscreenSurface(offscreenSurface) diff --git a/src/plugins/platforms/xcb/qglxintegration.h b/src/plugins/platforms/xcb/qglxintegration.h index 1666f71060..00bba94ab3 100644 --- a/src/plugins/platforms/xcb/qglxintegration.h +++ b/src/plugins/platforms/xcb/qglxintegration.h @@ -72,6 +72,9 @@ public: GLXContext glxContext() const { return m_context; } + static bool supportsThreading(); + static void queryDummyContext(); + private: QXcbScreen *m_screen; GLXContext m_context; @@ -79,6 +82,8 @@ private: QSurfaceFormat m_format; bool m_isPBufferCurrent; int m_swapInterval; + static bool m_queriedDummyContext; + static bool m_supportsThreading; }; diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index 3f599123b9..96e6534b74 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -1429,7 +1429,10 @@ static const char * xcb_atomnames = { #if XCB_USE_MAEMO_WINDOW_PROPERTIES "_MEEGOTOUCH_ORIENTATION_ANGLE\0" #endif - "_XSETTINGS_SETTINGS\0" // \0\0 terminates loop. + "_XSETTINGS_SETTINGS\0" + "_COMPIZ_DECOR_PENDING\0" + "_COMPIZ_DECOR_REQUEST\0" + "_COMPIZ_DECOR_DELETE_PIXMAP\0" // \0\0 terminates loop. }; QXcbAtom::Atom QXcbConnection::qatom(xcb_atom_t xatom) const @@ -1737,10 +1740,26 @@ bool QXcbConnection::xi2GetValuatorValueIfSet(void *event, int valuatorNum, doub return true; } -bool QXcbConnection::xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *event, int opCode) +// Starting from the xcb version 1.9.3 struct xcb_ge_event_t has changed: +// - "pad0" became "extension" +// - "pad1" and "pad" became "pad0" +// New and old version of this struct share the following fields: +// NOTE: API might change again in the next release of xcb in which case this comment will +// need to be updated to reflect the reality. +typedef struct qt_xcb_ge_event_t { + uint8_t response_type; + uint8_t extension; + uint16_t sequence; + uint32_t length; + uint16_t event_type; +} qt_xcb_ge_event_t; + +bool QXcbConnection::xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *ev, int opCode) { - // xGenericEvent has "extension" on the second byte, xcb_ge_event_t has "pad0". - if (event->pad0 == opCode) { + qt_xcb_ge_event_t *event = (qt_xcb_ge_event_t *)ev; + // xGenericEvent has "extension" on the second byte, the same is true for xcb_ge_event_t starting from + // the xcb version 1.9.3, prior to that it was called "pad0". + if (event->extension == opCode) { // xcb event structs contain stuff that wasn't on the wire, the full_sequence field // adds an extra 4 bytes and generic events cookie data is on the wire right after the standard 32 bytes. // Move this data back to have the same layout in memory as it was on the wire diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h index 3f46f1c462..3a71eb2574 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -277,6 +277,10 @@ namespace QXcbAtom { #endif _XSETTINGS_SETTINGS, + _COMPIZ_DECOR_PENDING, + _COMPIZ_DECOR_REQUEST, + _COMPIZ_DECOR_DELETE_PIXMAP, + NPredefinedAtoms, _QT_SETTINGS_TIMESTAMP = NPredefinedAtoms, diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp index 5168bd818b..ecbf28bab9 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.cpp +++ b/src/plugins/platforms/xcb/qxcbintegration.cpp @@ -278,10 +278,15 @@ bool QXcbIntegration::hasCapability(QPlatformIntegration::Capability cap) const #else case OpenGL: return false; #endif +#if defined(XCB_USE_GLX) + case ThreadedOpenGL: return m_connections.at(0)->supportsThreadedRendering() && QGLXContext::supportsThreading(); +#else case ThreadedOpenGL: return m_connections.at(0)->supportsThreadedRendering(); +#endif case WindowMasks: return true; case MultipleWindows: return true; case ForeignWindows: return true; + case SyncState: return true; default: return QPlatformIntegration::hasCapability(cap); } } @@ -458,4 +463,11 @@ QPlatformSessionManager *QXcbIntegration::createPlatformSessionManager(const QSt } #endif +void QXcbIntegration::sync() +{ + for (int i = 0; i < m_connections.size(); i++) { + m_connections.at(i)->sync(); + } +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbintegration.h b/src/plugins/platforms/xcb/qxcbintegration.h index 79fb1965c4..6ae23125c8 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.h +++ b/src/plugins/platforms/xcb/qxcbintegration.h @@ -106,6 +106,7 @@ public: QPlatformSessionManager *createPlatformSessionManager(const QString &id, const QString &key) const Q_DECL_OVERRIDE; #endif + void sync(); private: QList<QXcbConnection *> m_connections; diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index 832f871bb7..806f948cc2 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -154,7 +154,7 @@ enum QX11EmbedMessageType { XEMBED_ACTIVATE_ACCELERATOR = 14 }; -const long XEMBED_VERSION = 0; +const quint32 XEMBED_VERSION = 0; // Returns \c true if we should set WM_TRANSIENT_FOR on \a w static inline bool isTransient(const QWindow *w) @@ -403,7 +403,7 @@ void QXcbWindow::create() } // set the PID to let the WM kill the application if unresponsive - long pid = getpid(); + quint32 pid = getpid(); Q_XCB_CALL(xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, atom(QXcbAtom::_NET_WM_PID), XCB_ATOM_CARDINAL, 32, 1, &pid)); @@ -422,7 +422,7 @@ void QXcbWindow::create() 1, &leader)); /* Add XEMBED info; this operation doesn't initiate the embedding. */ - long data[] = { XEMBED_VERSION, XEMBED_MAPPED }; + quint32 data[] = { XEMBED_VERSION, XEMBED_MAPPED }; Q_XCB_CALL(xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, atom(QXcbAtom::_XEMBED_INFO), atom(QXcbAtom::_XEMBED_INFO), @@ -1544,11 +1544,18 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even #endif } else if (event->type == atom(QXcbAtom::_XEMBED)) { handleXEmbedMessage(event); - } else if (event->type == atom(QXcbAtom::MANAGER) || event->type == atom(QXcbAtom::_NET_ACTIVE_WINDOW) - || event->type == atom(QXcbAtom::_NET_WM_STATE) || event->type == atom(QXcbAtom::MANAGER) + } else if (event->type == atom(QXcbAtom::_NET_ACTIVE_WINDOW)) { + connection()->setFocusWindow(this); + QWindowSystemInterface::handleWindowActivated(window()); + } else if (event->type == atom(QXcbAtom::MANAGER) + || event->type == atom(QXcbAtom::_NET_WM_STATE) || event->type == atom(QXcbAtom::WM_CHANGE_STATE)) { - // Ignore _NET_ACTIVE_WINDOW, _NET_WM_STATE, MANAGER which are relate to tray icons + // Ignore _NET_WM_STATE, MANAGER which are relate to tray icons // and other messages. + } else if (event->type == atom(QXcbAtom::_COMPIZ_DECOR_PENDING) + || event->type == atom(QXcbAtom::_COMPIZ_DECOR_REQUEST) + || event->type == atom(QXcbAtom::_COMPIZ_DECOR_DELETE_PIXMAP)) { + //silence the _COMPIZ messages for now } else { qWarning() << "QXcbWindow: Unhandled client message:" << connection()->atomName(event->type); } @@ -1817,7 +1824,7 @@ void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *ev xcb_get_property_reply(xcb_connection(), get_cookie, NULL); if (reply && reply->format == 32 && reply->type == wmStateAtom) { - const long *data = (const long *)xcb_get_property_value(reply); + const quint32 *data = (const quint32 *)xcb_get_property_value(reply); if (reply->length != 0 && XCB_WM_STATE_ICONIC == data[0]) newState = Qt::WindowMinimized; } @@ -1988,8 +1995,8 @@ bool QXcbWindow::startSystemResize(const QPoint &pos, Qt::Corner corner) } // Sends an XEmbed message. -void QXcbWindow::sendXEmbedMessage(xcb_window_t window, long message, - long detail, long data1, long data2) +void QXcbWindow::sendXEmbedMessage(xcb_window_t window, quint32 message, + quint32 detail, quint32 data1, quint32 data2) { xcb_client_message_event_t event; diff --git a/src/plugins/platforms/xcb/qxcbwindow.h b/src/plugins/platforms/xcb/qxcbwindow.h index 5601a115e9..45d44b213f 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.h +++ b/src/plugins/platforms/xcb/qxcbwindow.h @@ -169,8 +169,8 @@ private: void updateDoesNotAcceptFocus(bool doesNotAcceptFocus); QRect windowToWmGeometry(QRect r) const; - void sendXEmbedMessage(xcb_window_t window, long message, - long detail = 0, long data1 = 0, long data2 = 0); + void sendXEmbedMessage(xcb_window_t window, quint32 message, + quint32 detail = 0, quint32 data1 = 0, quint32 data2 = 0); void handleXEmbedMessage(const xcb_client_message_event_t *event); void create(); diff --git a/src/plugins/platformthemes/gtk2/qgtk2theme.cpp b/src/plugins/platformthemes/gtk2/qgtk2theme.cpp index f069d9f97c..812f4bc000 100644 --- a/src/plugins/platformthemes/gtk2/qgtk2theme.cpp +++ b/src/plugins/platformthemes/gtk2/qgtk2theme.cpp @@ -87,8 +87,16 @@ QVariant QGtk2Theme::themeHint(QPlatformTheme::ThemeHint hint) const bool QGtk2Theme::usePlatformNativeDialog(DialogType type) const { - Q_UNUSED(type); - return true; + switch (type) { + case ColorDialog: + return true; + case FileDialog: + return true; + case FontDialog: + return true; + default: + return false; + } } QPlatformDialogHelper *QGtk2Theme::createPlatformDialogHelper(DialogType type) const |