diff options
Diffstat (limited to 'src/plugins/platforms')
-rw-r--r-- | src/plugins/platforms/android/extract-dummy.cpp | 5 | ||||
-rw-r--r-- | src/plugins/platforms/android/extract.cpp | 15 | ||||
-rw-r--r-- | src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp | 17 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm | 2 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoamenu.mm | 23 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoascreen.h | 5 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoascreen.mm | 115 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoawindow.mm | 27 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qnswindow.mm | 2 | ||||
-rw-r--r-- | src/plugins/platforms/ios/qiosviewcontroller.mm | 4 | ||||
-rw-r--r-- | src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp | 17 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbclipboard.cpp | 8 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbconnection.cpp | 45 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbwindow.cpp | 115 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbwindow.h | 16 |
15 files changed, 260 insertions, 156 deletions
diff --git a/src/plugins/platforms/android/extract-dummy.cpp b/src/plugins/platforms/android/extract-dummy.cpp index fdce8ec64c..8cd317be4d 100644 --- a/src/plugins/platforms/android/extract-dummy.cpp +++ b/src/plugins/platforms/android/extract-dummy.cpp @@ -44,8 +44,3 @@ extern "C" JNIEXPORT jintArray JNICALL Java_org_qtproject_qt5_android_ExtractSty { return 0; } - -extern "C" JNIEXPORT jintArray JNICALL Java_org_qtproject_qt5_android_ExtractStyle_extractChunkInfo20(JNIEnv *, jobject, jbyteArray) -{ - return 0; -} diff --git a/src/plugins/platforms/android/extract.cpp b/src/plugins/platforms/android/extract.cpp index acffa353f1..6ce6153966 100644 --- a/src/plugins/platforms/android/extract.cpp +++ b/src/plugins/platforms/android/extract.cpp @@ -1,5 +1,6 @@ /**************************************************************************** ** +** Copyright (C) 2021 The Qt Company Ltd. ** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org> ** Contact: https://www.qt.io/licensing/ ** @@ -123,20 +124,6 @@ extern "C" JNIEXPORT jintArray JNICALL Java_org_qtproject_qt5_android_ExtractSty return result; } -extern "C" JNIEXPORT jintArray JNICALL Java_org_qtproject_qt5_android_ExtractStyle_extractChunkInfo20(JNIEnv * env, jobject obj, jbyteArray chunkObj) -{ - size_t chunkSize = env->GetArrayLength(chunkObj); - void* storage = alloca(chunkSize); - env->GetByteArrayRegion(chunkObj, 0, chunkSize, - reinterpret_cast<jbyte*>(storage)); - - if (!env->ExceptionCheck()) - return Java_org_qtproject_qt5_android_ExtractStyle_extractNativeChunkInfo20(env, obj, long(storage)); - else - env->ExceptionClear(); - return 0; -} - static inline void fill9patchOffsets(Res_png_9patch20* patch) { patch->xDivsOffset = sizeof(Res_png_9patch20); patch->yDivsOffset = patch->xDivsOffset + (patch->numXDivs * sizeof(int32_t)); diff --git a/src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp b/src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp index 6bb3372380..6f25045ae7 100644 --- a/src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp +++ b/src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp @@ -147,10 +147,10 @@ QStringList nameFilterExtensions(const QString nameFilters) { QStringList ret; #if QT_CONFIG(regularexpression) - QRegularExpression re("(\\*\\.?\\w*)"); + QRegularExpression re("(\\*\\.[a-z .]+)"); QRegularExpressionMatchIterator i = re.globalMatch(nameFilters); while (i.hasNext()) - ret << i.next().captured(1); + ret << i.next().captured(1).trimmed(); #endif // QT_CONFIG(regularexpression) ret.removeAll("*"); return ret; @@ -159,23 +159,24 @@ QStringList nameFilterExtensions(const QString nameFilters) void QAndroidPlatformFileDialogHelper::setMimeTypes() { QStringList mimeTypes = options()->mimeTypeFilters(); - const QString nameFilter = options()->initiallySelectedNameFilter(); + const QStringList nameFilters = options()->nameFilters(); + const QString nameFilter = nameFilters.isEmpty() ? QString() : nameFilters.first(); - if (mimeTypes.isEmpty() && !nameFilter.isEmpty()) { + if (!nameFilter.isEmpty()) { QMimeDatabase db; for (const QString &filter : nameFilterExtensions(nameFilter)) - mimeTypes.append(db.mimeTypeForFile(filter).name()); + mimeTypes.append(db.mimeTypeForFile(filter, QMimeDatabase::MatchExtension).name()); } - QString type = !mimeTypes.isEmpty() ? mimeTypes.at(0) : QLatin1String("*/*"); + const QString initialType = mimeTypes.size() == 1 ? mimeTypes.at(0) : QLatin1String("*/*"); m_intent.callObjectMethod("setType", "(Ljava/lang/String;)Landroid/content/Intent;", - QJNIObjectPrivate::fromString(type).object()); + QJNIObjectPrivate::fromString(initialType).object()); if (!mimeTypes.isEmpty()) { const QJNIObjectPrivate extraMimeType = QJNIObjectPrivate::getStaticObjectField( JniIntentClass, "EXTRA_MIME_TYPES", "Ljava/lang/String;"); - QJNIObjectPrivate mimeTypesArray = QJNIObjectPrivate::callStaticObjectMethod( + const QJNIObjectPrivate mimeTypesArray = QJNIObjectPrivate::callStaticObjectMethod( "org/qtproject/qt5/android/QtNative", "getStringArray", "(Ljava/lang/String;)[Ljava/lang/String;", diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm index ad40c6b0cb..e1f664c9da 100644 --- a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm +++ b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm @@ -246,7 +246,7 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of return iface->text(QAccessible::Name).toNSString(); } -- (BOOL) accessibilityEnabledAttribute { +- (BOOL) isAccessibilityEnabled { QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); if (!iface || !iface->isValid()) return false; diff --git a/src/plugins/platforms/cocoa/qcocoamenu.mm b/src/plugins/platforms/cocoa/qcocoamenu.mm index e88cc54143..68af82b5cd 100644 --- a/src/plugins/platforms/cocoa/qcocoamenu.mm +++ b/src/plugins/platforms/cocoa/qcocoamenu.mm @@ -290,31 +290,26 @@ void QCocoaMenu::syncSeparatorsCollapsible(bool enable) QMacAutoReleasePool pool; if (enable) { bool previousIsSeparator = true; // setting to true kills all the separators placed at the top. - NSMenuItem *previousItem = nil; + NSMenuItem *lastVisibleItem = nil; for (NSMenuItem *item in m_nativeMenu.itemArray) { if (item.separatorItem) { // hide item if previous was a separator, or if it's explicitly hidden - bool itemVisible = !previousIsSeparator; - if (auto *cocoaItem = qt_objc_cast<QCocoaNSMenuItem *>(item).platformMenuItem) { - cocoaItem->setVisible(!previousIsSeparator && cocoaItem->isVisible()); - itemVisible = cocoaItem->isVisible(); - } - item.hidden = !itemVisible; + bool hideItem = previousIsSeparator; + if (auto *cocoaItem = qt_objc_cast<QCocoaNSMenuItem *>(item).platformMenuItem) + hideItem = previousIsSeparator || !cocoaItem->isVisible(); + item.hidden = hideItem; } if (!item.hidden) { - previousItem = item; - previousIsSeparator = previousItem.separatorItem; + lastVisibleItem = item; + previousIsSeparator = lastVisibleItem.separatorItem; } } // We now need to check the final item since we don't want any separators at the end of the list. - if (previousItem && previousIsSeparator) { - if (auto *cocoaItem = qt_objc_cast<QCocoaNSMenuItem *>(previousItem).platformMenuItem) - cocoaItem->setVisible(false); - previousItem.hidden = YES; - } + if (lastVisibleItem && lastVisibleItem.separatorItem) + lastVisibleItem.hidden = YES; } else { for (auto *item : qAsConst(m_menuItems)) { if (!item->isSeparator()) diff --git a/src/plugins/platforms/cocoa/qcocoascreen.h b/src/plugins/platforms/cocoa/qcocoascreen.h index dcf6f1c753..93dff027a6 100644 --- a/src/plugins/platforms/cocoa/qcocoascreen.h +++ b/src/plugins/platforms/cocoa/qcocoascreen.h @@ -45,6 +45,7 @@ #include "qcocoacursor.h" #include <qpa/qplatformintegration.h> +#include <QtCore/private/qcore_mac_p.h> QT_BEGIN_NAMESPACE @@ -96,8 +97,8 @@ private: static void updateScreens(); static void cleanupScreens(); - static bool updateScreensIfNeeded(); - static NSArray *s_screenConfigurationBeforeUpdate; + static QMacNotificationObserver s_screenParameterObserver; + static CGDisplayReconfigurationCallBack s_displayReconfigurationCallBack; static void add(CGDirectDisplayID displayId); QCocoaScreen(CGDirectDisplayID displayId); diff --git a/src/plugins/platforms/cocoa/qcocoascreen.mm b/src/plugins/platforms/cocoa/qcocoascreen.mm index 6a3172fb19..581ea01fcc 100644 --- a/src/plugins/platforms/cocoa/qcocoascreen.mm +++ b/src/plugins/platforms/cocoa/qcocoascreen.mm @@ -72,91 +72,33 @@ namespace CoreGraphics { Q_ENUM_NS(DisplayChange) } -NSArray *QCocoaScreen::s_screenConfigurationBeforeUpdate = nil; +QMacNotificationObserver QCocoaScreen::s_screenParameterObserver; +CGDisplayReconfigurationCallBack QCocoaScreen::s_displayReconfigurationCallBack = nullptr; void QCocoaScreen::initializeScreens() { updateScreens(); - CGDisplayRegisterReconfigurationCallback([](CGDirectDisplayID displayId, CGDisplayChangeSummaryFlags flags, void *userInfo) { + s_displayReconfigurationCallBack = [](CGDirectDisplayID displayId, CGDisplayChangeSummaryFlags flags, void *userInfo) { Q_UNUSED(userInfo); - // Displays are reconfigured in batches, and we want to update our screens - // once a batch ends, so that all the states of the displays are up to date. - static int displayReconfigurationsInProgress = 0; - const bool beforeReconfigure = flags & kCGDisplayBeginConfigurationFlag; - qCDebug(lcQpaScreen).verbosity(0).nospace() << "Display " << displayId - << (beforeReconfigure ? " about to reconfigure" : " was ") - << QFlags<CoreGraphics::DisplayChange>(flags) - << " with " << displayReconfigurationsInProgress - << " display configuration(s) in progress"; - - if (!flags) { - // CGDisplayRegisterReconfigurationCallback has been observed to be called - // with flags unset. This seems like a bug. The callback is not paired with - // a matching "completion" callback either, so we don't know whether to treat - // it as a begin or end of reconfigure. - return; - } - - if (beforeReconfigure) { - if (!displayReconfigurationsInProgress++) { - // There might have been a screen reconfigure before this that - // we didn't process yet, so do that now if that's the case. - updateScreensIfNeeded(); - - Q_ASSERT(!s_screenConfigurationBeforeUpdate); - s_screenConfigurationBeforeUpdate = NSScreen.screens; - qCDebug(lcQpaScreen, "Display reconfigure transaction started" - " with screen configuration %p", s_screenConfigurationBeforeUpdate); - - static void (^tryScreenUpdate)(); - tryScreenUpdate = ^void () { - qCDebug(lcQpaScreen) << "Attempting screen update from runloop block"; - if (!updateScreensIfNeeded()) - CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, tryScreenUpdate); - }; - CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, tryScreenUpdate); - } - } else { - Q_ASSERT_X(displayReconfigurationsInProgress, "QCococaScreen", - "Display configuration transactions are expected to be balanced"); + qCDebug(lcQpaScreen).verbosity(0) << "Display" << displayId + << (beforeReconfigure ? "beginning" : "finished") << "reconfigure" + << QFlags<CoreGraphics::DisplayChange>(flags); - if (!--displayReconfigurationsInProgress) { - qCDebug(lcQpaScreen) << "Display reconfigure transaction completed"; - // We optimistically update now, in case the NSScreens have changed - updateScreensIfNeeded(); - } - } - }, nullptr); + if (!beforeReconfigure) + updateScreens(); + }; + CGDisplayRegisterReconfigurationCallback(s_displayReconfigurationCallBack, nullptr); - static QMacNotificationObserver screenParameterObserver(NSApplication.sharedApplication, + s_screenParameterObserver = QMacNotificationObserver(NSApplication.sharedApplication, NSApplicationDidChangeScreenParametersNotification, [&]() { qCDebug(lcQpaScreen) << "Received screen parameter change notification"; - updateScreensIfNeeded(); // As a last resort we update screens here + updateScreens(); }); } -bool QCocoaScreen::updateScreensIfNeeded() -{ - if (!s_screenConfigurationBeforeUpdate) { - qCDebug(lcQpaScreen) << "QScreens have already been updated, all good"; - return true; - } - - if (s_screenConfigurationBeforeUpdate == NSScreen.screens) { - qCDebug(lcQpaScreen) << "Still waiting for NSScreen configuration change"; - return false; - } - - qCDebug(lcQpaScreen, "NSScreen configuration changed to %p", NSScreen.screens); - updateScreens(); - - s_screenConfigurationBeforeUpdate = nil; - return true; -} - /* Update the list of available QScreens, and the properties of existing screens. @@ -239,6 +181,12 @@ void QCocoaScreen::cleanupScreens() // Remove screens in reverse order to avoid crash in case of multiple screens for (QScreen *screen : backwards(QGuiApplication::screens())) static_cast<QCocoaScreen*>(screen->handle())->remove(); + + Q_ASSERT(s_displayReconfigurationCallBack); + CGDisplayRemoveReconfigurationCallback(s_displayReconfigurationCallBack, nullptr); + s_displayReconfigurationCallBack = nullptr; + + s_screenParameterObserver.remove(); } void QCocoaScreen::remove() @@ -310,15 +258,18 @@ void QCocoaScreen::update(CGDirectDisplayID displayId) Q_ASSERT(isOnline()); + // Some properties are only available via NSScreen + NSScreen *nsScreen = nativeScreen(); + if (!nsScreen) { + qCDebug(lcQpaScreen) << "Corresponding NSScreen not yet available. Deferring update"; + return; + } + const QRect previousGeometry = m_geometry; const QRect previousAvailableGeometry = m_availableGeometry; const QDpi previousLogicalDpi = m_logicalDpi; const qreal previousRefreshRate = m_refreshRate; - // Some properties are only available via NSScreen - NSScreen *nsScreen = nativeScreen(); - Q_ASSERT(nsScreen); - // The reference screen for the geometry is always the primary screen QRectF primaryScreenGeometry = QRectF::fromCGRect(CGDisplayBounds(CGMainDisplayID())); m_geometry = qt_mac_flip(QRectF::fromCGRect(nsScreen.frame), primaryScreenGeometry).toRect(); @@ -714,13 +665,17 @@ QList<QPlatformScreen*> QCocoaScreen::virtualSiblings() const QCocoaScreen *QCocoaScreen::get(NSScreen *nsScreen) { - if (s_screenConfigurationBeforeUpdate) { - qCWarning(lcQpaScreen) << "Trying to resolve screen while waiting for screen reconfigure!"; - if (!updateScreensIfNeeded()) - qCWarning(lcQpaScreen) << "Failed to do last minute screen update. Expect crashes."; + auto displayId = nsScreen.qt_displayId; + auto *cocoaScreen = get(displayId); + if (!cocoaScreen) { + qCWarning(lcQpaScreen) << "Failed to map" << nsScreen + << "to QCocoaScreen. Doing last minute update."; + updateScreens(); + cocoaScreen = get(displayId); + if (!cocoaScreen) + qCWarning(lcQpaScreen) << "Last minute update failed!"; } - - return get(nsScreen.qt_displayId); + return cocoaScreen; } QCocoaScreen *QCocoaScreen::get(CGDirectDisplayID displayId) diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index 5be65f2141..ad3ebf61b4 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -522,7 +522,12 @@ NSUInteger QCocoaWindow::windowStyleMask(Qt::WindowFlags flags) NSUInteger styleMask = (frameless || !resizable) ? NSWindowStyleMaskBorderless : NSWindowStyleMaskResizable; if (frameless) { - // No further customizations for frameless since there are no window decorations. + // Frameless windows do not display the traffic lights buttons for + // e.g. minimize, however StyleMaskMiniaturizable is required to allow + // programatic minimize. However, for framless tool windows (e.g. dock windows) + // we don't want that, as it breaks translucency. + if (type != Qt::Tool) + styleMask |= NSWindowStyleMaskMiniaturizable; } else if (flags & Qt::CustomizeWindowHint) { if (flags & Qt::WindowTitleHint) styleMask |= NSWindowStyleMaskTitled; @@ -1265,11 +1270,15 @@ void QCocoaWindow::windowDidChangeScreen() return; // Note: When a window is resized to 0x0 Cocoa will report the window's screen as nil - auto *currentScreen = QCocoaScreen::get(m_view.window.screen); + NSScreen *nsScreen = m_view.window.screen; + + qCDebug(lcQpaWindow) << window() << "did change" << nsScreen; + QCocoaScreen::updateScreens(); + auto *previousScreen = static_cast<QCocoaScreen*>(screen()); + auto *currentScreen = QCocoaScreen::get(nsScreen); - Q_ASSERT_X(!m_view.window.screen || currentScreen, - "QCocoaWindow", "Failed to get QCocoaScreen for NSScreen"); + qCDebug(lcQpaWindow) << "Screen changed for" << window() << "from" << previousScreen << "to" << currentScreen; // Note: The previous screen may be the same as the current screen, either because // a) the screen was just reconfigured, which still results in AppKit sending an @@ -1282,7 +1291,6 @@ void QCocoaWindow::windowDidChangeScreen() // device-pixel ratio may have changed, and needs to be delivered to all // windows, both top level and child windows. - qCDebug(lcQpaWindow) << "Screen changed for" << window() << "from" << previousScreen << "to" << currentScreen; QWindowSystemInterface::handleWindowScreenChanged<QWindowSystemInterface::SynchronousDelivery>( window(), currentScreen ? currentScreen->screen() : nullptr); @@ -1307,10 +1315,19 @@ void QCocoaWindow::windowWillClose() bool QCocoaWindow::windowShouldClose() { qCDebug(lcQpaWindow) << "QCocoaWindow::windowShouldClose" << window(); + // This callback should technically only determine if the window // should (be allowed to) close, but since our QPA API to determine // that also involves actually closing the window we do both at the // same time, instead of doing the latter in windowWillClose. + + // If the window is closed, we will release and deallocate the NSWindow. + // But frames higher up in the stack might still expect the window to + // be alive, since the windowShouldClose: callback is technically only + // supposed to answer YES or NO. To ensure the window is still alive + // we put an autorelease in the closest pool (typically the runloop). + [[m_view.window retain] autorelease]; + return QWindowSystemInterface::handleCloseEvent<QWindowSystemInterface::SynchronousDelivery>(window()); } diff --git a/src/plugins/platforms/cocoa/qnswindow.mm b/src/plugins/platforms/cocoa/qnswindow.mm index 8967636fd2..1ce8ab5f53 100644 --- a/src/plugins/platforms/cocoa/qnswindow.mm +++ b/src/plugins/platforms/cocoa/qnswindow.mm @@ -104,7 +104,7 @@ static bool isMouseEvent(NSEvent *ev) // Unfortunately there's no NSWindowListOrderedBackToFront, // so we have to manually reverse the order using an array. - NSMutableArray<NSWindow *> *windows = [NSMutableArray<NSWindow *> new]; + NSMutableArray<NSWindow *> *windows = [[NSMutableArray<NSWindow *> new] autorelease]; [application enumerateWindowsWithOptions:NSWindowListOrderedFrontToBack usingBlock:^(NSWindow *window, BOOL *) { // For some reason AppKit will give us nil-windows, skip those diff --git a/src/plugins/platforms/ios/qiosviewcontroller.mm b/src/plugins/platforms/ios/qiosviewcontroller.mm index cd4af46ef7..e17da6cc06 100644 --- a/src/plugins/platforms/ios/qiosviewcontroller.mm +++ b/src/plugins/platforms/ios/qiosviewcontroller.mm @@ -246,6 +246,7 @@ @implementation QIOSViewController { BOOL m_updatingProperties; QMetaObject::Connection m_focusWindowChangeConnection; + QMetaObject::Connection m_appStateChangedConnection; } #ifndef Q_OS_TVOS @@ -274,7 +275,7 @@ }); QIOSApplicationState *applicationState = &QIOSIntegration::instance()->applicationState; - QObject::connect(applicationState, &QIOSApplicationState::applicationStateDidChange, + m_appStateChangedConnection = QObject::connect(applicationState, &QIOSApplicationState::applicationStateDidChange, [self](Qt::ApplicationState oldState, Qt::ApplicationState newState) { if (oldState == Qt::ApplicationSuspended && newState != Qt::ApplicationSuspended) { // We may have ignored an earlier layout because the application was suspended, @@ -294,6 +295,7 @@ - (void)dealloc { QObject::disconnect(m_focusWindowChangeConnection); + QObject::disconnect(m_appStateChangedConnection); [super dealloc]; } diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp index 682b8c19c0..12bdc9e6b7 100644 --- a/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp +++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp @@ -132,22 +132,19 @@ void setVariantString(const QString &value, VARIANT *variant) void rectToNativeUiaRect(const QRect &rect, const QWindow *w, UiaRect *uiaRect) { if (w && uiaRect) { - const qreal factor = QHighDpiScaling::factor(w); - uiaRect->left = qreal(rect.x()) * factor; - uiaRect->top = qreal(rect.y()) * factor; - uiaRect->width = qreal(rect.width()) * factor; - uiaRect->height = qreal(rect.height()) * factor; + QRectF r = QHighDpi::toNativePixels(QRectF(rect), w); + uiaRect->left =r.x(); + uiaRect->top = r.y(); + uiaRect->width = r.width(); + uiaRect->height = r.height(); } } // Scales a point from native coordinates, according to high dpi settings. void nativeUiaPointToPoint(const UiaPoint &uiaPoint, const QWindow *w, QPoint *point) { - if (w && point) { - const qreal factor = QHighDpiScaling::factor(w); - point->setX(int(std::lround(uiaPoint.x / factor))); - point->setY(int(std::lround(uiaPoint.y / factor))); - } + if (w && point) + *point = QHighDpi::fromNativePixels(QPoint(uiaPoint.x, uiaPoint.y), w); } // Maps an accessibility role ID to an UI Automation control type ID. diff --git a/src/plugins/platforms/xcb/qxcbclipboard.cpp b/src/plugins/platforms/xcb/qxcbclipboard.cpp index 0a4d675606..dabdfcb6c5 100644 --- a/src/plugins/platforms/xcb/qxcbclipboard.cpp +++ b/src/plugins/platforms/xcb/qxcbclipboard.cpp @@ -835,6 +835,8 @@ QByteArray QXcbClipboard::clipboardReadIncrementalProperty(xcb_window_t win, xcb alloc_error = buf.size() != nbytes+1; } + QElapsedTimer timer; + timer.start(); for (;;) { connection()->flush(); xcb_generic_event_t *ge = waitForClipboardEvent(win, XCB_PROPERTY_NOTIFY); @@ -870,9 +872,11 @@ QByteArray QXcbClipboard::clipboardReadIncrementalProperty(xcb_window_t win, xcb tmp_buf.resize(0); offset += length; } - } else { - break; } + + const auto elapsed = timer.elapsed(); + if (elapsed > clipboard_timeout) + break; } // timed out ... create a new requestor window, otherwise the requestor diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index 9abdae6a7c..34fbc0b10b 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -221,6 +221,12 @@ void QXcbConnection::printXcbEvent(const QLoggingCategory &log, const char *mess } #define CASE_PRINT_AND_RETURN(name) case name : PRINT_AND_RETURN(#name); +#define XI_PRINT_AND_RETURN(name) { \ + qCDebug(log, "%s | XInput Event(%s) | sequence: %d", message, name, sequence); \ + return; \ +} +#define XI_CASE_PRINT_AND_RETURN(name) case name : XI_PRINT_AND_RETURN(#name); + switch (response_type) { CASE_PRINT_AND_RETURN( XCB_KEY_PRESS ); CASE_PRINT_AND_RETURN( XCB_KEY_RELEASE ); @@ -255,7 +261,44 @@ void QXcbConnection::printXcbEvent(const QLoggingCategory &log, const char *mess CASE_PRINT_AND_RETURN( XCB_COLORMAP_NOTIFY ); CASE_PRINT_AND_RETURN( XCB_CLIENT_MESSAGE ); CASE_PRINT_AND_RETURN( XCB_MAPPING_NOTIFY ); - CASE_PRINT_AND_RETURN( XCB_GE_GENERIC ); + case XCB_GE_GENERIC: { + if (hasXInput2() && isXIEvent(event)) { + auto *xiDeviceEvent = reinterpret_cast<const xcb_input_button_press_event_t*>(event); // qt_xcb_input_device_event_t + switch (xiDeviceEvent->event_type) { + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_KEY_PRESS ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_KEY_RELEASE ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_BUTTON_PRESS ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_BUTTON_RELEASE ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_MOTION ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_ENTER ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_LEAVE ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_FOCUS_IN ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_FOCUS_OUT ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_HIERARCHY ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_PROPERTY ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_RAW_KEY_PRESS ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_RAW_KEY_RELEASE ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_RAW_BUTTON_PRESS ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_RAW_BUTTON_RELEASE ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_RAW_MOTION ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_TOUCH_BEGIN ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_TOUCH_UPDATE ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_TOUCH_END ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_TOUCH_OWNERSHIP ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_RAW_TOUCH_BEGIN ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_RAW_TOUCH_UPDATE ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_RAW_TOUCH_END ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_BARRIER_HIT ); + XI_CASE_PRINT_AND_RETURN( XCB_INPUT_BARRIER_LEAVE ); + default: + qCDebug(log, "%s | XInput Event(other type) | sequence: %d", message, sequence); + return; + } + } else { + qCDebug(log, "%s | %s(%d) | sequence: %d", message, "XCB_GE_GENERIC", response_type, sequence); + return; + } + } } // XFixes if (isXFixesType(response_type, XCB_XFIXES_SELECTION_NOTIFY)) diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index 050182537d..da179591e9 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -564,6 +564,11 @@ void QXcbWindow::setGeometry(const QRect &rect) { QPlatformWindow::setGeometry(rect); + if (shouldDeferTask(Task::SetGeometry)) { + m_deferredGeometry = rect; + return; + } + propagateSizeHints(); QXcbScreen *currentScreen = xcbScreen(); @@ -688,6 +693,9 @@ void QXcbWindow::setVisible(bool visible) void QXcbWindow::show() { + if (shouldDeferTask(Task::Map)) + return; + if (window()->isTopLevel()) { // update WM_NORMAL_HINTS @@ -738,6 +746,10 @@ void QXcbWindow::show() void QXcbWindow::hide() { + if (shouldDeferTask(Task::Unmap)) + return; + + m_wmStateValid = false; xcb_unmap_window(xcb_connection(), m_window); // send synthetic UnmapNotify event according to icccm 4.1.4 @@ -897,6 +909,9 @@ QXcbWindow::NetWmStates QXcbWindow::netWmStates() void QXcbWindow::setWindowFlags(Qt::WindowFlags flags) { + if (shouldDeferTask(Task::SetWindowFlags)) + return; + Qt::WindowType type = static_cast<Qt::WindowType>(int(flags & Qt::WindowType_Mask)); if (type == Qt::ToolTip) @@ -926,6 +941,8 @@ void QXcbWindow::setWindowFlags(Qt::WindowFlags flags) setTransparentForMouseEvents(flags & Qt::WindowTransparentForInput); updateDoesNotAcceptFocus(flags & Qt::WindowDoesNotAcceptFocus); + + m_isWmManagedWindow = !(flags & Qt::X11BypassWindowManagerHint); } void QXcbWindow::setMotifWmHints(Qt::WindowFlags flags) @@ -1125,6 +1142,9 @@ void QXcbWindow::setWindowState(Qt::WindowStates state) if (state == m_windowState) return; + if (shouldDeferTask(Task::SetWindowState)) + return; + // unset old state if (m_windowState & Qt::WindowMinimized) xcb_map_window(xcb_connection(), m_window); @@ -1874,6 +1894,10 @@ void QXcbWindow::handleUnmapNotifyEvent(const xcb_unmap_notify_event_t *event) if (event->window == m_window) { m_mapped = false; QWindowSystemInterface::handleExposeEvent(window(), QRegion()); + if (!m_isWmManagedWindow) { + m_wmStateValid = true; + handleDeferredTasks(); + } } } @@ -2188,30 +2212,98 @@ void QXcbWindow::handleLeaveNotifyEvent(const xcb_leave_notify_event_t *event) handleLeaveNotifyEvent(event->root_x, event->root_y, event->mode, event->detail, event->time); } +bool QXcbWindow::shouldDeferTask(Task task) +{ + if (m_wmStateValid) + return false; + + m_deferredTasks.append(task); + return true; +} + +void QXcbWindow::handleDeferredTasks() +{ + Q_ASSERT(m_wmStateValid == true); + if (m_deferredTasks.isEmpty()) + return; + + bool map = false; + bool unmap = false; + + QVector<Task> tasks; + for (auto taskIt = m_deferredTasks.rbegin(); taskIt != m_deferredTasks.rend(); ++taskIt) { + if (!tasks.contains(*taskIt)) + tasks.prepend(*taskIt); + } + + for (Task task : tasks) { + switch (task) { + case Task::Map: + map = true; + unmap = false; + break; + case Task::Unmap: + unmap = true; + map = false; + break; + case Task::SetGeometry: + setGeometry(m_deferredGeometry); + break; + case Task::SetWindowFlags: + setWindowFlags(window()->flags()); + break; + case Task::SetWindowState: + setWindowState(window()->windowState()); + break; + } + } + m_deferredTasks.clear(); + + if (map) { + Q_ASSERT(unmap == false); + show(); + } + if (unmap) { + Q_ASSERT(map == false); + hide(); + } +} + void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *event) { connection()->setTime(event->time); - const bool propertyDeleted = event->state == XCB_PROPERTY_DELETE; - - if (event->atom == atom(QXcbAtom::_NET_WM_STATE) || event->atom == atom(QXcbAtom::WM_STATE)) { - if (propertyDeleted) + const bool wmStateChanged = event->atom == atom(QXcbAtom::WM_STATE); + const bool netWmStateChanged = event->atom == atom(QXcbAtom::_NET_WM_STATE); + if (netWmStateChanged || wmStateChanged) { + if (wmStateChanged && !m_wmStateValid && m_isWmManagedWindow) { + // ICCCM 4.1.4 + // Clients that want to re-use a client window (e.g. by mapping it again) + // after withdrawing it must wait for the withdrawal to be complete before + // proceeding. The preferred method for doing this is for clients to wait for + // a window manager to update or remove the WM_STATE property. + m_wmStateValid = true; + handleDeferredTasks(); + } + if (event->state == XCB_PROPERTY_DELETE) return; - Qt::WindowStates newState = Qt::WindowNoState; - - if (event->atom == atom(QXcbAtom::WM_STATE)) { // WM_STATE: Quick check for 'Minimize'. + if (wmStateChanged) { auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), 0, m_window, atom(QXcbAtom::WM_STATE), XCB_ATOM_ANY, 0, 1024); if (reply && reply->format == 32 && reply->type == atom(QXcbAtom::WM_STATE)) { - const quint32 *data = (const quint32 *)xcb_get_property_value(reply.get()); - if (reply->length != 0) - m_minimized = (data[0] == XCB_ICCCM_WM_STATE_ICONIC - || (data[0] == XCB_ICCCM_WM_STATE_WITHDRAWN && m_minimized)); + auto data = static_cast<const quint32 *>(xcb_get_property_value(reply.get())); + if (reply->length != 0) { + const bool changedToWithdrawn = data[0] == XCB_ICCCM_WM_STATE_WITHDRAWN; + const bool changedToIconic = data[0] == XCB_ICCCM_WM_STATE_ICONIC; + m_minimized = changedToIconic || (changedToWithdrawn && m_minimized); + } } } + // _NET_WM_STATE handling + Qt::WindowStates newState = Qt::WindowNoState; const NetWmStates states = netWmStates(); // _NET_WM_STATE_HIDDEN should be set by the Window Manager to indicate that a window would // not be visible on the screen if its desktop/viewport were active and its coordinates were @@ -2233,7 +2325,6 @@ void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *ev if ((m_windowState & Qt::WindowMinimized) && connection()->mouseGrabber() == this) connection()->setMouseGrabber(nullptr); } - return; } else if (event->atom == atom(QXcbAtom::_NET_FRAME_EXTENTS)) { m_dirtyFrameMargins = true; } diff --git a/src/plugins/platforms/xcb/qxcbwindow.h b/src/plugins/platforms/xcb/qxcbwindow.h index 6f5c1f5ed9..55af9279b1 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.h +++ b/src/plugins/platforms/xcb/qxcbwindow.h @@ -74,6 +74,14 @@ public: Q_DECLARE_FLAGS(NetWmStates, NetWmState) + enum Task { + Map, + Unmap, + SetGeometry, + SetWindowFlags, + SetWindowState + }; + QXcbWindow(QWindow *window); ~QXcbWindow(); @@ -143,6 +151,9 @@ public: QXcbWindow *toWindow() override; + bool shouldDeferTask(Task task); + void handleDeferredTasks(); + void handleMouseEvent(xcb_timestamp_t time, const QPoint &local, const QPoint &global, Qt::KeyboardModifiers modifiers, QEvent::Type type, Qt::MouseEventSource source); @@ -281,6 +292,11 @@ protected: int m_swapInterval = -1; qreal m_sizeHintsScaleFactor = 1.0; + + bool m_wmStateValid = true; + QVector<Task> m_deferredTasks; + bool m_isWmManagedWindow = true; + QRect m_deferredGeometry; }; class QXcbForeignWindow : public QXcbWindow |