summaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/imageformats/ico/qicohandler.cpp2
-rw-r--r--src/plugins/networkinformation/CMakeLists.txt4
-rw-r--r--src/plugins/networkinformation/android/jar/.gitignore6
-rw-r--r--src/plugins/networkinformation/android/jar/build.gradle53
-rw-r--r--src/plugins/networkinformation/android/jar/settings.gradle1
-rw-r--r--src/plugins/networkinformation/android/jar/src/org/qtproject/qt/android/networkinformation/QtAndroidNetworkInformation.java76
-rw-r--r--src/plugins/networkinformation/android/qandroidnetworkinformationbackend.cpp85
-rw-r--r--src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.cpp58
-rw-r--r--src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.h20
-rw-r--r--src/plugins/networkinformation/glib/CMakeLists.txt14
-rw-r--r--src/plugins/networkinformation/glib/qglibnetworkinformationbackend.cpp157
-rw-r--r--src/plugins/networkinformation/networklistmanager/CMakeLists.txt9
-rw-r--r--src/plugins/networkinformation/networklistmanager/qnetworklistmanagerevents.cpp264
-rw-r--r--src/plugins/networkinformation/networklistmanager/qnetworklistmanagerevents.h118
-rw-r--r--src/plugins/networkinformation/networklistmanager/qnetworklistmanagernetworkinformationbackend.cpp211
-rw-r--r--src/plugins/networkinformation/networkmanager/qnetworkmanagernetworkinformationbackend.cpp118
-rw-r--r--src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.cpp112
-rw-r--r--src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.h62
-rw-r--r--src/plugins/networkinformation/scnetworkreachability/qscnetworkreachabilitynetworkinformationbackend.mm59
-rw-r--r--src/plugins/platforms/android/androidcontentfileengine.cpp4
-rw-r--r--src/plugins/platforms/android/androidcontentfileengine.h2
-rw-r--r--src/plugins/platforms/android/androidjniinput.cpp52
-rw-r--r--src/plugins/platforms/android/androidjniinput.h4
-rw-r--r--src/plugins/platforms/android/androidjnimain.cpp18
-rw-r--r--src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp16
-rw-r--r--src/plugins/platforms/android/qandroidinputcontext.cpp115
-rw-r--r--src/plugins/platforms/android/qandroidinputcontext.h4
-rw-r--r--src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp11
-rw-r--r--src/plugins/platforms/android/qandroidplatformfiledialoghelper.h2
-rw-r--r--src/plugins/platforms/android/qandroidplatformscreen.cpp8
-rw-r--r--src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm5
-rw-r--r--src/plugins/platforms/cocoa/qcocoabackingstore.h11
-rw-r--r--src/plugins/platforms/cocoa/qcocoabackingstore.mm166
-rw-r--r--src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm5
-rw-r--r--src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm2
-rw-r--r--src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm4
-rw-r--r--src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm5
-rw-r--r--src/plugins/platforms/cocoa/qcocoaglcontext.mm9
-rw-r--r--src/plugins/platforms/cocoa/qcocoahelpers.mm2
-rw-r--r--src/plugins/platforms/cocoa/qcocoainputcontext.h1
-rw-r--r--src/plugins/platforms/cocoa/qcocoainputcontext.mm27
-rw-r--r--src/plugins/platforms/cocoa/qcocoaintegration.mm2
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenu.mm2
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenubar.h3
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenubar.mm76
-rw-r--r--src/plugins/platforms/cocoa/qcocoascreen.mm2
-rw-r--r--src/plugins/platforms/cocoa/qcocoatheme.mm4
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindow.h14
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindow.mm91
-rw-r--r--src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.h1
-rw-r--r--src/plugins/platforms/cocoa/qmacclipboard.mm2
-rw-r--r--src/plugins/platforms/cocoa/qnsview.mm2
-rw-r--r--src/plugins/platforms/cocoa/qnsview_keys.mm14
-rw-r--r--src/plugins/platforms/cocoa/qnsview_mouse.mm46
-rw-r--r--src/plugins/platforms/cocoa/qnsview_tablet.mm188
-rw-r--r--src/plugins/platforms/cocoa/qnswindowdelegate.mm8
-rw-r--r--src/plugins/platforms/direct2d/CMakeLists.txt4
-rw-r--r--src/plugins/platforms/directfb/qdirectfbinput.cpp2
-rw-r--r--src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp34
-rw-r--r--src/plugins/platforms/eglfs/api/qeglfsdeviceintegration_p.h4
-rw-r--r--src/plugins/platforms/eglfs/api/qeglfswindow.cpp2
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.cpp7
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp9
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen_p.h1
-rw-r--r--src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.h3
-rw-r--r--src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.mm10
-rw-r--r--src/plugins/platforms/ios/qiosfiledialog.mm3
-rw-r--r--src/plugins/platforms/ios/qiosinputcontext.h4
-rw-r--r--src/plugins/platforms/ios/qiosinputcontext.mm22
-rw-r--r--src/plugins/platforms/ios/qiosintegration.mm3
-rw-r--r--src/plugins/platforms/ios/qiostextinputoverlay.mm32
-rw-r--r--src/plugins/platforms/ios/qiostextresponder.h13
-rw-r--r--src/plugins/platforms/ios/qiostextresponder.mm333
-rw-r--r--src/plugins/platforms/ios/qiosviewcontroller.mm10
-rw-r--r--src/plugins/platforms/ios/quiview.mm2
-rw-r--r--src/plugins/platforms/ios/quiview_accessibility.mm5
-rw-r--r--src/plugins/platforms/offscreen/qoffscreenintegration_x11.cpp7
-rw-r--r--src/plugins/platforms/offscreen/qoffscreenintegration_x11.h9
-rw-r--r--src/plugins/platforms/offscreen/qoffscreenwindow.cpp20
-rw-r--r--src/plugins/platforms/qnx/qqnxeglwindow.cpp2
-rw-r--r--src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp4
-rw-r--r--src/plugins/platforms/qnx/qqnxvirtualkeyboardpps.cpp2
-rw-r--r--src/plugins/platforms/wasm/qtloader.js4
-rw-r--r--src/plugins/platforms/wasm/qwasmcompositor.cpp10
-rw-r--r--src/plugins/platforms/wasm/qwasmcursor.cpp16
-rw-r--r--src/plugins/platforms/wasm/qwasmeventdispatcher.cpp2
-rw-r--r--src/plugins/platforms/wasm/qwasmeventtranslator.cpp25
-rw-r--r--src/plugins/platforms/wasm/qwasmeventtranslator.h6
-rw-r--r--src/plugins/platforms/wasm/qwasmintegration.cpp6
-rw-r--r--src/plugins/platforms/wasm/qwasmwindow.cpp30
-rw-r--r--src/plugins/platforms/wasm/qwasmwindow.h3
-rw-r--r--src/plugins/platforms/windows/CMakeLists.txt4
-rw-r--r--src/plugins/platforms/windows/openglblacklists/default.json2
-rw-r--r--src/plugins/platforms/windows/qtwindowsglobal.h7
-rw-r--r--src/plugins/platforms/windows/qwin10helpers.cpp43
-rw-r--r--src/plugins/platforms/windows/qwindowsclipboard.cpp11
-rw-r--r--src/plugins/platforms/windows/qwindowscontext.cpp155
-rw-r--r--src/plugins/platforms/windows/qwindowscontext.h82
-rw-r--r--src/plugins/platforms/windows/qwindowsdialoghelpers.cpp45
-rw-r--r--src/plugins/platforms/windows/qwindowsdrag.cpp2
-rw-r--r--src/plugins/platforms/windows/qwindowsglcontext.cpp2
-rw-r--r--src/plugins/platforms/windows/qwindowsinputcontext.cpp5
-rw-r--r--src/plugins/platforms/windows/qwindowsintegration.cpp14
-rw-r--r--src/plugins/platforms/windows/qwindowsmousehandler.cpp11
-rw-r--r--src/plugins/platforms/windows/qwindowsopengltester.cpp12
-rw-r--r--src/plugins/platforms/windows/qwindowspointerhandler.cpp82
-rw-r--r--src/plugins/platforms/windows/qwindowspointerhandler.h3
-rw-r--r--src/plugins/platforms/windows/qwindowsscreen.cpp92
-rw-r--r--src/plugins/platforms/windows/qwindowsscreen.h3
-rw-r--r--src/plugins/platforms/windows/qwindowsservices.cpp2
-rw-r--r--src/plugins/platforms/windows/qwindowssystemtrayicon.cpp14
-rw-r--r--src/plugins/platforms/windows/qwindowstheme.cpp10
-rw-r--r--src/plugins/platforms/windows/qwindowswindow.cpp118
-rw-r--r--src/plugins/platforms/windows/qwindowswindow.h3
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp4
-rw-r--r--src/plugins/platforms/xcb/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/xcb/gl_integrations/qxcbglintegrationfactory.cpp17
-rw-r--r--src/plugins/platforms/xcb/gl_integrations/qxcbglintegrationfactory.h2
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qtessellator.cpp2
-rw-r--r--src/plugins/platforms/xcb/qxcbbackingstore.cpp38
-rw-r--r--src/plugins/platforms/xcb/qxcbclipboard.cpp70
-rw-r--r--src/plugins/platforms/xcb/qxcbclipboard.h4
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.cpp16
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.h10
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_basic.cpp12
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_basic.h1
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_xi2.cpp265
-rw-r--r--src/plugins/platforms/xcb/qxcbcursor.cpp2
-rw-r--r--src/plugins/platforms/xcb/qxcbdrag.cpp53
-rw-r--r--src/plugins/platforms/xcb/qxcbkeyboard.cpp24
-rw-r--r--src/plugins/platforms/xcb/qxcbkeyboard.h1
-rw-r--r--src/plugins/platforms/xcb/qxcbscreen.cpp10
-rw-r--r--src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp12
-rw-r--r--src/plugins/platforms/xcb/qxcbsystemtraytracker.h2
-rw-r--r--src/plugins/platforms/xcb/qxcbwindow.cpp7
-rw-r--r--src/plugins/platforms/xcb/qxcbwindow.h4
-rw-r--r--src/plugins/platforms/xcb/qxcbxsettings.cpp7
-rw-r--r--src/plugins/platformthemes/gtk3/qgtk3dialoghelpers.cpp40
-rw-r--r--src/plugins/platformthemes/gtk3/qgtk3dialoghelpers.h3
-rw-r--r--src/plugins/platformthemes/gtk3/qgtk3theme.cpp42
-rw-r--r--src/plugins/platformthemes/gtk3/qgtk3theme.h2
-rw-r--r--src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportalfiledialog.cpp1
-rw-r--r--src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.cpp1
-rw-r--r--src/plugins/sqldrivers/CMakeLists.txt2
-rw-r--r--src/plugins/sqldrivers/ibase/qsql_ibase.cpp2
-rw-r--r--src/plugins/sqldrivers/mysql/qsql_mysql.cpp31
-rw-r--r--src/plugins/sqldrivers/oci/qsql_oci.cpp30
-rw-r--r--src/plugins/sqldrivers/odbc/CMakeLists.txt2
-rw-r--r--src/plugins/sqldrivers/psql/CMakeLists.txt2
-rw-r--r--src/plugins/sqldrivers/qt_cmdline.cmake1
-rw-r--r--src/plugins/styles/android/qandroidstyle.cpp3
-rw-r--r--src/plugins/styles/mac/qmacstyle_mac.mm58
-rw-r--r--src/plugins/styles/windowsvista/qwindowsxpstyle.cpp14
-rw-r--r--src/plugins/tls/openssl/qsslcontext_openssl.cpp4
-rw-r--r--src/plugins/tls/openssl/qssldiffiehellmanparameters_openssl.cpp18
-rw-r--r--src/plugins/tls/openssl/qsslsocket_openssl_symbols.cpp65
-rw-r--r--src/plugins/tls/openssl/qsslsocket_openssl_symbols_p.h24
-rw-r--r--src/plugins/tls/openssl/qtls_openssl.cpp52
-rw-r--r--src/plugins/tls/openssl/qtls_openssl_p.h8
-rw-r--r--src/plugins/tls/openssl/qwindowscarootfetcher.cpp2
-rw-r--r--src/plugins/tls/shared/qx509_generic.cpp2
161 files changed, 3067 insertions, 1635 deletions
diff --git a/src/plugins/imageformats/ico/qicohandler.cpp b/src/plugins/imageformats/ico/qicohandler.cpp
index 1b084e2eea..a3fe9d740c 100644
--- a/src/plugins/imageformats/ico/qicohandler.cpp
+++ b/src/plugins/imageformats/ico/qicohandler.cpp
@@ -208,9 +208,7 @@ bool ICOReader::canRead(QIODevice *iodev)
ICONDIR ikonDir;
if (readIconDir(iodev, &ikonDir)) {
- qint64 readBytes = ICONDIR_SIZE;
if (readIconDirEntry(iodev, &ikonDir.idEntries[0])) {
- readBytes += ICONDIRENTRY_SIZE;
// ICO format does not have a magic identifier, so we read 6 different values, which will hopefully be enough to identify the file.
if ( ikonDir.idReserved == 0
&& (ikonDir.idType == 1 || ikonDir.idType == 2)
diff --git a/src/plugins/networkinformation/CMakeLists.txt b/src/plugins/networkinformation/CMakeLists.txt
index 142031c62a..ddb907d1e7 100644
--- a/src/plugins/networkinformation/CMakeLists.txt
+++ b/src/plugins/networkinformation/CMakeLists.txt
@@ -13,3 +13,7 @@ endif()
if(ANDROID)
add_subdirectory(android)
endif()
+
+if(QT_FEATURE_glib)
+ add_subdirectory(glib)
+endif()
diff --git a/src/plugins/networkinformation/android/jar/.gitignore b/src/plugins/networkinformation/android/jar/.gitignore
new file mode 100644
index 0000000000..364420a59a
--- /dev/null
+++ b/src/plugins/networkinformation/android/jar/.gitignore
@@ -0,0 +1,6 @@
+.gradle/
+build/
+gradle/
+gradlew
+gradlew.bat
+local.properties
diff --git a/src/plugins/networkinformation/android/jar/build.gradle b/src/plugins/networkinformation/android/jar/build.gradle
new file mode 100644
index 0000000000..c947852f79
--- /dev/null
+++ b/src/plugins/networkinformation/android/jar/build.gradle
@@ -0,0 +1,53 @@
+// This is mainly used to allow Android Studio to easily read this folder as an android project.
+
+buildscript {
+ repositories {
+ google()
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:7.0.2'
+ }
+}
+
+apply plugin: 'com.android.library'
+
+dependencies {
+ implementation fileTree(dir: "libs", include: ["*.jar"])
+}
+
+repositories {
+ google()
+ mavenCentral()
+}
+
+android {
+ compileSdkVersion 31
+ buildToolsVersion "31.0.3"
+
+ defaultConfig {
+ minSdkVersion 23
+ targetSdkVersion 31
+ }
+
+ sourceSets {
+ main {
+ java.srcDir 'src/'
+ resources.srcDir 'libs/'
+ manifest.srcFile 'AndroidManifest.xml'
+ res.srcDirs = ['res/']
+ }
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ android {
+ lintOptions {
+ abortOnError true
+ }
+ }
+}
diff --git a/src/plugins/networkinformation/android/jar/settings.gradle b/src/plugins/networkinformation/android/jar/settings.gradle
new file mode 100644
index 0000000000..cbb1ff361b
--- /dev/null
+++ b/src/plugins/networkinformation/android/jar/settings.gradle
@@ -0,0 +1 @@
+rootProject.name = "QtAndroidNetworkInformationBackend"
diff --git a/src/plugins/networkinformation/android/jar/src/org/qtproject/qt/android/networkinformation/QtAndroidNetworkInformation.java b/src/plugins/networkinformation/android/jar/src/org/qtproject/qt/android/networkinformation/QtAndroidNetworkInformation.java
index 0e89d23ab4..a35424f8e8 100644
--- a/src/plugins/networkinformation/android/jar/src/org/qtproject/qt/android/networkinformation/QtAndroidNetworkInformation.java
+++ b/src/plugins/networkinformation/android/jar/src/org/qtproject/qt/android/networkinformation/QtAndroidNetworkInformation.java
@@ -39,31 +39,45 @@
package org.qtproject.qt.android.networkinformation;
-import android.content.BroadcastReceiver;
+import android.annotation.SuppressLint;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.NetworkRequest;
import android.net.NetworkCapabilities;
import android.net.Network;
+import android.os.Build;
public class QtAndroidNetworkInformation {
private static final String LOG_TAG = "QtAndroidNetworkInformation";
- private static native void connectivityChanged();
- private static native void behindCaptivePortalChanged(boolean state);
+ private static native void connectivityChanged(AndroidConnectivity connectivity);
+ private static native void genericInfoChanged(boolean captivePortal, boolean metered);
+ private static native void transportMediumChanged(Transport transportMedium);
private static QtNetworkInformationCallback m_callback = null;
private static final Object m_lock = new Object();
+ // Keep synchronized with AndroidConnectivity in androidconnectivitymanager.h
enum AndroidConnectivity {
Connected, Unknown, Disconnected
}
+ // Keep synchronized with AndroidTransport in androidconnectivitymanager.h
+ enum Transport {
+ Unknown,
+ Bluetooth,
+ Cellular,
+ Ethernet,
+ LoWPAN,
+ Usb,
+ WiFi,
+ WiFiAware,
+ }
+
private static class QtNetworkInformationCallback extends NetworkCallback {
public AndroidConnectivity previousState = null;
+ public Transport previousTransport = null;
QtNetworkInformationCallback() {
}
@@ -77,17 +91,55 @@ public class QtAndroidNetworkInformation {
s = AndroidConnectivity.Connected;
else
s = AndroidConnectivity.Unknown; // = we _may_ have Internet access
+
+ final Transport transport = getTransport(capabilities);
+ if (transport == Transport.Unknown) // If we don't have any transport media: override
+ s = AndroidConnectivity.Unknown;
+
setState(s);
+ setTransportMedium(transport);
final boolean captive
= capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL);
- behindCaptivePortalChanged(captive);
+ final boolean metered
+ = !capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+ genericInfoChanged(captive, metered);
+ }
+
+ private Transport getTransport(NetworkCapabilities capabilities)
+ {
+ if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
+ return Transport.WiFi;
+ } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
+ return Transport.Cellular;
+ } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH)) {
+ return Transport.Bluetooth;
+ } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) {
+ return Transport.Ethernet;
+ } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI_AWARE)) {
+ // Build.VERSION_CODES.O
+ return Transport.WiFiAware;
+ } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_LOWPAN)) {
+ // Build.VERSION_CODES.O_MR1
+ return Transport.LoWPAN;
+ }/* else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_USB)) {
+ // Build.VERSION_CODES.S
+ return Transport.Usb;
+ }*/ // @todo: Uncomment once we can use SDK 31
+ return Transport.Unknown;
}
private void setState(AndroidConnectivity s) {
if (previousState != s) {
previousState = s;
- connectivityChanged();
+ connectivityChanged(s);
+ }
+ }
+
+ private void setTransportMedium(Transport t) {
+ if (previousTransport != t) {
+ previousTransport = t;
+ transportMediumChanged(t);
}
}
@@ -106,16 +158,24 @@ public class QtAndroidNetworkInformation {
return AndroidConnectivity.Unknown;
}
+ @SuppressLint("MissingPermission")
public static void registerReceiver(final Context context) {
synchronized (m_lock) {
if (m_callback == null) {
ConnectivityManager manager = getConnectivityManager(context);
m_callback = new QtNetworkInformationCallback();
NetworkRequest.Builder builder = new NetworkRequest.Builder();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
+ builder = builder.clearCapabilities();
builder = builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
- if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ builder = builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
builder = builder.addCapability(NetworkCapabilities.NET_CAPABILITY_FOREGROUND);
+ }
NetworkRequest request = builder.build();
+
+ // Can't use registerDefaultNetworkCallback because it doesn't let us know when
+ // the network disconnects!
manager.registerNetworkCallback(request, m_callback);
}
}
diff --git a/src/plugins/networkinformation/android/qandroidnetworkinformationbackend.cpp b/src/plugins/networkinformation/android/qandroidnetworkinformationbackend.cpp
index 9eef471989..2e4149f686 100644
--- a/src/plugins/networkinformation/android/qandroidnetworkinformationbackend.cpp
+++ b/src/plugins/networkinformation/android/qandroidnetworkinformationbackend.cpp
@@ -47,7 +47,10 @@ QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcNetInfoAndroid)
Q_LOGGING_CATEGORY(lcNetInfoAndroid, "qt.network.info.android");
-static const QString backendName = QStringLiteral("android");
+static QString backendName() {
+ return QString::fromUtf16(QNetworkInformationBackend::PluginNames
+ [QNetworkInformationBackend::PluginNamesAndroidIndex]);
+}
class QAndroidNetworkInformationBackend : public QNetworkInformationBackend
{
@@ -56,7 +59,7 @@ public:
QAndroidNetworkInformationBackend();
~QAndroidNetworkInformationBackend() { m_valid = false; }
- QString name() const override { return backendName; }
+ QString name() const override { return backendName(); }
QNetworkInformation::Features featuresSupported() const override
{
return featuresSupportedStatic();
@@ -65,13 +68,18 @@ public:
static QNetworkInformation::Features featuresSupportedStatic()
{
using Feature = QNetworkInformation::Feature;
- return QNetworkInformation::Features(Feature::Reachability | Feature::CaptivePortal);
+ return QNetworkInformation::Features(Feature::Reachability | Feature::CaptivePortal
+ | Feature::TransportMedium);
}
bool isValid() { return m_valid; }
private:
Q_DISABLE_COPY_MOVE(QAndroidNetworkInformationBackend);
+
+ void updateConnectivity(AndroidConnectivityManager::AndroidConnectivity connectivity);
+ void updateTransportMedium(AndroidConnectivityManager::AndroidTransport transport);
+
bool m_valid = false;
};
@@ -83,7 +91,7 @@ class QAndroidNetworkInformationBackendFactory : public QNetworkInformationBacke
public:
QAndroidNetworkInformationBackendFactory() = default;
~QAndroidNetworkInformationBackendFactory() = default;
- QString name() const override { return backendName; }
+ QString name() const override { return backendName(); }
QNetworkInformation::Features featuresSupported() const override
{
return QAndroidNetworkInformationBackend::featuresSupportedStatic();
@@ -111,24 +119,63 @@ QAndroidNetworkInformationBackend::QAndroidNetworkInformationBackend()
return;
m_valid = true;
setReachability(QNetworkInformation::Reachability::Unknown);
- connect(conman, &AndroidConnectivityManager::connectivityChanged, this, [this, conman]() {
- static const auto mapState = [](AndroidConnectivityManager::AndroidConnectivity state) {
- switch (state) {
- case AndroidConnectivityManager::AndroidConnectivity::Connected:
- return QNetworkInformation::Reachability::Online;
- case AndroidConnectivityManager::AndroidConnectivity::Disconnected:
- return QNetworkInformation::Reachability::Disconnected;
- case AndroidConnectivityManager::AndroidConnectivity::Unknown:
- default:
- return QNetworkInformation::Reachability::Unknown;
- }
- };
-
- setReachability(mapState(conman->networkConnectivity()));
- });
+ connect(conman, &AndroidConnectivityManager::connectivityChanged, this,
+ &QAndroidNetworkInformationBackend::updateConnectivity);
connect(conman, &AndroidConnectivityManager::captivePortalChanged, this,
&QAndroidNetworkInformationBackend::setBehindCaptivePortal);
+
+ connect(conman, &AndroidConnectivityManager::transportMediumChanged, this,
+ &QAndroidNetworkInformationBackend::updateTransportMedium);
+
+ connect(conman, &AndroidConnectivityManager::meteredChanged, this,
+ &QAndroidNetworkInformationBackend::setMetered);
+}
+
+void QAndroidNetworkInformationBackend::updateConnectivity(
+ AndroidConnectivityManager::AndroidConnectivity connectivity)
+{
+ using AndroidConnectivity = AndroidConnectivityManager::AndroidConnectivity;
+ static const auto mapState = [](AndroidConnectivity state) {
+ switch (state) {
+ case AndroidConnectivity::Connected:
+ return QNetworkInformation::Reachability::Online;
+ case AndroidConnectivity::Disconnected:
+ return QNetworkInformation::Reachability::Disconnected;
+ case AndroidConnectivity::Unknown:
+ default:
+ return QNetworkInformation::Reachability::Unknown;
+ }
+ };
+
+ setReachability(mapState(connectivity));
+}
+
+void QAndroidNetworkInformationBackend::updateTransportMedium(
+ AndroidConnectivityManager::AndroidTransport transport)
+{
+ using AndroidTransport = AndroidConnectivityManager::AndroidTransport;
+ using TransportMedium = QNetworkInformation::TransportMedium;
+ static const auto mapTransport = [](AndroidTransport state) -> TransportMedium {
+ switch (state) {
+ case AndroidTransport::Cellular:
+ return TransportMedium::Cellular;
+ case AndroidTransport::WiFi:
+ return TransportMedium::WiFi;
+ case AndroidTransport::Bluetooth:
+ return TransportMedium::Bluetooth;
+ case AndroidTransport::Ethernet:
+ return TransportMedium::Ethernet;
+ // These are not covered yet (but may be in the future)
+ case AndroidTransport::Usb:
+ case AndroidTransport::LoWPAN:
+ case AndroidTransport::WiFiAware:
+ case AndroidTransport::Unknown:
+ return TransportMedium::Unknown;
+ }
+ };
+
+ setTransportMedium(mapTransport(transport));
}
QT_END_NAMESPACE
diff --git a/src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.cpp b/src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.cpp
index e88fe7d955..5c1230bfaa 100644
--- a/src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.cpp
+++ b/src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.cpp
@@ -56,18 +56,30 @@ Q_GLOBAL_STATIC(AndroidConnectivityManagerInstance, androidConnManagerInstance)
static const char networkInformationClass[] =
"org/qtproject/qt/android/networkinformation/QtAndroidNetworkInformation";
-static void networkConnectivityChanged(JNIEnv *env, jobject obj)
+static void networkConnectivityChanged(JNIEnv *env, jobject obj, jobject enumValue)
{
Q_UNUSED(env);
Q_UNUSED(obj);
- Q_EMIT androidConnManagerInstance->connManager->connectivityChanged();
+ const jint value = QJniObject(enumValue).callMethod<jint>("ordinal");
+ const auto connectivity = static_cast<AndroidConnectivityManager::AndroidConnectivity>(value);
+ Q_EMIT androidConnManagerInstance->connManager->connectivityChanged(connectivity);
}
-static void behindCaptivePortalChanged(JNIEnv *env, jobject obj, jboolean state)
+static void genericInfoChanged(JNIEnv *env, jobject obj, jboolean captivePortal, jboolean metered)
{
Q_UNUSED(env);
Q_UNUSED(obj);
- Q_EMIT androidConnManagerInstance->connManager->captivePortalChanged(state);
+ Q_EMIT androidConnManagerInstance->connManager->captivePortalChanged(captivePortal);
+ Q_EMIT androidConnManagerInstance->connManager->meteredChanged(metered);
+}
+
+static void transportMediumChangedCallback(JNIEnv *env, jobject obj, jobject enumValue)
+{
+ Q_UNUSED(env);
+ Q_UNUSED(obj);
+ const jint value = QJniObject(enumValue).callMethod<jint>("ordinal");
+ const auto transport = static_cast<AndroidConnectivityManager::AndroidTransport>(value);
+ emit androidConnManagerInstance->connManager->transportMediumChanged(transport);
}
AndroidConnectivityManager::AndroidConnectivityManager()
@@ -101,30 +113,6 @@ AndroidConnectivityManager::~AndroidConnectivityManager()
"(Landroid/content/Context;)V", QAndroidApplication::context());
}
-AndroidConnectivityManager::AndroidConnectivity AndroidConnectivityManager::networkConnectivity()
-{
- QJniEnvironment env;
- QJniObject networkReceiver(networkInformationClass);
- jclass clazz = env->GetObjectClass(networkReceiver.object());
- static const QByteArray functionSignature =
- QByteArray(QByteArray("()L") + networkInformationClass + "$AndroidConnectivity;");
- QJniObject enumObject =
- QJniObject::callStaticObjectMethod(clazz, "state", functionSignature.data());
- if (!enumObject.isValid())
- return AndroidConnectivityManager::AndroidConnectivity::Unknown;
-
- QJniObject enumName = enumObject.callObjectMethod<jstring>("name");
- if (!enumName.isValid())
- return AndroidConnectivityManager::AndroidConnectivity::Unknown;
-
- QString name = enumName.toString();
- if (name == u"Connected")
- return AndroidConnectivity::Connected;
- if (name == u"Disconnected")
- return AndroidConnectivity::Disconnected;
- return AndroidConnectivity::Unknown;
-}
-
bool AndroidConnectivityManager::registerNatives()
{
QJniEnvironment env;
@@ -132,11 +120,19 @@ bool AndroidConnectivityManager::registerNatives()
if (!networkReceiver.isValid())
return false;
+ const QByteArray connectivityEnumSig =
+ QByteArray("(L") + networkInformationClass + "$AndroidConnectivity;)V";
+ const QByteArray transportEnumSig =
+ QByteArray("(L") + networkInformationClass + "$Transport;)V";
+
jclass clazz = env->GetObjectClass(networkReceiver.object());
static JNINativeMethod methods[] = {
- { "connectivityChanged", "()V", reinterpret_cast<void *>(networkConnectivityChanged) },
- { "behindCaptivePortalChanged", "(Z)V",
- reinterpret_cast<void *>(behindCaptivePortalChanged) }
+ { "connectivityChanged", connectivityEnumSig.data(),
+ reinterpret_cast<void *>(networkConnectivityChanged) },
+ { "genericInfoChanged", "(ZZ)V",
+ reinterpret_cast<void *>(genericInfoChanged) },
+ { "transportMediumChanged", transportEnumSig.data(),
+ reinterpret_cast<void *>(transportMediumChangedCallback) },
};
const bool ret = (env->RegisterNatives(clazz, methods, std::size(methods)) == JNI_OK);
env->DeleteLocalRef(clazz);
diff --git a/src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.h b/src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.h
index 14f5aa9b57..414ef2748a 100644
--- a/src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.h
+++ b/src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.h
@@ -49,17 +49,33 @@ class AndroidConnectivityManager : public QObject
{
Q_OBJECT
public:
+ // Keep synchronized with AndroidConnectivity in QtAndroidNetworkInformation.java
enum class AndroidConnectivity { Connected, Unknown, Disconnected };
Q_ENUM(AndroidConnectivity);
+
+ // Keep synchronized with Transport in QtAndroidNetworkInformation.java
+ enum class AndroidTransport {
+ Unknown,
+ Bluetooth,
+ Cellular,
+ Ethernet,
+ LoWPAN,
+ Usb,
+ WiFi,
+ WiFiAware,
+ };
+ Q_ENUM(AndroidTransport);
+
static AndroidConnectivityManager *getInstance();
~AndroidConnectivityManager();
- AndroidConnectivity networkConnectivity();
inline bool isValid() const { return m_connectivityManager.isValid(); }
Q_SIGNALS:
- void connectivityChanged();
+ void connectivityChanged(AndroidConnectivity connectivity);
void captivePortalChanged(bool state);
+ void transportMediumChanged(AndroidTransport transport);
+ void meteredChanged(bool state);
private:
friend struct AndroidConnectivityManagerInstance;
diff --git a/src/plugins/networkinformation/glib/CMakeLists.txt b/src/plugins/networkinformation/glib/CMakeLists.txt
new file mode 100644
index 0000000000..17a16a15c0
--- /dev/null
+++ b/src/plugins/networkinformation/glib/CMakeLists.txt
@@ -0,0 +1,14 @@
+qt_internal_add_plugin(QGlibNetworkInformationPlugin
+ OUTPUT_NAME qglib
+ CLASS_NAME QGlibNetworkInformationBackendFactory
+ PLUGIN_TYPE networkinformation
+ DEFAULT_IF LINUX
+ SOURCES
+ qglibnetworkinformationbackend.cpp
+ LIBRARIES
+ Qt::NetworkPrivate
+ GLIB2::GOBJECT
+ GLIB2::GIO
+ DEFINES
+ QT_NO_SIGNALS_SLOTS_KEYWORDS
+)
diff --git a/src/plugins/networkinformation/glib/qglibnetworkinformationbackend.cpp b/src/plugins/networkinformation/glib/qglibnetworkinformationbackend.cpp
new file mode 100644
index 0000000000..f26079b041
--- /dev/null
+++ b/src/plugins/networkinformation/glib/qglibnetworkinformationbackend.cpp
@@ -0,0 +1,157 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 Ilya Fedin <fedin-ilja2010@ya.ru>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module 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 <QtNetwork/private/qnetworkinformation_p.h>
+
+#include <QtCore/qglobal.h>
+#include <QtCore/private/qobject_p.h>
+
+#include <gio/gio.h>
+
+QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcNetInfoGlib)
+Q_LOGGING_CATEGORY(lcNetInfoGlib, "qt.network.info.glib");
+
+namespace {
+QNetworkInformation::Reachability reachabilityFromGNetworkConnectivity(GNetworkConnectivity connectivity)
+{
+ switch (connectivity) {
+ case G_NETWORK_CONNECTIVITY_LOCAL:
+ return QNetworkInformation::Reachability::Disconnected;
+ case G_NETWORK_CONNECTIVITY_LIMITED:
+ case G_NETWORK_CONNECTIVITY_PORTAL:
+ return QNetworkInformation::Reachability::Site;
+ case G_NETWORK_CONNECTIVITY_FULL:
+ return QNetworkInformation::Reachability::Online;
+ }
+ return QNetworkInformation::Reachability::Unknown;
+}
+}
+
+static QString backendName = QStringLiteral("glib");
+
+class QGlibNetworkInformationBackend : public QNetworkInformationBackend
+{
+ Q_OBJECT
+public:
+ QGlibNetworkInformationBackend();
+ ~QGlibNetworkInformationBackend();
+
+ QString name() const override { return backendName; }
+ QNetworkInformation::Features featuresSupported() const override
+ {
+ if (!isValid())
+ return {};
+ return featuresSupportedStatic();
+ }
+
+ static QNetworkInformation::Features featuresSupportedStatic()
+ {
+ using Feature = QNetworkInformation::Feature;
+ return QNetworkInformation::Features(Feature::Reachability | Feature::CaptivePortal);
+ }
+
+ bool isValid() const;
+
+private:
+ Q_DISABLE_COPY_MOVE(QGlibNetworkInformationBackend)
+
+ static void updateInformation(QGlibNetworkInformationBackend *backend);
+
+ GNetworkMonitor *networkMonitor = nullptr;
+ gulong handlerId = 0;
+};
+
+class QGlibNetworkInformationBackendFactory : public QNetworkInformationBackendFactory
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QNetworkInformationBackendFactory_iid)
+ Q_INTERFACES(QNetworkInformationBackendFactory)
+public:
+ QGlibNetworkInformationBackendFactory() = default;
+ ~QGlibNetworkInformationBackendFactory() = default;
+ QString name() const override { return backendName; }
+ QNetworkInformation::Features featuresSupported() const override
+ {
+ return QGlibNetworkInformationBackend::featuresSupportedStatic();
+ }
+
+ QNetworkInformationBackend *create(QNetworkInformation::Features requiredFeatures) const override
+ {
+ if ((requiredFeatures & featuresSupported()) != requiredFeatures)
+ return nullptr;
+ auto backend = new QGlibNetworkInformationBackend();
+ if (!backend->isValid())
+ delete std::exchange(backend, nullptr);
+ return backend;
+ }
+private:
+ Q_DISABLE_COPY_MOVE(QGlibNetworkInformationBackendFactory)
+};
+
+QGlibNetworkInformationBackend::QGlibNetworkInformationBackend()
+: networkMonitor(g_network_monitor_get_default())
+{
+ updateInformation(this);
+
+ handlerId = g_signal_connect_swapped(networkMonitor, "notify::connectivity",
+ G_CALLBACK(updateInformation), this);
+}
+
+QGlibNetworkInformationBackend::~QGlibNetworkInformationBackend()
+{
+ g_signal_handler_disconnect(networkMonitor, handlerId);
+}
+
+bool QGlibNetworkInformationBackend::isValid() const
+{
+ return G_OBJECT_TYPE_NAME(networkMonitor) != QLatin1String("GNetworkMonitorBase");
+}
+
+void QGlibNetworkInformationBackend::updateInformation(QGlibNetworkInformationBackend *backend)
+{
+ const auto connectivityState = g_network_monitor_get_connectivity(backend->networkMonitor);
+ const bool behindPortal = (connectivityState == G_NETWORK_CONNECTIVITY_PORTAL);
+ backend->setReachability(reachabilityFromGNetworkConnectivity(connectivityState));
+ backend->setBehindCaptivePortal(behindPortal);
+}
+
+QT_END_NAMESPACE
+
+#include "qglibnetworkinformationbackend.moc"
diff --git a/src/plugins/networkinformation/networklistmanager/CMakeLists.txt b/src/plugins/networkinformation/networklistmanager/CMakeLists.txt
index 170bce0f7b..d927d4af60 100644
--- a/src/plugins/networkinformation/networklistmanager/CMakeLists.txt
+++ b/src/plugins/networkinformation/networklistmanager/CMakeLists.txt
@@ -3,11 +3,18 @@ qt_internal_add_plugin(QNLMNIPlugin
CLASS_NAME QNetworkListManagerNetworkInformationBackendFactory
PLUGIN_TYPE networkinformation
DEFAULT_IF WIN32 AND QT_FEATURE_networklistmanager
- SOURCES qnetworklistmanagernetworkinformationbackend.cpp
+ SOURCES
+ qnetworklistmanagernetworkinformationbackend.cpp
+ qnetworklistmanagerevents.h qnetworklistmanagerevents.cpp
LIBRARIES
Qt::NetworkPrivate
)
+qt_internal_extend_target(QNLMNIPlugin CONDITION WIN32 AND MSVC AND NOT CLANG
+ LIBRARIES
+ runtimeobject
+)
+
# Don't repeat the target name in AUTOGEN_BUILD_DIR to work around issues with overlong paths.
set_property(TARGET QNLMNIPlugin PROPERTY
AUTOGEN_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/autogen")
diff --git a/src/plugins/networkinformation/networklistmanager/qnetworklistmanagerevents.cpp b/src/plugins/networkinformation/networklistmanager/qnetworklistmanagerevents.cpp
new file mode 100644
index 0000000000..65824cc262
--- /dev/null
+++ b/src/plugins/networkinformation/networklistmanager/qnetworklistmanagerevents.cpp
@@ -0,0 +1,264 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module 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 "qnetworklistmanagerevents.h"
+
+#ifdef SUPPORTS_WINRT
+#include <winrt/base.h>
+// Workaround for Windows SDK bug.
+// See https://github.com/microsoft/Windows.UI.Composition-Win32-Samples/issues/47
+namespace winrt::impl
+{
+ template <typename Async>
+ auto wait_for(Async const& async, Windows::Foundation::TimeSpan const& timeout);
+}
+
+#include <winrt/Windows.Networking.Connectivity.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+namespace {
+template<typename T>
+bool QueryInterfaceImpl(IUnknown *from, REFIID riid, void **ppvObject)
+{
+ if (riid == __uuidof(T)) {
+ *ppvObject = static_cast<T *>(from);
+ from->AddRef();
+ return true;
+ }
+ return false;
+}
+}
+
+QNetworkListManagerEvents::QNetworkListManagerEvents() : QObject(nullptr)
+{
+ auto hr = CoCreateInstance(CLSID_NetworkListManager, nullptr, CLSCTX_INPROC_SERVER,
+ IID_INetworkListManager, &networkListManager);
+ if (FAILED(hr)) {
+ qCWarning(lcNetInfoNLM) << "Could not get a NetworkListManager instance:"
+ << errorStringFromHResult(hr);
+ return;
+ }
+
+ ComPtr<IConnectionPointContainer> connectionPointContainer;
+ hr = networkListManager.As(&connectionPointContainer);
+ if (SUCCEEDED(hr)) {
+ hr = connectionPointContainer->FindConnectionPoint(IID_INetworkListManagerEvents,
+ &connectionPoint);
+ }
+ if (FAILED(hr)) {
+ qCWarning(lcNetInfoNLM) << "Failed to get connection point for network list manager events:"
+ << errorStringFromHResult(hr);
+ }
+}
+
+QNetworkListManagerEvents::~QNetworkListManagerEvents()
+{
+ Q_ASSERT(ref == 0);
+}
+
+HRESULT STDMETHODCALLTYPE QNetworkListManagerEvents::QueryInterface(REFIID riid, void **ppvObject)
+{
+ if (!ppvObject)
+ return E_INVALIDARG;
+
+ return QueryInterfaceImpl<IUnknown>(this, riid, ppvObject)
+ || QueryInterfaceImpl<INetworkListManagerEvents>(this, riid, ppvObject)
+ ? S_OK
+ : E_NOINTERFACE;
+}
+
+HRESULT STDMETHODCALLTYPE
+QNetworkListManagerEvents::ConnectivityChanged(NLM_CONNECTIVITY newConnectivity)
+{
+ // This function is run on a different thread than 'monitor' is created on, so we need to run
+ // it on that thread
+ emit connectivityChanged(newConnectivity);
+ return S_OK;
+}
+
+bool QNetworkListManagerEvents::start()
+{
+ if (!connectionPoint) {
+ qCWarning(lcNetInfoNLM, "Initialization failed, can't start!");
+ return false;
+ }
+ auto hr = connectionPoint->Advise(this, &cookie);
+ if (FAILED(hr)) {
+ qCWarning(lcNetInfoNLM) << "Failed to subscribe to network connectivity events:"
+ << errorStringFromHResult(hr);
+ return false;
+ }
+
+ // Update connectivity since it might have changed since this class was constructed
+ NLM_CONNECTIVITY connectivity;
+ hr = networkListManager->GetConnectivity(&connectivity);
+ if (FAILED(hr))
+ qCWarning(lcNetInfoNLM) << "Could not get connectivity:" << errorStringFromHResult(hr);
+ else
+ emit connectivityChanged(connectivity);
+
+#ifdef SUPPORTS_WINRT
+ using namespace winrt::Windows::Networking::Connectivity;
+ // Register for changes in the network and store a token to unregister later:
+ token = NetworkInformation::NetworkStatusChanged(
+ [this](const winrt::Windows::Foundation::IInspectable sender) {
+ Q_UNUSED(sender);
+ emitWinRTUpdates();
+ });
+ // Emit initial state
+ emitWinRTUpdates();
+#endif
+
+ return true;
+}
+
+bool QNetworkListManagerEvents::stop()
+{
+ Q_ASSERT(connectionPoint);
+ auto hr = connectionPoint->Unadvise(cookie);
+ if (FAILED(hr)) {
+ qCWarning(lcNetInfoNLM) << "Failed to unsubscribe from network connectivity events:"
+ << errorStringFromHResult(hr);
+ return false;
+ }
+ cookie = 0;
+
+#ifdef SUPPORTS_WINRT
+ using namespace winrt::Windows::Networking::Connectivity;
+ // Pass the token we stored earlier to unregister:
+ NetworkInformation::NetworkStatusChanged(token);
+ token = {};
+#endif
+ return true;
+}
+
+bool QNetworkListManagerEvents::checkBehindCaptivePortal()
+{
+ if (!networkListManager)
+ return false;
+ ComPtr<IEnumNetworks> networks;
+ HRESULT hr =
+ networkListManager->GetNetworks(NLM_ENUM_NETWORK_CONNECTED, networks.GetAddressOf());
+ if (FAILED(hr) || networks == nullptr)
+ return false;
+
+ // @note: This checks all connected networks, but that might not be necessary
+ ComPtr<INetwork> network;
+ hr = networks->Next(1, network.GetAddressOf(), nullptr);
+ while (SUCCEEDED(hr) && network != nullptr) {
+ ComPtr<IPropertyBag> propertyBag;
+ hr = network.As(&propertyBag);
+ if (SUCCEEDED(hr) && propertyBag != nullptr) {
+ VARIANT variant;
+ VariantInit(&variant);
+ const auto scopedVariantClear = qScopeGuard([&variant]() { VariantClear(&variant); });
+
+ const wchar_t *versions[] = { NA_InternetConnectivityV6, NA_InternetConnectivityV4 };
+ for (const auto version : versions) {
+ hr = propertyBag->Read(version, &variant, nullptr);
+ if (SUCCEEDED(hr)
+ && (V_UINT(&variant) & NLM_INTERNET_CONNECTIVITY_WEBHIJACK)
+ == NLM_INTERNET_CONNECTIVITY_WEBHIJACK) {
+ return true;
+ }
+ }
+ }
+
+ hr = networks->Next(1, network.GetAddressOf(), nullptr);
+ }
+
+ return false;
+}
+
+#ifdef SUPPORTS_WINRT
+namespace {
+using namespace winrt::Windows::Networking::Connectivity;
+// NB: this isn't part of "network list manager", but sadly NLM doesn't have an
+// equivalent API (at least not that I've found...)!
+[[nodiscard]]
+QNetworkInformation::TransportMedium getTransportMedium(const ConnectionProfile &profile)
+{
+ if (profile.IsWwanConnectionProfile())
+ return QNetworkInformation::TransportMedium::Cellular;
+ if (profile.IsWlanConnectionProfile())
+ return QNetworkInformation::TransportMedium::WiFi;
+
+ NetworkAdapter adapter = profile.NetworkAdapter();
+ if (adapter == nullptr)
+ return QNetworkInformation::TransportMedium::Unknown;
+
+ // Note: Bluetooth is given an iana iftype of 6, which is the same as Ethernet.
+ // In Windows itself there is clearly a distinction between a Bluetooth PAN
+ // and an Ethernet LAN, though it is not clear how they make this distinction.
+ auto fromIanaId = [](quint32 ianaId) -> QNetworkInformation::TransportMedium {
+ // https://www.iana.org/assignments/ianaiftype-mib/ianaiftype-mib
+ switch (ianaId) {
+ case 6:
+ return QNetworkInformation::TransportMedium::Ethernet;
+ case 71: // Should be handled before entering this lambda
+ return QNetworkInformation::TransportMedium::WiFi;
+ }
+ return QNetworkInformation::TransportMedium::Unknown;
+ };
+
+ return fromIanaId(adapter.IanaInterfaceType());
+}
+
+[[nodiscard]] bool getMetered(const ConnectionProfile &profile)
+{
+ ConnectionCost cost = profile.GetConnectionCost();
+ NetworkCostType type = cost.NetworkCostType();
+ return type == NetworkCostType::Fixed || type == NetworkCostType::Variable;
+}
+} // unnamed namespace
+
+void QNetworkListManagerEvents::emitWinRTUpdates()
+{
+ using namespace winrt::Windows::Networking::Connectivity;
+ ConnectionProfile profile = NetworkInformation::GetInternetConnectionProfile();
+ if (profile == nullptr)
+ return;
+ emit transportMediumChanged(getTransportMedium(profile));
+ emit isMeteredChanged(getMetered(profile));
+}
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/plugins/networkinformation/networklistmanager/qnetworklistmanagerevents.h b/src/plugins/networkinformation/networklistmanager/qnetworklistmanagerevents.h
new file mode 100644
index 0000000000..4c4625adb5
--- /dev/null
+++ b/src/plugins/networkinformation/networklistmanager/qnetworklistmanagerevents.h
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module 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 <QtNetwork/private/qtnetworkglobal_p.h>
+
+#include <QtNetwork/qnetworkinformation.h>
+
+#include <QtCore/qstring.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qloggingcategory.h>
+
+#include <objbase.h>
+#include <netlistmgr.h>
+#include <wrl/client.h>
+#include <wrl/wrappers/corewrappers.h>
+#include <comdef.h>
+
+#if defined(Q_CC_MSVC) && !defined(Q_CC_CLANG)
+#define SUPPORTS_WINRT 1
+#endif
+
+#ifdef SUPPORTS_WINRT
+#include <winrt/base.h>
+#endif
+
+using namespace Microsoft::WRL;
+
+QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcNetInfoNLM)
+
+inline QString errorStringFromHResult(HRESULT hr)
+{
+ _com_error error(hr);
+ return QString::fromWCharArray(error.ErrorMessage());
+}
+
+class QNetworkListManagerEvents : public QObject, public INetworkListManagerEvents
+{
+ Q_OBJECT
+public:
+ QNetworkListManagerEvents();
+ virtual ~QNetworkListManagerEvents();
+
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override;
+
+ ULONG STDMETHODCALLTYPE AddRef() override { return ++ref; }
+ ULONG STDMETHODCALLTYPE Release() override
+ {
+ if (--ref == 0) {
+ delete this;
+ return 0;
+ }
+ return ref;
+ }
+
+ HRESULT STDMETHODCALLTYPE ConnectivityChanged(NLM_CONNECTIVITY newConnectivity) override;
+
+ [[nodiscard]] bool start();
+ bool stop();
+
+ [[nodiscard]] bool checkBehindCaptivePortal();
+
+signals:
+ void connectivityChanged(NLM_CONNECTIVITY);
+ void transportMediumChanged(QNetworkInformation::TransportMedium);
+ void isMeteredChanged(bool);
+
+private:
+ ComPtr<INetworkListManager> networkListManager = nullptr;
+ ComPtr<IConnectionPoint> connectionPoint = nullptr;
+
+#ifdef SUPPORTS_WINRT
+ void emitWinRTUpdates();
+
+ winrt::event_token token;
+#endif
+
+ QAtomicInteger<ULONG> ref = 0;
+ DWORD cookie = 0;
+};
+
+QT_END_NAMESPACE
diff --git a/src/plugins/networkinformation/networklistmanager/qnetworklistmanagernetworkinformationbackend.cpp b/src/plugins/networkinformation/networklistmanager/qnetworklistmanagernetworkinformationbackend.cpp
index 5157b778b9..4cdc80cd5d 100644
--- a/src/plugins/networkinformation/networklistmanager/qnetworklistmanagernetworkinformationbackend.cpp
+++ b/src/plugins/networkinformation/networklistmanager/qnetworklistmanagernetworkinformationbackend.cpp
@@ -39,42 +39,24 @@
#include <QtNetwork/private/qnetworkinformation_p.h>
+#include "qnetworklistmanagerevents.h"
+
#include <QtCore/qglobal.h>
#include <QtCore/private/qobject_p.h>
#include <QtCore/qscopeguard.h>
-#include <objbase.h>
-#include <netlistmgr.h>
-#include <wrl/client.h>
-#include <wrl/wrappers/corewrappers.h>
-#include <comdef.h>
-#include <iphlpapi.h>
-using namespace Microsoft::WRL;
-
QT_BEGIN_NAMESPACE
-Q_DECLARE_LOGGING_CATEGORY(lcNetInfoNLM)
-Q_LOGGING_CATEGORY(lcNetInfoNLM, "qt.network.info.netlistmanager");
-static const QString backendName = QStringLiteral("networklistmanager");
-
-namespace {
-QString errorStringFromHResult(HRESULT hr)
-{
- _com_error error(hr);
- return QString::fromWCharArray(error.ErrorMessage());
-}
+// Declared in qnetworklistmanagerevents.h
+Q_LOGGING_CATEGORY(lcNetInfoNLM, "qt.network.info.netlistmanager");
-template<typename T>
-bool QueryInterfaceImpl(IUnknown *from, REFIID riid, void **ppvObject)
+static QString backendName()
{
- if (riid == __uuidof(T)) {
- *ppvObject = static_cast<T *>(from);
- from->AddRef();
- return true;
- }
- return false;
+ return QString::fromUtf16(QNetworkInformationBackend::PluginNames
+ [QNetworkInformationBackend::PluginNamesWindowsIndex]);
}
+namespace {
bool testCONNECTIVITY(NLM_CONNECTIVITY connectivity, NLM_CONNECTIVITY flag)
{
return (connectivity & flag) == flag;
@@ -105,7 +87,6 @@ QNetworkInformation::Reachability reachabilityFromNLM_CONNECTIVITY(NLM_CONNECTIV
}
}
-class QNetworkListManagerEvents;
class QNetworkListManagerNetworkInformationBackend : public QNetworkInformationBackend
{
Q_OBJECT
@@ -113,7 +94,7 @@ public:
QNetworkListManagerNetworkInformationBackend();
~QNetworkListManagerNetworkInformationBackend();
- QString name() const override { return backendName; }
+ QString name() const override { return backendName(); }
QNetworkInformation::Features featuresSupported() const override
{
return featuresSupportedStatic();
@@ -122,15 +103,17 @@ public:
static QNetworkInformation::Features featuresSupportedStatic()
{
return QNetworkInformation::Features(QNetworkInformation::Feature::Reachability
- | QNetworkInformation::Feature::CaptivePortal);
+ | QNetworkInformation::Feature::CaptivePortal
+#ifdef SUPPORTS_WINRT
+ | QNetworkInformation::Feature::TransportMedium
+#endif
+ );
}
[[nodiscard]] bool start();
void stop();
private:
- friend class QNetworkListManagerEvents;
-
bool event(QEvent *event) override;
void setConnectivity(NLM_CONNECTIVITY newConnectivity);
void checkCaptivePortal();
@@ -151,7 +134,7 @@ class QNetworkListManagerNetworkInformationBackendFactory : public QNetworkInfor
public:
QNetworkListManagerNetworkInformationBackendFactory() = default;
~QNetworkListManagerNetworkInformationBackendFactory() = default;
- QString name() const override { return backendName; }
+ QString name() const override { return backendName(); }
QNetworkInformation::Features featuresSupported() const override
{
return QNetworkListManagerNetworkInformationBackend::featuresSupportedStatic();
@@ -172,164 +155,6 @@ public:
}
};
-class QNetworkListManagerEvents : public QObject, public INetworkListManagerEvents
-{
- Q_OBJECT
-public:
- QNetworkListManagerEvents();
- virtual ~QNetworkListManagerEvents();
-
- HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override;
-
- ULONG STDMETHODCALLTYPE AddRef() override { return ++ref; }
- ULONG STDMETHODCALLTYPE Release() override
- {
- if (--ref == 0) {
- delete this;
- return 0;
- }
- return ref;
- }
-
- HRESULT STDMETHODCALLTYPE ConnectivityChanged(NLM_CONNECTIVITY newConnectivity) override;
-
- [[nodiscard]] bool start();
- bool stop();
-
- [[nodiscard]] bool checkBehindCaptivePortal();
-
-signals:
- void connectivityChanged(NLM_CONNECTIVITY);
-
-private:
- ComPtr<INetworkListManager> networkListManager = nullptr;
- ComPtr<IConnectionPoint> connectionPoint = nullptr;
-
- QAtomicInteger<ULONG> ref = 0;
- DWORD cookie = 0;
-};
-
-QNetworkListManagerEvents::QNetworkListManagerEvents() : QObject(nullptr)
-{
- auto hr = CoCreateInstance(CLSID_NetworkListManager, nullptr, CLSCTX_INPROC_SERVER,
- IID_INetworkListManager, &networkListManager);
- if (FAILED(hr)) {
- qCWarning(lcNetInfoNLM) << "Could not get a NetworkListManager instance:"
- << errorStringFromHResult(hr);
- return;
- }
-
- ComPtr<IConnectionPointContainer> connectionPointContainer;
- hr = networkListManager.As(&connectionPointContainer);
- if (SUCCEEDED(hr)) {
- hr = connectionPointContainer->FindConnectionPoint(IID_INetworkListManagerEvents,
- &connectionPoint);
- }
- if (FAILED(hr)) {
- qCWarning(lcNetInfoNLM) << "Failed to get connection point for network list manager events:"
- << errorStringFromHResult(hr);
- }
-}
-
-QNetworkListManagerEvents::~QNetworkListManagerEvents()
-{
- Q_ASSERT(ref == 0);
-}
-
-HRESULT STDMETHODCALLTYPE QNetworkListManagerEvents::QueryInterface(REFIID riid, void **ppvObject)
-{
- if (!ppvObject)
- return E_INVALIDARG;
-
- return QueryInterfaceImpl<IUnknown>(this, riid, ppvObject)
- || QueryInterfaceImpl<INetworkListManagerEvents>(this, riid, ppvObject)
- ? S_OK
- : E_NOINTERFACE;
-}
-
-HRESULT STDMETHODCALLTYPE
-QNetworkListManagerEvents::ConnectivityChanged(NLM_CONNECTIVITY newConnectivity)
-{
- // This function is run on a different thread than 'monitor' is created on, so we need to run
- // it on that thread
- emit connectivityChanged(newConnectivity);
- return S_OK;
-}
-
-bool QNetworkListManagerEvents::start()
-{
- if (!connectionPoint) {
- qCWarning(lcNetInfoNLM, "Initialization failed, can't start!");
- return false;
- }
- auto hr = connectionPoint->Advise(this, &cookie);
- if (FAILED(hr)) {
- qCWarning(lcNetInfoNLM) << "Failed to subscribe to network connectivity events:"
- << errorStringFromHResult(hr);
- return false;
- }
-
- // Update connectivity since it might have changed since this class was constructed
- NLM_CONNECTIVITY connectivity;
- hr = networkListManager->GetConnectivity(&connectivity);
- if (FAILED(hr))
- qCWarning(lcNetInfoNLM) << "Could not get connectivity:" << errorStringFromHResult(hr);
- else
- emit connectivityChanged(connectivity);
- return true;
-}
-
-bool QNetworkListManagerEvents::stop()
-{
- Q_ASSERT(connectionPoint);
- auto hr = connectionPoint->Unadvise(cookie);
- if (FAILED(hr)) {
- qCWarning(lcNetInfoNLM) << "Failed to unsubscribe from network connectivity events:"
- << errorStringFromHResult(hr);
- return false;
- }
- cookie = 0;
- return true;
-}
-
-bool QNetworkListManagerEvents::checkBehindCaptivePortal()
-{
- if (!networkListManager)
- return false;
- ComPtr<IEnumNetworks> networks;
- HRESULT hr =
- networkListManager->GetNetworks(NLM_ENUM_NETWORK_CONNECTED, networks.GetAddressOf());
- if (FAILED(hr) || networks == nullptr)
- return false;
-
- // @note: This checks all connected networks, but that might not be necessary
- ComPtr<INetwork> network;
- hr = networks->Next(1, network.GetAddressOf(), nullptr);
- while (SUCCEEDED(hr) && network != nullptr) {
- ComPtr<IPropertyBag> propertyBag;
- hr = network.As(&propertyBag);
- if (SUCCEEDED(hr) && propertyBag != nullptr) {
- VARIANT variant;
- VariantInit(&variant);
- const auto scopedVariantClear = qScopeGuard([&variant]() { VariantClear(&variant); });
-
- const wchar_t *versions[] = { NA_InternetConnectivityV6, NA_InternetConnectivityV4 };
- for (const auto version : versions) {
- hr = propertyBag->Read(version, &variant, nullptr);
- if (SUCCEEDED(hr)
- && (V_UINT(&variant) & NLM_INTERNET_CONNECTIVITY_WEBHIJACK)
- == NLM_INTERNET_CONNECTIVITY_WEBHIJACK) {
- return true;
- }
- }
- }
-
- hr = networks->Next(1, network.GetAddressOf(), nullptr);
- }
-
- return false;
-}
-
QNetworkListManagerNetworkInformationBackend::QNetworkListManagerNetworkInformationBackend()
{
auto hr = CoInitialize(nullptr);
@@ -341,6 +166,12 @@ QNetworkListManagerNetworkInformationBackend::QNetworkListManagerNetworkInformat
managerEvents = new QNetworkListManagerEvents();
connect(managerEvents.Get(), &QNetworkListManagerEvents::connectivityChanged, this,
&QNetworkListManagerNetworkInformationBackend::setConnectivity);
+
+ connect(managerEvents.Get(), &QNetworkListManagerEvents::transportMediumChanged, this,
+ &QNetworkListManagerNetworkInformationBackend::setTransportMedium);
+
+ connect(managerEvents.Get(), &QNetworkListManagerEvents::isMeteredChanged, this,
+ &QNetworkListManagerNetworkInformationBackend::setMetered);
}
QNetworkListManagerNetworkInformationBackend::~QNetworkListManagerNetworkInformationBackend()
diff --git a/src/plugins/networkinformation/networkmanager/qnetworkmanagernetworkinformationbackend.cpp b/src/plugins/networkinformation/networkmanager/qnetworkmanagernetworkinformationbackend.cpp
index bfb04ae4a6..7018504abe 100644
--- a/src/plugins/networkinformation/networkmanager/qnetworkmanagernetworkinformationbackend.cpp
+++ b/src/plugins/networkinformation/networkmanager/qnetworkmanagernetworkinformationbackend.cpp
@@ -70,9 +70,76 @@ QNetworkInformation::Reachability reachabilityFromNMState(QNetworkManagerInterfa
}
return QNetworkInformation::Reachability::Unknown;
}
+
+QNetworkInformation::TransportMedium
+transportMediumFromDeviceType(QNetworkManagerInterface::NMDeviceType type)
+{
+ switch (type) {
+ case QNetworkManagerInterface::NM_DEVICE_TYPE_ETHERNET:
+ return QNetworkInformation::TransportMedium::Ethernet;
+ case QNetworkManagerInterface::NM_DEVICE_TYPE_WIFI:
+ return QNetworkInformation::TransportMedium::WiFi;
+ case QNetworkManagerInterface::NM_DEVICE_TYPE_BT:
+ return QNetworkInformation::TransportMedium::Bluetooth;
+ case QNetworkManagerInterface::NM_DEVICE_TYPE_MODEM:
+ return QNetworkInformation::TransportMedium::Cellular;
+
+ case QNetworkManagerInterface::NM_DEVICE_TYPE_UNKNOWN:
+ case QNetworkManagerInterface::NM_DEVICE_TYPE_GENERIC:
+ case QNetworkManagerInterface::NM_DEVICE_TYPE_UNUSED1:
+ case QNetworkManagerInterface::NM_DEVICE_TYPE_UNUSED2:
+ case QNetworkManagerInterface::NM_DEVICE_TYPE_OLPC_MESH:
+ case QNetworkManagerInterface::NM_DEVICE_TYPE_WIMAX:
+ case QNetworkManagerInterface::NM_DEVICE_TYPE_INFINIBAND:
+ case QNetworkManagerInterface::NM_DEVICE_TYPE_BOND:
+ case QNetworkManagerInterface::NM_DEVICE_TYPE_VLAN:
+ case QNetworkManagerInterface::NM_DEVICE_TYPE_ADSL:
+ case QNetworkManagerInterface::NM_DEVICE_TYPE_BRIDGE:
+ case QNetworkManagerInterface::NM_DEVICE_TYPE_TEAM:
+ case QNetworkManagerInterface::NM_DEVICE_TYPE_TUN:
+ case QNetworkManagerInterface::NM_DEVICE_TYPE_IP_TUNNEL:
+ case QNetworkManagerInterface::NM_DEVICE_TYPE_MACVLAN:
+ case QNetworkManagerInterface::NM_DEVICE_TYPE_VXLAN:
+ case QNetworkManagerInterface::NM_DEVICE_TYPE_VETH:
+ case QNetworkManagerInterface::NM_DEVICE_TYPE_MACSEC:
+ case QNetworkManagerInterface::NM_DEVICE_TYPE_DUMMY:
+ case QNetworkManagerInterface::NM_DEVICE_TYPE_PPP:
+ case QNetworkManagerInterface::NM_DEVICE_TYPE_OVS_INTERFACE:
+ case QNetworkManagerInterface::NM_DEVICE_TYPE_OVS_PORT:
+ case QNetworkManagerInterface::NM_DEVICE_TYPE_OVS_BRIDGE:
+ case QNetworkManagerInterface::NM_DEVICE_TYPE_WPAN:
+ case QNetworkManagerInterface::NM_DEVICE_TYPE_6LOWPAN:
+ case QNetworkManagerInterface::NM_DEVICE_TYPE_WIREGUARD:
+ case QNetworkManagerInterface::NM_DEVICE_TYPE_WIFI_P2P:
+ case QNetworkManagerInterface::NM_DEVICE_TYPE_VRF:
+ break;
+ }
+ // While the list is exhaustive of the enum there can be additional
+ // entries added in NetworkManager that isn't listed here
+ return QNetworkInformation::TransportMedium::Unknown;
+}
+
+bool isMeteredFromNMMetered(QNetworkManagerInterface::NMMetered metered)
+{
+ switch (metered) {
+ case QNetworkManagerInterface::NM_METERED_YES:
+ case QNetworkManagerInterface::NM_METERED_GUESS_YES:
+ return true;
+ case QNetworkManagerInterface::NM_METERED_NO:
+ case QNetworkManagerInterface::NM_METERED_GUESS_NO:
+ case QNetworkManagerInterface::NM_METERED_UNKNOWN:
+ return false;
+ }
+ Q_UNREACHABLE();
+ return false;
}
+} // unnamed namespace
-static QString backendName = QStringLiteral("networkmanager");
+static QString backendName()
+{
+ return QString::fromUtf16(QNetworkInformationBackend::PluginNames
+ [QNetworkInformationBackend::PluginNamesLinuxIndex]);
+}
class QNetworkManagerNetworkInformationBackend : public QNetworkInformationBackend
{
@@ -81,7 +148,7 @@ public:
QNetworkManagerNetworkInformationBackend();
~QNetworkManagerNetworkInformationBackend() = default;
- QString name() const override { return backendName; }
+ QString name() const override { return backendName(); }
QNetworkInformation::Features featuresSupported() const override
{
if (!isValid())
@@ -92,7 +159,8 @@ public:
static QNetworkInformation::Features featuresSupportedStatic()
{
using Feature = QNetworkInformation::Feature;
- return QNetworkInformation::Features(Feature::Reachability | Feature::CaptivePortal);
+ return QNetworkInformation::Features(Feature::Reachability | Feature::CaptivePortal
+ | Feature::TransportMedium | Feature::Metered);
}
bool isValid() const { return iface.isValid(); }
@@ -111,7 +179,7 @@ class QNetworkManagerNetworkInformationBackendFactory : public QNetworkInformati
public:
QNetworkManagerNetworkInformationBackendFactory() = default;
~QNetworkManagerNetworkInformationBackendFactory() = default;
- QString name() const override { return backendName; }
+ QString name() const override { return backendName(); }
QNetworkInformation::Features featuresSupported() const override
{
if (!QNetworkManagerInterfaceBase::networkManagerAvailable())
@@ -136,24 +204,32 @@ private:
QNetworkManagerNetworkInformationBackend::QNetworkManagerNetworkInformationBackend()
{
- using NMState = QNetworkManagerInterface::NMState;
- setReachability(reachabilityFromNMState(iface.state()));
- connect(&iface, &QNetworkManagerInterface::stateChanged, this,
- [this](NMState newState) {
- setReachability(reachabilityFromNMState(newState));
- });
-
- using ConnectivityState = QNetworkManagerInterface::NMConnectivityState;
-
- const auto connectivityState = iface.connectivityState();
- const bool behindPortal = (connectivityState == ConnectivityState::NM_CONNECTIVITY_PORTAL);
- setBehindCaptivePortal(behindPortal);
-
+ auto updateReachability = [this](QNetworkManagerInterface::NMState newState) {
+ setReachability(reachabilityFromNMState(newState));
+ };
+ updateReachability(iface.state());
+ connect(&iface, &QNetworkManagerInterface::stateChanged, this, std::move(updateReachability));
+
+ auto updateBehindCaptivePortal = [this](QNetworkManagerInterface::NMConnectivityState state) {
+ const bool behindPortal = (state == QNetworkManagerInterface::NM_CONNECTIVITY_PORTAL);
+ setBehindCaptivePortal(behindPortal);
+ };
+ updateBehindCaptivePortal(iface.connectivityState());
connect(&iface, &QNetworkManagerInterface::connectivityChanged, this,
- [this](ConnectivityState state) {
- const bool behindPortal = (state == ConnectivityState::NM_CONNECTIVITY_PORTAL);
- setBehindCaptivePortal(behindPortal);
- });
+ std::move(updateBehindCaptivePortal));
+
+ auto updateTransportMedium = [this](QNetworkManagerInterface::NMDeviceType newDevice) {
+ setTransportMedium(transportMediumFromDeviceType(newDevice));
+ };
+ updateTransportMedium(iface.deviceType());
+ connect(&iface, &QNetworkManagerInterface::deviceTypeChanged, this,
+ std::move(updateTransportMedium));
+
+ auto updateMetered = [this](QNetworkManagerInterface::NMMetered metered) {
+ setMetered(isMeteredFromNMMetered(metered));
+ };
+ updateMetered(iface.meteredState());
+ connect(&iface, &QNetworkManagerInterface::meteredChanged, this, std::move(updateMetered));
}
QT_END_NAMESPACE
diff --git a/src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.cpp b/src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.cpp
index 764507fd4b..f892348992 100644
--- a/src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.cpp
+++ b/src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.cpp
@@ -52,6 +52,13 @@
#define DBUS_PROPERTIES_INTERFACE "org.freedesktop.DBus.Properties"
+#define NM_DBUS_SERVICE "org.freedesktop.NetworkManager"
+
+#define NM_DBUS_PATH "/org/freedesktop/NetworkManager"
+#define NM_DBUS_INTERFACE NM_DBUS_SERVICE
+#define NM_CONNECTION_DBUS_INTERFACE NM_DBUS_SERVICE ".Connection.Active"
+#define NM_DEVICE_DBUS_INTERFACE NM_DBUS_SERVICE ".Device"
+
QT_BEGIN_NAMESPACE
QNetworkManagerInterfaceBase::QNetworkManagerInterfaceBase(QObject *parent)
@@ -86,16 +93,16 @@ QNetworkManagerInterface::QNetworkManagerInterface(QObject *parent)
QDBusConnection::systemBus().connect(
QLatin1String(NM_DBUS_SERVICE), QLatin1String(NM_DBUS_PATH),
- QLatin1String(NM_DBUS_INTERFACE), QLatin1String("PropertiesChanged"), this,
- SLOT(setProperties(QMap<QString, QVariant>)));
+ QLatin1String(DBUS_PROPERTIES_INTERFACE), QLatin1String("PropertiesChanged"), this,
+ SLOT(setProperties(QString, QMap<QString, QVariant>, QList<QString>)));
}
QNetworkManagerInterface::~QNetworkManagerInterface()
{
QDBusConnection::systemBus().disconnect(
QLatin1String(NM_DBUS_SERVICE), QLatin1String(NM_DBUS_PATH),
- QLatin1String(NM_DBUS_INTERFACE), QLatin1String("PropertiesChanged"), this,
- SLOT(setProperties(QMap<QString, QVariant>)));
+ QLatin1String(DBUS_PROPERTIES_INTERFACE), QLatin1String("PropertiesChanged"), this,
+ SLOT(setProperties(QString, QMap<QString, QVariant>, QList<QString>)));
}
QNetworkManagerInterface::NMState QNetworkManagerInterface::state() const
@@ -112,29 +119,100 @@ QNetworkManagerInterface::NMConnectivityState QNetworkManagerInterface::connecti
return QNetworkManagerInterface::NM_CONNECTIVITY_UNKNOWN;
}
-void QNetworkManagerInterface::setProperties(const QMap<QString, QVariant> &map)
+static QDBusInterface getPrimaryDevice(const QDBusObjectPath &devicePath)
+{
+ const QDBusInterface connection(NM_DBUS_SERVICE, devicePath.path(),
+ NM_CONNECTION_DBUS_INTERFACE, QDBusConnection::systemBus());
+ if (!connection.isValid())
+ return QDBusInterface({}, {});
+
+ const auto devicePaths = connection.property("Devices").value<QList<QDBusObjectPath>>();
+ if (devicePaths.isEmpty())
+ return QDBusInterface({}, {});
+
+ const QDBusObjectPath primaryDevicePath = devicePaths.front();
+ return QDBusInterface(NM_DBUS_SERVICE, primaryDevicePath.path(), NM_DEVICE_DBUS_INTERFACE,
+ QDBusConnection::systemBus());
+}
+
+std::optional<QDBusObjectPath> QNetworkManagerInterface::primaryConnectionDevicePath() const
+{
+ auto it = propertyMap.constFind(u"PrimaryConnection"_qs);
+ if (it != propertyMap.cend())
+ return it->value<QDBusObjectPath>();
+ return std::nullopt;
+}
+
+auto QNetworkManagerInterface::deviceType() const -> NMDeviceType
+{
+ if (const auto path = primaryConnectionDevicePath())
+ return extractDeviceType(*path);
+ return NM_DEVICE_TYPE_UNKNOWN;
+}
+
+auto QNetworkManagerInterface::meteredState() const -> NMMetered
{
+ if (const auto path = primaryConnectionDevicePath())
+ return extractDeviceMetered(*path);
+ return NM_METERED_UNKNOWN;
+}
+
+auto QNetworkManagerInterface::extractDeviceType(const QDBusObjectPath &devicePath) const
+ -> NMDeviceType
+{
+ QDBusInterface primaryDevice = getPrimaryDevice(devicePath);
+ if (!primaryDevice.isValid())
+ return NM_DEVICE_TYPE_UNKNOWN;
+ const QVariant deviceType = primaryDevice.property("DeviceType");
+ if (!deviceType.isValid())
+ return NM_DEVICE_TYPE_UNKNOWN;
+ return static_cast<NMDeviceType>(deviceType.toUInt());
+}
+
+auto QNetworkManagerInterface::extractDeviceMetered(const QDBusObjectPath &devicePath) const
+ -> NMMetered
+{
+ QDBusInterface primaryDevice = getPrimaryDevice(devicePath);
+ if (!primaryDevice.isValid())
+ return NM_METERED_UNKNOWN;
+ const QVariant metered = primaryDevice.property("Metered");
+ if (!metered.isValid())
+ return NM_METERED_UNKNOWN;
+ return static_cast<NMMetered>(metered.toUInt());
+}
+
+void QNetworkManagerInterface::setProperties(const QString &interfaceName,
+ const QMap<QString, QVariant> &map,
+ const QStringList &invalidatedProperties)
+{
+ Q_UNUSED(interfaceName);
+ Q_UNUSED(invalidatedProperties);
+
for (auto i = map.cbegin(), end = map.cend(); i != end; ++i) {
- const bool isState = i.key() == QLatin1String("State");
- const bool isConnectivity = i.key() == QLatin1String("Connectivity");
- bool stateUpdate = isState;
- bool connectivityUpdate = isConnectivity;
+ bool valueChanged = true;
auto it = propertyMap.lowerBound(i.key());
if (it != propertyMap.end() && it.key() == i.key()) {
- stateUpdate &= (it.value() != i.value());
- connectivityUpdate &= (it.value() != i.value());
+ valueChanged = (it.value() != i.value());
*it = *i;
} else {
propertyMap.insert(it, i.key(), i.value());
}
- if (stateUpdate) {
- quint32 state = i.value().toUInt();
- Q_EMIT stateChanged(static_cast<NMState>(state));
- } else if (connectivityUpdate) {
- quint32 state = i.value().toUInt();
- Q_EMIT connectivityChanged(static_cast<NMConnectivityState>(state));
+ if (valueChanged) {
+ if (i.key() == QLatin1String("State")) {
+ quint32 state = i.value().toUInt();
+ Q_EMIT stateChanged(static_cast<NMState>(state));
+ } else if (i.key() == QLatin1String("Connectivity")) {
+ quint32 state = i.value().toUInt();
+ Q_EMIT connectivityChanged(static_cast<NMConnectivityState>(state));
+ } else if (i.key() == QLatin1String("PrimaryConnection")) {
+ const QDBusObjectPath devicePath = i->value<QDBusObjectPath>();
+ Q_EMIT deviceTypeChanged(extractDeviceType(devicePath));
+ Q_EMIT meteredChanged(extractDeviceMetered(devicePath));
+ } else if (i.key() == QLatin1String("Metered")) {
+ Q_EMIT meteredChanged(static_cast<NMMetered>(i->toUInt()));
+ }
}
}
}
diff --git a/src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.h b/src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.h
index 57c5aed763..f13ec88ad5 100644
--- a/src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.h
+++ b/src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.h
@@ -55,10 +55,7 @@
#include <QtDBus/QDBusPendingCallWatcher>
#include <QtDBus/QDBusObjectPath>
-#define NM_DBUS_SERVICE "org.freedesktop.NetworkManager"
-
-#define NM_DBUS_PATH "/org/freedesktop/NetworkManager"
-#define NM_DBUS_INTERFACE "org.freedesktop.NetworkManager"
+#include <optional>
// Matches 'NMDeviceState' from https://developer.gnome.org/NetworkManager/stable/nm-dbus-types.html
enum NMDeviceState {
@@ -119,23 +116,78 @@ public:
NM_CONNECTIVITY_FULL = 4,
};
Q_ENUM(NMConnectivityState);
+ // Matches 'NMDeviceType' from
+ // https://developer-old.gnome.org/NetworkManager/stable/nm-dbus-types.html#NMDeviceType
+ enum NMDeviceType {
+ NM_DEVICE_TYPE_UNKNOWN = 0,
+ NM_DEVICE_TYPE_GENERIC = 14,
+ NM_DEVICE_TYPE_ETHERNET = 1,
+ NM_DEVICE_TYPE_WIFI = 2,
+ NM_DEVICE_TYPE_UNUSED1 = 3,
+ NM_DEVICE_TYPE_UNUSED2 = 4,
+ NM_DEVICE_TYPE_BT = 5,
+ NM_DEVICE_TYPE_OLPC_MESH = 6,
+ NM_DEVICE_TYPE_WIMAX = 7,
+ NM_DEVICE_TYPE_MODEM = 8,
+ NM_DEVICE_TYPE_INFINIBAND = 9,
+ NM_DEVICE_TYPE_BOND = 10,
+ NM_DEVICE_TYPE_VLAN = 11,
+ NM_DEVICE_TYPE_ADSL = 12,
+ NM_DEVICE_TYPE_BRIDGE = 13,
+ NM_DEVICE_TYPE_TEAM = 15,
+ NM_DEVICE_TYPE_TUN = 16,
+ NM_DEVICE_TYPE_IP_TUNNEL = 17,
+ NM_DEVICE_TYPE_MACVLAN = 18,
+ NM_DEVICE_TYPE_VXLAN = 19,
+ NM_DEVICE_TYPE_VETH = 20,
+ NM_DEVICE_TYPE_MACSEC = 21,
+ NM_DEVICE_TYPE_DUMMY = 22,
+ NM_DEVICE_TYPE_PPP = 23,
+ NM_DEVICE_TYPE_OVS_INTERFACE = 24,
+ NM_DEVICE_TYPE_OVS_PORT = 25,
+ NM_DEVICE_TYPE_OVS_BRIDGE = 26,
+ NM_DEVICE_TYPE_WPAN = 27,
+ NM_DEVICE_TYPE_6LOWPAN = 28,
+ NM_DEVICE_TYPE_WIREGUARD = 29,
+ NM_DEVICE_TYPE_WIFI_P2P = 30,
+ NM_DEVICE_TYPE_VRF = 31,
+ };
+ // Matches 'NMMetered' from
+ // https://developer-old.gnome.org/NetworkManager/stable/nm-dbus-types.html#NMMetered
+ enum NMMetered {
+ NM_METERED_UNKNOWN,
+ NM_METERED_YES,
+ NM_METERED_NO,
+ NM_METERED_GUESS_YES,
+ NM_METERED_GUESS_NO,
+ };
QNetworkManagerInterface(QObject *parent = nullptr);
~QNetworkManagerInterface();
NMState state() const;
NMConnectivityState connectivityState() const;
+ NMDeviceType deviceType() const;
+ NMMetered meteredState() const;
Q_SIGNALS:
void stateChanged(NMState);
void connectivityChanged(NMConnectivityState);
+ void deviceTypeChanged(NMDeviceType);
+ void meteredChanged(NMMetered);
private Q_SLOTS:
- void setProperties(const QMap<QString, QVariant> &map);
+ void setProperties(const QString &interfaceName, const QMap<QString, QVariant> &map,
+ const QStringList &invalidatedProperties);
private:
Q_DISABLE_COPY_MOVE(QNetworkManagerInterface)
+ NMDeviceType extractDeviceType(const QDBusObjectPath &devicePath) const;
+ NMMetered extractDeviceMetered(const QDBusObjectPath &devicePath) const;
+
+ std::optional<QDBusObjectPath> primaryConnectionDevicePath() const;
+
QVariantMap propertyMap;
};
diff --git a/src/plugins/networkinformation/scnetworkreachability/qscnetworkreachabilitynetworkinformationbackend.mm b/src/plugins/networkinformation/scnetworkreachability/qscnetworkreachabilitynetworkinformationbackend.mm
index c732afc012..272327f7da 100644
--- a/src/plugins/networkinformation/scnetworkreachability/qscnetworkreachabilitynetworkinformationbackend.mm
+++ b/src/plugins/networkinformation/scnetworkreachability/qscnetworkreachabilitynetworkinformationbackend.mm
@@ -48,8 +48,11 @@ QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcNetInfoSCR)
Q_LOGGING_CATEGORY(lcNetInfoSCR, "qt.network.info.scnetworkreachability");
-
-static QString backendName = QStringLiteral("scnetworkreachability");
+static QString backendName()
+{
+ return QString::fromUtf16(QNetworkInformationBackend::PluginNames
+ [QNetworkInformationBackend::PluginNamesAppleIndex]);
+}
class QSCNetworkReachabilityNetworkInformationBackend : public QNetworkInformationBackend
{
@@ -58,7 +61,7 @@ public:
QSCNetworkReachabilityNetworkInformationBackend();
~QSCNetworkReachabilityNetworkInformationBackend();
- QString name() const override { return backendName; }
+ QString name() const override { return backendName(); }
QNetworkInformation::Features featuresSupported() const override
{
return featuresSupportedStatic();
@@ -66,12 +69,20 @@ public:
static QNetworkInformation::Features featuresSupportedStatic()
{
- return QNetworkInformation::Features(QNetworkInformation::Feature::Reachability);
+ return QNetworkInformation::Features(QNetworkInformation::Feature::Reachability
+#ifdef QT_PLATFORM_UIKIT
+ | QNetworkInformation::Feature::TransportMedium
+#endif
+ );
}
private Q_SLOTS:
void reachabilityChanged(bool isOnline);
+#ifdef QT_PLATFORM_UIKIT
+ void isWwanChanged(bool isOnline);
+#endif
+
private:
Q_DISABLE_COPY_MOVE(QSCNetworkReachabilityNetworkInformationBackend);
@@ -87,7 +98,7 @@ class QSCNetworkReachabilityNetworkInformationBackendFactory : public QNetworkIn
public:
QSCNetworkReachabilityNetworkInformationBackendFactory() = default;
~QSCNetworkReachabilityNetworkInformationBackendFactory() = default;
- QString name() const override { return backendName; }
+ QString name() const override { return backendName(); }
QNetworkInformation::Features featuresSupported() const override
{
return QSCNetworkReachabilityNetworkInformationBackend::featuresSupportedStatic();
@@ -107,10 +118,16 @@ private:
QSCNetworkReachabilityNetworkInformationBackend::QSCNetworkReachabilityNetworkInformationBackend()
{
bool isOnline = false;
+#ifdef QT_PLATFORM_UIKIT
+ bool isWwan = false;
+#endif
if (ipv4Probe.setTargets(QHostAddress::AnyIPv4, {})) {
// We manage to create SCNetworkReachabilityRef for IPv4, let's
// read the last known state then!
isOnline |= ipv4Probe.isReachable();
+#ifdef QT_PLATFORM_UIKIT
+ isWwan |= ipv4Probe.isWwan();
+#endif
ipv4Probe.startMonitoring();
}
@@ -118,9 +135,15 @@ QSCNetworkReachabilityNetworkInformationBackend::QSCNetworkReachabilityNetworkIn
// We manage to create SCNetworkReachability ref for IPv6, let's
// read the last known state then!
isOnline |= ipv6Probe.isReachable();
+#ifdef QT_PLATFORM_UIKIT
+ isWwan |= ipv6Probe.isWwan();
+#endif
ipv6Probe.startMonitoring();
}
reachabilityChanged(isOnline);
+#ifdef QT_PLATFORM_UIKIT
+ isWwanChanged(isWwan);
+#endif
connect(&ipv4Probe, &QNetworkConnectionMonitor::reachabilityChanged, this,
&QSCNetworkReachabilityNetworkInformationBackend::reachabilityChanged,
@@ -128,6 +151,15 @@ QSCNetworkReachabilityNetworkInformationBackend::QSCNetworkReachabilityNetworkIn
connect(&ipv6Probe, &QNetworkConnectionMonitor::reachabilityChanged, this,
&QSCNetworkReachabilityNetworkInformationBackend::reachabilityChanged,
Qt::QueuedConnection);
+
+#ifdef QT_PLATFORM_UIKIT
+ connect(&ipv4Probe, &QNetworkConnectionMonitor::isWwanChanged, this,
+ &QSCNetworkReachabilityNetworkInformationBackend::isWwanChanged,
+ Qt::QueuedConnection);
+ connect(&ipv6Probe, &QNetworkConnectionMonitor::isWwanChanged, this,
+ &QSCNetworkReachabilityNetworkInformationBackend::isWwanChanged,
+ Qt::QueuedConnection);
+#endif
}
QSCNetworkReachabilityNetworkInformationBackend::~QSCNetworkReachabilityNetworkInformationBackend()
@@ -140,6 +172,23 @@ void QSCNetworkReachabilityNetworkInformationBackend::reachabilityChanged(bool i
: QNetworkInformation::Reachability::Disconnected);
}
+#ifdef QT_PLATFORM_UIKIT
+void QSCNetworkReachabilityNetworkInformationBackend::isWwanChanged(bool isWwan)
+{
+ // The reachability API from Apple only has one entry regarding transport medium: "IsWWAN"[0].
+ // This is _serviceable_ on iOS where the only other credible options are "WLAN" or
+ // "Disconnected". But on macOS you could be connected by Ethernet as well, so how would that be
+ // reported? It doesn't matter anyway since "IsWWAN" is not available on macOS.
+ // [0]: https://developer.apple.com/documentation/systemconfiguration/scnetworkreachabilityflags/kscnetworkreachabilityflagsiswwan?language=objc
+ if (reachability() == QNetworkInformation::Reachability::Disconnected) {
+ setTransportMedium(QNetworkInformation::TransportMedium::Unknown);
+ } else {
+ setTransportMedium(isWwan ? QNetworkInformation::TransportMedium::Cellular
+ : QNetworkInformation::TransportMedium::WiFi);
+ }
+}
+#endif
+
QT_END_NAMESPACE
#include "qscnetworkreachabilitynetworkinformationbackend.moc"
diff --git a/src/plugins/platforms/android/androidcontentfileengine.cpp b/src/plugins/platforms/android/androidcontentfileengine.cpp
index 2c47b71c88..b18c81e896 100644
--- a/src/plugins/platforms/android/androidcontentfileengine.cpp
+++ b/src/plugins/platforms/android/androidcontentfileengine.cpp
@@ -54,8 +54,10 @@ AndroidContentFileEngine::AndroidContentFileEngine(const QString &f)
setFileName(f);
}
-bool AndroidContentFileEngine::open(QIODevice::OpenMode openMode)
+bool AndroidContentFileEngine::open(QIODevice::OpenMode openMode,
+ std::optional<QFile::Permissions> permissions)
{
+ Q_UNUSED(permissions);
QString openModeStr;
if (openMode & QFileDevice::ReadOnly) {
openModeStr += QLatin1Char('r');
diff --git a/src/plugins/platforms/android/androidcontentfileengine.h b/src/plugins/platforms/android/androidcontentfileengine.h
index 31eaf9b0ab..531d0f80ff 100644
--- a/src/plugins/platforms/android/androidcontentfileengine.h
+++ b/src/plugins/platforms/android/androidcontentfileengine.h
@@ -46,7 +46,7 @@ class AndroidContentFileEngine : public QFSFileEngine
{
public:
AndroidContentFileEngine(const QString &fileName);
- bool open(QIODevice::OpenMode openMode) override;
+ bool open(QIODevice::OpenMode openMode, std::optional<QFile::Permissions> permissions) override;
qint64 size() const override;
FileFlags fileFlags(FileFlags type = FileInfoAll) const override;
QString fileName(FileName file = DefaultName) const override;
diff --git a/src/plugins/platforms/android/androidjniinput.cpp b/src/plugins/platforms/android/androidjniinput.cpp
index ebdc18cc07..d4b51d38ba 100644
--- a/src/plugins/platforms/android/androidjniinput.cpp
+++ b/src/plugins/platforms/android/androidjniinput.cpp
@@ -78,16 +78,15 @@ namespace QtAndroidInput
candidatesEnd);
}
- void showSoftwareKeyboard(int left, int top, int width, int height, int editorHeight, int inputHints, int enterKeyType)
+ void showSoftwareKeyboard(int left, int top, int width, int height, int inputHints, int enterKeyType)
{
QJniObject::callStaticMethod<void>(applicationClass(),
"showSoftwareKeyboard",
- "(IIIIIII)V",
+ "(IIIIII)V",
left,
top,
width,
height,
- editorHeight,
inputHints,
enterKeyType);
#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
@@ -134,17 +133,6 @@ namespace QtAndroidInput
anchor.x(), anchor.y(), rtl);
}
- void updateInputItemRectangle(int left, int top, int width, int height)
- {
- QJniObject::callStaticMethod<void>(applicationClass(),
- "updateInputItemRectangle",
- "(IIII)V",
- left,
- top,
- width,
- height);
- }
-
static void mouseDown(JNIEnv */*env*/, jobject /*thiz*/, jint /*winId*/, jint x, jint y)
{
if (m_ignoreMouseEvents)
@@ -279,15 +267,11 @@ namespace QtAndroidInput
}
}
- static void touchEnd(JNIEnv */*env*/, jobject /*thiz*/, jint /*winId*/, jint /*action*/)
+ static QPointingDevice *getTouchDevice()
{
- if (m_touchPoints.isEmpty())
- return;
-
- QMutexLocker lock(QtAndroid::platformInterfaceMutex());
QAndroidPlatformIntegration *platformIntegration = QtAndroid::androidPlatformIntegration();
if (!platformIntegration)
- return;
+ return nullptr;
QPointingDevice *touchDevice = platformIntegration->touchDevice();
if (!touchDevice) {
@@ -303,10 +287,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());
+ QPointingDevice *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());
+ QPointingDevice *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)
@@ -858,6 +869,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/androidjniinput.h b/src/plugins/platforms/android/androidjniinput.h
index ceef2a826c..30c083f51c 100644
--- a/src/plugins/platforms/android/androidjniinput.h
+++ b/src/plugins/platforms/android/androidjniinput.h
@@ -49,7 +49,7 @@ QT_BEGIN_NAMESPACE
namespace QtAndroidInput
{
// Software keyboard support
- void showSoftwareKeyboard(int top, int left, int width, int editorHeight, int height, int inputHints, int enterKeyType);
+ void showSoftwareKeyboard(int top, int left, int width, int height, int inputHints, int enterKeyType);
void resetSoftwareKeyboard();
void hideSoftwareKeyboard();
bool isSoftwareKeyboardVisible();
@@ -57,8 +57,6 @@ namespace QtAndroidInput
void updateSelection(int selStart, int selEnd, int candidatesStart, int candidatesEnd);
// Software keyboard support
- // edit field resize
- void updateInputItemRectangle(int left, int top, int width, int height);
// cursor/selection handles
void updateHandles(int handleCount, QPoint editMenuPos = QPoint(), uint32_t editButtons = 0,
QPoint cursor = QPoint(), QPoint anchor = QPoint(), bool rtl = false);
diff --git a/src/plugins/platforms/android/androidjnimain.cpp b/src/plugins/platforms/android/androidjnimain.cpp
index 6dff19f3d5..c5cb056739 100644
--- a/src/plugins/platforms/android/androidjnimain.cpp
+++ b/src/plugins/platforms/android/androidjnimain.cpp
@@ -60,6 +60,7 @@
#include <android/bitmap.h>
#include <QtCore/private/qjnihelpers_p.h>
+#include <QtCore/qbasicatomic.h>
#include <QtCore/qjnienvironment.h>
#include <QtCore/qjniobject.h>
#include <QtCore/qresource.h>
@@ -104,7 +105,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;
@@ -123,6 +123,8 @@ static const char m_qtTag[] = "Qt";
static const char m_classErrorMsg[] = "Can't find class \"%s\"";
static const char m_methodErrorMsg[] = "Can't find method \"%s%s\"";
+static QBasicAtomicInt startQtAndroidPluginCalled = Q_BASIC_ATOMIC_INITIALIZER(0);
+
namespace QtAndroid
{
QBasicMutex *platformInterfaceMutex()
@@ -322,6 +324,11 @@ namespace QtAndroid
return manufacturer + QLatin1Char(' ') + model;
}
+ jint generateViewId()
+ {
+ return QJniObject::callStaticMethod<jint>("android/view/View", "generateViewId", "()I");
+ }
+
int createSurface(AndroidSurfaceClient *client, const QRect &geometry, bool onTop, int imageDepth)
{
QJniEnvironment env;
@@ -329,7 +336,7 @@ namespace QtAndroid
return -1;
m_surfacesMutex.lock();
- int surfaceId = m_surfaceId++;
+ jint surfaceId = generateViewId();
m_surfaces[surfaceId] = client;
m_surfacesMutex.unlock();
@@ -352,7 +359,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();
@@ -545,6 +552,7 @@ static void startQtApplication(JNIEnv */*env*/, jclass /*clazz*/)
for (int i = 0; i < m_applicationParams.size(); i++)
params[i] = static_cast<const char *>(m_applicationParams[i].constData());
+ startQtAndroidPluginCalled.fetchAndAddRelease(1);
int ret = m_main(m_applicationParams.length(), const_cast<char **>(params.data()));
if (m_mainLibraryHnd) {
@@ -592,7 +600,9 @@ static void terminateQt(JNIEnv *env, jclass /*clazz*/)
QAndroidEventDispatcherStopper::instance()->goingToStop(false);
}
- sem_wait(&m_terminateSemaphore);
+ if (startQtAndroidPluginCalled.loadAcquire())
+ sem_wait(&m_terminateSemaphore);
+
sem_destroy(&m_terminateSemaphore);
env->DeleteGlobalRef(m_applicationClass);
diff --git a/src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp b/src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp
index 15c0aada95..0c6fb92ffb 100644
--- a/src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp
+++ b/src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp
@@ -261,8 +261,10 @@ public:
close();
}
- bool open(QIODevice::OpenMode openMode) override
+ bool open(QIODevice::OpenMode openMode, std::optional<QFile::Permissions> permissions) override
{
+ Q_UNUSED(permissions);
+
if (m_isFolder || (openMode & QIODevice::WriteOnly))
return false;
close();
@@ -309,21 +311,11 @@ public:
return -1;
}
- bool isSequential() const override
- {
- return false;
- }
-
bool caseSensitive() const override
{
return true;
}
- bool isRelativePath() const override
- {
- return false;
- }
-
FileFlags fileFlags(FileFlags type = FileInfoAll) const override
{
FileFlags commonFlags(ReadOwnerPerm|ReadUserPerm|ReadGroupPerm|ReadOtherPerm|ExistsFlag);
@@ -368,7 +360,7 @@ public:
m_fileName = cleanedAssetPath(file);
switch (FolderIterator::fileType(m_fileName)) {
case AssetItem::Type::File:
- open(QIODevice::ReadOnly);
+ open(QIODevice::ReadOnly, std::nullopt);
break;
case AssetItem::Type::Folder:
m_isFolder = true;
diff --git a/src/plugins/platforms/android/qandroidinputcontext.cpp b/src/plugins/platforms/android/qandroidinputcontext.cpp
index fb51e1dba1..417f3469a8 100644
--- a/src/plugins/platforms/android/qandroidinputcontext.cpp
+++ b/src/plugins/platforms/android/qandroidinputcontext.cpp
@@ -60,6 +60,7 @@
#include <qthread.h>
#include <qwindow.h>
#include <qpa/qplatformwindow.h>
+
QT_BEGIN_NAMESPACE
namespace {
@@ -492,7 +493,7 @@ QAndroidInputContext::QAndroidInputContext()
m_androidInputContext = this;
QObject::connect(QGuiApplication::inputMethod(), &QInputMethod::cursorRectangleChanged,
- this, &QAndroidInputContext::updateInputItemRectangle);
+ this, &QAndroidInputContext::updateSelectionHandles);
QObject::connect(QGuiApplication::inputMethod(), &QInputMethod::anchorRectangleChanged,
this, &QAndroidInputContext::updateSelectionHandles);
QObject::connect(QGuiApplication::inputMethod(), &QInputMethod::inputItemClipRectangleChanged, this, [this]{
@@ -882,44 +883,9 @@ void QAndroidInputContext::update(Qt::InputMethodQueries queries)
QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(queries);
if (query.isNull())
return;
-
- if (query->value(Qt::ImCursorPosition).toInt() >= 0 &&
- query->value(Qt::ImSurroundingText).toString()
- .left(query->value(Qt::ImCursorPosition).toInt()).length() >= 0) {
-
- // Cursor position should be always valid
- // when object is composing
- if (focusObjectIsComposing())
- return;
-
- // NOTE: This seems to be happening sometimes
- // when qt quick application is booted up
- if (m_focusObject == nullptr)
- return;
-
- if (m_focusObject->isWidgetType())
- updateCursorPosition();
- else
- updateCursorPositionInRange(query);
- }
+#warning TODO extract the needed data from query
}
-void QAndroidInputContext::updateCursorPositionInRange(const QSharedPointer<QInputMethodQueryEvent> &query)
-{
- QObject *input = qGuiApp->focusObject();
- QList<QInputMethodEvent::Attribute> attributes;
- attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor,
- query->value(Qt::ImCursorPosition).toInt(), 1));
-
- QInputMethodEvent event(QString(), attributes);
- QCoreApplication::sendEvent(input, &event);
-
- QtAndroidInput::updateSelection(query->value(Qt::ImCursorPosition).toInt(),
- query->value(Qt::ImCursorPosition).toInt(), 0,
- query->value(Qt::ImSurroundingText).toString().length());
-}
-
-
void QAndroidInputContext::invokeAction(QInputMethod::Action action, int cursorPosition)
{
#warning TODO Handle at least QInputMethod::ContextMenu action
@@ -950,49 +916,16 @@ void QAndroidInputContext::showInputPanel()
if (query.isNull())
return;
- QRect rect = cursorRect();
- if (!isInputPanelVisible())
- QtAndroidInput::showSoftwareKeyboard(rect.left(), rect.top(), rect.width(), rect.height(),
- screenInputItemRectangle().height(),
- query->value(Qt::ImHints).toUInt(),
- query->value(Qt::ImEnterKeyType).toUInt());
-}
-
-QRect QAndroidInputContext::cursorRect()
-{
- QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
- // if single line, we do not want to mess with the editor's position, as we do not
- // have to follow the cursor in vertical axis
- if (query.isNull()
- || (query->value(Qt::ImHints).toUInt() & Qt::ImhMultiLine) != Qt::ImhMultiLine)
- return {};
-
- auto im = qGuiApp->inputMethod();
- if (!im)
- return {};
+ disconnect(m_updateCursorPosConnection);
+ if (qGuiApp->focusObject()->metaObject()->indexOfSignal("cursorPositionChanged(int,int)") >= 0) // QLineEdit breaks the pattern
+ m_updateCursorPosConnection = connect(qGuiApp->focusObject(), SIGNAL(cursorPositionChanged(int,int)), this, SLOT(updateCursorPosition()));
+ else
+ m_updateCursorPosConnection = connect(qGuiApp->focusObject(), SIGNAL(cursorPositionChanged()), this, SLOT(updateCursorPosition()));
- const auto cursorRect= im->cursorRectangle().toRect();
- QRect finalRect(inputItemRectangle().toRect());
- const QWindow *window = qGuiApp->focusWindow();
- const double pd = window
- ? QHighDpiScaling::factor(window)
- : QHighDpiScaling::factor(QtAndroid::androidPlatformIntegration()->screen());
- finalRect.setY(cursorRect.y() * pd);
- finalRect.setHeight(cursorRect.height() * pd);
- //fiddle a bit with vert margins, so the tracking rectangle is not too tight.
- finalRect += QMargins(0, cursorRect.height() / 4, 0, cursorRect.height() / 4);
- return finalRect;
-}
-
-void QAndroidInputContext::updateInputItemRectangle()
-{
- QRect rect = cursorRect();
-
- if (!rect.isValid())
- return;
- QtAndroidInput::updateInputItemRectangle(rect.left(), rect.top(),
- rect.width(), rect.height());
- updateSelectionHandles();
+ QRect rect = screenInputItemRectangle();
+ QtAndroidInput::showSoftwareKeyboard(rect.left(), rect.top(), rect.width(), rect.height(),
+ query->value(Qt::ImHints).toUInt(),
+ query->value(Qt::ImEnterKeyType).toUInt());
}
void QAndroidInputContext::showInputPanelLater(Qt::ApplicationState state)
@@ -1096,7 +1029,7 @@ jboolean QAndroidInputContext::deleteSurroundingText(jint leftLength, jint right
absolutely not what Android's native EditText does. It deletes leftLength characters before
min(selection start, composing region start) and rightLength characters after max(selection
end, composing region end). There are no known keyboards that depend on this behavior, but
- it is better to be consistent with EditText behavior, because there definetly should be no
+ it is better to be consistent with EditText behavior, because there definitely should be no
keyboards that depend on documented behavior.
*/
const int leftEnd =
@@ -1254,13 +1187,21 @@ bool QAndroidInputContext::focusObjectStopComposing()
m_composingCursor = -1;
- // Moving Qt's cursor to where the preedit cursor used to be
- QList<QInputMethodEvent::Attribute> attributes;
- attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Selection, localCursorPos, 0));
-
- QInputMethodEvent event(QString(), attributes);
- event.setCommitString(m_composingText);
- sendInputMethodEvent(&event);
+ {
+ // commit the composing test
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent event(QString(), attributes);
+ event.setCommitString(m_composingText);
+ sendInputMethodEvent(&event);
+ }
+ {
+ // Moving Qt's cursor to where the preedit cursor used to be
+ QList<QInputMethodEvent::Attribute> attributes;
+ attributes.append(
+ QInputMethodEvent::Attribute(QInputMethodEvent::Selection, localCursorPos, 0));
+ QInputMethodEvent event(QString(), attributes);
+ sendInputMethodEvent(&event);
+ }
return true;
}
diff --git a/src/plugins/platforms/android/qandroidinputcontext.h b/src/plugins/platforms/android/qandroidinputcontext.h
index 521061b02c..e9bfb98e66 100644
--- a/src/plugins/platforms/android/qandroidinputcontext.h
+++ b/src/plugins/platforms/android/qandroidinputcontext.h
@@ -138,7 +138,6 @@ public:
public slots:
void safeCall(const std::function<void()> &func, Qt::ConnectionType conType = Qt::BlockingQueuedConnection);
void updateCursorPosition();
- void updateInputItemRectangle();
void updateSelectionHandles();
void handleLocationChanged(int handleId, int x, int y);
void touchDown(int x, int y);
@@ -155,14 +154,13 @@ private:
bool focusObjectIsComposing() const;
void focusObjectStartComposing();
bool focusObjectStopComposing();
- QRect cursorRect();
- void updateCursorPositionInRange(const QSharedPointer<QInputMethodQueryEvent> &query);
private:
ExtractedText m_extractedText;
QString m_composingText;
int m_composingTextStart;
int m_composingCursor;
+ QMetaObject::Connection m_updateCursorPosConnection;
HandleModes m_handleMode;
int m_batchEditNestingLevel;
QObject *m_focusObject;
diff --git a/src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp b/src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp
index 7741fc755d..1ec5867a38 100644
--- a/src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp
+++ b/src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp
@@ -46,6 +46,7 @@
#include <QMimeDatabase>
#include <QMimeType>
#include <QRegularExpression>
+#include <QUrl>
QT_BEGIN_NAMESPACE
@@ -119,7 +120,7 @@ void QAndroidPlatformFileDialogHelper::takePersistableUriPermission(const QJniOb
uri.object(), modeFlags);
}
-void QAndroidPlatformFileDialogHelper::setIntentTitle(const QString &title)
+void QAndroidPlatformFileDialogHelper::setInitialFileName(const QString &title)
{
const QJniObject extraTitle = QJniObject::getStaticObjectField(
JniIntentClass, "EXTRA_TITLE", "Ljava/lang/String;");
@@ -209,6 +210,12 @@ bool QAndroidPlatformFileDialogHelper::show(Qt::WindowFlags windowFlags, Qt::Win
if (options()->acceptMode() == QFileDialogOptions::AcceptSave) {
m_intent = getFileDialogIntent("ACTION_CREATE_DOCUMENT");
+ const QList<QUrl> selectedFiles = options()->initiallySelectedFiles();
+ if (selectedFiles.size() > 0) {
+ // TODO: The initial folder to show at the start should be handled by EXTRA_INITIAL_URI
+ // Take only the file name.
+ setInitialFileName(selectedFiles.first().fileName());
+ }
} else if (options()->acceptMode() == QFileDialogOptions::AcceptOpen) {
switch (options()->fileMode()) {
case QFileDialogOptions::FileMode::DirectoryOnly:
@@ -232,8 +239,6 @@ bool QAndroidPlatformFileDialogHelper::show(Qt::WindowFlags windowFlags, Qt::Win
setMimeTypes();
}
- setIntentTitle(options()->windowTitle());
-
QtAndroidPrivate::registerActivityResultListener(this);
m_activity.callMethod<void>("startActivityForResult", "(Landroid/content/Intent;I)V",
m_intent.object(), REQUEST_CODE);
diff --git a/src/plugins/platforms/android/qandroidplatformfiledialoghelper.h b/src/plugins/platforms/android/qandroidplatformfiledialoghelper.h
index 2d3cb49a96..f573e72fef 100644
--- a/src/plugins/platforms/android/qandroidplatformfiledialoghelper.h
+++ b/src/plugins/platforms/android/qandroidplatformfiledialoghelper.h
@@ -76,7 +76,7 @@ public:
private:
QJniObject getFileDialogIntent(const QString &intentType);
void takePersistableUriPermission(const QJniObject &uri);
- void setIntentTitle(const QString &title);
+ void setInitialFileName(const QString &title);
void setOpenableCategory();
void setAllowMultipleSelections(bool allowMultiple);
void setMimeTypes();
diff --git a/src/plugins/platforms/android/qandroidplatformscreen.cpp b/src/plugins/platforms/android/qandroidplatformscreen.cpp
index 6a83827262..1463351c77 100644
--- a/src/plugins/platforms/android/qandroidplatformscreen.cpp
+++ b/src/plugins/platforms/android/qandroidplatformscreen.cpp
@@ -158,7 +158,7 @@ void QAndroidPlatformScreen::addWindow(QAndroidPlatformWindow *window)
}
QWindow *w = topWindow();
- QWindowSystemInterface::handleWindowActivated(w);
+ QWindowSystemInterface::handleWindowActivated(w, Qt::ActiveWindowFocusReason);
topWindowChanged(w);
}
@@ -178,7 +178,7 @@ void QAndroidPlatformScreen::removeWindow(QAndroidPlatformWindow *window)
}
QWindow *w = topWindow();
- QWindowSystemInterface::handleWindowActivated(w);
+ QWindowSystemInterface::handleWindowActivated(w, Qt::ActiveWindowFocusReason);
topWindowChanged(w);
}
@@ -195,7 +195,7 @@ void QAndroidPlatformScreen::raise(QAndroidPlatformWindow *window)
setDirty(window->geometry());
}
QWindow *w = topWindow();
- QWindowSystemInterface::handleWindowActivated(w);
+ QWindowSystemInterface::handleWindowActivated(w, Qt::ActiveWindowFocusReason);
topWindowChanged(w);
}
@@ -212,7 +212,7 @@ void QAndroidPlatformScreen::lower(QAndroidPlatformWindow *window)
setDirty(window->geometry());
}
QWindow *w = topWindow();
- QWindowSystemInterface::handleWindowActivated(w);
+ QWindowSystemInterface::handleWindowActivated(w, Qt::ActiveWindowFocusReason);
topWindowChanged(w);
}
diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
index c32b8d9c30..8e5ecd0f69 100644
--- a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
+++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
@@ -75,6 +75,7 @@
#include "qcocoaapplicationdelegate.h"
#include "qcocoaintegration.h"
+#include "qcocoamenubar.h"
#include "qcocoamenu.h"
#include "qcocoamenuloader.h"
#include "qcocoamenuitem.h"
@@ -230,6 +231,8 @@ QT_USE_NAMESPACE
// (See the activateIgnoringOtherApps docs.)
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
}
+
+ QCocoaMenuBar::insertWindowMenu();
}
- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames
@@ -359,7 +362,7 @@ QT_USE_NAMESPACE
return item.enabled; // FIXME Test with with Qt as plugin or embedded QWindow.
auto *platformItem = nativeItem.platformMenuItem;
- if (!platformItem) // Try a bit harder with orphan menu itens
+ if (!platformItem) // Try a bit harder with orphan menu items
return item.hasSubmenu || (item.enabled && (item.action != @selector(qt_itemFired:)));
// Menu-holding items are always enabled, as it's conventional in Cocoa
diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.h b/src/plugins/platforms/cocoa/qcocoabackingstore.h
index b96bb1e3dc..eca595b23f 100644
--- a/src/plugins/platforms/cocoa/qcocoabackingstore.h
+++ b/src/plugins/platforms/cocoa/qcocoabackingstore.h
@@ -51,7 +51,7 @@
QT_BEGIN_NAMESPACE
-class QCocoaBackingStore : public QRasterBackingStore
+class QCocoaBackingStore : public QPlatformBackingStore
{
protected:
QCocoaBackingStore(QWindow *window);
@@ -85,7 +85,6 @@ private:
bool eventFilter(QObject *watched, QEvent *event) override;
QSize m_requestedSize;
- QRegion m_paintedRegion;
class GraphicsBuffer : public QIOSurfaceGraphicsBuffer
{
@@ -96,15 +95,21 @@ private:
QRegion dirtyRegion; // In unscaled coordinates
QImage *asImage();
qreal devicePixelRatio() const { return m_devicePixelRatio; }
+ bool isDirty() const { return !dirtyRegion.isEmpty(); }
+ QRegion validRegion() const;
private:
qreal m_devicePixelRatio;
QImage m_image;
};
+ void updateDirtyStates(const QRegion &paintedRegion);
+
void ensureBackBuffer();
bool recreateBackBufferIfNeeded();
- bool prepareForFlush();
+ void finalizeBackBuffer();
+
+ void preserveFromFrontBuffer(const QRegion &region, const QPoint &offset = QPoint());
void backingPropertiesChanged();
QMacNotificationObserver m_backingPropertiesObserver;
diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm
index 01787da1af..d5db3adccd 100644
--- a/src/plugins/platforms/cocoa/qcocoabackingstore.mm
+++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm
@@ -52,7 +52,7 @@
QT_BEGIN_NAMESPACE
QCocoaBackingStore::QCocoaBackingStore(QWindow *window)
- : QRasterBackingStore(window)
+ : QPlatformBackingStore(window)
{
}
@@ -67,7 +67,9 @@ QCFType<CGColorSpaceRef> QCocoaBackingStore::colorSpace() const
QCALayerBackingStore::QCALayerBackingStore(QWindow *window)
: QCocoaBackingStore(window)
{
- qCDebug(lcQpaBackingStore) << "Creating QCALayerBackingStore for" << window;
+ qCDebug(lcQpaBackingStore) << "Creating QCALayerBackingStore for" << window
+ << "with" << window->format();
+
m_buffers.resize(1);
observeBackingPropertiesChanges();
@@ -138,7 +140,8 @@ void QCALayerBackingStore::beginPaint(const QRegion &region)
painter.fillRect(rect, Qt::transparent);
}
- m_paintedRegion += region;
+ // We assume the client is going to paint the entire region
+ updateDirtyStates(region);
}
void QCALayerBackingStore::ensureBackBuffer()
@@ -146,13 +149,6 @@ void QCALayerBackingStore::ensureBackBuffer()
if (window()->format().swapBehavior() == QSurfaceFormat::SingleBuffer)
return;
- // The current back buffer may have been assigned to a layer in a previous flush,
- // but we deferred the swap. Do it now if the surface has been picked up by CA.
- if (m_buffers.back() && m_buffers.back()->isInUse() && m_buffers.back() != m_buffers.front()) {
- qCInfo(lcQpaBackingStore) << "Back buffer has been picked up by CA, swapping to front";
- std::swap(m_buffers.back(), m_buffers.front());
- }
-
if (Q_UNLIKELY(lcQpaBackingStore().isDebugEnabled())) {
// ┌───────┬───────┬───────┬─────┬──────┐
// │ front ┊ spare ┊ spare ┊ ... ┊ back │
@@ -247,8 +243,11 @@ QPaintDevice *QCALayerBackingStore::paintDevice()
void QCALayerBackingStore::endPaint()
{
- qCInfo(lcQpaBackingStore) << "Paint ended with painted region" << m_paintedRegion;
+ qCInfo(lcQpaBackingStore) << "Paint ended. Back buffer valid region is now" << m_buffers.back()->validRegion();
m_buffers.back()->unlock();
+
+ // Since we can have multiple begin/endPaint rounds before a flush
+ // we defer finalizing the back buffer until its content is needed.
}
void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion &region, const QPoint &offset)
@@ -256,14 +255,23 @@ void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion &region,
Q_UNUSED(region);
Q_UNUSED(offset);
- if (!prepareForFlush())
+ if (!m_buffers.back()) {
+ qCWarning(lcQpaBackingStore) << "Tried to flush backingstore without painting to it first";
return;
+ }
+
+ finalizeBackBuffer();
if (flushedWindow != window()) {
flushSubWindow(flushedWindow);
return;
}
+ if (m_buffers.front()->isInUse() && !m_buffers.front()->isDirty()) {
+ qCInfo(lcQpaBackingStore) << "Asked to flush, but front buffer is up to date. Ignoring.";
+ return;
+ }
+
QMacAutoReleasePool pool;
NSView *flushedView = static_cast<QCocoaWindow *>(flushedWindow->handle())->view();
@@ -287,16 +295,6 @@ void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion &region,
const bool isSingleBuffered = window()->format().swapBehavior() == QSurfaceFormat::SingleBuffer;
id backBufferSurface = (__bridge id)m_buffers.back()->surface();
- if (!isSingleBuffered && flushedView.layer.contents == backBufferSurface) {
- // We've managed to paint to the back buffer again before Core Animation had time
- // to flush the transaction and persist the layer changes to the window server, or
- // we've been asked to flush without painting anything. The layer already knows about
- // the back buffer, and we don't need to re-apply it to pick up any possible surface
- // changes, so bail out early.
- qCInfo(lcQpaBackingStore).nospace() << "Skipping flush of " << flushedView
- << ", layer already reflects back buffer";
- return;
- }
// Trigger a new display cycle if there isn't one. This ensures that our layer updates
// are committed as part of a display-cycle instead of on the next runloop pass. This
@@ -315,14 +313,17 @@ void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion &region,
flushedView.layer.contents = backBufferSurface;
- // Since we may receive multiple flushes before a new frame is started, we do not
- // swap any buffers just yet. Instead we check in the next beginPaint if the layer's
- // surface is in use, and if so swap to an unused surface as the new back buffer.
+ if (!isSingleBuffered) {
+ // Mark the surface as in use, so that we don't end up rendering
+ // to it while it's assigned to a layer.
+ IOSurfaceIncrementUseCount(m_buffers.back()->surface());
- // Note: Ideally CoreAnimation would mark a surface as in use the moment we assign
- // it to a layer, but as that's not the case we may end up painting to the same back
- // buffer once more if we are painting faster than CA can ship the surfaces over to
- // the window server.
+ if (m_buffers.back() != m_buffers.front()) {
+ qCInfo(lcQpaBackingStore) << "Swapping back buffer to front";
+ std::swap(m_buffers.back(), m_buffers.front());
+ IOSurfaceDecrementUseCount(m_buffers.back()->surface());
+ }
+ }
}
void QCALayerBackingStore::flushSubWindow(QWindow *subWindow)
@@ -376,8 +377,12 @@ void QCALayerBackingStore::windowDestroyed(QObject *object)
void QCALayerBackingStore::composeAndFlush(QWindow *window, const QRegion &region, const QPoint &offset,
QPlatformTextureList *textures, bool translucentBackground)
{
- if (!prepareForFlush())
+ if (!m_buffers.back()) {
+ qCWarning(lcQpaBackingStore) << "Tried to flush backingstore without painting to it first";
return;
+ }
+
+ finalizeBackBuffer();
QPlatformBackingStore::composeAndFlush(window, region, offset, textures, translucentBackground);
}
@@ -385,9 +390,11 @@ void QCALayerBackingStore::composeAndFlush(QWindow *window, const QRegion &regio
QImage QCALayerBackingStore::toImage() const
{
- if (!const_cast<QCALayerBackingStore*>(this)->prepareForFlush())
+ if (!m_buffers.back())
return QImage();
+ const_cast<QCALayerBackingStore*>(this)->finalizeBackBuffer();
+
// 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
@@ -421,69 +428,77 @@ QPlatformGraphicsBuffer *QCALayerBackingStore::graphicsBuffer() const
return m_buffers.back().get();
}
-bool QCALayerBackingStore::prepareForFlush()
+void QCALayerBackingStore::updateDirtyStates(const QRegion &paintedRegion)
{
- if (!m_buffers.back()) {
- qCWarning(lcQpaBackingStore) << "Tried to flush backingstore without painting to it first";
- return false;
- }
-
// Update dirty state of buffers based on what was painted. The back buffer will be
// less dirty, since we painted to it, while other buffers will become more dirty.
// This allows us to minimize copies between front and back buffers on swap in the
// cases where the painted region overlaps with the previous frame (front buffer).
for (const auto &buffer : m_buffers) {
if (buffer == m_buffers.back())
- buffer->dirtyRegion -= m_paintedRegion;
+ buffer->dirtyRegion -= paintedRegion;
else
- buffer->dirtyRegion += m_paintedRegion;
+ buffer->dirtyRegion += paintedRegion;
}
+}
+void QCALayerBackingStore::finalizeBackBuffer()
+{
// After painting, the back buffer is only guaranteed to have content for the painted
// region, and may still have dirty areas that need to be synced up with the front buffer,
// if we have one. We know that the front buffer is always up to date.
- if (!m_buffers.back()->dirtyRegion.isEmpty() && m_buffers.front() != m_buffers.back()) {
- QRegion preserveRegion = m_buffers.back()->dirtyRegion;
- qCDebug(lcQpaBackingStore) << "Preserving" << preserveRegion << "from front to back buffer";
- m_buffers.front()->lock(QPlatformGraphicsBuffer::SWReadAccess);
- const QImage *frontBuffer = m_buffers.front()->asImage();
+ if (!m_buffers.back()->isDirty())
+ return;
- const QRect frontSurfaceBounds(QPoint(0, 0), m_buffers.front()->size());
- const qreal sourceDevicePixelRatio = frontBuffer->devicePixelRatio();
+ m_buffers.back()->lock(QPlatformGraphicsBuffer::SWWriteAccess);
+ preserveFromFrontBuffer(m_buffers.back()->dirtyRegion);
+ m_buffers.back()->unlock();
- m_buffers.back()->lock(QPlatformGraphicsBuffer::SWWriteAccess);
- QPainter painter(m_buffers.back()->asImage());
- painter.setCompositionMode(QPainter::CompositionMode_Source);
+ // The back buffer is now completely in sync, ready to be presented
+ m_buffers.back()->dirtyRegion = QRegion();
+}
- // Let painter operate in device pixels, to make it easier to compare coordinates
- const qreal targetDevicePixelRatio = painter.device()->devicePixelRatio();
- painter.scale(1.0 / targetDevicePixelRatio, 1.0 / targetDevicePixelRatio);
+void QCALayerBackingStore::preserveFromFrontBuffer(const QRegion &region, const QPoint &offset)
+{
- for (const QRect &rect : preserveRegion) {
- QRect sourceRect(rect.topLeft() * sourceDevicePixelRatio, rect.size() * sourceDevicePixelRatio);
- QRect targetRect(rect.topLeft() * targetDevicePixelRatio, rect.size() * targetDevicePixelRatio);
+ if (m_buffers.front() == m_buffers.back())
+ return; // Nothing to preserve from
-#ifdef QT_DEBUG
- if (Q_UNLIKELY(!frontSurfaceBounds.contains(sourceRect.bottomRight()))) {
- qCWarning(lcQpaBackingStore) << "Front buffer too small to preserve"
- << QRegion(sourceRect).subtracted(frontSurfaceBounds);
- }
-#endif
- painter.drawImage(targetRect, *frontBuffer, sourceRect);
- }
+ qCDebug(lcQpaBackingStore) << "Preserving" << region << "of front buffer to"
+ << region.translated(offset) << "of back buffer";
- m_buffers.back()->unlock();
- m_buffers.front()->unlock();
+ Q_ASSERT(m_buffers.back()->isLocked() == QPlatformGraphicsBuffer::SWWriteAccess);
- // The back buffer is now completely in sync, ready to be presented
- m_buffers.back()->dirtyRegion = QRegion();
- }
+ m_buffers.front()->lock(QPlatformGraphicsBuffer::SWReadAccess);
+ const QImage *frontBuffer = m_buffers.front()->asImage();
- // Prepare for another round of painting
- m_paintedRegion = QRegion();
+ const QRect frontSurfaceBounds(QPoint(0, 0), m_buffers.front()->size());
+ const qreal sourceDevicePixelRatio = frontBuffer->devicePixelRatio();
- return true;
+ QPainter painter(m_buffers.back()->asImage());
+ painter.setCompositionMode(QPainter::CompositionMode_Source);
+
+ // Let painter operate in device pixels, to make it easier to compare coordinates
+ const qreal targetDevicePixelRatio = painter.device()->devicePixelRatio();
+ painter.scale(1.0 / targetDevicePixelRatio, 1.0 / targetDevicePixelRatio);
+
+ for (const QRect &rect : region) {
+ QRect sourceRect(rect.topLeft() * sourceDevicePixelRatio,
+ rect.size() * sourceDevicePixelRatio);
+ QRect targetRect((rect.topLeft() + offset) * targetDevicePixelRatio,
+ rect.size() * targetDevicePixelRatio);
+
+#ifdef QT_DEBUG
+ if (Q_UNLIKELY(!frontSurfaceBounds.contains(sourceRect.bottomRight()))) {
+ qCWarning(lcQpaBackingStore) << "Front buffer too small to preserve"
+ << QRegion(sourceRect).subtracted(frontSurfaceBounds);
+ }
+#endif
+ painter.drawImage(targetRect, *frontBuffer, sourceRect);
+ }
+
+ m_buffers.front()->unlock();
}
// ----------------------------------------------------------------------------
@@ -491,12 +506,19 @@ bool QCALayerBackingStore::prepareForFlush()
QCALayerBackingStore::GraphicsBuffer::GraphicsBuffer(const QSize &size, qreal devicePixelRatio,
const QPixelFormat &format, QCFType<CGColorSpaceRef> colorSpace)
: QIOSurfaceGraphicsBuffer(size, format)
- , dirtyRegion(0, 0, size.width() / devicePixelRatio, size.height() / devicePixelRatio)
+ , dirtyRegion(QRect(QPoint(0, 0), size / devicePixelRatio))
, m_devicePixelRatio(devicePixelRatio)
{
setColorSpace(colorSpace);
}
+QRegion QCALayerBackingStore::GraphicsBuffer::validRegion() const
+{
+
+ QRegion fullRegion = QRect(QPoint(0, 0), size() / m_devicePixelRatio);
+ return fullRegion - dirtyRegion;
+}
+
QImage *QCALayerBackingStore::GraphicsBuffer::asImage()
{
if (m_image.isNull()) {
diff --git a/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm b/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm
index 4e8b87c41c..b21b7c2cc9 100644
--- a/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm
+++ b/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm
@@ -236,6 +236,11 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSColorPanelDelegate);
[NSApp runModalForWindow:mColorPanel];
mDialogIsExecuting = false;
+
+ // Wake up the event dispatcher so it can check whether the
+ // current event loop should continue spinning or not.
+ QCoreApplication::eventDispatcher()->wakeUp();
+
return (mResultCode == NSModalResponseOK);
}
diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm
index 042abae1ca..423a8a1ccd 100644
--- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm
+++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm
@@ -688,7 +688,7 @@ bool QCocoaEventDispatcherPrivate::hasModalSession() const
void QCocoaEventDispatcherPrivate::cleanupModalSessions()
{
// Go through the list of modal sessions, and end those
- // that no longer has a window assosiated; no window means
+ // that no longer has a window associated; no window means
// the session has logically ended. The reason we wait like
// this to actually end the sessions for real (rather than at the
// point they were marked as stopped), is that ending a session
diff --git a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm
index e84d50d729..8bd649e461 100644
--- a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm
+++ b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm
@@ -206,6 +206,10 @@ typedef QSharedPointer<QFileDialogOptions> SharedPointerFileDialogOptions;
auto result = [m_panel runModal];
m_helper->panelClosed(result);
+
+ // Wake up the event dispatcher so it can check whether the
+ // current event loop should continue spinning or not.
+ QCoreApplication::eventDispatcher()->wakeUp();
}
- (void)closePanel
diff --git a/src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm b/src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm
index 4fa8ea2721..355d658242 100644
--- a/src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm
+++ b/src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm
@@ -218,6 +218,11 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSFontPanelDelegate);
[NSApp runModalForWindow:mFontPanel];
mDialogIsExecuting = false;
+
+ // Wake up the event dispatcher so it can check whether the
+ // current event loop should continue spinning or not.
+ QCoreApplication::eventDispatcher()->wakeUp();
+
return (mResultCode == NSModalResponseOK);
}
diff --git a/src/plugins/platforms/cocoa/qcocoaglcontext.mm b/src/plugins/platforms/cocoa/qcocoaglcontext.mm
index 40a7a6ef5e..026a99a152 100644
--- a/src/plugins/platforms/cocoa/qcocoaglcontext.mm
+++ b/src/plugins/platforms/cocoa/qcocoaglcontext.mm
@@ -422,6 +422,15 @@ bool QCocoaGLContext::setDrawable(QPlatformSurface *surface)
m_updateObservers.append(QMacNotificationObserver([NSApplication sharedApplication],
NSApplicationDidChangeScreenParametersNotification, updateCallback));
+ m_updateObservers.append(QMacNotificationObserver(view,
+ QCocoaWindowWillReleaseQNSViewNotification, [this, view] {
+ if (QT_IGNORE_DEPRECATIONS(m_context.view) != view)
+ return;
+ qCDebug(lcQpaOpenGLContext) << view << "about to be released."
+ << "Clearing current drawable for" << m_context;
+ [m_context clearDrawable];
+ }));
+
// If any of the observers fire at this point it's fine. We check the
// view association (atomically) in the update callback, and skip the
// update if we haven't associated yet. Setting the drawable below will
diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm
index deede36ac0..2578fb2140 100644
--- a/src/plugins/platforms/cocoa/qcocoahelpers.mm
+++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm
@@ -157,7 +157,7 @@ Qt::DropActions qt_mac_mapNSDragOperations(NSDragOperation nsActions)
that the platform window is not a foreign window before using
this cast, via QPlatformWindow::isForeignWindow().
- Do not use this method soley to check for foreign windows, as
+ Do not use this method solely to check for foreign windows, as
that will make the code harder to read for people not working
primarily on macOS, who do not know the difference between the
NSView and QNSView cases.
diff --git a/src/plugins/platforms/cocoa/qcocoainputcontext.h b/src/plugins/platforms/cocoa/qcocoainputcontext.h
index 2b97ab6a82..3a8322461c 100644
--- a/src/plugins/platforms/cocoa/qcocoainputcontext.h
+++ b/src/plugins/platforms/cocoa/qcocoainputcontext.h
@@ -59,6 +59,7 @@ public:
void setFocusObject(QObject *object) override;
+ void commit() override;
void reset() override;
QLocale locale() const override { return m_locale; }
diff --git a/src/plugins/platforms/cocoa/qcocoainputcontext.mm b/src/plugins/platforms/cocoa/qcocoainputcontext.mm
index d7fd6e8998..63d23b0f43 100644
--- a/src/plugins/platforms/cocoa/qcocoainputcontext.mm
+++ b/src/plugins/platforms/cocoa/qcocoainputcontext.mm
@@ -96,6 +96,33 @@ QCocoaInputContext::~QCocoaInputContext()
}
/*!
+ Commits the current composition if there is one,
+ by "unmarking" the text in the edit buffer, and
+ informing the system input context of this fact.
+*/
+void QCocoaInputContext::commit()
+{
+ qCDebug(lcQpaInputMethods) << "Committing composition";
+
+ if (!m_focusWindow)
+ return;
+
+ auto *platformWindow = m_focusWindow->handle();
+ if (!platformWindow)
+ return;
+
+ auto *cocoaWindow = static_cast<QCocoaWindow *>(platformWindow);
+ QNSView *view = qnsview_cast(cocoaWindow->view());
+ if (!view)
+ return;
+
+ QMacAutoReleasePool pool;
+ [view unmarkText];
+ [view.inputContext discardMarkedText];
+}
+
+
+/*!
\brief Cancels a composition.
*/
void QCocoaInputContext::reset()
diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm
index b5d9d48a8a..b8954509b9 100644
--- a/src/plugins/platforms/cocoa/qcocoaintegration.mm
+++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm
@@ -163,7 +163,7 @@ QCocoaIntegration::QCocoaIntegration(const QStringList &paramList)
if (qEnvironmentVariableIsEmpty("QT_MAC_DISABLE_FOREGROUND_APPLICATION_TRANSFORM")) {
// Applications launched from plain executables (without an app
- // bundle) are "background" applications that does not take keybaord
+ // bundle) are "background" applications that does not take keyboard
// focus or have a dock icon or task switcher entry. Qt Gui apps generally
// wants to be foreground applications so change the process type. (But
// see the function implementation for exceptions.)
diff --git a/src/plugins/platforms/cocoa/qcocoamenu.mm b/src/plugins/platforms/cocoa/qcocoamenu.mm
index d6af2a5523..5023afa3a6 100644
--- a/src/plugins/platforms/cocoa/qcocoamenu.mm
+++ b/src/plugins/platforms/cocoa/qcocoamenu.mm
@@ -324,7 +324,7 @@ void QCocoaMenu::syncSeparatorsCollapsible(bool enable)
if (!item->isSeparator())
continue;
- // sync the visiblity directly
+ // sync the visibility directly
item->sync();
}
}
diff --git a/src/plugins/platforms/cocoa/qcocoamenubar.h b/src/plugins/platforms/cocoa/qcocoamenubar.h
index 7186e48829..6f8409434c 100644
--- a/src/plugins/platforms/cocoa/qcocoamenubar.h
+++ b/src/plugins/platforms/cocoa/qcocoamenubar.h
@@ -67,6 +67,7 @@ public:
NSMenu *nsMenu() const override { return m_nativeMenu; }
static void updateMenuBarImmediately();
+ static void insertWindowMenu();
QList<QCocoaMenuItem*> merged() const;
NSMenuItem *itemForRole(QPlatformMenuItem::MenuRole role);
@@ -80,12 +81,14 @@ private:
bool needsImmediateUpdate();
bool shouldDisable(QCocoaWindow *active) const;
+ void insertDefaultEditItems(QCocoaMenu *menu);
NSMenuItem *nativeItemForMenu(QCocoaMenu *menu) const;
QList<QPointer<QCocoaMenu> > m_menus;
NSMenu *m_nativeMenu;
QPointer<QCocoaWindow> m_window;
+ QList<QPointer<QCocoaMenuItem>> m_defaultEditMenuItems;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qcocoamenubar.mm b/src/plugins/platforms/cocoa/qcocoamenubar.mm
index a2a8535547..365346ac8c 100644
--- a/src/plugins/platforms/cocoa/qcocoamenubar.mm
+++ b/src/plugins/platforms/cocoa/qcocoamenubar.mm
@@ -194,9 +194,14 @@ void QCocoaMenuBar::syncMenu_helper(QPlatformMenu *menu, bool menubarUpdate)
for (QCocoaMenuItem *item : cocoaMenu->items())
cocoaMenu->syncMenuItem_helper(item, menubarUpdate);
+ const QString captionNoAmpersand = QString::fromNSString(cocoaMenu->nsMenu().title)
+ .remove(QLatin1Char('&'));
+ if (captionNoAmpersand == QCoreApplication::translate("QCocoaMenu", "Edit"))
+ insertDefaultEditItems(cocoaMenu);
+
BOOL shouldHide = YES;
if (cocoaMenu->isVisible()) {
- // If the NSMenu has no visble items, or only separators, we should hide it
+ // If the NSMenu has no visible items, or only separators, we should hide it
// on the menubar. This can happen after syncing the menu items since they
// can be moved to other menus.
for (NSMenuItem *item in cocoaMenu->nsMenu().itemArray)
@@ -328,9 +333,36 @@ void QCocoaMenuBar::updateMenuBarImmediately()
[mergedItems release];
[NSApp setMainMenu:mb->nsMenu()];
+ insertWindowMenu();
[loader qtTranslateApplicationMenu];
}
+void QCocoaMenuBar::insertWindowMenu()
+{
+ // For such an item/menu we get for 'free' an additional feature -
+ // a list of windows the application has created in the Dock's menu.
+
+ NSApplication *app = NSApplication.sharedApplication;
+ if (app.windowsMenu)
+ return;
+
+ NSMenu *mainMenu = app.mainMenu;
+ NSMenuItem *winMenuItem = [[[NSMenuItem alloc] initWithTitle:@"QtWindowMenu"
+ action:nil keyEquivalent:@""] autorelease];
+ // We don't want to show this menu, nobody asked us to do so:
+ winMenuItem.hidden = YES;
+
+ winMenuItem.submenu = [[[NSMenu alloc] initWithTitle:@"QtWindowMenu"] autorelease];
+ [mainMenu insertItem:winMenuItem atIndex:mainMenu.itemArray.count];
+ app.windowsMenu = winMenuItem.submenu;
+
+ // Windows, created and 'ordered front' before, will not be in this menu:
+ for (NSWindow *win in app.windows) {
+ if (win.title && ![win.title isEqualToString:@""])
+ [app addWindowsItem:win title:win.title filename:NO];
+ }
+}
+
QList<QCocoaMenuItem*> QCocoaMenuBar::merged() const
{
QList<QCocoaMenuItem*> r;
@@ -400,6 +432,48 @@ QCocoaWindow *QCocoaMenuBar::cocoaWindow() const
return m_window.data();
}
+void QCocoaMenuBar::insertDefaultEditItems(QCocoaMenu *menu)
+{
+ if (menu->items().isEmpty())
+ return;
+
+ NSMenu *nsEditMenu = menu->nsMenu();
+ if ([nsEditMenu itemAtIndex:nsEditMenu.numberOfItems - 1].action
+ == @selector(orderFrontCharacterPalette:)) {
+ for (auto defaultEditMenuItem : qAsConst(m_defaultEditMenuItems)) {
+ if (menu->items().contains(defaultEditMenuItem))
+ menu->removeMenuItem(defaultEditMenuItem);
+ }
+ qDeleteAll(m_defaultEditMenuItems);
+ m_defaultEditMenuItems.clear();
+ } else {
+ if (m_defaultEditMenuItems.isEmpty()) {
+ QCocoaMenuItem *separator = new QCocoaMenuItem;
+ separator->setIsSeparator(true);
+
+ QCocoaMenuItem *dictationItem = new QCocoaMenuItem;
+ dictationItem->setText(QCoreApplication::translate("QCocoaMenuItem", "Start Dictation..."));
+ QObject::connect(dictationItem, &QPlatformMenuItem::activated, this, []{
+ [NSApplication.sharedApplication performSelector:@selector(startDictation:)];
+ });
+
+ QCocoaMenuItem *emojiItem = new QCocoaMenuItem;
+ emojiItem->setText(QCoreApplication::translate("QCocoaMenuItem", "Emoji && Symbols"));
+ emojiItem->setShortcut(QKeyCombination(Qt::MetaModifier|Qt::ControlModifier, Qt::Key_Space));
+ QObject::connect(emojiItem, &QPlatformMenuItem::activated, this, []{
+ [NSApplication.sharedApplication orderFrontCharacterPalette:nil];
+ });
+
+ m_defaultEditMenuItems << separator << dictationItem << emojiItem;
+ }
+ for (auto defaultEditMenuItem : qAsConst(m_defaultEditMenuItems)) {
+ if (menu->items().contains(defaultEditMenuItem))
+ menu->removeMenuItem(defaultEditMenuItem);
+ menu->insertMenuItem(defaultEditMenuItem, nullptr);
+ }
+ }
+}
+
QT_END_NAMESPACE
#include "moc_qcocoamenubar.cpp"
diff --git a/src/plugins/platforms/cocoa/qcocoascreen.mm b/src/plugins/platforms/cocoa/qcocoascreen.mm
index e419492202..82fd1c44df 100644
--- a/src/plugins/platforms/cocoa/qcocoascreen.mm
+++ b/src/plugins/platforms/cocoa/qcocoascreen.mm
@@ -552,7 +552,7 @@ QWindow *QCocoaScreen::topLevelAt(const QPoint &point) const
\internal
Coordinates are in screen coordinates if \a view is 0, otherwise they are in view
- coordiantes.
+ coordinates.
*/
QPixmap QCocoaScreen::grabWindow(WId view, int x, int y, int width, int height) const
{
diff --git a/src/plugins/platforms/cocoa/qcocoatheme.mm b/src/plugins/platforms/cocoa/qcocoatheme.mm
index 8f0f6b90d8..dfc1c8197b 100644
--- a/src/plugins/platforms/cocoa/qcocoatheme.mm
+++ b/src/plugins/platforms/cocoa/qcocoatheme.mm
@@ -209,7 +209,7 @@ static QHash<QPlatformTheme::Palette, QPalette*> qt_mac_createRolePalettes()
} else {
// selectedMenuItemColor would presumably be the correct color to use as the background
// for selected menu items. But that color is always blue, and doesn't follow the
- // appearance color in system preferences. So we therefore deliberatly choose to use
+ // appearance color in system preferences. So we therefore deliberately choose to use
// keyboardFocusIndicatorColor instead, which appears to have the same color value.
selectedMenuItemColor = [NSColor keyboardFocusIndicatorColor];
}
@@ -527,6 +527,8 @@ QVariant QCocoaTheme::themeHint(ThemeHint hint) const
return QVariant(bool([[NSApplication sharedApplication] presentationOptions] & NSApplicationPresentationFullScreen));
case QPlatformTheme::InteractiveResizeAcrossScreens:
return !NSScreen.screensHaveSeparateSpaces;
+ case QPlatformTheme::ShowDirectoriesFirst:
+ return false;
default:
break;
}
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.h b/src/plugins/platforms/cocoa/qcocoawindow.h
index a36f85d620..c0684504aa 100644
--- a/src/plugins/platforms/cocoa/qcocoawindow.h
+++ b/src/plugins/platforms/cocoa/qcocoawindow.h
@@ -55,6 +55,8 @@
#include <MoltenVK/mvk_vulkan.h>
#endif
+#include <QHash>
+
Q_FORWARD_DECLARE_OBJC_CLASS(NSWindow);
Q_FORWARD_DECLARE_OBJC_CLASS(NSView);
Q_FORWARD_DECLARE_OBJC_CLASS(NSCursor);
@@ -111,6 +113,7 @@ public:
void setGeometry(const QRect &rect) override;
QRect geometry() const override;
+ QRect normalGeometry() const override;
void setCocoaGeometry(const QRect &rect);
void setVisible(bool visible) override;
@@ -169,6 +172,8 @@ public:
Q_NOTIFICATION_HANDLER(NSWindowDidChangeOcclusionStateNotification) void windowDidChangeOcclusionState();
Q_NOTIFICATION_HANDLER(NSWindowDidChangeScreenNotification) void windowDidChangeScreen();
+ void windowWillZoom();
+
bool windowShouldClose();
bool windowIsPopupType(Qt::WindowType type = Qt::Widget) const;
@@ -203,6 +208,8 @@ public:
QPoint bottomLeftClippedByNSWindowOffset() const override;
+ void updateNormalGeometry();
+
enum RecreationReason {
RecreationNotNeeded = 0,
ParentChanged = 0x1,
@@ -260,6 +267,7 @@ public: // for QNSView
bool m_frameStrutEventsEnabled;
QRect m_exposedRect;
+ QRect m_normalGeometry;
int m_registerTouchCount;
bool m_resizableTransientParent;
@@ -279,14 +287,16 @@ public: // for QNSView
return upper < right.upper;
}
};
- QHash<quintptr, BorderRange> m_contentBorderAreas; // identifer -> uppper/lower
- QHash<quintptr, bool> m_enabledContentBorderAreas; // identifer -> enabled state (true/false)
+ QHash<quintptr, BorderRange> m_contentBorderAreas; // identifier -> uppper/lower
+ QHash<quintptr, bool> m_enabledContentBorderAreas; // identifier -> enabled state (true/false)
#if QT_CONFIG(vulkan)
VkSurfaceKHR m_vulkanSurface = nullptr;
#endif
};
+extern const NSNotificationName QCocoaWindowWillReleaseQNSViewNotification;
+
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug debug, const QCocoaWindow *window);
#endif
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm
index 16e33ee014..70cfa42974 100644
--- a/src/plugins/platforms/cocoa/qcocoawindow.mm
+++ b/src/plugins/platforms/cocoa/qcocoawindow.mm
@@ -105,7 +105,7 @@ static void qRegisterNotificationCallbacks()
if (QNSView *qnsView = qnsview_cast(notification.object))
cocoaWindows += qnsView.platformWindow;
} else {
- qCWarning(lcCocoaNotifications) << "Unhandled notifcation"
+ qCWarning(lcCocoaNotifications) << "Unhandled notification"
<< notification.name << "for" << notification.object;
return;
}
@@ -186,6 +186,8 @@ void QCocoaWindow::initialize()
m_initialized = true;
}
+const NSNotificationName QCocoaWindowWillReleaseQNSViewNotification = @"QCocoaWindowWillReleaseQNSViewNotification";
+
QCocoaWindow::~QCocoaWindow()
{
qCDebug(lcQpaWindow) << "QCocoaWindow::~QCocoaWindow" << window();
@@ -209,6 +211,13 @@ QCocoaWindow::~QCocoaWindow()
}
#endif
+ // Must send notification before calling release, as doing it from
+ // [QNSView dealloc] would mean that any weak references to the view
+ // would already return nil.
+ [NSNotificationCenter.defaultCenter
+ postNotificationName:QCocoaWindowWillReleaseQNSViewNotification
+ object:m_view];
+
[m_view release];
[m_nsWindow close];
[m_nsWindow release];
@@ -246,7 +255,7 @@ bool QCocoaWindow::isForeignWindow() const
QRect QCocoaWindow::geometry() const
{
- // QWindows that are embedded in a NSView hiearchy may be considered
+ // QWindows that are embedded in a NSView hierarchy may be considered
// top-level from Qt's point of view but are not from Cocoa's point
// of view. Embedded QWindows get global (screen) geometry.
if (isEmbedded()) {
@@ -261,6 +270,40 @@ QRect QCocoaWindow::geometry() const
return QPlatformWindow::geometry();
}
+/*!
+ \brief the geometry of the window as it will appear when shown as
+ a normal (not maximized or full screen) top-level window.
+
+ For child windows this property always holds an empty rectangle.
+
+ \sa QWidget::normalGeometry()
+*/
+QRect QCocoaWindow::normalGeometry() const
+{
+ if (!isContentView())
+ return QRect();
+
+ // We only persist the normal the geometry when going into
+ // fullscreen and maximized states. For all other cases we
+ // can just report the geometry as is.
+
+ if (!(windowState() & (Qt::WindowFullScreen | Qt::WindowMaximized)))
+ return geometry();
+
+ return m_normalGeometry;
+}
+
+void QCocoaWindow::updateNormalGeometry()
+{
+ if (!isContentView())
+ return;
+
+ if (windowState() != Qt::WindowNoState)
+ return;
+
+ m_normalGeometry = geometry();
+}
+
void QCocoaWindow::setCocoaGeometry(const QRect &rect)
{
qCDebug(lcQpaWindow) << "QCocoaWindow::setCocoaGeometry" << window() << rect;
@@ -365,7 +408,9 @@ void QCocoaWindow::setVisible(bool visible)
// Show the window as application modal
eventDispatcher()->beginModalSession(window());
} else if (m_view.window.canBecomeKeyWindow) {
- bool shouldBecomeKeyNow = !NSApp.modalWindow || m_view.window.worksWhenModal;
+ bool shouldBecomeKeyNow = !NSApp.modalWindow
+ || m_view.window.worksWhenModal
+ || !NSApp.modalWindow.visible;
// Panels with becomesKeyOnlyIfNeeded set should not activate until a view
// with needsPanelToBecomeKey, for example a line edit, is clicked.
@@ -752,6 +797,11 @@ void QCocoaWindow::toggleMaximized()
window.styleMask &= ~NSWindowStyleMaskResizable;
}
+void QCocoaWindow::windowWillZoom()
+{
+ updateNormalGeometry();
+}
+
void QCocoaWindow::toggleFullScreen()
{
const NSWindow *window = m_view.window;
@@ -770,6 +820,8 @@ void QCocoaWindow::windowWillEnterFullScreen()
if (!isContentView())
return;
+ updateNormalGeometry();
+
// The NSWindow needs to be resizable, otherwise we'll end up with
// the normal window geometry, centered in the middle of the screen
// on a black background. The styleMask will be reset below.
@@ -1113,7 +1165,7 @@ NSWindow *QCocoaWindow::nativeWindow() const
void QCocoaWindow::setEmbeddedInForeignView()
{
- // Release any previosly created NSWindow.
+ // Release any previously created NSWindow.
[m_nsWindow closeAndRelease];
m_nsWindow = 0;
}
@@ -1208,13 +1260,18 @@ void QCocoaWindow::windowDidResignKey()
if (isForeignWindow())
return;
+ // Make sure popups are closed before we deliver activation changes, which are
+ // otherwise ignored by QApplication.
+ QGuiApplicationPrivate::instance()->closeAllPopups();
+
// The current key window will be non-nil if another window became key. If that
// window is a Qt window, we delay the window activation event until the didBecomeKey
// notification is delivered to the active window, to ensure an atomic update.
NSWindow *newKeyWindow = [NSApp keyWindow];
if (newKeyWindow && newKeyWindow != m_view.window
- && [newKeyWindow conformsToProtocol:@protocol(QNSWindowProtocol)])
+ && [newKeyWindow conformsToProtocol:@protocol(QNSWindowProtocol)]) {
return;
+ }
// Lost key window, go ahead and set the active window to zero
if (!windowIsPopupType()) {
@@ -1225,12 +1282,32 @@ void QCocoaWindow::windowDidResignKey()
void QCocoaWindow::windowDidOrderOnScreen()
{
+ // The current mouse window needs to get a leave event when a popup window opens.
+ // For modal dialogs, QGuiApplicationPrivate::showModalWindow takes care of this.
+ if (QWindowPrivate::get(window())->isPopup()) {
+ QWindowSystemInterface::handleLeaveEvent<QWindowSystemInterface::SynchronousDelivery>
+ (QGuiApplicationPrivate::currentMouseWindow);
+ }
+
[m_view setNeedsDisplay:YES];
}
void QCocoaWindow::windowDidOrderOffScreen()
{
handleExposeEvent(QRegion());
+ // We are closing a window, so the window that is now under the mouse
+ // might need to get an Enter event if it isn't already the mouse window.
+ if (window()->type() & Qt::Window) {
+ const QPointF screenPoint = QCocoaScreen::mapFromNative([NSEvent mouseLocation]);
+ if (QWindow *windowUnderMouse = QGuiApplication::topLevelAt(screenPoint.toPoint())) {
+ if (windowUnderMouse != QGuiApplicationPrivate::instance()->currentMouseWindow) {
+ const auto windowPoint = windowUnderMouse->mapFromGlobal(screenPoint);
+ // asynchronous delivery on purpose
+ QWindowSystemInterface::handleEnterEvent<QWindowSystemInterface::AsynchronousDelivery>
+ (windowUnderMouse, windowPoint, screenPoint);
+ }
+ }
+ }
}
void QCocoaWindow::windowDidChangeOcclusionState()
@@ -1768,7 +1845,7 @@ void QCocoaWindow::applyContentBorderThickness(NSWindow *window)
if (!m_enabledContentBorderAreas.value(range.identifier, false))
continue;
- // Is this sub-range adjacent to or overlaping the
+ // Is this sub-range adjacent to or overlapping the
// existing total border area range? If so merge
// it into the total range,
if (range.upper <= (effectiveTopContentBorderThickness + 1))
@@ -1819,7 +1896,7 @@ bool QCocoaWindow::testContentBorderAreaPosition(int position) const
if (!m_drawContentBorderGradient || !isContentView())
return false;
- // Determine if the given y postion (relative to the content area) is inside the
+ // Determine if the given y position (relative to the content area) is inside the
// unified toolbar area. Note that the value returned by contentBorderThicknessForEdge
// includes the title bar height; subtract it.
const int contentBorderThickness = [m_view.window contentBorderThicknessForEdge:NSMaxYEdge];
diff --git a/src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.h b/src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.h
index 5d4b6d6a71..cc7193d8b7 100644
--- a/src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.h
+++ b/src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.h
@@ -43,6 +43,7 @@
#include <qpa/qplatformgraphicsbuffer.h>
#include <private/qcore_mac_p.h>
+#include <CoreGraphics/CGColorSpace.h>
#include <IOSurface/IOSurface.h>
QT_BEGIN_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qmacclipboard.mm b/src/plugins/platforms/cocoa/qmacclipboard.mm
index 35eaa8e328..23c8749b5d 100644
--- a/src/plugins/platforms/cocoa/qmacclipboard.mm
+++ b/src/plugins/platforms/cocoa/qmacclipboard.mm
@@ -191,7 +191,7 @@ OSStatus QMacPasteboard::promiseKeeper(PasteboardRef paste, PasteboardItemID id,
if (!promise.itemId) {
// There was no promise that could deliver data for the
- // given id and flavor. This should not happend.
+ // given id and flavor. This should not happen.
qDebug("Pasteboard: %d: Request for %ld, %s, but no promise found!", __LINE__, promise_id, qPrintable(flavorAsQString));
return cantGetFlavorErr;
}
diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm
index 453ef9a0cd..d3f1a211de 100644
--- a/src/plugins/platforms/cocoa/qnsview.mm
+++ b/src/plugins/platforms/cocoa/qnsview.mm
@@ -322,7 +322,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSViewMouseMoveHelper);
// the QWindow. The latter means that the QWindow should have keyboard
// focus. But those two are not necessarily the same; A tool window could e.g be
// rendered as Active while the parent window, which is also Active, has
- // input focus. But we currently don't distiguish between that cleanly in Qt.
+ // input focus. But we currently don't distinguish between that cleanly in Qt.
// Since we don't want a QWindow to be rendered as Active when the NSWindow
// it belongs to is not key, we skip calling handleWindowActivated when
// that is the case. Instead, we wait for the window to become key, and handle
diff --git a/src/plugins/platforms/cocoa/qnsview_keys.mm b/src/plugins/platforms/cocoa/qnsview_keys.mm
index d6e0c4b012..9acd5cd14a 100644
--- a/src/plugins/platforms/cocoa/qnsview_keys.mm
+++ b/src/plugins/platforms/cocoa/qnsview_keys.mm
@@ -148,7 +148,7 @@
const bool keyUpAccepted = [self handleKeyEvent:nsevent];
// Propagate the keyUp if neither Qt accepted it nor the corresponding KeyDown was
- // accepted. Qt text controls wil often not use and ignore keyUp events, but we
+ // accepted. Qt text controls will often not use and ignore keyUp events, but we
// want to avoid propagating unmatched keyUps.
const bool keyDownAccepted = m_acceptedKeyDowns.remove(nsevent.keyCode);
if (!keyUpAccepted && !keyDownAccepted)
@@ -165,8 +165,10 @@
// Handling the key event may recurse back here through interpretKeyEvents
// (when IM is enabled), so we need to guard against that.
- if (currentEvent == m_currentlyInterpretedKeyEvent)
+ if (currentEvent == m_currentlyInterpretedKeyEvent) {
+ m_sendKeyEvent = true;
return;
+ }
// Send Command+Key_Period and Escape as normal keypresses so that
// the key sequence is delivered through Qt. That way clients can
@@ -232,6 +234,9 @@
KeyEvent::KeyEvent(NSEvent *nsevent)
{
timestamp = nsevent.timestamp * 1000;
+ nativeModifiers = nsevent.modifierFlags;
+ nativeVirtualKey = nsevent.keyCode;
+ modifiers = QAppleKeyMapper::fromCocoaModifiers(nativeModifiers);
switch (nsevent.type) {
case NSEventTypeKeyDown: type = QEvent::KeyPress; break;
@@ -267,11 +272,6 @@ KeyEvent::KeyEvent(NSEvent *nsevent)
isRepeat = nsevent.ARepeat;
}
-
- nativeVirtualKey = nsevent.keyCode;
-
- nativeModifiers = nsevent.modifierFlags;
- modifiers = QAppleKeyMapper::fromCocoaModifiers(nativeModifiers);
}
bool KeyEvent::sendWindowSystemEvent(QWindow *window) const
diff --git a/src/plugins/platforms/cocoa/qnsview_mouse.mm b/src/plugins/platforms/cocoa/qnsview_mouse.mm
index 1040b66644..e689feef52 100644
--- a/src/plugins/platforms/cocoa/qnsview_mouse.mm
+++ b/src/plugins/platforms/cocoa/qnsview_mouse.mm
@@ -123,7 +123,7 @@ static const QPointingDevice *pointingDeviceFor(qint64 deviceID)
- (void)resetMouseButtons
{
- qCDebug(lcQpaMouse) << "Reseting mouse buttons";
+ qCDebug(lcQpaMouse) << "Resetting mouse buttons";
m_buttons = Qt::NoButton;
m_frameStrutButtons = Qt::NoButton;
}
@@ -429,17 +429,35 @@ static const QPointingDevice *pointingDeviceFor(qint64 deviceID)
return;
}
- if ([self hasMarkedText]) {
- [[NSTextInputContext currentInputContext] handleEvent:theEvent];
- } else {
- if (!m_dontOverrideCtrlLMB && (theEvent.modifierFlags & NSEventModifierFlagControl)) {
- m_buttons |= Qt::RightButton;
- m_sendUpAsRightButton = true;
- } else {
- m_buttons |= Qt::LeftButton;
+ // FIXME: AppKit transfers first responder to the view before calling mouseDown,
+ // whereas we only transfer focus once the mouse press is delivered, which means
+ // on first click the focus item won't be the correct one when transferring focus.
+ auto *focusObject = m_platformWindow->window()->focusObject();
+ if (queryInputMethod(focusObject)) {
+ // Input method is enabled. Pass on to the input context if we
+ // are hitting the input item.
+ if (QPlatformInputContext::inputItemClipRectangle().contains(qtWindowPoint)) {
+ qCDebug(lcQpaInputMethods) << "Asking input context to handle mouse press"
+ << "for focus object" << focusObject;
+ if ([NSTextInputContext.currentInputContext handleEvent:theEvent]) {
+ // NSTextView bails out if the input context handled the event,
+ // which is e.g. the case for 2-Set Korean input. We follow suit,
+ // even if that means having to click twice to move the cursor
+ // for these input methods when they are composing.
+ qCDebug(lcQpaInputMethods) << "Input context handled event; bailing out.";
+ return;
+ }
}
- [self handleMouseEvent:theEvent];
}
+
+ if (!m_dontOverrideCtrlLMB && (theEvent.modifierFlags & NSEventModifierFlagControl)) {
+ m_buttons |= Qt::RightButton;
+ m_sendUpAsRightButton = true;
+ } else {
+ m_buttons |= Qt::LeftButton;
+ }
+
+ [self handleMouseEvent:theEvent];
}
- (void)mouseDragged:(NSEvent *)theEvent
@@ -696,11 +714,11 @@ static const QPointingDevice *pointingDeviceFor(qint64 deviceID)
// had time to emit a momentum phase event.
if ([NSApp nextEventMatchingMask:NSEventMaskScrollWheel untilDate:[NSDate distantPast]
inMode:@"QtMomementumEventSearchMode" dequeue:NO].momentumPhase == NSEventPhaseBegan) {
- Q_ASSERT(pixelDelta.isNull() && angleDelta.isNull());
- return; // Ignore this event, as it has a delta of 0,0
+ return; // Ignore, even if it has delta
+ } else {
+ phase = Qt::ScrollEnd;
+ m_scrolling = false;
}
- phase = Qt::ScrollEnd;
- m_scrolling = false;
} else if (theEvent.momentumPhase == NSEventPhaseBegan) {
Q_ASSERT(!pixelDelta.isNull() && !angleDelta.isNull());
phase = Qt::ScrollUpdate; // Send as update, it has a delta
diff --git a/src/plugins/platforms/cocoa/qnsview_tablet.mm b/src/plugins/platforms/cocoa/qnsview_tablet.mm
index 2b269d038a..318a32f7cf 100644
--- a/src/plugins/platforms/cocoa/qnsview_tablet.mm
+++ b/src/plugins/platforms/cocoa/qnsview_tablet.mm
@@ -41,20 +41,13 @@
#ifndef QT_NO_TABLETEVENT
-#include "qpointingdevice.h"
+#include <QtGui/qpointingdevice.h>
+#include <QtCore/private/qflatmap_p.h>
Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet")
-struct QCocoaTabletDeviceData
-{
- QInputDevice::DeviceType device;
- QPointingDevice::PointerType pointerType;
- uint capabilityMask;
- qint64 uid;
-};
-
-typedef QHash<uint, QCocoaTabletDeviceData> QCocoaTabletDeviceDataHash;
-Q_GLOBAL_STATIC(QCocoaTabletDeviceDataHash, tabletDeviceDataHash)
+using QCocoaTabletDeviceMap = QFlatMap<qint64, const QPointingDevice*>;
+Q_GLOBAL_STATIC(QCocoaTabletDeviceMap, devicesInProximity)
@implementation QNSView (Tablet)
@@ -75,14 +68,15 @@ Q_GLOBAL_STATIC(QCocoaTabletDeviceDataHash, tabletDeviceDataHash)
QPointF screenPoint;
[self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint: &windowPoint andScreenPoint: &screenPoint];
- uint deviceId = [theEvent deviceID];
- if (!tabletDeviceDataHash->contains(deviceId)) {
+ // We use devicesInProximity because deviceID is typically 0,
+ // so QInputDevicePrivate::fromId() won't work.
+ const auto *device = devicesInProximity->value(theEvent.deviceID);
+ if (!device) {
// Error: Unknown tablet device. Qt also gets into this state
// when running on a VM. This appears to be harmless; don't
// print a warning.
return false;
}
- const QCocoaTabletDeviceData &deviceData = tabletDeviceDataHash->value(deviceId);
bool down = (eventType != NSEventTypeMouseMoved);
@@ -99,10 +93,10 @@ Q_GLOBAL_STATIC(QCocoaTabletDeviceDataHash, tabletDeviceDataHash)
qreal tangentialPressure = 0;
qreal rotation = 0;
int z = 0;
- if (deviceData.capabilityMask & 0x0200)
+ if (device->hasCapability(QInputDevice::Capability::ZPosition))
z = [theEvent absoluteZ];
- if (deviceData.capabilityMask & 0x0800)
+ if (device->hasCapability(QInputDevice::Capability::TangentialPressure))
tangentialPressure = ([theEvent tangentialPressure] * 2.0) - 1.0;
rotation = 360.0 - [theEvent rotation];
@@ -112,15 +106,8 @@ Q_GLOBAL_STATIC(QCocoaTabletDeviceDataHash, tabletDeviceDataHash)
Qt::KeyboardModifiers keyboardModifiers = QAppleKeyMapper::fromCocoaModifiers(theEvent.modifierFlags);
Qt::MouseButtons buttons = ignoreButtonMapping ? static_cast<Qt::MouseButtons>(static_cast<uint>([theEvent buttonMask])) : m_buttons;
- qCDebug(lcQpaTablet, "event on tablet %d with tool %d type %d unique ID %lld pos %6.1f, %6.1f root pos %6.1f, %6.1f buttons 0x%x pressure %4.2lf tilt %d, %d rotation %6.2lf",
- deviceId, deviceData.device, deviceData.pointerType, deviceData.uid,
- windowPoint.x(), windowPoint.y(), screenPoint.x(), screenPoint.y(),
- static_cast<uint>(buttons), pressure, xTilt, yTilt, rotation);
-
- QWindowSystemInterface::handleTabletEvent(m_platformWindow->window(), timestamp, windowPoint, screenPoint,
- int(deviceData.device), int(deviceData.pointerType), buttons, pressure, xTilt, yTilt,
- tangentialPressure, rotation, z, deviceData.uid,
- keyboardModifiers);
+ QWindowSystemInterface::handleTabletEvent(m_platformWindow->window(), timestamp, device, windowPoint, screenPoint,
+ buttons, pressure, xTilt, yTilt, tangentialPressure, rotation, z, keyboardModifiers);
return true;
}
@@ -132,10 +119,17 @@ Q_GLOBAL_STATIC(QCocoaTabletDeviceDataHash, tabletDeviceDataHash)
[self handleTabletEvent: theEvent];
}
-static QInputDevice::DeviceType wacomTabletDevice(NSEvent *theEvent)
+/*!
+ \internal
+ Find the existing QPointingDevice instance representing a particular tablet or stylus;
+ or create and register a new instance if it was not found.
+
+ An instance can be uniquely identified by various properties taken from \a theEvent.
+*/
+static const QPointingDevice *tabletToolInstance(NSEvent *theEvent)
{
- qint64 uid = [theEvent uniqueID];
- uint bits = [theEvent vendorPointingDeviceType];
+ qint64 uid = theEvent.uniqueID;
+ uint bits = theEvent.vendorPointingDeviceType;
if (bits == 0 && uid != 0) {
// Fallback. It seems that the driver doesn't always include all the information.
// High-End Wacom devices store their "type" in the uper bits of the Unique ID.
@@ -143,82 +137,96 @@ static QInputDevice::DeviceType wacomTabletDevice(NSEvent *theEvent)
bits = uid >> 32;
}
- QInputDevice::DeviceType device;
// Defined in the "EN0056-NxtGenImpGuideX"
// on Wacom's Developer Website (www.wacomeng.com)
- if (((bits & 0x0006) == 0x0002) && ((bits & 0x0F06) != 0x0902)) {
+ static const quint32 CursorTypeBitMask = 0x0F06;
+ quint32 toolId = bits & CursorTypeBitMask;
+ QInputDevice::Capabilities caps = QInputDevice::Capability::Position |
+ QInputDevice::Capability::Pressure | QInputDevice::Capability::Hover;
+ QInputDevice::DeviceType device;
+ int buttonCount = 3; // the tip, plus two barrel buttons
+ if (((bits & 0x0006) == 0x0002) && (toolId != 0x0902)) {
device = QInputDevice::DeviceType::Stylus;
} else {
- switch (bits & 0x0F06) {
- case 0x0802:
- device = QInputDevice::DeviceType::Stylus;
- break;
- case 0x0902:
- device = QInputDevice::DeviceType::Airbrush;
- break;
- case 0x0004:
- device = QInputDevice::DeviceType::Mouse; // TODO set capabilities
- break;
- case 0x0006:
- device = QInputDevice::DeviceType::Puck;
- break;
- case 0x0804:
- device = QInputDevice::DeviceType::Stylus; // TODO set capability rotation
- break;
- default:
- device = QInputDevice::DeviceType::Unknown;
- }
- }
- return device;
-}
-
-- (void)tabletProximity:(NSEvent *)theEvent
-{
- if ([self isTransparentForUserInput])
- return [super tabletProximity:theEvent];
-
- ulong timestamp = [theEvent timestamp] * 1000;
-
- QCocoaTabletDeviceData deviceData;
- deviceData.uid = [theEvent uniqueID];
- deviceData.capabilityMask = [theEvent capabilityMask];
-
- switch ([theEvent pointingDeviceType]) {
- case NSPointingDeviceTypeUnknown:
- default:
- deviceData.pointerType = QPointingDevice::PointerType::Unknown;
+ switch (toolId) {
+ // TODO same cases as in qxcbconnection_xi2.cpp? then we could share this function
+ case 0x0802:
+ device = QInputDevice::DeviceType::Stylus;
break;
- case NSPointingDeviceTypePen:
- deviceData.pointerType = QPointingDevice::PointerType::Pen;
+ case 0x0902:
+ device = QInputDevice::DeviceType::Airbrush;
+ caps.setFlag(QInputDevice::Capability::TangentialPressure);
+ buttonCount = 2;
break;
- case NSPointingDeviceTypeCursor:
- deviceData.pointerType = QPointingDevice::PointerType::Cursor;
+ case 0x0004:
+ device = QInputDevice::DeviceType::Mouse;
+ caps.setFlag(QInputDevice::Capability::Scroll);
break;
- case NSPointingDeviceTypeEraser:
- deviceData.pointerType = QPointingDevice::PointerType::Eraser;
+ case 0x0006:
+ device = QInputDevice::DeviceType::Puck;
break;
+ case 0x0804:
+ device = QInputDevice::DeviceType::Stylus; // Art Pen
+ caps.setFlag(QInputDevice::Capability::Rotation);
+ buttonCount = 1;
+ break;
+ default:
+ device = QInputDevice::DeviceType::Unknown;
+ }
}
- deviceData.device = wacomTabletDevice(theEvent);
+ uint capabilityMask = theEvent.capabilityMask;
+ if (capabilityMask & 0x0200)
+ caps.setFlag(QInputDevice::Capability::ZPosition);
+ if (capabilityMask & 0x0800)
+ Q_ASSERT(caps.testFlag(QInputDevice::Capability::TangentialPressure));
+
+ QPointingDevice::PointerType pointerType = QPointingDevice::PointerType::Unknown;
+ switch (theEvent.pointingDeviceType) {
+ case NSPointingDeviceTypeUnknown:
+ default:
+ break;
+ case NSPointingDeviceTypePen:
+ pointerType = QPointingDevice::PointerType::Pen;
+ break;
+ case NSPointingDeviceTypeCursor:
+ pointerType = QPointingDevice::PointerType::Cursor;
+ break;
+ case NSPointingDeviceTypeEraser:
+ pointerType = QPointingDevice::PointerType::Eraser;
+ break;
+ }
- // The deviceID is "unique" while in the proximity, it's a key that we can use for
- // linking up QCocoaTabletDeviceData to an event (especially if there are two devices in action).
- bool entering = [theEvent isEnteringProximity];
- uint deviceId = [theEvent deviceID];
- if (entering) {
- tabletDeviceDataHash->insert(deviceId, deviceData);
- } else {
- tabletDeviceDataHash->remove(deviceId);
+ const auto uniqueID = QPointingDeviceUniqueId::fromNumericId(uid);
+ auto windowSystemId = theEvent.deviceID;
+ const QPointingDevice *ret = QPointingDevicePrivate::queryTabletDevice(device, pointerType, uniqueID, caps, windowSystemId);
+ if (!ret) {
+ // TODO get the device name? (first argument)
+ // TODO associate each stylus with a "master" device representing the tablet itself
+ ret = new QPointingDevice(QString(), windowSystemId, device, pointerType,
+ caps, 1, buttonCount, QString(), uniqueID, QCocoaIntegration::instance());
+ QWindowSystemInterface::registerInputDevice(ret);
}
+ QPointingDevicePrivate *devPriv = QPointingDevicePrivate::get(const_cast<QPointingDevice *>(ret));
+ devPriv->toolId = toolId;
+ return ret;
+}
- qCDebug(lcQpaTablet, "proximity change on tablet %d: current tool %d type %d unique ID %lld",
- deviceId, deviceData.device, deviceData.pointerType, deviceData.uid);
+- (void)tabletProximity:(NSEvent *)theEvent
+{
+ if ([self isTransparentForUserInput])
+ return [super tabletProximity:theEvent];
- if (entering) {
- QWindowSystemInterface::handleTabletEnterProximityEvent(timestamp, int(deviceData.device), int(deviceData.pointerType), deviceData.uid);
- } else {
- QWindowSystemInterface::handleTabletLeaveProximityEvent(timestamp, int(deviceData.device), int(deviceData.pointerType), deviceData.uid);
- }
+ const ulong timestamp = theEvent.timestamp * 1000;
+ const qint64 windowSystemId = theEvent.deviceID;
+ const QPointingDevice *device = tabletToolInstance(theEvent);
+ // TODO which window?
+ QWindowSystemInterface::handleTabletEnterLeaveProximityEvent(nullptr, timestamp, device, theEvent.isEnteringProximity);
+ // The windowSystemId starts at 0, but is "unique" while in proximity
+ if (theEvent.isEnteringProximity)
+ devicesInProximity->insert(windowSystemId, device);
+ else
+ devicesInProximity->remove(windowSystemId);
}
@end
diff --git a/src/plugins/platforms/cocoa/qnswindowdelegate.mm b/src/plugins/platforms/cocoa/qnswindowdelegate.mm
index 8b80ba4076..ecaa14bcf7 100644
--- a/src/plugins/platforms/cocoa/qnswindowdelegate.mm
+++ b/src/plugins/platforms/cocoa/qnswindowdelegate.mm
@@ -112,6 +112,14 @@ static QCocoaWindow *toPlatformWindow(NSWindow *window)
return QCocoaScreen::mapToNative(maximizedFrame);
}
+- (BOOL)windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame
+{
+ QCocoaWindow *platformWindow = toPlatformWindow(window);
+ Q_ASSERT(platformWindow);
+ platformWindow->windowWillZoom();
+ return YES;
+}
+
- (BOOL)window:(NSWindow *)window shouldPopUpDocumentPathMenu:(NSMenu *)menu
{
Q_UNUSED(menu);
diff --git a/src/plugins/platforms/direct2d/CMakeLists.txt b/src/plugins/platforms/direct2d/CMakeLists.txt
index e87d2886d8..b88ac617aa 100644
--- a/src/plugins/platforms/direct2d/CMakeLists.txt
+++ b/src/plugins/platforms/direct2d/CMakeLists.txt
@@ -72,6 +72,10 @@ qt_internal_add_plugin(QWindowsDirect2DIntegrationPlugin
winmm
winspool
wtsapi32
+ shcore
+ comdlg32
+ d3d9
+ runtimeobject
)
# Resources:
diff --git a/src/plugins/platforms/directfb/qdirectfbinput.cpp b/src/plugins/platforms/directfb/qdirectfbinput.cpp
index 025edb28b5..c2b4adbd16 100644
--- a/src/plugins/platforms/directfb/qdirectfbinput.cpp
+++ b/src/plugins/platforms/directfb/qdirectfbinput.cpp
@@ -209,7 +209,7 @@ void QDirectFbInput::handleEnterLeaveEvents(const DFBEvent &event)
void QDirectFbInput::handleGotFocusEvent(const DFBEvent &event)
{
QWindow *tlw = m_tlwMap.value(event.window.window_id);
- QWindowSystemInterface::handleWindowActivated(tlw);
+ QWindowSystemInterface::handleWindowActivated(tlw, Qt::ActiveWindowFocusReason);
}
void QDirectFbInput::handleCloseEvent(const DFBEvent &event)
diff --git a/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp b/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp
index 813de2fc47..09c102b66f 100644
--- a/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp
+++ b/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration.cpp
@@ -74,47 +74,17 @@ Q_LOGGING_CATEGORY(qLcEglDevDebug, "qt.qpa.egldeviceintegration")
Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
(QEglFSDeviceIntegrationFactoryInterface_iid, QLatin1String("/egldeviceintegrations"), Qt::CaseInsensitive))
-#if QT_CONFIG(library)
-Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, directLoader,
- (QEglFSDeviceIntegrationFactoryInterface_iid, QLatin1String(""), Qt::CaseInsensitive))
-
-#endif // QT_CONFIG(library)
-
-QStringList QEglFSDeviceIntegrationFactory::keys(const QString &pluginPath)
+QStringList QEglFSDeviceIntegrationFactory::keys()
{
QStringList list;
-#if QT_CONFIG(library)
- if (!pluginPath.isEmpty()) {
- QCoreApplication::addLibraryPath(pluginPath);
- list = directLoader()->keyMap().values();
- if (!list.isEmpty()) {
- const QString postFix = QLatin1String(" (from ")
- + QDir::toNativeSeparators(pluginPath)
- + QLatin1Char(')');
- const QStringList::iterator end = list.end();
- for (QStringList::iterator it = list.begin(); it != end; ++it)
- (*it).append(postFix);
- }
- }
-#else
- Q_UNUSED(pluginPath);
-#endif
list.append(loader()->keyMap().values());
qCDebug(qLcEglDevDebug) << "EGL device integration plugin keys:" << list;
return list;
}
-QEglFSDeviceIntegration *QEglFSDeviceIntegrationFactory::create(const QString &key, const QString &pluginPath)
+QEglFSDeviceIntegration *QEglFSDeviceIntegrationFactory::create(const QString &key)
{
QEglFSDeviceIntegration *integration = nullptr;
-#if QT_CONFIG(library)
- if (!pluginPath.isEmpty()) {
- QCoreApplication::addLibraryPath(pluginPath);
- integration = qLoadPlugin<QEglFSDeviceIntegration, QEglFSDeviceIntegrationPlugin>(directLoader(), key);
- }
-#else
- Q_UNUSED(pluginPath);
-#endif
if (!integration)
integration = qLoadPlugin<QEglFSDeviceIntegration, QEglFSDeviceIntegrationPlugin>(loader(), key);
if (integration)
diff --git a/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration_p.h b/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration_p.h
index f8c0d6c508..4e43a7c37b 100644
--- a/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration_p.h
+++ b/src/plugins/platforms/eglfs/api/qeglfsdeviceintegration_p.h
@@ -126,8 +126,8 @@ public:
class Q_EGLFS_EXPORT QEglFSDeviceIntegrationFactory
{
public:
- static QStringList keys(const QString &pluginPath = QString());
- static QEglFSDeviceIntegration *create(const QString &name, const QString &platformPluginPath = QString());
+ static QStringList keys();
+ static QEglFSDeviceIntegration *create(const QString &name);
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/eglfs/api/qeglfswindow.cpp b/src/plugins/platforms/eglfs/api/qeglfswindow.cpp
index b1fdba5a46..9cd1f17f5f 100644
--- a/src/plugins/platforms/eglfs/api/qeglfswindow.cpp
+++ b/src/plugins/platforms/eglfs/api/qeglfswindow.cpp
@@ -265,7 +265,7 @@ void QEglFSWindow::requestActivateWindow()
QOpenGLCompositor::instance()->moveToTop(this);
#endif
QWindow *wnd = window();
- QWindowSystemInterface::handleWindowActivated(wnd);
+ QWindowSystemInterface::handleWindowActivated(wnd, Qt::ActiveWindowFocusReason);
QWindowSystemInterface::handleExposeEvent(wnd, QRect(QPoint(0, 0), wnd->geometry().size()));
}
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.cpp
index 67606f895f..a3ac9205a9 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmseventreader.cpp
@@ -39,6 +39,7 @@
#include "qeglfskmseventreader_p.h"
#include "qeglfskmsdevice_p.h"
+#include "qeglfskmsscreen_p.h"
#include <QSocketNotifier>
#include <QCoreApplication>
#include <QLoggingCategory>
@@ -50,12 +51,12 @@ Q_DECLARE_LOGGING_CATEGORY(qLcEglfsKmsDebug)
static void pageFlipHandler(int fd, unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec, void *user_data)
{
Q_UNUSED(fd);
- Q_UNUSED(sequence);
- Q_UNUSED(tv_sec);
- Q_UNUSED(tv_usec);
QEglFSKmsEventReaderThread *t = static_cast<QEglFSKmsEventReaderThread *>(QThread::currentThread());
t->eventHost()->handlePageFlipCompleted(user_data);
+
+ QEglFSKmsScreen *screen = static_cast<QEglFSKmsScreen *>(user_data);
+ screen->pageFlipped(sequence, tv_sec, tv_usec);
}
class RegisterWaitFlipEvent : public QEvent
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp
index 0a77e3e642..3bbdd55d34 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen.cpp
@@ -254,4 +254,13 @@ void QEglFSKmsScreen::setPowerState(QPlatformScreen::PowerState state)
m_powerState = state;
}
+/* Informs exact page flip timing which can be used rendering optimization.
+ Consider this is from drm event reader thread. */
+void QEglFSKmsScreen::pageFlipped(unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec)
+{
+ Q_UNUSED(sequence);
+ Q_UNUSED(tv_sec);
+ Q_UNUSED(tv_usec);
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen_p.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen_p.h
index db0aca0bd2..16ed6f4c30 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen_p.h
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support/qeglfskmsscreen_p.h
@@ -115,6 +115,7 @@ public:
bool isCursorOutOfRange() const { return m_cursorOutOfRange; }
void setCursorOutOfRange(bool b) { m_cursorOutOfRange = b; }
+ virtual void pageFlipped(unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec);
protected:
QEglFSKmsDevice *m_device;
diff --git a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.h b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.h
index 8d7cabf15b..3bb9fab1fc 100644
--- a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.h
+++ b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.h
@@ -54,7 +54,7 @@ public:
QIOSFileEngineAssetsLibrary(const QString &fileName);
~QIOSFileEngineAssetsLibrary();
- bool open(QIODevice::OpenMode openMode) override;
+ bool open(QIODevice::OpenMode openMode, std::optional<QFile::Permissions> permissions) override;
bool close() override;
FileFlags fileFlags(FileFlags type) const override;
qint64 size() const override;
@@ -63,7 +63,6 @@ public:
bool seek(qint64 pos) override;
QString fileName(FileName file) const override;
void setFileName(const QString &file) override;
- QStringList entryList(QDir::Filters filters, const QStringList &filterNames) const override;
#ifndef QT_NO_FILESYSTEMITERATOR
Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) override;
diff --git a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.mm b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.mm
index 01524a00e8..9cb38a3461 100644
--- a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.mm
+++ b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/qiosfileengineassetslibrary.mm
@@ -353,8 +353,11 @@ ALAsset *QIOSFileEngineAssetsLibrary::loadAsset() const
return m_data->m_asset;
}
-bool QIOSFileEngineAssetsLibrary::open(QIODevice::OpenMode openMode)
+bool QIOSFileEngineAssetsLibrary::open(QIODevice::OpenMode openMode,
+ std::optional<QFile::Permissions> permissions)
{
+ Q_UNUSED(permissions);
+
if (openMode & (QIODevice::WriteOnly | QIODevice::Text))
return false;
return loadAsset();
@@ -456,11 +459,6 @@ void QIOSFileEngineAssetsLibrary::setFileName(const QString &file)
m_assetUrl = QLatin1String("assets-library:/") + file.mid(index);
}
-QStringList QIOSFileEngineAssetsLibrary::entryList(QDir::Filters filters, const QStringList &filterNames) const
-{
- return QAbstractFileEngine::entryList(filters, filterNames);
-}
-
#ifndef QT_NO_FILESYSTEMITERATOR
QAbstractFileEngine::Iterator *QIOSFileEngineAssetsLibrary::beginEntryList(
diff --git a/src/plugins/platforms/ios/qiosfiledialog.mm b/src/plugins/platforms/ios/qiosfiledialog.mm
index edf04016fd..a56bf25c16 100644
--- a/src/plugins/platforms/ios/qiosfiledialog.mm
+++ b/src/plugins/platforms/ios/qiosfiledialog.mm
@@ -87,7 +87,8 @@ bool QIOSFileDialog::showImagePickerDialog(QWindow *parent)
{
if (!m_viewController) {
QFactoryLoader *plugins = QIOSIntegration::instance()->optionalPlugins();
- for (int i = 0; i < plugins->metaData().size(); ++i) {
+ qsizetype size = QList<QPluginParsedMetaData>(plugins->metaData()).size();
+ for (qsizetype i = 0; i < size; ++i) {
QIosOptionalPluginInterface *plugin = qobject_cast<QIosOptionalPluginInterface *>(plugins->instance(i));
m_viewController = [plugin->createImagePickerController(this) retain];
if (m_viewController)
diff --git a/src/plugins/platforms/ios/qiosinputcontext.h b/src/plugins/platforms/ios/qiosinputcontext.h
index 36421a57c3..d834abda40 100644
--- a/src/plugins/platforms/ios/qiosinputcontext.h
+++ b/src/plugins/platforms/ios/qiosinputcontext.h
@@ -54,7 +54,7 @@ const char kImePlatformDataReturnKeyType[] = "returnKeyType";
@class QIOSLocaleListener;
@class QIOSKeyboardListener;
-@class QIOSTextInputResponder;
+@class QIOSTextResponder;
@protocol KeyboardState;
QT_BEGIN_NAMESPACE
@@ -125,7 +125,7 @@ private:
QIOSLocaleListener *m_localeListener;
QIOSKeyboardListener *m_keyboardHideGesture;
- QIOSTextInputResponder *m_textResponder;
+ QIOSTextResponder *m_textResponder;
KeyboardState m_keyboardState;
ImeState m_imeState;
};
diff --git a/src/plugins/platforms/ios/qiosinputcontext.mm b/src/plugins/platforms/ios/qiosinputcontext.mm
index c6032b5147..1ae7f953b2 100644
--- a/src/plugins/platforms/ios/qiosinputcontext.mm
+++ b/src/plugins/platforms/ios/qiosinputcontext.mm
@@ -675,16 +675,23 @@ void QIOSInputContext::update(Qt::InputMethodQueries updatedProperties)
}
// Mask for properties that we are interested in and see if any of them changed
- updatedProperties &= (Qt::ImEnabled | Qt::ImHints | Qt::ImQueryInput | Qt::ImEnterKeyType | Qt::ImPlatformData);
+ updatedProperties &= (Qt::ImEnabled | Qt::ImHints | Qt::ImQueryInput | Qt::ImEnterKeyType | Qt::ImPlatformData | Qt::ImReadOnly);
// Perform update first, so we can trust the value of inputMethodAccepted()
Qt::InputMethodQueries changedProperties = m_imeState.update(updatedProperties);
- if (inputMethodAccepted()) {
+ const bool inputIsReadOnly = m_imeState.currentState.value(Qt::ImReadOnly).toBool();
+
+ if (inputMethodAccepted() || inputIsReadOnly) {
if (!m_textResponder || [m_textResponder needsKeyboardReconfigure:changedProperties]) {
- qImDebug("creating new text responder");
[m_textResponder autorelease];
- m_textResponder = [[QIOSTextInputResponder alloc] initWithInputContext:this];
+ if (inputIsReadOnly) {
+ qImDebug("creating new read-only text responder");
+ m_textResponder = [[QIOSTextResponder alloc] initWithInputContext:this];
+ } else {
+ qImDebug("creating new read/write text responder");
+ m_textResponder = [[QIOSTextInputResponder alloc] initWithInputContext:this];
+ }
} else {
qImDebug("no need to reconfigure keyboard, just notifying input delegate");
[m_textResponder notifyInputDelegate:changedProperties];
@@ -732,8 +739,7 @@ void QIOSInputContext::reset()
update(Qt::ImQueryAll);
- [m_textResponder setMarkedText:@"" selectedRange:NSMakeRange(0, 0)];
- [m_textResponder notifyInputDelegate:Qt::ImQueryInput];
+ [m_textResponder reset];
}
/*!
@@ -747,9 +753,7 @@ void QIOSInputContext::reset()
void QIOSInputContext::commit()
{
qImDebug("unmarking text");
-
- [m_textResponder unmarkText];
- [m_textResponder notifyInputDelegate:Qt::ImSurroundingText];
+ [m_textResponder commit];
}
QLocale QIOSInputContext::locale() const
diff --git a/src/plugins/platforms/ios/qiosintegration.mm b/src/plugins/platforms/ios/qiosintegration.mm
index 725c280129..8938f97ae5 100644
--- a/src/plugins/platforms/ios/qiosintegration.mm
+++ b/src/plugins/platforms/ios/qiosintegration.mm
@@ -125,7 +125,8 @@ void QIOSIntegration::initialize()
#endif
QMacInternalPasteboardMime::initializeMimeTypes();
- for (int i = 0; i < m_optionalPlugins->metaData().size(); ++i)
+ qsizetype size = QList<QPluginParsedMetaData>(m_optionalPlugins->metaData()).size();
+ for (qsizetype i = 0; i < size; ++i)
qobject_cast<QIosOptionalPluginInterface *>(m_optionalPlugins->instance(i))->initPlugin();
}
diff --git a/src/plugins/platforms/ios/qiostextinputoverlay.mm b/src/plugins/platforms/ios/qiostextinputoverlay.mm
index 2ab732df7a..c72d8dc596 100644
--- a/src/plugins/platforms/ios/qiostextinputoverlay.mm
+++ b/src/plugins/platforms/ios/qiostextinputoverlay.mm
@@ -1048,15 +1048,39 @@ void QIOSTextInputOverlay::updateFocusObject()
s_editMenu = nullptr;
}
- if (platformInputContext()->inputMethodAccepted()) {
- s_editMenu = [QIOSEditMenu new];
- m_cursorRecognizer = [QIOSCursorRecognizer new];
+ const QVariant hintsVariant = QGuiApplication::inputMethod()->queryFocusObject(Qt::ImHints, QVariant());
+ const Qt::InputMethodHints hints = Qt::InputMethodHints(hintsVariant.toUInt());
+ if (hints & Qt::ImhNoTextHandles)
+ return;
+
+ // The focus object can emit selection updates (e.g from mouse drag), and
+ // accept modifying it through IM when dragging on the handles, even if it
+ // doesn't accept text input and IM in general (and hence return false from
+ // inputMethodAccepted()). This is the case for read-only text fields.
+ // Therefore, listen for selection changes also when the focus object
+ // reports that it's ImReadOnly (which we take as a hint that it's actually
+ // a text field, and that selections therefore might happen). But since
+ // we have no guarantee that the focus object can actually accept new selections
+ // through IM (and since we also need to respect if the input accepts selections
+ // in the first place), we only support selections started by the text field (e.g from
+ // mouse drag), even if we in theory could also start selections from a loupe.
+
+ const bool inputAccepted = platformInputContext()->inputMethodAccepted();
+ const bool readOnly = QGuiApplication::inputMethod()->queryFocusObject(Qt::ImReadOnly, QVariant()).toBool();
+
+ if (inputAccepted || readOnly) {
+ if (!(hints & Qt::ImhNoEditMenu))
+ s_editMenu = [QIOSEditMenu new];
m_selectionRecognizer = [QIOSSelectionRecognizer new];
m_openMenuOnTapRecognizer = [QIOSTapRecognizer new];
- m_cursorRecognizer.enabled = YES;
m_selectionRecognizer.enabled = YES;
m_openMenuOnTapRecognizer.enabled = YES;
}
+
+ if (inputAccepted) {
+ m_cursorRecognizer = [QIOSCursorRecognizer new];
+ m_cursorRecognizer.enabled = YES;
+ }
}
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/ios/qiostextresponder.h b/src/plugins/platforms/ios/qiostextresponder.h
index 074598c1c3..d6f1cfd03f 100644
--- a/src/plugins/platforms/ios/qiostextresponder.h
+++ b/src/plugins/platforms/ios/qiostextresponder.h
@@ -48,7 +48,18 @@ class QIOSInputContext;
QT_END_NAMESPACE
-@interface QIOSTextInputResponder : UIResponder <UITextInputTraits, UIKeyInput, UITextInput>
+@interface QIOSTextResponder : UIResponder
+
+- (instancetype)initWithInputContext:(QT_PREPEND_NAMESPACE(QIOSInputContext) *)context;
+
+- (void)notifyInputDelegate:(Qt::InputMethodQueries)updatedProperties;
+- (BOOL)needsKeyboardReconfigure:(Qt::InputMethodQueries)updatedProperties;
+- (void)reset;
+- (void)commit;
+
+@end
+
+@interface QIOSTextInputResponder : QIOSTextResponder <UITextInputTraits, UIKeyInput, UITextInput>
- (instancetype)initWithInputContext:(QT_PREPEND_NAMESPACE(QIOSInputContext) *)context;
- (BOOL)needsKeyboardReconfigure:(Qt::InputMethodQueries)updatedProperties;
diff --git a/src/plugins/platforms/ios/qiostextresponder.mm b/src/plugins/platforms/ios/qiostextresponder.mm
index a65f8bbaa1..deca8c7617 100644
--- a/src/plugins/platforms/ios/qiostextresponder.mm
+++ b/src/plugins/platforms/ios/qiostextresponder.mm
@@ -161,12 +161,11 @@
// -------------------------------------------------------------------------
-@implementation QIOSTextInputResponder {
+@implementation QIOSTextResponder {
+ @public
QT_PREPEND_NAMESPACE(QIOSInputContext) *m_inputContext;
QT_PREPEND_NAMESPACE(QInputMethodQueryEvent) *m_configuredImeState;
- QString m_markedText;
BOOL m_inSendEventToFocusObject;
- BOOL m_inSelectionChange;
}
- (instancetype)initWithInputContext:(QT_PREPEND_NAMESPACE(QIOSInputContext) *)inputContext
@@ -174,14 +173,207 @@
if (!(self = [self init]))
return self;
+ m_inputContext = inputContext;
+ m_configuredImeState = static_cast<QInputMethodQueryEvent*>(m_inputContext->imeState().currentState.clone());
m_inSendEventToFocusObject = NO;
+
+ return self;
+}
+
+- (void)dealloc
+{
+ delete m_configuredImeState;
+ [super dealloc];
+}
+
+- (QVariant)currentImeState:(Qt::InputMethodQuery)query
+{
+ return m_inputContext->imeState().currentState.value(query);
+}
+
+- (BOOL)canBecomeFirstResponder
+{
+ return YES;
+}
+
+- (BOOL)becomeFirstResponder
+{
+ FirstResponderCandidate firstResponderCandidate(self);
+
+ qImDebug() << "self:" << self << "first:" << [UIResponder currentFirstResponder];
+
+ if (![super becomeFirstResponder]) {
+ qImDebug() << self << "was not allowed to become first responder";
+ return NO;
+ }
+
+ qImDebug() << self << "became first responder";
+
+ return YES;
+}
+
+- (BOOL)resignFirstResponder
+{
+ qImDebug() << "self:" << self << "first:" << [UIResponder currentFirstResponder];
+
+ // Don't allow activation events of the window that we're doing text on behalf on
+ // to steal responder.
+ if (FirstResponderCandidate::currentCandidate() == [self nextResponder]) {
+ qImDebug("not allowing parent window to steal responder");
+ return NO;
+ }
+
+ if (![super resignFirstResponder])
+ return NO;
+
+ qImDebug() << self << "resigned first responder";
+
+ // Dismissing the keyboard will trigger resignFirstResponder, but so will
+ // a regular responder transfer to another window. In the former case, iOS
+ // will set the new first-responder to our next-responder, and in the latter
+ // case we'll have an active responder candidate.
+ if (![UIResponder currentFirstResponder] && !FirstResponderCandidate::currentCandidate()) {
+ // No first responder set anymore, sync this with Qt by clearing the
+ // focus object.
+ m_inputContext->clearCurrentFocusObject();
+ } else if ([UIResponder currentFirstResponder] == [self nextResponder]) {
+ // We have resigned the keyboard, and transferred first responder back to the parent view
+ Q_ASSERT(!FirstResponderCandidate::currentCandidate());
+ if ([self currentImeState:Qt::ImEnabled].toBool()) {
+ // The current focus object expects text input, but there
+ // is no keyboard to get input from. So we clear focus.
+ qImDebug("no keyboard available, clearing focus object");
+ m_inputContext->clearCurrentFocusObject();
+ }
+ } else {
+ // We've lost responder status because another Qt window was made active,
+ // another QIOSTextResponder was made first-responder, another UIView was
+ // made first-responder, or the first-responder was cleared globally. In
+ // either of these cases we don't have to do anything.
+ qImDebug("lost first responder, but not clearing focus object");
+ }
+
+ return YES;
+}
+
+- (UIResponder*)nextResponder
+{
+ return qApp->focusWindow() ?
+ reinterpret_cast<QUIView *>(qApp->focusWindow()->handle()->winId()) : 0;
+}
+
+// -------------------------------------------------------------------------
+
+- (void)notifyInputDelegate:(Qt::InputMethodQueries)updatedProperties
+{
+ Q_UNUSED(updatedProperties);
+}
+
+- (BOOL)needsKeyboardReconfigure:(Qt::InputMethodQueries)updatedProperties
+{
+ if (updatedProperties & Qt::ImEnabled) {
+ qImDebug() << "Qt::ImEnabled has changed since text responder was configured, need reconfigure";
+ return YES;
+ }
+
+ if (updatedProperties & Qt::ImReadOnly) {
+ qImDebug() << "Qt::ImReadOnly has changed since text responder was configured, need reconfigure";
+ return YES;
+ }
+
+ return NO;
+}
+
+- (void)reset
+{
+ // Nothing to reset for read-only text fields
+}
+
+- (void)commit
+{
+ // Nothing to commit for read-only text fields
+}
+
+// -------------------------------------------------------------------------
+
+#ifndef QT_NO_SHORTCUT
+
+- (void)sendKeyPressRelease:(Qt::Key)key modifiers:(Qt::KeyboardModifiers)modifiers
+{
+ QScopedValueRollback<BOOL> rollback(m_inSendEventToFocusObject, true);
+ QWindowSystemInterface::handleKeyEvent(qApp->focusWindow(), QEvent::KeyPress, key, modifiers);
+ QWindowSystemInterface::handleKeyEvent(qApp->focusWindow(), QEvent::KeyRelease, key, modifiers);
+}
+
+- (void)sendShortcut:(QKeySequence::StandardKey)standardKey
+{
+ const QKeyCombination combination = QKeySequence(standardKey)[0];
+ [self sendKeyPressRelease:combination.key() modifiers:combination.keyboardModifiers()];
+}
+
+- (BOOL)hasSelection
+{
+ QInputMethodQueryEvent query(Qt::ImAnchorPosition | Qt::ImCursorPosition);
+ QGuiApplication::sendEvent(QGuiApplication::focusObject(), &query);
+ int anchorPos = query.value(Qt::ImAnchorPosition).toInt();
+ int cursorPos = query.value(Qt::ImCursorPosition).toInt();
+ return anchorPos != cursorPos;
+}
+
+- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
+{
+ const bool isSelectAction =
+ action == @selector(select:) ||
+ action == @selector(selectAll:);
+
+ const bool isReadAction = action == @selector(copy:);
+
+ if (!isSelectAction && !isReadAction)
+ return [super canPerformAction:action withSender:sender];
+
+ const bool hasSelection = [self hasSelection];
+ return (!hasSelection && isSelectAction) || (hasSelection && isReadAction);
+}
+
+- (void)copy:(id)sender
+{
+ Q_UNUSED(sender);
+ [self sendShortcut:QKeySequence::Copy];
+}
+
+- (void)select:(id)sender
+{
+ Q_UNUSED(sender);
+ [self sendShortcut:QKeySequence::MoveToPreviousWord];
+ [self sendShortcut:QKeySequence::SelectNextWord];
+}
+
+- (void)selectAll:(id)sender
+{
+ Q_UNUSED(sender);
+ [self sendShortcut:QKeySequence::SelectAll];
+}
+
+#endif // QT_NO_SHORTCUT
+
+@end
+
+// -------------------------------------------------------------------------
+
+@implementation QIOSTextInputResponder {
+ QString m_markedText;
+ BOOL m_inSelectionChange;
+}
+
+- (instancetype)initWithInputContext:(QT_PREPEND_NAMESPACE(QIOSInputContext) *)inputContext
+{
+ if (!(self = [super initWithInputContext:inputContext]))
+ return self;
+
m_inSelectionChange = NO;
- m_inputContext = inputContext;
- m_configuredImeState = static_cast<QInputMethodQueryEvent*>(m_inputContext->imeState().currentState.clone());
QVariantMap platformData = m_configuredImeState->value(Qt::ImPlatformData).toMap();
Qt::InputMethodHints hints = Qt::InputMethodHints(m_configuredImeState->value(Qt::ImHints).toUInt());
-
Qt::EnterKeyType enterKeyType = Qt::EnterKeyType(m_configuredImeState->value(Qt::ImEnterKeyType).toUInt());
switch (enterKeyType) {
@@ -264,29 +456,27 @@
{
self.inputView = 0;
self.inputAccessoryView = 0;
- delete m_configuredImeState;
[super dealloc];
}
- (BOOL)needsKeyboardReconfigure:(Qt::InputMethodQueries)updatedProperties
{
- if ((updatedProperties & Qt::ImEnabled)) {
- Q_ASSERT([self currentImeState:Qt::ImEnabled].toBool());
-
+ Qt::InputMethodQueries relevantProperties = updatedProperties;
+ if ((relevantProperties & Qt::ImEnabled)) {
// When switching on input-methods we need to consider hints and platform data
// as well, as the IM state that we were based on may have been invalidated when
// IM was switched off.
qImDebug("IM was turned on, we need to check hints and platform data as well");
- updatedProperties |= (Qt::ImHints | Qt::ImPlatformData);
+ relevantProperties |= (Qt::ImHints | Qt::ImPlatformData);
}
// Based on what we set up in initWithInputContext above
- updatedProperties &= (Qt::ImHints | Qt::ImEnterKeyType | Qt::ImPlatformData);
+ relevantProperties &= (Qt::ImHints | Qt::ImEnterKeyType | Qt::ImPlatformData);
- if (!updatedProperties)
- return NO;
+ if (!relevantProperties)
+ return [super needsKeyboardReconfigure:updatedProperties];
for (uint i = 0; i < (sizeof(Qt::ImQueryAll) * CHAR_BIT); ++i) {
if (Qt::InputMethodQuery property = Qt::InputMethodQuery(int(updatedProperties & (1 << i)))) {
@@ -297,100 +487,13 @@
}
}
- return NO;
-}
-
-- (BOOL)canBecomeFirstResponder
-{
- return YES;
-}
-
-- (BOOL)becomeFirstResponder
-{
- FirstResponderCandidate firstResponderCandidate(self);
-
- qImDebug() << "self:" << self << "first:" << [UIResponder currentFirstResponder];
-
- if (![super becomeFirstResponder]) {
- qImDebug() << self << "was not allowed to become first responder";
- return NO;
- }
-
- qImDebug() << self << "became first responder";
-
- return YES;
-}
-
-- (BOOL)resignFirstResponder
-{
- qImDebug() << "self:" << self << "first:" << [UIResponder currentFirstResponder];
-
- // Don't allow activation events of the window that we're doing text on behalf on
- // to steal responder.
- if (FirstResponderCandidate::currentCandidate() == [self nextResponder]) {
- qImDebug("not allowing parent window to steal responder");
- return NO;
- }
-
- if (![super resignFirstResponder])
- return NO;
-
- qImDebug() << self << "resigned first responder";
-
- // Dismissing the keyboard will trigger resignFirstResponder, but so will
- // a regular responder transfer to another window. In the former case, iOS
- // will set the new first-responder to our next-responder, and in the latter
- // case we'll have an active responder candidate.
- if (![UIResponder currentFirstResponder] && !FirstResponderCandidate::currentCandidate()) {
- // No first responder set anymore, sync this with Qt by clearing the
- // focus object.
- m_inputContext->clearCurrentFocusObject();
- } else if ([UIResponder currentFirstResponder] == [self nextResponder]) {
- // We have resigned the keyboard, and transferred first responder back to the parent view
- Q_ASSERT(!FirstResponderCandidate::currentCandidate());
- if ([self currentImeState:Qt::ImEnabled].toBool()) {
- // The current focus object expects text input, but there
- // is no keyboard to get input from. So we clear focus.
- qImDebug("no keyboard available, clearing focus object");
- m_inputContext->clearCurrentFocusObject();
- }
- } else {
- // We've lost responder status because another Qt window was made active,
- // another QIOSTextResponder was made first-responder, another UIView was
- // made first-responder, or the first-responder was cleared globally. In
- // either of these cases we don't have to do anything.
- qImDebug("lost first responder, but not clearing focus object");
- }
-
- return YES;
-}
-
-
-- (UIResponder*)nextResponder
-{
- return qApp->focusWindow() ?
- reinterpret_cast<QUIView *>(qApp->focusWindow()->handle()->winId()) : 0;
+ return [super needsKeyboardReconfigure:updatedProperties];
}
// -------------------------------------------------------------------------
-- (void)sendKeyPressRelease:(Qt::Key)key modifiers:(Qt::KeyboardModifiers)modifiers
-{
- QScopedValueRollback<BOOL> rollback(m_inSendEventToFocusObject, true);
- QWindowSystemInterface::handleKeyEvent(qApp->focusWindow(), QEvent::KeyPress, key, modifiers);
- QWindowSystemInterface::handleKeyEvent(qApp->focusWindow(), QEvent::KeyRelease, key, modifiers);
-}
-
#ifndef QT_NO_SHORTCUT
-- (void)sendShortcut:(QKeySequence::StandardKey)standardKey
-{
- const int keys = QKeySequence(standardKey)[0];
- Qt::Key key = Qt::Key(keys & 0x0000FFFF);
- Qt::KeyboardModifiers modifiers = Qt::KeyboardModifiers(keys & 0xFFFF0000);
- [self sendKeyPressRelease:key modifiers:modifiers];
-}
-
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
bool isEditAction = (action == @selector(cut:)
@@ -410,7 +513,7 @@
|| action == @selector(redo));
const bool unknownAction = !isEditAction && !isSelectAction;
- const bool hasSelection = ![self selectedTextRange].empty;
+ const bool hasSelection = [self hasSelection];
if (unknownAction)
return [super canPerformAction:action withSender:sender];
@@ -434,31 +537,12 @@
[self sendShortcut:QKeySequence::Cut];
}
-- (void)copy:(id)sender
-{
- Q_UNUSED(sender);
- [self sendShortcut:QKeySequence::Copy];
-}
-
- (void)paste:(id)sender
{
Q_UNUSED(sender);
[self sendShortcut:QKeySequence::Paste];
}
-- (void)select:(id)sender
-{
- Q_UNUSED(sender);
- [self sendShortcut:QKeySequence::MoveToPreviousWord];
- [self sendShortcut:QKeySequence::SelectNextWord];
-}
-
-- (void)selectAll:(id)sender
-{
- Q_UNUSED(sender);
- [self sendShortcut:QKeySequence::SelectAll];
-}
-
- (void)delete:(id)sender
{
Q_UNUSED(sender);
@@ -647,11 +731,6 @@
QCoreApplication::sendEvent(focusObject, &e);
}
-- (QVariant)currentImeState:(Qt::InputMethodQuery)query
-{
- return m_inputContext->imeState().currentState.value(query);
-}
-
- (id<UITextInputTokenizer>)tokenizer
{
return [[[UITextInputStringTokenizer alloc] initWithTextInput:self] autorelease];
diff --git a/src/plugins/platforms/ios/qiosviewcontroller.mm b/src/plugins/platforms/ios/qiosviewcontroller.mm
index 7aeb3b9ca6..40beda6f5f 100644
--- a/src/plugins/platforms/ios/qiosviewcontroller.mm
+++ b/src/plugins/platforms/ios/qiosviewcontroller.mm
@@ -147,9 +147,13 @@
UIWindow *uiWindow = self.window;
if (uiWindow.screen != [UIScreen mainScreen] && self.subviews.count == 1) {
- // Removing the last view of an external screen, go back to mirror mode
- uiWindow.screen = [UIScreen mainScreen];
- uiWindow.hidden = YES;
+ // We're about to remove the last view of an external screen, so go back
+ // to mirror mode, but defer it until after the view has been removed,
+ // to ensure that we don't try to layout the view that's being removed.
+ dispatch_async(dispatch_get_main_queue(), ^{
+ uiWindow.hidden = YES;
+ uiWindow.screen = [UIScreen mainScreen];
+ });
}
}
diff --git a/src/plugins/platforms/ios/quiview.mm b/src/plugins/platforms/ios/quiview.mm
index ef6c9f49ad..1c97eee216 100644
--- a/src/plugins/platforms/ios/quiview.mm
+++ b/src/plugins/platforms/ios/quiview.mm
@@ -306,7 +306,7 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet")
// Nor do we want to deactivate the Qt window if the new responder
// is temporarily handling text input on behalf of a Qt window.
- if ([responder isKindOfClass:[QIOSTextInputResponder class]]) {
+ if ([responder isKindOfClass:[QIOSTextResponder class]]) {
while ((responder = [responder nextResponder])) {
if ([responder isKindOfClass:[QUIView class]])
return NO;
diff --git a/src/plugins/platforms/ios/quiview_accessibility.mm b/src/plugins/platforms/ios/quiview_accessibility.mm
index 6612dc131e..9a103509cc 100644
--- a/src/plugins/platforms/ios/quiview_accessibility.mm
+++ b/src/plugins/platforms/ios/quiview_accessibility.mm
@@ -66,6 +66,11 @@
- (void)initAccessibility
{
+ // The window may have gone away, but with the view
+ // temporarily caught in the a11y subsystem.
+ if (!self.platformWindow)
+ return;
+
static bool init = false;
if (!init)
QGuiApplicationPrivate::platformIntegration()->accessibility()->setActive(true);
diff --git a/src/plugins/platforms/offscreen/qoffscreenintegration_x11.cpp b/src/plugins/platforms/offscreen/qoffscreenintegration_x11.cpp
index 3c2fdb1f0f..0bb19bc4f6 100644
--- a/src/plugins/platforms/offscreen/qoffscreenintegration_x11.cpp
+++ b/src/plugins/platforms/offscreen/qoffscreenintegration_x11.cpp
@@ -155,6 +155,13 @@ void *QOffscreenX11PlatformNativeInterface::nativeResourceForContext(const QByte
}
#endif
+#if QT_CONFIG(xcb)
+Display *QOffscreenX11PlatformNativeInterface::display() const
+{
+ return m_connection ? reinterpret_cast<Display *>(m_connection->display()) : nullptr;
+}
+#endif
+
QOffscreenX11Connection::QOffscreenX11Connection()
{
XInitThreads();
diff --git a/src/plugins/platforms/offscreen/qoffscreenintegration_x11.h b/src/plugins/platforms/offscreen/qoffscreenintegration_x11.h
index afd30d7b4b..35c91d47d8 100644
--- a/src/plugins/platforms/offscreen/qoffscreenintegration_x11.h
+++ b/src/plugins/platforms/offscreen/qoffscreenintegration_x11.h
@@ -47,6 +47,7 @@
#include <qscopedpointer.h>
#include <qpa/qplatformopenglcontext.h>
+#include <QtGui/qguiapplication.h>
QT_BEGIN_NAMESPACE
@@ -54,6 +55,9 @@ class QOffscreenX11Connection;
class QOffscreenX11Info;
class QOffscreenX11PlatformNativeInterface : public QOffscreenPlatformNativeInterface
+#if QT_CONFIG(xcb)
+ , public QNativeInterface::QX11Application
+#endif
{
public:
QOffscreenX11PlatformNativeInterface(QOffscreenIntegration *integration);
@@ -63,7 +67,10 @@ public:
#if !defined(QT_NO_OPENGL) && QT_CONFIG(xcb_glx_plugin)
void *nativeResourceForContext(const QByteArray &resource, QOpenGLContext *context) override;
#endif
-
+#if QT_CONFIG(xcb)
+ Display *display() const override;
+ xcb_connection_t *connection() const override { return nullptr; };
+#endif
QScopedPointer<QOffscreenX11Connection> m_connection;
};
diff --git a/src/plugins/platforms/offscreen/qoffscreenwindow.cpp b/src/plugins/platforms/offscreen/qoffscreenwindow.cpp
index a46258a401..1046b2a908 100644
--- a/src/plugins/platforms/offscreen/qoffscreenwindow.cpp
+++ b/src/plugins/platforms/offscreen/qoffscreenwindow.cpp
@@ -44,6 +44,7 @@
#include <qpa/qwindowsysteminterface.h>
#include <private/qwindow_p.h>
+#include <private/qguiapplication_p.h>
QT_BEGIN_NAMESPACE
@@ -121,7 +122,7 @@ void QOffscreenWindow::setVisible(bool visible)
if (visible) {
if (window()->type() != Qt::ToolTip)
- QWindowSystemInterface::handleWindowActivated(window());
+ QWindowSystemInterface::handleWindowActivated(window(), Qt::ActiveWindowFocusReason);
if (m_pendingGeometryChangeOnShow) {
m_pendingGeometryChangeOnShow = false;
@@ -129,11 +130,26 @@ void QOffscreenWindow::setVisible(bool visible)
}
}
+ const QPoint cursorPos = QCursor::pos();
if (visible) {
QRect rect(QPoint(), geometry().size());
QWindowSystemInterface::handleExposeEvent(window(), rect);
+ if (QWindowPrivate::get(window())->isPopup() && QGuiApplicationPrivate::currentMouseWindow) {
+ QWindowSystemInterface::handleLeaveEvent<QWindowSystemInterface::SynchronousDelivery>
+ (QGuiApplicationPrivate::currentMouseWindow);
+ }
+ if (geometry().contains(cursorPos))
+ QWindowSystemInterface::handleEnterEvent(window(),
+ window()->mapFromGlobal(cursorPos), cursorPos);
} else {
QWindowSystemInterface::handleExposeEvent(window(), QRegion());
+ if (window()->type() & Qt::Window) {
+ if (QWindow *windowUnderMouse = QGuiApplication::topLevelAt(cursorPos)) {
+ QWindowSystemInterface::handleEnterEvent(windowUnderMouse,
+ windowUnderMouse->mapFromGlobal(cursorPos),
+ cursorPos);
+ }
+ }
}
m_visible = visible;
@@ -142,7 +158,7 @@ void QOffscreenWindow::setVisible(bool visible)
void QOffscreenWindow::requestActivateWindow()
{
if (m_visible)
- QWindowSystemInterface::handleWindowActivated(window());
+ QWindowSystemInterface::handleWindowActivated(window(), Qt::ActiveWindowFocusReason);
}
WId QOffscreenWindow::winId() const
diff --git a/src/plugins/platforms/qnx/qqnxeglwindow.cpp b/src/plugins/platforms/qnx/qqnxeglwindow.cpp
index 48766fc435..df36048e48 100644
--- a/src/plugins/platforms/qnx/qqnxeglwindow.cpp
+++ b/src/plugins/platforms/qnx/qqnxeglwindow.cpp
@@ -79,7 +79,7 @@ bool QQnxEglWindow::isInitialized() const
void QQnxEglWindow::ensureInitialized(QQnxGLContext* context)
{
if (m_newSurfaceRequested.testAndSetOrdered(true, false)) {
- const QMutexLocker locker(&m_mutex); // Set geomety must not reset the requestedBufferSize till
+ const QMutexLocker locker(&m_mutex); // Set geometry must not reset the requestedBufferSize till
// the surface is created
if (m_requestedBufferSize != bufferSize() || m_eglSurface == EGL_NO_SURFACE) {
diff --git a/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp b/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp
index fc682d66e2..84cfbea27d 100644
--- a/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp
+++ b/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp
@@ -577,7 +577,7 @@ void QQnxScreenEventHandler::handleTouchEvent(screen_event_t event, int qnxType)
parent = parent->parent();
}
- //Qt expects the pressure between 0 and 1. There is however no definit upper limit for
+ //Qt expects the pressure between 0 and 1. There is however no definite upper limit for
//the integer value of touch event pressure. The 200 was determined by experiment, it
//usually does not get higher than that.
m_touchPoints[touchId].pressure = static_cast<qreal>(touchPressure)/200.0;
@@ -742,7 +742,7 @@ void QQnxScreenEventHandler::handleKeyboardFocusPropertyEvent(screen_window_t wi
}
if (focus && focusWindow != QGuiApplication::focusWindow())
- QWindowSystemInterface::handleWindowActivated(focusWindow);
+ QWindowSystemInterface::handleWindowActivated(focusWindow, Qt::ActiveWindowFocusReason);
else if (!focus && focusWindow == QGuiApplication::focusWindow())
m_focusLostTimer = startTimer(50);
}
diff --git a/src/plugins/platforms/qnx/qqnxvirtualkeyboardpps.cpp b/src/plugins/platforms/qnx/qqnxvirtualkeyboardpps.cpp
index 6f496571fa..0d7bf09599 100644
--- a/src/plugins/platforms/qnx/qqnxvirtualkeyboardpps.cpp
+++ b/src/plugins/platforms/qnx/qqnxvirtualkeyboardpps.cpp
@@ -240,7 +240,7 @@ bool QQnxVirtualKeyboardPps::showKeyboard()
if (!prepareToSend())
return false;
- // NOTE: This must be done everytime the keyboard is shown even if there is no change because
+ // NOTE: This must be done every time the keyboard is shown even if there is no change because
// hiding the keyboard wipes the setting.
applyKeyboardOptions();
diff --git a/src/plugins/platforms/wasm/qtloader.js b/src/plugins/platforms/wasm/qtloader.js
index d73fe3e23a..1180e55f73 100644
--- a/src/plugins/platforms/wasm/qtloader.js
+++ b/src/plugins/platforms/wasm/qtloader.js
@@ -68,7 +68,7 @@
//
// containerElements : [container-element, ...]
// One or more HTML elements. QtLoader will display loader elements
-// on these while loading the applicaton, and replace the loader with a
+// on these while loading the application, and replace the loader with a
// canvas on load complete.
// canvasElements : [canvas-element, ...]
// One or more canvas elements.
@@ -280,7 +280,7 @@ function QtLoader(config)
function fetchThenCompileWasm(response) {
return response.arrayBuffer().then(function(data) {
self.loaderSubState = "Compiling";
- setStatus("Loading") // trigger loaderSubState udpate
+ setStatus("Loading") // trigger loaderSubState update
return WebAssembly.compile(data);
});
}
diff --git a/src/plugins/platforms/wasm/qwasmcompositor.cpp b/src/plugins/platforms/wasm/qwasmcompositor.cpp
index 3883bf3c95..c2d03340d6 100644
--- a/src/plugins/platforms/wasm/qwasmcompositor.cpp
+++ b/src/plugins/platforms/wasm/qwasmcompositor.cpp
@@ -131,8 +131,8 @@ void QWasmCompositor::removeWindow(QWasmWindow *window)
if (!m_windowStack.isEmpty() && !QGuiApplication::focusWindow()) {
auto lastWindow = m_windowStack.last();
lastWindow->requestActivateWindow();
+ notifyTopWindowChanged(lastWindow);
}
- notifyTopWindowChanged(window);
}
void QWasmCompositor::setVisible(QWasmWindow *window, bool visible)
@@ -146,7 +146,7 @@ void QWasmCompositor::setVisible(QWasmWindow *window, bool visible)
if (visible)
compositedWindow.damage = compositedWindow.window->geometry();
else
- m_globalDamage = compositedWindow.window->geometry(); // repaint previosly covered area.
+ m_globalDamage = compositedWindow.window->geometry(); // repaint previously covered area.
requestRedraw();
}
@@ -172,7 +172,7 @@ void QWasmCompositor::lower(QWasmWindow *window)
m_windowStack.removeAll(window);
m_windowStack.prepend(window);
QWasmCompositedWindow &compositedWindow = m_compositedWindows[window];
- m_globalDamage = compositedWindow.window->geometry(); // repaint previosly covered area.
+ m_globalDamage = compositedWindow.window->geometry(); // repaint previously covered area.
notifyTopWindowChanged(window);
}
@@ -554,13 +554,11 @@ void QWasmCompositor::drawTitlebarWindow(QWasmTitleBarOptions tb, QPainter *pain
Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine, tb.titleBarOptionsString);
} // SC_TitleBarLabel
- bool down = false;
QPixmap pixmap;
if (tb.subControls.testFlag(SC_TitleBarCloseButton)
&& tb.flags & Qt::WindowSystemMenuHint) {
ir = titlebarRect(tb, SC_TitleBarCloseButton);
- down = tb.subControls & SC_TitleBarCloseButton && (tb.state & State_Sunken);
pixmap = cachedPixmapFromXPM(qt_close_xpm).scaled(QSize(10, 10));
drawItemPixmap(painter, ir, Qt::AlignCenter, pixmap);
} //SC_TitleBarCloseButton
@@ -569,7 +567,6 @@ void QWasmCompositor::drawTitlebarWindow(QWasmTitleBarOptions tb, QPainter *pain
&& tb.flags & Qt::WindowMaximizeButtonHint
&& !(tb.state & Qt::WindowMaximized)) {
ir = titlebarRect(tb, SC_TitleBarMaxButton);
- down = tb.subControls & SC_TitleBarMaxButton && (tb.state & State_Sunken);
pixmap = cachedPixmapFromXPM(qt_maximize_xpm).scaled(QSize(10, 10));
drawItemPixmap(painter, ir, Qt::AlignCenter, pixmap);
} //SC_TitleBarMaxButton
@@ -582,7 +579,6 @@ void QWasmCompositor::drawTitlebarWindow(QWasmTitleBarOptions tb, QPainter *pain
if (drawNormalButton) {
ir = titlebarRect(tb, SC_TitleBarNormalButton);
- down = tb.subControls & SC_TitleBarNormalButton && (tb.state & State_Sunken);
pixmap = cachedPixmapFromXPM(qt_normalizeup_xpm).scaled( QSize(10, 10));
drawItemPixmap(painter, ir, Qt::AlignCenter, pixmap);
diff --git a/src/plugins/platforms/wasm/qwasmcursor.cpp b/src/plugins/platforms/wasm/qwasmcursor.cpp
index 61204517ce..4b4bb61071 100644
--- a/src/plugins/platforms/wasm/qwasmcursor.cpp
+++ b/src/plugins/platforms/wasm/qwasmcursor.cpp
@@ -41,20 +41,24 @@ using namespace emscripten;
void QWasmCursor::changeCursor(QCursor *windowCursor, QWindow *window)
{
- if (!windowCursor || !window)
+ if (!window)
return;
QScreen *screen = window->screen();
if (!screen)
return;
- // Bitmap and custom cursors are not implemented (will fall back to "auto")
- if (windowCursor->shape() == Qt::BitmapCursor || windowCursor->shape() >= Qt::CustomCursor)
- qWarning() << "QWasmCursor: bitmap and custom cursors are not supported";
+ QByteArray htmlCursorName;
+ if (windowCursor) {
- QByteArray htmlCursorName = cursorShapeToHtml(windowCursor->shape());
+ // Bitmap and custom cursors are not implemented (will fall back to "auto")
+ if (windowCursor->shape() == Qt::BitmapCursor || windowCursor->shape() >= Qt::CustomCursor)
+ qWarning() << "QWasmCursor: bitmap and custom cursors are not supported";
+
+ htmlCursorName = cursorShapeToHtml(windowCursor->shape());
+ }
if (htmlCursorName.isEmpty())
- htmlCursorName = "auto";
+ htmlCursorName = "default";
// Set cursor on the canvas
val canvas = QWasmScreen::get(screen)->canvas();
diff --git a/src/plugins/platforms/wasm/qwasmeventdispatcher.cpp b/src/plugins/platforms/wasm/qwasmeventdispatcher.cpp
index 69a38ecd68..8f9eeecbb0 100644
--- a/src/plugins/platforms/wasm/qwasmeventdispatcher.cpp
+++ b/src/plugins/platforms/wasm/qwasmeventdispatcher.cpp
@@ -148,7 +148,7 @@ void QWasmEventDispatcher::doMaintainTimers()
{
Q_D(QWasmEventDispatcher);
- // This functon schedules native timers in order to wake up to
+ // This function schedules native timers in order to wake up to
// process events and activate Qt timers. This is done using the
// emscripten_async_call() API which schedules a new timer.
// There is unfortunately no way to cancel or update a current
diff --git a/src/plugins/platforms/wasm/qwasmeventtranslator.cpp b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp
index a3e02d9d4a..5f809140f5 100644
--- a/src/plugins/platforms/wasm/qwasmeventtranslator.cpp
+++ b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp
@@ -437,16 +437,13 @@ bool QWasmEventTranslator::processMouse(int eventType, const EmscriptenMouseEven
QPoint globalPoint = screen()->geometry().topLeft() + targetPoint;
QEvent::Type buttonEventType = QEvent::None;
- Qt::MouseButton button = translateMouseButton(mouseEvent->button);
+ Qt::MouseButton button = Qt::NoButton;
Qt::KeyboardModifiers modifiers = translateMouseEventModifier(mouseEvent);
QWindow *window2 = nullptr;
if (resizeMode == QWasmWindow::ResizeNone)
window2 = screen()->compositor()->windowAt(globalPoint, 5);
- if (lastWindow && lastWindow->cursor() != Qt::ArrowCursor) {
- lastWindow->setCursor(Qt::ArrowCursor);
- }
if (window2 == nullptr) {
window2 = lastWindow;
} else {
@@ -460,14 +457,22 @@ bool QWasmEventTranslator::processMouse(int eventType, const EmscriptenMouseEven
switch (eventType) {
case EMSCRIPTEN_EVENT_MOUSEDOWN:
{
+ button = translateMouseButton(mouseEvent->button);
+
if (window2)
window2->requestActivate();
pressedButtons.setFlag(button);
+ pressedWindow = window2;
+ buttonEventType = QEvent::MouseButtonPress;
+
+ // button overview:
+ // 0 = primary mouse button, usually left click
+ // 1 = middle mouse button, usually mouse wheel
+ // 2 = right mouse button, usually right click
+ // from: https://w3c.github.io/uievents/#dom-mouseevent-button
if (mouseEvent->button == 0) {
- pressedWindow = window2;
- buttonEventType = QEvent::MouseButtonPress;
if (!(htmlWindow->m_windowState & Qt::WindowFullScreen) && !(htmlWindow->m_windowState & Qt::WindowMaximized)) {
if (htmlWindow && window2->flags().testFlag(Qt::WindowTitleHint) && htmlWindow->isPointOnTitle(globalPoint))
draggedWindow = window2;
@@ -485,7 +490,8 @@ bool QWasmEventTranslator::processMouse(int eventType, const EmscriptenMouseEven
}
case EMSCRIPTEN_EVENT_MOUSEUP:
{
- pressedButtons.setFlag(translateMouseButton(mouseEvent->button), false);
+ button = translateMouseButton(mouseEvent->button);
+ pressedButtons.setFlag(button, false);
buttonEventType = QEvent::MouseButtonRelease;
QWasmWindow *oldWindow = nullptr;
@@ -873,22 +879,17 @@ QCursor QWasmEventTranslator::cursorForMode(QWasmWindow::ResizeMode m)
case QWasmWindow::ResizeTopLeft:
case QWasmWindow::ResizeBottomRight:
return Qt::SizeFDiagCursor;
- break;
case QWasmWindow::ResizeBottomLeft:
case QWasmWindow::ResizeTopRight:
return Qt::SizeBDiagCursor;
- break;
case QWasmWindow::ResizeTop:
case QWasmWindow::ResizeBottom:
return Qt::SizeVerCursor;
- break;
case QWasmWindow::ResizeLeft:
case QWasmWindow::ResizeRight:
return Qt::SizeHorCursor;
- break;
case QWasmWindow::ResizeNone:
return Qt::ArrowCursor;
- break;
}
return Qt::ArrowCursor;
}
diff --git a/src/plugins/platforms/wasm/qwasmeventtranslator.h b/src/plugins/platforms/wasm/qwasmeventtranslator.h
index 3e772583af..7a87c541de 100644
--- a/src/plugins/platforms/wasm/qwasmeventtranslator.h
+++ b/src/plugins/platforms/wasm/qwasmeventtranslator.h
@@ -82,9 +82,9 @@ private:
QMap <int, QPointF> pressedTouchIds;
private:
- QWindow *draggedWindow;
- QWindow *pressedWindow;
- QWindow *lastWindow;
+ QPointer<QWindow> draggedWindow;
+ QPointer<QWindow> pressedWindow;
+ QPointer<QWindow> lastWindow;
Qt::MouseButtons pressedButtons;
QWasmWindow::ResizeMode resizeMode;
diff --git a/src/plugins/platforms/wasm/qwasmintegration.cpp b/src/plugins/platforms/wasm/qwasmintegration.cpp
index 470deb6d70..3dfd8cfe12 100644
--- a/src/plugins/platforms/wasm/qwasmintegration.cpp
+++ b/src/plugins/platforms/wasm/qwasmintegration.cpp
@@ -146,7 +146,7 @@ QWasmIntegration::QWasmIntegration()
QWasmIntegration::~QWasmIntegration()
{
- // Remove event listenes
+ // Remove event listener
emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, nullptr, EM_TRUE, nullptr);
emscripten::val visualViewport = emscripten::val::global("window")["visualViewport"];
if (!visualViewport.isUndefined()) {
@@ -248,8 +248,8 @@ QVariant QWasmIntegration::styleHint(QPlatformIntegration::StyleHint hint) const
Qt::WindowState QWasmIntegration::defaultWindowState(Qt::WindowFlags flags) const
{
- // Don't maximize dialogs
- if (flags & Qt::Dialog & ~Qt::Window)
+ // Don't maximize dialogs or popups
+ if (flags.testFlag(Qt::Dialog) || flags.testFlag(Qt::Popup))
return Qt::WindowNoState;
return QPlatformIntegration::defaultWindowState(flags);
diff --git a/src/plugins/platforms/wasm/qwasmwindow.cpp b/src/plugins/platforms/wasm/qwasmwindow.cpp
index 1bcc407bc1..15dd9057fa 100644
--- a/src/plugins/platforms/wasm/qwasmwindow.cpp
+++ b/src/plugins/platforms/wasm/qwasmwindow.cpp
@@ -171,7 +171,12 @@ WId QWasmWindow::winId() const
void QWasmWindow::propagateSizeHints()
{
-// get rid of base class warning
+ QRect rect = windowGeometry();
+ if (rect.size().width() < windowMinimumSize().width()
+ && rect.size().height() < windowMinimumSize().height()) {
+ rect.setSize(windowMinimumSize());
+ setGeometry(rect);
+ }
}
void QWasmWindow::injectMousePressed(const QPoint &local, const QPoint &global,
@@ -404,21 +409,26 @@ void QWasmWindow::requestUpdate()
bool QWasmWindow::hasTitleBar() const
{
- auto flags = window()->flags();
+ Qt::WindowFlags flags = window()->flags();
return !(m_windowState & Qt::WindowFullScreen)
&& flags.testFlag(Qt::WindowTitleHint)
- && !flags.testFlag(Qt::Popup)
- && !flags.testFlag(Qt::ToolTip)
+ && !(windowIsPopupType(flags))
&& m_needsCompositor;
}
-bool QWasmWindow::windowIsPopupType(Qt::WindowType type) const
+bool QWasmWindow::windowIsPopupType(Qt::WindowFlags flags) const
{
- if (type == Qt::Widget)
- type = window()->type();
- if (type != Qt::Tool)
- return true;
- return false;
+ if (flags.testFlag(Qt::Tool))
+ return false; // Qt::Tool has the Popup bit set but isn't
+
+ return (flags.testFlag(Qt::Popup));
+}
+
+void QWasmWindow::requestActivateWindow()
+{
+ if (window()->isTopLevel())
+ raise();
+ QPlatformWindow::requestActivateWindow();
}
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wasm/qwasmwindow.h b/src/plugins/platforms/wasm/qwasmwindow.h
index 24b56a28fc..870377ce29 100644
--- a/src/plugins/platforms/wasm/qwasmwindow.h
+++ b/src/plugins/platforms/wasm/qwasmwindow.h
@@ -74,6 +74,7 @@ public:
QRect normalGeometry() const override;
qreal devicePixelRatio() const override;
void requestUpdate() override;
+ void requestActivateWindow() override;
QWasmScreen *platformScreen() const;
void setBackingStore(QWasmBackingStore *store) { m_backingStore = store; }
@@ -123,7 +124,7 @@ protected:
bool m_needsCompositor = false;
friend class QWasmCompositor;
friend class QWasmEventTranslator;
- bool windowIsPopupType(Qt::WindowType type = Qt::Widget) const;
+ bool windowIsPopupType(Qt::WindowFlags flags) const;
};
QT_END_NAMESPACE
#endif // QWASMWINDOW_H
diff --git a/src/plugins/platforms/windows/CMakeLists.txt b/src/plugins/platforms/windows/CMakeLists.txt
index 880fa79ef5..d71d7e547f 100644
--- a/src/plugins/platforms/windows/CMakeLists.txt
+++ b/src/plugins/platforms/windows/CMakeLists.txt
@@ -59,6 +59,10 @@ qt_internal_add_plugin(QWindowsIntegrationPlugin
winmm
winspool
wtsapi32
+ shcore
+ comdlg32
+ d3d9
+ runtimeobject
)
# Resources:
diff --git a/src/plugins/platforms/windows/openglblacklists/default.json b/src/plugins/platforms/windows/openglblacklists/default.json
index e37351f9e0..072acdd115 100644
--- a/src/plugins/platforms/windows/openglblacklists/default.json
+++ b/src/plugins/platforms/windows/openglblacklists/default.json
@@ -93,7 +93,7 @@
},
{
"id": 8,
- "description": "Standard VGA: Insufficent support for OpenGL, D3D9 and D3D11",
+ "description": "Standard VGA: Insufficient support for OpenGL, D3D9 and D3D11",
"vendor_id": "0x0000",
"device_id": ["0x0000"],
"os": {
diff --git a/src/plugins/platforms/windows/qtwindowsglobal.h b/src/plugins/platforms/windows/qtwindowsglobal.h
index 573a8d07c8..3853a0afa4 100644
--- a/src/plugins/platforms/windows/qtwindowsglobal.h
+++ b/src/plugins/platforms/windows/qtwindowsglobal.h
@@ -60,6 +60,10 @@
# define WM_DPICHANGED 0x02E0
#endif
+#ifndef WM_GETDPISCALEDSIZE
+# define WM_GETDPISCALEDSIZE 0x02E4
+#endif
+
// WM_POINTER support from Windows 8 onwards (WINVER >= 0x0602)
#ifndef WM_POINTERUPDATE
# define WM_NCPOINTERUPDATE 0x0241
@@ -129,6 +133,7 @@ enum WindowsEventType // Simplify event types
EnterSizeMoveEvent = WindowEventFlag + 22,
ExitSizeMoveEvent = WindowEventFlag + 23,
PointerActivateWindowEvent = WindowEventFlag + 24,
+ DpiScaledSizeEvent = WindowEventFlag + 25,
MouseEvent = MouseEventFlag + 1,
MouseWheelEvent = MouseEventFlag + 2,
CursorEvent = MouseEventFlag + 3,
@@ -316,6 +321,8 @@ inline QtWindows::WindowsEventType windowsEventType(UINT message, WPARAM wParamI
return HIWORD(wParamIn) ? QtWindows::AcceleratorCommandEvent : QtWindows::MenuCommandEvent;
case WM_DPICHANGED:
return QtWindows::DpiChangedEvent;
+ case WM_GETDPISCALEDSIZE:
+ return QtWindows::DpiScaledSizeEvent;
case WM_ENTERSIZEMOVE:
return QtWindows::EnterSizeMoveEvent;
case WM_EXITSIZEMOVE:
diff --git a/src/plugins/platforms/windows/qwin10helpers.cpp b/src/plugins/platforms/windows/qwin10helpers.cpp
index 9a7fce9cd5..589311a902 100644
--- a/src/plugins/platforms/windows/qwin10helpers.cpp
+++ b/src/plugins/platforms/windows/qwin10helpers.cpp
@@ -40,13 +40,13 @@
#include "qwin10helpers.h"
#include <QtCore/qdebug.h>
-#include <QtCore/qoperatingsystemversion.h>
-#include <QtCore/private/qsystemlibrary_p.h>
+#include <winstring.h>
+#include <roapi.h>
#if defined(Q_CC_MINGW) || defined(Q_CC_CLANG)
# define HAS_UI_VIEW_SETTINGS_INTEROP
// Present from MSVC2015 + SDK 10 onwards
-#elif (!defined(Q_CC_MSVC) || _MSC_VER >= 1900) && NTDDI_VERSION >= 0xa000000
+#elif (!defined(Q_CC_MSVC) || _MSC_VER >= 1900) && WINVER >= 0x0A00
# define HAS_UI_VIEW_SETTINGS_INTEROP
# define HAS_UI_VIEW_SETTINGS
#endif
@@ -96,56 +96,23 @@ public:
QT_BEGIN_NAMESPACE
-// Starting from Windows 10
-struct QWindowsComBaseDLL
-{
- bool init();
- bool isValid() const
- {
- return roGetActivationFactory != nullptr && windowsCreateStringReference != nullptr;
- }
-
- typedef HRESULT (WINAPI *RoGetActivationFactory)(HSTRING, REFIID, void **);
- typedef HRESULT (WINAPI *WindowsCreateStringReference)(PCWSTR, UINT32, HSTRING_HEADER *, HSTRING *);
-
- RoGetActivationFactory roGetActivationFactory = nullptr;
- WindowsCreateStringReference windowsCreateStringReference = nullptr;
-};
-
-static QWindowsComBaseDLL baseComDll;
-
-bool QWindowsComBaseDLL::init()
-{
- if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10 && !isValid()) {
- QSystemLibrary library(QStringLiteral("combase"));
- roGetActivationFactory =
- reinterpret_cast<RoGetActivationFactory>(library.resolve("RoGetActivationFactory"));
- windowsCreateStringReference =
- reinterpret_cast<WindowsCreateStringReference>(library.resolve("WindowsCreateStringReference"));
- }
- return isValid();
-}
-
// Return tablet mode, note: Does not work for GetDesktopWindow().
bool qt_windowsIsTabletMode(HWND hwnd)
{
bool result = false;
- if (!baseComDll.init())
- return false;
-
const wchar_t uiViewSettingsId[] = L"Windows.UI.ViewManagement.UIViewSettings";
HSTRING_HEADER uiViewSettingsIdRefHeader;
HSTRING uiViewSettingsIdHs = nullptr;
const auto uiViewSettingsIdLen = UINT32(sizeof(uiViewSettingsId) / sizeof(uiViewSettingsId[0]) - 1);
- if (FAILED(baseComDll.windowsCreateStringReference(uiViewSettingsId, uiViewSettingsIdLen, &uiViewSettingsIdRefHeader, &uiViewSettingsIdHs)))
+ if (FAILED(WindowsCreateStringReference(uiViewSettingsId, uiViewSettingsIdLen, &uiViewSettingsIdRefHeader, &uiViewSettingsIdHs)))
return false;
IUIViewSettingsInterop *uiViewSettingsInterop = nullptr;
// __uuidof(IUIViewSettingsInterop);
const GUID uiViewSettingsInteropRefId = {0x3694dbf9, 0x8f68, 0x44be,{0x8f, 0xf5, 0x19, 0x5c, 0x98, 0xed, 0xe8, 0xa6}};
- HRESULT hr = baseComDll.roGetActivationFactory(uiViewSettingsIdHs, uiViewSettingsInteropRefId,
+ HRESULT hr = RoGetActivationFactory(uiViewSettingsIdHs, uiViewSettingsInteropRefId,
reinterpret_cast<void **>(&uiViewSettingsInterop));
if (FAILED(hr))
return false;
diff --git a/src/plugins/platforms/windows/qwindowsclipboard.cpp b/src/plugins/platforms/windows/qwindowsclipboard.cpp
index 01377a55e0..73111a13fb 100644
--- a/src/plugins/platforms/windows/qwindowsclipboard.cpp
+++ b/src/plugins/platforms/windows/qwindowsclipboard.cpp
@@ -191,12 +191,9 @@ void QWindowsClipboard::registerViewer()
createDummyWindow(QStringLiteral("ClipboardView"), L"QtClipboardView",
qClipboardViewerWndProc, WS_OVERLAPPED);
- // Try format listener API (Vista onwards) first.
- if (QWindowsContext::user32dll.addClipboardFormatListener && QWindowsContext::user32dll.removeClipboardFormatListener) {
- m_formatListenerRegistered = QWindowsContext::user32dll.addClipboardFormatListener(m_clipboardViewer);
- if (!m_formatListenerRegistered)
- qErrnoWarning("AddClipboardFormatListener() failed.");
- }
+ m_formatListenerRegistered = AddClipboardFormatListener(m_clipboardViewer);
+ if (!m_formatListenerRegistered)
+ qErrnoWarning("AddClipboardFormatListener() failed.");
if (!m_formatListenerRegistered)
m_nextClipboardViewer = SetClipboardViewer(m_clipboardViewer);
@@ -210,7 +207,7 @@ void QWindowsClipboard::unregisterViewer()
{
if (m_clipboardViewer) {
if (m_formatListenerRegistered) {
- QWindowsContext::user32dll.removeClipboardFormatListener(m_clipboardViewer);
+ RemoveClipboardFormatListener(m_clipboardViewer);
m_formatListenerRegistered = false;
} else {
ChangeClipboardChain(m_clipboardViewer, m_nextClipboardViewer);
diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp
index 433e58a97a..ee461ec666 100644
--- a/src/plugins/platforms/windows/qwindowscontext.cpp
+++ b/src/plugins/platforms/windows/qwindowscontext.cpp
@@ -77,11 +77,9 @@
#include <QtCore/qlibraryinfo.h>
#include <QtCore/qstringlist.h>
#include <QtCore/qdebug.h>
-#include <QtCore/qoperatingsystemversion.h>
#include <QtCore/qsysinfo.h>
#include <QtCore/qscopedpointer.h>
#include <QtCore/quuid.h>
-#include <QtCore/private/qsystemlibrary_p.h>
#include <QtCore/private/qwinregistry_p.h>
#include <QtGui/private/qwindowsguieventdispatcher_p.h>
@@ -92,6 +90,7 @@
#include <comdef.h>
#include <dbt.h>
#include <wtsapi32.h>
+#include <shellscalingapi.h>
QT_BEGIN_NAMESPACE
@@ -153,17 +152,15 @@ static inline bool sessionManagerInteractionBlocked() { return false; }
static inline int windowDpiAwareness(HWND hwnd)
{
- return QWindowsContext::user32dll.getWindowDpiAwarenessContext && QWindowsContext::user32dll.getAwarenessFromDpiAwarenessContext
- ? QWindowsContext::user32dll.getAwarenessFromDpiAwarenessContext(QWindowsContext::user32dll.getWindowDpiAwarenessContext(hwnd))
- : -1;
+ return static_cast<int>(GetAwarenessFromDpiAwarenessContext(GetWindowDpiAwarenessContext(hwnd)));
}
// Note: This only works within WM_NCCREATE
static bool enableNonClientDpiScaling(HWND hwnd)
{
bool result = false;
- if (QWindowsContext::user32dll.enableNonClientDpiScaling && windowDpiAwareness(hwnd) == 2) {
- result = QWindowsContext::user32dll.enableNonClientDpiScaling(hwnd) != FALSE;
+ if (windowDpiAwareness(hwnd) == 2) {
+ result = EnableNonClientDpiScaling(hwnd) != FALSE;
if (!result) {
const DWORD errorCode = GetLastError();
qErrnoWarning(int(errorCode), "EnableNonClientDpiScaling() failed for HWND %p (%lu)",
@@ -173,73 +170,6 @@ static bool enableNonClientDpiScaling(HWND hwnd)
return result;
}
-/*!
- \class QWindowsUser32DLL
- \brief Struct that contains dynamically resolved symbols of User32.dll.
-
- The stub libraries shipped with the MinGW compiler miss some of the
- functions. They need to be retrieved dynamically.
-
- In addition, touch-related functions are available only from Windows onwards.
- These need to resolved dynamically for Q_CC_MSVC as well.
-
- \sa QWindowsShell32DLL
-
- \internal
-*/
-
-void QWindowsUser32DLL::init()
-{
- QSystemLibrary library(QStringLiteral("user32"));
- setProcessDPIAware = (SetProcessDPIAware)library.resolve("SetProcessDPIAware");
- setProcessDpiAwarenessContext = (SetProcessDpiAwarenessContext)library.resolve("SetProcessDpiAwarenessContext");
-
- addClipboardFormatListener = (AddClipboardFormatListener)library.resolve("AddClipboardFormatListener");
- removeClipboardFormatListener = (RemoveClipboardFormatListener)library.resolve("RemoveClipboardFormatListener");
-
- getDisplayAutoRotationPreferences = (GetDisplayAutoRotationPreferences)library.resolve("GetDisplayAutoRotationPreferences");
- setDisplayAutoRotationPreferences = (SetDisplayAutoRotationPreferences)library.resolve("SetDisplayAutoRotationPreferences");
-
- enableMouseInPointer = (EnableMouseInPointer)library.resolve("EnableMouseInPointer");
- getPointerType = (GetPointerType)library.resolve("GetPointerType");
- getPointerInfo = (GetPointerInfo)library.resolve("GetPointerInfo");
- getPointerDeviceRects = (GetPointerDeviceRects)library.resolve("GetPointerDeviceRects");
- getPointerTouchInfo = (GetPointerTouchInfo)library.resolve("GetPointerTouchInfo");
- getPointerFrameTouchInfo = (GetPointerFrameTouchInfo)library.resolve("GetPointerFrameTouchInfo");
- getPointerFrameTouchInfoHistory = (GetPointerFrameTouchInfoHistory)library.resolve("GetPointerFrameTouchInfoHistory");
- getPointerPenInfo = (GetPointerPenInfo)library.resolve("GetPointerPenInfo");
- getPointerPenInfoHistory = (GetPointerPenInfoHistory)library.resolve("GetPointerPenInfoHistory");
- skipPointerFrameMessages = (SkipPointerFrameMessages)library.resolve("SkipPointerFrameMessages");
-
- 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");
- systemParametersInfoForDpi = (SystemParametersInfoForDpi)library.resolve("SystemParametersInfoForDpi");
- getDpiForWindow = (GetDpiForWindow)library.resolve("GetDpiForWindow");
- }
-}
-
-bool QWindowsUser32DLL::supportsPointerApi()
-{
- return enableMouseInPointer && getPointerType && getPointerInfo && getPointerDeviceRects
- && getPointerTouchInfo && getPointerFrameTouchInfo && getPointerFrameTouchInfoHistory
- && getPointerPenInfo && getPointerPenInfoHistory && skipPointerFrameMessages;
-}
-
-void QWindowsShcoreDLL::init()
-{
- QSystemLibrary library(QStringLiteral("SHCore"));
- getProcessDpiAwareness = (GetProcessDpiAwareness)library.resolve("GetProcessDpiAwareness");
- setProcessDpiAwareness = (SetProcessDpiAwareness)library.resolve("SetProcessDpiAwareness");
- getDpiForMonitor = (GetDpiForMonitor)library.resolve("GetDpiForMonitor");
-}
-
-QWindowsUser32DLL QWindowsContext::user32dll;
-QWindowsShcoreDLL QWindowsContext::shcoredll;
-
QWindowsContext *QWindowsContext::m_instance = nullptr;
/*!
@@ -285,9 +215,6 @@ bool QWindowsContextPrivate::m_v2DpiAware = false;
QWindowsContextPrivate::QWindowsContextPrivate()
: m_oleInitializeResult(OleInitialize(nullptr))
{
- QWindowsContext::user32dll.init();
- QWindowsContext::shcoredll.init();
-
if (m_pointerHandler.touchDevice() || m_mouseHandler.touchDevice())
m_systemInfo |= QWindowsContext::SI_SupportsTouch;
m_displayContext = GetDC(nullptr);
@@ -402,9 +329,6 @@ bool QWindowsContext::initPointer(unsigned integrationOptions)
if (integrationOptions & QWindowsIntegration::DontUseWMPointer)
return false;
- if (!QWindowsContext::user32dll.supportsPointerApi())
- return false;
-
d->m_systemInfo |= QWindowsContext::SI_SupportsPointer;
return true;
}
@@ -474,10 +398,9 @@ void QWindowsContext::setDetectAltGrModifier(bool a)
int QWindowsContext::processDpiAwareness()
{
- int result;
- if (QWindowsContext::shcoredll.getProcessDpiAwareness
- && SUCCEEDED(QWindowsContext::shcoredll.getProcessDpiAwareness(nullptr, &result))) {
- return result;
+ PROCESS_DPI_AWARENESS result;
+ if (SUCCEEDED(GetProcessDpiAwareness(nullptr, &result))) {
+ return static_cast<int>(result);
}
return -1;
}
@@ -485,27 +408,20 @@ int QWindowsContext::processDpiAwareness()
void QWindowsContext::setProcessDpiAwareness(QtWindows::ProcessDpiAwareness dpiAwareness)
{
qCDebug(lcQpaWindows) << __FUNCTION__ << dpiAwareness;
- if (QWindowsContext::shcoredll.isValid()) {
- const HRESULT hr = QWindowsContext::shcoredll.setProcessDpiAwareness(dpiAwareness);
- // E_ACCESSDENIED means set externally (MSVC manifest or external app loading Qt plugin).
- // Silence warning in that case unless debug is enabled.
- if (FAILED(hr) && (hr != E_ACCESSDENIED || lcQpaWindows().isDebugEnabled())) {
- qWarning().noquote().nospace() << "SetProcessDpiAwareness("
- << dpiAwareness << ") failed: " << QWindowsContext::comErrorString(hr)
- << ", using " << QWindowsContext::processDpiAwareness();
- }
- } else {
- if (dpiAwareness != QtWindows::ProcessDpiUnaware && QWindowsContext::user32dll.setProcessDPIAware) {
- if (!QWindowsContext::user32dll.setProcessDPIAware())
- qErrnoWarning("SetProcessDPIAware() failed");
- }
+ const HRESULT hr = SetProcessDpiAwareness(static_cast<PROCESS_DPI_AWARENESS>(dpiAwareness));
+ // E_ACCESSDENIED means set externally (MSVC manifest or external app loading Qt plugin).
+ // Silence warning in that case unless debug is enabled.
+ if (FAILED(hr) && (hr != E_ACCESSDENIED || lcQpaWindows().isDebugEnabled())) {
+ qWarning().noquote().nospace() << "SetProcessDpiAwareness("
+ << dpiAwareness << ") failed: " << QWindowsContext::comErrorString(hr)
+ << ", using " << QWindowsContext::processDpiAwareness();
}
}
void QWindowsContext::setProcessDpiV2Awareness()
{
qCDebug(lcQpaWindows) << __FUNCTION__;
- const BOOL ok = QWindowsContext::user32dll.setProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
+ const BOOL ok = SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
if (ok) {
QWindowsContextPrivate::m_v2DpiAware = true;
} else {
@@ -1046,8 +962,8 @@ void QWindowsContext::forceNcCalcSize(HWND hwnd)
bool QWindowsContext::systemParametersInfo(unsigned action, unsigned param, void *out,
unsigned dpi)
{
- const BOOL result = QWindowsContext::user32dll.systemParametersInfoForDpi != nullptr && dpi != 0
- ? QWindowsContext::user32dll.systemParametersInfoForDpi(action, param, out, 0, dpi)
+ const BOOL result = dpi != 0
+ ? SystemParametersInfoForDpi(action, param, out, 0, dpi)
: SystemParametersInfo(action, param, out, 0);
return result == TRUE;
}
@@ -1094,8 +1010,7 @@ bool QWindowsContext::shouldHaveNonClientDpiScaling(const QWindow *window)
if (QWindowsContextPrivate::m_v2DpiAware)
return true;
- return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10
- && window->isTopLevel()
+ return window->isTopLevel()
&& !window->property(QWindowsWindow::embeddedNativeParentHandleProperty).isValid()
#if QT_CONFIG(opengl) // /QTBUG-62901, EnableNonClientDpiScaling has problems with GL
&& (window->surfaceType() != QSurface::OpenGLSurface
@@ -1453,34 +1368,12 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
return true;
#endif
} break;
- case QtWindows::DpiChangedEvent: {
-
- const UINT dpi = HIWORD(wParam);
- const qreal scale = qreal(dpi) / qreal(platformWindow->savedDpi());
- platformWindow->setSavedDpi(dpi);
-
- // Send screen change first, so that the new sceen is set during any following resize
- platformWindow->checkForScreenChanged(QWindowsWindow::FromDpiChange);
-
- // Apply the suggested window geometry to the native window. This will make
- // sure the window tracks the mouse cursor during screen change, and also
- // that the window size is scaled according to the DPI change.
- platformWindow->updateFullFrameMargins();
- const auto prcNewWindow = reinterpret_cast<RECT *>(lParam);
- SetWindowPos(hwnd, nullptr, prcNewWindow->left, prcNewWindow->top,
- prcNewWindow->right - prcNewWindow->left,
- prcNewWindow->bottom - prcNewWindow->top, SWP_NOZORDER | SWP_NOACTIVATE);
-
- // Scale child QPlatformWindow size. Windows sends WM_DPICHANGE to top-level windows only.
- for (QWindow *childWindow : platformWindow->window()->findChildren<QWindow *>()) {
- QWindowsWindow *platformChildWindow = static_cast<QWindowsWindow *>(childWindow->handle());
- QRect currentGeometry = platformChildWindow->geometry();
- QRect scaledGeometry = QRect(currentGeometry.topLeft() * scale, currentGeometry.size() * scale);
- platformChildWindow->setGeometry(scaledGeometry);
- }
-
+ case QtWindows::DpiScaledSizeEvent:
+ platformWindow->handleDpiScaledSize(wParam, lParam, result);
+ return true;
+ case QtWindows::DpiChangedEvent:
+ platformWindow->handleDpiChanged(hwnd, wParam, lParam);
return true;
- }
#if QT_CONFIG(sessionmanager)
case QtWindows::QueryEndSessionApplicationEvent: {
QWindowsSessionManager *sessionManager = platformSessionManager();
@@ -1564,7 +1457,7 @@ void QWindowsContext::handleFocusEvent(QtWindows::WindowsEventType et,
}
if (nextActiveWindow != d->m_lastActiveWindow) {
d->m_lastActiveWindow = nextActiveWindow;
- QWindowSystemInterface::handleWindowActivated(nextActiveWindow);
+ QWindowSystemInterface::handleWindowActivated(nextActiveWindow, Qt::ActiveWindowFocusReason);
}
}
diff --git a/src/plugins/platforms/windows/qwindowscontext.h b/src/plugins/platforms/windows/qwindowscontext.h
index 04328a0369..d1d5139fb3 100644
--- a/src/plugins/platforms/windows/qwindowscontext.h
+++ b/src/plugins/platforms/windows/qwindowscontext.h
@@ -82,85 +82,6 @@ class QPoint;
class QKeyEvent;
class QPointingDevice;
-struct QWindowsUser32DLL
-{
- inline void init();
- inline bool supportsPointerApi();
-
- typedef BOOL (WINAPI *EnableMouseInPointer)(BOOL);
- typedef BOOL (WINAPI *GetPointerType)(UINT32, PVOID);
- typedef BOOL (WINAPI *GetPointerInfo)(UINT32, PVOID);
- typedef BOOL (WINAPI *GetPointerDeviceRects)(HANDLE, RECT *, RECT *);
- typedef BOOL (WINAPI *GetPointerTouchInfo)(UINT32, PVOID);
- typedef BOOL (WINAPI *GetPointerFrameTouchInfo)(UINT32, UINT32 *, PVOID);
- typedef BOOL (WINAPI *GetPointerFrameTouchInfoHistory)(UINT32, UINT32 *, UINT32 *, PVOID);
- typedef BOOL (WINAPI *GetPointerPenInfo)(UINT32, PVOID);
- typedef BOOL (WINAPI *GetPointerPenInfoHistory)(UINT32, UINT32 *, PVOID);
- typedef BOOL (WINAPI *SkipPointerFrameMessages)(UINT32);
- typedef BOOL (WINAPI *SetProcessDPIAware)();
- typedef BOOL (WINAPI *SetProcessDpiAwarenessContext)(HANDLE);
- typedef BOOL (WINAPI *AddClipboardFormatListener)(HWND);
- 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);
- typedef BOOL (WINAPI *SystemParametersInfoForDpi)(UINT, UINT, PVOID, UINT, UINT);
- typedef int (WINAPI *GetDpiForWindow)(HWND);
-
- // Windows pointer functions (Windows 8 or later).
- EnableMouseInPointer enableMouseInPointer = nullptr;
- GetPointerType getPointerType = nullptr;
- GetPointerInfo getPointerInfo = nullptr;
- GetPointerDeviceRects getPointerDeviceRects = nullptr;
- GetPointerTouchInfo getPointerTouchInfo = nullptr;
- GetPointerFrameTouchInfo getPointerFrameTouchInfo = nullptr;
- GetPointerFrameTouchInfoHistory getPointerFrameTouchInfoHistory = nullptr;
- GetPointerPenInfo getPointerPenInfo = nullptr;
- GetPointerPenInfoHistory getPointerPenInfoHistory = nullptr;
- SkipPointerFrameMessages skipPointerFrameMessages = nullptr;
-
- // Windows Vista onwards
- SetProcessDPIAware setProcessDPIAware = nullptr;
-
- // Windows 10 version 1607 onwards
- GetDpiForWindow getDpiForWindow = nullptr;
-
- // Windows 10 version 1703 onwards
- SetProcessDpiAwarenessContext setProcessDpiAwarenessContext = nullptr;
-
- // Clipboard listeners are present on Windows Vista onwards
- // but missing in MinGW 4.9 stub libs. Can be removed in MinGW 5.
- AddClipboardFormatListener addClipboardFormatListener = nullptr;
- RemoveClipboardFormatListener removeClipboardFormatListener = nullptr;
-
- // Rotation API
- GetDisplayAutoRotationPreferences getDisplayAutoRotationPreferences = nullptr;
- SetDisplayAutoRotationPreferences setDisplayAutoRotationPreferences = nullptr;
-
- AdjustWindowRectExForDpi adjustWindowRectExForDpi = nullptr;
- EnableNonClientDpiScaling enableNonClientDpiScaling = nullptr;
- GetWindowDpiAwarenessContext getWindowDpiAwarenessContext = nullptr;
- GetAwarenessFromDpiAwarenessContext getAwarenessFromDpiAwarenessContext = nullptr;
- SystemParametersInfoForDpi systemParametersInfoForDpi = nullptr;
-};
-
-// Shell scaling library (Windows 8.1 onwards)
-struct QWindowsShcoreDLL {
- void init();
- inline bool isValid() const { return getProcessDpiAwareness && setProcessDpiAwareness && getDpiForMonitor; }
-
- typedef HRESULT (WINAPI *GetProcessDpiAwareness)(HANDLE,int *);
- typedef HRESULT (WINAPI *SetProcessDpiAwareness)(int);
- typedef HRESULT (WINAPI *GetDpiForMonitor)(HMONITOR,int,UINT *,UINT *);
-
- GetProcessDpiAwareness getProcessDpiAwareness = nullptr;
- SetProcessDpiAwareness setProcessDpiAwareness = nullptr;
- GetDpiForMonitor getDpiForMonitor = nullptr;
-};
-
class QWindowsContext
{
Q_DISABLE_COPY_MOVE(QWindowsContext)
@@ -253,9 +174,6 @@ public:
QWindowsScreenManager &screenManager();
QWindowsTabletSupport *tabletSupport() const;
- static QWindowsUser32DLL user32dll;
- static QWindowsShcoreDLL shcoredll;
-
static QByteArray comErrorString(HRESULT hr);
bool asyncExpose() const;
void setAsyncExpose(bool value);
diff --git a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp
index 0312bde563..e8399bd3d2 100644
--- a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp
+++ b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp
@@ -40,7 +40,7 @@
#define QT_NO_URL_CAST_FROM_STRING 1
#ifndef _WIN32_WINNT
-#define _WIN32_WINNT 0x0601
+#define _WIN32_WINNT 0x0A00
#endif
#include "qwindowscombase.h"
@@ -70,7 +70,6 @@
#include <QtCore/qmutex.h>
#include <QtCore/quuid.h>
#include <QtCore/qtemporaryfile.h>
-#include <QtCore/private/qsystemlibrary_p.h>
#include <algorithm>
#include <vector>
@@ -150,7 +149,7 @@ void eatMouseMove()
Base classes for native dialogs (using the CLSID-based
dialog interfaces "IFileDialog", etc. available from Windows
- Vista on) that mimick the behaviour of their QDialog
+ Vista on) that mimic the behavior of their QDialog
counterparts as close as possible.
Instances of derived classes are controlled by
@@ -1707,9 +1706,6 @@ public slots:
void close() override {}
private:
- typedef BOOL (APIENTRY *PtrGetOpenFileNameW)(LPOPENFILENAMEW);
- typedef BOOL (APIENTRY *PtrGetSaveFileNameW)(LPOPENFILENAMEW);
-
explicit QWindowsXpNativeFileDialog(const OptionsPtr &options, const QWindowsFileDialogSharedData &data);
void populateOpenFileName(OPENFILENAME *ofn, HWND owner) const;
QList<QUrl> execExistingDir(HWND owner);
@@ -1719,27 +1715,11 @@ private:
QString m_title;
QPlatformDialogHelper::DialogCode m_result;
QWindowsFileDialogSharedData m_data;
-
- static PtrGetOpenFileNameW m_getOpenFileNameW;
- static PtrGetSaveFileNameW m_getSaveFileNameW;
};
-QWindowsXpNativeFileDialog::PtrGetOpenFileNameW QWindowsXpNativeFileDialog::m_getOpenFileNameW = nullptr;
-QWindowsXpNativeFileDialog::PtrGetSaveFileNameW QWindowsXpNativeFileDialog::m_getSaveFileNameW = nullptr;
-
QWindowsXpNativeFileDialog *QWindowsXpNativeFileDialog::create(const OptionsPtr &options, const QWindowsFileDialogSharedData &data)
{
- // GetOpenFileNameW() GetSaveFileName() are resolved
- // dynamically as not to create a dependency on Comdlg32, which
- // is used on XP only.
- if (!m_getOpenFileNameW) {
- QSystemLibrary library(QStringLiteral("Comdlg32"));
- m_getOpenFileNameW = (PtrGetOpenFileNameW)(library.resolve("GetOpenFileNameW"));
- m_getSaveFileNameW = (PtrGetSaveFileNameW)(library.resolve("GetSaveFileNameW"));
- }
- if (m_getOpenFileNameW && m_getSaveFileNameW)
- return new QWindowsXpNativeFileDialog(options, data);
- return nullptr;
+ return new QWindowsXpNativeFileDialog(options, data);
}
QWindowsXpNativeFileDialog::QWindowsXpNativeFileDialog(const OptionsPtr &options,
@@ -1903,7 +1883,7 @@ QList<QUrl> QWindowsXpNativeFileDialog::execFileNames(HWND owner, int *selectedF
populateOpenFileName(&ofn, owner);
QList<QUrl> result;
const bool isSave = m_options->acceptMode() == QFileDialogOptions::AcceptSave;
- if (isSave ? m_getSaveFileNameW(&ofn) : m_getOpenFileNameW(&ofn)) {
+ if (isSave ? GetSaveFileNameW(&ofn) : GetOpenFileNameW(&ofn)) {
*selectedFilterIndex = ofn.nFilterIndex - 1;
const QString dir = QDir::cleanPath(QString::fromWCharArray(ofn.lpstrFile));
result.push_back(QUrl::fromLocalFile(dir));
@@ -2045,8 +2025,6 @@ QWindowsNativeColorDialog::QWindowsNativeColorDialog(const SharedPointerColor &c
void QWindowsNativeColorDialog::doExec(HWND owner)
{
- typedef BOOL (WINAPI *ChooseColorWType)(LPCHOOSECOLORW);
-
CHOOSECOLOR chooseColor;
ZeroMemory(&chooseColor, sizeof(chooseColor));
chooseColor.lStructSize = sizeof(chooseColor);
@@ -2059,18 +2037,9 @@ void QWindowsNativeColorDialog::doExec(HWND owner)
m_customColors[c] = qColorToCOLORREF(QColor(qCustomColors[c]));
chooseColor.rgbResult = qColorToCOLORREF(*m_color);
chooseColor.Flags = CC_FULLOPEN | CC_RGBINIT;
- static ChooseColorWType chooseColorW = 0;
- if (!chooseColorW) {
- QSystemLibrary library(QStringLiteral("Comdlg32"));
- chooseColorW = (ChooseColorWType)library.resolve("ChooseColorW");
- }
- if (chooseColorW) {
- m_code = chooseColorW(&chooseColor) ?
- QPlatformDialogHelper::Accepted : QPlatformDialogHelper::Rejected;
- QWindowsDialogs::eatMouseMove();
- } else {
- m_code = QPlatformDialogHelper::Rejected;
- }
+ m_code = ChooseColorW(&chooseColor) ?
+ QPlatformDialogHelper::Accepted : QPlatformDialogHelper::Rejected;
+ QWindowsDialogs::eatMouseMove();
if (m_code == QPlatformDialogHelper::Accepted) {
*m_color = COLORREFToQColor(chooseColor.rgbResult);
for (int c= 0; c < customColorCount; ++c)
diff --git a/src/plugins/platforms/windows/qwindowsdrag.cpp b/src/plugins/platforms/windows/qwindowsdrag.cpp
index f46adf9132..af32c191c3 100644
--- a/src/plugins/platforms/windows/qwindowsdrag.cpp
+++ b/src/plugins/platforms/windows/qwindowsdrag.cpp
@@ -420,7 +420,7 @@ QWindowsOleDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState)
}
/*!
- \brief Give feedback: Change cursor accoding to action.
+ \brief Give feedback: Change cursor according to action.
*/
QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP
diff --git a/src/plugins/platforms/windows/qwindowsglcontext.cpp b/src/plugins/platforms/windows/qwindowsglcontext.cpp
index 1a7be0d271..a3039d3b8e 100644
--- a/src/plugins/platforms/windows/qwindowsglcontext.cpp
+++ b/src/plugins/platforms/windows/qwindowsglcontext.cpp
@@ -1296,7 +1296,7 @@ bool QWindowsGLContext::makeCurrent(QPlatformSurface *surface)
if (const QOpenGLContextData *contextData = findByHWND(m_windowContexts, hwnd)) {
// Repeated calls to wglMakeCurrent when vsync is enabled in the driver will
// often result in 100% cpuload. This check is cheap and avoids the problem.
- // This is reproducable on NVidia cards and Intel onboard chips.
+ // This is reproducible on NVidia cards and Intel onboard chips.
if (QOpenGLStaticContext::opengl32.wglGetCurrentContext() == contextData->renderingContext
&& QOpenGLStaticContext::opengl32.wglGetCurrentDC() == contextData->hdc) {
return true;
diff --git a/src/plugins/platforms/windows/qwindowsinputcontext.cpp b/src/plugins/platforms/windows/qwindowsinputcontext.cpp
index 03be0b9451..4cda4e1efe 100644
--- a/src/plugins/platforms/windows/qwindowsinputcontext.cpp
+++ b/src/plugins/platforms/windows/qwindowsinputcontext.cpp
@@ -47,7 +47,6 @@
#include <QtCore/qobject.h>
#include <QtCore/qrect.h>
#include <QtCore/qtextboundaryfinder.h>
-#include <QtCore/qoperatingsystemversion.h>
#include <QtGui/qevent.h>
#include <QtGui/qtextformat.h>
@@ -285,9 +284,7 @@ void QWindowsInputContext::showInputPanel()
// Only trigger the native OSK if the Qt OSK is not in use.
static bool imModuleEmpty = qEnvironmentVariableIsEmpty("QT_IM_MODULE");
bool nativeVKDisabled = QCoreApplication::testAttribute(Qt::AA_DisableNativeVirtualKeyboard);
- if ((imModuleEmpty && !nativeVKDisabled)
- && QOperatingSystemVersion::current()
- >= QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, 16299)) {
+ if (imModuleEmpty && !nativeVKDisabled) {
ShowCaret(platformWindow->handle());
} else {
HideCaret(platformWindow->handle());
diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp
index 822c11d0f2..397b988e61 100644
--- a/src/plugins/platforms/windows/qwindowsintegration.cpp
+++ b/src/plugins/platforms/windows/qwindowsintegration.cpp
@@ -122,14 +122,6 @@ QT_BEGIN_NAMESPACE
MinGW-w64 provides more complete headers (compared to stock MinGW from mingw.org),
including a considerable part of the Windows SDK.
\endlist
-
- When using a function from the WinAPI, the minimum supported Windows version
- and Windows Embedded support should be checked. If the function is not supported
- on Windows XP or is not present in the MinGW-headers, it should be dynamically
- resolved. For this purpose, QWindowsContext has static structs like
- QWindowsUser32DLL and QWindowsShell32DLL. All function pointers should go to
- these structs to avoid lookups in several places.
-
*/
struct QWindowsIntegrationPrivate
@@ -245,10 +237,8 @@ void QWindowsIntegrationPrivate::parseOptions(QWindowsIntegration *q, const QStr
initOpenGlBlacklistResources();
static bool dpiAwarenessSet = false;
- static bool hasDpiAwarenessContext = QWindowsContext::user32dll.setProcessDpiAwarenessContext != nullptr;
// Default to per-monitor-v2 awareness (if available)
- QtWindows::ProcessDpiAwareness dpiAwareness = hasDpiAwarenessContext ?
- QtWindows::ProcessPerMonitorV2DpiAware : QtWindows::ProcessPerMonitorDpiAware;
+ QtWindows::ProcessDpiAwareness dpiAwareness = QtWindows::ProcessPerMonitorV2DpiAware;
int tabletAbsoluteRange = -1;
DarkModeHandling darkModeHandling;
@@ -267,7 +257,7 @@ void QWindowsIntegrationPrivate::parseOptions(QWindowsIntegration *q, const QStr
if (!QCoreApplication::testAttribute(Qt::AA_PluginApplication)) {
// DpiAwareV2 requires using new API
- if (dpiAwareness == QtWindows::ProcessPerMonitorV2DpiAware && hasDpiAwarenessContext) {
+ if (dpiAwareness == QtWindows::ProcessPerMonitorV2DpiAware) {
m_context.setProcessDpiV2Awareness();
qCDebug(lcQpaWindows)
<< __FUNCTION__ << "DpiAwareness: DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2";
diff --git a/src/plugins/platforms/windows/qwindowsmousehandler.cpp b/src/plugins/platforms/windows/qwindowsmousehandler.cpp
index 5165b4f2e4..9fc04da245 100644
--- a/src/plugins/platforms/windows/qwindowsmousehandler.cpp
+++ b/src/plugins/platforms/windows/qwindowsmousehandler.cpp
@@ -258,8 +258,13 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd,
globalPosition = winEventPosition;
clientPosition = QWindowsGeometryHint::mapFromGlobal(hwnd, globalPosition);
} else {
- clientPosition = winEventPosition;
globalPosition = QWindowsGeometryHint::mapToGlobal(hwnd, winEventPosition);
+ auto targetHwnd = hwnd;
+ if (auto *pw = window->handle())
+ targetHwnd = HWND(pw->winId());
+ clientPosition = targetHwnd == hwnd
+ ? winEventPosition
+ : QWindowsGeometryHint::mapFromGlobal(targetHwnd, globalPosition);
}
// Windows sends a mouse move with no buttons pressed to signal "Enter"
@@ -353,7 +358,7 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd,
auto *platformWindow = static_cast<QWindowsWindow *>(window->handle());
- // If the window was recently resized via mouse doubleclick on the frame or title bar,
+ // If the window was recently resized via mouse double-click on the frame or title bar,
// we don't get WM_LBUTTONDOWN or WM_LBUTTONDBLCLK for the second click,
// but we will get at least one WM_MOUSEMOVE with left button down and the WM_LBUTTONUP,
// which will result undesired mouse press and release events.
@@ -476,7 +481,7 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd,
}
if (!discardEvent && mouseEvent.type != QEvent::None) {
- QWindowSystemInterface::handleMouseEvent(window, device, winEventPosition, globalPosition, buttons,
+ QWindowSystemInterface::handleMouseEvent(window, device, clientPosition, globalPosition, buttons,
mouseEvent.button, mouseEvent.type,
keyModifiers, source);
}
diff --git a/src/plugins/platforms/windows/qwindowsopengltester.cpp b/src/plugins/platforms/windows/qwindowsopengltester.cpp
index 0b1af47a65..59bdb38fad 100644
--- a/src/plugins/platforms/windows/qwindowsopengltester.cpp
+++ b/src/plugins/platforms/windows/qwindowsopengltester.cpp
@@ -55,7 +55,6 @@
#endif
#include <QtCore/qt_windows.h>
-#include <private/qsystemlibrary_p.h>
#include <d3d9.h>
QT_BEGIN_NAMESPACE
@@ -94,19 +93,12 @@ public:
bool retrieveAdapterIdentifier(UINT n, D3DADAPTER_IDENTIFIER9 *adapterIdentifier) const;
private:
- QSystemLibrary m_d3d9lib;
IDirect3D9 *m_direct3D9 = nullptr;
};
-QDirect3D9Handle::QDirect3D9Handle() :
- m_d3d9lib(QStringLiteral("d3d9"))
+QDirect3D9Handle::QDirect3D9Handle()
{
- using PtrDirect3DCreate9 = IDirect3D9 *(WINAPI *)(UINT);
-
- if (m_d3d9lib.load()) {
- if (auto direct3DCreate9 = (PtrDirect3DCreate9)m_d3d9lib.resolve("Direct3DCreate9"))
- m_direct3D9 = direct3DCreate9(D3D_SDK_VERSION);
- }
+ m_direct3D9 = Direct3DCreate9(D3D_SDK_VERSION);
}
QDirect3D9Handle::~QDirect3D9Handle()
diff --git a/src/plugins/platforms/windows/qwindowspointerhandler.cpp b/src/plugins/platforms/windows/qwindowspointerhandler.cpp
index 24d85c76e6..dc74e28a12 100644
--- a/src/plugins/platforms/windows/qwindowspointerhandler.cpp
+++ b/src/plugins/platforms/windows/qwindowspointerhandler.cpp
@@ -37,11 +37,8 @@
**
****************************************************************************/
-#if defined(WINVER) && WINVER < 0x0603
-# undef WINVER
-#endif
-#if !defined(WINVER)
-# define WINVER 0x0603 // Enable pointer functions for MinGW
+#ifndef WINVER
+# define WINVER 0x0A00 // Enable pointer functions for MinGW
#endif
#include "qwindowspointerhandler.h"
@@ -62,7 +59,6 @@
#include <QtGui/private/qguiapplication_p.h>
#include <QtCore/qvarlengtharray.h>
#include <QtCore/qloggingcategory.h>
-#include <QtCore/qoperatingsystemversion.h>
#include <QtCore/qqueue.h>
#include <algorithm>
@@ -90,7 +86,7 @@ bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, Q
*result = 0;
const quint32 pointerId = GET_POINTERID_WPARAM(msg.wParam);
- if (!QWindowsContext::user32dll.getPointerType(pointerId, &m_pointerType)) {
+ if (!GetPointerType(pointerId, &m_pointerType)) {
qWarning() << "GetPointerType() failed:" << qt_error_string();
return false;
}
@@ -104,12 +100,12 @@ bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, Q
}
case QT_PT_TOUCH: {
quint32 pointerCount = 0;
- if (!QWindowsContext::user32dll.getPointerFrameTouchInfo(pointerId, &pointerCount, nullptr)) {
+ if (!GetPointerFrameTouchInfo(pointerId, &pointerCount, nullptr)) {
qWarning() << "GetPointerFrameTouchInfo() failed:" << qt_error_string();
return false;
}
QVarLengthArray<POINTER_TOUCH_INFO, 10> touchInfo(pointerCount);
- if (!QWindowsContext::user32dll.getPointerFrameTouchInfo(pointerId, &pointerCount, touchInfo.data())) {
+ if (!GetPointerFrameTouchInfo(pointerId, &pointerCount, touchInfo.data())) {
qWarning() << "GetPointerFrameTouchInfo() failed:" << qt_error_string();
return false;
}
@@ -122,10 +118,10 @@ bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, Q
// dispatch any skipped frames if event compression is disabled by the app
if (historyCount > 1 && !QCoreApplication::testAttribute(Qt::AA_CompressHighFrequencyEvents)) {
touchInfo.resize(pointerCount * historyCount);
- if (!QWindowsContext::user32dll.getPointerFrameTouchInfoHistory(pointerId,
- &historyCount,
- &pointerCount,
- touchInfo.data())) {
+ if (!GetPointerFrameTouchInfoHistory(pointerId,
+ &historyCount,
+ &pointerCount,
+ touchInfo.data())) {
qWarning() << "GetPointerFrameTouchInfoHistory() failed:" << qt_error_string();
return false;
}
@@ -143,7 +139,7 @@ bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, Q
}
case QT_PT_PEN: {
POINTER_PEN_INFO penInfo;
- if (!QWindowsContext::user32dll.getPointerPenInfo(pointerId, &penInfo)) {
+ if (!GetPointerPenInfo(pointerId, &penInfo)) {
qWarning() << "GetPointerPenInfo() failed:" << qt_error_string();
return false;
}
@@ -155,9 +151,7 @@ bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, Q
|| !QCoreApplication::testAttribute(Qt::AA_CompressTabletEvents))) {
QVarLengthArray<POINTER_PEN_INFO, 10> penInfoHistory(historyCount);
- if (!QWindowsContext::user32dll.getPointerPenInfoHistory(pointerId,
- &historyCount,
- penInfoHistory.data())) {
+ if (!GetPointerPenInfoHistory(pointerId, &historyCount, penInfoHistory.data())) {
qWarning() << "GetPointerPenInfoHistory() failed:" << qt_error_string();
return false;
}
@@ -463,6 +457,8 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd,
{
Q_UNUSED(hwnd);
+ auto *touchInfo = static_cast<POINTER_TOUCH_INFO *>(vTouchInfo);
+
if (et & QtWindows::NonClientEventFlag)
return false; // Let DefWindowProc() handle Non Client messages.
@@ -472,10 +468,19 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd,
if (msg.message == WM_POINTERCAPTURECHANGED) {
QWindowSystemInterface::handleTouchCancelEvent(window, m_touchDevice.data(),
QWindowsKeyMapper::queryKeyboardModifiers());
- m_lastTouchPositions.clear();
+ m_lastTouchPoints.clear();
return true;
}
+ if (msg.message == WM_POINTERLEAVE) {
+ for (quint32 i = 0; i < count; ++i) {
+ const quint32 pointerId = touchInfo[i].pointerInfo.pointerId;
+ int id = m_touchInputIDToTouchPointID.value(pointerId, -1);
+ if (id != -1)
+ m_lastTouchPoints.remove(id);
+ }
+ }
+
// Only handle down/up/update, ignore others like WM_POINTERENTER, WM_POINTERLEAVE, etc.
if (msg.message > WM_POINTERUP)
return false;
@@ -486,8 +491,6 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd,
if (!screen)
return false;
- auto *touchInfo = static_cast<POINTER_TOUCH_INFO *>(vTouchInfo);
-
const QRect screenGeometry = screen->geometry();
QList<QWindowSystemInterface::TouchPoint> touchPoints;
@@ -499,6 +502,7 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd,
<< " count=" << Qt::dec << count;
QEventPoint::States allStates;
+ QSet<int> inputIds;
for (quint32 i = 0; i < count; ++i) {
if (QWindowsContext::verbose > 1)
@@ -511,14 +515,17 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd,
const quint32 pointerId = touchInfo[i].pointerInfo.pointerId;
int id = m_touchInputIDToTouchPointID.value(pointerId, -1);
if (id == -1) {
+ // Start tracking after fingers touch the screen. Ignore bogus updates after touch is released.
+ if ((touchInfo[i].pointerInfo.pointerFlags & POINTER_FLAG_DOWN) == 0)
+ continue;
id = m_touchInputIDToTouchPointID.size();
m_touchInputIDToTouchPointID.insert(pointerId, id);
}
touchPoint.id = id;
touchPoint.pressure = (touchInfo[i].touchMask & TOUCH_MASK_PRESSURE) ?
touchInfo[i].pressure / 1024.0 : 1.0;
- if (m_lastTouchPositions.contains(touchPoint.id))
- touchPoint.normalPosition = m_lastTouchPositions.value(touchPoint.id);
+ if (m_lastTouchPoints.contains(touchPoint.id))
+ touchPoint.normalPosition = m_lastTouchPoints.value(touchPoint.id).normalPosition;
const QPointF screenPos = QPointF(touchInfo[i].pointerInfo.ptPixelLocation.x,
touchInfo[i].pointerInfo.ptPixelLocation.y);
@@ -534,22 +541,36 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd,
if (touchInfo[i].pointerInfo.pointerFlags & POINTER_FLAG_DOWN) {
touchPoint.state = QEventPoint::State::Pressed;
- m_lastTouchPositions.insert(touchPoint.id, touchPoint.normalPosition);
+ m_lastTouchPoints.insert(touchPoint.id, touchPoint);
} else if (touchInfo[i].pointerInfo.pointerFlags & POINTER_FLAG_UP) {
touchPoint.state = QEventPoint::State::Released;
- m_lastTouchPositions.remove(touchPoint.id);
+ m_lastTouchPoints.remove(touchPoint.id);
} else {
touchPoint.state = stationaryTouchPoint ? QEventPoint::State::Stationary : QEventPoint::State::Updated;
- m_lastTouchPositions.insert(touchPoint.id, touchPoint.normalPosition);
+ m_lastTouchPoints.insert(touchPoint.id, touchPoint);
}
allStates |= touchPoint.state;
touchPoints.append(touchPoint);
+ inputIds.insert(touchPoint.id);
// Avoid getting repeated messages for this frame if there are multiple pointerIds
- QWindowsContext::user32dll.skipPointerFrameMessages(touchInfo[i].pointerInfo.pointerId);
+ SkipPointerFrameMessages(touchInfo[i].pointerInfo.pointerId);
+ }
+
+ // Some devices send touches for each finger in a different message/frame, instead of consolidating
+ // them in the same frame as we were expecting. We account for missing unreleased touches here.
+ for (auto tp : qAsConst(m_lastTouchPoints)) {
+ if (!inputIds.contains(tp.id)) {
+ tp.state = QEventPoint::State::Stationary;
+ allStates |= tp.state;
+ touchPoints.append(tp);
+ }
}
+ if (touchPoints.count() == 0)
+ return false;
+
// all touch points released, forget the ids we've seen.
if (allStates == QEventPoint::State::Released)
m_touchInputIDToTouchPointID.clear();
@@ -580,7 +601,7 @@ bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWin
auto *penInfo = static_cast<POINTER_PEN_INFO *>(vPenInfo);
RECT pRect, dRect;
- if (!QWindowsContext::user32dll.getPointerDeviceRects(penInfo->pointerInfo.sourceDevice, &pRect, &dRect))
+ if (!GetPointerDeviceRects(penInfo->pointerInfo.sourceDevice, &pRect, &dRect))
return false;
const auto systemId = (qint64)penInfo->pointerInfo.sourceDevice;
@@ -768,8 +789,13 @@ bool QWindowsPointerHandler::translateMouseEvent(QWindow *window,
globalPos = eventPos;
localPos = QWindowsGeometryHint::mapFromGlobal(hwnd, eventPos);
} else {
- localPos = eventPos;
globalPos = QWindowsGeometryHint::mapToGlobal(hwnd, eventPos);
+ auto targetHwnd = hwnd;
+ if (auto *pw = window->handle())
+ targetHwnd = HWND(pw->winId());
+ localPos = targetHwnd == hwnd
+ ? eventPos
+ : QWindowsGeometryHint::mapFromGlobal(targetHwnd, globalPos);
}
const Qt::KeyboardModifiers keyModifiers = QWindowsKeyMapper::queryKeyboardModifiers();
diff --git a/src/plugins/platforms/windows/qwindowspointerhandler.h b/src/plugins/platforms/windows/qwindowspointerhandler.h
index 5b6179b7fa..376902c530 100644
--- a/src/plugins/platforms/windows/qwindowspointerhandler.h
+++ b/src/plugins/platforms/windows/qwindowspointerhandler.h
@@ -48,6 +48,7 @@
#include <QtCore/qsharedpointer.h>
#include <QtCore/qhash.h>
#include <QtGui/qevent.h>
+#include <qpa/qwindowsysteminterface.h>
QT_BEGIN_NAMESPACE
@@ -88,7 +89,7 @@ private:
QList<QPointingDevicePtr> m_tabletDevices;
QPointingDevicePtr m_activeTabletDevice;
#endif
- QHash<int, QPointF> m_lastTouchPositions;
+ QHash<int, QWindowSystemInterface::TouchPoint> m_lastTouchPoints;
QHash<DWORD, int> m_touchInputIDToTouchPointID;
QPointer<QWindow> m_windowUnderPointer;
QPointer<QWindow> m_currentWindow;
diff --git a/src/plugins/platforms/windows/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp
index 2544dd6200..4474905744 100644
--- a/src/plugins/platforms/windows/qwindowsscreen.cpp
+++ b/src/plugins/platforms/windows/qwindowsscreen.cpp
@@ -56,6 +56,8 @@
#include <QtCore/qdebug.h>
+#include <shellscalingapi.h>
+
QT_BEGIN_NAMESPACE
static inline QDpi deviceDPI(HDC hdc)
@@ -65,12 +67,10 @@ static inline QDpi deviceDPI(HDC hdc)
static inline QDpi monitorDPI(HMONITOR hMonitor)
{
- if (QWindowsContext::shcoredll.isValid()) {
- UINT dpiX;
- UINT dpiY;
- if (SUCCEEDED(QWindowsContext::shcoredll.getDpiForMonitor(hMonitor, 0, &dpiX, &dpiY)))
- return QDpi(dpiX, dpiY);
- }
+ UINT dpiX;
+ UINT dpiY;
+ if (SUCCEEDED(GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY)))
+ return QDpi(dpiX, dpiY);
return {0, 0};
}
@@ -429,62 +429,50 @@ QRect QWindowsScreen::virtualGeometry(const QPlatformScreen *screen) // cf QScre
return result;
}
-enum OrientationPreference : DWORD // matching Win32 API ORIENTATION_PREFERENCE
-{
- orientationPreferenceNone = 0,
- orientationPreferenceLandscape = 0x1,
- orientationPreferencePortrait = 0x2,
- orientationPreferenceLandscapeFlipped = 0x4,
- orientationPreferencePortraitFlipped = 0x8
-};
-
bool QWindowsScreen::setOrientationPreference(Qt::ScreenOrientation o)
{
bool result = false;
- if (QWindowsContext::user32dll.setDisplayAutoRotationPreferences) {
- DWORD orientationPreference = 0;
- switch (o) {
- case Qt::PrimaryOrientation:
- orientationPreference = orientationPreferenceNone;
- break;
- case Qt::PortraitOrientation:
- orientationPreference = orientationPreferencePortrait;
- break;
- case Qt::LandscapeOrientation:
- orientationPreference = orientationPreferenceLandscape;
- break;
- case Qt::InvertedPortraitOrientation:
- orientationPreference = orientationPreferencePortraitFlipped;
- break;
- case Qt::InvertedLandscapeOrientation:
- orientationPreference = orientationPreferenceLandscapeFlipped;
- break;
- }
- result = QWindowsContext::user32dll.setDisplayAutoRotationPreferences(orientationPreference);
+ ORIENTATION_PREFERENCE orientationPreference = ORIENTATION_PREFERENCE_NONE;
+ switch (o) {
+ case Qt::PrimaryOrientation:
+ break;
+ case Qt::PortraitOrientation:
+ orientationPreference = ORIENTATION_PREFERENCE_PORTRAIT;
+ break;
+ case Qt::LandscapeOrientation:
+ orientationPreference = ORIENTATION_PREFERENCE_LANDSCAPE;
+ break;
+ case Qt::InvertedPortraitOrientation:
+ orientationPreference = ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED;
+ break;
+ case Qt::InvertedLandscapeOrientation:
+ orientationPreference = ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED;
+ break;
}
+ result = SetDisplayAutoRotationPreferences(orientationPreference);
return result;
}
Qt::ScreenOrientation QWindowsScreen::orientationPreference()
{
Qt::ScreenOrientation result = Qt::PrimaryOrientation;
- if (QWindowsContext::user32dll.getDisplayAutoRotationPreferences) {
- DWORD orientationPreference = 0;
- if (QWindowsContext::user32dll.getDisplayAutoRotationPreferences(&orientationPreference)) {
- switch (orientationPreference) {
- case orientationPreferenceLandscape:
- result = Qt::LandscapeOrientation;
- break;
- case orientationPreferencePortrait:
- result = Qt::PortraitOrientation;
- break;
- case orientationPreferenceLandscapeFlipped:
- result = Qt::InvertedLandscapeOrientation;
- break;
- case orientationPreferencePortraitFlipped:
- result = Qt::InvertedPortraitOrientation;
- break;
- }
+ ORIENTATION_PREFERENCE orientationPreference = ORIENTATION_PREFERENCE_NONE;
+ if (GetDisplayAutoRotationPreferences(&orientationPreference)) {
+ switch (orientationPreference) {
+ case ORIENTATION_PREFERENCE_NONE:
+ break;
+ case ORIENTATION_PREFERENCE_LANDSCAPE:
+ result = Qt::LandscapeOrientation;
+ break;
+ case ORIENTATION_PREFERENCE_PORTRAIT:
+ result = Qt::PortraitOrientation;
+ break;
+ case ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED:
+ result = Qt::InvertedLandscapeOrientation;
+ break;
+ case ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED:
+ result = Qt::InvertedPortraitOrientation;
+ break;
}
}
return result;
diff --git a/src/plugins/platforms/windows/qwindowsscreen.h b/src/plugins/platforms/windows/qwindowsscreen.h
index 2052648933..319e998c80 100644
--- a/src/plugins/platforms/windows/qwindowsscreen.h
+++ b/src/plugins/platforms/windows/qwindowsscreen.h
@@ -86,7 +86,7 @@ public:
QImage::Format format() const override { return m_data.format; }
QSizeF physicalSize() const override { return m_data.physicalSizeMM; }
QDpi logicalDpi() const override { return m_data.dpi; }
- QDpi logicalBaseDpi() const override { return QDpi(96, 96); }
+ QDpi logicalBaseDpi() const override { return QDpi(baseDpi, baseDpi); }
qreal devicePixelRatio() const override { return 1.0; }
qreal refreshRate() const override { return m_data.refreshRateHz; }
QString name() const override { return m_data.name; }
@@ -115,6 +115,7 @@ public:
const QWindowsScreenData &data() const { return m_data; }
static QRect virtualGeometry(const QPlatformScreen *screen);
+ static inline int baseDpi = 96;
private:
QWindowsScreenData m_data;
diff --git a/src/plugins/platforms/windows/qwindowsservices.cpp b/src/plugins/platforms/windows/qwindowsservices.cpp
index fe2d8a36c0..6c3e4a9bad 100644
--- a/src/plugins/platforms/windows/qwindowsservices.cpp
+++ b/src/plugins/platforms/windows/qwindowsservices.cpp
@@ -119,7 +119,7 @@ static inline QString mailCommand()
// QTBUG-57816: As of Windows 10, if there is no mail client installed, an entry like
// "rundll32.exe .. url.dll,MailToProtocolHandler %l" is returned. Launching it
// silently fails or brings up a broken dialog after a long time, so exclude it and
- // fall back to ShellExecute() which brings up the URL assocation dialog.
+ // fall back to ShellExecute() which brings up the URL association dialog.
if (command.isEmpty() || command.contains(u",MailToProtocolHandler"))
return QString();
wchar_t expandedCommand[MAX_PATH] = {0};
diff --git a/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp b/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp
index 7c473e66b6..0a96d5a0f5 100644
--- a/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp
+++ b/src/plugins/platforms/windows/qwindowssystemtrayicon.cpp
@@ -37,18 +37,12 @@
**
****************************************************************************/
-#if defined(WINVER) && WINVER < 0x0601
-# undef WINVER
-#endif
-#if !defined(WINVER)
-# define WINVER 0x0601 // required for NOTIFYICONDATA_V2_SIZE, ChangeWindowMessageFilterEx() (MinGW 5.3)
+#ifndef WINVER
+# define WINVER 0x0A00 // required for NOTIFYICONDATA_V2_SIZE, ChangeWindowMessageFilterEx() (MinGW 5.3)
#endif
-#if defined(NTDDI_VERSION) && NTDDI_VERSION < 0x06010000
-# undef NTDDI_VERSION
-#endif
-#if !defined(NTDDI_VERSION)
-# define NTDDI_VERSION 0x06010000 // required for Shell_NotifyIconGetRect (MinGW 5.3)
+#ifndef NTDDI_VERSION
+# define NTDDI_VERSION 0x0A00000B // required for Shell_NotifyIconGetRect (MinGW 5.3)
#endif
#include "qwindowssystemtrayicon.h"
diff --git a/src/plugins/platforms/windows/qwindowstheme.cpp b/src/plugins/platforms/windows/qwindowstheme.cpp
index 6fbf4183da..9ee6bbd79a 100644
--- a/src/plugins/platforms/windows/qwindowstheme.cpp
+++ b/src/plugins/platforms/windows/qwindowstheme.cpp
@@ -38,9 +38,8 @@
****************************************************************************/
// SHSTOCKICONINFO is only available since Vista
-#if _WIN32_WINNT < 0x0601
-# undef _WIN32_WINNT
-# define _WIN32_WINNT 0x0601
+#ifndef _WIN32_WINNT
+# define _WIN32_WINNT 0x0A00
#endif
#include "qwindowstheme.h"
@@ -64,7 +63,6 @@
#include <QtCore/qcoreapplication.h>
#include <QtCore/qdebug.h>
#include <QtCore/qtextstream.h>
-#include <QtCore/qoperatingsystemversion.h>
#include <QtCore/qsysinfo.h>
#include <QtCore/qcache.h>
#include <QtCore/qthread.h>
@@ -1054,9 +1052,7 @@ bool QWindowsTheme::useNativeMenus()
bool QWindowsTheme::queryDarkMode()
{
- if (QOperatingSystemVersion::current()
- < QOperatingSystemVersion(QOperatingSystemVersion::Windows, 10, 0, 17763)
- || queryHighContrast()) {
+ if (queryHighContrast()) {
return false;
}
const auto setting = QWinRegistryKey(HKEY_CURRENT_USER, LR"(Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)")
diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp
index a65ac482b2..53fbc167da 100644
--- a/src/plugins/platforms/windows/qwindowswindow.cpp
+++ b/src/plugins/platforms/windows/qwindowswindow.cpp
@@ -37,11 +37,8 @@
**
****************************************************************************/
-#if defined(WINVER) && WINVER < 0x0601
-# undef WINVER
-#endif
-#if !defined(WINVER)
-# define WINVER 0x0601 // Enable touch functions for MinGW
+#ifndef WINVER
+# define WINVER 0x0A00 // Enable touch functions for MinGW
#endif
#include "qwindowswindow.h"
@@ -68,7 +65,6 @@
#include <QtGui/qwindow.h>
#include <QtGui/qregion.h>
#include <QtGui/qopenglcontext.h>
-#include <private/qsystemlibrary_p.h>
#include <private/qwindow_p.h> // QWINDOWSIZE_MAX
#include <private/qguiapplication_p.h>
#include <private/qhighdpiscaling_p.h>
@@ -76,7 +72,6 @@
#include <QtCore/qdebug.h>
#include <QtCore/qlibraryinfo.h>
-#include <QtCore/qoperatingsystemversion.h>
#include <dwmapi.h>
@@ -84,6 +79,8 @@
#include "qwindowsvulkaninstance.h"
#endif
+#include <shellscalingapi.h>
+
QT_BEGIN_NAMESPACE
using QWindowCreationContextPtr = QSharedPointer<QWindowCreationContext>;
@@ -439,18 +436,14 @@ static inline void updateGLWindowSettings(const QWindow *w, HWND hwnd, Qt::Windo
static QMargins invisibleMargins(QPoint screenPoint)
{
- if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10) {
- POINT pt = {screenPoint.x(), screenPoint.y()};
- if (HMONITOR hMonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONULL)) {
- if (QWindowsContext::shcoredll.isValid()) {
- UINT dpiX;
- UINT dpiY;
- if (SUCCEEDED(QWindowsContext::shcoredll.getDpiForMonitor(hMonitor, 0, &dpiX, &dpiY))) {
- const qreal sc = (dpiX - 96) / 96.0;
- const int gap = 7 + qRound(5*sc) - int(sc);
- return QMargins(gap, 0, gap, gap);
- }
- }
+ POINT pt = {screenPoint.x(), screenPoint.y()};
+ if (HMONITOR hMonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONULL)) {
+ UINT dpiX;
+ UINT dpiY;
+ if (SUCCEEDED(GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY))) {
+ const qreal sc = (dpiX - 96) / 96.0;
+ const int gap = 7 + qRound(5*sc) - int(sc);
+ return QMargins(gap, 0, gap, gap);
}
}
return QMargins();
@@ -933,12 +926,9 @@ QMargins QWindowsGeometryHint::frameOnPrimaryScreen(HWND hwnd)
QMargins QWindowsGeometryHint::frame(DWORD style, DWORD exStyle, qreal dpi)
{
- if (QWindowsContext::user32dll.adjustWindowRectExForDpi == nullptr)
- return frameOnPrimaryScreen(style, exStyle);
RECT rect = {0,0,0,0};
style &= ~DWORD(WS_OVERLAPPED); // Not permitted, see docs.
- if (QWindowsContext::user32dll.adjustWindowRectExForDpi(&rect, style, FALSE, exStyle,
- unsigned(qRound(dpi))) == FALSE) {
+ if (AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle, unsigned(qRound(dpi))) == FALSE) {
qErrnoWarning("%s: AdjustWindowRectExForDpi failed", __FUNCTION__);
}
const QMargins result(qAbs(rect.left), qAbs(rect.top),
@@ -968,8 +958,7 @@ QMargins QWindowsGeometryHint::frame(const QWindow *w, const QRect &geometry,
{
if (!w->isTopLevel() || w->flags().testFlag(Qt::FramelessWindowHint))
return {};
- if (!QWindowsContext::user32dll.adjustWindowRectExForDpi
- || QWindowsScreenManager::isSingleScreen()
+ if (QWindowsScreenManager::isSingleScreen()
|| !QWindowsContext::shouldHaveNonClientDpiScaling(w)) {
return frameOnPrimaryScreen(style, exStyle);
}
@@ -1345,6 +1334,7 @@ void QWindowCreationContext::applyToMinMaxInfo(MINMAXINFO *mmi) const
const char *QWindowsWindow::embeddedNativeParentHandleProperty = "_q_embedded_native_parent_handle";
const char *QWindowsWindow::hasBorderInFullScreenProperty = "_q_has_border_in_fullscreen";
bool QWindowsWindow::m_borderInFullScreenDefault = false;
+bool QWindowsWindow::m_inSetgeometry = false;
QWindowsWindow::QWindowsWindow(QWindow *aWindow, const QWindowsWindowData &data) :
QWindowsBaseWindow(aWindow),
@@ -1419,8 +1409,7 @@ void QWindowsWindow::initialize()
if (obtainedScreen && screen() != obtainedScreen)
QWindowSystemInterface::handleWindowScreenChanged<QWindowSystemInterface::SynchronousDelivery>(w, obtainedScreen->screen());
}
- QWindowsWindow::setSavedDpi(QWindowsContext::user32dll.getDpiForWindow ?
- QWindowsContext::user32dll.getDpiForWindow(handle()) : 96);
+ QWindowsWindow::setSavedDpi(GetDpiForWindow(handle()));
}
QSurfaceFormat QWindowsWindow::format() const
@@ -1833,6 +1822,68 @@ void QWindowsWindow::handleCompositionSettingsChanged()
}
}
+void QWindowsWindow::handleDpiScaledSize(WPARAM wParam, LPARAM lParam, LRESULT *result)
+{
+ // We want to keep QWindow's device independent size constant across the
+ // DPI change. To accomplish this, scale QPlatformWindow's native size
+ // by the change of DPI (e.g. 120 -> 144 = 1.2), also taking any scale
+ // factor rounding into account. The win32 window size includes the margins;
+ // add the margins for the new DPI to the window size.
+ const int dpi = int(wParam);
+ const qreal scale = QHighDpiScaling::roundScaleFactor(qreal(dpi) / QWindowsScreen::baseDpi) /
+ QHighDpiScaling::roundScaleFactor(qreal(savedDpi()) / QWindowsScreen::baseDpi);
+ const QMargins margins = QWindowsGeometryHint::frame(style(), exStyle(), dpi);
+ const QSize windowSize = (geometry().size() * scale).grownBy(margins);
+ SIZE *size = reinterpret_cast<SIZE *>(lParam);
+ size->cx = windowSize.width();
+ size->cy = windowSize.height();
+ *result = true; // Inform Windows that we've set a size
+}
+
+void QWindowsWindow::handleDpiChanged(HWND hwnd, WPARAM wParam, LPARAM lParam)
+{
+ const UINT dpi = HIWORD(wParam);
+ const qreal scale = qreal(dpi) / qreal(savedDpi());
+ setSavedDpi(dpi);
+
+ // Send screen change first, so that the new screen is set during any following resize
+ checkForScreenChanged(QWindowsWindow::FromDpiChange);
+
+ // We get WM_DPICHANGED in one of two situations:
+ //
+ // 1. The DPI change is a "spontaneous" DPI change as a result of e.g.
+ // the user dragging the window to a new screen. In this case Windows
+ // first sends WM_GETDPISCALEDSIZE, where we set the new window size,
+ // followed by this event where we apply the suggested window geometry
+ // to the native window. This will make sure the window tracks the mouse
+ // cursor during screen change, and also that the window size is scaled
+ // according to the DPI change.
+ //
+ // 2. The DPI change is a result of a setGeometry() call. In this case
+ // Qt has already scaled the window size for the new DPI. Further, Windows
+ // does not call WM_GETDPISCALEDSIZE, and also applies its own scaling
+ // to the already scaled window size. Since there is no need to set the
+ // window geometry again, and the provided geometry is incorrect, we omit
+ // making the SetWindowPos() call.
+ if (!m_inSetgeometry) {
+ updateFullFrameMargins();
+ const auto prcNewWindow = reinterpret_cast<RECT *>(lParam);
+ SetWindowPos(hwnd, nullptr, prcNewWindow->left, prcNewWindow->top,
+ prcNewWindow->right - prcNewWindow->left,
+ prcNewWindow->bottom - prcNewWindow->top, SWP_NOZORDER | SWP_NOACTIVATE);
+ }
+
+ // Scale child QPlatformWindow size. Windows sends WM_DPICHANGE to top-level windows only.
+ for (QWindow *childWindow : window()->findChildren<QWindow *>()) {
+ QWindowsWindow *platformChildWindow = static_cast<QWindowsWindow *>(childWindow->handle());
+ if (!platformChildWindow)
+ continue;
+ QRect currentGeometry = platformChildWindow->geometry();
+ QRect scaledGeometry = QRect(currentGeometry.topLeft() * scale, currentGeometry.size() * scale);
+ platformChildWindow->setGeometry(scaledGeometry);
+ }
+}
+
static QRect normalFrameGeometry(HWND hwnd)
{
WINDOWPLACEMENT wp;
@@ -1903,6 +1954,8 @@ static QString msgUnableToSetGeometry(const QWindowsWindow *platformWindow,
void QWindowsWindow::setGeometry(const QRect &rectIn)
{
+ QBoolBlocker b(m_inSetgeometry);
+
QRect rect = rectIn;
// This means it is a call from QWindow::setFramePosition() and
// the coordinates include the frame (size is still the contents rectangle).
@@ -2079,7 +2132,7 @@ HDC QWindowsWindow::getDC()
}
/*!
- Relases the HDC for the window or does nothing in
+ Releases the HDC for the window or does nothing in
case it was obtained from WinAPI BeginPaint within a WM_PAINT event.
\sa getDC()
@@ -2419,6 +2472,11 @@ void QWindowsWindow::propagateSizeHints()
bool QWindowsWindow::handleGeometryChangingMessage(MSG *message, const QWindow *qWindow, const QMargins &margins)
{
auto *windowPos = reinterpret_cast<WINDOWPOS *>(message->lParam);
+
+ // Tell Windows to discard the entire contents of the client area, as re-using
+ // parts of the client area would lead to jitter during resize.
+ windowPos->flags |= SWP_NOCOPYBITS;
+
if ((windowPos->flags & SWP_NOZORDER) == 0) {
if (QWindowsWindow *platformWindow = QWindowsWindow::windowsWindowOf(qWindow)) {
QWindow *parentWindow = qWindow->parent();
@@ -2431,7 +2489,7 @@ bool QWindowsWindow::handleGeometryChangingMessage(MSG *message, const QWindow *
}
if (!qWindow->isTopLevel()) // Implement hasHeightForWidth().
return false;
- if ((windowPos->flags & (SWP_NOCOPYBITS | SWP_NOSIZE)))
+ if (windowPos->flags & SWP_NOSIZE)
return false;
const QRect suggestedFrameGeometry(windowPos->x, windowPos->y,
windowPos->cx, windowPos->cy);
@@ -3027,7 +3085,7 @@ void *QWindowsWindow::surface(void *nativeConfig, int *err)
#elif defined(QT_NO_OPENGL)
Q_UNUSED(err);
Q_UNUSED(nativeConfig);
- return 0;
+ return nullptr;
#endif
#ifndef QT_NO_OPENGL
if (!m_surface) {
diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h
index ea170aeee1..8b9e304e9b 100644
--- a/src/plugins/platforms/windows/qwindowswindow.h
+++ b/src/plugins/platforms/windows/qwindowswindow.h
@@ -318,6 +318,8 @@ public:
void handleResized(int wParam);
void handleHidden();
void handleCompositionSettingsChanged();
+ void handleDpiScaledSize(WPARAM wParam, LPARAM lParam, LRESULT *result);
+ void handleDpiChanged(HWND hwnd, WPARAM wParam, LPARAM lParam);
static void displayChanged();
static void settingsChanged();
@@ -416,6 +418,7 @@ private:
VkSurfaceKHR m_vkSurface = VK_NULL_HANDLE;
#endif
static bool m_borderInFullScreenDefault;
+ static bool m_inSetgeometry;
};
#ifndef QT_NO_DEBUG_STREAM
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp
index 171a3c268b..7082fe9bd3 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp
@@ -75,7 +75,7 @@ QT_BEGIN_NAMESPACE
using namespace QWindowsUiAutomation;
-// Returns a cached instance of the provider for a specific acessible interface.
+// Returns a cached instance of the provider for a specific accessible interface.
QWindowsUiaMainProvider *QWindowsUiaMainProvider::providerForAccessible(QAccessibleInterface *accessible)
{
if (!accessible)
@@ -440,7 +440,7 @@ HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pR
// Control type converted from role.
auto controlType = roleToControlTypeId(accessible->role());
- // The native OSK should be disbled if the Qt OSK is in use,
+ // The native OSK should be disabled if the Qt OSK is in use,
// or if disabled via application attribute.
static bool imModuleEmpty = qEnvironmentVariableIsEmpty("QT_IM_MODULE");
bool nativeVKDisabled = QCoreApplication::testAttribute(Qt::AA_DisableNativeVirtualKeyboard);
diff --git a/src/plugins/platforms/xcb/CMakeLists.txt b/src/plugins/platforms/xcb/CMakeLists.txt
index 48c44a92ab..c73b2de73f 100644
--- a/src/plugins/platforms/xcb/CMakeLists.txt
+++ b/src/plugins/platforms/xcb/CMakeLists.txt
@@ -111,7 +111,7 @@ qt_internal_extend_target(XcbQpaPrivate CONDITION QT_FEATURE_dlopen
${CMAKE_DL_LIBS}
)
-qt_internal_extend_target(XcbQpaPrivate CONDITION CLANG AND NOT ICC
+qt_internal_extend_target(XcbQpaPrivate CONDITION CLANG
COMPILE_OPTIONS
-ftemplate-depth=1024
)
diff --git a/src/plugins/platforms/xcb/gl_integrations/qxcbglintegrationfactory.cpp b/src/plugins/platforms/xcb/gl_integrations/qxcbglintegrationfactory.cpp
index d69d969783..8412663e3c 100644
--- a/src/plugins/platforms/xcb/gl_integrations/qxcbglintegrationfactory.cpp
+++ b/src/plugins/platforms/xcb/gl_integrations/qxcbglintegrationfactory.cpp
@@ -50,23 +50,8 @@ QT_BEGIN_NAMESPACE
Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
(QXcbGlIntegrationFactoryInterface_iid, QLatin1String("/xcbglintegrations"), Qt::CaseInsensitive))
-#if QT_CONFIG(library)
-Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, directLoader,
- (QXcbGlIntegrationFactoryInterface_iid, QLatin1String(""), Qt::CaseInsensitive))
-#endif // QT_CONFIG(library)
-
-QXcbGlIntegration *QXcbGlIntegrationFactory::create(const QString &platform, const QString &pluginPath)
+QXcbGlIntegration *QXcbGlIntegrationFactory::create(const QString &platform)
{
-#if QT_CONFIG(library)
- // Try loading the plugin from pluginPath first:
- if (!pluginPath.isEmpty()) {
- QCoreApplication::addLibraryPath(pluginPath);
- if (QXcbGlIntegration *ret = qLoadPlugin<QXcbGlIntegration, QXcbGlIntegrationPlugin>(directLoader(), platform))
- return ret;
- }
-#else
- Q_UNUSED(pluginPath);
-#endif
return qLoadPlugin<QXcbGlIntegration, QXcbGlIntegrationPlugin>(loader(), platform);
}
diff --git a/src/plugins/platforms/xcb/gl_integrations/qxcbglintegrationfactory.h b/src/plugins/platforms/xcb/gl_integrations/qxcbglintegrationfactory.h
index c4aa0a3d8d..30ddeb2297 100644
--- a/src/plugins/platforms/xcb/gl_integrations/qxcbglintegrationfactory.h
+++ b/src/plugins/platforms/xcb/gl_integrations/qxcbglintegrationfactory.h
@@ -49,7 +49,7 @@ class QXcbGlIntegration;
class QXcbGlIntegrationFactory
{
public:
- static QXcbGlIntegration *create(const QString &name, const QString &platformPluginPath = QString());
+ static QXcbGlIntegration *create(const QString &name);
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/nativepainting/qtessellator.cpp b/src/plugins/platforms/xcb/nativepainting/qtessellator.cpp
index 388f5aba76..3ea4afe54c 100644
--- a/src/plugins/platforms/xcb/nativepainting/qtessellator.cpp
+++ b/src/plugins/platforms/xcb/nativepainting/qtessellator.cpp
@@ -268,7 +268,7 @@ QTessellatorPrivate::Edge::Edge(const QTessellatorPrivate::Vertices &vertices, i
//
// WARNING: It's absolutely critical that the intersect() and isLeftOf() methods use
-// exactly the same algorithm to calulate yi. It's also important to be sure the algorithms
+// exactly the same algorithm to calculate yi. It's also important to be sure the algorithms
// are transitive (ie. the conditions below are true for all input data):
//
// a.intersect(b) == b.intersect(a)
diff --git a/src/plugins/platforms/xcb/qxcbbackingstore.cpp b/src/plugins/platforms/xcb/qxcbbackingstore.cpp
index bfcd1b2320..94edd87eb7 100644
--- a/src/plugins/platforms/xcb/qxcbbackingstore.cpp
+++ b/src/plugins/platforms/xcb/qxcbbackingstore.cpp
@@ -479,24 +479,28 @@ bool QXcbBackingStoreImage::scroll(const QRegion &area, int dx, int dy)
const QRect bounds(QPoint(), size());
const QRegion scrollArea(area & bounds);
const QPoint delta(dx, dy);
+ const QRegion destinationRegion = scrollArea.translated(delta).intersected(bounds);
if (m_clientSideScroll) {
if (m_qimage.isNull())
return false;
if (hasShm())
- preparePaint(scrollArea);
+ preparePaint(destinationRegion);
for (const QRect &rect : scrollArea)
qt_scrollRectInImage(m_qimage, rect, delta);
} else {
- if (hasShm())
- shmPutImage(m_xcb_pixmap, m_pendingFlush.intersected(scrollArea));
- else
- flushPixmap(scrollArea);
-
ensureGC(m_xcb_pixmap);
+ if (hasShm()) {
+ QRegion partialFlushRegion = m_pendingFlush.intersected(scrollArea);
+ shmPutImage(m_xcb_pixmap, partialFlushRegion);
+ m_pendingFlush -= partialFlushRegion;
+ } else {
+ flushPixmap(scrollArea);
+ }
+
for (const QRect &src : scrollArea) {
const QRect dst = src.translated(delta).intersected(bounds);
xcb_copy_area(xcb_connection(),
@@ -507,14 +511,13 @@ bool QXcbBackingStoreImage::scroll(const QRegion &area, int dx, int dy)
dst.x(), dst.y(),
dst.width(), dst.height());
}
- }
- m_scrolledRegion |= scrollArea.translated(delta).intersected(bounds);
- if (hasShm()) {
- m_pendingFlush -= scrollArea;
- m_pendingFlush -= m_scrolledRegion;
+ if (hasShm())
+ m_pendingFlush -= destinationRegion;
}
+ m_scrolledRegion |= destinationRegion;
+
return true;
}
@@ -842,7 +845,18 @@ QImage QXcbBackingStore::toImage() const
// If the backingstore is rgbSwapped, return the internal image type here.
if (!m_rgbImage.isNull())
return m_rgbImage;
- return m_image && m_image->image() ? *m_image->image() : QImage();
+
+ if (!m_image || !m_image->image())
+ return QImage();
+
+ m_image->flushScrolledRegion(true);
+
+ QImage image = *m_image->image();
+
+ // Return an image that does not share QImageData with the original image,
+ // even if they both point to the same data of the m_xcb_image, otherwise
+ // painting to m_qimage would detach it from the m_xcb_image data.
+ return QImage(image.constBits(), image.width(), image.height(), image.format());
}
QPlatformGraphicsBuffer *QXcbBackingStore::graphicsBuffer() const
diff --git a/src/plugins/platforms/xcb/qxcbclipboard.cpp b/src/plugins/platforms/xcb/qxcbclipboard.cpp
index 21bc710987..54fdcf620b 100644
--- a/src/plugins/platforms/xcb/qxcbclipboard.cpp
+++ b/src/plugins/platforms/xcb/qxcbclipboard.cpp
@@ -71,7 +71,7 @@ public:
break;
default:
- qWarning("QXcbClipboardMime: Internal error: Unsupported clipboard mode");
+ qCWarning(lcQpaClipboard, "QXcbClipboardMime: Internal error: Unsupported clipboard mode");
break;
}
}
@@ -83,7 +83,7 @@ public:
bool isEmpty() const
{
- return m_clipboard->getSelectionOwner(modeAtom) == XCB_NONE;
+ return m_clipboard->connection()->selectionOwner(modeAtom) == XCB_NONE;
}
protected:
@@ -231,14 +231,15 @@ QXcbClipboard::QXcbClipboard(QXcbConnection *c)
m_clientClipboard[QClipboard::Selection] = nullptr;
m_timestamp[QClipboard::Clipboard] = XCB_CURRENT_TIME;
m_timestamp[QClipboard::Selection] = XCB_CURRENT_TIME;
- m_owner = connection()->getQtSelectionOwner();
if (connection()->hasXFixes()) {
const uint32_t mask = XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER |
XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY |
XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE;
- xcb_xfixes_select_selection_input_checked(xcb_connection(), m_owner, XCB_ATOM_PRIMARY, mask);
- xcb_xfixes_select_selection_input_checked(xcb_connection(), m_owner, atom(QXcbAtom::CLIPBOARD), mask);
+ xcb_xfixes_select_selection_input_checked(xcb_connection(), connection()->qtSelectionOwner(),
+ XCB_ATOM_PRIMARY, mask);
+ xcb_xfixes_select_selection_input_checked(xcb_connection(), connection()->qtSelectionOwner(),
+ atom(QXcbAtom::CLIPBOARD), mask);
}
// xcb_change_property_request_t and xcb_get_property_request_t are the same size
@@ -253,19 +254,21 @@ QXcbClipboard::~QXcbClipboard()
m_timestamp[QClipboard::Selection] != XCB_CURRENT_TIME) {
// First we check if there is a clipboard manager.
- auto reply = Q_XCB_REPLY(xcb_get_selection_owner, xcb_connection(), atom(QXcbAtom::CLIPBOARD_MANAGER));
- if (reply && reply->owner != XCB_NONE) {
+ if (connection()->selectionOwner(atom(QXcbAtom::CLIPBOARD_MANAGER)) != XCB_NONE) {
// we delete the property so the manager saves all TARGETS.
- xcb_delete_property(xcb_connection(), m_owner, atom(QXcbAtom::_QT_SELECTION));
- xcb_convert_selection(xcb_connection(), m_owner, atom(QXcbAtom::CLIPBOARD_MANAGER), atom(QXcbAtom::SAVE_TARGETS),
+ xcb_delete_property(xcb_connection(), connection()->qtSelectionOwner(),
+ atom(QXcbAtom::_QT_SELECTION));
+ xcb_convert_selection(xcb_connection(), connection()->qtSelectionOwner(),
+ atom(QXcbAtom::CLIPBOARD_MANAGER), atom(QXcbAtom::SAVE_TARGETS),
atom(QXcbAtom::_QT_SELECTION), connection()->time());
connection()->sync();
// waiting until the clipboard manager fetches the content.
- if (auto event = waitForClipboardEvent(m_owner, XCB_SELECTION_NOTIFY, true)) {
+ if (auto event = waitForClipboardEvent(connection()->qtSelectionOwner(),
+ XCB_SELECTION_NOTIFY, true)) {
free(event);
} else {
- qWarning("QXcbClipboard: Unable to receive an event from the "
+ qCWarning(lcQpaClipboard, "QXcbClipboard: Unable to receive an event from the "
"clipboard manager in a reasonable time");
}
}
@@ -289,11 +292,6 @@ bool QXcbClipboard::handlePropertyNotify(const xcb_generic_event_t *event)
return (*it)->updateIncrementalProperty(propertyNotify);
}
-xcb_window_t QXcbClipboard::getSelectionOwner(xcb_atom_t atom) const
-{
- return connection()->getSelectionOwner(atom);
-}
-
xcb_atom_t QXcbClipboard::atomForMode(QClipboard::Mode mode) const
{
if (mode == QClipboard::Clipboard)
@@ -319,8 +317,8 @@ QMimeData * QXcbClipboard::mimeData(QClipboard::Mode mode)
if (mode > QClipboard::Selection)
return nullptr;
- xcb_window_t clipboardOwner = getSelectionOwner(atomForMode(mode));
- if (clipboardOwner == owner()) {
+ xcb_window_t clipboardOwner = connection()->selectionOwner(atomForMode(mode));
+ if (clipboardOwner == connection()->qtSelectionOwner()) {
return m_clientClipboard[mode];
} else {
if (!m_xClipboard[mode])
@@ -362,7 +360,7 @@ void QXcbClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode)
connection()->setTime(connection()->getTimestamp());
if (data) {
- newOwner = owner();
+ newOwner = connection()->qtSelectionOwner();
m_clientClipboard[mode] = data;
m_timestamp[mode] = connection()->time();
@@ -370,8 +368,8 @@ void QXcbClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode)
xcb_set_selection_owner(xcb_connection(), newOwner, modeAtom, connection()->time());
- if (getSelectionOwner(modeAtom) != newOwner) {
- qWarning("QXcbClipboard::setMimeData: Cannot set X11 selection owner");
+ if (connection()->selectionOwner(modeAtom) != newOwner) {
+ qCWarning(lcQpaClipboard, "QXcbClipboard::setMimeData: Cannot set X11 selection owner");
}
emitChanged(mode);
@@ -386,10 +384,11 @@ bool QXcbClipboard::supportsMode(QClipboard::Mode mode) const
bool QXcbClipboard::ownsMode(QClipboard::Mode mode) const
{
- if (m_owner == XCB_NONE || mode > QClipboard::Selection)
+ if (connection()->qtSelectionOwner() == XCB_NONE || mode > QClipboard::Selection)
return false;
- Q_ASSERT(m_timestamp[mode] == XCB_CURRENT_TIME || getSelectionOwner(atomForMode(mode)) == m_owner);
+ Q_ASSERT(m_timestamp[mode] == XCB_CURRENT_TIME
+ || connection()->selectionOwner(atomForMode(mode)) == connection()->qtSelectionOwner());
return m_timestamp[mode] != XCB_CURRENT_TIME;
}
@@ -438,11 +437,6 @@ void QXcbClipboard::setRequestor(xcb_window_t window)
m_requestor = window;
}
-xcb_window_t QXcbClipboard::owner() const
-{
- return m_owner;
-}
-
xcb_atom_t QXcbClipboard::sendTargetsSelection(QMimeData *d, xcb_window_t window, xcb_atom_t property)
{
QList<xcb_atom_t> types;
@@ -521,7 +515,7 @@ void QXcbClipboard::handleSelectionClearRequest(xcb_selection_clear_event_t *eve
// XGetSelectionOwner(dpy, XA_PRIMARY),
// xevent->xselectionclear.time, d->timestamp);
- xcb_window_t newOwner = getSelectionOwner(event->selection);
+ xcb_window_t newOwner = connection()->selectionOwner(event->selection);
/* If selection ownership was given up voluntarily from QClipboard::clear(), then we do nothing here
since its already handled in setMimeData. Otherwise, the event must have come from another client
@@ -538,7 +532,7 @@ void QXcbClipboard::handleSelectionClearRequest(xcb_selection_clear_event_t *eve
void QXcbClipboard::handleSelectionRequest(xcb_selection_request_event_t *req)
{
if (requestor() && req->requestor == requestor()) {
- qWarning("QXcbClipboard: Selection request should be caught before");
+ qCWarning(lcQpaClipboard, "QXcbClipboard: Selection request should be caught before");
return;
}
@@ -553,7 +547,8 @@ void QXcbClipboard::handleSelectionRequest(xcb_selection_request_event_t *req)
QMimeData *d;
QClipboard::Mode mode = modeForAtom(req->selection);
if (mode > QClipboard::Selection) {
- qWarning() << "QXcbClipboard: Unknown selection" << connection()->atomName(req->selection);
+ qCWarning(lcQpaClipboard, "QXcbClipboard: Unknown selection %s",
+ connection()->atomName(req->selection).constData());
xcb_send_event(xcb_connection(), false, req->requestor, XCB_EVENT_MASK_NO_EVENT, (const char *)&event);
return;
}
@@ -561,14 +556,14 @@ void QXcbClipboard::handleSelectionRequest(xcb_selection_request_event_t *req)
d = m_clientClipboard[mode];
if (!d) {
- qWarning("QXcbClipboard: Cannot transfer data, no data available");
+ qCWarning(lcQpaClipboard, "QXcbClipboard: Cannot transfer data, no data available");
xcb_send_event(xcb_connection(), false, req->requestor, XCB_EVENT_MASK_NO_EVENT, (const char *)&event);
return;
}
if (m_timestamp[mode] == XCB_CURRENT_TIME // we don't own the selection anymore
|| (req->time != XCB_CURRENT_TIME && req->time < m_timestamp[mode])) {
- qWarning("QXcbClipboard: SelectionRequest too old");
+ qCDebug(lcQpaClipboard, "QXcbClipboard: SelectionRequest too old");
xcb_send_event(xcb_connection(), false, req->requestor, XCB_EVENT_MASK_NO_EVENT, (const char *)&event);
return;
}
@@ -623,7 +618,7 @@ void QXcbClipboard::handleSelectionRequest(xcb_selection_request_event_t *req)
property, XCB_ATOM_INTEGER, 32, 1, &m_timestamp[mode]);
ret = property;
} else {
- qWarning("QXcbClipboard: Invalid data timestamp");
+ qCWarning(lcQpaClipboard, "QXcbClipboard: Invalid data timestamp");
}
} else if (target == targetsAtom) {
ret = sendTargetsSelection(d, req->requestor, property);
@@ -667,7 +662,7 @@ void QXcbClipboard::handleXFixesSelectionRequest(xcb_xfixes_selection_notify_eve
// Note1: Here we care only about the xfixes events that come from other processes.
// Note2: If the QClipboard::clear() is issued, event->owner is XCB_NONE,
// so we check selection_timestamp to not handle our own QClipboard::clear().
- if (event->owner != owner() && event->selection_timestamp > m_timestamp[mode]) {
+ if (event->owner != connection()->qtSelectionOwner() && event->selection_timestamp > m_timestamp[mode]) {
if (!m_xClipboard[mode]) {
m_xClipboard[mode].reset(new QXcbClipboardMime(mode, this));
} else {
@@ -728,7 +723,7 @@ bool QXcbClipboard::clipboardReadProperty(xcb_window_t win, xcb_atom_t property,
// recover -- this shouldn't normally happen, but it doesn't
// hurt to be defensive
if ((int)(buffer_offset + length) > buffer->size()) {
- qWarning("QXcbClipboard: buffer overflow");
+ qCWarning(lcQpaClipboard, "QXcbClipboard: buffer overflow");
length = buffer->size() - buffer_offset;
// escape loop
@@ -788,8 +783,7 @@ xcb_generic_event_t *QXcbClipboard::waitForClipboardEvent(xcb_window_t window, i
const QXcbEventNode *flushedTailNode = queue->flushedTail();
if (checkManager) {
- auto reply = Q_XCB_REPLY(xcb_get_selection_owner, xcb_connection(), atom(QXcbAtom::CLIPBOARD_MANAGER));
- if (!reply || reply->owner == XCB_NONE)
+ if (connection()->selectionOwner(atom(QXcbAtom::CLIPBOARD_MANAGER)) == XCB_NONE)
return nullptr;
}
diff --git a/src/plugins/platforms/xcb/qxcbclipboard.h b/src/plugins/platforms/xcb/qxcbclipboard.h
index 51ae0dc1ee..a33f43c435 100644
--- a/src/plugins/platforms/xcb/qxcbclipboard.h
+++ b/src/plugins/platforms/xcb/qxcbclipboard.h
@@ -97,8 +97,6 @@ public:
xcb_window_t requestor() const;
void setRequestor(xcb_window_t window);
- xcb_window_t owner() const;
-
void handleSelectionRequest(xcb_selection_request_event_t *event);
void handleSelectionClearRequest(xcb_selection_clear_event_t *event);
void handleXFixesSelectionRequest(xcb_xfixes_selection_notify_event_t *event);
@@ -110,7 +108,6 @@ public:
bool handlePropertyNotify(const xcb_generic_event_t *event);
- xcb_window_t getSelectionOwner(xcb_atom_t atom) const;
QByteArray getSelection(xcb_atom_t selection, xcb_atom_t target, xcb_atom_t property, xcb_timestamp_t t = 0);
int increment() const { return m_maxPropertyRequestDataBytes; }
@@ -133,7 +130,6 @@ private:
xcb_timestamp_t m_timestamp[2];
xcb_window_t m_requestor = XCB_NONE;
- xcb_window_t m_owner = XCB_NONE;
static const int clipboard_timeout;
diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp
index b5b9b72ca8..191d5af26c 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection.cpp
@@ -763,7 +763,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
if (xkb_event->any.deviceID == m_keyboard->coreDeviceId()) {
switch (xkb_event->any.xkbType) {
// XkbNewKkdNotify and XkbMapNotify together capture all sorts of keymap
- // updates (e.g. xmodmap, xkbcomp, setxkbmap), with minimal redundent recompilations.
+ // updates (e.g. xmodmap, xkbcomp, setxkbmap), with minimal redundant recompilations.
case XCB_XKB_STATE_NOTIFY:
m_keyboard->updateXKBState(&xkb_event->state_notify);
break;
@@ -773,7 +773,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
case XCB_XKB_NEW_KEYBOARD_NOTIFY: {
xcb_xkb_new_keyboard_notify_event_t *ev = &xkb_event->new_keyboard_notify;
if (ev->changed & XCB_XKB_NKN_DETAIL_KEYCODES)
- m_keyboard->updateKeymap();
+ m_keyboard->updateKeymap(ev);
break;
}
default:
@@ -868,12 +868,18 @@ xcb_timestamp_t QXcbConnection::getTimestamp()
return timestamp;
}
-xcb_window_t QXcbConnection::getSelectionOwner(xcb_atom_t atom) const
+xcb_window_t QXcbConnection::selectionOwner(xcb_atom_t atom) const
{
- return Q_XCB_REPLY(xcb_get_selection_owner, xcb_connection(), atom)->owner;
+ auto reply = Q_XCB_REPLY(xcb_get_selection_owner, xcb_connection(), atom);
+ if (!reply) {
+ qCDebug(lcQpaXcb) << "failed to query selection owner";
+ return XCB_NONE;
+ }
+
+ return reply->owner;
}
-xcb_window_t QXcbConnection::getQtSelectionOwner()
+xcb_window_t QXcbConnection::qtSelectionOwner()
{
if (!m_qtSelectionOwner) {
xcb_screen_t *xcbScreen = primaryVirtualDesktop()->screen();
diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h
index b0b26085d7..943f380d5c 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.h
+++ b/src/plugins/platforms/xcb/qxcbconnection.h
@@ -191,8 +191,8 @@ public:
inline void setNetWmUserTime(xcb_timestamp_t t) { if (timeGreaterThan(t, m_netWmUserTime)) m_netWmUserTime = t; }
xcb_timestamp_t getTimestamp();
- xcb_window_t getSelectionOwner(xcb_atom_t atom) const;
- xcb_window_t getQtSelectionOwner();
+ xcb_window_t selectionOwner(xcb_atom_t atom) const;
+ xcb_window_t qtSelectionOwner();
void setButtonState(Qt::MouseButton button, bool down);
Qt::MouseButtons buttonState() const { return m_buttonState; }
@@ -299,6 +299,8 @@ private:
TouchDeviceData *populateTouchDevices(void *info, QXcbScrollingDevicePrivate *scrollingDeviceP);
TouchDeviceData *touchDeviceForId(int id);
void xi2HandleEvent(xcb_ge_event_t *event);
+ void xi2HandleGesturePinchEvent(void *event);
+ void xi2HandleGestureSwipeEvent(void *event);
void xi2HandleHierarchyEvent(void *event);
void xi2HandleDeviceChangedEvent(void *event);
void xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindow);
@@ -373,6 +375,10 @@ private:
QXcbWindow *m_mouseGrabber = nullptr;
QXcbWindow *m_mousePressWindow = nullptr;
+#if QT_CONFIG(gestures)
+ qreal m_lastPinchScale = 0;
+#endif
+
xcb_window_t m_clientLeader = 0;
QByteArray m_startupId;
QXcbSystemTrayTracker *m_systemTrayTracker = nullptr;
diff --git a/src/plugins/platforms/xcb/qxcbconnection_basic.cpp b/src/plugins/platforms/xcb/qxcbconnection_basic.cpp
index 6482395f86..921ddf574b 100644
--- a/src/plugins/platforms/xcb/qxcbconnection_basic.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection_basic.cpp
@@ -176,7 +176,13 @@ xcb_atom_t QXcbBasicConnection::internAtom(const char *name)
if (!name || *name == 0)
return XCB_NONE;
- return Q_XCB_REPLY(xcb_intern_atom, m_xcbConnection, false, strlen(name), name)->atom;
+ auto reply = Q_XCB_REPLY(xcb_intern_atom, m_xcbConnection, false, strlen(name), name);
+ if (!reply) {
+ qCDebug(lcQpaXcb) << "failed to query intern atom: " << name;
+ return XCB_NONE;
+ }
+
+ return reply->atom;
}
QByteArray QXcbBasicConnection::atomName(xcb_atom_t atom)
@@ -352,7 +358,9 @@ void QXcbBasicConnection::initializeXInput2()
return;
}
- auto xinputQuery = Q_XCB_REPLY(xcb_input_xi_query_version, m_xcbConnection, 2, 2);
+ // depending on whether bundled xcb is used we may support different XCB protocol versions.
+ auto xinputQuery = Q_XCB_REPLY(xcb_input_xi_query_version, m_xcbConnection,
+ 2, XCB_INPUT_MINOR_VERSION);
if (!xinputQuery || xinputQuery->major_version != 2) {
qCWarning(lcQpaXcb, "X server does not support XInput 2");
return;
diff --git a/src/plugins/platforms/xcb/qxcbconnection_basic.h b/src/plugins/platforms/xcb/qxcbconnection_basic.h
index d6eb962f56..baadcb840b 100644
--- a/src/plugins/platforms/xcb/qxcbconnection_basic.h
+++ b/src/plugins/platforms/xcb/qxcbconnection_basic.h
@@ -103,6 +103,7 @@ public:
bool isAtLeastXI21() const { return m_xi2Enabled && m_xi2Minor >= 1; }
bool isAtLeastXI22() const { return m_xi2Enabled && m_xi2Minor >= 2; }
+ bool isAtLeastXI24() const { return m_xi2Enabled && m_xi2Minor >= 4; }
bool isXIEvent(xcb_generic_event_t *event) const;
bool isXIType(xcb_generic_event_t *event, uint16_t type) const;
diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
index c0bd2c6165..5ddf545d51 100644
--- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
@@ -51,13 +51,25 @@
#include <xcb/xinput.h>
+#define QT_XCB_HAS_TOUCHPAD_GESTURES (XCB_INPUT_MINOR_VERSION >= 4)
+
using qt_xcb_input_device_event_t = xcb_input_button_press_event_t;
+#if QT_XCB_HAS_TOUCHPAD_GESTURES
+using qt_xcb_input_pinch_event_t = xcb_input_gesture_pinch_begin_event_t;
+using qt_xcb_input_swipe_event_t = xcb_input_gesture_swipe_begin_event_t;
+#endif
struct qt_xcb_input_event_mask_t {
xcb_input_event_mask_t header;
- uint32_t mask;
+ alignas(4) uint8_t mask[8] = {}; // up to 2 units of 4 bytes
};
+static inline void setXcbMask(uint8_t* mask, int bit)
+{
+ // note that XI protocol always uses little endian for masks over the wire
+ mask[bit >> 3] |= 1 << (bit & 7);
+}
+
void QXcbConnection::xi2SelectStateEvents()
{
// These state events do not depend on a specific X window, but are global
@@ -65,9 +77,9 @@ void QXcbConnection::xi2SelectStateEvents()
qt_xcb_input_event_mask_t xiEventMask;
xiEventMask.header.deviceid = XCB_INPUT_DEVICE_ALL;
xiEventMask.header.mask_len = 1;
- xiEventMask.mask = XCB_INPUT_XI_EVENT_MASK_HIERARCHY;
- xiEventMask.mask |= XCB_INPUT_XI_EVENT_MASK_DEVICE_CHANGED;
- xiEventMask.mask |= XCB_INPUT_XI_EVENT_MASK_PROPERTY;
+ setXcbMask(xiEventMask.mask, XCB_INPUT_HIERARCHY);
+ setXcbMask(xiEventMask.mask, XCB_INPUT_DEVICE_CHANGED);
+ setXcbMask(xiEventMask.mask, XCB_INPUT_PROPERTY);
xcb_input_xi_select_events(xcb_connection(), rootWindow(), 1, &xiEventMask.header);
}
@@ -76,23 +88,33 @@ void QXcbConnection::xi2SelectDeviceEvents(xcb_window_t window)
if (window == rootWindow())
return;
- uint32_t bitMask = XCB_INPUT_XI_EVENT_MASK_BUTTON_PRESS;
- bitMask |= XCB_INPUT_XI_EVENT_MASK_BUTTON_RELEASE;
- bitMask |= XCB_INPUT_XI_EVENT_MASK_MOTION;
+ qt_xcb_input_event_mask_t mask;
+
+ setXcbMask(mask.mask, XCB_INPUT_BUTTON_PRESS);
+ setXcbMask(mask.mask, XCB_INPUT_BUTTON_RELEASE);
+ setXcbMask(mask.mask, XCB_INPUT_MOTION);
// There is a check for enter/leave events in plain xcb enter/leave event handler,
// core enter/leave events will be ignored in this case.
- bitMask |= XCB_INPUT_XI_EVENT_MASK_ENTER;
- bitMask |= XCB_INPUT_XI_EVENT_MASK_LEAVE;
+ setXcbMask(mask.mask, XCB_INPUT_ENTER);
+ setXcbMask(mask.mask, XCB_INPUT_LEAVE);
if (isAtLeastXI22()) {
- bitMask |= XCB_INPUT_XI_EVENT_MASK_TOUCH_BEGIN;
- bitMask |= XCB_INPUT_XI_EVENT_MASK_TOUCH_UPDATE;
- bitMask |= XCB_INPUT_XI_EVENT_MASK_TOUCH_END;
+ setXcbMask(mask.mask, XCB_INPUT_TOUCH_BEGIN);
+ setXcbMask(mask.mask, XCB_INPUT_TOUCH_UPDATE);
+ setXcbMask(mask.mask, XCB_INPUT_TOUCH_END);
+ }
+#if QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES
+ if (isAtLeastXI24()) {
+ setXcbMask(mask.mask, XCB_INPUT_GESTURE_PINCH_BEGIN);
+ setXcbMask(mask.mask, XCB_INPUT_GESTURE_PINCH_UPDATE);
+ setXcbMask(mask.mask, XCB_INPUT_GESTURE_PINCH_END);
+ setXcbMask(mask.mask, XCB_INPUT_GESTURE_SWIPE_BEGIN);
+ setXcbMask(mask.mask, XCB_INPUT_GESTURE_SWIPE_UPDATE);
+ setXcbMask(mask.mask, XCB_INPUT_GESTURE_SWIPE_END);
}
+#endif // QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES
- qt_xcb_input_event_mask_t mask;
mask.header.deviceid = XCB_INPUT_DEVICE_ALL;
- mask.header.mask_len = 1;
- mask.mask = bitMask;
+ mask.header.mask_len = 2;
xcb_void_cookie_t cookie =
xcb_input_xi_select_events_checked(xcb_connection(), window, 1, &mask.header);
xcb_generic_error_t *error = xcb_request_check(xcb_connection(), cookie);
@@ -329,6 +351,9 @@ void QXcbConnection::xi2SetupSlavePointerDevice(void *info, bool removeExisting,
qCDebug(lcQpaXInputDevices) << " it's a keyboard";
break;
case XCB_INPUT_DEVICE_CLASS_TYPE_TOUCH:
+#if QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES
+ case XCB_INPUT_DEVICE_CLASS_TYPE_GESTURE:
+#endif // QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES
// will be handled in populateTouchDevices()
break;
default:
@@ -549,6 +574,18 @@ QXcbConnection::TouchDeviceData *QXcbConnection::populateTouchDevices(void *info
}
break;
}
+#if QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES
+ case XCB_INPUT_DEVICE_CLASS_TYPE_GESTURE: {
+ // Note that gesture devices can only be touchpads (i.e. dependent devices in XInput
+ // naming convention). According to XI 2.4, the same device can't have touch and
+ // gesture device classes.
+ auto *gci = reinterpret_cast<xcb_input_gesture_class_t *>(classinfo);
+ maxTouchPoints = gci->num_touches;
+ qCDebug(lcQpaXInputDevices, " has gesture class");
+ type = QInputDevice::DeviceType::TouchPad;
+ break;
+ }
+#endif // QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES
case XCB_INPUT_DEVICE_CLASS_TYPE_VALUATOR: {
auto *vci = reinterpret_cast<xcb_input_valuator_class_t *>(classinfo);
const QXcbAtom::Atom valuatorAtom = qatom(vci->label);
@@ -674,6 +711,18 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
sourceDeviceId = xiDeviceEvent->sourceid; // use the actual device id instead of the master
break;
}
+#if QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES
+ case XCB_INPUT_GESTURE_PINCH_BEGIN:
+ case XCB_INPUT_GESTURE_PINCH_UPDATE:
+ case XCB_INPUT_GESTURE_PINCH_END:
+ xi2HandleGesturePinchEvent(event);
+ return;
+ case XCB_INPUT_GESTURE_SWIPE_BEGIN:
+ case XCB_INPUT_GESTURE_SWIPE_UPDATE:
+ case XCB_INPUT_GESTURE_SWIPE_END:
+ xi2HandleGestureSwipeEvent(event);
+ return;
+#endif // QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES
case XCB_INPUT_ENTER:
case XCB_INPUT_LEAVE: {
xiEnterEvent = reinterpret_cast<xcb_input_enter_event_t *>(event);
@@ -974,22 +1023,33 @@ bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab)
bool ok = false;
if (grab) { // grab
- uint32_t mask = XCB_INPUT_XI_EVENT_MASK_BUTTON_PRESS
- | XCB_INPUT_XI_EVENT_MASK_BUTTON_RELEASE
- | XCB_INPUT_XI_EVENT_MASK_MOTION
- | XCB_INPUT_XI_EVENT_MASK_ENTER
- | XCB_INPUT_XI_EVENT_MASK_LEAVE;
+ uint8_t mask[8] = {};
+ setXcbMask(mask, XCB_INPUT_BUTTON_PRESS);
+ setXcbMask(mask, XCB_INPUT_BUTTON_RELEASE);
+ setXcbMask(mask, XCB_INPUT_MOTION);
+ setXcbMask(mask, XCB_INPUT_ENTER);
+ setXcbMask(mask, XCB_INPUT_LEAVE);
if (isAtLeastXI22()) {
- mask |= XCB_INPUT_XI_EVENT_MASK_TOUCH_BEGIN;
- mask |= XCB_INPUT_XI_EVENT_MASK_TOUCH_UPDATE;
- mask |= XCB_INPUT_XI_EVENT_MASK_TOUCH_END;
+ setXcbMask(mask, XCB_INPUT_TOUCH_BEGIN);
+ setXcbMask(mask, XCB_INPUT_TOUCH_UPDATE);
+ setXcbMask(mask, XCB_INPUT_TOUCH_END);
+ }
+#if QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES
+ if (isAtLeastXI24()) {
+ setXcbMask(mask, XCB_INPUT_GESTURE_PINCH_BEGIN);
+ setXcbMask(mask, XCB_INPUT_GESTURE_PINCH_UPDATE);
+ setXcbMask(mask, XCB_INPUT_GESTURE_PINCH_END);
+ setXcbMask(mask, XCB_INPUT_GESTURE_SWIPE_BEGIN);
+ setXcbMask(mask, XCB_INPUT_GESTURE_SWIPE_UPDATE);
+ setXcbMask(mask, XCB_INPUT_GESTURE_SWIPE_END);
}
+#endif // QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES
for (int id : qAsConst(m_xiMasterPointerIds)) {
xcb_generic_error_t *error = nullptr;
auto cookie = xcb_input_xi_grab_device(xcb_connection(), w, XCB_CURRENT_TIME, XCB_CURSOR_NONE, id,
XCB_INPUT_GRAB_MODE_22_ASYNC, XCB_INPUT_GRAB_MODE_22_ASYNC,
- false, 1, &mask);
+ false, 2, reinterpret_cast<uint32_t *>(mask));
auto *reply = xcb_input_xi_grab_device_reply(xcb_connection(), cookie, &error);
if (error) {
qCDebug(lcQpaXInput, "failed to grab events for device %d on window %x"
@@ -1034,6 +1094,163 @@ void QXcbConnection::xi2HandleHierarchyEvent(void *event)
xi2SetupDevices();
}
+#if QT_XCB_HAS_TOUCHPAD_GESTURES
+void QXcbConnection::xi2HandleGesturePinchEvent(void *event)
+{
+ auto *xiEvent = reinterpret_cast<qt_xcb_input_pinch_event_t *>(event);
+
+ if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled())) {
+ qCDebug(lcQpaXInputEvents, "XI2 gesture event type %d seq %d fingers %d pos %6.1f, "
+ "%6.1f root pos %6.1f, %6.1f delta_angle %6.1f scale %6.1f on window %x",
+ xiEvent->event_type, xiEvent->sequence, xiEvent->detail,
+ fixed1616ToReal(xiEvent->event_x), fixed1616ToReal(xiEvent->event_y),
+ fixed1616ToReal(xiEvent->root_x), fixed1616ToReal(xiEvent->root_y),
+ fixed1616ToReal(xiEvent->delta_angle), fixed1616ToReal(xiEvent->scale),
+ xiEvent->event);
+ }
+ QXcbWindow *platformWindow = platformWindowFromId(xiEvent->event);
+ if (!platformWindow)
+ return;
+
+ setTime(xiEvent->time);
+
+ TouchDeviceData *dev = touchDeviceForId(xiEvent->sourceid);
+ Q_ASSERT(dev);
+
+ uint32_t fingerCount = xiEvent->detail;
+
+ switch (xiEvent->event_type) {
+ case XCB_INPUT_GESTURE_PINCH_BEGIN:
+ // Gestures must be accepted when we are grabbing gesture events. Otherwise the entire
+ // sequence will get replayed when the grab ends.
+ if (m_xiGrab) {
+ xcb_input_xi_allow_events(xcb_connection(), XCB_CURRENT_TIME, xiEvent->deviceid,
+ XCB_INPUT_EVENT_MODE_ASYNC_DEVICE, 0, xiEvent->event);
+ }
+ m_lastPinchScale = 1.0;
+ QWindowSystemInterface::handleGestureEvent(platformWindow->window(), xiEvent->time,
+ dev->qtTouchDevice,
+ Qt::BeginNativeGesture,
+ platformWindow->lastPointerPosition(),
+ platformWindow->lastPointerGlobalPosition(),
+ fingerCount);
+ break;
+
+ case XCB_INPUT_GESTURE_PINCH_UPDATE: {
+ qreal rotationDelta = fixed1616ToReal(xiEvent->delta_angle);
+ qreal scale = fixed1616ToReal(xiEvent->scale);
+ qreal scaleDelta = scale - m_lastPinchScale;
+ m_lastPinchScale = scale;
+
+ QPointF delta = QPointF(fixed1616ToReal(xiEvent->delta_x),
+ fixed1616ToReal(xiEvent->delta_y));
+
+ if (!delta.isNull()) {
+ QWindowSystemInterface::handleGestureEventWithValueAndDelta(
+ platformWindow->window(), xiEvent->time, dev->qtTouchDevice,
+ Qt::PanNativeGesture, 0, delta,
+ platformWindow->lastPointerPosition(),
+ platformWindow->lastPointerGlobalPosition(),
+ fingerCount);
+ }
+ if (rotationDelta != 0) {
+ QWindowSystemInterface::handleGestureEventWithRealValue(
+ platformWindow->window(), xiEvent->time, dev->qtTouchDevice,
+ Qt::RotateNativeGesture,
+ rotationDelta,
+ platformWindow->lastPointerPosition(),
+ platformWindow->lastPointerGlobalPosition(),
+ fingerCount);
+ }
+ if (scaleDelta != 0) {
+ QWindowSystemInterface::handleGestureEventWithRealValue(
+ platformWindow->window(), xiEvent->time, dev->qtTouchDevice,
+ Qt::ZoomNativeGesture,
+ scaleDelta,
+ platformWindow->lastPointerPosition(),
+ platformWindow->lastPointerGlobalPosition(),
+ fingerCount);
+ }
+ break;
+ }
+ case XCB_INPUT_GESTURE_PINCH_END:
+ QWindowSystemInterface::handleGestureEvent(platformWindow->window(), xiEvent->time,
+ dev->qtTouchDevice,
+ Qt::EndNativeGesture,
+ platformWindow->lastPointerPosition(),
+ platformWindow->lastPointerGlobalPosition(),
+ fingerCount);
+ break;
+ }
+}
+
+void QXcbConnection::xi2HandleGestureSwipeEvent(void *event)
+{
+ auto *xiEvent = reinterpret_cast<qt_xcb_input_swipe_event_t *>(event);
+
+ if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled())) {
+ qCDebug(lcQpaXInputEvents, "XI2 gesture event type %d seq %d detail %d pos %6.1f, %6.1f root pos %6.1f, %6.1f on window %x",
+ xiEvent->event_type, xiEvent->sequence, xiEvent->detail,
+ fixed1616ToReal(xiEvent->event_x), fixed1616ToReal(xiEvent->event_y),
+ fixed1616ToReal(xiEvent->root_x), fixed1616ToReal(xiEvent->root_y),
+ xiEvent->event);
+ }
+ QXcbWindow *platformWindow = platformWindowFromId(xiEvent->event);
+ if (!platformWindow)
+ return;
+
+ setTime(xiEvent->time);
+
+ TouchDeviceData *dev = touchDeviceForId(xiEvent->sourceid);
+ Q_ASSERT(dev);
+
+ uint32_t fingerCount = xiEvent->detail;
+
+ switch (xiEvent->event_type) {
+ case XCB_INPUT_GESTURE_SWIPE_BEGIN:
+ // Gestures must be accepted when we are grabbing gesture events. Otherwise the entire
+ // sequence will get replayed when the grab ends.
+ if (m_xiGrab) {
+ xcb_input_xi_allow_events(xcb_connection(), XCB_CURRENT_TIME, xiEvent->deviceid,
+ XCB_INPUT_EVENT_MODE_ASYNC_DEVICE, 0, xiEvent->event);
+ }
+ QWindowSystemInterface::handleGestureEvent(platformWindow->window(), xiEvent->time,
+ dev->qtTouchDevice,
+ Qt::BeginNativeGesture,
+ platformWindow->lastPointerPosition(),
+ platformWindow->lastPointerGlobalPosition(),
+ fingerCount);
+ break;
+ case XCB_INPUT_GESTURE_SWIPE_UPDATE: {
+ QPointF delta = QPointF(fixed1616ToReal(xiEvent->delta_x),
+ fixed1616ToReal(xiEvent->delta_y));
+
+ if (xiEvent->delta_x != 0 || xiEvent->delta_y != 0) {
+ QWindowSystemInterface::handleGestureEventWithValueAndDelta(
+ platformWindow->window(), xiEvent->time, dev->qtTouchDevice,
+ Qt::PanNativeGesture, 0, delta,
+ platformWindow->lastPointerPosition(),
+ platformWindow->lastPointerGlobalPosition(),
+ fingerCount);
+ }
+ break;
+ }
+ case XCB_INPUT_GESTURE_SWIPE_END:
+ QWindowSystemInterface::handleGestureEvent(platformWindow->window(), xiEvent->time,
+ dev->qtTouchDevice,
+ Qt::EndNativeGesture,
+ platformWindow->lastPointerPosition(),
+ platformWindow->lastPointerGlobalPosition(),
+ fingerCount);
+ break;
+ }
+}
+
+#else // QT_XCB_HAS_TOUCHPAD_GESTURES
+void QXcbConnection::xi2HandleGesturePinchEvent(void*) {}
+void QXcbConnection::xi2HandleGestureSwipeEvent(void*) {}
+#endif
+
void QXcbConnection::xi2HandleDeviceChangedEvent(void *event)
{
auto *xiEvent = reinterpret_cast<xcb_input_device_changed_event_t *>(event);
diff --git a/src/plugins/platforms/xcb/qxcbcursor.cpp b/src/plugins/platforms/xcb/qxcbcursor.cpp
index 4210bf428e..9f8007066d 100644
--- a/src/plugins/platforms/xcb/qxcbcursor.cpp
+++ b/src/plugins/platforms/xcb/qxcbcursor.cpp
@@ -590,7 +590,7 @@ xcb_cursor_t QXcbCursor::createFontCursor(int cshape)
// Non-standard X11 cursors are created from bitmaps
cursor = createNonStandardCursor(cshape);
- // Create a glpyh cursor if everything else failed
+ // Create a glyph cursor if everything else failed
if (!cursor && cursorId) {
cursor = xcb_generate_id(conn);
xcb_create_glyph_cursor(conn, cursor, cursorFont, cursorFont,
diff --git a/src/plugins/platforms/xcb/qxcbdrag.cpp b/src/plugins/platforms/xcb/qxcbdrag.cpp
index d2143ce953..4437da3520 100644
--- a/src/plugins/platforms/xcb/qxcbdrag.cpp
+++ b/src/plugins/platforms/xcb/qxcbdrag.cpp
@@ -174,11 +174,9 @@ void QXcbDrag::startDrag()
{
init();
-#ifndef QT_NO_CLIPBOARD
- qCDebug(lcQpaXDnd) << "starting drag where source:" << connection()->clipboard()->owner();
- xcb_set_selection_owner(xcb_connection(), connection()->clipboard()->owner(),
+ qCDebug(lcQpaXDnd) << "starting drag where source:" << connection()->qtSelectionOwner();
+ xcb_set_selection_owner(xcb_connection(), connection()->qtSelectionOwner(),
atom(QXcbAtom::XdndSelection), connection()->time());
-#endif
QStringList fmts = QXcbMime::formatsHelper(drag()->mimeData());
for (int i = 0; i < fmts.size(); ++i) {
@@ -188,12 +186,11 @@ void QXcbDrag::startDrag()
drag_types.append(atoms.at(j));
}
}
-#ifndef QT_NO_CLIPBOARD
+
if (drag_types.size() > 3)
- xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, connection()->clipboard()->owner(),
+ xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, connection()->qtSelectionOwner(),
atom(QXcbAtom::XdndTypelist),
XCB_ATOM_ATOM, 32, drag_types.size(), (const void *)drag_types.constData());
-#endif
setUseCompositing(current_virtual_desktop->compositingActive());
setScreen(current_virtual_desktop->screens().constFirst()->screen());
@@ -444,11 +441,7 @@ void QXcbDrag::move(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardMod
enter.window = target;
enter.format = 32;
enter.type = atom(QXcbAtom::XdndEnter);
-#ifndef QT_NO_CLIPBOARD
- enter.data.data32[0] = connection()->clipboard()->owner();
-#else
- enter.data.data32[0] = 0;
-#endif
+ enter.data.data32[0] = connection()->qtSelectionOwner();
enter.data.data32[1] = flags;
enter.data.data32[2] = drag_types.size() > 0 ? drag_types.at(0) : 0;
enter.data.data32[3] = drag_types.size() > 1 ? drag_types.at(1) : 0;
@@ -479,11 +472,7 @@ void QXcbDrag::move(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardMod
move.window = target;
move.format = 32;
move.type = atom(QXcbAtom::XdndPosition);
-#ifndef QT_NO_CLIPBOARD
- move.data.data32[0] = connection()->clipboard()->owner();
-#else
- move.data.data32[0] = 0;
-#endif
+ move.data.data32[0] = connection()->qtSelectionOwner();
move.data.data32[1] = 0; // flags
move.data.data32[2] = (globalPos.x() << 16) + globalPos.y();
move.data.data32[3] = connection()->time();
@@ -529,11 +518,7 @@ void QXcbDrag::drop(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardMod
drop.window = current_target;
drop.format = 32;
drop.type = atom(QXcbAtom::XdndDrop);
-#ifndef QT_NO_CLIPBOARD
- drop.data.data32[0] = connection()->clipboard()->owner();
-#else
- drop.data.data32[0] = 0;
-#endif
+ drop.data.data32[0] = connection()->qtSelectionOwner();
drop.data.data32[1] = 0; // flags
drop.data.data32[2] = connection()->time();
@@ -640,7 +625,7 @@ void QXcbDrag::setActionList(Qt::DropAction requestedAction, Qt::DropActions sup
checkAppend(Qt::LinkAction);
if (current_actions != actions) {
- xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, connection()->clipboard()->owner(),
+ xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, connection()->qtSelectionOwner(),
atom(QXcbAtom::XdndActionList),
XCB_ATOM_ATOM, 32, actions.size(), actions.constData());
current_actions = actions;
@@ -861,11 +846,9 @@ void QXcbDrag::handle_xdnd_position(QPlatformWindow *w, const xcb_client_message
qCDebug(lcQpaXDnd) << "sending XdndStatus to source:" << xdnd_dragsource;
-#ifndef QT_NO_CLIPBOARD
- if (xdnd_dragsource == connection()->clipboard()->owner())
+ if (xdnd_dragsource == connection()->qtSelectionOwner())
handle_xdnd_status(&response);
else
-#endif
xcb_send_event(xcb_connection(), false, current_proxy_target,
XCB_EVENT_MASK_NO_EVENT, (const char *)&response);
}
@@ -931,11 +914,7 @@ void QXcbDrag::handle_xdnd_status(const xcb_client_message_event_t *event)
void QXcbDrag::handleStatus(const xcb_client_message_event_t *event)
{
- if (
-#ifndef QT_NO_CLIPBOARD
- event->window != connection()->clipboard()->owner() ||
-#endif
- !drag())
+ if (event->window != connection()->qtSelectionOwner() || !drag())
return;
xcb_client_message_event_t *lastEvent = const_cast<xcb_client_message_event_t *>(event);
@@ -992,11 +971,7 @@ void QXcbDrag::send_leave()
leave.window = current_target;
leave.format = 32;
leave.type = atom(QXcbAtom::XdndLeave);
-#ifndef QT_NO_CLIPBOARD
- leave.data.data32[0] = connection()->clipboard()->owner();
-#else
- leave.data.data32[0] = 0;
-#endif
+ leave.data.data32[0] = connection()->qtSelectionOwner();
leave.data.data32[1] = 0; // flags
leave.data.data32[2] = 0; // x, y
leave.data.data32[3] = 0; // w, h
@@ -1091,10 +1066,8 @@ void QXcbDrag::handleFinished(const xcb_client_message_event_t *event)
// Source receives XdndFinished when target is done processing the drop data.
qCDebug(lcQpaXDnd) << "source:" << event->window << "received XdndFinished";
-#ifndef QT_NO_CLIPBOARD
- if (event->window != connection()->clipboard()->owner())
+ if (event->window != connection()->qtSelectionOwner())
return;
-#endif
const unsigned long *l = (const unsigned long *)event->data.data32;
if (l[0]) {
@@ -1369,7 +1342,7 @@ QVariant QXcbDropData::xdndObtainData(const QByteArray &format, QMetaType reques
return result;
#ifndef QT_NO_CLIPBOARD
- if (c->clipboard()->getSelectionOwner(drag->atom(QXcbAtom::XdndSelection)) == XCB_NONE)
+ if (c->selectionOwner(c->atom(QXcbAtom::XdndSelection)) == XCB_NONE)
return result; // should never happen?
xcb_atom_t xdnd_selection = c->atom(QXcbAtom::XdndSelection);
diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.cpp b/src/plugins/platforms/xcb/qxcbkeyboard.cpp
index 9ab804ca1b..48abbbf74c 100644
--- a/src/plugins/platforms/xcb/qxcbkeyboard.cpp
+++ b/src/plugins/platforms/xcb/qxcbkeyboard.cpp
@@ -365,6 +365,17 @@ void QXcbKeyboard::updateKeymap(xcb_mapping_notify_event_t *event)
updateKeymap();
}
+void QXcbKeyboard::updateKeymap(xcb_xkb_new_keyboard_notify_event_t *event)
+{
+ if (!event)
+ return;
+
+ if (event->deviceID != event->oldDeviceID)
+ m_config = false;
+
+ updateKeymap();
+}
+
void QXcbKeyboard::updateKeymap()
{
KeysymModifierMap keysymMods;
@@ -372,8 +383,6 @@ void QXcbKeyboard::updateKeymap()
keysymMods = keysymsToModifiers();
updateModifiers(keysymMods);
- m_config = true;
-
if (!m_xkbContext) {
m_xkbContext.reset(xkb_context_new(XKB_CONTEXT_NO_DEFAULT_INCLUDES));
if (!m_xkbContext) {
@@ -389,8 +398,13 @@ void QXcbKeyboard::updateKeymap()
if (connection()->hasXKB()) {
m_xkbKeymap.reset(xkb_x11_keymap_new_from_device(m_xkbContext.get(), xcb_connection(),
core_device_id, XKB_KEYMAP_COMPILE_NO_FLAGS));
- if (m_xkbKeymap)
- m_xkbState.reset(xkb_x11_state_new_from_device(m_xkbKeymap.get(), xcb_connection(), core_device_id));
+ if (m_xkbKeymap) {
+ if (m_config)
+ m_xkbState.reset(xkb_state_new(m_xkbKeymap.get()));
+ else
+ m_xkbState.reset(xkb_x11_state_new_from_device(m_xkbKeymap.get(), xcb_connection(), core_device_id));
+
+ }
} else {
m_xkbKeymap.reset(keymapFromCore(keysymMods));
if (m_xkbKeymap)
@@ -411,6 +425,8 @@ void QXcbKeyboard::updateKeymap()
updateXKBMods();
QXkbCommon::verifyHasLatinLayout(m_xkbKeymap.get());
+
+ m_config = true;
}
QList<int> QXcbKeyboard::possibleKeys(const QKeyEvent *event) const
diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.h b/src/plugins/platforms/xcb/qxcbkeyboard.h
index cf89acff6d..aaf4577985 100644
--- a/src/plugins/platforms/xcb/qxcbkeyboard.h
+++ b/src/plugins/platforms/xcb/qxcbkeyboard.h
@@ -69,6 +69,7 @@ public:
Qt::KeyboardModifiers translateModifiers(int s) const;
void updateKeymap(xcb_mapping_notify_event_t *event);
+ void updateKeymap(xcb_xkb_new_keyboard_notify_event_t *event);
void updateKeymap();
QList<int> possibleKeys(const QKeyEvent *event) const;
diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp
index 1ecf995f7c..6524397cef 100644
--- a/src/plugins/platforms/xcb/qxcbscreen.cpp
+++ b/src/plugins/platforms/xcb/qxcbscreen.cpp
@@ -62,7 +62,7 @@ QXcbVirtualDesktop::QXcbVirtualDesktop(QXcbConnection *connection, xcb_screen_t
{
const QByteArray cmAtomName = "_NET_WM_CM_S" + QByteArray::number(m_number);
m_net_wm_cm_atom = connection->internAtom(cmAtomName.constData());
- m_compositingActive = connection->getSelectionOwner(m_net_wm_cm_atom);
+ m_compositingActive = connection->selectionOwner(m_net_wm_cm_atom);
m_workArea = getWorkArea();
@@ -177,7 +177,7 @@ bool QXcbVirtualDesktop::compositingActive() const
if (connection()->hasXFixes())
return m_compositingActive;
else
- return connection()->getSelectionOwner(m_net_wm_cm_atom);
+ return connection()->selectionOwner(m_net_wm_cm_atom);
}
void QXcbVirtualDesktop::handleXFixesSelectionNotify(xcb_xfixes_selection_notify_event_t *notify_event)
@@ -192,7 +192,7 @@ void QXcbVirtualDesktop::subscribeToXFixesSelectionNotify()
const uint32_t mask = XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER |
XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY |
XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE;
- xcb_xfixes_select_selection_input_checked(xcb_connection(), connection()->getQtSelectionOwner(), m_net_wm_cm_atom, mask);
+ xcb_xfixes_select_selection_input_checked(xcb_connection(), connection()->qtSelectionOwner(), m_net_wm_cm_atom, mask);
}
}
@@ -271,7 +271,7 @@ void QXcbVirtualDesktop::handleScreenChange(xcb_randr_screen_change_notify_event
_NET_WORKAREA means with multiple attached monitors. This gets worse when monitors have
different dimensions and/or screens are not virtually aligned. In Qt we want the available
geometry per monitor (QScreen), not desktop (represented by _NET_WORKAREA). WM specification
- does not have an atom for this. Thus, QScreen is limted by the lack of support from the
+ does not have an atom for this. Thus, QScreen is limited by the lack of support from the
underlying system.
One option could be that Qt does WM's job of calculating this by subtracting geometries of
@@ -884,7 +884,7 @@ QDpi QXcbScreen::logicalDpi() const
return QDpi(forcedDpi, forcedDpi);
// Fall back to 96 DPI in case no logical DPI is set. We don't want to
- // return physical DPI here, since that is a differnt type of DPI: Logical
+ // return physical DPI here, since that is a different type of DPI: Logical
// DPI typically accounts for user preference and viewing distance, and is
// quantized into DPI classes (96, 144, 192, etc); pysical DPI is an exact
// physical measure.
diff --git a/src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp b/src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp
index ff5ad98cd2..3960c5d33f 100644
--- a/src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp
+++ b/src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp
@@ -56,7 +56,7 @@ enum {
};
// QXcbSystemTrayTracker provides API for accessing the tray window and tracks
-// its lifecyle by listening for its destruction and recreation.
+// its lifecycle by listening for its destruction and recreation.
// See http://standards.freedesktop.org/systemtray-spec/systemtray-spec-latest.html
QXcbSystemTrayTracker *QXcbSystemTrayTracker::create(QXcbConnection *connection)
@@ -83,14 +83,6 @@ QXcbSystemTrayTracker::QXcbSystemTrayTracker(QXcbConnection *connection,
{
}
-xcb_window_t QXcbSystemTrayTracker::locateTrayWindow(const QXcbConnection *connection, xcb_atom_t selection)
-{
- auto reply = Q_XCB_REPLY(xcb_get_selection_owner, connection->xcb_connection(), selection);
- if (!reply)
- return 0;
- return reply->owner;
-}
-
// Request a window to be docked on the tray.
void QXcbSystemTrayTracker::requestSystemTrayWindowDock(xcb_window_t window) const
{
@@ -110,7 +102,7 @@ void QXcbSystemTrayTracker::requestSystemTrayWindowDock(xcb_window_t window) con
xcb_window_t QXcbSystemTrayTracker::trayWindow()
{
if (!m_trayWindow) {
- m_trayWindow = QXcbSystemTrayTracker::locateTrayWindow(m_connection, m_selection);
+ m_trayWindow = m_connection->selectionOwner(m_selection);
if (m_trayWindow) { // Listen for DestroyNotify on tray.
m_connection->addWindowEventListener(m_trayWindow, this);
const quint32 mask = XCB_CW_EVENT_MASK;
diff --git a/src/plugins/platforms/xcb/qxcbsystemtraytracker.h b/src/plugins/platforms/xcb/qxcbsystemtraytracker.h
index d2fc24c957..e8e24c82f3 100644
--- a/src/plugins/platforms/xcb/qxcbsystemtraytracker.h
+++ b/src/plugins/platforms/xcb/qxcbsystemtraytracker.h
@@ -70,7 +70,7 @@ private:
explicit QXcbSystemTrayTracker(QXcbConnection *connection,
xcb_atom_t trayAtom,
xcb_atom_t selection);
- static xcb_window_t locateTrayWindow(const QXcbConnection *connection, xcb_atom_t selection);
+
void emitSystemTrayWindowChanged();
xcb_visualid_t netSystemTrayVisual();
diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp
index fd1a1970f7..c9b6c10e05 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.cpp
+++ b/src/plugins/platforms/xcb/qxcbwindow.cpp
@@ -246,7 +246,7 @@ enum : quint32 {
| XCB_EVENT_MASK_POINTER_MOTION,
transparentForInputEventMask = baseEventMask
- | XCB_EVENT_MASK_VISIBILITY_CHANGE | XCB_EVENT_MASK_RESIZE_REDIRECT
+ | XCB_EVENT_MASK_VISIBILITY_CHANGE
| XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT
| XCB_EVENT_MASK_COLOR_MAP_CHANGE | XCB_EVENT_MASK_OWNER_GRAB_BUTTON
};
@@ -1771,7 +1771,7 @@ void QXcbWindow::handleConfigureNotifyEvent(const xcb_configure_notify_event_t *
if (!qFuzzyCompare(QHighDpiScaling::factor(newScreen), m_sizeHintsScaleFactor))
propagateSizeHints();
- // Send the synthetic expose event on resize only when the window is shrinked,
+ // Send the synthetic expose event on resize only when the window is shrunk,
// because the "XCB_GRAVITY_NORTH_WEST" flag doesn't send it automatically.
if (!m_oldWindowSize.isEmpty()
&& (actualGeometry.width() < m_oldWindowSize.width()
@@ -1846,7 +1846,7 @@ void QXcbWindow::handleUnmapNotifyEvent(const xcb_unmap_notify_event_t *event)
if (event->window == m_window) {
m_mapped = false;
QWindowSystemInterface::handleExposeEvent(window(), QRegion());
- if (!m_isWmManagedWindow) {
+ if (!m_isWmManagedWindow || parent()) {
m_wmStateValid = true;
handleDeferredTasks();
}
@@ -2147,6 +2147,7 @@ void QXcbWindow::handleMouseEvent(xcb_timestamp_t time, const QPoint &local, con
Qt::KeyboardModifiers modifiers, QEvent::Type type, Qt::MouseEventSource source)
{
m_lastPointerPosition = local;
+ m_lastPointerGlobalPosition = global;
connection()->setTime(time);
Qt::MouseButton button = type == QEvent::MouseMove ? Qt::NoButton : connection()->button();
QWindowSystemInterface::handleMouseEvent(window(), time, local, global,
diff --git a/src/plugins/platforms/xcb/qxcbwindow.h b/src/plugins/platforms/xcb/qxcbwindow.h
index f7bed7f67b..01aabfc429 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.h
+++ b/src/plugins/platforms/xcb/qxcbwindow.h
@@ -175,6 +175,9 @@ public:
QXcbScreen *xcbScreen() const;
+ QPoint lastPointerPosition() const { return m_lastPointerPosition; }
+ QPoint lastPointerGlobalPosition() const { return m_lastPointerGlobalPosition; }
+
bool startSystemMoveResize(const QPoint &pos, int edges);
void doStartSystemMoveResize(const QPoint &globalPos, int edges);
@@ -271,6 +274,7 @@ protected:
QRegion m_exposeRegion;
QSize m_oldWindowSize;
QPoint m_lastPointerPosition;
+ QPoint m_lastPointerGlobalPosition;
xcb_visualid_t m_visualId = 0;
// Last sent state. Initialized to an invalid state, on purpose.
diff --git a/src/plugins/platforms/xcb/qxcbxsettings.cpp b/src/plugins/platforms/xcb/qxcbxsettings.cpp
index 902f196ba9..54b410f446 100644
--- a/src/plugins/platforms/xcb/qxcbxsettings.cpp
+++ b/src/plugins/platforms/xcb/qxcbxsettings.cpp
@@ -235,12 +235,11 @@ QXcbXSettings::QXcbXSettings(QXcbVirtualDesktop *screen)
xcb_atom_t selection_owner_atom = atom_reply->atom;
- auto selection_result = Q_XCB_REPLY(xcb_get_selection_owner,
- screen->xcb_connection(), selection_owner_atom);
- if (!selection_result)
+ xcb_window_t owner = screen->connection()->selectionOwner(selection_owner_atom);
+ if (owner == XCB_NONE)
return;
- d_ptr->x_settings_window = selection_result->owner;
+ d_ptr->x_settings_window = owner;
if (!d_ptr->x_settings_window)
return;
diff --git a/src/plugins/platformthemes/gtk3/qgtk3dialoghelpers.cpp b/src/plugins/platformthemes/gtk3/qgtk3dialoghelpers.cpp
index 16d5eb3ab4..4f417d77d8 100644
--- a/src/plugins/platformthemes/gtk3/qgtk3dialoghelpers.cpp
+++ b/src/plugins/platformthemes/gtk3/qgtk3dialoghelpers.cpp
@@ -45,6 +45,7 @@
#include <qcolor.h>
#include <qdebug.h>
#include <qfont.h>
+#include <qfileinfo.h>
#include <private/qguiapplication_p.h>
#include <qpa/qplatformfontdatabase.h>
@@ -55,6 +56,14 @@
#include <gdk/gdkx.h>
#include <pango/pango.h>
+// The size of the preview we display for selected image files. We set height
+// larger than width because generally there is more free space vertically
+// than horiztonally (setting the preview image will alway expand the width of
+// the dialog, but usually not the height). The image's aspect ratio will always
+// be preserved.
+#define PREVIEW_WIDTH 256
+#define PREVIEW_HEIGHT 512
+
QT_BEGIN_NAMESPACE
class QGtk3Dialog : public QWindow
@@ -250,6 +259,10 @@ QGtk3FileDialogHelper::QGtk3FileDialogHelper()
g_signal_connect(GTK_FILE_CHOOSER(d->gtkDialog()), "selection-changed", G_CALLBACK(onSelectionChanged), this);
g_signal_connect_swapped(GTK_FILE_CHOOSER(d->gtkDialog()), "current-folder-changed", G_CALLBACK(onCurrentFolderChanged), this);
g_signal_connect_swapped(GTK_FILE_CHOOSER(d->gtkDialog()), "notify::filter", G_CALLBACK(onFilterChanged), this);
+
+ previewWidget = gtk_image_new();
+ g_signal_connect(G_OBJECT(d->gtkDialog()), "update-preview", G_CALLBACK(onUpdatePreview), this);
+ gtk_file_chooser_set_preview_widget(GTK_FILE_CHOOSER(d->gtkDialog()), previewWidget);
}
QGtk3FileDialogHelper::~QGtk3FileDialogHelper()
@@ -390,6 +403,33 @@ void QGtk3FileDialogHelper::onFilterChanged(QGtk3FileDialogHelper *dialog)
emit dialog->filterSelected(dialog->selectedNameFilter());
}
+void QGtk3FileDialogHelper::onUpdatePreview(GtkDialog *gtkDialog, QGtk3FileDialogHelper *helper)
+{
+ gchar *filename = gtk_file_chooser_get_preview_filename(GTK_FILE_CHOOSER(gtkDialog));
+ if (!filename) {
+ gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(gtkDialog), false);
+ return;
+ }
+
+ // Don't attempt to open anything which isn't a regular file. If a named pipe,
+ // this may hang.
+ QFileInfo fileinfo(filename);
+ if (!fileinfo.exists() || !fileinfo.isFile()) {
+ g_free(filename);
+ gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(gtkDialog), false);
+ return;
+ }
+
+ // This will preserve the image's aspect ratio.
+ GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file_at_size(filename, PREVIEW_WIDTH, PREVIEW_HEIGHT, 0);
+ g_free(filename);
+ if (pixbuf) {
+ gtk_image_set_from_pixbuf(GTK_IMAGE(helper->previewWidget), pixbuf);
+ g_object_unref(pixbuf);
+ }
+ gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(gtkDialog), pixbuf ? true : false);
+}
+
static GtkFileChooserAction gtkFileChooserAction(const QSharedPointer<QFileDialogOptions> &options)
{
switch (options->fileMode()) {
diff --git a/src/plugins/platformthemes/gtk3/qgtk3dialoghelpers.h b/src/plugins/platformthemes/gtk3/qgtk3dialoghelpers.h
index e78a7fc6d1..1a055ac055 100644
--- a/src/plugins/platformthemes/gtk3/qgtk3dialoghelpers.h
+++ b/src/plugins/platformthemes/gtk3/qgtk3dialoghelpers.h
@@ -47,6 +47,7 @@
#include <QtCore/qstring.h>
#include <qpa/qplatformdialoghelper.h>
+typedef struct _GtkWidget GtkWidget;
typedef struct _GtkDialog GtkDialog;
typedef struct _GtkFileFilter GtkFileFilter;
@@ -108,6 +109,7 @@ private:
static void onSelectionChanged(GtkDialog *dialog, QGtk3FileDialogHelper *helper);
static void onCurrentFolderChanged(QGtk3FileDialogHelper *helper);
static void onFilterChanged(QGtk3FileDialogHelper *helper);
+ static void onUpdatePreview(GtkDialog *dialog, QGtk3FileDialogHelper *helper);
void applyOptions();
void setNameFilters(const QStringList &filters);
void selectFileInternal(const QUrl &filename);
@@ -118,6 +120,7 @@ private:
QHash<QString, GtkFileFilter*> _filters;
QHash<GtkFileFilter*, QString> _filterNames;
QScopedPointer<QGtk3Dialog> d;
+ GtkWidget *previewWidget;
};
class QGtk3FontDialogHelper : public QPlatformFontDialogHelper
diff --git a/src/plugins/platformthemes/gtk3/qgtk3theme.cpp b/src/plugins/platformthemes/gtk3/qgtk3theme.cpp
index 93520344f8..a47720384c 100644
--- a/src/plugins/platformthemes/gtk3/qgtk3theme.cpp
+++ b/src/plugins/platformthemes/gtk3/qgtk3theme.cpp
@@ -41,6 +41,7 @@
#include "qgtk3dialoghelpers.h"
#include "qgtk3menu.h"
#include <QVariant>
+#include <QtCore/qregularexpression.h>
#undef signals
#include <gtk/gtk.h>
@@ -147,6 +148,47 @@ QString QGtk3Theme::gtkFontName() const
return QGnomeTheme::gtkFontName();
}
+QPlatformTheme::Appearance QGtk3Theme::appearance() const
+{
+ /*
+ https://docs.gtk.org/gtk3/running.html
+
+ It's possible to set a theme variant after the theme name when using GTK_THEME:
+
+ GTK_THEME=Adwaita:dark
+
+ Some themes also have "-dark" as part of their name.
+
+ We test this environment variable first because the documentation says
+ it's mainly used for easy debugging, so it should be possible to use it
+ to override any other settings.
+ */
+ QString themeName = qEnvironmentVariable("GTK_THEME");
+ const QRegularExpression darkRegex(QStringLiteral("[:-]dark"), QRegularExpression::CaseInsensitiveOption);
+ if (!themeName.isEmpty())
+ return darkRegex.match(themeName).hasMatch() ? Appearance::Dark : Appearance::Light;
+
+ /*
+ https://docs.gtk.org/gtk3/property.Settings.gtk-application-prefer-dark-theme.html
+
+ This setting controls which theme is used when the theme specified by
+ gtk-theme-name provides both light and dark variants. We can save a
+ regex check by testing this property first.
+ */
+ const auto preferDark = gtkSetting<bool>("gtk-application-prefer-dark-theme");
+ if (preferDark)
+ return Appearance::Dark;
+
+ /*
+ https://docs.gtk.org/gtk3/property.Settings.gtk-theme-name.html
+ */
+ themeName = gtkSetting("gtk-theme-name");
+ if (!themeName.isEmpty())
+ return darkRegex.match(themeName).hasMatch() ? Appearance::Dark : Appearance::Light;
+
+ return Appearance::Unknown;
+}
+
bool QGtk3Theme::usePlatformNativeDialog(DialogType type) const
{
switch (type) {
diff --git a/src/plugins/platformthemes/gtk3/qgtk3theme.h b/src/plugins/platformthemes/gtk3/qgtk3theme.h
index 54296d2ff1..5f439067af 100644
--- a/src/plugins/platformthemes/gtk3/qgtk3theme.h
+++ b/src/plugins/platformthemes/gtk3/qgtk3theme.h
@@ -52,6 +52,8 @@ public:
virtual QVariant themeHint(ThemeHint hint) const override;
virtual QString gtkFontName() const override;
+ Appearance appearance() const override;
+
bool usePlatformNativeDialog(DialogType type) const override;
QPlatformDialogHelper *createPlatformDialogHelper(DialogType type) const override;
diff --git a/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportalfiledialog.cpp b/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportalfiledialog.cpp
index 85bdd1ab88..66e374f621 100644
--- a/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportalfiledialog.cpp
+++ b/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportalfiledialog.cpp
@@ -302,6 +302,7 @@ void QXdgDesktopPortalFileDialog::openPortal()
this,
SLOT(gotResponse(uint,QVariantMap)));
}
+ watcher->deleteLater();
});
}
diff --git a/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.cpp b/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.cpp
index fb324afbd8..2fc3167fd5 100644
--- a/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.cpp
+++ b/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.cpp
@@ -111,6 +111,7 @@ QXdgDesktopPortalTheme::QXdgDesktopPortalTheme()
if (reply.isValid()) {
d->fileChooserPortalVersion = reply.value().toUInt();
}
+ watcher->deleteLater();
});
}
diff --git a/src/plugins/sqldrivers/CMakeLists.txt b/src/plugins/sqldrivers/CMakeLists.txt
index 91c971790b..b704e272f6 100644
--- a/src/plugins/sqldrivers/CMakeLists.txt
+++ b/src/plugins/sqldrivers/CMakeLists.txt
@@ -36,7 +36,7 @@ include(configure.cmake)
qt_feature_module_end(NO_MODULE)
-if(QT_FEATURE_sql_psql)
+if(QT_FEATURE_sql_psql AND QT_FEATURE_regularexpression)
add_subdirectory(psql)
endif()
diff --git a/src/plugins/sqldrivers/ibase/qsql_ibase.cpp b/src/plugins/sqldrivers/ibase/qsql_ibase.cpp
index 0f2cfd73bf..ba820a4416 100644
--- a/src/plugins/sqldrivers/ibase/qsql_ibase.cpp
+++ b/src/plugins/sqldrivers/ibase/qsql_ibase.cpp
@@ -798,7 +798,7 @@ bool QIBaseResultPrivate::writeArray(int column, const QList<QVariant> &list)
ba.resize(int(bufLen));
if (list.size() > arraySize) {
- error = QLatin1String("Array size missmatch: size of %1 is %2, size of provided list is %3");
+ error = QLatin1String("Array size mismatch: size of %1 is %2, size of provided list is %3");
error = error.arg(QLatin1String(sqlname)).arg(arraySize).arg(list.size());
q->setLastError(QSqlError(error, QLatin1String(""), QSqlError::StatementError));
return false;
diff --git a/src/plugins/sqldrivers/mysql/qsql_mysql.cpp b/src/plugins/sqldrivers/mysql/qsql_mysql.cpp
index 96bba79da1..caf406da46 100644
--- a/src/plugins/sqldrivers/mysql/qsql_mysql.cpp
+++ b/src/plugins/sqldrivers/mysql/qsql_mysql.cpp
@@ -1304,11 +1304,20 @@ bool QMYSQLDriver::open(const QString& db,
unixSocket.isNull() ? nullptr : unixSocket.toUtf8().constData(),
optionFlags);
+ if (mysql != d->mysql) {
+ setLastError(qMakeError(tr("Unable to connect"),
+ QSqlError::ConnectionError, d));
+ mysql_close(d->mysql);
+ d->mysql = nullptr;
+ setOpenError(true);
+ return false;
+ }
+
// now ask the server to match the charset we selected
- if (!cs || mysql_set_character_set(d->mysql, cs->csname)) {
+ if (!cs || mysql_set_character_set(d->mysql, cs->csname) != 0) {
bool ok = false;
for (const char *p : wanted_charsets) {
- if (mysql_set_character_set(d->mysql, p)) {
+ if (mysql_set_character_set(d->mysql, p) == 0) {
ok = true;
break;
}
@@ -1319,24 +1328,16 @@ bool QMYSQLDriver::open(const QString& db,
mysql_character_set_name(d->mysql));
}
- if (mysql == d->mysql) {
- if (!db.isEmpty() && mysql_select_db(d->mysql, db.toUtf8().constData())) {
- setLastError(qMakeError(tr("Unable to open database '%1'").arg(db), QSqlError::ConnectionError, d));
- mysql_close(d->mysql);
- setOpenError(true);
- return false;
- }
- if (reconnect)
- mysql_options(d->mysql, MYSQL_OPT_RECONNECT, &reconnect);
- } else {
- setLastError(qMakeError(tr("Unable to connect"),
- QSqlError::ConnectionError, d));
+ if (!db.isEmpty() && mysql_select_db(d->mysql, db.toUtf8().constData())) {
+ setLastError(qMakeError(tr("Unable to open database '%1'").arg(db), QSqlError::ConnectionError, d));
mysql_close(d->mysql);
- d->mysql = nullptr;
setOpenError(true);
return false;
}
+ if (reconnect)
+ mysql_options(d->mysql, MYSQL_OPT_RECONNECT, &reconnect);
+
d->preparedQuerysEnabled = checkPreparedQueries(d->mysql);
#if QT_CONFIG(thread)
diff --git a/src/plugins/sqldrivers/oci/qsql_oci.cpp b/src/plugins/sqldrivers/oci/qsql_oci.cpp
index 638b01b022..b395c41d0f 100644
--- a/src/plugins/sqldrivers/oci/qsql_oci.cpp
+++ b/src/plugins/sqldrivers/oci/qsql_oci.cpp
@@ -168,16 +168,27 @@ public:
QOCIDateTime::QOCIDateTime(OCIEnv *env, OCIError *err, const QDateTime &dt)
: dateTime(nullptr)
{
- OCIDescriptorAlloc(env, reinterpret_cast<void**>(&dateTime), OCI_DTYPE_TIMESTAMP_TZ, 0, 0);
- if (dt.isValid()) {
+ if (!dt.isValid()) {
+ // nothing to do, leave dateTime null
+ } else if (OCIDescriptorAlloc(env, reinterpret_cast<void**>(&dateTime),
+ OCI_DTYPE_TIMESTAMP_TZ, 0, 0) == OCI_SUCCESS) {
+ Q_ASSERT(!dt.isNull());
const QDate date = dt.date();
+ Q_ASSERT(date.isValid());
const QTime time = dt.time();
+ Q_ASSERT(time.isValid());
// Zone in +hh:mm format (stripping UTC prefix from OffsetName)
- QString timeZone = dt.timeZone().displayName(dt, QTimeZone::OffsetName).mid(3);
+ QString timeZone = dt.timeZone().displayName(dt, QTimeZone::OffsetName);
+ Q_ASSERT(timeZone.startsWith(u"UTC"));
+ timeZone = std::move(timeZone).sliced(3);
const OraText *tz = reinterpret_cast<const OraText *>(timeZone.utf16());
- OCIDateTimeConstruct(env, err, dateTime, date.year(), date.month(), date.day(), time.hour(),
- time.minute(), time.second(), time.msec() * 1000000,
- const_cast<OraText *>(tz), timeZone.length() * sizeof(QChar));
+ if (OCIDateTimeConstruct(env, err, dateTime, date.year(), date.month(), date.day(),
+ time.hour(), time.minute(), time.second(), time.msec() * 1000000,
+ const_cast<OraText *>(tz), timeZone.length() * sizeof(QChar))) {
+ qWarning("QOCIDateTime: Failed to construct the OCIDateTime descriptor");
+ }
+ } else {
+ qWarning("QOCIDateTime: Failed to allocate the OCIDateTime descriptor");
}
}
@@ -189,6 +200,9 @@ QOCIDateTime::~QOCIDateTime()
QDateTime QOCIDateTime::fromOCIDateTime(OCIEnv *env, OCIError *err, OCIDateTime *dateTime)
{
+ if (!dateTime)
+ return QDateTime();
+
sb2 year;
ub1 month, day, hour, minute, second;
ub4 nsec;
@@ -937,7 +951,8 @@ QOCICols::QOCICols(int size, QOCIResultPrivate* dp)
switch (ofi.type.id()) {
case QMetaType::QDateTime:
- r = OCIDescriptorAlloc(d->env, (void **)&fieldInf[idx].dataPtr, OCI_DTYPE_TIMESTAMP_TZ, 0, 0);
+ r = OCIDescriptorAlloc(d->env, static_cast<void **>(&fieldInf[idx].dataPtr),
+ OCI_DTYPE_TIMESTAMP_TZ, 0, 0);
if (r != OCI_SUCCESS) {
qWarning("QOCICols: Unable to allocate the OCIDateTime descriptor");
break;
@@ -1482,6 +1497,7 @@ bool QOCICols::execBatch(QOCIResultPrivate *d, QVariantList &boundValues, bool a
columns[i].lengths[row] = columns[i].maxLen;
QOCIDateTime *date = new QOCIDateTime(d->env, d->err, val.toDateTime());
*reinterpret_cast<OCIDateTime**>(dataPtr) = date->dateTime;
+ tmpStorage.dateTimes.append(date);
break;
}
case QMetaType::Int:
diff --git a/src/plugins/sqldrivers/odbc/CMakeLists.txt b/src/plugins/sqldrivers/odbc/CMakeLists.txt
index 1bbae1b3ec..c521f38160 100644
--- a/src/plugins/sqldrivers/odbc/CMakeLists.txt
+++ b/src/plugins/sqldrivers/odbc/CMakeLists.txt
@@ -32,5 +32,3 @@ qt_internal_extend_target(QODBCDriverPlugin CONDITION UNIX
DEFINES
UNICODE
)
-
-qt_internal_force_macos_intel_arch(QODBCDriverPlugin)
diff --git a/src/plugins/sqldrivers/psql/CMakeLists.txt b/src/plugins/sqldrivers/psql/CMakeLists.txt
index 8ed84c9028..d3a6a8588e 100644
--- a/src/plugins/sqldrivers/psql/CMakeLists.txt
+++ b/src/plugins/sqldrivers/psql/CMakeLists.txt
@@ -35,5 +35,3 @@ if(MINGW)
DISABLE_PRECOMPILE_HEADERS ON
)
endif()
-
-qt_internal_force_macos_intel_arch(QPSQLDriverPlugin)
diff --git a/src/plugins/sqldrivers/qt_cmdline.cmake b/src/plugins/sqldrivers/qt_cmdline.cmake
index 91b6f9f767..93991fb2a9 100644
--- a/src/plugins/sqldrivers/qt_cmdline.cmake
+++ b/src/plugins/sqldrivers/qt_cmdline.cmake
@@ -15,4 +15,3 @@ qt_commandline_option(plugin-sql-oci TYPE void NAME sql-oci)
qt_commandline_option(plugin-sql-odbc TYPE void NAME sql-odbc)
qt_commandline_option(plugin-sql-psql TYPE void NAME sql-psql)
qt_commandline_option(plugin-sql-sqlite TYPE void NAME sql-sqlite)
-qt_commandline_assignment(MYSQL_PATH mysql.prefix)
diff --git a/src/plugins/styles/android/qandroidstyle.cpp b/src/plugins/styles/android/qandroidstyle.cpp
index 1d9ce7e9b0..464f48b84b 100644
--- a/src/plugins/styles/android/qandroidstyle.cpp
+++ b/src/plugins/styles/android/qandroidstyle.cpp
@@ -677,6 +677,9 @@ int QAndroidStyle::styleHint(QStyle::StyleHint hint, const QStyleOption *option,
case SH_RequestSoftwareInputPanel:
return RSIP_OnMouseClick;
+ case SH_SpinBox_SelectOnStep:
+ return 0;
+
default:
return QFusionStyle::styleHint(hint, option, widget, returnData);
}
diff --git a/src/plugins/styles/mac/qmacstyle_mac.mm b/src/plugins/styles/mac/qmacstyle_mac.mm
index 076a729170..f8f2d1d0ef 100644
--- a/src/plugins/styles/mac/qmacstyle_mac.mm
+++ b/src/plugins/styles/mac/qmacstyle_mac.mm
@@ -288,7 +288,7 @@ static const QColor titlebarSeparatorLineInactive(131, 131, 131);
static const QColor darkModeSeparatorLine(88, 88, 88);
// Gradient colors used for the dock widget title bar and
-// non-unifed tool bar bacground.
+// non-unifed tool bar background.
static const QColor lightMainWindowGradientBegin(240, 240, 240);
static const QColor lightMainWindowGradientEnd(200, 200, 200);
static const QColor darkMainWindowGradientBegin(47, 47, 47);
@@ -467,7 +467,11 @@ static bool setupSlider(NSSlider *slider, const QStyleOptionSlider *sl)
if (sl->minimum >= sl->maximum)
return false;
- slider.frame = sl->rect.toCGRect();
+ // NSSlider seems to cache values based on tracking and the last layout of the
+ // NSView, resulting in incorrect knob rects that break the interaction with
+ // multiple sliders. So completely reinitialize the slider.
+ [slider initWithFrame:sl->rect.toCGRect()];
+
slider.minValue = sl->minimum;
slider.maxValue = sl->maximum;
slider.intValue = sl->sliderPosition;
@@ -497,6 +501,14 @@ static bool setupSlider(NSSlider *slider, const QStyleOptionSlider *sl)
// the cell for its metrics and to draw itself.
[slider layoutSubtreeIfNeeded];
+ if (sl->state & QStyle::State_Sunken) {
+ const CGRect knobRect = [slider.cell knobRectFlipped:slider.isFlipped];
+ CGPoint pressPoint;
+ pressPoint.x = CGRectGetMidX(knobRect);
+ pressPoint.y = CGRectGetMidY(knobRect);
+ [slider.cell startTrackingAt:pressPoint inView:slider];
+ }
+
return true;
}
@@ -844,6 +856,8 @@ static QSize qt_aqua_get_known_size(QStyle::ContentsType ct, const QStyleOption
return ret;
}
+ const bool isBigSurOrAbove = QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSBigSur;
+
if (ct == QStyle::CT_CustomBase && widg) {
#if QT_CONFIG(pushbutton)
if (qobject_cast<const QPushButton *>(widg))
@@ -1024,6 +1038,8 @@ static QSize qt_aqua_get_known_size(QStyle::ContentsType ct, const QStyleOption
w = qt_mac_aqua_get_metric(HSliderHeight);
if (sld->tickPosition != QSlider::NoTicks)
w += qt_mac_aqua_get_metric(HSliderTickHeight);
+ else if (isBigSurOrAbove)
+ w += 3;
} else {
w = qt_mac_aqua_get_metric(VSliderWidth);
if (sld->tickPosition != QSlider::NoTicks)
@@ -1087,7 +1103,7 @@ static QSize qt_aqua_get_known_size(QStyle::ContentsType ct, const QStyleOption
#if QT_CONFIG(combobox)
case QStyle::CT_LineEdit:
if (!widg || !qobject_cast<QComboBox *>(widg->parentWidget())) {
- //should I take into account the font dimentions of the lineedit? -Sam
+ //should I take into account the font dimensions of the lineedit? -Sam
if (sz == QStyleHelper::SizeLarge)
ret = QSize(-1, 21);
else
@@ -3112,7 +3128,7 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai
// vertically and even worse, if QTabWidget has autoFillBackground
// set, this background overpaints NSBox making it to disappear.
// We trick our NSBox to render in a larger rectangle, so that
- // the actuall result (which is again smaller than requested),
+ // the actual result (which is again smaller than requested),
// more or less is what we really want. We'll have to adjust CTM
// and translate accordingly.
adjustedRect.adjust(0, 0, 6, 6);
@@ -3557,34 +3573,6 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter
}
break;
- case CE_HeaderLabel:
- if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
- p->save();
- QRect textr = header->rect;
- if (!header->icon.isNull()) {
- QIcon::Mode mode = QIcon::Disabled;
- if (opt->state & State_Enabled)
- mode = QIcon::Normal;
- int iconExtent = proxy()->pixelMetric(PM_SmallIconSize);
- QPixmap pixmap = header->icon.pixmap(QSize(iconExtent, iconExtent), p->device()->devicePixelRatio(), mode);
-
- QRect pixr = header->rect;
- QSizeF size = pixmap.deviceIndependentSize();
- pixr.setY(header->rect.center().y() - (size.height() - 1) / 2);
- proxy()->drawItemPixmap(p, pixr, Qt::AlignVCenter, pixmap);
- textr.translate(size.width() + 2, 0);
- }
- QString text = header->text;
- if (const QStyleOptionHeaderV2 *headerV2 = qstyleoption_cast<const QStyleOptionHeaderV2 *>(header)) {
- if (headerV2->textElideMode != Qt::ElideNone)
- text = header->fontMetrics.elidedText(text, headerV2->textElideMode, textr.width());
- }
-
- proxy()->drawItemText(p, textr, header->textAlignment | Qt::AlignVCenter, header->palette,
- header->state.testFlag(State_Enabled), text, QPalette::ButtonText);
- p->restore();
- }
- break;
case CE_ToolButtonLabel:
if (const QStyleOptionToolButton *tb = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) {
QStyleOptionToolButton myTb = *tb;
@@ -3722,7 +3710,7 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter
const auto cw = QMacStylePrivate::CocoaControl(ct, cs);
auto *pb = static_cast<NSButton *>(d->cocoaControl(cw));
// Ensure same size and location as we used to have with HITheme.
- // This is more convoluted than we initialy thought. See for example
+ // This is more convoluted than we initially thought. See for example
// differences between plain and menu button frames.
const QRectF frameRect = cw.adjustedControlFrame(btn->rect);
pb.frame = frameRect.toCGRect();
@@ -4362,7 +4350,7 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter
const QChar key = rightMarginText.at(rightMarginText.length() - 1);
const QString modifiers = rightMarginText.left(rightMarginText.size() - 1);
p->drawText(xp + tabwidth - maxKeyWidth, yPos, maxKeyWidth, mi->rect.height(), text_flags, key);
- // don't clip the shortcuts; maxKeyWidth might be more than what we have been alotted by the menu
+ // don't clip the shortcuts; maxKeyWidth might be more than what we have been allotted by the menu
p->drawText(xp, yPos, tabwidth - maxKeyWidth, mi->rect.height(),
text_flags | Qt::AlignRight | Qt::TextDontClip, modifiers);
} else { // draw the whole thing left-aligned for complex or unparsable cases
@@ -5449,7 +5437,7 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex
}
#if 0
- // FIXME: Sadly, this part doesn't work. It seems to somehow polute the
+ // FIXME: Sadly, this part doesn't work. It seems to somehow pollute the
// NSSlider's internal state and, when we need to use the "else" part,
// the slider's frame is not in sync with its cell dimensions.
const bool drawAllParts = drawKnob && drawBar && (!hasTicks || drawTicks);
diff --git a/src/plugins/styles/windowsvista/qwindowsxpstyle.cpp b/src/plugins/styles/windowsvista/qwindowsxpstyle.cpp
index d91da5a04c..80b344c8e1 100644
--- a/src/plugins/styles/windowsvista/qwindowsxpstyle.cpp
+++ b/src/plugins/styles/windowsvista/qwindowsxpstyle.cpp
@@ -270,7 +270,7 @@ void QWindowsXPStylePrivate::cleanup(bool force)
/* In order to obtain the correct VistaTreeViewTheme (arrows for PE_IndicatorBranch),
* we need to set the windows "explorer" theme explicitly on a native
* window and open the "TREEVIEW" theme handle passing its window handle
- * in order to get Vista-style item view themes (particulary drawBackground()
+ * in order to get Vista-style item view themes (particularly drawBackground()
* for selected items needs this).
* We invoke a service of the native Windows interface to create
* a non-visible window handle, open the theme on it and insert it into
@@ -316,7 +316,7 @@ void QWindowsXPStylePrivate::cleanupVistaTreeViewTheming()
/* \internal
Closes all open theme data handles to ensure that we don't leak
- resources, and that we don't refere to old handles when for
+ resources, and that we don't refer to old handles when for
example the user changes the theme style.
*/
void QWindowsXPStylePrivate::cleanupHandleMap()
@@ -360,7 +360,7 @@ bool QWindowsXPStylePrivate::isItemViewDelegateLineEdit(const QWidget *widget)
if (!widget)
return false;
const QWidget *parent1 = widget->parentWidget();
- // Exlude dialogs or other toplevels parented on item views.
+ // Exclude dialogs or other toplevels parented on item views.
if (!parent1 || parent1->isWindow())
return false;
const QWidget *parent2 = parent1->parentWidget();
@@ -570,7 +570,7 @@ bool QWindowsXPStylePrivate::hasAlphaChannel(const QRect &rect)
The rule of thumb for premultiplied pixmaps is that the color
values of a pixel can never be higher than the alpha values, so
we use this to our advantage here, and fix all instances where
- this occures.
+ this occurs.
*/
bool QWindowsXPStylePrivate::fixAlphaChannel(const QRect &rect)
{
@@ -598,7 +598,7 @@ bool QWindowsXPStylePrivate::fixAlphaChannel(const QRect &rect)
Swaps the alpha values on certain pixels:
0xFF?????? -> 0x00??????
0x00?????? -> 0xFF??????
- Used to determin the mask of a non-alpha transparent pixmap in
+ Used to determine the mask of a non-alpha transparent pixmap in
the native doublebuffer, and swap the alphas so we may paint
the image as a Premultiplied QImage with drawImage(), and obtain
the mask transparency.
@@ -782,7 +782,7 @@ bool QWindowsXPStylePrivate::drawBackgroundDirectly(HDC dc, XPThemeData &themeDa
It should only be used when the painteengine doesn't provide a proper
HDC for direct painting (e.g. when doing a grabWidget(), painting to
other pixmaps etc), or when special transformations are needed (e.g.
- flips (horizonal mirroring only, vertical are handled by the theme
+ flips (horizontal mirroring only, vertical are handled by the theme
engine).
\a correctionFactor is an additional factor used to scale up controls
@@ -3509,7 +3509,7 @@ static QList<PropPair> all_props;
directly.
Since we cannot rely on the pixel data we get from Microsoft
when drawing into the DIB section, we use this function to
- see the actual data we got, and can determin the appropriate
+ see the actual data we got, and can determine the appropriate
action.
*/
void QWindowsXPStylePrivate::dumpNativeDIB(int w, int h)
diff --git a/src/plugins/tls/openssl/qsslcontext_openssl.cpp b/src/plugins/tls/openssl/qsslcontext_openssl.cpp
index 54d749b147..0ed0590409 100644
--- a/src/plugins/tls/openssl/qsslcontext_openssl.cpp
+++ b/src/plugins/tls/openssl/qsslcontext_openssl.cpp
@@ -734,6 +734,7 @@ QT_WARNING_POP
}
if (!dhparams.isEmpty()) {
+#ifndef OPENSSL_NO_DEPRECATED_3_0
const QByteArray &params = dhparams.d->derData;
const char *ptr = params.constData();
DH *dh = q_d2i_DHparams(nullptr, reinterpret_cast<const unsigned char **>(&ptr),
@@ -742,6 +743,9 @@ QT_WARNING_POP
qFatal("q_d2i_DHparams failed to convert QSslDiffieHellmanParameters to DER form");
q_SSL_CTX_set_tmp_dh(sslContext->ctx, dh);
q_DH_free(dh);
+#else
+ qCWarning(lcTlsBackend, "Diffie-Hellman parameters are not supported, because OpenSSL v3 was built with deprecated API removed");
+#endif
}
#ifndef OPENSSL_NO_PSK
diff --git a/src/plugins/tls/openssl/qssldiffiehellmanparameters_openssl.cpp b/src/plugins/tls/openssl/qssldiffiehellmanparameters_openssl.cpp
index 8a268eeebe..f1cbb835d1 100644
--- a/src/plugins/tls/openssl/qssldiffiehellmanparameters_openssl.cpp
+++ b/src/plugins/tls/openssl/qssldiffiehellmanparameters_openssl.cpp
@@ -53,6 +53,8 @@
QT_BEGIN_NAMESPACE
+#ifndef OPENSSL_NO_DEPRECATED_3_0
+
namespace {
bool isSafeDH(DH *dh)
@@ -102,8 +104,11 @@ bool isSafeDH(DH *dh)
} // unnamed namespace
+#endif
+
int QTlsBackendOpenSSL::dhParametersFromDer(const QByteArray &der, QByteArray *derData) const
{
+#ifndef OPENSSL_NO_DEPRECATED_3_0
Q_ASSERT(derData);
if (der.isEmpty())
@@ -127,12 +132,17 @@ int QTlsBackendOpenSSL::dhParametersFromDer(const QByteArray &der, QByteArray *d
} else {
return DHParams::InvalidInputDataError;
}
-
+#else
+ Q_UNUSED(der);
+ Q_UNUSED(derData);
+ qCWarning(lcTlsBackend, "Diffie-Hellman parameters are not supported, because OpenSSL v3 was built with deprecated API removed");
+#endif
return DHParams::NoError;
}
int QTlsBackendOpenSSL::dhParametersFromPem(const QByteArray &pem, QByteArray *data) const
{
+#ifndef OPENSSL_NO_DEPRECATED_3_0
Q_ASSERT(data);
if (pem.isEmpty())
@@ -173,7 +183,11 @@ int QTlsBackendOpenSSL::dhParametersFromPem(const QByteArray &pem, QByteArray *d
} else {
return DHParams::InvalidInputDataError;
}
-
+#else
+ Q_UNUSED(pem);
+ Q_UNUSED(data);
+ qCWarning(lcTlsBackend, "Diffie-Hellman parameters are not supported, because OpenSSL v3 was built with deprecated API removed");
+#endif
return DHParams::NoError;
}
diff --git a/src/plugins/tls/openssl/qsslsocket_openssl_symbols.cpp b/src/plugins/tls/openssl/qsslsocket_openssl_symbols.cpp
index a30348b20e..27ed594d6d 100644
--- a/src/plugins/tls/openssl/qsslsocket_openssl_symbols.cpp
+++ b/src/plugins/tls/openssl/qsslsocket_openssl_symbols.cpp
@@ -258,8 +258,6 @@ DEFINEFUNC3(int, BIO_read, BIO *a, a, void *b, b, int c, c, return -1, return)
DEFINEFUNC3(int, BIO_write, BIO *a, a, const void *b, b, int c, c, return -1, return)
DEFINEFUNC(int, BN_num_bits, const BIGNUM *a, a, return 0, return)
DEFINEFUNC2(BN_ULONG, BN_mod_word, const BIGNUM *a, a, BN_ULONG w, w, return static_cast<BN_ULONG>(-1), return)
-DEFINEFUNC(DSA *, DSA_new, DUMMYARG, DUMMYARG, return nullptr, return)
-DEFINEFUNC(void, DSA_free, DSA *a, a, return, DUMMYARG)
DEFINEFUNC3(X509 *, d2i_X509, X509 **a, a, const unsigned char **b, b, long c, c, return nullptr, return)
DEFINEFUNC2(char *, ERR_error_string, unsigned long a, a, char *b, b, return nullptr, return)
DEFINEFUNC3(void, ERR_error_string_n, unsigned long e, e, char *b, b, size_t len, len, return, DUMMYARG)
@@ -307,8 +305,6 @@ DEFINEFUNC2(int, PEM_write_bio_PUBKEY, BIO *a, a, EVP_PKEY *b, b, return 0, retu
DEFINEFUNC2(void, RAND_seed, const void *a, a, int b, b, return, DUMMYARG)
DEFINEFUNC(int, RAND_status, void, DUMMYARG, return -1, return)
DEFINEFUNC2(int, RAND_bytes, unsigned char *b, b, int n, n, return 0, return)
-DEFINEFUNC(RSA *, RSA_new, DUMMYARG, DUMMYARG, return nullptr, return)
-DEFINEFUNC(void, RSA_free, RSA *a, a, return, DUMMYARG)
DEFINEFUNC(int, SSL_accept, SSL *a, a, return -1, return)
DEFINEFUNC(int, SSL_clear, SSL *a, a, return -1, return)
DEFINEFUNC3(char *, SSL_CIPHER_description, const SSL_CIPHER *a, a, char *b, b, int c, c, return nullptr, return)
@@ -327,7 +323,6 @@ DEFINEFUNC2(void, SSL_CTX_set_verify_depth, SSL_CTX *a, a, int b, b, return, DUM
DEFINEFUNC2(int, SSL_CTX_use_certificate, SSL_CTX *a, a, X509 *b, b, return -1, return)
DEFINEFUNC3(int, SSL_CTX_use_certificate_file, SSL_CTX *a, a, const char *b, b, int c, c, return -1, return)
DEFINEFUNC2(int, SSL_CTX_use_PrivateKey, SSL_CTX *a, a, EVP_PKEY *b, b, return -1, return)
-DEFINEFUNC2(int, SSL_CTX_use_RSAPrivateKey, SSL_CTX *a, a, RSA *b, b, return -1, return)
DEFINEFUNC3(int, SSL_CTX_use_PrivateKey_file, SSL_CTX *a, a, const char *b, b, int c, c, return -1, return)
DEFINEFUNC(X509_STORE *, SSL_CTX_get_cert_store, const SSL_CTX *a, a, return nullptr, return)
DEFINEFUNC(SSL_CONF_CTX *, SSL_CONF_CTX_new, DUMMYARG, DUMMYARG, return nullptr, return);
@@ -476,9 +471,6 @@ DEFINEFUNC2(int, DH_check, DH *dh, dh, int *codes, codes, return 0, return)
DEFINEFUNC3(BIGNUM *, BN_bin2bn, const unsigned char *s, s, int len, len, BIGNUM *ret, ret, return nullptr, return)
#ifndef OPENSSL_NO_EC
-DEFINEFUNC(EC_KEY *, EC_KEY_dup, const EC_KEY *ec, ec, return nullptr, return)
-DEFINEFUNC(EC_KEY *, EC_KEY_new_by_curve_name, int nid, nid, return nullptr, return)
-DEFINEFUNC(void, EC_KEY_free, EC_KEY *ecdh, ecdh, return, DUMMYARG)
DEFINEFUNC2(size_t, EC_get_builtin_curves, EC_builtin_curve * r, r, size_t nitems, nitems, return 0, return)
DEFINEFUNC(int, EC_curve_nist2nid, const char *name, name, return 0, return)
#endif // OPENSSL_NO_EC
@@ -500,6 +492,14 @@ DEFINEFUNC2(int, PEM_write_bio_RSA_PUBKEY, BIO *a, a, RSA *b, b, return 0, retur
DEFINEFUNC7(int, PEM_write_bio_DSAPrivateKey, BIO *a, a, DSA *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return)
DEFINEFUNC7(int, PEM_write_bio_RSAPrivateKey, BIO *a, a, RSA *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return)
+DEFINEFUNC2(int, SSL_CTX_use_RSAPrivateKey, SSL_CTX *a, a, RSA *b, b, return -1, return)
+
+DEFINEFUNC(DSA *, DSA_new, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(void, DSA_free, DSA *a, a, return, DUMMYARG)
+
+DEFINEFUNC(RSA *, RSA_new, DUMMYARG, DUMMYARG, return nullptr, return)
+DEFINEFUNC(void, RSA_free, RSA *a, a, return, DUMMYARG)
+
DEFINEFUNC(int, RSA_bits, RSA *a, a, return 0, return)
DEFINEFUNC(int, DSA_bits, DSA *a, a, return 0, return)
DEFINEFUNC(int, DH_bits, DH *dh, dh, return 0, return)
@@ -529,6 +529,10 @@ DEFINEFUNC(int, EC_GROUP_get_degree, const EC_GROUP* g, g, return 0, return)
DEFINEFUNC2(int, EVP_PKEY_set1_EC_KEY, EVP_PKEY *a, a, EC_KEY *b, b, return -1, return)
DEFINEFUNC(EC_KEY *, EVP_PKEY_get1_EC_KEY, EVP_PKEY *a, a, return nullptr, return)
+DEFINEFUNC(EC_KEY *, EC_KEY_dup, const EC_KEY *ec, ec, return nullptr, return)
+DEFINEFUNC(EC_KEY *, EC_KEY_new_by_curve_name, int nid, nid, return nullptr, return)
+DEFINEFUNC(void, EC_KEY_free, EC_KEY *ecdh, ecdh, return, DUMMYARG)
+
#endif // OPENSSL_NO_EC
@@ -700,7 +704,14 @@ static LoadedOpenSsl loadOpenSsl()
LoadedOpenSsl result;
// With OpenSSL 1.1 the names have changed to libssl-1_1 and libcrypto-1_1 for builds using
- // MSVC and GCC, with architecture suffixes for non-x86 builds.
+ // MSVC and GCC. For 3.0 the version suffix changed again, to just '3'.
+ // For non-x86 builds, an architecture suffix is also appended.
+
+#if (OPENSSL_VERSION_NUMBER >> 28) < 3
+#define QT_OPENSSL_VERSION "1_1"
+#elif OPENSSL_VERSION_MAJOR == 3 // Starting with 3.0 this define is available
+#define QT_OPENSSL_VERSION "3"
+#endif // > 3 intentionally left undefined
#if defined(Q_PROCESSOR_X86_64)
#define QT_SSL_SUFFIX "-x64"
@@ -712,8 +723,8 @@ static LoadedOpenSsl loadOpenSsl()
#define QT_SSL_SUFFIX
#endif
- tryToLoadOpenSslWin32Library(QLatin1String("libssl-1_1" QT_SSL_SUFFIX),
- QLatin1String("libcrypto-1_1" QT_SSL_SUFFIX), result);
+ tryToLoadOpenSslWin32Library(QLatin1String("libssl-" QT_OPENSSL_VERSION QT_SSL_SUFFIX),
+ QLatin1String("libcrypto-" QT_OPENSSL_VERSION QT_SSL_SUFFIX), result);
#undef QT_SSL_SUFFIX
return result;
@@ -930,13 +941,25 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(OpenSSL_version_num)
RESOLVEFUNC(OpenSSL_version)
- if (!_q_OpenSSL_version) {
+ if (!_q_OpenSSL_version || !_q_OpenSSL_version_num) {
// Apparently, we were built with OpenSSL 1.1 enabled but are now using
// a wrong library.
qCWarning(lcTlsBackend, "Incompatible version of OpenSSL");
return false;
}
+#if OPENSSL_VERSION_NUMBER >= 0x30000000
+ if (q_OpenSSL_version_num() < 0x30000000) {
+ qCWarning(lcTlsBackend, "Incompatible version of OpenSSL (built with OpenSSL >= 3.x, runtime version is < 3.x)");
+ return false;
+ }
+#else
+ if (q_OpenSSL_version_num() >= 0x30000000) {
+ qCWarning(lcTlsBackend, "Incompatible version of OpenSSL (built with OpenSSL 1.x, runtime version is >= 3.x)");
+ return false;
+ }
+#endif // OPENSSL_VERSION_NUMBER
+
RESOLVEFUNC(SSL_SESSION_get_ticket_lifetime_hint)
#if QT_CONFIG(dtls)
@@ -1002,8 +1025,6 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(BN_num_bits)
RESOLVEFUNC(BN_is_word)
RESOLVEFUNC(BN_mod_word)
- RESOLVEFUNC(DSA_new)
- RESOLVEFUNC(DSA_free)
RESOLVEFUNC(ERR_error_string)
RESOLVEFUNC(ERR_error_string_n)
RESOLVEFUNC(ERR_get_error)
@@ -1048,8 +1069,6 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(RAND_seed)
RESOLVEFUNC(RAND_status)
RESOLVEFUNC(RAND_bytes)
- RESOLVEFUNC(RSA_new)
- RESOLVEFUNC(RSA_free)
RESOLVEFUNC(SSL_CIPHER_description)
RESOLVEFUNC(SSL_CIPHER_get_bits)
RESOLVEFUNC(SSL_get_rbio)
@@ -1065,7 +1084,6 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(SSL_CTX_use_certificate)
RESOLVEFUNC(SSL_CTX_use_certificate_file)
RESOLVEFUNC(SSL_CTX_use_PrivateKey)
- RESOLVEFUNC(SSL_CTX_use_RSAPrivateKey)
RESOLVEFUNC(SSL_CTX_use_PrivateKey_file)
RESOLVEFUNC(SSL_CTX_get_cert_store);
RESOLVEFUNC(SSL_CONF_CTX_new);
@@ -1114,6 +1132,13 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(PEM_write_bio_RSA_PUBKEY)
RESOLVEFUNC(PEM_write_bio_DSAPrivateKey)
RESOLVEFUNC(PEM_write_bio_RSAPrivateKey)
+ RESOLVEFUNC(SSL_CTX_use_RSAPrivateKey)
+
+ RESOLVEFUNC(DSA_new)
+ RESOLVEFUNC(DSA_free)
+
+ RESOLVEFUNC(RSA_new)
+ RESOLVEFUNC(RSA_free)
RESOLVEFUNC(DH_bits)
RESOLVEFUNC(DSA_bits)
@@ -1129,6 +1154,9 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(PEM_write_bio_ECPrivateKey)
RESOLVEFUNC(EC_KEY_get0_group)
RESOLVEFUNC(EC_GROUP_get_degree)
+ RESOLVEFUNC(EC_KEY_dup)
+ RESOLVEFUNC(EC_KEY_new_by_curve_name)
+ RESOLVEFUNC(EC_KEY_free)
#endif // OPENSSL_NO_EC
@@ -1236,9 +1264,6 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(BN_bin2bn)
#ifndef OPENSSL_NO_EC
- RESOLVEFUNC(EC_KEY_dup)
- RESOLVEFUNC(EC_KEY_new_by_curve_name)
- RESOLVEFUNC(EC_KEY_free)
RESOLVEFUNC(EC_get_builtin_curves)
#endif // OPENSSL_NO_EC
diff --git a/src/plugins/tls/openssl/qsslsocket_openssl_symbols_p.h b/src/plugins/tls/openssl/qsslsocket_openssl_symbols_p.h
index 3426635464..5e3feb77b8 100644
--- a/src/plugins/tls/openssl/qsslsocket_openssl_symbols_p.h
+++ b/src/plugins/tls/openssl/qsslsocket_openssl_symbols_p.h
@@ -378,8 +378,6 @@ int q_BN_num_bits(const BIGNUM *a);
int q_BN_is_word(BIGNUM *a, BN_ULONG w);
BN_ULONG q_BN_mod_word(const BIGNUM *a, BN_ULONG w);
-DSA *q_DSA_new();
-void q_DSA_free(DSA *a);
X509 *q_d2i_X509(X509 **a, const unsigned char **b, long c);
char *q_ERR_error_string(unsigned long a, char *b);
void q_ERR_error_string_n(unsigned long e, char *buf, size_t len);
@@ -436,8 +434,6 @@ int q_PEM_write_bio_PUBKEY(BIO *a, EVP_PKEY *b);
void q_RAND_seed(const void *a, int b);
int q_RAND_status();
int q_RAND_bytes(unsigned char *b, int n);
-RSA *q_RSA_new();
-void q_RSA_free(RSA *a);
int q_SSL_accept(SSL *a);
int q_SSL_clear(SSL *a);
char *q_SSL_CIPHER_description(const SSL_CIPHER *a, char *b, int c);
@@ -459,7 +455,6 @@ long q_SSL_CTX_callback_ctrl(SSL_CTX *, int, GenericCallbackType);
int q_SSL_CTX_use_certificate(SSL_CTX *a, X509 *b);
int q_SSL_CTX_use_certificate_file(SSL_CTX *a, const char *b, int c);
int q_SSL_CTX_use_PrivateKey(SSL_CTX *a, EVP_PKEY *b);
-int q_SSL_CTX_use_RSAPrivateKey(SSL_CTX *a, RSA *b);
int q_SSL_CTX_use_PrivateKey_file(SSL_CTX *a, const char *b, int c);
X509_STORE *q_SSL_CTX_get_cert_store(const SSL_CTX *a);
SSL_CONF_CTX *q_SSL_CONF_CTX_new();
@@ -552,9 +547,6 @@ BIGNUM *q_BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret);
#ifndef OPENSSL_NO_EC
// EC Diffie-Hellman support
-EC_KEY *q_EC_KEY_dup(const EC_KEY *src);
-EC_KEY *q_EC_KEY_new_by_curve_name(int nid);
-void q_EC_KEY_free(EC_KEY *ecdh);
#define q_SSL_CTX_set_tmp_ecdh(ctx, ecdh) q_SSL_CTX_ctrl((ctx), SSL_CTRL_SET_TMP_ECDH, 0, (char *)ecdh)
// EC curves management
@@ -724,6 +716,22 @@ int q_EVP_PKEY_base_id(EVP_PKEY *a);
#ifndef OPENSSL_NO_DEPRECATED_3_0
+DSA *q_DSA_new();
+void q_DSA_free(DSA *a);
+
+RSA *q_RSA_new();
+void q_RSA_free(RSA *a);
+
+#ifndef OPENSSL_NO_EC
+
+EC_KEY *q_EC_KEY_dup(const EC_KEY *src);
+EC_KEY *q_EC_KEY_new_by_curve_name(int nid);
+void q_EC_KEY_free(EC_KEY *ecdh);
+
+#endif // OPENSSL_NO_EC
+
+int q_SSL_CTX_use_RSAPrivateKey(SSL_CTX *a, RSA *b);
+
DSA *q_PEM_read_bio_DSA_PUBKEY(BIO *a, DSA **b, pem_password_cb *c, void *d);
RSA *q_PEM_read_bio_RSA_PUBKEY(BIO *a, RSA **b, pem_password_cb *c, void *d);
diff --git a/src/plugins/tls/openssl/qtls_openssl.cpp b/src/plugins/tls/openssl/qtls_openssl.cpp
index 5a9a55ebbd..189730a594 100644
--- a/src/plugins/tls/openssl/qtls_openssl.cpp
+++ b/src/plugins/tls/openssl/qtls_openssl.cpp
@@ -169,9 +169,25 @@ int q_X509Callback(int ok, X509_STORE_CTX *ctx)
// To retrieve this pointer the X509_STORE_CTX_get_ex_data() function can be
// used with the correct index."
const auto offset = QTlsBackendOpenSSL::s_indexForSSLExtraData
- + TlsCryptographOpenSSL::errorOffsetInExData;
- if (SSL *ssl = static_cast<SSL *>(q_X509_STORE_CTX_get_ex_data(ctx, q_SSL_get_ex_data_X509_STORE_CTX_idx())))
+ + TlsCryptographOpenSSL::errorOffsetInExData;
+ if (SSL *ssl = static_cast<SSL *>(q_X509_STORE_CTX_get_ex_data(
+ ctx, q_SSL_get_ex_data_X509_STORE_CTX_idx()))) {
+
+ // We may be in a renegotiation, check if we are inside a call to SSL_read:
+ const auto tlsOffset = QTlsBackendOpenSSL::s_indexForSSLExtraData
+ + TlsCryptographOpenSSL::socketOffsetInExData;
+ auto tls = static_cast<TlsCryptographOpenSSL *>(q_SSL_get_ex_data(ssl, tlsOffset));
+ Q_ASSERT(tls);
+ if (tls->isInSslRead()) {
+ // We are in a renegotiation, make a note of this for later.
+ // We'll check that the certificate is the same as the one we got during
+ // the initial handshake
+ tls->setRenegotiated(true);
+ return 1;
+ }
+
errors = ErrorListPtr(q_SSL_get_ex_data(ssl, offset));
+ }
}
if (!errors) {
@@ -333,7 +349,7 @@ int qt_OCSP_status_server_callback(SSL *ssl, void *ocspRequest)
return SSL_TLSEXT_ERR_ALERT_FATAL;
std::copy(response.data(), response.data() + response.size(), derCopy);
- // We don't check the return value: internally OpenSSL simply assignes the
+ // We don't check the return value: internally OpenSSL simply assigns the
// pointer (it assumes it now owns this memory btw!) and the length.
q_SSL_set_tlsext_status_ocsp_resp(ssl, derCopy, response.size());
@@ -479,7 +495,7 @@ bool qt_OCSP_certificate_match(OCSP_SINGLERESP *singleResponse, X509 *peerCert,
const QSharedPointer<OCSP_CERTID> guard(recreatedId, q_OCSP_CERTID_free);
if (q_OCSP_id_cmp(const_cast<OCSP_CERTID *>(certId), recreatedId)) {
- qDebug(lcTlsBackend, "Certificate ID mismatch");
+ qCDebug(lcTlsBackend, "Certificate ID mismatch");
return false;
}
// Bingo!
@@ -1047,7 +1063,25 @@ void TlsCryptographOpenSSL::transmit()
break;
}
// Don't use SSL_pending(). It's very unreliable.
+ inSslRead = true;
readBytes = q_SSL_read(ssl, buffer.reserve(bytesToRead), bytesToRead);
+ inSslRead = false;
+ if (renegotiated) {
+ renegotiated = false;
+ X509 *x509 = q_SSL_get_peer_certificate(ssl);
+ const auto peerCertificate =
+ QTlsPrivate::X509CertificateOpenSSL::certificateFromX509(x509);
+ // Fail the renegotiate if the certificate has changed, else: continue.
+ if (peerCertificate != q->peerCertificate()) {
+ const ScopedBool bg(inSetAndEmitError, true);
+ setErrorAndEmit(
+ d, QAbstractSocket::RemoteHostClosedError,
+ QSslSocket::tr(
+ "TLS certificate unexpectedly changed during renegotiation!"));
+ q->abort();
+ return;
+ }
+ }
if (readBytes > 0) {
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcTlsBackend) << "TlsCryptographOpenSSL::transmit: decrypted" << readBytes << "bytes";
@@ -1757,6 +1791,16 @@ unsigned TlsCryptographOpenSSL::pskServerTlsCallback(const char *identity, unsig
return pskLength;
}
+bool TlsCryptographOpenSSL::isInSslRead() const
+{
+ return inSslRead;
+}
+
+void TlsCryptographOpenSSL::setRenegotiated(bool renegotiated)
+{
+ this->renegotiated = renegotiated;
+}
+
#ifdef Q_OS_WIN
void TlsCryptographOpenSSL::fetchCaRootForCert(const QSslCertificate &cert)
diff --git a/src/plugins/tls/openssl/qtls_openssl_p.h b/src/plugins/tls/openssl/qtls_openssl_p.h
index 9e7283b15d..2fcefb222c 100644
--- a/src/plugins/tls/openssl/qtls_openssl_p.h
+++ b/src/plugins/tls/openssl/qtls_openssl_p.h
@@ -121,6 +121,9 @@ public:
unsigned pskServerTlsCallback(const char *identity, unsigned char *psk,
unsigned max_psk_len);
+ bool isInSslRead() const;
+ void setRenegotiated(bool renegotiated);
+
#ifdef Q_OS_WIN
void fetchCaRootForCert(const QSslCertificate &cert);
void caRootLoaded(QSslCertificate certificate, QSslCertificate trustedRoot);
@@ -144,7 +147,7 @@ private:
QList<QOcspResponse> ocspResponses;
- // This decription will go to setErrorAndEmit(SslHandshakeError, ocspErrorDescription)
+ // This description will go to setErrorAndEmit(SslHandshakeError, ocspErrorDescription)
QString ocspErrorDescription;
// These will go to sslErrors()
QList<QSslError> ocspErrors;
@@ -160,6 +163,9 @@ private:
bool errorsReportedFromCallback = false;
bool shutdown = false;
+
+ bool inSslRead = false;
+ bool renegotiated = false;
};
} // namespace QTlsPrivate
diff --git a/src/plugins/tls/openssl/qwindowscarootfetcher.cpp b/src/plugins/tls/openssl/qwindowscarootfetcher.cpp
index 45ecbce258..614f907fac 100644
--- a/src/plugins/tls/openssl/qwindowscarootfetcher.cpp
+++ b/src/plugins/tls/openssl/qwindowscarootfetcher.cpp
@@ -84,7 +84,7 @@ const QList<QSslCertificate> buildVerifiedChain(const QList<QSslCertificate> &ca
// by setting custom CA certificates. We convert wincrypt's
// structures in QSslCertificate and give OpenSSL the second
// chance to verify the now (apparently) complete chain.
- // In addition, wincrypt gives us a benifit of some checks
+ // In addition, wincrypt gives us a benefit of some checks
// we don't have in OpenSSL back-end.
Q_ASSERT(chainContext);
diff --git a/src/plugins/tls/shared/qx509_generic.cpp b/src/plugins/tls/shared/qx509_generic.cpp
index f5fd1b6b30..5e06c765e1 100644
--- a/src/plugins/tls/shared/qx509_generic.cpp
+++ b/src/plugins/tls/shared/qx509_generic.cpp
@@ -295,7 +295,7 @@ bool X509CertificateGeneric::parse(const QByteArray &data)
// subjectAltName
// Note, parseExtension() returns true for this extensions,
- // but considers it to be unsupported and assignes a useless
+ // but considers it to be unsupported and assigns a useless
// value. OpenSSL also treats this extension as unsupported,
// but properly creates a map with 'name' and 'value' taken
// from the extension. We only support 'email', 'IP' and 'DNS',