diff options
author | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2019-03-26 08:24:59 +0100 |
---|---|---|
committer | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2019-03-26 08:24:59 +0100 |
commit | 7f635d9777baf4af85a2575de123a75ec58bda78 (patch) | |
tree | 90ef3a223f8b0b1601f8e72a3adce6f4513fe2dd /src/plugins | |
parent | d0f016ebfb86fcebcf72c37c489260a0d02147e7 (diff) | |
parent | 945198fd237a83348feb4537d811565a2c2cd8e0 (diff) |
Merge remote-tracking branch 'origin/5.13' into dev
Change-Id: I38389a69411f4549fed432f1181dbe23398b34a2
Diffstat (limited to 'src/plugins')
50 files changed, 918 insertions, 230 deletions
diff --git a/src/plugins/platforminputcontexts/ibus/ibus.pro b/src/plugins/platforminputcontexts/ibus/ibus.pro index 52836bb8b6..9ba2297e38 100644 --- a/src/plugins/platforminputcontexts/ibus/ibus.pro +++ b/src/plugins/platforminputcontexts/ibus/ibus.pro @@ -1,6 +1,6 @@ TARGET = ibusplatforminputcontextplugin -QT += dbus gui-private +QT += dbus gui-private xkbcommon_support-private SOURCES += $$PWD/qibusplatforminputcontext.cpp \ $$PWD/qibusproxy.cpp \ $$PWD/qibusproxyportal.cpp \ diff --git a/src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp b/src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp index 7e66439ea7..f2429f24ff 100644 --- a/src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp +++ b/src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp @@ -51,6 +51,8 @@ #include <QtGui/private/qguiapplication_p.h> +#include <QtXkbCommonSupport/private/qxkbcommon_p.h> + #include "qibusproxy.h" #include "qibusproxyportal.h" #include "qibusinputcontextproxy.h" @@ -335,14 +337,12 @@ void QIBusPlatformInputContext::forwardKeyEvent(uint keyval, uint keycode, uint if (!input) return; - if (debug) - qDebug() << "forwardKeyEvent" << keyval << keycode << state; - QEvent::Type type = QEvent::KeyPress; if (state & IBUS_RELEASE_MASK) type = QEvent::KeyRelease; state &= ~IBUS_RELEASE_MASK; + keycode += 8; Qt::KeyboardModifiers modifiers = Qt::NoModifier; if (state & IBUS_SHIFT_MASK) @@ -354,7 +354,13 @@ void QIBusPlatformInputContext::forwardKeyEvent(uint keyval, uint keycode, uint if (state & IBUS_META_MASK) modifiers |= Qt::MetaModifier; - QKeyEvent event(type, keyval, modifiers, QString(keyval)); + int qtcode = QXkbCommon::keysymToQtKey(keyval, modifiers); + QString text = QXkbCommon::lookupStringNoKeysymTransformations(keyval); + + if (debug) + qDebug() << "forwardKeyEvent" << keyval << keycode << state << modifiers << qtcode << text; + + QKeyEvent event(type, qtcode, modifiers, keycode, keyval, state, text); QCoreApplication::sendEvent(input, &event); } diff --git a/src/plugins/platforminputcontexts/platforminputcontexts.pro b/src/plugins/platforminputcontexts/platforminputcontexts.pro index 68f6792377..56a39a49e7 100644 --- a/src/plugins/platforminputcontexts/platforminputcontexts.pro +++ b/src/plugins/platforminputcontexts/platforminputcontexts.pro @@ -1,10 +1,11 @@ TEMPLATE = subdirs QT_FOR_CONFIG += gui-private -qtHaveModule(dbus) { -!mac:!win32:SUBDIRS += ibus -} - -qtConfig(xkbcommon): SUBDIRS += compose +qtConfig(xkbcommon) { + SUBDIRS += compose + qtHaveModule(dbus) { + !macos:!win32:SUBDIRS += ibus + } +} diff --git a/src/plugins/platforms/android/qandroidplatformintegration.cpp b/src/plugins/platforms/android/qandroidplatformintegration.cpp index 763b294660..e0c437be27 100644 --- a/src/plugins/platforms/android/qandroidplatformintegration.cpp +++ b/src/plugins/platforms/android/qandroidplatformintegration.cpp @@ -173,7 +173,7 @@ QAndroidPlatformIntegration::QAndroidPlatformIntegration(const QStringList ¶ qFatal("Could not bind GL_ES API"); m_primaryScreen = new QAndroidPlatformScreen(); - screenAdded(m_primaryScreen); + QWindowSystemInterface::handleScreenAdded(m_primaryScreen); m_primaryScreen->setPhysicalSize(QSize(m_defaultPhysicalSizeWidth, m_defaultPhysicalSizeHeight)); m_primaryScreen->setSize(QSize(m_defaultScreenWidth, m_defaultScreenHeight)); m_primaryScreen->setAvailableGeometry(QRect(0, 0, m_defaultGeometryWidth, m_defaultGeometryHeight)); diff --git a/src/plugins/platforms/bsdfb/qbsdfbintegration.cpp b/src/plugins/platforms/bsdfb/qbsdfbintegration.cpp index 6a7d445e69..e4c90d26af 100644 --- a/src/plugins/platforms/bsdfb/qbsdfbintegration.cpp +++ b/src/plugins/platforms/bsdfb/qbsdfbintegration.cpp @@ -53,6 +53,7 @@ #include <QtGui/private/qguiapplication_p.h> #include <qpa/qplatforminputcontext.h> #include <qpa/qplatforminputcontextfactory_p.h> +#include <qpa/qwindowsysteminterface.h> #if QT_CONFIG(tslib) #include <QtInputSupport/private/qtslib_p.h> @@ -69,13 +70,13 @@ QBsdFbIntegration::QBsdFbIntegration(const QStringList ¶mList) QBsdFbIntegration::~QBsdFbIntegration() { - destroyScreen(m_primaryScreen.take()); + QWindowSystemInterface::handleScreenRemoved(m_primaryScreen.take()); } void QBsdFbIntegration::initialize() { if (m_primaryScreen->initialize()) - screenAdded(m_primaryScreen.data()); + QWindowSystemInterface::handleScreenAdded(m_primaryScreen.data()); else qWarning("bsdfb: Failed to initialize screen"); diff --git a/src/plugins/platforms/cocoa/cocoa.pro b/src/plugins/platforms/cocoa/cocoa.pro index 8d65cf328f..083b7c1655 100644 --- a/src/plugins/platforms/cocoa/cocoa.pro +++ b/src/plugins/platforms/cocoa/cocoa.pro @@ -33,6 +33,7 @@ SOURCES += main.mm \ qcocoaintrospection.mm \ qcocoakeymapper.mm \ qcocoamimetypes.mm \ + qiosurfacegraphicsbuffer.mm \ messages.cpp HEADERS += qcocoaintegration.h \ @@ -67,6 +68,7 @@ HEADERS += qcocoaintegration.h \ qcocoaintrospection.h \ qcocoakeymapper.h \ messages.h \ + qiosurfacegraphicsbuffer.h \ qcocoamimetypes.h qtConfig(opengl.*) { @@ -81,7 +83,7 @@ qtConfig(vulkan) { RESOURCES += qcocoaresources.qrc -LIBS += -framework AppKit -framework CoreServices -framework Carbon -framework IOKit -framework QuartzCore -framework CoreVideo -framework Metal -lcups +LIBS += -framework AppKit -framework CoreServices -framework Carbon -framework IOKit -framework QuartzCore -framework CoreVideo -framework Metal -framework IOSurface -lcups QT += \ core-private gui-private \ diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.h b/src/plugins/platforms/cocoa/qcocoabackingstore.h index b4cd506513..508f24d578 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.h +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.h @@ -44,13 +44,16 @@ #include <private/qcore_mac_p.h> +#include <QScopedPointer> +#include "qiosurfacegraphicsbuffer.h" + QT_BEGIN_NAMESPACE -class QCocoaBackingStore : public QRasterBackingStore +class QNSWindowBackingStore : public QRasterBackingStore { public: - QCocoaBackingStore(QWindow *window); - ~QCocoaBackingStore(); + QNSWindowBackingStore(QWindow *window); + ~QNSWindowBackingStore(); void flush(QWindow *, const QRegion &, const QPoint &) override; @@ -60,6 +63,49 @@ private: void redrawRoundedBottomCorners(CGRect) const; }; +class QCALayerBackingStore : public QPlatformBackingStore +{ +public: + QCALayerBackingStore(QWindow *window); + ~QCALayerBackingStore(); + + void resize(const QSize &size, const QRegion &staticContents) override; + + void beginPaint(const QRegion ®ion) override; + QPaintDevice *paintDevice() override; + void endPaint() override; + + void flush(QWindow *, const QRegion &, const QPoint &) override; + void composeAndFlush(QWindow *window, const QRegion ®ion, const QPoint &offset, + QPlatformTextureList *textures, bool translucentBackground) override; + + QPlatformGraphicsBuffer *graphicsBuffer() const override; + +private: + QSize m_requestedSize; + QRegion m_paintedRegion; + + class GraphicsBuffer : public QIOSurfaceGraphicsBuffer + { + public: + GraphicsBuffer(const QSize &size, qreal devicePixelRatio, + const QPixelFormat &format, QCFType<CGColorSpaceRef> colorSpace); + + QRegion dirtyRegion; // In unscaled coordinates + QImage *asImage(); + + private: + qreal m_devicePixelRatio; + QImage m_image; + }; + + void ensureBackBuffer(); + bool recreateBackBufferIfNeeded(); + bool prepareForFlush(); + + std::list<std::unique_ptr<GraphicsBuffer>> m_buffers; +}; + QT_END_NAMESPACE #endif diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm index 81a0a7d040..8e4e928bc5 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.mm +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm @@ -42,24 +42,28 @@ #include "qcocoawindow.h" #include "qcocoahelpers.h" +#include <QtCore/qmath.h> + +#include <QuartzCore/CATransaction.h> + QT_BEGIN_NAMESPACE -QCocoaBackingStore::QCocoaBackingStore(QWindow *window) +QNSWindowBackingStore::QNSWindowBackingStore(QWindow *window) : QRasterBackingStore(window) { } -QCocoaBackingStore::~QCocoaBackingStore() +QNSWindowBackingStore::~QNSWindowBackingStore() { } -bool QCocoaBackingStore::windowHasUnifiedToolbar() const +bool QNSWindowBackingStore::windowHasUnifiedToolbar() const { Q_ASSERT(window()->handle()); return static_cast<QCocoaWindow *>(window()->handle())->m_drawContentBorderGradient; } -QImage::Format QCocoaBackingStore::format() const +QImage::Format QNSWindowBackingStore::format() const { if (windowHasUnifiedToolbar()) return QImage::Format_ARGB32_Premultiplied; @@ -78,7 +82,7 @@ QImage::Format QCocoaBackingStore::format() const coordinates, and the \a offset will be the child window's offset in relation to the backingstore's top level window. */ -void QCocoaBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoint &offset) +void QNSWindowBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoint &offset) { if (m_image.isNull()) return; @@ -103,131 +107,113 @@ void QCocoaBackingStore::flush(QWindow *window, const QRegion ®ion, const QPo qCDebug(lcQpaBackingStore) << "Flushing" << region << "of" << view << qPrintable(targetViewDescription); } - // Prevent potentially costly color conversion by assigning the display color space - // to the backingstore image. This does not copy the underlying image data. - CGColorSpaceRef displayColorSpace = view.window.screen.colorSpace.CGColorSpace; - QCFType<CGImageRef> cgImage = CGImageCreateCopyWithColorSpace( - QCFType<CGImageRef>(m_image.toCGImage()), displayColorSpace); - - if (view.layer) { - // In layer-backed mode, locking focus on a view does not give the right - // view transformation, and doesn't give us a graphics context to render - // via when drawing outside of the display cycle. Instead we tell AppKit - // that we want to update the layer's content, via [NSView wantsUpdateLayer], - // which result in AppKit not creating a backingstore for each layer, and - // we then directly set the layer's backingstore (content) to our backingstore, - // masked to the part of the subview that is relevant. - // FIXME: Figure out if there's a way to do partial updates - view.layer.contents = (__bridge id)static_cast<CGImageRef>(cgImage); - if (view != topLevelView) { - const CGSize topLevelSize = topLevelView.bounds.size; - view.layer.contentsRect = CGRectApplyAffineTransform( - [view convertRect:view.bounds toView:topLevelView], - // The contentsRect is in unit coordinate system - CGAffineTransformMakeScale(1.0 / topLevelSize.width, 1.0 / topLevelSize.height)); - } - } else { - // Normally a NSView is drawn via drawRect, as part of the display cycle in the - // main runloop, via setNeedsDisplay and friends. AppKit will lock focus on each - // individual view, starting with the top level and then traversing any subviews, - // calling drawRect for each of them. This pull model results in expose events - // sent to Qt, which result in drawing to the backingstore and flushing it. - // Qt may also decide to paint and flush the backingstore via e.g. timers, - // or other events such as mouse events, in which case we're in a push model. - // If there is no focused view, it means we're in the latter case, and need - // to manually flush the NSWindow after drawing to its graphic context. - const bool drawingOutsideOfDisplayCycle = ![NSView focusView]; - - // We also need to ensure the flushed view has focus, so that the graphics - // context is set up correctly (coordinate system, clipping, etc). Outside - // of the normal display cycle there is no focused view, as explained above, - // so we have to handle it manually. There's also a corner case inside the - // normal display cycle due to way QWidgetBackingStore composits native child - // widgets, where we'll get a flush of a native child during the drawRect of - // its parent/ancestor, and the parent/ancestor being the one locked by AppKit. - // In this case we also need to lock and unlock focus manually. - const bool shouldHandleViewLockManually = [NSView focusView] != view; - if (shouldHandleViewLockManually && ![view lockFocusIfCanDraw]) { - qWarning() << "failed to lock focus of" << view; - return; - } + // Normally a NSView is drawn via drawRect, as part of the display cycle in the + // main runloop, via setNeedsDisplay and friends. AppKit will lock focus on each + // individual view, starting with the top level and then traversing any subviews, + // calling drawRect for each of them. This pull model results in expose events + // sent to Qt, which result in drawing to the backingstore and flushing it. + // Qt may also decide to paint and flush the backingstore via e.g. timers, + // or other events such as mouse events, in which case we're in a push model. + // If there is no focused view, it means we're in the latter case, and need + // to manually flush the NSWindow after drawing to its graphic context. + const bool drawingOutsideOfDisplayCycle = ![NSView focusView]; + + // We also need to ensure the flushed view has focus, so that the graphics + // context is set up correctly (coordinate system, clipping, etc). Outside + // of the normal display cycle there is no focused view, as explained above, + // so we have to handle it manually. There's also a corner case inside the + // normal display cycle due to way QWidgetBackingStore composits native child + // widgets, where we'll get a flush of a native child during the drawRect of + // its parent/ancestor, and the parent/ancestor being the one locked by AppKit. + // In this case we also need to lock and unlock focus manually. + const bool shouldHandleViewLockManually = [NSView focusView] != view; + if (shouldHandleViewLockManually && ![view lockFocusIfCanDraw]) { + qWarning() << "failed to lock focus of" << view; + return; + } - const qreal devicePixelRatio = m_image.devicePixelRatio(); + const qreal devicePixelRatio = m_image.devicePixelRatio(); - // If the flushed window is a content view, and we're filling the drawn area - // completely, or it doesn't have a window background we need to preserve, - // we can get away with copying instead of blending the backing store. - QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(window->handle()); - const NSCompositingOperation compositingOperation = cocoaWindow->isContentView() - && (cocoaWindow->isOpaque() || view.window.backgroundColor == NSColor.clearColor) - ? NSCompositingOperationCopy : NSCompositingOperationSourceOver; + // If the flushed window is a content view, and we're filling the drawn area + // completely, or it doesn't have a window background we need to preserve, + // we can get away with copying instead of blending the backing store. + QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow *>(window->handle()); + const NSCompositingOperation compositingOperation = cocoaWindow->isContentView() + && (cocoaWindow->isOpaque() || view.window.backgroundColor == NSColor.clearColor) + ? NSCompositingOperationCopy : NSCompositingOperationSourceOver; #ifdef QT_DEBUG - static bool debugBackingStoreFlush = [[NSUserDefaults standardUserDefaults] - boolForKey:@"QtCocoaDebugBackingStoreFlush"]; + static bool debugBackingStoreFlush = [[NSUserDefaults standardUserDefaults] + boolForKey:@"QtCocoaDebugBackingStoreFlush"]; #endif - // ------------------------------------------------------------------------- + // ------------------------------------------------------------------------- - // The current contexts is typically a NSWindowGraphicsContext, but can be - // NSBitmapGraphicsContext e.g. when debugging the view hierarchy in Xcode. - // If we need to distinguish things here in the future, we can use e.g. - // [NSGraphicsContext drawingToScreen], or the attributes of the context. - NSGraphicsContext *graphicsContext = [NSGraphicsContext currentContext]; - Q_ASSERT_X(graphicsContext, "QCocoaBackingStore", - "Focusing the view should give us a current graphics context"); + // The current contexts is typically a NSWindowGraphicsContext, but can be + // NSBitmapGraphicsContext e.g. when debugging the view hierarchy in Xcode. + // If we need to distinguish things here in the future, we can use e.g. + // [NSGraphicsContext drawingToScreen], or the attributes of the context. + NSGraphicsContext *graphicsContext = [NSGraphicsContext currentContext]; + Q_ASSERT_X(graphicsContext, "QCocoaBackingStore", + "Focusing the view should give us a current graphics context"); - // Create temporary image to use for blitting, without copying image data - NSImage *backingStoreImage = [[[NSImage alloc] initWithCGImage:cgImage size:NSZeroSize] autorelease]; + // Prevent potentially costly color conversion by assigning the display color space + // to the backingstore image. This does not copy the underlying image data. + CGColorSpaceRef displayColorSpace = view.window.screen.colorSpace.CGColorSpace; + QCFType<CGImageRef> cgImage = CGImageCreateCopyWithColorSpace( + QCFType<CGImageRef>(m_image.toCGImage()), displayColorSpace); - QRegion clippedRegion = region; - for (QWindow *w = window; w; w = w->parent()) { - if (!w->mask().isEmpty()) { - clippedRegion &= w == window ? w->mask() - : w->mask().translated(window->mapFromGlobal(w->mapToGlobal(QPoint(0, 0)))); - } + // Create temporary image to use for blitting, without copying image data + NSImage *backingStoreImage = [[[NSImage alloc] initWithCGImage:cgImage size:NSZeroSize] autorelease]; + + QRegion clippedRegion = region; + for (QWindow *w = window; w; w = w->parent()) { + if (!w->mask().isEmpty()) { + clippedRegion &= w == window ? w->mask() + : w->mask().translated(window->mapFromGlobal(w->mapToGlobal(QPoint(0, 0)))); } + } - for (const QRect &viewLocalRect : clippedRegion) { - QPoint backingStoreOffset = viewLocalRect.topLeft() + offset; - QRect backingStoreRect(backingStoreOffset * devicePixelRatio, viewLocalRect.size() * devicePixelRatio); - if (graphicsContext.flipped) // Flip backingStoreRect to match graphics context - backingStoreRect.moveTop(m_image.height() - (backingStoreRect.y() + backingStoreRect.height())); + for (const QRect &viewLocalRect : clippedRegion) { + QPoint backingStoreOffset = viewLocalRect.topLeft() + offset; + QRect backingStoreRect(backingStoreOffset * devicePixelRatio, viewLocalRect.size() * devicePixelRatio); + if (graphicsContext.flipped) // Flip backingStoreRect to match graphics context + backingStoreRect.moveTop(m_image.height() - (backingStoreRect.y() + backingStoreRect.height())); - CGRect viewRect = viewLocalRect.toCGRect(); + CGRect viewRect = viewLocalRect.toCGRect(); - if (windowHasUnifiedToolbar()) - NSDrawWindowBackground(viewRect); + if (windowHasUnifiedToolbar()) + NSDrawWindowBackground(viewRect); - [backingStoreImage drawInRect:viewRect fromRect:backingStoreRect.toCGRect() - operation:compositingOperation fraction:1.0 respectFlipped:YES hints:nil]; + [backingStoreImage drawInRect:viewRect fromRect:backingStoreRect.toCGRect() + operation:compositingOperation fraction:1.0 respectFlipped:YES hints:nil]; #ifdef QT_DEBUG - if (Q_UNLIKELY(debugBackingStoreFlush)) { - [[NSColor colorWithCalibratedRed:drand48() green:drand48() blue:drand48() alpha:0.3] set]; - [NSBezierPath fillRect:viewRect]; - - if (drawingOutsideOfDisplayCycle) { - [[[NSColor magentaColor] colorWithAlphaComponent:0.5] set]; - [NSBezierPath strokeLineFromPoint:viewLocalRect.topLeft().toCGPoint() - toPoint:viewLocalRect.bottomRight().toCGPoint()]; - } + if (Q_UNLIKELY(debugBackingStoreFlush)) { + [[NSColor colorWithCalibratedRed:drand48() green:drand48() blue:drand48() alpha:0.3] set]; + [NSBezierPath fillRect:viewRect]; + + if (drawingOutsideOfDisplayCycle) { + [[[NSColor magentaColor] colorWithAlphaComponent:0.5] set]; + [NSBezierPath strokeLineFromPoint:viewLocalRect.topLeft().toCGPoint() + toPoint:viewLocalRect.bottomRight().toCGPoint()]; } -#endif } +#endif + } - // ------------------------------------------------------------------------- + // ------------------------------------------------------------------------- - if (shouldHandleViewLockManually) - [view unlockFocus]; + if (shouldHandleViewLockManually) + [view unlockFocus]; - if (drawingOutsideOfDisplayCycle) { - redrawRoundedBottomCorners([view convertRect:region.boundingRect().toCGRect() toView:nil]); - [view.window flushWindow]; - } + if (drawingOutsideOfDisplayCycle) { + redrawRoundedBottomCorners([view convertRect:region.boundingRect().toCGRect() toView:nil]); + [view.window flushWindow]; } - // Done flushing to either CALayer or NSWindow backingstore + + // Done flushing to NSWindow backingstore QCocoaWindow *topLevelCocoaWindow = static_cast<QCocoaWindow *>(topLevelWindow->handle()); if (Q_UNLIKELY(topLevelCocoaWindow->m_needsInvalidateShadow)) { @@ -251,7 +237,7 @@ void QCocoaBackingStore::flush(QWindow *window, const QRegion ®ion, const QPo https://trac.webkit.org/changeset/85376/webkit */ -void QCocoaBackingStore::redrawRoundedBottomCorners(CGRect windowRect) const +void QNSWindowBackingStore::redrawRoundedBottomCorners(CGRect windowRect) const { #if !defined(QT_APPLE_NO_PRIVATE_APIS) Q_ASSERT(this->window()->handle()); @@ -285,4 +271,345 @@ void QCocoaBackingStore::redrawRoundedBottomCorners(CGRect windowRect) const #endif } +// ---------------------------------------------------------------------------- + +// https://stackoverflow.com/a/52722575/2761869 +template<class R> +struct backwards_t { + R r; + constexpr auto begin() const { using std::rbegin; return rbegin(r); } + constexpr auto begin() { using std::rbegin; return rbegin(r); } + constexpr auto end() const { using std::rend; return rend(r); } + constexpr auto end() { using std::rend; return rend(r); } +}; +template<class R> +constexpr backwards_t<R> backwards(R&& r) { return {std::forward<R>(r)}; } + +QCALayerBackingStore::QCALayerBackingStore(QWindow *window) + : QPlatformBackingStore(window) +{ + qCDebug(lcQpaBackingStore) << "Creating QCALayerBackingStore for" << window; + m_buffers.resize(1); +} + +QCALayerBackingStore::~QCALayerBackingStore() +{ +} + +void QCALayerBackingStore::resize(const QSize &size, const QRegion &staticContents) +{ + qCDebug(lcQpaBackingStore) << "Resize requested to" << size; + + if (!staticContents.isNull()) + qCWarning(lcQpaBackingStore) << "QCALayerBackingStore does not support static contents"; + + m_requestedSize = size; +} + +void QCALayerBackingStore::beginPaint(const QRegion ®ion) +{ + Q_UNUSED(region); + + QMacAutoReleasePool pool; + + qCInfo(lcQpaBackingStore) << "Beginning paint of" << region << "into backingstore of" << m_requestedSize; + + ensureBackBuffer(); // Find an unused back buffer, or reserve space for a new one + + const bool bufferWasRecreated = recreateBackBufferIfNeeded(); + + m_buffers.back()->lock(QPlatformGraphicsBuffer::SWWriteAccess); + + // 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()) { + qCDebug(lcQpaBackingStore) << "Clearing" << region << "before use"; + QPainter painter(m_buffers.back()->asImage()); + painter.setCompositionMode(QPainter::CompositionMode_Source); + for (const QRect &rect : region) + painter.fillRect(rect, Qt::transparent); + } + + m_paintedRegion += region; +} + +void QCALayerBackingStore::ensureBackBuffer() +{ + if (window()->format().swapBehavior() == QSurfaceFormat::SingleBuffer) + return; + + // The current back buffer may have been assigned to a layer in a previous flush, + // but we deferred the swap. Do it now if the surface has been picked up by CA. + if (m_buffers.back() && m_buffers.back()->isInUse() && m_buffers.back() != m_buffers.front()) { + qCInfo(lcQpaBackingStore) << "Back buffer has been picked up by CA, swapping to front"; + std::swap(m_buffers.back(), m_buffers.front()); + } + + if (Q_UNLIKELY(lcQpaBackingStore().isDebugEnabled())) { + // ┌───────┬───────┬───────┬─────┬──────┐ + // │ front ┊ spare ┊ spare ┊ ... ┊ back │ + // └───────┴───────┴───────┴─────┴──────┘ + for (const auto &buffer : m_buffers) { + qCDebug(lcQpaBackingStore).nospace() << " " + << (buffer == m_buffers.front() ? "front" : + buffer == m_buffers.back() ? " back" : + "spare" + ) << ": " << buffer.get(); + } + } + + // Ensure our back buffer is ready to draw into. If not, find a buffer that + // is not in use, or reserve space for a new buffer if none can be found. + for (auto &buffer : backwards(m_buffers)) { + if (!buffer || !buffer->isInUse()) { + // Buffer is okey to use, swap if necessary + if (buffer != m_buffers.back()) + std::swap(buffer, m_buffers.back()); + qCDebug(lcQpaBackingStore) << "Using back buffer" << m_buffers.back().get(); + + static const int kMaxSwapChainDepth = 3; + if (m_buffers.size() > kMaxSwapChainDepth) { + qCDebug(lcQpaBackingStore) << "Reducing swap chain depth to" << kMaxSwapChainDepth; + m_buffers.erase(std::next(m_buffers.begin(), 1), std::prev(m_buffers.end(), 2)); + } + + break; + } else if (buffer == m_buffers.front()) { + // We've exhausted the available buffers, make room for a new one + const int swapChainDepth = m_buffers.size() + 1; + qCDebug(lcQpaBackingStore) << "Available buffers exhausted, increasing swap chain depth to" << swapChainDepth; + m_buffers.resize(swapChainDepth); + break; + } + } + + Q_ASSERT(!m_buffers.back() || !m_buffers.back()->isInUse()); +} + +// Disabled until performance issue on 5K iMac Pro has been investigated further, +// as rounding up during resize will typically result in full screen buffer sizes +// and low frame rate also for smaller window sizes. +#define USE_LAZY_BUFFER_ALLOCATION_DURING_LIVE_WINDOW_RESIZE 0 + +bool QCALayerBackingStore::recreateBackBufferIfNeeded() +{ + const qreal devicePixelRatio = window()->devicePixelRatio(); + QSize requestedBufferSize = m_requestedSize * devicePixelRatio; + + const NSView *backingStoreView = static_cast<QCocoaWindow *>(window()->handle())->view(); + Q_UNUSED(backingStoreView); + + auto bufferSizeMismatch = [&](const QSize requested, const QSize actual) { +#if USE_LAZY_BUFFER_ALLOCATION_DURING_LIVE_WINDOW_RESIZE + if (backingStoreView.inLiveResize) { + // Prevent over-eager buffer allocation during window resize by reusing larger buffers + return requested.width() > actual.width() || requested.height() > actual.height(); + } +#endif + return requested != actual; + }; + + if (!m_buffers.back() || bufferSizeMismatch(requestedBufferSize, m_buffers.back()->size())) { +#if USE_LAZY_BUFFER_ALLOCATION_DURING_LIVE_WINDOW_RESIZE + if (backingStoreView.inLiveResize) { + // Prevent over-eager buffer allocation during window resize by rounding up + QSize nativeScreenSize = window()->screen()->geometry().size() * devicePixelRatio; + requestedBufferSize = QSize(qNextPowerOfTwo(requestedBufferSize.width()), + qNextPowerOfTwo(requestedBufferSize.height())).boundedTo(nativeScreenSize); + } +#endif + + qCInfo(lcQpaBackingStore) << "Creating surface of" << requestedBufferSize + << "based on requested" << m_requestedSize << "and dpr =" << devicePixelRatio; + + static auto pixelFormat = QImage::toPixelFormat(QImage::Format_ARGB32_Premultiplied); + + NSView *view = static_cast<QCocoaWindow *>(window()->handle())->view(); + auto colorSpace = QCFType<CGColorSpaceRef>::constructFromGet(view.window.screen.colorSpace.CGColorSpace); + + m_buffers.back().reset(new GraphicsBuffer(requestedBufferSize, devicePixelRatio, pixelFormat, colorSpace)); + return true; + } + + return false; +} + +QPaintDevice *QCALayerBackingStore::paintDevice() +{ + Q_ASSERT(m_buffers.back()); + return m_buffers.back()->asImage(); +} + +void QCALayerBackingStore::endPaint() +{ + qCInfo(lcQpaBackingStore) << "Paint ended with painted region" << m_paintedRegion; + m_buffers.back()->unlock(); +} + +void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion ®ion, const QPoint &offset) +{ + Q_UNUSED(region); + Q_UNUSED(offset); + + if (!prepareForFlush()) + return; + + QMacAutoReleasePool pool; + + NSView *backingStoreView = static_cast<QCocoaWindow *>(window()->handle())->view(); + NSView *flushedView = static_cast<QCocoaWindow *>(flushedWindow->handle())->view(); + + id backBufferSurface = (__bridge id)m_buffers.back()->surface(); + if (flushedView.layer.contents == backBufferSurface) { + // We've managed to paint to the back buffer again before Core Animation had time + // to flush the transaction and persist the layer changes to the window server. + // The layer already knows about the back buffer, and we don't need to re-apply + // it to pick up the surface changes, so bail out early. + qCInfo(lcQpaBackingStore).nospace() << "Skipping flush of " << flushedView + << ", layer already reflects back buffer"; + return; + } + + // Trigger a new display cycle if there isn't one. This ensures that our layer updates + // 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; + + if (window()->format().swapBehavior() == QSurfaceFormat::SingleBuffer) { + // The private API [CALayer reloadValueForKeyPath:@"contents"] would be preferable, + // but barring any side effects or performance issues we opt for the hammer for now. + flushedView.layer.contents = nil; + } + + qCInfo(lcQpaBackingStore) << "Flushing" << backBufferSurface + << "to" << flushedView.layer << "of" << flushedView; + + flushedView.layer.contents = backBufferSurface; + + if (flushedView != backingStoreView) { + const CGSize backingStoreSize = backingStoreView.bounds.size; + flushedView.layer.contentsRect = CGRectApplyAffineTransform( + [flushedView convertRect:flushedView.bounds toView:backingStoreView], + // The contentsRect is in unit coordinate system + CGAffineTransformMakeScale(1.0 / backingStoreSize.width, 1.0 / backingStoreSize.height)); + } + + // 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 + // surface is in use, and if so swap to an unused surface as the new back buffer. + + // Note: Ideally CoreAnimation would mark a surface as in use the moment we assign + // it to a layer, but as that's not the case we may end up painting to the same back + // buffer once more if we are painting faster than CA can ship the surfaces over to + // the window server. +} + +void QCALayerBackingStore::composeAndFlush(QWindow *window, const QRegion ®ion, const QPoint &offset, + QPlatformTextureList *textures, bool translucentBackground) +{ + if (!prepareForFlush()) + return; + + QPlatformBackingStore::composeAndFlush(window, region, offset, textures, translucentBackground); +} + +QPlatformGraphicsBuffer *QCALayerBackingStore::graphicsBuffer() const +{ + return m_buffers.back().get(); +} + +bool QCALayerBackingStore::prepareForFlush() +{ + if (!m_buffers.back()) { + qCWarning(lcQpaBackingStore) << "Tried to flush backingstore without painting to it first"; + return false; + } + + // Update dirty state of buffers based on what was painted. The back buffer will be + // less dirty, since we painted to it, while other buffers will become more dirty. + // This allows us to minimize copies between front and back buffers on swap in the + // cases where the painted region overlaps with the previous frame (front buffer). + for (const auto &buffer : m_buffers) { + if (buffer == m_buffers.back()) + buffer->dirtyRegion -= m_paintedRegion; + else + buffer->dirtyRegion += m_paintedRegion; + } + + // After painting, the back buffer is only guaranteed to have content for the painted + // region, and may still have dirty areas that need to be synced up with the front buffer, + // if we have one. We know that the front buffer is always up to date. + if (!m_buffers.back()->dirtyRegion.isEmpty() && m_buffers.front() != m_buffers.back()) { + QRegion preserveRegion = m_buffers.back()->dirtyRegion; + qCDebug(lcQpaBackingStore) << "Preserving" << preserveRegion << "from front to back buffer"; + + m_buffers.front()->lock(QPlatformGraphicsBuffer::SWReadAccess); + const QImage *frontBuffer = m_buffers.front()->asImage(); + + const QRect frontSurfaceBounds(QPoint(0, 0), m_buffers.front()->size()); + const qreal sourceDevicePixelRatio = frontBuffer->devicePixelRatio(); + + m_buffers.back()->lock(QPlatformGraphicsBuffer::SWWriteAccess); + QPainter painter(m_buffers.back()->asImage()); + painter.setCompositionMode(QPainter::CompositionMode_Source); + + // Let painter operate in device pixels, to make it easier to compare coordinates + const qreal targetDevicePixelRatio = painter.device()->devicePixelRatio(); + painter.scale(1.0 / targetDevicePixelRatio, 1.0 / targetDevicePixelRatio); + + for (const QRect &rect : preserveRegion) { + QRect sourceRect(rect.topLeft() * sourceDevicePixelRatio, rect.size() * sourceDevicePixelRatio); + QRect targetRect(rect.topLeft() * targetDevicePixelRatio, rect.size() * targetDevicePixelRatio); + +#ifdef QT_DEBUG + if (Q_UNLIKELY(!frontSurfaceBounds.contains(sourceRect.bottomRight()))) { + qCWarning(lcQpaBackingStore) << "Front buffer too small to preserve" + << QRegion(sourceRect).subtracted(frontSurfaceBounds); + } +#endif + painter.drawImage(targetRect, *frontBuffer, sourceRect); + } + + m_buffers.back()->unlock(); + m_buffers.front()->unlock(); + + // The back buffer is now completely in sync, ready to be presented + m_buffers.back()->dirtyRegion = QRegion(); + } + + // Prepare for another round of painting + m_paintedRegion = QRegion(); + + return true; +} + +// ---------------------------------------------------------------------------- + +QCALayerBackingStore::GraphicsBuffer::GraphicsBuffer(const QSize &size, qreal devicePixelRatio, + const QPixelFormat &format, QCFType<CGColorSpaceRef> colorSpace) + : QIOSurfaceGraphicsBuffer(size, format, colorSpace) + , dirtyRegion(0, 0, size.width() / devicePixelRatio, size.height() / devicePixelRatio) + , m_devicePixelRatio(devicePixelRatio) +{ +} + +QImage *QCALayerBackingStore::GraphicsBuffer::asImage() +{ + if (m_image.isNull()) { + qCDebug(lcQpaBackingStore) << "Setting up paint device for" << this; + CFRetain(surface()); + m_image = QImage(data(), size().width(), size().height(), + bytesPerLine(), QImage::toImageFormat(format()), + QImageCleanupFunction(CFRelease), surface()); + m_image.setDevicePixelRatio(m_devicePixelRatio); + } + + Q_ASSERT_X(m_image.constBits() == data(), "QCALayerBackingStore", + "IOSurfaces should have have a fixed location in memory once created"); + + return &m_image; +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index affbee35b7..fb3d05d3e4 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.mm +++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm @@ -244,7 +244,7 @@ QCocoaIntegration::~QCocoaIntegration() // Delete screens in reverse order to avoid crash in case of multiple screens while (!mScreens.isEmpty()) { - destroyScreen(mScreens.takeLast()); + QWindowSystemInterface::handleScreenRemoved(mScreens.takeLast()); } clearToolbars(); @@ -304,7 +304,7 @@ void QCocoaIntegration::updateScreens() screen = new QCocoaScreen(i); mScreens.append(screen); qCDebug(lcQpaScreen) << "Adding" << screen; - screenAdded(screen); + QWindowSystemInterface::handleScreenAdded(screen); } siblings << screen; } @@ -321,7 +321,7 @@ void QCocoaIntegration::updateScreens() // Prevent stale references to NSScreen during destroy screen->m_screenIndex = -1; qCDebug(lcQpaScreen) << "Removing" << screen; - destroyScreen(screen); + QWindowSystemInterface::handleScreenRemoved(screen); } } @@ -407,7 +407,16 @@ QPlatformOpenGLContext *QCocoaIntegration::createPlatformOpenGLContext(QOpenGLCo QPlatformBackingStore *QCocoaIntegration::createPlatformBackingStore(QWindow *window) const { - return new QCocoaBackingStore(window); + QCocoaWindow *platformWindow = static_cast<QCocoaWindow*>(window->handle()); + if (!platformWindow) { + qWarning() << window << "must be created before being used with a backingstore"; + return nullptr; + } + + if (platformWindow->view().layer) + return new QCALayerBackingStore(window); + else + return new QNSWindowBackingStore(window); } QAbstractEventDispatcher *QCocoaIntegration::createEventDispatcher() const diff --git a/src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.h b/src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.h new file mode 100644 index 0000000000..872773cb7a --- /dev/null +++ b/src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** 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 QIOSURFACEGRAPHICSBUFFER_H +#define QIOSURFACEGRAPHICSBUFFER_H + +#include <qpa/qplatformgraphicsbuffer.h> +#include <private/qcore_mac_p.h> + +QT_BEGIN_NAMESPACE + +class QIOSurfaceGraphicsBuffer : public QPlatformGraphicsBuffer +{ +public: + QIOSurfaceGraphicsBuffer(const QSize &size, const QPixelFormat &format, QCFType<CGColorSpaceRef> colorSpace); + ~QIOSurfaceGraphicsBuffer(); + + const uchar *data() const override; + uchar *data() override; + int bytesPerLine() const override; + + IOSurfaceRef surface(); + bool isInUse() const; + +protected: + bool doLock(AccessTypes access, const QRect &rect) override; + void doUnlock() override; + +private: + QCFType<IOSurfaceRef> m_surface; + + friend QDebug operator<<(QDebug, const QIOSurfaceGraphicsBuffer *); +}; + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug, const QIOSurfaceGraphicsBuffer *); +#endif + +QT_END_NAMESPACE + +#endif // QIOSURFACEGRAPHICSBUFFER_H diff --git a/src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.mm b/src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.mm new file mode 100644 index 0000000000..a367487e85 --- /dev/null +++ b/src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.mm @@ -0,0 +1,188 @@ +/**************************************************************************** +** +** 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 "qiosurfacegraphicsbuffer.h" + +#include <QtCore/qdebug.h> +#include <QtCore/qloggingcategory.h> + +#include <CoreGraphics/CoreGraphics.h> +#include <IOSurface/IOSurface.h> + +// CGColorSpaceCopyPropertyList is available on 10.12 and above, +// but was only added in the 10.14 SDK, so declare it just in case. +extern "C" CFPropertyListRef CGColorSpaceCopyPropertyList(CGColorSpaceRef space); + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcQpaIOSurface, "qt.qpa.backingstore.iosurface"); + +QIOSurfaceGraphicsBuffer::QIOSurfaceGraphicsBuffer(const QSize &size, const QPixelFormat &format, QCFType<CGColorSpaceRef> colorSpace) + : QPlatformGraphicsBuffer(size, format) +{ + const size_t width = size.width(); + const size_t height = size.height(); + + Q_ASSERT(width <= IOSurfaceGetPropertyMaximum(kIOSurfaceWidth)); + Q_ASSERT(height <= IOSurfaceGetPropertyMaximum(kIOSurfaceHeight)); + + static const char bytesPerElement = 4; + + const size_t bytesPerRow = IOSurfaceAlignProperty(kIOSurfaceBytesPerRow, width * bytesPerElement); + const size_t totalBytes = IOSurfaceAlignProperty(kIOSurfaceAllocSize, height * bytesPerRow); + + NSDictionary *options = @{ + (id)kIOSurfaceWidth: @(width), + (id)kIOSurfaceHeight: @(height), + (id)kIOSurfacePixelFormat: @(unsigned('BGRA')), + (id)kIOSurfaceBytesPerElement: @(bytesPerElement), + (id)kIOSurfaceBytesPerRow: @(bytesPerRow), + (id)kIOSurfaceAllocSize: @(totalBytes), + }; + + m_surface = IOSurfaceCreate((CFDictionaryRef)options); + Q_ASSERT(m_surface); + + 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() +{ +} + +const uchar *QIOSurfaceGraphicsBuffer::data() const +{ + return (const uchar *)IOSurfaceGetBaseAddress(m_surface); +} + +uchar *QIOSurfaceGraphicsBuffer::data() +{ + return (uchar *)IOSurfaceGetBaseAddress(m_surface); +} + +int QIOSurfaceGraphicsBuffer::bytesPerLine() const +{ + return IOSurfaceGetBytesPerRow(m_surface); +} + +IOSurfaceRef QIOSurfaceGraphicsBuffer::surface() +{ + return m_surface; +} + +bool QIOSurfaceGraphicsBuffer::isInUse() const +{ + return IOSurfaceIsInUse(m_surface); +} + +IOSurfaceLockOptions lockOptionsForAccess(QPlatformGraphicsBuffer::AccessTypes access) +{ + IOSurfaceLockOptions lockOptions = 0; + if (!(access & QPlatformGraphicsBuffer::SWWriteAccess)) + lockOptions |= kIOSurfaceLockReadOnly; + return lockOptions; +} + +bool QIOSurfaceGraphicsBuffer::doLock(AccessTypes access, const QRect &rect) +{ + Q_UNUSED(rect); + Q_ASSERT(!isLocked()); + + qCDebug(lcQpaIOSurface) << "Locking" << this << "for" << access; + + // FIXME: Teach QPlatformBackingStore::composeAndFlush about non-2D texture + // targets, so that we can use CGLTexImageIOSurface2D to support TextureAccess. + if (access & (TextureAccess | HWCompositor)) + return false; + + auto lockOptions = lockOptionsForAccess(access); + + // Try without read-back first + lockOptions |= kIOSurfaceLockAvoidSync; + kern_return_t ret = IOSurfaceLock(m_surface, lockOptions, nullptr); + if (ret == kIOSurfaceSuccess) + return true; + + if (ret == kIOReturnCannotLock) { + qCWarning(lcQpaIOSurface) << "Locking of" << this << "requires read-back"; + lockOptions ^= kIOSurfaceLockAvoidSync; + ret = IOSurfaceLock(m_surface, lockOptions, nullptr); + } + + if (ret != kIOSurfaceSuccess) { + qCWarning(lcQpaIOSurface) << "Failed to lock" << this << ret; + return false; + } + + return true; +} + +void QIOSurfaceGraphicsBuffer::doUnlock() +{ + qCDebug(lcQpaIOSurface) << "Unlocking" << this << "from" << isLocked(); + + auto lockOptions = lockOptionsForAccess(isLocked()); + bool success = IOSurfaceUnlock(m_surface, lockOptions, nullptr) == kIOSurfaceSuccess; + Q_ASSERT_X(success, "QIOSurfaceGraphicsBuffer", "Unlocking surface should succeed"); +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug debug, const QIOSurfaceGraphicsBuffer *graphicsBuffer) +{ + QDebugStateSaver saver(debug); + debug.nospace(); + debug << "QIOSurfaceGraphicsBuffer(" << (const void *)graphicsBuffer; + if (graphicsBuffer) { + debug << ", surface=" << graphicsBuffer->m_surface; + debug << ", size=" << graphicsBuffer->size(); + debug << ", isLocked=" << bool(graphicsBuffer->isLocked()); + debug << ", isInUse=" << graphicsBuffer->isInUse(); + } + debug << ')'; + return debug; +} +#endif // !QT_NO_DEBUG_STREAM + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qnsview_drawing.mm b/src/plugins/platforms/cocoa/qnsview_drawing.mm index 6db5ed8bad..f7e14b7883 100644 --- a/src/plugins/platforms/cocoa/qnsview_drawing.mm +++ b/src/plugins/platforms/cocoa/qnsview_drawing.mm @@ -163,6 +163,13 @@ return NSViewLayerContentsRedrawDuringViewResize; } +- (NSViewLayerContentsPlacement)layerContentsPlacement +{ + // Always place the layer at top left without any automatic scaling, + // so that we can re-use larger layers when resizing a window down. + return NSViewLayerContentsPlacementTopLeft; +} + - (void)updateMetalLayerDrawableSize:(CAMetalLayer *)layer { CGSize drawableSize = layer.bounds.size; diff --git a/src/plugins/platforms/directfb/qdirectfb_egl.cpp b/src/plugins/platforms/directfb/qdirectfb_egl.cpp index dad553c890..d3c95f0b65 100644 --- a/src/plugins/platforms/directfb/qdirectfb_egl.cpp +++ b/src/plugins/platforms/directfb/qdirectfb_egl.cpp @@ -44,6 +44,7 @@ #include <QtGui/QOpenGLContext> #include <qpa/qplatformopenglcontext.h> +#include <qpa/qwindowsysteminterface.h> #include <QtGui/QScreen> #include <QtEglSupport/private/qeglplatformcontext_p.h> @@ -248,7 +249,7 @@ QPlatformOpenGLContext *QDirectFbIntegrationEGL::createPlatformOpenGLContext(QOp void QDirectFbIntegrationEGL::initializeScreen() { m_primaryScreen.reset(new QDirectFbScreenEGL(0)); - screenAdded(m_primaryScreen.data()); + QWindowSystemInterface::handleScreenAdded(m_primaryScreen.data()); } bool QDirectFbIntegrationEGL::hasCapability(QPlatformIntegration::Capability cap) const diff --git a/src/plugins/platforms/directfb/qdirectfbintegration.cpp b/src/plugins/platforms/directfb/qdirectfbintegration.cpp index cdf340da7a..73e308da53 100644 --- a/src/plugins/platforms/directfb/qdirectfbintegration.cpp +++ b/src/plugins/platforms/directfb/qdirectfbintegration.cpp @@ -56,6 +56,7 @@ #include <QtCore/QThread> #include <QtCore/QAbstractEventDispatcher> #include <qpa/qplatforminputcontextfactory_p.h> +#include <qpa/qwindowsysteminterface.h> QT_BEGIN_NAMESPACE @@ -113,7 +114,7 @@ void QDirectFbIntegration::initializeDirectFB() void QDirectFbIntegration::initializeScreen() { m_primaryScreen.reset(new QDirectFbScreen(0)); - screenAdded(m_primaryScreen.data()); + QWindowSystemInterface::handleScreenAdded(m_primaryScreen.data()); } void QDirectFbIntegration::initializeInput() diff --git a/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp b/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp index 0a3a37863a..81bad45cd2 100644 --- a/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp +++ b/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp @@ -200,10 +200,8 @@ void QEglFSDeviceIntegration::screenInit() void QEglFSDeviceIntegration::screenDestroy() { QGuiApplication *app = qGuiApp; - QEglFSIntegration *platformIntegration = static_cast<QEglFSIntegration *>( - QGuiApplicationPrivate::platformIntegration()); while (!app->screens().isEmpty()) - platformIntegration->removeScreen(app->screens().constLast()->handle()); + QWindowSystemInterface::handleScreenRemoved(app->screens().constLast()->handle()); } QSizeF QEglFSDeviceIntegration::physicalScreenSize() const diff --git a/src/plugins/platforms/eglfs/api/qeglfsintegration.cpp b/src/plugins/platforms/eglfs/api/qeglfsintegration.cpp index 8ccb0ef2cd..48469b0f8c 100644 --- a/src/plugins/platforms/eglfs/api/qeglfsintegration.cpp +++ b/src/plugins/platforms/eglfs/api/qeglfsintegration.cpp @@ -120,16 +120,6 @@ QEglFSIntegration::QEglFSIntegration() initResources(); } -void QEglFSIntegration::addScreen(QPlatformScreen *screen, bool isPrimary) -{ - screenAdded(screen, isPrimary); -} - -void QEglFSIntegration::removeScreen(QPlatformScreen *screen) -{ - destroyScreen(screen); -} - void QEglFSIntegration::initialize() { qt_egl_device_integration()->platformInit(); @@ -147,7 +137,7 @@ void QEglFSIntegration::initialize() m_vtHandler.reset(new QFbVtHandler); if (qt_egl_device_integration()->usesDefaultScreen()) - addScreen(new QEglFSScreen(display())); + QWindowSystemInterface::handleScreenAdded(new QEglFSScreen(display())); else qt_egl_device_integration()->screenInit(); diff --git a/src/plugins/platforms/eglfs/api/qeglfsintegration_p.h b/src/plugins/platforms/eglfs/api/qeglfsintegration_p.h index 4b4585d33c..898b322834 100644 --- a/src/plugins/platforms/eglfs/api/qeglfsintegration_p.h +++ b/src/plugins/platforms/eglfs/api/qeglfsintegration_p.h @@ -103,9 +103,6 @@ public: QFbVtHandler *vtHandler() { return m_vtHandler.data(); } - void addScreen(QPlatformScreen *screen, bool isPrimary = false); - void removeScreen(QPlatformScreen *screen); - private: EGLNativeDisplayType nativeDisplay() const; void createInputHandlers(); diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorintegration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorintegration.cpp index 5e2708e958..cb7844aff0 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorintegration.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/qeglfsemulatorintegration.cpp @@ -45,6 +45,8 @@ #include <QtEglSupport/private/qeglconvenience_p.h> #include <QtEglSupport/private/qeglplatformcontext_p.h> +#include <qpa/qwindowsysteminterface.h> + #include <QtCore/QJsonDocument> #include <QtCore/QJsonArray> #include <QtCore/QJsonParseError> @@ -80,8 +82,6 @@ bool QEglFSEmulatorIntegration::usesDefaultScreen() void QEglFSEmulatorIntegration::screenInit() { - QEglFSIntegration *integration = static_cast<QEglFSIntegration *>(QGuiApplicationPrivate::platformIntegration()); - // Use qgsGetDisplays() call to retrieve the available screens from the Emulator if (getDisplays) { QByteArray displaysInfo = getDisplays(); @@ -93,7 +93,7 @@ void QEglFSEmulatorIntegration::screenInit() QJsonArray screenArray = displaysDocument.array(); for (auto screenValue : screenArray) { if (screenValue.isObject()) - integration->addScreen(new QEglFSEmulatorScreen(screenValue.toObject())); + QWindowSystemInterface::handleScreenAdded(new QEglFSEmulatorScreen(screenValue.toObject())); } } } else { diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.cpp index 9f4c9f5703..ef732f9832 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.cpp @@ -310,7 +310,7 @@ QKmsDevice *QEglFSKmsEglDeviceIntegration::createDevice() if (Q_UNLIKELY(!deviceName)) qFatal("Failed to query device name from EGLDevice"); - return new QEglFSKmsEglDevice(this, screenConfig(), deviceName); + return new QEglFSKmsEglDevice(this, screenConfig(), QLatin1String(deviceName)); } bool QEglFSKmsEglDeviceIntegration::query_egl_device() diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsdevice.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsdevice.cpp index b073577797..4f0b0d7725 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsdevice.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsdevice.cpp @@ -58,7 +58,7 @@ void QEglFSKmsDevice::registerScreen(QPlatformScreen *screen, QEglFSKmsScreen *s = static_cast<QEglFSKmsScreen *>(screen); s->setVirtualPosition(virtualPos); s->setVirtualSiblings(virtualSiblings); - static_cast<QEglFSIntegration *>(QGuiApplicationPrivate::platformIntegration())->addScreen(s, isPrimary); + QWindowSystemInterface::handleScreenAdded(s, isPrimary); } QT_END_NAMESPACE diff --git a/src/plugins/platforms/haiku/qhaikuintegration.cpp b/src/plugins/platforms/haiku/qhaikuintegration.cpp index 8bd2171794..44df6ae8f5 100644 --- a/src/plugins/platforms/haiku/qhaikuintegration.cpp +++ b/src/plugins/platforms/haiku/qhaikuintegration.cpp @@ -49,6 +49,7 @@ #include <QCoreApplication> #include <QFileInfo> #include <qpa/qplatformwindow.h> +#include <qpa/qwindowsysteminterface.h> #include <QtEventDispatcherSupport/private/qgenericunixeventdispatcher_p.h> #include <Application.h> @@ -81,12 +82,12 @@ QHaikuIntegration::QHaikuIntegration(const QStringList ¶meters) m_services = new QHaikuServices; // notify system about available screen - screenAdded(m_screen); + QWindowSystemInterface::handleScreenAdded(m_screen); } QHaikuIntegration::~QHaikuIntegration() { - destroyScreen(m_screen); + QWindowSystemInterface::handleScreenRemoved(m_screen); m_screen = nullptr; delete m_services; diff --git a/src/plugins/platforms/integrity/qintegrityfbintegration.cpp b/src/plugins/platforms/integrity/qintegrityfbintegration.cpp index ae802bb1fa..3ad31dc562 100644 --- a/src/plugins/platforms/integrity/qintegrityfbintegration.cpp +++ b/src/plugins/platforms/integrity/qintegrityfbintegration.cpp @@ -51,7 +51,7 @@ #include <QtGui/private/qguiapplication_p.h> #include <qpa/qplatforminputcontextfactory_p.h> - +#include <qpa/qwindowsysteminterface.h> QT_BEGIN_NAMESPACE @@ -64,13 +64,13 @@ QIntegrityFbIntegration::QIntegrityFbIntegration(const QStringList ¶mList) QIntegrityFbIntegration::~QIntegrityFbIntegration() { - destroyScreen(m_primaryScreen); + QWindowSystemInterface::handleScreenRemoved(m_primaryScreen); } void QIntegrityFbIntegration::initialize() { if (m_primaryScreen->initialize()) - screenAdded(m_primaryScreen); + QWindowSystemInterface::handleScreenAdded(m_primaryScreen); else qWarning("integrityfb: Failed to initialize screen"); diff --git a/src/plugins/platforms/ios/qiosintegration.h b/src/plugins/platforms/ios/qiosintegration.h index 818250fcee..eeb44b54d3 100644 --- a/src/plugins/platforms/ios/qiosintegration.h +++ b/src/plugins/platforms/ios/qiosintegration.h @@ -92,10 +92,6 @@ public: QPlatformAccessibility *accessibility() const override; #endif - // Called from Objective-C class QIOSScreenTracker, which can't be friended - void addScreen(QPlatformScreen *screen) { screenAdded(screen); } - void destroyScreen(QPlatformScreen *screen) { QPlatformIntegration::destroyScreen(screen); } - void beep() const override; static QIOSIntegration *instance(); diff --git a/src/plugins/platforms/ios/qiosintegration.mm b/src/plugins/platforms/ios/qiosintegration.mm index bec8354fcc..9eca0eaad3 100644 --- a/src/plugins/platforms/ios/qiosintegration.mm +++ b/src/plugins/platforms/ios/qiosintegration.mm @@ -107,7 +107,7 @@ void QIOSIntegration::initialize() } for (UIScreen *screen in screens) - addScreen(new QIOSScreen(screen)); + QWindowSystemInterface::handleScreenAdded(new QIOSScreen(screen)); // Depends on a primary screen being present m_inputContext = new QIOSInputContext; @@ -143,7 +143,7 @@ QIOSIntegration::~QIOSIntegration() m_inputContext = 0; foreach (QScreen *screen, QGuiApplication::screens()) - destroyScreen(screen->handle()); + QWindowSystemInterface::handleScreenRemoved(screen->handle()); delete m_platformServices; m_platformServices = 0; diff --git a/src/plugins/platforms/ios/qiosscreen.mm b/src/plugins/platforms/ios/qiosscreen.mm index 4f753be21a..9aba658479 100644 --- a/src/plugins/platforms/ios/qiosscreen.mm +++ b/src/plugins/platforms/ios/qiosscreen.mm @@ -50,6 +50,7 @@ #include <QtGui/private/qwindow_p.h> #include <private/qcoregraphics_p.h> +#include <qpa/qwindowsysteminterface.h> #include <sys/sysctl.h> @@ -105,10 +106,10 @@ static QIOSScreen* qtPlatformScreenFor(UIScreen *uiScreen) + (void)screenConnected:(NSNotification*)notification { - QIOSIntegration *integration = QIOSIntegration::instance(); - Q_ASSERT_X(integration, Q_FUNC_INFO, "Screen connected before QIOSIntegration creation"); + Q_ASSERT_X(QIOSIntegration::instance(), Q_FUNC_INFO, + "Screen connected before QIOSIntegration creation"); - integration->addScreen(new QIOSScreen([notification object])); + QWindowSystemInterface::handleScreenAdded(new QIOSScreen([notification object])); } + (void)screenDisconnected:(NSNotification*)notification @@ -116,8 +117,7 @@ static QIOSScreen* qtPlatformScreenFor(UIScreen *uiScreen) QIOSScreen *screen = qtPlatformScreenFor([notification object]); Q_ASSERT_X(screen, Q_FUNC_INFO, "Screen disconnected that we didn't know about"); - QIOSIntegration *integration = QIOSIntegration::instance(); - integration->destroyScreen(screen); + QWindowSystemInterface::handleScreenRemoved(screen); } + (void)screenModeChanged:(NSNotification*)notification diff --git a/src/plugins/platforms/linuxfb/qlinuxfbintegration.cpp b/src/plugins/platforms/linuxfb/qlinuxfbintegration.cpp index 9e38900bcd..68843aa4c5 100644 --- a/src/plugins/platforms/linuxfb/qlinuxfbintegration.cpp +++ b/src/plugins/platforms/linuxfb/qlinuxfbintegration.cpp @@ -54,6 +54,7 @@ #include <QtGui/private/qguiapplication_p.h> #include <qpa/qplatforminputcontextfactory_p.h> +#include <qpa/qwindowsysteminterface.h> #if QT_CONFIG(libinput) #include <QtInputSupport/private/qlibinputhandler_p.h> @@ -89,13 +90,13 @@ QLinuxFbIntegration::QLinuxFbIntegration(const QStringList ¶mList) QLinuxFbIntegration::~QLinuxFbIntegration() { - destroyScreen(m_primaryScreen); + QWindowSystemInterface::handleScreenRemoved(m_primaryScreen); } void QLinuxFbIntegration::initialize() { if (m_primaryScreen->initialize()) - screenAdded(m_primaryScreen); + QWindowSystemInterface::handleScreenAdded(m_primaryScreen); else qWarning("linuxfb: Failed to initialize screen"); diff --git a/src/plugins/platforms/minimal/qminimalintegration.cpp b/src/plugins/platforms/minimal/qminimalintegration.cpp index 0c04608fca..f457f69f11 100644 --- a/src/plugins/platforms/minimal/qminimalintegration.cpp +++ b/src/plugins/platforms/minimal/qminimalintegration.cpp @@ -43,6 +43,7 @@ #include <QtGui/private/qpixmap_raster_p.h> #include <QtGui/private/qguiapplication_p.h> #include <qpa/qplatformwindow.h> +#include <qpa/qwindowsysteminterface.h> #include <QtFontDatabaseSupport/private/qfreetypefontdatabase_p.h> #if defined(Q_OS_WINRT) @@ -108,7 +109,7 @@ QMinimalIntegration::QMinimalIntegration(const QStringList ¶meters) mPrimaryScreen->mDepth = 32; mPrimaryScreen->mFormat = QImage::Format_ARGB32_Premultiplied; - screenAdded(mPrimaryScreen); + QWindowSystemInterface::handleScreenAdded(mPrimaryScreen); } QMinimalIntegration::~QMinimalIntegration() diff --git a/src/plugins/platforms/minimalegl/qminimaleglintegration.cpp b/src/plugins/platforms/minimalegl/qminimaleglintegration.cpp index da58441d67..a0d35a80cd 100644 --- a/src/plugins/platforms/minimalegl/qminimaleglintegration.cpp +++ b/src/plugins/platforms/minimalegl/qminimaleglintegration.cpp @@ -58,6 +58,7 @@ #include <QtGui/QSurfaceFormat> #include <QtGui/QOpenGLContext> #include <QtGui/QScreen> +#include <qpa/qwindowsysteminterface.h> // this is where EGL headers are pulled in, make sure it is last #include "qminimaleglscreen.h" @@ -90,7 +91,7 @@ protected: QMinimalEglIntegration::QMinimalEglIntegration() : mFontDb(new QGenericUnixFontDatabase()), mScreen(new QMinimalEglScreen(EGL_DEFAULT_DISPLAY)) { - screenAdded(mScreen); + QWindowSystemInterface::handleScreenAdded(mScreen); #ifdef QEGL_EXTRA_DEBUG qWarning("QMinimalEglIntegration\n"); @@ -99,7 +100,7 @@ QMinimalEglIntegration::QMinimalEglIntegration() QMinimalEglIntegration::~QMinimalEglIntegration() { - destroyScreen(mScreen); + QWindowSystemInterface::handleScreenRemoved(mScreen); delete mFontDb; } diff --git a/src/plugins/platforms/offscreen/qoffscreenintegration.cpp b/src/plugins/platforms/offscreen/qoffscreenintegration.cpp index f2933b7179..869e9228cd 100644 --- a/src/plugins/platforms/offscreen/qoffscreenintegration.cpp +++ b/src/plugins/platforms/offscreen/qoffscreenintegration.cpp @@ -63,6 +63,7 @@ #include <qpa/qplatforminputcontextfactory_p.h> #include <qpa/qplatforminputcontext.h> #include <qpa/qplatformtheme.h> +#include <qpa/qwindowsysteminterface.h> #include <qpa/qplatformservices.h> @@ -121,7 +122,7 @@ QOffscreenIntegration::QOffscreenIntegration() #endif m_services.reset(new QPlatformServices); - screenAdded(new QOffscreenScreen); + QWindowSystemInterface::handleScreenAdded(new QOffscreenScreen); } QOffscreenIntegration::~QOffscreenIntegration() diff --git a/src/plugins/platforms/openwfd/qopenwfdintegration.cpp b/src/plugins/platforms/openwfd/qopenwfdintegration.cpp index 4850ca2e45..c5dc40a206 100644 --- a/src/plugins/platforms/openwfd/qopenwfdintegration.cpp +++ b/src/plugins/platforms/openwfd/qopenwfdintegration.cpp @@ -50,6 +50,7 @@ #include <QtGui/private/qguiapplication_p.h> #include <QtGui/QOpenGLContext> #include <QtGui/QScreen> +#include <qpa/qwindowsysteminterface.h> #include <QtEventDispatcherSupport/private/qgenericunixeventdispatcher_p.h> #include <QtFontDatabaseSupport/private/qgenericunixfontdatabase_p.h> @@ -135,13 +136,3 @@ QPlatformPrinterSupport * QOpenWFDIntegration::printerSupport() const { return mPrinterSupport; } - -void QOpenWFDIntegration::addScreen(QOpenWFDScreen *screen) -{ - screenAdded(screen); -} - -void QOpenWFDIntegration::destroyScreen(QOpenWFDScreen *screen) -{ - QPlatformIntegration::destroyScreen(screen); -} diff --git a/src/plugins/platforms/openwfd/qopenwfdintegration.h b/src/plugins/platforms/openwfd/qopenwfdintegration.h index 1ce1001bed..444aaccaaf 100644 --- a/src/plugins/platforms/openwfd/qopenwfdintegration.h +++ b/src/plugins/platforms/openwfd/qopenwfdintegration.h @@ -68,8 +68,6 @@ public: QPlatformPrinterSupport *printerSupport() const; - void addScreen(QOpenWFDScreen *screen); - void destroyScreen(QOpenWFDScreen *screen); private: QList<QPlatformScreen *> mScreens; QList<QOpenWFDDevice *>mDevices; diff --git a/src/plugins/platforms/openwfd/qopenwfdport.cpp b/src/plugins/platforms/openwfd/qopenwfdport.cpp index 33254fe83c..34b4439958 100644 --- a/src/plugins/platforms/openwfd/qopenwfdport.cpp +++ b/src/plugins/platforms/openwfd/qopenwfdport.cpp @@ -133,7 +133,7 @@ void QOpenWFDPort::attach() wfdBindPipelineToPort(mDevice->handle(),mPort,mPipeline); mScreen = new QOpenWFDScreen(this); - mDevice->integration()->addScreen(mScreen); + QWindowSystemInterface::handleScreenAdded(mScreen); mAttached = true; } @@ -145,7 +145,7 @@ void QOpenWFDPort::detach() mAttached = false; mOn = false; - mDevice->integration()->destroyScreen(mScreen); + QWindowSystemInterface::handleScreenRemoved(mScreen); wfdDestroyPipeline(mDevice->handle(),mPipeline); mPipelineId = WFD_INVALID_PIPELINE_ID; diff --git a/src/plugins/platforms/qnx/qqnxintegration.cpp b/src/plugins/platforms/qnx/qqnxintegration.cpp index a996e765c4..a45dcabeb7 100644 --- a/src/plugins/platforms/qnx/qqnxintegration.cpp +++ b/src/plugins/platforms/qnx/qqnxintegration.cpp @@ -649,7 +649,7 @@ void QQnxIntegration::createDisplay(screen_display_t display, bool isPrimary) { QQnxScreen *screen = new QQnxScreen(m_screenContext, display, isPrimary); m_screens.append(screen); - screenAdded(screen); + QWindowSystemInterface::handleScreenAdded(screen); screen->adjustOrientation(); QObject::connect(m_screenEventHandler, SIGNAL(newWindowCreated(void*)), @@ -669,14 +669,14 @@ void QQnxIntegration::removeDisplay(QQnxScreen *screen) Q_CHECK_PTR(screen); Q_ASSERT(m_screens.contains(screen)); m_screens.removeAll(screen); - destroyScreen(screen); + QWindowSystemInterface::handleScreenRemoved(screen); } void QQnxIntegration::destroyDisplays() { qIntegrationDebug(); Q_FOREACH (QQnxScreen *screen, m_screens) { - QPlatformIntegration::destroyScreen(screen); + QWindowSystemInterface::handleScreenRemoved(screen); } m_screens.clear(); } diff --git a/src/plugins/platforms/vnc/qvncintegration.cpp b/src/plugins/platforms/vnc/qvncintegration.cpp index 1e2cf6292c..c7a796464a 100644 --- a/src/plugins/platforms/vnc/qvncintegration.cpp +++ b/src/plugins/platforms/vnc/qvncintegration.cpp @@ -52,6 +52,7 @@ #include <QtGui/private/qguiapplication_p.h> #include <qpa/qplatforminputcontextfactory_p.h> #include <private/qinputdevicemanager_p_p.h> +#include <qpa/qwindowsysteminterface.h> #include <QtCore/QRegularExpression> @@ -77,13 +78,13 @@ QVncIntegration::QVncIntegration(const QStringList ¶mList) QVncIntegration::~QVncIntegration() { delete m_server; - destroyScreen(m_primaryScreen); + QWindowSystemInterface::handleScreenRemoved(m_primaryScreen); } void QVncIntegration::initialize() { if (m_primaryScreen->initialize()) - screenAdded(m_primaryScreen); + QWindowSystemInterface::handleScreenAdded(m_primaryScreen); else qWarning("vnc: Failed to initialize screen"); diff --git a/src/plugins/platforms/wasm/qwasmintegration.cpp b/src/plugins/platforms/wasm/qwasmintegration.cpp index 1964cefdad..486f4cb2b3 100644 --- a/src/plugins/platforms/wasm/qwasmintegration.cpp +++ b/src/plugins/platforms/wasm/qwasmintegration.cpp @@ -98,7 +98,10 @@ QWasmIntegration::QWasmIntegration() QWasmIntegration::~QWasmIntegration() { delete m_fontDb; - qDeleteAll(m_screens); + + while (!m_screens.isEmpty()) + QWindowSystemInterface::handleScreenRemoved(m_screens.takeLast()); + s_instance = nullptr; } @@ -113,7 +116,7 @@ bool QWasmIntegration::hasCapability(QPlatformIntegration::Capability cap) const switch (cap) { case ThreadedPixmaps: return true; case OpenGL: return true; - case ThreadedOpenGL: return true; + case ThreadedOpenGL: return false; case RasterGLSurface: return false; // to enable this you need to fix qopenglwidget and quickwidget for wasm case MultipleWindows: return true; case WindowManagement: return true; @@ -191,7 +194,7 @@ void QWasmIntegration::addScreen(const QString &canvasId) QWasmScreen *screen = new QWasmScreen(canvasId); m_clipboard->installEventHandlers(canvasId); m_screens.append(screen); - screenAdded(screen); + QWindowSystemInterface::handleScreenAdded(screen); } QT_END_NAMESPACE diff --git a/src/plugins/platforms/wasm/wasm_shell.html b/src/plugins/platforms/wasm/wasm_shell.html index 39bb711b6b..6ad7886ed4 100644 --- a/src/plugins/platforms/wasm/wasm_shell.html +++ b/src/plugins/platforms/wasm/wasm_shell.html @@ -27,9 +27,9 @@ <script type='text/javascript'> function init() { - var spinner = document.getElementById('qtspinner'); - var canvas = document.getElementById('qtcanvas'); - var status = document.getElementById('qtstatus') + var spinner = document.querySelector('#qtspinner'); + var canvas = document.querySelector('#qtcanvas'); + var status = document.querySelector('#qtstatus') var qtLoader = QtLoader({ canvasElements : [canvas], diff --git a/src/plugins/platforms/windows/qwindowsintegration.h b/src/plugins/platforms/windows/qwindowsintegration.h index da86852766..e28b2c2fb3 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.h +++ b/src/plugins/platforms/windows/qwindowsintegration.h @@ -107,9 +107,6 @@ public: static QWindowsIntegration *instance() { return m_instance; } - inline void emitScreenAdded(QPlatformScreen *s, bool isPrimary = false) { screenAdded(s, isPrimary); } - inline void emitDestroyScreen(QPlatformScreen *s) { destroyScreen(s); } - unsigned options() const; void beep() const override; diff --git a/src/plugins/platforms/windows/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp index a161dc46e9..0520f88935 100644 --- a/src/plugins/platforms/windows/qwindowsscreen.cpp +++ b/src/plugins/platforms/windows/qwindowsscreen.cpp @@ -121,7 +121,7 @@ BOOL QT_WIN_CALLBACK monitorEnumCallback(HMONITOR hMonitor, HDC, LPRECT, LPARAM QWindowsScreenData data; if (monitorData(hMonitor, &data)) { WindowsScreenDataList *result = reinterpret_cast<WindowsScreenDataList *>(p); - // QPlatformIntegration::screenAdded() documentation specifies that first + // QWindowSystemInterface::handleScreenAdded() documentation specifies that first // added screen will be the primary screen, so order accordingly. // Note that the side effect of this policy is that there is no way to change primary // screen reported by Qt, unless we want to delete all existing screens and add them @@ -521,7 +521,7 @@ void QWindowsScreenManager::removeScreen(int index) if (movedWindowCount) QWindowSystemInterface::flushWindowSystemEvents(); } - QWindowsIntegration::instance()->emitDestroyScreen(m_screens.takeAt(index)); + QWindowSystemInterface::handleScreenRemoved(m_screens.takeAt(index)); } /*! @@ -541,7 +541,7 @@ bool QWindowsScreenManager::handleScreenChanges() } else { QWindowsScreen *newScreen = new QWindowsScreen(newData); m_screens.push_back(newScreen); - QWindowsIntegration::instance()->emitScreenAdded(newScreen, + QWindowSystemInterface::handleScreenAdded(newScreen, newData.flags & QWindowsScreenData::PrimaryScreen); qCDebug(lcQpaWindows) << "New Monitor: " << newData; } // exists @@ -561,7 +561,7 @@ void QWindowsScreenManager::clearScreens() { // Delete screens in reverse order to avoid crash in case of multiple screens while (!m_screens.isEmpty()) - QWindowsIntegration::instance()->emitDestroyScreen(m_screens.takeLast()); + QWindowSystemInterface::handleScreenRemoved(m_screens.takeLast()); } const QWindowsScreen *QWindowsScreenManager::screenAtDp(const QPoint &p) const diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index 8bf090bf03..07fd2916c2 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -1759,15 +1759,12 @@ void QWindowsWindow::checkForScreenChanged() QPlatformScreen *currentScreen = screen(); const auto &screenManager = QWindowsContext::instance()->screenManager(); - // QTBUG-62971: When dragging a window by its border, detect by mouse position - // to prevent it from oscillating between screens when it resizes - const QWindowsScreen *newScreen = testFlag(ResizeMoveActive) - ? screenManager.screenAtDp(QWindowsCursor::mousePosition()) - : screenManager.screenForHwnd(m_data.hwnd); + const QWindowsScreen *newScreen = screenManager.screenForHwnd(m_data.hwnd); if (newScreen != nullptr && newScreen != currentScreen) { qCDebug(lcQpaWindows).noquote().nospace() << __FUNCTION__ << ' ' << window() << " \"" << currentScreen->name() << "\"->\"" << newScreen->name() << '"'; + setFlag(SynchronousGeometryChangeEvent); QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->screen()); } } @@ -1786,11 +1783,14 @@ void QWindowsWindow::handleGeometryChange() fireExpose(QRect(QPoint(0, 0), m_data.geometry.size()), true); } + const bool wasSync = testFlag(SynchronousGeometryChangeEvent); checkForScreenChanged(); if (testFlag(SynchronousGeometryChangeEvent)) QWindowSystemInterface::flushWindowSystemEvents(QEventLoop::ExcludeUserInputEvents); + if (!wasSync) + clearFlag(SynchronousGeometryChangeEvent); qCDebug(lcQpaEvents) << __FUNCTION__ << this << window() << m_data.geometry; } diff --git a/src/plugins/platforms/winrt/qwinrtintegration.cpp b/src/plugins/platforms/winrt/qwinrtintegration.cpp index 78cbc3aec3..27d3746933 100644 --- a/src/plugins/platforms/winrt/qwinrtintegration.cpp +++ b/src/plugins/platforms/winrt/qwinrtintegration.cpp @@ -133,7 +133,7 @@ QWinRTIntegration::QWinRTIntegration() : d_ptr(new QWinRTIntegrationPrivate) }); d->inputContext.reset(new QWinRTInputContext(d->mainScreen)); - screenAdded(d->mainScreen); + QWindowSystemInterface::handleScreenAdded(d->mainScreen); d->platformServices = new QWinRTServices; d->clipboard = new QWinRTClipboard; #if QT_CONFIG(accessibility) @@ -154,7 +154,7 @@ QWinRTIntegration::~QWinRTIntegration() Q_ASSERT_SUCCEEDED(hr); } - destroyScreen(d->mainScreen); + QWindowSystemInterface::handleScreenRemoved(d->mainScreen); Windows::Foundation::Uninitialize(); } diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp index d42a33c22b..476de6d1e5 100644 --- a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp +++ b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp @@ -652,6 +652,12 @@ static const char *qglx_threadedgl_blacklist_renderer[] = { 0 }; +static const char *qglx_threadedgl_blacklist_vendor[] = { + "llvmpipe", // QTCREATORBUG-10666 + "nouveau", // https://bugs.freedesktop.org/show_bug.cgi?id=91632 + nullptr +}; + void QGLXContext::queryDummyContext() { if (m_queriedDummyContext) @@ -710,6 +716,18 @@ void QGLXContext::queryDummyContext() } } } + if (const char *vendor = (const char *) glGetString(GL_VENDOR)) { + for (int i = 0; qglx_threadedgl_blacklist_vendor[i]; ++i) { + if (strstr(vendor, qglx_threadedgl_blacklist_vendor[i]) != 0) { + qCDebug(lcQpaGl).nospace() << "Multithreaded OpenGL disabled: " + "blacklisted vendor \"" + << qglx_threadedgl_blacklist_vendor[i] + << "\""; + m_supportsThreading = false; + break; + } + } + } if (glxvendor && m_supportsThreading) { // Blacklist Mesa drivers due to QTCREATORBUG-10875 (crash in creator), diff --git a/src/plugins/platforms/xcb/nativepainting/qbackingstore_x11.cpp b/src/plugins/platforms/xcb/nativepainting/qbackingstore_x11.cpp index ed482e5dae..bbc156fc53 100644 --- a/src/plugins/platforms/xcb/nativepainting/qbackingstore_x11.cpp +++ b/src/plugins/platforms/xcb/nativepainting/qbackingstore_x11.cpp @@ -192,6 +192,10 @@ bool QXcbNativeBackingStore::scroll(const QRegion &area, int dx, int dy) void QXcbNativeBackingStore::beginPaint(const QRegion ®ion) { + QX11PlatformPixmap *x11pm = qt_x11Pixmap(m_pixmap); + if (x11pm) + x11pm->setIsBackingStore(true); + #if QT_CONFIG(xrender) if (m_translucentBackground) { const QVector<XRectangle> xrects = qt_region_to_xrectangles(region); diff --git a/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp b/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp index 2688d6e884..3377eeaae9 100644 --- a/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp +++ b/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp @@ -200,6 +200,7 @@ public: uint has_pattern : 1; uint has_alpha_pen : 1; uint has_alpha_brush : 1; + uint use_sysclip : 1; uint render_hints; const QXcbX11Info *xinfo; @@ -701,6 +702,9 @@ bool QX11PaintEngine::begin(QPaintDevice *pdev) d->xlibMaxLinePoints = 32762; // a safe number used to avoid, call to XMaxRequestSize(d->dpy) - 3; d->opacity = 1; + QX11PlatformPixmap *x11pm = paintDevice()->devType() == QInternal::Pixmap ? qt_x11Pixmap(*static_cast<QPixmap *>(paintDevice())) : nullptr; + d->use_sysclip = paintDevice()->devType() == QInternal::Widget || (x11pm ? x11pm->isBackingStore() : false); + // Set up the polygon clipper. Note: This will only work in // polyline mode as long as we have a buffer zone, since a // polyline may be clipped into several non-connected polylines. @@ -1472,7 +1476,7 @@ void QX11PaintEngine::updatePen(const QPen &pen) } if (!d->has_clipping) { // if clipping is set the paintevent clip region is merged with the clip region - QRegion sysClip = systemClip(); + QRegion sysClip = d->use_sysclip ? systemClip() : QRegion(); if (!sysClip.isEmpty()) x11SetClipRegion(d->dpy, d->gc, 0, d->picture, sysClip); else @@ -1603,7 +1607,7 @@ void QX11PaintEngine::updateBrush(const QBrush &brush, const QPointF &origin) vals.fill_style = s; XChangeGC(d->dpy, d->gc_brush, mask, &vals); if (!d->has_clipping) { - QRegion sysClip = systemClip(); + QRegion sysClip = d->use_sysclip ? systemClip() : QRegion(); if (!sysClip.isEmpty()) x11SetClipRegion(d->dpy, d->gc_brush, 0, d->picture, sysClip); else @@ -2223,7 +2227,7 @@ void QX11PaintEngine::updateMatrix(const QTransform &mtx) void QX11PaintEngine::updateClipRegion_dev(const QRegion &clipRegion, Qt::ClipOperation op) { Q_D(QX11PaintEngine); - QRegion sysClip = systemClip(); + QRegion sysClip = d->use_sysclip ? systemClip() : QRegion(); if (op == Qt::NoClip) { d->has_clipping = false; d->crgn = sysClip; diff --git a/src/plugins/platforms/xcb/nativepainting/qpixmap_x11.cpp b/src/plugins/platforms/xcb/nativepainting/qpixmap_x11.cpp index 86c87e5e30..b1ce39f363 100644 --- a/src/plugins/platforms/xcb/nativepainting/qpixmap_x11.cpp +++ b/src/plugins/platforms/xcb/nativepainting/qpixmap_x11.cpp @@ -1772,6 +1772,20 @@ XID QX11PlatformPixmap::createBitmapFromImage(const QImage &image) return hd; } +bool QX11PlatformPixmap::isBackingStore() const +{ + return (flags & IsBackingStore); +} + +void QX11PlatformPixmap::setIsBackingStore(bool on) +{ + if (on) + flags |= IsBackingStore; + else { + flags &= ~IsBackingStore; + } +} + #if QT_CONFIG(xrender) void QX11PlatformPixmap::convertToARGB32(bool preserveContents) { diff --git a/src/plugins/platforms/xcb/nativepainting/qpixmap_x11_p.h b/src/plugins/platforms/xcb/nativepainting/qpixmap_x11_p.h index 7392cbfccf..9c0ba98300 100644 --- a/src/plugins/platforms/xcb/nativepainting/qpixmap_x11_p.h +++ b/src/plugins/platforms/xcb/nativepainting/qpixmap_x11_p.h @@ -90,6 +90,8 @@ public: void convertToARGB32(bool preserveContents = true); #endif + bool isBackingStore() const; + void setIsBackingStore(bool on); private: friend class QX11PaintEngine; friend const QXcbX11Info &qt_x11Info(const QPixmap &pixmap); @@ -110,7 +112,8 @@ private: Uninitialized = 0x1, Readonly = 0x2, InvertedWhenBoundToTexture = 0x4, - GlSurfaceCreatedWithAlpha = 0x8 + GlSurfaceCreatedWithAlpha = 0x8, + IsBackingStore = 0x10 }; uint flags; diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index e35b97dab4..d27a288feb 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -152,10 +152,9 @@ QXcbConnection::~QXcbConnection() if (m_eventQueue) delete m_eventQueue; - QXcbIntegration *integration = QXcbIntegration::instance(); // Delete screens in reverse order to avoid crash in case of multiple screens while (!m_screens.isEmpty()) - integration->destroyScreen(m_screens.takeLast()); + QWindowSystemInterface::handleScreenRemoved(m_screens.takeLast()); while (!m_virtualDesktops.isEmpty()) delete m_virtualDesktops.takeLast(); diff --git a/src/plugins/platforms/xcb/qxcbconnection_basic.h b/src/plugins/platforms/xcb/qxcbconnection_basic.h index 3691763398..b979129cba 100644 --- a/src/plugins/platforms/xcb/qxcbconnection_basic.h +++ b/src/plugins/platforms/xcb/qxcbconnection_basic.h @@ -40,6 +40,7 @@ #define QXCBBASICCONNECTION_H #include "qxcbatom.h" +#include "qxcbexport.h" #include <QtCore/QPair> #include <QtCore/QObject> @@ -55,7 +56,7 @@ QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(lcQpaXcb) -class QXcbBasicConnection : public QObject +class Q_XCB_EXPORT QXcbBasicConnection : public QObject { Q_OBJECT public: diff --git a/src/plugins/platforms/xcb/qxcbconnection_screens.cpp b/src/plugins/platforms/xcb/qxcbconnection_screens.cpp index 4bafe83156..9ba71ada37 100644 --- a/src/plugins/platforms/xcb/qxcbconnection_screens.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection_screens.cpp @@ -44,6 +44,8 @@ #include <QtCore/QString> #include <QtCore/QList> +#include <qpa/qwindowsysteminterface.h> + #include <xcb/xinerama.h> void QXcbConnection::xrandrSelectEvents() @@ -211,7 +213,7 @@ void QXcbConnection::updateScreen(QXcbScreen *screen, const xcb_randr_output_cha m_screens.swapItemsAt(0, idx); } screen->virtualDesktop()->setPrimaryScreen(screen); - QXcbIntegration::instance()->setPrimaryScreen(screen); + QWindowSystemInterface::handlePrimaryScreenChanged(screen); } } } @@ -234,7 +236,7 @@ QXcbScreen *QXcbConnection::createScreen(QXcbVirtualDesktop *virtualDesktop, m_screens.append(screen); } virtualDesktop->addScreen(screen); - QXcbIntegration::instance()->screenAdded(screen, screen->isPrimary()); + QWindowSystemInterface::handleScreenAdded(screen, screen->isPrimary()); return screen; } @@ -261,10 +263,10 @@ void QXcbConnection::destroyScreen(QXcbScreen *screen) const int idx = m_screens.indexOf(newPrimary); if (idx > 0) m_screens.swapItemsAt(0, idx); - QXcbIntegration::instance()->setPrimaryScreen(newPrimary); + QWindowSystemInterface::handlePrimaryScreenChanged(newPrimary); } - QXcbIntegration::instance()->destroyScreen(screen); + QWindowSystemInterface::handleScreenRemoved(screen); } } @@ -406,7 +408,7 @@ void QXcbConnection::initializeScreens() // Push the screens to QGuiApplication for (QXcbScreen *screen : qAsConst(m_screens)) { qCDebug(lcQpaScreen) << "adding" << screen << "(Primary:" << screen->isPrimary() << ")"; - QXcbIntegration::instance()->screenAdded(screen, screen->isPrimary()); + QWindowSystemInterface::handleScreenAdded(screen, screen->isPrimary()); } qCDebug(lcQpaScreen) << "primary output is" << qAsConst(m_screens).first()->name(); diff --git a/src/plugins/platforms/xcb/qxcbintegration.h b/src/plugins/platforms/xcb/qxcbintegration.h index f13e232291..571726c354 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.h +++ b/src/plugins/platforms/xcb/qxcbintegration.h @@ -137,8 +137,6 @@ private: QScopedPointer<QPlatformServices> m_services; - friend class QXcbConnection; // access QPlatformIntegration::screenAdded() - mutable QByteArray m_wmClass; const char *m_instanceName; bool m_canGrab; diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp index 4d526a6bda..899081e752 100644 --- a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp +++ b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp @@ -412,12 +412,15 @@ void *QXcbNativeInterface::atspiBus() auto reply = Q_XCB_REPLY(xcb_get_property, defaultConnection->xcb_connection(), false, defaultConnection->rootWindow(), atspiBusAtom, XCB_ATOM_STRING, 0, 128); - Q_ASSERT(!reply->bytes_after); + if (!reply) + return nullptr; + char *data = (char *)xcb_get_property_value(reply.get()); int length = xcb_get_property_value_length(reply.get()); return new QByteArray(data, length); } - return 0; + + return nullptr; } void QXcbNativeInterface::setAppTime(QScreen* screen, xcb_timestamp_t time) |