summaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/generic/evdevkeyboard/CMakeLists.txt2
-rw-r--r--src/plugins/generic/evdevmouse/CMakeLists.txt2
-rw-r--r--src/plugins/generic/evdevtablet/CMakeLists.txt2
-rw-r--r--src/plugins/generic/evdevtouch/CMakeLists.txt2
-rw-r--r--src/plugins/generic/libinput/CMakeLists.txt2
-rw-r--r--src/plugins/generic/tslib/CMakeLists.txt2
-rw-r--r--src/plugins/generic/tuiotouch/CMakeLists.txt2
-rw-r--r--src/plugins/imageformats/gif/CMakeLists.txt2
-rw-r--r--src/plugins/imageformats/ico/CMakeLists.txt2
-rw-r--r--src/plugins/imageformats/ico/qicohandler.cpp2
-rw-r--r--src/plugins/imageformats/jpeg/CMakeLists.txt2
-rw-r--r--src/plugins/networkinformation/android/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.java70
-rw-r--r--src/plugins/networkinformation/android/qandroidnetworkinformationbackend.cpp73
-rw-r--r--src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.cpp51
-rw-r--r--src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.h19
-rw-r--r--src/plugins/networkinformation/networklistmanager/CMakeLists.txt13
-rw-r--r--src/plugins/networkinformation/networklistmanager/qnetworklistmanagerevents.cpp250
-rw-r--r--src/plugins/networkinformation/networklistmanager/qnetworklistmanagerevents.h117
-rw-r--r--src/plugins/networkinformation/networklistmanager/qnetworklistmanagernetworkinformationbackend.cpp201
-rw-r--r--src/plugins/networkinformation/networkmanager/CMakeLists.txt4
-rw-r--r--src/plugins/networkinformation/networkmanager/qnetworkmanagernetworkinformationbackend.cpp59
-rw-r--r--src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.cpp74
-rw-r--r--src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.h48
-rw-r--r--src/plugins/networkinformation/scnetworkreachability/CMakeLists.txt4
-rw-r--r--src/plugins/platforminputcontexts/compose/CMakeLists.txt2
-rw-r--r--src/plugins/platforminputcontexts/ibus/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/android/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/android/extract-dummy.cpp8
-rw-r--r--src/plugins/platforms/android/extract.cpp21
-rw-r--r--src/plugins/platforms/android/qandroidinputcontext.cpp65
-rw-r--r--src/plugins/platforms/android/qandroidinputcontext.h2
-rw-r--r--src/plugins/platforms/android/qandroidplatformintegration.cpp13
-rw-r--r--src/plugins/platforms/android/qandroidplatformintegration.h1
-rw-r--r--src/plugins/platforms/android/qandroidplatformservices.cpp37
-rw-r--r--src/plugins/platforms/android/qandroidplatformservices.h14
-rw-r--r--src/plugins/platforms/android/qandroidsystemlocale.cpp12
-rw-r--r--src/plugins/platforms/cocoa/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm18
-rw-r--r--src/plugins/platforms/cocoa/qcocoabackingstore.h2
-rw-r--r--src/plugins/platforms/cocoa/qcocoabackingstore.mm2
-rw-r--r--src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm14
-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.mm13
-rw-r--r--src/plugins/platforms/cocoa/qcocoahelpers.h41
-rw-r--r--src/plugins/platforms/cocoa/qcocoahelpers.mm47
-rw-r--r--src/plugins/platforms/cocoa/qcocoainputcontext.h12
-rw-r--r--src/plugins/platforms/cocoa/qcocoainputcontext.mm95
-rw-r--r--src/plugins/platforms/cocoa/qcocoaintegration.h8
-rw-r--r--src/plugins/platforms/cocoa/qcocoaintegration.mm41
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenu.mm2
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenubar.h2
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenubar.mm49
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenuloader.mm9
-rw-r--r--src/plugins/platforms/cocoa/qcocoascreen.h3
-rw-r--r--src/plugins/platforms/cocoa/qcocoascreen.mm102
-rw-r--r--src/plugins/platforms/cocoa/qcocoatheme.h1
-rw-r--r--src/plugins/platforms/cocoa/qcocoatheme.mm10
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindow.h24
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindow.mm297
-rw-r--r--src/plugins/platforms/cocoa/qmacclipboard.mm2
-rw-r--r--src/plugins/platforms/cocoa/qnsview.h1
-rw-r--r--src/plugins/platforms/cocoa/qnsview.mm43
-rw-r--r--src/plugins/platforms/cocoa/qnsview_complextext.mm666
-rw-r--r--src/plugins/platforms/cocoa/qnsview_keys.mm310
-rw-r--r--src/plugins/platforms/cocoa/qnsview_mouse.mm338
-rw-r--r--src/plugins/platforms/cocoa/qnsview_tablet.mm188
-rw-r--r--src/plugins/platforms/cocoa/qnswindow.mm78
-rw-r--r--src/plugins/platforms/cocoa/qnswindowdelegate.mm8
-rw-r--r--src/plugins/platforms/direct2d/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/directfb/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/eglfs/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/eglfs/api/qeglfswindow.cpp6
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_mali/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_openwfd/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_viv/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/ios/CMakeLists.txt3
-rw-r--r--src/plugins/platforms/ios/optional/nsphotolibrarysupport/CMakeLists.txt7
-rw-r--r--src/plugins/platforms/ios/qiosfiledialog.mm3
-rw-r--r--src/plugins/platforms/ios/qiosintegration.mm3
-rw-r--r--src/plugins/platforms/ios/qiosservices.mm10
-rw-r--r--src/plugins/platforms/ios/quiaccessibilityelement.mm5
-rw-r--r--src/plugins/platforms/ios/quiview.mm7
-rw-r--r--src/plugins/platforms/ios/uistrings.cpp51
-rw-r--r--src/plugins/platforms/ios/uistrings_p.h66
-rw-r--r--src/plugins/platforms/linuxfb/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/minimal/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/minimalegl/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/offscreen/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/offscreen/qoffscreencommon.cpp38
-rw-r--r--src/plugins/platforms/offscreen/qoffscreencommon.h9
-rw-r--r--src/plugins/platforms/offscreen/qoffscreenintegration.cpp210
-rw-r--r--src/plugins/platforms/offscreen/qoffscreenintegration.h15
-rw-r--r--src/plugins/platforms/offscreen/qoffscreenintegration_x11.cpp21
-rw-r--r--src/plugins/platforms/offscreen/qoffscreenintegration_x11.h11
-rw-r--r--src/plugins/platforms/offscreen/qoffscreenwindow.cpp16
-rw-r--r--src/plugins/platforms/qnx/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/vkkhrdisplay/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/vnc/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/wasm/CMakeLists.txt21
-rw-r--r--src/plugins/platforms/wasm/qtloader.js16
-rw-r--r--src/plugins/platforms/wasm/qwasmcompositor.cpp4
-rw-r--r--src/plugins/platforms/wasm/qwasmeventdispatcher.cpp12
-rw-r--r--src/plugins/platforms/wasm/qwasmeventtranslator.cpp5
-rw-r--r--src/plugins/platforms/wasm/qwasmintegration.cpp6
-rw-r--r--src/plugins/platforms/wasm/qwasmwindow.cpp1
-rw-r--r--src/plugins/platforms/windows/CMakeLists.txt2
-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/qwindowscontext.cpp65
-rw-r--r--src/plugins/platforms/windows/qwindowscontext.h4
-rw-r--r--src/plugins/platforms/windows/qwindowsdialoghelpers.cpp2
-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/qwindowsmenu.cpp2
-rw-r--r--src/plugins/platforms/windows/qwindowsmousehandler.cpp11
-rw-r--r--src/plugins/platforms/windows/qwindowspointerhandler.cpp7
-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/qwindowstheme.cpp5
-rw-r--r--src/plugins/platforms/windows/qwindowstheme.h3
-rw-r--r--src/plugins/platforms/windows/qwindowswindow.cpp78
-rw-r--r--src/plugins/platforms/windows/qwindowswindow.h16
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp4
-rw-r--r--src/plugins/platforms/xcb/CMakeLists.txt4
-rw-r--r--src/plugins/platforms/xcb/gl_integrations/xcb_egl/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglintegration.cpp7
-rw-r--r--src/plugins/platforms/xcb/gl_integrations/xcb_glx/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qtessellator.cpp2
-rw-r--r--src/plugins/platforms/xcb/qxcbbackingstore.cpp7
-rw-r--r--src/plugins/platforms/xcb/qxcbclipboard.cpp27
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.cpp44
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.h11
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_basic.cpp5
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_basic.h5
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_screens.cpp392
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_xi2.cpp54
-rw-r--r--src/plugins/platforms/xcb/qxcbcursor.cpp2
-rw-r--r--src/plugins/platforms/xcb/qxcbkeyboard.cpp6
-rw-r--r--src/plugins/platforms/xcb/qxcbnativeinterface.cpp15
-rw-r--r--src/plugins/platforms/xcb/qxcbnativeinterface.h11
-rw-r--r--src/plugins/platforms/xcb/qxcbscreen.cpp141
-rw-r--r--src/plugins/platforms/xcb/qxcbscreen.h19
-rw-r--r--src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp2
-rw-r--r--src/plugins/platforms/xcb/qxcbwindow.cpp117
-rw-r--r--src/plugins/platforms/xcb/qxcbwindow.h16
-rw-r--r--src/plugins/platformthemes/gtk3/CMakeLists.txt2
-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/CMakeLists.txt2
-rw-r--r--src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportalfiledialog.cpp1
-rw-r--r--src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.cpp1
-rw-r--r--src/plugins/printsupport/cups/CMakeLists.txt2
-rw-r--r--src/plugins/sqldrivers/.cmake.conf2
-rw-r--r--src/plugins/sqldrivers/db2/CMakeLists.txt2
-rw-r--r--src/plugins/sqldrivers/ibase/CMakeLists.txt2
-rw-r--r--src/plugins/sqldrivers/ibase/qsql_ibase.cpp2
-rw-r--r--src/plugins/sqldrivers/mysql/CMakeLists.txt2
-rw-r--r--src/plugins/sqldrivers/mysql/qsql_mysql.cpp149
-rw-r--r--src/plugins/sqldrivers/oci/CMakeLists.txt2
-rw-r--r--src/plugins/sqldrivers/odbc/CMakeLists.txt2
-rw-r--r--src/plugins/sqldrivers/psql/CMakeLists.txt2
-rw-r--r--src/plugins/sqldrivers/sqlite/CMakeLists.txt9
-rw-r--r--src/plugins/styles/android/CMakeLists.txt2
-rw-r--r--src/plugins/styles/android/qandroidstyle.cpp3
-rw-r--r--src/plugins/styles/mac/CMakeLists.txt2
-rw-r--r--src/plugins/styles/mac/qmacstyle_mac.mm79
-rw-r--r--src/plugins/styles/windowsvista/CMakeLists.txt2
-rw-r--r--src/plugins/styles/windowsvista/qwindowsvistastyle.cpp4
-rw-r--r--src/plugins/styles/windowsvista/qwindowsxpstyle.cpp18
-rw-r--r--src/plugins/tls/certonly/CMakeLists.txt4
-rw-r--r--src/plugins/tls/openssl/CMakeLists.txt6
-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.cpp76
-rw-r--r--src/plugins/tls/openssl/qsslsocket_openssl_symbols_p.h30
-rw-r--r--src/plugins/tls/openssl/qtls_openssl.cpp13
-rw-r--r--src/plugins/tls/openssl/qtls_openssl_p.h2
-rw-r--r--src/plugins/tls/openssl/qtlsbackend_openssl.cpp7
-rw-r--r--src/plugins/tls/openssl/qwindowscarootfetcher.cpp2
-rw-r--r--src/plugins/tls/openssl/qx509_openssl.cpp63
-rw-r--r--src/plugins/tls/schannel/CMakeLists.txt4
-rw-r--r--src/plugins/tls/securetransport/CMakeLists.txt4
-rw-r--r--src/plugins/tls/shared/qx509_generic.cpp2
196 files changed, 4157 insertions, 1831 deletions
diff --git a/src/plugins/generic/evdevkeyboard/CMakeLists.txt b/src/plugins/generic/evdevkeyboard/CMakeLists.txt
index 7134a2d858..1568ac82cb 100644
--- a/src/plugins/generic/evdevkeyboard/CMakeLists.txt
+++ b/src/plugins/generic/evdevkeyboard/CMakeLists.txt
@@ -6,7 +6,7 @@
qt_internal_add_plugin(QEvdevKeyboardPlugin
OUTPUT_NAME qevdevkeyboardplugin
- TYPE generic
+ PLUGIN_TYPE generic
DEFAULT_IF FALSE
SOURCES
main.cpp
diff --git a/src/plugins/generic/evdevmouse/CMakeLists.txt b/src/plugins/generic/evdevmouse/CMakeLists.txt
index 78cf80f588..f467f631f5 100644
--- a/src/plugins/generic/evdevmouse/CMakeLists.txt
+++ b/src/plugins/generic/evdevmouse/CMakeLists.txt
@@ -6,7 +6,7 @@
qt_internal_add_plugin(QEvdevMousePlugin
OUTPUT_NAME qevdevmouseplugin
- TYPE generic
+ PLUGIN_TYPE generic
DEFAULT_IF FALSE
SOURCES
main.cpp
diff --git a/src/plugins/generic/evdevtablet/CMakeLists.txt b/src/plugins/generic/evdevtablet/CMakeLists.txt
index 880090dd02..4f39c1be87 100644
--- a/src/plugins/generic/evdevtablet/CMakeLists.txt
+++ b/src/plugins/generic/evdevtablet/CMakeLists.txt
@@ -6,7 +6,7 @@
qt_internal_add_plugin(QEvdevTabletPlugin
OUTPUT_NAME qevdevtabletplugin
- TYPE generic
+ PLUGIN_TYPE generic
DEFAULT_IF FALSE
SOURCES
main.cpp
diff --git a/src/plugins/generic/evdevtouch/CMakeLists.txt b/src/plugins/generic/evdevtouch/CMakeLists.txt
index d15367c6c2..4b90efbd69 100644
--- a/src/plugins/generic/evdevtouch/CMakeLists.txt
+++ b/src/plugins/generic/evdevtouch/CMakeLists.txt
@@ -6,7 +6,7 @@
qt_internal_add_plugin(QEvdevTouchScreenPlugin
OUTPUT_NAME qevdevtouchplugin
- TYPE generic
+ PLUGIN_TYPE generic
DEFAULT_IF FALSE
SOURCES
main.cpp
diff --git a/src/plugins/generic/libinput/CMakeLists.txt b/src/plugins/generic/libinput/CMakeLists.txt
index 79da268510..bf423c601d 100644
--- a/src/plugins/generic/libinput/CMakeLists.txt
+++ b/src/plugins/generic/libinput/CMakeLists.txt
@@ -6,7 +6,7 @@
qt_internal_add_plugin(QLibInputPlugin
OUTPUT_NAME qlibinputplugin
- TYPE generic
+ PLUGIN_TYPE generic
DEFAULT_IF FALSE
SOURCES
main.cpp
diff --git a/src/plugins/generic/tslib/CMakeLists.txt b/src/plugins/generic/tslib/CMakeLists.txt
index 9a6e368825..42d6f59b13 100644
--- a/src/plugins/generic/tslib/CMakeLists.txt
+++ b/src/plugins/generic/tslib/CMakeLists.txt
@@ -8,7 +8,7 @@ qt_find_package(Tslib) # special case
qt_internal_add_plugin(QTsLibPlugin
OUTPUT_NAME qtslibplugin
- TYPE generic
+ PLUGIN_TYPE generic
DEFAULT_IF FALSE
SOURCES
main.cpp
diff --git a/src/plugins/generic/tuiotouch/CMakeLists.txt b/src/plugins/generic/tuiotouch/CMakeLists.txt
index b572b140b4..8271216182 100644
--- a/src/plugins/generic/tuiotouch/CMakeLists.txt
+++ b/src/plugins/generic/tuiotouch/CMakeLists.txt
@@ -6,7 +6,7 @@
qt_internal_add_plugin(QTuioTouchPlugin
OUTPUT_NAME qtuiotouchplugin
- TYPE generic
+ PLUGIN_TYPE generic
DEFAULT_IF FALSE
SOURCES
main.cpp
diff --git a/src/plugins/imageformats/gif/CMakeLists.txt b/src/plugins/imageformats/gif/CMakeLists.txt
index 1ffd702f85..fc9d013e75 100644
--- a/src/plugins/imageformats/gif/CMakeLists.txt
+++ b/src/plugins/imageformats/gif/CMakeLists.txt
@@ -6,7 +6,7 @@
qt_internal_add_plugin(QGifPlugin
OUTPUT_NAME qgif
- TYPE imageformats
+ PLUGIN_TYPE imageformats
SOURCES
main.cpp main.h
qgifhandler.cpp qgifhandler_p.h
diff --git a/src/plugins/imageformats/ico/CMakeLists.txt b/src/plugins/imageformats/ico/CMakeLists.txt
index 2d1dc38e17..4d6f765e5a 100644
--- a/src/plugins/imageformats/ico/CMakeLists.txt
+++ b/src/plugins/imageformats/ico/CMakeLists.txt
@@ -6,7 +6,7 @@
qt_internal_add_plugin(QICOPlugin
OUTPUT_NAME qico
- TYPE imageformats
+ PLUGIN_TYPE imageformats
SOURCES
main.cpp main.h
qicohandler.cpp qicohandler.h
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/imageformats/jpeg/CMakeLists.txt b/src/plugins/imageformats/jpeg/CMakeLists.txt
index c13c6807e1..54c6441a14 100644
--- a/src/plugins/imageformats/jpeg/CMakeLists.txt
+++ b/src/plugins/imageformats/jpeg/CMakeLists.txt
@@ -8,7 +8,7 @@ qt_find_package(JPEG) # special case
qt_internal_add_plugin(QJpegPlugin
OUTPUT_NAME qjpeg
- TYPE imageformats
+ PLUGIN_TYPE imageformats
SOURCES
main.cpp main.h
qjpeghandler.cpp qjpeghandler_p.h
diff --git a/src/plugins/networkinformation/android/CMakeLists.txt b/src/plugins/networkinformation/android/CMakeLists.txt
index f1d260547a..0883ec74e2 100644
--- a/src/plugins/networkinformation/android/CMakeLists.txt
+++ b/src/plugins/networkinformation/android/CMakeLists.txt
@@ -15,9 +15,9 @@ install_jar(Qt${QtBase_VERSION_MAJOR}AndroidNetworkInformationBackend
)
qt_internal_add_plugin(QAndroidNetworkInformationPlugin
- OUTPUT_NAME androidnetworkinformation
+ OUTPUT_NAME qandroidnetworkinformation
CLASS_NAME QAndroidNetworkInformationBackendFactory
- TYPE networkinformation
+ PLUGIN_TYPE networkinformation
DEFAULT_IF ANDROID
SOURCES
qandroidnetworkinformationbackend.cpp
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..51a3b43fd0 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 connectivityChanged(AndroidConnectivity connectivity);
private static native void behindCaptivePortalChanged(boolean state);
+ 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,53 @@ 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);
}
+ 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 +156,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..14e42a9de8 100644
--- a/src/plugins/networkinformation/android/qandroidnetworkinformationbackend.cpp
+++ b/src/plugins/networkinformation/android/qandroidnetworkinformationbackend.cpp
@@ -65,13 +65,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;
};
@@ -111,24 +116,60 @@ 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);
+}
+
+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..e95a7db610 100644
--- a/src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.cpp
+++ b/src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.cpp
@@ -56,11 +56,13 @@ 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)
@@ -70,6 +72,15 @@ static void behindCaptivePortalChanged(JNIEnv *env, jobject obj, jboolean state)
Q_EMIT androidConnManagerInstance->connManager->captivePortalChanged(state);
}
+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()
{
if (!registerNatives())
@@ -101,30 +112,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 +119,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) },
+ { "connectivityChanged", connectivityEnumSig.data(),
+ reinterpret_cast<void *>(networkConnectivityChanged) },
{ "behindCaptivePortalChanged", "(Z)V",
- reinterpret_cast<void *>(behindCaptivePortalChanged) }
+ reinterpret_cast<void *>(behindCaptivePortalChanged) },
+ { "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..b335a4a3e4 100644
--- a/src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.h
+++ b/src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.h
@@ -49,17 +49,32 @@ 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);
private:
friend struct AndroidConnectivityManagerInstance;
diff --git a/src/plugins/networkinformation/networklistmanager/CMakeLists.txt b/src/plugins/networkinformation/networklistmanager/CMakeLists.txt
index 1ffdfdec17..d927d4af60 100644
--- a/src/plugins/networkinformation/networklistmanager/CMakeLists.txt
+++ b/src/plugins/networkinformation/networklistmanager/CMakeLists.txt
@@ -1,13 +1,20 @@
qt_internal_add_plugin(QNLMNIPlugin
- OUTPUT_NAME networklistmanager
+ OUTPUT_NAME qnetworklistmanager
CLASS_NAME QNetworkListManagerNetworkInformationBackendFactory
- TYPE networkinformation
+ 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..dcc1439482
--- /dev/null
+++ b/src/plugins/networkinformation/networklistmanager/qnetworklistmanagerevents.cpp
@@ -0,0 +1,250 @@
+/****************************************************************************
+**
+** 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);
+ emit transportMediumChanged(getTransportMedium());
+ });
+ // Emit initial state
+ emit transportMediumChanged(getTransportMedium());
+#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;
+}
+
+// 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...)!
+QNetworkInformation::TransportMedium QNetworkListManagerEvents::getTransportMedium()
+{
+#ifdef SUPPORTS_WINRT
+ using namespace winrt::Windows::Networking::Connectivity;
+ ConnectionProfile profile = NetworkInformation::GetInternetConnectionProfile();
+ if (profile == nullptr)
+ return QNetworkInformation::TransportMedium::Unknown;
+
+ 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());
+#else
+ return QNetworkInformation::TransportMedium::Unknown;
+#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..8618a09a84
--- /dev/null
+++ b/src/plugins/networkinformation/networklistmanager/qnetworklistmanagerevents.h
@@ -0,0 +1,117 @@
+/****************************************************************************
+**
+** 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);
+
+private:
+ [[nodiscard]] QNetworkInformation::TransportMedium getTransportMedium();
+
+ ComPtr<INetworkListManager> networkListManager = nullptr;
+ ComPtr<IConnectionPoint> connectionPoint = nullptr;
+
+#ifdef SUPPORTS_WINRT
+ 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 d37f83832b..e8b36be02e 100644
--- a/src/plugins/networkinformation/networklistmanager/qnetworklistmanagernetworkinformationbackend.cpp
+++ b/src/plugins/networkinformation/networklistmanager/qnetworklistmanagernetworkinformationbackend.cpp
@@ -39,42 +39,20 @@
#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)
+
+// Declared in qnetworklistmanagerevents.h
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());
-}
-
-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;
-}
-
bool testCONNECTIVITY(NLM_CONNECTIVITY connectivity, NLM_CONNECTIVITY flag)
{
return (connectivity & flag) == flag;
@@ -105,7 +83,6 @@ QNetworkInformation::Reachability reachabilityFromNLM_CONNECTIVITY(NLM_CONNECTIV
}
}
-class QNetworkListManagerEvents;
class QNetworkListManagerNetworkInformationBackend : public QNetworkInformationBackend
{
Q_OBJECT
@@ -122,15 +99,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();
@@ -172,165 +151,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
- 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);
@@ -342,6 +162,9 @@ QNetworkListManagerNetworkInformationBackend::QNetworkListManagerNetworkInformat
managerEvents = new QNetworkListManagerEvents();
connect(managerEvents.Get(), &QNetworkListManagerEvents::connectivityChanged, this,
&QNetworkListManagerNetworkInformationBackend::setConnectivity);
+
+ connect(managerEvents.Get(), &QNetworkListManagerEvents::transportMediumChanged, this,
+ &QNetworkListManagerNetworkInformationBackend::setTransportMedium);
}
QNetworkListManagerNetworkInformationBackend::~QNetworkListManagerNetworkInformationBackend()
diff --git a/src/plugins/networkinformation/networkmanager/CMakeLists.txt b/src/plugins/networkinformation/networkmanager/CMakeLists.txt
index a80a7ac656..5fc69f2d55 100644
--- a/src/plugins/networkinformation/networkmanager/CMakeLists.txt
+++ b/src/plugins/networkinformation/networkmanager/CMakeLists.txt
@@ -1,7 +1,7 @@
qt_internal_add_plugin(QNetworkManagerNetworkInformationPlugin
- OUTPUT_NAME networkmanager
+ OUTPUT_NAME qnetworkmanager
CLASS_NAME QNetworkManagerNetworkInformationBackendFactory
- TYPE networkinformation
+ PLUGIN_TYPE networkinformation
DEFAULT_IF LINUX
SOURCES
qnetworkmanagernetworkinformationbackend.cpp
diff --git a/src/plugins/networkinformation/networkmanager/qnetworkmanagernetworkinformationbackend.cpp b/src/plugins/networkinformation/networkmanager/qnetworkmanagernetworkinformationbackend.cpp
index bfb04ae4a6..ad61b6baea 100644
--- a/src/plugins/networkinformation/networkmanager/qnetworkmanagernetworkinformationbackend.cpp
+++ b/src/plugins/networkinformation/networkmanager/qnetworkmanagernetworkinformationbackend.cpp
@@ -70,6 +70,54 @@ 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;
+}
}
static QString backendName = QStringLiteral("networkmanager");
@@ -92,7 +140,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);
}
bool isValid() const { return iface.isValid(); }
@@ -154,6 +203,14 @@ QNetworkManagerNetworkInformationBackend::QNetworkManagerNetworkInformationBacke
const bool behindPortal = (state == ConnectivityState::NM_CONNECTIVITY_PORTAL);
setBehindCaptivePortal(behindPortal);
});
+
+ using NMDeviceType = QNetworkManagerInterface::NMDeviceType;
+ setTransportMedium(transportMediumFromDeviceType(iface.deviceType()));
+
+ connect(&iface, &QNetworkManagerInterface::deviceTypeChanged, this,
+ [this](NMDeviceType newDevice) {
+ setTransportMedium(transportMediumFromDeviceType(newDevice));
+ });
}
QT_END_NAMESPACE
diff --git a/src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.cpp b/src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.cpp
index 764507fd4b..82a797ab87 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,62 @@ QNetworkManagerInterface::NMConnectivityState QNetworkManagerInterface::connecti
return QNetworkManagerInterface::NM_CONNECTIVITY_UNKNOWN;
}
-void QNetworkManagerInterface::setProperties(const QMap<QString, QVariant> &map)
+QNetworkManagerInterface::NMDeviceType QNetworkManagerInterface::deviceType() const
+{
+ auto it = propertyMap.constFind("PrimaryConnection");
+ if (it != propertyMap.cend())
+ return extractDeviceType(it->value<QDBusObjectPath>());
+ return NM_DEVICE_TYPE_UNKNOWN;
+}
+
+auto QNetworkManagerInterface::extractDeviceType(QDBusObjectPath devicePath) const -> NMDeviceType
{
+ const QDBusInterface connection(NM_DBUS_SERVICE, devicePath.path(),
+ NM_CONNECTION_DBUS_INTERFACE, QDBusConnection::systemBus());
+ if (!connection.isValid())
+ return NM_DEVICE_TYPE_UNKNOWN;
+
+ const auto devicePaths = connection.property("Devices").value<QList<QDBusObjectPath>>();
+ if (devicePaths.isEmpty())
+ return NM_DEVICE_TYPE_UNKNOWN;
+
+ const QDBusObjectPath primaryDevicePath = devicePaths.front();
+ const QDBusInterface device(NM_DBUS_SERVICE, primaryDevicePath.path(), NM_DEVICE_DBUS_INTERFACE,
+ QDBusConnection::systemBus());
+ const QVariant deviceType = device.property("DeviceType");
+ if (!deviceType.isValid())
+ return NM_DEVICE_TYPE_UNKNOWN;
+ return static_cast<NMDeviceType>(deviceType.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")) {
+ Q_EMIT deviceTypeChanged(extractDeviceType(i->value<QDBusObjectPath>()));
+ }
}
}
}
diff --git a/src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.h b/src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.h
index 57c5aed763..93fa23870f 100644
--- a/src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.h
+++ b/src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.h
@@ -55,11 +55,6 @@
#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"
-
// Matches 'NMDeviceState' from https://developer.gnome.org/NetworkManager/stable/nm-dbus-types.html
enum NMDeviceState {
NM_DEVICE_STATE_UNKNOWN = 0,
@@ -119,23 +114,64 @@ 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,
+ };
QNetworkManagerInterface(QObject *parent = nullptr);
~QNetworkManagerInterface();
NMState state() const;
NMConnectivityState connectivityState() const;
+ NMDeviceType deviceType() const;
Q_SIGNALS:
void stateChanged(NMState);
void connectivityChanged(NMConnectivityState);
+ void deviceTypeChanged(NMDeviceType);
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(QDBusObjectPath devicePath) const;
+
QVariantMap propertyMap;
};
diff --git a/src/plugins/networkinformation/scnetworkreachability/CMakeLists.txt b/src/plugins/networkinformation/scnetworkreachability/CMakeLists.txt
index 6a15bde1c0..c9ee7d9a42 100644
--- a/src/plugins/networkinformation/scnetworkreachability/CMakeLists.txt
+++ b/src/plugins/networkinformation/scnetworkreachability/CMakeLists.txt
@@ -1,7 +1,7 @@
qt_internal_add_plugin(QSCNetworkReachabilityNetworkInformationPlugin
- OUTPUT_NAME scnetworkreachability
+ OUTPUT_NAME qscnetworkreachability
CLASS_NAME QSCNetworkReachabilityNetworkInformationBackendFactory
- TYPE networkinformation
+ PLUGIN_TYPE networkinformation
DEFAULT_IF APPLE
SOURCES
qscnetworkreachabilitynetworkinformationbackend.mm
diff --git a/src/plugins/platforminputcontexts/compose/CMakeLists.txt b/src/plugins/platforminputcontexts/compose/CMakeLists.txt
index fe14de36f4..e3fbf913b9 100644
--- a/src/plugins/platforminputcontexts/compose/CMakeLists.txt
+++ b/src/plugins/platforminputcontexts/compose/CMakeLists.txt
@@ -9,7 +9,7 @@ pkg_get_variable(PKG_X11_PREFIX x11 prefix) # special case
qt_internal_add_plugin(QComposePlatformInputContextPlugin
OUTPUT_NAME composeplatforminputcontextplugin
- TYPE platforminputcontexts
+ PLUGIN_TYPE platforminputcontexts
DEFAULT_IF FALSE
SOURCES
qcomposeplatforminputcontext.cpp qcomposeplatforminputcontext.h
diff --git a/src/plugins/platforminputcontexts/ibus/CMakeLists.txt b/src/plugins/platforminputcontexts/ibus/CMakeLists.txt
index 6d6de4fe8b..7ccc627eb1 100644
--- a/src/plugins/platforminputcontexts/ibus/CMakeLists.txt
+++ b/src/plugins/platforminputcontexts/ibus/CMakeLists.txt
@@ -6,7 +6,7 @@
qt_internal_add_plugin(QIbusPlatformInputContextPlugin
OUTPUT_NAME ibusplatforminputcontextplugin
- TYPE platforminputcontexts
+ PLUGIN_TYPE platforminputcontexts
DEFAULT_IF FALSE
SOURCES
main.cpp
diff --git a/src/plugins/platforms/CMakeLists.txt b/src/plugins/platforms/CMakeLists.txt
index 8081f0a82e..ba64b6b0f5 100644
--- a/src/plugins/platforms/CMakeLists.txt
+++ b/src/plugins/platforms/CMakeLists.txt
@@ -1,6 +1,6 @@
# Generated from platforms.pro.
-if(ANDROID AND NOT ANDROID_EMBEDDED)
+if(ANDROID)
add_subdirectory(android)
endif()
if(NOT ANDROID AND NOT WASM)
diff --git a/src/plugins/platforms/android/CMakeLists.txt b/src/plugins/platforms/android/CMakeLists.txt
index 7f9150d4bf..416e25c28a 100644
--- a/src/plugins/platforms/android/CMakeLists.txt
+++ b/src/plugins/platforms/android/CMakeLists.txt
@@ -7,7 +7,7 @@ qt_find_package(EGL) # special case
qt_internal_add_plugin(QAndroidIntegrationPlugin
OUTPUT_NAME qtforandroid
- TYPE platforms
+ PLUGIN_TYPE platforms
DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES android # special case
SOURCES
androidcontentfileengine.cpp androidcontentfileengine.h
diff --git a/src/plugins/platforms/android/extract-dummy.cpp b/src/plugins/platforms/android/extract-dummy.cpp
index bfc0dafd04..43c03792d4 100644
--- a/src/plugins/platforms/android/extract-dummy.cpp
+++ b/src/plugins/platforms/android/extract-dummy.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
@@ -45,9 +45,3 @@ Java_org_qtproject_qt_android_ExtractStyle_extractNativeChunkInfo20(JNIEnv *, jo
{
return 0;
}
-
-extern "C" JNIEXPORT jintArray JNICALL
-Java_org_qtproject_qt_android_ExtractStyle_extractChunkInfo20(JNIEnv *, jobject, jbyteArray)
-{
- return 0;
-}
diff --git a/src/plugins/platforms/android/extract.cpp b/src/plugins/platforms/android/extract.cpp
index cad54d580b..4566d2ac69 100644
--- a/src/plugins/platforms/android/extract.cpp
+++ b/src/plugins/platforms/android/extract.cpp
@@ -1,5 +1,6 @@
/****************************************************************************
**
+** Copyright (C) 2021 The Qt Company Ltd.
** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
** Contact: https://www.qt.io/licensing/
**
@@ -126,26 +127,6 @@ Java_org_qtproject_qt_android_ExtractStyle_extractNativeChunkInfo20(JNIEnv *env,
return result;
}
-extern "C" JNIEXPORT jintArray JNICALL
-Java_org_qtproject_qt_android_ExtractStyle_extractChunkInfo20(JNIEnv *env, jobject obj,
- jbyteArray chunkObj)
-{
- size_t chunkSize = env->GetArrayLength(chunkObj);
- void* storage = alloca(chunkSize);
- env->GetByteArrayRegion(chunkObj, 0, chunkSize,
- reinterpret_cast<jbyte*>(storage));
-
- if (QJniEnvironment::checkAndClearExceptions(env))
- return 0;
-
- jintArray res = Java_org_qtproject_qt_android_ExtractStyle_extractNativeChunkInfo20(env, obj,
- long(storage));
- if (QJniEnvironment::checkAndClearExceptions(env))
- res = nullptr;
-
- return res;
-}
-
static inline void fill9patchOffsets(Res_png_9patch20* patch) {
patch->xDivsOffset = sizeof(Res_png_9patch20);
patch->yDivsOffset = patch->xDivsOffset + (patch->numXDivs * sizeof(int32_t));
diff --git a/src/plugins/platforms/android/qandroidinputcontext.cpp b/src/plugins/platforms/android/qandroidinputcontext.cpp
index fb51e1dba1..37a5191dfc 100644
--- a/src/plugins/platforms/android/qandroidinputcontext.cpp
+++ b/src/plugins/platforms/android/qandroidinputcontext.cpp
@@ -882,44 +882,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,6 +915,12 @@ void QAndroidInputContext::showInputPanel()
if (query.isNull())
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()));
+
QRect rect = cursorRect();
if (!isInputPanelVisible())
QtAndroidInput::showSoftwareKeyboard(rect.left(), rect.top(), rect.width(), rect.height(),
@@ -1254,13 +1225,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..02a66c367a 100644
--- a/src/plugins/platforms/android/qandroidinputcontext.h
+++ b/src/plugins/platforms/android/qandroidinputcontext.h
@@ -156,13 +156,13 @@ private:
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/qandroidplatformintegration.cpp b/src/plugins/platforms/android/qandroidplatformintegration.cpp
index 7b4102fac6..c9ef977816 100644
--- a/src/plugins/platforms/android/qandroidplatformintegration.cpp
+++ b/src/plugins/platforms/android/qandroidplatformintegration.cpp
@@ -143,6 +143,19 @@ void *QAndroidPlatformNativeInterface::nativeResourceForWindow(const QByteArray
return nullptr;
}
+void *QAndroidPlatformNativeInterface::nativeResourceForContext(const QByteArray &resource, QOpenGLContext *context)
+{
+ if (QEGLPlatformContext *platformContext = static_cast<QEGLPlatformContext *>(context->handle())) {
+ if (resource == "eglcontext")
+ return platformContext->eglContext();
+ else if (resource == "eglconfig")
+ return platformContext->eglConfig();
+ else if (resource == "egldisplay")
+ return platformContext->eglDisplay();
+ }
+ return nullptr;
+}
+
void QAndroidPlatformNativeInterface::customEvent(QEvent *event)
{
if (event->type() != QEvent::User)
diff --git a/src/plugins/platforms/android/qandroidplatformintegration.h b/src/plugins/platforms/android/qandroidplatformintegration.h
index d579bc29ae..52442e87af 100644
--- a/src/plugins/platforms/android/qandroidplatformintegration.h
+++ b/src/plugins/platforms/android/qandroidplatformintegration.h
@@ -65,6 +65,7 @@ class QAndroidPlatformNativeInterface: public QPlatformNativeInterface
public:
void *nativeResourceForIntegration(const QByteArray &resource) override;
void *nativeResourceForWindow(const QByteArray &resource, QWindow *window) override;
+ void *nativeResourceForContext(const QByteArray &resource, QOpenGLContext *context) override;
std::shared_ptr<AndroidStyle> m_androidStyle;
protected:
diff --git a/src/plugins/platforms/android/qandroidplatformservices.cpp b/src/plugins/platforms/android/qandroidplatformservices.cpp
index 36e1aef609..8c2af5fff4 100644
--- a/src/plugins/platforms/android/qandroidplatformservices.cpp
+++ b/src/plugins/platforms/android/qandroidplatformservices.cpp
@@ -41,16 +41,32 @@
#include "qandroidplatformservices.h"
#include <QDebug>
+#include <QDesktopServices>
#include <QFile>
#include <QMimeDatabase>
-#include <QUrl>
#include <QtCore/QJniObject>
#include <QtCore/qcoreapplication.h>
+#include <QtCore/qscopedvaluerollback.h>
QT_BEGIN_NAMESPACE
QAndroidPlatformServices::QAndroidPlatformServices()
{
+ m_actionView = QJniObject::getStaticObjectField("android/content/Intent", "ACTION_VIEW",
+ "Ljava/lang/String;")
+ .toString();
+
+ QtAndroidPrivate::registerNewIntentListener(this);
+
+ QMetaObject::invokeMethod(
+ this,
+ [this] {
+ QJniObject context = QJniObject(QtAndroidPrivate::context());
+ QJniObject intent =
+ context.callObjectMethod("getIntent", "()Landroid/content/Intent;");
+ handleNewIntent(nullptr, intent.object());
+ },
+ Qt::QueuedConnection);
}
bool QAndroidPlatformServices::openUrl(const QUrl &theUrl)
@@ -58,6 +74,10 @@ bool QAndroidPlatformServices::openUrl(const QUrl &theUrl)
QString mime;
QUrl url(theUrl);
+ // avoid recursing back into self
+ if (url == m_handlingUrl)
+ return false;
+
// if the file is local, we need to pass the MIME type, otherwise Android
// does not start an Intent to view this file
QLatin1String fileScheme("file");
@@ -87,4 +107,19 @@ QByteArray QAndroidPlatformServices::desktopEnvironment() const
return QByteArray("Android");
}
+bool QAndroidPlatformServices::handleNewIntent(JNIEnv *env, jobject intent)
+{
+ Q_UNUSED(env);
+
+ const QJniObject jniIntent(intent);
+
+ const QString action = jniIntent.callObjectMethod<jstring>("getAction").toString();
+ if (action != m_actionView)
+ return false;
+
+ const QString url = jniIntent.callObjectMethod<jstring>("getDataString").toString();
+ QScopedValueRollback<QUrl> rollback(m_handlingUrl, url);
+ return QDesktopServices::openUrl(url);
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/android/qandroidplatformservices.h b/src/plugins/platforms/android/qandroidplatformservices.h
index 6f2f0a394f..dff132d56d 100644
--- a/src/plugins/platforms/android/qandroidplatformservices.h
+++ b/src/plugins/platforms/android/qandroidplatformservices.h
@@ -42,16 +42,28 @@
#include <qpa/qplatformservices.h>
#include "androidjnimain.h"
+#include <QtCore/private/qjnihelpers_p.h>
+#include <QtCore/qobject.h>
+#include <QUrl>
QT_BEGIN_NAMESPACE
-class QAndroidPlatformServices: public QPlatformServices
+class QAndroidPlatformServices : public QObject,
+ public QPlatformServices,
+ public QtAndroidPrivate::NewIntentListener
{
public:
QAndroidPlatformServices();
+
bool openUrl(const QUrl &url) override;
bool openDocument(const QUrl &url) override;
QByteArray desktopEnvironment() const override;
+
+ bool handleNewIntent(JNIEnv *env, jobject intent) override;
+
+private:
+ QUrl m_handlingUrl;
+ QString m_actionView;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/android/qandroidsystemlocale.cpp b/src/plugins/platforms/android/qandroidsystemlocale.cpp
index 351274a560..74a549454d 100644
--- a/src/plugins/platforms/android/qandroidsystemlocale.cpp
+++ b/src/plugins/platforms/android/qandroidsystemlocale.cpp
@@ -105,14 +105,26 @@ QVariant QAndroidSystemLocale::query(QueryType type, QVariant in) const
return m_locale.dayName(in.toInt(), QLocale::LongFormat);
case DayNameShort:
return m_locale.dayName(in.toInt(), QLocale::ShortFormat);
+ case DayNameNarrow:
+ return m_locale.dayName(in.toInt(), QLocale::NarrowFormat);
+ case StandaloneDayNameLong:
+ return m_locale.standaloneDayName(in.toInt(), QLocale::LongFormat);
+ case StandaloneDayNameShort:
+ return m_locale.standaloneDayName(in.toInt(), QLocale::ShortFormat);
+ case StandaloneDayNameNarrow:
+ return m_locale.standaloneDayName(in.toInt(), QLocale::NarrowFormat);
case MonthNameLong:
return m_locale.monthName(in.toInt(), QLocale::LongFormat);
case MonthNameShort:
return m_locale.monthName(in.toInt(), QLocale::ShortFormat);
+ case MonthNameNarrow:
+ return m_locale.monthName(in.toInt(), QLocale::NarrowFormat);
case StandaloneMonthNameLong:
return m_locale.standaloneMonthName(in.toInt(), QLocale::LongFormat);
case StandaloneMonthNameShort:
return m_locale.standaloneMonthName(in.toInt(), QLocale::ShortFormat);
+ case StandaloneMonthNameNarrow:
+ return m_locale.standaloneMonthName(in.toInt(), QLocale::NarrowFormat);
case DateToStringLong:
return m_locale.toString(in.toDate(), QLocale::LongFormat);
case DateToStringShort:
diff --git a/src/plugins/platforms/cocoa/CMakeLists.txt b/src/plugins/platforms/cocoa/CMakeLists.txt
index 62eaad8e02..21f87dae83 100644
--- a/src/plugins/platforms/cocoa/CMakeLists.txt
+++ b/src/plugins/platforms/cocoa/CMakeLists.txt
@@ -9,7 +9,7 @@
qt_internal_add_plugin(QCocoaIntegrationPlugin
OUTPUT_NAME qcocoa
DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES cocoa # special case
- TYPE platforms
+ PLUGIN_TYPE platforms
SOURCES
main.mm
qcocoaapplication.h qcocoaapplication.mm
diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
index 40d8f639fc..8b346fa89f 100644
--- a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
+++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
@@ -269,6 +269,16 @@ QT_USE_NAMESPACE
[reflectionDelegate applicationDidBecomeActive:notification];
QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationActive);
+
+ if (QCocoaWindow::s_windowUnderMouse) {
+ QPointF windowPoint;
+ QPointF screenPoint;
+ QNSView *view = qnsview_cast(QCocoaWindow::s_windowUnderMouse->m_view);
+ [view convertFromScreen:[NSEvent mouseLocation] toWindowPoint:&windowPoint andScreenPoint:&screenPoint];
+ QWindow *windowUnderMouse = QCocoaWindow::s_windowUnderMouse->window();
+ qCInfo(lcQpaMouse) << "Application activated with mouse at" << windowPoint << "; sending" << QEvent::Enter << "to" << windowUnderMouse;
+ QWindowSystemInterface::handleEnterEvent(windowUnderMouse, windowPoint, screenPoint);
+ }
}
- (void)applicationDidResignActive:(NSNotification *)notification
@@ -277,6 +287,12 @@ QT_USE_NAMESPACE
[reflectionDelegate applicationDidResignActive:notification];
QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationInactive);
+
+ if (QCocoaWindow::s_windowUnderMouse) {
+ QWindow *windowUnderMouse = QCocoaWindow::s_windowUnderMouse->window();
+ qCInfo(lcQpaMouse) << "Application deactivated; sending" << QEvent::Leave << "to" << windowUnderMouse;
+ QWindowSystemInterface::handleLeaveEvent(windowUnderMouse);
+ }
}
- (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag
@@ -343,7 +359,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..374dc97f7d 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);
diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm
index 01787da1af..994ab3036c 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)
{
}
diff --git a/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm b/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm
index 8d256ae09e..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);
}
@@ -325,9 +330,9 @@ public:
bool show(Qt::WindowModality windowModality, QWindow *parent)
{
Q_UNUSED(parent);
- if (windowModality != Qt::WindowModal)
+ if (windowModality != Qt::ApplicationModal)
[mDelegate showModelessPanel];
- // no need to show a Qt::WindowModal dialog here, because it's necessary to call exec() in that case
+ // no need to show a Qt::ApplicationModal dialog here, because it will be shown in runApplicationModalPanel
return true;
}
@@ -391,9 +396,8 @@ void QCocoaColorDialogHelper::exec()
bool QCocoaColorDialogHelper::show(Qt::WindowFlags, Qt::WindowModality windowModality, QWindow *parent)
{
- if (windowModality == Qt::WindowModal)
- windowModality = Qt::ApplicationModal;
-
+ if (windowModality == Qt::ApplicationModal)
+ windowModality = Qt::WindowModal;
// Workaround for Apple rdar://25792119: If you invoke
// -setShowsAlpha: multiple times before showing the color
// picker, its height grows irrevocably. Instead, only
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 e1c9d0a194..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);
}
@@ -314,9 +319,9 @@ public:
bool show(Qt::WindowModality windowModality, QWindow *parent)
{
Q_UNUSED(parent);
- if (windowModality != Qt::WindowModal)
+ if (windowModality != Qt::ApplicationModal)
[mDelegate showModelessPanel];
- // no need to show a Qt::WindowModal dialog here, because it's necessary to call exec() in that case
+ // no need to show a Qt::ApplicationModal dialog here, because it will be shown in runApplicationModalPanel
return true;
}
@@ -380,8 +385,8 @@ void QCocoaFontDialogHelper::exec()
bool QCocoaFontDialogHelper::show(Qt::WindowFlags, Qt::WindowModality windowModality, QWindow *parent)
{
- if (windowModality == Qt::WindowModal)
- windowModality = Qt::ApplicationModal;
+ if (windowModality == Qt::ApplicationModal)
+ windowModality = Qt::WindowModal;
sharedFontPanel()->init(this);
return sharedFontPanel()->show(windowModality, parent);
}
diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.h b/src/plugins/platforms/cocoa/qcocoahelpers.h
index 25069bb56d..0fa868c73f 100644
--- a/src/plugins/platforms/cocoa/qcocoahelpers.h
+++ b/src/plugins/platforms/cocoa/qcocoahelpers.h
@@ -71,6 +71,8 @@ QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcQpaWindow)
Q_DECLARE_LOGGING_CATEGORY(lcQpaDrawing)
Q_DECLARE_LOGGING_CATEGORY(lcQpaMouse)
+Q_DECLARE_LOGGING_CATEGORY(lcQpaKeys)
+Q_DECLARE_LOGGING_CATEGORY(lcQpaInputMethods)
Q_DECLARE_LOGGING_CATEGORY(lcQpaScreen)
Q_DECLARE_LOGGING_CATEGORY(lcQpaApplication)
Q_DECLARE_LOGGING_CATEGORY(lcQpaClipboard)
@@ -358,6 +360,45 @@ QSendSuperHelper<Args...> qt_objcDynamicSuperHelper(id receiver, SEL selector, A
// Same as calling super, but the super_class field resolved at runtime instead of compile time
#define qt_objcDynamicSuper(...) qt_objcDynamicSuperHelper(self, _cmd, ##__VA_ARGS__)
+// -------------------------------------------------------------------------
+
+struct InputMethodQueryResult : public QHash<Qt::InputMethodQuery, QVariant>
+{
+ operator bool() { return !isEmpty(); }
+};
+
+InputMethodQueryResult queryInputMethod(QObject *object, Qt::InputMethodQueries queries = Qt::ImEnabled);
+
+// -------------------------------------------------------------------------
+
+struct KeyEvent
+{
+ ulong timestamp = 0;
+ QEvent::Type type = QEvent::None;
+
+ Qt::Key key = Qt::Key_unknown;
+ Qt::KeyboardModifiers modifiers = Qt::NoModifier;
+ QString text;
+ bool isRepeat = false;
+
+ // Scan codes are hardware dependent codes for each key. There is no way to get these
+ // from Carbon or Cocoa, so leave it 0, as documented in QKeyEvent::nativeScanCode().
+ static const quint32 nativeScanCode = 0;
+
+ quint32 nativeVirtualKey = 0;
+ NSEventModifierFlags nativeModifiers = 0;
+
+ KeyEvent(NSEvent *nsevent);
+ bool sendWindowSystemEvent(QWindow *window) const;
+};
+
+QDebug operator<<(QDebug debug, const KeyEvent &e);
+
+// -------------------------------------------------------------------------
+
+QDebug operator<<(QDebug, const NSRange &);
+QDebug operator<<(QDebug, SEL);
+
#endif // __OBJC__
#endif //QCOCOAHELPERS_H
diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm
index 299c655d14..2578fb2140 100644
--- a/src/plugins/platforms/cocoa/qcocoahelpers.mm
+++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm
@@ -58,6 +58,8 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcQpaWindow, "qt.qpa.window");
Q_LOGGING_CATEGORY(lcQpaDrawing, "qt.qpa.drawing");
Q_LOGGING_CATEGORY(lcQpaMouse, "qt.qpa.input.mouse", QtCriticalMsg);
+Q_LOGGING_CATEGORY(lcQpaKeys, "qt.qpa.input.keys", QtCriticalMsg);
+Q_LOGGING_CATEGORY(lcQpaInputMethods, "qt.qpa.input.methods")
Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen", QtCriticalMsg);
Q_LOGGING_CATEGORY(lcQpaApplication, "qt.qpa.application");
Q_LOGGING_CATEGORY(lcQpaClipboard, "qt.qpa.clipboard")
@@ -155,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.
@@ -493,6 +495,8 @@ QT_END_NAMESPACE
[super layout];
}
+@end // QNSPanelContentsWrapper
+
// -------------------------------------------------------------------------
io_object_t q_IOObjectRetain(io_object_t obj)
@@ -508,4 +512,43 @@ void q_IOObjectRelease(io_object_t obj)
Q_ASSERT(!ret);
}
-@end
+// -------------------------------------------------------------------------
+
+InputMethodQueryResult queryInputMethod(QObject *object, Qt::InputMethodQueries queries)
+{
+ if (object) {
+ QInputMethodQueryEvent queryEvent(queries | Qt::ImEnabled);
+ if (QCoreApplication::sendEvent(object, &queryEvent)) {
+ if (queryEvent.value(Qt::ImEnabled).toBool()) {
+ InputMethodQueryResult result;
+ static QMetaEnum queryEnum = QMetaEnum::fromType<Qt::InputMethodQuery>();
+ for (int i = 0; i < queryEnum.keyCount(); ++i) {
+ auto query = Qt::InputMethodQuery(queryEnum.value(i));
+ if (queries & query)
+ result.insert(query, queryEvent.value(query));
+ }
+ return result;
+ }
+ }
+ }
+ return {};
+}
+
+// -------------------------------------------------------------------------
+
+QDebug operator<<(QDebug debug, const NSRange &range)
+{
+ if (range.location == NSNotFound) {
+ QDebugStateSaver saver(debug);
+ debug.nospace() << "{NSNotFound, " << range.length << "}";
+ } else {
+ debug << NSStringFromRange(range);
+ }
+ return debug;
+}
+
+QDebug operator<<(QDebug debug, SEL selector)
+{
+ debug << NSStringFromSelector(selector);
+ return debug;
+}
diff --git a/src/plugins/platforms/cocoa/qcocoainputcontext.h b/src/plugins/platforms/cocoa/qcocoainputcontext.h
index 7190ba0de8..3a8322461c 100644
--- a/src/plugins/platforms/cocoa/qcocoainputcontext.h
+++ b/src/plugins/platforms/cocoa/qcocoainputcontext.h
@@ -44,6 +44,8 @@
#include <QtCore/QLocale>
#include <QtCore/QPointer>
+#include <QtCore/private/qcore_mac_p.h>
+
QT_BEGIN_NAMESPACE
class QCocoaInputContext : public QPlatformInputContext
@@ -55,18 +57,18 @@ public:
bool isValid() const override { return true; }
+ void setFocusObject(QObject *object) override;
+
+ void commit() override;
void reset() override;
QLocale locale() const override { return m_locale; }
void updateLocale();
-private Q_SLOTS:
- void connectSignals();
- void focusObjectChanged(QObject *focusObject);
-
private:
- QPointer<QWindow> mWindow;
+ QPointer<QWindow> m_focusWindow;
QLocale m_locale;
+ QMacNotificationObserver m_inputSourceObserver;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qcocoainputcontext.mm b/src/plugins/platforms/cocoa/qcocoainputcontext.mm
index 49c622e83b..63d23b0f43 100644
--- a/src/plugins/platforms/cocoa/qcocoainputcontext.mm
+++ b/src/plugins/platforms/cocoa/qcocoainputcontext.mm
@@ -57,8 +57,8 @@ QT_BEGIN_NAMESPACE
\class QCocoaInputContext
\brief Cocoa Input context implementation
- Handles input of foreign characters (particularly East Asian)
- languages.
+ Handles input of languages that support character composition,
+ for example East Asian languages.
\section1 Testing
@@ -74,17 +74,20 @@ QT_BEGIN_NAMESPACE
\section1 Interaction
- Input method support in Cocoa uses NSTextInput protorol. Therefore
- almost all functionality is implemented in QNSView.
+ Input method support in Cocoa is based on the NSTextInputClient protocol,
+ therefore almost all functionality is in QNSView (qnsview_complextext.mm).
*/
-
-
QCocoaInputContext::QCocoaInputContext()
: QPlatformInputContext()
- , mWindow(QGuiApplication::focusWindow())
+ , m_focusWindow(QGuiApplication::focusWindow())
{
- QMetaObject::invokeMethod(this, "connectSignals", Qt::QueuedConnection);
+ m_inputSourceObserver = QMacNotificationObserver(nil,
+ NSTextInputContextKeyboardSelectionDidChangeNotification, [&]() {
+ qCDebug(lcQpaInputMethods) << "Text input source changed";
+ updateLocale();
+ });
+
updateLocale();
}
@@ -93,17 +96,45 @@ QCocoaInputContext::~QCocoaInputContext()
}
/*!
- \brief Cancels a composition.
+ 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()
{
+ qCDebug(lcQpaInputMethods) << "Resetting input method";
+
QPlatformInputContext::reset();
- if (!mWindow)
+ if (!m_focusWindow)
return;
- QCocoaWindow *window = static_cast<QCocoaWindow *>(mWindow->handle());
+ QCocoaWindow *window = static_cast<QCocoaWindow *>(m_focusWindow->handle());
QNSView *view = qnsview_cast(window->view());
if (!view)
return;
@@ -115,20 +146,15 @@ void QCocoaInputContext::reset()
}
}
-void QCocoaInputContext::connectSignals()
+void QCocoaInputContext::setFocusObject(QObject *focusObject)
{
- connect(qApp, SIGNAL(focusObjectChanged(QObject*)), this, SLOT(focusObjectChanged(QObject*)));
- focusObjectChanged(qApp->focusObject());
-}
+ qCDebug(lcQpaInputMethods) << "Focus object changed to" << focusObject;
-void QCocoaInputContext::focusObjectChanged(QObject *focusObject)
-{
- Q_UNUSED(focusObject);
- if (mWindow == QGuiApplication::focusWindow()) {
- if (!mWindow)
+ if (m_focusWindow == QGuiApplication::focusWindow()) {
+ if (!m_focusWindow)
return;
- QCocoaWindow *window = static_cast<QCocoaWindow *>(mWindow->handle());
+ QCocoaWindow *window = static_cast<QCocoaWindow *>(m_focusWindow->handle());
if (!window)
return;
QNSView *view = qnsview_cast(window->view());
@@ -140,24 +166,27 @@ void QCocoaInputContext::focusObjectChanged(QObject *focusObject)
[view cancelComposingText];
}
} else {
- mWindow = QGuiApplication::focusWindow();
+ m_focusWindow = QGuiApplication::focusWindow();
}
}
void QCocoaInputContext::updateLocale()
{
- TISInputSourceRef source = TISCopyCurrentKeyboardInputSource();
- CFArrayRef languages = (CFArrayRef) TISGetInputSourceProperty(source, kTISPropertyInputSourceLanguages);
- if (CFArrayGetCount(languages) > 0) {
- CFStringRef langRef = (CFStringRef)CFArrayGetValueAtIndex(languages, 0);
- QString name = QString::fromCFString(langRef);
- QLocale locale(name);
- if (m_locale != locale) {
- m_locale = locale;
- emitLocaleChanged();
- }
+ QCFType<TISInputSourceRef> source = TISCopyCurrentKeyboardInputSource();
+ NSArray *languages = static_cast<NSArray*>(TISGetInputSourceProperty(source,
+ kTISPropertyInputSourceLanguages));
+
+ qCDebug(lcQpaInputMethods) << "Input source supports" << languages;
+ if (!languages.count)
+ return;
+
+ QString language = QString::fromNSString(languages.firstObject);
+ QLocale locale(language);
+ if (m_locale != locale) {
+ qCDebug(lcQpaInputMethods) << "Reporting new locale" << locale;
+ m_locale = locale;
+ emitLocaleChanged();
}
- CFRelease(source);
}
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.h b/src/plugins/platforms/cocoa/qcocoaintegration.h
index caf47e38d3..fc9580b1b8 100644
--- a/src/plugins/platforms/cocoa/qcocoaintegration.h
+++ b/src/plugins/platforms/cocoa/qcocoaintegration.h
@@ -124,18 +124,11 @@ public:
NSToolbar *toolbar(QWindow *window) const;
void clearToolbars();
- void pushPopupWindow(QCocoaWindow *window);
- QCocoaWindow *popPopupWindow();
- QCocoaWindow *activePopupWindow() const;
- QList<QCocoaWindow *> *popupWindowStack();
-
void setApplicationIcon(const QIcon &icon) const override;
void beep() const override;
void quit() const override;
- void closePopups(QWindow *forWindow = nullptr);
-
private Q_SLOTS:
void focusWindowChanged(QWindow *);
@@ -162,7 +155,6 @@ private:
mutable QCocoaVulkanInstance *mCocoaVulkanInstance = nullptr;
#endif
QHash<QWindow *, NSToolbar *> mToolbars;
- QList<QCocoaWindow *> m_popupWindowStack;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QCocoaIntegration::Options)
diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm
index 1ab30df7e8..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.)
@@ -412,6 +412,8 @@ QVariant QCocoaIntegration::styleHint(StyleHint hint) const
return QCoreTextFontEngine::fontSmoothingGamma();
case ShowShortcutsInContextMenus:
return QVariant(false);
+ case ReplayMousePressOutsidePopup:
+ return QVariant(false);
default: break;
}
@@ -452,30 +454,6 @@ void QCocoaIntegration::clearToolbars()
mToolbars.clear();
}
-void QCocoaIntegration::pushPopupWindow(QCocoaWindow *window)
-{
- m_popupWindowStack.append(window);
-}
-
-QCocoaWindow *QCocoaIntegration::popPopupWindow()
-{
- if (m_popupWindowStack.isEmpty())
- return nullptr;
- return m_popupWindowStack.takeLast();
-}
-
-QCocoaWindow *QCocoaIntegration::activePopupWindow() const
-{
- if (m_popupWindowStack.isEmpty())
- return nullptr;
- return m_popupWindowStack.front();
-}
-
-QList<QCocoaWindow *> *QCocoaIntegration::popupWindowStack()
-{
- return &m_popupWindowStack;
-}
-
void QCocoaIntegration::setApplicationIcon(const QIcon &icon) const
{
// Fall back to a size that looks good on the highest resolution screen available
@@ -494,19 +472,6 @@ void QCocoaIntegration::quit() const
[NSApp terminate:nil];
}
-void QCocoaIntegration::closePopups(QWindow *forWindow)
-{
- for (auto it = m_popupWindowStack.begin(); it != m_popupWindowStack.end();) {
- auto *popup = *it;
- if (!forWindow || popup->window()->transientParent() == forWindow) {
- it = m_popupWindowStack.erase(it);
- QWindowSystemInterface::handleCloseEvent<QWindowSystemInterface::SynchronousDelivery>(popup->window());
- } else {
- ++it;
- }
- }
-}
-
void QCocoaIntegration::focusWindowChanged(QWindow *focusWindow)
{
// Don't revert icon just because we lost focus
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..0054cdf6ad 100644
--- a/src/plugins/platforms/cocoa/qcocoamenubar.h
+++ b/src/plugins/platforms/cocoa/qcocoamenubar.h
@@ -80,12 +80,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..524746f952 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)
@@ -400,6 +405,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/qcocoamenuloader.mm b/src/plugins/platforms/cocoa/qcocoamenuloader.mm
index ba37b40f5e..50072b4e51 100644
--- a/src/plugins/platforms/cocoa/qcocoamenuloader.mm
+++ b/src/plugins/platforms/cocoa/qcocoamenuloader.mm
@@ -320,13 +320,8 @@
- (BOOL)validateMenuItem:(NSMenuItem*)menuItem
{
if (menuItem.action == @selector(hideOtherApplications:)
- || menuItem.action == @selector(unhideAllApplications:))
- return [NSApp validateMenuItem:menuItem];
-
- if (menuItem.action == @selector(hide:)) {
- auto *w = QCocoaIntegration::instance()->activePopupWindow();
- if (w && (w->window()->type() != Qt::ToolTip))
- return NO;
+ || menuItem.action == @selector(unhideAllApplications:)
+ || menuItem.action == @selector(hide:)) {
return [NSApp validateMenuItem:menuItem];
}
diff --git a/src/plugins/platforms/cocoa/qcocoascreen.h b/src/plugins/platforms/cocoa/qcocoascreen.h
index 0eb6d114e2..91c1e3c63c 100644
--- a/src/plugins/platforms/cocoa/qcocoascreen.h
+++ b/src/plugins/platforms/cocoa/qcocoascreen.h
@@ -106,9 +106,6 @@ private:
static QMacNotificationObserver s_screenParameterObserver;
static CGDisplayReconfigurationCallBack s_displayReconfigurationCallBack;
- static bool updateScreensIfNeeded();
- static NSArray *s_screenConfigurationBeforeUpdate;
-
static void add(CGDirectDisplayID displayId);
QCocoaScreen(CGDirectDisplayID displayId);
void update(CGDirectDisplayID displayId);
diff --git a/src/plugins/platforms/cocoa/qcocoascreen.mm b/src/plugins/platforms/cocoa/qcocoascreen.mm
index d326d149f1..82fd1c44df 100644
--- a/src/plugins/platforms/cocoa/qcocoascreen.mm
+++ b/src/plugins/platforms/cocoa/qcocoascreen.mm
@@ -74,7 +74,6 @@ namespace CoreGraphics {
Q_ENUM_NS(DisplayChange)
}
-NSArray *QCocoaScreen::s_screenConfigurationBeforeUpdate = nil;
QMacNotificationObserver QCocoaScreen::s_screenParameterObserver;
CGDisplayReconfigurationCallBack QCocoaScreen::s_displayReconfigurationCallBack = nullptr;
@@ -85,83 +84,23 @@ void QCocoaScreen::initializeScreens()
s_displayReconfigurationCallBack = [](CGDirectDisplayID displayId, CGDisplayChangeSummaryFlags flags, void *userInfo) {
Q_UNUSED(userInfo);
- // Displays are reconfigured in batches, and we want to update our screens
- // once a batch ends, so that all the states of the displays are up to date.
- static int displayReconfigurationsInProgress = 0;
-
const bool beforeReconfigure = flags & kCGDisplayBeginConfigurationFlag;
- qCDebug(lcQpaScreen).verbosity(0).nospace() << "Display " << displayId
- << (beforeReconfigure ? " about to reconfigure" : " was ")
- << QFlags<CoreGraphics::DisplayChange>(flags)
- << " with " << displayReconfigurationsInProgress
- << " display configuration(s) in progress";
-
- if (!flags) {
- // CGDisplayRegisterReconfigurationCallback has been observed to be called
- // with flags unset. This seems like a bug. The callback is not paired with
- // a matching "completion" callback either, so we don't know whether to treat
- // it as a begin or end of reconfigure.
- return;
- }
+ qCDebug(lcQpaScreen).verbosity(0) << "Display" << displayId
+ << (beforeReconfigure ? "beginning" : "finished") << "reconfigure"
+ << QFlags<CoreGraphics::DisplayChange>(flags);
- if (beforeReconfigure) {
- if (!displayReconfigurationsInProgress++) {
- // There might have been a screen reconfigure before this that
- // we didn't process yet, so do that now if that's the case.
- updateScreensIfNeeded();
-
- Q_ASSERT(!s_screenConfigurationBeforeUpdate);
- s_screenConfigurationBeforeUpdate = NSScreen.screens;
- qCDebug(lcQpaScreen, "Display reconfigure transaction started"
- " with screen configuration %p", s_screenConfigurationBeforeUpdate);
-
- static void (^tryScreenUpdate)();
- tryScreenUpdate = ^void () {
- qCDebug(lcQpaScreen) << "Attempting screen update from runloop block";
- if (!updateScreensIfNeeded())
- CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, tryScreenUpdate);
- };
- CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, tryScreenUpdate);
- }
- } else {
- Q_ASSERT_X(displayReconfigurationsInProgress, "QCococaScreen",
- "Display configuration transactions are expected to be balanced");
-
- if (!--displayReconfigurationsInProgress) {
- qCDebug(lcQpaScreen) << "Display reconfigure transaction completed";
- // We optimistically update now, in case the NSScreens have changed
- updateScreensIfNeeded();
- }
- }
+ if (!beforeReconfigure)
+ updateScreens();
};
CGDisplayRegisterReconfigurationCallback(s_displayReconfigurationCallBack, nullptr);
s_screenParameterObserver = QMacNotificationObserver(NSApplication.sharedApplication,
NSApplicationDidChangeScreenParametersNotification, [&]() {
qCDebug(lcQpaScreen) << "Received screen parameter change notification";
- updateScreensIfNeeded(); // As a last resort we update screens here
+ updateScreens();
});
}
-bool QCocoaScreen::updateScreensIfNeeded()
-{
- if (!s_screenConfigurationBeforeUpdate) {
- qCDebug(lcQpaScreen) << "QScreens have already been updated, all good";
- return true;
- }
-
- if (s_screenConfigurationBeforeUpdate == NSScreen.screens) {
- qCDebug(lcQpaScreen) << "Still waiting for NSScreen configuration change";
- return false;
- }
-
- qCDebug(lcQpaScreen, "NSScreen configuration changed to %p", NSScreen.screens);
- updateScreens();
-
- s_screenConfigurationBeforeUpdate = nil;
- return true;
-}
-
/*
Update the list of available QScreens, and the properties of existing screens.
@@ -321,15 +260,18 @@ void QCocoaScreen::update(CGDirectDisplayID displayId)
Q_ASSERT(isOnline());
+ // Some properties are only available via NSScreen
+ NSScreen *nsScreen = nativeScreen();
+ if (!nsScreen) {
+ qCDebug(lcQpaScreen) << "Corresponding NSScreen not yet available. Deferring update";
+ return;
+ }
+
const QRect previousGeometry = m_geometry;
const QRect previousAvailableGeometry = m_availableGeometry;
const QDpi previousLogicalDpi = m_logicalDpi;
const qreal previousRefreshRate = m_refreshRate;
- // Some properties are only available via NSScreen
- NSScreen *nsScreen = nativeScreen();
- Q_ASSERT(nsScreen);
-
// The reference screen for the geometry is always the primary screen
QRectF primaryScreenGeometry = QRectF::fromCGRect(CGDisplayBounds(CGMainDisplayID()));
m_geometry = qt_mac_flip(QRectF::fromCGRect(nsScreen.frame), primaryScreenGeometry).toRect();
@@ -610,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
{
@@ -759,13 +701,17 @@ QList<QPlatformScreen*> QCocoaScreen::virtualSiblings() const
QCocoaScreen *QCocoaScreen::get(NSScreen *nsScreen)
{
- if (s_screenConfigurationBeforeUpdate) {
- qCWarning(lcQpaScreen) << "Trying to resolve screen while waiting for screen reconfigure!";
- if (!updateScreensIfNeeded())
- qCWarning(lcQpaScreen) << "Failed to do last minute screen update. Expect crashes.";
+ auto displayId = nsScreen.qt_displayId;
+ auto *cocoaScreen = get(displayId);
+ if (!cocoaScreen) {
+ qCWarning(lcQpaScreen) << "Failed to map" << nsScreen
+ << "to QCocoaScreen. Doing last minute update.";
+ updateScreens();
+ cocoaScreen = get(displayId);
+ if (!cocoaScreen)
+ qCWarning(lcQpaScreen) << "Last minute update failed!";
}
-
- return get(nsScreen.qt_displayId);
+ return cocoaScreen;
}
QCocoaScreen *QCocoaScreen::get(CGDirectDisplayID displayId)
diff --git a/src/plugins/platforms/cocoa/qcocoatheme.h b/src/plugins/platforms/cocoa/qcocoatheme.h
index f6276e3d1a..436ed94351 100644
--- a/src/plugins/platforms/cocoa/qcocoatheme.h
+++ b/src/plugins/platforms/cocoa/qcocoatheme.h
@@ -73,6 +73,7 @@ public:
QIcon fileIcon(const QFileInfo &fileInfo, QPlatformTheme::IconOptions options = {}) const override;
QVariant themeHint(ThemeHint hint) const override;
+ Appearance appearance() const override;
QString standardButtonText(int button) const override;
QKeySequence standardButtonShortcut(int button) const override;
diff --git a/src/plugins/platforms/cocoa/qcocoatheme.mm b/src/plugins/platforms/cocoa/qcocoatheme.mm
index 21f127103f..6b2dfafd92 100644
--- a/src/plugins/platforms/cocoa/qcocoatheme.mm
+++ b/src/plugins/platforms/cocoa/qcocoatheme.mm
@@ -99,7 +99,6 @@ static QPalette *qt_mac_createSystemPalette()
palette->setBrush(QPalette::Disabled, QPalette::WindowText, dark);
palette->setBrush(QPalette::Disabled, QPalette::Text, dark);
- palette->setBrush(QPalette::Disabled, QPalette::ButtonText, dark);
palette->setBrush(QPalette::Disabled, QPalette::Base, backgroundBrush);
QBrush textBackgroundBrush = qt_mac_toQBrush([NSColor textBackgroundColor]);
palette->setBrush(QPalette::Active, QPalette::Base, textBackgroundBrush);
@@ -124,6 +123,7 @@ static QPalette *qt_mac_createSystemPalette()
qc = qt_mac_toQColor([NSColor controlTextColor]);
palette->setColor(QPalette::Active, QPalette::Text, qc);
+ palette->setColor(QPalette::Active, QPalette::ButtonText, qc);
palette->setColor(QPalette::Active, QPalette::WindowText, qc);
palette->setColor(QPalette::Active, QPalette::HighlightedText, qc);
palette->setColor(QPalette::Inactive, QPalette::Text, qc);
@@ -132,6 +132,7 @@ static QPalette *qt_mac_createSystemPalette()
qc = qt_mac_toQColor([NSColor disabledControlTextColor]);
palette->setColor(QPalette::Disabled, QPalette::Text, qc);
+ palette->setColor(QPalette::Disabled, QPalette::ButtonText, qc);
palette->setColor(QPalette::Disabled, QPalette::WindowText, qc);
palette->setColor(QPalette::Disabled, QPalette::HighlightedText, qc);
@@ -208,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];
}
@@ -532,6 +533,11 @@ QVariant QCocoaTheme::themeHint(ThemeHint hint) const
return QPlatformTheme::themeHint(hint);
}
+QPlatformTheme::Appearance QCocoaTheme::appearance() const
+{
+ return qt_mac_applicationIsInDarkMode() ? Appearance::Dark : Appearance::Light;
+}
+
QString QCocoaTheme::standardButtonText(int button) const
{
return button == QPlatformDialogHelper::Discard ?
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.h b/src/plugins/platforms/cocoa/qcocoawindow.h
index 9a2fa5f025..e3152f153c 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;
@@ -153,13 +156,11 @@ public:
Q_NOTIFICATION_HANDLER(NSViewFrameDidChangeNotification) void viewDidChangeFrame();
Q_NOTIFICATION_HANDLER(NSViewGlobalFrameDidChangeNotification) void viewDidChangeGlobalFrame();
- Q_NOTIFICATION_HANDLER(NSWindowWillMoveNotification) void windowWillMove();
Q_NOTIFICATION_HANDLER(NSWindowDidMoveNotification) void windowDidMove();
Q_NOTIFICATION_HANDLER(NSWindowDidResizeNotification) void windowDidResize();
Q_NOTIFICATION_HANDLER(NSWindowDidEndLiveResizeNotification) void windowDidEndLiveResize();
Q_NOTIFICATION_HANDLER(NSWindowDidBecomeKeyNotification) void windowDidBecomeKey();
Q_NOTIFICATION_HANDLER(NSWindowDidResignKeyNotification) void windowDidResignKey();
- Q_NOTIFICATION_HANDLER(NSWindowWillMiniaturizeNotification) void windowWillMiniaturize();
Q_NOTIFICATION_HANDLER(NSWindowDidMiniaturizeNotification) void windowDidMiniaturize();
Q_NOTIFICATION_HANDLER(NSWindowDidDeminiaturizeNotification) void windowDidDeminiaturize();
Q_NOTIFICATION_HANDLER(NSWindowWillEnterFullScreenNotification) void windowWillEnterFullScreen();
@@ -170,14 +171,16 @@ public:
Q_NOTIFICATION_HANDLER(NSWindowDidOrderOffScreenNotification) void windowDidOrderOffScreen();
Q_NOTIFICATION_HANDLER(NSWindowDidChangeOcclusionStateNotification) void windowDidChangeOcclusionState();
Q_NOTIFICATION_HANDLER(NSWindowDidChangeScreenNotification) void windowDidChangeScreen();
- Q_NOTIFICATION_HANDLER(NSWindowWillCloseNotification) void windowWillClose();
+
+ void windowWillZoom();
bool windowShouldClose();
bool windowIsPopupType(Qt::WindowType type = Qt::Widget) const;
NSInteger windowLevel(Qt::WindowFlags flags);
NSUInteger windowStyleMask(Qt::WindowFlags flags);
- void setWindowZoomButton(Qt::WindowFlags flags);
+ void updateTitleBarButtons(Qt::WindowFlags flags);
+ bool isFixedSize() const;
bool setWindowModified(bool modified) override;
@@ -205,6 +208,8 @@ public:
QPoint bottomLeftClippedByNSWindowOffset() const override;
+ void updateNormalGeometry();
+
enum RecreationReason {
RecreationNotNeeded = 0,
ParentChanged = 0x1,
@@ -236,7 +241,6 @@ public: // for QNSView
bool isContentView() const;
bool alwaysShowToolWindow() const;
- void removeMonitor();
enum HandleFlags {
NoHandleFlags = 0,
@@ -252,8 +256,8 @@ public: // for QNSView
Qt::WindowStates m_lastReportedWindowState;
Qt::WindowModality m_windowModality;
- QPointer<QWindow> m_enterLeaveTargetWindow;
- bool m_windowUnderMouse;
+
+ static QPointer<QCocoaWindow> s_windowUnderMouse;
bool m_initialized;
bool m_inSetVisible;
@@ -263,12 +267,12 @@ public: // for QNSView
bool m_frameStrutEventsEnabled;
QRect m_exposedRect;
+ QRect m_normalGeometry;
int m_registerTouchCount;
bool m_resizableTransientParent;
static const int NoAlertRequest;
NSInteger m_alertRequest;
- NSObject *m_monitor;
bool m_drawContentBorderGradient;
int m_topContentBorderThickness;
@@ -283,8 +287,8 @@ 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;
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm
index ee1689c2f4..1bc53bde5c 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;
}
@@ -132,6 +132,7 @@ static void qRegisterNotificationCallbacks()
Q_CONSTRUCTOR_FUNCTION(qRegisterNotificationCallbacks)
const int QCocoaWindow::NoAlertRequest = -1;
+QPointer<QCocoaWindow> QCocoaWindow::s_windowUnderMouse;
QCocoaWindow::QCocoaWindow(QWindow *win, WId nativeHandle)
: QPlatformWindow(win)
@@ -139,7 +140,6 @@ QCocoaWindow::QCocoaWindow(QWindow *win, WId nativeHandle)
, m_nsWindow(nil)
, m_lastReportedWindowState(Qt::WindowNoState)
, m_windowModality(Qt::NonModal)
- , m_windowUnderMouse(false)
, m_initialized(false)
, m_inSetVisible(false)
, m_inSetGeometry(false)
@@ -149,7 +149,6 @@ QCocoaWindow::QCocoaWindow(QWindow *win, WId nativeHandle)
, m_registerTouchCount(0)
, m_resizableTransientParent(false)
, m_alertRequest(NoAlertRequest)
- , m_monitor(nil)
, m_drawContentBorderGradient(false)
, m_topContentBorderThickness(0)
, m_bottomContentBorderThickness(0)
@@ -197,24 +196,18 @@ QCocoaWindow::~QCocoaWindow()
if ([m_view superview])
[m_view removeFromSuperview];
- removeMonitor();
-
// Make sure to disconnect observer in all case if view is valid
// to avoid notifications received when deleting when using Qt::AA_NativeWindows attribute
if (!isForeignWindow())
[[NSNotificationCenter defaultCenter] removeObserver:m_view];
- if (QCocoaIntegration *cocoaIntegration = QCocoaIntegration::instance()) {
- // While it is unlikely that this window will be in the popup stack
- // during deletetion we clear any pointers here to make sure.
- cocoaIntegration->popupWindowStack()->removeAll(this);
-
#if QT_CONFIG(vulkan)
+ if (QCocoaIntegration *cocoaIntegration = QCocoaIntegration::instance()) {
auto vulcanInstance = cocoaIntegration->getCocoaVulkanInstance();
if (vulcanInstance)
vulcanInstance->destroySurface(m_vulkanSurface);
-#endif
}
+#endif
[m_view release];
[m_nsWindow close];
@@ -253,7 +246,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()) {
@@ -268,6 +261,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;
@@ -336,11 +363,6 @@ void QCocoaWindow::setVisible(bool visible)
// so we can send the geometry change. FIXME: Get rid of this workaround.
handleGeometryChange();
- // Register popup windows. The Cocoa platform plugin will forward mouse events
- // to them and close them when needed.
- if (window()->type() == Qt::Popup || window()->type() == Qt::ToolTip)
- QCocoaIntegration::instance()->pushPopupWindow(this);
-
if (parentCocoaWindow) {
// The parent window might have moved while this window was hidden,
// update the window geometry if there is a parent.
@@ -368,12 +390,18 @@ void QCocoaWindow::setVisible(bool visible)
if (window()->windowState() != Qt::WindowMinimized) {
if (parentCocoaWindow && (window()->modality() == Qt::WindowModal || window()->type() == Qt::Sheet)) {
// Show the window as a sheet
- [parentCocoaWindow->nativeWindow() beginSheet:m_view.window completionHandler:nil];
+ NSWindow *nativeParentWindow = parentCocoaWindow->nativeWindow();
+ if (!nativeParentWindow.attachedSheet)
+ [nativeParentWindow beginSheet:m_view.window completionHandler:nil];
+ else
+ [nativeParentWindow beginCriticalSheet:m_view.window completionHandler:nil];
} else if (window()->modality() == Qt::ApplicationModal) {
// 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.
@@ -387,21 +415,6 @@ void QCocoaWindow::setVisible(bool visible)
} else {
[m_view.window orderFront:nil];
}
-
- // Close popup when clicking outside it
- if (window()->type() == Qt::Popup && !(parentCocoaWindow && window()->transientParent()->isActive())) {
- removeMonitor();
- NSEventMask eventMask = NSEventMaskLeftMouseDown | NSEventMaskRightMouseDown
- | NSEventMaskOtherMouseDown | NSEventMaskMouseMoved;
- m_monitor = [NSEvent addGlobalMonitorForEventsMatchingMask:eventMask handler:^(NSEvent *e) {
- const auto button = cocoaButton2QtButton(e);
- const auto buttons = currentlyPressedMouseButtons();
- const auto eventType = cocoaEvent2QtMouseEvent(e);
- const auto globalPoint = QCocoaScreen::mapFromNative(NSEvent.mouseLocation);
- const auto localPoint = window()->mapFromGlobal(globalPoint.toPoint());
- QWindowSystemInterface::handleMouseEvent(window(), localPoint, globalPoint, buttons, button, eventType);
- }];
- }
}
}
@@ -443,11 +456,6 @@ void QCocoaWindow::setVisible(bool visible)
[m_view setHidden:YES];
}
- removeMonitor();
-
- if (window()->type() == Qt::Popup || window()->type() == Qt::ToolTip)
- QCocoaIntegration::instance()->popupWindowStack()->removeAll(this);
-
if (parentCocoaWindow && window()->type() == Qt::Popup) {
NSWindow *nativeParentWindow = parentCocoaWindow->nativeWindow();
if (m_resizableTransientParent
@@ -513,65 +521,92 @@ NSInteger QCocoaWindow::windowLevel(Qt::WindowFlags flags)
NSUInteger QCocoaWindow::windowStyleMask(Qt::WindowFlags flags)
{
const Qt::WindowType type = static_cast<Qt::WindowType>(int(flags & Qt::WindowType_Mask));
- const bool frameless = (flags & Qt::FramelessWindowHint) || windowIsPopupType(type);
-
- // Remove zoom button by disabling resize for CustomizeWindowHint windows, except for
- // Qt::Tool windows (e.g. dock windows) which should always be resizable.
- const bool resizable = !(flags & Qt::CustomizeWindowHint) || (type == Qt::Tool);
-
- // Select base window type. Note that the value of NSBorderlessWindowMask is 0.
- NSUInteger styleMask = (frameless || !resizable) ? NSWindowStyleMaskBorderless : NSWindowStyleMaskResizable;
-
- if (frameless) {
- // Frameless windows do not display the traffic lights buttons for
- // e.g. minimize, however StyleMaskMiniaturizable is required to allow
- // programatic minimize. However, for framless tool windows (e.g. dock windows)
- // we don't want that, as it breaks translucency.
- if (type != Qt::Tool)
- styleMask |= NSWindowStyleMaskMiniaturizable;
- } else if (flags & Qt::CustomizeWindowHint) {
- if (flags & Qt::WindowTitleHint)
- styleMask |= NSWindowStyleMaskTitled;
- if (flags & Qt::WindowCloseButtonHint)
- styleMask |= NSWindowStyleMaskClosable;
- if (flags & Qt::WindowMinimizeButtonHint)
- styleMask |= NSWindowStyleMaskMiniaturizable;
- if (flags & Qt::WindowMaximizeButtonHint)
- styleMask |= NSWindowStyleMaskResizable;
- } else {
- styleMask |= NSWindowStyleMaskClosable | NSWindowStyleMaskTitled;
- if (type != Qt::Dialog)
- styleMask |= NSWindowStyleMaskMiniaturizable;
- }
+ // Determine initial style mask based on whether the window should
+ // have a frame and title or not. The NSWindowStyleMaskBorderless
+ // and NSWindowStyleMaskTitled styles are mutually exclusive, with
+ // values of 0 and 1 correspondingly.
+ NSUInteger styleMask = [&]{
+ // Honor explicit requests for borderless windows
+ if (flags & Qt::FramelessWindowHint)
+ return NSWindowStyleMaskBorderless;
+
+ // Popup windows should always be borderless
+ if (windowIsPopupType(type))
+ return NSWindowStyleMaskBorderless;
+
+ if (flags & Qt::CustomizeWindowHint) {
+ // CustomizeWindowHint turns off the default window title hints,
+ // so the choice is then up to the user via Qt::WindowTitleHint.
+ return flags & Qt::WindowTitleHint
+ ? NSWindowStyleMaskTitled
+ : NSWindowStyleMaskBorderless;
+ } else {
+ // Otherwise, default to using titled windows
+ return NSWindowStyleMaskTitled;
+ }
+ }();
+
+ // We determine which buttons to show in updateTitleBarButtons,
+ // so we can enable all the relevant style masks here to ensure
+ // that behaviors that don't involve the title bar buttons are
+ // working (for example minimizing frameless windows, or resizing
+ // windows that don't have zoom or fullscreen titlebar buttons).
+ styleMask |= NSWindowStyleMaskClosable
+ | NSWindowStyleMaskResizable
+ | NSWindowStyleMaskMiniaturizable;
if (type == Qt::Tool)
styleMask |= NSWindowStyleMaskUtilityWindow;
+ // FIXME: Remove use of deprecated style mask
if (m_drawContentBorderGradient)
styleMask |= NSWindowStyleMaskTexturedBackground;
- // Don't wipe fullscreen state
+ // Don't wipe existing states
if (m_view.window.styleMask & NSWindowStyleMaskFullScreen)
styleMask |= NSWindowStyleMaskFullScreen;
+ if (m_view.window.styleMask & NSWindowStyleMaskFullSizeContentView)
+ styleMask |= NSWindowStyleMaskFullSizeContentView;
return styleMask;
}
-void QCocoaWindow::setWindowZoomButton(Qt::WindowFlags flags)
+bool QCocoaWindow::isFixedSize() const
+{
+ return windowMinimumSize().isValid() && windowMaximumSize().isValid()
+ && windowMinimumSize() == windowMaximumSize();
+}
+
+void QCocoaWindow::updateTitleBarButtons(Qt::WindowFlags windowFlags)
{
if (!isContentView())
return;
- // Disable the zoom (maximize) button for fixed-sized windows and customized
- // no-WindowMaximizeButtonHint windows. From a Qt perspective it migth be expected
- // that the button would be removed in the latter case, but disabling it is more
- // in line with the platform style guidelines.
- bool fixedSizeNoZoom = (windowMinimumSize().isValid() && windowMaximumSize().isValid()
- && windowMinimumSize() == windowMaximumSize());
- bool customizeNoZoom = ((flags & Qt::CustomizeWindowHint)
- && !(flags & (Qt::WindowMaximizeButtonHint | Qt::WindowFullscreenButtonHint)));
- [[m_view.window standardWindowButton:NSWindowZoomButton] setEnabled:!(fixedSizeNoZoom || customizeNoZoom)];
+ NSWindow *window = m_view.window;
+
+ static constexpr std::pair<NSWindowButton, Qt::WindowFlags> buttons[] = {
+ { NSWindowCloseButton, Qt::WindowCloseButtonHint },
+ { NSWindowMiniaturizeButton, Qt::WindowMinimizeButtonHint},
+ { NSWindowZoomButton, Qt::WindowMaximizeButtonHint | Qt::WindowFullscreenButtonHint }
+ };
+
+ bool hideButtons = true;
+ for (const auto &[button, buttonHint] : buttons) {
+ bool enabled = true;
+ if (windowFlags & Qt::CustomizeWindowHint)
+ enabled = windowFlags & buttonHint;
+
+ if (button == NSWindowZoomButton && isFixedSize())
+ enabled = false;
+
+ [window standardWindowButton:button].enabled = enabled;
+ hideButtons &= !enabled;
+ }
+
+ // Hide buttons in case we disabled all of them
+ for (const auto &[button, buttonHint] : buttons)
+ [window standardWindowButton:button].hidden = hideButtons;
}
void QCocoaWindow::setWindowFlags(Qt::WindowFlags flags)
@@ -616,7 +651,7 @@ void QCocoaWindow::setWindowFlags(Qt::WindowFlags flags)
if (!(flags & Qt::FramelessWindowHint))
setWindowTitle(window()->title());
- setWindowZoomButton(flags);
+ updateTitleBarButtons(flags);
// Make window ignore mouse events if WindowTransparentForInput is set.
// Note that ignoresMouseEvents has a special initial state where events
@@ -753,6 +788,11 @@ void QCocoaWindow::toggleMaximized()
window.styleMask &= ~NSWindowStyleMaskResizable;
}
+void QCocoaWindow::windowWillZoom()
+{
+ updateNormalGeometry();
+}
+
void QCocoaWindow::toggleFullScreen()
{
const NSWindow *window = m_view.window;
@@ -771,6 +811,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.
@@ -830,11 +872,6 @@ void QCocoaWindow::windowDidExitFullScreen()
}
}
-void QCocoaWindow::windowWillMiniaturize()
-{
- QCocoaIntegration::instance()->closePopups(window());
-}
-
void QCocoaWindow::windowDidMiniaturize()
{
if (!isContentView())
@@ -1028,7 +1065,7 @@ void QCocoaWindow::propagateSizeHints()
window.contentMaxSize = NSSizeFromCGSize(windowMaximumSize().toCGSize());
// The window may end up with a fixed size; in this case the zoom button should be disabled.
- setWindowZoomButton(this->window()->flags());
+ updateTitleBarButtons(this->window()->flags());
// sizeIncrement is observed to take values of (-1, -1) and (0, 0) for windows that should be
// resizable and that have no specific size increment set. Cocoa expects (1.0, 1.0) in this case.
@@ -1119,7 +1156,7 @@ NSWindow *QCocoaWindow::nativeWindow() const
void QCocoaWindow::setEmbeddedInForeignView()
{
- // Release any previosly created NSWindow.
+ // Release any previously created NSWindow.
[m_nsWindow closeAndRelease];
m_nsWindow = 0;
}
@@ -1155,12 +1192,6 @@ void QCocoaWindow::viewDidChangeGlobalFrame()
// callback should make sure to filter out notifications if they do not
// apply to that QCocoaWindow, e.g. if the window is not a content view.
-void QCocoaWindow::windowWillMove()
-{
- // Close any open popups on window move
- QCocoaIntegration::instance()->closePopups();
-}
-
void QCocoaWindow::windowDidMove()
{
if (!isContentView())
@@ -1199,13 +1230,6 @@ void QCocoaWindow::windowDidBecomeKey()
if (isForeignWindow())
return;
- if (m_windowUnderMouse) {
- QPointF windowPoint;
- QPointF screenPoint;
- [qnsview_cast(m_view) convertFromScreen:[NSEvent mouseLocation] toWindowPoint:&windowPoint andScreenPoint:&screenPoint];
- QWindowSystemInterface::handleEnterEvent(m_enterLeaveTargetWindow, windowPoint, screenPoint);
- }
-
QNSView *firstResponderView = qt_objc_cast<QNSView *>(m_view.window.firstResponder);
if (!firstResponderView)
return;
@@ -1227,13 +1251,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()) {
@@ -1244,12 +1273,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()
@@ -1268,11 +1317,15 @@ void QCocoaWindow::windowDidChangeScreen()
return;
// Note: When a window is resized to 0x0 Cocoa will report the window's screen as nil
- auto *currentScreen = QCocoaScreen::get(m_view.window.screen);
+ NSScreen *nsScreen = m_view.window.screen;
+
+ qCDebug(lcQpaWindow) << window() << "did change" << nsScreen;
+ QCocoaScreen::updateScreens();
+
auto *previousScreen = static_cast<QCocoaScreen*>(screen());
+ auto *currentScreen = QCocoaScreen::get(nsScreen);
- Q_ASSERT_X(!m_view.window.screen || currentScreen,
- "QCocoaWindow", "Failed to get QCocoaScreen for NSScreen");
+ qCDebug(lcQpaWindow) << "Screen changed for" << window() << "from" << previousScreen << "to" << currentScreen;
// Note: The previous screen may be the same as the current screen, either because
// a) the screen was just reconfigured, which still results in AppKit sending an
@@ -1285,7 +1338,6 @@ void QCocoaWindow::windowDidChangeScreen()
// device-pixel ratio may have changed, and needs to be delivered to all
// windows, both top level and child windows.
- qCDebug(lcQpaWindow) << "Screen changed for" << window() << "from" << previousScreen << "to" << currentScreen;
QWindowSystemInterface::handleWindowScreenChanged<QWindowSystemInterface::SynchronousDelivery>(
window(), currentScreen ? currentScreen->screen() : nullptr);
@@ -1298,13 +1350,6 @@ void QCocoaWindow::windowDidChangeScreen()
}
}
-void QCocoaWindow::windowWillClose()
-{
- // Close any open popups on window closing.
- if (window() && !windowIsPopupType(window()->type()))
- QCocoaIntegration::instance()->closePopups();
-}
-
// ----------------------- NSWindowDelegate callbacks -----------------------
bool QCocoaWindow::windowShouldClose()
@@ -1682,14 +1727,6 @@ bool QCocoaWindow::alwaysShowToolWindow() const
return qt_mac_resolveOption(false, window(), "_q_macAlwaysShowToolWindow", "");
}
-void QCocoaWindow::removeMonitor()
-{
- if (!m_monitor)
- return;
- [NSEvent removeMonitor:m_monitor];
- m_monitor = nil;
-}
-
bool QCocoaWindow::setWindowModified(bool modified)
{
if (!isContentView())
@@ -1722,6 +1759,20 @@ void QCocoaWindow::setWindowCursor(NSCursor *cursor)
view.cursor = cursor;
[m_view.window invalidateCursorRectsForView:m_view];
+
+ // There's a bug in AppKit where calling invalidateCursorRectsForView when
+ // there's an override cursor active (for example when hovering over the
+ // window frame), will not result in a cursorUpdate: callback. To work around
+ // this we synthesize a cursor update event and call the callback ourselves,
+ // if we detect that the mouse is currently over the view.
+ auto locationInWindow = m_view.window.mouseLocationOutsideOfEventStream;
+ auto locationInSuperview = [m_view.superview convertPoint:locationInWindow fromView:nil];
+ if ([m_view hitTest:locationInSuperview] == m_view) {
+ [m_view cursorUpdate:[NSEvent enterExitEventWithType:NSEventTypeCursorUpdate
+ location:locationInWindow modifierFlags:0 timestamp:0
+ windowNumber:m_view.window.windowNumber context:nil
+ eventNumber:0 trackingNumber:0 userData:0]];
+ }
}
void QCocoaWindow::registerTouch(bool enable)
@@ -1785,7 +1836,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))
@@ -1836,7 +1887,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/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.h b/src/plugins/platforms/cocoa/qnsview.h
index a9a547c891..526e4c0be2 100644
--- a/src/plugins/platforms/cocoa/qnsview.h
+++ b/src/plugins/platforms/cocoa/qnsview.h
@@ -56,6 +56,7 @@ QT_DECLARE_NAMESPACED_OBJC_INTERFACE(QNSView, NSView
#if defined(__OBJC__)
@interface QNSView (MouseAPI)
+- (void)handleMouseEvent:(NSEvent *)theEvent;
- (void)handleFrameStrutMouseEvent:(NSEvent *)theEvent;
- (void)resetMouseButtons;
@end
diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm
index bcc6c5ee61..d3f1a211de 100644
--- a/src/plugins/platforms/cocoa/qnsview.mm
+++ b/src/plugins/platforms/cocoa/qnsview.mm
@@ -63,6 +63,7 @@
#include <private/qcoregraphics_p.h>
#include <private/qwindow_p.h>
#include <private/qpointingdevice_p.h>
+#include <private/qhighdpiscaling_p.h>
#include "qcocoabackingstore.h"
#ifndef QT_NO_OPENGL
#include "qcocoaglcontext.h"
@@ -116,40 +117,37 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSViewMouseMoveHelper);
@end
@interface QNSView (ComplexText) <NSTextInputClient>
-- (void)textInputContextKeyboardSelectionDidChangeNotification:(NSNotification *)textInputContextKeyboardSelectionDidChangeNotification;
@end
@implementation QNSView {
QPointer<QCocoaWindow> m_platformWindow;
+
+ // Mouse
+ QNSViewMouseMoveHelper *m_mouseMoveHelper;
Qt::MouseButtons m_buttons;
Qt::MouseButtons m_acceptedMouseDowns;
Qt::MouseButtons m_frameStrutButtons;
- QString m_composingText;
- QPointer<QObject> m_composingFocusObject;
- bool m_lastKeyDead;
- bool m_sendKeyEvent;
+ Qt::KeyboardModifiers m_currentWheelModifiers;
bool m_dontOverrideCtrlLMB;
bool m_sendUpAsRightButton;
- Qt::KeyboardModifiers m_currentWheelModifiers;
- NSString *m_inputSource;
- QNSViewMouseMoveHelper *m_mouseMoveHelper;
- bool m_resendKeyEvent;
bool m_scrolling;
bool m_updatingDrag;
+
+ // Keys
+ bool m_lastKeyDead;
+ bool m_sendKeyEvent;
NSEvent *m_currentlyInterpretedKeyEvent;
QSet<quint32> m_acceptedKeyDowns;
+
+ // Text
+ QString m_composingText;
+ QPointer<QObject> m_composingFocusObject;
}
- (instancetype)initWithCocoaWindow:(QCocoaWindow *)platformWindow
{
if ((self = [super initWithFrame:NSZeroRect])) {
m_platformWindow = platformWindow;
- m_lastKeyDead = false;
- m_sendKeyEvent = false;
- m_inputSource = nil;
- m_resendKeyEvent = false;
- m_updatingDrag = false;
- m_currentlyInterpretedKeyEvent = nil;
self.focusRingType = NSFocusRingTypeNone;
@@ -160,10 +158,11 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSViewMouseMoveHelper);
[self initMouse];
[self registerDragTypes];
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(textInputContextKeyboardSelectionDidChangeNotification:)
- name:NSTextInputContextKeyboardSelectionDidChangeNotification
- object:nil];
+ m_updatingDrag = false;
+
+ m_lastKeyDead = false;
+ m_sendKeyEvent = false;
+ m_currentlyInterpretedKeyEvent = nil;
}
return self;
}
@@ -172,7 +171,6 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSViewMouseMoveHelper);
{
qCDebug(lcQpaWindow) << "Deallocating" << self;
- [m_inputSource release];
[[NSNotificationCenter defaultCenter] removeObserver:self];
[m_mouseMoveHelper release];
@@ -324,14 +322,15 @@ 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
// QWindow activation from QCocoaWindow::windowDidBecomeKey instead. The only
// exception is if the window can never become key, in which case we naturally
// cannot wait for that to happen.
- QWindowSystemInterface::handleWindowActivated([self topLevelWindow], Qt::ActiveWindowFocusReason);
+ QWindowSystemInterface::handleWindowActivated<QWindowSystemInterface::SynchronousDelivery>(
+ [self topLevelWindow], Qt::ActiveWindowFocusReason);
}
return YES;
diff --git a/src/plugins/platforms/cocoa/qnsview_complextext.mm b/src/plugins/platforms/cocoa/qnsview_complextext.mm
index 48cea12a14..39739d9725 100644
--- a/src/plugins/platforms/cocoa/qnsview_complextext.mm
+++ b/src/plugins/platforms/cocoa/qnsview_complextext.mm
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
@@ -39,277 +39,543 @@
// This file is included from qnsview.mm, and only used to organize the code
-@implementation QNSView (ComplexTextAPI)
+@implementation QNSView (ComplexText)
-- (void)cancelComposingText
-{
- if (m_composingText.isEmpty())
- return;
+// ------------- Text insertion -------------
- if (m_composingFocusObject) {
- QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
- if (QCoreApplication::sendEvent(m_composingFocusObject, &queryEvent)) {
- if (queryEvent.value(Qt::ImEnabled).toBool()) {
- QInputMethodEvent e;
- QCoreApplication::sendEvent(m_composingFocusObject, &e);
- }
+/*
+ Inserts the given text, potentially replacing existing text.
+
+ The text input management system calls this as a result of:
+
+ - A normal key press, via [NSView interpretKeyEvents:] or
+ [NSInputContext handleEvent:]
+
+ - An input method finishing (confirming) composition
+
+ - Pressing a key in the Keyboard Viewer panel
+
+ - Confirming an inline input area (accent popup e.g.)
+
+ \a replacementRange refers to the existing text to replace.
+ Under normal circumstances this is {NSNotFound, 0}, and the
+ implementation should replace either the existing marked text,
+ the current selection, or just insert the text at the current
+ cursor location.
+*/
+- (void)insertText:(id)text replacementRange:(NSRange)replacementRange
+{
+ qCDebug(lcQpaKeys).nospace() << "Inserting \"" << text << "\""
+ << ", replacing range " << replacementRange;
+
+ if (m_composingText.isEmpty()) {
+ // The input method may have transformed the incoming key event
+ // to text that doesn't match what the original key event would
+ // have produced, for example when 'Pinyin - Simplified' does smart
+ // replacement of quotes. If that's the case we can't rely on
+ // handleKeyEvent for sending the text.
+ auto *currentEvent = NSApp.currentEvent;
+ NSString *eventText = currentEvent.type == NSEventTypeKeyDown
+ || currentEvent.type == NSEventTypeKeyUp
+ ? currentEvent.characters : nil;
+
+ if ([text isEqualToString:eventText]) {
+ // We do not send input method events for simple text input,
+ // and instead let handleKeyEvent send the key event.
+ qCDebug(lcQpaKeys) << "Ignoring text insertion for simple text";
+ m_sendKeyEvent = true;
+ return;
}
}
- m_composingText.clear();
- m_composingFocusObject = nullptr;
-}
+ QObject *focusObject = m_platformWindow->window()->focusObject();
+ if (queryInputMethod(focusObject)) {
+ QInputMethodEvent inputMethodEvent;
-- (void)unmarkText
-{
- if (!m_composingText.isEmpty()) {
- if (QObject *fo = m_platformWindow->window()->focusObject()) {
- QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
- if (QCoreApplication::sendEvent(fo, &queryEvent)) {
- if (queryEvent.value(Qt::ImEnabled).toBool()) {
- QInputMethodEvent e;
- e.setCommitString(m_composingText);
- QCoreApplication::sendEvent(fo, &e);
- }
- }
+ const bool isAttributedString = [text isKindOfClass:NSAttributedString.class];
+ QString commitString = QString::fromNSString(isAttributedString ? [text string] : text);
+
+ // Ensure we have a valid replacement range
+ replacementRange = [self sanitizeReplacementRange:replacementRange];
+
+ // Qt's QInputMethodEvent has different semantics for the replacement
+ // range than AppKit does, so we need to sanitize the range first.
+ auto [replaceFrom, replaceLength] = [self inputMethodRangeForRange:replacementRange];
+
+ if (replaceFrom == NSNotFound) {
+ qCWarning(lcQpaKeys) << "Failed to compute valid replacement range for text insertion";
+ inputMethodEvent.setCommitString(commitString);
+ } else {
+ qCDebug(lcQpaKeys) << "Replacing from" << replaceFrom << "with length" << replaceLength
+ << "based on replacement range" << replacementRange;
+ inputMethodEvent.setCommitString(commitString, replaceFrom, replaceLength);
}
+
+ QCoreApplication::sendEvent(focusObject, &inputMethodEvent);
}
+
m_composingText.clear();
m_composingFocusObject = nullptr;
}
-@end
-
-@implementation QNSView (ComplexText)
-
- (void)insertNewline:(id)sender
{
Q_UNUSED(sender);
- m_resendKeyEvent = true;
-}
-- (void)doCommandBySelector:(SEL)aSelector
-{
- [self tryToPerform:aSelector with:self];
+ // Depending on the input method, pressing enter may
+ // result in simply dismissing the input method editor,
+ // without confirming the composition. In other cases
+ // it may confirm the composition as well. And in some
+ // cases the IME will produce an explicit new line, which
+ // brings us here.
+
+ // Semantically, the input method has asked us to insert
+ // a newline, and we should do so via an QInputMethodEvent,
+ // either directly or via [self insertText:@"\r"]. This is
+ // also how NSTextView handles the command. But, if we did,
+ // we would bypass all the code in Qt (and clients) that
+ // assume that pressing the return key results in a key
+ // event, for example the QLineEdit::returnPressed logic.
+ // To ensure that clients will still see the Qt::Key_Return
+ // key event, we send it as a normal key event.
+
+ // But, we can not fall back to handleKeyEvent for this,
+ // as the original key event may have text that reflects
+ // the combination of the inserted text and the newline,
+ // e.g. "~\r". We have already inserted the composition,
+ // so we need to follow up with a single newline event.
+
+ KeyEvent newlineEvent(NSApp.currentEvent);
+ newlineEvent.key = Qt::Key_Return;
+ newlineEvent.text = QLatin1Char(kReturnCharCode);
+ newlineEvent.nativeVirtualKey = kVK_Return;
+ qCDebug(lcQpaKeys) << "Inserting newline via" << newlineEvent;
+ newlineEvent.sendWindowSystemEvent(m_platformWindow->window());
}
-- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange
+// ------------- Text composition -------------
+
+/*
+ Updates the composed text, potentially replacing existing text.
+
+ The NSTextInputClient protocol refers to composed text as "marked",
+ since it is "marked differently from the selection, using temporary
+ attributes that affect only display, not layout or storage.""
+
+ The concept maps to the preeditString of our QInputMethodEvent.
+
+ \a selectedRange refers to the part of the marked text that
+ is considered selected, for example when composing text with
+ multiple clause segments (Hiragana - Kana e.g.).
+
+ \a replacementRange refers to the existing text to replace.
+ Under normal circumstances this is {NSNotFound, 0}, and the
+ implementation should replace either the existing marked text,
+ the current selection, or just insert the text at the current
+ cursor location. But when initiating composition of existing
+ committed text (Hiragana - Kana e.g.), the range will be valid.
+*/
+- (void)setMarkedText:(id)text selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
{
- Q_UNUSED(replacementRange);
+ qCDebug(lcQpaKeys).nospace() << "Marking \"" << text << "\""
+ << " with selected range " << selectedRange
+ << ", replacing range " << replacementRange;
+
+ const bool isAttributedString = [text isKindOfClass:NSAttributedString.class];
+ QString preeditString = QString::fromNSString(isAttributedString ? [text string] : text);
+
+ QList<QInputMethodEvent::Attribute> preeditAttributes;
+
+ // The QInputMethodEvent::Cursor specifies that the length
+ // determines whether the cursor is visible or not, but uses
+ // logic opposite of that of native AppKit application, where
+ // the cursor is visible if there's no selection, and hidden
+ // if there's a selection. Instead of passing on the length
+ // directly we need to inverse the logic.
+ const bool showCursor = !selectedRange.length;
+ preeditAttributes << QInputMethodEvent::Attribute(
+ QInputMethodEvent::Cursor, selectedRange.location, showCursor);
+
+ // QInputMethodEvent::Selection unfortunately doesn't apply to the
+ // preedit text, and QInputMethodEvent::Cursor which does, doesn't
+ // support setting a selection. Until we've introduced attributes
+ // that allow us to propagate the preedit selection semantically
+ // we resort to styling the selection via the TextFormat attribute,
+ // so that the preedit selection is visible to the user.
+ QTextCharFormat selectionFormat;
+ auto *platformTheme = QGuiApplicationPrivate::platformTheme();
+ auto *systemPalette = platformTheme->palette();
+ selectionFormat.setBackground(systemPalette->color(QPalette::Highlight));
+ preeditAttributes << QInputMethodEvent::Attribute(
+ QInputMethodEvent::TextFormat,
+ selectedRange.location, selectedRange.length,
+ selectionFormat);
+
+ int index = 0;
+ int composingLength = preeditString.length();
+ while (index < composingLength) {
+ NSRange range = NSMakeRange(index, composingLength - index);
+
+ static NSDictionary *defaultMarkedTextAttributes = []{
+ NSTextView *textView = [[NSTextView new] autorelease];
+ return [textView.markedTextAttributes retain];
+ }();
+
+ NSDictionary *attributes = isAttributedString
+ ? [text attributesAtIndex:index longestEffectiveRange:&range inRange:range]
+ : defaultMarkedTextAttributes;
+
+ qCDebug(lcQpaKeys) << "Decorating range" << range << "based on" << attributes;
+ QTextCharFormat format;
- if (m_sendKeyEvent && m_composingText.isEmpty() && [aString isEqualToString:m_inputSource]) {
- // don't send input method events for simple text input (let handleKeyEvent send key events instead)
- return;
- }
+ if (NSNumber *underlineStyle = attributes[NSUnderlineStyleAttributeName]) {
+ format.setFontUnderline(true);
+ NSUnderlineStyle style = underlineStyle.integerValue;
+ if (style & NSUnderlineStylePatternDot)
+ format.setUnderlineStyle(QTextCharFormat::DotLine);
+ else if (style & NSUnderlineStylePatternDash)
+ format.setUnderlineStyle(QTextCharFormat::DashUnderline);
+ else if (style & NSUnderlineStylePatternDashDot)
+ format.setUnderlineStyle(QTextCharFormat::DashDotLine);
+ if (style & NSUnderlineStylePatternDashDotDot)
+ format.setUnderlineStyle(QTextCharFormat::DashDotDotLine);
+ else
+ format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
+
+ // Unfortunately QTextCharFormat::UnderlineStyle does not distinguish
+ // between NSUnderlineStyle{Single,Thick,Double}, which is used by CJK
+ // input methods to highlight the selected clause segments.
+ }
+ if (NSColor *underlineColor = attributes[NSUnderlineColorAttributeName])
+ format.setUnderlineColor(qt_mac_toQColor(underlineColor));
+ if (NSColor *foregroundColor = attributes[NSForegroundColorAttributeName])
+ format.setForeground(qt_mac_toQColor(foregroundColor));
+ if (NSColor *backgroundColor = attributes[NSBackgroundColorAttributeName])
+ format.setBackground(qt_mac_toQColor(backgroundColor));
+
+ if (format != QTextCharFormat()) {
+ preeditAttributes << QInputMethodEvent::Attribute(
+ QInputMethodEvent::TextFormat, range.location, range.length, format);
+ }
- QString commitString;
- if ([aString length]) {
- if ([aString isKindOfClass:[NSAttributedString class]]) {
- commitString = QString::fromCFString(reinterpret_cast<CFStringRef>([aString string]));
- } else {
- commitString = QString::fromCFString(reinterpret_cast<CFStringRef>(aString));
- };
+ index = range.location + range.length;
}
- if (QObject *fo = m_platformWindow->window()->focusObject()) {
- QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
- if (QCoreApplication::sendEvent(fo, &queryEvent)) {
- if (queryEvent.value(Qt::ImEnabled).toBool()) {
- QInputMethodEvent e;
- e.setCommitString(commitString);
- QCoreApplication::sendEvent(fo, &e);
- // prevent handleKeyEvent from sending a key event
- m_sendKeyEvent = false;
+
+ // Ensure we have a valid replacement range
+ replacementRange = [self sanitizeReplacementRange:replacementRange];
+
+ // Qt's QInputMethodEvent has different semantics for the replacement
+ // range than AppKit does, so we need to sanitize the range first.
+ auto [replaceFrom, replaceLength] = [self inputMethodRangeForRange:replacementRange];
+
+ // Update the composition, now that we've computed the replacement range
+ m_composingText = preeditString;
+
+ if (QObject *focusObject = m_platformWindow->window()->focusObject()) {
+ m_composingFocusObject = focusObject;
+ if (queryInputMethod(focusObject)) {
+ QInputMethodEvent event(preeditString, preeditAttributes);
+ if (replaceLength > 0) {
+ // The input method may extend the preedit into already
+ // committed text. If so, we need to replace existing text
+ // by committing an empty string.
+ qCDebug(lcQpaKeys) << "Replacing from" << replaceFrom << "with length"
+ << replaceLength << "based on replacement range" << replacementRange;
+ event.setCommitString(QString(), replaceFrom, replaceLength);
}
+ QCoreApplication::sendEvent(focusObject, &event);
}
}
+}
- m_composingText.clear();
- m_composingFocusObject = nullptr;
+- (NSArray<NSString *> *)validAttributesForMarkedText
+{
+ return @[
+ NSUnderlineColorAttributeName,
+ NSUnderlineStyleAttributeName,
+ NSForegroundColorAttributeName,
+ NSBackgroundColorAttributeName
+ ];
}
-- (void)setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
+- (BOOL)hasMarkedText
{
- Q_UNUSED(replacementRange);
- QString preeditString;
-
- QList<QInputMethodEvent::Attribute> attrs;
- attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, selectedRange.location + selectedRange.length, 1, QVariant());
-
- if ([aString isKindOfClass:[NSAttributedString class]]) {
- // Preedit string has attribution
- preeditString = QString::fromCFString(reinterpret_cast<CFStringRef>([aString string]));
- int composingLength = preeditString.length();
- int index = 0;
- // Create attributes for individual sections of preedit text
- while (index < composingLength) {
- NSRange effectiveRange;
- NSRange range = NSMakeRange(index, composingLength-index);
- NSDictionary *attributes = [aString attributesAtIndex:index
- longestEffectiveRange:&effectiveRange
- inRange:range];
- NSNumber *underlineStyle = [attributes objectForKey:NSUnderlineStyleAttributeName];
- if (underlineStyle) {
- QColor clr (Qt::black);
- NSColor *color = [attributes objectForKey:NSUnderlineColorAttributeName];
- if (color) {
- clr = qt_mac_toQColor(color);
- }
- QTextCharFormat format;
- format.setFontUnderline(true);
- format.setUnderlineColor(clr);
- attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
- effectiveRange.location,
- effectiveRange.length,
- format);
- }
- index = effectiveRange.location + effectiveRange.length;
- }
+ return !m_composingText.isEmpty();
+}
+
+/*
+ Returns the range of marked text or {cursorPosition, 0} if there's none.
+
+ This maps to the location and length of the current preedit (composited) string.
+
+ The returned range measures from the start of the receiver’s text storage,
+ that is, from 0 to the document length.
+*/
+- (NSRange)markedRange
+{
+ QObject *focusObject = m_platformWindow->window()->focusObject();
+ if (auto queryResult = queryInputMethod(focusObject, Qt::ImAbsolutePosition)) {
+ int absoluteCursorPosition = queryResult.value(Qt::ImAbsolutePosition).toInt();
+
+ // The cursor position as reflected by Qt::ImAbsolutePosition is not
+ // affected by the offset of the cursor in the preedit area. That means
+ // that when composing text, the cursor position stays the same, at the
+ // preedit insertion point, regardless of where the cursor is positioned within
+ // the preedit string by the QInputMethodEvent::Cursor attribute. This means
+ // we can use the cursor position to determine the range of the marked text.
+
+ // The NSTextInputClient documentation says {NSNotFound, 0} should be returned if there
+ // is no marked text, but in practice NSTextView seems to report {cursorPosition, 0},
+ // so we do the same.
+ return NSMakeRange(absoluteCursorPosition, m_composingText.length());
} else {
- // No attributes specified, take only the preedit text.
- preeditString = QString::fromCFString(reinterpret_cast<CFStringRef>(aString));
+ return {NSNotFound, 0};
}
+}
- if (attrs.isEmpty()) {
- QTextCharFormat format;
- format.setFontUnderline(true);
- attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
- 0, preeditString.length(), format);
- }
+/*
+ Confirms the marked (composed) text.
- m_composingText = preeditString;
+ The marked text is accepted as if it had been inserted normally,
+ and the preedit string is cleared.
- if (QObject *fo = m_platformWindow->window()->focusObject()) {
- m_composingFocusObject = fo;
- QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
- if (QCoreApplication::sendEvent(fo, &queryEvent)) {
- if (queryEvent.value(Qt::ImEnabled).toBool()) {
- QInputMethodEvent e(preeditString, attrs);
- QCoreApplication::sendEvent(fo, &e);
- // prevent handleKeyEvent from sending a key event
- m_sendKeyEvent = false;
- }
+ If there is no marked text this method has no effect.
+*/
+- (void)unmarkText
+{
+ // FIXME: Match cancelComposingText in early exit and focus object handling
+
+ qCDebug(lcQpaKeys) << "Unmarking" << m_composingText
+ << "for focus object" << m_composingFocusObject;
+
+ if (!m_composingText.isEmpty()) {
+ QObject *focusObject = m_platformWindow->window()->focusObject();
+ if (queryInputMethod(focusObject)) {
+ QInputMethodEvent e;
+ e.setCommitString(m_composingText);
+ QCoreApplication::sendEvent(focusObject, &e);
}
}
+
+ m_composingText.clear();
+ m_composingFocusObject = nullptr;
}
-- (BOOL)hasMarkedText
+/*
+ Cancels composition.
+
+ The marked text is discarded, and the preedit string is cleared.
+
+ If there is no marked text this method has no effect.
+*/
+- (void)cancelComposingText
{
- return (m_composingText.isEmpty() ? NO: YES);
+ if (m_composingText.isEmpty())
+ return;
+
+ qCDebug(lcQpaKeys) << "Canceling composition" << m_composingText
+ << "for focus object" << m_composingFocusObject;
+
+ if (queryInputMethod(m_composingFocusObject)) {
+ QInputMethodEvent e;
+ QCoreApplication::sendEvent(m_composingFocusObject, &e);
+ }
+
+ m_composingText.clear();
+ m_composingFocusObject = nullptr;
}
-- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
+// ------------- Key binding command handling -------------
+
+- (void)doCommandBySelector:(SEL)selector
{
- Q_UNUSED(actualRange);
- QObject *fo = m_platformWindow->window()->focusObject();
- if (!fo)
- return nil;
- QInputMethodQueryEvent queryEvent(Qt::ImEnabled | Qt::ImCurrentSelection);
- if (!QCoreApplication::sendEvent(fo, &queryEvent))
- return nil;
- if (!queryEvent.value(Qt::ImEnabled).toBool())
- return nil;
+ // Note: if the selector cannot be invoked, then doCommandBySelector:
+ // should not pass this message up the responder chain (nor should it
+ // call super, as the NSResponder base class would in that case pass
+ // the message up the responder chain, which we don't want). We will
+ // pass the originating key event up the responder chain if applicable.
+
+ qCDebug(lcQpaKeys) << "Trying to perform command" << selector;
+ if (![self tryToPerform:selector with:self])
+ m_sendKeyEvent = true;
+}
- QString selectedText = queryEvent.value(Qt::ImCurrentSelection).toString();
- if (selectedText.isEmpty())
- return nil;
+// ------------- Various text properties -------------
- QCFString string(selectedText.mid(aRange.location, aRange.length));
- const NSString *tmpString = reinterpret_cast<const NSString *>((CFStringRef)string);
- return [[[NSAttributedString alloc] initWithString:const_cast<NSString *>(tmpString)] autorelease];
-}
+/*
+ Returns the range of selected text, or {cursorPosition, 0} if there's none.
-- (NSRange)markedRange
+ The returned range measures from the start of the receiver’s text storage,
+ that is, from 0 to the document length.
+*/
+- (NSRange)selectedRange
{
- NSRange range;
- if (!m_composingText.isEmpty()) {
- range.location = 0;
- range.length = m_composingText.length();
+ QObject *focusObject = m_platformWindow->window()->focusObject();
+ if (auto queryResult = queryInputMethod(focusObject,
+ Qt::ImCursorPosition | Qt::ImAbsolutePosition | Qt::ImAnchorPosition)) {
+
+ // Unfortunately the Qt::InputMethodQuery values are all relative
+ // to the start of the current editing block (paragraph), but we
+ // need them in absolute values relative to the entire text.
+ // Luckily we have one property, Qt::ImAbsolutePosition, that
+ // we can use to compute the offset.
+ int cursorPosition = queryResult.value(Qt::ImCursorPosition).toInt();
+ int absoluteCursorPosition = queryResult.value(Qt::ImAbsolutePosition).toInt();
+ int absoluteOffset = absoluteCursorPosition - cursorPosition;
+
+ int anchorPosition = absoluteOffset + queryResult.value(Qt::ImAnchorPosition).toInt();
+ int selectionStart = anchorPosition >= absoluteCursorPosition ? absoluteCursorPosition : anchorPosition;
+ int selectionEnd = selectionStart == anchorPosition ? absoluteCursorPosition : anchorPosition;
+ int selectionLength = selectionEnd - selectionStart;
+
+ // Note: The cursor position as reflected by these properties are not
+ // affected by the offset of the cursor in the preedit area. That means
+ // that when composing text, the cursor position stays the same, at the
+ // preedit insertion point, regardless of where the cursor is positioned within
+ // the preedit string by the QInputMethodEvent::Cursor attribute.
+
+ // The NSTextInputClient documentation says {NSNotFound, 0} should be returned if there is no
+ // selection, but in practice NSTextView seems to report {cursorPosition, 0}, so we do the same.
+ return NSMakeRange(selectionStart, selectionLength);
} else {
- range.location = NSNotFound;
- range.length = 0;
+ return {NSNotFound, 0};
}
- return range;
}
-- (NSRange)selectedRange
+/*
+ Returns an attributed string derived from the given range
+ in the underlying focus object's text storage.
+
+ Input methods may call this with a proposed range that is
+ out of bounds. For example, the InkWell text input service
+ may ask for the contents of the text input client that extends
+ beyond the document's range. To remedy this we always compute
+ the intersection between the proposed range and the available
+ text.
+
+ If the intersection is completely outside of the available text
+ this method returns nil.
+*/
+- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range actualRange:(NSRangePointer)actualRange
{
- NSRange selectedRange = {0, 0};
-
- QObject *fo = m_platformWindow->window()->focusObject();
- if (!fo)
- return selectedRange;
- QInputMethodQueryEvent queryEvent(Qt::ImEnabled | Qt::ImCurrentSelection);
- if (!QCoreApplication::sendEvent(fo, &queryEvent))
- return selectedRange;
- if (!queryEvent.value(Qt::ImEnabled).toBool())
- return selectedRange;
-
- QString selectedText = queryEvent.value(Qt::ImCurrentSelection).toString();
-
- if (!selectedText.isEmpty()) {
- selectedRange.location = 0;
- selectedRange.length = selectedText.length();
+ QObject *focusObject = m_platformWindow->window()->focusObject();
+ if (auto queryResult = queryInputMethod(focusObject,
+ Qt::ImAbsolutePosition | Qt::ImTextBeforeCursor | Qt::ImTextAfterCursor)) {
+ const int absoluteCursorPosition = queryResult.value(Qt::ImAbsolutePosition).toInt();
+ const QString textBeforeCursor = queryResult.value(Qt::ImTextBeforeCursor).toString();
+ const QString textAfterCursor = queryResult.value(Qt::ImTextAfterCursor).toString();
+
+ // The documentation doesn't say whether the marked text should be included
+ // in the available text, but observing NSTextView shows that this is the
+ // case, so we follow suit.
+ const QString availableText = textBeforeCursor + m_composingText + textAfterCursor;
+ const NSRange availableRange = NSMakeRange(absoluteCursorPosition - textBeforeCursor.length(),
+ availableText.length());
+
+ const NSRange intersectedRange = NSIntersectionRange(range, availableRange);
+ if (actualRange)
+ *actualRange = intersectedRange;
+
+ if (!intersectedRange.length)
+ return nil;
+
+ NSString *substring = QStringView(availableText).mid(
+ intersectedRange.location - availableRange.location,
+ intersectedRange.length).toNSString();
+
+ return [[[NSAttributedString alloc] initWithString:substring] autorelease];
+
+ } else {
+ return nil;
}
- return selectedRange;
}
-- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
+- (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(NSRangePointer)actualRange
{
- Q_UNUSED(aRange);
+ Q_UNUSED(range);
Q_UNUSED(actualRange);
- QObject *fo = m_platformWindow->window()->focusObject();
- if (!fo)
- return NSZeroRect;
-
- QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
- if (!QCoreApplication::sendEvent(fo, &queryEvent))
- return NSZeroRect;
- if (!queryEvent.value(Qt::ImEnabled).toBool())
+ QWindow *window = m_platformWindow->window();
+ if (queryInputMethod(window->focusObject())) {
+ QRect cursorRect = qApp->inputMethod()->cursorRectangle().toRect();
+ cursorRect.moveBottomLeft(window->mapToGlobal(cursorRect.bottomLeft()));
+ return QCocoaScreen::mapToNative(cursorRect);
+ } else {
return NSZeroRect;
-
- // The returned rect is always based on the internal cursor.
- QRect mr = qApp->inputMethod()->cursorRectangle().toRect();
- mr.moveBottomLeft(m_platformWindow->window()->mapToGlobal(mr.bottomLeft()));
- return QCocoaScreen::mapToNative(mr);
+ }
}
-- (NSUInteger)characterIndexForPoint:(NSPoint)aPoint
+- (NSUInteger)characterIndexForPoint:(NSPoint)point
{
// We don't support cursor movements using mouse while composing.
- Q_UNUSED(aPoint);
+ Q_UNUSED(point);
return NSNotFound;
}
-- (NSArray<NSString *> *)validAttributesForMarkedText
-{
- if (!m_platformWindow)
- return nil;
+// ------------- Helper functions -------------
- if (m_platformWindow->window() != QGuiApplication::focusWindow())
- return nil;
+/*
+ Sanitizes the replacement range, ensuring it's valid.
- QObject *fo = m_platformWindow->window()->focusObject();
- if (!fo)
- return nil;
+ If \a range is not valid the range of the current
+ marked text will be used.
- QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
- if (!QCoreApplication::sendEvent(fo, &queryEvent))
- return nil;
- if (!queryEvent.value(Qt::ImEnabled).toBool())
- return nil;
+ If there's no marked text the range of the current
+ selection will be used.
+
+ If there's no selection the range will be {cursorPosition, 0}.
+*/
+- (NSRange)sanitizeReplacementRange:(NSRange)range
+{
+ if (range.location != NSNotFound)
+ return range; // Use as is
- // Support only underline color/style.
- return @[NSUnderlineColorAttributeName, NSUnderlineStyleAttributeName];
+ // If the replacement range is not specified we are expected to compute
+ // the range ourselves, based on the current state of the input context.
+
+ const auto markedRange = [self markedRange];
+ if (markedRange.location != NSNotFound)
+ return markedRange;
+ else
+ return [self selectedRange];
}
-- (void)textInputContextKeyboardSelectionDidChangeNotification:(NSNotification *)textInputContextKeyboardSelectionDidChangeNotification
+/*
+ Computes the QInputMethodEvent commit string range,
+ based on the NSTextInputClient replacement range.
+
+ The two APIs have different semantics.
+*/
+- (std::pair<long long, long long>)inputMethodRangeForRange:(NSRange)range
{
- Q_UNUSED(textInputContextKeyboardSelectionDidChangeNotification);
- if (([NSApp keyWindow] == self.window) && self.window.firstResponder == self) {
- if (QCocoaInputContext *ic = qobject_cast<QCocoaInputContext *>(QCocoaIntegration::instance()->inputContext()))
- ic->updateLocale();
- }
+ long long replaceFrom = range.location;
+ long long replaceLength = range.length;
+
+ const auto markedRange = [self markedRange];
+ const auto selectedRange = [self selectedRange];
+
+ // The QInputMethodEvent replacement start is relative to the start
+ // of the marked text (the location of the preedit string).
+ if (markedRange.location != NSNotFound)
+ replaceFrom -= markedRange.location;
+ else
+ replaceFrom = 0;
+
+ // The replacement length of QInputMethodEvent already includes
+ // the selection, as the documentation says that "If the widget
+ // has selected text, the selected text should get removed."
+ replaceLength -= selectedRange.length;
+
+ // The replacement length of QInputMethodEvent already includes
+ // the preedit string, as the documentation says that "When doing
+ // replacement, the area of the preedit string is ignored".
+ replaceLength -= markedRange.length;
+
+ // What we're left with is any _additional_ replacement.
+ // Make sure it's valid before passing it on.
+ replaceLength = qMax(0ll, replaceLength);
+
+ return {replaceFrom, replaceLength};
}
@end
diff --git a/src/plugins/platforms/cocoa/qnsview_keys.mm b/src/plugins/platforms/cocoa/qnsview_keys.mm
index 57f7fe1fdd..96ab7ab168 100644
--- a/src/plugins/platforms/cocoa/qnsview_keys.mm
+++ b/src/plugins/platforms/cocoa/qnsview_keys.mm
@@ -41,105 +41,80 @@
@implementation QNSView (Keys)
-- (bool)handleKeyEvent:(NSEvent *)nsevent eventType:(int)eventType
+- (bool)handleKeyEvent:(NSEvent *)nsevent
{
- ulong timestamp = [nsevent timestamp] * 1000;
- ulong nativeModifiers = [nsevent modifierFlags];
- Qt::KeyboardModifiers modifiers = QAppleKeyMapper::fromCocoaModifiers(nativeModifiers);
- NSString *charactersIgnoringModifiers = [nsevent charactersIgnoringModifiers];
- NSString *characters = [nsevent characters];
- if (m_inputSource != characters) {
- [m_inputSource release];
- m_inputSource = [characters retain];
- }
-
- // Scan codes are hardware dependent codes for each key. There is no way to get these
- // from Carbon or Cocoa, so leave it 0, as documented in QKeyEvent::nativeScanCode().
- const quint32 nativeScanCode = 0;
-
- // Virtual keys on the other hand are mapped to be the same keys on any system
- const quint32 nativeVirtualKey = nsevent.keyCode;
-
- QChar ch = QChar::ReplacementCharacter;
- int keyCode = Qt::Key_unknown;
-
- // If a dead key occurs as a result of pressing a key combination then
- // characters will have 0 length, but charactersIgnoringModifiers will
- // have a valid character in it. This enables key combinations such as
- // ALT+E to be used as a shortcut with an English keyboard even though
- // pressing ALT+E will give a dead key while doing normal text input.
- if ([characters length] != 0 || [charactersIgnoringModifiers length] != 0) {
- if (nativeModifiers & (NSEventModifierFlagControl | NSEventModifierFlagOption)
- && [charactersIgnoringModifiers length] != 0)
- ch = QChar([charactersIgnoringModifiers characterAtIndex:0]);
- else if ([characters length] != 0)
- ch = QChar([characters characterAtIndex:0]);
- keyCode = QAppleKeyMapper::fromCocoaKey(ch);
- }
-
- // we will send a key event unless the input method sets m_sendKeyEvent to false
- m_sendKeyEvent = true;
- QString text;
- // ignore text for the U+F700-U+F8FF range. This is used by Cocoa when
- // delivering function keys (e.g. arrow keys, backspace, F1-F35, etc.)
- if (!(modifiers & (Qt::ControlModifier | Qt::MetaModifier)) && (ch.unicode() < 0xf700 || ch.unicode() > 0xf8ff))
- text = QString::fromNSString(characters);
+ qCDebug(lcQpaKeys) << "Handling" << nsevent;
+ KeyEvent keyEvent(nsevent);
+ // FIXME: Why is this the top level window and not m_platformWindow?
QWindow *window = [self topLevelWindow];
- // Popups implicitly grab key events; forward to the active popup if there is one.
- // This allows popups to e.g. intercept shortcuts and close the popup in response.
- if (QCocoaWindow *popup = QCocoaIntegration::instance()->activePopupWindow()) {
- if (!popup->window()->flags().testFlag(Qt::ToolTip))
- window = popup->window();
- }
+ // We will send a key event unless the input method handles it
+ QBoolBlocker sendKeyEventGuard(m_sendKeyEvent, true);
- if (eventType == QEvent::KeyPress) {
+ if (keyEvent.type == QEvent::KeyPress) {
if (m_composingText.isEmpty()) {
- m_sendKeyEvent = !QWindowSystemInterface::handleShortcutEvent(window, timestamp, keyCode,
- modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers, text, [nsevent isARepeat], 1);
+ KeyEvent shortcutEvent = keyEvent;
+ shortcutEvent.type = QEvent::Shortcut;
+ qCDebug(lcQpaKeys) << "Trying potential shortcuts in" << window
+ << "for" << shortcutEvent;
- // Handling a shortcut may result in closing the window
- if (!m_platformWindow)
+ if (shortcutEvent.sendWindowSystemEvent(window)) {
+ qCDebug(lcQpaKeys) << "Found matching shortcut; will not send as key event";
return true;
+ } else {
+ qCDebug(lcQpaKeys) << "No matching shortcuts; continuing with key event delivery";
+ }
}
- QObject *fo = m_platformWindow->window()->focusObject();
- if (m_sendKeyEvent && fo) {
- QInputMethodQueryEvent queryEvent(Qt::ImEnabled | Qt::ImHints);
- if (QCoreApplication::sendEvent(fo, &queryEvent)) {
- bool imEnabled = queryEvent.value(Qt::ImEnabled).toBool();
- Qt::InputMethodHints hints = static_cast<Qt::InputMethodHints>(queryEvent.value(Qt::ImHints).toUInt());
- // make sure we send dead keys and the next key to the input method for composition
- const bool ignoreHidden = (hints & Qt::ImhHiddenText) && !text.isEmpty() && !m_lastKeyDead;
- if (imEnabled && !(hints & Qt::ImhDigitsOnly || hints & Qt::ImhFormattedNumbersOnly || ignoreHidden)) {
- // pass the key event to the input method. note that m_sendKeyEvent may be set to false during this call
+ QObject *focusObject = m_platformWindow->window()->focusObject();
+ if (m_sendKeyEvent && focusObject) {
+ if (auto queryResult = queryInputMethod(focusObject, Qt::ImHints)) {
+ auto hints = static_cast<Qt::InputMethodHints>(queryResult.value(Qt::ImHints).toUInt());
+
+ // Make sure we send dead keys and the next key to the input method for composition
+ const bool isDeadKey = !nsevent.characters.length;
+ const bool ignoreHidden = (hints & Qt::ImhHiddenText) && !isDeadKey && !m_lastKeyDead;
+
+ if (!(hints & Qt::ImhDigitsOnly || hints & Qt::ImhFormattedNumbersOnly || ignoreHidden)) {
+ // Pass the key event to the input method, and assume it handles the event,
+ // unless we explicit set m_sendKeyEvent to deliver as a normal key event.
+ m_sendKeyEvent = false;
+
+ // Match NSTextView's keyDown behavior of hiding the cursor before
+ // interpreting key events. Shortcuts should not trigger this though.
+ // Unfortunately many of our controls handle shortcuts by accepting
+ // the ShortcutOverride event and then handling the shortcut in the
+ // following key event, and QWSI::handleShortcutEvent doesn't reveal
+ // whether this will be the case. For NSTextView this is not an issue
+ // as shortcuts are handled via performKeyEquivalent, which happens
+ // prior to keyDown. To work around this until we can get the info
+ // we need from handleShortcutEvent we match AppKit and assume that
+ // any key press with a command or control modifier is a shortcut.
+ if (!(nsevent.modifierFlags & (NSEventModifierFlagCommand | NSEventModifierFlagControl)))
+ [NSCursor setHiddenUntilMouseMoves:YES];
+
+ qCDebug(lcQpaKeys) << "Interpreting key event for focus object" << focusObject;
m_currentlyInterpretedKeyEvent = nsevent;
[self interpretKeyEvents:@[nsevent]];
- // If the receiver opens an editor in response to a key press, then the focus will change, the input
- // method will be reset, and the first key press will be gone. If the focus object changes, then we
- // need to pass the key event to the input method once more.
- if (qApp->focusObject() != fo)
- [self interpretKeyEvents:@[nsevent]];
m_currentlyInterpretedKeyEvent = 0;
- // if the last key we sent was dead, then pass the next key to the IM as well to complete composition
- m_lastKeyDead = text.isEmpty();
+
+ // If the last key we sent was dead, then pass the next
+ // key to the IM as well to complete composition.
+ m_lastKeyDead = isDeadKey;
}
+
}
}
- if (m_resendKeyEvent)
- m_sendKeyEvent = true;
}
bool accepted = true;
if (m_sendKeyEvent && m_composingText.isEmpty()) {
- QWindowSystemInterface::handleExtendedKeyEvent(window, timestamp, QEvent::Type(eventType), keyCode, modifiers,
- nativeScanCode, nativeVirtualKey, nativeModifiers, text, [nsevent isARepeat], 1, false);
- accepted = QWindowSystemInterface::flushWindowSystemEvents();
+ KeyEvent keyEvent(nsevent);
+ qCDebug(lcQpaKeys) << "Sending as" << keyEvent;
+ accepted = keyEvent.sendWindowSystemEvent(window);
}
- m_sendKeyEvent = false;
- m_resendKeyEvent = false;
return accepted;
}
@@ -148,7 +123,7 @@
if ([self isTransparentForUserInput])
return [super keyDown:nsevent];
- const bool accepted = [self handleKeyEvent:nsevent eventType:int(QEvent::KeyPress)];
+ const bool accepted = [self handleKeyEvent:nsevent];
// When Qt is used to implement a plugin for a native application we
// want to propagate unhandled events to other native views. However,
@@ -159,7 +134,7 @@
// Track keyDown acceptance/forward state for later acceptance of the keyUp.
if (!shouldPropagate)
- m_acceptedKeyDowns.insert([nsevent keyCode]);
+ m_acceptedKeyDowns.insert(nsevent.keyCode);
if (shouldPropagate)
[super keyDown:nsevent];
@@ -170,12 +145,12 @@
if ([self isTransparentForUserInput])
return [super keyUp:nsevent];
- const bool keyUpAccepted = [self handleKeyEvent:nsevent eventType:int(QEvent::KeyRelease)];
+ 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]);
+ const bool keyDownAccepted = m_acceptedKeyDowns.remove(nsevent.keyCode);
if (!keyUpAccepted && !keyDownAccepted)
[super keyUp:nsevent];
}
@@ -184,73 +159,156 @@
{
Q_UNUSED(sender);
- NSEvent *currentEvent = [NSApp currentEvent];
+ NSEvent *currentEvent = NSApp.currentEvent;
if (!currentEvent || currentEvent.type != NSEventTypeKeyDown)
return;
// 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
// intercept the shortcut and override its effect.
- [self handleKeyEvent:currentEvent eventType:int(QEvent::KeyPress)];
+ [self handleKeyEvent:currentEvent];
}
- (void)flagsChanged:(NSEvent *)nsevent
{
- ulong timestamp = [nsevent timestamp] * 1000;
- ulong nativeModifiers = [nsevent modifierFlags];
- Qt::KeyboardModifiers modifiers = QAppleKeyMapper::fromCocoaModifiers(nativeModifiers);
-
- // Scan codes are hardware dependent codes for each key. There is no way to get these
- // from Carbon or Cocoa, so leave it 0, as documented in QKeyEvent::nativeScanCode().
- const quint32 nativeScanCode = 0;
-
- // Virtual keys on the other hand are mapped to be the same keys on any system
- const quint32 nativeVirtualKey = nsevent.keyCode;
-
- // calculate the delta and remember the current modifiers for next time
- static ulong m_lastKnownModifiers;
- ulong lastKnownModifiers = m_lastKnownModifiers;
- ulong delta = lastKnownModifiers ^ nativeModifiers;
- m_lastKnownModifiers = nativeModifiers;
-
- struct qt_mac_enum_mapper
- {
- ulong mac_mask;
- Qt::Key qt_code;
- };
- static qt_mac_enum_mapper modifier_key_symbols[] = {
+ // FIXME: Why are we not checking isTransparentForUserInput here?
+
+ KeyEvent keyEvent(nsevent);
+ qCDebug(lcQpaKeys) << "Flags changed resulting in" << keyEvent.modifiers;
+
+ // Calculate the delta and remember the current modifiers for next time
+ static NSEventModifierFlags m_lastKnownModifiers;
+ NSEventModifierFlags lastKnownModifiers = m_lastKnownModifiers;
+ NSEventModifierFlags newModifiers = lastKnownModifiers ^ keyEvent.nativeModifiers;
+ m_lastKnownModifiers = keyEvent.nativeModifiers;
+
+ static constexpr std::tuple<NSEventModifierFlags, Qt::Key> modifierMap[] = {
{ NSEventModifierFlagShift, Qt::Key_Shift },
{ NSEventModifierFlagControl, Qt::Key_Meta },
{ NSEventModifierFlagCommand, Qt::Key_Control },
{ NSEventModifierFlagOption, Qt::Key_Alt },
- { NSEventModifierFlagCapsLock, Qt::Key_CapsLock },
- { 0ul, Qt::Key_unknown } };
- for (int i = 0; modifier_key_symbols[i].mac_mask != 0u; ++i) {
- uint mac_mask = modifier_key_symbols[i].mac_mask;
- if ((delta & mac_mask) == 0u)
+ { NSEventModifierFlagCapsLock, Qt::Key_CapsLock }
+ };
+
+ for (auto [macModifier, qtKey] : modifierMap) {
+ if (!(newModifiers & macModifier))
continue;
- Qt::Key qtCode = modifier_key_symbols[i].qt_code;
+ // FIXME: Use QAppleKeyMapper helper
if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) {
- if (qtCode == Qt::Key_Meta)
- qtCode = Qt::Key_Control;
- else if (qtCode == Qt::Key_Control)
- qtCode = Qt::Key_Meta;
+ if (qtKey == Qt::Key_Meta)
+ qtKey = Qt::Key_Control;
+ else if (qtKey == Qt::Key_Control)
+ qtKey = Qt::Key_Meta;
}
- QWindowSystemInterface::handleExtendedKeyEvent(m_platformWindow->window(),
- timestamp,
- (lastKnownModifiers & mac_mask) ? QEvent::KeyRelease
- : QEvent::KeyPress,
- qtCode,
- modifiers ^ QAppleKeyMapper::fromCocoaModifiers(mac_mask),
- nativeScanCode, nativeVirtualKey,
- nativeModifiers ^ mac_mask);
+
+ KeyEvent modifierEvent = keyEvent;
+ modifierEvent.type = lastKnownModifiers & macModifier
+ ? QEvent::KeyRelease : QEvent::KeyPress;
+
+ modifierEvent.key = qtKey;
+
+ // FIXME: Shouldn't this be based on lastKnownModifiers?
+ modifierEvent.modifiers ^= QAppleKeyMapper::fromCocoaModifiers(macModifier);
+ modifierEvent.nativeModifiers ^= macModifier;
+
+ // FIXME: Why are we sending to m_platformWindow here, but not for key events?
+ QWindow *window = m_platformWindow->window();
+
+ qCDebug(lcQpaKeys) << "Sending" << modifierEvent;
+ modifierEvent.sendWindowSystemEvent(window);
}
}
@end
+
+// -------------------------------------------------------------------------
+
+KeyEvent::KeyEvent(NSEvent *nsevent)
+{
+ timestamp = nsevent.timestamp * 1000;
+
+ switch (nsevent.type) {
+ case NSEventTypeKeyDown: type = QEvent::KeyPress; break;
+ case NSEventTypeKeyUp: type = QEvent::KeyRelease; break;
+ default: break; // Must be manually set
+ }
+
+ if (nsevent.type == NSEventTypeKeyDown || nsevent.type == NSEventTypeKeyUp) {
+ NSString *charactersIgnoringModifiers = nsevent.charactersIgnoringModifiers;
+ NSString *characters = nsevent.characters;
+
+ QChar character = QChar::ReplacementCharacter;
+
+ // If a dead key occurs as a result of pressing a key combination then
+ // characters will have 0 length, but charactersIgnoringModifiers will
+ // have a valid character in it. This enables key combinations such as
+ // ALT+E to be used as a shortcut with an English keyboard even though
+ // pressing ALT+E will give a dead key while doing normal text input.
+ if (characters.length || charactersIgnoringModifiers.length) {
+ if (nativeModifiers & (NSEventModifierFlagControl | NSEventModifierFlagOption)
+ && charactersIgnoringModifiers.length)
+ character = QChar([charactersIgnoringModifiers characterAtIndex:0]);
+ else if (characters.length)
+ character = QChar([characters characterAtIndex:0]);
+ key = QAppleKeyMapper::fromCocoaKey(character);
+ }
+
+ // Ignore text for the U+F700-U+F8FF range. This is used by Cocoa when
+ // delivering function keys (e.g. arrow keys, backspace, F1-F35, etc.)
+ if (!(modifiers & (Qt::ControlModifier | Qt::MetaModifier))
+ && (character.unicode() < 0xf700 || character.unicode() > 0xf8ff))
+ text = QString::fromNSString(characters);
+
+ isRepeat = nsevent.ARepeat;
+ }
+
+ nativeVirtualKey = nsevent.keyCode;
+
+ nativeModifiers = nsevent.modifierFlags;
+ modifiers = QAppleKeyMapper::fromCocoaModifiers(nativeModifiers);
+}
+
+bool KeyEvent::sendWindowSystemEvent(QWindow *window) const
+{
+ switch (type) {
+ case QEvent::Shortcut: {
+ return QWindowSystemInterface::handleShortcutEvent(window, timestamp,
+ key, modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers,
+ text, isRepeat);
+ }
+ case QEvent::KeyPress:
+ case QEvent::KeyRelease: {
+ static const int count = 1;
+ static const bool tryShortcutOverride = false;
+ QWindowSystemInterface::handleExtendedKeyEvent(window, timestamp,
+ type, key, modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers,
+ text, isRepeat, count, tryShortcutOverride);
+ // FIXME: Make handleExtendedKeyEvent synchronous
+ return QWindowSystemInterface::flushWindowSystemEvents();
+ }
+ default:
+ qCritical() << "KeyEvent can not send event type" << type;
+ return false;
+ }
+}
+
+QDebug operator<<(QDebug debug, const KeyEvent &e)
+{
+ QDebugStateSaver saver(debug);
+ debug.nospace().verbosity(0) << "KeyEvent("
+ << e.type << ", timestamp=" << e.timestamp
+ << ", key=" << e.key << ", modifiers=" << e.modifiers
+ << ", text="<< e.text << ", isRepeat=" << e.isRepeat
+ << ", nativeVirtualKey=" << e.nativeVirtualKey
+ << ", nativeModifiers=" << e.nativeModifiers
+ << ")";
+ return debug;
+}
diff --git a/src/plugins/platforms/cocoa/qnsview_mouse.mm b/src/plugins/platforms/cocoa/qnsview_mouse.mm
index f5896de99e..559257c4df 100644
--- a/src/plugins/platforms/cocoa/qnsview_mouse.mm
+++ b/src/plugins/platforms/cocoa/qnsview_mouse.mm
@@ -39,6 +39,36 @@
// This file is included from qnsview.mm, and only used to organize the code
+static const QPointingDevice *pointingDeviceFor(qint64 deviceID)
+{
+ // macOS will in many cases not report a deviceID (0 value).
+ // We can't pass this on directly, as the QInputDevicePrivate
+ // constructor will treat this as a request to assign a new Id.
+ // Instead we use the default Id of the primary pointing device.
+ static const int kDefaultPrimaryPointingDeviceId = 1;
+ if (!deviceID)
+ deviceID = kDefaultPrimaryPointingDeviceId;
+
+ if (const auto *device = QPointingDevicePrivate::pointingDeviceById(deviceID))
+ return device; // All good, already have the device registered
+
+ const auto *primaryDevice = QPointingDevice::primaryPointingDevice();
+ if (primaryDevice->systemId() == kDefaultPrimaryPointingDeviceId) {
+ // Adopt existing primary device instead of creating a new one
+ QPointingDevicePrivate::get(const_cast<QPointingDevice *>(primaryDevice))->systemId = deviceID;
+ qCDebug(lcInputDevices) << "primaryPointingDevice is now" << primaryDevice;
+ return primaryDevice;
+ } else {
+ // Register a new device. Name and capabilities may need updating later.
+ const auto *device = new QPointingDevice(QLatin1String("mouse"), deviceID,
+ QInputDevice::DeviceType::Mouse, QPointingDevice::PointerType::Generic,
+ QInputDevice::Capability::Scroll | QInputDevice::Capability::Position,
+ 1, 3, QString(), QPointingDeviceUniqueId(), QCocoaIntegration::instance());
+ QWindowSystemInterface::registerInputDevice(device);
+ return device;
+ }
+}
+
/*
The reason for using this helper is to ensure that QNSView doesn't implement
the NSResponder callbacks for mouseEntered, mouseExited, and mouseMoved.
@@ -93,11 +123,55 @@
- (void)resetMouseButtons
{
- qCDebug(lcQpaMouse) << "Reseting mouse buttons";
+ qCDebug(lcQpaMouse) << "Resetting mouse buttons";
m_buttons = Qt::NoButton;
m_frameStrutButtons = Qt::NoButton;
}
+- (void)handleMouseEvent:(NSEvent *)theEvent
+{
+ if (!m_platformWindow)
+ return;
+
+#ifndef QT_NO_TABLETEVENT
+ // Tablet events may come in via the mouse event handlers,
+ // check if this is a valid tablet event first.
+ if ([self handleTabletEvent: theEvent])
+ return;
+#endif
+
+ QPointF qtWindowPoint;
+ QPointF qtScreenPoint;
+ QNSView *targetView = self;
+ if (!targetView.platformWindow)
+ return;
+
+
+ [targetView convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&qtWindowPoint andScreenPoint:&qtScreenPoint];
+ ulong timestamp = [theEvent timestamp] * 1000;
+
+ QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag();
+ nativeDrag->setLastMouseEvent(theEvent, self);
+
+ const auto modifiers = QAppleKeyMapper::fromCocoaModifiers(theEvent.modifierFlags);
+ auto button = cocoaButton2QtButton(theEvent);
+ if (button == Qt::LeftButton && m_sendUpAsRightButton)
+ button = Qt::RightButton;
+ const auto eventType = cocoaEvent2QtMouseEvent(theEvent);
+
+ const QPointingDevice *device = pointingDeviceFor(theEvent.deviceID);
+ Q_ASSERT(device);
+
+ if (eventType == QEvent::MouseMove)
+ qCDebug(lcQpaMouse) << eventType << "at" << qtWindowPoint << "with" << m_buttons;
+ else
+ qCInfo(lcQpaMouse) << eventType << "of" << button << "at" << qtWindowPoint << "with" << m_buttons;
+
+ QWindowSystemInterface::handleMouseEvent(targetView->m_platformWindow->window(),
+ timestamp, qtWindowPoint, qtScreenPoint,
+ m_buttons, button, eventType, modifiers);
+}
+
- (void)handleFrameStrutMouseEvent:(NSEvent *)theEvent
{
if (!m_platformWindow)
@@ -187,36 +261,6 @@
}
@end
-static const QPointingDevice *pointingDeviceFor(qint64 deviceID)
-{
- // macOS will in many cases not report a deviceID (0 value).
- // We can't pass this on directly, as the QInputDevicePrivate
- // constructor will treat this as a request to assign a new Id.
- // Instead we use the default Id of the primary pointing device.
- static const int kDefaultPrimaryPointingDeviceId = 1;
- if (!deviceID)
- deviceID = kDefaultPrimaryPointingDeviceId;
-
- if (const auto *device = QPointingDevicePrivate::pointingDeviceById(deviceID))
- return device; // All good, already have the device registered
-
- const auto *primaryDevice = QPointingDevice::primaryPointingDevice();
- if (primaryDevice->systemId() == kDefaultPrimaryPointingDeviceId) {
- // Adopt existing primary device instead of creating a new one
- QPointingDevicePrivate::get(const_cast<QPointingDevice *>(primaryDevice))->systemId = deviceID;
- qCDebug(lcInputDevices) << "primaryPointingDevice is now" << primaryDevice;
- return primaryDevice;
- } else {
- // Register a new device. Name and capabilities may need updating later.
- const auto *device = new QPointingDevice(QLatin1String("mouse"), deviceID,
- QInputDevice::DeviceType::Mouse, QPointingDevice::PointerType::Generic,
- QInputDevice::Capability::Scroll | QInputDevice::Capability::Position,
- 1, 3, QString(), QPointingDeviceUniqueId(), QCocoaIntegration::instance());
- QWindowSystemInterface::registerInputDevice(device);
- return device;
- }
-}
-
@implementation QNSView (Mouse)
- (void)initMouse
@@ -234,8 +278,13 @@ static const QPointingDevice *pointingDeviceFor(qint64 deviceID)
m_mouseMoveHelper = [[QNSViewMouseMoveHelper alloc] initWithView:self];
- NSUInteger trackingOptions = NSTrackingActiveInActiveApp
- | NSTrackingMouseEnteredAndExited | NSTrackingCursorUpdate;
+ NSUInteger trackingOptions = NSTrackingCursorUpdate | NSTrackingMouseEnteredAndExited;
+
+ // Ideally we should have used NSTrackingActiveInActiveApp, but that
+ // fails when the application is deactivated from using e.g cmd+tab, and later
+ // reactivated again from a mouse click. So as a work-around we use NSTrackingActiveAlways
+ // instead, and simply ignore any related callbacks while the application is inactive.
+ trackingOptions |= NSTrackingActiveAlways;
// Ideally, NSTrackingMouseMoved should be turned on only if QWidget::mouseTracking
// is enabled, hover is on, or a tool tip is set. Unfortunately, Qt will send "tooltip"
@@ -284,59 +333,6 @@ static const QPointingDevice *pointingDeviceFor(qint64 deviceID)
return screenPoint;
}
-- (void)handleMouseEvent:(NSEvent *)theEvent
-{
- if (!m_platformWindow)
- return;
-
-#ifndef QT_NO_TABLETEVENT
- // Tablet events may come in via the mouse event handlers,
- // check if this is a valid tablet event first.
- if ([self handleTabletEvent: theEvent])
- return;
-#endif
-
- QPointF qtWindowPoint;
- QPointF qtScreenPoint;
- QNSView *targetView = self;
- if (!targetView.platformWindow)
- return;
-
- // Popups implicitly grap mouse events; forward to the active popup if there is one
- if (QCocoaWindow *popup = QCocoaIntegration::instance()->activePopupWindow()) {
- // Tooltips must be transparent for mouse events
- // The bug reference is QTBUG-46379
- if (!popup->window()->flags().testFlag(Qt::ToolTip)) {
- if (QNSView *popupView = qnsview_cast(popup->view()))
- targetView = popupView;
- }
- }
-
- [targetView convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&qtWindowPoint andScreenPoint:&qtScreenPoint];
- ulong timestamp = [theEvent timestamp] * 1000;
-
- QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag();
- nativeDrag->setLastMouseEvent(theEvent, self);
-
- const auto modifiers = QAppleKeyMapper::fromCocoaModifiers(theEvent.modifierFlags);
- auto button = cocoaButton2QtButton(theEvent);
- if (button == Qt::LeftButton && m_sendUpAsRightButton)
- button = Qt::RightButton;
- const auto eventType = cocoaEvent2QtMouseEvent(theEvent);
-
- const QPointingDevice *device = pointingDeviceFor(theEvent.deviceID);
- Q_ASSERT(device);
-
- if (eventType == QEvent::MouseMove)
- qCDebug(lcQpaMouse) << eventType << "at" << qtWindowPoint << "with" << m_buttons;
- else
- qCInfo(lcQpaMouse) << eventType << "of" << button << "at" << qtWindowPoint << "with" << m_buttons;
-
- QWindowSystemInterface::handleMouseEvent(targetView->m_platformWindow->window(),
- timestamp, qtWindowPoint, qtScreenPoint,
- m_buttons, button, eventType, modifiers);
-}
-
- (bool)handleMouseDownEvent:(NSEvent *)theEvent
{
if ([self isTransparentForUserInput])
@@ -350,7 +346,7 @@ static const QPointingDevice *pointingDeviceFor(qint64 deviceID)
Q_UNUSED(qtScreenPoint);
// Maintain masked state for the button for use by MouseDragged and MouseUp.
- QRegion mask = m_platformWindow->window()->mask();
+ QRegion mask = QHighDpi::toNativeLocalPosition(m_platformWindow->window()->mask(), m_platformWindow->window());
const bool masked = !mask.isEmpty() && !mask.contains(qtWindowPoint.toPoint());
if (masked)
m_acceptedMouseDowns &= ~button;
@@ -414,47 +410,12 @@ static const QPointingDevice *pointingDeviceFor(qint64 deviceID)
return [super mouseDown:theEvent];
m_sendUpAsRightButton = false;
- // Handle any active poup windows; clicking outisde them should close them
- // all. Don't do anything or clicks inside one of the menus, let Cocoa
- // handle that case. Note that in practice many windows of the Qt::Popup type
- // will actually close themselves in this case using logic implemented in
- // that particular poup type (for example context menus). However, Qt expects
- // that plain popup QWindows will also be closed, so we implement the logic
- // here as well.
- QList<QCocoaWindow *> *popups = QCocoaIntegration::instance()->popupWindowStack();
- if (!popups->isEmpty()) {
- // Check if the click is outside all popups.
- bool inside = false;
- QPointF qtScreenPoint = QCocoaScreen::mapFromNative([self screenMousePoint:theEvent]);
- for (QList<QCocoaWindow *>::const_iterator it = popups->begin(); it != popups->end(); ++it) {
- if ((*it)->geometry().contains(qtScreenPoint.toPoint())) {
- inside = true;
- break;
- }
- }
- // Close the popups if the click was outside.
- if (!inside) {
- bool selfClosed = false;
- Qt::WindowType type = QCocoaIntegration::instance()->activePopupWindow()->window()->type();
- while (QCocoaWindow *popup = QCocoaIntegration::instance()->popPopupWindow()) {
- selfClosed = self == popup->view();
- QWindowSystemInterface::handleCloseEvent<QWindowSystemInterface::SynchronousDelivery>(popup->window());
- if (!m_platformWindow)
- return; // Bail out if window was destroyed
- }
- // Consume the mouse event when closing the popup, except for tool tips
- // were it's expected that the event is processed normally.
- if (type != Qt::ToolTip || selfClosed)
- return;
- }
- }
-
QPointF qtWindowPoint;
QPointF qtScreenPoint;
[self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&qtWindowPoint andScreenPoint:&qtScreenPoint];
Q_UNUSED(qtScreenPoint);
- QRegion mask = m_platformWindow->window()->mask();
+ QRegion mask = QHighDpi::toNativeLocalPosition(m_platformWindow->window()->mask(), m_platformWindow->window());
const bool masked = !mask.isEmpty() && !mask.contains(qtWindowPoint.toPoint());
// Maintain masked state for the button for use by MouseDragged and Up.
if (masked)
@@ -468,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
@@ -543,6 +522,9 @@ static const QPointingDevice *pointingDeviceFor(qint64 deviceID)
// uses the legacy cursorRect API, so the cursor is reset to the arrow
// cursor. See rdar://34183708
+ if (!NSApp.active)
+ return;
+
auto previousCursor = NSCursor.currentCursor;
if (self.cursor)
@@ -559,29 +541,42 @@ static const QPointingDevice *pointingDeviceFor(qint64 deviceID)
if (!m_platformWindow)
return;
- if ([self isTransparentForUserInput])
- return;
-
- QPointF windowPoint;
- QPointF screenPoint;
- [self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&windowPoint andScreenPoint:&screenPoint];
- QWindow *childWindow = m_platformWindow->childWindowAt(windowPoint.toPoint());
-
// Top-level windows generate enter-leave events for sub-windows.
// Qt wants to know which window (if any) will be entered at the
// the time of the leave. This is dificult to accomplish by
// handling mouseEnter and mouseLeave envents, since they are sent
// individually to different views.
- if (m_platformWindow->isContentView() && childWindow) {
- if (childWindow != m_platformWindow->m_enterLeaveTargetWindow) {
- QWindowSystemInterface::handleEnterLeaveEvent(childWindow, m_platformWindow->m_enterLeaveTargetWindow, windowPoint, screenPoint);
- m_platformWindow->m_enterLeaveTargetWindow = childWindow;
+ QPointF windowPoint;
+ QPointF screenPoint;
+ QCocoaWindow *windowToLeave = nullptr;
+
+ if (m_platformWindow->isContentView()) {
+ [self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&windowPoint andScreenPoint:&screenPoint];
+ QWindow *childUnderMouse = m_platformWindow->childWindowAt(windowPoint.toPoint());
+ QCocoaWindow *childWindow = static_cast<QCocoaWindow *>(childUnderMouse->handle());
+ if (childWindow != QCocoaWindow::s_windowUnderMouse) {
+ if (QCocoaWindow::s_windowUnderMouse)
+ windowToLeave = QCocoaWindow::s_windowUnderMouse;
+ QCocoaWindow::s_windowUnderMouse = childWindow;
}
}
+ if (!NSApp.active)
+ return;
+
+ if ([self isTransparentForUserInput])
+ return;
+
+ if (windowToLeave) {
+ qCInfo(lcQpaMouse) << "Detected new window under mouse at" << windowPoint << "; sending"
+ << QEvent::Enter << QCocoaWindow::s_windowUnderMouse->window()
+ << QEvent::Leave << windowToLeave->window();
+ QWindowSystemInterface::handleEnterLeaveEvent(QCocoaWindow::s_windowUnderMouse->window(), windowToLeave->window(), windowPoint, screenPoint);
+ }
+
// Cocoa keeps firing mouse move events for obscured parent views. Qt should not
// send those events so filter them out here.
- if (childWindow != m_platformWindow->window())
+ if (m_platformWindow != QCocoaWindow::s_windowUnderMouse)
return;
[self handleMouseEvent: theEvent];
@@ -593,10 +588,22 @@ static const QPointingDevice *pointingDeviceFor(qint64 deviceID)
if (!m_platformWindow)
return;
- m_platformWindow->m_windowUnderMouse = true;
-
- if ([self isTransparentForUserInput])
- return;
+ // We send out enter and leave events mainly from mouse move events (mouseMovedImpl).
+ // Therefore, in most cases, we should not send out enter/leave events from here, as
+ // this results in duplicated enter/leave events being delivered.
+ // This is especially important when working with NSTrackingArea, since AppKit documents that
+ // the order of enter/exit events when several NSTrackingAreas are in use is not guaranteed.
+ // So if we just forwarded enter/leave events from NSTrackingArea directly, it would not only
+ // result in duplicated events, but also sometimes events that would be out of sync.
+ // But not all enter events can be resolved from mouse move events. E.g if a window is raised
+ // in front of the mouse, or if the application is activated while the mouse is on top of a
+ // window, we need to send out enter events for those cases as well. And we do so from this
+ // function to support the former case. But only when we receive an enter event for the
+ // top-level window, when no child QWindows are being hovered from before.
+ // Since QWSI expects us to send both the window entered, and the window left, in the same
+ // callback, we manually keep track of which child QWindow is under the mouse at any point
+ // in time (s_windowUnderMouse). The latter is also used to also send out enter/leave
+ // events when the application is activated/deactivated.
// Top-level windows generate enter events for sub-windows.
if (!m_platformWindow->isContentView())
@@ -605,10 +612,18 @@ static const QPointingDevice *pointingDeviceFor(qint64 deviceID)
QPointF windowPoint;
QPointF screenPoint;
[self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&windowPoint andScreenPoint:&screenPoint];
- m_platformWindow->m_enterLeaveTargetWindow = m_platformWindow->childWindowAt(windowPoint.toPoint());
+ QWindow *childUnderMouse = m_platformWindow->childWindowAt(windowPoint.toPoint());
+ QCocoaWindow::s_windowUnderMouse = static_cast<QCocoaWindow *>(childUnderMouse->handle());
+
+ if ([self isTransparentForUserInput])
+ return;
+
+ if (!NSApp.active)
+ return;
- qCInfo(lcQpaMouse) << QEvent::Enter << self << "at" << windowPoint << "with" << currentlyPressedMouseButtons();
- QWindowSystemInterface::handleEnterEvent(m_platformWindow->m_enterLeaveTargetWindow, windowPoint, screenPoint);
+ qCInfo(lcQpaMouse) << "Mouse entered" << self << "at" << windowPoint << "with" << currentlyPressedMouseButtons()
+ << "; sending" << QEvent::Enter << "to" << QCocoaWindow::s_windowUnderMouse->window();
+ QWindowSystemInterface::handleEnterEvent(QCocoaWindow::s_windowUnderMouse->window(), windowPoint, screenPoint);
}
- (void)mouseExitedImpl:(NSEvent *)theEvent
@@ -617,18 +632,24 @@ static const QPointingDevice *pointingDeviceFor(qint64 deviceID)
if (!m_platformWindow)
return;
- m_platformWindow->m_windowUnderMouse = false;
+ // Top-level windows generate leave events for sub-windows.
+ if (!m_platformWindow->isContentView())
+ return;
+
+ QCocoaWindow *windowToLeave = QCocoaWindow::s_windowUnderMouse;
+ QCocoaWindow::s_windowUnderMouse = nullptr;
if ([self isTransparentForUserInput])
return;
- // Top-level windows generate leave events for sub-windows.
- if (!m_platformWindow->isContentView())
+ if (!NSApp.active)
+ return;
+
+ if (!windowToLeave)
return;
- qCInfo(lcQpaMouse) << QEvent::Leave << self;
- QWindowSystemInterface::handleLeaveEvent(m_platformWindow->m_enterLeaveTargetWindow);
- m_platformWindow->m_enterLeaveTargetWindow = 0;
+ qCInfo(lcQpaMouse) << "Mouse left" << self << "; sending" << QEvent::Leave << "to" << windowToLeave->window();
+ QWindowSystemInterface::handleLeaveEvent(windowToLeave->window());
}
#if QT_CONFIG(wheelevent)
@@ -736,6 +757,7 @@ static const QPointingDevice *pointingDeviceFor(qint64 deviceID)
auto *devicePriv = QPointingDevicePrivate::get(const_cast<QPointingDevice *>(device));
if (!devicePriv->capabilities.testFlag(QInputDevice::Capability::PixelScroll)) {
devicePriv->name = QLatin1String("trackpad or magic mouse");
+ devicePriv->deviceType = QInputDevice::DeviceType::TouchPad;
devicePriv->capabilities |= QInputDevice::Capability::PixelScroll;
qCDebug(lcInputDevices) << "mouse scrolling: updated capabilities" << device;
}
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/qnswindow.mm b/src/plugins/platforms/cocoa/qnswindow.mm
index d9158bbcae..7c9e0dce2d 100644
--- a/src/plugins/platforms/cocoa/qnswindow.mm
+++ b/src/plugins/platforms/cocoa/qnswindow.mm
@@ -45,6 +45,7 @@
#include "qcocoawindow.h"
#include "qcocoahelpers.h"
#include "qcocoaeventdispatcher.h"
+#include "qcocoaintegration.h"
#include <qpa/qwindowsysteminterface.h>
#include <qoperatingsystemversion.h>
@@ -106,7 +107,7 @@ static bool isMouseEvent(NSEvent *ev)
// Unfortunately there's no NSWindowListOrderedBackToFront,
// so we have to manually reverse the order using an array.
- NSMutableArray<NSWindow *> *windows = [NSMutableArray<NSWindow *> new];
+ NSMutableArray<NSWindow *> *windows = [[NSMutableArray<NSWindow *> new] autorelease];
[application enumerateWindowsWithOptions:NSWindowListOrderedFrontToBack
usingBlock:^(NSWindow *window, BOOL *) {
// For some reason AppKit will give us nil-windows, skip those
@@ -248,6 +249,7 @@ OSStatus CGSClearWindowTags(const CGSConnectionID, const CGSWindowID, int *, int
{
// Member variables
QPointer<QCocoaWindow> m_platformWindow;
+ bool m_isMinimizing;
}
- (instancetype)initWithContentRect:(NSRect)contentRect styleMask:(NSWindowStyleMask)style
@@ -259,6 +261,8 @@ OSStatus CGSClearWindowTags(const CGSConnectionID, const CGSWindowID, int *, int
// we can properly reflect the window's state during initialization.
m_platformWindow = window;
+ m_isMinimizing = false;
+
return [super initWithContentRect:contentRect styleMask:style backing:backingStoreType defer:defer screen:screen];
}
@@ -323,8 +327,18 @@ OSStatus CGSClearWindowTags(const CGSConnectionID, const CGSWindowID, int *, int
- (NSColor *)backgroundColor
{
- return self.styleMask == NSWindowStyleMaskBorderless ?
- [NSColor clearColor] : [super backgroundColor];
+ // FIXME: Plumb to a WA_NoSystemBackground-like window flag,
+ // or a QWindow::backgroundColor() property. In the meantime
+ // we assume that if you have translucent content, without a
+ // frame then you intend to do all background drawing yourself.
+ const QWindow *window = m_platformWindow ? m_platformWindow->window() : nullptr;
+ if (!self.opaque && window && window->flags().testFlag(Qt::FramelessWindowHint))
+ return [NSColor clearColor];
+
+ // This still allows you to have translucent content with a frame,
+ // where the system background (or color set via NSWindow) will
+ // shine through.
+ return [super backgroundColor];
}
- (void)sendEvent:(NSEvent*)theEvent
@@ -349,18 +363,64 @@ OSStatus CGSClearWindowTags(const CGSConnectionID, const CGSWindowID, int *, int
return;
}
+ const bool mouseEventInFrameStrut = [theEvent, self]{
+ if (isMouseEvent(theEvent)) {
+ const NSPoint loc = theEvent.locationInWindow;
+ const NSRect windowFrame = [self convertRectFromScreen:self.frame];
+ const NSRect contentFrame = self.contentView.frame;
+ if (NSMouseInRect(loc, windowFrame, NO) && !NSMouseInRect(loc, contentFrame, NO))
+ return true;
+ }
+ return false;
+ }();
+ // Any mouse-press in the frame of the window, including the title bar buttons, should
+ // close open popups. Presses within the window's content are handled to do that in the
+ // NSView::mouseDown implementation.
+ if (theEvent.type == NSEventTypeLeftMouseDown && mouseEventInFrameStrut)
+ QGuiApplicationPrivate::instance()->closeAllPopups();
+
[super sendEvent:theEvent];
if (!m_platformWindow)
return; // Platform window went away while processing event
- if (m_platformWindow->frameStrutEventsEnabled() && isMouseEvent(theEvent)) {
- NSPoint loc = [theEvent locationInWindow];
- NSRect windowFrame = [self convertRectFromScreen:self.frame];
- NSRect contentFrame = self.contentView.frame;
- if (NSMouseInRect(loc, windowFrame, NO) && !NSMouseInRect(loc, contentFrame, NO))
- [qnsview_cast(m_platformWindow->view()) handleFrameStrutMouseEvent:theEvent];
+ // Cocoa will not deliver mouse events to a window that is modally blocked (by Cocoa,
+ // not Qt). However, an active popup is expected to grab any mouse event within the
+ // application, so we need to handle those explicitly and trust Qt's isWindowBlocked
+ // implementation to eat events that shouldn't be delivered anyway.
+ if (isMouseEvent(theEvent) && QGuiApplicationPrivate::instance()->popupActive()
+ && QGuiApplicationPrivate::instance()->isWindowBlocked(m_platformWindow->window(), nullptr)) {
+ qCDebug(lcQpaWindow) << "Mouse event over modally blocked window" << m_platformWindow->window()
+ << "while popup is open - redirecting";
+ [qnsview_cast(m_platformWindow->view()) handleMouseEvent:theEvent];
}
+ if (m_platformWindow->frameStrutEventsEnabled() && mouseEventInFrameStrut)
+ [qnsview_cast(m_platformWindow->view()) handleFrameStrutMouseEvent:theEvent];
+}
+
+- (void)miniaturize:(id)sender
+{
+ QBoolBlocker miniaturizeTracker(m_isMinimizing, true);
+ [super miniaturize:sender];
+}
+
+- (NSButton *)standardWindowButton:(NSWindowButton)buttonType
+{
+ NSButton *button = [super standardWindowButton:buttonType];
+
+ // When an NSWindow is asked to minimize it will check the
+ // NSWindowMiniaturizeButton for enablement before continuing,
+ // even if the style mask includes NSWindowStyleMaskMiniaturizable.
+ // To ensure that a window can be minimized, even when the
+ // minimize button has been disabled in response to the user
+ // setting CustomizeWindowHint, we temporarily return a default
+ // minimize-button that we haven't modified in updateTitleBarButtons.
+ // This ensures the window can be minimized, without visually
+ // toggling the actual minimize-button on and off.
+ if (buttonType == NSWindowMiniaturizeButton && m_isMinimizing && !button.enabled)
+ return [NSWindow standardWindowButton:buttonType forStyleMask:self.styleMask];
+
+ return button;
}
- (void)closeAndRelease
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 a3ff060e2d..e87d2886d8 100644
--- a/src/plugins/platforms/direct2d/CMakeLists.txt
+++ b/src/plugins/platforms/direct2d/CMakeLists.txt
@@ -6,7 +6,7 @@
qt_internal_add_plugin(QWindowsDirect2DIntegrationPlugin
OUTPUT_NAME qdirect2d
- TYPE platforms
+ PLUGIN_TYPE platforms
SOURCES
../windows/qtwindowsglobal.h
../windows/qwin10helpers.cpp ../windows/qwin10helpers.h
diff --git a/src/plugins/platforms/directfb/CMakeLists.txt b/src/plugins/platforms/directfb/CMakeLists.txt
index e54c6a0b79..c634afb4f8 100644
--- a/src/plugins/platforms/directfb/CMakeLists.txt
+++ b/src/plugins/platforms/directfb/CMakeLists.txt
@@ -11,7 +11,7 @@ qt_find_package(EGL)
qt_internal_add_plugin(QDirectFbIntegrationPlugin
OUTPUT_NAME qdirectfb
- TYPE platforms
+ PLUGIN_TYPE platforms
SOURCES
main.cpp
qdirectfbbackingstore.cpp qdirectfbbackingstore.h
diff --git a/src/plugins/platforms/eglfs/CMakeLists.txt b/src/plugins/platforms/eglfs/CMakeLists.txt
index 4b11c85bad..61adb94778 100644
--- a/src/plugins/platforms/eglfs/CMakeLists.txt
+++ b/src/plugins/platforms/eglfs/CMakeLists.txt
@@ -98,7 +98,7 @@ endif()
qt_internal_add_plugin(QEglFSIntegrationPlugin
OUTPUT_NAME qeglfs
- TYPE platforms
+ PLUGIN_TYPE platforms
DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES eglfs # special case
SOURCES
qeglfsmain.cpp
diff --git a/src/plugins/platforms/eglfs/api/qeglfswindow.cpp b/src/plugins/platforms/eglfs/api/qeglfswindow.cpp
index 2ee237ec28..b1fdba5a46 100644
--- a/src/plugins/platforms/eglfs/api/qeglfswindow.cpp
+++ b/src/plugins/platforms/eglfs/api/qeglfswindow.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
@@ -111,11 +111,11 @@ void QEglFSWindow::create()
QOpenGLCompositor *compositor = QOpenGLCompositor::instance();
if (screen->primarySurface() != EGL_NO_SURFACE) {
if (Q_UNLIKELY(!isRaster() || !compositor->targetWindow())) {
-#if !defined(Q_OS_ANDROID) || defined(Q_OS_ANDROID_EMBEDDED)
+# ifndef Q_OS_ANDROID
// We can have either a single OpenGL window or multiple raster windows.
// Other combinations cannot work.
qFatal("EGLFS: OpenGL windows cannot be mixed with others.");
-#endif
+# endif
return;
}
m_format = compositor->targetWindow()->format();
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/CMakeLists.txt b/src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/CMakeLists.txt
index d496e334b3..e1de63ea42 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/CMakeLists.txt
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_emu/CMakeLists.txt
@@ -6,7 +6,7 @@
qt_internal_add_plugin(QEglFSEmulatorIntegrationPlugin
OUTPUT_NAME qeglfs-emu-integration
- TYPE egldeviceintegrations
+ PLUGIN_TYPE egldeviceintegrations
SOURCES
qeglfsemulatorintegration.cpp qeglfsemulatorintegration.h
qeglfsemulatorscreen.cpp qeglfsemulatorscreen.h
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/CMakeLists.txt b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/CMakeLists.txt
index f08b960eb0..a20a4a084d 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/CMakeLists.txt
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/CMakeLists.txt
@@ -30,7 +30,7 @@ qt_internal_add_module(EglFsKmsGbmSupportPrivate
qt_internal_add_plugin(QEglFSKmsGbmIntegrationPlugin
OUTPUT_NAME qeglfs-kms-integration
- TYPE egldeviceintegrations
+ PLUGIN_TYPE egldeviceintegrations
SOURCES
qeglfskmsgbmmain.cpp
DEFINES
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/CMakeLists.txt b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/CMakeLists.txt
index bc033c9f05..7cac8ea5dc 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/CMakeLists.txt
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice/CMakeLists.txt
@@ -6,7 +6,7 @@
qt_internal_add_plugin(QEglFSKmsEglDeviceIntegrationPlugin
OUTPUT_NAME qeglfs-kms-egldevice-integration
- TYPE egldeviceintegrations
+ PLUGIN_TYPE egldeviceintegrations
SOURCES
qeglfskmsegldevice.cpp qeglfskmsegldevice.h
qeglfskmsegldeviceintegration.cpp qeglfskmsegldeviceintegration.h
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_mali/CMakeLists.txt b/src/plugins/platforms/eglfs/deviceintegration/eglfs_mali/CMakeLists.txt
index bb0e7619fc..7550529f79 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_mali/CMakeLists.txt
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_mali/CMakeLists.txt
@@ -6,7 +6,7 @@
qt_internal_add_plugin(QEglFSMaliIntegrationPlugin
OUTPUT_NAME qeglfs-mali-integration
- TYPE egldeviceintegrations
+ PLUGIN_TYPE egldeviceintegrations
SOURCES
qeglfsmaliintegration.cpp qeglfsmaliintegration.h
qeglfsmalimain.cpp
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_openwfd/CMakeLists.txt b/src/plugins/platforms/eglfs/deviceintegration/eglfs_openwfd/CMakeLists.txt
index 45c2a0ca4e..fb8b79454d 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_openwfd/CMakeLists.txt
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_openwfd/CMakeLists.txt
@@ -6,7 +6,7 @@
qt_internal_add_plugin(QEglFSOpenWFDIntegrationPlugin
OUTPUT_NAME qeglfs-openwfd-integration
- TYPE egldeviceintegrations
+ PLUGIN_TYPE egldeviceintegrations
SOURCES
qeglfsopenwfdintegration.cpp qeglfsopenwfdintegration.h
qeglfsopenwfdmain.cpp
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv/CMakeLists.txt b/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv/CMakeLists.txt
index 91195f2b63..988ab76d79 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv/CMakeLists.txt
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv/CMakeLists.txt
@@ -6,7 +6,7 @@
qt_internal_add_plugin(QEglFSVivIntegrationPlugin
OUTPUT_NAME qeglfs-viv-integration
- TYPE egldeviceintegrations
+ PLUGIN_TYPE egldeviceintegrations
SOURCES
qeglfsvivintegration.cpp qeglfsvivintegration.h
qeglfsvivmain.cpp
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/CMakeLists.txt b/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/CMakeLists.txt
index 118a805ad7..cb07c9aecc 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/CMakeLists.txt
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/CMakeLists.txt
@@ -6,7 +6,7 @@
qt_internal_add_plugin(QEglFSVivWaylandIntegrationPlugin
OUTPUT_NAME qeglfs-viv-wl-integration
- TYPE egldeviceintegrations
+ PLUGIN_TYPE egldeviceintegrations
SOURCES
qeglfsvivwlintegration.cpp qeglfsvivwlintegration.h
qeglfsvivwlmain.cpp
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/CMakeLists.txt b/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/CMakeLists.txt
index 24190cd811..18bb02efe0 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/CMakeLists.txt
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/CMakeLists.txt
@@ -6,7 +6,7 @@
qt_internal_add_plugin(QEglFSX11IntegrationPlugin
OUTPUT_NAME qeglfs-x11-integration
- TYPE egldeviceintegrations
+ PLUGIN_TYPE egldeviceintegrations
SOURCES
qeglfsx11integration.cpp qeglfsx11integration.h
qeglfsx11main.cpp
diff --git a/src/plugins/platforms/ios/CMakeLists.txt b/src/plugins/platforms/ios/CMakeLists.txt
index cebecadc3c..204add279c 100644
--- a/src/plugins/platforms/ios/CMakeLists.txt
+++ b/src/plugins/platforms/ios/CMakeLists.txt
@@ -7,7 +7,7 @@
qt_internal_add_plugin(QIOSIntegrationPlugin
OUTPUT_NAME qios
DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES ios # special case
- TYPE platforms
+ PLUGIN_TYPE platforms
SOURCES
plugin.mm
qiosapplicationdelegate.h qiosapplicationdelegate.mm
@@ -27,6 +27,7 @@ qt_internal_add_plugin(QIOSIntegrationPlugin
qioswindow.h qioswindow.mm
quiaccessibilityelement.h quiaccessibilityelement.mm
quiview.h quiview.mm
+ uistrings_p.h uistrings.cpp
LIBRARIES
${FWAudioToolbox}
${FWFoundation}
diff --git a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/CMakeLists.txt b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/CMakeLists.txt
index 76253adb47..2fceac77ab 100644
--- a/src/plugins/platforms/ios/optional/nsphotolibrarysupport/CMakeLists.txt
+++ b/src/plugins/platforms/ios/optional/nsphotolibrarysupport/CMakeLists.txt
@@ -4,9 +4,10 @@
## QIosOptionalPlugin_NSPhotoLibrary Plugin:
#####################################################################
-qt_internal_add_plugin(QIosOptionalPlugin_NSPhotoLibrary
+qt_internal_add_plugin(QIosOptionalPlugin_NSPhotoLibraryPlugin
OUTPUT_NAME qiosnsphotolibrarysupport
- TYPE platforms/darwin
+ PLUGIN_TYPE platforms/darwin
+ CLASS_NAME QIosOptionalPlugin_NSPhotoLibrary
DEFAULT_IF FALSE
SOURCES
plugin.mm
@@ -23,7 +24,7 @@ qt_internal_add_plugin(QIosOptionalPlugin_NSPhotoLibrary
)
# special case begin
-set_target_properties(QIosOptionalPlugin_NSPhotoLibrary
+set_target_properties(QIosOptionalPlugin_NSPhotoLibraryPlugin
PROPERTIES
DISABLE_PRECOMPILE_HEADERS ON
)
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/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/qiosservices.mm b/src/plugins/platforms/ios/qiosservices.mm
index 7222bf6793..41c75dcac8 100644
--- a/src/plugins/platforms/ios/qiosservices.mm
+++ b/src/plugins/platforms/ios/qiosservices.mm
@@ -42,6 +42,7 @@
#include <QtCore/qurl.h>
#include <QtCore/qdebug.h>
#include <QtCore/private/qcore_mac_p.h>
+#include <QtCore/qscopedvaluerollback.h>
#include <QtGui/qdesktopservices.h>
@@ -56,6 +57,7 @@ bool QIOSServices::openUrl(const QUrl &url)
return false;
}
+ // avoid recursing back into self
if (url == m_handlingUrl)
return false;
@@ -94,16 +96,12 @@ bool QIOSServices::openDocument(const QUrl &url)
/* Callback from iOS that the application should handle a URL */
bool QIOSServices::handleUrl(const QUrl &url)
{
- QUrl previouslyHandling = m_handlingUrl;
- m_handlingUrl = url;
+ QScopedValueRollback<QUrl> rollback(m_handlingUrl, url);
// FIXME: Add platform services callback from QDesktopServices::setUrlHandler
// so that we can warn the user if calling setUrlHandler without also setting
// up the matching keys in the Info.plist file (CFBundleURLTypes and friends).
- bool couldHandle = QDesktopServices::openUrl(url);
-
- m_handlingUrl = previouslyHandling;
- return couldHandle;
+ return QDesktopServices::openUrl(url);
}
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/ios/quiaccessibilityelement.mm b/src/plugins/platforms/ios/quiaccessibilityelement.mm
index 4dd1f9f035..b6c8ca0620 100644
--- a/src/plugins/platforms/ios/quiaccessibilityelement.mm
+++ b/src/plugins/platforms/ios/quiaccessibilityelement.mm
@@ -43,6 +43,7 @@
#include "private/qaccessiblecache_p.h"
#include "private/qcore_mac_p.h"
+#include "uistrings_p.h"
QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement);
@@ -117,7 +118,9 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAccessibilityElement);
QAccessible::State state = iface->state();
if (state.checkable)
- return state.checked ? @"checked" : @"unchecked"; // FIXME: translation
+ return state.checked
+ ? QCoreApplication::translate(ACCESSIBILITY_ELEMENT, AE_CHECKED).toNSString()
+ : QCoreApplication::translate(ACCESSIBILITY_ELEMENT, AE_UNCHECKED).toNSString();
QAccessibleValueInterface *val = iface->valueInterface();
if (val) {
diff --git a/src/plugins/platforms/ios/quiview.mm b/src/plugins/platforms/ios/quiview.mm
index c7f94cc707..ef6c9f49ad 100644
--- a/src/plugins/platforms/ios/quiview.mm
+++ b/src/plugins/platforms/ios/quiview.mm
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
@@ -50,6 +50,7 @@
#include "qiosmenu.h"
#endif
+#include <QtCore/qmath.h>
#include <QtGui/qpointingdevice.h>
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/private/qwindow_p.h>
@@ -388,10 +389,10 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet")
// azimuth unit vector: +x to the right, +y going downwards
CGVector azimuth = [cTouch azimuthUnitVectorInView: self];
// azimuthAngle given in radians, zero when the stylus points towards +x axis; converted to degrees with 0 pointing straight up
- qreal azimuthAngle = [cTouch azimuthAngleInView: self] * 180 / M_PI + 90;
+ qreal azimuthAngle = qRadiansToDegrees([cTouch azimuthAngleInView: self]) + 90;
// altitudeAngle given in radians, pi / 2 is with the stylus perpendicular to the iPad, smaller values mean more tilted, but never negative.
// Convert to degrees with zero being perpendicular.
- qreal altitudeAngle = 90 - cTouch.altitudeAngle * 180 / M_PI;
+ qreal altitudeAngle = 90 - qRadiansToDegrees(cTouch.altitudeAngle);
qCDebug(lcQpaTablet) << i << ":" << timeStamp << localViewPosition << pressure << state << "azimuth" << azimuth.dx << azimuth.dy
<< "angle" << azimuthAngle << "altitude" << cTouch.altitudeAngle
<< "xTilt" << qBound(-60.0, altitudeAngle * azimuth.dx, 60.0) << "yTilt" << qBound(-60.0, altitudeAngle * azimuth.dy, 60.0);
diff --git a/src/plugins/platforms/ios/uistrings.cpp b/src/plugins/platforms/ios/uistrings.cpp
new file mode 100644
index 0000000000..2772d28bc8
--- /dev/null
+++ b/src/plugins/platforms/ios/uistrings.cpp
@@ -0,0 +1,51 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "uistrings_p.h"
+
+// Translatable messages should go into this .cpp file for them to
+// be picked up by lupdate.
+
+QT_BEGIN_NAMESPACE
+
+const char ACCESSIBILITY_ELEMENT[] = "quiaccessibilityelement";
+const char AE_CHECKED[] = QT_TRANSLATE_NOOP("quiaccessibilityelement", "checked");
+const char AE_UNCHECKED[] = QT_TRANSLATE_NOOP("quiaccessibilityelement", "unchecked");
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/ios/uistrings_p.h b/src/plugins/platforms/ios/uistrings_p.h
new file mode 100644
index 0000000000..e77afb2740
--- /dev/null
+++ b/src/plugins/platforms/ios/uistrings_p.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef UISTRINGS_P_H
+#define UISTRINGS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QString>
+
+QT_BEGIN_NAMESPACE
+
+// quiaccessibilityelement related strings
+extern const char ACCESSIBILITY_ELEMENT[];
+extern const char AE_CHECKED[];
+extern const char AE_UNCHECKED[];
+
+QT_END_NAMESPACE
+
+#endif // UISTRINGS_P_H
diff --git a/src/plugins/platforms/linuxfb/CMakeLists.txt b/src/plugins/platforms/linuxfb/CMakeLists.txt
index 1a241463a7..a671dcfc1e 100644
--- a/src/plugins/platforms/linuxfb/CMakeLists.txt
+++ b/src/plugins/platforms/linuxfb/CMakeLists.txt
@@ -6,7 +6,7 @@
qt_internal_add_plugin(QLinuxFbIntegrationPlugin
OUTPUT_NAME qlinuxfb
- TYPE platforms
+ PLUGIN_TYPE platforms
DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES linuxfb # special case
SOURCES
main.cpp
diff --git a/src/plugins/platforms/minimal/CMakeLists.txt b/src/plugins/platforms/minimal/CMakeLists.txt
index 3abecdcb4a..a763dbac9a 100644
--- a/src/plugins/platforms/minimal/CMakeLists.txt
+++ b/src/plugins/platforms/minimal/CMakeLists.txt
@@ -8,7 +8,7 @@ qt_find_package(WrapFreetype PROVIDED_TARGETS WrapFreetype::WrapFreetype) # spec
qt_internal_add_plugin(QMinimalIntegrationPlugin
OUTPUT_NAME qminimal
- TYPE platforms
+ PLUGIN_TYPE platforms
DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES minimal # special case
SOURCES
main.cpp
diff --git a/src/plugins/platforms/minimalegl/CMakeLists.txt b/src/plugins/platforms/minimalegl/CMakeLists.txt
index 5a5b57f963..f285d8ece6 100644
--- a/src/plugins/platforms/minimalegl/CMakeLists.txt
+++ b/src/plugins/platforms/minimalegl/CMakeLists.txt
@@ -7,7 +7,7 @@ qt_find_package(EGL) # special case
qt_internal_add_plugin(QMinimalEglIntegrationPlugin
OUTPUT_NAME qminimalegl
- TYPE platforms
+ PLUGIN_TYPE platforms
DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES minimalegl # special case
SOURCES
main.cpp
diff --git a/src/plugins/platforms/offscreen/CMakeLists.txt b/src/plugins/platforms/offscreen/CMakeLists.txt
index bb73ce38aa..424da0a980 100644
--- a/src/plugins/platforms/offscreen/CMakeLists.txt
+++ b/src/plugins/platforms/offscreen/CMakeLists.txt
@@ -6,7 +6,7 @@
qt_internal_add_plugin(QOffscreenIntegrationPlugin
OUTPUT_NAME qoffscreen
- TYPE platforms
+ PLUGIN_TYPE platforms
DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES offscreen # special case
SOURCES
main.cpp
diff --git a/src/plugins/platforms/offscreen/qoffscreencommon.cpp b/src/plugins/platforms/offscreen/qoffscreencommon.cpp
index 33b98cfa1b..f485b4eed8 100644
--- a/src/plugins/platforms/offscreen/qoffscreencommon.cpp
+++ b/src/plugins/platforms/offscreen/qoffscreencommon.cpp
@@ -55,7 +55,11 @@ QPlatformWindow *QOffscreenScreen::windowContainingCursor = nullptr;
QList<QPlatformScreen *> QOffscreenScreen::virtualSiblings() const
{
- return m_integration->screens();
+ QList<QPlatformScreen *> platformScreens;
+ for (auto screen : m_integration->screens()) {
+ platformScreens.append(screen);
+ }
+ return platformScreens;
}
class QOffscreenCursor : public QPlatformCursor
@@ -232,6 +236,38 @@ void QOffscreenBackingStore::clearHash()
QHash<WId, QOffscreenBackingStore *> QOffscreenBackingStore::m_backingStoreForWinIdHash;
+QOffscreenPlatformNativeInterface::QOffscreenPlatformNativeInterface(QOffscreenIntegration *integration)
+ : m_integration(integration)
+{
+
+}
+
QOffscreenPlatformNativeInterface::~QOffscreenPlatformNativeInterface() = default;
+/*
+ Set platform configuration, e.g. screen configuration
+*/
+void QOffscreenPlatformNativeInterface::setConfiguration(const QJsonObject &configuration, QOffscreenPlatformNativeInterface *iface)
+{
+ iface->m_integration->setConfiguration(configuration);
+}
+
+/*
+ Get the current platform configuration
+*/
+QJsonObject QOffscreenPlatformNativeInterface::configuration(QOffscreenPlatformNativeInterface *iface)
+{
+ return iface->m_integration->configuration();
+}
+
+void *QOffscreenPlatformNativeInterface::nativeResourceForIntegration(const QByteArray &resource)
+{
+ if (resource == "setConfiguration")
+ return reinterpret_cast<void*>(&QOffscreenPlatformNativeInterface::setConfiguration);
+ else if (resource == "configuration")
+ return reinterpret_cast<void*>(&QOffscreenPlatformNativeInterface::configuration);
+ else
+ return nullptr;
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/offscreen/qoffscreencommon.h b/src/plugins/platforms/offscreen/qoffscreencommon.h
index 7f92c5b4d9..d0edfcc48b 100644
--- a/src/plugins/platforms/offscreen/qoffscreencommon.h
+++ b/src/plugins/platforms/offscreen/qoffscreencommon.h
@@ -51,6 +51,7 @@
#include <qscopedpointer.h>
#include <qimage.h>
+#include <qjsonobject.h>
#include <qhash.h>
QT_BEGIN_NAMESPACE
@@ -120,7 +121,15 @@ private:
class QOffscreenPlatformNativeInterface : public QPlatformNativeInterface
{
public:
+ QOffscreenPlatformNativeInterface(QOffscreenIntegration *integration);
~QOffscreenPlatformNativeInterface();
+
+ static void setConfiguration(const QJsonObject &configuration, QOffscreenPlatformNativeInterface *iface);
+ static QJsonObject configuration(QOffscreenPlatformNativeInterface *iface);
+
+ void *nativeResourceForIntegration(const QByteArray &resource) override;
+private:
+ QOffscreenIntegration *m_integration;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/offscreen/qoffscreenintegration.cpp b/src/plugins/platforms/offscreen/qoffscreenintegration.cpp
index 310e71ae13..2613a42755 100644
--- a/src/plugins/platforms/offscreen/qoffscreenintegration.cpp
+++ b/src/plugins/platforms/offscreen/qoffscreenintegration.cpp
@@ -93,7 +93,7 @@ public:
}
};
-QOffscreenIntegration::QOffscreenIntegration()
+QOffscreenIntegration::QOffscreenIntegration(const QStringList& paramList)
{
#if defined(Q_OS_UNIX)
#if defined(Q_OS_MAC)
@@ -109,6 +109,9 @@ QOffscreenIntegration::QOffscreenIntegration()
m_drag.reset(new QOffscreenDrag);
#endif
m_services.reset(new QPlatformServices);
+
+ QJsonObject config = resolveConfigFileConfiguration(paramList).value_or(defaultConfiguration());
+ setConfiguration(config);
}
QOffscreenIntegration::~QOffscreenIntegration()
@@ -118,8 +121,12 @@ QOffscreenIntegration::~QOffscreenIntegration()
}
/*
- The offscren platform plugin is configurable with a JSON configuration
- file. Write the config to disk and pass the file path as a platform argument:
+ The offscren platform plugin is configurable with a JSON configuration.
+ The confiuration can be provided either from a file on disk on startup,
+ or at by calling setConfiguration().
+
+ To provide a configuration on startuip, write the config to disk and pass
+ the file path as a platform argument:
./myapp -platform offscreen:configfile=/path/to/config.json
@@ -130,9 +137,9 @@ QOffscreenIntegration::~QOffscreenIntegration()
"screens": [<screens>],
}
- Screen:
+ "screens" is an array of:
{
- "name" : string,
+ "name": string,
"x": int,
"y": int,
"width": int,
@@ -142,9 +149,29 @@ QOffscreenIntegration::~QOffscreenIntegration()
"dpr": double,
}
*/
-void QOffscreenIntegration::configure(const QStringList& paramList)
+
+QJsonObject QOffscreenIntegration::defaultConfiguration() const
+{
+ const auto defaultScreen = QJsonObject {
+ {"name", ""},
+ {"x", 0},
+ {"y", 0},
+ {"width", 800},
+ {"height", 800},
+ {"logicalDpi", 96},
+ {"logicalBaseDpi", 96},
+ {"dpr", 1.0},
+ };
+ const auto defaultConfiguration = QJsonObject {
+ {"synchronousWindowSystemEvents", false},
+ {"windowFrameMargins", true},
+ {"screens", QJsonArray { defaultScreen } },
+ };
+ return defaultConfiguration;
+}
+
+std::optional<QJsonObject> QOffscreenIntegration::resolveConfigFileConfiguration(const QStringList& paramList) const
{
- // Use config file configuring platform plugin, if one was specified
bool hasConfigFile = false;
QString configFilePath;
for (const QString &param : paramList) {
@@ -152,17 +179,11 @@ void QOffscreenIntegration::configure(const QStringList& paramList)
QString configPrefix(QLatin1String("configfile="));
if (param.startsWith(configPrefix)) {
hasConfigFile = true;
- configFilePath= param.mid(configPrefix.length());
+ configFilePath = param.mid(configPrefix.length());
}
}
-
- // Create the default screen if there was no config file
- if (!hasConfigFile) {
- QOffscreenScreen *offscreenScreen = new QOffscreenScreen(this);
- m_screens.append(offscreenScreen);
- QWindowSystemInterface::handleScreenAdded(offscreenScreen);
- return;
- }
+ if (!hasConfigFile)
+ return std::nullopt;
// Read config file
if (configFilePath.isEmpty())
@@ -179,28 +200,145 @@ void QOffscreenIntegration::configure(const QStringList& paramList)
if (config.isNull())
qFatal("Platform config file parse error: %s", qPrintable(error.errorString()));
- // Apply configuration (create screens)
- bool synchronousWindowSystemEvents = config["synchronousWindowSystemEvents"].toBool(false);
+ return config.object();
+}
+
+
+void QOffscreenIntegration::setConfiguration(const QJsonObject &configuration)
+{
+ // Apply the new configuration, diffing against the current m_configuration
+
+ const bool synchronousWindowSystemEvents = configuration["synchronousWindowSystemEvents"].toBool(
+ m_configuration["synchronousWindowSystemEvents"].toBool(false));
QWindowSystemInterface::setSynchronousWindowSystemEvents(synchronousWindowSystemEvents);
- m_windowFrameMarginsEnabled = config["windowFrameMargins"].toBool(true);
- QJsonArray screens = config["screens"].toArray();
- for (QJsonValue screenValue : screens) {
- QJsonObject screen = screenValue.toObject();
- if (screen.isEmpty()) {
- qWarning("QOffscreenIntegration::initializeWithPlatformArguments: empty screen object");
+
+ m_windowFrameMarginsEnabled = configuration["windowFrameMargins"].toBool(
+ m_configuration["windowFrameMargins"].toBool(true));
+
+ // Diff screens array, using the screen name as the screen identity.
+ QJsonArray currentScreens = m_configuration["screens"].toArray();
+ QJsonArray newScreens = configuration["screens"].toArray();
+
+ auto getScreenNames = [](const QJsonArray &screens) -> QList<QString> {
+ QList<QString> names;
+ for (QJsonValue screen : screens) {
+ names.append(screen["name"].toString());
+ };
+ std::sort(names.begin(), names.end());
+ return names;
+ };
+
+ auto currentNames = getScreenNames(currentScreens);
+ auto newNames = getScreenNames(newScreens);
+
+ QList<QString> present;
+ std::set_intersection(currentNames.begin(), currentNames.end(), newNames.begin(), newNames.end(),
+ std::inserter(present, present.begin()));
+ QList<QString> added;
+ std::set_difference(newNames.begin(), newNames.end(), currentNames.begin(), currentNames.end(),
+ std::inserter(added, added.begin()));
+ QList<QString> removed;
+ std::set_difference(currentNames.begin(), currentNames.end(), newNames.begin(), newNames.end(),
+ std::inserter(removed, removed.begin()));
+
+ auto platformScreenByName = [](const QString &name, QList<QOffscreenScreen *> screens) -> QOffscreenScreen * {
+ for (QOffscreenScreen *screen : screens) {
+ if (screen->m_name == name)
+ return screen;
+ }
+ Q_UNREACHABLE();
+ };
+
+ auto screenConfigByName = [](const QString &name, QJsonArray screenConfigs) -> QJsonValue {
+ for (QJsonValue screenConfig : screenConfigs) {
+ if (screenConfig["name"].toString() == name)
+ return screenConfig;
+ }
+ Q_UNREACHABLE();
+ };
+
+ auto geometryFromConfig = [](const QJsonObject &config) -> QRect {
+ return QRect(config["x"].toInt(0), config["y"].toInt(0), config["width"].toInt(640), config["height"].toInt(480));
+ };
+
+ // Remove removed screens
+ for (const QString &remove : removed) {
+ QOffscreenScreen *screen = platformScreenByName(remove, m_screens);
+ m_screens.removeAll(screen);
+ QWindowSystemInterface::handleScreenRemoved(screen);
+ }
+
+ // Add new screens
+ for (const QString &add : added) {
+ QJsonValue configValue = screenConfigByName(add, newScreens);
+ QJsonObject config = configValue.toObject();
+ if (config.isEmpty()) {
+ qWarning("empty screen object");
continue;
}
QOffscreenScreen *offscreenScreen = new QOffscreenScreen(this);
- offscreenScreen->m_name = screen["name"].toString();
- offscreenScreen->m_geometry = QRect(screen["x"].toInt(0), screen["y"].toInt(0),
- screen["width"].toInt(640), screen["height"].toInt(480));
- offscreenScreen->m_logicalDpi = screen["logicalDpi"].toInt(96);
- offscreenScreen->m_logicalBaseDpi = screen["logicalBaseDpi"].toInt(96);
- offscreenScreen->m_dpr = screen["dpr"].toDouble(1.0);
-
+ offscreenScreen->m_name = config["name"].toString();
+ offscreenScreen->m_geometry = geometryFromConfig(config);
+ offscreenScreen->m_logicalDpi = config["logicalDpi"].toInt(96);
+ offscreenScreen->m_logicalBaseDpi = config["logicalBaseDpi"].toInt(96);
+ offscreenScreen->m_dpr = config["dpr"].toDouble(1.0);
m_screens.append(offscreenScreen);
QWindowSystemInterface::handleScreenAdded(offscreenScreen);
}
+
+ // Update present screens
+ for (const QString &pres : present) {
+ QOffscreenScreen *screen = platformScreenByName(pres, m_screens);
+ Q_ASSERT(screen);
+ QJsonObject currentConfig = screenConfigByName(pres, currentScreens).toObject();
+ QJsonObject newConfig = screenConfigByName(pres, newScreens).toObject();
+
+ // Name can't change, because it'd be a different screen
+ Q_ASSERT(currentConfig["name"] == newConfig["name"]);
+
+ // Geometry
+ QRect currentGeomtry = geometryFromConfig(currentConfig);
+ QRect newGeomtry = geometryFromConfig(newConfig);
+ if (currentGeomtry != newGeomtry) {
+ screen->m_geometry = newGeomtry;
+ QWindowSystemInterface::handleScreenGeometryChange(screen->screen(), newGeomtry, newGeomtry);
+ }
+
+ // logical DPI
+ int currentLogicalDpi = currentConfig["logicalDpi"].toInt(96);
+ int newLogicalDpi = newConfig["logicalDpi"].toInt(96);
+ if (currentLogicalDpi != newLogicalDpi) {
+ screen->m_logicalDpi = newLogicalDpi;
+ QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen->screen(), newLogicalDpi, newLogicalDpi);
+ }
+
+ // The base DPI is more of a platform constant, and should not change, and
+ // there is no handleChange function for it. Print a warning.
+ int currentLogicalBaseDpi = currentConfig["logicalBaseDpi"].toInt(96);
+ int newLogicalBaseDpi = newConfig["logicalBaseDpi"].toInt(96);
+ if (currentLogicalBaseDpi != newLogicalBaseDpi) {
+ screen->m_logicalBaseDpi = newLogicalBaseDpi;
+ qWarning("You ain't supposed to change logicalBaseDpi - its a platform constant. Qt may not react to the change");
+ }
+
+ // DPR. There is also no handleChange function in Qt at this point, instead
+ // the new DPR value will be used during the next repaint. We could repaint
+ // all windows here, but don't. Print a warning.
+ double currentDpr = currentConfig["dpr"].toDouble(1);
+ double newDpr = newConfig["dpr"].toDouble(1);
+ if (currentDpr != newDpr) {
+ screen->m_dpr = newDpr;
+ qWarning("DPR change notifications is not implemented - Qt may not react to the change");
+ }
+ }
+
+ // Now the new configuration is the current configuration
+ m_configuration = configuration;
+}
+
+QJsonObject QOffscreenIntegration::configuration() const
+{
+ return m_configuration;
}
void QOffscreenIntegration::initialize()
@@ -250,7 +388,7 @@ QAbstractEventDispatcher *QOffscreenIntegration::createEventDispatcher() const
QPlatformNativeInterface *QOffscreenIntegration::nativeInterface() const
{
if (!m_nativeInterface)
- m_nativeInterface.reset(new QOffscreenPlatformNativeInterface);
+ m_nativeInterface.reset(new QOffscreenPlatformNativeInterface(const_cast<QOffscreenIntegration*>(this)));
return m_nativeInterface.get();
}
@@ -323,17 +461,15 @@ QOffscreenIntegration *QOffscreenIntegration::createOffscreenIntegration(const Q
#if QT_CONFIG(xlib) && QT_CONFIG(opengl) && !QT_CONFIG(opengles2)
QByteArray glx = qgetenv("QT_QPA_OFFSCREEN_NO_GLX");
if (glx.isEmpty())
- offscreenIntegration = new QOffscreenX11Integration;
+ offscreenIntegration = new QOffscreenX11Integration(paramList);
#endif
if (!offscreenIntegration)
- offscreenIntegration = new QOffscreenIntegration;
-
- offscreenIntegration->configure(paramList);
+ offscreenIntegration = new QOffscreenIntegration(paramList);
return offscreenIntegration;
}
-QList<QPlatformScreen *> QOffscreenIntegration::screens() const
+QList<QOffscreenScreen *> QOffscreenIntegration::screens() const
{
return m_screens;
}
diff --git a/src/plugins/platforms/offscreen/qoffscreenintegration.h b/src/plugins/platforms/offscreen/qoffscreenintegration.h
index 38d145eee3..a490cf5bed 100644
--- a/src/plugins/platforms/offscreen/qoffscreenintegration.h
+++ b/src/plugins/platforms/offscreen/qoffscreenintegration.h
@@ -44,18 +44,24 @@
#include <qpa/qplatformnativeinterface.h>
#include <qscopedpointer.h>
+#include <qjsonobject.h>
QT_BEGIN_NAMESPACE
class QOffscreenBackendData;
+class QOffscreenScreen;
class QOffscreenIntegration : public QPlatformIntegration
{
public:
- QOffscreenIntegration();
+ QOffscreenIntegration(const QStringList& paramList);
~QOffscreenIntegration();
- void configure(const QStringList& paramList);
+ QJsonObject defaultConfiguration() const;
+ std::optional<QJsonObject> resolveConfigFileConfiguration(const QStringList& paramList) const;
+ void setConfiguration(const QJsonObject &configuration);
+ QJsonObject configuration() const;
+
void initialize() override;
bool hasCapability(QPlatformIntegration::Capability cap) const override;
@@ -78,7 +84,7 @@ public:
static QOffscreenIntegration *createOffscreenIntegration(const QStringList& paramList);
- QList<QPlatformScreen *> screens() const;
+ QList<QOffscreenScreen *> screens() const;
protected:
QScopedPointer<QPlatformFontDatabase> m_fontDatabase;
#if QT_CONFIG(draganddrop)
@@ -87,8 +93,9 @@ protected:
QScopedPointer<QPlatformInputContext> m_inputContext;
QScopedPointer<QPlatformServices> m_services;
mutable QScopedPointer<QPlatformNativeInterface> m_nativeInterface;
- QList<QPlatformScreen *> m_screens;
+ QList<QOffscreenScreen *> m_screens;
bool m_windowFrameMarginsEnabled = true;
+ QJsonObject m_configuration;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/offscreen/qoffscreenintegration_x11.cpp b/src/plugins/platforms/offscreen/qoffscreenintegration_x11.cpp
index 1e533f87dc..0bb19bc4f6 100644
--- a/src/plugins/platforms/offscreen/qoffscreenintegration_x11.cpp
+++ b/src/plugins/platforms/offscreen/qoffscreenintegration_x11.cpp
@@ -76,6 +76,12 @@ private:
QOffscreenX11Connection *m_connection;
};
+QOffscreenX11Integration::QOffscreenX11Integration(const QStringList& paramList)
+: QOffscreenIntegration(paramList)
+{
+
+}
+
QOffscreenX11Integration::~QOffscreenX11Integration() = default;
bool QOffscreenX11Integration::hasCapability(QPlatformIntegration::Capability cap) const
@@ -106,10 +112,16 @@ QPlatformOpenGLContext *QOffscreenX11Integration::createPlatformOpenGLContext(QO
QOffscreenX11PlatformNativeInterface *QOffscreenX11Integration::nativeInterface() const
{
if (!m_nativeInterface)
- m_nativeInterface.reset(new QOffscreenX11PlatformNativeInterface);
+ m_nativeInterface.reset(new QOffscreenX11PlatformNativeInterface(const_cast<QOffscreenX11Integration *>(this)));
return static_cast<QOffscreenX11PlatformNativeInterface *>(m_nativeInterface.data());
}
+QOffscreenX11PlatformNativeInterface::QOffscreenX11PlatformNativeInterface(QOffscreenIntegration *integration)
+:QOffscreenPlatformNativeInterface(integration)
+{
+
+}
+
QOffscreenX11PlatformNativeInterface::~QOffscreenX11PlatformNativeInterface() = default;
void *QOffscreenX11PlatformNativeInterface::nativeResourceForScreen(const QByteArray &resource, QScreen *screen)
@@ -143,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 7e26b76759..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,21 +55,29 @@ class QOffscreenX11Connection;
class QOffscreenX11Info;
class QOffscreenX11PlatformNativeInterface : public QOffscreenPlatformNativeInterface
+#if QT_CONFIG(xcb)
+ , public QNativeInterface::QX11Application
+#endif
{
public:
+ QOffscreenX11PlatformNativeInterface(QOffscreenIntegration *integration);
~QOffscreenX11PlatformNativeInterface();
void *nativeResourceForScreen(const QByteArray &resource, QScreen *screen) override;
#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;
};
class QOffscreenX11Integration : public QOffscreenIntegration
{
public:
+ QOffscreenX11Integration(const QStringList& paramList);
~QOffscreenX11Integration();
bool hasCapability(QPlatformIntegration::Capability cap) const override;
diff --git a/src/plugins/platforms/offscreen/qoffscreenwindow.cpp b/src/plugins/platforms/offscreen/qoffscreenwindow.cpp
index a46258a401..20aae06650 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
@@ -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;
diff --git a/src/plugins/platforms/qnx/CMakeLists.txt b/src/plugins/platforms/qnx/CMakeLists.txt
index 8554874919..3e952e6ddf 100644
--- a/src/plugins/platforms/qnx/CMakeLists.txt
+++ b/src/plugins/platforms/qnx/CMakeLists.txt
@@ -6,7 +6,7 @@
qt_internal_add_plugin(QQnxIntegrationPlugin
OUTPUT_NAME qqnx
- TYPE platforms
+ PLUGIN_TYPE platforms
DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES qnx # special case
SOURCES
main.cpp main.h
diff --git a/src/plugins/platforms/vkkhrdisplay/CMakeLists.txt b/src/plugins/platforms/vkkhrdisplay/CMakeLists.txt
index 0110782326..98fb6d39b7 100644
--- a/src/plugins/platforms/vkkhrdisplay/CMakeLists.txt
+++ b/src/plugins/platforms/vkkhrdisplay/CMakeLists.txt
@@ -2,7 +2,7 @@ qt_find_package(WrapFreetype PROVIDED_TARGETS WrapFreetype::WrapFreetype)
qt_internal_add_plugin(QVkKhrDisplayIntegrationPlugin
OUTPUT_NAME qvkkhrdisplay
- TYPE platforms
+ PLUGIN_TYPE platforms
DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES vkkhrdisplay
SOURCES
main.cpp
diff --git a/src/plugins/platforms/vnc/CMakeLists.txt b/src/plugins/platforms/vnc/CMakeLists.txt
index 7c28e26d9b..ca9445207b 100644
--- a/src/plugins/platforms/vnc/CMakeLists.txt
+++ b/src/plugins/platforms/vnc/CMakeLists.txt
@@ -6,7 +6,7 @@
qt_internal_add_plugin(QVncIntegrationPlugin
OUTPUT_NAME qvnc
- TYPE platforms
+ PLUGIN_TYPE platforms
DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES vnc # special case
SOURCES
main.cpp
diff --git a/src/plugins/platforms/wasm/CMakeLists.txt b/src/plugins/platforms/wasm/CMakeLists.txt
index bcd828b013..00f86b837c 100644
--- a/src/plugins/platforms/wasm/CMakeLists.txt
+++ b/src/plugins/platforms/wasm/CMakeLists.txt
@@ -7,7 +7,7 @@
qt_internal_add_plugin(QWasmIntegrationPlugin
OUTPUT_NAME qwasm
DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES wasm # special case
- TYPE platforms
+ PLUGIN_TYPE platforms
STATIC
SOURCES
main.cpp
@@ -62,15 +62,20 @@ qt_internal_extend_target(QWasmIntegrationPlugin CONDITION QT_FEATURE_opengl
#### Keys ignored in scope 4:.:.:wasm.pro:NOT TARGET___equals____ss_QT_DEFAULT_QPA_PLUGIN:
# PLUGIN_EXTENDS = "-"
-qt_copy_or_install(FILES
+set(wasm_support_files
wasm_shell.html
- DESTINATION "${CMAKE_INSTALL_PREFIX}/plugins/platforms/"
-)
-qt_copy_or_install(FILES
qtloader.js
- DESTINATION "${CMAKE_INSTALL_PREFIX}/plugins/platforms/"
+ qtlogo.svg
)
+
qt_copy_or_install(FILES
- qtlogo.svg
- DESTINATION "${CMAKE_INSTALL_PREFIX}/plugins/platforms/"
+ ${wasm_support_files}
+ DESTINATION "${CMAKE_INSTALL_PREFIX}/plugins/platforms"
)
+# Need to copy the support files to the build dir in a top-level prefix build
+# So _qt_internal_wasm_add_target_helpers finds them.
+if(QT_WILL_INSTALL)
+ foreach(wasm_support_file ${wasm_support_files})
+ file(COPY "${wasm_support_file}" DESTINATION "${QT_BUILD_DIR}/plugins/platforms")
+ endforeach()
+endif()
diff --git a/src/plugins/platforms/wasm/qtloader.js b/src/plugins/platforms/wasm/qtloader.js
index 14a22fc0f1..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.
@@ -126,6 +126,11 @@
// Signals to the application that a canvas has been resized.
// setFontDpi
// Sets the logical font dpi for the application.
+// module
+// Returns the Emscripten module object, or undefined if the module
+// has not been created yet. Note that the module object becomes available
+// at the very end of the loading sequence, _after_ the transition from
+// Loading to Running occurs.
function QtLoader(config)
@@ -249,6 +254,7 @@ function QtLoader(config)
publicAPI.resizeCanvasElement = resizeCanvasElement;
publicAPI.setFontDpi = setFontDpi;
publicAPI.fontDpi = fontDpi;
+ publicAPI.module = module;
self.restartCount = 0;
@@ -274,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);
});
}
@@ -416,7 +422,7 @@ function QtLoader(config)
self.moduleConfig.preRun.push(function(module) {
// Set environment variables
for (var [key, value] of Object.entries(config.environment)) {
- ENV[key.toUpperCase()] = value;
+ module.ENV[key.toUpperCase()] = value;
}
// Propagate Qt module properties
module.qtCanvasElements = self.qtCanvasElements;
@@ -586,6 +592,10 @@ function QtLoader(config)
return self.qtFontDpi;
}
+ function module() {
+ return self.module;
+ }
+
setStatus("Created");
return publicAPI;
diff --git a/src/plugins/platforms/wasm/qwasmcompositor.cpp b/src/plugins/platforms/wasm/qwasmcompositor.cpp
index 3883bf3c95..385e47f0a0 100644
--- a/src/plugins/platforms/wasm/qwasmcompositor.cpp
+++ b/src/plugins/platforms/wasm/qwasmcompositor.cpp
@@ -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);
}
diff --git a/src/plugins/platforms/wasm/qwasmeventdispatcher.cpp b/src/plugins/platforms/wasm/qwasmeventdispatcher.cpp
index 29dbf96883..8f9eeecbb0 100644
--- a/src/plugins/platforms/wasm/qwasmeventdispatcher.cpp
+++ b/src/plugins/platforms/wasm/qwasmeventdispatcher.cpp
@@ -91,6 +91,16 @@ bool QWasmEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
if (!(flags & QEventLoop::EventLoopExec))
return QUnixEventDispatcherQPA::processEvents(flags);
+ if (flags & QEventLoop::DialogExec) {
+ qWarning() << "Warning: dialog exec() is not supported on Qt for WebAssembly, please use"
+ << "show() instead. When using exec() the dialog will show, the user can interact"
+ << "with it and the appropriate signals will be emitted on close. However, the"
+ << "exec() call never returns, stack content at the time of the exec() call"
+ << "is leaked, and the exec() call may interfere with input event processing";
+
+ emscripten_sleep(1); // This call never returns
+ }
+
// Handle processEvents from QEventLoop::exec():
//
// At this point the application has created its root objects on
@@ -138,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..1344af3816 100644
--- a/src/plugins/platforms/wasm/qwasmeventtranslator.cpp
+++ b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp
@@ -465,6 +465,11 @@ bool QWasmEventTranslator::processMouse(int eventType, const EmscriptenMouseEven
pressedButtons.setFlag(button);
+ // 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;
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..aac7413994 100644
--- a/src/plugins/platforms/wasm/qwasmwindow.cpp
+++ b/src/plugins/platforms/wasm/qwasmwindow.cpp
@@ -407,7 +407,6 @@ bool QWasmWindow::hasTitleBar() const
auto flags = window()->flags();
return !(m_windowState & Qt::WindowFullScreen)
&& flags.testFlag(Qt::WindowTitleHint)
- && !flags.testFlag(Qt::Popup)
&& !flags.testFlag(Qt::ToolTip)
&& m_needsCompositor;
}
diff --git a/src/plugins/platforms/windows/CMakeLists.txt b/src/plugins/platforms/windows/CMakeLists.txt
index f03cf250ea..880fa79ef5 100644
--- a/src/plugins/platforms/windows/CMakeLists.txt
+++ b/src/plugins/platforms/windows/CMakeLists.txt
@@ -6,7 +6,7 @@
qt_internal_add_plugin(QWindowsIntegrationPlugin
OUTPUT_NAME qwindows
- TYPE platforms
+ PLUGIN_TYPE platforms
DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES windows # special case
SOURCES
main.cpp
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/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp
index 78961472b0..1c75f7f7d2 100644
--- a/src/plugins/platforms/windows/qwindowscontext.cpp
+++ b/src/plugins/platforms/windows/qwindowscontext.cpp
@@ -218,6 +218,7 @@ void QWindowsUser32DLL::init()
getWindowDpiAwarenessContext = (GetWindowDpiAwarenessContext)library.resolve("GetWindowDpiAwarenessContext");
getAwarenessFromDpiAwarenessContext = (GetAwarenessFromDpiAwarenessContext)library.resolve("GetAwarenessFromDpiAwarenessContext");
systemParametersInfoForDpi = (SystemParametersInfoForDpi)library.resolve("SystemParametersInfoForDpi");
+ getDpiForWindow = (GetDpiForWindow)library.resolve("GetDpiForWindow");
}
}
@@ -509,9 +510,9 @@ void QWindowsContext::setProcessDpiV2Awareness()
QWindowsContextPrivate::m_v2DpiAware = true;
} else {
const HRESULT errorCode = GetLastError();
- // E_ACCESSDENIED means set externally (MSVC manifest or external app loading Qt plugin).
+ // ERROR_ACCESS_DENIED means set externally (MSVC manifest or external app loading Qt plugin).
// Silence warning in that case unless debug is enabled.
- if (errorCode != E_ACCESSDENIED || lcQpaWindows().isDebugEnabled()) {
+ if (errorCode != ERROR_ACCESS_DENIED || lcQpaWindows().isDebugEnabled()) {
qWarning().noquote().nospace() << "setProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) failed: "
<< QWindowsContext::comErrorString(errorCode);
}
@@ -1087,31 +1088,13 @@ static inline QWindowsInputContext *windowsInputContext()
return qobject_cast<QWindowsInputContext *>(QWindowsIntegration::instance()->inputContext());
}
-
-// Child windows, fixed-size windows or pop-ups and similar should not be resized
-static inline bool resizeOnDpiChanged(const QWindow *w)
-{
- bool result = false;
- if (w->isTopLevel()) {
- switch (w->type()) {
- case Qt::Window:
- case Qt::Dialog:
- case Qt::Sheet:
- case Qt::Drawer:
- case Qt::Tool:
- result = !w->flags().testFlag(Qt::MSWindowsFixedSizeDialogHint);
- break;
- default:
- break;
- }
- }
- return result;
-}
-
bool QWindowsContext::shouldHaveNonClientDpiScaling(const QWindow *window)
{
+ // DPI aware V2 processes always have NonClientDpiScaling enabled.
+ if (QWindowsContextPrivate::m_v2DpiAware)
+ return true;
+
return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10
- && !QWindowsContextPrivate::m_v2DpiAware // V2 implies NonClientDpiScaling; no need to enable
&& window->isTopLevel()
&& !window->property(QWindowsWindow::embeddedNativeParentHandleProperty).isValid()
#if QT_CONFIG(opengl) // /QTBUG-62901, EnableNonClientDpiScaling has problems with GL
@@ -1288,8 +1271,12 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
d->m_creationContext->obtainedPos = QPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
return true;
case QtWindows::NonClientCreate:
- if (shouldHaveNonClientDpiScaling(d->m_creationContext->window))
- enableNonClientDpiScaling(msg.hwnd);
+ if (shouldHaveNonClientDpiScaling(d->m_creationContext->window) &&
+ // DPI aware V2 processes always have NonClientDpiScaling enabled
+ // and there is no need to make an API call to manually enable.
+ !QWindowsContextPrivate::m_v2DpiAware) {
+ enableNonClientDpiScaling(msg.hwnd);
+ }
return false;
case QtWindows::CalculateSize:
return QWindowsGeometryHint::handleCalculateSize(d->m_creationContext->customMargins, msg, result);
@@ -1466,26 +1453,12 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
return true;
#endif
} break;
- case QtWindows::DpiChangedEvent: {
- // Try to apply the suggested size first and then notify ScreenChanged
- // so that the resize event sent from QGuiApplication incorporates it
- // WM_DPICHANGED is sent with a size that avoids resize loops (by
- // snapping back to the previous screen, see QTBUG-65580).
- const bool doResize = resizeOnDpiChanged(platformWindow->window());
- if (doResize) {
- platformWindow->setFlag(QWindowsWindow::WithinDpiChanged);
- platformWindow->updateFullFrameMargins();
- const auto prcNewWindow = reinterpret_cast<RECT *>(lParam);
- qCDebug(lcQpaWindows) << __FUNCTION__ << "WM_DPICHANGED"
- << platformWindow->window() << *prcNewWindow;
- SetWindowPos(hwnd, nullptr, prcNewWindow->left, prcNewWindow->top,
- prcNewWindow->right - prcNewWindow->left,
- prcNewWindow->bottom - prcNewWindow->top, SWP_NOZORDER | SWP_NOACTIVATE);
- platformWindow->clearFlag(QWindowsWindow::WithinDpiChanged);
- }
- platformWindow->checkForScreenChanged(QWindowsWindow::FromDpiChange);
- return doResize;
- }
+ 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();
diff --git a/src/plugins/platforms/windows/qwindowscontext.h b/src/plugins/platforms/windows/qwindowscontext.h
index 99b4bb6c77..04328a0369 100644
--- a/src/plugins/platforms/windows/qwindowscontext.h
+++ b/src/plugins/platforms/windows/qwindowscontext.h
@@ -108,6 +108,7 @@ struct QWindowsUser32DLL
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;
@@ -124,6 +125,9 @@ struct QWindowsUser32DLL
// Windows Vista onwards
SetProcessDPIAware setProcessDPIAware = nullptr;
+ // Windows 10 version 1607 onwards
+ GetDpiForWindow getDpiForWindow = nullptr;
+
// Windows 10 version 1703 onwards
SetProcessDpiAwarenessContext setProcessDpiAwarenessContext = nullptr;
diff --git a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp
index 0312bde563..1ddde93f90 100644
--- a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp
+++ b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp
@@ -150,7 +150,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
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/qwindowsmenu.cpp b/src/plugins/platforms/windows/qwindowsmenu.cpp
index 31d9d3e09b..a0ace1f06a 100644
--- a/src/plugins/platforms/windows/qwindowsmenu.cpp
+++ b/src/plugins/platforms/windows/qwindowsmenu.cpp
@@ -56,7 +56,7 @@ QT_BEGIN_NAMESPACE
\brief Windows native menu bar
\list
- \li \l{https://msdn.microsoft.com/de-de/library/windows/desktop/ms647553(v=vs.85).aspx#_win32_Menu_Creation_Functions},
+ \li \l{https://docs.microsoft.com/en-us/windows/win32/menurc/about-menus},
\e{About Menus}
\endlist
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/qwindowspointerhandler.cpp b/src/plugins/platforms/windows/qwindowspointerhandler.cpp
index 24d85c76e6..46fbfcc8d6 100644
--- a/src/plugins/platforms/windows/qwindowspointerhandler.cpp
+++ b/src/plugins/platforms/windows/qwindowspointerhandler.cpp
@@ -768,8 +768,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/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/qwindowstheme.cpp b/src/plugins/platforms/windows/qwindowstheme.cpp
index 61a885953a..6fbf4183da 100644
--- a/src/plugins/platforms/windows/qwindowstheme.cpp
+++ b/src/plugins/platforms/windows/qwindowstheme.cpp
@@ -529,6 +529,11 @@ QVariant QWindowsTheme::themeHint(ThemeHint hint) const
return QPlatformTheme::themeHint(hint);
}
+QPlatformTheme::Appearance QWindowsTheme::appearance() const
+{
+ return QWindowsContext::isDarkMode() ? Appearance::Dark : Appearance::Light;
+}
+
void QWindowsTheme::clearPalettes()
{
qDeleteAll(m_palettes, m_palettes + NPalettes);
diff --git a/src/plugins/platforms/windows/qwindowstheme.h b/src/plugins/platforms/windows/qwindowstheme.h
index af28f2878c..9d5fcc92fe 100644
--- a/src/plugins/platforms/windows/qwindowstheme.h
+++ b/src/plugins/platforms/windows/qwindowstheme.h
@@ -64,6 +64,9 @@ public:
QPlatformSystemTrayIcon *createPlatformSystemTrayIcon() const override;
#endif
QVariant themeHint(ThemeHint) const override;
+
+ Appearance appearance() const override;
+
const QPalette *palette(Palette type = SystemPalette) const override
{ return m_palettes[type]; }
const QFont *font(Font type = SystemFont) const override
diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp
index 3e291f310d..6e75b9aa1b 100644
--- a/src/plugins/platforms/windows/qwindowswindow.cpp
+++ b/src/plugins/platforms/windows/qwindowswindow.cpp
@@ -1345,6 +1345,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,6 +1420,8 @@ void QWindowsWindow::initialize()
if (obtainedScreen && screen() != obtainedScreen)
QWindowSystemInterface::handleWindowScreenChanged<QWindowSystemInterface::SynchronousDelivery>(w, obtainedScreen->screen());
}
+ QWindowsWindow::setSavedDpi(QWindowsContext::user32dll.getDpiForWindow ?
+ QWindowsContext::user32dll.getDpiForWindow(handle()) : 96);
}
QSurfaceFormat QWindowsWindow::format() const
@@ -1831,6 +1834,66 @@ 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());
+ QRect currentGeometry = platformChildWindow->geometry();
+ QRect scaledGeometry = QRect(currentGeometry.topLeft() * scale, currentGeometry.size() * scale);
+ platformChildWindow->setGeometry(scaledGeometry);
+ }
+}
+
static QRect normalFrameGeometry(HWND hwnd)
{
WINDOWPLACEMENT wp;
@@ -1901,6 +1964,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).
@@ -1997,10 +2062,6 @@ void QWindowsWindow::handleGeometryChange()
{
const QRect previousGeometry = m_data.geometry;
m_data.geometry = geometry_sys();
- if (testFlag(WithinDpiChanged)
- && QWindowsContext::instance()->screenManager().screenForHwnd(m_data.hwnd) != screen()) {
- return; // QGuiApplication will send resize when screen actually changes
- }
QWindowSystemInterface::handleGeometryChange(window(), m_data.geometry);
// QTBUG-32121: OpenGL/normal windows (with exception of ANGLE
// which we no longer support in Qt 6) do not receive expose
@@ -2081,7 +2142,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()
@@ -2697,10 +2758,7 @@ static int getBorderWidth(const QPlatformScreen *screen)
void QWindowsWindow::getSizeHints(MINMAXINFO *mmi) const
{
- // We don't apply the min/max size hint as we change the dpi, because we did not adjust the
- // QScreen of the window yet so we don't have the min/max with the right ratio
- if (!testFlag(QWindowsWindow::WithinDpiChanged))
- QWindowsGeometryHint::applyToMinMaxInfo(window(), fullFrameMargins(), mmi);
+ QWindowsGeometryHint::applyToMinMaxInfo(window(), fullFrameMargins(), mmi);
// This block fixes QTBUG-8361, QTBUG-4362: Frameless/title-less windows shouldn't cover the
// taskbar when maximized
@@ -3032,7 +3090,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 b0f58b09a9..8b9e304e9b 100644
--- a/src/plugins/platforms/windows/qwindowswindow.h
+++ b/src/plugins/platforms/windows/qwindowswindow.h
@@ -235,11 +235,10 @@ public:
MaximizeToFullScreen = 0x80000,
Compositing = 0x100000,
HasBorderInFullScreen = 0x200000,
- WithinDpiChanged = 0x400000,
- VulkanSurface = 0x800000,
- ResizeMoveActive = 0x1000000,
- DisableNonClientScaling = 0x2000000,
- Direct3DSurface = 0x4000000
+ VulkanSurface = 0x400000,
+ ResizeMoveActive = 0x800000,
+ DisableNonClientScaling = 0x1000000,
+ Direct3DSurface = 0x2000000
};
QWindowsWindow(QWindow *window, const QWindowsWindowData &data);
@@ -319,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();
@@ -372,6 +373,9 @@ public:
static const char *embeddedNativeParentHandleProperty;
static const char *hasBorderInFullScreenProperty;
+ void setSavedDpi(int dpi) { m_savedDpi = dpi; }
+ int savedDpi() const { return m_savedDpi; }
+
private:
inline void show_sys() const;
inline QWindowsWindowData setWindowFlags_sys(Qt::WindowFlags wt, unsigned flags = 0) const;
@@ -405,6 +409,7 @@ private:
HICON m_iconSmall = nullptr;
HICON m_iconBig = nullptr;
void *m_surface = nullptr;
+ int m_savedDpi = 96;
static bool m_screenForGLInitialized;
@@ -413,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 83f4d5b209..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
)
@@ -162,7 +162,7 @@ endif()
qt_internal_add_plugin(QXcbIntegrationPlugin
OUTPUT_NAME qxcb
- TYPE platforms
+ PLUGIN_TYPE platforms
DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES xcb # special case
SOURCES
qxcbmain.cpp
diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/CMakeLists.txt b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/CMakeLists.txt
index d24050edf3..4a62c8ad46 100644
--- a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/CMakeLists.txt
+++ b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/CMakeLists.txt
@@ -8,7 +8,7 @@ qt_find_package(EGL) # special case
qt_internal_add_plugin(QXcbEglIntegrationPlugin
OUTPUT_NAME qxcb-egl-integration
- TYPE xcbglintegrations
+ PLUGIN_TYPE xcbglintegrations
SOURCES
qxcbeglcontext.h
qxcbeglintegration.cpp qxcbeglintegration.h
diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglintegration.cpp b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglintegration.cpp
index f3630d57b9..5f07b10f04 100644
--- a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglintegration.cpp
+++ b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglintegration.cpp
@@ -67,7 +67,12 @@ bool QXcbEglIntegration::initialize(QXcbConnection *connection)
const char *extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
- if (extensions && strstr(extensions, "EGL_EXT_platform_x11")) {
+ if (extensions && (strstr(extensions, "EGL_EXT_platform_xcb") || strstr(extensions, "EGL_MESA_platform_xcb"))) {
+ QEGLStreamConvenience streamFuncs;
+ m_egl_display = streamFuncs.get_platform_display(EGL_PLATFORM_XCB_KHR,
+ reinterpret_cast<void *>(connection->xcb_connection()),
+ nullptr);
+ } else if (extensions && strstr(extensions, "EGL_EXT_platform_x11")) {
QEGLStreamConvenience streamFuncs;
m_egl_display = streamFuncs.get_platform_display(EGL_PLATFORM_X11_KHR,
xlib_display(),
diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/CMakeLists.txt b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/CMakeLists.txt
index 80f785a2f0..ae81eba545 100644
--- a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/CMakeLists.txt
+++ b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/CMakeLists.txt
@@ -6,7 +6,7 @@
qt_internal_add_plugin(QXcbGlxIntegrationPlugin
OUTPUT_NAME qxcb-glx-integration
- TYPE xcbglintegrations
+ PLUGIN_TYPE xcbglintegrations
SOURCES
qglxintegration.cpp qglxintegration.h
qxcbglxintegration.cpp qxcbglxintegration.h
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..4ef2d53343 100644
--- a/src/plugins/platforms/xcb/qxcbbackingstore.cpp
+++ b/src/plugins/platforms/xcb/qxcbbackingstore.cpp
@@ -842,7 +842,12 @@ 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);
+ return *m_image->image();
}
QPlatformGraphicsBuffer *QXcbBackingStore::graphicsBuffer() const
diff --git a/src/plugins/platforms/xcb/qxcbclipboard.cpp b/src/plugins/platforms/xcb/qxcbclipboard.cpp
index 736b56ef60..dcffa568c4 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;
}
}
@@ -265,7 +265,7 @@ QXcbClipboard::~QXcbClipboard()
if (auto event = waitForClipboardEvent(m_owner, 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");
}
}
@@ -371,7 +371,7 @@ 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");
+ qCWarning(lcQpaClipboard, "QXcbClipboard::setMimeData: Cannot set X11 selection owner");
}
emitChanged(mode);
@@ -538,7 +538,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 +553,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 +562,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 +624,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);
@@ -728,7 +729,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
@@ -835,6 +836,8 @@ QByteArray QXcbClipboard::clipboardReadIncrementalProperty(xcb_window_t win, xcb
alloc_error = buf.size() != nbytes+1;
}
+ QElapsedTimer timer;
+ timer.start();
for (;;) {
connection()->flush();
xcb_generic_event_t *ge = waitForClipboardEvent(win, XCB_PROPERTY_NOTIFY);
@@ -870,9 +873,11 @@ QByteArray QXcbClipboard::clipboardReadIncrementalProperty(xcb_window_t win, xcb
tmp_buf.resize(0);
offset += length;
}
- } else {
- break;
}
+
+ const auto elapsed = timer.elapsed();
+ if (elapsed > clipboard_timeout)
+ break;
}
// timed out ... create a new requestor window, otherwise the requestor
diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp
index e1c9bf80fc..04718f09db 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection.cpp
@@ -99,7 +99,7 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra
if (hasXRandr())
xrandrSelectEvents();
- initializeScreens();
+ initializeScreens(false);
if (hasXInput2()) {
xi2SetupDevices();
@@ -579,6 +579,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
HANDLE_PLATFORM_WINDOW_EVENT(xcb_expose_event_t, window, handleExposeEvent);
case XCB_BUTTON_PRESS: {
auto ev = reinterpret_cast<xcb_button_press_event_t *>(event);
+ setTime(ev->time);
m_keyboard->updateXKBStateFromCore(ev->state);
// the event explicitly contains the state of the three first buttons,
// the rest we need to manage ourselves
@@ -591,6 +592,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
}
case XCB_BUTTON_RELEASE: {
auto ev = reinterpret_cast<xcb_button_release_event_t *>(event);
+ setTime(ev->time);
if (m_duringSystemMoveResize && ev->root != XCB_NONE)
abortSystemMoveResize(ev->root);
m_keyboard->updateXKBStateFromCore(ev->state);
@@ -603,6 +605,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
}
case XCB_MOTION_NOTIFY: {
auto ev = reinterpret_cast<xcb_motion_notify_event_t *>(event);
+ setTime(ev->time);
m_keyboard->updateXKBStateFromCore(ev->state);
m_buttonState = (m_buttonState & ~0x7) | translateMouseButtons(ev->state);
if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
@@ -610,8 +613,14 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
ev->event_x, ev->event_y, ev->detail, static_cast<unsigned int>(m_buttonState));
HANDLE_PLATFORM_WINDOW_EVENT(xcb_motion_notify_event_t, event, handleMotionNotifyEvent);
}
- case XCB_CONFIGURE_NOTIFY:
+ case XCB_CONFIGURE_NOTIFY: {
+ if (isAtLeastXRandR15()) {
+ auto ev = reinterpret_cast<xcb_configure_notify_event_t *>(event);
+ if (ev->event == rootWindow())
+ initializeScreens(true);
+ }
HANDLE_PLATFORM_WINDOW_EVENT(xcb_configure_notify_event_t, event, handleConfigureNotifyEvent);
+ }
case XCB_MAP_NOTIFY:
HANDLE_PLATFORM_WINDOW_EVENT(xcb_map_notify_event_t, event, handleMapNotifyEvent);
case XCB_UNMAP_NOTIFY:
@@ -637,14 +646,19 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
// Prefer XI2 enter (XCB_INPUT_ENTER) events over core events.
break;
}
+ setTime(reinterpret_cast<xcb_enter_notify_event_t *>(event)->time);
HANDLE_PLATFORM_WINDOW_EVENT(xcb_enter_notify_event_t, event, handleEnterNotifyEvent);
case XCB_LEAVE_NOTIFY:
+ {
if (hasXInput2()) {
// Prefer XI2 leave (XCB_INPUT_LEAVE) events over core events.
break;
}
- m_keyboard->updateXKBStateFromCore(reinterpret_cast<xcb_leave_notify_event_t *>(event)->state);
+ auto ev = reinterpret_cast<xcb_leave_notify_event_t *>(event);
+ setTime(ev->time);
+ m_keyboard->updateXKBStateFromCore(ev->state);
HANDLE_PLATFORM_WINDOW_EVENT(xcb_leave_notify_event_t, event, handleLeaveNotifyEvent);
+ }
case XCB_FOCUS_IN:
HANDLE_PLATFORM_WINDOW_EVENT(xcb_focus_in_event_t, event, handleFocusInEvent);
case XCB_FOCUS_OUT:
@@ -652,13 +666,18 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
case XCB_KEY_PRESS:
{
auto keyPress = reinterpret_cast<xcb_key_press_event_t *>(event);
+ setTime(keyPress->time);
m_keyboard->updateXKBStateFromCore(keyPress->state);
setTime(keyPress->time);
HANDLE_KEYBOARD_EVENT(xcb_key_press_event_t, handleKeyPressEvent);
}
case XCB_KEY_RELEASE:
- m_keyboard->updateXKBStateFromCore(reinterpret_cast<xcb_key_release_event_t *>(event)->state);
+ {
+ auto keyRelease = reinterpret_cast<xcb_key_release_event_t *>(event);
+ setTime(keyRelease->time);
+ m_keyboard->updateXKBStateFromCore(keyRelease->state);
HANDLE_KEYBOARD_EVENT(xcb_key_release_event_t, handleKeyReleaseEvent);
+ }
case XCB_MAPPING_NOTIFY:
m_keyboard->updateKeymap(reinterpret_cast<xcb_mapping_notify_event_t *>(event));
break;
@@ -666,6 +685,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
{
#if QT_CONFIG(draganddrop) || QT_CONFIG(clipboard)
auto selectionRequest = reinterpret_cast<xcb_selection_request_event_t *>(event);
+ setTime(selectionRequest->time);
#endif
#if QT_CONFIG(draganddrop)
if (selectionRequest->selection == atom(QXcbAtom::XdndSelection))
@@ -690,11 +710,12 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
break;
case XCB_PROPERTY_NOTIFY:
{
+ auto propertyNotify = reinterpret_cast<xcb_property_notify_event_t *>(event);
+ setTime(propertyNotify->time);
#ifndef QT_NO_CLIPBOARD
if (m_clipboard->handlePropertyNotify(event))
break;
#endif
- auto propertyNotify = reinterpret_cast<xcb_property_notify_event_t *>(event);
if (propertyNotify->atom == atom(QXcbAtom::_NET_WORKAREA)) {
QXcbVirtualDesktop *virtualDesktop = virtualDesktopForRootWindow(propertyNotify->window);
if (virtualDesktop)
@@ -729,17 +750,20 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
for (QXcbVirtualDesktop *virtualDesktop : qAsConst(m_virtualDesktops))
virtualDesktop->handleXFixesSelectionNotify(notify_event);
} else if (isXRandrType(response_type, XCB_RANDR_NOTIFY)) {
- updateScreens(reinterpret_cast<xcb_randr_notify_event_t *>(event));
+ if (!isAtLeastXRandR15())
+ updateScreens(reinterpret_cast<xcb_randr_notify_event_t *>(event));
} else if (isXRandrType(response_type, XCB_RANDR_SCREEN_CHANGE_NOTIFY)) {
- auto change_event = reinterpret_cast<xcb_randr_screen_change_notify_event_t *>(event);
- if (auto virtualDesktop = virtualDesktopForRootWindow(change_event->root))
- virtualDesktop->handleScreenChange(change_event);
+ if (!isAtLeastXRandR15()) {
+ auto change_event = reinterpret_cast<xcb_randr_screen_change_notify_event_t *>(event);
+ if (auto virtualDesktop = virtualDesktopForRootWindow(change_event->root))
+ virtualDesktop->handleScreenChange(change_event);
+ }
} else if (isXkbType(response_type)) {
auto xkb_event = reinterpret_cast<_xkb_event *>(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;
diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h
index a7db3eda89..b0b26085d7 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.h
+++ b/src/plugins/platforms/xcb/qxcbconnection.h
@@ -261,7 +261,16 @@ private:
const xcb_randr_output_change_t &outputChange,
xcb_randr_get_output_info_reply_t *outputInfo);
void destroyScreen(QXcbScreen *screen);
- void initializeScreens();
+ void initializeScreens(bool initialized);
+ void initializeScreensFromOutput(xcb_screen_iterator_t *it, int screenNumber, QXcbScreen *primaryScreen);
+
+ void updateScreen_monitor(QXcbScreen *screen, xcb_randr_monitor_info_t *monitorInfo, xcb_timestamp_t timestamp = XCB_NONE);
+ QXcbScreen *createScreen_monitor(QXcbVirtualDesktop *virtualDesktop,
+ xcb_randr_monitor_info_t *monitorInfo, xcb_timestamp_t timestamp = XCB_NONE);
+ QXcbVirtualDesktop* virtualDesktopForNumber(int n) const;
+ QXcbScreen* findScreenForMonitorInfo(const QList<QPlatformScreen *> &screens, xcb_randr_monitor_info_t *monitorInfo);
+ void initializeScreensFromMonitor(xcb_screen_iterator_t *it, int screenNumber, QXcbScreen *primaryScreen, bool initialized);
+
bool compressEvent(xcb_generic_event_t *event) const;
inline bool timeGreaterThan(xcb_timestamp_t a, xcb_timestamp_t b) const
{ return static_cast<int32_t>(a - b) > 0 || b == XCB_CURRENT_TIME; }
diff --git a/src/plugins/platforms/xcb/qxcbconnection_basic.cpp b/src/plugins/platforms/xcb/qxcbconnection_basic.cpp
index 020412fc87..6482395f86 100644
--- a/src/plugins/platforms/xcb/qxcbconnection_basic.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection_basic.cpp
@@ -333,11 +333,14 @@ void QXcbBasicConnection::initializeXRandr()
XCB_RANDR_MINOR_VERSION);
if (!xrandrQuery || (xrandrQuery->major_version < 1 ||
(xrandrQuery->major_version == 1 && xrandrQuery->minor_version < 2))) {
- qCWarning(lcQpaXcb, "failed to initialize XRandr");
+ qCWarning(lcQpaXcb, "failed to initialize XRandr 1.2");
return;
}
m_hasXRandr = true;
+
+ m_xrandr1Minor = xrandrQuery->minor_version;
+
m_xrandrFirstEvent = reply->first_event;
}
diff --git a/src/plugins/platforms/xcb/qxcbconnection_basic.h b/src/plugins/platforms/xcb/qxcbconnection_basic.h
index bda02ce9c2..d6eb962f56 100644
--- a/src/plugins/platforms/xcb/qxcbconnection_basic.h
+++ b/src/plugins/platforms/xcb/qxcbconnection_basic.h
@@ -98,6 +98,9 @@ public:
bool hasXSync() const { return m_hasXSync; }
bool hasBigRequest() const;
+ bool isAtLeastXRandR12() const { return m_hasXRandr && m_xrandr1Minor >= 2; }
+ bool isAtLeastXRandR15() const { return m_hasXRandr && m_xrandr1Minor >= 5; }
+
bool isAtLeastXI21() const { return m_xi2Enabled && m_xi2Minor >= 1; }
bool isAtLeastXI22() const { return m_xi2Enabled && m_xi2Minor >= 2; }
bool isXIEvent(xcb_generic_event_t *event) const;
@@ -144,6 +147,8 @@ private:
int m_xiOpCode = -1;
uint32_t m_xinputFirstEvent = 0;
+ int m_xrandr1Minor = -1;
+
uint32_t m_xfixesFirstEvent = 0;
uint32_t m_xrandrFirstEvent = 0;
uint32_t m_xkbFirstEvent = 0;
diff --git a/src/plugins/platforms/xcb/qxcbconnection_screens.cpp b/src/plugins/platforms/xcb/qxcbconnection_screens.cpp
index 867bbef020..22285b836d 100644
--- a/src/plugins/platforms/xcb/qxcbconnection_screens.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection_screens.cpp
@@ -63,8 +63,15 @@ void QXcbConnection::xrandrSelectEvents()
QXcbScreen* QXcbConnection::findScreenForCrtc(xcb_window_t rootWindow, xcb_randr_crtc_t crtc) const
{
for (QXcbScreen *screen : m_screens) {
- if (screen->root() == rootWindow && screen->crtc() == crtc)
- return screen;
+ if (screen->root() == rootWindow) {
+ if (screen->m_monitor) {
+ if (screen->crtcs().contains(crtc))
+ return screen;
+ } else {
+ if (screen->crtc() == crtc)
+ return screen;
+ }
+ }
}
return nullptr;
@@ -73,8 +80,15 @@ QXcbScreen* QXcbConnection::findScreenForCrtc(xcb_window_t rootWindow, xcb_randr
QXcbScreen* QXcbConnection::findScreenForOutput(xcb_window_t rootWindow, xcb_randr_output_t output) const
{
for (QXcbScreen *screen : m_screens) {
- if (screen->root() == rootWindow && screen->output() == output)
- return screen;
+ if (screen->root() == rootWindow) {
+ if (screen->m_monitor) {
+ if (screen->outputs().contains(output))
+ return screen;
+ } else {
+ if (screen->output() == output)
+ return screen;
+ }
+ }
}
return nullptr;
@@ -263,117 +277,84 @@ void QXcbConnection::destroyScreen(QXcbScreen *screen)
QWindowSystemInterface::handlePrimaryScreenChanged(newPrimary);
}
+ qCDebug(lcQpaScreen) << "destroyScreen: destroy" << screen;
QWindowSystemInterface::handleScreenRemoved(screen);
}
}
-void QXcbConnection::initializeScreens()
+void QXcbConnection::updateScreen_monitor(QXcbScreen *screen, xcb_randr_monitor_info_t *monitorInfo, xcb_timestamp_t timestamp)
+{
+ screen->setMonitor(monitorInfo, timestamp);
+
+ if (screen->isPrimary()) {
+ const int idx = m_screens.indexOf(screen);
+ if (idx > 0) {
+ qAsConst(m_screens).first()->setPrimary(false);
+ m_screens.swapItemsAt(0, idx);
+ }
+ screen->virtualDesktop()->setPrimaryScreen(screen);
+ QWindowSystemInterface::handlePrimaryScreenChanged(screen);
+ }
+ qCDebug(lcQpaScreen) << "updateScreen_monitor: update" << screen << "(Primary:" << screen->isPrimary() << ")";
+}
+
+QXcbScreen *QXcbConnection::createScreen_monitor(QXcbVirtualDesktop *virtualDesktop, xcb_randr_monitor_info_t *monitorInfo, xcb_timestamp_t timestamp)
+{
+ QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, monitorInfo, timestamp);
+
+ if (screen->isPrimary()) {
+ if (!m_screens.isEmpty())
+ qAsConst(m_screens).first()->setPrimary(false);
+
+ m_screens.prepend(screen);
+ } else {
+ m_screens.append(screen);
+ }
+ qCDebug(lcQpaScreen) << "createScreen_monitor: adding" << screen << "(Primary:" << screen->isPrimary() << ")";
+ virtualDesktop->addScreen(screen);
+ QWindowSystemInterface::handleScreenAdded(screen, screen->isPrimary());
+ return screen;
+}
+
+QXcbVirtualDesktop *QXcbConnection::virtualDesktopForNumber(int n) const
+{
+ for (QXcbVirtualDesktop *virtualDesktop : m_virtualDesktops) {
+ if (virtualDesktop->number() == n)
+ return virtualDesktop;
+ }
+
+ return nullptr;
+}
+
+QXcbScreen *QXcbConnection::findScreenForMonitorInfo(const QList<QPlatformScreen *> &screens, xcb_randr_monitor_info_t *monitorInfo)
+{
+ for (int i = 0; i < screens.size(); ++i) {
+ auto s = static_cast<QXcbScreen*>(screens[i]);
+ if (s->m_monitor && monitorInfo) {
+ QByteArray ba1 = atomName(s->m_monitor->name);
+ QByteArray ba2 = atomName(monitorInfo->name);
+ if (ba1 == ba2)
+ return s;
+ }
+ }
+
+ return nullptr;
+}
+
+void QXcbConnection::initializeScreens(bool initialized)
{
xcb_screen_iterator_t it = xcb_setup_roots_iterator(setup());
int xcbScreenNumber = 0; // screen number in the xcb sense
QXcbScreen *primaryScreen = nullptr;
while (it.rem) {
- // Each "screen" in xcb terminology is a virtual desktop,
- // potentially a collection of separate juxtaposed monitors.
- // But we want a separate QScreen for each output (e.g. DVI-I-1, VGA-1, etc.)
- // which will become virtual siblings.
- xcb_screen_t *xcbScreen = it.data;
- QXcbVirtualDesktop *virtualDesktop = new QXcbVirtualDesktop(this, xcbScreen, xcbScreenNumber);
- m_virtualDesktops.append(virtualDesktop);
- QList<QPlatformScreen *> siblings;
- if (hasXRandr()) {
- // RRGetScreenResourcesCurrent is fast but it may return nothing if the
- // configuration is not initialized wrt to the hardware. We should call
- // RRGetScreenResources in this case.
- auto resources_current = Q_XCB_REPLY(xcb_randr_get_screen_resources_current,
- xcb_connection(), xcbScreen->root);
- decltype(Q_XCB_REPLY(xcb_randr_get_screen_resources,
- xcb_connection(), xcbScreen->root)) resources;
- if (!resources_current) {
- qWarning("failed to get the current screen resources");
- } else {
- xcb_timestamp_t timestamp = 0;
- xcb_randr_output_t *outputs = nullptr;
- int outputCount = xcb_randr_get_screen_resources_current_outputs_length(resources_current.get());
- if (outputCount) {
- timestamp = resources_current->config_timestamp;
- outputs = xcb_randr_get_screen_resources_current_outputs(resources_current.get());
- } else {
- resources = Q_XCB_REPLY(xcb_randr_get_screen_resources,
- xcb_connection(), xcbScreen->root);
- if (!resources) {
- qWarning("failed to get the screen resources");
- } else {
- timestamp = resources->config_timestamp;
- outputCount = xcb_randr_get_screen_resources_outputs_length(resources.get());
- outputs = xcb_randr_get_screen_resources_outputs(resources.get());
- }
- }
-
- if (outputCount) {
- auto primary = Q_XCB_REPLY(xcb_randr_get_output_primary, xcb_connection(), xcbScreen->root);
- if (!primary) {
- qWarning("failed to get the primary output of the screen");
- } else {
- for (int i = 0; i < outputCount; i++) {
- auto output = Q_XCB_REPLY_UNCHECKED(xcb_randr_get_output_info,
- xcb_connection(), outputs[i], timestamp);
- // Invalid, disconnected or disabled output
- if (!output)
- continue;
-
- if (output->connection != XCB_RANDR_CONNECTION_CONNECTED) {
- qCDebug(lcQpaScreen, "Output %s is not connected", qPrintable(
- QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output.get()),
- xcb_randr_get_output_info_name_length(output.get()))));
- continue;
- }
-
- if (output->crtc == XCB_NONE) {
- qCDebug(lcQpaScreen, "Output %s is not enabled", qPrintable(
- QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output.get()),
- xcb_randr_get_output_info_name_length(output.get()))));
- continue;
- }
+ if (isAtLeastXRandR15())
+ initializeScreensFromMonitor(&it, xcbScreenNumber, primaryScreen, initialized);
+ else if (isAtLeastXRandR12())
+ initializeScreensFromOutput(&it, xcbScreenNumber, primaryScreen);
- QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, outputs[i], output.get());
- siblings << screen;
- m_screens << screen;
-
- // There can be multiple outputs per screen, use either
- // the first or an exact match. An exact match isn't
- // always available if primary->output is XCB_NONE
- // or currently disconnected output.
- if (primaryScreenNumber() == xcbScreenNumber) {
- if (!primaryScreen || (primary && outputs[i] == primary->output)) {
- if (primaryScreen)
- primaryScreen->setPrimary(false);
- primaryScreen = screen;
- primaryScreen->setPrimary(true);
- siblings.prepend(siblings.takeLast());
- }
- }
- }
- }
- }
- }
- }
- if (siblings.isEmpty()) {
- // If there are no XRandR outputs or XRandR extension is missing,
- // then create a fake/legacy screen.
- QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, XCB_NONE, nullptr);
- qCDebug(lcQpaScreen) << "created fake screen" << screen;
- m_screens << screen;
- if (primaryScreenNumber() == xcbScreenNumber) {
- primaryScreen = screen;
- primaryScreen->setPrimary(true);
- }
- siblings << screen;
- }
- virtualDesktop->setScreens(std::move(siblings));
xcb_screen_next(&it);
++xcbScreenNumber;
- } // for each xcb screen
+ }
for (QXcbVirtualDesktop *virtualDesktop : qAsConst(m_virtualDesktops))
virtualDesktop->subscribeToXFixesSelectionNotify();
@@ -390,11 +371,208 @@ void QXcbConnection::initializeScreens()
}
// Push the screens to QGuiApplication
- for (QXcbScreen *screen : qAsConst(m_screens)) {
- qCDebug(lcQpaScreen) << "adding" << screen << "(Primary:" << screen->isPrimary() << ")";
- QWindowSystemInterface::handleScreenAdded(screen, screen->isPrimary());
+ if (!initialized) {
+ for (QXcbScreen *screen : qAsConst(m_screens)) {
+ qCDebug(lcQpaScreen) << "adding" << screen << "(Primary:" << screen->isPrimary() << ")";
+ QWindowSystemInterface::handleScreenAdded(screen, screen->isPrimary());
+ }
}
- qCDebug(lcQpaScreen) << "primary output is" << qAsConst(m_screens).first()->name();
+ if (!m_screens.isEmpty())
+ qCDebug(lcQpaScreen) << "primary output is" << qAsConst(m_screens).first()->name();
+ }
+}
+
+void QXcbConnection::initializeScreensFromOutput(xcb_screen_iterator_t *it, int xcbScreenNumber, QXcbScreen *primaryScreen)
+{
+ // Each "screen" in xcb terminology is a virtual desktop,
+ // potentially a collection of separate juxtaposed monitors.
+ // But we want a separate QScreen for each output (e.g. DVI-I-1, VGA-1, etc.)
+ // which will become virtual siblings.
+ xcb_screen_t *xcbScreen = it->data;
+ QXcbVirtualDesktop *virtualDesktop = new QXcbVirtualDesktop(this, xcbScreen, xcbScreenNumber);
+ m_virtualDesktops.append(virtualDesktop);
+ QList<QPlatformScreen *> siblings;
+ if (isAtLeastXRandR12()) {
+ // RRGetScreenResourcesCurrent is fast but it may return nothing if the
+ // configuration is not initialized wrt to the hardware. We should call
+ // RRGetScreenResources in this case.
+ auto resources_current = Q_XCB_REPLY(xcb_randr_get_screen_resources_current,
+ xcb_connection(), xcbScreen->root);
+ decltype(Q_XCB_REPLY(xcb_randr_get_screen_resources,
+ xcb_connection(), xcbScreen->root)) resources;
+ if (!resources_current) {
+ qWarning("failed to get the current screen resources");
+ } else {
+ xcb_timestamp_t timestamp = 0;
+ xcb_randr_output_t *outputs = nullptr;
+ int outputCount = xcb_randr_get_screen_resources_current_outputs_length(resources_current.get());
+ if (outputCount) {
+ timestamp = resources_current->config_timestamp;
+ outputs = xcb_randr_get_screen_resources_current_outputs(resources_current.get());
+ } else {
+ resources = Q_XCB_REPLY(xcb_randr_get_screen_resources,
+ xcb_connection(), xcbScreen->root);
+ if (!resources) {
+ qWarning("failed to get the screen resources");
+ } else {
+ timestamp = resources->config_timestamp;
+ outputCount = xcb_randr_get_screen_resources_outputs_length(resources.get());
+ outputs = xcb_randr_get_screen_resources_outputs(resources.get());
+ }
+ }
+
+ if (outputCount) {
+ auto primary = Q_XCB_REPLY(xcb_randr_get_output_primary, xcb_connection(), xcbScreen->root);
+ if (!primary) {
+ qWarning("failed to get the primary output of the screen");
+ } else {
+ for (int i = 0; i < outputCount; i++) {
+ auto output = Q_XCB_REPLY_UNCHECKED(xcb_randr_get_output_info,
+ xcb_connection(), outputs[i], timestamp);
+ // Invalid, disconnected or disabled output
+ if (!output)
+ continue;
+
+ if (output->connection != XCB_RANDR_CONNECTION_CONNECTED) {
+ qCDebug(lcQpaScreen, "Output %s is not connected", qPrintable(
+ QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output.get()),
+ xcb_randr_get_output_info_name_length(output.get()))));
+ continue;
+ }
+
+ if (output->crtc == XCB_NONE) {
+ qCDebug(lcQpaScreen, "Output %s is not enabled", qPrintable(
+ QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output.get()),
+ xcb_randr_get_output_info_name_length(output.get()))));
+ continue;
+ }
+
+ QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, outputs[i], output.get());
+ siblings << screen;
+ m_screens << screen;
+
+ // There can be multiple outputs per screen, use either
+ // the first or an exact match. An exact match isn't
+ // always available if primary->output is XCB_NONE
+ // or currently disconnected output.
+ if (primaryScreenNumber() == xcbScreenNumber) {
+ if (!primaryScreen || (primary && outputs[i] == primary->output)) {
+ if (primaryScreen)
+ primaryScreen->setPrimary(false);
+ primaryScreen = screen;
+ primaryScreen->setPrimary(true);
+ siblings.prepend(siblings.takeLast());
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if (siblings.isEmpty()) {
+ // If there are no XRandR outputs or XRandR extension is missing,
+ // then create a fake/legacy screen.
+ QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, XCB_NONE, nullptr);
+ qCDebug(lcQpaScreen) << "created fake screen" << screen;
+ m_screens << screen;
+ if (primaryScreenNumber() == xcbScreenNumber) {
+ primaryScreen = screen;
+ primaryScreen->setPrimary(true);
+ }
+ siblings << screen;
}
+ virtualDesktop->setScreens(std::move(siblings));
+}
+
+void QXcbConnection::initializeScreensFromMonitor(xcb_screen_iterator_t *it, int xcbScreenNumber, QXcbScreen *primaryScreen, bool initialized)
+{
+ // Each "screen" in xcb terminology is a virtual desktop,
+ // potentially a collection of separate juxtaposed monitors.
+ // But we want a separate QScreen for each output (e.g. DVI-I-1, VGA-1, etc.)
+ // which will become virtual siblings.
+ xcb_screen_t *xcbScreen = it->data;
+ QXcbVirtualDesktop *virtualDesktop = nullptr;
+ if (initialized)
+ virtualDesktop = virtualDesktopForNumber(xcbScreenNumber);
+ if (!virtualDesktop) {
+ virtualDesktop = new QXcbVirtualDesktop(this, xcbScreen, xcbScreenNumber);
+ m_virtualDesktops.append(virtualDesktop);
+ }
+ QList<QPlatformScreen*> old = virtualDesktop->m_screens;
+ if (initialized)
+ m_screens.clear();
+
+ QList<QPlatformScreen *> siblings;
+
+ xcb_randr_get_monitors_cookie_t monitors_c = xcb_randr_get_monitors(xcb_connection(), xcbScreen->root, 1);
+ xcb_randr_get_monitors_reply_t *monitors_r = xcb_randr_get_monitors_reply(xcb_connection(), monitors_c, nullptr);
+
+ if (!monitors_r) {
+ qWarning("RANDR GetMonitors failed; this should not be possible");
+ return;
+ }
+
+ xcb_randr_monitor_info_iterator_t monitor_iter = xcb_randr_get_monitors_monitors_iterator(monitors_r);
+ while (monitor_iter.rem) {
+ xcb_randr_monitor_info_t *monitor_info = monitor_iter.data;
+ QXcbScreen *screen = nullptr;
+ if (!initialized) {
+ screen = new QXcbScreen(this, virtualDesktop, monitor_info, monitors_r->timestamp);
+ m_screens << screen;
+ } else {
+ screen = findScreenForMonitorInfo(virtualDesktop->m_screens, monitor_info);
+ if (!screen) {
+ screen = createScreen_monitor(virtualDesktop, monitor_info, monitors_r->timestamp);
+ QHighDpiScaling::updateHighDpiScaling();
+ } else {
+ m_screens << screen;
+ updateScreen_monitor(screen, monitor_info, monitors_r->timestamp);
+ old.removeAll(screen);
+ }
+ }
+
+ siblings << screen;
+
+ if (primaryScreenNumber() == xcbScreenNumber) {
+ primaryScreen = screen;
+ primaryScreen->setPrimary(true);
+ }
+
+ xcb_randr_monitor_info_next(&monitor_iter);
+ }
+
+ if (siblings.isEmpty()) {
+ QXcbScreen *screen = nullptr;
+ if (initialized && !old.isEmpty()) {
+ // If there are no other screens on the same virtual desktop,
+ // then transform the physical screen into a fake screen.
+ screen = static_cast<QXcbScreen *>(old.takeFirst());
+ const QString nameWas = screen->name();
+ screen->setMonitor(nullptr, XCB_NONE);
+ qCDebug(lcQpaScreen) << "transformed" << nameWas << "to fake" << screen;
+ } else {
+ // If there are no XRandR outputs or XRandR extension is missing,
+ // then create a fake/legacy screen.
+ screen = new QXcbScreen(this, virtualDesktop, nullptr);
+ qCDebug(lcQpaScreen) << "create a fake screen: " << screen;
+ }
+
+ if (primaryScreenNumber() == xcbScreenNumber) {
+ primaryScreen = screen;
+ primaryScreen->setPrimary(true);
+ }
+ siblings << screen;
+ m_screens << screen;
+ }
+
+ if (initialized) {
+ for (QPlatformScreen *ps : old) {
+ virtualDesktop->removeScreen(ps);
+ qCDebug(lcQpaScreen) << "destroy screen: " << ps;
+ QWindowSystemInterface::handleScreenRemoved(ps);
+ }
+ }
+
+ virtualDesktop->setScreens(std::move(siblings));
}
diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
index 4657c2ac89..4c39c7ee3e 100644
--- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
@@ -55,9 +55,15 @@ using qt_xcb_input_device_event_t = xcb_input_button_press_event_t;
struct qt_xcb_input_event_mask_t {
xcb_input_event_mask_t header;
- uint32_t mask;
+ alignas(4) uint8_t mask[4] = {};
};
+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 +71,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 +82,23 @@ 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);
}
- qt_xcb_input_event_mask_t mask;
mask.header.deviceid = XCB_INPUT_DEVICE_ALL;
mask.header.mask_len = 1;
- mask.mask = bitMask;
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);
@@ -639,6 +645,7 @@ static inline qreal fixed1616ToReal(xcb_input_fp1616_t val)
void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
{
auto *xiEvent = reinterpret_cast<qt_xcb_input_device_event_t *>(event);
+ setTime(xiEvent->time);
if (m_xiSlavePointerIds.contains(xiEvent->deviceid)) {
if (!m_duringSystemMoveResize)
return;
@@ -973,22 +980,23 @@ 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);
}
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, 1, 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"
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/qxcbkeyboard.cpp b/src/plugins/platforms/xcb/qxcbkeyboard.cpp
index 7495d0fdc3..9ab804ca1b 100644
--- a/src/plugins/platforms/xcb/qxcbkeyboard.cpp
+++ b/src/plugins/platforms/xcb/qxcbkeyboard.cpp
@@ -60,11 +60,11 @@ Qt::KeyboardModifiers QXcbKeyboard::translateModifiers(int s) const
ret |= Qt::ShiftModifier;
if (s & XCB_MOD_MASK_CONTROL)
ret |= Qt::ControlModifier;
- if ((s & rmod_masks.alt) == rmod_masks.alt)
+ if (s & rmod_masks.alt)
ret |= Qt::AltModifier;
- if ((s & rmod_masks.meta) == rmod_masks.meta)
+ if (s & rmod_masks.meta)
ret |= Qt::MetaModifier;
- if ((s & rmod_masks.altgr) == rmod_masks.altgr)
+ if (s & rmod_masks.altgr)
ret |= Qt::GroupSwitchModifier;
return ret;
}
diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp
index 95c552a468..55633aade7 100644
--- a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp
+++ b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp
@@ -118,7 +118,7 @@ void *QXcbNativeInterface::nativeResourceForIntegration(const QByteArray &resour
case RootWindow:
result = rootWindow();
break;
- case Display:
+ case XDisplay:
result = display();
break;
case AtspiBus:
@@ -155,7 +155,7 @@ void *QXcbNativeInterface::nativeResourceForScreen(const QByteArray &resourceStr
const QXcbScreen *xcbScreen = static_cast<QXcbScreen *>(screen->handle());
switch (resourceType(lowerCaseResource)) {
- case Display:
+ case XDisplay:
#if QT_CONFIG(xcb_xlib)
result = xcbScreen->connection()->xlib_display();
#endif
@@ -203,7 +203,7 @@ void *QXcbNativeInterface::nativeResourceForWindow(const QByteArray &resourceStr
return result;
switch (resourceType(lowerCaseResource)) {
- case Display:
+ case XDisplay:
result = displayForWindow(window);
break;
case Connection:
@@ -365,18 +365,17 @@ void *QXcbNativeInterface::rootWindow()
return nullptr;
}
-void *QXcbNativeInterface::display()
+Display *QXcbNativeInterface::display() const
{
#if QT_CONFIG(xcb_xlib)
QXcbIntegration *integration = QXcbIntegration::instance();
- QXcbConnection *connection = integration->connection();
- if (connection)
- return connection->xlib_display();
+ if (QXcbConnection *connection = integration->connection())
+ return reinterpret_cast<Display *>(connection->xlib_display());
#endif
return nullptr;
}
-void *QXcbNativeInterface::connection()
+xcb_connection_t *QXcbNativeInterface::connection() const
{
QXcbIntegration *integration = QXcbIntegration::instance();
return integration->connection()->xcb_connection();
diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.h b/src/plugins/platforms/xcb/qxcbnativeinterface.h
index 4656f46be5..5508d57679 100644
--- a/src/plugins/platforms/xcb/qxcbnativeinterface.h
+++ b/src/plugins/platforms/xcb/qxcbnativeinterface.h
@@ -45,6 +45,8 @@
#include <QtCore/QRect>
+#include <QtGui/qguiapplication.h>
+
#include "qxcbexport.h"
#include "qxcbconnection.h"
@@ -54,11 +56,12 @@ class QXcbScreen;
class QXcbNativeInterfaceHandler;
class Q_XCB_EXPORT QXcbNativeInterface : public QPlatformNativeInterface
+ , public QNativeInterface::QX11Application
{
Q_OBJECT
public:
enum ResourceType {
- Display,
+ XDisplay,
Connection,
Screen,
AppTime,
@@ -109,9 +112,11 @@ public:
void *startupId();
void *x11Screen();
void *rootWindow();
- void *display();
+
+ Display *display() const override;
+ xcb_connection_t *connection() const override;
+
void *atspiBus();
- void *connection();
static void setStartupId(const char *);
static void setAppTime(QScreen *screen, xcb_timestamp_t time);
static void setAppUserTime(QScreen *screen, xcb_timestamp_t time);
diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp
index c084b17782..effd7c6da6 100644
--- a/src/plugins/platforms/xcb/qxcbscreen.cpp
+++ b/src/plugins/platforms/xcb/qxcbscreen.cpp
@@ -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
@@ -530,12 +530,13 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDe
xcb_randr_output_t outputId, xcb_randr_get_output_info_reply_t *output)
: QXcbObject(connection)
, m_virtualDesktop(virtualDesktop)
+ , m_monitor(nullptr)
, m_output(outputId)
, m_crtc(output ? output->crtc : XCB_NONE)
, m_outputName(getOutputName(output))
, m_outputSizeMillimeters(output ? QSize(output->mm_width, output->mm_height) : QSize())
{
- if (connection->hasXRandr()) {
+ if (connection->isAtLeastXRandR12()) {
xcb_randr_select_input(xcb_connection(), screen()->root, true);
auto crtc = Q_XCB_REPLY_UNCHECKED(xcb_randr_get_crtc_info, xcb_connection(),
m_crtc, output ? output->timestamp : 0);
@@ -567,7 +568,7 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDe
m_colorSpace = QColorSpace::fromIccProfile(data);
}
}
- if (connection->hasXRandr()) { // Parse EDID
+ if (connection->isAtLeastXRandR12()) { // Parse EDID
QByteArray edid = getEdid();
if (m_edid.parse(edid)) {
qCDebug(lcQpaScreen, "EDID data for output \"%s\": identifier '%s', manufacturer '%s',"
@@ -610,6 +611,119 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDe
m_colorSpace = QColorSpace::SRgb;
}
+QXcbScreen::QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDesktop,
+ xcb_randr_monitor_info_t *monitorInfo, xcb_timestamp_t timestamp)
+ : QXcbObject(connection)
+ , m_virtualDesktop(virtualDesktop)
+ , m_monitor(monitorInfo)
+{
+ setMonitor(monitorInfo, timestamp);
+}
+
+void QXcbScreen::setMonitor(xcb_randr_monitor_info_t *monitorInfo, xcb_timestamp_t timestamp)
+{
+ if (!connection()->isAtLeastXRandR15())
+ return;
+
+ m_outputs.clear();
+ m_crtcs.clear();
+
+ if (!monitorInfo) {
+ m_monitor = nullptr;
+ m_output = XCB_NONE;
+ m_crtc = XCB_NONE;
+ m_mode = XCB_NONE;
+ m_outputName = defaultName();
+ // TODO: Send an event to the QScreen instance that the screen changed its name
+ return;
+ }
+
+ xcb_randr_select_input(xcb_connection(), screen()->root, true);
+
+ m_monitor = monitorInfo;
+ QRect monitorGeometry = QRect(m_monitor->x, m_monitor->y,
+ m_monitor->width, m_monitor->height);
+
+ int outputCount = xcb_randr_monitor_info_outputs_length(m_monitor);
+ xcb_randr_output_t *outputs = nullptr;
+ if (outputCount) {
+ outputs = xcb_randr_monitor_info_outputs(m_monitor);
+ for (int i = 0; i < outputCount; i++) {
+ auto output = Q_XCB_REPLY_UNCHECKED(xcb_randr_get_output_info,
+ xcb_connection(), outputs[i], timestamp);
+ // Invalid, disconnected or disabled output
+ if (!output)
+ continue;
+
+ if (output->connection != XCB_RANDR_CONNECTION_CONNECTED) {
+ qCDebug(lcQpaScreen, "Output %s is not connected", qPrintable(
+ QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output.get()),
+ xcb_randr_get_output_info_name_length(output.get()))));
+ continue;
+ }
+
+ if (output->crtc == XCB_NONE) {
+ qCDebug(lcQpaScreen, "Output %s is not enabled", qPrintable(
+ QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output.get()),
+ xcb_randr_get_output_info_name_length(output.get()))));
+ continue;
+ }
+
+ m_outputs << outputs[i];
+ m_crtcs << output->crtc;
+ }
+ }
+
+ if (m_crtcs.size() == 1) {
+ auto crtc = Q_XCB_REPLY(xcb_randr_get_crtc_info,
+ xcb_connection(), m_crtcs[0], timestamp);
+ m_singlescreen = (monitorGeometry == (QRect(crtc->x, crtc->y, crtc->width, crtc->height)));
+ if (m_singlescreen) {
+ if (crtc->mode) {
+ if (crtc->rotation == XCB_RANDR_ROTATION_ROTATE_90 ||
+ crtc->rotation == XCB_RANDR_ROTATION_ROTATE_270)
+ std::swap(crtc->width, crtc->height);
+ updateGeometry(QRect(crtc->x, crtc->y, crtc->width, crtc->height), crtc->rotation);
+ if (mode() != crtc->mode)
+ updateRefreshRate(crtc->mode);
+ }
+ }
+ }
+
+ if (!m_singlescreen)
+ m_geometry = monitorGeometry;
+ m_availableGeometry = m_virtualDesktop->availableGeometry(m_geometry);
+ if (m_geometry.isEmpty())
+ m_geometry = QRect(QPoint(), virtualDesktop()->size());
+ if (m_availableGeometry.isEmpty())
+ m_availableGeometry = m_virtualDesktop->availableGeometry(m_geometry);
+
+ m_sizeMillimeters = sizeInMillimeters(m_geometry.size(), m_virtualDesktop->dpi());
+
+ if (m_sizeMillimeters.isEmpty())
+ m_sizeMillimeters = virtualDesktop()->physicalSize();
+
+ QByteArray ba = connection()->atomName(monitorInfo->name);
+ m_outputName = getName(monitorInfo);
+
+ if (monitorInfo->primary)
+ m_primary = true;
+
+ m_cursor = new QXcbCursor(connection(), this);
+}
+
+QString QXcbScreen::defaultName()
+{
+ QString name;
+ QByteArray displayName = connection()->displayName();
+ int dotPos = displayName.lastIndexOf('.');
+ if (dotPos != -1)
+ displayName.truncate(dotPos);
+ name = QString::fromLocal8Bit(displayName) + QLatin1Char('.')
+ + QString::number(m_virtualDesktop->number());
+ return name;
+}
+
QXcbScreen::~QXcbScreen()
{
delete m_cursor;
@@ -622,6 +736,18 @@ QString QXcbScreen::getOutputName(xcb_randr_get_output_info_reply_t *outputInfo)
name = QString::fromUtf8((const char*)xcb_randr_get_output_info_name(outputInfo),
xcb_randr_get_output_info_name_length(outputInfo));
} else {
+ name = defaultName();
+ }
+ return name;
+}
+
+QString QXcbScreen::getName(xcb_randr_monitor_info_t *monitorInfo)
+{
+ QString name;
+ QByteArray ba = connection()->atomName(monitorInfo->name);
+ if (!ba.isEmpty()) {
+ name = QString::fromLatin1(ba.constData());
+ } else {
QByteArray displayName = connection()->displayName();
int dotPos = displayName.lastIndexOf('.');
if (dotPos != -1)
@@ -758,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.
@@ -773,6 +899,7 @@ QPlatformCursor *QXcbScreen::cursor() const
void QXcbScreen::setOutput(xcb_randr_output_t outputId,
xcb_randr_get_output_info_reply_t *outputInfo)
{
+ m_monitor = nullptr;
m_output = outputId;
m_crtc = outputInfo ? outputInfo->crtc : XCB_NONE;
m_mode = XCB_NONE;
@@ -782,7 +909,7 @@ void QXcbScreen::setOutput(xcb_randr_output_t outputId,
void QXcbScreen::updateGeometry(xcb_timestamp_t timestamp)
{
- if (!connection()->hasXRandr())
+ if (!connection()->isAtLeastXRandR12())
return;
auto crtc = Q_XCB_REPLY_UNCHECKED(xcb_randr_get_crtc_info, xcb_connection(),
@@ -838,7 +965,7 @@ void QXcbScreen::updateAvailableGeometry()
void QXcbScreen::updateRefreshRate(xcb_randr_mode_t mode)
{
- if (!connection()->hasXRandr())
+ if (!connection()->isAtLeastXRandR12())
return;
if (m_mode == mode)
@@ -974,7 +1101,7 @@ QByteArray QXcbScreen::getOutputProperty(xcb_atom_t atom) const
QByteArray QXcbScreen::getEdid() const
{
QByteArray result;
- if (!connection()->hasXRandr())
+ if (!connection()->isAtLeastXRandR12())
return result;
// Try a bunch of atoms
diff --git a/src/plugins/platforms/xcb/qxcbscreen.h b/src/plugins/platforms/xcb/qxcbscreen.h
index ff30f599d6..04d90e0c68 100644
--- a/src/plugins/platforms/xcb/qxcbscreen.h
+++ b/src/plugins/platforms/xcb/qxcbscreen.h
@@ -138,6 +138,8 @@ private:
QMap<xcb_visualid_t, quint8> m_visualDepths;
mutable QMap<xcb_visualid_t, xcb_colormap_t> m_visualColormaps;
uint16_t m_rotation = 0;
+
+ friend class QXcbConnection;
};
class Q_XCB_EXPORT QXcbScreen : public QXcbObject, public QPlatformScreen
@@ -146,9 +148,12 @@ class Q_XCB_EXPORT QXcbScreen : public QXcbObject, public QPlatformScreen
public:
QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDesktop,
xcb_randr_output_t outputId, xcb_randr_get_output_info_reply_t *outputInfo);
+ QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDesktop,
+ xcb_randr_monitor_info_t *monitorInfo, xcb_timestamp_t timestamp = XCB_NONE);
~QXcbScreen();
QString getOutputName(xcb_randr_get_output_info_reply_t *outputInfo);
+ QString getName(xcb_randr_monitor_info_t *monitorInfo);
QPixmap grabWindow(WId window, int x, int y, int width, int height) const override;
@@ -184,9 +189,14 @@ public:
xcb_randr_crtc_t crtc() const { return m_crtc; }
xcb_randr_mode_t mode() const { return m_mode; }
+ QList<xcb_randr_output_t> outputs() const { return m_outputs; }
+ QList<xcb_randr_crtc_t> crtcs() const { return m_crtcs; }
+
void setOutput(xcb_randr_output_t outputId,
xcb_randr_get_output_info_reply_t *outputInfo);
void setCrtc(xcb_randr_crtc_t crtc) { m_crtc = crtc; }
+ void setMonitor(xcb_randr_monitor_info_t *monitorInfo, xcb_timestamp_t timestamp = XCB_NONE);
+ QString defaultName();
void windowShown(QXcbWindow *window);
QString windowManagerName() const { return m_virtualDesktop->windowManagerName(); }
@@ -219,11 +229,17 @@ private:
QByteArray getEdid() const;
QXcbVirtualDesktop *m_virtualDesktop;
+ xcb_randr_monitor_info_t *m_monitor;
xcb_randr_output_t m_output;
xcb_randr_crtc_t m_crtc;
xcb_randr_mode_t m_mode = XCB_NONE;
bool m_primary = false;
+ bool m_singlescreen = false;
+
+ QList<xcb_randr_output_t> m_outputs;
+ QList<xcb_randr_crtc_t> m_crtcs;
+
QString m_outputName;
QSizeF m_outputSizeMillimeters;
QSizeF m_sizeMillimeters;
@@ -234,6 +250,9 @@ private:
QXcbCursor *m_cursor;
qreal m_refreshRate = 60.0;
QEdidParser m_edid;
+
+ friend class QXcbConnection;
+ friend class QXcbVirtualDesktop;
};
#ifndef QT_NO_DEBUG_STREAM
diff --git a/src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp b/src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp
index ff5ad98cd2..6fae930497 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)
diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp
index ea8e1f479d..00bca0def9 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.cpp
+++ b/src/plugins/platforms/xcb/qxcbwindow.cpp
@@ -548,6 +548,11 @@ void QXcbWindow::setGeometry(const QRect &rect)
{
QPlatformWindow::setGeometry(rect);
+ if (shouldDeferTask(Task::SetGeometry)) {
+ m_deferredGeometry = rect;
+ return;
+ }
+
propagateSizeHints();
QXcbScreen *currentScreen = xcbScreen();
@@ -672,6 +677,9 @@ void QXcbWindow::setVisible(bool visible)
void QXcbWindow::show()
{
+ if (shouldDeferTask(Task::Map))
+ return;
+
if (window()->isTopLevel()) {
// update WM_NORMAL_HINTS
@@ -722,6 +730,10 @@ void QXcbWindow::show()
void QXcbWindow::hide()
{
+ if (shouldDeferTask(Task::Unmap))
+ return;
+
+ m_wmStateValid = false;
xcb_unmap_window(xcb_connection(), m_window);
// send synthetic UnmapNotify event according to icccm 4.1.4
@@ -881,6 +893,9 @@ QXcbWindow::NetWmStates QXcbWindow::netWmStates()
void QXcbWindow::setWindowFlags(Qt::WindowFlags flags)
{
+ if (shouldDeferTask(Task::SetWindowFlags))
+ return;
+
Qt::WindowType type = static_cast<Qt::WindowType>(int(flags & Qt::WindowType_Mask));
if (type == Qt::ToolTip)
@@ -910,6 +925,8 @@ void QXcbWindow::setWindowFlags(Qt::WindowFlags flags)
setTransparentForMouseEvents(flags & Qt::WindowTransparentForInput);
updateDoesNotAcceptFocus(flags & Qt::WindowDoesNotAcceptFocus);
+
+ m_isWmManagedWindow = !(flags & Qt::X11BypassWindowManagerHint);
}
void QXcbWindow::setMotifWmHints(Qt::WindowFlags flags)
@@ -1109,6 +1126,9 @@ void QXcbWindow::setWindowState(Qt::WindowStates state)
if (state == m_windowState)
return;
+ if (shouldDeferTask(Task::SetWindowState))
+ return;
+
// unset old state
if (m_windowState & Qt::WindowMinimized)
xcb_map_window(xcb_connection(), m_window);
@@ -1751,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()
@@ -1826,6 +1846,10 @@ 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) {
+ m_wmStateValid = true;
+ handleDeferredTasks();
+ }
}
}
@@ -2140,30 +2164,98 @@ void QXcbWindow::handleLeaveNotifyEvent(const xcb_leave_notify_event_t *event)
handleLeaveNotifyEvent(event->root_x, event->root_y, event->mode, event->detail, event->time);
}
+bool QXcbWindow::shouldDeferTask(Task task)
+{
+ if (m_wmStateValid)
+ return false;
+
+ m_deferredTasks.append(task);
+ return true;
+}
+
+void QXcbWindow::handleDeferredTasks()
+{
+ Q_ASSERT(m_wmStateValid == true);
+ if (m_deferredTasks.isEmpty())
+ return;
+
+ bool map = false;
+ bool unmap = false;
+
+ QVector<Task> tasks;
+ for (auto taskIt = m_deferredTasks.rbegin(); taskIt != m_deferredTasks.rend(); ++taskIt) {
+ if (!tasks.contains(*taskIt))
+ tasks.prepend(*taskIt);
+ }
+
+ for (Task task : tasks) {
+ switch (task) {
+ case Task::Map:
+ map = true;
+ unmap = false;
+ break;
+ case Task::Unmap:
+ unmap = true;
+ map = false;
+ break;
+ case Task::SetGeometry:
+ setGeometry(m_deferredGeometry);
+ break;
+ case Task::SetWindowFlags:
+ setWindowFlags(window()->flags());
+ break;
+ case Task::SetWindowState:
+ setWindowState(window()->windowState());
+ break;
+ }
+ }
+ m_deferredTasks.clear();
+
+ if (map) {
+ Q_ASSERT(unmap == false);
+ show();
+ }
+ if (unmap) {
+ Q_ASSERT(map == false);
+ hide();
+ }
+}
+
void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *event)
{
connection()->setTime(event->time);
- const bool propertyDeleted = event->state == XCB_PROPERTY_DELETE;
-
- if (event->atom == atom(QXcbAtom::_NET_WM_STATE) || event->atom == atom(QXcbAtom::WM_STATE)) {
- if (propertyDeleted)
+ const bool wmStateChanged = event->atom == atom(QXcbAtom::WM_STATE);
+ const bool netWmStateChanged = event->atom == atom(QXcbAtom::_NET_WM_STATE);
+ if (netWmStateChanged || wmStateChanged) {
+ if (wmStateChanged && !m_wmStateValid && m_isWmManagedWindow) {
+ // ICCCM 4.1.4
+ // Clients that want to re-use a client window (e.g. by mapping it again)
+ // after withdrawing it must wait for the withdrawal to be complete before
+ // proceeding. The preferred method for doing this is for clients to wait for
+ // a window manager to update or remove the WM_STATE property.
+ m_wmStateValid = true;
+ handleDeferredTasks();
+ }
+ if (event->state == XCB_PROPERTY_DELETE)
return;
- Qt::WindowStates newState = Qt::WindowNoState;
-
- if (event->atom == atom(QXcbAtom::WM_STATE)) { // WM_STATE: Quick check for 'Minimize'.
+ if (wmStateChanged) {
auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(),
0, m_window, atom(QXcbAtom::WM_STATE),
XCB_ATOM_ANY, 0, 1024);
if (reply && reply->format == 32 && reply->type == atom(QXcbAtom::WM_STATE)) {
- const quint32 *data = (const quint32 *)xcb_get_property_value(reply.get());
- if (reply->length != 0)
- m_minimized = (data[0] == XCB_ICCCM_WM_STATE_ICONIC
- || (data[0] == XCB_ICCCM_WM_STATE_WITHDRAWN && m_minimized));
+ auto data = static_cast<const quint32 *>(xcb_get_property_value(reply.get()));
+ if (reply->length != 0) {
+ const bool changedToWithdrawn = data[0] == XCB_ICCCM_WM_STATE_WITHDRAWN;
+ const bool changedToIconic = data[0] == XCB_ICCCM_WM_STATE_ICONIC;
+ m_minimized = changedToIconic || (changedToWithdrawn && m_minimized);
+ }
}
}
+ // _NET_WM_STATE handling
+ Qt::WindowStates newState = Qt::WindowNoState;
const NetWmStates states = netWmStates();
// _NET_WM_STATE_HIDDEN should be set by the Window Manager to indicate that a window would
// not be visible on the screen if its desktop/viewport were active and its coordinates were
@@ -2185,7 +2277,6 @@ void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *ev
if ((m_windowState & Qt::WindowMinimized) && connection()->mouseGrabber() == this)
connection()->setMouseGrabber(nullptr);
}
- return;
} else if (event->atom == atom(QXcbAtom::_NET_FRAME_EXTENTS)) {
m_dirtyFrameMargins = true;
}
diff --git a/src/plugins/platforms/xcb/qxcbwindow.h b/src/plugins/platforms/xcb/qxcbwindow.h
index 5e5aa96629..f7bed7f67b 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.h
+++ b/src/plugins/platforms/xcb/qxcbwindow.h
@@ -74,6 +74,14 @@ public:
Q_DECLARE_FLAGS(NetWmStates, NetWmState)
+ enum Task {
+ Map,
+ Unmap,
+ SetGeometry,
+ SetWindowFlags,
+ SetWindowState
+ };
+
QXcbWindow(QWindow *window);
~QXcbWindow();
@@ -143,6 +151,9 @@ public:
QXcbWindow *toWindow() override;
+ bool shouldDeferTask(Task task);
+ void handleDeferredTasks();
+
void handleMouseEvent(xcb_timestamp_t time, const QPoint &local, const QPoint &global,
Qt::KeyboardModifiers modifiers, QEvent::Type type, Qt::MouseEventSource source);
@@ -276,6 +287,11 @@ protected:
int m_swapInterval = -1;
qreal m_sizeHintsScaleFactor = 1.0;
+
+ bool m_wmStateValid = true;
+ QVector<Task> m_deferredTasks;
+ bool m_isWmManagedWindow = true;
+ QRect m_deferredGeometry;
};
class QXcbForeignWindow : public QXcbWindow
diff --git a/src/plugins/platformthemes/gtk3/CMakeLists.txt b/src/plugins/platformthemes/gtk3/CMakeLists.txt
index da61354237..62e752bd92 100644
--- a/src/plugins/platformthemes/gtk3/CMakeLists.txt
+++ b/src/plugins/platformthemes/gtk3/CMakeLists.txt
@@ -9,7 +9,7 @@ qt_find_package(X11) # special case
qt_internal_add_plugin(QGtk3ThemePlugin
OUTPUT_NAME qgtk3
- TYPE platformthemes
+ PLUGIN_TYPE platformthemes
DEFAULT_IF FALSE
SOURCES
main.cpp
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/CMakeLists.txt b/src/plugins/platformthemes/xdgdesktopportal/CMakeLists.txt
index 980ef4a44a..82fb94e31d 100644
--- a/src/plugins/platformthemes/xdgdesktopportal/CMakeLists.txt
+++ b/src/plugins/platformthemes/xdgdesktopportal/CMakeLists.txt
@@ -6,7 +6,7 @@
qt_internal_add_plugin(QXdgDesktopPortalThemePlugin
OUTPUT_NAME qxdgdesktopportal
- TYPE platformthemes
+ PLUGIN_TYPE platformthemes
DEFAULT_IF FALSE
SOURCES
main.cpp
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/printsupport/cups/CMakeLists.txt b/src/plugins/printsupport/cups/CMakeLists.txt
index 0a8b2253af..1132ff0845 100644
--- a/src/plugins/printsupport/cups/CMakeLists.txt
+++ b/src/plugins/printsupport/cups/CMakeLists.txt
@@ -8,7 +8,7 @@ qt_find_package(Cups PROVIDED_TARGETS Cups::Cups) # special case
qt_internal_add_plugin(QCupsPrinterSupportPlugin
OUTPUT_NAME cupsprintersupport
- TYPE printsupport
+ PLUGIN_TYPE printsupport
SOURCES
main.cpp
qcupsprintengine.cpp qcupsprintengine_p.h
diff --git a/src/plugins/sqldrivers/.cmake.conf b/src/plugins/sqldrivers/.cmake.conf
index 213991f589..aae2a6822b 100644
--- a/src/plugins/sqldrivers/.cmake.conf
+++ b/src/plugins/sqldrivers/.cmake.conf
@@ -1 +1 @@
-set(QT_REPO_MODULE_VERSION "6.1.0")
+set(QT_REPO_MODULE_VERSION "6.3.0")
diff --git a/src/plugins/sqldrivers/db2/CMakeLists.txt b/src/plugins/sqldrivers/db2/CMakeLists.txt
index de90d92581..1c693faa3b 100644
--- a/src/plugins/sqldrivers/db2/CMakeLists.txt
+++ b/src/plugins/sqldrivers/db2/CMakeLists.txt
@@ -6,7 +6,7 @@
qt_internal_add_plugin(QDB2DriverPlugin
OUTPUT_NAME qsqldb2
- TYPE sqldrivers
+ PLUGIN_TYPE sqldrivers
SOURCES
main.cpp
qsql_db2.cpp qsql_db2_p.h
diff --git a/src/plugins/sqldrivers/ibase/CMakeLists.txt b/src/plugins/sqldrivers/ibase/CMakeLists.txt
index 4b914401ed..8cd5c24dfc 100644
--- a/src/plugins/sqldrivers/ibase/CMakeLists.txt
+++ b/src/plugins/sqldrivers/ibase/CMakeLists.txt
@@ -1,6 +1,6 @@
qt_internal_add_plugin(QIBaseDriverPlugin
OUTPUT_NAME qsqlibase
- TYPE sqldrivers
+ PLUGIN_TYPE sqldrivers
SOURCES
main.cpp
qsql_ibase.cpp qsql_ibase_p.h
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/CMakeLists.txt b/src/plugins/sqldrivers/mysql/CMakeLists.txt
index 0679bcb698..a05fc513f1 100644
--- a/src/plugins/sqldrivers/mysql/CMakeLists.txt
+++ b/src/plugins/sqldrivers/mysql/CMakeLists.txt
@@ -6,7 +6,7 @@
qt_internal_add_plugin(QMYSQLDriverPlugin
OUTPUT_NAME qsqlmysql
- TYPE sqldrivers
+ PLUGIN_TYPE sqldrivers
SOURCES
main.cpp
qsql_mysql.cpp qsql_mysql_p.h
diff --git a/src/plugins/sqldrivers/mysql/qsql_mysql.cpp b/src/plugins/sqldrivers/mysql/qsql_mysql.cpp
index 12e15bfc47..caf406da46 100644
--- a/src/plugins/sqldrivers/mysql/qsql_mysql.cpp
+++ b/src/plugins/sqldrivers/mysql/qsql_mysql.cpp
@@ -121,6 +121,20 @@ static inline QVariant qDateTimeFromString(QString &val)
#endif
}
+// check if this client and server version of MySQL/MariaDB support prepared statements
+static inline bool checkPreparedQueries(MYSQL *mysql)
+{
+ std::unique_ptr<MYSQL_STMT, decltype(&mysql_stmt_close)> stmt(mysql_stmt_init(mysql), &mysql_stmt_close);
+ if (!stmt)
+ return false;
+
+ static const char dummyQuery[] = "SELECT ? + ?";
+ if (mysql_stmt_prepare(stmt.get(), dummyQuery, sizeof(dummyQuery) - 1))
+ return false;
+
+ return mysql_stmt_param_count(stmt.get()) == 2;
+}
+
class QMYSQLResultPrivate;
class QMYSQLResult : public QSqlResult
@@ -172,7 +186,7 @@ public:
struct QMyField
{
char *outField = nullptr;
- MYSQL_FIELD *myField = nullptr;
+ const MYSQL_FIELD *myField = nullptr;
QMetaType type = QMetaType();
my_bool nullIndicator = false;
ulong bufLength = 0ul;
@@ -289,6 +303,13 @@ static bool qIsBlob(int t)
|| t == MYSQL_TYPE_LONG_BLOB;
}
+static bool qIsTimeOrDate(int t)
+{
+ // *not* MYSQL_TYPE_TIME because its range is bigger than QTime
+ // (see above)
+ return t == MYSQL_TYPE_DATE || t == MYSQL_TYPE_DATETIME || t == MYSQL_TYPE_TIMESTAMP;
+}
+
static bool qIsInteger(int t)
{
return t == QMetaType::Char || t == QMetaType::UChar
@@ -300,7 +321,7 @@ static bool qIsInteger(int t)
void QMYSQLResultPrivate::bindBlobs()
{
for (int i = 0; i < fields.count(); ++i) {
- MYSQL_FIELD *fieldInfo = fields.at(i).myField;
+ const MYSQL_FIELD *fieldInfo = fields.at(i).myField;
if (qIsBlob(inBinds[i].buffer_type) && meta && fieldInfo) {
MYSQL_BIND *bind = &inBinds[i];
bind->buffer_length = fieldInfo->max_length;
@@ -323,36 +344,39 @@ bool QMYSQLResultPrivate::bindInValues()
inBinds = new MYSQL_BIND[fields.size()];
memset(inBinds, 0, fields.size() * sizeof(MYSQL_BIND));
- MYSQL_FIELD *fieldInfo;
+ const MYSQL_FIELD *fieldInfo;
int i = 0;
while((fieldInfo = mysql_fetch_field(meta))) {
+ MYSQL_BIND *bind = &inBinds[i];
+
QMyField &f = fields[i];
f.myField = fieldInfo;
-
+ bind->buffer_length = f.bufLength = fieldInfo->length + 1;
+ bind->buffer_type = fieldInfo->type;
f.type = qDecodeMYSQLType(fieldInfo->type, fieldInfo->flags);
if (qIsBlob(fieldInfo->type)) {
// the size of a blob-field is available as soon as we call
// mysql_stmt_store_result()
// after mysql_stmt_exec() in QMYSQLResult::exec()
- fieldInfo->length = 0;
+ bind->buffer_length = f.bufLength = 0;
hasBlobs = true;
+ } else if (qIsTimeOrDate(fieldInfo->type)) {
+ bind->buffer_length = f.bufLength = sizeof(MYSQL_TIME);
} else if (qIsInteger(f.type.id())) {
- fieldInfo->length = 8;
+ bind->buffer_length = f.bufLength = 8;
} else {
- fieldInfo->type = MYSQL_TYPE_STRING;
+ bind->buffer_type = MYSQL_TYPE_STRING;
}
- MYSQL_BIND *bind = &inBinds[i];
- char *field = new char[fieldInfo->length + 1];
- memset(field, 0, fieldInfo->length + 1);
- bind->buffer_type = fieldInfo->type;
- bind->buffer = field;
- bind->buffer_length = f.bufLength = fieldInfo->length + 1;
bind->is_null = &f.nullIndicator;
bind->length = &f.bufLength;
bind->is_unsigned = fieldInfo->flags & UNSIGNED_FLAG ? 1 : 0;
- f.outField=field;
+
+ char *field = bind->buffer_length ? new char[bind->buffer_length + 1]{} : nullptr;
+ bind->buffer = f.outField = field;
+ if (qIsTimeOrDate(fieldInfo->type))
+ new (field) MYSQL_TIME;
++i;
}
@@ -403,9 +427,11 @@ void QMYSQLResult::cleanup()
d->meta = 0;
}
- int i;
- for (i = 0; i < d->fields.count(); ++i)
- delete[] d->fields[i].outField;
+ for (const auto &field : qAsConst(d->fields)) {
+ if (qIsTimeOrDate(field.myField->type))
+ reinterpret_cast<MYSQL_TIME *>(field.outField)->~MYSQL_TIME();
+ delete[] field.outField;
+ }
if (d->outBinds) {
delete[] d->outBinds;
@@ -544,6 +570,20 @@ QVariant QMYSQLResult::data(int field)
else if (f.type.id() == QMetaType::Char)
return variant.toInt();
return variant;
+ } else if (qIsTimeOrDate(f.myField->type) && f.bufLength == sizeof(MYSQL_TIME)) {
+ auto t = reinterpret_cast<const MYSQL_TIME *>(f.outField);
+ QDate date;
+ QTime time;
+ if (f.type.id() != QMetaType::QTime)
+ date = QDate(t->year, t->month, t->day);
+ if (f.type.id() != QMetaType::QDate)
+ time = QTime(t->hour, t->minute, t->second, t->second_part / 1000);
+ if (f.type.id() == QMetaType::QDateTime)
+ return QDateTime(date, time);
+ else if (f.type.id() == QMetaType::QDate)
+ return date;
+ else
+ return time;
}
if (f.type.id() != QMetaType::QByteArray)
@@ -1221,9 +1261,22 @@ bool QMYSQLDriver::open(const QString& db,
}
// try utf8 with non BMP first, utf8 (BMP only) if that fails
- if (mysql_set_character_set(d->mysql, "utf8mb4"))
- if (mysql_set_character_set(d->mysql, "utf8"))
- qWarning() << "MySQL: Unable to set the client character set to utf8.";
+ static const char wanted_charsets[][8] = { "utf8mb4", "utf8" };
+#ifdef MARIADB_VERSION_ID
+ MARIADB_CHARSET_INFO *cs = nullptr;
+ for (const char *p : wanted_charsets) {
+ cs = mariadb_get_charset_by_name(p);
+ if (cs) {
+ d->mysql->charset = cs;
+ break;
+ }
+ }
+#else
+ // dummy
+ struct {
+ const char *csname;
+ } *cs = nullptr;
+#endif
if (!sslKey.isNull() || !sslCert.isNull() || !sslCA.isNull() ||
!sslCAPath.isNull() || !sslCipher.isNull()) {
@@ -1251,16 +1304,7 @@ bool QMYSQLDriver::open(const QString& db,
unixSocket.isNull() ? nullptr : unixSocket.toUtf8().constData(),
optionFlags);
- 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 {
+ if (mysql != d->mysql) {
setLastError(qMakeError(tr("Unable to connect"),
QSqlError::ConnectionError, d));
mysql_close(d->mysql);
@@ -1269,15 +1313,32 @@ bool QMYSQLDriver::open(const QString& db,
return false;
}
- // force the communication to be utf8mb4 (only utf8mb4 supports 4-byte characters)
- if (mysql_set_character_set(d->mysql, "utf8mb4")) {
- // this failed, try forcing it to utf (BMP only)
- if (mysql_set_character_set(d->mysql, "utf8"))
- qWarning() << "MySQL: Unable to set the client character set to utf8.";
+ // now ask the server to match the charset we selected
+ 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) == 0) {
+ ok = true;
+ break;
+ }
+ }
+ if (!ok)
+ qWarning("MySQL: Unable to set the client character set to utf8 (\"%s\"). Using '%s' instead.",
+ mysql_error(d->mysql),
+ mysql_character_set_name(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;
}
- d->preparedQuerysEnabled = mysql_get_client_version() >= 40108
- && mysql_get_server_version(d->mysql) >= 40100;
+ if (reconnect)
+ mysql_options(d->mysql, MYSQL_OPT_RECONNECT, &reconnect);
+
+ d->preparedQuerysEnabled = checkPreparedQueries(d->mysql);
#if QT_CONFIG(thread)
mysql_thread_init();
@@ -1452,6 +1513,20 @@ QString QMYSQLDriver::formatValue(const QSqlField &field, bool trimStrings) cons
qWarning("QMYSQLDriver::formatValue: Database not open");
}
Q_FALLTHROUGH();
+ case QMetaType::QDateTime:
+ if (QDateTime dt = field.value().toDateTime(); dt.isValid()) {
+ // MySQL format doesn't like the "Z" at the end, but does allow
+ // "+00:00" starting in version 8.0.19. However, if we got here,
+ // it's because the MySQL server is too old for prepared queries
+ // in the first place, so it won't understand timezones either.
+ // Besides, MYSQL_TIME does not support timezones, so match it.
+ r = QLatin1Char('\'') +
+ dt.date().toString(Qt::ISODate) +
+ QLatin1Char('T') +
+ dt.time().toString(Qt::ISODate) +
+ QLatin1Char('\'');
+ }
+ break;
default:
r = QSqlDriver::formatValue(field, trimStrings);
}
diff --git a/src/plugins/sqldrivers/oci/CMakeLists.txt b/src/plugins/sqldrivers/oci/CMakeLists.txt
index 4830586064..15ab7f7f87 100644
--- a/src/plugins/sqldrivers/oci/CMakeLists.txt
+++ b/src/plugins/sqldrivers/oci/CMakeLists.txt
@@ -6,7 +6,7 @@
qt_internal_add_plugin(QOCIDriverPlugin
OUTPUT_NAME qsqloci
- TYPE sqldrivers
+ PLUGIN_TYPE sqldrivers
SOURCES
main.cpp
qsql_oci.cpp qsql_oci_p.h
diff --git a/src/plugins/sqldrivers/odbc/CMakeLists.txt b/src/plugins/sqldrivers/odbc/CMakeLists.txt
index 032d89a244..1bbae1b3ec 100644
--- a/src/plugins/sqldrivers/odbc/CMakeLists.txt
+++ b/src/plugins/sqldrivers/odbc/CMakeLists.txt
@@ -8,7 +8,7 @@ qt_find_package(ODBC) # special case
qt_internal_add_plugin(QODBCDriverPlugin
OUTPUT_NAME qsqlodbc
- TYPE sqldrivers
+ PLUGIN_TYPE sqldrivers
SOURCES
main.cpp
qsql_odbc.cpp qsql_odbc_p.h
diff --git a/src/plugins/sqldrivers/psql/CMakeLists.txt b/src/plugins/sqldrivers/psql/CMakeLists.txt
index 648d29f582..8ed84c9028 100644
--- a/src/plugins/sqldrivers/psql/CMakeLists.txt
+++ b/src/plugins/sqldrivers/psql/CMakeLists.txt
@@ -8,7 +8,7 @@ qt_find_package(PostgreSQL) # special case
qt_internal_add_plugin(QPSQLDriverPlugin
OUTPUT_NAME qsqlpsql
- TYPE sqldrivers
+ PLUGIN_TYPE sqldrivers
SOURCES
main.cpp
qsql_psql.cpp qsql_psql_p.h
diff --git a/src/plugins/sqldrivers/sqlite/CMakeLists.txt b/src/plugins/sqldrivers/sqlite/CMakeLists.txt
index bbd03e451f..1fba18290b 100644
--- a/src/plugins/sqldrivers/sqlite/CMakeLists.txt
+++ b/src/plugins/sqldrivers/sqlite/CMakeLists.txt
@@ -6,7 +6,7 @@
qt_internal_add_plugin(QSQLiteDriverPlugin
OUTPUT_NAME qsqlite
- TYPE sqldrivers
+ PLUGIN_TYPE sqldrivers
SOURCES
qsql_sqlite.cpp qsql_sqlite_p.h
smain.cpp
@@ -82,6 +82,10 @@ qt_internal_extend_target(QSQLiteDriverPlugin CONDITION UNIX AND NOT QT_FEATURE_
HAVE_USLEEP=1
)
+qt_internal_extend_target(QSQLiteDriverPlugin CONDITION INTEGRITY
+ COMPILE_OPTIONS -include qplatformdefs.h
+)
+
qt_internal_extend_target(QSQLiteDriverPlugin CONDITION QT_FEATURE_dlopen AND NOT QT_FEATURE_system_sqlite
LIBRARIES
${CMAKE_DL_LIBS}
@@ -91,6 +95,3 @@ qt_internal_extend_target(QSQLiteDriverPlugin CONDITION NOT QT_FEATURE_dlopen AN
DEFINES
SQLITE_OMIT_LOAD_EXTENSION
)
-
-#### Keys ignored in scope 12:.:../../../3rdparty:../../../3rdparty/sqlite.pri:INTEGRITY:
-# QMAKE_CFLAGS = "-include" "qplatformdefs.h"
diff --git a/src/plugins/styles/android/CMakeLists.txt b/src/plugins/styles/android/CMakeLists.txt
index 312f8ecfff..3dc0ed1e2e 100644
--- a/src/plugins/styles/android/CMakeLists.txt
+++ b/src/plugins/styles/android/CMakeLists.txt
@@ -6,7 +6,7 @@
qt_internal_add_plugin(QAndroidStylePlugin
OUTPUT_NAME qandroidstyle
- TYPE styles
+ PLUGIN_TYPE styles
SOURCES
main.cpp
qandroidstyle.cpp qandroidstyle_p.h
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/CMakeLists.txt b/src/plugins/styles/mac/CMakeLists.txt
index f95579b474..98d6791d82 100644
--- a/src/plugins/styles/mac/CMakeLists.txt
+++ b/src/plugins/styles/mac/CMakeLists.txt
@@ -6,7 +6,7 @@
qt_internal_add_plugin(QMacStylePlugin
OUTPUT_NAME qmacstyle
- TYPE styles
+ PLUGIN_TYPE styles
SOURCES
main.mm
qmacstyle_mac.mm qmacstyle_mac_p.h
diff --git a/src/plugins/styles/mac/qmacstyle_mac.mm b/src/plugins/styles/mac/qmacstyle_mac.mm
index 4f141327e2..93d51171da 100644
--- a/src/plugins/styles/mac/qmacstyle_mac.mm
+++ b/src/plugins/styles/mac/qmacstyle_mac.mm
@@ -196,6 +196,8 @@ const int pushButtonBevelRectOffsets[3] = {
QVector<QPointer<QObject> > QMacStylePrivate::scrollBars;
+bool isDarkMode() { return QGuiApplicationPrivate::platformTheme()->appearance() == QPlatformTheme::Appearance::Dark; }
+
// Title bar gradient colors for Lion were determined by inspecting PSDs exported
// using CoreUI's CoreThemeDocument; there is no public API to retrieve them
@@ -216,7 +218,7 @@ static QLinearGradient titlebarGradientActive()
gradient.setColorAt(1, QColor(180, 180, 180));
return gradient;
}();
- return qt_mac_applicationIsInDarkMode() ? darkGradient : lightGradient;
+ return isDarkMode() ? darkGradient : lightGradient;
}
static QLinearGradient titlebarGradientInactive()
@@ -232,7 +234,7 @@ static QLinearGradient titlebarGradientInactive()
gradient.setColorAt(1, QColor(225, 225, 225));
return gradient;
}();
- return qt_mac_applicationIsInDarkMode() ? darkGradient : lightGradient;
+ return isDarkMode() ? darkGradient : lightGradient;
}
#if QT_CONFIG(tabwidget)
@@ -254,7 +256,7 @@ static void clipTabBarFrame(const QStyleOption *option, const QMacStyle *style,
Q_ASSERT(style);
Q_ASSERT(ctx);
- if (qt_mac_applicationIsInDarkMode()) {
+ if (isDarkMode()) {
QTabWidget *tabWidget = qobject_cast<QTabWidget *>(option->styleObject);
Q_ASSERT(tabWidget);
@@ -286,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);
@@ -302,7 +304,6 @@ static const qreal titleBarButtonSpacing = 8;
// active: window is active
// selected: tab is selected
// hovered: tab is hovered
-bool isDarkMode() { return qt_mac_applicationIsInDarkMode(); }
#if QT_CONFIG(tabbar)
static const QColor lightTabBarTabBackgroundActive(190, 190, 190);
@@ -422,7 +423,7 @@ public:
{
#if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14)
if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave
- && !qt_mac_applicationIsInDarkMode()) {
+ && !isDarkMode()) {
auto requiredAppearanceName = NSApplication.sharedApplication.effectiveAppearance.name;
if (![NSAppearance.currentAppearance.name isEqualToString:requiredAppearanceName]) {
previous = NSAppearance.currentAppearance;
@@ -1086,7 +1087,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
@@ -1288,7 +1289,7 @@ void QMacStylePrivate::drawFocusRing(QPainter *p, const QRectF &targetRect, int
}
auto focusRingColor = qt_mac_toQColor(NSColor.keyboardFocusIndicatorColor.CGColor);
- if (!qt_mac_applicationIsInDarkMode()) {
+ if (!isDarkMode()) {
// This color already has alpha ~ 0.25, this value is too small - the ring is
// very pale and nothing like the native one. 0.39 makes it better (not ideal
// anyway). The color seems to be correct in dark more without any modification.
@@ -1836,7 +1837,7 @@ NSView *QMacStylePrivate::cocoaControl(CocoaControl widget) const
if (widget.type == Box) {
if (__builtin_available(macOS 10.14, *)) {
- if (qt_mac_applicationIsInDarkMode()) {
+ if (isDarkMode()) {
// See render code in drawPrimitive(PE_FrameTabWidget)
widget.type = Box_Dark;
}
@@ -3102,7 +3103,7 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai
auto adjustedRect = opt->rect;
bool needTranslation = false;
if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave
- && !qt_mac_applicationIsInDarkMode()) {
+ && !isDarkMode()) {
// In Aqua theme we have to use the 'default' NSBox (as opposite
// to the 'custom' QDarkNSBox we use in dark theme). Since -drawRect:
// does nothing in default NSBox, we call -displayRectIgnoringOpaticty:.
@@ -3111,7 +3112,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);
@@ -3150,7 +3151,7 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai
theStroker.setCapStyle(Qt::FlatCap);
theStroker.setDashPattern(QVector<qreal>() << 1 << 2);
path = theStroker.createStroke(path);
- const auto dark = qt_mac_applicationIsInDarkMode() ? opt->palette.dark().color().darker()
+ const auto dark = isDarkMode() ? opt->palette.dark().color().darker()
: QColor(0, 0, 0, 119);
p->fillPath(path, dark);
}
@@ -3344,7 +3345,7 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai
static_cast<NSTextFieldCell *>(tf.cell).bezelStyle = isRounded ? NSTextFieldRoundedBezel : NSTextFieldSquareBezel;
tf.frame = opt->rect.toCGRect();
d->drawNSViewInRect(tf, opt->rect, p, ^(CGContextRef, const CGRect &rect) {
- if (!qt_mac_applicationIsInDarkMode()) {
+ if (!isDarkMode()) {
// In 'Dark' mode controls are transparent, so we do not
// over-paint the (potentially custom) color in the background.
// In 'Light' mode we have to care about the correct
@@ -3377,7 +3378,7 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai
case PE_PanelLineEdit:
{
const QStyleOptionFrame *panel = qstyleoption_cast<const QStyleOptionFrame *>(opt);
- if (qt_mac_applicationIsInDarkMode() || (panel && panel->lineWidth <= 0)) {
+ if (isDarkMode() || (panel && panel->lineWidth <= 0)) {
// QCommonStyle::drawPrimitive(PE_PanelLineEdit) fill the background with
// a proper color, defined in opt->palette and then, if lineWidth > 0, it
// calls QMacStyle::drawPrimitive(PE_FrameLineEdit). We use NSTextFieldCell
@@ -3568,9 +3569,10 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter
QPixmap pixmap = header->icon.pixmap(QSize(iconExtent, iconExtent), p->device()->devicePixelRatio(), mode);
QRect pixr = header->rect;
- pixr.setY(header->rect.center().y() - (pixmap.height() / pixmap.devicePixelRatio() - 1) / 2);
+ QSizeF size = pixmap.deviceIndependentSize();
+ pixr.setY(header->rect.center().y() - (size.height() - 1) / 2);
proxy()->drawItemPixmap(p, pixr, Qt::AlignVCenter, pixmap);
- textr.translate(pixmap.width() / pixmap.devicePixelRatio() + 2, 0);
+ textr.translate(size.width() + 2, 0);
}
QString text = header->text;
if (const QStyleOptionHeaderV2 *headerV2 = qstyleoption_cast<const QStyleOptionHeaderV2 *>(header)) {
@@ -3627,12 +3629,13 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter
// Draw the text if it's needed.
if (tb->toolButtonStyle != Qt::ToolButtonIconOnly) {
needText = true;
+ QSizeF size = pixmap.deviceIndependentSize();
if (tb->toolButtonStyle == Qt::ToolButtonTextUnderIcon) {
- pr.setHeight(pixmap.size().height() / pixmap.devicePixelRatio() + 6);
+ pr.setHeight(size.height() + 6);
cr.adjust(0, pr.bottom(), 0, -3);
alignment |= Qt::AlignCenter;
} else {
- pr.setWidth(pixmap.width() / pixmap.devicePixelRatio() + 8);
+ pr.setWidth(size.width() + 8);
cr.adjust(pr.right(), 0, 0, 0);
alignment |= Qt::AlignLeft | Qt::AlignVCenter;
}
@@ -3719,7 +3722,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();
@@ -3812,12 +3815,11 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter
if (btn.state & State_On)
state = QIcon::On;
QPixmap pixmap = btn.icon.pixmap(btn.iconSize, p->device()->devicePixelRatio(), mode, state);
- int pixmapWidth = pixmap.width() / pixmap.devicePixelRatio();
- int pixmapHeight = pixmap.height() / pixmap.devicePixelRatio();
- contentW += pixmapWidth + QMacStylePrivate::PushButtonContentPadding;
+ QSizeF pixmapSize = pixmap.deviceIndependentSize();
+ contentW += pixmapSize.width() + QMacStylePrivate::PushButtonContentPadding;
int iconLeftOffset = freeContentRect.x() + (freeContentRect.width() - contentW) / 2;
- int iconTopOffset = freeContentRect.y() + (freeContentRect.height() - pixmapHeight) / 2;
- QRect iconDestRect(iconLeftOffset, iconTopOffset, pixmapWidth, pixmapHeight);
+ int iconTopOffset = freeContentRect.y() + (freeContentRect.height() - pixmapSize.height()) / 2;
+ QRect iconDestRect(iconLeftOffset, iconTopOffset, pixmapSize.width(), pixmapSize.height());
QRect visualIconDestRect = visualRect(btn.direction, freeContentRect, iconDestRect);
proxy()->drawItemPixmap(p, visualIconDestRect, Qt::AlignLeft | Qt::AlignVCenter, pixmap);
int newOffset = iconDestRect.x() + iconDestRect.width()
@@ -4314,13 +4316,12 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter
}
#endif
QPixmap pixmap = mi->icon.pixmap(iconSize, p->device()->devicePixelRatio(), mode);
- int pixw = pixmap.width() / pixmap.devicePixelRatio();
- int pixh = pixmap.height() / pixmap.devicePixelRatio();
QRect cr(xpos, mi->rect.y(), checkcol, mi->rect.height());
- QRect pmr(0, 0, pixw, pixh);
+ QSize size = pixmap.deviceIndependentSize().toSize();
+ QRect pmr(QPoint(0, 0), size);
pmr.moveCenter(cr.center());
p->drawPixmap(pmr.topLeft(), pixmap);
- xpos += pixw + 6;
+ xpos += size.width() + 6;
}
QString s = mi->text;
@@ -4361,7 +4362,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
@@ -4594,7 +4595,7 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter
#ifndef QT_NO_TOOLBAR
case CE_ToolBar: {
const QStyleOptionToolBar *toolBar = qstyleoption_cast<const QStyleOptionToolBar *>(opt);
- const bool isDarkMode = qt_mac_applicationIsInDarkMode();
+ const bool darkMode = isDarkMode();
// Unified title and toolbar drawing. In this mode the cocoa platform plugin will
// fill the top toolbar area part with a background gradient that "unifies" with
@@ -4619,7 +4620,7 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter
if (isEndOfUnifiedArea) {
const int margin = qt_mac_aqua_get_metric(SeparatorSize);
const auto separatorRect = QRect(opt->rect.left(), opt->rect.bottom(), opt->rect.width(), margin);
- p->fillRect(separatorRect, isDarkMode ? darkModeSeparatorLine : opt->palette.dark().color());
+ p->fillRect(separatorRect, darkMode ? darkModeSeparatorLine : opt->palette.dark().color());
}
break;
}
@@ -4634,24 +4635,24 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter
else
linearGrad = QLinearGradient(opt->rect.left(), 0, opt->rect.right(), 0);
- QColor mainWindowGradientBegin = isDarkMode ? darkMainWindowGradientBegin : lightMainWindowGradientBegin;
- QColor mainWindowGradientEnd = isDarkMode ? darkMainWindowGradientEnd : lightMainWindowGradientEnd;
+ QColor mainWindowGradientBegin = darkMode ? darkMainWindowGradientBegin : lightMainWindowGradientBegin;
+ QColor mainWindowGradientEnd = darkMode ? darkMainWindowGradientEnd : lightMainWindowGradientEnd;
linearGrad.setColorAt(0, mainWindowGradientBegin);
linearGrad.setColorAt(1, mainWindowGradientEnd);
p->fillRect(opt->rect, linearGrad);
p->save();
- QRect toolbarRect = isDarkMode ? opt->rect.adjusted(0, 0, 0, 1) : opt->rect;
+ QRect toolbarRect = darkMode ? opt->rect.adjusted(0, 0, 0, 1) : opt->rect;
if (opt->state & State_Horizontal) {
- p->setPen(isDarkMode ? darkModeSeparatorLine : mainWindowGradientBegin.lighter(114));
+ p->setPen(darkMode ? darkModeSeparatorLine : mainWindowGradientBegin.lighter(114));
p->drawLine(toolbarRect.topLeft(), toolbarRect.topRight());
- p->setPen(isDarkMode ? darkModeSeparatorLine :mainWindowGradientEnd.darker(114));
+ p->setPen(darkMode ? darkModeSeparatorLine :mainWindowGradientEnd.darker(114));
p->drawLine(toolbarRect.bottomLeft(), toolbarRect.bottomRight());
} else {
- p->setPen(isDarkMode ? darkModeSeparatorLine : mainWindowGradientBegin.lighter(114));
+ p->setPen(darkMode ? darkModeSeparatorLine : mainWindowGradientBegin.lighter(114));
p->drawLine(toolbarRect.topLeft(), toolbarRect.bottomLeft());
- p->setPen(isDarkMode ? darkModeSeparatorLine : mainWindowGradientEnd.darker(114));
+ p->setPen(darkMode ? darkModeSeparatorLine : mainWindowGradientEnd.darker(114));
p->drawLine(toolbarRect.topRight(), toolbarRect.bottomRight());
}
p->restore();
@@ -5448,7 +5449,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/CMakeLists.txt b/src/plugins/styles/windowsvista/CMakeLists.txt
index f668b21632..efd552ab08 100644
--- a/src/plugins/styles/windowsvista/CMakeLists.txt
+++ b/src/plugins/styles/windowsvista/CMakeLists.txt
@@ -6,7 +6,7 @@
qt_internal_add_plugin(QWindowsVistaStylePlugin
OUTPUT_NAME qwindowsvistastyle
- TYPE styles
+ PLUGIN_TYPE styles
SOURCES
main.cpp
qwindowsvistastyle.cpp qwindowsvistastyle_p.h
diff --git a/src/plugins/styles/windowsvista/qwindowsvistastyle.cpp b/src/plugins/styles/windowsvista/qwindowsvistastyle.cpp
index 67d6b0c460..73be33b278 100644
--- a/src/plugins/styles/windowsvista/qwindowsvistastyle.cpp
+++ b/src/plugins/styles/windowsvista/qwindowsvistastyle.cpp
@@ -1273,9 +1273,7 @@ void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption
pixmap = menuitem->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize, option, widget), mode, QIcon::On);
else
pixmap = menuitem->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize, option, widget), mode);
- const int pixw = pixmap.width() / pixmap.devicePixelRatio();
- const int pixh = pixmap.height() / pixmap.devicePixelRatio();
- QRect pmr(0, 0, pixw, pixh);
+ QRect pmr(QPoint(0, 0), pixmap.deviceIndependentSize().toSize());
pmr.moveCenter(vCheckRect.center());
painter->setPen(menuitem->palette.text().color());
painter->drawPixmap(pmr.topLeft(), pixmap);
diff --git a/src/plugins/styles/windowsvista/qwindowsxpstyle.cpp b/src/plugins/styles/windowsvista/qwindowsxpstyle.cpp
index 000c27e685..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
@@ -1974,9 +1974,7 @@ void QWindowsXPStyle::drawControl(ControlElement element, const QStyleOption *op
QPixmap pixmap = checked ?
menuitem->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize, option, widget), mode, QIcon::On) :
menuitem->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize, option, widget), mode);
- const int pixw = pixmap.width() / pixmap.devicePixelRatio();
- const int pixh = pixmap.height() / pixmap.devicePixelRatio();
- QRect iconRect(0, 0, pixw, pixh);
+ QRect iconRect(QPoint(0, 0), pixmap.deviceIndependentSize().toSize());
iconRect.moveCenter(QRect(xpos, y, checkcol, h).center());
QRect vIconRect = visualRect(option->direction, option->rect, iconRect);
p->setPen(menuitem->palette.text().color());
@@ -3511,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/certonly/CMakeLists.txt b/src/plugins/tls/certonly/CMakeLists.txt
index 1c8cc0de15..14b769cbba 100644
--- a/src/plugins/tls/certonly/CMakeLists.txt
+++ b/src/plugins/tls/certonly/CMakeLists.txt
@@ -1,7 +1,7 @@
qt_internal_add_plugin(QTlsBackendCertOnlyPlugin
- OUTPUT_NAME certonlybackend
+ OUTPUT_NAME qcertonlybackend
CLASS_NAME QTlsBackendCertOnly
- TYPE tls
+ PLUGIN_TYPE tls
DEFAULT_IF NOT QT_FEATURE_securetransport AND NOT (QT_FEATURE_openssl OR QT_FEATURE_openssl_linked) AND NOT QT_FEATURE_schannel
SOURCES
../shared/qx509_base_p.h
diff --git a/src/plugins/tls/openssl/CMakeLists.txt b/src/plugins/tls/openssl/CMakeLists.txt
index 4e1c711aa8..0a35d5888b 100644
--- a/src/plugins/tls/openssl/CMakeLists.txt
+++ b/src/plugins/tls/openssl/CMakeLists.txt
@@ -1,7 +1,7 @@
qt_internal_add_plugin(QTlsBackendOpenSSLPlugin
- OUTPUT_NAME opensslbackend
+ OUTPUT_NAME qopensslbackend
CLASS_NAME QTlsBackendOpenSSL
- TYPE tls
+ PLUGIN_TYPE tls
SOURCES
../shared/qx509_base.cpp ../shared/qx509_base_p.h
../shared/qtlskey_base.cpp ../shared/qtlskey_base_p.h
@@ -35,7 +35,7 @@ qt_internal_extend_target(QTlsBackendOpenSSLPlugin CONDITION APPLE
${FWSecurity}
)
-qt_internal_extend_target(QTlsBackendOpenSSLPlugin CONDITION ANDROID AND NOT ANDROID_EMBEDDED
+qt_internal_extend_target(QTlsBackendOpenSSLPlugin CONDITION ANDROID
SOURCES
qsslsocket_openssl_android.cpp
)
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 5fb95c443a..27ed594d6d 100644
--- a/src/plugins/tls/openssl/qsslsocket_openssl_symbols.cpp
+++ b/src/plugins/tls/openssl/qsslsocket_openssl_symbols.cpp
@@ -181,6 +181,8 @@ DEFINEFUNC(const SSL_METHOD *, TLS_server_method, DUMMYARG, DUMMYARG, return nul
DEFINEFUNC(void, X509_up_ref, X509 *a, a, return, DUMMYARG)
DEFINEFUNC(ASN1_TIME *, X509_getm_notBefore, X509 *a, a, return nullptr, return)
DEFINEFUNC(ASN1_TIME *, X509_getm_notAfter, X509 *a, a, return nullptr, return)
+DEFINEFUNC2(void, ASN1_item_free, ASN1_VALUE *val, val, const ASN1_ITEM *it, it, return, return)
+DEFINEFUNC(void, X509V3_conf_free, CONF_VALUE *val, val, return, return)
DEFINEFUNC(long, X509_get_version, X509 *a, a, return -1, return)
DEFINEFUNC(EVP_PKEY *, X509_get_pubkey, X509 *a, a, return nullptr, return)
DEFINEFUNC2(void, X509_STORE_set_verify_cb, X509_STORE *a, a, X509_STORE_CTX_verify_cb verify_cb, verify_cb, return, DUMMYARG)
@@ -236,6 +238,7 @@ DEFINEFUNC6(int, OCSP_basic_sign, OCSP_BASICRESP *br, br, X509 *signer, signer,
const EVP_MD *dg, dg, STACK_OF(X509) *cs, cs, unsigned long flags, flags, return 0, return)
#endif // ocsp
+DEFINEFUNC(void, AUTHORITY_INFO_ACCESS_free, AUTHORITY_INFO_ACCESS *p, p, return, return)
DEFINEFUNC2(void, BIO_set_data, BIO *a, a, void *ptr, ptr, return, DUMMYARG)
DEFINEFUNC(void *, BIO_get_data, BIO *a, a, return nullptr, return)
DEFINEFUNC2(void, BIO_set_init, BIO *a, a, int init, init, return, DUMMYARG)
@@ -255,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)
@@ -304,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)
@@ -324,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);
@@ -473,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
@@ -497,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)
@@ -526,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
@@ -697,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"
@@ -709,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;
@@ -816,6 +830,11 @@ static LoadedOpenSsl loadOpenSsl()
const QStringList cryptoList = findAllLibCrypto();
for (const QString &crypto : cryptoList) {
+#ifdef Q_OS_DARWIN
+ // Clients should not load the unversioned libcrypto dylib as it does not have a stable ABI
+ if (crypto.endsWith("libcrypto.dylib"))
+ continue;
+#endif
libcrypto->setFileNameAndVersion(crypto, -1);
if (libcrypto->load()) {
QFileInfo fi(crypto);
@@ -873,6 +892,7 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(OPENSSL_init_crypto)
RESOLVEFUNC(ASN1_STRING_get0_data)
RESOLVEFUNC(EVP_CIPHER_CTX_reset)
+ RESOLVEFUNC(AUTHORITY_INFO_ACCESS_free)
RESOLVEFUNC(EVP_PKEY_up_ref)
RESOLVEFUNC(EVP_PKEY_CTX_new)
RESOLVEFUNC(EVP_PKEY_param_check)
@@ -910,6 +930,8 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(X509_STORE_CTX_get0_chain)
RESOLVEFUNC(X509_getm_notBefore)
RESOLVEFUNC(X509_getm_notAfter)
+ RESOLVEFUNC(ASN1_item_free)
+ RESOLVEFUNC(X509V3_conf_free)
RESOLVEFUNC(X509_get_version)
RESOLVEFUNC(X509_get_pubkey)
RESOLVEFUNC(X509_STORE_set_verify_cb)
@@ -919,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)
@@ -991,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)
@@ -1037,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)
@@ -1054,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);
@@ -1103,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)
@@ -1118,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
@@ -1225,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 98fa04ac79..5e3feb77b8 100644
--- a/src/plugins/tls/openssl/qsslsocket_openssl_symbols_p.h
+++ b/src/plugins/tls/openssl/qsslsocket_openssl_symbols_p.h
@@ -232,6 +232,7 @@ const unsigned char * q_ASN1_STRING_get0_data(const ASN1_STRING *x);
BIO *q_BIO_new(const BIO_METHOD *a);
const BIO_METHOD *q_BIO_s_mem();
+void q_AUTHORITY_INFO_ACCESS_free(AUTHORITY_INFO_ACCESS *a);
int q_EVP_CIPHER_CTX_reset(EVP_CIPHER_CTX *c);
int q_EVP_PKEY_up_ref(EVP_PKEY *a);
EVP_PKEY_CTX *q_EVP_PKEY_CTX_new(EVP_PKEY *pkey, ENGINE *e);
@@ -254,6 +255,8 @@ const SSL_METHOD *q_TLS_client_method();
const SSL_METHOD *q_TLS_server_method();
ASN1_TIME *q_X509_getm_notBefore(X509 *a);
ASN1_TIME *q_X509_getm_notAfter(X509 *a);
+void q_ASN1_item_free(ASN1_VALUE *val, const ASN1_ITEM *it);
+void q_X509V3_conf_free(CONF_VALUE *val);
void q_X509_up_ref(X509 *a);
long q_X509_get_version(X509 *a);
@@ -277,7 +280,6 @@ void q_DH_get0_pqg(const DH *dh, const BIGNUM **p, const BIGNUM **q, const BIGNU
| OPENSSL_INIT_ADD_ALL_DIGESTS, NULL)
int q_OPENSSL_init_crypto(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings);
-void q_CRYPTO_free(void *str, const char *file, int line);
long q_OpenSSL_version_num();
const char *q_OpenSSL_version(int type);
@@ -376,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);
@@ -434,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);
@@ -457,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();
@@ -550,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
@@ -698,6 +692,8 @@ int q_OCSP_id_cmp(OCSP_CERTID *a, OCSP_CERTID *b);
void *q_CRYPTO_malloc(size_t num, const char *file, int line);
#define q_OPENSSL_malloc(num) q_CRYPTO_malloc(num, "", 0)
+void q_CRYPTO_free(void *str, const char *file, int line);
+# define q_OPENSSL_free(addr) q_CRYPTO_free(addr, "", 0)
void q_SSL_set_info_callback(SSL *ssl, void (*cb) (const SSL *ssl, int type, int val));
const char *q_SSL_alert_type_string(int value);
@@ -720,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 db7316e927..569e1d9c84 100644
--- a/src/plugins/tls/openssl/qtls_openssl.cpp
+++ b/src/plugins/tls/openssl/qtls_openssl.cpp
@@ -258,15 +258,22 @@ static unsigned q_ssl_psk_restore_client(SSL *ssl, const char *hint, char *ident
Q_ASSERT(tls->d);
Q_ASSERT(tls->d->tlsMode() == QSslSocket::SslClientMode);
#endif
+ unsigned retVal = 0;
+
+ // Let developers opt-in to having the normal PSK callback get called for TLS 1.3
+ // PSK (which works differently in a few ways, and is called at the start of every connection).
+ // When they do opt-in we just call the old callback from here.
+ if (qEnvironmentVariableIsSet("QT_USE_TLS_1_3_PSK"))
+ retVal = q_ssl_psk_client_callback(ssl, hint, identity, max_identity_len, psk, max_psk_len);
+
q_SSL_set_psk_client_callback(ssl, &q_ssl_psk_client_callback);
- return 0;
+ return retVal;
}
static int q_ssl_psk_use_session_callback(SSL *ssl, const EVP_MD *md, const unsigned char **id,
size_t *idlen, SSL_SESSION **sess)
{
- Q_UNUSED(ssl);
Q_UNUSED(md);
Q_UNUSED(id);
Q_UNUSED(idlen);
@@ -326,7 +333,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());
diff --git a/src/plugins/tls/openssl/qtls_openssl_p.h b/src/plugins/tls/openssl/qtls_openssl_p.h
index 9e7283b15d..60dae884fc 100644
--- a/src/plugins/tls/openssl/qtls_openssl_p.h
+++ b/src/plugins/tls/openssl/qtls_openssl_p.h
@@ -144,7 +144,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;
diff --git a/src/plugins/tls/openssl/qtlsbackend_openssl.cpp b/src/plugins/tls/openssl/qtlsbackend_openssl.cpp
index 5d3b3ea582..9acac9b9d1 100644
--- a/src/plugins/tls/openssl/qtlsbackend_openssl.cpp
+++ b/src/plugins/tls/openssl/qtlsbackend_openssl.cpp
@@ -367,8 +367,7 @@ QTlsPrivate::X509Certificate *QTlsBackendOpenSSL::createCertificate() const
namespace QTlsPrivate {
-// TLSTODO: remove.
-#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
+#ifdef Q_OS_ANDROID
QList<QByteArray> fetchSslCertificateData();
#endif
@@ -399,7 +398,9 @@ QList<QSslCertificate> systemCaCertificates()
CertCloseStore(hSystemStore, 0);
}
#elif defined(Q_OS_ANDROID)
- // TODO: find where it hides its system certs !
+ const QList<QByteArray> certData = fetchSslCertificateData();
+ for (auto certDatum : certData)
+ systemCerts.append(QSslCertificate::fromData(certDatum, QSsl::Der));
#elif defined(Q_OS_UNIX)
{
const QList<QByteArray> directories = QSslSocketPrivate::unixRootCertDirectories();
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/openssl/qx509_openssl.cpp b/src/plugins/tls/openssl/qx509_openssl.cpp
index e9b1450d54..b7c2e7cff4 100644
--- a/src/plugins/tls/openssl/qx509_openssl.cpp
+++ b/src/plugins/tls/openssl/qx509_openssl.cpp
@@ -197,10 +197,27 @@ QVariant x509UnknownExtensionToValue(X509_EXTENSION *ext)
}
void *ext_internal = q_X509V3_EXT_d2i(ext);
+ if (!ext_internal)
+ return {};
+
+ const auto extCleaner = qScopeGuard([meth, ext_internal]{
+ Q_ASSERT(ext_internal && meth);
+
+ if (meth->it)
+ q_ASN1_item_free(static_cast<ASN1_VALUE *>(ext_internal), ASN1_ITEM_ptr(meth->it));
+ else if (meth->ext_free)
+ meth->ext_free(ext_internal);
+ else
+ qCWarning(lcTlsBackend, "No method to free an unknown extension, a potential memory leak?");
+ });
// If this extension can be converted
- if (meth->i2v && ext_internal) {
+ if (meth->i2v) {
STACK_OF(CONF_VALUE) *val = meth->i2v(meth, ext_internal, nullptr);
+ const auto stackCleaner = qScopeGuard([val]{
+ if (val)
+ q_OPENSSL_sk_pop_free((OPENSSL_STACK *)val, (void(*)(void*))q_X509V3_conf_free);
+ });
QVariantMap map;
QVariantList list;
@@ -222,10 +239,12 @@ QVariant x509UnknownExtensionToValue(X509_EXTENSION *ext)
return map;
else
return list;
- } else if (meth->i2s && ext_internal) {
- QVariant result(QString::fromUtf8(meth->i2s(meth, ext_internal)));
+ } else if (meth->i2s) {
+ const char *hexString = meth->i2s(meth, ext_internal);
+ QVariant result(hexString ? QString::fromUtf8(hexString) : QString{});
+ q_OPENSSL_free((void *)hexString);
return result;
- } else if (meth->i2r && ext_internal) {
+ } else if (meth->i2r) {
QByteArray result;
BIO *bio = q_BIO_new(q_BIO_s_mem());
@@ -254,6 +273,31 @@ QVariant x509ExtensionToValue(X509_EXTENSION *ext)
{
ASN1_OBJECT *obj = q_X509_EXTENSION_get_object(ext);
int nid = q_OBJ_obj2nid(obj);
+
+ // We cast away the const-ness here because some versions of openssl
+ // don't use const for the parameters in the functions pointers stored
+ // in the object.
+ X509V3_EXT_METHOD *meth = const_cast<X509V3_EXT_METHOD *>(q_X509V3_EXT_get(ext));
+
+ void *ext_internal = nullptr; // The value, returned by X509V3_EXT_d2i.
+ const auto extCleaner = qScopeGuard([meth, &ext_internal]() {
+ if (!meth || !ext_internal)
+ return;
+
+ if (meth->it)
+ q_ASN1_item_free(static_cast<ASN1_VALUE *>(ext_internal), ASN1_ITEM_ptr(meth->it));
+ else if (meth->ext_free)
+ meth->ext_free(ext_internal);
+ else
+ qWarning(lcTlsBackend, "Cannot free an extension, a potential memory leak?");
+ });
+
+ const char * hexString = nullptr; // The value returned by meth->i2s.
+ const auto hexStringCleaner = qScopeGuard([&hexString](){
+ if (hexString)
+ q_OPENSSL_free((void*)hexString);
+ });
+
switch (nid) {
case NID_basic_constraints:
{
@@ -295,21 +339,18 @@ QVariant x509ExtensionToValue(X509_EXTENSION *ext)
}
}
- q_OPENSSL_sk_pop_free((OPENSSL_STACK*)info, reinterpret_cast<void(*)(void *)>(q_OPENSSL_sk_free));
+ q_AUTHORITY_INFO_ACCESS_free(info);
return result;
}
break;
case NID_subject_key_identifier:
{
- void *ext_internal = q_X509V3_EXT_d2i(ext);
+ ext_internal = q_X509V3_EXT_d2i(ext);
if (!ext_internal)
return {};
- // we cast away the const-ness here because some versions of openssl
- // don't use const for the parameters in the functions pointers stored
- // in the object.
- X509V3_EXT_METHOD *meth = const_cast<X509V3_EXT_METHOD *>(q_X509V3_EXT_get(ext));
- return QVariant(QString::fromUtf8(meth->i2s(meth, ext_internal)));
+ hexString = meth->i2s(meth, ext_internal);
+ return QVariant(QString::fromUtf8(hexString));
}
break;
case NID_authority_key_identifier:
diff --git a/src/plugins/tls/schannel/CMakeLists.txt b/src/plugins/tls/schannel/CMakeLists.txt
index b875e85de9..f03964069a 100644
--- a/src/plugins/tls/schannel/CMakeLists.txt
+++ b/src/plugins/tls/schannel/CMakeLists.txt
@@ -1,7 +1,7 @@
qt_internal_add_plugin(QSchannelBackendPlugin
- OUTPUT_NAME schannelbackend
+ OUTPUT_NAME qschannelbackend
CLASS_NAME QSchannelBackend
- TYPE tls
+ PLUGIN_TYPE tls
DEFAULT_IF WIN32
SOURCES
../shared/qtlskey_base_p.h
diff --git a/src/plugins/tls/securetransport/CMakeLists.txt b/src/plugins/tls/securetransport/CMakeLists.txt
index 23ec54176e..0355049157 100644
--- a/src/plugins/tls/securetransport/CMakeLists.txt
+++ b/src/plugins/tls/securetransport/CMakeLists.txt
@@ -1,7 +1,7 @@
qt_internal_add_plugin(QSecureTransportBackendPlugin
- OUTPUT_NAME securetransportbackend
+ OUTPUT_NAME qsecuretransportbackend
CLASS_NAME QSecureTransportBackend
- TYPE tls
+ PLUGIN_TYPE tls
DEFAULT_IF APPLE
SOURCES
../shared/qsslsocket_mac_shared.cpp
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',