summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms')
-rw-r--r--src/plugins/platforms/android/extract-dummy.cpp5
-rw-r--r--src/plugins/platforms/android/extract.cpp15
-rw-r--r--src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp17
-rw-r--r--src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm2
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenu.mm23
-rw-r--r--src/plugins/platforms/cocoa/qcocoascreen.h5
-rw-r--r--src/plugins/platforms/cocoa/qcocoascreen.mm115
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindow.mm27
-rw-r--r--src/plugins/platforms/cocoa/qnswindow.mm2
-rw-r--r--src/plugins/platforms/ios/qiosviewcontroller.mm4
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp17
-rw-r--r--src/plugins/platforms/xcb/qxcbclipboard.cpp8
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.cpp45
-rw-r--r--src/plugins/platforms/xcb/qxcbwindow.cpp115
-rw-r--r--src/plugins/platforms/xcb/qxcbwindow.h16
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