Merge "Merge remote-tracking branch 'origin/5.13' into dev" into refs/staging/dev

This commit is contained in:
Liang Qi 2019-05-15 06:08:37 +00:00 committed by The Qt Project
commit 7bf90b0dde
50 changed files with 768 additions and 332 deletions

View File

@ -69,11 +69,11 @@ int main(int argc, char *argv[])
QSurfaceFormat fmt; QSurfaceFormat fmt;
fmt.setDepthBufferSize(24); 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) { if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) {
qDebug("Requesting 3.3 compatibility context"); qDebug("Requesting 3.3 core context");
fmt.setVersion(3, 3); fmt.setVersion(3, 3);
fmt.setProfile(QSurfaceFormat::CompatibilityProfile); fmt.setProfile(QSurfaceFormat::CoreProfile);
} else { } else {
qDebug("Requesting 3.0 context"); qDebug("Requesting 3.0 context");
fmt.setVersion(3, 0); fmt.setVersion(3, 0);

View File

@ -211,10 +211,10 @@ CMAKE_INTERFACE_QT5_MODULE_DEPS = $$join(aux_lib_deps, ";")
mac { mac {
!isEmpty(CMAKE_STATIC_TYPE) { !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_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 CMAKE_PRL_FILE_LOCATION_RELEASE = lib$${CMAKE_QT_STEM}.prl
} else { } else {
qt_framework { qt_framework {
@ -222,7 +222,7 @@ mac {
CMAKE_LIB_FILE_LOCATION_RELEASE = $${CMAKE_QT_STEM}.framework/$${CMAKE_QT_STEM} CMAKE_LIB_FILE_LOCATION_RELEASE = $${CMAKE_QT_STEM}.framework/$${CMAKE_QT_STEM}
CMAKE_BUILD_IS_FRAMEWORK = "true" CMAKE_BUILD_IS_FRAMEWORK = "true"
} else { } 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 CMAKE_LIB_FILE_LOCATION_RELEASE = lib$${CMAKE_QT_STEM}.$$eval(QT.$${MODULE}.VERSION).dylib
} }
} }

View File

@ -120,6 +120,7 @@ breakpad {
} }
c++17: CONFIG += c++1z c++17: CONFIG += c++1z
c++latest: CONFIG *= c++2a c++1z c++14 c++11
!c++11:!c++14:!c++1z:!c++2a { !c++11:!c++14:!c++1z:!c++2a {
# Qt requires C++11 since 5.7, check if we need to force a compiler option # Qt requires C++11 since 5.7, check if we need to force a compiler option

View File

@ -977,6 +977,9 @@
\row \li c++2a \li C++2a support is enabled. This option has no effect if \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. the compiler does not support C++2a, or can't select the C++ standard.
By default, support is disabled. 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. \row \li strict_c++ \li Disables support for C++ compiler extensions.
By default, they are enabled. By default, they are enabled.
\row \li depend_includepath \li Appending the value of INCLUDEPATH to \row \li depend_includepath \li Appending the value of INCLUDEPATH to

View File

@ -49,6 +49,7 @@ on big endian machines, or a byte-by-byte read if the endianess is unknown.
#include "PMurHash.h" #include "PMurHash.h"
#include <stdint.h>
/* I used ugly type names in the header to avoid potential conflicts with /* 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 * 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 */ /* This CPU does not handle unaligned word access */
/* Consume enough so that the next data byte is word aligned */ /* 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) { if(i && i <= len) {
DOBYTES(i, h1, c, n, ptr, len); DOBYTES(i, h1, c, n, ptr, len);
} }

View File

@ -41,6 +41,7 @@ package org.qtproject.qt5.android;
import android.content.Context; import android.content.Context;
import android.text.TextUtils;
import android.view.Gravity; import android.view.Gravity;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -73,7 +74,7 @@ public class EditContextView extends LinearLayout implements View.OnClickListene
m_buttonId = stringId; m_buttonId = stringId;
setText(stringId); setText(stringId);
setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT)); ViewGroup.LayoutParams.WRAP_CONTENT, 1));
setGravity(Gravity.CENTER); setGravity(Gravity.CENTER);
setTextColor(getResources().getColor(R.color.widget_edittext_dark)); setTextColor(getResources().getColor(R.color.widget_edittext_dark));
EditContextView.this.setBackground(getResources().getDrawable(R.drawable.editbox_background_normal)); 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 hPadding = (int)(16 * scale + 0.5f);
int vPadding = (int)(8 * scale + 0.5f); int vPadding = (int)(8 * scale + 0.5f);
setPadding(hPadding, vPadding, hPadding, vPadding); setPadding(hPadding, vPadding, hPadding, vPadding);
setSingleLine();
setEllipsize(TextUtils.TruncateAt.END);
setOnClickListener(EditContextView.this); setOnClickListener(EditContextView.this);
} }
} }

View File

@ -0,0 +1,35 @@
From e7ff4aa4ef2221aa02d39bdead7f35008016994e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martin=20Storsj=C3=B6?= <martin@martin.st>
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 <stdint.h>
/* 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)

View File

@ -66,6 +66,19 @@ QT_BEGIN_NAMESPACE
\since 5.9 \since 5.9
*/ */
/*!
\macro QT_NO_FLOAT16_OPERATORS
\relates <QFloat16>
\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 {<QFloat16>} and \c{<bitset>} in the same translation unit, which would
otherwise cause a compilation error due to a toolchain bug (see
[QTBUG-72073]).
*/
/*! /*!
\fn bool qIsInf(qfloat16 f) \fn bool qIsInf(qfloat16 f)
\relates <QFloat16> \relates <QFloat16>

View File

@ -106,7 +106,9 @@ private:
Q_CORE_EXPORT static const quint32 shifttable[]; Q_CORE_EXPORT static const quint32 shifttable[];
friend bool qIsNull(qfloat16 f) noexcept; friend bool qIsNull(qfloat16 f) noexcept;
#if !defined(QT_NO_FLOAT16_OPERATORS)
friend qfloat16 operator-(qfloat16 a) noexcept; friend qfloat16 operator-(qfloat16 a) noexcept;
#endif
}; };
Q_DECLARE_TYPEINFO(qfloat16, Q_PRIMITIVE_TYPE); Q_DECLARE_TYPEINFO(qfloat16, Q_PRIMITIVE_TYPE);
@ -191,6 +193,7 @@ inline qfloat16::operator float() const noexcept
} }
#endif #endif
#if !defined(QT_NO_FLOAT16_OPERATORS)
inline qfloat16 operator-(qfloat16 a) noexcept inline qfloat16 operator-(qfloat16 a) noexcept
{ {
qfloat16 f; qfloat16 f;
@ -272,6 +275,7 @@ QF16_MAKE_BOOL_OP_INT(!=)
#undef QF16_MAKE_BOOL_OP_INT #undef QF16_MAKE_BOOL_OP_INT
QT_WARNING_POP QT_WARNING_POP
#endif // QT_NO_FLOAT16_OPERATORS
/*! /*!
\internal \internal

View File

@ -628,7 +628,7 @@ void QTimer::singleShot(int msec, Qt::TimerType timerType, const QObject *receiv
*/ */
/*! /*!
\fn template <typename PointerToMemberFunction> QMetaObject::Connection QTimer::callOnTimeout(const QObject *receiver, PointerToMemberFunction slot, Qt::ConnectionType connectionType = Qt::AutoConnection) \fn template <typename MemberFunction> QMetaObject::Connection QTimer::callOnTimeout(const QObject *receiver, MemberFunction *slot, Qt::ConnectionType connectionType = Qt::AutoConnection)
\since 5.12 \since 5.12
\overload callOnTimeout() \overload callOnTimeout()

View File

@ -100,8 +100,8 @@ public:
QMetaObject::Connection callOnTimeout(Functor slot, Qt::ConnectionType connectionType = Qt::AutoConnection); QMetaObject::Connection callOnTimeout(Functor slot, Qt::ConnectionType connectionType = Qt::AutoConnection);
template <typename Functor> template <typename Functor>
QMetaObject::Connection callOnTimeout(const QObject *context, Functor slot, Qt::ConnectionType connectionType = Qt::AutoConnection); QMetaObject::Connection callOnTimeout(const QObject *context, Functor slot, Qt::ConnectionType connectionType = Qt::AutoConnection);
template <typename PointerToMemberFunction> template <typename MemberFunction>
QMetaObject::Connection callOnTimeout(const QObject *receiver, PointerToMemberFunction slot, Qt::ConnectionType connectionType = Qt::AutoConnection); QMetaObject::Connection callOnTimeout(const QObject *receiver, MemberFunction *slot, Qt::ConnectionType connectionType = Qt::AutoConnection);
#else #else
// singleShot to a QObject slot // singleShot to a QObject slot
template <typename Duration, typename Func1> template <typename Duration, typename Func1>

View File

@ -58,7 +58,6 @@ static inline uchar hexdig(uint u)
static QByteArray escapedString(const QString &s) static QByteArray escapedString(const QString &s)
{ {
const uchar replacement = '?';
QByteArray ba(s.length(), Qt::Uninitialized); QByteArray ba(s.length(), Qt::Uninitialized);
uchar *cursor = reinterpret_cast<uchar *>(const_cast<char *>(ba.constData())); uchar *cursor = reinterpret_cast<uchar *>(const_cast<char *>(ba.constData()));
@ -111,9 +110,14 @@ static QByteArray escapedString(const QString &s)
} else { } else {
*cursor++ = (uchar)u; *cursor++ = (uchar)u;
} }
} else { } else if (QUtf8Functions::toUtf8<QUtf8BaseTraits>(u, cursor, src, end) < 0) {
if (QUtf8Functions::toUtf8<QUtf8BaseTraits>(u, cursor, src, end) < 0) // failed to get valid utf8 use JSON escape sequence
*cursor++ = replacement; *cursor++ = '\\';
*cursor++ = 'u';
*cursor++ = hexdig(u>>12 & 0x0f);
*cursor++ = hexdig(u>>8 & 0x0f);
*cursor++ = hexdig(u>>4 & 0x0f);
*cursor++ = hexdig(u & 0x0f);
} }
} }

View File

@ -65,14 +65,19 @@
\section1 Thread-Safety \section1 Thread-Safety
QSharedPointer and QWeakPointer are thread-safe and operate QSharedPointer and QWeakPointer are reentrant classes. This means that, in
atomically on the pointer value. Different threads can also access general, a given QSharedPointer or QWeakPointer object \b{cannot} be
the QSharedPointer or QWeakPointer pointing to the same object at accessed by multiple threads at the same time without synchronization.
the same time without need for locking mechanisms.
It should be noted that, while the pointer value can be accessed Different QSharedPointer and QWeakPointer objects can safely be accessed
in this manner, QSharedPointer and QWeakPointer provide no by multiple threads at the same time. This includes the case where they
guarantee about the object being pointed to. Thread-safety and 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. reentrancy rules for that object still apply.
\section1 Other Pointer Classes \section1 Other Pointer Classes

View File

@ -29,7 +29,7 @@
world's languages, with the largest and most extensive standard repository of locale data world's languages, with the largest and most extensive standard repository of locale data
available.", available.",
"Homepage": "http://cldr.unicode.org/", "Homepage": "http://cldr.unicode.org/",
"Version": "v34", "Version": "v35.1",
"License": "// as specified in https://spdx.org/licenses/Unicode-DFS-2016.html", "License": "// as specified in https://spdx.org/licenses/Unicode-DFS-2016.html",
"License": "Unicode License Agreement - Data Files and Software (2016)", "License": "Unicode License Agreement - Data Files and Software (2016)",
"LicenseId": "Unicode-DFS-2016", "LicenseId": "Unicode-DFS-2016",

View File

@ -41,7 +41,9 @@
#include "qguiapplication.h" #include "qguiapplication.h"
#include "qscreen.h" #include "qscreen.h"
#include "qplatformintegration.h" #include "qplatformintegration.h"
#include "qplatformwindow.h"
#include "private/qscreen_p.h" #include "private/qscreen_p.h"
#include <private/qguiapplication_p.h>
#include <QtCore/qdebug.h> #include <QtCore/qdebug.h>
@ -376,6 +378,46 @@ QPoint QHighDpiScaling::mapPositionFromNative(const QPoint &pos, const QPlatform
return (pos - topLeft) / scaleFactor + topLeft; 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 QHighDpiScaling::screenSubfactor(const QPlatformScreen *screen)
{ {
qreal factor = qreal(1.0); qreal factor = qreal(1.0);

View File

@ -83,8 +83,10 @@ public:
static qreal factor(const QPlatformScreen *platformScreen); static qreal factor(const QPlatformScreen *platformScreen);
static QPoint origin(const QScreen *screen); static QPoint origin(const QScreen *screen);
static QPoint origin(const QPlatformScreen *platformScreen); 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 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(); static QDpi logicalDpi();
private: private:

View File

@ -1682,9 +1682,9 @@ void QWindow::setGeometry(const QRect &rect)
chicken and egg problem here: we cannot convert to native coordinates chicken and egg problem here: we cannot convert to native coordinates
before we know which screen we are on. 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 *currentScreen = q->screen();
QScreen *fallback = currentScreen; QScreen *fallback = currentScreen;
QPoint center = newGeometry.center(); QPoint center = newGeometry.center();
@ -2559,6 +2559,10 @@ QPoint QWindow::mapToGlobal(const QPoint &pos) const
&& (d->platformWindow->isForeignWindow() || d->platformWindow->isEmbedded())) { && (d->platformWindow->isForeignWindow() || d->platformWindow->isEmbedded())) {
return QHighDpi::fromNativeLocalPosition(d->platformWindow->mapToGlobal(QHighDpi::toNativeLocalPosition(pos, this)), this); 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(); return pos + d->globalPosition();
} }
@ -2579,6 +2583,10 @@ QPoint QWindow::mapFromGlobal(const QPoint &pos) const
&& (d->platformWindow->isForeignWindow() || d->platformWindow->isEmbedded())) { && (d->platformWindow->isForeignWindow() || d->platformWindow->isEmbedded())) {
return QHighDpi::fromNativeLocalPosition(d->platformWindow->mapFromGlobal(QHighDpi::toNativeLocalPosition(pos, this)), this); 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(); return pos - d->globalPosition();
} }

View File

@ -113,7 +113,7 @@ public:
void connectToScreen(QScreen *topLevelScreen); void connectToScreen(QScreen *topLevelScreen);
void disconnectFromScreen(); void disconnectFromScreen();
void emitScreenChangedRecursion(QScreen *newScreen); void emitScreenChangedRecursion(QScreen *newScreen);
QScreen *screenForGeometry(const QRect &rect); QScreen *screenForGeometry(const QRect &rect) const;
void setTransientParent(QWindow *parent); void setTransientParent(QWindow *parent);
virtual void clearFocusObject(); virtual void clearFocusObject();

View File

@ -156,6 +156,17 @@ template<bool RGBA, bool RGBx>
static inline void convertARGBFromARGB32PM_sse4(uint *buffer, const uint *src, int count) static inline void convertARGBFromARGB32PM_sse4(uint *buffer, const uint *src, int count)
{ {
int i = 0; 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 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 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(); const __m128i zero = _mm_setzero_si128();
@ -223,6 +234,13 @@ template<bool RGBA>
static inline void convertARGBFromRGBA64PM_sse4(uint *buffer, const QRgba64 *src, int count) static inline void convertARGBFromRGBA64PM_sse4(uint *buffer, const QRgba64 *src, int count)
{ {
int i = 0; 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 alphaMask = _mm_set1_epi64x(qint64(Q_UINT64_C(0xffff) << 48));
const __m128i alphaMask32 = _mm_set1_epi32(0xff000000); 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); const __m128i rgbaMask = _mm_setr_epi8(2, 1, 0, 3, 6, 5, 4, 7, 10, 9, 8, 11, 14, 13, 12, 15);

View File

@ -6,7 +6,7 @@ CONFIG += static internal_module
DEFINES += QT_NO_CAST_FROM_ASCII DEFINES += QT_NO_CAST_FROM_ASCII
HEADERS += HEADERS += \
qkmsdevice_p.h qkmsdevice_p.h
SOURCES += \ SOURCES += \

View File

@ -791,7 +791,7 @@ void QAndroidInputContext::longPress(int x, int y)
return; return;
} }
QList<QInputMethodEvent::Attribute> imAttributes; QList<QInputMethodEvent::Attribute> 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())); imAttributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Selection, anchor, cursor - anchor, QVariant()));
QInputMethodEvent event(QString(), imAttributes); QInputMethodEvent event(QString(), imAttributes);
QGuiApplication::sendEvent(m_focusObject, &event); QGuiApplication::sendEvent(m_focusObject, &event);

View File

@ -81,6 +81,7 @@ public:
QPlatformTextureList *textures, bool translucentBackground) override; QPlatformTextureList *textures, bool translucentBackground) override;
#endif #endif
QImage toImage() const override;
QPlatformGraphicsBuffer *graphicsBuffer() const override; QPlatformGraphicsBuffer *graphicsBuffer() const override;
private: private:

View File

@ -273,18 +273,6 @@ void QNSWindowBackingStore::redrawRoundedBottomCorners(CGRect windowRect) const
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// https://stackoverflow.com/a/52722575/2761869
template<class R>
struct backwards_t {
R r;
constexpr auto begin() const { using std::rbegin; return rbegin(r); }
constexpr auto begin() { using std::rbegin; return rbegin(r); }
constexpr auto end() const { using std::rend; return rend(r); }
constexpr auto end() { using std::rend; return rend(r); }
};
template<class R>
constexpr backwards_t<R> backwards(R&& r) { return {std::forward<R>(r)}; }
QCALayerBackingStore::QCALayerBackingStore(QWindow *window) QCALayerBackingStore::QCALayerBackingStore(QWindow *window)
: QPlatformBackingStore(window) : QPlatformBackingStore(window)
{ {
@ -534,6 +522,21 @@ void QCALayerBackingStore::composeAndFlush(QWindow *window, const QRegion &regio
} }
#endif #endif
QImage QCALayerBackingStore::toImage() const
{
if (!const_cast<QCALayerBackingStore*>(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 QPlatformGraphicsBuffer *QCALayerBackingStore::graphicsBuffer() const
{ {
return m_buffers.back().get(); return m_buffers.back().get();

View File

@ -176,6 +176,18 @@ T qt_mac_resolveOption(const T &fallback, QWindow *window, const QByteArray &pro
return fallback; return fallback;
} }
// https://stackoverflow.com/a/52722575/2761869
template<class R>
struct backwards_t {
R r;
constexpr auto begin() const { using std::rbegin; return rbegin(r); }
constexpr auto begin() { using std::rbegin; return rbegin(r); }
constexpr auto end() const { using std::rend; return rend(r); }
constexpr auto end() { using std::rend; return rend(r); }
};
template<class R>
constexpr backwards_t<R> backwards(R&& r) { return {std::forward<R>(r)}; }
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
#if !defined(Q_PROCESSOR_X86_64) #if !defined(Q_PROCESSOR_X86_64)

View File

@ -63,7 +63,7 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcQpaWindow, "qt.qpa.window"); Q_LOGGING_CATEGORY(lcQpaWindow, "qt.qpa.window");
Q_LOGGING_CATEGORY(lcQpaDrawing, "qt.qpa.drawing"); Q_LOGGING_CATEGORY(lcQpaDrawing, "qt.qpa.drawing");
Q_LOGGING_CATEGORY(lcQpaMouse, "qt.qpa.input.mouse", QtCriticalMsg); 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 // Conversion Functions

View File

@ -61,8 +61,6 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QCocoaScreen;
class QCocoaIntegration : public QObject, public QPlatformIntegration class QCocoaIntegration : public QObject, public QPlatformIntegration
{ {
Q_OBJECT Q_OBJECT
@ -113,9 +111,6 @@ public:
Qt::KeyboardModifiers queryKeyboardModifiers() const override; Qt::KeyboardModifiers queryKeyboardModifiers() const override;
QList<int> possibleKeys(const QKeyEvent *event) const override; QList<int> possibleKeys(const QKeyEvent *event) const override;
void updateScreens();
QCocoaScreen *screenForNSScreen(NSScreen *nsScreen);
void setToolbar(QWindow *window, NSToolbar *toolbar); void setToolbar(QWindow *window, NSToolbar *toolbar);
NSToolbar *toolbar(QWindow *window) const; NSToolbar *toolbar(QWindow *window) const;
void clearToolbars(); void clearToolbars();
@ -143,8 +138,6 @@ private:
QScopedPointer<QCocoaAccessibility> mAccessibility; QScopedPointer<QCocoaAccessibility> mAccessibility;
#endif #endif
QScopedPointer<QPlatformTheme> mPlatformTheme; QScopedPointer<QPlatformTheme> mPlatformTheme;
QList<QCocoaScreen *> mScreens;
QMacScopedObserver m_screensObserver;
#ifndef QT_NO_CLIPBOARD #ifndef QT_NO_CLIPBOARD
QCocoaClipboard *mCocoaClipboard; QCocoaClipboard *mCocoaClipboard;
#endif #endif

View File

@ -207,9 +207,7 @@ QCocoaIntegration::QCocoaIntegration(const QStringList &paramList)
// which will resolve to an actual value and result in screen invalidation. // which will resolve to an actual value and result in screen invalidation.
cocoaApplication.presentationOptions = NSApplicationPresentationDefault; cocoaApplication.presentationOptions = NSApplicationPresentationDefault;
m_screensObserver = QMacScopedObserver([NSApplication sharedApplication], QCocoaScreen::initializeScreens();
NSApplicationDidChangeScreenParametersNotification, [&]() { updateScreens(); });
updateScreens();
QMacInternalPasteboardMime::initializeMimeTypes(); QMacInternalPasteboardMime::initializeMimeTypes();
QCocoaMimeTypes::initializeMimeTypes(); QCocoaMimeTypes::initializeMimeTypes();
@ -242,10 +240,7 @@ QCocoaIntegration::~QCocoaIntegration()
QMacInternalPasteboardMime::destroyMimeTypes(); QMacInternalPasteboardMime::destroyMimeTypes();
#endif #endif
// Delete screens in reverse order to avoid crash in case of multiple screens QCocoaScreen::cleanupScreens();
while (!mScreens.isEmpty()) {
QWindowSystemInterface::handleScreenRemoved(mScreens.takeLast());
}
clearToolbars(); clearToolbars();
} }
@ -260,88 +255,6 @@ QCocoaIntegration::Options QCocoaIntegration::options() const
return mOptions; return mOptions;
} }
/*!
\brief Synchronizes the screen list, adds new screens, removes deleted ones
*/
void QCocoaIntegration::updateScreens()
{
NSArray<NSScreen *> *scrs = [NSScreen screens];
NSMutableArray<NSScreen *> *screens = [NSMutableArray<NSScreen *> arrayWithArray:scrs];
if ([screens count] == 0)
if ([NSScreen mainScreen])
[screens addObject:[NSScreen mainScreen]];
if ([screens count] == 0)
return;
QSet<QCocoaScreen*> remainingScreens = QSet<QCocoaScreen*>::fromList(mScreens);
QList<QPlatformScreen *> 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 bool QCocoaIntegration::hasCapability(QPlatformIntegration::Capability cap) const
{ {
switch (cap) { switch (cap) {

View File

@ -48,10 +48,14 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QCocoaIntegration;
class QCocoaScreen : public QPlatformScreen class QCocoaScreen : public QPlatformScreen
{ {
public: public:
QCocoaScreen(int screenIndex); static void initializeScreens();
static void cleanupScreens();
~QCocoaScreen(); ~QCocoaScreen();
// ---------------------------------------------------- // ----------------------------------------------------
@ -61,19 +65,18 @@ public:
QRect availableGeometry() const override { return m_availableGeometry; } QRect availableGeometry() const override { return m_availableGeometry; }
int depth() const override { return m_depth; } int depth() const override { return m_depth; }
QImage::Format format() const override { return m_format; } 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; } QSizeF physicalSize() const override { return m_physicalSize; }
QDpi logicalDpi() const override { return m_logicalDpi; } QDpi logicalDpi() const override { return m_logicalDpi; }
qreal refreshRate() const override { return m_refreshRate; } qreal refreshRate() const override { return m_refreshRate; }
QString name() const override { return m_name; } QString name() const override { return m_name; }
QPlatformCursor *cursor() const override { return m_cursor; } QPlatformCursor *cursor() const override { return m_cursor; }
QWindow *topLevelAt(const QPoint &point) const override; QWindow *topLevelAt(const QPoint &point) const override;
QList<QPlatformScreen *> virtualSiblings() const override { return m_siblings; } QList<QPlatformScreen *> virtualSiblings() const override;
QPlatformScreen::SubpixelAntialiasingType subpixelAntialiasingTypeHint() const override; QPlatformScreen::SubpixelAntialiasingType subpixelAntialiasingTypeHint() const override;
// ---------------------------------------------------- // ----------------------------------------------------
// Additional methods
void setVirtualSiblings(const QList<QPlatformScreen *> &siblings) { m_siblings = siblings; }
NSScreen *nativeScreen() const; NSScreen *nativeScreen() const;
void updateProperties(); void updateProperties();
@ -82,14 +85,21 @@ public:
bool isRunningDisplayLink() const; bool isRunningDisplayLink() const;
static QCocoaScreen *primaryScreen(); static QCocoaScreen *primaryScreen();
static QCocoaScreen *get(NSScreen *nsScreen);
static QCocoaScreen *get(CGDirectDisplayID displayId);
static CGPoint mapToNative(const QPointF &pos, QCocoaScreen *screen = QCocoaScreen::primaryScreen()); static CGPoint mapToNative(const QPointF &pos, QCocoaScreen *screen = QCocoaScreen::primaryScreen());
static CGRect mapToNative(const QRectF &rect, QCocoaScreen *screen = QCocoaScreen::primaryScreen()); static CGRect mapToNative(const QRectF &rect, QCocoaScreen *screen = QCocoaScreen::primaryScreen());
static QPointF mapFromNative(CGPoint pos, QCocoaScreen *screen = QCocoaScreen::primaryScreen()); static QPointF mapFromNative(CGPoint pos, QCocoaScreen *screen = QCocoaScreen::primaryScreen());
static QRectF mapFromNative(CGRect rect, QCocoaScreen *screen = QCocoaScreen::primaryScreen()); static QRectF mapFromNative(CGRect rect, QCocoaScreen *screen = QCocoaScreen::primaryScreen());
public: private:
int m_screenIndex; QCocoaScreen(CGDirectDisplayID displayId);
static void add(CGDirectDisplayID displayId);
void remove();
CGDirectDisplayID m_displayId = 0;
QRect m_geometry; QRect m_geometry;
QRect m_availableGeometry; QRect m_availableGeometry;
QDpi m_logicalDpi; QDpi m_logicalDpi;
@ -99,11 +109,13 @@ public:
QImage::Format m_format; QImage::Format m_format;
QSizeF m_physicalSize; QSizeF m_physicalSize;
QCocoaCursor *m_cursor; QCocoaCursor *m_cursor;
QList<QPlatformScreen *> m_siblings; qreal m_devicePixelRatio;
CVDisplayLinkRef m_displayLink = nullptr; CVDisplayLinkRef m_displayLink = nullptr;
dispatch_source_t m_displayLinkSource = nullptr; dispatch_source_t m_displayLinkSource = nullptr;
QAtomicInt m_pendingUpdates; QAtomicInt m_pendingUpdates;
friend QDebug operator<<(QDebug debug, const QCocoaScreen *screen);
}; };
#ifndef QT_NO_DEBUG_STREAM #ifndef QT_NO_DEBUG_STREAM
@ -116,5 +128,4 @@ QT_END_NAMESPACE
@property(readonly) CGDirectDisplayID qt_displayId; @property(readonly) CGDirectDisplayID qt_displayId;
@end @end
#endif #endif // QCOCOASCREEN_H

View File

@ -41,6 +41,7 @@
#include "qcocoawindow.h" #include "qcocoawindow.h"
#include "qcocoahelpers.h" #include "qcocoahelpers.h"
#include "qcocoaintegration.h"
#include <QtCore/qcoreapplication.h> #include <QtCore/qcoreapplication.h>
#include <QtGui/private/qcoregraphics_p.h> #include <QtGui/private/qcoregraphics_p.h>
@ -53,18 +54,99 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QCoreTextFontEngine; void QCocoaScreen::initializeScreens()
class QFontEngineFT; {
uint32_t displayCount = 0;
if (CGGetActiveDisplayList(0, nullptr, &displayCount) != kCGErrorSuccess)
qFatal("Failed to get number of active displays");
QCocoaScreen::QCocoaScreen(int screenIndex) CGDirectDisplayID activeDisplays[displayCount];
: QPlatformScreen(), m_screenIndex(screenIndex), m_refreshRate(60.0) 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);
}
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(); updateProperties();
m_cursor = new QCocoaCursor; m_cursor = new QCocoaCursor;
} }
void QCocoaScreen::cleanupScreens()
{
// Remove screens in reverse order to avoid crash in case of multiple screens
for (QScreen *screen : backwards(QGuiApplication::screens()))
static_cast<QCocoaScreen*>(screen->handle())->remove();
}
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);
}
QCocoaScreen::~QCocoaScreen() QCocoaScreen::~QCocoaScreen()
{ {
Q_ASSERT_X(!screen(), "QCocoaScreen", "QScreen should be deleted first");
delete m_cursor; delete m_cursor;
CVDisplayLinkRelease(m_displayLink); CVDisplayLinkRelease(m_displayLink);
@ -72,17 +154,6 @@ QCocoaScreen::~QCocoaScreen()
dispatch_release(m_displayLinkSource); dispatch_release(m_displayLinkSource);
} }
NSScreen *QCocoaScreen::nativeScreen() const
{
NSArray<NSScreen *> *screens = [NSScreen screens];
// Stale reference, screen configuration has changed
if (m_screenIndex < 0 || (NSUInteger)m_screenIndex >= [screens count])
return nil;
return [screens objectAtIndex:m_screenIndex];
}
static QString displayName(CGDirectDisplayID displayID) static QString displayName(CGDirectDisplayID displayID)
{ {
QIOType<io_iterator_t> iterator; QIOType<io_iterator_t> iterator;
@ -117,35 +188,37 @@ static QString displayName(CGDirectDisplayID displayID)
void QCocoaScreen::updateProperties() void QCocoaScreen::updateProperties()
{ {
NSScreen *nsScreen = nativeScreen(); Q_ASSERT(m_displayId);
if (!nsScreen)
return;
const QRect previousGeometry = m_geometry; const QRect previousGeometry = m_geometry;
const QRect previousAvailableGeometry = m_availableGeometry; const QRect previousAvailableGeometry = m_availableGeometry;
const QDpi previousLogicalDpi = m_logicalDpi; const QDpi previousLogicalDpi = m_logicalDpi;
const qreal previousRefreshRate = m_refreshRate; 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 // 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_geometry = qt_mac_flip(QRectF::fromCGRect(nsScreen.frame), primaryScreenGeometry).toRect();
m_availableGeometry = qt_mac_flip(QRectF::fromCGRect(nsScreen.visibleFrame), primaryScreenGeometry).toRect(); m_availableGeometry = qt_mac_flip(QRectF::fromCGRect(nsScreen.visibleFrame), primaryScreenGeometry).toRect();
m_format = QImage::Format_RGB32; m_devicePixelRatio = nsScreen.backingScaleFactor;
m_depth = NSBitsPerPixelFromDepth([nsScreen depth]);
CGDirectDisplayID dpy = nsScreen.qt_displayId; m_format = QImage::Format_RGB32;
CGSize size = CGDisplayScreenSize(dpy); m_depth = NSBitsPerPixelFromDepth(nsScreen.depth);
CGSize size = CGDisplayScreenSize(m_displayId);
m_physicalSize = QSizeF(size.width, size.height); m_physicalSize = QSizeF(size.width, size.height);
m_logicalDpi.first = 72; m_logicalDpi.first = 72;
m_logicalDpi.second = 72; m_logicalDpi.second = 72;
CGDisplayModeRef displayMode = CGDisplayCopyDisplayMode(dpy);
float refresh = CGDisplayModeGetRefreshRate(displayMode);
CGDisplayModeRelease(displayMode);
if (refresh > 0)
m_refreshRate = refresh;
m_name = displayName(dpy); QCFType<CGDisplayModeRef> displayMode = CGDisplayCopyDisplayMode(m_displayId);
float refresh = CGDisplayModeGetRefreshRate(displayMode);
m_refreshRate = refresh > 0 ? refresh : 60.0;
m_name = displayName(m_displayId);
const bool didChangeGeometry = m_geometry != previousGeometry || m_availableGeometry != previousAvailableGeometry; 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); QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen(), m_logicalDpi.first, m_logicalDpi.second);
if (m_refreshRate != previousRefreshRate) if (m_refreshRate != previousRefreshRate)
QWindowSystemInterface::handleScreenRefreshRateChange(screen(), m_refreshRate); 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<QCocoaWindow*>(window->handle()))
cocoaWindow->handleGeometryChange();
}
}
} }
// ----------------------- Display link ----------------------- // ----------------------- Display link -----------------------
@ -181,8 +236,10 @@ Q_LOGGING_CATEGORY(lcQpaScreenUpdates, "qt.qpa.screen.updates", QtCriticalMsg);
void QCocoaScreen::requestUpdate() void QCocoaScreen::requestUpdate()
{ {
Q_ASSERT(m_displayId);
if (!m_displayLink) { if (!m_displayLink) {
CVDisplayLinkCreateWithCGDisplay(nativeScreen().qt_displayId, &m_displayLink); CVDisplayLinkCreateWithCGDisplay(m_displayId, &m_displayLink);
CVDisplayLinkSetOutputCallback(m_displayLink, [](CVDisplayLinkRef, const CVTimeStamp*, CVDisplayLinkSetOutputCallback(m_displayLink, [](CVDisplayLinkRef, const CVTimeStamp*,
const CVTimeStamp*, CVOptionFlags, CVOptionFlags*, void* displayLinkContext) -> int { const CVTimeStamp*, CVOptionFlags, CVOptionFlags*, void* displayLinkContext) -> int {
// FIXME: It would be nice if update requests would include timing info // FIXME: It would be nice if update requests would include timing info
@ -269,6 +326,9 @@ struct DeferredDebugHelper
void QCocoaScreen::deliverUpdateRequests() void QCocoaScreen::deliverUpdateRequests()
{ {
if (!m_displayId)
return; // Screen removed
QMacAutoReleasePool pool; QMacAutoReleasePool pool;
// The CVDisplayLink callback is a notification that it's a good time to produce a new frame. // 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; const int pendingUpdates = ++m_pendingUpdates;
DeferredDebugHelper screenUpdates(lcQpaScreenUpdates()); 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 (const int framesAheadOfDelivery = pendingUpdates - 1) {
// If we have more than one update pending it means that a previous display link callback // 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 QCocoaScreen::subpixelAntialiasingTypeHint() const
{ {
QPlatformScreen::SubpixelAntialiasingType type = QPlatformScreen::subpixelAntialiasingTypeHint(); 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 // 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 // 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. // only, since its NSWindow has the NSWindowStyleMaskTitled flag set.
Q_UNUSED(view); Q_UNUSED(view);
QRect grabRect = QRect(x, y, width, height); 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) for (uint i = 0; i < displayCount; ++i)
dpr = qMax(dpr, images.at(i).devicePixelRatio()); 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; qCDebug(lcQpaScreen) << "Create grap pixmap" << grabRect.size() << "at devicePixelRatio" << dpr;
QPixmap windowPixmap(grabRect.size() * dpr); QPixmap windowPixmap(grabRect.size() * dpr);
windowPixmap.setDevicePixelRatio(dpr); windowPixmap.setDevicePixelRatio(dpr);
@ -499,7 +552,57 @@ QPixmap QCocoaScreen::grabWindow(WId view, int x, int y, int width, int height)
*/ */
QCocoaScreen *QCocoaScreen::primaryScreen() QCocoaScreen *QCocoaScreen::primaryScreen()
{ {
return static_cast<QCocoaScreen *>(QGuiApplication::primaryScreen()->handle()); auto screen = static_cast<QCocoaScreen *>(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<QPlatformScreen*> QCocoaScreen::virtualSiblings() const
{
QList<QPlatformScreen*> 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<QCocoaScreen*>(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<CFUUIDRef> 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) CGPoint QCocoaScreen::mapToNative(const QPointF &pos, QCocoaScreen *screen)
@ -533,11 +636,10 @@ QDebug operator<<(QDebug debug, const QCocoaScreen *screen)
debug.nospace(); debug.nospace();
debug << "QCocoaScreen(" << (const void *)screen; debug << "QCocoaScreen(" << (const void *)screen;
if (screen) { if (screen) {
debug << ", index=" << screen->m_screenIndex;
debug << ", native=" << screen->nativeScreen();
debug << ", geometry=" << screen->geometry(); debug << ", geometry=" << screen->geometry();
debug << ", dpr=" << screen->devicePixelRatio(); debug << ", dpr=" << screen->devicePixelRatio();
debug << ", name=" << screen->name(); debug << ", name=" << screen->name();
debug << ", native=" << screen->nativeScreen();
} }
debug << ')'; debug << ')';
return debug; return debug;

View File

@ -383,9 +383,9 @@ QT_END_NAMESPACE
} }
- (QRectF)geometry { - (QRectF)geometry {
if (NSWindow *window = [[item view] window]) { if (NSWindow *window = item.view.window) {
if (QCocoaScreen *screen = QCocoaIntegration::instance()->screenForNSScreen([window screen])) if (QCocoaScreen *screen = QCocoaScreen::get(window.screen))
return screen->mapFromNative([window frame]); return screen->mapFromNative(window.frame);
} }
return QRectF(); return QRectF();
} }

View File

@ -1086,9 +1086,11 @@ void QCocoaWindow::setEmbeddedInForeignView()
void QCocoaWindow::viewDidChangeFrame() void QCocoaWindow::viewDidChangeFrame()
{ {
if (isContentView()) // Note: When the view is the content view, it would seem redundant
return; // Handled below // 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(); handleGeometryChange();
} }
@ -1209,17 +1211,17 @@ void QCocoaWindow::windowDidChangeScreen()
return; return;
// Note: When a window is resized to 0x0 Cocoa will report the window's screen as nil // 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<QCocoaScreen*>(screen()); auto *previousScreen = static_cast<QCocoaScreen*>(screen());
Q_ASSERT_X(!m_view.window.screen || currentScreen, Q_ASSERT_X(!m_view.window.screen || currentScreen,
"QCocoaWindow", "Failed to get QCocoaScreen for NSScreen"); "QCocoaWindow", "Failed to get QCocoaScreen for NSScreen");
// Note: The previous screen may be the same as the current screen, either because // 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 // a) the screen was just reconfigured, which still results in AppKit sending an
// NSWindowDidChangeScreenNotification, because the previous screen was removed, // NSWindowDidChangeScreenNotification, b) because the previous screen was removed,
// and we ended up calling QWindow::setScreen to move the window, which doesn't // 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 // 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(). // 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 // We still need to deliver the screen change in all these cases, as the

View File

@ -189,6 +189,15 @@
- (void)displayLayer:(CALayer *)layer - (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); Q_ASSERT(layer == self.layer);
if (!m_platformWindow) if (!m_platformWindow)

View File

@ -124,6 +124,8 @@
// Remove canvas at run-time. Removes the corresponding QScreen. // Remove canvas at run-time. Removes the corresponding QScreen.
// resizeCanvasElement // resizeCanvasElement
// Signals to the application that a canvas has been resized. // Signals to the application that a canvas has been resized.
// setFontDpi
// Sets the logical font dpi for the application.
var Module = {} var Module = {}
@ -237,6 +239,8 @@ function QtLoader(config)
publicAPI.addCanvasElement = addCanvasElement; publicAPI.addCanvasElement = addCanvasElement;
publicAPI.removeCanvasElement = removeCanvasElement; publicAPI.removeCanvasElement = removeCanvasElement;
publicAPI.resizeCanvasElement = resizeCanvasElement; publicAPI.resizeCanvasElement = resizeCanvasElement;
publicAPI.setFontDpi = setFontDpi;
publicAPI.fontDpi = fontDpi;
restartCount = 0; restartCount = 0;
@ -557,6 +561,16 @@ function QtLoader(config)
Module.qtResizeCanvasElement(element); Module.qtResizeCanvasElement(element);
} }
function setFontDpi(dpi) {
Module.qtFontDpi = dpi;
if (publicAPI.status == "Running")
Module.qtSetFontDpi(dpi);
}
function fontDpi() {
return Module.qtFontDpi;
}
setStatus("Created"); setStatus("Created");
return publicAPI; return publicAPI;

View File

@ -50,6 +50,7 @@
#include <QtCore/qcoreapplication.h> #include <QtCore/qcoreapplication.h>
#include <emscripten/bind.h> #include <emscripten/bind.h>
#include <emscripten/val.h>
// this is where EGL headers are pulled in, make sure it is last // this is where EGL headers are pulled in, make sure it is last
#include "qwasmscreen.h" #include "qwasmscreen.h"
@ -80,12 +81,18 @@ static void resizeCanvasElement(emscripten::val canvas)
QWasmIntegration::get()->resizeScreen(canvasId); QWasmIntegration::get()->resizeScreen(canvasId);
} }
static void qtUpdateDpi()
{
QWasmIntegration::get()->updateDpi();
}
EMSCRIPTEN_BINDINGS(qtQWasmIntegraton) EMSCRIPTEN_BINDINGS(qtQWasmIntegraton)
{ {
function("qtBrowserBeforeUnload", &browserBeforeUnload); function("qtBrowserBeforeUnload", &browserBeforeUnload);
function("qtAddCanvasElement", &addCanvasElement); function("qtAddCanvasElement", &addCanvasElement);
function("qtRemoveCanvasElement", &removeCanvasElement); function("qtRemoveCanvasElement", &removeCanvasElement);
function("qtResizeCanvasElement", &resizeCanvasElement); function("qtResizeCanvasElement", &resizeCanvasElement);
function("qtUpdateDpi", &qtUpdateDpi);
} }
QWasmIntegration *QWasmIntegration::s_instance; QWasmIntegration *QWasmIntegration::s_instance;
@ -245,4 +252,14 @@ void QWasmIntegration::resizeScreen(const QString &canvasId)
m_screens.value(canvasId)->updateQScreenAndCanvasRenderSize(); m_screens.value(canvasId)->updateQScreenAndCanvasRenderSize();
} }
void QWasmIntegration::updateDpi()
{
emscripten::val dpi = emscripten::val::module_property("qtFontDpi");
if (dpi.isUndefined())
return;
qreal dpiValue = dpi.as<qreal>();
for (QWasmScreen *screen : m_screens)
QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen->screen(), dpiValue, dpiValue);
}
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -81,6 +81,7 @@ public:
void addScreen(const QString &canvasId); void addScreen(const QString &canvasId);
void removeScreen(const QString &canvasId); void removeScreen(const QString &canvasId);
void resizeScreen(const QString &canvasId); void resizeScreen(const QString &canvasId);
void updateDpi();
private: private:
mutable QWasmFontDatabase *m_fontDb; mutable QWasmFontDatabase *m_fontDb;
@ -89,6 +90,7 @@ private:
QHash<QString, QWasmScreen *> m_screens; QHash<QString, QWasmScreen *> m_screens;
mutable QWasmClipboard *m_clipboard; mutable QWasmClipboard *m_clipboard;
qreal m_fontDpi = -1;
static QWasmIntegration *s_instance; static QWasmIntegration *s_instance;
}; };

View File

@ -31,6 +31,7 @@
#include "qwasmwindow.h" #include "qwasmwindow.h"
#include "qwasmeventtranslator.h" #include "qwasmeventtranslator.h"
#include "qwasmcompositor.h" #include "qwasmcompositor.h"
#include "qwasmintegration.h"
#include <emscripten/bind.h> #include <emscripten/bind.h>
#include <emscripten/val.h> #include <emscripten/val.h>
@ -100,6 +101,17 @@ QImage::Format QWasmScreen::format() const
return m_format; return m_format;
} }
QDpi QWasmScreen::logicalDpi() const
{
emscripten::val dpi = emscripten::val::module_property("qtFontDpi");
if (!dpi.isUndefined()) {
qreal dpiValue = dpi.as<qreal>();
return QDpi(dpiValue, dpiValue);
}
const qreal defaultDpi = 96;
return QDpi(defaultDpi, defaultDpi);
}
qreal QWasmScreen::devicePixelRatio() const qreal QWasmScreen::devicePixelRatio() const
{ {
// FIXME: The effective device pixel ratio may be different from the // FIXME: The effective device pixel ratio may be different from the

View File

@ -63,6 +63,7 @@ public:
QRect geometry() const override; QRect geometry() const override;
int depth() const override; int depth() const override;
QImage::Format format() const override; QImage::Format format() const override;
QDpi logicalDpi() const override;
qreal devicePixelRatio() const override; qreal devicePixelRatio() const override;
QString name() const override; QString name() const override;
QPlatformCursor *cursor() const override; QPlatformCursor *cursor() const override;

View File

@ -210,6 +210,7 @@ void QWindowsUser32DLL::init()
if (QOperatingSystemVersion::current() if (QOperatingSystemVersion::current()
>= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, 14393)) { >= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, 14393)) {
adjustWindowRectExForDpi = (AdjustWindowRectExForDpi)library.resolve("AdjustWindowRectExForDpi");
enableNonClientDpiScaling = (EnableNonClientDpiScaling)library.resolve("EnableNonClientDpiScaling"); enableNonClientDpiScaling = (EnableNonClientDpiScaling)library.resolve("EnableNonClientDpiScaling");
getWindowDpiAwarenessContext = (GetWindowDpiAwarenessContext)library.resolve("GetWindowDpiAwarenessContext"); getWindowDpiAwarenessContext = (GetWindowDpiAwarenessContext)library.resolve("GetWindowDpiAwarenessContext");
getAwarenessFromDpiAwarenessContext = (GetAwarenessFromDpiAwarenessContext)library.resolve("GetAwarenessFromDpiAwarenessContext"); getAwarenessFromDpiAwarenessContext = (GetAwarenessFromDpiAwarenessContext)library.resolve("GetAwarenessFromDpiAwarenessContext");
@ -977,7 +978,7 @@ static inline bool resizeOnDpiChanged(const QWindow *w)
return result; return result;
} }
static bool shouldHaveNonClientDpiScaling(const QWindow *window) bool QWindowsContext::shouldHaveNonClientDpiScaling(const QWindow *window)
{ {
return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10 return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10
&& window->isTopLevel() && window->isTopLevel()
@ -1321,15 +1322,24 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
#endif #endif
} break; } break;
case QtWindows::DpiChangedEvent: { case QtWindows::DpiChangedEvent: {
if (!resizeOnDpiChanged(platformWindow->window())) // Try to apply the suggested size first and then notify ScreenChanged
return false; // 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->setFlag(QWindowsWindow::WithinDpiChanged);
const RECT *prcNewWindow = reinterpret_cast<RECT *>(lParam); platformWindow->updateFullFrameMargins();
const auto prcNewWindow = reinterpret_cast<RECT *>(lParam);
qCDebug(lcQpaWindows) << __FUNCTION__ << "WM_DPICHANGED"
<< platformWindow->window() << *prcNewWindow;
SetWindowPos(hwnd, nullptr, prcNewWindow->left, prcNewWindow->top, SetWindowPos(hwnd, nullptr, prcNewWindow->left, prcNewWindow->top,
prcNewWindow->right - prcNewWindow->left, prcNewWindow->right - prcNewWindow->left,
prcNewWindow->bottom - prcNewWindow->top, SWP_NOZORDER | SWP_NOACTIVATE); prcNewWindow->bottom - prcNewWindow->top, SWP_NOZORDER | SWP_NOACTIVATE);
platformWindow->clearFlag(QWindowsWindow::WithinDpiChanged); platformWindow->clearFlag(QWindowsWindow::WithinDpiChanged);
return true; }
platformWindow->checkForScreenChanged(QWindowsWindow::FromDpiChange);
return doResize;
} }
#if QT_CONFIG(sessionmanager) #if QT_CONFIG(sessionmanager)
case QtWindows::QueryEndSessionApplicationEvent: { case QtWindows::QueryEndSessionApplicationEvent: {
@ -1587,6 +1597,7 @@ extern "C" LRESULT QT_WIN_CALLBACK qWindowsWndProc(HWND hwnd, UINT message, WPAR
marginsFromRects(ncCalcSizeFrame, rectFromNcCalcSize(message, wParam, lParam, 0)); marginsFromRects(ncCalcSizeFrame, rectFromNcCalcSize(message, wParam, lParam, 0));
if (margins.left() >= 0) { if (margins.left() >= 0) {
if (platformWindow) { if (platformWindow) {
qCDebug(lcQpaWindows) << __FUNCTION__ << "WM_NCCALCSIZE for" << hwnd << margins;
platformWindow->setFullFrameMargins(margins); platformWindow->setFullFrameMargins(margins);
} else { } else {
const QSharedPointer<QWindowCreationContext> ctx = QWindowsContext::instance()->windowCreationContext(); const QSharedPointer<QWindowCreationContext> ctx = QWindowsContext::instance()->windowCreationContext();

View File

@ -102,6 +102,7 @@ struct QWindowsUser32DLL
typedef BOOL (WINAPI *RemoveClipboardFormatListener)(HWND); typedef BOOL (WINAPI *RemoveClipboardFormatListener)(HWND);
typedef BOOL (WINAPI *GetDisplayAutoRotationPreferences)(DWORD *); typedef BOOL (WINAPI *GetDisplayAutoRotationPreferences)(DWORD *);
typedef BOOL (WINAPI *SetDisplayAutoRotationPreferences)(DWORD); typedef BOOL (WINAPI *SetDisplayAutoRotationPreferences)(DWORD);
typedef BOOL (WINAPI *AdjustWindowRectExForDpi)(LPRECT,DWORD,BOOL,DWORD,UINT);
typedef BOOL (WINAPI *EnableNonClientDpiScaling)(HWND); typedef BOOL (WINAPI *EnableNonClientDpiScaling)(HWND);
typedef int (WINAPI *GetWindowDpiAwarenessContext)(HWND); typedef int (WINAPI *GetWindowDpiAwarenessContext)(HWND);
typedef int (WINAPI *GetAwarenessFromDpiAwarenessContext)(int); typedef int (WINAPI *GetAwarenessFromDpiAwarenessContext)(int);
@ -131,6 +132,7 @@ struct QWindowsUser32DLL
GetDisplayAutoRotationPreferences getDisplayAutoRotationPreferences = nullptr; GetDisplayAutoRotationPreferences getDisplayAutoRotationPreferences = nullptr;
SetDisplayAutoRotationPreferences setDisplayAutoRotationPreferences = nullptr; SetDisplayAutoRotationPreferences setDisplayAutoRotationPreferences = nullptr;
AdjustWindowRectExForDpi adjustWindowRectExForDpi = nullptr;
EnableNonClientDpiScaling enableNonClientDpiScaling = nullptr; EnableNonClientDpiScaling enableNonClientDpiScaling = nullptr;
GetWindowDpiAwarenessContext getWindowDpiAwarenessContext = nullptr; GetWindowDpiAwarenessContext getWindowDpiAwarenessContext = nullptr;
GetAwarenessFromDpiAwarenessContext getAwarenessFromDpiAwarenessContext = nullptr; GetAwarenessFromDpiAwarenessContext getAwarenessFromDpiAwarenessContext = nullptr;
@ -201,6 +203,8 @@ public:
QWindowsWindow *findPlatformWindowAt(HWND parent, const QPoint &screenPoint, QWindowsWindow *findPlatformWindowAt(HWND parent, const QPoint &screenPoint,
unsigned cwex_flags) const; unsigned cwex_flags) const;
static bool shouldHaveNonClientDpiScaling(const QWindow *window);
QWindow *windowUnderMouse() const; QWindow *windowUnderMouse() const;
void clearWindowUnderMouse(); void clearWindowUnderMouse();

View File

@ -353,6 +353,9 @@ QPlatformWindow *QWindowsIntegration::createPlatformWindow(QWindow *window) cons
QWindowsWindow *result = createPlatformWindowHelper(window, obtained); QWindowsWindow *result = createPlatformWindowHelper(window, obtained);
Q_ASSERT(result); Q_ASSERT(result);
if (window->isTopLevel() && !QWindowsContext::shouldHaveNonClientDpiScaling(window))
result->setFlag(QWindowsWindow::DisableNonClientScaling);
if (QWindowsMenuBar *menuBarToBeInstalled = QWindowsMenuBar::menuBarOf(window)) if (QWindowsMenuBar *menuBarToBeInstalled = QWindowsMenuBar::menuBarOf(window))
menuBarToBeInstalled->install(result); menuBarToBeInstalled->install(result);

View File

@ -240,6 +240,7 @@ QWindow *QWindowsScreen::topLevelAt(const QPoint &point) const
QWindow *result = nullptr; QWindow *result = nullptr;
if (QWindow *child = QWindowsScreen::windowAt(point, CWP_SKIPINVISIBLE)) if (QWindow *child = QWindowsScreen::windowAt(point, CWP_SKIPINVISIBLE))
result = QWindowsWindow::topLevelOf(child); result = QWindowsWindow::topLevelOf(child);
if (QWindowsContext::verbose > 1)
qCDebug(lcQpaWindows) <<__FUNCTION__ << point << result; qCDebug(lcQpaWindows) <<__FUNCTION__ << point << result;
return result; return result;
} }
@ -250,6 +251,7 @@ QWindow *QWindowsScreen::windowAt(const QPoint &screenPoint, unsigned flags)
if (QPlatformWindow *bw = QWindowsContext::instance()-> if (QPlatformWindow *bw = QWindowsContext::instance()->
findPlatformWindowAt(GetDesktopWindow(), screenPoint, flags)) findPlatformWindowAt(GetDesktopWindow(), screenPoint, flags))
result = bw->window(); result = bw->window();
if (QWindowsContext::verbose > 1)
qCDebug(lcQpaWindows) <<__FUNCTION__ << screenPoint << " returns " << result; qCDebug(lcQpaWindows) <<__FUNCTION__ << screenPoint << " returns " << result;
return result; return result;
} }
@ -438,6 +440,12 @@ QPlatformScreen::SubpixelAntialiasingType QWindowsScreen::subpixelAntialiasingTy
QWindowsScreenManager::QWindowsScreenManager() = default; QWindowsScreenManager::QWindowsScreenManager() = default;
bool QWindowsScreenManager::isSingleScreen()
{
return QWindowsContext::instance()->screenManager().screens().size() < 2;
}
/*! /*!
\brief Triggers synchronization of screens (WM_DISPLAYCHANGE). \brief Triggers synchronization of screens (WM_DISPLAYCHANGE).

View File

@ -140,6 +140,8 @@ public:
const QWindowsScreen *screenAtDp(const QPoint &p) const; const QWindowsScreen *screenAtDp(const QPoint &p) const;
const QWindowsScreen *screenForHwnd(HWND hwnd) const; const QWindowsScreen *screenForHwnd(HWND hwnd) const;
static bool isSingleScreen();
private: private:
void removeScreen(int index); void removeScreen(int index);

View File

@ -184,6 +184,7 @@ static inline RECT RECTfromQRect(const QRect &rect)
return result; return result;
} }
#ifndef QT_NO_DEBUG_STREAM #ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug d, const RECT &r) QDebug operator<<(QDebug d, const RECT &r)
{ {
@ -262,6 +263,16 @@ QDebug operator<<(QDebug d, const GUID &guid)
} }
#endif // !QT_NO_DEBUG_STREAM #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 // QTBUG-43872, for windows that do not have WS_EX_TOOLWINDOW set, WINDOWPLACEMENT
// is in workspace/available area coordinates. // is in workspace/available area coordinates.
static QPoint windowPlacementOffset(HWND hwnd, const QPoint &point) static QPoint windowPlacementOffset(HWND hwnd, const QPoint &point)
@ -859,35 +870,78 @@ static QSize toNativeSizeConstrained(QSize dip, const QWindow *w)
\ingroup qt-lighthouse-win \ingroup qt-lighthouse-win
*/ */
QWindowsGeometryHint::QWindowsGeometryHint(const QWindow *w, const QMargins &cm) : QMargins QWindowsGeometryHint::frameOnPrimaryScreen(DWORD style, DWORD exStyle)
minimumSize(toNativeSizeConstrained(w->minimumSize(), w)),
maximumSize(toNativeSizeConstrained(w->maximumSize(), w)),
customMargins(cm)
{
}
bool QWindowsGeometryHint::validSize(const QSize &s) const
{
const int width = s.width();
const int height = s.height();
return width >= minimumSize.width() && width <= maximumSize.width()
&& height >= minimumSize.height() && height <= maximumSize.height();
}
QMargins QWindowsGeometryHint::frame(DWORD style, DWORD exStyle)
{ {
RECT rect = {0,0,0,0}; RECT rect = {0,0,0,0};
style &= ~(WS_OVERLAPPED); // Not permitted, see docs. style &= ~DWORD(WS_OVERLAPPED); // Not permitted, see docs.
if (!AdjustWindowRectEx(&rect, style, FALSE, exStyle)) if (AdjustWindowRectEx(&rect, style, FALSE, exStyle) == FALSE)
qErrnoWarning("%s: AdjustWindowRectEx failed", __FUNCTION__); qErrnoWarning("%s: AdjustWindowRectEx failed", __FUNCTION__);
const QMargins result(qAbs(rect.left), qAbs(rect.top), const QMargins result(qAbs(rect.left), qAbs(rect.top),
qAbs(rect.right), qAbs(rect.bottom)); qAbs(rect.right), qAbs(rect.bottom));
qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << " style=" qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << " style="
<< Qt::showbase << Qt::hex << style << " exStyle=" << exStyle << Qt::dec << Qt::noshowbase << showbase << hex << style << " exStyle=" << exStyle << dec << noshowbase
<< ' ' << rect << ' ' << result; << ' ' << rect << ' ' << result;
return result; return result;
} }
QMargins QWindowsGeometryHint::frameOnPrimaryScreen(HWND hwnd)
{
return frameOnPrimaryScreen(DWORD(GetWindowLongPtr(hwnd, GWL_STYLE)),
DWORD(GetWindowLongPtr(hwnd, GWL_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 &= ~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="
<< Qt::showbase << Qt::hex << style << " exStyle=" << exStyle << Qt::dec << Qt::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) bool QWindowsGeometryHint::handleCalculateSize(const QMargins &customMargins, const MSG &msg, LRESULT *result)
{ {
// NCCALCSIZE_PARAMS structure if wParam==TRUE // NCCALCSIZE_PARAMS structure if wParam==TRUE
@ -907,36 +961,50 @@ bool QWindowsGeometryHint::handleCalculateSize(const QMargins &customMargins, co
return true; 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)), *minimumSize = toNativeSizeConstrained(w->minimumSize(), w);
DWORD(GetWindowLong(hwnd, GWL_EXSTYLE)), mmi); *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=" qCDebug(lcQpaWindows).nospace() << '>' << __FUNCTION__ << '<' << " min="
<< minimumSize.width() << ',' << minimumSize.height() << minimumSize.width() << ',' << minimumSize.height()
<< " max=" << maximumSize.width() << ',' << maximumSize.height() << " max=" << maximumSize.width() << ',' << maximumSize.height()
<< " margins=" << margins
<< " in " << *mmi; << " 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) if (minimumSize.width() > 0)
mmi->ptMinTrackSize.x = minimumSize.width() + frameWidth; mmi->ptMinTrackSize.x = minimumSize.width();
if (minimumSize.height() > 0) if (minimumSize.height() > 0)
mmi->ptMinTrackSize.y = minimumSize.height() + frameHeight; mmi->ptMinTrackSize.y = minimumSize.height();
const int maximumWidth = qMax(maximumSize.width(), minimumSize.width()); if (maximumSize.width() < QWINDOWSIZE_MAX)
const int maximumHeight = qMax(maximumSize.height(), minimumSize.height()); mmi->ptMaxTrackSize.x = maximumSize.width();
if (maximumWidth < QWINDOWSIZE_MAX) if (maximumSize.height() < QWINDOWSIZE_MAX)
mmi->ptMaxTrackSize.x = maximumWidth + frameWidth; mmi->ptMaxTrackSize.y = maximumSize.height();
if (maximumHeight < QWINDOWSIZE_MAX) qCDebug(lcQpaWindows).nospace() << '<' << __FUNCTION__ << " out " << *mmi;
mmi->ptMaxTrackSize.y = maximumHeight + frameHeight;
qCDebug(lcQpaWindows).nospace() << '<' << __FUNCTION__
<< " frame=" << margins << ' ' << frameWidth << ',' << frameHeight
<< " out " << *mmi;
} }
bool QWindowsGeometryHint::positionIncludesFrame(const QWindow *w) bool QWindowsGeometryHint::positionIncludesFrame(const QWindow *w)
@ -996,7 +1064,7 @@ QRect QWindowsBaseWindow::geometry_sys() const
QMargins QWindowsBaseWindow::frameMargins_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. void QWindowsBaseWindow::hide_sys() // Normal hide, do not activate other windows.
@ -1122,11 +1190,12 @@ void QWindowsForeignWindow::setVisible(bool visible)
QWindowCreationContext::QWindowCreationContext(const QWindow *w, QWindowCreationContext::QWindowCreationContext(const QWindow *w,
const QRect &geometryIn, const QRect &geometry, const QRect &geometryIn, const QRect &geometry,
const QMargins &cm, const QMargins &cm,
DWORD style_, DWORD exStyle_) : DWORD style, DWORD exStyle) :
geometryHint(w, cm), window(w), style(style_), exStyle(exStyle_), window(w),
requestedGeometryIn(geometryIn), requestedGeometryIn(geometryIn),
requestedGeometry(geometry), obtainedGeometry(geometry), 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. // Geometry of toplevels does not consider window frames.
// TODO: No concept of WA_wasMoved yet that would indicate a // TODO: No concept of WA_wasMoved yet that would indicate a
@ -1155,8 +1224,12 @@ QWindowCreationContext::QWindowCreationContext(const QWindow *w,
<< " pos incl. frame=" << QWindowsGeometryHint::positionIncludesFrame(w) << " pos incl. frame=" << QWindowsGeometryHint::positionIncludesFrame(w)
<< " frame=" << frameWidth << 'x' << frameHeight << '+' << " frame=" << frameWidth << 'x' << frameHeight << '+'
<< frameX << '+' << frameY << frameX << '+' << frameY
<< " min=" << geometryHint.minimumSize << " max=" << geometryHint.maximumSize << " margins=" << margins << " custom margins=" << customMargins;
<< " custom margins=" << customMargins; }
void QWindowCreationContext::applyToMinMaxInfo(MINMAXINFO *mmi) const
{
QWindowsGeometryHint::applyToMinMaxInfo(window, margins + customMargins, mmi);
} }
/*! /*!
@ -1672,10 +1745,57 @@ QRect QWindowsWindow::normalGeometry() const
const bool fakeFullScreen = const bool fakeFullScreen =
m_savedFrameGeometry.isValid() && (window()->windowStates() & Qt::WindowFullScreen); m_savedFrameGeometry.isValid() && (window()->windowStates() & Qt::WindowFullScreen);
const QRect frame = fakeFullScreen ? m_savedFrameGeometry : normalFrameGeometry(m_data.hwnd); 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; 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) void QWindowsWindow::setGeometry(const QRect &rectIn)
{ {
QRect rect = rectIn; QRect rect = rectIn;
@ -1695,21 +1815,10 @@ void QWindowsWindow::setGeometry(const QRect &rectIn)
setGeometry_sys(rect); setGeometry_sys(rect);
clearFlag(WithinSetGeometry); clearFlag(WithinSetGeometry);
if (m_data.geometry != rect && (isVisible() || QLibraryInfo::isDebugBuild())) { if (m_data.geometry != rect && (isVisible() || QLibraryInfo::isDebugBuild())) {
qWarning("%s: Unable to set geometry %dx%d+%d+%d on %s/'%s'." const auto warning =
" Resulting geometry: %dx%d+%d+%d " msgUnableToSetGeometry(this, rectIn, m_data.geometry,
"(frame: %d, %d, %d, %d, custom margin: %d, %d, %d, %d" m_data.fullFrameMargins, m_data.customMargins);
", minimum size: %dx%d, maximum size: %dx%d).", qWarning("%s: %s", __FUNCTION__, qPrintable(warning));
__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());
} }
} else { } else {
QPlatformWindow::setGeometry(rect); QPlatformWindow::setGeometry(rect);
@ -1753,27 +1862,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; return;
QPlatformScreen *currentScreen = screen(); QPlatformScreen *currentScreen = screen();
const auto &screenManager = QWindowsContext::instance()->screenManager(); const QWindowsScreen *newScreen =
const QWindowsScreen *newScreen = screenManager.screenForHwnd(m_data.hwnd); QWindowsContext::instance()->screenManager().screenForHwnd(m_data.hwnd);
if (newScreen != nullptr && newScreen != currentScreen) { 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__ qCDebug(lcQpaWindows).noquote().nospace() << __FUNCTION__
<< ' ' << window() << " \"" << currentScreen->name() << ' ' << window() << " \"" << currentScreen->name()
<< "\"->\"" << newScreen->name() << '"'; << "\"->\"" << newScreen->name() << '"';
if (mode == FromGeometryChange)
setFlag(SynchronousGeometryChangeEvent); setFlag(SynchronousGeometryChangeEvent);
updateFullFrameMargins();
QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->screen()); QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->screen());
}
} }
void QWindowsWindow::handleGeometryChange() void QWindowsWindow::handleGeometryChange()
{ {
const QRect previousGeometry = m_data.geometry; const QRect previousGeometry = m_data.geometry;
m_data.geometry = geometry_sys(); m_data.geometry = geometry_sys();
if (testFlag(WithinDpiChanged))
return; // QGuiApplication will send resize
QWindowSystemInterface::handleGeometryChange(window(), m_data.geometry); QWindowSystemInterface::handleGeometryChange(window(), m_data.geometry);
// QTBUG-32121: OpenGL/normal windows (with exception of ANGLE) do not receive // QTBUG-32121: OpenGL/normal windows (with exception of ANGLE) do not receive
// expose events when shrinking, synthesize. // expose events when shrinking, synthesize.
@ -2236,6 +2359,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 QWindowsWindow::frameMargins() const
{ {
QMargins result = fullFrameMargins(); QMargins result = fullFrameMargins();
@ -2446,10 +2578,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 // 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 // QScreen of the window yet so we don't have the min/max with the right ratio
if (!testFlag(QWindowsWindow::WithinDpiChanged)) { if (!testFlag(QWindowsWindow::WithinDpiChanged))
const QWindowsGeometryHint hint(window(), m_data.customMargins); QWindowsGeometryHint::applyToMinMaxInfo(window(), fullFrameMargins(), mmi);
hint.applyToMinMaxInfo(m_data.hwnd, mmi);
}
// This block fixes QTBUG-8361, QTBUG-4362: Frameless/title-less windows shouldn't cover the // This block fixes QTBUG-8361, QTBUG-4362: Frameless/title-less windows shouldn't cover the
// taskbar when maximized // taskbar when maximized

View File

@ -59,24 +59,23 @@ class QDebug;
struct QWindowsGeometryHint struct QWindowsGeometryHint
{ {
QWindowsGeometryHint() = default; static QMargins frameOnPrimaryScreen(DWORD style, DWORD exStyle);
explicit QWindowsGeometryHint(const QWindow *w, const QMargins &customMargins); static QMargins frameOnPrimaryScreen(HWND hwnd);
static QMargins frame(DWORD style, DWORD exStyle); 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); static bool handleCalculateSize(const QMargins &customMargins, const MSG &msg, LRESULT *result);
void applyToMinMaxInfo(DWORD style, DWORD exStyle, MINMAXINFO *mmi) const; static void applyToMinMaxInfo(const QWindow *w, const QMargins &margins,
void applyToMinMaxInfo(HWND hwnd, MINMAXINFO *mmi) const; MINMAXINFO *mmi);
bool validSize(const QSize &s) const; 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(HWND hwnd, const QPoint &);
static inline QPoint mapToGlobal(const QWindow *w, 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 HWND hwnd, const QPoint &);
static inline QPoint mapFromGlobal(const QWindow *w, const QPoint &); static inline QPoint mapFromGlobal(const QWindow *w, const QPoint &);
static bool positionIncludesFrame(const QWindow *w); static bool positionIncludesFrame(const QWindow *w);
QSize minimumSize;
QSize maximumSize;
QMargins customMargins;
}; };
struct QWindowCreationContext struct QWindowCreationContext
@ -85,13 +84,9 @@ struct QWindowCreationContext
const QRect &geometryIn, const QRect &geometry, const QRect &geometryIn, const QRect &geometry,
const QMargins &customMargins, const QMargins &customMargins,
DWORD style, DWORD exStyle); DWORD style, DWORD exStyle);
void applyToMinMaxInfo(MINMAXINFO *mmi) const void applyToMinMaxInfo(MINMAXINFO *mmi) const;
{ geometryHint.applyToMinMaxInfo(style, exStyle, mmi); }
QWindowsGeometryHint geometryHint;
const QWindow *window; const QWindow *window;
DWORD style;
DWORD exStyle;
QRect requestedGeometryIn; // QWindow scaled QRect requestedGeometryIn; // QWindow scaled
QRect requestedGeometry; // after QPlatformWindow::initialGeometry() QRect requestedGeometry; // after QPlatformWindow::initialGeometry()
QRect obtainedGeometry; QRect obtainedGeometry;
@ -221,7 +216,8 @@ public:
HasBorderInFullScreen = 0x200000, HasBorderInFullScreen = 0x200000,
WithinDpiChanged = 0x400000, WithinDpiChanged = 0x400000,
VulkanSurface = 0x800000, VulkanSurface = 0x800000,
ResizeMoveActive = 0x1000000 ResizeMoveActive = 0x1000000,
DisableNonClientScaling = 0x2000000
}; };
QWindowsWindow(QWindow *window, const QWindowsWindowData &data); QWindowsWindow(QWindow *window, const QWindowsWindowData &data);
@ -262,6 +258,7 @@ public:
QMargins frameMargins() const override; QMargins frameMargins() const override;
QMargins fullFrameMargins() const override; QMargins fullFrameMargins() const override;
void setFullFrameMargins(const QMargins &newMargins); void setFullFrameMargins(const QMargins &newMargins);
void updateFullFrameMargins();
void setOpacity(qreal level) override; void setOpacity(qreal level) override;
void setMask(const QRegion &region) override; void setMask(const QRegion &region) override;
@ -337,7 +334,8 @@ public:
void alertWindow(int durationMs = 0); void alertWindow(int durationMs = 0);
void stopAlertWindow(); void stopAlertWindow();
void checkForScreenChanged(); enum ScreenChangeMode { FromGeometryChange, FromDpiChange };
void checkForScreenChanged(ScreenChangeMode mode = FromGeometryChange);
static void setTouchWindowTouchTypeStatic(QWindow *window, QWindowsWindowFunctions::TouchWindowTouchTypes touchTypes); static void setTouchWindowTouchTypeStatic(QWindow *window, QWindowsWindowFunctions::TouchWindowTouchTypes touchTypes);
void registerTouchWindow(QWindowsWindowFunctions::TouchWindowTouchTypes touchTypes = QWindowsWindowFunctions::NormalTouch); void registerTouchWindow(QWindowsWindowFunctions::TouchWindowTouchTypes touchTypes = QWindowsWindowFunctions::NormalTouch);

View File

@ -4290,12 +4290,15 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter
alpha:pc.alphaF()]; alpha:pc.alphaF()];
s = qt_mac_removeMnemonics(s); s = qt_mac_removeMnemonics(s);
const auto textRect = CGRectMake(xpos, yPos, mi->rect.width() - xm - tabwidth + 1, mi->rect.height());
QMacCGContext cgCtx(p); QMacCGContext cgCtx(p);
d->setupNSGraphicsContext(cgCtx, YES); 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, withAttributes:@{ NSFontAttributeName:f, NSForegroundColorAttributeName:c,
NSObliquenessAttributeName: [NSNumber numberWithDouble: myFont.italic() ? 0.3 : 0.0]}]; NSObliquenessAttributeName: [NSNumber numberWithDouble: myFont.italic() ? 0.3 : 0.0]}];

View File

@ -241,7 +241,8 @@ Symbols Preprocessor::tokenize(const QByteArray& input, int lineNum, Preprocesso
if (!*data || *data != '.') { if (!*data || *data != '.') {
token = INTEGER_LITERAL; token = INTEGER_LITERAL;
if (data - lexem == 1 && if (data - lexem == 1 &&
(*data == 'x' || *data == 'X') (*data == 'x' || *data == 'X'
|| *data == 'b' || *data == 'B')
&& *lexem == '0') { && *lexem == '0') {
++data; ++data;
while (is_hex_char(*data) || *data == '\'') while (is_hex_char(*data) || *data == '\'')

View File

@ -897,11 +897,15 @@ QHeaderView *QAccessibleTableCell::verticalHeader() const
int QAccessibleTableCell::columnIndex() const int QAccessibleTableCell::columnIndex() const
{ {
if (!isValid())
return -1;
return m_index.column(); return m_index.column();
} }
int QAccessibleTableCell::rowIndex() const int QAccessibleTableCell::rowIndex() const
{ {
if (!isValid())
return -1;
#if QT_CONFIG(treeview) #if QT_CONFIG(treeview)
if (role() == QAccessible::TreeItem) { if (role() == QAccessible::TreeItem) {
const QTreeView *treeView = qobject_cast<const QTreeView*>(view); const QTreeView *treeView = qobject_cast<const QTreeView*>(view);
@ -915,6 +919,8 @@ int QAccessibleTableCell::rowIndex() const
bool QAccessibleTableCell::isSelected() const bool QAccessibleTableCell::isSelected() const
{ {
if (!isValid())
return false;
return view->selectionModel()->isSelected(m_index); return view->selectionModel()->isSelected(m_index);
} }
@ -943,8 +949,10 @@ QStringList QAccessibleTableCell::keyBindingsForAction(const QString &) const
void QAccessibleTableCell::selectCell() void QAccessibleTableCell::selectCell()
{ {
if (!isValid())
return;
QAbstractItemView::SelectionMode selectionMode = view->selectionMode(); QAbstractItemView::SelectionMode selectionMode = view->selectionMode();
if (!m_index.isValid() || (selectionMode == QAbstractItemView::NoSelection)) if (selectionMode == QAbstractItemView::NoSelection)
return; return;
Q_ASSERT(table()); Q_ASSERT(table());
QAccessibleTableInterface *cellTable = table()->tableInterface(); QAccessibleTableInterface *cellTable = table()->tableInterface();
@ -971,9 +979,10 @@ void QAccessibleTableCell::selectCell()
void QAccessibleTableCell::unselectCell() void QAccessibleTableCell::unselectCell()
{ {
if (!isValid())
return;
QAbstractItemView::SelectionMode selectionMode = view->selectionMode(); QAbstractItemView::SelectionMode selectionMode = view->selectionMode();
if (!m_index.isValid() || (selectionMode == QAbstractItemView::NoSelection)) if (selectionMode == QAbstractItemView::NoSelection)
return; return;
QAccessibleTableInterface *cellTable = table()->tableInterface(); QAccessibleTableInterface *cellTable = table()->tableInterface();
@ -1014,7 +1023,7 @@ QAccessible::Role QAccessibleTableCell::role() const
QAccessible::State QAccessibleTableCell::state() const QAccessible::State QAccessibleTableCell::state() const
{ {
QAccessible::State st; QAccessible::State st;
if (!view) if (!isValid())
return st; return st;
QRect globalRect = view->rect(); QRect globalRect = view->rect();
@ -1054,6 +1063,8 @@ QAccessible::State QAccessibleTableCell::state() const
QRect QAccessibleTableCell::rect() const QRect QAccessibleTableCell::rect() const
{ {
QRect r; QRect r;
if (!isValid())
return r;
r = view->visualRect(m_index); r = view->visualRect(m_index);
if (!r.isNull()) { if (!r.isNull()) {
@ -1065,8 +1076,10 @@ QRect QAccessibleTableCell::rect() const
QString QAccessibleTableCell::text(QAccessible::Text t) const QString QAccessibleTableCell::text(QAccessible::Text t) const
{ {
QAbstractItemModel *model = view->model();
QString value; QString value;
if (!isValid())
return value;
QAbstractItemModel *model = view->model();
switch (t) { switch (t) {
case QAccessible::Name: case QAccessible::Name:
value = model->data(m_index, Qt::AccessibleTextRole).toString(); 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) void QAccessibleTableCell::setText(QAccessible::Text /*t*/, const QString &text)
{ {
if (!(m_index.flags() & Qt::ItemIsEditable)) if (!isValid() || !(m_index.flags() & Qt::ItemIsEditable))
return; return;
view->model()->setData(m_index, text); view->model()->setData(m_index, text);
} }

View File

@ -9356,6 +9356,12 @@ bool QWidget::event(QEvent *event)
d->renderToTextureReallyDirty = 1; d->renderToTextureReallyDirty = 1;
#endif #endif
break; break;
case QEvent::PlatformSurface: {
auto surfaceEvent = static_cast<QPlatformSurfaceEvent*>(event);
if (surfaceEvent->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed)
d->setWinId(0);
break;
}
#ifndef QT_NO_PROPERTIES #ifndef QT_NO_PROPERTIES
case QEvent::DynamicPropertyChange: { case QEvent::DynamicPropertyChange: {
const QByteArray &propName = static_cast<QDynamicPropertyChangeEvent *>(event)->propertyName(); const QByteArray &propName = static_cast<QDynamicPropertyChangeEvent *>(event)->propertyName();

View File

@ -163,7 +163,8 @@ private Q_SLOTS:
void streamSerializationQJsonValue(); void streamSerializationQJsonValue();
void streamSerializationQJsonValueEmpty(); void streamSerializationQJsonValueEmpty();
void streamVariantSerialization(); void streamVariantSerialization();
void escapeSurrogateCodePoints_data();
void escapeSurrogateCodePoints();
private: private:
QString testDataDir; QString testDataDir;
}; };
@ -3085,6 +3086,9 @@ void tst_QtJson::streamSerializationQJsonValue_data()
QTest::newRow("string") << QJsonValue{QStringLiteral("bum")}; QTest::newRow("string") << QJsonValue{QStringLiteral("bum")};
QTest::newRow("array") << QJsonValue{QJsonArray{12,1,5,6,7}}; QTest::newRow("array") << QJsonValue{QJsonArray{12,1,5,6,7}};
QTest::newRow("object") << QJsonValue{QJsonObject{{"foo", 665}, {"bar", 666}}}; 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() void tst_QtJson::streamSerializationQJsonValue()
@ -3173,5 +3177,26 @@ void tst_QtJson::streamVariantSerialization()
} }
} }
void tst_QtJson::escapeSurrogateCodePoints_data()
{
QTest::addColumn<QString>("str");
QTest::addColumn<QByteArray>("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) QTEST_MAIN(tst_QtJson)
#include "tst_qtjson.moc" #include "tst_qtjson.moc"

View File

@ -525,6 +525,7 @@ private:
#ifdef Q_MOC_RUN #ifdef Q_MOC_RUN
int xx = 11'11; // digit separator must not confuse moc (QTBUG-59351) 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 #endif
private slots: private slots: