diff options
author | Iikka Eklund <iikka.eklund@digia.com> | 2014-04-02 07:36:48 +0200 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2014-04-02 07:36:48 +0200 |
commit | d6f58e5e17d58c640fc7779652008fda0d66f41d (patch) | |
tree | d50d27e260c9eaf4bc2bcb4480b8a069a3aed857 /src/plugins/platforms | |
parent | 83de197a57ff6c3e5bbad26bd871981285384fcb (diff) | |
parent | 0ab63b035a649dc1982c867cd37d466d249004b9 (diff) |
Merge "Merge remote-tracking branch 'origin/stable' into dev" into refs/staging/dev
Diffstat (limited to 'src/plugins/platforms')
50 files changed, 886 insertions, 517 deletions
diff --git a/src/plugins/platforms/android/androidjnimain.cpp b/src/plugins/platforms/android/androidjnimain.cpp index 784cc2e38b..ff1a40bfc5 100644 --- a/src/plugins/platforms/android/androidjnimain.cpp +++ b/src/plugins/platforms/android/androidjnimain.cpp @@ -576,7 +576,8 @@ static void updateWindow(JNIEnv */*env*/, jobject /*thiz*/) } QAndroidPlatformScreen *screen = static_cast<QAndroidPlatformScreen *>(m_androidPlatformIntegration->screen()); - QMetaObject::invokeMethod(screen, "setDirty", Qt::QueuedConnection, Q_ARG(QRect,screen->geometry())); + if (screen->rasterSurfaces()) + QMetaObject::invokeMethod(screen, "setDirty", Qt::QueuedConnection, Q_ARG(QRect,screen->geometry())); } static void updateApplicationState(JNIEnv */*env*/, jobject /*thiz*/, jint state) diff --git a/src/plugins/platforms/android/qandroidinputcontext.cpp b/src/plugins/platforms/android/qandroidinputcontext.cpp index bfb13811e3..3324d9ba49 100644 --- a/src/plugins/platforms/android/qandroidinputcontext.cpp +++ b/src/plugins/platforms/android/qandroidinputcontext.cpp @@ -70,6 +70,35 @@ static jfieldID m_selectionStartFieldID = 0; static jfieldID m_startOffsetFieldID = 0; static jfieldID m_textFieldID = 0; +static jboolean beginBatchEdit(JNIEnv */*env*/, jobject /*thiz*/) +{ + if (!m_androidInputContext) + return JNI_FALSE; + +#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL + qDebug() << "@@@ BEGINBATCH"; +#endif + + return m_androidInputContext->beginBatchEdit(); + + return JNI_TRUE; +} + +static jboolean endBatchEdit(JNIEnv */*env*/, jobject /*thiz*/) +{ + if (!m_androidInputContext) + return JNI_FALSE; + +#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL + qDebug() << "@@@ ENDBATCH"; +#endif + + return m_androidInputContext->endBatchEdit(); + + return JNI_TRUE; +} + + static jboolean commitText(JNIEnv *env, jobject /*thiz*/, jstring text, jint newCursorPosition) { if (!m_androidInputContext) @@ -121,12 +150,13 @@ static jobject getExtractedText(JNIEnv *env, jobject /*thiz*/, int hintMaxChars, if (!m_androidInputContext) return 0; -#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL - qDebug() << "@@@ GETEX"; -#endif const QAndroidInputContext::ExtractedText &extractedText = m_androidInputContext->getExtractedText(hintMaxChars, hintMaxLines, flags); +#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL + qDebug() << "@@@ GETEX" << hintMaxChars << hintMaxLines << QString::fromLatin1("0x") + QString::number(flags,16) << extractedText.text << "partOff:" << extractedText.partialStartOffset << extractedText.partialEndOffset << "sel:" << extractedText.selectionStart << extractedText.selectionEnd << "offset:" << extractedText.startOffset; +#endif + jobject object = env->NewObject(m_extractedTextClass, m_classConstructorMethodID); env->SetIntField(object, m_partialStartOffsetFieldID, extractedText.partialStartOffset); env->SetIntField(object, m_partialEndOffsetFieldID, extractedText.partialEndOffset); @@ -285,6 +315,8 @@ static jboolean updateCursorPosition(JNIEnv */*env*/, jobject /*thiz*/) static JNINativeMethod methods[] = { + {"beginBatchEdit", "()Z", (void *)beginBatchEdit}, + {"endBatchEdit", "()Z", (void *)endBatchEdit}, {"commitText", "(Ljava/lang/String;I)Z", (void *)commitText}, {"deleteSurroundingText", "(II)Z", (void *)deleteSurroundingText}, {"finishComposingText", "()Z", (void *)finishComposingText}, @@ -306,7 +338,7 @@ static JNINativeMethod methods[] = { QAndroidInputContext::QAndroidInputContext() - : QPlatformInputContext(), m_blockUpdateSelection(false) + : QPlatformInputContext(), m_blockUpdateSelection(false), m_batchEditNestingLevel(0) { QtAndroid::AttachedJNIEnv env; if (!env.jniEnv) @@ -416,11 +448,14 @@ void QAndroidInputContext::commit() void QAndroidInputContext::updateCursorPosition() { QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(); - if (!query.isNull() && !m_blockUpdateSelection) { + if (!query.isNull() && !m_blockUpdateSelection && !m_batchEditNestingLevel) { // make sure it also works with editors that have not been updated to the new API QVariant absolutePos = query->value(Qt::ImAbsolutePosition); const int cursorPos = absolutePos.isValid() ? absolutePos.toInt() : query->value(Qt::ImCursorPosition).toInt(); - QtAndroidInput::updateSelection(cursorPos, cursorPos, -1, -1); //selection empty and no pre-edit text + const int composeLength = m_composingText.length(); + const int composeStart = composeLength ? cursorPos : -1; + QtAndroidInput::updateSelection(cursorPos + composeLength, cursorPos + composeLength, //empty selection + composeStart, composeStart + composeLength); // pre-edit text } } @@ -507,6 +542,19 @@ void QAndroidInputContext::sendEvent(QObject *receiver, QInputMethodQueryEvent * QCoreApplication::sendEvent(receiver, event); } +jboolean QAndroidInputContext::beginBatchEdit() +{ + ++m_batchEditNestingLevel; + return JNI_TRUE; +} + +jboolean QAndroidInputContext::endBatchEdit() +{ + if (--m_batchEditNestingLevel == 0 && !m_blockUpdateSelection) //ending batch edit mode + updateCursorPosition(); + return JNI_TRUE; +} + jboolean QAndroidInputContext::commitText(const QString &text, jint /*newCursorPosition*/) { m_composingText = text; @@ -559,19 +607,39 @@ jint QAndroidInputContext::getCursorCapsMode(jint /*reqModes*/) const QAndroidInputContext::ExtractedText &QAndroidInputContext::getExtractedText(jint hintMaxChars, jint /*hintMaxLines*/, jint /*flags*/) { + // Note to self: "if the GET_EXTRACTED_TEXT_MONITOR flag is set, you should be calling + // updateExtractedText(View, int, ExtractedText) whenever you call + // updateSelection(View, int, int, int, int)." QTBUG-37980 + QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(); if (query.isNull()) return m_extractedText; - if (hintMaxChars) - m_extractedText.text = query->value(Qt::ImSurroundingText).toString().right(hintMaxChars); + int localPos = query->value(Qt::ImCursorPosition).toInt(); //position before pre-edit text relative to the current block + QVariant absolutePos = query->value(Qt::ImAbsolutePosition); + int blockPos = absolutePos.isValid() ? absolutePos.toInt() - localPos : 0; // position of the start of the current block + QString blockText = query->value(Qt::ImSurroundingText).toString() + m_composingText; + int composeLength = m_composingText.length(); + + int cpos = localPos + composeLength; //actual cursor pos relative to the current block + + int localOffset = 0; // start of extracted text relative to the current block + if (hintMaxChars) { + if (cpos > hintMaxChars) + localOffset = cpos - hintMaxChars; + m_extractedText.text = blockText.mid(localOffset, hintMaxChars); + } + + m_extractedText.startOffset = blockPos + localOffset; // "The offset in the overall text at which the extracted text starts." - m_extractedText.startOffset = query->value(Qt::ImCursorPosition).toInt(); const QString &selection = query->value(Qt::ImCurrentSelection).toString(); const int selLen = selection.length(); if (selLen) { - m_extractedText.selectionStart = query->value(Qt::ImAnchorPosition).toInt(); - m_extractedText.selectionEnd = m_extractedText.startOffset; + m_extractedText.selectionStart = query->value(Qt::ImAnchorPosition).toInt() - localOffset; + m_extractedText.selectionEnd = m_extractedText.selectionStart + selLen; + } else { + m_extractedText.selectionStart = cpos - localOffset; + m_extractedText.selectionEnd = cpos - localOffset; } return m_extractedText; @@ -610,7 +678,7 @@ QString QAndroidInputContext::getTextBeforeCursor(jint length, jint /*flags*/) { QVariant textBefore = queryFocusObjectThreadSafe(Qt::ImTextBeforeCursor, QVariant(length)); if (textBefore.isValid()) { - return textBefore.toString().left(length); + return textBefore.toString().left(length) + m_composingText; } //compatibility code for old controls that do not implement the new API @@ -624,7 +692,7 @@ QString QAndroidInputContext::getTextBeforeCursor(jint length, jint /*flags*/) return text; const int wordLeftPos = cursorPos - length; - return text.mid(wordLeftPos > 0 ? wordLeftPos : 0, cursorPos); + return text.mid(wordLeftPos > 0 ? wordLeftPos : 0, cursorPos) + m_composingText; } jboolean QAndroidInputContext::setComposingText(const QString &text, jint newCursorPosition) @@ -647,11 +715,11 @@ jboolean QAndroidInputContext::setComposingText(const QString &text, jint newCur sendInputMethodEvent(&event); QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(); - if (!query.isNull() && !m_blockUpdateSelection) { + if (!query.isNull() && !m_blockUpdateSelection && !m_batchEditNestingLevel) { QVariant absolutePos = query->value(Qt::ImAbsolutePosition); const int cursorPos = absolutePos.isValid() ? absolutePos.toInt() : query->value(Qt::ImCursorPosition).toInt(); const int preeditLength = text.length(); - QtAndroidInput::updateSelection(cursorPos+preeditLength, cursorPos+preeditLength, cursorPos, cursorPos+preeditLength); + QtAndroidInput::updateSelection(cursorPos+preeditLength, cursorPos+preeditLength, -1, -1); } return JNI_TRUE; @@ -713,9 +781,17 @@ jboolean QAndroidInputContext::setComposingRegion(jint start, jint end) jboolean QAndroidInputContext::setSelection(jint start, jint end) { + QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(); + if (query.isNull()) + return JNI_FALSE; + + int localPos = query->value(Qt::ImCursorPosition).toInt(); + QVariant absolutePos = query->value(Qt::ImAbsolutePosition); + int blockPosition = absolutePos.isValid() ? absolutePos.toInt() - localPos : 0; + QList<QInputMethodEvent::Attribute> attributes; attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Selection, - start, + start - blockPosition, end - start, QVariant())); diff --git a/src/plugins/platforms/android/qandroidinputcontext.h b/src/plugins/platforms/android/qandroidinputcontext.h index 2fb54a97c4..f7b29a855f 100644 --- a/src/plugins/platforms/android/qandroidinputcontext.h +++ b/src/plugins/platforms/android/qandroidinputcontext.h @@ -97,6 +97,8 @@ public: void clear(); //---------------// + jboolean beginBatchEdit(); + jboolean endBatchEdit(); jboolean commitText(const QString &text, jint newCursorPosition); jboolean deleteSurroundingText(jint leftLength, jint rightLength); jboolean finishComposingText(); @@ -133,6 +135,7 @@ private: QString m_composingText; QMetaObject::Connection m_updateCursorPosConnection; bool m_blockUpdateSelection; + int m_batchEditNestingLevel; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/android/qandroidplatformdialoghelpers.cpp b/src/plugins/platforms/android/qandroidplatformdialoghelpers.cpp index e76eedbfd9..897feb5802 100644 --- a/src/plugins/platforms/android/qandroidplatformdialoghelpers.cpp +++ b/src/plugins/platforms/android/qandroidplatformdialoghelpers.cpp @@ -41,6 +41,9 @@ #include "qandroidplatformdialoghelpers.h" #include "androidjnimain.h" + +#include <QTextDocument> + #include <private/qguiapplication_p.h> #include <qpa/qplatformtheme.h> @@ -61,6 +64,14 @@ void QAndroidPlatformMessageDialogHelper::exec() m_loop.exec(); } +static QString htmlText(QString text) +{ + if (Qt::mightBeRichText(text)) + return text; + text.remove(QLatin1Char('\r')); + return text.toHtmlEscaped().replace(QLatin1Char('\n'), QLatin1String("<br />")); +} + bool QAndroidPlatformMessageDialogHelper::show(Qt::WindowFlags windowFlags , Qt::WindowModality windowModality , QWindow *parent) @@ -74,19 +85,19 @@ bool QAndroidPlatformMessageDialogHelper::show(Qt::WindowFlags windowFlags m_javaMessageDialog.callMethod<void>("setIcon", "(I)V", opt->icon()); - QString str = opt->windowTitle(); + QString str = htmlText(opt->windowTitle()); if (!str.isEmpty()) m_javaMessageDialog.callMethod<void>("setTile", "(Ljava/lang/String;)V", QJNIObjectPrivate::fromString(str).object()); - str = opt->text(); + str = htmlText(opt->text()); if (!str.isEmpty()) m_javaMessageDialog.callMethod<void>("setText", "(Ljava/lang/String;)V", QJNIObjectPrivate::fromString(str).object()); - str = opt->informativeText(); + str = htmlText(opt->informativeText()); if (!str.isEmpty()) m_javaMessageDialog.callMethod<void>("setInformativeText", "(Ljava/lang/String;)V", QJNIObjectPrivate::fromString(str).object()); - str = opt->detailedText(); + str = htmlText(opt->detailedText()); if (!str.isEmpty()) m_javaMessageDialog.callMethod<void>("setDetailedText", "(Ljava/lang/String;)V", QJNIObjectPrivate::fromString(str).object()); diff --git a/src/plugins/platforms/android/qandroidplatformfontdatabase.cpp b/src/plugins/platforms/android/qandroidplatformfontdatabase.cpp index 7f68b44ed8..7423e6c55a 100644 --- a/src/plugins/platforms/android/qandroidplatformfontdatabase.cpp +++ b/src/plugins/platforms/android/qandroidplatformfontdatabase.cpp @@ -60,7 +60,17 @@ void QAndroidPlatformFontDatabase::populateFontDatabase() QDir dir(fontpath, QLatin1String("*.ttf")); for (int i = 0; i < int(dir.count()); ++i) { const QByteArray file = QFile::encodeName(dir.absoluteFilePath(dir[i])); - addTTFile(QByteArray(), file); + + QSupportedWritingSystems supportedWritingSystems; + QStringList families = addTTFile(QByteArray(), file, &supportedWritingSystems); + + extern int qt_script_for_writing_system(QFontDatabase::WritingSystem writingSystem); + for (int i = 0; i < QFontDatabase::WritingSystemsCount; ++i) { + if (i == QFontDatabase::Any || supportedWritingSystems.supported(QFontDatabase::WritingSystem(i))) { + QChar::Script script = QChar::Script(qt_script_for_writing_system(QFontDatabase::WritingSystem(i))); + m_fallbacks[script] += families; + } + } } } @@ -71,9 +81,9 @@ QStringList QAndroidPlatformFontDatabase::fallbacksForFamily(const QString &fami { Q_UNUSED(family); Q_UNUSED(style); - Q_UNUSED(script); + if (styleHint == QFont::Monospace) - return QString(qgetenv("QT_ANDROID_FONTS_MONOSPACE")).split(";"); + return QString(qgetenv("QT_ANDROID_FONTS_MONOSPACE")).split(";") + m_fallbacks[script]; - return QString(qgetenv("QT_ANDROID_FONTS")).split(";"); + return QString(qgetenv("QT_ANDROID_FONTS")).split(";") + m_fallbacks[script]; } diff --git a/src/plugins/platforms/android/qandroidplatformfontdatabase.h b/src/plugins/platforms/android/qandroidplatformfontdatabase.h index 3cbfe95d36..cdd3cf1674 100644 --- a/src/plugins/platforms/android/qandroidplatformfontdatabase.h +++ b/src/plugins/platforms/android/qandroidplatformfontdatabase.h @@ -53,6 +53,9 @@ public: QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const; + +private: + QHash<QChar::Script, QStringList> m_fallbacks; }; #endif // QANDROIDPLATFORMFONTDATABASE_H diff --git a/src/plugins/platforms/android/qandroidplatformintegration.cpp b/src/plugins/platforms/android/qandroidplatformintegration.cpp index 9adefd5b2c..7f0f40be0f 100644 --- a/src/plugins/platforms/android/qandroidplatformintegration.cpp +++ b/src/plugins/platforms/android/qandroidplatformintegration.cpp @@ -41,6 +41,7 @@ #include "qandroidplatformintegration.h" +#include <QtCore/private/qjni_p.h> #include <QGuiApplication> #include <QOpenGLContext> #include <QThread> @@ -103,9 +104,6 @@ QAndroidPlatformIntegration::QAndroidPlatformIntegration(const QStringList ¶ m_androidPlatformNativeInterface = new QAndroidPlatformNativeInterface(); - if (!eglBindAPI(EGL_OPENGL_ES_API)) - qFatal("Could not bind GL_ES API"); - m_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); if (m_eglDisplay == EGL_NO_DISPLAY) qFatal("Could not open egl display"); @@ -114,6 +112,9 @@ QAndroidPlatformIntegration::QAndroidPlatformIntegration(const QStringList ¶ if (!eglInitialize(m_eglDisplay, &major, &minor)) qFatal("Could not initialize egl display"); + if (!eglBindAPI(EGL_OPENGL_ES_API)) + qFatal("Could not bind GL_ES API"); + m_primaryScreen = new QAndroidPlatformScreen(); screenAdded(m_primaryScreen); m_primaryScreen->setPhysicalSize(QSize(m_defaultPhysicalSizeWidth, m_defaultPhysicalSizeHeight)); @@ -130,9 +131,41 @@ QAndroidPlatformIntegration::QAndroidPlatformIntegration(const QStringList ¶ #endif m_androidSystemLocale = new QAndroidSystemLocale; + + QJNIObjectPrivate javaActivity(QtAndroid::activity()); + if (javaActivity.isValid()) { + QJNIObjectPrivate resources = javaActivity.callObjectMethod("getResources", "()Landroid/content/res/Resources;"); + QJNIObjectPrivate configuration = resources.callObjectMethod("getConfiguration", "()Landroid/content/res/Configuration;"); + + int touchScreen = configuration.getField<jint>("touchscreen"); + if (touchScreen == QJNIObjectPrivate::getStaticField<jint>("android/content/res/Configuration", "TOUCHSCREEN_FINGER") + || touchScreen == QJNIObjectPrivate::getStaticField<jint>("android/content/res/Configuration", "TOUCHSCREEN_STYLUS")) + { + m_touchDevice = new QTouchDevice; + m_touchDevice->setType(QTouchDevice::TouchScreen); + m_touchDevice->setCapabilities(QTouchDevice::Position + | QTouchDevice::Area + | QTouchDevice::Pressure + | QTouchDevice::NormalizedPosition); + + QJNIObjectPrivate pm = javaActivity.callObjectMethod("getPackageManager", "()Landroid/content/pm/PackageManager;"); + Q_ASSERT(pm.isValid()); + if (pm.callMethod<jboolean>("hasSystemFeature","(Ljava/lang/String;)Z", + QJNIObjectPrivate::getStaticObjectField("android/content/pm/PackageManager", "FEATURE_TOUCHSCREEN_MULTITOUCH_JAZZHAND", "Ljava/lang/String;").object())) { + m_touchDevice->setMaximumTouchPoints(10); + } else if (pm.callMethod<jboolean>("hasSystemFeature","(Ljava/lang/String;)Z", + QJNIObjectPrivate::getStaticObjectField("android/content/pm/PackageManager", "FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT", "Ljava/lang/String;").object())) { + m_touchDevice->setMaximumTouchPoints(4); + } else if (pm.callMethod<jboolean>("hasSystemFeature","(Ljava/lang/String;)Z", + QJNIObjectPrivate::getStaticObjectField("android/content/pm/PackageManager", "FEATURE_TOUCHSCREEN_MULTITOUCH", "Ljava/lang/String;").object())) { + m_touchDevice->setMaximumTouchPoints(2); + } + QWindowSystemInterface::registerTouchDevice(m_touchDevice); + } + } } -bool QAndroidPlatformIntegration::needsWorkaround() +bool QAndroidPlatformIntegration::needsBasicRenderloopWorkaround() { static bool needsWorkaround = QtAndroid::deviceName().compare(QStringLiteral("samsung SM-T211"), Qt::CaseInsensitive) == 0 @@ -150,7 +183,7 @@ bool QAndroidPlatformIntegration::hasCapability(Capability cap) const case OpenGL: return true; case ForeignWindows: return true; case ThreadedOpenGL: - if (needsWorkaround()) + if (needsBasicRenderloopWorkaround()) return false; else return true; diff --git a/src/plugins/platforms/android/qandroidplatformintegration.h b/src/plugins/platforms/android/qandroidplatformintegration.h index 2d685bc567..4a3fe6c766 100644 --- a/src/plugins/platforms/android/qandroidplatformintegration.h +++ b/src/plugins/platforms/android/qandroidplatformintegration.h @@ -120,9 +120,9 @@ public: QTouchDevice *touchDevice() const { return m_touchDevice; } void setTouchDevice(QTouchDevice *touchDevice) { m_touchDevice = touchDevice; } - static bool needsWorkaround(); EGLDisplay m_eglDisplay; private: + static bool needsBasicRenderloopWorkaround(); QTouchDevice *m_touchDevice; diff --git a/src/plugins/platforms/android/qandroidplatformopenglcontext.cpp b/src/plugins/platforms/android/qandroidplatformopenglcontext.cpp index a0b3ae066c..289480c625 100644 --- a/src/plugins/platforms/android/qandroidplatformopenglcontext.cpp +++ b/src/plugins/platforms/android/qandroidplatformopenglcontext.cpp @@ -62,6 +62,22 @@ void QAndroidPlatformOpenGLContext::swapBuffers(QPlatformSurface *surface) static_cast<QAndroidPlatformOpenGLWindow *>(surface)->checkNativeSurface(eglConfig()); } +bool QAndroidPlatformOpenGLContext::needsFBOReadBackWorkaroud() +{ + static bool set = false; + static bool needsWorkaround = false; + + if (!set) { + const char *rendererString = reinterpret_cast<const char *>(glGetString(GL_RENDERER)); + needsWorkaround = + qstrcmp(rendererString, "Mali-400 MP") == 0 + || qstrcmp(rendererString, "Adreno (TM) 200") == 0; + set = true; + } + + return needsWorkaround; +} + bool QAndroidPlatformOpenGLContext::makeCurrent(QPlatformSurface *surface) { bool ret = QEGLPlatformContext::makeCurrent(surface); @@ -71,7 +87,7 @@ bool QAndroidPlatformOpenGLContext::makeCurrent(QPlatformSurface *surface) if (rendererString != 0 && qstrncmp(rendererString, "Android Emulator", 16) == 0) ctx_d->workaround_missingPrecisionQualifiers = true; - if (!ctx_d->workaround_brokenFBOReadBack && QAndroidPlatformIntegration::needsWorkaround()) + if (!ctx_d->workaround_brokenFBOReadBack && needsFBOReadBackWorkaroud()) ctx_d->workaround_brokenFBOReadBack = true; return ret; diff --git a/src/plugins/platforms/android/qandroidplatformopenglcontext.h b/src/plugins/platforms/android/qandroidplatformopenglcontext.h index 29e5f596d5..10a89d541b 100644 --- a/src/plugins/platforms/android/qandroidplatformopenglcontext.h +++ b/src/plugins/platforms/android/qandroidplatformopenglcontext.h @@ -56,6 +56,8 @@ public: private: virtual EGLSurface eglSurfaceForPlatformSurface(QPlatformSurface *surface); + + static bool needsFBOReadBackWorkaroud(); }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/android/qandroidplatformrasterwindow.cpp b/src/plugins/platforms/android/qandroidplatformrasterwindow.cpp index 334b9cdd23..eb5a73c4a3 100644 --- a/src/plugins/platforms/android/qandroidplatformrasterwindow.cpp +++ b/src/plugins/platforms/android/qandroidplatformrasterwindow.cpp @@ -57,7 +57,7 @@ void QAndroidPlatformRasterWindow::repaint(const QRegion ®ion) if (QAndroidPlatformWindow::parent()) return; - QRect currentGeometry = geometry().translated(mapToGlobal(QPoint(0,0))); + QRect currentGeometry = geometry(); QRect dirtyClient = region.boundingRect(); QRect dirtyRegion(currentGeometry.left() + dirtyClient.left(), @@ -74,7 +74,7 @@ void QAndroidPlatformRasterWindow::repaint(const QRegion ®ion) void QAndroidPlatformRasterWindow::setGeometry(const QRect &rect) { - m_oldGeometry = geometry().translated(mapToGlobal(QPoint(0,0)));; + m_oldGeometry = geometry(); QAndroidPlatformWindow::setGeometry(rect); } diff --git a/src/plugins/platforms/android/qandroidplatformscreen.cpp b/src/plugins/platforms/android/qandroidplatformscreen.cpp index dbf317696f..678f4e6b5a 100644 --- a/src/plugins/platforms/android/qandroidplatformscreen.cpp +++ b/src/plugins/platforms/android/qandroidplatformscreen.cpp @@ -133,8 +133,10 @@ void QAndroidPlatformScreen::addWindow(QAndroidPlatformWindow *window) return; m_windowStack.prepend(window); - if (window->isRaster()) + if (window->isRaster()) { + m_rasterSurfaces.ref(); setDirty(window->geometry()); + } QWindow *w = topWindow(); QWindowSystemInterface::handleWindowActivated(w); @@ -148,8 +150,10 @@ void QAndroidPlatformScreen::removeWindow(QAndroidPlatformWindow *window) m_windowStack.removeOne(window); if (window->isRaster()) { + m_rasterSurfaces.deref(); setDirty(window->geometry()); } + QWindow *w = topWindow(); QWindowSystemInterface::handleWindowActivated(w); topWindowChanged(w); @@ -238,6 +242,11 @@ void QAndroidPlatformScreen::topWindowChanged(QWindow *w) } } +int QAndroidPlatformScreen::rasterSurfaces() +{ + return m_rasterSurfaces; +} + void QAndroidPlatformScreen::doRedraw() { PROFILE_SCOPE; @@ -246,7 +255,7 @@ void QAndroidPlatformScreen::doRedraw() return; QMutexLocker lock(&m_surfaceMutex); - if (m_id == -1) { + if (m_id == -1 && m_rasterSurfaces) { m_id = QtAndroid::createSurface(this, m_geometry, true, m_depth); m_surfaceWaitCondition.wait(&m_surfaceMutex); } diff --git a/src/plugins/platforms/android/qandroidplatformscreen.h b/src/plugins/platforms/android/qandroidplatformscreen.h index 625e77840e..96a91fbf06 100644 --- a/src/plugins/platforms/android/qandroidplatformscreen.h +++ b/src/plugins/platforms/android/qandroidplatformscreen.h @@ -82,6 +82,7 @@ public: void scheduleUpdate(); void topWindowChanged(QWindow *w); + int rasterSurfaces(); public slots: void setDirty(const QRect &rect); @@ -110,6 +111,7 @@ private slots: private: int m_id = -1; + QAtomicInt m_rasterSurfaces = 0; ANativeWindow* m_nativeSurface = nullptr; QWaitCondition m_surfaceWaitCondition; }; diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibility.mm b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm index 0f99a414a0..990acd5301 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibility.mm +++ b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm @@ -298,7 +298,8 @@ bool hasValueAttribute(QAccessibleInterface *interface) Q_ASSERT(interface); const QAccessible::Role qtrole = interface->role(); if (qtrole == QAccessible::EditableText - || interface->valueInterface()) { + || interface->valueInterface() + || interface->state().checkable) { return true; } @@ -330,6 +331,10 @@ id getValueAttribute(QAccessibleInterface *interface) return QCFString::toNSString(QString::number(valueInterface->currentValue().toDouble())); } + if (interface->state().checkable) { + return [NSNumber numberWithInt: (interface->state().checked ? 1 : 0)]; + } + return nil; } diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm index bc98d002f0..0b674b8d2f 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm +++ b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm @@ -417,7 +417,23 @@ } - (id)accessibilityFocusedUIElement { - return NSAccessibilityUnignoredAncestor(self); + QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); + + if (!iface || !iface->isValid()) { + qWarning() << "FocusedUIElement for INVALID"; + return nil; + } + QAccessibleInterface *childInterface = iface->focusChild(); + if (childInterface) { + QAccessible::Id childAxid = QAccessible::uniqueId(childInterface); + // FIXME: parent could be wrong + QCocoaAccessibleElement *accessibleElement = [QCocoaAccessibleElement createElementWithId:childAxid parent:self]; + [accessibleElement autorelease]; + return accessibleElement; + } + + // no focus found + return nil; } @end diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h index 33d7dcbcf4..0274ed8201 100644 --- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h +++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h @@ -163,6 +163,7 @@ public: // The following variables help organizing modal sessions: QStack<QCocoaModalSessionInfo> cocoaModalSessionStack; bool currentExecIsNSAppRun; + bool modalSessionOnNSAppRun; bool nsAppRunCalledByQt; bool cleanupModalSessionsNeeded; uint processEventsCalled; diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm index 91b631bff9..495a54cac4 100644 --- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm +++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm @@ -721,7 +721,6 @@ void QCocoaEventDispatcherPrivate::beginModalSession(QWindow *window) // setting currentModalSessionCached to zero, so that interrupt() calls // [NSApp abortModal] if another modal session is currently running Q_Q(QCocoaEventDispatcher); - q->interrupt(); // Add a new, empty (null), NSModalSession to the stack. // It will become active the next time QEventDispatcher::processEvents is called. @@ -734,6 +733,12 @@ void QCocoaEventDispatcherPrivate::beginModalSession(QWindow *window) cocoaModalSessionStack.push(info); updateChildrenWorksWhenModal(); currentModalSessionCached = 0; + if (currentExecIsNSAppRun) { + modalSessionOnNSAppRun = true; + q->wakeUp(); + } else { + q->interrupt(); + } } void QCocoaEventDispatcherPrivate::endModalSession(QWindow *window) @@ -772,6 +777,7 @@ QCocoaEventDispatcherPrivate::QCocoaEventDispatcherPrivate() runLoopTimerRef(0), blockSendPostedEvents(false), currentExecIsNSAppRun(false), + modalSessionOnNSAppRun(false), nsAppRunCalledByQt(false), cleanupModalSessionsNeeded(false), processEventsCalled(0), @@ -902,6 +908,14 @@ void QCocoaEventDispatcherPrivate::postedEventsSourceCallback(void *info) // processEvents() was called "manually," ignore this source for now d->maybeCancelWaitForMoreEvents(); return; + } else if (d->modalSessionOnNSAppRun) { + // We're about to spawn the 1st modal session on top of the main runloop. + // Instead of calling processPostedEvents(), which would need us stop + // NSApp, we just re-enter processEvents(). This is equivalent to calling + // QDialog::exec() except that it's done in a non-blocking way. + d->modalSessionOnNSAppRun = false; + d->q_func()->processEvents(QEventLoop::DialogExec | QEventLoop::EventLoopExec | QEventLoop::WaitForMoreEvents); + return; } d->processPostedEvents(); d->maybeCancelWaitForMoreEvents(); diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index 65a9f87e2d..412818ae47 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.mm +++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm @@ -316,11 +316,17 @@ QCocoaIntegration *QCocoaIntegration::instance() */ void QCocoaIntegration::updateScreens() { - NSArray *screens = [NSScreen screens]; + NSArray *scrs = [NSScreen screens]; + NSMutableArray *screens = [NSMutableArray arrayWithArray:scrs]; + if ([screens count] == 0) + if ([NSScreen mainScreen]) + [screens addObject:[NSScreen mainScreen]]; + if ([screens count] == 0) + return; QSet<QCocoaScreen*> remainingScreens = QSet<QCocoaScreen*>::fromList(mScreens); QList<QPlatformScreen *> siblings; for (uint i = 0; i < [screens count]; i++) { - NSScreen* scr = [[NSScreen screens] objectAtIndex:i]; + NSScreen* scr = [screens objectAtIndex:i]; CGDirectDisplayID dpy = [[[scr deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue]; // If this screen is a mirror and is not the primary one of the mirror set, ignore it. if (CGDisplayIsInMirrorSet(dpy)) { diff --git a/src/plugins/platforms/cocoa/qcocoawindow.h b/src/plugins/platforms/cocoa/qcocoawindow.h index 6e1f00eebe..b7a6a14d4a 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.h +++ b/src/plugins/platforms/cocoa/qcocoawindow.h @@ -183,6 +183,7 @@ public: void windowWillMove(); void windowDidMove(); void windowDidResize(); + void windowDidEndLiveResize(); bool windowShouldClose(); bool windowIsPopupType(Qt::WindowType type = Qt::Widget) const; @@ -252,6 +253,7 @@ public: // for QNSView QList<QCocoaWindow *> m_childWindows; Qt::WindowFlags m_windowFlags; + bool m_effectivelyMaximized; Qt::WindowState m_synchedWindowState; Qt::WindowModality m_windowModality; QPointer<QWindow> m_activePopupWindow; diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index c7fba4eef0..b27e1b03db 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -352,6 +352,7 @@ QCocoaWindow::QCocoaWindow(QWindow *tlw) , m_contentViewIsToBeEmbedded(false) , m_parentCocoaWindow(0) , m_isNSWindowChild(false) + , m_effectivelyMaximized(false) , m_synchedWindowState(Qt::WindowActive) , m_windowModality(Qt::NonModal) , m_windowUnderMouse(false) @@ -1164,6 +1165,14 @@ void QCocoaWindow::windowDidResize() [m_qtView updateGeometry]; } +void QCocoaWindow::windowDidEndLiveResize() +{ + if (m_synchedWindowState == Qt::WindowMaximized && ![m_nsWindow isZoomed]) { + m_effectivelyMaximized = false; + [m_qtView notifyWindowStateChanged:Qt::WindowNoState]; + } +} + bool QCocoaWindow::windowShouldClose() { bool accepted = false; @@ -1436,7 +1445,6 @@ void QCocoaWindow::syncWindowState(Qt::WindowState newState) { if (!m_nsWindow) return; - // if content view width or height is 0 then the window animations will crash so // do nothing except set the new state NSRect contentRect = [contentView() frame]; @@ -1446,9 +1454,7 @@ void QCocoaWindow::syncWindowState(Qt::WindowState newState) return; } - if ((m_synchedWindowState & Qt::WindowMaximized) != (newState & Qt::WindowMaximized)) { - [m_nsWindow performZoom : m_nsWindow]; // toggles - } + Qt::WindowState predictedState = newState; if ((m_synchedWindowState & Qt::WindowMinimized) != (newState & Qt::WindowMinimized)) { if (newState & Qt::WindowMinimized) { @@ -1458,12 +1464,26 @@ void QCocoaWindow::syncWindowState(Qt::WindowState newState) } } + if ((m_synchedWindowState & Qt::WindowMaximized) != (newState & Qt::WindowMaximized) || (m_effectivelyMaximized && newState == Qt::WindowNoState)) { + if ((m_synchedWindowState & Qt::WindowFullScreen) == (newState & Qt::WindowFullScreen)) { + [m_nsWindow performZoom : m_nsWindow]; // toggles + m_effectivelyMaximized = !m_effectivelyMaximized; + } else if (!(newState & Qt::WindowMaximized)) { + // it would be nice to change the target geometry that toggleFullScreen will animate toward + // but there is no known way, so the maximized state is not possible at this time + predictedState = static_cast<Qt::WindowState>(static_cast<int>(newState) | Qt::WindowMaximized); + m_effectivelyMaximized = true; + } + } + if ((m_synchedWindowState & Qt::WindowFullScreen) != (newState & Qt::WindowFullScreen)) { bool fakeFullScreen = true; #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 if (QSysInfo::QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7) { if (window()->flags() & Qt::WindowFullscreenButtonHint) { fakeFullScreen = false; + if (m_effectivelyMaximized && m_synchedWindowState == Qt::WindowFullScreen) + predictedState = Qt::WindowMaximized; [m_nsWindow toggleFullScreen : m_nsWindow]; } } @@ -1490,8 +1510,12 @@ void QCocoaWindow::syncWindowState(Qt::WindowState newState) } } +#ifdef QT_COCOA_ENABLE_WINDOW_DEBUG + qDebug() << "QCocoaWindow::syncWindowState" << newState << "actual" << predictedState << "was" << m_synchedWindowState << "effectively maximized" << m_effectivelyMaximized; +#endif + // New state is now the current synched state - m_synchedWindowState = newState; + m_synchedWindowState = predictedState; } bool QCocoaWindow::setWindowModified(bool modified) diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm index 47081ab890..1197aa9148 100644 --- a/src/plugins/platforms/cocoa/qnsview.mm +++ b/src/plugins/platforms/cocoa/qnsview.mm @@ -281,6 +281,12 @@ static QTouchDevice *touchDevice = 0; - (void)notifyWindowStateChanged:(Qt::WindowState)newState { + // If the window was maximized, then fullscreen, then tried to go directly to "normal" state, + // this notification will say that it is "normal", but it will still look maximized, and + // if you called performZoom it would actually take it back to "normal". + // So we should say that it is maximized because it actually is. + if (newState == Qt::WindowNoState && m_platformWindow->m_effectivelyMaximized) + newState = Qt::WindowMaximized; QWindowSystemInterface::handleWindowStateChanged(m_window, newState); // We want to read the window state back from the window, // but the event we just sent may be asynchronous. @@ -346,6 +352,8 @@ static QTouchDevice *touchDevice = 0; - (void)notifyWindowWillZoom:(BOOL)willZoom { Qt::WindowState newState = willZoom ? Qt::WindowMaximized : Qt::WindowNoState; + if (!willZoom) + m_platformWindow->m_effectivelyMaximized = false; [self notifyWindowStateChanged:newState]; } diff --git a/src/plugins/platforms/cocoa/qnsviewaccessibility.mm b/src/plugins/platforms/cocoa/qnsviewaccessibility.mm index a438950a55..31e3e343b9 100644 --- a/src/plugins/platforms/cocoa/qnsviewaccessibility.mm +++ b/src/plugins/platforms/cocoa/qnsviewaccessibility.mm @@ -54,6 +54,15 @@ @implementation QNSView (QNSViewAccessibility) +- (id)childAccessibleElement { + if (!m_window->accessibleRoot()) + return nil; + + QAccessible::Id childId = QAccessible::uniqueId(m_window->accessibleRoot()); + QCocoaAccessibleElement *child = [QCocoaAccessibleElement createElementWithId: childId parent: self]; + return [child autorelease]; +} + // The QNSView is a container that the user does not interact directly with: // Remove it from the user-visible accessibility tree. - (BOOL)accessibilityIsIgnored { @@ -61,58 +70,22 @@ } - (id)accessibilityAttributeValue:(NSString *)attribute { - // activate accessibility updates QCocoaIntegration::instance()->accessibility()->setActive(true); - if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) { - if (m_window->accessibleRoot()) - return QCocoaAccessible::macRole(m_window->accessibleRoot()); - return NSAccessibilityUnknownRole; - } else if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) { - return NSAccessibilityRoleDescriptionForUIElement(self); - } else if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) { - if (!m_window->accessibleRoot()) - return [super accessibilityAttributeValue:attribute]; - return QCocoaAccessible::unignoredChildren(self, m_window->accessibleRoot()); + if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) { + return NSAccessibilityUnignoredChildrenForOnlyChild([self childAccessibleElement]); } else { return [super accessibilityAttributeValue:attribute]; } } - (id)accessibilityHitTest:(NSPoint)point { - if (!m_window->accessibleRoot()) - return [super accessibilityHitTest:point]; - - QAccessibleInterface *childInterface = m_window->accessibleRoot()->childAt(point.x, qt_mac_flipYCoordinate(point.y)); - // No child found, meaning we hit the NSView - if (!childInterface) { - return [super accessibilityHitTest:point]; - } - - // Hit a child, forward to child accessible interface. - QAccessible::Id childAxid = QAccessible::uniqueId(childInterface); - // FIXME: parent could be wrong - QCocoaAccessibleElement *accessibleElement = [QCocoaAccessibleElement createElementWithId:childAxid parent:self ]; - [accessibleElement autorelease]; - return [accessibleElement accessibilityHitTest:point]; + return [[self childAccessibleElement] accessibilityHitTest: point]; } - (id)accessibilityFocusedUIElement { - if (!m_window->accessibleRoot()) - return [super accessibilityFocusedUIElement]; - - QAccessibleInterface *childInterface = m_window->accessibleRoot()->focusChild(); - if (childInterface) { - QAccessible::Id childAxid = QAccessible::uniqueId(childInterface); - // FIXME: parent could be wrong - QCocoaAccessibleElement *accessibleElement = [QCocoaAccessibleElement createElementWithId:childAxid parent:self]; - [accessibleElement autorelease]; - return accessibleElement; - } - - // should not happen - return nil; + return [[self childAccessibleElement] accessibilityFocusedUIElement]; } @end diff --git a/src/plugins/platforms/cocoa/qnswindowdelegate.mm b/src/plugins/platforms/cocoa/qnswindowdelegate.mm index c9b3d69381..d9509098c6 100644 --- a/src/plugins/platforms/cocoa/qnswindowdelegate.mm +++ b/src/plugins/platforms/cocoa/qnswindowdelegate.mm @@ -75,6 +75,14 @@ } } +- (void)windowDidEndLiveResize:(NSNotification *)notification +{ + Q_UNUSED(notification); + if (m_cocoaWindow) { + m_cocoaWindow->windowDidEndLiveResize(); + } +} + - (void)windowWillMove:(NSNotification *)notification { Q_UNUSED(notification); diff --git a/src/plugins/platforms/eglfs/qeglfscontext.cpp b/src/plugins/platforms/eglfs/qeglfscontext.cpp index 9083abc562..4d443b91e3 100644 --- a/src/plugins/platforms/eglfs/qeglfscontext.cpp +++ b/src/plugins/platforms/eglfs/qeglfscontext.cpp @@ -53,8 +53,8 @@ QT_BEGIN_NAMESPACE QEglFSContext::QEglFSContext(const QSurfaceFormat &format, QPlatformOpenGLContext *share, EGLDisplay display) - : QEGLPlatformContext(QEglFSHooks::hooks()->surfaceFormatFor(format), share, display, - QEglFSIntegration::chooseConfig(display, QEglFSHooks::hooks()->surfaceFormatFor(format))) + : QEGLPlatformContext(format, share, display, + QEglFSIntegration::chooseConfig(display, format)) { } diff --git a/src/plugins/platforms/eglfs/qeglfshooks_stub.cpp b/src/plugins/platforms/eglfs/qeglfshooks_stub.cpp index dfb766db32..4aa3f29260 100644 --- a/src/plugins/platforms/eglfs/qeglfshooks_stub.cpp +++ b/src/plugins/platforms/eglfs/qeglfshooks_stub.cpp @@ -141,7 +141,16 @@ QImage::Format QEglFSHooks::screenFormat() const QSurfaceFormat QEglFSHooks::surfaceFormatFor(const QSurfaceFormat &inputFormat) const { - return inputFormat; + QSurfaceFormat format = inputFormat; + + static const bool force888 = qgetenv("QT_QPA_EGLFS_FORCE888").toInt(); + if (force888) { + format.setRedBufferSize(8); + format.setGreenBufferSize(8); + format.setBlueBufferSize(8); + } + + return format; } bool QEglFSHooks::filterConfig(EGLDisplay, EGLConfig) const diff --git a/src/plugins/platforms/ios/qioscontext.h b/src/plugins/platforms/ios/qioscontext.h index c48a0251a9..52357a5d58 100644 --- a/src/plugins/platforms/ios/qioscontext.h +++ b/src/plugins/platforms/ios/qioscontext.h @@ -85,10 +85,12 @@ private: GLuint depthRenderbuffer; GLint renderbufferWidth; GLint renderbufferHeight; + bool isComplete; }; static void deleteBuffers(const FramebufferObject &framebufferObject); + FramebufferObject &backingFramebufferObjectFor(QPlatformSurface *) const; mutable QHash<QIOSWindow *, FramebufferObject> m_framebufferObjects; }; diff --git a/src/plugins/platforms/ios/qioscontext.mm b/src/plugins/platforms/ios/qioscontext.mm index 7310d2904f..ddee52196a 100644 --- a/src/plugins/platforms/ios/qioscontext.mm +++ b/src/plugins/platforms/ios/qioscontext.mm @@ -94,14 +94,38 @@ QSurfaceFormat QIOSContext::format() const return m_format; } +#define QT_IOS_GL_STATUS_CASE(val) case val: return QLatin1Literal(#val) + +static QString fboStatusString(GLenum status) +{ + switch (status) { + QT_IOS_GL_STATUS_CASE(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT); + QT_IOS_GL_STATUS_CASE(GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS); + QT_IOS_GL_STATUS_CASE(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT); + QT_IOS_GL_STATUS_CASE(GL_FRAMEBUFFER_UNSUPPORTED); + default: + return QString(QStringLiteral("unknown status: %x")).arg(status); + } +} + bool QIOSContext::makeCurrent(QPlatformSurface *surface) { Q_ASSERT(surface && surface->surface()->surfaceType() == QSurface::OpenGLSurface); [EAGLContext setCurrentContext:m_eaglContext]; - glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject(surface)); - return true; + // For offscreen surfaces we don't prepare a default FBO + if (surface->surface()->surfaceClass() == QSurface::Offscreen) + return true; + + FramebufferObject &framebufferObject = backingFramebufferObjectFor(surface); + + // We bind the default FBO even if it's incomplete, so that clients who + // call glCheckFramebufferStatus as a result of this function returning + // false will get a matching error code. + glBindFramebuffer(GL_FRAMEBUFFER, framebufferObject.handle); + + return framebufferObject.isComplete; } void QIOSContext::doneCurrent() @@ -112,17 +136,26 @@ void QIOSContext::doneCurrent() void QIOSContext::swapBuffers(QPlatformSurface *surface) { Q_ASSERT(surface && surface->surface()->surfaceType() == QSurface::OpenGLSurface); - Q_ASSERT(surface->surface()->surfaceClass() == QSurface::Window); - QIOSWindow *window = static_cast<QIOSWindow *>(surface); - Q_ASSERT(m_framebufferObjects.contains(window)); + + if (surface->surface()->surfaceClass() == QSurface::Offscreen) + return; // Nothing to do + + FramebufferObject &framebufferObject = backingFramebufferObjectFor(surface); [EAGLContext setCurrentContext:m_eaglContext]; - glBindRenderbuffer(GL_RENDERBUFFER, m_framebufferObjects[window].colorRenderbuffer); + glBindRenderbuffer(GL_RENDERBUFFER, framebufferObject.colorRenderbuffer); [m_eaglContext presentRenderbuffer:GL_RENDERBUFFER]; } -GLuint QIOSContext::defaultFramebufferObject(QPlatformSurface *surface) const +QIOSContext::FramebufferObject &QIOSContext::backingFramebufferObjectFor(QPlatformSurface *surface) const { + // We keep track of default-FBOs in the root context of a share-group. This assumes + // that the contexts form a tree, where leaf nodes are always destroyed before their + // parents. If that assumption (based on the current implementation) doesn't hold we + // should probably use QOpenGLMultiGroupSharedResource to track the shared default-FBOs. + if (m_sharedContext) + return m_sharedContext->backingFramebufferObjectFor(surface); + Q_ASSERT(surface && surface->surface()->surfaceClass() == QSurface::Window); QIOSWindow *window = static_cast<QIOSWindow *>(surface); @@ -181,11 +214,27 @@ GLuint QIOSContext::defaultFramebufferObject(QPlatformSurface *surface) const framebufferObject.renderbufferWidth, framebufferObject.renderbufferHeight); } - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) - NSLog(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER)); + framebufferObject.isComplete = glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE; + + if (!framebufferObject.isComplete) { + qWarning("QIOSContext failed to make complete framebuffer object (%s)", + qPrintable(fboStatusString(glCheckFramebufferStatus(GL_FRAMEBUFFER)))); + } + } + + return framebufferObject; +} + +GLuint QIOSContext::defaultFramebufferObject(QPlatformSurface *surface) const +{ + if (surface->surface()->surfaceClass() == QSurface::Offscreen) { + // Binding and rendering to the zero-FBO on iOS seems to be + // no-ops, so we can safely return 0 here, even if it's not + // really a valid FBO on iOS. + return 0; } - return framebufferObject.handle; + return backingFramebufferObjectFor(surface).handle; } void QIOSContext::windowDestroyed(QObject *object) diff --git a/src/plugins/platforms/ios/qiosinputcontext.mm b/src/plugins/platforms/ios/qiosinputcontext.mm index 13a0b46745..9a2c55f7f2 100644 --- a/src/plugins/platforms/ios/qiosinputcontext.mm +++ b/src/plugins/platforms/ios/qiosinputcontext.mm @@ -47,6 +47,7 @@ #include "qioswindow.h" #include "quiview.h" #include <QGuiApplication> +#include <QtGui/private/qwindow_p.h> @interface QIOSKeyboardListener : UIGestureRecognizer { @public @@ -54,6 +55,8 @@ BOOL m_keyboardVisible; BOOL m_keyboardVisibleAndDocked; BOOL m_ignoreKeyboardChanges; + BOOL m_touchPressWhileKeyboardVisible; + BOOL m_keyboardHiddenByGesture; QRectF m_keyboardRect; QRectF m_keyboardEndRect; NSTimeInterval m_duration; @@ -72,6 +75,8 @@ m_keyboardVisible = NO; m_keyboardVisibleAndDocked = NO; m_ignoreKeyboardChanges = NO; + m_touchPressWhileKeyboardVisible = NO; + m_keyboardHiddenByGesture = NO; m_duration = 0; m_curve = UIViewAnimationCurveEaseOut; m_viewController = 0; @@ -145,9 +150,6 @@ - (void) keyboardDidChangeFrame:(NSNotification *)notification { Q_UNUSED(notification); - if (m_ignoreKeyboardChanges) - return; - [self handleKeyboardRectChanged]; // If the keyboard was visible and docked from before, this is just a geometry @@ -178,7 +180,11 @@ // Note that UIKeyboardWillHideNotification is also sendt when the keyboard is undocked. m_keyboardVisibleAndDocked = NO; m_keyboardEndRect = [self getKeyboardRect:notification]; - self.enabled = NO; + if (!m_keyboardHiddenByGesture) { + // Only disable the gesture if the hiding of the keyboard was not caused by it. + // Otherwise we need to await the final touchEnd callback for doing some clean-up. + self.enabled = NO; + } m_context->scroll(0); } @@ -201,12 +207,45 @@ - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { QPointF p = fromCGPoint([[touches anyObject] locationInView:m_viewController.view]); - if (m_keyboardRect.contains(p)) + if (m_keyboardRect.contains(p)) { + m_keyboardHiddenByGesture = YES; m_context->hideInputPanel(); + } [super touchesMoved:touches withEvent:event]; } +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event +{ + Q_ASSERT(m_keyboardVisibleAndDocked); + m_touchPressWhileKeyboardVisible = YES; + [super touchesBegan:touches withEvent:event]; +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event +{ + m_touchPressWhileKeyboardVisible = NO; + [self performSelectorOnMainThread:@selector(touchesEndedPostDelivery) withObject:nil waitUntilDone:NO]; + [super touchesEnded:touches withEvent:event]; +} + +- (void)touchesEndedPostDelivery +{ + // Do some clean-up _after_ touchEnd has been delivered to QUIView + m_keyboardHiddenByGesture = NO; + if (!m_keyboardVisibleAndDocked) { + self.enabled = NO; + if (qApp->focusObject()) { + // UI Controls are told to gain focus on touch release. So when the 'hide keyboard' gesture + // finishes, the final touch end can trigger a control to gain focus. This is in conflict with + // the gesture, so we clear focus once more as a work-around. + static_cast<QWindowPrivate *>(QObjectPrivate::get(qApp->focusWindow()))->clearFocusObject(); + } + } else { + m_context->scrollToCursor(); + } +} + @end QIOSInputContext::QIOSInputContext() @@ -233,6 +272,12 @@ QRectF QIOSInputContext::keyboardRect() const void QIOSInputContext::showInputPanel() { + if (m_keyboardListener->m_keyboardHiddenByGesture) { + // We refuse to re-show the keyboard until the touch + // sequence that triggered the gesture has ended. + return; + } + // Documentation tells that one should call (and recall, if necessary) becomeFirstResponder/resignFirstResponder // to show/hide the keyboard. This is slightly inconvenient, since there exist no API to get the current first // responder. Rather than searching for it from the top, we let the active QIOSWindow tell us which view to use. @@ -306,6 +351,13 @@ void QIOSInputContext::scrollToCursor() if (!isQtApplication() || !m_focusView) return; + if (m_keyboardListener->m_touchPressWhileKeyboardVisible) { + // Don't scroll to the cursor if the user is touching the screen. This + // interferes with selection and the 'hide keyboard' gesture. Instead + // we update scrolling upon touchEnd. + return; + } + UIView *view = m_keyboardListener->m_viewController.view; if (view.window != m_focusView.window) return; @@ -332,7 +384,7 @@ void QIOSInputContext::scroll(int y) newBounds.origin.y = y; QPointer<QIOSInputContext> self = this; [UIView animateWithDuration:m_keyboardListener->m_duration delay:0 - options:m_keyboardListener->m_curve + options:m_keyboardListener->m_curve | UIViewAnimationOptionBeginFromCurrentState animations:^{ view.bounds = newBounds; } completion:^(BOOL){ if (self) diff --git a/src/plugins/platforms/ios/qiosintegration.h b/src/plugins/platforms/ios/qiosintegration.h index a28926ff99..956c112399 100644 --- a/src/plugins/platforms/ios/qiosintegration.h +++ b/src/plugins/platforms/ios/qiosintegration.h @@ -64,6 +64,7 @@ public: QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const; QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const; + QPlatformOffscreenSurface *createPlatformOffscreenSurface(QOffscreenSurface *surface) const Q_DECL_OVERRIDE; QPlatformFontDatabase *fontDatabase() const; QPlatformClipboard *clipboard() const; diff --git a/src/plugins/platforms/ios/qiosintegration.mm b/src/plugins/platforms/ios/qiosintegration.mm index 7a40e349c9..0fe7adff9f 100644 --- a/src/plugins/platforms/ios/qiosintegration.mm +++ b/src/plugins/platforms/ios/qiosintegration.mm @@ -51,6 +51,8 @@ #include "qiostheme.h" #include "qiosservices.h" +#include <qpa/qplatformoffscreensurface.h> + #include <QtPlatformSupport/private/qcoretextfontdatabase_p.h> #include <QtPlatformSupport/private/qmacmime_p.h> #include <QDir> @@ -144,6 +146,11 @@ QPlatformOpenGLContext *QIOSIntegration::createPlatformOpenGLContext(QOpenGLCont return new QIOSContext(context); } +QPlatformOffscreenSurface *QIOSIntegration::createPlatformOffscreenSurface(QOffscreenSurface *surface) const +{ + return new QPlatformOffscreenSurface(surface); +} + QAbstractEventDispatcher *QIOSIntegration::createEventDispatcher() const { if (isQtApplication()) diff --git a/src/plugins/platforms/linuxfb/qlinuxfbscreen.cpp b/src/plugins/platforms/linuxfb/qlinuxfbscreen.cpp index aca8d76041..72d5833e73 100644 --- a/src/plugins/platforms/linuxfb/qlinuxfbscreen.cpp +++ b/src/plugins/platforms/linuxfb/qlinuxfbscreen.cpp @@ -360,7 +360,7 @@ bool QLinuxFbScreen::initialize() // Open the device mFbFd = openFramebufferDevice(fbDevice); if (mFbFd == -1) { - qWarning("Failed to open framebuffer %s : %s", qPrintable(fbDevice), strerror(errno)); + qErrnoWarning(errno, "Failed to open framebuffer %s", qPrintable(fbDevice)); return false; } @@ -371,12 +371,12 @@ bool QLinuxFbScreen::initialize() memset(&finfo, 0, sizeof(finfo)); if (ioctl(mFbFd, FBIOGET_FSCREENINFO, &finfo) != 0) { - qWarning("Error reading fixed information: %s", strerror(errno)); + qErrnoWarning(errno, "Error reading fixed information"); return false; } if (ioctl(mFbFd, FBIOGET_VSCREENINFO, &vinfo)) { - qWarning("Error reading variable information: %s", strerror(errno)); + qErrnoWarning(errno, "Error reading variable information"); return false; } @@ -391,7 +391,7 @@ bool QLinuxFbScreen::initialize() mMmap.size = finfo.smem_len; uchar *data = (unsigned char *)mmap(0, mMmap.size, PROT_READ | PROT_WRITE, MAP_SHARED, mFbFd, 0); if ((long)data == -1) { - qWarning("Failed to mmap framebuffer: %s", strerror(errno)); + qErrnoWarning(errno, "Failed to mmap framebuffer"); return false; } @@ -420,10 +420,12 @@ bool QLinuxFbScreen::initialize() mTtyFd = openTtyDevice(ttyDevice); if (mTtyFd == -1) - qWarning() << "Failed to open tty" << strerror(errno); + qErrnoWarning(errno, "Failed to open tty"); - if (doSwitchToGraphicsMode && !switchToGraphicsMode(mTtyFd, &mOldTtyMode)) - qWarning() << "Failed to set graphics mode" << strerror(errno); + if (doSwitchToGraphicsMode) + switchToGraphicsMode(mTtyFd, &mOldTtyMode); + // Do not warn if the switch fails: the ioctl fails when launching from + // a remote console and there is nothing we can do about it. blankScreen(mFbFd, false); diff --git a/src/plugins/platforms/qnx/qqnxfilepicker.cpp b/src/plugins/platforms/qnx/qqnxfilepicker.cpp index 830b110f2a..08119ce0ae 100644 --- a/src/plugins/platforms/qnx/qqnxfilepicker.cpp +++ b/src/plugins/platforms/qnx/qqnxfilepicker.cpp @@ -130,9 +130,9 @@ void QQnxFilePicker::open() map[QStringLiteral("AllowOverwrite")] = false; if (!m_defaultSaveFileNames.isEmpty()) - map[QStringLiteral("DefaultFileNames")] = m_defaultSaveFileNames.join(","); + map[QStringLiteral("DefaultFileNames")] = m_defaultSaveFileNames.join(QLatin1Char(',')); if (!m_filters.isEmpty()) - map[QStringLiteral("Filter")] = m_filters.join(";"); + map[QStringLiteral("Filter")] = m_filters.join(QLatin1Char(';')); QByteArray ppsData; #if defined(Q_OS_BLACKBERRY_TABLET) @@ -288,8 +288,8 @@ QString QQnxFilePicker::filePickerType() const bool video = false; bool music = false; QMimeDatabase mimeDb; - for (int i = 0; i < filters().count(); i++) { - QList<QMimeType> mimeTypes = mimeDb.mimeTypesForFileName(filters().at(i)); + for (int i = 0; i < m_filters.count(); i++) { + QList<QMimeType> mimeTypes = mimeDb.mimeTypesForFileName(m_filters.at(i)); if (mimeTypes.isEmpty()) return QStringLiteral("Other"); diff --git a/src/plugins/platforms/qnx/qqnxscreen.cpp b/src/plugins/platforms/qnx/qqnxscreen.cpp index a6c69164c7..2707f14db2 100644 --- a/src/plugins/platforms/qnx/qqnxscreen.cpp +++ b/src/plugins/platforms/qnx/qqnxscreen.cpp @@ -177,6 +177,11 @@ QQnxScreen::QQnxScreen(screen_context_t screenContext, screen_display_t display, m_currentGeometry = m_initialGeometry = QRect(0, 0, val[0], val[1]); + char name[100]; + Q_SCREEN_CHECKERROR(screen_get_display_property_cv(m_display, SCREEN_PROPERTY_ID_STRING, 100, + name), "Failed to query display name"); + m_name = QString::fromUtf8(name); + // Cache size of this display in millimeters. We have to take care of the orientation. // libscreen always reports the physical size dimensions as width and height in the // native orientation. Contrary to this, QPlatformScreen::physicalSize() expects the diff --git a/src/plugins/platforms/qnx/qqnxscreen.h b/src/plugins/platforms/qnx/qqnxscreen.h index d39a210d4b..a0b760135f 100644 --- a/src/plugins/platforms/qnx/qqnxscreen.h +++ b/src/plugins/platforms/qnx/qqnxscreen.h @@ -77,6 +77,8 @@ public: int rotation() const { return m_currentRotation; } + QString name() const { return m_name; } + int nativeFormat() const { return (depth() == 32) ? SCREEN_FORMAT_RGBA8888 : SCREEN_FORMAT_RGB565; } screen_display_t nativeDisplay() const { return m_display; } screen_context_t nativeContext() const { return m_screenContext; } @@ -132,6 +134,7 @@ private: int m_initialRotation; int m_currentRotation; int m_keyboardHeight; + QString m_name; QSize m_initialPhysicalSize; QSize m_currentPhysicalSize; Qt::ScreenOrientation m_nativeOrientation; diff --git a/src/plugins/platforms/qnx/qqnxwindow.cpp b/src/plugins/platforms/qnx/qqnxwindow.cpp index f11a009bca..42318729b1 100644 --- a/src/plugins/platforms/qnx/qqnxwindow.cpp +++ b/src/plugins/platforms/qnx/qqnxwindow.cpp @@ -709,12 +709,12 @@ void QQnxWindow::initWindow() if (window()->parent() && window()->parent()->handle()) setParent(window()->parent()->handle()); - if (shouldMakeFullScreen()) { + if (shouldMakeFullScreen()) setGeometryHelper(screen()->geometry()); - QWindowSystemInterface::handleGeometryChange(window(), screen()->geometry()); - } else { + else setGeometryHelper(window()->geometry()); - } + + QWindowSystemInterface::handleGeometryChange(window(), screen()->geometry()); } void QQnxWindow::createWindowGroup() diff --git a/src/plugins/platforms/windows/qwindowsfontdatabase.cpp b/src/plugins/platforms/windows/qwindowsfontdatabase.cpp index 8f9ddc2168..1432dfdcd9 100644 --- a/src/plugins/platforms/windows/qwindowsfontdatabase.cpp +++ b/src/plugins/platforms/windows/qwindowsfontdatabase.cpp @@ -1040,8 +1040,7 @@ QWindowsFontDatabase::~QWindowsFontDatabase() QFontEngineMulti *QWindowsFontDatabase::fontEngineMulti(QFontEngine *fontEngine, QChar::Script script) { - Q_UNUSED(script) - return new QWindowsMultiFontEngine(fontEngine, QStringList()); + return new QWindowsMultiFontEngine(fontEngine, script); } QFontEngine * QWindowsFontDatabase::fontEngine(const QFontDef &fontDef, void *handle) diff --git a/src/plugins/platforms/windows/qwindowsfontengine.cpp b/src/plugins/platforms/windows/qwindowsfontengine.cpp index c3774064e3..4f3a007bd7 100644 --- a/src/plugins/platforms/windows/qwindowsfontengine.cpp +++ b/src/plugins/platforms/windows/qwindowsfontengine.cpp @@ -1278,35 +1278,14 @@ void QWindowsFontEngine::initFontInfo(const QFontDef &request, Will probably be superseded by a common Free Type font engine in Qt 5.X. */ - -QWindowsMultiFontEngine::QWindowsMultiFontEngine(QFontEngine *first, const QStringList &fallbacks) - : QFontEngineMulti(fallbacks.size() + 1), - fallbackFamilies(fallbacks) -{ - engines[0] = first; - first->ref.ref(); - fontDef = engines[0]->fontDef; - cache_cost = first->cache_cost; -} - -void QWindowsMultiFontEngine::setFallbackFamiliesList(const QStringList &fallbacks) +QWindowsMultiFontEngine::QWindowsMultiFontEngine(QFontEngine *fe, int script) + : QFontEngineMultiQPA(fe, script) { - // Original FontEngine to restore after the fill. - QFontEngine *fe = engines[0]; - fallbackFamilies = fallbacks; - if (!fallbackFamilies.isEmpty()) { - engines.fill(0, fallbackFamilies.size() + 1); - engines[0] = fe; - } else { - // Turns out we lied about having any fallback at all. - fallbackFamilies << fe->fontDef.family; - engines[1] = fe; - fe->ref.ref(); - } } void QWindowsMultiFontEngine::loadEngine(int at) { + ensureFallbackFamiliesQueried(); Q_ASSERT(at < engines.size()); Q_ASSERT(engines.at(at) == 0); @@ -1329,7 +1308,7 @@ void QWindowsMultiFontEngine::loadEngine(int at) data = fe->fontEngineData(); } - const QString fam = fallbackFamilies.at(at-1); + const QString fam = fallbackFamilyAt(at - 1); memcpy(lf.lfFaceName, fam.utf16(), sizeof(wchar_t) * qMin(fam.length() + 1, 32)); // 32 = Windows hard-coded #ifndef QT_NO_DIRECTWRITE diff --git a/src/plugins/platforms/windows/qwindowsfontengine.h b/src/plugins/platforms/windows/qwindowsfontengine.h index 7d93484220..7a0803830c 100644 --- a/src/plugins/platforms/windows/qwindowsfontengine.h +++ b/src/plugins/platforms/windows/qwindowsfontengine.h @@ -53,7 +53,7 @@ // We mean it. // -#include <QtGui/private/qfontengine_p.h> +#include <QtGui/private/qfontengine_qpa_p.h> #include <QtGui/QImage> #include <QtCore/QSharedPointer> @@ -166,15 +166,13 @@ private: mutable int designAdvancesSize; }; -class QWindowsMultiFontEngine : public QFontEngineMulti + +class QWindowsMultiFontEngine : public QFontEngineMultiQPA { public: - QWindowsMultiFontEngine(QFontEngine *first, const QStringList &fallbacks); + explicit QWindowsMultiFontEngine(QFontEngine *fe, int script); - void setFallbackFamiliesList(const QStringList &fallbacks); void loadEngine(int at); - - QStringList fallbackFamilies; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index 274366d4fe..00229a7540 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -1365,9 +1365,10 @@ void QWindowsWindow::handleResized(int wParam) handleGeometryChange(); break; case SIZE_RESTORED: - bool fullScreen = isFullScreen_sys(); - if ((m_windowState != Qt::WindowNoState) || fullScreen) - handleWindowStateChange(fullScreen ? Qt::WindowFullScreen : Qt::WindowNoState); + if (isFullScreen_sys()) + handleWindowStateChange(Qt::WindowFullScreen); + else if (m_windowState != Qt::WindowNoState && !testFlag(MaximizeToFullScreen)) + handleWindowStateChange(Qt::WindowNoState); handleGeometryChange(); break; } @@ -1623,8 +1624,11 @@ void QWindowsWindow::setWindowState_sys(Qt::WindowState newState) if ((oldState == Qt::WindowMaximized) != (newState == Qt::WindowMaximized)) { if (visible && !(newState == Qt::WindowMinimized)) { setFlag(WithinMaximize); + if (newState == Qt::WindowFullScreen) + setFlag(MaximizeToFullScreen); ShowWindow(m_data.hwnd, (newState == Qt::WindowMaximized) ? SW_MAXIMIZE : SW_SHOWNOACTIVATE); clearFlag(WithinMaximize); + clearFlag(MaximizeToFullScreen); } } diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h index ba0f22bb0a..cb437b76d0 100644 --- a/src/plugins/platforms/windows/qwindowswindow.h +++ b/src/plugins/platforms/windows/qwindowswindow.h @@ -153,7 +153,8 @@ public: AlertState = 0x8000, Exposed = 0x10000, WithinCreate = 0x20000, - WithinMaximize = 0x40000 + WithinMaximize = 0x40000, + MaximizeToFullScreen = 0x80000 }; QWindowsWindow(QWindow *window, const QWindowsWindowData &data); diff --git a/src/plugins/platforms/winrt/qwinrtfontdatabase.cpp b/src/plugins/platforms/winrt/qwinrtfontdatabase.cpp index 3da87de708..70bb9469db 100644 --- a/src/plugins/platforms/winrt/qwinrtfontdatabase.cpp +++ b/src/plugins/platforms/winrt/qwinrtfontdatabase.cpp @@ -63,7 +63,7 @@ QString QWinRTFontDatabase::fontDir() const fontDirectory = applicationDirPath + QLatin1String("/fonts"); if (!QFile::exists(fontDirectory)) { #ifndef Q_OS_WINPHONE - if (m_fonts.isEmpty()) + if (m_fontFamilies.isEmpty()) #endif qWarning("No fonts directory found in application package."); fontDirectory = applicationDirPath; @@ -78,6 +78,9 @@ QWinRTFontDatabase::~QWinRTFontDatabase() { foreach (IDWriteFontFile *fontFile, m_fonts.keys()) fontFile->Release(); + + foreach (IDWriteFontFamily *fontFamily, m_fontFamilies) + fontFamily->Release(); } QFont QWinRTFontDatabase::defaultFont() const @@ -132,175 +135,196 @@ void QWinRTFontDatabase::populateFontDatabase() } QString familyName = QString::fromWCharArray(familyBuffer.data(), familyNameLength); - int fontCount = fontFamily->GetFontCount(); - for (int j = 0; j < fontCount; ++j) { - ComPtr<IDWriteFont> font; - hr = fontFamily->GetFont(j, &font); - if (FAILED(hr)) { - qWarning("Unable to get base font: %s", qPrintable(qt_error_string(hr))); - continue; - } + m_fontFamilies.insert(familyName, fontFamily.Detach()); - ComPtr<IDWriteFontFace> baseFontFace; - hr = font->CreateFontFace(&baseFontFace); - if (FAILED(hr)) { - qWarning("Unable to create base font face: %s", qPrintable(qt_error_string(hr))); - continue; - } - ComPtr<IDWriteFontFace1> fontFace; - hr = baseFontFace.As(&fontFace); - if (FAILED(hr)) { - qWarning("Unable to create font face: %s", qPrintable(qt_error_string(hr))); - continue; - } + registerFontFamily(familyName); + } - // Only try to load true-type fonts - DWRITE_FONT_FACE_TYPE type = fontFace->GetType(); - if (!(type == DWRITE_FONT_FACE_TYPE_TRUETYPE - || type == DWRITE_FONT_FACE_TYPE_TRUETYPE_COLLECTION)) { - continue; - } + QBasicFontDatabase::populateFontDatabase(); +} - // We can't deal with multi-file fonts - quint32 fileCount; - hr = fontFace->GetFiles(&fileCount, NULL); - if (FAILED(hr)) { - qWarning("Unable to get font file count: %s", qPrintable(qt_error_string(hr))); - continue; - } - if (fileCount != 1) // Should not happen as we only look at TT fonts - continue; - - ComPtr<IDWriteLocalizedStrings> informationalStrings; - BOOL exists; - hr = font->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_MANUFACTURER, - &informationalStrings, &exists); - if (FAILED(hr)) { - qWarning("Unable to get font foundry: %s", qPrintable(qt_error_string(hr))); - continue; - } - QString foundryName; - if (exists) { - quint32 length; - hr = informationalStrings->GetStringLength(0, &length); +void QWinRTFontDatabase::populateFamily(const QString &familyName) +{ + IDWriteFontFamily *fontFamily = m_fontFamilies.value(familyName); + if (!fontFamily) { + qWarning("The font family %s was not found.", qPrintable(familyName)); + return; + } + + bool fontRegistered = false; + const int fontCount = fontFamily->GetFontCount(); + for (int j = 0; j < fontCount; ++j) { + ComPtr<IDWriteFont> font; + HRESULT hr = fontFamily->GetFont(j, &font); + if (FAILED(hr)) { + qWarning("Unable to get font: %s", qPrintable(qt_error_string(hr))); + continue; + } + + // Skip simulated faces + if (font->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) + continue; + + ComPtr<IDWriteFontFace> baseFontFace; + hr = font->CreateFontFace(&baseFontFace); + if (FAILED(hr)) { + qWarning("Unable to create base font face: %s", qPrintable(qt_error_string(hr))); + continue; + } + ComPtr<IDWriteFontFace1> fontFace; + hr = baseFontFace.As(&fontFace); + if (FAILED(hr)) { + qWarning("Unable to create font face: %s", qPrintable(qt_error_string(hr))); + continue; + } + + // We can't deal with multi-file fonts + quint32 fileCount; + hr = fontFace->GetFiles(&fileCount, NULL); + if (FAILED(hr)) { + qWarning("Unable to get font file count: %s", qPrintable(qt_error_string(hr))); + continue; + } + if (fileCount != 1) + continue; + + ComPtr<IDWriteLocalizedStrings> informationalStrings; + BOOL exists; + hr = font->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_MANUFACTURER, + &informationalStrings, &exists); + if (FAILED(hr)) { + qWarning("Unable to get font foundry: %s", qPrintable(qt_error_string(hr))); + continue; + } + QString foundryName; + if (exists) { + quint32 length; + hr = informationalStrings->GetStringLength(0, &length); + if (FAILED(hr)) + qWarning("Unable to get foundry name length: %s", qPrintable(qt_error_string(hr))); + if (SUCCEEDED(hr)) { + QVector<wchar_t> buffer(length + 1); + hr = informationalStrings->GetString(0, buffer.data(), buffer.size()); if (FAILED(hr)) - qWarning("Unable to get foundry name length: %s", qPrintable(qt_error_string(hr))); - if (SUCCEEDED(hr)) { - QVector<wchar_t> buffer(length + 1); - hr = informationalStrings->GetString(0, buffer.data(), buffer.size()); - if (FAILED(hr)) - qWarning("Unable to get foundry name: %s", qPrintable(qt_error_string(hr))); - if (SUCCEEDED(hr)) - foundryName = QString::fromWCharArray(buffer.data(), length); - } + qWarning("Unable to get foundry name: %s", qPrintable(qt_error_string(hr))); + if (SUCCEEDED(hr)) + foundryName = QString::fromWCharArray(buffer.data(), length); } + } - QFont::Weight weight; - switch (font->GetWeight()) { - case DWRITE_FONT_WEIGHT_THIN: - case DWRITE_FONT_WEIGHT_EXTRA_LIGHT: - case DWRITE_FONT_WEIGHT_LIGHT: - case DWRITE_FONT_WEIGHT_SEMI_LIGHT: - weight = QFont::Light; - break; - default: - case DWRITE_FONT_WEIGHT_NORMAL: - case DWRITE_FONT_WEIGHT_MEDIUM: - weight = QFont::Normal; - break; - case DWRITE_FONT_WEIGHT_DEMI_BOLD: - weight = QFont::DemiBold; - break; - case DWRITE_FONT_WEIGHT_BOLD: - case DWRITE_FONT_WEIGHT_EXTRA_BOLD: - weight = QFont::Bold; - break; - case DWRITE_FONT_WEIGHT_BLACK: - case DWRITE_FONT_WEIGHT_EXTRA_BLACK: - weight = QFont::Black; - break; - } + QFont::Weight weight; + switch (font->GetWeight()) { + case DWRITE_FONT_WEIGHT_THIN: + case DWRITE_FONT_WEIGHT_EXTRA_LIGHT: + case DWRITE_FONT_WEIGHT_LIGHT: + case DWRITE_FONT_WEIGHT_SEMI_LIGHT: + weight = QFont::Light; + break; + default: + case DWRITE_FONT_WEIGHT_NORMAL: + case DWRITE_FONT_WEIGHT_MEDIUM: + weight = QFont::Normal; + break; + case DWRITE_FONT_WEIGHT_DEMI_BOLD: + weight = QFont::DemiBold; + break; + case DWRITE_FONT_WEIGHT_BOLD: + case DWRITE_FONT_WEIGHT_EXTRA_BOLD: + weight = QFont::Bold; + break; + case DWRITE_FONT_WEIGHT_BLACK: + case DWRITE_FONT_WEIGHT_EXTRA_BLACK: + weight = QFont::Black; + break; + } - QFont::Style style; - switch (font->GetStyle()) { - default: - case DWRITE_FONT_STYLE_NORMAL: - style = QFont::StyleNormal; - break; - case DWRITE_FONT_STYLE_OBLIQUE: - style = QFont::StyleOblique; - break; - case DWRITE_FONT_STYLE_ITALIC: - style = QFont::StyleItalic; - break; - } + QFont::Style style; + switch (font->GetStyle()) { + default: + case DWRITE_FONT_STYLE_NORMAL: + style = QFont::StyleNormal; + break; + case DWRITE_FONT_STYLE_OBLIQUE: + style = QFont::StyleOblique; + break; + case DWRITE_FONT_STYLE_ITALIC: + style = QFont::StyleItalic; + break; + } - QFont::Stretch stretch; - switch (font->GetStretch()) { - default: - case DWRITE_FONT_STRETCH_UNDEFINED: - case DWRITE_FONT_STRETCH_NORMAL: - stretch = QFont::Unstretched; - break; - case DWRITE_FONT_STRETCH_ULTRA_CONDENSED: - stretch = QFont::UltraCondensed; - break; - case DWRITE_FONT_STRETCH_EXTRA_CONDENSED: - stretch = QFont::ExtraCondensed; - break; - case DWRITE_FONT_STRETCH_CONDENSED: - stretch = QFont::Condensed; - break; - case DWRITE_FONT_STRETCH_SEMI_CONDENSED: - stretch = QFont::SemiCondensed; - break; - case DWRITE_FONT_STRETCH_SEMI_EXPANDED: - stretch = QFont::SemiExpanded; - break; - case DWRITE_FONT_STRETCH_EXPANDED: - stretch = QFont::Expanded; - break; - case DWRITE_FONT_STRETCH_EXTRA_EXPANDED: - stretch = QFont::ExtraExpanded; - break; - case DWRITE_FONT_STRETCH_ULTRA_EXPANDED: - stretch = QFont::UltraExpanded; - break; - } + QFont::Stretch stretch; + switch (font->GetStretch()) { + default: + case DWRITE_FONT_STRETCH_UNDEFINED: + case DWRITE_FONT_STRETCH_NORMAL: + stretch = QFont::Unstretched; + break; + case DWRITE_FONT_STRETCH_ULTRA_CONDENSED: + stretch = QFont::UltraCondensed; + break; + case DWRITE_FONT_STRETCH_EXTRA_CONDENSED: + stretch = QFont::ExtraCondensed; + break; + case DWRITE_FONT_STRETCH_CONDENSED: + stretch = QFont::Condensed; + break; + case DWRITE_FONT_STRETCH_SEMI_CONDENSED: + stretch = QFont::SemiCondensed; + break; + case DWRITE_FONT_STRETCH_SEMI_EXPANDED: + stretch = QFont::SemiExpanded; + break; + case DWRITE_FONT_STRETCH_EXPANDED: + stretch = QFont::Expanded; + break; + case DWRITE_FONT_STRETCH_EXTRA_EXPANDED: + stretch = QFont::ExtraExpanded; + break; + case DWRITE_FONT_STRETCH_ULTRA_EXPANDED: + stretch = QFont::UltraExpanded; + break; + } - const bool fixedPitch = fontFace->IsMonospacedFont(); + const bool fixedPitch = fontFace->IsMonospacedFont(); - quint32 unicodeRange[4]; - quint32 actualRangeCount; - hr = fontFace->GetUnicodeRanges( - 2, reinterpret_cast<DWRITE_UNICODE_RANGE *>(unicodeRange), &actualRangeCount); - if (FAILED(hr) && hr != E_NOT_SUFFICIENT_BUFFER) { // Ignore insufficient buffer; we only need 4 indices - qWarning("Unable to get font unicode range: %s", qPrintable(qt_error_string(hr))); - continue; - } - quint32 codePageRange[2] = { 0, 0 }; - QSupportedWritingSystems writingSystems = - QPlatformFontDatabase::writingSystemsFromTrueTypeBits(unicodeRange, codePageRange); - - IDWriteFontFile *fontFile; - hr = fontFace->GetFiles(&fileCount, &fontFile); - if (FAILED(hr)) { - qWarning("Unable to get font file: %s", qPrintable(qt_error_string(hr))); - continue; - } + quint32 unicodeRange[4]; + quint32 actualRangeCount; + hr = fontFace->GetUnicodeRanges( + 2, reinterpret_cast<DWRITE_UNICODE_RANGE *>(unicodeRange), &actualRangeCount); + if (FAILED(hr) && hr != E_NOT_SUFFICIENT_BUFFER) { // Ignore insufficient buffer; we only need 4 indices + qWarning("Unable to get font unicode range: %s", qPrintable(qt_error_string(hr))); + continue; + } + quint32 codePageRange[2] = { 0, 0 }; + QSupportedWritingSystems writingSystems = + QPlatformFontDatabase::writingSystemsFromTrueTypeBits(unicodeRange, codePageRange); - FontDescription description = { fontFace->GetIndex(), QUuid::createUuid().toByteArray() }; - m_fonts.insert(fontFile, description); - registerFont(familyName, QString(), foundryName, weight, style, stretch, - true, true, 0, fixedPitch, writingSystems, fontFile); + IDWriteFontFile *fontFile; + hr = fontFace->GetFiles(&fileCount, &fontFile); + if (FAILED(hr)) { + qWarning("Unable to get font file: %s", qPrintable(qt_error_string(hr))); + continue; } + + FontDescription description = { fontFace->GetIndex(), QUuid::createUuid().toByteArray() }; + m_fonts.insert(fontFile, description); + registerFont(familyName, QString(), foundryName, weight, style, stretch, + true, true, 0, fixedPitch, writingSystems, fontFile); + fontRegistered = true; } - QBasicFontDatabase::populateFontDatabase(); + // Always populate something to avoid an assert + if (!fontRegistered) { + registerFont(familyName, QString(), QString(), QFont::Normal, QFont::StyleNormal, + QFont::Unstretched, false, false, 0, false, QSupportedWritingSystems(), 0); + } } QFontEngine *QWinRTFontDatabase::fontEngine(const QFontDef &fontDef, void *handle) { + if (!handle) // Happens if a font family population failed + return 0; + IDWriteFontFile *fontFile = reinterpret_cast<IDWriteFontFile *>(handle); if (!m_fonts.contains(fontFile)) return QBasicFontDatabase::fontEngine(fontDef, handle); @@ -361,6 +385,9 @@ QFontEngine *QWinRTFontDatabase::fontEngine(const QFontDef &fontDef, void *handl void QWinRTFontDatabase::releaseHandle(void *handle) { + if (!handle) + return; + IDWriteFontFile *fontFile = reinterpret_cast<IDWriteFontFile *>(handle); if (m_fonts.contains(fontFile)) { m_fonts.remove(fontFile); diff --git a/src/plugins/platforms/winrt/qwinrtfontdatabase.h b/src/plugins/platforms/winrt/qwinrtfontdatabase.h index 6f194a10cc..b318a95502 100644 --- a/src/plugins/platforms/winrt/qwinrtfontdatabase.h +++ b/src/plugins/platforms/winrt/qwinrtfontdatabase.h @@ -48,6 +48,7 @@ QT_BEGIN_NAMESPACE #ifndef Q_OS_WINPHONE struct IDWriteFontFile; +struct IDWriteFontFamily; struct FontDescription { @@ -64,10 +65,12 @@ public: ~QWinRTFontDatabase(); QFont defaultFont() const Q_DECL_OVERRIDE; void populateFontDatabase() Q_DECL_OVERRIDE; + void populateFamily(const QString &familyName) Q_DECL_OVERRIDE; QFontEngine *fontEngine(const QFontDef &fontDef, void *handle) Q_DECL_OVERRIDE; void releaseHandle(void *handle) Q_DECL_OVERRIDE; private: QHash<IDWriteFontFile *, FontDescription> m_fonts; + QHash<QString, IDWriteFontFamily *> m_fontFamilies; #endif // !Q_OS_WINPHONE }; diff --git a/src/plugins/platforms/winrt/qwinrtintegration.cpp b/src/plugins/platforms/winrt/qwinrtintegration.cpp index be82390723..b3a2cafa2e 100644 --- a/src/plugins/platforms/winrt/qwinrtintegration.cpp +++ b/src/plugins/platforms/winrt/qwinrtintegration.cpp @@ -124,6 +124,8 @@ bool QWinRTIntegration::hasCapability(QPlatformIntegration::Capability cap) cons case OpenGL: case ApplicationState: return true; + case NonFullScreenWindows: + return false; default: return QPlatformIntegration::hasCapability(cap); } diff --git a/src/plugins/platforms/winrt/winrt.pro b/src/plugins/platforms/winrt/winrt.pro index 306bbc8174..60c87bb61a 100644 --- a/src/plugins/platforms/winrt/winrt.pro +++ b/src/plugins/platforms/winrt/winrt.pro @@ -1,6 +1,15 @@ TARGET = qwinrt CONFIG -= precompile_header +# For Windows Phone 8 we have to deploy fonts together with the application as DirectWrite +# is not supported here. +# TODO: Add a condition/remove this block if Windows Phone 8.1 supports DirectWrite +winphone { + fonts.path = $$[QT_INSTALL_LIBS]/fonts + fonts.files = $$QT_SOURCE_TREE/lib/fonts/DejaVu*.ttf + INSTALLS += fonts +} + PLUGIN_TYPE = platforms PLUGIN_CLASS_NAME = QWinRTIntegrationPlugin load(qt_plugin) diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index a68ae8cf71..66b8401ea2 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -185,8 +185,12 @@ void QXcbConnection::updateScreens() siblings << screen; activeScreens << screen; ++screenNumber; - if (!primaryScreen && primary) { - if (m_primaryScreen == xcbScreenNumber && (primary->output == XCB_NONE || outputs[i] == primary->output)) { + // There can be multiple outputs per screen, use either + // the first or an exact match. An exact match isn't + // always available if primary->output is XCB_NONE + // or currently disconnected output. + if (m_primaryScreen == xcbScreenNumber) { + if (!primaryScreen || (primary && outputs[i] == primary->output)) { primaryScreen = screen; siblings.prepend(siblings.takeLast()); #ifdef Q_XCB_DEBUG @@ -766,6 +770,7 @@ namespace { xcb_timestamp_t time; uint8_t deviceID; } any; + xcb_xkb_new_keyboard_notify_event_t new_keyboard_notify; xcb_xkb_map_notify_event_t map_notify; xcb_xkb_state_notify_event_t state_notify; } _xkb_event; @@ -796,15 +801,11 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) case XCB_EXPOSE: HANDLE_PLATFORM_WINDOW_EVENT(xcb_expose_event_t, window, handleExposeEvent); case XCB_BUTTON_PRESS: -#ifdef QT_NO_XKB m_keyboard->updateXKBStateFromCore(((xcb_button_press_event_t *)event)->state); -#endif handleButtonPress(event); HANDLE_PLATFORM_WINDOW_EVENT(xcb_button_press_event_t, event, handleButtonPressEvent); case XCB_BUTTON_RELEASE: -#ifdef QT_NO_XKB m_keyboard->updateXKBStateFromCore(((xcb_button_release_event_t *)event)->state); -#endif handleButtonRelease(event); HANDLE_PLATFORM_WINDOW_EVENT(xcb_button_release_event_t, event, handleButtonReleaseEvent); case XCB_MOTION_NOTIFY: @@ -812,9 +813,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) xcb_motion_notify_event_t *mev = (xcb_motion_notify_event_t *)event; qDebug("xcb: moved mouse to %4d, %4d; button state %X", mev->event_x, mev->event_y, static_cast<unsigned int>(m_buttons)); } -#ifdef QT_NO_XKB m_keyboard->updateXKBStateFromCore(((xcb_motion_notify_event_t *)event)->state); -#endif HANDLE_PLATFORM_WINDOW_EVENT(xcb_motion_notify_event_t, event, handleMotionNotifyEvent); case XCB_CONFIGURE_NOTIFY: HANDLE_PLATFORM_WINDOW_EVENT(xcb_configure_notify_event_t, event, handleConfigureNotifyEvent); @@ -830,29 +829,21 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) case XCB_ENTER_NOTIFY: HANDLE_PLATFORM_WINDOW_EVENT(xcb_enter_notify_event_t, event, handleEnterNotifyEvent); case XCB_LEAVE_NOTIFY: -#ifdef QT_NO_XKB m_keyboard->updateXKBStateFromCore(((xcb_leave_notify_event_t *)event)->state); -#endif HANDLE_PLATFORM_WINDOW_EVENT(xcb_leave_notify_event_t, event, handleLeaveNotifyEvent); case XCB_FOCUS_IN: HANDLE_PLATFORM_WINDOW_EVENT(xcb_focus_in_event_t, event, handleFocusInEvent); case XCB_FOCUS_OUT: HANDLE_PLATFORM_WINDOW_EVENT(xcb_focus_out_event_t, event, handleFocusOutEvent); case XCB_KEY_PRESS: -#ifdef QT_NO_XKB m_keyboard->updateXKBStateFromCore(((xcb_key_press_event_t *)event)->state); -#endif HANDLE_KEYBOARD_EVENT(xcb_key_press_event_t, handleKeyPressEvent); case XCB_KEY_RELEASE: -#ifdef QT_NO_XKB m_keyboard->updateXKBStateFromCore(((xcb_key_release_event_t *)event)->state); -#endif HANDLE_KEYBOARD_EVENT(xcb_key_release_event_t, handleKeyReleaseEvent); -#ifdef QT_NO_XKB case XCB_MAPPING_NOTIFY: m_keyboard->handleMappingNotifyEvent((xcb_mapping_notify_event_t *)event); break; -#endif case XCB_SELECTION_REQUEST: { xcb_selection_request_event_t *sr = (xcb_selection_request_event_t *)event; @@ -920,6 +911,8 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) _xkb_event *xkb_event = reinterpret_cast<_xkb_event *>(event); if (xkb_event->any.deviceID == m_keyboard->coreDeviceId()) { switch (xkb_event->any.xkbType) { + // XkbNewKkdNotify and XkbMapNotify together capture all sorts of keymap + // updates (e.g. xmodmap, xkbcomp, setxkbmap), with minimal redundent recompilations. case XCB_XKB_STATE_NOTIFY: m_keyboard->updateXKBState(&xkb_event->state_notify); handled = true; @@ -928,6 +921,12 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) m_keyboard->handleMappingNotifyEvent(&xkb_event->map_notify); handled = true; break; + case XCB_XKB_NEW_KEYBOARD_NOTIFY: { + xcb_xkb_new_keyboard_notify_event_t *ev = &xkb_event->new_keyboard_notify; + if (ev->changed & XCB_XKB_NKN_DETAIL_KEYCODES) + m_keyboard->updateKeymap(); + break; + } default: break; } @@ -1661,6 +1660,7 @@ void QXcbConnection::initializeXKB() #ifndef QT_NO_XKB const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_xkb_id); if (!reply || !reply->present) { + qWarning() << "Qt: XKEYBOARD extension not present on the X server."; xkb_first_event = 0; return; } @@ -1670,14 +1670,14 @@ void QXcbConnection::initializeXKB() xcb_xkb_use_extension_cookie_t xkb_query_cookie; xcb_xkb_use_extension_reply_t *xkb_query; - xkb_query_cookie = xcb_xkb_use_extension(c, XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION); + xkb_query_cookie = xcb_xkb_use_extension(c, XKB_X11_MIN_MAJOR_XKB_VERSION, XKB_X11_MIN_MINOR_XKB_VERSION); xkb_query = xcb_xkb_use_extension_reply(c, xkb_query_cookie, 0); if (!xkb_query) { qWarning("Qt: Failed to initialize XKB extension"); return; } else if (!xkb_query->supported) { - qWarning("Qt: Unsupported XKB version (want %d %d, has %d %d)", + qWarning("Qt: Unsupported XKB version (We want %d %d, but X server has %d %d)", XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION, xkb_query->serverMajor, xkb_query->serverMinor); free(xkb_query); @@ -1687,25 +1687,28 @@ void QXcbConnection::initializeXKB() has_xkb = true; free(xkb_query); - uint affectMap, map; - affectMap = map = XCB_XKB_MAP_PART_KEY_TYPES | - XCB_XKB_MAP_PART_KEY_SYMS | - XCB_XKB_MAP_PART_MODIFIER_MAP | - XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS | - XCB_XKB_MAP_PART_KEY_ACTIONS | - XCB_XKB_MAP_PART_KEY_BEHAVIORS | - XCB_XKB_MAP_PART_VIRTUAL_MODS | - XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP; - - // Xkb events are reported to all interested clients without regard + const uint16_t required_map_parts = (XCB_XKB_MAP_PART_KEY_TYPES | + XCB_XKB_MAP_PART_KEY_SYMS | + XCB_XKB_MAP_PART_MODIFIER_MAP | + XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS | + XCB_XKB_MAP_PART_KEY_ACTIONS | + XCB_XKB_MAP_PART_KEY_BEHAVIORS | + XCB_XKB_MAP_PART_VIRTUAL_MODS | + XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP); + + const uint16_t required_events = (XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY | + XCB_XKB_EVENT_TYPE_MAP_NOTIFY | + XCB_XKB_EVENT_TYPE_STATE_NOTIFY); + + // XKB events are reported to all interested clients without regard // to the current keyboard input focus or grab state xcb_void_cookie_t select = xcb_xkb_select_events_checked(c, XCB_XKB_ID_USE_CORE_KBD, - XCB_XKB_EVENT_TYPE_STATE_NOTIFY | XCB_XKB_EVENT_TYPE_MAP_NOTIFY, + required_events, 0, - XCB_XKB_EVENT_TYPE_STATE_NOTIFY | XCB_XKB_EVENT_TYPE_MAP_NOTIFY, - affectMap, - map, + required_events, + required_map_parts, + required_map_parts, 0); xcb_generic_error_t *error = xcb_request_check(c, select); diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h index 71f5ce13fb..a994c51c7d 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -54,7 +54,7 @@ #include <qpa/qwindowsysteminterface.h> // This is needed to make Qt compile together with XKB. xkb.h is using a variable -// which is called 'explicit', this is a reserved keyword in c++ */ +// which is called 'explicit', this is a reserved keyword in c++ #ifndef QT_NO_XKB #define explicit dont_use_cxx_explicit #include <xcb/xkb.h> diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.cpp b/src/plugins/platforms/xcb/qxcbkeyboard.cpp index 0a52640c9a..92b24f4722 100644 --- a/src/plugins/platforms/xcb/qxcbkeyboard.cpp +++ b/src/plugins/platforms/xcb/qxcbkeyboard.cpp @@ -38,20 +38,22 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ - #include "qxcbkeyboard.h" #include "qxcbwindow.h" #include "qxcbscreen.h" -#include <X11/keysym.h> + #include <qpa/qwindowsysteminterface.h> +#include <qpa/qplatforminputcontext.h> +#include <qpa/qplatformintegration.h> +#include <qpa/qplatformcursor.h> + #include <QtCore/QTextCodec> #include <QtCore/QMetaMethod> +#include <QtCore/QDir> #include <private/qguiapplication_p.h> -#include <stdio.h> -#include <qpa/qplatforminputcontext.h> -#include <qpa/qplatformintegration.h> -#include <qpa/qplatformcursor.h> +#include <stdio.h> +#include <X11/keysym.h> #ifndef XK_ISO_Left_Tab #define XK_ISO_Left_Tab 0xFE20 @@ -619,6 +621,12 @@ void QXcbKeyboard::readXKBConfig() char *xkb_config = (char *)xcb_get_property_value(config_reply); int length = xcb_get_property_value_length(config_reply); + // on old X servers xkb_config can be 0 even if config_reply indicates a succesfull read + if (!xkb_config || length == 0) + return; + // ### TODO some X servers don't set _XKB_RULES_NAMES at all, in these cases it is filled + // with gibberish, we would need to do some kind of sanity check + char *names[5] = { 0, 0, 0, 0, 0 }; char *p = xkb_config, *end = p + length; int i = 0; @@ -655,78 +663,97 @@ void QXcbKeyboard::clearXKBConfig() memset(&xkb_names, 0, sizeof(xkb_names)); } +void QXcbKeyboard::printKeymapError(const QString &error) const +{ + qWarning() << "Qt: " << error; + // check if XKB config root is a valid path + const QDir xkbRoot = qEnvironmentVariableIsSet("QT_XKB_CONFIG_ROOT") + ? QString::fromLocal8Bit(qgetenv("QT_XKB_CONFIG_ROOT")) + : DFLT_XKB_CONFIG_ROOT; + if (!xkbRoot.exists() || xkbRoot.dirName() != "xkb") { + qWarning() << "Set QT_XKB_CONFIG_ROOT to provide a valid XKB configuration data path, current search paths: " + << xkbRoot.path() << ". Use ':' as separator to provide several search paths."; + return; + } + qWarning() << "_XKB_RULES_NAMES property contains:" << "\nrules : " << xkb_names.rules << + "\nmodel : " << xkb_names.model << "\nlayout : " << xkb_names.layout << + "\nvariant : " << xkb_names.variant << "\noptions : " << xkb_names.options << + "\nIf this looks like a valid keyboard layout information then you might need to " + "update XKB configuration data on the system (http://cgit.freedesktop.org/xkeyboard-config/)."; +} + void QXcbKeyboard::updateKeymap() { m_config = true; + // set xkb context object if (!xkb_context) { - xkb_context = xkb_context_new((xkb_context_flags)0); + if (qEnvironmentVariableIsSet("QT_XKB_CONFIG_ROOT")) { + xkb_context = xkb_context_new((xkb_context_flags)XKB_CONTEXT_NO_DEFAULT_INCLUDES); + QList<QByteArray> xkbRootList = QByteArray(qgetenv("QT_XKB_CONFIG_ROOT")).split(':'); + foreach (QByteArray xkbRoot, xkbRootList) + xkb_context_include_path_append(xkb_context, xkbRoot.constData()); + } else { + xkb_context = xkb_context_new((xkb_context_flags)0); + } if (!xkb_context) { - qWarning("Qt: Failed to create XKB context"); + printKeymapError("Failed to create XKB context!"); m_config = false; return; } + // log only critical errors, we do our own error logging from printKeymapError() + xkb_context_set_log_level(xkb_context, (xkb_log_level)XKB_LOG_LEVEL_CRITICAL); } - readXKBConfig(); - // Compile a keymap from RMLVO (rules, models, layouts, variants and options) names - if (xkb_keymap) - xkb_keymap_unref(xkb_keymap); - - xkb_keymap = xkb_keymap_new_from_names(xkb_context, &xkb_names, (xkb_keymap_compile_flags)0); + // update xkb keymap object + xkb_keymap_unref(xkb_keymap); + xkb_keymap = 0; + struct xkb_state *new_state = 0; +#ifndef QT_NO_XKB + if (connection()->hasXKB()) { + xkb_keymap = xkb_x11_keymap_new_from_device(xkb_context, xcb_connection(), core_device_id, (xkb_keymap_compile_flags)0); + if (xkb_keymap) { + // Create a new keyboard state object for a keymap + new_state = xkb_x11_state_new_from_device(xkb_keymap, xcb_connection(), core_device_id); + } + } +#endif if (!xkb_keymap) { - qWarning("Qt: Failed to compile a keymap"); - m_config = false; - return; + // Compile a keymap from RMLVO (rules, models, layouts, variants and options) names + readXKBConfig(); + xkb_keymap = xkb_keymap_new_from_names(xkb_context, &xkb_names, (xkb_keymap_compile_flags)0); + if (!xkb_keymap) { + // last fallback is to used hard-coded keymap name, see DEFAULT_XKB_* in xkbcommon.pri + qWarning() << "Qt: Could not determine keyboard configuration data" + " from X server, will use hard-coded keymap configuration."; + clearXKBConfig(); + xkb_keymap = xkb_keymap_new_from_names(xkb_context, &xkb_names, (xkb_keymap_compile_flags)0); + } + if (xkb_keymap) { + new_state = xkb_state_new(xkb_keymap); + } else { + // failed to compile from RMLVO, give a verbose error message + printKeymapError("Qt: Failed to compile a keymap!"); + m_config = false; + return; + } + } - // Create a new keyboard state object for a keymap - struct xkb_state *new_state = xkb_state_new(xkb_keymap); if (!new_state) { - qWarning("Qt: Failed to create a new keyboard state"); + qWarning("Qt: Failed to create xkb state!"); m_config = false; return; } - - if (xkb_state) { - xkb_state_unref(xkb_state); - xkb_state = new_state; - } else { - xkb_state = new_state; -#ifndef QT_NO_XKB - // get initial state from the X server (and keep it up-to-date at all times) - xcb_xkb_get_state_cookie_t state; - xcb_xkb_get_state_reply_t *init_state; - - xcb_connection_t *c = xcb_connection(); - state = xcb_xkb_get_state(c, XCB_XKB_ID_USE_CORE_KBD); - init_state = xcb_xkb_get_state_reply(c, state, 0); - if (!init_state) { - qWarning("Qt: couldn't retrieve an initial keyboard state"); - return; - } - /* The xkb keyboard state is comprised of the state of all keyboard modifiers, - the keyboard group, and the state of the pointer buttons */ - xkb_state_update_mask(xkb_state, - init_state->baseMods, - init_state->latchedMods, - init_state->lockedMods, - init_state->baseGroup, - init_state->latchedGroup, - init_state->lockedGroup); - free(init_state); -#else + // update xkb state object + xkb_state_unref(xkb_state); + xkb_state = new_state; + if (!connection()->hasXKB()) updateXKBMods(); -#endif - } } #ifndef QT_NO_XKB void QXcbKeyboard::updateXKBState(xcb_xkb_state_notify_event_t *state) { - if (!m_config) - return; - - if (connection()->hasXKB()) { - + if (m_config && connection()->hasXKB()) { const xkb_state_component newState = xkb_state_update_mask(xkb_state, state->baseMods, @@ -741,35 +768,34 @@ void QXcbKeyboard::updateXKBState(xcb_xkb_state_notify_event_t *state) } } } +#endif -#else void QXcbKeyboard::updateXKBStateFromCore(quint16 state) { - if (!m_config) - return; + if (m_config && !connection()->hasXKB()) { + const quint32 modsDepressed = xkb_state_serialize_mods(xkb_state, XKB_STATE_MODS_DEPRESSED); + const quint32 modsLatched = xkb_state_serialize_mods(xkb_state, XKB_STATE_MODS_LATCHED); + const quint32 modsLocked = xkb_state_serialize_mods(xkb_state, XKB_STATE_MODS_LOCKED); + const quint32 xkbMask = xkbModMask(state); + + const quint32 latched = modsLatched & xkbMask; + const quint32 locked = modsLocked & xkbMask; + quint32 depressed = modsDepressed & xkbMask; + // set modifiers in depressed if they don't appear in any of the final masks + depressed |= ~(depressed | latched | locked) & xkbMask; - const quint32 modsDepressed = xkb_state_serialize_mods(xkb_state, XKB_STATE_MODS_DEPRESSED); - const quint32 modsLatched = xkb_state_serialize_mods(xkb_state, XKB_STATE_MODS_LATCHED); - const quint32 modsLocked = xkb_state_serialize_mods(xkb_state, XKB_STATE_MODS_LOCKED); - const quint32 xkbMask = xkbModMask(state); - - const quint32 latched = modsLatched & xkbMask; - const quint32 locked = modsLocked & xkbMask; - quint32 depressed = modsDepressed & xkbMask; - // set modifiers in depressed if they don't appear in any of the final masks - depressed |= ~(depressed | latched | locked) & xkbMask; - - const xkb_state_component newState - = xkb_state_update_mask(xkb_state, - depressed, - latched, - locked, - 0, - 0, - (state >> 13) & 3); // bits 13 and 14 report the state keyboard group - - if ((newState & XKB_STATE_LAYOUT_EFFECTIVE) == XKB_STATE_LAYOUT_EFFECTIVE) { - //qWarning("TODO: Support KeyboardLayoutChange on QPA (QTBUG-27681)"); + const xkb_state_component newState + = xkb_state_update_mask(xkb_state, + depressed, + latched, + locked, + 0, + 0, + (state >> 13) & 3); // bits 13 and 14 report the state keyboard group + + if ((newState & XKB_STATE_LAYOUT_EFFECTIVE) == XKB_STATE_LAYOUT_EFFECTIVE) { + //qWarning("TODO: Support KeyboardLayoutChange on QPA (QTBUG-27681)"); + } } } @@ -799,16 +825,15 @@ quint32 QXcbKeyboard::xkbModMask(quint16 state) void QXcbKeyboard::updateXKBMods() { - xkb_mods.shift = xkb_map_mod_get_index(xkb_keymap, XKB_MOD_NAME_SHIFT); - xkb_mods.lock = xkb_map_mod_get_index(xkb_keymap, XKB_MOD_NAME_CAPS); - xkb_mods.control = xkb_map_mod_get_index(xkb_keymap, XKB_MOD_NAME_CTRL); - xkb_mods.mod1 = xkb_map_mod_get_index(xkb_keymap, "Mod1"); - xkb_mods.mod2 = xkb_map_mod_get_index(xkb_keymap, "Mod2"); - xkb_mods.mod3 = xkb_map_mod_get_index(xkb_keymap, "Mod3"); - xkb_mods.mod4 = xkb_map_mod_get_index(xkb_keymap, "Mod4"); - xkb_mods.mod5 = xkb_map_mod_get_index(xkb_keymap, "Mod5"); + xkb_mods.shift = xkb_keymap_mod_get_index(xkb_keymap, XKB_MOD_NAME_SHIFT); + xkb_mods.lock = xkb_keymap_mod_get_index(xkb_keymap, XKB_MOD_NAME_CAPS); + xkb_mods.control = xkb_keymap_mod_get_index(xkb_keymap, XKB_MOD_NAME_CTRL); + xkb_mods.mod1 = xkb_keymap_mod_get_index(xkb_keymap, "Mod1"); + xkb_mods.mod2 = xkb_keymap_mod_get_index(xkb_keymap, "Mod2"); + xkb_mods.mod3 = xkb_keymap_mod_get_index(xkb_keymap, "Mod3"); + xkb_mods.mod4 = xkb_keymap_mod_get_index(xkb_keymap, "Mod4"); + xkb_mods.mod5 = xkb_keymap_mod_get_index(xkb_keymap, "Mod5"); } -#endif QList<int> QXcbKeyboard::possibleKeys(const QKeyEvent *event) const { @@ -893,10 +918,8 @@ QList<int> QXcbKeyboard::possibleKeys(const QKeyEvent *event) const result += (qtKey + mods); } } - if (kb_state) - xkb_state_unref(kb_state); - if (fallback_keymap) - xkb_keymap_unref(fallback_keymap); + xkb_state_unref(kb_state); + xkb_keymap_unref(fallback_keymap); return result; } @@ -963,58 +986,41 @@ QXcbKeyboard::QXcbKeyboard(QXcbConnection *connection) , xkb_context(0) , xkb_keymap(0) , xkb_state(0) -#ifndef QT_NO_XKB , core_device_id(0) -#endif { memset(&xkb_names, 0, sizeof(xkb_names)); - updateKeymap(); #ifndef QT_NO_XKB if (connection->hasXKB()) { - updateVModMapping(); updateVModToRModMapping(); - - // get the core keyboard id - xcb_xkb_get_device_info_cookie_t device_id_cookie; - xcb_xkb_get_device_info_reply_t *device_id; - - device_id_cookie = xcb_xkb_get_device_info(xcb_connection(), - XCB_XKB_ID_USE_CORE_KBD, - 0, 0, 0, 0, 0, 0); - - device_id = xcb_xkb_get_device_info_reply(xcb_connection(), device_id_cookie, 0); - if (!device_id) { + core_device_id = xkb_x11_get_core_keyboard_device_id(xcb_connection()); + if (core_device_id == -1) { qWarning("Qt: couldn't get core keyboard device info"); return; } - - core_device_id = device_id->deviceID; - free(device_id); + } else { +#endif + m_key_symbols = xcb_key_symbols_alloc(xcb_connection()); + updateModifiers(); +#ifndef QT_NO_XKB } -#else - m_key_symbols = xcb_key_symbols_alloc(xcb_connection()); - updateModifiers(); #endif + updateKeymap(); } QXcbKeyboard::~QXcbKeyboard() { - if (xkb_state) - xkb_state_unref(xkb_state); - if (xkb_keymap) - xkb_keymap_unref(xkb_keymap); - if (xkb_context) - xkb_context_unref(xkb_context); -#ifdef QT_NO_XKB - xcb_key_symbols_free(m_key_symbols); -#endif + xkb_state_unref(xkb_state); + xkb_keymap_unref(xkb_keymap); + xkb_context_unref(xkb_context); + if (!connection()->hasXKB()) + xcb_key_symbols_free(m_key_symbols); clearXKBConfig(); } -#ifndef QT_NO_XKB void QXcbKeyboard::updateVModMapping() { +#ifndef QT_NO_XKB xcb_xkb_get_names_cookie_t names_cookie; xcb_xkb_get_names_reply_t *name_reply; xcb_xkb_get_names_value_list_t names_list; @@ -1078,10 +1084,12 @@ void QXcbKeyboard::updateVModMapping() } free(name_reply); +#endif } void QXcbKeyboard::updateVModToRModMapping() { +#ifndef QT_NO_XKB xcb_xkb_get_map_cookie_t map_cookie; xcb_xkb_get_map_reply_t *map_reply; xcb_xkb_get_map_map_t map; @@ -1144,8 +1152,9 @@ void QXcbKeyboard::updateVModToRModMapping() free(map_reply); resolveMaskConflicts(); +#endif } -#else + void QXcbKeyboard::updateModifiers() { // The core protocol does not provide a convenient way to determine the mapping @@ -1209,7 +1218,6 @@ void QXcbKeyboard::updateModifiers() free(modMapReply); resolveMaskConflicts(); } -#endif void QXcbKeyboard::resolveMaskConflicts() { @@ -1292,17 +1300,9 @@ void QXcbKeyboard::handleKeyEvent(QWindow *window, QEvent::Type type, xcb_keycod if (!m_config) return; - // It is crucial the order of xkb_state_key_get_one_sym & - // xkb_state_update_key operations is not reversed! + + // It is crucial the order of xkb_state_key_get_one_sym & xkb_state_update_key operations is not reversed! xcb_keysym_t sym = xkb_state_key_get_one_sym(xkb_state, code); -#ifdef QT_NO_XKB - enum xkb_key_direction direction; - if (type == QEvent::KeyPress) - direction = XKB_KEY_DOWN; - else - direction = XKB_KEY_UP; - xkb_state_update_key(xkb_state, code, direction); -#endif QPlatformInputContext *inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext(); QMetaMethod method; @@ -1422,17 +1422,14 @@ void QXcbKeyboard::handleKeyReleaseEvent(QXcbWindowEventListener *eventListener, void QXcbKeyboard::handleMappingNotifyEvent(const void *event) { updateKeymap(); -#ifdef QT_NO_XKB - void *ev = const_cast<void *>(event); - xcb_refresh_keyboard_mapping(m_key_symbols, static_cast<xcb_mapping_notify_event_t *>(ev)); - updateModifiers(); -#else - Q_UNUSED(event) if (connection()->hasXKB()) { updateVModMapping(); updateVModToRModMapping(); + } else { + void *ev = const_cast<void *>(event); + xcb_refresh_keyboard_mapping(m_key_symbols, static_cast<xcb_mapping_notify_event_t *>(ev)); + updateModifiers(); } -#endif } QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.h b/src/plugins/platforms/xcb/qxcbkeyboard.h index 0256602782..36ce1ea2f0 100644 --- a/src/plugins/platforms/xcb/qxcbkeyboard.h +++ b/src/plugins/platforms/xcb/qxcbkeyboard.h @@ -44,11 +44,15 @@ #include "qxcbobject.h" -#ifdef QT_NO_XKB #include <xcb/xcb_keysyms.h> -#endif #include <xkbcommon/xkbcommon.h> +#ifndef QT_NO_XKB +// note: extern won't be needed from libxkbcommon 0.4.1 and above +extern "C" { +#include <xkbcommon/xkbcommon-x11.h> +} +#endif #include <QEvent> @@ -65,41 +69,38 @@ public: void handleKeyPressEvent(QXcbWindowEventListener *eventListener, const xcb_key_press_event_t *event); void handleKeyReleaseEvent(QXcbWindowEventListener *eventListener, const xcb_key_release_event_t *event); - void handleMappingNotifyEvent(const void *event); Qt::KeyboardModifiers translateModifiers(int s) const; - void updateKeymap(); QList<int> possibleKeys(const QKeyEvent *e) const; -#ifdef QT_NO_XKB - void updateXKBStateFromCore(quint16 state); + // when XKEYBOARD not present on the X server void updateXKBMods(); quint32 xkbModMask(quint16 state); -#else - int coreDeviceId() { return core_device_id; } + void updateXKBStateFromCore(quint16 state); + // when XKEYBOARD is present on the X server + int coreDeviceId() const { return core_device_id; } +#ifndef QT_NO_XKB void updateXKBState(xcb_xkb_state_notify_event_t *state); #endif protected: void handleKeyEvent(QWindow *window, QEvent::Type type, xcb_keycode_t code, quint16 state, xcb_timestamp_t time); - void resolveMaskConflicts(); + void resolveMaskConflicts(); QString keysymToUnicode(xcb_keysym_t sym) const; - int keysymToQtKey(xcb_keysym_t keysym) const; int keysymToQtKey(xcb_keysym_t keysym, Qt::KeyboardModifiers &modifiers, QString text) const; + void printKeymapError(const QString &error) const; void readXKBConfig(); void clearXKBConfig(); - -#ifdef QT_NO_XKB + // when XKEYBOARD not present on the X server void updateModifiers(); -#else + // when XKEYBOARD is present on the X server void updateVModMapping(); void updateVModToRModMapping(); -#endif private: bool m_config; @@ -120,9 +121,8 @@ private: _mod_masks rmod_masks; -#ifdef QT_NO_XKB + // when XKEYBOARD not present on the X server xcb_key_symbols_t *m_key_symbols; - struct _xkb_mods { xkb_mod_index_t shift; xkb_mod_index_t lock; @@ -133,12 +133,10 @@ private: xkb_mod_index_t mod4; xkb_mod_index_t mod5; }; - _xkb_mods xkb_mods; -#else + // when XKEYBOARD is present on the X server _mod_masks vmod_masks; int core_device_id; -#endif }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index d890398416..bed6eb59dc 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -1829,21 +1829,21 @@ void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *ev return; Qt::WindowState newState = Qt::WindowNoState; - if (event->atom == atom(QXcbAtom::_NET_WM_STATE)) { // WM_STATE: Quick check for 'Minimize'. + if (event->atom == atom(QXcbAtom::WM_STATE)) { // WM_STATE: Quick check for 'Minimize'. const xcb_get_property_cookie_t get_cookie = - xcb_get_property(xcb_connection(), 0, m_window, atom(QXcbAtom::_NET_WM_STATE), - XCB_ATOM_ANY, 0, 1024); + xcb_get_property(xcb_connection(), 0, m_window, atom(QXcbAtom::WM_STATE), + XCB_ATOM_ANY, 0, 1024); xcb_get_property_reply_t *reply = xcb_get_property_reply(xcb_connection(), get_cookie, NULL); - if (reply && reply->format == 32 && reply->type == atom(QXcbAtom::_NET_WM_STATE)) { + if (reply && reply->format == 32 && reply->type == atom(QXcbAtom::WM_STATE)) { const quint32 *data = (const quint32 *)xcb_get_property_value(reply); if (reply->length != 0 && XCB_WM_STATE_ICONIC == data[0]) newState = Qt::WindowMinimized; } free(reply); - } // WM_STATE: Quick check for 'Minimize'. + } if (newState != Qt::WindowMinimized) { // Something else changed, get _NET_WM_STATE. const NetWmStates states = netWmStates(); if ((states & NetWmStateMaximizedHorz) && (states & NetWmStateMaximizedVert)) diff --git a/src/plugins/platforms/xcb/xcb-plugin.pro b/src/plugins/platforms/xcb/xcb-plugin.pro index e19bb921e1..9e4e997f55 100644 --- a/src/plugins/platforms/xcb/xcb-plugin.pro +++ b/src/plugins/platforms/xcb/xcb-plugin.pro @@ -121,12 +121,8 @@ contains(QT_CONFIG, xcb-qt) { INCLUDEPATH += $$XCB_DIR/include $$XCB_DIR/sysinclude LIBS += -lxcb -L$$OUT_PWD/xcb-static -lxcb-static } else { - LIBS += -lxcb -lxcb-image -lxcb-icccm -lxcb-sync -lxcb-xfixes -lxcb-shm -lxcb-randr -lxcb-shape - contains(DEFINES, QT_NO_XKB) { - LIBS += -lxcb-keysyms - } else { - LIBS += -lxcb-xkb - } + LIBS += -lxcb -lxcb-image -lxcb-icccm -lxcb-sync -lxcb-xfixes -lxcb-shm -lxcb-randr -lxcb-shape -lxcb-keysyms + !contains(DEFINES, QT_NO_XKB):LIBS += -lxcb-xkb } # libxkbcommon |