From 827b7afba7b16cb25e276dc5e7bd74cd69c3acf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Storsj=C3=B6?= Date: Fri, 26 Apr 2019 14:57:01 +0300 Subject: ANGLE: Backport fix for compilation on mingw/64bit with clang This backports the following upstream fix from angle: https://github.com/google/angle/commit/63cc351fbad06c6241d1c7372fe76f74e1d09a10 Change-Id: Id80dba62c69f3505eb836f758367b4bf054b1fd5 Reviewed-by: Oliver Wolff --- .../common/third_party/smhasher/src/PMurHash.cpp | 3 +- ...ort-fix-for-compilation-on-mingw-64bit-wi.patch | 35 ++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 src/angle/patches/0014-ANGLE-Backport-fix-for-compilation-on-mingw-64bit-wi.patch diff --git a/src/3rdparty/angle/src/common/third_party/smhasher/src/PMurHash.cpp b/src/3rdparty/angle/src/common/third_party/smhasher/src/PMurHash.cpp index 071bc31539..93b48713cd 100644 --- a/src/3rdparty/angle/src/common/third_party/smhasher/src/PMurHash.cpp +++ b/src/3rdparty/angle/src/common/third_party/smhasher/src/PMurHash.cpp @@ -49,6 +49,7 @@ on big endian machines, or a byte-by-byte read if the endianess is unknown. #include "PMurHash.h" +#include /* I used ugly type names in the header to avoid potential conflicts with * application or system typedefs & defines. Since I'm not including any more @@ -208,7 +209,7 @@ void PMurHash32_Process(uint32_t *ph1, uint32_t *pcarry, const void *key, int le /* This CPU does not handle unaligned word access */ /* Consume enough so that the next data byte is word aligned */ - int i = -(long)ptr & 3; + int i = -(intptr_t)ptr & 3; if(i && i <= len) { DOBYTES(i, h1, c, n, ptr, len); } diff --git a/src/angle/patches/0014-ANGLE-Backport-fix-for-compilation-on-mingw-64bit-wi.patch b/src/angle/patches/0014-ANGLE-Backport-fix-for-compilation-on-mingw-64bit-wi.patch new file mode 100644 index 0000000000..a32f25d2c0 --- /dev/null +++ b/src/angle/patches/0014-ANGLE-Backport-fix-for-compilation-on-mingw-64bit-wi.patch @@ -0,0 +1,35 @@ +From e7ff4aa4ef2221aa02d39bdead7f35008016994e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Martin=20Storsj=C3=B6?= +Date: Fri, 26 Apr 2019 14:57:01 +0300 +Subject: [PATCH] ANGLE: Backport fix for compilation on mingw/64bit with clang + +This backports the following upstream fix from angle: +https://github.com/google/angle/commit/63cc351fbad06c6241d1c7372fe76f74e1d09a10 +--- + .../angle/src/common/third_party/smhasher/src/PMurHash.cpp | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/3rdparty/angle/src/common/third_party/smhasher/src/PMurHash.cpp b/src/3rdparty/angle/src/common/third_party/smhasher/src/PMurHash.cpp +index 071bc31539..93b48713cd 100644 +--- a/src/3rdparty/angle/src/common/third_party/smhasher/src/PMurHash.cpp ++++ b/src/3rdparty/angle/src/common/third_party/smhasher/src/PMurHash.cpp +@@ -49,6 +49,7 @@ on big endian machines, or a byte-by-byte read if the endianess is unknown. + + + #include "PMurHash.h" ++#include + + /* I used ugly type names in the header to avoid potential conflicts with + * application or system typedefs & defines. Since I'm not including any more +@@ -208,7 +209,7 @@ void PMurHash32_Process(uint32_t *ph1, uint32_t *pcarry, const void *key, int le + /* This CPU does not handle unaligned word access */ + + /* Consume enough so that the next data byte is word aligned */ +- int i = -(long)ptr & 3; ++ int i = -(intptr_t)ptr & 3; + if(i && i <= len) { + DOBYTES(i, h1, c, n, ptr, len); + } +-- +2.20.1 (Apple Git-117) + -- cgit v1.2.3 From fa890c4686f3971b30e78147853db6bd0a8a3512 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Tue, 7 May 2019 12:22:27 +0200 Subject: Windows QPA: Improve debug messages Include screen and MINMAXINFO values in the message about not being able to set the geometry. Suppress output of some window finding functions unless verbose. Change-Id: Iaaae59ecb302438b3444735067d018c77d2af162 Reviewed-by: Oliver Wolff --- src/plugins/platforms/windows/qwindowscontext.cpp | 2 + src/plugins/platforms/windows/qwindowsscreen.cpp | 6 +- src/plugins/platforms/windows/qwindowswindow.cpp | 75 ++++++++++++++++++----- 3 files changed, 66 insertions(+), 17 deletions(-) diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp index 80517ffe69..6c1f5c8f93 100644 --- a/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -1325,6 +1325,8 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, return false; platformWindow->setFlag(QWindowsWindow::WithinDpiChanged); const RECT *prcNewWindow = reinterpret_cast(lParam); + qCDebug(lcQpaWindows) << __FUNCTION__ << "WM_DPICHANGED" + << platformWindow->window() << *prcNewWindow; SetWindowPos(hwnd, nullptr, prcNewWindow->left, prcNewWindow->top, prcNewWindow->right - prcNewWindow->left, prcNewWindow->bottom - prcNewWindow->top, SWP_NOZORDER | SWP_NOACTIVATE); diff --git a/src/plugins/platforms/windows/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp index 0520f88935..94608bfd82 100644 --- a/src/plugins/platforms/windows/qwindowsscreen.cpp +++ b/src/plugins/platforms/windows/qwindowsscreen.cpp @@ -240,7 +240,8 @@ QWindow *QWindowsScreen::topLevelAt(const QPoint &point) const QWindow *result = nullptr; if (QWindow *child = QWindowsScreen::windowAt(point, CWP_SKIPINVISIBLE)) result = QWindowsWindow::topLevelOf(child); - qCDebug(lcQpaWindows) <<__FUNCTION__ << point << result; + if (QWindowsContext::verbose > 1) + qCDebug(lcQpaWindows) <<__FUNCTION__ << point << result; return result; } @@ -250,7 +251,8 @@ QWindow *QWindowsScreen::windowAt(const QPoint &screenPoint, unsigned flags) if (QPlatformWindow *bw = QWindowsContext::instance()-> findPlatformWindowAt(GetDesktopWindow(), screenPoint, flags)) result = bw->window(); - qCDebug(lcQpaWindows) <<__FUNCTION__ << screenPoint << " returns " << result; + if (QWindowsContext::verbose > 1) + qCDebug(lcQpaWindows) <<__FUNCTION__ << screenPoint << " returns " << result; return result; } diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index dc9aa0da23..f538b6bad7 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -184,6 +184,7 @@ static inline RECT RECTfromQRect(const QRect &rect) return result; } + #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug d, const RECT &r) { @@ -262,6 +263,16 @@ QDebug operator<<(QDebug d, const GUID &guid) } #endif // !QT_NO_DEBUG_STREAM +static void formatBriefRectangle(QDebug &d, const QRect &r) +{ + d << r.width() << 'x' << r.height() << forcesign << r.x() << r.y() << noforcesign; +} + +static void formatBriefMargins(QDebug &d, const QMargins &m) +{ + d << m.left() << ", " << m.top() << ", " << m.right() << ", " << m.bottom(); +} + // QTBUG-43872, for windows that do not have WS_EX_TOOLWINDOW set, WINDOWPLACEMENT // is in workspace/available area coordinates. static QPoint windowPlacementOffset(HWND hwnd, const QPoint &point) @@ -1675,6 +1686,51 @@ QRect QWindowsWindow::normalGeometry() const return frame.isValid() ? frame.marginsRemoved(margins) : frame; } +static QString msgUnableToSetGeometry(const QWindowsWindow *platformWindow, + const QRect &requestedRect, + const QRect &obtainedRect, + const QMargins &fullMargins, + const QMargins &customMargins) +{ + QString result; + QDebug debug(&result); + debug.nospace(); + debug.noquote(); + const auto window = platformWindow->window(); + debug << "Unable to set geometry "; + formatBriefRectangle(debug, requestedRect); + debug << " (frame: "; + formatBriefRectangle(debug, requestedRect + fullMargins); + debug << ") on " << window->metaObject()->className() << "/\"" + << window->objectName() << "\" on \"" << window->screen()->name() + << "\". Resulting geometry: "; + formatBriefRectangle(debug, obtainedRect); + debug << " (frame: "; + formatBriefRectangle(debug, obtainedRect + fullMargins); + debug << ") margins: "; + formatBriefMargins(debug, fullMargins); + if (!customMargins.isNull()) { + debug << " custom margin: "; + formatBriefMargins(debug, customMargins); + } + const auto minimumSize = window->minimumSize(); + const bool hasMinimumSize = !minimumSize.isEmpty(); + if (hasMinimumSize) + debug << " minimum size: " << minimumSize.width() << 'x' << minimumSize.height(); + const auto maximumSize = window->maximumSize(); + const bool hasMaximumSize = maximumSize.width() != QWINDOWSIZE_MAX || maximumSize.height() != QWINDOWSIZE_MAX; + if (hasMaximumSize) + debug << " maximum size: " << maximumSize.width() << 'x' << maximumSize.height(); + if (hasMinimumSize || hasMaximumSize) { + MINMAXINFO minmaxInfo; + memset(&minmaxInfo, 0, sizeof(minmaxInfo)); + platformWindow->getSizeHints(&minmaxInfo); + debug << ' ' << minmaxInfo; + } + debug << ')'; + return result; +} + void QWindowsWindow::setGeometry(const QRect &rectIn) { QRect rect = rectIn; @@ -1694,21 +1750,10 @@ void QWindowsWindow::setGeometry(const QRect &rectIn) setGeometry_sys(rect); clearFlag(WithinSetGeometry); if (m_data.geometry != rect && (isVisible() || QLibraryInfo::isDebugBuild())) { - qWarning("%s: Unable to set geometry %dx%d+%d+%d on %s/'%s'." - " Resulting geometry: %dx%d+%d+%d " - "(frame: %d, %d, %d, %d, custom margin: %d, %d, %d, %d" - ", minimum size: %dx%d, maximum size: %dx%d).", - __FUNCTION__, - rect.width(), rect.height(), rect.x(), rect.y(), - window()->metaObject()->className(), qPrintable(window()->objectName()), - m_data.geometry.width(), m_data.geometry.height(), - m_data.geometry.x(), m_data.geometry.y(), - m_data.fullFrameMargins.left(), m_data.fullFrameMargins.top(), - m_data.fullFrameMargins.right(), m_data.fullFrameMargins.bottom(), - m_data.customMargins.left(), m_data.customMargins.top(), - m_data.customMargins.right(), m_data.customMargins.bottom(), - window()->minimumWidth(), window()->minimumHeight(), - window()->maximumWidth(), window()->maximumHeight()); + const auto warning = + msgUnableToSetGeometry(this, rectIn, m_data.geometry, + m_data.fullFrameMargins, m_data.customMargins); + qWarning("%s: %s", __FUNCTION__, qPrintable(warning)); } } else { QPlatformWindow::setGeometry(rect); -- cgit v1.2.3 From 17c44479aa848341d46800cf9a95ab04814cb903 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Wed, 8 May 2019 14:21:31 +0200 Subject: Update CLDR version in attribution This is a follow-up to 43abe86e. Change-Id: I2442304c9c79bcb1932fb173b8d993a242d79f4b Reviewed-by: Konstantin Ritt --- src/corelib/tools/qt_attribution.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/corelib/tools/qt_attribution.json b/src/corelib/tools/qt_attribution.json index a842d9467b..912da3f22c 100644 --- a/src/corelib/tools/qt_attribution.json +++ b/src/corelib/tools/qt_attribution.json @@ -29,7 +29,7 @@ world's languages, with the largest and most extensive standard repository of locale data available.", "Homepage": "http://cldr.unicode.org/", - "Version": "v34", + "Version": "v35.1", "License": "// as specified in https://spdx.org/licenses/Unicode-DFS-2016.html", "License": "Unicode License Agreement - Data Files and Software (2016)", "LicenseId": "Unicode-DFS-2016", -- cgit v1.2.3 From a12b6e7bf6688021c6af809d024958b59dfa3555 Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Tue, 7 May 2019 14:09:14 +0200 Subject: Fix CMake file generation for debug libs on macOS CMAKE_QT_STEM already contains the _debug suffix. Do not add it again. This amends commit bb8a3dfc. Fixes: QTBUG-75520 Change-Id: I6c311f0913ea83fcf299a21a0ee1f28c3861371f Reviewed-by: Kai Koehne --- mkspecs/features/create_cmake.prf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mkspecs/features/create_cmake.prf b/mkspecs/features/create_cmake.prf index 2ab7775fea..c9910dda53 100644 --- a/mkspecs/features/create_cmake.prf +++ b/mkspecs/features/create_cmake.prf @@ -211,10 +211,10 @@ CMAKE_INTERFACE_QT5_MODULE_DEPS = $$join(aux_lib_deps, ";") mac { !isEmpty(CMAKE_STATIC_TYPE) { - CMAKE_LIB_FILE_LOCATION_DEBUG = lib$${CMAKE_QT_STEM}_debug.a + CMAKE_LIB_FILE_LOCATION_DEBUG = lib$${CMAKE_QT_STEM}.a CMAKE_LIB_FILE_LOCATION_RELEASE = lib$${CMAKE_QT_STEM}.a - CMAKE_PRL_FILE_LOCATION_DEBUG = lib$${CMAKE_QT_STEM}_debug.prl + CMAKE_PRL_FILE_LOCATION_DEBUG = lib$${CMAKE_QT_STEM}.prl CMAKE_PRL_FILE_LOCATION_RELEASE = lib$${CMAKE_QT_STEM}.prl } else { qt_framework { @@ -222,7 +222,7 @@ mac { CMAKE_LIB_FILE_LOCATION_RELEASE = $${CMAKE_QT_STEM}.framework/$${CMAKE_QT_STEM} CMAKE_BUILD_IS_FRAMEWORK = "true" } else { - CMAKE_LIB_FILE_LOCATION_DEBUG = lib$${CMAKE_QT_STEM}_debug.$$eval(QT.$${MODULE}.VERSION).dylib + CMAKE_LIB_FILE_LOCATION_DEBUG = lib$${CMAKE_QT_STEM}.$$eval(QT.$${MODULE}.VERSION).dylib CMAKE_LIB_FILE_LOCATION_RELEASE = lib$${CMAKE_QT_STEM}.$$eval(QT.$${MODULE}.VERSION).dylib } } -- cgit v1.2.3 From 64a2dc3962eabd7e59fb408a0b1604891302df72 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Thu, 9 May 2019 13:25:48 +0200 Subject: Add missing backslash to kmsconvenience.pro Change-Id: I3519447af657bdbb7304aca272de416104dca0f9 Fixes: QTBUG-75730 Reviewed-by: Johan Helsing --- src/platformsupport/kmsconvenience/kmsconvenience.pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platformsupport/kmsconvenience/kmsconvenience.pro b/src/platformsupport/kmsconvenience/kmsconvenience.pro index 5ea2e3f208..0c5a20a239 100644 --- a/src/platformsupport/kmsconvenience/kmsconvenience.pro +++ b/src/platformsupport/kmsconvenience/kmsconvenience.pro @@ -6,7 +6,7 @@ CONFIG += static internal_module DEFINES += QT_NO_CAST_FROM_ASCII -HEADERS += +HEADERS += \ qkmsdevice_p.h SOURCES += \ -- cgit v1.2.3 From 3159845c9c6c72c3e4492c2359c8a020fdb6ce5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Thu, 9 May 2019 14:08:23 +0200 Subject: macOS: Implement QCALayerBackingStore::toImage() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's not part of the QBackingStore API, but clients such as the Qt Quick software renderer access it through the platform backingstore, to grab the window. Change-Id: I203484ce13a5f8fb6815d27ab07f874fa9d16b8c Fixes: QTBUG-75467 Reviewed-by: Eirik Aavitsland Reviewed-by: Tor Arne Vestbø --- src/plugins/platforms/cocoa/qcocoabackingstore.h | 1 + src/plugins/platforms/cocoa/qcocoabackingstore.mm | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.h b/src/plugins/platforms/cocoa/qcocoabackingstore.h index 470da63e3d..acddc3ecc8 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.h +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.h @@ -81,6 +81,7 @@ public: QPlatformTextureList *textures, bool translucentBackground) override; #endif + QImage toImage() const override; QPlatformGraphicsBuffer *graphicsBuffer() const override; private: diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm index e786ecb5a5..cff1f96615 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.mm +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm @@ -534,6 +534,21 @@ void QCALayerBackingStore::composeAndFlush(QWindow *window, const QRegion ®io } #endif +QImage QCALayerBackingStore::toImage() const +{ + if (!const_cast(this)->prepareForFlush()) + return QImage(); + + // We need to make a copy here, as the returned image could be used just + // for reading, in which case it won't detach, and then the underlying + // image data might change under the feet of the client when we re-use + // the buffer at a later point. + m_buffers.back()->lock(QPlatformGraphicsBuffer::SWReadAccess); + QImage imageCopy = m_buffers.back()->asImage()->copy(); + m_buffers.back()->unlock(); + return imageCopy; +} + QPlatformGraphicsBuffer *QCALayerBackingStore::graphicsBuffer() const { return m_buffers.back().get(); -- cgit v1.2.3 From b7edc811ec00b6e0687cb96fda64c7e608af56c6 Mon Sep 17 00:00:00 2001 From: Keith Kyzivat Date: Fri, 15 Mar 2019 12:07:42 -0400 Subject: Work around VS2015/17 bitset + qfloat16.h compiler bug [ChangeLog][QtCore][Global] Added the QT_NO_FLOAT16_OPERATORS macro in order to work around a Microsoft <= VS2017 compiler bug that is exposed when using std::bitset along with any Qt header that includes . This is fixed in MSVC 2019[1], but the workaround is needed for earlier versions. In this case, cl.exe fails with C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\VC\Tools\MSVC\14.10.25017\include\bitset(270): error C2666: 'operator /': 10 overloads have similar conversions C:\Qt\5.12.0\msvc2017_64\include\QtCore/qsize.h(364): note: could be 'const QSizeF operator /(const QSizeF &,qreal)' C:\Qt\5.12.0\msvc2017_64\include\QtCore/qsize.h(194): note: or 'const QSize operator /(const QSize &,qreal)' c:\qt\5.12.0\msvc2017_64\include\qtcore\qmargins.h(427): note: or 'QMarginsF operator /(const QMarginsF &,qreal)' c:\qt\5.12.0\msvc2017_64\include\qtcore\qmargins.h(213): note: or 'QMargins operator /(const QMargins &,qreal)' c:\qt\5.12.0\msvc2017_64\include\qtcore\qmargins.h(207): note: or 'QMargins operator /(const QMargins &,int)' C:\Qt\5.12.0\msvc2017_64\include\QtCore/qfloat16.h(205): note: or 'double operator /(int,qfloat16) noexcept' C:\Qt\5.12.0\msvc2017_64\include\QtCore/qfloat16.h(205): note: or 'double operator /(qfloat16,int) noexcept' C:\Qt\5.12.0\msvc2017_64\include\QtCore/qfloat16.h(195): note: or 'float operator /(float,qfloat16) noexcept' C:\Qt\5.12.0\msvc2017_64\include\QtCore/qfloat16.h(195): note: or 'float operator /(qfloat16,float) noexcept' C:\Qt\5.12.0\msvc2017_64\include\QtCore/qfloat16.h(194): note: or 'double operator /(double,qfloat16) noexcept' C:\Qt\5.12.0\msvc2017_64\include\QtCore/qfloat16.h(194): note: or 'double operator /(qfloat16,double) noexcept' C:\Qt\5.12.0\msvc2017_64\include\QtCore/qfloat16.h(193): note: or 'long double operator /(long double,qfloat16) noexcept' C:\Qt\5.12.0\msvc2017_64\include\QtCore/qfloat16.h(193): note: or 'long double operator /(qfloat16,long double) noexcept' C:\Qt\5.12.0\msvc2017_64\include\QtCore/qfloat16.h(176): note: or 'qfloat16 operator /(qfloat16,qfloat16) noexcept' C:\Qt\5.12.0\msvc2017_64\include\QtCore/qpoint.h(402): note: or 'const QPointF operator /(const QPointF &,qreal)' C:\Qt\5.12.0\msvc2017_64\include\QtCore/qpoint.h(206): note: or 'const QPoint operator /(const QPoint &,qreal)' C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\VC\Tools\MSVC\14.10.25017\include\bitset(270): note: or 'built-in C++ operator/(::size_t, )' C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\VC\Tools\MSVC\14.10.25017\include\bitset(270): note: while trying to match the argument list '(::size_t, )' C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\VC\Tools\MSVC\14.10.25017\include\bitset(266): note: while compiling class template member function 'std::bitset<8> &std::bitset<8>::set(::size_t,bool)' C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\VC\Tools\MSVC\14.10.25017\include\bitset(39): note: see reference to function template instantiation 'std::bitset<8> &std::bitset<8>::set(::size_t,bool)' being compiled ..\Qt5.12.0-C2666\main.cpp(7): note: see reference to class template instantiation 'std::bitset<8>' being compiled Invoke this workaround by defining the macro QT_NO_FLOAT16_OPERATORS in user code prior to the inclusion of Qt includes in a translation unit. Arithmetic operators from qfloat16 will then not be present in that compilation unit. [1] https://developercommunity.visualstudio.com/content/problem/406329/compiler-error-c2666-when-using-stdbitset-and-cust.html Task-number: QTBUG-72073 Change-Id: I58f8400bf933ad781d4213731695e20e0c482166 Reviewed-by: Thiago Macieira Reviewed-by: Edward Welbourne --- src/corelib/global/qfloat16.cpp | 13 +++++++++++++ src/corelib/global/qfloat16.h | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/src/corelib/global/qfloat16.cpp b/src/corelib/global/qfloat16.cpp index fd608efe55..87ff796368 100644 --- a/src/corelib/global/qfloat16.cpp +++ b/src/corelib/global/qfloat16.cpp @@ -64,6 +64,19 @@ QT_BEGIN_NAMESPACE \since 5.9 */ +/*! + \macro QT_NO_FLOAT16_OPERATORS + \relates + \since 5.12.4 + + Defining this macro disables the arithmetic operators for qfloat16. + + This is only necessary on Visual Studio 2017 (and earlier) when including + \c {} and \c{} in the same translation unit, which would + otherwise cause a compilation error due to a toolchain bug (see + [QTBUG-72073]). +*/ + /*! Returns true if the \c qfloat16 \a {f} is equivalent to infinity. \relates diff --git a/src/corelib/global/qfloat16.h b/src/corelib/global/qfloat16.h index 3e50ad8467..b76d2b9616 100644 --- a/src/corelib/global/qfloat16.h +++ b/src/corelib/global/qfloat16.h @@ -83,7 +83,9 @@ private: Q_CORE_EXPORT static const quint32 shifttable[]; friend bool qIsNull(qfloat16 f) Q_DECL_NOTHROW; +#if !defined(QT_NO_FLOAT16_OPERATORS) friend qfloat16 operator-(qfloat16 a) Q_DECL_NOTHROW; +#endif }; Q_DECLARE_TYPEINFO(qfloat16, Q_PRIMITIVE_TYPE); @@ -165,6 +167,7 @@ inline qfloat16::operator float() const Q_DECL_NOTHROW } #endif +#if !defined(QT_NO_FLOAT16_OPERATORS) inline qfloat16 operator-(qfloat16 a) Q_DECL_NOTHROW { qfloat16 f; @@ -246,6 +249,7 @@ QF16_MAKE_BOOL_OP_INT(!=) #undef QF16_MAKE_BOOL_OP_INT QT_WARNING_POP +#endif // QT_NO_FLOAT16_OPERATORS /*! \internal -- cgit v1.2.3 From 123053bba8dc2241041c31a262420edd3583b1c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Thu, 9 May 2019 20:43:30 +0200 Subject: macOS: Don't clip menu item drawing to bounding rect when using CoreText MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bounding rect was computed based on the font metrics HarfBuzz gave us, but those may not be 1:1 with what CoreText ends up using. When that happens, drawInRect: will line-break the last word, which makes it fall completely outside of the single line bounding rect. This is not a good failure mode, so we prefer to draw the text at a point instead, allowing the resulting text to draw slightly outside of the bounding rect. This is preferable to adding a random padding to the width and hoping it will be enough to solve the problem. Change-Id: Ifa58a33bd9fad689ed4ee947327b7079f3c1b61d Fixes: QTBUG-74565 Reviewed-by: Eskil Abrahamsen Blomfeldt Reviewed-by: Tor Arne Vestbø --- src/plugins/styles/mac/qmacstyle_mac.mm | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/plugins/styles/mac/qmacstyle_mac.mm b/src/plugins/styles/mac/qmacstyle_mac.mm index 2a6f212569..81835b7c63 100644 --- a/src/plugins/styles/mac/qmacstyle_mac.mm +++ b/src/plugins/styles/mac/qmacstyle_mac.mm @@ -4290,12 +4290,15 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter alpha:pc.alphaF()]; s = qt_mac_removeMnemonics(s); - const auto textRect = CGRectMake(xpos, yPos, mi->rect.width() - xm - tabwidth + 1, mi->rect.height()); QMacCGContext cgCtx(p); d->setupNSGraphicsContext(cgCtx, YES); - [s.toNSString() drawInRect:textRect + // Draw at point instead of in rect, as the rect we've computed for the menu item + // is based on the font metrics we got from HarfBuzz, so we may risk having CoreText + // line-break the string if it doesn't fit the given rect. It's better to draw outside + // the rect and possibly overlap something than to have part of the text disappear. + [s.toNSString() drawAtPoint:CGPointMake(xpos, yPos) withAttributes:@{ NSFontAttributeName:f, NSForegroundColorAttributeName:c, NSObliquenessAttributeName: [NSNumber numberWithDouble: myFont.italic() ? 0.3 : 0.0]}]; -- cgit v1.2.3 From c99678fb19e579466f2a9e4df626de3cdb51f272 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Thu, 9 May 2019 15:07:22 +0200 Subject: macOS: Deliver geometry changes when content view changes frame MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This was disabled in 9f22ac0aa0254f20f9b26aec7b124d74141fdfcd under the assumption that the windowDidResize callback was sufficient, but in the situation when macOS native tabs are enabled, AppKit will report the wrong geometry for the first windowDidResize callback when a new tab is created. We could potentially remove the geometry change in windowDidResize, as the viewDidChangeFrame callback should be enough for content views, but this is something that needs more investigation. Change-Id: I85045507da1a01b4a906e6f88301f3321c660943 Fixes: QTBUG-75482 Reviewed-by: Morten Johan Sørvig Reviewed-by: Tor Arne Vestbø --- src/plugins/platforms/cocoa/qcocoawindow.mm | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index f992248275..d0ad1791c3 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -1085,9 +1085,11 @@ void QCocoaWindow::setEmbeddedInForeignView() void QCocoaWindow::viewDidChangeFrame() { - if (isContentView()) - return; // Handled below - + // Note: When the view is the content view, it would seem redundant + // to deliver geometry changes both from windowDidResize and this + // callback, but in some cases such as when macOS native tabbed + // windows are enabled we may end up with the wrong geometry in + // the initial windowDidResize callback when a new tab is created. handleGeometryChange(); } -- cgit v1.2.3 From 2a1651cc164b270dac81f4caa1fb8ded4fab6e4b Mon Sep 17 00:00:00 2001 From: Ville Voutilainen Date: Fri, 10 May 2019 16:45:56 +0300 Subject: Make moc grok binary literals with digit separators Task-number: QTBUG-75656 Change-Id: I6011ef2fb07497cc2a055d6828a1b6356927c281 Reviewed-by: Lars Knoll --- src/tools/moc/preprocessor.cpp | 3 ++- tests/auto/tools/moc/tst_moc.cpp | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tools/moc/preprocessor.cpp b/src/tools/moc/preprocessor.cpp index e83125925d..d135bddb4c 100644 --- a/src/tools/moc/preprocessor.cpp +++ b/src/tools/moc/preprocessor.cpp @@ -241,7 +241,8 @@ Symbols Preprocessor::tokenize(const QByteArray& input, int lineNum, Preprocesso if (!*data || *data != '.') { token = INTEGER_LITERAL; if (data - lexem == 1 && - (*data == 'x' || *data == 'X') + (*data == 'x' || *data == 'X' + || *data == 'b' || *data == 'B') && *lexem == '0') { ++data; while (is_hex_char(*data) || *data == '\'') diff --git a/tests/auto/tools/moc/tst_moc.cpp b/tests/auto/tools/moc/tst_moc.cpp index 293cbd8323..41bc4bc73b 100644 --- a/tests/auto/tools/moc/tst_moc.cpp +++ b/tests/auto/tools/moc/tst_moc.cpp @@ -524,6 +524,7 @@ private: #ifdef Q_MOC_RUN int xx = 11'11; // digit separator must not confuse moc (QTBUG-59351) + int xx = 0b11'11; // digit separator in a binary literal must not confuse moc (QTBUG-75656) #endif private slots: -- cgit v1.2.3 From 4d2ee7f358a5cea64e7093aa0ab54e6422f8915e Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Mon, 6 May 2019 12:35:05 +0200 Subject: Add unvectorized fallback in case FP exceptions are not masked If an application enables FP exceptions our FP-based unpremul will raise the INVALID exception. Since disabling them locally might be slow just take a slow path when detected. Fixes: QTBUG-75592 Change-Id: Ie22a032a4f62229f68ad21ede359c62291adc9bf Reviewed-by: Thiago Macieira --- src/gui/painting/qdrawhelper_sse4.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/gui/painting/qdrawhelper_sse4.cpp b/src/gui/painting/qdrawhelper_sse4.cpp index f6c2f11eaf..9daaaecc98 100644 --- a/src/gui/painting/qdrawhelper_sse4.cpp +++ b/src/gui/painting/qdrawhelper_sse4.cpp @@ -107,6 +107,17 @@ template static inline void convertARGBFromARGB32PM_sse4(uint *buffer, const uint *src, int count) { int i = 0; + if ((_MM_GET_EXCEPTION_MASK() & _MM_MASK_INVALID) == 0) { + for (; i < count; ++i) { + uint v = qUnpremultiply(src[i]); + if (RGBx) + v = 0xff000000 | v; + if (RGBA) + v = ARGB2RGBA(v); + buffer[i] = v; + } + return; + } const __m128i alphaMask = _mm_set1_epi32(0xff000000); const __m128i rgbaMask = _mm_setr_epi8(2, 1, 0, 3, 6, 5, 4, 7, 10, 9, 8, 11, 14, 13, 12, 15); const __m128i zero = _mm_setzero_si128(); @@ -174,6 +185,13 @@ template static inline void convertARGBFromRGBA64PM_sse4(uint *buffer, const QRgba64 *src, int count) { int i = 0; + if ((_MM_GET_EXCEPTION_MASK() & _MM_MASK_INVALID) == 0) { + for (; i < count; ++i) { + const QRgba64 v = src[i].unpremultiplied(); + buffer[i] = RGBA ? toRgba8888(v) : toArgb32(v); + } + return; + } const __m128i alphaMask = _mm_set1_epi64x(qint64(Q_UINT64_C(0xffff) << 48)); const __m128i alphaMask32 = _mm_set1_epi32(0xff000000); const __m128i rgbaMask = _mm_setr_epi8(2, 1, 0, 3, 6, 5, 4, 7, 10, 9, 8, 11, 14, 13, 12, 15); -- cgit v1.2.3 From c905ff4392ee0f71b5b003ee3ed4fa42f4e3f8f1 Mon Sep 17 00:00:00 2001 From: Vova Mshanetskiy Date: Mon, 6 May 2019 19:20:11 +0300 Subject: QAndroidInputContext: Fix start value of Cursor attribute in longPress() The value of start for a QInputMethodEvent::Cursor attribute must be specified relative to the start of preedit string, but longPress() was specifying it relative to start of surrounding text. This was causing QQuickTextInput to return wrong values of cursor and anchor rectangles. And this was causing invalid positioning of cursor selection handles after a long press. Change-Id: Ief67e86dd90b09ebf2ba191a2b0311ff803afdd9 Reviewed-by: BogDan Vatra --- src/plugins/platforms/android/qandroidinputcontext.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/platforms/android/qandroidinputcontext.cpp b/src/plugins/platforms/android/qandroidinputcontext.cpp index 00ab3409d3..07a6b52dbe 100644 --- a/src/plugins/platforms/android/qandroidinputcontext.cpp +++ b/src/plugins/platforms/android/qandroidinputcontext.cpp @@ -791,7 +791,7 @@ void QAndroidInputContext::longPress(int x, int y) return; } QList imAttributes; - imAttributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, cursor, 0, QVariant())); + imAttributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant())); imAttributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Selection, anchor, cursor - anchor, QVariant())); QInputMethodEvent event(QString(), imAttributes); QGuiApplication::sendEvent(m_focusObject, &event); -- cgit v1.2.3 From 341c9106881810962f253c1502d2c4f6da90e149 Mon Sep 17 00:00:00 2001 From: Vova Mshanetskiy Date: Tue, 7 May 2019 15:26:32 +0300 Subject: Android: Fix wrong height of text editor context menu in some locales Combined width of all four buttons (cut, copy, paste, select all) is greater than width of the screen in some locales and/or on some devices. This was causing width of the last button to be set to zero and height of the whole popup to grow too much due to word wrapping in the last button. The context menu used to look something like this then: Cut Copy Paste S e l e c t a l l This commit disables word wrapping and enables text ellipsizing for button labels. This fixes height of the popup. In the long term though Qt will probably have to implement an overflow button like in Android's built context menu. The linked bug report contains before and after screenshots. Fixes: QTBUG-72933 Change-Id: I8e270dbf8ca66f99748cdc531a77e11a5ab11c2b Reviewed-by: BogDan Vatra --- src/android/jar/src/org/qtproject/qt5/android/EditContextView.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/android/jar/src/org/qtproject/qt5/android/EditContextView.java b/src/android/jar/src/org/qtproject/qt5/android/EditContextView.java index 6d9987ca2a..104132934d 100644 --- a/src/android/jar/src/org/qtproject/qt5/android/EditContextView.java +++ b/src/android/jar/src/org/qtproject/qt5/android/EditContextView.java @@ -41,6 +41,7 @@ package org.qtproject.qt5.android; import android.content.Context; +import android.text.TextUtils; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; @@ -73,7 +74,7 @@ public class EditContextView extends LinearLayout implements View.OnClickListene m_buttonId = stringId; setText(stringId); setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT)); + ViewGroup.LayoutParams.WRAP_CONTENT, 1)); setGravity(Gravity.CENTER); setTextColor(getResources().getColor(R.color.widget_edittext_dark)); EditContextView.this.setBackground(getResources().getDrawable(R.drawable.editbox_background_normal)); @@ -81,6 +82,8 @@ public class EditContextView extends LinearLayout implements View.OnClickListene int hPadding = (int)(16 * scale + 0.5f); int vPadding = (int)(8 * scale + 0.5f); setPadding(hPadding, vPadding, hPadding, vPadding); + setSingleLine(); + setEllipsize(TextUtils.TruncateAt.END); setOnClickListener(EditContextView.this); } } -- cgit v1.2.3 From 41aa78856e054ee0d770e375f127dbc18317fbeb Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Fri, 10 May 2019 20:47:33 +0200 Subject: QSharedPointer: fix threadsafety docs Try and explain better the situation around QSharedPointer: it's reentrant, not thread safe. Change-Id: Ief9d28be8ea3fbaa6014cb6b999626db1bab52ca Reviewed-by: Martin Smith Reviewed-by: Thiago Macieira --- src/corelib/tools/qsharedpointer.cpp | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/corelib/tools/qsharedpointer.cpp b/src/corelib/tools/qsharedpointer.cpp index 95b584e914..b755941b73 100644 --- a/src/corelib/tools/qsharedpointer.cpp +++ b/src/corelib/tools/qsharedpointer.cpp @@ -65,14 +65,19 @@ \section1 Thread-Safety - QSharedPointer and QWeakPointer are thread-safe and operate - atomically on the pointer value. Different threads can also access - the QSharedPointer or QWeakPointer pointing to the same object at - the same time without need for locking mechanisms. - - It should be noted that, while the pointer value can be accessed - in this manner, QSharedPointer and QWeakPointer provide no - guarantee about the object being pointed to. Thread-safety and + QSharedPointer and QWeakPointer are reentrant classes. This means that, in + general, a given QSharedPointer or QWeakPointer object \b{cannot} be + accessed by multiple threads at the same time without synchronization. + + Different QSharedPointer and QWeakPointer objects can safely be accessed + by multiple threads at the same time. This includes the case where they + hold pointers to the same object; the reference counting mechanism + is atomic, and no manual synchronization is required. + + It should be noted that, while the pointer value can be accessed in this + manner (that is, by multiple threads at the same time, without + synchronization), QSharedPointer and QWeakPointer provide no guarantee + about the object being pointed to. The specific thread-safety and reentrancy rules for that object still apply. \section1 Other Pointer Classes -- cgit v1.2.3 From a9246c7132a2c8864d3ae6cebd260bb9ee711fcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Thu, 9 May 2019 18:20:16 +0200 Subject: Reset QWidget's winId when backing window surface is destroyed We already reset it though e.g. QWidget::destroy, but if the backing window is destroyed spontaneously or via another API we need to catch that and send a WinIdChange event so clients who pulled out the original winId will not think the pointer is still valid Change-Id: I8556940ee871e81a51f73daeb2064f95bf41371c Fixes: QTBUG-69289 Reviewed-by: Richard Moe Gustavsen --- src/widgets/kernel/qwidget.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index 2c84ff7161..53d87c6113 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -9365,6 +9365,12 @@ bool QWidget::event(QEvent *event) d->renderToTextureReallyDirty = 1; #endif break; + case QEvent::PlatformSurface: { + auto surfaceEvent = static_cast(event); + if (surfaceEvent->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) + d->setWinId(0); + break; + } #ifndef QT_NO_PROPERTIES case QEvent::DynamicPropertyChange: { const QByteArray &propName = static_cast(event)->propertyName(); -- cgit v1.2.3 From 45aa3c73f78c6c06874d0f826e1f112976cd522d Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 10 May 2019 13:42:02 +0200 Subject: hellogles3: Request core profile context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ...instead of compatibility, in order to play nice with systems that have no compatibility profile support (macOS). Instancing needs OpenGL 3.x so sticking with 2.x contexts is not an option. The example looks fully compatible with core profile so its functionality should not change. Change-Id: If0d554a6208973aa8a4fb86757e246d170cd0e71 Fixes: QTBUG-75680 Reviewed-by: Tor Arne Vestbø --- examples/opengl/hellogles3/main.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/opengl/hellogles3/main.cpp b/examples/opengl/hellogles3/main.cpp index 29b3b9617a..9451b2882f 100644 --- a/examples/opengl/hellogles3/main.cpp +++ b/examples/opengl/hellogles3/main.cpp @@ -69,11 +69,11 @@ int main(int argc, char *argv[]) QSurfaceFormat fmt; fmt.setDepthBufferSize(24); - // Request OpenGL 3.3 compatibility or OpenGL ES 3.0. + // Request OpenGL 3.3 core or OpenGL ES 3.0. if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) { - qDebug("Requesting 3.3 compatibility context"); + qDebug("Requesting 3.3 core context"); fmt.setVersion(3, 3); - fmt.setProfile(QSurfaceFormat::CompatibilityProfile); + fmt.setProfile(QSurfaceFormat::CoreProfile); } else { qDebug("Requesting 3.0 context"); fmt.setVersion(3, 0); -- cgit v1.2.3 From 3da9d8a4fefc6f4e06320d2ba4c7c5fa8ebd1a10 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Mon, 13 May 2019 11:13:17 +0200 Subject: Make sure QAccessibleTableCell is valid before reference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are some cases (model resets in weird positions) where we would crash due to accessing invalid model indices. Fixes: QTBUG-61416 Fixes: QTBUG-71608 Change-Id: Ibfedcbd921a3145f3e1596ac424a77f2319a5c46 Reviewed-by: Jan Arve Sæther --- src/widgets/accessible/itemviews.cpp | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/widgets/accessible/itemviews.cpp b/src/widgets/accessible/itemviews.cpp index 51cfaa7f5e..3bfe215c05 100644 --- a/src/widgets/accessible/itemviews.cpp +++ b/src/widgets/accessible/itemviews.cpp @@ -897,11 +897,15 @@ QHeaderView *QAccessibleTableCell::verticalHeader() const int QAccessibleTableCell::columnIndex() const { + if (!isValid()) + return -1; return m_index.column(); } int QAccessibleTableCell::rowIndex() const { + if (!isValid()) + return -1; #if QT_CONFIG(treeview) if (role() == QAccessible::TreeItem) { const QTreeView *treeView = qobject_cast(view); @@ -915,6 +919,8 @@ int QAccessibleTableCell::rowIndex() const bool QAccessibleTableCell::isSelected() const { + if (!isValid()) + return false; return view->selectionModel()->isSelected(m_index); } @@ -943,8 +949,10 @@ QStringList QAccessibleTableCell::keyBindingsForAction(const QString &) const void QAccessibleTableCell::selectCell() { + if (!isValid()) + return; QAbstractItemView::SelectionMode selectionMode = view->selectionMode(); - if (!m_index.isValid() || (selectionMode == QAbstractItemView::NoSelection)) + if (selectionMode == QAbstractItemView::NoSelection) return; Q_ASSERT(table()); QAccessibleTableInterface *cellTable = table()->tableInterface(); @@ -971,9 +979,10 @@ void QAccessibleTableCell::selectCell() void QAccessibleTableCell::unselectCell() { - + if (!isValid()) + return; QAbstractItemView::SelectionMode selectionMode = view->selectionMode(); - if (!m_index.isValid() || (selectionMode == QAbstractItemView::NoSelection)) + if (selectionMode == QAbstractItemView::NoSelection) return; QAccessibleTableInterface *cellTable = table()->tableInterface(); @@ -1014,7 +1023,7 @@ QAccessible::Role QAccessibleTableCell::role() const QAccessible::State QAccessibleTableCell::state() const { QAccessible::State st; - if (!view) + if (!isValid()) return st; QRect globalRect = view->rect(); @@ -1054,6 +1063,8 @@ QAccessible::State QAccessibleTableCell::state() const QRect QAccessibleTableCell::rect() const { QRect r; + if (!isValid()) + return r; r = view->visualRect(m_index); if (!r.isNull()) { @@ -1065,8 +1076,10 @@ QRect QAccessibleTableCell::rect() const QString QAccessibleTableCell::text(QAccessible::Text t) const { - QAbstractItemModel *model = view->model(); QString value; + if (!isValid()) + return value; + QAbstractItemModel *model = view->model(); switch (t) { case QAccessible::Name: value = model->data(m_index, Qt::AccessibleTextRole).toString(); @@ -1084,7 +1097,7 @@ QString QAccessibleTableCell::text(QAccessible::Text t) const void QAccessibleTableCell::setText(QAccessible::Text /*t*/, const QString &text) { - if (!(m_index.flags() & Qt::ItemIsEditable)) + if (!isValid() || !(m_index.flags() & Qt::ItemIsEditable)) return; view->model()->setData(m_index, text); } -- cgit v1.2.3 From 3976df280521cad0bb3e782369494ad4ef906fc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Thu, 21 Mar 2019 15:38:41 +0100 Subject: macOS: Track screens via Quartz Display Services instead of NSScreen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using NSScreen as the basis for tracking screens is not recommended, as the list of screens can be added, removed, or dynamically reconfigured at any time, and the NSScreen instance, or index in the NSScreen.screens array may not be stable. Quartz Display Services on the other hand tracks displays via a unique display ID, which typically remains constant until the machine is restarted. The lower level API also gives us earlier callbacks about screen changes than the corresponding NSApplicationDidChangeScreenParametersNotification does. By reacting to screen changes _before_ AppKit does, we can remove workarounds for receiving window move and screen change notifications before the screen was actually visibly reconfigured. The new approach also handles changes to the primary screen, which can happen if the user moves the menu bar in the macOS display arrangement pane. The device pixel ratio of the screen has been made into a cached property, like all the other properties of QCocoaScreen. This is more consistent, and allows us to qDebug the screen even when it has been removed and we no longer have access to resolve the properties from the associated Quarts display. Change-Id: I2d86c7629ed3bf5fb8c77f174712633752ae4079 Reviewed-by: Morten Johan Sørvig --- src/plugins/platforms/cocoa/qcocoabackingstore.mm | 12 -- src/plugins/platforms/cocoa/qcocoahelpers.h | 12 ++ src/plugins/platforms/cocoa/qcocoahelpers.mm | 2 +- src/plugins/platforms/cocoa/qcocoaintegration.h | 7 - src/plugins/platforms/cocoa/qcocoaintegration.mm | 91 +-------- src/plugins/platforms/cocoa/qcocoascreen.h | 31 ++- src/plugins/platforms/cocoa/qcocoascreen.mm | 220 +++++++++++++++------ .../platforms/cocoa/qcocoasystemtrayicon.mm | 6 +- src/plugins/platforms/cocoa/qcocoawindow.mm | 8 +- 9 files changed, 204 insertions(+), 185 deletions(-) diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm index e786ecb5a5..a98fcfae92 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.mm +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm @@ -273,18 +273,6 @@ void QNSWindowBackingStore::redrawRoundedBottomCorners(CGRect windowRect) const // ---------------------------------------------------------------------------- -// 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) { diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.h b/src/plugins/platforms/cocoa/qcocoahelpers.h index 69aa7937b6..69a1854598 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.h +++ b/src/plugins/platforms/cocoa/qcocoahelpers.h @@ -176,6 +176,18 @@ T qt_mac_resolveOption(const T &fallback, QWindow *window, const QByteArray &pro return fallback; } +// 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)}; } + // ------------------------------------------------------------------------- #if !defined(Q_PROCESSOR_X86_64) diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm index 9c705616ba..1b184cd60f 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.mm +++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm @@ -63,7 +63,7 @@ QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcQpaWindow, "qt.qpa.window"); Q_LOGGING_CATEGORY(lcQpaDrawing, "qt.qpa.drawing"); Q_LOGGING_CATEGORY(lcQpaMouse, "qt.qpa.input.mouse", QtCriticalMsg); -Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen"); +Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen", QtCriticalMsg); // // Conversion Functions diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.h b/src/plugins/platforms/cocoa/qcocoaintegration.h index 04cb4e1226..bfc3bfe9de 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.h +++ b/src/plugins/platforms/cocoa/qcocoaintegration.h @@ -61,8 +61,6 @@ QT_BEGIN_NAMESPACE -class QCocoaScreen; - class QCocoaIntegration : public QObject, public QPlatformIntegration { Q_OBJECT @@ -113,9 +111,6 @@ public: Qt::KeyboardModifiers queryKeyboardModifiers() const override; QList possibleKeys(const QKeyEvent *event) const override; - void updateScreens(); - QCocoaScreen *screenForNSScreen(NSScreen *nsScreen); - void setToolbar(QWindow *window, NSToolbar *toolbar); NSToolbar *toolbar(QWindow *window) const; void clearToolbars(); @@ -143,8 +138,6 @@ private: QScopedPointer mAccessibility; #endif QScopedPointer mPlatformTheme; - QList mScreens; - QMacScopedObserver m_screensObserver; #ifndef QT_NO_CLIPBOARD QCocoaClipboard *mCocoaClipboard; #endif diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index fb3d05d3e4..1d35d9f440 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.mm +++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm @@ -207,9 +207,7 @@ QCocoaIntegration::QCocoaIntegration(const QStringList ¶mList) // which will resolve to an actual value and result in screen invalidation. cocoaApplication.presentationOptions = NSApplicationPresentationDefault; - m_screensObserver = QMacScopedObserver([NSApplication sharedApplication], - NSApplicationDidChangeScreenParametersNotification, [&]() { updateScreens(); }); - updateScreens(); + QCocoaScreen::initializeScreens(); QMacInternalPasteboardMime::initializeMimeTypes(); QCocoaMimeTypes::initializeMimeTypes(); @@ -242,10 +240,7 @@ QCocoaIntegration::~QCocoaIntegration() QMacInternalPasteboardMime::destroyMimeTypes(); #endif - // Delete screens in reverse order to avoid crash in case of multiple screens - while (!mScreens.isEmpty()) { - QWindowSystemInterface::handleScreenRemoved(mScreens.takeLast()); - } + QCocoaScreen::cleanupScreens(); clearToolbars(); } @@ -260,88 +255,6 @@ QCocoaIntegration::Options QCocoaIntegration::options() const return mOptions; } -/*! - \brief Synchronizes the screen list, adds new screens, removes deleted ones -*/ -void QCocoaIntegration::updateScreens() -{ - NSArray *scrs = [NSScreen screens]; - NSMutableArray *screens = [NSMutableArray arrayWithArray:scrs]; - if ([screens count] == 0) - if ([NSScreen mainScreen]) - [screens addObject:[NSScreen mainScreen]]; - if ([screens count] == 0) - return; - QSet remainingScreens = QSet::fromList(mScreens); - QList siblings; - uint screenCount = [screens count]; - for (uint i = 0; i < screenCount; i++) { - NSScreen* scr = [screens objectAtIndex:i]; - CGDirectDisplayID dpy = scr.qt_displayId; - // If this screen is a mirror and is not the primary one of the mirror set, ignore it. - // Exception: The NSScreen API has been observed to a return a screen list with one - // mirrored, non-primary screen when Qt is running as a startup item. Always use the - // screen if there's only one screen in the list. - if (screenCount > 1 && CGDisplayIsInMirrorSet(dpy)) { - CGDirectDisplayID primary = CGDisplayMirrorsDisplay(dpy); - if (primary != kCGNullDirectDisplay && primary != dpy) - continue; - } - QCocoaScreen* screen = nullptr; - foreach (QCocoaScreen* existingScr, mScreens) { - // NSScreen documentation says do not cache the array returned from [NSScreen screens]. - // However in practice, we can identify a screen by its pointer: if resolution changes, - // the NSScreen object will be the same instance, just with different values. - if (existingScr->nativeScreen() == scr) { - screen = existingScr; - break; - } - } - if (screen) { - remainingScreens.remove(screen); - screen->updateProperties(); - } else { - screen = new QCocoaScreen(i); - mScreens.append(screen); - qCDebug(lcQpaScreen) << "Adding" << screen; - QWindowSystemInterface::handleScreenAdded(screen); - } - siblings << screen; - } - - // Set virtual siblings list. All screens in mScreens are siblings, because we ignored the - // mirrors. Note that some of the screens we update the siblings list for here may be deleted - // below, but update anyway to keep the to-be-deleted screens out of the siblings list. - foreach (QCocoaScreen* screen, mScreens) - screen->setVirtualSiblings(siblings); - - // Now the leftovers in remainingScreens are no longer current, so we can delete them. - foreach (QCocoaScreen* screen, remainingScreens) { - mScreens.removeOne(screen); - // Prevent stale references to NSScreen during destroy - screen->m_screenIndex = -1; - qCDebug(lcQpaScreen) << "Removing" << screen; - QWindowSystemInterface::handleScreenRemoved(screen); - } -} - -QCocoaScreen *QCocoaIntegration::screenForNSScreen(NSScreen *nsScreen) -{ - NSUInteger index = [[NSScreen screens] indexOfObject:nsScreen]; - if (index == NSNotFound) - return nullptr; - - if (index >= unsigned(mScreens.count())) - updateScreens(); - - for (QCocoaScreen *screen : mScreens) { - if (screen->nativeScreen() == nsScreen) - return screen; - } - - return nullptr; -} - bool QCocoaIntegration::hasCapability(QPlatformIntegration::Capability cap) const { switch (cap) { diff --git a/src/plugins/platforms/cocoa/qcocoascreen.h b/src/plugins/platforms/cocoa/qcocoascreen.h index 9ded98df32..491af2fe9c 100644 --- a/src/plugins/platforms/cocoa/qcocoascreen.h +++ b/src/plugins/platforms/cocoa/qcocoascreen.h @@ -48,10 +48,14 @@ QT_BEGIN_NAMESPACE +class QCocoaIntegration; + class QCocoaScreen : public QPlatformScreen { public: - QCocoaScreen(int screenIndex); + static void initializeScreens(); + static void cleanupScreens(); + ~QCocoaScreen(); // ---------------------------------------------------- @@ -61,19 +65,18 @@ public: QRect availableGeometry() const override { return m_availableGeometry; } int depth() const override { return m_depth; } QImage::Format format() const override { return m_format; } - qreal devicePixelRatio() const override; + qreal devicePixelRatio() const override { return m_devicePixelRatio; } QSizeF physicalSize() const override { return m_physicalSize; } QDpi logicalDpi() const override { return m_logicalDpi; } qreal refreshRate() const override { return m_refreshRate; } QString name() const override { return m_name; } QPlatformCursor *cursor() const override { return m_cursor; } QWindow *topLevelAt(const QPoint &point) const override; - QList virtualSiblings() const override { return m_siblings; } + QList virtualSiblings() const override; QPlatformScreen::SubpixelAntialiasingType subpixelAntialiasingTypeHint() const override; // ---------------------------------------------------- - // Additional methods - void setVirtualSiblings(const QList &siblings) { m_siblings = siblings; } + NSScreen *nativeScreen() const; void updateProperties(); @@ -82,14 +85,21 @@ public: bool isRunningDisplayLink() const; static QCocoaScreen *primaryScreen(); + static QCocoaScreen *get(NSScreen *nsScreen); + static QCocoaScreen *get(CGDirectDisplayID displayId); static CGPoint mapToNative(const QPointF &pos, QCocoaScreen *screen = QCocoaScreen::primaryScreen()); static CGRect mapToNative(const QRectF &rect, QCocoaScreen *screen = QCocoaScreen::primaryScreen()); static QPointF mapFromNative(CGPoint pos, QCocoaScreen *screen = QCocoaScreen::primaryScreen()); static QRectF mapFromNative(CGRect rect, QCocoaScreen *screen = QCocoaScreen::primaryScreen()); -public: - int m_screenIndex; +private: + QCocoaScreen(CGDirectDisplayID displayId); + static void add(CGDirectDisplayID displayId); + void remove(); + + CGDirectDisplayID m_displayId = 0; + QRect m_geometry; QRect m_availableGeometry; QDpi m_logicalDpi; @@ -99,11 +109,13 @@ public: QImage::Format m_format; QSizeF m_physicalSize; QCocoaCursor *m_cursor; - QList m_siblings; + qreal m_devicePixelRatio; CVDisplayLinkRef m_displayLink = nullptr; dispatch_source_t m_displayLinkSource = nullptr; QAtomicInt m_pendingUpdates; + + friend QDebug operator<<(QDebug debug, const QCocoaScreen *screen); }; #ifndef QT_NO_DEBUG_STREAM @@ -116,5 +128,4 @@ QT_END_NAMESPACE @property(readonly) CGDirectDisplayID qt_displayId; @end -#endif - +#endif // QCOCOASCREEN_H diff --git a/src/plugins/platforms/cocoa/qcocoascreen.mm b/src/plugins/platforms/cocoa/qcocoascreen.mm index 6a5b0e6e3e..392099d083 100644 --- a/src/plugins/platforms/cocoa/qcocoascreen.mm +++ b/src/plugins/platforms/cocoa/qcocoascreen.mm @@ -41,6 +41,7 @@ #include "qcocoawindow.h" #include "qcocoahelpers.h" +#include "qcocoaintegration.h" #include #include @@ -53,34 +54,104 @@ QT_BEGIN_NAMESPACE -class QCoreTextFontEngine; -class QFontEngineFT; +void QCocoaScreen::initializeScreens() +{ + uint32_t displayCount = 0; + if (CGGetActiveDisplayList(0, nullptr, &displayCount) != kCGErrorSuccess) + qFatal("Failed to get number of active displays"); + + CGDirectDisplayID activeDisplays[displayCount]; + if (CGGetActiveDisplayList(displayCount, &activeDisplays[0], &displayCount) != kCGErrorSuccess) + qFatal("Failed to get active displays"); + + for (CGDirectDisplayID displayId : activeDisplays) + QCocoaScreen::add(displayId); + + CGDisplayRegisterReconfigurationCallback([](CGDirectDisplayID displayId, CGDisplayChangeSummaryFlags flags, void *userInfo) { + if (flags & kCGDisplayBeginConfigurationFlag) + return; // Wait for changes to apply + + Q_UNUSED(userInfo); + + QCocoaScreen *cocoaScreen = QCocoaScreen::get(displayId); + + if ((flags & kCGDisplayAddFlag) || !cocoaScreen) { + if (!CGDisplayIsActive(displayId)) { + qCDebug(lcQpaScreen) << "Not adding inactive display" << displayId; + return; // Will be added when activated + } + QCocoaScreen::add(displayId); + } else if ((flags & kCGDisplayRemoveFlag) || !CGDisplayIsActive(displayId)) { + cocoaScreen->remove(); + } else { + // Detect changes to the primary screen immediately, instead of + // waiting for a display reconfigure with kCGDisplaySetMainFlag. + // This ensures that any property updates to the other screens + // will be in reference to the correct primary screen. + QCocoaScreen *mainDisplay = QCocoaScreen::get(CGMainDisplayID()); + if (QGuiApplication::primaryScreen()->handle() != mainDisplay) { + mainDisplay->updateProperties(); + qCInfo(lcQpaScreen) << "Primary screen changed to" << mainDisplay; + QWindowSystemInterface::handlePrimaryScreenChanged(mainDisplay); + } -QCocoaScreen::QCocoaScreen(int screenIndex) - : QPlatformScreen(), m_screenIndex(screenIndex), m_refreshRate(60.0) + if (cocoaScreen == mainDisplay) + return; // Already reconfigured + + cocoaScreen->updateProperties(); + qCInfo(lcQpaScreen) << "Reconfigured" << cocoaScreen; + } + }, nullptr); +} + +void QCocoaScreen::add(CGDirectDisplayID displayId) +{ + QCocoaScreen *cocoaScreen = new QCocoaScreen(displayId); + qCInfo(lcQpaScreen) << "Adding" << cocoaScreen; + QWindowSystemInterface::handleScreenAdded(cocoaScreen, CGDisplayIsMain(displayId)); +} + +QCocoaScreen::QCocoaScreen(CGDirectDisplayID displayId) + : QPlatformScreen(), m_displayId(displayId) { updateProperties(); m_cursor = new QCocoaCursor; } -QCocoaScreen::~QCocoaScreen() +void QCocoaScreen::cleanupScreens() { - delete m_cursor; + // Remove screens in reverse order to avoid crash in case of multiple screens + for (QScreen *screen : backwards(QGuiApplication::screens())) + static_cast(screen->handle())->remove(); +} - CVDisplayLinkRelease(m_displayLink); - if (m_displayLinkSource) - dispatch_release(m_displayLinkSource); +void QCocoaScreen::remove() +{ + m_displayId = 0; // Prevent stale references during removal + + // This may result in the application responding to QGuiApplication::screenRemoved + // by moving the window to another screen, either by setGeometry, or by setScreen. + // If the window isn't moved by the application, Qt will as a fallback move it to + // the primary screen via setScreen. Due to the way setScreen works, this won't + // actually recreate the window on the new screen, it will just assign the new + // QScreen to the window. The associated NSWindow will have an NSScreen determined + // by AppKit. AppKit will then move the window to another screen by changing the + // geometry, and we will get a callback in QCocoaWindow::windowDidMove and then + // QCocoaWindow::windowDidChangeScreen. At that point the window will appear to have + // already changed its screen, but that's only true if comparing the Qt screens, + // not when comparing the NSScreens. + QWindowSystemInterface::handleScreenRemoved(this); } -NSScreen *QCocoaScreen::nativeScreen() const +QCocoaScreen::~QCocoaScreen() { - NSArray *screens = [NSScreen screens]; + Q_ASSERT_X(!screen(), "QCocoaScreen", "QScreen should be deleted first"); - // Stale reference, screen configuration has changed - if (m_screenIndex < 0 || (NSUInteger)m_screenIndex >= [screens count]) - return nil; + delete m_cursor; - return [screens objectAtIndex:m_screenIndex]; + CVDisplayLinkRelease(m_displayLink); + if (m_displayLinkSource) + dispatch_release(m_displayLinkSource); } static QString displayName(CGDirectDisplayID displayID) @@ -117,35 +188,37 @@ static QString displayName(CGDirectDisplayID displayID) void QCocoaScreen::updateProperties() { - NSScreen *nsScreen = nativeScreen(); - if (!nsScreen) - return; + Q_ASSERT(m_displayId); const QRect previousGeometry = m_geometry; const QRect previousAvailableGeometry = m_availableGeometry; const QDpi previousLogicalDpi = m_logicalDpi; const qreal previousRefreshRate = m_refreshRate; + // Some properties are only available via NSScreen + NSScreen *nsScreen = nativeScreen(); + Q_ASSERT(nsScreen); + // The reference screen for the geometry is always the primary screen - QRectF primaryScreenGeometry = QRectF::fromCGRect([[NSScreen screens] firstObject].frame); + QRectF primaryScreenGeometry = QRectF::fromCGRect(CGDisplayBounds(CGMainDisplayID())); m_geometry = qt_mac_flip(QRectF::fromCGRect(nsScreen.frame), primaryScreenGeometry).toRect(); m_availableGeometry = qt_mac_flip(QRectF::fromCGRect(nsScreen.visibleFrame), primaryScreenGeometry).toRect(); + m_devicePixelRatio = nsScreen.backingScaleFactor; + m_format = QImage::Format_RGB32; - m_depth = NSBitsPerPixelFromDepth([nsScreen depth]); + m_depth = NSBitsPerPixelFromDepth(nsScreen.depth); - CGDirectDisplayID dpy = nsScreen.qt_displayId; - CGSize size = CGDisplayScreenSize(dpy); + CGSize size = CGDisplayScreenSize(m_displayId); m_physicalSize = QSizeF(size.width, size.height); m_logicalDpi.first = 72; m_logicalDpi.second = 72; - CGDisplayModeRef displayMode = CGDisplayCopyDisplayMode(dpy); + + QCFType displayMode = CGDisplayCopyDisplayMode(m_displayId); float refresh = CGDisplayModeGetRefreshRate(displayMode); - CGDisplayModeRelease(displayMode); - if (refresh > 0) - m_refreshRate = refresh; + m_refreshRate = refresh > 0 ? refresh : 60.0; - m_name = displayName(dpy); + m_name = displayName(m_displayId); const bool didChangeGeometry = m_geometry != previousGeometry || m_availableGeometry != previousAvailableGeometry; @@ -155,24 +228,6 @@ void QCocoaScreen::updateProperties() QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen(), m_logicalDpi.first, m_logicalDpi.second); if (m_refreshRate != previousRefreshRate) QWindowSystemInterface::handleScreenRefreshRateChange(screen(), m_refreshRate); - - qCDebug(lcQpaScreen) << "Updated properties for" << this; - - if (didChangeGeometry) { - // When a screen changes its geometry, AppKit will send us a NSWindowDidMoveNotification - // for each window, resulting in calls to handleGeometryChange(), but this happens before - // the NSApplicationDidChangeScreenParametersNotification, so when we map the new geometry - // (which is correct at that point) to the screen using QCocoaScreen::mapFromNative(), we - // end up using the stale screen geometry, and the new window geometry we report is wrong. - // To make sure we finally report the correct window geometry, we need to do another pass - // of geometry reporting, now that the screen properties have been updates. FIXME: Ideally - // this would be solved by not caching the screen properties in QCocoaScreen, but that - // requires more research. - for (QWindow *window : windows()) { - if (QCocoaWindow *cocoaWindow = static_cast(window->handle())) - cocoaWindow->handleGeometryChange(); - } - } } // ----------------------- Display link ----------------------- @@ -181,8 +236,10 @@ Q_LOGGING_CATEGORY(lcQpaScreenUpdates, "qt.qpa.screen.updates", QtCriticalMsg); void QCocoaScreen::requestUpdate() { + Q_ASSERT(m_displayId); + if (!m_displayLink) { - CVDisplayLinkCreateWithCGDisplay(nativeScreen().qt_displayId, &m_displayLink); + CVDisplayLinkCreateWithCGDisplay(m_displayId, &m_displayLink); CVDisplayLinkSetOutputCallback(m_displayLink, [](CVDisplayLinkRef, const CVTimeStamp*, const CVTimeStamp*, CVOptionFlags, CVOptionFlags*, void* displayLinkContext) -> int { // FIXME: It would be nice if update requests would include timing info @@ -269,6 +326,9 @@ struct DeferredDebugHelper void QCocoaScreen::deliverUpdateRequests() { + if (!m_displayId) + return; // Screen removed + QMacAutoReleasePool pool; // The CVDisplayLink callback is a notification that it's a good time to produce a new frame. @@ -283,7 +343,7 @@ void QCocoaScreen::deliverUpdateRequests() const int pendingUpdates = ++m_pendingUpdates; DeferredDebugHelper screenUpdates(lcQpaScreenUpdates()); - qDeferredDebug(screenUpdates) << "display link callback for screen " << m_screenIndex; + qDeferredDebug(screenUpdates) << "display link callback for screen " << m_displayId; if (const int framesAheadOfDelivery = pendingUpdates - 1) { // If we have more than one update pending it means that a previous display link callback @@ -370,13 +430,6 @@ bool QCocoaScreen::isRunningDisplayLink() const // ----------------------------------------------------------- -qreal QCocoaScreen::devicePixelRatio() const -{ - QMacAutoReleasePool pool; - NSScreen *nsScreen = nativeScreen(); - return qreal(nsScreen ? [nsScreen backingScaleFactor] : 1.0); -} - QPlatformScreen::SubpixelAntialiasingType QCocoaScreen::subpixelAntialiasingTypeHint() const { QPlatformScreen::SubpixelAntialiasingType type = QPlatformScreen::subpixelAntialiasingTypeHint(); @@ -430,7 +483,7 @@ QPixmap QCocoaScreen::grabWindow(WId view, int x, int y, int width, int height) { // Determine the grab rect. FIXME: The rect should be bounded by the view's // geometry, but note that for the pixeltool use case that window will be the - // desktop widgets's view, which currently gets resized to fit one screen + // desktop widget's view, which currently gets resized to fit one screen // only, since its NSWindow has the NSWindowStyleMaskTitled flag set. Q_UNUSED(view); QRect grabRect = QRect(x, y, width, height); @@ -482,7 +535,7 @@ QPixmap QCocoaScreen::grabWindow(WId view, int x, int y, int width, int height) for (uint i = 0; i < displayCount; ++i) dpr = qMax(dpr, images.at(i).devicePixelRatio()); - // Alocate target pixmap and draw each screen's content + // Allocate target pixmap and draw each screen's content qCDebug(lcQpaScreen) << "Create grap pixmap" << grabRect.size() << "at devicePixelRatio" << dpr; QPixmap windowPixmap(grabRect.size() * dpr); windowPixmap.setDevicePixelRatio(dpr); @@ -499,7 +552,57 @@ QPixmap QCocoaScreen::grabWindow(WId view, int x, int y, int width, int height) */ QCocoaScreen *QCocoaScreen::primaryScreen() { - return static_cast(QGuiApplication::primaryScreen()->handle()); + auto screen = static_cast(QGuiApplication::primaryScreen()->handle()); + Q_ASSERT_X(screen == get(CGMainDisplayID()), "QCocoaScreen", + "The application's primary screen should always be in sync with the main display"); + return screen; +} + +QList QCocoaScreen::virtualSiblings() const +{ + QList siblings; + + // Screens on macOS are always part of the same virtual desktop + for (QScreen *screen : QGuiApplication::screens()) + siblings << screen->handle(); + + return siblings; +} + +QCocoaScreen *QCocoaScreen::get(NSScreen *nsScreen) +{ + return get(nsScreen.qt_displayId); +} + +QCocoaScreen *QCocoaScreen::get(CGDirectDisplayID displayId) +{ + for (QScreen *screen : QGuiApplication::screens()) { + QCocoaScreen *cocoaScreen = static_cast(screen->handle()); + if (cocoaScreen->m_displayId == displayId) + return cocoaScreen; + } + + return nullptr; +} + +NSScreen *QCocoaScreen::nativeScreen() const +{ + if (!m_displayId) + return nil; // The display has been disconnected + + // A single display may have different displayIds depending on + // which GPU is in use or which physical port the display is + // connected to. By comparing UUIDs instead of display IDs we + // ensure that we always pick up the appropriate NSScreen. + QCFType uuid = CGDisplayCreateUUIDFromDisplayID(m_displayId); + + for (NSScreen *screen in [NSScreen screens]) { + if (CGDisplayCreateUUIDFromDisplayID(screen.qt_displayId) == uuid) + return screen; + } + + qCWarning(lcQpaScreen) << "Could not find NSScreen for display ID" << m_displayId; + return nil; } CGPoint QCocoaScreen::mapToNative(const QPointF &pos, QCocoaScreen *screen) @@ -533,11 +636,10 @@ QDebug operator<<(QDebug debug, const QCocoaScreen *screen) debug.nospace(); debug << "QCocoaScreen(" << (const void *)screen; if (screen) { - debug << ", index=" << screen->m_screenIndex; - debug << ", native=" << screen->nativeScreen(); debug << ", geometry=" << screen->geometry(); debug << ", dpr=" << screen->devicePixelRatio(); debug << ", name=" << screen->name(); + debug << ", native=" << screen->nativeScreen(); } debug << ')'; return debug; diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm index 4982f5ee05..de5cf85854 100644 --- a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm +++ b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm @@ -383,9 +383,9 @@ QT_END_NAMESPACE } - (QRectF)geometry { - if (NSWindow *window = [[item view] window]) { - if (QCocoaScreen *screen = QCocoaIntegration::instance()->screenForNSScreen([window screen])) - return screen->mapFromNative([window frame]); + if (NSWindow *window = item.view.window) { + if (QCocoaScreen *screen = QCocoaScreen::get(window.screen)) + return screen->mapFromNative(window.frame); } return QRectF(); } diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index 0d7eab9a94..c09ff12c2b 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -1209,17 +1209,17 @@ void QCocoaWindow::windowDidChangeScreen() return; // Note: When a window is resized to 0x0 Cocoa will report the window's screen as nil - auto *currentScreen = QCocoaIntegration::instance()->screenForNSScreen(m_view.window.screen); + auto *currentScreen = QCocoaScreen::get(m_view.window.screen); auto *previousScreen = static_cast(screen()); Q_ASSERT_X(!m_view.window.screen || currentScreen, "QCocoaWindow", "Failed to get QCocoaScreen for NSScreen"); // Note: The previous screen may be the same as the current screen, either because - // the screen was just reconfigured, which still results in AppKit sending an - // NSWindowDidChangeScreenNotification, because the previous screen was removed, + // a) the screen was just reconfigured, which still results in AppKit sending an + // NSWindowDidChangeScreenNotification, b) because the previous screen was removed, // and we ended up calling QWindow::setScreen to move the window, which doesn't - // actually move the window to the new screen, or because we've delivered the + // actually move the window to the new screen, or c) because we've delivered the // screen change to the top level window, which will make all the child windows // of that window report the new screen when requested via QWindow::screen(). // We still need to deliver the screen change in all these cases, as the -- cgit v1.2.3 From 98cb9275d064d8b996dcd78324c4249f69a981a9 Mon Sep 17 00:00:00 2001 From: Martin Smith Date: Wed, 5 Dec 2018 13:18:50 +0100 Subject: doc: clang reported two fake declarations to be the same These declarations are provided for qdoc, but clang says they are the same: template QMetaObject::Connection callOnTimeout(const QObject *context, Functor slot, Qt::ConnectionType connectionType = Qt::AutoConnection); template QMetaObject::Connection callOnTimeout(const QObject *receiver, PointerToMemberFunction slot, Qt::ConnectionType connectionType = Qt::AutoConnection); clang accepts this one, but is it ok for the documentation? template QMetaObject::Connection callOnTimeout(const QObject *receiver, MemberFunction *slot, Qt::ConnectionType connectionType = Qt::AutoConnection); Change-Id: I9d63b1bccfa8d73dbc17ab70c4415eb7891fbbe2 Reviewed-by: Martin Smith --- src/corelib/kernel/qtimer.cpp | 2 +- src/corelib/kernel/qtimer.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/corelib/kernel/qtimer.cpp b/src/corelib/kernel/qtimer.cpp index 13f027074a..9d3bd5fdbf 100644 --- a/src/corelib/kernel/qtimer.cpp +++ b/src/corelib/kernel/qtimer.cpp @@ -599,7 +599,7 @@ void QTimer::singleShot(int msec, Qt::TimerType timerType, const QObject *receiv */ /*! - \fn template QMetaObject::Connection QTimer::callOnTimeout(const QObject *receiver, PointerToMemberFunction slot, Qt::ConnectionType connectionType = Qt::AutoConnection) + \fn template QMetaObject::Connection QTimer::callOnTimeout(const QObject *receiver, MemberFunction *slot, Qt::ConnectionType connectionType = Qt::AutoConnection) \since 5.12 \overload callOnTimeout() diff --git a/src/corelib/kernel/qtimer.h b/src/corelib/kernel/qtimer.h index 66f317c567..336b814b27 100644 --- a/src/corelib/kernel/qtimer.h +++ b/src/corelib/kernel/qtimer.h @@ -100,8 +100,8 @@ public: QMetaObject::Connection callOnTimeout(Functor slot, Qt::ConnectionType connectionType = Qt::AutoConnection); template QMetaObject::Connection callOnTimeout(const QObject *context, Functor slot, Qt::ConnectionType connectionType = Qt::AutoConnection); - template - QMetaObject::Connection callOnTimeout(const QObject *receiver, PointerToMemberFunction slot, Qt::ConnectionType connectionType = Qt::AutoConnection); + template + QMetaObject::Connection callOnTimeout(const QObject *receiver, MemberFunction *slot, Qt::ConnectionType connectionType = Qt::AutoConnection); #else // singleShot to a QObject slot template -- cgit v1.2.3 From 56acf089c7759ce21a5bc3ce32616df5b01e78ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Tue, 30 Apr 2019 09:17:54 +0200 Subject: wasm: support setting the font DPI from JS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have not really been able to determine what the default DPI should be, so make it configurable with API on qtloader.js: qtLoader.setFontDpi(72); Also lowers the default DPI to the standard value of 96 (down from Qt default 100). Task-number: QTBUG-75510 Change-Id: Ica1164c8d80bb06519233adebf2c9e400c0991ce Reviewed-by: Tor Arne Vestbø --- src/plugins/platforms/wasm/qtloader.js | 14 ++++++++++++++ src/plugins/platforms/wasm/qwasmintegration.cpp | 17 +++++++++++++++++ src/plugins/platforms/wasm/qwasmintegration.h | 2 ++ src/plugins/platforms/wasm/qwasmscreen.cpp | 12 ++++++++++++ src/plugins/platforms/wasm/qwasmscreen.h | 1 + 5 files changed, 46 insertions(+) diff --git a/src/plugins/platforms/wasm/qtloader.js b/src/plugins/platforms/wasm/qtloader.js index 4752a1dcee..ef4a6ec2b9 100644 --- a/src/plugins/platforms/wasm/qtloader.js +++ b/src/plugins/platforms/wasm/qtloader.js @@ -124,6 +124,8 @@ // Remove canvas at run-time. Removes the corresponding QScreen. // resizeCanvasElement // Signals to the application that a canvas has been resized. +// setFontDpi +// Sets the logical font dpi for the application. var Module = {} @@ -237,6 +239,8 @@ function QtLoader(config) publicAPI.addCanvasElement = addCanvasElement; publicAPI.removeCanvasElement = removeCanvasElement; publicAPI.resizeCanvasElement = resizeCanvasElement; + publicAPI.setFontDpi = setFontDpi; + publicAPI.fontDpi = fontDpi; restartCount = 0; @@ -557,6 +561,16 @@ function QtLoader(config) Module.qtResizeCanvasElement(element); } + function setFontDpi(dpi) { + Module.qtFontDpi = dpi; + if (publicAPI.status == "Running") + Module.qtSetFontDpi(dpi); + } + + function fontDpi() { + return Module.qtFontDpi; + } + setStatus("Created"); return publicAPI; diff --git a/src/plugins/platforms/wasm/qwasmintegration.cpp b/src/plugins/platforms/wasm/qwasmintegration.cpp index e601d553f2..150783e193 100644 --- a/src/plugins/platforms/wasm/qwasmintegration.cpp +++ b/src/plugins/platforms/wasm/qwasmintegration.cpp @@ -50,6 +50,7 @@ #include #include +#include // this is where EGL headers are pulled in, make sure it is last #include "qwasmscreen.h" @@ -80,12 +81,18 @@ static void resizeCanvasElement(emscripten::val canvas) QWasmIntegration::get()->resizeScreen(canvasId); } +static void qtUpdateDpi() +{ + QWasmIntegration::get()->updateDpi(); +} + EMSCRIPTEN_BINDINGS(qtQWasmIntegraton) { function("qtBrowserBeforeUnload", &browserBeforeUnload); function("qtAddCanvasElement", &addCanvasElement); function("qtRemoveCanvasElement", &removeCanvasElement); function("qtResizeCanvasElement", &resizeCanvasElement); + function("qtUpdateDpi", &qtUpdateDpi); } QWasmIntegration *QWasmIntegration::s_instance; @@ -245,4 +252,14 @@ void QWasmIntegration::resizeScreen(const QString &canvasId) m_screens.value(canvasId)->updateQScreenAndCanvasRenderSize(); } +void QWasmIntegration::updateDpi() +{ + emscripten::val dpi = emscripten::val::module_property("qtFontDpi"); + if (dpi.isUndefined()) + return; + qreal dpiValue = dpi.as(); + for (QWasmScreen *screen : m_screens) + QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen->screen(), dpiValue, dpiValue); +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/wasm/qwasmintegration.h b/src/plugins/platforms/wasm/qwasmintegration.h index 11d8d0f7f5..6bd2f857db 100644 --- a/src/plugins/platforms/wasm/qwasmintegration.h +++ b/src/plugins/platforms/wasm/qwasmintegration.h @@ -81,6 +81,7 @@ public: void addScreen(const QString &canvasId); void removeScreen(const QString &canvasId); void resizeScreen(const QString &canvasId); + void updateDpi(); private: mutable QWasmFontDatabase *m_fontDb; @@ -89,6 +90,7 @@ private: QHash m_screens; mutable QWasmClipboard *m_clipboard; + qreal m_fontDpi = -1; static QWasmIntegration *s_instance; }; diff --git a/src/plugins/platforms/wasm/qwasmscreen.cpp b/src/plugins/platforms/wasm/qwasmscreen.cpp index a26cafa900..af50ce7440 100644 --- a/src/plugins/platforms/wasm/qwasmscreen.cpp +++ b/src/plugins/platforms/wasm/qwasmscreen.cpp @@ -31,6 +31,7 @@ #include "qwasmwindow.h" #include "qwasmeventtranslator.h" #include "qwasmcompositor.h" +#include "qwasmintegration.h" #include #include @@ -100,6 +101,17 @@ QImage::Format QWasmScreen::format() const return m_format; } +QDpi QWasmScreen::logicalDpi() const +{ + emscripten::val dpi = emscripten::val::module_property("qtFontDpi"); + if (!dpi.isUndefined()) { + qreal dpiValue = dpi.as(); + return QDpi(dpiValue, dpiValue); + } + const qreal defaultDpi = 96; + return QDpi(defaultDpi, defaultDpi); +} + qreal QWasmScreen::devicePixelRatio() const { // FIXME: The effective device pixel ratio may be different from the diff --git a/src/plugins/platforms/wasm/qwasmscreen.h b/src/plugins/platforms/wasm/qwasmscreen.h index 82d2a83edb..8d0d15f245 100644 --- a/src/plugins/platforms/wasm/qwasmscreen.h +++ b/src/plugins/platforms/wasm/qwasmscreen.h @@ -63,6 +63,7 @@ public: QRect geometry() const override; int depth() const override; QImage::Format format() const override; + QDpi logicalDpi() const override; qreal devicePixelRatio() const override; QString name() const override; QPlatformCursor *cursor() const override; -- cgit v1.2.3 From 3af7b279177f7fb092f0e0fb9ffc8e8d846ed774 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Wed, 30 Jan 2019 13:55:15 +0100 Subject: Fix QWindow::mapToGlobal()/mapFromGlobal() for multi-screen windows Make these functions handle the case where a window spans multiple screens, and high-DPI scaling is enabled, and the local position (in the window) is not on the window primary screen (as returned by QWindow::screen()). This is done by detecting the case, and then calculating the correct position using the native coordinate system. [ChangeLog][QtGui] QWindow::mapToGlobal()/mapFromGlobal() now handle windows spanning screens correctly. Done-with: Friedemann Kleint Task-number: QTBUG-73231 Change-Id: I3c31b741344d9e85e4f5d9e60bae75acce2db741 Reviewed-by: Friedemann Kleint --- src/gui/kernel/qhighdpiscaling.cpp | 42 ++++++++++++++++++++++++++++++++++++++ src/gui/kernel/qhighdpiscaling_p.h | 4 +++- src/gui/kernel/qwindow.cpp | 12 +++++++++-- src/gui/kernel/qwindow_p.h | 2 +- 4 files changed, 56 insertions(+), 4 deletions(-) diff --git a/src/gui/kernel/qhighdpiscaling.cpp b/src/gui/kernel/qhighdpiscaling.cpp index 22e46e0851..4b85973e92 100644 --- a/src/gui/kernel/qhighdpiscaling.cpp +++ b/src/gui/kernel/qhighdpiscaling.cpp @@ -41,7 +41,9 @@ #include "qguiapplication.h" #include "qscreen.h" #include "qplatformintegration.h" +#include "qplatformwindow.h" #include "private/qscreen_p.h" +#include #include @@ -376,6 +378,46 @@ QPoint QHighDpiScaling::mapPositionFromNative(const QPoint &pos, const QPlatform return (pos - topLeft) / scaleFactor + topLeft; } +QPoint QHighDpiScaling::mapPositionToGlobal(const QPoint &pos, const QPoint &windowGlobalPosition, const QWindow *window) +{ + QPoint globalPosCandidate = pos + windowGlobalPosition; + if (QGuiApplicationPrivate::screen_list.size() <= 1) + return globalPosCandidate; + + // The global position may be outside device independent screen geometry + // in cases where a window spans screens. Detect this case and map via + // native coordinates to the correct screen. + auto currentScreen = window->screen(); + if (currentScreen && !currentScreen->geometry().contains(globalPosCandidate)) { + auto nativeGlobalPos = QHighDpi::toNativePixels(globalPosCandidate, currentScreen); + if (auto actualPlatformScreen = currentScreen->handle()->screenForPosition(nativeGlobalPos)) + return QHighDpi::fromNativePixels(nativeGlobalPos, actualPlatformScreen->screen()); + } + + return globalPosCandidate; +} + +QPoint QHighDpiScaling::mapPositionFromGlobal(const QPoint &pos, const QPoint &windowGlobalPosition, const QWindow *window) +{ + QPoint windowPosCandidate = pos - windowGlobalPosition; + if (QGuiApplicationPrivate::screen_list.size() <= 1) + return windowPosCandidate; + + // Device independent global (screen) space may discontiguous when high-dpi scaling + // is active. This means that the normal subtracting of the window global position from the + // position-to-be-mapped may not work in cases where a window spans multiple screens. + // Map both positions to native global space (using the correct screens), subtract there, + // and then map the difference back using the scale factor for the window. + QScreen *posScreen = QGuiApplication::screenAt(pos); + if (posScreen && posScreen != window->screen()) { + QPoint nativePos = QHighDpi::toNativePixels(pos, posScreen); + QPoint windowNativePos = window->handle()->geometry().topLeft(); + return QHighDpi::fromNativeLocalPosition(nativePos - windowNativePos, window); + } + + return windowPosCandidate; +} + qreal QHighDpiScaling::screenSubfactor(const QPlatformScreen *screen) { qreal factor = qreal(1.0); diff --git a/src/gui/kernel/qhighdpiscaling_p.h b/src/gui/kernel/qhighdpiscaling_p.h index 83fc9452c5..28cf7de75b 100644 --- a/src/gui/kernel/qhighdpiscaling_p.h +++ b/src/gui/kernel/qhighdpiscaling_p.h @@ -83,8 +83,10 @@ public: static qreal factor(const QPlatformScreen *platformScreen); static QPoint origin(const QScreen *screen); static QPoint origin(const QPlatformScreen *platformScreen); - static QPoint mapPositionFromNative(const QPoint &pos, const QPlatformScreen *platformScreen); static QPoint mapPositionToNative(const QPoint &pos, const QPlatformScreen *platformScreen); + static QPoint mapPositionFromNative(const QPoint &pos, const QPlatformScreen *platformScreen); + static QPoint mapPositionToGlobal(const QPoint &pos, const QPoint &windowGlobalPosition, const QWindow *window); + static QPoint mapPositionFromGlobal(const QPoint &pos, const QPoint &windowGlobalPosition, const QWindow *window); static QDpi logicalDpi(); private: diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp index 453aa1ed83..bcd8351619 100644 --- a/src/gui/kernel/qwindow.cpp +++ b/src/gui/kernel/qwindow.cpp @@ -1670,9 +1670,9 @@ void QWindow::setGeometry(const QRect &rect) chicken and egg problem here: we cannot convert to native coordinates before we know which screen we are on. */ -QScreen *QWindowPrivate::screenForGeometry(const QRect &newGeometry) +QScreen *QWindowPrivate::screenForGeometry(const QRect &newGeometry) const { - Q_Q(QWindow); + Q_Q(const QWindow); QScreen *currentScreen = q->screen(); QScreen *fallback = currentScreen; QPoint center = newGeometry.center(); @@ -2542,6 +2542,10 @@ QPoint QWindow::mapToGlobal(const QPoint &pos) const && (d->platformWindow->isForeignWindow() || d->platformWindow->isEmbedded())) { return QHighDpi::fromNativeLocalPosition(d->platformWindow->mapToGlobal(QHighDpi::toNativeLocalPosition(pos, this)), this); } + + if (QHighDpiScaling::isActive()) + return QHighDpiScaling::mapPositionToGlobal(pos, d->globalPosition(), this); + return pos + d->globalPosition(); } @@ -2562,6 +2566,10 @@ QPoint QWindow::mapFromGlobal(const QPoint &pos) const && (d->platformWindow->isForeignWindow() || d->platformWindow->isEmbedded())) { return QHighDpi::fromNativeLocalPosition(d->platformWindow->mapFromGlobal(QHighDpi::toNativeLocalPosition(pos, this)), this); } + + if (QHighDpiScaling::isActive()) + return QHighDpiScaling::mapPositionFromGlobal(pos, d->globalPosition(), this); + return pos - d->globalPosition(); } diff --git a/src/gui/kernel/qwindow_p.h b/src/gui/kernel/qwindow_p.h index 5103d97c6d..745890f63f 100644 --- a/src/gui/kernel/qwindow_p.h +++ b/src/gui/kernel/qwindow_p.h @@ -148,7 +148,7 @@ public: void connectToScreen(QScreen *topLevelScreen); void disconnectFromScreen(); void emitScreenChangedRecursion(QScreen *newScreen); - QScreen *screenForGeometry(const QRect &rect); + QScreen *screenForGeometry(const QRect &rect) const; virtual void clearFocusObject(); virtual QRectF closestAcceptableGeometry(const QRectF &rect) const; -- cgit v1.2.3 From d2fd9b1b9818b3ec88487967e010f66e92952f55 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Mon, 6 May 2019 15:06:52 +0200 Subject: Windows QPA: Fix window frame calculation in multi-monitor setups When introducing EnableNonClientDpiScaling() for QTBUG-53255, the window frame calculation was not adapted. That is, window frames were calculated from the style for the primary screen only, causing - minimum size constraints not being calculated correctly for applications on secondary screens when populating the MINMAXINFO structure. - warnings about not being able to apply a geometry when moving fixed size windows across screens. The calculation of the frames for propagating size hints is also no longer required after 3035400f36731c400adb9204b94e9afe346a71b7, which retrieves them from the WM_NCCALCSIZE message; QWindowsWindow::fullFrameMargins() can be used instead. For newly created windows, use the newly added AdjustWindowRectExForDpi() function to calculate the initial frame size. Change QWindowsGeometryHint from a class to a collection of static functions and add overloads to calculate the frame. In checkForScreenChanged(), update the margins until WM_NCCALCSIZE is received. Task-number: QTBUG-67777 Task-number: QTBUG-65580 Task-number: QTBUG-53255 Change-Id: Iff2d382b2b316adec6c1a0622ae8015dba6de371 Reviewed-by: Oliver Wolff Reviewed-by: Andre de la Rocha --- src/plugins/platforms/windows/qwindowscontext.cpp | 4 +- src/plugins/platforms/windows/qwindowscontext.h | 4 + .../platforms/windows/qwindowsintegration.cpp | 3 + src/plugins/platforms/windows/qwindowsscreen.cpp | 6 + src/plugins/platforms/windows/qwindowsscreen.h | 2 + src/plugins/platforms/windows/qwindowswindow.cpp | 156 +++++++++++++++------ src/plugins/platforms/windows/qwindowswindow.h | 31 ++-- 7 files changed, 146 insertions(+), 60 deletions(-) diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp index 6c1f5c8f93..8d1ef9f34a 100644 --- a/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -210,6 +210,7 @@ void QWindowsUser32DLL::init() if (QOperatingSystemVersion::current() >= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, 14393)) { + adjustWindowRectExForDpi = (AdjustWindowRectExForDpi)library.resolve("AdjustWindowRectExForDpi"); enableNonClientDpiScaling = (EnableNonClientDpiScaling)library.resolve("EnableNonClientDpiScaling"); getWindowDpiAwarenessContext = (GetWindowDpiAwarenessContext)library.resolve("GetWindowDpiAwarenessContext"); getAwarenessFromDpiAwarenessContext = (GetAwarenessFromDpiAwarenessContext)library.resolve("GetAwarenessFromDpiAwarenessContext"); @@ -977,7 +978,7 @@ static inline bool resizeOnDpiChanged(const QWindow *w) return result; } -static bool shouldHaveNonClientDpiScaling(const QWindow *window) +bool QWindowsContext::shouldHaveNonClientDpiScaling(const QWindow *window) { return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10 && window->isTopLevel() @@ -1589,6 +1590,7 @@ extern "C" LRESULT QT_WIN_CALLBACK qWindowsWndProc(HWND hwnd, UINT message, WPAR marginsFromRects(ncCalcSizeFrame, rectFromNcCalcSize(message, wParam, lParam, 0)); if (margins.left() >= 0) { if (platformWindow) { + qCDebug(lcQpaWindows) << __FUNCTION__ << "WM_NCCALCSIZE for" << hwnd << margins; platformWindow->setFullFrameMargins(margins); } else { const QSharedPointer ctx = QWindowsContext::instance()->windowCreationContext(); diff --git a/src/plugins/platforms/windows/qwindowscontext.h b/src/plugins/platforms/windows/qwindowscontext.h index fd6c72668c..4908f14629 100644 --- a/src/plugins/platforms/windows/qwindowscontext.h +++ b/src/plugins/platforms/windows/qwindowscontext.h @@ -102,6 +102,7 @@ struct QWindowsUser32DLL typedef BOOL (WINAPI *RemoveClipboardFormatListener)(HWND); typedef BOOL (WINAPI *GetDisplayAutoRotationPreferences)(DWORD *); typedef BOOL (WINAPI *SetDisplayAutoRotationPreferences)(DWORD); + typedef BOOL (WINAPI *AdjustWindowRectExForDpi)(LPRECT,DWORD,BOOL,DWORD,UINT); typedef BOOL (WINAPI *EnableNonClientDpiScaling)(HWND); typedef int (WINAPI *GetWindowDpiAwarenessContext)(HWND); typedef int (WINAPI *GetAwarenessFromDpiAwarenessContext)(int); @@ -131,6 +132,7 @@ struct QWindowsUser32DLL GetDisplayAutoRotationPreferences getDisplayAutoRotationPreferences = nullptr; SetDisplayAutoRotationPreferences setDisplayAutoRotationPreferences = nullptr; + AdjustWindowRectExForDpi adjustWindowRectExForDpi = nullptr; EnableNonClientDpiScaling enableNonClientDpiScaling = nullptr; GetWindowDpiAwarenessContext getWindowDpiAwarenessContext = nullptr; GetAwarenessFromDpiAwarenessContext getAwarenessFromDpiAwarenessContext = nullptr; @@ -201,6 +203,8 @@ public: QWindowsWindow *findPlatformWindowAt(HWND parent, const QPoint &screenPoint, unsigned cwex_flags) const; + static bool shouldHaveNonClientDpiScaling(const QWindow *window); + QWindow *windowUnderMouse() const; void clearWindowUnderMouse(); diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp index 2c90b0484e..5c1fa00088 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.cpp +++ b/src/plugins/platforms/windows/qwindowsintegration.cpp @@ -353,6 +353,9 @@ QPlatformWindow *QWindowsIntegration::createPlatformWindow(QWindow *window) cons QWindowsWindow *result = createPlatformWindowHelper(window, obtained); Q_ASSERT(result); + if (window->isTopLevel() && !QWindowsContext::shouldHaveNonClientDpiScaling(window)) + result->setFlag(QWindowsWindow::DisableNonClientScaling); + if (QWindowsMenuBar *menuBarToBeInstalled = QWindowsMenuBar::menuBarOf(window)) menuBarToBeInstalled->install(result); diff --git a/src/plugins/platforms/windows/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp index 94608bfd82..b28a113ce6 100644 --- a/src/plugins/platforms/windows/qwindowsscreen.cpp +++ b/src/plugins/platforms/windows/qwindowsscreen.cpp @@ -435,6 +435,12 @@ QPlatformScreen::SubpixelAntialiasingType QWindowsScreen::subpixelAntialiasingTy QWindowsScreenManager::QWindowsScreenManager() = default; + +bool QWindowsScreenManager::isSingleScreen() +{ + return QWindowsContext::instance()->screenManager().screens().size() < 2; +} + /*! \brief Triggers synchronization of screens (WM_DISPLAYCHANGE). diff --git a/src/plugins/platforms/windows/qwindowsscreen.h b/src/plugins/platforms/windows/qwindowsscreen.h index 824bcb1ad6..8ad012512e 100644 --- a/src/plugins/platforms/windows/qwindowsscreen.h +++ b/src/plugins/platforms/windows/qwindowsscreen.h @@ -138,6 +138,8 @@ public: const QWindowsScreen *screenAtDp(const QPoint &p) const; const QWindowsScreen *screenForHwnd(HWND hwnd) const; + static bool isSingleScreen(); + private: void removeScreen(int index); diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index f538b6bad7..841d3dccdc 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -870,35 +870,78 @@ static QSize toNativeSizeConstrained(QSize dip, const QWindow *w) \ingroup qt-lighthouse-win */ -QWindowsGeometryHint::QWindowsGeometryHint(const QWindow *w, const QMargins &cm) : - minimumSize(toNativeSizeConstrained(w->minimumSize(), w)), - maximumSize(toNativeSizeConstrained(w->maximumSize(), w)), - customMargins(cm) +QMargins QWindowsGeometryHint::frameOnPrimaryScreen(DWORD style, DWORD exStyle) { + RECT rect = {0,0,0,0}; + style &= ~DWORD(WS_OVERLAPPED); // Not permitted, see docs. + if (AdjustWindowRectEx(&rect, style, FALSE, exStyle) == FALSE) + qErrnoWarning("%s: AdjustWindowRectEx failed", __FUNCTION__); + const QMargins result(qAbs(rect.left), qAbs(rect.top), + qAbs(rect.right), qAbs(rect.bottom)); + qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << " style=" + << showbase << hex << style << " exStyle=" << exStyle << dec << noshowbase + << ' ' << rect << ' ' << result; + return result; } -bool QWindowsGeometryHint::validSize(const QSize &s) const +QMargins QWindowsGeometryHint::frameOnPrimaryScreen(HWND hwnd) { - const int width = s.width(); - const int height = s.height(); - return width >= minimumSize.width() && width <= maximumSize.width() - && height >= minimumSize.height() && height <= maximumSize.height(); + return frameOnPrimaryScreen(DWORD(GetWindowLongPtr(hwnd, GWL_STYLE)), + DWORD(GetWindowLongPtr(hwnd, GWL_EXSTYLE))); } -QMargins QWindowsGeometryHint::frame(DWORD style, DWORD exStyle) +QMargins QWindowsGeometryHint::frame(DWORD style, DWORD exStyle, qreal dpi) { + if (QWindowsContext::user32dll.adjustWindowRectExForDpi == nullptr) + return frameOnPrimaryScreen(style, exStyle); RECT rect = {0,0,0,0}; - style &= ~(WS_OVERLAPPED); // Not permitted, see docs. - if (!AdjustWindowRectEx(&rect, style, FALSE, exStyle)) - qErrnoWarning("%s: AdjustWindowRectEx failed", __FUNCTION__); + style &= ~DWORD(WS_OVERLAPPED); // Not permitted, see docs. + if (QWindowsContext::user32dll.adjustWindowRectExForDpi(&rect, style, FALSE, exStyle, + unsigned(qRound(dpi))) == FALSE) { + qErrnoWarning("%s: AdjustWindowRectExForDpi failed", __FUNCTION__); + } const QMargins result(qAbs(rect.left), qAbs(rect.top), qAbs(rect.right), qAbs(rect.bottom)); qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << " style=" << showbase << hex << style << " exStyle=" << exStyle << dec << noshowbase + << " dpi=" << dpi << ' ' << rect << ' ' << result; return result; } +QMargins QWindowsGeometryHint::frame(HWND hwnd, DWORD style, DWORD exStyle) +{ + if (QWindowsScreenManager::isSingleScreen()) + return frameOnPrimaryScreen(style, exStyle); + auto screenManager = QWindowsContext::instance()->screenManager(); + auto screen = screenManager.screenForHwnd(hwnd); + if (!screen) + screen = screenManager.screens().value(0); + const auto dpi = screen ? screen->logicalDpi().first : qreal(96); + return frame(style, exStyle, dpi); +} + +// For newly created windows. +QMargins QWindowsGeometryHint::frame(const QWindow *w, const QRect &geometry, + DWORD style, DWORD exStyle) +{ + if (!w->isTopLevel() || w->flags().testFlag(Qt::FramelessWindowHint)) + return {}; + if (!QWindowsContext::user32dll.adjustWindowRectExForDpi + || QWindowsScreenManager::isSingleScreen() + || !QWindowsContext::shouldHaveNonClientDpiScaling(w)) { + return frameOnPrimaryScreen(style, exStyle); + } + qreal dpi = 96; + auto screenManager = QWindowsContext::instance()->screenManager(); + auto screen = screenManager.screenAtDp(geometry.center()); + if (!screen) + screen = screenManager.screens().value(0); + if (screen) + dpi = screen->logicalDpi().first; + return QWindowsGeometryHint::frame(style, exStyle, dpi); +} + bool QWindowsGeometryHint::handleCalculateSize(const QMargins &customMargins, const MSG &msg, LRESULT *result) { // NCCALCSIZE_PARAMS structure if wParam==TRUE @@ -918,36 +961,50 @@ bool QWindowsGeometryHint::handleCalculateSize(const QMargins &customMargins, co return true; } -void QWindowsGeometryHint::applyToMinMaxInfo(HWND hwnd, MINMAXINFO *mmi) const +void QWindowsGeometryHint::frameSizeConstraints(const QWindow *w, const QMargins &margins, + QSize *minimumSize, QSize *maximumSize) { - return applyToMinMaxInfo(DWORD(GetWindowLong(hwnd, GWL_STYLE)), - DWORD(GetWindowLong(hwnd, GWL_EXSTYLE)), mmi); + *minimumSize = toNativeSizeConstrained(w->minimumSize(), w); + *maximumSize = toNativeSizeConstrained(w->maximumSize(), w); + + const int maximumWidth = qMax(maximumSize->width(), minimumSize->width()); + const int maximumHeight = qMax(maximumSize->height(), minimumSize->height()); + const int frameWidth = margins.left() + margins.right(); + const int frameHeight = margins.top() + margins.bottom(); + + if (minimumSize->width() > 0) + minimumSize->rwidth() += frameWidth; + if (minimumSize->height() > 0) + minimumSize->rheight() += frameHeight; + if (maximumWidth < QWINDOWSIZE_MAX) + maximumSize->setWidth(maximumWidth + frameWidth); + if (maximumHeight < QWINDOWSIZE_MAX) + maximumSize->setHeight(maximumHeight + frameHeight); } -void QWindowsGeometryHint::applyToMinMaxInfo(DWORD style, DWORD exStyle, MINMAXINFO *mmi) const +void QWindowsGeometryHint::applyToMinMaxInfo(const QWindow *w, + const QMargins &margins, + MINMAXINFO *mmi) { + QSize minimumSize; + QSize maximumSize; + frameSizeConstraints(w, margins, &minimumSize, &maximumSize); qCDebug(lcQpaWindows).nospace() << '>' << __FUNCTION__ << '<' << " min=" << minimumSize.width() << ',' << minimumSize.height() << " max=" << maximumSize.width() << ',' << maximumSize.height() + << " margins=" << margins << " in " << *mmi; - const QMargins margins = QWindowsGeometryHint::frame(style, exStyle); - const int frameWidth = margins.left() + margins.right() + customMargins.left() + customMargins.right(); - const int frameHeight = margins.top() + margins.bottom() + customMargins.top() + customMargins.bottom(); if (minimumSize.width() > 0) - mmi->ptMinTrackSize.x = minimumSize.width() + frameWidth; + mmi->ptMinTrackSize.x = minimumSize.width(); if (minimumSize.height() > 0) - mmi->ptMinTrackSize.y = minimumSize.height() + frameHeight; + mmi->ptMinTrackSize.y = minimumSize.height(); - const int maximumWidth = qMax(maximumSize.width(), minimumSize.width()); - const int maximumHeight = qMax(maximumSize.height(), minimumSize.height()); - if (maximumWidth < QWINDOWSIZE_MAX) - mmi->ptMaxTrackSize.x = maximumWidth + frameWidth; - if (maximumHeight < QWINDOWSIZE_MAX) - mmi->ptMaxTrackSize.y = maximumHeight + frameHeight; - qCDebug(lcQpaWindows).nospace() << '<' << __FUNCTION__ - << " frame=" << margins << ' ' << frameWidth << ',' << frameHeight - << " out " << *mmi; + if (maximumSize.width() < QWINDOWSIZE_MAX) + mmi->ptMaxTrackSize.x = maximumSize.width(); + if (maximumSize.height() < QWINDOWSIZE_MAX) + mmi->ptMaxTrackSize.y = maximumSize.height(); + qCDebug(lcQpaWindows).nospace() << '<' << __FUNCTION__ << " out " << *mmi; } bool QWindowsGeometryHint::positionIncludesFrame(const QWindow *w) @@ -1007,7 +1064,7 @@ QRect QWindowsBaseWindow::geometry_sys() const QMargins QWindowsBaseWindow::frameMargins_sys() const { - return QWindowsGeometryHint::frame(style(), exStyle()); + return QWindowsGeometryHint::frame(handle(), style(), exStyle()); } void QWindowsBaseWindow::hide_sys() // Normal hide, do not activate other windows. @@ -1133,11 +1190,12 @@ void QWindowsForeignWindow::setVisible(bool visible) QWindowCreationContext::QWindowCreationContext(const QWindow *w, const QRect &geometryIn, const QRect &geometry, const QMargins &cm, - DWORD style_, DWORD exStyle_) : - geometryHint(w, cm), window(w), style(style_), exStyle(exStyle_), + DWORD style, DWORD exStyle) : + window(w), requestedGeometryIn(geometryIn), requestedGeometry(geometry), obtainedGeometry(geometry), - margins(QWindowsGeometryHint::frame(style, exStyle)), customMargins(cm) + margins(QWindowsGeometryHint::frame(w, geometry, style, exStyle)), + customMargins(cm) { // Geometry of toplevels does not consider window frames. // TODO: No concept of WA_wasMoved yet that would indicate a @@ -1166,8 +1224,12 @@ QWindowCreationContext::QWindowCreationContext(const QWindow *w, << " pos incl. frame=" << QWindowsGeometryHint::positionIncludesFrame(w) << " frame=" << frameWidth << 'x' << frameHeight << '+' << frameX << '+' << frameY - << " min=" << geometryHint.minimumSize << " max=" << geometryHint.maximumSize - << " custom margins=" << customMargins; + << " margins=" << margins << " custom margins=" << customMargins; +} + +void QWindowCreationContext::applyToMinMaxInfo(MINMAXINFO *mmi) const +{ + QWindowsGeometryHint::applyToMinMaxInfo(window, margins + customMargins, mmi); } /*! @@ -1682,7 +1744,9 @@ QRect QWindowsWindow::normalGeometry() const const bool fakeFullScreen = m_savedFrameGeometry.isValid() && (window()->windowStates() & Qt::WindowFullScreen); const QRect frame = fakeFullScreen ? m_savedFrameGeometry : normalFrameGeometry(m_data.hwnd); - const QMargins margins = fakeFullScreen ? QWindowsGeometryHint::frame(m_savedStyle, 0) : fullFrameMargins(); + const QMargins margins = fakeFullScreen + ? QWindowsGeometryHint::frame(handle(), m_savedStyle, 0) + : fullFrameMargins(); return frame.isValid() ? frame.marginsRemoved(margins) : frame; } @@ -1809,6 +1873,7 @@ void QWindowsWindow::checkForScreenChanged() qCDebug(lcQpaWindows).noquote().nospace() << __FUNCTION__ << ' ' << window() << " \"" << currentScreen->name() << "\"->\"" << newScreen->name() << '"'; + updateFullFrameMargins(); setFlag(SynchronousGeometryChangeEvent); QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->screen()); } @@ -2280,6 +2345,15 @@ void QWindowsWindow::setFullFrameMargins(const QMargins &newMargins) } } +void QWindowsWindow::updateFullFrameMargins() +{ + // Normally obtained from WM_NCCALCSIZE + const auto systemMargins = testFlag(DisableNonClientScaling) + ? QWindowsGeometryHint::frameOnPrimaryScreen(m_data.hwnd) + : frameMargins_sys(); + setFullFrameMargins(systemMargins + m_data.customMargins); +} + QMargins QWindowsWindow::frameMargins() const { QMargins result = fullFrameMargins(); @@ -2490,10 +2564,8 @@ void QWindowsWindow::getSizeHints(MINMAXINFO *mmi) const { // We don't apply the min/max size hint as we change the dpi, because we did not adjust the // QScreen of the window yet so we don't have the min/max with the right ratio - if (!testFlag(QWindowsWindow::WithinDpiChanged)) { - const QWindowsGeometryHint hint(window(), m_data.customMargins); - hint.applyToMinMaxInfo(m_data.hwnd, mmi); - } + if (!testFlag(QWindowsWindow::WithinDpiChanged)) + QWindowsGeometryHint::applyToMinMaxInfo(window(), fullFrameMargins(), mmi); // This block fixes QTBUG-8361, QTBUG-4362: Frameless/title-less windows shouldn't cover the // taskbar when maximized diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h index 2675990cf1..5e44511e5d 100644 --- a/src/plugins/platforms/windows/qwindowswindow.h +++ b/src/plugins/platforms/windows/qwindowswindow.h @@ -59,24 +59,23 @@ class QDebug; struct QWindowsGeometryHint { - QWindowsGeometryHint() = default; - explicit QWindowsGeometryHint(const QWindow *w, const QMargins &customMargins); - static QMargins frame(DWORD style, DWORD exStyle); + static QMargins frameOnPrimaryScreen(DWORD style, DWORD exStyle); + static QMargins frameOnPrimaryScreen(HWND hwnd); + static QMargins frame(DWORD style, DWORD exStyle, qreal dpi); + static QMargins frame(HWND hwnd, DWORD style, DWORD exStyle); + static QMargins frame(const QWindow *w, const QRect &geometry, + DWORD style, DWORD exStyle); static bool handleCalculateSize(const QMargins &customMargins, const MSG &msg, LRESULT *result); - void applyToMinMaxInfo(DWORD style, DWORD exStyle, MINMAXINFO *mmi) const; - void applyToMinMaxInfo(HWND hwnd, MINMAXINFO *mmi) const; - bool validSize(const QSize &s) const; - + static void applyToMinMaxInfo(const QWindow *w, const QMargins &margins, + MINMAXINFO *mmi); + static void frameSizeConstraints(const QWindow *w, const QMargins &margins, + QSize *minimumSize, QSize *maximumSize); static inline QPoint mapToGlobal(HWND hwnd, const QPoint &); static inline QPoint mapToGlobal(const QWindow *w, const QPoint &); static inline QPoint mapFromGlobal(const HWND hwnd, const QPoint &); static inline QPoint mapFromGlobal(const QWindow *w, const QPoint &); static bool positionIncludesFrame(const QWindow *w); - - QSize minimumSize; - QSize maximumSize; - QMargins customMargins; }; struct QWindowCreationContext @@ -85,13 +84,9 @@ struct QWindowCreationContext const QRect &geometryIn, const QRect &geometry, const QMargins &customMargins, DWORD style, DWORD exStyle); - void applyToMinMaxInfo(MINMAXINFO *mmi) const - { geometryHint.applyToMinMaxInfo(style, exStyle, mmi); } + void applyToMinMaxInfo(MINMAXINFO *mmi) const; - QWindowsGeometryHint geometryHint; const QWindow *window; - DWORD style; - DWORD exStyle; QRect requestedGeometryIn; // QWindow scaled QRect requestedGeometry; // after QPlatformWindow::initialGeometry() QRect obtainedGeometry; @@ -221,7 +216,8 @@ public: HasBorderInFullScreen = 0x200000, WithinDpiChanged = 0x400000, VulkanSurface = 0x800000, - ResizeMoveActive = 0x1000000 + ResizeMoveActive = 0x1000000, + DisableNonClientScaling = 0x2000000 }; QWindowsWindow(QWindow *window, const QWindowsWindowData &data); @@ -262,6 +258,7 @@ public: QMargins frameMargins() const override; QMargins fullFrameMargins() const override; void setFullFrameMargins(const QMargins &newMargins); + void updateFullFrameMargins(); void setOpacity(qreal level) override; void setMask(const QRegion ®ion) override; -- cgit v1.2.3 From 7eed1e40d4d3b6a066bac52995eed7e75d17de2d Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Wed, 8 May 2019 13:36:39 +0200 Subject: Windows QPA: Fix resize loops when moving fixed size windows between screens Postpone the screen change until the DPI changed event in case a move between screens with different DPI is detected. Task-number: QTBUG-65580 Change-Id: I356f144b243d7d1ce7feabf0434c3f534b903965 Reviewed-by: Oliver Wolff Reviewed-by: Andre de la Rocha --- src/plugins/platforms/windows/qwindowscontext.cpp | 29 ++++++++++++------- src/plugins/platforms/windows/qwindowswindow.cpp | 35 ++++++++++++++++------- src/plugins/platforms/windows/qwindowswindow.h | 3 +- 3 files changed, 44 insertions(+), 23 deletions(-) diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp index 8d1ef9f34a..281f18af5b 100644 --- a/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -1322,17 +1322,24 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, #endif } break; case QtWindows::DpiChangedEvent: { - if (!resizeOnDpiChanged(platformWindow->window())) - return false; - platformWindow->setFlag(QWindowsWindow::WithinDpiChanged); - const RECT *prcNewWindow = reinterpret_cast(lParam); - qCDebug(lcQpaWindows) << __FUNCTION__ << "WM_DPICHANGED" - << platformWindow->window() << *prcNewWindow; - SetWindowPos(hwnd, nullptr, prcNewWindow->left, prcNewWindow->top, - prcNewWindow->right - prcNewWindow->left, - prcNewWindow->bottom - prcNewWindow->top, SWP_NOZORDER | SWP_NOACTIVATE); - platformWindow->clearFlag(QWindowsWindow::WithinDpiChanged); - return true; + // Try to apply the suggested size first and then notify ScreenChanged + // so that the resize event sent from QGuiApplication incorporates it + // WM_DPICHANGED is sent with a size that avoids resize loops (by + // snapping back to the previous screen, see QTBUG-65580). + const bool doResize = resizeOnDpiChanged(platformWindow->window()); + if (doResize) { + platformWindow->setFlag(QWindowsWindow::WithinDpiChanged); + platformWindow->updateFullFrameMargins(); + const auto prcNewWindow = reinterpret_cast(lParam); + qCDebug(lcQpaWindows) << __FUNCTION__ << "WM_DPICHANGED" + << platformWindow->window() << *prcNewWindow; + SetWindowPos(hwnd, nullptr, prcNewWindow->left, prcNewWindow->top, + prcNewWindow->right - prcNewWindow->left, + prcNewWindow->bottom - prcNewWindow->top, SWP_NOZORDER | SWP_NOACTIVATE); + platformWindow->clearFlag(QWindowsWindow::WithinDpiChanged); + } + platformWindow->checkForScreenChanged(QWindowsWindow::FromDpiChange); + return doResize; } #if QT_CONFIG(sessionmanager) case QtWindows::QueryEndSessionApplicationEvent: { diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index 841d3dccdc..3bf424d0ac 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -1861,28 +1861,41 @@ void QWindowsWindow::handleResized(int wParam) } } -void QWindowsWindow::checkForScreenChanged() +static inline bool equalDpi(const QDpi &d1, const QDpi &d2) { - if (parent()) + return qFuzzyCompare(d1.first, d2.first) && qFuzzyCompare(d1.second, d2.second); +} + +void QWindowsWindow::checkForScreenChanged(ScreenChangeMode mode) +{ + if (parent() || QWindowsScreenManager::isSingleScreen()) return; QPlatformScreen *currentScreen = screen(); - const auto &screenManager = QWindowsContext::instance()->screenManager(); - const QWindowsScreen *newScreen = screenManager.screenForHwnd(m_data.hwnd); - if (newScreen != nullptr && newScreen != currentScreen) { - qCDebug(lcQpaWindows).noquote().nospace() << __FUNCTION__ - << ' ' << window() << " \"" << currentScreen->name() - << "\"->\"" << newScreen->name() << '"'; - updateFullFrameMargins(); - setFlag(SynchronousGeometryChangeEvent); - QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->screen()); + const QWindowsScreen *newScreen = + QWindowsContext::instance()->screenManager().screenForHwnd(m_data.hwnd); + if (newScreen == nullptr || newScreen == currentScreen) + return; + // For screens with different DPI: postpone until WM_DPICHANGE + if (mode == FromGeometryChange + && !equalDpi(currentScreen->logicalDpi(), newScreen->logicalDpi())) { + return; } + qCDebug(lcQpaWindows).noquote().nospace() << __FUNCTION__ + << ' ' << window() << " \"" << currentScreen->name() + << "\"->\"" << newScreen->name() << '"'; + if (mode == FromGeometryChange) + setFlag(SynchronousGeometryChangeEvent); + updateFullFrameMargins(); + QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->screen()); } void QWindowsWindow::handleGeometryChange() { const QRect previousGeometry = m_data.geometry; m_data.geometry = geometry_sys(); + if (testFlag(WithinDpiChanged)) + return; // QGuiApplication will send resize QWindowSystemInterface::handleGeometryChange(window(), m_data.geometry); // QTBUG-32121: OpenGL/normal windows (with exception of ANGLE) do not receive // expose events when shrinking, synthesize. diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h index 5e44511e5d..ce67e46df3 100644 --- a/src/plugins/platforms/windows/qwindowswindow.h +++ b/src/plugins/platforms/windows/qwindowswindow.h @@ -334,7 +334,8 @@ public: void alertWindow(int durationMs = 0); void stopAlertWindow(); - void checkForScreenChanged(); + enum ScreenChangeMode { FromGeometryChange, FromDpiChange }; + void checkForScreenChanged(ScreenChangeMode mode = FromGeometryChange); static void setTouchWindowTouchTypeStatic(QWindow *window, QWindowsWindowFunctions::TouchWindowTouchTypes touchTypes); void registerTouchWindow(QWindowsWindowFunctions::TouchWindowTouchTypes touchTypes = QWindowsWindowFunctions::NormalTouch); -- cgit v1.2.3 From 09504d484c258eafb10ac12eeda1becda5b985c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Fri, 10 May 2019 16:06:47 +0200 Subject: macOS: Guard against display on non-main threads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AppKit will in some cases ask our view to display on secondary threads if we call APIs that are only supposed to be called on the main thread, such as -[NSOpenGLContext setView:] or -[NSOpenGLContext update]. Forwarding this display-request is bad, as QtGui expects all window system events to come on the main thread, and we can easily deadlock client code such as the Qt Quick threaded renderer. Change-Id: I1daeabf1dca6ca8ba908d3998b444a2089681e3a Reviewed-by: Morten Johan Sørvig Reviewed-by: Tor Arne Vestbø --- src/plugins/platforms/cocoa/qnsview_drawing.mm | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/plugins/platforms/cocoa/qnsview_drawing.mm b/src/plugins/platforms/cocoa/qnsview_drawing.mm index 3690d6ebb4..116a9a73df 100644 --- a/src/plugins/platforms/cocoa/qnsview_drawing.mm +++ b/src/plugins/platforms/cocoa/qnsview_drawing.mm @@ -189,6 +189,15 @@ - (void)displayLayer:(CALayer *)layer { + if (!NSThread.isMainThread) { + // Qt is calling AppKit APIs such as -[NSOpenGLContext setView:] on secondary threads, + // which we shouldn't do. This may result in AppKit (wrongly) triggering a display on + // the thread where we made the call, so block it here and defer to the main thread. + qCWarning(lcQpaDrawing) << "Display non non-main thread! Deferring to main thread"; + dispatch_async(dispatch_get_main_queue(), ^{ self.needsDisplay = YES; }); + return; + } + Q_ASSERT(layer == self.layer); if (!m_platformWindow) -- cgit v1.2.3 From 871b65ab1023d7a9c5e1582816bd038c93f3a86c Mon Sep 17 00:00:00 2001 From: Joerg Bornemann Date: Wed, 8 May 2019 11:32:48 +0200 Subject: Add the c++latest CONFIG value to select the latest C++ standard [ChangeLog][qmake] The CONFIG value c++latest was added to select the latest C++ standard the currently used toolchain supports. Task-number: QTBUG-75653 Change-Id: I22ddc9d293109d99e652b7ccb19d7226fca4716d Reviewed-by: Thiago Macieira --- mkspecs/features/default_post.prf | 1 + qmake/doc/src/qmake-manual.qdoc | 3 +++ 2 files changed, 4 insertions(+) diff --git a/mkspecs/features/default_post.prf b/mkspecs/features/default_post.prf index 9df99b8648..0e41b825ec 100644 --- a/mkspecs/features/default_post.prf +++ b/mkspecs/features/default_post.prf @@ -120,6 +120,7 @@ breakpad { } c++17: CONFIG += c++1z +c++latest: CONFIG *= c++2a c++1z c++14 c++11 !c++11:!c++14:!c++1z:!c++2a { # Qt requires C++11 since 5.7, check if we need to force a compiler option diff --git a/qmake/doc/src/qmake-manual.qdoc b/qmake/doc/src/qmake-manual.qdoc index 27f45089d1..48ca96ae92 100644 --- a/qmake/doc/src/qmake-manual.qdoc +++ b/qmake/doc/src/qmake-manual.qdoc @@ -977,6 +977,9 @@ \row \li c++2a \li C++2a support is enabled. This option has no effect if the compiler does not support C++2a, or can't select the C++ standard. By default, support is disabled. + \row \li c++latest \li Support for the latest C++ language standard is + enabled that is supported by the compiler. By default, this option is + disabled. \row \li strict_c++ \li Disables support for C++ compiler extensions. By default, they are enabled. \row \li depend_includepath \li Appending the value of INCLUDEPATH to -- cgit v1.2.3 From 1e5deb06416b6efc33a2009d9678fd8f743c5ce7 Mon Sep 17 00:00:00 2001 From: Michal Klocek Date: Thu, 2 May 2019 18:02:45 +0200 Subject: Add 'well-formated' JSON string values Add support for surrogate code points U+D800 through U+DFFF, represent them with JSON escape sequences. https://github.com/tc39/proposal-well-formed-stringify Change-Id: I84fea53a8ef400beebefdba10ea82dc510fe7dda Reviewed-by: Thiago Macieira --- src/corelib/serialization/qjsonwriter.cpp | 12 ++++++---- .../auto/corelib/serialization/json/tst_qtjson.cpp | 27 +++++++++++++++++++++- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/corelib/serialization/qjsonwriter.cpp b/src/corelib/serialization/qjsonwriter.cpp index 12ce20ef09..5b246837d2 100644 --- a/src/corelib/serialization/qjsonwriter.cpp +++ b/src/corelib/serialization/qjsonwriter.cpp @@ -58,7 +58,6 @@ static inline uchar hexdig(uint u) static QByteArray escapedString(const QString &s) { - const uchar replacement = '?'; QByteArray ba(s.length(), Qt::Uninitialized); uchar *cursor = reinterpret_cast(const_cast(ba.constData())); @@ -111,9 +110,14 @@ static QByteArray escapedString(const QString &s) } else { *cursor++ = (uchar)u; } - } else { - if (QUtf8Functions::toUtf8(u, cursor, src, end) < 0) - *cursor++ = replacement; + } else if (QUtf8Functions::toUtf8(u, cursor, src, end) < 0) { + // failed to get valid utf8 use JSON escape sequence + *cursor++ = '\\'; + *cursor++ = 'u'; + *cursor++ = hexdig(u>>12 & 0x0f); + *cursor++ = hexdig(u>>8 & 0x0f); + *cursor++ = hexdig(u>>4 & 0x0f); + *cursor++ = hexdig(u & 0x0f); } } diff --git a/tests/auto/corelib/serialization/json/tst_qtjson.cpp b/tests/auto/corelib/serialization/json/tst_qtjson.cpp index 083e78375a..8907704a33 100644 --- a/tests/auto/corelib/serialization/json/tst_qtjson.cpp +++ b/tests/auto/corelib/serialization/json/tst_qtjson.cpp @@ -163,7 +163,8 @@ private Q_SLOTS: void streamSerializationQJsonValue(); void streamSerializationQJsonValueEmpty(); void streamVariantSerialization(); - + void escapeSurrogateCodePoints_data(); + void escapeSurrogateCodePoints(); private: QString testDataDir; }; @@ -3093,6 +3094,9 @@ void tst_QtJson::streamSerializationQJsonValue_data() QTest::newRow("string") << QJsonValue{QStringLiteral("bum")}; QTest::newRow("array") << QJsonValue{QJsonArray{12,1,5,6,7}}; QTest::newRow("object") << QJsonValue{QJsonObject{{"foo", 665}, {"bar", 666}}}; + // test json escape sequence + QTest::newRow("array with 0xD800") << QJsonValue(QJsonArray{QString(0xD800)}); + QTest::newRow("array with 0xDF06,0xD834") << QJsonValue(QJsonArray{QString(0xDF06).append(0xD834)}); } void tst_QtJson::streamSerializationQJsonValue() @@ -3181,5 +3185,26 @@ void tst_QtJson::streamVariantSerialization() } } +void tst_QtJson::escapeSurrogateCodePoints_data() +{ + QTest::addColumn("str"); + QTest::addColumn("escStr"); + QTest::newRow("0xD800") << QString(0xD800) << QByteArray("\\ud800"); + QTest::newRow("0xDF06,0xD834") << QString(0xDF06).append(0xD834) << QByteArray("\\udf06\\ud834"); +} + +void tst_QtJson::escapeSurrogateCodePoints() +{ + QFETCH(QString, str); + QFETCH(QByteArray, escStr); + QJsonArray array; + array.append(str); + QByteArray buffer; + QDataStream save(&buffer, QIODevice::WriteOnly); + save << array; + // verify the buffer has escaped values + QVERIFY(buffer.contains(escStr)); +} + QTEST_MAIN(tst_QtJson) #include "tst_qtjson.moc" -- cgit v1.2.3