diff options
Diffstat (limited to 'src/plugins')
58 files changed, 1602 insertions, 413 deletions
diff --git a/src/plugins/bearer/android/jar/jar.pri b/src/plugins/bearer/android/jar/jar.pri index 6d9aac3bb3..e43dbf0711 100644 --- a/src/plugins/bearer/android/jar/jar.pri +++ b/src/plugins/bearer/android/jar/jar.pri @@ -9,5 +9,3 @@ JAVASOURCES += $$PWD/src/org/qtproject/qt5/android/bearer/QtNetworkReceiver.java # install target.path = $$[QT_INSTALL_PREFIX]/jar INSTALLS += target - -OTHER_FILES += $$JAVASOURCES diff --git a/src/plugins/bearer/android/src/qandroidbearerengine.cpp b/src/plugins/bearer/android/src/qandroidbearerengine.cpp index e7a1d2b5fb..a43ed69570 100644 --- a/src/plugins/bearer/android/src/qandroidbearerengine.cpp +++ b/src/plugins/bearer/android/src/qandroidbearerengine.cpp @@ -275,7 +275,8 @@ void QAndroidBearerEngine::updateConfigurations() interfaces = QNetworkInterface::allInterfaces(); // Create a configuration for each of the main types (WiFi, Mobile, Bluetooth, WiMax, Ethernet) - foreach (const AndroidNetworkInfo &netInfo, m_connectivityManager->getAllNetworkInfo()) { + const auto netInfos = m_connectivityManager->getAllNetworkInfo(); + for (const AndroidNetworkInfo &netInfo : netInfos) { if (!netInfo.isValid()) continue; diff --git a/src/plugins/platforms/android/androidjnimain.cpp b/src/plugins/platforms/android/androidjnimain.cpp index 51849aa688..52f34166eb 100644 --- a/src/plugins/platforms/android/androidjnimain.cpp +++ b/src/plugins/platforms/android/androidjnimain.cpp @@ -494,7 +494,7 @@ static jboolean startQtApplication(JNIEnv *env, jobject /*object*/, jstring para const char *nativeString = env->GetStringUTFChars(environmentString, 0); const QList<QByteArray> envVars = QByteArray(nativeString).split('\t'); env->ReleaseStringUTFChars(environmentString, nativeString); - foreach (const QByteArray &envVar, envVars) { + for (const QByteArray &envVar : envVars) { const QList<QByteArray> envVarPair = envVar.split('='); if (envVarPair.size() == 2 && ::setenv(envVarPair[0], envVarPair[1], 1) != 0) qWarning() << "Can't set environment" << envVarPair; @@ -627,7 +627,8 @@ static void updateWindow(JNIEnv */*env*/, jobject /*thiz*/) return; if (QGuiApplication::instance() != nullptr) { - foreach (QWindow *w, QGuiApplication::topLevelWindows()) { + const auto tlw = QGuiApplication::topLevelWindows(); + for (QWindow *w : tlw) { QRect availableGeometry = w->screen()->availableGeometry(); if (w->geometry().width() > 0 && w->geometry().height() > 0 && availableGeometry.width() > 0 && availableGeometry.height() > 0) QWindowSystemInterface::handleExposeEvent(w, QRegion(QRect(QPoint(), w->geometry().size()))); diff --git a/src/plugins/platforms/android/androidjnimenu.cpp b/src/plugins/platforms/android/androidjnimenu.cpp index 551931b688..6f548aba52 100644 --- a/src/plugins/platforms/android/androidjnimenu.cpp +++ b/src/plugins/platforms/android/androidjnimenu.cpp @@ -152,7 +152,7 @@ namespace QtAndroidMenu visibleMenuBar = 0; activeTopLevelWindow = window; - foreach (QAndroidPlatformMenuBar *menuBar, menuBars) { + for (QAndroidPlatformMenuBar *menuBar : qAsConst(menuBars)) { if (menuBar->parentWindow() == window) { visibleMenuBar = menuBar; resetMenuBar(); @@ -218,7 +218,8 @@ namespace QtAndroidMenu static int addAllMenuItemsToMenu(JNIEnv *env, jobject menu, QAndroidPlatformMenu *platformMenu) { int order = 0; QMutexLocker lock(platformMenu->menuItemsMutex()); - foreach (QAndroidPlatformMenuItem *item, platformMenu->menuItems()) { + const auto items = platformMenu->menuItems(); + for (QAndroidPlatformMenuItem *item : items) { if (item->isSeparator()) continue; QString itemText = removeAmpersandEscapes(item->text()); @@ -257,7 +258,7 @@ namespace QtAndroidMenu if (menus.size() == 1) { // Expand the menu order = addAllMenuItemsToMenu(env, menu, static_cast<QAndroidPlatformMenu *>(menus.front())); } else { - foreach (QAndroidPlatformMenu *item, menus) { + for (QAndroidPlatformMenu *item : menus) { QString itemText = removeAmpersandEscapes(item->text()); jstring jtext = env->NewString(reinterpret_cast<const jchar *>(itemText.data()), itemText.length()); @@ -350,7 +351,7 @@ namespace QtAndroidMenu item->activated(); visibleMenu->aboutToHide(); visibleMenu = 0; - foreach (QAndroidPlatformMenu *menu, pendingContextMenus) { + for (QAndroidPlatformMenu *menu : qAsConst(pendingContextMenus)) { if (menu->isVisible()) menu->aboutToHide(); } diff --git a/src/plugins/platforms/android/qandroideventdispatcher.cpp b/src/plugins/platforms/android/qandroideventdispatcher.cpp index 72f093a6eb..104e905b8f 100644 --- a/src/plugins/platforms/android/qandroideventdispatcher.cpp +++ b/src/plugins/platforms/android/qandroideventdispatcher.cpp @@ -112,7 +112,7 @@ void QAndroidEventDispatcherStopper::startAll() return; started = true; - foreach (QAndroidEventDispatcher *d, m_dispatchers) + for (QAndroidEventDispatcher *d : qAsConst(m_dispatchers)) d->start(); } @@ -123,7 +123,7 @@ void QAndroidEventDispatcherStopper::stopAll() return; started = false; - foreach (QAndroidEventDispatcher *d, m_dispatchers) + for (QAndroidEventDispatcher *d : qAsConst(m_dispatchers)) d->stop(); } @@ -142,6 +142,6 @@ void QAndroidEventDispatcherStopper::removeEventDispatcher(QAndroidEventDispatch void QAndroidEventDispatcherStopper::goingToStop(bool stop) { QMutexLocker lock(&m_mutex); - foreach (QAndroidEventDispatcher *d, m_dispatchers) + for (QAndroidEventDispatcher *d : qAsConst(m_dispatchers)) d->goingToStop(stop); } diff --git a/src/plugins/platforms/android/qandroidplatformfontdatabase.cpp b/src/plugins/platforms/android/qandroidplatformfontdatabase.cpp index efa8681d3d..0667a9073f 100644 --- a/src/plugins/platforms/android/qandroidplatformfontdatabase.cpp +++ b/src/plugins/platforms/android/qandroidplatformfontdatabase.cpp @@ -62,7 +62,8 @@ void QAndroidPlatformFontDatabase::populateFontDatabase() nameFilters << QLatin1String("*.ttf") << QLatin1String("*.otf"); - foreach (const QFileInfo &fi, dir.entryInfoList(nameFilters, QDir::Files)) { + const auto entries = dir.entryInfoList(nameFilters, QDir::Files); + for (const QFileInfo &fi : entries) { const QByteArray file = QFile::encodeName(fi.absoluteFilePath()); QBasicFontDatabase::addTTFile(QByteArray(), file); } diff --git a/src/plugins/platforms/android/qandroidplatformmenu.cpp b/src/plugins/platforms/android/qandroidplatformmenu.cpp index 83e79eb76e..06b297a1ad 100644 --- a/src/plugins/platforms/android/qandroidplatformmenu.cpp +++ b/src/plugins/platforms/android/qandroidplatformmenu.cpp @@ -159,7 +159,7 @@ QPlatformMenuItem *QAndroidPlatformMenu::menuItemAt(int position) const QPlatformMenuItem *QAndroidPlatformMenu::menuItemForTag(quintptr tag) const { - foreach (QPlatformMenuItem *menuItem, m_menuItems) { + for (QPlatformMenuItem *menuItem : m_menuItems) { if (menuItem->tag() == tag) return menuItem; } diff --git a/src/plugins/platforms/android/qandroidplatformmenubar.cpp b/src/plugins/platforms/android/qandroidplatformmenubar.cpp index 56ccbe1afe..35930f0628 100644 --- a/src/plugins/platforms/android/qandroidplatformmenubar.cpp +++ b/src/plugins/platforms/android/qandroidplatformmenubar.cpp @@ -86,7 +86,7 @@ void QAndroidPlatformMenuBar::handleReparent(QWindow *newParentWindow) QPlatformMenu *QAndroidPlatformMenuBar::menuForTag(quintptr tag) const { - foreach (QPlatformMenu *menu, m_menus) { + for (QPlatformMenu *menu : m_menus) { if (menu->tag() == tag) return menu; } diff --git a/src/plugins/platforms/android/qandroidplatformscreen.cpp b/src/plugins/platforms/android/qandroidplatformscreen.cpp index 35a93bb847..155d6bfb8d 100644 --- a/src/plugins/platforms/android/qandroidplatformscreen.cpp +++ b/src/plugins/platforms/android/qandroidplatformscreen.cpp @@ -117,7 +117,7 @@ QAndroidPlatformScreen::~QAndroidPlatformScreen() QWindow *QAndroidPlatformScreen::topWindow() const { - foreach (QAndroidPlatformWindow *w, m_windowStack) { + for (QAndroidPlatformWindow *w : m_windowStack) { if (w->window()->type() == Qt::Window || w->window()->type() == Qt::Popup || w->window()->type() == Qt::Dialog) { @@ -129,7 +129,7 @@ QWindow *QAndroidPlatformScreen::topWindow() const QWindow *QAndroidPlatformScreen::topLevelAt(const QPoint &p) const { - foreach (QAndroidPlatformWindow *w, m_windowStack) { + for (QAndroidPlatformWindow *w : m_windowStack) { if (w->geometry().contains(p, false) && w->window()->isVisible()) return w->window(); } @@ -263,7 +263,7 @@ void QAndroidPlatformScreen::setAvailableGeometry(const QRect &rect) void QAndroidPlatformScreen::applicationStateChanged(Qt::ApplicationState state) { - foreach (QAndroidPlatformWindow *w, m_windowStack) + for (QAndroidPlatformWindow *w : qAsConst(m_windowStack)) w->applicationStateChanged(state); if (state <= Qt::ApplicationHidden) { @@ -304,7 +304,7 @@ void QAndroidPlatformScreen::doRedraw() // windows that have renderToTexture children (i.e. they need the OpenGL path) then // we do not need an overlay surface. bool hasVisibleRasterWindows = false; - foreach (QAndroidPlatformWindow *window, m_windowStack) { + for (QAndroidPlatformWindow *window : qAsConst(m_windowStack)) { if (window->window()->isVisible() && window->isRaster() && !qt_window_private(window->window())->compositing) { hasVisibleRasterWindows = true; break; @@ -357,14 +357,14 @@ void QAndroidPlatformScreen::doRedraw() compositePainter.setCompositionMode(QPainter::CompositionMode_Source); QRegion visibleRegion(m_dirtyRect); - foreach (QAndroidPlatformWindow *window, m_windowStack) { + for (QAndroidPlatformWindow *window : qAsConst(m_windowStack)) { if (!window->window()->isVisible() || qt_window_private(window->window())->compositing || !window->isRaster()) continue; - QVector<QRect> visibleRects = visibleRegion.rects(); - foreach (const QRect &rect, visibleRects) { + const QVector<QRect> visibleRects = visibleRegion.rects(); + for (const QRect &rect : visibleRects) { QRect targetRect = window->geometry(); targetRect &= rect; diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index c021a551a7..f6a3b3943f 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.mm +++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm @@ -337,10 +337,20 @@ QCocoaIntegration::QCocoaIntegration(const QStringList ¶mList) [newDelegate setMenuLoader:qtMenuLoader]; } + // The presentation options such as whether or not the dock and/or menu bar is + // hidden (automatically by the system) affects the main screen's available + // geometry. Since we're initializing the screens synchronously at application + // startup we need to ensure that the presentation options have been propagated + // to the screen before we read out its properties. Normally OS X does this in + // an asynchronous callback, but that's too late for us. We force the propagation + // by explicitly setting the presentation option to the magic 'default value', + // which will resolve to an actual value and result in screen invalidation. + cocoaApplication.presentationOptions = NSApplicationPresentationDefault; updateScreens(); QMacInternalPasteboardMime::initializeMimeTypes(); QCocoaMimeTypes::initializeMimeTypes(); + QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false); } QCocoaIntegration::~QCocoaIntegration() diff --git a/src/plugins/platforms/cocoa/qcocoamenu.mm b/src/plugins/platforms/cocoa/qcocoamenu.mm index 0690a8e0fa..a388155c03 100644 --- a/src/plugins/platforms/cocoa/qcocoamenu.mm +++ b/src/plugins/platforms/cocoa/qcocoamenu.mm @@ -105,7 +105,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QCocoaMenuDelegate); - (NSInteger)numberOfItemsInMenu:(NSMenu *)menu { Q_ASSERT(m_menu->nsMenu() == menu); - return m_menu->items().count(); + return menu.numberOfItems; } - (BOOL)menu:(NSMenu *)menu updateItem:(NSMenuItem *)item atIndex:(NSInteger)index shouldCancel:(BOOL)shouldCancel diff --git a/src/plugins/platforms/cocoa/qnsview.h b/src/plugins/platforms/cocoa/qnsview.h index b5738abf4c..00d65ea7f8 100644 --- a/src/plugins/platforms/cocoa/qnsview.h +++ b/src/plugins/platforms/cocoa/qnsview.h @@ -140,7 +140,7 @@ Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(QNSViewMouseMoveHelper)); - (void)otherMouseUp:(NSEvent *)theEvent; - (void)handleFrameStrutMouseEvent:(NSEvent *)theEvent; -- (void)handleTabletEvent: (NSEvent *)theEvent; +- (bool)handleTabletEvent: (NSEvent *)theEvent; - (void)tabletPoint: (NSEvent *)theEvent; - (void)tabletProximity: (NSEvent *)theEvent; diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm index 66df5c724b..c9783df44b 100644 --- a/src/plugins/platforms/cocoa/qnsview.mm +++ b/src/plugins/platforms/cocoa/qnsview.mm @@ -52,6 +52,7 @@ #include <qpa/qwindowsysteminterface.h> #include <QtGui/QTextFormat> #include <QtCore/QDebug> +#include <QtCore/qsysinfo.h> #include <private/qguiapplication_p.h> #include "qcocoabackingstore.h" #ifndef QT_NO_OPENGL @@ -74,6 +75,8 @@ static QTouchDevice *touchDevice = 0; // ### HACK Remove once 10.8 is unsupported static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil; +static bool _q_dontOverrideCtrlLMB = false; + @interface NSEvent (Qt_Compile_Leopard_DeviceDelta) - (CGFloat)deviceDeltaX; - (CGFloat)deviceDeltaY; @@ -134,6 +137,8 @@ static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil; NSString **notificationNameVar = (NSString **)dlsym(RTLD_NEXT, "NSWindowDidChangeOcclusionStateNotification"); if (notificationNameVar) _q_NSWindowDidChangeOcclusionStateNotification = *notificationNameVar; + + _q_dontOverrideCtrlLMB = qt_mac_resolveOption(false, "QT_MAC_DONT_OVERRIDE_CTRL_LMB"); } - (id) init @@ -746,7 +751,7 @@ QT_WARNING_POP - (void)handleMouseEvent:(NSEvent *)theEvent { - [self handleTabletEvent: theEvent]; + bool isTabletEvent = [self handleTabletEvent: theEvent]; QPointF qtWindowPoint; QPointF qtScreenPoint; @@ -775,7 +780,8 @@ QT_WARNING_POP nativeDrag->setLastMouseEvent(theEvent, self); Qt::KeyboardModifiers keyboardModifiers = [QNSView convertKeyModifiers:[theEvent modifierFlags]]; - QWindowSystemInterface::handleMouseEvent(targetView->m_window, timestamp, qtWindowPoint, qtScreenPoint, m_buttons, keyboardModifiers); + QWindowSystemInterface::handleMouseEvent(targetView->m_window, timestamp, qtWindowPoint, qtScreenPoint, m_buttons, keyboardModifiers, + isTabletEvent ? Qt::MouseEventSynthesizedByQt : Qt::MouseEventNotSynthesized); } - (void)handleFrameStrutMouseEvent:(NSEvent *)theEvent @@ -967,7 +973,7 @@ QT_WARNING_POP if ([self hasMarkedText]) { [[NSTextInputContext currentInputContext] handleEvent:theEvent]; } else { - if ([QNSView convertKeyModifiers:[theEvent modifierFlags]] & Qt::MetaModifier) { + if (!_q_dontOverrideCtrlLMB && [QNSView convertKeyModifiers:[theEvent modifierFlags]] & Qt::MetaModifier) { m_buttons |= Qt::RightButton; m_sendUpAsRightButton = true; } else { @@ -1152,11 +1158,11 @@ struct QCocoaTabletDeviceData typedef QHash<uint, QCocoaTabletDeviceData> QCocoaTabletDeviceDataHash; Q_GLOBAL_STATIC(QCocoaTabletDeviceDataHash, tabletDeviceDataHash) -- (void)handleTabletEvent: (NSEvent *)theEvent +- (bool)handleTabletEvent: (NSEvent *)theEvent { NSEventType eventType = [theEvent type]; if (eventType != NSTabletPoint && [theEvent subtype] != NSTabletPointEventSubtype) - return; // Not a tablet event. + return false; // Not a tablet event. ulong timestamp = [theEvent timestamp] * 1000; @@ -1169,7 +1175,7 @@ Q_GLOBAL_STATIC(QCocoaTabletDeviceDataHash, tabletDeviceDataHash) // Error: Unknown tablet device. Qt also gets into this state // when running on a VM. This appears to be harmless; don't // print a warning. - return; + return false; } const QCocoaTabletDeviceData &deviceData = tabletDeviceDataHash->value(deviceId); @@ -1210,6 +1216,7 @@ Q_GLOBAL_STATIC(QCocoaTabletDeviceDataHash, tabletDeviceDataHash) deviceData.device, deviceData.pointerType, buttons, pressure, xTilt, yTilt, tangentialPressure, rotation, z, deviceData.uid, keyboardModifiers); + return true; } - (void)tabletPoint: (NSEvent *)theEvent @@ -1349,8 +1356,29 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) } #ifndef QT_NO_GESTURES + +- (bool)handleGestureAsBeginEnd:(NSEvent *)event +{ + if (QSysInfo::QSysInfo::MacintoshVersion < QSysInfo::MV_10_11) + return false; + + if ([event phase] == NSEventPhaseBegan) { + [self beginGestureWithEvent:event]; + return true; + } + + if ([event phase] == NSEventPhaseEnded) { + [self endGestureWithEvent:event]; + return true; + } + + return false; +} - (void)magnifyWithEvent:(NSEvent *)event { + if ([self handleGestureAsBeginEnd:event]) + return; + qCDebug(lcQpaGestures) << "magnifyWithEvent" << [event magnification]; const NSTimeInterval timestamp = [event timestamp]; QPointF windowPoint; @@ -1377,7 +1405,9 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) - (void)rotateWithEvent:(NSEvent *)event { - qCDebug(lcQpaGestures) << "rotateWithEvent" << [event rotation]; + if ([self handleGestureAsBeginEnd:event]) + return; + const NSTimeInterval timestamp = [event timestamp]; QPointF windowPoint; QPointF screenPoint; diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/eglfs_kms.pro b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/eglfs_kms.pro index 82877710fc..979bfe3ea9 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/eglfs_kms.pro +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/eglfs_kms.pro @@ -1,7 +1,7 @@ TARGET = qeglfs-kms-integration PLUGIN_TYPE = egldeviceintegrations -PLUGIN_CLASS_NAME = QEglFSKmsIntegrationPlugin +PLUGIN_CLASS_NAME = QEglFSKmsGbmIntegrationPlugin load(qt_plugin) QT += core-private gui-private platformsupport-private eglfs_device_lib-private eglfs_kms_support-private diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmcursor.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmcursor.cpp index 8536e2c239..d96c3964df 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmcursor.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmcursor.cpp @@ -107,8 +107,10 @@ QEglFSKmsGbmCursor::~QEglFSKmsGbmCursor() drmModeMoveCursor(kmsScreen->device()->fd(), kmsScreen->output().crtc_id, 0, 0); } - gbm_bo_destroy(m_bo); - m_bo = Q_NULLPTR; + if (m_bo) { + gbm_bo_destroy(m_bo); + m_bo = Q_NULLPTR; + } } void QEglFSKmsGbmCursor::pointerEvent(const QMouseEvent &event) @@ -121,6 +123,9 @@ void QEglFSKmsGbmCursor::changeCursor(QCursor *windowCursor, QWindow *window) { Q_UNUSED(window); + if (!m_bo) + return; + if (!m_visible) return; diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.cpp index 9bb489d6b4..278752bddf 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.cpp @@ -116,9 +116,9 @@ void QEglFSKmsGbmDevice::close() m_globalCursor = Q_NULLPTR; } -EGLNativeDisplayType QEglFSKmsGbmDevice::device() const +EGLNativeDisplayType QEglFSKmsGbmDevice::nativeDisplay() const { - return 0; + return reinterpret_cast<EGLNativeDisplayType>(m_gbm_device); } gbm_device * QEglFSKmsGbmDevice::gbmDevice() const diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.h index 6203a6dc7f..6a45f9ffa0 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.h +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.h @@ -59,7 +59,7 @@ public: bool open() Q_DECL_OVERRIDE; void close() Q_DECL_OVERRIDE; - EGLNativeDisplayType device() const Q_DECL_OVERRIDE; + EGLNativeDisplayType nativeDisplay() const Q_DECL_OVERRIDE; gbm_device *gbmDevice() const; QPlatformCursor *globalCursor() const; diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevice.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevice.cpp index e09f2fdb18..743f714cf0 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevice.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevice.cpp @@ -39,6 +39,7 @@ #include "qeglfskmsegldevice.h" #include "qeglfskmsegldevicescreen.h" +#include "qeglfskmsegldeviceintegration.h" #include <QtCore/private/qcore_unix_p.h> @@ -68,9 +69,9 @@ void QEglFSKmsEglDevice::close() setFd(-1); } -EGLNativeDisplayType QEglFSKmsEglDevice::device() const +EGLNativeDisplayType QEglFSKmsEglDevice::nativeDisplay() const { - return 0; + return static_cast<QEglFSKmsEglDeviceIntegration *>(m_integration)->eglDevice(); } QEglFSKmsScreen *QEglFSKmsEglDevice::createScreen(QEglFSKmsIntegration *integration, QEglFSKmsDevice *device, QEglFSKmsOutput output, QPoint position) diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevice.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevice.h index f85ec27fa2..b1c98f3fe6 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevice.h +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevice.h @@ -50,7 +50,7 @@ public: virtual bool open() Q_DECL_OVERRIDE; virtual void close() Q_DECL_OVERRIDE; - virtual EGLNativeDisplayType device() const Q_DECL_OVERRIDE; + virtual EGLNativeDisplayType nativeDisplay() const Q_DECL_OVERRIDE; virtual QEglFSKmsScreen *createScreen(QEglFSKmsIntegration *integration, QEglFSKmsDevice *device, diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.h index 43c1945a7f..f04c42267a 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.h +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.h @@ -62,6 +62,9 @@ public: QEglFSWindow *createWindow(QWindow *window) const Q_DECL_OVERRIDE; virtual bool separateScreens() const Q_DECL_OVERRIDE; + + EGLDeviceEXT eglDevice() const { return m_egl_device; } + protected: QEglFSKmsDevice *createDevice(const QString &devicePath) Q_DECL_OVERRIDE; diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsdevice.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsdevice.h index ffa8bcbaa5..041c063695 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsdevice.h +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsdevice.h @@ -61,7 +61,7 @@ public: virtual void createScreens(); - virtual EGLNativeDisplayType device() const = 0; + virtual EGLNativeDisplayType nativeDisplay() const = 0; int fd() const; QString devicePath() const; @@ -72,9 +72,6 @@ protected: QPoint position); void setFd(int fd); -private: - Q_DISABLE_COPY(QEglFSKmsDevice) - QEglFSKmsIntegration *m_integration; QString m_path; int m_dri_fd; @@ -91,6 +88,9 @@ private: unsigned int tv_sec, unsigned int tv_usec, void *user_data); + +private: + Q_DISABLE_COPY(QEglFSKmsDevice) }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.cpp index e25e481878..07ea7d4439 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.cpp @@ -90,7 +90,7 @@ void QEglFSKmsIntegration::platformDestroy() EGLNativeDisplayType QEglFSKmsIntegration::platformDisplay() const { Q_ASSERT(m_device); - return reinterpret_cast<EGLNativeDisplayType>(m_device->device()); + return m_device->nativeDisplay(); } bool QEglFSKmsIntegration::usesDefaultScreen() @@ -183,12 +183,12 @@ void QEglFSKmsIntegration::loadConfig() const QJsonObject object = doc.object(); - m_hwCursor = object.value(QStringLiteral("hwcursor")).toBool(m_hwCursor); - m_pbuffers = object.value(QStringLiteral("pbuffers")).toBool(m_pbuffers); - m_devicePath = object.value(QStringLiteral("device")).toString(); - m_separateScreens = object.value(QStringLiteral("separateScreens")).toBool(m_separateScreens); + m_hwCursor = object.value(QLatin1String("hwcursor")).toBool(m_hwCursor); + m_pbuffers = object.value(QLatin1String("pbuffers")).toBool(m_pbuffers); + m_devicePath = object.value(QLatin1String("device")).toString(); + m_separateScreens = object.value(QLatin1String("separateScreens")).toBool(m_separateScreens); - const QJsonArray outputs = object.value(QStringLiteral("outputs")).toArray(); + const QJsonArray outputs = object.value(QLatin1String("outputs")).toArray(); for (int i = 0; i < outputs.size(); i++) { const QVariantMap outputSettings = outputs.at(i).toObject().toVariantMap(); diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.h index c630d93fce..34ac5385a5 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.h +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.h @@ -77,7 +77,6 @@ public: protected: virtual QEglFSKmsDevice *createDevice(const QString &devicePath) = 0; -private: void loadConfig(); QEglFSKmsDevice *m_device; diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp index f614351a40..e6b256f6b2 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp @@ -73,7 +73,7 @@ QEglFSKmsScreen::QEglFSKmsScreen(QEglFSKmsIntegration *integration, QEglFSKmsDevice *device, QEglFSKmsOutput output, QPoint position) - : QEglFSScreen(eglGetDisplay(reinterpret_cast<EGLNativeDisplayType>(device->device()))) + : QEglFSScreen(eglGetDisplay(device->nativeDisplay())) , m_integration(integration) , m_device(device) , m_output(output) diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.h index ed26ca0419..aa698e1b5d 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.h +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.h @@ -111,7 +111,7 @@ public: QPlatformScreen::PowerState powerState() const Q_DECL_OVERRIDE; void setPowerState(QPlatformScreen::PowerState state) Q_DECL_OVERRIDE; -private: +protected: QEglFSKmsIntegration *m_integration; QEglFSKmsDevice *m_device; diff --git a/src/plugins/platforms/ios/ios.pro b/src/plugins/platforms/ios/ios.pro index d82b47fb74..7b0a573ffa 100644 --- a/src/plugins/platforms/ios/ios.pro +++ b/src/plugins/platforms/ios/ios.pro @@ -26,7 +26,8 @@ OBJECTIVE_SOURCES = \ qiosmenu.mm \ qiosfileengineassetslibrary.mm \ qiosfiledialog.mm \ - qiosmessagedialog.mm + qiosmessagedialog.mm \ + qiostextinputoverlay.mm HEADERS = \ qiosintegration.h \ @@ -51,7 +52,8 @@ HEADERS = \ qiosfileenginefactory.h \ qiosfileengineassetslibrary.h \ qiosfiledialog.h \ - qiosmessagedialog.h + qiosmessagedialog.h \ + qiostextinputoverlay.h OTHER_FILES = \ quiview_textinput.mm \ diff --git a/src/plugins/platforms/ios/qiosintegration.h b/src/plugins/platforms/ios/qiosintegration.h index 3372fd539b..a50d9aa571 100644 --- a/src/plugins/platforms/ios/qiosintegration.h +++ b/src/plugins/platforms/ios/qiosintegration.h @@ -46,6 +46,7 @@ #include "qiosapplicationstate.h" #include "qiosfileenginefactory.h" +#include "qiostextinputoverlay.h" QT_BEGIN_NAMESPACE @@ -108,6 +109,7 @@ private: QIOSServices *m_platformServices; mutable QPlatformAccessibility *m_accessibility; QIOSFileEngineFactory m_fileEngineFactory; + QIOSTextInputOverlay m_textInputOverlay; bool m_debugWindowManagement; }; diff --git a/src/plugins/platforms/ios/qiosintegration.mm b/src/plugins/platforms/ios/qiosintegration.mm index 346c1f21aa..fa12d54b28 100644 --- a/src/plugins/platforms/ios/qiosintegration.mm +++ b/src/plugins/platforms/ios/qiosintegration.mm @@ -233,6 +233,8 @@ QPlatformServices *QIOSIntegration::services() const QVariant QIOSIntegration::styleHint(StyleHint hint) const { switch (hint) { + case StartDragTime: + return 300; case PasswordMaskDelay: // this number is based on timing the native delay // since there is no API to get it diff --git a/src/plugins/platforms/ios/qiosscreen.h b/src/plugins/platforms/ios/qiosscreen.h index 461298269d..56a0874bb4 100644 --- a/src/plugins/platforms/ios/qiosscreen.h +++ b/src/plugins/platforms/ios/qiosscreen.h @@ -79,7 +79,7 @@ private: QRect m_geometry; QRect m_availableGeometry; int m_depth; - uint m_pixelDensity; + uint m_physicalDpi; QSizeF m_physicalSize; QIOSOrientationListener *m_orientationListener; }; diff --git a/src/plugins/platforms/ios/qiosscreen.mm b/src/plugins/platforms/ios/qiosscreen.mm index 29cb5876af..bfd22abaa4 100644 --- a/src/plugins/platforms/ios/qiosscreen.mm +++ b/src/plugins/platforms/ios/qiosscreen.mm @@ -186,18 +186,18 @@ QIOSScreen::QIOSScreen(UIScreen *screen) if (deviceIdentifier.contains(QRegularExpression("^iPhone(7,1|8,2)$"))) { // iPhone 6 Plus or iPhone 6S Plus - m_pixelDensity = 401; + m_physicalDpi = 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(); + m_physicalDpi = 132 * devicePixelRatio(); } else { // All non-Plus iPhones, and iPad Minis - m_pixelDensity = 163 * devicePixelRatio(); + m_physicalDpi = 163 * devicePixelRatio(); } } else { // External display, hard to say m_depth = 24; - m_pixelDensity = 96; + m_physicalDpi = 96; } for (UIWindow *existingWindow in [[UIApplication sharedApplication] windows]) { @@ -259,8 +259,23 @@ void QIOSScreen::updateProperties() } if (m_geometry != previousGeometry) { - const qreal millimetersPerInch = 25.4; - m_physicalSize = QSizeF(m_geometry.size() * devicePixelRatio()) / m_pixelDensity * millimetersPerInch; + QRectF physicalGeometry; + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_IOS_8_0) { + // We can't use the primaryOrientation of screen(), as we haven't reported the new geometry yet + Qt::ScreenOrientation primaryOrientation = m_geometry.width() >= m_geometry.height() ? + Qt::LandscapeOrientation : Qt::PortraitOrientation; + + // On iPhone 6+ devices, or when display zoom is enabled, the render buffer is scaled + // before being output on the physical display. We have to take this into account when + // computing the physical size. Note that unlike the native bounds, the physical size + // follows the primary orientation of the screen. + physicalGeometry = mapBetween(nativeOrientation(), primaryOrientation, fromCGRect(m_uiScreen.nativeBounds).toRect()); + } else { + physicalGeometry = QRectF(0, 0, m_geometry.width() * devicePixelRatio(), m_geometry.height() * devicePixelRatio()); + } + + static const qreal millimetersPerInch = 25.4; + m_physicalSize = physicalGeometry.size() / m_physicalDpi * millimetersPerInch; } // At construction time, we don't yet have an associated QScreen, but we still want diff --git a/src/plugins/platforms/ios/qiostextinputoverlay.h b/src/plugins/platforms/ios/qiostextinputoverlay.h new file mode 100644 index 0000000000..2f01993b19 --- /dev/null +++ b/src/plugins/platforms/ios/qiostextinputoverlay.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 QIOSTEXTEDITOVERLAY_H +#define QIOSTEXTEDITOVERLAY_H + +#include <QtCore/QObject> + +Q_FORWARD_DECLARE_OBJC_CLASS(QIOSEditMenu); +Q_FORWARD_DECLARE_OBJC_CLASS(QIOSCursorRecognizer); +Q_FORWARD_DECLARE_OBJC_CLASS(QIOSSelectionRecognizer); +Q_FORWARD_DECLARE_OBJC_CLASS(QIOSTapRecognizer); + +QT_BEGIN_NAMESPACE + +class QIOSTextInputOverlay : public QObject +{ +public: + QIOSTextInputOverlay(); + ~QIOSTextInputOverlay(); + + static QIOSEditMenu *s_editMenu; + +private: + QIOSCursorRecognizer *m_cursorRecognizer; + QIOSSelectionRecognizer *m_selectionRecognizer; + QIOSTapRecognizer *m_openMenuOnTapRecognizer; + + void updateFocusObject(); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/platforms/ios/qiostextinputoverlay.mm b/src/plugins/platforms/ios/qiostextinputoverlay.mm new file mode 100644 index 0000000000..3fa9341540 --- /dev/null +++ b/src/plugins/platforms/ios/qiostextinputoverlay.mm @@ -0,0 +1,995 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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$ +** +****************************************************************************/ + +#import <UIKit/UIGestureRecognizerSubclass.h> +#import <UIKit/UITextView.h> + +#include <QtGui/QGuiApplication> +#include <QtGui/QInputMethod> +#include <QtGui/QStyleHints> + +#include <QtGui/private/qinputmethod_p.h> +#include <QtCore/private/qobject_p.h> + +#include "qiosglobal.h" +#include "qiostextinputoverlay.h" + +typedef QPair<int, int> SelectionPair; + +static const CGFloat kKnobWidth = 10; + +static QPlatformInputContext *platformInputContext() +{ + return static_cast<QInputMethodPrivate *>(QObjectPrivate::get(QGuiApplication::inputMethod()))->platformInputContext(); +} + +static SelectionPair querySelection() +{ + QInputMethodQueryEvent query(Qt::ImAnchorPosition | Qt::ImCursorPosition); + QGuiApplication::sendEvent(QGuiApplication::focusObject(), &query); + int anchorPos = query.value(Qt::ImAnchorPosition).toInt(); + int cursorPos = query.value(Qt::ImCursorPosition).toInt(); + return qMakePair<int, int>(anchorPos, cursorPos); +} + +static bool hasSelection() +{ + SelectionPair selection = querySelection(); + return selection.first != selection.second; +} + +static void executeBlockWithoutAnimation(void (^block)(void)) +{ + [CATransaction begin]; + [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions]; + block(); + [CATransaction commit]; +} + +// ------------------------------------------------------------------------- +/** + QIOSEditMenu is just a wrapper class around UIMenuController to + ease showing and hiding it correcly. + */ +@interface QIOSEditMenu : NSObject +@property (nonatomic, assign) BOOL visible; +@property (nonatomic, readonly) BOOL isHiding; +@property (nonatomic, assign) BOOL reshowAfterHidden; +@end + +@implementation QIOSEditMenu + +- (id)init +{ + if (self = [super init]) { + NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; + + [center addObserverForName:UIMenuControllerWillHideMenuNotification + object:nil queue:nil usingBlock:^(NSNotification *) { + _isHiding = YES; + }]; + + [center addObserverForName:UIMenuControllerDidHideMenuNotification + object:nil queue:nil usingBlock:^(NSNotification *) { + _isHiding = NO; + if (self.reshowAfterHidden) { + // To not abort an ongoing hide transition when showing the menu, you can set + // reshowAfterHidden to wait until the transition finishes before reshowing it. + self.reshowAfterHidden = NO; + dispatch_async(dispatch_get_main_queue (), ^{ self.visible = YES; }); + } + }]; + } + + return self; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self name:nil object:nil]; + [super dealloc]; +} + +- (BOOL)visible +{ + return [UIMenuController sharedMenuController].menuVisible; +} + +- (void)setVisible:(BOOL)visible +{ + if (visible == self.visible) + return; + + if (visible) { + // Note that the contents of the edit menu is decided by + // first responder, which is normally QIOSTextResponder. + QRectF cr = qApp->inputMethod()->cursorRectangle(); + QRectF ar = qApp->inputMethod()->anchorRectangle(); + CGRect targetRect = toCGRect(cr.united(ar)); + UIView *focusView = reinterpret_cast<UIView *>(qApp->focusWindow()->winId()); + [[UIMenuController sharedMenuController] setTargetRect:targetRect inView:focusView]; + [[UIMenuController sharedMenuController] setMenuVisible:YES animated:YES]; + } else { + [[UIMenuController sharedMenuController] setMenuVisible:NO animated:YES]; + } +} + +@end + +// ------------------------------------------------------------------------- + +@interface QIOSLoupeLayer : CALayer { + UIView *_snapshotView; + BOOL _pendingSnapshotUpdate; + UIView *_loupeImageView; + CALayer *_containerLayer; + CGFloat _loupeOffset; + QTimer _updateTimer; +} +@property (nonatomic, retain) UIView *targetView; +@property (nonatomic, assign) CGPoint focalPoint; +@property (nonatomic, assign) BOOL visible; +@end + +@implementation QIOSLoupeLayer + +- (id)initWithSize:(CGSize)size cornerRadius:(CGFloat)cornerRadius bottomOffset:(CGFloat)bottomOffset +{ + if (self = [super init]) { + _loupeOffset = bottomOffset + (size.height / 2); + _snapshotView = nil; + _pendingSnapshotUpdate = YES; + _updateTimer.setInterval(100); + _updateTimer.setSingleShot(true); + QObject::connect(&_updateTimer, &QTimer::timeout, [self](){ [self updateSnapshot]; }); + + // Create own geometry and outer shadow + self.frame = CGRectMake(0, 0, size.width, size.height); + self.cornerRadius = cornerRadius; + self.shadowColor = [[UIColor grayColor] CGColor]; + self.shadowOffset = CGSizeMake(0, 1); + self.shadowRadius = 2.0; + self.shadowOpacity = 0.75; + self.transform = CATransform3DMakeScale(0, 0, 0); + + // Create container view for the snapshots + _containerLayer = [[CALayer new] autorelease]; + _containerLayer.frame = self.bounds; + _containerLayer.cornerRadius = cornerRadius; + _containerLayer.masksToBounds = YES; + [self addSublayer:_containerLayer]; + + // Create inner loupe shadow + const CGFloat inset = 30; + CALayer *topShadeLayer = [[CALayer new] autorelease]; + topShadeLayer.frame = CGRectOffset(CGRectInset(self.bounds, -inset, -inset), 0, inset / 2); + topShadeLayer.borderWidth = inset / 2; + topShadeLayer.cornerRadius = cornerRadius; + topShadeLayer.borderColor = [[UIColor blackColor] CGColor]; + topShadeLayer.shadowColor = [[UIColor blackColor] CGColor]; + topShadeLayer.shadowOffset = CGSizeMake(0, 0); + topShadeLayer.shadowRadius = 15.0; + topShadeLayer.shadowOpacity = 0.6; + // Keep the shadow inside the loupe + CALayer *mask = [[CALayer new] autorelease]; + mask.frame = CGRectOffset(self.bounds, inset, inset / 2); + mask.backgroundColor = [[UIColor blackColor] CGColor]; + mask.cornerRadius = cornerRadius; + topShadeLayer.mask = mask; + [self addSublayer:topShadeLayer]; + + // Create border around the loupe. We need to do this in a separate + // layer (as opposed to on self) to not draw the border on top of + // overlapping external children (arrow). + CALayer *borderLayer = [[CALayer new] autorelease]; + borderLayer.frame = self.bounds; + borderLayer.borderWidth = 0.75; + borderLayer.cornerRadius = cornerRadius; + borderLayer.borderColor = [[UIColor lightGrayColor] CGColor]; + [self addSublayer:borderLayer]; + + if (QSysInfo::MacintoshVersion < QSysInfo::MV_IOS_7_0) { + // [UIView snapshotViewAfterScreenUpdates:] is available since iOS 7.0. + // Just silently ignore showing the loupe for older versions. + self.hidden = YES; + } + } + + return self; +} + +- (void)dealloc +{ + _targetView = nil; + [super dealloc]; +} + +- (void)setVisible:(BOOL)visible +{ + if (_visible == visible) + return; + + _visible = visible; + + dispatch_async(dispatch_get_main_queue (), ^{ + // Setting transform later, since CA will not perform an animation if + // changing values directly after init, and if the scale ends up empty. + self.transform = _visible ? CATransform3DMakeScale(1, 1, 1) : CATransform3DMakeScale(0.0, 0.0, 1); + }); +} + +- (void)updateSnapshot +{ + _pendingSnapshotUpdate = YES; + [self setNeedsDisplay]; +} + +- (void)setFocalPoint:(CGPoint)point +{ + _focalPoint = point; + [self updateSnapshot]; + + // Schedule a delayed update as well to ensure that we end up with a correct + // snapshot of the cursor, since QQuickRenderThread lags a bit behind + _updateTimer.start(); +} + +- (void)display +{ + if (QSysInfo::MacintoshVersion < QSysInfo::MV_IOS_7_0) + return; + + // Take a snapshow of the target view, magnify the area around the focal + // point, and add the snapshow layer as a child of the container layer + // to make it look like a loupe. Then place this layer at the position of + // the focal point with the requested offset. + executeBlockWithoutAnimation(^{ + if (_pendingSnapshotUpdate) { + UIView *newSnapshot = [_targetView snapshotViewAfterScreenUpdates:NO]; + [_snapshotView.layer removeFromSuperlayer]; + [_snapshotView release]; + _snapshotView = [newSnapshot retain]; + [_containerLayer addSublayer:_snapshotView.layer]; + _pendingSnapshotUpdate = NO; + } + + self.position = CGPointMake(_focalPoint.x, _focalPoint.y - _loupeOffset); + + const CGFloat loupeScale = 1.5; + CGFloat x = -(_focalPoint.x * loupeScale) + self.frame.size.width / 2; + CGFloat y = -(_focalPoint.y * loupeScale) + self.frame.size.height / 2; + CGFloat w = _targetView.frame.size.width * loupeScale; + CGFloat h = _targetView.frame.size.height * loupeScale; + _snapshotView.layer.frame = CGRectMake(x, y, w, h); + }); +} + +@end + +// ------------------------------------------------------------------------- + +@interface QIOSHandleLayer : CALayer { + CALayer *_handleCursorLayer; + CALayer *_handleKnobLayer; + Qt::Edge _selectionEdge; +} +@property (nonatomic, assign) CGRect cursorRectangle; +@property (nonatomic, assign) CGFloat handleScale; +@property (nonatomic, assign) BOOL visible; +@end + +@implementation QIOSHandleLayer + +@dynamic handleScale; + +- (id)initWithKnobAtEdge:(Qt::Edge)selectionEdge +{ + if (self = [super init]) { + CGColorRef bgColor = [UIColor colorWithRed:0.1 green:0.4 blue:0.9 alpha:1].CGColor; + _selectionEdge = selectionEdge; + self.handleScale = 0; + + _handleCursorLayer = [[CALayer new] autorelease]; + _handleCursorLayer.masksToBounds = YES; + _handleCursorLayer.backgroundColor = bgColor; + [self addSublayer:_handleCursorLayer]; + + _handleKnobLayer = [[CALayer new] autorelease]; + _handleKnobLayer.masksToBounds = YES; + _handleKnobLayer.backgroundColor = bgColor; + _handleKnobLayer.cornerRadius = kKnobWidth / 2; + [self addSublayer:_handleKnobLayer]; + } + return self; +} + ++ (BOOL)needsDisplayForKey:(NSString *)key +{ + if ([key isEqualToString:@"handleScale"]) + return YES; + return [super needsDisplayForKey:key]; +} + +- (id<CAAction>)actionForKey:(NSString *)key +{ + if ([key isEqualToString:@"handleScale"]) { + if (_visible) { + // The handle should "bounce" in when becoming visible + CAKeyframeAnimation * animation = [CAKeyframeAnimation animationWithKeyPath:key]; + [animation setDuration:0.5]; + animation.values = [NSArray arrayWithObjects: + [NSNumber numberWithFloat:0], + [NSNumber numberWithFloat:1.3], + [NSNumber numberWithFloat:1.3], + [NSNumber numberWithFloat:1], nil]; + animation.keyTimes = [NSArray arrayWithObjects: + [NSNumber numberWithFloat:0], + [NSNumber numberWithFloat:0.3], + [NSNumber numberWithFloat:0.9], + [NSNumber numberWithFloat:1], nil]; + return animation; + } else { + CABasicAnimation * animation = [CABasicAnimation animationWithKeyPath:key]; + animation.fromValue = [self valueForKey:key]; + [animation setDuration:0.2]; + return animation; + } + } + return [super actionForKey:key]; +} + +- (void)setVisible:(BOOL)visible +{ + if (visible == _visible) + return; + + _visible = visible; + + self.handleScale = visible ? 1 : 0; +} + +- (void)setCursorRectangle:(CGRect)cursorRect +{ + if (CGRectEqualToRect(_cursorRectangle, cursorRect)) + return; + + _cursorRectangle = cursorRect; + + executeBlockWithoutAnimation(^{ + [self setNeedsDisplay]; + [self displayIfNeeded]; + }); +} + +- (void)display +{ + CGFloat cursorWidth = 2; + CGPoint origin = _cursorRectangle.origin; + CGSize size = _cursorRectangle.size; + CGFloat scale = ((QIOSHandleLayer *)[self presentationLayer]).handleScale; + CGFloat edgeAdjustment = (_selectionEdge == Qt::LeftEdge) ? 0.5 - cursorWidth : -0.5; + + CGFloat cursorX = origin.x + (size.width / 2) + edgeAdjustment; + CGFloat cursorY = origin.y; + CGFloat knobX = cursorX - (kKnobWidth - cursorWidth) / 2; + CGFloat knobY = origin.y + ((_selectionEdge == Qt::LeftEdge) ? -kKnobWidth : size.height); + + _handleCursorLayer.frame = CGRectMake(cursorX, cursorY, cursorWidth, size.height); + _handleKnobLayer.frame = CGRectMake(knobX, knobY, kKnobWidth, kKnobWidth); + _handleCursorLayer.transform = CATransform3DMakeScale(1, scale, scale); + _handleKnobLayer.transform = CATransform3DMakeScale(scale, scale, scale); +} + +@end + +// ------------------------------------------------------------------------- + +/** + QIOSLoupeRecognizer is only a base class from which other recognisers + below will inherit. It takes care of creating and showing a magnifier + glass depending on the current gesture state. + */ +@interface QIOSLoupeRecognizer : UIGestureRecognizer <UIGestureRecognizerDelegate> { +@public + QIOSLoupeLayer *_loupeLayer; + UIView *_desktopView; + CGPoint _firstTouchPoint; + CGPoint _lastTouchPoint; + QTimer _triggerStateBeganTimer; + int _originalCursorFlashTime; +} +@property (nonatomic, assign) QPointF focalPoint; +@property (nonatomic, assign) BOOL dragTriggersGesture; +@property (nonatomic, readonly) UIView *focusView; +@end + +@implementation QIOSLoupeRecognizer + +- (id)init +{ + if (self = [super initWithTarget:self action:@selector(gestureStateChanged)]) { + self.enabled = NO; + _triggerStateBeganTimer.setInterval(QGuiApplication::styleHints()->startDragTime()); + _triggerStateBeganTimer.setSingleShot(true); + QObject::connect(&_triggerStateBeganTimer, &QTimer::timeout, [=](){ + self.state = UIGestureRecognizerStateBegan; + }); + } + + return self; +} + +- (void)setEnabled:(BOOL)enabled +{ + if (enabled == self.enabled) + return; + + [super setEnabled:enabled]; + + if (enabled) { + _focusView = [reinterpret_cast<UIView *>(qApp->focusWindow()->winId()) retain]; + _desktopView = [[UIApplication sharedApplication].keyWindow.rootViewController.view retain]; + Q_ASSERT(_focusView && _desktopView && _desktopView.superview); + [_desktopView addGestureRecognizer:self]; + } else { + [_desktopView removeGestureRecognizer:self]; + [_desktopView release]; + _desktopView = nil; + [_focusView release]; + _focusView = nil; + _triggerStateBeganTimer.stop(); + if (_loupeLayer) { + [_loupeLayer removeFromSuperlayer]; + [_loupeLayer release]; + _loupeLayer = nil; + } + } +} + +- (void)gestureStateChanged +{ + switch (self.state) { + case UIGestureRecognizerStateBegan: + // Stop cursor blinking, and show the loupe + _originalCursorFlashTime = QGuiApplication::styleHints()->cursorFlashTime(); + QGuiApplication::styleHints()->setCursorFlashTime(0); + if (!_loupeLayer) + [self createLoupe]; + [self updateFocalPoint:fromCGPoint(_lastTouchPoint)]; + _loupeLayer.visible = YES; + break; + case UIGestureRecognizerStateChanged: + // Tell the sub class to move the loupe to the correct position + [self updateFocalPoint:fromCGPoint(_lastTouchPoint)]; + break; + case UIGestureRecognizerStateEnded: + // Restore cursor blinking, and hide the loupe + QGuiApplication::styleHints()->setCursorFlashTime(_originalCursorFlashTime); + QIOSTextInputOverlay::s_editMenu.visible = YES; + _loupeLayer.visible = NO; + break; + default: + _loupeLayer.visible = NO; + break; + } +} + +- (void)createLoupe +{ + // We magnify the the desktop view. But the loupe itself will be added as a child + // of the desktop view's parent, so it doesn't become a part of what we magnify. + _loupeLayer = [[self createLoupeLayer] retain]; + _loupeLayer.targetView = _desktopView; + [_desktopView.superview.layer addSublayer:_loupeLayer]; +} + +- (QPointF)focalPoint +{ + return fromCGPoint([_loupeLayer.targetView convertPoint:_loupeLayer.focalPoint toView:_focusView]); +} + +- (void)setFocalPoint:(QPointF)point +{ + _loupeLayer.focalPoint = [_loupeLayer.targetView convertPoint:toCGPoint(point) fromView:_focusView]; +} + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event +{ + [super touchesBegan:touches withEvent:event]; + if ([event allTouches].count > 1) { + // We only support text selection with one finger + self.state = UIGestureRecognizerStateFailed; + return; + } + + _firstTouchPoint = [static_cast<UITouch *>([touches anyObject]) locationInView:_focusView]; + _lastTouchPoint = _firstTouchPoint; + + // If the touch point is accepted by the sub class (e.g touch on cursor), we start a + // press'n'hold timer that eventually will move the state to UIGestureRecognizerStateBegan. + if ([self acceptTouchesBegan:fromCGPoint(_firstTouchPoint)]) + _triggerStateBeganTimer.start(); + else + self.state = UIGestureRecognizerStateFailed; +} + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event +{ + [super touchesMoved:touches withEvent:event]; + _lastTouchPoint = [static_cast<UITouch *>([touches anyObject]) locationInView:_focusView]; + + if (self.state == UIGestureRecognizerStatePossible) { + // If the touch was moved too far before the timer triggered (meaning that this + // is a drag, not a press'n'hold), we should either fail, or trigger the gesture + // immediately, depending on self.dragTriggersGesture. + int startDragDistance = QGuiApplication::styleHints()->startDragDistance(); + int dragDistance = hypot(_firstTouchPoint.x - _lastTouchPoint.x, _firstTouchPoint.y - _lastTouchPoint.y); + if (dragDistance > startDragDistance) { + _triggerStateBeganTimer.stop(); + self.state = self.dragTriggersGesture ? UIGestureRecognizerStateBegan : UIGestureRecognizerStateFailed; + } + } else { + self.state = UIGestureRecognizerStateChanged; + } +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event +{ + [super touchesEnded:touches withEvent:event]; + _triggerStateBeganTimer.stop(); + _lastTouchPoint = [static_cast<UITouch *>([touches anyObject]) locationInView:_focusView]; + self.state = self.state == UIGestureRecognizerStatePossible ? UIGestureRecognizerStateFailed : UIGestureRecognizerStateEnded; +} + +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event +{ + [super touchesCancelled:touches withEvent:event]; + _triggerStateBeganTimer.stop(); + _lastTouchPoint = [static_cast<UITouch *>([touches anyObject]) locationInView:_focusView]; + self.state = UIGestureRecognizerStateCancelled; +} + +// Methods implemented by subclasses: + +- (BOOL)acceptTouchesBegan:(QPointF)touchPoint +{ + Q_UNUSED(touchPoint) + Q_UNREACHABLE(); + return NO; +} + +- (QIOSLoupeLayer *)createLoupeLayer +{ + Q_UNREACHABLE(); + return Q_NULLPTR; +} + +- (void)updateFocalPoint:(QPointF)touchPoint +{ + Q_UNUSED(touchPoint) + Q_UNREACHABLE(); +} + +@end + +// ------------------------------------------------------------------------- + +/** + This recognizer will be active when there's no selection. It will trigger if + the user does a press and hold, which will start a session where the user can move + the cursor around with his finger together with a magnifier glass. + */ +@interface QIOSCursorRecognizer : QIOSLoupeRecognizer +@end + +@implementation QIOSCursorRecognizer + +- (QIOSLoupeLayer *)createLoupeLayer +{ + return [[[QIOSLoupeLayer alloc] initWithSize:CGSizeMake(120, 120) cornerRadius:60 bottomOffset:4] autorelease]; +} + +- (BOOL)acceptTouchesBegan:(QPointF)touchPoint +{ + QRectF inputRect = QGuiApplication::inputMethod()->inputItemClipRectangle(); + return !hasSelection() && inputRect.contains(touchPoint); +} + +- (void)updateFocalPoint:(QPointF)touchPoint +{ + platformInputContext()->setSelectionOnFocusObject(touchPoint, touchPoint); + self.focalPoint = touchPoint; +} + +@end + +// ------------------------------------------------------------------------- + +/** + This recognizer will watch for selections, and draw handles as overlay + on the sides. If the user starts dragging on a handle (or do a press and + hold), it will show a magnifier glass that follows the handle as it moves. + */ +@interface QIOSSelectionRecognizer : QIOSLoupeRecognizer { + CALayer *_clipRectLayer; + QIOSHandleLayer *_cursorLayer; + QIOSHandleLayer *_anchorLayer; + QPointF _touchOffset; + bool _dragOnCursor; + bool _multiLine; + QTimer _updateSelectionTimer; + QMetaObject::Connection _cursorConnection; + QMetaObject::Connection _anchorConnection; + QMetaObject::Connection _clipRectConnection; +} +@end + +@implementation QIOSSelectionRecognizer + +- (id)init +{ + if (self = [super init]) { + self.delaysTouchesBegan = YES; + self.dragTriggersGesture = YES; + _multiLine = QInputMethod::queryFocusObject(Qt::ImHints, QVariant()).toUInt() & Qt::ImhMultiLine; + _updateSelectionTimer.setInterval(1); + _updateSelectionTimer.setSingleShot(true); + QObject::connect(&_updateSelectionTimer, &QTimer::timeout, [self](){ [self updateSelection]; }); + } + + return self; +} + +- (void)setEnabled:(BOOL)enabled +{ + if (enabled == self.enabled) + return; + + [super setEnabled:enabled]; + + if (enabled) { + // Create a layer that clips the handles inside the input field + _clipRectLayer = [[CALayer new] autorelease]; + _clipRectLayer.masksToBounds = YES; + [self.focusView.layer addSublayer:_clipRectLayer]; + + // Create the handle layers, and add them to the clipped input rect layer + _cursorLayer = [[[QIOSHandleLayer alloc] initWithKnobAtEdge:Qt::RightEdge] autorelease]; + _anchorLayer = [[[QIOSHandleLayer alloc] initWithKnobAtEdge:Qt::LeftEdge] autorelease]; + bool selection = hasSelection(); + _cursorLayer.visible = selection; + _anchorLayer.visible = selection; + [_clipRectLayer addSublayer:_cursorLayer]; + [_clipRectLayer addSublayer:_anchorLayer]; + + // iOS text input will sometimes set a temporary text selection to perform operations + // such as backspace (select last character + cut selection). To avoid briefly showing + // the selection handles for such cases, and to avoid calling updateSelection when + // both handles and clip rectangle change, we use a timer to wait a cycle before we update. + // (Note that since QTimer::start is overloaded, we need some extra syntax for the connections). + QInputMethod *im = QGuiApplication::inputMethod(); + void(QTimer::*start)(void) = &QTimer::start; + _cursorConnection = QObject::connect(im, &QInputMethod::cursorRectangleChanged, &_updateSelectionTimer, start); + _anchorConnection = QObject::connect(im, &QInputMethod::anchorRectangleChanged, &_updateSelectionTimer, start); + _clipRectConnection = QObject::connect(im, &QInputMethod::inputItemClipRectangleChanged, &_updateSelectionTimer, start); + + [self updateSelection]; + } else { + [_clipRectLayer removeFromSuperlayer]; + _clipRectLayer = 0; + _cursorLayer = 0; + _anchorLayer = 0; + _updateSelectionTimer.stop(); + + QObject::disconnect(_cursorConnection); + QObject::disconnect(_anchorConnection); + QObject::disconnect(_clipRectConnection); + } +} + +- (QIOSLoupeLayer *)createLoupeLayer +{ + CGSize loupeSize = CGSizeMake(123, 33); + CGSize arrowSize = CGSizeMake(25, 12); + CGFloat loupeOffset = arrowSize.height + 20; + + // Create loupe and arrow layers + QIOSLoupeLayer *loupeLayer = [[[QIOSLoupeLayer alloc] initWithSize:loupeSize cornerRadius:5 bottomOffset:loupeOffset] autorelease]; + CAShapeLayer *arrowLayer = [[[CAShapeLayer alloc] init] autorelease]; + + // Build a triangular path to both draw and mask the arrow layer as a triangle + UIBezierPath *path = [[UIBezierPath new] autorelease]; + [path moveToPoint:CGPointMake(0, 0)]; + [path addLineToPoint:CGPointMake(arrowSize.width / 2, arrowSize.height)]; + [path addLineToPoint:CGPointMake(arrowSize.width, 0)]; + + arrowLayer.frame = CGRectMake((loupeSize.width - arrowSize.width) / 2, loupeSize.height - 1, arrowSize.width, arrowSize.height); + arrowLayer.path = path.CGPath; + arrowLayer.backgroundColor = [[UIColor whiteColor] CGColor]; + arrowLayer.strokeColor = [[UIColor lightGrayColor] CGColor]; + arrowLayer.lineWidth = 0.75 * 2; + arrowLayer.fillColor = nil; + + CAShapeLayer *mask = [[CAShapeLayer new] autorelease]; + mask.frame = arrowLayer.bounds; + mask.path = path.CGPath; + arrowLayer.mask = mask; + + [loupeLayer addSublayer:arrowLayer]; + + return loupeLayer; +} + +- (BOOL)acceptTouchesBegan:(QPointF)touchPoint +{ + if (!hasSelection()) + return NO; + + // Accept the touch if it "overlaps" with any of the handles + const int handleRadius = 50; + QPointF cursorCenter = qApp->inputMethod()->cursorRectangle().center(); + QPointF anchorCenter = qApp->inputMethod()->anchorRectangle().center(); + QPointF cursorOffset = QPointF(cursorCenter.x() - touchPoint.x(), cursorCenter.y() - touchPoint.y()); + QPointF anchorOffset = QPointF(anchorCenter.x() - touchPoint.x(), anchorCenter.y() - touchPoint.y()); + double cursorDist = hypot(cursorOffset.x(), cursorOffset.y()); + double anchorDist = hypot(anchorOffset.x(), anchorOffset.y()); + + if (cursorDist > handleRadius && anchorDist > handleRadius) + return NO; + + if (cursorDist < anchorDist) { + _touchOffset = cursorOffset; + _dragOnCursor = YES; + } else { + _touchOffset = anchorOffset; + _dragOnCursor = NO; + } + + return YES; +} + +- (void)updateFocalPoint:(QPointF)touchPoint +{ + touchPoint += _touchOffset; + + // Get the text position under the touch + SelectionPair selection = querySelection(); + const QTransform mapToLocal = QGuiApplication::inputMethod()->inputItemTransform().inverted(); + int touchTextPos = QInputMethod::queryFocusObject(Qt::ImCursorPosition, touchPoint * mapToLocal).toInt(); + + // Ensure that the handels cannot be dragged past each other + if (_dragOnCursor) + selection.second = (touchTextPos > selection.first) ? touchTextPos : selection.first + 1; + else + selection.first = (touchTextPos < selection.second) ? touchTextPos : selection.second - 1; + + // Set new selection + QList<QInputMethodEvent::Attribute> imAttributes; + imAttributes.append(QInputMethodEvent::Attribute( + QInputMethodEvent::Selection, selection.first, selection.second - selection.first, QVariant())); + QInputMethodEvent event(QString(), imAttributes); + QGuiApplication::sendEvent(qApp->focusObject(), &event); + + // Move loupe to new position + QRectF handleRect = _dragOnCursor ? + qApp->inputMethod()->cursorRectangle() : + qApp->inputMethod()->anchorRectangle(); + self.focalPoint = QPointF(touchPoint.x(), handleRect.center().y()); +} + +- (void)updateSelection +{ + if (!hasSelection()) { + _cursorLayer.visible = NO; + _anchorLayer.visible = NO; + QIOSTextInputOverlay::s_editMenu.visible = NO; + return; + } + + if (!_cursorLayer.visible && QIOSTextInputOverlay::s_editMenu.isHiding) { + // Since the edit menu is hiding and this is the first selection thereafter, we + // assume that the selection came from the user tapping on a menu item. In that + // case, we reshow the menu after it has closed (but then with selection based + // menu items, as specified by first responder). + QIOSTextInputOverlay::s_editMenu.reshowAfterHidden = YES; + } + + // Adjust handles and input rect to match the new selection + QRectF inputRect = QGuiApplication::inputMethod()->inputItemClipRectangle(); + CGRect cursorRect = toCGRect(QGuiApplication::inputMethod()->cursorRectangle()); + CGRect anchorRect = toCGRect(QGuiApplication::inputMethod()->anchorRectangle()); + + if (!_multiLine) { + // Resize the layer a bit bigger to ensure that the handles are + // not cut if if they are otherwise visible inside the clip rect. + int margin = kKnobWidth + 5; + inputRect.adjust(-margin / 2, -margin, margin / 2, margin); + } + + executeBlockWithoutAnimation(^{ _clipRectLayer.frame = toCGRect(inputRect); }); + _cursorLayer.cursorRectangle = [self.focusView.layer convertRect:cursorRect toLayer:_clipRectLayer]; + _anchorLayer.cursorRectangle = [self.focusView.layer convertRect:anchorRect toLayer:_clipRectLayer]; + _cursorLayer.visible = YES; + _anchorLayer.visible = YES; +} + +@end + +// ------------------------------------------------------------------------- + +/** + This recognizer will trigger if the user taps inside the edit rectangle. + If there's no selection, and the tap doesn't change the cursor position, the + visibility of the edit menu will be toggled. Otherwise, if there's a selection, a + first tap will close the edit menu (if any), and a second tap will remove the selection. + */ +@interface QIOSTapRecognizer : UITapGestureRecognizer { + int _cursorPosOnPress; + UIView *_focusView; +} +@end + +@implementation QIOSTapRecognizer + +- (id)init +{ + if (self = [super initWithTarget:self action:@selector(gestureStateChanged)]) { + self.enabled = NO; + } + + return self; +} + +- (void)setEnabled:(BOOL)enabled +{ + if (enabled == self.enabled) + return; + + [super setEnabled:enabled]; + + if (enabled) { + _focusView = [reinterpret_cast<UIView *>(qApp->focusWindow()->winId()) retain]; + [_focusView addGestureRecognizer:self]; + } else { + [_focusView removeGestureRecognizer:self]; + [_focusView release]; + _focusView = nil; + } +} + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event +{ + [super touchesBegan:touches withEvent:event]; + + if (hasSelection() && !QIOSTextInputOverlay::s_editMenu.isHiding) { + // If there's a selection and the menu is visible, UIKit will hide the menu on the + // first tap. But if we get a second tap while the menu is hidden, we choose to diverge + // a bit from native behavior and instead fail the tap and forward the touch + // to Qt. This will effectively move the cursor (and remove the selection). + // This is needed to ensure that the user can remove the selection at any time, but + // at the same time, also be able to tap on other items in the UI while keeping the + // selection (e.g make the selection bold by tapping on a bold button in the UI). + self.state = UIGestureRecognizerStateFailed; + return; + } + + QRectF inputRect = QGuiApplication::inputMethod()->inputItemClipRectangle(); + QPointF touchPos = fromCGPoint([static_cast<UITouch *>([touches anyObject]) locationInView:_focusView]); + if (!inputRect.contains(touchPos)) + self.state = UIGestureRecognizerStateFailed; + + _cursorPosOnPress = QInputMethod::queryFocusObject(Qt::ImCursorPosition, QVariant()).toInt(); +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event +{ + QPointF touchPos = fromCGPoint([static_cast<UITouch *>([touches anyObject]) locationInView:_focusView]); + const QTransform mapToLocal = QGuiApplication::inputMethod()->inputItemTransform().inverted(); + int cursorPosOnRelease = QInputMethod::queryFocusObject(Qt::ImCursorPosition, touchPos * mapToLocal).toInt(); + + if (!QIOSTextInputOverlay::s_editMenu.isHiding && cursorPosOnRelease != _cursorPosOnPress) { + // We also want to track if the user taps on the screen to close the edit menu. And + // the way we detect that is to check if the edit menu is hiding when we receive this + // call. If that's the case, we leave the state as-is to allow a tap to be recognized. + // Otherwise, if we also see that the cursor will change position, we fail, so that + // touch events for Qt are not cancelled. + self.state = UIGestureRecognizerStateFailed; + } + + [super touchesEnded:touches withEvent:event]; +} + +- (void)gestureStateChanged +{ + if (self.state != UIGestureRecognizerStateEnded) + return; + + if (QIOSTextInputOverlay::s_editMenu.isHiding) { + // Closing the menu is what we want for the first tap, so just return + return; + } + + QIOSTextInputOverlay::s_editMenu.visible = !QIOSTextInputOverlay::s_editMenu.visible; +} + +@end + +// ------------------------------------------------------------------------- + +QT_BEGIN_NAMESPACE + +QIOSEditMenu *QIOSTextInputOverlay::s_editMenu = Q_NULLPTR; + +QIOSTextInputOverlay::QIOSTextInputOverlay() + : m_cursorRecognizer(Q_NULLPTR) + , m_selectionRecognizer(Q_NULLPTR) + , m_openMenuOnTapRecognizer(Q_NULLPTR) +{ + connect(qApp, &QGuiApplication::focusObjectChanged, this, &QIOSTextInputOverlay::updateFocusObject); +} + +QIOSTextInputOverlay::~QIOSTextInputOverlay() +{ + disconnect(qApp, 0, this, 0); +} + +void QIOSTextInputOverlay::updateFocusObject() +{ + if (m_cursorRecognizer) { + // Destroy old recognizers since they were created with + // dependencies to the old focus object (focus view). + m_cursorRecognizer.enabled = NO; + m_selectionRecognizer.enabled = NO; + m_openMenuOnTapRecognizer.enabled = NO; + [m_cursorRecognizer release]; + [m_selectionRecognizer release]; + [m_openMenuOnTapRecognizer release]; + [s_editMenu release]; + m_cursorRecognizer = Q_NULLPTR; + m_selectionRecognizer = Q_NULLPTR; + m_openMenuOnTapRecognizer = Q_NULLPTR; + s_editMenu = Q_NULLPTR; + } + + if (platformInputContext()->inputMethodAccepted()) { + s_editMenu = [QIOSEditMenu new]; + m_cursorRecognizer = [QIOSCursorRecognizer new]; + m_selectionRecognizer = [QIOSSelectionRecognizer new]; + m_openMenuOnTapRecognizer = [QIOSTapRecognizer new]; + m_cursorRecognizer.enabled = YES; + m_selectionRecognizer.enabled = YES; + m_openMenuOnTapRecognizer.enabled = YES; + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qiostextresponder.mm b/src/plugins/platforms/ios/qiostextresponder.mm index 6a66bf213e..eb12739e98 100644 --- a/src/plugins/platforms/ios/qiostextresponder.mm +++ b/src/plugins/platforms/ios/qiostextresponder.mm @@ -324,7 +324,7 @@ // a regular responder transfer to another window. In the former case, iOS // will set the new first-responder to our next-responder, and in the latter // case we'll have an active responder candidate. - if (![UIResponder currentFirstResponder]) { + if (![UIResponder currentFirstResponder] && !FirstResponderCandidate::currentCandidate()) { // No first responder set anymore, sync this with Qt by clearing the // focus object. m_inputContext->clearCurrentFocusObject(); diff --git a/src/plugins/platforms/linuxfb/qlinuxfbscreen.cpp b/src/plugins/platforms/linuxfb/qlinuxfbscreen.cpp index 025dc22111..f0c29130f8 100644 --- a/src/plugins/platforms/linuxfb/qlinuxfbscreen.cpp +++ b/src/plugins/platforms/linuxfb/qlinuxfbscreen.cpp @@ -263,15 +263,15 @@ static int openTtyDevice(const QString &device) return fd; } -static bool switchToGraphicsMode(int ttyfd, int *oldMode) +static void switchToGraphicsMode(int ttyfd, bool doSwitch, int *oldMode) { - ioctl(ttyfd, KDGETMODE, oldMode); - if (*oldMode != KD_GRAPHICS) { - if (ioctl(ttyfd, KDSETMODE, KD_GRAPHICS) != 0) - return false; + // Do not warn if the switch fails: the ioctl fails when launching from a + // remote console and there is nothing we can do about it. The matching + // call in resetTty should at least fail then, too, so we do no harm. + if (ioctl(ttyfd, KDGETMODE, oldMode) == 0) { + if (doSwitch && *oldMode != KD_GRAPHICS) + ioctl(ttyfd, KDSETMODE, KD_GRAPHICS); } - - return true; } static void resetTty(int ttyfd, int oldMode) @@ -287,14 +287,16 @@ static void blankScreen(int fd, bool on) } QLinuxFbScreen::QLinuxFbScreen(const QStringList &args) - : mArgs(args), mFbFd(-1), mBlitter(0) + : mArgs(args), mFbFd(-1), mTtyFd(-1), mBlitter(0) { + mMmap.data = 0; } QLinuxFbScreen::~QLinuxFbScreen() { if (mFbFd != -1) { - munmap(mMmap.data - mMmap.offset, mMmap.size); + if (mMmap.data) + munmap(mMmap.data - mMmap.offset, mMmap.size); close(mFbFd); } @@ -394,11 +396,7 @@ bool QLinuxFbScreen::initialize() if (mTtyFd == -1) qErrnoWarning(errno, "Failed to open tty"); - if (doSwitchToGraphicsMode) - switchToGraphicsMode(mTtyFd, &mOldTtyMode); - // Do not warn if the switch fails: the ioctl fails when launching from - // a remote console and there is nothing we can do about it. - + switchToGraphicsMode(mTtyFd, doSwitchToGraphicsMode, &mOldTtyMode); blankScreen(mFbFd, false); return true; diff --git a/src/plugins/platforms/windows/accessible/iaccessible2.cpp b/src/plugins/platforms/windows/accessible/iaccessible2.cpp index f34649e327..5ba49a8a98 100644 --- a/src/plugins/platforms/windows/accessible/iaccessible2.cpp +++ b/src/plugins/platforms/windows/accessible/iaccessible2.cpp @@ -45,6 +45,7 @@ #include <QtGui/qaccessible.h> #include <QtGui/qclipboard.h> #include <QtGui/qguiapplication.h> +#include <QtGui/private/qhighdpiscaling_p.h> #include <QtCore/qdebug.h> #include <algorithm> @@ -607,7 +608,8 @@ HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_keyBinding(long actionIndex // The IDL documents that the client must free with CoTaskMemFree arrayOfBindingsToReturn = coTaskMemAllocArray<BSTR>(numBindings); std::transform(keyBindings.constBegin(), keyBindings.constEnd(), - arrayOfBindingsToReturn, QStringToBSTR); + QT_MAKE_CHECKED_ARRAY_ITERATOR(arrayOfBindingsToReturn, numBindings), + QStringToBSTR); } } *keyBindings = arrayOfBindingsToReturn; @@ -666,9 +668,11 @@ HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_locationInParent(long *x, l QAccessibleInterface *parentIface = accessible->parent(); if (parentIface && parentIface->isValid()) topLeft -= parentIface->rect().topLeft(); + const QPoint nativeTopLeft = QHighDpi::toNativeLocalPosition(topLeft, accessible->window()); - *x = topLeft.x(); - *y = topLeft.y(); + + *x = nativeTopLeft.x(); + *y = nativeTopLeft.y(); return S_OK; } @@ -989,7 +993,8 @@ HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_selectedColumns(long **sele *selectedColumns = Q_NULLPTR; if (count) { *selectedColumns = coTaskMemAllocArray<long>(count); - std::copy(selectedIndices.constBegin(), selectedIndices.constEnd(), *selectedColumns); + std::copy(selectedIndices.constBegin(), selectedIndices.constEnd(), + QT_MAKE_CHECKED_ARRAY_ITERATOR(*selectedColumns, count)); } return count ? S_OK : S_FALSE; } @@ -1011,7 +1016,8 @@ HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::get_selectedRows(long **selecte *selectedRows = Q_NULLPTR; if (count) { *selectedRows = coTaskMemAllocArray<long>(count); - std::copy(selectedIndices.constBegin(), selectedIndices.constEnd(), *selectedRows); + std::copy(selectedIndices.constBegin(), selectedIndices.constEnd(), + QT_MAKE_CHECKED_ARRAY_ITERATOR(*selectedRows, count)); } return count ? S_OK : S_FALSE; } @@ -1680,7 +1686,8 @@ HRESULT QWindowsIA2Accessible::wrapListOfCells(const QList<QAccessibleInterface* if (count) { *outputAccessibles = coTaskMemAllocArray<IUnknown *>(count); std::transform(inputCells.constBegin(), inputCells.constEnd(), - *outputAccessibles, QWindowsAccessibility::wrap); + QT_MAKE_CHECKED_ARRAY_ITERATOR(*outputAccessibles, count), + QWindowsAccessibility::wrap); } return count > 0 ? S_OK : S_FALSE; } diff --git a/src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.cpp b/src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.cpp index 0fff804e29..5fb06a6ed1 100644 --- a/src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.cpp +++ b/src/plugins/platforms/windows/accessible/qwindowsmsaaaccessible.cpp @@ -55,6 +55,7 @@ #include <QtGui/qguiapplication.h> #include <qpa/qplatformnativeinterface.h> #include <QtGui/qwindow.h> +#include <QtGui/private/qhighdpiscaling_p.h> //#include <uiautomationcoreapi.h> #ifndef UiaRootObjectId @@ -503,7 +504,8 @@ HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::accHitTest(long xLeft, long yT if (!accessible) return E_FAIL; - QAccessibleInterface *child = accessible->childAt(xLeft, yTop); + const QPoint pos = QHighDpi::fromNativeLocalPosition(QPoint(xLeft, yTop), accessible->window()); + QAccessibleInterface *child = accessible->childAt(pos.x(), pos.y()); if (child == 0) { // no child found, return this item if it contains the coordinates if (accessible->rect().contains(xLeft, yTop)) { @@ -545,7 +547,7 @@ HRESULT STDMETHODCALLTYPE QWindowsMsaaAccessible::accLocation(long *pxLeft, long QAccessibleInterface *acc = childPointer(accessible, varID); if (!acc || !acc->isValid()) return E_FAIL; - const QRect rect = acc->rect(); + const QRect rect = QHighDpi::toNativePixels(acc->rect(), accessible->window()); *pxLeft = rect.x(); *pyTop = rect.y(); diff --git a/src/plugins/platforms/windows/qwindowseglcontext.cpp b/src/plugins/platforms/windows/qwindowseglcontext.cpp index 96cabb20e4..23a6f35576 100644 --- a/src/plugins/platforms/windows/qwindowseglcontext.cpp +++ b/src/plugins/platforms/windows/qwindowseglcontext.cpp @@ -556,7 +556,7 @@ QFunctionPointer QWindowsEGLContext::getProcAddress(const char *procName) // return a function pointer for standard GLES2 functions too. These are not // guaranteed to be queryable via eglGetProcAddress(). if (!procAddress) { -#if defined(QT_STATIC) +#if defined(QT_STATIC) && !defined(QT_OPENGL_DYNAMIC) static struct StdFunc { const char *name; void *func; diff --git a/src/plugins/platforms/windows/qwindowsinputcontext.cpp b/src/plugins/platforms/windows/qwindowsinputcontext.cpp index ddadbbea5d..8adbd494c4 100644 --- a/src/plugins/platforms/windows/qwindowsinputcontext.cpp +++ b/src/plugins/platforms/windows/qwindowsinputcontext.cpp @@ -650,7 +650,7 @@ int QWindowsInputContext::reconvertString(RECONVERTSTRING *reconv) reconv->dwTargetStrOffset = reconv->dwCompStrOffset; ushort *pastReconv = reinterpret_cast<ushort *>(reconv + 1); std::copy(surroundingText.utf16(), surroundingText.utf16() + surroundingText.size(), - pastReconv); + QT_MAKE_UNCHECKED_ARRAY_ITERATOR(pastReconv)); return memSize; } diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp index b2fde7a0c6..8a229db812 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.cpp +++ b/src/plugins/platforms/windows/qwindowsintegration.cpp @@ -371,13 +371,11 @@ QWindowsWindow *QWindowsIntegration::createPlatformWindowHelper(QWindow *window, QWindowsStaticOpenGLContext *QWindowsStaticOpenGLContext::doCreate() { #if defined(QT_OPENGL_DYNAMIC) - const QWindowsOpenGLTester::Renderers supportedRenderers = QWindowsOpenGLTester::supportedRenderers(); - QWindowsOpenGLTester::Renderer requestedRenderer = QWindowsOpenGLTester::requestedRenderer(); switch (requestedRenderer) { case QWindowsOpenGLTester::DesktopGl: if (QWindowsStaticOpenGLContext *glCtx = QOpenGLStaticContext::create()) { - if ((supportedRenderers & QWindowsOpenGLTester::DisableRotationFlag) + if ((QWindowsOpenGLTester::supportedRenderers() & QWindowsOpenGLTester::DisableRotationFlag) && !QWindowsScreen::setOrientationPreference(Qt::LandscapeOrientation)) { qCWarning(lcQpaGl, "Unable to disable rotation."); } @@ -403,6 +401,7 @@ QWindowsStaticOpenGLContext *QWindowsStaticOpenGLContext::doCreate() break; } + const QWindowsOpenGLTester::Renderers supportedRenderers = QWindowsOpenGLTester::supportedRenderers(); if (supportedRenderers & QWindowsOpenGLTester::DesktopGl) { if (QWindowsStaticOpenGLContext *glCtx = QOpenGLStaticContext::create()) { if ((supportedRenderers & QWindowsOpenGLTester::DisableRotationFlag) diff --git a/src/plugins/platforms/windows/qwindowstheme.cpp b/src/plugins/platforms/windows/qwindowstheme.cpp index 046c32a80a..a9307f79fb 100644 --- a/src/plugins/platforms/windows/qwindowstheme.cpp +++ b/src/plugins/platforms/windows/qwindowstheme.cpp @@ -484,11 +484,8 @@ static QPixmap loadIconFromShell32(int resourceId, QSizeF size) return QPixmap(); } -QPixmap QWindowsTheme::standardPixmap(StandardPixmap sp, const QSizeF &size) const +QPixmap QWindowsTheme::standardPixmap(StandardPixmap sp, const QSizeF &pixmapSize) const { - const QScreen *primaryScreen = QGuiApplication::primaryScreen(); - const int scaleFactor = primaryScreen ? qRound(QHighDpiScaling::factor(primaryScreen)) : 1; - const QSizeF pixmapSize = size * scaleFactor; int resourceId = -1; SHSTOCKICONID stockId = SIID_INVALID; UINT stockFlags = 0; @@ -584,7 +581,6 @@ QPixmap QWindowsTheme::standardPixmap(StandardPixmap sp, const QSizeF &size) con stockFlags |= (pixmapSize.width() > 16 ? SHGFI_LARGEICON : SHGFI_SMALLICON); if (SHGetStockIconInfo(stockId, SHGFI_ICON | stockFlags, &iconInfo) == S_OK) { pixmap = qt_pixmapFromWinHICON(iconInfo.hIcon); - pixmap.setDevicePixelRatio(scaleFactor); DestroyIcon(iconInfo.hIcon); return pixmap; } @@ -598,7 +594,6 @@ QPixmap QWindowsTheme::standardPixmap(StandardPixmap sp, const QSizeF &size) con QPixmap link = loadIconFromShell32(30, pixmapSize); painter.drawPixmap(0, 0, int(pixmapSize.width()), int(pixmapSize.height()), link); } - pixmap.setDevicePixelRatio(scaleFactor); return pixmap; } } @@ -606,13 +601,12 @@ QPixmap QWindowsTheme::standardPixmap(StandardPixmap sp, const QSizeF &size) con if (iconName) { HICON iconHandle = LoadIcon(NULL, iconName); QPixmap pixmap = qt_pixmapFromWinHICON(iconHandle); - pixmap.setDevicePixelRatio(scaleFactor); DestroyIcon(iconHandle); if (!pixmap.isNull()) return pixmap; } - return QPlatformTheme::standardPixmap(sp, size); + return QPlatformTheme::standardPixmap(sp, pixmapSize); } enum { // Shell image list ids diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index c369bafafc..4aa6eaea4a 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -432,6 +432,8 @@ static inline void fixTopLevelWindowFlags(Qt::WindowFlags &flags) default: break; } + if ((flags & Qt::WindowType_Mask) == Qt::SplashScreen) + flags |= Qt::FramelessWindowHint; } void WindowCreationData::fromWindow(const QWindow *w, const Qt::WindowFlags flagsIn, diff --git a/src/plugins/platforms/winrt/qwinrteglcontext.cpp b/src/plugins/platforms/winrt/qwinrteglcontext.cpp index fc5cabc033..5c3ecd8726 100644 --- a/src/plugins/platforms/winrt/qwinrteglcontext.cpp +++ b/src/plugins/platforms/winrt/qwinrteglcontext.cpp @@ -138,6 +138,7 @@ void QWinRTEGLContext::initialize() EGL_CONTEXT_CLIENT_VERSION, d->format.majorVersion(), EGL_CONTEXT_MINOR_VERSION_KHR, d->format.minorVersion(), EGL_CONTEXT_FLAGS_KHR, flags, + EGL_CONTEXT_OPENGL_NO_ERROR_KHR, true, EGL_NONE }; d->eglContext = eglCreateContext(g->eglDisplay, d->eglConfig, d->eglShareContext, attributes); diff --git a/src/plugins/platforms/winrt/qwinrtfileengine.cpp b/src/plugins/platforms/winrt/qwinrtfileengine.cpp index ad81ef4f5f..53e7ebd30d 100644 --- a/src/plugins/platforms/winrt/qwinrtfileengine.cpp +++ b/src/plugins/platforms/winrt/qwinrtfileengine.cpp @@ -466,14 +466,20 @@ qint64 QWinRTFileEngine::read(char *data, qint64 maxlen) hr = stream->ReadAsync(buffer.Get(), length, InputStreamOptions_None, &op); RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::ReadError, -1); - hr = QWinRTFunctions::await(op, buffer.GetAddressOf()); + // Quoting MSDN IInputStream::ReadAsync() documentation: + // "Depending on the implementation, the data that's read might be placed + // into the input buffer, or it might be returned in a different buffer." + // Using GetAddressOf can cause ref counting errors leaking the original + // buffer. + ComPtr<IBuffer> effectiveBuffer; + hr = QWinRTFunctions::await(op, effectiveBuffer.GetAddressOf()); RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::ReadError, -1); - hr = buffer->get_Length(&length); + hr = effectiveBuffer->get_Length(&length); RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::ReadError, -1); ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteArrayAccess; - hr = buffer.As(&byteArrayAccess); + hr = effectiveBuffer.As(&byteArrayAccess); RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::ReadError, -1); byte *bytes; diff --git a/src/plugins/platforms/winrt/qwinrtscreen.cpp b/src/plugins/platforms/winrt/qwinrtscreen.cpp index 562372d0b8..aed33f6b48 100644 --- a/src/plugins/platforms/winrt/qwinrtscreen.cpp +++ b/src/plugins/platforms/winrt/qwinrtscreen.cpp @@ -88,9 +88,9 @@ typedef ITypedEventHandler<CoreWindow*, PointerEventArgs*> PointerHandler; typedef ITypedEventHandler<CoreWindow*, WindowSizeChangedEventArgs*> SizeChangedHandler; typedef ITypedEventHandler<CoreWindow*, VisibilityChangedEventArgs*> VisibilityChangedHandler; typedef ITypedEventHandler<DisplayInformation*, IInspectable*> DisplayInformationHandler; -#ifdef Q_OS_WINPHONE -typedef ITypedEventHandler<StatusBar*, IInspectable*> StatusBarHandler; -#endif +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) +typedef ITypedEventHandler<ApplicationView*, IInspectable*> VisibleBoundsChangedHandler; +#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) QT_BEGIN_NAMESPACE @@ -451,10 +451,10 @@ typedef HRESULT (__stdcall ICoreWindow::*CoreWindowCallbackRemover)(EventRegistr 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 IStatusBar::*StatusBarCallbackRemover)(EventRegistrationToken); -uint qHash(StatusBarCallbackRemover key) { void *ptr = *(void **)(&key); return qHash(ptr); } -#endif +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) +typedef HRESULT (__stdcall IApplicationView2::*ApplicationView2CallbackRemover)(EventRegistrationToken); +uint qHash(ApplicationView2CallbackRemover key) { void *ptr = *(void **)(&key); return qHash(ptr); } +#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) class QWinRTScreenPrivate { @@ -464,13 +464,11 @@ public: ComPtr<Xaml::IDependencyObject> canvas; ComPtr<IApplicationView> view; ComPtr<IDisplayInformation> displayInformation; -#ifdef Q_OS_WINPHONE - ComPtr<IStatusBar> statusBar; -#endif QScopedPointer<QWinRTCursor> cursor; QHash<quint32, QWindowSystemInterface::TouchPoint> touchPoints; - QSizeF logicalSize; + QRectF logicalRect; + QRectF visibleRect; QSurfaceFormat surfaceFormat; qreal logicalDpi; QDpi physicalDpi; @@ -481,9 +479,10 @@ public: QHash<Qt::Key, KeyInfo> activeKeys; QHash<CoreWindowCallbackRemover, EventRegistrationToken> windowTokens; QHash<DisplayCallbackRemover, EventRegistrationToken> displayTokens; -#ifdef Q_OS_WINPHONE - QHash<StatusBarCallbackRemover, EventRegistrationToken> statusBarTokens; -#endif +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) + QHash<ApplicationView2CallbackRemover, EventRegistrationToken> view2Tokens; + ComPtr<IApplicationView2> view2; +#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) }; // To be called from the XAML thread @@ -514,7 +513,8 @@ QWinRTScreen::QWinRTScreen() Rect rect; hr = d->coreWindow->get_Bounds(&rect); Q_ASSERT_SUCCEEDED(hr); - d->logicalSize = QSizeF(rect.Width, rect.Height); + d->logicalRect = QRectF(0.0f, 0.0f, rect.Width, rect.Height); + d->visibleRect = QRectF(0.0f, 0.0f, rect.Width, rect.Height); // Orientation handling ComPtr<IDisplayInformationStatics> displayInformationStatics; @@ -549,9 +549,9 @@ QWinRTScreen::QWinRTScreen() ComPtr<Xaml::IFrameworkElement> frameworkElement; hr = canvas.As(&frameworkElement); Q_ASSERT_SUCCEEDED(hr); - hr = frameworkElement->put_Width(d->logicalSize.width()); + hr = frameworkElement->put_Width(d->logicalRect.width()); Q_ASSERT_SUCCEEDED(hr); - hr = frameworkElement->put_Height(d->logicalSize.height()); + hr = frameworkElement->put_Height(d->logicalRect.height()); Q_ASSERT_SUCCEEDED(hr); ComPtr<Xaml::IUIElement> uiElement; hr = canvas.As(&uiElement); @@ -563,14 +563,10 @@ QWinRTScreen::QWinRTScreen() d->cursor.reset(new QWinRTCursor); -#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); +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) + hr = d->view.As(&d->view2); Q_ASSERT_SUCCEEDED(hr); -#endif // Q_OS_WINPHONE +#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) } QWinRTScreen::~QWinRTScreen() @@ -590,12 +586,12 @@ QWinRTScreen::~QWinRTScreen() hr = (d->displayInformation.Get()->*i.key())(i.value()); Q_ASSERT_SUCCEEDED(hr); } -#ifdef Q_OS_WINPHONE - for (QHash<StatusBarCallbackRemover, EventRegistrationToken>::const_iterator i = d->statusBarTokens.begin(); i != d->statusBarTokens.end(); ++i) { - hr = (d->statusBar.Get()->*i.key())(i.value()); +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) + for (QHash<ApplicationView2CallbackRemover, EventRegistrationToken>::const_iterator i = d->view2Tokens.begin(); i != d->view2Tokens.end(); ++i) { + hr = (d->view2.Get()->*i.key())(i.value()); Q_ASSERT_SUCCEEDED(hr); } -#endif //Q_OS_WINPHONE +#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) return hr; }); RETURN_VOID_IF_FAILED("Failed to unregister screen event callbacks"); @@ -604,33 +600,17 @@ QWinRTScreen::~QWinRTScreen() QRect QWinRTScreen::geometry() const { Q_D(const QWinRTScreen); - return QRect(QPoint(), (d->logicalSize * d->scaleFactor).toSize()); + return QRect(QPoint(), QSizeF(d->logicalRect.size() * 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); + return QRectF((d->visibleRect.x() - d->logicalRect.x())* d->scaleFactor, + (d->visibleRect.y() - d->logicalRect.y()) * d->scaleFactor, + d->visibleRect.width() * d->scaleFactor, + d->visibleRect.height() * d->scaleFactor).toRect(); } -#endif //Q_OS_WINPHONE int QWinRTScreen::depth() const { @@ -645,8 +625,8 @@ QImage::Format QWinRTScreen::format() const QSizeF QWinRTScreen::physicalSize() const { Q_D(const QWinRTScreen); - return QSizeF(d->logicalSize.width() * d->scaleFactor / d->physicalDpi.first * qreal(25.4), - d->logicalSize.height() * d->scaleFactor / d->physicalDpi.second * qreal(25.4)); + return QSizeF(d->logicalRect.width() * d->scaleFactor / d->physicalDpi.first * qreal(25.4), + d->logicalRect.height() * d->scaleFactor / d->physicalDpi.second * qreal(25.4)); } QDpi QWinRTScreen::logicalDpi() const @@ -723,29 +703,6 @@ Xaml::IDependencyObject *QWinRTScreen::canvas() const return d->canvas.Get(); } -#ifdef Q_OS_WINPHONE -void QWinRTScreen::setStatusBarVisibility(bool visible, QWindow *window) -{ - Q_D(QWinRTScreen); - qCDebug(lcQpaWindows) << __FUNCTION__ << window << visible; - - 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 - void QWinRTScreen::initialize() { Q_D(QWinRTScreen); @@ -768,15 +725,14 @@ void QWinRTScreen::initialize() 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]); +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) + hr = d->view2->add_VisibleBoundsChanged(Callback<VisibleBoundsChangedHandler>(this, &QWinRTScreen::onWindowSizeChanged).Get(), &d->view2Tokens[&IApplicationView2::remove_VisibleBoundsChanged]); 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_SizeChanged(Callback<SizeChangedHandler>(this, &QWinRTScreen::onWindowSizeChanged).Get(), &d->windowTokens[&ICoreWindow::remove_SizeChanged]); + Q_ASSERT_SUCCEEDED(hr) +#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) + 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]); @@ -804,11 +760,6 @@ void QWinRTScreen::addWindow(QWindow *window) 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(); @@ -820,11 +771,6 @@ void QWinRTScreen::removeWindow(QWindow *window) Q_D(QWinRTScreen); qCDebug(lcQpaWindows) << __FUNCTION__ << window; -#ifdef Q_OS_WINPHONE - if (window->visibility() == QWindow::Minimized) - setStatusBarVisibility(false, window); -#endif - const bool wasTopWindow = window == topWindow(); if (!d->visibleWindows.removeAll(window)) return; @@ -1009,8 +955,20 @@ HRESULT QWinRTScreen::onPointerEntered(ICoreWindow *, IPointerEventArgs *args) return S_OK; } -HRESULT QWinRTScreen::onPointerExited(ICoreWindow *, IPointerEventArgs *) +HRESULT QWinRTScreen::onPointerExited(ICoreWindow *, IPointerEventArgs *args) { + Q_D(QWinRTScreen); + + ComPtr<IPointerPoint> pointerPoint; + if (FAILED(args->get_CurrentPoint(&pointerPoint))) + return E_INVALIDARG; + + quint32 id; + if (FAILED(pointerPoint->get_PointerId(&id))) + return E_INVALIDARG; + + d->touchPoints.remove(id); + QWindowSystemInterface::handleLeaveEvent(0); return S_OK; } @@ -1094,6 +1052,7 @@ HRESULT QWinRTScreen::onPointerUpdated(ICoreWindow *, IPointerEventArgs *args) break; } + case PointerDeviceType_Pen: case PointerDeviceType_Touch: { if (!d->touchDevice) { d->touchDevice = new QTouchDevice; @@ -1112,51 +1071,45 @@ HRESULT QWinRTScreen::onPointerUpdated(ICoreWindow *, IPointerEventArgs *args) float pressure; properties->get_Pressure(&pressure); - QHash<quint32, QWindowSystemInterface::TouchPoint>::iterator it = d->touchPoints.find(id); - if (it != d->touchPoints.end()) { - boolean isPressed; + boolean isPressed; #ifndef Q_OS_WINPHONE - pointerPoint->get_IsInContact(&isPressed); + pointerPoint->get_IsInContact(&isPressed); #else - properties->get_IsLeftButtonPressed(&isPressed); // IsInContact not reliable on phone + properties->get_IsLeftButtonPressed(&isPressed); // IsInContact not reliable on phone #endif - it.value().state = isPressed ? Qt::TouchPointMoved : Qt::TouchPointReleased; - } else { + + const QRectF areaRect(area.X * d->scaleFactor, area.Y * d->scaleFactor, + area.Width * d->scaleFactor, area.Height * d->scaleFactor); + + QHash<quint32, QWindowSystemInterface::TouchPoint>::iterator it = d->touchPoints.find(id); + if (it == d->touchPoints.end()) { it = d->touchPoints.insert(id, QWindowSystemInterface::TouchPoint()); - it.value().state = Qt::TouchPointPressed; it.value().id = id; } - it.value().area = QRectF(area.X * d->scaleFactor, area.Y * d->scaleFactor, - area.Width * d->scaleFactor, area.Height * d->scaleFactor); - it.value().normalPosition = QPointF(point.X/d->logicalSize.width(), point.Y/d->logicalSize.height()); - it.value().pressure = pressure; - QWindowSystemInterface::handleTouchEvent(topWindow(), d->touchDevice, d->touchPoints.values(), mods); + if (isPressed && it.value().pressure == 0.) + it.value().state = Qt::TouchPointPressed; + else if (!isPressed && it.value().pressure > 0.) + it.value().state = Qt::TouchPointReleased; + else if (it.value().area == areaRect) + it.value().state = Qt::TouchPointStationary; + else + it.value().state = Qt::TouchPointMoved; - // Remove released points, station others - for (QHash<quint32, QWindowSystemInterface::TouchPoint>::iterator i = d->touchPoints.begin(); i != d->touchPoints.end();) { - if (i.value().state == Qt::TouchPointReleased) - i = d->touchPoints.erase(i); - else - (i++).value().state = Qt::TouchPointStationary; - } + it.value().area = areaRect; + it.value().normalPosition = QPointF(point.X/d->logicalRect.width(), point.Y/d->logicalRect.height()); + it.value().pressure = pressure; - break; - } - case PointerDeviceType_Pen: { - quint32 id; - pointerPoint->get_PointerId(&id); + QWindowSystemInterface::handleTouchEvent(topWindow(), d->touchDevice, d->touchPoints.values(), mods); - boolean isPressed; - pointerPoint->get_IsInContact(&isPressed); + // Fall-through for pen to generate tablet event + if (pointerDeviceType != PointerDeviceType_Pen) + break; boolean isEraser; properties->get_IsEraser(&isEraser); int pointerType = isEraser ? 3 : 1; - float pressure; - properties->get_Pressure(&pressure); - float xTilt; properties->get_XTilt(&xTilt); @@ -1177,22 +1130,6 @@ HRESULT QWinRTScreen::onPointerUpdated(ICoreWindow *, IPointerEventArgs *args) return S_OK; } -HRESULT QWinRTScreen::onSizeChanged(ICoreWindow *, IWindowSizeChangedEventArgs *) -{ - Q_D(QWinRTScreen); - - Rect size; - HRESULT hr; - hr = d->coreWindow->get_Bounds(&size); - RETURN_OK_IF_FAILED("Failed to get window bounds"); - d->logicalSize = QSizeF(size.Width, size.Height); - qCDebug(lcQpaWindows) << __FUNCTION__ << d->logicalSize; - QWindowSystemInterface::handleScreenGeometryChange(screen(), geometry(), availableGeometry()); - QPlatformScreen::resizeMaximizedWindows(); - handleExpose(); - return S_OK; -} - HRESULT QWinRTScreen::onActivated(ICoreWindow *, IWindowActivatedEventArgs *args) { Q_D(QWinRTScreen); @@ -1205,6 +1142,8 @@ HRESULT QWinRTScreen::onActivated(ICoreWindow *, IWindowActivatedEventArgs *args return S_OK; } + QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationActive); + // Activate topWindow if (!d->visibleWindows.isEmpty()) { Qt::FocusReason focusReason = activationState == CoreWindowActivationState_PointerActivated @@ -1231,8 +1170,10 @@ HRESULT QWinRTScreen::onVisibilityChanged(ICoreWindow *, IVisibilityChangedEvent RETURN_OK_IF_FAILED("Failed to get visibility."); qCDebug(lcQpaWindows) << __FUNCTION__ << visible; QWindowSystemInterface::handleApplicationStateChanged(visible ? Qt::ApplicationActive : Qt::ApplicationHidden); - if (visible) + if (visible) { handleExpose(); + onWindowSizeChanged(nullptr, nullptr); + } return S_OK; } @@ -1248,9 +1189,7 @@ HRESULT QWinRTScreen::onOrientationChanged(IDisplayInformation *, IInspectable * if (d->orientation != newOrientation) { d->orientation = newOrientation; qCDebug(lcQpaWindows) << " New orientation:" << newOrientation; -#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) - onSizeChanged(nullptr, nullptr); -#endif + onWindowSizeChanged(nullptr, nullptr); QWindowSystemInterface::handleScreenOrientationChange(screen(), d->orientation); handleExpose(); // Clean broken frames caused by race between Qt and ANGLE } @@ -1294,20 +1233,35 @@ HRESULT QWinRTScreen::onDpiChanged(IDisplayInformation *, IInspectable *) return S_OK; } -#ifdef Q_OS_WINPHONE -HRESULT QWinRTScreen::onStatusBarShowing(IStatusBar *, IInspectable *) +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) +HRESULT QWinRTScreen::onWindowSizeChanged(IApplicationView *, IInspectable *) +#else +HRESULT QWinRTScreen::onWindowSizeChanged(ICoreWindow *, IWindowSizeChangedEventArgs *) +#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) { - qCDebug(lcQpaWindows) << __FUNCTION__; - onSizeChanged(nullptr, nullptr); - return S_OK; -} + Q_D(QWinRTScreen); -HRESULT QWinRTScreen::onStatusBarHiding(IStatusBar *, IInspectable *) -{ - qCDebug(lcQpaWindows) << __FUNCTION__; - onSizeChanged(nullptr, nullptr); + HRESULT hr; + Rect windowSize; + + hr = d->coreWindow->get_Bounds(&windowSize); + RETURN_OK_IF_FAILED("Failed to get window bounds"); + d->logicalRect = QRectF(windowSize.X, windowSize.Y, windowSize.Width, windowSize.Height); + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) + Rect visibleRect; + hr = d->view2->get_VisibleBounds(&visibleRect); + RETURN_OK_IF_FAILED("Failed to get window visible bounds"); + d->visibleRect = QRectF(visibleRect.X, visibleRect.Y, visibleRect.Width, visibleRect.Height); +#else + d->visibleRect = QRectF(windowSize.X, windowSize.Y, windowSize.Width, windowSize.Height); +#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) + + qCDebug(lcQpaWindows) << __FUNCTION__ << d->logicalRect; + QWindowSystemInterface::handleScreenGeometryChange(screen(), geometry(), availableGeometry()); + QPlatformScreen::resizeMaximizedWindows(); + handleExpose(); return S_OK; } -#endif //Q_OS_WINPHONE QT_END_NAMESPACE diff --git a/src/plugins/platforms/winrt/qwinrtscreen.h b/src/plugins/platforms/winrt/qwinrtscreen.h index bfab1e8385..5cada9726d 100644 --- a/src/plugins/platforms/winrt/qwinrtscreen.h +++ b/src/plugins/platforms/winrt/qwinrtscreen.h @@ -65,7 +65,7 @@ namespace ABI { struct IWindow; } namespace ViewManagement { - struct IStatusBar; + struct IApplicationView; } } namespace Graphics { @@ -88,10 +88,9 @@ class QWinRTScreen : public QPlatformScreen public: explicit QWinRTScreen(); ~QWinRTScreen(); + 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; @@ -115,10 +114,6 @@ public: ABI::Windows::UI::Core::ICoreWindow *coreWindow() const; ABI::Windows::UI::Xaml::IDependencyObject *canvas() const; -#ifdef Q_OS_WINPHONE - void setStatusBarVisibility(bool visible, QWindow *window); -#endif - void initialize(); private: @@ -130,7 +125,6 @@ private: HRESULT onPointerEntered(ABI::Windows::UI::Core::ICoreWindow *, ABI::Windows::UI::Core::IPointerEventArgs *); HRESULT onPointerExited(ABI::Windows::UI::Core::ICoreWindow *, ABI::Windows::UI::Core::IPointerEventArgs *); HRESULT onPointerUpdated(ABI::Windows::UI::Core::ICoreWindow *, ABI::Windows::UI::Core::IPointerEventArgs *); - HRESULT onSizeChanged(ABI::Windows::UI::Core::ICoreWindow *, ABI::Windows::UI::Core::IWindowSizeChangedEventArgs *); HRESULT onActivated(ABI::Windows::UI::Core::ICoreWindow *, ABI::Windows::UI::Core::IWindowActivatedEventArgs *); @@ -139,10 +133,10 @@ private: HRESULT onOrientationChanged(ABI::Windows::Graphics::Display::IDisplayInformation *, IInspectable *); HRESULT onDpiChanged(ABI::Windows::Graphics::Display::IDisplayInformation *, IInspectable *); - -#ifdef Q_OS_WINPHONE - HRESULT onStatusBarShowing(ABI::Windows::UI::ViewManagement::IStatusBar *, IInspectable *); - HRESULT onStatusBarHiding(ABI::Windows::UI::ViewManagement::IStatusBar *, IInspectable *); +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) + HRESULT onWindowSizeChanged(ABI::Windows::UI::ViewManagement::IApplicationView *, IInspectable *); +#else + HRESULT onWindowSizeChanged(ABI::Windows::UI::Core::ICoreWindow *, ABI::Windows::UI::Core::IWindowSizeChangedEventArgs *); #endif QScopedPointer<QWinRTScreenPrivate> d_ptr; diff --git a/src/plugins/platforms/winrt/qwinrtwindow.cpp b/src/plugins/platforms/winrt/qwinrtwindow.cpp index 9df087c055..3cae53d0a0 100644 --- a/src/plugins/platforms/winrt/qwinrtwindow.cpp +++ b/src/plugins/platforms/winrt/qwinrtwindow.cpp @@ -42,7 +42,6 @@ #include <private/qeventdispatcher_winrt_p.h> #include <EGL/egl.h> -#define EGL_EGLEXT_PROTOTYPES #include <EGL/eglext.h> #include <qfunctions_winrt.h> @@ -322,10 +321,6 @@ void QWinRTWindow::setWindowState(Qt::WindowState state) 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); diff --git a/src/plugins/platforms/winrt/winrt.pro b/src/plugins/platforms/winrt/winrt.pro index 261295ef0b..144c581015 100644 --- a/src/plugins/platforms/winrt/winrt.pro +++ b/src/plugins/platforms/winrt/winrt.pro @@ -4,7 +4,7 @@ CONFIG -= precompile_header QT += core-private gui-private platformsupport-private -DEFINES *= QT_NO_CAST_FROM_ASCII __WRL_NO_DEFAULT_LIB__ GL_GLEXT_PROTOTYPES +DEFINES *= QT_NO_CAST_FROM_ASCII __WRL_NO_DEFAULT_LIB__ LIBS += $$QMAKE_LIBS_CORE -ldwrite -ld3d11 INCLUDEPATH += $$QT_SOURCE_TREE/src/3rdparty/freetype/include diff --git a/src/plugins/platforms/xcb/qxcbbackingstore.cpp b/src/plugins/platforms/xcb/qxcbbackingstore.cpp index 2925917562..130ae9be0c 100644 --- a/src/plugins/platforms/xcb/qxcbbackingstore.cpp +++ b/src/plugins/platforms/xcb/qxcbbackingstore.cpp @@ -525,28 +525,36 @@ void QXcbBackingStore::beginPaint(const QRegion ®ion) if (!m_image) return; - m_paintRegion = region; - m_image->preparePaint(m_paintRegion); + m_paintRegions.push(region); + m_image->preparePaint(region); if (m_image->hasAlpha()) { QPainter p(paintDevice()); p.setCompositionMode(QPainter::CompositionMode_Source); const QColor blank = Qt::transparent; - for (const QRect &rect : m_paintRegion) + for (const QRect &rect : region) p.fillRect(rect, blank); } } void QXcbBackingStore::endPaint() { + if (Q_UNLIKELY(m_paintRegions.isEmpty())) { + qWarning("%s: paint regions empty!", Q_FUNC_INFO); + return; + } + + const QRegion region = m_paintRegions.pop(); + m_image->preparePaint(region); + QXcbWindow *platformWindow = static_cast<QXcbWindow *>(window()->handle()); if (!platformWindow || !platformWindow->imageNeedsRgbSwap()) return; // Slow path: the paint device was m_rgbImage. Now copy with swapping red // and blue into m_image. - auto it = m_paintRegion.begin(); - const auto end = m_paintRegion.end(); + auto it = region.begin(); + const auto end = region.end(); if (it == end) return; QPainter p(m_image->image()); diff --git a/src/plugins/platforms/xcb/qxcbbackingstore.h b/src/plugins/platforms/xcb/qxcbbackingstore.h index 4b8c040729..6af679d28a 100644 --- a/src/plugins/platforms/xcb/qxcbbackingstore.h +++ b/src/plugins/platforms/xcb/qxcbbackingstore.h @@ -41,6 +41,7 @@ #define QXCBBACKINGSTORE_H #include <qpa/qplatformbackingstore.h> +#include <QtCore/QStack> #include <xcb/xcb.h> @@ -75,7 +76,7 @@ public: private: QXcbShmImage *m_image; - QRegion m_paintRegion; + QStack<QRegion> m_paintRegions; QImage m_rgbImage; }; diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index cb222842db..87b4c7b91f 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -166,9 +166,9 @@ static int ioErrorHandler(Display *dpy) } #endif -QXcbScreen* QXcbConnection::findScreenForCrtc(xcb_window_t rootWindow, xcb_randr_crtc_t crtc) +QXcbScreen* QXcbConnection::findScreenForCrtc(xcb_window_t rootWindow, xcb_randr_crtc_t crtc) const { - foreach (QXcbScreen *screen, m_screens) { + for (QXcbScreen *screen : m_screens) { if (screen->root() == rootWindow && screen->crtc() == crtc) return screen; } @@ -176,9 +176,9 @@ QXcbScreen* QXcbConnection::findScreenForCrtc(xcb_window_t rootWindow, xcb_randr return 0; } -QXcbScreen* QXcbConnection::findScreenForOutput(xcb_window_t rootWindow, xcb_randr_output_t output) +QXcbScreen* QXcbConnection::findScreenForOutput(xcb_window_t rootWindow, xcb_randr_output_t output) const { - foreach (QXcbScreen *screen, m_screens) { + for (QXcbScreen *screen : m_screens) { if (screen->root() == rootWindow && screen->output() == output) return screen; } @@ -186,9 +186,9 @@ QXcbScreen* QXcbConnection::findScreenForOutput(xcb_window_t rootWindow, xcb_ran return 0; } -QXcbVirtualDesktop* QXcbConnection::virtualDesktopForRootWindow(xcb_window_t rootWindow) +QXcbVirtualDesktop* QXcbConnection::virtualDesktopForRootWindow(xcb_window_t rootWindow) const { - foreach (QXcbVirtualDesktop *virtualDesktop, m_virtualDesktops) { + for (QXcbVirtualDesktop *virtualDesktop : m_virtualDesktops) { if (virtualDesktop->screen()->root == rootWindow) return virtualDesktop; } @@ -215,6 +215,9 @@ void QXcbConnection::updateScreens(const xcb_randr_notify_event_t *event) // CRTC with node mode could mean that output has been disabled, and we'll // get RRNotifyOutputChange notification for that. if (screen && crtc.mode) { + if (crtc.rotation == XCB_RANDR_ROTATION_ROTATE_90 || + crtc.rotation == XCB_RANDR_ROTATION_ROTATE_270) + std::swap(crtc.width, crtc.height); screen->updateGeometry(QRect(crtc.x, crtc.y, crtc.width, crtc.height), crtc.rotation); if (screen->mode() != crtc.mode) screen->updateRefreshRate(crtc.mode); @@ -242,7 +245,8 @@ void QXcbConnection::updateScreens(const xcb_randr_notify_event_t *event) xcb_randr_get_output_info_reply(xcb_connection(), outputInfoCookie, NULL)); // Find a fake screen - foreach (QPlatformScreen *scr, virtualDesktop->screens()) { + const auto scrs = virtualDesktop->screens(); + for (QPlatformScreen *scr : scrs) { QXcbScreen *xcbScreen = (QXcbScreen *)scr; if (xcbScreen->output() == XCB_NONE) { screen = xcbScreen; @@ -284,7 +288,7 @@ void QXcbConnection::updateScreens(const xcb_randr_notify_event_t *event) } } - qCDebug(lcQpaScreen) << "primary output is" << m_screens.first()->name(); + qCDebug(lcQpaScreen) << "primary output is" << qAsConst(m_screens).first()->name(); } } @@ -319,7 +323,7 @@ void QXcbConnection::updateScreen(QXcbScreen *screen, const xcb_randr_output_cha // If the screen became primary, reshuffle the order in QGuiApplicationPrivate const int idx = m_screens.indexOf(screen); if (idx > 0) { - m_screens.first()->setPrimary(false); + qAsConst(m_screens).first()->setPrimary(false); m_screens.swap(0, idx); } screen->virtualDesktop()->setPrimaryScreen(screen); @@ -339,7 +343,7 @@ QXcbScreen *QXcbConnection::createScreen(QXcbVirtualDesktop *virtualDesktop, if (screen->isPrimary()) { if (!m_screens.isEmpty()) - m_screens.first()->setPrimary(false); + qAsConst(m_screens).first()->setPrimary(false); m_screens.prepend(screen); } else { @@ -518,7 +522,7 @@ void QXcbConnection::initializeScreens() ++xcbScreenNumber; } // for each xcb screen - foreach (QXcbVirtualDesktop *virtualDesktop, m_virtualDesktops) + for (QXcbVirtualDesktop *virtualDesktop : qAsConst(m_virtualDesktops)) virtualDesktop->subscribeToXFixesSelectionNotify(); if (m_virtualDesktops.isEmpty()) { @@ -526,19 +530,19 @@ void QXcbConnection::initializeScreens() } else { // Ensure the primary screen is first on the list if (primaryScreen) { - if (m_screens.first() != primaryScreen) { + if (qAsConst(m_screens).first() != primaryScreen) { m_screens.removeOne(primaryScreen); m_screens.prepend(primaryScreen); } } // Push the screens to QGuiApplication - foreach (QXcbScreen *screen, m_screens) { + for (QXcbScreen *screen : qAsConst(m_screens)) { qCDebug(lcQpaScreen) << "adding" << screen << "(Primary:" << screen->isPrimary() << ")"; QXcbIntegration::instance()->screenAdded(screen, screen->isPrimary()); } - qCDebug(lcQpaScreen) << "primary output is" << m_screens.first()->name(); + qCDebug(lcQpaScreen) << "primary output is" << qAsConst(m_screens).first()->name(); } } @@ -563,6 +567,7 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra , m_buttons(0) , m_focusWindow(0) , m_mouseGrabber(0) + , m_mousePressWindow(0) , m_clientLeader(0) , m_systemTrayTracker(0) , m_glIntegration(Q_NULLPTR) @@ -1010,8 +1015,8 @@ void QXcbConnection::handleXcbError(xcb_generic_error_t *error) } } if (i == m_callLog.size() && !m_callLog.isEmpty()) - qDebug("Caused some time after: %s:%d", m_callLog.first().file.constData(), - m_callLog.first().line); + qDebug("Caused some time after: %s:%d", qAsConst(m_callLog).first().file.constData(), + qAsConst(m_callLog).first().line); #endif } @@ -1231,7 +1236,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) #ifndef QT_NO_CLIPBOARD m_clipboard->handleXFixesSelectionRequest(notify_event); #endif - foreach (QXcbVirtualDesktop *virtualDesktop, m_virtualDesktops) + for (QXcbVirtualDesktop *virtualDesktop : qAsConst(m_virtualDesktops)) virtualDesktop->handleXFixesSelectionNotify(notify_event); handled = true; @@ -1240,7 +1245,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) handled = true; } else if (has_randr_extension && response_type == xrandr_first_event + XCB_RANDR_SCREEN_CHANGE_NOTIFY) { xcb_randr_screen_change_notify_event_t *change_event = (xcb_randr_screen_change_notify_event_t *)event; - foreach (QXcbScreen *s, m_screens) { + for (QXcbScreen *s : qAsConst(m_screens)) { if (s->root() == change_event->root ) s->handleScreenChange(change_event); } @@ -1375,6 +1380,11 @@ void QXcbConnection::setFocusWindow(QXcbWindow *w) void QXcbConnection::setMouseGrabber(QXcbWindow *w) { m_mouseGrabber = w; + m_mousePressWindow = Q_NULLPTR; +} +void QXcbConnection::setMousePressWindow(QXcbWindow *w) +{ + m_mousePressWindow = w; } void QXcbConnection::grabServer() @@ -1628,8 +1638,13 @@ bool QXcbConnection::compressEvent(xcb_generic_event_t *event, int currentIndex, if (!m_xi2Enabled) return false; - // compress XI_Motion + // compress XI_Motion, but not from tablet devices if (isXIType(event, m_xiOpCode, XI_Motion)) { +#ifndef QT_NO_TABLETEVENT + xXIDeviceEvent *xdev = reinterpret_cast<xXIDeviceEvent *>(event); + if (const_cast<QXcbConnection *>(this)->tabletDataForDevice(xdev->sourceid)) + return false; +#endif // QT_NO_TABLETEVENT for (int j = nextIndex; j < eventqueue->size(); ++j) { xcb_generic_event_t *next = eventqueue->at(j); if (!isValid(next)) @@ -1726,7 +1741,7 @@ void QXcbConnection::processXcbEvents() // Indicate with a null event that the event the callbacks are waiting for // is not in the queue currently. - Q_FOREACH (PeekFunc f, m_peekFuncs) + for (PeekFunc f : qAsConst(m_peekFuncs)) f(this, 0); m_peekFuncs.clear(); @@ -2248,7 +2263,7 @@ bool QXcbConnection::xi2MouseEvents() const #endif #if defined(XCB_USE_XINPUT2) -static int xi2ValuatorOffset(unsigned char *maskPtr, int maskLen, int number) +static int xi2ValuatorOffset(const unsigned char *maskPtr, int maskLen, int number) { int offset = 0; for (int i = 0; i < maskLen; i++) { @@ -2267,11 +2282,11 @@ static int xi2ValuatorOffset(unsigned char *maskPtr, int maskLen, int number) return -1; } -bool QXcbConnection::xi2GetValuatorValueIfSet(void *event, int valuatorNum, double *value) +bool QXcbConnection::xi2GetValuatorValueIfSet(const void *event, int valuatorNum, double *value) { - xXIDeviceEvent *xideviceevent = static_cast<xXIDeviceEvent *>(event); - unsigned char *buttonsMaskAddr = (unsigned char*)&xideviceevent[1]; - unsigned char *valuatorsMaskAddr = buttonsMaskAddr + xideviceevent->buttons_len * 4; + const xXIDeviceEvent *xideviceevent = static_cast<const xXIDeviceEvent *>(event); + const unsigned char *buttonsMaskAddr = (const unsigned char*)&xideviceevent[1]; + const unsigned char *valuatorsMaskAddr = buttonsMaskAddr + xideviceevent->buttons_len * 4; FP3232 *valuatorsValuesAddr = (FP3232*)(valuatorsMaskAddr + xideviceevent->valuators_len * 4); int valuatorOffset = xi2ValuatorOffset(valuatorsMaskAddr, xideviceevent->valuators_len, valuatorNum); diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h index 9fed7b52f1..891f0fbcb5 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -481,6 +481,8 @@ public: void setFocusWindow(QXcbWindow *); QXcbWindow *mouseGrabber() const { return m_mouseGrabber; } void setMouseGrabber(QXcbWindow *); + QXcbWindow *mousePressWindow() const { return m_mousePressWindow; } + void setMousePressWindow(QXcbWindow *); QByteArray startupId() const { return m_startupId; } void setStartupId(const QByteArray &nextId) { m_startupId = nextId; } @@ -533,9 +535,9 @@ private: void initializeXShape(); void initializeXKB(); void handleClientMessageEvent(const xcb_client_message_event_t *event); - QXcbScreen* findScreenForCrtc(xcb_window_t rootWindow, xcb_randr_crtc_t crtc); - QXcbScreen* findScreenForOutput(xcb_window_t rootWindow, xcb_randr_output_t output); - QXcbVirtualDesktop* virtualDesktopForRootWindow(xcb_window_t rootWindow); + QXcbScreen* findScreenForCrtc(xcb_window_t rootWindow, xcb_randr_crtc_t crtc) const; + QXcbScreen* findScreenForOutput(xcb_window_t rootWindow, xcb_randr_output_t output) const; + QXcbVirtualDesktop* virtualDesktopForRootWindow(xcb_window_t rootWindow) const; void updateScreens(const xcb_randr_notify_event_t *event); bool checkOutputIsPrimary(xcb_window_t rootWindow, xcb_randr_output_t output); void updateScreen(QXcbScreen *screen, const xcb_randr_output_change_t &outputChange); @@ -579,9 +581,10 @@ private: }; QHash<int, ValuatorClassInfo> valuatorInfo; }; - bool xi2HandleTabletEvent(void *event, TabletData *tabletData, QXcbWindowEventListener *eventListener); - void xi2ReportTabletEvent(TabletData &tabletData, void *event); + bool xi2HandleTabletEvent(const void *event, TabletData *tabletData); + void xi2ReportTabletEvent(const void *event, TabletData *tabletData); QVector<TabletData> m_tabletData; + TabletData *tabletDataForDevice(int id); #endif // !QT_NO_TABLETEVENT struct ScrollingDevice { ScrollingDevice() : deviceId(0), verticalIndex(0), horizontalIndex(0), orientations(0), legacyOrientations(0) { } @@ -596,7 +599,7 @@ private: void xi2HandleScrollEvent(void *event, ScrollingDevice &scrollingDevice); QHash<int, ScrollingDevice> m_scrollingDevices; - static bool xi2GetValuatorValueIfSet(void *event, int valuatorNum, double *value); + static bool xi2GetValuatorValueIfSet(const void *event, int valuatorNum, double *value); static void xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *event); #endif @@ -668,6 +671,7 @@ private: QXcbWindow *m_focusWindow; QXcbWindow *m_mouseGrabber; + QXcbWindow *m_mousePressWindow; xcb_window_t m_clientLeader; QByteArray m_startupId; diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp index 63a650a514..5b7f45fb6c 100644 --- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp @@ -42,7 +42,7 @@ #include "qxcbscreen.h" #include "qxcbwindow.h" #include "qtouchdevice.h" -#include <qpa/qwindowsysteminterface.h> +#include <qpa/qwindowsysteminterface_p.h> #include <QDebug> #include <cmath> @@ -119,7 +119,7 @@ void QXcbConnection::xi2SetupDevices() // Only non-master pointing devices are relevant here. if (devices[i].use != XISlavePointer) continue; - qCDebug(lcQpaXInputDevices) << "input device "<< devices[i].name; + qCDebug(lcQpaXInputDevices) << "input device " << devices[i].name << "ID" << devices[i].deviceid; #ifndef QT_NO_TABLETEVENT TabletData tabletData; #endif @@ -274,7 +274,7 @@ void QXcbConnection::xi2SetupDevices() void QXcbConnection::finalizeXInput2() { - foreach (XInput2TouchDeviceData *dev, m_touchDevices) { + for (XInput2TouchDeviceData *dev : qAsConst(m_touchDevices)) { if (dev->xiDeviceInfo) XIFreeDeviceInfo(dev->xiDeviceInfo); delete dev; @@ -313,12 +313,13 @@ void QXcbConnection::xi2Select(xcb_window_t window) mask.mask_len = sizeof(bitMask); mask.mask = xiBitMask; // When xi2MouseEvents() is true (the default), pointer emulation for touch and tablet - // events will get disabled. This is preferable for touch, as Qt Quick handles touch events - // directly while for others QtGui synthesizes mouse events, not so much for tablets. For - // the latter we will synthesize the events ourselves. + // events will get disabled. This is preferable, as Qt Quick handles touch events + // directly, while for other applications QtGui synthesizes mouse events. mask.deviceid = XIAllMasterDevices; Status result = XISelectEvents(xDisplay, window, &mask, 1); - if (result != Success) + if (result == Success) + QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false); + else qCDebug(lcQpaXInput, "XInput 2.2: failed to select pointer/touch events, window %x, result %d", window, result); } @@ -358,7 +359,7 @@ void QXcbConnection::xi2Select(xcb_window_t window) scrollBitMask = XI_MotionMask; scrollBitMask |= XI_ButtonReleaseMask; int i=0; - Q_FOREACH (const ScrollingDevice& scrollingDevice, m_scrollingDevices) { + for (const ScrollingDevice& scrollingDevice : qAsConst(m_scrollingDevices)) { if (tabletDevices.contains(scrollingDevice.deviceId)) continue; // All necessary events are already captured. xiEventMask[i].deviceid = scrollingDevice.deviceId; @@ -536,12 +537,9 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event) #ifndef QT_NO_TABLETEVENT if (!xiEnterEvent) { - for (int i = 0; i < m_tabletData.count(); ++i) { - if (m_tabletData.at(i).deviceId == sourceDeviceId) { - if (xi2HandleTabletEvent(xiEvent, &m_tabletData[i], eventListener)) - return; - } - } + QXcbConnection::TabletData *tablet = tabletDataForDevice(sourceDeviceId); + if (tablet && xi2HandleTabletEvent(xiEvent, tablet)) + return; } #endif // QT_NO_TABLETEVENT @@ -832,9 +830,8 @@ void QXcbConnection::xi2HandleHierachyEvent(void *event) return; xi2SetupDevices(); // Reselect events for all event-listening windows. - Q_FOREACH (xcb_window_t window, m_mapper.keys()) { - xi2Select(window); - } + for (auto it = m_mapper.cbegin(), end = m_mapper.cend(); it != end; ++it) + xi2Select(it.key()); } void QXcbConnection::xi2HandleDeviceChangedEvent(void *event) @@ -1042,36 +1039,36 @@ static QTabletEvent::TabletDevice toolIdToTabletDevice(quint32 toolId) { } #ifndef QT_NO_TABLETEVENT -bool QXcbConnection::xi2HandleTabletEvent(void *event, TabletData *tabletData, QXcbWindowEventListener *eventListener) +bool QXcbConnection::xi2HandleTabletEvent(const void *event, TabletData *tabletData) { bool handled = true; Display *xDisplay = static_cast<Display *>(m_xlib_display); - xXIGenericDeviceEvent *xiEvent = static_cast<xXIGenericDeviceEvent *>(event); - xXIDeviceEvent *xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(xiEvent); + const xXIGenericDeviceEvent *xiEvent = static_cast<const xXIGenericDeviceEvent *>(event); + const xXIDeviceEvent *xiDeviceEvent = reinterpret_cast<const xXIDeviceEvent *>(xiEvent); switch (xiEvent->evtype) { case XI_ButtonPress: { Qt::MouseButton b = xiToQtMouseButton(xiDeviceEvent->detail); tabletData->buttons |= b; - xi2ReportTabletEvent(*tabletData, xiEvent); + xi2ReportTabletEvent(xiEvent, tabletData); break; } case XI_ButtonRelease: { Qt::MouseButton b = xiToQtMouseButton(xiDeviceEvent->detail); tabletData->buttons ^= b; - xi2ReportTabletEvent(*tabletData, xiEvent); + xi2ReportTabletEvent(xiEvent, tabletData); break; } case XI_Motion: // Report TabletMove only when the stylus is touching the tablet or any button is pressed. // TODO: report proximity (hover) motion (no suitable Qt event exists yet). if (tabletData->buttons != Qt::NoButton) - xi2ReportTabletEvent(*tabletData, xiEvent); + xi2ReportTabletEvent(xiEvent, tabletData); break; case XI_PropertyEvent: { // This is the wacom driver's way of reporting tool proximity. // The evdev driver doesn't do it this way. - xXIPropertyEvent *ev = reinterpret_cast<xXIPropertyEvent *>(event); + const xXIPropertyEvent *ev = reinterpret_cast<const xXIPropertyEvent *>(event); if (ev->what == XIPropertyModified) { if (ev->property == atom(QXcbAtom::WacomSerialIDs)) { enum WacomSerialIndex { @@ -1132,21 +1129,12 @@ bool QXcbConnection::xi2HandleTabletEvent(void *event, TabletData *tabletData, Q break; } -#ifdef XCB_USE_XINPUT22 - // Synthesize mouse events since otherwise there are no mouse events from - // the pen on the XI 2.2+ path. - if (xi2MouseEvents() && eventListener) - eventListener->handleXIMouseEvent(reinterpret_cast<xcb_ge_event_t *>(event), Qt::MouseEventSynthesizedByQt); -#else - Q_UNUSED(eventListener); -#endif - return handled; } -void QXcbConnection::xi2ReportTabletEvent(TabletData &tabletData, void *event) +void QXcbConnection::xi2ReportTabletEvent(const void *event, TabletData *tabletData) { - xXIDeviceEvent *ev = reinterpret_cast<xXIDeviceEvent *>(event); + const xXIDeviceEvent *ev = reinterpret_cast<const xXIDeviceEvent *>(event); QXcbWindow *xcbWindow = platformWindowFromId(ev->event); if (!xcbWindow) return; @@ -1157,8 +1145,8 @@ void QXcbConnection::xi2ReportTabletEvent(TabletData &tabletData, void *event) double pressure = 0, rotation = 0, tangentialPressure = 0; int xTilt = 0, yTilt = 0; - for (QHash<int, TabletData::ValuatorClassInfo>::iterator it = tabletData.valuatorInfo.begin(), - ite = tabletData.valuatorInfo.end(); it != ite; ++it) { + for (QHash<int, TabletData::ValuatorClassInfo>::iterator it = tabletData->valuatorInfo.begin(), + ite = tabletData->valuatorInfo.end(); it != ite; ++it) { int valuator = it.key(); TabletData::ValuatorClassInfo &classInfo(it.value()); xi2GetValuatorValueIfSet(event, classInfo.number, &classInfo.curVal); @@ -1174,7 +1162,7 @@ void QXcbConnection::xi2ReportTabletEvent(TabletData &tabletData, void *event) yTilt = classInfo.curVal; break; case QXcbAtom::AbsWheel: - switch (tabletData.tool) { + switch (tabletData->tool) { case QTabletEvent::Airbrush: tangentialPressure = normalizedValue * 2.0 - 1.0; // Convert 0..1 range to -1..+1 range break; @@ -1193,17 +1181,27 @@ 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 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, + 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); + (int)tabletData->buttons, pressure, xTilt, yTilt, rotation); QWindowSystemInterface::handleTabletEvent(window, ev->time, local, global, - tabletData.tool, tabletData.pointerType, - tabletData.buttons, pressure, + tabletData->tool, tabletData->pointerType, + tabletData->buttons, pressure, xTilt, yTilt, tangentialPressure, - rotation, 0, tabletData.serialId); + rotation, 0, tabletData->serialId); } + +QXcbConnection::TabletData *QXcbConnection::tabletDataForDevice(int id) +{ + for (int i = 0; i < m_tabletData.count(); ++i) { + if (m_tabletData.at(i).deviceId == id) + return &m_tabletData[i]; + } + return Q_NULLPTR; +} + #endif // QT_NO_TABLETEVENT #endif // XCB_USE_XINPUT2 diff --git a/src/plugins/platforms/xcb/qxcbcursor.cpp b/src/plugins/platforms/xcb/qxcbcursor.cpp index 0b539a2241..11e0c998b1 100644 --- a/src/plugins/platforms/xcb/qxcbcursor.cpp +++ b/src/plugins/platforms/xcb/qxcbcursor.cpp @@ -343,7 +343,7 @@ QXcbCursor::~QXcbCursor() xcb_close_font(conn, cursorFont); #ifndef QT_NO_CURSOR - foreach (xcb_cursor_t cursor, m_cursorHash) + for (xcb_cursor_t cursor : qAsConst(m_cursorHash)) xcb_free_cursor(conn, cursor); #endif } @@ -624,7 +624,8 @@ void QXcbCursor::queryPointer(QXcbConnection *c, QXcbVirtualDesktop **virtualDes 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()) { + const auto virtualDesktops = c->virtualDesktops(); + for (QXcbVirtualDesktop *vd : virtualDesktops) { if (vd->root() == reply->root) { *virtualDesktop = vd; break; diff --git a/src/plugins/platforms/xcb/qxcbdrag.cpp b/src/plugins/platforms/xcb/qxcbdrag.cpp index ea20ef7a04..b55fbd8f03 100644 --- a/src/plugins/platforms/xcb/qxcbdrag.cpp +++ b/src/plugins/platforms/xcb/qxcbdrag.cpp @@ -1078,6 +1078,40 @@ void QXcbDrag::cancel() send_leave(); } +// find an ancestor with XdndAware on it +static xcb_window_t findXdndAwareParent(QXcbConnection *c, xcb_window_t window) +{ + xcb_window_t target = 0; + forever { + // check if window has XdndAware + xcb_get_property_cookie_t gpCookie = Q_XCB_CALL( + xcb_get_property(c->xcb_connection(), false, window, + c->atom(QXcbAtom::XdndAware), XCB_GET_PROPERTY_TYPE_ANY, 0, 0)); + xcb_get_property_reply_t *gpReply = xcb_get_property_reply( + c->xcb_connection(), gpCookie, 0); + bool aware = gpReply && gpReply->type != XCB_NONE; + free(gpReply); + if (aware) { + target = window; + break; + } + + // try window's parent + xcb_query_tree_cookie_t qtCookie = Q_XCB_CALL( + xcb_query_tree_unchecked(c->xcb_connection(), window)); + xcb_query_tree_reply_t *qtReply = xcb_query_tree_reply( + c->xcb_connection(), qtCookie, NULL); + if (!qtReply) + break; + xcb_window_t root = qtReply->root; + xcb_window_t parent = qtReply->parent; + free(qtReply); + if (window == root) + break; + window = parent; + } + return target; +} void QXcbDrag::handleSelectionRequest(const xcb_selection_request_event_t *event) { @@ -1105,17 +1139,16 @@ void QXcbDrag::handleSelectionRequest(const xcb_selection_request_event_t *event // xcb_convert_selection() that we sent the XdndDrop event to. at = findTransactionByWindow(event->requestor); } -// if (at == -1 && event->time == XCB_CURRENT_TIME) { -// // previous Qt versions always requested the data on a child of the target window -// // using CurrentTime... but it could be asking for either drop data or the current drag's data -// Window target = findXdndAwareParent(event->requestor); -// if (target) { -// if (current_target && current_target == target) -// at = -2; -// else -// at = findXdndDropTransactionByWindow(target); -// } -// } + + if (at == -1) { + xcb_window_t target = findXdndAwareParent(connection(), event->requestor); + if (target) { + if (event->time == XCB_CURRENT_TIME && current_target == target) + at = -2; + else + at = findTransactionByWindow(target); + } + } } QDrag *transactionDrag = 0; diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp index b5e03e68fe..703167c0cd 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.cpp +++ b/src/plugins/platforms/xcb/qxcbintegration.cpp @@ -250,17 +250,25 @@ QPlatformOffscreenSurface *QXcbIntegration::createPlatformOffscreenSurface(QOffs bool QXcbIntegration::hasCapability(QPlatformIntegration::Capability cap) const { switch (cap) { - case ThreadedPixmaps: return true; - case OpenGL: return m_connections.first()->glIntegration(); - case ThreadedOpenGL: return m_connections.at(0)->threadedEventHandling() - && m_connections.at(0)->glIntegration() - && m_connections.at(0)->glIntegration()->supportsThreadedOpenGL(); - case WindowMasks: return true; - case MultipleWindows: return true; - case ForeignWindows: return true; - case SyncState: return true; - case RasterGLSurface: return true; - case SwitchableWidgetComposition: return true; + case OpenGL: + case ThreadedOpenGL: + { + const auto *connection = qAsConst(m_connections).first(); + if (const auto *integration = connection->glIntegration()) + return cap != ThreadedOpenGL + || (connection->threadedEventHandling() && integration->supportsThreadedOpenGL()); + return false; + } + + case ThreadedPixmaps: + case WindowMasks: + case MultipleWindows: + case ForeignWindows: + case SyncState: + case RasterGLSurface: + case SwitchableWidgetComposition: + return true; + default: return QPlatformIntegration::hasCapability(cap); } } diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.cpp b/src/plugins/platforms/xcb/qxcbkeyboard.cpp index 48912e0520..7168e6e3bd 100644 --- a/src/plugins/platforms/xcb/qxcbkeyboard.cpp +++ b/src/plugins/platforms/xcb/qxcbkeyboard.cpp @@ -692,8 +692,8 @@ void QXcbKeyboard::updateKeymap() if (!xkb_context) { if (qEnvironmentVariableIsSet("QT_XKB_CONFIG_ROOT")) { xkb_context = xkb_context_new((xkb_context_flags)XKB_CONTEXT_NO_DEFAULT_INCLUDES); - QList<QByteArray> xkbRootList = QByteArray(qgetenv("QT_XKB_CONFIG_ROOT")).split(':'); - foreach (const QByteArray &xkbRoot, xkbRootList) + const QList<QByteArray> xkbRootList = QByteArray(qgetenv("QT_XKB_CONFIG_ROOT")).split(':'); + for (const QByteArray &xkbRoot : xkbRootList) xkb_context_include_path_append(xkb_context, xkbRoot.constData()); } else { xkb_context = xkb_context_new((xkb_context_flags)0); @@ -1038,7 +1038,7 @@ QList<int> QXcbKeyboard::possibleKeys(const QKeyEvent *event) const // catch only more specific shortcuts, i.e. Ctrl+Shift+= also generates Ctrl++ and +, // but Ctrl++ is more specific than +, so we should skip the last one bool ambiguous = false; - foreach (int shortcut, result) { + for (int shortcut : qAsConst(result)) { if (int(shortcut & ~Qt::KeyboardModifierMask) == qtKey && (shortcut & mods) == mods) { ambiguous = true; break; diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp index 4cb1b29152..dc6846b20b 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.cpp +++ b/src/plugins/platforms/xcb/qxcbscreen.cpp @@ -76,7 +76,8 @@ QXcbVirtualDesktop::~QXcbVirtualDesktop() QXcbScreen *QXcbVirtualDesktop::screenAt(const QPoint &pos) const { - foreach (QXcbScreen *screen, connection()->screens()) { + const auto screens = connection()->screens(); + for (QXcbScreen *screen : screens) { if (screen->virtualDesktop() == this && screen->geometry().contains(pos)) return screen; } @@ -157,7 +158,7 @@ void QXcbVirtualDesktop::updateWorkArea() QRect workArea = getWorkArea(); if (m_workArea != workArea) { m_workArea = workArea; - foreach (QPlatformScreen *screen, m_screens) + for (QPlatformScreen *screen : qAsConst(m_screens)) ((QXcbScreen *)screen)->updateAvailableGeometry(); } } @@ -386,10 +387,9 @@ QSurfaceFormat QXcbScreen::surfaceFormatFor(const QSurfaceFormat &format) const const xcb_visualtype_t *QXcbScreen::visualForFormat(const QSurfaceFormat &format) const { - QVector<const xcb_visualtype_t *> candidates; + const xcb_visualtype_t *candidate = nullptr; - for (auto ii = m_visuals.constBegin(); ii != m_visuals.constEnd(); ++ii) { - const xcb_visualtype_t &xcb_visualtype = ii.value(); + for (const xcb_visualtype_t &xcb_visualtype : m_visuals) { const int redSize = qPopulationCount(xcb_visualtype.red_mask); const int greenSize = qPopulationCount(xcb_visualtype.green_mask); @@ -408,19 +408,17 @@ const xcb_visualtype_t *QXcbScreen::visualForFormat(const QSurfaceFormat &format if (format.alphaBufferSize() != -1 && alphaSize != format.alphaBufferSize()) continue; - candidates.append(&xcb_visualtype); - } - - if (candidates.isEmpty()) - return nullptr; + // Try to find a RGB visual rather than e.g. BGR or GBR + if (qCountTrailingZeroBits(xcb_visualtype.blue_mask) == 0) + return &xcb_visualtype; - // Try to find a RGB visual rather than e.g. BGR or GBR - for (const xcb_visualtype_t *candidate : qAsConst(candidates)) - if (qCountTrailingZeroBits(candidate->blue_mask) == 0) - return candidate; + // In case we do not find anything we like, just remember the first one + // and hope for the best: + if (!candidate) + candidate = &xcb_visualtype; + } - // Did not find anything we like, just grab the first one and hope for the best - return candidates.first(); + return candidate; } void QXcbScreen::sendStartupMessage(const QByteArray &message) const diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index b0b21a5be4..17fafd5ef0 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -286,7 +286,7 @@ static inline XTextProperty* qstringToXTP(Display *dpy, const QString& s) // TODO move this into a utility function in QWindow or QGuiApplication static QWindow *childWindowAt(QWindow *win, const QPoint &p) { - foreach (QObject *obj, win->children()) { + for (QObject *obj : win->children()) { if (obj->isWindowType()) { QWindow *childWin = static_cast<QWindow *>(obj); if (childWin->isVisible()) { @@ -592,8 +592,12 @@ QXcbWindow::~QXcbWindow() { if (window()->type() != Qt::ForeignWindow) destroy(); - else if (connection()->mouseGrabber() == this) - connection()->setMouseGrabber(Q_NULLPTR); + else { + if (connection()->mouseGrabber() == this) + connection()->setMouseGrabber(Q_NULLPTR); + if (connection()->mousePressWindow() == this) + connection()->setMousePressWindow(Q_NULLPTR); + } } void QXcbWindow::destroy() @@ -851,6 +855,16 @@ void QXcbWindow::hide() if (connection()->mouseGrabber() == this) connection()->setMouseGrabber(Q_NULLPTR); + if (QPlatformWindow *w = connection()->mousePressWindow()) { + // Unset mousePressWindow when it (or one of its parents) is unmapped + while (w) { + if (w == this) { + connection()->setMousePressWindow(Q_NULLPTR); + break; + } + w = w->parent(); + } + } m_mapped = false; @@ -864,7 +878,8 @@ void QXcbWindow::hide() // Find the top level window at cursor position. // Don't use QGuiApplication::topLevelAt(): search only the virtual siblings of this window's screen QWindow *enterWindow = Q_NULLPTR; - foreach (QPlatformScreen *screen, xcbScreen()->virtualSiblings()) { + const auto screens = xcbScreen()->virtualSiblings(); + for (QPlatformScreen *screen : screens) { if (screen->geometry().contains(cursorPos)) { const QPoint devicePosition = QHighDpi::toNativePixels(cursorPos, screen->screen()); enterWindow = screen->topLevelAt(devicePosition); @@ -2198,6 +2213,8 @@ void QXcbWindow::handleButtonPressEvent(int event_x, int event_y, int root_x, in return; } + connection()->setMousePressWindow(this); + handleMouseEvent(timestamp, local, global, modifiers, source); } @@ -2212,19 +2229,44 @@ void QXcbWindow::handleButtonReleaseEvent(int event_x, int event_y, int root_x, return; } + if (connection()->buttons() == Qt::NoButton) + connection()->setMousePressWindow(Q_NULLPTR); + handleMouseEvent(timestamp, local, global, modifiers, source); } -static bool ignoreLeaveEvent(quint8 mode, quint8 detail) +static inline bool doCheckUnGrabAncestor(QXcbConnection *conn) +{ + /* Checking for XCB_NOTIFY_MODE_GRAB and XCB_NOTIFY_DETAIL_ANCESTOR prevents unwanted + * enter/leave events on AwesomeWM on mouse button press. It also ignores duplicated + * enter/leave events on Alt+Tab switching on some WMs with XInput2 events. + * Without XInput2 events the (Un)grabAncestor cannot be checked when mouse button is + * not pressed, otherwise (e.g. on Alt+Tab) it can igonre important enter/leave events. + */ + if (conn) { + const bool mouseButtonsPressed = (conn->buttons() != Qt::NoButton); +#ifdef XCB_USE_XINPUT22 + return mouseButtonsPressed || (conn->isAtLeastXI22() && conn->xi2MouseEvents()); +#else + return mouseButtonsPressed; +#endif + } + return true; +} + +static bool ignoreLeaveEvent(quint8 mode, quint8 detail, QXcbConnection *conn = Q_NULLPTR) { - return (mode == XCB_NOTIFY_MODE_GRAB && detail == XCB_NOTIFY_DETAIL_ANCESTOR) // Check for AwesomeWM + return ((doCheckUnGrabAncestor(conn) + && mode == XCB_NOTIFY_MODE_GRAB && detail == XCB_NOTIFY_DETAIL_ANCESTOR) + || (mode == XCB_NOTIFY_MODE_UNGRAB && detail == XCB_NOTIFY_DETAIL_INFERIOR) || detail == XCB_NOTIFY_DETAIL_VIRTUAL - || detail == XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL; + || detail == XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL); } -static bool ignoreEnterEvent(quint8 mode, quint8 detail) +static bool ignoreEnterEvent(quint8 mode, quint8 detail, QXcbConnection *conn = Q_NULLPTR) { - return ((mode == XCB_NOTIFY_MODE_UNGRAB && detail == XCB_NOTIFY_DETAIL_ANCESTOR) // Check for AwesomeWM + return ((doCheckUnGrabAncestor(conn) + && mode == XCB_NOTIFY_MODE_UNGRAB && detail == XCB_NOTIFY_DETAIL_ANCESTOR) || (mode != XCB_NOTIFY_MODE_NORMAL && mode != XCB_NOTIFY_MODE_UNGRAB) || detail == XCB_NOTIFY_DETAIL_VIRTUAL || detail == XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL); @@ -2258,9 +2300,7 @@ void QXcbWindow::handleEnterNotifyEvent(int event_x, int event_y, int root_x, in const QPoint global = QPoint(root_x, root_y); - if (ignoreEnterEvent(mode, detail) - || (connection()->buttons() != Qt::NoButton - && QGuiApplicationPrivate::lastCursorPosition != global)) + if (ignoreEnterEvent(mode, detail, connection()) || connection()->mousePressWindow()) return; const QPoint local(event_x, event_y); @@ -2272,11 +2312,7 @@ void QXcbWindow::handleLeaveNotifyEvent(int root_x, int root_y, { connection()->setTime(timestamp); - const QPoint global(root_x, root_y); - - if (ignoreLeaveEvent(mode, detail) - || (connection()->buttons() != Qt::NoButton - && QGuiApplicationPrivate::lastCursorPosition != global)) + if (ignoreLeaveEvent(mode, detail, connection()) || connection()->mousePressWindow()) return; EnterEventChecker checker; @@ -2299,6 +2335,11 @@ void QXcbWindow::handleMotionNotifyEvent(int event_x, int event_y, int root_x, i { QPoint local(event_x, event_y); QPoint global(root_x, root_y); + + // "mousePressWindow" can be NULL i.e. if a window will be grabbed or umnapped, so set it again here + if (connection()->buttons() != Qt::NoButton && connection()->mousePressWindow() == Q_NULLPTR) + connection()->setMousePressWindow(this); + handleMouseEvent(timestamp, local, global, modifiers, source); } @@ -2350,7 +2391,7 @@ void QXcbWindow::handleXIMouseEvent(xcb_ge_event_t *event, Qt::MouseEventSource conn->setButton(conn->translateMouseButton(i), XIMaskIsSet(buttonMask, i)); } - const char *sourceName = nullptr; + const char *sourceName = 0; if (lcQpaXInput().isDebugEnabled()) { const QMetaObject *metaObject = qt_getEnumMetaObject(source); const QMetaEnum me = metaObject->enumerator(metaObject->indexOfEnumerator(qt_getEnumName(source))); |