Merge remote-tracking branch 'origin/5.13' into dev

Conflicts:
	src/corelib/global/qfloat16.cpp
	src/corelib/global/qfloat16.h
	src/plugins/platforms/windows/qwindowswindow.cpp

Change-Id: I0938aaa6a9771f55e48c95ed29f6f5291431b947
This commit is contained in:
Liang Qi 2019-05-15 07:11:41 +02:00
commit c0359bd3c3
50 changed files with 768 additions and 332 deletions

View File

@ -69,11 +69,11 @@ int main(int argc, char *argv[])
QSurfaceFormat fmt;
fmt.setDepthBufferSize(24);
// Request OpenGL 3.3 compatibility or OpenGL ES 3.0.
// Request OpenGL 3.3 core or OpenGL ES 3.0.
if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) {
qDebug("Requesting 3.3 compatibility context");
qDebug("Requesting 3.3 core context");
fmt.setVersion(3, 3);
fmt.setProfile(QSurfaceFormat::CompatibilityProfile);
fmt.setProfile(QSurfaceFormat::CoreProfile);
} else {
qDebug("Requesting 3.0 context");
fmt.setVersion(3, 0);

View File

@ -211,10 +211,10 @@ CMAKE_INTERFACE_QT5_MODULE_DEPS = $$join(aux_lib_deps, ";")
mac {
!isEmpty(CMAKE_STATIC_TYPE) {
CMAKE_LIB_FILE_LOCATION_DEBUG = lib$${CMAKE_QT_STEM}_debug.a
CMAKE_LIB_FILE_LOCATION_DEBUG = lib$${CMAKE_QT_STEM}.a
CMAKE_LIB_FILE_LOCATION_RELEASE = lib$${CMAKE_QT_STEM}.a
CMAKE_PRL_FILE_LOCATION_DEBUG = lib$${CMAKE_QT_STEM}_debug.prl
CMAKE_PRL_FILE_LOCATION_DEBUG = lib$${CMAKE_QT_STEM}.prl
CMAKE_PRL_FILE_LOCATION_RELEASE = lib$${CMAKE_QT_STEM}.prl
} else {
qt_framework {
@ -222,7 +222,7 @@ mac {
CMAKE_LIB_FILE_LOCATION_RELEASE = $${CMAKE_QT_STEM}.framework/$${CMAKE_QT_STEM}
CMAKE_BUILD_IS_FRAMEWORK = "true"
} else {
CMAKE_LIB_FILE_LOCATION_DEBUG = lib$${CMAKE_QT_STEM}_debug.$$eval(QT.$${MODULE}.VERSION).dylib
CMAKE_LIB_FILE_LOCATION_DEBUG = lib$${CMAKE_QT_STEM}.$$eval(QT.$${MODULE}.VERSION).dylib
CMAKE_LIB_FILE_LOCATION_RELEASE = lib$${CMAKE_QT_STEM}.$$eval(QT.$${MODULE}.VERSION).dylib
}
}

View File

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

View File

@ -977,6 +977,9 @@
\row \li c++2a \li C++2a support is enabled. This option has no effect if
the compiler does not support C++2a, or can't select the C++ standard.
By default, support is disabled.
\row \li c++latest \li Support for the latest C++ language standard is
enabled that is supported by the compiler. By default, this option is
disabled.
\row \li strict_c++ \li Disables support for C++ compiler extensions.
By default, they are enabled.
\row \li depend_includepath \li Appending the value of INCLUDEPATH to

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 <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);
}

View File

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

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
*/
/*!
\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)
\relates <QFloat16>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -41,7 +41,9 @@
#include "qguiapplication.h"
#include "qscreen.h"
#include "qplatformintegration.h"
#include "qplatformwindow.h"
#include "private/qscreen_p.h"
#include <private/qguiapplication_p.h>
#include <QtCore/qdebug.h>
@ -376,6 +378,46 @@ QPoint QHighDpiScaling::mapPositionFromNative(const QPoint &pos, const QPlatform
return (pos - topLeft) / scaleFactor + topLeft;
}
QPoint QHighDpiScaling::mapPositionToGlobal(const QPoint &pos, const QPoint &windowGlobalPosition, const QWindow *window)
{
QPoint globalPosCandidate = pos + windowGlobalPosition;
if (QGuiApplicationPrivate::screen_list.size() <= 1)
return globalPosCandidate;
// The global position may be outside device independent screen geometry
// in cases where a window spans screens. Detect this case and map via
// native coordinates to the correct screen.
auto currentScreen = window->screen();
if (currentScreen && !currentScreen->geometry().contains(globalPosCandidate)) {
auto nativeGlobalPos = QHighDpi::toNativePixels(globalPosCandidate, currentScreen);
if (auto actualPlatformScreen = currentScreen->handle()->screenForPosition(nativeGlobalPos))
return QHighDpi::fromNativePixels(nativeGlobalPos, actualPlatformScreen->screen());
}
return globalPosCandidate;
}
QPoint QHighDpiScaling::mapPositionFromGlobal(const QPoint &pos, const QPoint &windowGlobalPosition, const QWindow *window)
{
QPoint windowPosCandidate = pos - windowGlobalPosition;
if (QGuiApplicationPrivate::screen_list.size() <= 1)
return windowPosCandidate;
// Device independent global (screen) space may discontiguous when high-dpi scaling
// is active. This means that the normal subtracting of the window global position from the
// position-to-be-mapped may not work in cases where a window spans multiple screens.
// Map both positions to native global space (using the correct screens), subtract there,
// and then map the difference back using the scale factor for the window.
QScreen *posScreen = QGuiApplication::screenAt(pos);
if (posScreen && posScreen != window->screen()) {
QPoint nativePos = QHighDpi::toNativePixels(pos, posScreen);
QPoint windowNativePos = window->handle()->geometry().topLeft();
return QHighDpi::fromNativeLocalPosition(nativePos - windowNativePos, window);
}
return windowPosCandidate;
}
qreal QHighDpiScaling::screenSubfactor(const QPlatformScreen *screen)
{
qreal factor = qreal(1.0);

View File

@ -83,8 +83,10 @@ public:
static qreal factor(const QPlatformScreen *platformScreen);
static QPoint origin(const QScreen *screen);
static QPoint origin(const QPlatformScreen *platformScreen);
static QPoint mapPositionFromNative(const QPoint &pos, const QPlatformScreen *platformScreen);
static QPoint mapPositionToNative(const QPoint &pos, const QPlatformScreen *platformScreen);
static QPoint mapPositionFromNative(const QPoint &pos, const QPlatformScreen *platformScreen);
static QPoint mapPositionToGlobal(const QPoint &pos, const QPoint &windowGlobalPosition, const QWindow *window);
static QPoint mapPositionFromGlobal(const QPoint &pos, const QPoint &windowGlobalPosition, const QWindow *window);
static QDpi logicalDpi();
private:

View File

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

View File

@ -113,7 +113,7 @@ public:
void connectToScreen(QScreen *topLevelScreen);
void disconnectFromScreen();
void emitScreenChangedRecursion(QScreen *newScreen);
QScreen *screenForGeometry(const QRect &rect);
QScreen *screenForGeometry(const QRect &rect) const;
void setTransientParent(QWindow *parent);
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)
{
int i = 0;
if ((_MM_GET_EXCEPTION_MASK() & _MM_MASK_INVALID) == 0) {
for (; i < count; ++i) {
uint v = qUnpremultiply(src[i]);
if (RGBx)
v = 0xff000000 | v;
if (RGBA)
v = ARGB2RGBA(v);
buffer[i] = v;
}
return;
}
const __m128i alphaMask = _mm_set1_epi32(0xff000000);
const __m128i rgbaMask = _mm_setr_epi8(2, 1, 0, 3, 6, 5, 4, 7, 10, 9, 8, 11, 14, 13, 12, 15);
const __m128i zero = _mm_setzero_si128();
@ -223,6 +234,13 @@ template<bool RGBA>
static inline void convertARGBFromRGBA64PM_sse4(uint *buffer, const QRgba64 *src, int count)
{
int i = 0;
if ((_MM_GET_EXCEPTION_MASK() & _MM_MASK_INVALID) == 0) {
for (; i < count; ++i) {
const QRgba64 v = src[i].unpremultiplied();
buffer[i] = RGBA ? toRgba8888(v) : toArgb32(v);
}
return;
}
const __m128i alphaMask = _mm_set1_epi64x(qint64(Q_UINT64_C(0xffff) << 48));
const __m128i alphaMask32 = _mm_set1_epi32(0xff000000);
const __m128i rgbaMask = _mm_setr_epi8(2, 1, 0, 3, 6, 5, 4, 7, 10, 9, 8, 11, 14, 13, 12, 15);

View File

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

View File

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

View File

@ -81,6 +81,7 @@ public:
QPlatformTextureList *textures, bool translucentBackground) override;
#endif
QImage toImage() const override;
QPlatformGraphicsBuffer *graphicsBuffer() const override;
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)
: QPlatformBackingStore(window)
{
@ -534,6 +522,21 @@ void QCALayerBackingStore::composeAndFlush(QWindow *window, const QRegion &regio
}
#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
{
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;
}
// 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)

View File

@ -63,7 +63,7 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcQpaWindow, "qt.qpa.window");
Q_LOGGING_CATEGORY(lcQpaDrawing, "qt.qpa.drawing");
Q_LOGGING_CATEGORY(lcQpaMouse, "qt.qpa.input.mouse", QtCriticalMsg);
Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen");
Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen", QtCriticalMsg);
//
// Conversion Functions

View File

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

View File

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

View File

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

View File

@ -41,6 +41,7 @@
#include "qcocoawindow.h"
#include "qcocoahelpers.h"
#include "qcocoaintegration.h"
#include <QtCore/qcoreapplication.h>
#include <QtGui/private/qcoregraphics_p.h>
@ -53,18 +54,99 @@
QT_BEGIN_NAMESPACE
class QCoreTextFontEngine;
class QFontEngineFT;
void QCocoaScreen::initializeScreens()
{
uint32_t displayCount = 0;
if (CGGetActiveDisplayList(0, nullptr, &displayCount) != kCGErrorSuccess)
qFatal("Failed to get number of active displays");
QCocoaScreen::QCocoaScreen(int screenIndex)
: QPlatformScreen(), m_screenIndex(screenIndex), m_refreshRate(60.0)
CGDirectDisplayID activeDisplays[displayCount];
if (CGGetActiveDisplayList(displayCount, &activeDisplays[0], &displayCount) != kCGErrorSuccess)
qFatal("Failed to get active displays");
for (CGDirectDisplayID displayId : activeDisplays)
QCocoaScreen::add(displayId);
CGDisplayRegisterReconfigurationCallback([](CGDirectDisplayID displayId, CGDisplayChangeSummaryFlags flags, void *userInfo) {
if (flags & kCGDisplayBeginConfigurationFlag)
return; // Wait for changes to apply
Q_UNUSED(userInfo);
QCocoaScreen *cocoaScreen = QCocoaScreen::get(displayId);
if ((flags & kCGDisplayAddFlag) || !cocoaScreen) {
if (!CGDisplayIsActive(displayId)) {
qCDebug(lcQpaScreen) << "Not adding inactive display" << displayId;
return; // Will be added when activated
}
QCocoaScreen::add(displayId);
} else if ((flags & kCGDisplayRemoveFlag) || !CGDisplayIsActive(displayId)) {
cocoaScreen->remove();
} else {
// Detect changes to the primary screen immediately, instead of
// waiting for a display reconfigure with kCGDisplaySetMainFlag.
// This ensures that any property updates to the other screens
// will be in reference to the correct primary screen.
QCocoaScreen *mainDisplay = QCocoaScreen::get(CGMainDisplayID());
if (QGuiApplication::primaryScreen()->handle() != mainDisplay) {
mainDisplay->updateProperties();
qCInfo(lcQpaScreen) << "Primary screen changed to" << mainDisplay;
QWindowSystemInterface::handlePrimaryScreenChanged(mainDisplay);
}
if (cocoaScreen == mainDisplay)
return; // Already reconfigured
cocoaScreen->updateProperties();
qCInfo(lcQpaScreen) << "Reconfigured" << cocoaScreen;
}
}, nullptr);
}
void QCocoaScreen::add(CGDirectDisplayID displayId)
{
QCocoaScreen *cocoaScreen = new QCocoaScreen(displayId);
qCInfo(lcQpaScreen) << "Adding" << cocoaScreen;
QWindowSystemInterface::handleScreenAdded(cocoaScreen, CGDisplayIsMain(displayId));
}
QCocoaScreen::QCocoaScreen(CGDirectDisplayID displayId)
: QPlatformScreen(), m_displayId(displayId)
{
updateProperties();
m_cursor = new QCocoaCursor;
}
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()
{
Q_ASSERT_X(!screen(), "QCocoaScreen", "QScreen should be deleted first");
delete m_cursor;
CVDisplayLinkRelease(m_displayLink);
@ -72,17 +154,6 @@ QCocoaScreen::~QCocoaScreen()
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)
{
QIOType<io_iterator_t> iterator;
@ -117,35 +188,37 @@ static QString displayName(CGDirectDisplayID displayID)
void QCocoaScreen::updateProperties()
{
NSScreen *nsScreen = nativeScreen();
if (!nsScreen)
return;
Q_ASSERT(m_displayId);
const QRect previousGeometry = m_geometry;
const QRect previousAvailableGeometry = m_availableGeometry;
const QDpi previousLogicalDpi = m_logicalDpi;
const qreal previousRefreshRate = m_refreshRate;
// Some properties are only available via NSScreen
NSScreen *nsScreen = nativeScreen();
Q_ASSERT(nsScreen);
// The reference screen for the geometry is always the primary screen
QRectF primaryScreenGeometry = QRectF::fromCGRect([[NSScreen screens] firstObject].frame);
QRectF primaryScreenGeometry = QRectF::fromCGRect(CGDisplayBounds(CGMainDisplayID()));
m_geometry = qt_mac_flip(QRectF::fromCGRect(nsScreen.frame), primaryScreenGeometry).toRect();
m_availableGeometry = qt_mac_flip(QRectF::fromCGRect(nsScreen.visibleFrame), primaryScreenGeometry).toRect();
m_format = QImage::Format_RGB32;
m_depth = NSBitsPerPixelFromDepth([nsScreen depth]);
m_devicePixelRatio = nsScreen.backingScaleFactor;
CGDirectDisplayID dpy = nsScreen.qt_displayId;
CGSize size = CGDisplayScreenSize(dpy);
m_format = QImage::Format_RGB32;
m_depth = NSBitsPerPixelFromDepth(nsScreen.depth);
CGSize size = CGDisplayScreenSize(m_displayId);
m_physicalSize = QSizeF(size.width, size.height);
m_logicalDpi.first = 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;
@ -155,24 +228,6 @@ void QCocoaScreen::updateProperties()
QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen(), m_logicalDpi.first, m_logicalDpi.second);
if (m_refreshRate != previousRefreshRate)
QWindowSystemInterface::handleScreenRefreshRateChange(screen(), m_refreshRate);
qCDebug(lcQpaScreen) << "Updated properties for" << this;
if (didChangeGeometry) {
// When a screen changes its geometry, AppKit will send us a NSWindowDidMoveNotification
// for each window, resulting in calls to handleGeometryChange(), but this happens before
// the NSApplicationDidChangeScreenParametersNotification, so when we map the new geometry
// (which is correct at that point) to the screen using QCocoaScreen::mapFromNative(), we
// end up using the stale screen geometry, and the new window geometry we report is wrong.
// To make sure we finally report the correct window geometry, we need to do another pass
// of geometry reporting, now that the screen properties have been updates. FIXME: Ideally
// this would be solved by not caching the screen properties in QCocoaScreen, but that
// requires more research.
for (QWindow *window : windows()) {
if (QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow*>(window->handle()))
cocoaWindow->handleGeometryChange();
}
}
}
// ----------------------- Display link -----------------------
@ -181,8 +236,10 @@ Q_LOGGING_CATEGORY(lcQpaScreenUpdates, "qt.qpa.screen.updates", QtCriticalMsg);
void QCocoaScreen::requestUpdate()
{
Q_ASSERT(m_displayId);
if (!m_displayLink) {
CVDisplayLinkCreateWithCGDisplay(nativeScreen().qt_displayId, &m_displayLink);
CVDisplayLinkCreateWithCGDisplay(m_displayId, &m_displayLink);
CVDisplayLinkSetOutputCallback(m_displayLink, [](CVDisplayLinkRef, const CVTimeStamp*,
const CVTimeStamp*, CVOptionFlags, CVOptionFlags*, void* displayLinkContext) -> int {
// FIXME: It would be nice if update requests would include timing info
@ -269,6 +326,9 @@ struct DeferredDebugHelper
void QCocoaScreen::deliverUpdateRequests()
{
if (!m_displayId)
return; // Screen removed
QMacAutoReleasePool pool;
// The CVDisplayLink callback is a notification that it's a good time to produce a new frame.
@ -283,7 +343,7 @@ void QCocoaScreen::deliverUpdateRequests()
const int pendingUpdates = ++m_pendingUpdates;
DeferredDebugHelper screenUpdates(lcQpaScreenUpdates());
qDeferredDebug(screenUpdates) << "display link callback for screen " << m_screenIndex;
qDeferredDebug(screenUpdates) << "display link callback for screen " << m_displayId;
if (const int framesAheadOfDelivery = pendingUpdates - 1) {
// If we have more than one update pending it means that a previous display link callback
@ -370,13 +430,6 @@ bool QCocoaScreen::isRunningDisplayLink() const
// -----------------------------------------------------------
qreal QCocoaScreen::devicePixelRatio() const
{
QMacAutoReleasePool pool;
NSScreen *nsScreen = nativeScreen();
return qreal(nsScreen ? [nsScreen backingScaleFactor] : 1.0);
}
QPlatformScreen::SubpixelAntialiasingType QCocoaScreen::subpixelAntialiasingTypeHint() const
{
QPlatformScreen::SubpixelAntialiasingType type = QPlatformScreen::subpixelAntialiasingTypeHint();
@ -430,7 +483,7 @@ QPixmap QCocoaScreen::grabWindow(WId view, int x, int y, int width, int height)
{
// Determine the grab rect. FIXME: The rect should be bounded by the view's
// geometry, but note that for the pixeltool use case that window will be the
// desktop widgets's view, which currently gets resized to fit one screen
// desktop widget's view, which currently gets resized to fit one screen
// only, since its NSWindow has the NSWindowStyleMaskTitled flag set.
Q_UNUSED(view);
QRect grabRect = QRect(x, y, width, height);
@ -482,7 +535,7 @@ QPixmap QCocoaScreen::grabWindow(WId view, int x, int y, int width, int height)
for (uint i = 0; i < displayCount; ++i)
dpr = qMax(dpr, images.at(i).devicePixelRatio());
// Alocate target pixmap and draw each screen's content
// Allocate target pixmap and draw each screen's content
qCDebug(lcQpaScreen) << "Create grap pixmap" << grabRect.size() << "at devicePixelRatio" << dpr;
QPixmap windowPixmap(grabRect.size() * dpr);
windowPixmap.setDevicePixelRatio(dpr);
@ -499,7 +552,57 @@ QPixmap QCocoaScreen::grabWindow(WId view, int x, int y, int width, int height)
*/
QCocoaScreen *QCocoaScreen::primaryScreen()
{
return static_cast<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)
@ -533,11 +636,10 @@ QDebug operator<<(QDebug debug, const QCocoaScreen *screen)
debug.nospace();
debug << "QCocoaScreen(" << (const void *)screen;
if (screen) {
debug << ", index=" << screen->m_screenIndex;
debug << ", native=" << screen->nativeScreen();
debug << ", geometry=" << screen->geometry();
debug << ", dpr=" << screen->devicePixelRatio();
debug << ", name=" << screen->name();
debug << ", native=" << screen->nativeScreen();
}
debug << ')';
return debug;

View File

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

View File

@ -1086,9 +1086,11 @@ void QCocoaWindow::setEmbeddedInForeignView()
void QCocoaWindow::viewDidChangeFrame()
{
if (isContentView())
return; // Handled below
// Note: When the view is the content view, it would seem redundant
// to deliver geometry changes both from windowDidResize and this
// callback, but in some cases such as when macOS native tabbed
// windows are enabled we may end up with the wrong geometry in
// the initial windowDidResize callback when a new tab is created.
handleGeometryChange();
}
@ -1209,17 +1211,17 @@ void QCocoaWindow::windowDidChangeScreen()
return;
// Note: When a window is resized to 0x0 Cocoa will report the window's screen as nil
auto *currentScreen = QCocoaIntegration::instance()->screenForNSScreen(m_view.window.screen);
auto *currentScreen = QCocoaScreen::get(m_view.window.screen);
auto *previousScreen = static_cast<QCocoaScreen*>(screen());
Q_ASSERT_X(!m_view.window.screen || currentScreen,
"QCocoaWindow", "Failed to get QCocoaScreen for NSScreen");
// Note: The previous screen may be the same as the current screen, either because
// the screen was just reconfigured, which still results in AppKit sending an
// NSWindowDidChangeScreenNotification, because the previous screen was removed,
// a) the screen was just reconfigured, which still results in AppKit sending an
// NSWindowDidChangeScreenNotification, b) because the previous screen was removed,
// and we ended up calling QWindow::setScreen to move the window, which doesn't
// actually move the window to the new screen, or because we've delivered the
// actually move the window to the new screen, or c) because we've delivered the
// screen change to the top level window, which will make all the child windows
// of that window report the new screen when requested via QWindow::screen().
// We still need to deliver the screen change in all these cases, as the

View File

@ -189,6 +189,15 @@
- (void)displayLayer:(CALayer *)layer
{
if (!NSThread.isMainThread) {
// Qt is calling AppKit APIs such as -[NSOpenGLContext setView:] on secondary threads,
// which we shouldn't do. This may result in AppKit (wrongly) triggering a display on
// the thread where we made the call, so block it here and defer to the main thread.
qCWarning(lcQpaDrawing) << "Display non non-main thread! Deferring to main thread";
dispatch_async(dispatch_get_main_queue(), ^{ self.needsDisplay = YES; });
return;
}
Q_ASSERT(layer == self.layer);
if (!m_platformWindow)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4290,12 +4290,15 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter
alpha:pc.alphaF()];
s = qt_mac_removeMnemonics(s);
const auto textRect = CGRectMake(xpos, yPos, mi->rect.width() - xm - tabwidth + 1, mi->rect.height());
QMacCGContext cgCtx(p);
d->setupNSGraphicsContext(cgCtx, YES);
[s.toNSString() drawInRect:textRect
// Draw at point instead of in rect, as the rect we've computed for the menu item
// is based on the font metrics we got from HarfBuzz, so we may risk having CoreText
// line-break the string if it doesn't fit the given rect. It's better to draw outside
// the rect and possibly overlap something than to have part of the text disappear.
[s.toNSString() drawAtPoint:CGPointMake(xpos, yPos)
withAttributes:@{ NSFontAttributeName:f, NSForegroundColorAttributeName:c,
NSObliquenessAttributeName: [NSNumber numberWithDouble: myFont.italic() ? 0.3 : 0.0]}];

View File

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

View File

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

View File

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

View File

@ -163,7 +163,8 @@ private Q_SLOTS:
void streamSerializationQJsonValue();
void streamSerializationQJsonValueEmpty();
void streamVariantSerialization();
void escapeSurrogateCodePoints_data();
void escapeSurrogateCodePoints();
private:
QString testDataDir;
};
@ -3085,6 +3086,9 @@ void tst_QtJson::streamSerializationQJsonValue_data()
QTest::newRow("string") << QJsonValue{QStringLiteral("bum")};
QTest::newRow("array") << QJsonValue{QJsonArray{12,1,5,6,7}};
QTest::newRow("object") << QJsonValue{QJsonObject{{"foo", 665}, {"bar", 666}}};
// test json escape sequence
QTest::newRow("array with 0xD800") << QJsonValue(QJsonArray{QString(0xD800)});
QTest::newRow("array with 0xDF06,0xD834") << QJsonValue(QJsonArray{QString(0xDF06).append(0xD834)});
}
void tst_QtJson::streamSerializationQJsonValue()
@ -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)
#include "tst_qtjson.moc"

View File

@ -525,6 +525,7 @@ private:
#ifdef Q_MOC_RUN
int xx = 11'11; // digit separator must not confuse moc (QTBUG-59351)
int xx = 0b11'11; // digit separator in a binary literal must not confuse moc (QTBUG-75656)
#endif
private slots: