summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms')
-rw-r--r--src/plugins/platforms/android/androidjniaccessibility.cpp311
-rw-r--r--src/plugins/platforms/android/androidjniaccessibility.h4
-rw-r--r--src/plugins/platforms/android/androidjniinput.cpp38
-rw-r--r--src/plugins/platforms/android/androidjnimain.cpp75
-rw-r--r--src/plugins/platforms/android/androidjnimain.h3
-rw-r--r--src/plugins/platforms/android/qandroidplatformaccessibility.cpp8
-rw-r--r--src/plugins/platforms/android/qandroidplatformaccessibility.h1
-rw-r--r--src/plugins/platforms/android/qandroidplatformintegration.cpp32
-rw-r--r--src/plugins/platforms/android/qandroidplatformintegration.h7
-rw-r--r--src/plugins/platforms/android/qandroidplatformscreen.cpp68
-rw-r--r--src/plugins/platforms/android/qandroidplatformscreen.h14
-rw-r--r--src/plugins/platforms/cocoa/qcocoacursor.mm37
-rw-r--r--src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm7
-rw-r--r--src/plugins/platforms/cocoa/qcocoascreen.mm6
-rw-r--r--src/plugins/platforms/cocoa/qnsview_mouse.mm16
-rw-r--r--src/plugins/platforms/cocoa/qnsview_tablet.mm7
-rw-r--r--src/plugins/platforms/ios/qiosdocumentpickercontroller.h4
-rw-r--r--src/plugins/platforms/ios/qiosdocumentpickercontroller.mm15
-rw-r--r--src/plugins/platforms/windows/qwindowsglcontext.cpp17
-rw-r--r--src/plugins/platforms/windows/qwindowsopengltester.cpp3
-rw-r--r--src/plugins/platforms/windows/qwindowswindow.cpp24
-rw-r--r--src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxwindow.cpp9
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_xi2.cpp2
-rw-r--r--src/plugins/platforms/xcb/qxcbwindow.cpp2
24 files changed, 584 insertions, 126 deletions
diff --git a/src/plugins/platforms/android/androidjniaccessibility.cpp b/src/plugins/platforms/android/androidjniaccessibility.cpp
index 989d0d18f4..afe15677b6 100644
--- a/src/plugins/platforms/android/androidjniaccessibility.cpp
+++ b/src/plugins/platforms/android/androidjniaccessibility.cpp
@@ -50,6 +50,7 @@
#include <QtCore/private/qjnihelpers_p.h>
#include <QtCore/private/qjni_p.h>
#include <QtGui/private/qhighdpiscaling_p.h>
+#include <QtCore/QObject>
#include "qdebug.h"
@@ -75,6 +76,28 @@ namespace QtAndroidAccessibility
static bool m_accessibilityActivated = false;
+ // This object is needed to schedule the execution of the code that
+ // deals with accessibility instances to the Qt main thread.
+ // Because of that almost every method here is split into two parts.
+ // The _helper part is executed in the context of m_accessibilityContext
+ // on the main thread. The other part is executed in Java thread.
+ static QObject *m_accessibilityContext = nullptr;
+
+ // This method is called from the Qt main thread, and normally a
+ // QGuiApplication instance will be used as a parent.
+ void createAccessibilityContextObject(QObject *parent)
+ {
+ if (m_accessibilityContext)
+ m_accessibilityContext->deleteLater();
+ m_accessibilityContext = new QObject(parent);
+ }
+
+ template <typename Func, typename Ret>
+ void runInObjectContext(QObject *context, Func &&func, Ret *retVal)
+ {
+ QMetaObject::invokeMethod(context, func, Qt::BlockingQueuedConnection, retVal);
+ }
+
void initialize()
{
QJNIObjectPrivate::callStaticMethod<void>(QtAndroid::applicationClass(),
@@ -115,9 +138,12 @@ namespace QtAndroidAccessibility
QtAndroid::notifyAccessibilityLocationChange();
}
+ static int parentId_helper(int objectId); // forward declaration
+
void notifyObjectHide(uint accessibilityObjectId)
{
- QtAndroid::notifyObjectHide(accessibilityObjectId);
+ const auto parentObjectId = parentId_helper(accessibilityObjectId);
+ QtAndroid::notifyObjectHide(accessibilityObjectId, parentObjectId);
}
void notifyObjectFocus(uint accessibilityObjectId)
@@ -125,7 +151,15 @@ namespace QtAndroidAccessibility
QtAndroid::notifyObjectFocus(accessibilityObjectId);
}
- static jintArray childIdListForAccessibleObject(JNIEnv *env, jobject /*thiz*/, jint objectId)
+ static jstring jvalueForAccessibleObject(int objectId); // forward declaration
+
+ void notifyValueChanged(uint accessibilityObjectId)
+ {
+ jstring value = jvalueForAccessibleObject(accessibilityObjectId);
+ QtAndroid::notifyValueChanged(accessibilityObjectId, value);
+ }
+
+ static QVarLengthArray<int, 8> childIdListForAccessibleObject_helper(int objectId)
{
QAccessibleInterface *iface = interfaceFromId(objectId);
if (iface && iface->isValid()) {
@@ -137,6 +171,18 @@ namespace QtAndroidAccessibility
if (child && child->isValid())
ifaceIdArray.append(QAccessible::uniqueId(child));
}
+ return ifaceIdArray;
+ }
+ return {};
+ }
+
+ static jintArray childIdListForAccessibleObject(JNIEnv *env, jobject /*thiz*/, jint objectId)
+ {
+ if (m_accessibilityContext) {
+ QVarLengthArray<jint, 8> ifaceIdArray;
+ runInObjectContext(m_accessibilityContext, [objectId]() {
+ return childIdListForAccessibleObject_helper(objectId);
+ }, &ifaceIdArray);
jintArray jArray = env->NewIntArray(jsize(ifaceIdArray.count()));
env->SetIntArrayRegion(jArray, 0, ifaceIdArray.count(), ifaceIdArray.data());
return jArray;
@@ -145,7 +191,7 @@ namespace QtAndroidAccessibility
return env->NewIntArray(jsize(0));
}
- static jint parentId(JNIEnv */*env*/, jobject /*thiz*/, jint objectId)
+ static int parentId_helper(int objectId)
{
QAccessibleInterface *iface = interfaceFromId(objectId);
if (iface && iface->isValid()) {
@@ -159,7 +205,18 @@ namespace QtAndroidAccessibility
return -1;
}
- static jobject screenRect(JNIEnv *env, jobject /*thiz*/, jint objectId)
+ static jint parentId(JNIEnv */*env*/, jobject /*thiz*/, jint objectId)
+ {
+ jint result = -1;
+ if (m_accessibilityContext) {
+ runInObjectContext(m_accessibilityContext, [objectId]() {
+ return parentId_helper(objectId);
+ }, &result);
+ }
+ return result;
+ }
+
+ static QRect screenRect_helper(int objectId)
{
QRect rect;
QAccessibleInterface *iface = interfaceFromId(objectId);
@@ -171,14 +228,24 @@ namespace QtAndroidAccessibility
const auto parentRect = QHighDpi::toNativePixels(iface->parent()->rect(), iface->parent()->window());
rect = rect.intersected(parentRect);
}
+ return rect;
+ }
+ static jobject screenRect(JNIEnv *env, jobject /*thiz*/, jint objectId)
+ {
+ QRect rect;
+ if (m_accessibilityContext) {
+ runInObjectContext(m_accessibilityContext, [objectId]() {
+ return screenRect_helper(objectId);
+ }, &rect);
+ }
jclass rectClass = env->FindClass("android/graphics/Rect");
jmethodID ctor = env->GetMethodID(rectClass, "<init>", "(IIII)V");
jobject jrect = env->NewObject(rectClass, ctor, rect.left(), rect.top(), rect.right(), rect.bottom());
return jrect;
}
- static jint hitTest(JNIEnv */*env*/, jobject /*thiz*/, jfloat x, jfloat y)
+ static int hitTest_helper(float x, float y)
{
QAccessibleInterface *root = interfaceFromId(-1);
if (root && root->isValid()) {
@@ -196,17 +263,29 @@ namespace QtAndroidAccessibility
return -1;
}
+ static jint hitTest(JNIEnv */*env*/, jobject /*thiz*/, jfloat x, jfloat y)
+ {
+ jint result = -1;
+ if (m_accessibilityContext) {
+ runInObjectContext(m_accessibilityContext, [x, y]() {
+ return hitTest_helper(x, y);
+ }, &result);
+ }
+ return result;
+ }
+
static void invokeActionOnInterfaceInMainThread(QAccessibleActionInterface* actionInterface,
const QString& action)
{
+ // Queue the action and return back to Java thread, so that we do not
+ // block it for too long
QMetaObject::invokeMethod(qApp, [actionInterface, action]() {
actionInterface->doAction(action);
- });
+ }, Qt::QueuedConnection);
}
- static jboolean clickAction(JNIEnv */*env*/, jobject /*thiz*/, jint objectId)
+ static bool clickAction_helper(int objectId)
{
-// qDebug() << "A11Y: CLICK: " << objectId;
QAccessibleInterface *iface = interfaceFromId(objectId);
if (!iface || !iface->isValid() || !iface->actionInterface())
return false;
@@ -225,20 +304,45 @@ namespace QtAndroidAccessibility
return true;
}
- static jboolean scrollForward(JNIEnv */*env*/, jobject /*thiz*/, jint objectId)
+ static jboolean clickAction(JNIEnv */*env*/, jobject /*thiz*/, jint objectId)
+ {
+ bool result = false;
+ if (m_accessibilityContext) {
+ runInObjectContext(m_accessibilityContext, [objectId]() {
+ return clickAction_helper(objectId);
+ }, &result);
+ }
+ return result;
+ }
+
+ static bool scroll_helper(int objectId, const QString &actionName)
{
QAccessibleInterface *iface = interfaceFromId(objectId);
if (iface && iface->isValid())
- return QAccessibleBridgeUtils::performEffectiveAction(iface, QAccessibleActionInterface::increaseAction());
+ return QAccessibleBridgeUtils::performEffectiveAction(iface, actionName);
return false;
}
+ static jboolean scrollForward(JNIEnv */*env*/, jobject /*thiz*/, jint objectId)
+ {
+ bool result = false;
+ if (m_accessibilityContext) {
+ runInObjectContext(m_accessibilityContext, [objectId]() {
+ return scroll_helper(objectId, QAccessibleActionInterface::increaseAction());
+ }, &result);
+ }
+ return result;
+ }
+
static jboolean scrollBackward(JNIEnv */*env*/, jobject /*thiz*/, jint objectId)
{
- QAccessibleInterface *iface = interfaceFromId(objectId);
- if (iface && iface->isValid())
- return QAccessibleBridgeUtils::performEffectiveAction(iface, QAccessibleActionInterface::decreaseAction());
- return false;
+ bool result = false;
+ if (m_accessibilityContext) {
+ runInObjectContext(m_accessibilityContext, [objectId]() {
+ return scroll_helper(objectId, QAccessibleActionInterface::decreaseAction());
+ }, &result);
+ }
+ return result;
}
@@ -251,66 +355,173 @@ if (!clazz) { \
//__android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, METHOD_NAME, METHOD_SIGNATURE);
+ static QString textFromValue(QAccessibleInterface *iface)
+ {
+ QString valueStr;
+ QAccessibleValueInterface *valueIface = iface->valueInterface();
+ if (valueIface) {
+ const QVariant valueVar = valueIface->currentValue();
+ const auto type = static_cast<QMetaType::Type>(valueVar.type());
+ if (type == QMetaType::Double || type == QMetaType::Float) {
+ // QVariant's toString() formats floating-point values with
+ // FloatingPointShortest, which is not an accessible
+ // representation; nor, in many cases, is it suitable to the UI
+ // element whose value we're looking at. So roll our own
+ // A11Y-friendly conversion to string.
+ const double val = valueVar.toDouble();
+ // Try to use minimumStepSize() to determine precision
+ bool stepIsValid = false;
+ const double step = qAbs(valueIface->minimumStepSize().toDouble(&stepIsValid));
+ if (!stepIsValid || qFuzzyIsNull(step)) {
+ // Ignore step, use default precision
+ valueStr = qFuzzyIsNull(val) ? QStringLiteral("0") : QString::number(val, 'f');
+ } else {
+ const int precision = [](double s) {
+ int count = 0;
+ while (s < 1. && !qFuzzyCompare(s, 1.)) {
+ ++count;
+ s *= 10;
+ }
+ // If s is now 1.25, we want to show some more digits,
+ // but don't want to get silly with a step like 1./7;
+ // so only include a few extra digits.
+ const int stop = count + 3;
+ const auto fractional = [](double v) {
+ double whole = 0.0;
+ std::modf(v + 0.5, &whole);
+ return qAbs(v - whole);
+ };
+ s = fractional(s);
+ while (count < stop && !qFuzzyIsNull(s)) {
+ ++count;
+ s = fractional(s * 10);
+ }
+ return count;
+ }(step);
+ valueStr = qFuzzyIsNull(val / step) ? QStringLiteral("0")
+ : QString::number(val, 'f', precision);
+ }
+ } else {
+ valueStr = valueVar.toString();
+ }
+ }
+ return valueStr;
+ }
+ static jstring jvalueForAccessibleObject(int objectId)
+ {
+ QAccessibleInterface *iface = interfaceFromId(objectId);
+ const QString value = textFromValue(iface);
+ QJNIEnvironmentPrivate env;
+ jstring jstr = env->NewString((jchar*)value.constData(), (jsize)value.size());
+#ifdef QT_DEBUG
+ env->ExceptionDescribe();
+#endif // QT_DEBUG
+ env->ExceptionClear();
+ return jstr;
+ }
- static jstring descriptionForAccessibleObject_helper(JNIEnv *env, QAccessibleInterface *iface)
+ static QString descriptionForInterface(QAccessibleInterface *iface)
{
QString desc;
if (iface && iface->isValid()) {
+ bool hasValue = false;
desc = iface->text(QAccessible::Name);
if (desc.isEmpty())
desc = iface->text(QAccessible::Description);
if (desc.isEmpty()) {
desc = iface->text(QAccessible::Value);
- if (desc.isEmpty()) {
- if (QAccessibleValueInterface *valueIface = iface->valueInterface()) {
- desc= valueIface->currentValue().toString();
- }
- }
+ hasValue = !desc.isEmpty();
+ }
+ if (!hasValue) {
+ if (!desc.isEmpty())
+ desc.append(QChar(QChar::Space));
+ desc.append(textFromValue(iface));
}
}
- return env->NewString((jchar*) desc.constData(), (jsize) desc.size());
+ return desc;
}
- static jstring descriptionForAccessibleObject(JNIEnv *env, jobject /*thiz*/, jint objectId)
+ static QString descriptionForAccessibleObject_helper(int objectId)
{
QAccessibleInterface *iface = interfaceFromId(objectId);
- return descriptionForAccessibleObject_helper(env, iface);
+ return descriptionForInterface(iface);
}
- static bool populateNode(JNIEnv *env, jobject /*thiz*/, jint objectId, jobject node)
+ static jstring descriptionForAccessibleObject(JNIEnv *env, jobject /*thiz*/, jint objectId)
{
+ QString desc;
+ if (m_accessibilityContext) {
+ runInObjectContext(m_accessibilityContext, [objectId]() {
+ return descriptionForAccessibleObject_helper(objectId);
+ }, &desc);
+ }
+ return env->NewString((jchar*) desc.constData(), (jsize) desc.size());
+ }
+
+
+ struct NodeInfo
+ {
+ bool valid = false;
+ QAccessible::State state;
+ QStringList actions;
+ QString description;
+ bool hasTextSelection = false;
+ int selectionStart = 0;
+ int selectionEnd = 0;
+ };
+
+ static NodeInfo populateNode_helper(int objectId)
+ {
+ NodeInfo info;
QAccessibleInterface *iface = interfaceFromId(objectId);
- if (!iface || !iface->isValid()) {
+ if (iface && iface->isValid()) {
+ info.valid = true;
+ info.state = iface->state();
+ info.actions = QAccessibleBridgeUtils::effectiveActionNames(iface);
+ info.description = descriptionForInterface(iface);
+ QAccessibleTextInterface *textIface = iface->textInterface();
+ if (textIface && (textIface->selectionCount() > 0)) {
+ info.hasTextSelection = true;
+ textIface->selection(0, &info.selectionStart, &info.selectionEnd);
+ }
+ }
+ return info;
+ }
+
+ static jboolean populateNode(JNIEnv *env, jobject /*thiz*/, jint objectId, jobject node)
+ {
+ NodeInfo info;
+ if (m_accessibilityContext) {
+ runInObjectContext(m_accessibilityContext, [objectId]() {
+ return populateNode_helper(objectId);
+ }, &info);
+ }
+ if (!info.valid) {
__android_log_print(ANDROID_LOG_WARN, m_qtTag, "Accessibility: populateNode for Invalid ID");
return false;
}
- QAccessible::State state = iface->state();
- const QStringList actions = QAccessibleBridgeUtils::effectiveActionNames(iface);
- const bool hasClickableAction = actions.contains(QAccessibleActionInterface::pressAction())
- || actions.contains(QAccessibleActionInterface::toggleAction());
- const bool hasIncreaseAction = actions.contains(QAccessibleActionInterface::increaseAction());
- const bool hasDecreaseAction = actions.contains(QAccessibleActionInterface::decreaseAction());
- // try to fill in the text property, this is what the screen reader reads
- jstring jdesc = descriptionForAccessibleObject_helper(env, iface);
-
- if (QAccessibleTextInterface *textIface = iface->textInterface()) {
- if (m_setTextSelectionMethodID && textIface->selectionCount() > 0) {
- int startSelection;
- int endSelection;
- textIface->selection(0, &startSelection, &endSelection);
- env->CallVoidMethod(node, m_setTextSelectionMethodID, startSelection, endSelection);
- }
+ const bool hasClickableAction =
+ info.actions.contains(QAccessibleActionInterface::pressAction()) ||
+ info.actions.contains(QAccessibleActionInterface::toggleAction());
+ const bool hasIncreaseAction =
+ info.actions.contains(QAccessibleActionInterface::increaseAction());
+ const bool hasDecreaseAction =
+ info.actions.contains(QAccessibleActionInterface::decreaseAction());
+
+ if (info.hasTextSelection && m_setTextSelectionMethodID) {
+ env->CallVoidMethod(node, m_setTextSelectionMethodID, info.selectionStart,
+ info.selectionEnd);
}
- env->CallVoidMethod(node, m_setCheckableMethodID, (bool)state.checkable);
- env->CallVoidMethod(node, m_setCheckedMethodID, (bool)state.checked);
- env->CallVoidMethod(node, m_setEditableMethodID, state.editable);
- env->CallVoidMethod(node, m_setEnabledMethodID, !state.disabled);
- env->CallVoidMethod(node, m_setFocusableMethodID, (bool)state.focusable);
- env->CallVoidMethod(node, m_setFocusedMethodID, (bool)state.focused);
- env->CallVoidMethod(node, m_setVisibleToUserMethodID, !state.invisible);
+ env->CallVoidMethod(node, m_setCheckableMethodID, (bool)info.state.checkable);
+ env->CallVoidMethod(node, m_setCheckedMethodID, (bool)info.state.checked);
+ env->CallVoidMethod(node, m_setEditableMethodID, info.state.editable);
+ env->CallVoidMethod(node, m_setEnabledMethodID, !info.state.disabled);
+ env->CallVoidMethod(node, m_setFocusableMethodID, (bool)info.state.focusable);
+ env->CallVoidMethod(node, m_setFocusedMethodID, (bool)info.state.focused);
+ env->CallVoidMethod(node, m_setVisibleToUserMethodID, !info.state.invisible);
env->CallVoidMethod(node, m_setScrollableMethodID, hasIncreaseAction || hasDecreaseAction);
env->CallVoidMethod(node, m_setClickableMethodID, hasClickableAction);
@@ -326,7 +537,9 @@ if (!clazz) { \
if (hasDecreaseAction)
env->CallVoidMethod(node, m_addActionMethodID, (int)0x00002000); // ACTION_SCROLL_BACKWARD defined in AccessibilityNodeInfo
-
+ // try to fill in the text property, this is what the screen reader reads
+ jstring jdesc = env->NewString((jchar*)info.description.constData(),
+ (jsize)info.description.size());
//CALL_METHOD(node, "setText", "(Ljava/lang/CharSequence;)V", jdesc)
env->CallVoidMethod(node, m_setContentDescriptionMethodID, jdesc);
diff --git a/src/plugins/platforms/android/androidjniaccessibility.h b/src/plugins/platforms/android/androidjniaccessibility.h
index de9d32a099..a6e2edee5e 100644
--- a/src/plugins/platforms/android/androidjniaccessibility.h
+++ b/src/plugins/platforms/android/androidjniaccessibility.h
@@ -44,6 +44,8 @@
QT_BEGIN_NAMESPACE
+class QObject;
+
namespace QtAndroidAccessibility
{
void initialize();
@@ -52,6 +54,8 @@ namespace QtAndroidAccessibility
void notifyLocationChange();
void notifyObjectHide(uint accessibilityObjectId);
void notifyObjectFocus(uint accessibilityObjectId);
+ void notifyValueChanged(uint accessibilityObjectId);
+ void createAccessibilityContextObject(QObject *parent);
}
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/android/androidjniinput.cpp b/src/plugins/platforms/android/androidjniinput.cpp
index 05516929bf..1abd23485e 100644
--- a/src/plugins/platforms/android/androidjniinput.cpp
+++ b/src/plugins/platforms/android/androidjniinput.cpp
@@ -263,18 +263,14 @@ namespace QtAndroidInput
}
}
- static void touchEnd(JNIEnv */*env*/, jobject /*thiz*/, jint /*winId*/, jint /*action*/)
+ static QTouchDevice *getTouchDevice()
{
- if (m_touchPoints.isEmpty())
- return;
-
- QMutexLocker lock(QtAndroid::platformInterfaceMutex());
QAndroidPlatformIntegration *platformIntegration = QtAndroid::androidPlatformIntegration();
if (!platformIntegration)
- return;
+ return nullptr;
QTouchDevice *touchDevice = platformIntegration->touchDevice();
- if (touchDevice == 0) {
+ if (!touchDevice) {
touchDevice = new QTouchDevice;
touchDevice->setType(QTouchDevice::TouchScreen);
touchDevice->setCapabilities(QTouchDevice::Position
@@ -285,10 +281,37 @@ namespace QtAndroidInput
platformIntegration->setTouchDevice(touchDevice);
}
+ return touchDevice;
+ }
+
+ static void touchEnd(JNIEnv * /*env*/, jobject /*thiz*/, jint /*winId*/, jint /*action*/)
+ {
+ if (m_touchPoints.isEmpty())
+ return;
+
+ QMutexLocker lock(QtAndroid::platformInterfaceMutex());
+ QTouchDevice *touchDevice = getTouchDevice();
+ if (!touchDevice)
+ return;
+
QWindow *window = QtAndroid::topLevelWindowAt(m_touchPoints.at(0).area.center().toPoint());
QWindowSystemInterface::handleTouchEvent(window, touchDevice, m_touchPoints);
}
+ static void touchCancel(JNIEnv * /*env*/, jobject /*thiz*/, jint /*winId*/)
+ {
+ if (m_touchPoints.isEmpty())
+ return;
+
+ QMutexLocker lock(QtAndroid::platformInterfaceMutex());
+ QTouchDevice *touchDevice = getTouchDevice();
+ if (!touchDevice)
+ return;
+
+ QWindow *window = QtAndroid::topLevelWindowAt(m_touchPoints.at(0).area.center().toPoint());
+ QWindowSystemInterface::handleTouchCancelEvent(window, touchDevice);
+ }
+
static bool isTabletEventSupported(JNIEnv */*env*/, jobject /*thiz*/)
{
#if QT_CONFIG(tabletevent)
@@ -840,6 +863,7 @@ namespace QtAndroidInput
{"touchBegin","(I)V",(void*)touchBegin},
{"touchAdd","(IIIZIIFFFF)V",(void*)touchAdd},
{"touchEnd","(II)V",(void*)touchEnd},
+ {"touchCancel", "(I)V", (void *)touchCancel},
{"mouseDown", "(III)V", (void *)mouseDown},
{"mouseUp", "(III)V", (void *)mouseUp},
{"mouseMove", "(III)V", (void *)mouseMove},
diff --git a/src/plugins/platforms/android/androidjnimain.cpp b/src/plugins/platforms/android/androidjnimain.cpp
index 8c6d9d9be3..9796a4c9c6 100644
--- a/src/plugins/platforms/android/androidjnimain.cpp
+++ b/src/plugins/platforms/android/androidjnimain.cpp
@@ -1,7 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
@@ -107,7 +107,6 @@ static sem_t m_exitSemaphore, m_terminateSemaphore;
QHash<int, AndroidSurfaceClient *> m_surfaces;
static QBasicMutex m_surfacesMutex;
-static int m_surfaceId = 1;
static QAndroidPlatformIntegration *m_androidPlatformIntegration = nullptr;
@@ -231,9 +230,10 @@ namespace QtAndroid
QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass, "notifyAccessibilityLocationChange");
}
- void notifyObjectHide(uint accessibilityObjectId)
+ void notifyObjectHide(uint accessibilityObjectId, uint parentObjectId)
{
- QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass, "notifyObjectHide","(I)V", accessibilityObjectId);
+ QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass, "notifyObjectHide", "(II)V",
+ accessibilityObjectId, parentObjectId);
}
void notifyObjectFocus(uint accessibilityObjectId)
@@ -241,6 +241,13 @@ namespace QtAndroid
QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass, "notifyObjectFocus","(I)V", accessibilityObjectId);
}
+ void notifyValueChanged(uint accessibilityObjectId, jstring value)
+ {
+ QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass, "notifyValueChanged",
+ "(ILjava/lang/String;)V", accessibilityObjectId,
+ value);
+ }
+
void notifyQtAndroidPluginRunning(bool running)
{
QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass, "notifyQtAndroidPluginRunning","(Z)V", running);
@@ -340,6 +347,12 @@ namespace QtAndroid
return manufacturer + QLatin1Char(' ') + model;
}
+ jint generateViewId()
+ {
+ return QJNIObjectPrivate::callStaticMethod<jint>("android/view/View","generateViewId",
+ "()I");
+ }
+
int createSurface(AndroidSurfaceClient *client, const QRect &geometry, bool onTop, int imageDepth)
{
QJNIEnvironmentPrivate env;
@@ -347,7 +360,7 @@ namespace QtAndroid
return -1;
m_surfacesMutex.lock();
- int surfaceId = m_surfaceId++;
+ jint surfaceId = generateViewId();
m_surfaces[surfaceId] = client;
m_surfacesMutex.unlock();
@@ -370,7 +383,7 @@ namespace QtAndroid
int insertNativeView(jobject view, const QRect &geometry)
{
m_surfacesMutex.lock();
- const int surfaceId = m_surfaceId++;
+ jint surfaceId = generateViewId();
m_surfaces[surfaceId] = nullptr; // dummy
m_surfacesMutex.unlock();
@@ -554,16 +567,21 @@ static void startQtApplication(JNIEnv */*env*/, jclass /*clazz*/)
vm->AttachCurrentThread(&env, &args);
}
+ // Register type for invokeMethod() calls.
+ qRegisterMetaType<Qt::ScreenOrientation>("Qt::ScreenOrientation");
+
// Register resources if they are available
if (QFile{QStringLiteral("assets:/android_rcc_bundle.rcc")}.exists())
QResource::registerResource(QStringLiteral("assets:/android_rcc_bundle.rcc"));
- QVarLengthArray<const char *> params(m_applicationParams.size());
- for (int i = 0; i < m_applicationParams.size(); i++)
- params[i] = static_cast<const char *>(m_applicationParams[i].constData());
+ const int argc = m_applicationParams.size();
+ QVarLengthArray<char *> argv(argc + 1);
+ for (int i = 0; i < argc; i++)
+ argv[i] = m_applicationParams[i].data();
+ argv[argc] = nullptr;
startQtAndroidPluginCalled.fetchAndAddRelease(1);
- int ret = m_main(m_applicationParams.length(), const_cast<char **>(params.data()));
+ int ret = m_main(argc, argv.data());
if (m_mainLibraryHnd) {
int res = dlclose(m_mainLibraryHnd);
@@ -655,12 +673,11 @@ static void setDisplayMetrics(JNIEnv */*env*/, jclass /*clazz*/,
jint widthPixels, jint heightPixels,
jint desktopWidthPixels, jint desktopHeightPixels,
jdouble xdpi, jdouble ydpi,
- jdouble scaledDensity, jdouble density, bool forceUpdate)
+ jdouble scaledDensity, jdouble density, jfloat refreshRate)
{
// Android does not give us the correct screen size for immersive mode, but
// the surface does have the right size
- bool updateDesktopSize = m_desktopWidthPixels != desktopWidthPixels;
widthPixels = qMax(widthPixels, desktopWidthPixels);
heightPixels = qMax(heightPixels, desktopHeightPixels);
@@ -678,12 +695,13 @@ static void setDisplayMetrics(JNIEnv */*env*/, jclass /*clazz*/,
widthPixels,
heightPixels);
} else {
- m_androidPlatformIntegration->setDisplayMetrics(qRound(double(widthPixels) / xdpi * 25.4),
- qRound(double(heightPixels) / ydpi * 25.4));
- m_androidPlatformIntegration->setScreenSize(widthPixels, heightPixels);
- if (updateDesktopSize || forceUpdate) {
- m_androidPlatformIntegration->setDesktopSize(desktopWidthPixels, desktopHeightPixels);
- }
+ const QSize physicalSize(qRound(double(widthPixels) / xdpi * 25.4),
+ qRound(double(heightPixels) / ydpi * 25.4));
+ const QSize screenSize(widthPixels, heightPixels);
+ const QRect availableGeometry(0, 0, desktopWidthPixels, desktopHeightPixels);
+ m_androidPlatformIntegration->setScreenSizeParameters(physicalSize, screenSize,
+ availableGeometry);
+ m_androidPlatformIntegration->setRefreshRate(refreshRate);
}
}
@@ -778,12 +796,22 @@ static void handleOrientationChanged(JNIEnv */*env*/, jobject /*thiz*/, jint new
QAndroidPlatformIntegration::setScreenOrientation(screenOrientation, native);
QMutexLocker lock(&m_platformMutex);
if (m_androidPlatformIntegration) {
- QPlatformScreen *screen = m_androidPlatformIntegration->screen();
- QWindowSystemInterface::handleScreenOrientationChange(screen->screen(),
- screenOrientation);
+ QAndroidPlatformScreen *screen = m_androidPlatformIntegration->screen();
+ // Use invokeMethod to keep the certain order of the "geometry change"
+ // and "orientation change" event handling.
+ if (screen) {
+ QMetaObject::invokeMethod(screen, "setOrientation", Qt::AutoConnection,
+ Q_ARG(Qt::ScreenOrientation, screenOrientation));
+ }
}
}
+static void handleRefreshRateChanged(JNIEnv */*env*/, jclass /*cls*/, jfloat refreshRate)
+{
+ if (m_androidPlatformIntegration)
+ m_androidPlatformIntegration->setRefreshRate(refreshRate);
+}
+
static void onActivityResult(JNIEnv */*env*/, jclass /*cls*/,
jint requestCode,
jint resultCode,
@@ -809,14 +837,15 @@ static JNINativeMethod methods[] = {
{"quitQtCoreApplication", "()V", (void *)quitQtCoreApplication},
{"terminateQt", "()V", (void *)terminateQt},
{"waitForServiceSetup", "()V", (void *)waitForServiceSetup},
- {"setDisplayMetrics", "(IIIIDDDDZ)V", (void *)setDisplayMetrics},
+ {"setDisplayMetrics", "(IIIIDDDDF)V", (void *)setDisplayMetrics},
{"setSurface", "(ILjava/lang/Object;II)V", (void *)setSurface},
{"updateWindow", "()V", (void *)updateWindow},
{"updateApplicationState", "(I)V", (void *)updateApplicationState},
{"handleOrientationChanged", "(II)V", (void *)handleOrientationChanged},
{"onActivityResult", "(IILandroid/content/Intent;)V", (void *)onActivityResult},
{"onNewIntent", "(Landroid/content/Intent;)V", (void *)onNewIntent},
- {"onBind", "(Landroid/content/Intent;)Landroid/os/IBinder;", (void *)onBind}
+ {"onBind", "(Landroid/content/Intent;)Landroid/os/IBinder;", (void *)onBind},
+ {"handleRefreshRateChanged", "(F)V", (void *)handleRefreshRateChanged}
};
#define FIND_AND_CHECK_CLASS(CLASS_NAME) \
diff --git a/src/plugins/platforms/android/androidjnimain.h b/src/plugins/platforms/android/androidjnimain.h
index 641b2dbdb4..240a0b83f9 100644
--- a/src/plugins/platforms/android/androidjnimain.h
+++ b/src/plugins/platforms/android/androidjnimain.h
@@ -96,8 +96,9 @@ namespace QtAndroid
jobject createBitmapDrawable(jobject bitmap, JNIEnv *env = 0);
void notifyAccessibilityLocationChange();
- void notifyObjectHide(uint accessibilityObjectId);
+ void notifyObjectHide(uint accessibilityObjectId, uint parentObjectId);
void notifyObjectFocus(uint accessibilityObjectId);
+ void notifyValueChanged(uint accessibilityObjectId, jstring value);
void notifyQtAndroidPluginRunning(bool running);
const char *classErrorMsgFmt();
diff --git a/src/plugins/platforms/android/qandroidplatformaccessibility.cpp b/src/plugins/platforms/android/qandroidplatformaccessibility.cpp
index cc05dad749..706d1bb4fa 100644
--- a/src/plugins/platforms/android/qandroidplatformaccessibility.cpp
+++ b/src/plugins/platforms/android/qandroidplatformaccessibility.cpp
@@ -66,7 +66,15 @@ void QAndroidPlatformAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *
QtAndroidAccessibility::notifyObjectHide(event->uniqueId());
} else if (event->type() == QAccessible::Focus) {
QtAndroidAccessibility::notifyObjectFocus(event->uniqueId());
+ } else if (event->type() == QAccessible::ValueChanged) {
+ QtAndroidAccessibility::notifyValueChanged(event->uniqueId());
}
}
+void QAndroidPlatformAccessibility::setRootObject(QObject *obj)
+{
+ QPlatformAccessibility::setRootObject(obj);
+ QtAndroidAccessibility::createAccessibilityContextObject(obj);
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/android/qandroidplatformaccessibility.h b/src/plugins/platforms/android/qandroidplatformaccessibility.h
index 8216c05fa6..df3fe43a04 100644
--- a/src/plugins/platforms/android/qandroidplatformaccessibility.h
+++ b/src/plugins/platforms/android/qandroidplatformaccessibility.h
@@ -52,6 +52,7 @@ public:
~QAndroidPlatformAccessibility();
void notifyAccessibilityUpdate(QAccessibleEvent *event) override;
+ void setRootObject(QObject *obj) override;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/android/qandroidplatformintegration.cpp b/src/plugins/platforms/android/qandroidplatformintegration.cpp
index 3074dee26f..6fe0caba2e 100644
--- a/src/plugins/platforms/android/qandroidplatformintegration.cpp
+++ b/src/plugins/platforms/android/qandroidplatformintegration.cpp
@@ -188,9 +188,10 @@ QAndroidPlatformIntegration::QAndroidPlatformIntegration(const QStringList &para
m_primaryScreen = new QAndroidPlatformScreen();
QWindowSystemInterface::handleScreenAdded(m_primaryScreen);
- m_primaryScreen->setPhysicalSize(QSize(m_defaultPhysicalSizeWidth, m_defaultPhysicalSizeHeight));
- m_primaryScreen->setSize(QSize(m_defaultScreenWidth, m_defaultScreenHeight));
- m_primaryScreen->setAvailableGeometry(QRect(0, 0, m_defaultGeometryWidth, m_defaultGeometryHeight));
+ const QSize physicalSize(m_defaultPhysicalSizeWidth, m_defaultPhysicalSizeHeight);
+ const QSize screenSize(m_defaultScreenWidth, m_defaultScreenHeight);
+ const QRect geometry(0, 0, m_defaultGeometryWidth, m_defaultGeometryHeight);
+ m_primaryScreen->setSizeParameters(physicalSize, screenSize, geometry);
m_mainThread = QThread::currentThread();
@@ -463,10 +464,10 @@ void QAndroidPlatformIntegration::setScreenOrientation(Qt::ScreenOrientation cur
void QAndroidPlatformIntegration::flushPendingUpdates()
{
- m_primaryScreen->setPhysicalSize(QSize(m_defaultPhysicalSizeWidth,
- m_defaultPhysicalSizeHeight));
- m_primaryScreen->setSize(QSize(m_defaultScreenWidth, m_defaultScreenHeight));
- m_primaryScreen->setAvailableGeometry(QRect(0, 0, m_defaultGeometryWidth, m_defaultGeometryHeight));
+ const QSize physicalSize(m_defaultPhysicalSizeWidth, m_defaultPhysicalSizeHeight);
+ const QSize screenSize(m_defaultScreenWidth, m_defaultScreenHeight);
+ const QRect geometry(0, 0, m_defaultGeometryWidth, m_defaultGeometryHeight);
+ m_primaryScreen->setSizeParameters(physicalSize, screenSize, geometry);
}
#ifndef QT_NO_ACCESSIBILITY
@@ -494,6 +495,23 @@ void QAndroidPlatformIntegration::setScreenSize(int width, int height)
QMetaObject::invokeMethod(m_primaryScreen, "setSize", Qt::AutoConnection, Q_ARG(QSize, QSize(width, height)));
}
+void QAndroidPlatformIntegration::setScreenSizeParameters(const QSize &physicalSize,
+ const QSize &screenSize,
+ const QRect &availableGeometry)
+{
+ if (m_primaryScreen) {
+ QMetaObject::invokeMethod(m_primaryScreen, "setSizeParameters", Qt::AutoConnection,
+ Q_ARG(QSize, physicalSize), Q_ARG(QSize, screenSize),
+ Q_ARG(QRect, availableGeometry));
+ }
+}
+
+void QAndroidPlatformIntegration::setRefreshRate(qreal refreshRate)
+{
+ if (m_primaryScreen)
+ QMetaObject::invokeMethod(m_primaryScreen, "setRefreshRate", Qt::AutoConnection,
+ Q_ARG(qreal, refreshRate));
+}
#if QT_CONFIG(vulkan)
QPlatformVulkanInstance *QAndroidPlatformIntegration::createPlatformVulkanInstance(QVulkanInstance *instance) const
diff --git a/src/plugins/platforms/android/qandroidplatformintegration.h b/src/plugins/platforms/android/qandroidplatformintegration.h
index ecbde4f951..f34972b81e 100644
--- a/src/plugins/platforms/android/qandroidplatformintegration.h
+++ b/src/plugins/platforms/android/qandroidplatformintegration.h
@@ -96,6 +96,13 @@ public:
virtual void setDesktopSize(int width, int height);
virtual void setDisplayMetrics(int width, int height);
void setScreenSize(int width, int height);
+ // The 3 methods above were replaced by a new one, so that we could have
+ // a better control over "geometry changed" event handling. Technically
+ // they are no longer used and can be removed. Not doing it now, because
+ // I'm not sure if it might be helpful to have them or not.
+ void setScreenSizeParameters(const QSize &physicalSize, const QSize &screenSize,
+ const QRect &availableGeometry);
+ void setRefreshRate(qreal refreshRate);
bool isVirtualDesktop() { return true; }
QPlatformFontDatabase *fontDatabase() const override;
diff --git a/src/plugins/platforms/android/qandroidplatformscreen.cpp b/src/plugins/platforms/android/qandroidplatformscreen.cpp
index 7e036868fc..0f60202eb9 100644
--- a/src/plugins/platforms/android/qandroidplatformscreen.cpp
+++ b/src/plugins/platforms/android/qandroidplatformscreen.cpp
@@ -55,6 +55,7 @@
#include <android/native_window_jni.h>
#include <qguiapplication.h>
+#include <QtCore/private/qjnihelpers_p.h>
#include <QtGui/QGuiApplication>
#include <QtGui/QWindow>
#include <QtGui/private/qwindow_p.h>
@@ -104,6 +105,42 @@ QAndroidPlatformScreen::QAndroidPlatformScreen()
m_physicalSize.setHeight(QAndroidPlatformIntegration::m_defaultPhysicalSizeHeight);
m_physicalSize.setWidth(QAndroidPlatformIntegration::m_defaultPhysicalSizeWidth);
connect(qGuiApp, &QGuiApplication::applicationStateChanged, this, &QAndroidPlatformScreen::applicationStateChanged);
+
+ QJNIObjectPrivate activity(QtAndroid::activity());
+ if (!activity.isValid())
+ return;
+ QJNIObjectPrivate display;
+ if (QtAndroidPrivate::androidSdkVersion() < 30) {
+ display = activity.callObjectMethod("getWindowManager", "()Landroid/view/WindowManager;")
+ .callObjectMethod("getDefaultDisplay", "()Landroid/view/Display;");
+ } else {
+ display = activity.callObjectMethod("getDisplay", "()Landroid/view/Display;");
+ }
+ if (!display.isValid())
+ return;
+ m_name = display.callObjectMethod("getName", "()Ljava/lang/String;").toString();
+ m_refreshRate = display.callMethod<jfloat>("getRefreshRate");
+ if (QtAndroidPrivate::androidSdkVersion() < 23) {
+ m_modes << Mode { .size = m_physicalSize.toSize(), .refreshRate = m_refreshRate };
+ return;
+ }
+ QJNIEnvironmentPrivate env;
+ const jint currentMode = display.callObjectMethod("getMode", "()Landroid/view/Display$Mode;")
+ .callMethod<jint>("getModeId");
+ const auto modes = display.callObjectMethod("getSupportedModes",
+ "()[Landroid/view/Display$Mode;");
+ const auto modesArray = jobjectArray(modes.object());
+ const auto sz = env->GetArrayLength(modesArray);
+ for (jsize i = 0; i < sz; ++i) {
+ auto mode = QJNIObjectPrivate::fromLocalRef(env->GetObjectArrayElement(modesArray, i));
+ if (currentMode == mode.callMethod<jint>("getModeId"))
+ m_currentMode = m_modes.size();
+ m_modes << Mode { .size = QSize { mode.callMethod<jint>("getPhysicalHeight"),
+ mode.callMethod<jint>("getPhysicalWidth") },
+ .refreshRate = mode.callMethod<jfloat>("getRefreshRate") };
+ }
+ if (m_modes.isEmpty())
+ m_modes << Mode { .size = m_physicalSize.toSize(), .refreshRate = m_refreshRate };
}
QAndroidPlatformScreen::~QAndroidPlatformScreen()
@@ -243,6 +280,37 @@ void QAndroidPlatformScreen::setSize(const QSize &size)
QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), geometry(), availableGeometry());
}
+void QAndroidPlatformScreen::setSizeParameters(const QSize &physicalSize, const QSize &size,
+ const QRect &availableGeometry)
+{
+ // The goal of this method is to set all geometry-related parameters
+ // at the same time and generate only one screen geometry change event.
+ m_physicalSize = physicalSize;
+ m_size = size;
+ // If available geometry has changed, the event will be handled in
+ // setAvailableGeometry. Otherwise we need to explicitly handle it to
+ // retain the behavior, because setSize() does the handling unconditionally.
+ if (m_availableGeometry != availableGeometry) {
+ setAvailableGeometry(availableGeometry);
+ } else {
+ QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), geometry(),
+ this->availableGeometry());
+ }
+}
+
+void QAndroidPlatformScreen::setRefreshRate(qreal refreshRate)
+{
+ if (refreshRate == m_refreshRate)
+ return;
+ m_refreshRate = refreshRate;
+ QWindowSystemInterface::handleScreenRefreshRateChange(QPlatformScreen::screen(), refreshRate);
+}
+
+void QAndroidPlatformScreen::setOrientation(Qt::ScreenOrientation orientation)
+{
+ QWindowSystemInterface::handleScreenOrientationChange(QPlatformScreen::screen(), orientation);
+}
+
void QAndroidPlatformScreen::setAvailableGeometry(const QRect &rect)
{
QMutexLocker lock(&m_surfaceMutex);
diff --git a/src/plugins/platforms/android/qandroidplatformscreen.h b/src/plugins/platforms/android/qandroidplatformscreen.h
index 54b3c5b8a8..e73ea31a86 100644
--- a/src/plugins/platforms/android/qandroidplatformscreen.h
+++ b/src/plugins/platforms/android/qandroidplatformscreen.h
@@ -69,6 +69,12 @@ public:
QImage::Format format() const override { return m_format; }
QSizeF physicalSize() const override { return m_physicalSize; }
+ QString name() const override { return m_name; }
+ QVector<Mode> modes() const override { return m_modes; }
+ int currentMode() const override { return m_currentMode; }
+ int preferredMode() const override { return m_currentMode; }
+ qreal refreshRate() const override { return m_refreshRate; }
+
inline QWindow *topWindow() const;
QWindow *topLevelAt(const QPoint & p) const override;
@@ -87,6 +93,10 @@ public slots:
void setPhysicalSize(const QSize &size);
void setAvailableGeometry(const QRect &rect);
void setSize(const QSize &size);
+ void setSizeParameters(const QSize &physicalSize, const QSize &size,
+ const QRect &availableGeometry);
+ void setRefreshRate(qreal refreshRate);
+ void setOrientation(Qt::ScreenOrientation orientation);
protected:
bool event(QEvent *event) override;
@@ -100,6 +110,10 @@ protected:
int m_depth;
QImage::Format m_format;
QSizeF m_physicalSize;
+ qreal m_refreshRate;
+ QString m_name;
+ QVector<Mode> m_modes;
+ int m_currentMode = 0;
private:
QDpi logicalDpi() const override;
diff --git a/src/plugins/platforms/cocoa/qcocoacursor.mm b/src/plugins/platforms/cocoa/qcocoacursor.mm
index 8ca72ec619..c963ff937c 100644
--- a/src/plugins/platforms/cocoa/qcocoacursor.mm
+++ b/src/plugins/platforms/cocoa/qcocoacursor.mm
@@ -45,6 +45,15 @@
#include <QtGui/QBitmap>
+#if !defined(QT_APPLE_NO_PRIVATE_APIS)
+@interface NSCursor()
++ (id)_windowResizeNorthWestSouthEastCursor;
++ (id)_windowResizeNorthEastSouthWestCursor;
++ (id)_windowResizeNorthSouthCursor;
++ (id)_windowResizeEastWestCursor;
+@end
+#endif // QT_APPLE_NO_PRIVATE_APIS
+
QT_BEGIN_NAMESPACE
QCocoaCursor::QCocoaCursor()
@@ -116,7 +125,7 @@ NSCursor *QCocoaCursor::convertCursor(QCursor *cursor)
return nil;
const Qt::CursorShape newShape = cursor->shape();
- NSCursor *cocoaCursor;
+ NSCursor *cocoaCursor = nil;
// Check for a suitable built-in NSCursor first:
switch (newShape) {
@@ -157,7 +166,29 @@ NSCursor *QCocoaCursor::convertCursor(QCursor *cursor)
case Qt::DragLinkCursor:
cocoaCursor = [NSCursor dragLinkCursor];
break;
- default : {
+#if !defined(QT_APPLE_NO_PRIVATE_APIS)
+ case Qt::SizeVerCursor:
+ if ([NSCursor respondsToSelector:@selector(_windowResizeNorthSouthCursor)])
+ cocoaCursor = [NSCursor _windowResizeNorthSouthCursor];
+ break;
+ case Qt::SizeHorCursor:
+ if ([NSCursor respondsToSelector:@selector(_windowResizeEastWestCursor)])
+ cocoaCursor = [NSCursor _windowResizeEastWestCursor];
+ break;
+ case Qt::SizeBDiagCursor:
+ if ([NSCursor respondsToSelector:@selector(_windowResizeNorthEastSouthWestCursor)])
+ cocoaCursor = [NSCursor _windowResizeNorthEastSouthWestCursor];
+ break;
+ case Qt::SizeFDiagCursor:
+ if ([NSCursor respondsToSelector:@selector(_windowResizeNorthWestSouthEastCursor)])
+ cocoaCursor = [NSCursor _windowResizeNorthWestSouthEastCursor];
+ break;
+#endif // QT_APPLE_NO_PRIVATE_APIS
+ default:
+ break;
+ }
+
+ if (!cocoaCursor) {
// No suitable OS cursor exist, use cursors provided
// by Qt for the rest. Check for a cached cursor:
cocoaCursor = m_cursors.value(newShape);
@@ -172,8 +203,6 @@ NSCursor *QCocoaCursor::convertCursor(QCursor *cursor)
m_cursors.insert(newShape, cocoaCursor);
}
-
- break; }
}
return cocoaCursor;
}
diff --git a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm
index 5dac9b9a91..7c3f69b408 100644
--- a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm
+++ b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm
@@ -292,7 +292,12 @@ static QString strippedText(QString s)
}
}
- QString qtFileName = QFileInfo(QString::fromNSString(filename)).fileName();
+ // Treat symbolic links and aliases to directories like directories
+ QFileInfo fileInfo(QString::fromNSString(filename));
+ if (fileInfo.isSymLink() && QFileInfo(fileInfo.symLinkTarget()).isDir())
+ return YES;
+
+ QString qtFileName = fileInfo.fileName();
// No filter means accept everything
bool nameMatches = mSelectedNameFilter->isEmpty();
// Check if the current file name filter accepts the file:
diff --git a/src/plugins/platforms/cocoa/qcocoascreen.mm b/src/plugins/platforms/cocoa/qcocoascreen.mm
index 581ea01fcc..c03c191376 100644
--- a/src/plugins/platforms/cocoa/qcocoascreen.mm
+++ b/src/plugins/platforms/cocoa/qcocoascreen.mm
@@ -230,13 +230,13 @@ static QString displayName(CGDirectDisplayID displayID)
NSDictionary *info = [(__bridge NSDictionary*)IODisplayCreateInfoDictionary(
display, kIODisplayOnlyPreferredName) autorelease];
- if ([[info objectForKey:@kDisplayVendorID] longValue] != CGDisplayVendorNumber(displayID))
+ if ([[info objectForKey:@kDisplayVendorID] unsignedIntValue] != CGDisplayVendorNumber(displayID))
continue;
- if ([[info objectForKey:@kDisplayProductID] longValue] != CGDisplayModelNumber(displayID))
+ if ([[info objectForKey:@kDisplayProductID] unsignedIntValue] != CGDisplayModelNumber(displayID))
continue;
- if ([[info objectForKey:@kDisplaySerialNumber] longValue] != CGDisplaySerialNumber(displayID))
+ if ([[info objectForKey:@kDisplaySerialNumber] unsignedIntValue] != CGDisplaySerialNumber(displayID))
continue;
NSDictionary *localizedNames = [info objectForKey:@kDisplayProductName];
diff --git a/src/plugins/platforms/cocoa/qnsview_mouse.mm b/src/plugins/platforms/cocoa/qnsview_mouse.mm
index ef80a47e0f..73995639aa 100644
--- a/src/plugins/platforms/cocoa/qnsview_mouse.mm
+++ b/src/plugins/platforms/cocoa/qnsview_mouse.mm
@@ -678,7 +678,11 @@
}
} else if (theEvent.momentumPhase == NSEventPhaseBegan) {
Q_ASSERT(!pixelDelta.isNull() && !angleDelta.isNull());
- phase = Qt::ScrollUpdate; // Send as update, it has a delta
+ // If we missed finding a momentum NSEventPhaseBegan when the non-momentum
+ // phase ended we need to treat this as a scroll begin, to not confuse client
+ // code. Otherwise we treat it as a continuation of the existing scroll.
+ phase = m_scrolling ? Qt::ScrollUpdate : Qt::ScrollBegin;
+ m_scrolling = true;
} else if (theEvent.momentumPhase == NSEventPhaseChanged) {
phase = Qt::ScrollMomentum;
} else if (theEvent.phase == NSEventPhaseCancelled
@@ -690,6 +694,16 @@
Q_ASSERT(theEvent.momentumPhase != NSEventPhaseStationary);
}
+ // Sanitize deltas for events that should not result in scrolling.
+ // On macOS 12.1 this phase has been observed to report deltas.
+ if (theEvent.phase == NSEventPhaseCancelled) {
+ if (!pixelDelta.isNull() || !angleDelta.isNull()) {
+ qCInfo(lcQpaMouse) << "Ignoring unexpected delta for" << theEvent;
+ pixelDelta = QPoint();
+ angleDelta = QPoint();
+ }
+ }
+
// Prevent keyboard modifier state from changing during scroll event streams.
// A two-finger trackpad flick generates a stream of scroll events. We want
// the keyboard modifier state to be the state at the beginning of the
diff --git a/src/plugins/platforms/cocoa/qnsview_tablet.mm b/src/plugins/platforms/cocoa/qnsview_tablet.mm
index ba1fa55892..f365403502 100644
--- a/src/plugins/platforms/cocoa/qnsview_tablet.mm
+++ b/src/plugins/platforms/cocoa/qnsview_tablet.mm
@@ -148,9 +148,6 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent)
device = QTabletEvent::Stylus;
} else {
switch (bits & 0x0F06) {
- case 0x0802:
- device = QTabletEvent::Stylus;
- break;
case 0x0902:
device = QTabletEvent::Airbrush;
break;
@@ -163,8 +160,8 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent)
case 0x0804:
device = QTabletEvent::RotationStylus;
break;
- default:
- device = QTabletEvent::NoDevice;
+ default: // usually 0x0802, but 0 on iPad sidecar with Apple Pencil
+ device = QTabletEvent::Stylus;
}
}
return device;
diff --git a/src/plugins/platforms/ios/qiosdocumentpickercontroller.h b/src/plugins/platforms/ios/qiosdocumentpickercontroller.h
index dba6f24fc5..2fe3c9e382 100644
--- a/src/plugins/platforms/ios/qiosdocumentpickercontroller.h
+++ b/src/plugins/platforms/ios/qiosdocumentpickercontroller.h
@@ -41,6 +41,8 @@
#include "qiosfiledialog.h"
-@interface QIOSDocumentPickerController : UIDocumentPickerViewController <UIDocumentPickerDelegate, UINavigationControllerDelegate>
+@interface QIOSDocumentPickerController : UIDocumentPickerViewController <UIDocumentPickerDelegate,
+ UINavigationControllerDelegate,
+ UIAdaptivePresentationControllerDelegate>
- (instancetype)initWithQIOSFileDialog:(QIOSFileDialog *)fileDialog;
@end
diff --git a/src/plugins/platforms/ios/qiosdocumentpickercontroller.mm b/src/plugins/platforms/ios/qiosdocumentpickercontroller.mm
index c1b641e839..da78d41115 100644
--- a/src/plugins/platforms/ios/qiosdocumentpickercontroller.mm
+++ b/src/plugins/platforms/ios/qiosdocumentpickercontroller.mm
@@ -72,6 +72,7 @@
m_fileDialog = fileDialog;
self.modalPresentationStyle = UIModalPresentationFormSheet;
self.delegate = self;
+ self.presentationController.delegate = self;
if (m_fileDialog->options()->fileMode() == QFileDialogOptions::ExistingFiles)
self.allowsMultipleSelection = YES;
@@ -100,4 +101,18 @@
emit m_fileDialog->reject();
}
+- (void)presentationControllerDidDismiss:(UIPresentationController *)presentationController
+{
+ Q_UNUSED(presentationController);
+
+ // "Called on the delegate when the user has taken action to dismiss the
+ // presentation successfully, after all animations are finished.
+ // This is not called if the presentation is dismissed programatically."
+
+ // So if document picker's view was dismissed, for example by swiping it away,
+ // we got this method called. But not if the dialog was cancelled or a file
+ // was selected.
+ emit m_fileDialog->reject();
+}
+
@end
diff --git a/src/plugins/platforms/windows/qwindowsglcontext.cpp b/src/plugins/platforms/windows/qwindowsglcontext.cpp
index 6fa5a8a2b3..1f49f0aefd 100644
--- a/src/plugins/platforms/windows/qwindowsglcontext.cpp
+++ b/src/plugins/platforms/windows/qwindowsglcontext.cpp
@@ -48,6 +48,7 @@
#include <qpa/qplatformnativeinterface.h>
#include <QtPlatformHeaders/qwglnativecontext.h>
+#include <private/qsystemlibrary_p.h>
#include <algorithm>
#include <wingdi.h>
@@ -162,19 +163,25 @@ QFunctionPointer QWindowsOpengl32DLL::resolve(const char *name)
bool QWindowsOpengl32DLL::init(bool softwareRendering)
{
- const QByteArray opengl32 = QByteArrayLiteral("opengl32.dll");
- const QByteArray swopengl = QByteArrayLiteral("opengl32sw.dll");
+ const QByteArray opengl32 = QByteArrayLiteral("opengl32");
+ const QByteArray swopengl = QByteArrayLiteral("opengl32sw");
+ bool useSystemLib = false;
QByteArray openglDll = qgetenv("QT_OPENGL_DLL");
- if (openglDll.isEmpty())
+ if (openglDll.isEmpty()) {
openglDll = softwareRendering ? swopengl : opengl32;
+ useSystemLib = !softwareRendering;
+ }
openglDll = openglDll.toLower();
m_nonOpengl32 = openglDll != opengl32;
qCDebug(lcQpaGl) << "Qt: Using WGL and OpenGL from" << openglDll;
- m_lib = ::LoadLibraryA(openglDll.constData());
+ if (useSystemLib)
+ m_lib = QSystemLibrary::load((wchar_t*)(QString::fromLatin1(openglDll).utf16()));
+ else
+ m_lib = LoadLibraryA(openglDll.constData());
if (!m_lib) {
qErrnoWarning(::GetLastError(), "Failed to load %s", openglDll.constData());
return false;
@@ -184,7 +191,7 @@ bool QWindowsOpengl32DLL::init(bool softwareRendering)
// Load opengl32.dll always. GDI functions like ChoosePixelFormat do
// GetModuleHandle for opengl32.dll and behave differently (and call back into
// opengl32) when the module is present. This is fine for dummy contexts and windows.
- ::LoadLibraryA("opengl32.dll");
+ QSystemLibrary::load(L"opengl32");
}
wglCreateContext = reinterpret_cast<HGLRC (WINAPI *)(HDC)>(resolve("wglCreateContext"));
diff --git a/src/plugins/platforms/windows/qwindowsopengltester.cpp b/src/plugins/platforms/windows/qwindowsopengltester.cpp
index d7d186e804..9eb4011bf2 100644
--- a/src/plugins/platforms/windows/qwindowsopengltester.cpp
+++ b/src/plugins/platforms/windows/qwindowsopengltester.cpp
@@ -49,6 +49,7 @@
#include <QtCore/qstandardpaths.h>
#include <QtCore/qlibraryinfo.h>
#include <QtCore/qhash.h>
+#include <private/qsystemlibrary_p.h>
#ifndef QT_NO_OPENGL
#include <private/qopengl_p.h>
@@ -396,7 +397,7 @@ bool QWindowsOpenGLTester::testDesktopGL()
// Test #1: Load opengl32.dll and try to resolve an OpenGL 2 function.
// This will typically fail on systems that do not have a real OpenGL driver.
- lib = LoadLibraryA("opengl32.dll");
+ lib = QSystemLibrary::load(L"opengl32");
if (lib) {
CreateContext = reinterpret_cast<CreateContextType>(
reinterpret_cast<QFunctionPointer>(::GetProcAddress(lib, "wglCreateContext")));
diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp
index 3f7f925877..9820d1ec2c 100644
--- a/src/plugins/platforms/windows/qwindowswindow.cpp
+++ b/src/plugins/platforms/windows/qwindowswindow.cpp
@@ -2683,32 +2683,30 @@ void QWindowsWindow::getSizeHints(MINMAXINFO *mmi) const
// This block fixes QTBUG-8361, QTBUG-4362: Frameless/title-less windows shouldn't cover the
// taskbar when maximized
- if ((testFlag(WithinMaximize) || window()->windowStates().testFlag(Qt::WindowMinimized))
- && (m_data.flags.testFlag(Qt::FramelessWindowHint)
- || (m_data.flags.testFlag(Qt::CustomizeWindowHint) && !m_data.flags.testFlag(Qt::WindowTitleHint)))) {
- const QScreen *screen = window()->screen();
-
- // Documentation of MINMAXINFO states that it will only work for the primary screen
- if (screen && screen == QGuiApplication::primaryScreen()) {
- const QRect availableGeometry = QHighDpi::toNativePixels(screen->availableGeometry(), screen);
+ if (m_data.flags.testFlag(Qt::FramelessWindowHint)
+ || (m_data.flags.testFlag(Qt::CustomizeWindowHint) && !m_data.flags.testFlag(Qt::WindowTitleHint))) {
+ if (QPlatformScreen *currentScreen = screen()) {
+ const QRect geometry = currentScreen->geometry();
+ const QRect availableGeometry = currentScreen->availableGeometry();
mmi->ptMaxSize.y = availableGeometry.height();
// Width, because you can have the taskbar on the sides too.
mmi->ptMaxSize.x = availableGeometry.width();
// If you have the taskbar on top, or on the left you don't want it at (0,0):
- mmi->ptMaxPosition.x = availableGeometry.x();
- mmi->ptMaxPosition.y = availableGeometry.y();
+ QPoint availablePositionDiff = geometry.topLeft() - availableGeometry.topLeft();
+ mmi->ptMaxPosition.x = availablePositionDiff.x();
+ mmi->ptMaxPosition.y = availablePositionDiff.y();
if (!m_data.flags.testFlag(Qt::FramelessWindowHint)) {
- const int borderWidth = getBorderWidth(screen->handle());
+ const int borderWidth = getBorderWidth(currentScreen);
mmi->ptMaxSize.x += borderWidth * 2;
mmi->ptMaxSize.y += borderWidth * 2;
mmi->ptMaxTrackSize = mmi->ptMaxSize;
mmi->ptMaxPosition.x -= borderWidth;
mmi->ptMaxPosition.y -= borderWidth;
}
- } else if (!screen){
- qWarning("window()->screen() returned a null screen");
+ } else {
+ qWarning("screen() returned a null screen");
}
}
diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxwindow.cpp b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxwindow.cpp
index 5e406017ca..e8c9c27ba8 100644
--- a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxwindow.cpp
+++ b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxwindow.cpp
@@ -58,7 +58,7 @@ const xcb_visualtype_t *QXcbGlxWindow::createVisual()
{
QXcbScreen *scr = xcbScreen();
if (!scr)
- return nullptr;
+ return QXcbWindow::createVisual();
qCDebug(lcQpaGl) << "Requested format before FBConfig/Visual selection:" << m_format;
@@ -71,10 +71,13 @@ const xcb_visualtype_t *QXcbGlxWindow::createVisual()
flags |= QGLX_SUPPORTS_SRGB;
}
+ const auto formatBackup = m_format;
XVisualInfo *visualInfo = qglx_findVisualInfo(dpy, scr->screenNumber(), &m_format, GLX_WINDOW_BIT, flags);
if (!visualInfo) {
- qWarning() << "No XVisualInfo for format" << m_format;
- return nullptr;
+ qCDebug(lcQpaGl) << "No XVisualInfo for format" << m_format;
+ // restore initial format before requesting it again
+ m_format = formatBackup;
+ return QXcbWindow::createVisual();
}
const xcb_visualtype_t *xcb_visualtype = scr->visualForId(visualInfo->visualid);
XFree(visualInfo);
diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
index 27a2526df1..5a36cfa0d3 100644
--- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
@@ -645,7 +645,7 @@ void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindo
continue;
if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
qCDebug(lcQpaXInputEvents, " valuator %20s value %lf from range %lf -> %lf",
- atomName(vci.label).constData(), value, vci.min, vci.max);
+ atomName(atom(vci.label)).constData(), value, vci.min, vci.max);
if (value > vci.max)
value = vci.max;
if (value < vci.min)
diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp
index ffda7b6a57..dfc9c7d7a3 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.cpp
+++ b/src/plugins/platforms/xcb/qxcbwindow.cpp
@@ -1639,7 +1639,7 @@ void QXcbWindow::setWmWindowType(QXcbWindowFunctions::WmWindowTypes types, Qt::W
break;
}
- if ((flags & Qt::FramelessWindowHint) && !(type & QXcbWindowFunctions::KdeOverride)) {
+ if ((flags & Qt::FramelessWindowHint) && !(types & QXcbWindowFunctions::KdeOverride)) {
// override netwm type - quick and easy for KDE noborder
atoms.append(atom(QXcbAtom::_KDE_NET_WM_WINDOW_TYPE_OVERRIDE));
}