diff options
Diffstat (limited to 'src/plugins/platforms')
255 files changed, 9638 insertions, 5365 deletions
diff --git a/src/plugins/platforms/android/androidjniinput.cpp b/src/plugins/platforms/android/androidjniinput.cpp index 8ee3ff88d1..8982787ec9 100644 --- a/src/plugins/platforms/android/androidjniinput.cpp +++ b/src/plugins/platforms/android/androidjniinput.cpp @@ -70,18 +70,20 @@ namespace QtAndroidInput candidatesEnd); } - void showSoftwareKeyboard(int left, int top, int width, int height, int inputHints) + void showSoftwareKeyboard(int left, int top, int width, int height, int inputHints, int enterKeyType) { QJNIObjectPrivate::callStaticMethod<void>(applicationClass(), "showSoftwareKeyboard", - "(IIIII)V", + "(IIIIII)V", left, top, width, height, - inputHints); + inputHints, + enterKeyType + ); #ifdef QT_DEBUG_ANDROID_IM_PROTOCOL - qDebug() << "@@@ SHOWSOFTWAREKEYBOARD" << left << top << width << height << inputHints; + qDebug() << "@@@ SHOWSOFTWAREKEYBOARD" << left << top << width << height << inputHints << enterKeyType; #endif } @@ -238,6 +240,52 @@ namespace QtAndroidInput QWindowSystemInterface::handleTouchEvent(window, touchDevice, m_touchPoints); } + static void tabletEvent(JNIEnv */*env*/, jobject /*thiz*/, jint /*winId*/, jint deviceId, jlong time, jint action, + jint pointerType, jint buttonState, jfloat x, jfloat y, jfloat pressure) + { + QPointF globalPosF(x, y); + QPoint globalPos((int)x, (int)y); + QWindow *tlw = topLevelWindowAt(globalPos); + QPointF localPos = tlw ? (globalPosF - tlw->position()) : globalPosF; + + // Galaxy Note with plain Android: + // 0 1 0 stylus press + // 2 1 0 stylus drag + // 1 1 0 stylus release + // 0 1 2 stylus press with side-button held + // 2 1 2 stylus drag with side-button held + // 1 1 2 stylus release with side-button held + // Galaxy Note 4 with Samsung firmware: + // 0 1 0 stylus press + // 2 1 0 stylus drag + // 1 1 0 stylus release + // 211 1 2 stylus press with side-button held + // 213 1 2 stylus drag with side-button held + // 212 1 2 stylus release with side-button held + // when action == ACTION_UP (1) it's a release; otherwise we say which button is pressed + Qt::MouseButtons buttons = Qt::NoButton; + switch (action) { + case 1: // ACTION_UP + case 212: // stylus release while side-button held on Galaxy Note 4 + buttons = Qt::NoButton; + break; + default: // action is press or drag + if (buttonState == 0) + buttons = Qt::LeftButton; + else // 2 means RightButton + buttons = Qt::MouseButtons(buttonState); + break; + } + +#ifdef QT_DEBUG_ANDROID_STYLUS + qDebug() << action << pointerType << buttonState << '@' << x << y << "pressure" << pressure << ": buttons" << buttons; +#endif + + QWindowSystemInterface::handleTabletEvent(tlw, ulong(time), + localPos, globalPosF, QTabletEvent::Stylus, pointerType, + buttons, pressure, 0, 0, 0., 0., 0, deviceId, Qt::NoModifier); + } + static int mapAndroidKey(int key) { // 0--9 0x00000007 -- 0x00000010 @@ -633,7 +681,7 @@ namespace QtAndroidInput return Qt::Key_AudioCycleTrack; default: - qWarning() << "Unhandled key code " << key << "!"; + qWarning() << "Unhandled key code " << key << '!'; return 0; } } @@ -702,6 +750,7 @@ namespace QtAndroidInput {"mouseUp", "(III)V", (void *)mouseUp}, {"mouseMove", "(III)V", (void *)mouseMove}, {"longPress", "(III)V", (void *)longPress}, + {"tabletEvent", "(IIJIIIFFF)V", (void *)tabletEvent}, {"keyDown", "(IIIZ)V", (void *)keyDown}, {"keyUp", "(IIIZ)V", (void *)keyUp}, {"keyboardVisibilityChanged", "(Z)V", (void *)keyboardVisibilityChanged} diff --git a/src/plugins/platforms/android/androidjniinput.h b/src/plugins/platforms/android/androidjniinput.h index b5a2ef06e4..d737dc9c98 100644 --- a/src/plugins/platforms/android/androidjniinput.h +++ b/src/plugins/platforms/android/androidjniinput.h @@ -41,7 +41,7 @@ QT_BEGIN_NAMESPACE namespace QtAndroidInput { // Software keyboard support - void showSoftwareKeyboard(int top, int left, int width, int height, int inputHints); + void showSoftwareKeyboard(int top, int left, int width, int height, int inputHints, int enterKeyType); void resetSoftwareKeyboard(); void hideSoftwareKeyboard(); bool isSoftwareKeyboardVisible(); diff --git a/src/plugins/platforms/android/androidjnimain.cpp b/src/plugins/platforms/android/androidjnimain.cpp index a04bf1eccb..d419e42cd5 100644 --- a/src/plugins/platforms/android/androidjnimain.cpp +++ b/src/plugins/platforms/android/androidjnimain.cpp @@ -55,6 +55,7 @@ #include <QtCore/private/qjnihelpers_p.h> #include <QtCore/private/qjni_p.h> #include <QtGui/private/qguiapplication_p.h> +#include <QtGui/private/qhighdpiscaling_p.h> #include <qpa/qwindowsysteminterface.h> @@ -109,6 +110,7 @@ static QAndroidPlatformIntegration *m_androidPlatformIntegration = nullptr; static int m_desktopWidthPixels = 0; static int m_desktopHeightPixels = 0; static double m_scaledDensity = 0; +static double m_density = 1.0; static volatile bool m_pauseApplication; @@ -157,6 +159,11 @@ namespace QtAndroid return m_scaledDensity; } + double pixelDensity() + { + return m_density; + } + JavaVM *javaVM() { return m_javaVM; @@ -292,7 +299,7 @@ namespace QtAndroid QString manufacturer = QJNIObjectPrivate::getStaticObjectField("android/os/Build", "MANUFACTURER", "Ljava/lang/String;").toString(); QString model = QJNIObjectPrivate::getStaticObjectField("android/os/Build", "MODEL", "Ljava/lang/String;").toString(); - return manufacturer + QStringLiteral(" ") + model; + return manufacturer + QLatin1Char(' ') + model; } int createSurface(AndroidSurfaceClient *client, const QRect &geometry, bool onTop, int imageDepth) @@ -432,7 +439,6 @@ static void *startMainMethod(void */*data*/) params[i] = static_cast<const char *>(m_applicationParams[i].constData()); int ret = m_main(m_applicationParams.length(), const_cast<char **>(params.data())); - Q_UNUSED(ret); if (m_mainLibraryHnd) { int res = dlclose(m_mainLibraryHnd); @@ -448,6 +454,8 @@ static void *startMainMethod(void */*data*/) if (vm != 0) vm->DetachCurrentThread(); + // We must call exit() to ensure that all global objects will be destructed + exit(ret); return 0; } @@ -546,7 +554,8 @@ static void setSurface(JNIEnv *env, jobject /*thiz*/, jint id, jobject jSurface, static void setDisplayMetrics(JNIEnv */*env*/, jclass /*clazz*/, jint widthPixels, jint heightPixels, jint desktopWidthPixels, jint desktopHeightPixels, - jdouble xdpi, jdouble ydpi, jdouble scaledDensity) + jdouble xdpi, jdouble ydpi, + jdouble scaledDensity, jdouble density) { // Android does not give us the correct screen size for immersive mode, but // the surface does have the right size @@ -557,6 +566,7 @@ static void setDisplayMetrics(JNIEnv */*env*/, jclass /*clazz*/, m_desktopWidthPixels = desktopWidthPixels; m_desktopHeightPixels = desktopHeightPixels; m_scaledDensity = scaledDensity; + m_density = density; if (!m_androidPlatformIntegration) { QAndroidPlatformIntegration::setDefaultDisplayMetrics(desktopWidthPixels, @@ -600,6 +610,11 @@ static void updateApplicationState(JNIEnv */*env*/, jobject /*thiz*/, jint state return; } + if (state == Qt::ApplicationActive) + QtAndroidPrivate::handleResume(); + else if (state == Qt::ApplicationInactive) + QtAndroidPrivate::handlePause(); + if (state <= Qt::ApplicationInactive) { // NOTE: sometimes we will receive two consecutive suspended notifications, // In the second suspended notification, QWindowSystemInterface::flushWindowSystemEvents() @@ -677,7 +692,7 @@ static JNINativeMethod methods[] = { {"startQtApplication", "(Ljava/lang/String;Ljava/lang/String;)V", (void *)startQtApplication}, {"quitQtAndroidPlugin", "()V", (void *)quitQtAndroidPlugin}, {"terminateQt", "()V", (void *)terminateQt}, - {"setDisplayMetrics", "(IIIIDDD)V", (void *)setDisplayMetrics}, + {"setDisplayMetrics", "(IIIIDDDD)V", (void *)setDisplayMetrics}, {"setSurface", "(ILjava/lang/Object;II)V", (void *)setSurface}, {"updateWindow", "()V", (void *)updateWindow}, {"updateApplicationState", "(I)V", (void *)updateApplicationState}, diff --git a/src/plugins/platforms/android/androidjnimain.h b/src/plugins/platforms/android/androidjnimain.h index 4d037f4b74..cdedeb38f8 100644 --- a/src/plugins/platforms/android/androidjnimain.h +++ b/src/plugins/platforms/android/androidjnimain.h @@ -71,6 +71,7 @@ namespace QtAndroid int desktopWidthPixels(); int desktopHeightPixels(); double scaledDensity(); + double pixelDensity(); JavaVM *javaVM(); AAssetManager *assetManager(); jclass applicationClass(); diff --git a/src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp b/src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp index 64be75b63f..6ad3d2dc9a 100644 --- a/src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp +++ b/src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp @@ -122,8 +122,8 @@ public: m_assetFile = 0; m_assetDir = asset; m_fileName = fileName; - if (!m_fileName.endsWith(QChar(QLatin1Char('/')))) - m_fileName += "/"; + if (!m_fileName.endsWith(QLatin1Char('/'))) + m_fileName += QLatin1Char('/'); } ~AndroidAbstractFileEngine() @@ -231,8 +231,8 @@ public: return; m_fileName = file; - if (!m_fileName.endsWith(QChar(QLatin1Char('/')))) - m_fileName += "/"; + if (!m_fileName.endsWith(QLatin1Char('/'))) + m_fileName += QLatin1Char('/'); close(); } diff --git a/src/plugins/platforms/android/qandroidinputcontext.cpp b/src/plugins/platforms/android/qandroidinputcontext.cpp index d264f74d66..e3ea048e84 100644 --- a/src/plugins/platforms/android/qandroidinputcontext.cpp +++ b/src/plugins/platforms/android/qandroidinputcontext.cpp @@ -342,7 +342,7 @@ QAndroidInputContext::QAndroidInputContext() if (clazz == NULL) { qCritical() << "Native registration unable to find class '" << QtNativeInputConnectionClassName - << "'"; + << '\''; return; } @@ -350,7 +350,7 @@ QAndroidInputContext::QAndroidInputContext() if (env->RegisterNatives(clazz, methods, sizeof(methods) / sizeof(methods[0])) < 0) { qCritical() << "RegisterNatives failed for '" << QtNativeInputConnectionClassName - << "'"; + << '\''; return; } @@ -358,7 +358,7 @@ QAndroidInputContext::QAndroidInputContext() if (clazz == NULL) { qCritical() << "Native registration unable to find class '" << QtExtractedTextClassName - << "'"; + << '\''; return; } @@ -545,7 +545,9 @@ void QAndroidInputContext::showInputPanel() rect.top(), rect.width(), rect.height(), - query->value(Qt::ImHints).toUInt()); + query->value(Qt::ImHints).toUInt(), + query->value(Qt::ImEnterKeyType).toUInt() + ); } void QAndroidInputContext::showInputPanelLater(Qt::ApplicationState state) diff --git a/src/plugins/platforms/android/qandroidplatformfontdatabase.cpp b/src/plugins/platforms/android/qandroidplatformfontdatabase.cpp index be1a3d7bdf..a8bbec9400 100644 --- a/src/plugins/platforms/android/qandroidplatformfontdatabase.cpp +++ b/src/plugins/platforms/android/qandroidplatformfontdatabase.cpp @@ -69,11 +69,11 @@ QStringList QAndroidPlatformFontDatabase::fallbacksForFamily(const QString &fami { QStringList result; if (styleHint == QFont::Monospace || styleHint == QFont::Courier) - result.append(QString(qgetenv("QT_ANDROID_FONTS_MONOSPACE")).split(";")); + result.append(QString(qgetenv("QT_ANDROID_FONTS_MONOSPACE")).split(QLatin1Char(';'))); else if (styleHint == QFont::Serif) - result.append(QString(qgetenv("QT_ANDROID_FONTS_SERIF")).split(";")); + result.append(QString(qgetenv("QT_ANDROID_FONTS_SERIF")).split(QLatin1Char(';'))); else - result.append(QString(qgetenv("QT_ANDROID_FONTS")).split(";")); + result.append(QString(qgetenv("QT_ANDROID_FONTS")).split(QLatin1Char(';'))); result.append(QPlatformFontDatabase::fallbacksForFamily(family, style, styleHint, script)); return result; diff --git a/src/plugins/platforms/android/qandroidplatformscreen.cpp b/src/plugins/platforms/android/qandroidplatformscreen.cpp index 7a509e4d61..209ce2f7db 100644 --- a/src/plugins/platforms/android/qandroidplatformscreen.cpp +++ b/src/plugins/platforms/android/qandroidplatformscreen.cpp @@ -382,6 +382,11 @@ QDpi QAndroidPlatformScreen::logicalDpi() const return QDpi(lDpi, lDpi); } +qreal QAndroidPlatformScreen::pixelDensity() const +{ + return QtAndroid::pixelDensity(); +} + Qt::ScreenOrientation QAndroidPlatformScreen::orientation() const { return QAndroidPlatformIntegration::m_orientation; diff --git a/src/plugins/platforms/android/qandroidplatformscreen.h b/src/plugins/platforms/android/qandroidplatformscreen.h index f4f9cedb70..1b7bc91c83 100644 --- a/src/plugins/platforms/android/qandroidplatformscreen.h +++ b/src/plugins/platforms/android/qandroidplatformscreen.h @@ -95,6 +95,7 @@ protected: private: QDpi logicalDpi() const; + qreal pixelDensity() const; Qt::ScreenOrientation orientation() const; Qt::ScreenOrientation nativeOrientation() const; void surfaceChanged(JNIEnv *env, jobject surface, int w, int h); diff --git a/src/plugins/platforms/android/qandroidplatformtheme.cpp b/src/plugins/platforms/android/qandroidplatformtheme.cpp index 3e2ae7c939..71a3f910d2 100644 --- a/src/plugins/platforms/android/qandroidplatformtheme.cpp +++ b/src/plugins/platforms/android/qandroidplatformtheme.cpp @@ -31,6 +31,7 @@ ** ****************************************************************************/ +#include "androidjnimain.h" #include "androidjnimenu.h" #include "qandroidplatformtheme.h" #include "qandroidplatformmenubar.h" @@ -216,6 +217,7 @@ QJsonObject AndroidStyle::loadStyleData() static std::shared_ptr<AndroidStyle> loadAndroidStyle(QPalette *defaultPalette) { + double pixelDensity = qEnvironmentVariableIsSet("QT_AUTO_SCREEN_SCALE_FACTOR") ? QtAndroid::pixelDensity() : 1.0; std::shared_ptr<AndroidStyle> style(new AndroidStyle); style->m_styleData = AndroidStyle::loadStyleData(); if (style->m_styleData.isEmpty()) @@ -245,7 +247,7 @@ static std::shared_ptr<AndroidStyle> loadAndroidStyle(QPalette *defaultPalette) // Font size (in pixels) attributeIterator = item.find(QLatin1String("TextAppearance_textSize")); if (attributeIterator != item.constEnd()) - font.setPixelSize(int(attributeIterator.value().toDouble())); + font.setPixelSize(int(attributeIterator.value().toDouble() / pixelDensity)); // Font style attributeIterator = item.find(QLatin1String("TextAppearance_textStyle")); diff --git a/src/plugins/platforms/cocoa/cocoa.pro b/src/plugins/platforms/cocoa/cocoa.pro index ad6cb3a1fc..ba0e6b001a 100644 --- a/src/plugins/platforms/cocoa/cocoa.pro +++ b/src/plugins/platforms/cocoa/cocoa.pro @@ -12,7 +12,6 @@ OBJECTIVE_SOURCES += main.mm \ qcocoawindow.mm \ qnsview.mm \ qnsviewaccessibility.mm \ - qcocoaautoreleasepool.mm \ qnswindowdelegate.mm \ qcocoanativeinterface.mm \ qcocoaeventdispatcher.mm \ @@ -48,7 +47,6 @@ HEADERS += qcocoaintegration.h \ qcocoabackingstore.h \ qcocoawindow.h \ qnsview.h \ - qcocoaautoreleasepool.h \ qnswindowdelegate.h \ qcocoanativeinterface.h \ qcocoaeventdispatcher.h \ diff --git a/src/plugins/platforms/cocoa/main.mm b/src/plugins/platforms/cocoa/main.mm index b7e8fa1fca..43ff715161 100644 --- a/src/plugins/platforms/cocoa/main.mm +++ b/src/plugins/platforms/cocoa/main.mm @@ -50,11 +50,9 @@ public: QPlatformIntegration * QCocoaIntegrationPlugin::create(const QString& system, const QStringList& paramList) { - Q_UNUSED(paramList); - - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; if (system.compare(QLatin1String("cocoa"), Qt::CaseInsensitive) == 0) - return new QCocoaIntegration; + return new QCocoaIntegration(paramList); return 0; } diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibility.h b/src/plugins/platforms/cocoa/qcocoaaccessibility.h index 061dfac156..228643ab26 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibility.h +++ b/src/plugins/platforms/cocoa/qcocoaaccessibility.h @@ -38,6 +38,8 @@ #include <QtGui> #include <qpa/qplatformaccessibility.h> +#ifndef QT_NO_ACCESSIBILITY + QT_BEGIN_NAMESPACE class QCocoaAccessibility : public QPlatformAccessibility @@ -45,10 +47,10 @@ class QCocoaAccessibility : public QPlatformAccessibility public: QCocoaAccessibility(); ~QCocoaAccessibility(); - void notifyAccessibilityUpdate(QAccessibleEvent *event); - void setRootObject(QObject *o); - void initialize(); - void cleanup(); + void notifyAccessibilityUpdate(QAccessibleEvent *event) Q_DECL_OVERRIDE; + void setRootObject(QObject *o) Q_DECL_OVERRIDE; + void initialize() Q_DECL_OVERRIDE; + void cleanup() Q_DECL_OVERRIDE; }; namespace QCocoaAccessible { @@ -85,4 +87,6 @@ id getValueAttribute(QAccessibleInterface *interface); QT_END_NAMESPACE -#endif +#endif // QT_NO_ACCESSIBILITY + +#endif // QCOCOAACCESIBILITY_H diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibility.mm b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm index 03f585d19d..4e901ba015 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibility.mm +++ b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm @@ -37,6 +37,8 @@ QT_BEGIN_NAMESPACE +#ifndef QT_NO_ACCESSIBILITY + QCocoaAccessibility::QCocoaAccessibility() { @@ -374,5 +376,7 @@ id getValueAttribute(QAccessibleInterface *interface) } // namespace QCocoaAccessible +#endif // QT_NO_ACCESSIBILITY + QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h index 9f56d306f4..73aeae129b 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h +++ b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.h @@ -37,6 +37,8 @@ #include "qt_mac_p.h" +#ifndef QT_NO_ACCESSIBILITY + #import <Cocoa/Cocoa.h> #import <AppKit/NSAccessibility.h> @@ -56,4 +58,6 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); -#endif +#endif // QT_NO_ACCESSIBILITY + +#endif // QCOCOAACCESIBILITYELEMENT_H diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm index 6769f4ab0c..b55393c7dc 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm +++ b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm @@ -42,6 +42,8 @@ QT_USE_NAMESPACE +#ifndef QT_NO_ACCESSIBILITY + static void convertLineOffset(QAccessibleTextInterface *text, int &line, int &offset, NSUInteger *start = 0, NSUInteger *end = 0) { Q_ASSERT(line == -1 || offset == -1); @@ -588,3 +590,5 @@ static void convertLineOffset(QAccessibleTextInterface *text, int &line, int &of } @end + +#endif // QT_NO_ACCESSIBILITY diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h index 04e51d5392..abaaba91a5 100644 --- a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h +++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.h @@ -100,6 +100,7 @@ - (void)setReflectionDelegate:(NSObject <NSApplicationDelegate> *)oldDelegate; - (void)getUrl:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent; - (void) removeAppleEventHandlers; +- (bool) inLaunch; @end QT_NAMESPACE_ALIAS_OBJC_CLASS(QCocoaApplicationDelegate); diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm index 6c673a4f5d..caa8884661 100644 --- a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm +++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm @@ -221,12 +221,8 @@ QT_END_NAMESPACE const QWindowList topLevels = QGuiApplication::topLevelWindows(); for (int i = 0; i < topLevels.size(); ++i) { QWindow *topLevelWindow = topLevels.at(i); - // Widgets have alreay received a CloseEvent from the QApplication - // QCloseEvent handler. (see canQuit above). Prevent running the - // CloseEvent logic twice, call close() directly. - if (topLevelWindow->inherits("QWidgetWindow")) - topLevelWindow->close(); - else + // Already closed windows will not have a platform window, skip those + if (topLevelWindow->handle()) QWindowSystemInterface::handleCloseEvent(topLevelWindow); } QWindowSystemInterface::flushWindowSystemEvents(); @@ -284,6 +280,11 @@ QT_END_NAMESPACE [eventManager removeEventHandlerForEventClass:kInternetEventClass andEventID:kAEGetURL]; } +- (bool) inLaunch +{ + return inLaunch; +} + - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { Q_UNUSED(aNotification); diff --git a/src/plugins/platforms/cocoa/qcocoaautoreleasepool.mm b/src/plugins/platforms/cocoa/qcocoaautoreleasepool.mm deleted file mode 100644 index 8f30365186..0000000000 --- a/src/plugins/platforms/cocoa/qcocoaautoreleasepool.mm +++ /dev/null @@ -1,48 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qcocoaautoreleasepool.h" - -QT_BEGIN_NAMESPACE - -QCocoaAutoReleasePool::QCocoaAutoReleasePool() -{ - pool = [[NSAutoreleasePool alloc] init]; -} - -QCocoaAutoReleasePool::~QCocoaAutoReleasePool() -{ - [pool release]; -} - -QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.h b/src/plugins/platforms/cocoa/qcocoabackingstore.h index 3737584c4c..5a199de4a5 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.h +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.h @@ -49,12 +49,12 @@ public: QCocoaBackingStore(QWindow *window); ~QCocoaBackingStore(); - QPaintDevice *paintDevice(); - void flush(QWindow *widget, const QRegion ®ion, const QPoint &offset); - QImage toImage() const; - void resize (const QSize &size, const QRegion &); - bool scroll(const QRegion &area, int dx, int dy); - void beginPaint(const QRegion ®ion); + QPaintDevice *paintDevice() Q_DECL_OVERRIDE; + void flush(QWindow *widget, const QRegion ®ion, const QPoint &offset) Q_DECL_OVERRIDE; + QImage toImage() const Q_DECL_OVERRIDE; + void resize (const QSize &size, const QRegion &) Q_DECL_OVERRIDE; + bool scroll(const QRegion &area, int dx, int dy) Q_DECL_OVERRIDE; + void beginPaint(const QRegion ®ion) Q_DECL_OVERRIDE; qreal getBackingStoreDevicePixelRatio(); private: diff --git a/src/plugins/platforms/cocoa/qcocoaclipboard.h b/src/plugins/platforms/cocoa/qcocoaclipboard.h index e3df9a99b1..e53942b068 100644 --- a/src/plugins/platforms/cocoa/qcocoaclipboard.h +++ b/src/plugins/platforms/cocoa/qcocoaclipboard.h @@ -47,10 +47,10 @@ class QCocoaClipboard : public QObject, public QPlatformClipboard public: QCocoaClipboard(); - QMimeData *mimeData(QClipboard::Mode mode = QClipboard::Clipboard); - void setMimeData(QMimeData *data, QClipboard::Mode mode = QClipboard::Clipboard); - bool supportsMode(QClipboard::Mode mode) const; - bool ownsMode(QClipboard::Mode mode) const; + QMimeData *mimeData(QClipboard::Mode mode = QClipboard::Clipboard) Q_DECL_OVERRIDE; + void setMimeData(QMimeData *data, QClipboard::Mode mode = QClipboard::Clipboard) Q_DECL_OVERRIDE; + bool supportsMode(QClipboard::Mode mode) const Q_DECL_OVERRIDE; + bool ownsMode(QClipboard::Mode mode) const Q_DECL_OVERRIDE; private Q_SLOTS: void handleApplicationStateChanged(Qt::ApplicationState state); diff --git a/src/plugins/platforms/cocoa/qcocoacolordialoghelper.h b/src/plugins/platforms/cocoa/qcocoacolordialoghelper.h index 3183705330..705f97cd2a 100644 --- a/src/plugins/platforms/cocoa/qcocoacolordialoghelper.h +++ b/src/plugins/platforms/cocoa/qcocoacolordialoghelper.h @@ -43,14 +43,14 @@ class QCocoaColorDialogHelper : public QPlatformColorDialogHelper { public: QCocoaColorDialogHelper(); - virtual ~QCocoaColorDialogHelper(); + ~QCocoaColorDialogHelper(); - void exec(); - bool show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent); - void hide(); + void exec() Q_DECL_OVERRIDE; + bool show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) Q_DECL_OVERRIDE; + void hide() Q_DECL_OVERRIDE; - void setCurrentColor(const QColor&); - QColor currentColor() const; + void setCurrentColor(const QColor&) Q_DECL_OVERRIDE; + QColor currentColor() const Q_DECL_OVERRIDE; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoacursor.h b/src/plugins/platforms/cocoa/qcocoacursor.h index cc6cbaf59e..d104939f0c 100644 --- a/src/plugins/platforms/cocoa/qcocoacursor.h +++ b/src/plugins/platforms/cocoa/qcocoacursor.h @@ -47,9 +47,9 @@ public: QCocoaCursor(); ~QCocoaCursor(); - virtual void changeCursor(QCursor *cursor, QWindow *window); - virtual QPoint pos() const; - virtual void setPos(const QPoint &position); + void changeCursor(QCursor *cursor, QWindow *window) Q_DECL_OVERRIDE; + QPoint pos() const Q_DECL_OVERRIDE; + void setPos(const QPoint &position) Q_DECL_OVERRIDE; private: QHash<Qt::CursorShape, NSCursor *> m_cursors; NSCursor *convertCursor(QCursor *cursor); diff --git a/src/plugins/platforms/cocoa/qcocoacursor.mm b/src/plugins/platforms/cocoa/qcocoacursor.mm index f08386d18e..8e38181c29 100644 --- a/src/plugins/platforms/cocoa/qcocoacursor.mm +++ b/src/plugins/platforms/cocoa/qcocoacursor.mm @@ -34,7 +34,6 @@ #include "qcocoacursor.h" #include "qcocoawindow.h" #include "qcocoahelpers.h" -#include "qcocoaautoreleasepool.h" #include <QtGui/QBitmap> diff --git a/src/plugins/platforms/cocoa/qcocoadrag.h b/src/plugins/platforms/cocoa/qcocoadrag.h index e087fcee26..c1eeb34679 100644 --- a/src/plugins/platforms/cocoa/qcocoadrag.h +++ b/src/plugins/platforms/cocoa/qcocoadrag.h @@ -47,13 +47,13 @@ class QCocoaDrag : public QPlatformDrag { public: QCocoaDrag(); - virtual ~QCocoaDrag(); + ~QCocoaDrag(); - virtual QMimeData *platformDropData(); - virtual Qt::DropAction drag(QDrag *m_drag); + QMimeData *platformDropData() Q_DECL_OVERRIDE; + Qt::DropAction drag(QDrag *m_drag) Q_DECL_OVERRIDE; - virtual Qt::DropAction defaultAction(Qt::DropActions possibleActions, - Qt::KeyboardModifiers modifiers) const; + Qt::DropAction defaultAction(Qt::DropActions possibleActions, + Qt::KeyboardModifiers modifiers) const Q_DECL_OVERRIDE; /** * to meet NSView dragImage:at guarantees, we need to record the original diff --git a/src/plugins/platforms/cocoa/qcocoadrag.mm b/src/plugins/platforms/cocoa/qcocoadrag.mm index 4466d28128..8aa7a6b583 100644 --- a/src/plugins/platforms/cocoa/qcocoadrag.mm +++ b/src/plugins/platforms/cocoa/qcocoadrag.mm @@ -127,16 +127,17 @@ Qt::DropAction QCocoaDrag::drag(QDrag *o) dragBoard.setMimeData(m_drag->mimeData(), QMacPasteboard::LazyRequest); NSPoint event_location = [m_lastEvent locationInWindow]; - NSPoint local_point = [m_lastView convertPoint:event_location fromView:nil]; - local_point.x -= hotSpot.x(); + NSWindow *theWindow = [m_lastEvent window]; + Q_ASSERT(theWindow != nil); + event_location.x -= hotSpot.x(); CGFloat flippedY = pm.height() - hotSpot.y(); - local_point.y += flippedY; - NSSize mouseOffset = NSMakeSize(0.0, 0.0); + event_location.y -= flippedY; + NSSize mouseOffset_unused = NSMakeSize(0.0, 0.0); NSPasteboard *pboard = [NSPasteboard pasteboardWithName:NSDragPboard]; - [m_lastView dragImage:nsimage - at:local_point - offset:mouseOffset + [theWindow dragImage:nsimage + at:event_location + offset:mouseOffset_unused event:m_lastEvent pasteboard:pboard source:m_lastView diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h index 13a8c89dbb..8a2a478a72 100644 --- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h +++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h @@ -80,12 +80,11 @@ // #include <QtCore/qabstracteventdispatcher.h> -#include <QtCore/qhash.h> #include <QtCore/qstack.h> #include <QtGui/qwindowdefs.h> #include <QtCore/private/qabstracteventdispatcher_p.h> +#include <QtCore/private/qcfsocketnotifier_p.h> #include <QtCore/private/qtimerinfo_unix_p.h> -#include <QtPlatformSupport/private/qcfsocketnotifier_p.h> #include <CoreFoundation/CoreFoundation.h> diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm index 52b2e23345..1865624d57 100644 --- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm +++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm @@ -66,13 +66,11 @@ ****************************************************************************/ #include "qcocoaeventdispatcher.h" -#include "qcocoaautoreleasepool.h" #include "qcocoawindow.h" #include "qcocoahelpers.h" #include "qguiapplication.h" #include "qevent.h" -#include "qhash.h" #include "qmutex.h" #include "qsocketnotifier.h" #include <qpa/qplatformwindow.h> @@ -365,7 +363,7 @@ bool QCocoaEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags) if (d->interrupt) break; - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; NSEvent* event = 0; // First, send all previously excluded input events, if any: @@ -623,7 +621,7 @@ NSModalSession QCocoaEventDispatcherPrivate::currentModalSession() continue; if (!info.session) { - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(info.window->handle()); NSWindow *nswindow = cocoaWindow->nativeWindow(); if (!nswindow) @@ -671,7 +669,7 @@ void QCocoaEventDispatcherPrivate::updateChildrenWorksWhenModal() // Make the dialog children of the window // active. And make the dialog children of // the previous modal dialog unactive again: - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; int size = cocoaModalSessionStack.size(); if (size > 0){ if (QWindow *prevModal = cocoaModalSessionStack[size-1].window) @@ -692,7 +690,7 @@ void QCocoaEventDispatcherPrivate::cleanupModalSessions() // point they were marked as stopped), is that ending a session // when no other session runs below it on the stack will make cocoa // drop some events on the floor. - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; int stackSize = cocoaModalSessionStack.size(); for (int i=stackSize-1; i>=0; --i) { @@ -927,7 +925,7 @@ void QCocoaEventDispatcherPrivate::cancelWaitForMoreEvents() { // In case the event dispatcher is waiting for more // events somewhere, we post a dummy event to wake it up: - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined location:NSZeroPoint modifierFlags:0 timestamp:0. windowNumber:0 context:0 subtype:QtCocoaEventSubTypeWakeup data1:0 data2:0] atStart:NO]; diff --git a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm index 4ece1b5a22..9dc013ba4d 100644 --- a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm +++ b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm @@ -53,7 +53,6 @@ #include <qvarlengtharray.h> #include <stdlib.h> #include <qabstracteventdispatcher.h> -#include "qcocoaautoreleasepool.h" #include <QDir> #include <qpa/qplatformnativeinterface.h> @@ -557,7 +556,7 @@ QCocoaFileDialogHelper::~QCocoaFileDialogHelper() { if (!mDelegate) return; - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; [mDelegate release]; mDelegate = 0; } @@ -589,9 +588,6 @@ void QCocoaFileDialogHelper::QNSOpenSavePanelDelegate_filterSelected(int menuInd emit filterSelected(menuIndex >= 0 && menuIndex < filters.size() ? filters.at(menuIndex) : QString()); } -extern OSErr qt_mac_create_fsref(const QString &, FSRef *); // qglobal.cpp -extern void qt_mac_to_pascal_string(QString s, Str255 str, TextEncoding encoding=0, int len=-1); // qglobal.cpp - void QCocoaFileDialogHelper::setDirectory(const QUrl &directory) { if (mDelegate) @@ -687,7 +683,7 @@ bool QCocoaFileDialogHelper::show(Qt::WindowFlags windowFlags, Qt::WindowModalit void QCocoaFileDialogHelper::createNSOpenSavePanelDelegate() { - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; const SharedPointerFileDialogOptions &opts = options(); const QList<QUrl> selectedFiles = opts->initiallySelectedFiles(); @@ -737,7 +733,7 @@ void QCocoaFileDialogHelper::exec() // QEventLoop has been interrupted, and the second-most event loop has not // yet been reactivated (regardless if [NSApp run] is still on the stack)), // showing a native modal dialog will fail. - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; if ([mDelegate runApplicationModalPanel]) emit accept(); else diff --git a/src/plugins/platforms/cocoa/qcocoafontdialoghelper.h b/src/plugins/platforms/cocoa/qcocoafontdialoghelper.h index 5a241bc242..23266cfa79 100644 --- a/src/plugins/platforms/cocoa/qcocoafontdialoghelper.h +++ b/src/plugins/platforms/cocoa/qcocoafontdialoghelper.h @@ -45,13 +45,13 @@ public: QCocoaFontDialogHelper(); ~QCocoaFontDialogHelper(); - void exec(); + void exec() Q_DECL_OVERRIDE; - bool show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent); - void hide(); + bool show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) Q_DECL_OVERRIDE; + void hide() Q_DECL_OVERRIDE; - void setCurrentFont(const QFont &); - QFont currentFont() const; + void setCurrentFont(const QFont &) Q_DECL_OVERRIDE; + QFont currentFont() const Q_DECL_OVERRIDE; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoaglcontext.h b/src/plugins/platforms/cocoa/qcocoaglcontext.h index 2c8a3e39f3..fa6db018a7 100644 --- a/src/plugins/platforms/cocoa/qcocoaglcontext.h +++ b/src/plugins/platforms/cocoa/qcocoaglcontext.h @@ -50,22 +50,22 @@ public: QCocoaGLContext(const QSurfaceFormat &format, QPlatformOpenGLContext *share, const QVariant &nativeHandle); ~QCocoaGLContext(); - QSurfaceFormat format() const; + QSurfaceFormat format() const Q_DECL_OVERRIDE; - void swapBuffers(QPlatformSurface *surface); + void swapBuffers(QPlatformSurface *surface) Q_DECL_OVERRIDE; - bool makeCurrent(QPlatformSurface *surface); - void doneCurrent(); + bool makeCurrent(QPlatformSurface *surface) Q_DECL_OVERRIDE; + void doneCurrent() Q_DECL_OVERRIDE; - void (*getProcAddress(const QByteArray &procName)) (); + void (*getProcAddress(const QByteArray &procName)) () Q_DECL_OVERRIDE; void update(); static NSOpenGLPixelFormat *createNSOpenGLPixelFormat(const QSurfaceFormat &format); NSOpenGLContext *nsOpenGLContext() const; - bool isSharing() const; - bool isValid() const; + bool isSharing() const Q_DECL_OVERRIDE; + bool isValid() const Q_DECL_OVERRIDE; void windowWasHidden(); diff --git a/src/plugins/platforms/cocoa/qcocoaglcontext.mm b/src/plugins/platforms/cocoa/qcocoaglcontext.mm index a3c72c58b9..0f9b8b900d 100644 --- a/src/plugins/platforms/cocoa/qcocoaglcontext.mm +++ b/src/plugins/platforms/cocoa/qcocoaglcontext.mm @@ -33,7 +33,6 @@ #include "qcocoaglcontext.h" #include "qcocoawindow.h" -#include "qcocoaautoreleasepool.h" #include "qcocoahelpers.h" #include <qdebug.h> #include <QtCore/private/qcore_mac_p.h> @@ -145,21 +144,27 @@ QCocoaGLContext::QCocoaGLContext(const QSurfaceFormat &format, QPlatformOpenGLCo if (m_format.renderableType() != QSurfaceFormat::OpenGL) return; - QCocoaAutoReleasePool pool; // For the SG Canvas render thread + QMacAutoReleasePool pool; // For the SG Canvas render thread + // create native context for the requested pixel format and share NSOpenGLPixelFormat *pixelFormat = static_cast <NSOpenGLPixelFormat *>(qcgl_createNSOpenGLPixelFormat(m_format)); m_shareContext = share ? static_cast<QCocoaGLContext *>(share)->nsOpenGLContext() : nil; + m_context = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:m_shareContext]; - m_context = [NSOpenGLContext alloc]; - [m_context initWithFormat:pixelFormat shareContext:m_shareContext]; - + // retry without sharing on context creation failure. if (!m_context && m_shareContext) { - // try without shared context m_shareContext = nil; - [m_context initWithFormat:pixelFormat shareContext:nil]; + m_context = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil]; + if (m_context) + qWarning("QCocoaGLContext: Falling back to unshared context."); } + // give up if we still did not get a native context [pixelFormat release]; + if (!m_context) { + qWarning("QCocoaGLContext: Failed to create context."); + return; + } const GLint interval = format.swapInterval() >= 0 ? format.swapInterval() : 1; [m_context setValues:&interval forParameter:NSOpenGLCPSwapInterval]; @@ -218,7 +223,7 @@ bool QCocoaGLContext::makeCurrent(QPlatformSurface *surface) { Q_ASSERT(surface->surface()->supportsOpenGL()); - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; QWindow *window = static_cast<QCocoaWindow *>(surface)->window(); setActiveWindow(window); diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm index 5f97e2996c..f51c21ee9b 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.mm +++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm @@ -33,7 +33,6 @@ #include "qcocoahelpers.h" -#include "qcocoaautoreleasepool.h" #include <QtCore> #include <QtGui> @@ -270,10 +269,14 @@ bool operator<(const KeyPair &entry, const Qt::Key &key) return entry.qtKey < key; } -static bool qtKey2CocoaKeySortLessThan(const KeyPair &entry1, const KeyPair &entry2) +struct qtKey2CocoaKeySortLessThan { - return entry1.qtKey < entry2.qtKey; -} + typedef bool result_type; + Q_DECL_CONSTEXPR result_type operator()(const KeyPair &entry1, const KeyPair &entry2) const Q_DECL_NOTHROW + { + return entry1.qtKey < entry2.qtKey; + } +}; static const int NumEntries = 59; static const KeyPair entries[NumEntries] = { @@ -353,7 +356,7 @@ QChar qt_mac_qtKey2CocoaKey(Qt::Key key) mustInit = false; for (int i=0; i<NumEntries; ++i) rev_entries[i] = entries[i]; - std::sort(rev_entries.begin(), rev_entries.end(), qtKey2CocoaKeySortLessThan); + std::sort(rev_entries.begin(), rev_entries.end(), qtKey2CocoaKeySortLessThan()); } const QVector<KeyPair>::iterator i = std::lower_bound(rev_entries.begin(), rev_entries.end(), key); @@ -619,7 +622,7 @@ QString qt_mac_applicationName() if (appName.isEmpty()) { QString arg0 = QGuiApplicationPrivate::instance()->appName(); if (arg0.contains("/")) { - QStringList parts = arg0.split("/"); + QStringList parts = arg0.split(QLatin1Char('/')); appName = parts.at(parts.count() - 1); } else { appName = arg0; @@ -630,11 +633,15 @@ QString qt_mac_applicationName() int qt_mac_mainScreenHeight() { - QCocoaAutoReleasePool pool; - // The first screen in the screens array is documented - // to have the (0,0) origin. - NSRect screenFrame = [[[NSScreen screens] firstObject] frame]; - return screenFrame.size.height; + QMacAutoReleasePool pool; + NSArray *screens = [NSScreen screens]; + if ([screens count] > 0) { + // The first screen in the screens array is documented + // to have the (0,0) origin. + NSRect screenFrame = [[screens objectAtIndex: 0] frame]; + return screenFrame.size.height; + } + return 0; } int qt_mac_flipYCoordinate(int y) diff --git a/src/plugins/platforms/cocoa/qcocoainputcontext.h b/src/plugins/platforms/cocoa/qcocoainputcontext.h index abe45344a9..054c4795cb 100644 --- a/src/plugins/platforms/cocoa/qcocoainputcontext.h +++ b/src/plugins/platforms/cocoa/qcocoainputcontext.h @@ -35,6 +35,7 @@ #define QCOCOAINPUTCONTEXT_H #include <qpa/qplatforminputcontext.h> +#include <QtCore/QLocale> #include <QtCore/QPointer> QT_BEGIN_NAMESPACE @@ -46,9 +47,12 @@ public: explicit QCocoaInputContext(); ~QCocoaInputContext(); - virtual bool isValid() const { return true; } + bool isValid() const Q_DECL_OVERRIDE { return true; } - virtual void reset(); + void reset() Q_DECL_OVERRIDE; + + QLocale locale() const Q_DECL_OVERRIDE { return m_locale; } + void updateLocale(); private Q_SLOTS: void connectSignals(); @@ -56,6 +60,7 @@ private Q_SLOTS: private: QPointer<QWindow> mWindow; + QLocale m_locale; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoainputcontext.mm b/src/plugins/platforms/cocoa/qcocoainputcontext.mm index c22fe8774b..7d01826ffe 100644 --- a/src/plugins/platforms/cocoa/qcocoainputcontext.mm +++ b/src/plugins/platforms/cocoa/qcocoainputcontext.mm @@ -34,9 +34,10 @@ #include "qnsview.h" #include "qcocoainputcontext.h" #include "qcocoanativeinterface.h" -#include "qcocoaautoreleasepool.h" #include "qcocoawindow.h" +#include <Carbon/Carbon.h> + #include <QtCore/QRect> #include <QtGui/QGuiApplication> #include <QtGui/QWindow> @@ -77,6 +78,7 @@ QCocoaInputContext::QCocoaInputContext() , mWindow(QGuiApplication::focusWindow()) { QMetaObject::invokeMethod(this, "connectSignals", Qt::QueuedConnection); + updateLocale(); } QCocoaInputContext::~QCocoaInputContext() @@ -98,7 +100,7 @@ void QCocoaInputContext::reset() if (!view) return; - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; if (NSTextInputContext *ctxt = [NSTextInputContext currentInputContext]) { [ctxt discardMarkedText]; [view unmarkText]; @@ -117,4 +119,20 @@ void QCocoaInputContext::focusObjectChanged(QObject *focusObject) mWindow = QGuiApplication::focusWindow(); } +void QCocoaInputContext::updateLocale() +{ + TISInputSourceRef source = TISCopyCurrentKeyboardInputSource(); + CFArrayRef languages = (CFArrayRef) TISGetInputSourceProperty(source, kTISPropertyInputSourceLanguages); + if (CFArrayGetCount(languages) > 0) { + CFStringRef langRef = (CFStringRef)CFArrayGetValueAtIndex(languages, 0); + QString name = QCFString::toQString(langRef); + QLocale locale(name); + if (m_locale != locale) { + m_locale = locale; + emitLocaleChanged(); + } + CFRelease(langRef); + } +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.h b/src/plugins/platforms/cocoa/qcocoaintegration.h index 8b5d78826c..e7e21c356a 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.h +++ b/src/plugins/platforms/cocoa/qcocoaintegration.h @@ -36,7 +36,6 @@ #include <Cocoa/Cocoa.h> -#include "qcocoaautoreleasepool.h" #include "qcocoacursor.h" #include "qcocoawindow.h" #include "qcocoanativeinterface.h" @@ -74,6 +73,7 @@ public: QPlatformCursor *cursor() const Q_DECL_OVERRIDE { return m_cursor; } QWindow *topLevelAt(const QPoint &point) const Q_DECL_OVERRIDE; QList<QPlatformScreen *> virtualSiblings() const Q_DECL_OVERRIDE { return m_siblings; } + QPlatformScreen::SubpixelAntialiasingType subpixelAntialiasingTypeHint() const Q_DECL_OVERRIDE; // ---------------------------------------------------- // Additional methods @@ -98,34 +98,42 @@ public: class QCocoaIntegration : public QPlatformIntegration { public: - QCocoaIntegration(); + enum Option { + UseFreeTypeFontEngine = 0x1 + }; + Q_DECLARE_FLAGS(Options, Option) + + QCocoaIntegration(const QStringList ¶mList); ~QCocoaIntegration(); static QCocoaIntegration *instance(); + Options options() const; - bool hasCapability(QPlatformIntegration::Capability cap) const; - QPlatformWindow *createPlatformWindow(QWindow *window) const; + bool hasCapability(QPlatformIntegration::Capability cap) const Q_DECL_OVERRIDE; + QPlatformWindow *createPlatformWindow(QWindow *window) const Q_DECL_OVERRIDE; #ifndef QT_NO_OPENGL - QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const; + QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const Q_DECL_OVERRIDE; #endif - QPlatformBackingStore *createPlatformBackingStore(QWindow *widget) const; + QPlatformBackingStore *createPlatformBackingStore(QWindow *widget) const Q_DECL_OVERRIDE; - QAbstractEventDispatcher *createEventDispatcher() const; + QAbstractEventDispatcher *createEventDispatcher() const Q_DECL_OVERRIDE; - QCoreTextFontDatabase *fontDatabase() const; - QCocoaNativeInterface *nativeInterface() const; - QCocoaInputContext *inputContext() const; - QCocoaAccessibility *accessibility() const; - QCocoaClipboard *clipboard() const; - QCocoaDrag *drag() const; + QCoreTextFontDatabase *fontDatabase() const Q_DECL_OVERRIDE; + QCocoaNativeInterface *nativeInterface() const Q_DECL_OVERRIDE; + QPlatformInputContext *inputContext() const Q_DECL_OVERRIDE; +#ifndef QT_NO_ACCESSIBILITY + QCocoaAccessibility *accessibility() const Q_DECL_OVERRIDE; +#endif + QCocoaClipboard *clipboard() const Q_DECL_OVERRIDE; + QCocoaDrag *drag() const Q_DECL_OVERRIDE; - QStringList themeNames() const; - QPlatformTheme *createPlatformTheme(const QString &name) const; - QCocoaServices *services() const; - QVariant styleHint(StyleHint hint) const; + QStringList themeNames() const Q_DECL_OVERRIDE; + QPlatformTheme *createPlatformTheme(const QString &name) const Q_DECL_OVERRIDE; + QCocoaServices *services() const Q_DECL_OVERRIDE; + QVariant styleHint(StyleHint hint) const Q_DECL_OVERRIDE; - Qt::KeyboardModifiers queryKeyboardModifiers() const; - QList<int> possibleKeys(const QKeyEvent *event) const; + Qt::KeyboardModifiers queryKeyboardModifiers() const Q_DECL_OVERRIDE; + QList<int> possibleKeys(const QKeyEvent *event) const Q_DECL_OVERRIDE; void updateScreens(); QCocoaScreen *screenAtIndex(int index); @@ -139,13 +147,14 @@ public: QCocoaWindow *activePopupWindow() const; QList<QCocoaWindow *> *popupWindowStack(); - void setApplicationIcon(const QIcon &icon) const; + void setApplicationIcon(const QIcon &icon) const Q_DECL_OVERRIDE; private: static QCocoaIntegration *mInstance; + Options mOptions; QScopedPointer<QCoreTextFontDatabase> mFontDb; - QScopedPointer<QCocoaInputContext> mInputContext; + QScopedPointer<QPlatformInputContext> mInputContext; #ifndef QT_NO_ACCESSIBILITY QScopedPointer<QCocoaAccessibility> mAccessibility; #endif @@ -161,6 +170,8 @@ private: QList<QCocoaWindow *> m_popupWindowStack; }; +Q_DECLARE_OPERATORS_FOR_FLAGS(QCocoaIntegration::Options) + QT_END_NAMESPACE #endif diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index c8f6dd05db..6bec6b191d 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.mm +++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm @@ -33,7 +33,6 @@ #include "qcocoaintegration.h" -#include "qcocoaautoreleasepool.h" #include "qcocoawindow.h" #include "qcocoabackingstore.h" #include "qcocoanativeinterface.h" @@ -48,7 +47,9 @@ #include "qcocoamimetypes.h" #include "qcocoaaccessibility.h" +#include <qpa/qplatforminputcontextfactory_p.h> #include <qpa/qplatformaccessibility.h> +#include <qpa/qplatforminputcontextfactory_p.h> #include <QtCore/qcoreapplication.h> #include <IOKit/graphics/IOGraphicsLib.h> @@ -137,11 +138,21 @@ void QCocoaScreen::updateGeometry() qreal QCocoaScreen::devicePixelRatio() const { - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; NSScreen * screen = osScreen(); return qreal(screen ? [screen backingScaleFactor] : 1.0); } +QPlatformScreen::SubpixelAntialiasingType QCocoaScreen::subpixelAntialiasingTypeHint() const +{ + QPlatformScreen::SubpixelAntialiasingType type = QPlatformScreen::subpixelAntialiasingTypeHint(); + if (type == QPlatformScreen::Subpixel_None) { + // Every OSX machine has RGB pixels unless a peculiar or rotated non-Apple screen is attached + type = QPlatformScreen::Subpixel_RGB; + } + return type; +} + QWindow *QCocoaScreen::topLevelAt(const QPoint &point) const { NSPoint screenPoint = qt_mac_flipPoint(point); @@ -244,11 +255,25 @@ QPixmap QCocoaScreen::grabWindow(WId window, int x, int y, int width, int height return windowPixmap; } +static QCocoaIntegration::Options parseOptions(const QStringList ¶mList) +{ + QCocoaIntegration::Options options; + foreach (const QString ¶m, paramList) { +#ifndef QT_NO_FREETYPE + if (param == QLatin1String("fontengine=freetype")) + options |= QCocoaIntegration::UseFreeTypeFontEngine; + else +#endif + qWarning() << "Unknown option" << param; + } + return options; +} + QCocoaIntegration *QCocoaIntegration::mInstance = 0; -QCocoaIntegration::QCocoaIntegration() - : mFontDb(new QCoreTextFontDatabase()) - , mInputContext(new QCocoaInputContext) +QCocoaIntegration::QCocoaIntegration(const QStringList ¶mList) + : mOptions(parseOptions(paramList)) + , mFontDb(new QCoreTextFontDatabase(mOptions.testFlag(UseFreeTypeFontEngine))) #ifndef QT_NO_ACCESSIBILITY , mAccessibility(new QCocoaAccessibility) #endif @@ -262,8 +287,12 @@ QCocoaIntegration::QCocoaIntegration() qWarning("Creating multiple Cocoa platform integrations is not supported"); mInstance = this; + QString icStr = QPlatformInputContextFactory::requested(); + icStr.isNull() ? mInputContext.reset(new QCocoaInputContext) + : mInputContext.reset(QPlatformInputContextFactory::create(icStr)); + initResources(); - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; qApp->setAttribute(Qt::AA_DontUseNativeMenuBar, false); @@ -314,7 +343,7 @@ QCocoaIntegration::~QCocoaIntegration() qt_resetNSApplicationSendEvent(); - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; if (!QCoreApplication::testAttribute(Qt::AA_MacPluginApplication)) { // remove the apple event handlers installed by QCocoaApplicationDelegate QCocoaApplicationDelegate *delegate = [QCocoaApplicationDelegate sharedDelegate]; @@ -342,6 +371,11 @@ QCocoaIntegration *QCocoaIntegration::instance() return mInstance; } +QCocoaIntegration::Options QCocoaIntegration::options() const +{ + return mOptions; +} + /*! \brief Synchronizes the screen list, adds new screens, removes deleted ones */ @@ -463,19 +497,17 @@ QCocoaNativeInterface *QCocoaIntegration::nativeInterface() const return mNativeInterface.data(); } -QCocoaInputContext *QCocoaIntegration::inputContext() const +QPlatformInputContext *QCocoaIntegration::inputContext() const { return mInputContext.data(); } +#ifndef QT_NO_ACCESSIBILITY QCocoaAccessibility *QCocoaIntegration::accessibility() const { -#ifndef QT_NO_ACCESSIBILITY return mAccessibility.data(); -#else - return 0; -#endif } +#endif QCocoaClipboard *QCocoaIntegration::clipboard() const { diff --git a/src/plugins/platforms/cocoa/qcocoamenu.h b/src/plugins/platforms/cocoa/qcocoamenu.h index 59807deb5a..eccc5230b5 100644 --- a/src/plugins/platforms/cocoa/qcocoamenu.h +++ b/src/plugins/platforms/cocoa/qcocoamenu.h @@ -49,29 +49,29 @@ public: QCocoaMenu(); ~QCocoaMenu(); - inline virtual void setTag(quintptr tag) - { m_tag = tag; } - inline virtual quintptr tag() const - { return m_tag; } - - void insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before); - void removeMenuItem(QPlatformMenuItem *menuItem); - void syncMenuItem(QPlatformMenuItem *menuItem); - void setEnabled(bool enabled); - bool isEnabled() const; - void setVisible(bool visible); - void showPopup(const QWindow *parentWindow, const QRect &targetRect, const QPlatformMenuItem *item); - void dismiss(); - - void syncSeparatorsCollapsible(bool enable); + void setTag(quintptr tag) Q_DECL_OVERRIDE + { m_tag = tag; } + quintptr tag() const Q_DECL_OVERRIDE + { return m_tag; } + + void insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before) Q_DECL_OVERRIDE; + void removeMenuItem(QPlatformMenuItem *menuItem) Q_DECL_OVERRIDE; + void syncMenuItem(QPlatformMenuItem *menuItem) Q_DECL_OVERRIDE; + void setEnabled(bool enabled) Q_DECL_OVERRIDE; + bool isEnabled() const Q_DECL_OVERRIDE; + void setVisible(bool visible) Q_DECL_OVERRIDE; + void showPopup(const QWindow *parentWindow, const QRect &targetRect, const QPlatformMenuItem *item) Q_DECL_OVERRIDE; + void dismiss() Q_DECL_OVERRIDE; + + void syncSeparatorsCollapsible(bool enable) Q_DECL_OVERRIDE; void syncModalState(bool modal); - virtual void setIcon(const QIcon &icon) { Q_UNUSED(icon) } + void setIcon(const QIcon &icon) Q_DECL_OVERRIDE { Q_UNUSED(icon) } - void setText(const QString &text); - void setMinimumWidth(int width); - void setFont(const QFont &font); + void setText(const QString &text) Q_DECL_OVERRIDE; + void setMinimumWidth(int width) Q_DECL_OVERRIDE; + void setFont(const QFont &font) Q_DECL_OVERRIDE; inline NSMenu *nsMenu() const { return m_nativeMenu; } @@ -80,8 +80,8 @@ public: inline bool isVisible() const { return m_visible; } - virtual QPlatformMenuItem *menuItemAt(int position) const; - virtual QPlatformMenuItem *menuItemForTag(quintptr tag) const; + QPlatformMenuItem *menuItemAt(int position) const Q_DECL_OVERRIDE; + QPlatformMenuItem *menuItemForTag(quintptr tag) const Q_DECL_OVERRIDE; QList<QCocoaMenuItem *> items() const; QList<QCocoaMenuItem *> merged() const; diff --git a/src/plugins/platforms/cocoa/qcocoamenu.mm b/src/plugins/platforms/cocoa/qcocoamenu.mm index a6157bdc3a..aa7a09805a 100644 --- a/src/plugins/platforms/cocoa/qcocoamenu.mm +++ b/src/plugins/platforms/cocoa/qcocoamenu.mm @@ -34,7 +34,6 @@ #include "qcocoamenu.h" #include "qcocoahelpers.h" -#include "qcocoaautoreleasepool.h" #include <QtCore/QtDebug> #include <QtCore/qmetaobject.h> @@ -235,7 +234,7 @@ QCocoaMenu::QCocoaMenu() : m_menuBar(0), m_containingMenuItem(0) { - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; m_delegate = [[QCocoaMenuDelegate alloc] initWithMenu:this]; m_nativeItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; @@ -255,7 +254,7 @@ QCocoaMenu::~QCocoaMenu() if (m_containingMenuItem) m_containingMenuItem->clearMenu(this); - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; [m_nativeItem setSubmenu:nil]; [m_nativeMenu release]; [m_delegate release]; @@ -264,7 +263,7 @@ QCocoaMenu::~QCocoaMenu() void QCocoaMenu::setText(const QString &text) { - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; QString stripped = qt_mac_removeAmpersandEscapes(text); [m_nativeMenu setTitle:QCFString::toNSString(stripped)]; [m_nativeItem setTitle:QCFString::toNSString(stripped)]; @@ -286,7 +285,7 @@ void QCocoaMenu::setFont(const QFont &font) void QCocoaMenu::insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before) { - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; QCocoaMenuItem *cocoaItem = static_cast<QCocoaMenuItem *>(menuItem); QCocoaMenuItem *beforeItem = static_cast<QCocoaMenuItem *>(before); @@ -340,7 +339,7 @@ void QCocoaMenu::insertNative(QCocoaMenuItem *item, QCocoaMenuItem *beforeItem) void QCocoaMenu::removeMenuItem(QPlatformMenuItem *menuItem) { - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; QCocoaMenuItem *cocoaItem = static_cast<QCocoaMenuItem *>(menuItem); if (!m_menuItems.contains(cocoaItem)) { qWarning() << Q_FUNC_INFO << "Menu does not contain the item to be removed"; @@ -370,7 +369,7 @@ QCocoaMenuItem *QCocoaMenu::itemOrNull(int index) const void QCocoaMenu::syncMenuItem(QPlatformMenuItem *menuItem) { - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; QCocoaMenuItem *cocoaItem = static_cast<QCocoaMenuItem *>(menuItem); if (!m_menuItems.contains(cocoaItem)) { qWarning() << Q_FUNC_INFO << "Item does not belong to this menu"; @@ -399,7 +398,7 @@ void QCocoaMenu::syncMenuItem(QPlatformMenuItem *menuItem) void QCocoaMenu::syncSeparatorsCollapsible(bool enable) { - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; if (enable) { bool previousIsSeparator = true; // setting to true kills all the separators placed at the top. NSMenuItem *previousItem = nil; @@ -457,7 +456,7 @@ void QCocoaMenu::setVisible(bool visible) void QCocoaMenu::showPopup(const QWindow *parentWindow, const QRect &targetRect, const QPlatformMenuItem *item) { - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; QPoint pos = QPoint(targetRect.left(), targetRect.top() + targetRect.height()); QCocoaWindow *cocoaWindow = parentWindow ? static_cast<QCocoaWindow *>(parentWindow->handle()) : 0; @@ -583,7 +582,7 @@ QList<QCocoaMenuItem *> QCocoaMenu::merged() const void QCocoaMenu::syncModalState(bool modal) { - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; if (!m_enabled) modal = true; diff --git a/src/plugins/platforms/cocoa/qcocoamenubar.h b/src/plugins/platforms/cocoa/qcocoamenubar.h index b5ee21ca52..d5f75abf34 100644 --- a/src/plugins/platforms/cocoa/qcocoamenubar.h +++ b/src/plugins/platforms/cocoa/qcocoamenubar.h @@ -48,13 +48,13 @@ class QCocoaMenuBar : public QPlatformMenuBar Q_OBJECT public: QCocoaMenuBar(); - virtual ~QCocoaMenuBar(); + ~QCocoaMenuBar(); - virtual void insertMenu(QPlatformMenu *menu, QPlatformMenu* before); - virtual void removeMenu(QPlatformMenu *menu); - virtual void syncMenu(QPlatformMenu *menuItem); - virtual void handleReparent(QWindow *newParentWindow); - virtual QPlatformMenu *menuForTag(quintptr tag) const; + void insertMenu(QPlatformMenu *menu, QPlatformMenu* before) Q_DECL_OVERRIDE; + void removeMenu(QPlatformMenu *menu) Q_DECL_OVERRIDE; + void syncMenu(QPlatformMenu *menuItem) Q_DECL_OVERRIDE; + void handleReparent(QWindow *newParentWindow) Q_DECL_OVERRIDE; + QPlatformMenu *menuForTag(quintptr tag) const Q_DECL_OVERRIDE; inline NSMenu *nsMenu() const { return m_nativeMenu; } diff --git a/src/plugins/platforms/cocoa/qcocoamenubar.mm b/src/plugins/platforms/cocoa/qcocoamenubar.mm index 764a01370d..7775cdbde6 100644 --- a/src/plugins/platforms/cocoa/qcocoamenubar.mm +++ b/src/plugins/platforms/cocoa/qcocoamenubar.mm @@ -37,7 +37,7 @@ #include "qcocoawindow.h" #include "qcocoamenuloader.h" #include "qcocoaapplication.h" // for custom application category -#include "qcocoaautoreleasepool.h" +#include "qcocoaapplicationdelegate.h" #include <QtGui/QGuiApplication> #include <QtCore/QDebug> @@ -83,7 +83,7 @@ QCocoaMenuBar::~QCocoaMenuBar() void QCocoaMenuBar::insertNativeMenu(QCocoaMenu *menu, QCocoaMenu *beforeMenu) { - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; if (beforeMenu) { NSUInteger nativeIndex = [m_nativeMenu indexOfItem:beforeMenu->nsMenuItem()]; @@ -126,7 +126,7 @@ void QCocoaMenuBar::insertMenu(QPlatformMenu *platformMenu, QPlatformMenu *befor void QCocoaMenuBar::removeNativeMenu(QCocoaMenu *menu) { - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; if (menu->menuBar() == this) menu->setMenuBar(0); @@ -147,7 +147,7 @@ void QCocoaMenuBar::removeMenu(QPlatformMenu *platformMenu) void QCocoaMenuBar::syncMenu(QPlatformMenu *menu) { - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; QCocoaMenu *cocoaMenu = static_cast<QCocoaMenu *>(menu); Q_FOREACH (QCocoaMenuItem *item, cocoaMenu->items()) @@ -260,13 +260,26 @@ void QCocoaMenuBar::resetKnownMenuItemsToQt() void QCocoaMenuBar::updateMenuBarImmediately() { - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; QCocoaMenuBar *mb = findGlobalMenubar(); QCocoaWindow *cw = findWindowForMenubar(); QWindow *win = cw ? cw->window() : 0; - if (win && (win->flags() & Qt::Popup) == Qt::Popup) - return; // context menus, comboboxes, etc. don't need to update the menubar + if (win && (win->flags() & Qt::Popup) == Qt::Popup) { + // context menus, comboboxes, etc. don't need to update the menubar, + // but if an application has only Qt::Tool window(s) on start, + // we still have to update the menubar. + if ((win->flags() & Qt::WindowType_Mask) != Qt::Tool) + return; + typedef QT_MANGLE_NAMESPACE(QCocoaApplicationDelegate) AppDelegate; + NSApplication *app = [NSApplication sharedApplication]; + if (![app.delegate isKindOfClass:[AppDelegate class]]) + return; + // We apply this logic _only_ during the startup. + AppDelegate *appDelegate = app.delegate; + if (!appDelegate.inLaunch) + return; + } if (cw && cw->menubar()) mb = cw->menubar(); diff --git a/src/plugins/platforms/cocoa/qcocoamenuitem.h b/src/plugins/platforms/cocoa/qcocoamenuitem.h index 5c85824ab8..289f38fd18 100644 --- a/src/plugins/platforms/cocoa/qcocoamenuitem.h +++ b/src/plugins/platforms/cocoa/qcocoamenuitem.h @@ -60,27 +60,27 @@ class QCocoaMenuItem : public QPlatformMenuItem { public: QCocoaMenuItem(); - virtual ~QCocoaMenuItem(); + ~QCocoaMenuItem(); - inline virtual void setTag(quintptr tag) + void setTag(quintptr tag) Q_DECL_OVERRIDE { m_tag = tag; } - inline virtual quintptr tag() const + quintptr tag() const Q_DECL_OVERRIDE { return m_tag; } - void setText(const QString &text); - void setIcon(const QIcon &icon); - void setMenu(QPlatformMenu *menu); - void setVisible(bool isVisible); - void setIsSeparator(bool isSeparator); - void setFont(const QFont &font); - void setRole(MenuRole role); - void setShortcut(const QKeySequence& shortcut); - void setCheckable(bool checkable) { Q_UNUSED(checkable) } - void setChecked(bool isChecked); - void setEnabled(bool isEnabled); - void setIconSize(int size); - - void setNativeContents(WId item); + void setText(const QString &text) Q_DECL_OVERRIDE; + void setIcon(const QIcon &icon) Q_DECL_OVERRIDE; + void setMenu(QPlatformMenu *menu) Q_DECL_OVERRIDE; + void setVisible(bool isVisible) Q_DECL_OVERRIDE; + void setIsSeparator(bool isSeparator) Q_DECL_OVERRIDE; + void setFont(const QFont &font) Q_DECL_OVERRIDE; + void setRole(MenuRole role) Q_DECL_OVERRIDE; + void setShortcut(const QKeySequence& shortcut) Q_DECL_OVERRIDE; + void setCheckable(bool checkable) Q_DECL_OVERRIDE { Q_UNUSED(checkable) } + void setChecked(bool isChecked) Q_DECL_OVERRIDE; + void setEnabled(bool isEnabled) Q_DECL_OVERRIDE; + void setIconSize(int size) Q_DECL_OVERRIDE; + + void setNativeContents(WId item) Q_DECL_OVERRIDE; inline QString text() const { return m_text; } inline NSMenuItem * nsItem() { return m_native; } diff --git a/src/plugins/platforms/cocoa/qcocoamenuitem.mm b/src/plugins/platforms/cocoa/qcocoamenuitem.mm index f288ab85c0..0fc1bff325 100644 --- a/src/plugins/platforms/cocoa/qcocoamenuitem.mm +++ b/src/plugins/platforms/cocoa/qcocoamenuitem.mm @@ -37,7 +37,6 @@ #include "qcocoamenubar.h" #include "messages.h" #include "qcocoahelpers.h" -#include "qcocoaautoreleasepool.h" #include "qt_mac_p.h" #include "qcocoaapplication.h" // for custom application category #include "qcocoamenuloader.h" @@ -104,7 +103,7 @@ QCocoaMenuItem::QCocoaMenuItem() : QCocoaMenuItem::~QCocoaMenuItem() { - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; if (m_menu && COCOA_MENU_ANCESTOR(m_menu) == this) SET_COCOA_MENU_ANCESTOR(m_menu, 0); @@ -139,7 +138,7 @@ void QCocoaMenuItem::setMenu(QPlatformMenu *menu) m_menu->setContainingMenuItem(0); } - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; m_menu = static_cast<QCocoaMenu *>(menu); if (m_menu) { SET_COCOA_MENU_ANCESTOR(m_menu, this); @@ -198,6 +197,8 @@ void QCocoaMenuItem::setEnabled(bool enabled) void QCocoaMenuItem::setNativeContents(WId item) { NSView *itemView = (NSView *)item; + if (m_itemView == itemView) + return; [m_itemView release]; m_itemView = [itemView retain]; [m_itemView setAutoresizesSubviews:YES]; @@ -302,8 +303,8 @@ NSMenuItem *QCocoaMenuItem::sync() if (!m_native) { m_native = [[NSMenuItem alloc] initWithTitle:QCFString::toNSString(m_text) - action:nil - keyEquivalent:@""]; + action:nil + keyEquivalent:@""]; [m_native setTag:reinterpret_cast<NSInteger>(this)]; } diff --git a/src/plugins/platforms/cocoa/qcocoanativeinterface.h b/src/plugins/platforms/cocoa/qcocoanativeinterface.h index 2250f7c084..d018c05635 100644 --- a/src/plugins/platforms/cocoa/qcocoanativeinterface.h +++ b/src/plugins/platforms/cocoa/qcocoanativeinterface.h @@ -67,6 +67,8 @@ public: static void *nsOpenGLContextForContext(QOpenGLContext* context); #endif + QFunctionPointer platformFunction(const QByteArray &function) const Q_DECL_OVERRIDE; + public Q_SLOTS: void onAppFocusWindowChanged(QWindow *window); diff --git a/src/plugins/platforms/cocoa/qcocoanativeinterface.mm b/src/plugins/platforms/cocoa/qcocoanativeinterface.mm index 205a49d25a..41ea1fa49c 100644 --- a/src/plugins/platforms/cocoa/qcocoanativeinterface.mm +++ b/src/plugins/platforms/cocoa/qcocoanativeinterface.mm @@ -58,6 +58,8 @@ #include <qpa/qplatformprintersupport.h> #endif +#include <QtPlatformHeaders/qcocoawindowfunctions.h> + #include <Cocoa/Cocoa.h> QT_BEGIN_NAMESPACE @@ -217,6 +219,14 @@ void *QCocoaNativeInterface::nsOpenGLContextForContext(QOpenGLContext* context) } #endif +QFunctionPointer QCocoaNativeInterface::platformFunction(const QByteArray &function) const +{ + if (function == QCocoaWindowFunctions::bottomLeftClippedByNSWindowOffsetIdentifier()) + return QFunctionPointer(QCocoaWindowFunctions::BottomLeftClippedByNSWindowOffset(QCocoaWindow::bottomLeftClippedByNSWindowOffsetStatic)); + + return Q_NULLPTR; +} + void QCocoaNativeInterface::addToMimeList(void *macPasteboardMime) { qt_mac_addToGlobalMimeList(reinterpret_cast<QMacInternalPasteboardMime *>(macPasteboardMime)); @@ -234,7 +244,7 @@ void QCocoaNativeInterface::registerDraggedTypes(const QStringList &types) void QCocoaNativeInterface::setDockMenu(QPlatformMenu *platformMenu) { - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; QCocoaMenu *cocoaPlatformMenu = static_cast<QCocoaMenu *>(platformMenu); NSMenu *menu = cocoaPlatformMenu->nsMenu(); [NSApp QT_MANGLE_NAMESPACE(qt_setDockMenu): menu]; diff --git a/src/plugins/platforms/cocoa/qcocoaprintdevice.mm b/src/plugins/platforms/cocoa/qcocoaprintdevice.mm index 4d319e149b..b0d04f6f82 100644 --- a/src/plugins/platforms/cocoa/qcocoaprintdevice.mm +++ b/src/plugins/platforms/cocoa/qcocoaprintdevice.mm @@ -100,7 +100,7 @@ QCocoaPrintDevice::~QCocoaPrintDevice() { if (m_ppd) ppdClose(m_ppd); - foreach (PMPaper paper, m_macPapers.values()) + foreach (PMPaper paper, m_macPapers) PMRelease(paper); // Releasing the session appears to also release the printer if (m_session) @@ -154,7 +154,7 @@ QPageSize QCocoaPrintDevice::createPageSize(const PMPaper &paper) const void QCocoaPrintDevice::loadPageSizes() const { m_pageSizes.clear(); - foreach (PMPaper paper, m_macPapers.values()) + foreach (PMPaper paper, m_macPapers) PMRelease(paper); m_macPapers.clear(); m_printableMargins.clear(); diff --git a/src/plugins/platforms/cocoa/qcocoaservices.h b/src/plugins/platforms/cocoa/qcocoaservices.h index 331fe810fa..1a447c6242 100644 --- a/src/plugins/platforms/cocoa/qcocoaservices.h +++ b/src/plugins/platforms/cocoa/qcocoaservices.h @@ -41,8 +41,8 @@ QT_BEGIN_NAMESPACE class QCocoaServices : public QPlatformServices { public: - bool openUrl(const QUrl &url); - bool openDocument(const QUrl &url); + bool openUrl(const QUrl &url) Q_DECL_OVERRIDE; + bool openDocument(const QUrl &url) Q_DECL_OVERRIDE; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoasystemsettings.mm b/src/plugins/platforms/cocoa/qcocoasystemsettings.mm index 65cc9bc38b..93f8b2ba6f 100644 --- a/src/plugins/platforms/cocoa/qcocoasystemsettings.mm +++ b/src/plugins/platforms/cocoa/qcocoasystemsettings.mm @@ -33,7 +33,6 @@ #include "qcocoasystemsettings.h" -#include "qcocoaautoreleasepool.h" #include "qcocoahelpers.h" #include <QtCore/private/qcore_mac_p.h> @@ -45,7 +44,7 @@ QT_BEGIN_NAMESPACE QColor qt_mac_colorForTheme(ThemeBrush brush) { - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; QCFType<CGColorRef> cgClr = 0; HIThemeBrushCreateCGColor(brush, &cgClr); diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.h b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.h index b5f038094f..cdfd92c0aa 100755..100644 --- a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.h +++ b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.h @@ -51,17 +51,17 @@ class Q_GUI_EXPORT QCocoaSystemTrayIcon : public QPlatformSystemTrayIcon public: QCocoaSystemTrayIcon() : m_sys(0) {} - virtual void init(); - virtual void cleanup(); - virtual void updateIcon(const QIcon &icon); - virtual void updateToolTip(const QString &toolTip); - virtual void updateMenu(QPlatformMenu *menu); - virtual QRect geometry() const; - virtual void showMessage(const QString &title, const QString &msg, - const QIcon& icon, MessageIcon iconType, int secs); + void init() Q_DECL_OVERRIDE; + void cleanup() Q_DECL_OVERRIDE; + void updateIcon(const QIcon &icon) Q_DECL_OVERRIDE; + void updateToolTip(const QString &toolTip) Q_DECL_OVERRIDE; + void updateMenu(QPlatformMenu *menu) Q_DECL_OVERRIDE; + QRect geometry() const Q_DECL_OVERRIDE; + void showMessage(const QString &title, const QString &msg, + const QIcon& icon, MessageIcon iconType, int secs) Q_DECL_OVERRIDE; - virtual bool isSystemTrayAvailable() const; - virtual bool supportsMessages() const; + bool isSystemTrayAvailable() const Q_DECL_OVERRIDE; + bool supportsMessages() const Q_DECL_OVERRIDE; private: QSystemTrayIconSys *m_sys; diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm index 713758cc7e..a3ffb5be66 100755..100644 --- a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm +++ b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm @@ -252,6 +252,7 @@ void QCocoaSystemTrayIcon::updateIcon(const QIcon &icon) } NSImage *nsimage = static_cast<NSImage *>(qt_mac_create_nsimage(fullHeightPixmap)); + [nsimage setTemplate:icon.isMask()]; [(NSImageView*)[[m_sys->item item] view] setImage: nsimage]; [nsimage release]; } diff --git a/src/plugins/platforms/cocoa/qcocoatheme.mm b/src/plugins/platforms/cocoa/qcocoatheme.mm index 11749e14de..4b73d0af08 100644 --- a/src/plugins/platforms/cocoa/qcocoatheme.mm +++ b/src/plugins/platforms/cocoa/qcocoatheme.mm @@ -47,7 +47,6 @@ #include "qcocoamenu.h" #include "qcocoamenubar.h" #include "qcocoahelpers.h" -#include "qcocoaautoreleasepool.h" #include <QtCore/qfileinfo.h> #include <QtGui/private/qguiapplication_p.h> @@ -253,7 +252,7 @@ QPixmap QCocoaTheme::fileIconPixmap(const QFileInfo &fileInfo, const QSizeF &siz QPlatformTheme::IconOptions iconOptions) const { Q_UNUSED(iconOptions); - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; NSImage *iconImage = [[NSWorkspace sharedWorkspace] iconForFile:QCFString::toNSString(fileInfo.canonicalFilePath())]; if (!iconImage) diff --git a/src/plugins/platforms/cocoa/qcocoawindow.h b/src/plugins/platforms/cocoa/qcocoawindow.h index 455d4a8580..05e6cf3c9e 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.h +++ b/src/plugins/platforms/cocoa/qcocoawindow.h @@ -227,6 +227,9 @@ public: void updateExposedGeometry(); QWindow *childWindowAt(QPoint windowPoint); bool shouldRefuseKeyWindowAndFirstResponder(); + + static QPoint bottomLeftClippedByNSWindowOffsetStatic(QWindow *window); + QPoint bottomLeftClippedByNSWindowOffset() const; protected: void recreateWindow(const QPlatformWindow *parentWindow); QCocoaNSWindow *createNSWindow(); @@ -234,7 +237,7 @@ protected: bool shouldUseNSPanel(); - QRect windowGeometry() const; + QRect nativeWindowGeometry() const; QCocoaWindow *parentCocoaWindow() const; void syncWindowState(Qt::WindowState newState); void reinsertChildWindow(QCocoaWindow *child); @@ -245,6 +248,8 @@ public: // for QNSView friend class QCocoaBackingStore; friend class QCocoaNativeInterface; + void removeMonitor(); + NSView *m_contentView; QNSView *m_qtView; QCocoaNSWindow *m_nsWindow; @@ -265,10 +270,10 @@ public: // for QNSView QPointer<QWindow> m_enterLeaveTargetWindow; bool m_windowUnderMouse; - bool m_ignoreWindowShouldClose; bool m_inConstructor; bool m_inSetVisible; bool m_inSetGeometry; + bool m_inSetStyleMask; #ifndef QT_NO_OPENGL QCocoaGLContext *m_glContext; #endif diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index 809acdd87e..a545dbddd5 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -33,7 +33,6 @@ #include "qcocoawindow.h" #include "qcocoaintegration.h" #include "qnswindowdelegate.h" -#include "qcocoaautoreleasepool.h" #include "qcocoaeventdispatcher.h" #ifndef QT_NO_OPENGL #include "qcocoaglcontext.h" @@ -346,10 +345,10 @@ QCocoaWindow::QCocoaWindow(QWindow *tlw) , m_synchedWindowState(Qt::WindowActive) , m_windowModality(Qt::NonModal) , m_windowUnderMouse(false) - , m_ignoreWindowShouldClose(false) , m_inConstructor(true) , m_inSetVisible(false) , m_inSetGeometry(false) + , m_inSetStyleMask(false) #ifndef QT_NO_OPENGL , m_glContext(0) #endif @@ -373,7 +372,7 @@ QCocoaWindow::QCocoaWindow(QWindow *tlw) #ifdef QT_COCOA_ENABLE_WINDOW_DEBUG qDebug() << "QCocoaWindow::QCocoaWindow" << this; #endif - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; if (tlw->type() == Qt::ForeignWindow) { NSView *foreignView = (NSView *)WId(tlw->property("_q_foreignWinId").value<WId>()); @@ -409,7 +408,7 @@ QCocoaWindow::~QCocoaWindow() qDebug() << "QCocoaWindow::~QCocoaWindow" << this; #endif - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; [m_nsWindow setContentView:nil]; [m_nsWindow.helper detachFromPlatformWindow]; if (m_isNSWindowChild) { @@ -419,6 +418,8 @@ QCocoaWindow::~QCocoaWindow() [m_contentView removeFromSuperview]; } + removeMonitor(); + // Make sure to disconnect observer in all case if view is valid // to avoid notifications received when deleting when using Qt::AA_NativeWindows attribute if (m_qtView) { @@ -499,7 +500,7 @@ QRect QCocoaWindow::geometry() const void QCocoaWindow::setCocoaGeometry(const QRect &rect) { - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; if (m_contentViewIsEmbedded) { QPlatformWindow::setGeometry(rect); @@ -604,7 +605,7 @@ void QCocoaWindow::show(bool becauseOfAncestor) && !m_hiddenByClipping) { // ... NOR clipped if (m_isNSWindowChild) { m_hiddenByAncestor = false; - setCocoaGeometry(window()->geometry()); + setCocoaGeometry(windowGeometry()); } if (!m_hiddenByClipping) { // setCocoaGeometry() can change the clipping status [m_nsWindow orderFront:nil]; @@ -623,7 +624,7 @@ void QCocoaWindow::setVisible(bool visible) m_inSetVisible = true; - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; QCocoaWindow *parentCocoaWindow = 0; if (window()->transientParent()) parentCocoaWindow = static_cast<QCocoaWindow *>(window()->transientParent()->handle()); @@ -643,7 +644,7 @@ void QCocoaWindow::setVisible(bool visible) if (parentCocoaWindow) { // The parent window might have moved while this window was hidden, // update the window geometry if there is a parent. - setGeometry(window()->geometry()); + setGeometry(windowGeometry()); if (window()->type() == Qt::Popup) { // QTBUG-30266: a window should not be resizable while a transient popup is open @@ -696,6 +697,7 @@ void QCocoaWindow::setVisible(bool visible) && [m_nsWindow isKindOfClass:[NSPanel class]]) { [(NSPanel *)m_nsWindow setWorksWhenModal:YES]; if (!(parentCocoaWindow && window()->transientParent()->isActive()) && window()->type() == Qt::Popup) { + removeMonitor(); monitor = [NSEvent addGlobalMonitorForEventsMatchingMask:NSLeftMouseDownMask|NSRightMouseDownMask|NSOtherMouseDownMask|NSMouseMovedMask handler:^(NSEvent *e) { QPointF localPoint = qt_mac_flipPoint([NSEvent mouseLocation]); QWindowSystemInterface::handleMouseEvent(window(), window()->mapFromGlobal(localPoint.toPoint()), localPoint, @@ -744,10 +746,7 @@ void QCocoaWindow::setVisible(bool visible) } else { [m_contentView setHidden:YES]; } - if (monitor && window()->type() == Qt::Popup) { - [NSEvent removeMonitor:monitor]; - monitor = nil; - } + removeMonitor(); if (window()->type() == Qt::Popup || window()->type() == Qt::ToolTip) QCocoaIntegration::instance()->popupWindowStack()->removeAll(this); @@ -861,8 +860,8 @@ void QCocoaWindow::setWindowZoomButton(Qt::WindowFlags flags) // no-WindowMaximizeButtonHint windows. From a Qt perspective it migth be expected // that the button would be removed in the latter case, but disabling it is more // in line with the platform style guidelines. - bool fixedSizeNoZoom = (window()->minimumSize().isValid() && window()->maximumSize().isValid() - && window()->minimumSize() == window()->maximumSize()); + bool fixedSizeNoZoom = (windowMinimumSize().isValid() && windowMaximumSize().isValid() + && windowMinimumSize() == windowMaximumSize()); bool customizeNoZoom = ((flags & Qt::CustomizeWindowHint) && !(flags & Qt::WindowMaximizeButtonHint)); [[m_nsWindow standardWindowButton:NSWindowZoomButton] setEnabled:!(fixedSizeNoZoom || customizeNoZoom)]; } @@ -872,20 +871,27 @@ void QCocoaWindow::setWindowFlags(Qt::WindowFlags flags) if (m_nsWindow && !m_isNSWindowChild) { NSUInteger styleMask = windowStyleMask(flags); NSInteger level = this->windowLevel(flags); + // While setting style mask we can have -updateGeometry calls on a content + // view with null geometry, reporting an invalid coordinates as a result. + m_inSetStyleMask = true; [m_nsWindow setStyleMask:styleMask]; + m_inSetStyleMask = false; [m_nsWindow setLevel:level]; setWindowShadow(flags); - if (!(styleMask & NSBorderlessWindowMask)) { + if (!(flags & Qt::FramelessWindowHint)) { setWindowTitle(window()->title()); } Qt::WindowType type = window()->type(); if ((type & Qt::Popup) != Qt::Popup && (type & Qt::Dialog) != Qt::Dialog) { NSWindowCollectionBehavior behavior = [m_nsWindow collectionBehavior]; - if (flags & Qt::WindowFullscreenButtonHint) + if (flags & Qt::WindowFullscreenButtonHint) { behavior |= NSWindowCollectionBehaviorFullScreenPrimary; - else + behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary; + } else { + behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary; behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary; + } [m_nsWindow setCollectionBehavior:behavior]; } setWindowZoomButton(flags); @@ -902,7 +908,7 @@ void QCocoaWindow::setWindowState(Qt::WindowState state) void QCocoaWindow::setWindowTitle(const QString &title) { - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; if (!m_nsWindow) return; @@ -913,7 +919,7 @@ void QCocoaWindow::setWindowTitle(const QString &title) void QCocoaWindow::setWindowFilePath(const QString &filePath) { - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; if (!m_nsWindow) return; @@ -923,7 +929,7 @@ void QCocoaWindow::setWindowFilePath(const QString &filePath) void QCocoaWindow::setWindowIcon(const QIcon &icon) { - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; NSButton *iconButton = [m_nsWindow standardWindowButton:NSWindowDocumentIconButton]; if (iconButton == nil) { @@ -1041,26 +1047,26 @@ bool QCocoaWindow::isOpaque() const void QCocoaWindow::propagateSizeHints() { - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; if (!m_nsWindow) return; #ifdef QT_COCOA_ENABLE_WINDOW_DEBUG qDebug() << "QCocoaWindow::propagateSizeHints" << this; - qDebug() << " min/max " << window()->minimumSize() << window()->maximumSize(); - qDebug() << "size increment" << window()->sizeIncrement(); - qDebug() << " basesize" << window()->baseSize(); - qDebug() << " geometry" << geometry(); + qDebug() << " min/max" << windowMinimumSize() << windowMaximumSize(); + qDebug() << "size increment" << windowSizeIncrement(); + qDebug() << " basesize" << windowBaseSize(); + qDebug() << " geometry" << windowGeometry(); #endif // Set the minimum content size. - const QSize minimumSize = window()->minimumSize(); + const QSize minimumSize = windowMinimumSize(); if (!minimumSize.isValid()) // minimumSize is (-1, -1) when not set. Make that (0, 0) for Cocoa. [m_nsWindow setContentMinSize : NSMakeSize(0.0, 0.0)]; [m_nsWindow setContentMinSize : NSMakeSize(minimumSize.width(), minimumSize.height())]; // Set the maximum content size. - const QSize maximumSize = window()->maximumSize(); + const QSize maximumSize = windowMaximumSize(); [m_nsWindow setContentMaxSize : NSMakeSize(maximumSize.width(), maximumSize.height())]; // The window may end up with a fixed size; in this case the zoom button should be disabled. @@ -1068,13 +1074,14 @@ void QCocoaWindow::propagateSizeHints() // sizeIncrement is observed to take values of (-1, -1) and (0, 0) for windows that should be // resizable and that have no specific size increment set. Cocoa expects (1.0, 1.0) in this case. - if (!window()->sizeIncrement().isEmpty()) - [m_nsWindow setResizeIncrements : qt_mac_toNSSize(window()->sizeIncrement())]; + const QSize sizeIncrement = windowSizeIncrement(); + if (!sizeIncrement.isEmpty()) + [m_nsWindow setResizeIncrements : qt_mac_toNSSize(sizeIncrement)]; else [m_nsWindow setResizeIncrements : NSMakeSize(1.0, 1.0)]; QRect rect = geometry(); - QSize baseSize = window()->baseSize(); + QSize baseSize = windowBaseSize(); if (!baseSize.isNull() && baseSize.isValid()) { [m_nsWindow setFrame:NSMakeRect(rect.x(), rect.y(), baseSize.width(), baseSize.height()) display:YES]; } @@ -1211,9 +1218,10 @@ void QCocoaWindow::windowDidEndLiveResize() bool QCocoaWindow::windowShouldClose() { - // might have been set from qnsview.mm - if (m_ignoreWindowShouldClose) - return false; + // This callback should technically only determine if the window + // should (be allowed to) close, but since our QPA API to determine + // that also involves actually closing the window we do both at the + // same time, instead of doing the latter in windowWillClose. bool accepted = false; QWindowSystemInterface::handleCloseEvent(window(), &accepted); QWindowSystemInterface::flushWindowSystemEvents(); @@ -1320,7 +1328,7 @@ void QCocoaWindow::recreateWindow(const QPlatformWindow *parentWindow) | NSWindowCollectionBehaviorFullScreenAuxiliary; m_nsWindow.animationBehavior = NSWindowAnimationBehaviorNone; m_nsWindow.collectionBehavior = collectionBehavior; - setCocoaGeometry(window()->geometry()); + setCocoaGeometry(windowGeometry()); QList<QCocoaWindow *> &siblings = m_parentCocoaWindow->m_childWindows; if (siblings.contains(this)) { @@ -1334,7 +1342,7 @@ void QCocoaWindow::recreateWindow(const QPlatformWindow *parentWindow) } else { // Child windows have no NSWindow, link the NSViews instead. [m_parentCocoaWindow->m_contentView addSubview : m_contentView]; - QRect rect = window()->geometry(); + QRect rect = windowGeometry(); // Prevent setting a (0,0) window size; causes opengl context // "Invalid Drawable" warnings. if (rect.isNull()) @@ -1387,9 +1395,9 @@ bool QCocoaWindow::shouldUseNSPanel() QCocoaNSWindow * QCocoaWindow::createNSWindow() { - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; - QRect rect = initialGeometry(window(), window()->geometry(), defaultWindowWidth, defaultWindowHeight); + QRect rect = initialGeometry(window(), windowGeometry(), defaultWindowWidth, defaultWindowHeight); NSRect frame = qt_mac_flipRect(rect); Qt::WindowType type = window()->type(); @@ -1476,8 +1484,16 @@ void QCocoaWindow::removeChildWindow(QCocoaWindow *child) [m_nsWindow removeChildWindow:child->m_nsWindow]; } +void QCocoaWindow::removeMonitor() +{ + if (!monitor) + return; + [NSEvent removeMonitor:monitor]; + monitor = nil; +} + // Returns the current global screen geometry for the nswindow associated with this window. -QRect QCocoaWindow::windowGeometry() const +QRect QCocoaWindow::nativeWindowGeometry() const { if (!m_nsWindow || m_isNSWindowChild) return geometry(); @@ -1560,7 +1576,7 @@ void QCocoaWindow::syncWindowState(Qt::WindowState newState) if (m_normalGeometry.width() < 0) { m_oldWindowFlags = m_windowFlags; window()->setFlags(window()->flags() | Qt::FramelessWindowHint); - m_normalGeometry = windowGeometry(); + m_normalGeometry = nativeWindowGeometry(); setGeometry(screen->geometry()); m_presentationOptions = [NSApp presentationOptions]; [NSApp setPresentationOptions : m_presentationOptions | NSApplicationPresentationAutoHideMenuBar | NSApplicationPresentationAutoHideDock]; @@ -1827,6 +1843,24 @@ bool QCocoaWindow::shouldRefuseKeyWindowAndFirstResponder() return false; } +QPoint QCocoaWindow::bottomLeftClippedByNSWindowOffsetStatic(QWindow *window) +{ + if (window->handle()) + return static_cast<QCocoaWindow *>(window->handle())->bottomLeftClippedByNSWindowOffset(); + return QPoint(); +} + +QPoint QCocoaWindow::bottomLeftClippedByNSWindowOffset() const +{ + if (!m_contentView) + return QPoint(); + const NSPoint origin = [m_contentView isFlipped] ? NSMakePoint(0, [m_contentView frame].size.height) + : NSMakePoint(0, 0); + const NSRect visibleRect = [m_contentView visibleRect]; + + return QPoint(visibleRect.origin.x, -visibleRect.origin.y + (origin.y - visibleRect.size.height)); +} + QMargins QCocoaWindow::frameMargins() const { NSRect frameW = [m_nsWindow frame]; diff --git a/src/plugins/platforms/cocoa/qmacclipboard.mm b/src/plugins/platforms/cocoa/qmacclipboard.mm index 3d88a8d5df..f4fd32ffd1 100644 --- a/src/plugins/platforms/cocoa/qmacclipboard.mm +++ b/src/plugins/platforms/cocoa/qmacclipboard.mm @@ -43,7 +43,6 @@ #include <stdlib.h> #include <string.h> #include "qcocoahelpers.h" -#include "qcocoaautoreleasepool.h" QT_BEGIN_NAMESPACE @@ -555,7 +554,7 @@ QMacPasteboard::sync() const QString qt_mac_get_pasteboardString(PasteboardRef paste) { - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; NSPasteboard *pb = nil; CFStringRef pbname; if (PasteboardCopyName(paste, &pbname) == noErr) { diff --git a/src/plugins/platforms/cocoa/qmultitouch_mac.mm b/src/plugins/platforms/cocoa/qmultitouch_mac.mm index 6e7ebcc37c..6099add6bb 100644 --- a/src/plugins/platforms/cocoa/qmultitouch_mac.mm +++ b/src/plugins/platforms/cocoa/qmultitouch_mac.mm @@ -173,7 +173,7 @@ QCocoaTouch::getCurrentTouchPointList(NSEvent *event, bool acceptSingleTouch) if (_touchCount != _currentTouches.size()) { // Remove all instances, and basically start from scratch: touchPoints.clear(); - foreach (QCocoaTouch *qcocoaTouch, _currentTouches.values()) { + foreach (QCocoaTouch *qcocoaTouch, _currentTouches) { if (!_updateInternalStateOnly) { qcocoaTouch->_touchPoint.state = Qt::TouchPointReleased; touchPoints.insert(qcocoaTouch->_touchPoint.id, qcocoaTouch->_touchPoint); @@ -190,7 +190,7 @@ QCocoaTouch::getCurrentTouchPointList(NSEvent *event, bool acceptSingleTouch) // touch now (and refake a begin for it later, if needed). if (_updateInternalStateOnly && !wasUpdateInternalStateOnly && !_currentTouches.isEmpty()) { - QCocoaTouch *qcocoaTouch = _currentTouches.values().first(); + QCocoaTouch *qcocoaTouch = _currentTouches.cbegin().value(); qcocoaTouch->_touchPoint.state = Qt::TouchPointReleased; touchPoints.insert(qcocoaTouch->_touchPoint.id, qcocoaTouch->_touchPoint); // Since this last touch also will end up being the first diff --git a/src/plugins/platforms/cocoa/qnsview.h b/src/plugins/platforms/cocoa/qnsview.h index 14a61554a3..50b456cab7 100644 --- a/src/plugins/platforms/cocoa/qnsview.h +++ b/src/plugins/platforms/cocoa/qnsview.h @@ -75,7 +75,10 @@ Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper)); QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper) *m_mouseMoveHelper; bool m_resendKeyEvent; bool m_scrolling; + bool m_updatingDrag; bool m_exposedOnMoveToWindow; + NSEvent *m_currentlyInterpretedKeyEvent; + bool m_isMenuView; } - (id)init; @@ -93,8 +96,10 @@ Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper)); - (void)notifyWindowStateChanged:(Qt::WindowState)newState; - (void)windowNotification : (NSNotification *) windowNotification; - (void)notifyWindowWillZoom:(BOOL)willZoom; +- (void)textInputContextKeyboardSelectionDidChangeNotification : (NSNotification *) textInputContextKeyboardSelectionDidChangeNotification; - (void)viewDidHide; - (void)viewDidUnhide; +- (void)removeFromSuperview; - (BOOL)isFlipped; - (BOOL)acceptsFirstResponder; @@ -131,7 +136,6 @@ Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper)); - (void)handleKeyEvent:(NSEvent *)theEvent eventType:(int)eventType; - (void)keyDown:(NSEvent *)theEvent; - (void)keyUp:(NSEvent *)theEvent; -- (BOOL)performKeyEquivalent:(NSEvent *)theEvent; - (void)registerDragTypes; - (NSDragOperation)handleDrag:(id <NSDraggingInfo>)sender; diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm index 1ec33df744..3faa292ae0 100644 --- a/src/plugins/platforms/cocoa/qnsview.mm +++ b/src/plugins/platforms/cocoa/qnsview.mm @@ -39,9 +39,9 @@ #include "qnsview.h" #include "qcocoawindow.h" #include "qcocoahelpers.h" -#include "qcocoaautoreleasepool.h" #include "qmultitouch_mac_p.h" #include "qcocoadrag.h" +#include "qcocoainputcontext.h" #include <qpa/qplatformintegration.h> #include <qpa/qwindowsysteminterface.h> @@ -153,6 +153,8 @@ static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil; m_mouseMoveHelper = [[QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper) alloc] initWithView:self]; m_resendKeyEvent = false; m_scrolling = false; + m_updatingDrag = false; + m_currentlyInterpretedKeyEvent = 0; if (!touchDevice) { touchDevice = new QTouchDevice; @@ -160,6 +162,8 @@ static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil; touchDevice->setCapabilities(QTouchDevice::Position | QTouchDevice::NormalizedPosition | QTouchDevice::MouseEmulation); QWindowSystemInterface::registerTouchDevice(touchDevice); } + + m_isMenuView = false; } return self; } @@ -213,6 +217,11 @@ static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil; name:NSViewFrameDidChangeNotification object:self]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(textInputContextKeyboardSelectionDidChangeNotification:) + name:NSTextInputContextKeyboardSelectionDidChangeNotification + object:nil]; + return self; } @@ -269,11 +278,11 @@ static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil; - (void)viewDidMoveToWindow { + m_isMenuView = [self.window.className isEqualToString:@"NSCarbonMenuWindow"]; if (self.window) { // This is the case of QWidgetAction's generated QWidget inserted in an NSMenu. // 10.9 and newer get the NSWindowDidChangeOcclusionStateNotification - if ((!_q_NSWindowDidChangeOcclusionStateNotification - && [self.window.className isEqualToString:@"NSCarbonMenuWindow"])) { + if (!_q_NSWindowDidChangeOcclusionStateNotification && m_isMenuView) { m_exposedOnMoveToWindow = true; m_platformWindow->exposeWindow(); } @@ -347,6 +356,12 @@ static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil; if (m_platformWindow->m_nsWindow && geometry == m_platformWindow->geometry()) return; + // It can happen that self.window is nil (if we are changing + // styleMask from/to borderless and content view is being re-parented) + // - this results in an invalid coordinates. + if (m_platformWindow->m_inSetStyleMask && !self.window) + return; + #ifdef QT_COCOA_ENABLE_WINDOW_DEBUG qDebug() << "QNSView::udpateGeometry" << m_platformWindow << geometry; #endif @@ -396,7 +411,7 @@ static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil; NSString *notificationName = [windowNotification name]; if (notificationName == NSWindowDidBecomeKeyNotification) { - if (!m_platformWindow->windowIsPopupType()) + if (!m_platformWindow->windowIsPopupType() && !m_isMenuView) QWindowSystemInterface::handleWindowActivated(m_window); } else if (notificationName == NSWindowDidResignKeyNotification) { // key window will be non-nil if another window became key... do not @@ -405,7 +420,7 @@ static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil; NSWindow *keyWindow = [NSApp keyWindow]; if (!keyWindow) { // no new key window, go ahead and set the active window to zero - if (!m_platformWindow->windowIsPopupType()) + if (!m_platformWindow->windowIsPopupType() && !m_isMenuView) QWindowSystemInterface::handleWindowActivated(0); } } else if (notificationName == NSWindowDidMiniaturizeNotification @@ -456,6 +471,15 @@ QT_WARNING_POP } } +- (void)textInputContextKeyboardSelectionDidChangeNotification : (NSNotification *) textInputContextKeyboardSelectionDidChangeNotification +{ + Q_UNUSED(textInputContextKeyboardSelectionDidChangeNotification) + if (([NSApp keyWindow] == [self window]) && [[self window] firstResponder] == self) { + QCocoaInputContext *ic = qobject_cast<QCocoaInputContext *>(QCocoaIntegration::instance()->inputContext()); + ic->updateLocale(); + } +} + - (void)notifyWindowWillZoom:(BOOL)willZoom { Qt::WindowState newState = willZoom ? Qt::WindowMaximized : Qt::WindowNoState; @@ -474,6 +498,12 @@ QT_WARNING_POP m_platformWindow->exposeWindow(); } +- (void)removeFromSuperview +{ + QMacAutoReleasePool pool; + [super removeFromSuperview]; +} + - (void) flushBackingStore:(QCocoaBackingStore *)backingStore region:(const QRegion &)region offset:(QPoint)offset { m_backingStore = backingStore; @@ -615,13 +645,15 @@ QT_WARNING_POP { if (m_window && (m_window->flags() & Qt::WindowTransparentForInput) ) return NO; - if (!m_platformWindow->windowIsPopupType()) + if (!m_platformWindow->windowIsPopupType() && !m_isMenuView) QWindowSystemInterface::handleWindowActivated([self topLevelWindow]); return YES; } - (BOOL)acceptsFirstResponder { + if (m_isMenuView) + return NO; if (m_platformWindow->shouldRefuseKeyWindowAndFirstResponder()) return NO; if (m_window && (m_window->flags() & Qt::WindowTransparentForInput) ) @@ -852,7 +884,7 @@ QT_WARNING_POP { [super updateTrackingAreas]; - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; // NSTrackingInVisibleRect keeps care of updating once the tracking is set up, so bail out early if (m_trackingArea && [[self trackingAreas] containsObject:m_trackingArea]) @@ -1435,41 +1467,42 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) if (!(modifiers & (Qt::ControlModifier | Qt::MetaModifier)) && (ch.unicode() < 0xf700 || ch.unicode() > 0xf8ff)) text = QCFString::toQString(characters); - QWindow *focusWindow = [self topLevelWindow]; + QWindow *window = [self topLevelWindow]; + + // Popups implicitly grab key events; forward to the active popup if there is one. + // This allows popups to e.g. intercept shortcuts and close the popup in response. + if (QCocoaWindow *popup = QCocoaIntegration::instance()->activePopupWindow()) { + if (!popup->m_windowFlags.testFlag(Qt::ToolTip)) + window = popup->window(); + } if (eventType == QEvent::KeyPress) { if (m_composingText.isEmpty()) { - QKeyEvent override(QEvent::ShortcutOverride, keyCode, modifiers, - nativeScanCode, nativeVirtualKey, nativeModifiers, text, [nsevent isARepeat], 1); - override.setTimestamp(timestamp); - m_sendKeyEvent = !QWindowSystemInterface::tryHandleShortcutOverrideEvent(focusWindow, &override); + m_sendKeyEvent = !QWindowSystemInterface::handleShortcutEvent(window, timestamp, keyCode, + modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers, text, [nsevent isARepeat], 1); } QObject *fo = QGuiApplication::focusObject(); if (m_sendKeyEvent && fo) { - // if escape is pressed we don't want interpretKeyEvents to close a dialog. This will be done via QWindowSystemInterface - if (keyCode == Qt::Key_Escape) - m_platformWindow->m_ignoreWindowShouldClose = true; - QInputMethodQueryEvent queryEvent(Qt::ImEnabled | Qt::ImHints); if (QCoreApplication::sendEvent(fo, &queryEvent)) { bool imEnabled = queryEvent.value(Qt::ImEnabled).toBool(); Qt::InputMethodHints hints = static_cast<Qt::InputMethodHints>(queryEvent.value(Qt::ImHints).toUInt()); if (imEnabled && !(hints & Qt::ImhDigitsOnly || hints & Qt::ImhFormattedNumbersOnly || hints & Qt::ImhHiddenText)) { // pass the key event to the input method. note that m_sendKeyEvent may be set to false during this call + m_currentlyInterpretedKeyEvent = nsevent; [self interpretKeyEvents:[NSArray arrayWithObject:nsevent]]; + m_currentlyInterpretedKeyEvent = 0; } } - - m_platformWindow->m_ignoreWindowShouldClose = false;; } if (m_resendKeyEvent) m_sendKeyEvent = true; } if (m_sendKeyEvent && m_composingText.isEmpty()) - QWindowSystemInterface::handleExtendedKeyEvent(focusWindow, timestamp, QEvent::Type(eventType), keyCode, modifiers, + QWindowSystemInterface::handleExtendedKeyEvent(window, timestamp, QEvent::Type(eventType), keyCode, modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers, text, [nsevent isARepeat], 1, false); m_sendKeyEvent = false; @@ -1490,21 +1523,23 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) [self handleKeyEvent:nsevent eventType:int(QEvent::KeyRelease)]; } -- (BOOL)performKeyEquivalent:(NSEvent *)nsevent +- (void)cancelOperation:(id)sender { - NSString *chars = [nsevent charactersIgnoringModifiers]; + Q_UNUSED(sender); - if ([nsevent type] == NSKeyDown && [chars length] > 0) { - QChar ch = [chars characterAtIndex:0]; - Qt::Key qtKey = qt_mac_cocoaKey2QtKey(ch); - // check for Command + Key_Period - if ([nsevent modifierFlags] & NSCommandKeyMask - && qtKey == Qt::Key_Period) { - [self handleKeyEvent:nsevent eventType:int(QEvent::KeyPress)]; - return YES; - } - } - return [super performKeyEquivalent:nsevent]; + NSEvent *currentEvent = [NSApp currentEvent]; + if (!currentEvent || currentEvent.type != NSKeyDown) + return; + + // Handling the key event may recurse back here through interpretKeyEvents + // (when IM is enabled), so we need to guard against that. + if (currentEvent == m_currentlyInterpretedKeyEvent) + return; + + // Send Command+Key_Period and Escape as normal keypresses so that + // the key sequence is delivered through Qt. That way clients can + // intercept the shortcut and override its effect. + [self handleKeyEvent:currentEvent eventType:int(QEvent::KeyPress)]; } - (void)flagsChanged:(NSEvent *)nsevent @@ -1793,7 +1828,7 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) -(void)registerDragTypes { - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; QStringList customTypes = qt_mac_enabledDraggedTypes(); if (currentCustomDragTypes == 0 || *currentCustomDragTypes != customTypes) { if (currentCustomDragTypes == 0) @@ -1891,6 +1926,9 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin // Make sure the cursor is updated correctly if the mouse does not move and window is under cursor // by creating a fake move event + if (m_updatingDrag) + return; + const QPoint mousePos(QCursor::pos()); CGEventRef moveEvent(CGEventCreateMouseEvent( NULL, kCGEventMouseMoved, @@ -1908,7 +1946,11 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender { - return [self handleDrag : sender]; + m_updatingDrag = true; + const NSDragOperation ret([self handleDrag : sender]); + m_updatingDrag = false; + + return ret; } // Sends drag update to Qt, return the action diff --git a/src/plugins/platforms/cocoa/qnsviewaccessibility.mm b/src/plugins/platforms/cocoa/qnsviewaccessibility.mm index 93f0817aad..1f15da5b3b 100644 --- a/src/plugins/platforms/cocoa/qnsviewaccessibility.mm +++ b/src/plugins/platforms/cocoa/qnsviewaccessibility.mm @@ -44,6 +44,8 @@ #import <AppKit/NSAccessibility.h> +#ifndef QT_NO_ACCESSIBILITY + @implementation QNSView (QNSViewAccessibility) - (id)childAccessibleElement { @@ -80,3 +82,5 @@ } @end + +#endif // QT_NO_ACCESSIBILITY diff --git a/src/plugins/platforms/cocoa/qprintengine_mac.mm b/src/plugins/platforms/cocoa/qprintengine_mac.mm index 9e8fe8f1c8..edd1d656f0 100644 --- a/src/plugins/platforms/cocoa/qprintengine_mac.mm +++ b/src/plugins/platforms/cocoa/qprintengine_mac.mm @@ -38,7 +38,6 @@ #include <QtCore/qcoreapplication.h> #include <QtCore/qdebug.h> -#include "qcocoaautoreleasepool.h" #ifndef QT_NO_PRINTER @@ -212,6 +211,9 @@ int QMacPrintEngine::metric(QPaintDevice::PaintDeviceMetric m) const case QPaintDevice::PdmDevicePixelRatio: val = 1; break; + case QPaintDevice::PdmDevicePixelRatioScaled: + val = 1 * QPaintDevice::devicePixelRatioFScale(); + break; default: val = 0; qWarning("QPrinter::metric: Invalid metric command"); @@ -230,7 +232,7 @@ void QMacPrintEnginePrivate::initialize() q->gccaps = paintEngine->gccaps; - QCocoaAutoReleasePool pool; + QMacAutoReleasePool pool; printInfo = [[NSPrintInfo alloc] initWithDictionary:[NSDictionary dictionary]]; QList<int> resolutions = m_printDevice->supportedResolutions(); diff --git a/src/plugins/platforms/cocoa/qt_mac_p.h b/src/plugins/platforms/cocoa/qt_mac_p.h index e210f0221f..576e0f9729 100644 --- a/src/plugins/platforms/cocoa/qt_mac_p.h +++ b/src/plugins/platforms/cocoa/qt_mac_p.h @@ -103,17 +103,6 @@ public: } }; -class Q_WIDGETS_EXPORT QMacCocoaAutoReleasePool -{ -private: - void *pool; -public: - QMacCocoaAutoReleasePool(); - ~QMacCocoaAutoReleasePool(); - - inline void *handle() const { return pool; } -}; - QString qt_mac_removeMnemonics(const QString &original); //implemented in qmacstyle_mac.cpp class Q_WIDGETS_EXPORT QMacWindowChangeEvent diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dintegration.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dintegration.cpp index 20dde476c1..3bd6e76c52 100644 --- a/src/plugins/platforms/direct2d/qwindowsdirect2dintegration.cpp +++ b/src/plugins/platforms/direct2d/qwindowsdirect2dintegration.cpp @@ -39,12 +39,12 @@ #include "qwindowsdirect2dwindow.h" #include "qwindowscontext.h" -#include "qwindowsguieventdispatcher.h" #include <qplatformdefs.h> #include <QtCore/QCoreApplication> #include <QtGui/private/qpixmap_raster_p.h> #include <QtGui/qpa/qwindowsysteminterface.h> +#include <QtPlatformSupport/private/qwindowsguieventdispatcher_p.h> QT_BEGIN_NAMESPACE @@ -219,12 +219,11 @@ QWindowsDirect2DIntegration::~QWindowsDirect2DIntegration() return static_cast<QWindowsDirect2DIntegration *>(QWindowsIntegration::instance()); } - QPlatformWindow *QWindowsDirect2DIntegration::createPlatformWindow(QWindow *window) const - { - QWindowsWindowData data = createWindowData(window); - return data.hwnd ? new QWindowsDirect2DWindow(window, data) - : Q_NULLPTR; - } + +QWindowsWindow *QWindowsDirect2DIntegration::createPlatformWindowHelper(QWindow *window, const QWindowsWindowData &data) const +{ + return new QWindowsDirect2DWindow(window, data); +} QPlatformNativeInterface *QWindowsDirect2DIntegration::nativeInterface() const { diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dintegration.h b/src/plugins/platforms/direct2d/qwindowsdirect2dintegration.h index 6b7b3fbea2..64042cab0a 100644 --- a/src/plugins/platforms/direct2d/qwindowsdirect2dintegration.h +++ b/src/plugins/platforms/direct2d/qwindowsdirect2dintegration.h @@ -52,7 +52,6 @@ public: static QWindowsDirect2DIntegration *instance(); - QPlatformWindow *createPlatformWindow(QWindow *window) const Q_DECL_OVERRIDE; QPlatformNativeInterface *nativeInterface() const Q_DECL_OVERRIDE; QPlatformPixmap *createPlatformPixmap(QPlatformPixmap::PixelType type) const Q_DECL_OVERRIDE; QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const Q_DECL_OVERRIDE; @@ -60,6 +59,9 @@ public: QWindowsDirect2DContext *direct2DContext() const; +protected: + QWindowsWindow *createPlatformWindowHelper(QWindow *window, const QWindowsWindowData &) const Q_DECL_OVERRIDE; + private: explicit QWindowsDirect2DIntegration(const QStringList ¶mList); bool init(); diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintdevice.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintdevice.cpp index fee9ad88fa..40709fc3d0 100644 --- a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintdevice.cpp +++ b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintdevice.cpp @@ -113,6 +113,9 @@ int QWindowsDirect2DPaintDevice::metric(QPaintDevice::PaintDeviceMetric metric) case QPaintDevice::PdmDevicePixelRatio: return 1; break; + case QPaintDevice::PdmDevicePixelRatioScaled: + return 1 * devicePixelRatioFScale(); + break; case QPaintDevice::PdmWidthMM: case QPaintDevice::PdmHeightMM: return -1; diff --git a/src/plugins/platforms/directfb/qdirectfbblitter.cpp b/src/plugins/platforms/directfb/qdirectfbblitter.cpp index 4deac969e4..b87310ed76 100644 --- a/src/plugins/platforms/directfb/qdirectfbblitter.cpp +++ b/src/plugins/platforms/directfb/qdirectfbblitter.cpp @@ -473,7 +473,7 @@ IDirectFBSurface *QDirectFbTextureGlyphCache::sourceSurface() case QImage::Format_Mono: desc.pixelformat = DSPF_A1; break; - case QImage::Format_Indexed8: + case QImage::Format_Alpha8: desc.pixelformat = DSPF_A8; break; default: diff --git a/src/plugins/platforms/directfb/qdirectfbconvenience.cpp b/src/plugins/platforms/directfb/qdirectfbconvenience.cpp index e635d4fd22..e1d97d9628 100644 --- a/src/plugins/platforms/directfb/qdirectfbconvenience.cpp +++ b/src/plugins/platforms/directfb/qdirectfbconvenience.cpp @@ -217,7 +217,7 @@ Qt::KeyboardModifiers QDirectFbConvenience::keyboardModifiers(DFBInputDeviceModi modifiers |= Qt::ControlModifier; } if (mask & DIMM_META) { - modifiers | Qt::MetaModifier; + modifiers |= Qt::MetaModifier; } return modifiers; } diff --git a/src/plugins/platforms/eglfs/cursor-atlas.png b/src/plugins/platforms/eglfs/cursor-atlas.png Binary files differindex 40c5b6ef4f..40c5b6ef4f 100755..100644 --- a/src/plugins/platforms/eglfs/cursor-atlas.png +++ b/src/plugins/platforms/eglfs/cursor-atlas.png diff --git a/src/plugins/platforms/eglfs/deviceintegration/deviceintegration.pro b/src/plugins/platforms/eglfs/deviceintegration/deviceintegration.pro index 0adbb0d49f..266a97dff5 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/deviceintegration.pro +++ b/src/plugins/platforms/eglfs/deviceintegration/deviceintegration.pro @@ -1,7 +1,9 @@ TEMPLATE = subdirs contains(QT_CONFIG, egl_x11): SUBDIRS += eglfs_x11 -contains(QT_CONFIG, kms): SUBDIRS += eglfs_kms +contains(QT_CONFIG, eglfs_gbm): SUBDIRS += eglfs_kms +contains(QT_CONFIG, eglfs_egldevice): SUBDIRS += eglfs_kms_egldevice contains(QT_CONFIG, eglfs_brcm): SUBDIRS += eglfs_brcm contains(QT_CONFIG, eglfs_mali): SUBDIRS += eglfs_mali contains(QT_CONFIG, eglfs_viv): SUBDIRS += eglfs_viv +contains(QT_CONFIG, eglfs_viv_wl): SUBDIRS += eglfs_viv_wl diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_brcm/eglfs_brcm.pro b/src/plugins/platforms/eglfs/deviceintegration/eglfs_brcm/eglfs_brcm.pro index 98797e2106..2026b6a6c6 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_brcm/eglfs_brcm.pro +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_brcm/eglfs_brcm.pro @@ -12,6 +12,9 @@ CONFIG += egl LIBS += -lbcm_host QMAKE_LFLAGS += $$QMAKE_LFLAGS_NOUNDEF +# Avoid X11 header collision +DEFINES += MESA_EGL_NO_X11_HEADERS + SOURCES += $$PWD/qeglfsbrcmmain.cpp \ $$PWD/qeglfsbrcmintegration.cpp diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsdevice.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsdevice.cpp index 18a66e34f5..c29d64c06d 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsdevice.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsdevice.cpp @@ -191,6 +191,12 @@ QEglFSKmsScreen *QEglFSKmsDevice::screenForConnector(drmModeResPtr resources, dr return Q_NULLPTR; } + // Skip disconnected output + if (configuration == OutputConfigPreferred && connector->connection == DRM_MODE_DISCONNECTED) { + qCDebug(qLcEglfsKmsDebug) << "Skipping disconnected output" << connectorName; + return Q_NULLPTR; + } + // Get the current mode on the current crtc drmModeModeInfo crtc_mode; memset(&crtc_mode, 0, sizeof crtc_mode); @@ -208,11 +214,12 @@ QEglFSKmsScreen *QEglFSKmsDevice::screenForConnector(drmModeResPtr resources, dr } QList<drmModeModeInfo> modes; + modes.reserve(connector->count_modes); qCDebug(qLcEglfsKmsDebug) << connectorName << "mode count:" << connector->count_modes; for (int i = 0; i < connector->count_modes; i++) { const drmModeModeInfo &mode = connector->modes[i]; qCDebug(qLcEglfsKmsDebug) << "mode" << i << mode.hdisplay << "x" << mode.vdisplay - << "@" << mode.vrefresh << "hz"; + << '@' << mode.vrefresh << "hz"; modes << connector->modes[i]; } @@ -271,7 +278,7 @@ QEglFSKmsScreen *QEglFSKmsDevice::screenForConnector(drmModeResPtr resources, dr int height = modes[selected_mode].vdisplay; int refresh = modes[selected_mode].vrefresh; qCDebug(qLcEglfsKmsDebug) << "Selected mode" << selected_mode << ":" << width << "x" << height - << "@" << refresh << "hz for output" << connectorName; + << '@' << refresh << "hz for output" << connectorName; } QEglFSKmsOutput output = { @@ -282,7 +289,9 @@ QEglFSKmsScreen *QEglFSKmsDevice::screenForConnector(drmModeResPtr resources, dr selected_mode, false, drmModeGetCrtc(m_dri_fd, crtc_id), - modes + modes, + connector->subpixel, + connectorProperty(connector, QByteArrayLiteral("DPMS")) }; m_crtc_allocator |= (1 << output.crtc_id); @@ -291,6 +300,22 @@ QEglFSKmsScreen *QEglFSKmsDevice::screenForConnector(drmModeResPtr resources, dr return new QEglFSKmsScreen(m_integration, this, output, pos); } +drmModePropertyPtr QEglFSKmsDevice::connectorProperty(drmModeConnectorPtr connector, const QByteArray &name) +{ + drmModePropertyPtr prop; + + for (int i = 0; i < connector->count_props; i++) { + prop = drmModeGetProperty(m_dri_fd, connector->props[i]); + if (!prop) + continue; + if (strcmp(prop->name, name.constData()) == 0) + return prop; + drmModeFreeProperty(prop); + } + + return Q_NULLPTR; +} + void QEglFSKmsDevice::pageFlipHandler(int fd, unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec, void *user_data) { Q_UNUSED(fd); diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsdevice.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsdevice.h index 23fca934e5..411f9a7200 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsdevice.h +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsdevice.h @@ -78,6 +78,7 @@ private: int crtcForConnector(drmModeResPtr resources, drmModeConnectorPtr connector); QEglFSKmsScreen *screenForConnector(drmModeResPtr resources, drmModeConnectorPtr connector, QPoint pos); + drmModePropertyPtr connectorProperty(drmModeConnectorPtr connector, const QByteArray &name); static void pageFlipHandler(int fd, unsigned int sequence, diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsintegration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsintegration.cpp index 45224ccb87..d1814fb85d 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsintegration.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsintegration.cpp @@ -36,8 +36,8 @@ #include "qeglfskmsdevice.h" #include "qeglfskmsscreen.h" #include "qeglfskmscursor.h" +#include "qeglfscursor.h" -#include <QtPlatformSupport/private/qeglplatformcursor_p.h> #include <QtPlatformSupport/private/qdevicediscovery_p.h> #include <QtCore/QLoggingCategory> #include <QtCore/QJsonDocument> @@ -176,7 +176,7 @@ QPlatformCursor *QEglFSKmsIntegration::createCursor(QPlatformScreen *screen) con if (m_hwCursor) return Q_NULLPTR; else - return new QEGLPlatformCursor(screen); + return new QEglFSCursor(screen); } void QEglFSKmsIntegration::waitForVSync(QPlatformSurface *surface) const diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsscreen.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsscreen.cpp index 5e49c224a0..60586f98a7 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsscreen.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsscreen.cpp @@ -35,11 +35,11 @@ #include "qeglfskmsscreen.h" #include "qeglfskmsdevice.h" #include "qeglfskmscursor.h" +#include "qeglfsintegration.h" #include <QtCore/QLoggingCategory> #include <QtGui/private/qguiapplication_p.h> -#include <QtPlatformSupport/private/qeglplatformintegration_p.h> #include <QtPlatformSupport/private/qfbvthandler_p.h> QT_BEGIN_NAMESPACE @@ -50,17 +50,13 @@ class QEglFSKmsInterruptHandler : public QObject { public: QEglFSKmsInterruptHandler(QEglFSKmsScreen *screen) : m_screen(screen) { - m_vtHandler = static_cast<QEGLPlatformIntegration *>(QGuiApplicationPrivate::platformIntegration())->vtHandler(); + m_vtHandler = static_cast<QEglFSIntegration *>(QGuiApplicationPrivate::platformIntegration())->vtHandler(); connect(m_vtHandler, &QFbVtHandler::interrupted, this, &QEglFSKmsInterruptHandler::restoreVideoMode); - connect(m_vtHandler, &QFbVtHandler::suspendRequested, this, &QEglFSKmsInterruptHandler::handleSuspendRequest); + connect(m_vtHandler, &QFbVtHandler::aboutToSuspend, this, &QEglFSKmsInterruptHandler::restoreVideoMode); } public slots: void restoreVideoMode() { m_screen->restoreMode(); } - void handleSuspendRequest() { - m_screen->restoreMode(); - m_vtHandler->suspend(); - } private: QFbVtHandler *m_vtHandler; @@ -119,6 +115,7 @@ QEglFSKmsScreen::QEglFSKmsScreen(QEglFSKmsIntegration *integration, , m_output(output) , m_pos(position) , m_cursor(Q_NULLPTR) + , m_powerState(PowerStateOn) , m_interruptHandler(new QEglFSKmsInterruptHandler(this)) { m_siblings << this; @@ -126,6 +123,10 @@ QEglFSKmsScreen::QEglFSKmsScreen(QEglFSKmsIntegration *integration, QEglFSKmsScreen::~QEglFSKmsScreen() { + if (m_output.dpms_prop) { + drmModeFreeProperty(m_output.dpms_prop); + m_output.dpms_prop = Q_NULLPTR; + } restoreMode(); if (m_output.saved_crtc) { drmModeFreeCrtc(m_output.saved_crtc); @@ -266,10 +267,12 @@ void QEglFSKmsScreen::flip() &m_output.connector_id, 1, &m_output.modes[m_output.mode]); - if (ret) + if (ret) { qErrnoWarning("Could not set DRM mode!"); - else + } else { m_output.mode_set = true; + setPowerState(PowerStateOn); + } } int ret = drmModePageFlip(m_device->fd(), @@ -314,4 +317,37 @@ qreal QEglFSKmsScreen::refreshRate() const return refresh > 0 ? refresh : 60; } +QPlatformScreen::SubpixelAntialiasingType QEglFSKmsScreen::subpixelAntialiasingTypeHint() const +{ + switch (m_output.subpixel) { + default: + case DRM_MODE_SUBPIXEL_UNKNOWN: + case DRM_MODE_SUBPIXEL_NONE: + return Subpixel_None; + case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB: + return Subpixel_RGB; + case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR: + return Subpixel_BGR; + case DRM_MODE_SUBPIXEL_VERTICAL_RGB: + return Subpixel_VRGB; + case DRM_MODE_SUBPIXEL_VERTICAL_BGR: + return Subpixel_VBGR; + } +} + +QPlatformScreen::PowerState QEglFSKmsScreen::powerState() const +{ + return m_powerState; +} + +void QEglFSKmsScreen::setPowerState(QPlatformScreen::PowerState state) +{ + if (!m_output.dpms_prop) + return; + + drmModeConnectorSetProperty(m_device->fd(), m_output.connector_id, + m_output.dpms_prop->prop_id, (int)state); + m_powerState = state; +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsscreen.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsscreen.h index 4c1b0d02ad..7fd6ccaa31 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsscreen.h +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsscreen.h @@ -60,6 +60,8 @@ struct QEglFSKmsOutput bool mode_set; drmModeCrtcPtr saved_crtc; QList<drmModeModeInfo> modes; + int subpixel; + drmModePropertyPtr dpms_prop; }; class QEglFSKmsScreen : public QEglFSScreen @@ -103,6 +105,11 @@ public: QEglFSKmsOutput &output() { return m_output; } void restoreMode(); + SubpixelAntialiasingType subpixelAntialiasingTypeHint() const Q_DECL_OVERRIDE; + + QPlatformScreen::PowerState powerState() const Q_DECL_OVERRIDE; + void setPowerState(QPlatformScreen::PowerState state) Q_DECL_OVERRIDE; + private: QEglFSKmsIntegration *m_integration; QEglFSKmsDevice *m_device; @@ -117,6 +124,8 @@ private: QList<QPlatformScreen *> m_siblings; + PowerState m_powerState; + struct FrameBuffer { FrameBuffer() : fb(0) {} uint32_t fb; diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/eglfs_kms_egldevice.json b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/eglfs_kms_egldevice.json new file mode 100644 index 0000000000..169ba1eb02 --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/eglfs_kms_egldevice.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "eglfs_kms_egldevice" ] +} diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/eglfs_kms_egldevice.pro b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/eglfs_kms_egldevice.pro new file mode 100644 index 0000000000..393ddd14a5 --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/eglfs_kms_egldevice.pro @@ -0,0 +1,23 @@ +TARGET = qeglfs-kms-egldevice-integration + +PLUGIN_TYPE = egldeviceintegrations +PLUGIN_CLASS_NAME = QEglFSKmsEglDeviceIntegrationPlugin +load(qt_plugin) + +QT += core-private gui-private platformsupport-private eglfs_device_lib-private + +INCLUDEPATH += $$PWD/../.. + +DEFINES += MESA_EGL_NO_X11_HEADERS + +CONFIG += egl +QMAKE_LFLAGS += $$QMAKE_LFLAGS_NOUNDEF + +SOURCES += $$PWD/qeglfskmsegldevicemain.cpp \ + $$PWD/qeglfskmsegldeviceintegration.cpp + +HEADERS += $$PWD/qeglfskmsegldeviceintegration.h + +OTHER_FILES += $$PWD/eglfs_kms_egldevice.json + +LIBS += -ldrm diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.cpp new file mode 100644 index 0000000000..1ddcb3b862 --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.cpp @@ -0,0 +1,409 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qeglfskmsegldeviceintegration.h" +#include <QLoggingCategory> + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(qLcEglfsKmsDebug, "qt.qpa.eglfs.kms") + +QEglFSKmsEglDeviceIntegration::QEglFSKmsEglDeviceIntegration() + : m_dri_fd(-1) + , m_egl_device(EGL_NO_DEVICE_EXT) + , m_egl_display(EGL_NO_DISPLAY) + , m_drm_connector(Q_NULLPTR) + , m_drm_encoder(Q_NULLPTR) + , m_drm_crtc(0) + , m_funcs(Q_NULLPTR) +{ + qCDebug(qLcEglfsKmsDebug, "New DRM/KMS on EGLDevice integration created"); +} + +void QEglFSKmsEglDeviceIntegration::platformInit() +{ + if (!query_egl_device()) + qFatal("Could not set up EGL device!"); + + const char *deviceName = m_funcs->query_device_string(m_egl_device, EGL_DRM_DEVICE_FILE_EXT); + if (!deviceName) + qFatal("Failed to query device name from EGLDevice"); + + qCDebug(qLcEglfsKmsDebug, "Opening %s", deviceName); + + m_dri_fd = drmOpen(deviceName, Q_NULLPTR); + if (m_dri_fd < 0) + qFatal("Could not open DRM device"); + + if (!setup_kms()) + qFatal("Could not set up KMS on device %s!", m_device.constData()); + + qCDebug(qLcEglfsKmsDebug, "DRM/KMS initialized"); +} + +void QEglFSKmsEglDeviceIntegration::platformDestroy() +{ + if (qt_safe_close(m_dri_fd) == -1) + qErrnoWarning("Could not close DRM device"); + + m_dri_fd = -1; + + delete m_funcs; + m_funcs = Q_NULLPTR; +} + +EGLNativeDisplayType QEglFSKmsEglDeviceIntegration::platformDisplay() const +{ + return static_cast<EGLNativeDisplayType>(m_egl_device); +} + +EGLDisplay QEglFSKmsEglDeviceIntegration::createDisplay(EGLNativeDisplayType nativeDisplay) +{ + qCDebug(qLcEglfsKmsDebug, "Creating display"); + + EGLDisplay display; + + if (m_funcs->has_egl_platform_device) { + display = m_funcs->get_platform_display(EGL_PLATFORM_DEVICE_EXT, nativeDisplay, Q_NULLPTR); + } else { + qWarning("EGL_EXT_platform_device not available, falling back to legacy path!"); + display = eglGetDisplay(nativeDisplay); + } + + if (display == EGL_NO_DISPLAY) + qFatal("Could not get EGL display"); + + EGLint major, minor; + if (!eglInitialize(display, &major, &minor)) + qFatal("Could not initialize egl display"); + + if (!eglBindAPI(EGL_OPENGL_ES_API)) + qFatal("Failed to bind EGL_OPENGL_ES_API\n"); + + return display; +} + +QSizeF QEglFSKmsEglDeviceIntegration::physicalScreenSize() const +{ + return QSizeF(m_drm_connector->mmWidth, m_drm_connector->mmHeight); +} + +QSize QEglFSKmsEglDeviceIntegration::screenSize() const +{ + return QSize(m_drm_mode.hdisplay, m_drm_mode.vdisplay); +} + +int QEglFSKmsEglDeviceIntegration::screenDepth() const +{ + return 32; +} + +QSurfaceFormat QEglFSKmsEglDeviceIntegration::surfaceFormatFor(const QSurfaceFormat &inputFormat) const +{ + QSurfaceFormat format(inputFormat); + format.setRenderableType(QSurfaceFormat::OpenGLES); + format.setSwapBehavior(QSurfaceFormat::DoubleBuffer); + format.setRedBufferSize(8); + format.setGreenBufferSize(8); + format.setBlueBufferSize(8); + return format; +} + +EGLint QEglFSKmsEglDeviceIntegration::surfaceType() const +{ + return EGL_STREAM_BIT_KHR; +} + +class QEglJetsonTK1Window : public QEglFSWindow +{ +public: + QEglJetsonTK1Window(QWindow *w, const QEglFSKmsEglDeviceIntegration *integration) + : QEglFSWindow(w) + , m_integration(integration) + , m_egl_stream(EGL_NO_STREAM_KHR) + { } + + void invalidateSurface() Q_DECL_OVERRIDE; + void resetSurface() Q_DECL_OVERRIDE; + + const QEglFSKmsEglDeviceIntegration *m_integration; + EGLStreamKHR m_egl_stream; + EGLint m_latency; +}; + +void QEglJetsonTK1Window::invalidateSurface() +{ + QEglFSWindow::invalidateSurface(); + m_integration->m_funcs->destroy_stream(screen()->display(), m_egl_stream); +} + +void QEglJetsonTK1Window::resetSurface() +{ + qCDebug(qLcEglfsKmsDebug, "Creating stream"); + + EGLDisplay display = screen()->display(); + EGLOutputLayerEXT layer = EGL_NO_OUTPUT_LAYER_EXT; + EGLint count; + + m_egl_stream = m_integration->m_funcs->create_stream(display, Q_NULLPTR); + if (m_egl_stream == EGL_NO_STREAM_KHR) { + qWarning("resetSurface: Couldn't create EGLStream for native window"); + return; + } + + qCDebug(qLcEglfsKmsDebug, "Created stream %p on display %p", m_egl_stream, display); + + if (!m_integration->m_funcs->get_output_layers(display, Q_NULLPTR, Q_NULLPTR, 0, &count) || count == 0) { + qWarning("No output layers found"); + return; + } + + qCDebug(qLcEglfsKmsDebug, "Output has %d layers", count); + + QVector<EGLOutputLayerEXT> layers; + layers.resize(count); + EGLint actualCount; + if (!m_integration->m_funcs->get_output_layers(display, Q_NULLPTR, layers.data(), count, &actualCount)) { + qWarning("Failed to get layers"); + return; + } + + for (int i = 0; i < actualCount; ++i) { + EGLAttrib id; + if (m_integration->m_funcs->query_output_layer_attrib(display, layers[i], EGL_DRM_CRTC_EXT, &id)) { + qCDebug(qLcEglfsKmsDebug, " [%d] layer %p - crtc %d", i, layers[i], (int) id); + if (id == EGLAttrib(m_integration->m_drm_crtc)) + layer = layers[i]; + } else if (m_integration->m_funcs->query_output_layer_attrib(display, layers[i], EGL_DRM_PLANE_EXT, &id)) { + // Not used yet, just for debugging. + qCDebug(qLcEglfsKmsDebug, " [%d] layer %p - plane %d", i, layers[i], (int) id); + } else { + qCDebug(qLcEglfsKmsDebug, " [%d] layer %p - unknown", i, layers[i]); + } + } + + QByteArray reqLayerIndex = qgetenv("QT_QPA_EGLFS_LAYER_INDEX"); + if (!reqLayerIndex.isEmpty()) { + int idx = reqLayerIndex.toInt(); + if (idx >= 0 && idx < layers.count()) + layer = layers[idx]; + } + + if (layer == EGL_NO_OUTPUT_LAYER_EXT) { + qWarning("resetSurface: Couldn't get EGLOutputLayer for native window"); + return; + } + + qCDebug(qLcEglfsKmsDebug, "Using layer %p", layer); + + if (!m_integration->m_funcs->stream_consumer_output(display, m_egl_stream, layer)) + qWarning("resetSurface: Unable to connect stream"); + + m_config = QEglFSIntegration::chooseConfig(display, m_integration->surfaceFormatFor(window()->requestedFormat())); + m_format = q_glFormatFromConfig(display, m_config); + qCDebug(qLcEglfsKmsDebug) << "Stream producer format is" << m_format; + + const int w = m_integration->screenSize().width(); + const int h = m_integration->screenSize().height(); + qCDebug(qLcEglfsKmsDebug, "Creating stream producer surface of size %dx%d", w, h); + + const EGLint stream_producer_attribs[] = { + EGL_WIDTH, w, + EGL_HEIGHT, h, + EGL_NONE + }; + + m_surface = m_integration->m_funcs->create_stream_producer_surface(display, m_config, m_egl_stream, stream_producer_attribs); + if (m_surface == EGL_NO_SURFACE) + return; + + qCDebug(qLcEglfsKmsDebug, "Created stream producer surface %p", m_surface); +} + +QEglFSWindow *QEglFSKmsEglDeviceIntegration::createWindow(QWindow *window) const +{ + QEglJetsonTK1Window *eglWindow = new QEglJetsonTK1Window(window, this); + + m_funcs->initialize(eglWindow->screen()->display()); + if (!(m_funcs->has_egl_output_base && m_funcs->has_egl_output_drm && m_funcs->has_egl_stream + && m_funcs->has_egl_stream_producer_eglsurface && m_funcs->has_egl_stream_consumer_egloutput)) + qFatal("Required extensions missing!"); + + return eglWindow; +} + +bool QEglFSKmsEglDeviceIntegration::hasCapability(QPlatformIntegration::Capability cap) const +{ + switch (cap) { + case QPlatformIntegration::ThreadedPixmaps: + case QPlatformIntegration::OpenGL: + case QPlatformIntegration::ThreadedOpenGL: + case QPlatformIntegration::BufferQueueingOpenGL: + return true; + default: + return false; + } +} + +void QEglFSKmsEglDeviceIntegration::waitForVSync(QPlatformSurface *) const +{ + static bool mode_set = false; + + if (!mode_set) { + mode_set = true; + + drmModeCrtcPtr currentMode = drmModeGetCrtc(m_dri_fd, m_drm_crtc); + const bool alreadySet = currentMode + && currentMode->width == m_drm_mode.hdisplay + && currentMode->height == m_drm_mode.vdisplay; + if (currentMode) + drmModeFreeCrtc(currentMode); + if (alreadySet) { + qCDebug(qLcEglfsKmsDebug, "Mode already set"); + return; + } + + qCDebug(qLcEglfsKmsDebug, "Setting mode"); + int ret = drmModeSetCrtc(m_dri_fd, m_drm_crtc, + -1, 0, 0, + &m_drm_connector->connector_id, 1, + const_cast<const drmModeModeInfoPtr>(&m_drm_mode)); + if (ret) + qFatal("drmModeSetCrtc failed"); + } +} + +qreal QEglFSKmsEglDeviceIntegration::refreshRate() const +{ + quint32 refresh = m_drm_mode.vrefresh; + return refresh > 0 ? refresh : 60; +} + +bool QEglFSKmsEglDeviceIntegration::supportsSurfacelessContexts() const +{ + // Returning false disables the usage of EGL_KHR_surfaceless_context even when the + // extension is available. This is just what we need since, at least with NVIDIA + // 352.00 making a null surface current with a context breaks. + return false; +} + +bool QEglFSKmsEglDeviceIntegration::setup_kms() +{ + drmModeRes *resources; + drmModeConnector *connector; + drmModeEncoder *encoder; + quint32 crtc = 0; + int i; + + resources = drmModeGetResources(m_dri_fd); + if (!resources) { + qWarning("drmModeGetResources failed"); + return false; + } + + for (i = 0; i < resources->count_connectors; i++) { + connector = drmModeGetConnector(m_dri_fd, resources->connectors[i]); + if (!connector) + continue; + + if (connector->connection == DRM_MODE_CONNECTED && connector->count_modes > 0) + break; + + drmModeFreeConnector(connector); + } + + if (i == resources->count_connectors) { + qWarning("No currently active connector found."); + return false; + } + + qCDebug(qLcEglfsKmsDebug, "Using connector with type %d", connector->connector_type); + + for (i = 0; i < resources->count_encoders; i++) { + encoder = drmModeGetEncoder(m_dri_fd, resources->encoders[i]); + if (!encoder) + continue; + + if (encoder->encoder_id == connector->encoder_id) + break; + + drmModeFreeEncoder(encoder); + } + + for (int j = 0; j < resources->count_crtcs; j++) { + if ((encoder->possible_crtcs & (1 << j))) { + crtc = resources->crtcs[j]; + break; + } + } + + if (crtc == 0) + qFatal("No suitable CRTC available"); + + m_drm_connector = connector; + m_drm_encoder = encoder; + m_drm_mode = connector->modes[0]; + m_drm_crtc = crtc; + + qCDebug(qLcEglfsKmsDebug).noquote() << "Using crtc" << m_drm_crtc + << "with mode" << m_drm_mode.hdisplay << "x" << m_drm_mode.vdisplay + << "@" << m_drm_mode.vrefresh; + + drmModeFreeResources(resources); + + return true; +} + +bool QEglFSKmsEglDeviceIntegration::query_egl_device() +{ + m_funcs = new QEGLStreamConvenience; + if (!m_funcs->has_egl_device_base) + qFatal("EGL_EXT_device_base missing"); + + EGLint num_devices = 0; + if (m_funcs->query_devices(1, &m_egl_device, &num_devices) != EGL_TRUE) { + qWarning("eglQueryDevicesEXT failed: eglError: %x", eglGetError()); + return false; + } + + qCDebug(qLcEglfsKmsDebug, "Found %d EGL devices", num_devices); + + if (num_devices < 1 || m_egl_device == EGL_NO_DEVICE_EXT) { + qWarning("eglQueryDevicesEXT could not find any EGL devices"); + return false; + } + + return true; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.h new file mode 100644 index 0000000000..a89a65ca55 --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QEGLFSKMSEGLDEVICEINTEGRATION_H +#define QEGLFSKMSEGLDEVICEINTEGRATION_H + +#include "qeglfsdeviceintegration.h" +#include "qeglfswindow.h" +#include "qeglfsintegration.h" + +#include <QtPlatformSupport/private/qdevicediscovery_p.h> +#include <QtPlatformSupport/private/qeglconvenience_p.h> +#include <QtCore/private/qcore_unix_p.h> +#include <QtCore/QScopedPointer> +#include <QtGui/qpa/qplatformwindow.h> +#include <QtGui/qguiapplication.h> +#include <QDebug> + +#include <xf86drm.h> +#include <xf86drmMode.h> + +#include <QtPlatformSupport/private/qeglstreamconvenience_p.h> + +QT_BEGIN_NAMESPACE + +class QEglFSKmsEglDeviceIntegration : public QEGLDeviceIntegration +{ +public: + QEglFSKmsEglDeviceIntegration(); + + void platformInit() Q_DECL_OVERRIDE; + void platformDestroy() Q_DECL_OVERRIDE; + EGLNativeDisplayType platformDisplay() const Q_DECL_OVERRIDE; + EGLDisplay createDisplay(EGLNativeDisplayType nativeDisplay) Q_DECL_OVERRIDE; + QSizeF physicalScreenSize() const Q_DECL_OVERRIDE; + QSize screenSize() const Q_DECL_OVERRIDE; + int screenDepth() const Q_DECL_OVERRIDE; + qreal refreshRate() const Q_DECL_OVERRIDE; + QSurfaceFormat surfaceFormatFor(const QSurfaceFormat &inputFormat) const Q_DECL_OVERRIDE; + EGLint surfaceType() const Q_DECL_OVERRIDE; + QEglFSWindow *createWindow(QWindow *window) const Q_DECL_OVERRIDE; + bool hasCapability(QPlatformIntegration::Capability cap) const Q_DECL_OVERRIDE; + void waitForVSync(QPlatformSurface *surface) const Q_DECL_OVERRIDE; + bool supportsSurfacelessContexts() const Q_DECL_OVERRIDE; + + bool setup_kms(); + bool query_egl_device(); + + // device bits + QByteArray m_device; + int m_dri_fd; + EGLDeviceEXT m_egl_device; + EGLDisplay m_egl_display; + + // KMS bits + drmModeConnector *m_drm_connector; + drmModeEncoder *m_drm_encoder; + drmModeModeInfo m_drm_mode; + quint32 m_drm_crtc; + + // EGLStream infrastructure + QEGLStreamConvenience *m_funcs; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/cocoa/qcocoaautoreleasepool.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevicemain.cpp index 8b2a9f3788..f987ae38a6 100644 --- a/src/plugins/platforms/cocoa/qcocoaautoreleasepool.h +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevicemain.cpp @@ -3,7 +3,7 @@ ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** -** This file is part of the plugins of the Qt Toolkit. +** This file is part of the qmake spec of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL21$ ** Commercial License Usage @@ -31,23 +31,19 @@ ** ****************************************************************************/ -#ifndef QCOCOAAUTORELEASEPOOL_H -#define QCOCOAAUTORELEASEPOOL_H - -#undef slots -#include <qglobal.h> -#include <Cocoa/Cocoa.h> +#include "qeglfskmsegldeviceintegration.h" QT_BEGIN_NAMESPACE -class QCocoaAutoReleasePool + +class QEglFSKmsEglDeviceIntegrationPlugin : public QEGLDeviceIntegrationPlugin { -public: - QCocoaAutoReleasePool(); - ~QCocoaAutoReleasePool(); + Q_OBJECT + Q_PLUGIN_METADATA(IID QEGLDeviceIntegrationFactoryInterface_iid FILE "eglfs_kms_egldevice.json") -private: - NSAutoreleasePool *pool; +public: + QEGLDeviceIntegration *create() Q_DECL_OVERRIDE { return new QEglFSKmsEglDeviceIntegration; } }; + QT_END_NAMESPACE -#endif // QCOCOAAUTORELEASEPOOL_H +#include "qeglfskmsegldevicemain.moc" diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_mali/qeglfsmaliintegration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_mali/qeglfsmaliintegration.cpp index 455d78035a..43decdf849 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_mali/qeglfsmaliintegration.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_mali/qeglfsmaliintegration.cpp @@ -32,7 +32,6 @@ ****************************************************************************/ #include "qeglfsmaliintegration.h" -#include <EGL/fbdev_window.h> #include <unistd.h> #include <fcntl.h> @@ -43,6 +42,11 @@ QT_BEGIN_NAMESPACE +struct fbdev_window { + unsigned short width; + unsigned short height; +}; + void QEglFSMaliIntegration::platformInit() { // Keep the non-overridden base class functions based on fb0 working. diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/eglfs_viv_wl.json b/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/eglfs_viv_wl.json new file mode 100644 index 0000000000..ced5245fa0 --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/eglfs_viv_wl.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "eglfs_viv_wl" ] +} diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/eglfs_viv_wl.pro b/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/eglfs_viv_wl.pro new file mode 100644 index 0000000000..26b6a2e9ea --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/eglfs_viv_wl.pro @@ -0,0 +1,23 @@ +TARGET = qeglfs-viv-wl-integration + +PLUGIN_TYPE = egldeviceintegrations +PLUGIN_CLASS_NAME = QEglFSVivWaylandIntegrationPlugin +load(qt_plugin) + +QT += core-private gui-private platformsupport-private eglfs_device_lib-private + +INCLUDEPATH += $$PWD/../.. +CONFIG += egl +DEFINES += LINUX=1 EGL_API_FB=1 +LIBS += -lGAL +QMAKE_LFLAGS += $$QMAKE_LFLAGS_NOUNDEF + +SOURCES += $$PWD/qeglfsvivwlmain.cpp \ + $$PWD/qeglfsvivwlintegration.cpp + +HEADERS += $$PWD/qeglfsvivwlintegration.h + +OTHER_FILES += $$PWD/eglfs_viv_wl.json + +CONFIG += link_pkgconfig +PKGCONFIG_PRIVATE += wayland-server diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/qeglfsvivwlintegration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/qeglfsvivwlintegration.cpp new file mode 100644 index 0000000000..9eebcc772a --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/qeglfsvivwlintegration.cpp @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qeglfsvivwlintegration.h" +#include <EGL/eglvivante.h> +#include <QDebug> + +#include <wayland-server.h> + +QT_BEGIN_NAMESPACE + +void QEglFSVivWaylandIntegration::platformInit() +{ + QEGLDeviceIntegration::platformInit(); + + int width, height; + + bool multiBufferNotEnabledYet = qEnvironmentVariableIsEmpty("FB_MULTI_BUFFER"); + bool multiBuffer = qEnvironmentVariableIsEmpty("QT_EGLFS_IMX6_NO_FB_MULTI_BUFFER"); + if (multiBufferNotEnabledYet && multiBuffer) { + qWarning() << "QEglFSVivWaylandIntegration will set environment variable FB_MULTI_BUFFER=2 to enable double buffering and vsync.\n" + << "If this is not desired, you can override this via: export QT_EGLFS_IMX6_NO_FB_MULTI_BUFFER=1"; + qputenv("FB_MULTI_BUFFER", "2"); + } + + mWaylandDisplay = wl_display_create(); + mNativeDisplay = fbGetDisplay(mWaylandDisplay); + fbGetDisplayGeometry(mNativeDisplay, &width, &height); + mScreenSize.setHeight(height); + mScreenSize.setWidth(width); +} + +QSize QEglFSVivWaylandIntegration::screenSize() const +{ + return mScreenSize; +} + +EGLNativeDisplayType QEglFSVivWaylandIntegration::platformDisplay() const +{ + return mNativeDisplay; +} + +EGLNativeWindowType QEglFSVivWaylandIntegration::createNativeWindow(QPlatformWindow *window, const QSize &size, const QSurfaceFormat &format) +{ + Q_UNUSED(window) + Q_UNUSED(format) + + EGLNativeWindowType eglWindow = fbCreateWindow(mNativeDisplay, 0, 0, size.width(), size.height()); + return eglWindow; +} + +void QEglFSVivWaylandIntegration::destroyNativeWindow(EGLNativeWindowType window) +{ + fbDestroyWindow(window); +} + +void *QEglFSVivWaylandIntegration::wlDisplay() const +{ + return mWaylandDisplay; +} + + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsguieventdispatcher.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/qeglfsvivwlintegration.h index 3389139461..677a1b6337 100644 --- a/src/plugins/platforms/windows/qwindowsguieventdispatcher.h +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/qeglfsvivwlintegration.h @@ -31,30 +31,30 @@ ** ****************************************************************************/ -#ifndef QWINDOWSGUIEVENTDISPATCHER_H -#define QWINDOWSGUIEVENTDISPATCHER_H +#ifndef QEGLFSVIVINTEGRATION_H +#define QEGLFSVIVINTEGRATION_H -#include "qtwindows_additional.h" - -#include <QtCore/private/qeventdispatcher_win_p.h> +#include "qeglfsdeviceintegration.h" +struct wl_display; QT_BEGIN_NAMESPACE -class QWindowsGuiEventDispatcher : public QEventDispatcherWin32 +class QEglFSVivWaylandIntegration : public QEGLDeviceIntegration { - Q_OBJECT public: - explicit QWindowsGuiEventDispatcher(QObject *parent = 0); - - static const char *windowsMessageName(UINT msg); - - bool QT_ENSURE_STACK_ALIGNED_FOR_SSE processEvents(QEventLoop::ProcessEventsFlags flags) Q_DECL_OVERRIDE; - void sendPostedEvents() Q_DECL_OVERRIDE; + void platformInit() Q_DECL_OVERRIDE; + QSize screenSize() const Q_DECL_OVERRIDE; + EGLNativeWindowType createNativeWindow(QPlatformWindow *window, const QSize &size, const QSurfaceFormat &format) Q_DECL_OVERRIDE; + void destroyNativeWindow(EGLNativeWindowType window) Q_DECL_OVERRIDE; + EGLNativeDisplayType platformDisplay() const Q_DECL_OVERRIDE; + void *wlDisplay() const Q_DECL_OVERRIDE; private: - QEventLoop::ProcessEventsFlags m_flags; + QSize mScreenSize; + EGLNativeDisplayType mNativeDisplay; + wl_display *mWaylandDisplay; }; QT_END_NAMESPACE -#endif // QWINDOWSGUIEVENTDISPATCHER_H +#endif diff --git a/src/plugins/platforms/kms/qkmswindow.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/qeglfsvivwlmain.cpp index aec6d55b5d..a48aa08e40 100644 --- a/src/plugins/platforms/kms/qkmswindow.h +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/qeglfsvivwlmain.cpp @@ -31,27 +31,20 @@ ** ****************************************************************************/ -#ifndef QKMSWINDOW_H -#define QKMSWINDOW_H - -#include <qpa/qplatformwindow.h> +#include "qeglfsdeviceintegration.h" +#include "qeglfsvivwlintegration.h" QT_BEGIN_NAMESPACE -class QKmsWindow : public QPlatformWindow +class QEglFSVivWaylandIntegrationPlugin : public QEGLDeviceIntegrationPlugin { - Q_DECLARE_PRIVATE(QPlatformWindow) + Q_OBJECT + Q_PLUGIN_METADATA(IID QEGLDeviceIntegrationFactoryInterface_iid FILE "eglfs_viv_wl.json") public: - QKmsWindow(QWindow *window); - - void setGeometry(const QRect &rect) Q_DECL_OVERRIDE; - QSurfaceFormat format() const Q_DECL_OVERRIDE; - -private: - QPlatformScreen *m_screen; + QEGLDeviceIntegration *create() Q_DECL_OVERRIDE { return new QEglFSVivWaylandIntegration; } }; QT_END_NAMESPACE -#endif // QKMSWINDOW_H +#include "qeglfsvivwlmain.moc" diff --git a/src/plugins/platforms/eglfs/eglfs_device_lib.pro b/src/plugins/platforms/eglfs/eglfs_device_lib.pro index 729290706d..4fe2ce4897 100644 --- a/src/plugins/platforms/eglfs/eglfs_device_lib.pro +++ b/src/plugins/platforms/eglfs/eglfs_device_lib.pro @@ -6,13 +6,6 @@ TARGET = QtEglDeviceIntegration CONFIG += no_module_headers internal_module -MODULE_INCLUDES = \ - \$\$QT_MODULE_INCLUDE_BASE \ - \$\$QT_MODULE_INCLUDE_BASE/QtQGui -MODULE_PRIVATE_INCLUDES = \ - \$\$QT_MODULE_INCLUDE_BASE/QtGui/$$QT.gui.VERSION \ - \$\$QT_MODULE_INCLUDE_BASE/QtGui/$$QT.gui.VERSION/QtGui - load(qt_module) QT += core-private gui-private platformsupport-private @@ -26,6 +19,7 @@ DEFINES += QT_BUILD_EGL_DEVICE_LIB SOURCES += $$PWD/qeglfsintegration.cpp \ $$PWD/qeglfswindow.cpp \ $$PWD/qeglfsscreen.cpp \ + $$PWD/qeglfscursor.cpp \ $$PWD/qeglfshooks.cpp \ $$PWD/qeglfscontext.cpp \ $$PWD/qeglfsoffscreenwindow.cpp \ @@ -34,6 +28,7 @@ SOURCES += $$PWD/qeglfsintegration.cpp \ HEADERS += $$PWD/qeglfsintegration.h \ $$PWD/qeglfswindow.h \ $$PWD/qeglfsscreen.h \ + $$PWD/qeglfscursor.h \ $$PWD/qeglfshooks.h \ $$PWD/qeglfscontext.h \ $$PWD/qeglfsoffscreenwindow.h \ diff --git a/src/plugins/platforms/eglfs/qeglfscontext.cpp b/src/plugins/platforms/eglfs/qeglfscontext.cpp index 9216b7a85d..6fcdae7ad2 100644 --- a/src/plugins/platforms/eglfs/qeglfscontext.cpp +++ b/src/plugins/platforms/eglfs/qeglfscontext.cpp @@ -32,21 +32,20 @@ ****************************************************************************/ #include <QtGui/QSurface> -#include <QtDebug> - -#include <QtPlatformSupport/private/qeglplatformcursor_p.h> #include <QtPlatformSupport/private/qeglconvenience_p.h> #include <QtPlatformSupport/private/qeglpbuffer_p.h> +#include "qeglfscontext.h" #include "qeglfswindow.h" #include "qeglfshooks.h" -#include "qeglfscontext.h" +#include "qeglfscursor.h" QT_BEGIN_NAMESPACE QEglFSContext::QEglFSContext(const QSurfaceFormat &format, QPlatformOpenGLContext *share, EGLDisplay display, EGLConfig *config, const QVariant &nativeHandle) - : QEGLPlatformContext(format, share, display, config, nativeHandle), + : QEGLPlatformContext(format, share, display, config, nativeHandle, + qt_egl_device_integration()->supportsSurfacelessContexts() ? Flags(0) : QEGLPlatformContext::NoSurfaceless), m_tempWindow(0) { } @@ -91,7 +90,7 @@ void QEglFSContext::swapBuffers(QPlatformSurface *surface) // draw the cursor if (surface->surface()->surfaceClass() == QSurface::Window) { QPlatformWindow *window = static_cast<QPlatformWindow *>(surface); - if (QEGLPlatformCursor *cursor = qobject_cast<QEGLPlatformCursor *>(window->screen()->cursor())) + if (QEglFSCursor *cursor = qobject_cast<QEglFSCursor *>(window->screen()->cursor())) cursor->paintOnScreen(); } diff --git a/src/plugins/platforms/eglfs/qeglfscursor.cpp b/src/plugins/platforms/eglfs/qeglfscursor.cpp new file mode 100644 index 0000000000..eea130a754 --- /dev/null +++ b/src/plugins/platforms/eglfs/qeglfscursor.cpp @@ -0,0 +1,502 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qeglfscursor.h" +#include "qeglfsintegration.h" +#include "qeglfsscreen.h" + +#include <qpa/qwindowsysteminterface.h> +#include <QtGui/QOpenGLContext> +#include <QtGui/QOpenGLShaderProgram> +#include <QtCore/QJsonDocument> +#include <QtCore/QJsonArray> +#include <QtCore/QJsonObject> + +#include <QtGui/private/qguiapplication_p.h> +#include <QtGui/private/qopenglvertexarrayobject_p.h> + +#ifndef GL_VERTEX_ARRAY_BINDING +#define GL_VERTEX_ARRAY_BINDING 0x85B5 +#endif + +QT_BEGIN_NAMESPACE + +QEglFSCursor::QEglFSCursor(QPlatformScreen *screen) + : m_visible(true), + m_screen(static_cast<QEglFSScreen *>(screen)), + m_program(0), + m_textureEntry(0), + m_deviceListener(0), + m_updateRequested(false) +{ + QByteArray hideCursorVal = qgetenv("QT_QPA_EGLFS_HIDECURSOR"); + if (!hideCursorVal.isEmpty()) + m_visible = hideCursorVal.toInt() == 0; + if (!m_visible) + return; + + // Try to load the cursor atlas. If this fails, m_visible is set to false and + // paintOnScreen() and setCurrentCursor() become no-ops. + initCursorAtlas(); + + // initialize the cursor +#ifndef QT_NO_CURSOR + QCursor cursor(Qt::ArrowCursor); + setCurrentCursor(&cursor); +#endif + + m_deviceListener = new QEglFSCursorDeviceListener(this); + connect(QGuiApplicationPrivate::inputDeviceManager(), &QInputDeviceManager::deviceListChanged, + m_deviceListener, &QEglFSCursorDeviceListener::onDeviceListChanged); + updateMouseStatus(); +} + +QEglFSCursor::~QEglFSCursor() +{ + resetResources(); + delete m_deviceListener; +} + +void QEglFSCursor::updateMouseStatus() +{ + m_visible = m_deviceListener->hasMouse(); +} + +bool QEglFSCursorDeviceListener::hasMouse() const +{ + return QGuiApplicationPrivate::inputDeviceManager()->deviceCount(QInputDeviceManager::DeviceTypePointer) > 0; +} + +void QEglFSCursorDeviceListener::onDeviceListChanged(QInputDeviceManager::DeviceType type) +{ + if (type == QInputDeviceManager::DeviceTypePointer) + m_cursor->updateMouseStatus(); +} + +void QEglFSCursor::resetResources() +{ + if (QOpenGLContext::currentContext()) { + delete m_program; + glDeleteTextures(1, &m_cursor.customCursorTexture); + glDeleteTextures(1, &m_cursorAtlas.texture); + } + m_program = 0; + m_cursor.customCursorTexture = 0; + m_cursor.customCursorPending = !m_cursor.customCursorImage.isNull(); + m_cursorAtlas.texture = 0; +} + +void QEglFSCursor::createShaderPrograms() +{ + static const char *textureVertexProgram = + "attribute highp vec2 vertexCoordEntry;\n" + "attribute highp vec2 textureCoordEntry;\n" + "varying highp vec2 textureCoord;\n" + "void main() {\n" + " textureCoord = textureCoordEntry;\n" + " gl_Position = vec4(vertexCoordEntry, 1.0, 1.0);\n" + "}\n"; + + static const char *textureFragmentProgram = + "uniform sampler2D texture;\n" + "varying highp vec2 textureCoord;\n" + "void main() {\n" + " gl_FragColor = texture2D(texture, textureCoord).bgra;\n" + "}\n"; + + m_program = new QOpenGLShaderProgram; + m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, textureVertexProgram); + m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, textureFragmentProgram); + m_program->bindAttributeLocation("vertexCoordEntry", 0); + m_program->bindAttributeLocation("textureCoordEntry", 1); + m_program->link(); + + m_textureEntry = m_program->uniformLocation("texture"); +} + +void QEglFSCursor::createCursorTexture(uint *texture, const QImage &image) +{ + if (!*texture) + glGenTextures(1, texture); + glBindTexture(GL_TEXTURE_2D, *texture); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glTexImage2D(GL_TEXTURE_2D, 0 /* level */, GL_RGBA, image.width(), image.height(), 0 /* border */, + GL_RGBA, GL_UNSIGNED_BYTE, image.constBits()); +} + +void QEglFSCursor::initCursorAtlas() +{ + static QByteArray json = qgetenv("QT_QPA_EGLFS_CURSOR"); + if (json.isEmpty()) + json = ":/cursor.json"; + + QFile file(QString::fromUtf8(json)); + if (!file.open(QFile::ReadOnly)) { + m_visible = false; + return; + } + + QJsonDocument doc = QJsonDocument::fromJson(file.readAll()); + QJsonObject object = doc.object(); + + QString atlas = object.value(QLatin1String("image")).toString(); + Q_ASSERT(!atlas.isEmpty()); + + const int cursorsPerRow = object.value(QLatin1String("cursorsPerRow")).toDouble(); + Q_ASSERT(cursorsPerRow); + m_cursorAtlas.cursorsPerRow = cursorsPerRow; + + const QJsonArray hotSpots = object.value(QLatin1String("hotSpots")).toArray(); + Q_ASSERT(hotSpots.count() == Qt::LastCursor + 1); + for (int i = 0; i < hotSpots.count(); i++) { + QPoint hotSpot(hotSpots[i].toArray()[0].toDouble(), hotSpots[i].toArray()[1].toDouble()); + m_cursorAtlas.hotSpots << hotSpot; + } + + QImage image = QImage(atlas).convertToFormat(QImage::Format_ARGB32_Premultiplied); + m_cursorAtlas.cursorWidth = image.width() / m_cursorAtlas.cursorsPerRow; + m_cursorAtlas.cursorHeight = image.height() / ((Qt::LastCursor + cursorsPerRow) / cursorsPerRow); + m_cursorAtlas.width = image.width(); + m_cursorAtlas.height = image.height(); + m_cursorAtlas.image = image; +} + +#ifndef QT_NO_CURSOR +void QEglFSCursor::changeCursor(QCursor *cursor, QWindow *window) +{ + Q_UNUSED(window); + const QRect oldCursorRect = cursorRect(); + if (setCurrentCursor(cursor)) + update(oldCursorRect | cursorRect()); +} + +bool QEglFSCursor::setCurrentCursor(QCursor *cursor) +{ + if (!m_visible) + return false; + + const Qt::CursorShape newShape = cursor ? cursor->shape() : Qt::ArrowCursor; + if (m_cursor.shape == newShape && newShape != Qt::BitmapCursor) + return false; + + if (m_cursor.shape == Qt::BitmapCursor) { + m_cursor.customCursorImage = QImage(); + m_cursor.customCursorPending = false; + } + m_cursor.shape = newShape; + if (newShape != Qt::BitmapCursor) { // standard cursor + const float ws = (float)m_cursorAtlas.cursorWidth / m_cursorAtlas.width, + hs = (float)m_cursorAtlas.cursorHeight / m_cursorAtlas.height; + m_cursor.textureRect = QRectF(ws * (m_cursor.shape % m_cursorAtlas.cursorsPerRow), + hs * (m_cursor.shape / m_cursorAtlas.cursorsPerRow), + ws, hs); + m_cursor.hotSpot = m_cursorAtlas.hotSpots[m_cursor.shape]; + m_cursor.texture = m_cursorAtlas.texture; + m_cursor.size = QSize(m_cursorAtlas.cursorWidth, m_cursorAtlas.cursorHeight); + } else { + QImage image = cursor->pixmap().toImage(); + m_cursor.textureRect = QRectF(0, 0, 1, 1); + m_cursor.hotSpot = cursor->hotSpot(); + m_cursor.texture = 0; // will get updated in the next render() + m_cursor.size = image.size(); + m_cursor.customCursorImage = image; + m_cursor.customCursorPending = true; + } + + return true; +} +#endif + +class CursorUpdateEvent : public QEvent +{ +public: + CursorUpdateEvent(const QPoint &pos, const QRegion &rgn) + : QEvent(QEvent::Type(QEvent::User + 1)), + m_pos(pos), + m_region(rgn) + { } + QPoint pos() const { return m_pos; } + QRegion region() const { return m_region; } + +private: + QPoint m_pos; + QRegion m_region; +}; + +bool QEglFSCursor::event(QEvent *e) +{ + if (e->type() == QEvent::User + 1) { + CursorUpdateEvent *ev = static_cast<CursorUpdateEvent *>(e); + m_updateRequested = false; + QWindowSystemInterface::handleExposeEvent(m_screen->topLevelAt(ev->pos()), ev->region()); + QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents); + return true; + } + return QPlatformCursor::event(e); +} + +void QEglFSCursor::update(const QRegion &rgn) +{ + if (!m_updateRequested) { + // Must not flush the window system events directly from here since we are likely to + // be a called directly from QGuiApplication's processMouseEvents. Flushing events + // could cause reentering by dispatching more queued mouse events. + m_updateRequested = true; + QCoreApplication::postEvent(this, new CursorUpdateEvent(m_cursor.pos, rgn)); + } +} + +QRect QEglFSCursor::cursorRect() const +{ + return QRect(m_cursor.pos - m_cursor.hotSpot, m_cursor.size); +} + +QPoint QEglFSCursor::pos() const +{ + return m_cursor.pos; +} + +void QEglFSCursor::setPos(const QPoint &pos) +{ + QGuiApplicationPrivate::inputDeviceManager()->setCursorPos(pos); + const QRect oldCursorRect = cursorRect(); + m_cursor.pos = pos; + update(oldCursorRect | cursorRect()); + m_screen->handleCursorMove(m_cursor.pos); +} + +void QEglFSCursor::pointerEvent(const QMouseEvent &event) +{ + if (event.type() != QEvent::MouseMove) + return; + const QRect oldCursorRect = cursorRect(); + m_cursor.pos = event.screenPos().toPoint(); + update(oldCursorRect | cursorRect()); + m_screen->handleCursorMove(m_cursor.pos); +} + +void QEglFSCursor::paintOnScreen() +{ + if (!m_visible) + return; + + const QRectF cr = cursorRect(); + const QRect screenRect(m_screen->geometry()); + const GLfloat x1 = 2 * (cr.left() / screenRect.width()) - 1; + const GLfloat x2 = 2 * (cr.right() / screenRect.width()) - 1; + const GLfloat y1 = 1 - (cr.top() / screenRect.height()) * 2; + const GLfloat y2 = 1 - (cr.bottom() / screenRect.height()) * 2; + QRectF r(QPointF(x1, y1), QPointF(x2, y2)); + + draw(r); +} + +// In order to prevent breaking code doing custom OpenGL rendering while +// expecting the state in the context unchanged, save and restore all the state +// we touch. The exception is Qt Quick where the scenegraph is known to be able +// to deal with the changes we make. +struct StateSaver +{ + StateSaver() { + f = QOpenGLContext::currentContext()->functions(); + vaoHelper = new QOpenGLVertexArrayObjectHelper(QOpenGLContext::currentContext()); + + static bool windowsChecked = false; + static bool shouldSave = true; + if (!windowsChecked) { + windowsChecked = true; + QWindowList windows = QGuiApplication::allWindows(); + if (!windows.isEmpty() && windows[0]->inherits("QQuickWindow")) + shouldSave = false; + } + saved = shouldSave; + if (!shouldSave) + return; + + f->glGetIntegerv(GL_CURRENT_PROGRAM, &program); + f->glGetIntegerv(GL_TEXTURE_BINDING_2D, &texture); + f->glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTexture); + f->glGetIntegerv(GL_FRONT_FACE, &frontFace); + cull = f->glIsEnabled(GL_CULL_FACE); + depthTest = f->glIsEnabled(GL_DEPTH_TEST); + blend = f->glIsEnabled(GL_BLEND); + f->glGetIntegerv(GL_BLEND_SRC_RGB, blendFunc); + f->glGetIntegerv(GL_BLEND_SRC_ALPHA, blendFunc + 1); + f->glGetIntegerv(GL_BLEND_DST_RGB, blendFunc + 2); + f->glGetIntegerv(GL_BLEND_DST_ALPHA, blendFunc + 3); + f->glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &arrayBuf); + if (vaoHelper->isValid()) + f->glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &vao); + for (int i = 0; i < 2; ++i) { + f->glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &va[i].enabled); + f->glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_SIZE, &va[i].size); + f->glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_TYPE, &va[i].type); + f->glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &va[i].normalized); + f->glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_STRIDE, &va[i].stride); + f->glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &va[i].buffer); + f->glGetVertexAttribPointerv(i, GL_VERTEX_ATTRIB_ARRAY_POINTER, &va[i].pointer); + } + } + ~StateSaver() { + if (saved) { + f->glUseProgram(program); + f->glBindTexture(GL_TEXTURE_2D, texture); + f->glActiveTexture(activeTexture); + f->glFrontFace(frontFace); + if (cull) + f->glEnable(GL_CULL_FACE); + else + f->glDisable(GL_CULL_FACE); + if (depthTest) + f->glEnable(GL_DEPTH_TEST); + else + f->glDisable(GL_DEPTH_TEST); + if (blend) + f->glEnable(GL_BLEND); + else + f->glDisable(GL_BLEND); + f->glBlendFuncSeparate(blendFunc[0], blendFunc[1], blendFunc[2], blendFunc[3]); + f->glBindBuffer(GL_ARRAY_BUFFER, arrayBuf); + if (vaoHelper->isValid()) + vaoHelper->glBindVertexArray(vao); + for (int i = 0; i < 2; ++i) { + if (va[i].enabled) + f->glEnableVertexAttribArray(i); + else + f->glDisableVertexAttribArray(i); + f->glBindBuffer(GL_ARRAY_BUFFER, va[i].buffer); + f->glVertexAttribPointer(i, va[i].size, va[i].type, va[i].normalized, va[i].stride, va[i].pointer); + } + } + delete vaoHelper; + } + QOpenGLFunctions *f; + QOpenGLVertexArrayObjectHelper *vaoHelper; + bool saved; + GLint program; + GLint texture; + GLint activeTexture; + GLint frontFace; + bool cull; + bool depthTest; + bool blend; + GLint blendFunc[4]; + GLint vao; + GLint arrayBuf; + struct { GLint enabled, type, size, normalized, stride, buffer; GLvoid *pointer; } va[2]; +}; + +void QEglFSCursor::draw(const QRectF &r) +{ + StateSaver stateSaver; + + if (!m_program) { + // one time initialization + initializeOpenGLFunctions(); + + createShaderPrograms(); + + if (!m_cursorAtlas.texture) { + createCursorTexture(&m_cursorAtlas.texture, m_cursorAtlas.image); + + if (m_cursor.shape != Qt::BitmapCursor) + m_cursor.texture = m_cursorAtlas.texture; + } + } + + if (m_cursor.shape == Qt::BitmapCursor && m_cursor.customCursorPending) { + // upload the custom cursor + createCursorTexture(&m_cursor.customCursorTexture, m_cursor.customCursorImage); + m_cursor.texture = m_cursor.customCursorTexture; + m_cursor.customCursorPending = false; + } + + Q_ASSERT(m_cursor.texture); + + m_program->bind(); + + const GLfloat x1 = r.left(); + const GLfloat x2 = r.right(); + const GLfloat y1 = r.top(); + const GLfloat y2 = r.bottom(); + const GLfloat cursorCoordinates[] = { + x1, y2, + x2, y2, + x1, y1, + x2, y1 + }; + + const GLfloat s1 = m_cursor.textureRect.left(); + const GLfloat s2 = m_cursor.textureRect.right(); + const GLfloat t1 = m_cursor.textureRect.top(); + const GLfloat t2 = m_cursor.textureRect.bottom(); + const GLfloat textureCoordinates[] = { + s1, t2, + s2, t2, + s1, t1, + s2, t1 + }; + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, m_cursor.texture); + + if (stateSaver.vaoHelper->isValid()) + stateSaver.vaoHelper->glBindVertexArray(0); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + + m_program->enableAttributeArray(0); + m_program->enableAttributeArray(1); + m_program->setAttributeArray(0, cursorCoordinates, 2); + m_program->setAttributeArray(1, textureCoordinates, 2); + + m_program->setUniformValue(m_textureEntry, 0); + + glDisable(GL_CULL_FACE); + glFrontFace(GL_CCW); + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_DEPTH_TEST); // disable depth testing to make sure cursor is always on top + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + m_program->disableAttributeArray(0); + m_program->disableAttributeArray(1); + m_program->release(); +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/qeglfscursor.h b/src/plugins/platforms/eglfs/qeglfscursor.h new file mode 100644 index 0000000000..be5db41b37 --- /dev/null +++ b/src/plugins/platforms/eglfs/qeglfscursor.h @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QEGLFSCURSOR_H +#define QEGLFSCURSOR_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qeglfsglobal.h" +#include <qpa/qplatformcursor.h> +#include <qpa/qplatformscreen.h> +#include <QtGui/QOpenGLFunctions> +#include <QtGui/private/qinputdevicemanager_p.h> + +QT_BEGIN_NAMESPACE + +class QOpenGLShaderProgram; +class QEglFSCursor; +class QEglFSScreen; + +class QEglFSCursorDeviceListener : public QObject +{ + Q_OBJECT + +public: + QEglFSCursorDeviceListener(QEglFSCursor *cursor) : m_cursor(cursor) { } + bool hasMouse() const; + +public slots: + void onDeviceListChanged(QInputDeviceManager::DeviceType type); + +private: + QEglFSCursor *m_cursor; +}; + +class Q_EGLFS_EXPORT QEglFSCursor : public QPlatformCursor, protected QOpenGLFunctions +{ + Q_OBJECT +public: + QEglFSCursor(QPlatformScreen *screen); + ~QEglFSCursor(); + +#ifndef QT_NO_CURSOR + void changeCursor(QCursor *cursor, QWindow *widget) Q_DECL_OVERRIDE; +#endif + void pointerEvent(const QMouseEvent &event) Q_DECL_OVERRIDE; + QPoint pos() const Q_DECL_OVERRIDE; + void setPos(const QPoint &pos) Q_DECL_OVERRIDE; + + QRect cursorRect() const; + void paintOnScreen(); + void resetResources(); + + void updateMouseStatus(); + +private: + bool event(QEvent *e) Q_DECL_OVERRIDE; +#ifndef QT_NO_CURSOR + bool setCurrentCursor(QCursor *cursor); +#endif + void draw(const QRectF &rect); + void update(const QRegion ®ion); + void createShaderPrograms(); + void createCursorTexture(uint *texture, const QImage &image); + void initCursorAtlas(); + + // current cursor information + struct Cursor { + Cursor() : texture(0), shape(Qt::BlankCursor), customCursorTexture(0), customCursorPending(false) { } + uint texture; // a texture from 'image' or the atlas + Qt::CursorShape shape; + QRectF textureRect; // normalized rect inside texture + QSize size; // size of the cursor + QPoint hotSpot; + QImage customCursorImage; + QPoint pos; // current cursor position + uint customCursorTexture; + bool customCursorPending; + } m_cursor; + + // cursor atlas information + struct CursorAtlas { + CursorAtlas() : cursorsPerRow(0), texture(0), cursorWidth(0), cursorHeight(0) { } + int cursorsPerRow; + uint texture; + int width, height; // width and height of the atlas + int cursorWidth, cursorHeight; // width and height of cursors inside the atlas + QList<QPoint> hotSpots; + QImage image; // valid until it's uploaded + } m_cursorAtlas; + + bool m_visible; + QEglFSScreen *m_screen; + QOpenGLShaderProgram *m_program; + int m_textureEntry; + QEglFSCursorDeviceListener *m_deviceListener; + bool m_updateRequested; +}; + +QT_END_NAMESPACE + +#endif // QEGLFSCURSOR_H diff --git a/src/plugins/platforms/eglfs/qeglfsdeviceintegration.cpp b/src/plugins/platforms/eglfs/qeglfsdeviceintegration.cpp index 359b90f205..064b9f6306 100644 --- a/src/plugins/platforms/eglfs/qeglfsdeviceintegration.cpp +++ b/src/plugins/platforms/eglfs/qeglfsdeviceintegration.cpp @@ -33,8 +33,9 @@ #include "qeglfsdeviceintegration.h" #include "qeglfsintegration.h" +#include "qeglfscursor.h" +#include "qeglfswindow.h" #include <QtPlatformSupport/private/qeglconvenience_p.h> -#include <QtPlatformSupport/private/qeglplatformcursor_p.h> #include <QGuiApplication> #include <private/qguiapplication_p.h> #include <QScreen> @@ -99,6 +100,7 @@ QStringList QEGLDeviceIntegrationFactory::keys(const QString &pluginPath) qCDebug(qLcEglDevDebug) << "EGL device integration plugin keys:" << list; return list; #else + Q_UNUSED(pluginPath); return QStringList(); #endif } @@ -117,6 +119,9 @@ QEGLDeviceIntegration *QEGLDeviceIntegrationFactory::create(const QString &key, qCDebug(qLcEglDevDebug) << "Using EGL device integration" << key; else qCWarning(qLcEglDevDebug) << "Failed to load EGL device integration" << key; +#else + Q_UNUSED(key); + Q_UNUSED(pluginPath); #endif return integration; } @@ -171,6 +176,11 @@ EGLNativeDisplayType QEGLDeviceIntegration::platformDisplay() const return EGL_DEFAULT_DISPLAY; } +EGLDisplay QEGLDeviceIntegration::createDisplay(EGLNativeDisplayType nativeDisplay) +{ + return eglGetDisplay(nativeDisplay); +} + bool QEGLDeviceIntegration::usesDefaultScreen() { return true; @@ -209,6 +219,11 @@ QDpi QEGLDeviceIntegration::logicalDpi() const 25.4 * s.height() / ps.height()); } +qreal QEGLDeviceIntegration::pixelDensity() const +{ + return logicalDpi().first / qreal(100); +} + Qt::ScreenOrientation QEGLDeviceIntegration::nativeOrientation() const { return Qt::PrimaryOrientation; @@ -234,6 +249,11 @@ qreal QEGLDeviceIntegration::refreshRate() const return q_refreshRateFromFb(framebuffer); } +EGLint QEGLDeviceIntegration::surfaceType() const +{ + return EGL_WINDOW_BIT; +} + QSurfaceFormat QEGLDeviceIntegration::surfaceFormatFor(const QSurfaceFormat &inputFormat) const { QSurfaceFormat format = inputFormat; @@ -253,6 +273,11 @@ bool QEGLDeviceIntegration::filterConfig(EGLDisplay, EGLConfig) const return true; } +QEglFSWindow *QEGLDeviceIntegration::createWindow(QWindow *window) const +{ + return new QEglFSWindow(window); +} + EGLNativeWindowType QEGLDeviceIntegration::createNativeWindow(QPlatformWindow *platformWindow, const QSize &size, const QSurfaceFormat &format) @@ -282,7 +307,7 @@ bool QEGLDeviceIntegration::hasCapability(QPlatformIntegration::Capability cap) QPlatformCursor *QEGLDeviceIntegration::createCursor(QPlatformScreen *screen) const { - return new QEGLPlatformCursor(screen); + return new QEglFSCursor(screen); } void QEGLDeviceIntegration::waitForVSync(QPlatformSurface *surface) const @@ -309,4 +334,14 @@ bool QEGLDeviceIntegration::supportsPBuffers() const return true; } +bool QEGLDeviceIntegration::supportsSurfacelessContexts() const +{ + return true; +} + +void *QEGLDeviceIntegration::wlDisplay() const +{ + return Q_NULLPTR; +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/qeglfsdeviceintegration.h b/src/plugins/platforms/eglfs/qeglfsdeviceintegration.h index 260fc313f7..5ec98b37d1 100644 --- a/src/plugins/platforms/eglfs/qeglfsdeviceintegration.h +++ b/src/plugins/platforms/eglfs/qeglfsdeviceintegration.h @@ -56,6 +56,7 @@ QT_BEGIN_NAMESPACE class QPlatformSurface; +class QEglFSWindow; #define QEGLDeviceIntegrationFactoryInterface_iid "org.qt-project.qt.qpa.egl.QEGLDeviceIntegrationFactoryInterface.5.5" @@ -67,18 +68,22 @@ public: virtual void platformInit(); virtual void platformDestroy(); virtual EGLNativeDisplayType platformDisplay() const; + virtual EGLDisplay createDisplay(EGLNativeDisplayType nativeDisplay); virtual bool usesDefaultScreen(); virtual void screenInit(); virtual void screenDestroy(); virtual QSizeF physicalScreenSize() const; virtual QSize screenSize() const; virtual QDpi logicalDpi() const; + virtual qreal pixelDensity() const; virtual Qt::ScreenOrientation nativeOrientation() const; virtual Qt::ScreenOrientation orientation() const; virtual int screenDepth() const; virtual QImage::Format screenFormat() const; virtual qreal refreshRate() const; virtual QSurfaceFormat surfaceFormatFor(const QSurfaceFormat &inputFormat) const; + virtual EGLint surfaceType() const; + virtual QEglFSWindow *createWindow(QWindow *window) const; virtual EGLNativeWindowType createNativeWindow(QPlatformWindow *platformWindow, const QSize &size, const QSurfaceFormat &format); @@ -92,6 +97,9 @@ public: virtual QByteArray fbDeviceName() const; virtual int framebufferIndex() const; virtual bool supportsPBuffers() const; + virtual bool supportsSurfacelessContexts() const; + + virtual void *wlDisplay() const; }; class Q_EGLFS_EXPORT QEGLDeviceIntegrationPlugin : public QObject diff --git a/src/plugins/platforms/eglfs/qeglfsintegration.cpp b/src/plugins/platforms/eglfs/qeglfsintegration.cpp index 5eb8485dc7..2086ce56e2 100644 --- a/src/plugins/platforms/eglfs/qeglfsintegration.cpp +++ b/src/plugins/platforms/eglfs/qeglfsintegration.cpp @@ -39,19 +39,46 @@ #include <QtGui/QOpenGLContext> #include <QtGui/QScreen> #include <QtGui/QOffscreenSurface> -#include <qpa/qplatformcursor.h> +#include <QtGui/QWindow> +#include <QtCore/QLoggingCategory> +#include <qpa/qwindowsysteminterface.h> +#include <qpa/qplatforminputcontextfactory_p.h> #include "qeglfsintegration.h" #include "qeglfswindow.h" #include "qeglfshooks.h" #include "qeglfscontext.h" #include "qeglfsoffscreenwindow.h" +#include "qeglfscursor.h" #include <QtPlatformSupport/private/qeglconvenience_p.h> #include <QtPlatformSupport/private/qeglplatformcontext_p.h> #include <QtPlatformSupport/private/qeglpbuffer_p.h> + +#include <QtPlatformSupport/private/qgenericunixfontdatabase_p.h> +#include <QtPlatformSupport/private/qgenericunixservices_p.h> +#include <QtPlatformSupport/private/qgenericunixeventdispatcher_p.h> +#include <QtPlatformSupport/private/qfbvthandler_p.h> +#include <QtPlatformSupport/private/qopenglcompositorbackingstore_p.h> + #include <QtPlatformHeaders/QEGLNativeContext> +#ifndef QT_NO_LIBINPUT +#include <QtPlatformSupport/private/qlibinputhandler_p.h> +#endif + +#if !defined(QT_NO_EVDEV) && (!defined(Q_OS_ANDROID) || defined(Q_OS_ANDROID_NO_SDK)) +#include <QtPlatformSupport/private/qevdevmousemanager_p.h> +#include <QtPlatformSupport/private/qevdevkeyboardmanager_p.h> +#include <QtPlatformSupport/private/qevdevtouchmanager_p.h> +#endif + +#if !defined(QT_NO_TSLIB) && (!defined(Q_OS_ANDROID) || defined(Q_OS_ANDROID_NO_SDK)) +#include <QtPlatformSupport/private/qtslib_p.h> +#endif + +#include <QtPlatformHeaders/qeglfsfunctions.h> + #include <EGL/egl.h> static void initResources() @@ -64,21 +91,18 @@ static void initResources() QT_BEGIN_NAMESPACE QEglFSIntegration::QEglFSIntegration() + : m_display(EGL_NO_DISPLAY), + m_inputContext(0), + m_fontDb(new QGenericUnixFontDatabase), + m_services(new QGenericUnixServices), + m_kbdMgr(0), + m_disableInputHandlers(false) { - mDisableInputHandlers = qEnvironmentVariableIntValue("QT_QPA_EGLFS_DISABLE_INPUT"); + m_disableInputHandlers = qEnvironmentVariableIntValue("QT_QPA_EGLFS_DISABLE_INPUT"); initResources(); } -bool QEglFSIntegration::hasCapability(QPlatformIntegration::Capability cap) const -{ - // We assume that devices will have more and not less capabilities - if (qt_egl_device_integration()->hasCapability(cap)) - return true; - - return QEGLPlatformIntegration::hasCapability(cap); -} - void QEglFSIntegration::addScreen(QPlatformScreen *screen) { screenAdded(screen); @@ -93,65 +117,316 @@ void QEglFSIntegration::initialize() { qt_egl_device_integration()->platformInit(); - QEGLPlatformIntegration::initialize(); + m_display = qt_egl_device_integration()->createDisplay(nativeDisplay()); + if (m_display == EGL_NO_DISPLAY) + qFatal("Could not open egl display"); - if (!mDisableInputHandlers) - createInputHandlers(); + EGLint major, minor; + if (!eglInitialize(m_display, &major, &minor)) + qFatal("Could not initialize egl display"); + + m_inputContext = QPlatformInputContextFactory::create(); + + m_vtHandler.reset(new QFbVtHandler); if (qt_egl_device_integration()->usesDefaultScreen()) addScreen(new QEglFSScreen(display())); else qt_egl_device_integration()->screenInit(); + + // Input code may rely on the screens, so do it only after the screen init. + if (!m_disableInputHandlers) + createInputHandlers(); } void QEglFSIntegration::destroy() { foreach (QWindow *w, qGuiApp->topLevelWindows()) w->destroy(); + qt_egl_device_integration()->screenDestroy(); - if (display() != EGL_NO_DISPLAY) - eglTerminate(display()); + + if (m_display != EGL_NO_DISPLAY) + eglTerminate(m_display); + qt_egl_device_integration()->platformDestroy(); } -EGLNativeDisplayType QEglFSIntegration::nativeDisplay() const +QAbstractEventDispatcher *QEglFSIntegration::createEventDispatcher() const { - return qt_egl_device_integration()->platformDisplay(); + return createUnixEventDispatcher(); } -QEGLPlatformWindow *QEglFSIntegration::createWindow(QWindow *window) const +QPlatformServices *QEglFSIntegration::services() const { - return new QEglFSWindow(window); + return m_services.data(); } -QEGLPlatformContext *QEglFSIntegration::createContext(const QSurfaceFormat &format, - QPlatformOpenGLContext *shareContext, - EGLDisplay display, - QVariant *nativeHandle) const +QPlatformFontDatabase *QEglFSIntegration::fontDatabase() const { + return m_fontDb.data(); +} + +QPlatformBackingStore *QEglFSIntegration::createPlatformBackingStore(QWindow *window) const +{ + QOpenGLCompositorBackingStore *bs = new QOpenGLCompositorBackingStore(window); + if (!window->handle()) + window->create(); + static_cast<QEglFSWindow *>(window->handle())->setBackingStore(bs); + return bs; +} + +QPlatformWindow *QEglFSIntegration::createPlatformWindow(QWindow *window) const +{ + QWindowSystemInterface::flushWindowSystemEvents(); + QEglFSWindow *w = qt_egl_device_integration()->createWindow(window); + w->create(); + if (window->type() != Qt::ToolTip) + w->requestActivateWindow(); + return w; +} + +QPlatformOpenGLContext *QEglFSIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const +{ + // If there is a "root" window into which raster and QOpenGLWidget content is + // composited, all other contexts must share with its context. + QOpenGLContext *compositingContext = QOpenGLCompositor::instance()->context(); + EGLDisplay dpy = context->screen() ? static_cast<QEglFSScreen *>(context->screen()->handle())->display() : display(); + QPlatformOpenGLContext *share = compositingContext ? compositingContext->handle() : context->shareHandle(); + QVariant nativeHandle = context->nativeHandle(); + QEglFSContext *ctx; - QSurfaceFormat adjustedFormat = qt_egl_device_integration()->surfaceFormatFor(format); - if (!nativeHandle || nativeHandle->isNull()) { - EGLConfig config = QEglFSIntegration::chooseConfig(display, adjustedFormat); - ctx = new QEglFSContext(adjustedFormat, shareContext, display, &config, QVariant()); + QSurfaceFormat adjustedFormat = qt_egl_device_integration()->surfaceFormatFor(context->format()); + if (nativeHandle.isNull()) { + EGLConfig config = QEglFSIntegration::chooseConfig(dpy, adjustedFormat); + ctx = new QEglFSContext(adjustedFormat, share, dpy, &config, QVariant()); } else { - ctx = new QEglFSContext(adjustedFormat, shareContext, display, 0, *nativeHandle); + ctx = new QEglFSContext(adjustedFormat, share, dpy, 0, nativeHandle); } - *nativeHandle = QVariant::fromValue<QEGLNativeContext>(QEGLNativeContext(ctx->eglContext(), display)); + nativeHandle = QVariant::fromValue<QEGLNativeContext>(QEGLNativeContext(ctx->eglContext(), dpy)); + + context->setNativeHandle(nativeHandle); return ctx; } -QPlatformOffscreenSurface *QEglFSIntegration::createOffscreenSurface(EGLDisplay display, - const QSurfaceFormat &format, - QOffscreenSurface *surface) const +QPlatformOffscreenSurface *QEglFSIntegration::createPlatformOffscreenSurface(QOffscreenSurface *surface) const +{ + EGLDisplay dpy = surface->screen() ? static_cast<QEglFSScreen *>(surface->screen()->handle())->display() : display(); + QSurfaceFormat fmt = qt_egl_device_integration()->surfaceFormatFor(surface->requestedFormat()); + if (qt_egl_device_integration()->supportsPBuffers()) { + QEGLPlatformContext::Flags flags = 0; + if (!qt_egl_device_integration()->supportsSurfacelessContexts()) + flags |= QEGLPlatformContext::NoSurfaceless; + return new QEGLPbuffer(dpy, fmt, surface, flags); + } else { + return new QEglFSOffscreenWindow(dpy, fmt, surface); + } + // Never return null. Multiple QWindows are not supported by this plugin. +} + +bool QEglFSIntegration::hasCapability(QPlatformIntegration::Capability cap) const +{ + // We assume that devices will have more and not less capabilities + if (qt_egl_device_integration()->hasCapability(cap)) + return true; + + switch (cap) { + case ThreadedPixmaps: return true; + case OpenGL: return true; + case ThreadedOpenGL: return true; + case WindowManagement: return false; + case RasterGLSurface: return true; + default: return QPlatformIntegration::hasCapability(cap); + } +} + +QPlatformNativeInterface *QEglFSIntegration::nativeInterface() const +{ + return const_cast<QEglFSIntegration *>(this); +} + +enum ResourceType { + EglDisplay, + EglWindow, + EglContext, + EglConfig, + NativeDisplay, + XlibDisplay, + WaylandDisplay +}; + +static int resourceType(const QByteArray &key) { - QSurfaceFormat fmt = qt_egl_device_integration()->surfaceFormatFor(format); - if (qt_egl_device_integration()->supportsPBuffers()) - return new QEGLPbuffer(display, fmt, surface); + static const QByteArray names[] = { // match ResourceType + QByteArrayLiteral("egldisplay"), + QByteArrayLiteral("eglwindow"), + QByteArrayLiteral("eglcontext"), + QByteArrayLiteral("eglconfig"), + QByteArrayLiteral("nativedisplay"), + QByteArrayLiteral("display"), + QByteArrayLiteral("server_wl_display") + }; + const QByteArray *end = names + sizeof(names) / sizeof(names[0]); + const QByteArray *result = std::find(names, end, key); + if (result == end) + result = std::find(names, end, key.toLower()); + return int(result - names); +} + +void *QEglFSIntegration::nativeResourceForIntegration(const QByteArray &resource) +{ + void *result = 0; + + switch (resourceType(resource)) { + case EglDisplay: + result = display(); + break; + case NativeDisplay: + result = reinterpret_cast<void*>(nativeDisplay()); + break; + case WaylandDisplay: + result = qt_egl_device_integration()->wlDisplay(); + break; + default: + break; + } + + return result; +} + +void *QEglFSIntegration::nativeResourceForScreen(const QByteArray &resource, QScreen *) +{ + void *result = 0; + + switch (resourceType(resource)) { + case XlibDisplay: + // Play nice when using the x11 hooks: Be compatible with xcb that allows querying + // the X Display pointer, which is nothing but our native display. + result = reinterpret_cast<void*>(nativeDisplay()); + break; + default: + break; + } + + return result; +} + +void *QEglFSIntegration::nativeResourceForWindow(const QByteArray &resource, QWindow *window) +{ + void *result = 0; + + switch (resourceType(resource)) { + case EglDisplay: + if (window && window->handle()) + result = static_cast<QEglFSScreen *>(window->handle()->screen())->display(); + else + result = display(); + break; + case EglWindow: + if (window && window->handle()) + result = reinterpret_cast<void*>(static_cast<QEglFSWindow *>(window->handle())->eglWindow()); + break; + default: + break; + } + + return result; +} + +void *QEglFSIntegration::nativeResourceForContext(const QByteArray &resource, QOpenGLContext *context) +{ + void *result = 0; + + switch (resourceType(resource)) { + case EglContext: + if (context->handle()) + result = static_cast<QEglFSContext *>(context->handle())->eglContext(); + break; + case EglConfig: + if (context->handle()) + result = static_cast<QEglFSContext *>(context->handle())->eglConfig(); + break; + case EglDisplay: + if (context->handle()) + result = static_cast<QEglFSContext *>(context->handle())->eglDisplay(); + break; + default: + break; + } + + return result; +} + +static void *eglContextForContext(QOpenGLContext *context) +{ + Q_ASSERT(context); + + QEglFSContext *handle = static_cast<QEglFSContext *>(context->handle()); + if (!handle) + return 0; + + return handle->eglContext(); +} + +QPlatformNativeInterface::NativeResourceForContextFunction QEglFSIntegration::nativeResourceFunctionForContext(const QByteArray &resource) +{ + QByteArray lowerCaseResource = resource.toLower(); + if (lowerCaseResource == "get_egl_context") + return NativeResourceForContextFunction(eglContextForContext); + + return 0; +} + +QFunctionPointer QEglFSIntegration::platformFunction(const QByteArray &function) const +{ +#if !defined(QT_NO_EVDEV) && (!defined(Q_OS_ANDROID) || defined(Q_OS_ANDROID_NO_SDK)) + if (function == QEglFSFunctions::loadKeymapTypeIdentifier()) + return QFunctionPointer(loadKeymapStatic); +#else + Q_UNUSED(function) +#endif + + return 0; +} + +void QEglFSIntegration::loadKeymapStatic(const QString &filename) +{ +#if !defined(QT_NO_EVDEV) && (!defined(Q_OS_ANDROID) || defined(Q_OS_ANDROID_NO_SDK)) + QEglFSIntegration *self = static_cast<QEglFSIntegration *>(QGuiApplicationPrivate::platformIntegration()); + if (self->m_kbdMgr) + self->m_kbdMgr->loadKeymap(filename); else - return new QEglFSOffscreenWindow(display, fmt, surface); + qWarning("QEglFSIntegration: Cannot load keymap, no keyboard handler found"); +#else + Q_UNUSED(filename); +#endif +} - // Never return null. Multiple QWindows are not supported by this plugin. +void QEglFSIntegration::createInputHandlers() +{ +#ifndef QT_NO_LIBINPUT + if (!qEnvironmentVariableIntValue("QT_QPA_EGLFS_NO_LIBINPUT")) { + new QLibInputHandler(QLatin1String("libinput"), QString()); + return; + } +#endif + +#if !defined(QT_NO_EVDEV) && (!defined(Q_OS_ANDROID) || defined(Q_OS_ANDROID_NO_SDK)) + m_kbdMgr = new QEvdevKeyboardManager(QLatin1String("EvdevKeyboard"), QString() /* spec */, this); + new QEvdevMouseManager(QLatin1String("EvdevMouse"), QString() /* spec */, this); +#ifndef QT_NO_TSLIB + const bool useTslib = qEnvironmentVariableIntValue("QT_QPA_EGLFS_TSLIB"); + if (useTslib) + new QTsLibMouseHandler(QLatin1String("TsLib"), QString() /* spec */); + else +#endif // QT_NO_TSLIB + new QEvdevTouchManager(QLatin1String("EvdevTouch"), QString() /* spec */, this); +#endif +} + +EGLNativeDisplayType QEglFSIntegration::nativeDisplay() const +{ + return qt_egl_device_integration()->platformDisplay(); } EGLConfig QEglFSIntegration::chooseConfig(EGLDisplay display, const QSurfaceFormat &format) @@ -167,6 +442,7 @@ EGLConfig QEglFSIntegration::chooseConfig(EGLDisplay display, const QSurfaceForm }; Chooser chooser(display); + chooser.setSurfaceType(qt_egl_device_integration()->surfaceType()); chooser.setSurfaceFormat(format); return chooser.chooseConfig(); } diff --git a/src/plugins/platforms/eglfs/qeglfsintegration.h b/src/plugins/platforms/eglfs/qeglfsintegration.h index 11b643d540..98c7ee9f78 100644 --- a/src/plugins/platforms/eglfs/qeglfsintegration.h +++ b/src/plugins/platforms/eglfs/qeglfsintegration.h @@ -34,41 +34,72 @@ #ifndef QEGLFSINTEGRATION_H #define QEGLFSINTEGRATION_H -#include <QtPlatformSupport/private/qeglplatformintegration_p.h> +#include <QtCore/QVariant> +#include <qpa/qplatformintegration.h> +#include <qpa/qplatformnativeinterface.h> #include <qpa/qplatformscreen.h> #include <EGL/egl.h> #include "qeglfsglobal.h" QT_BEGIN_NAMESPACE -class Q_EGLFS_EXPORT QEglFSIntegration : public QEGLPlatformIntegration +class QEglFSWindow; +class QEglFSContext; +class QFbVtHandler; +class QEvdevKeyboardManager; + +class Q_EGLFS_EXPORT QEglFSIntegration : public QPlatformIntegration, public QPlatformNativeInterface { public: QEglFSIntegration(); - void addScreen(QPlatformScreen *screen); - void removeScreen(QPlatformScreen *screen); - void initialize() Q_DECL_OVERRIDE; void destroy() Q_DECL_OVERRIDE; + EGLDisplay display() const { return m_display; } + + QAbstractEventDispatcher *createEventDispatcher() const Q_DECL_OVERRIDE; + QPlatformFontDatabase *fontDatabase() const Q_DECL_OVERRIDE; + QPlatformServices *services() const Q_DECL_OVERRIDE; + QPlatformInputContext *inputContext() const Q_DECL_OVERRIDE { return m_inputContext; } + + QPlatformWindow *createPlatformWindow(QWindow *window) const Q_DECL_OVERRIDE; + QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const Q_DECL_OVERRIDE; + QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const Q_DECL_OVERRIDE; + QPlatformOffscreenSurface *createPlatformOffscreenSurface(QOffscreenSurface *surface) const Q_DECL_OVERRIDE; + bool hasCapability(QPlatformIntegration::Capability cap) const Q_DECL_OVERRIDE; - static EGLConfig chooseConfig(EGLDisplay display, const QSurfaceFormat &format); + QPlatformNativeInterface *nativeInterface() const Q_DECL_OVERRIDE; -protected: - QEGLPlatformWindow *createWindow(QWindow *window) const Q_DECL_OVERRIDE; - QEGLPlatformContext *createContext(const QSurfaceFormat &format, - QPlatformOpenGLContext *shareContext, - EGLDisplay display, - QVariant *nativeHandle) const Q_DECL_OVERRIDE; - QPlatformOffscreenSurface *createOffscreenSurface(EGLDisplay display, - const QSurfaceFormat &format, - QOffscreenSurface *surface) const Q_DECL_OVERRIDE; - EGLNativeDisplayType nativeDisplay() const Q_DECL_OVERRIDE; + // QPlatformNativeInterface + void *nativeResourceForIntegration(const QByteArray &resource) Q_DECL_OVERRIDE; + void *nativeResourceForScreen(const QByteArray &resource, QScreen *screen) Q_DECL_OVERRIDE; + void *nativeResourceForWindow(const QByteArray &resource, QWindow *window) Q_DECL_OVERRIDE; + void *nativeResourceForContext(const QByteArray &resource, QOpenGLContext *context) Q_DECL_OVERRIDE; + NativeResourceForContextFunction nativeResourceFunctionForContext(const QByteArray &resource) Q_DECL_OVERRIDE; + + QFunctionPointer platformFunction(const QByteArray &function) const Q_DECL_OVERRIDE; + + QFbVtHandler *vtHandler() { return m_vtHandler.data(); } + + void addScreen(QPlatformScreen *screen); + void removeScreen(QPlatformScreen *screen); + + static EGLConfig chooseConfig(EGLDisplay display, const QSurfaceFormat &format); private: - bool mDisableInputHandlers; + EGLNativeDisplayType nativeDisplay() const; + void createInputHandlers(); + static void loadKeymapStatic(const QString &filename); + + EGLDisplay m_display; + QPlatformInputContext *m_inputContext; + QScopedPointer<QPlatformFontDatabase> m_fontDb; + QScopedPointer<QPlatformServices> m_services; + QScopedPointer<QFbVtHandler> m_vtHandler; + QEvdevKeyboardManager *m_kbdMgr; + bool m_disableInputHandlers; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/qeglfsscreen.cpp b/src/plugins/platforms/eglfs/qeglfsscreen.cpp index 1b6e2307f8..a14e68b667 100644 --- a/src/plugins/platforms/eglfs/qeglfsscreen.cpp +++ b/src/plugins/platforms/eglfs/qeglfsscreen.cpp @@ -32,7 +32,10 @@ ****************************************************************************/ #include <QtCore/qtextstream.h> -#include <QtGui/qpa/qplatformcursor.h> +#include <QtGui/qwindow.h> +#include <qpa/qwindowsysteminterface.h> +#include <qpa/qplatformcursor.h> +#include <QtPlatformSupport/private/qopenglcompositor_p.h> #include "qeglfsscreen.h" #include "qeglfswindow.h" @@ -41,7 +44,7 @@ QT_BEGIN_NAMESPACE QEglFSScreen::QEglFSScreen(EGLDisplay dpy) - : QEGLPlatformScreen(dpy), + : m_dpy(dpy), m_surface(EGL_NO_SURFACE), m_cursor(0) { @@ -51,6 +54,7 @@ QEglFSScreen::QEglFSScreen(EGLDisplay dpy) QEglFSScreen::~QEglFSScreen() { delete m_cursor; + QOpenGLCompositor::destroy(); } QRect QEglFSScreen::geometry() const @@ -78,6 +82,11 @@ QDpi QEglFSScreen::logicalDpi() const return qt_egl_device_integration()->logicalDpi(); } +qreal QEglFSScreen::pixelDensity() const +{ + return qt_egl_device_integration()->pixelDensity(); +} + Qt::ScreenOrientation QEglFSScreen::nativeOrientation() const { return qt_egl_device_integration()->nativeOrientation(); @@ -103,4 +112,89 @@ void QEglFSScreen::setPrimarySurface(EGLSurface surface) m_surface = surface; } +void QEglFSScreen::handleCursorMove(const QPoint &pos) +{ + const QOpenGLCompositor *compositor = QOpenGLCompositor::instance(); + const QList<QOpenGLCompositorWindow *> windows = compositor->windows(); + + // Generate enter and leave events like a real windowing system would do. + if (windows.isEmpty()) + return; + + // First window is always fullscreen. + if (windows.count() == 1) { + QWindow *window = windows[0]->sourceWindow(); + if (m_pointerWindow != window) { + m_pointerWindow = window; + QWindowSystemInterface::handleEnterEvent(window, window->mapFromGlobal(pos), pos); + } + return; + } + + QWindow *enter = 0, *leave = 0; + for (int i = windows.count() - 1; i >= 0; --i) { + QWindow *window = windows[i]->sourceWindow(); + const QRect geom = window->geometry(); + if (geom.contains(pos)) { + if (m_pointerWindow != window) { + leave = m_pointerWindow; + m_pointerWindow = window; + enter = window; + } + break; + } + } + + if (enter && leave) + QWindowSystemInterface::handleEnterLeaveEvent(enter, leave, enter->mapFromGlobal(pos), pos); +} + +QPixmap QEglFSScreen::grabWindow(WId wid, int x, int y, int width, int height) const +{ + QOpenGLCompositor *compositor = QOpenGLCompositor::instance(); + const QList<QOpenGLCompositorWindow *> windows = compositor->windows(); + Q_ASSERT(!windows.isEmpty()); + + QImage img; + + if (static_cast<QEglFSWindow *>(windows.first()->sourceWindow()->handle())->isRaster()) { + // Request the compositor to render everything into an FBO and read it back. This + // is of course slow, but it's safe and reliable. It will not include the mouse + // cursor, which is a plus. + img = compositor->grab(); + } else { + // Just a single OpenGL window without compositing. Do not support this case for now. Doing + // glReadPixels is not an option since it would read from the back buffer which may have + // undefined content when calling right after a swapBuffers (unless preserved swap is + // available and enabled, but we have no support for that). + qWarning("grabWindow: Not supported for non-composited OpenGL content. Use QQuickWindow::grabWindow() instead."); + return QPixmap(); + } + + if (!wid) { + const QSize screenSize = geometry().size(); + if (width < 0) + width = screenSize.width() - x; + if (height < 0) + height = screenSize.height() - y; + return QPixmap::fromImage(img).copy(x, y, width, height); + } + + foreach (QOpenGLCompositorWindow *w, windows) { + const QWindow *window = w->sourceWindow(); + if (window->winId() == wid) { + const QRect geom = window->geometry(); + if (width < 0) + width = geom.width() - x; + if (height < 0) + height = geom.height() - y; + QRect rect(geom.topLeft() + QPoint(x, y), QSize(width, height)); + rect &= window->geometry(); + return QPixmap::fromImage(img).copy(rect); + } + } + + return QPixmap(); +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/qeglfsscreen.h b/src/plugins/platforms/eglfs/qeglfsscreen.h index 07b6ff63ef..8f1d87ea25 100644 --- a/src/plugins/platforms/eglfs/qeglfsscreen.h +++ b/src/plugins/platforms/eglfs/qeglfsscreen.h @@ -35,7 +35,7 @@ #define QEGLFSSCREEN_H #include "qeglfsglobal.h" -#include <QtPlatformSupport/private/qeglplatformscreen_p.h> +#include <QtCore/QPointer> #include <EGL/egl.h> QT_BEGIN_NAMESPACE @@ -43,7 +43,7 @@ QT_BEGIN_NAMESPACE class QEglFSWindow; class QOpenGLContext; -class Q_EGLFS_EXPORT QEglFSScreen : public QEGLPlatformScreen +class Q_EGLFS_EXPORT QEglFSScreen : public QPlatformScreen { public: QEglFSScreen(EGLDisplay display); @@ -55,6 +55,7 @@ public: QSizeF physicalSize() const Q_DECL_OVERRIDE; QDpi logicalDpi() const Q_DECL_OVERRIDE; + qreal pixelDensity() const Q_DECL_OVERRIDE; Qt::ScreenOrientation nativeOrientation() const Q_DECL_OVERRIDE; Qt::ScreenOrientation orientation() const Q_DECL_OVERRIDE; @@ -62,16 +63,23 @@ public: qreal refreshRate() const Q_DECL_OVERRIDE; + QPixmap grabWindow(WId wid, int x, int y, int width, int height) const Q_DECL_OVERRIDE; + EGLSurface primarySurface() const { return m_surface; } -protected: - void setPrimarySurface(EGLSurface surface); + EGLDisplay display() const { return m_dpy; } + + void handleCursorMove(const QPoint &pos); private: - friend class QEglFSWindow; + void setPrimarySurface(EGLSurface surface); + EGLDisplay m_dpy; + QPointer<QWindow> m_pointerWindow; EGLSurface m_surface; QPlatformCursor *m_cursor; + + friend class QEglFSWindow; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/qeglfswindow.cpp b/src/plugins/platforms/eglfs/qeglfswindow.cpp index c0d51c94a5..8301be8c17 100644 --- a/src/plugins/platforms/eglfs/qeglfswindow.cpp +++ b/src/plugins/platforms/eglfs/qeglfswindow.cpp @@ -37,21 +37,23 @@ #include <private/qguiapplication_p.h> #include <QtGui/private/qopenglcontext_p.h> #include <QtGui/QOpenGLContext> -#include <QtPlatformSupport/private/qeglplatformcursor_p.h> #include <QtPlatformSupport/private/qeglconvenience_p.h> +#include <QtPlatformSupport/private/qopenglcompositorbackingstore_p.h> #include "qeglfswindow.h" +#include "qeglfscursor.h" #include "qeglfshooks.h" -#include <QtDebug> - QT_BEGIN_NAMESPACE QEglFSWindow::QEglFSWindow(QWindow *w) - : QEGLPlatformWindow(w) - , m_surface(0) - , m_window(0) - , m_flags(0) + : QPlatformWindow(w), + m_backingStore(0), + m_raster(false), + m_winId(0), + m_surface(EGL_NO_SURFACE), + m_window(0), + m_flags(0) { } @@ -60,12 +62,34 @@ QEglFSWindow::~QEglFSWindow() destroy(); } +static WId newWId() +{ + static WId id = 0; + + if (id == std::numeric_limits<WId>::max()) + qWarning("QEGLPlatformWindow: Out of window IDs"); + + return ++id; +} + void QEglFSWindow::create() { if (m_flags.testFlag(Created)) return; - QEGLPlatformWindow::create(); + m_winId = newWId(); + + // Save the original surface type before changing to OpenGLSurface. + m_raster = (window()->surfaceType() == QSurface::RasterSurface); + if (m_raster) // change to OpenGL, but not for RasterGLSurface + window()->setSurfaceType(QSurface::OpenGLSurface); + + if (window()->type() == Qt::Desktop) { + QRect fullscreenRect(QPoint(), screen()->availableGeometry().size()); + QPlatformWindow::setGeometry(fullscreenRect); + QWindowSystemInterface::handleGeometryChange(window(), fullscreenRect); + return; + } m_flags = Created; @@ -96,13 +120,14 @@ void QEglFSWindow::create() setGeometry(QRect()); // will become fullscreen QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), geometry().size())); - EGLDisplay display = static_cast<QEglFSScreen *>(screen)->display(); - QSurfaceFormat platformFormat = qt_egl_device_integration()->surfaceFormatFor(window()->requestedFormat()); - m_config = QEglFSIntegration::chooseConfig(display, platformFormat); - m_format = q_glFormatFromConfig(display, m_config, platformFormat); - resetSurface(); + if (m_surface == EGL_NO_SURFACE) { + EGLint error = eglGetError(); + eglTerminate(screen->display()); + qFatal("EGL Error : Could not create the egl surface: error = 0x%x\n", error); + } + screen->setPrimarySurface(m_surface); if (isRaster()) { @@ -120,7 +145,7 @@ void QEglFSWindow::destroy() { QEglFSScreen *screen = this->screen(); if (m_flags.testFlag(HasNativeWindow)) { - QEGLPlatformCursor *cursor = qobject_cast<QEGLPlatformCursor *>(screen->cursor()); + QEglFSCursor *cursor = qobject_cast<QEglFSCursor *>(screen->cursor()); if (cursor) cursor->resetResources(); @@ -134,15 +159,10 @@ void QEglFSWindow::destroy() QOpenGLCompositor::instance()->removeWindow(this); } -// The virtual functions resetSurface and invalidateSurface may get overridden -// in derived classes, for example in the Android port, to perform the native -// window and surface creation differently. - void QEglFSWindow::invalidateSurface() { if (m_surface != EGL_NO_SURFACE) { - EGLDisplay display = static_cast<QEglFSScreen *>(screen())->display(); - eglDestroySurface(display, m_surface); + eglDestroySurface(screen()->display(), m_surface); m_surface = EGL_NO_SURFACE; } qt_egl_device_integration()->destroyNativeWindow(m_window); @@ -151,15 +171,13 @@ void QEglFSWindow::invalidateSurface() void QEglFSWindow::resetSurface() { - QEglFSScreen *nativeScreen = static_cast<QEglFSScreen *>(screen()); - EGLDisplay display = nativeScreen->display(); - m_window = qt_egl_device_integration()->createNativeWindow(this, nativeScreen->geometry().size(), m_format); + EGLDisplay display = screen()->display(); + QSurfaceFormat platformFormat = qt_egl_device_integration()->surfaceFormatFor(window()->requestedFormat()); + + m_config = QEglFSIntegration::chooseConfig(display, platformFormat); + m_format = q_glFormatFromConfig(display, m_config, platformFormat); + m_window = qt_egl_device_integration()->createNativeWindow(this, screen()->geometry().size(), m_format); m_surface = eglCreateWindowSurface(display, m_config, m_window, NULL); - if (m_surface == EGL_NO_SURFACE) { - EGLint error = eglGetError(); - eglTerminate(display); - qFatal("EGL Error : Could not create the egl surface: error = 0x%x\n", error); - } } void QEglFSWindow::setVisible(bool visible) @@ -265,4 +283,41 @@ QEglFSScreen *QEglFSWindow::screen() const return static_cast<QEglFSScreen *>(QPlatformWindow::screen()); } +bool QEglFSWindow::isRaster() const +{ + return m_raster || window()->surfaceType() == QSurface::RasterGLSurface; +} + +QWindow *QEglFSWindow::sourceWindow() const +{ + return window(); +} + +const QPlatformTextureList *QEglFSWindow::textures() const +{ + if (m_backingStore) + return m_backingStore->textures(); + + return 0; +} + +void QEglFSWindow::endCompositing() +{ + if (m_backingStore) + m_backingStore->notifyComposited(); +} + +WId QEglFSWindow::winId() const +{ + return m_winId; +} + +void QEglFSWindow::setOpacity(qreal) +{ + if (!isRaster()) + qWarning("QEglFSWindow: Cannot set opacity for non-raster windows"); + + // Nothing to do here. The opacity is stored in the QWindow. +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/qeglfswindow.h b/src/plugins/platforms/eglfs/qeglfswindow.h index f9d207c153..806b21de0a 100644 --- a/src/plugins/platforms/eglfs/qeglfswindow.h +++ b/src/plugins/platforms/eglfs/qeglfswindow.h @@ -37,17 +37,23 @@ #include "qeglfsintegration.h" #include "qeglfsscreen.h" #include "qeglfsglobal.h" -#include <QtPlatformSupport/private/qeglplatformwindow_p.h> + +#include <qpa/qplatformwindow.h> +#include <QtPlatformSupport/private/qopenglcompositor_p.h> +#include <EGL/egl.h> QT_BEGIN_NAMESPACE -class Q_EGLFS_EXPORT QEglFSWindow : public QEGLPlatformWindow +class QOpenGLCompositorBackingStore; +class QPlatformTextureList; + +class Q_EGLFS_EXPORT QEglFSWindow : public QPlatformWindow, public QOpenGLCompositorWindow { public: QEglFSWindow(QWindow *w); ~QEglFSWindow(); - void create() Q_DECL_OVERRIDE; + void create(); void destroy(); void setGeometry(const QRect &) Q_DECL_OVERRIDE; @@ -58,13 +64,15 @@ public: void lower() Q_DECL_OVERRIDE; void propagateSizeHints() Q_DECL_OVERRIDE { } - void setOpacity(qreal) Q_DECL_OVERRIDE { } void setMask(const QRegion &) Q_DECL_OVERRIDE { } bool setKeyboardGrabEnabled(bool) Q_DECL_OVERRIDE { return false; } bool setMouseGrabEnabled(bool) Q_DECL_OVERRIDE { return false; } + void setOpacity(qreal) Q_DECL_OVERRIDE; + WId winId() const Q_DECL_OVERRIDE; QSurfaceFormat format() const Q_DECL_OVERRIDE; - EGLNativeWindowType eglWindow() const Q_DECL_OVERRIDE; + + EGLNativeWindowType eglWindow() const; EGLSurface surface() const; QEglFSScreen *screen() const; @@ -73,11 +81,22 @@ public: virtual void invalidateSurface() Q_DECL_OVERRIDE; virtual void resetSurface(); + QOpenGLCompositorBackingStore *backingStore() { return m_backingStore; } + void setBackingStore(QOpenGLCompositorBackingStore *backingStore) { m_backingStore = backingStore; } + bool isRaster() const; + + QWindow *sourceWindow() const Q_DECL_OVERRIDE; + const QPlatformTextureList *textures() const Q_DECL_OVERRIDE; + void endCompositing() Q_DECL_OVERRIDE; + protected: + QOpenGLCompositorBackingStore *m_backingStore; + bool m_raster; + WId m_winId; + EGLSurface m_surface; EGLNativeWindowType m_window; -private: EGLConfig m_config; QSurfaceFormat m_format; diff --git a/src/plugins/platforms/haiku/qhaikuclipboard.cpp b/src/plugins/platforms/haiku/qhaikuclipboard.cpp index f3aa9dc36e..a2d7e96d71 100644 --- a/src/plugins/platforms/haiku/qhaikuclipboard.cpp +++ b/src/plugins/platforms/haiku/qhaikuclipboard.cpp @@ -41,6 +41,8 @@ #include <Clipboard.h> QHaikuClipboard::QHaikuClipboard() + : m_systemMimeData(Q_NULLPTR) + , m_userMimeData(Q_NULLPTR) { if (be_clipboard) be_clipboard->StartWatching(BMessenger(this)); @@ -50,17 +52,26 @@ QHaikuClipboard::~QHaikuClipboard() { if (be_clipboard) be_clipboard->StopWatching(BMessenger(this)); + + delete m_userMimeData; + delete m_systemMimeData; } QMimeData *QHaikuClipboard::mimeData(QClipboard::Mode mode) { - QMimeData *mimeData = new QMimeData(); - if (mode != QClipboard::Clipboard) - return mimeData; + return 0; + + if (m_userMimeData) + return m_userMimeData; if (!be_clipboard->Lock()) - return mimeData; + return 0; + + if (!m_systemMimeData) + m_systemMimeData = new QMimeData(); + else + m_systemMimeData->clear(); const BMessage *clipboard = be_clipboard->Data(); if (clipboard) { @@ -76,11 +87,11 @@ QMimeData *QHaikuClipboard::mimeData(QClipboard::Mode mode) if (dataLen && (status == B_OK)) { const QString format = QString::fromLatin1(name); if (format == QStringLiteral("text/plain")) { - mimeData->setText(QString::fromLocal8Bit(reinterpret_cast<const char*>(data), dataLen)); + m_systemMimeData->setText(QString::fromLocal8Bit(reinterpret_cast<const char*>(data), dataLen)); } else if (format == QStringLiteral("text/html")) { - mimeData->setHtml(QString::fromLocal8Bit(reinterpret_cast<const char*>(data), dataLen)); + m_systemMimeData->setHtml(QString::fromLocal8Bit(reinterpret_cast<const char*>(data), dataLen)); } else { - mimeData->setData(format, QByteArray(reinterpret_cast<const char*>(data), dataLen)); + m_systemMimeData->setData(format, QByteArray(reinterpret_cast<const char*>(data), dataLen)); } } } @@ -88,7 +99,7 @@ QMimeData *QHaikuClipboard::mimeData(QClipboard::Mode mode) be_clipboard->Unlock(); - return mimeData; + return m_systemMimeData; } void QHaikuClipboard::setMimeData(QMimeData *mimeData, QClipboard::Mode mode) @@ -96,6 +107,14 @@ void QHaikuClipboard::setMimeData(QMimeData *mimeData, QClipboard::Mode mode) if (mode != QClipboard::Clipboard) return; + if (mimeData) { + if (m_systemMimeData == mimeData) + return; + + if (m_userMimeData == mimeData) + return; + } + if (!be_clipboard->Lock()) return; @@ -115,6 +134,10 @@ void QHaikuClipboard::setMimeData(QMimeData *mimeData, QClipboard::Mode mode) qWarning("Unable to store mime data on clipboard"); be_clipboard->Unlock(); + + m_userMimeData = mimeData; + + emitChanged(QClipboard::Clipboard); } bool QHaikuClipboard::supportsMode(QClipboard::Mode mode) const @@ -131,8 +154,12 @@ bool QHaikuClipboard::ownsMode(QClipboard::Mode mode) const void QHaikuClipboard::MessageReceived(BMessage* message) { - if (message->what == B_CLIPBOARD_CHANGED) + if (message->what == B_CLIPBOARD_CHANGED) { + delete m_userMimeData; + m_userMimeData = Q_NULLPTR; + emitChanged(QClipboard::Clipboard); + } BHandler::MessageReceived(message); } diff --git a/src/plugins/platforms/haiku/qhaikuclipboard.h b/src/plugins/platforms/haiku/qhaikuclipboard.h index 0dc2bfdd3b..3c1f92c615 100644 --- a/src/plugins/platforms/haiku/qhaikuclipboard.h +++ b/src/plugins/platforms/haiku/qhaikuclipboard.h @@ -55,6 +55,10 @@ public: // override from BHandler to catch change notifications from Haiku clipboard void MessageReceived(BMessage* message) Q_DECL_OVERRIDE; + +private: + QMimeData *m_systemMimeData; + QMimeData *m_userMimeData; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/haiku/qhaikuwindow.cpp b/src/plugins/platforms/haiku/qhaikuwindow.cpp index 5768e1cb40..9622d12111 100644 --- a/src/plugins/platforms/haiku/qhaikuwindow.cpp +++ b/src/plugins/platforms/haiku/qhaikuwindow.cpp @@ -130,6 +130,7 @@ QHaikuWindow::QHaikuWindow(QWindow *window) if (!m_window) qFatal("QHaikuWindow: failed to create window"); + setGeometry(rect); setWindowFlags(window->flags()); } @@ -164,13 +165,13 @@ void QHaikuWindow::setVisible(bool visible) { if (visible) { m_window->Show(); + + window()->requestActivate(); + + QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), window()->geometry().size())); } else { m_window->Hide(); } - - window()->requestActivate(); - - QWindowSystemInterface::handleExposeEvent(window(), window()->geometry()); } bool QHaikuWindow::isExposed() const @@ -306,10 +307,8 @@ void QHaikuWindow::haikuWindowMoved(const QPoint &pos) const QRect newGeometry(pos, geometry().size()); QPlatformWindow::setGeometry(newGeometry); - QWindowSystemInterface::setSynchronousWindowsSystemEvents(true); QWindowSystemInterface::handleGeometryChange(window(), newGeometry); - QWindowSystemInterface::handleExposeEvent(window(), newGeometry); - QWindowSystemInterface::setSynchronousWindowsSystemEvents(false); + QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), newGeometry.size())); } void QHaikuWindow::haikuWindowResized(const QSize &size, bool zoomInProgress) @@ -317,10 +316,8 @@ void QHaikuWindow::haikuWindowResized(const QSize &size, bool zoomInProgress) const QRect newGeometry(geometry().topLeft(), size); QPlatformWindow::setGeometry(newGeometry); - QWindowSystemInterface::setSynchronousWindowsSystemEvents(true); QWindowSystemInterface::handleGeometryChange(window(), newGeometry); - QWindowSystemInterface::handleExposeEvent(window(), newGeometry); - QWindowSystemInterface::setSynchronousWindowsSystemEvents(false); + QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), newGeometry.size())); if ((m_windowState == Qt::WindowMaximized) && !zoomInProgress) { // the user has resized the window while maximized -> reset maximized flag diff --git a/src/plugins/platforms/ios/qiosapplicationstate.mm b/src/plugins/platforms/ios/qiosapplicationstate.mm index 92799f80c1..7a37e213bd 100644 --- a/src/plugins/platforms/ios/qiosapplicationstate.mm +++ b/src/plugins/platforms/ios/qiosapplicationstate.mm @@ -43,7 +43,7 @@ @implementation QIOSApplicationStateListener -- (id) init +- (id)init { self = [super init]; if (self) { @@ -75,7 +75,7 @@ return self; } -- (void) dealloc +- (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self @@ -92,12 +92,12 @@ [super dealloc]; } -- (void) applicationDidBecomeActive +- (void)applicationDidBecomeActive { [self handleApplicationStateChanged:UIApplicationStateActive]; } -- (void) applicationWillResignActive +- (void)applicationWillResignActive { // Note that UIApplication is still UIApplicationStateActive at this // point, but since there is no separate notification for the inactive @@ -105,12 +105,12 @@ [self handleApplicationStateChanged:UIApplicationStateInactive]; } -- (void) applicationDidEnterBackground +- (void)applicationDidEnterBackground { [self handleApplicationStateChanged:UIApplicationStateBackground]; } -- (void) handleApplicationStateChanged:(UIApplicationState) uiApplicationState +- (void)handleApplicationStateChanged:(UIApplicationState)uiApplicationState { // We may receive application state changes after QCoreApplication has // gone down, as the block we schedule on the main queue keeps the diff --git a/src/plugins/platforms/ios/qiosclipboard.mm b/src/plugins/platforms/ios/qiosclipboard.mm index 192ee67689..e0c6ec5d72 100644 --- a/src/plugins/platforms/ios/qiosclipboard.mm +++ b/src/plugins/platforms/ios/qiosclipboard.mm @@ -62,7 +62,7 @@ @implementation QUIClipboard --(id)initWithQIOSClipboard:(QIOSClipboard *)qiosClipboard +- (id)initWithQIOSClipboard:(QIOSClipboard *)qiosClipboard { self = [super init]; if (self) { @@ -87,7 +87,7 @@ return self; } --(void)dealloc +- (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self diff --git a/src/plugins/platforms/ios/qioseventdispatcher.h b/src/plugins/platforms/ios/qioseventdispatcher.h index fdaa7e68fe..98977eb670 100644 --- a/src/plugins/platforms/ios/qioseventdispatcher.h +++ b/src/plugins/platforms/ios/qioseventdispatcher.h @@ -34,7 +34,7 @@ #ifndef QIOSEVENTDISPATCHER_H #define QIOSEVENTDISPATCHER_H -#include <QtPlatformSupport/private/qeventdispatcher_cf_p.h> +#include <QtCore/private/qeventdispatcher_cf_p.h> QT_BEGIN_NAMESPACE @@ -46,6 +46,7 @@ public: explicit QIOSEventDispatcher(QObject *parent = 0); bool processEvents(QEventLoop::ProcessEventsFlags flags) Q_DECL_OVERRIDE; + bool processPostedEvents() Q_DECL_OVERRIDE; void handleRunLoopExit(CFRunLoopActivity activity); diff --git a/src/plugins/platforms/ios/qioseventdispatcher.mm b/src/plugins/platforms/ios/qioseventdispatcher.mm index fc12e83a81..0e9f176487 100644 --- a/src/plugins/platforms/ios/qioseventdispatcher.mm +++ b/src/plugins/platforms/ios/qioseventdispatcher.mm @@ -39,6 +39,8 @@ #include <QtCore/private/qcoreapplication_p.h> #include <QtCore/private/qthread_p.h> +#include <qpa/qwindowsysteminterface.h> + #import <Foundation/NSArray.h> #import <Foundation/NSString.h> #import <Foundation/NSProcessInfo.h> @@ -198,7 +200,7 @@ namespace bool debugStackUsage = false; } -extern "C" int __attribute__((weak)) main(int argc, char *argv[]) +extern "C" int qt_main_wrapper(int argc, char *argv[]) { @autoreleasepool { size_t defaultStackSize = 512 * kBytesPerKiloByte; // Same as secondary threads @@ -233,18 +235,7 @@ enum SetJumpResult kJumpedFromUserMainTrampoline, }; -// We define qtmn so that user_main_trampoline() will not cause -// missing symbols in the case of hybrid applications that don't -// use our main wrapper. Since the symbol is weak, it will not -// get used or cause a clash in the normal Qt application usecase, -// where we rename main to qtmn before linking. -extern "C" int __attribute__((weak)) qtmn(int argc, char *argv[]) -{ - Q_UNUSED(argc); - Q_UNUSED(argv); - - Q_UNREACHABLE(); -} +extern "C" int main(int argc, char *argv[]); static void __attribute__((noinline, noreturn)) user_main_trampoline() { @@ -263,7 +254,7 @@ static void __attribute__((noinline, noreturn)) user_main_trampoline() qFatal("Could not convert argv[%d] to C string", i); } - int exitCode = qtmn(argc, argv); + int exitCode = main(argc, argv); delete[] argv; qEventDispatcherDebug() << "Returned from main with exit code " << exitCode; @@ -293,7 +284,7 @@ static bool rootLevelRunLoopIntegration() @implementation QIOSApplicationStateTracker -+ (void) load ++ (void)load { [[NSNotificationCenter defaultCenter] addObserver:self @@ -323,7 +314,7 @@ static bool rootLevelRunLoopIntegration() # error "Unknown processor family" #endif -+ (void) applicationDidFinishLaunching ++ (void)applicationDidFinishLaunching { if (!isQtApplication()) return; @@ -377,7 +368,7 @@ static bool rootLevelRunLoopIntegration() // four bits of the exit code (exit(3) will only pass on the lower 8 bits). static const char kApplicationWillTerminateExitCode = SIGTERM | 0x80; -+ (void) applicationWillTerminate ++ (void)applicationWillTerminate { if (!isQtApplication()) return; @@ -472,6 +463,25 @@ bool __attribute__((returns_twice)) QIOSEventDispatcher::processEvents(QEventLoo return processedEvents; } +/*! + Override of the CoreFoundation posted events runloop source callback + so that we can send window system (QPA) events in addition to sending + normal Qt events. +*/ +bool QIOSEventDispatcher::processPostedEvents() +{ + // Don't send window system events if the base CF dispatcher has determined + // that events should not be sent for this pass of the runloop source. + if (!QEventDispatcherCoreFoundation::processPostedEvents()) + return false; + + qEventDispatcherDebug() << "Sending window system events for " << m_processEvents.flags; qIndent(); + QWindowSystemInterface::sendWindowSystemEvents(m_processEvents.flags); + qUnIndent(); + + return true; +} + void QIOSEventDispatcher::handleRunLoopExit(CFRunLoopActivity activity) { Q_UNUSED(activity); diff --git a/src/plugins/platforms/ios/qiosfileengineassetslibrary.mm b/src/plugins/platforms/ios/qiosfileengineassetslibrary.mm index 761a89c989..bb12c164d6 100644 --- a/src/plugins/platforms/ios/qiosfileengineassetslibrary.mm +++ b/src/plugins/platforms/ios/qiosfileengineassetslibrary.mm @@ -48,20 +48,29 @@ static QThreadStorage<QPointer<QIOSAssetData> > g_assetDataCache; static const int kBufferSize = 10; static ALAsset *kNoAsset = 0; -static void ensureAuthorizationDialogNotBlocked() +static bool ensureAuthorizationDialogNotBlocked() { if ([ALAssetsLibrary authorizationStatus] != ALAuthorizationStatusNotDetermined) - return; + return true; + if (static_cast<QCoreApplicationPrivate *>(QObjectPrivate::get(qApp))->in_exec) - return; - - // Since authorization status has not been determined, the user will be asked - // to authorize the app. But since main has not finished, the dialog will be held - // back until the launch completes. To avoid a dead-lock below, we start an event - // loop to complete the launch. - QEventLoop loop; - QTimer::singleShot(1, &loop, &QEventLoop::quit); - loop.exec(); + return true; + + if ([NSThread isMainThread]) { + // The dialog is about to show, but since main has not finished, the dialog will be held + // back until the launch completes. This is problematic since we cannot successfully return + // back to the caller before the asset is ready, which also includes showing the dialog. To + // work around this, we create an event loop to that will complete the launch (return from the + // applicationDidFinishLaunching callback). But this will only work if we're on the main thread. + QEventLoop loop; + QTimer::singleShot(1, &loop, &QEventLoop::quit); + loop.exec(); + } else { + NSLog(@"QIOSFileEngine: unable to show assets authorization dialog from non-gui thread before QApplication is executing."); + return false; + } + + return true; } // ------------------------------------------------------------------------- @@ -80,8 +89,10 @@ public: , m_writeIndex(0) , m_nextAssetReady(false) { - ensureAuthorizationDialogNotBlocked(); - startEnumerate(); + if (!ensureAuthorizationDialogNotBlocked()) + writeAsset(kNoAsset); + else + startEnumerate(); } ~QIOSAssetEnumerator() @@ -186,7 +197,8 @@ public: , m_assetUrl(assetUrl) , m_assetLibrary(0) { - ensureAuthorizationDialogNotBlocked(); + if (!ensureAuthorizationDialogNotBlocked()) + return; if (QIOSAssetData *assetData = g_assetDataCache.localData()) { // It's a common pattern that QFiles pointing to the same path are created and destroyed diff --git a/src/plugins/platforms/ios/qiosglobal.h b/src/plugins/platforms/ios/qiosglobal.h index 86b784618f..544f9e0a42 100644 --- a/src/plugins/platforms/ios/qiosglobal.h +++ b/src/plugins/platforms/ios/qiosglobal.h @@ -68,7 +68,7 @@ int infoPlistValue(NSString* key, int defaultValue); QT_END_NAMESPACE @interface UIResponder (QtFirstResponder) -+(id)currentFirstResponder; ++ (id)currentFirstResponder; @end class FirstResponderCandidate : public QScopedValueRollback<UIResponder *> diff --git a/src/plugins/platforms/ios/qiosglobal.mm b/src/plugins/platforms/ios/qiosglobal.mm index ef24abbfd9..f5b971391d 100644 --- a/src/plugins/platforms/ios/qiosglobal.mm +++ b/src/plugins/platforms/ios/qiosglobal.mm @@ -133,7 +133,7 @@ int infoPlistValue(NSString* key, int defaultValue) @end @implementation QtFirstResponderEvent -- (void) dealloc +- (void)dealloc { self.firstResponder = 0; [super dealloc]; @@ -158,7 +158,7 @@ int infoPlistValue(NSString* key, int defaultValue) @implementation UIResponder (QtFirstResponder) -+(id)currentFirstResponder ++ (id)currentFirstResponder { QtFirstResponderEvent *event = [[[QtFirstResponderEvent alloc] init] autorelease]; [[UIApplication sharedApplication] sendAction:@selector(qt_findFirstResponder:event:) to:nil from:nil forEvent:event]; diff --git a/src/plugins/platforms/ios/qiosinputcontext.mm b/src/plugins/platforms/ios/qiosinputcontext.mm index 090df66e0d..d03c589b2a 100644 --- a/src/plugins/platforms/ios/qiosinputcontext.mm +++ b/src/plugins/platforms/ios/qiosinputcontext.mm @@ -579,7 +579,7 @@ void QIOSInputContext::focusWindowChanged(QWindow *focusWindow) void QIOSInputContext::update(Qt::InputMethodQueries updatedProperties) { // Mask for properties that we are interested in and see if any of them changed - updatedProperties &= (Qt::ImEnabled | Qt::ImHints | Qt::ImQueryInput | Qt::ImPlatformData); + updatedProperties &= (Qt::ImEnabled | Qt::ImHints | Qt::ImQueryInput | Qt::ImEnterKeyType | Qt::ImPlatformData); qImDebug() << "fw =" << qApp->focusWindow() << "fo =" << qApp->focusObject(); diff --git a/src/plugins/platforms/ios/qiosintegration.mm b/src/plugins/platforms/ios/qiosintegration.mm index 8395db81ff..0e3da8dce8 100644 --- a/src/plugins/platforms/ios/qiosintegration.mm +++ b/src/plugins/platforms/ios/qiosintegration.mm @@ -89,10 +89,11 @@ QIOSIntegration::QIOSIntegration() // Set current directory to app bundle folder QDir::setCurrent(QString::fromUtf8([[[NSBundle mainBundle] bundlePath] UTF8String])); + UIScreen *mainScreen = [UIScreen mainScreen]; NSMutableArray *screens = [[[UIScreen screens] mutableCopy] autorelease]; - if (![screens containsObject:[UIScreen mainScreen]]) { + if (![screens containsObject:mainScreen]) { // Fallback for iOS 7.1 (QTBUG-42345) - [screens insertObject:[UIScreen mainScreen] atIndex:0]; + [screens insertObject:mainScreen atIndex:0]; } for (UIScreen *screen in screens) @@ -103,7 +104,10 @@ QIOSIntegration::QIOSIntegration() m_touchDevice = new QTouchDevice; m_touchDevice->setType(QTouchDevice::TouchScreen); - m_touchDevice->setCapabilities(QTouchDevice::Position | QTouchDevice::NormalizedPosition); + QTouchDevice::Capabilities touchCapabilities = QTouchDevice::Position | QTouchDevice::NormalizedPosition; + if (mainScreen.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) + touchCapabilities |= QTouchDevice::Pressure; + m_touchDevice->setCapabilities(touchCapabilities); QWindowSystemInterface::registerTouchDevice(m_touchDevice); QMacInternalPasteboardMime::initializeMimeTypes(); } @@ -219,6 +223,10 @@ QPlatformServices *QIOSIntegration::services() const QVariant QIOSIntegration::styleHint(StyleHint hint) const { switch (hint) { + case PasswordMaskDelay: + // this number is based on timing the native delay + // since there is no API to get it + return 2000; case ShowIsMaximized: return true; case SetFocusOnTouchRelease: diff --git a/src/plugins/platforms/ios/qiosmenu.mm b/src/plugins/platforms/ios/qiosmenu.mm index 09395805bf..7aea3729fd 100644 --- a/src/plugins/platforms/ios/qiosmenu.mm +++ b/src/plugins/platforms/ios/qiosmenu.mm @@ -165,7 +165,7 @@ static NSString *const kSelectorPrefix = @"_qtMenuItem_"; [self reloadAllComponents]; } --(void)listenForKeyboardWillHideNotification:(BOOL)listen +- (void)listenForKeyboardWillHideNotification:(BOOL)listen { if (listen) { [[NSNotificationCenter defaultCenter] @@ -179,7 +179,7 @@ static NSString *const kSelectorPrefix = @"_qtMenuItem_"; } } --(void)dealloc +- (void)dealloc { [self listenForKeyboardWillHideNotification:NO]; self.toolbar = 0; diff --git a/src/plugins/platforms/ios/qiosscreen.h b/src/plugins/platforms/ios/qiosscreen.h index a0aa922a31..f31be9756c 100644 --- a/src/plugins/platforms/ios/qiosscreen.h +++ b/src/plugins/platforms/ios/qiosscreen.h @@ -73,7 +73,7 @@ private: QRect m_geometry; QRect m_availableGeometry; int m_depth; - uint m_unscaledDpi; + uint m_pixelDensity; QSizeF m_physicalSize; QIOSOrientationListener *m_orientationListener; }; diff --git a/src/plugins/platforms/ios/qiosscreen.mm b/src/plugins/platforms/ios/qiosscreen.mm index 324133074b..5cb06d591d 100644 --- a/src/plugins/platforms/ios/qiosscreen.mm +++ b/src/plugins/platforms/ios/qiosscreen.mm @@ -104,12 +104,12 @@ static QIOSScreen* qtPlatformScreenFor(UIScreen *uiScreen) @public QIOSScreen *m_screen; } -- (id) initWithQIOSScreen:(QIOSScreen *)screen; +- (id)initWithQIOSScreen:(QIOSScreen *)screen; @end @implementation QIOSOrientationListener -- (id) initWithQIOSScreen:(QIOSScreen *)screen +- (id)initWithQIOSScreen:(QIOSScreen *)screen { self = [super init]; if (self) { @@ -123,7 +123,7 @@ static QIOSScreen* qtPlatformScreenFor(UIScreen *uiScreen) return self; } -- (void) dealloc +- (void)dealloc { [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications]; [[NSNotificationCenter defaultCenter] @@ -132,7 +132,7 @@ static QIOSScreen* qtPlatformScreenFor(UIScreen *uiScreen) [super dealloc]; } -- (void) orientationChanged:(NSNotification *)notification +- (void)orientationChanged:(NSNotification *)notification { Q_UNUSED(notification); m_screen->updateProperties(); @@ -170,23 +170,28 @@ QIOSScreen::QIOSScreen(UIScreen *screen) if (screen == [UIScreen mainScreen]) { QString deviceIdentifier = deviceModelIdentifier(); - if (deviceIdentifier == QLatin1String("iPhone2,1") /* iPhone 3GS */ - || deviceIdentifier == QLatin1String("iPod3,1") /* iPod touch 3G */) { + // Based on https://en.wikipedia.org/wiki/List_of_iOS_devices#Display + + // iPhone (1st gen), 3G, 3GS, and iPod Touch (1st–3rd gen) are 18-bit devices + if (deviceIdentifier.contains(QRegularExpression("^(iPhone1,[12]|iPhone2,1|iPod[1-3],1)$"))) m_depth = 18; - } else { + else m_depth = 24; - } - if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad - && !deviceIdentifier.contains(QRegularExpression("^iPad2,[567]$")) /* excluding iPad Mini */) { - m_unscaledDpi = 132; + if (deviceIdentifier.contains(QRegularExpression("^iPhone(7,1|8,2)$"))) { + // iPhone 6 Plus or iPhone 6S Plus + m_pixelDensity = 401; + } else if (deviceIdentifier.contains(QRegularExpression("^iPad(1,1|2,[1-4]|3,[1-6]|4,[1-3]|5,[3-4]|6,[7-8])$"))) { + // All iPads except the iPad Mini series + m_pixelDensity = 132 * devicePixelRatio(); } else { - m_unscaledDpi = 163; // Regular iPhone DPI + // All non-Plus iPhones, and iPad Minis + m_pixelDensity = 163 * devicePixelRatio(); } } else { // External display, hard to say m_depth = 24; - m_unscaledDpi = 96; + m_pixelDensity = 96; } for (UIWindow *existingWindow in [[UIApplication sharedApplication] windows]) { @@ -249,7 +254,7 @@ void QIOSScreen::updateProperties() if (m_geometry != previousGeometry) { const qreal millimetersPerInch = 25.4; - m_physicalSize = QSizeF(m_geometry.size()) / m_unscaledDpi * millimetersPerInch; + m_physicalSize = QSizeF(m_geometry.size() * devicePixelRatio()) / m_pixelDensity * millimetersPerInch; } // At construction time, we don't yet have an associated QScreen, but we still want diff --git a/src/plugins/platforms/ios/qiostextresponder.mm b/src/plugins/platforms/ios/qiostextresponder.mm index f3ea68cdc4..9ca5e22b90 100644 --- a/src/plugins/platforms/ios/qiostextresponder.mm +++ b/src/plugins/platforms/ios/qiostextresponder.mm @@ -92,12 +92,12 @@ return [QUITextPosition positionWithIndex:(self.range.location + self.range.length)]; } -- (NSRange) range +- (NSRange)range { return _range; } --(BOOL)isEmpty +- (BOOL)isEmpty { return (self.range.length == 0); } @@ -111,7 +111,7 @@ @implementation WrapperView --(id)initWithView:(UIView *)view +- (id)initWithView:(UIView *)view { if (self = [self init]) { [self addSubview:view]; @@ -143,7 +143,7 @@ // retained, we ensure that all messages sent to the view during // its lifetime in a window hierarcy will be able to traverse the // responder chain. --(void)willMoveToWindow:(UIWindow *)window +- (void)willMoveToWindow:(UIWindow *)window { if (window) [[self nextResponder] retain]; @@ -170,9 +170,31 @@ QVariantMap platformData = m_configuredImeState->value(Qt::ImPlatformData).toMap(); Qt::InputMethodHints hints = Qt::InputMethodHints(m_configuredImeState->value(Qt::ImHints).toUInt()); - self.returnKeyType = platformData.value(kImePlatformDataReturnKeyType).isValid() ? - UIReturnKeyType(platformData.value(kImePlatformDataReturnKeyType).toInt()) : - (hints & Qt::ImhMultiLine) ? UIReturnKeyDefault : UIReturnKeyDone; + Qt::EnterKeyType enterKeyType = Qt::EnterKeyType(m_configuredImeState->value(Qt::ImEnterKeyType).toUInt()); + + switch (enterKeyType) { + case Qt::EnterKeyReturn: + self.returnKeyType = UIReturnKeyDefault; + break; + case Qt::EnterKeyDone: + self.returnKeyType = UIReturnKeyDone; + break; + case Qt::EnterKeyGo: + self.returnKeyType = UIReturnKeyGo; + break; + case Qt::EnterKeySend: + self.returnKeyType = UIReturnKeySend; + break; + case Qt::EnterKeySearch: + self.returnKeyType = UIReturnKeySearch; + break; + case Qt::EnterKeyNext: + self.returnKeyType = UIReturnKeyNext; + break; + default: + self.returnKeyType = (hints & Qt::ImhMultiLine) ? UIReturnKeyDefault : UIReturnKeyDone; + break; + } self.secureTextEntry = BOOL(hints & Qt::ImhHiddenText); self.autocorrectionType = (hints & Qt::ImhNoPredictiveText) ? @@ -238,7 +260,7 @@ } // Based on what we set up in initWithInputContext above - updatedProperties &= (Qt::ImHints | Qt::ImPlatformData); + updatedProperties &= (Qt::ImHints | Qt::ImEnterKeyType | Qt::ImPlatformData); if (!updatedProperties) return NO; @@ -546,17 +568,17 @@ return m_inputContext->imeState().currentState.value(query); } --(id<UITextInputTokenizer>)tokenizer +- (id<UITextInputTokenizer>)tokenizer { return [[[UITextInputStringTokenizer alloc] initWithTextInput:id<UITextInput>(self)] autorelease]; } --(UITextPosition *)beginningOfDocument +- (UITextPosition *)beginningOfDocument { return [QUITextPosition positionWithIndex:0]; } --(UITextPosition *)endOfDocument +- (UITextPosition *)endOfDocument { int endPosition = [self currentImeState:Qt::ImSurroundingText].toString().length(); return [QUITextPosition positionWithIndex:endPosition]; @@ -841,7 +863,7 @@ return [NSDictionary dictionaryWithObject:uifont forKey:UITextInputTextFontKey]; } --(NSDictionary *)markedTextStyle +- (NSDictionary *)markedTextStyle { return [NSDictionary dictionary]; } @@ -860,7 +882,8 @@ if ([text isEqualToString:@"\n"]) { [self sendKeyPressRelease:Qt::Key_Return modifiers:Qt::NoModifier]; - if (self.returnKeyType == UIReturnKeyDone) + if (self.returnKeyType == UIReturnKeyDone || self.returnKeyType == UIReturnKeyGo + || self.returnKeyType == UIReturnKeySend || self.returnKeyType == UIReturnKeySearch) [self resignFirstResponder]; return; diff --git a/src/plugins/platforms/ios/qiosviewcontroller.mm b/src/plugins/platforms/ios/qiosviewcontroller.mm index 62f5387979..94d894bba7 100644 --- a/src/plugins/platforms/ios/qiosviewcontroller.mm +++ b/src/plugins/platforms/ios/qiosviewcontroller.mm @@ -143,7 +143,7 @@ if (uiWindow.screen != [UIScreen mainScreen] && self.subviews.count == 1) { // Removing the last view of an external screen, go back to mirror mode - uiWindow.screen = nil; + uiWindow.screen = [UIScreen mainScreen]; uiWindow.hidden = YES; } } @@ -296,13 +296,13 @@ // ------------------------------------------------------------------------- --(BOOL)shouldAutorotate +- (BOOL)shouldAutorotate { return m_screen && m_screen->uiScreen() == [UIScreen mainScreen] && !self.lockedOrientation; } #if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_6_0) --(NSUInteger)supportedInterfaceOrientations +- (NSUInteger)supportedInterfaceOrientations { // As documented by Apple in the iOS 6.0 release notes, setStatusBarOrientation:animated: // only works if the supportedInterfaceOrientations of the view controller is 0, making @@ -315,7 +315,7 @@ #endif #if QT_IOS_DEPLOYMENT_TARGET_BELOW(__IPHONE_6_0) --(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { Q_UNUSED(interfaceOrientation); return [self shouldAutorotate]; diff --git a/src/plugins/platforms/ios/quiaccessibilityelement.h b/src/plugins/platforms/ios/quiaccessibilityelement.h index a690e12c7d..c76e3a6a1e 100644 --- a/src/plugins/platforms/ios/quiaccessibilityelement.h +++ b/src/plugins/platforms/ios/quiaccessibilityelement.h @@ -42,8 +42,8 @@ @property (readonly) QAccessible::Id axid; -- (id) initWithId: (QAccessible::Id) anId withAccessibilityContainer: (id) view; -+ (QMacAccessibilityElement *) elementWithId: (QAccessible::Id) anId withAccessibilityContainer: (id) view; +- (id)initWithId:(QAccessible::Id)anId withAccessibilityContainer:(id)view; ++ (QMacAccessibilityElement *)elementWithId:(QAccessible::Id)anId withAccessibilityContainer:(id)view; @end diff --git a/src/plugins/platforms/ios/quiaccessibilityelement.mm b/src/plugins/platforms/ios/quiaccessibilityelement.mm index 2cecfc1126..3bac1ca88d 100644 --- a/src/plugins/platforms/ios/quiaccessibilityelement.mm +++ b/src/plugins/platforms/ios/quiaccessibilityelement.mm @@ -37,7 +37,7 @@ @implementation QMacAccessibilityElement -- (id) initWithId: (QAccessible::Id) anId withAccessibilityContainer: (id) view +- (id)initWithId:(QAccessible::Id)anId withAccessibilityContainer:(id)view { Q_ASSERT((int)anId < 0); self = [super initWithAccessibilityContainer: view]; @@ -47,7 +47,7 @@ return self; } -+ (id) elementWithId: (QAccessible::Id) anId withAccessibilityContainer: (id) view ++ (id)elementWithId:(QAccessible::Id)anId withAccessibilityContainer:(id)view { Q_ASSERT(anId); if (!anId) @@ -64,17 +64,17 @@ return element; } -- (void) invalidate +- (void)invalidate { [self release]; } -- (BOOL) isAccessibilityElement +- (BOOL)isAccessibilityElement { return YES; } -- (NSString*) accessibilityLabel +- (NSString*)accessibilityLabel { QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid); if (!iface) { @@ -85,7 +85,7 @@ return iface->text(QAccessible::Name).toNSString(); } -- (NSString*) accessibilityHint +- (NSString*)accessibilityHint { QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid); if (!iface) { @@ -95,7 +95,7 @@ return iface->text(QAccessible::Description).toNSString(); } -- (NSString*) accessibilityValue +- (NSString*)accessibilityValue { QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid); if (!iface) { @@ -119,7 +119,7 @@ return [super accessibilityHint]; } -- (CGRect) accessibilityFrame +- (CGRect)accessibilityFrame { QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid); if (!iface) { @@ -131,7 +131,7 @@ return CGRectMake(rect.x(), rect.y(), rect.width(), rect.height()); } -- (UIAccessibilityTraits) accessibilityTraits +- (UIAccessibilityTraits)accessibilityTraits { UIAccessibilityTraits traits = UIAccessibilityTraitNone; @@ -156,7 +156,7 @@ return traits; } -- (BOOL) accessibilityActivate +- (BOOL)accessibilityActivate { QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid); if (QAccessibleActionInterface *action = iface->actionInterface()) { @@ -171,21 +171,21 @@ return NO; // fall back to sending mouse clicks } -- (void) accessibilityIncrement +- (void)accessibilityIncrement { QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid); if (QAccessibleActionInterface *action = iface->actionInterface()) action->doAction(QAccessibleActionInterface::increaseAction()); } -- (void) accessibilityDecrement +- (void)accessibilityDecrement { QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid); if (QAccessibleActionInterface *action = iface->actionInterface()) action->doAction(QAccessibleActionInterface::decreaseAction()); } -- (BOOL) accessibilityScroll : (UIAccessibilityScrollDirection) direction +- (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction { QAccessibleInterface *iface = QAccessible::accessibleInterface(self.axid); QAccessibleActionInterface *action = iface->actionInterface(); diff --git a/src/plugins/platforms/ios/quiview.mm b/src/plugins/platforms/ios/quiview.mm index 87dc3b9dcb..53b3d30327 100644 --- a/src/plugins/platforms/ios/quiview.mm +++ b/src/plugins/platforms/ios/quiview.mm @@ -50,7 +50,7 @@ return [CAEAGLLayer class]; } --(id)initWithQIOSWindow:(QIOSWindow *)window +- (id)initWithQIOSWindow:(QIOSWindow *)window { if (self = [self initWithFrame:toCGRect(window->geometry())]) m_qioswindow = window; @@ -280,6 +280,19 @@ // ------------------------------------------------------------------------- +- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection +{ + [super traitCollectionDidChange: previousTraitCollection]; + + QTouchDevice *touchDevice = QIOSIntegration::instance()->touchDevice(); + QTouchDevice::Capabilities touchCapabilities = touchDevice->capabilities(); + if (self.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable) + touchCapabilities |= QTouchDevice::Pressure; + else + touchCapabilities &= ~QTouchDevice::Pressure; + touchDevice->setCapabilities(touchCapabilities); +} + -(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { if (m_qioswindow->window()->flags() & Qt::WindowTransparentForInput) @@ -289,6 +302,8 @@ - (void)updateTouchList:(NSSet *)touches withState:(Qt::TouchPointState)state { + bool supportsPressure = QIOSIntegration::instance()->touchDevice()->capabilities() & QTouchDevice::Pressure; + foreach (UITouch *uiTouch, m_activeTouches.keys()) { QWindowSystemInterface::TouchPoint &touchPoint = m_activeTouches[uiTouch]; if (![touches containsObject:uiTouch]) { @@ -309,14 +324,22 @@ touchPoint.normalPosition = QPointF(globalScreenPosition.x() / screenSize.width(), globalScreenPosition.y() / screenSize.height()); - // We don't claim that our touch device supports QTouchDevice::Pressure, - // but fill in a meaningfull value in case clients use it anyways. - touchPoint.pressure = (state == Qt::TouchPointReleased) ? 0.0 : 1.0; + if (supportsPressure) { + // Note: iOS will deliver touchesBegan with a touch force of 0, which + // we will reflect/propagate as a 0 pressure, but there is no clear + // alternative, as we don't want to wait for a touchedMoved before + // sending a touch press event to Qt, just to have a valid pressure. + touchPoint.pressure = uiTouch.force / uiTouch.maximumPossibleForce; + } else { + // We don't claim that our touch device supports QTouchDevice::Pressure, + // but fill in a meaningfull value in case clients use it anyways. + touchPoint.pressure = (state == Qt::TouchPointReleased) ? 0.0 : 1.0; + } } } } -- (void) sendTouchEventWithTimestamp:(ulong)timeStamp +- (void)sendTouchEventWithTimestamp:(ulong)timeStamp { // Send touch event synchronously QIOSIntegration *iosIntegration = QIOSIntegration::instance(); diff --git a/src/plugins/platforms/kms/kms.json b/src/plugins/platforms/kms/kms.json deleted file mode 100644 index be662226ae..0000000000 --- a/src/plugins/platforms/kms/kms.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "Keys": [ "kms" ] -} diff --git a/src/plugins/platforms/kms/kms.pro b/src/plugins/platforms/kms/kms.pro deleted file mode 100644 index baa8778153..0000000000 --- a/src/plugins/platforms/kms/kms.pro +++ /dev/null @@ -1,37 +0,0 @@ -TARGET = qkms - -PLUGIN_TYPE = platforms -PLUGIN_CLASS_NAME = QKmsIntegrationPlugin -!equals(TARGET, $$QT_DEFAULT_QPA_PLUGIN): PLUGIN_EXTENDS = - -load(qt_plugin) - -QT += core-private gui-private platformsupport-private -qtHaveModule(opengl):QT += opengl-private - -DEFINES += MESA_EGL_NO_X11_HEADERS __GBM__ - -CONFIG += link_pkgconfig egl qpa/genericunixfontdatabase - -PKGCONFIG += libdrm libudev egl gbm glesv2 - -SOURCES = main.cpp \ - qkmsintegration.cpp \ - qkmsscreen.cpp \ - qkmscontext.cpp \ - qkmswindow.cpp \ - qkmscursor.cpp \ - qkmsdevice.cpp \ - qkmsbackingstore.cpp \ - qkmsnativeinterface.cpp - -HEADERS = qkmsintegration.h \ - qkmsscreen.h \ - qkmscontext.h \ - qkmswindow.h \ - qkmscursor.h \ - qkmsdevice.h \ - qkmsbackingstore.h \ - qkmsnativeinterface.h - -OTHER_FILES += \ - kms.json diff --git a/src/plugins/platforms/kms/main.cpp b/src/plugins/platforms/kms/main.cpp deleted file mode 100644 index 565ac7a7d4..0000000000 --- a/src/plugins/platforms/kms/main.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <qpa/qplatformintegrationplugin.h> -#include "qkmsintegration.h" - -QT_BEGIN_NAMESPACE - -class QKmsIntegrationPlugin : public QPlatformIntegrationPlugin -{ - Q_OBJECT - Q_PLUGIN_METADATA(IID QPlatformIntegrationFactoryInterface_iid FILE "kms.json") -public: - QPlatformIntegration *create(const QString&, const QStringList&) Q_DECL_OVERRIDE; -}; - -QPlatformIntegration *QKmsIntegrationPlugin::create(const QString& system, const QStringList& paramList) -{ - Q_UNUSED(paramList); - if (!system.compare(QLatin1String("kms"), Qt::CaseInsensitive)) - return new QKmsIntegration; - - return 0; -} - -QT_END_NAMESPACE - -#include "main.moc" diff --git a/src/plugins/platforms/kms/qkmsbackingstore.cpp b/src/plugins/platforms/kms/qkmsbackingstore.cpp deleted file mode 100644 index 6e5a3f9192..0000000000 --- a/src/plugins/platforms/kms/qkmsbackingstore.cpp +++ /dev/null @@ -1,217 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qkmsbackingstore.h" - -#include <QtGui/QOpenGLContext> -#include <QtGui/QOpenGLShaderProgram> -#include <QtGui/QScreen> - -QT_BEGIN_NAMESPACE - -QKmsBackingStore::QKmsBackingStore(QWindow *window) - : QPlatformBackingStore(window) - , m_context(new QOpenGLContext) - , m_texture(0) - , m_program(0) - , m_initialized(false) -{ - m_context->setFormat(window->requestedFormat()); - m_context->setScreen(window->screen()); - m_context->create(); -} - -QKmsBackingStore::~QKmsBackingStore() -{ - delete m_program; - if (m_texture) - glDeleteTextures(1, &m_texture); - - delete m_context; -} - -QPaintDevice *QKmsBackingStore::paintDevice() -{ - return &m_image; -} - -void QKmsBackingStore::beginPaint(const QRegion &rgn) -{ - m_dirty |= rgn; -} - -void QKmsBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoint &offset) -{ - Q_UNUSED(region) - Q_UNUSED(offset) - - m_context->makeCurrent(window); - - if (!m_initialized) { - initializeOpenGLFunctions(); - m_initialized = true; - } - - if (!m_program) { - static const char *textureVertexProgram = - "attribute highp vec2 vertexCoordEntry;\n" - "attribute highp vec2 textureCoordEntry;\n" - "varying highp vec2 textureCoord;\n" - "void main() {\n" - " textureCoord = textureCoordEntry;\n" - " gl_Position = vec4(vertexCoordEntry, 0.0, 1.0);\n" - "}\n"; - - static const char *textureFragmentProgram = - "uniform sampler2D texture;\n" - "varying highp vec2 textureCoord;\n" - "void main() {\n" - " gl_FragColor = texture2D(texture, textureCoord).bgra;\n" - "}\n"; - - m_program = new QOpenGLShaderProgram; - - m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, textureVertexProgram); - m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, textureFragmentProgram); - m_program->bindAttributeLocation("vertexCoordEntry", 0); - m_program->bindAttributeLocation("textureCoordEntry", 1); - m_program->link(); - } - - m_program->bind(); - - QRectF r = window->geometry(); - QRectF sr = window->screen()->geometry(); - - GLfloat x1 = (r.left() / sr.width()) * 2 - 1; - GLfloat x2 = (r.right() / sr.width()) * 2 - 1; - GLfloat y1 = -1 * ((r.top() / sr.height()) * 2 - 1); - GLfloat y2 = -1 * ((r.bottom() / sr.height()) * 2 - 1); - - const GLfloat vertexCoordinates[] = { - x1, y1, - x2, y1, - x2, y2, - x1, y2 - }; - - const GLfloat textureCoordinates[] = { - 0, 0, - 1, 0, - 1, 1, - 0, 1 - }; - - glEnableVertexAttribArray(0); - glEnableVertexAttribArray(1); - - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, vertexCoordinates); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, textureCoordinates); - - glBindTexture(GL_TEXTURE_2D, m_texture); - - if (!m_dirty.isNull()) { - QRect imageRect = m_image.rect(); - QRegion fixed; - Q_FOREACH (const QRect &rect, m_dirty.rects()) { - // intersect with image rect to be sure - QRect r = imageRect & rect; - // if the rect is wide enough it's cheaper to just - // extend it instead of doing an image copy - if (r.width() >= imageRect.width() / 2) { - r.setX(0); - r.setWidth(imageRect.width()); - } - fixed |= r; - } - - Q_FOREACH (const QRect &rect, fixed.rects()) { - // if the sub-rect is full-width we can pass the image data directly to - // OpenGL instead of copying, since there's no gap between scanlines - if (rect.width() == imageRect.width()) { - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, rect.y(), - rect.width(), rect.height(), - GL_RGBA, GL_UNSIGNED_BYTE, - m_image.constScanLine(rect.y())); - } else { - glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x(), rect.y(), - rect.width(), rect.height(), - GL_RGBA, GL_UNSIGNED_BYTE, - m_image.copy(rect).constBits()); - } - } - - m_dirty = QRegion(); - } - - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - - m_program->release(); - glBindTexture(GL_TEXTURE_2D, 0); - glDisableVertexAttribArray(0); - glDisableVertexAttribArray(1); - - m_context->swapBuffers(window); - - m_context->doneCurrent(); -} - -void QKmsBackingStore::resize(const QSize &size, const QRegion &staticContents) -{ - Q_UNUSED(staticContents) - - m_image = QImage(size, QImage::Format_RGB32); - - m_context->makeCurrent(window()); - - if (!m_initialized) { - initializeOpenGLFunctions(); - m_initialized = true; - } - - if (m_texture) - glDeleteTextures(1, &m_texture); - - glGenTextures(1, &m_texture); - glBindTexture(GL_TEXTURE_2D, m_texture); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width(), size.height(), - 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - -} - -QT_END_NAMESPACE diff --git a/src/plugins/platforms/kms/qkmsbackingstore.h b/src/plugins/platforms/kms/qkmsbackingstore.h deleted file mode 100644 index a34b10d3d9..0000000000 --- a/src/plugins/platforms/kms/qkmsbackingstore.h +++ /dev/null @@ -1,72 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QBACKINGSTORE_KMS_H -#define QBACKINGSTORE_KMS_H - -#include <qpa/qplatformbackingstore.h> -#include <QtGui/QOpenGLFunctions> -#include <QImage> - -QT_BEGIN_NAMESPACE - -class QOpenGLContext; -class QOpenGLShaderProgram; - -class QKmsBackingStore : public QPlatformBackingStore, public QOpenGLFunctions -{ -public: - QKmsBackingStore(QWindow *window); - ~QKmsBackingStore(); - - QPaintDevice *paintDevice() Q_DECL_OVERRIDE; - - void beginPaint(const QRegion &) Q_DECL_OVERRIDE; - - void flush(QWindow *window, const QRegion ®ion, const QPoint &offset) Q_DECL_OVERRIDE; - void resize(const QSize &size, const QRegion &staticContents) Q_DECL_OVERRIDE; - - QImage toImage() const Q_DECL_OVERRIDE { return m_image; } - -private: - QOpenGLContext *m_context; - QImage m_image; - uint m_texture; - QOpenGLShaderProgram *m_program; - QRegion m_dirty; - bool m_initialized; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/plugins/platforms/kms/qkmscontext.cpp b/src/plugins/platforms/kms/qkmscontext.cpp deleted file mode 100644 index e00835fbac..0000000000 --- a/src/plugins/platforms/kms/qkmscontext.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qkmsscreen.h" -#include "qkmsdevice.h" -#include "qkmscontext.h" -#include "qkmswindow.h" -#include "qkmsintegration.h" - -#include <QtGui/QOpenGLContext> -#include <QtPlatformSupport/private/qeglconvenience_p.h> - -QT_BEGIN_NAMESPACE - -QKmsContext::QKmsContext(QOpenGLContext *context, QKmsDevice *device) - : m_device(device) -{ - EGLDisplay display = m_device->eglDisplay(); - EGLConfig config = q_configFromGLFormat(display, QKmsScreen::tweakFormat(context->format())); - m_format = q_glFormatFromConfig(display, config); - - //Initialize EGLContext - static EGLint contextAttribs[] = { - EGL_CONTEXT_CLIENT_VERSION, context->format().majorVersion(), - EGL_NONE - }; - - eglBindAPI(EGL_OPENGL_ES_API); - - EGLContext share = EGL_NO_CONTEXT; - if (context->shareContext()) - share = static_cast<QKmsContext *>(context->shareContext()->handle())->eglContext(); - - m_eglContext = eglCreateContext(display, config, share, contextAttribs); - if (m_eglContext == EGL_NO_CONTEXT) { - qWarning("QKmsContext::QKmsContext(): eglError: %x, this: %p", - eglGetError(), this); - m_eglContext = 0; - } -} - -bool QKmsContext::isValid() const -{ - return m_eglContext != EGL_NO_CONTEXT; -} - -bool QKmsContext::makeCurrent(QPlatformSurface *surface) -{ - Q_ASSERT(surface->surface()->supportsOpenGL()); - - EGLDisplay display = m_device->eglDisplay(); - EGLSurface eglSurface; - - if (surface->surface()->surfaceClass() == QSurface::Window) { - QPlatformWindow *window = static_cast<QPlatformWindow *>(surface); - QKmsScreen *screen = static_cast<QKmsScreen *>(QPlatformScreen::platformScreenForWindow(window->window())); - eglSurface = screen->eglSurface(); - screen->waitForPageFlipComplete(); - } else { - eglSurface = static_cast<QKmsOffscreenWindow *>(surface)->surface(); - } - - bool ok = eglMakeCurrent(display, eglSurface, eglSurface, m_eglContext); - if (!ok) - qWarning("QKmsContext::makeCurrent(): eglError: %x, this: %p", - eglGetError(), this); - - return true; -} - -void QKmsContext::doneCurrent() -{ - bool ok = eglMakeCurrent(m_device->eglDisplay(), EGL_NO_SURFACE, EGL_NO_SURFACE, - EGL_NO_CONTEXT); - if (!ok) - qWarning("QKmsContext::doneCurrent(): eglError: %x, this: %p", - eglGetError(), this); - -} - -void QKmsContext::swapBuffers(QPlatformSurface *surface) -{ - //Cast context to a window surface and get the screen the context - //is on and call swapBuffers on that screen. - QPlatformWindow *window = static_cast<QPlatformWindow *>(surface); - QKmsScreen *screen = static_cast<QKmsScreen *> (QPlatformScreen::platformScreenForWindow(window->window())); - screen->swapBuffers(); -} - -void (*QKmsContext::getProcAddress(const QByteArray &procName)) () -{ - return eglGetProcAddress(procName.data()); -} - - -EGLContext QKmsContext::eglContext() const -{ - return m_eglContext; -} - -QSurfaceFormat QKmsContext::format() const -{ - return m_format; -} - -QT_END_NAMESPACE diff --git a/src/plugins/platforms/kms/qkmscontext.h b/src/plugins/platforms/kms/qkmscontext.h deleted file mode 100644 index 59cf9b1e34..0000000000 --- a/src/plugins/platforms/kms/qkmscontext.h +++ /dev/null @@ -1,71 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QKMSCONTEXT_H -#define QKMSCONTEXT_H - -#include <qpa/qplatformopenglcontext.h> - -#define EGL_EGLEXT_PROTOTYPES 1 -#include <EGL/egl.h> - -QT_BEGIN_NAMESPACE - -class QKmsDevice; - -class QKmsContext : public QPlatformOpenGLContext -{ -public: - QKmsContext(QOpenGLContext *context, QKmsDevice *device); - - bool makeCurrent(QPlatformSurface *surface) Q_DECL_OVERRIDE; - void doneCurrent() Q_DECL_OVERRIDE; - void swapBuffers(QPlatformSurface *surface) Q_DECL_OVERRIDE; - void (*getProcAddress(const QByteArray &procName)) () Q_DECL_OVERRIDE; - - bool isValid() const Q_DECL_OVERRIDE; - - QSurfaceFormat format() const Q_DECL_OVERRIDE; - - EGLContext eglContext() const; - -private: - EGLContext m_eglContext; - QSurfaceFormat m_format; - - QKmsDevice *m_device; -}; - -QT_END_NAMESPACE - -#endif // QKMSCONTEXT_H diff --git a/src/plugins/platforms/kms/qkmscursor.cpp b/src/plugins/platforms/kms/qkmscursor.cpp deleted file mode 100644 index 44212cd3c8..0000000000 --- a/src/plugins/platforms/kms/qkmscursor.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -//#include <QDebug> -#include "qkmscursor.h" -#include "qkmsscreen.h" -#include "qkmsdevice.h" - -QT_BEGIN_NAMESPACE - -#ifndef DRM_CAP_CURSOR_WIDTH -#define DRM_CAP_CURSOR_WIDTH 0x8 -#endif - -#ifndef DRM_CAP_CURSOR_HEIGHT -#define DRM_CAP_CURSOR_HEIGHT 0x9 -#endif - -QKmsCursor::QKmsCursor(QKmsScreen *screen) - : m_screen(screen), - m_graphicsBufferManager(screen->device()->gbmDevice()), - m_cursorImage(new QPlatformCursorImage(0, 0, 0, 0, 0, 0)), - m_moved(false), - m_cursorSize(64, 64) -{ - uint64_t value = 0; - if (!drmGetCap(m_screen->device()->fd(), DRM_CAP_CURSOR_WIDTH, &value)) - m_cursorSize.setWidth(value); - if (!drmGetCap(m_screen->device()->fd(), DRM_CAP_CURSOR_HEIGHT, &value)) - m_cursorSize.setHeight(value); - - m_cursorBufferObject = gbm_bo_create(m_graphicsBufferManager, m_cursorSize.width(), m_cursorSize.height(), - GBM_FORMAT_ARGB8888, GBM_BO_USE_CURSOR_64X64 | GBM_BO_USE_WRITE); -} - -QKmsCursor::~QKmsCursor() -{ - drmModeSetCursor(m_screen->device()->fd(), m_screen->crtcId(), 0, 0, 0); - gbm_bo_destroy(m_cursorBufferObject); -} - -void QKmsCursor::pointerEvent(const QMouseEvent &event) -{ - m_moved = true; - int status = drmModeMoveCursor(m_screen->device()->fd(), - m_screen->crtcId(), - event.globalX(), - event.globalY()); - if (status) { - qWarning("failed to move cursor: %d", status); - } -} - -void QKmsCursor::changeCursor(QCursor *windowCursor, QWindow *window) -{ - Q_UNUSED(window) - - if (!m_moved) - drmModeMoveCursor(m_screen->device()->fd(), m_screen->crtcId(), 0, 0); - - const Qt::CursorShape newShape = windowCursor ? windowCursor->shape() : Qt::ArrowCursor; - if (newShape != Qt::BitmapCursor) { - m_cursorImage->set(newShape); - } else { - m_cursorImage->set(windowCursor->pixmap().toImage(), - windowCursor->hotSpot().x(), - windowCursor->hotSpot().y()); - } - - if (m_cursorImage->image()->width() > m_cursorSize.width() || m_cursorImage->image()->width() > m_cursorSize.height()) - qWarning("cursor larger than %dx%d, cursor truncated", m_cursorSize.width(), m_cursorSize.height()); - - QImage cursorImage = m_cursorImage->image()->convertToFormat(QImage::Format_ARGB32) - .copy(0, 0, m_cursorSize.width(), m_cursorSize.height()); - gbm_bo_write(m_cursorBufferObject, cursorImage.constBits(), cursorImage.byteCount()); - - quint32 handle = gbm_bo_get_handle(m_cursorBufferObject).u32; - int status = drmModeSetCursor(m_screen->device()->fd(), - m_screen->crtcId(), handle, - m_cursorSize.width(), m_cursorSize.height()); - - if (status) { - qWarning("failed to set cursor: %d", status); - } -} - -QT_END_NAMESPACE diff --git a/src/plugins/platforms/kms/qkmscursor.h b/src/plugins/platforms/kms/qkmscursor.h deleted file mode 100644 index 9aadf407c0..0000000000 --- a/src/plugins/platforms/kms/qkmscursor.h +++ /dev/null @@ -1,66 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QKMSCURSOR_H -#define QKMSCURSOR_H - -#include <qpa/qplatformcursor.h> - -struct gbm_device; -struct gbm_bo; - -QT_BEGIN_NAMESPACE - -class QKmsScreen; - -class QKmsCursor : public QPlatformCursor -{ -public: - QKmsCursor(QKmsScreen *screen); - ~QKmsCursor(); - - void pointerEvent(const QMouseEvent &event) Q_DECL_OVERRIDE; - void changeCursor(QCursor *windowCursor, QWindow *window) Q_DECL_OVERRIDE; - -private: - QKmsScreen *m_screen; - gbm_device *m_graphicsBufferManager; - gbm_bo *m_cursorBufferObject; - QPlatformCursorImage *m_cursorImage; - bool m_moved; - QSize m_cursorSize; -}; - -QT_END_NAMESPACE - -#endif // QKMSCURSOR_H diff --git a/src/plugins/platforms/kms/qkmsdevice.cpp b/src/plugins/platforms/kms/qkmsdevice.cpp deleted file mode 100644 index 74fa59c16a..0000000000 --- a/src/plugins/platforms/kms/qkmsdevice.cpp +++ /dev/null @@ -1,118 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -//#include <QDebug> -#include "qkmsscreen.h" -#include "qkmsdevice.h" - -#include "qkmsintegration.h" - -#include <QtCore/QSocketNotifier> -#include <QtCore/private/qcore_unix_p.h> - -QT_BEGIN_NAMESPACE - -QKmsDevice::QKmsDevice(const QString &path, QKmsIntegration *parent) : - QObject(0), m_integration(parent) -{ - m_fd = QT_OPEN(path.toLatin1().constData(), O_RDWR); - if (m_fd < 0) { - qWarning("Could not open %s.", path.toLatin1().constData()); - qFatal("No DRM display device"); - } - - m_graphicsBufferManager = gbm_create_device(m_fd); - m_eglDisplay = eglGetDisplay(m_graphicsBufferManager); - - if (m_eglDisplay == EGL_NO_DISPLAY) { - qWarning("Could not open EGL display"); - qFatal("EGL error"); - } - - EGLint major; - EGLint minor; - if (!eglInitialize(m_eglDisplay, &major, &minor)) { - qWarning("Could not initialize EGL display"); - qFatal("EGL error"); - } - - createScreens(); - -// QSocketNotifier *notifier = new QSocketNotifier(m_fd, QSocketNotifier::Read, this); -// connect(notifier, SIGNAL(activated(int)), this, SLOT(handlePageFlipCompleted())); -} - -QKmsDevice::~QKmsDevice() -{ -} - -void QKmsDevice::createScreens() -{ - drmModeRes *resources = drmModeGetResources(m_fd); - if (!resources) - qFatal("drmModeGetResources failed"); - - //Iterate connectors and create screens on each one active - for (int i = 0; i < resources->count_connectors; i++) { - drmModeConnector *connector = 0; - connector = drmModeGetConnector(m_fd, resources->connectors[i]); - if (connector && connector->connection == DRM_MODE_CONNECTED) { - m_integration->addScreen(new QKmsScreen(this, resources, connector)); - } - drmModeFreeConnector(connector); - } - drmModeFreeResources(resources); -} - -void QKmsDevice::handlePageFlipCompleted() -{ - drmEventContext eventContext; - - memset(&eventContext, 0, sizeof eventContext); - eventContext.version = DRM_EVENT_CONTEXT_VERSION; - eventContext.page_flip_handler = QKmsDevice::pageFlipHandler; - drmHandleEvent(m_fd, &eventContext); - -} - -void QKmsDevice::pageFlipHandler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void *data) -{ - Q_UNUSED(fd) - Q_UNUSED(frame) - Q_UNUSED(sec) - Q_UNUSED(usec) - - QKmsScreen *screen = static_cast<QKmsScreen *>(data); - screen->handlePageFlipped(); -} - -QT_END_NAMESPACE diff --git a/src/plugins/platforms/kms/qkmsdevice.h b/src/plugins/platforms/kms/qkmsdevice.h deleted file mode 100644 index d5e33cb8c7..0000000000 --- a/src/plugins/platforms/kms/qkmsdevice.h +++ /dev/null @@ -1,81 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QKMSDEVICE_H -#define QKMSDEVICE_H - -#include <stddef.h> - -extern "C" { -#include <gbm.h> -} -#include <EGL/egl.h> - -#include <QObject> - -struct gbm_device; - -QT_BEGIN_NAMESPACE - -class QKmsIntegration; - -class QKmsDevice : public QObject -{ - Q_OBJECT -public: - explicit QKmsDevice(const QString &path, QKmsIntegration *parent); - ~QKmsDevice(); - - EGLDisplay eglDisplay() { return m_eglDisplay; } - gbm_device *gbmDevice() { return m_graphicsBufferManager; } - int fd() const { return m_fd; } - - static void pageFlipHandler(int fd, unsigned int frame, unsigned int sec, - unsigned int usec, void *data); - -public slots: - void handlePageFlipCompleted(); -private: - void createScreens(); - - QKmsIntegration *m_integration; - - EGLDisplay m_eglDisplay; - EGLContext m_eglContext; - gbm_device *m_graphicsBufferManager; - int m_fd; -}; - -QT_END_NAMESPACE - -#endif // QKMSDEVICE_H diff --git a/src/plugins/platforms/kms/qkmsintegration.cpp b/src/plugins/platforms/kms/qkmsintegration.cpp deleted file mode 100644 index f48c868ae5..0000000000 --- a/src/plugins/platforms/kms/qkmsintegration.cpp +++ /dev/null @@ -1,204 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qkmsintegration.h" -#include "qkmsdevice.h" -#include "qkmsscreen.h" -#include "qkmswindow.h" -#include "qkmsbackingstore.h" -#include "qkmscontext.h" -#include "qkmsnativeinterface.h" - -#if !defined(QT_NO_EVDEV) -#include <QtPlatformSupport/private/qevdevmousemanager_p.h> -#include <QtPlatformSupport/private/qevdevkeyboardmanager_p.h> -#include <QtPlatformSupport/private/qevdevtouch_p.h> -#endif - -#include <QtPlatformSupport/private/qgenericunixeventdispatcher_p.h> -#include <QtPlatformSupport/private/qgenericunixfontdatabase_p.h> -#include <QtPlatformSupport/private/qfbvthandler_p.h> -#include <QtPlatformSupport/private/qeglconvenience_p.h> - -#include <QtGui/private/qguiapplication_p.h> -#include <QtGui/QOpenGLContext> -#include <QtGui/QScreen> -#include <QtGui/QOffscreenSurface> -#include <qpa/qplatformoffscreensurface.h> - -QT_BEGIN_NAMESPACE - -QKmsIntegration::QKmsIntegration() - : QPlatformIntegration(), - m_fontDatabase(new QGenericUnixFontDatabase()), - m_nativeInterface(new QKmsNativeInterface), - m_vtHandler(0), - m_deviceDiscovery(0) -{ -} - -QKmsIntegration::~QKmsIntegration() -{ - delete m_deviceDiscovery; - foreach (QKmsDevice *device, m_devices) { - delete device; - } - foreach (QPlatformScreen *screen, m_screens) { - destroyScreen(screen); - } - delete m_fontDatabase; - delete m_vtHandler; -} - -void QKmsIntegration::initialize() -{ - qputenv("EGL_PLATFORM", "drm"); - m_vtHandler = new QFbVtHandler; - - m_deviceDiscovery = QDeviceDiscovery::create(QDeviceDiscovery::Device_DRM | QDeviceDiscovery::Device_DRM_PrimaryGPU, 0); - if (m_deviceDiscovery) { - QStringList devices = m_deviceDiscovery->scanConnectedDevices(); - foreach (const QString &device, devices) - addDevice(device); - - connect(m_deviceDiscovery, SIGNAL(deviceDetected(QString)), this, SLOT(addDevice(QString))); - connect(m_deviceDiscovery, SIGNAL(deviceRemoved(QString)), this, SLOT(removeDevice(QString))); - } - -#if !defined(QT_NO_EVDEV) - new QEvdevKeyboardManager(QLatin1String("EvdevKeyboard"), QString() /* spec */, this); - new QEvdevMouseManager(QLatin1String("EvdevMouse"), QString() /* spec */, this); - new QEvdevTouchScreenHandlerThread(QString() /* spec */, this); -#endif -} - -void QKmsIntegration::addDevice(const QString &deviceNode) -{ - m_devices.append(new QKmsDevice(deviceNode, this)); -} - -void QKmsIntegration::removeDevice(const QString &deviceNode) -{ - // TODO: support hot-plugging some day? - Q_UNUSED(deviceNode); -} - -bool QKmsIntegration::hasCapability(QPlatformIntegration::Capability cap) const -{ - switch (cap) { - case ThreadedPixmaps: return true; - case OpenGL: return true; - case ThreadedOpenGL: return false; - case RasterGLSurface: return true; - default: return QPlatformIntegration::hasCapability(cap); - } -} - -QPlatformOpenGLContext *QKmsIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const -{ - QKmsScreen *screen = static_cast<QKmsScreen *>(context->screen()->handle()); - return new QKmsContext(context, screen->device()); -} - -QPlatformWindow *QKmsIntegration::createPlatformWindow(QWindow *window) const -{ - QKmsWindow *w = new QKmsWindow(window); - w->requestActivateWindow(); - return w; -} - -QPlatformBackingStore *QKmsIntegration::createPlatformBackingStore(QWindow *window) const -{ - return new QKmsBackingStore(window); -} - -// Neither a pbuffer nor a hidden QWindow is suitable. Just use an additional, small gbm surface. -QKmsOffscreenWindow::QKmsOffscreenWindow(EGLDisplay display, const QSurfaceFormat &format, QOffscreenSurface *offscreenSurface) - : QPlatformOffscreenSurface(offscreenSurface) - , m_format(format) - , m_display(display) - , m_surface(EGL_NO_SURFACE) - , m_window(0) -{ - QKmsScreen *screen = static_cast<QKmsScreen *>(offscreenSurface->screen()->handle()); - m_window = gbm_surface_create(screen->device()->gbmDevice(), - 10, 10, - GBM_FORMAT_XRGB8888, - GBM_BO_USE_RENDERING); - if (!m_window) { - qWarning("QKmsOffscreenWindow: Failed to create native window"); - return; - } - - EGLConfig config = q_configFromGLFormat(m_display, m_format); - m_surface = eglCreateWindowSurface(m_display, config, m_window, 0); - if (m_surface != EGL_NO_SURFACE) - m_format = q_glFormatFromConfig(m_display, config); -} - -QKmsOffscreenWindow::~QKmsOffscreenWindow() -{ - if (m_surface != EGL_NO_SURFACE) - eglDestroySurface(m_display, m_surface); - if (m_window) - gbm_surface_destroy((gbm_surface *) m_window); -} - -QPlatformOffscreenSurface *QKmsIntegration::createPlatformOffscreenSurface(QOffscreenSurface *surface) const -{ - QKmsScreen *screen = static_cast<QKmsScreen *>(surface->screen()->handle()); - return new QKmsOffscreenWindow(screen->device()->eglDisplay(), QKmsScreen::tweakFormat(surface->format()), surface); -} - -QPlatformFontDatabase *QKmsIntegration::fontDatabase() const -{ - return m_fontDatabase; -} - -void QKmsIntegration::addScreen(QKmsScreen *screen) -{ - m_screens.append(screen); - screenAdded(screen); -} - -QAbstractEventDispatcher *QKmsIntegration::createEventDispatcher() const -{ - return createUnixEventDispatcher(); -} - -QPlatformNativeInterface *QKmsIntegration::nativeInterface() const -{ - return m_nativeInterface; -} - -QT_END_NAMESPACE diff --git a/src/plugins/platforms/kms/qkmsintegration.h b/src/plugins/platforms/kms/qkmsintegration.h deleted file mode 100644 index bcf9ac7296..0000000000 --- a/src/plugins/platforms/kms/qkmsintegration.h +++ /dev/null @@ -1,108 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QPLATFORMINTEGRATION_KMS_H -#define QPLATFORMINTEGRATION_KMS_H - -#include <qpa/qplatformintegration.h> -#include <qpa/qplatformnativeinterface.h> -#include <qpa/qplatformoffscreensurface.h> -#include <QtPlatformSupport/private/qdevicediscovery_p.h> -#include <EGL/egl.h> - -QT_BEGIN_NAMESPACE - -class QKmsScreen; -class QKmsDevice; -class QFbVtHandler; - -class QKmsOffscreenWindow : public QPlatformOffscreenSurface -{ -public: - QKmsOffscreenWindow(EGLDisplay display, const QSurfaceFormat &format, QOffscreenSurface *offscreenSurface); - ~QKmsOffscreenWindow(); - - QSurfaceFormat format() const Q_DECL_OVERRIDE { return m_format; } - bool isValid() const Q_DECL_OVERRIDE { return m_surface != EGL_NO_SURFACE; } - - EGLSurface surface() const { return m_surface; } - -private: - QSurfaceFormat m_format; - EGLDisplay m_display; - EGLSurface m_surface; - EGLNativeWindowType m_window; -}; - -class QKmsIntegration : public QObject, public QPlatformIntegration -{ - Q_OBJECT - -public: - QKmsIntegration(); - ~QKmsIntegration(); - - void initialize() Q_DECL_OVERRIDE; - bool hasCapability(QPlatformIntegration::Capability cap) const Q_DECL_OVERRIDE; - - QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const Q_DECL_OVERRIDE; - QPlatformWindow *createPlatformWindow(QWindow *window) const Q_DECL_OVERRIDE; - QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const Q_DECL_OVERRIDE; - QPlatformOffscreenSurface *createPlatformOffscreenSurface(QOffscreenSurface *surface) const Q_DECL_OVERRIDE; - - QPlatformFontDatabase *fontDatabase() const Q_DECL_OVERRIDE; - QAbstractEventDispatcher *createEventDispatcher() const Q_DECL_OVERRIDE; - - QPlatformNativeInterface *nativeInterface() const Q_DECL_OVERRIDE; - - void addScreen(QKmsScreen *screen); - QObject *createDevice(const char *); - -private slots: - void addDevice(const QString &deviceNode); - void removeDevice(const QString &deviceNode); - -private: - QStringList findDrmDevices(); - - QList<QPlatformScreen *> m_screens; - QList<QKmsDevice *> m_devices; - QPlatformFontDatabase *m_fontDatabase; - QPlatformNativeInterface *m_nativeInterface; - QFbVtHandler *m_vtHandler; - QDeviceDiscovery *m_deviceDiscovery; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/plugins/platforms/kms/qkmsnativeinterface.cpp b/src/plugins/platforms/kms/qkmsnativeinterface.cpp deleted file mode 100644 index 1538a7f8c3..0000000000 --- a/src/plugins/platforms/kms/qkmsnativeinterface.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <private/qguiapplication_p.h> -#include "qkmsnativeinterface.h" -#include "qkmsdevice.h" - -#include "qscreen.h" -#include "qkmscontext.h" -#include <QOpenGLContext> - -class QKmsResourceMap : public QMap<QByteArray, QKmsNativeInterface::ResourceType> -{ -public: - QKmsResourceMap() - :QMap<QByteArray, QKmsNativeInterface::ResourceType>() - { - insert("egldisplay", QKmsNativeInterface::EglDisplay); - insert("eglcontext", QKmsNativeInterface::EglContext); - } -}; - -Q_GLOBAL_STATIC(QKmsResourceMap, qKmsResourceMap) - -void *QKmsNativeInterface::nativeResourceForIntegration(const QByteArray &resourceString) -{ - QByteArray lowerCaseResource = resourceString.toLower(); - ResourceType resource = qKmsResourceMap()->value(lowerCaseResource); - void *result = 0; - switch (resource) { - case EglDisplay: - result = eglDisplay(); - break; - default: - result = 0; - } - return result; - -} -void *QKmsNativeInterface::nativeResourceForWindow(const QByteArray &resourceString, QWindow *window) -{ - QByteArray lowerCaseResource = resourceString.toLower(); - ResourceType resource = qKmsResourceMap()->value(lowerCaseResource); - void *result = 0; - switch (resource) { - case EglDisplay: - result = eglDisplayForWindow(window); - break; - case EglContext: - result = eglContextForWindow(window); - break; - default: - result = 0; - } - return result; -} - -QPlatformNativeInterface::NativeResourceForContextFunction QKmsNativeInterface::nativeResourceFunctionForContext(const QByteArray &resource) -{ - QByteArray lowerCaseResource = resource.toLower(); - if (lowerCaseResource == "get_egl_context") { - return eglContextForContext; - } - return 0; -} - -void *QKmsNativeInterface::eglDisplay() -{ - //QKmsIntegration *integration = static_cast<QKmsIntegration *>(QGuiApplicationPrivate::platformIntegration()); - QKmsScreen *screen = static_cast<QKmsScreen *>(QGuiApplication::primaryScreen()->handle()); - if (!screen || !screen->device()) - return 0; - return screen->device()->eglDisplay(); -} - -void *QKmsNativeInterface::eglDisplayForWindow(QWindow *window) -{ - QKmsScreen *screen = qPlatformScreenForWindow(window); - if (!screen) - return 0; - QKmsDevice *device = screen->device(); - if (!device) - return 0; - return device->eglDisplay(); -} - -void *QKmsNativeInterface::eglContextForWindow(QWindow *) -{ - return 0; -} - -QKmsScreen *QKmsNativeInterface::qPlatformScreenForWindow(QWindow *window) -{ - QScreen *screen = window ? window->screen() : QGuiApplication::primaryScreen(); - return static_cast<QKmsScreen *>(screen->handle()); -} - -void *QKmsNativeInterface::eglContextForContext(QOpenGLContext *context) -{ - Q_ASSERT(context); - - QKmsContext *eglPlatformContext = static_cast<QKmsContext *>(context->handle()); - - return eglPlatformContext->eglContext(); -} diff --git a/src/plugins/platforms/kms/qkmsnativeinterface.h b/src/plugins/platforms/kms/qkmsnativeinterface.h deleted file mode 100644 index 56879d0a3a..0000000000 --- a/src/plugins/platforms/kms/qkmsnativeinterface.h +++ /dev/null @@ -1,64 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QKMSNATIVEINTERFACE_H -#define QKMSNATIVEINTERFACE_H - -#include "qkmsscreen.h" - -#include <qpa/qplatformnativeinterface.h> - -class QKmsNativeInterface : public QPlatformNativeInterface -{ -public: - enum ResourceType { - EglDisplay, - EglContext - }; - - void *nativeResourceForIntegration(const QByteArray &resource) Q_DECL_OVERRIDE; - void *nativeResourceForWindow(const QByteArray &resourceString, QWindow *window) Q_DECL_OVERRIDE; - - NativeResourceForContextFunction nativeResourceFunctionForContext(const QByteArray &resource) Q_DECL_OVERRIDE; - - void *eglDisplay(); - void *eglDisplayForWindow(QWindow *window); - void *eglContextForWindow(QWindow *window); - static void *eglContextForContext(QOpenGLContext *context); - -private: - static QKmsScreen *qPlatformScreenForWindow(QWindow *window); -}; - - -#endif // QKMSNATIVEINTERFACE_H diff --git a/src/plugins/platforms/kms/qkmsscreen.cpp b/src/plugins/platforms/kms/qkmsscreen.cpp deleted file mode 100644 index 6392b99cd5..0000000000 --- a/src/plugins/platforms/kms/qkmsscreen.cpp +++ /dev/null @@ -1,275 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qkmsscreen.h" -#include "qkmscursor.h" -#include "qkmsdevice.h" -#include "qkmscontext.h" - -#include <QtPlatformSupport/private/qeglconvenience_p.h> - -#include <QCoreApplication> -#include <QtDebug> - -QT_BEGIN_NAMESPACE - -Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.kms.screen") - -//Fallback mode (taken from Wayland DRM demo compositor) -static drmModeModeInfo builtin_1024x768 = { - 63500, //clock - 1024, 1072, 1176, 1328, 0, - 768, 771, 775, 798, 0, - 59920, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC, - 0, - "1024x768" -}; - -QKmsScreen::QKmsScreen(QKmsDevice *device, const drmModeRes *resources, const drmModeConnector *connector) - : m_device(device), - m_current_bo(0), - m_next_bo(0), - m_connectorId(connector->connector_id), - m_depth(32), - m_format(QImage::Format_Invalid), - m_eglWindowSurface(EGL_NO_SURFACE), - m_modeSet(false) -{ - m_cursor = new QKmsCursor(this); - initializeScreenMode(resources, connector); -} - -QKmsScreen::~QKmsScreen() -{ - delete m_cursor; - drmModeSetCrtc(m_device->fd(), m_oldCrtc->crtc_id, m_oldCrtc->buffer_id, - m_oldCrtc->x, m_oldCrtc->y, - &m_connectorId, 1, &m_oldCrtc->mode); - drmModeFreeCrtc(m_oldCrtc); - if (m_eglWindowSurface != EGL_NO_SURFACE) - eglDestroySurface(m_device->eglDisplay(), m_eglWindowSurface); - gbm_surface_destroy(m_gbmSurface); -} - -QRect QKmsScreen::geometry() const -{ - return m_geometry; -} - -int QKmsScreen::depth() const -{ - return m_depth; -} - -QImage::Format QKmsScreen::format() const -{ - return m_format; -} - -QSizeF QKmsScreen::physicalSize() const -{ - return m_physicalSize; -} - -QPlatformCursor *QKmsScreen::cursor() const -{ - return m_cursor; -} - -void QKmsScreen::initializeScreenMode(const drmModeRes *resources, const drmModeConnector *connector) -{ - //Determine optimal mode for screen - drmModeModeInfo *mode = 0; - for (int i = 0; i < connector->count_modes; ++i) { - if (connector->modes[i].type & DRM_MODE_TYPE_PREFERRED) { - mode = &connector->modes[i]; - break; - } - } - if (!mode) { - if (connector->count_modes > 0) - mode = &connector->modes[0]; - else - mode = &builtin_1024x768; - } - - drmModeEncoder *encoder = drmModeGetEncoder(m_device->fd(), connector->encoders[0]); - if (encoder == 0) - qFatal("No encoder for connector."); - - int i; - for (i = 0; i < resources->count_crtcs; i++) { - if (encoder->possible_crtcs & (1 << i)) - break; - } - if (i == resources->count_crtcs) - qFatal("No usable crtc for encoder."); - - m_oldCrtc = drmModeGetCrtc(m_device->fd(), encoder->crtc_id); - - m_crtcId = resources->crtcs[i]; - m_mode = *mode; - m_geometry = QRect(0, 0, m_mode.hdisplay, m_mode.vdisplay); - qCDebug(lcQpaScreen) << "kms initialized with geometry" << m_geometry; - m_depth = 32; - m_format = QImage::Format_RGB32; - m_physicalSize = QSizeF(connector->mmWidth, connector->mmHeight); - - m_gbmSurface = gbm_surface_create(m_device->gbmDevice(), - m_mode.hdisplay, m_mode.vdisplay, - GBM_BO_FORMAT_XRGB8888, - GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); - - qCDebug(lcQpaScreen) << "created gbm surface" << m_gbmSurface << m_mode.hdisplay << m_mode.vdisplay; - //Cleanup - drmModeFreeEncoder(encoder); -} - -QSurfaceFormat QKmsScreen::tweakFormat(const QSurfaceFormat &format) -{ - QSurfaceFormat fmt = format; - fmt.setRedBufferSize(8); - fmt.setGreenBufferSize(8); - fmt.setBlueBufferSize(8); - if (fmt.alphaBufferSize() != -1) - fmt.setAlphaBufferSize(8); - return fmt; -} - -void QKmsScreen::initializeWithFormat(const QSurfaceFormat &format) -{ - EGLDisplay display = m_device->eglDisplay(); - EGLConfig config = q_configFromGLFormat(display, tweakFormat(format)); - - m_eglWindowSurface = eglCreateWindowSurface(display, config, (EGLNativeWindowType)m_gbmSurface, NULL); - qCDebug(lcQpaScreen) << "created window surface"; - m_surfaceFormat = q_glFormatFromConfig(display, config); -} - -void QKmsScreen::swapBuffers() -{ - eglSwapBuffers(m_device->eglDisplay(), m_eglWindowSurface); - - m_next_bo = gbm_surface_lock_front_buffer(m_gbmSurface); - if (!m_next_bo) - qFatal("kms: Failed to lock front buffer"); - - performPageFlip(); -} - -void QKmsScreen::performPageFlip() -{ - if (!m_next_bo) - return; - - uint32_t width = gbm_bo_get_width(m_next_bo); - uint32_t height = gbm_bo_get_height(m_next_bo); - uint32_t stride = gbm_bo_get_stride(m_next_bo); - uint32_t handle = gbm_bo_get_handle(m_next_bo).u32; - - uint32_t fb_id; - int ret = drmModeAddFB(m_device->fd(), width, height, 24, 32, - stride, handle, &fb_id); - if (ret) { - qFatal("kms: Failed to create fb: fd %d, w %d, h %d, stride %d, handle %d, ret %d", - m_device->fd(), width, height, stride, handle, ret); - } - - if (!m_modeSet) { - //Set the Mode of the screen. - int ret = drmModeSetCrtc(m_device->fd(), m_crtcId, fb_id, - 0, 0, &m_connectorId, 1, &m_mode); - if (ret) - qFatal("failed to set mode"); - m_modeSet = true; - - // Initialize cursor - - static int hideCursor = qEnvironmentVariableIntValue("QT_QPA_KMS_HIDECURSOR"); - if (!hideCursor) { - QCursor cursor(Qt::ArrowCursor); - m_cursor->changeCursor(&cursor, 0); - } - } - - int pageFlipStatus = drmModePageFlip(m_device->fd(), m_crtcId, - fb_id, - DRM_MODE_PAGE_FLIP_EVENT, this); - if (pageFlipStatus) - { - qWarning("Pageflip status: %d", pageFlipStatus); - gbm_surface_release_buffer(m_gbmSurface, m_next_bo); - m_next_bo = 0; - } -} - -void QKmsScreen::handlePageFlipped() -{ - if (m_current_bo) - gbm_surface_release_buffer(m_gbmSurface, m_current_bo); - - m_current_bo = m_next_bo; - m_next_bo = 0; -} - -QKmsDevice * QKmsScreen::device() const -{ - return m_device; -} - -void QKmsScreen::waitForPageFlipComplete() -{ - while (m_next_bo) { -#if 0 - //Check manually if there is something to be read on the device - //as there are senarios where the signal is not received (starvation) - fd_set fdSet; - timeval timeValue; - int returnValue; - - FD_ZERO(&fdSet); - FD_SET(m_device->fd(), &fdSet); - timeValue.tv_sec = 0; - timeValue.tv_usec = 1000; - - returnValue = select(1, &fdSet, 0, 0, &timeValue); - printf("select returns %d\n", returnValue); -#endif - - m_device->handlePageFlipCompleted(); - } -} - - -QT_END_NAMESPACE diff --git a/src/plugins/platforms/kms/qkmsscreen.h b/src/plugins/platforms/kms/qkmsscreen.h deleted file mode 100644 index c52d0211b3..0000000000 --- a/src/plugins/platforms/kms/qkmsscreen.h +++ /dev/null @@ -1,122 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QKMSSCREEN_H -#define QKMSSCREEN_H - -#include <stddef.h> - -#define EGL_EGLEXT_PROTOTYPES 1 -#define GL_GLEXT_PROTOTYPES 1 - -extern "C" { -#include <gbm.h> -#include <xf86drmMode.h> -#include <xf86drm.h> -} - -#include <EGL/egl.h> -#include <EGL/eglext.h> -#include <QtGui/qopengl.h> -#include <QtGui/qsurfaceformat.h> -#include <QtCore/qloggingcategory.h> - -#include <qpa/qplatformscreen.h> - -QT_BEGIN_NAMESPACE - -Q_DECLARE_LOGGING_CATEGORY(lcQpaScreen) - -class QKmsCursor; -class QKmsDevice; -class QKmsContext; - -class QKmsScreen : public QPlatformScreen -{ -public: - QKmsScreen(QKmsDevice *device, const drmModeRes *resources, const drmModeConnector *connector); - ~QKmsScreen(); - - QRect geometry() const Q_DECL_OVERRIDE; - int depth() const Q_DECL_OVERRIDE; - QImage::Format format() const Q_DECL_OVERRIDE; - QSizeF physicalSize() const Q_DECL_OVERRIDE; - QPlatformCursor *cursor() const Q_DECL_OVERRIDE; - - quint32 crtcId() const { return m_crtcId; } - QKmsDevice *device() const; - - void initializeWithFormat(const QSurfaceFormat &format); - - //Called by context for each screen - void swapBuffers(); - void handlePageFlipped(); - - EGLSurface eglSurface() const { return m_eglWindowSurface; } - - void waitForPageFlipComplete(); - - static QSurfaceFormat tweakFormat(const QSurfaceFormat &format); - - QSurfaceFormat surfaceFormat() const { return m_surfaceFormat; } - -private: - void performPageFlip(); - void initializeScreenMode(const drmModeRes *resources, const drmModeConnector *connector); - - QKmsDevice *m_device; - gbm_bo *m_current_bo; - gbm_bo *m_next_bo; - quint32 m_connectorId; - - quint32 m_crtcId; - drmModeModeInfo m_mode; - QRect m_geometry; - QSizeF m_physicalSize; - int m_depth; - QImage::Format m_format; - - drmModeCrtcPtr m_oldCrtc; - - QKmsCursor *m_cursor; - - gbm_surface *m_gbmSurface; - EGLSurface m_eglWindowSurface; - - bool m_modeSet; - QSurfaceFormat m_surfaceFormat; -}; - -QT_END_NAMESPACE - -#endif // QKMSSCREEN_H diff --git a/src/plugins/platforms/kms/qkmswindow.cpp b/src/plugins/platforms/kms/qkmswindow.cpp deleted file mode 100644 index 3b01dfedca..0000000000 --- a/src/plugins/platforms/kms/qkmswindow.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qkmswindow.h" -#include "qkmsscreen.h" - -#include <qpa/qwindowsysteminterface.h> -#include <qpa/qplatformwindow_p.h> - -QT_BEGIN_NAMESPACE - -QKmsWindow::QKmsWindow(QWindow *window) - : QPlatformWindow(window) -{ - Q_D(QPlatformWindow); - m_screen = QPlatformScreen::platformScreenForWindow(window); - static_cast<QKmsScreen *>(m_screen)->initializeWithFormat(window->requestedFormat()); - setGeometry(d->rect); // rect is set to window->geometry() in base ctor -} - -void QKmsWindow::setGeometry(const QRect &rect) -{ - // All windows must be fullscreen - QRect fullscreenRect = m_screen->availableGeometry(); - if (rect != fullscreenRect) - QWindowSystemInterface::handleGeometryChange(window(), fullscreenRect); - - QPlatformWindow::setGeometry(fullscreenRect); -} - -QSurfaceFormat QKmsWindow::format() const -{ - return static_cast<QKmsScreen *>(m_screen)->surfaceFormat(); -} - -QT_END_NAMESPACE diff --git a/src/plugins/platforms/linuxfb/qlinuxfbintegration.cpp b/src/plugins/platforms/linuxfb/qlinuxfbintegration.cpp index ccf86dafb2..8c8c8a15ea 100644 --- a/src/plugins/platforms/linuxfb/qlinuxfbintegration.cpp +++ b/src/plugins/platforms/linuxfb/qlinuxfbintegration.cpp @@ -46,6 +46,10 @@ #include <QtGui/private/qguiapplication_p.h> #include <qpa/qplatforminputcontextfactory_p.h> +#ifndef QT_NO_LIBINPUT +#include <QtPlatformSupport/private/qlibinputhandler_p.h> +#endif + #if !defined(QT_NO_EVDEV) && (!defined(Q_OS_ANDROID) || defined(Q_OS_ANDROID_NO_SDK)) #include <QtPlatformSupport/private/qevdevmousemanager_p.h> #include <QtPlatformSupport/private/qevdevkeyboardmanager_p.h> @@ -130,6 +134,13 @@ QPlatformServices *QLinuxFbIntegration::services() const void QLinuxFbIntegration::createInputHandlers() { +#ifndef QT_NO_LIBINPUT + if (!qEnvironmentVariableIntValue("QT_QPA_FB_NO_LIBINPUT")) { + new QLibInputHandler(QLatin1String("libinput"), QString()); + return; + } +#endif + #if !defined(QT_NO_EVDEV) && (!defined(Q_OS_ANDROID) || defined(Q_OS_ANDROID_NO_SDK)) new QEvdevKeyboardManager(QLatin1String("EvdevKeyboard"), QString(), this); new QEvdevMouseManager(QLatin1String("EvdevMouse"), QString(), this); diff --git a/src/plugins/platforms/minimalegl/qminimaleglintegration.cpp b/src/plugins/platforms/minimalegl/qminimaleglintegration.cpp index ef96985f1a..1bcb22618e 100644 --- a/src/plugins/platforms/minimalegl/qminimaleglintegration.cpp +++ b/src/plugins/platforms/minimalegl/qminimaleglintegration.cpp @@ -37,7 +37,15 @@ #include "qminimaleglbackingstore.h" #include <QtPlatformSupport/private/qgenericunixfontdatabase_p.h> -#include <QtPlatformSupport/private/qgenericunixeventdispatcher_p.h> + +#if defined(Q_OS_UNIX) +# include <QtPlatformSupport/private/qgenericunixeventdispatcher_p.h> +#elif defined(Q_OS_WINRT) +# include <QtCore/private/qeventdispatcher_winrt_p.h> +# include <QtGui/qpa/qwindowsysteminterface.h> +#elif defined(Q_OS_WIN) +# include <QtPlatformSupport/private/qwindowsguieventdispatcher_p.h> +#endif #include <qpa/qplatformwindow.h> #include <QtGui/QSurfaceFormat> @@ -48,6 +56,29 @@ QT_BEGIN_NAMESPACE +#ifdef Q_OS_WINRT +namespace { +class QWinRTEventDispatcher : public QEventDispatcherWinRT { +public: + QWinRTEventDispatcher() {} + +protected: + bool hasPendingEvents() Q_DECL_OVERRIDE + { + return QEventDispatcherWinRT::hasPendingEvents() || QWindowSystemInterface::windowSystemEventsQueued(); + } + + bool sendPostedEvents(QEventLoop::ProcessEventsFlags flags) + { + bool didProcess = QEventDispatcherWinRT::sendPostedEvents(flags); + if (!(flags & QEventLoop::ExcludeUserInputEvents)) + didProcess |= QWindowSystemInterface::sendWindowSystemEvents(flags); + return didProcess; + } +}; +} // anonymous namespace +#endif // Q_OS_WINRT + QMinimalEglIntegration::QMinimalEglIntegration() : mFontDb(new QGenericUnixFontDatabase()), mScreen(new QMinimalEglScreen(EGL_DEFAULT_DISPLAY)) { @@ -104,7 +135,15 @@ QPlatformFontDatabase *QMinimalEglIntegration::fontDatabase() const QAbstractEventDispatcher *QMinimalEglIntegration::createEventDispatcher() const { +#if defined(Q_OS_UNIX) return createUnixEventDispatcher(); +#elif defined(Q_OS_WINRT) + return new QWinRTEventDispatcher; +#elif defined(Q_OS_WIN) + return new QWindowsGuiEventDispatcher; +#else + return Q_NULLPTR; +#endif } QVariant QMinimalEglIntegration::styleHint(QPlatformIntegration::StyleHint hint) const diff --git a/src/plugins/platforms/mirclient/mirclient.json b/src/plugins/platforms/mirclient/mirclient.json new file mode 100644 index 0000000000..c31558a2f1 --- /dev/null +++ b/src/plugins/platforms/mirclient/mirclient.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "mirclient" ] +} diff --git a/src/plugins/platforms/mirclient/mirclient.pro b/src/plugins/platforms/mirclient/mirclient.pro new file mode 100644 index 0000000000..033ce579b9 --- /dev/null +++ b/src/plugins/platforms/mirclient/mirclient.pro @@ -0,0 +1,47 @@ +TARGET = mirclient +TEMPLATE = lib + +PLUGIN_TYPE = platforms +PLUGIN_CLASS_NAME = MirServerIntegrationPlugin +!equals(TARGET, $$QT_DEFAULT_QPA_PLUGIN): PLUGIN_EXTENDS = - +load(qt_plugin) + +QT += core-private gui-private platformsupport-private dbus + +CONFIG += qpa/genericunixfontdatabase + +DEFINES += MESA_EGL_NO_X11_HEADERS +# CONFIG += c++11 # only enables C++0x +QMAKE_CXXFLAGS += -fvisibility=hidden -fvisibility-inlines-hidden -std=c++11 -Werror -Wall +QMAKE_LFLAGS += -std=c++11 -Wl,-no-undefined + +CONFIG += link_pkgconfig +PKGCONFIG += egl mirclient ubuntu-platform-api + +SOURCES = \ + qmirclientbackingstore.cpp \ + qmirclientclipboard.cpp \ + qmirclientglcontext.cpp \ + qmirclientinput.cpp \ + qmirclientintegration.cpp \ + qmirclientnativeinterface.cpp \ + qmirclientplatformservices.cpp \ + qmirclientplugin.cpp \ + qmirclientscreen.cpp \ + qmirclienttheme.cpp \ + qmirclientwindow.cpp + +HEADERS = \ + qmirclientbackingstore.h \ + qmirclientclipboard.h \ + qmirclientglcontext.h \ + qmirclientinput.h \ + qmirclientintegration.h \ + qmirclientlogging.h \ + qmirclientnativeinterface.h \ + qmirclientorientationchangeevent_p.h \ + qmirclientplatformservices.h \ + qmirclientplugin.h \ + qmirclientscreen.h \ + qmirclienttheme.h \ + qmirclientwindow.h diff --git a/src/plugins/platforms/mirclient/qmirclientbackingstore.cpp b/src/plugins/platforms/mirclient/qmirclientbackingstore.cpp new file mode 100644 index 0000000000..daa0b229ec --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientbackingstore.cpp @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qmirclientbackingstore.h" +#include "qmirclientlogging.h" +#include <QtGui/QOpenGLContext> +#include <QtGui/QOpenGLTexture> +#include <QtGui/QMatrix4x4> +#include <QtGui/private/qopengltextureblitter_p.h> +#include <QtGui/qopenglfunctions.h> + +QMirClientBackingStore::QMirClientBackingStore(QWindow* window) + : QPlatformBackingStore(window) + , mContext(new QOpenGLContext) + , mTexture(new QOpenGLTexture(QOpenGLTexture::Target2D)) + , mBlitter(new QOpenGLTextureBlitter) +{ + mContext->setFormat(window->requestedFormat()); + mContext->setScreen(window->screen()); + mContext->create(); + + window->setSurfaceType(QSurface::OpenGLSurface); +} + +QMirClientBackingStore::~QMirClientBackingStore() +{ +} + +void QMirClientBackingStore::flush(QWindow* window, const QRegion& region, const QPoint& offset) +{ + Q_UNUSED(region); + Q_UNUSED(offset); + mContext->makeCurrent(window); + glViewport(0, 0, window->width(), window->height()); + + updateTexture(); + + if (!mBlitter->isCreated()) + mBlitter->create(); + + mBlitter->bind(); + mBlitter->setSwizzleRB(true); + mBlitter->blit(mTexture->textureId(), QMatrix4x4(), QOpenGLTextureBlitter::OriginTopLeft); + mBlitter->release(); + + mContext->swapBuffers(window); +} + +void QMirClientBackingStore::updateTexture() +{ + if (mDirty.isNull()) + return; + + if (!mTexture->isCreated()) { + mTexture->setMinificationFilter(QOpenGLTexture::Nearest); + mTexture->setMagnificationFilter(QOpenGLTexture::Nearest); + mTexture->setWrapMode(QOpenGLTexture::ClampToEdge); + mTexture->setData(mImage, QOpenGLTexture::DontGenerateMipMaps); + mTexture->create(); + } + mTexture->bind(); + + QRegion fixed; + QRect imageRect = mImage.rect(); + + Q_FOREACH (const QRect &rect, mDirty.rects()) { + // intersect with image rect to be sure + QRect r = imageRect & rect; + + // if the rect is wide enough it is cheaper to just extend it instead of doing an image copy + if (r.width() >= imageRect.width() / 2) { + r.setX(0); + r.setWidth(imageRect.width()); + } + + fixed |= r; + } + + Q_FOREACH (const QRect &rect, fixed.rects()) { + // if the sub-rect is full-width we can pass the image data directly to + // OpenGL instead of copying, since there is no gap between scanlines + if (rect.width() == imageRect.width()) { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, rect.y(), rect.width(), rect.height(), GL_RGBA, GL_UNSIGNED_BYTE, + mImage.constScanLine(rect.y())); + } else { + glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x(), rect.y(), rect.width(), rect.height(), GL_RGBA, GL_UNSIGNED_BYTE, + mImage.copy(rect).constBits()); + } + } + /* End of code taken from QEGLPlatformBackingStore */ + + mDirty = QRegion(); +} + + +void QMirClientBackingStore::beginPaint(const QRegion& region) +{ + mDirty |= region; +} + +void QMirClientBackingStore::resize(const QSize& size, const QRegion& /*staticContents*/) +{ + mImage = QImage(size, QImage::Format_RGB32); + + if (mTexture->isCreated()) + mTexture->destroy(); +} + +QPaintDevice* QMirClientBackingStore::paintDevice() +{ + return &mImage; +} diff --git a/src/plugins/platforms/mirclient/qmirclientbackingstore.h b/src/plugins/platforms/mirclient/qmirclientbackingstore.h new file mode 100644 index 0000000000..22b8bf9bc5 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientbackingstore.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QMIRCLIENTBACKINGSTORE_H +#define QMIRCLIENTBACKINGSTORE_H + +#include <qpa/qplatformbackingstore.h> + +class QOpenGLContext; +class QOpenGLTexture; +class QOpenGLTextureBlitter; + +class QMirClientBackingStore : public QPlatformBackingStore +{ +public: + QMirClientBackingStore(QWindow* window); + virtual ~QMirClientBackingStore(); + + // QPlatformBackingStore methods. + void beginPaint(const QRegion&) override; + void flush(QWindow* window, const QRegion& region, const QPoint& offset) override; + void resize(const QSize& size, const QRegion& staticContents) override; + QPaintDevice* paintDevice() override; + +protected: + void updateTexture(); + +private: + QScopedPointer<QOpenGLContext> mContext; + QScopedPointer<QOpenGLTexture> mTexture; + QScopedPointer<QOpenGLTextureBlitter> mBlitter; + QImage mImage; + QRegion mDirty; +}; + +#endif // QMIRCLIENTBACKINGSTORE_H diff --git a/src/plugins/platforms/mirclient/qmirclientclipboard.cpp b/src/plugins/platforms/mirclient/qmirclientclipboard.cpp new file mode 100644 index 0000000000..aa2ddf2103 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientclipboard.cpp @@ -0,0 +1,305 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qmirclientclipboard.h" + +#include <QtCore/QMimeData> +#include <QtCore/QStringList> +#include <QDBusInterface> +#include <QDBusPendingCallWatcher> +#include <QDBusPendingReply> + +// FIXME(loicm) The clipboard data format is not defined by Ubuntu Platform API +// which makes it impossible to have non-Qt applications communicate with Qt +// applications through the clipboard API. The solution would be to have +// Ubuntu Platform define the data format or propose an API that supports +// embedding different mime types in the clipboard. + +// Data format: +// number of mime types (sizeof(int)) +// data layout ((4 * sizeof(int)) * number of mime types) +// mime type string offset (sizeof(int)) +// mime type string size (sizeof(int)) +// data offset (sizeof(int)) +// data size (sizeof(int)) +// data (n bytes) + +namespace { + +const int maxFormatsCount = 16; +const int maxBufferSize = 4 * 1024 * 1024; // 4 Mb + +} + +QMirClientClipboard::QMirClientClipboard() + : mMimeData(new QMimeData) + , mIsOutdated(true) + , mUpdatesDisabled(false) + , mDBusSetupDone(false) +{ +} + +QMirClientClipboard::~QMirClientClipboard() +{ + delete mMimeData; +} + +void QMirClientClipboard::requestDBusClipboardContents() +{ + if (!mDBusSetupDone) { + setupDBus(); + } + + if (!mPendingGetContentsCall.isNull()) + return; + + QDBusPendingCall pendingCall = mDBusClipboard->asyncCall("GetContents"); + + mPendingGetContentsCall = new QDBusPendingCallWatcher(pendingCall, this); + + QObject::connect(mPendingGetContentsCall.data(), SIGNAL(finished(QDBusPendingCallWatcher*)), + this, SLOT(onDBusClipboardGetContentsFinished(QDBusPendingCallWatcher*))); +} + +void QMirClientClipboard::onDBusClipboardGetContentsFinished(QDBusPendingCallWatcher* call) +{ + Q_ASSERT(call == mPendingGetContentsCall.data()); + + QDBusPendingReply<QByteArray> reply = *call; + if (reply.isError()) { + qCritical("QMirClientClipboard - Failed to get system clipboard contents via D-Bus. %s, %s", + qPrintable(reply.error().name()), qPrintable(reply.error().message())); + // TODO: Might try again later a number of times... + } else { + QByteArray serializedMimeData = reply.argumentAt<0>(); + updateMimeData(serializedMimeData); + } + call->deleteLater(); +} + +void QMirClientClipboard::onDBusClipboardSetContentsFinished(QDBusPendingCallWatcher *call) +{ + QDBusPendingReply<void> reply = *call; + if (reply.isError()) { + qCritical("QMirClientClipboard - Failed to set the system clipboard contents via D-Bus. %s, %s", + qPrintable(reply.error().name()), qPrintable(reply.error().message())); + // TODO: Might try again later a number of times... + } + call->deleteLater(); +} + +void QMirClientClipboard::updateMimeData(const QByteArray &serializedMimeData) +{ + if (mUpdatesDisabled) + return; + + QMimeData *newMimeData = deserializeMimeData(serializedMimeData); + if (newMimeData) { + delete mMimeData; + mMimeData = newMimeData; + mIsOutdated = false; + emitChanged(QClipboard::Clipboard); + } else { + qWarning("QMirClientClipboard - Got invalid serialized mime data. Ignoring it."); + } +} + +void QMirClientClipboard::setupDBus() +{ + QDBusConnection dbusConnection = QDBusConnection::sessionBus(); + + bool ok = dbusConnection.connect( + "com.canonical.QtMir", + "/com/canonical/QtMir/Clipboard", + "com.canonical.QtMir.Clipboard", + "ContentsChanged", + this, SLOT(updateMimeData(QByteArray))); + if (!ok) { + qCritical("QMirClientClipboard - Failed to connect to ContentsChanged signal form the D-Bus system clipboard."); + } + + mDBusClipboard = new QDBusInterface("com.canonical.QtMir", + "/com/canonical/QtMir/Clipboard", + "com.canonical.QtMir.Clipboard", + dbusConnection); + + mDBusSetupDone = true; +} + +QByteArray QMirClientClipboard::serializeMimeData(QMimeData *mimeData) const +{ + const QStringList formats = mimeData->formats(); + const int formatCount = qMin(formats.size(), maxFormatsCount); + const int headerSize = sizeof(int) + (formatCount * 4 * sizeof(int)); + int bufferSize = headerSize; + + for (int i = 0; i < formatCount; i++) + bufferSize += formats[i].size() + mimeData->data(formats[i]).size(); + + QByteArray serializedMimeData; + if (bufferSize <= maxBufferSize) { + // Serialize data. + serializedMimeData.resize(bufferSize); + { + char *buffer = serializedMimeData.data(); + int* header = reinterpret_cast<int*>(serializedMimeData.data()); + int offset = headerSize; + header[0] = formatCount; + for (int i = 0; i < formatCount; i++) { + const int formatOffset = offset; + const int formatSize = formats[i].size(); + const int dataOffset = offset + formatSize; + const int dataSize = mimeData->data(formats[i]).size(); + memcpy(&buffer[formatOffset], formats[i].toLatin1().data(), formatSize); + memcpy(&buffer[dataOffset], mimeData->data(formats[i]).data(), dataSize); + header[i*4+1] = formatOffset; + header[i*4+2] = formatSize; + header[i*4+3] = dataOffset; + header[i*4+4] = dataSize; + offset += formatSize + dataSize; + } + } + } else { + qWarning("QMirClientClipboard: Not sending contents (%d bytes) to the global clipboard as it's" + " bigger than the maximum allowed size of %d bytes", bufferSize, maxBufferSize); + } + + return serializedMimeData; +} + +QMimeData *QMirClientClipboard::deserializeMimeData(const QByteArray &serializedMimeData) const +{ + if (static_cast<std::size_t>(serializedMimeData.size()) < sizeof(int)) { + // Data is invalid + return nullptr; + } + + QMimeData *mimeData = new QMimeData; + + const char* const buffer = serializedMimeData.constData(); + const int* const header = reinterpret_cast<const int*>(serializedMimeData.constData()); + + const int count = qMin(header[0], maxFormatsCount); + + for (int i = 0; i < count; i++) { + const int formatOffset = header[i*4+1]; + const int formatSize = header[i*4+2]; + const int dataOffset = header[i*4+3]; + const int dataSize = header[i*4+4]; + + if (formatOffset + formatSize <= serializedMimeData.size() + && dataOffset + dataSize <= serializedMimeData.size()) { + + QString mimeType = QString::fromLatin1(&buffer[formatOffset], formatSize); + QByteArray mimeDataBytes(&buffer[dataOffset], dataSize); + + mimeData->setData(mimeType, mimeDataBytes); + } + } + + return mimeData; +} + +QMimeData* QMirClientClipboard::mimeData(QClipboard::Mode mode) +{ + if (mode != QClipboard::Clipboard) + return nullptr; + + if (mIsOutdated && mPendingGetContentsCall.isNull()) { + requestDBusClipboardContents(); + } + + // Return whatever we have at the moment instead of blocking until we have something. + // + // This might be called during app startup just for the sake of checking if some + // "Paste" UI control should be enabled or not. + // We will emit QClipboard::changed() once we finally have something. + return mMimeData; +} + +void QMirClientClipboard::setMimeData(QMimeData* mimeData, QClipboard::Mode mode) +{ + if (mode != QClipboard::Clipboard) + return; + + if (!mPendingGetContentsCall.isNull()) { + // Ignore whatever comes from the system clipboard as we are going to change it anyway + QObject::disconnect(mPendingGetContentsCall.data(), 0, this, 0); + mUpdatesDisabled = true; + mPendingGetContentsCall->waitForFinished(); + mUpdatesDisabled = false; + delete mPendingGetContentsCall.data(); + } + + QByteArray serializedMimeData = serializeMimeData(mimeData); + if (!serializedMimeData.isEmpty()) { + setDBusClipboardContents(serializedMimeData); + } + + mMimeData = mimeData; + emitChanged(QClipboard::Clipboard); +} + +bool QMirClientClipboard::supportsMode(QClipboard::Mode mode) const +{ + return mode == QClipboard::Clipboard; +} + +bool QMirClientClipboard::ownsMode(QClipboard::Mode mode) const +{ + Q_UNUSED(mode); + return false; +} + +void QMirClientClipboard::setDBusClipboardContents(const QByteArray &clipboardContents) +{ + if (!mPendingSetContentsCall.isNull()) { + // Ignore any previous set call as we are going to overwrite it anyway + QObject::disconnect(mPendingSetContentsCall.data(), 0, this, 0); + mUpdatesDisabled = true; + mPendingSetContentsCall->waitForFinished(); + mUpdatesDisabled = false; + delete mPendingSetContentsCall.data(); + } + + QDBusPendingCall pendingCall = mDBusClipboard->asyncCall("SetContents", clipboardContents); + + mPendingSetContentsCall = new QDBusPendingCallWatcher(pendingCall, this); + + QObject::connect(mPendingSetContentsCall.data(), SIGNAL(finished(QDBusPendingCallWatcher*)), + this, SLOT(onDBusClipboardSetContentsFinished(QDBusPendingCallWatcher*))); +} diff --git a/src/plugins/platforms/mirclient/qmirclientclipboard.h b/src/plugins/platforms/mirclient/qmirclientclipboard.h new file mode 100644 index 0000000000..d3d3d400d2 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientclipboard.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QMIRCLIENTCLIPBOARD_H +#define QMIRCLIENTCLIPBOARD_H + +#include <qpa/qplatformclipboard.h> + +#include <QMimeData> +#include <QPointer> +class QDBusInterface; +class QDBusPendingCallWatcher; + +class QMirClientClipboard : public QObject, public QPlatformClipboard +{ + Q_OBJECT +public: + QMirClientClipboard(); + virtual ~QMirClientClipboard(); + + // QPlatformClipboard methods. + QMimeData* mimeData(QClipboard::Mode mode = QClipboard::Clipboard) override; + void setMimeData(QMimeData* data, QClipboard::Mode mode = QClipboard::Clipboard) override; + bool supportsMode(QClipboard::Mode mode) const override; + bool ownsMode(QClipboard::Mode mode) const override; + + void requestDBusClipboardContents(); + +private Q_SLOTS: + void onDBusClipboardGetContentsFinished(QDBusPendingCallWatcher*); + void onDBusClipboardSetContentsFinished(QDBusPendingCallWatcher*); + void updateMimeData(const QByteArray &serializedMimeData); + +private: + void setupDBus(); + + QByteArray serializeMimeData(QMimeData *mimeData) const; + QMimeData *deserializeMimeData(const QByteArray &serializedMimeData) const; + + void setDBusClipboardContents(const QByteArray &clipboardContents); + + QMimeData *mMimeData; + bool mIsOutdated; + + QPointer<QDBusInterface> mDBusClipboard; + + QPointer<QDBusPendingCallWatcher> mPendingGetContentsCall; + QPointer<QDBusPendingCallWatcher> mPendingSetContentsCall; + + bool mUpdatesDisabled; + bool mDBusSetupDone; +}; + +#endif // QMIRCLIENTCLIPBOARD_H diff --git a/src/plugins/platforms/mirclient/qmirclientglcontext.cpp b/src/plugins/platforms/mirclient/qmirclientglcontext.cpp new file mode 100644 index 0000000000..bfba5051e5 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientglcontext.cpp @@ -0,0 +1,156 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qmirclientglcontext.h" +#include "qmirclientwindow.h" +#include "qmirclientlogging.h" +#include <QtPlatformSupport/private/qeglconvenience_p.h> + +#if !defined(QT_NO_DEBUG) +static void printOpenGLESConfig() { + static bool once = true; + if (once) { + const char* string = (const char*) glGetString(GL_VENDOR); + LOG("OpenGL ES vendor: %s", string); + string = (const char*) glGetString(GL_RENDERER); + LOG("OpenGL ES renderer: %s", string); + string = (const char*) glGetString(GL_VERSION); + LOG("OpenGL ES version: %s", string); + string = (const char*) glGetString(GL_SHADING_LANGUAGE_VERSION); + LOG("OpenGL ES Shading Language version: %s", string); + string = (const char*) glGetString(GL_EXTENSIONS); + LOG("OpenGL ES extensions: %s", string); + once = false; + } +} +#endif + +static EGLenum api_in_use() +{ +#ifdef QTUBUNTU_USE_OPENGL + return EGL_OPENGL_API; +#else + return EGL_OPENGL_ES_API; +#endif +} + +QMirClientOpenGLContext::QMirClientOpenGLContext(QMirClientScreen* screen, QMirClientOpenGLContext* share) +{ + ASSERT(screen != NULL); + mEglDisplay = screen->eglDisplay(); + mScreen = screen; + + // Create an OpenGL ES 2 context. + QVector<EGLint> attribs; + attribs.append(EGL_CONTEXT_CLIENT_VERSION); + attribs.append(2); + attribs.append(EGL_NONE); + ASSERT(eglBindAPI(api_in_use()) == EGL_TRUE); + + mEglContext = eglCreateContext(mEglDisplay, screen->eglConfig(), share ? share->eglContext() : EGL_NO_CONTEXT, + attribs.constData()); + DASSERT(mEglContext != EGL_NO_CONTEXT); +} + +QMirClientOpenGLContext::~QMirClientOpenGLContext() +{ + ASSERT(eglDestroyContext(mEglDisplay, mEglContext) == EGL_TRUE); +} + +bool QMirClientOpenGLContext::makeCurrent(QPlatformSurface* surface) +{ + DASSERT(surface->surface()->surfaceType() == QSurface::OpenGLSurface); + EGLSurface eglSurface = static_cast<QMirClientWindow*>(surface)->eglSurface(); +#if defined(QT_NO_DEBUG) + eglBindAPI(api_in_use()); + eglMakeCurrent(mEglDisplay, eglSurface, eglSurface, mEglContext); +#else + ASSERT(eglBindAPI(api_in_use()) == EGL_TRUE); + ASSERT(eglMakeCurrent(mEglDisplay, eglSurface, eglSurface, mEglContext) == EGL_TRUE); + printOpenGLESConfig(); +#endif + return true; +} + +void QMirClientOpenGLContext::doneCurrent() +{ +#if defined(QT_NO_DEBUG) + eglBindAPI(api_in_use()); + eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); +#else + ASSERT(eglBindAPI(api_in_use()) == EGL_TRUE); + ASSERT(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) == EGL_TRUE); +#endif +} + +void QMirClientOpenGLContext::swapBuffers(QPlatformSurface* surface) +{ + QMirClientWindow *ubuntuWindow = static_cast<QMirClientWindow*>(surface); + + EGLSurface eglSurface = ubuntuWindow->eglSurface(); +#if defined(QT_NO_DEBUG) + eglBindAPI(api_in_use()); + eglSwapBuffers(mEglDisplay, eglSurface); +#else + ASSERT(eglBindAPI(api_in_use()) == EGL_TRUE); + ASSERT(eglSwapBuffers(mEglDisplay, eglSurface) == EGL_TRUE); +#endif + + // "Technique" copied from mir, in examples/eglapp.c around line 96 + EGLint newBufferWidth = -1; + EGLint newBufferHeight = -1; + /* + * Querying the surface (actually the current buffer) dimensions here is + * the only truly safe way to be sure that the dimensions we think we + * have are those of the buffer being rendered to. But this should be + * improved in future; https://bugs.launchpad.net/mir/+bug/1194384 + */ + eglQuerySurface(mEglDisplay, eglSurface, EGL_WIDTH, &newBufferWidth); + eglQuerySurface(mEglDisplay, eglSurface, EGL_HEIGHT, &newBufferHeight); + + ubuntuWindow->onBuffersSwapped_threadSafe(newBufferWidth, newBufferHeight); +} + +void (*QMirClientOpenGLContext::getProcAddress(const QByteArray& procName)) () +{ +#if defined(QT_NO_DEBUG) + eglBindAPI(api_in_use()); +#else + ASSERT(eglBindAPI(api_in_use()) == EGL_TRUE); +#endif + return eglGetProcAddress(procName.constData()); +} diff --git a/src/plugins/platforms/mirclient/qmirclientglcontext.h b/src/plugins/platforms/mirclient/qmirclientglcontext.h new file mode 100644 index 0000000000..cc40298259 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientglcontext.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QMIRCLIENTGLCONTEXT_H +#define QMIRCLIENTGLCONTEXT_H + +#include <qpa/qplatformopenglcontext.h> +#include "qmirclientscreen.h" + +class QMirClientOpenGLContext : public QPlatformOpenGLContext +{ +public: + QMirClientOpenGLContext(QMirClientScreen* screen, QMirClientOpenGLContext* share); + virtual ~QMirClientOpenGLContext(); + + // QPlatformOpenGLContext methods. + QSurfaceFormat format() const override { return mScreen->surfaceFormat(); } + void swapBuffers(QPlatformSurface* surface) override; + bool makeCurrent(QPlatformSurface* surface) override; + void doneCurrent() override; + bool isValid() const override { return mEglContext != EGL_NO_CONTEXT; } + void (*getProcAddress(const QByteArray& procName)) (); + + EGLContext eglContext() const { return mEglContext; } + +private: + QMirClientScreen* mScreen; + EGLContext mEglContext; + EGLDisplay mEglDisplay; +}; + +#endif // QMIRCLIENTGLCONTEXT_H diff --git a/src/plugins/platforms/mirclient/qmirclientinput.cpp b/src/plugins/platforms/mirclient/qmirclientinput.cpp new file mode 100644 index 0000000000..56bc21f420 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientinput.cpp @@ -0,0 +1,520 @@ +/**************************************************************************** +** +** Copyright (C) 2014-2015 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +// Local +#include "qmirclientinput.h" +#include "qmirclientintegration.h" +#include "qmirclientnativeinterface.h" +#include "qmirclientscreen.h" +#include "qmirclientwindow.h" +#include "qmirclientlogging.h" +#include "qmirclientorientationchangeevent_p.h" + +// Qt +#if !defined(QT_NO_DEBUG) +#include <QtCore/QThread> +#endif +#include <QtCore/qglobal.h> +#include <QtCore/QCoreApplication> +#include <private/qguiapplication_p.h> +#include <qpa/qplatforminputcontext.h> +#include <qpa/qwindowsysteminterface.h> + +#include <xkbcommon/xkbcommon.h> +#include <xkbcommon/xkbcommon-keysyms.h> + +#include <mir_toolkit/mir_client_library.h> + +#define LOG_EVENTS 0 + +// XKB Keysyms which do not map directly to Qt types (i.e. Unicode points) +static const uint32_t KeyTable[] = { + XKB_KEY_Escape, Qt::Key_Escape, + XKB_KEY_Tab, Qt::Key_Tab, + XKB_KEY_ISO_Left_Tab, Qt::Key_Backtab, + XKB_KEY_BackSpace, Qt::Key_Backspace, + XKB_KEY_Return, Qt::Key_Return, + XKB_KEY_Insert, Qt::Key_Insert, + XKB_KEY_Delete, Qt::Key_Delete, + XKB_KEY_Clear, Qt::Key_Delete, + XKB_KEY_Pause, Qt::Key_Pause, + XKB_KEY_Print, Qt::Key_Print, + + XKB_KEY_Home, Qt::Key_Home, + XKB_KEY_End, Qt::Key_End, + XKB_KEY_Left, Qt::Key_Left, + XKB_KEY_Up, Qt::Key_Up, + XKB_KEY_Right, Qt::Key_Right, + XKB_KEY_Down, Qt::Key_Down, + XKB_KEY_Prior, Qt::Key_PageUp, + XKB_KEY_Next, Qt::Key_PageDown, + + XKB_KEY_Shift_L, Qt::Key_Shift, + XKB_KEY_Shift_R, Qt::Key_Shift, + XKB_KEY_Shift_Lock, Qt::Key_Shift, + XKB_KEY_Control_L, Qt::Key_Control, + XKB_KEY_Control_R, Qt::Key_Control, + XKB_KEY_Meta_L, Qt::Key_Meta, + XKB_KEY_Meta_R, Qt::Key_Meta, + XKB_KEY_Alt_L, Qt::Key_Alt, + XKB_KEY_Alt_R, Qt::Key_Alt, + XKB_KEY_Caps_Lock, Qt::Key_CapsLock, + XKB_KEY_Num_Lock, Qt::Key_NumLock, + XKB_KEY_Scroll_Lock, Qt::Key_ScrollLock, + XKB_KEY_Super_L, Qt::Key_Super_L, + XKB_KEY_Super_R, Qt::Key_Super_R, + XKB_KEY_Menu, Qt::Key_Menu, + XKB_KEY_Hyper_L, Qt::Key_Hyper_L, + XKB_KEY_Hyper_R, Qt::Key_Hyper_R, + XKB_KEY_Help, Qt::Key_Help, + + XKB_KEY_KP_Space, Qt::Key_Space, + XKB_KEY_KP_Tab, Qt::Key_Tab, + XKB_KEY_KP_Enter, Qt::Key_Enter, + XKB_KEY_KP_Home, Qt::Key_Home, + XKB_KEY_KP_Left, Qt::Key_Left, + XKB_KEY_KP_Up, Qt::Key_Up, + XKB_KEY_KP_Right, Qt::Key_Right, + XKB_KEY_KP_Down, Qt::Key_Down, + XKB_KEY_KP_Prior, Qt::Key_PageUp, + XKB_KEY_KP_Next, Qt::Key_PageDown, + XKB_KEY_KP_End, Qt::Key_End, + XKB_KEY_KP_Begin, Qt::Key_Clear, + XKB_KEY_KP_Insert, Qt::Key_Insert, + XKB_KEY_KP_Delete, Qt::Key_Delete, + XKB_KEY_KP_Equal, Qt::Key_Equal, + XKB_KEY_KP_Multiply, Qt::Key_Asterisk, + XKB_KEY_KP_Add, Qt::Key_Plus, + XKB_KEY_KP_Separator, Qt::Key_Comma, + XKB_KEY_KP_Subtract, Qt::Key_Minus, + XKB_KEY_KP_Decimal, Qt::Key_Period, + XKB_KEY_KP_Divide, Qt::Key_Slash, + + XKB_KEY_ISO_Level3_Shift, Qt::Key_AltGr, + XKB_KEY_Multi_key, Qt::Key_Multi_key, + XKB_KEY_Codeinput, Qt::Key_Codeinput, + XKB_KEY_SingleCandidate, Qt::Key_SingleCandidate, + XKB_KEY_MultipleCandidate, Qt::Key_MultipleCandidate, + XKB_KEY_PreviousCandidate, Qt::Key_PreviousCandidate, + + XKB_KEY_Mode_switch, Qt::Key_Mode_switch, + XKB_KEY_script_switch, Qt::Key_Mode_switch, + XKB_KEY_XF86AudioRaiseVolume, Qt::Key_VolumeUp, + XKB_KEY_XF86AudioLowerVolume, Qt::Key_VolumeDown, + XKB_KEY_XF86PowerOff, Qt::Key_PowerOff, + XKB_KEY_XF86PowerDown, Qt::Key_PowerDown, + + 0, 0 +}; + +class QMirClientEvent : public QEvent +{ +public: + QMirClientEvent(QMirClientWindow* window, const MirEvent *event, QEvent::Type type) + : QEvent(type), window(window) { + nativeEvent = mir_event_ref(event); + } + ~QMirClientEvent() + { + mir_event_unref(nativeEvent); + } + + QPointer<QMirClientWindow> window; + const MirEvent *nativeEvent; +}; + +QMirClientInput::QMirClientInput(QMirClientClientIntegration* integration) + : QObject(nullptr) + , mIntegration(integration) + , mEventFilterType(static_cast<QMirClientNativeInterface*>( + integration->nativeInterface())->genericEventFilterType()) + , mEventType(static_cast<QEvent::Type>(QEvent::registerEventType())) +{ + // Initialize touch device. + mTouchDevice = new QTouchDevice; + mTouchDevice->setType(QTouchDevice::TouchScreen); + mTouchDevice->setCapabilities( + QTouchDevice::Position | QTouchDevice::Area | QTouchDevice::Pressure | + QTouchDevice::NormalizedPosition); + QWindowSystemInterface::registerTouchDevice(mTouchDevice); +} + +QMirClientInput::~QMirClientInput() +{ + // Qt will take care of deleting mTouchDevice. +} + +#if (LOG_EVENTS != 0) +static const char* nativeEventTypeToStr(MirEventType t) +{ + switch (t) + { + case mir_event_type_key: + return "mir_event_type_key"; + case mir_event_type_motion: + return "mir_event_type_motion"; + case mir_event_type_surface: + return "mir_event_type_surface"; + case mir_event_type_resize: + return "mir_event_type_resize"; + case mir_event_type_prompt_session_state_change: + return "mir_event_type_prompt_session_state_change"; + case mir_event_type_orientation: + return "mir_event_type_orientation"; + case mir_event_type_close_surface: + return "mir_event_type_close_surface"; + case mir_event_type_input: + return "mir_event_type_input"; + default: + DLOG("Invalid event type %d", t); + return "invalid"; + } +} +#endif // LOG_EVENTS != 0 + +void QMirClientInput::customEvent(QEvent* event) +{ + DASSERT(QThread::currentThread() == thread()); + QMirClientEvent* ubuntuEvent = static_cast<QMirClientEvent*>(event); + const MirEvent *nativeEvent = ubuntuEvent->nativeEvent; + + if ((ubuntuEvent->window == nullptr) || (ubuntuEvent->window->window() == nullptr)) { + qWarning() << "Attempted to deliver an event to a non-existent window, ignoring."; + return; + } + + // Event filtering. + long result; + if (QWindowSystemInterface::handleNativeEvent( + ubuntuEvent->window->window(), mEventFilterType, + const_cast<void *>(static_cast<const void *>(nativeEvent)), &result) == true) { + DLOG("event filtered out by native interface"); + return; + } + + #if (LOG_EVENTS != 0) + LOG("QMirClientInput::customEvent(type=%s)", nativeEventTypeToStr(mir_event_get_type(nativeEvent))); + #endif + + // Event dispatching. + switch (mir_event_get_type(nativeEvent)) + { + case mir_event_type_input: + dispatchInputEvent(ubuntuEvent->window->window(), mir_event_get_input_event(nativeEvent)); + break; + case mir_event_type_resize: + { + Q_ASSERT(ubuntuEvent->window->screen() == mIntegration->screen()); + + auto resizeEvent = mir_event_get_resize_event(nativeEvent); + + mIntegration->screen()->handleWindowSurfaceResize( + mir_resize_event_get_width(resizeEvent), + mir_resize_event_get_height(resizeEvent)); + + ubuntuEvent->window->handleSurfaceResize(mir_resize_event_get_width(resizeEvent), + mir_resize_event_get_height(resizeEvent)); + break; + } + case mir_event_type_surface: + { + auto surfaceEvent = mir_event_get_surface_event(nativeEvent); + if (mir_surface_event_get_attribute(surfaceEvent) == mir_surface_attrib_focus) { + ubuntuEvent->window->handleSurfaceFocusChange(mir_surface_event_get_attribute_value(surfaceEvent) == + mir_surface_focused); + } + break; + } + case mir_event_type_orientation: + dispatchOrientationEvent(ubuntuEvent->window->window(), mir_event_get_orientation_event(nativeEvent)); + break; + case mir_event_type_close_surface: + QWindowSystemInterface::handleCloseEvent(ubuntuEvent->window->window()); + break; + default: + DLOG("unhandled event type: %d", static_cast<int>(mir_event_get_type(nativeEvent))); + } +} + +void QMirClientInput::postEvent(QMirClientWindow *platformWindow, const MirEvent *event) +{ + QWindow *window = platformWindow->window(); + + QCoreApplication::postEvent(this, new QMirClientEvent( + platformWindow, event, mEventType)); + + if ((window->flags().testFlag(Qt::WindowTransparentForInput)) && window->parent()) { + QCoreApplication::postEvent(this, new QMirClientEvent( + static_cast<QMirClientWindow*>(platformWindow->QPlatformWindow::parent()), + event, mEventType)); + } +} + +void QMirClientInput::dispatchInputEvent(QWindow *window, const MirInputEvent *ev) +{ + switch (mir_input_event_get_type(ev)) + { + case mir_input_event_type_key: + dispatchKeyEvent(window, ev); + break; + case mir_input_event_type_touch: + dispatchTouchEvent(window, ev); + break; + case mir_input_event_type_pointer: + dispatchPointerEvent(window, ev); + break; + default: + break; + } +} + +void QMirClientInput::dispatchTouchEvent(QWindow *window, const MirInputEvent *ev) +{ + const MirTouchEvent *tev = mir_input_event_get_touch_event(ev); + + // FIXME(loicm) Max pressure is device specific. That one is for the Samsung Galaxy Nexus. That + // needs to be fixed as soon as the compat input lib adds query support. + const float kMaxPressure = 1.28; + const QRect kWindowGeometry = window->geometry(); + QList<QWindowSystemInterface::TouchPoint> touchPoints; + + + // TODO: Is it worth setting the Qt::TouchPointStationary ones? Currently they are left + // as Qt::TouchPointMoved + const unsigned int kPointerCount = mir_touch_event_point_count(tev); + for (unsigned int i = 0; i < kPointerCount; ++i) { + QWindowSystemInterface::TouchPoint touchPoint; + + const float kX = mir_touch_event_axis_value(tev, i, mir_touch_axis_x) + kWindowGeometry.x(); + const float kY = mir_touch_event_axis_value(tev, i, mir_touch_axis_y) + kWindowGeometry.y(); // see bug lp:1346633 workaround comments elsewhere + const float kW = mir_touch_event_axis_value(tev, i, mir_touch_axis_touch_major); + const float kH = mir_touch_event_axis_value(tev, i, mir_touch_axis_touch_minor); + const float kP = mir_touch_event_axis_value(tev, i, mir_touch_axis_pressure); + touchPoint.id = mir_touch_event_id(tev, i); + touchPoint.normalPosition = QPointF(kX / kWindowGeometry.width(), kY / kWindowGeometry.height()); + touchPoint.area = QRectF(kX - (kW / 2.0), kY - (kH / 2.0), kW, kH); + touchPoint.pressure = kP / kMaxPressure; + + MirTouchAction touch_action = mir_touch_event_action(tev, i); + switch (touch_action) + { + case mir_touch_action_down: + touchPoint.state = Qt::TouchPointPressed; + break; + case mir_touch_action_up: + touchPoint.state = Qt::TouchPointReleased; + break; + case mir_touch_action_change: + default: + touchPoint.state = Qt::TouchPointMoved; + } + + touchPoints.append(touchPoint); + } + + ulong timestamp = mir_input_event_get_event_time(ev) / 1000000; + QWindowSystemInterface::handleTouchEvent(window, timestamp, + mTouchDevice, touchPoints); +} + +static uint32_t translateKeysym(uint32_t sym, char *string, size_t size) +{ + Q_UNUSED(size); + string[0] = '\0'; + + if (sym >= XKB_KEY_F1 && sym <= XKB_KEY_F35) + return Qt::Key_F1 + (int(sym) - XKB_KEY_F1); + + for (int i = 0; KeyTable[i]; i += 2) { + if (sym == KeyTable[i]) + return KeyTable[i + 1]; + } + + string[0] = sym; + string[1] = '\0'; + return toupper(sym); +} + +namespace +{ +Qt::KeyboardModifiers qt_modifiers_from_mir(MirInputEventModifiers modifiers) +{ + Qt::KeyboardModifiers q_modifiers = Qt::NoModifier; + if (modifiers & mir_input_event_modifier_shift) { + q_modifiers |= Qt::ShiftModifier; + } + if (modifiers & mir_input_event_modifier_ctrl) { + q_modifiers |= Qt::ControlModifier; + } + if (modifiers & mir_input_event_modifier_alt) { + q_modifiers |= Qt::AltModifier; + } + if (modifiers & mir_input_event_modifier_meta) { + q_modifiers |= Qt::MetaModifier; + } + return q_modifiers; +} +} + +void QMirClientInput::dispatchKeyEvent(QWindow *window, const MirInputEvent *event) +{ + const MirKeyboardEvent *key_event = mir_input_event_get_keyboard_event(event); + + ulong timestamp = mir_input_event_get_event_time(event) / 1000000; + xkb_keysym_t xk_sym = mir_keyboard_event_key_code(key_event); + + // Key modifier and unicode index mapping. + auto modifiers = qt_modifiers_from_mir(mir_keyboard_event_modifiers(key_event)); + + MirKeyboardAction action = mir_keyboard_event_action(key_event); + QEvent::Type keyType = action == mir_keyboard_action_up + ? QEvent::KeyRelease : QEvent::KeyPress; + + char s[2]; + int sym = translateKeysym(xk_sym, s, sizeof(s)); + QString text = QString::fromLatin1(s); + + bool is_auto_rep = action == mir_keyboard_action_repeat; + + QPlatformInputContext *context = QGuiApplicationPrivate::platformIntegration()->inputContext(); + if (context) { + QKeyEvent qKeyEvent(keyType, sym, modifiers, text, is_auto_rep); + qKeyEvent.setTimestamp(timestamp); + if (context->filterEvent(&qKeyEvent)) { + DLOG("key event filtered out by input context"); + return; + } + } + + QWindowSystemInterface::handleKeyEvent(window, timestamp, keyType, sym, modifiers, text, is_auto_rep); +} + +namespace +{ +Qt::MouseButtons extract_buttons(const MirPointerEvent *pev) +{ + Qt::MouseButtons buttons = Qt::NoButton; + if (mir_pointer_event_button_state(pev, mir_pointer_button_primary)) + buttons |= Qt::LeftButton; + if (mir_pointer_event_button_state(pev, mir_pointer_button_secondary)) + buttons |= Qt::RightButton; + if (mir_pointer_event_button_state(pev, mir_pointer_button_tertiary)) + buttons |= Qt::MidButton; + + // TODO: Should mir back and forward buttons exist? + // should they be Qt::X button 1 and 2? + return buttons; +} +} + +void QMirClientInput::dispatchPointerEvent(QWindow *window, const MirInputEvent *ev) +{ + auto timestamp = mir_input_event_get_event_time(ev) / 1000000; + + auto pev = mir_input_event_get_pointer_event(ev); + auto modifiers = qt_modifiers_from_mir(mir_pointer_event_modifiers(pev)); + auto buttons = extract_buttons(pev); + + auto local_point = QPointF(mir_pointer_event_axis_value(pev, mir_pointer_axis_x), + mir_pointer_event_axis_value(pev, mir_pointer_axis_y)); + + QWindowSystemInterface::handleMouseEvent(window, timestamp, local_point, local_point /* Should we omit global point instead? */, + buttons, modifiers); +} + +#if (LOG_EVENTS != 0) +static const char* nativeOrientationDirectionToStr(MirOrientation orientation) +{ + switch (orientation) { + case mir_orientation_normal: + return "Normal"; + break; + case mir_orientation_left: + return "Left"; + break; + case mir_orientation_inverted: + return "Inverted"; + break; + case mir_orientation_right: + return "Right"; + break; + default: + return "INVALID!"; + } +} +#endif + +void QMirClientInput::dispatchOrientationEvent(QWindow *window, const MirOrientationEvent *event) +{ + MirOrientation mir_orientation = mir_orientation_event_get_direction(event); + #if (LOG_EVENTS != 0) + // Orientation event logging. + LOG("ORIENTATION direction: %s", nativeOrientationDirectionToStr(mir_orientation)); + #endif + + if (!window->screen()) { + DLOG("Window has no associated screen, dropping orientation event"); + return; + } + + OrientationChangeEvent::Orientation orientation; + switch (mir_orientation) { + case mir_orientation_normal: + orientation = OrientationChangeEvent::TopUp; + break; + case mir_orientation_left: + orientation = OrientationChangeEvent::LeftUp; + break; + case mir_orientation_inverted: + orientation = OrientationChangeEvent::TopDown; + break; + case mir_orientation_right: + orientation = OrientationChangeEvent::RightUp; + break; + default: + DLOG("No such orientation %d", mir_orientation); + return; + } + + // Dispatch orientation event to [Platform]Screen, as that is where Qt reads it. Screen will handle + // notifying Qt of the actual orientation change - done to prevent multiple Windows each creating + // an identical orientation change event and passing it directly to Qt. + // [Platform]Screen can also factor in the native orientation. + QCoreApplication::postEvent(static_cast<QMirClientScreen*>(window->screen()->handle()), + new OrientationChangeEvent(OrientationChangeEvent::mType, orientation)); +} + diff --git a/src/plugins/platforms/mirclient/qmirclientinput.h b/src/plugins/platforms/mirclient/qmirclientinput.h new file mode 100644 index 0000000000..c987d18c12 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientinput.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2014-2015 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QMIRCLIENTINPUT_H +#define QMIRCLIENTINPUT_H + +// Qt +#include <qpa/qwindowsysteminterface.h> + +#include <mir_toolkit/mir_client_library.h> + +class QMirClientClientIntegration; +class QMirClientWindow; + +class QMirClientInput : public QObject +{ + Q_OBJECT + +public: + QMirClientInput(QMirClientClientIntegration* integration); + virtual ~QMirClientInput(); + + // QObject methods. + void customEvent(QEvent* event) override; + + void postEvent(QMirClientWindow* window, const MirEvent *event); + QMirClientClientIntegration* integration() const { return mIntegration; } + +protected: + void dispatchKeyEvent(QWindow *window, const MirInputEvent *event); + void dispatchPointerEvent(QWindow *window, const MirInputEvent *event); + void dispatchTouchEvent(QWindow *window, const MirInputEvent *event); + void dispatchInputEvent(QWindow *window, const MirInputEvent *event); + + void dispatchOrientationEvent(QWindow* window, const MirOrientationEvent *event); + +private: + QMirClientClientIntegration* mIntegration; + QTouchDevice* mTouchDevice; + const QByteArray mEventFilterType; + const QEvent::Type mEventType; +}; + +#endif // QMIRCLIENTINPUT_H diff --git a/src/plugins/platforms/mirclient/qmirclientintegration.cpp b/src/plugins/platforms/mirclient/qmirclientintegration.cpp new file mode 100644 index 0000000000..a234f4eac6 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientintegration.cpp @@ -0,0 +1,264 @@ +/**************************************************************************** +** +** Copyright (C) 2014-2015 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +// Qt +#include <QGuiApplication> +#include <private/qguiapplication_p.h> +#include <qpa/qplatformnativeinterface.h> +#include <qpa/qplatforminputcontextfactory_p.h> +#include <qpa/qplatforminputcontext.h> +#include <QtPlatformSupport/private/qgenericunixfontdatabase_p.h> +#include <QtPlatformSupport/private/qgenericunixeventdispatcher_p.h> +#include <QOpenGLContext> + +// Local +#include "qmirclientbackingstore.h" +#include "qmirclientclipboard.h" +#include "qmirclientglcontext.h" +#include "qmirclientinput.h" +#include "qmirclientintegration.h" +#include "qmirclientlogging.h" +#include "qmirclientnativeinterface.h" +#include "qmirclientscreen.h" +#include "qmirclienttheme.h" +#include "qmirclientwindow.h" + +// platform-api +#include <ubuntu/application/lifecycle_delegate.h> +#include <ubuntu/application/id.h> +#include <ubuntu/application/options.h> + +static void resumedCallback(const UApplicationOptions *options, void* context) +{ + Q_UNUSED(options) + Q_UNUSED(context) + DASSERT(context != NULL); + QCoreApplication::postEvent(QCoreApplication::instance(), + new QEvent(QEvent::ApplicationActivate)); +} + +static void aboutToStopCallback(UApplicationArchive *archive, void* context) +{ + Q_UNUSED(archive) + DASSERT(context != NULL); + QMirClientClientIntegration* integration = static_cast<QMirClientClientIntegration*>(context); + integration->inputContext()->hideInputPanel(); + QCoreApplication::postEvent(QCoreApplication::instance(), + new QEvent(QEvent::ApplicationDeactivate)); +} + +QMirClientClientIntegration::QMirClientClientIntegration() + : QPlatformIntegration() + , mNativeInterface(new QMirClientNativeInterface) + , mFontDb(new QGenericUnixFontDatabase) + , mServices(new QMirClientPlatformServices) + , mClipboard(new QMirClientClipboard) + , mScaleFactor(1.0) +{ + setupOptions(); + setupDescription(); + + // Create new application instance + mInstance = u_application_instance_new_from_description_with_options(mDesc, mOptions); + + if (mInstance == nullptr) + qFatal("QMirClientClientIntegration: connection to Mir server failed. Check that a Mir server is\n" + "running, and the correct socket is being used and is accessible. The shell may have\n" + "rejected the incoming connection, so check its log file"); + + // Create default screen. + mScreen = new QMirClientScreen(u_application_instance_get_mir_connection(mInstance)); + screenAdded(mScreen); + + // Initialize input. + if (qEnvironmentVariableIsEmpty("QTUBUNTU_NO_INPUT")) { + mInput = new QMirClientInput(this); + mInputContext = QPlatformInputContextFactory::create(); + } else { + mInput = nullptr; + mInputContext = nullptr; + } + + // compute the scale factor + const int defaultGridUnit = 8; + int gridUnit = defaultGridUnit; + QByteArray gridUnitString = qgetenv("GRID_UNIT_PX"); + if (!gridUnitString.isEmpty()) { + bool ok; + gridUnit = gridUnitString.toInt(&ok); + if (!ok) { + gridUnit = defaultGridUnit; + } + } + mScaleFactor = static_cast<qreal>(gridUnit) / defaultGridUnit; +} + +QMirClientClientIntegration::~QMirClientClientIntegration() +{ + delete mInput; + delete mInputContext; + delete mScreen; + delete mServices; +} + +QPlatformServices *QMirClientClientIntegration::services() const +{ + return mServices; +} + +void QMirClientClientIntegration::setupOptions() +{ + QStringList args = QCoreApplication::arguments(); + int argc = args.size() + 1; + char **argv = new char*[argc]; + for (int i = 0; i < argc - 1; i++) + argv[i] = qstrdup(args.at(i).toLocal8Bit()); + argv[argc - 1] = nullptr; + + mOptions = u_application_options_new_from_cmd_line(argc - 1, argv); + + for (int i = 0; i < argc; i++) + delete [] argv[i]; + delete [] argv; +} + +void QMirClientClientIntegration::setupDescription() +{ + mDesc = u_application_description_new(); + UApplicationId* id = u_application_id_new_from_stringn("QtUbuntu", 8); + u_application_description_set_application_id(mDesc, id); + + UApplicationLifecycleDelegate* delegate = u_application_lifecycle_delegate_new(); + u_application_lifecycle_delegate_set_application_resumed_cb(delegate, &resumedCallback); + u_application_lifecycle_delegate_set_application_about_to_stop_cb(delegate, &aboutToStopCallback); + u_application_lifecycle_delegate_set_context(delegate, this); + u_application_description_set_application_lifecycle_delegate(mDesc, delegate); +} + +QPlatformWindow* QMirClientClientIntegration::createPlatformWindow(QWindow* window) const +{ + return const_cast<QMirClientClientIntegration*>(this)->createPlatformWindow(window); +} + +QPlatformWindow* QMirClientClientIntegration::createPlatformWindow(QWindow* window) +{ + QPlatformWindow* platformWindow = new QMirClientWindow( + window, mClipboard, static_cast<QMirClientScreen*>(mScreen), mInput, u_application_instance_get_mir_connection(mInstance)); + platformWindow->requestActivateWindow(); + return platformWindow; +} + +bool QMirClientClientIntegration::hasCapability(QPlatformIntegration::Capability cap) const +{ + switch (cap) { + case ThreadedPixmaps: + return true; + break; + + case OpenGL: + return true; + break; + + case ThreadedOpenGL: + if (qEnvironmentVariableIsEmpty("QTUBUNTU_NO_THREADED_OPENGL")) { + return true; + } else { + DLOG("ubuntumirclient: disabled threaded OpenGL"); + return false; + } + break; + + default: + return QPlatformIntegration::hasCapability(cap); + } +} + +QAbstractEventDispatcher *QMirClientClientIntegration::createEventDispatcher() const +{ + return createUnixEventDispatcher(); +} + +QPlatformBackingStore* QMirClientClientIntegration::createPlatformBackingStore(QWindow* window) const +{ + return new QMirClientBackingStore(window); +} + +QPlatformOpenGLContext* QMirClientClientIntegration::createPlatformOpenGLContext( + QOpenGLContext* context) const +{ + return const_cast<QMirClientClientIntegration*>(this)->createPlatformOpenGLContext(context); +} + +QPlatformOpenGLContext* QMirClientClientIntegration::createPlatformOpenGLContext( + QOpenGLContext* context) +{ + return new QMirClientOpenGLContext(static_cast<QMirClientScreen*>(context->screen()->handle()), + static_cast<QMirClientOpenGLContext*>(context->shareHandle())); +} + +QStringList QMirClientClientIntegration::themeNames() const +{ + return QStringList(QMirClientTheme::name); +} + +QPlatformTheme* QMirClientClientIntegration::createPlatformTheme(const QString& name) const +{ + Q_UNUSED(name); + return new QMirClientTheme; +} + +QVariant QMirClientClientIntegration::styleHint(StyleHint hint) const +{ + switch (hint) { + case QPlatformIntegration::StartDragDistance: { + // default is 10 pixels (see QPlatformTheme::defaultThemeHint) + return 10.0 * mScaleFactor; + } + case QPlatformIntegration::PasswordMaskDelay: { + // return time in milliseconds - 1 second + return QVariant(1000); + } + default: + break; + } + return QPlatformIntegration::styleHint(hint); +} + +QPlatformClipboard* QMirClientClientIntegration::clipboard() const +{ + return mClipboard.data(); +} diff --git a/src/plugins/platforms/mirclient/qmirclientintegration.h b/src/plugins/platforms/mirclient/qmirclientintegration.h new file mode 100644 index 0000000000..2960209691 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientintegration.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QMIRCLIENTINTEGRATION_H +#define QMIRCLIENTINTEGRATION_H + +#include <qpa/qplatformintegration.h> +#include <QSharedPointer> + +#include "qmirclientplatformservices.h" + +// platform-api +#include <ubuntu/application/description.h> +#include <ubuntu/application/instance.h> + +class QMirClientClipboard; +class QMirClientInput; +class QMirClientScreen; + +class QMirClientClientIntegration : public QPlatformIntegration { +public: + QMirClientClientIntegration(); + virtual ~QMirClientClientIntegration(); + + // QPlatformIntegration methods. + bool hasCapability(QPlatformIntegration::Capability cap) const override; + QAbstractEventDispatcher *createEventDispatcher() const override; + QPlatformNativeInterface* nativeInterface() const override { return mNativeInterface; } + QPlatformBackingStore* createPlatformBackingStore(QWindow* window) const override; + QPlatformOpenGLContext* createPlatformOpenGLContext(QOpenGLContext* context) const override; + QPlatformFontDatabase* fontDatabase() const override { return mFontDb; } + QStringList themeNames() const override; + QPlatformTheme* createPlatformTheme(const QString& name) const override; + QVariant styleHint(StyleHint hint) const override; + QPlatformServices *services() const override; + QPlatformWindow* createPlatformWindow(QWindow* window) const override; + QPlatformInputContext* inputContext() const override { return mInputContext; } + QPlatformClipboard* clipboard() const override; + + QPlatformOpenGLContext* createPlatformOpenGLContext(QOpenGLContext* context); + QPlatformWindow* createPlatformWindow(QWindow* window); + QMirClientScreen* screen() const { return mScreen; } + +private: + void setupOptions(); + void setupDescription(); + + QPlatformNativeInterface* mNativeInterface; + QPlatformFontDatabase* mFontDb; + + QMirClientPlatformServices* mServices; + + QMirClientScreen* mScreen; + QMirClientInput* mInput; + QPlatformInputContext* mInputContext; + QSharedPointer<QMirClientClipboard> mClipboard; + qreal mScaleFactor; + + // Platform API stuff + UApplicationOptions* mOptions; + UApplicationDescription* mDesc; + UApplicationInstance* mInstance; +}; + +#endif // QMIRCLIENTINTEGRATION_H diff --git a/src/plugins/platforms/mirclient/qmirclientlogging.h b/src/plugins/platforms/mirclient/qmirclientlogging.h new file mode 100644 index 0000000000..80914d28b9 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientlogging.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QMIRCLIENTLOGGING_H +#define QMIRCLIENTLOGGING_H + +// Logging and assertion macros. +#define LOG(...) qDebug(__VA_ARGS__) +#define LOG_IF(cond,...) do { if (cond) qDebug(__VA_ARGS__); } while (0) +#define ASSERT(cond) ((!(cond)) ? qt_assert(#cond,__FILE__,__LINE__) : qt_noop()) +#define NOT_REACHED() qt_assert("Not reached!",__FILE__,__LINE__) + +// Logging and assertion macros are compiled out for release builds. +#if !defined(QT_NO_DEBUG) +#define DLOG(...) LOG(__VA_ARGS__) +#define DLOG_IF(cond,...) LOG_IF((cond), __VA_ARGS__) +#define DASSERT(cond) ASSERT((cond)) +#define DNOT_REACHED() NOT_REACHED() +#else +#define DLOG(...) qt_noop() +#define DLOG_IF(cond,...) qt_noop() +#define DASSERT(cond) qt_noop() +#define DNOT_REACHED() qt_noop() +#endif + +#endif // QMIRCLIENTLOGGING_H diff --git a/src/plugins/platforms/mirclient/qmirclientnativeinterface.cpp b/src/plugins/platforms/mirclient/qmirclientnativeinterface.cpp new file mode 100644 index 0000000000..a0bb932df3 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientnativeinterface.cpp @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +// Qt +#include <private/qguiapplication_p.h> +#include <QtGui/qopenglcontext.h> +#include <QtGui/qscreen.h> +#include <QtCore/QMap> + +// Local +#include "qmirclientnativeinterface.h" +#include "qmirclientscreen.h" +#include "qmirclientglcontext.h" + +class QMirClientResourceMap : public QMap<QByteArray, QMirClientNativeInterface::ResourceType> +{ +public: + QMirClientResourceMap() + : QMap<QByteArray, QMirClientNativeInterface::ResourceType>() { + insert("egldisplay", QMirClientNativeInterface::EglDisplay); + insert("eglcontext", QMirClientNativeInterface::EglContext); + insert("nativeorientation", QMirClientNativeInterface::NativeOrientation); + insert("display", QMirClientNativeInterface::Display); + } +}; + +Q_GLOBAL_STATIC(QMirClientResourceMap, ubuntuResourceMap) + +QMirClientNativeInterface::QMirClientNativeInterface() + : mGenericEventFilterType(QByteArrayLiteral("Event")) + , mNativeOrientation(nullptr) +{ +} + +QMirClientNativeInterface::~QMirClientNativeInterface() +{ + delete mNativeOrientation; + mNativeOrientation = nullptr; +} + +void* QMirClientNativeInterface::nativeResourceForContext( + const QByteArray& resourceString, QOpenGLContext* context) +{ + if (!context) + return nullptr; + + const QByteArray kLowerCaseResource = resourceString.toLower(); + + if (!ubuntuResourceMap()->contains(kLowerCaseResource)) + return nullptr; + + const ResourceType kResourceType = ubuntuResourceMap()->value(kLowerCaseResource); + + if (kResourceType == QMirClientNativeInterface::EglContext) + return static_cast<QMirClientOpenGLContext*>(context->handle())->eglContext(); + else + return nullptr; +} + +void* QMirClientNativeInterface::nativeResourceForWindow(const QByteArray& resourceString, QWindow* window) +{ + const QByteArray kLowerCaseResource = resourceString.toLower(); + if (!ubuntuResourceMap()->contains(kLowerCaseResource)) + return NULL; + const ResourceType kResourceType = ubuntuResourceMap()->value(kLowerCaseResource); + if (kResourceType == QMirClientNativeInterface::EglDisplay) { + if (window) { + return static_cast<QMirClientScreen*>(window->screen()->handle())->eglDisplay(); + } else { + return static_cast<QMirClientScreen*>( + QGuiApplication::primaryScreen()->handle())->eglDisplay(); + } + } else if (kResourceType == QMirClientNativeInterface::NativeOrientation) { + // Return the device's native screen orientation. + if (window) { + QMirClientScreen *ubuntuScreen = static_cast<QMirClientScreen*>(window->screen()->handle()); + mNativeOrientation = new Qt::ScreenOrientation(ubuntuScreen->nativeOrientation()); + } else { + QPlatformScreen *platformScreen = QGuiApplication::primaryScreen()->handle(); + mNativeOrientation = new Qt::ScreenOrientation(platformScreen->nativeOrientation()); + } + return mNativeOrientation; + } else { + return NULL; + } +} + +void* QMirClientNativeInterface::nativeResourceForScreen(const QByteArray& resourceString, QScreen* screen) +{ + const QByteArray kLowerCaseResource = resourceString.toLower(); + if (!ubuntuResourceMap()->contains(kLowerCaseResource)) + return NULL; + const ResourceType kResourceType = ubuntuResourceMap()->value(kLowerCaseResource); + if (kResourceType == QMirClientNativeInterface::Display) { + if (!screen) + screen = QGuiApplication::primaryScreen(); + return static_cast<QMirClientScreen*>(screen->handle())->eglNativeDisplay(); + } else + return NULL; +} diff --git a/src/plugins/platforms/mirclient/qmirclientnativeinterface.h b/src/plugins/platforms/mirclient/qmirclientnativeinterface.h new file mode 100644 index 0000000000..84f03bb915 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientnativeinterface.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QMIRCLIENTNATIVEINTERFACE_H +#define QMIRCLIENTNATIVEINTERFACE_H + +#include <qpa/qplatformnativeinterface.h> + +class QMirClientNativeInterface : public QPlatformNativeInterface { +public: + enum ResourceType { EglDisplay, EglContext, NativeOrientation, Display }; + + QMirClientNativeInterface(); + ~QMirClientNativeInterface(); + + // QPlatformNativeInterface methods. + void* nativeResourceForContext(const QByteArray& resourceString, + QOpenGLContext* context) override; + void* nativeResourceForWindow(const QByteArray& resourceString, + QWindow* window) override; + void* nativeResourceForScreen(const QByteArray& resourceString, + QScreen* screen) override; + + // New methods. + const QByteArray& genericEventFilterType() const { return mGenericEventFilterType; } + +private: + const QByteArray mGenericEventFilterType; + Qt::ScreenOrientation* mNativeOrientation; +}; + +#endif // QMIRCLIENTNATIVEINTERFACE_H diff --git a/src/plugins/platforms/mirclient/qmirclientorientationchangeevent_p.h b/src/plugins/platforms/mirclient/qmirclientorientationchangeevent_p.h new file mode 100644 index 0000000000..24d7307faa --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientorientationchangeevent_p.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QMIRCLIENTORIENTATIONCHANGEEVENT_P_H +#define QMIRCLIENTORIENTATIONCHANGEEVENT_P_H + +#include <QEvent> +#include "qmirclientlogging.h" + +class OrientationChangeEvent : public QEvent { +public: + enum Orientation { + Undefined = 0, + TopUp, + TopDown, + LeftUp, + RightUp, + FaceUp, + FaceDown + }; + + OrientationChangeEvent(QEvent::Type type, Orientation orientation) + : QEvent(type) + , mOrientation(orientation) + { + } + + static const QEvent::Type mType; + Orientation mOrientation; +}; + +#endif // QMIRCLIENTORIENTATIONCHANGEEVENT_P_H diff --git a/src/plugins/platforms/mirclient/qmirclientplatformservices.cpp b/src/plugins/platforms/mirclient/qmirclientplatformservices.cpp new file mode 100644 index 0000000000..d0260c79d3 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientplatformservices.cpp @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qmirclientplatformservices.h" + +#include <QUrl> + +#include <ubuntu/application/url_dispatcher/service.h> +#include <ubuntu/application/url_dispatcher/session.h> + +bool QMirClientPlatformServices::openUrl(const QUrl &url) +{ + return callDispatcher(url); +} + +bool QMirClientPlatformServices::openDocument(const QUrl &url) +{ + return callDispatcher(url); +} + +bool QMirClientPlatformServices::callDispatcher(const QUrl &url) +{ + UAUrlDispatcherSession* session = ua_url_dispatcher_session(); + if (!session) + return false; + + ua_url_dispatcher_session_open(session, url.toEncoded().constData(), NULL, NULL); + + free(session); + + // We are returning true here because the other option + // is spawning a nested event loop and wait for the + // callback. But there is no guarantee on how fast + // the callback is going to be so we prefer to avoid the + // nested event loop. Long term plan is improve Qt API + // to support an async openUrl + return true; +} diff --git a/src/plugins/platforms/mirclient/qmirclientplatformservices.h b/src/plugins/platforms/mirclient/qmirclientplatformservices.h new file mode 100644 index 0000000000..64a0432d06 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientplatformservices.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QMIRCLIENTPLATFORMSERVICES_H +#define QMIRCLIENTPLATFORMSERVICES_H + +#include <qpa/qplatformservices.h> +#include <QtPlatformSupport/private/qgenericunixfontdatabase_p.h> +#include <QtPlatformSupport/private/qgenericunixeventdispatcher_p.h> + +class QMirClientPlatformServices : public QPlatformServices { +public: + bool openUrl(const QUrl &url) override; + bool openDocument(const QUrl &url) override; + +private: + bool callDispatcher(const QUrl &url); +}; + +#endif // QMIRCLIENTPLATFORMSERVICES_H diff --git a/src/plugins/platforms/mirclient/qmirclientplugin.cpp b/src/plugins/platforms/mirclient/qmirclientplugin.cpp new file mode 100644 index 0000000000..203a1cbfd8 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientplugin.cpp @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qmirclientplugin.h" +#include "qmirclientintegration.h" + +QStringList QMirClientIntegrationPlugin::keys() const +{ + QStringList list; + list << "mirclient"; + return list; +} + +QPlatformIntegration* QMirClientIntegrationPlugin::create(const QString &system, + const QStringList &) +{ + if (system.toLower() == "mirclient") { + return new QMirClientClientIntegration; + } else { + return 0; + } +} diff --git a/src/plugins/platforms/mirclient/qmirclientplugin.h b/src/plugins/platforms/mirclient/qmirclientplugin.h new file mode 100644 index 0000000000..a6f1a1081a --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientplugin.h @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QMIRCLIENTPLUGIN_H +#define QMIRCLIENTPLUGIN_H + +#include <qpa/qplatformintegrationplugin.h> + +class QMirClientIntegrationPlugin : public QPlatformIntegrationPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QPlatformIntegrationFactoryInterface_iid FILE "mirclient.json") + +public: + QStringList keys() const; + QPlatformIntegration* create(const QString&, const QStringList&); +}; + +#endif // QMIRCLIENTPLUGIN_H diff --git a/src/plugins/platforms/mirclient/qmirclientscreen.cpp b/src/plugins/platforms/mirclient/qmirclientscreen.cpp new file mode 100644 index 0000000000..5c4b1cd0d6 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientscreen.cpp @@ -0,0 +1,296 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <mir_toolkit/mir_client_library.h> + +// Qt +#include <QCoreApplication> +#include <QtCore/qmath.h> +#include <QScreen> +#include <QThread> +#include <qpa/qwindowsysteminterface.h> +#include <QtPlatformSupport/private/qeglconvenience_p.h> + +// local +#include "qmirclientscreen.h" +#include "qmirclientlogging.h" +#include "qmirclientorientationchangeevent_p.h" + +#include "memory" + +static const int kSwapInterval = 1; + +#if !defined(QT_NO_DEBUG) + +static const char *orientationToStr(Qt::ScreenOrientation orientation) { + switch (orientation) { + case Qt::PrimaryOrientation: + return "primary"; + case Qt::PortraitOrientation: + return "portrait"; + case Qt::LandscapeOrientation: + return "landscape"; + case Qt::InvertedPortraitOrientation: + return "inverted portrait"; + case Qt::InvertedLandscapeOrientation: + return "inverted landscape"; + default: + return "INVALID!"; + } +} + +static void printEglConfig(EGLDisplay display, EGLConfig config) { + DASSERT(display != EGL_NO_DISPLAY); + DASSERT(config != nullptr); + static const struct { const EGLint attrib; const char* name; } kAttribs[] = { + { EGL_BUFFER_SIZE, "EGL_BUFFER_SIZE" }, + { EGL_ALPHA_SIZE, "EGL_ALPHA_SIZE" }, + { EGL_BLUE_SIZE, "EGL_BLUE_SIZE" }, + { EGL_GREEN_SIZE, "EGL_GREEN_SIZE" }, + { EGL_RED_SIZE, "EGL_RED_SIZE" }, + { EGL_DEPTH_SIZE, "EGL_DEPTH_SIZE" }, + { EGL_STENCIL_SIZE, "EGL_STENCIL_SIZE" }, + { EGL_CONFIG_CAVEAT, "EGL_CONFIG_CAVEAT" }, + { EGL_CONFIG_ID, "EGL_CONFIG_ID" }, + { EGL_LEVEL, "EGL_LEVEL" }, + { EGL_MAX_PBUFFER_HEIGHT, "EGL_MAX_PBUFFER_HEIGHT" }, + { EGL_MAX_PBUFFER_PIXELS, "EGL_MAX_PBUFFER_PIXELS" }, + { EGL_MAX_PBUFFER_WIDTH, "EGL_MAX_PBUFFER_WIDTH" }, + { EGL_NATIVE_RENDERABLE, "EGL_NATIVE_RENDERABLE" }, + { EGL_NATIVE_VISUAL_ID, "EGL_NATIVE_VISUAL_ID" }, + { EGL_NATIVE_VISUAL_TYPE, "EGL_NATIVE_VISUAL_TYPE" }, + { EGL_SAMPLES, "EGL_SAMPLES" }, + { EGL_SAMPLE_BUFFERS, "EGL_SAMPLE_BUFFERS" }, + { EGL_SURFACE_TYPE, "EGL_SURFACE_TYPE" }, + { EGL_TRANSPARENT_TYPE, "EGL_TRANSPARENT_TYPE" }, + { EGL_TRANSPARENT_BLUE_VALUE, "EGL_TRANSPARENT_BLUE_VALUE" }, + { EGL_TRANSPARENT_GREEN_VALUE, "EGL_TRANSPARENT_GREEN_VALUE" }, + { EGL_TRANSPARENT_RED_VALUE, "EGL_TRANSPARENT_RED_VALUE" }, + { EGL_BIND_TO_TEXTURE_RGB, "EGL_BIND_TO_TEXTURE_RGB" }, + { EGL_BIND_TO_TEXTURE_RGBA, "EGL_BIND_TO_TEXTURE_RGBA" }, + { EGL_MIN_SWAP_INTERVAL, "EGL_MIN_SWAP_INTERVAL" }, + { EGL_MAX_SWAP_INTERVAL, "EGL_MAX_SWAP_INTERVAL" }, + { -1, NULL } + }; + const char* string = eglQueryString(display, EGL_VENDOR); + LOG("EGL vendor: %s", string); + string = eglQueryString(display, EGL_VERSION); + LOG("EGL version: %s", string); + string = eglQueryString(display, EGL_EXTENSIONS); + LOG("EGL extensions: %s", string); + LOG("EGL configuration attibutes:"); + for (int index = 0; kAttribs[index].attrib != -1; index++) { + EGLint value; + if (eglGetConfigAttrib(display, config, kAttribs[index].attrib, &value)) + LOG(" %s: %d", kAttribs[index].name, static_cast<int>(value)); + } +} +#endif + + +const QEvent::Type OrientationChangeEvent::mType = + static_cast<QEvent::Type>(QEvent::registerEventType()); + +static const MirDisplayOutput *find_active_output( + const MirDisplayConfiguration *conf) +{ + const MirDisplayOutput *output = NULL; + for (uint32_t d = 0; d < conf->num_outputs; d++) + { + const MirDisplayOutput *out = conf->outputs + d; + + if (out->used && + out->connected && + out->num_modes && + out->current_mode < out->num_modes) + { + output = out; + break; + } + } + + return output; +} + +QMirClientScreen::QMirClientScreen(MirConnection *connection) + : mFormat(QImage::Format_RGB32) + , mDepth(32) + , mSurfaceFormat() + , mEglDisplay(EGL_NO_DISPLAY) + , mEglConfig(nullptr) +{ + // Initialize EGL. + ASSERT(eglBindAPI(EGL_OPENGL_ES_API) == EGL_TRUE); + + mEglNativeDisplay = mir_connection_get_egl_native_display(connection); + ASSERT((mEglDisplay = eglGetDisplay(mEglNativeDisplay)) != EGL_NO_DISPLAY); + ASSERT(eglInitialize(mEglDisplay, nullptr, nullptr) == EGL_TRUE); + + // Configure EGL buffers format. + mSurfaceFormat.setRedBufferSize(8); + mSurfaceFormat.setGreenBufferSize(8); + mSurfaceFormat.setBlueBufferSize(8); + mSurfaceFormat.setAlphaBufferSize(8); + mSurfaceFormat.setDepthBufferSize(24); + mSurfaceFormat.setStencilBufferSize(8); + if (!qEnvironmentVariableIsEmpty("QTUBUNTU_MULTISAMPLE")) { + mSurfaceFormat.setSamples(4); + DLOG("ubuntumirclient: setting MSAA to 4 samples"); + } +#ifdef QTUBUNTU_USE_OPENGL + mSurfaceFormat.setRenderableType(QSurfaceFormat::OpenGL); +#else + mSurfaceFormat.setRenderableType(QSurfaceFormat::OpenGLES); +#endif + mEglConfig = q_configFromGLFormat(mEglDisplay, mSurfaceFormat, true); + + #if !defined(QT_NO_DEBUG) + printEglConfig(mEglDisplay, mEglConfig); + #endif + + // Set vblank swap interval. + int swapInterval = kSwapInterval; + QByteArray swapIntervalString = qgetenv("QTUBUNTU_SWAPINTERVAL"); + if (!swapIntervalString.isEmpty()) { + bool ok; + swapInterval = swapIntervalString.toInt(&ok); + if (!ok) + swapInterval = kSwapInterval; + } + DLOG("ubuntumirclient: setting swap interval to %d", swapInterval); + eglSwapInterval(mEglDisplay, swapInterval); + + // Get screen resolution. + auto configDeleter = [](MirDisplayConfiguration *config) { mir_display_config_destroy(config); }; + using configUp = std::unique_ptr<MirDisplayConfiguration, decltype(configDeleter)>; + configUp displayConfig(mir_connection_create_display_config(connection), configDeleter); + ASSERT(displayConfig != nullptr); + + auto const displayOutput = find_active_output(displayConfig.get()); + ASSERT(displayOutput != nullptr); + + const MirDisplayMode *mode = &displayOutput->modes[displayOutput->current_mode]; + const int kScreenWidth = mode->horizontal_resolution; + const int kScreenHeight = mode->vertical_resolution; + DASSERT(kScreenWidth > 0 && kScreenHeight > 0); + + DLOG("ubuntumirclient: screen resolution: %dx%d", kScreenWidth, kScreenHeight); + + mGeometry = QRect(0, 0, kScreenWidth, kScreenHeight); + + DLOG("QQMirClientScreen::QQMirClientScreen (this=%p)", this); + + // Set the default orientation based on the initial screen dimmensions. + mNativeOrientation = (mGeometry.width() >= mGeometry.height()) ? Qt::LandscapeOrientation : Qt::PortraitOrientation; + + // If it's a landscape device (i.e. some tablets), start in landscape, otherwise portrait + mCurrentOrientation = (mNativeOrientation == Qt::LandscapeOrientation) ? Qt::LandscapeOrientation : Qt::PortraitOrientation; +} + +QMirClientScreen::~QMirClientScreen() +{ + eglTerminate(mEglDisplay); +} + +void QMirClientScreen::customEvent(QEvent* event) { + DASSERT(QThread::currentThread() == thread()); + + OrientationChangeEvent* oReadingEvent = static_cast<OrientationChangeEvent*>(event); + switch (oReadingEvent->mOrientation) { + case OrientationChangeEvent::LeftUp: { + mCurrentOrientation = (screen()->primaryOrientation() == Qt::LandscapeOrientation) ? + Qt::InvertedPortraitOrientation : Qt::LandscapeOrientation; + break; + } + case OrientationChangeEvent::TopUp: { + mCurrentOrientation = (screen()->primaryOrientation() == Qt::LandscapeOrientation) ? + Qt::LandscapeOrientation : Qt::PortraitOrientation; + break; + } + case OrientationChangeEvent::RightUp: { + mCurrentOrientation = (screen()->primaryOrientation() == Qt::LandscapeOrientation) ? + Qt::PortraitOrientation : Qt::InvertedLandscapeOrientation; + break; + } + case OrientationChangeEvent::TopDown: { + mCurrentOrientation = (screen()->primaryOrientation() == Qt::LandscapeOrientation) ? + Qt::InvertedLandscapeOrientation : Qt::InvertedPortraitOrientation; + break; + } + default: { + DLOG("QMirClientScreen::customEvent - Unknown orientation."); + return; + } + } + + // Raise the event signal so that client apps know the orientation changed + DLOG("QMirClientScreen::customEvent - handling orientation change to %s", orientationToStr(mCurrentOrientation)); + QWindowSystemInterface::handleScreenOrientationChange(screen(), mCurrentOrientation); +} + +void QMirClientScreen::handleWindowSurfaceResize(int windowWidth, int windowHeight) +{ + if ((windowWidth > windowHeight && mGeometry.width() < mGeometry.height()) + || (windowWidth < windowHeight && mGeometry.width() > mGeometry.height())) { + + // The window aspect ratio differ's from the screen one. This means that + // unity8 has rotated the window in its scene. + // As there's no way to express window rotation in Qt's API, we have + // Flip QScreen's dimensions so that orientation properties match + // (primaryOrientation particularly). + // FIXME: This assumes a phone scenario. Won't work, or make sense, + // on the desktop + + QRect currGeometry = mGeometry; + mGeometry.setWidth(currGeometry.height()); + mGeometry.setHeight(currGeometry.width()); + + DLOG("QMirClientScreen::handleWindowSurfaceResize - new screen geometry (w=%d, h=%d)", + mGeometry.width(), mGeometry.height()); + QWindowSystemInterface::handleScreenGeometryChange(screen(), + mGeometry /* newGeometry */, + mGeometry /* newAvailableGeometry */); + + if (mGeometry.width() < mGeometry.height()) { + mCurrentOrientation = Qt::PortraitOrientation; + } else { + mCurrentOrientation = Qt::LandscapeOrientation; + } + DLOG("QMirClientScreen::handleWindowSurfaceResize - new orientation %s",orientationToStr(mCurrentOrientation)); + QWindowSystemInterface::handleScreenOrientationChange(screen(), mCurrentOrientation); + } +} diff --git a/src/plugins/platforms/mirclient/qmirclientscreen.h b/src/plugins/platforms/mirclient/qmirclientscreen.h new file mode 100644 index 0000000000..5d9325354f --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientscreen.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QMIRCLIENTSCREEN_H +#define QMIRCLIENTSCREEN_H + +#include <qpa/qplatformscreen.h> +#include <QSurfaceFormat> +#include <EGL/egl.h> + +struct MirConnection; + +class QMirClientScreen : public QObject, public QPlatformScreen +{ + Q_OBJECT +public: + QMirClientScreen(MirConnection *connection); + virtual ~QMirClientScreen(); + + // QPlatformScreen methods. + QImage::Format format() const override { return mFormat; } + int depth() const override { return mDepth; } + QRect geometry() const override { return mGeometry; } + QRect availableGeometry() const override { return mGeometry; } + Qt::ScreenOrientation nativeOrientation() const override { return mNativeOrientation; } + Qt::ScreenOrientation orientation() const override { return mNativeOrientation; } + + // New methods. + QSurfaceFormat surfaceFormat() const { return mSurfaceFormat; } + EGLDisplay eglDisplay() const { return mEglDisplay; } + EGLConfig eglConfig() const { return mEglConfig; } + EGLNativeDisplayType eglNativeDisplay() const { return mEglNativeDisplay; } + void handleWindowSurfaceResize(int width, int height); + + // QObject methods. + void customEvent(QEvent* event); + +private: + QRect mGeometry; + Qt::ScreenOrientation mNativeOrientation; + Qt::ScreenOrientation mCurrentOrientation; + QImage::Format mFormat; + int mDepth; + QSurfaceFormat mSurfaceFormat; + EGLDisplay mEglDisplay; + EGLConfig mEglConfig; + EGLNativeDisplayType mEglNativeDisplay; +}; + +#endif // QMIRCLIENTSCREEN_H diff --git a/src/plugins/platforms/mirclient/qmirclienttheme.cpp b/src/plugins/platforms/mirclient/qmirclienttheme.cpp new file mode 100644 index 0000000000..c15da23945 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclienttheme.cpp @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qmirclienttheme.h" + +#include <QtCore/QVariant> + +const char *QMirClientTheme::name = "ubuntu"; + +QMirClientTheme::QMirClientTheme() +{ +} + +QMirClientTheme::~QMirClientTheme() +{ +} + +QVariant QMirClientTheme::themeHint(ThemeHint hint) const +{ + if (hint == QPlatformTheme::SystemIconThemeName) { + QByteArray iconTheme = qgetenv("QTUBUNTU_ICON_THEME"); + if (iconTheme.isEmpty()) { + return QVariant(QStringLiteral("ubuntu-mobile")); + } else { + return QVariant(QString(iconTheme)); + } + } else { + return QGenericUnixTheme::themeHint(hint); + } +} diff --git a/src/plugins/platforms/mirclient/qmirclienttheme.h b/src/plugins/platforms/mirclient/qmirclienttheme.h new file mode 100644 index 0000000000..8f330395a0 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclienttheme.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QMIRCLIENTTHEME_H +#define QMIRCLIENTTHEME_H + +#include <QtPlatformSupport/private/qgenericunixthemes_p.h> + +class QMirClientTheme : public QGenericUnixTheme +{ +public: + static const char* name; + QMirClientTheme(); + virtual ~QMirClientTheme(); + + // From QPlatformTheme + QVariant themeHint(ThemeHint hint) const override; +}; + +#endif // QMIRCLIENTTHEME_H diff --git a/src/plugins/platforms/mirclient/qmirclientwindow.cpp b/src/plugins/platforms/mirclient/qmirclientwindow.cpp new file mode 100644 index 0000000000..3d1e5377e5 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientwindow.cpp @@ -0,0 +1,454 @@ +/**************************************************************************** +** +** Copyright (C) 2014-2015 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +// Local +#include "qmirclientclipboard.h" +#include "qmirclientinput.h" +#include "qmirclientwindow.h" +#include "qmirclientscreen.h" +#include "qmirclientlogging.h" + +// Qt +#include <qpa/qwindowsysteminterface.h> +#include <qpa/qwindowsysteminterface.h> +#include <QMutex> +#include <QMutexLocker> +#include <QSize> +#include <QtMath> + +// Platform API +#include <ubuntu/application/instance.h> + +#include <EGL/egl.h> + +#define IS_OPAQUE_FLAG 1 + +namespace +{ +MirSurfaceState qtWindowStateToMirSurfaceState(Qt::WindowState state) +{ + switch (state) { + case Qt::WindowNoState: + return mir_surface_state_restored; + + case Qt::WindowFullScreen: + return mir_surface_state_fullscreen; + + case Qt::WindowMaximized: + return mir_surface_state_maximized; + + case Qt::WindowMinimized: + return mir_surface_state_minimized; + + default: + LOG("Unexpected Qt::WindowState: %d", state); + return mir_surface_state_restored; + } +} + +#if !defined(QT_NO_DEBUG) +const char *qtWindowStateToStr(Qt::WindowState state) +{ + switch (state) { + case Qt::WindowNoState: + return "NoState"; + + case Qt::WindowFullScreen: + return "FullScreen"; + + case Qt::WindowMaximized: + return "Maximized"; + + case Qt::WindowMinimized: + return "Minimized"; + + default: + return "!?"; + } +} +#endif + +} // anonymous namespace + +class QMirClientWindowPrivate +{ +public: + void createEGLSurface(EGLNativeWindowType nativeWindow); + void destroyEGLSurface(); + int panelHeight(); + + QMirClientScreen* screen; + EGLSurface eglSurface; + WId id; + QMirClientInput* input; + Qt::WindowState state; + MirConnection *connection; + MirSurface* surface; + QSize bufferSize; + QMutex mutex; + QSharedPointer<QMirClientClipboard> clipboard; +}; + +static void eventCallback(MirSurface* surface, const MirEvent *event, void* context) +{ + (void) surface; + DASSERT(context != NULL); + QMirClientWindow* platformWindow = static_cast<QMirClientWindow*>(context); + platformWindow->priv()->input->postEvent(platformWindow, event); +} + +static void surfaceCreateCallback(MirSurface* surface, void* context) +{ + DASSERT(context != NULL); + QMirClientWindow* platformWindow = static_cast<QMirClientWindow*>(context); + platformWindow->priv()->surface = surface; + + mir_surface_set_event_handler(surface, eventCallback, context); +} + +QMirClientWindow::QMirClientWindow(QWindow* w, QSharedPointer<QMirClientClipboard> clipboard, QMirClientScreen* screen, + QMirClientInput* input, MirConnection* connection) + : QObject(nullptr), QPlatformWindow(w) +{ + DASSERT(screen != NULL); + + d = new QMirClientWindowPrivate; + d->screen = screen; + d->eglSurface = EGL_NO_SURFACE; + d->input = input; + d->state = window()->windowState(); + d->connection = connection; + d->clipboard = clipboard; + + static int id = 1; + d->id = id++; + + // Use client geometry if set explicitly, use available screen geometry otherwise. + QPlatformWindow::setGeometry(window()->geometry().isValid() && window()->geometry() != screen->geometry() ? + window()->geometry() : screen->availableGeometry()); + createWindow(); + DLOG("QMirClientWindow::QMirClientWindow (this=%p, w=%p, screen=%p, input=%p)", this, w, screen, input); +} + +QMirClientWindow::~QMirClientWindow() +{ + DLOG("QMirClientWindow::~QMirClientWindow"); + d->destroyEGLSurface(); + + mir_surface_release_sync(d->surface); + + delete d; +} + +void QMirClientWindowPrivate::createEGLSurface(EGLNativeWindowType nativeWindow) +{ + DLOG("QMirClientWindowPrivate::createEGLSurface (this=%p, nativeWindow=%p)", + this, reinterpret_cast<void*>(nativeWindow)); + + eglSurface = eglCreateWindowSurface(screen->eglDisplay(), screen->eglConfig(), + nativeWindow, nullptr); + + DASSERT(eglSurface != EGL_NO_SURFACE); +} + +void QMirClientWindowPrivate::destroyEGLSurface() +{ + DLOG("QMirClientWindowPrivate::destroyEGLSurface (this=%p)", this); + if (eglSurface != EGL_NO_SURFACE) { + eglDestroySurface(screen->eglDisplay(), eglSurface); + eglSurface = EGL_NO_SURFACE; + } +} + +// FIXME - in order to work around https://bugs.launchpad.net/mir/+bug/1346633 +// we need to guess the panel height (3GU + 2DP) +int QMirClientWindowPrivate::panelHeight() +{ + if (qEnvironmentVariableIsSet("QT_MIRCLIENT_IGNORE_PANEL")) + return 0; + const int defaultGridUnit = 8; + int gridUnit = defaultGridUnit; + QByteArray gridUnitString = qgetenv("GRID_UNIT_PX"); + if (!gridUnitString.isEmpty()) { + bool ok; + gridUnit = gridUnitString.toInt(&ok); + if (!ok) { + gridUnit = defaultGridUnit; + } + } + qreal densityPixelRatio = static_cast<qreal>(gridUnit) / defaultGridUnit; + return gridUnit * 3 + qFloor(densityPixelRatio) * 2; +} + +namespace +{ +static MirPixelFormat +mir_choose_default_pixel_format(MirConnection *connection) +{ + MirPixelFormat format[mir_pixel_formats]; + unsigned int nformats; + + mir_connection_get_available_surface_formats(connection, + format, mir_pixel_formats, &nformats); + + return format[0]; +} +} + +void QMirClientWindow::createWindow() +{ + DLOG("QMirClientWindow::createWindow (this=%p)", this); + + // FIXME: remove this remnant of an old platform-api enum - needs ubuntu-keyboard update + const int SCREEN_KEYBOARD_ROLE = 7; + // Get surface role and flags. + QVariant roleVariant = window()->property("role"); + int role = roleVariant.isValid() ? roleVariant.toUInt() : 1; // 1 is the default role for apps. + QVariant opaqueVariant = window()->property("opaque"); + uint flags = opaqueVariant.isValid() ? + opaqueVariant.toUInt() ? static_cast<uint>(IS_OPAQUE_FLAG) : 0 : 0; + + // FIXME(loicm) Opaque flag is forced for now for non-system sessions (applications) for + // performance reasons. + flags |= static_cast<uint>(IS_OPAQUE_FLAG); + + const QByteArray title = (!window()->title().isNull()) ? window()->title().toUtf8() : "Window 1"; // legacy title + const int panelHeight = d->panelHeight(); + +#if !defined(QT_NO_DEBUG) + LOG("panelHeight: '%d'", panelHeight); + LOG("role: '%d'", role); + LOG("flags: '%s'", (flags & static_cast<uint>(1)) ? "Opaque" : "NotOpaque"); + LOG("title: '%s'", title.constData()); +#endif + + // Get surface geometry. + QRect geometry; + if (d->state == Qt::WindowFullScreen) { + printf("QMirClientWindow - fullscreen geometry\n"); + geometry = screen()->geometry(); + } else if (d->state == Qt::WindowMaximized) { + printf("QMirClientWindow - maximized geometry\n"); + geometry = screen()->availableGeometry(); + /* + * FIXME: Autopilot relies on being able to convert coordinates relative of the window + * into absolute screen coordinates. Mir does not allow this, see bug lp:1346633 + * Until there's a correct way to perform this transformation agreed, this horrible hack + * guesses the transformation heuristically. + * + * Assumption: this method only used on phone devices! + */ + geometry.setY(panelHeight); + } else { + printf("QMirClientWindow - regular geometry\n"); + geometry = this->geometry(); + geometry.setY(panelHeight); + } + + DLOG("[ubuntumirclient QPA] creating surface at (%d, %d) with size (%d, %d) with title '%s'\n", + geometry.x(), geometry.y(), geometry.width(), geometry.height(), title.data()); + + MirSurfaceSpec *spec; + if (role == SCREEN_KEYBOARD_ROLE) + { + spec = mir_connection_create_spec_for_input_method(d->connection, geometry.width(), + geometry.height(), mir_choose_default_pixel_format(d->connection)); + } + else + { + spec = mir_connection_create_spec_for_normal_surface(d->connection, geometry.width(), + geometry.height(), mir_choose_default_pixel_format(d->connection)); + } + mir_surface_spec_set_name(spec, title.data()); + + // Create platform window + mir_wait_for(mir_surface_create(spec, surfaceCreateCallback, this)); + mir_surface_spec_release(spec); + + DASSERT(d->surface != NULL); + d->createEGLSurface((EGLNativeWindowType)mir_buffer_stream_get_egl_native_window(mir_surface_get_buffer_stream(d->surface))); + + if (d->state == Qt::WindowFullScreen) { + // TODO: We could set this on creation once surface spec supports it (mps already up) + mir_wait_for(mir_surface_set_state(d->surface, mir_surface_state_fullscreen)); + } + + // Window manager can give us a final size different from what we asked for + // so let's check what we ended up getting + { + MirSurfaceParameters parameters; + mir_surface_get_parameters(d->surface, ¶meters); + + geometry.setWidth(parameters.width); + geometry.setHeight(parameters.height); + } + + DLOG("[ubuntumirclient QPA] created surface has size (%d, %d)", + geometry.width(), geometry.height()); + + // Assume that the buffer size matches the surface size at creation time + d->bufferSize = geometry.size(); + + // Tell Qt about the geometry. + QWindowSystemInterface::handleGeometryChange(window(), geometry); + QPlatformWindow::setGeometry(geometry); +} + +void QMirClientWindow::moveResize(const QRect& rect) +{ + (void) rect; + // TODO: Not yet supported by mir. +} + +void QMirClientWindow::handleSurfaceResize(int width, int height) +{ + QMutexLocker(&d->mutex); + LOG("QMirClientWindow::handleSurfaceResize(width=%d, height=%d)", width, height); + + // The current buffer size hasn't actually changed. so just render on it and swap + // buffers in the hope that the next buffer will match the surface size advertised + // in this event. + // But since this event is processed by a thread different from the one that swaps + // buffers, you can never know if this information is already outdated as there's + // no synchronicity whatsoever between the processing of resize events and the + // consumption of buffers. + if (d->bufferSize.width() != width || d->bufferSize.height() != height) { + QWindowSystemInterface::handleExposeEvent(window(), geometry()); + QWindowSystemInterface::flushWindowSystemEvents(); + } +} + +void QMirClientWindow::handleSurfaceFocusChange(bool focused) +{ + LOG("QMirClientWindow::handleSurfaceFocusChange(focused=%s)", focused ? "true" : "false"); + QWindow *activatedWindow = focused ? window() : nullptr; + + // System clipboard contents might have changed while this window was unfocused and wihtout + // this process getting notified about it because it might have been suspended (due to + // application lifecycle policies), thus unable to listen to any changes notified through + // D-Bus. + // Therefore let's ensure we are up to date with the system clipboard now that we are getting + // focused again. + if (focused) { + d->clipboard->requestDBusClipboardContents(); + } + + QWindowSystemInterface::handleWindowActivated(activatedWindow, Qt::ActiveWindowFocusReason); +} + +void QMirClientWindow::setWindowState(Qt::WindowState state) +{ + QMutexLocker(&d->mutex); + DLOG("QMirClientWindow::setWindowState (this=%p, %s)", this, qtWindowStateToStr(state)); + + if (state == d->state) + return; + + // TODO: Perhaps we should check if the states are applied? + mir_wait_for(mir_surface_set_state(d->surface, qtWindowStateToMirSurfaceState(state))); + d->state = state; +} + +void QMirClientWindow::setGeometry(const QRect& rect) +{ + DLOG("QMirClientWindow::setGeometry (this=%p)", this); + + bool doMoveResize; + + { + QMutexLocker(&d->mutex); + QPlatformWindow::setGeometry(rect); + doMoveResize = d->state != Qt::WindowFullScreen && d->state != Qt::WindowMaximized; + } + + if (doMoveResize) { + moveResize(rect); + } +} + +void QMirClientWindow::setVisible(bool visible) +{ + QMutexLocker(&d->mutex); + DLOG("QMirClientWindow::setVisible (this=%p, visible=%s)", this, visible ? "true" : "false"); + + if (visible) { + mir_wait_for(mir_surface_set_state(d->surface, qtWindowStateToMirSurfaceState(d->state))); + + QWindowSystemInterface::handleExposeEvent(window(), QRect()); + QWindowSystemInterface::flushWindowSystemEvents(); + } else { + // TODO: Use the new mir_surface_state_hidden state instead of mir_surface_state_minimized. + // Will have to change qtmir and unity8 for that. + mir_wait_for(mir_surface_set_state(d->surface, mir_surface_state_minimized)); + } +} + +void* QMirClientWindow::eglSurface() const +{ + return d->eglSurface; +} + +WId QMirClientWindow::winId() const +{ + return d->id; +} + +void QMirClientWindow::onBuffersSwapped_threadSafe(int newBufferWidth, int newBufferHeight) +{ + QMutexLocker(&d->mutex); + + bool sizeKnown = newBufferWidth > 0 && newBufferHeight > 0; + + if (sizeKnown && (d->bufferSize.width() != newBufferWidth || + d->bufferSize.height() != newBufferHeight)) { + + DLOG("QMirClientWindow::onBuffersSwapped_threadSafe - buffer size changed from (%d,%d) to (%d,%d)", + d->bufferSize.width(), d->bufferSize.height(), newBufferWidth, newBufferHeight); + + d->bufferSize.rwidth() = newBufferWidth; + d->bufferSize.rheight() = newBufferHeight; + + QRect newGeometry; + + newGeometry = geometry(); + newGeometry.setWidth(d->bufferSize.width()); + newGeometry.setHeight(d->bufferSize.height()); + + QPlatformWindow::setGeometry(newGeometry); + QWindowSystemInterface::handleGeometryChange(window(), newGeometry, QRect()); + } +} diff --git a/src/plugins/platforms/mirclient/qmirclientwindow.h b/src/plugins/platforms/mirclient/qmirclientwindow.h new file mode 100644 index 0000000000..f342669544 --- /dev/null +++ b/src/plugins/platforms/mirclient/qmirclientwindow.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2014-2015 Canonical, Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QMIRCLIENTWINDOW_H +#define QMIRCLIENTWINDOW_H + +#include <qpa/qplatformwindow.h> +#include <QSharedPointer> + +#include <mir_toolkit/mir_client_library.h> + +class QMirClientClipboard; +class QMirClientInput; +class QMirClientScreen; +class QMirClientWindowPrivate; + +class QMirClientWindow : public QObject, public QPlatformWindow +{ + Q_OBJECT +public: + QMirClientWindow(QWindow *w, QSharedPointer<QMirClientClipboard> clipboard, QMirClientScreen *screen, + QMirClientInput *input, MirConnection *mir_connection); + virtual ~QMirClientWindow(); + + // QPlatformWindow methods. + WId winId() const override; + void setGeometry(const QRect&) override; + void setWindowState(Qt::WindowState state) override; + void setVisible(bool visible) override; + + // New methods. + void* eglSurface() const; + void handleSurfaceResize(int width, int height); + void handleSurfaceFocusChange(bool focused); + void onBuffersSwapped_threadSafe(int newBufferWidth, int newBufferHeight); + + QMirClientWindowPrivate* priv() { return d; } + +private: + void createWindow(); + void moveResize(const QRect& rect); + + QMirClientWindowPrivate *d; +}; + +#endif // QMIRCLIENTWINDOW_H diff --git a/src/plugins/platforms/platforms.pro b/src/plugins/platforms/platforms.pro index 22d443733e..43bb04e318 100644 --- a/src/plugins/platforms/platforms.pro +++ b/src/plugins/platforms/platforms.pro @@ -40,3 +40,5 @@ contains(QT_CONFIG, linuxfb): SUBDIRS += linuxfb haiku { SUBDIRS += haiku } + +contains(QT_CONFIG, mirclient): SUBDIRS += mirclient diff --git a/src/plugins/platforms/qnx/qqnxabstractvirtualkeyboard.cpp b/src/plugins/platforms/qnx/qqnxabstractvirtualkeyboard.cpp index e5c853dad8..04e264860e 100644 --- a/src/plugins/platforms/qnx/qqnxabstractvirtualkeyboard.cpp +++ b/src/plugins/platforms/qnx/qqnxabstractvirtualkeyboard.cpp @@ -124,4 +124,25 @@ void QQnxAbstractVirtualKeyboard::setLocale(const QLocale &locale) emit localeChanged(locale); } +QQnxAbstractVirtualKeyboard::EnterKeyType + QQnxAbstractVirtualKeyboard::qtEnterKeyTypeToQnx(Qt::EnterKeyType type) +{ + switch (type) { + case Qt::EnterKeyDone: + return Done; + case Qt::EnterKeyGo: + return Go; + case Qt::EnterKeyNext: + return Next; + case Qt::EnterKeySearch: + return Search; + case Qt::EnterKeySend: + return Send; + case Qt::EnterKeyDefault: + case Qt::EnterKeyReturn: + case Qt::EnterKeyPrevious: // unsupported + return DefaultReturn; + } +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/qnx/qqnxabstractvirtualkeyboard.h b/src/plugins/platforms/qnx/qqnxabstractvirtualkeyboard.h index 8bf8313000..2fa2ed7291 100644 --- a/src/plugins/platforms/qnx/qqnxabstractvirtualkeyboard.h +++ b/src/plugins/platforms/qnx/qqnxabstractvirtualkeyboard.h @@ -74,6 +74,8 @@ public: KeyboardMode keyboardMode() const { return m_keyboardMode; } EnterKeyType enterKeyType() const { return m_enterKeyType; } + static EnterKeyType qtEnterKeyTypeToQnx(Qt::EnterKeyType type); + Q_SIGNALS: void heightChanged(int height); void visibilityChanged(bool visible); diff --git a/src/plugins/platforms/qnx/qqnxinputcontext_imf.cpp b/src/plugins/platforms/qnx/qqnxinputcontext_imf.cpp index 3506de4bc0..ed0db82685 100644 --- a/src/plugins/platforms/qnx/qqnxinputcontext_imf.cpp +++ b/src/plugins/platforms/qnx/qqnxinputcontext_imf.cpp @@ -1384,13 +1384,17 @@ void QQnxInputContext::setFocusObject(QObject *object) if (hasSession()) dispatchFocusLossEvent(); } else { - QInputMethodQueryEvent query(Qt::ImHints); + QInputMethodQueryEvent query(Qt::ImHints | Qt::ImEnterKeyType); QCoreApplication::sendEvent(object, &query); int inputHints = query.value(Qt::ImHints).toInt(); + Qt::EnterKeyType qtEnterKeyType = Qt::EnterKeyType(query.value(Qt::ImEnterKeyType).toInt()); dispatchFocusGainEvent(inputHints); m_virtualKeyboard.setInputHints(inputHints); + m_virtualKeyboard.setEnterKeyType( + QQnxAbstractVirtualKeyboard::qtEnterKeyTypeToQnx(qtEnterKeyType) + ); if (!m_inputPanelVisible) showInputPanel(); diff --git a/src/plugins/platforms/qnx/qqnxinputcontext_noimf.cpp b/src/plugins/platforms/qnx/qqnxinputcontext_noimf.cpp index 91ecffa2aa..3860cdf0db 100644 --- a/src/plugins/platforms/qnx/qqnxinputcontext_noimf.cpp +++ b/src/plugins/platforms/qnx/qqnxinputcontext_noimf.cpp @@ -172,11 +172,15 @@ void QQnxInputContext::setFocusObject(QObject *object) if (m_inputPanelVisible) hideInputPanel(); } else { - QInputMethodQueryEvent query(Qt::ImHints); + QInputMethodQueryEvent query(Qt::ImHints | Qt::ImEnterKeyType); QCoreApplication::sendEvent(object, &query); int inputHints = query.value(Qt::ImHints).toInt(); + Qt::EnterKeyType qtEnterKeyType = Qt::EnterKeyType(query.value(Qt::ImEnterKeyType).toInt()); m_virtualKeyboard.setInputHints(inputHints); + m_virtualKeyboard.setEnterKeyType( + QQnxAbstractVirtualKeyboard::qtEnterKeyTypeToQnx(qtEnterKeyType) + ); if (!m_inputPanelVisible) showInputPanel(); diff --git a/src/plugins/platforms/qnx/qqnxintegration.cpp b/src/plugins/platforms/qnx/qqnxintegration.cpp index 071bab7920..1c825dbbdd 100644 --- a/src/plugins/platforms/qnx/qqnxintegration.cpp +++ b/src/plugins/platforms/qnx/qqnxintegration.cpp @@ -95,7 +95,6 @@ #include <private/qsimpledrag_p.h> #include <QtCore/QDebug> -#include <QtCore/QHash> #include <errno.h> diff --git a/src/plugins/platforms/qnx/qqnxvirtualkeyboardpps.cpp b/src/plugins/platforms/qnx/qqnxvirtualkeyboardpps.cpp index 392d45c5b4..2c7a28e835 100644 --- a/src/plugins/platforms/qnx/qqnxvirtualkeyboardpps.cpp +++ b/src/plugins/platforms/qnx/qqnxvirtualkeyboardpps.cpp @@ -121,7 +121,7 @@ bool QQnxVirtualKeyboardPps::connect() if (m_fd == -1) { qVirtualKeyboardDebug() << Q_FUNC_INFO << ": Unable to open" << ms_PPSPath - << ":" << strerror(errno); + << ':' << strerror(errno); close(); return false; } diff --git a/src/plugins/platforms/windows/openglblacklists/default.json b/src/plugins/platforms/windows/openglblacklists/default.json index 23607523bd..1e003e2d15 100644 --- a/src/plugins/platforms/windows/openglblacklists/default.json +++ b/src/plugins/platforms/windows/openglblacklists/default.json @@ -4,7 +4,7 @@ "entries": [ { "id": 1, - "description": "Desktop OpenGL is unreliable on some Intel HD laptops (QTBUG-43263, QTBUG-42240)", + "description": "Desktop OpenGL is unreliable on some Intel HD laptops (QTBUG-43263)", "vendor_id": "0x8086", "device_id": [ "0x0A16" ], "os": { @@ -17,6 +17,55 @@ "features": [ "disable_desktopgl" ] + }, + { + "id": 2, + "description": "Intel Q965/Q963 - GMA 3000 has insufficient support of opengl and directx", + "vendor_id": "0x8086", + "device_id": [ "0x2992" ], + "os": { + "type": "win" + }, + "features": [ + "disable_desktopgl", + "disable_angle" + ] + }, + { + "id": 3, + "description": "No OpenGL on Intel G33/G31 (QTBUG-47522)", + "vendor_id": "0x8086", + "device_id": [ "0x29C2" ], + "os": { + "type": "win" + }, + "features": [ + "disable_desktopgl" + ] + }, + { + "id": 4, + "description": "Intel HD Graphics 3000 crashes when initializing the OpenGL driver (QTBUG-42240)", + "vendor_id": "0x8086", + "device_id": [ "0x0102", "0x0106", "0x010A", "0x0112", "0x0116", "0x0122", "0x0126" ], + "os": { + "type": "win" + }, + "features": [ + "disable_desktopgl" + ] + }, + { + "id": 5, + "description": "Intel GMA 3150 (QTBUG-43243), Mobile Intel 945GM (QTBUG-47435) crash", + "vendor_id": "0x8086", + "device_id": [ "0xA001", "0xA011", "0x27A0" ], + "os": { + "type": "win" + }, + "features": [ + "disable_desktopgl", "disable_angle" + ] } ] } diff --git a/src/plugins/platforms/windows/qwindowsbackingstore.cpp b/src/plugins/platforms/windows/qwindowsbackingstore.cpp index 16c278d9df..bd5c35037d 100644 --- a/src/plugins/platforms/windows/qwindowsbackingstore.cpp +++ b/src/plugins/platforms/windows/qwindowsbackingstore.cpp @@ -35,10 +35,10 @@ #include "qwindowswindow.h" #include "qwindowsnativeimage.h" #include "qwindowscontext.h" -#include "qwindowsscaling.h" #include <QtGui/QWindow> #include <QtGui/QPainter> +#include <private/qhighdpiscaling_p.h> #include <QtCore/QDebug> @@ -68,10 +68,12 @@ QPaintDevice *QWindowsBackingStore::paintDevice() return &m_image->image(); } -void QWindowsBackingStore::flushDp(QWindow *window, const QRect &br, const QPoint &offset) +void QWindowsBackingStore::flush(QWindow *window, const QRegion ®ion, + const QPoint &offset) { Q_ASSERT(window); + const QRect br = region.boundingRect(); if (QWindowsContext::verbose > 1) qCDebug(lcQpaBackingStore) << __FUNCTION__ << this << window << offset << br; QWindowsWindow *rw = QWindowsWindow::baseWindowOf(window); @@ -81,9 +83,9 @@ void QWindowsBackingStore::flushDp(QWindow *window, const QRect &br, const QPoin const Qt::WindowFlags flags = window->flags(); if ((flags & Qt::FramelessWindowHint) && QWindowsWindow::setWindowLayered(rw->handle(), flags, hasAlpha, rw->opacity()) && hasAlpha) { // Windows with alpha: Use blend function to update. - const QMargins marginsDP = rw->frameMarginsDp(); - const QRect r = rw->geometryDp() + marginsDP; - const QPoint frameOffset(marginsDP.left(), marginsDP.top()); + QRect r = QHighDpi::toNativePixels(window->frameGeometry(), window); + QPoint frameOffset(QHighDpi::toNativePixels(QPoint(window->frameMargins().left(), window->frameMargins().top()), + static_cast<const QWindow *>(Q_NULLPTR))); QRect dirtyRect = br.translated(offset + frameOffset); SIZE size = {r.width(), r.height()}; @@ -127,15 +129,14 @@ void QWindowsBackingStore::flushDp(QWindow *window, const QRect &br, const QPoin } } -void QWindowsBackingStore::resize(const QSize &sizeDip, const QRegion ®ionDip) +void QWindowsBackingStore::resize(const QSize &size, const QRegion ®ion) { - const QSize size = sizeDip * QWindowsScaling::factor(); if (m_image.isNull() || m_image->image().size() != size) { #ifndef QT_NO_DEBUG_OUTPUT if (QWindowsContext::verbose && lcQpaBackingStore().isDebugEnabled()) { qCDebug(lcQpaBackingStore) - << __FUNCTION__ << ' ' << window() << ' ' << size << ' ' << sizeDip << ' ' - << regionDip << " from: " << (m_image.isNull() ? QSize() : m_image->image().size()); + << __FUNCTION__ << ' ' << window() << ' ' << size << ' ' << region + << " from: " << (m_image.isNull() ? QSize() : m_image->image().size()); } #endif const QImage::Format format = window()->format().hasAlpha() ? @@ -144,10 +145,10 @@ void QWindowsBackingStore::resize(const QSize &sizeDip, const QRegion ®ionDip QWindowsNativeImage *oldwni = m_image.data(); QWindowsNativeImage *newwni = new QWindowsNativeImage(size.width(), size.height(), format); - if (oldwni && !regionDip.isEmpty()) { + if (oldwni && !region.isEmpty()) { const QImage &oldimg(oldwni->image()); QImage &newimg(newwni->image()); - QRegion staticRegion = QWindowsScaling::mapToNative(regionDip); + QRegion staticRegion(region); staticRegion &= QRect(0, 0, oldimg.width(), oldimg.height()); staticRegion &= QRect(0, 0, newimg.width(), newimg.height()); QPainter painter(&newimg); @@ -156,38 +157,36 @@ void QWindowsBackingStore::resize(const QSize &sizeDip, const QRegion ®ionDip painter.drawImage(rect, oldimg, rect); } - if (QWindowsScaling::isActive()) - newwni->setDevicePixelRatio(QWindowsScaling::factor()); m_image.reset(newwni); } } Q_GUI_EXPORT void qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &offset); -bool QWindowsBackingStore::scroll(const QRegion &areaDip, int dxDip, int dyDip) +bool QWindowsBackingStore::scroll(const QRegion &area, int dx, int dy) { if (m_image.isNull() || m_image->image().isNull()) return false; - const QPoint dp = QPoint(dxDip, dyDip) * QWindowsScaling::factor(); - const QVector<QRect> rects = areaDip.rects(); + const QVector<QRect> rects = area.rects(); + const QPoint offset(dx, dy); for (int i = 0; i < rects.size(); ++i) - qt_scrollRectInImage(m_image->image(), QWindowsScaling::mapToNative(rects.at(i)), dp); + qt_scrollRectInImage(m_image->image(), rects.at(i), offset); return true; } -void QWindowsBackingStore::beginPaint(const QRegion ®ionDip) +void QWindowsBackingStore::beginPaint(const QRegion ®ion) { if (QWindowsContext::verbose > 1) - qCDebug(lcQpaBackingStore) <<__FUNCTION__ << regionDip; + qCDebug(lcQpaBackingStore) <<__FUNCTION__ << region; if (m_image->image().hasAlphaChannel()) { QPainter p(&m_image->image()); p.setCompositionMode(QPainter::CompositionMode_Source); const QColor blank = Qt::transparent; - foreach (const QRect &r, regionDip.rects()) - p.fillRect(QWindowsScaling::mapToNative(r), blank); + foreach (const QRect &r, region.rects()) + p.fillRect(r, blank); } } diff --git a/src/plugins/platforms/windows/qwindowsbackingstore.h b/src/plugins/platforms/windows/qwindowsbackingstore.h index 41ad29babc..4badcf1b09 100644 --- a/src/plugins/platforms/windows/qwindowsbackingstore.h +++ b/src/plugins/platforms/windows/qwindowsbackingstore.h @@ -35,7 +35,6 @@ #define QWINDOWSBACKINGSTORE_H #include "qtwindows_additional.h" -#include "qwindowsscaling.h" #include <qpa/qplatformbackingstore.h> #include <QtCore/QScopedPointer> @@ -53,12 +52,7 @@ public: ~QWindowsBackingStore(); QPaintDevice *paintDevice() Q_DECL_OVERRIDE; - void flush(QWindow *window, const QRegion ®ion, const QPoint &offset) Q_DECL_OVERRIDE - { - flushDp(window, QWindowsScaling::mapToNative(region.boundingRect()), - offset * QWindowsScaling::factor()); - } - void flushDp(QWindow *window, const QRect &boundingRect, const QPoint &offset); + void flush(QWindow *window, const QRegion ®ion, const QPoint &offset) Q_DECL_OVERRIDE; void resize(const QSize &size, const QRegion &r) Q_DECL_OVERRIDE; bool scroll(const QRegion &area, int dx, int dy) Q_DECL_OVERRIDE; void beginPaint(const QRegion &) Q_DECL_OVERRIDE; diff --git a/src/plugins/platforms/windows/qwindowsclipboard.cpp b/src/plugins/platforms/windows/qwindowsclipboard.cpp index 925427ac30..25cfd12b44 100644 --- a/src/plugins/platforms/windows/qwindowsclipboard.cpp +++ b/src/plugins/platforms/windows/qwindowsclipboard.cpp @@ -35,7 +35,6 @@ #include "qwindowscontext.h" #include "qwindowsole.h" #include "qwindowsmime.h" -#include "qwindowsguieventdispatcher.h" #include <QtGui/QGuiApplication> #include <QtGui/QClipboard> @@ -48,6 +47,8 @@ #include <QtCore/QVariant> #include <QtCore/QUrl> +#include <QtPlatformSupport/private/qwindowsguieventdispatcher_p.h> + QT_BEGIN_NAMESPACE static const char formatTextPlainC[] = "text/plain"; @@ -69,6 +70,7 @@ static const char formatTextHtmlC[] = "text/html"; \ingroup qt-lighthouse-win */ +#ifndef QT_NO_DEBUG_STREAM static QDebug operator<<(QDebug d, const QMimeData *mimeData) { QDebugStateSaver saver(d); @@ -93,6 +95,7 @@ static QDebug operator<<(QDebug d, const QMimeData *mimeData) d << ')'; return d; } +#endif // !QT_NO_DEBUG_STREAM /*! \class QWindowsClipboardRetrievalMimeData @@ -109,8 +112,11 @@ static QDebug operator<<(QDebug d, const QMimeData *mimeData) IDataObject *QWindowsClipboardRetrievalMimeData::retrieveDataObject() const { IDataObject * pDataObj = 0; - if (OleGetClipboard(&pDataObj) == S_OK) + if (OleGetClipboard(&pDataObj) == S_OK) { + if (QWindowsContext::verbose > 1) + qCDebug(lcQpaMime) << __FUNCTION__ << pDataObj; return pDataObj; + } return 0; } diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp index 00049bd0d6..5cda6379de 100644 --- a/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -36,7 +36,6 @@ #include "qwindowsintegration.h" #include "qwindowswindow.h" #include "qwindowskeymapper.h" -#include "qwindowsguieventdispatcher.h" #include "qwindowsmousehandler.h" #include "qtwindowsglobal.h" #include "qwindowsmime.h" @@ -52,7 +51,6 @@ #endif #include "qwindowsscreen.h" #include "qwindowstheme.h" -#include "qwindowsscaling.h" #include <QtGui/QWindow> #include <qpa/qwindowsysteminterface.h> @@ -67,6 +65,8 @@ #include <QtCore/QScopedArrayPointer> #include <QtCore/private/qsystemlibrary_p.h> +#include <QtPlatformSupport/private/qwindowsguieventdispatcher_p.h> + #include <stdlib.h> #include <stdio.h> #include <windowsx.h> @@ -893,6 +893,11 @@ QByteArray QWindowsContext::comErrorString(HRESULT hr) return result; } +static inline QWindowsInputContext *windowsInputContext() +{ + return qobject_cast<QWindowsInputContext *>(QWindowsIntegration::instance()->inputContext()); +} + /*! \brief Main windows procedure registered for windows. @@ -941,16 +946,29 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, return true; } } + if (et & QtWindows::InputMethodEventFlag) { + QWindowsInputContext *windowsInputContext = ::windowsInputContext(); + // Disable IME assuming this is a special implementation hooking into keyboard input. + // "Real" IME implementations should use a native event filter intercepting IME events. + if (!windowsInputContext) { + QWindowsInputContext::setWindowsImeEnabled(platformWindow, false); + return false; + } + switch (et) { + case QtWindows::InputMethodStartCompositionEvent: + return windowsInputContext->startComposition(hwnd); + case QtWindows::InputMethodCompositionEvent: + return windowsInputContext->composition(hwnd, lParam); + case QtWindows::InputMethodEndCompositionEvent: + return windowsInputContext->endComposition(hwnd); + case QtWindows::InputMethodRequest: + return windowsInputContext->handleIME_Request(wParam, lParam, result); + default: + break; + } + } // InputMethodEventFlag switch (et) { - case QtWindows::InputMethodStartCompositionEvent: - return QWindowsInputContext::instance()->startComposition(hwnd); - case QtWindows::InputMethodCompositionEvent: - return QWindowsInputContext::instance()->composition(hwnd, lParam); - case QtWindows::InputMethodEndCompositionEvent: - return QWindowsInputContext::instance()->endComposition(hwnd); - case QtWindows::InputMethodRequest: - return QWindowsInputContext::instance()->handleIME_Request(wParam, lParam, result); case QtWindows::GestureEvent: #if !defined(Q_OS_WINCE) && !defined(QT_NO_SESSIONMANAGER) return platformSessionManager()->isInteractionBlocked() ? true : d->m_mouseHandler.translateGestureEvent(platformWindow->window(), hwnd, et, msg, result); @@ -1025,11 +1043,13 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, } switch (et) { + case QtWindows::KeyboardLayoutChangeEvent: + if (QWindowsInputContext *wic = windowsInputContext()) + wic->handleInputLanguageChanged(wParam, lParam); // fallthrough intended. case QtWindows::KeyDownEvent: case QtWindows::KeyEvent: case QtWindows::InputMethodKeyEvent: case QtWindows::InputMethodKeyDownEvent: - case QtWindows::KeyboardLayoutChangeEvent: case QtWindows::AppCommandEvent: #if !defined(Q_OS_WINCE) && !defined(QT_NO_SESSIONMANAGER) return platformSessionManager()->isInteractionBlocked() ? true : d->m_keyMapper.translateKeyEvent(platformWindow->window(), hwnd, msg, result); @@ -1265,9 +1285,7 @@ bool QWindowsContext::handleContextMenuEvent(QWindow *window, const MSG &msg) } } - QWindowSystemInterface::handleContextMenuEvent(window, mouseTriggered, - pos / QWindowsScaling::factor(), - globalPos / QWindowsScaling::factor(), + QWindowSystemInterface::handleContextMenuEvent(window, mouseTriggered, pos, globalPos, QWindowsKeyMapper::queryKeyboardModifiers()); return true; } diff --git a/src/plugins/platforms/windows/qwindowscursor.cpp b/src/plugins/platforms/windows/qwindowscursor.cpp index 5f443f2675..c769eb04a4 100644 --- a/src/plugins/platforms/windows/qwindowscursor.cpp +++ b/src/plugins/platforms/windows/qwindowscursor.cpp @@ -36,7 +36,6 @@ #include "qwindowscontext.h" #include "qwindowswindow.h" #include "qwindowsscreen.h" -#include "qwindowsscaling.h" #include <QtGui/QBitmap> #include <QtGui/QImage> @@ -44,15 +43,17 @@ #include <QtGui/QGuiApplication> #include <QtGui/QScreen> #include <QtGui/private/qguiapplication_p.h> // getPixmapCursor() +#include <QtGui/private/qhighdpiscaling_p.h> #include <QtCore/QDebug> #include <QtCore/QScopedArrayPointer> -static void initResources() +static bool initResources() { #if !defined (Q_OS_WINCE) && !defined (QT_NO_IMAGEFORMAT_PNG) Q_INIT_RESOURCE(cursors); #endif + return true; } QT_BEGIN_NAMESPACE @@ -68,19 +69,14 @@ Q_GUI_EXPORT HBITMAP qt_createIconMask(const QBitmap &bitmap); \ingroup qt-lighthouse-win */ -QWindowsCursorCacheKey::QWindowsCursorCacheKey(const QCursor &c) - : shape(c.shape()), bitmapCacheKey(0), maskCacheKey(0) +QWindowsPixmapCursorCacheKey::QWindowsPixmapCursorCacheKey(const QCursor &c) + : bitmapCacheKey(c.pixmap().cacheKey()), maskCacheKey(0) { - if (shape == Qt::BitmapCursor) { - const qint64 pixmapCacheKey = c.pixmap().cacheKey(); - if (pixmapCacheKey) { - bitmapCacheKey = pixmapCacheKey; - } else { - Q_ASSERT(c.bitmap()); - Q_ASSERT(c.mask()); - bitmapCacheKey = c.bitmap()->cacheKey(); - maskCacheKey = c.mask()->cacheKey(); - } + if (!bitmapCacheKey) { + Q_ASSERT(c.bitmap()); + Q_ASSERT(c.mask()); + bitmapCacheKey = c.bitmap()->cacheKey(); + maskCacheKey = c.mask()->cacheKey(); } } @@ -98,9 +94,14 @@ QWindowsCursorCacheKey::QWindowsCursorCacheKey(const QCursor &c) \sa QWindowsWindowCursor */ -HCURSOR QWindowsCursor::createPixmapCursor(const QPixmap &pixmap, const QPoint &hotSpot) +HCURSOR QWindowsCursor::createPixmapCursor(QPixmap pixmap, const QPoint &hotSpot, qreal scaleFactor) { HCURSOR cur = 0; + scaleFactor /= pixmap.devicePixelRatioF(); + if (!qFuzzyCompare(scaleFactor, 1)) { + pixmap = pixmap.scaled((scaleFactor * QSizeF(pixmap.size())).toSize(), + Qt::KeepAspectRatio, Qt::SmoothTransformation); + } QBitmap mask = pixmap.mask(); if (mask.isNull()) { mask = QBitmap(pixmap.size()); @@ -207,7 +208,44 @@ static HCURSOR createBitmapCursor(const QImage &bbits, const QImage &mbits, #endif } -static inline QSize systemCursorSize() { return QSize(GetSystemMetrics(SM_CXCURSOR), GetSystemMetrics(SM_CYCURSOR)); } +// Create a cursor from image and mask of the format QImage::Format_Mono. +static HCURSOR createBitmapCursor(const QCursor &cursor, qreal scaleFactor = 1) +{ + Q_ASSERT(cursor.shape() == Qt::BitmapCursor && cursor.bitmap()); + QImage bbits = cursor.bitmap()->toImage(); + QImage mbits = cursor.mask()->toImage(); + scaleFactor /= bbits.devicePixelRatioF(); + if (!qFuzzyCompare(scaleFactor, 1)) { + const QSize scaledSize = (QSizeF(bbits.size()) * scaleFactor).toSize(); + bbits = bbits.scaled(scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); + mbits = mbits.scaled(scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); + } + bbits = bbits.convertToFormat(QImage::Format_Mono); + mbits = mbits.convertToFormat(QImage::Format_Mono); + const bool invb = bbits.colorCount() > 1 && qGray(bbits.color(0)) < qGray(bbits.color(1)); + const bool invm = mbits.colorCount() > 1 && qGray(mbits.color(0)) < qGray(mbits.color(1)); + return createBitmapCursor(bbits, mbits, cursor.hotSpot(), invb, invm); +} + +static QSize systemCursorSize(const QPlatformScreen *screen = Q_NULLPTR) +{ + const QSize primaryScreenCursorSize(GetSystemMetrics(SM_CXCURSOR), GetSystemMetrics(SM_CYCURSOR)); + if (screen) { + // Correct the size if the DPI value of the screen differs from + // that of the primary screen. + if (const QScreen *primaryQScreen = QGuiApplication::primaryScreen()) { + const QPlatformScreen *primaryScreen = primaryQScreen->handle(); + if (screen != primaryScreen) { + const qreal logicalDpi = screen->logicalDpi().first; + const qreal primaryScreenLogicalDpi = primaryScreen->logicalDpi().first; + if (!qFuzzyCompare(logicalDpi, primaryScreenLogicalDpi)) + return (QSizeF(primaryScreenCursorSize) * logicalDpi / primaryScreenLogicalDpi).toSize(); + } + } + } + return primaryScreenCursorSize; +} + static inline QSize standardCursorSize() { return QSize(32, 32); } #if defined (Q_OS_WINCE) || defined (QT_NO_IMAGEFORMAT_PNG) @@ -216,7 +254,7 @@ static inline QSize standardCursorSize() { return QSize(32, 32); } // createBitmapCursor() only work for standard sizes (32,48,64...), which does // not work when scaling the 16x16 openhand cursor bitmaps to 150% (resulting // in a non-standard 24x24 size). -static QCursor createPixmapCursorFromData(const QSize &systemCursorSize, +static QWindowsCursor::PixmapCursor createPixmapCursorFromData(const QSize &systemCursorSize, // The cursor size the bitmap is targeted for const QSize &bitmapTargetCursorSize, // The actual size of the bitmap data @@ -234,10 +272,11 @@ static QCursor createPixmapCursorFromData(const QSize &systemCursorSize, rawImage = rawImage.transformed(transform, Qt::SmoothTransformation); } const QPoint hotSpot(rawImage.width() / 2, rawImage.height() / 2); - return QCursor(rawImage, hotSpot.x(), hotSpot.y()); + return QWindowsCursor::PixmapCursor(rawImage, hotSpot); } -QCursor QWindowsCursor::customCursor(Qt::CursorShape cursorShape) +QWindowsCursor::PixmapCursor QWindowsCursor::customCursor(Qt::CursorShape cursorShape, + const QPlatformScreen *screen) { // Non-standard Windows cursors are created from bitmaps static const uchar vsplit_bits[] = { @@ -405,22 +444,22 @@ QCursor QWindowsCursor::customCursor(Qt::CursorShape cursorShape) switch (cursorShape) { case Qt::SplitVCursor: - return createPixmapCursorFromData(systemCursorSize(), standardCursorSize(), 32, vsplit_bits, vsplitm_bits); + return createPixmapCursorFromData(systemCursorSize(screen), standardCursorSize(), 32, vsplit_bits, vsplitm_bits); case Qt::SplitHCursor: - return createPixmapCursorFromData(systemCursorSize(), standardCursorSize(), 32, hsplit_bits, hsplitm_bits); + return createPixmapCursorFromData(systemCursorSize(screen), standardCursorSize(), 32, hsplit_bits, hsplitm_bits); case Qt::OpenHandCursor: - return createPixmapCursorFromData(systemCursorSize(), standardCursorSize(), 16, openhand_bits, openhandm_bits); + return createPixmapCursorFromData(systemCursorSize(screen), standardCursorSize(), 16, openhand_bits, openhandm_bits); case Qt::ClosedHandCursor: - return createPixmapCursorFromData(systemCursorSize(), standardCursorSize(), 16, closedhand_bits, closedhandm_bits); + return createPixmapCursorFromData(systemCursorSize(screen), standardCursorSize(), 16, closedhand_bits, closedhandm_bits); case Qt::DragCopyCursor: - return QCursor(QPixmap(copyDragCursorXpmC), 0, 0); + return QWindowsCursor::PixmapCursor(QPixmap(copyDragCursorXpmC), QPoint(0, 0)); case Qt::DragMoveCursor: - return QCursor(QPixmap(moveDragCursorXpmC), 0, 0); + return QWindowsCursor::PixmapCursor(QPixmap(moveDragCursorXpmC), QPoint(0, 0)); case Qt::DragLinkCursor: - return QCursor(QPixmap(linkDragCursorXpmC), 0, 0); + return QWindowsCursor::PixmapCursor(QPixmap(linkDragCursorXpmC), QPoint(0, 0)); } - return QCursor(); + return QWindowsCursor::PixmapCursor(); } #else // Q_OS_WINCE || QT_NO_IMAGEFORMAT_PNG struct QWindowsCustomPngCursor { @@ -431,7 +470,7 @@ struct QWindowsCustomPngCursor { int hotSpotY; }; -QCursor QWindowsCursor::customCursor(Qt::CursorShape cursorShape) +QWindowsCursor::PixmapCursor QWindowsCursor::customCursor(Qt::CursorShape cursorShape, const QPlatformScreen *screen) { static const QWindowsCustomPngCursor pngCursors[] = { { Qt::SplitVCursor, 32, "splitvcursor_32.png", 11, 11 }, @@ -457,14 +496,14 @@ QCursor QWindowsCursor::customCursor(Qt::CursorShape cursorShape) { Qt::DragLinkCursor, 64, "draglinkcursor_64.png", 0, 0 } }; - const int cursorSize = GetSystemMetrics(SM_CXCURSOR); + const QSize cursorSize = systemCursorSize(screen); const QWindowsCustomPngCursor *sEnd = pngCursors + sizeof(pngCursors) / sizeof(pngCursors[0]); const QWindowsCustomPngCursor *bestFit = 0; int sizeDelta = INT_MAX; for (const QWindowsCustomPngCursor *s = pngCursors; s < sEnd; ++s) { if (s->shape != cursorShape) continue; - const int currentSizeDelta = qMax(s->size, cursorSize) - qMin(s->size, cursorSize); + const int currentSizeDelta = qMax(s->size, cursorSize.width()) - qMin(s->size, cursorSize.width()); if (currentSizeDelta < sizeDelta) { bestFit = s; if (currentSizeDelta == 0) @@ -474,11 +513,11 @@ QCursor QWindowsCursor::customCursor(Qt::CursorShape cursorShape) } if (!bestFit) - return QCursor(); + return PixmapCursor(); const QPixmap rawImage(QStringLiteral(":/qt-project.org/windows/cursors/images/") + QString::fromLatin1(bestFit->fileName)); - return QCursor(rawImage, bestFit->hotSpotX, bestFit->hotSpotY); + return PixmapCursor(rawImage, QPoint(bestFit->hotSpotX, bestFit->hotSpotY)); } #endif // Q_OS_WINCE || QT_NO_IMAGEFORMAT_PNG @@ -487,8 +526,10 @@ struct QWindowsStandardCursorMapping { LPCWSTR resource; }; -HCURSOR QWindowsCursor::createSystemCursor(const QCursor &c) +HCURSOR QWindowsCursor::createCursorFromShape(Qt::CursorShape cursorShape, const QPlatformScreen *screen) { + Q_ASSERT(cursorShape != Qt::BitmapCursor); + static const QWindowsStandardCursorMapping standardCursors[] = { { Qt::ArrowCursor, IDC_ARROW}, { Qt::UpArrowCursor, IDC_UPARROW }, @@ -506,20 +547,9 @@ HCURSOR QWindowsCursor::createSystemCursor(const QCursor &c) { Qt::PointingHandCursor, IDC_HAND } }; - const Qt::CursorShape cursorShape = c.shape(); switch (cursorShape) { - case Qt::BitmapCursor: { - const QPixmap pixmap = c.pixmap(); - if (!pixmap.isNull()) - return QWindowsCursor::createPixmapCursor(pixmap, c.hotSpot()); - const QImage bbits = c.bitmap()->toImage().convertToFormat(QImage::Format_Mono); - const QImage mbits = c.mask()->toImage().convertToFormat(QImage::Format_Mono); - const bool invb = bbits.colorCount() > 1 && qGray(bbits.color(0)) < qGray(bbits.color(1)); - const bool invm = mbits.colorCount() > 1 && qGray(mbits.color(0)) < qGray(mbits.color(1)); - return createBitmapCursor(bbits, mbits, c.hotSpot(), invb, invm); - } case Qt::BlankCursor: { - QImage blank = QImage(systemCursorSize(), QImage::Format_Mono); + QImage blank = QImage(systemCursorSize(screen), QImage::Format_Mono); blank.fill(0); // ignore color table return createBitmapCursor(blank, blank); } @@ -530,7 +560,7 @@ HCURSOR QWindowsCursor::createSystemCursor(const QCursor &c) case Qt::DragCopyCursor: case Qt::DragMoveCursor: case Qt::DragLinkCursor: - return createSystemCursor(customCursor(cursorShape)); + return QWindowsCursor::createPixmapCursor(customCursor(cursorShape, screen)); default: break; } @@ -555,44 +585,52 @@ HCURSOR QWindowsCursor::createSystemCursor(const QCursor &c) \brief Return cached standard cursor resources or create new ones. */ -QWindowsWindowCursor QWindowsCursor::standardWindowCursor(Qt::CursorShape shape) +CursorHandlePtr QWindowsCursor::standardWindowCursor(Qt::CursorShape shape) { - const QWindowsCursorCacheKey key(shape); - CursorCache::iterator it = m_cursorCache.find(key); - if (it == m_cursorCache.end()) - it = m_cursorCache.insert(key, QWindowsWindowCursor(QCursor(shape))); - return it.value(); + StandardCursorCache::Iterator it = m_standardCursorCache.find(shape); + if (it == m_standardCursorCache.end()) { + if (const HCURSOR hc = QWindowsCursor::createCursorFromShape(shape, m_screen)) + it = m_standardCursorCache.insert(shape, CursorHandlePtr(new CursorHandle(hc))); + } + return it != m_standardCursorCache.end() ? it.value() : CursorHandlePtr(new CursorHandle); } /*! \brief Return cached pixmap cursor or create new one. */ -QWindowsWindowCursor QWindowsCursor::pixmapWindowCursor(const QCursor &c) +CursorHandlePtr QWindowsCursor::pixmapWindowCursor(const QCursor &c) { - const QWindowsCursorCacheKey cacheKey(c); - CursorCache::iterator it = m_cursorCache.find(cacheKey); - if (it == m_cursorCache.end()) { - if (m_cursorCache.size() > 50) { + const QWindowsPixmapCursorCacheKey cacheKey(c); + PixmapCursorCache::iterator it = m_pixmapCursorCache.find(cacheKey); + if (it == m_pixmapCursorCache.end()) { + if (m_pixmapCursorCache.size() > 50) { // Prevent the cursor cache from growing indefinitely hitting GDI resource // limits if new pixmap cursors are created repetitively by purging out // all-noncurrent pixmap cursors (QTBUG-43515) const HCURSOR currentCursor = GetCursor(); - for (it = m_cursorCache.begin(); it != m_cursorCache.end() ; ) { - if (it.key().bitmapCacheKey && it.value().handle() != currentCursor) - it = m_cursorCache.erase(it); + for (it = m_pixmapCursorCache.begin(); it != m_pixmapCursorCache.end() ; ) { + if (it.value()->handle() != currentCursor) + it = m_pixmapCursorCache.erase(it); else ++it; } } - it = m_cursorCache.insert(cacheKey, QWindowsWindowCursor(c)); + const qreal scaleFactor = QHighDpiScaling::factor(m_screen); + const QPixmap pixmap = c.pixmap(); + const HCURSOR hc = pixmap.isNull() + ? createBitmapCursor(c, scaleFactor) + : QWindowsCursor::createPixmapCursor(pixmap, c.hotSpot(), scaleFactor); + it = m_pixmapCursorCache.insert(cacheKey, CursorHandlePtr(new CursorHandle(hc))); } return it.value(); } -QWindowsCursor::QWindowsCursor() +QWindowsCursor::QWindowsCursor(const QPlatformScreen *screen) + : m_screen(screen) { - initResources(); + static const bool dummy = initResources(); + Q_UNUSED(dummy) } /*! @@ -607,13 +645,13 @@ void QWindowsCursor::changeCursor(QCursor *cursorIn, QWindow *window) if (!window) return; if (!cursorIn) { - QWindowsWindow::baseWindowOf(window)->setCursor(QWindowsWindowCursor()); + QWindowsWindow::baseWindowOf(window)->setCursor(CursorHandlePtr(new CursorHandle)); return; } - const QWindowsWindowCursor wcursor = + const CursorHandlePtr wcursor = cursorIn->shape() == Qt::BitmapCursor ? pixmapWindowCursor(*cursorIn) : standardWindowCursor(cursorIn->shape()); - if (wcursor.handle()) { + if (wcursor->handle()) { QWindowsWindow::baseWindowOf(window)->setCursor(wcursor); } else { qWarning("%s: Unable to obtain system cursor for %d", @@ -646,13 +684,100 @@ QWindowsCursor::CursorState QWindowsCursor::cursorState() QPoint QWindowsCursor::pos() const { - return mousePosition() / QWindowsScaling::factor(); + return mousePosition(); } void QWindowsCursor::setPos(const QPoint &pos) { - const QPoint posDp = pos * QWindowsScaling::factor(); - SetCursorPos(posDp.x() , posDp.y()); + SetCursorPos(pos.x() , pos.y()); +} + +QPixmap QWindowsCursor::dragDefaultCursor(Qt::DropAction action) const +{ + switch (action) { + case Qt::CopyAction: + if (m_copyDragCursor.isNull()) + m_copyDragCursor = QWindowsCursor::customCursor(Qt::DragCopyCursor, m_screen).pixmap; + return m_copyDragCursor; + case Qt::TargetMoveAction: + case Qt::MoveAction: + if (m_moveDragCursor.isNull()) + m_moveDragCursor = QWindowsCursor::customCursor(Qt::DragMoveCursor, m_screen).pixmap; + return m_moveDragCursor; + case Qt::LinkAction: + if (m_linkDragCursor.isNull()) + m_linkDragCursor = QWindowsCursor::customCursor(Qt::DragLinkCursor, m_screen).pixmap; + return m_linkDragCursor; + default: + break; + } + + static const char * const ignoreDragCursorXpmC[] = { + "24 30 3 1", + ". c None", + "a c #000000", + "X c #FFFFFF", + "aa......................", + "aXa.....................", + "aXXa....................", + "aXXXa...................", + "aXXXXa..................", + "aXXXXXa.................", + "aXXXXXXa................", + "aXXXXXXXa...............", + "aXXXXXXXXa..............", + "aXXXXXXXXXa.............", + "aXXXXXXaaaa.............", + "aXXXaXXa................", + "aXXaaXXa................", + "aXa..aXXa...............", + "aa...aXXa...............", + "a.....aXXa..............", + "......aXXa.....XXXX.....", + ".......aXXa..XXaaaaXX...", + ".......aXXa.XaaaaaaaaX..", + "........aa.XaaaXXXXaaaX.", + "...........XaaaaX..XaaX.", + "..........XaaXaaaX..XaaX", + "..........XaaXXaaaX.XaaX", + "..........XaaX.XaaaXXaaX", + "..........XaaX..XaaaXaaX", + "...........XaaX..XaaaaX.", + "...........XaaaXXXXaaaX.", + "............XaaaaaaaaX..", + ".............XXaaaaXX...", + "...............XXXX....."}; + + if (m_ignoreDragCursor.isNull()) { +#if !defined (Q_OS_WINCE) + HCURSOR cursor = LoadCursor(NULL, IDC_NO); + ICONINFO iconInfo = {0, 0, 0, 0, 0}; + GetIconInfo(cursor, &iconInfo); + BITMAP bmColor = {0, 0, 0, 0, 0, 0, 0}; + + if (iconInfo.hbmColor + && GetObject(iconInfo.hbmColor, sizeof(BITMAP), &bmColor) + && bmColor.bmWidth == bmColor.bmWidthBytes / 4) { + const int colorBitsLength = bmColor.bmHeight * bmColor.bmWidthBytes; + uchar *colorBits = new uchar[colorBitsLength]; + GetBitmapBits(iconInfo.hbmColor, colorBitsLength, colorBits); + const QImage colorImage(colorBits, bmColor.bmWidth, bmColor.bmHeight, + bmColor.bmWidthBytes, QImage::Format_ARGB32); + + m_ignoreDragCursor = QPixmap::fromImage(colorImage); + delete [] colorBits; + } else { + m_ignoreDragCursor = QPixmap(ignoreDragCursorXpmC); + } + + DeleteObject(iconInfo.hbmMask); + DeleteObject(iconInfo.hbmColor); + DestroyCursor(cursor); +#else // !Q_OS_WINCE + m_ignoreDragCursor = QPixmap(ignoreDragCursorXpmC); +#endif // !Q_OS_WINCE + } + return m_ignoreDragCursor; } /*! @@ -660,78 +785,11 @@ void QWindowsCursor::setPos(const QPoint &pos) \brief Per-Window cursor. Contains a QCursor and manages its associated system cursor handle resource. - Based on QSharedDataPointer, so that it can be passed around and - used as a property of QWindowsBaseWindow. - \internal \ingroup qt-lighthouse-win \sa QWindowsCursor */ -class QWindowsWindowCursorData : public QSharedData -{ -public: - QWindowsWindowCursorData() : m_cursor(Qt::ArrowCursor), m_handle(0) {} - explicit QWindowsWindowCursorData(const QCursor &c); - ~QWindowsWindowCursorData(); - - const QCursor m_cursor; - const HCURSOR m_handle; -}; - -QWindowsWindowCursorData::QWindowsWindowCursorData(const QCursor &c) : - m_cursor(c), - m_handle(QWindowsCursor::createSystemCursor(c)) -{ -} - -QWindowsWindowCursorData::~QWindowsWindowCursorData() -{ - if (m_handle) - DestroyCursor(m_handle); -} - -QWindowsWindowCursor::QWindowsWindowCursor() : - m_data(new QWindowsWindowCursorData) -{ -} - -QWindowsWindowCursor::QWindowsWindowCursor(const QCursor &c) : - m_data(new QWindowsWindowCursorData(c)) -{ -} - -QWindowsWindowCursor::~QWindowsWindowCursor() -{ -} - -QWindowsWindowCursor::QWindowsWindowCursor(const QWindowsWindowCursor &rhs) : - m_data(rhs.m_data) -{ -} - -QWindowsWindowCursor & QWindowsWindowCursor::operator =(const QWindowsWindowCursor &rhs) -{ - if (this != &rhs) - m_data.operator =(rhs.m_data); - return *this; -} - -bool QWindowsWindowCursor::isNull() const -{ - return m_data->m_handle == 0; -} - -QCursor QWindowsWindowCursor::cursor() const -{ - return m_data->m_cursor; -} - -HCURSOR QWindowsWindowCursor::handle() const -{ - return m_data->m_handle; -} - QT_END_NAMESPACE #endif // !QT_NO_CURSOR diff --git a/src/plugins/platforms/windows/qwindowscursor.h b/src/plugins/platforms/windows/qwindowscursor.h index f1763ddd7d..e93f779f94 100644 --- a/src/plugins/platforms/windows/qwindowscursor.h +++ b/src/plugins/platforms/windows/qwindowscursor.h @@ -37,51 +37,49 @@ #include "qtwindows_additional.h" #include <qpa/qplatformcursor.h> -#include <QtCore/QSharedDataPointer> +#include <QtCore/QSharedPointer> #include <QtCore/QHash> QT_BEGIN_NAMESPACE -class QWindowsWindowCursorData; - -struct QWindowsCursorCacheKey +struct QWindowsPixmapCursorCacheKey { - explicit QWindowsCursorCacheKey(const QCursor &c); - explicit QWindowsCursorCacheKey(Qt::CursorShape s) : shape(s), bitmapCacheKey(0), maskCacheKey(0) {} - QWindowsCursorCacheKey() : shape(Qt::CustomCursor), bitmapCacheKey(0), maskCacheKey(0) {} + explicit QWindowsPixmapCursorCacheKey(const QCursor &c); - Qt::CursorShape shape; qint64 bitmapCacheKey; qint64 maskCacheKey; }; -inline bool operator==(const QWindowsCursorCacheKey &k1, const QWindowsCursorCacheKey &k2) +inline bool operator==(const QWindowsPixmapCursorCacheKey &k1, const QWindowsPixmapCursorCacheKey &k2) { - return k1.shape == k2.shape && k1.bitmapCacheKey == k2.bitmapCacheKey && k1.maskCacheKey == k2.maskCacheKey; + return k1.bitmapCacheKey == k2.bitmapCacheKey && k1.maskCacheKey == k2.maskCacheKey; } -inline uint qHash(const QWindowsCursorCacheKey &k, uint seed) Q_DECL_NOTHROW +inline uint qHash(const QWindowsPixmapCursorCacheKey &k, uint seed) Q_DECL_NOTHROW { - return (uint(k.shape) + uint(k.bitmapCacheKey) + uint(k.maskCacheKey)) ^ seed; + return (uint(k.bitmapCacheKey) + uint(k.maskCacheKey)) ^ seed; } -class QWindowsWindowCursor +class CursorHandle { + Q_DISABLE_COPY(CursorHandle) public: - QWindowsWindowCursor(); - explicit QWindowsWindowCursor(const QCursor &c); - ~QWindowsWindowCursor(); - QWindowsWindowCursor(const QWindowsWindowCursor &c); - QWindowsWindowCursor &operator=(const QWindowsWindowCursor &c); + explicit CursorHandle(HCURSOR hcursor = Q_NULLPTR) : m_hcursor(hcursor) {} + ~CursorHandle() + { + if (m_hcursor) + DestroyCursor(m_hcursor); + } - bool isNull() const; - QCursor cursor() const; - HCURSOR handle() const; + bool isNull() const { return !m_hcursor; } + HCURSOR handle() const { return m_hcursor; } private: - QSharedDataPointer<QWindowsWindowCursorData> m_data; + const HCURSOR m_hcursor; }; +typedef QSharedPointer<CursorHandle> CursorHandlePtr; + class QWindowsCursor : public QPlatformCursor { public: @@ -91,25 +89,44 @@ public: CursorSuppressed // Cursor suppressed by touch interaction (Windows 8). }; - QWindowsCursor(); + struct PixmapCursor { + explicit PixmapCursor(const QPixmap &pix = QPixmap(), const QPoint &h = QPoint()) : pixmap(pix), hotSpot(h) {} + + QPixmap pixmap; + QPoint hotSpot; + }; + + explicit QWindowsCursor(const QPlatformScreen *screen); void changeCursor(QCursor * widgetCursor, QWindow * widget) Q_DECL_OVERRIDE; QPoint pos() const Q_DECL_OVERRIDE; void setPos(const QPoint &pos) Q_DECL_OVERRIDE; - static HCURSOR createPixmapCursor(const QPixmap &pixmap, const QPoint &hotSpot); - static HCURSOR createSystemCursor(const QCursor &c); - static QCursor customCursor(Qt::CursorShape cursorShape); + static HCURSOR createPixmapCursor(QPixmap pixmap, const QPoint &hotSpot, qreal scaleFactor = 1); + static HCURSOR createPixmapCursor(const PixmapCursor &pc, qreal scaleFactor = 1) { return createPixmapCursor(pc.pixmap, pc.hotSpot, scaleFactor); } + static PixmapCursor customCursor(Qt::CursorShape cursorShape, const QPlatformScreen *screen = Q_NULLPTR); + + static HCURSOR createCursorFromShape(Qt::CursorShape cursorShape, const QPlatformScreen *screen = Q_NULLPTR); static QPoint mousePosition(); static CursorState cursorState(); - QWindowsWindowCursor standardWindowCursor(Qt::CursorShape s = Qt::ArrowCursor); - QWindowsWindowCursor pixmapWindowCursor(const QCursor &c); + CursorHandlePtr standardWindowCursor(Qt::CursorShape s = Qt::ArrowCursor); + CursorHandlePtr pixmapWindowCursor(const QCursor &c); + + QPixmap dragDefaultCursor(Qt::DropAction action) const; private: - typedef QHash<QWindowsCursorCacheKey, QWindowsWindowCursor> CursorCache; + typedef QHash<Qt::CursorShape, CursorHandlePtr> StandardCursorCache; + typedef QHash<QWindowsPixmapCursorCacheKey, CursorHandlePtr> PixmapCursorCache; + + const QPlatformScreen *const m_screen; + StandardCursorCache m_standardCursorCache; + PixmapCursorCache m_pixmapCursorCache; - CursorCache m_cursorCache; + mutable QPixmap m_copyDragCursor; + mutable QPixmap m_moveDragCursor; + mutable QPixmap m_linkDragCursor; + mutable QPixmap m_ignoreDragCursor; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp index da0ba27e3a..b983ba3354 100644 --- a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp +++ b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp @@ -360,6 +360,7 @@ public: QT_BEGIN_NAMESPACE +#ifndef QT_NO_DEBUG_STREAM /* Output UID (IID, CLSID) as C++ constants. * The constants are contained in the Windows SDK libs, but not for MinGW. */ static inline QString guidToString(const GUID &g) @@ -385,6 +386,7 @@ inline QDebug operator<<(QDebug d, const GUID &g) d << guidToString(g); return d; } +#endif // !QT_NO_DEBUG_STREAM // Return an allocated wchar_t array from a QString, reserve more memory if desired. static wchar_t *qStringToWCharArray(const QString &s, size_t reserveSize = 0) diff --git a/src/plugins/platforms/windows/qwindowsdrag.cpp b/src/plugins/platforms/windows/qwindowsdrag.cpp index 03438e3ee2..021058fa33 100644 --- a/src/plugins/platforms/windows/qwindowsdrag.cpp +++ b/src/plugins/platforms/windows/qwindowsdrag.cpp @@ -33,7 +33,7 @@ #include "qwindowsdrag.h" #include "qwindowscontext.h" -#include "qwindowsscaling.h" +#include "qwindowsscreen.h" #ifndef QT_NO_CLIPBOARD # include "qwindowsclipboard.h" #endif @@ -43,7 +43,6 @@ #include "qwindowswindow.h" #include "qwindowsmousehandler.h" #include "qwindowscursor.h" -#include "qwindowsscaling.h" #include <QtGui/QMouseEvent> #include <QtGui/QPixmap> @@ -52,6 +51,7 @@ #include <QtGui/QGuiApplication> #include <qpa/qwindowsysteminterface_p.h> #include <QtGui/private/qguiapplication_p.h> +#include <QtGui/private/qhighdpiscaling_p.h> #include <QtCore/QDebug> #include <QtCore/QBuffer> @@ -218,23 +218,14 @@ public: STDMETHOD(GiveFeedback)(DWORD dwEffect); private: - class DragCursorHandle { - Q_DISABLE_COPY(DragCursorHandle) - public: - DragCursorHandle(HCURSOR c) : cursor(c) {} - ~DragCursorHandle() { DestroyCursor(cursor); } - const HCURSOR cursor; - }; - typedef QSharedPointer<DragCursorHandle> DragCursorHandlePtr; - struct CursorEntry { CursorEntry() : cacheKey(0) {} - CursorEntry(const QPixmap &p, qint64 cK, const DragCursorHandlePtr &c, const QPoint &h) : + CursorEntry(const QPixmap &p, qint64 cK, const CursorHandlePtr &c, const QPoint &h) : pixmap(p), cacheKey(cK), cursor(c), hotSpot(h) {} QPixmap pixmap; qint64 cacheKey; // Cache key of cursor - DragCursorHandlePtr cursor; + CursorHandlePtr cursor; QPoint hotSpot; }; @@ -249,7 +240,7 @@ private: QWindowsDragCursorWindow *m_touchDragWindow; ULONG m_refs; -#ifndef QT_NO_DEBUG_OUTPUT +#ifndef QT_NO_DEBUG_STREAM friend QDebug operator<<(QDebug, const QWindowsOleDropSource::CursorEntry &); #endif }; @@ -271,14 +262,14 @@ QWindowsOleDropSource::~QWindowsOleDropSource() qCDebug(lcQpaMime) << __FUNCTION__; } -#ifndef QT_NO_DEBUG_OUTPUT +#ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug d, const QWindowsOleDropSource::CursorEntry &e) { d << "CursorEntry:" << e.pixmap.size() << '#' << e.cacheKey - << "HCURSOR" << e.cursor->cursor << "hotspot:" << e.hotSpot; + << "HCURSOR" << e.cursor->handle() << "hotspot:" << e.hotSpot; return d; } -#endif // !QT_NO_DEBUG_OUTPUT +#endif // !QT_NO_DEBUG_STREAM /*! \brief Blend custom pixmap with cursors. @@ -289,24 +280,37 @@ void QWindowsOleDropSource::createCursors() const QDrag *drag = m_drag->currentDrag(); const QPixmap pixmap = drag->pixmap(); const bool hasPixmap = !pixmap.isNull(); - const int scaleFactor = QWindowsScaling::factor(); - const QSize pixmapSizeDp = pixmap.size() * scaleFactor; + + // Find screen for drag. Could be obtained from QDrag::source(), but that might be a QWidget. + + qreal scaleFactor = 1; + QPlatformCursor *platformCursor = Q_NULLPTR; + if (const QPlatformScreen *platformScreen = QWindowsContext::instance()->screenManager().screenAtDp(QWindowsCursor::mousePosition())) { + scaleFactor = QHighDpiScaling::factor(platformScreen); + platformCursor = platformScreen->cursor(); + } + if (!platformCursor && QGuiApplication::primaryScreen()) + platformCursor = QGuiApplication::primaryScreen()->handle()->cursor(); + const bool scalePixmap = hasPixmap && m_mode != TouchDrag // Touch drag: pixmap is shown in a separate QWindow, which will be scaled. && (scaleFactor != 1 && scaleFactor != qRound(pixmap.devicePixelRatio())); - const QPixmap drawPixmap = scalePixmap - ? pixmap.scaled(pixmapSizeDp, Qt::KeepAspectRatio, Qt::SmoothTransformation) : pixmap; - + const QPixmap scaledPixmap = scalePixmap + ? pixmap.scaled((QSizeF(pixmap.size()) * scaleFactor).toSize(), + Qt::KeepAspectRatio, Qt::SmoothTransformation) + : pixmap; Qt::DropAction actions[] = { Qt::MoveAction, Qt::CopyAction, Qt::LinkAction, Qt::IgnoreAction }; int actionCount = int(sizeof(actions) / sizeof(actions[0])); if (!hasPixmap) --actionCount; // No Qt::IgnoreAction unless pixmap - const QPoint hotSpot = drag->hotSpot() * scaleFactor; + const QPoint hotSpot = scalePixmap + ? (QPointF(drag->hotSpot()) * scaleFactor).toPoint() + : drag->hotSpot(); for (int cnum = 0; cnum < actionCount; ++cnum) { const Qt::DropAction action = actions[cnum]; QPixmap cursorPixmap = drag->dragCursor(action); - if (cursorPixmap.isNull()) - cursorPixmap = m_drag->defaultCursor(action); + if (cursorPixmap.isNull() && platformCursor) + cursorPixmap = static_cast<QWindowsCursor *>(platformCursor)->dragDefaultCursor(action); const qint64 cacheKey = cursorPixmap.cacheKey(); const ActionCursorMapIt it = m_cursors.find(action); if (it != m_cursors.end() && it.value().cacheKey == cacheKey) @@ -321,21 +325,21 @@ void QWindowsOleDropSource::createCursors() if (hasPixmap) { const int x1 = qMin(-hotSpot.x(), 0); - const int x2 = qMax(pixmapSizeDp.width() - hotSpot.x(), cursorPixmap.width()); + const int x2 = qMax(scaledPixmap.width() - hotSpot.x(), cursorPixmap.width()); const int y1 = qMin(-hotSpot.y(), 0); - const int y2 = qMax(pixmapSizeDp.height() - hotSpot.y(), cursorPixmap.height()); + const int y2 = qMax(scaledPixmap.height() - hotSpot.y(), cursorPixmap.height()); QPixmap newCursor(x2 - x1 + 1, y2 - y1 + 1); newCursor.fill(Qt::transparent); QPainter p(&newCursor); const QPoint pmDest = QPoint(qMax(0, -hotSpot.x()), qMax(0, -hotSpot.y())); - p.drawPixmap(pmDest, drawPixmap); + p.drawPixmap(pmDest, scaledPixmap); p.drawPixmap(qMax(0, hotSpot.x()),qMax(0, hotSpot.y()), cursorPixmap); newPixmap = newCursor; newHotSpot = QPoint(qMax(0, hotSpot.x()), qMax(0, hotSpot.y())); } if (const HCURSOR sysCursor = QWindowsCursor::createPixmapCursor(newPixmap, newHotSpot)) { - const CursorEntry entry(newPixmap, cacheKey, DragCursorHandlePtr(new DragCursorHandle(sysCursor)), newHotSpot); + const CursorEntry entry(newPixmap, cacheKey, CursorHandlePtr(new CursorHandle(sysCursor)), newHotSpot); if (it == m_cursors.end()) m_cursors.insert(action, entry); else @@ -448,13 +452,13 @@ QWindowsOleDropSource::GiveFeedback(DWORD dwEffect) const CursorEntry &e = it.value(); switch (m_mode) { case MouseDrag: - SetCursor(e.cursor->cursor); + SetCursor(e.cursor->handle()); break; case TouchDrag: if (!m_touchDragWindow) m_touchDragWindow = new QWindowsDragCursorWindow; m_touchDragWindow->setPixmap(e.pixmap); - m_touchDragWindow->setFramePosition((QWindowsCursor::mousePosition() - e.hotSpot) / QWindowsScaling::factor()); + m_touchDragWindow->setFramePosition(QWindowsCursor::mousePosition() - e.hotSpot); if (!m_touchDragWindow->isVisible()) m_touchDragWindow->show(); break; @@ -530,9 +534,7 @@ void QWindowsOleDropTarget::handleDrag(QWindow *window, DWORD grfKeyState, QGuiApplicationPrivate::mouse_buttons = QWindowsMouseHandler::keyStateToMouseButtons(grfKeyState); const QPlatformDragQtResponse response = - QWindowSystemInterface::handleDrag(window, windowsDrag->dropData(), - m_lastPoint / QWindowsScaling::factor(), - actions); + QWindowSystemInterface::handleDrag(window, windowsDrag->dropData(), m_lastPoint, actions); m_answerRect = response.answerRect(); const Qt::DropAction action = response.acceptedAction(); @@ -625,9 +627,8 @@ QWindowsOleDropTarget::Drop(LPDATAOBJECT pDataObj, DWORD grfKeyState, const QPlatformDropQtResponse response = QWindowSystemInterface::handleDrop(m_window, windowsDrag->dropData(), - m_lastPoint / QWindowsScaling::factor(), + m_lastPoint, translateToQDragDropActions(*pdwEffect)); - if (response.isAccepted()) { const Qt::DropAction action = response.acceptedAction(); if (action == Qt::MoveAction || action == Qt::TargetMoveAction) { @@ -706,94 +707,6 @@ IDropTargetHelper* QWindowsDrag::dropHelper() { return m_cachedDropTargetHelper; } -QPixmap QWindowsDrag::defaultCursor(Qt::DropAction action) const -{ - switch (action) { - case Qt::CopyAction: - if (m_copyDragCursor.isNull()) - m_copyDragCursor = QWindowsCursor::customCursor(Qt::DragCopyCursor).pixmap(); - return m_copyDragCursor; - case Qt::TargetMoveAction: - case Qt::MoveAction: - if (m_moveDragCursor.isNull()) - m_moveDragCursor = QWindowsCursor::customCursor(Qt::DragMoveCursor).pixmap(); - return m_moveDragCursor; - case Qt::LinkAction: - if (m_linkDragCursor.isNull()) - m_linkDragCursor = QWindowsCursor::customCursor(Qt::DragLinkCursor).pixmap(); - return m_linkDragCursor; - default: - break; - } - - static const char * const ignoreDragCursorXpmC[] = { - "24 30 3 1", - ". c None", - "a c #000000", - "X c #FFFFFF", - "aa......................", - "aXa.....................", - "aXXa....................", - "aXXXa...................", - "aXXXXa..................", - "aXXXXXa.................", - "aXXXXXXa................", - "aXXXXXXXa...............", - "aXXXXXXXXa..............", - "aXXXXXXXXXa.............", - "aXXXXXXaaaa.............", - "aXXXaXXa................", - "aXXaaXXa................", - "aXa..aXXa...............", - "aa...aXXa...............", - "a.....aXXa..............", - "......aXXa.....XXXX.....", - ".......aXXa..XXaaaaXX...", - ".......aXXa.XaaaaaaaaX..", - "........aa.XaaaXXXXaaaX.", - "...........XaaaaX..XaaX.", - "..........XaaXaaaX..XaaX", - "..........XaaXXaaaX.XaaX", - "..........XaaX.XaaaXXaaX", - "..........XaaX..XaaaXaaX", - "...........XaaX..XaaaaX.", - "...........XaaaXXXXaaaX.", - "............XaaaaaaaaX..", - ".............XXaaaaXX...", - "...............XXXX....."}; - - if (m_ignoreDragCursor.isNull()) { -#if !defined (Q_OS_WINCE) - HCURSOR cursor = LoadCursor(NULL, IDC_NO); - ICONINFO iconInfo = {0, 0, 0, 0, 0}; - GetIconInfo(cursor, &iconInfo); - BITMAP bmColor = {0, 0, 0, 0, 0, 0, 0}; - - if (iconInfo.hbmColor - && GetObject(iconInfo.hbmColor, sizeof(BITMAP), &bmColor) - && bmColor.bmWidth == bmColor.bmWidthBytes / 4) { - const int colorBitsLength = bmColor.bmHeight * bmColor.bmWidthBytes; - uchar *colorBits = new uchar[colorBitsLength]; - GetBitmapBits(iconInfo.hbmColor, colorBitsLength, colorBits); - const QImage colorImage(colorBits, bmColor.bmWidth, bmColor.bmHeight, - bmColor.bmWidthBytes, QImage::Format_ARGB32); - - m_ignoreDragCursor = QPixmap::fromImage(colorImage); - delete [] colorBits; - } else { - m_ignoreDragCursor = QPixmap(ignoreDragCursorXpmC); - } - - DeleteObject(iconInfo.hbmMask); - DeleteObject(iconInfo.hbmColor); - DestroyCursor(cursor); -#else // !Q_OS_WINCE - m_ignoreDragCursor = QPixmap(ignoreDragCursorXpmC); -#endif // !Q_OS_WINCE - } - return m_ignoreDragCursor; -} - Qt::DropAction QWindowsDrag::drag(QDrag *drag) { // TODO: Accessibility handling? diff --git a/src/plugins/platforms/windows/qwindowsdrag.h b/src/plugins/platforms/windows/qwindowsdrag.h index 9a5e0b17f2..890ad6c142 100644 --- a/src/plugins/platforms/windows/qwindowsdrag.h +++ b/src/plugins/platforms/windows/qwindowsdrag.h @@ -42,6 +42,9 @@ struct IDropTargetHelper; QT_BEGIN_NAMESPACE + +class QPlatformScreen; + class QWindowsDropMimeData : public QWindowsInternalMimeData { public: QWindowsDropMimeData() {} @@ -95,18 +98,11 @@ public: IDropTargetHelper* dropHelper(); - QPixmap defaultCursor(Qt::DropAction action) const; - private: QWindowsDropMimeData m_dropData; IDataObject *m_dropDataObject; IDropTargetHelper* m_cachedDropTargetHelper; - - mutable QPixmap m_copyDragCursor; - mutable QPixmap m_moveDragCursor; - mutable QPixmap m_linkDragCursor; - mutable QPixmap m_ignoreDragCursor; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowseglcontext.cpp b/src/plugins/platforms/windows/qwindowseglcontext.cpp index f2547d5cdd..21eba6da7e 100644 --- a/src/plugins/platforms/windows/qwindowseglcontext.cpp +++ b/src/plugins/platforms/windows/qwindowseglcontext.cpp @@ -39,7 +39,7 @@ #include <QtGui/QOpenGLContext> #if defined(QT_OPENGL_ES_2_ANGLE) || defined(QT_OPENGL_DYNAMIC) -# include <QtANGLE/EGL/eglext.h> +# include <EGL/eglext.h> #endif QT_BEGIN_NAMESPACE @@ -350,16 +350,16 @@ QWindowsEGLStaticContext *QWindowsEGLStaticContext::create(QWindowsOpenGLTester: { const HDC dc = QWindowsContext::instance()->displayContext(); if (!dc){ - qWarning("%s: No Display", Q_FUNC_INFO); + qWarning("%s: No Display", __FUNCTION__); return 0; } if (!libEGL.init()) { - qWarning("%s: Failed to load and resolve libEGL functions", Q_FUNC_INFO); + qWarning("%s: Failed to load and resolve libEGL functions", __FUNCTION__); return 0; } if (!libGLESv2.init()) { - qWarning("%s: Failed to load and resolve libGLESv2 functions", Q_FUNC_INFO); + qWarning("%s: Failed to load and resolve libGLESv2 functions", __FUNCTION__); return 0; } @@ -396,15 +396,15 @@ QWindowsEGLStaticContext *QWindowsEGLStaticContext::create(QWindowsOpenGLTester: if (display == EGL_NO_DISPLAY) display = libEGL.eglGetDisplay((EGLNativeDisplayType)dc); if (!display) { - qWarning("%s: Could not obtain EGL display", Q_FUNC_INFO); + qWarning("%s: Could not obtain EGL display", __FUNCTION__); return 0; } if (!major && !libEGL.eglInitialize(display, &major, &minor)) { int err = libEGL.eglGetError(); - qWarning("%s: Could not initialize EGL display: error 0x%x\n", Q_FUNC_INFO, err); + qWarning("%s: Could not initialize EGL display: error 0x%x", __FUNCTION__, err); if (err == 0x3001) - qWarning("%s: When using ANGLE, check if d3dcompiler_4x.dll is available", Q_FUNC_INFO); + qWarning("%s: When using ANGLE, check if d3dcompiler_4x.dll is available", __FUNCTION__); return 0; } @@ -430,7 +430,7 @@ void *QWindowsEGLStaticContext::createWindowSurface(void *nativeWindow, void *na (EGLNativeWindowType) nativeWindow, 0); if (surface == EGL_NO_SURFACE) { *err = libEGL.eglGetError(); - qWarning("%s: Could not create the EGL window surface: 0x%x\n", Q_FUNC_INFO, *err); + qWarning("%s: Could not create the EGL window surface: 0x%x", __FUNCTION__, *err); } return surface; @@ -533,7 +533,12 @@ QWindowsEGLContext::QWindowsEGLContext(QWindowsEGLStaticContext *staticContext, } if (m_eglContext == EGL_NO_CONTEXT) { - qWarning("QWindowsEGLContext: eglError: %x, this: %p \n", QWindowsEGLStaticContext::libEGL.eglGetError(), this); + int err = QWindowsEGLStaticContext::libEGL.eglGetError(); + qWarning("QWindowsEGLContext: Failed to create context, eglError: %x, this: %p", err, this); + // ANGLE gives bad alloc when it fails to reset a previously lost D3D device. + // A common cause for this is disabling the graphics adapter used by the app. + if (err == EGL_BAD_ALLOC) + qWarning("QWindowsEGLContext: Graphics device lost. (Did the adapter get disabled?)"); return; } @@ -594,6 +599,12 @@ bool QWindowsEGLContext::makeCurrent(QPlatformSurface *surface) if (err == EGL_CONTEXT_LOST) { m_eglContext = EGL_NO_CONTEXT; qCDebug(lcQpaGl) << "Got EGL context lost in createWindowSurface() for context" << this; + } else if (err == EGL_BAD_ACCESS) { + // With ANGLE this means no (D3D) device and can happen when disabling/changing graphics adapters. + qCDebug(lcQpaGl) << "Bad access (missing device?) in createWindowSurface() for context" << this; + // Simulate context loss as the context is useless. + QWindowsEGLStaticContext::libEGL.eglDestroyContext(m_eglDisplay, m_eglContext); + m_eglContext = EGL_NO_CONTEXT; } return false; } @@ -623,7 +634,7 @@ bool QWindowsEGLContext::makeCurrent(QPlatformSurface *surface) // Drop the surface. Will recreate on the next makeCurrent. window->invalidateSurface(); } else { - qWarning("QWindowsEGLContext::makeCurrent: eglError: %x, this: %p \n", err, this); + qWarning("%s: Failed to make surface current. eglError: %x, this: %p", __FUNCTION__, err, this); } } @@ -635,7 +646,8 @@ void QWindowsEGLContext::doneCurrent() QWindowsEGLStaticContext::libEGL.eglBindAPI(m_api); bool ok = QWindowsEGLStaticContext::libEGL.eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); if (!ok) - qWarning("QWindowsEGLContext::doneCurrent: eglError: %d, this: %p \n", QWindowsEGLStaticContext::libEGL.eglGetError(), this); + qWarning("%s: Failed to make no context/surface current. eglError: %d, this: %p", __FUNCTION__, + QWindowsEGLStaticContext::libEGL.eglGetError(), this); } void QWindowsEGLContext::swapBuffers(QPlatformSurface *surface) @@ -653,8 +665,15 @@ void QWindowsEGLContext::swapBuffers(QPlatformSurface *surface) } bool ok = QWindowsEGLStaticContext::libEGL.eglSwapBuffers(m_eglDisplay, eglSurface); - if (!ok) - qWarning("QWindowsEGLContext::swapBuffers: eglError: %d, this: %p \n", QWindowsEGLStaticContext::libEGL.eglGetError(), this); + if (!ok) { + err = QWindowsEGLStaticContext::libEGL.eglGetError(); + if (err == EGL_CONTEXT_LOST) { + m_eglContext = EGL_NO_CONTEXT; + qCDebug(lcQpaGl) << "Got EGL context lost in eglSwapBuffers()"; + } else { + qWarning("%s: Failed to swap buffers. eglError: %d, this: %p", __FUNCTION__, err, this); + } + } } QFunctionPointer QWindowsEGLContext::getProcAddress(const QByteArray &procName) diff --git a/src/plugins/platforms/windows/qwindowsfontdatabase.cpp b/src/plugins/platforms/windows/qwindowsfontdatabase.cpp index 3b27964b0e..c8eaccd956 100644 --- a/src/plugins/platforms/windows/qwindowsfontdatabase.cpp +++ b/src/plugins/platforms/windows/qwindowsfontdatabase.cpp @@ -40,6 +40,7 @@ #include <QtGui/QFont> #include <QtGui/QGuiApplication> +#include <QtGui/private/qhighdpiscaling_p.h> #include <QtCore/qmath.h> #include <QtCore/QDebug> @@ -606,6 +607,7 @@ static inline bool initDirectWrite(QWindowsFontEngineData *d) \ingroup qt-lighthouse-win */ +#ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug d, const QFontDef &def) { QDebugStateSaver saver(d); @@ -617,6 +619,7 @@ QDebug operator<<(QDebug d, const QFontDef &def) << def.hintingPreference; return d; } +#endif // !QT_NO_DEBUG_STREAM static inline QFontDatabase::WritingSystem writingSystemFromCharSet(uchar charSet) { @@ -1098,8 +1101,11 @@ QFontEngine *QWindowsFontDatabase::fontEngine(const QByteArray &fontData, qreal QFontEngine *fontEngine = 0; #if !defined(QT_NO_DIRECTWRITE) - if (hintingPreference == QFont::PreferDefaultHinting - || hintingPreference == QFont::PreferFullHinting) + bool useDirectWrite = (hintingPreference == QFont::PreferNoHinting) + || (hintingPreference == QFont::PreferVerticalHinting) + || (QHighDpiScaling::isActive() && hintingPreference == QFont::PreferDefaultHinting); + + if (!useDirectWrite) #endif { GUID guid; @@ -1702,7 +1708,8 @@ QFontEngine *QWindowsFontDatabase::createEngine(const QFontDef &request, #if !defined(QT_NO_DIRECTWRITE) bool useDirectWrite = (request.hintingPreference == QFont::PreferNoHinting) - || (request.hintingPreference == QFont::PreferVerticalHinting); + || (request.hintingPreference == QFont::PreferVerticalHinting) + || (QHighDpiScaling::isActive() && request.hintingPreference == QFont::PreferDefaultHinting); if (useDirectWrite && initDirectWrite(data.data())) { const QString fam = QString::fromWCharArray(lf.lfFaceName); const QString nameSubstitute = QWindowsFontEngineDirectWrite::fontNameSubstitute(fam); diff --git a/src/plugins/platforms/windows/qwindowsfontdatabase.h b/src/plugins/platforms/windows/qwindowsfontdatabase.h index efb5421996..10b6315aab 100644 --- a/src/plugins/platforms/windows/qwindowsfontdatabase.h +++ b/src/plugins/platforms/windows/qwindowsfontdatabase.h @@ -118,6 +118,10 @@ private: QMap<QString, UniqueFontData> m_uniqueFontData; }; +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug, const QFontDef &def); +#endif + QT_END_NAMESPACE #endif // QWINDOWSFONTDATABASE_H diff --git a/src/plugins/platforms/windows/qwindowsfontdatabase_ft.cpp b/src/plugins/platforms/windows/qwindowsfontdatabase_ft.cpp index 795554698c..16cc2afef6 100644 --- a/src/plugins/platforms/windows/qwindowsfontdatabase_ft.cpp +++ b/src/plugins/platforms/windows/qwindowsfontdatabase_ft.cpp @@ -355,7 +355,7 @@ static bool addFontToDatabase(const QString &faceName, const QFont::Weight weight = QPlatformFontDatabase::weightFromInteger(tm->tmWeight); const QFont::Stretch stretch = QFont::Unstretched; -#ifndef QT_NO_DEBUG_OUTPUT +#ifndef QT_NO_DEBUG_STREAM if (QWindowsContext::verbose > 2) { QString message; QTextStream str(&message); diff --git a/src/plugins/platforms/windows/qwindowsglcontext.cpp b/src/plugins/platforms/windows/qwindowsglcontext.cpp index a7c14ed2ac..a06707b84c 100644 --- a/src/plugins/platforms/windows/qwindowsglcontext.cpp +++ b/src/plugins/platforms/windows/qwindowsglcontext.cpp @@ -284,6 +284,7 @@ static inline void initPixelFormatDescriptor(PIXELFORMATDESCRIPTOR *d) d->nVersion = 1; } +#ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug d, const PIXELFORMATDESCRIPTOR &pd) { QDebugStateSaver saver(d); @@ -326,6 +327,32 @@ QDebug operator<<(QDebug d, const PIXELFORMATDESCRIPTOR &pd) return d; } +QDebug operator<<(QDebug d, const QOpenGLStaticContext &s) +{ + QDebugStateSaver saver(d); + d.nospace(); + d << "OpenGL: " << s.vendor << ',' << s.renderer << " default " + << s.defaultFormat; + if (s.extensions & QOpenGLStaticContext::SampleBuffers) + d << ",SampleBuffers"; + if (s.hasExtensions()) + d << ", Extension-API present"; + d << "\nExtensions: " << (s.extensionNames.count(' ') + 1); + if (QWindowsContext::verbose > 1) + d << s.extensionNames; + return d; +} + +QDebug operator<<(QDebug d, const QWindowsOpenGLContextFormat &f) +{ + QDebugStateSaver saver(d); + d.nospace(); + d << "ContextFormat: v" << (f.version >> 8) << '.' << (f.version & 0xFF) + << " profile: " << f.profile << " options: " << f.options; + return d; +} +#endif // !QT_NO_DEBUG_STREAM + // Check whether an obtained PIXELFORMATDESCRIPTOR matches the request. static inline bool isAcceptableFormat(const QWindowsOpenGLAdditionalFormat &additional, @@ -856,16 +883,11 @@ QWindowsOpenGLContextFormat QWindowsOpenGLContextFormat::current() { QWindowsOpenGLContextFormat result; const QByteArray version = QOpenGLStaticContext::getGlString(GL_VERSION); - const int majorDot = version.indexOf('.'); - if (majorDot != -1) { - int minorDot = version.indexOf('.', majorDot + 1); - if (minorDot == -1) - minorDot = version.size(); - result.version = (version.mid(0, majorDot).toInt() << 8) - + version.mid(majorDot + 1, minorDot - majorDot - 1).toInt(); - } else { + int major, minor; + if (QPlatformOpenGLContext::parseOpenGLVersion(version, major, minor)) + result.version = (major << 8) + minor; + else result.version = 0x0200; - } result.profile = QSurfaceFormat::NoProfile; if (result.version < 0x0300) { result.options |= QSurfaceFormat::DeprecatedFunctions; @@ -905,15 +927,6 @@ void QWindowsOpenGLContextFormat::apply(QSurfaceFormat *format) const format->setOption(QSurfaceFormat::DeprecatedFunctions); } -QDebug operator<<(QDebug d, const QWindowsOpenGLContextFormat &f) -{ - QDebugStateSaver saver(d); - d.nospace(); - d << "ContextFormat: v" << (f.version >> 8) << '.' << (f.version & 0xFF) - << " profile: " << f.profile << " options: " << f.options; - return d; -} - /*! \class QOpenGLTemporaryContext \brief A temporary context that can be instantiated on the stack. @@ -1018,22 +1031,6 @@ QOpenGLStaticContext *QOpenGLStaticContext::create(bool softwareRendering) return result; } -QDebug operator<<(QDebug d, const QOpenGLStaticContext &s) -{ - QDebugStateSaver saver(d); - d.nospace(); - d << "OpenGL: " << s.vendor << ',' << s.renderer << " default " - << s.defaultFormat; - if (s.extensions & QOpenGLStaticContext::SampleBuffers) - d << ",SampleBuffers"; - if (s.hasExtensions()) - d << ", Extension-API present"; - d << "\nExtensions: " << (s.extensionNames.count(' ') + 1); - if (QWindowsContext::verbose > 1) - d << s.extensionNames; - return d; -} - /*! \class QWindowsGLContext \brief Open GL context. diff --git a/src/plugins/platforms/windows/qwindowsglcontext.h b/src/plugins/platforms/windows/qwindowsglcontext.h index 516fa0707e..ba617f13ce 100644 --- a/src/plugins/platforms/windows/qwindowsglcontext.h +++ b/src/plugins/platforms/windows/qwindowsglcontext.h @@ -85,7 +85,11 @@ struct QWindowsOpenGLContextFormat QSurfaceFormat::FormatOptions options; }; +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug d, const PIXELFORMATDESCRIPTOR &); QDebug operator<<(QDebug d, const QWindowsOpenGLContextFormat &); +QDebug operator<<(QDebug d, const QOpenGLStaticContext &s); +#endif struct QWindowsOpengl32DLL { @@ -224,8 +228,6 @@ public: static QWindowsOpengl32DLL opengl32; }; -QDebug operator<<(QDebug d, const QOpenGLStaticContext &); - class QWindowsGLContext : public QWindowsOpenGLContext { public: diff --git a/src/plugins/platforms/windows/qwindowsguieventdispatcher.cpp b/src/plugins/platforms/windows/qwindowsguieventdispatcher.cpp deleted file mode 100644 index 0bfa0239aa..0000000000 --- a/src/plugins/platforms/windows/qwindowsguieventdispatcher.cpp +++ /dev/null @@ -1,201 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Samuel Gaist <samuel.gaist@edeltech.ch> -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qwindowsguieventdispatcher.h" -#include "qwindowscontext.h" - -#include <qpa/qwindowsysteminterface.h> - -#include <QtCore/QCoreApplication> -#include <QtCore/QStack> -#include <QtCore/QDebug> - -#include <windowsx.h> - -QT_BEGIN_NAMESPACE - -/*! - \class QWindowsGuiEventDispatcher - \brief Event dispatcher for Windows - - Maintains a global stack storing the current event dispatcher and - its processing flags for access from the Windows procedure - qWindowsWndProc. Handling the Lighthouse gui events should be done - from within the qWindowsWndProc to ensure correct processing of messages. - - \internal - \ingroup qt-lighthouse-win -*/ - -QWindowsGuiEventDispatcher::QWindowsGuiEventDispatcher(QObject *parent) : - QEventDispatcherWin32(parent), m_flags(0) -{ - setObjectName(QStringLiteral("QWindowsGuiEventDispatcher")); - createInternalHwnd(); // QTBUG-40881: Do not delay registering timers, etc. for QtMfc. -} - -bool QWindowsGuiEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags) -{ - const QEventLoop::ProcessEventsFlags oldFlags = m_flags; - m_flags = flags; - if (QWindowsContext::verbose > 2 && lcQpaEvents().isDebugEnabled()) - qCDebug(lcQpaEvents) << '>' << __FUNCTION__ << objectName() << flags; - const bool rc = QEventDispatcherWin32::processEvents(flags); - if (QWindowsContext::verbose > 2 && lcQpaEvents().isDebugEnabled()) - qCDebug(lcQpaEvents) << '<' << __FUNCTION__ << "returns" << rc; - m_flags = oldFlags; - return rc; -} - -void QWindowsGuiEventDispatcher::sendPostedEvents() -{ - QEventDispatcherWin32::sendPostedEvents(); - QWindowSystemInterface::sendWindowSystemEvents(m_flags); -} - -// Helpers for printing debug output for WM_* messages. -struct MessageDebugEntry -{ - UINT message; - const char *description; - bool interesting; -}; - -static const MessageDebugEntry -messageDebugEntries[] = { - {WM_CREATE, "WM_CREATE", true}, - {WM_PAINT, "WM_PAINT", true}, - {WM_CLOSE, "WM_CLOSE", true}, - {WM_DESTROY, "WM_DESTROY", true}, - {WM_MOVE, "WM_MOVE", true}, - {WM_SIZE, "WM_SIZE", true}, - {WM_MOUSEACTIVATE,"WM_MOUSEACTIVATE", true}, - {WM_CHILDACTIVATE, "WM_CHILDACTIVATE", true}, - {WM_PARENTNOTIFY, "WM_PARENTNOTIFY", true}, - {WM_ENTERIDLE, "WM_ENTERIDLE", false}, - {WM_GETICON, "WM_GETICON", false}, - {WM_KEYDOWN, "WM_KEYDOWN", true}, - {WM_SYSKEYDOWN, "WM_SYSKEYDOWN", true}, - {WM_SYSCOMMAND, "WM_SYSCOMMAND", true}, - {WM_KEYUP, "WM_KEYUP", true}, - {WM_SYSKEYUP, "WM_SYSKEYUP", true}, -#if defined(WM_APPCOMMAND) - {WM_APPCOMMAND, "WM_APPCOMMAND", true}, -#endif - {WM_IME_CHAR, "WM_IMECHAR", true}, - {WM_IME_KEYDOWN, "WM_IMECHAR", true}, - {WM_CANCELMODE, "WM_CANCELMODE", true}, - {WM_CHAR, "WM_CHAR", true}, - {WM_DEADCHAR, "WM_DEADCHAR", true}, - {WM_ACTIVATE, "WM_ACTIVATE", true}, - {WM_GETMINMAXINFO, "WM_GETMINMAXINFO", true}, - {WM_SETFOCUS, "WM_SETFOCUS", true}, - {WM_KILLFOCUS, "WM_KILLFOCUS", true}, - {WM_ENABLE, "WM_ENABLE", true}, - {WM_SHOWWINDOW, "WM_SHOWWINDOW", true}, - {WM_WINDOWPOSCHANGING, "WM_WINDOWPOSCHANGING", true}, - {WM_WINDOWPOSCHANGED, "WM_WINDOWPOSCHANGED", true}, - {WM_SETCURSOR, "WM_SETCURSOR", false}, - {WM_GETFONT, "WM_GETFONT", true}, - {WM_NCMOUSEMOVE, "WM_NCMOUSEMOVE", true}, - {WM_LBUTTONDOWN, "WM_LBUTTONDOWN", true}, - {WM_LBUTTONUP, "WM_LBUTTONUP", true}, - {WM_LBUTTONDBLCLK, "WM_LBUTTONDBLCLK", true}, - {WM_RBUTTONDOWN, "WM_RBUTTONDOWN", true}, - {WM_RBUTTONUP, "WM_RBUTTONUP", true}, - {WM_RBUTTONDBLCLK, "WM_RBUTTONDBLCLK", true}, - {WM_MBUTTONDOWN, "WM_MBUTTONDOWN", true}, - {WM_MBUTTONUP, "WM_MBUTTONUP", true}, - {WM_MBUTTONDBLCLK, "WM_MBUTTONDBLCLK", true}, - {WM_MOUSEWHEEL, "WM_MOUSEWHEEL", true}, - {WM_XBUTTONDOWN, "WM_XBUTTONDOWN", true}, - {WM_XBUTTONUP, "WM_XBUTTONUP", true}, - {WM_XBUTTONDBLCLK, "WM_XBUTTONDBLCLK", true}, - {WM_MOUSEHWHEEL, "WM_MOUSEHWHEEL", true}, - {WM_NCCREATE, "WM_NCCREATE", true}, - {WM_NCCALCSIZE, "WM_NCCALCSIZE", true}, - {WM_NCACTIVATE, "WM_NCACTIVATE", true}, - {WM_NCMOUSELEAVE, "WM_NCMOUSELEAVE", true}, - {WM_NCLBUTTONDOWN, "WM_NCLBUTTONDOWN", true}, - {WM_NCLBUTTONUP, "WM_NCLBUTTONUP", true}, - {WM_ACTIVATEAPP, "WM_ACTIVATEAPP", true}, - {WM_NCPAINT, "WM_NCPAINT", true}, - {WM_ERASEBKGND, "WM_ERASEBKGND", true}, - {WM_MOUSEMOVE, "WM_MOUSEMOVE", true}, - {WM_MOUSELEAVE, "WM_MOUSELEAVE", true}, - {WM_NCHITTEST, "WM_NCHITTEST", false}, - {WM_IME_SETCONTEXT, "WM_IME_SETCONTEXT", true}, - {WM_INPUTLANGCHANGE, "WM_INPUTLANGCHANGE", true}, - {WM_IME_NOTIFY, "WM_IME_NOTIFY", true}, -#if defined(WM_DWMNCRENDERINGCHANGED) - {WM_DWMNCRENDERINGCHANGED, "WM_DWMNCRENDERINGCHANGED", true}, -#endif - {WM_IME_SETCONTEXT, "WM_IME_SETCONTEXT", true}, - {WM_IME_NOTIFY, "WM_IME_NOTIFY", true}, - {WM_TOUCH, "WM_TOUCH", true}, - {WM_CHANGECBCHAIN, "WM_CHANGECBCHAIN", true}, - {WM_DRAWCLIPBOARD, "WM_DRAWCLIPBOARD", true}, - {WM_RENDERFORMAT, "WM_RENDERFORMAT", true}, - {WM_RENDERALLFORMATS, "WM_RENDERALLFORMATS", true}, - {WM_DESTROYCLIPBOARD, "WM_DESTROYCLIPBOARD", true}, - {WM_CAPTURECHANGED, "WM_CAPTURECHANGED", true}, - {WM_IME_STARTCOMPOSITION, "WM_IME_STARTCOMPOSITION", true}, - {WM_IME_COMPOSITION, "WM_IME_COMPOSITION", true}, - {WM_IME_ENDCOMPOSITION, "WM_IME_ENDCOMPOSITION", true}, - {WM_IME_NOTIFY, "WM_IME_NOTIFY", true}, - {WM_IME_REQUEST, "WM_IME_REQUEST", true}, -#if !defined(Q_OS_WINCE) && !defined(QT_NO_SESSIONMANAGER) - {WM_QUERYENDSESSION, "WM_QUERYENDSESSION", true}, - {WM_ENDSESSION, "WM_ENDSESSION", true}, -#endif - {WM_DISPLAYCHANGE, "WM_DISPLAYCHANGE", true}, - {WM_THEMECHANGED, "WM_THEMECHANGED", true} -}; - -static inline const MessageDebugEntry *messageDebugEntry(UINT msg) -{ - for (size_t i = 0; i < sizeof(messageDebugEntries)/sizeof(MessageDebugEntry); i++) - if (messageDebugEntries[i].message == msg) - return messageDebugEntries + i; - return 0; -} - -const char *QWindowsGuiEventDispatcher::windowsMessageName(UINT msg) -{ - if (const MessageDebugEntry *e = messageDebugEntry(msg)) - return e->description; - return "Unknown"; -} - -QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsinputcontext.cpp b/src/plugins/platforms/windows/qwindowsinputcontext.cpp index 68e38dc4a6..7e1cc563cb 100644 --- a/src/plugins/platforms/windows/qwindowsinputcontext.cpp +++ b/src/plugins/platforms/windows/qwindowsinputcontext.cpp @@ -36,7 +36,6 @@ #include "qwindowswindow.h" #include "qwindowsintegration.h" #include "qwindowsmousehandler.h" -#include "qwindowsscaling.h" #include <QtCore/QDebug> #include <QtCore/QObject> @@ -85,6 +84,18 @@ static inline void imeNotifyCancelComposition(HWND hwnd) ImmReleaseContext(hwnd, himc); } +static inline LCID languageIdFromLocaleId(LCID localeId) +{ + return localeId & 0xFFFF; +} + +static inline LCID currentInputLanguageId() +{ + return languageIdFromLocaleId(reinterpret_cast<quintptr>(GetKeyboardLayout(0))); +} + +Q_CORE_EXPORT QLocale qt_localeFromLCID(LCID id); // from qlocale_win.cpp + /*! \class QWindowsInputContext \brief Windows Input context implementation @@ -154,7 +165,9 @@ QWindowsInputContext::CompositionContext::CompositionContext() : QWindowsInputContext::QWindowsInputContext() : m_WM_MSIME_MOUSE(RegisterWindowMessage(L"MSIMEMouseOperation")), - m_endCompositionRecursionGuard(false) + m_endCompositionRecursionGuard(false), + m_languageId(currentInputLanguageId()), + m_locale(qt_localeFromLCID(m_languageId)) { connect(QGuiApplication::inputMethod(), &QInputMethod::cursorRectangleChanged, this, &QWindowsInputContext::cursorRectChanged); @@ -220,21 +233,24 @@ void QWindowsInputContext::updateEnabled() const bool accepted = inputMethodAccepted(); if (QWindowsContext::verbose > 1) qCDebug(lcQpaInputMethods) << __FUNCTION__ << window << "accepted=" << accepted; - if (accepted) { - // Re-enable IME by associating default context saved on first disabling. - if (platformWindow->testFlag(QWindowsWindow::InputMethodDisabled)) { - ImmAssociateContext(platformWindow->handle(), QWindowsInputContext::m_defaultContext); - platformWindow->clearFlag(QWindowsWindow::InputMethodDisabled); - } - } else { - // Disable IME by associating 0 context. Store context first time. - if (!platformWindow->testFlag(QWindowsWindow::InputMethodDisabled)) { - const HIMC oldImC = ImmAssociateContext(platformWindow->handle(), 0); - platformWindow->setFlag(QWindowsWindow::InputMethodDisabled); - if (!QWindowsInputContext::m_defaultContext && oldImC) - QWindowsInputContext::m_defaultContext = oldImC; - } - } + QWindowsInputContext::setWindowsImeEnabled(platformWindow, accepted); + } +} + +void QWindowsInputContext::setWindowsImeEnabled(QWindowsWindow *platformWindow, bool enabled) +{ + if (!platformWindow || platformWindow->testFlag(QWindowsWindow::InputMethodDisabled) == !enabled) + return; + if (enabled) { + // Re-enable Windows IME by associating default context saved on first disabling. + ImmAssociateContext(platformWindow->handle(), QWindowsInputContext::m_defaultContext); + platformWindow->clearFlag(QWindowsWindow::InputMethodDisabled); + } else { + // Disable Windows IME by associating 0 context. Store context first time. + const HIMC oldImC = ImmAssociateContext(platformWindow->handle(), 0); + platformWindow->setFlag(QWindowsWindow::InputMethodDisabled); + if (!QWindowsInputContext::m_defaultContext && oldImC) + QWindowsInputContext::m_defaultContext = oldImC; } } @@ -254,10 +270,9 @@ void QWindowsInputContext::cursorRectChanged() if (!m_compositionContext.hwnd) return; const QInputMethod *inputMethod = QGuiApplication::inputMethod(); - const QRect cursorRectangleDip = inputMethod->cursorRectangle().toRect(); - if (!cursorRectangleDip.isValid()) + const QRect cursorRectangle = inputMethod->cursorRectangle().toRect(); + if (!cursorRectangle.isValid()) return; - const QRect cursorRectangle = QWindowsScaling::mapToNative(cursorRectangleDip); qCDebug(lcQpaInputMethods) << __FUNCTION__<< cursorRectangle; @@ -308,11 +323,6 @@ void QWindowsInputContext::invokeAction(QInputMethod::Action action, int cursorP ImmReleaseContext(m_compositionContext.hwnd, himc); } -QWindowsInputContext *QWindowsInputContext::instance() -{ - return static_cast<QWindowsInputContext *>(QWindowsIntegration::instance()->inputContext()); -} - static inline QString getCompositionString(HIMC himc, DWORD dwIndex) { enum { bufferSize = 256 }; @@ -375,7 +385,7 @@ bool QWindowsInputContext::startComposition(HWND hwnd) QWindow *window = QGuiApplication::focusWindow(); if (!window) return false; - qCDebug(lcQpaInputMethods) << __FUNCTION__ << fo << window; + qCDebug(lcQpaInputMethods) << __FUNCTION__ << fo << window << "language=" << m_languageId; if (!fo || QWindowsWindow::handleOf(window) != hwnd) return false; initContext(hwnd, fo); @@ -559,6 +569,21 @@ bool QWindowsInputContext::handleIME_Request(WPARAM wParam, return false; } +void QWindowsInputContext::handleInputLanguageChanged(WPARAM wparam, LPARAM lparam) +{ + const LCID newLanguageId = languageIdFromLocaleId(lparam); + if (newLanguageId == m_languageId) + return; + const LCID oldLanguageId = m_languageId; + m_languageId = newLanguageId; + m_locale = qt_localeFromLCID(m_languageId); + emitLocaleChanged(); + + qCDebug(lcQpaInputMethods) << __FUNCTION__ << hex << showbase + << oldLanguageId << "->" << newLanguageId << "Character set:" + << DWORD(wparam) << dec << noshowbase << m_locale; +} + /*! \brief Determines the string for reconversion with selection. diff --git a/src/plugins/platforms/windows/qwindowsinputcontext.h b/src/plugins/platforms/windows/qwindowsinputcontext.h index 110986c20c..636d481e70 100644 --- a/src/plugins/platforms/windows/qwindowsinputcontext.h +++ b/src/plugins/platforms/windows/qwindowsinputcontext.h @@ -36,12 +36,14 @@ #include "qtwindows_additional.h" +#include <QtCore/QLocale> #include <QtCore/QPointer> #include <qpa/qplatforminputcontext.h> QT_BEGIN_NAMESPACE class QInputMethodEvent; +class QWindowsWindow; class QWindowsInputContext : public QPlatformInputContext { @@ -62,14 +64,16 @@ public: explicit QWindowsInputContext(); ~QWindowsInputContext(); + static void setWindowsImeEnabled(QWindowsWindow *platformWindow, bool enabled); + bool hasCapability(Capability capability) const Q_DECL_OVERRIDE; + QLocale locale() const Q_DECL_OVERRIDE { return m_locale; } + void reset() Q_DECL_OVERRIDE; void update(Qt::InputMethodQueries) Q_DECL_OVERRIDE; void invokeAction(QInputMethod::Action, int cursorPosition) Q_DECL_OVERRIDE; void setFocusObject(QObject *object) Q_DECL_OVERRIDE; - static QWindowsInputContext *instance(); - bool startComposition(HWND hwnd); bool composition(HWND hwnd, LPARAM lParam); bool endComposition(HWND hwnd); @@ -78,6 +82,7 @@ public: int reconvertString(RECONVERTSTRING *reconv); bool handleIME_Request(WPARAM wparam, LPARAM lparam, LRESULT *result); + void handleInputLanguageChanged(WPARAM wparam, LPARAM lparam); private slots: void cursorRectChanged(); @@ -93,6 +98,8 @@ private: static HIMC m_defaultContext; CompositionContext m_compositionContext; bool m_endCompositionRecursionGuard; + LCID m_languageId; + QLocale m_locale; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp index 089c3cd0fe..797a96cda7 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.cpp +++ b/src/plugins/platforms/windows/qwindowsintegration.cpp @@ -33,7 +33,6 @@ ****************************************************************************/ #include "qwindowsintegration.h" -#include "qwindowsscaling.h" #include "qwindowswindow.h" #include "qwindowscontext.h" #include "qwindowsopenglcontext.h" @@ -45,7 +44,6 @@ # include "qwindowsfontdatabase_ft.h" #endif #include "qwindowsfontdatabase.h" -#include "qwindowsguieventdispatcher.h" #ifndef QT_NO_CLIPBOARD # include "qwindowsclipboard.h" # ifndef QT_NO_DRAGANDDROP @@ -64,9 +62,11 @@ # include "qwindowssessionmanager.h" #endif #include <QtGui/private/qguiapplication_p.h> +#include <QtGui/private/qhighdpiscaling_p.h> #include <QtGui/qpa/qplatforminputcontextfactory_p.h> -#include <QtCore/private/qeventdispatcher_win_p.h> +#include <QtPlatformSupport/private/qwindowsguieventdispatcher_p.h> + #include <QtCore/QDebug> #include <QtCore/QVariant> @@ -223,12 +223,8 @@ QWindowsIntegrationPrivate::QWindowsIntegrationPrivate(const QStringList ¶mL m_context.setProcessDpiAwareness(dpiAwareness); dpiAwarenessSet = true; } - // Determine suitable scale factor, don't mix Windows and Qt scaling - if (dpiAwareness != QtWindows::ProcessDpiUnaware) - QWindowsScaling::setFactor(QWindowsScaling::determineUiScaleFactor()); qCDebug(lcQpaWindows) - << __FUNCTION__ << "DpiAwareness=" << dpiAwareness <<",Scaling=" - << QWindowsScaling::factor(); + << __FUNCTION__ << "DpiAwareness=" << dpiAwareness; m_context.initTouch(m_options); } @@ -258,10 +254,9 @@ QWindowsIntegration::~QWindowsIntegration() void QWindowsIntegration::initialize() { - if (QPlatformInputContext *pluginContext = QPlatformInputContextFactory::create()) - d->m_inputContext.reset(pluginContext); - else - d->m_inputContext.reset(new QWindowsInputContext); + QString icStr = QPlatformInputContextFactory::requested(); + icStr.isNull() ? d->m_inputContext.reset(new QWindowsInputContext) + : d->m_inputContext.reset(QPlatformInputContextFactory::create(icStr)); } bool QWindowsIntegration::hasCapability(QPlatformIntegration::Capability cap) const @@ -293,11 +288,11 @@ bool QWindowsIntegration::hasCapability(QPlatformIntegration::Capability cap) co return false; } -QWindowsWindowData QWindowsIntegration::createWindowData(QWindow *window) const +QPlatformWindow *QWindowsIntegration::createPlatformWindow(QWindow *window) const { QWindowsWindowData requested; requested.flags = window->flags(); - requested.geometry = QWindowsScaling::mapToNative(window->geometry()); + requested.geometry = QHighDpi::toNativePixels(window->geometry(), window); // Apply custom margins (see QWindowsWindow::setCustomMargins())). const QVariant customMarginsV = window->property("_q_windowsCustomMargins"); if (customMarginsV.isValid()) @@ -312,22 +307,30 @@ QWindowsWindowData QWindowsIntegration::createWindowData(QWindow *window) const << "\n Obtained : " << obtained.geometry << " margins=" << obtained.frame << " handle=" << obtained.hwnd << ' ' << obtained.flags << '\n'; - if (obtained.hwnd) { - if (requested.flags != obtained.flags) - window->setFlags(obtained.flags); - // Trigger geometry change signals of QWindow. - if ((obtained.flags & Qt::Desktop) != Qt::Desktop && requested.geometry != obtained.geometry) - QWindowSystemInterface::handleGeometryChange(window, QWindowsScaling::mapFromNative(obtained.geometry)); + if (Q_UNLIKELY(!obtained.hwnd)) + return Q_NULLPTR; + + QWindowsWindow *result = createPlatformWindowHelper(window, obtained); + Q_ASSERT(result); + + if (requested.flags != obtained.flags) + window->setFlags(obtained.flags); + // Trigger geometry/screen change signals of QWindow. + if ((obtained.flags & Qt::Desktop) != Qt::Desktop) { + if (requested.geometry != obtained.geometry) + QWindowSystemInterface::handleGeometryChange(window, obtained.geometry); + QPlatformScreen *screen = result->screenForGeometry(obtained.geometry); + if (screen && result->screen() != screen) + QWindowSystemInterface::handleWindowScreenChanged(window, screen->screen()); } - return obtained; + return result; } -QPlatformWindow *QWindowsIntegration::createPlatformWindow(QWindow *window) const +// Overridden to return a QWindowsDirect2DWindow in Direct2D plugin. +QWindowsWindow *QWindowsIntegration::createPlatformWindowHelper(QWindow *window, const QWindowsWindowData &data) const { - QWindowsWindowData data = createWindowData(window); - return data.hwnd ? new QWindowsWindow(window, data) - : Q_NULLPTR; + return new QWindowsWindow(window, data); } #ifndef QT_NO_OPENGL diff --git a/src/plugins/platforms/windows/qwindowsintegration.h b/src/plugins/platforms/windows/qwindowsintegration.h index fa5192ba03..cb10bf08f5 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.h +++ b/src/plugins/platforms/windows/qwindowsintegration.h @@ -62,7 +62,6 @@ public: bool hasCapability(QPlatformIntegration::Capability cap) const; - QWindowsWindowData createWindowData(QWindow *window) const; QPlatformWindow *createPlatformWindow(QWindow *window) const; #ifndef QT_NO_OPENGL QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const Q_DECL_OVERRIDE; @@ -101,6 +100,9 @@ public: QPlatformSessionManager *createPlatformSessionManager(const QString &id, const QString &key) const Q_DECL_OVERRIDE; #endif +protected: + virtual QWindowsWindow *createPlatformWindowHelper(QWindow *window, const QWindowsWindowData &) const; + private: QScopedPointer<QWindowsIntegrationPrivate> d; diff --git a/src/plugins/platforms/windows/qwindowskeymapper.cpp b/src/plugins/platforms/windows/qwindowskeymapper.cpp index d47c7df9e0..4c0b94e6e7 100644 --- a/src/plugins/platforms/windows/qwindowskeymapper.cpp +++ b/src/plugins/platforms/windows/qwindowskeymapper.cpp @@ -33,15 +33,16 @@ #include "qwindowskeymapper.h" #include "qwindowscontext.h" +#include "qwindowsintegration.h" #include "qwindowswindow.h" -#include "qwindowsguieventdispatcher.h" -#include "qwindowsscaling.h" #include "qwindowsinputcontext.h" #include <QtGui/QWindow> #include <qpa/qwindowsysteminterface.h> #include <private/qguiapplication_p.h> +#include <private/qhighdpiscaling_p.h> #include <QtGui/QKeyEvent> +#include <QtPlatformSupport/private/qwindowsguieventdispatcher_p.h> #if defined(WM_APPCOMMAND) # ifndef FAPPCOMMAND_MOUSE @@ -792,10 +793,12 @@ static void showSystemMenu(QWindow* w) #undef enabled #undef disabled #endif // !Q_OS_WINCE - const QPoint topLeft = topLevel->geometry().topLeft() * QWindowsScaling::factor(); + const QPoint pos = QHighDpi::toNativePixels(topLevel->geometry().topLeft(), topLevel); const int ret = TrackPopupMenuEx(menu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_NONOTIFY | TPM_RETURNCMD, - topLeft.x(), topLeft.y(), topLevelHwnd, 0); + pos.x(), pos.y(), + topLevelHwnd, + 0); if (ret) qWindowsWndProc(topLevelHwnd, WM_SYSCOMMAND, ret, 0); } @@ -1072,7 +1075,9 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, const MSG &ms // results, if we map this virtual key-code directly (for eg '?' US layouts). So try // to find the correct key using the current message parameters & keyboard state. if (uch.isNull() && msgType == WM_IME_KEYDOWN) { - if (!QWindowsInputContext::instance()->isComposing()) + const QWindowsInputContext *windowsInputContext = + qobject_cast<const QWindowsInputContext *>(QWindowsIntegration::instance()->inputContext()); + if (!(windowsInputContext && windowsInputContext->isComposing())) vk_key = ImmGetVirtualKey((HWND)window->winId()); BYTE keyState[256]; wchar_t newKey[3] = {0}; diff --git a/src/plugins/platforms/windows/qwindowsmime.cpp b/src/plugins/platforms/windows/qwindowsmime.cpp index 622352e987..0a2ba9b0e7 100644 --- a/src/plugins/platforms/windows/qwindowsmime.cpp +++ b/src/plugins/platforms/windows/qwindowsmime.cpp @@ -299,8 +299,6 @@ static bool qt_read_dibv5(QDataStream &s, QImage &image) return true; } -//#define QMIME_DEBUG - // helpers for using global memory static int getCf(const FORMATETC &formatetc) @@ -380,6 +378,73 @@ static bool canGetData(int cf, IDataObject * pDataObj) return true; } +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug d, const FORMATETC &tc) +{ + QDebugStateSaver saver(d); + d.nospace(); + d << "FORMATETC(cfFormat=" << tc.cfFormat << ' '; + switch (tc.cfFormat) { + case CF_TEXT: + d << "CF_TEXT"; + break; + case CF_BITMAP: + d << "CF_BITMAP"; + break; + case CF_TIFF: + d << "CF_TIFF"; + break; + case CF_OEMTEXT: + d << "CF_OEMTEXT"; + break; + case CF_DIB: + d << "CF_DIB"; + break; + case CF_DIBV5: + d << "CF_DIBV5"; + break; + case CF_UNICODETEXT: + d << "CF_UNICODETEXT"; + break; +#ifndef Q_OS_WINCE + case CF_ENHMETAFILE: + d << "CF_ENHMETAFILE"; + break; +#endif // !Q_OS_WINCE + default: + d << QWindowsMimeConverter::clipboardFormatName(tc.cfFormat); + break; + } + d << ", dwAspect=" << tc.dwAspect << ", lindex=" << tc.lindex + << ", tymed=" << tc.tymed << ", ptd=" << tc.ptd << ')'; + return d; +} + +QDebug operator<<(QDebug d, IDataObject *dataObj) +{ + QDebugStateSaver saver(d); + d.nospace(); + d.noquote(); + d << "IDataObject("; + if (dataObj) { // Output formats contained in IDataObject. + IEnumFORMATETC *enumFormatEtc; + if (SUCCEEDED(dataObj->EnumFormatEtc(DATADIR_GET, &enumFormatEtc)) && enumFormatEtc) { + FORMATETC formatEtc[1]; + ULONG fetched; + if (SUCCEEDED(enumFormatEtc->Reset())) { + while (SUCCEEDED(enumFormatEtc->Next(1, formatEtc, &fetched)) && fetched) + d << formatEtc[0] << ','; + enumFormatEtc->Release(); + } + } + } else { + d << '0'; + } + d << ')'; + return d; +} +#endif // !QT_NO_DEBUG_STREAM + /*! \class QWindowsMime \brief The QWindowsMime class maps open-standard MIME to Window Clipboard formats. @@ -894,11 +959,7 @@ QVariant QWindowsMimeHtml::convertToMime(const QString &mime, IDataObject *pData QVariant result; if (canConvertToMime(mime, pDataObj)) { QByteArray html = getData(CF_HTML, pDataObj); -#ifdef QMIME_DEBUG - qDebug("QWindowsMimeHtml::convertToMime"); - qDebug("raw :"); - qDebug(html); -#endif + qCDebug(lcQpaMime) << __FUNCTION__ << "raw:" << html; int start = html.indexOf("StartHTML:"); int end = html.indexOf("EndHTML:"); @@ -996,6 +1057,8 @@ QVector<FORMATETC> QWindowsMimeImage::formatsForMime(const QString &mimeType, co formatetcs += setCf(CF_DIBV5); formatetcs += setCf(CF_DIB); } + if (!formatetcs.isEmpty()) + qCDebug(lcQpaMime) << __FUNCTION__ << mimeType << formatetcs; return formatetcs; } @@ -1216,9 +1279,7 @@ QVariant QBuiltInMimes::convertToMime(const QString &mimeType, IDataObject *pDat if (canConvertToMime(mimeType, pDataObj)) { QByteArray data = getData(inFormats.key(mimeType), pDataObj); if (!data.isEmpty()) { -#ifdef QMIME_DEBUG - qDebug("QBuiltInMimes::convertToMime()"); -#endif + qCDebug(lcQpaMime) << __FUNCTION__; if (mimeType == QLatin1String("text/html") && preferredType == QVariant::String) { // text/html is in wide chars on windows (compatible with Mozilla) val = QString::fromWCharArray((const wchar_t *)data.data()); @@ -1326,6 +1387,8 @@ QVector<FORMATETC> QLastResortMimes::formatsForMime(const QString &mimeType, con that->formats.insert(cf, mimeType); formatetcs += setCf(cf); } + if (!formatetcs.isEmpty()) + qCDebug(lcQpaMime) << __FUNCTION__ << mimeType << formatetcs; return formatetcs; } static const char x_qt_windows_mime[] = "application/x-qt-windows-mime;value=\""; @@ -1400,11 +1463,8 @@ QString QLastResortMimes::mimeForFormat(const FORMATETC &formatetc) const if (!format.isEmpty()) return format; - wchar_t buffer[256]; - int len = GetClipboardFormatName(getCf(formatetc), buffer, 256); - - if (len) { - QString clipFormat = QString::fromWCharArray(buffer, len); + const QString clipFormat = QWindowsMimeConverter::clipboardFormatName(getCf(formatetc)); + if (!clipFormat.isEmpty()) { #ifndef QT_NO_DRAGANDDROP if (QInternalMimeData::canReadData(clipFormat)) format = clipFormat; @@ -1470,15 +1530,12 @@ QStringList QWindowsMimeConverter::allMimesForFormats(IDataObject *pDataObj) con if (hr == NOERROR) { FORMATETC fmtetc; while (S_OK == fmtenum->Next(1, &fmtetc, 0)) { -#if defined(QMIME_DEBUG) - wchar_t buf[256] = {0}; - GetClipboardFormatName(fmtetc.cfFormat, buf, 255); - qDebug("CF = %d : %s", fmtetc.cfFormat, qPrintable(QString::fromWCharArray(buf))); -#endif for (int i= m_mimes.size() - 1; i >= 0; --i) { QString format = m_mimes.at(i)->mimeForFormat(fmtetc); if (!format.isEmpty() && !formats.contains(format)) { formats += format; + if (QWindowsContext::verbose > 1 && lcQpaMime().isDebugEnabled()) + qCDebug(lcQpaMime) << __FUNCTION__ << fmtetc << format; } } // as documented in MSDN to avoid possible memleak @@ -1494,6 +1551,7 @@ QStringList QWindowsMimeConverter::allMimesForFormats(IDataObject *pDataObj) con QWindowsMime * QWindowsMimeConverter::converterFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const { ensureInitialized(); + qCDebug(lcQpaMime) << __FUNCTION__ << formatetc; for (int i = m_mimes.size()-1; i >= 0; --i) { if (m_mimes.at(i)->canConvertFromMime(formatetc, mimeData)) return m_mimes.at(i); @@ -1521,13 +1579,24 @@ QVector<FORMATETC> QWindowsMimeConverter::allFormatsForMime(const QMimeData *mim void QWindowsMimeConverter::ensureInitialized() const { if (m_mimes.isEmpty()) { - m_mimes << new QWindowsMimeImage << new QLastResortMimes + m_mimes +#ifndef QT_NO_IMAGEFORMAT_BMP + << new QWindowsMimeImage +#endif //QT_NO_IMAGEFORMAT_BMP + << new QLastResortMimes << new QWindowsMimeText << new QWindowsMimeURI << new QWindowsMimeHtml << new QBuiltInMimes; m_internalMimeCount = m_mimes.size(); } } +QString QWindowsMimeConverter::clipboardFormatName(int cf) +{ + wchar_t buf[256] = {0}; + return GetClipboardFormatName(cf, buf, 255) + ? QString::fromWCharArray(buf) : QString(); +} + QVariant QWindowsMimeConverter::convertToMime(const QStringList &mimeTypes, IDataObject *pDataObj, QVariant::Type preferredType, diff --git a/src/plugins/platforms/windows/qwindowsmime.h b/src/plugins/platforms/windows/qwindowsmime.h index 952410e14b..1ec0dccdf8 100644 --- a/src/plugins/platforms/windows/qwindowsmime.h +++ b/src/plugins/platforms/windows/qwindowsmime.h @@ -42,6 +42,7 @@ QT_BEGIN_NAMESPACE +class QDebug; class QMimeData; class QWindowsMime @@ -83,6 +84,8 @@ public: void registerMime(QWindowsMime *mime); void unregisterMime(QWindowsMime *mime) { m_mimes.removeOne(mime); } + static QString clipboardFormatName(int cf); + private: void ensureInitialized() const; @@ -90,6 +93,11 @@ private: mutable int m_internalMimeCount; }; +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug, const FORMATETC &); +QDebug operator<<(QDebug d, IDataObject *); +#endif + QT_END_NAMESPACE #endif // QWINDOWSMIME_H diff --git a/src/plugins/platforms/windows/qwindowsmousehandler.cpp b/src/plugins/platforms/windows/qwindowsmousehandler.cpp index e83354157b..e26010b5c4 100644 --- a/src/plugins/platforms/windows/qwindowsmousehandler.cpp +++ b/src/plugins/platforms/windows/qwindowsmousehandler.cpp @@ -218,10 +218,8 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, const QPoint globalPosition = winEventPosition; const QPoint clientPosition = QWindowsGeometryHint::mapFromGlobal(hwnd, globalPosition); const Qt::MouseButtons buttons = QWindowsMouseHandler::queryMouseButtons(); - QWindowSystemInterface::handleFrameStrutMouseEvent(window, - clientPosition / QWindowsScaling::factor(), - globalPosition / QWindowsScaling::factor(), - buttons, + QWindowSystemInterface::handleFrameStrutMouseEvent(window, clientPosition, + globalPosition, buttons, QWindowsKeyMapper::queryKeyboardModifiers(), source); return false; // Allow further event processing (dragging of windows). @@ -286,7 +284,7 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, // ChildWindowFromPointEx() may not find the Qt window (failing with ERROR_ACCESS_DENIED) if (!currentWindowUnderMouse) { const QRect clientRect(QPoint(0, 0), window->size()); - if (clientRect.contains(winEventPosition / QWindowsScaling::factor())) + if (clientRect.contains(winEventPosition)) currentWindowUnderMouse = window; } @@ -373,14 +371,14 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, m_windowUnderMouse = currentWindowUnderMouse; } - QWindowSystemInterface::handleMouseEvent(window, - winEventPosition / QWindowsScaling::factor(), - globalPosition / QWindowsScaling::factor(), - buttons, + QWindowSystemInterface::handleMouseEvent(window, winEventPosition, globalPosition, buttons, QWindowsKeyMapper::queryKeyboardModifiers(), source); m_previousCaptureWindow = hasCapture ? window : 0; - return true; + // QTBUG-48117, force synchronous handling for the extra buttons so that WM_APPCOMMAND + // is sent for unhandled WM_XBUTTONDOWN. + return (msg.message != WM_XBUTTONUP && msg.message != WM_XBUTTONDOWN && msg.message != WM_XBUTTONDBLCLK) + || QWindowSystemInterface::flushWindowSystemEvents(); } static bool isValidWheelReceiver(QWindow *candidate) @@ -411,11 +409,9 @@ static void redirectWheelEvent(QWindow *window, const QPoint &globalPos, int del } if (handleEvent) { - const QPoint posDip = QWindowsGeometryHint::mapFromGlobal(receiver, globalPos) / QWindowsScaling::factor(); QWindowSystemInterface::handleWheelEvent(receiver, - posDip, globalPos / QWindowsScaling::factor(), - delta / QWindowsScaling::factor(), - orientation, mods); + QWindowsGeometryHint::mapFromGlobal(receiver, globalPos), + globalPos, delta, orientation, mods); } } @@ -492,7 +488,12 @@ bool QWindowsMouseHandler::translateTouchEvent(QWindow *window, HWND, return true; } - const QRect screenGeometry = window->screen()->geometry(); + const QScreen *screen = window->screen(); + if (!screen) + screen = QGuiApplication::primaryScreen(); + if (!screen) + return true; + const QRect screenGeometry = screen->geometry(); const int winTouchPointCount = msg.wParam; QScopedArrayPointer<TOUCHINPUT> winTouchInputs(new TOUCHINPUT[winTouchPointCount]); @@ -503,7 +504,6 @@ bool QWindowsMouseHandler::translateTouchEvent(QWindow *window, HWND, Qt::TouchPointStates allStates = 0; QWindowsContext::user32dll.getTouchInputInfo((HANDLE) msg.lParam, msg.wParam, winTouchInputs.data(), sizeof(TOUCHINPUT)); - const qreal screenPosFactor = 0.01 / qreal(QWindowsScaling::factor()); for (int i = 0; i < winTouchPointCount; ++i) { const TOUCHINPUT &winTouchInput = winTouchInputs[i]; int id = m_touchInputIDToTouchPointID.value(winTouchInput.dwID, -1); @@ -517,9 +517,9 @@ bool QWindowsMouseHandler::translateTouchEvent(QWindow *window, HWND, if (m_lastTouchPositions.contains(id)) touchPoint.normalPosition = m_lastTouchPositions.value(id); - const QPointF screenPos = QPointF(winTouchInput.x, winTouchInput.y) * screenPosFactor; + const QPointF screenPos = QPointF(winTouchInput.x, winTouchInput.y) / qreal(100.); if (winTouchInput.dwMask & TOUCHINPUTMASKF_CONTACTAREA) - touchPoint.area.setSize(QSizeF(winTouchInput.cxContact, winTouchInput.cyContact) * screenPosFactor); + touchPoint.area.setSize(QSizeF(winTouchInput.cxContact, winTouchInput.cyContact) / qreal(100.)); touchPoint.area.moveCenter(screenPos); QPointF normalPosition = QPointF(screenPos.x() / screenGeometry.width(), screenPos.y() / screenGeometry.height()); @@ -583,7 +583,12 @@ bool QWindowsMouseHandler::translateGestureEvent(QWindow *window, HWND hwnd, if (gi.dwID != GID_DIRECTMANIPULATION) return true; static QPoint lastTouchPos; - const QRect screenGeometry = window->screen()->geometry(); + const QScreen *screen = window->screen(); + if (!screen) + screen = QGuiApplication::primaryScreen(); + if (!screen) + return true; + const QRect screenGeometry = screen->geometry(); QWindowSystemInterface::TouchPoint touchPoint; static QWindowSystemInterface::TouchPoint touchPoint2; touchPoint.id = 0;//gi.dwInstanceID; diff --git a/src/plugins/platforms/windows/qwindowsnativeimage.h b/src/plugins/platforms/windows/qwindowsnativeimage.h index 6f9ce93ef0..80a1de1ea5 100644 --- a/src/plugins/platforms/windows/qwindowsnativeimage.h +++ b/src/plugins/platforms/windows/qwindowsnativeimage.h @@ -57,8 +57,6 @@ public: HDC hdc() const { return m_hdc; } - void setDevicePixelRatio(qreal scaleFactor) { m_image.setDevicePixelRatio(scaleFactor); } - static QImage::Format systemFormat(); private: diff --git a/src/plugins/platforms/windows/qwindowsole.cpp b/src/plugins/platforms/windows/qwindowsole.cpp index 6f5a521af8..e480c1ebcf 100644 --- a/src/plugins/platforms/windows/qwindowsole.cpp +++ b/src/plugins/platforms/windows/qwindowsole.cpp @@ -132,12 +132,6 @@ QWindowsOleDataObject::GetData(LPFORMATETC pformatetc, LPSTGMEDIUM pmedium) { HRESULT hr = ResultFromScode(DATA_E_FORMATETC); - if (QWindowsContext::verbose > 1 && lcQpaMime().isDebugEnabled()) { - wchar_t buf[256] = {0}; - GetClipboardFormatName(pformatetc->cfFormat, buf, 255); - qCDebug(lcQpaMime) <<__FUNCTION__ << "CF = " << pformatetc->cfFormat << QString::fromWCharArray(buf); - } - if (data) { const QWindowsMimeConverter &mc = QWindowsContext::instance()->mimeConverter(); if (QWindowsMime *converter = mc.converterFromMime(*pformatetc, data)) @@ -145,11 +139,8 @@ QWindowsOleDataObject::GetData(LPFORMATETC pformatetc, LPSTGMEDIUM pmedium) hr = ResultFromScode(S_OK); } - if (QWindowsContext::verbose > 1) { - wchar_t buf[256] = {0}; - GetClipboardFormatName(pformatetc->cfFormat, buf, 255); - qCDebug(lcQpaMime) <<__FUNCTION__ << "CF = " << pformatetc->cfFormat << " returns 0x" << int(hr) << dec; - } + if (QWindowsContext::verbose > 1 && lcQpaMime().isDebugEnabled()) + qCDebug(lcQpaMime) <<__FUNCTION__ << *pformatetc << "returns" << hex << showbase << quint64(hr); return hr; } @@ -211,7 +202,7 @@ STDMETHODIMP QWindowsOleDataObject::EnumFormatEtc(DWORD dwDirection, LPENUMFORMATETC FAR* ppenumFormatEtc) { if (QWindowsContext::verbose > 1) - qCDebug(lcQpaMime) << __FUNCTION__; + qCDebug(lcQpaMime) << __FUNCTION__ << "dwDirection=" << dwDirection; if (!data) return ResultFromScode(DATA_E_FORMATETC); @@ -274,7 +265,7 @@ QWindowsOleEnumFmtEtc::QWindowsOleEnumFmtEtc(const QVector<FORMATETC> &fmtetcs) m_dwRefs(1), m_nIndex(0), m_isNull(false) { if (QWindowsContext::verbose > 1) - qCDebug(lcQpaMime) << __FUNCTION__; + qCDebug(lcQpaMime) << __FUNCTION__ << fmtetcs; m_lpfmtetcs.reserve(fmtetcs.count()); for (int idx = 0; idx < fmtetcs.count(); ++idx) { LPFORMATETC destetc = new FORMATETC(); diff --git a/src/plugins/platforms/windows/qwindowsopengltester.cpp b/src/plugins/platforms/windows/qwindowsopengltester.cpp index 9ebd946ce4..e32a7e32af 100644 --- a/src/plugins/platforms/windows/qwindowsopengltester.cpp +++ b/src/plugins/platforms/windows/qwindowsopengltester.cpp @@ -98,6 +98,7 @@ GpuDescription GpuDescription::detect() #endif } +#ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug d, const GpuDescription &gd) { QDebugStateSaver s(d); @@ -109,6 +110,7 @@ QDebug operator<<(QDebug d, const GpuDescription &gd) << ", version=" << gd.driverVersion << ", " << gd.description << ')'; return d; } +#endif // !QT_NO_DEBUG_STREAM // Return printable string formatted like the output of the dxdiag tool. QString GpuDescription::toString() const @@ -224,7 +226,7 @@ QWindowsOpenGLTester::Renderers QWindowsOpenGLTester::detectSupportedRenderers(c #elif defined(Q_OS_WINCE) return QWindowsOpenGLTester::Gles; #else - QOpenGLConfig::Gpu qgpu = QOpenGLConfig::Gpu::fromDevice(gpu.vendorId, gpu.deviceId, gpu.driverVersion); + QOpenGLConfig::Gpu qgpu = QOpenGLConfig::Gpu::fromDevice(gpu.vendorId, gpu.deviceId, gpu.driverVersion, gpu.description); SupportedRenderersCache *srCache = supportedRenderersCache(); SupportedRenderersCache::const_iterator it = srCache->find(qgpu); if (it != srCache->cend()) @@ -250,19 +252,19 @@ QWindowsOpenGLTester::Renderers QWindowsOpenGLTester::detectSupportedRenderers(c qCDebug(lcQpaGl) << "GPU features:" << features; if (features.contains(QStringLiteral("disable_desktopgl"))) { // Qt-specific - qCWarning(lcQpaGl) << "Disabling Desktop GL: " << gpu; + qCDebug(lcQpaGl) << "Disabling Desktop GL: " << gpu; result &= ~QWindowsOpenGLTester::DesktopGl; } if (features.contains(QStringLiteral("disable_angle"))) { // Qt-specific keyword - qCWarning(lcQpaGl) << "Disabling ANGLE: " << gpu; + qCDebug(lcQpaGl) << "Disabling ANGLE: " << gpu; result &= ~QWindowsOpenGLTester::GlesMask; } else { if (features.contains(QStringLiteral("disable_d3d11"))) { // standard keyword - qCWarning(lcQpaGl) << "Disabling D3D11: " << gpu; + qCDebug(lcQpaGl) << "Disabling D3D11: " << gpu; result &= ~QWindowsOpenGLTester::AngleRendererD3d11; } if (features.contains(QStringLiteral("disable_d3d9"))) { // Qt-specific - qCWarning(lcQpaGl) << "Disabling D3D9: " << gpu; + qCDebug(lcQpaGl) << "Disabling D3D9: " << gpu; result &= ~QWindowsOpenGLTester::AngleRendererD3d9; } } diff --git a/src/plugins/platforms/windows/qwindowsopengltester.h b/src/plugins/platforms/windows/qwindowsopengltester.h index 748885542d..f22031aa4e 100644 --- a/src/plugins/platforms/windows/qwindowsopengltester.h +++ b/src/plugins/platforms/windows/qwindowsopengltester.h @@ -36,7 +36,7 @@ #include <QtCore/QByteArray> #include <QtCore/QFlags> -#include <private/qversionnumber_p.h> +#include <QtCore/qversionnumber.h> QT_BEGIN_NAMESPACE @@ -60,7 +60,9 @@ struct GpuDescription QByteArray description; }; +#ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug d, const GpuDescription &gd); +#endif class QWindowsOpenGLTester { diff --git a/src/plugins/platforms/windows/qwindowsscaling.cpp b/src/plugins/platforms/windows/qwindowsscaling.cpp deleted file mode 100644 index 6ab7f254fd..0000000000 --- a/src/plugins/platforms/windows/qwindowsscaling.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qwindowsscaling.h" -#include "qwindowsscreen.h" - -#include <QtCore/QDebug> -#include <QtCore/QCoreApplication> - -QT_BEGIN_NAMESPACE - -/*! - \class QWindowsScaling - \brief Windows scaling utilities - - \internal - \ingroup qt-lighthouse-win -*/ - -int QWindowsScaling::m_factor = 1; - -static const char devicePixelRatioEnvVar[] = "QT_DEVICE_PIXEL_RATIO"; - -// Suggest a scale factor by checking monitor sizes. -int QWindowsScaling::determineUiScaleFactor() -{ - if (!qEnvironmentVariableIsSet(devicePixelRatioEnvVar)) - return 1; - const QByteArray envDevicePixelRatioEnv = qgetenv(devicePixelRatioEnvVar); - // Auto: Suggest a scale factor by checking monitor resolution. - if (envDevicePixelRatioEnv == "auto") { - const int maxResolution = QWindowsScreen::maxMonitorHorizResolution(); - return maxResolution > 180 ? maxResolution / 96 : 1; - } - // Get factor from environment - bool ok = false; - const int envFactor = envDevicePixelRatioEnv.toInt(&ok); - return ok && envFactor > 0 ? envFactor : 1; -} - -QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsscaling.h b/src/plugins/platforms/windows/qwindowsscaling.h deleted file mode 100644 index 39e554bbe2..0000000000 --- a/src/plugins/platforms/windows/qwindowsscaling.h +++ /dev/null @@ -1,106 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QWINDOWSSCALING_H -#define QWINDOWSSCALING_H - -#include <QtGui/QRegion> -#include <QtCore/QVector> -#include <QtCore/QRect> - -QT_BEGIN_NAMESPACE - -enum -#if defined(Q_COMPILER_CLASS_ENUM) || defined(Q_CC_MSVC) - : int -#endif -{ QWINDOWSIZE_MAX = 16777215 }; - -class QWindowsScaling { -public: - static bool isActive() { return m_factor > 1; } - static int factor() { return m_factor; } - static void setFactor(int factor) { m_factor = factor; } - static int determineUiScaleFactor(); - - // Scaling helpers for size constraints. - static int mapToNativeConstrained(int qt) - { return m_factor != 1 && qt > 0 && qt < QWINDOWSIZE_MAX ? qt * m_factor : qt; } - - static int mapFromNativeConstrained(int dp) - { return m_factor != 1 && dp > 0 && dp < QWINDOWSIZE_MAX ? dp / m_factor : dp; } - - static QSize mapToNativeConstrained(const QSize &qt) - { return QSize(mapToNativeConstrained(qt.width()), mapToNativeConstrained(qt.height())); } - - static QRect mapToNative(const QRect &qRect) - { - return QRect(qRect.x() * m_factor, qRect.y() * m_factor, qRect.width() * m_factor, qRect.height() * m_factor); - } - - static QRect mapFromNative(const QRect &dp) - { - return isActive() ? - QRect(dp.x() / m_factor, dp.y() / m_factor, (dp.width() + 1) / m_factor, (dp.height() + 1) / m_factor) : - dp; - } - - static QRegion mapToNative(const QRegion ®ionQt) - { - if (!QWindowsScaling::isActive() || regionQt.isEmpty()) - return regionQt; - - QRegion result; - foreach (const QRect &rectQt, regionQt.rects()) - result += QWindowsScaling::mapToNative(rectQt); - return result; - } - - static QRegion mapFromNative(const QRegion ®ionDp) - { - if (!QWindowsScaling::isActive() || regionDp.isEmpty()) - return regionDp; - - QRegion result; - foreach (const QRect &rectDp, regionDp.rects()) - result += QWindowsScaling::mapFromNative(rectDp); - return result; - } - -private: - static int m_factor; -}; - -QT_END_NAMESPACE - -#endif // QWINDOWSSCALING_H diff --git a/src/plugins/platforms/windows/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp index 7756c77001..e69665e4a9 100644 --- a/src/plugins/platforms/windows/qwindowsscreen.cpp +++ b/src/plugins/platforms/windows/qwindowsscreen.cpp @@ -43,6 +43,7 @@ #include <QtGui/QPixmap> #include <QtGui/QGuiApplication> #include <qpa/qwindowsysteminterface.h> +#include <private/qhighdpiscaling_p.h> #include <QtGui/QScreen> #include <QtCore/QDebug> @@ -171,6 +172,7 @@ static inline WindowsScreenDataList monitorData() return result; } +#ifndef QT_NO_DEBUG_STREAM static QDebug operator<<(QDebug dbg, const QWindowsScreenData &d) { QDebugStateSaver saver(dbg); @@ -191,16 +193,7 @@ static QDebug operator<<(QDebug dbg, const QWindowsScreenData &d) dbg << " lock screen"; return dbg; } - -// Return the cursor to be shared by all screens (virtual desktop). -static inline QSharedPointer<QPlatformCursor> sharedCursor() -{ -#ifndef QT_NO_CURSOR - if (const QScreen *primaryScreen = QGuiApplication::primaryScreen()) - return static_cast<const QWindowsScreen *>(primaryScreen->handle())->cursorPtr(); -#endif - return QSharedPointer<QPlatformCursor>(new QWindowsCursor); -} +#endif // !QT_NO_DEBUG_STREAM /*! \class QWindowsScreen @@ -213,41 +206,19 @@ static inline QSharedPointer<QPlatformCursor> sharedCursor() QWindowsScreen::QWindowsScreen(const QWindowsScreenData &data) : m_data(data) #ifndef QT_NO_CURSOR - ,m_cursor(sharedCursor()) + , m_cursor(new QWindowsCursor(this)) #endif { } -BOOL QT_WIN_CALLBACK monitorResolutionEnumCallback(HMONITOR hMonitor, HDC, LPRECT, LPARAM p) -{ - QWindowsScreenData data; - if (monitorData(hMonitor, &data)) { - int *maxHorizResolution = reinterpret_cast<int *>(p); - const int horizResolution = qRound(data.dpi.first); - if (horizResolution > *maxHorizResolution) - *maxHorizResolution = horizResolution; - } - return TRUE; -} - -int QWindowsScreen::maxMonitorHorizResolution() -{ - int result = 0; - EnumDisplayMonitors(0, 0, monitorResolutionEnumCallback, (LPARAM)&result); - return result; -} - Q_GUI_EXPORT QPixmap qt_pixmapFromWinHBITMAP(HBITMAP bitmap, int hbitmapFormat = 0); -QPixmap QWindowsScreen::grabWindow(WId window, int qX, int qY, int qWidth, int qHeight) const +QPixmap QWindowsScreen::grabWindow(WId window, int x, int y, int width, int height) const { RECT r; HWND hwnd = window ? (HWND)window : GetDesktopWindow(); GetClientRect(hwnd, &r); - const int x = qX * QWindowsScaling::factor(); - const int y = qY * QWindowsScaling::factor(); - int width = qWidth * QWindowsScaling::factor(); - int height = qHeight * QWindowsScaling::factor(); + if (width < 0) width = r.right - r.left; if (height < 0) height = r.bottom - r.top; @@ -271,10 +242,6 @@ QPixmap QWindowsScreen::grabWindow(WId window, int qX, int qY, int qWidth, int q DeleteObject(bitmap); ReleaseDC(0, display_dc); - if (QWindowsScaling::isActive()) { - const qreal factor = 1.0 / qreal(QWindowsScaling::factor()); - return pixmap.transformed(QTransform::fromScale(factor, factor)); - } return pixmap; } @@ -285,7 +252,7 @@ QPixmap QWindowsScreen::grabWindow(WId window, int qX, int qY, int qWidth, int q QWindow *QWindowsScreen::topLevelAt(const QPoint &point) const { QWindow *result = 0; - if (QWindow *child = QWindowsScreen::windowAt(point * QWindowsScaling::factor(), CWP_SKIPINVISIBLE)) + if (QWindow *child = QWindowsScreen::windowAt(point, CWP_SKIPINVISIBLE)) result = QWindowsWindow::topLevelOf(child); qCDebug(lcQpaWindows) <<__FUNCTION__ << point << result; return result; @@ -301,16 +268,13 @@ QWindow *QWindowsScreen::windowAt(const QPoint &screenPoint, unsigned flags) return result; } -QWindowsScreen *QWindowsScreen::screenOf(const QWindow *w) +qreal QWindowsScreen::pixelDensity() const { - if (w) - if (const QScreen *s = w->screen()) - if (QPlatformScreen *pscr = s->handle()) - return static_cast<QWindowsScreen *>(pscr); - if (const QScreen *ps = QGuiApplication::primaryScreen()) - if (QPlatformScreen *ppscr = ps->handle()) - return static_cast<QWindowsScreen *>(ppscr); - return 0; + // QTBUG-49195: Use logical DPI instead of physical DPI to calculate + // the pixel density since it is reflects the Windows UI scaling. + // High DPI auto scaling should be disabled when the user chooses + // small fonts on a High DPI monitor, resulting in lower logical DPI. + return qRound(logicalDpi().first / 96); } /*! @@ -542,7 +506,7 @@ void QWindowsScreenManager::clearScreens() const QWindowsScreen *QWindowsScreenManager::screenAtDp(const QPoint &p) const { foreach (QWindowsScreen *scr, m_screens) { - if (scr->geometryDp().contains(p)) + if (scr->geometry().contains(p)) return scr; } return Q_NULLPTR; diff --git a/src/plugins/platforms/windows/qwindowsscreen.h b/src/plugins/platforms/windows/qwindowsscreen.h index 7352c45777..879cda047e 100644 --- a/src/plugins/platforms/windows/qwindowsscreen.h +++ b/src/plugins/platforms/windows/qwindowsscreen.h @@ -34,7 +34,6 @@ #ifndef QWINDOWSSCREEN_H #define QWINDOWSSCREEN_H -#include "qwindowsscaling.h" #include "qtwindowsglobal.h" #ifdef Q_OS_WINCE # include "qplatformfunctions_wince.h" @@ -43,7 +42,7 @@ #include <QtCore/QList> #include <QtCore/QVector> #include <QtCore/QPair> -#include <QtCore/QSharedPointer> +#include <QtCore/QScopedPointer> #include <qpa/qplatformscreen.h> QT_BEGIN_NAMESPACE @@ -75,23 +74,19 @@ class QWindowsScreen : public QPlatformScreen { public: #ifndef QT_NO_CURSOR - typedef QSharedPointer<QPlatformCursor> CursorPtr; + typedef QScopedPointer<QPlatformCursor> CursorPtr; #endif explicit QWindowsScreen(const QWindowsScreenData &data); - static QWindowsScreen *screenOf(const QWindow *w = 0); - - QRect geometryDp() const { return m_data.geometry; } - QRect geometry() const Q_DECL_OVERRIDE { return QWindowsScaling::mapFromNative(geometryDp()); } - QRect availableGeometryDp() const { return m_data.availableGeometry; } - QRect availableGeometry() const Q_DECL_OVERRIDE { return QWindowsScaling::mapFromNative(availableGeometryDp()); } + QRect geometry() const Q_DECL_OVERRIDE { return m_data.geometry; } + QRect availableGeometry() const Q_DECL_OVERRIDE { return m_data.availableGeometry; } int depth() const Q_DECL_OVERRIDE { return m_data.depth; } QImage::Format format() const Q_DECL_OVERRIDE { return m_data.format; } QSizeF physicalSize() const Q_DECL_OVERRIDE { return m_data.physicalSizeMM; } - QDpi logicalDpi() const Q_DECL_OVERRIDE - { return QDpi(m_data.dpi.first / QWindowsScaling::factor(), m_data.dpi.second / QWindowsScaling::factor()); } - qreal devicePixelRatio() const Q_DECL_OVERRIDE { return QWindowsScaling::factor(); } + QDpi logicalDpi() const Q_DECL_OVERRIDE { return m_data.dpi; } + qreal pixelDensity() const Q_DECL_OVERRIDE; + qreal devicePixelRatio() const Q_DECL_OVERRIDE { return 1.0; } qreal refreshRate() const Q_DECL_OVERRIDE { return m_data.refreshRateHz; } QString name() const Q_DECL_OVERRIDE { return m_data.name; } Qt::ScreenOrientation orientation() const Q_DECL_OVERRIDE { return m_data.orientation; } @@ -112,7 +107,6 @@ public: #endif // !QT_NO_CURSOR const QWindowsScreenData &data() const { return m_data; } - static int maxMonitorHorizResolution(); private: QWindowsScreenData m_data; diff --git a/src/plugins/platforms/windows/qwindowstabletsupport.cpp b/src/plugins/platforms/windows/qwindowstabletsupport.cpp index 7b871be015..b27811df9e 100644 --- a/src/plugins/platforms/windows/qwindowstabletsupport.cpp +++ b/src/plugins/platforms/windows/qwindowstabletsupport.cpp @@ -32,7 +32,6 @@ ****************************************************************************/ #include "qwindowstabletsupport.h" -#include "qwindowsscaling.h" #ifndef QT_NO_TABLETEVENT @@ -55,7 +54,7 @@ #include <QtCore/private/qsystemlibrary_p.h> // Note: The definition of the PACKET structure in pktdef.h depends on this define. -#define PACKETDATA (PK_X | PK_Y | PK_BUTTONS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION | PK_CURSOR | PK_Z) +#define PACKETDATA (PK_X | PK_Y | PK_BUTTONS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION | PK_CURSOR | PK_Z | PK_TIME) #include <pktdef.h> QT_BEGIN_NAMESPACE @@ -303,8 +302,11 @@ static inline QTabletEvent::PointerType pointerType(unsigned currentCursor) return QTabletEvent::UnknownPointer; } +#ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug d, const QWindowsTabletDeviceData &t) { + QDebugStateSaver saver(d); + d.nospace(); d << "TabletDevice id:" << t.uniqueId << " pressure: " << t.minPressure << ".." << t.maxPressure << " tan pressure: " << t.minTanPressure << ".." << t.maxTanPressure << " area:" << t.minX << t.minY <<t.minZ @@ -312,6 +314,7 @@ QDebug operator<<(QDebug d, const QWindowsTabletDeviceData &t) << " pointer " << t.currentPointerType; return d; } +#endif // !QT_NO_DEBUG_STREAM QWindowsTabletDeviceData QWindowsTabletSupport::tabletInit(const quint64 uniqueId, const UINT cursorType) const { @@ -343,17 +346,18 @@ QWindowsTabletDeviceData QWindowsTabletSupport::tabletInit(const quint64 uniqueI bool QWindowsTabletSupport::translateTabletProximityEvent(WPARAM /* wParam */, LPARAM lParam) { + PACKET proximityBuffer[1]; // we are only interested in the first packet in this case + const int totalPacks = QWindowsTabletSupport::m_winTab32DLL.wTPacketsGet(m_context, 1, proximityBuffer); + if (!totalPacks) + return false; if (!LOWORD(lParam)) { qCDebug(lcQpaTablet) << "leave proximity for device #" << m_currentDevice; - QWindowSystemInterface::handleTabletLeaveProximityEvent(m_devices.at(m_currentDevice).currentDevice, + QWindowSystemInterface::handleTabletLeaveProximityEvent(proximityBuffer[0].pkTime, + m_devices.at(m_currentDevice).currentDevice, m_devices.at(m_currentDevice).currentPointerType, m_devices.at(m_currentDevice).uniqueId); return true; } - PACKET proximityBuffer[1]; // we are only interested in the first packet in this case - const int totalPacks = QWindowsTabletSupport::m_winTab32DLL.wTPacketsGet(m_context, 1, proximityBuffer); - if (!totalPacks) - return false; const UINT currentCursor = proximityBuffer[0].pkCursor; UINT physicalCursorId; QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_CURSORS + currentCursor, CSR_PHYSID, &physicalCursorId); @@ -371,7 +375,8 @@ bool QWindowsTabletSupport::translateTabletProximityEvent(WPARAM /* wParam */, L m_devices[m_currentDevice].currentPointerType = pointerType(currentCursor); qCDebug(lcQpaTablet) << "enter proximity for device #" << m_currentDevice << m_devices.at(m_currentDevice); - QWindowSystemInterface::handleTabletEnterProximityEvent(m_devices.at(m_currentDevice).currentDevice, + QWindowSystemInterface::handleTabletEnterProximityEvent(proximityBuffer[0].pkTime, + m_devices.at(m_currentDevice).currentDevice, m_devices.at(m_currentDevice).currentPointerType, m_devices.at(m_currentDevice).uniqueId); return true; @@ -399,8 +404,7 @@ bool QWindowsTabletSupport::translateTabletPacketEvent() // in which case we snap the position to the mouse position. // It seems there is no way to find out the mode programmatically, the LOGCONTEXT orgX/Y/Ext // area is always the virtual desktop. - const QRect virtualDesktopArea - = QWindowsScaling::mapToNative(QGuiApplication::primaryScreen()->virtualGeometry()); + const QRect virtualDesktopArea = QGuiApplication::primaryScreen()->virtualGeometry(); qCDebug(lcQpaTablet) << __FUNCTION__ << "processing " << packetCount << "target:" << QGuiApplicationPrivate::tabletPressTarget; @@ -420,7 +424,7 @@ bool QWindowsTabletSupport::translateTabletPacketEvent() QPoint globalPos = globalPosF.toPoint(); // Get Mouse Position and compare to tablet info - QPoint mouseLocation = QWindowsCursor::mousePosition(); + const QPoint mouseLocation = QWindowsCursor::mousePosition(); // Positions should be almost the same if we are in absolute // mode. If they are not, use the mouse location. @@ -475,9 +479,7 @@ bool QWindowsTabletSupport::translateTabletPacketEvent() << tiltY << "tanP:" << tangentialPressure << "rotation:" << rotation; } - const QPointF localPosDip = QPointF(localPos / QWindowsScaling::factor()); - const QPointF globalPosDip = globalPosF / qreal(QWindowsScaling::factor()); - QWindowSystemInterface::handleTabletEvent(target, localPosDip, globalPosDip, + QWindowSystemInterface::handleTabletEvent(target, packet.pkTime, QPointF(localPos), globalPosF, currentDevice, currentPointer, static_cast<Qt::MouseButtons>(packet.pkButtons), pressureNew, tiltX, tiltY, diff --git a/src/plugins/platforms/windows/qwindowstabletsupport.h b/src/plugins/platforms/windows/qwindowstabletsupport.h index 718ae98572..a6d2771206 100644 --- a/src/plugins/platforms/windows/qwindowstabletsupport.h +++ b/src/plugins/platforms/windows/qwindowstabletsupport.h @@ -97,7 +97,9 @@ struct QWindowsTabletDeviceData int currentPointerType; }; +#ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug d, const QWindowsTabletDeviceData &t); +#endif class QWindowsTabletSupport { diff --git a/src/plugins/platforms/windows/qwindowstheme.cpp b/src/plugins/platforms/windows/qwindowstheme.cpp index d3f67e9eaa..877bdfec17 100644 --- a/src/plugins/platforms/windows/qwindowstheme.cpp +++ b/src/plugins/platforms/windows/qwindowstheme.cpp @@ -43,7 +43,6 @@ #include "qwindowsintegration.h" #include "qt_windows.h" #include "qwindowsfontdatabase.h" -#include "qwindowsscaling.h" #ifdef Q_OS_WINCE # include "qplatformfunctions_wince.h" # include "winuser.h" @@ -68,6 +67,7 @@ #include <QtGui/QPainter> #include <QtGui/QPixmapCache> #include <qpa/qwindowsysteminterface.h> +#include <private/qhighdpiscaling_p.h> #include <private/qsystemlibrary_p.h> #include <algorithm> @@ -393,6 +393,8 @@ QVariant QWindowsTheme::themeHint(ThemeHint hint) const return QVariant(booleanSystemParametersInfo(SPI_GETSNAPTODEFBUTTON, false)); case ContextMenuOnMouseRelease: return QVariant(true); + case WheelScrollLines: + return dWordSystemParametersInfo(SPI_GETWHEELSCROLLLINES, 3); default: break; } @@ -495,7 +497,8 @@ static QPixmap loadIconFromShell32(int resourceId, QSizeF size) QPixmap QWindowsTheme::standardPixmap(StandardPixmap sp, const QSizeF &size) const { - const int scaleFactor = QWindowsScaling::factor(); + const QScreen *primaryScreen = QGuiApplication::primaryScreen(); + const int scaleFactor = primaryScreen ? qRound(QHighDpiScaling::factor(primaryScreen)) : 1; const QSizeF pixmapSize = size * scaleFactor; int resourceId = -1; LPCTSTR iconName = 0; @@ -632,7 +635,7 @@ public: static FakePointer *create(T thing) { - return reinterpret_cast<FakePointer *>(thing); + return reinterpret_cast<FakePointer *>(qintptr(thing)); } T operator * () const @@ -720,6 +723,7 @@ QPixmap QWindowsTheme::fileIconPixmap(const QFileInfo &fileInfo, const QSizeF &s iconSize|SHGFI_SYSICONINDEX; #endif // Q_OS_WINCE unsigned long val = 0; +#if !defined(QT_NO_WINCE_SHELLSDK) if (cacheableDirIcon && useDefaultFolderIcon) { flags |= SHGFI_USEFILEATTRIBUTES; val = SHGetFileInfo(L"dummy", @@ -729,6 +733,7 @@ QPixmap QWindowsTheme::fileIconPixmap(const QFileInfo &fileInfo, const QSizeF &s val = SHGetFileInfo(reinterpret_cast<const wchar_t *>(filePath.utf16()), 0, &info, sizeof(SHFILEINFO), flags); } +#endif // !QT_NO_WINCE_SHELLSDK // Even if GetFileInfo returns a valid result, hIcon can be empty in some cases if (val && info.hIcon) { diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index 288f73cb8f..c49682cc26 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -36,7 +36,6 @@ #include "qwindowscontext.h" #include "qwindowsdrag.h" #include "qwindowsscreen.h" -#include "qwindowsscaling.h" #include "qwindowsintegration.h" #include "qwindowsopenglcontext.h" #ifdef QT_NO_CURSOR @@ -49,8 +48,9 @@ #include <QtGui/QRegion> #include <QtGui/QOpenGLContext> #include <private/qsystemlibrary_p.h> -#include <private/qwindow_p.h> +#include <private/qwindow_p.h> // QWINDOWSIZE_MAX #include <private/qguiapplication_p.h> +#include <private/qhighdpiscaling_p.h> #include <qpa/qwindowsysteminterface.h> #include <QtCore/QDebug> @@ -106,20 +106,6 @@ static QByteArray debugWinExStyle(DWORD exStyle) return rc; } -#ifndef Q_OS_WINCE // maybe available on some SDKs revisit WM_GETMINMAXINFO -QDebug operator<<(QDebug d, const MINMAXINFO &i) -{ - QDebugStateSaver saver(d); - d.nospace(); - d << "MINMAXINFO maxSize=" << i.ptMaxSize.x << ',' - << i.ptMaxSize.y << " maxpos=" << i.ptMaxPosition.x - << ',' << i.ptMaxPosition.y << " mintrack=" - << i.ptMinTrackSize.x << ',' << i.ptMinTrackSize.y - << " maxtrack=" << i.ptMaxTrackSize.x << ',' << i.ptMaxTrackSize.y; - return d; -} -#endif // !Q_OS_WINCE - static inline QSize qSizeOfRect(const RECT &rect) { return QSize(rect.right -rect.left, rect.bottom - rect.top); @@ -138,6 +124,7 @@ static inline RECT RECTfromQRect(const QRect &rect) return result; } +#ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug d, const RECT &r) { QDebugStateSaver saver(d); @@ -147,7 +134,13 @@ QDebug operator<<(QDebug d, const RECT &r) return d; } -#ifndef Q_OS_WINCE // maybe available on some SDKs revisit WM_NCCALCSIZE +QDebug operator<<(QDebug d, const POINT &p) +{ + d << p.x << ',' << p.y; + return d; +} + +# ifndef Q_OS_WINCE QDebug operator<<(QDebug d, const NCCALCSIZE_PARAMS &p) { QDebugStateSaver saver(d); @@ -156,7 +149,30 @@ QDebug operator<<(QDebug d, const NCCALCSIZE_PARAMS &p) << ' ' << qrectFromRECT(p.rgrc[1]) << ' ' << qrectFromRECT(p.rgrc[2]); return d; } -#endif // !Q_OS_WINCE + +QDebug operator<<(QDebug d, const MINMAXINFO &i) +{ + QDebugStateSaver saver(d); + d.nospace(); + d << "MINMAXINFO maxSize=" << i.ptMaxSize.x << ',' + << i.ptMaxSize.y << " maxpos=" << i.ptMaxPosition.x + << ',' << i.ptMaxPosition.y << " mintrack=" + << i.ptMinTrackSize.x << ',' << i.ptMinTrackSize.y + << " maxtrack=" << i.ptMaxTrackSize.x << ',' << i.ptMaxTrackSize.y; + return d; +} + +QDebug operator<<(QDebug d, const WINDOWPLACEMENT &wp) +{ + QDebugStateSaver saver(d); + d.nospace(); + d << "WINDOWPLACEMENT(flags=0x" << hex << wp.flags << dec << ", showCmd=" + << wp.showCmd << ", ptMinPosition=" << wp.ptMinPosition << ", ptMaxPosition=" << wp.ptMaxPosition + << ", rcNormalPosition=" << wp.rcNormalPosition; + return d; +} +# endif // !Q_OS_WINCE +#endif // !QT_NO_DEBUG_STREAM // QTBUG-43872, for windows that do not have WS_EX_TOOLWINDOW set, WINDOWPLACEMENT // is in workspace/available area coordinates. @@ -169,7 +185,7 @@ static QPoint windowPlacementOffset(HWND hwnd, const QPoint &point) const QWindowsScreen *screen = screenManager.screens().size() == 1 ? screenManager.screens().first() : screenManager.screenAtDp(point); if (screen) - return screen->availableGeometryDp().topLeft() - screen->geometryDp().topLeft(); + return screen->availableGeometry().topLeft() - screen->geometry().topLeft(); #else Q_UNUSED(hwnd) Q_UNUSED(point) @@ -608,9 +624,7 @@ QWindowsWindowData const QString windowClassName = QWindowsContext::instance()->registerWindowClass(w); - const QRect geometryDip = QWindowsScaling::mapFromNative(data.geometry); - QRect fixedGeometryDip = QPlatformWindow::initialGeometry(w, geometryDip, defaultWindowWidth, defaultWindowHeight); - const QRect rect = fixedGeometryDip != geometryDip ? QWindowsScaling::mapToNative(fixedGeometryDip) : data.geometry; + const QRect rect = QPlatformWindow::initialGeometry(w, data.geometry, defaultWindowWidth, defaultWindowHeight); if (title.isEmpty() && (result.flags & Qt::WindowTitleHint)) title = topLevel ? qAppName() : w->objectName(); @@ -706,6 +720,20 @@ void WindowCreationData::initialize(const QWindow *w, HWND hwnd, bool frameChang } } + +// Scaling helpers for size constraints. +static QSize toNativeSizeConstrained(QSize dip, const QWindow *w) +{ + if (QHighDpiScaling::isActive()) { + const qreal factor = QHighDpiScaling::factor(w); + if (dip.width() > 0 && dip.width() < QWINDOWSIZE_MAX) + dip.rwidth() *= factor; + if (dip.height() > 0 && dip.height() < QWINDOWSIZE_MAX) + dip.rheight() *= factor; + } + return dip; +} + /*! \class QWindowsGeometryHint \brief Stores geometry constraints and provides utility functions. @@ -718,8 +746,8 @@ void WindowCreationData::initialize(const QWindow *w, HWND hwnd, bool frameChang */ QWindowsGeometryHint::QWindowsGeometryHint(const QWindow *w, const QMargins &cm) : - minimumSize(QWindowsScaling::mapToNativeConstrained(w->minimumSize())), - maximumSize(QWindowsScaling::mapToNativeConstrained(w->maximumSize())), + minimumSize(toNativeSizeConstrained(w->minimumSize(), w)), + maximumSize(toNativeSizeConstrained(w->maximumSize(), w)), customMargins(cm) { } @@ -900,6 +928,7 @@ QWindowsWindow::QWindowsWindow(QWindow *aWindow, const QWindowsWindowData &data) m_hdc(0), m_windowState(Qt::WindowNoState), m_opacity(1.0), + m_cursor(new CursorHandle), m_dropTarget(0), m_savedStyle(0), m_format(aWindow->requestedFormat()), @@ -953,8 +982,7 @@ void QWindowsWindow::fireExpose(const QRegion ®ion, bool force) clearFlag(Exposed); else setFlag(Exposed); - QWindowSystemInterface::handleExposeEvent(window(), - QWindowsScaling::mapFromNative(region)); + QWindowSystemInterface::handleExposeEvent(window(), region); } static inline QWindow *findTransientChild(const QWindow *parent) @@ -1051,7 +1079,7 @@ QWindow *QWindowsWindow::topLevelOf(QWindow *w) if (const QPlatformWindow *handle = w->handle()) { const QWindowsWindow *ww = static_cast<const QWindowsWindow *>(handle); - if (ww->isEmbedded(0)) { + if (ww->isEmbedded()) { HWND parentHWND = GetAncestor(ww->handle(), GA_PARENT); const HWND desktopHwnd = GetDesktopWindow(); const QWindowsContext *ctx = QWindowsContext::instance(); @@ -1129,12 +1157,12 @@ bool QWindowsWindow::isEmbedded(const QPlatformWindow *parentWindow) const } if (!m_data.embedded && parent()) - return parent()->isEmbedded(0); + return parent()->isEmbedded(); return m_data.embedded; } -QPoint QWindowsWindow::mapToGlobalDp(const QPoint &pos) const +QPoint QWindowsWindow::mapToGlobal(const QPoint &pos) const { if (m_data.hwnd) return QWindowsGeometryHint::mapToGlobal(m_data.hwnd, pos); @@ -1142,7 +1170,7 @@ QPoint QWindowsWindow::mapToGlobalDp(const QPoint &pos) const return pos; } -QPoint QWindowsWindow::mapFromGlobalDp(const QPoint &pos) const +QPoint QWindowsWindow::mapFromGlobal(const QPoint &pos) const { if (m_data.hwnd) return QWindowsGeometryHint::mapFromGlobal(m_data.hwnd, pos); @@ -1323,22 +1351,22 @@ static QRect normalFrameGeometry(HWND hwnd) return QRect(); } -QRect QWindowsWindow::normalGeometryDp() const +QRect QWindowsWindow::normalGeometry() const { // Check for fake 'fullscreen' mode. const bool fakeFullScreen = m_savedFrameGeometry.isValid() && window()->windowState() == Qt::WindowFullScreen; const QRect frame = fakeFullScreen ? m_savedFrameGeometry : normalFrameGeometry(m_data.hwnd); - const QMargins margins = fakeFullScreen ? QWindowsGeometryHint::frame(m_savedStyle, 0) : frameMarginsDp(); + const QMargins margins = fakeFullScreen ? QWindowsGeometryHint::frame(m_savedStyle, 0) : frameMargins(); return frame.isValid() ? frame.marginsRemoved(margins) : frame; } -void QWindowsWindow::setGeometryDp(const QRect &rectIn) +void QWindowsWindow::setGeometry(const QRect &rectIn) { QRect rect = rectIn; // This means it is a call from QWindow::setFramePosition() and // the coordinates include the frame (size is still the contents rectangle). if (QWindowsGeometryHint::positionIncludesFrame(window())) { - const QMargins margins = frameMarginsDp(); + const QMargins margins = frameMargins(); rect.moveTopLeft(rect.topLeft() + QPoint(margins.left(), margins.top())); } if (m_windowState == Qt::WindowMinimized) @@ -1407,9 +1435,8 @@ void QWindowsWindow::handleGeometryChange() return; const QRect previousGeometry = m_data.geometry; m_data.geometry = geometry_sys(); - const QRect geometryDip = QWindowsScaling::mapFromNative(m_data.geometry); - QPlatformWindow::setGeometry(geometryDip); - QWindowSystemInterface::handleGeometryChange(window(), geometryDip); + QPlatformWindow::setGeometry(m_data.geometry); + QWindowSystemInterface::handleGeometryChange(window(), m_data.geometry); // QTBUG-32121: OpenGL/normal windows (with exception of ANGLE) do not receive // expose events when shrinking, synthesize. if (!testFlag(OpenGL_ES2) && isExposed() @@ -1417,7 +1444,7 @@ void QWindowsWindow::handleGeometryChange() fireExpose(QRect(QPoint(0, 0), m_data.geometry.size()), true); } if (previousGeometry.topLeft() != m_data.geometry.topLeft()) { - QPlatformScreen *newScreen = screenForGeometry(geometryDip); + QPlatformScreen *newScreen = screenForGeometry(m_data.geometry); if (newScreen != screen()) QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->screen()); } @@ -1429,7 +1456,7 @@ void QWindowsWindow::handleGeometryChange() void QWindowsWindow::setGeometry_sys(const QRect &rect) const { - const QMargins margins = frameMarginsDp(); + const QMargins margins = frameMargins(); const QRect frameGeometry = rect + margins; qCDebug(lcQpaWindows) << '>' << __FUNCTION__ << window() @@ -1468,7 +1495,7 @@ QRect QWindowsWindow::frameGeometry_sys() const QRect QWindowsWindow::geometry_sys() const { - return frameGeometry_sys().marginsRemoved(frameMarginsDp()); + return frameGeometry_sys().marginsRemoved(frameMargins()); } /*! @@ -1540,7 +1567,7 @@ void QWindowsWindow::setWindowFlags(Qt::WindowFlags flags) { qCDebug(lcQpaWindows) << '>' << __FUNCTION__ << this << window() << "\n from: " << m_data.flags << "\n to: " << flags; - const QRect oldGeometry = geometryDp(); + const QRect oldGeometry = geometry(); if (m_data.flags != flags) { m_data.flags = flags; if (m_data.hwnd) { @@ -1626,8 +1653,13 @@ void QWindowsWindow::setWindowState(Qt::WindowState state) bool QWindowsWindow::isFullScreen_sys() const { - return window()->isTopLevel() - && geometry_sys() == QWindowsScaling::mapToNative(window()->screen()->geometry()); + const QWindow *w = window(); + if (!w->isTopLevel()) + return false; + const QScreen *screen = w->screen(); + if (!screen) + screen = QGuiApplication::primaryScreen(); + return screen && geometry_sys() == QHighDpi::toNativePixels(screen->geometry(), w); } /*! @@ -1697,15 +1729,16 @@ void QWindowsWindow::setWindowState_sys(Qt::WindowState newState) // Use geometry of QWindow::screen() within creation or the virtual screen the // window is in (QTBUG-31166, QTBUG-30724). const QScreen *screen = window()->screen(); - const QRect rDip = screen->geometry(); - const QRect r = QWindowsScaling::mapToNative(rDip); + if (!screen) + screen = QGuiApplication::primaryScreen(); + const QRect r = screen ? QHighDpi::toNativePixels(screen->geometry(), window()) : m_savedFrameGeometry; const UINT swpf = SWP_FRAMECHANGED | SWP_NOACTIVATE; const bool wasSync = testFlag(SynchronousGeometryChangeEvent); setFlag(SynchronousGeometryChangeEvent); SetWindowPos(m_data.hwnd, HWND_TOP, r.left(), r.top(), r.width(), r.height(), swpf); if (!wasSync) clearFlag(SynchronousGeometryChangeEvent); - QWindowSystemInterface::handleGeometryChange(window(), rDip); + QWindowSystemInterface::handleGeometryChange(window(), r); QWindowSystemInterface::flushWindowSystemEvents(); } else if (newState != Qt::WindowMinimized) { // Restore saved state. @@ -1804,7 +1837,7 @@ void QWindowsWindow::propagateSizeHints() qCDebug(lcQpaWindows) << __FUNCTION__ << this << window(); } -bool QWindowsWindow::handleGeometryChangingMessage(MSG *message, const QWindow *qWindow, const QMargins &marginsDp) +bool QWindowsWindow::handleGeometryChangingMessage(MSG *message, const QWindow *qWindow, const QMargins &margins) { #ifndef Q_OS_WINCE if (!qWindow->isTopLevel()) // Implement hasHeightForWidth(). @@ -1812,26 +1845,19 @@ bool QWindowsWindow::handleGeometryChangingMessage(MSG *message, const QWindow * WINDOWPOS *windowPos = reinterpret_cast<WINDOWPOS *>(message->lParam); if ((windowPos->flags & (SWP_NOCOPYBITS | SWP_NOSIZE))) return false; - const QRect suggestedFrameGeometryDp(windowPos->x, windowPos->y, - windowPos->cx, windowPos->cy); - const qreal factor = QWindowsScaling::factor(); - const QRect suggestedGeometryDp = suggestedFrameGeometryDp - marginsDp; - const QRectF suggestedGeometry = QRectF(QPointF(suggestedGeometryDp.topLeft()) / factor, - QSizeF(suggestedGeometryDp.size()) / factor); - const QRectF correctedGeometryF = - qt_window_private(const_cast<QWindow *>(qWindow))->closestAcceptableGeometry(suggestedGeometry); + const QRect suggestedFrameGeometry(windowPos->x, windowPos->y, + windowPos->cx, windowPos->cy); + const QRect suggestedGeometry = suggestedFrameGeometry - margins; + const QRectF correctedGeometryF = QPlatformWindow::closestAcceptableGeometry(qWindow, suggestedGeometry); if (!correctedGeometryF.isValid()) return false; - const QRect correctedFrameGeometryDp - = QRectF(correctedGeometryF.topLeft() * factor, - correctedGeometryF.size() * factor).toRect() - + marginsDp; - if (correctedFrameGeometryDp == suggestedFrameGeometryDp) + const QRect correctedFrameGeometry = correctedGeometryF.toRect() + margins; + if (correctedFrameGeometry == suggestedFrameGeometry) return false; - windowPos->x = correctedFrameGeometryDp.left(); - windowPos->y = correctedFrameGeometryDp.top(); - windowPos->cx = correctedFrameGeometryDp.width(); - windowPos->cy = correctedFrameGeometryDp.height(); + windowPos->x = correctedFrameGeometry.left(); + windowPos->y = correctedFrameGeometry.top(); + windowPos->cx = correctedFrameGeometry.width(); + windowPos->cy = correctedFrameGeometry.height(); return true; #else // !Q_OS_WINCE Q_UNUSED(message) @@ -1841,11 +1867,11 @@ bool QWindowsWindow::handleGeometryChangingMessage(MSG *message, const QWindow * bool QWindowsWindow::handleGeometryChanging(MSG *message) const { - const QMargins marginsDp = window()->isTopLevel() ? frameMarginsDp() : QMargins(); - return QWindowsWindow::handleGeometryChangingMessage(message, window(), marginsDp); + const QMargins margins = window()->isTopLevel() ? frameMargins() : QMargins(); + return QWindowsWindow::handleGeometryChangingMessage(message, window(), margins); } -QMargins QWindowsWindow::frameMarginsDp() const +QMargins QWindowsWindow::frameMargins() const { // Frames are invalidated by style changes (window state, flags). // As they are also required for geometry calculations in resize @@ -1892,17 +1918,17 @@ static inline void addRectToWinRegion(const QRect &rect, HRGN *winRegion) } } -static HRGN qRegionToWinRegion(const QRegion ®ionDip) +static HRGN qRegionToWinRegion(const QRegion ®ion) { - const QVector<QRect> rects = regionDip.rects(); + const QVector<QRect> rects = region.rects(); if (rects.isEmpty()) return NULL; const int rectCount = rects.size(); if (rectCount == 1) - return createRectRegion(QWindowsScaling::mapToNative(regionDip.boundingRect())); - HRGN hRegion = createRectRegion(QWindowsScaling::mapToNative(rects.front())); + return createRectRegion(region.boundingRect()); + HRGN hRegion = createRectRegion(rects.front()); for (int i = 1; i < rectCount; ++i) - addRectToWinRegion(QWindowsScaling::mapToNative(rects.at(i)), &hRegion); + addRectToWinRegion(rects.at(i), &hRegion); return hRegion; } @@ -1916,7 +1942,7 @@ void QWindowsWindow::setMask(const QRegion ®ion) // Mask is in client area coordinates, so offset it in case we have a frame if (window()->isTopLevel()) { - const QMargins margins = frameMarginsDp(); + const QMargins margins = frameMargins(); OffsetRgn(winRegion, margins.left(), margins.top()); } @@ -2053,23 +2079,23 @@ bool QWindowsWindow::handleNonClientHitTest(const QPoint &globalPos, LRESULT *re || (m_data.flags & Qt::FramelessWindowHint)) { return false; } - const QSize minimumSize = QWindowsScaling::mapToNativeConstrained(w->minimumSize()); + const QSize minimumSize = w->minimumSize(); if (minimumSize.isEmpty()) return false; - const QSize maximumSize = QWindowsScaling::mapToNativeConstrained(w->maximumSize()); + const QSize maximumSize = w->maximumSize(); const bool fixedWidth = minimumSize.width() == maximumSize.width(); const bool fixedHeight = minimumSize.height() == maximumSize.height(); if (!fixedWidth && !fixedHeight) return false; - const QPoint localPos = mapFromGlobalDp(globalPos); - const QSize size = w->size() * QWindowsScaling::factor(); + const QPoint localPos = w->mapFromGlobal(QHighDpi::fromNativePixels(globalPos, w)); + const QSize size = w->size(); if (fixedHeight) { if (localPos.y() >= size.height()) { *result = HTBORDER; // Unspecified border, no resize cursor. return true; } if (localPos.y() < 0) { - const QMargins margins = frameMarginsDp(); + const QMargins margins = frameMargins(); const int topResizeBarPos = margins.left() - margins.top(); if (localPos.y() < topResizeBarPos) { *result = HTCAPTION; // Extend caption over top resize bar, let's user move the window. @@ -2088,13 +2114,13 @@ bool QWindowsWindow::handleNonClientHitTest(const QPoint &globalPos, LRESULT *re #ifndef QT_NO_CURSOR // Return the default cursor (Arrow) from QWindowsCursor's cache. -static inline QWindowsWindowCursor defaultCursor(const QWindow *w) +static inline CursorHandlePtr defaultCursor(const QWindow *w) { if (QScreen *screen = w->screen()) if (const QPlatformScreen *platformScreen = screen->handle()) if (QPlatformCursor *cursor = platformScreen->cursor()) return static_cast<QWindowsCursor *>(cursor)->standardWindowCursor(Qt::ArrowCursor); - return QWindowsWindowCursor(Qt::ArrowCursor); + return CursorHandlePtr(new CursorHandle(QWindowsCursor::createCursorFromShape(Qt::ArrowCursor))); } // Check whether to apply a new cursor. Either the window in question is @@ -2108,7 +2134,7 @@ static inline bool applyNewCursor(const QWindow *w) for (const QWindow *p = underMouse; p ; p = p->parent()) { if (p == w) return true; - if (!QWindowsWindow::baseWindowOf(p)->cursor().isNull()) + if (!QWindowsWindow::baseWindowOf(p)->cursor()->isNull()) return false; } return false; @@ -2124,25 +2150,25 @@ static inline bool applyNewCursor(const QWindow *w) void QWindowsWindow::applyCursor() { #ifndef QT_NO_CURSOR - if (m_cursor.isNull()) { // Recurse up to parent with non-null cursor. Set default for toplevel. + if (m_cursor->isNull()) { // Recurse up to parent with non-null cursor. Set default for toplevel. if (const QWindow *p = window()->parent()) { QWindowsWindow::baseWindowOf(p)->applyCursor(); } else { - SetCursor(defaultCursor(window()).handle()); + SetCursor(defaultCursor(window())->handle()); } } else { - SetCursor(m_cursor.handle()); + SetCursor(m_cursor->handle()); } #endif } -void QWindowsWindow::setCursor(const QWindowsWindowCursor &c) +void QWindowsWindow::setCursor(const CursorHandlePtr &c) { #ifndef QT_NO_CURSOR - if (c.handle() != m_cursor.handle()) { + if (c->handle() != m_cursor->handle()) { const bool apply = applyNewCursor(window()); qCDebug(lcQpaWindows) << window() << __FUNCTION__ - << c.cursor().shape() << " doApply=" << apply; + << c->handle() << " doApply=" << apply; m_cursor = c; if (apply) applyCursor(); @@ -2245,10 +2271,6 @@ void QWindowsWindow::setWindowIcon(const QIcon &icon) The property can be set using QPlatformNativeInterface::setWindowProperty() or, before platform window creation, by setting a dynamic property on the QWindow (see QWindowsIntegration::createPlatformWindow()). - - Note: The function uses (unscaled) device pixels since the QWizard also - uses AdjustWindowRect() and using device independent pixels would introduce - rounding errors. */ void QWindowsWindow::setCustomMargins(const QMargins &newCustomMargins) diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h index fff90b4b11..4172a3d850 100644 --- a/src/plugins/platforms/windows/qwindowswindow.h +++ b/src/plugins/platforms/windows/qwindowswindow.h @@ -38,7 +38,6 @@ #ifdef Q_OS_WINCE # include "qplatformfunctions_wince.h" #endif -#include "qwindowsscaling.h" #include "qwindowscursor.h" #include <qpa/qplatformwindow.h> @@ -144,29 +143,21 @@ public: QWindowsWindow(QWindow *window, const QWindowsWindowData &data); ~QWindowsWindow(); + using QPlatformWindow::screenForGeometry; + QSurfaceFormat format() const Q_DECL_OVERRIDE { return m_format; } - void setGeometryDp(const QRect &rectIn); - void setGeometry(const QRect &rect) Q_DECL_OVERRIDE - { setGeometryDp(QWindowsScaling::mapToNative(rect)); } - QRect geometryDp() const { return m_data.geometry; } - QRect geometry() const Q_DECL_OVERRIDE - { return QWindowsScaling::mapFromNative(geometryDp()); } - QRect normalGeometryDp() const; - QRect normalGeometry() const Q_DECL_OVERRIDE - { return QWindowsScaling::mapFromNative(normalGeometryDp()); } - qreal devicePixelRatio() const Q_DECL_OVERRIDE - { return qreal(QWindowsScaling::factor()); } + void setGeometry(const QRect &rect) Q_DECL_OVERRIDE; + QRect geometry() const Q_DECL_OVERRIDE { return m_data.geometry; } + QRect normalGeometry() const Q_DECL_OVERRIDE; + void setVisible(bool visible) Q_DECL_OVERRIDE; bool isVisible() const; bool isExposed() const Q_DECL_OVERRIDE { return testFlag(Exposed); } bool isActive() const Q_DECL_OVERRIDE; - bool isEmbedded(const QPlatformWindow *parentWindow) const Q_DECL_OVERRIDE; - QPoint mapToGlobalDp(const QPoint &pos) const; - QPoint mapToGlobal(const QPoint &pos) const Q_DECL_OVERRIDE - { return mapToGlobalDp(pos * QWindowsScaling::factor()) / QWindowsScaling::factor(); } - QPoint mapFromGlobalDp(const QPoint &pos) const; - QPoint mapFromGlobal(const QPoint &pos) const Q_DECL_OVERRIDE - { return mapFromGlobalDp(pos * QWindowsScaling::factor()) / QWindowsScaling::factor(); } + bool isEmbedded(const QPlatformWindow *parentWindow = 0) const Q_DECL_OVERRIDE; + QPoint mapToGlobal(const QPoint &pos) const Q_DECL_OVERRIDE; + QPoint mapFromGlobal(const QPoint &pos) const Q_DECL_OVERRIDE; + void setWindowFlags(Qt::WindowFlags flags) Q_DECL_OVERRIDE; void setWindowState(Qt::WindowState state) Q_DECL_OVERRIDE; @@ -184,8 +175,7 @@ public: void propagateSizeHints() Q_DECL_OVERRIDE; static bool handleGeometryChangingMessage(MSG *message, const QWindow *qWindow, const QMargins &marginsDp); bool handleGeometryChanging(MSG *message) const; - QMargins frameMarginsDp() const; - QMargins frameMargins() const Q_DECL_OVERRIDE { return frameMarginsDp() / QWindowsScaling::factor(); } + QMargins frameMargins() const Q_DECL_OVERRIDE; void setOpacity(qreal level) Q_DECL_OVERRIDE; void setMask(const QRegion ®ion) Q_DECL_OVERRIDE; @@ -196,7 +186,7 @@ public: bool setMouseGrabEnabled(bool grab) Q_DECL_OVERRIDE; inline bool hasMouseCapture() const { return GetCapture() == m_data.hwnd; } - bool startSystemResize(const QPoint &, Qt::Corner corner) Q_DECL_OVERRIDE; + bool startSystemResize(const QPoint &pos, Qt::Corner corner) Q_DECL_OVERRIDE; void setFrameStrutEventsEnabled(bool enabled); bool frameStrutEventsEnabled() const { return testFlag(FrameStrutEventsEnabled); } @@ -235,9 +225,9 @@ public: #endif // !Q_OS_WINCE #ifndef QT_NO_CURSOR - QWindowsWindowCursor cursor() const { return m_cursor; } + CursorHandlePtr cursor() const { return m_cursor; } #endif - void setCursor(const QWindowsWindowCursor &c); + void setCursor(const CursorHandlePtr &c); void applyCursor(); inline bool testFlag(unsigned f) const { return (m_flags & f) != 0; } @@ -288,7 +278,7 @@ private: Qt::WindowState m_windowState; qreal m_opacity; #ifndef QT_NO_CURSOR - QWindowsWindowCursor m_cursor; + CursorHandlePtr m_cursor; #endif QWindowsOleDropTarget *m_dropTarget; unsigned m_savedStyle; @@ -302,12 +292,15 @@ private: void *m_surface; }; -// Debug +#ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug d, const RECT &r); -#ifndef Q_OS_WINCE // maybe available on some SDKs revisit WM_GETMINMAXINFO/WM_NCCALCSIZE +QDebug operator<<(QDebug d, const POINT &); +# ifndef Q_OS_WINCE QDebug operator<<(QDebug d, const MINMAXINFO &i); QDebug operator<<(QDebug d, const NCCALCSIZE_PARAMS &p); -#endif +QDebug operator<<(QDebug d, const WINDOWPLACEMENT &); +# endif // !Q_OS_WINCE +#endif // !QT_NO_DEBUG_STREAM // ---------- QWindowsGeometryHint inline functions. QPoint QWindowsGeometryHint::mapToGlobal(HWND hwnd, const QPoint &qp) diff --git a/src/plugins/platforms/windows/windows.pri b/src/plugins/platforms/windows/windows.pri index de901aaeb1..29297116da 100644 --- a/src/plugins/platforms/windows/windows.pri +++ b/src/plugins/platforms/windows/windows.pri @@ -29,7 +29,6 @@ SOURCES += \ $$PWD/qwindowsfontengine.cpp \ $$PWD/qwindowsfontdatabase.cpp \ $$PWD/qwindowsmousehandler.cpp \ - $$PWD/qwindowsguieventdispatcher.cpp \ $$PWD/qwindowsole.cpp \ $$PWD/qwindowsmime.cpp \ $$PWD/qwindowsinternalmimedata.cpp \ @@ -40,7 +39,6 @@ SOURCES += \ $$PWD/qwindowsservices.cpp \ $$PWD/qwindowsnativeimage.cpp \ $$PWD/qwindowsnativeinterface.cpp \ - $$PWD/qwindowsscaling.cpp \ $$PWD/qwindowsopengltester.cpp HEADERS += \ @@ -52,7 +50,6 @@ HEADERS += \ $$PWD/qwindowsfontengine.h \ $$PWD/qwindowsfontdatabase.h \ $$PWD/qwindowsmousehandler.h \ - $$PWD/qwindowsguieventdispatcher.h \ $$PWD/qtwindowsglobal.h \ $$PWD/qtwindows_additional.h \ $$PWD/qwindowsole.h \ @@ -67,7 +64,6 @@ HEADERS += \ $$PWD/qplatformfunctions_wince.h \ $$PWD/qwindowsnativeimage.h \ $$PWD/qwindowsnativeinterface.h \ - $$PWD/qwindowsscaling.h \ $$PWD/qwindowsopengltester.h INCLUDEPATH += $$PWD diff --git a/src/plugins/platforms/winrt/qwinrtbackingstore.cpp b/src/plugins/platforms/winrt/qwinrtbackingstore.cpp index dcf8239538..4517200a2d 100644 --- a/src/plugins/platforms/winrt/qwinrtbackingstore.cpp +++ b/src/plugins/platforms/winrt/qwinrtbackingstore.cpp @@ -42,8 +42,8 @@ #include <QtGui/QOpenGLContext> #include <QtGui/QOpenGLFramebufferObject> -#include <GLES3/gl3.h> -#include <GLES3/gl3ext.h> +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> QT_BEGIN_NAMESPACE @@ -66,7 +66,8 @@ QWinRTBackingStore::QWinRTBackingStore(QWindow *window) d->initialized = false; d->screen = static_cast<QWinRTScreen*>(window->screen()->handle()); - window->setSurfaceType(QSurface::OpenGLSurface); // Required for flipping, but could be done in the swap + if (window->surfaceType() == QSurface::RasterSurface) + window->setSurfaceType(QSurface::OpenGLSurface); } bool QWinRTBackingStore::initialize() @@ -78,7 +79,6 @@ bool QWinRTBackingStore::initialize() d->context.reset(new QOpenGLContext); QSurfaceFormat format = window()->requestedFormat(); - format.setVersion(3, 0); // Required for ES3 framebuffer blit d->context->setFormat(format); d->context->setScreen(window()->screen()); if (!d->context->create()) @@ -121,13 +121,13 @@ void QWinRTBackingStore::flush(QWindow *window, const QRegion ®ion, const QPo GL_RGBA, GL_UNSIGNED_BYTE, d->paintDevice.constScanLine(bounds.y())); glBindTexture(GL_TEXTURE_2D, 0); - glBindFramebuffer(GL_READ_FRAMEBUFFER, d->fbo->handle()); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glBindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, d->fbo->handle()); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, 0); const int y1 = bounds.y(); const int y2 = y1 + bounds.height(); const int x1 = bounds.x(); const int x2 = x1 + bounds.width(); - glBlitFramebuffer(x1, y1, x2, y2, + glBlitFramebufferANGLE(x1, y1, x2, y2, x1, d->size.height() - y1, x2, d->size.height() - y2, GL_COLOR_BUFFER_BIT, GL_NEAREST); diff --git a/src/plugins/platforms/winrt/qwinrtcursor.cpp b/src/plugins/platforms/winrt/qwinrtcursor.cpp index 94ce23bd2c..1a511f103f 100644 --- a/src/plugins/platforms/winrt/qwinrtcursor.cpp +++ b/src/plugins/platforms/winrt/qwinrtcursor.cpp @@ -36,11 +36,13 @@ #include "qwinrtcursor.h" #include "qwinrtscreen.h" +#include <private/qeventdispatcher_winrt_p.h> #include <QtCore/qfunctions_winrt.h> #include <QtGui/QGuiApplication> #include <QtGui/QScreen> +#include <functional> #include <wrl.h> #include <windows.ui.core.h> #include <windows.foundation.h> @@ -77,12 +79,17 @@ void QWinRTCursor::changeCursor(QCursor *windowCursor, QWindow *window) { Q_D(QWinRTCursor); + HRESULT hr; ICoreWindow *coreWindow = static_cast<QWinRTScreen *>(window->screen()->handle())->coreWindow(); CoreCursorType type; switch (windowCursor ? windowCursor->shape() : Qt::ArrowCursor) { case Qt::BlankCursor: - coreWindow->put_PointerCursor(Q_NULLPTR); + hr = QEventDispatcherWinRT::runOnXamlThread([coreWindow]() { + coreWindow->put_PointerCursor(Q_NULLPTR); + return S_OK; + }); + RETURN_VOID_IF_FAILED("Failed to set blank native cursor"); return; default: case Qt::OpenHandCursor: @@ -142,21 +149,31 @@ void QWinRTCursor::changeCursor(QCursor *windowCursor, QWindow *window) } ComPtr<ICoreCursor> cursor; - HRESULT hr = d->cursorFactory->CreateCursor(type, 0, &cursor); + hr = d->cursorFactory->CreateCursor(type, 0, &cursor); RETURN_VOID_IF_FAILED("Failed to create native cursor."); - hr = coreWindow->put_PointerCursor(cursor.Get()); - RETURN_VOID_IF_FAILED("Failed to set native cursor."); + hr = QEventDispatcherWinRT::runOnXamlThread([coreWindow, &cursor]() { + return coreWindow->put_PointerCursor(cursor.Get()); + }); + RETURN_VOID_IF_FAILED("Failed to set native cursor"); } #endif // QT_NO_CURSOR QPoint QWinRTCursor::pos() const { - ICoreWindow *coreWindow = - static_cast<QWinRTScreen *>(QGuiApplication::primaryScreen()->handle())->coreWindow(); + const QWinRTScreen *screen = static_cast<QWinRTScreen *>(QGuiApplication::primaryScreen()->handle()); + Q_ASSERT(screen); + ICoreWindow *coreWindow = screen->coreWindow(); + Q_ASSERT(coreWindow); Point point; - coreWindow->get_PointerPosition(&point); - return QPoint(point.X, point.Y); + HRESULT hr = QEventDispatcherWinRT::runOnXamlThread([coreWindow, &point]() { + return coreWindow->get_PointerPosition(&point); + }); + Q_ASSERT_SUCCEEDED(hr); + const QPoint position = QPoint(point.X, point.Y) * screen->scaleFactor(); + // If no cursor get_PointerPosition returns SHRT_MIN for x and y + return position.x() == SHRT_MIN && position.y() == SHRT_MIN || FAILED(hr) ? QPointF(Q_INFINITY, Q_INFINITY).toPoint() + : position; } QT_END_NAMESPACE diff --git a/src/plugins/platforms/winrt/qwinrteglcontext.cpp b/src/plugins/platforms/winrt/qwinrteglcontext.cpp index 42ffe8f716..3fd0278360 100644 --- a/src/plugins/platforms/winrt/qwinrteglcontext.cpp +++ b/src/plugins/platforms/winrt/qwinrteglcontext.cpp @@ -35,40 +35,153 @@ ****************************************************************************/ #include "qwinrteglcontext.h" +#include "qwinrtwindow.h" +#include <private/qeventdispatcher_winrt_p.h> +#include <functional> + +#include <d3d11.h> + +#include <EGL/egl.h> #define EGL_EGLEXT_PROTOTYPES -#include "EGL/eglext.h" +#include <EGL/eglext.h> + +#include <QOpenGLContext> +#include <QtPlatformSupport/private/qeglconvenience_p.h> QT_BEGIN_NAMESPACE -QWinRTEGLContext::QWinRTEGLContext(const QSurfaceFormat &format, QPlatformOpenGLContext *share, EGLDisplay display, EGLSurface surface, EGLConfig config) - : QEGLPlatformContext(format, share, display, &config), m_eglSurface(surface) +struct WinRTEGLDisplay +{ + WinRTEGLDisplay() { + eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (eglDisplay == EGL_NO_DISPLAY) + qCritical("Failed to initialize EGL display: 0x%x", eglGetError()); + } + ~WinRTEGLDisplay() { + eglTerminate(eglDisplay); + } + + EGLDisplay eglDisplay; +}; + +Q_GLOBAL_STATIC(WinRTEGLDisplay, g) + +class QWinRTEGLContextPrivate { +public: + QWinRTEGLContextPrivate() : eglContext(EGL_NO_CONTEXT), eglShareContext(EGL_NO_CONTEXT) { } + QSurfaceFormat format; + EGLConfig eglConfig; + EGLContext eglContext; + EGLContext eglShareContext; +}; + +QWinRTEGLContext::QWinRTEGLContext(QOpenGLContext *context) + : d_ptr(new QWinRTEGLContextPrivate) +{ + Q_D(QWinRTEGLContext); + d->format = context->format(); + d->format.setRenderableType(QSurfaceFormat::OpenGLES); + if (QPlatformOpenGLContext *shareHandle = context->shareHandle()) + d->eglShareContext = static_cast<QWinRTEGLContext *>(shareHandle)->d_ptr->eglContext; } -void QWinRTEGLContext::swapBuffers(QPlatformSurface *surface) +QWinRTEGLContext::~QWinRTEGLContext() { -#ifdef Q_OS_WINPHONE - const QSize size = surface->surface()->size(); - eglPostSubBufferNV(eglDisplay(), eglSurfaceForPlatformSurface(surface), - 0, 0, size.width(), size.height()); -#else - eglSwapBuffers(eglDisplay(), eglSurfaceForPlatformSurface(surface)); -#endif + Q_D(QWinRTEGLContext); + if (d->eglContext != EGL_NO_CONTEXT) + eglDestroyContext(g->eglDisplay, d->eglContext); } -EGLSurface QWinRTEGLContext::eglSurfaceForPlatformSurface(QPlatformSurface *surface) +void QWinRTEGLContext::initialize() { - if (surface->surface()->surfaceClass() == QSurface::Window) { - // All windows use the same surface - return m_eglSurface; - } else { - // TODO: return EGL surfaces for offscreen surfaces - qWarning("This plugin does not support offscreen surfaces."); - return EGL_NO_SURFACE; + Q_D(QWinRTEGLContext); + + // Test if the hardware supports at least level 9_3 + D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_9_3 }; // minimum feature level + HRESULT hr = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, featureLevels, 1, + D3D11_SDK_VERSION, nullptr, nullptr, nullptr); + EGLint deviceType = SUCCEEDED(hr) ? EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE + : EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE; + + eglBindAPI(EGL_OPENGL_ES_API); + + const EGLint displayAttributes[] = { + EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, + EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE, deviceType, + EGL_PLATFORM_ANGLE_ENABLE_AUTOMATIC_TRIM_ANGLE, true, + EGL_NONE, + }; + g->eglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, EGL_DEFAULT_DISPLAY, displayAttributes); + if (g->eglDisplay == EGL_NO_DISPLAY) + qCritical("Failed to initialize EGL display: 0x%x", eglGetError()); + + if (!eglInitialize(g->eglDisplay, nullptr, nullptr)) + qCritical("Failed to initialize EGL: 0x%x", eglGetError()); + + d->eglConfig = q_configFromGLFormat(g->eglDisplay, d->format); + + const EGLint flags = d->format.testOption(QSurfaceFormat::DebugContext) + ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0; + const EGLint attributes[] = { + EGL_CONTEXT_CLIENT_VERSION, d->format.majorVersion(), + EGL_CONTEXT_MINOR_VERSION_KHR, d->format.minorVersion(), + EGL_CONTEXT_FLAGS_KHR, flags, + EGL_NONE + }; + d->eglContext = eglCreateContext(g->eglDisplay, d->eglConfig, d->eglShareContext, attributes); + if (d->eglContext == EGL_NO_CONTEXT) { + qWarning("QEGLPlatformContext: Failed to create context: %x", eglGetError()); + return; } } +bool QWinRTEGLContext::makeCurrent(QPlatformSurface *windowSurface) +{ + Q_D(QWinRTEGLContext); + Q_ASSERT(windowSurface->surface()->supportsOpenGL()); + + QWinRTWindow *window = static_cast<QWinRTWindow *>(windowSurface); + if (window->eglSurface() == EGL_NO_SURFACE) + window->createEglSurface(g->eglDisplay, d->eglConfig); + + EGLSurface surface = window->eglSurface(); + if (surface == EGL_NO_SURFACE) + return false; + + const bool ok = eglMakeCurrent(g->eglDisplay, surface, surface, d->eglContext); + if (!ok) { + qWarning("QEGLPlatformContext: eglMakeCurrent failed: %x", eglGetError()); + return false; + } + + eglSwapInterval(g->eglDisplay, d->format.swapInterval()); + return true; +} + +void QWinRTEGLContext::doneCurrent() +{ + const bool ok = eglMakeCurrent(g->eglDisplay, EGL_NO_SURFACE, + EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (!ok) + qWarning("QEGLPlatformContext: eglMakeCurrent failed: %x", eglGetError()); +} + +void QWinRTEGLContext::swapBuffers(QPlatformSurface *windowSurface) +{ + Q_ASSERT(windowSurface->surface()->supportsOpenGL()); + + const QWinRTWindow *window = static_cast<QWinRTWindow *>(windowSurface); + eglSwapBuffers(g->eglDisplay, window->eglSurface()); +} + +QSurfaceFormat QWinRTEGLContext::format() const +{ + Q_D(const QWinRTEGLContext); + return d->format; +} + QFunctionPointer QWinRTEGLContext::getProcAddress(const QByteArray &procName) { static QHash<QByteArray, QFunctionPointer> standardFuncs; @@ -221,7 +334,7 @@ QFunctionPointer QWinRTEGLContext::getProcAddress(const QByteArray &procName) if (i != standardFuncs.end()) return i.value(); - return QEGLPlatformContext::getProcAddress(procName); + return eglGetProcAddress(procName.constData()); } QT_END_NAMESPACE diff --git a/src/plugins/platforms/winrt/qwinrteglcontext.h b/src/plugins/platforms/winrt/qwinrteglcontext.h index 958d623c4c..31a2124b03 100644 --- a/src/plugins/platforms/winrt/qwinrteglcontext.h +++ b/src/plugins/platforms/winrt/qwinrteglcontext.h @@ -37,23 +37,29 @@ #ifndef QWINDOWSEGLCONTEXT_H #define QWINDOWSEGLCONTEXT_H -#include <QtPlatformSupport/private/qeglplatformcontext_p.h> +#include <qpa/qplatformopenglcontext.h> QT_BEGIN_NAMESPACE -class QWinRTEGLContext : public QEGLPlatformContext +class QWinRTEGLContextPrivate; +class QWinRTEGLContext : public QPlatformOpenGLContext { public: - explicit QWinRTEGLContext(const QSurfaceFormat &format, QPlatformOpenGLContext *share, EGLDisplay display, EGLSurface surface, EGLConfig config); + explicit QWinRTEGLContext(QOpenGLContext *context); + ~QWinRTEGLContext(); - void swapBuffers(QPlatformSurface *surface) Q_DECL_OVERRIDE; - QFunctionPointer getProcAddress(const QByteArray &procName) Q_DECL_OVERRIDE; + void initialize() Q_DECL_OVERRIDE; + + bool makeCurrent(QPlatformSurface *windowSurface) Q_DECL_OVERRIDE; + void doneCurrent() Q_DECL_OVERRIDE; + void swapBuffers(QPlatformSurface *windowSurface) Q_DECL_OVERRIDE; -protected: - EGLSurface eglSurfaceForPlatformSurface(QPlatformSurface *surface); + QSurfaceFormat format() const Q_DECL_OVERRIDE; + QFunctionPointer getProcAddress(const QByteArray &procName) Q_DECL_OVERRIDE; private: - EGLSurface m_eglSurface; + QScopedPointer<QWinRTEGLContextPrivate> d_ptr; + Q_DECLARE_PRIVATE(QWinRTEGLContext) }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/winrt/qwinrtfiledialoghelper.cpp b/src/plugins/platforms/winrt/qwinrtfiledialoghelper.cpp index e1b2a07d5f..b0f377147e 100644 --- a/src/plugins/platforms/winrt/qwinrtfiledialoghelper.cpp +++ b/src/plugins/platforms/winrt/qwinrtfiledialoghelper.cpp @@ -37,25 +37,32 @@ #include "qwinrtfiledialoghelper.h" #include "qwinrtfileengine.h" +#include <QtCore/qcoreapplication.h> #include <QtCore/QEventLoop> #include <QtCore/QMap> #include <QtCore/QVector> #include <QtCore/qfunctions_winrt.h> +#include <private/qeventdispatcher_winrt_p.h> +#include <functional> #include <wrl.h> #include <windows.foundation.h> #include <windows.storage.pickers.h> +#include <Windows.ApplicationModel.activation.h> using namespace Microsoft::WRL; using namespace Microsoft::WRL::Wrappers; +using namespace ABI::Windows::ApplicationModel::Activation; using namespace ABI::Windows::Foundation; using namespace ABI::Windows::Foundation::Collections; using namespace ABI::Windows::Storage; using namespace ABI::Windows::Storage::Pickers; +#ifndef Q_OS_WINPHONE typedef IAsyncOperationCompletedHandler<StorageFile *> SingleFileHandler; typedef IAsyncOperationCompletedHandler<IVectorView<StorageFile *> *> MultipleFileHandler; typedef IAsyncOperationCompletedHandler<StorageFolder *> SingleFolderHandler; +#endif QT_BEGIN_NAMESPACE @@ -142,6 +149,16 @@ private: QVector<HSTRING> impl; }; +#ifdef Q_OS_WINPHONE +class QActivationEvent : public QEvent +{ +public: + IInspectable *args() const { + return reinterpret_cast<IInspectable *>(d); + } +}; +#endif + template<typename T> static bool initializePicker(HSTRING runtimeId, T **picker, const QSharedPointer<QFileDialogOptions> &options) { @@ -200,6 +217,111 @@ static bool initializeOpenPickerOptions(T *picker, const QSharedPointer<QFileDia return true; } +static bool pickFiles(IFileOpenPicker *picker, QWinRTFileDialogHelper *helper, bool singleFile) +{ + Q_ASSERT(picker); + Q_ASSERT(helper); + HRESULT hr; +#ifdef Q_OS_WINPHONE + hr = QEventDispatcherWinRT::runOnXamlThread([picker, singleFile]() { + HRESULT hr; + ComPtr<IFileOpenPicker2> picker2; + hr = picker->QueryInterface(IID_PPV_ARGS(picker2.GetAddressOf())); + RETURN_HR_IF_FAILED("Failed to cast file picker"); + if (singleFile) + return picker2->PickSingleFileAndContinue(); + else + return picker2->PickMultipleFilesAndContinue(); + }); + RETURN_FALSE_IF_FAILED("Failed to open file picker"); + QAbstractEventDispatcher *eventDispatcher = QCoreApplication::eventDispatcher(); + Q_ASSERT(eventDispatcher); + eventDispatcher->installEventFilter(helper); + return true; +#else + hr = QEventDispatcherWinRT::runOnXamlThread([picker, helper, singleFile]() { + HRESULT hr; + if (singleFile) { + ComPtr<IAsyncOperation<StorageFile *>> op; + hr = picker->PickSingleFileAsync(&op); + RETURN_HR_IF_FAILED("Failed to open single file picker"); + hr = op->put_Completed(Callback<SingleFileHandler>(helper, &QWinRTFileDialogHelper::onSingleFilePicked).Get()); + RETURN_HR_IF_FAILED("Failed to attach file picker callback"); + } else { + ComPtr<IAsyncOperation<IVectorView<StorageFile *> *>> op; + hr = picker->PickMultipleFilesAsync(&op); + RETURN_HR_IF_FAILED("Failed to open multi file picker"); + hr = op->put_Completed(Callback<MultipleFileHandler>(helper, &QWinRTFileDialogHelper::onMultipleFilesPicked).Get()); + RETURN_HR_IF_FAILED("Failed to attach multi file callback"); + } + return S_OK; + }); + return SUCCEEDED(hr); +#endif +} + +static bool pickFolder(IFolderPicker *picker, QWinRTFileDialogHelper *helper) +{ + Q_ASSERT(picker); + Q_ASSERT(helper); + HRESULT hr; +#ifdef Q_OS_WINPHONE + hr = QEventDispatcherWinRT::runOnXamlThread([picker]() { + HRESULT hr; + ComPtr<IFolderPicker2> picker2; + hr = picker->QueryInterface(IID_PPV_ARGS(picker2.GetAddressOf())); + RETURN_HR_IF_FAILED("Failed to cast folder picker"); + return picker2->PickFolderAndContinue(); + }); + RETURN_FALSE_IF_FAILED("Failed to open folder picker"); + QAbstractEventDispatcher *eventDispatcher = QCoreApplication::eventDispatcher(); + Q_ASSERT(eventDispatcher); + eventDispatcher->installEventFilter(helper); +#else + hr = QEventDispatcherWinRT::runOnXamlThread([picker, helper]() { + HRESULT hr; + ComPtr<IAsyncOperation<StorageFolder *>> op; + hr = picker->PickSingleFolderAsync(&op); + RETURN_HR_IF_FAILED("Failed to open folder picker"); + hr = op->put_Completed(Callback<SingleFolderHandler>(helper, &QWinRTFileDialogHelper::onSingleFolderPicked).Get()); + RETURN_HR_IF_FAILED("Failed to attach folder picker callback"); + return S_OK; + }); +#endif + return SUCCEEDED(hr); +} + +static bool pickSaveFile(IFileSavePicker *picker, QWinRTFileDialogHelper *helper) +{ + Q_ASSERT(picker); + Q_ASSERT(helper); + HRESULT hr; +#ifdef Q_OS_WINPHONE + hr = QEventDispatcherWinRT::runOnXamlThread([picker]() { + HRESULT hr; + ComPtr<IFileSavePicker2> picker2; + hr = picker->QueryInterface(IID_PPV_ARGS(picker2.GetAddressOf())); + RETURN_HR_IF_FAILED("Failed to cast save file picker"); + return picker2->PickSaveFileAndContinue(); + }); + RETURN_FALSE_IF_FAILED("Failed to open single file picker"); + QAbstractEventDispatcher *eventDispatcher = QCoreApplication::eventDispatcher(); + Q_ASSERT(eventDispatcher); + eventDispatcher->installEventFilter(helper); +#else + hr = QEventDispatcherWinRT::runOnXamlThread([picker, helper]() { + HRESULT hr; + ComPtr<IAsyncOperation<StorageFile *>> op; + hr = picker->PickSaveFileAsync(&op); + RETURN_HR_IF_FAILED("Failed to open save file picker"); + hr = op->put_Completed(Callback<SingleFileHandler>(helper, &QWinRTFileDialogHelper::onSingleFilePicked).Get()); + RETURN_HR_IF_FAILED("Failed to attach save file picker callback"); + return S_OK; + }); +#endif + return SUCCEEDED(hr); +} + class QWinRTFileDialogHelperPrivate { public: @@ -260,18 +382,9 @@ bool QWinRTFileDialogHelper::show(Qt::WindowFlags windowFlags, Qt::WindowModalit if (!initializeOpenPickerOptions(picker.Get(), dialogOptions)) return false; - if (dialogOptions->fileMode() == QFileDialogOptions::ExistingFiles) { - ComPtr<IAsyncOperation<IVectorView<StorageFile *> *>> op; - hr = picker->PickMultipleFilesAsync(&op); - RETURN_FALSE_IF_FAILED("Failed to open multi file picker"); - hr = op->put_Completed(Callback<MultipleFileHandler>(this, &QWinRTFileDialogHelper::onMultipleFilesPicked).Get()); - } else { - ComPtr<IAsyncOperation<StorageFile *>> op; - hr = picker->PickSingleFileAsync(&op); - RETURN_FALSE_IF_FAILED("Failed to open single file picker"); - hr = op->put_Completed(Callback<SingleFileHandler>(this, &QWinRTFileDialogHelper::onSingleFilePicked).Get()); - } - RETURN_FALSE_IF_FAILED("Failed to attach file picker callback"); + if (!pickFiles(picker.Get(), this, dialogOptions->fileMode() == QFileDialogOptions::ExistingFile)) + return false; + break; } case QFileDialogOptions::Directory: @@ -284,11 +397,9 @@ bool QWinRTFileDialogHelper::show(Qt::WindowFlags windowFlags, Qt::WindowModalit if (!initializeOpenPickerOptions(picker.Get(), dialogOptions)) return false; - ComPtr<IAsyncOperation<StorageFolder *>> op; - hr = picker->PickSingleFolderAsync(&op); - RETURN_FALSE_IF_FAILED("Failed to open folder picker"); - hr = op->put_Completed(Callback<SingleFolderHandler>(this, &QWinRTFileDialogHelper::onSingleFolderPicked).Get()); - RETURN_FALSE_IF_FAILED("Failed to attach folder picker callback"); + if (!pickFolder(picker.Get(), this)) + return false; + break; } } @@ -324,7 +435,12 @@ bool QWinRTFileDialogHelper::show(Qt::WindowFlags windowFlags, Qt::WindowModalit filterTitle.length()); boolean replaced; hr = choices->Insert(namedFilterRef.Get(), entry.Get(), &replaced); - RETURN_FALSE_IF_FAILED("Failed to insert file extension choice entry"); + // Only print a warning as * or *.* is not a valid choice on Windows 10 + // but used on a regular basis on all other platforms + if (FAILED(hr)) { + qWarning("Failed to insert file extension choice entry: %s: %s", + qPrintable(filterTitle), qPrintable(qt_error_string(hr))); + } } } @@ -344,11 +460,9 @@ bool QWinRTFileDialogHelper::show(Qt::WindowFlags windowFlags, Qt::WindowModalit RETURN_FALSE_IF_FAILED("Failed to set suggested file name"); } - ComPtr<IAsyncOperation<StorageFile *>> op; - hr = picker->PickSaveFileAsync(&op); - RETURN_FALSE_IF_FAILED("Failed to open save file picker"); - hr = op->put_Completed(Callback<SingleFileHandler>(this, &QWinRTFileDialogHelper::onSingleFilePicked).Get()); - RETURN_FALSE_IF_FAILED("Failed to attach file picker callback"); + if (!pickSaveFile(picker.Get(), this)) + return false; + break; } } @@ -367,6 +481,68 @@ void QWinRTFileDialogHelper::hide() d->shown = false; } +#ifdef Q_OS_WINPHONE +bool QWinRTFileDialogHelper::eventFilter(QObject *, QEvent *e) +{ + if (e->type() != QEvent::WinEventAct) + return false; + + HRESULT hr; + QActivationEvent *event = static_cast<QActivationEvent *>(e); + ComPtr<IInspectable> inspectable = event->args(); + ComPtr<IActivatedEventArgs> arguments; + hr = inspectable.As(&arguments); + Q_ASSERT_SUCCEEDED(hr); + + ActivationKind activationKind; + hr = arguments->get_Kind(&activationKind); + Q_ASSERT_SUCCEEDED(hr); + + // Handle only File, Folder and Save file pick continuation here. + if (activationKind != ActivationKind_PickFileContinuation + && activationKind != ActivationKind_PickFolderContinuation + && activationKind != ActivationKind_PickSaveFileContinuation) { + return false; + } + + QAbstractEventDispatcher *eventDispatcher = QCoreApplication::eventDispatcher(); + Q_ASSERT(eventDispatcher); + eventDispatcher->removeEventFilter(this); + e->accept(); + + if (activationKind == ActivationKind_PickFileContinuation) { + ComPtr<IFileOpenPickerContinuationEventArgs> fileContinuationArgs; + hr = arguments.As(&fileContinuationArgs); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IVectorView<StorageFile *>> files; + hr = fileContinuationArgs->get_Files(&files); + Q_ASSERT_SUCCEEDED(hr); + hr = onFilesPicked(files.Get()); + Q_ASSERT_SUCCEEDED(hr); + } else if (activationKind == ActivationKind_PickFolderContinuation) { + ComPtr<IFolderPickerContinuationEventArgs> folderContinuationArgs; + hr = arguments.As(&folderContinuationArgs); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IStorageFolder> folder; + hr = folderContinuationArgs->get_Folder(&folder); + Q_ASSERT_SUCCEEDED(hr); + hr = onFolderPicked(folder.Get()); + Q_ASSERT_SUCCEEDED(hr); + } else { + ComPtr<IFileSavePickerContinuationEventArgs> saveFileContinuationArgs; + hr = arguments.As(&saveFileContinuationArgs); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IStorageFile> file; + hr = saveFileContinuationArgs->get_File(&file); + Q_ASSERT_SUCCEEDED(hr); + hr = onFilePicked(file.Get()); + Q_ASSERT_SUCCEEDED(hr); + } + + return true; +} +#endif + void QWinRTFileDialogHelper::setDirectory(const QUrl &directory) { Q_D(QWinRTFileDialogHelper); @@ -403,6 +579,7 @@ QString QWinRTFileDialogHelper::selectedNameFilter() const return d->selectedNameFilter; } +#ifndef Q_OS_WINPHONE HRESULT QWinRTFileDialogHelper::onSingleFilePicked(IAsyncOperation<StorageFile *> *args, AsyncStatus status) { Q_D(QWinRTFileDialogHelper); @@ -419,14 +596,7 @@ HRESULT QWinRTFileDialogHelper::onSingleFilePicked(IAsyncOperation<StorageFile * ComPtr<IStorageFile> file; hr = args->GetResults(&file); Q_ASSERT_SUCCEEDED(hr); - if (!file) { - emit reject(); - return S_OK; - } - - appendFile(file.Get()); - emit accept(); - return S_OK; + return onFilePicked(file.Get()); } HRESULT QWinRTFileDialogHelper::onMultipleFilesPicked(IAsyncOperation<IVectorView<StorageFile *> *> *args, AsyncStatus status) @@ -445,17 +615,50 @@ HRESULT QWinRTFileDialogHelper::onMultipleFilesPicked(IAsyncOperation<IVectorVie ComPtr<IVectorView<StorageFile *>> fileList; hr = args->GetResults(&fileList); RETURN_HR_IF_FAILED("Failed to get file list"); + return onFilesPicked(fileList.Get()); +} +HRESULT QWinRTFileDialogHelper::onSingleFolderPicked(IAsyncOperation<StorageFolder *> *args, AsyncStatus status) +{ + Q_D(QWinRTFileDialogHelper); + + QEventLoopLocker locker(&d->loop); + d->shown = false; + d->selectedFiles.clear(); + if (status == Canceled || status == Error) { + emit reject(); + return S_OK; + } + + HRESULT hr; + ComPtr<IStorageFolder> folder; + hr = args->GetResults(&folder); + Q_ASSERT_SUCCEEDED(hr); + return onFolderPicked(folder.Get()); +} +#endif //Q_OS_WINPHONE + +HRESULT QWinRTFileDialogHelper::onFilesPicked(IVectorView<StorageFile *> *files) +{ +#ifdef Q_OS_WINPHONE + Q_D(QWinRTFileDialogHelper); + QEventLoopLocker locker(&d->loop); + d->shown = false; + d->selectedFiles.clear(); +#endif + + HRESULT hr; quint32 size; - hr = fileList->get_Size(&size); + hr = files->get_Size(&size); Q_ASSERT_SUCCEEDED(hr); if (!size) { emit reject(); return S_OK; } + for (quint32 i = 0; i < size; ++i) { ComPtr<IStorageFile> file; - hr = fileList->GetAt(i, &file); + hr = files->GetAt(i, &file); Q_ASSERT_SUCCEEDED(hr); appendFile(file.Get()); } @@ -464,28 +667,40 @@ HRESULT QWinRTFileDialogHelper::onMultipleFilesPicked(IAsyncOperation<IVectorVie return S_OK; } -HRESULT QWinRTFileDialogHelper::onSingleFolderPicked(IAsyncOperation<StorageFolder *> *args, AsyncStatus status) +HRESULT QWinRTFileDialogHelper::onFolderPicked(IStorageFolder *folder) { +#ifdef Q_OS_WINPHONE Q_D(QWinRTFileDialogHelper); - QEventLoopLocker locker(&d->loop); d->shown = false; d->selectedFiles.clear(); - if (status == Canceled || status == Error) { +#endif + + if (!folder) { emit reject(); return S_OK; } - HRESULT hr; - ComPtr<IStorageFolder> folder; - hr = args->GetResults(&folder); - Q_ASSERT_SUCCEEDED(hr); - if (!folder) { + appendFile(folder); + emit accept(); + return S_OK; +} + +HRESULT QWinRTFileDialogHelper::onFilePicked(IStorageFile *file) +{ +#ifdef Q_OS_WINPHONE + Q_D(QWinRTFileDialogHelper); + QEventLoopLocker locker(&d->loop); + d->shown = false; + d->selectedFiles.clear(); +#endif + + if (!file) { emit reject(); return S_OK; } - appendFile(folder.Get()); + appendFile(file); emit accept(); return S_OK; } diff --git a/src/plugins/platforms/winrt/qwinrtfiledialoghelper.h b/src/plugins/platforms/winrt/qwinrtfiledialoghelper.h index 51b79c84ef..d6bacd2db9 100644 --- a/src/plugins/platforms/winrt/qwinrtfiledialoghelper.h +++ b/src/plugins/platforms/winrt/qwinrtfiledialoghelper.h @@ -47,6 +47,7 @@ namespace ABI { class StorageFile; class StorageFolder; struct IStorageFile; + struct IStorageFolder; } namespace Foundation { enum class AsyncStatus; @@ -71,6 +72,9 @@ public: void exec() Q_DECL_OVERRIDE; bool show(Qt::WindowFlags, Qt::WindowModality, QWindow *) Q_DECL_OVERRIDE; void hide() Q_DECL_OVERRIDE; +#ifdef Q_OS_WINPHONE + bool eventFilter(QObject *o, QEvent *e) Q_DECL_OVERRIDE; +#endif bool defaultNameFilterDisables() const Q_DECL_OVERRIDE { return false; } void setDirectory(const QUrl &directory) Q_DECL_OVERRIDE; @@ -81,13 +85,19 @@ public: void selectNameFilter(const QString &selectedNameFilter) Q_DECL_OVERRIDE; QString selectedNameFilter() const; -private: +#ifndef Q_OS_WINPHONE HRESULT onSingleFilePicked(ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Storage::StorageFile *> *, ABI::Windows::Foundation::AsyncStatus); HRESULT onMultipleFilesPicked(ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Foundation::Collections::IVectorView<ABI::Windows::Storage::StorageFile *> *> *, ABI::Windows::Foundation::AsyncStatus); HRESULT onSingleFolderPicked(ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Storage::StorageFolder *> *, ABI::Windows::Foundation::AsyncStatus); +#endif + +private: + HRESULT onFilesPicked(ABI::Windows::Foundation::Collections::IVectorView<ABI::Windows::Storage::StorageFile *> *files); + HRESULT onFolderPicked(ABI::Windows::Storage::IStorageFolder *folder); + HRESULT onFilePicked(ABI::Windows::Storage::IStorageFile *file); void appendFile(IInspectable *); QScopedPointer<QWinRTFileDialogHelperPrivate> d_ptr; diff --git a/src/plugins/platforms/winrt/qwinrtfontdatabase.cpp b/src/plugins/platforms/winrt/qwinrtfontdatabase.cpp index 09edea52e7..c348faf015 100644 --- a/src/plugins/platforms/winrt/qwinrtfontdatabase.cpp +++ b/src/plugins/platforms/winrt/qwinrtfontdatabase.cpp @@ -39,13 +39,11 @@ #include <QtCore/QCoreApplication> #include <QtCore/QFile> -#ifdef QT_WINRT_USE_DWRITE #include <QtCore/QUuid> #include <QtGui/private/qfontengine_ft_p.h> #include <dwrite_1.h> #include <wrl.h> using namespace Microsoft::WRL; -#endif // QT_WINRT_USE_DWRITE QT_BEGIN_NAMESPACE @@ -122,9 +120,7 @@ QString QWinRTFontDatabase::fontDir() const const QString applicationDirPath = QCoreApplication::applicationDirPath(); fontDirectory = applicationDirPath + QLatin1String("/fonts"); if (!QFile::exists(fontDirectory)) { -#ifdef QT_WINRT_USE_DWRITE if (m_fontFamilies.isEmpty()) -#endif qWarning("No fonts directory found in application package."); fontDirectory = applicationDirPath; } @@ -132,8 +128,6 @@ QString QWinRTFontDatabase::fontDir() const return fontDirectory; } -#ifdef QT_WINRT_USE_DWRITE - QWinRTFontDatabase::~QWinRTFontDatabase() { foreach (IDWriteFontFile *fontFile, m_fonts.keys()) @@ -449,13 +443,4 @@ void QWinRTFontDatabase::releaseHandle(void *handle) QBasicFontDatabase::releaseHandle(handle); } -#else // QT_WINRT_USE_DWRITE - -QFont QWinRTFontDatabase::defaultFont() const -{ - return QFont(QFontDatabase().families().value(0)); -} - -#endif // !QT_WINRT_USE_DWRITE - QT_END_NAMESPACE diff --git a/src/plugins/platforms/winrt/qwinrtfontdatabase.h b/src/plugins/platforms/winrt/qwinrtfontdatabase.h index 7b3f402c13..a88092e432 100644 --- a/src/plugins/platforms/winrt/qwinrtfontdatabase.h +++ b/src/plugins/platforms/winrt/qwinrtfontdatabase.h @@ -39,26 +39,21 @@ #include <QtPlatformSupport/private/qbasicfontdatabase_p.h> -#ifdef QT_WINRT_USE_DWRITE struct IDWriteFontFile; struct IDWriteFontFamily; -#endif QT_BEGIN_NAMESPACE -#ifdef QT_WINRT_USE_DWRITE struct FontDescription { quint32 index; QByteArray uuid; }; -#endif class QWinRTFontDatabase : public QBasicFontDatabase { public: QString fontDir() const; -#ifdef QT_WINRT_USE_DWRITE ~QWinRTFontDatabase(); QFont defaultFont() const Q_DECL_OVERRIDE; bool fontsAlwaysScalable() const Q_DECL_OVERRIDE; @@ -69,7 +64,6 @@ public: private: QHash<IDWriteFontFile *, FontDescription> m_fonts; QHash<QString, IDWriteFontFamily *> m_fontFamilies; -#endif // QT_WINRT_USE_DWRITE }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/winrt/qwinrtinputcontext.cpp b/src/plugins/platforms/winrt/qwinrtinputcontext.cpp index c94b53ef1c..9d8792a6db 100644 --- a/src/plugins/platforms/winrt/qwinrtinputcontext.cpp +++ b/src/plugins/platforms/winrt/qwinrtinputcontext.cpp @@ -37,7 +37,9 @@ #include "qwinrtinputcontext.h" #include "qwinrtscreen.h" #include <QtGui/QWindow> +#include <private/qeventdispatcher_winrt_p.h> +#include <functional> #include <wrl.h> #include <roapi.h> #include <windows.ui.viewmanagement.h> @@ -52,6 +54,14 @@ typedef ITypedEventHandler<InputPane*, InputPaneVisibilityEventArgs*> InputPaneV QT_BEGIN_NAMESPACE +inline QRectF getInputPaneRect(IInputPane *pane, qreal scaleFactor) +{ + Rect rect; + pane->get_OccludedRect(&rect); + return QRectF(qRound(rect.X * scaleFactor), qRound(rect.Y * scaleFactor), + qRound(rect.Width * scaleFactor), qRound(rect.Height * scaleFactor)); +} + /*! \class QWinRTInputContext \brief Manages Input Method visibility @@ -85,7 +95,7 @@ QWinRTInputContext::QWinRTInputContext(QWinRTScreen *screen) inputPane->add_Hiding(Callback<InputPaneVisibilityHandler>( this, &QWinRTInputContext::onHiding).Get(), &hideToken); - handleVisibilityChange(inputPane); + m_keyboardRect = getInputPaneRect(inputPane, m_screen->scaleFactor()); m_isInputPanelVisible = !m_keyboardRect.isEmpty(); } else { qWarning(Q_FUNC_INFO ": failed to retrieve InputPane."); @@ -118,10 +128,7 @@ HRESULT QWinRTInputContext::onHiding(IInputPane *pane, IInputPaneVisibilityEvent HRESULT QWinRTInputContext::handleVisibilityChange(IInputPane *pane) { - Rect rect; - pane->get_OccludedRect(&rect); - const QRectF keyboardRect = QRectF(qRound(rect.X * m_screen->scaleFactor()), qRound(rect.Y * m_screen->scaleFactor()), - qRound(rect.Width * m_screen->scaleFactor()), qRound(rect.Height * m_screen->scaleFactor())); + const QRectF keyboardRect = getInputPaneRect(pane, m_screen->scaleFactor()); if (m_keyboardRect != keyboardRect) { m_keyboardRect = keyboardRect; emitKeyboardRectChanged(); @@ -129,7 +136,7 @@ HRESULT QWinRTInputContext::handleVisibilityChange(IInputPane *pane) return S_OK; } -#ifdef Q_OS_WINPHONE +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) static HRESULT getInputPane(ComPtr<IInputPane2> *inputPane2) { @@ -158,159 +165,34 @@ static HRESULT getInputPane(ComPtr<IInputPane2> *inputPane2) void QWinRTInputContext::showInputPanel() { - ComPtr<IInputPane2> inputPane; - HRESULT hr = getInputPane(&inputPane); - if (FAILED(hr)) - return; - - boolean success; - hr = inputPane->TryShow(&success); - if (FAILED(hr)) - qErrnoWarning(hr, "Failed to show input panel."); + QEventDispatcherWinRT::runOnXamlThread([&]() { + ComPtr<IInputPane2> inputPane; + HRESULT hr = getInputPane(&inputPane); + if (FAILED(hr)) + return hr; + boolean success; + hr = inputPane->TryShow(&success); + if (FAILED(hr) || !success) + qErrnoWarning(hr, "Failed to show input panel."); + return hr; + }); } void QWinRTInputContext::hideInputPanel() { - ComPtr<IInputPane2> inputPane; - HRESULT hr = getInputPane(&inputPane); - if (FAILED(hr)) - return; - - boolean success; - hr = inputPane->TryHide(&success); - if (FAILED(hr)) - qErrnoWarning(hr, "Failed to hide input panel."); -} - -#else // Q_OS_WINPHONE - -// IRawElementProviderSimple -HRESULT QWinRTInputContext::get_ProviderOptions(ProviderOptions *retVal) -{ - *retVal = ProviderOptions_ServerSideProvider|ProviderOptions_UseComThreading; - return S_OK; -} - -HRESULT QWinRTInputContext::GetPatternProvider(PATTERNID id, IUnknown **retVal) -{ - switch (id) { - case 10002: //UIA_ValuePatternId - return QueryInterface(__uuidof(IValueProvider), (void**)retVal); - break; - case 10014: //UIA_TextPatternId: - return QueryInterface(__uuidof(ITextProvider), (void**)retVal); - case 10029: //UIA_TextChildPatternId: - *retVal = nullptr; - break; - default: - qWarning("Unhandled pattern ID: %d", id); - break; - } - return S_OK; -} - -HRESULT QWinRTInputContext::GetPropertyValue(PROPERTYID idProp, VARIANT *retVal) -{ - switch (idProp) { - case 30003: //UIA_ControlTypePropertyId - retVal->vt = VT_I4; - retVal->lVal = 50025; //UIA_CustomControlTypeId - break; - case 30008: //UIA_IsKeyboardFocusablePropertyId - case 30009: //UIA_HasKeyboardFocusPropertyId - // These are probably never actually called - case 30016: //UIA_IsControlElementPropertyId - case 30017: //UIA_IsContentElementPropertyId - retVal->vt = VT_BOOL; - retVal->boolVal = VARIANT_TRUE; - break; - case 30019: //UIA_IsPasswordPropertyId - retVal->vt = VT_BOOL; - retVal->boolVal = VARIANT_FALSE; - break; - case 30020: //UIA_NativeWindowHandlePropertyId - retVal->vt = VT_PTR; - retVal->punkVal = m_screen->coreWindow(); - break; - } - return S_OK; -} - -HRESULT QWinRTInputContext::get_HostRawElementProvider(IRawElementProviderSimple **retVal) -{ - // Return the window's element provider - IInspectable *hostProvider; - HRESULT hr = m_screen->coreWindow()->get_AutomationHostProvider(&hostProvider); - if (SUCCEEDED(hr)) { - hr = hostProvider->QueryInterface(IID_PPV_ARGS(retVal)); - hostProvider->Release(); - } - return hr; -} - -// ITextProvider -HRESULT QWinRTInputContext::GetSelection(SAFEARRAY **) -{ - // To be useful, requires listening to the focus object for a selection change and raising an event - return S_OK; -} - -HRESULT QWinRTInputContext::GetVisibleRanges(SAFEARRAY **) -{ - // To be useful, requires listening to the focus object for a selection change and raising an event - return S_OK; -} - -HRESULT QWinRTInputContext::RangeFromChild(IRawElementProviderSimple *,ITextRangeProvider **) -{ - // To be useful, requires listening to the focus object for a selection change and raising an event - return S_OK; -} - -HRESULT QWinRTInputContext::RangeFromPoint(UiaPoint, ITextRangeProvider **) -{ - // To be useful, requires listening to the focus object for a selection change and raising an event - return S_OK; -} - -HRESULT QWinRTInputContext::get_DocumentRange(ITextRangeProvider **) -{ - // To be useful, requires listening to the focus object for a selection change and raising an event - return S_OK; -} - -HRESULT QWinRTInputContext::get_SupportedTextSelection(SupportedTextSelection *) -{ - // To be useful, requires listening to the focus object for a selection change and raising an event - return S_OK; -} - -// IValueProvider -HRESULT QWinRTInputContext::SetValue(LPCWSTR) -{ - // To be useful, requires listening to the focus object for a value change and raising an event - // May be useful for inputPanel autocomplete, etc. - return S_OK; -} - -HRESULT QWinRTInputContext::get_Value(BSTR *) -{ - // To be useful, requires listening to the focus object for a value change and raising an event - // May be useful for inputPanel autocomplete, etc. - return S_OK; -} - -HRESULT QWinRTInputContext::get_IsReadOnly(BOOL *isReadOnly) -{ - // isReadOnly dictates keyboard opening behavior when view is tapped. - // We need to decide if the user tapped within a control which is about to receive focus... - // Since this isn't possible (this function gets called before we receive the touch event), - // the most platform-aligned option is to show the keyboard if an editable item has focus, - // and close the keyboard if it is already open. - *isReadOnly = m_isInputPanelVisible || !inputMethodAccepted(); - return S_OK; + QEventDispatcherWinRT::runOnXamlThread([&]() { + ComPtr<IInputPane2> inputPane; + HRESULT hr = getInputPane(&inputPane); + if (FAILED(hr)) + return hr; + boolean success; + hr = inputPane->TryHide(&success); + if (FAILED(hr) || !success) + qErrnoWarning(hr, "Failed to hide input panel."); + return hr; + }); } -#endif // !Q_OS_WINPHONE +#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) QT_END_NAMESPACE diff --git a/src/plugins/platforms/winrt/qwinrtinputcontext.h b/src/plugins/platforms/winrt/qwinrtinputcontext.h index ce7fbabf49..6f88ff46e6 100644 --- a/src/plugins/platforms/winrt/qwinrtinputcontext.h +++ b/src/plugins/platforms/winrt/qwinrtinputcontext.h @@ -41,9 +41,6 @@ #include <QtCore/QRectF> #include <wrl.h> -#ifndef Q_OS_WINPHONE -# include <UIAutomationCore.h> -#endif namespace ABI { namespace Windows { @@ -63,11 +60,6 @@ QT_BEGIN_NAMESPACE class QWinRTScreen; class QWinRTInputContext : public QPlatformInputContext -#ifndef Q_OS_WINPHONE - , public Microsoft::WRL::RuntimeClass< - Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::WinRtClassicComMix>, - IRawElementProviderSimple, ITextProvider, IValueProvider> -#endif // !Q_OS_WINPHONE { public: explicit QWinRTInputContext(QWinRTScreen *); @@ -76,29 +68,10 @@ public: bool isInputPanelVisible() const; -#ifdef Q_OS_WINPHONE +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) void showInputPanel(); void hideInputPanel(); -#else // Q_OS_WINPHONE - // IRawElementProviderSimple - HRESULT __stdcall get_ProviderOptions(ProviderOptions *retVal); - HRESULT __stdcall GetPatternProvider(PATTERNID, IUnknown **); - HRESULT __stdcall GetPropertyValue(PROPERTYID idProp, VARIANT *retVal); - HRESULT __stdcall get_HostRawElementProvider(IRawElementProviderSimple **retVal); - - // ITextProvider - HRESULT __stdcall GetSelection(SAFEARRAY **); - HRESULT __stdcall GetVisibleRanges(SAFEARRAY **); - HRESULT __stdcall RangeFromChild(IRawElementProviderSimple *,ITextRangeProvider **); - HRESULT __stdcall RangeFromPoint(UiaPoint, ITextRangeProvider **); - HRESULT __stdcall get_DocumentRange(ITextRangeProvider **); - HRESULT __stdcall get_SupportedTextSelection(SupportedTextSelection *); - - // IValueProvider - HRESULT __stdcall SetValue(LPCWSTR); - HRESULT __stdcall get_Value(BSTR *); - HRESULT __stdcall get_IsReadOnly(BOOL *); -#endif // !Q_OS_WINPHONE +#endif private: HRESULT onShowing(ABI::Windows::UI::ViewManagement::IInputPane *, diff --git a/src/plugins/platforms/winrt/qwinrtintegration.cpp b/src/plugins/platforms/winrt/qwinrtintegration.cpp index 70ee6dbe6a..9db5df995a 100644 --- a/src/plugins/platforms/winrt/qwinrtintegration.cpp +++ b/src/plugins/platforms/winrt/qwinrtintegration.cpp @@ -45,51 +45,151 @@ #include "qwinrtfontdatabase.h" #include "qwinrttheme.h" +#include <QtGui/QSurface> #include <QtGui/QOpenGLContext> +#include <qfunctions_winrt.h> +#include <functional> #include <wrl.h> +#include <windows.ui.xaml.h> +#include <windows.applicationmodel.h> +#include <windows.applicationmodel.core.h> #include <windows.ui.core.h> #include <windows.ui.viewmanagement.h> -#include <Windows.ApplicationModel.core.h> +#include <windows.graphics.display.h> +#ifdef Q_OS_WINPHONE +# include <windows.phone.ui.input.h> +#endif using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::ApplicationModel; +using namespace ABI::Windows::ApplicationModel::Core; +using namespace ABI::Windows::UI; using namespace ABI::Windows::UI::Core; using namespace ABI::Windows::UI::ViewManagement; +using namespace ABI::Windows::Graphics::Display; using namespace ABI::Windows::ApplicationModel::Core; +#ifdef Q_OS_WINPHONE +using namespace ABI::Windows::Phone::UI::Input; +#endif + +typedef IEventHandler<IInspectable *> ResumeHandler; +typedef IEventHandler<SuspendingEventArgs *> SuspendHandler; +#ifdef Q_OS_WINPHONE +typedef IEventHandler<BackPressedEventArgs*> BackPressedHandler; +#endif QT_BEGIN_NAMESPACE -QWinRTIntegration::QWinRTIntegration() - : m_success(false) - , m_fontDatabase(new QWinRTFontDatabase) - , m_services(new QWinRTServices) +typedef HRESULT (__stdcall ICoreApplication::*CoreApplicationCallbackRemover)(EventRegistrationToken); +uint qHash(CoreApplicationCallbackRemover key) { void *ptr = *(void **)(&key); return qHash(ptr); } +#ifdef Q_OS_WINPHONE +typedef HRESULT (__stdcall IHardwareButtonsStatics::*HardwareButtonsCallbackRemover)(EventRegistrationToken); +uint qHash(HardwareButtonsCallbackRemover key) { void *ptr = *(void **)(&key); return qHash(ptr); } +#endif + +class QWinRTIntegrationPrivate +{ +public: + QPlatformFontDatabase *fontDatabase; + QPlatformServices *platformServices; + QWinRTScreen *mainScreen; + QScopedPointer<QWinRTInputContext> inputContext; + + ComPtr<ICoreApplication> application; + QHash<CoreApplicationCallbackRemover, EventRegistrationToken> applicationTokens; +#ifdef Q_OS_WINPHONE + ComPtr<IHardwareButtonsStatics> hardwareButtons; + QHash<HardwareButtonsCallbackRemover, EventRegistrationToken> buttonsTokens; +#endif +}; + +QWinRTIntegration::QWinRTIntegration() : d_ptr(new QWinRTIntegrationPrivate) { - m_screen = new QWinRTScreen; - screenAdded(m_screen); + Q_D(QWinRTIntegration); + + d->fontDatabase = new QWinRTFontDatabase; + + HRESULT hr; + hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_ApplicationModel_Core_CoreApplication).Get(), + IID_PPV_ARGS(&d->application)); + Q_ASSERT_SUCCEEDED(hr); + hr = d->application->add_Suspending(Callback<SuspendHandler>(this, &QWinRTIntegration::onSuspended).Get(), + &d->applicationTokens[&ICoreApplication::remove_Resuming]); + Q_ASSERT_SUCCEEDED(hr); + hr = d->application->add_Resuming(Callback<ResumeHandler>(this, &QWinRTIntegration::onResume).Get(), + &d->applicationTokens[&ICoreApplication::remove_Resuming]); + Q_ASSERT_SUCCEEDED(hr); - m_success = true; +#ifdef Q_OS_WINPHONE + hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Phone_UI_Input_HardwareButtons).Get(), + IID_PPV_ARGS(&d->hardwareButtons)); + Q_ASSERT_SUCCEEDED(hr); + hr = d->hardwareButtons->add_BackPressed(Callback<BackPressedHandler>(this, &QWinRTIntegration::onBackButtonPressed).Get(), + &d->buttonsTokens[&IHardwareButtonsStatics::remove_BackPressed]); + Q_ASSERT_SUCCEEDED(hr); +#endif // Q_OS_WINPHONE + + QEventDispatcherWinRT::runOnXamlThread([d]() { + d->mainScreen = new QWinRTScreen; + d->inputContext.reset(new QWinRTInputContext(d->mainScreen)); + return S_OK; + }); + + screenAdded(d->mainScreen); + d->platformServices = new QWinRTServices; } QWinRTIntegration::~QWinRTIntegration() { + Q_D(QWinRTIntegration); + HRESULT hr; +#ifdef Q_OS_WINPHONE + for (QHash<HardwareButtonsCallbackRemover, EventRegistrationToken>::const_iterator i = d->buttonsTokens.begin(); i != d->buttonsTokens.end(); ++i) { + hr = (d->hardwareButtons.Get()->*i.key())(i.value()); + Q_ASSERT_SUCCEEDED(hr); + } +#endif + for (QHash<CoreApplicationCallbackRemover, EventRegistrationToken>::const_iterator i = d->applicationTokens.begin(); i != d->applicationTokens.end(); ++i) { + hr = (d->application.Get()->*i.key())(i.value()); + Q_ASSERT_SUCCEEDED(hr); + } + destroyScreen(d->mainScreen); Windows::Foundation::Uninitialize(); } +bool QWinRTIntegration::succeeded() const +{ + Q_D(const QWinRTIntegration); + return d->mainScreen; +} + QAbstractEventDispatcher *QWinRTIntegration::createEventDispatcher() const { return new QWinRTEventDispatcher; } +void QWinRTIntegration::initialize() +{ + Q_D(const QWinRTIntegration); + QEventDispatcherWinRT::runOnXamlThread([d]() { + d->mainScreen->initialize(); + return S_OK; + }); +} + bool QWinRTIntegration::hasCapability(QPlatformIntegration::Capability cap) const { switch (cap) { case ThreadedPixmaps: case OpenGL: case ApplicationState: - return true; case NonFullScreenWindows: - return false; + case MultipleWindows: + case RasterGLSurface: + return true; default: return QPlatformIntegration::hasCapability(cap); } @@ -112,28 +212,31 @@ QPlatformBackingStore *QWinRTIntegration::createPlatformBackingStore(QWindow *wi QPlatformOpenGLContext *QWinRTIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const { - QWinRTScreen *screen = static_cast<QWinRTScreen *>(context->screen()->handle()); - return new QWinRTEGLContext(context->format(), context->handle(), screen->eglDisplay(), screen->eglSurface(), screen->eglConfig()); + return new QWinRTEGLContext(context); } QPlatformFontDatabase *QWinRTIntegration::fontDatabase() const { - return m_fontDatabase; + Q_D(const QWinRTIntegration); + return d->fontDatabase; } QPlatformInputContext *QWinRTIntegration::inputContext() const { - return m_screen->inputContext(); + Q_D(const QWinRTIntegration); + return d->inputContext.data(); } QPlatformServices *QWinRTIntegration::services() const { - return m_services; + Q_D(const QWinRTIntegration); + return d->platformServices; } Qt::KeyboardModifiers QWinRTIntegration::queryKeyboardModifiers() const { - return m_screen->keyboardModifiers(); + Q_D(const QWinRTIntegration); + return d->mainScreen->keyboardModifiers(); } QStringList QWinRTIntegration::themeNames() const @@ -141,12 +244,45 @@ QStringList QWinRTIntegration::themeNames() const return QStringList(QLatin1String("winrt")); } -QPlatformTheme *QWinRTIntegration::createPlatformTheme(const QString & -name) const +QPlatformTheme *QWinRTIntegration::createPlatformTheme(const QString &name) const { if (name == QLatin1String("winrt")) return new QWinRTTheme(); return 0; } + +// System-level integration points + +#ifdef Q_OS_WINPHONE +HRESULT QWinRTIntegration::onBackButtonPressed(IInspectable *, IBackPressedEventArgs *args) +{ + Q_D(QWinRTIntegration); + QWindow *window = d->mainScreen->topWindow(); + QWindowSystemInterface::setSynchronousWindowSystemEvents(true); + const bool pressed = QWindowSystemInterface::handleExtendedKeyEvent(window, QEvent::KeyPress, Qt::Key_Back, Qt::NoModifier, + 0, 0, 0, QString(), false, 1, false); + const bool released = QWindowSystemInterface::handleExtendedKeyEvent(window, QEvent::KeyRelease, Qt::Key_Back, Qt::NoModifier, + 0, 0, 0, QString(), false, 1, false); + QWindowSystemInterface::setSynchronousWindowSystemEvents(false); + args->put_Handled(pressed || released); + return S_OK; +} +#endif // Q_OS_WINPHONE + +HRESULT QWinRTIntegration::onSuspended(IInspectable *, ISuspendingEventArgs *) +{ + QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationSuspended); + QWindowSystemInterface::flushWindowSystemEvents(); + return S_OK; +} + +HRESULT QWinRTIntegration::onResume(IInspectable *, IInspectable *) +{ + // First the system invokes onResume and then changes + // the visibility of the screen to be active. + QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationHidden); + return S_OK; +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/winrt/qwinrtintegration.h b/src/plugins/platforms/winrt/qwinrtintegration.h index bbd6c1e41b..3a151e1ed8 100644 --- a/src/plugins/platforms/winrt/qwinrtintegration.h +++ b/src/plugins/platforms/winrt/qwinrtintegration.h @@ -39,11 +39,33 @@ #include <qpa/qplatformintegration.h> +namespace ABI { + namespace Windows { + namespace ApplicationModel { + struct ISuspendingEventArgs; + } + namespace Foundation { + struct IAsyncAction; + } +#ifdef Q_OS_WINPHONE + namespace Phone { + namespace UI { + namespace Input { + struct IBackPressedEventArgs; + } + } + } +#endif + } +} +struct IAsyncInfo; +struct IInspectable; + QT_BEGIN_NAMESPACE class QAbstractEventDispatcher; -class QWinRTScreen; +class QWinRTIntegrationPrivate; class QWinRTIntegration : public QPlatformIntegration { private: @@ -53,29 +75,37 @@ public: static QWinRTIntegration *create() { - QWinRTIntegration *integration = new QWinRTIntegration; - return integration->m_success ? integration : 0; + QScopedPointer<QWinRTIntegration> integration(new QWinRTIntegration); + return integration->succeeded() ? integration.take() : nullptr; } - bool hasCapability(QPlatformIntegration::Capability cap) const; - QVariant styleHint(StyleHint hint) const; + bool succeeded() const; + + bool hasCapability(QPlatformIntegration::Capability cap) const Q_DECL_OVERRIDE; + QVariant styleHint(StyleHint hint) const Q_DECL_OVERRIDE; - QPlatformWindow *createPlatformWindow(QWindow *window) const; - QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const; - QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const; - QAbstractEventDispatcher *createEventDispatcher() const; - QPlatformFontDatabase *fontDatabase() const; - QPlatformInputContext *inputContext() const; - QPlatformServices *services() const; - Qt::KeyboardModifiers queryKeyboardModifiers() const; + QPlatformWindow *createPlatformWindow(QWindow *window) const Q_DECL_OVERRIDE; + QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const Q_DECL_OVERRIDE; + QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const Q_DECL_OVERRIDE; + QAbstractEventDispatcher *createEventDispatcher() const Q_DECL_OVERRIDE; + void initialize() Q_DECL_OVERRIDE; + QPlatformFontDatabase *fontDatabase() const Q_DECL_OVERRIDE; + QPlatformInputContext *inputContext() const Q_DECL_OVERRIDE; + QPlatformServices *services() const Q_DECL_OVERRIDE; + Qt::KeyboardModifiers queryKeyboardModifiers() const Q_DECL_OVERRIDE; + + QStringList themeNames() const Q_DECL_OVERRIDE; + QPlatformTheme *createPlatformTheme(const QString &name) const Q_DECL_OVERRIDE; - QStringList themeNames() const; - QPlatformTheme *createPlatformTheme(const QString &name) const; private: - bool m_success; - QWinRTScreen *m_screen; - QPlatformFontDatabase *m_fontDatabase; - QPlatformServices *m_services; +#ifdef Q_OS_WINPHONE + HRESULT onBackButtonPressed(IInspectable *, ABI::Windows::Phone::UI::Input::IBackPressedEventArgs *args); +#endif + HRESULT onSuspended(IInspectable *, ABI::Windows::ApplicationModel::ISuspendingEventArgs *); + HRESULT onResume(IInspectable *, IInspectable *); + + QScopedPointer<QWinRTIntegrationPrivate> d_ptr; + Q_DECLARE_PRIVATE(QWinRTIntegration) }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/winrt/qwinrtmessagedialoghelper.cpp b/src/plugins/platforms/winrt/qwinrtmessagedialoghelper.cpp index 4fc1fea626..bb04144563 100644 --- a/src/plugins/platforms/winrt/qwinrtmessagedialoghelper.cpp +++ b/src/plugins/platforms/winrt/qwinrtmessagedialoghelper.cpp @@ -38,7 +38,9 @@ #include "qwinrttheme.h" #include <QtCore/qfunctions_winrt.h> +#include <private/qeventdispatcher_winrt_p.h> +#include <functional> #include <windows.ui.popups.h> #include <windows.foundation.h> #include <windows.foundation.collections.h> @@ -131,53 +133,58 @@ bool QWinRTMessageDialogHelper::show(Qt::WindowFlags windowFlags, Qt::WindowModa RETURN_FALSE_IF_FAILED("Failed to create dialog"); } - // Add Buttons - ComPtr<IVector<IUICommand *>> dialogCommands; - hr = dialog->get_Commands(&dialogCommands); - RETURN_FALSE_IF_FAILED("Failed to get dialog commands"); - - // If no button is specified we need to create one to get close notification - int buttons = options->standardButtons(); - if (buttons == 0) - buttons = Ok; - - for (int i = FirstButton; i < LastButton; i<<=1) { - if (!(buttons & i)) - continue; - // Add native command - const QString label = d->theme->standardButtonText(i); - HStringReference nativeLabel(reinterpret_cast<LPCWSTR>(label.utf16()), label.size()); - ComPtr<IUICommand> command; - hr = commandFactory->Create(nativeLabel.Get(), &command); - RETURN_FALSE_IF_FAILED("Failed to create message box command"); - ComPtr<IInspectable> id = Make<CommandId>(static_cast<StandardButton>(i)); - hr = command->put_Id(id.Get()); - RETURN_FALSE_IF_FAILED("Failed to set command ID"); - hr = dialogCommands->Append(command.Get()); - if (hr == E_BOUNDS) { - qErrnoWarning(hr, "The WinRT message dialog supports a maximum of three buttons"); - continue; + hr = QEventDispatcherWinRT::runOnXamlThread([this, d, options, commandFactory, dialog]() { + HRESULT hr; + + // Add Buttons + ComPtr<IVector<IUICommand *>> dialogCommands; + hr = dialog->get_Commands(&dialogCommands); + RETURN_HR_IF_FAILED("Failed to get dialog commands"); + + // If no button is specified we need to create one to get close notification + int buttons = options->standardButtons(); + if (buttons == 0) + buttons = Ok; + + for (int i = FirstButton; i < LastButton; i<<=1) { + if (!(buttons & i)) + continue; + // Add native command + const QString label = d->theme->standardButtonText(i); + HStringReference nativeLabel(reinterpret_cast<LPCWSTR>(label.utf16()), label.size()); + ComPtr<IUICommand> command; + hr = commandFactory->Create(nativeLabel.Get(), &command); + RETURN_HR_IF_FAILED("Failed to create message box command"); + ComPtr<IInspectable> id = Make<CommandId>(static_cast<StandardButton>(i)); + hr = command->put_Id(id.Get()); + RETURN_HR_IF_FAILED("Failed to set command ID"); + hr = dialogCommands->Append(command.Get()); + if (hr == E_BOUNDS) { + qErrnoWarning(hr, "The WinRT message dialog supports a maximum of three buttons"); + continue; + } + RETURN_HR_IF_FAILED("Failed to append message box command"); + if (i == Abort || i == Cancel || i == Close) { + quint32 size; + hr = dialogCommands->get_Size(&size); + RETURN_HR_IF_FAILED("Failed to get command list size"); + hr = dialog->put_CancelCommandIndex(size - 1); + RETURN_HR_IF_FAILED("Failed to set cancel index"); + } } - RETURN_FALSE_IF_FAILED("Failed to append message box command"); - if (i == Abort || i == Cancel || i == Close) { - quint32 size; - hr = dialogCommands->get_Size(&size); - RETURN_FALSE_IF_FAILED("Failed to get command list size"); - hr = dialog->put_CancelCommandIndex(size - 1); - RETURN_FALSE_IF_FAILED("Failed to set cancel index"); - } - } - - ComPtr<IAsyncOperation<IUICommand *>> op; - hr = dialog->ShowAsync(&op); - RETURN_FALSE_IF_FAILED("Failed to show dialog"); - hr = op->put_Completed(Callback<DialogCompletedHandler>(this, &QWinRTMessageDialogHelper::onCompleted).Get()); - RETURN_FALSE_IF_FAILED("Failed to set dialog callback"); - - d->shown = true; - hr = op.As(&d->info); - RETURN_FALSE_IF_FAILED("Failed to acquire AsyncInfo for MessageDialog"); + ComPtr<IAsyncOperation<IUICommand *>> op; + hr = dialog->ShowAsync(&op); + RETURN_HR_IF_FAILED("Failed to show dialog"); + hr = op->put_Completed(Callback<DialogCompletedHandler>(this, &QWinRTMessageDialogHelper::onCompleted).Get()); + RETURN_HR_IF_FAILED("Failed to set dialog callback"); + d->shown = true; + hr = op.As(&d->info); + RETURN_HR_IF_FAILED("Failed to acquire AsyncInfo for MessageDialog"); + return hr; + }); + + RETURN_FALSE_IF_FAILED("Failed to show dialog") return true; } @@ -200,8 +207,7 @@ HRESULT QWinRTMessageDialogHelper::onCompleted(IAsyncOperation<IUICommand *> *as Q_UNUSED(status); Q_D(QWinRTMessageDialogHelper); - if (d->loop.isRunning()) - d->loop.exit(); + QEventLoopLocker locker(&d->loop); d->shown = false; diff --git a/src/plugins/platforms/winrt/qwinrtscreen.cpp b/src/plugins/platforms/winrt/qwinrtscreen.cpp index 8962332208..a642443386 100644 --- a/src/plugins/platforms/winrt/qwinrtscreen.cpp +++ b/src/plugins/platforms/winrt/qwinrtscreen.cpp @@ -36,26 +36,18 @@ #include "qwinrtscreen.h" -#define EGL_EGLEXT_PROTOTYPES -#include <EGL/eglext.h> -#include <d3d11.h> -#include <dxgi1_2.h> -#ifndef Q_OS_WINPHONE -#include <dxgi1_3.h> -#endif - #include "qwinrtbackingstore.h" #include "qwinrtinputcontext.h" #include "qwinrtcursor.h" -#include "qwinrteglcontext.h" +#include <private/qeventdispatcher_winrt_p.h> #include <QtGui/QSurfaceFormat> #include <QtGui/QGuiApplication> -#include <QtPlatformSupport/private/qeglconvenience_p.h> #include <qpa/qwindowsysteminterface.h> #include <QtCore/qt_windows.h> #include <QtCore/qfunctions_winrt.h> +#include <functional> #include <wrl.h> #include <windows.system.h> #include <Windows.Applicationmodel.h> @@ -64,12 +56,10 @@ #include <windows.ui.h> #include <windows.ui.core.h> #include <windows.ui.input.h> +#include <windows.ui.xaml.h> #include <windows.ui.viewmanagement.h> #include <windows.graphics.display.h> #include <windows.foundation.h> -#ifdef Q_OS_WINPHONE -#include <windows.phone.ui.input.h> -#endif using namespace Microsoft::WRL; using namespace Microsoft::WRL::Wrappers; @@ -77,17 +67,13 @@ using namespace ABI::Windows::ApplicationModel; using namespace ABI::Windows::ApplicationModel::Core; using namespace ABI::Windows::Foundation; using namespace ABI::Windows::System; +using namespace ABI::Windows::UI; using namespace ABI::Windows::UI::Core; using namespace ABI::Windows::UI::Input; using namespace ABI::Windows::UI::ViewManagement; using namespace ABI::Windows::Devices::Input; using namespace ABI::Windows::Graphics::Display; -#ifdef Q_OS_WINPHONE -using namespace ABI::Windows::Phone::UI::Input; -#endif -typedef IEventHandler<IInspectable*> ResumeHandler; -typedef IEventHandler<SuspendingEventArgs*> SuspendHandler; typedef ITypedEventHandler<CoreWindow*, WindowActivatedEventArgs*> ActivatedHandler; typedef ITypedEventHandler<CoreWindow*, CoreWindowEventArgs*> ClosedHandler; typedef ITypedEventHandler<CoreWindow*, CharacterReceivedEventArgs*> CharacterReceivedHandler; @@ -96,14 +82,34 @@ typedef ITypedEventHandler<CoreWindow*, KeyEventArgs*> KeyHandler; typedef ITypedEventHandler<CoreWindow*, PointerEventArgs*> PointerHandler; typedef ITypedEventHandler<CoreWindow*, WindowSizeChangedEventArgs*> SizeChangedHandler; typedef ITypedEventHandler<CoreWindow*, VisibilityChangedEventArgs*> VisibilityChangedHandler; -typedef ITypedEventHandler<CoreWindow*, AutomationProviderRequestedEventArgs*> AutomationProviderRequestedHandler; typedef ITypedEventHandler<DisplayInformation*, IInspectable*> DisplayInformationHandler; #ifdef Q_OS_WINPHONE -typedef IEventHandler<BackPressedEventArgs*> BackPressedHandler; +typedef ITypedEventHandler<StatusBar*, IInspectable*> StatusBarHandler; #endif QT_BEGIN_NAMESPACE +struct KeyInfo { + KeyInfo() + : virtualKey(0) + { + } + + KeyInfo(const QString &text, quint32 virtualKey) + : text(text) + , virtualKey(virtualKey) + { + } + + KeyInfo(quint32 virtualKey) + : virtualKey(virtualKey) + { + } + + QString text; + quint32 virtualKey; +}; + static inline Qt::ScreenOrientations qtOrientationsFromNative(DisplayOrientations native) { Qt::ScreenOrientations orientations = Qt::PrimaryOrientation; @@ -419,34 +425,29 @@ static inline Qt::Key qKeyFromCode(quint32 code, int mods) return static_cast<Qt::Key>(code & 0xff); } -typedef HRESULT (__stdcall ICoreApplication::*CoreApplicationCallbackRemover)(EventRegistrationToken); -uint qHash(CoreApplicationCallbackRemover key) { void *ptr = *(void **)(&key); return qHash(ptr); } typedef HRESULT (__stdcall ICoreWindow::*CoreWindowCallbackRemover)(EventRegistrationToken); uint qHash(CoreWindowCallbackRemover key) { void *ptr = *(void **)(&key); return qHash(ptr); } typedef HRESULT (__stdcall IDisplayInformation::*DisplayCallbackRemover)(EventRegistrationToken); uint qHash(DisplayCallbackRemover key) { void *ptr = *(void **)(&key); return qHash(ptr); } #ifdef Q_OS_WINPHONE -typedef HRESULT (__stdcall IHardwareButtonsStatics::*HardwareButtonsCallbackRemover)(EventRegistrationToken); -uint qHash(HardwareButtonsCallbackRemover key) { void *ptr = *(void **)(&key); return qHash(ptr); } +typedef HRESULT (__stdcall IStatusBar::*StatusBarCallbackRemover)(EventRegistrationToken); +uint qHash(StatusBarCallbackRemover key) { void *ptr = *(void **)(&key); return qHash(ptr); } #endif class QWinRTScreenPrivate { public: - ComPtr<ICoreApplication> application; + QTouchDevice *touchDevice; ComPtr<ICoreWindow> coreWindow; + ComPtr<Xaml::IDependencyObject> canvas; + ComPtr<IApplicationView> view; ComPtr<IDisplayInformation> displayInformation; #ifdef Q_OS_WINPHONE - ComPtr<IHardwareButtonsStatics> hardwareButtons; + ComPtr<IStatusBar> statusBar; #endif QScopedPointer<QWinRTCursor> cursor; -#ifdef Q_OS_WINPHONE - QScopedPointer<QWinRTInputContext> inputContext; -#else - ComPtr<QWinRTInputContext> inputContext; -#endif - + QHash<quint32, QWindowSystemInterface::TouchPoint> touchPoints; QSizeF logicalSize; QSurfaceFormat surfaceFormat; qreal logicalDpi; @@ -455,108 +456,43 @@ public: Qt::ScreenOrientation nativeOrientation; Qt::ScreenOrientation orientation; QList<QWindow *> visibleWindows; -#ifndef Q_OS_WINPHONE - QHash<quint32, QPair<Qt::Key, QString>> activeKeys; -#endif - QTouchDevice *touchDevice; - QHash<quint32, QWindowSystemInterface::TouchPoint> touchPoints; - - EGLDisplay eglDisplay; - EGLSurface eglSurface; - EGLConfig eglConfig; - - QHash<CoreApplicationCallbackRemover, EventRegistrationToken> applicationTokens; + QHash<Qt::Key, KeyInfo> activeKeys; QHash<CoreWindowCallbackRemover, EventRegistrationToken> windowTokens; QHash<DisplayCallbackRemover, EventRegistrationToken> displayTokens; #ifdef Q_OS_WINPHONE - QHash<HardwareButtonsCallbackRemover, EventRegistrationToken> buttonsTokens; + QHash<StatusBarCallbackRemover, EventRegistrationToken> statusBarTokens; #endif }; +// To be called from the XAML thread QWinRTScreen::QWinRTScreen() : d_ptr(new QWinRTScreenPrivate) { Q_D(QWinRTScreen); d->orientation = Qt::PrimaryOrientation; d->touchDevice = Q_NULLPTR; - d->eglDisplay = EGL_NO_DISPLAY; - // Obtain the WinRT Application, view, and window HRESULT hr; - hr = RoGetActivationFactory(Wrappers::HString::MakeReference(RuntimeClass_Windows_ApplicationModel_Core_CoreApplication).Get(), - IID_PPV_ARGS(&d->application)); + ComPtr<Xaml::IWindowStatics> windowStatics; + hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_UI_Xaml_Window).Get(), + IID_PPV_ARGS(&windowStatics)); Q_ASSERT_SUCCEEDED(hr); - hr = d->application->add_Suspending(Callback<SuspendHandler>(this, &QWinRTScreen::onSuspended).Get(), &d->applicationTokens[&ICoreApplication::remove_Resuming]); + ComPtr<Xaml::IWindow> window; + hr = windowStatics->get_Current(&window); Q_ASSERT_SUCCEEDED(hr); - hr = d->application->add_Resuming(Callback<ResumeHandler>(this, &QWinRTScreen::onResume).Get(), &d->applicationTokens[&ICoreApplication::remove_Resuming]); + hr = window->Activate(); Q_ASSERT_SUCCEEDED(hr); - ComPtr<ICoreApplicationView> view; - hr = d->application->GetCurrentView(&view); - Q_ASSERT_SUCCEEDED(hr); - hr = view->get_CoreWindow(&d->coreWindow); + hr = window->get_CoreWindow(&d->coreWindow); Q_ASSERT_SUCCEEDED(hr); hr = d->coreWindow->Activate(); Q_ASSERT_SUCCEEDED(hr); -#ifdef Q_OS_WINPHONE - d->inputContext.reset(new QWinRTInputContext(this)); -#else - d->inputContext = Make<QWinRTInputContext>(this); -#endif - Rect rect; hr = d->coreWindow->get_Bounds(&rect); Q_ASSERT_SUCCEEDED(hr); d->logicalSize = QSizeF(rect.Width, rect.Height); - d->surfaceFormat.setAlphaBufferSize(0); - d->surfaceFormat.setRedBufferSize(8); - d->surfaceFormat.setGreenBufferSize(8); - d->surfaceFormat.setBlueBufferSize(8); - d->surfaceFormat.setDepthBufferSize(24); - d->surfaceFormat.setStencilBufferSize(8); - d->surfaceFormat.setRenderableType(QSurfaceFormat::OpenGLES); - d->surfaceFormat.setSamples(1); - d->surfaceFormat.setSwapBehavior(QSurfaceFormat::DoubleBuffer); - - hr = d->coreWindow->add_KeyDown(Callback<KeyHandler>(this, &QWinRTScreen::onKeyDown).Get(), &d->windowTokens[&ICoreWindow::remove_KeyDown]); - Q_ASSERT_SUCCEEDED(hr); - hr = d->coreWindow->add_KeyUp(Callback<KeyHandler>(this, &QWinRTScreen::onKeyUp).Get(), &d->windowTokens[&ICoreWindow::remove_KeyUp]); - Q_ASSERT_SUCCEEDED(hr); - hr = d->coreWindow->add_CharacterReceived(Callback<CharacterReceivedHandler>(this, &QWinRTScreen::onCharacterReceived).Get(), &d->windowTokens[&ICoreWindow::remove_CharacterReceived]); - Q_ASSERT_SUCCEEDED(hr); - hr = d->coreWindow->add_PointerEntered(Callback<PointerHandler>(this, &QWinRTScreen::onPointerEntered).Get(), &d->windowTokens[&ICoreWindow::remove_PointerEntered]); - Q_ASSERT_SUCCEEDED(hr); - hr = d->coreWindow->add_PointerExited(Callback<PointerHandler>(this, &QWinRTScreen::onPointerExited).Get(), &d->windowTokens[&ICoreWindow::remove_PointerExited]); - Q_ASSERT_SUCCEEDED(hr); - hr = d->coreWindow->add_PointerMoved(Callback<PointerHandler>(this, &QWinRTScreen::onPointerUpdated).Get(), &d->windowTokens[&ICoreWindow::remove_PointerMoved]); - Q_ASSERT_SUCCEEDED(hr); - hr = d->coreWindow->add_PointerPressed(Callback<PointerHandler>(this, &QWinRTScreen::onPointerUpdated).Get(), &d->windowTokens[&ICoreWindow::remove_PointerPressed]); - Q_ASSERT_SUCCEEDED(hr); - hr = d->coreWindow->add_PointerReleased(Callback<PointerHandler>(this, &QWinRTScreen::onPointerUpdated).Get(), &d->windowTokens[&ICoreWindow::remove_PointerReleased]); - Q_ASSERT_SUCCEEDED(hr); - hr = d->coreWindow->add_PointerWheelChanged(Callback<PointerHandler>(this, &QWinRTScreen::onPointerUpdated).Get(), &d->windowTokens[&ICoreWindow::remove_PointerWheelChanged]); - Q_ASSERT_SUCCEEDED(hr); -#ifndef Q_OS_WINPHONE - hr = d->coreWindow->add_SizeChanged(Callback<SizeChangedHandler>(this, &QWinRTScreen::onSizeChanged).Get(), &d->windowTokens[&ICoreWindow::remove_SizeChanged]); - Q_ASSERT_SUCCEEDED(hr); -#endif - hr = d->coreWindow->add_Activated(Callback<ActivatedHandler>(this, &QWinRTScreen::onActivated).Get(), &d->windowTokens[&ICoreWindow::remove_Activated]); - Q_ASSERT_SUCCEEDED(hr); - hr = d->coreWindow->add_Closed(Callback<ClosedHandler>(this, &QWinRTScreen::onClosed).Get(), &d->windowTokens[&ICoreWindow::remove_Closed]); - Q_ASSERT_SUCCEEDED(hr); - hr = d->coreWindow->add_VisibilityChanged(Callback<VisibilityChangedHandler>(this, &QWinRTScreen::onVisibilityChanged).Get(), &d->windowTokens[&ICoreWindow::remove_VisibilityChanged]); - Q_ASSERT_SUCCEEDED(hr); - hr = d->coreWindow->add_AutomationProviderRequested(Callback<AutomationProviderRequestedHandler>(this, &QWinRTScreen::onAutomationProviderRequested).Get(), &d->windowTokens[&ICoreWindow::remove_AutomationProviderRequested]); - Q_ASSERT_SUCCEEDED(hr); -#ifdef Q_OS_WINPHONE - hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Phone_UI_Input_HardwareButtons).Get(), IID_PPV_ARGS(&d->hardwareButtons)); - Q_ASSERT_SUCCEEDED(hr); - hr = d->hardwareButtons->add_BackPressed(Callback<BackPressedHandler>(this, &QWinRTScreen::onBackButtonPressed).Get(), &d->buttonsTokens[&IHardwareButtonsStatics::remove_BackPressed]); - Q_ASSERT_SUCCEEDED(hr); -#endif // Q_OS_WINPHONE - // Orientation handling ComPtr<IDisplayInformationStatics> displayInformationStatics; hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Graphics_Display_DisplayInformation).Get(), @@ -571,57 +507,47 @@ QWinRTScreen::QWinRTScreen() hr = d->displayInformation->get_NativeOrientation(&displayOrientation); Q_ASSERT_SUCCEEDED(hr); d->nativeOrientation = static_cast<Qt::ScreenOrientation>(static_cast<int>(qtOrientationsFromNative(displayOrientation))); + // Set initial pixel density + onDpiChanged(Q_NULLPTR, Q_NULLPTR); + d->orientation = d->nativeOrientation; - hr = d->displayInformation->add_OrientationChanged(Callback<DisplayInformationHandler>(this, &QWinRTScreen::onOrientationChanged).Get(), &d->displayTokens[&IDisplayInformation::remove_OrientationChanged]); - Q_ASSERT_SUCCEEDED(hr); + ComPtr<IApplicationViewStatics2> applicationViewStatics; + hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_UI_ViewManagement_ApplicationView).Get(), + IID_PPV_ARGS(&applicationViewStatics)); + RETURN_VOID_IF_FAILED("Could not get ApplicationViewStatics"); - hr = d->displayInformation->add_DpiChanged(Callback<DisplayInformationHandler>(this, &QWinRTScreen::onDpiChanged).Get(), &d->displayTokens[&IDisplayInformation::remove_DpiChanged]); - Q_ASSERT_SUCCEEDED(hr); + hr = applicationViewStatics->GetForCurrentView(&d->view); + RETURN_VOID_IF_FAILED("Could not access currentView"); - // Set initial orientation & pixel density - onDpiChanged(Q_NULLPTR, Q_NULLPTR); - d->orientation = d->nativeOrientation; - onOrientationChanged(Q_NULLPTR, Q_NULLPTR); + // Create a canvas and set it as the window content. Eventually, this should have its own method so multiple "screens" can be added + ComPtr<Xaml::Controls::ICanvas> canvas; + hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_UI_Xaml_Controls_Canvas).Get(), &canvas); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<Xaml::IFrameworkElement> frameworkElement; + hr = canvas.As(&frameworkElement); + Q_ASSERT_SUCCEEDED(hr); + hr = frameworkElement->put_Width(d->logicalSize.width()); + Q_ASSERT_SUCCEEDED(hr); + hr = frameworkElement->put_Height(d->logicalSize.height()); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<Xaml::IUIElement> uiElement; + hr = canvas.As(&uiElement); + Q_ASSERT_SUCCEEDED(hr); + hr = window->put_Content(uiElement.Get()); + Q_ASSERT_SUCCEEDED(hr); + hr = canvas.As(&d->canvas); + Q_ASSERT_SUCCEEDED(hr); - d->eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); - if (d->eglDisplay == EGL_NO_DISPLAY) - qCritical("Failed to initialize EGL display: 0x%x", eglGetError()); - - if (!eglInitialize(d->eglDisplay, NULL, NULL)) - qCritical("Failed to initialize EGL: 0x%x", eglGetError()); - - // Check that the device properly supports depth/stencil rendering, and disable them if not - ComPtr<ID3D11Device> d3dDevice; - const EGLBoolean ok = eglQuerySurfacePointerANGLE(d->eglDisplay, EGL_NO_SURFACE, EGL_DEVICE_EXT, (void **)d3dDevice.GetAddressOf()); - if (ok && d3dDevice) { - ComPtr<IDXGIDevice> dxgiDevice; - hr = d3dDevice.As(&dxgiDevice); - if (SUCCEEDED(hr)) { - ComPtr<IDXGIAdapter> dxgiAdapter; - hr = dxgiDevice->GetAdapter(&dxgiAdapter); - if (SUCCEEDED(hr)) { - ComPtr<IDXGIAdapter2> dxgiAdapter2; - hr = dxgiAdapter.As(&dxgiAdapter2); - if (SUCCEEDED(hr)) { - DXGI_ADAPTER_DESC2 desc; - hr = dxgiAdapter2->GetDesc2(&desc); - if (SUCCEEDED(hr)) { - // The following GPUs do not render properly with depth/stencil - if ((desc.VendorId == 0x4d4f4351 && desc.DeviceId == 0x32303032)) { // Qualcomm Adreno 225 - d->surfaceFormat.setDepthBufferSize(-1); - d->surfaceFormat.setStencilBufferSize(-1); - } - } - } - } - } - } + d->cursor.reset(new QWinRTCursor); - d->eglConfig = q_configFromGLFormat(d->eglDisplay, d->surfaceFormat); - d->surfaceFormat = q_glFormatFromConfig(d->eglDisplay, d->eglConfig, d->surfaceFormat); - d->eglSurface = eglCreateWindowSurface(d->eglDisplay, d->eglConfig, d->coreWindow.Get(), NULL); - if (d->eglSurface == EGL_NO_SURFACE) - qCritical("Failed to create EGL window surface: 0x%x", eglGetError()); +#ifdef Q_OS_WINPHONE + ComPtr<IStatusBarStatics> statusBarStatics; + hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_UI_ViewManagement_StatusBar).Get(), + IID_PPV_ARGS(&statusBarStatics)); + Q_ASSERT_SUCCEEDED(hr); + hr = statusBarStatics->GetForCurrentView(&d->statusBar); + Q_ASSERT_SUCCEEDED(hr); +#endif // Q_OS_WINPHONE } QWinRTScreen::~QWinRTScreen() @@ -629,16 +555,26 @@ QWinRTScreen::~QWinRTScreen() Q_D(QWinRTScreen); // Unregister callbacks - for (QHash<CoreApplicationCallbackRemover, EventRegistrationToken>::const_iterator i = d->applicationTokens.begin(); i != d->applicationTokens.end(); ++i) - (d->application.Get()->*i.key())(i.value()); - for (QHash<CoreWindowCallbackRemover, EventRegistrationToken>::const_iterator i = d->windowTokens.begin(); i != d->windowTokens.end(); ++i) - (d->coreWindow.Get()->*i.key())(i.value()); - for (QHash<DisplayCallbackRemover, EventRegistrationToken>::const_iterator i = d->displayTokens.begin(); i != d->displayTokens.end(); ++i) - (d->displayInformation.Get()->*i.key())(i.value()); + HRESULT hr; + hr = QEventDispatcherWinRT::runOnXamlThread([this, d]() { + HRESULT hr; + for (QHash<CoreWindowCallbackRemover, EventRegistrationToken>::const_iterator i = d->windowTokens.begin(); i != d->windowTokens.end(); ++i) { + hr = (d->coreWindow.Get()->*i.key())(i.value()); + Q_ASSERT_SUCCEEDED(hr); + } + for (QHash<DisplayCallbackRemover, EventRegistrationToken>::const_iterator i = d->displayTokens.begin(); i != d->displayTokens.end(); ++i) { + hr = (d->displayInformation.Get()->*i.key())(i.value()); + Q_ASSERT_SUCCEEDED(hr); + } #ifdef Q_OS_WINPHONE - for (QHash<HardwareButtonsCallbackRemover, EventRegistrationToken>::const_iterator i = d->buttonsTokens.begin(); i != d->buttonsTokens.end(); ++i) - (d->hardwareButtons.Get()->*i.key())(i.value()); -#endif + for (QHash<StatusBarCallbackRemover, EventRegistrationToken>::const_iterator i = d->statusBarTokens.begin(); i != d->statusBarTokens.end(); ++i) { + hr = (d->statusBar.Get()->*i.key())(i.value()); + Q_ASSERT_SUCCEEDED(hr); + } +#endif //Q_OS_WINPHONE + return hr; + }); + RETURN_VOID_IF_FAILED("Failed to unregister screen event callbacks"); } QRect QWinRTScreen::geometry() const @@ -647,6 +583,31 @@ QRect QWinRTScreen::geometry() const return QRect(QPoint(), (d->logicalSize * d->scaleFactor).toSize()); } +#ifdef Q_OS_WINPHONE +QRect QWinRTScreen::availableGeometry() const +{ + Q_D(const QWinRTScreen); + QRect statusBar; + QEventDispatcherWinRT::runOnXamlThread([d, &statusBar]() { + HRESULT hr; + Rect rect; + hr = d->statusBar->get_OccludedRect(&rect); + Q_ASSERT_SUCCEEDED(hr); + statusBar.setRect(qRound(rect.X * d->scaleFactor), + qRound(rect.Y * d->scaleFactor), + qRound(rect.Width * d->scaleFactor), + qRound(rect.Height * d->scaleFactor)); + return S_OK; + }); + + return geometry().adjusted( + d->orientation == Qt::LandscapeOrientation ? statusBar.width() : 0, + d->orientation == Qt::PortraitOrientation ? statusBar.height() : 0, + d->orientation == Qt::InvertedLandscapeOrientation ? -statusBar.width() : 0, + 0); +} +#endif //Q_OS_WINPHONE + int QWinRTScreen::depth() const { return 32; @@ -657,12 +618,6 @@ QImage::Format QWinRTScreen::format() const return QImage::Format_ARGB32_Premultiplied; } -QSurfaceFormat QWinRTScreen::surfaceFormat() const -{ - Q_D(const QWinRTScreen); - return d->surfaceFormat; -} - QSizeF QWinRTScreen::physicalSize() const { Q_D(const QWinRTScreen); @@ -682,21 +637,9 @@ qreal QWinRTScreen::scaleFactor() const return d->scaleFactor; } -QWinRTInputContext *QWinRTScreen::inputContext() const -{ - Q_D(const QWinRTScreen); -#ifdef Q_OS_WINPHONE - return d->inputContext.data(); -#else - return d->inputContext.Get(); -#endif -} - QPlatformCursor *QWinRTScreen::cursor() const { Q_D(const QWinRTScreen); - if (!d->cursor) - const_cast<QWinRTScreenPrivate *>(d)->cursor.reset(new QWinRTCursor); return d->cursor.data(); } @@ -744,22 +687,76 @@ ICoreWindow *QWinRTScreen::coreWindow() const return d->coreWindow.Get(); } -EGLDisplay QWinRTScreen::eglDisplay() const +Xaml::IDependencyObject *QWinRTScreen::canvas() const { Q_D(const QWinRTScreen); - return d->eglDisplay; + return d->canvas.Get(); } -EGLSurface QWinRTScreen::eglSurface() const +#ifdef Q_OS_WINPHONE +void QWinRTScreen::setStatusBarVisibility(bool visible, QWindow *window) { - Q_D(const QWinRTScreen); - return d->eglSurface; + Q_D(QWinRTScreen); + const Qt::WindowFlags windowType = window->flags() & Qt::WindowType_Mask; + if (!window || (windowType != Qt::Window && windowType != Qt::Dialog)) + return; + + QEventDispatcherWinRT::runOnXamlThread([d, visible]() { + HRESULT hr; + ComPtr<IAsyncAction> op; + if (visible) + hr = d->statusBar->ShowAsync(&op); + else + hr = d->statusBar->HideAsync(&op); + Q_ASSERT_SUCCEEDED(hr); + return S_OK; + }); } +#endif //Q_OS_WINPHONE -EGLConfig QWinRTScreen::eglConfig() const +void QWinRTScreen::initialize() { - Q_D(const QWinRTScreen); - return d->eglConfig; + Q_D(QWinRTScreen); + HRESULT hr; + hr = d->coreWindow->add_KeyDown(Callback<KeyHandler>(this, &QWinRTScreen::onKeyDown).Get(), &d->windowTokens[&ICoreWindow::remove_KeyDown]); + Q_ASSERT_SUCCEEDED(hr); + hr = d->coreWindow->add_KeyUp(Callback<KeyHandler>(this, &QWinRTScreen::onKeyUp).Get(), &d->windowTokens[&ICoreWindow::remove_KeyUp]); + Q_ASSERT_SUCCEEDED(hr); + hr = d->coreWindow->add_CharacterReceived(Callback<CharacterReceivedHandler>(this, &QWinRTScreen::onCharacterReceived).Get(), &d->windowTokens[&ICoreWindow::remove_CharacterReceived]); + Q_ASSERT_SUCCEEDED(hr); + hr = d->coreWindow->add_PointerEntered(Callback<PointerHandler>(this, &QWinRTScreen::onPointerEntered).Get(), &d->windowTokens[&ICoreWindow::remove_PointerEntered]); + Q_ASSERT_SUCCEEDED(hr); + hr = d->coreWindow->add_PointerExited(Callback<PointerHandler>(this, &QWinRTScreen::onPointerExited).Get(), &d->windowTokens[&ICoreWindow::remove_PointerExited]); + Q_ASSERT_SUCCEEDED(hr); + hr = d->coreWindow->add_PointerMoved(Callback<PointerHandler>(this, &QWinRTScreen::onPointerUpdated).Get(), &d->windowTokens[&ICoreWindow::remove_PointerMoved]); + Q_ASSERT_SUCCEEDED(hr); + hr = d->coreWindow->add_PointerPressed(Callback<PointerHandler>(this, &QWinRTScreen::onPointerUpdated).Get(), &d->windowTokens[&ICoreWindow::remove_PointerPressed]); + Q_ASSERT_SUCCEEDED(hr); + hr = d->coreWindow->add_PointerReleased(Callback<PointerHandler>(this, &QWinRTScreen::onPointerUpdated).Get(), &d->windowTokens[&ICoreWindow::remove_PointerReleased]); + Q_ASSERT_SUCCEEDED(hr); + hr = d->coreWindow->add_PointerWheelChanged(Callback<PointerHandler>(this, &QWinRTScreen::onPointerUpdated).Get(), &d->windowTokens[&ICoreWindow::remove_PointerWheelChanged]); + Q_ASSERT_SUCCEEDED(hr); +#ifndef Q_OS_WINPHONE + hr = d->coreWindow->add_SizeChanged(Callback<SizeChangedHandler>(this, &QWinRTScreen::onSizeChanged).Get(), &d->windowTokens[&ICoreWindow::remove_SizeChanged]); + Q_ASSERT_SUCCEEDED(hr); +#else + hr = d->statusBar->add_Showing(Callback<StatusBarHandler>(this, &QWinRTScreen::onStatusBarShowing).Get(), &d->statusBarTokens[&IStatusBar::remove_Showing]); + Q_ASSERT_SUCCEEDED(hr); + hr = d->statusBar->add_Hiding(Callback<StatusBarHandler>(this, &QWinRTScreen::onStatusBarHiding).Get(), &d->statusBarTokens[&IStatusBar::remove_Hiding]); + Q_ASSERT_SUCCEEDED(hr); +#endif + hr = d->coreWindow->add_Activated(Callback<ActivatedHandler>(this, &QWinRTScreen::onActivated).Get(), &d->windowTokens[&ICoreWindow::remove_Activated]); + Q_ASSERT_SUCCEEDED(hr); + hr = d->coreWindow->add_Closed(Callback<ClosedHandler>(this, &QWinRTScreen::onClosed).Get(), &d->windowTokens[&ICoreWindow::remove_Closed]); + Q_ASSERT_SUCCEEDED(hr); + hr = d->coreWindow->add_VisibilityChanged(Callback<VisibilityChangedHandler>(this, &QWinRTScreen::onVisibilityChanged).Get(), &d->windowTokens[&ICoreWindow::remove_VisibilityChanged]); + Q_ASSERT_SUCCEEDED(hr); + hr = d->displayInformation->add_OrientationChanged(Callback<DisplayInformationHandler>(this, &QWinRTScreen::onOrientationChanged).Get(), &d->displayTokens[&IDisplayInformation::remove_OrientationChanged]); + Q_ASSERT_SUCCEEDED(hr); + hr = d->displayInformation->add_DpiChanged(Callback<DisplayInformationHandler>(this, &QWinRTScreen::onDpiChanged).Get(), &d->displayTokens[&IDisplayInformation::remove_DpiChanged]); + Q_ASSERT_SUCCEEDED(hr); + onOrientationChanged(Q_NULLPTR, Q_NULLPTR); + onVisibilityChanged(nullptr, nullptr); } QWindow *QWinRTScreen::topWindow() const @@ -773,6 +770,12 @@ void QWinRTScreen::addWindow(QWindow *window) Q_D(QWinRTScreen); if (window == topWindow()) return; + +#ifdef Q_OS_WINPHONE + if (window->visibility() != QWindow::Maximized && window->visibility() != QWindow::Windowed) + setStatusBarVisibility(false, window); +#endif + d->visibleWindows.prepend(window); QWindowSystemInterface::handleWindowActivated(window, Qt::OtherFocusReason); handleExpose(); @@ -781,6 +784,12 @@ void QWinRTScreen::addWindow(QWindow *window) void QWinRTScreen::removeWindow(QWindow *window) { Q_D(QWinRTScreen); + +#ifdef Q_OS_WINPHONE + if (window->visibility() == QWindow::Minimized) + setStatusBarVisibility(false, window); +#endif + const bool wasTopWindow = window == topWindow(); if (!d->visibleWindows.removeAll(window)) return; @@ -809,6 +818,20 @@ void QWinRTScreen::lower(QWindow *window) handleExpose(); } +void QWinRTScreen::updateWindowTitle() +{ + Q_D(QWinRTScreen); + + QWindow *window = topWindow(); + if (!window) + return; + + const QString title = window->title(); + HStringReference titleRef(reinterpret_cast<LPCWSTR>(title.utf16()), title.length()); + HRESULT hr = d->view->put_Title(titleRef.Get()); + RETURN_VOID_IF_FAILED("Unable to set window title"); +} + void QWinRTScreen::handleExpose() { Q_D(QWinRTScreen); @@ -822,57 +845,94 @@ void QWinRTScreen::handleExpose() HRESULT QWinRTScreen::onKeyDown(ABI::Windows::UI::Core::ICoreWindow *, ABI::Windows::UI::Core::IKeyEventArgs *args) { + Q_D(QWinRTScreen); VirtualKey virtualKey; - args->get_VirtualKey(&virtualKey); + HRESULT hr = args->get_VirtualKey(&virtualKey); + Q_ASSERT_SUCCEEDED(hr); + CorePhysicalKeyStatus status; + hr = args->get_KeyStatus(&status); + Q_ASSERT_SUCCEEDED(hr); + Qt::Key key = qKeyFromVirtual(virtualKey); // Defer character key presses to onCharacterReceived - if (key == Qt::Key_unknown || (key >= Qt::Key_Space && key <= Qt::Key_ydiaeresis)) + if (key == Qt::Key_unknown || (key >= Qt::Key_Space && key <= Qt::Key_ydiaeresis)) { + d->activeKeys.insert(key, KeyInfo(virtualKey)); return S_OK; - QWindowSystemInterface::handleKeyEvent(topWindow(), QEvent::KeyPress, key, keyboardModifiers()); + } + + QWindowSystemInterface::handleExtendedKeyEvent( + topWindow(), + QEvent::KeyPress, + key, + keyboardModifiers(), + !status.ScanCode ? -1 : status.ScanCode, + virtualKey, + 0, + QString(), + status.RepeatCount > 1, + !status.RepeatCount ? 1 : status.RepeatCount, + false); return S_OK; } HRESULT QWinRTScreen::onKeyUp(ABI::Windows::UI::Core::ICoreWindow *, ABI::Windows::UI::Core::IKeyEventArgs *args) { - Qt::KeyboardModifiers mods = keyboardModifiers(); -#ifndef Q_OS_WINPHONE Q_D(QWinRTScreen); - CorePhysicalKeyStatus status; // Look for a pressed character key - if (SUCCEEDED(args->get_KeyStatus(&status)) && d->activeKeys.contains(status.ScanCode)) { - QPair<Qt::Key, QString> keyStatus = d->activeKeys.take(status.ScanCode); - QWindowSystemInterface::handleKeyEvent(topWindow(), QEvent::KeyRelease, - keyStatus.first, mods, keyStatus.second); - return S_OK; - } -#endif // !Q_OS_WINPHONE VirtualKey virtualKey; - args->get_VirtualKey(&virtualKey); - QWindowSystemInterface::handleKeyEvent(topWindow(), QEvent::KeyRelease, - qKeyFromVirtual(virtualKey), mods); + HRESULT hr = args->get_VirtualKey(&virtualKey); + Q_ASSERT_SUCCEEDED(hr); + CorePhysicalKeyStatus status; + hr = args->get_KeyStatus(&status); + Q_ASSERT_SUCCEEDED(hr); + + Qt::Key key = qKeyFromVirtual(virtualKey); + const KeyInfo info = d->activeKeys.take(key); + QWindowSystemInterface::handleExtendedKeyEvent( + topWindow(), + QEvent::KeyRelease, + key, + keyboardModifiers(), + !status.ScanCode ? -1 : status.ScanCode, + virtualKey, + 0, + info.text, + status.RepeatCount > 1, + !status.RepeatCount ? 1 : status.RepeatCount, + false); return S_OK; } HRESULT QWinRTScreen::onCharacterReceived(ICoreWindow *, ICharacterReceivedEventArgs *args) { + Q_D(QWinRTScreen); quint32 keyCode; - args->get_KeyCode(&keyCode); + HRESULT hr = args->get_KeyCode(&keyCode); + Q_ASSERT_SUCCEEDED(hr); + CorePhysicalKeyStatus status; + hr = args->get_KeyStatus(&status); + Q_ASSERT_SUCCEEDED(hr); + // Don't generate character events for non-printables; the meta key stage is enough if (qIsNonPrintable(keyCode)) return S_OK; - Qt::KeyboardModifiers mods = keyboardModifiers(); - Qt::Key key = qKeyFromCode(keyCode, mods); - QString text = QChar(keyCode); - QWindowSystemInterface::handleKeyEvent(topWindow(), QEvent::KeyPress, key, mods, text); -#ifndef Q_OS_WINPHONE - Q_D(QWinRTScreen); - CorePhysicalKeyStatus status; // Defer release to onKeyUp for physical keys - if (SUCCEEDED(args->get_KeyStatus(&status)) && !status.IsKeyReleased) { - d->activeKeys.insert(status.ScanCode, qMakePair(key, text)); - return S_OK; - } -#endif // !Q_OS_WINPHONE - QWindowSystemInterface::handleKeyEvent(topWindow(), QEvent::KeyRelease, key, mods, text); + const Qt::KeyboardModifiers modifiers = keyboardModifiers(); + const Qt::Key key = qKeyFromCode(keyCode, modifiers); + const QString text = QChar(keyCode); + const quint32 virtualKey = d->activeKeys.value(key).virtualKey; + QWindowSystemInterface::handleExtendedKeyEvent( + topWindow(), + QEvent::KeyPress, + key, + modifiers, + !status.ScanCode ? -1 : status.ScanCode, + virtualKey, + 0, + text, + status.RepeatCount > 1, + !status.RepeatCount ? 1 : status.RepeatCount, + false); + d->activeKeys.insert(key, KeyInfo(text, virtualKey)); return S_OK; } @@ -909,6 +969,11 @@ HRESULT QWinRTScreen::onPointerUpdated(ICoreWindow *, IPointerEventArgs *args) Point point; pointerPoint->get_Position(&point); QPointF pos(point.X * d->scaleFactor, point.Y * d->scaleFactor); + QPointF localPos = pos; + if (topWindow()) { + const QPointF globalPosDelta = pos - pos.toPoint(); + localPos = topWindow()->mapFromGlobal(pos.toPoint()) + globalPosDelta; + } VirtualKeyModifiers modifiers; args->get_KeyModifiers(&modifiers); @@ -942,7 +1007,7 @@ HRESULT QWinRTScreen::onPointerUpdated(ICoreWindow *, IPointerEventArgs *args) boolean isHorizontal; properties->get_IsHorizontalMouseWheel(&isHorizontal); QPoint angleDelta(isHorizontal ? delta : 0, isHorizontal ? 0 : delta); - QWindowSystemInterface::handleWheelEvent(topWindow(), pos, pos, QPoint(), angleDelta, mods); + QWindowSystemInterface::handleWheelEvent(topWindow(), localPos, pos, QPoint(), angleDelta, mods); break; } @@ -968,7 +1033,7 @@ HRESULT QWinRTScreen::onPointerUpdated(ICoreWindow *, IPointerEventArgs *args) if (isPressed) buttons |= Qt::XButton2; - QWindowSystemInterface::handleMouseEvent(topWindow(), pos, pos, buttons, mods); + QWindowSystemInterface::handleMouseEvent(topWindow(), localPos, pos, buttons, mods); break; } @@ -1055,17 +1120,6 @@ HRESULT QWinRTScreen::onPointerUpdated(ICoreWindow *, IPointerEventArgs *args) return S_OK; } -HRESULT QWinRTScreen::onAutomationProviderRequested(ICoreWindow *, IAutomationProviderRequestedEventArgs *args) -{ -#ifndef Q_OS_WINPHONE - Q_D(const QWinRTScreen); - args->put_AutomationProvider(d->inputContext.Get()); -#else - Q_UNUSED(args) -#endif - return S_OK; -} - HRESULT QWinRTScreen::onSizeChanged(ICoreWindow *, IWindowSizeChangedEventArgs *) { Q_D(QWinRTScreen); @@ -1074,19 +1128,10 @@ HRESULT QWinRTScreen::onSizeChanged(ICoreWindow *, IWindowSizeChangedEventArgs * HRESULT hr; hr = d->coreWindow->get_Bounds(&size); RETURN_OK_IF_FAILED("Failed to get window bounds"); - QSizeF logicalSize = QSizeF(size.Width, size.Height); -#ifndef Q_OS_WINPHONE // This handler is called from orientation changed, in which case we should always update the size - if (d->logicalSize == logicalSize) - return S_OK; -#endif - - d->logicalSize = logicalSize; - if (d->eglDisplay) { - const QRect newGeometry = geometry(); - QWindowSystemInterface::handleScreenGeometryChange(screen(), newGeometry, newGeometry); - QPlatformScreen::resizeMaximizedWindows(); - handleExpose(); - } + d->logicalSize = QSizeF(size.Width, size.Height); + QWindowSystemInterface::handleScreenGeometryChange(screen(), geometry(), availableGeometry()); + QPlatformScreen::resizeMaximizedWindows(); + handleExpose(); return S_OK; } @@ -1110,31 +1155,6 @@ HRESULT QWinRTScreen::onActivated(ICoreWindow *, IWindowActivatedEventArgs *args return S_OK; } -HRESULT QWinRTScreen::onSuspended(IInspectable *, ISuspendingEventArgs *) -{ -#ifndef Q_OS_WINPHONE - Q_D(QWinRTScreen); - ComPtr<ID3D11Device> d3dDevice; - const EGLBoolean ok = eglQuerySurfacePointerANGLE(d->eglDisplay, d->eglSurface, EGL_DEVICE_EXT, (void **)d3dDevice.GetAddressOf()); - if (ok && d3dDevice) { - ComPtr<IDXGIDevice3> dxgiDevice; - if (SUCCEEDED(d3dDevice.As(&dxgiDevice))) - dxgiDevice->Trim(); - } -#endif - QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationSuspended); - QWindowSystemInterface::flushWindowSystemEvents(); - return S_OK; -} - -HRESULT QWinRTScreen::onResume(IInspectable *, IInspectable *) -{ - // First the system invokes onResume and then changes - // the visibility of the screen to be active. - QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationHidden); - return S_OK; -} - HRESULT QWinRTScreen::onClosed(ICoreWindow *, ICoreWindowEventArgs *) { foreach (QWindow *w, QGuiApplication::topLevelWindows()) @@ -1144,8 +1164,10 @@ HRESULT QWinRTScreen::onClosed(ICoreWindow *, ICoreWindowEventArgs *) HRESULT QWinRTScreen::onVisibilityChanged(ICoreWindow *, IVisibilityChangedEventArgs *args) { + Q_D(QWinRTScreen); boolean visible; - args->get_Visible(&visible); + HRESULT hr = args ? args->get_Visible(&visible) : d->coreWindow->get_Visible(&visible); + RETURN_OK_IF_FAILED("Failed to get visbile."); QWindowSystemInterface::handleApplicationStateChanged(visible ? Qt::ApplicationActive : Qt::ApplicationHidden); if (visible) handleExpose(); @@ -1163,12 +1185,12 @@ HRESULT QWinRTScreen::onOrientationChanged(IDisplayInformation *, IInspectable * Qt::ScreenOrientation newOrientation = static_cast<Qt::ScreenOrientation>(static_cast<int>(qtOrientationsFromNative(displayOrientation))); if (d->orientation != newOrientation) { d->orientation = newOrientation; +#ifdef Q_OS_WINPHONE + onSizeChanged(nullptr, nullptr); +#endif QWindowSystemInterface::handleScreenOrientationChange(screen(), d->orientation); + handleExpose(); // Clean broken frames caused by race between Qt and ANGLE } - -#ifdef Q_OS_WINPHONE // The size changed handler is ignored in favor of this callback - onSizeChanged(Q_NULLPTR, Q_NULLPTR); -#endif return S_OK; } @@ -1206,26 +1228,17 @@ HRESULT QWinRTScreen::onDpiChanged(IDisplayInformation *, IInspectable *) } #ifdef Q_OS_WINPHONE -HRESULT QWinRTScreen::onBackButtonPressed(IInspectable *, IBackPressedEventArgs *args) +HRESULT QWinRTScreen::onStatusBarShowing(IStatusBar *, IInspectable *) { - Q_D(QWinRTScreen); - - QKeyEvent backPress(QEvent::KeyPress, Qt::Key_Back, Qt::NoModifier); - QKeyEvent backRelease(QEvent::KeyRelease, Qt::Key_Back, Qt::NoModifier); - backPress.setAccepted(false); - backRelease.setAccepted(false); - - QObject *receiver = d->visibleWindows.isEmpty() - ? static_cast<QObject *>(QGuiApplication::instance()) - : static_cast<QObject *>(d->visibleWindows.first()); - - // If the event is ignored, the app will suspend - QGuiApplication::sendEvent(receiver, &backPress); - QGuiApplication::sendEvent(receiver, &backRelease); - args->put_Handled(backPress.isAccepted() || backRelease.isAccepted()); + onSizeChanged(nullptr, nullptr); + return S_OK; +} +HRESULT QWinRTScreen::onStatusBarHiding(IStatusBar *, IInspectable *) +{ + onSizeChanged(nullptr, nullptr); return S_OK; } -#endif // Q_OS_WINPHONE +#endif //Q_OS_WINPHONE QT_END_NAMESPACE diff --git a/src/plugins/platforms/winrt/qwinrtscreen.h b/src/plugins/platforms/winrt/qwinrtscreen.h index d34ce75748..0043b2cfa3 100644 --- a/src/plugins/platforms/winrt/qwinrtscreen.h +++ b/src/plugins/platforms/winrt/qwinrtscreen.h @@ -40,8 +40,6 @@ #include <qpa/qplatformscreen.h> #include <qpa/qwindowsysteminterface.h> -#include <EGL/egl.h> - namespace ABI { namespace Windows { namespace ApplicationModel { @@ -59,21 +57,19 @@ namespace ABI { struct IWindowActivatedEventArgs; struct IWindowSizeChangedEventArgs; } + namespace Xaml { + struct IDependencyObject; + struct IWindow; + } + namespace ViewManagement { + struct IStatusBar; + } } namespace Graphics { namespace Display { struct IDisplayInformation; } } -#ifdef Q_OS_WINPHONE - namespace Phone { - namespace UI { - namespace Input { - struct IBackPressedEventArgs; - } - } - } -#endif } } struct IInspectable; @@ -81,7 +77,6 @@ struct IInspectable; QT_BEGIN_NAMESPACE class QTouchDevice; -class QWinRTEGLContext; class QWinRTCursor; class QWinRTInputContext; class QWinRTScreenPrivate; @@ -90,19 +85,20 @@ class QWinRTScreen : public QPlatformScreen public: explicit QWinRTScreen(); ~QWinRTScreen(); - QRect geometry() const; - int depth() const; - QImage::Format format() const; - QSurfaceFormat surfaceFormat() const; + QRect geometry() const Q_DECL_OVERRIDE; +#ifdef Q_OS_WINPHONE + QRect availableGeometry() const Q_DECL_OVERRIDE; +#endif + int depth() const Q_DECL_OVERRIDE; + QImage::Format format() const Q_DECL_OVERRIDE; QSizeF physicalSize() const Q_DECL_OVERRIDE; QDpi logicalDpi() const Q_DECL_OVERRIDE; qreal scaleFactor() const; - QWinRTInputContext *inputContext() const; - QPlatformCursor *cursor() const; + QPlatformCursor *cursor() const Q_DECL_OVERRIDE; Qt::KeyboardModifiers keyboardModifiers() const; - Qt::ScreenOrientation nativeOrientation() const; - Qt::ScreenOrientation orientation() const; + Qt::ScreenOrientation nativeOrientation() const Q_DECL_OVERRIDE; + Qt::ScreenOrientation orientation() const Q_DECL_OVERRIDE; QWindow *topWindow() const; void addWindow(QWindow *window); @@ -110,10 +106,16 @@ public: void raise(QWindow *window); void lower(QWindow *window); + void updateWindowTitle(); + ABI::Windows::UI::Core::ICoreWindow *coreWindow() const; - EGLDisplay eglDisplay() const; // To opengl context - EGLSurface eglSurface() const; // To window - EGLConfig eglConfig() const; + ABI::Windows::UI::Xaml::IDependencyObject *canvas() const; + +#ifdef Q_OS_WINPHONE + void setStatusBarVisibility(bool visible, QWindow *window); +#endif + + void initialize(); private: void handleExpose(); @@ -127,18 +129,16 @@ private: HRESULT onSizeChanged(ABI::Windows::UI::Core::ICoreWindow *, ABI::Windows::UI::Core::IWindowSizeChangedEventArgs *); HRESULT onActivated(ABI::Windows::UI::Core::ICoreWindow *, ABI::Windows::UI::Core::IWindowActivatedEventArgs *); - HRESULT onSuspended(IInspectable *, ABI::Windows::ApplicationModel::ISuspendingEventArgs *); - HRESULT onResume(IInspectable *, IInspectable *); HRESULT onClosed(ABI::Windows::UI::Core::ICoreWindow *, ABI::Windows::UI::Core::ICoreWindowEventArgs *); HRESULT onVisibilityChanged(ABI::Windows::UI::Core::ICoreWindow *, ABI::Windows::UI::Core::IVisibilityChangedEventArgs *); - HRESULT onAutomationProviderRequested(ABI::Windows::UI::Core::ICoreWindow *, ABI::Windows::UI::Core::IAutomationProviderRequestedEventArgs *); HRESULT onOrientationChanged(ABI::Windows::Graphics::Display::IDisplayInformation *, IInspectable *); HRESULT onDpiChanged(ABI::Windows::Graphics::Display::IDisplayInformation *, IInspectable *); #ifdef Q_OS_WINPHONE - HRESULT onBackButtonPressed(IInspectable *, ABI::Windows::Phone::UI::Input::IBackPressedEventArgs *args); + HRESULT onStatusBarShowing(ABI::Windows::UI::ViewManagement::IStatusBar *, IInspectable *); + HRESULT onStatusBarHiding(ABI::Windows::UI::ViewManagement::IStatusBar *, IInspectable *); #endif QScopedPointer<QWinRTScreenPrivate> d_ptr; diff --git a/src/plugins/platforms/winrt/qwinrttheme.cpp b/src/plugins/platforms/winrt/qwinrttheme.cpp index a0fa2798a8..7d09551f5b 100644 --- a/src/plugins/platforms/winrt/qwinrttheme.cpp +++ b/src/plugins/platforms/winrt/qwinrttheme.cpp @@ -44,7 +44,13 @@ #include <wrl.h> #include <windows.ui.h> #include <windows.ui.viewmanagement.h> +#if _MSC_VER >= 1900 +#include <windows.foundation.metadata.h> +using namespace ABI::Windows::Foundation::Metadata; +#endif + using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; using namespace ABI::Windows::UI; using namespace ABI::Windows::UI::ViewManagement; @@ -73,102 +79,215 @@ static inline QColor fromColor(const Color &color) return QColor(color.R, color.G, color.B, color.A); } -QWinRTTheme::QWinRTTheme() - : d_ptr(new QWinRTThemePrivate) +#if _MSC_VER >= 1900 +static bool uiColorSettings(const wchar_t *value, UIElementType type, Color *color) { - Q_D(QWinRTTheme); + static ComPtr<IApiInformationStatics> apiInformationStatics; + HRESULT hr; + if (!apiInformationStatics) { + hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Foundation_Metadata_ApiInformation).Get(), + IID_PPV_ARGS(&apiInformationStatics)); + RETURN_FALSE_IF_FAILED("Could not get ApiInformationStatics"); + } + + static const HStringReference enumRef(L"Windows.UI.ViewManagement.UIElementType"); + HStringReference valueRef(value); + + boolean exists; + hr = apiInformationStatics->IsEnumNamedValuePresent(enumRef.Get(), valueRef.Get(), &exists); + + if (hr != S_OK || !exists) + return false; + + return SUCCEEDED(uiSettings()->UIElementColor(type, color)); +} + +static void nativeColorSettings(QPalette &p) +{ + Color color; + + if (uiColorSettings(L"ActiveCaption", UIElementType_ActiveCaption, &color)) + p.setColor(QPalette::ToolTipBase, fromColor(color)); + + if (uiColorSettings(L"Background", UIElementType_Background, &color)) + p.setColor(QPalette::AlternateBase, fromColor(color)); + + if (uiColorSettings(L"ButtonFace", UIElementType_ButtonFace, &color)) { + p.setColor(QPalette::Button, fromColor(color)); + p.setColor(QPalette::Midlight, fromColor(color).lighter(110)); + p.setColor(QPalette::Light, fromColor(color).lighter(150)); + p.setColor(QPalette::Mid, fromColor(color).dark(130)); + p.setColor(QPalette::Dark, fromColor(color).dark(150)); + } + + if (uiColorSettings(L"ButtonText", UIElementType_ButtonText, &color)) { + p.setColor(QPalette::ButtonText, fromColor(color)); + p.setColor(QPalette::Text, fromColor(color)); + } + + if (uiColorSettings(L"CaptionText", UIElementType_CaptionText, &color)) + p.setColor(QPalette::ToolTipText, fromColor(color)); + + if (uiColorSettings(L"Highlight", UIElementType_Highlight, &color)) + p.setColor(QPalette::Highlight, fromColor(color)); + + if (uiColorSettings(L"HighlightText", UIElementType_HighlightText, &color)) + p.setColor(QPalette::HighlightedText, fromColor(color)); + + if (uiColorSettings(L"Window", UIElementType_Window, &color)) { + p.setColor(QPalette::Window, fromColor(color)); + p.setColor(QPalette::Base, fromColor(color)); + } + + if (uiColorSettings(L"Hotlight", UIElementType_Hotlight, &color)) + p.setColor(QPalette::BrightText, fromColor(color)); + //Phone related + if (uiColorSettings(L"PopupBackground", UIElementType_PopupBackground, &color)) { + p.setColor(QPalette::ToolTipBase, fromColor(color)); + p.setColor(QPalette::AlternateBase, fromColor(color)); + } + + if (uiColorSettings(L"NonTextMedium", UIElementType_NonTextMedium, &color)) + p.setColor(QPalette::Button, fromColor(color)); + + if (uiColorSettings(L"NonTextMediumHigh", UIElementType_NonTextMediumHigh, &color)) + p.setColor(QPalette::Midlight, fromColor(color)); + + if (uiColorSettings(L"NonTextHigh", UIElementType_NonTextHigh, &color)) + p.setColor(QPalette::Light, fromColor(color)); + + if (uiColorSettings(L"NonTextMediumLow", UIElementType_NonTextMediumLow, &color)) + p.setColor(QPalette::Mid, fromColor(color)); + + if (uiColorSettings(L"NonTextLow", UIElementType_NonTextLow, &color)) + p.setColor(QPalette::Dark, fromColor(color)); + + if (uiColorSettings(L"TextHigh", UIElementType_TextHigh, &color)) { + p.setColor(QPalette::ButtonText, fromColor(color)); + p.setColor(QPalette::Text, fromColor(color)); + p.setColor(QPalette::WindowText, fromColor(color)); + } + + if (uiColorSettings(L"TextMedium", UIElementType_TextMedium, &color)) + p.setColor(QPalette::ToolTipText, fromColor(color)); + + if (uiColorSettings(L"AccentColor", UIElementType_AccentColor, &color)) + p.setColor(QPalette::Highlight, fromColor(color)); + + if (uiColorSettings(L"PageBackground", UIElementType_PageBackground, &color)) { + p.setColor(QPalette::Window, fromColor(color)); + p.setColor(QPalette::Base, fromColor(color)); + } + + if (uiColorSettings(L"TextContrastWithHigh", UIElementType_TextContrastWithHigh, &color)) + p.setColor(QPalette::BrightText, fromColor(color)); +} + +#else // _MSC_VER >= 1900 + +static void nativeColorSettings(QPalette &p) +{ HRESULT hr; Color color; #ifdef Q_OS_WINPHONE hr = uiSettings()->UIElementColor(UIElementType_PopupBackground, &color); Q_ASSERT_SUCCEEDED(hr); - d->palette.setColor(QPalette::ToolTipBase, fromColor(color)); - d->palette.setColor(QPalette::AlternateBase, fromColor(color)); + p.setColor(QPalette::ToolTipBase, fromColor(color)); + p.setColor(QPalette::AlternateBase, fromColor(color)); hr = uiSettings()->UIElementColor(UIElementType_NonTextMedium, &color); Q_ASSERT_SUCCEEDED(hr); - d->palette.setColor(QPalette::Button, fromColor(color)); + p.setColor(QPalette::Button, fromColor(color)); hr = uiSettings()->UIElementColor(UIElementType_NonTextMediumHigh, &color); Q_ASSERT_SUCCEEDED(hr); - d->palette.setColor(QPalette::Midlight, fromColor(color)); + p.setColor(QPalette::Midlight, fromColor(color)); hr = uiSettings()->UIElementColor(UIElementType_NonTextHigh, &color); Q_ASSERT_SUCCEEDED(hr); - d->palette.setColor(QPalette::Light, fromColor(color)); + p.setColor(QPalette::Light, fromColor(color)); hr = uiSettings()->UIElementColor(UIElementType_NonTextMediumLow, &color); Q_ASSERT_SUCCEEDED(hr); - d->palette.setColor(QPalette::Mid, fromColor(color)); + p.setColor(QPalette::Mid, fromColor(color)); hr = uiSettings()->UIElementColor(UIElementType_NonTextLow, &color); Q_ASSERT_SUCCEEDED(hr); - d->palette.setColor(QPalette::Dark, fromColor(color)); + p.setColor(QPalette::Dark, fromColor(color)); hr = uiSettings()->UIElementColor(UIElementType_TextHigh, &color); Q_ASSERT_SUCCEEDED(hr); - d->palette.setColor(QPalette::ButtonText, fromColor(color)); - d->palette.setColor(QPalette::Text, fromColor(color)); - d->palette.setColor(QPalette::WindowText, fromColor(color)); + p.setColor(QPalette::ButtonText, fromColor(color)); + p.setColor(QPalette::Text, fromColor(color)); + p.setColor(QPalette::WindowText, fromColor(color)); hr = uiSettings()->UIElementColor(UIElementType_TextMedium, &color); Q_ASSERT_SUCCEEDED(hr); - d->palette.setColor(QPalette::ToolTipText, fromColor(color)); + p.setColor(QPalette::ToolTipText, fromColor(color)); hr = uiSettings()->UIElementColor(UIElementType_AccentColor, &color); Q_ASSERT_SUCCEEDED(hr); - d->palette.setColor(QPalette::Highlight, fromColor(color)); + p.setColor(QPalette::Highlight, fromColor(color)); hr = uiSettings()->UIElementColor(UIElementType_PageBackground, &color); Q_ASSERT_SUCCEEDED(hr); - d->palette.setColor(QPalette::Window, fromColor(color)); - d->palette.setColor(QPalette::Base, fromColor(color)); + p.setColor(QPalette::Window, fromColor(color)); + p.setColor(QPalette::Base, fromColor(color)); hr = uiSettings()->UIElementColor(UIElementType_TextContrastWithHigh, &color); Q_ASSERT_SUCCEEDED(hr); - d->palette.setColor(QPalette::BrightText, fromColor(color)); + p.setColor(QPalette::BrightText, fromColor(color)); #else hr = uiSettings()->UIElementColor(UIElementType_ActiveCaption, &color); Q_ASSERT_SUCCEEDED(hr); - d->palette.setColor(QPalette::ToolTipBase, fromColor(color)); + p.setColor(QPalette::ToolTipBase, fromColor(color)); hr = uiSettings()->UIElementColor(UIElementType_Background, &color); Q_ASSERT_SUCCEEDED(hr); - d->palette.setColor(QPalette::AlternateBase, fromColor(color)); + p.setColor(QPalette::AlternateBase, fromColor(color)); hr = uiSettings()->UIElementColor(UIElementType_ButtonFace, &color); Q_ASSERT_SUCCEEDED(hr); - d->palette.setColor(QPalette::Button, fromColor(color)); - d->palette.setColor(QPalette::Midlight, fromColor(color).lighter(110)); - d->palette.setColor(QPalette::Light, fromColor(color).lighter(150)); - d->palette.setColor(QPalette::Mid, fromColor(color).dark(130)); - d->palette.setColor(QPalette::Dark, fromColor(color).dark(150)); + p.setColor(QPalette::Button, fromColor(color)); + p.setColor(QPalette::Midlight, fromColor(color).lighter(110)); + p.setColor(QPalette::Light, fromColor(color).lighter(150)); + p.setColor(QPalette::Mid, fromColor(color).dark(130)); + p.setColor(QPalette::Dark, fromColor(color).dark(150)); hr = uiSettings()->UIElementColor(UIElementType_ButtonText, &color); Q_ASSERT_SUCCEEDED(hr); - d->palette.setColor(QPalette::ButtonText, fromColor(color)); - d->palette.setColor(QPalette::Text, fromColor(color)); + p.setColor(QPalette::ButtonText, fromColor(color)); + p.setColor(QPalette::Text, fromColor(color)); hr = uiSettings()->UIElementColor(UIElementType_CaptionText, &color); Q_ASSERT_SUCCEEDED(hr); - d->palette.setColor(QPalette::ToolTipText, fromColor(color)); + p.setColor(QPalette::ToolTipText, fromColor(color)); hr = uiSettings()->UIElementColor(UIElementType_Highlight, &color); Q_ASSERT_SUCCEEDED(hr); - d->palette.setColor(QPalette::Highlight, fromColor(color)); + p.setColor(QPalette::Highlight, fromColor(color)); hr = uiSettings()->UIElementColor(UIElementType_HighlightText, &color); Q_ASSERT_SUCCEEDED(hr); - d->palette.setColor(QPalette::HighlightedText, fromColor(color)); + p.setColor(QPalette::HighlightedText, fromColor(color)); hr = uiSettings()->UIElementColor(UIElementType_Window, &color); Q_ASSERT_SUCCEEDED(hr); - d->palette.setColor(QPalette::Window, fromColor(color)); - d->palette.setColor(QPalette::Base, fromColor(color)); + p.setColor(QPalette::Window, fromColor(color)); + p.setColor(QPalette::Base, fromColor(color)); hr = uiSettings()->UIElementColor(UIElementType_Hotlight, &color); Q_ASSERT_SUCCEEDED(hr); - d->palette.setColor(QPalette::BrightText, fromColor(color)); + p.setColor(QPalette::BrightText, fromColor(color)); #endif } +#endif // _MSC_VER < 1900 + +QWinRTTheme::QWinRTTheme() + : d_ptr(new QWinRTThemePrivate) +{ + Q_D(QWinRTTheme); + + nativeColorSettings(d->palette); +} bool QWinRTTheme::usePlatformNativeDialog(DialogType type) const { @@ -218,7 +337,7 @@ QVariant QWinRTTheme::styleHint(QPlatformIntegration::StyleHint hint) case QPlatformIntegration::KeyboardAutoRepeatRate: return defaultThemeHint(KeyboardAutoRepeatRate); case QPlatformIntegration::ShowIsFullScreen: - return true; + return false; case QPlatformIntegration::PasswordMaskDelay: return defaultThemeHint(PasswordMaskDelay); case QPlatformIntegration::FontSmoothingGamma: @@ -232,7 +351,7 @@ QVariant QWinRTTheme::styleHint(QPlatformIntegration::StyleHint hint) case QPlatformIntegration::SetFocusOnTouchRelease: return false; case QPlatformIntegration::ShowIsMaximized: - return false; + return true; case QPlatformIntegration::MousePressAndHoldInterval: return defaultThemeHint(MousePressAndHoldInterval); default: diff --git a/src/plugins/platforms/winrt/qwinrtwindow.cpp b/src/plugins/platforms/winrt/qwinrtwindow.cpp index adc5dfb776..bec94c1e51 100644 --- a/src/plugins/platforms/winrt/qwinrtwindow.cpp +++ b/src/plugins/platforms/winrt/qwinrtwindow.cpp @@ -36,47 +36,164 @@ #include "qwinrtwindow.h" #include "qwinrtscreen.h" +#include <private/qeventdispatcher_winrt_p.h> -#include <qpa/qwindowsysteminterface.h> +#include <EGL/egl.h> +#define EGL_EGLEXT_PROTOTYPES +#include <EGL/eglext.h> + +#include <qfunctions_winrt.h> #include <qpa/qplatformscreen.h> +#include <qpa/qwindowsysteminterface.h> #include <QtGui/QGuiApplication> -#include <QtGui/QWindow> #include <QtGui/QOpenGLContext> +#include <QtGui/QWindow> +#include <QtPlatformSupport/private/qeglconvenience_p.h> -#include <qfunctions_winrt.h> -#include <windows.ui.viewmanagement.h> +#include <functional> #include <wrl.h> +#include <windows.foundation.h> +#include <windows.foundation.collections.h> +#include <windows.ui.xaml.h> +#include <windows.ui.xaml.controls.h> +#include <windows.ui.viewmanagement.h> using namespace ABI::Windows::UI::ViewManagement; using namespace Microsoft::WRL; using namespace Microsoft::WRL::Wrappers; +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::Foundation::Collections; +using namespace ABI::Windows::UI; +using namespace ABI::Windows::UI::Xaml; +using namespace ABI::Windows::UI::Xaml::Controls; QT_BEGIN_NAMESPACE +static void setUIElementVisibility(IUIElement *uiElement, bool visibility) +{ + Q_ASSERT(uiElement); + QEventDispatcherWinRT::runOnXamlThread([uiElement, visibility]() { + HRESULT hr; + hr = uiElement->put_Visibility(visibility ? Visibility_Visible : Visibility_Collapsed); + Q_ASSERT_SUCCEEDED(hr); + return S_OK; + }); +} + +class QWinRTWindowPrivate +{ +public: + QWinRTScreen *screen; + + QSurfaceFormat surfaceFormat; + QString windowTitle; + Qt::WindowState state; + EGLDisplay display; + EGLSurface surface; + + ComPtr<ISwapChainPanel> swapChainPanel; + ComPtr<ICanvasStatics> canvas; + ComPtr<IUIElement> uiElement; +}; + QWinRTWindow::QWinRTWindow(QWindow *window) : QPlatformWindow(window) - , m_screen(static_cast<QWinRTScreen*>(screen())) + , d_ptr(new QWinRTWindowPrivate) { + Q_D(QWinRTWindow); + + d->surface = EGL_NO_SURFACE; + d->display = EGL_NO_DISPLAY; + d->screen = static_cast<QWinRTScreen *>(screen()); setWindowFlags(window->flags()); setWindowState(window->windowState()); setWindowTitle(window->title()); handleContentOrientationChange(window->contentOrientation()); + + d->surfaceFormat.setAlphaBufferSize(0); + d->surfaceFormat.setRedBufferSize(8); + d->surfaceFormat.setGreenBufferSize(8); + d->surfaceFormat.setBlueBufferSize(8); + d->surfaceFormat.setDepthBufferSize(24); + d->surfaceFormat.setStencilBufferSize(8); + d->surfaceFormat.setRenderableType(QSurfaceFormat::OpenGLES); + d->surfaceFormat.setSamples(1); + d->surfaceFormat.setSwapBehavior(QSurfaceFormat::DoubleBuffer); + + HRESULT hr; + hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_UI_Xaml_Controls_Canvas).Get(), + IID_PPV_ARGS(&d->canvas)); + Q_ASSERT_SUCCEEDED(hr); + hr = QEventDispatcherWinRT::runOnXamlThread([this, d]() { + // Create a new swapchain and place it inside the canvas + HRESULT hr; + hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_UI_Xaml_Controls_SwapChainPanel).Get(), + &d->swapChainPanel); + Q_ASSERT_SUCCEEDED(hr); + hr = d->swapChainPanel.As(&d->uiElement); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr<IDependencyObject> canvas = d->screen->canvas(); + ComPtr<IPanel> panel; + hr = canvas.As(&panel); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IVector<UIElement *>> children; + hr = panel->get_Children(&children); + Q_ASSERT_SUCCEEDED(hr); + hr = children->Append(d->uiElement.Get()); + Q_ASSERT_SUCCEEDED(hr); + return S_OK; + }); + Q_ASSERT_SUCCEEDED(hr); + setGeometry(window->geometry()); } QWinRTWindow::~QWinRTWindow() { - m_screen->removeWindow(window()); + Q_D(QWinRTWindow); + + HRESULT hr; + hr = QEventDispatcherWinRT::runOnXamlThread([d]() { + HRESULT hr; + ComPtr<IDependencyObject> canvas = d->screen->canvas(); + ComPtr<IPanel> panel; + hr = canvas.As(&panel); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IVector<UIElement *>> children; + hr = panel->get_Children(&children); + Q_ASSERT_SUCCEEDED(hr); + quint32 index; + boolean found; + hr = children->IndexOf(d->uiElement.Get(), &index, &found); + Q_ASSERT_SUCCEEDED(hr); + if (found) { + hr = children->RemoveAt(index); + Q_ASSERT_SUCCEEDED(hr); + } + return S_OK; + }); + RETURN_VOID_IF_FAILED("Failed to completely destroy window resources, likely because the application is shutting down"); + + if (!d->surface) + return; + + EGLBoolean value = eglDestroySurface(d->display, d->surface); + d->surface = EGL_NO_SURFACE; + if (value == EGL_FALSE) + qCritical("Failed to destroy EGL window surface: 0x%x", eglGetError()); } QSurfaceFormat QWinRTWindow::format() const { - return m_screen->surfaceFormat(); + Q_D(const QWinRTWindow); + return d->surfaceFormat; } bool QWinRTWindow::isActive() const { - return m_screen->topWindow() == window(); + Q_D(const QWinRTWindow); + return d->screen->topWindow() == window(); } bool QWinRTWindow::isExposed() const @@ -87,55 +204,82 @@ bool QWinRTWindow::isExposed() const void QWinRTWindow::setGeometry(const QRect &rect) { - if (window()->isTopLevel()) { - QPlatformWindow::setGeometry(m_screen->geometry()); + Q_D(QWinRTWindow); + + const Qt::WindowFlags windowFlags = window()->flags(); + const Qt::WindowFlags windowType = windowFlags & Qt::WindowType_Mask; + if (window()->isTopLevel() && (windowType == Qt::Window || windowType == Qt::Dialog)) { + QPlatformWindow::setGeometry(windowFlags & Qt::MaximizeUsingFullscreenGeometryHint + ? d->screen->geometry() : d->screen->availableGeometry()); QWindowSystemInterface::handleGeometryChange(window(), geometry()); } else { QPlatformWindow::setGeometry(rect); QWindowSystemInterface::handleGeometryChange(window(), rect); } + + HRESULT hr; + hr = QEventDispatcherWinRT::runOnXamlThread([this, d]() { + HRESULT hr; + const QRect windowGeometry = geometry(); + const QPointF topLeft= QPointF(windowGeometry.topLeft()) / d->screen->scaleFactor(); + hr = d->canvas->SetTop(d->uiElement.Get(), topLeft.y()); + Q_ASSERT_SUCCEEDED(hr); + hr = d->canvas->SetLeft(d->uiElement.Get(), topLeft.x()); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<Xaml::IFrameworkElement> frameworkElement; + hr = d->swapChainPanel.As(&frameworkElement); + Q_ASSERT_SUCCEEDED(hr); + const QSizeF size = QSizeF(windowGeometry.size()) / d->screen->scaleFactor(); + hr = frameworkElement->put_Width(size.width()); + Q_ASSERT_SUCCEEDED(hr); + hr = frameworkElement->put_Height(size.height()); + Q_ASSERT_SUCCEEDED(hr); + return S_OK; + }); + Q_ASSERT_SUCCEEDED(hr); } void QWinRTWindow::setVisible(bool visible) { + Q_D(QWinRTWindow); if (!window()->isTopLevel()) return; - if (visible) - m_screen->addWindow(window()); - else - m_screen->removeWindow(window()); + if (visible) { + d->screen->addWindow(window()); + setUIElementVisibility(d->uiElement.Get(), d->state != Qt::WindowMinimized); + } else { + d->screen->removeWindow(window()); + setUIElementVisibility(d->uiElement.Get(), false); + } } void QWinRTWindow::setWindowTitle(const QString &title) { - ComPtr<IApplicationViewStatics2> statics; - HRESULT hr; - - hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_UI_ViewManagement_ApplicationView).Get(), - IID_PPV_ARGS(&statics)); - RETURN_VOID_IF_FAILED("Could not get ApplicationViewStatics"); - - ComPtr<IApplicationView> view; - hr = statics->GetForCurrentView(&view); - RETURN_VOID_IF_FAILED("Could not access currentView"); - - HStringReference str(reinterpret_cast<LPCWSTR>(title.utf16()), title.length()); - hr = view->put_Title(str.Get()); - RETURN_VOID_IF_FAILED("Unable to set window title"); + Q_D(QWinRTWindow); + d->windowTitle = title; + d->screen->updateWindowTitle(); } void QWinRTWindow::raise() { + Q_D(QWinRTWindow); if (!window()->isTopLevel()) return; - m_screen->raise(window()); + d->screen->raise(window()); } void QWinRTWindow::lower() { + Q_D(QWinRTWindow); if (!window()->isTopLevel()) return; - m_screen->lower(window()); + d->screen->lower(window()); +} + +WId QWinRTWindow::winId() const +{ + Q_D(const QWinRTWindow); + return WId(d->swapChainPanel.Get()); } qreal QWinRTWindow::devicePixelRatio() const @@ -143,4 +287,45 @@ qreal QWinRTWindow::devicePixelRatio() const return screen()->devicePixelRatio(); } +void QWinRTWindow::setWindowState(Qt::WindowState state) +{ + Q_D(QWinRTWindow); + if (d->state == state) + return; + +#ifdef Q_OS_WINPHONE + d->screen->setStatusBarVisibility(state == Qt::WindowMaximized || state == Qt::WindowNoState, window()); +#endif + + if (state == Qt::WindowMinimized) + setUIElementVisibility(d->uiElement.Get(), false); + + if (d->state == Qt::WindowMinimized) + setUIElementVisibility(d->uiElement.Get(), true); + + d->state = state; +} + +EGLSurface QWinRTWindow::eglSurface() const +{ + Q_D(const QWinRTWindow); + return d->surface; +} + +void QWinRTWindow::createEglSurface(EGLDisplay display, EGLConfig config) +{ + Q_D(QWinRTWindow); + if (d->surface == EGL_NO_SURFACE) { + d->display = display; + QEventDispatcherWinRT::runOnXamlThread([this, d, display, config]() { + d->surface = eglCreateWindowSurface(display, config, + reinterpret_cast<EGLNativeWindowType>(winId()), + nullptr); + if (d->surface == EGL_NO_SURFACE) + qCritical("Failed to create EGL window surface: 0x%x", eglGetError()); + return S_OK; + }); + } +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/winrt/qwinrtwindow.h b/src/plugins/platforms/winrt/qwinrtwindow.h index 3cfe346ab2..9ac7adbf4d 100644 --- a/src/plugins/platforms/winrt/qwinrtwindow.h +++ b/src/plugins/platforms/winrt/qwinrtwindow.h @@ -39,11 +39,11 @@ #include <qpa/qplatformwindow.h> #include <qpa/qwindowsysteminterface.h> +#include <EGL/egl.h> QT_BEGIN_NAMESPACE -class QWinRTScreen; - +class QWinRTWindowPrivate; class QWinRTWindow : public QPlatformWindow { public: @@ -59,10 +59,17 @@ public: void raise(); void lower(); + WId winId() const Q_DECL_OVERRIDE; + qreal devicePixelRatio() const Q_DECL_OVERRIDE; + void setWindowState(Qt::WindowState state) Q_DECL_OVERRIDE; + + EGLSurface eglSurface() const; + void createEglSurface(EGLDisplay display, EGLConfig config); private: - QWinRTScreen *m_screen; + QScopedPointer<QWinRTWindowPrivate> d_ptr; + Q_DECLARE_PRIVATE(QWinRTWindow) }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/winrt/winrt.pro b/src/plugins/platforms/winrt/winrt.pro index 80429daeed..be6aad02d1 100644 --- a/src/plugins/platforms/winrt/winrt.pro +++ b/src/plugins/platforms/winrt/winrt.pro @@ -1,14 +1,6 @@ TARGET = qwinrt CONFIG -= precompile_header -# For Windows Phone 8 we have to deploy fonts together with the application as DirectWrite -# is not supported here. -winphone:equals(WINSDK_VER, 8.0): { - fonts.path = $$[QT_INSTALL_LIBS]/fonts - fonts.files = $$QT_SOURCE_TREE/lib/fonts/DejaVu*.ttf - INSTALLS += fonts -} - PLUGIN_TYPE = platforms PLUGIN_CLASS_NAME = QWinRTIntegrationPlugin !equals(TARGET, $$QT_DEFAULT_QPA_PLUGIN): PLUGIN_EXTENDS = - @@ -18,13 +10,8 @@ QT += core-private gui-private platformsupport-private DEFINES *= QT_NO_CAST_FROM_ASCII __WRL_NO_DEFAULT_LIB__ GL_GLEXT_PROTOTYPES -LIBS += $$QMAKE_LIBS_CORE - -!if(winphone:equals(WINSDK_VER, 8.0)) { - LIBS += -ldwrite - INCLUDEPATH += $$QT_SOURCE_TREE/src/3rdparty/freetype/include - DEFINES += QT_WINRT_USE_DWRITE -} +LIBS += $$QMAKE_LIBS_CORE -ldwrite -ld3d11 +INCLUDEPATH += $$QT_SOURCE_TREE/src/3rdparty/freetype/include SOURCES = \ main.cpp \ @@ -60,9 +47,4 @@ HEADERS = \ qwinrttheme.h \ qwinrtwindow.h -winphone:equals(WINSDK_VER, 8.0): { - SOURCES -= qwinrtplatformmessagedialoghelper.cpp - HEADERS -= qwinrtplatformmessagedialoghelper.h -} - OTHER_FILES += winrt.json diff --git a/src/plugins/platforms/xcb/gl_integrations/qxcbglintegrationfactory.cpp b/src/plugins/platforms/xcb/gl_integrations/qxcbglintegrationfactory.cpp index 10411e72e2..508f5e82e6 100644 --- a/src/plugins/platforms/xcb/gl_integrations/qxcbglintegrationfactory.cpp +++ b/src/plugins/platforms/xcb/gl_integrations/qxcbglintegrationfactory.cpp @@ -78,6 +78,7 @@ QStringList QXcbGlIntegrationFactory::keys(const QString &pluginPath) list.append(loader()->keyMap().values()); return list; #else + Q_UNUSED(pluginPath); return QStringList(); #endif } @@ -93,6 +94,9 @@ QXcbGlIntegration *QXcbGlIntegrationFactory::create(const QString &platform, con } if (QXcbGlIntegration *ret = loadIntegration(loader(), platform)) return ret; +#else + Q_UNUSED(platform); + Q_UNUSED(pluginPath); #endif return Q_NULLPTR; } diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp index 8b14fc7d70..37f01d4eed 100644 --- a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp +++ b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp @@ -188,16 +188,18 @@ void QGLXContext::init(QXcbScreen *screen, QPlatformOpenGLContext *share) Window window = 0; // Temporary window used to query OpenGL context if (config) { + const QByteArrayList glxExt = QByteArray(glXQueryExtensionsString(m_display, screen->screenNumber())).split(' '); + // Resolve entry point for glXCreateContextAttribsARB glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0; - glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc) glXGetProcAddress((const GLubyte*)"glXCreateContextAttribsARB"); + if (glxExt.contains("GLX_ARB_create_context")) + glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc) glXGetProcAddress((const GLubyte*)"glXCreateContextAttribsARB"); - QList<QByteArray> glxExt = QByteArray(glXQueryExtensionsString(m_display, screen->screenNumber())).split(' '); - bool supportsProfiles = glxExt.contains("GLX_ARB_create_context_profile"); + const bool supportsProfiles = glxExt.contains("GLX_ARB_create_context_profile"); // Use glXCreateContextAttribsARB if available // Also, GL ES context creation requires GLX_EXT_create_context_es2_profile - if (glxExt.contains("GLX_ARB_create_context") && glXCreateContextAttribsARB != 0 + if (glXCreateContextAttribsARB != 0 && (m_format.renderableType() != QSurfaceFormat::OpenGLES || (supportsProfiles && glxExt.contains("GLX_EXT_create_context_es2_profile")))) { // Try to create an OpenGL context for each known OpenGL version in descending // order from the requested version. @@ -561,10 +563,12 @@ void (*QGLXContext::getProcAddress(const QByteArray &procName)) () if (!glXGetProcAddressARB) #endif { +#ifndef QT_NO_LIBRARY extern const QString qt_gl_library_name(); // QLibrary lib(qt_gl_library_name()); QLibrary lib(QLatin1String("GL")); glXGetProcAddressARB = (qt_glXGetProcAddressARB) lib.resolve("glXGetProcAddressARB"); +#endif } } resolved = true; diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/xcb_glx.pro b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/xcb_glx.pro index 57cd81ec3b..1c577e5dc9 100644 --- a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/xcb_glx.pro +++ b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/xcb_glx.pro @@ -17,6 +17,8 @@ contains(QT_CONFIG, xcb-glx) { LIBS += -lxcb-glx } +LIBS += $$QMAKE_LIBS_DYNLOAD + HEADERS += \ qxcbglxintegration.h \ qxcbglxwindow.h \ diff --git a/src/plugins/platforms/xcb/qxcbbackingstore.cpp b/src/plugins/platforms/xcb/qxcbbackingstore.cpp index c0f5477f82..1825a463d0 100644 --- a/src/plugins/platforms/xcb/qxcbbackingstore.cpp +++ b/src/plugins/platforms/xcb/qxcbbackingstore.cpp @@ -49,6 +49,7 @@ #include <qdebug.h> #include <qpainter.h> #include <qscreen.h> +#include <QtGui/private/qhighdpiscaling_p.h> #include <qpa/qplatformgraphicsbuffer.h> #include <algorithm> @@ -145,8 +146,8 @@ QXcbShmImage::QXcbShmImage(QXcbScreen *screen, const QSize &size, uint depth, QI int id = shmget(IPC_PRIVATE, segmentSize, IPC_CREAT | 0600); if (id == -1) - qWarning("QXcbShmImage: shmget() failed (%d) for size %d (%dx%d)", - errno, segmentSize, size.width(), size.height()); + qWarning("QXcbShmImage: shmget() failed (%d: %s) for size %d (%dx%d)", + errno, strerror(errno), segmentSize, size.width(), size.height()); else m_shm_info.shmid = id; m_shm_info.shmaddr = m_xcb_image->data = (quint8 *)shmat (m_shm_info.shmid, 0, 0); @@ -310,17 +311,13 @@ QPaintDevice *QXcbBackingStore::paintDevice() void QXcbBackingStore::beginPaint(const QRegion ®ion) { + if (!m_image && !m_size.isEmpty()) + resize(m_size, QRegion()); + if (!m_image) return; - - int dpr = int(m_image->image()->devicePixelRatio()); - const int windowDpr = int(window()->devicePixelRatio()); - if (windowDpr != dpr) { - resize(window()->size(), QRegion()); - dpr = int(m_image->image()->devicePixelRatio()); - } - - m_paintRegion = dpr == 1 ? region : QTransform::fromScale(dpr,dpr).map(region); + m_size = QSize(); + m_paintRegion = region; m_image->preparePaint(m_paintRegion); if (m_image->image()->hasAlphaChannel()) { @@ -369,18 +366,10 @@ void QXcbBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoin if (!m_image || m_image->size().isEmpty()) return; - const int dpr = int(window->devicePixelRatio()); - -#ifndef QT_NO_DEBUG - const int imageDpr = int(m_image->image()->devicePixelRatio()); - if (dpr != imageDpr) - qWarning() << "QXcbBackingStore::flush() wrong devicePixelRatio for backingstore image" << dpr << imageDpr; -#endif - - QSize imageSize = m_image->size() / dpr; //because we multiply with the DPR later + QSize imageSize = m_image->size(); QRegion clipped = region; - clipped &= QRect(0, 0, window->width(), window->height()); + clipped &= QRect(QPoint(), QHighDpi::toNativePixels(window->size(), window)); clipped &= QRect(0, 0, imageSize.width(), imageSize.height()).translated(-offset); QRect bounds = clipped.boundingRect(); @@ -398,8 +387,8 @@ void QXcbBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoin QVector<QRect> rects = clipped.rects(); for (int i = 0; i < rects.size(); ++i) { - QRect rect = QRect(rects.at(i).topLeft() * dpr, rects.at(i).size() * dpr); - m_image->put(platformWindow->xcb_window(), rect.topLeft(), rect.translated(offset * dpr)); + QRect rect = QRect(rects.at(i).topLeft(), rects.at(i).size()); + m_image->put(platformWindow->xcb_window(), rect.topLeft(), rect.translated(offset)); } Q_XCB_NOOP(connection()); @@ -430,13 +419,12 @@ void QXcbBackingStore::composeAndFlush(QWindow *window, const QRegion ®ion, c void QXcbBackingStore::resize(const QSize &size, const QRegion &) { - const int dpr = int(window()->devicePixelRatio()); - const QSize xSize = size * dpr; - if (m_image && xSize == m_image->size() && dpr == m_image->image()->devicePixelRatio()) + if (m_image && size == m_image->size()) return; Q_XCB_NOOP(connection()); - QXcbScreen *screen = static_cast<QXcbScreen *>(window()->screen()->handle()); + + QXcbScreen *screen = window()->screen() ? static_cast<QXcbScreen *>(window()->screen()->handle()) : 0; QPlatformWindow *pw = window()->handle(); if (!pw) { window()->create(); @@ -445,13 +433,16 @@ void QXcbBackingStore::resize(const QSize &size, const QRegion &) QXcbWindow* win = static_cast<QXcbWindow *>(pw); delete m_image; - m_image = new QXcbShmImage(screen, xSize, win->depth(), win->imageFormat()); - m_image->image()->setDevicePixelRatio(dpr); + if (!screen) { + m_image = 0; + m_size = size; + return; + } + m_image = new QXcbShmImage(screen, size, win->depth(), win->imageFormat()); // Slow path for bgr888 VNC: Create an additional image, paint into that and // swap R and B while copying to m_image after each paint. if (win->imageNeedsRgbSwap()) { - m_rgbImage = QImage(xSize, win->imageFormat()); - m_rgbImage.setDevicePixelRatio(dpr); + m_rgbImage = QImage(size, win->imageFormat()); } Q_XCB_NOOP(connection()); } @@ -463,14 +454,12 @@ bool QXcbBackingStore::scroll(const QRegion &area, int dx, int dy) if (!m_image || m_image->image()->isNull()) return false; - const int dpr = int(m_image->image()->devicePixelRatio()); - QRegion xArea = dpr == 1 ? area : QTransform::fromScale(dpr,dpr).map(area); m_image->preparePaint(area); - QPoint delta(dx * dpr, dy * dpr); - const QVector<QRect> xRects = xArea.rects(); - for (int i = 0; i < xRects.size(); ++i) - qt_scrollRectInImage(*m_image->image(), xRects.at(i), delta); + QPoint delta(dx, dy); + const QVector<QRect> rects = area.rects(); + for (int i = 0; i < rects.size(); ++i) + qt_scrollRectInImage(*m_image->image(), rects.at(i), delta); return true; } diff --git a/src/plugins/platforms/xcb/qxcbbackingstore.h b/src/plugins/platforms/xcb/qxcbbackingstore.h index b58a32d313..1bea36d423 100644 --- a/src/plugins/platforms/xcb/qxcbbackingstore.h +++ b/src/plugins/platforms/xcb/qxcbbackingstore.h @@ -71,6 +71,7 @@ private: QXcbShmImage *m_image; QRegion m_paintRegion; QImage m_rgbImage; + QSize m_size; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbclipboard.cpp b/src/plugins/platforms/xcb/qxcbclipboard.cpp index 248d1b4bbb..8b75c130fb 100644 --- a/src/plugins/platforms/xcb/qxcbclipboard.cpp +++ b/src/plugins/platforms/xcb/qxcbclipboard.cpp @@ -275,22 +275,8 @@ QXcbClipboard::QXcbClipboard(QXcbConnection *c) m_clientClipboard[QClipboard::Selection] = 0; m_timestamp[QClipboard::Clipboard] = XCB_CURRENT_TIME; m_timestamp[QClipboard::Selection] = XCB_CURRENT_TIME; + m_owner = connection()->getQtSelectionOwner(); - QXcbScreen *platformScreen = screen(); - - int x = 0, y = 0, w = 3, h = 3; - - m_owner = xcb_generate_id(xcb_connection()); - Q_XCB_CALL(xcb_create_window(xcb_connection(), - XCB_COPY_FROM_PARENT, // depth -- same as root - m_owner, // window id - platformScreen->screen()->root, // parent window id - x, y, w, h, - 0, // border width - XCB_WINDOW_CLASS_INPUT_OUTPUT, // window class - platformScreen->screen()->root_visual, // visual - 0, // value mask - 0)); // value list #ifndef QT_NO_DEBUG QByteArray ba("Qt clipboard window"); Q_XCB_CALL(xcb_change_property(xcb_connection(), @@ -353,13 +339,7 @@ void QXcbClipboard::incrTransactionPeeker(xcb_generic_event_t *ge, bool &accepte xcb_window_t QXcbClipboard::getSelectionOwner(xcb_atom_t atom) const { - xcb_connection_t *c = xcb_connection(); - xcb_get_selection_owner_cookie_t cookie = xcb_get_selection_owner(c, atom); - xcb_get_selection_owner_reply_t *reply; - reply = xcb_get_selection_owner_reply(c, cookie, 0); - xcb_window_t win = reply->owner; - free(reply); - return win; + return connection()->getSelectionOwner(atom); } xcb_atom_t QXcbClipboard::atomForMode(QClipboard::Mode mode) const diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index d2e08aecee..50d49ca798 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -107,6 +107,24 @@ Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen") #define XCB_GE_GENERIC 35 #endif +// Starting from the xcb version 1.9.3 struct xcb_ge_event_t has changed: +// - "pad0" became "extension" +// - "pad1" and "pad" became "pad0" +// New and old version of this struct share the following fields: +typedef struct qt_xcb_ge_event_t { + uint8_t response_type; + uint8_t extension; + uint16_t sequence; + uint32_t length; + uint16_t event_type; +} qt_xcb_ge_event_t; + +static inline bool isXIEvent(xcb_generic_event_t *event, int opCode) +{ + qt_xcb_ge_event_t *e = (qt_xcb_ge_event_t *)event; + return e->extension == opCode; +} + #ifdef XCB_USE_XLIB static const char * const xcbConnectionErrors[] = { "No error", /* Error 0 */ @@ -245,8 +263,7 @@ void QXcbConnection::updateScreens(const xcb_randr_notify_event_t *event) // Known screen removed -> delete it m_screens.removeOne(screen); - foreach (QXcbScreen *otherScreen, m_screens) - otherScreen->removeVirtualSibling((QPlatformScreen *) screen); + virtualDesktop->removeScreen(screen); QXcbIntegration::instance()->destroyScreen(screen); @@ -265,9 +282,7 @@ void QXcbConnection::updateScreens(const xcb_randr_notify_event_t *event) qCDebug(lcQpaScreen) << "output" << screen->name() << "is connected and enabled"; screen->setPrimary(checkOutputIsPrimary(output.window, output.output)); - foreach (QXcbScreen *otherScreen, m_screens) - if (otherScreen->root() == output.window) - otherScreen->addVirtualSibling(screen); + virtualDesktop->addScreen(screen); m_screens << screen; QXcbIntegration::instance()->screenAdded(screen, screen->isPrimary()); @@ -290,8 +305,7 @@ void QXcbConnection::updateScreens(const xcb_randr_notify_event_t *event) if (outputInfo->crtc == XCB_NONE) { qCDebug(lcQpaScreen) << "output" << screen->name() << "has been disabled"; m_screens.removeOne(screen); - foreach (QXcbScreen *otherScreen, m_screens) - otherScreen->removeVirtualSibling((QPlatformScreen *) screen); + virtualDesktop->removeScreen(screen); QXcbIntegration::instance()->destroyScreen(screen); } else { qCDebug(lcQpaScreen) << "output" << screen->name() << "has been temporarily disabled for the mode switch"; @@ -305,13 +319,10 @@ void QXcbConnection::updateScreens(const xcb_randr_notify_event_t *event) screen->updateRefreshRate(output.mode); // If the screen became primary, reshuffle the order in QGuiApplicationPrivate - // TODO: add a proper mechanism for updating primary screen if (!wasPrimary && screen->isPrimary()) { - QScreen *realScreen = static_cast<QPlatformScreen*>(screen)->screen(); - QGuiApplicationPrivate::screen_list.removeOne(realScreen); - QGuiApplicationPrivate::screen_list.prepend(realScreen); - m_screens.removeOne(screen); - m_screens.prepend(screen); + const int idx = m_screens.indexOf(screen); + m_screens.swap(0, idx); + QXcbIntegration::instance()->setPrimaryScreen(screen); } qCDebug(lcQpaScreen) << "output has changed" << screen; } @@ -428,12 +439,14 @@ void QXcbConnection::initializeScreens() } } } - foreach (QPlatformScreen* s, siblings) - ((QXcbScreen*)s)->setVirtualSiblings(siblings); + virtualDesktop->setScreens(siblings); xcb_screen_next(&it); ++xcbScreenNumber; } // for each xcb screen + foreach (QXcbVirtualDesktop *virtualDesktop, m_virtualDesktops) + virtualDesktop->subscribeToXFixesSelectionNotify(); + // If there's no randr extension, or there was some error above, or we found a // screen which doesn't have outputs for some other reason (e.g. on VNC or ssh -X), // but the dimensions are known anyway, and we don't already have any lingering @@ -442,7 +455,7 @@ void QXcbConnection::initializeScreens() QXcbVirtualDesktop *virtualDesktop = m_virtualDesktops.value(0); if (virtualDesktop && !hasOutputs && !virtualDesktop->size().isEmpty() && m_screens.isEmpty()) { QXcbScreen *screen = createScreen(virtualDesktop, 0, Q_NULLPTR); - screen->setVirtualSiblings(QList<QPlatformScreen *>() << screen); + virtualDesktop->setScreens(QList<QPlatformScreen *>() << screen); m_screens << screen; primaryScreen = screen; primaryScreen->setPrimary(true); @@ -461,7 +474,7 @@ void QXcbConnection::initializeScreens() // Push the screens to QApplication QXcbIntegration *integration = QXcbIntegration::instance(); foreach (QXcbScreen* screen, m_screens) { - qCDebug(lcQpaScreen) << "adding" << screen << "(Primary:" << screen->isPrimary() << ")"; + qCDebug(lcQpaScreen) << "adding" << screen << "(Primary:" << screen->isPrimary() << ')'; integration->screenAdded(screen, screen->isPrimary()); } @@ -492,6 +505,7 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra , m_systemTrayTracker(0) , m_glIntegration(Q_NULLPTR) , m_xiGrab(false) + , m_qtSelectionOwner(0) { #ifdef XCB_USE_XLIB Display *dpy = XOpenDisplay(m_displayName.constData()); @@ -536,12 +550,12 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra m_netWmUserTime = XCB_CURRENT_TIME; initializeXRandr(); + initializeXFixes(); initializeScreens(); if (m_screens.isEmpty()) qFatal("QXcbConnection: no screens available"); - initializeXFixes(); initializeXRender(); m_xi2Enabled = false; #if defined(XCB_USE_XINPUT2) @@ -590,9 +604,6 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra qCDebug(QT_XCB_GLINTEGRATION) << "Failed to create xcb gl-integration"; sync(); - - if (qEnvironmentVariableIsEmpty("QT_IM_MODULE")) - qputenv("QT_IM_MODULE", QByteArray("compose")); } QXcbConnection::~QXcbConnection() @@ -1075,8 +1086,12 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) case XCB_FOCUS_OUT: HANDLE_PLATFORM_WINDOW_EVENT(xcb_focus_out_event_t, event, handleFocusOutEvent); case XCB_KEY_PRESS: - m_keyboard->updateXKBStateFromCore(((xcb_key_press_event_t *)event)->state); + { + xcb_key_press_event_t *kp = (xcb_key_press_event_t *)event; + m_keyboard->updateXKBStateFromCore(kp->state); + setTime(kp->time); HANDLE_KEYBOARD_EVENT(xcb_key_press_event_t, handleKeyPressEvent); + } case XCB_KEY_RELEASE: m_keyboard->updateXKBStateFromCore(((xcb_key_release_event_t *)event)->state); HANDLE_KEYBOARD_EVENT(xcb_key_release_event_t, handleKeyReleaseEvent); @@ -1110,12 +1125,21 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) handled = false; break; case XCB_PROPERTY_NOTIFY: - HANDLE_PLATFORM_WINDOW_EVENT(xcb_property_notify_event_t, window, handlePropertyNotifyEvent); + { + xcb_property_notify_event_t *pn = (xcb_property_notify_event_t *)event; + if (pn->atom == atom(QXcbAtom::_NET_WORKAREA)) { + QXcbVirtualDesktop *virtualDesktop = virtualDesktopForRootWindow(pn->window); + if (virtualDesktop) + virtualDesktop->updateWorkArea(); + } else { + HANDLE_PLATFORM_WINDOW_EVENT(xcb_property_notify_event_t, window, handlePropertyNotifyEvent); + } break; + } #if defined(XCB_USE_XINPUT2) case XCB_GE_GENERIC: // Here the windowEventListener is invoked from xi2HandleEvent() - if (m_xi2Enabled) + if (m_xi2Enabled && isXIEvent(event, m_xiOpCode)) xi2HandleEvent(reinterpret_cast<xcb_ge_event_t *>(event)); break; #endif @@ -1127,10 +1151,14 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) if (!handled) { if (response_type == xfixes_first_event + XCB_XFIXES_SELECTION_NOTIFY) { - setTime(((xcb_xfixes_selection_notify_event_t *)event)->timestamp); + xcb_xfixes_selection_notify_event_t *notify_event = (xcb_xfixes_selection_notify_event_t *)event; + setTime(notify_event->timestamp); #ifndef QT_NO_CLIPBOARD - m_clipboard->handleXFixesSelectionRequest((xcb_xfixes_selection_notify_event_t *)event); + m_clipboard->handleXFixesSelectionRequest(notify_event); #endif + foreach (QXcbVirtualDesktop *virtualDesktop, m_virtualDesktops) + virtualDesktop->handleXFixesSelectionNotify(notify_event); + handled = true; } else if (has_randr_extension && response_type == xrandr_first_event + XCB_RANDR_NOTIFY) { updateScreens((xcb_randr_notify_event_t *)event); @@ -1288,10 +1316,12 @@ void QXcbConnection::sendConnectionEvent(QXcbAtom::Atom a, uint id) memset(&event, 0, sizeof(event)); const xcb_window_t eventListener = xcb_generate_id(m_connection); + xcb_screen_iterator_t it = xcb_setup_roots_iterator(m_setup); + xcb_screen_t *screen = it.data; Q_XCB_CALL(xcb_create_window(m_connection, XCB_COPY_FROM_PARENT, - eventListener, m_screens.at(0)->root(), + eventListener, screen->root, 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_ONLY, - m_screens.at(0)->screen()->root_visual, 0, 0)); + screen->root_visual, 0, 0)); event.response_type = XCB_CLIENT_MESSAGE; event.format = 32; @@ -1357,6 +1387,37 @@ xcb_timestamp_t QXcbConnection::getTimestamp() return timestamp; } +xcb_window_t QXcbConnection::getSelectionOwner(xcb_atom_t atom) const +{ + xcb_connection_t *c = xcb_connection(); + xcb_get_selection_owner_cookie_t cookie = xcb_get_selection_owner(c, atom); + xcb_get_selection_owner_reply_t *reply; + reply = xcb_get_selection_owner_reply(c, cookie, 0); + xcb_window_t win = reply->owner; + free(reply); + return win; +} + +xcb_window_t QXcbConnection::getQtSelectionOwner() +{ + if (!m_qtSelectionOwner) { + xcb_screen_t *xcbScreen = primaryVirtualDesktop()->screen(); + int x = 0, y = 0, w = 3, h = 3; + m_qtSelectionOwner = xcb_generate_id(xcb_connection()); + Q_XCB_CALL(xcb_create_window(xcb_connection(), + XCB_COPY_FROM_PARENT, // depth -- same as root + m_qtSelectionOwner, // window id + xcbScreen->root, // parent window id + x, y, w, h, + 0, // border width + XCB_WINDOW_CLASS_INPUT_OUTPUT, // window class + xcbScreen->root_visual, // visual + 0, // value mask + 0)); // value list + } + return m_qtSelectionOwner; +} + xcb_window_t QXcbConnection::rootWindow() { QXcbScreen *s = primaryScreen(); @@ -1437,6 +1498,105 @@ void *QXcbConnection::createVisualInfoForDefaultVisualId() const #endif +#if defined(XCB_USE_XINPUT2) +// it is safe to cast XI_* events here as long as we are only touching the first 32 bytes, +// after that position event needs memmove, see xi2PrepareXIGenericDeviceEvent +static inline bool isXIType(xcb_generic_event_t *event, int opCode, uint16_t type) +{ + if (!isXIEvent(event, opCode)) + return false; + + xXIGenericDeviceEvent *xiEvent = reinterpret_cast<xXIGenericDeviceEvent *>(event); + return xiEvent->evtype == type; +} +#endif +static inline bool isValid(xcb_generic_event_t *event) +{ + return event && (event->response_type & ~0x80); +} + +/*! \internal + + Compresses events of the same type to avoid swamping the event queue. + If event compression is not desired there are several options what developers can do: + + 1) Write responsive applications. We drop events that have been buffered in the event + queue while waiting on unresponsive GUI thread. + 2) Use QAbstractNativeEventFilter to get all events from X connection. This is not optimal + because it requires working with native event types. + 3) Or add public API to Qt for disabling event compression QTBUG-44964 + +*/ +bool QXcbConnection::compressEvent(xcb_generic_event_t *event, int currentIndex, QXcbEventArray *eventqueue) const +{ + uint responseType = event->response_type & ~0x80; + int nextIndex = currentIndex + 1; + + if (responseType == XCB_MOTION_NOTIFY) { + // compress XCB_MOTION_NOTIFY notify events + for (int j = nextIndex; j < eventqueue->size(); ++j) { + xcb_generic_event_t *next = eventqueue->at(j); + if (!isValid(next)) + continue; + if (next->response_type == XCB_MOTION_NOTIFY) + return true; + } + return false; + } +#if defined(XCB_USE_XINPUT2) + // compress XI_* events + if (responseType == XCB_GE_GENERIC) { + if (!m_xi2Enabled) + return false; + + // compress XI_Motion + if (isXIType(event, m_xiOpCode, XI_Motion)) { + for (int j = nextIndex; j < eventqueue->size(); ++j) { + xcb_generic_event_t *next = eventqueue->at(j); + if (!isValid(next)) + continue; + if (isXIType(next, m_xiOpCode, XI_Motion)) + return true; + } + return false; + } +#ifdef XCB_USE_XINPUT22 + // compress XI_TouchUpdate for the same touch point id + if (isXIType(event, m_xiOpCode, XI_TouchUpdate)) { + xXIDeviceEvent *xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(event); + uint32_t id = xiDeviceEvent->detail % INT_MAX; + for (int j = nextIndex; j < eventqueue->size(); ++j) { + xcb_generic_event_t *next = eventqueue->at(j); + if (!isValid(next)) + continue; + if (isXIType(next, m_xiOpCode, XI_TouchUpdate)) { + xXIDeviceEvent *xiDeviceNextEvent = reinterpret_cast<xXIDeviceEvent *>(next); + if (id == xiDeviceNextEvent->detail % INT_MAX) + return true; + } + } + return false; + } +#endif + return false; + } +#endif + if (responseType == XCB_CONFIGURE_NOTIFY) { + // compress multiple configure notify events for the same window + for (int j = nextIndex; j < eventqueue->size(); ++j) { + xcb_generic_event_t *next = eventqueue->at(j); + if (isValid(next) && next->response_type == XCB_CONFIGURE_NOTIFY + && ((xcb_configure_notify_event_t *)next)->event == ((xcb_configure_notify_event_t*)event)->event) + { + return true; + } + } + return false; + } + + return false; +} + void QXcbConnection::processXcbEvents() { int connection_error = xcb_connection_has_error(xcb_connection()); @@ -1447,61 +1607,39 @@ void QXcbConnection::processXcbEvents() QXcbEventArray *eventqueue = m_reader->lock(); - for(int i = 0; i < eventqueue->size(); ++i) { + for (int i = 0; i < eventqueue->size(); ++i) { xcb_generic_event_t *event = eventqueue->at(i); if (!event) continue; QScopedPointer<xcb_generic_event_t, QScopedPointerPodDeleter> eventGuard(event); (*eventqueue)[i] = 0; - uint response_type = event->response_type & ~0x80; - - if (!response_type) { + if (!(event->response_type & ~0x80)) { handleXcbError((xcb_generic_error_t *)event); - } else { - if (response_type == XCB_MOTION_NOTIFY) { - // compress multiple motion notify events in a row - // to avoid swamping the event queue - xcb_generic_event_t *next = eventqueue->value(i+1, 0); - if (next && (next->response_type & ~0x80) == XCB_MOTION_NOTIFY) - continue; - } + continue; + } - if (response_type == XCB_CONFIGURE_NOTIFY) { - // compress multiple configure notify events for the same window - bool found = false; - for (int j = i; j < eventqueue->size(); ++j) { - xcb_generic_event_t *other = eventqueue->at(j); - if (other && (other->response_type & ~0x80) == XCB_CONFIGURE_NOTIFY - && ((xcb_configure_notify_event_t *)other)->event == ((xcb_configure_notify_event_t *)event)->event) - { - found = true; - break; - } - } - if (found) - continue; - } + if (compressEvent(event, i, eventqueue)) + continue; - bool accepted = false; - if (clipboard()->processIncr()) - clipboard()->incrTransactionPeeker(event, accepted); - if (accepted) - continue; + bool accepted = false; + if (clipboard()->processIncr()) + clipboard()->incrTransactionPeeker(event, accepted); + if (accepted) + continue; - QVector<PeekFunc>::iterator it = m_peekFuncs.begin(); - while (it != m_peekFuncs.end()) { - // These callbacks return true if the event is what they were - // waiting for, remove them from the list in that case. - if ((*it)(this, event)) - it = m_peekFuncs.erase(it); - else - ++it; - } - m_reader->unlock(); - handleXcbEvent(event); - m_reader->lock(); + QVector<PeekFunc>::iterator it = m_peekFuncs.begin(); + while (it != m_peekFuncs.end()) { + // These callbacks return true if the event is what they were + // waiting for, remove them from the list in that case. + if ((*it)(this, event)) + it = m_peekFuncs.erase(it); + else + ++it; } + m_reader->unlock(); + handleXcbEvent(event); + m_reader->lock(); } eventqueue->clear(); @@ -1746,7 +1884,8 @@ static const char * xcb_atomnames = { "_XSETTINGS_SETTINGS\0" "_COMPIZ_DECOR_PENDING\0" "_COMPIZ_DECOR_REQUEST\0" - "_COMPIZ_DECOR_DELETE_PIXMAP\0" // \0\0 terminates loop. + "_COMPIZ_DECOR_DELETE_PIXMAP\0" + "_COMPIZ_TOOLKIT_ACTION\0" // \0\0 terminates loop. }; QXcbAtom::Atom QXcbConnection::qatom(xcb_atom_t xatom) const @@ -2043,41 +2182,21 @@ bool QXcbConnection::xi2GetValuatorValueIfSet(void *event, int valuatorNum, doub return true; } -// Starting from the xcb version 1.9.3 struct xcb_ge_event_t has changed: -// - "pad0" became "extension" -// - "pad1" and "pad" became "pad0" -// New and old version of this struct share the following fields: -// NOTE: API might change again in the next release of xcb in which case this comment will -// need to be updated to reflect the reality. -typedef struct qt_xcb_ge_event_t { - uint8_t response_type; - uint8_t extension; - uint16_t sequence; - uint32_t length; - uint16_t event_type; -} qt_xcb_ge_event_t; - -bool QXcbConnection::xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *ev, int opCode) -{ - qt_xcb_ge_event_t *event = (qt_xcb_ge_event_t *)ev; - // xGenericEvent has "extension" on the second byte, the same is true for xcb_ge_event_t starting from - // the xcb version 1.9.3, prior to that it was called "pad0". - if (event->extension == opCode) { - // xcb event structs contain stuff that wasn't on the wire, the full_sequence field - // adds an extra 4 bytes and generic events cookie data is on the wire right after the standard 32 bytes. - // Move this data back to have the same layout in memory as it was on the wire - // and allow casting, overwriting the full_sequence field. - memmove((char*) event + 32, (char*) event + 36, event->length * 4); - return true; - } - return false; +void QXcbConnection::xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *event) +{ + // xcb event structs contain stuff that wasn't on the wire, the full_sequence field + // adds an extra 4 bytes and generic events cookie data is on the wire right after the standard 32 bytes. + // Move this data back to have the same layout in memory as it was on the wire + // and allow casting, overwriting the full_sequence field. + memmove((char*) event + 32, (char*) event + 36, event->length * 4); } #endif // defined(XCB_USE_XINPUT2) -QXcbSystemTrayTracker *QXcbConnection::systemTrayTracker() +QXcbSystemTrayTracker *QXcbConnection::systemTrayTracker() const { if (!m_systemTrayTracker) { - if ( (m_systemTrayTracker = QXcbSystemTrayTracker::create(this)) ) { + QXcbConnection *self = const_cast<QXcbConnection *>(this); + if ((self->m_systemTrayTracker = QXcbSystemTrayTracker::create(self))) { connect(m_systemTrayTracker, SIGNAL(systemTrayWindowChanged(QScreen*)), QGuiApplication::platformNativeInterface(), SIGNAL(systemTrayWindowChanged(QScreen*))); } @@ -2085,6 +2204,22 @@ QXcbSystemTrayTracker *QXcbConnection::systemTrayTracker() return m_systemTrayTracker; } +bool QXcbConnection::xEmbedSystemTrayAvailable() +{ + if (!QGuiApplicationPrivate::platformIntegration()) + return false; + QXcbConnection *connection = static_cast<QXcbIntegration *>(QGuiApplicationPrivate::platformIntegration())->defaultConnection(); + return connection->systemTrayTracker(); +} + +bool QXcbConnection::xEmbedSystemTrayVisualHasAlphaChannel() +{ + if (!QGuiApplicationPrivate::platformIntegration()) + return false; + QXcbConnection *connection = static_cast<QXcbIntegration *>(QGuiApplicationPrivate::platformIntegration())->defaultConnection(); + return connection->systemTrayTracker() && connection->systemTrayTracker()->visualHasAlphaChannel(); +} + bool QXcbConnection::event(QEvent *e) { if (e->type() == QEvent::User + 1) { diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h index a183a72353..3c82170679 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -286,6 +286,7 @@ namespace QXcbAtom { _COMPIZ_DECOR_PENDING, _COMPIZ_DECOR_REQUEST, _COMPIZ_DECOR_DELETE_PIXMAP, + _COMPIZ_TOOLKIT_ACTION, NPredefinedAtoms, @@ -375,8 +376,10 @@ public: QXcbConnection *connection() const { return const_cast<QXcbConnection *>(this); } + const QList<QXcbVirtualDesktop *> &virtualDesktops() const { return m_virtualDesktops; } const QList<QXcbScreen *> &screens() const { return m_screens; } int primaryScreenNumber() const { return m_primaryScreenNumber; } + QXcbVirtualDesktop *primaryVirtualDesktop() const { return m_virtualDesktops.value(m_primaryScreenNumber); } QXcbScreen *primaryScreen() const; inline xcb_atom_t atom(QXcbAtom::Atom atom) const { return m_allAtoms[atom]; } @@ -457,6 +460,8 @@ public: bool threadedEventHandling() const { return m_reader->isRunning(); } xcb_timestamp_t getTimestamp(); + xcb_window_t getSelectionOwner(xcb_atom_t atom) const; + xcb_window_t getQtSelectionOwner(); void setButton(Qt::MouseButton button, bool down) { if (down) m_buttons |= button; else m_buttons &= ~button; } Qt::MouseButtons buttons() const { return m_buttons; } @@ -474,7 +479,9 @@ public: QXcbNativeInterface *nativeInterface() const { return m_nativeInterface; } - QXcbSystemTrayTracker *systemTrayTracker(); + QXcbSystemTrayTracker *systemTrayTracker() const; + static bool xEmbedSystemTrayAvailable(); + static bool xEmbedSystemTrayVisualHasAlphaChannel(); #ifdef XCB_USE_XINPUT2 void handleEnterEvent(const xcb_enter_notify_event_t *); @@ -520,6 +527,7 @@ private: bool checkOutputIsPrimary(xcb_window_t rootWindow, xcb_randr_output_t output); void initializeScreens(); void updateScreens(const xcb_randr_notify_event_t *event); + bool compressEvent(xcb_generic_event_t *event, int currentIndex, QXcbEventArray *eventqueue) const; bool m_xi2Enabled; int m_xi2Minor; @@ -572,7 +580,7 @@ private: QHash<int, ScrollingDevice> m_scrollingDevices; static bool xi2GetValuatorValueIfSet(void *event, int valuatorNum, double *value); - static bool xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *event, int opCode); + static void xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *event); #endif xcb_connection_t *m_connection; @@ -644,6 +652,8 @@ private: QXcbGlIntegration *m_glIntegration; bool m_xiGrab; + xcb_window_t m_qtSelectionOwner; + friend class QXcbEventReader; }; diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp index d7688be0ff..8097cce709 100644 --- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp @@ -216,6 +216,12 @@ void QXcbConnection::xi2SetupDevices() isTablet = true; tabletData.pointerType = QTabletEvent::Pen; dbgType = QLatin1String("pen"); + } else if (name.contains("waltop") && name.contains("tablet")) { + // other "Genius" tablets + // WALTOP International Corp. Slim Tablet + isTablet = true; + tabletData.pointerType = QTabletEvent::Pen; + dbgType = QLatin1String("pen"); } else { isTablet = false; } @@ -413,8 +419,10 @@ XInput2TouchDeviceData *QXcbConnection::touchDeviceForId(int id) hasRelativeCoords = true; dev->size.setHeight((vci->max - vci->min) * 1000.0 / vci->resolution); } else if (vci->label == atom(QXcbAtom::AbsX)) { + caps |= QTouchDevice::Position; dev->size.setHeight((vci->max - vci->min) * 1000.0 / vci->resolution); } else if (vci->label == atom(QXcbAtom::AbsY)) { + caps |= QTouchDevice::Position; dev->size.setWidth((vci->max - vci->min) * 1000.0 / vci->resolution); } break; @@ -459,82 +467,81 @@ static inline qreal fixed1616ToReal(FP1616 val) void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event) { - if (xi2PrepareXIGenericDeviceEvent(event, m_xiOpCode)) { - xXIGenericDeviceEvent *xiEvent = reinterpret_cast<xXIGenericDeviceEvent *>(event); - int sourceDeviceId = xiEvent->deviceid; // may be the master id - xXIDeviceEvent *xiDeviceEvent = 0; - QXcbWindowEventListener *eventListener = 0; + xi2PrepareXIGenericDeviceEvent(event); + xXIGenericDeviceEvent *xiEvent = reinterpret_cast<xXIGenericDeviceEvent *>(event); + int sourceDeviceId = xiEvent->deviceid; // may be the master id + xXIDeviceEvent *xiDeviceEvent = 0; + QXcbWindowEventListener *eventListener = 0; - switch (xiEvent->evtype) { - case XI_ButtonPress: - case XI_ButtonRelease: - case XI_Motion: + switch (xiEvent->evtype) { + case XI_ButtonPress: + case XI_ButtonRelease: + case XI_Motion: #ifdef XCB_USE_XINPUT22 - case XI_TouchBegin: - case XI_TouchUpdate: - case XI_TouchEnd: + case XI_TouchBegin: + case XI_TouchUpdate: + case XI_TouchEnd: #endif - { - xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(event); - eventListener = windowEventListenerFromId(xiDeviceEvent->event); - if (eventListener) { - long result = 0; - if (eventListener->handleGenericEvent(reinterpret_cast<xcb_generic_event_t *>(event), &result)) - return; - } - sourceDeviceId = xiDeviceEvent->sourceid; // use the actual device id instead of the master - break; - } - case XI_HierarchyChanged: - xi2HandleHierachyEvent(xiEvent); - return; - case XI_DeviceChanged: - xi2HandleDeviceChangedEvent(xiEvent); - return; - default: - break; + { + xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(event); + eventListener = windowEventListenerFromId(xiDeviceEvent->event); + if (eventListener) { + long result = 0; + if (eventListener->handleGenericEvent(reinterpret_cast<xcb_generic_event_t *>(event), &result)) + return; } + sourceDeviceId = xiDeviceEvent->sourceid; // use the actual device id instead of the master + break; + } + case XI_HierarchyChanged: + xi2HandleHierachyEvent(xiEvent); + return; + case XI_DeviceChanged: + xi2HandleDeviceChangedEvent(xiEvent); + return; + default: + break; + } #ifndef QT_NO_TABLETEVENT - for (int i = 0; i < m_tabletData.count(); ++i) { - if (m_tabletData.at(i).deviceId == sourceDeviceId) { - if (xi2HandleTabletEvent(xiEvent, &m_tabletData[i], eventListener)) - return; - } + for (int i = 0; i < m_tabletData.count(); ++i) { + if (m_tabletData.at(i).deviceId == sourceDeviceId) { + if (xi2HandleTabletEvent(xiEvent, &m_tabletData[i], eventListener)) + return; } + } #endif // QT_NO_TABLETEVENT #ifdef XCB_USE_XINPUT21 - QHash<int, ScrollingDevice>::iterator device = m_scrollingDevices.find(sourceDeviceId); - if (device != m_scrollingDevices.end()) - xi2HandleScrollEvent(xiEvent, device.value()); + QHash<int, ScrollingDevice>::iterator device = m_scrollingDevices.find(sourceDeviceId); + if (device != m_scrollingDevices.end()) + xi2HandleScrollEvent(xiEvent, device.value()); #endif // XCB_USE_XINPUT21 #ifdef XCB_USE_XINPUT22 - if (xiDeviceEvent) { - switch (xiDeviceEvent->evtype) { - case XI_ButtonPress: - case XI_ButtonRelease: - case XI_Motion: - if (xi2MouseEvents() && eventListener && !(xiDeviceEvent->flags & XIPointerEmulated)) - eventListener->handleXIMouseEvent(event); - break; + if (xiDeviceEvent) { + switch (xiDeviceEvent->evtype) { + case XI_ButtonPress: + case XI_ButtonRelease: + case XI_Motion: + if (xi2MouseEvents() && eventListener && !(xiDeviceEvent->flags & XIPointerEmulated)) + eventListener->handleXIMouseEvent(event); + break; - case XI_TouchBegin: - case XI_TouchUpdate: - case XI_TouchEnd: - if (Q_UNLIKELY(lcQpaXInput().isDebugEnabled())) - qCDebug(lcQpaXInput, "XI2 touch event type %d seq %d detail %d pos %6.1f, %6.1f root pos %6.1f, %6.1f on window %x", - event->event_type, xiDeviceEvent->sequenceNumber, xiDeviceEvent->detail, - fixed1616ToReal(xiDeviceEvent->event_x), fixed1616ToReal(xiDeviceEvent->event_y), - fixed1616ToReal(xiDeviceEvent->root_x), fixed1616ToReal(xiDeviceEvent->root_y),xiDeviceEvent->event); - if (QXcbWindow *platformWindow = platformWindowFromId(xiDeviceEvent->event)) - xi2ProcessTouch(xiDeviceEvent, platformWindow); - break; - } + case XI_TouchBegin: + case XI_TouchUpdate: + case XI_TouchEnd: + if (Q_UNLIKELY(lcQpaXInput().isDebugEnabled())) + qCDebug(lcQpaXInput, "XI2 touch event type %d seq %d detail %d pos %6.1f, %6.1f root pos %6.1f, %6.1f on window %x", + event->event_type, xiDeviceEvent->sequenceNumber, xiDeviceEvent->detail, + fixed1616ToReal(xiDeviceEvent->event_x), fixed1616ToReal(xiDeviceEvent->event_y), + fixed1616ToReal(xiDeviceEvent->root_x), fixed1616ToReal(xiDeviceEvent->root_y),xiDeviceEvent->event); + if (QXcbWindow *platformWindow = platformWindowFromId(xiDeviceEvent->event)) + xi2ProcessTouch(xiDeviceEvent, platformWindow); + break; } -#endif // XCB_USE_XINPUT22 } +#endif // XCB_USE_XINPUT22 } #ifdef XCB_USE_XINPUT22 @@ -562,10 +569,8 @@ void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindo } QWindowSystemInterface::TouchPoint &touchPoint = dev->touchPoints[xiDeviceEvent->detail]; QXcbScreen* screen = platformWindow->xcbScreen(); - QPointF pos = screen->mapFromNative(QPointF(fixed1616ToReal(xiDeviceEvent->root_x), - fixed1616ToReal(xiDeviceEvent->root_y))); - qreal x = pos.x(); - qreal y = pos.y(); + qreal x = fixed1616ToReal(xiDeviceEvent->root_x); + qreal y = fixed1616ToReal(xiDeviceEvent->root_y); qreal nx = -1.0, ny = -1.0, d = 0.0; for (int i = 0; i < dev->xiDeviceInfo->num_classes; ++i) { XIAnyClassInfo *classinfo = dev->xiDeviceInfo->classes[i]; @@ -872,9 +877,8 @@ void QXcbConnection::xi2HandleScrollEvent(void *event, ScrollingDevice &scrollin } } if (!angleDelta.isNull()) { - const int dpr = int(platformWindow->devicePixelRatio()); - QPoint local(fixed1616ToReal(xiDeviceEvent->event_x)/dpr, fixed1616ToReal(xiDeviceEvent->event_y)/dpr); - QPoint global(fixed1616ToReal(xiDeviceEvent->root_x)/dpr, fixed1616ToReal(xiDeviceEvent->root_y)/dpr); + QPoint local(fixed1616ToReal(xiDeviceEvent->event_x), fixed1616ToReal(xiDeviceEvent->event_y)); + QPoint global(fixed1616ToReal(xiDeviceEvent->root_x), fixed1616ToReal(xiDeviceEvent->root_y)); Qt::KeyboardModifiers modifiers = keyboard()->translateModifiers(xiDeviceEvent->mods.effective_mods); if (modifiers & Qt::AltModifier) { std::swap(angleDelta.rx(), angleDelta.ry()); @@ -900,9 +904,8 @@ void QXcbConnection::xi2HandleScrollEvent(void *event, ScrollingDevice &scrollin angleDelta.setX(-120); } if (!angleDelta.isNull()) { - const int dpr = int(platformWindow->devicePixelRatio()); - QPoint local(fixed1616ToReal(xiDeviceEvent->event_x)/dpr, fixed1616ToReal(xiDeviceEvent->event_y)/dpr); - QPoint global(fixed1616ToReal(xiDeviceEvent->root_x)/dpr, fixed1616ToReal(xiDeviceEvent->root_y)/dpr); + QPoint local(fixed1616ToReal(xiDeviceEvent->event_x), fixed1616ToReal(xiDeviceEvent->event_y)); + QPoint global(fixed1616ToReal(xiDeviceEvent->root_x), fixed1616ToReal(xiDeviceEvent->root_y)); Qt::KeyboardModifiers modifiers = keyboard()->translateModifiers(xiDeviceEvent->mods.effective_mods); if (modifiers & Qt::AltModifier) std::swap(angleDelta.rx(), angleDelta.ry()); @@ -1024,9 +1027,8 @@ bool QXcbConnection::xi2HandleTabletEvent(void *event, TabletData *tabletData, Q tabletData->inProximity = true; tabletData->tool = toolIdToTabletDevice(tool); tabletData->serialId = qint64(ptr[_WACSER_USB_ID]) << 32 | qint64(ptr[_WACSER_TOOL_SERIAL]); - QWindowSystemInterface::handleTabletEnterProximityEvent(tabletData->tool, - tabletData->pointerType, - tabletData->serialId); + QWindowSystemInterface::handleTabletEnterProximityEvent(ev->time, + tabletData->tool, tabletData->pointerType, tabletData->serialId); } else { tabletData->inProximity = false; tabletData->tool = toolIdToTabletDevice(ptr[_WACSER_LAST_TOOL_ID]); @@ -1035,9 +1037,8 @@ bool QXcbConnection::xi2HandleTabletEvent(void *event, TabletData *tabletData, Q if (!tabletData->tool) tabletData->tool = toolIdToTabletDevice(ptr[_WACSER_LAST_TOOL_SERIAL]); tabletData->serialId = qint64(ptr[_WACSER_USB_ID]) << 32 | qint64(ptr[_WACSER_LAST_TOOL_SERIAL]); - QWindowSystemInterface::handleTabletLeaveProximityEvent(tabletData->tool, - tabletData->pointerType, - tabletData->serialId); + QWindowSystemInterface::handleTabletLeaveProximityEvent(ev->time, + tabletData->tool, tabletData->pointerType, tabletData->serialId); } // TODO maybe have a hash of tabletData->deviceId to device data so we can // look up the tablet name here, and distinguish multiple tablets @@ -1115,13 +1116,14 @@ void QXcbConnection::xi2ReportTabletEvent(TabletData &tabletData, void *event) } if (Q_UNLIKELY(lcQpaXInput().isDebugEnabled())) - qCDebug(lcQpaXInput, "XI2 event on tablet %d with tool %d type %d seq %d detail %d pos %6.1f, %6.1f root pos %6.1f, %6.1f buttons 0x%x pressure %4.2lf tilt %d, %d rotation %6.2lf", - tabletData.deviceId, tabletData.tool, ev->evtype, ev->sequenceNumber, ev->detail, + qCDebug(lcQpaXInput, "XI2 event on tablet %d with tool %d type %d seq %d detail %d time %d " + "pos %6.1f, %6.1f root pos %6.1f, %6.1f buttons 0x%x pressure %4.2lf tilt %d, %d rotation %6.2lf", + tabletData.deviceId, tabletData.tool, ev->evtype, ev->sequenceNumber, ev->detail, ev->time, fixed1616ToReal(ev->event_x), fixed1616ToReal(ev->event_y), fixed1616ToReal(ev->root_x), fixed1616ToReal(ev->root_y), (int)tabletData.buttons, pressure, xTilt, yTilt, rotation); - QWindowSystemInterface::handleTabletEvent(window, local, global, + QWindowSystemInterface::handleTabletEvent(window, ev->time, local, global, tabletData.tool, tabletData.pointerType, tabletData.buttons, pressure, xTilt, yTilt, tangentialPressure, diff --git a/src/plugins/platforms/xcb/qxcbcursor.cpp b/src/plugins/platforms/xcb/qxcbcursor.cpp index bd880698e6..b321ed95dc 100644 --- a/src/plugins/platforms/xcb/qxcbcursor.cpp +++ b/src/plugins/platforms/xcb/qxcbcursor.cpp @@ -52,7 +52,7 @@ typedef char *(*PtrXcursorLibraryGetTheme)(void *); typedef int (*PtrXcursorLibrarySetTheme)(void *, const char *); typedef int (*PtrXcursorLibraryGetDefaultSize)(void *); -#ifdef XCB_USE_XLIB +#if defined(XCB_USE_XLIB) && !defined(QT_NO_LIBRARY) #include <X11/Xlib.h> enum { XCursorShape = CursorShape @@ -300,7 +300,7 @@ QXcbCursor::QXcbCursor(QXcbConnection *conn, QXcbScreen *screen) const char *cursorStr = "cursor"; xcb_open_font(xcb_connection(), cursorFont, strlen(cursorStr), cursorStr); -#ifdef XCB_USE_XLIB +#if defined(XCB_USE_XLIB) && !defined(QT_NO_LIBRARY) static bool function_ptrs_not_initialized = true; if (function_ptrs_not_initialized) { QLibrary xcursorLib(QLatin1String("Xcursor"), 1); @@ -491,7 +491,7 @@ xcb_cursor_t QXcbCursor::createNonStandardCursor(int cshape) return cursor; } -#ifdef XCB_USE_XLIB +#if defined(XCB_USE_XLIB) && !defined(QT_NO_LIBRARY) bool updateCursorTheme(void *dpy, const QByteArray &theme) { if (!ptrXcursorLibraryGetTheme || !ptrXcursorLibrarySetTheme) @@ -535,7 +535,7 @@ static xcb_cursor_t loadCursor(void *dpy, int cshape) } return cursor; } -#endif //XCB_USE_XLIB +#endif //XCB_USE_XLIB / QT_NO_LIBRARY xcb_cursor_t QXcbCursor::createFontCursor(int cshape) { @@ -544,7 +544,7 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape) xcb_cursor_t cursor = XCB_NONE; // Try Xcursor first -#ifdef XCB_USE_XLIB +#if defined(XCB_USE_XLIB) && !defined(QT_NO_LIBRARY) if (cshape >= 0 && cshape <= Qt::LastCursor) { void *dpy = connection()->xlib_display(); // special case for non-standard dnd-* cursors @@ -607,45 +607,47 @@ xcb_cursor_t QXcbCursor::createBitmapCursor(QCursor *cursor) } #endif -void QXcbCursor::queryPointer(QXcbConnection *c, xcb_window_t *rootWin, QPoint *pos, int *keybMask) +void QXcbCursor::queryPointer(QXcbConnection *c, QXcbVirtualDesktop **virtualDesktop, QPoint *pos, int *keybMask) { if (pos) *pos = QPoint(); - xcb_screen_iterator_t it = xcb_setup_roots_iterator(c->setup()); - while (it.rem) { - xcb_window_t root = it.data->root; - xcb_query_pointer_cookie_t cookie = xcb_query_pointer(c->xcb_connection(), root); - xcb_generic_error_t *err = 0; - xcb_query_pointer_reply_t *reply = xcb_query_pointer_reply(c->xcb_connection(), cookie, &err); - if (!err && reply) { - if (pos) - *pos = QPoint(reply->root_x, reply->root_y); - if (rootWin) - *rootWin = root; - if (keybMask) - *keybMask = reply->mask; - free(reply); - return; + + xcb_window_t root = c->primaryVirtualDesktop()->root(); + xcb_query_pointer_cookie_t cookie = xcb_query_pointer(c->xcb_connection(), root); + xcb_generic_error_t *err = 0; + xcb_query_pointer_reply_t *reply = xcb_query_pointer_reply(c->xcb_connection(), cookie, &err); + if (!err && reply) { + if (virtualDesktop) { + foreach (QXcbVirtualDesktop *vd, c->virtualDesktops()) { + if (vd->root() == reply->root) { + *virtualDesktop = vd; + break; + } + } } - free(err); + if (pos) + *pos = QPoint(reply->root_x, reply->root_y); + if (keybMask) + *keybMask = reply->mask; free(reply); - xcb_screen_next(&it); + return; } + free(err); + free(reply); } QPoint QXcbCursor::pos() const { QPoint p; queryPointer(connection(), 0, &p); - return m_screen->mapFromNative(p); + return p; } void QXcbCursor::setPos(const QPoint &pos) { - const QPoint xPos = m_screen->mapToNative(pos); - xcb_window_t root = 0; - queryPointer(connection(), &root, 0); - xcb_warp_pointer(xcb_connection(), XCB_NONE, root, 0, 0, 0, 0, xPos.x(), xPos.y()); + QXcbVirtualDesktop *virtualDesktop = Q_NULLPTR; + queryPointer(connection(), &virtualDesktop, 0); + xcb_warp_pointer(xcb_connection(), XCB_NONE, virtualDesktop->root(), 0, 0, 0, 0, pos.x(), pos.y()); xcb_flush(xcb_connection()); } diff --git a/src/plugins/platforms/xcb/qxcbcursor.h b/src/plugins/platforms/xcb/qxcbcursor.h index 7e5cdc6870..3c6dece1f2 100644 --- a/src/plugins/platforms/xcb/qxcbcursor.h +++ b/src/plugins/platforms/xcb/qxcbcursor.h @@ -75,7 +75,7 @@ public: QPoint pos() const Q_DECL_OVERRIDE; void setPos(const QPoint &pos) Q_DECL_OVERRIDE; - static void queryPointer(QXcbConnection *c, xcb_window_t *rootWin, QPoint *pos, int *keybMask = 0); + static void queryPointer(QXcbConnection *c, QXcbVirtualDesktop **virtualDesktop, QPoint *pos, int *keybMask = 0); private: #ifndef QT_NO_CURSOR @@ -90,7 +90,7 @@ private: #ifndef QT_NO_CURSOR CursorHash m_cursorHash; #endif -#ifdef XCB_USE_XLIB +#if defined(XCB_USE_XLIB) && !defined(QT_NO_LIBRARY) static void cursorThemePropertyChanged(QXcbVirtualDesktop *screen, const QByteArray &name, const QVariant &property, diff --git a/src/plugins/platforms/xcb/qxcbdrag.cpp b/src/plugins/platforms/xcb/qxcbdrag.cpp index 1d13adf851..d19ea241f1 100644 --- a/src/plugins/platforms/xcb/qxcbdrag.cpp +++ b/src/plugins/platforms/xcb/qxcbdrag.cpp @@ -39,6 +39,7 @@ #include "qxcbwindow.h" #include "qxcbscreen.h" #include "qwindow.h" +#include "qxcbcursor.h" #include <private/qdnd_p.h> #include <qdebug.h> #include <qevent.h> @@ -51,6 +52,7 @@ #include <private/qshapedpixmapdndwindow_p.h> #include <private/qsimpledrag_p.h> +#include <private/qhighdpiscaling_p.h> QT_BEGIN_NAMESPACE @@ -71,12 +73,16 @@ QT_BEGIN_NAMESPACE const int xdnd_version = 5; +static inline xcb_window_t xcb_window(QPlatformWindow *w) +{ + return static_cast<QXcbWindow *>(w)->xcb_window(); +} + static inline xcb_window_t xcb_window(QWindow *w) { return static_cast<QXcbWindow *>(w->handle())->xcb_window(); } - static xcb_window_t xdndProxy(QXcbConnection *c, xcb_window_t w) { xcb_window_t proxy = XCB_NONE; @@ -155,7 +161,7 @@ void QXcbDrag::init() source_time = XCB_CURRENT_TIME; target_time = XCB_CURRENT_TIME; - current_screen = 0; + QXcbCursor::queryPointer(connection(), ¤t_virtual_desktop, 0); drag_types.clear(); } @@ -185,6 +191,8 @@ void QXcbDrag::startDrag() xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, connection()->clipboard()->owner(), atom(QXcbAtom::XdndTypelist), XCB_ATOM_ATOM, 32, drag_types.size(), (const void *)drag_types.constData()); + + setUseCompositing(current_virtual_desktop->compositingActive()); QBasicDrag::startDrag(); } @@ -297,50 +305,29 @@ xcb_window_t QXcbDrag::findRealWindow(const QPoint & pos, xcb_window_t w, int md return 0; } -void QXcbDrag::move(const QMouseEvent *me) +void QXcbDrag::move(const QPoint &globalPos) { - // The mouse event is in the coordinate system of the window that started the drag. - // We do not know which window that was at this point, so we just use the device pixel ratio - // of the QGuiApplication. This will break once we support screens with different DPR. Fixing - // this properly requires some redesign of the drag and drop architecture. - static const int dpr = int(qApp->devicePixelRatio()); - QBasicDrag::move(me); - QPoint globalPos = me->globalPos(); if (source_sameanswer.contains(globalPos) && source_sameanswer.isValid()) return; - const QList<QXcbScreen *> &screens = connection()->screens(); - QXcbScreen *screen = connection()->primaryScreen(); - for (int i = 0; i < screens.size(); ++i) { - if (screens.at(i)->geometry().contains(globalPos)) { - screen = screens.at(i); - break; - } - } - if (screen != current_screen) { - // ### need to recreate the shaped pixmap window? -// int screen = QCursor::x11Screen(); -// if ((qt_xdnd_current_screen == -1 && screen != X11->defaultScreen) || (screen != qt_xdnd_current_screen)) { -// // recreate the pixmap on the new screen... -// delete xdnd_data.deco; -// QWidget* parent = object->source()->window()->x11Info().screen() == screen -// ? object->source()->window() : QApplication::desktop()->screen(screen); -// xdnd_data.deco = new QShapedPixmapWidget(parent); -// if (!QWidget::mouseGrabber()) { -// updatePixmap(); -// xdnd_data.deco->grabMouse(); -// } -// } -// xdnd_data.deco->move(QCursor::pos() - xdnd_data.deco->pm_hot); - current_screen = screen; - } + QXcbVirtualDesktop *virtualDesktop = Q_NULLPTR; + QPoint cursorPos; + QXcbCursor::queryPointer(connection(), &virtualDesktop, &cursorPos); + QXcbScreen *screen = virtualDesktop->screenAt(cursorPos); + QPoint deviceIndependentPos = QHighDpiScaling::mapPositionFromNative(globalPos, screen); + if (virtualDesktop != current_virtual_desktop) { + setUseCompositing(virtualDesktop->compositingActive()); + recreateShapedPixmapWindow(static_cast<QPlatformScreen*>(screen)->screen(), deviceIndependentPos); + current_virtual_desktop = virtualDesktop; + } else { + QBasicDrag::moveShapedPixmapWindow(deviceIndependentPos); + } -// qt_xdnd_current_screen = screen; - xcb_window_t rootwin = current_screen->root(); + xcb_window_t rootwin = current_virtual_desktop->root(); xcb_translate_coordinates_reply_t *translate = - ::translateCoordinates(connection(), rootwin, rootwin, globalPos.x() * dpr, globalPos.y() * dpr); + ::translateCoordinates(connection(), rootwin, rootwin, globalPos.x(), globalPos.y()); if (!translate) return; @@ -443,7 +430,7 @@ void QXcbDrag::move(const QMouseEvent *me) DEBUG() << "sending Xdnd enter source=" << enter.data.data32[0]; if (w) - handleEnter(w->window(), &enter); + handleEnter(w, &enter, current_proxy_target); else if (target) xcb_send_event(xcb_connection(), false, proxy_target, XCB_EVENT_MASK_NO_EVENT, (const char *)&enter); waiting_for_status = false; @@ -463,7 +450,7 @@ void QXcbDrag::move(const QMouseEvent *me) move.type = atom(QXcbAtom::XdndPosition); move.data.data32[0] = connection()->clipboard()->owner(); move.data.data32[1] = 0; // flags - move.data.data32[2] = (globalPos.x() * dpr << 16) + globalPos.y() * dpr; + move.data.data32[2] = (globalPos.x() << 16) + globalPos.y(); move.data.data32[3] = connection()->time(); move.data.data32[4] = toXdndAction(defaultAction(currentDrag()->supportedActions(), QGuiApplication::keyboardModifiers())); DEBUG() << "sending Xdnd position source=" << move.data.data32[0] << "target=" << move.window; @@ -471,15 +458,15 @@ void QXcbDrag::move(const QMouseEvent *me) source_time = connection()->time(); if (w) - handle_xdnd_position(w->window(), &move); + handle_xdnd_position(w, &move); else xcb_send_event(xcb_connection(), false, proxy_target, XCB_EVENT_MASK_NO_EVENT, (const char *)&move); } } -void QXcbDrag::drop(const QMouseEvent *event) +void QXcbDrag::drop(const QPoint &globalPos) { - QBasicDrag::drop(event); + QBasicDrag::drop(globalPos); if (!current_target) return; @@ -505,7 +492,7 @@ void QXcbDrag::drop(const QMouseEvent *event) connection()->time(), current_target, current_proxy_target, - (w ? w->window() : 0), + w, // current_embeddig_widget, currentDrag(), QTime::currentTime() @@ -518,7 +505,7 @@ void QXcbDrag::drop(const QMouseEvent *event) } if (w) { - handleDrop(w->window(), &drop); + handleDrop(w, &drop); } else { xcb_send_event(xcb_connection(), false, current_proxy_target, XCB_EVENT_MASK_NO_EVENT, (const char *)&drop); } @@ -664,7 +651,7 @@ static bool checkEmbedded(QWidget* w, const XEvent* xe) #endif -void QXcbDrag::handleEnter(QWindow *window, const xcb_client_message_event_t *event) +void QXcbDrag::handleEnter(QPlatformWindow *window, const xcb_client_message_event_t *event, xcb_window_t proxy) { Q_UNUSED(window); DEBUG() << "handleEnter" << window; @@ -676,6 +663,9 @@ void QXcbDrag::handleEnter(QWindow *window, const xcb_client_message_event_t *ev return; xdnd_dragsource = event->data.data32[0]; + if (!proxy) + proxy = xdndProxy(connection(), xdnd_dragsource); + current_proxy_target = proxy ? proxy : xdnd_dragsource; if (event->data.data32[1] & 1) { // get the types from XdndTypeList @@ -689,6 +679,7 @@ void QXcbDrag::handleEnter(QWindow *window, const xcb_client_message_event_t *ev length = xdnd_max_type; xcb_atom_t *atoms = (xcb_atom_t *)xcb_get_property_value(reply); + xdnd_types.reserve(length); for (int i = 0; i < length; ++i) xdnd_types.append(atoms[i]); } @@ -704,17 +695,14 @@ void QXcbDrag::handleEnter(QWindow *window, const xcb_client_message_event_t *ev DEBUG() << " " << connection()->atomName(xdnd_types.at(i)); } -void QXcbDrag::handle_xdnd_position(QWindow *w, const xcb_client_message_event_t *e) +void QXcbDrag::handle_xdnd_position(QPlatformWindow *w, const xcb_client_message_event_t *e) { QPoint p((e->data.data32[2] & 0xffff0000) >> 16, e->data.data32[2] & 0x0000ffff); Q_ASSERT(w); QRect geometry = w->geometry(); - const int dpr = int(w->handle()->devicePixelRatio()); - - p /= dpr; p -= geometry.topLeft(); - if (!w || (w->type() == Qt::Desktop)) + if (!w || !w->window() || (w->window()->type() == Qt::Desktop)) return; if (e->data.data32[0] != xdnd_dragsource) { @@ -723,7 +711,7 @@ void QXcbDrag::handle_xdnd_position(QWindow *w, const xcb_client_message_event_t } currentPosition = p; - currentWindow = w; + currentWindow = w->window(); // timestamp from the source if (e->data.data32[3] != XCB_NONE) { @@ -740,7 +728,7 @@ void QXcbDrag::handle_xdnd_position(QWindow *w, const xcb_client_message_event_t supported_actions = Qt::DropActions(toDropAction(e->data.data32[4])); } - QPlatformDragQtResponse qt_response = QWindowSystemInterface::handleDrag(w,dropData,p,supported_actions); + QPlatformDragQtResponse qt_response = QWindowSystemInterface::handleDrag(w->window(),dropData,p,supported_actions); QRect answerRect(p + geometry.topLeft(), QSize(1,1)); answerRect = qt_response.answerRect().translated(geometry.topLeft()).intersected(geometry); @@ -776,7 +764,7 @@ void QXcbDrag::handle_xdnd_position(QWindow *w, const xcb_client_message_event_t if (xdnd_dragsource == connection()->clipboard()->owner()) handle_xdnd_status(&response); else - Q_XCB_CALL(xcb_send_event(xcb_connection(), false, xdnd_dragsource, + Q_XCB_CALL(xcb_send_event(xcb_connection(), false, current_proxy_target, XCB_EVENT_MASK_NO_EVENT, (const char *)&response)); } @@ -796,7 +784,7 @@ namespace }; } -void QXcbDrag::handlePosition(QWindow * w, const xcb_client_message_event_t *event) +void QXcbDrag::handlePosition(QPlatformWindow * w, const xcb_client_message_event_t *event) { xcb_client_message_event_t *lastEvent = const_cast<xcb_client_message_event_t *>(event); xcb_generic_event_t *nextEvent; @@ -830,12 +818,10 @@ void QXcbDrag::handle_xdnd_status(const xcb_client_message_event_t *event) updateCursor(Qt::IgnoreAction); } - static const int dpr = int(qApp->devicePixelRatio()); - if ((event->data.data32[1] & 2) == 0) { QPoint p((event->data.data32[2] & 0xffff0000) >> 16, event->data.data32[2] & 0x0000ffff); QSize s((event->data.data32[3] & 0xffff0000) >> 16, event->data.data32[3] & 0x0000ffff); - source_sameanswer = QRect(p / dpr, s / dpr); + source_sameanswer = QRect(p, s); } else { source_sameanswer = QRect(); } @@ -861,10 +847,10 @@ void QXcbDrag::handleStatus(const xcb_client_message_event_t *event) DEBUG("xdndHandleStatus end"); } -void QXcbDrag::handleLeave(QWindow *w, const xcb_client_message_event_t *event) +void QXcbDrag::handleLeave(QPlatformWindow *w, const xcb_client_message_event_t *event) { DEBUG("xdnd leave"); - if (!currentWindow || w != currentWindow.data()) + if (!currentWindow || w != currentWindow.data()->handle()) return; // sanity // ### @@ -879,7 +865,7 @@ void QXcbDrag::handleLeave(QWindow *w, const xcb_client_message_event_t *event) DEBUG("xdnd drag leave from unexpected source (%x not %x", event->data.data32[0], xdnd_dragsource); } - QWindowSystemInterface::handleDrag(w,0,QPoint(),Qt::IgnoreAction); + QWindowSystemInterface::handleDrag(w->window(),0,QPoint(),Qt::IgnoreAction); xdnd_dragsource = 0; xdnd_types.clear(); @@ -909,7 +895,7 @@ void QXcbDrag::send_leave() w = 0; if (w) - handleLeave(w->window(), (const xcb_client_message_event_t *)&leave); + handleLeave(w, (const xcb_client_message_event_t *)&leave); else xcb_send_event(xcb_connection(), false,current_proxy_target, XCB_EVENT_MASK_NO_EVENT, (const char *)&leave); @@ -920,7 +906,7 @@ void QXcbDrag::send_leave() waiting_for_status = false; } -void QXcbDrag::handleDrop(QWindow *, const xcb_client_message_event_t *event) +void QXcbDrag::handleDrop(QPlatformWindow *, const xcb_client_message_event_t *event) { DEBUG("xdndHandleDrop"); if (!currentWindow) { @@ -970,7 +956,7 @@ void QXcbDrag::handleDrop(QWindow *, const xcb_client_message_event_t *event) finished.data.data32[0] = currentWindow ? xcb_window(currentWindow.data()) : XCB_NONE; finished.data.data32[1] = response.isAccepted(); // flags finished.data.data32[2] = toXdndAction(response.acceptedAction()); - Q_XCB_CALL(xcb_send_event(xcb_connection(), false, xdnd_dragsource, + Q_XCB_CALL(xcb_send_event(xcb_connection(), false, current_proxy_target, XCB_EVENT_MASK_NO_EVENT, (char *)&finished)); xdnd_dragsource = 0; @@ -1135,7 +1121,11 @@ void QXcbDrag::handleSelectionRequest(const xcb_selection_request_event_t *event } } - xcb_send_event(xcb_connection(), false, event->requestor, XCB_EVENT_MASK_NO_EVENT, (const char *)¬ify); + xcb_window_t proxy_target = xdndProxy(connection(), event->requestor); + if (!proxy_target) + proxy_target = event->requestor; + + xcb_send_event(xcb_connection(), false, proxy_target, XCB_EVENT_MASK_NO_EVENT, (const char *)¬ify); } diff --git a/src/plugins/platforms/xcb/qxcbdrag.h b/src/plugins/platforms/xcb/qxcbdrag.h index 95da76b732..c9d257906f 100644 --- a/src/plugins/platforms/xcb/qxcbdrag.h +++ b/src/plugins/platforms/xcb/qxcbdrag.h @@ -53,8 +53,8 @@ QT_BEGIN_NAMESPACE #ifndef QT_NO_DRAGANDDROP -class QMouseEvent; class QWindow; +class QPlatformWindow; class QXcbConnection; class QXcbWindow; class QXcbDropData; @@ -72,14 +72,14 @@ public: void startDrag() Q_DECL_OVERRIDE; void cancel() Q_DECL_OVERRIDE; - void move(const QMouseEvent *me) Q_DECL_OVERRIDE; - void drop(const QMouseEvent *me) Q_DECL_OVERRIDE; + void move(const QPoint &globalPos) Q_DECL_OVERRIDE; + void drop(const QPoint &globalPos) Q_DECL_OVERRIDE; void endDrag() Q_DECL_OVERRIDE; - void handleEnter(QWindow *window, const xcb_client_message_event_t *event); - void handlePosition(QWindow *w, const xcb_client_message_event_t *event); - void handleLeave(QWindow *w, const xcb_client_message_event_t *event); - void handleDrop(QWindow *, const xcb_client_message_event_t *event); + void handleEnter(QPlatformWindow *window, const xcb_client_message_event_t *event, xcb_window_t proxy = 0); + void handlePosition(QPlatformWindow *w, const xcb_client_message_event_t *event); + void handleLeave(QPlatformWindow *w, const xcb_client_message_event_t *event); + void handleDrop(QPlatformWindow *, const xcb_client_message_event_t *event); void handleStatus(const xcb_client_message_event_t *event); void handleSelectionRequest(const xcb_selection_request_event_t *event); @@ -99,7 +99,7 @@ private: void init(); - void handle_xdnd_position(QWindow *w, const xcb_client_message_event_t *event); + void handle_xdnd_position(QPlatformWindow *w, const xcb_client_message_event_t *event); void handle_xdnd_status(const xcb_client_message_event_t *event); void send_leave(); @@ -133,7 +133,7 @@ private: // window to send events to (always valid if current_target) xcb_window_t current_proxy_target; - QXcbScreen *current_screen; + QXcbVirtualDesktop *current_virtual_desktop; // 10 minute timer used to discard old XdndDrop transactions enum { XdndDropTransactionTimeout = 600000 }; @@ -146,7 +146,7 @@ private: xcb_timestamp_t timestamp; xcb_window_t target; xcb_window_t proxy_target; - QWindow *targetWindow; + QPlatformWindow *targetWindow; // QWidget *embedding_widget; QPointer<QDrag> drag; QTime time; diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp index fc06f1a7b0..9cedfa77ad 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.cpp +++ b/src/plugins/platforms/xcb/qxcbintegration.cpp @@ -268,7 +268,10 @@ void QXcbIntegration::initialize() { // Perform everything that may potentially need the event dispatcher (timers, socket // notifiers) here instead of the constructor. - m_inputContext.reset(QPlatformInputContextFactory::create()); + QString icStr = QPlatformInputContextFactory::requested(); + if (icStr.isNull()) + icStr = QLatin1String("compose"); + m_inputContext.reset(QPlatformInputContextFactory::create(icStr)); } void QXcbIntegration::moveToScreen(QWindow *window, int screen) diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.cpp b/src/plugins/platforms/xcb/qxcbkeyboard.cpp index ea541e4556..2e088d3ca5 100644 --- a/src/plugins/platforms/xcb/qxcbkeyboard.cpp +++ b/src/plugins/platforms/xcb/qxcbkeyboard.cpp @@ -1545,11 +1545,13 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type, QString QXcbKeyboard::lookupString(struct xkb_state *state, xcb_keycode_t code) const { - QByteArray chars; - chars.resize(1 + xkb_state_key_get_utf8(state, code, 0, 0)); - // equivalent of XLookupString - xkb_state_key_get_utf8(state, code, chars.data(), chars.size()); - return QString::fromUtf8(chars); + QVarLengthArray<char, 32> chars(32); + const int size = xkb_state_key_get_utf8(state, code, chars.data(), chars.size()); + if (Q_UNLIKELY(size + 1 > chars.size())) { // +1 for NUL + chars.resize(size + 1); + xkb_state_key_get_utf8(state, code, chars.data(), chars.size()); + } + return QString::fromUtf8(chars.constData(), size); } void QXcbKeyboard::handleKeyPressEvent(const xcb_key_press_event_t *event) diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp index 8bf9003af7..dfb0a125e2 100644 --- a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp +++ b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp @@ -47,6 +47,7 @@ #include <QtGui/qscreen.h> #include <QtPlatformHeaders/qxcbwindowfunctions.h> +#include <QtPlatformHeaders/qxcbintegrationfunctions.h> #ifndef QT_NO_DBUS #include "QtPlatformSupport/private/qdbusmenuconnection_p.h" @@ -76,7 +77,8 @@ static int resourceType(const QByteArray &key) QByteArrayLiteral("gettimestamp"), QByteArrayLiteral("x11screen"), QByteArrayLiteral("rootwindow"), QByteArrayLiteral("subpixeltype"), QByteArrayLiteral("antialiasingEnabled"), - QByteArrayLiteral("nofonthinting") + QByteArrayLiteral("nofonthinting"), + QByteArrayLiteral("atspibus") }; const QByteArray *end = names + sizeof(names) / sizeof(names[0]); const QByteArray *result = std::find(names, end, key); @@ -85,8 +87,7 @@ static int resourceType(const QByteArray &key) QXcbNativeInterface::QXcbNativeInterface() : m_genericEventFilterType(QByteArrayLiteral("xcb_generic_event_t")), - m_sysTraySelectionAtom(XCB_ATOM_NONE), - m_systrayVisualId(XCB_NONE) + m_sysTraySelectionAtom(XCB_ATOM_NONE) { } @@ -117,22 +118,12 @@ bool QXcbNativeInterface::systemTrayAvailable(const QScreen *screen) const bool QXcbNativeInterface::requestSystemTrayWindowDock(const QWindow *window) { - const QPlatformWindow *platformWindow = window->handle(); - if (!platformWindow) - return false; - QXcbSystemTrayTracker *trayTracker = systemTrayTracker(window->screen()); - if (!trayTracker) - return false; - trayTracker->requestSystemTrayWindowDock(static_cast<const QXcbWindow *>(platformWindow)->xcb_window()); - return true; + return QXcbWindow::requestSystemTrayWindowDockStatic(window); } QRect QXcbNativeInterface::systemTrayWindowGlobalGeometry(const QWindow *window) { - if (const QPlatformWindow *platformWindow = window->handle()) - if (const QXcbSystemTrayTracker *trayTracker = systemTrayTracker(window->screen())) - return trayTracker->systemTrayWindowGlobalGeometry(static_cast<const QXcbWindow *>(platformWindow)->xcb_window()); - return QRect(); + return QXcbWindow::systemTrayWindowGlobalGeometryStatic(window); } xcb_window_t QXcbNativeInterface::locateSystemTray(xcb_connection_t *conn, const QXcbScreen *screen) @@ -163,54 +154,14 @@ xcb_window_t QXcbNativeInterface::locateSystemTray(xcb_connection_t *conn, const return selection_window; } -bool QXcbNativeInterface::systrayVisualHasAlphaChannel() { - const QXcbScreen *screen = static_cast<QXcbScreen *>(QGuiApplication::primaryScreen()->handle()); - - if (m_systrayVisualId == XCB_NONE) { - xcb_connection_t *xcb_conn = screen->xcb_connection(); - xcb_atom_t tray_atom = screen->atom(QXcbAtom::_NET_SYSTEM_TRAY_VISUAL); - - xcb_window_t systray_window = locateSystemTray(xcb_conn, screen); - if (systray_window == XCB_WINDOW_NONE) - return false; - - // Get the xcb property for the _NET_SYSTEM_TRAY_VISUAL atom - xcb_get_property_cookie_t systray_atom_cookie; - xcb_get_property_reply_t *systray_atom_reply; - - systray_atom_cookie = xcb_get_property_unchecked(xcb_conn, false, systray_window, - tray_atom, XCB_ATOM_VISUALID, 0, 1); - systray_atom_reply = xcb_get_property_reply(xcb_conn, systray_atom_cookie, 0); - - if (!systray_atom_reply) - return false; - - if (systray_atom_reply->value_len > 0 && xcb_get_property_value_length(systray_atom_reply) > 0) { - xcb_visualid_t * vids = (uint32_t *)xcb_get_property_value(systray_atom_reply); - m_systrayVisualId = vids[0]; - } - - free(systray_atom_reply); - } - - if (m_systrayVisualId != XCB_NONE) { - quint8 depth = screen->depthOfVisual(m_systrayVisualId); - return depth == 32; - } else { - return false; - } +bool QXcbNativeInterface::systrayVisualHasAlphaChannel() +{ + return QXcbConnection::xEmbedSystemTrayVisualHasAlphaChannel(); } -void QXcbNativeInterface::setParentRelativeBackPixmap(const QWindow *qwindow) +void QXcbNativeInterface::setParentRelativeBackPixmap(QWindow *window) { - if (const QPlatformWindow *platformWindow = qwindow->handle()) { - const QXcbWindow *qxwindow = static_cast<const QXcbWindow *>(platformWindow); - xcb_connection_t *xcb_conn = qxwindow->xcb_connection(); - - const quint32 mask = XCB_CW_BACK_PIXMAP; - const quint32 values[] = { XCB_BACK_PIXMAP_PARENT_RELATIVE }; - Q_XCB_CALL(xcb_change_window_attributes(xcb_conn, qxwindow->xcb_window(), mask, values)); - } + QXcbWindow::setParentRelativeBackPixmapStatic(window); } void *QXcbNativeInterface::nativeResourceForIntegration(const QByteArray &resourceString) @@ -233,6 +184,9 @@ void *QXcbNativeInterface::nativeResourceForIntegration(const QByteArray &resour case Display: result = display(); break; + case AtspiBus: + result = atspiBus(); + break; case Connection: result = connection(); break; @@ -294,6 +248,9 @@ void *QXcbNativeInterface::nativeResourceForScreen(const QByteArray &resourceStr case NoFontHinting: result = xcbScreen->noFontHinting() ? this : 0; //qboolptr... break; + case RootWindow: + result = reinterpret_cast<void *>(xcbScreen->root()); + break; default: break; } @@ -389,9 +346,24 @@ QFunctionPointer QXcbNativeInterface::platformFunction(const QByteArray &functio return func; //case sensitive - if (function == QXcbWindowFunctions::setWmWindowTypeIdentifier()) { - return QFunctionPointer(QXcbWindow::setWmWindowTypeStatic); - } + if (function == QXcbWindowFunctions::setWmWindowTypeIdentifier()) + return QFunctionPointer(QXcbWindowFunctions::SetWmWindowType(QXcbWindow::setWmWindowTypeStatic)); + + if (function == QXcbWindowFunctions::setWmWindowIconTextIdentifier()) + return QFunctionPointer(QXcbWindowFunctions::SetWmWindowIconText(QXcbWindow::setWindowIconTextStatic)); + + if (function == QXcbWindowFunctions::setParentRelativeBackPixmapIdentifier()) + return QFunctionPointer(QXcbWindowFunctions::SetParentRelativeBackPixmap(QXcbWindow::setParentRelativeBackPixmapStatic)); + + if (function == QXcbWindowFunctions::requestSystemTrayWindowDockIdentifier()) + return QFunctionPointer(QXcbWindowFunctions::RequestSystemTrayWindowDock(QXcbWindow::requestSystemTrayWindowDockStatic)); + + if (function == QXcbWindowFunctions::systemTrayWindowGlobalGeometryIdentifier()) + return QFunctionPointer(QXcbWindowFunctions::SystemTrayWindowGlobalGeometry(QXcbWindow::systemTrayWindowGlobalGeometryStatic)); + + if (function == QXcbIntegrationFunctions::xEmbedSystemTrayVisualHasAlphaChannelIdentifier()) + return QFunctionPointer(QXcbIntegrationFunctions::XEmbedSystemTrayVisualHasAlphaChannel(QXcbConnection::xEmbedSystemTrayVisualHasAlphaChannel)); + if (function == QXcbWindowFunctions::visualIdIdentifier()) { return QFunctionPointer(QXcbWindowFunctions::VisualId(QXcbWindow::visualIdStatic)); } @@ -466,6 +438,27 @@ void *QXcbNativeInterface::connection() return integration->defaultConnection()->xcb_connection(); } +void *QXcbNativeInterface::atspiBus() +{ + QXcbIntegration *integration = static_cast<QXcbIntegration *>(QGuiApplicationPrivate::platformIntegration()); + QXcbConnection *defaultConnection = integration->defaultConnection(); + if (defaultConnection) { + xcb_atom_t atspiBusAtom = defaultConnection->internAtom("AT_SPI_BUS"); + xcb_get_property_cookie_t cookie = Q_XCB_CALL(xcb_get_property(defaultConnection->xcb_connection(), false, + defaultConnection->rootWindow(), + atspiBusAtom, + XCB_ATOM_STRING, 0, 128)); + xcb_get_property_reply_t *reply = Q_XCB_CALL(xcb_get_property_reply(defaultConnection->xcb_connection(), cookie, 0)); + Q_ASSERT(!reply->bytes_after); + char *data = (char *)xcb_get_property_value(reply); + int length = xcb_get_property_value_length(reply); + QByteArray *busAddress = new QByteArray(data, length); + free(reply); + return busAddress; + } + return 0; +} + void QXcbNativeInterface::setAppTime(QScreen* screen, xcb_timestamp_t time) { if (screen) { diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.h b/src/plugins/platforms/xcb/qxcbnativeinterface.h index 721c6f4b1d..f88b710864 100644 --- a/src/plugins/platforms/xcb/qxcbnativeinterface.h +++ b/src/plugins/platforms/xcb/qxcbnativeinterface.h @@ -67,7 +67,8 @@ public: RootWindow, ScreenSubpixelType, ScreenAntialiasingEnabled, - NoFontHinting + NoFontHinting, + AtspiBus }; QXcbNativeInterface(); @@ -98,6 +99,7 @@ public: void *x11Screen(); void *rootWindow(); void *display(); + void *atspiBus(); void *connection(); static void setStartupId(const char *); static void setAppTime(QScreen *screen, xcb_timestamp_t time); @@ -105,7 +107,7 @@ public: Q_INVOKABLE void beep(); Q_INVOKABLE bool systemTrayAvailable(const QScreen *screen) const; - Q_INVOKABLE void setParentRelativeBackPixmap(const QWindow *window); + Q_INVOKABLE void setParentRelativeBackPixmap(QWindow *window); Q_INVOKABLE bool systrayVisualHasAlphaChannel(); Q_INVOKABLE bool requestSystemTrayWindowDock(const QWindow *window); Q_INVOKABLE QRect systemTrayWindowGlobalGeometry(const QWindow *window); @@ -121,7 +123,6 @@ private: const QByteArray m_genericEventFilterType; xcb_atom_t m_sysTraySelectionAtom; - xcb_visualid_t m_systrayVisualId; static QXcbScreen *qPlatformScreenForWindow(QWindow *window); diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp index 3dcd6a713a..0e99d58679 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.cpp +++ b/src/plugins/platforms/xcb/qxcbscreen.cpp @@ -44,6 +44,7 @@ #include <qpa/qwindowsysteminterface.h> #include <private/qmath_p.h> +#include <QtGui/private/qhighdpiscaling_p.h> QT_BEGIN_NAMESPACE @@ -53,6 +54,12 @@ QXcbVirtualDesktop::QXcbVirtualDesktop(QXcbConnection *connection, xcb_screen_t , m_number(number) , m_xSettings(Q_NULLPTR) { + QByteArray cmAtomName("_NET_WM_CM_S"); + cmAtomName += QByteArray::number(m_number); + m_net_wm_cm_atom = connection->internAtom(cmAtomName.constData()); + m_compositingActive = connection->getSelectionOwner(m_net_wm_cm_atom); + + m_workArea = getWorkArea(); } QXcbVirtualDesktop::~QXcbVirtualDesktop() @@ -60,6 +67,20 @@ QXcbVirtualDesktop::~QXcbVirtualDesktop() delete m_xSettings; } +QXcbScreen *QXcbVirtualDesktop::screenAt(const QPoint &pos) const +{ + foreach (QXcbScreen *screen, connection()->screens()) { + if (screen->virtualDesktop() == this && screen->nativeGeometry().contains(pos)) + return screen; + } + return Q_NULLPTR; +} + +void QXcbVirtualDesktop::addScreen(QPlatformScreen *s) +{ + ((QXcbScreen *) s)->isPrimary() ? m_screens.prepend(s) : m_screens.append(s); +} + QXcbXSettings *QXcbVirtualDesktop::xSettings() const { if (!m_xSettings) { @@ -69,6 +90,64 @@ QXcbXSettings *QXcbVirtualDesktop::xSettings() const return m_xSettings; } +bool QXcbVirtualDesktop::compositingActive() const +{ + if (connection()->hasXFixes()) + return m_compositingActive; + else + return connection()->getSelectionOwner(m_net_wm_cm_atom); +} + +void QXcbVirtualDesktop::handleXFixesSelectionNotify(xcb_xfixes_selection_notify_event_t *notify_event) +{ + if (notify_event->selection == m_net_wm_cm_atom) + m_compositingActive = notify_event->owner; +} + +void QXcbVirtualDesktop::subscribeToXFixesSelectionNotify() +{ + if (connection()->hasXFixes()) { + const uint32_t mask = XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER | + XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY | + XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE; + Q_XCB_CALL(xcb_xfixes_select_selection_input_checked(xcb_connection(), connection()->getQtSelectionOwner(), m_net_wm_cm_atom, mask)); + } +} + +QRect QXcbVirtualDesktop::getWorkArea() const +{ + QRect r; + xcb_get_property_reply_t * workArea = + xcb_get_property_reply(xcb_connection(), + xcb_get_property_unchecked(xcb_connection(), false, screen()->root, + atom(QXcbAtom::_NET_WORKAREA), + XCB_ATOM_CARDINAL, 0, 1024), NULL); + if (workArea && workArea->type == XCB_ATOM_CARDINAL && workArea->format == 32 && workArea->value_len >= 4) { + // If workArea->value_len > 4, the remaining ones seem to be for WM's virtual desktops + // (don't mess with QXcbVirtualDesktop which represents an X screen). + // But QScreen doesn't know about that concept. In reality there could be a + // "docked" panel (with _NET_WM_STRUT_PARTIAL atom set) on just one desktop. + // But for now just assume the first 4 values give us the geometry of the + // "work area", AKA "available geometry" + uint32_t *geom = (uint32_t*)xcb_get_property_value(workArea); + r = QRect(geom[0], geom[1], geom[2], geom[3]); + } else { + r = QRect(QPoint(), size()); + } + free(workArea); + return r; +} + +void QXcbVirtualDesktop::updateWorkArea() +{ + QRect workArea = getWorkArea(); + if (m_workArea != workArea) { + m_workArea = workArea; + foreach (QPlatformScreen *screen, m_screens) + ((QXcbScreen *)screen)->updateAvailableGeometry(); + } +} + QXcbScreen::QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDesktop, xcb_randr_output_t outputId, xcb_randr_get_output_info_reply_t *output, QString outputName) @@ -86,7 +165,7 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDe , m_orientation(Qt::PrimaryOrientation) , m_refreshRate(60) , m_forcedDpi(-1) - , m_devicePixelRatio(1) + , m_pixelDensity(1) , m_hintStyle(QFontEngine::HintStyle(-1)) , m_noFontHinting(false) , m_subpixelType(QFontEngine::SubpixelAntialiasingType(-1)) @@ -107,9 +186,8 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDe updateGeometry(output ? output->timestamp : 0); } - const int dpr = int(devicePixelRatio()); if (m_geometry.isEmpty()) { - m_geometry = QRect(QPoint(), m_virtualSize/dpr); + m_geometry = QRect(QPoint(), m_virtualSize); m_nativeGeometry = QRect(QPoint(), m_virtualSize); } if (m_availableGeometry.isEmpty()) @@ -117,12 +195,6 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDe readXResources(); - // disable font hinting when we do UI scaling - static bool dpr_scaling_enabled = (qgetenv("QT_DEVICE_PIXEL_RATIO").toInt() > 1 - || qgetenv("QT_DEVICE_PIXEL_RATIO").toLower() == "auto"); - if (dpr_scaling_enabled) - m_noFontHinting = true; - QScopedPointer<xcb_get_window_attributes_reply_t, QScopedPointerPodDeleter> rootAttribs( xcb_get_window_attributes_reply(xcb_connection(), xcb_get_window_attributes_unchecked(xcb_connection(), screen()->root), NULL)); @@ -201,9 +273,8 @@ QWindow *QXcbScreen::topLevelAt(const QPoint &p) const { xcb_window_t root = screen()->root; - int dpr = int(devicePixelRatio()); - int x = p.x() / dpr; - int y = p.y() / dpr; + int x = p.x(); + int y = p.y(); xcb_window_t parent = root; xcb_window_t child = root; @@ -237,43 +308,6 @@ QWindow *QXcbScreen::topLevelAt(const QPoint &p) const return 0; } - -QPoint QXcbScreen::mapToNative(const QPoint &pos) const -{ - const int dpr = int(devicePixelRatio()); - return (pos - m_geometry.topLeft()) * dpr + m_nativeGeometry.topLeft(); -} - -QPoint QXcbScreen::mapFromNative(const QPoint &pos) const -{ - const int dpr = int(devicePixelRatio()); - return (pos - m_nativeGeometry.topLeft()) / dpr + m_geometry.topLeft(); -} - -QPointF QXcbScreen::mapToNative(const QPointF &pos) const -{ - const int dpr = int(devicePixelRatio()); - return (pos - m_geometry.topLeft()) * dpr + m_nativeGeometry.topLeft(); -} - -QPointF QXcbScreen::mapFromNative(const QPointF &pos) const -{ - const int dpr = int(devicePixelRatio()); - return (pos - m_nativeGeometry.topLeft()) / dpr + m_geometry.topLeft(); -} - -QRect QXcbScreen::mapToNative(const QRect &rect) const -{ - const int dpr = int(devicePixelRatio()); - return QRect(mapToNative(rect.topLeft()), rect.size() * dpr); -} - -QRect QXcbScreen::mapFromNative(const QRect &rect) const -{ - const int dpr = int(devicePixelRatio()); - return QRect(mapFromNative(rect.topLeft()), rect.size() / dpr); -} - void QXcbScreen::windowShown(QXcbWindow *window) { // Freedesktop.org Startup Notification @@ -335,29 +369,22 @@ QDpi QXcbScreen::virtualDpi() const Q_MM_PER_INCH * m_virtualSize.height() / m_virtualSizeMillimeters.height()); } + QDpi QXcbScreen::logicalDpi() const { static const int overrideDpi = qEnvironmentVariableIntValue("QT_FONT_DPI"); if (overrideDpi) return QDpi(overrideDpi, overrideDpi); - int primaryDpr = int(connection()->screens().at(0)->devicePixelRatio()); - if (m_forcedDpi > 0) - return QDpi(m_forcedDpi/primaryDpr, m_forcedDpi/primaryDpr); - QDpi vDpi = virtualDpi(); - return QDpi(vDpi.first/primaryDpr, vDpi.second/primaryDpr); + if (m_forcedDpi > 0) { + return QDpi(m_forcedDpi, m_forcedDpi); + } + return virtualDpi(); } - -qreal QXcbScreen::devicePixelRatio() const +qreal QXcbScreen::pixelDensity() const { - static int override_dpr = qEnvironmentVariableIntValue("QT_DEVICE_PIXEL_RATIO"); - static bool auto_dpr = qgetenv("QT_DEVICE_PIXEL_RATIO").toLower() == "auto"; - if (override_dpr > 0) - return override_dpr; - if (auto_dpr) - return m_devicePixelRatio; - return 1.0; + return m_pixelDensity; } QPlatformCursor *QXcbScreen::cursor() const @@ -466,7 +493,6 @@ void QXcbScreen::updateGeometry(xcb_timestamp_t timestamp) void QXcbScreen::updateGeometry(const QRect &geom, uint8_t rotation) { QRect xGeometry = geom; - QRect xAvailableGeometry = xGeometry; switch (rotation) { case XCB_RANDR_ROTATION_ROTATE_0: // xrandr --rotate normal m_orientation = Qt::LandscapeOrientation; @@ -495,35 +521,23 @@ void QXcbScreen::updateGeometry(const QRect &geom, uint8_t rotation) Q_MM_PER_INCH * xGeometry.width() / dpi.second); } - xcb_get_property_reply_t * workArea = - xcb_get_property_reply(xcb_connection(), - xcb_get_property_unchecked(xcb_connection(), false, screen()->root, - atom(QXcbAtom::_NET_WORKAREA), - XCB_ATOM_CARDINAL, 0, 1024), NULL); - - if (workArea && workArea->type == XCB_ATOM_CARDINAL && workArea->format == 32 && workArea->value_len >= 4) { - // If workArea->value_len > 4, the remaining ones seem to be for virtual desktops. - // But QScreen doesn't know about that concept. In reality there could be a - // "docked" panel (with _NET_WM_STRUT_PARTIAL atom set) on just one desktop. - // But for now just assume the first 4 values give us the geometry of the - // "work area", AKA "available geometry" - uint32_t *geom = (uint32_t*)xcb_get_property_value(workArea); - QRect virtualAvailableGeometry(geom[0], geom[1], geom[2], geom[3]); - // Take the intersection of the desktop's available geometry with this screen's geometry - // to get the part of the available geometry which belongs to this screen. - xAvailableGeometry = xGeometry & virtualAvailableGeometry; - } - free(workArea); - qreal dpi = xGeometry.width() / physicalSize().width() * qreal(25.4); - m_devicePixelRatio = qRound(dpi/96); - const int dpr = int(devicePixelRatio()); // we may override m_devicePixelRatio - m_geometry = QRect(xGeometry.topLeft(), xGeometry.size()/dpr); + m_pixelDensity = qRound(dpi/96); + m_geometry = QRect(xGeometry.topLeft(), xGeometry.size()); m_nativeGeometry = QRect(xGeometry.topLeft(), xGeometry.size()); - m_availableGeometry = QRect(mapFromNative(xAvailableGeometry.topLeft()), xAvailableGeometry.size()/dpr); + m_availableGeometry = xGeometry & m_virtualDesktop->workArea(); QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), m_geometry, m_availableGeometry); } +void QXcbScreen::updateAvailableGeometry() +{ + QRect availableGeometry = m_geometry & m_virtualDesktop->workArea(); + if (m_availableGeometry != availableGeometry) { + m_availableGeometry = availableGeometry; + QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), m_geometry, m_availableGeometry); + } +} + void QXcbScreen::updateRefreshRate(xcb_randr_mode_t mode) { if (!connection()->hasXRandr()) @@ -544,7 +558,8 @@ void QXcbScreen::updateRefreshRate(xcb_randr_mode_t mode) for (; modesIter.rem; xcb_randr_mode_info_next(&modesIter)) { xcb_randr_mode_info_t *modeInfo = modesIter.data; if (modeInfo->id == mode) { - m_refreshRate = modeInfo->dot_clock / (modeInfo->htotal * modeInfo->vtotal); + const uint32_t dotCount = modeInfo->htotal * modeInfo->vtotal; + m_refreshRate = (dotCount != 0) ? modeInfo->dot_clock / dotCount : 0; m_mode = mode; break; } @@ -778,7 +793,7 @@ QDebug operator<<(QDebug debug, const QXcbScreen *screen) formatSizeF(debug, screen->physicalSize()); // TODO 5.6 if (debug.verbosity() > 2) { debug << ", screenNumber=" << screen->screenNumber(); - debug << ", virtualSize=" << screen->virtualSize().width() << "x" << screen->virtualSize().height() << " ("; + debug << ", virtualSize=" << screen->virtualSize().width() << 'x' << screen->virtualSize().height() << " ("; formatSizeF(debug, screen->virtualSize()); debug << "), nativeGeometry="; formatRect(debug, screen->nativeGeometry()); diff --git a/src/plugins/platforms/xcb/qxcbscreen.h b/src/plugins/platforms/xcb/qxcbscreen.h index ccc30c0b84..c68c290791 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.h +++ b/src/plugins/platforms/xcb/qxcbscreen.h @@ -39,6 +39,7 @@ #include <xcb/xcb.h> #include <xcb/randr.h> +#include <xcb/xfixes.h> #include "qxcbobject.h" #include "qxcbscreen.h" @@ -64,14 +65,36 @@ public: int number() const { return m_number; } QSize size() const { return QSize(m_screen->width_in_pixels, m_screen->height_in_pixels); } QSize physicalSize() const { return QSize(m_screen->width_in_millimeters, m_screen->height_in_millimeters); } + xcb_window_t root() const { return m_screen->root; } + QXcbScreen *screenAt(const QPoint &pos) const; + + QList<QPlatformScreen *> screens() const { return m_screens; } + void setScreens(QList<QPlatformScreen *> sl) { m_screens = sl; } + void removeScreen(QPlatformScreen *s) { m_screens.removeOne(s); } + void addScreen(QPlatformScreen *s); QXcbXSettings *xSettings() const; + bool compositingActive() const; + + QRect workArea() const { return m_workArea; } + void updateWorkArea(); + + void handleXFixesSelectionNotify(xcb_xfixes_selection_notify_event_t *notify_event); + void subscribeToXFixesSelectionNotify(); + private: + QRect getWorkArea() const; + xcb_screen_t *m_screen; int m_number; + QList<QPlatformScreen *> m_screens; QXcbXSettings *m_xSettings; + xcb_atom_t m_net_wm_cm_atom; + bool m_compositingActive; + + QRect m_workArea; }; class Q_XCB_EXPORT QXcbScreen : public QXcbObject, public QPlatformScreen @@ -96,14 +119,12 @@ public: QSizeF physicalVirtualSize() const { return m_virtualSizeMillimeters; } QDpi virtualDpi() const; QDpi logicalDpi() const Q_DECL_OVERRIDE; - qreal devicePixelRatio() const Q_DECL_OVERRIDE; + qreal pixelDensity() const Q_DECL_OVERRIDE; QPlatformCursor *cursor() const Q_DECL_OVERRIDE; qreal refreshRate() const Q_DECL_OVERRIDE { return m_refreshRate; } Qt::ScreenOrientation orientation() const Q_DECL_OVERRIDE { return m_orientation; } - QList<QPlatformScreen *> virtualSiblings() const Q_DECL_OVERRIDE { return m_siblings; } - void setVirtualSiblings(QList<QPlatformScreen *> sl) { m_siblings = sl; } - void removeVirtualSibling(QPlatformScreen *s) { m_siblings.removeOne(s); } - void addVirtualSibling(QPlatformScreen *s) { ((QXcbScreen *) s)->isPrimary() ? m_siblings.prepend(s) : m_siblings.append(s); } + QList<QPlatformScreen *> virtualSiblings() const Q_DECL_OVERRIDE { return m_virtualDesktop->screens(); } + QXcbVirtualDesktop *virtualDesktop() const { return m_virtualDesktop; } void setPrimary(bool primary) { m_primary = primary; } bool isPrimary() const { return m_primary; } @@ -128,6 +149,7 @@ public: void handleScreenChange(xcb_randr_screen_change_notify_event_t *change_event); void updateGeometry(const QRect &geom, uint8_t rotation); void updateGeometry(xcb_timestamp_t timestamp = XCB_TIME_CURRENT_TIME); + void updateAvailableGeometry(); void updateRefreshRate(xcb_randr_mode_t mode); void readXResources(); @@ -139,13 +161,6 @@ public: QXcbXSettings *xSettings() const; - QPoint mapToNative(const QPoint &pos) const; - QPoint mapFromNative(const QPoint &pos) const; - QPointF mapToNative(const QPointF &pos) const; - QPointF mapFromNative(const QPointF &pos) const; - QRect mapToNative(const QRect &rect) const; - QRect mapFromNative(const QRect &rect) const; - private: static bool xResource(const QByteArray &identifier, const QByteArray &expectedIdentifier, @@ -167,7 +182,6 @@ private: QRect m_availableGeometry; QSize m_virtualSize; QSizeF m_virtualSizeMillimeters; - QList<QPlatformScreen *> m_siblings; Qt::ScreenOrientation m_orientation; QString m_windowManagerName; bool m_syncRequestSupported; @@ -176,7 +190,7 @@ private: QXcbCursor *m_cursor; int m_refreshRate; int m_forcedDpi; - int m_devicePixelRatio; + int m_pixelDensity; QFontEngine::HintStyle m_hintStyle; bool m_noFontHinting; QFontEngine::SubpixelAntialiasingType m_subpixelType; diff --git a/src/plugins/platforms/xcb/qxcbsessionmanager.cpp b/src/plugins/platforms/xcb/qxcbsessionmanager.cpp index 328b72234a..c2101a71c1 100644 --- a/src/plugins/platforms/xcb/qxcbsessionmanager.cpp +++ b/src/plugins/platforms/xcb/qxcbsessionmanager.cpp @@ -134,6 +134,7 @@ static void sm_setProperty(const QString &name, const QStringList &value) SmPropValue *prop = new SmPropValue[value.count()]; int count = 0; QList<QByteArray> vl; + vl.reserve(value.size()); for (QStringList::ConstIterator it = value.begin(); it != value.end(); ++it) { prop[count].length = (*it).length(); vl.append((*it).toUtf8()); diff --git a/src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp b/src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp index a4fdd70b79..1f217e8de7 100644 --- a/src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp +++ b/src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp @@ -63,14 +63,14 @@ QXcbSystemTrayTracker *QXcbSystemTrayTracker::create(QXcbConnection *connection) const xcb_atom_t selection = connection->internAtom(netSysTray.constData()); if (!selection) return 0; - return new QXcbSystemTrayTracker(connection, trayAtom, selection, connection); + + return new QXcbSystemTrayTracker(connection, trayAtom, selection); } QXcbSystemTrayTracker::QXcbSystemTrayTracker(QXcbConnection *connection, xcb_atom_t trayAtom, - xcb_atom_t selection, - QObject *parent) - : QObject(parent) + xcb_atom_t selection) + : QObject(connection) , m_selection(selection) , m_trayAtom(trayAtom) , m_connection(connection) @@ -125,6 +125,7 @@ xcb_window_t QXcbSystemTrayTracker::trayWindow() // does not work for the QWindow parented on the tray. QRect QXcbSystemTrayTracker::systemTrayWindowGlobalGeometry(xcb_window_t window) const { + xcb_connection_t *conn = m_connection->xcb_connection(); xcb_get_geometry_reply_t *geomReply = xcb_get_geometry_reply(conn, xcb_get_geometry(conn, window), 0); @@ -161,9 +162,43 @@ void QXcbSystemTrayTracker::handleDestroyNotifyEvent(const xcb_destroy_notify_ev { if (event->window == m_trayWindow) { m_connection->removeWindowEventListener(m_trayWindow); - m_trayWindow = 0; + m_trayWindow = XCB_WINDOW_NONE; emitSystemTrayWindowChanged(); } } +bool QXcbSystemTrayTracker::visualHasAlphaChannel() +{ + if (m_trayWindow == XCB_WINDOW_NONE) + return false; + + xcb_atom_t tray_atom = m_connection->atom(QXcbAtom::_NET_SYSTEM_TRAY_VISUAL); + + // Get the xcb property for the _NET_SYSTEM_TRAY_VISUAL atom + xcb_get_property_cookie_t systray_atom_cookie; + xcb_get_property_reply_t *systray_atom_reply; + + systray_atom_cookie = xcb_get_property_unchecked(m_connection->xcb_connection(), false, m_trayWindow, + tray_atom, XCB_ATOM_VISUALID, 0, 1); + systray_atom_reply = xcb_get_property_reply(m_connection->xcb_connection(), systray_atom_cookie, 0); + + if (!systray_atom_reply) + return false; + + xcb_visualid_t systrayVisualId = XCB_NONE; + if (systray_atom_reply->value_len > 0 && xcb_get_property_value_length(systray_atom_reply) > 0) { + xcb_visualid_t * vids = (uint32_t *)xcb_get_property_value(systray_atom_reply); + systrayVisualId = vids[0]; + } + + free(systray_atom_reply); + + if (systrayVisualId != XCB_NONE) { + quint8 depth = m_connection->primaryScreen()->depthOfVisual(systrayVisualId); + return depth == 32; + } + + return false; +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbsystemtraytracker.h b/src/plugins/platforms/xcb/qxcbsystemtraytracker.h index 9c20f1729a..b619afb9c4 100644 --- a/src/plugins/platforms/xcb/qxcbsystemtraytracker.h +++ b/src/plugins/platforms/xcb/qxcbsystemtraytracker.h @@ -57,14 +57,14 @@ public: void handleDestroyNotifyEvent(const xcb_destroy_notify_event_t *) Q_DECL_OVERRIDE; + bool visualHasAlphaChannel(); signals: void systemTrayWindowChanged(QScreen *screen); private: explicit QXcbSystemTrayTracker(QXcbConnection *connection, xcb_atom_t trayAtom, - xcb_atom_t selection, - QObject *parent = 0); + xcb_atom_t selection); static xcb_window_t locateTrayWindow(const QXcbConnection *connection, xcb_atom_t selection); void emitSystemTrayWindowChanged(); diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index 4fdebe1ebb..6023ee6b29 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -37,6 +37,7 @@ #include <QScreen> #include <QtGui/QIcon> #include <QtGui/QRegion> +#include <QtGui/private/qhighdpiscaling_p.h> #include "qxcbintegration.h" #include "qxcbconnection.h" @@ -46,6 +47,7 @@ #include "qxcbwmsupport.h" #include "qxcbimage.h" #include "qxcbnativeinterface.h" +#include "qxcbsystemtraytracker.h" #include <qpa/qplatformintegration.h> @@ -139,73 +141,11 @@ enum QX11EmbedMessageType { const quint32 XEMBED_VERSION = 0; -static inline QRect mapLocalGeometryToNative(const QRect &qtRect, int dpr) -{ - return QRect(qtRect.x() * dpr, qtRect.y() * dpr, qtRect.width() * dpr, qtRect.height() * dpr); -} - -// When mapping expose events to Qt rects: round top/left towards the origin and -// bottom/right away from the origin, making sure that we cover the whole widget - -static inline QPoint dpr_floor(const QPoint &p, int dpr) -{ - return QPoint(p.x()/dpr, p.y()/dpr); -} - -static inline QPoint dpr_ceil(const QPoint &p, int dpr) -{ - return QPoint((p.x() + dpr - 1) / dpr, (p.y() + dpr - 1) / dpr); -} - -static inline QSize dpr_ceil(const QSize &s, int dpr) -{ - return QSize((s.width() + dpr - 1) / dpr, (s.height() + dpr - 1) / dpr); -} - -static inline QRect mapExposeFromNative(const QRect &xRect, int dpr) -{ - return QRect(dpr_floor(xRect.topLeft(), dpr), dpr_ceil(xRect.bottomRight(), dpr)); -} - -static inline QRect mapLocalGeometryFromNative(const QRect &xRect, int dpr) -{ - return QRect(xRect.topLeft() / dpr, dpr_ceil(xRect.size(), dpr)); -} - QXcbScreen *QXcbWindow::parentScreen() { return parent() ? static_cast<QXcbWindow*>(parent())->parentScreen() : m_xcbScreen; } -QPoint QXcbWindow::mapToNative(const QPoint &pos, const QXcbScreen *screen) const -{ - if (parent()) - return pos * int(screen->devicePixelRatio()); - else - return screen->mapToNative(pos); -} -QPoint QXcbWindow::mapFromNative(const QPoint &pos, const QXcbScreen *screen) const -{ - if (parent()) - return pos / int(screen->devicePixelRatio()); - else - return screen->mapFromNative(pos); -} -QRect QXcbWindow::mapToNative(const QRect &rect, const QXcbScreen *screen) const -{ - if (parent()) - return mapLocalGeometryToNative(rect, int(screen->devicePixelRatio())); - else - return screen->mapToNative(rect); -} -QRect QXcbWindow::mapFromNative(const QRect &rect, const QXcbScreen *screen) const -{ - if (parent()) - return mapLocalGeometryFromNative(rect, int(screen->devicePixelRatio())); - else - return screen->mapFromNative(rect); -} - // Returns \c true if we should set WM_TRANSIENT_FOR on \a w static inline bool isTransient(const QWindow *w) { @@ -374,14 +314,12 @@ void QXcbWindow::create() destroy(); - m_deferredExpose = false; - m_configureNotifyPending = true; m_windowState = Qt::WindowNoState; Qt::WindowType type = window()->type(); QXcbScreen *currentScreen = xcbScreen(); - QRect rect = window()->geometry(); + QRect rect = windowGeometry(); QXcbScreen *platformScreen = parent() ? parentScreen() : static_cast<QXcbScreen*>(screenForGeometry(rect)); m_xcbScreen = platformScreen; @@ -424,17 +362,15 @@ void QXcbWindow::create() if (platformScreen != currentScreen) QWindowSystemInterface::handleWindowScreenChanged(window(), platformScreen->QPlatformScreen::screen()); - const int dpr = int(devicePixelRatio()); - - QSize minimumSize = window()->minimumSize(); + const QSize minimumSize = windowMinimumSize(); if (rect.width() > 0 || rect.height() > 0) { - rect.setWidth(qBound(1, rect.width(), XCOORD_MAX/dpr)); - rect.setHeight(qBound(1, rect.height(), XCOORD_MAX/dpr)); + rect.setWidth(qBound(1, rect.width(), XCOORD_MAX)); + rect.setHeight(qBound(1, rect.height(), XCOORD_MAX)); } else if (minimumSize.width() > 0 || minimumSize.height() > 0) { rect.setSize(minimumSize); } else { - rect.setWidth(defaultWindowWidth); - rect.setHeight(defaultWindowHeight); + rect.setWidth(QHighDpi::toNativePixels(int(defaultWindowWidth), platformScreen->QPlatformScreen::screen())); + rect.setHeight(QHighDpi::toNativePixels(int(defaultWindowHeight), platformScreen->QPlatformScreen::screen())); } xcb_window_t xcb_parent_id = platformScreen->root(); @@ -451,7 +387,8 @@ void QXcbWindow::create() resolveFormat(); #ifdef XCB_USE_XLIB - if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL)) { + if (window()->surfaceType() != QSurface::RasterSurface + && QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL)) { XVisualInfo *visualInfo = Q_NULLPTR; if (connection()->hasDefaultVisualId()) visualInfo = CREATE_VISUALINFO_FROM_DEFAULT_VISUALID(this); @@ -478,9 +415,7 @@ void QXcbWindow::create() m_visualId = visualInfo->visualid; - const QRect xRect = mapToNative(rect, platformScreen); - - m_window = XCreateWindow(DISPLAY_FROM_XCB(this), xcb_parent_id, xRect.x(), xRect.y(), xRect.width(), xRect.height(), + m_window = XCreateWindow(DISPLAY_FROM_XCB(this), xcb_parent_id, rect.x(), rect.y(), rect.width(), rect.height(), 0, visualInfo->depth, InputOutput, visualInfo->visual, CWBackPixel|CWBorderPixel|CWColormap, &a); @@ -536,16 +471,14 @@ void QXcbWindow::create() } m_imageFormat = imageFormatForVisual(m_depth, visual->red_mask, visual->blue_mask, &m_imageRgbSwap); - const QRect xRect = mapToNative(rect, platformScreen); - Q_XCB_CALL(xcb_create_window(xcb_connection(), m_depth, m_window, // window id xcb_parent_id, // parent window id - xRect.x(), - xRect.y(), - xRect.width(), - xRect.height(), + rect.x(), + rect.y(), + rect.width(), + rect.height(), 0, // border width XCB_WINDOW_CLASS_INPUT_OUTPUT, // window class m_visualId, // visual @@ -565,10 +498,7 @@ void QXcbWindow::create() properties[propertyCount++] = atom(QXcbAtom::WM_TAKE_FOCUS); properties[propertyCount++] = atom(QXcbAtom::_NET_WM_PING); - if (platformScreen->syncRequestSupported()) - m_usingSyncProtocol = supportsSyncProtocol(); - else - m_usingSyncProtocol = false; + m_usingSyncProtocol = platformScreen->syncRequestSupported(); if (m_usingSyncProtocol) properties[propertyCount++] = atom(QXcbAtom::_NET_WM_SYNC_REQUEST); @@ -708,17 +638,16 @@ void QXcbWindow::setGeometry(const QRect &rect) propagateSizeHints(); - QXcbScreen *currentScreen = xcbScreen(); + QXcbScreen *currentScreen = m_xcbScreen; QXcbScreen *newScreen = parent() ? parentScreen() : static_cast<QXcbScreen*>(screenForGeometry(rect)); if (!newScreen) - newScreen = currentScreen; + newScreen = xcbScreen(); m_xcbScreen = newScreen; - const QRect xRect = mapToNative(rect, newScreen); - const QRect wmGeometry = windowToWmGeometry(xRect); + const QRect wmGeometry = windowToWmGeometry(rect); - if (newScreen != currentScreen) + if (newScreen && newScreen != currentScreen) QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->QPlatformScreen::screen()); if (qt_window_private(window())->positionAutomatic) { @@ -842,6 +771,13 @@ void QXcbWindow::setVisible(bool visible) hide(); } +static inline bool testShowWithoutActivating(const QWindow *window) +{ + // QWidget-attribute Qt::WA_ShowWithoutActivating. + const QVariant showWithoutActivating = window->property("_q_showWithoutActivating"); + return showWithoutActivating.isValid() && showWithoutActivating.toBool(); +} + void QXcbWindow::show() { if (window()->isTopLevel()) { @@ -889,7 +825,9 @@ void QXcbWindow::show() updateNetWmStateBeforeMap(); } - if (connection()->time() != XCB_TIME_CURRENT_TIME) + if (testShowWithoutActivating(window())) + updateNetWmUserTime(0); + else if (connection()->time() != XCB_TIME_CURRENT_TIME) updateNetWmUserTime(connection()->time()); if (window()->objectName() == QLatin1String("QSystemTrayIconSysWindow")) @@ -1179,6 +1117,9 @@ void QXcbWindow::setMotifWindowFlags(Qt::WindowFlags flags) mwmhints.flags |= MWM_HINTS_DECORATIONS; bool customize = flags & Qt::CustomizeWindowHint; + if (type == Qt::Window && !customize) + flags |= Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint; + if (!(flags & Qt::FramelessWindowHint) && !(customize && !(flags & Qt::WindowTitleHint))) { mwmhints.decorations |= MWM_DECOR_BORDER; mwmhints.decorations |= MWM_DECOR_RESIZEH; @@ -1328,7 +1269,7 @@ void QXcbWindow::updateMotifWmHintsBeforeMap() mwmhints.flags &= ~MWM_HINTS_INPUT_MODE; } - if (window()->minimumSize() == window()->maximumSize()) { + if (windowMinimumSize() == windowMaximumSize()) { // fixed size, remove the resize handle (since mwm/dtwm // isn't smart enough to do it itself) mwmhints.flags |= MWM_HINTS_FUNCTIONS; @@ -1393,7 +1334,11 @@ void QXcbWindow::updateNetWmStateBeforeMap() void QXcbWindow::updateNetWmUserTime(xcb_timestamp_t timestamp) { xcb_window_t wid = m_window; - connection()->setNetWmUserTime(timestamp); + // If timestamp == 0, then it means that the window should not be + // initially activated. Don't update global user time for this + // special case. + if (timestamp != 0) + connection()->setNetWmUserTime(timestamp); const bool isSupportedByWM = connection()->wmSupport()->isSupportedByWM(atom(QXcbAtom::_NET_WM_USER_TIME_WINDOW)); if (m_netWmUserTimeWindow || isSupportedByWM) { @@ -1522,10 +1467,22 @@ void QXcbWindow::setWindowTitle(const QString &title) xcb_flush(xcb_connection()); } +void QXcbWindow::setWindowIconText(const QString &title) +{ + const QByteArray ba = title.toUtf8(); + Q_XCB_CALL(xcb_change_property(xcb_connection(), + XCB_PROP_MODE_REPLACE, + m_window, + atom(QXcbAtom::_NET_WM_ICON_NAME), + atom(QXcbAtom::UTF8_STRING), + 8, + ba.length(), + ba.constData())); +} + void QXcbWindow::setWindowIcon(const QIcon &icon) { QVector<quint32> icon_data; - if (!icon.isNull()) { QList<QSize> availableSizes = icon.availableSizes(); if (availableSizes.isEmpty()) { @@ -1602,8 +1559,7 @@ void QXcbWindow::propagateSizeHints() xcb_size_hints_t hints; memset(&hints, 0, sizeof(hints)); - const int dpr = int(devicePixelRatio()); - const QRect xRect = windowToWmGeometry(mapToNative(geometry(), xcbScreen())); + const QRect xRect = windowToWmGeometry(geometry()); QWindow *win = window(); @@ -1613,10 +1569,10 @@ void QXcbWindow::propagateSizeHints() xcb_size_hints_set_size(&hints, true, xRect.width(), xRect.height()); xcb_size_hints_set_win_gravity(&hints, m_gravity); - QSize minimumSize = win->minimumSize() * dpr; - QSize maximumSize = win->maximumSize() * dpr; - QSize baseSize = win->baseSize() * dpr; - QSize sizeIncrement = win->sizeIncrement() * dpr; + QSize minimumSize = windowMinimumSize(); + QSize maximumSize = windowMaximumSize(); + QSize baseSize = windowBaseSize(); + QSize sizeIncrement = windowSizeIncrement(); if (minimumSize.width() > 0 || minimumSize.height() > 0) xcb_size_hints_set_min_size(&hints, @@ -1645,7 +1601,7 @@ void QXcbWindow::requestActivateWindow() return; } - if (!m_mapped) { + if (!m_mapped || !xcbScreen()) { m_deferredActivation = true; return; } @@ -1690,6 +1646,12 @@ void QXcbWindow::setWmWindowTypeStatic(QWindow *window, QXcbWindowFunctions::WmW static_cast<QXcbWindow *>(window->handle())->setWmWindowType(windowTypes, window->flags()); } +void QXcbWindow::setWindowIconTextStatic(QWindow *window, const QString &text) +{ + if (window->handle()) + static_cast<QXcbWindow *>(window->handle())->setWindowIconText(text); +} + uint QXcbWindow::visualIdStatic(QWindow *window) { if (window && window->handle()) @@ -1855,13 +1817,54 @@ void QXcbWindow::setWmWindowType(QXcbWindowFunctions::WmWindowTypes types, Qt::W xcb_flush(xcb_connection()); } +void QXcbWindow::setParentRelativeBackPixmapStatic(QWindow *window) +{ + if (window->handle()) + static_cast<QXcbWindow *>(window->handle())->setParentRelativeBackPixmap(); +} + +void QXcbWindow::setParentRelativeBackPixmap() +{ + const quint32 mask = XCB_CW_BACK_PIXMAP; + const quint32 values[] = { XCB_BACK_PIXMAP_PARENT_RELATIVE }; + Q_XCB_CALL(xcb_change_window_attributes(xcb_connection(), m_window, mask, values)); +} + +bool QXcbWindow::requestSystemTrayWindowDockStatic(const QWindow *window) +{ + if (window->handle()) + return static_cast<QXcbWindow *>(window->handle())->requestSystemTrayWindowDock(); + return false; +} + +bool QXcbWindow::requestSystemTrayWindowDock() const +{ + if (!connection()->systemTrayTracker()) + return false; + connection()->systemTrayTracker()->requestSystemTrayWindowDock(m_window); + return true; +} + +QRect QXcbWindow::systemTrayWindowGlobalGeometryStatic(const QWindow *window) +{ + if (window->handle()) + return static_cast<QXcbWindow *>(window->handle())->systemTrayWindowGlobalGeometry(); + return QRect(); +} + +QRect QXcbWindow::systemTrayWindowGlobalGeometry() const +{ + if (!connection()->systemTrayTracker()) + return QRect(); + return connection()->systemTrayTracker()->systemTrayWindowGlobalGeometry(m_window); +} + class ExposeCompressor { public: - ExposeCompressor(xcb_window_t window, QRegion *region, int devicePixelRatio) + ExposeCompressor(xcb_window_t window, QRegion *region) : m_window(window) , m_region(region) - , m_dpr(devicePixelRatio) , m_pending(true) { } @@ -1877,7 +1880,7 @@ public: return false; if (expose->count == 0) m_pending = false; - *m_region |= mapExposeFromNative(QRect(expose->x, expose->y, expose->width, expose->height), m_dpr); + *m_region |= QRect(expose->x, expose->y, expose->width, expose->height); return true; } @@ -1889,7 +1892,6 @@ public: private: xcb_window_t m_window; QRegion *m_region; - int m_dpr; bool m_pending; }; @@ -1903,16 +1905,14 @@ bool QXcbWindow::handleGenericEvent(xcb_generic_event_t *event, long *result) void QXcbWindow::handleExposeEvent(const xcb_expose_event_t *event) { - const int dpr = int(devicePixelRatio()); - QRect x_rect(event->x, event->y, event->width, event->height); - QRect rect = mapExposeFromNative(x_rect, dpr); + QRect rect(event->x, event->y, event->width, event->height); if (m_exposeRegion.isEmpty()) m_exposeRegion = rect; else m_exposeRegion |= rect; - ExposeCompressor compressor(m_window, &m_exposeRegion, dpr); + ExposeCompressor compressor(m_window, &m_exposeRegion); xcb_generic_event_t *filter = 0; do { filter = connection()->checkEvent(compressor); @@ -1964,13 +1964,13 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even } #ifndef QT_NO_DRAGANDDROP } else if (event->type == atom(QXcbAtom::XdndEnter)) { - connection()->drag()->handleEnter(window(), event); + connection()->drag()->handleEnter(this, event); } else if (event->type == atom(QXcbAtom::XdndPosition)) { - connection()->drag()->handlePosition(window(), event); + connection()->drag()->handlePosition(this, event); } else if (event->type == atom(QXcbAtom::XdndLeave)) { - connection()->drag()->handleLeave(window(), event); + connection()->drag()->handleLeave(this, event); } else if (event->type == atom(QXcbAtom::XdndDrop)) { - connection()->drag()->handleDrop(window(), event); + connection()->drag()->handleDrop(this, event); #endif } else if (event->type == atom(QXcbAtom::_XEMBED)) { handleXEmbedMessage(event); @@ -1983,33 +1983,14 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even // and other messages. } else if (event->type == atom(QXcbAtom::_COMPIZ_DECOR_PENDING) || event->type == atom(QXcbAtom::_COMPIZ_DECOR_REQUEST) - || event->type == atom(QXcbAtom::_COMPIZ_DECOR_DELETE_PIXMAP)) { + || event->type == atom(QXcbAtom::_COMPIZ_DECOR_DELETE_PIXMAP) + || event->type == atom(QXcbAtom::_COMPIZ_TOOLKIT_ACTION)) { //silence the _COMPIZ messages for now } else { qWarning() << "QXcbWindow: Unhandled client message:" << connection()->atomName(event->type); } } -// Temporary workaround for bug in QPlatformScreen::screenForGeometry -// we need the native geometries to detect our screen, but that's not -// available in cross-platform code. Will be fixed properly when highDPI -// support is refactored to expose the native coordinate system. - -QXcbScreen *QXcbWindow::screenForNativeGeometry(const QRect &newGeometry) const -{ - QXcbScreen *currentScreen = xcbScreen(); - if (!currentScreen && QGuiApplication::primaryScreen()) - currentScreen = static_cast<QXcbScreen*>(QGuiApplication::primaryScreen()->handle()); - if (currentScreen && !parent() && !currentScreen->nativeGeometry().intersects(newGeometry)) { - Q_FOREACH (QPlatformScreen* screen, currentScreen->virtualSiblings()) { - QXcbScreen *xcbScreen = static_cast<QXcbScreen*>(screen); - if (xcbScreen->nativeGeometry().intersects(newGeometry)) - return xcbScreen; - } - } - return currentScreen; -} - void QXcbWindow::handleConfigureNotifyEvent(const xcb_configure_notify_event_t *event) { bool fromSendEvent = (event->response_type & 0x80); @@ -2026,26 +2007,21 @@ void QXcbWindow::handleConfigureNotifyEvent(const xcb_configure_notify_event_t * } } - const QRect nativeRect = QRect(pos, QSize(event->width, event->height)); - QXcbScreen *newScreen = parent() ? parentScreen() : screenForNativeGeometry(nativeRect); + const QRect rect = QRect(pos, QSize(event->width, event->height)); + QPlatformScreen *newScreen = parent() ? parent()->screen() : screenForGeometry(rect); QXcbScreen *currentScreen = m_xcbScreen; - m_xcbScreen = newScreen; + m_xcbScreen = static_cast<QXcbScreen*>(newScreen); if (!newScreen) return; - const QRect rect = mapFromNative(nativeRect, newScreen); QPlatformWindow::setGeometry(rect); QWindowSystemInterface::handleGeometryChange(window(), rect); if (newScreen != currentScreen) - QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->QPlatformScreen::screen()); + QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->screen()); - m_configureNotifyPending = false; - - if (m_deferredExpose) { - m_deferredExpose = false; + if (m_mapped) QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(), geometry().size())); - } if (m_usingSyncProtocol && m_syncState == SyncReceived) m_syncState = SyncAndConfigureReceived; @@ -2071,11 +2047,10 @@ QPoint QXcbWindow::mapToGlobal(const QPoint &pos) const if (!m_embedded) return pos; - const int dpr = int(devicePixelRatio()); QPoint ret; xcb_translate_coordinates_cookie_t cookie = xcb_translate_coordinates(xcb_connection(), xcb_window(), xcbScreen()->root(), - pos.x() * dpr, pos.y() * dpr); + pos.x(), pos.y()); xcb_translate_coordinates_reply_t *reply = xcb_translate_coordinates_reply(xcb_connection(), cookie, NULL); if (reply) { @@ -2084,7 +2059,7 @@ QPoint QXcbWindow::mapToGlobal(const QPoint &pos) const free(reply); } - return mapFromNative(ret, xcbScreen()); + return ret; } QPoint QXcbWindow::mapFromGlobal(const QPoint &pos) const @@ -2092,17 +2067,15 @@ QPoint QXcbWindow::mapFromGlobal(const QPoint &pos) const if (!m_embedded) return pos; - const int dpr = int(devicePixelRatio()); QPoint ret; - QPoint xPos = mapToNative(pos, xcbScreen()); xcb_translate_coordinates_cookie_t cookie = xcb_translate_coordinates(xcb_connection(), xcbScreen()->root(), xcb_window(), - xPos.x(), xPos.y()); + pos.x(), pos.y()); xcb_translate_coordinates_reply_t *reply = xcb_translate_coordinates_reply(xcb_connection(), cookie, NULL); if (reply) { - ret.setX(reply->dst_x / dpr); - ret.setY(reply->dst_y / dpr); + ret.setX(reply->dst_x); + ret.setY(reply->dst_y); free(reply); } @@ -2115,10 +2088,8 @@ void QXcbWindow::handleMapNotifyEvent(const xcb_map_notify_event_t *event) m_mapped = true; if (m_deferredActivation) requestActivateWindow(); - if (m_configureNotifyPending) - m_deferredExpose = true; - else - QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(), geometry().size() * int(devicePixelRatio()))); + + QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(), geometry().size())); } } @@ -2150,9 +2121,8 @@ void QXcbWindow::handleButtonPressEvent(int event_x, int event_y, int root_x, in sendXEmbedMessage(container->xcb_window(), XEMBED_REQUEST_FOCUS); } } - const int dpr = int(devicePixelRatio()); - QPoint local(event_x / dpr, event_y / dpr); - QPoint global = xcbScreen()->mapFromNative(QPoint(root_x, root_y)); + QPoint local(event_x, event_y); + QPoint global(root_x, root_y); if (isWheel) { if (!connection()->isAtLeastXI21()) { @@ -2174,9 +2144,8 @@ void QXcbWindow::handleButtonPressEvent(int event_x, int event_y, int root_x, in void QXcbWindow::handleButtonReleaseEvent(int event_x, int event_y, int root_x, int root_y, int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp) { - const int dpr = int(devicePixelRatio()); - QPoint local(event_x / dpr, event_y / dpr); - QPoint global = xcbScreen()->mapFromNative(QPoint(root_x, root_y)); + QPoint local(event_x, event_y); + QPoint global(root_x, root_y); if (detail >= 4 && detail <= 7) { // mouse wheel, handled in handleButtonPressEvent() @@ -2189,11 +2158,8 @@ void QXcbWindow::handleButtonReleaseEvent(int event_x, int event_y, int root_x, void QXcbWindow::handleMotionNotifyEvent(int event_x, int event_y, int root_x, int root_y, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp) { - if (!xcbScreen()) - return; - const int dpr = int(devicePixelRatio()); - QPoint local(event_x / dpr, event_y / dpr); - QPoint global = xcbScreen()->mapFromNative(QPoint(root_x, root_y)); + QPoint local(event_x, event_y); + QPoint global(root_x, root_y); handleMouseEvent(timestamp, local, global, modifiers); } @@ -2321,11 +2287,10 @@ void QXcbWindow::handleEnterNotifyEvent(const xcb_enter_notify_event_t *event) if (ignoreEnterEvent(event)) return; - const int dpr = int(devicePixelRatio()); - const QPoint local(event->event_x/dpr, event->event_y/dpr); + const QPoint local(event->event_x, event->event_y); if (!xcbScreen()) return; - QPoint global = xcbScreen()->mapFromNative(QPoint(event->root_x, event->root_y)); + QPoint global = QPoint(event->root_x, event->root_y); QWindowSystemInterface::handleEnterEvent(window(), local, global); } @@ -2341,11 +2306,10 @@ void QXcbWindow::handleLeaveNotifyEvent(const xcb_leave_notify_event_t *event) QXcbWindow *enterWindow = enter ? connection()->platformWindowFromId(enter->event) : 0; if (enterWindow) { - const int dpr = int(devicePixelRatio()); - QPoint local(enter->event_x/dpr, enter->event_y/dpr); + QPoint local(enter->event_x, enter->event_y); if (!xcbScreen()) return; - QPoint global = xcbScreen()->mapFromNative(QPoint(event->root_x, event->root_y)); + QPoint global = QPoint(event->root_x, event->root_y); QWindowSystemInterface::handleEnterLeaveEvent(enterWindow->window(), window(), local, global); } else { @@ -2403,8 +2367,6 @@ void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *ev return; } else if (event->atom == atom(QXcbAtom::_NET_FRAME_EXTENTS)) { m_dirtyFrameMargins = true; - } else if (event->atom == atom(QXcbAtom::_NET_WORKAREA) && xcbScreen() && event->window == xcbScreen()->root()) { - xcbScreen()->updateGeometry(event->time); } } @@ -2524,7 +2486,7 @@ bool QXcbWindow::startSystemResize(const QPoint &pos, Qt::Corner corner) xev.type = moveResize; xev.window = xcb_window(); xev.format = 32; - const QPoint globalPos = mapToNative(window()->mapToGlobal(pos), xcbScreen()); + const QPoint globalPos = window()->mapToGlobal(pos); xev.data.data32[0] = globalPos.x(); xev.data.data32[1] = globalPos.y(); const bool bottom = corner == Qt::BottomRightCorner || corner == Qt::BottomLeftCorner; @@ -2650,10 +2612,11 @@ void QXcbWindow::setMask(const QRegion ®ion) xcb_shape_mask(connection()->xcb_connection(), XCB_SHAPE_SO_SET, XCB_SHAPE_SK_BOUNDING, xcb_window(), 0, 0, XCB_NONE); } else { - const int dpr = devicePixelRatio(); QVector<xcb_rectangle_t> rects; - foreach (const QRect &r, region.rects()) - rects.push_back(qRectToXCBRectangle(mapLocalGeometryToNative(r, dpr))); + const QVector<QRect> regionRects = region.rects(); + rects.reserve(regionRects.count()); + foreach (const QRect &r, regionRects) + rects.push_back(qRectToXCBRectangle(r)); xcb_shape_rectangles(connection()->xcb_connection(), XCB_SHAPE_SO_SET, XCB_SHAPE_SK_BOUNDING, XCB_CLIP_ORDERING_UNSORTED, xcb_window(), 0, 0, rects.size(), &rects[0]); @@ -2691,11 +2654,6 @@ void QXcbWindow::postSyncWindowRequest() } } -qreal QXcbWindow::devicePixelRatio() const -{ - return xcbScreen() ? xcbScreen()->devicePixelRatio() : 1.0; -} - QXcbScreen *QXcbWindow::xcbScreen() const { return static_cast<QXcbScreen *>(screen()); diff --git a/src/plugins/platforms/xcb/qxcbwindow.h b/src/plugins/platforms/xcb/qxcbwindow.h index a379a6f9db..43e66a774d 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.h +++ b/src/plugins/platforms/xcb/qxcbwindow.h @@ -81,11 +81,12 @@ public: void setParent(const QPlatformWindow *window) Q_DECL_OVERRIDE; bool isExposed() const Q_DECL_OVERRIDE; - bool isEmbedded(const QPlatformWindow *parentWindow) const Q_DECL_OVERRIDE; + bool isEmbedded(const QPlatformWindow *parentWindow = 0) const Q_DECL_OVERRIDE; QPoint mapToGlobal(const QPoint &pos) const Q_DECL_OVERRIDE; QPoint mapFromGlobal(const QPoint &pos) const Q_DECL_OVERRIDE; void setWindowTitle(const QString &title) Q_DECL_OVERRIDE; + void setWindowIconText(const QString &title); void setWindowIcon(const QIcon &icon) Q_DECL_OVERRIDE; void raise() Q_DECL_OVERRIDE; void lower() Q_DECL_OVERRIDE; @@ -145,6 +146,16 @@ public: QXcbWindowFunctions::WmWindowTypes wmWindowTypes() const; void setWmWindowType(QXcbWindowFunctions::WmWindowTypes types, Qt::WindowFlags flags); + static void setWindowIconTextStatic(QWindow *window, const QString &text); + + static void setParentRelativeBackPixmapStatic(QWindow *window); + void setParentRelativeBackPixmap(); + + static bool requestSystemTrayWindowDockStatic(const QWindow *window); + bool requestSystemTrayWindowDock() const; + + static QRect systemTrayWindowGlobalGeometryStatic(const QWindow *window); + QRect systemTrayWindowGlobalGeometry() const; uint visualId() const; bool needsSync() const; @@ -152,8 +163,6 @@ public: void postSyncWindowRequest(); void clearSyncWindowRequest() { m_pendingSyncRequest = 0; } - qreal devicePixelRatio() const Q_DECL_OVERRIDE; - QXcbScreen *xcbScreen() const; virtual void create(); @@ -167,12 +176,7 @@ public Q_SLOTS: protected: virtual void resolveFormat() { m_format = window()->requestedFormat(); } virtual void *createVisual() { return Q_NULLPTR; } - virtual bool supportsSyncProtocol() { return !window()->supportsOpenGL(); } - QPoint mapToNative(const QPoint &pos, const QXcbScreen *screen) const; - QPoint mapFromNative(const QPoint &pos, const QXcbScreen *screen) const; - QRect mapToNative(const QRect &rect, const QXcbScreen *screen) const; - QRect mapFromNative(const QRect &rect, const QXcbScreen *screen) const; QXcbScreen *parentScreen(); void changeNetWmState(bool set, xcb_atom_t one, xcb_atom_t two = 0); @@ -227,8 +231,6 @@ protected: bool m_transparent; bool m_usingSyncProtocol; bool m_deferredActivation; - bool m_deferredExpose; - bool m_configureNotifyPending; bool m_embedded; bool m_alertState; xcb_window_t m_netWmUserTimeWindow; diff --git a/src/plugins/platforms/xcb/qxcbwmsupport.cpp b/src/plugins/platforms/xcb/qxcbwmsupport.cpp index 7d31ac7118..82c1c1de77 100644 --- a/src/plugins/platforms/xcb/qxcbwmsupport.cpp +++ b/src/plugins/platforms/xcb/qxcbwmsupport.cpp @@ -94,14 +94,14 @@ void QXcbWMSupport::updateVirtualRoots() int offset = 0; int remaining = 0; do { - xcb_get_property_cookie_t cookie = xcb_get_property(xcb_connection(), false, root, atom(QXcbAtom::_NET_VIRTUAL_ROOTS), XCB_ATOM_ATOM, offset, 1024); + xcb_get_property_cookie_t cookie = xcb_get_property(xcb_connection(), false, root, atom(QXcbAtom::_NET_VIRTUAL_ROOTS), XCB_ATOM_WINDOW, offset, 1024); xcb_get_property_reply_t *reply = xcb_get_property_reply(xcb_connection(), cookie, NULL); if (!reply) break; remaining = 0; - if (reply->type == XCB_ATOM_ATOM && reply->format == 32) { + if (reply->type == XCB_ATOM_WINDOW && reply->format == 32) { int len = xcb_get_property_value_length(reply)/sizeof(xcb_window_t); xcb_window_t *roots = (xcb_window_t *)xcb_get_property_value(reply); int s = net_virtual_roots.size(); diff --git a/src/plugins/platforms/xcb/qxlibconvenience.cpp b/src/plugins/platforms/xcb/qxlibconvenience.cpp deleted file mode 100644 index f3c7d2b24e..0000000000 --- a/src/plugins/platforms/xcb/qxlibconvenience.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifdef XCB_USE_XLIB - -#include "qxlibconvenience.h" - -// Some Xlib headers are heavy macro namespace polluters and conflict with Qt types. -// This unit makes it easier to deal with them by encapsulating these includes in this .cpp. -#include <X11/Xutil.h> - -QT_BEGIN_NAMESPACE - -xcb_keysym_t q_XLookupString(void *display, xcb_window_t window, xcb_window_t root, uint state, xcb_keycode_t code, int type, QByteArray *chars) -{ - KeySym sym = 0; - chars->resize(512); - XKeyEvent event; - memset(&event, 0, sizeof(event)); - event.type = type; - event.display = static_cast<Display*>(display); - event.window = window; - event.root = root; - event.state = state; - event.keycode = code; - int count = XLookupString(&event, chars->data(), chars->size(), &sym, 0); - chars->resize(count); - return sym; -} - -QT_END_NAMESPACE - -#endif diff --git a/src/plugins/platforms/xcb/qxlibconvenience.h b/src/plugins/platforms/xcb/qxlibconvenience.h deleted file mode 100644 index 0e6e1c37ec..0000000000 --- a/src/plugins/platforms/xcb/qxlibconvenience.h +++ /dev/null @@ -1,49 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef XLIBUTILS_H -#define XLIBUTILS_H - -#ifdef XCB_USE_XLIB - -#include <xcb/xcb_keysyms.h> -#include <QByteArray> - -QT_BEGIN_NAMESPACE - -xcb_keysym_t q_XLookupString(void *display, xcb_window_t window, xcb_window_t root, uint state, xcb_keycode_t code, int type, QByteArray *chars); - -QT_END_NAMESPACE - -#endif // XCB_USE_XLIB -#endif diff --git a/src/plugins/platforms/xcb/xcb_qpa_lib.pro b/src/plugins/platforms/xcb/xcb_qpa_lib.pro index 12987567ff..60eb8a02e3 100644 --- a/src/plugins/platforms/xcb/xcb_qpa_lib.pro +++ b/src/plugins/platforms/xcb/xcb_qpa_lib.pro @@ -1,13 +1,6 @@ TARGET = QtXcbQpa CONFIG += no_module_headers internal_module -MODULE_INCLUDES = \ - \$\$QT_MODULE_INCLUDE_BASE \ - \$\$QT_MODULE_INCLUDE_BASE/QtQGui -MODULE_PRIVATE_INCLUDES = \ - \$\$QT_MODULE_INCLUDE_BASE/QtGui/$$QT.gui.VERSION \ - \$\$QT_MODULE_INCLUDE_BASE/QtGui/$$QT.gui.VERSION/QtGui - load(qt_module) QT += core-private gui-private platformsupport-private @@ -47,8 +40,6 @@ HEADERS = \ qxcbxsettings.h \ qxcbsystemtraytracker.h -LIBS += $$QMAKE_LIBS_DYNLOAD - DEFINES += QT_BUILD_XCB_PLUGIN # needed by Xcursor ... contains(QT_CONFIG, xcb-xlib) { |