From a5cded843f495b4276a8289b1324778d97bed5ba Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Mon, 11 Feb 2019 10:52:06 +0100 Subject: [PATCH 01/27] Avoid creating wide images with negative bytesPerLine The QImage API can not handle images with more bytes per line than what an integer can hold. Fixes: QTBUG-73731 Fixes: QTBUG-73732 Change-Id: Ieed6fec7645661fd58d8d25335f806faaa1bb3e9 Reviewed-by: Thiago Macieira --- src/gui/image/qimage.cpp | 11 +++++++++-- src/gui/image/qimage.h | 4 ++++ src/gui/image/qimage_p.h | 6 ++++++ tests/auto/gui/image/qimage/tst_qimage.cpp | 20 ++++++++++++++++++++ 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index 9897c3aa6f..3e18ca6528 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -124,7 +124,7 @@ QImageData * QImageData::create(const QSize &size, QImage::Format format) int height = size.height(); int depth = qt_depthForFormat(format); auto params = calculateImageParameters(width, height, depth); - if (params.bytesPerLine < 0) + if (!params.isValid()) return nullptr; QScopedPointer d(new QImageData); @@ -781,7 +781,7 @@ QImageData *QImageData::create(uchar *data, int width, int height, int bpl, QIm const int depth = qt_depthForFormat(format); auto params = calculateImageParameters(width, height, depth); - if (params.totalSize < 0) + if (!params.isValid()) return nullptr; if (bpl > 0) { @@ -1484,10 +1484,17 @@ qsizetype QImage::sizeInBytes() const \sa scanLine() */ +#if QT_VERSION >= QT_VERSION_CHECK(6,0,0) +qsizetype QImage::bytesPerLine() const +{ + return d ? d->bytes_per_line : 0; +} +#else int QImage::bytesPerLine() const { return d ? d->bytes_per_line : 0; } +#endif /*! diff --git a/src/gui/image/qimage.h b/src/gui/image/qimage.h index 4b7a3b1ead..6505fd5845 100644 --- a/src/gui/image/qimage.h +++ b/src/gui/image/qimage.h @@ -227,7 +227,11 @@ public: uchar *scanLine(int); const uchar *scanLine(int) const; const uchar *constScanLine(int) const; +#if QT_VERSION >= QT_VERSION_CHECK(6,0,0) + qsizetype bytesPerLine() const; +#else int bytesPerLine() const; +#endif bool valid(int x, int y) const; bool valid(const QPoint &pt) const; diff --git a/src/gui/image/qimage_p.h b/src/gui/image/qimage_p.h index e3a6c53833..a0a3b5406e 100644 --- a/src/gui/image/qimage_p.h +++ b/src/gui/image/qimage_p.h @@ -109,6 +109,7 @@ struct Q_GUI_EXPORT QImageData { // internal image data struct ImageSizeParameters { qsizetype bytesPerLine; qsizetype totalSize; + bool isValid() const { return bytesPerLine > 0 && totalSize > 0; } }; static ImageSizeParameters calculateImageParameters(qsizetype width, qsizetype height, qsizetype depth); }; @@ -135,6 +136,11 @@ QImageData::calculateImageParameters(qsizetype width, qsizetype height, qsizetyp qsizetype dummy; if (mul_overflow(height, qsizetype(sizeof(uchar *)), &dummy)) return invalid; // why is this here? +#if QT_VERSION < QT_VERSION_CHECK(6,0,0) + // Disallow images where width * depth calculations might overflow + if (width > (INT_MAX - 31) / depth) + return invalid; +#endif return { bytes_per_line, total_size }; } diff --git a/tests/auto/gui/image/qimage/tst_qimage.cpp b/tests/auto/gui/image/qimage/tst_qimage.cpp index eded206d37..6bc27a6e16 100644 --- a/tests/auto/gui/image/qimage/tst_qimage.cpp +++ b/tests/auto/gui/image/qimage/tst_qimage.cpp @@ -230,6 +230,8 @@ private slots: void convertColorTable(); + void wideImage(); + #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) void toWinHBITMAP_data(); void toWinHBITMAP(); @@ -3535,6 +3537,24 @@ void tst_QImage::convertColorTable() QCOMPARE(rgb32.pixel(0,0), 0xffffffff); } +void tst_QImage::wideImage() +{ + // QTBUG-73731 and QTBUG-73732 + QImage i(538994187, 2, QImage::Format_ARGB32); + QImage i2(32, 32, QImage::Format_ARGB32); + i2.fill(Qt::white); + + // Test that it doesn't crash: + QPainter painter(&i); + // With the composition mode is SourceOver out it's an invalid write + // With the composition mode is Source it's an invalid read + painter.drawImage(0, 0, i2); + painter.setCompositionMode(QPainter::CompositionMode_Source); + painter.drawImage(0, 0, i2); + + // Qt6: Test that it actually works on 64bit architectures. +} + #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) QT_BEGIN_NAMESPACE Q_GUI_EXPORT HBITMAP qt_imageToWinHBITMAP(const QImage &p, int hbitmapFormat = 0); From 1366c4f04645d74e83847687adcf61ecfa20b3d2 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Mon, 18 Feb 2019 10:44:21 +0100 Subject: [PATCH 02/27] androiddeployqt: Do not check for stdcpp-path in auxiliary mode Fixes: QBS-1429 Change-Id: I189bc42fdee5e63f55705084247fbfc4448a6b65 Reviewed-by: Joerg Bornemann --- src/tools/androiddeployqt/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/androiddeployqt/main.cpp b/src/tools/androiddeployqt/main.cpp index 712a8091fb..6f08238bcc 100644 --- a/src/tools/androiddeployqt/main.cpp +++ b/src/tools/androiddeployqt/main.cpp @@ -891,7 +891,7 @@ bool readInputFile(Options *options) options->extraPlugins = extraPlugins.toString().split(QLatin1Char(',')); } - { + if (!options->auxMode) { const QJsonValue stdcppPath = jsonObject.value(QStringLiteral("stdcpp-path")); if (stdcppPath.isUndefined()) { fprintf(stderr, "No stdcpp-path defined in json file.\n"); From 93a78799c3df7c8859b2d9addad45bb4a535dc97 Mon Sep 17 00:00:00 2001 From: Yuhang Zhao <2546789017@qq.com> Date: Wed, 13 Feb 2019 23:26:55 +0800 Subject: [PATCH 03/27] Fix compilation with icc, converting between egl's and gl's Error types Each has two constructors from the other, one copying the other moving; and this leads to an ambiguous overload when converting Texture::onDestroy()'s gl::error to the egl::Error that gl::Context::onDestroy() returns. Passing the value through a temporary prevents the move-constructor from being attempted and saves the day. Thanks to Ville Voutilainen for suggesting the fix. Fixes: QTBUG-73698 Change-Id: I628173399a73cee2e253201bc3e8d3e6477a2fbf Reviewed-by: Oliver Wolff Reviewed-by: Edward Welbourne --- src/3rdparty/angle/src/libANGLE/Context.cpp | 3 +- src/3rdparty/angle/src/libANGLE/Stream.cpp | 8 +- src/3rdparty/angle/src/libANGLE/Texture.cpp | 3 +- .../libANGLE/renderer/d3d/d3d9/Renderer9.cpp | 3 +- ...with-icc-converting-between-egl-s-an.patch | 93 +++++++++++++++++++ 5 files changed, 104 insertions(+), 6 deletions(-) create mode 100644 src/angle/patches/0013-Fix-compilation-with-icc-converting-between-egl-s-an.patch diff --git a/src/3rdparty/angle/src/libANGLE/Context.cpp b/src/3rdparty/angle/src/libANGLE/Context.cpp index f638beda58..84f7936feb 100644 --- a/src/3rdparty/angle/src/libANGLE/Context.cpp +++ b/src/3rdparty/angle/src/libANGLE/Context.cpp @@ -451,7 +451,8 @@ egl::Error Context::onDestroy(const egl::Display *display) for (auto &zeroTexture : mZeroTextures) { - ANGLE_TRY(zeroTexture.second->onDestroy(this)); + auto result = zeroTexture.second->onDestroy(this); + ANGLE_TRY(egl::Error(result)); zeroTexture.second.set(this, nullptr); } mZeroTextures.clear(); diff --git a/src/3rdparty/angle/src/libANGLE/Stream.cpp b/src/3rdparty/angle/src/libANGLE/Stream.cpp index 68279976b7..e384c7d486 100644 --- a/src/3rdparty/angle/src/libANGLE/Stream.cpp +++ b/src/3rdparty/angle/src/libANGLE/Stream.cpp @@ -192,8 +192,9 @@ Error Stream::consumerAcquire(const gl::Context *context) { if (mPlanes[i].texture != nullptr) { - ANGLE_TRY(mPlanes[i].texture->acquireImageFromStream( - context, mProducerImplementation->getGLFrameDescription(i))); + auto result = mPlanes[i].texture->acquireImageFromStream( + context, mProducerImplementation->getGLFrameDescription(i)); + ANGLE_TRY(Error(result)); } } @@ -213,7 +214,8 @@ Error Stream::consumerRelease(const gl::Context *context) { if (mPlanes[i].texture != nullptr) { - ANGLE_TRY(mPlanes[i].texture->releaseImageFromStream(context)); + auto result = mPlanes[i].texture->releaseImageFromStream(context); + ANGLE_TRY(Error(result)); } } diff --git a/src/3rdparty/angle/src/libANGLE/Texture.cpp b/src/3rdparty/angle/src/libANGLE/Texture.cpp index da92e65916..7447604fe6 100644 --- a/src/3rdparty/angle/src/libANGLE/Texture.cpp +++ b/src/3rdparty/angle/src/libANGLE/Texture.cpp @@ -550,7 +550,8 @@ Error Texture::onDestroy(const Context *context) { if (mBoundSurface) { - ANGLE_TRY(mBoundSurface->releaseTexImage(context, EGL_BACK_BUFFER)); + auto result = mBoundSurface->releaseTexImage(context, EGL_BACK_BUFFER); + ANGLE_TRY(Error(result)); mBoundSurface = nullptr; } if (mBoundStream) diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp index 75c6298868..b583273641 100644 --- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp +++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp @@ -376,7 +376,8 @@ egl::Error Renderer9::initializeDevice() ASSERT(!mBlit); mBlit = new Blit9(this); - ANGLE_TRY(mBlit->initialize()); + auto result = mBlit->initialize(); + ANGLE_TRY(egl::Error(result)); ASSERT(!mVertexDataManager && !mIndexDataManager); mVertexDataManager = new VertexDataManager(this); diff --git a/src/angle/patches/0013-Fix-compilation-with-icc-converting-between-egl-s-an.patch b/src/angle/patches/0013-Fix-compilation-with-icc-converting-between-egl-s-an.patch new file mode 100644 index 0000000000..6d3b1cac08 --- /dev/null +++ b/src/angle/patches/0013-Fix-compilation-with-icc-converting-between-egl-s-an.patch @@ -0,0 +1,93 @@ +From 2d8118620d4871f74a3ddca233529ff540384477 Mon Sep 17 00:00:00 2001 +From: Yuhang Zhao <2546789017@qq.com> +Date: Wed, 13 Feb 2019 23:26:55 +0800 +Subject: [PATCH] Fix compilation with icc, converting between egl's and gl's + Error types + +Each has two constructors from the other, one copying the other +moving; and this leads to an ambiguous overload when converting +Texture::onDestroy()'s gl::error to the egl::Error that +gl::Context::onDestroy() returns. Passing the value through a +temporary prevents the move-constructor from being attempted and saves +the day. Thanks to Ville Voutilainen for suggesting the fix. + +Fixes: QTBUG-73698 +Change-Id: I628173399a73cee2e253201bc3e8d3e6477a2fbf +--- + src/3rdparty/angle/src/libANGLE/Context.cpp | 3 ++- + src/3rdparty/angle/src/libANGLE/Stream.cpp | 8 +++++--- + src/3rdparty/angle/src/libANGLE/Texture.cpp | 3 ++- + .../angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp | 3 ++- + 4 files changed, 11 insertions(+), 6 deletions(-) + +diff --git a/src/3rdparty/angle/src/libANGLE/Context.cpp b/src/3rdparty/angle/src/libANGLE/Context.cpp +index f638beda58..84f7936feb 100644 +--- a/src/3rdparty/angle/src/libANGLE/Context.cpp ++++ b/src/3rdparty/angle/src/libANGLE/Context.cpp +@@ -451,7 +451,8 @@ egl::Error Context::onDestroy(const egl::Display *display) + + for (auto &zeroTexture : mZeroTextures) + { +- ANGLE_TRY(zeroTexture.second->onDestroy(this)); ++ auto result = zeroTexture.second->onDestroy(this); ++ ANGLE_TRY(egl::Error(result)); + zeroTexture.second.set(this, nullptr); + } + mZeroTextures.clear(); +diff --git a/src/3rdparty/angle/src/libANGLE/Stream.cpp b/src/3rdparty/angle/src/libANGLE/Stream.cpp +index 68279976b7..e384c7d486 100644 +--- a/src/3rdparty/angle/src/libANGLE/Stream.cpp ++++ b/src/3rdparty/angle/src/libANGLE/Stream.cpp +@@ -192,8 +192,9 @@ Error Stream::consumerAcquire(const gl::Context *context) + { + if (mPlanes[i].texture != nullptr) + { +- ANGLE_TRY(mPlanes[i].texture->acquireImageFromStream( +- context, mProducerImplementation->getGLFrameDescription(i))); ++ auto result = mPlanes[i].texture->acquireImageFromStream( ++ context, mProducerImplementation->getGLFrameDescription(i)); ++ ANGLE_TRY(Error(result)); + } + } + +@@ -213,7 +214,8 @@ Error Stream::consumerRelease(const gl::Context *context) + { + if (mPlanes[i].texture != nullptr) + { +- ANGLE_TRY(mPlanes[i].texture->releaseImageFromStream(context)); ++ auto result = mPlanes[i].texture->releaseImageFromStream(context); ++ ANGLE_TRY(Error(result)); + } + } + +diff --git a/src/3rdparty/angle/src/libANGLE/Texture.cpp b/src/3rdparty/angle/src/libANGLE/Texture.cpp +index da92e65916..7447604fe6 100644 +--- a/src/3rdparty/angle/src/libANGLE/Texture.cpp ++++ b/src/3rdparty/angle/src/libANGLE/Texture.cpp +@@ -550,7 +550,8 @@ Error Texture::onDestroy(const Context *context) + { + if (mBoundSurface) + { +- ANGLE_TRY(mBoundSurface->releaseTexImage(context, EGL_BACK_BUFFER)); ++ auto result = mBoundSurface->releaseTexImage(context, EGL_BACK_BUFFER); ++ ANGLE_TRY(Error(result)); + mBoundSurface = nullptr; + } + if (mBoundStream) +diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp +index 75c6298868..b583273641 100644 +--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp ++++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp +@@ -376,7 +376,8 @@ egl::Error Renderer9::initializeDevice() + + ASSERT(!mBlit); + mBlit = new Blit9(this); +- ANGLE_TRY(mBlit->initialize()); ++ auto result = mBlit->initialize(); ++ ANGLE_TRY(egl::Error(result)); + + ASSERT(!mVertexDataManager && !mIndexDataManager); + mVertexDataManager = new VertexDataManager(this); +-- +2.20.1.windows.1 + From 655e8623afed01de63ce43f55227fb019e800fe9 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Thu, 14 Feb 2019 11:45:13 +0100 Subject: [PATCH 04/27] Fix QSplashscreen positioning on Android Android does not use QPlatformWindow::initialGeometry(), so the underlying assumption of 56e92dfdf255231aff0034d2e197fd096da7f0c0 was wrong. Try to explicitly find a screen and default to primary. Fixes: QTBUG-73794 Change-Id: Iba3e70657a60babfcedf751335ca55cb971a4f99 Reviewed-by: Oliver Wolff --- src/widgets/widgets/qsplashscreen.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/widgets/widgets/qsplashscreen.cpp b/src/widgets/widgets/qsplashscreen.cpp index 4af4f90119..bf6bf1c7c9 100644 --- a/src/widgets/widgets/qsplashscreen.cpp +++ b/src/widgets/widgets/qsplashscreen.cpp @@ -289,8 +289,7 @@ void QSplashScreen::setPixmap(const QPixmap &pixmap) // 1) If a QDesktopScreenWidget is found in the parent hierarchy, use that (see docs on // QSplashScreen(QWidget *, QPixmap). // 2) If a widget with associated QWindow is found, use that -// 3) When nothing can be found, do not position the widget, allowing for -// QPlatformWindow::initialGeometry() to center it over the cursor +// 3) When nothing can be found, try to center it over the cursor static inline int screenNumberOf(const QDesktopScreenWidget *dsw) { @@ -307,7 +306,15 @@ const QScreen *QSplashScreenPrivate::screenFor(const QWidget *w) if (QWindow *window = p->windowHandle()) return window->screen(); } - return nullptr; +#if QT_CONFIG(cursor) + // Note: We could rely on QPlatformWindow::initialGeometry() to center it + // over the cursor, but not all platforms (namely Android) use that. + if (QGuiApplication::screens().size() > 1) { + if (auto screenAtCursor = QGuiApplication::screenAt(QCursor::pos())) + return screenAtCursor; + } +#endif // cursor + return QGuiApplication::primaryScreen(); } void QSplashScreenPrivate::setPixmap(const QPixmap &p, const QScreen *screen) From 0f163887b526d00ccdcead907dde042aa370fc16 Mon Sep 17 00:00:00 2001 From: Juha Karjalainen Date: Tue, 19 Feb 2019 13:57:47 +0200 Subject: [PATCH 05/27] Fix blacklisting tst_QTimer::basic_chrono() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Blacklisting did not work as blacklist should have contained osx instead macos Change-Id: Ifd76a38d371ccce545eb5df030aaa819b00a5b48 Reviewed-by: Jędrzej Nowacki --- tests/auto/corelib/kernel/qtimer/BLACKLIST | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/auto/corelib/kernel/qtimer/BLACKLIST b/tests/auto/corelib/kernel/qtimer/BLACKLIST index c31e15f171..16cbab4587 100644 --- a/tests/auto/corelib/kernel/qtimer/BLACKLIST +++ b/tests/auto/corelib/kernel/qtimer/BLACKLIST @@ -2,4 +2,4 @@ windows osx [basic_chrono] -macos +osx From d6d80ff2e925c5c52de498535cfdb808b3bd3670 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Thu, 21 Feb 2019 16:43:58 +0100 Subject: [PATCH 06/27] Automatic resources: Fix tooling support The files to be put into an auto-generated qrc file must not simply disappear, because IDE users still need to have them in the project tree. So we add them to OTHER_FILES now. Task-number: QTCREATORBUG-20103 Task-number: QTCREATORBUG-20104 Change-Id: I8a9136491f975def7c33385e375c407815ad269a Reviewed-by: hjk Reviewed-by: Joerg Bornemann --- mkspecs/features/resources.prf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mkspecs/features/resources.prf b/mkspecs/features/resources.prf index bb2a55b93d..b4e0db6445 100644 --- a/mkspecs/features/resources.prf +++ b/mkspecs/features/resources.prf @@ -28,6 +28,7 @@ for(resource, RESOURCES) { !exists($$absolute_path($$resource, $$_PRO_FILE_PWD_)): \ warning("Failure to find: $$resource") qmake_immediate.files += $$resource + OTHER_FILES *= $$resource } RESOURCES -= $$resource next() @@ -57,6 +58,7 @@ for(resource, RESOURCES) { alias = $$relative_path($$file, $$abs_base) resource_file_content += \ "$$xml_escape($$file)" + OTHER_FILES *= $$file } } From 90959f7080d0d6d9953f0bc5fa7ac01a3e82e85f Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Mon, 25 Feb 2019 12:08:21 +0100 Subject: [PATCH 07/27] Add all library dependencies for static OpenSSL builds on Windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Static builds of OpenSSL can now be linked with -openssl-linked without passing additional library dependencies like user32 or advapi32. Fixes: QTBUG-73205 Change-Id: I66c13096b0a1466c1e6dfbd014123e18655270e6 Reviewed-by: Mårten Nordheim Reviewed-by: Timur Pocheptsov --- src/network/configure.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/network/configure.json b/src/network/configure.json index f3e18662aa..2c005f0efb 100644 --- a/src/network/configure.json +++ b/src/network/configure.json @@ -84,11 +84,11 @@ "sources": [ { "type": "openssl" }, { - "libs": "-lssleay32 -llibeay32", + "libs": "-lssleay32 -llibeay32 -lUser32 -lWs2_32 -lAdvapi32 -lGdi32", "condition": "config.win32" }, { - "libs": "-llibssl -llibcrypto", + "libs": "-llibssl -llibcrypto -lUser32 -lWs2_32 -lAdvapi32 -lCrypt32", "condition": "config.msvc" }, { From 96f6cab22cab252cbe7a98bbeadde95497e0bd75 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Tue, 26 Feb 2019 10:38:58 +0100 Subject: [PATCH 08/27] Blacklist nouveau and llvmpipe for multithreading After removing Mesa drivers from being blank blacklisted, we still need to blacklist nouveau specifically due to their lack of proper locking: https://bugs.freedesktop.org/show_bug.cgi?id=91632 llvmpipe is similarly blacklisted for now, as we lack enough information to know if the underlying issue behind QTCREATORBUG-10666 has been solved. Fixes: QTBUG-73715 Change-Id: I1a60b562cd9db94fa8462b922d6bfeebf0088dc5 Reviewed-by: Laszlo Agocs --- .../xcb_glx/qglxintegration.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) 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), From 01f5d41a406b7baf1cb01692c870e5084fc11b1f Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Wed, 20 Feb 2019 13:30:52 +0100 Subject: [PATCH 09/27] Make tst_QUdpSocket::lincLocalIPv6 less sadistic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It fails on CI (Windows 10). Given our qabstractsocket disables read notifications/stops emitting readyRead if it already has pending data (unbuffered, aka UDP socket type) - make sure we do not suffer from this. The change does not affect the test's logic (unless the logic was to fail), it just makes it more fail-proof. Change-Id: I6c9b7ded20478f675260872a2a7032b4f356f197 Fixes: QTBUG-73884 Reviewed-by: Edward Welbourne Reviewed-by: Mårten Nordheim (cherry picked from commit d3eb9e944ac73f238b8716bb25b8051377bba946) Reviewed-by: Timur Pocheptsov --- tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp b/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp index 8ebb27e58c..707c1acf48 100644 --- a/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp +++ b/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp @@ -1640,15 +1640,14 @@ void tst_QUdpSocket::linkLocalIPv6() sockets << s; } - QUdpSocket neutral; - QVERIFY(neutral.bind(QHostAddress(QHostAddress::AnyIPv6))); - QSignalSpy neutralReadSpy(&neutral, SIGNAL(readyRead())); - QByteArray testData("hello"); foreach (QUdpSocket *s, sockets) { + QUdpSocket neutral; + QVERIFY(neutral.bind(QHostAddress(QHostAddress::AnyIPv6))); + QSignalSpy neutralReadSpy(&neutral, SIGNAL(readyRead())); + QSignalSpy spy(s, SIGNAL(readyRead())); - neutralReadSpy.clear(); QVERIFY(s->writeDatagram(testData, s->localAddress(), neutral.localPort())); QTRY_VERIFY(neutralReadSpy.count() > 0); //note may need to accept a firewall prompt From e8d3306c8f86bf21648693521d8be91bb8f1335e Mon Sep 17 00:00:00 2001 From: Antti Kokko Date: Wed, 20 Feb 2019 15:21:30 +0200 Subject: [PATCH 10/27] Add changes file for Qt 5.12.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Done-with: Thiago Macieira Change-Id: Ia8c265403aa557be180eaa634474fb5f96a52b9f Reviewed-by: Edward Welbourne Reviewed-by: Tor Arne Vestbø Reviewed-by: Thiago Macieira --- dist/changes-5.12.2 | 105 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 dist/changes-5.12.2 diff --git a/dist/changes-5.12.2 b/dist/changes-5.12.2 new file mode 100644 index 0000000000..dc61d135a7 --- /dev/null +++ b/dist/changes-5.12.2 @@ -0,0 +1,105 @@ +Qt 5.12.2 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.12.0 through 5.12.1. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + +https://doc.qt.io/qt-5/index.html + +The Qt version 5.12 series is binary compatible with the 5.11.x series. +Applications compiled for 5.11 will continue to run with 5.12. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + +https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Important Behavior Changes * +**************************************************************************** + + - QtTestLib: + * [QTBUG-72928] Blacklisting of tests will be taken into account for + XPASS and XFAIL. A blacklisted test that causes an XPASS will no + longer be a fail. + +**************************************************************************** +* QtCore * +**************************************************************************** + + - [QTBUG-72885] Fixed a number of warnings with Clang or Clang-Tidy in + Qt headers related to alignment of Qt private classes. + + - QDate, QTime and QDateTime; + * [QTBUG-51208] Corrected documentation of how non-placeholder + characters are handled in format patterns passed to toString(). + + - QCoreApplication: + * [QTBUG-57171] Fixed an out-of-bounds access if the translatable + string passed to tr() ended in '%'. + + - QFileInfo: + * [QTBUG-72644] Fixed a bug that would cause QFileInfo to report an + link incorrectly as a non-link. + + - QLocale: + * Fixed a crash if qDebug() is used after main() has exited. + * [QTBUG-73403] Fixed a race condition in getting the system locale + (possible regression from Qt 5.11.x) + + - QSysInfo: + * Fixed a bug on BSD systems in getting the machineUniqueId(). + * Fixed a bug on Windows in 32-bit applications getting the + machineUniqueId() when the OS is 64-bit. + + - QWaitCondition: + * Fixed handling of wait(QDeadlineTimer::Forever) on 32-bit platforms. + +**************************************************************************** +* QtWidgets * +**************************************************************************** + + - ItemViews: + * Fixed a regression with wrongly drawn centered/right aligned item + texts + +**************************************************************************** +* Third-Party Code * +**************************************************************************** + + - libpng was updated to version 1.6.36 + +**************************************************************************** +* Freetype * +**************************************************************************** + + - Upgraded bundled Freetype version to 2.9.1. This also adds support for + the latest emoji font in use on Android 9. + +**************************************************************************** +* Android * +**************************************************************************** + + - Added the --no-strip command line option to androiddeployqt. + + - qmake: + * Can now set the version name and code for Android using + ANDROID_VERSION_NAME and ANDROID_VERSION_CODE respectively in the pro + file. + +**************************************************************************** +* Windows * +**************************************************************************** + + - Fixed an issue where loading fonts from files or data would sometimes + mistakenly classify them as oblique. + +**************************************************************************** +* qmake * +**************************************************************************** + + - [QTBUG-27079] A new feature "cmdline" was added that implies "CONFIG += + console" and "CONFIG -= app_bundle". From 856fb1ab44722f5165fb6b5dec0bd748006acd10 Mon Sep 17 00:00:00 2001 From: Gatis Paeglis Date: Tue, 5 Mar 2019 10:49:10 +0100 Subject: [PATCH 11/27] xcb: check for nullptr when reading AT_SPI_BUS property We always have to check the return value of xcb_get_property(), but this code did not do it. These xcb functions do not check for validity of the pointer, so we have to make sure that we pass-in something valid: void * xcb_get_property_value (const xcb_get_property_reply_t *R) { return (void *) (R + 1); } int xcb_get_property_value_length (const xcb_get_property_reply_t *R) { return (R->value_len * (R->format / 8)); } Fixes: QTBUG-74067 Change-Id: Iabbc81e6079d96c7314d16dd78783de07f9ad629 Reviewed-by: Mikhail Svetkin Reviewed-by: Frederik Gladhorn --- src/plugins/platforms/xcb/qxcbnativeinterface.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp index 524af5a2a7..6f3584f509 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) From 77a4915bf900aac0c96260018c0a4ccfbdd7c094 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Wed, 30 Jan 2019 13:14:38 +0100 Subject: [PATCH 12/27] macOS: Add IOSurface based backingstore for layer-backed views MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The naïve approach used for layer-backing in the past caused a detach of the backingstore QImage on each beginPaint, since the image was assigned to the layer via a CGImageRef that participated in the QImage implicit sharing (and had to, so we couldn't get around that). We now use IOSurfaces, wrapped in a QPlatformGraphicsBuffer abstraction. The surfaces can be assigned to the layer's content the same way images could, but allows us to reason more closely about whether or a buffer is in use, and increases the chance that we will have a zero-copy path to the screen. Unless the window has requested a surface format with single buffering we use a dynamic swap chain of buffers. In most situations there will be two buffers in play, one assigned to the layer and one ready to paint to, but during resize and some other situations the buffers will grow temporarily to accommodate the increased back-pressure. Since QBackingStore is documented as having single-buffer behavior, we take care to persist content between the buffers before every swap. By doing this before swapping, instead of before each paint, we can avoid preserving areas that will be painted to anyways, and will in many situations (such as blinking cursors e.g.) end up not persisting anything. The RasterGL surface case is handled by reading out the buffer data and doing a manual texture upload. In the future we can support direct texture access via CGLTexImageIOSurface2D, but this requires QPlatformBackingStore::composeAndFlush to learn how to support other targets than GL_TEXTURE_2D, as CGLTexImageIOSurface2D only works with GL_TEXTURE_RECTANGLE_ARB targets. Fixes: QTBUG-48763 Fixes: QTBUG-72360 Fixes: QTBUG-71162 Change-Id: Ica12f69b244e54d0fd31c929730d15657c286af8 Reviewed-by: Morten Johan Sørvig --- src/gui/kernel/qplatformgraphicsbuffer.h | 2 + src/plugins/platforms/cocoa/cocoa.pro | 4 +- .../platforms/cocoa/qcocoabackingstore.h | 52 +- .../platforms/cocoa/qcocoabackingstore.mm | 569 ++++++++++++++---- .../platforms/cocoa/qcocoaintegration.mm | 11 +- .../cocoa/qiosurfacegraphicsbuffer.h | 77 +++ .../cocoa/qiosurfacegraphicsbuffer.mm | 188 ++++++ .../platforms/cocoa/qnsview_drawing.mm | 7 + 8 files changed, 784 insertions(+), 126 deletions(-) create mode 100644 src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.h create mode 100644 src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.mm diff --git a/src/gui/kernel/qplatformgraphicsbuffer.h b/src/gui/kernel/qplatformgraphicsbuffer.h index 0aeef946e6..11566e1201 100644 --- a/src/gui/kernel/qplatformgraphicsbuffer.h +++ b/src/gui/kernel/qplatformgraphicsbuffer.h @@ -71,12 +71,14 @@ public: TextureAccess = 0x04, HWCompositor = 0x08 }; + Q_ENUM(AccessType); Q_DECLARE_FLAGS(AccessTypes, AccessType); enum Origin { OriginBottomLeft, OriginTopLeft }; + Q_ENUM(Origin); virtual ~QPlatformGraphicsBuffer(); 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 +#include +#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 colorSpace); + + QRegion dirtyRegion; // In unscaled coordinates + QImage *asImage(); + + private: + qreal m_devicePixelRatio; + QImage m_image; + }; + + void ensureBackBuffer(); + bool recreateBackBufferIfNeeded(); + bool prepareForFlush(); + + std::list> 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 + +#include + 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(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); } + // 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(); + + // 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(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"]; +#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"); + // 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 cgImage = CGImageCreateCopyWithColorSpace( QCFType(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(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]; + // Create temporary image to use for blitting, without copying image data + NSImage *backingStoreImage = [[[NSImage alloc] initWithCGImage:cgImage size:NSZeroSize] autorelease]; - // 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(); - - // 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(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"]; -#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"); - - // 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())); - - CGRect viewRect = viewLocalRect.toCGRect(); - - if (windowHasUnifiedToolbar()) - NSDrawWindowBackground(viewRect); - - [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()]; - } - } -#endif - } - - // ------------------------------------------------------------------------- - - if (shouldHandleViewLockManually) - [view unlockFocus]; - - if (drawingOutsideOfDisplayCycle) { - redrawRoundedBottomCorners([view convertRect:region.boundingRect().toCGRect() toView:nil]); - [view.window flushWindow]; + 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)))); } } - // Done flushing to either CALayer or NSWindow backingstore + 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(); + + if (windowHasUnifiedToolbar()) + NSDrawWindowBackground(viewRect); + + [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()]; + } + } +#endif + } + + // ------------------------------------------------------------------------- + + if (shouldHandleViewLockManually) + [view unlockFocus]; + + if (drawingOutsideOfDisplayCycle) { + redrawRoundedBottomCorners([view convertRect:region.boundingRect().toCGRect() toView:nil]); + [view.window flushWindow]; + } + + + // Done flushing to NSWindow backingstore QCocoaWindow *topLevelCocoaWindow = static_cast(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 +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 +constexpr backwards_t backwards(R&& r) { return {std::forward(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(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(window()->handle())->view(); + auto colorSpace = QCFType::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(window()->handle())->view(); + NSView *flushedView = static_cast(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 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..6d6c66e242 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.mm +++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm @@ -407,7 +407,16 @@ QPlatformOpenGLContext *QCocoaIntegration::createPlatformOpenGLContext(QOpenGLCo QPlatformBackingStore *QCocoaIntegration::createPlatformBackingStore(QWindow *window) const { - return new QCocoaBackingStore(window); + QCocoaWindow *platformWindow = static_cast(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 +#include + +QT_BEGIN_NAMESPACE + +class QIOSurfaceGraphicsBuffer : public QPlatformGraphicsBuffer +{ +public: + QIOSurfaceGraphicsBuffer(const QSize &size, const QPixelFormat &format, QCFType 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 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 +#include + +#include +#include + +// 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 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(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 e9af90a45c..0cebc8d98d 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; From 9b72613512a36a0ab0ec5d58f0f34016d959a9c1 Mon Sep 17 00:00:00 2001 From: BogDan Vatra Date: Tue, 12 Mar 2019 10:48:05 +0200 Subject: [PATCH 13/27] Fix link error on linux On debian buster (using gcc 8.2) I'm getting link error: ...86_64-linux-gnu/libdl.so /usr/lib/x86_64-linux-gnu/libEGL.so /usr/bin/ld: .obj/qxcbeglintegration.o:(.data.rel+0x8b8): undefined reference to `typeinfo for QXcbBasicConnection' Change-Id: I4c2b5aad8eac44737982d68f46fbc80e3b830668 Reviewed-by: Gatis Paeglis --- src/plugins/platforms/xcb/qxcbconnection_basic.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/platforms/xcb/qxcbconnection_basic.h b/src/plugins/platforms/xcb/qxcbconnection_basic.h index ca91f7fa45..6e407a5f80 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 #include @@ -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: From 50f3f8f6bb8f5482c713480fad80421760184216 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Wed, 13 Mar 2019 08:34:01 +0100 Subject: [PATCH 14/27] Use High DPI pixmaps in prominent examples and tests Set AA_UseHighDpiPixmaps. Task-number: QTBUG-52622 Change-Id: Ic4373a9c94952f50bc1ad36bcc0dec850efc124a Reviewed-by: Robert Loehning Reviewed-by: Richard Moe Gustavsen --- examples/widgets/desktop/systray/main.cpp | 2 ++ examples/widgets/dialogs/classwizard/main.cpp | 2 ++ examples/widgets/dialogs/licensewizard/main.cpp | 2 ++ examples/widgets/dialogs/standarddialogs/main.cpp | 1 + examples/widgets/dialogs/trivialwizard/trivialwizard.cpp | 2 ++ tests/manual/dialogs/main.cpp | 4 ++++ 6 files changed, 13 insertions(+) diff --git a/examples/widgets/desktop/systray/main.cpp b/examples/widgets/desktop/systray/main.cpp index 49b0e10412..d981415b44 100644 --- a/examples/widgets/desktop/systray/main.cpp +++ b/examples/widgets/desktop/systray/main.cpp @@ -59,6 +59,8 @@ int main(int argc, char *argv[]) { Q_INIT_RESOURCE(systray); + QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); + QApplication app(argc, argv); if (!QSystemTrayIcon::isSystemTrayAvailable()) { diff --git a/examples/widgets/dialogs/classwizard/main.cpp b/examples/widgets/dialogs/classwizard/main.cpp index 72eb1ae6ec..03ffe97a23 100644 --- a/examples/widgets/dialogs/classwizard/main.cpp +++ b/examples/widgets/dialogs/classwizard/main.cpp @@ -59,6 +59,8 @@ int main(int argc, char *argv[]) { Q_INIT_RESOURCE(classwizard); + QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); + QApplication app(argc, argv); #ifndef QT_NO_TRANSLATION diff --git a/examples/widgets/dialogs/licensewizard/main.cpp b/examples/widgets/dialogs/licensewizard/main.cpp index 025b79243c..ed46e792d0 100644 --- a/examples/widgets/dialogs/licensewizard/main.cpp +++ b/examples/widgets/dialogs/licensewizard/main.cpp @@ -59,6 +59,8 @@ int main(int argc, char *argv[]) { Q_INIT_RESOURCE(licensewizard); + QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); + QApplication app(argc, argv); #ifndef QT_NO_TRANSLATION diff --git a/examples/widgets/dialogs/standarddialogs/main.cpp b/examples/widgets/dialogs/standarddialogs/main.cpp index 150d07318e..a0cded4f47 100644 --- a/examples/widgets/dialogs/standarddialogs/main.cpp +++ b/examples/widgets/dialogs/standarddialogs/main.cpp @@ -59,6 +59,7 @@ int main(int argc, char *argv[]) { + QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); QApplication app(argc, argv); QGuiApplication::setApplicationDisplayName(Dialog::tr("Standard Dialogs")); diff --git a/examples/widgets/dialogs/trivialwizard/trivialwizard.cpp b/examples/widgets/dialogs/trivialwizard/trivialwizard.cpp index fe49bf6304..ce13a59b0c 100644 --- a/examples/widgets/dialogs/trivialwizard/trivialwizard.cpp +++ b/examples/widgets/dialogs/trivialwizard/trivialwizard.cpp @@ -123,6 +123,8 @@ QWizardPage *createConclusionPage() int main(int argc, char *argv[]) //! [9] //! [11] { + QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); + QApplication app(argc, argv); #ifndef QT_NO_TRANSLATION diff --git a/tests/manual/dialogs/main.cpp b/tests/manual/dialogs/main.cpp index 2676ceeb52..07df8f5cf4 100644 --- a/tests/manual/dialogs/main.cpp +++ b/tests/manual/dialogs/main.cpp @@ -126,6 +126,10 @@ void MainWindow::aboutDialog() int main(int argc, char *argv[]) { +#if QT_VERSION >= 0x050600 + QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); +#endif + for (int a = 1; a < argc; ++a) { if (!qstrcmp(argv[a], "-n")) { qDebug("AA_DontUseNativeDialogs"); From c0b265662e618f0d953ed2c389bad9f50ac24f7f Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Mon, 15 Oct 2018 20:36:06 +0300 Subject: [PATCH 15/27] QTranslator: avoid unhandled exception Add std::nothrow param to avoid exception and to check pointer against nullptr. Change-Id: I505abb1ca15b8c10a80b0cd3784a6b0c4c6bcc1c Reviewed-by: Edward Welbourne Reviewed-by: Oswald Buddenhagen Reviewed-by: Allan Sandfeld Jensen --- src/corelib/kernel/qtranslator.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/corelib/kernel/qtranslator.cpp b/src/corelib/kernel/qtranslator.cpp index 744bbfbff5..929554f6bc 100644 --- a/src/corelib/kernel/qtranslator.cpp +++ b/src/corelib/kernel/qtranslator.cpp @@ -72,6 +72,7 @@ #endif #include +#include #include "qobject_p.h" @@ -592,7 +593,7 @@ bool QTranslatorPrivate::do_load(const QString &realname, const QString &directo #endif // QT_USE_MMAP if (!ok) { - d->unmapPointer = new char[d->unmapLength]; + d->unmapPointer = new (std::nothrow) char[d->unmapLength]; if (d->unmapPointer) { file.seek(0); qint64 readResult = file.read(d->unmapPointer, d->unmapLength); From 5dd4f18cef4fb9ae1e121622c793396e7f132924 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Arve=20S=C3=A6ther?= Date: Wed, 13 Mar 2019 15:55:01 +0100 Subject: [PATCH 16/27] Improve documentation for opaque resize in QSplitter This also fixes the documentation for QStyle::SH_Splitter_OpaqueResize Change-Id: If8afb52ae300e9735a8bc6b065327f17d67f4323 Reviewed-by: Paul Wicking --- src/widgets/styles/qstyle.cpp | 4 ++-- src/widgets/widgets/qsplitter.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/widgets/styles/qstyle.cpp b/src/widgets/styles/qstyle.cpp index ac4fb7fbd1..97ec1d3f19 100644 --- a/src/widgets/styles/qstyle.cpp +++ b/src/widgets/styles/qstyle.cpp @@ -1971,8 +1971,8 @@ void QStyle::drawItemPixmap(QPainter *painter, const QRect &rect, int alignment, \value SH_Widget_Animate Deprecated. Use \l{SH_Widget_Animation_Duration} instead. - \value SH_Splitter_OpaqueResize Determines if resizing is opaque - This enum value has been introduced in Qt 5.2 + \value SH_Splitter_OpaqueResize Determines if widgets are resized dynamically (opaquely) while + interactively moving the splitter. This enum value was introduced in Qt 5.2. \value SH_TabBar_ChangeCurrentDelay Determines the delay before the current tab is changed while dragging over the tabbar, in milliseconds. This diff --git a/src/widgets/widgets/qsplitter.cpp b/src/widgets/widgets/qsplitter.cpp index 9e38c8f18a..3b516f36b6 100644 --- a/src/widgets/widgets/qsplitter.cpp +++ b/src/widgets/widgets/qsplitter.cpp @@ -161,11 +161,10 @@ Qt::Orientation QSplitterHandle::orientation() const /*! - Returns \c true if widgets are resized dynamically (opaquely), otherwise - returns \c false. This value is controlled by the QSplitter. + Returns \c true if widgets are resized dynamically (opaquely) while interactively moving the + splitter. Otherwise returns \c false. This value is controlled by the QSplitter. \sa QSplitter::opaqueResize() - */ bool QSplitterHandle::opaqueResize() const { @@ -1483,7 +1482,8 @@ int QSplitter::closestLegalPosition(int pos, int index) /*! \property QSplitter::opaqueResize - \brief whether resizing is opaque + Returns \c true if widgets are resized dynamically (opaquely) while interactively moving the + splitter. Otherwise returns \c false. The default resize behavior is style dependent (determined by the SH_Splitter_OpaqueResize style hint). However, you can override it From 9f9ed383c6910ddfdccc7115b2f8787ba25dd2f6 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 27 Feb 2019 20:42:17 +0100 Subject: [PATCH 17/27] Replace LDEBUG with categorized logging in QTextDocumentLayout(Private) This adds four new logging categories: qt.text.drawing, qt.text.hittest, qt.text.layout and qt.text.layout.table Task-number: QTBUG-72457 Change-Id: Ifbfd6d16231c7f4ba664bc521699e44f98310f77 Reviewed-by: Lars Knoll --- src/gui/text/qtextdocumentlayout.cpp | 81 +++++++++++++--------------- 1 file changed, 36 insertions(+), 45 deletions(-) diff --git a/src/gui/text/qtextdocumentlayout.cpp b/src/gui/text/qtextdocumentlayout.cpp index 323253c70d..2e1a2b5bff 100644 --- a/src/gui/text/qtextdocumentlayout.cpp +++ b/src/gui/text/qtextdocumentlayout.cpp @@ -58,23 +58,17 @@ #include #include #include "private/qfunctions_p.h" +#include #include -// #define LAYOUT_DEBUG - -#ifdef LAYOUT_DEBUG -#define LDEBUG qDebug() -#define INC_INDENT debug_indent += " " -#define DEC_INDENT debug_indent = debug_indent.left(debug_indent.length()-2) -#else -#define LDEBUG if(0) qDebug() -#define INC_INDENT do {} while(0) -#define DEC_INDENT do {} while(0) -#endif - QT_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcDraw, "qt.text.drawing") +Q_LOGGING_CATEGORY(lcHit, "qt.text.hittest") +Q_LOGGING_CATEGORY(lcLayout, "qt.text.layout") +Q_LOGGING_CATEGORY(lcTable, "qt.text.layout.table") + // ################ should probably add frameFormatChange notification! struct QTextLayoutStruct; @@ -583,16 +577,16 @@ QTextDocumentLayoutPrivate::hitTest(QTextFrame *frame, const QFixedPoint &point, QTextFrame *rootFrame = docPrivate->rootFrame(); -// LDEBUG << "checking frame" << frame->firstPosition() << "point=" << point -// << "position" << fd->position << "size" << fd->size; + qCDebug(lcHit) << "checking frame" << frame->firstPosition() << "point=" << point.toPointF() + << "position" << fd->position.toPointF() << "size" << fd->size.toSizeF(); if (frame != rootFrame) { if (relativePoint.y < 0 || relativePoint.x < 0) { *position = frame->firstPosition() - 1; -// LDEBUG << "before pos=" << *position; + qCDebug(lcHit) << "before pos=" << *position; return PointBefore; } else if (relativePoint.y > fd->size.height || relativePoint.x > fd->size.width) { *position = frame->lastPosition() + 1; -// LDEBUG << "after pos=" << *position; + qCDebug(lcHit) << "after pos=" << *position; return PointAfter; } } @@ -666,8 +660,6 @@ QTextDocumentLayoutPrivate::HitPoint QTextDocumentLayoutPrivate::hitTest(QTextFrame::Iterator it, HitPoint hit, const QFixedPoint &p, int *position, QTextLayout **l, Qt::HitTestAccuracy accuracy) const { - INC_INDENT; - for (; !it.atEnd(); ++it) { QTextFrame *c = it.currentFrame(); HitPoint hp; @@ -693,8 +685,7 @@ QTextDocumentLayoutPrivate::hitTest(QTextFrame::Iterator it, HitPoint hit, const } } - DEC_INDENT; -// LDEBUG << "inside=" << hit << " pos=" << *position; + qCDebug(lcHit) << "inside=" << hit << " pos=" << *position; return hit; } @@ -741,15 +732,14 @@ QTextDocumentLayoutPrivate::hitTest(const QTextBlock &bl, const QFixedPoint &poi QTextLayout *tl = bl.layout(); QRectF textrect = tl->boundingRect(); textrect.translate(tl->position()); -// LDEBUG << " checking block" << bl.position() << "point=" << point -// << " tlrect" << textrect; + qCDebug(lcHit) << " checking block" << bl.position() << "point=" << point.toPointF() << " tlrect" << textrect; *position = bl.position(); if (point.y.toReal() < textrect.top()) { -// LDEBUG << " before pos=" << *position; + qCDebug(lcHit) << " before pos=" << *position; return PointBefore; } else if (point.y.toReal() > textrect.bottom()) { *position += bl.length(); -// LDEBUG << " after pos=" << *position; + qCDebug(lcHit) << " after pos=" << *position; return PointAfter; } @@ -781,7 +771,7 @@ QTextDocumentLayoutPrivate::hitTest(const QTextBlock &bl, const QFixedPoint &poi } *position += off; -// LDEBUG << " inside=" << hit << " pos=" << *position; + qCDebug(lcHit) << " inside=" << hit << " pos=" << *position; return hit; } @@ -944,8 +934,7 @@ void QTextDocumentLayoutPrivate::drawFrame(const QPointF &offset, QPainter *pain || off.x() > context.clip.right() || off.x() + fd->size.width.toReal() < context.clip.left())) return; -// LDEBUG << debug_indent << "drawFrame" << frame->firstPosition() << "--" << frame->lastPosition() << "at" << offset; -// INC_INDENT; + qCDebug(lcDraw) << "drawFrame" << frame->firstPosition() << "--" << frame->lastPosition() << "at" << offset; // if the cursor is /on/ a table border we may need to repaint it // afterwards, as we usually draw the decoration first @@ -1076,8 +1065,6 @@ void QTextDocumentLayoutPrivate::drawFrame(const QPointF &offset, QPainter *pain painter->setPen(oldPen); } -// DEC_INDENT; - return; } @@ -1280,7 +1267,7 @@ void QTextDocumentLayoutPrivate::drawBlock(const QPointF &offset, QPainter *pain r.translate(offset + tl->position()); if (!bl.isVisible() || (context.clip.isValid() && (r.bottom() < context.clip.y() || r.top() > context.clip.bottom()))) return; -// LDEBUG << debug_indent << "drawBlock" << bl.position() << "at" << offset << "br" << tl->boundingRect(); + qCDebug(lcDraw) << "drawBlock" << bl.position() << "at" << offset << "br" << tl->boundingRect(); QTextBlockFormat blockFormat = bl.blockFormat(); @@ -1512,7 +1499,7 @@ QTextLayoutStruct QTextDocumentLayoutPrivate::layoutCell(QTextTable *t, const QT int layoutFrom, int layoutTo, QTextTableData *td, QFixed absoluteTableY, bool withPageBreaks) { - LDEBUG << "layoutCell"; + qCDebug(lcTable) << "layoutCell"; QTextLayoutStruct layoutStruct; layoutStruct.frame = t; layoutStruct.minimumWidth = 0; @@ -1587,7 +1574,7 @@ QTextLayoutStruct QTextDocumentLayoutPrivate::layoutCell(QTextTable *t, const QT QRectF QTextDocumentLayoutPrivate::layoutTable(QTextTable *table, int layoutFrom, int layoutTo, QFixed parentY) { - LDEBUG << "layoutTable"; + qCDebug(lcTable) << "layoutTable from" << layoutFrom << "to" << layoutTo << "parentY" << parentY; QTextTableData *td = static_cast(data(table)); Q_ASSERT(td->sizeDirty); const int rows = table->rows(); @@ -1709,6 +1696,7 @@ recalc_minmax_widths: if (length.type() == QTextLength::FixedLength) { td->minWidths[i] = td->widths[i] = qMax(scaleToDevice(QFixed::fromReal(length.rawValue())), td->minWidths.at(i)); remainingWidth -= td->widths.at(i); + qCDebug(lcTable) << "column" << i << "has width constraint" << td->minWidths.at(i) << "px, remaining width now" << remainingWidth; } else if (length.type() == QTextLength::PercentageLength) { totalPercentage += QFixed::fromReal(length.rawValue()); } else if (length.type() == QTextLength::VariableLength) { @@ -1716,6 +1704,7 @@ recalc_minmax_widths: td->widths[i] = td->minWidths.at(i); remainingWidth -= td->minWidths.at(i); + qCDebug(lcTable) << "column" << i << "has variable width, min" << td->minWidths.at(i) << "remaining width now" << remainingWidth; } totalMinWidth += td->minWidths.at(i); } @@ -1735,6 +1724,8 @@ recalc_minmax_widths: } else { td->widths[i] = td->minWidths.at(i); } + qCDebug(lcTable) << "column" << i << "has width constraint" << columnWidthConstraints.at(i).rawValue() + << "%, allocated width" << td->widths[i] << "remaining width now" << remainingWidth; remainingWidth -= td->widths.at(i); } } @@ -1978,9 +1969,12 @@ relayout: td->minimumWidth += rightMargin - td->border; td->maximumWidth = td->columnPositions.at(0); - for (int i = 0; i < columns; ++i) + for (int i = 0; i < columns; ++i) { if (td->maxWidths.at(i) != QFIXED_MAX) td->maximumWidth += td->maxWidths.at(i) + 2 * td->border + cellSpacing; + qCDebug(lcTable) << "column" << i << "has final width" << td->widths.at(i).toReal() + << "min" << td->minWidths.at(i).toReal() << "max" << td->maxWidths.at(i).toReal(); + } td->maximumWidth += rightMargin - td->border; td->updateTableSize(); @@ -2052,9 +2046,8 @@ void QTextDocumentLayoutPrivate::positionFloat(QTextFrame *frame, QTextLine *cur QRectF QTextDocumentLayoutPrivate::layoutFrame(QTextFrame *f, int layoutFrom, int layoutTo, QFixed parentY) { - LDEBUG << "layoutFrame (pre)"; + qCDebug(lcLayout, "layoutFrame (%d--%d), parent=%p", f->firstPosition(), f->lastPosition(), f->parentFrame()); Q_ASSERT(data(f)->sizeDirty); -// qDebug("layouting frame (%d--%d), parent=%p", f->firstPosition(), f->lastPosition(), f->parentFrame()); QTextFrameFormat fformat = f->frameFormat(); @@ -2076,9 +2069,8 @@ QRectF QTextDocumentLayoutPrivate::layoutFrame(QTextFrame *f, int layoutFrom, in QRectF QTextDocumentLayoutPrivate::layoutFrame(QTextFrame *f, int layoutFrom, int layoutTo, QFixed frameWidth, QFixed frameHeight, QFixed parentY) { - LDEBUG << "layoutFrame from=" << layoutFrom << "to=" << layoutTo; + qCDebug(lcLayout, "layoutFrame (%d--%d), parent=%p", f->firstPosition(), f->lastPosition(), f->parentFrame()); Q_ASSERT(data(f)->sizeDirty); -// qDebug("layouting frame (%d--%d), parent=%p", f->firstPosition(), f->lastPosition(), f->parentFrame()); QTextFrameData *fd = data(f); QFixed newContentsWidth; @@ -2165,8 +2157,8 @@ QRectF QTextDocumentLayoutPrivate::layoutFrame(QTextFrame *f, int layoutFrom, in layoutStruct.maximumWidth = QFIXED_MAX; layoutStruct.fullLayout = fullLayout || (fd->oldContentsWidth != newContentsWidth); layoutStruct.updateRect = QRectF(QPointF(0, 0), QSizeF(qreal(INT_MAX), qreal(INT_MAX))); - LDEBUG << "layoutStruct: x_left" << layoutStruct.x_left << "x_right" << layoutStruct.x_right - << "fullLayout" << layoutStruct.fullLayout; + qCDebug(lcLayout) << "layoutStruct: x_left" << layoutStruct.x_left << "x_right" << layoutStruct.x_right + << "fullLayout" << layoutStruct.fullLayout; fd->oldContentsWidth = newContentsWidth; layoutStruct.pageHeight = QFixed::fromReal(document->pageSize().height()); @@ -2220,7 +2212,7 @@ QRectF QTextDocumentLayoutPrivate::layoutFrame(QTextFrame *f, int layoutFrom, in void QTextDocumentLayoutPrivate::layoutFlow(QTextFrame::Iterator it, QTextLayoutStruct *layoutStruct, int layoutFrom, int layoutTo, QFixed width) { - LDEBUG << "layoutFlow from=" << layoutFrom << "to=" << layoutTo; + qCDebug(lcLayout) << "layoutFlow from=" << layoutFrom << "to=" << layoutTo; QTextFrameData *fd = data(layoutStruct->frame); fd->currentLayoutStruct = layoutStruct; @@ -2578,9 +2570,8 @@ void QTextDocumentLayoutPrivate::layoutBlock(const QTextBlock &bl, int blockPosi QTextLayout *tl = bl.layout(); const int blockLength = bl.length(); - LDEBUG << "layoutBlock from=" << layoutFrom << "to=" << layoutTo; - -// qDebug() << "layoutBlock; width" << layoutStruct->x_right - layoutStruct->x_left << "(maxWidth is btw" << tl->maximumWidth() << ')'; + qCDebug(lcLayout) << "layoutBlock from=" << layoutFrom << "to=" << layoutTo + << "; width" << layoutStruct->x_right - layoutStruct->x_left << "(maxWidth is btw" << tl->maximumWidth() << ')'; if (previousBlockFormat) { qreal margin = qMax(blockFormat.topMargin(), previousBlockFormat->bottomMargin()); @@ -2612,7 +2603,7 @@ void QTextDocumentLayoutPrivate::layoutBlock(const QTextBlock &bl, int blockPosi // force relayout if we cross a page boundary || (layoutStruct->pageHeight != QFIXED_MAX && layoutStruct->absoluteY() + QFixed::fromReal(tl->boundingRect().height()) > layoutStruct->pageBottom)) { - LDEBUG << " do layout"; + qCDebug(lcLayout) << "do layout"; QTextOption option = docPrivate->defaultTextOption; option.setTextDirection(dir); option.setTabs( blockFormat.tabPositions() ); @@ -2741,7 +2732,7 @@ void QTextDocumentLayoutPrivate::layoutBlock(const QTextBlock &bl, int blockPosi const int cnt = tl->lineCount(); QFixed bottom; for (int i = 0; i < cnt; ++i) { - LDEBUG << "going to move text line" << i; + qCDebug(lcLayout) << "going to move text line" << i; QTextLine line = tl->lineAt(i); layoutStruct->contentsWidth = qMax(layoutStruct->contentsWidth, QFixed::fromReal(line.x() + tl->lineAt(i).naturalTextWidth()) + totalRightMargin); From 12aafa294a75329d667068e4229a21d8ae7e94e0 Mon Sep 17 00:00:00 2001 From: Jesus Fernandez Date: Fri, 15 Mar 2019 12:16:41 +0100 Subject: [PATCH 18/27] Avoid warning about the deprecation of QString::QString(const char*) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes the warning: ‘QString::QString(const char*)’ is deprecated: Use fromUtf8, QStringLiteral, or QLatin1String [-Wdeprecated-declarations] return new QEglFSKmsEglDevice(this, screenConfig(), deviceName); ^ Change-Id: I36654f40219bf0f487e70cf2900d3f30335d4fc1 Reviewed-by: Mårten Nordheim --- .../eglfs_kms_egldevice/qeglfskmsegldeviceintegration.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ab39af6b80..ecdfb352ab 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/qeglfskmsegldeviceintegration.cpp @@ -260,7 +260,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() From adaa3acc7418ba212d403bc5f79a2d6fbd0af0ed Mon Sep 17 00:00:00 2001 From: Jesus Fernandez Date: Fri, 15 Mar 2019 12:02:18 +0100 Subject: [PATCH 19/27] Use Q_UNUSED(updateRect) when building without QT_BUILD_INTERNAL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I3e5292bc09ae53bee5f8bb8c7c1922d1a20b2e10 Reviewed-by: Mårten Nordheim --- .../widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp b/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp index 55139ff99a..28df3a3c38 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp @@ -4075,6 +4075,8 @@ void tst_QGraphicsView::update() QTRY_COMPARE(view.lastUpdateRegions.at(0), QRegion(updateRect) & viewportRect); } QTRY_VERIFY(!viewPrivate->fullUpdatePending); +#else + Q_UNUSED(updateRect); #endif } From e59ba35f1b1954062266164f7b802076dc152c7b Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Mon, 18 Mar 2019 08:27:22 +0100 Subject: [PATCH 20/27] Wasm: Add workaround for Emscripten compiler again MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This work around was added directly to the generated files at some point, and never to the generator it seems. So to avoid removing the workaround again when we regenerate the next time, we need to add it. Task-number: QTBUG-74511 Change-Id: Ided1bd949234ba82df61c55891646823e7f72e80 Reviewed-by: Morten Johan Sørvig --- util/unicode/main.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/util/unicode/main.cpp b/util/unicode/main.cpp index 00c69de008..1dbfa2cef0 100644 --- a/util/unicode/main.cpp +++ b/util/unicode/main.cpp @@ -798,6 +798,9 @@ static const char *property_string = " signed short mirrorDiff : 16;\n" " ushort lowerCaseSpecial : 1;\n" " signed short lowerCaseDiff : 15;\n" + "#ifdef Q_OS_WASM\n" + " unsigned char : 0; //wasm 64 packing trick\n" + "#endif\n" " ushort upperCaseSpecial : 1;\n" " signed short upperCaseDiff : 15;\n" " ushort titleCaseSpecial : 1;\n" @@ -806,6 +809,9 @@ static const char *property_string = " signed short caseFoldDiff : 15;\n" " ushort unicodeVersion : 8; /* 5 used */\n" " ushort nfQuickCheck : 8;\n" // could be narrowed + "#ifdef Q_OS_WASM\n" + " unsigned char : 0; //wasm 64 packing trick\n" + "#endif\n" " ushort graphemeBreakClass : 5; /* 5 used */\n" " ushort wordBreakClass : 5; /* 5 used */\n" " ushort sentenceBreakClass : 8; /* 4 used */\n" From 01380dc2673d1aecc3e216fff84da76223e447d7 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Fri, 15 Mar 2019 13:04:22 +0100 Subject: [PATCH 21/27] Remove broken code from unicode generator The current state produces uncompilable code. Change-Id: I9a68b61866a4a416335ed4d7204c58122803fb1c Reviewed-by: Eskil Abrahamsen Blomfeldt --- util/unicode/main.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/util/unicode/main.cpp b/util/unicode/main.cpp index 1dbfa2cef0..22a4405ca9 100644 --- a/util/unicode/main.cpp +++ b/util/unicode/main.cpp @@ -2476,10 +2476,6 @@ static QByteArray createPropertyInfo() out += ", "; out += QByteArray::number( p.lowerCaseDiff ); out += ", "; - out += "#ifdef Q_OS_WASM \n"; -// " unsigned char : 0; //wasm 64 packing trick QTBUG-65259\n" - out += "#endif \n"; - out += ", "; // " ushort upperCaseSpecial : 1;\n" // " signed short upperCaseDiff : 15;\n" out += QByteArray::number( p.upperCaseSpecial ); @@ -2504,10 +2500,6 @@ static QByteArray createPropertyInfo() // " ushort nfQuickCheck : 8;\n" out += QByteArray::number( p.nfQuickCheck ); out += ", "; - out += "#ifdef Q_OS_WASM \n"; -// " unsigned char : 0; //wasm 64 packing trick QTBUG-65259\n" - out += "#endif \n"; - out += ", "; // " ushort graphemeBreakClass : 5; /* 5 used */\n" // " ushort wordBreakClass : 5; /* 5 used */\n" // " ushort sentenceBreakClass : 8; /* 4 used */\n" From 1320b2f64412f0d86bd09c66c22df845e13a94a1 Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Wed, 17 Oct 2018 14:54:43 +0200 Subject: [PATCH 22/27] Fix memory leak when unregistering a gesture recognizer When a gesture was unrecognized, then it would add itself to the obsolete gestures hash. This was cleaned up only on application exit, but as the unregister call happens whenever a widget that had registered gestures was deleted then the hash could grow quite considerably. In order to ensure the original intention of the code here, we only call unregisterGestureRecognizer() when there is a QGestureManager in place to call it on. Otherwise it would create a memory leak in itself. Change-Id: I2342f3f737b28be4af7ed531d83f02197eb66c0e Reviewed-by: Richard Moe Gustavsen --- src/widgets/kernel/qgesturemanager.cpp | 5 ----- src/widgets/kernel/qgesturerecognizer.cpp | 6 ++++++ 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/widgets/kernel/qgesturemanager.cpp b/src/widgets/kernel/qgesturemanager.cpp index c4188044cf..891858b035 100644 --- a/src/widgets/kernel/qgesturemanager.cpp +++ b/src/widgets/kernel/qgesturemanager.cpp @@ -143,11 +143,6 @@ Qt::GestureType QGestureManager::registerGestureRecognizer(QGestureRecognizer *r void QGestureManager::unregisterGestureRecognizer(Qt::GestureType type) { QList list = m_recognizers.values(type); - while (QGestureRecognizer *recognizer = m_recognizers.take(type)) { - // ensuring an entry exists causes the recognizer to be deleted on destruction of the manager - auto &gestures = m_obsoleteGestures[recognizer]; - Q_UNUSED(gestures); - } foreach (QGesture *g, m_gestureToRecognizer.keys()) { QGestureRecognizer *recognizer = m_gestureToRecognizer.value(g); if (list.contains(recognizer)) { diff --git a/src/widgets/kernel/qgesturerecognizer.cpp b/src/widgets/kernel/qgesturerecognizer.cpp index 65c46a5e56..75d091ce4e 100644 --- a/src/widgets/kernel/qgesturerecognizer.cpp +++ b/src/widgets/kernel/qgesturerecognizer.cpp @@ -41,6 +41,7 @@ #include "private/qgesture_p.h" #include "private/qgesturemanager_p.h" +#include "private/qapplication_p.h" #ifndef QT_NO_GESTURES @@ -231,6 +232,11 @@ Qt::GestureType QGestureRecognizer::registerRecognizer(QGestureRecognizer *recog */ void QGestureRecognizer::unregisterRecognizer(Qt::GestureType type) { + auto qAppPriv = QApplicationPrivate::instance(); + if (!qAppPriv) + return; + if (!qAppPriv->gestureManager) + return; QGestureManager::instance()->unregisterGestureRecognizer(type); } From dc753374478d751c7c124030429e90d058934f9f Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Fri, 21 Dec 2018 15:53:57 +0100 Subject: [PATCH 23/27] Windows QPA: Make the expected screen be in sync with the geometry changes When the window moves to a new screen then we should ensure the screen is updated at that point with the new size so it can account for any scaling changes. This reverts f1ec81b543fe1d5090acff298e24faf10a7bac63. Change-Id: I2be3aab677c4677841a07beaaf373f498483b320 Fixes: QTBUG-72504 Reviewed-by: Friedemann Kleint --- src/plugins/platforms/windows/qwindowswindow.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index 2c0ffc9b26..2abd1eef8b 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -1758,15 +1758,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()); } } @@ -1785,11 +1782,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; } From 01e1df90a7debd333314720fdd5cf6cd9964d796 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Thu, 14 Mar 2019 00:36:34 +0100 Subject: [PATCH 24/27] Move screen maintenance functions from QPlatformIntegration to QWSI QWindowSystemInterface is the de facto API for any plumbing going from the platform plugin to QtGui. Having the functions as protected members of QPlatformIntegration was idiosyncratic, and resulted in awkward workarounds to be able to call the functions from outside of the QPlatformIntegration subclass. The functions in QPlatformIntegration have been left in, but deprecated so that platform plugins outside of qtbase have a chance to move over to the new QWSI API before they are removed. Change-Id: I327fec460db6b0faaf0ae2a151c20aa30dbe7182 Reviewed-by: Gatis Paeglis --- src/gui/kernel/qplatformintegration.cpp | 58 +++-------------- src/gui/kernel/qplatformintegration.h | 12 ++-- src/gui/kernel/qplatformscreen.cpp | 5 +- src/gui/kernel/qscreen.h | 1 + src/gui/kernel/qwindowsysteminterface.cpp | 64 +++++++++++++++++++ src/gui/kernel/qwindowsysteminterface.h | 4 ++ .../android/qandroidplatformintegration.cpp | 2 +- .../platforms/bsdfb/qbsdfbintegration.cpp | 5 +- .../platforms/cocoa/qcocoaintegration.mm | 6 +- .../platforms/directfb/qdirectfb_egl.cpp | 3 +- .../directfb/qdirectfbintegration.cpp | 3 +- .../eglfs/api/qeglfsdeviceintegration.cpp | 4 +- .../platforms/eglfs/api/qeglfsintegration.cpp | 12 +--- .../platforms/eglfs/api/qeglfsintegration_p.h | 3 - .../eglfs_emu/qeglfsemulatorintegration.cpp | 6 +- .../eglfs_kms_support/qeglfskmsdevice.cpp | 2 +- .../platforms/haiku/qhaikuintegration.cpp | 5 +- .../integrity/qintegrityfbintegration.cpp | 6 +- src/plugins/platforms/ios/qiosintegration.h | 4 -- src/plugins/platforms/ios/qiosintegration.mm | 4 +- src/plugins/platforms/ios/qiosscreen.mm | 10 +-- .../platforms/linuxfb/qlinuxfbintegration.cpp | 5 +- .../platforms/minimal/qminimalintegration.cpp | 3 +- .../minimalegl/qminimaleglintegration.cpp | 5 +- .../mirclient/qmirclientintegration.cpp | 7 +- .../offscreen/qoffscreenintegration.cpp | 3 +- .../platforms/openwfd/qopenwfdintegration.cpp | 11 +--- .../platforms/openwfd/qopenwfdintegration.h | 2 - .../platforms/openwfd/qopenwfdport.cpp | 4 +- src/plugins/platforms/qnx/qqnxintegration.cpp | 6 +- src/plugins/platforms/vnc/qvncintegration.cpp | 5 +- .../platforms/wasm/qwasmintegration.cpp | 4 +- .../platforms/windows/qwindowsintegration.h | 3 - .../platforms/windows/qwindowsscreen.cpp | 8 +-- .../platforms/winrt/qwinrtintegration.cpp | 4 +- src/plugins/platforms/xcb/qxcbconnection.cpp | 3 +- .../platforms/xcb/qxcbconnection_screens.cpp | 12 ++-- src/plugins/platforms/xcb/qxcbintegration.h | 2 - 38 files changed, 159 insertions(+), 147 deletions(-) diff --git a/src/gui/kernel/qplatformintegration.cpp b/src/gui/kernel/qplatformintegration.cpp index 7d1fcd4eeb..ac4d12b024 100644 --- a/src/gui/kernel/qplatformintegration.cpp +++ b/src/gui/kernel/qplatformintegration.cpp @@ -463,37 +463,16 @@ QList QPlatformIntegration::possibleKeys(const QKeyEvent *) const } /*! - Should be called by the implementation whenever a new screen is added. - - The first screen added will be the primary screen, used for default-created - windows, GL contexts, and other resources unless otherwise specified. - - This adds the screen to QGuiApplication::screens(), and emits the - QGuiApplication::screenAdded() signal. - - The screen should be deleted by calling QPlatformIntegration::destroyScreen(). + \deprecated Use QWindowSystemInterface::handleScreenAdded instead. */ void QPlatformIntegration::screenAdded(QPlatformScreen *ps, bool isPrimary) { - QScreen *screen = new QScreen(ps); - - if (isPrimary) { - QGuiApplicationPrivate::screen_list.prepend(screen); - } else { - QGuiApplicationPrivate::screen_list.append(screen); - } - emit qGuiApp->screenAdded(screen); - - if (isPrimary) - emit qGuiApp->primaryScreenChanged(screen); + QWindowSystemInterface::handleScreenAdded(ps, isPrimary); } /*! - Just removes the screen, call destroyScreen instead. - - \sa destroyScreen() + \deprecated Use QWindowSystemInterface::handleScreenRemoved instead. */ - void QPlatformIntegration::removeScreen(QScreen *screen) { const bool wasPrimary = (!QGuiApplicationPrivate::screen_list.isEmpty() && QGuiApplicationPrivate::screen_list.at(0) == screen); @@ -504,38 +483,19 @@ void QPlatformIntegration::removeScreen(QScreen *screen) } /*! - Should be called by the implementation whenever a screen is removed. - - This removes the screen from QGuiApplication::screens(), and deletes it. - - Failing to call this and manually deleting the QPlatformScreen instead may - lead to a crash due to a pure virtual call. + \deprecated Use QWindowSystemInterface::handleScreenRemoved instead. */ -void QPlatformIntegration::destroyScreen(QPlatformScreen *screen) +void QPlatformIntegration::destroyScreen(QPlatformScreen *platformScreen) { - QScreen *qScreen = screen->screen(); - removeScreen(qScreen); - delete qScreen; - delete screen; + QWindowSystemInterface::handleScreenRemoved(platformScreen); } /*! - Should be called whenever the primary screen changes. - - When the screen specified as primary changes, this method will notify - QGuiApplication and emit the QGuiApplication::primaryScreenChanged signal. - */ - + \deprecated Use QWindowSystemInterface::handlePrimaryScreenChanged instead. +*/ void QPlatformIntegration::setPrimaryScreen(QPlatformScreen *newPrimary) { - QScreen* newPrimaryScreen = newPrimary->screen(); - int idx = QGuiApplicationPrivate::screen_list.indexOf(newPrimaryScreen); - Q_ASSERT(idx >= 0); - if (idx == 0) - return; - - QGuiApplicationPrivate::screen_list.swap(0, idx); - emit qGuiApp->primaryScreenChanged(newPrimaryScreen); + QWindowSystemInterface::handlePrimaryScreenChanged(newPrimary); } QStringList QPlatformIntegration::themeNames() const diff --git a/src/gui/kernel/qplatformintegration.h b/src/gui/kernel/qplatformintegration.h index efb1481f6d..1179daeb32 100644 --- a/src/gui/kernel/qplatformintegration.h +++ b/src/gui/kernel/qplatformintegration.h @@ -190,7 +190,9 @@ public: #endif virtual void setApplicationIcon(const QIcon &icon) const; - void removeScreen(QScreen *screen); +#if QT_DEPRECATED_SINCE(5, 12) + QT_DEPRECATED_X("Use QWindowSystemInterface::handleScreenRemoved") void removeScreen(QScreen *screen); +#endif virtual void beep() const; @@ -199,9 +201,11 @@ public: #endif protected: - void screenAdded(QPlatformScreen *screen, bool isPrimary = false); - void destroyScreen(QPlatformScreen *screen); - void setPrimaryScreen(QPlatformScreen *newPrimary); +#if QT_DEPRECATED_SINCE(5, 12) + QT_DEPRECATED_X("Use QWindowSystemInterface::handleScreenAdded") void screenAdded(QPlatformScreen *screen, bool isPrimary = false); + QT_DEPRECATED_X("Use QWindowSystemInterface::handleScreenRemoved") void destroyScreen(QPlatformScreen *screen); + QT_DEPRECATED_X("Use QWindowSystemInterface::handlePrimaryScreenChanged") void setPrimaryScreen(QPlatformScreen *newPrimary); +#endif }; QT_END_NAMESPACE diff --git a/src/gui/kernel/qplatformscreen.cpp b/src/gui/kernel/qplatformscreen.cpp index b7b312e89e..21ae75ba8f 100644 --- a/src/gui/kernel/qplatformscreen.cpp +++ b/src/gui/kernel/qplatformscreen.cpp @@ -61,8 +61,11 @@ QPlatformScreen::~QPlatformScreen() { Q_D(QPlatformScreen); if (d->screen) { - qWarning("Manually deleting a QPlatformScreen. Call QPlatformIntegration::destroyScreen instead."); + qWarning("Manually deleting a QPlatformScreen. Call QWindowSystemInterface::handleScreenRemoved instead."); +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED QGuiApplicationPrivate::platformIntegration()->removeScreen(d->screen); +QT_WARNING_POP delete d->screen; } } diff --git a/src/gui/kernel/qscreen.h b/src/gui/kernel/qscreen.h index 8c9b16e08e..14392d3036 100644 --- a/src/gui/kernel/qscreen.h +++ b/src/gui/kernel/qscreen.h @@ -169,6 +169,7 @@ private: friend class QPlatformIntegration; friend class QPlatformScreen; friend class QHighDpiScaling; + friend class QWindowSystemInterface; }; #ifndef QT_NO_DEBUG_STREAM diff --git a/src/gui/kernel/qwindowsysteminterface.cpp b/src/gui/kernel/qwindowsysteminterface.cpp index 7067ece1d8..b0f2869128 100644 --- a/src/gui/kernel/qwindowsysteminterface.cpp +++ b/src/gui/kernel/qwindowsysteminterface.cpp @@ -780,6 +780,70 @@ QT_DEFINE_QPA_EVENT_HANDLER(void, handleTouchCancelEvent, QWindow *window, ulong QWindowSystemInterfacePrivate::handleWindowSystemEvent(e); } +/*! + Should be called by the implementation whenever a new screen is added. + + The first screen added will be the primary screen, used for default-created + windows, GL contexts, and other resources unless otherwise specified. + + This adds the screen to QGuiApplication::screens(), and emits the + QGuiApplication::screenAdded() signal. + + The screen should be deleted by calling QWindowSystemInterface::handleScreenRemoved(). +*/ +void QWindowSystemInterface::handleScreenAdded(QPlatformScreen *ps, bool isPrimary) +{ + QScreen *screen = new QScreen(ps); + + if (isPrimary) + QGuiApplicationPrivate::screen_list.prepend(screen); + else + QGuiApplicationPrivate::screen_list.append(screen); + + emit qGuiApp->screenAdded(screen); + + if (isPrimary) + emit qGuiApp->primaryScreenChanged(screen); +} + +/*! + Should be called by the implementation whenever a screen is removed. + + This removes the screen from QGuiApplication::screens(), and deletes it. + + Failing to call this and manually deleting the QPlatformScreen instead may + lead to a crash due to a pure virtual call. +*/ +void QWindowSystemInterface::handleScreenRemoved(QPlatformScreen *platformScreen) +{ +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED + QGuiApplicationPrivate::platformIntegration()->removeScreen(platformScreen->screen()); +QT_WARNING_POP + + // Important to keep this order since the QSceen doesn't own the platform screen + delete platformScreen->screen(); + delete platformScreen; +} + +/*! + Should be called whenever the primary screen changes. + + When the screen specified as primary changes, this method will notify + QGuiApplication and emit the QGuiApplication::primaryScreenChanged signal. + */ +void QWindowSystemInterface::handlePrimaryScreenChanged(QPlatformScreen *newPrimary) +{ + QScreen *newPrimaryScreen = newPrimary->screen(); + int indexOfScreen = QGuiApplicationPrivate::screen_list.indexOf(newPrimaryScreen); + Q_ASSERT(indexOfScreen >= 0); + if (indexOfScreen == 0) + return; + + QGuiApplicationPrivate::screen_list.swap(0, indexOfScreen); + emit qGuiApp->primaryScreenChanged(newPrimaryScreen); +} + void QWindowSystemInterface::handleScreenOrientationChange(QScreen *screen, Qt::ScreenOrientation orientation) { QWindowSystemInterfacePrivate::ScreenOrientationEvent *e = diff --git a/src/gui/kernel/qwindowsysteminterface.h b/src/gui/kernel/qwindowsysteminterface.h index 1dde9130ac..bf98c33a1a 100644 --- a/src/gui/kernel/qwindowsysteminterface.h +++ b/src/gui/kernel/qwindowsysteminterface.h @@ -233,6 +233,10 @@ public: static bool handleNativeEvent(QWindow *window, const QByteArray &eventType, void *message, long *result); // Changes to the screen + static void handleScreenAdded(QPlatformScreen *screen, bool isPrimary = false); + static void handleScreenRemoved(QPlatformScreen *screen); + static void handlePrimaryScreenChanged(QPlatformScreen *newPrimary); + static void handleScreenOrientationChange(QScreen *screen, Qt::ScreenOrientation newOrientation); static void handleScreenGeometryChange(QScreen *screen, const QRect &newGeometry, const QRect &newAvailableGeometry); static void handleScreenLogicalDotsPerInchChange(QScreen *screen, qreal newDpiX, qreal newDpiY); 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 #include #include +#include #if QT_CONFIG(tslib) #include @@ -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/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index 6d6c66e242..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); } } 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 #include +#include #include #include @@ -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 #include #include +#include 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( - 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 #include +#include + #include #include #include @@ -80,8 +82,6 @@ bool QEglFSEmulatorIntegration::usesDefaultScreen() void QEglFSEmulatorIntegration::screenInit() { - QEglFSIntegration *integration = static_cast(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_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(screen); s->setVirtualPosition(virtualPos); s->setVirtualSiblings(virtualSiblings); - static_cast(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 #include #include +#include #include #include @@ -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 #include - +#include 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 #include +#include #include @@ -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 #include +#include #if QT_CONFIG(libinput) #include @@ -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 #include #include +#include #include #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 #include #include +#include // 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/mirclient/qmirclientintegration.cpp b/src/plugins/platforms/mirclient/qmirclientintegration.cpp index eef96ee3de..d2b1dbee0d 100644 --- a/src/plugins/platforms/mirclient/qmirclientintegration.cpp +++ b/src/plugins/platforms/mirclient/qmirclientintegration.cpp @@ -58,6 +58,7 @@ #include #include #include +#include #include #include #include @@ -149,12 +150,12 @@ void QMirClientClientIntegration::initialize() // Init the ScreenObserver mScreenObserver.reset(new QMirClientScreenObserver(mMirConnection)); connect(mScreenObserver.data(), &QMirClientScreenObserver::screenAdded, - [this](QMirClientScreen *screen) { this->screenAdded(screen); }); + [this](QMirClientScreen *screen) { QWindowSystemInterface::handleScreenAdded(screen); }); connect(mScreenObserver.data(), &QMirClientScreenObserver::screenRemoved, this, &QMirClientClientIntegration::destroyScreen); Q_FOREACH (auto screen, mScreenObserver->screens()) { - screenAdded(screen); + QWindowSystemInterface::handleScreenAdded(screen); } // Initialize input. @@ -392,7 +393,7 @@ void QMirClientClientIntegration::destroyScreen(QMirClientScreen *screen) #if QT_VERSION < QT_VERSION_CHECK(5, 5, 0) delete screen; #else - QPlatformIntegration::destroyScreen(screen); + QWindowSystemInterface::handleScreenRemoved(screen); #endif } diff --git a/src/plugins/platforms/offscreen/qoffscreenintegration.cpp b/src/plugins/platforms/offscreen/qoffscreenintegration.cpp index 9815be16a3..ef3b0dd3ff 100644 --- a/src/plugins/platforms/offscreen/qoffscreenintegration.cpp +++ b/src/plugins/platforms/offscreen/qoffscreenintegration.cpp @@ -63,6 +63,7 @@ #include #include #include +#include #include @@ -117,7 +118,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 #include #include +#include #include #include @@ -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 mScreens; QListmDevices; 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 #include #include +#include #include @@ -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 1be909f0a0..3829043d07 100644 --- a/src/plugins/platforms/wasm/qwasmintegration.cpp +++ b/src/plugins/platforms/wasm/qwasmintegration.cpp @@ -78,7 +78,7 @@ QWasmIntegration::QWasmIntegration() globalHtml5Integration = this; updateQScreenAndCanvasRenderSize(); - screenAdded(m_screen); + QWindowSystemInterface::handleScreenAdded(m_screen); emscripten_set_resize_callback(0, (void *)this, 1, uiEvent_cb); m_eventTranslator = new QWasmEventTranslator; @@ -93,7 +93,7 @@ QWasmIntegration::QWasmIntegration() QWasmIntegration::~QWasmIntegration() { delete m_compositor; - destroyScreen(m_screen); + QWindowSystemInterface::handleScreenRemoved(m_screen); delete m_fontDb; delete m_eventTranslator; } 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(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/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/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index 29acf0e86d..0d71a5a552 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -146,10 +146,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_screens.cpp b/src/plugins/platforms/xcb/qxcbconnection_screens.cpp index 9aba996bb9..4e631beb25 100644 --- a/src/plugins/platforms/xcb/qxcbconnection_screens.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection_screens.cpp @@ -44,6 +44,8 @@ #include #include +#include + #include void QXcbConnection::xrandrSelectEvents() @@ -211,7 +213,7 @@ void QXcbConnection::updateScreen(QXcbScreen *screen, const xcb_randr_output_cha m_screens.swap(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.swap(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 m_services; - friend class QXcbConnection; // access QPlatformIntegration::screenAdded() - mutable QByteArray m_wmClass; const char *m_instanceName; bool m_canGrab; From d2dff76a4fc2c28bf204db183230ffa1b86cbf43 Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Mon, 18 Mar 2019 09:51:35 +0100 Subject: [PATCH 25/27] Replace instances of - with _ when generating the function name As - cannot be used in a function name but can still be used as part of the TARGET, then we need to make sure that it is replaced to avoid a compile problem. Change-Id: I0b2e465310206e2522ce59235b1592517817d3e2 Reviewed-by: Joerg Bornemann --- mkspecs/features/resources.prf | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mkspecs/features/resources.prf b/mkspecs/features/resources.prf index b4e0db6445..fa8ff1fb58 100644 --- a/mkspecs/features/resources.prf +++ b/mkspecs/features/resources.prf @@ -75,9 +75,11 @@ for(resource, RESOURCES) { } !isEmpty(RESOURCES):contains(TEMPLATE, .*lib):plugin:static { - resource_init_function = $$lower($$basename(TARGET))_plugin_resource_init + pluginBaseName = $$basename(TARGET) + pluginName = $$lower($$replace(pluginBaseName, [-], _)) + resource_init_function = $${pluginName}_plugin_resource_init DEFINES += "QT_PLUGIN_RESOURCE_INIT_FUNCTION=$$resource_init_function" - RESOURCE_INIT_CPP = $$OUT_PWD/$$lower($$basename(TARGET))_plugin_resources.cpp + RESOURCE_INIT_CPP = $$OUT_PWD/$${pluginName}_plugin_resources.cpp GENERATED_SOURCES += $$RESOURCE_INIT_CPP QMAKE_DISTCLEAN += $$RESOURCE_INIT_CPP From fda57bbb32c7c08e750f9ed82d30d95052f927b5 Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Mon, 18 Mar 2019 14:32:48 +0100 Subject: [PATCH 26/27] QTemporaryFile: Return early if the passed in QFile cannot be opened Change-Id: I97f00163c24f74d31e1b41ec7337f72600bda2d5 Reviewed-by: Friedemann Kleint Reviewed-by: Mikhail Svetkin Reviewed-by: Thiago Macieira --- src/corelib/io/qtemporaryfile.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/corelib/io/qtemporaryfile.cpp b/src/corelib/io/qtemporaryfile.cpp index 35bb465a04..ced08a9a87 100644 --- a/src/corelib/io/qtemporaryfile.cpp +++ b/src/corelib/io/qtemporaryfile.cpp @@ -908,8 +908,8 @@ QTemporaryFile *QTemporaryFile::createNativeFile(QFile &file) qint64 old_off = 0; if(wasOpen) old_off = file.pos(); - else - file.open(QIODevice::ReadOnly); + else if (!file.open(QIODevice::ReadOnly)) + return nullptr; //dump data QTemporaryFile *ret = new QTemporaryFile; if (ret->open()) { From 2bafd997ee515d3b6a6a8fb030e1265a4713713e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Wed, 13 Mar 2019 15:45:38 +0100 Subject: [PATCH 27/27] Don't quit when last QWindow is destroyed, wait for it to close MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Destroying windows happens independently of closing them, e.g. when a window is moved from one screen to another non-sibling screen. The logic for quitting the application should apply to the actual closing of a window, as documented. Change-Id: I2226aff29278aa6fbf054a0994a320eb53196e9e Reviewed-by: Morten Johan Sørvig --- src/gui/kernel/qwindow.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp index f1e08826a8..453aa1ed83 100644 --- a/src/gui/kernel/qwindow.cpp +++ b/src/gui/kernel/qwindow.cpp @@ -1910,9 +1910,6 @@ void QWindowPrivate::destroy() resizeEventPending = true; receivedExpose = false; exposed = false; - - if (wasVisible) - maybeQuitOnLastWindowClosed(); } /*! @@ -2301,8 +2298,17 @@ bool QWindow::event(QEvent *ev) #endif case QEvent::Close: - if (ev->isAccepted()) + if (ev->isAccepted()) { + Q_D(QWindow); + bool wasVisible = isVisible(); destroy(); + if (wasVisible) { + // FIXME: This check for visibility is a workaround for both QWidgetWindow + // and QWindow having logic to emit lastWindowClosed, and possibly quit the + // application. We should find a better way to handle this. + d->maybeQuitOnLastWindowClosed(); + } + } break; case QEvent::Expose: