summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms')
-rw-r--r--src/plugins/platforms/android/qandroidinputcontext.cpp81
-rw-r--r--src/plugins/platforms/cocoa/qcocoabackingstore.h3
-rw-r--r--src/plugins/platforms/cocoa/qcocoabackingstore.mm29
-rw-r--r--src/plugins/platforms/cocoa/qcocoaglcontext.h2
-rw-r--r--src/plugins/platforms/cocoa/qcocoaglcontext.mm8
-rw-r--r--src/plugins/platforms/cocoa/qcocoahelpers.h12
-rw-r--r--src/plugins/platforms/cocoa/qcocoahelpers.mm7
-rw-r--r--src/plugins/platforms/cocoa/qcocoaintegration.h7
-rw-r--r--src/plugins/platforms/cocoa/qcocoaintegration.mm98
-rw-r--r--src/plugins/platforms/cocoa/qcocoascreen.h31
-rw-r--r--src/plugins/platforms/cocoa/qcocoascreen.mm220
-rw-r--r--src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm6
-rw-r--r--src/plugins/platforms/cocoa/qcocoatheme.h5
-rw-r--r--src/plugins/platforms/cocoa/qcocoatheme.mm62
-rw-r--r--src/plugins/platforms/cocoa/qcocoavulkaninstance.mm2
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindow.mm53
-rw-r--r--src/plugins/platforms/cocoa/qmacclipboard.mm15
-rw-r--r--src/plugins/platforms/cocoa/qnsview_drawing.mm9
-rw-r--r--src/plugins/platforms/cocoa/qnsview_mouse.mm49
-rw-r--r--src/plugins/platforms/cocoa/qnswindowdelegate.mm4
-rw-r--r--src/plugins/platforms/direct2d/qwindowsdirect2dhelpers.h7
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_openwfd/qeglfsopenwfdintegration.cpp2
-rw-r--r--src/plugins/platforms/ios/qiostextinputoverlay.mm11
-rw-r--r--src/plugins/platforms/wasm/qtloader.js14
-rw-r--r--src/plugins/platforms/wasm/qwasmintegration.cpp30
-rw-r--r--src/plugins/platforms/wasm/qwasmintegration.h7
-rw-r--r--src/plugins/platforms/wasm/qwasmopenglcontext.cpp16
-rw-r--r--src/plugins/platforms/wasm/qwasmscreen.cpp12
-rw-r--r--src/plugins/platforms/wasm/qwasmscreen.h1
-rw-r--r--src/plugins/platforms/wasm/wasm_shell.html6
-rw-r--r--src/plugins/platforms/windows/qwindowscontext.cpp46
-rw-r--r--src/plugins/platforms/windows/qwindowscontext.h4
-rw-r--r--src/plugins/platforms/windows/qwindowsintegration.cpp3
-rw-r--r--src/plugins/platforms/windows/qwindowsmousehandler.cpp45
-rw-r--r--src/plugins/platforms/windows/qwindowsmousehandler.h8
-rw-r--r--src/plugins/platforms/windows/qwindowspointerhandler.cpp45
-rw-r--r--src/plugins/platforms/windows/qwindowspointerhandler.h5
-rw-r--r--src/plugins/platforms/windows/qwindowsscreen.cpp14
-rw-r--r--src/plugins/platforms/windows/qwindowsscreen.h2
-rw-r--r--src/plugins/platforms/windows/qwindowstabletsupport.cpp72
-rw-r--r--src/plugins/platforms/windows/qwindowstabletsupport.h2
-rw-r--r--src/plugins/platforms/windows/qwindowswindow.cpp277
-rw-r--r--src/plugins/platforms/windows/qwindowswindow.h34
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp27
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp2
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiawindowprovider.cpp168
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiawindowprovider.h73
-rw-r--r--src/plugins/platforms/windows/uiautomation/uiautomation.pri2
-rw-r--r--src/plugins/platforms/winrt/qwinrttheme.cpp18
-rw-r--r--src/plugins/platforms/winrt/qwinrttheme.h1
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_xi2.cpp4
-rw-r--r--src/plugins/platforms/xcb/qxcbdrag.cpp2
52 files changed, 1209 insertions, 454 deletions
diff --git a/src/plugins/platforms/android/qandroidinputcontext.cpp b/src/plugins/platforms/android/qandroidinputcontext.cpp
index 00ab3409d3..db40c30d7d 100644
--- a/src/plugins/platforms/android/qandroidinputcontext.cpp
+++ b/src/plugins/platforms/android/qandroidinputcontext.cpp
@@ -791,7 +791,7 @@ void QAndroidInputContext::longPress(int x, int y)
return;
}
QList<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);
@@ -1129,46 +1129,63 @@ QString QAndroidInputContext::getSelectedText(jint /*flags*/)
QString QAndroidInputContext::getTextAfterCursor(jint length, jint /*flags*/)
{
- //### the preedit text could theoretically be after the cursor
- QVariant textAfter = QInputMethod::queryFocusObject(Qt::ImTextAfterCursor, QVariant(length));
- if (textAfter.isValid()) {
- return textAfter.toString().left(length);
- }
-
- //compatibility code for old controls that do not implement the new API
- QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
- if (query.isNull())
+ if (length <= 0)
return QString();
- QString text = query->value(Qt::ImSurroundingText).toString();
- if (!text.length())
- return text;
+ QString text;
- int cursorPos = query->value(Qt::ImCursorPosition).toInt();
- return text.mid(cursorPos, length);
+ QVariant reportedTextAfter = QInputMethod::queryFocusObject(Qt::ImTextAfterCursor, length);
+ if (reportedTextAfter.isValid()) {
+ text = reportedTextAfter.toString();
+ } else {
+ // Compatibility code for old controls that do not implement the new API
+ QSharedPointer<QInputMethodQueryEvent> query =
+ focusObjectInputMethodQuery(Qt::ImCursorPosition | Qt::ImSurroundingText);
+ if (query) {
+ const int cursorPos = query->value(Qt::ImCursorPosition).toInt();
+ text = query->value(Qt::ImSurroundingText).toString().mid(cursorPos);
+ }
+ }
+
+ // Controls do not report preedit text, so we have to add it
+ if (!m_composingText.isEmpty()) {
+ const int cursorPosInsidePreedit = m_composingCursor - m_composingTextStart;
+ text = m_composingText.midRef(cursorPosInsidePreedit) + text;
+ }
+
+ text.truncate(length);
+ return text;
}
QString QAndroidInputContext::getTextBeforeCursor(jint length, jint /*flags*/)
{
- QVariant textBefore = QInputMethod::queryFocusObject(Qt::ImTextBeforeCursor, QVariant(length));
- if (textBefore.isValid())
- return textBefore.toString().rightRef(length) + m_composingText;
-
- //compatibility code for old controls that do not implement the new API
- QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
- if (query.isNull())
+ if (length <= 0)
return QString();
- int cursorPos = query->value(Qt::ImCursorPosition).toInt();
- QString text = query->value(Qt::ImSurroundingText).toString();
- if (!text.length())
- return text;
+ QString text;
- //### the preedit text does not need to be immediately before the cursor
- if (cursorPos <= length)
- return text.leftRef(cursorPos) + m_composingText;
- else
- return text.midRef(cursorPos - length, length) + m_composingText;
+ QVariant reportedTextBefore = QInputMethod::queryFocusObject(Qt::ImTextBeforeCursor, length);
+ if (reportedTextBefore.isValid()) {
+ text = reportedTextBefore.toString();
+ } else {
+ // Compatibility code for old controls that do not implement the new API
+ QSharedPointer<QInputMethodQueryEvent> query =
+ focusObjectInputMethodQuery(Qt::ImCursorPosition | Qt::ImSurroundingText);
+ if (query) {
+ const int cursorPos = query->value(Qt::ImCursorPosition).toInt();
+ text = query->value(Qt::ImSurroundingText).toString().left(cursorPos);
+ }
+ }
+
+ // Controls do not report preedit text, so we have to add it
+ if (!m_composingText.isEmpty()) {
+ const int cursorPosInsidePreedit = m_composingCursor - m_composingTextStart;
+ text += m_composingText.leftRef(cursorPosInsidePreedit);
+ }
+
+ if (text.length() > length)
+ text = text.right(length);
+ return text;
}
/*
@@ -1231,6 +1248,8 @@ jboolean QAndroidInputContext::setComposingRegion(jint start, jint end)
if (query.isNull())
return JNI_FALSE;
+ if (start == end)
+ return JNI_TRUE;
if (start > end)
qSwap(start, end);
diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.h b/src/plugins/platforms/cocoa/qcocoabackingstore.h
index 6f24598250..acddc3ecc8 100644
--- a/src/plugins/platforms/cocoa/qcocoabackingstore.h
+++ b/src/plugins/platforms/cocoa/qcocoabackingstore.h
@@ -76,9 +76,12 @@ public:
void endPaint() override;
void flush(QWindow *, const QRegion &, const QPoint &) override;
+#ifndef QT_NO_OPENGL
void composeAndFlush(QWindow *window, const QRegion &region, const QPoint &offset,
QPlatformTextureList *textures, bool translucentBackground) override;
+#endif
+ QImage toImage() const override;
QPlatformGraphicsBuffer *graphicsBuffer() const override;
private:
diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm
index d42a723b47..c381f87844 100644
--- a/src/plugins/platforms/cocoa/qcocoabackingstore.mm
+++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm
@@ -273,18 +273,6 @@ void QNSWindowBackingStore::redrawRoundedBottomCorners(CGRect windowRect) const
// ----------------------------------------------------------------------------
-// https://stackoverflow.com/a/52722575/2761869
-template<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)
{
@@ -523,6 +511,7 @@ void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion &region,
// the window server.
}
+#ifndef QT_NO_OPENGL
void QCALayerBackingStore::composeAndFlush(QWindow *window, const QRegion &region, const QPoint &offset,
QPlatformTextureList *textures, bool translucentBackground)
{
@@ -531,6 +520,22 @@ void QCALayerBackingStore::composeAndFlush(QWindow *window, const QRegion &regio
QPlatformBackingStore::composeAndFlush(window, region, offset, textures, translucentBackground);
}
+#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
{
diff --git a/src/plugins/platforms/cocoa/qcocoaglcontext.h b/src/plugins/platforms/cocoa/qcocoaglcontext.h
index eefb1cd0fb..c1041ac2da 100644
--- a/src/plugins/platforms/cocoa/qcocoaglcontext.h
+++ b/src/plugins/platforms/cocoa/qcocoaglcontext.h
@@ -80,7 +80,7 @@ private:
NSOpenGLContext *m_shareContext = nil;
QSurfaceFormat m_format;
bool m_didCheckForSoftwareContext = false;
- QVarLengthArray<QMacScopedObserver, 3> m_updateObservers;
+ QVarLengthArray<QMacNotificationObserver, 3> m_updateObservers;
QAtomicInt m_needsUpdate = false;
};
diff --git a/src/plugins/platforms/cocoa/qcocoaglcontext.mm b/src/plugins/platforms/cocoa/qcocoaglcontext.mm
index fe1fc31553..0f8fec0548 100644
--- a/src/plugins/platforms/cocoa/qcocoaglcontext.mm
+++ b/src/plugins/platforms/cocoa/qcocoaglcontext.mm
@@ -404,13 +404,13 @@ bool QCocoaGLContext::setDrawable(QPlatformSurface *surface)
m_updateObservers.clear();
if (view.layer) {
- m_updateObservers.append(QMacScopedObserver(view, NSViewFrameDidChangeNotification, updateCallback));
- m_updateObservers.append(QMacScopedObserver(view.window, NSWindowDidChangeScreenNotification, updateCallback));
+ m_updateObservers.append(QMacNotificationObserver(view, NSViewFrameDidChangeNotification, updateCallback));
+ m_updateObservers.append(QMacNotificationObserver(view.window, NSWindowDidChangeScreenNotification, updateCallback));
} else {
- m_updateObservers.append(QMacScopedObserver(view, NSViewGlobalFrameDidChangeNotification, updateCallback));
+ m_updateObservers.append(QMacNotificationObserver(view, NSViewGlobalFrameDidChangeNotification, updateCallback));
}
- m_updateObservers.append(QMacScopedObserver([NSApplication sharedApplication],
+ m_updateObservers.append(QMacNotificationObserver([NSApplication sharedApplication],
NSApplicationDidChangeScreenParametersNotification, updateCallback));
// If any of the observers fire at this point it's fine. We check the
diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.h b/src/plugins/platforms/cocoa/qcocoahelpers.h
index 69aa7937b6..69a1854598 100644
--- a/src/plugins/platforms/cocoa/qcocoahelpers.h
+++ b/src/plugins/platforms/cocoa/qcocoahelpers.h
@@ -176,6 +176,18 @@ T qt_mac_resolveOption(const T &fallback, QWindow *window, const QByteArray &pro
return fallback;
}
+// https://stackoverflow.com/a/52722575/2761869
+template<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)
diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm
index 9c705616ba..c9eafa81d0 100644
--- a/src/plugins/platforms/cocoa/qcocoahelpers.mm
+++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm
@@ -63,7 +63,7 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcQpaWindow, "qt.qpa.window");
Q_LOGGING_CATEGORY(lcQpaDrawing, "qt.qpa.drawing");
Q_LOGGING_CATEGORY(lcQpaMouse, "qt.qpa.input.mouse", QtCriticalMsg);
-Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen");
+Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen", QtCriticalMsg);
//
// Conversion Functions
@@ -297,13 +297,12 @@ Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum)
*/
Qt::MouseButton cocoaButton2QtButton(NSEvent *event)
{
- switch (event.type) {
- case NSEventTypeMouseMoved:
+ if (cocoaEvent2QtMouseEvent(event) == QEvent::MouseMove)
return Qt::NoButton;
+ switch (event.type) {
case NSEventTypeRightMouseUp:
case NSEventTypeRightMouseDown:
- case NSEventTypeRightMouseDragged:
return Qt::RightButton;
default:
diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.h b/src/plugins/platforms/cocoa/qcocoaintegration.h
index 04cb4e1226..bfc3bfe9de 100644
--- a/src/plugins/platforms/cocoa/qcocoaintegration.h
+++ b/src/plugins/platforms/cocoa/qcocoaintegration.h
@@ -61,8 +61,6 @@
QT_BEGIN_NAMESPACE
-class QCocoaScreen;
-
class QCocoaIntegration : public QObject, public QPlatformIntegration
{
Q_OBJECT
@@ -113,9 +111,6 @@ public:
Qt::KeyboardModifiers queryKeyboardModifiers() const override;
QList<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
diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm
index fb3d05d3e4..300082d694 100644
--- a/src/plugins/platforms/cocoa/qcocoaintegration.mm
+++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm
@@ -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) {
@@ -490,8 +403,13 @@ QCocoaServices *QCocoaIntegration::services() const
QVariant QCocoaIntegration::styleHint(StyleHint hint) const
{
- if (hint == QPlatformIntegration::FontSmoothingGamma)
+ switch (hint) {
+ case FontSmoothingGamma:
return QCoreTextFontEngine::fontSmoothingGamma();
+ case ShowShortcutsInContextMenus:
+ return QVariant(false);
+ default: break;
+ }
return QPlatformIntegration::styleHint(hint);
}
diff --git a/src/plugins/platforms/cocoa/qcocoascreen.h b/src/plugins/platforms/cocoa/qcocoascreen.h
index 9ded98df32..491af2fe9c 100644
--- a/src/plugins/platforms/cocoa/qcocoascreen.h
+++ b/src/plugins/platforms/cocoa/qcocoascreen.h
@@ -48,10 +48,14 @@
QT_BEGIN_NAMESPACE
+class QCocoaIntegration;
+
class QCocoaScreen : public QPlatformScreen
{
public:
- QCocoaScreen(int screenIndex);
+ static void initializeScreens();
+ static void cleanupScreens();
+
~QCocoaScreen();
// ----------------------------------------------------
@@ -61,19 +65,18 @@ public:
QRect availableGeometry() const override { return m_availableGeometry; }
int depth() const override { return m_depth; }
QImage::Format format() const override { return m_format; }
- qreal devicePixelRatio() const override;
+ qreal devicePixelRatio() const override { return m_devicePixelRatio; }
QSizeF physicalSize() const override { return m_physicalSize; }
QDpi logicalDpi() const override { return m_logicalDpi; }
qreal refreshRate() const override { return m_refreshRate; }
QString name() const override { return m_name; }
QPlatformCursor *cursor() const override { return m_cursor; }
QWindow *topLevelAt(const QPoint &point) const override;
- QList<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
diff --git a/src/plugins/platforms/cocoa/qcocoascreen.mm b/src/plugins/platforms/cocoa/qcocoascreen.mm
index 6a5b0e6e3e..392099d083 100644
--- a/src/plugins/platforms/cocoa/qcocoascreen.mm
+++ b/src/plugins/platforms/cocoa/qcocoascreen.mm
@@ -41,6 +41,7 @@
#include "qcocoawindow.h"
#include "qcocoahelpers.h"
+#include "qcocoaintegration.h"
#include <QtCore/qcoreapplication.h>
#include <QtGui/private/qcoregraphics_p.h>
@@ -53,34 +54,104 @@
QT_BEGIN_NAMESPACE
-class QCoreTextFontEngine;
-class QFontEngineFT;
+void QCocoaScreen::initializeScreens()
+{
+ uint32_t displayCount = 0;
+ if (CGGetActiveDisplayList(0, nullptr, &displayCount) != kCGErrorSuccess)
+ qFatal("Failed to get number of active displays");
+
+ CGDirectDisplayID activeDisplays[displayCount];
+ if (CGGetActiveDisplayList(displayCount, &activeDisplays[0], &displayCount) != kCGErrorSuccess)
+ qFatal("Failed to get active displays");
+
+ for (CGDirectDisplayID displayId : activeDisplays)
+ QCocoaScreen::add(displayId);
+
+ CGDisplayRegisterReconfigurationCallback([](CGDirectDisplayID displayId, CGDisplayChangeSummaryFlags flags, void *userInfo) {
+ if (flags & kCGDisplayBeginConfigurationFlag)
+ return; // Wait for changes to apply
+
+ Q_UNUSED(userInfo);
+
+ QCocoaScreen *cocoaScreen = QCocoaScreen::get(displayId);
+
+ if ((flags & kCGDisplayAddFlag) || !cocoaScreen) {
+ if (!CGDisplayIsActive(displayId)) {
+ qCDebug(lcQpaScreen) << "Not adding inactive display" << displayId;
+ return; // Will be added when activated
+ }
+ QCocoaScreen::add(displayId);
+ } else if ((flags & kCGDisplayRemoveFlag) || !CGDisplayIsActive(displayId)) {
+ cocoaScreen->remove();
+ } else {
+ // Detect changes to the primary screen immediately, instead of
+ // waiting for a display reconfigure with kCGDisplaySetMainFlag.
+ // This ensures that any property updates to the other screens
+ // will be in reference to the correct primary screen.
+ QCocoaScreen *mainDisplay = QCocoaScreen::get(CGMainDisplayID());
+ if (QGuiApplication::primaryScreen()->handle() != mainDisplay) {
+ mainDisplay->updateProperties();
+ qCInfo(lcQpaScreen) << "Primary screen changed to" << mainDisplay;
+ QWindowSystemInterface::handlePrimaryScreenChanged(mainDisplay);
+ }
-QCocoaScreen::QCocoaScreen(int screenIndex)
- : QPlatformScreen(), m_screenIndex(screenIndex), m_refreshRate(60.0)
+ if (cocoaScreen == mainDisplay)
+ return; // Already reconfigured
+
+ cocoaScreen->updateProperties();
+ qCInfo(lcQpaScreen) << "Reconfigured" << cocoaScreen;
+ }
+ }, nullptr);
+}
+
+void QCocoaScreen::add(CGDirectDisplayID displayId)
+{
+ QCocoaScreen *cocoaScreen = new QCocoaScreen(displayId);
+ qCInfo(lcQpaScreen) << "Adding" << cocoaScreen;
+ QWindowSystemInterface::handleScreenAdded(cocoaScreen, CGDisplayIsMain(displayId));
+}
+
+QCocoaScreen::QCocoaScreen(CGDirectDisplayID displayId)
+ : QPlatformScreen(), m_displayId(displayId)
{
updateProperties();
m_cursor = new QCocoaCursor;
}
-QCocoaScreen::~QCocoaScreen()
+void QCocoaScreen::cleanupScreens()
{
- delete m_cursor;
+ // Remove screens in reverse order to avoid crash in case of multiple screens
+ for (QScreen *screen : backwards(QGuiApplication::screens()))
+ static_cast<QCocoaScreen*>(screen->handle())->remove();
+}
- CVDisplayLinkRelease(m_displayLink);
- if (m_displayLinkSource)
- dispatch_release(m_displayLinkSource);
+void QCocoaScreen::remove()
+{
+ m_displayId = 0; // Prevent stale references during removal
+
+ // This may result in the application responding to QGuiApplication::screenRemoved
+ // by moving the window to another screen, either by setGeometry, or by setScreen.
+ // If the window isn't moved by the application, Qt will as a fallback move it to
+ // the primary screen via setScreen. Due to the way setScreen works, this won't
+ // actually recreate the window on the new screen, it will just assign the new
+ // QScreen to the window. The associated NSWindow will have an NSScreen determined
+ // by AppKit. AppKit will then move the window to another screen by changing the
+ // geometry, and we will get a callback in QCocoaWindow::windowDidMove and then
+ // QCocoaWindow::windowDidChangeScreen. At that point the window will appear to have
+ // already changed its screen, but that's only true if comparing the Qt screens,
+ // not when comparing the NSScreens.
+ QWindowSystemInterface::handleScreenRemoved(this);
}
-NSScreen *QCocoaScreen::nativeScreen() const
+QCocoaScreen::~QCocoaScreen()
{
- NSArray<NSScreen *> *screens = [NSScreen screens];
+ Q_ASSERT_X(!screen(), "QCocoaScreen", "QScreen should be deleted first");
- // Stale reference, screen configuration has changed
- if (m_screenIndex < 0 || (NSUInteger)m_screenIndex >= [screens count])
- return nil;
+ delete m_cursor;
- return [screens objectAtIndex:m_screenIndex];
+ CVDisplayLinkRelease(m_displayLink);
+ if (m_displayLinkSource)
+ dispatch_release(m_displayLinkSource);
}
static QString displayName(CGDirectDisplayID displayID)
@@ -117,35 +188,37 @@ static QString displayName(CGDirectDisplayID displayID)
void QCocoaScreen::updateProperties()
{
- NSScreen *nsScreen = nativeScreen();
- if (!nsScreen)
- return;
+ Q_ASSERT(m_displayId);
const QRect previousGeometry = m_geometry;
const QRect previousAvailableGeometry = m_availableGeometry;
const QDpi previousLogicalDpi = m_logicalDpi;
const qreal previousRefreshRate = m_refreshRate;
+ // Some properties are only available via NSScreen
+ NSScreen *nsScreen = nativeScreen();
+ Q_ASSERT(nsScreen);
+
// The reference screen for the geometry is always the primary screen
- QRectF primaryScreenGeometry = QRectF::fromCGRect([[NSScreen screens] firstObject].frame);
+ QRectF primaryScreenGeometry = QRectF::fromCGRect(CGDisplayBounds(CGMainDisplayID()));
m_geometry = qt_mac_flip(QRectF::fromCGRect(nsScreen.frame), primaryScreenGeometry).toRect();
m_availableGeometry = qt_mac_flip(QRectF::fromCGRect(nsScreen.visibleFrame), primaryScreenGeometry).toRect();
+ m_devicePixelRatio = nsScreen.backingScaleFactor;
+
m_format = QImage::Format_RGB32;
- m_depth = NSBitsPerPixelFromDepth([nsScreen depth]);
+ m_depth = NSBitsPerPixelFromDepth(nsScreen.depth);
- CGDirectDisplayID dpy = nsScreen.qt_displayId;
- CGSize size = CGDisplayScreenSize(dpy);
+ CGSize size = CGDisplayScreenSize(m_displayId);
m_physicalSize = QSizeF(size.width, size.height);
m_logicalDpi.first = 72;
m_logicalDpi.second = 72;
- CGDisplayModeRef displayMode = CGDisplayCopyDisplayMode(dpy);
+
+ QCFType<CGDisplayModeRef> displayMode = CGDisplayCopyDisplayMode(m_displayId);
float refresh = CGDisplayModeGetRefreshRate(displayMode);
- CGDisplayModeRelease(displayMode);
- if (refresh > 0)
- m_refreshRate = refresh;
+ m_refreshRate = refresh > 0 ? refresh : 60.0;
- m_name = displayName(dpy);
+ m_name = displayName(m_displayId);
const bool didChangeGeometry = m_geometry != previousGeometry || m_availableGeometry != previousAvailableGeometry;
@@ -155,24 +228,6 @@ void QCocoaScreen::updateProperties()
QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen(), m_logicalDpi.first, m_logicalDpi.second);
if (m_refreshRate != previousRefreshRate)
QWindowSystemInterface::handleScreenRefreshRateChange(screen(), m_refreshRate);
-
- qCDebug(lcQpaScreen) << "Updated properties for" << this;
-
- if (didChangeGeometry) {
- // When a screen changes its geometry, AppKit will send us a NSWindowDidMoveNotification
- // for each window, resulting in calls to handleGeometryChange(), but this happens before
- // the NSApplicationDidChangeScreenParametersNotification, so when we map the new geometry
- // (which is correct at that point) to the screen using QCocoaScreen::mapFromNative(), we
- // end up using the stale screen geometry, and the new window geometry we report is wrong.
- // To make sure we finally report the correct window geometry, we need to do another pass
- // of geometry reporting, now that the screen properties have been updates. FIXME: Ideally
- // this would be solved by not caching the screen properties in QCocoaScreen, but that
- // requires more research.
- for (QWindow *window : windows()) {
- if (QCocoaWindow *cocoaWindow = static_cast<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;
diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
index 4982f5ee05..de5cf85854 100644
--- a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
+++ b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
@@ -383,9 +383,9 @@ QT_END_NAMESPACE
}
- (QRectF)geometry {
- if (NSWindow *window = [[item view] window]) {
- if (QCocoaScreen *screen = QCocoaIntegration::instance()->screenForNSScreen([window screen]))
- return screen->mapFromNative([window frame]);
+ if (NSWindow *window = item.view.window) {
+ if (QCocoaScreen *screen = QCocoaScreen::get(window.screen))
+ return screen->mapFromNative(window.frame);
}
return QRectF();
}
diff --git a/src/plugins/platforms/cocoa/qcocoatheme.h b/src/plugins/platforms/cocoa/qcocoatheme.h
index c42fa7d2e8..788b616e78 100644
--- a/src/plugins/platforms/cocoa/qcocoatheme.h
+++ b/src/plugins/platforms/cocoa/qcocoatheme.h
@@ -45,6 +45,8 @@
Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(QCocoaThemeAppAppearanceObserver));
+#include <QtCore/private/qcore_mac_p.h>
+
QT_BEGIN_NAMESPACE
class QPalette;
@@ -82,9 +84,10 @@ public:
private:
mutable QPalette *m_systemPalette;
+ QMacNotificationObserver m_systemColorObserver;
mutable QHash<QPlatformTheme::Palette, QPalette*> m_palettes;
mutable QHash<QPlatformTheme::Font, QFont*> m_fonts;
- QT_MANGLE_NAMESPACE(QCocoaThemeAppAppearanceObserver) *m_appearanceObserver;
+ QMacKeyValueObserver m_appearanceObserver;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qcocoatheme.mm b/src/plugins/platforms/cocoa/qcocoatheme.mm
index efe670abed..7c10456824 100644
--- a/src/plugins/platforms/cocoa/qcocoatheme.mm
+++ b/src/plugins/platforms/cocoa/qcocoatheme.mm
@@ -80,70 +80,32 @@
#include <CoreServices/CoreServices.h>
-#if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14)
-@interface QT_MANGLE_NAMESPACE(QCocoaThemeAppAppearanceObserver) : NSObject
-@property (readonly, nonatomic) QCocoaTheme *theme;
-- (instancetype)initWithTheme:(QCocoaTheme *)theme;
-@end
-
-QT_NAMESPACE_ALIAS_OBJC_CLASS(QCocoaThemeAppAppearanceObserver);
-
-@implementation QCocoaThemeAppAppearanceObserver
-- (instancetype)initWithTheme:(QCocoaTheme *)theme
-{
- if ((self = [super init])) {
- _theme = theme;
- [NSApp addObserver:self forKeyPath:@"effectiveAppearance" options:NSKeyValueObservingOptionNew context:nullptr];
- }
- return self;
-}
-
-- (void)dealloc
-{
- [NSApp removeObserver:self forKeyPath:@"effectiveAppearance"];
- [super dealloc];
-}
-
-- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
- change:(NSDictionary<NSKeyValueChangeKey, id> *)change context:(void *)context
-{
- Q_UNUSED(change);
- Q_UNUSED(context);
-
- Q_ASSERT(object == NSApp);
- Q_ASSERT([keyPath isEqualToString:@"effectiveAppearance"]);
-
- if (__builtin_available(macOS 10.14, *))
- NSAppearance.currentAppearance = NSApp.effectiveAppearance;
-
- self.theme->handleSystemThemeChange();
-}
-@end
-#endif // QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14)
-
QT_BEGIN_NAMESPACE
const char *QCocoaTheme::name = "cocoa";
QCocoaTheme::QCocoaTheme()
- : m_systemPalette(nullptr), m_appearanceObserver(nil)
+ : m_systemPalette(nullptr)
{
#if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14)
- if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave)
- m_appearanceObserver = [[QCocoaThemeAppAppearanceObserver alloc] initWithTheme:this];
+ if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave) {
+ m_appearanceObserver = QMacKeyValueObserver(NSApp, @"effectiveAppearance", [this] {
+ if (__builtin_available(macOS 10.14, *))
+ NSAppearance.currentAppearance = NSApp.effectiveAppearance;
+
+ handleSystemThemeChange();
+ });
+ }
#endif
- [[NSNotificationCenter defaultCenter] addObserverForName:NSSystemColorsDidChangeNotification
- object:nil queue:nil usingBlock:^(NSNotification *) {
+ m_systemColorObserver = QMacNotificationObserver(nil,
+ NSSystemColorsDidChangeNotification, [this] {
handleSystemThemeChange();
- }];
+ });
}
QCocoaTheme::~QCocoaTheme()
{
- if (m_appearanceObserver)
- [m_appearanceObserver release];
-
reset();
qDeleteAll(m_fonts);
}
diff --git a/src/plugins/platforms/cocoa/qcocoavulkaninstance.mm b/src/plugins/platforms/cocoa/qcocoavulkaninstance.mm
index b00fde6c6f..7ce78ee738 100644
--- a/src/plugins/platforms/cocoa/qcocoavulkaninstance.mm
+++ b/src/plugins/platforms/cocoa/qcocoavulkaninstance.mm
@@ -54,7 +54,7 @@ QCocoaVulkanInstance::~QCocoaVulkanInstance()
void QCocoaVulkanInstance::createOrAdoptInstance()
{
- initInstance(m_instance, QByteArrayList());
+ initInstance(m_instance, QByteArrayList() << QByteArrayLiteral("VK_MVK_macos_surface"));
}
VkSurfaceKHR *QCocoaVulkanInstance::createSurface(QWindow *window)
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm
index 298d11fe08..67b0cbdfe9 100644
--- a/src/plugins/platforms/cocoa/qcocoawindow.mm
+++ b/src/plugins/platforms/cocoa/qcocoawindow.mm
@@ -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();
}
@@ -1208,23 +1210,34 @@ void QCocoaWindow::windowDidChangeScreen()
if (!window())
return;
- const bool wasRunningDisplayLink = static_cast<QCocoaScreen *>(screen())->isRunningDisplayLink();
-
- if (QCocoaScreen *newScreen = QCocoaIntegration::instance()->screenForNSScreen(m_view.window.screen)) {
- if (newScreen == screen()) {
- // Screen properties have changed. Will be handled by
- // NSApplicationDidChangeScreenParametersNotification
- // in QCocoaIntegration::updateScreens().
- return;
- }
-
- qCDebug(lcQpaWindow) << window() << "moved to" << newScreen;
- QWindowSystemInterface::handleWindowScreenChanged<QWindowSystemInterface::SynchronousDelivery>(window(), newScreen->screen());
-
- if (hasPendingUpdateRequest() && wasRunningDisplayLink)
- requestUpdate(); // Restart display-link on new screen
- } else {
- qCWarning(lcQpaWindow) << "Failed to get QCocoaScreen for" << m_view.window.screen;
+ // Note: When a window is resized to 0x0 Cocoa will report the window's screen as nil
+ 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
+ // 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 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
+ // device-pixel ratio may have changed, and needs to be delivered to all
+ // windows, both top level and child windows.
+
+ qCDebug(lcQpaWindow) << "Screen changed for" << window() << "from" << previousScreen << "to" << currentScreen;
+ QWindowSystemInterface::handleWindowScreenChanged<QWindowSystemInterface::SynchronousDelivery>(
+ window(), currentScreen ? currentScreen->screen() : nullptr);
+
+ if (currentScreen && hasPendingUpdateRequest()) {
+ // Restart display-link on new screen. We need to do this unconditionally,
+ // since we can't rely on the previousScreen reflecting whether or not the
+ // window actually moved from one screen to another, or just stayed on the
+ // same screen.
+ currentScreen->requestUpdate();
}
}
diff --git a/src/plugins/platforms/cocoa/qmacclipboard.mm b/src/plugins/platforms/cocoa/qmacclipboard.mm
index ba6cfca219..358a6b49fd 100644
--- a/src/plugins/platforms/cocoa/qmacclipboard.mm
+++ b/src/plugins/platforms/cocoa/qmacclipboard.mm
@@ -489,19 +489,16 @@ QMacPasteboard::retrieveData(const QString &format, QVariant::Type) const
QMacInternalPasteboardMime *c = mimes.at(mime);
QString c_flavor = c->flavorFor(format);
if (!c_flavor.isEmpty()) {
- // Handle text/plain a little differently. Try handling Unicode first.
- bool checkForUtf16 = (c_flavor == QLatin1String("com.apple.traditional-mac-plain-text")
- || c_flavor == QLatin1String("public.utf8-plain-text"));
- if (checkForUtf16 || c_flavor == QLatin1String("public.utf16-plain-text")) {
- // Try to get the NSStringPboardType from NSPasteboard, newlines are mapped
- // correctly (as '\n') in this data. The 'public.utf16-plain-text' type
- // usually maps newlines to '\r' instead.
+ // Converting via PasteboardCopyItemFlavorData below will for some UITs result
+ // in newlines mapping to '\r' instead of '\n'. To work around this we shortcut
+ // the conversion via NSPasteboard's NSStringPboardType if possible.
+ if (c_flavor == QLatin1String("com.apple.traditional-mac-plain-text")
+ || c_flavor == QLatin1String("public.utf8-plain-text")
+ || c_flavor == QLatin1String("public.utf16-plain-text")) {
QString str = qt_mac_get_pasteboardString(paste);
if (!str.isEmpty())
return str;
}
- if (checkForUtf16 && hasFlavor(QLatin1String("public.utf16-plain-text")))
- c_flavor = QLatin1String("public.utf16-plain-text");
QVariant ret;
QList<QByteArray> retList;
diff --git a/src/plugins/platforms/cocoa/qnsview_drawing.mm b/src/plugins/platforms/cocoa/qnsview_drawing.mm
index 69d36ef61e..d2e6f848a0 100644
--- a/src/plugins/platforms/cocoa/qnsview_drawing.mm
+++ b/src/plugins/platforms/cocoa/qnsview_drawing.mm
@@ -189,6 +189,15 @@
- (void)displayLayer:(CALayer *)layer
{
+ if (!NSThread.isMainThread) {
+ // Qt is calling AppKit APIs such as -[NSOpenGLContext setView:] on secondary threads,
+ // which we shouldn't do. This may result in AppKit (wrongly) triggering a display on
+ // the thread where we made the call, so block it here and defer to the main thread.
+ qCWarning(lcQpaDrawing) << "Display non non-main thread! Deferring to main thread";
+ dispatch_async(dispatch_get_main_queue(), ^{ self.needsDisplay = YES; });
+ return;
+ }
+
Q_ASSERT(layer == self.layer);
if (!m_platformWindow)
diff --git a/src/plugins/platforms/cocoa/qnsview_mouse.mm b/src/plugins/platforms/cocoa/qnsview_mouse.mm
index 6e3cff2b48..a887cb841d 100644
--- a/src/plugins/platforms/cocoa/qnsview_mouse.mm
+++ b/src/plugins/platforms/cocoa/qnsview_mouse.mm
@@ -147,9 +147,34 @@
ulong timestamp = [theEvent timestamp] * 1000;
- auto eventType = cocoaEvent2QtMouseEvent(theEvent);
- qCInfo(lcQpaMouse) << "Frame-strut" << eventType << "at" << qtWindowPoint << "with" << m_frameStrutButtons << "in" << self.window;
- QWindowSystemInterface::handleFrameStrutMouseEvent(m_platformWindow->window(), timestamp, qtWindowPoint, qtScreenPoint, m_frameStrutButtons);
+ const auto button = cocoaButton2QtButton(theEvent);
+ auto eventType = [&]() {
+ switch (theEvent.type) {
+ case NSEventTypeLeftMouseDown:
+ case NSEventTypeRightMouseDown:
+ case NSEventTypeOtherMouseDown:
+ return QEvent::NonClientAreaMouseButtonPress;
+
+ case NSEventTypeLeftMouseUp:
+ case NSEventTypeRightMouseUp:
+ case NSEventTypeOtherMouseUp:
+ return QEvent::NonClientAreaMouseButtonRelease;
+
+ case NSEventTypeLeftMouseDragged:
+ case NSEventTypeRightMouseDragged:
+ case NSEventTypeOtherMouseDragged:
+ return QEvent::NonClientAreaMouseMove;
+
+ default:
+ break;
+ }
+
+ return QEvent::None;
+ }();
+
+ qCInfo(lcQpaMouse) << eventType << "at" << qtWindowPoint << "with" << m_frameStrutButtons << "in" << self.window;
+ QWindowSystemInterface::handleFrameStrutMouseEvent(m_platformWindow->window(),
+ timestamp, qtWindowPoint, qtScreenPoint, m_frameStrutButtons, button, eventType);
}
@end
@@ -252,20 +277,19 @@
nativeDrag->setLastMouseEvent(theEvent, self);
const auto modifiers = [QNSView convertKeyModifiers:theEvent.modifierFlags];
- const auto buttons = currentlyPressedMouseButtons();
auto button = cocoaButton2QtButton(theEvent);
if (button == Qt::LeftButton && m_sendUpAsRightButton)
button = Qt::RightButton;
const auto eventType = cocoaEvent2QtMouseEvent(theEvent);
if (eventType == QEvent::MouseMove)
- qCDebug(lcQpaMouse) << eventType << "at" << qtWindowPoint << "with" << buttons;
+ qCDebug(lcQpaMouse) << eventType << "at" << qtWindowPoint << "with" << m_buttons;
else
- qCInfo(lcQpaMouse) << eventType << "of" << button << "at" << qtWindowPoint << "with" << buttons;
+ qCInfo(lcQpaMouse) << eventType << "of" << button << "at" << qtWindowPoint << "with" << m_buttons;
QWindowSystemInterface::handleMouseEvent(targetView->m_platformWindow->window(),
timestamp, qtWindowPoint, qtScreenPoint,
- buttons, button, eventType, modifiers);
+ m_buttons, button, eventType, modifiers);
}
- (bool)handleMouseDownEvent:(NSEvent *)theEvent
@@ -472,12 +496,15 @@
// uses the legacy cursorRect API, so the cursor is reset to the arrow
// cursor. See rdar://34183708
- if (self.cursor && self.cursor != NSCursor.currentCursor) {
- qCInfo(lcQpaMouse) << "Updating cursor for" << self << "to" << self.cursor;
+ auto previousCursor = NSCursor.currentCursor;
+
+ if (self.cursor)
[self.cursor set];
- } else {
+ else
[super cursorUpdate:theEvent];
- }
+
+ if (NSCursor.currentCursor != previousCursor)
+ qCInfo(lcQpaMouse) << "Cursor update for" << self << "resulted in new cursor" << NSCursor.currentCursor;
}
- (void)mouseMovedImpl:(NSEvent *)theEvent
diff --git a/src/plugins/platforms/cocoa/qnswindowdelegate.mm b/src/plugins/platforms/cocoa/qnswindowdelegate.mm
index 087cb3651f..9502a315d8 100644
--- a/src/plugins/platforms/cocoa/qnswindowdelegate.mm
+++ b/src/plugins/platforms/cocoa/qnswindowdelegate.mm
@@ -51,7 +51,9 @@ static QRegExp whitespaceRegex = QRegExp(QStringLiteral("\\s*"));
static QCocoaWindow *toPlatformWindow(NSWindow *window)
{
- return qnsview_cast(window.contentView).platformWindow;
+ if ([window conformsToProtocol:@protocol(QNSWindowProtocol)])
+ return static_cast<QCocoaNSWindow *>(window).platformWindow;
+ return nullptr;
}
@implementation QNSWindowDelegate
diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dhelpers.h b/src/plugins/platforms/direct2d/qwindowsdirect2dhelpers.h
index babe646fd8..6d880e24cf 100644
--- a/src/plugins/platforms/direct2d/qwindowsdirect2dhelpers.h
+++ b/src/plugins/platforms/direct2d/qwindowsdirect2dhelpers.h
@@ -69,12 +69,15 @@ inline D2D1_RECT_F to_d2d_rect_f(const QRectF &qrect)
inline D2D1_SIZE_U to_d2d_size_u(const QSizeF &qsize)
{
- return D2D1::SizeU(qsize.width(), qsize.height());
+
+ return D2D1::SizeU(UINT32(qRound(qsize.width())),
+ UINT32(qRound(qsize.height())));
}
inline D2D1_SIZE_U to_d2d_size_u(const QSize &qsize)
{
- return D2D1::SizeU(qsize.width(), qsize.height());
+ return D2D1::SizeU(UINT32(qsize.width()),
+ UINT32(qsize.height()));
}
inline D2D1_POINT_2F to_d2d_point_2f(const QPointF &qpoint)
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_openwfd/qeglfsopenwfdintegration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_openwfd/qeglfsopenwfdintegration.cpp
index 738c30c65a..b8f04cf978 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_openwfd/qeglfsopenwfdintegration.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_openwfd/qeglfsopenwfdintegration.cpp
@@ -221,7 +221,7 @@ EGLNativeWindowType QEglFSOpenWFDIntegration::createNativeWindow(QPlatformWindow
QSurfaceFormat QEglFSOpenWFDIntegration::surfaceFormatFor(const QSurfaceFormat &inputFormat) const
{
- QSurfaceFormat format;
+ QSurfaceFormat format = inputFormat;
format.setRedBufferSize(8);
format.setGreenBufferSize(8);
format.setBlueBufferSize(8);
diff --git a/src/plugins/platforms/ios/qiostextinputoverlay.mm b/src/plugins/platforms/ios/qiostextinputoverlay.mm
index e5419b1766..0561a826c6 100644
--- a/src/plugins/platforms/ios/qiostextinputoverlay.mm
+++ b/src/plugins/platforms/ios/qiostextinputoverlay.mm
@@ -834,9 +834,14 @@ static void executeBlockWithoutAnimation(Block block)
- (void)updateSelection
{
if (!hasSelection()) {
- _cursorLayer.visible = NO;
- _anchorLayer.visible = NO;
- QIOSTextInputOverlay::s_editMenu.visible = NO;
+ if (_cursorLayer.visible) {
+ _cursorLayer.visible = NO;
+ _anchorLayer.visible = NO;
+ // Only hide the edit menu if we had a selection from before, since
+ // the edit menu can also be used for other purposes by others (in
+ // which case we try our best not to interfere).
+ QIOSTextInputOverlay::s_editMenu.visible = NO;
+ }
return;
}
diff --git a/src/plugins/platforms/wasm/qtloader.js b/src/plugins/platforms/wasm/qtloader.js
index 4752a1dcee..ef4a6ec2b9 100644
--- a/src/plugins/platforms/wasm/qtloader.js
+++ b/src/plugins/platforms/wasm/qtloader.js
@@ -124,6 +124,8 @@
// Remove canvas at run-time. Removes the corresponding QScreen.
// resizeCanvasElement
// Signals to the application that a canvas has been resized.
+// setFontDpi
+// Sets the logical font dpi for the application.
var Module = {}
@@ -237,6 +239,8 @@ function QtLoader(config)
publicAPI.addCanvasElement = addCanvasElement;
publicAPI.removeCanvasElement = removeCanvasElement;
publicAPI.resizeCanvasElement = resizeCanvasElement;
+ publicAPI.setFontDpi = setFontDpi;
+ publicAPI.fontDpi = fontDpi;
restartCount = 0;
@@ -557,6 +561,16 @@ function QtLoader(config)
Module.qtResizeCanvasElement(element);
}
+ function setFontDpi(dpi) {
+ Module.qtFontDpi = dpi;
+ if (publicAPI.status == "Running")
+ Module.qtSetFontDpi(dpi);
+ }
+
+ function fontDpi() {
+ return Module.qtFontDpi;
+ }
+
setStatus("Created");
return publicAPI;
diff --git a/src/plugins/platforms/wasm/qwasmintegration.cpp b/src/plugins/platforms/wasm/qwasmintegration.cpp
index 93d3b5f76d..116612c286 100644
--- a/src/plugins/platforms/wasm/qwasmintegration.cpp
+++ b/src/plugins/platforms/wasm/qwasmintegration.cpp
@@ -49,8 +49,10 @@
#include <QtGui/qscreen.h>
#include <qpa/qwindowsysteminterface.h>
#include <QtCore/qcoreapplication.h>
+#include <qpa/qplatforminputcontextfactory_p.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"
@@ -81,12 +83,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;
@@ -189,6 +197,18 @@ QPlatformOpenGLContext *QWasmIntegration::createPlatformOpenGLContext(QOpenGLCon
}
#endif
+void QWasmIntegration::initialize()
+{
+ QString icStr = QPlatformInputContextFactory::requested();
+ if (!icStr.isNull())
+ m_inputContext.reset(QPlatformInputContextFactory::create(icStr));
+}
+
+QPlatformInputContext *QWasmIntegration::inputContext() const
+{
+ return m_inputContext.data();
+}
+
QPlatformOffscreenSurface *QWasmIntegration::createPlatformOffscreenSurface(QOffscreenSurface *surface) const
{
return new QWasmOffscrenSurface(surface);
@@ -268,6 +288,16 @@ 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);
+}
+
void QWasmIntegration::resizeAllScreens()
{
qDebug() << "resizeAllScreens";
diff --git a/src/plugins/platforms/wasm/qwasmintegration.h b/src/plugins/platforms/wasm/qwasmintegration.h
index dc1cc72382..2102f5c226 100644
--- a/src/plugins/platforms/wasm/qwasmintegration.h
+++ b/src/plugins/platforms/wasm/qwasmintegration.h
@@ -34,6 +34,7 @@
#include <qpa/qplatformintegration.h>
#include <qpa/qplatformscreen.h>
+#include <qpa/qplatforminputcontext.h>
#include <QtCore/qhash.h>
@@ -74,6 +75,9 @@ public:
QPlatformTheme *createPlatformTheme(const QString &name) const override;
QPlatformServices *services() const override;
QPlatformClipboard *clipboard() const override;
+ void initialize() override;
+ QPlatformInputContext *inputContext() const override;
+
QWasmClipboard *getWasmClipboard() { return m_clipboard; }
static QWasmIntegration *get() { return s_instance; }
@@ -83,6 +87,7 @@ public:
void removeScreen(const QString &canvasId);
void resizeScreen(const QString &canvasId);
void resizeAllScreens();
+ void updateDpi();
private:
mutable QWasmFontDatabase *m_fontDb;
@@ -91,6 +96,8 @@ private:
QHash<QString, QWasmScreen *> m_screens;
mutable QWasmClipboard *m_clipboard;
+ qreal m_fontDpi = -1;
+ mutable QScopedPointer<QPlatformInputContext> m_inputContext;
static QWasmIntegration *s_instance;
};
diff --git a/src/plugins/platforms/wasm/qwasmopenglcontext.cpp b/src/plugins/platforms/wasm/qwasmopenglcontext.cpp
index 0eaffdb77e..62087f54bd 100644
--- a/src/plugins/platforms/wasm/qwasmopenglcontext.cpp
+++ b/src/plugins/platforms/wasm/qwasmopenglcontext.cpp
@@ -37,6 +37,14 @@ QWasmOpenGLContext::QWasmOpenGLContext(const QSurfaceFormat &format)
: m_requestedFormat(format)
{
m_requestedFormat.setRenderableType(QSurfaceFormat::OpenGLES);
+
+ // if we set one, we need to set the other as well since in webgl, these are tied together
+ if (format.depthBufferSize() < 0 && format.stencilBufferSize() > 0)
+ m_requestedFormat.setDepthBufferSize(16);
+
+ if (format.stencilBufferSize() < 0 && format.depthBufferSize() > 0)
+ m_requestedFormat.setStencilBufferSize(8);
+
}
QWasmOpenGLContext::~QWasmOpenGLContext()
@@ -82,10 +90,14 @@ EMSCRIPTEN_WEBGL_CONTEXT_HANDLE QWasmOpenGLContext::createEmscriptenContext(cons
attributes.majorVersion = 2;
}
+ // WebGL doesn't allow separate attach buffers to STENCIL_ATTACHMENT and DEPTH_ATTACHMENT
+ // we need both or none
+ bool useDepthStencil = (format.depthBufferSize() > 0 || format.stencilBufferSize() > 0);
+
// WebGL offers enable/disable control but not size control for these
attributes.alpha = format.alphaBufferSize() > 0;
- attributes.depth = format.depthBufferSize() > 0;
- attributes.stencil = format.stencilBufferSize() > 0;
+ attributes.depth = useDepthStencil;
+ attributes.stencil = useDepthStencil;
EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context = emscripten_webgl_create_context(canvasId.toLocal8Bit().constData(), &attributes);
diff --git a/src/plugins/platforms/wasm/qwasmscreen.cpp b/src/plugins/platforms/wasm/qwasmscreen.cpp
index 74d47555fc..f2eabfa486 100644
--- a/src/plugins/platforms/wasm/qwasmscreen.cpp
+++ b/src/plugins/platforms/wasm/qwasmscreen.cpp
@@ -31,6 +31,7 @@
#include "qwasmwindow.h"
#include "qwasmeventtranslator.h"
#include "qwasmcompositor.h"
+#include "qwasmintegration.h"
#include <emscripten/bind.h>
#include <emscripten/val.h>
@@ -105,6 +106,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
diff --git a/src/plugins/platforms/wasm/qwasmscreen.h b/src/plugins/platforms/wasm/qwasmscreen.h
index 4ad4ffa9ad..fcf693681c 100644
--- a/src/plugins/platforms/wasm/qwasmscreen.h
+++ b/src/plugins/platforms/wasm/qwasmscreen.h
@@ -64,6 +64,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;
diff --git a/src/plugins/platforms/wasm/wasm_shell.html b/src/plugins/platforms/wasm/wasm_shell.html
index f7c856d63d..a118c217f3 100644
--- a/src/plugins/platforms/wasm/wasm_shell.html
+++ b/src/plugins/platforms/wasm/wasm_shell.html
@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <title>APPNAME</title>
+ <title>@APPNAME@</title>
<style>
html, body { padding: 0; margin : 0; overflow:hidden; height: 100% }
/* the canvas *must not* have any border or padding, or mouse coords will be wrong */
@@ -18,7 +18,7 @@
<figure style="overflow:visible;" id="qtspinner">
<center style="margin-top:1.5em; line-height:150%">
<img src="qtlogo.svg"; width=320; height=200; style="display:block"> </img>
- <strong>Qt for WebAssembly: APPNAME</strong>
+ <strong>Qt for WebAssembly: @APPNAME@</strong>
<div id="qtstatus"></div>
<noscript>JavaScript is disabled. Please enable JavaScript to use this application.</noscript>
</center>
@@ -57,7 +57,7 @@
canvas.style.display = 'block';
},
});
- qtLoader.loadEmscriptenModule("APPNAME");
+ qtLoader.loadEmscriptenModule("@APPNAME@");
}
</script>
<script type="text/javascript" src="qtloader.js"></script>
diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp
index 80517ffe69..76747b7956 100644
--- a/src/plugins/platforms/windows/qwindowscontext.cpp
+++ b/src/plugins/platforms/windows/qwindowscontext.cpp
@@ -210,6 +210,7 @@ void QWindowsUser32DLL::init()
if (QOperatingSystemVersion::current()
>= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, 14393)) {
+ adjustWindowRectExForDpi = (AdjustWindowRectExForDpi)library.resolve("AdjustWindowRectExForDpi");
enableNonClientDpiScaling = (EnableNonClientDpiScaling)library.resolve("EnableNonClientDpiScaling");
getWindowDpiAwarenessContext = (GetWindowDpiAwarenessContext)library.resolve("GetWindowDpiAwarenessContext");
getAwarenessFromDpiAwarenessContext = (GetAwarenessFromDpiAwarenessContext)library.resolve("GetAwarenessFromDpiAwarenessContext");
@@ -748,6 +749,12 @@ QWindowsWindow *QWindowsContext::findPlatformWindowAt(HWND parent,
QWindowsWindow *result = nullptr;
const POINT screenPoint = { screenPointIn.x(), screenPointIn.y() };
while (findPlatformWindowHelper(screenPoint, cwex_flags, this, &parent, &result)) {}
+ // QTBUG-40815: ChildWindowFromPointEx() can hit on special windows from
+ // screen recorder applications like ScreenToGif. Fall back to WindowFromPoint().
+ if (result == nullptr) {
+ if (const HWND window = WindowFromPoint(screenPoint))
+ result = findPlatformWindow(window);
+ }
return result;
}
@@ -924,7 +931,7 @@ bool QWindowsContext::systemParametersInfo(unsigned action, unsigned param, void
bool QWindowsContext::systemParametersInfoForScreen(unsigned action, unsigned param, void *out,
const QPlatformScreen *screen)
{
- return systemParametersInfo(action, param, out, screen ? screen->logicalDpi().first : 0);
+ return systemParametersInfo(action, param, out, screen ? unsigned(screen->logicalDpi().first) : 0u);
}
bool QWindowsContext::systemParametersInfoForWindow(unsigned action, unsigned param, void *out,
@@ -943,7 +950,8 @@ bool QWindowsContext::nonClientMetrics(NONCLIENTMETRICS *ncm, unsigned dpi)
bool QWindowsContext::nonClientMetricsForScreen(NONCLIENTMETRICS *ncm,
const QPlatformScreen *screen)
{
- return nonClientMetrics(ncm, screen ? screen->logicalDpi().first : 0);
+ const int dpi = screen ? qRound(screen->logicalDpi().first) : 0;
+ return nonClientMetrics(ncm, unsigned(dpi));
}
bool QWindowsContext::nonClientMetricsForWindow(NONCLIENTMETRICS *ncm, const QPlatformWindow *win)
@@ -977,7 +985,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 +1329,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: {
@@ -1478,6 +1495,10 @@ void QWindowsContext::handleExitSizeMove(QWindow *window)
keyboardModifiers);
}
}
+ if (d->m_systemInfo & QWindowsContext::SI_SupportsPointer)
+ d->m_pointerHandler.clearEvents();
+ else
+ d->m_mouseHandler.clearEvents();
}
bool QWindowsContext::asyncExpose() const
@@ -1587,6 +1608,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();
diff --git a/src/plugins/platforms/windows/qwindowscontext.h b/src/plugins/platforms/windows/qwindowscontext.h
index fd6c72668c..4908f14629 100644
--- a/src/plugins/platforms/windows/qwindowscontext.h
+++ b/src/plugins/platforms/windows/qwindowscontext.h
@@ -102,6 +102,7 @@ struct QWindowsUser32DLL
typedef BOOL (WINAPI *RemoveClipboardFormatListener)(HWND);
typedef BOOL (WINAPI *GetDisplayAutoRotationPreferences)(DWORD *);
typedef BOOL (WINAPI *SetDisplayAutoRotationPreferences)(DWORD);
+ typedef BOOL (WINAPI *AdjustWindowRectExForDpi)(LPRECT,DWORD,BOOL,DWORD,UINT);
typedef BOOL (WINAPI *EnableNonClientDpiScaling)(HWND);
typedef int (WINAPI *GetWindowDpiAwarenessContext)(HWND);
typedef int (WINAPI *GetAwarenessFromDpiAwarenessContext)(int);
@@ -131,6 +132,7 @@ struct QWindowsUser32DLL
GetDisplayAutoRotationPreferences getDisplayAutoRotationPreferences = nullptr;
SetDisplayAutoRotationPreferences setDisplayAutoRotationPreferences = nullptr;
+ AdjustWindowRectExForDpi adjustWindowRectExForDpi = nullptr;
EnableNonClientDpiScaling enableNonClientDpiScaling = nullptr;
GetWindowDpiAwarenessContext getWindowDpiAwarenessContext = nullptr;
GetAwarenessFromDpiAwarenessContext getAwarenessFromDpiAwarenessContext = nullptr;
@@ -201,6 +203,8 @@ public:
QWindowsWindow *findPlatformWindowAt(HWND parent, const QPoint &screenPoint,
unsigned cwex_flags) const;
+ static bool shouldHaveNonClientDpiScaling(const QWindow *window);
+
QWindow *windowUnderMouse() const;
void clearWindowUnderMouse();
diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp
index 2c90b0484e..5c1fa00088 100644
--- a/src/plugins/platforms/windows/qwindowsintegration.cpp
+++ b/src/plugins/platforms/windows/qwindowsintegration.cpp
@@ -353,6 +353,9 @@ QPlatformWindow *QWindowsIntegration::createPlatformWindow(QWindow *window) cons
QWindowsWindow *result = createPlatformWindowHelper(window, obtained);
Q_ASSERT(result);
+ if (window->isTopLevel() && !QWindowsContext::shouldHaveNonClientDpiScaling(window))
+ result->setFlag(QWindowsWindow::DisableNonClientScaling);
+
if (QWindowsMenuBar *menuBarToBeInstalled = QWindowsMenuBar::menuBarOf(window))
menuBarToBeInstalled->install(result);
diff --git a/src/plugins/platforms/windows/qwindowsmousehandler.cpp b/src/plugins/platforms/windows/qwindowsmousehandler.cpp
index 737fd1d2a9..e33591c33d 100644
--- a/src/plugins/platforms/windows/qwindowsmousehandler.cpp
+++ b/src/plugins/platforms/windows/qwindowsmousehandler.cpp
@@ -157,6 +157,12 @@ QTouchDevice *QWindowsMouseHandler::ensureTouchDevice()
return m_touchDevice;
}
+void QWindowsMouseHandler::clearEvents()
+{
+ m_lastEventType = QEvent::None;
+ m_lastEventButton = Qt::NoButton;
+}
+
Qt::MouseButtons QWindowsMouseHandler::queryMouseButtons()
{
Qt::MouseButtons result = nullptr;
@@ -287,8 +293,6 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd,
Qt::MouseEventSource source = Qt::MouseEventNotSynthesized;
- const MouseEvent mouseEvent = eventFromMsg(msg);
-
// Check for events synthesized from touch. Lower byte is touch index, 0 means pen.
static const bool passSynthesizedMouseEvents =
!(QWindowsIntegration::instance()->options() & QWindowsIntegration::DontPassOsMouseEventsSynthesizedFromTouch);
@@ -305,13 +309,40 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd,
}
}
+ const Qt::KeyboardModifiers keyModifiers = QWindowsKeyMapper::queryKeyboardModifiers();
+ const MouseEvent mouseEvent = eventFromMsg(msg);
+ Qt::MouseButtons buttons;
+
+ if (mouseEvent.type >= QEvent::NonClientAreaMouseMove && mouseEvent.type <= QEvent::NonClientAreaMouseButtonDblClick)
+ buttons = queryMouseButtons();
+ else
+ buttons = keyStateToMouseButtons(msg.wParam);
+
+ // When the left/right mouse buttons are pressed over the window title bar
+ // WM_NCLBUTTONDOWN/WM_NCRBUTTONDOWN messages are received. But no UP
+ // messages are received on release, only WM_NCMOUSEMOVE/WM_MOUSEMOVE.
+ // We detect it and generate the missing release events here. (QTBUG-75678)
+ // The last event vars are cleared on QWindowsContext::handleExitSizeMove()
+ // to avoid generating duplicated release events.
+ if (m_lastEventType == QEvent::NonClientAreaMouseButtonPress
+ && (mouseEvent.type == QEvent::NonClientAreaMouseMove || mouseEvent.type == QEvent::MouseMove)
+ && (m_lastEventButton & buttons) == 0) {
+ if (mouseEvent.type == QEvent::NonClientAreaMouseMove) {
+ QWindowSystemInterface::handleFrameStrutMouseEvent(window, clientPosition, globalPosition, buttons, m_lastEventButton,
+ QEvent::NonClientAreaMouseButtonRelease, keyModifiers, source);
+ } else {
+ QWindowSystemInterface::handleMouseEvent(window, clientPosition, globalPosition, buttons, m_lastEventButton,
+ QEvent::MouseButtonRelease, keyModifiers, source);
+ }
+ }
+ m_lastEventType = mouseEvent.type;
+ m_lastEventButton = mouseEvent.button;
+
if (mouseEvent.type >= QEvent::NonClientAreaMouseMove && mouseEvent.type <= QEvent::NonClientAreaMouseButtonDblClick) {
- const Qt::MouseButtons buttons = QWindowsMouseHandler::queryMouseButtons();
QWindowSystemInterface::handleFrameStrutMouseEvent(window, clientPosition,
globalPosition, buttons,
mouseEvent.button, mouseEvent.type,
- QWindowsKeyMapper::queryKeyboardModifiers(),
- source);
+ keyModifiers, source);
return false; // Allow further event processing (dragging of windows).
}
@@ -334,7 +365,6 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd,
}
QWindowsWindow *platformWindow = static_cast<QWindowsWindow *>(window->handle());
- const Qt::MouseButtons buttons = keyStateToMouseButtons(int(msg.wParam));
// If the window was recently resized via mouse doubleclick on the frame or title bar,
// we don't get WM_LBUTTONDOWN or WM_LBUTTONDBLCLK for the second click,
@@ -461,8 +491,7 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd,
if (!discardEvent && mouseEvent.type != QEvent::None) {
QWindowSystemInterface::handleMouseEvent(window, winEventPosition, globalPosition, buttons,
mouseEvent.button, mouseEvent.type,
- QWindowsKeyMapper::queryKeyboardModifiers(),
- source);
+ keyModifiers, source);
}
m_previousCaptureWindow = hasCapture ? window : nullptr;
// QTBUG-48117, force synchronous handling for the extra buttons so that WM_APPCOMMAND
diff --git a/src/plugins/platforms/windows/qwindowsmousehandler.h b/src/plugins/platforms/windows/qwindowsmousehandler.h
index 480662c9bf..5fe4b09c1e 100644
--- a/src/plugins/platforms/windows/qwindowsmousehandler.h
+++ b/src/plugins/platforms/windows/qwindowsmousehandler.h
@@ -45,6 +45,7 @@
#include <QtCore/qpointer.h>
#include <QtCore/qhash.h>
+#include <QtGui/qevent.h>
QT_BEGIN_NAMESPACE
@@ -72,13 +73,14 @@ public:
bool translateScrollEvent(QWindow *window, HWND hwnd,
MSG msg, LRESULT *result);
- static inline Qt::MouseButtons keyStateToMouseButtons(int);
+ static inline Qt::MouseButtons keyStateToMouseButtons(WPARAM);
static inline Qt::KeyboardModifiers keyStateToModifiers(int);
static inline int mouseButtonsToKeyState(Qt::MouseButtons);
static Qt::MouseButtons queryMouseButtons();
QWindow *windowUnderMouse() const { return m_windowUnderMouse.data(); }
void clearWindowUnderMouse() { m_windowUnderMouse = 0; }
+ void clearEvents();
private:
inline bool translateMouseWheelEvent(QWindow *window, HWND hwnd,
@@ -91,9 +93,11 @@ private:
QTouchDevice *m_touchDevice = nullptr;
bool m_leftButtonDown = false;
QWindow *m_previousCaptureWindow = nullptr;
+ QEvent::Type m_lastEventType = QEvent::None;
+ Qt::MouseButton m_lastEventButton = Qt::NoButton;
};
-Qt::MouseButtons QWindowsMouseHandler::keyStateToMouseButtons(int wParam)
+Qt::MouseButtons QWindowsMouseHandler::keyStateToMouseButtons(WPARAM wParam)
{
Qt::MouseButtons mb(Qt::NoButton);
if (wParam & MK_LBUTTON)
diff --git a/src/plugins/platforms/windows/qwindowspointerhandler.cpp b/src/plugins/platforms/windows/qwindowspointerhandler.cpp
index 9a8b5d5121..8b88007949 100644
--- a/src/plugins/platforms/windows/qwindowspointerhandler.cpp
+++ b/src/plugins/platforms/windows/qwindowspointerhandler.cpp
@@ -269,7 +269,10 @@ static Qt::MouseButtons queryMouseButtons()
static QWindow *getWindowUnderPointer(QWindow *window, QPoint globalPos)
{
- QWindow *currentWindowUnderPointer = QWindowsScreen::windowAt(globalPos, CWP_SKIPINVISIBLE | CWP_SKIPTRANSPARENT);
+ QWindowsWindow *platformWindow = static_cast<QWindowsWindow *>(window->handle());
+
+ QWindow *currentWindowUnderPointer = platformWindow->hasMouseCapture() ?
+ QWindowsScreen::windowAt(globalPos, CWP_SKIPINVISIBLE | CWP_SKIPTRANSPARENT) : window;
while (currentWindowUnderPointer && currentWindowUnderPointer->flags() & Qt::WindowTransparentForInput)
currentWindowUnderPointer = currentWindowUnderPointer->parent();
@@ -336,6 +339,12 @@ QTouchDevice *QWindowsPointerHandler::ensureTouchDevice()
return m_touchDevice;
}
+void QWindowsPointerHandler::clearEvents()
+{
+ m_lastEventType = QEvent::None;
+ m_lastEventButton = Qt::NoButton;
+}
+
void QWindowsPointerHandler::handleCaptureRelease(QWindow *window,
QWindow *currentWindowUnderPointer,
HWND hwnd,
@@ -614,6 +623,9 @@ bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWin
if (m_needsEnterOnPointerUpdate) {
m_needsEnterOnPointerUpdate = false;
if (window != m_currentWindow) {
+ // make sure we subscribe to leave events for this window
+ trackLeave(hwnd);
+
QWindowSystemInterface::handleEnterEvent(window, localPos, globalPos);
m_currentWindow = window;
if (QWindowsWindow *wumPlatformWindow = QWindowsWindow::windowsWindowOf(target))
@@ -723,10 +735,35 @@ bool QWindowsPointerHandler::translateMouseEvent(QWindow *window,
}
const MouseEvent mouseEvent = eventFromMsg(msg);
+ Qt::MouseButtons mouseButtons;
+
+ if (mouseEvent.type >= QEvent::NonClientAreaMouseMove && mouseEvent.type <= QEvent::NonClientAreaMouseButtonDblClick)
+ mouseButtons = queryMouseButtons();
+ else
+ mouseButtons = mouseButtonsFromKeyState(msg.wParam);
+
+ // When the left/right mouse buttons are pressed over the window title bar
+ // WM_NCLBUTTONDOWN/WM_NCRBUTTONDOWN messages are received. But no UP
+ // messages are received on release, only WM_NCMOUSEMOVE/WM_MOUSEMOVE.
+ // We detect it and generate the missing release events here. (QTBUG-75678)
+ // The last event vars are cleared on QWindowsContext::handleExitSizeMove()
+ // to avoid generating duplicated release events.
+ if (m_lastEventType == QEvent::NonClientAreaMouseButtonPress
+ && (mouseEvent.type == QEvent::NonClientAreaMouseMove || mouseEvent.type == QEvent::MouseMove)
+ && (m_lastEventButton & mouseButtons) == 0) {
+ if (mouseEvent.type == QEvent::NonClientAreaMouseMove) {
+ QWindowSystemInterface::handleFrameStrutMouseEvent(window, localPos, globalPos, mouseButtons, m_lastEventButton,
+ QEvent::NonClientAreaMouseButtonRelease, keyModifiers, source);
+ } else {
+ QWindowSystemInterface::handleMouseEvent(window, localPos, globalPos, mouseButtons, m_lastEventButton,
+ QEvent::MouseButtonRelease, keyModifiers, source);
+ }
+ }
+ m_lastEventType = mouseEvent.type;
+ m_lastEventButton = mouseEvent.button;
if (mouseEvent.type >= QEvent::NonClientAreaMouseMove && mouseEvent.type <= QEvent::NonClientAreaMouseButtonDblClick) {
- const Qt::MouseButtons nonclientButtons = queryMouseButtons();
- QWindowSystemInterface::handleFrameStrutMouseEvent(window, localPos, globalPos, nonclientButtons,
+ QWindowSystemInterface::handleFrameStrutMouseEvent(window, localPos, globalPos, mouseButtons,
mouseEvent.button, mouseEvent.type, keyModifiers, source);
return false; // Allow further event processing
}
@@ -742,8 +779,6 @@ bool QWindowsPointerHandler::translateMouseEvent(QWindow *window,
return true;
}
- const Qt::MouseButtons mouseButtons = mouseButtonsFromKeyState(msg.wParam);
-
handleCaptureRelease(window, currentWindowUnderPointer, hwnd, mouseEvent.type, mouseButtons);
handleEnterLeave(window, currentWindowUnderPointer, globalPos);
diff --git a/src/plugins/platforms/windows/qwindowspointerhandler.h b/src/plugins/platforms/windows/qwindowspointerhandler.h
index aebef062bc..ccbb1d3939 100644
--- a/src/plugins/platforms/windows/qwindowspointerhandler.h
+++ b/src/plugins/platforms/windows/qwindowspointerhandler.h
@@ -46,7 +46,7 @@
#include <QtCore/qpointer.h>
#include <QtCore/qscopedpointer.h>
#include <QtCore/qhash.h>
-#include <qpa/qwindowsysteminterface.h>
+#include <QtGui/qevent.h>
QT_BEGIN_NAMESPACE
@@ -64,6 +64,7 @@ public:
QTouchDevice *ensureTouchDevice();
QWindow *windowUnderMouse() const { return m_windowUnderPointer.data(); }
void clearWindowUnderMouse() { m_windowUnderPointer = nullptr; }
+ void clearEvents();
private:
bool translateTouchEvent(QWindow *window, HWND hwnd, QtWindows::WindowsEventType et, MSG msg, PVOID vTouchInfo, unsigned int count);
@@ -79,6 +80,8 @@ private:
QPointer<QWindow> m_currentWindow;
QWindow *m_previousCaptureWindow = nullptr;
bool m_needsEnterOnPointerUpdate = false;
+ QEvent::Type m_lastEventType = QEvent::None;
+ Qt::MouseButton m_lastEventButton = Qt::NoButton;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/windows/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp
index 0520f88935..cc0f3c1a6e 100644
--- a/src/plugins/platforms/windows/qwindowsscreen.cpp
+++ b/src/plugins/platforms/windows/qwindowsscreen.cpp
@@ -91,7 +91,7 @@ static bool monitorData(HMONITOR hMonitor, QWindowsScreenData *data)
} else {
if (const HDC hdc = CreateDC(info.szDevice, nullptr, nullptr, nullptr)) {
const QDpi dpi = monitorDPI(hMonitor);
- data->dpi = dpi.first ? dpi : deviceDPI(hdc);
+ data->dpi = dpi.first > 0 ? dpi : deviceDPI(hdc);
data->depth = GetDeviceCaps(hdc, BITSPIXEL);
data->format = data->depth == 16 ? QImage::Format_RGB16 : QImage::Format_RGB32;
data->physicalSizeMM = QSizeF(GetDeviceCaps(hdc, HORZSIZE), GetDeviceCaps(hdc, VERTSIZE));
@@ -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;
}
@@ -433,6 +435,12 @@ QPlatformScreen::SubpixelAntialiasingType QWindowsScreen::subpixelAntialiasingTy
QWindowsScreenManager::QWindowsScreenManager() = default;
+
+bool QWindowsScreenManager::isSingleScreen()
+{
+ return QWindowsContext::instance()->screenManager().screens().size() < 2;
+}
+
/*!
\brief Triggers synchronization of screens (WM_DISPLAYCHANGE).
diff --git a/src/plugins/platforms/windows/qwindowsscreen.h b/src/plugins/platforms/windows/qwindowsscreen.h
index 824bcb1ad6..8ad012512e 100644
--- a/src/plugins/platforms/windows/qwindowsscreen.h
+++ b/src/plugins/platforms/windows/qwindowsscreen.h
@@ -138,6 +138,8 @@ public:
const QWindowsScreen *screenAtDp(const QPoint &p) const;
const QWindowsScreen *screenForHwnd(HWND hwnd) const;
+ static bool isSingleScreen();
+
private:
void removeScreen(int index);
diff --git a/src/plugins/platforms/windows/qwindowstabletsupport.cpp b/src/plugins/platforms/windows/qwindowstabletsupport.cpp
index fa209f09c4..44b94d044d 100644
--- a/src/plugins/platforms/windows/qwindowstabletsupport.cpp
+++ b/src/plugins/platforms/windows/qwindowstabletsupport.cpp
@@ -435,6 +435,27 @@ bool QWindowsTabletSupport::translateTabletProximityEvent(WPARAM /* wParam */, L
m_currentDevice = m_devices.size();
m_devices.push_back(tabletInit(uniqueId, cursorType));
}
+
+ /**
+ * We should check button map for changes on every proximity event, not
+ * only during initialization phase.
+ *
+ * WARNING: in 2016 there were some Wacom table drivers, which could mess up
+ * button mapping if the remapped button was pressed, while the
+ * application **didn't have input focus**. This bug is somehow
+ * related to the fact that Wacom drivers allow user to configure
+ * per-application button-mappings. If the bug shows up again,
+ * just move this button-map fetching into initialization block.
+ *
+ * See https://bugs.kde.org/show_bug.cgi?id=359561
+ */
+ BYTE logicalButtons[32];
+ memset(logicalButtons, 0, 32);
+ m_winTab32DLL.wTInfo(WTI_CURSORS + currentCursor, CSR_SYSBTNMAP, &logicalButtons);
+ m_devices[m_currentDevice].buttonsMap[0x1] = logicalButtons[0];
+ m_devices[m_currentDevice].buttonsMap[0x2] = logicalButtons[1];
+ m_devices[m_currentDevice].buttonsMap[0x4] = logicalButtons[2];
+
m_devices[m_currentDevice].currentPointerType = pointerType(currentCursor);
m_state = PenProximity;
qCDebug(lcQpaTablet) << "enter proximity for device #"
@@ -446,6 +467,52 @@ bool QWindowsTabletSupport::translateTabletProximityEvent(WPARAM /* wParam */, L
return true;
}
+Qt::MouseButton buttonValueToEnum(DWORD button,
+ const QWindowsTabletDeviceData &tdd) {
+
+ enum : unsigned {
+ leftButtonValue = 0x1,
+ middleButtonValue = 0x2,
+ rightButtonValue = 0x4,
+ doubleClickButtonValue = 0x7
+ };
+
+ button = tdd.buttonsMap.value(button);
+
+ return button == leftButtonValue ? Qt::LeftButton :
+ button == rightButtonValue ? Qt::RightButton :
+ button == doubleClickButtonValue ? Qt::MiddleButton :
+ button == middleButtonValue ? Qt::MiddleButton :
+ button ? Qt::LeftButton /* fallback item */ :
+ Qt::NoButton;
+}
+
+Qt::MouseButtons convertTabletButtons(DWORD btnNew,
+ const QWindowsTabletDeviceData &tdd) {
+
+ Qt::MouseButtons buttons = Qt::NoButton;
+ for (unsigned int i = 0; i < 3; i++) {
+ unsigned int btn = 0x1 << i;
+
+ if (btn & btnNew) {
+ Qt::MouseButton convertedButton =
+ buttonValueToEnum(btn, tdd);
+
+ buttons |= convertedButton;
+
+ /**
+ * If a button that is present in hardware input is
+ * mapped to a Qt::NoButton, it means that it is going
+ * to be eaten by the driver, for example by its
+ * "Pan/Scroll" feature. Therefore we shouldn't handle
+ * any of the events associated to it. We'll just return
+ * Qt::NoButtons here.
+ */
+ }
+ }
+ return buttons;
+}
+
bool QWindowsTabletSupport::translateTabletPacketEvent()
{
static PACKET localPacketBuf[TabletPacketQSize]; // our own tablet packet queue.
@@ -552,9 +619,12 @@ bool QWindowsTabletSupport::translateTabletPacketEvent()
<< tiltY << "tanP:" << tangentialPressure << "rotation:" << rotation;
}
+ Qt::MouseButtons buttons =
+ convertTabletButtons(packet.pkButtons, m_devices.at(m_currentDevice));
+
QWindowSystemInterface::handleTabletEvent(target, packet.pkTime, QPointF(localPos), globalPosF,
currentDevice, currentPointer,
- static_cast<Qt::MouseButtons>(packet.pkButtons),
+ buttons,
pressureNew, tiltX, tiltY,
tangentialPressure, rotation, z,
uniqueId,
diff --git a/src/plugins/platforms/windows/qwindowstabletsupport.h b/src/plugins/platforms/windows/qwindowstabletsupport.h
index d91701d6a5..8f97982308 100644
--- a/src/plugins/platforms/windows/qwindowstabletsupport.h
+++ b/src/plugins/platforms/windows/qwindowstabletsupport.h
@@ -45,6 +45,7 @@
#include <QtCore/qvector.h>
#include <QtCore/qpoint.h>
+#include <QtCore/qhash.h>
#include <wintab.h>
@@ -100,6 +101,7 @@ struct QWindowsTabletDeviceData
qint64 uniqueId = 0;
int currentDevice = 0;
int currentPointerType = 0;
+ QHash<quint8, quint8> buttonsMap;
};
#ifndef QT_NO_DEBUG_STREAM
diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp
index 338e594c7b..38d0f4d979 100644
--- a/src/plugins/platforms/windows/qwindowswindow.cpp
+++ b/src/plugins/platforms/windows/qwindowswindow.cpp
@@ -184,6 +184,7 @@ static inline RECT RECTfromQRect(const QRect &rect)
return result;
}
+
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug d, const RECT &r)
{
@@ -262,6 +263,16 @@ QDebug operator<<(QDebug d, const GUID &guid)
}
#endif // !QT_NO_DEBUG_STREAM
+static void formatBriefRectangle(QDebug &d, const QRect &r)
+{
+ d << r.width() << 'x' << r.height() << forcesign << r.x() << r.y() << noforcesign;
+}
+
+static void formatBriefMargins(QDebug &d, const QMargins &m)
+{
+ d << m.left() << ", " << m.top() << ", " << m.right() << ", " << m.bottom();
+}
+
// QTBUG-43872, for windows that do not have WS_EX_TOOLWINDOW set, WINDOWPLACEMENT
// is in workspace/available area coordinates.
static QPoint windowPlacementOffset(HWND hwnd, const QPoint &point)
@@ -840,10 +851,12 @@ static QSize toNativeSizeConstrained(QSize dip, const QWindow *w)
{
if (QHighDpiScaling::isActive()) {
const qreal factor = QHighDpiScaling::factor(w);
- if (dip.width() > 0 && dip.width() < QWINDOWSIZE_MAX)
- dip.rwidth() *= factor;
- if (dip.height() > 0 && dip.height() < QWINDOWSIZE_MAX)
- dip.rheight() *= factor;
+ if (!qFuzzyCompare(factor, qreal(1))) {
+ if (dip.width() > 0 && dip.width() < QWINDOWSIZE_MAX)
+ dip.setWidth(qRound(qreal(dip.width()) * factor));
+ if (dip.height() > 0 && dip.height() < QWINDOWSIZE_MAX)
+ dip.setHeight(qRound(qreal(dip.height()) * factor));
+ }
}
return dip;
}
@@ -859,35 +872,78 @@ static QSize toNativeSizeConstrained(QSize dip, const QWindow *w)
\ingroup qt-lighthouse-win
*/
-QWindowsGeometryHint::QWindowsGeometryHint(const QWindow *w, const QMargins &cm) :
- minimumSize(toNativeSizeConstrained(w->minimumSize(), w)),
- maximumSize(toNativeSizeConstrained(w->maximumSize(), w)),
- customMargins(cm)
+QMargins QWindowsGeometryHint::frameOnPrimaryScreen(DWORD style, DWORD exStyle)
{
+ RECT rect = {0,0,0,0};
+ style &= ~DWORD(WS_OVERLAPPED); // Not permitted, see docs.
+ if (AdjustWindowRectEx(&rect, style, FALSE, exStyle) == FALSE)
+ qErrnoWarning("%s: AdjustWindowRectEx failed", __FUNCTION__);
+ const QMargins result(qAbs(rect.left), qAbs(rect.top),
+ qAbs(rect.right), qAbs(rect.bottom));
+ qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << " style="
+ << showbase << hex << style << " exStyle=" << exStyle << dec << noshowbase
+ << ' ' << rect << ' ' << result;
+ return result;
}
-bool QWindowsGeometryHint::validSize(const QSize &s) const
+QMargins QWindowsGeometryHint::frameOnPrimaryScreen(HWND hwnd)
{
- const int width = s.width();
- const int height = s.height();
- return width >= minimumSize.width() && width <= maximumSize.width()
- && height >= minimumSize.height() && height <= maximumSize.height();
+ return frameOnPrimaryScreen(DWORD(GetWindowLongPtr(hwnd, GWL_STYLE)),
+ DWORD(GetWindowLongPtr(hwnd, GWL_EXSTYLE)));
}
-QMargins QWindowsGeometryHint::frame(DWORD style, DWORD exStyle)
+QMargins QWindowsGeometryHint::frame(DWORD style, DWORD exStyle, qreal dpi)
{
+ if (QWindowsContext::user32dll.adjustWindowRectExForDpi == nullptr)
+ return frameOnPrimaryScreen(style, exStyle);
RECT rect = {0,0,0,0};
- style &= ~(WS_OVERLAPPED); // Not permitted, see docs.
- if (!AdjustWindowRectEx(&rect, style, FALSE, exStyle))
- qErrnoWarning("%s: AdjustWindowRectEx failed", __FUNCTION__);
+ style &= ~DWORD(WS_OVERLAPPED); // Not permitted, see docs.
+ if (QWindowsContext::user32dll.adjustWindowRectExForDpi(&rect, style, FALSE, exStyle,
+ unsigned(qRound(dpi))) == FALSE) {
+ qErrnoWarning("%s: AdjustWindowRectExForDpi failed", __FUNCTION__);
+ }
const QMargins result(qAbs(rect.left), qAbs(rect.top),
qAbs(rect.right), qAbs(rect.bottom));
qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << " style="
<< showbase << hex << style << " exStyle=" << exStyle << dec << noshowbase
+ << " dpi=" << dpi
<< ' ' << rect << ' ' << result;
return result;
}
+QMargins QWindowsGeometryHint::frame(HWND hwnd, DWORD style, DWORD exStyle)
+{
+ if (QWindowsScreenManager::isSingleScreen())
+ return frameOnPrimaryScreen(style, exStyle);
+ auto screenManager = QWindowsContext::instance()->screenManager();
+ auto screen = screenManager.screenForHwnd(hwnd);
+ if (!screen)
+ screen = screenManager.screens().value(0);
+ const auto dpi = screen ? screen->logicalDpi().first : qreal(96);
+ return frame(style, exStyle, dpi);
+}
+
+// For newly created windows.
+QMargins QWindowsGeometryHint::frame(const QWindow *w, const QRect &geometry,
+ DWORD style, DWORD exStyle)
+{
+ if (!w->isTopLevel() || w->flags().testFlag(Qt::FramelessWindowHint))
+ return {};
+ if (!QWindowsContext::user32dll.adjustWindowRectExForDpi
+ || QWindowsScreenManager::isSingleScreen()
+ || !QWindowsContext::shouldHaveNonClientDpiScaling(w)) {
+ return frameOnPrimaryScreen(style, exStyle);
+ }
+ qreal dpi = 96;
+ auto screenManager = QWindowsContext::instance()->screenManager();
+ auto screen = screenManager.screenAtDp(geometry.center());
+ if (!screen)
+ screen = screenManager.screens().value(0);
+ if (screen)
+ dpi = screen->logicalDpi().first;
+ return QWindowsGeometryHint::frame(style, exStyle, dpi);
+}
+
bool QWindowsGeometryHint::handleCalculateSize(const QMargins &customMargins, const MSG &msg, LRESULT *result)
{
// NCCALCSIZE_PARAMS structure if wParam==TRUE
@@ -907,36 +963,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 +1066,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 +1192,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 +1226,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 +1747,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 +1817,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 +1864,41 @@ void QWindowsWindow::handleResized(int wParam)
}
}
-void QWindowsWindow::checkForScreenChanged()
+static inline bool equalDpi(const QDpi &d1, const QDpi &d2)
+{
+ return qFuzzyCompare(d1.first, d2.first) && qFuzzyCompare(d1.second, d2.second);
+}
+
+void QWindowsWindow::checkForScreenChanged(ScreenChangeMode mode)
{
- if (parent())
+ 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.
@@ -1876,6 +2001,9 @@ bool QWindowsWindow::handleWmPaint(HWND hwnd, UINT message,
{
if (message == WM_ERASEBKGND) // Backing store - ignored.
return true;
+ // QTBUG-75455: Suppress WM_PAINT sent to invisible windows when setting WS_EX_LAYERED
+ if (!window()->isVisible() && (GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED) != 0)
+ return false;
// Ignore invalid update bounding rectangles
RECT updateRect;
if (!GetUpdateRect(m_data.hwnd, &updateRect, FALSE))
@@ -2233,6 +2361,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();
@@ -2443,10 +2580,8 @@ void QWindowsWindow::getSizeHints(MINMAXINFO *mmi) const
{
// We don't apply the min/max size hint as we change the dpi, because we did not adjust the
// QScreen of the window yet so we don't have the min/max with the right ratio
- if (!testFlag(QWindowsWindow::WithinDpiChanged)) {
- const QWindowsGeometryHint hint(window(), m_data.customMargins);
- hint.applyToMinMaxInfo(m_data.hwnd, mmi);
- }
+ if (!testFlag(QWindowsWindow::WithinDpiChanged))
+ QWindowsGeometryHint::applyToMinMaxInfo(window(), fullFrameMargins(), mmi);
// This block fixes QTBUG-8361, QTBUG-4362: Frameless/title-less windows shouldn't cover the
// taskbar when maximized
diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h
index 0d8096ddfa..958564aa86 100644
--- a/src/plugins/platforms/windows/qwindowswindow.h
+++ b/src/plugins/platforms/windows/qwindowswindow.h
@@ -59,24 +59,23 @@ class QDebug;
struct QWindowsGeometryHint
{
- QWindowsGeometryHint() = default;
- explicit QWindowsGeometryHint(const QWindow *w, const QMargins &customMargins);
- static QMargins frame(DWORD style, DWORD exStyle);
+ static QMargins frameOnPrimaryScreen(DWORD style, DWORD exStyle);
+ static QMargins frameOnPrimaryScreen(HWND hwnd);
+ static QMargins frame(DWORD style, DWORD exStyle, qreal dpi);
+ static QMargins frame(HWND hwnd, DWORD style, DWORD exStyle);
+ static QMargins frame(const QWindow *w, const QRect &geometry,
+ DWORD style, DWORD exStyle);
static bool handleCalculateSize(const QMargins &customMargins, const MSG &msg, LRESULT *result);
- void applyToMinMaxInfo(DWORD style, DWORD exStyle, MINMAXINFO *mmi) const;
- void applyToMinMaxInfo(HWND hwnd, MINMAXINFO *mmi) const;
- bool validSize(const QSize &s) const;
-
+ static void applyToMinMaxInfo(const QWindow *w, const QMargins &margins,
+ MINMAXINFO *mmi);
+ static void frameSizeConstraints(const QWindow *w, const QMargins &margins,
+ QSize *minimumSize, QSize *maximumSize);
static inline QPoint mapToGlobal(HWND hwnd, const QPoint &);
static inline QPoint mapToGlobal(const QWindow *w, const QPoint &);
static inline QPoint mapFromGlobal(const HWND hwnd, const QPoint &);
static inline QPoint mapFromGlobal(const QWindow *w, const QPoint &);
static bool positionIncludesFrame(const QWindow *w);
-
- QSize minimumSize;
- QSize maximumSize;
- QMargins customMargins;
};
struct QWindowCreationContext
@@ -85,13 +84,9 @@ struct QWindowCreationContext
const QRect &geometryIn, const QRect &geometry,
const QMargins &customMargins,
DWORD style, DWORD exStyle);
- void applyToMinMaxInfo(MINMAXINFO *mmi) const
- { geometryHint.applyToMinMaxInfo(style, exStyle, mmi); }
+ void applyToMinMaxInfo(MINMAXINFO *mmi) const;
- QWindowsGeometryHint geometryHint;
const QWindow *window;
- DWORD style;
- DWORD exStyle;
QRect requestedGeometryIn; // QWindow scaled
QRect requestedGeometry; // after QPlatformWindow::initialGeometry()
QRect obtainedGeometry;
@@ -221,7 +216,8 @@ public:
HasBorderInFullScreen = 0x200000,
WithinDpiChanged = 0x400000,
VulkanSurface = 0x800000,
- ResizeMoveActive = 0x1000000
+ ResizeMoveActive = 0x1000000,
+ DisableNonClientScaling = 0x2000000
};
QWindowsWindow(QWindow *window, const QWindowsWindowData &data);
@@ -262,6 +258,7 @@ public:
QMargins frameMargins() const override;
QMargins fullFrameMargins() const override;
void setFullFrameMargins(const QMargins &newMargins);
+ void updateFullFrameMargins();
void setOpacity(qreal level) override;
void setMask(const QRegion &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);
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp
index 44328492a6..a427e553f0 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp
@@ -52,6 +52,7 @@
#include "qwindowsuiatableitemprovider.h"
#include "qwindowsuiagridprovider.h"
#include "qwindowsuiagriditemprovider.h"
+#include "qwindowsuiawindowprovider.h"
#include "qwindowscombase.h"
#include "qwindowscontext.h"
#include "qwindowsuiautils.h"
@@ -263,6 +264,11 @@ HRESULT QWindowsUiaMainProvider::GetPatternProvider(PATTERNID idPattern, IUnknow
return UIA_E_ELEMENTNOTAVAILABLE;
switch (idPattern) {
+ case UIA_WindowPatternId:
+ if (accessible->parent() && (accessible->parent()->role() == QAccessible::Application)) {
+ *pRetVal = new QWindowsUiaWindowProvider(id());
+ }
+ break;
case UIA_TextPatternId:
case UIA_TextPattern2Id:
// All text controls.
@@ -352,8 +358,7 @@ HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pR
if (!accessible)
return UIA_E_ELEMENTNOTAVAILABLE;
- bool clientTopLevel = (accessible->role() == QAccessible::Client)
- && accessible->parent() && (accessible->parent()->role() == QAccessible::Application);
+ bool topLevelWindow = accessible->parent() && (accessible->parent()->role() == QAccessible::Application);
switch (idProp) {
case UIA_ProcessIdPropertyId:
@@ -379,7 +384,7 @@ HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pR
setVariantString(QStringLiteral("Qt"), pRetVal);
break;
case UIA_ControlTypePropertyId:
- if (clientTopLevel) {
+ if (topLevelWindow) {
// Reports a top-level widget as a window, instead of "custom".
setVariantI4(UIA_WindowControlTypeId, pRetVal);
} else {
@@ -391,10 +396,20 @@ HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pR
setVariantString(accessible->text(QAccessible::Help), pRetVal);
break;
case UIA_HasKeyboardFocusPropertyId:
- setVariantBool(accessible->state().focused, pRetVal);
+ if (topLevelWindow) {
+ // Windows set the active state to true when they are focused
+ setVariantBool(accessible->state().active, pRetVal);
+ } else {
+ setVariantBool(accessible->state().focused, pRetVal);
+ }
break;
case UIA_IsKeyboardFocusablePropertyId:
- setVariantBool(accessible->state().focusable, pRetVal);
+ if (topLevelWindow) {
+ // Windows should always be focusable
+ setVariantBool(true, pRetVal);
+ } else {
+ setVariantBool(accessible->state().focusable, pRetVal);
+ }
break;
case UIA_IsOffscreenPropertyId:
setVariantBool(accessible->state().offscreen, pRetVal);
@@ -424,7 +439,7 @@ HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pR
break;
case UIA_NamePropertyId: {
QString name = accessible->text(QAccessible::Name);
- if (name.isEmpty() && clientTopLevel)
+ if (name.isEmpty() && topLevelWindow)
name = QCoreApplication::applicationName();
setVariantString(name, pRetVal);
break;
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp
index 7980826b49..ab04384616 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiautils.cpp
@@ -114,7 +114,7 @@ void setVariantBool(bool value, VARIANT *variant)
void setVariantDouble(double value, VARIANT *variant)
{
variant->vt = VT_R8;
- variant->boolVal = value;
+ variant->dblVal = value;
}
BSTR bStrFromQString(const QString &value)
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiawindowprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiawindowprovider.cpp
new file mode 100644
index 0000000000..3738aa72ff
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiawindowprovider.cpp
@@ -0,0 +1,168 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtGui/qtguiglobal.h>
+#if QT_CONFIG(accessibility)
+
+#include "qwindowsuiawindowprovider.h"
+#include "qwindowsuiautils.h"
+#include "qwindowscontext.h"
+
+#include <QtGui/qaccessible.h>
+#include <QtGui/private/qwindow_p.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QWindowsUiAutomation;
+
+
+QWindowsUiaWindowProvider::QWindowsUiaWindowProvider(QAccessible::Id id) :
+ QWindowsUiaBaseProvider(id)
+{
+}
+
+QWindowsUiaWindowProvider::~QWindowsUiaWindowProvider()
+{
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsUiaWindowProvider::SetVisualState(WindowVisualState state) {
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible || !accessible->window())
+ return UIA_E_ELEMENTNOTAVAILABLE;
+ auto window = accessible->window();
+ switch (state) {
+ case WindowVisualState_Normal:
+ window->showNormal();
+ break;
+ case WindowVisualState_Maximized:
+ window->showMaximized();
+ break;
+ case WindowVisualState_Minimized:
+ window->showMinimized();
+ break;
+ }
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsUiaWindowProvider::Close() {
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible || !accessible->window())
+ return UIA_E_ELEMENTNOTAVAILABLE;
+ accessible->window()->close();
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsUiaWindowProvider::WaitForInputIdle(int milliseconds, __RPC__out BOOL *pRetVal) {
+ Q_UNUSED(milliseconds);
+ Q_UNUSED(pRetVal);
+ return UIA_E_NOTSUPPORTED;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsUiaWindowProvider::get_CanMaximize(__RPC__out BOOL *pRetVal) {
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible || !accessible->window())
+ return UIA_E_ELEMENTNOTAVAILABLE;
+
+ auto window = accessible->window();
+ auto flags = window->flags();
+
+ *pRetVal = (!(flags & Qt::MSWindowsFixedSizeDialogHint)
+ && (flags & Qt::WindowMaximizeButtonHint)
+ && ((flags & Qt::CustomizeWindowHint)
+ || window->maximumSize() == QSize(QWINDOWSIZE_MAX, QWINDOWSIZE_MAX)));
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsUiaWindowProvider::get_CanMinimize(__RPC__out BOOL *pRetVal) {
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible || !accessible->window())
+ return UIA_E_ELEMENTNOTAVAILABLE;
+ *pRetVal = accessible->window()->flags() & Qt::WindowMinimizeButtonHint;
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsUiaWindowProvider::get_IsModal(__RPC__out BOOL *pRetVal) {
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible || !accessible->window())
+ return UIA_E_ELEMENTNOTAVAILABLE;
+ *pRetVal = accessible->window()->isModal();
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsUiaWindowProvider::get_WindowVisualState(__RPC__out enum WindowVisualState *pRetVal) {
+ qCDebug(lcQpaUiAutomation) << __FUNCTION__;
+ QAccessibleInterface *accessible = accessibleInterface();
+ if (!accessible || !accessible->window())
+ return UIA_E_ELEMENTNOTAVAILABLE;
+ auto visibility = accessible->window()->visibility();
+ switch (visibility) {
+ case QWindow::FullScreen:
+ case QWindow::Maximized:
+ *pRetVal = WindowVisualState_Maximized;
+ break;
+ case QWindow::Minimized:
+ *pRetVal = WindowVisualState_Minimized;
+ break;
+ default:
+ *pRetVal = WindowVisualState_Normal;
+ break;
+ }
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsUiaWindowProvider::get_WindowInteractionState(__RPC__out enum WindowInteractionState *pRetVal) {
+ Q_UNUSED(pRetVal);
+ return UIA_E_NOTSUPPORTED;
+}
+
+HRESULT STDMETHODCALLTYPE QWindowsUiaWindowProvider::get_IsTopmost(__RPC__out BOOL *pRetVal) {
+ Q_UNUSED(pRetVal);
+ return UIA_E_NOTSUPPORTED;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_CONFIG(accessibility)
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiawindowprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiawindowprovider.h
new file mode 100644
index 0000000000..343fb275f7
--- /dev/null
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiawindowprovider.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSUIAWINDOWPROVIDER_H
+#define QWINDOWSUIAWINDOWPROVIDER_H
+
+#include <QtGui/qtguiglobal.h>
+#if QT_CONFIG(accessibility)
+
+#include "qwindowsuiabaseprovider.h"
+
+QT_BEGIN_NAMESPACE
+
+class QWindowsUiaWindowProvider : public QWindowsUiaBaseProvider,
+ public QWindowsComBase<IWindowProvider>
+{
+ Q_DISABLE_COPY(QWindowsUiaWindowProvider)
+public:
+ explicit QWindowsUiaWindowProvider(QAccessible::Id id);
+ ~QWindowsUiaWindowProvider() override;
+
+ HRESULT STDMETHODCALLTYPE SetVisualState(WindowVisualState state) override;
+ HRESULT STDMETHODCALLTYPE Close( void) override;
+ HRESULT STDMETHODCALLTYPE WaitForInputIdle(int milliseconds, __RPC__out BOOL *pRetVal) override;
+ HRESULT STDMETHODCALLTYPE get_CanMaximize(__RPC__out BOOL *pRetVal) override;
+ HRESULT STDMETHODCALLTYPE get_CanMinimize(__RPC__out BOOL *pRetVal) override;
+ HRESULT STDMETHODCALLTYPE get_IsModal(__RPC__out BOOL *pRetVal) override;
+ HRESULT STDMETHODCALLTYPE get_WindowVisualState(__RPC__out WindowVisualState *pRetVal) override;
+ HRESULT STDMETHODCALLTYPE get_WindowInteractionState(__RPC__out WindowInteractionState *pRetVal) override;
+ HRESULT STDMETHODCALLTYPE get_IsTopmost(__RPC__out BOOL *pRetVal) override;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_CONFIG(accessibility)
+
+#endif // QWINDOWSUIAWINDOWPROVIDER_H
diff --git a/src/plugins/platforms/windows/uiautomation/uiautomation.pri b/src/plugins/platforms/windows/uiautomation/uiautomation.pri
index e3071766d9..5d4fa5755b 100644
--- a/src/plugins/platforms/windows/uiautomation/uiautomation.pri
+++ b/src/plugins/platforms/windows/uiautomation/uiautomation.pri
@@ -18,6 +18,7 @@ SOURCES += \
$$PWD/qwindowsuiatableitemprovider.cpp \
$$PWD/qwindowsuiagridprovider.cpp \
$$PWD/qwindowsuiagriditemprovider.cpp \
+ $$PWD/qwindowsuiawindowprovider.cpp \
$$PWD/qwindowsuiautils.cpp
HEADERS += \
@@ -37,6 +38,7 @@ HEADERS += \
$$PWD/qwindowsuiatableitemprovider.h \
$$PWD/qwindowsuiagridprovider.h \
$$PWD/qwindowsuiagriditemprovider.h \
+ $$PWD/qwindowsuiawindowprovider.h \
$$PWD/qwindowsuiautils.h
mingw: LIBS *= -luuid
diff --git a/src/plugins/platforms/winrt/qwinrttheme.cpp b/src/plugins/platforms/winrt/qwinrttheme.cpp
index 283825a880..0e1504b1c1 100644
--- a/src/plugins/platforms/winrt/qwinrttheme.cpp
+++ b/src/plugins/platforms/winrt/qwinrttheme.cpp
@@ -44,6 +44,8 @@
#include <QtCore/qfunctions_winrt.h>
#include <QtGui/QPalette>
+#include <QtFontDatabaseSupport/private/qwinrtfontdatabase_p.h>
+
#include <wrl.h>
#include <windows.ui.h>
#include <windows.ui.viewmanagement.h>
@@ -96,7 +98,13 @@ static IUISettings *uiSettings()
class QWinRTThemePrivate
{
public:
+ QWinRTThemePrivate()
+ : monospaceFont(QWinRTFontDatabase::familyForStyleHint(QFont::Monospace))
+ {
+ }
+
QPalette palette;
+ QFont monospaceFont;
};
static inline QColor fromColor(const Color &color)
@@ -321,4 +329,14 @@ const QPalette *QWinRTTheme::palette(Palette type) const
return QPlatformTheme::palette(type);
}
+const QFont *QWinRTTheme::font(QPlatformTheme::Font type) const
+{
+ Q_D(const QWinRTTheme);
+ qCDebug(lcQpaTheme) << __FUNCTION__ << type;
+ if (type == QPlatformTheme::FixedFont)
+ return &d->monospaceFont;
+
+ return QPlatformTheme::font(type);
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/winrt/qwinrttheme.h b/src/plugins/platforms/winrt/qwinrttheme.h
index cc5fc851e7..acf5a54a94 100644
--- a/src/plugins/platforms/winrt/qwinrttheme.h
+++ b/src/plugins/platforms/winrt/qwinrttheme.h
@@ -58,6 +58,7 @@ public:
QPlatformDialogHelper *createPlatformDialogHelper(DialogType type) const override;
const QPalette *palette(Palette type = SystemPalette) const override;
+ const QFont *font(Font type = SystemFont) const override;
static QVariant styleHint(QPlatformIntegration::StyleHint hint);
QVariant themeHint(ThemeHint hint) const override;
diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
index e4da207b00..bc09fe2f91 100644
--- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
@@ -240,6 +240,10 @@ void QXcbConnection::xi2SetupDevice(void *info, bool removeExisting)
} else if (name.contains("uc-logic") && isTablet) {
tabletData.pointerType = QTabletEvent::Pen;
dbgType = QLatin1String("pen");
+ } else if (name.contains("ugee")) {
+ isTablet = true;
+ tabletData.pointerType = QTabletEvent::Pen;
+ dbgType = QLatin1String("pen");
} else {
isTablet = false;
}
diff --git a/src/plugins/platforms/xcb/qxcbdrag.cpp b/src/plugins/platforms/xcb/qxcbdrag.cpp
index aa329d8cb7..1ce947165d 100644
--- a/src/plugins/platforms/xcb/qxcbdrag.cpp
+++ b/src/plugins/platforms/xcb/qxcbdrag.cpp
@@ -202,7 +202,7 @@ void QXcbDrag::startDrag()
if (connection()->mouseGrabber() == nullptr)
shapedPixmapWindow()->setMouseGrabEnabled(true);
- auto nativePixelPos = QHighDpi::toNativePixels(QCursor::pos(), initiatorWindow);
+ auto nativePixelPos = QHighDpi::toNativePixels(QCursor::pos(), initiatorWindow.data());
move(nativePixelPos, QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers());
}