diff options
Diffstat (limited to 'src/plugins/platforms')
55 files changed, 931 insertions, 376 deletions
diff --git a/src/plugins/platforms/android/androidjniaccessibility.cpp b/src/plugins/platforms/android/androidjniaccessibility.cpp index 309e41bfd6..6f1178e041 100644 --- a/src/plugins/platforms/android/androidjniaccessibility.cpp +++ b/src/plugins/platforms/android/androidjniaccessibility.cpp @@ -72,20 +72,28 @@ namespace QtAndroidAccessibility static jmethodID m_setTextSelectionMethodID = 0; static jmethodID m_setVisibleToUserMethodID = 0; + static bool m_accessibilityActivated = false; + void initialize() { QJNIObjectPrivate::callStaticMethod<void>(QtAndroid::applicationClass(), "initializeAccessibility"); } + bool isActive() + { + return m_accessibilityActivated; + } + static void setActive(JNIEnv */*env*/, jobject /*thiz*/, jboolean active) { QMutexLocker lock(QtAndroid::platformInterfaceMutex()); QAndroidPlatformIntegration *platformIntegration = QtAndroid::androidPlatformIntegration(); + m_accessibilityActivated = active; if (platformIntegration) platformIntegration->accessibility()->setActive(active); else - __android_log_print(ANDROID_LOG_WARN, m_qtTag, "Could not activate platform accessibility."); + __android_log_print(ANDROID_LOG_WARN, m_qtTag, "Could not (yet) activate platform accessibility."); } QAccessibleInterface *interfaceFromId(jint objectId) diff --git a/src/plugins/platforms/android/androidjniaccessibility.h b/src/plugins/platforms/android/androidjniaccessibility.h index f393ce0b08..508ed4462b 100644 --- a/src/plugins/platforms/android/androidjniaccessibility.h +++ b/src/plugins/platforms/android/androidjniaccessibility.h @@ -47,6 +47,7 @@ QT_BEGIN_NAMESPACE namespace QtAndroidAccessibility { void initialize(); + bool isActive(); bool registerNatives(JNIEnv *env); } diff --git a/src/plugins/platforms/android/androidjniinput.cpp b/src/plugins/platforms/android/androidjniinput.cpp index 56885f2e23..6ba1aa5e24 100644 --- a/src/plugins/platforms/android/androidjniinput.cpp +++ b/src/plugins/platforms/android/androidjniinput.cpp @@ -519,7 +519,7 @@ namespace QtAndroidInput return Qt::Key_Search; case 0x00000055: // KEYCODE_MEDIA_PLAY_PAUSE - return Qt::Key_MediaPlay; + return Qt::Key_MediaTogglePlayPause; case 0x00000056: // KEYCODE_MEDIA_STOP return Qt::Key_MediaStop; diff --git a/src/plugins/platforms/android/androidjnimain.cpp b/src/plugins/platforms/android/androidjnimain.cpp index 74edfd8356..9ce3353040 100644 --- a/src/plugins/platforms/android/androidjnimain.cpp +++ b/src/plugins/platforms/android/androidjnimain.cpp @@ -505,7 +505,7 @@ static void waitForServiceSetup(JNIEnv *env, jclass /*clazz*/) QtAndroidPrivate::waitForServiceSetup(); } -static jboolean startQtApplication(JNIEnv */*env*/, jclass /*clazz*/) +static void startQtApplication(JNIEnv */*env*/, jclass /*clazz*/) { { JNIEnv* env = nullptr; @@ -540,7 +540,8 @@ static jboolean startQtApplication(JNIEnv */*env*/, jclass /*clazz*/) sem_destroy(&m_exitSemaphore); // We must call exit() to ensure that all global objects will be destructed - exit(ret); + if (!qEnvironmentVariableIsSet("QT_ANDROID_NO_EXIT_CALL")) + exit(ret); } static void quitQtCoreApplication(JNIEnv *env, jclass /*clazz*/) diff --git a/src/plugins/platforms/android/qandroidplatformintegration.cpp b/src/plugins/platforms/android/qandroidplatformintegration.cpp index e0c437be27..ff59ca6bde 100644 --- a/src/plugins/platforms/android/qandroidplatformintegration.cpp +++ b/src/plugins/platforms/android/qandroidplatformintegration.cpp @@ -52,6 +52,7 @@ #include <qpa/qplatformoffscreensurface.h> #include "androidjnimain.h" +#include "androidjniaccessibility.h" #include "qabstracteventdispatcher.h" #include "qandroideventdispatcher.h" #include "qandroidplatformbackingstore.h" @@ -149,6 +150,12 @@ void QAndroidPlatformNativeInterface::customEvent(QEvent *event) QMutexLocker lock(QtAndroid::platformInterfaceMutex()); QAndroidPlatformIntegration *api = static_cast<QAndroidPlatformIntegration *>(QGuiApplicationPrivate::platformIntegration()); QtAndroid::setAndroidPlatformIntegration(api); + +#ifndef QT_NO_ACCESSIBILITY + // Android accessibility activation event might have been already received + api->accessibility()->setActive(QtAndroidAccessibility::isActive()); +#endif // QT_NO_ACCESSIBILITY + api->flushPendingUpdates(); } diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.h b/src/plugins/platforms/cocoa/qcocoabackingstore.h index a874936ce6..3d9dfd8359 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.h +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.h @@ -47,6 +47,8 @@ #include <QScopedPointer> #include "qiosurfacegraphicsbuffer.h" +#include <unordered_map> + QT_BEGIN_NAMESPACE class QCocoaBackingStore : public QRasterBackingStore @@ -54,8 +56,6 @@ class QCocoaBackingStore : public QRasterBackingStore protected: QCocoaBackingStore(QWindow *window); QCFType<CGColorSpaceRef> colorSpace() const; - QMacNotificationObserver m_backingPropertiesObserver; - virtual void backingPropertiesChanged() = 0; }; class QNSWindowBackingStore : public QCocoaBackingStore @@ -71,11 +71,11 @@ private: bool windowHasUnifiedToolbar() const; QImage::Format format() const override; void redrawRoundedBottomCorners(CGRect) const; - void backingPropertiesChanged() override; }; -class QCALayerBackingStore : public QCocoaBackingStore +class QCALayerBackingStore : public QObject, public QCocoaBackingStore { + Q_OBJECT public: QCALayerBackingStore(QWindow *window); ~QCALayerBackingStore(); @@ -118,9 +118,15 @@ private: bool recreateBackBufferIfNeeded(); bool prepareForFlush(); - void backingPropertiesChanged() override; + void backingPropertiesChanged(); + QMacNotificationObserver m_backingPropertiesObserver; std::list<std::unique_ptr<GraphicsBuffer>> m_buffers; + + void flushSubWindow(QWindow *window); + std::unordered_map<QWindow*, std::unique_ptr<QCALayerBackingStore>> m_subWindowBackingstores; + void windowDestroyed(QObject *object); + bool m_clearSurfaceOnPaint = true; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm index 2343f16b9f..6ea6714649 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.mm +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm @@ -51,17 +51,6 @@ QT_BEGIN_NAMESPACE QCocoaBackingStore::QCocoaBackingStore(QWindow *window) : QRasterBackingStore(window) { - // Ideally this would be plumbed from the platform layer to QtGui, and - // the QBackingStore would be recreated, but we don't have that code yet, - // so at least make sure we invalidate our backingstore when the backing - // properties (color space e.g.) are changed. - NSView *view = static_cast<QCocoaWindow *>(window->handle())->view(); - m_backingPropertiesObserver = QMacNotificationObserver(view.window, - NSWindowDidChangeBackingPropertiesNotification, [this]() { - qCDebug(lcQpaBackingStore) << "Backing properties for" - << this->window() << "did change"; - backingPropertiesChanged(); - }); } QCFType<CGColorSpaceRef> QCocoaBackingStore::colorSpace() const @@ -341,11 +330,6 @@ void QNSWindowBackingStore::redrawRoundedBottomCorners(CGRect windowRect) const #endif } -void QNSWindowBackingStore::backingPropertiesChanged() -{ - m_image = QImage(); -} - // ---------------------------------------------------------------------------- // https://stackoverflow.com/a/52722575/2761869 @@ -365,6 +349,26 @@ QCALayerBackingStore::QCALayerBackingStore(QWindow *window) { qCDebug(lcQpaBackingStore) << "Creating QCALayerBackingStore for" << window; m_buffers.resize(1); + + // Ideally this would be plumbed from the platform layer to QtGui, and + // the QBackingStore would be recreated, but we don't have that code yet, + // so at least make sure we update our backingstore when the backing + // properties (color space e.g.) are changed. + NSView *view = static_cast<QCocoaWindow *>(window->handle())->view(); + m_backingPropertiesObserver = QMacNotificationObserver(view.window, + NSWindowDidChangeBackingPropertiesNotification, [this]() { + if (!this->window()->handle()) { + // The platform window has been destroyed, but the backingstore + // is still alive, as that's tied to a QWindow. The original + // NSWindow we were observing is also likely gone. FIXME: + // We should listen for surface events from the QWindow and + // remove and re-attach our observer based on those. + return; + } + qCDebug(lcQpaBackingStore) << "Backing properties for" + << this->window() << "did change"; + backingPropertiesChanged(); + }); } QCALayerBackingStore::~QCALayerBackingStore() @@ -398,7 +402,7 @@ void QCALayerBackingStore::beginPaint(const QRegion ®ion) // Although undocumented, QBackingStore::beginPaint expects the painted region // to be cleared before use if the window has a surface format with an alpha. // Fresh IOSurfaces are already cleared, so we don't need to clear those. - if (!bufferWasRecreated && window()->format().hasAlpha()) { + if (m_clearSurfaceOnPaint && !bufferWasRecreated && window()->format().hasAlpha()) { qCDebug(lcQpaBackingStore) << "Clearing" << region << "before use"; QPainter painter(m_buffers.back()->asImage()); painter.setCompositionMode(QPainter::CompositionMode_Source); @@ -527,9 +531,13 @@ void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion ®ion, if (!prepareForFlush()) return; + if (flushedWindow != window()) { + flushSubWindow(flushedWindow); + return; + } + QMacAutoReleasePool pool; - NSView *backingStoreView = static_cast<QCocoaWindow *>(window()->handle())->view(); NSView *flushedView = static_cast<QCocoaWindow *>(flushedWindow->handle())->view(); // If the backingstore is just flushed, without being painted to first, then we may @@ -564,7 +572,7 @@ void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion ®ion, // are committed as part of a display-cycle instead of on the next runloop pass. This // means CA won't try to throttle us if we flush too fast, and we'll coalesce our flush // with other pending view and layer updates. - backingStoreView.window.viewsNeedDisplay = YES; + flushedView.window.viewsNeedDisplay = YES; if (window()->format().swapBehavior() == QSurfaceFormat::SingleBuffer) { // The private API [CALayer reloadValueForKeyPath:@"contents"] would be preferable, @@ -572,27 +580,10 @@ void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion ®ion, flushedView.layer.contents = nil; } - if (flushedView == backingStoreView) { - qCInfo(lcQpaBackingStore) << "Flushing" << backBufferSurface - << "to" << flushedView.layer << "of" << flushedView; - flushedView.layer.contents = backBufferSurface; - } else { - auto subviewRect = [flushedView convertRect:flushedView.bounds toView:backingStoreView]; - auto scale = flushedView.layer.contentsScale; - subviewRect = CGRectApplyAffineTransform(subviewRect, CGAffineTransformMakeScale(scale, scale)); - - // We make a copy of the image data up front, which means we don't - // need to mark the IOSurface as being in use. FIXME: Investigate - // if there's a cheaper way to get sub-image data to a layer. - m_buffers.back()->lock(QPlatformGraphicsBuffer::SWReadAccess); - QImage subImage = m_buffers.back()->asImage()->copy(QRectF::fromCGRect(subviewRect).toRect()); - m_buffers.back()->unlock(); + qCInfo(lcQpaBackingStore) << "Flushing" << backBufferSurface + << "to" << flushedView.layer << "of" << flushedView; - qCInfo(lcQpaBackingStore) << "Flushing" << subImage - << "to" << flushedView.layer << "of subview" << flushedView; - QCFType<CGImageRef> cgImage = subImage.toCGImage(); - flushedView.layer.contents = (__bridge id)static_cast<CGImageRef>(cgImage); - } + flushedView.layer.contents = backBufferSurface; // Since we may receive multiple flushes before a new frame is started, we do not // swap any buffers just yet. Instead we check in the next beginPaint if the layer's @@ -604,6 +595,53 @@ void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion ®ion, // the window server. } +void QCALayerBackingStore::flushSubWindow(QWindow *subWindow) +{ + qCInfo(lcQpaBackingStore) << "Flushing sub-window" << subWindow + << "via its own backingstore"; + + auto &subWindowBackingStore = m_subWindowBackingstores[subWindow]; + if (!subWindowBackingStore) { + subWindowBackingStore.reset(new QCALayerBackingStore(subWindow)); + QObject::connect(subWindow, &QObject::destroyed, this, &QCALayerBackingStore::windowDestroyed); + subWindowBackingStore->m_clearSurfaceOnPaint = false; + } + + auto subWindowSize = subWindow->size(); + static const auto kNoStaticContents = QRegion(); + subWindowBackingStore->resize(subWindowSize, kNoStaticContents); + + auto subWindowLocalRect = QRect(QPoint(), subWindowSize); + subWindowBackingStore->beginPaint(subWindowLocalRect); + + QPainter painter(subWindowBackingStore->m_buffers.back()->asImage()); + painter.setCompositionMode(QPainter::CompositionMode_Source); + + NSView *backingStoreView = static_cast<QCocoaWindow *>(window()->handle())->view(); + NSView *flushedView = static_cast<QCocoaWindow *>(subWindow->handle())->view(); + auto subviewRect = [flushedView convertRect:flushedView.bounds toView:backingStoreView]; + auto scale = flushedView.layer.contentsScale; + subviewRect = CGRectApplyAffineTransform(subviewRect, CGAffineTransformMakeScale(scale, scale)); + + m_buffers.back()->lock(QPlatformGraphicsBuffer::SWReadAccess); + const QImage *backingStoreImage = m_buffers.back()->asImage(); + painter.drawImage(subWindowLocalRect, *backingStoreImage, QRectF::fromCGRect(subviewRect)); + m_buffers.back()->unlock(); + + painter.end(); + subWindowBackingStore->endPaint(); + subWindowBackingStore->flush(subWindow, subWindowLocalRect, QPoint()); + + qCInfo(lcQpaBackingStore) << "Done flushing sub-window" << subWindow; +} + +void QCALayerBackingStore::windowDestroyed(QObject *object) +{ + auto *window = static_cast<QWindow*>(object); + qCInfo(lcQpaBackingStore) << "Removing backingstore for sub-window" << window; + m_subWindowBackingstores.erase(window); +} + #ifndef QT_NO_OPENGL void QCALayerBackingStore::composeAndFlush(QWindow *window, const QRegion ®ion, const QPoint &offset, QPlatformTextureList *textures, bool translucentBackground) @@ -632,8 +670,11 @@ QImage QCALayerBackingStore::toImage() const void QCALayerBackingStore::backingPropertiesChanged() { - m_buffers.clear(); - m_buffers.resize(1); + qCDebug(lcQpaBackingStore) << "Updating color space of existing buffers"; + for (auto &buffer : m_buffers) { + if (buffer) + buffer->setColorSpace(colorSpace()); + } } QPlatformGraphicsBuffer *QCALayerBackingStore::graphicsBuffer() const @@ -710,10 +751,11 @@ bool QCALayerBackingStore::prepareForFlush() QCALayerBackingStore::GraphicsBuffer::GraphicsBuffer(const QSize &size, qreal devicePixelRatio, const QPixelFormat &format, QCFType<CGColorSpaceRef> colorSpace) - : QIOSurfaceGraphicsBuffer(size, format, colorSpace) + : QIOSurfaceGraphicsBuffer(size, format) , dirtyRegion(0, 0, size.width() / devicePixelRatio, size.height() / devicePixelRatio) , m_devicePixelRatio(devicePixelRatio) { + setColorSpace(colorSpace); } QImage *QCALayerBackingStore::GraphicsBuffer::asImage() @@ -733,4 +775,6 @@ QImage *QCALayerBackingStore::GraphicsBuffer::asImage() return &m_image; } +#include "moc_qcocoabackingstore.cpp" + QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoaglcontext.mm b/src/plugins/platforms/cocoa/qcocoaglcontext.mm index d91b2cabaf..c8b5365a75 100644 --- a/src/plugins/platforms/cocoa/qcocoaglcontext.mm +++ b/src/plugins/platforms/cocoa/qcocoaglcontext.mm @@ -357,6 +357,8 @@ QCocoaGLContext::~QCocoaGLContext() bool QCocoaGLContext::makeCurrent(QPlatformSurface *surface) { + QMacAutoReleasePool pool; + qCDebug(lcQpaOpenGLContext) << "Making" << m_context << "current" << "in" << QThread::currentThread() << "for" << surface; @@ -479,6 +481,8 @@ void QCocoaGLContext::update() void QCocoaGLContext::swapBuffers(QPlatformSurface *surface) { + QMacAutoReleasePool pool; + qCDebug(lcQpaOpenGLContext) << "Swapping" << m_context << "in" << QThread::currentThread() << "to" << surface; @@ -491,12 +495,29 @@ void QCocoaGLContext::swapBuffers(QPlatformSurface *surface) return; } + if (m_context.view.layer) { + // Flushing an NSOpenGLContext will hit the screen immediately, ignoring + // any Core Animation transactions in place. This may result in major + // visual artifacts if the flush happens out of sync with the size + // of the layer, view, and window reflected by other parts of the UI, + // e.g. if the application flushes in the resize event or a timer during + // window resizing, instead of in the expose event. + auto *cocoaWindow = static_cast<QCocoaWindow *>(surface); + if (cocoaWindow->geometry().size() != cocoaWindow->m_exposedRect.size()) { + qCInfo(lcQpaOpenGLContext) << "Window exposed size does not match geometry (yet)." + << "Skipping flush to avoid visual artifacts."; + return; + } + } + QMutexLocker locker(&s_reentrancyMutex); [m_context flushBuffer]; } void QCocoaGLContext::doneCurrent() { + QMacAutoReleasePool pool; + qCDebug(lcQpaOpenGLContext) << "Clearing current context" << [NSOpenGLContext currentContext] << "in" << QThread::currentThread(); diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.h b/src/plugins/platforms/cocoa/qcocoahelpers.h index 69aa7937b6..514d3ac550 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.h +++ b/src/plugins/platforms/cocoa/qcocoahelpers.h @@ -55,6 +55,7 @@ #include <QtCore/qoperatingsystemversion.h> #include <QtGui/qpalette.h> #include <QtGui/qscreen.h> +#include <qpa/qplatformdialoghelper.h> #include <objc/runtime.h> #include <objc/message.h> @@ -178,32 +179,6 @@ T qt_mac_resolveOption(const T &fallback, QWindow *window, const QByteArray &pro // ------------------------------------------------------------------------- -#if !defined(Q_PROCESSOR_X86_64) -#error "32-bit builds are not supported" -#endif - -class QMacVersion -{ -public: - enum VersionTarget { - ApplicationBinary, - QtLibraries - }; - - static QOperatingSystemVersion buildSDK(VersionTarget target = ApplicationBinary); - static QOperatingSystemVersion deploymentTarget(VersionTarget target = ApplicationBinary); - static QOperatingSystemVersion currentRuntime(); - -private: - QMacVersion() = default; - using VersionTuple = QPair<QOperatingSystemVersion, QOperatingSystemVersion>; - static VersionTuple versionsForImage(const mach_header *machHeader); - static VersionTuple applicationVersion(); - static VersionTuple libraryVersion(); -}; - -// ------------------------------------------------------------------------- - QT_END_NAMESPACE // @compatibility_alias doesn't work with protocols @@ -225,7 +200,7 @@ QT_END_NAMESPACE - (instancetype)initWithPanelDelegate:(id<QNSPanelDelegate>)panelDelegate; - (void)dealloc; -- (NSButton *)createButtonWithTitle:(const char *)title; +- (NSButton *)createButtonWithTitle:(QPlatformDialogHelper::StandardButton)type; - (void)layout; @end @@ -254,14 +229,16 @@ template <typename T> struct objc_msgsend_requires_stret { static const bool value = #if defined(Q_PROCESSOR_X86) + #define PLATFORM_USES_SEND_SUPER_STRET 1 // Any return value larger than two registers on i386/x86_64 sizeof(T) > sizeof(void*) * 2; #elif defined(Q_PROCESSOR_ARM_32) + #define PLATFORM_USES_SEND_SUPER_STRET 1 // Any return value larger than a single register on arm - sizeof(T) > sizeof(void*); + sizeof(T) > sizeof(void*); #elif defined(Q_PROCESSOR_ARM_64) - // Stret not used on arm64 - false; + #define PLATFORM_USES_SEND_SUPER_STRET 0 + false; // Stret not used on arm64 #endif }; @@ -281,6 +258,7 @@ ReturnType qt_msgSendSuper(id receiver, SEL selector, Args... args) return superFn(&sup, selector, args...); } +#if PLATFORM_USES_SEND_SUPER_STRET template <typename ReturnType, typename... Args> ReturnType qt_msgSendSuper_stret(id receiver, SEL selector, Args... args) { @@ -295,6 +273,7 @@ ReturnType qt_msgSendSuper_stret(id receiver, SEL selector, Args... args) superStretFn(&ret, &sup, selector, args...); return ret; } +#endif template<typename... Args> class QSendSuperHelper { @@ -335,11 +314,13 @@ private: return qt_msgSendSuper<ReturnType>(m_receiver, m_selector, std::get<Is>(args)...); } +#if PLATFORM_USES_SEND_SUPER_STRET template <typename ReturnType, int... Is> if_requires_stret<ReturnType, true> msgSendSuper(std::tuple<Args...>& args, QtPrivate::IndexesList<Is...>) { return qt_msgSendSuper_stret<ReturnType>(m_receiver, m_selector, std::get<Is>(args)...); } +#endif template <typename ReturnType> ReturnType msgSendSuper(std::tuple<Args...>& args) diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm index d36a7f6d09..2b1e512cf3 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.mm +++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm @@ -55,9 +55,6 @@ #include <algorithm> -#include <mach-o/dyld.h> -#include <dlfcn.h> - QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcQpaWindow, "qt.qpa.window"); @@ -370,92 +367,6 @@ QString qt_mac_removeAmpersandEscapes(QString s) return QPlatformTheme::removeMnemonics(s).trimmed(); } -// ------------------------------------------------------------------------- - -#if !defined(Q_PROCESSOR_X86_64) -#error "32-bit builds are not supported" -#endif - -QOperatingSystemVersion QMacVersion::buildSDK(VersionTarget target) -{ - switch (target) { - case ApplicationBinary: return applicationVersion().second; - case QtLibraries: return libraryVersion().second; - } - Q_UNREACHABLE(); -} - -QOperatingSystemVersion QMacVersion::deploymentTarget(VersionTarget target) -{ - switch (target) { - case ApplicationBinary: return applicationVersion().first; - case QtLibraries: return libraryVersion().first; - } - Q_UNREACHABLE(); -} - -QOperatingSystemVersion QMacVersion::currentRuntime() -{ - return QOperatingSystemVersion::current(); -} - -QMacVersion::VersionTuple QMacVersion::versionsForImage(const mach_header *machHeader) -{ - static auto makeVersionTuple = [](uint32_t dt, uint32_t sdk) { - return qMakePair( - QOperatingSystemVersion(QOperatingSystemVersion::MacOS, - dt >> 16 & 0xffff, dt >> 8 & 0xff, dt & 0xff), - QOperatingSystemVersion(QOperatingSystemVersion::MacOS, - sdk >> 16 & 0xffff, sdk >> 8 & 0xff, sdk & 0xff) - ); - }; - - auto commandCursor = uintptr_t(machHeader) + sizeof(mach_header_64); - for (uint32_t i = 0; i < machHeader->ncmds; ++i) { - load_command *loadCommand = reinterpret_cast<load_command *>(commandCursor); - if (loadCommand->cmd == LC_VERSION_MIN_MACOSX) { - auto versionCommand = reinterpret_cast<version_min_command *>(loadCommand); - return makeVersionTuple(versionCommand->version, versionCommand->sdk); -#if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_13) - } else if (loadCommand->cmd == LC_BUILD_VERSION) { - auto versionCommand = reinterpret_cast<build_version_command *>(loadCommand); - return makeVersionTuple(versionCommand->minos, versionCommand->sdk); -#endif - } - commandCursor += loadCommand->cmdsize; - } - Q_ASSERT_X(false, "QCocoaIntegration", "Could not find any version load command"); - Q_UNREACHABLE(); -} - -QMacVersion::VersionTuple QMacVersion::applicationVersion() -{ - static VersionTuple version = []() { - const mach_header *executableHeader = nullptr; - for (uint32_t i = 0; i < _dyld_image_count(); ++i) { - auto header = _dyld_get_image_header(i); - if (header->filetype == MH_EXECUTE) { - executableHeader = header; - break; - } - } - Q_ASSERT_X(executableHeader, "QCocoaIntegration", "Failed to resolve Mach-O header of executable"); - return versionsForImage(executableHeader); - }(); - return version; -} - -QMacVersion::VersionTuple QMacVersion::libraryVersion() -{ - static VersionTuple version = []() { - Dl_info cocoaPluginImage; - dladdr((const void *)&QMacVersion::libraryVersion, &cocoaPluginImage); - Q_ASSERT_X(cocoaPluginImage.dli_fbase, "QCocoaIntegration", "Failed to resolve Mach-O header of Cocoa plugin"); - return versionsForImage(static_cast<mach_header*>(cocoaPluginImage.dli_fbase)); - }(); - return version; -} - QT_END_NAMESPACE /*! \internal @@ -483,11 +394,10 @@ QT_END_NAMESPACE { if ((self = [super initWithFrame:NSZeroRect])) { // create OK and Cancel buttons and add these as subviews - _okButton = [self createButtonWithTitle:"&OK"]; + _okButton = [self createButtonWithTitle:QPlatformDialogHelper::Ok]; _okButton.action = @selector(onOkClicked); _okButton.target = panelDelegate; - - _cancelButton = [self createButtonWithTitle:"Cancel"]; + _cancelButton = [self createButtonWithTitle:QPlatformDialogHelper::Cancel]; _cancelButton.action = @selector(onCancelClicked); _cancelButton.target = panelDelegate; @@ -511,12 +421,13 @@ QT_END_NAMESPACE [super dealloc]; } -- (NSButton *)createButtonWithTitle:(const char *)title +- (NSButton *)createButtonWithTitle:(QPlatformDialogHelper::StandardButton)type { NSButton *button = [[NSButton alloc] initWithFrame:NSZeroRect]; button.buttonType = NSMomentaryLightButton; button.bezelStyle = NSRoundedBezelStyle; - const QString &cleanTitle = QPlatformTheme::removeMnemonics(QCoreApplication::translate("QDialogButtonBox", title)); + const QString &cleanTitle = + QPlatformTheme::removeMnemonics(QGuiApplicationPrivate::platformTheme()->standardButtonText(type)); // FIXME: Not obvious, from Cocoa's documentation, that QString::toNSString() makes a deep copy button.title = (NSString *)cleanTitle.toCFString(); ((NSButtonCell *)button.cell).font = diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm index 597cfa8318..ed26d3afea 100644 --- a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm +++ b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm @@ -226,6 +226,7 @@ void QCocoaSystemTrayIcon::updateIcon(const QIcon &icon) r.moveCenter(fullHeightPixmap.rect().center()); p.drawPixmap(r, pixmap); } + fullHeightPixmap.setDevicePixelRatio(devicePixelRatio); NSImage *nsimage = static_cast<NSImage *>(qt_mac_create_nsimage(fullHeightPixmap)); [nsimage setTemplate:icon.isMask()]; diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index a3120f4ccc..2a23bf286f 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -500,7 +500,10 @@ 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. + styleMask |= NSWindowStyleMaskMiniaturizable; } else if (flags & Qt::CustomizeWindowHint) { if (flags & Qt::WindowTitleHint) styleMask |= NSWindowStyleMaskTitled; @@ -1197,15 +1200,17 @@ void QCocoaWindow::windowDidResignKey() if (isForeignWindow()) return; - // Key window will be non-nil if another window became key, so do not - // set the active window to zero here -- the new key window's - // NSWindowDidBecomeKeyNotification hander will change the active window. - NSWindow *keyWindow = [NSApp keyWindow]; - if (!keyWindow || keyWindow == m_view.window) { - // No new key window, go ahead and set the active window to zero - if (!windowIsPopupType()) - QWindowSystemInterface::handleWindowActivated<QWindowSystemInterface::SynchronousDelivery>(0); - } + // The current key window will be non-nil if another window became key. If that + // window is a Qt window, we delay the window activation event until the didBecomeKey + // notification is delivered to the active window, to ensure an atomic update. + NSWindow *newKeyWindow = [NSApp keyWindow]; + if (newKeyWindow && newKeyWindow != m_view.window + && [newKeyWindow conformsToProtocol:@protocol(QNSWindowProtocol)]) + return; + + // Lost key window, go ahead and set the active window to zero + if (!windowIsPopupType()) + QWindowSystemInterface::handleWindowActivated<QWindowSystemInterface::SynchronousDelivery>(nullptr); } void QCocoaWindow::windowDidOrderOnScreen() diff --git a/src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.h b/src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.h index 872773cb7a..e070ba977d 100644 --- a/src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.h +++ b/src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.h @@ -48,9 +48,11 @@ QT_BEGIN_NAMESPACE class QIOSurfaceGraphicsBuffer : public QPlatformGraphicsBuffer { public: - QIOSurfaceGraphicsBuffer(const QSize &size, const QPixelFormat &format, QCFType<CGColorSpaceRef> colorSpace); + QIOSurfaceGraphicsBuffer(const QSize &size, const QPixelFormat &format); ~QIOSurfaceGraphicsBuffer(); + void setColorSpace(QCFType<CGColorSpaceRef> colorSpace); + const uchar *data() const override; uchar *data() override; int bytesPerLine() const override; diff --git a/src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.mm b/src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.mm index a367487e85..fc187e0f51 100644 --- a/src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.mm +++ b/src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.mm @@ -53,7 +53,7 @@ QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcQpaIOSurface, "qt.qpa.backingstore.iosurface"); -QIOSurfaceGraphicsBuffer::QIOSurfaceGraphicsBuffer(const QSize &size, const QPixelFormat &format, QCFType<CGColorSpaceRef> colorSpace) +QIOSurfaceGraphicsBuffer::QIOSurfaceGraphicsBuffer(const QSize &size, const QPixelFormat &format) : QPlatformGraphicsBuffer(size, format) { const size_t width = size.width(); @@ -81,17 +81,26 @@ QIOSurfaceGraphicsBuffer::QIOSurfaceGraphicsBuffer(const QSize &size, const QPix Q_ASSERT(size_t(bytesPerLine()) == bytesPerRow); Q_ASSERT(size_t(byteCount()) == totalBytes); - - if (colorSpace) { - IOSurfaceSetValue(m_surface, CFSTR("IOSurfaceColorSpace"), - QCFType<CFPropertyListRef>(CGColorSpaceCopyPropertyList(colorSpace))); - } } QIOSurfaceGraphicsBuffer::~QIOSurfaceGraphicsBuffer() { } +void QIOSurfaceGraphicsBuffer::setColorSpace(QCFType<CGColorSpaceRef> colorSpace) +{ + static const auto kIOSurfaceColorSpace = CFSTR("IOSurfaceColorSpace"); + + qCDebug(lcQpaIOSurface) << "Tagging" << this << "with color space" << colorSpace; + + if (colorSpace) { + IOSurfaceSetValue(m_surface, kIOSurfaceColorSpace, + QCFType<CFPropertyListRef>(CGColorSpaceCopyPropertyList(colorSpace))); + } else { + IOSurfaceRemoveValue(m_surface, kIOSurfaceColorSpace); + } +} + const uchar *QIOSurfaceGraphicsBuffer::data() const { return (const uchar *)IOSurfaceGetBaseAddress(m_surface); diff --git a/src/plugins/platforms/cocoa/qnsview_drawing.mm b/src/plugins/platforms/cocoa/qnsview_drawing.mm index 116a9a73df..14e45a038e 100644 --- a/src/plugins/platforms/cocoa/qnsview_drawing.mm +++ b/src/plugins/platforms/cocoa/qnsview_drawing.mm @@ -95,8 +95,15 @@ // by AppKit at a point where we've already set up other parts of the platform plugin // based on the presence of layers or not. Once we've rewritten these parts to support // dynamically picking up layer enablement we can let AppKit do its thing. - return QMacVersion::buildSDK() >= QOperatingSystemVersion::MacOSMojave - && QMacVersion::currentRuntime() >= QOperatingSystemVersion::MacOSMojave; + + if (QMacVersion::currentRuntime() >= QOperatingSystemVersion::MacOSBigSur) + return true; // Big Sur always enables layer-backing, regardless of SDK + + if (QMacVersion::currentRuntime() >= QOperatingSystemVersion::MacOSMojave + && QMacVersion::buildSDK() >= QOperatingSystemVersion::MacOSMojave) + return true; // Mojave and Catalina enable layers based on the app's SDK + + return false; // Prior versions needed explicitly enabled layer backing } - (BOOL)layerExplicitlyRequested diff --git a/src/plugins/platforms/cocoa/qnswindow.mm b/src/plugins/platforms/cocoa/qnswindow.mm index 6b4e110af2..8cff444359 100644 --- a/src/plugins/platforms/cocoa/qnswindow.mm +++ b/src/plugins/platforms/cocoa/qnswindow.mm @@ -227,14 +227,12 @@ static bool isMouseEvent(NSEvent *ev) - (BOOL)canBecomeMainWindow { - BOOL canBecomeMain = YES; // By default, windows can become the main window - // Windows with a transient parent (such as combobox popup windows) // cannot become the main window: if (!m_platformWindow || m_platformWindow->window()->transientParent()) - canBecomeMain = NO; + return NO; - return canBecomeMain; + return [super canBecomeMainWindow]; } - (BOOL)worksWhenModal @@ -255,8 +253,18 @@ static bool isMouseEvent(NSEvent *ev) - (NSColor *)backgroundColor { - return self.styleMask == NSWindowStyleMaskBorderless ? - [NSColor clearColor] : [super backgroundColor]; + // FIXME: Plumb to a WA_NoSystemBackground-like window flag, + // or a QWindow::backgroundColor() property. In the meantime + // we assume that if you have translucent content, without a + // frame then you intend to do all background drawing yourself. + const QWindow *window = m_platformWindow ? m_platformWindow->window() : nullptr; + if (!self.opaque && window && window->flags().testFlag(Qt::FramelessWindowHint)) + return [NSColor clearColor]; + + // This still allows you to have translucent content with a frame, + // where the system background (or color set via NSWindow) will + // shine through. + return [super backgroundColor]; } - (void)sendEvent:(NSEvent*)theEvent diff --git a/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp b/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp index 81bad45cd2..dbfb0e6058 100644 --- a/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp +++ b/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp @@ -52,6 +52,7 @@ #include <QScreen> #include <QDir> #if QT_CONFIG(regularexpression) +# include <QFileInfo> # include <QRegularExpression> #endif #include <QLoggingCategory> @@ -144,7 +145,12 @@ int QEglFSDeviceIntegration::framebufferIndex() const int fbIndex = 0; #if QT_CONFIG(regularexpression) QRegularExpression fbIndexRx(QLatin1String("fb(\\d+)")); - QRegularExpressionMatch match = fbIndexRx.match(QString::fromLocal8Bit(fbDeviceName())); + QFileInfo fbinfo(QString::fromLocal8Bit(fbDeviceName())); + QRegularExpressionMatch match; + if (fbinfo.isSymLink()) + match = fbIndexRx.match(fbinfo.symLinkTarget()); + else + match = fbIndexRx.match(fbinfo.fileName()); if (match.hasMatch()) fbIndex = match.captured(1).toInt(); #endif diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.cpp index 20127ae7f7..8d0fb523c4 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmdevice.cpp @@ -83,6 +83,8 @@ bool QEglFSKmsGbmDevice::open() setFd(fd); + m_eventReader.create(this); + return true; } @@ -90,6 +92,8 @@ void QEglFSKmsGbmDevice::close() { // Note: screens are gone at this stage. + m_eventReader.destroy(); + if (m_gbm_device) { gbm_device_destroy(m_gbm_device); m_gbm_device = nullptr; diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration.cpp index f154520669..caa1187b40 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmintegration.cpp @@ -54,8 +54,6 @@ QT_BEGIN_NAMESPACE -QMutex QEglFSKmsGbmScreen::m_waitForFlipMutex; - QEglFSKmsGbmIntegration::QEglFSKmsGbmIntegration() { qCDebug(qLcEglfsKmsDebug, "New DRM/KMS via GBM integration created"); diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp index 3a2951efbd..02b975fc75 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp @@ -110,7 +110,7 @@ QEglFSKmsGbmScreen::FrameBuffer *QEglFSKmsGbmScreen::framebufferForBufferObject( return fb.take(); } -QEglFSKmsGbmScreen::QEglFSKmsGbmScreen(QKmsDevice *device, const QKmsOutput &output, bool headless) +QEglFSKmsGbmScreen::QEglFSKmsGbmScreen(QEglFSKmsDevice *device, const QKmsOutput &output, bool headless) : QEglFSKmsScreen(device, output, headless) , m_gbm_surface(nullptr) , m_gbm_bo_current(nullptr) @@ -243,7 +243,7 @@ void QEglFSKmsGbmScreen::ensureModeSet(uint32_t fb) if (device()->hasAtomicSupport()) { #if QT_CONFIG(drm_atomic) - drmModeAtomicReq *request = device()->atomic_request(); + drmModeAtomicReq *request = device()->threadLocalAtomicRequest(); if (request) { drmModeAtomicAddProperty(request, op.connector_id, op.crtcIdPropertyId, op.crtc_id); drmModeAtomicAddProperty(request, op.crtc_id, op.modeIdPropertyId, op.mode_blob_id); @@ -276,19 +276,15 @@ void QEglFSKmsGbmScreen::waitForFlip() if (!m_gbm_bo_next) return; - QMutexLocker lock(&m_waitForFlipMutex); - while (m_gbm_bo_next) { - drmEventContext drmEvent; - memset(&drmEvent, 0, sizeof(drmEvent)); - drmEvent.version = 2; - drmEvent.vblank_handler = nullptr; - drmEvent.page_flip_handler = pageFlipHandler; - drmHandleEvent(device()->fd(), &drmEvent); - } + m_flipMutex.lock(); + device()->eventReader()->startWaitFlip(this, &m_flipMutex, &m_flipCond); + m_flipCond.wait(&m_flipMutex); + m_flipMutex.unlock(); + + flipFinished(); #if QT_CONFIG(drm_atomic) - if (device()->hasAtomicSupport()) - device()->atomicReset(); + device()->threadLocalAtomicReset(); #endif } @@ -324,16 +320,16 @@ void QEglFSKmsGbmScreen::flip() if (device()->hasAtomicSupport()) { #if QT_CONFIG(drm_atomic) - drmModeAtomicReq *request = device()->atomic_request(); + drmModeAtomicReq *request = device()->threadLocalAtomicRequest(); if (request) { drmModeAtomicAddProperty(request, op.eglfs_plane->id, op.eglfs_plane->framebufferPropertyId, fb->fb); drmModeAtomicAddProperty(request, op.eglfs_plane->id, op.eglfs_plane->crtcPropertyId, op.crtc_id); drmModeAtomicAddProperty(request, op.eglfs_plane->id, op.eglfs_plane->srcwidthPropertyId, - output().size.width() << 16); + op.size.width() << 16); drmModeAtomicAddProperty(request, op.eglfs_plane->id, op.eglfs_plane->srcXPropertyId, 0); drmModeAtomicAddProperty(request, op.eglfs_plane->id, op.eglfs_plane->srcYPropertyId, 0); drmModeAtomicAddProperty(request, op.eglfs_plane->id, op.eglfs_plane->srcheightPropertyId, - output().size.height() << 16); + op.size.height() << 16); drmModeAtomicAddProperty(request, op.eglfs_plane->id, op.eglfs_plane->crtcXPropertyId, 0); drmModeAtomicAddProperty(request, op.eglfs_plane->id, op.eglfs_plane->crtcYPropertyId, 0); drmModeAtomicAddProperty(request, op.eglfs_plane->id, op.eglfs_plane->crtcwidthPropertyId, @@ -368,7 +364,7 @@ void QEglFSKmsGbmScreen::flip() if (device()->hasAtomicSupport()) { #if QT_CONFIG(drm_atomic) - drmModeAtomicReq *request = device()->atomic_request(); + drmModeAtomicReq *request = device()->threadLocalAtomicRequest(); if (request) { drmModeAtomicAddProperty(request, d.screen->output().eglfs_plane->id, d.screen->output().eglfs_plane->framebufferPropertyId, fb->fb); @@ -391,22 +387,10 @@ void QEglFSKmsGbmScreen::flip() } #if QT_CONFIG(drm_atomic) - if (device()->hasAtomicSupport()) - device()->atomicCommit(this); + device()->threadLocalAtomicCommit(this); #endif } -void QEglFSKmsGbmScreen::pageFlipHandler(int fd, unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec, void *user_data) -{ - Q_UNUSED(fd); - Q_UNUSED(sequence); - Q_UNUSED(tv_sec); - Q_UNUSED(tv_usec); - - QEglFSKmsGbmScreen *screen = static_cast<QEglFSKmsGbmScreen *>(user_data); - screen->flipFinished(); -} - void QEglFSKmsGbmScreen::flipFinished() { if (m_cloneSource) { diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.h index b94f44b7b1..69feeee703 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.h +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.h @@ -43,7 +43,8 @@ #define QEGLFSKMSGBMSCREEN_H #include "qeglfskmsscreen.h" -#include <QtCore/QMutex> +#include <QMutex> +#include <QWaitCondition> #include <gbm.h> @@ -54,7 +55,7 @@ class QEglFSKmsGbmCursor; class QEglFSKmsGbmScreen : public QEglFSKmsScreen { public: - QEglFSKmsGbmScreen(QKmsDevice *device, const QKmsOutput &output, bool headless); + QEglFSKmsGbmScreen(QEglFSKmsDevice *device, const QKmsOutput &output, bool headless); ~QEglFSKmsGbmScreen(); QPlatformCursor *cursor() const override; @@ -75,18 +76,15 @@ private: void cloneDestFlipFinished(QEglFSKmsGbmScreen *cloneDestScreen); void updateFlipStatus(); - static void pageFlipHandler(int fd, - unsigned int sequence, - unsigned int tv_sec, - unsigned int tv_usec, - void *user_data); - gbm_surface *m_gbm_surface; gbm_bo *m_gbm_bo_current; gbm_bo *m_gbm_bo_next; bool m_flipPending; + QMutex m_flipMutex; + QWaitCondition m_flipCond; + QScopedPointer<QEglFSKmsGbmCursor> m_cursor; struct FrameBuffer { @@ -101,8 +99,6 @@ private: bool cloneFlipPending = false; }; QVector<CloneDestination> m_cloneDests; - - static QMutex m_waitForFlipMutex; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevicescreen.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevicescreen.cpp index 1626c86239..5a62e437c4 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevicescreen.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevicescreen.cpp @@ -47,7 +47,7 @@ QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug) -QEglFSKmsEglDeviceScreen::QEglFSKmsEglDeviceScreen(QKmsDevice *device, const QKmsOutput &output) +QEglFSKmsEglDeviceScreen::QEglFSKmsEglDeviceScreen(QEglFSKmsDevice *device, const QKmsOutput &output) : QEglFSKmsScreen(device, output) { } diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevicescreen.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevicescreen.h index 5efe35f8b3..961398ba3e 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevicescreen.h +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldevicescreen.h @@ -47,7 +47,7 @@ QT_BEGIN_NAMESPACE class QEglFSKmsEglDeviceScreen : public QEglFSKmsScreen { public: - QEglFSKmsEglDeviceScreen(QKmsDevice *device, const QKmsOutput &output); + QEglFSKmsEglDeviceScreen(QEglFSKmsDevice *device, const QKmsOutput &output); ~QEglFSKmsEglDeviceScreen(); QPlatformCursor *cursor() const override; diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/eglfs_kms_support.pro b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/eglfs_kms_support.pro index 40806b6a9b..e51903ed96 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/eglfs_kms_support.pro +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/eglfs_kms_support.pro @@ -14,9 +14,11 @@ CONFIG += egl SOURCES += $$PWD/qeglfskmsintegration.cpp \ $$PWD/qeglfskmsdevice.cpp \ - $$PWD/qeglfskmsscreen.cpp + $$PWD/qeglfskmsscreen.cpp \ + $$PWD/qeglfskmseventreader.cpp HEADERS += $$PWD/qeglfskmsintegration.h \ $$PWD/qeglfskmsdevice.h \ $$PWD/qeglfskmsscreen.h \ - $$PWD/qeglfskmshelpers.h + $$PWD/qeglfskmshelpers.h \ + $$PWD/qeglfskmseventreader.h 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 fc83a620d9..34908aa60f 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsdevice.h +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsdevice.h @@ -42,6 +42,7 @@ #define QEGLFSKMSDEVICE_H #include "private/qeglfsglobal_p.h" +#include "qeglfskmseventreader.h" #include <QtKmsSupport/private/qkmsdevice_p.h> QT_BEGIN_NAMESPACE @@ -55,6 +56,11 @@ public: bool isPrimary, const QPoint &virtualPos, const QList<QPlatformScreen *> &virtualSiblings) override; + + QEglFSKmsEventReader *eventReader() { return &m_eventReader; } + +protected: + QEglFSKmsEventReader m_eventReader; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.cpp new file mode 100644 index 0000000000..645a0ae2e9 --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.cpp @@ -0,0 +1,218 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qeglfskmseventreader.h" +#include "qeglfskmsdevice.h" +#include <QSocketNotifier> +#include <QCoreApplication> +#include <QLoggingCategory> + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug) + +static void pageFlipHandler(int fd, unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec, void *user_data) +{ + Q_UNUSED(fd); + Q_UNUSED(sequence); + Q_UNUSED(tv_sec); + Q_UNUSED(tv_usec); + + QEglFSKmsEventReaderThread *t = static_cast<QEglFSKmsEventReaderThread *>(QThread::currentThread()); + t->eventHost()->handlePageFlipCompleted(user_data); +} + +class RegisterWaitFlipEvent : public QEvent +{ +public: + static const QEvent::Type TYPE = QEvent::Type(QEvent::User + 1); + RegisterWaitFlipEvent(void *key, QMutex *mutex, QWaitCondition *cond) + : QEvent(TYPE), key(key), mutex(mutex), cond(cond) + { } + void *key; + QMutex *mutex; + QWaitCondition *cond; +}; + +bool QEglFSKmsEventHost::event(QEvent *event) +{ + if (event->type() == RegisterWaitFlipEvent::TYPE) { + RegisterWaitFlipEvent *e = static_cast<RegisterWaitFlipEvent *>(event); + PendingFlipWait *p = &pendingFlipWaits[0]; + PendingFlipWait *end = p + MAX_FLIPS; + while (p < end) { + if (!p->key) { + p->key = e->key; + p->mutex = e->mutex; + p->cond = e->cond; + updateStatus(); + return true; + } + ++p; + } + qWarning("Cannot queue page flip wait (more than %d screens?)", MAX_FLIPS); + e->mutex->lock(); + e->cond->wakeOne(); + e->mutex->unlock(); + return true; + } + return QObject::event(event); +} + +void QEglFSKmsEventHost::updateStatus() +{ + void **begin = &completedFlips[0]; + void **end = begin + MAX_FLIPS; + + for (int i = 0; i < MAX_FLIPS; ++i) { + PendingFlipWait *w = pendingFlipWaits + i; + if (!w->key) + continue; + + void **p = begin; + while (p < end) { + if (*p == w->key) { + *p = nullptr; + w->key = nullptr; + w->mutex->lock(); + w->cond->wakeOne(); + w->mutex->unlock(); + return; + } + ++p; + } + } +} + +void QEglFSKmsEventHost::handlePageFlipCompleted(void *key) +{ + void **begin = &completedFlips[0]; + void **end = begin + MAX_FLIPS; + void **p = begin; + while (p < end) { + if (*p == key) { + updateStatus(); + return; + } + ++p; + } + p = begin; + while (p < end) { + if (!*p) { + *p = key; + updateStatus(); + return; + } + ++p; + } + qWarning("Cannot store page flip status (more than %d screens?)", MAX_FLIPS); +} + +void QEglFSKmsEventReaderThread::run() +{ + qCDebug(qLcEglfsKmsDebug, "Event reader thread: entering event loop"); + + QSocketNotifier notifier(m_fd, QSocketNotifier::Read); + QObject::connect(¬ifier, &QSocketNotifier::activated, ¬ifier, [this] { + drmEventContext drmEvent; + memset(&drmEvent, 0, sizeof(drmEvent)); + drmEvent.version = 2; + drmEvent.vblank_handler = nullptr; + drmEvent.page_flip_handler = pageFlipHandler; + drmHandleEvent(m_fd, &drmEvent); + }); + + exec(); + + m_ev.moveToThread(thread()); // move back to the thread where m_ev was created + + qCDebug(qLcEglfsKmsDebug, "Event reader thread: event loop stopped"); +} + +QEglFSKmsEventReader::~QEglFSKmsEventReader() +{ + destroy(); +} + +void QEglFSKmsEventReader::create(QEglFSKmsDevice *device) +{ + destroy(); + + if (!device) + return; + + m_device = device; + + qCDebug(qLcEglfsKmsDebug, "Initalizing event reader for device %p fd %d", + m_device, m_device->fd()); + + m_thread = new QEglFSKmsEventReaderThread(m_device->fd()); + m_thread->start(); + + // Change thread affinity for the event host, so that postEvent() + // goes through the event reader thread's event loop for that object. + m_thread->eventHost()->moveToThread(m_thread); +} + +void QEglFSKmsEventReader::destroy() +{ + if (!m_device) + return; + + qCDebug(qLcEglfsKmsDebug, "Stopping event reader for device %p", m_device); + + if (m_thread) { + m_thread->quit(); + m_thread->wait(); + delete m_thread; + m_thread = nullptr; + } + + m_device = nullptr; +} + +void QEglFSKmsEventReader::startWaitFlip(void *key, QMutex *mutex, QWaitCondition *cond) +{ + if (m_thread) { + QCoreApplication::postEvent(m_thread->eventHost(), + new RegisterWaitFlipEvent(key, mutex, cond)); + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.h new file mode 100644 index 0000000000..4aa285b0fe --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QEGLFSKKMSEVENTREADER_H +#define QEGLFSKKMSEVENTREADER_H + +#include "private/qeglfsglobal_p.h" +#include <QObject> +#include <QThread> +#include <QMutex> +#include <QWaitCondition> + +QT_BEGIN_NAMESPACE + +class QEglFSKmsDevice; + +struct QEglFSKmsEventHost : public QObject +{ + struct PendingFlipWait { + void *key; + QMutex *mutex; + QWaitCondition *cond; + }; + + static const int MAX_FLIPS = 32; + void *completedFlips[MAX_FLIPS] = {}; + QEglFSKmsEventHost::PendingFlipWait pendingFlipWaits[MAX_FLIPS] = {}; + + bool event(QEvent *event) override; + void updateStatus(); + void handlePageFlipCompleted(void *key); +}; + +class QEglFSKmsEventReaderThread : public QThread +{ +public: + QEglFSKmsEventReaderThread(int fd) : m_fd(fd) { } + void run() override; + QEglFSKmsEventHost *eventHost() { return &m_ev; } + +private: + int m_fd; + QEglFSKmsEventHost m_ev; +}; + +class Q_EGLFS_EXPORT QEglFSKmsEventReader +{ +public: + ~QEglFSKmsEventReader(); + + void create(QEglFSKmsDevice *device); + void destroy(); + + void startWaitFlip(void *key, QMutex *mutex, QWaitCondition *cond); + +private: + QEglFSKmsDevice *m_device = nullptr; + QEglFSKmsEventReaderThread *m_thread = nullptr; +}; + +QT_END_NAMESPACE + +#endif // QEGLFSKKMSEVENTREADER_H 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 a6aac61506..28b6b7df63 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsintegration.cpp @@ -140,7 +140,7 @@ void *QEglFSKmsIntegration::nativeResourceForIntegration(const QByteArray &name) #if QT_CONFIG(drm_atomic) if (name == QByteArrayLiteral("dri_atomic_request") && m_device) - return (void *) (qintptr) m_device->atomic_request(); + return (void *) (qintptr) m_device->threadLocalAtomicRequest(); #endif return nullptr; } 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 e5354d97bd..b2ee7206aa 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include "qeglfskmsscreen.h" +#include "qeglfskmsdevice.h" #include "qeglfsintegration_p.h" #include <QtCore/QLoggingCategory> @@ -68,7 +69,7 @@ private: QEglFSKmsScreen *m_screen; }; -QEglFSKmsScreen::QEglFSKmsScreen(QKmsDevice *device, const QKmsOutput &output, bool headless) +QEglFSKmsScreen::QEglFSKmsScreen(QEglFSKmsDevice *device, const QKmsOutput &output, bool headless) : QEglFSScreen(static_cast<QEglFSIntegration *>(QGuiApplicationPrivate::platformIntegration())->display()) , 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 7f395aacb7..bbd929cba5 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.h +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.h @@ -51,12 +51,13 @@ QT_BEGIN_NAMESPACE +class QEglFSKmsDevice; class QEglFSKmsInterruptHandler; class Q_EGLFS_EXPORT QEglFSKmsScreen : public QEglFSScreen { public: - QEglFSKmsScreen(QKmsDevice *device, const QKmsOutput &output, bool headless = false); + QEglFSKmsScreen(QEglFSKmsDevice *device, const QKmsOutput &output, bool headless = false); ~QEglFSKmsScreen(); void setVirtualPosition(const QPoint &pos); @@ -87,7 +88,7 @@ public: int currentMode() const override; int preferredMode() const override; - QKmsDevice *device() const { return m_device; } + QEglFSKmsDevice *device() const { return m_device; } virtual void waitForFlip(); @@ -100,7 +101,7 @@ public: void setPowerState(QPlatformScreen::PowerState state) override; protected: - QKmsDevice *m_device; + QEglFSKmsDevice *m_device; QKmsOutput m_output; QEdidParser m_edid; diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2screen.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2screen.cpp index 475d9d55dd..c255bc84b7 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2screen.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2screen.cpp @@ -99,7 +99,7 @@ QEglFSKmsVsp2Screen::DmaBuffer *QEglFSKmsVsp2Screen::dmaBufferForGbmBuffer(gbm_b return fb.take(); } -QEglFSKmsVsp2Screen::QEglFSKmsVsp2Screen(QKmsDevice *device, const QKmsOutput &output) +QEglFSKmsVsp2Screen::QEglFSKmsVsp2Screen(QEglFSKmsDevice *device, const QKmsOutput &output) : QEglFSKmsScreen(device, output) , m_blender(new Blender(this)) { diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2screen.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2screen.h index 7618510333..378786643d 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2screen.h +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_vsp2/qeglfskmsvsp2screen.h @@ -53,7 +53,7 @@ QT_BEGIN_NAMESPACE class QEglFSKmsVsp2Screen : public QEglFSKmsScreen { public: - QEglFSKmsVsp2Screen(QKmsDevice *device, const QKmsOutput &output); + QEglFSKmsVsp2Screen(QEglFSKmsDevice *device, const QKmsOutput &output); gbm_surface *createSurface(); void resetSurface(); diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_openwfd/qeglfsopenwfdintegration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_openwfd/qeglfsopenwfdintegration.cpp index b8f04cf978..2d3f3ff25a 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_openwfd/qeglfsopenwfdintegration.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_openwfd/qeglfsopenwfdintegration.cpp @@ -44,12 +44,6 @@ QT_BEGIN_NAMESPACE -#define MAX_NUM_OF_WFD_BUFFERS 3 -#define MAX_NUM_OF_WFD_DEVICES 4 -#define MAX_NUM_OF_WFD_PIPELINES 16 -#define MAX_NUM_OF_WFD_PORT_MODES 64 -#define MAX_NUM_OF_WFD_PORTS 4 - typedef struct wfd_buffer { WFD_EGLImageType* image; WFDSource source; @@ -78,8 +72,8 @@ void QEglFSOpenWFDIntegration::platformInit() // Create device WFDint dev_attribs[3] = {WFD_DEVICE_CLIENT_TYPE, - WFD_CLIENT_ID_CLUSTER, - WFD_NONE}; + WFD_CLIENT_ID_CLUSTER, + WFD_NONE}; bool ok; WFDint clientType = qgetenv("QT_OPENWFD_CLIENT_ID").toInt(&ok, 16); @@ -123,99 +117,100 @@ void QEglFSOpenWFDIntegration::platformInit() eError = wfdGetError(mDevice); if (WFD_ERROR_NONE != eError) qFatal("Failed to power on wfd port"); -} - -QSize QEglFSOpenWFDIntegration::screenSize() const -{ - return mScreenSize; -} - -EGLNativeDisplayType QEglFSOpenWFDIntegration::platformDisplay() const -{ - return mNativeDisplay; -} - -EGLNativeWindowType QEglFSOpenWFDIntegration::createNativeWindow(QPlatformWindow *window, - const QSize &size, - const QSurfaceFormat &format) -{ - Q_UNUSED(window); - Q_UNUSED(format); // Get list of pipelines WFDint numPipelines = wfdEnumeratePipelines(mDevice, nullptr, 0, nullptr); - WFDint pipelineIds[MAX_NUM_OF_WFD_PIPELINES]; wfdEnumeratePipelines(mDevice, pipelineIds, numPipelines, nullptr); - bool ok; WFDint pipelineId = qgetenv("QT_OPENWFD_PIPELINE_ID").toInt(&ok); if (!ok) pipelineId = pipelineIds[0]; - WFDPipeline pipeline = wfdCreatePipeline(mDevice, pipelineId, nullptr); - if (WFD_INVALID_HANDLE == pipeline) + mPipeline = wfdCreatePipeline(mDevice, pipelineId, nullptr); + if (WFD_INVALID_HANDLE == mPipeline) qFatal("Failed to create wfd pipeline"); - wfdSetPipelineAttribi(mDevice, pipeline, WFD_PIPELINE_TRANSPARENCY_ENABLE, + wfdSetPipelineAttribi(mDevice, mPipeline, WFD_PIPELINE_TRANSPARENCY_ENABLE, (WFD_TRANSPARENCY_SOURCE_ALPHA|WFD_TRANSPARENCY_GLOBAL_ALPHA)); - WFDErrorCode eError = wfdGetError(mDevice); + eError = wfdGetError(mDevice); if (WFD_ERROR_NONE != eError) qFatal("Failed to set WFD_PIPELINE_TRANSPARENCY_ENABLE"); - wfdSetPipelineAttribi(mDevice, pipeline, WFD_PIPELINE_GLOBAL_ALPHA, 255); + wfdSetPipelineAttribi(mDevice, mPipeline, WFD_PIPELINE_GLOBAL_ALPHA, 255); eError = wfdGetError(mDevice); if (WFD_ERROR_NONE != eError) qFatal("Failed to set WFD_PIPELINE_GLOBAL_ALPHA"); - wfdBindPipelineToPort(mDevice, mPort, pipeline); + wfdBindPipelineToPort(mDevice, mPort, mPipeline); eError = wfdGetError(mDevice); if (WFD_ERROR_NONE != eError) qFatal("Failed to bind port to pipeline"); - // Create buffers - WFDSource source[MAX_NUM_OF_WFD_BUFFERS] = {WFD_INVALID_HANDLE, WFD_INVALID_HANDLE, - WFD_INVALID_HANDLE}; - WFDEGLImage eglImageHandles[MAX_NUM_OF_WFD_BUFFERS]; - WFD_EGLImageType* wfdEglImages[MAX_NUM_OF_WFD_BUFFERS]; for (int i = 0; i < MAX_NUM_OF_WFD_BUFFERS; i++) { wfdCreateWFDEGLImages(mDevice, mScreenSize.width(), mScreenSize.height(), WFD_FORMAT_RGBA8888, WFD_USAGE_OPENGL_ES2 | WFD_USAGE_DISPLAY, - 1, &(eglImageHandles[i]), 0); + 1, &(mEGLImageHandles[i]), 0); - wfdEglImages[i] = (WFD_EGLImageType *)(eglImageHandles[i]); - if (WFD_INVALID_HANDLE == wfdEglImages[i]) + mWFDEglImages[i] = (WFD_EGLImageType *)(mEGLImageHandles[i]); + if (WFD_INVALID_HANDLE == mWFDEglImages[i]) qFatal("Failed to create WDFEGLImages"); - source[i] = wfdCreateSourceFromImage(mDevice, pipeline, eglImageHandles[i], nullptr); - if (WFD_INVALID_HANDLE == source[i]) + mSources[i] = wfdCreateSourceFromImage(mDevice, mPipeline, mEGLImageHandles[i], nullptr); + if (WFD_INVALID_HANDLE == mSources[i]) qFatal("Failed to create source from EGLImage"); } +} - // Commit port - wfdDeviceCommit(mDevice, WFD_COMMIT_ENTIRE_PORT, mPort); - eError = wfdGetError(mDevice); - if (WFD_ERROR_NONE != eError) - qFatal("Failed to commit port"); +QSize QEglFSOpenWFDIntegration::screenSize() const +{ + return mScreenSize; +} + +EGLNativeDisplayType QEglFSOpenWFDIntegration::platformDisplay() const +{ + return mNativeDisplay; +} + +EGLNativeWindowType QEglFSOpenWFDIntegration::createNativeWindow(QPlatformWindow *window, + const QSize &size, + const QSurfaceFormat &format) +{ + Q_UNUSED(window); + Q_UNUSED(format); // Create native window wfd_window_t* nativeWindow = (wfd_window_t*)malloc(sizeof(wfd_window_t)); if (nullptr == nativeWindow) - qFatal("Failed to allocate memory for native window"); + qFatal("Failed to allocate memory for native window"); nativeWindow->dev = mDevice; nativeWindow->port = mPort; - nativeWindow->pipeline = pipeline; + nativeWindow->pipeline = mPipeline; nativeWindow->numBuffers = MAX_NUM_OF_WFD_BUFFERS; + WFDErrorCode eError; + for (int i = 0; i < MAX_NUM_OF_WFD_BUFFERS; i++) { - nativeWindow->buffers[i].image = wfdEglImages[i]; - nativeWindow->buffers[i].source = source[i]; + nativeWindow->buffers[i].image = mWFDEglImages[i]; + nativeWindow->buffers[i].source = mSources[i]; + wfdBindSourceToPipeline(nativeWindow->dev, nativeWindow->pipeline, nativeWindow->buffers[i].source , WFD_TRANSITION_AT_VSYNC, nullptr); + eError = wfdGetError(nativeWindow->dev); + if (WFD_ERROR_NONE != eError) + { + qFatal("wfdBindSourceToPipeline eError=0x%08x", eError); + } } + // Commit port + wfdDeviceCommit(mDevice, WFD_COMMIT_ENTIRE_PORT, mPort); + eError = wfdGetError(mDevice); + if (WFD_ERROR_NONE != eError) + qFatal("Failed to commit port"); + return (EGLNativeWindowType)nativeWindow; } @@ -231,6 +226,13 @@ QSurfaceFormat QEglFSOpenWFDIntegration::surfaceFormatFor(const QSurfaceFormat & void QEglFSOpenWFDIntegration::destroyNativeWindow(EGLNativeWindowType window) { + wfdBindSourceToPipeline(mDevice, ((wfd_window_t*)window)->pipeline, nullptr, WFD_TRANSITION_IMMEDIATE, nullptr); + + for (int i = 0; i < MAX_NUM_OF_WFD_BUFFERS; i++) { + wfdDestroySource(mDevice, mSources[i]); + wfdDestroyWFDEGLImages(mDevice, 1, &mEGLImageHandles[i]); + } + free((void*)window); } diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_openwfd/qeglfsopenwfdintegration.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_openwfd/qeglfsopenwfdintegration.h index 189ddd4d7a..53d9cffaf2 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_openwfd/qeglfsopenwfdintegration.h +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_openwfd/qeglfsopenwfdintegration.h @@ -47,6 +47,12 @@ QT_BEGIN_NAMESPACE +#define MAX_NUM_OF_WFD_BUFFERS 3 +#define MAX_NUM_OF_WFD_DEVICES 4 +#define MAX_NUM_OF_WFD_PIPELINES 16 +#define MAX_NUM_OF_WFD_PORT_MODES 64 +#define MAX_NUM_OF_WFD_PORTS 4 + class QEglFSOpenWFDIntegration : public QEglFSDeviceIntegration { public: @@ -62,6 +68,10 @@ private: EGLNativeDisplayType mNativeDisplay; WFDDevice mDevice; WFDPort mPort; + WFDPipeline mPipeline; + WFDSource mSources[MAX_NUM_OF_WFD_BUFFERS] = {WFD_INVALID_HANDLE, WFD_INVALID_HANDLE, WFD_INVALID_HANDLE}; + WFD_EGLImageType* mWFDEglImages[MAX_NUM_OF_WFD_BUFFERS]; + WFDEGLImage mEGLImageHandles[MAX_NUM_OF_WFD_BUFFERS]; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qioscontext.mm b/src/plugins/platforms/ios/qioscontext.mm index 535e7d7aa6..cecbb17039 100644 --- a/src/plugins/platforms/ios/qioscontext.mm +++ b/src/plugins/platforms/ios/qioscontext.mm @@ -332,11 +332,8 @@ bool QIOSContext::verifyGraphicsHardwareAvailability() ); }); - if (applicationBackgrounded) { - static const char warning[] = "OpenGL ES calls are not allowed while an application is backgrounded"; - Q_ASSERT_X(!applicationBackgrounded, "QIOSContext", warning); - qCWarning(lcQpaGLContext, warning); - } + if (applicationBackgrounded) + qCWarning(lcQpaGLContext, "OpenGL ES calls are not allowed while an application is backgrounded"); return !applicationBackgrounded; } diff --git a/src/plugins/platforms/ios/qiostextresponder.mm b/src/plugins/platforms/ios/qiostextresponder.mm index 396c769be8..1bc9744528 100644 --- a/src/plugins/platforms/ios/qiostextresponder.mm +++ b/src/plugins/platforms/ios/qiostextresponder.mm @@ -781,12 +781,16 @@ - (UIView *)textInputView { + auto *focusWindow = QGuiApplication::focusWindow(); + if (!focusWindow) + return nil; + // iOS expects rects we return from other UITextInput methods // to be relative to the view this method returns. // Since QInputMethod returns rects relative to the top level // QWindow, that is also the view we need to return. - Q_ASSERT(qApp->focusWindow()->handle()); - QPlatformWindow *topLevel = qApp->focusWindow()->handle(); + Q_ASSERT(focusWindow->handle()); + QPlatformWindow *topLevel = focusWindow->handle(); while (QPlatformWindow *p = topLevel->parent()) topLevel = p; return reinterpret_cast<UIView *>(topLevel->winId()); diff --git a/src/plugins/platforms/ios/quiaccessibilityelement.mm b/src/plugins/platforms/ios/quiaccessibilityelement.mm index 3154536aad..4dd1f9f035 100644 --- a/src/plugins/platforms/ios/quiaccessibilityelement.mm +++ b/src/plugins/platforms/ios/quiaccessibilityelement.mm @@ -123,8 +123,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); if (val) { return val->currentValue().toString().toNSString(); } else if (QAccessibleTextInterface *text = iface->textInterface()) { - // FIXME doesn't work? - return text->text(0, text->characterCount() - 1).toNSString(); + return text->text(0, text->characterCount()).toNSString(); } return [super accessibilityHint]; @@ -158,8 +157,16 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement); if (state.searchEdit) traits |= UIAccessibilityTraitSearchField; - if (iface->role() == QAccessible::Button) + const auto accessibleRole = iface->role(); + if (accessibleRole == QAccessible::Button) { traits |= UIAccessibilityTraitButton; + } else if (accessibleRole == QAccessible::EditableText) { + static auto defaultTextFieldTraits = []{ + auto *textField = [[[UITextField alloc] initWithFrame:CGRectZero] autorelease]; + return textField.accessibilityTraits; + }(); + traits |= defaultTextFieldTraits; + } if (iface->valueInterface()) traits |= UIAccessibilityTraitAdjustable; diff --git a/src/plugins/platforms/ios/quiview.mm b/src/plugins/platforms/ios/quiview.mm index 962b1d929f..dca113abdd 100644 --- a/src/plugins/platforms/ios/quiview.mm +++ b/src/plugins/platforms/ios/quiview.mm @@ -56,7 +56,7 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") @implementation QUIView { - QHash<UITouch *, QWindowSystemInterface::TouchPoint> m_activeTouches; + QHash<NSUInteger, QWindowSystemInterface::TouchPoint> m_activeTouches; UITouch *m_activePencilTouch; int m_nextTouchId; NSMutableArray<UIAccessibilityElement *> *m_accessibleElements; @@ -403,9 +403,19 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") } #endif - for (UITouch *uiTouch : m_activeTouches.keys()) { - QWindowSystemInterface::TouchPoint &touchPoint = m_activeTouches[uiTouch]; - if (![touches containsObject:uiTouch]) { + if (m_activeTouches.isEmpty()) + return; + for (auto it = m_activeTouches.begin(); it != m_activeTouches.end(); ++it) { + auto hash = it.key(); + QWindowSystemInterface::TouchPoint &touchPoint = it.value(); + UITouch *uiTouch = nil; + for (UITouch *touch in touches) { + if (touch.hash == hash) { + uiTouch = touch; + break; + } + } + if (!uiTouch) { touchPoint.state = Qt::TouchPointStationary; } else { touchPoint.state = state; @@ -437,8 +447,6 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") } } } - if (m_activeTouches.isEmpty()) - return; if ([self.window isKindOfClass:[QUIWindow class]] && !static_cast<QUIWindow *>(self.window).sendingEvent) { @@ -474,9 +482,9 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") m_activePencilTouch = touch; } else { - Q_ASSERT(!m_activeTouches.contains(touch)); + Q_ASSERT(!m_activeTouches.contains(touch.hash)); #endif - m_activeTouches[touch].id = m_nextTouchId++; + m_activeTouches[touch.hash].id = m_nextTouchId++; #if QT_CONFIG(tabletevent) } #endif @@ -503,6 +511,7 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") [self handleTouches:touches withEvent:event withState:Qt::TouchPointReleased withTimestamp:ulong(event.timestamp * 1000)]; // Remove ended touch points from the active set: +#ifndef Q_OS_TVOS for (UITouch *touch in touches) { #if QT_CONFIG(tabletevent) if (touch.type == UITouchTypeStylus) { @@ -510,9 +519,14 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") } else #endif { - m_activeTouches.remove(touch); + m_activeTouches.remove(touch.hash); } } +#else + // tvOS only supports single touch + m_activeTouches.clear(); +#endif + if (m_activeTouches.isEmpty() && !m_activePencilTouch) m_nextTouchId = 0; } @@ -630,10 +644,8 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") - (void)addInteraction:(id<UIInteraction>)interaction { - if (__builtin_available(iOS 13.0, *)) { - if ([interaction isKindOfClass:UITextInteraction.class]) - return; // Prevent iOS from adding UITextInteraction - } + if ([NSStringFromClass(interaction.class) isEqualToString:@"UITextInteraction"]) + return; [super addInteraction:interaction]; } diff --git a/src/plugins/platforms/vnc/qvnc.cpp b/src/plugins/platforms/vnc/qvnc.cpp index ffe00de2b1..43fbc9daf7 100644 --- a/src/plugins/platforms/vnc/qvnc.cpp +++ b/src/plugins/platforms/vnc/qvnc.cpp @@ -477,6 +477,9 @@ void QRfbRawEncoder::write() // server->screen()->geometry().height()); // } + const QImage screenImage = client->server()->screenImage(); + rgn &= screenImage.rect(); + const auto rectsInRegion = rgn.rectCount(); { @@ -492,8 +495,6 @@ void QRfbRawEncoder::write() if (rectsInRegion <= 0) return; - const QImage screenImage = client->server()->screenImage(); - for (const QRect &tileRect: rgn) { const QRfbRect rect(tileRect.x(), tileRect.y(), tileRect.width(), tileRect.height()); diff --git a/src/plugins/platforms/vnc/qvncscreen.cpp b/src/plugins/platforms/vnc/qvncscreen.cpp index 67d33de2f0..7f418c4e37 100644 --- a/src/plugins/platforms/vnc/qvncscreen.cpp +++ b/src/plugins/platforms/vnc/qvncscreen.cpp @@ -139,6 +139,9 @@ void QVncScreen::enableClientCursor(QVncClient *client) void QVncScreen::disableClientCursor(QVncClient *client) { #if QT_CONFIG(cursor) + if (!clientCursor) + return; + uint clientCount = clientCursor->removeClient(client); if (clientCount == 0) { delete clientCursor; diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index febc30c325..adf0f918ca 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -1879,12 +1879,13 @@ void QWindowsWindow::checkForScreenChanged(ScreenChangeMode mode) if (newScreen == nullptr || newScreen == currentScreen) return; // For screens with different DPI: postpone until WM_DPICHANGE - if (mode == FromGeometryChange + // Check on currentScreen as it can be 0 when resuming a session (QTBUG-80436). + if (mode == FromGeometryChange && currentScreen != nullptr && !equalDpi(currentScreen->logicalDpi(), newScreen->logicalDpi())) { return; } qCDebug(lcQpaWindows).noquote().nospace() << __FUNCTION__ - << ' ' << window() << " \"" << currentScreen->name() + << ' ' << window() << " \"" << (currentScreen ? currentScreen->name() : QString()) << "\"->\"" << newScreen->name() << '"'; if (mode == FromGeometryChange) setFlag(SynchronousGeometryChangeEvent); @@ -2074,6 +2075,7 @@ QWindowsWindowData QWindowsWindow::setWindowFlags_sys(Qt::WindowFlags wt, QWindowsWindowData result = m_data; result.flags = creationData.flags; result.embedded = creationData.embedded; + result.hasFrame = (creationData.style & (WS_DLGFRAME | WS_THICKFRAME)); return result; } diff --git a/src/plugins/platforms/xcb/qxcbatom.cpp b/src/plugins/platforms/xcb/qxcbatom.cpp index ecb73cb90b..a73d28319d 100644 --- a/src/plugins/platforms/xcb/qxcbatom.cpp +++ b/src/plugins/platforms/xcb/qxcbatom.cpp @@ -122,6 +122,7 @@ static const char *xcb_atomnames = { "_NET_WM_STATE_MODAL\0" "_NET_WM_STATE_STAYS_ON_TOP\0" "_NET_WM_STATE_DEMANDS_ATTENTION\0" + "_NET_WM_STATE_HIDDEN\0" "_NET_WM_USER_TIME\0" "_NET_WM_USER_TIME_WINDOW\0" @@ -181,6 +182,7 @@ static const char *xcb_atomnames = { "XdndActionCopy\0" "XdndActionLink\0" "XdndActionMove\0" + "XdndActionAsk\0" "XdndActionPrivate\0" // Xkb diff --git a/src/plugins/platforms/xcb/qxcbatom.h b/src/plugins/platforms/xcb/qxcbatom.h index 233d2eadb7..9cf93ec314 100644 --- a/src/plugins/platforms/xcb/qxcbatom.h +++ b/src/plugins/platforms/xcb/qxcbatom.h @@ -123,6 +123,7 @@ public: _NET_WM_STATE_MODAL, _NET_WM_STATE_STAYS_ON_TOP, _NET_WM_STATE_DEMANDS_ATTENTION, + _NET_WM_STATE_HIDDEN, _NET_WM_USER_TIME, _NET_WM_USER_TIME_WINDOW, @@ -182,6 +183,7 @@ public: XdndActionCopy, XdndActionLink, XdndActionMove, + XdndActionAsk, XdndActionPrivate, // Xkb diff --git a/src/plugins/platforms/xcb/qxcbbackingstore.cpp b/src/plugins/platforms/xcb/qxcbbackingstore.cpp index f9240a45cc..32739b3572 100644 --- a/src/plugins/platforms/xcb/qxcbbackingstore.cpp +++ b/src/plugins/platforms/xcb/qxcbbackingstore.cpp @@ -709,9 +709,10 @@ void QXcbBackingStoreImage::put(xcb_drawable_t dst, const QRegion ®ion, const Q_ASSERT(!m_clientSideScroll); ensureGC(dst); - setClip(region); if (hasShm()) { + setClip(region); // Clip in window local coordinates + // Copy scrolled area on server-side from pixmap to window const QRegion scrolledRegion = m_scrolledRegion.translated(-offset); for (const QRect &rect : scrolledRegion) { @@ -732,7 +733,15 @@ void QXcbBackingStoreImage::put(xcb_drawable_t dst, const QRegion ®ion, const const QRect bounds = region.boundingRect(); const QPoint target = bounds.topLeft(); const QRect source = bounds.translated(offset); - flushPixmap(region); + + // First clip in backingstore-local coordinates, and upload + // the changed parts of the backingstore to the server. + setClip(source); + flushPixmap(source); + + // Then clip in window local coordinates, and copy the updated + // parts of the backingstore image server-side to the window. + setClip(region); xcb_copy_area(xcb_connection(), m_xcb_pixmap, dst, diff --git a/src/plugins/platforms/xcb/qxcbclipboard.cpp b/src/plugins/platforms/xcb/qxcbclipboard.cpp index ac8b029916..a4940f1c49 100644 --- a/src/plugins/platforms/xcb/qxcbclipboard.cpp +++ b/src/plugins/platforms/xcb/qxcbclipboard.cpp @@ -787,6 +787,12 @@ xcb_generic_event_t *QXcbClipboard::waitForClipboardEvent(xcb_window_t window, i if (e) // found the waited for event return e; + // It is safe to assume here that the pointed to node won't be re-used + // while we are holding the pointer to it. The nodes can be recycled + // only when they are dequeued, which is done only by + // QXcbConnection::processXcbEvents(). + const QXcbEventNode *flushedTailNode = queue->flushedTail(); + if (checkManager) { auto reply = Q_XCB_REPLY(xcb_get_selection_owner, xcb_connection(), atom(QXcbAtom::CLIPBOARD_MANAGER)); if (!reply || reply->owner == XCB_NONE) @@ -812,7 +818,7 @@ xcb_generic_event_t *QXcbClipboard::waitForClipboardEvent(xcb_window_t window, i const auto elapsed = timer.elapsed(); if (elapsed < clipboard_timeout) - queue->waitForNewEvents(clipboard_timeout - elapsed); + queue->waitForNewEvents(flushedTailNode, clipboard_timeout - elapsed); } while (timer.elapsed() < clipboard_timeout); return nullptr; @@ -835,6 +841,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 +878,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 0d71a5a552..8f0281c176 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -774,7 +774,10 @@ xcb_timestamp_t QXcbConnection::getTimestamp() xcb_generic_event_t *event = nullptr; - while (!event) { + // When disconnection is caused by X server, event will never be able to hold + // a valid pointer. isConnected(), which calls xcb_connection_has_error(), + // can handle this type of disconnection and properly quits the loop. + while (isConnected() && !event) { connection()->sync(); event = eventQueue()->peek([window, dummyAtom](xcb_generic_event_t *event, int type) { if (type != XCB_PROPERTY_NOTIFY) @@ -784,6 +787,14 @@ xcb_timestamp_t QXcbConnection::getTimestamp() }); } + if (!event) { + // https://www.x.org/releases/X11R7.7/doc/xproto/x11protocol.html#glossary + // > One timestamp value (named CurrentTime) is never generated by the + // > server. This value is reserved for use in requests to represent the + // > current server time. + return XCB_CURRENT_TIME; + } + xcb_property_notify_event_t *pn = reinterpret_cast<xcb_property_notify_event_t *>(event); xcb_timestamp_t timestamp = pn->time; free(event); @@ -795,7 +806,13 @@ xcb_timestamp_t QXcbConnection::getTimestamp() xcb_window_t QXcbConnection::getSelectionOwner(xcb_atom_t atom) const { - return Q_XCB_REPLY(xcb_get_selection_owner, xcb_connection(), atom)->owner; + auto reply = Q_XCB_REPLY(xcb_get_selection_owner, xcb_connection(), atom); + if (!reply) { + qCDebug(lcQpaXcb) << "failed to query selection owner"; + return XCB_NONE; + } + + return reply->owner; } xcb_window_t QXcbConnection::getQtSelectionOwner() diff --git a/src/plugins/platforms/xcb/qxcbconnection_basic.cpp b/src/plugins/platforms/xcb/qxcbconnection_basic.cpp index af72285135..bdd7e2d98a 100644 --- a/src/plugins/platforms/xcb/qxcbconnection_basic.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection_basic.cpp @@ -183,7 +183,13 @@ xcb_atom_t QXcbBasicConnection::internAtom(const char *name) if (!name || *name == 0) return XCB_NONE; - return Q_XCB_REPLY(xcb_intern_atom, m_xcbConnection, false, strlen(name), name)->atom; + auto reply = Q_XCB_REPLY(xcb_intern_atom, m_xcbConnection, false, strlen(name), name); + if (!reply) { + qCDebug(lcQpaXcb) << "failed to query intern atom: " << name; + return XCB_NONE; + } + + return reply->atom; } QByteArray QXcbBasicConnection::atomName(xcb_atom_t atom) diff --git a/src/plugins/platforms/xcb/qxcbconnection_screens.cpp b/src/plugins/platforms/xcb/qxcbconnection_screens.cpp index 4e631beb25..9eb365597b 100644 --- a/src/plugins/platforms/xcb/qxcbconnection_screens.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection_screens.cpp @@ -290,6 +290,8 @@ void QXcbConnection::initializeScreens() // RRGetScreenResources in this case. auto resources_current = Q_XCB_REPLY(xcb_randr_get_screen_resources_current, xcb_connection(), xcbScreen->root); + decltype(Q_XCB_REPLY(xcb_randr_get_screen_resources, + xcb_connection(), xcbScreen->root)) resources; if (!resources_current) { qWarning("failed to get the current screen resources"); } else { @@ -300,8 +302,8 @@ void QXcbConnection::initializeScreens() timestamp = resources_current->config_timestamp; outputs = xcb_randr_get_screen_resources_current_outputs(resources_current.get()); } else { - auto resources = Q_XCB_REPLY(xcb_randr_get_screen_resources, - xcb_connection(), xcbScreen->root); + resources = Q_XCB_REPLY(xcb_randr_get_screen_resources, + xcb_connection(), xcbScreen->root); if (!resources) { qWarning("failed to get the screen resources"); } else { diff --git a/src/plugins/platforms/xcb/qxcbdrag.cpp b/src/plugins/platforms/xcb/qxcbdrag.cpp index 1ce947165d..602e06adab 100644 --- a/src/plugins/platforms/xcb/qxcbdrag.cpp +++ b/src/plugins/platforms/xcb/qxcbdrag.cpp @@ -216,6 +216,22 @@ void QXcbDrag::endDrag() initiatorWindow.clear(); } +Qt::DropAction QXcbDrag::defaultAction(Qt::DropActions possibleActions, Qt::KeyboardModifiers modifiers) const +{ + if (currentDrag() || drop_actions.isEmpty()) + return QBasicDrag::defaultAction(possibleActions, modifiers); + + return toDropAction(drop_actions.first()); +} + +void QXcbDrag::handlePropertyNotifyEvent(const xcb_property_notify_event_t *event) +{ + if (event->window != xdnd_dragsource || event->atom != atom(QXcbAtom::XdndActionList)) + return; + + readActionList(); +} + static bool windowInteractsWithPosition(xcb_connection_t *connection, const QPoint & pos, xcb_window_t w, xcb_shape_sk_t shapeType) { @@ -465,16 +481,20 @@ void QXcbDrag::move(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardMod move.data.data32[1] = 0; // flags move.data.data32[2] = (globalPos.x() << 16) + globalPos.y(); move.data.data32[3] = connection()->time(); - move.data.data32[4] = toXdndAction(defaultAction(currentDrag()->supportedActions(), mods)); + const auto supportedActions = currentDrag()->supportedActions(); + const auto requestedAction = defaultAction(supportedActions, mods); + move.data.data32[4] = toXdndAction(requestedAction); qCDebug(lcQpaXDnd) << "sending XdndPosition to target:" << target; source_time = connection()->time(); - if (w) + if (w) { handle_xdnd_position(w, &move, b, mods); - else + } else { + setActionList(requestedAction, supportedActions); xcb_send_event(xcb_connection(), false, proxy_target, XCB_EVENT_MASK_NO_EVENT, (const char *)&move); + } } static const bool isUnity = qgetenv("XDG_CURRENT_DESKTOP").toLower() == "unity"; @@ -555,6 +575,16 @@ Qt::DropAction QXcbDrag::toDropAction(xcb_atom_t a) const return Qt::CopyAction; } +Qt::DropActions QXcbDrag::toDropActions(const QVector<xcb_atom_t> &atoms) const +{ + Qt::DropActions actions; + for (const auto actionAtom : atoms) { + if (actionAtom != atom(QXcbAtom::XdndActionAsk)) + actions |= toDropAction(actionAtom); + } + return actions; +} + xcb_atom_t QXcbDrag::toXdndAction(Qt::DropAction a) const { switch (a) { @@ -572,6 +602,60 @@ xcb_atom_t QXcbDrag::toXdndAction(Qt::DropAction a) const } } +void QXcbDrag::readActionList() +{ + drop_actions.clear(); + auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), false, xdnd_dragsource, + atom(QXcbAtom::XdndActionList), XCB_ATOM_ATOM, + 0, 1024); + if (reply && reply->type != XCB_NONE && reply->format == 32) { + int length = xcb_get_property_value_length(reply.get()) / 4; + + xcb_atom_t *atoms = (xcb_atom_t *)xcb_get_property_value(reply.get()); + for (int i = 0; i < length; ++i) + drop_actions.append(atoms[i]); + } +} + +void QXcbDrag::setActionList(Qt::DropAction requestedAction, Qt::DropActions supportedActions) +{ +#ifndef QT_NO_CLIPBOARD + QVector<xcb_atom_t> actions; + if (requestedAction != Qt::IgnoreAction) + actions.append(toXdndAction(requestedAction)); + + auto checkAppend = [this, requestedAction, supportedActions, &actions](Qt::DropAction action) { + if (requestedAction != action && supportedActions & action) + actions.append(toXdndAction(action)); + }; + + checkAppend(Qt::CopyAction); + checkAppend(Qt::MoveAction); + checkAppend(Qt::LinkAction); + + if (current_actions != actions) { + xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, connection()->clipboard()->owner(), + atom(QXcbAtom::XdndActionList), + XCB_ATOM_ATOM, 32, actions.size(), actions.constData()); + current_actions = actions; + } +#endif +} + +void QXcbDrag::startListeningForActionListChanges() +{ + connection()->addWindowEventListener(xdnd_dragsource, this); + const uint32_t event_mask[] = { XCB_EVENT_MASK_PROPERTY_CHANGE }; + xcb_change_window_attributes(xcb_connection(), xdnd_dragsource, XCB_CW_EVENT_MASK, event_mask); +} + +void QXcbDrag::stopListeningForActionListChanges() +{ + const uint32_t event_mask[] = { XCB_EVENT_MASK_NO_EVENT }; + xcb_change_window_attributes(xcb_connection(), xdnd_dragsource, XCB_CW_EVENT_MASK, event_mask); + connection()->removeWindowEventListener(xdnd_dragsource); +} + int QXcbDrag::findTransactionByWindow(xcb_window_t window) { int at = -1; @@ -652,6 +736,9 @@ void QXcbDrag::handleEnter(QPlatformWindow *, const xcb_client_message_event_t * return; xdnd_dragsource = event->data.data32[0]; + startListeningForActionListChanges(); + readActionList(); + if (!proxy) proxy = xdndProxy(connection(), xdnd_dragsource); current_proxy_target = proxy ? proxy : xdnd_dragsource; @@ -718,7 +805,9 @@ void QXcbDrag::handle_xdnd_position(QPlatformWindow *w, const xcb_client_message supported_actions = currentDrag()->supportedActions(); } else { dropData = m_dropData; - supported_actions = Qt::DropActions(toDropAction(e->data.data32[4])); + supported_actions = toDropActions(drop_actions); + if (e->data.data32[4] != atom(QXcbAtom::XdndActionAsk)) + supported_actions |= Qt::DropActions(toDropAction(e->data.data32[4])); } auto buttons = currentDrag() ? b : connection()->queryMouseButtons(); @@ -862,8 +951,10 @@ void QXcbDrag::handleLeave(QPlatformWindow *w, const xcb_client_message_event_t // If the target receives XdndLeave, it frees any cached data and forgets the whole incident. qCDebug(lcQpaXDnd) << "target:" << event->window << "received XdndLeave"; - if (!currentWindow || w != currentWindow.data()->handle()) + if (!currentWindow || w != currentWindow.data()->handle()) { + stopListeningForActionListChanges(); return; // sanity + } // ### // if (checkEmbedded(current_embedding_widget, event)) { @@ -878,6 +969,8 @@ void QXcbDrag::handleLeave(QPlatformWindow *w, const xcb_client_message_event_t event->data.data32[0], xdnd_dragsource); } + stopListeningForActionListChanges(); + QWindowSystemInterface::handleDrag(w->window(), nullptr, QPoint(), Qt::IgnoreAction, 0, 0); } @@ -924,6 +1017,7 @@ void QXcbDrag::handleDrop(QPlatformWindow *, const xcb_client_message_event_t *e qCDebug(lcQpaXDnd) << "target:" << event->window << "received XdndDrop"; if (!currentWindow) { + stopListeningForActionListChanges(); xdnd_dragsource = 0; return; // sanity } @@ -946,7 +1040,7 @@ void QXcbDrag::handleDrop(QPlatformWindow *, const xcb_client_message_event_t *e supported_drop_actions = Qt::DropActions(l[4]); } else { dropData = m_dropData; - supported_drop_actions = accepted_drop_action; + supported_drop_actions = accepted_drop_action | toDropActions(drop_actions); } if (!dropData) @@ -981,6 +1075,8 @@ void QXcbDrag::handleDrop(QPlatformWindow *, const xcb_client_message_event_t *e xcb_send_event(xcb_connection(), false, current_proxy_target, XCB_EVENT_MASK_NO_EVENT, (char *)&finished); + stopListeningForActionListChanges(); + dropped = true; } diff --git a/src/plugins/platforms/xcb/qxcbdrag.h b/src/plugins/platforms/xcb/qxcbdrag.h index c19008c04b..aedfc90d2c 100644 --- a/src/plugins/platforms/xcb/qxcbdrag.h +++ b/src/plugins/platforms/xcb/qxcbdrag.h @@ -68,7 +68,7 @@ class QXcbScreen; class QDrag; class QShapedPixmapWindow; -class QXcbDrag : public QXcbObject, public QBasicDrag +class QXcbDrag : public QXcbObject, public QBasicDrag, public QXcbWindowEventListener { public: QXcbDrag(QXcbConnection *c); @@ -82,6 +82,10 @@ public: void drop(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods) override; void endDrag() override; + Qt::DropAction defaultAction(Qt::DropActions possibleActions, Qt::KeyboardModifiers modifiers) const override; + + void handlePropertyNotifyEvent(const xcb_property_notify_event_t *event) override; + void handleEnter(QPlatformWindow *window, const xcb_client_message_event_t *event, xcb_window_t proxy = 0); void handlePosition(QPlatformWindow *w, const xcb_client_message_event_t *event); void handleLeave(QPlatformWindow *w, const xcb_client_message_event_t *event); @@ -114,8 +118,14 @@ private: void send_leave(); Qt::DropAction toDropAction(xcb_atom_t atom) const; + Qt::DropActions toDropActions(const QVector<xcb_atom_t> &atoms) const; xcb_atom_t toXdndAction(Qt::DropAction a) const; + void readActionList(); + void setActionList(Qt::DropAction requestedAction, Qt::DropActions supportedActions); + void startListeningForActionListChanges(); + void stopListeningForActionListChanges(); + QPointer<QWindow> initiatorWindow; QPointer<QWindow> currentWindow; QPoint currentPosition; @@ -159,6 +169,9 @@ private: QVector<xcb_atom_t> drag_types; + QVector<xcb_atom_t> current_actions; + QVector<xcb_atom_t> drop_actions; + struct Transaction { xcb_timestamp_t timestamp; diff --git a/src/plugins/platforms/xcb/qxcbeventqueue.cpp b/src/plugins/platforms/xcb/qxcbeventqueue.cpp index 4ca73e3048..b96bda3194 100644 --- a/src/plugins/platforms/xcb/qxcbeventqueue.cpp +++ b/src/plugins/platforms/xcb/qxcbeventqueue.cpp @@ -226,6 +226,8 @@ void QXcbEventQueue::run() }; while (!m_closeConnectionDetected && (event = xcb_wait_for_event(connection))) { + // This lock can block only if there are users of waitForNewEvents(). + // Currently only the clipboard implementation relies on it. m_newEventsMutex.lock(); enqueueEvent(event); while (!m_closeConnectionDetected && (event = xcb_poll_for_queued_event(connection))) @@ -350,12 +352,12 @@ bool QXcbEventQueue::peekEventQueue(PeekerCallback peeker, void *peekerData, return result; } -void QXcbEventQueue::waitForNewEvents(unsigned long time) +void QXcbEventQueue::waitForNewEvents(const QXcbEventNode *sinceFlushedTail, + unsigned long time) { QMutexLocker locker(&m_newEventsMutex); - QXcbEventNode *tailBeforeFlush = m_flushedTail; flushBufferedEvents(); - if (tailBeforeFlush != m_flushedTail) + if (sinceFlushedTail != m_flushedTail) return; m_newEventsCondition.wait(&m_newEventsMutex, time); } diff --git a/src/plugins/platforms/xcb/qxcbeventqueue.h b/src/plugins/platforms/xcb/qxcbeventqueue.h index 11d0b8e963..c8f09ed9e3 100644 --- a/src/plugins/platforms/xcb/qxcbeventqueue.h +++ b/src/plugins/platforms/xcb/qxcbeventqueue.h @@ -106,7 +106,9 @@ public: bool peekEventQueue(PeekerCallback peeker, void *peekerData = nullptr, PeekOptions option = PeekDefault, qint32 peekerId = -1); - void waitForNewEvents(unsigned long time = ULONG_MAX); + const QXcbEventNode *flushedTail() const { return m_flushedTail; } + void waitForNewEvents(const QXcbEventNode *sinceFlushedTail, + unsigned long time = ULONG_MAX); private: QXcbEventNode *qXcbEventNodeFactory(xcb_generic_event_t *event); diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp index ed9e87a036..f0ed49ecc0 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.cpp +++ b/src/plugins/platforms/xcb/qxcbintegration.cpp @@ -137,8 +137,6 @@ QXcbIntegration::QXcbIntegration(const QStringList ¶meters, int &argc, char m_instance = this; qApp->setAttribute(Qt::AA_CompressHighFrequencyEvents, true); - QWindowSystemInterface::setPlatformFiltersEvents(true); - qRegisterMetaType<QXcbWindow*>(); #if QT_CONFIG(xcb_xlib) XInitThreads(); diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index 3bfcbf2adb..a6d2054110 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -933,6 +933,8 @@ QXcbWindow::NetWmStates QXcbWindow::netWmStates() result |= NetWmStateStaysOnTop; if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_DEMANDS_ATTENTION))) result |= NetWmStateDemandsAttention; + if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_HIDDEN))) + result |= NetWmStateHidden; } else { qCDebug(lcQpaXcb, "getting net wm state (%x), empty\n", m_window); } @@ -1104,6 +1106,9 @@ void QXcbWindow::setNetWmStateOnUnmappedWindow() states |= NetWmStateBelow; } + if (window()->windowStates() & Qt::WindowMinimized) + states |= NetWmStateHidden; + if (window()->windowStates() & Qt::WindowFullScreen) states |= NetWmStateFullScreen; @@ -1137,6 +1142,8 @@ void QXcbWindow::setNetWmStateOnUnmappedWindow() atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_ABOVE)); if (states & NetWmStateBelow && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_BELOW))) atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_BELOW)); + if (states & NetWmStateHidden && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_HIDDEN))) + atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_HIDDEN)); if (states & NetWmStateFullScreen && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN))) atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN)); if (states & NetWmStateMaximizedHorz && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ))) @@ -1165,27 +1172,44 @@ void QXcbWindow::setWindowState(Qt::WindowStates state) if (state == m_windowState) return; - if ((m_windowState & Qt::WindowMinimized) && !(state & Qt::WindowMinimized)) { + // unset old state + if (m_windowState & Qt::WindowMinimized) xcb_map_window(xcb_connection(), m_window); - } else if (!(m_windowState & Qt::WindowMinimized) && (state & Qt::WindowMinimized)) { - xcb_client_message_event_t event; + if (m_windowState & Qt::WindowMaximized) + setNetWmState(false, + atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ), + atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_VERT)); + if (m_windowState & Qt::WindowFullScreen) + setNetWmState(false, atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN)); - event.response_type = XCB_CLIENT_MESSAGE; - event.format = 32; - event.sequence = 0; - event.window = m_window; - event.type = atom(QXcbAtom::WM_CHANGE_STATE); - event.data.data32[0] = XCB_ICCCM_WM_STATE_ICONIC; - event.data.data32[1] = 0; - event.data.data32[2] = 0; - event.data.data32[3] = 0; - event.data.data32[4] = 0; + // set new state + if (state & Qt::WindowMinimized) { + { + xcb_client_message_event_t event; + + event.response_type = XCB_CLIENT_MESSAGE; + event.format = 32; + event.sequence = 0; + event.window = m_window; + event.type = atom(QXcbAtom::WM_CHANGE_STATE); + event.data.data32[0] = XCB_ICCCM_WM_STATE_ICONIC; + event.data.data32[1] = 0; + event.data.data32[2] = 0; + event.data.data32[3] = 0; + event.data.data32[4] = 0; - xcb_send_event(xcb_connection(), 0, xcbScreen()->root(), - XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, - (const char *)&event); + xcb_send_event(xcb_connection(), 0, xcbScreen()->root(), + XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, + (const char *)&event); + } m_minimized = true; } + if (state & Qt::WindowMaximized) + setNetWmState(true, + atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ), + atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_VERT)); + if (state & Qt::WindowFullScreen) + setNetWmState(true, atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN)); setNetWmState(state); @@ -2236,10 +2260,16 @@ void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *ev || (data[0] == XCB_ICCCM_WM_STATE_WITHDRAWN && m_minimized)); } } - if (m_minimized) - newState = Qt::WindowMinimized; 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 + // within the screen bounds. The canonical example is that minimized windows should be in + // the _NET_WM_STATE_HIDDEN state. + if (m_minimized && (!connection()->wmSupport()->isSupportedByWM(NetWmStateHidden) + || states.testFlag(NetWmStateHidden))) + newState = Qt::WindowMinimized; + if (states & NetWmStateFullScreen) newState |= Qt::WindowFullScreen; if ((states & NetWmStateMaximizedHorz) && (states & NetWmStateMaximizedVert)) diff --git a/src/plugins/platforms/xcb/qxcbwindow.h b/src/plugins/platforms/xcb/qxcbwindow.h index 8258cc2dfa..7f86fc6bb6 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.h +++ b/src/plugins/platforms/xcb/qxcbwindow.h @@ -68,7 +68,8 @@ public: NetWmStateMaximizedVert = 0x10, NetWmStateModal = 0x20, NetWmStateStaysOnTop = 0x40, - NetWmStateDemandsAttention = 0x80 + NetWmStateDemandsAttention = 0x80, + NetWmStateHidden = 0x100 }; Q_DECLARE_FLAGS(NetWmStates, NetWmState) |