summaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/bearer/networkmanager/qnetworkmanagerengine.cpp4
-rw-r--r--src/plugins/bearer/qnetworksession_impl.cpp4
-rw-r--r--src/plugins/generic/tuiotouch/qtuiohandler.cpp10
-rw-r--r--src/plugins/imageformats/gif/qgifhandler.cpp2
-rw-r--r--src/plugins/imageformats/gif/qgifhandler_p.h2
-rw-r--r--src/plugins/imageformats/ico/qicohandler.cpp3
-rw-r--r--src/plugins/imageformats/ico/qicohandler.h2
-rw-r--r--src/plugins/imageformats/jpeg/qjpeghandler.cpp2
-rw-r--r--src/plugins/imageformats/jpeg/qjpeghandler_p.h2
-rw-r--r--src/plugins/platforminputcontexts/compose/compose.pro8
-rw-r--r--src/plugins/platforminputcontexts/compose/generator/qtablegenerator.cpp658
-rw-r--r--src/plugins/platforminputcontexts/compose/generator/qtablegenerator.h145
-rw-r--r--src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp297
-rw-r--r--src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.h33
-rw-r--r--src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontextmain.cpp4
-rw-r--r--src/plugins/platforminputcontexts/ibus/ibus.pro2
-rw-r--r--src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp60
-rw-r--r--src/plugins/platforminputcontexts/platforminputcontexts.pro11
-rw-r--r--src/plugins/platforms/android/android.pro8
-rw-r--r--src/plugins/platforms/android/androidcontentfileengine.cpp92
-rw-r--r--src/plugins/platforms/android/androidcontentfileengine.h (renamed from src/plugins/platforms/offscreen/qoffscreenintegration_dummy.cpp)25
-rw-r--r--src/plugins/platforms/android/androidjniaccessibility.cpp5
-rw-r--r--src/plugins/platforms/android/androidjnimain.cpp5
-rw-r--r--src/plugins/platforms/android/extract-dummy.cpp10
-rw-r--r--src/plugins/platforms/android/extract.cpp40
-rw-r--r--src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp149
-rw-r--r--src/plugins/platforms/android/qandroidplatformfiledialoghelper.h81
-rw-r--r--src/plugins/platforms/android/qandroidplatformtheme.cpp5
-rw-r--r--src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm46
-rw-r--r--src/plugins/platforms/cocoa/qcocoabackingstore.mm12
-rw-r--r--src/plugins/platforms/cocoa/qcocoaglcontext.mm2
-rw-r--r--src/plugins/platforms/cocoa/qcocoahelpers.h12
-rw-r--r--src/plugins/platforms/cocoa/qcocoahelpers.mm2
-rw-r--r--src/plugins/platforms/cocoa/qcocoaintegration.h7
-rw-r--r--src/plugins/platforms/cocoa/qcocoaintegration.mm91
-rw-r--r--src/plugins/platforms/cocoa/qcocoascreen.h31
-rw-r--r--src/plugins/platforms/cocoa/qcocoascreen.mm220
-rw-r--r--src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm6
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindow.mm11
-rw-r--r--src/plugins/platforms/cocoa/qmacclipboard.mm15
-rw-r--r--src/plugins/platforms/cocoa/qnsview_drawing.mm2
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp2
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/qeglfsvivwlintegration.cpp5
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/qeglfsvivwlintegration.h1
-rw-r--r--src/plugins/platforms/linuxfb/qlinuxfbintegration.h2
-rw-r--r--src/plugins/platforms/offscreen/offscreen.pro5
-rw-r--r--src/plugins/platforms/offscreen/qoffscreenintegration.cpp14
-rw-r--r--src/plugins/platforms/offscreen/qoffscreenintegration.h1
-rw-r--r--src/plugins/platforms/offscreen/qoffscreenintegration_x11.cpp117
-rw-r--r--src/plugins/platforms/offscreen/qoffscreenintegration_x11.h12
-rw-r--r--src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp5
-rw-r--r--src/plugins/platforms/qnx/qqnxwindow.cpp88
-rw-r--r--src/plugins/platforms/qnx/qqnxwindow.h4
-rw-r--r--src/plugins/platforms/vnc/qvnc.cpp10
-rw-r--r--src/plugins/platforms/vnc/qvncscreen.cpp2
-rw-r--r--src/plugins/platforms/wasm/qtloader.js101
-rw-r--r--src/plugins/platforms/wasm/qwasmbackingstore.cpp6
-rw-r--r--src/plugins/platforms/wasm/qwasmbackingstore.h1
-rw-r--r--src/plugins/platforms/wasm/qwasmclipboard.cpp229
-rw-r--r--src/plugins/platforms/wasm/qwasmclipboard.h60
-rw-r--r--src/plugins/platforms/wasm/qwasmcompositor.cpp63
-rw-r--r--src/plugins/platforms/wasm/qwasmcompositor.h10
-rw-r--r--src/plugins/platforms/wasm/qwasmcursor.cpp22
-rw-r--r--src/plugins/platforms/wasm/qwasmeventtranslator.cpp733
-rw-r--r--src/plugins/platforms/wasm/qwasmeventtranslator.h184
-rw-r--r--src/plugins/platforms/wasm/qwasmfontdatabase.cpp8
-rw-r--r--src/plugins/platforms/wasm/qwasmfontdatabase.h1
-rw-r--r--src/plugins/platforms/wasm/qwasmintegration.cpp205
-rw-r--r--src/plugins/platforms/wasm/qwasmintegration.h34
-rw-r--r--src/plugins/platforms/wasm/qwasmoffscreensurface.cpp41
-rw-r--r--src/plugins/platforms/wasm/qwasmoffscreensurface.h49
-rw-r--r--src/plugins/platforms/wasm/qwasmopenglcontext.cpp81
-rw-r--r--src/plugins/platforms/wasm/qwasmopenglcontext.h8
-rw-r--r--src/plugins/platforms/wasm/qwasmscreen.cpp92
-rw-r--r--src/plugins/platforms/wasm/qwasmscreen.h25
-rw-r--r--src/plugins/platforms/wasm/qwasmservices.cpp45
-rw-r--r--src/plugins/platforms/wasm/qwasmservices.h45
-rw-r--r--src/plugins/platforms/wasm/qwasmtheme.cpp15
-rw-r--r--src/plugins/platforms/wasm/qwasmtheme.h3
-rw-r--r--src/plugins/platforms/wasm/qwasmwindow.cpp10
-rw-r--r--src/plugins/platforms/wasm/qwasmwindow.h1
-rw-r--r--src/plugins/platforms/wasm/wasm.pro13
-rw-r--r--src/plugins/platforms/wasm/wasm_shell.html26
-rw-r--r--src/plugins/platforms/windows/qwindowsinputcontext.cpp2
-rw-r--r--src/plugins/platforms/windows/qwindowsnativeinterface.cpp2
-rw-r--r--src/plugins/platforms/windows/qwindowstabletsupport.cpp72
-rw-r--r--src/plugins/platforms/windows/qwindowstabletsupport.h2
-rw-r--r--src/plugins/platforms/windows/qwindowstheme.cpp19
-rw-r--r--src/plugins/platforms/windows/qwindowswindow.cpp8
-rw-r--r--src/plugins/platforms/windows/qwindowswindow.h2
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp2
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qpaintengine_x11_p.h2
-rw-r--r--src/plugins/platforms/xcb/qxcbbackingstore.cpp17
-rw-r--r--src/plugins/platforms/xcb/qxcbclipboard.cpp23
-rw-r--r--src/plugins/platforms/xcb/qxcbclipboard.h4
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.cpp28
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.h10
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_basic.cpp14
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_basic.h5
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_screens.cpp4
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_xi2.cpp6
-rw-r--r--src/plugins/platforms/xcb/qxcbeventqueue.cpp6
-rw-r--r--src/plugins/platforms/xcb/qxcbintegration.cpp3
-rw-r--r--src/plugins/platforms/xcb/qxcbkeyboard.cpp729
-rw-r--r--src/plugins/platforms/xcb/qxcbkeyboard.h36
-rw-r--r--src/plugins/platforms/xcb/qxcbnativeinterface.cpp2
-rw-r--r--src/plugins/platforms/xcb/qxcbscreen.cpp2
-rw-r--r--src/plugins/platforms/xcb/qxcbsessionmanager.h2
-rw-r--r--src/plugins/platforms/xcb/qxcbwindow.cpp33
-rw-r--r--src/plugins/platforms/xcb/qxcbxkbcommon.h233
-rw-r--r--src/plugins/platforms/xcb/xcb_qpa_lib.pro4
-rw-r--r--src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.h2
-rw-r--r--src/plugins/printsupport/cups/qcupsprintengine_p.h4
-rw-r--r--src/plugins/printsupport/windows/qwindowsprintersupport.h2
-rw-r--r--src/plugins/sqldrivers/mysql/qsql_mysql.cpp12
-rw-r--r--src/plugins/sqldrivers/odbc/qsql_odbc.cpp2
-rw-r--r--src/plugins/sqldrivers/psql/main.cpp8
-rw-r--r--src/plugins/sqldrivers/psql/qsql_psql.cpp296
-rw-r--r--src/plugins/sqldrivers/psql/qsql_psql_p.h18
-rw-r--r--src/plugins/styles/android/qandroidstyle_p.h2
-rw-r--r--src/plugins/styles/mac/qmacstyle_mac.mm7
-rw-r--r--src/plugins/styles/mac/qmacstyle_mac_p.h2
-rw-r--r--src/plugins/styles/windowsvista/qwindowsvistastyle.cpp12
-rw-r--r--src/plugins/styles/windowsvista/qwindowsvistastyle_p.h2
-rw-r--r--src/plugins/styles/windowsvista/qwindowsxpstyle.cpp9
-rw-r--r--src/plugins/styles/windowsvista/qwindowsxpstyle_p.h2
126 files changed, 2971 insertions, 3186 deletions
diff --git a/src/plugins/bearer/networkmanager/qnetworkmanagerengine.cpp b/src/plugins/bearer/networkmanager/qnetworkmanagerengine.cpp
index 543e66491d..e74b1cf744 100644
--- a/src/plugins/bearer/networkmanager/qnetworkmanagerengine.cpp
+++ b/src/plugins/bearer/networkmanager/qnetworkmanagerengine.cpp
@@ -465,7 +465,7 @@ void QNetworkManagerEngine::newConnection(const QDBusObjectPath &path,
cpPriv->state |= QNetworkConfiguration::Active;
if (deviceType == DEVICE_TYPE_ETHERNET) {
- for (const auto *interfaceDevice : interfaceDevices) {
+ for (auto interfaceDevice : qAsConst(interfaceDevices)) {
if (interfaceDevice->deviceType() == deviceType) {
auto *wiredDevice = wiredDevices.value(interfaceDevice->path());
if (wiredDevice && wiredDevice->carrier()) {
@@ -716,7 +716,7 @@ QNetworkSession::State QNetworkManagerEngine::sessionStateForId(const QString &i
if (!ptr->isValid)
return QNetworkSession::Invalid;
- for (QNetworkManagerConnectionActive *activeConnection : activeConnectionsList) {
+ for (QNetworkManagerConnectionActive *activeConnection : qAsConst(activeConnectionsList)) {
const QString identifier = activeConnection->connection().path();
if (id == identifier) {
diff --git a/src/plugins/bearer/qnetworksession_impl.cpp b/src/plugins/bearer/qnetworksession_impl.cpp
index 903525a204..c6b678ab20 100644
--- a/src/plugins/bearer/qnetworksession_impl.cpp
+++ b/src/plugins/bearer/qnetworksession_impl.cpp
@@ -83,8 +83,6 @@ Q_SIGNALS:
void forcedSessionClose(const QNetworkConfiguration &config);
};
-#include "qnetworksession_impl.moc"
-
Q_GLOBAL_STATIC(QNetworkSessionManagerPrivate, sessionManager);
void QNetworkSessionPrivateImpl::syncStateWithInterface()
@@ -433,3 +431,5 @@ void QNetworkSessionPrivateImpl::decrementTimeout()
}
QT_END_NAMESPACE
+
+#include "qnetworksession_impl.moc"
diff --git a/src/plugins/generic/tuiotouch/qtuiohandler.cpp b/src/plugins/generic/tuiotouch/qtuiohandler.cpp
index bb18ba5085..cb82672acd 100644
--- a/src/plugins/generic/tuiotouch/qtuiohandler.cpp
+++ b/src/plugins/generic/tuiotouch/qtuiohandler.cpp
@@ -169,7 +169,7 @@ void QTuioHandler::processPackets()
messages.push_back(msg);
}
- for (const QOscMessage &message : messages) {
+ for (const QOscMessage &message : qAsConst(messages)) {
if (message.addressPattern() == "/tuio/2Dcur") {
QList<QVariant> arguments = message.arguments();
if (arguments.count() == 0) {
@@ -368,12 +368,12 @@ void QTuioHandler::process2DCurFseq(const QOscMessage &message)
QList<QWindowSystemInterface::TouchPoint> tpl;
tpl.reserve(m_activeCursors.size() + m_deadCursors.size());
- for (const QTuioCursor &tc : m_activeCursors) {
+ for (const QTuioCursor &tc : qAsConst(m_activeCursors)) {
QWindowSystemInterface::TouchPoint tp = cursorToTouchPoint(tc, win);
tpl.append(tp);
}
- for (const QTuioCursor &tc : m_deadCursors) {
+ for (const QTuioCursor &tc : qAsConst(m_deadCursors)) {
QWindowSystemInterface::TouchPoint tp = cursorToTouchPoint(tc, win);
tp.state = Qt::TouchPointReleased;
tpl.append(tp);
@@ -542,12 +542,12 @@ void QTuioHandler::process2DObjFseq(const QOscMessage &message)
QList<QWindowSystemInterface::TouchPoint> tpl;
tpl.reserve(m_activeTokens.size() + m_deadTokens.size());
- for (const QTuioToken & t : m_activeTokens) {
+ for (const QTuioToken & t : qAsConst(m_activeTokens)) {
QWindowSystemInterface::TouchPoint tp = tokenToTouchPoint(t, win);
tpl.append(tp);
}
- for (const QTuioToken & t : m_deadTokens) {
+ for (const QTuioToken & t : qAsConst(m_deadTokens)) {
QWindowSystemInterface::TouchPoint tp = tokenToTouchPoint(t, win);
tp.state = Qt::TouchPointReleased;
tp.velocity = QVector2D();
diff --git a/src/plugins/imageformats/gif/qgifhandler.cpp b/src/plugins/imageformats/gif/qgifhandler.cpp
index 1aef1a24d2..a6029b691c 100644
--- a/src/plugins/imageformats/gif/qgifhandler.cpp
+++ b/src/plugins/imageformats/gif/qgifhandler.cpp
@@ -1215,9 +1215,11 @@ int QGifHandler::currentImageNumber() const
return frameNumber;
}
+#if QT_DEPRECATED_SINCE(5, 13)
QByteArray QGifHandler::name() const
{
return "gif";
}
+#endif
QT_END_NAMESPACE
diff --git a/src/plugins/imageformats/gif/qgifhandler_p.h b/src/plugins/imageformats/gif/qgifhandler_p.h
index b004ee610d..c6592043ce 100644
--- a/src/plugins/imageformats/gif/qgifhandler_p.h
+++ b/src/plugins/imageformats/gif/qgifhandler_p.h
@@ -73,7 +73,9 @@ public:
bool read(QImage *image) override;
bool write(const QImage &image) override;
+#if QT_DEPRECATED_SINCE(5, 13)
QByteArray name() const override;
+#endif
static bool canRead(QIODevice *device);
diff --git a/src/plugins/imageformats/ico/qicohandler.cpp b/src/plugins/imageformats/ico/qicohandler.cpp
index 4908850cc5..c8e31dceac 100644
--- a/src/plugins/imageformats/ico/qicohandler.cpp
+++ b/src/plugins/imageformats/ico/qicohandler.cpp
@@ -818,6 +818,7 @@ bool QtIcoHandler::write(const QImage &image)
return ICOReader::write(device, imgs);
}
+#if QT_DEPRECATED_SINCE(5, 13)
/*!
* Return the common identifier of the format.
* For ICO format this will return "ico".
@@ -826,7 +827,7 @@ QByteArray QtIcoHandler::name() const
{
return "ico";
}
-
+#endif
/*! \reimp
diff --git a/src/plugins/imageformats/ico/qicohandler.h b/src/plugins/imageformats/ico/qicohandler.h
index 435f036113..328dfce47e 100644
--- a/src/plugins/imageformats/ico/qicohandler.h
+++ b/src/plugins/imageformats/ico/qicohandler.h
@@ -54,7 +54,9 @@ public:
bool read(QImage *image) override;
bool write(const QImage &image) override;
+#if QT_DEPRECATED_SINCE(5, 13)
QByteArray name() const override;
+#endif
int imageCount() const override;
bool jumpToImage(int imageNumber) override;
diff --git a/src/plugins/imageformats/jpeg/qjpeghandler.cpp b/src/plugins/imageformats/jpeg/qjpeghandler.cpp
index 54fe857908..aed2900356 100644
--- a/src/plugins/imageformats/jpeg/qjpeghandler.cpp
+++ b/src/plugins/imageformats/jpeg/qjpeghandler.cpp
@@ -1124,9 +1124,11 @@ void QJpegHandler::setOption(ImageOption option, const QVariant &value)
}
}
+#if QT_DEPRECATED_SINCE(5, 13)
QByteArray QJpegHandler::name() const
{
return "jpeg";
}
+#endif
QT_END_NAMESPACE
diff --git a/src/plugins/imageformats/jpeg/qjpeghandler_p.h b/src/plugins/imageformats/jpeg/qjpeghandler_p.h
index d832bf82f3..fafa930c11 100644
--- a/src/plugins/imageformats/jpeg/qjpeghandler_p.h
+++ b/src/plugins/imageformats/jpeg/qjpeghandler_p.h
@@ -68,7 +68,9 @@ public:
bool read(QImage *image) override;
bool write(const QImage &image) override;
+#if QT_DEPRECATED_SINCE(5, 13)
QByteArray name() const override;
+#endif
static bool canRead(QIODevice *device);
diff --git a/src/plugins/platforminputcontexts/compose/compose.pro b/src/plugins/platforminputcontexts/compose/compose.pro
index 68bc2c3466..2e2f8600c3 100644
--- a/src/plugins/platforminputcontexts/compose/compose.pro
+++ b/src/plugins/platforminputcontexts/compose/compose.pro
@@ -3,18 +3,14 @@ TARGET = composeplatforminputcontextplugin
QT += core-private gui-private
SOURCES += $$PWD/qcomposeplatforminputcontextmain.cpp \
- $$PWD/qcomposeplatforminputcontext.cpp \
- $$PWD/generator/qtablegenerator.cpp \
+ $$PWD/qcomposeplatforminputcontext.cpp
-HEADERS += $$PWD/qcomposeplatforminputcontext.h \
- $$PWD/generator/qtablegenerator.h \
+HEADERS += $$PWD/qcomposeplatforminputcontext.h
QMAKE_USE_PRIVATE += xkbcommon
include($$OUT_PWD/../../../gui/qtgui-config.pri)
-DEFINES += X11_PREFIX='\\"$$QMAKE_X11_PREFIX\\"'
-
OTHER_FILES += $$PWD/compose.json
PLUGIN_TYPE = platforminputcontexts
diff --git a/src/plugins/platforminputcontexts/compose/generator/qtablegenerator.cpp b/src/plugins/platforminputcontexts/compose/generator/qtablegenerator.cpp
deleted file mode 100644
index b5a0a5bbeb..0000000000
--- a/src/plugins/platforminputcontexts/compose/generator/qtablegenerator.cpp
+++ /dev/null
@@ -1,658 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 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 "qtablegenerator.h"
-
-#include <QtCore/QByteArray>
-#include <QtCore/QTextCodec>
-#include <QtCore/QDebug>
-#include <QtCore/QDir>
-#include <QtCore/QStringList>
-#include <QtCore/QString>
-#include <QtCore/QSaveFile>
-#include <QtCore/QStandardPaths>
-#include <private/qcore_unix_p.h>
-
-#include <algorithm>
-
-#include <xkbcommon/xkbcommon.h>
-
-#include <locale.h> // LC_CTYPE
-#include <string.h> // strchr, strncmp, etc.
-#include <strings.h> // strncasecmp
-#include <clocale> // LC_CTYPE
-
-static const quint32 SupportedCacheVersion = 1;
-
-/*
- In short on how and why the "Compose" file is cached:
-
- The "Compose" file is large, for en_US it's likely located at:
- /usr/share/X11/locale/en_US.UTF-8/Compose
- and it has about 6000 string lines.
- Q(Gui)Applications parse this file each time they're created. On modern CPUs
- it incurs a 4-10 ms startup penalty of each Qt gui app, on older CPUs -
- tens of ms or more.
- Since the "Compose" file (almost) never changes using a pre-parsed
- cache file instead of the "Compose" file is a good idea to improve Qt5
- application startup time by about 5+ ms (or tens of ms on older CPUs).
-
- The cache file contains the contents of the QComposeCacheFileHeader struct at the
- beginning followed by the pre-parsed contents of the "Compose" file.
-
- struct QComposeCacheFileHeader stores
- (a) The cache version - in the unlikely event that some day one might need
- to break compatibility.
- (b) The (cache) file size.
- (c) The lastModified field tracks if anything changed since the last time
- the cache file was saved.
- If anything did change then we read the compose file and save (cache) it
- in binary/pre-parsed format, which should happen extremely rarely if at all.
-*/
-
-struct QComposeCacheFileHeader
-{
- quint32 cacheVersion;
- // The compiler will add 4 padding bytes anyway.
- // Reserve them explicitly to possibly use in the future.
- quint32 reserved;
- quint64 fileSize;
- qint64 lastModified;
-};
-
-// localHostName() copied from qtbase/src/corelib/io/qlockfile_unix.cpp
-static QByteArray localHostName()
-{
- QByteArray hostName(512, Qt::Uninitialized);
- if (gethostname(hostName.data(), hostName.size()) == -1)
- return QByteArray();
- hostName.truncate(strlen(hostName.data()));
- return hostName;
-}
-
-/*
- Reads metadata about the Compose file. Later used to determine if the
- compose cache should be updated. The fileSize field will be zero on failure.
-*/
-static QComposeCacheFileHeader readFileMetadata(const QString &path)
-{
- quint64 fileSize = 0;
- qint64 lastModified = 0;
- const QByteArray pathBytes = QFile::encodeName(path);
- QT_STATBUF st;
- if (QT_STAT(pathBytes.data(), &st) == 0) {
- lastModified = st.st_mtime;
- fileSize = st.st_size;
- }
- QComposeCacheFileHeader info = { 0, 0, fileSize, lastModified };
- return info;
-}
-
-static const QString getCacheFilePath()
-{
- QFile machineIdFile("/var/lib/dbus/machine-id");
- QString machineId;
- if (machineIdFile.exists()) {
- if (machineIdFile.open(QIODevice::ReadOnly))
- machineId = QString::fromLatin1(machineIdFile.readAll().trimmed());
- }
- if (machineId.isEmpty())
- machineId = localHostName();
- const QString dirPath = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation);
-
- if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
- return dirPath + QLatin1String("/qt_compose_cache_big_endian_") + machineId;
- return dirPath + QLatin1String("/qt_compose_cache_little_endian_") + machineId;
-}
-
-// Returns empty vector on failure
-static QVector<QComposeTableElement> loadCache(const QComposeCacheFileHeader &composeInfo)
-{
- QVector<QComposeTableElement> vec;
- const QString cacheFilePath = getCacheFilePath();
- QFile inputFile(cacheFilePath);
-
- if (!inputFile.open(QIODevice::ReadOnly))
- return vec;
- QComposeCacheFileHeader cacheInfo;
- // use a "buffer" variable to make the line after this one more readable.
- char *buffer = reinterpret_cast<char*>(&cacheInfo);
-
- if (inputFile.read(buffer, sizeof cacheInfo) != sizeof cacheInfo)
- return vec;
- if (cacheInfo.fileSize == 0)
- return vec;
- // using "!=" just in case someone replaced with a backup that existed before
- if (cacheInfo.lastModified != composeInfo.lastModified)
- return vec;
- if (cacheInfo.cacheVersion != SupportedCacheVersion)
- return vec;
- const QByteArray pathBytes = QFile::encodeName(cacheFilePath);
- QT_STATBUF st;
- if (QT_STAT(pathBytes.data(), &st) != 0)
- return vec;
- const off_t fileSize = st.st_size;
- if (fileSize > 1024 * 1024 * 5) {
- // The cache file size is usually about 150KB, so if its size is over
- // say 5MB then somebody inflated the file, abort.
- return vec;
- }
- const off_t bufferSize = fileSize - (sizeof cacheInfo);
- const size_t elemSize = sizeof (struct QComposeTableElement);
- const int elemCount = bufferSize / elemSize;
- const QByteArray ba = inputFile.read(bufferSize);
- const char *data = ba.data();
- // Since we know the number of the (many) elements and their size in
- // advance calling vector.reserve(..) seems reasonable.
- vec.reserve(elemCount);
-
- for (int i = 0; i < elemCount; i++) {
- const QComposeTableElement *elem =
- reinterpret_cast<const QComposeTableElement*>(data + (i * elemSize));
- vec.push_back(*elem);
- }
- return vec;
-}
-
-// Returns true on success, false otherwise.
-static bool saveCache(const QComposeCacheFileHeader &info, const QVector<QComposeTableElement> &vec)
-{
- const QString filePath = getCacheFilePath();
-#if QT_CONFIG(temporaryfile)
- QSaveFile outputFile(filePath);
-#else
- QFile outputFile(filePath);
-#endif
- if (!outputFile.open(QIODevice::WriteOnly))
- return false;
- const char *data = reinterpret_cast<const char*>(&info);
-
- if (outputFile.write(data, sizeof info) != sizeof info)
- return false;
- data = reinterpret_cast<const char*>(vec.constData());
- const qint64 size = vec.size() * (sizeof (struct QComposeTableElement));
-
- if (outputFile.write(data, size) != size)
- return false;
-#if QT_CONFIG(temporaryfile)
- return outputFile.commit();
-#else
- return true;
-#endif
-}
-
-TableGenerator::TableGenerator() : m_state(NoErrors),
- m_systemComposeDir(QString())
-{
- initPossibleLocations();
- QString composeFilePath = findComposeFile();
-#ifdef DEBUG_GENERATOR
-// don't use cache when in debug mode.
- if (!composeFilePath.isEmpty())
- qDebug() << "Using Compose file from: " << composeFilePath;
-#else
- QComposeCacheFileHeader fileInfo = readFileMetadata(composeFilePath);
- if (fileInfo.fileSize != 0)
- m_composeTable = loadCache(fileInfo);
-#endif
- if (m_composeTable.isEmpty() && cleanState()) {
- if (composeFilePath.isEmpty()) {
- m_state = MissingComposeFile;
- } else {
- QFile composeFile(composeFilePath);
- composeFile.open(QIODevice::ReadOnly);
- parseComposeFile(&composeFile);
- orderComposeTable();
- if (m_composeTable.isEmpty()) {
- m_state = EmptyTable;
-#ifndef DEBUG_GENERATOR
-// don't save cache when in debug mode
- } else {
- fileInfo.cacheVersion = SupportedCacheVersion;
- saveCache(fileInfo, m_composeTable);
-#endif
- }
- }
- }
-#ifdef DEBUG_GENERATOR
- printComposeTable();
-#endif
-}
-
-void TableGenerator::initPossibleLocations()
-{
- // Compose files come as a part of Xlib library. Xlib doesn't provide
- // a mechanism how to retrieve the location of these files reliably, since it was
- // never meant for external software to parse compose tables directly. Best we
- // can do is to hardcode search paths. To add an extra system path use
- // the QTCOMPOSE environment variable
- m_possibleLocations.reserve(7);
- if (qEnvironmentVariableIsSet("QTCOMPOSE"))
- m_possibleLocations.append(QString::fromLocal8Bit(qgetenv("QTCOMPOSE")));
- m_possibleLocations.append(QStringLiteral("/usr/share/X11/locale"));
- m_possibleLocations.append(QStringLiteral("/usr/local/share/X11/locale"));
- m_possibleLocations.append(QStringLiteral("/usr/lib/X11/locale"));
- m_possibleLocations.append(QStringLiteral("/usr/local/lib/X11/locale"));
- m_possibleLocations.append(QStringLiteral(X11_PREFIX "/share/X11/locale"));
- m_possibleLocations.append(QStringLiteral(X11_PREFIX "/lib/X11/locale"));
-}
-
-QString TableGenerator::findComposeFile()
-{
- // check if XCOMPOSEFILE points to a Compose file
- if (qEnvironmentVariableIsSet("XCOMPOSEFILE")) {
- const QString path = QFile::decodeName(qgetenv("XCOMPOSEFILE"));
- if (QFile::exists(path))
- return path;
- else
- qWarning("$XCOMPOSEFILE doesn't point to an existing file");
- }
-
- // check if user’s home directory has a file named .XCompose
- if (cleanState()) {
- QString path = qgetenv("HOME") + QLatin1String("/.XCompose");
- if (QFile::exists(path))
- return path;
- }
-
- // check for the system provided compose files
- if (cleanState()) {
- QString table = composeTableForLocale();
- if (cleanState()) {
- if (table.isEmpty())
- // no table mappings for the system's locale in the compose.dir
- m_state = UnsupportedLocale;
- else {
- QString path = QDir(systemComposeDir()).filePath(table);
- if (QFile::exists(path))
- return path;
- }
- }
- }
- return QString();
-}
-
-QString TableGenerator::composeTableForLocale()
-{
- QByteArray loc = locale().toUpper().toUtf8();
- QString table = readLocaleMappings(loc);
- if (table.isEmpty())
- table = readLocaleMappings(readLocaleAliases(loc));
- return table;
-}
-
-bool TableGenerator::findSystemComposeDir()
-{
- bool found = false;
- for (int i = 0; i < m_possibleLocations.size(); ++i) {
- QString path = m_possibleLocations.at(i);
- if (QFile::exists(path + QLatin1String("/compose.dir"))) {
- m_systemComposeDir = path;
- found = true;
- break;
- }
- }
-
- if (!found) {
- // should we ask to report this in the qt bug tracker?
- m_state = UnknownSystemComposeDir;
- qWarning("Qt Warning: Could not find a location of the system's Compose files. "
- "Consider setting the QTCOMPOSE environment variable.");
- }
-
- return found;
-}
-
-QString TableGenerator::systemComposeDir()
-{
- if (m_systemComposeDir.isNull()
- && !findSystemComposeDir()) {
- return QLatin1String("$QTCOMPOSE");
- }
-
- return m_systemComposeDir;
-}
-
-QString TableGenerator::locale() const
-{
- char *name = setlocale(LC_CTYPE, (char *)0);
- return QLatin1String(name);
-}
-
-QString TableGenerator::readLocaleMappings(const QByteArray &locale)
-{
- QString file;
- if (locale.isEmpty())
- return file;
-
- QFile mappings(systemComposeDir() + QLatin1String("/compose.dir"));
- if (mappings.open(QIODevice::ReadOnly)) {
- const int localeNameLength = locale.size();
- const char * const localeData = locale.constData();
-
- char l[1024];
- // formating of compose.dir has some inconsistencies
- while (!mappings.atEnd()) {
- int read = mappings.readLine(l, sizeof(l));
- if (read <= 0)
- break;
-
- char *line = l;
- if (*line >= 'a' && *line <= 'z') {
- // file name
- while (*line && *line != ':' && *line != ' ' && *line != '\t')
- ++line;
- if (!*line)
- continue;
- const char * const composeFileNameEnd = line;
- *line = '\0';
- ++line;
-
- // locale name
- while (*line && (*line == ' ' || *line == '\t'))
- ++line;
- const char * const lc = line;
- while (*line && *line != ' ' && *line != '\t' && *line != '\n')
- ++line;
- *line = '\0';
- if (localeNameLength == (line - lc) && !strncasecmp(lc, localeData, line - lc)) {
- file = QString::fromLocal8Bit(l, composeFileNameEnd - l);
- break;
- }
- }
- }
- mappings.close();
- }
- return file;
-}
-
-QByteArray TableGenerator::readLocaleAliases(const QByteArray &locale)
-{
- QFile aliases(systemComposeDir() + QLatin1String("/locale.alias"));
- QByteArray fullLocaleName;
- if (aliases.open(QIODevice::ReadOnly)) {
- while (!aliases.atEnd()) {
- char l[1024];
- int read = aliases.readLine(l, sizeof(l));
- char *line = l;
- if (read && ((*line >= 'a' && *line <= 'z') ||
- (*line >= 'A' && *line <= 'Z'))) {
- const char *alias = line;
- while (*line && *line != ':' && *line != ' ' && *line != '\t')
- ++line;
- if (!*line)
- continue;
- *line = 0;
- if (locale.size() == (line - alias)
- && !strncasecmp(alias, locale.constData(), line - alias)) {
- // found a match for alias, read the real locale name
- ++line;
- while (*line && (*line == ' ' || *line == '\t'))
- ++line;
- const char *fullName = line;
- while (*line && *line != ' ' && *line != '\t' && *line != '\n')
- ++line;
- *line = 0;
- fullLocaleName = fullName;
-#ifdef DEBUG_GENERATOR
- qDebug() << "Alias for: " << alias << "is: " << fullLocaleName;
- break;
-#endif
- }
- }
- }
- aliases.close();
- }
- return fullLocaleName;
-}
-
-bool TableGenerator::processFile(const QString &composeFileName)
-{
- QFile composeFile(composeFileName);
- if (composeFile.open(QIODevice::ReadOnly)) {
- parseComposeFile(&composeFile);
- return true;
- }
- qWarning() << QString(QLatin1String("Qt Warning: Compose file: \"%1\" can't be found"))
- .arg(composeFile.fileName());
- return false;
-}
-
-TableGenerator::~TableGenerator()
-{
-}
-
-QVector<QComposeTableElement> TableGenerator::composeTable() const
-{
- return m_composeTable;
-}
-
-void TableGenerator::parseComposeFile(QFile *composeFile)
-{
-#ifdef DEBUG_GENERATOR
- qDebug() << "TableGenerator::parseComposeFile: " << composeFile->fileName();
-#endif
-
- char line[1024];
- while (!composeFile->atEnd()) {
- composeFile->readLine(line, sizeof(line));
- if (*line == '<')
- parseKeySequence(line);
- else if (!strncmp(line, "include", 7))
- parseIncludeInstruction(QString::fromLocal8Bit(line));
- }
-
- composeFile->close();
-}
-
-void TableGenerator::parseIncludeInstruction(QString line)
-{
- // Parse something that looks like:
- // include "/usr/share/X11/locale/en_US.UTF-8/Compose"
- QString quote = QStringLiteral("\"");
- line.remove(0, line.indexOf(quote) + 1);
- line.chop(line.length() - line.indexOf(quote));
-
- // expand substitutions if present
- line.replace(QLatin1String("%H"), QString(qgetenv("HOME")));
- line.replace(QLatin1String("%L"), systemComposeDir() + QLatin1Char('/') + composeTableForLocale());
- line.replace(QLatin1String("%S"), systemComposeDir());
-
- processFile(line);
-}
-
-ushort TableGenerator::keysymToUtf8(quint32 sym)
-{
- QByteArray chars;
- int bytes;
- chars.resize(8);
- bytes = xkb_keysym_to_utf8(sym, chars.data(), chars.size());
- if (bytes == -1)
- qWarning("TableGenerator::keysymToUtf8 - buffer too small");
-
- chars.resize(bytes-1);
-
-#ifdef DEBUG_GENERATOR
- QTextCodec *codec = QTextCodec::codecForLocale();
- qDebug() << QString("keysym - 0x%1 : utf8 - %2").arg(QString::number(sym, 16))
- .arg(codec->toUnicode(chars));
-#endif
- return QString::fromUtf8(chars).at(0).unicode();
-}
-
-static inline int fromBase8(const char *s, const char *end)
-{
- int result = 0;
- while (*s && s != end) {
- if (*s < '0' || *s > '7')
- return 0;
- result *= 8;
- result += *s - '0';
- ++s;
- }
- return result;
-}
-
-static inline int fromBase16(const char *s, const char *end)
-{
- int result = 0;
- while (*s && s != end) {
- result *= 16;
- if (*s >= '0' && *s <= '9')
- result += *s - '0';
- else if (*s >= 'a' && *s <= 'f')
- result += *s - 'a' + 10;
- else if (*s >= 'A' && *s <= 'F')
- result += *s - 'A' + 10;
- else
- return 0;
- ++s;
- }
- return result;
-}
-
-void TableGenerator::parseKeySequence(char *line)
-{
- // we are interested in the lines with the following format:
- // <Multi_key> <numbersign> <S> : "♬" U266c # BEAMED SIXTEENTH NOTE
- char *keysEnd = strchr(line, ':');
- if (!keysEnd)
- return;
-
- QComposeTableElement elem;
- // find the composed value - strings may be direct text encoded in the locale
- // for which the compose file is to be used, or an escaped octal or hexadecimal
- // character code. Octal codes are specified as "\123" and hexadecimal codes as "\0x123a".
- char *composeValue = strchr(keysEnd, '"');
- if (!composeValue)
- return;
- ++composeValue;
-
- char *composeValueEnd = strchr(composeValue, '"');
- if (!composeValueEnd)
- return;
-
- // if composed value is a quotation mark adjust the end pointer
- if (composeValueEnd[1] == '"')
- ++composeValueEnd;
-
- if (*composeValue == '\\' && composeValue[1] >= '0' && composeValue[1] <= '9') {
- // handle octal and hex code values
- char detectBase = composeValue[2];
- if (detectBase == 'x') {
- // hexadecimal character code
- elem.value = keysymToUtf8(fromBase16(composeValue + 3, composeValueEnd));
- } else {
- // octal character code
- elem.value = keysymToUtf8(fromBase8(composeValue + 1, composeValueEnd));
- }
- } else {
- // handle direct text encoded in the locale
- if (*composeValue == '\\')
- ++composeValue;
- elem.value = QString::fromLocal8Bit(composeValue, composeValueEnd - composeValue).at(0).unicode();
- ++composeValue;
- }
-
-#ifdef DEBUG_GENERATOR
- // find the comment
- elem.comment = QString::fromLocal8Bit(composeValueEnd + 1).trimmed();
-#endif
-
- // find the key sequence and convert to X11 keysym
- char *k = line;
- const char *kend = keysEnd;
-
- for (int i = 0; i < QT_KEYSEQUENCE_MAX_LEN; i++) {
- // find the next pair of angle brackets and get the contents within
- while (k < kend && *k != '<')
- ++k;
- char *sym = ++k;
- while (k < kend && *k != '>')
- ++k;
- *k = '\0';
- if (k < kend) {
- elem.keys[i] = xkb_keysym_from_name(sym, (xkb_keysym_flags)0);
- if (elem.keys[i] == XKB_KEY_NoSymbol) {
- if (!strcmp(sym, "dead_inverted_breve"))
- elem.keys[i] = XKB_KEY_dead_invertedbreve;
- else if (!strcmp(sym, "dead_double_grave"))
- elem.keys[i] = XKB_KEY_dead_doublegrave;
-#ifdef DEBUG_GENERATOR
- else
- qWarning() << QString("Qt Warning - invalid keysym: %1").arg(sym);
-#endif
- }
- } else {
- elem.keys[i] = 0;
- }
- }
- m_composeTable.append(elem);
-}
-
-void TableGenerator::printComposeTable() const
-{
-#ifdef DEBUG_GENERATOR
-# ifndef QT_NO_DEBUG_STREAM
- if (m_composeTable.isEmpty())
- return;
-
- QDebug ds = qDebug() << "output:\n";
- ds.nospace();
- const int tableSize = m_composeTable.size();
- for (int i = 0; i < tableSize; ++i) {
- const QComposeTableElement &elem = m_composeTable.at(i);
- ds << "{ {";
- for (int j = 0; j < QT_KEYSEQUENCE_MAX_LEN; j++) {
- ds << hex << showbase << elem.keys[j] << ", ";
- }
- ds << "}, " << hex << showbase << elem.value << ", \"\" }, // " << elem.comment << " \n";
- }
-# endif
-#endif
-}
-
-void TableGenerator::orderComposeTable()
-{
- // Stable-sorting to ensure that the item that appeared before the other in the
- // original container will still appear first after the sort. This property is
- // needed to handle the cases when user re-defines already defined key sequence
- std::stable_sort(m_composeTable.begin(), m_composeTable.end(), ByKeys());
-}
-
diff --git a/src/plugins/platforminputcontexts/compose/generator/qtablegenerator.h b/src/plugins/platforminputcontexts/compose/generator/qtablegenerator.h
deleted file mode 100644
index 4f58358f4e..0000000000
--- a/src/plugins/platforminputcontexts/compose/generator/qtablegenerator.h
+++ /dev/null
@@ -1,145 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 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 QTABLEGENERATOR_H
-#define QTABLEGENERATOR_H
-
-#include <QtCore/QVector>
-#include <QtCore/QFile>
-#include <QtCore/QMap>
-#include <QtCore/QString>
-
-#include <algorithm>
-
-static Q_CONSTEXPR int QT_KEYSEQUENCE_MAX_LEN = 6;
-
-//#define DEBUG_GENERATOR
-
-/* Whenever QComposeTableElement gets modified supportedCacheVersion
- from qtablegenerator.cpp must be bumped. */
-struct QComposeTableElement {
- uint keys[QT_KEYSEQUENCE_MAX_LEN];
- uint value;
-#ifdef DEBUG_GENERATOR
- QString comment;
-#endif
-};
-
-#ifndef DEBUG_GENERATOR
-QT_BEGIN_NAMESPACE
-Q_DECLARE_TYPEINFO(QComposeTableElement, Q_PRIMITIVE_TYPE);
-QT_END_NAMESPACE
-#endif
-
-struct ByKeys
-{
- using uint_array = uint[QT_KEYSEQUENCE_MAX_LEN];
- using result_type = bool;
-
- bool operator()(const uint_array &lhs, const uint_array &rhs) const Q_DECL_NOTHROW
- {
- return std::lexicographical_compare(lhs, lhs + QT_KEYSEQUENCE_MAX_LEN,
- rhs, rhs + QT_KEYSEQUENCE_MAX_LEN);
- }
-
- bool operator()(const uint_array &lhs, const QComposeTableElement &rhs) const Q_DECL_NOTHROW
- {
- return operator()(lhs, rhs.keys);
- }
-
- bool operator()(const QComposeTableElement &lhs, const uint_array &rhs) const Q_DECL_NOTHROW
- {
- return operator()(lhs.keys, rhs);
- }
-
- bool operator()(const QComposeTableElement &lhs, const QComposeTableElement &rhs) const Q_DECL_NOTHROW
- {
- return operator()(lhs.keys, rhs.keys);
- }
-};
-
-class TableGenerator
-{
-
-public:
- enum TableState
- {
- UnsupportedLocale,
- EmptyTable,
- UnknownSystemComposeDir,
- MissingComposeFile,
- NoErrors
- };
-
- TableGenerator();
- ~TableGenerator();
-
- void parseComposeFile(QFile *composeFile);
- void printComposeTable() const;
- void orderComposeTable();
-
- QVector<QComposeTableElement> composeTable() const;
- TableState tableState() const { return m_state; }
-
-protected:
- bool processFile(const QString &composeFileName);
- void parseKeySequence(char *line);
- void parseIncludeInstruction(QString line);
-
- QString findComposeFile();
- bool findSystemComposeDir();
- QString systemComposeDir();
- QString composeTableForLocale();
-
- ushort keysymToUtf8(quint32 sym);
-
- QString readLocaleMappings(const QByteArray &locale);
- QByteArray readLocaleAliases(const QByteArray &locale);
- void initPossibleLocations();
- bool cleanState() const { return m_state == NoErrors; }
- QString locale() const;
-
-private:
- QVector<QComposeTableElement> m_composeTable;
- TableState m_state;
- QString m_systemComposeDir;
- QList<QString> m_possibleLocations;
-};
-
-#endif // QTABLEGENERATOR_H
diff --git a/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp b/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp
index 81a730232c..4e9828663f 100644
--- a/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp
+++ b/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
@@ -36,131 +36,110 @@
** $QT_END_LICENSE$
**
****************************************************************************/
-
#include "qcomposeplatforminputcontext.h"
#include <QtCore/QCoreApplication>
#include <QtGui/QKeyEvent>
-#include <QtCore/QDebug>
+#include <QtGui/QGuiApplication>
-#include <algorithm>
+#include <locale.h>
QT_BEGIN_NAMESPACE
-//#define DEBUG_COMPOSING
+Q_LOGGING_CATEGORY(lcXkbCompose, "qt.xkb.compose")
-static const int ignoreKeys[] = {
- Qt::Key_Shift,
- Qt::Key_Control,
- Qt::Key_Meta,
- Qt::Key_Alt,
- Qt::Key_CapsLock,
- Qt::Key_Super_L,
- Qt::Key_Super_R,
- Qt::Key_Hyper_L,
- Qt::Key_Hyper_R,
- Qt::Key_Mode_switch
-};
+QComposeInputContext::QComposeInputContext()
+{
+ setObjectName(QStringLiteral("QComposeInputContext"));
+ qCDebug(lcXkbCompose, "using xkb compose input context");
+}
-static const int composingKeys[] = {
- Qt::Key_Multi_key,
- Qt::Key_Dead_Grave,
- Qt::Key_Dead_Acute,
- Qt::Key_Dead_Circumflex,
- Qt::Key_Dead_Tilde,
- Qt::Key_Dead_Macron,
- Qt::Key_Dead_Breve,
- Qt::Key_Dead_Abovedot,
- Qt::Key_Dead_Diaeresis,
- Qt::Key_Dead_Abovering,
- Qt::Key_Dead_Doubleacute,
- Qt::Key_Dead_Caron,
- Qt::Key_Dead_Cedilla,
- Qt::Key_Dead_Ogonek,
- Qt::Key_Dead_Iota,
- Qt::Key_Dead_Voiced_Sound,
- Qt::Key_Dead_Semivoiced_Sound,
- Qt::Key_Dead_Belowdot,
- Qt::Key_Dead_Hook,
- Qt::Key_Dead_Horn,
- Qt::Key_Dead_Stroke,
- Qt::Key_Dead_Abovecomma,
- Qt::Key_Dead_Abovereversedcomma,
- Qt::Key_Dead_Doublegrave,
- Qt::Key_Dead_Belowring,
- Qt::Key_Dead_Belowmacron,
- Qt::Key_Dead_Belowcircumflex,
- Qt::Key_Dead_Belowtilde,
- Qt::Key_Dead_Belowbreve,
- Qt::Key_Dead_Belowdiaeresis,
- Qt::Key_Dead_Invertedbreve,
- Qt::Key_Dead_Belowcomma,
- Qt::Key_Dead_Currency,
- Qt::Key_Dead_a,
- Qt::Key_Dead_A,
- Qt::Key_Dead_e,
- Qt::Key_Dead_E,
- Qt::Key_Dead_i,
- Qt::Key_Dead_I,
- Qt::Key_Dead_o,
- Qt::Key_Dead_O,
- Qt::Key_Dead_u,
- Qt::Key_Dead_U,
- Qt::Key_Dead_Small_Schwa,
- Qt::Key_Dead_Capital_Schwa,
- Qt::Key_Dead_Greek,
- Qt::Key_Dead_Lowline,
- Qt::Key_Dead_Aboveverticalline,
- Qt::Key_Dead_Belowverticalline,
- Qt::Key_Dead_Longsolidusoverlay
-};
+QComposeInputContext::~QComposeInputContext()
+{
+ xkb_compose_state_unref(m_composeState);
+ xkb_compose_table_unref(m_composeTable);
+}
-QComposeInputContext::QComposeInputContext()
- : m_tableState(TableGenerator::EmptyTable)
- , m_compositionTableInitialized(false)
+void QComposeInputContext::ensureInitialized()
{
- clearComposeBuffer();
+ if (m_initialized)
+ return;
+
+ if (!m_XkbContext) {
+ qCWarning(lcXkbCompose) << "error: xkb context has not been set on" << metaObject()->className();
+ return;
+ }
+
+ m_initialized = true;
+ const char *locale = setlocale(LC_CTYPE, "");
+ if (!locale)
+ locale = setlocale(LC_CTYPE, nullptr);
+ qCDebug(lcXkbCompose) << "detected locale (LC_CTYPE):" << locale;
+
+ m_composeTable = xkb_compose_table_new_from_locale(m_XkbContext, locale, XKB_COMPOSE_COMPILE_NO_FLAGS);
+ if (m_composeTable)
+ m_composeState = xkb_compose_state_new(m_composeTable, XKB_COMPOSE_STATE_NO_FLAGS);
+
+ if (!m_composeTable) {
+ qCWarning(lcXkbCompose, "failed to create compose table");
+ return;
+ }
+ if (!m_composeState) {
+ qCWarning(lcXkbCompose, "failed to create compose state");
+ return;
+ }
}
bool QComposeInputContext::filterEvent(const QEvent *event)
{
- const QKeyEvent *keyEvent = (const QKeyEvent *)event;
- // should pass only the key presses
- if (keyEvent->type() != QEvent::KeyPress) {
+ auto keyEvent = static_cast<const QKeyEvent *>(event);
+ if (keyEvent->type() != QEvent::KeyPress)
return false;
- }
- // if there were errors when generating the compose table input
- // context should not try to filter anything, simply return false
- if (m_compositionTableInitialized && (m_tableState & TableGenerator::NoErrors) != TableGenerator::NoErrors)
+ if (!inputMethodAccepted())
return false;
- int keyval = keyEvent->key();
- int keysym = 0;
+ // lazy initialization - we don't want to do this on an app startup
+ ensureInitialized();
- if (ignoreKey(keyval))
+ if (!m_composeTable || !m_composeState)
return false;
- if (!composeKey(keyval) && keyEvent->text().isEmpty())
+ xkb_compose_state_feed(m_composeState, keyEvent->nativeVirtualKey());
+
+ switch (xkb_compose_state_get_status(m_composeState)) {
+ case XKB_COMPOSE_COMPOSING:
+ return true;
+ case XKB_COMPOSE_CANCELLED:
+ reset();
return false;
+ case XKB_COMPOSE_COMPOSED:
+ {
+ const int size = xkb_compose_state_get_utf8(m_composeState, nullptr, 0);
+ QVarLengthArray<char, 32> buffer(size + 1);
+ xkb_compose_state_get_utf8(m_composeState, buffer.data(), buffer.size());
+ QString composedText = QString::fromUtf8(buffer.constData());
- keysym = keyEvent->nativeVirtualKey();
+ QInputMethodEvent event;
+ event.setCommitString(composedText);
- int nCompose = 0;
- while (nCompose < QT_KEYSEQUENCE_MAX_LEN && m_composeBuffer[nCompose] != 0)
- nCompose++;
+ if (!m_focusObject && qApp)
+ m_focusObject = qApp->focusObject();
- if (nCompose == QT_KEYSEQUENCE_MAX_LEN) {
- reset();
- nCompose = 0;
- }
+ if (m_focusObject)
+ QCoreApplication::sendEvent(m_focusObject, &event);
+ else
+ qCWarning(lcXkbCompose, "no focus object");
- m_composeBuffer[nCompose] = keysym;
- // check sequence
- if (checkComposeTable())
+ reset();
return true;
-
- return false;
+ }
+ case XKB_COMPOSE_NOTHING:
+ return false;
+ default:
+ Q_UNREACHABLE();
+ return false;
+ }
}
bool QComposeInputContext::isValid() const
@@ -175,7 +154,8 @@ void QComposeInputContext::setFocusObject(QObject *object)
void QComposeInputContext::reset()
{
- clearComposeBuffer();
+ if (m_composeState)
+ xkb_compose_state_reset(m_composeState);
}
void QComposeInputContext::update(Qt::InputMethodQueries q)
@@ -183,125 +163,4 @@ void QComposeInputContext::update(Qt::InputMethodQueries q)
QPlatformInputContext::update(q);
}
-static bool isDuplicate(const QComposeTableElement &lhs, const QComposeTableElement &rhs)
-{
- return std::equal(lhs.keys, lhs.keys + QT_KEYSEQUENCE_MAX_LEN,
- QT_MAKE_CHECKED_ARRAY_ITERATOR(rhs.keys, QT_KEYSEQUENCE_MAX_LEN));
-}
-
-bool QComposeInputContext::checkComposeTable()
-{
- if (!m_compositionTableInitialized) {
- TableGenerator reader;
- m_tableState = reader.tableState();
-
- m_compositionTableInitialized = true;
- if ((m_tableState & TableGenerator::NoErrors) == TableGenerator::NoErrors) {
- m_composeTable = reader.composeTable();
- } else {
-#ifdef DEBUG_COMPOSING
- qDebug( "### FAILED_PARSING ###" );
-#endif
- // if we have errors, don' try to look things up anyways.
- reset();
- return false;
- }
- }
- Q_ASSERT(!m_composeTable.isEmpty());
- QVector<QComposeTableElement>::const_iterator it =
- std::lower_bound(m_composeTable.constBegin(), m_composeTable.constEnd(), m_composeBuffer, ByKeys());
-
- // prevent dereferencing an 'end' iterator, which would result in a crash
- if (it == m_composeTable.constEnd())
- it -= 1;
-
- QComposeTableElement elem = *it;
- // would be nicer if qLowerBound had API that tells if the item was actually found
- if (m_composeBuffer[0] != elem.keys[0]) {
-#ifdef DEBUG_COMPOSING
- qDebug( "### no match ###" );
-#endif
- reset();
- return false;
- }
- // check if compose buffer is matched
- for (int i=0; i < QT_KEYSEQUENCE_MAX_LEN; i++) {
-
- // check if partial match
- if (m_composeBuffer[i] == 0 && elem.keys[i]) {
-#ifdef DEBUG_COMPOSING
- qDebug("### partial match ###");
-#endif
- return true;
- }
-
- if (m_composeBuffer[i] != elem.keys[i]) {
-#ifdef DEBUG_COMPOSING
- qDebug("### different entry ###");
-#endif
- reset();
- return i != 0;
- }
- }
-#ifdef DEBUG_COMPOSING
- qDebug("### match exactly ###");
-#endif
-
- // check if the key sequence is overwriten - see the comment in
- // TableGenerator::orderComposeTable()
- int next = 1;
- do {
- // if we are at the end of the table, then we have nothing to do here
- if (it + next != m_composeTable.constEnd()) {
- QComposeTableElement nextElem = *(it + next);
- if (isDuplicate(elem, nextElem)) {
- elem = nextElem;
- next++;
- continue;
- } else {
- break;
- }
- }
- break;
- } while (true);
-
- commitText(elem.value);
- reset();
-
- return true;
-}
-
-void QComposeInputContext::commitText(uint character) const
-{
- QInputMethodEvent event;
- event.setCommitString(QChar(character));
- QCoreApplication::sendEvent(m_focusObject, &event);
-}
-
-bool QComposeInputContext::ignoreKey(int keyval) const
-{
- for (uint i = 0; i < (sizeof(ignoreKeys) / sizeof(ignoreKeys[0])); i++)
- if (keyval == ignoreKeys[i])
- return true;
-
- return false;
-}
-
-bool QComposeInputContext::composeKey(int keyval) const
-{
- for (uint i = 0; i < (sizeof(composingKeys) / sizeof(composingKeys[0])); i++)
- if (keyval == composingKeys[i])
- return true;
-
- return false;
-}
-
-void QComposeInputContext::clearComposeBuffer()
-{
- for (uint i=0; i < (sizeof(m_composeBuffer) / sizeof(int)); i++)
- m_composeBuffer[i] = 0;
-}
-
-QComposeInputContext::~QComposeInputContext() {}
-
QT_END_NAMESPACE
diff --git a/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.h b/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.h
index 4830959665..b1de1b1094 100644
--- a/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.h
+++ b/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
@@ -36,24 +36,24 @@
** $QT_END_LICENSE$
**
****************************************************************************/
-
#ifndef QCOMPOSEPLATFORMINPUTCONTEXT_H
#define QCOMPOSEPLATFORMINPUTCONTEXT_H
-#include <qpa/qplatforminputcontext.h>
+#include <QtCore/QLoggingCategory>
-#include <QtCore/QList>
+#include <qpa/qplatforminputcontext.h>
-#include "generator/qtablegenerator.h"
+#include <xkbcommon/xkbcommon-compose.h>
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcXkbCompose)
+
class QEvent;
class QComposeInputContext : public QPlatformInputContext
{
Q_OBJECT
-
public:
QComposeInputContext();
~QComposeInputContext();
@@ -62,21 +62,22 @@ public:
void setFocusObject(QObject *object) override;
void reset() override;
void update(Qt::InputMethodQueries) override;
+
bool filterEvent(const QEvent *event) override;
+ // This invokable is called from QXkbCommon::setXkbContext().
+ Q_INVOKABLE void setXkbContext(struct xkb_context *context) { m_XkbContext = context; }
+
protected:
- void clearComposeBuffer();
- bool ignoreKey(int keyval) const;
- bool composeKey(int keyval) const;
- bool checkComposeTable();
- void commitText(uint character) const;
+ void ensureInitialized();
private:
- QObject *m_focusObject;
- QVector<QComposeTableElement> m_composeTable;
- uint m_composeBuffer[QT_KEYSEQUENCE_MAX_LEN];
- TableGenerator::TableState m_tableState;
- bool m_compositionTableInitialized;
+ bool m_initialized = false;
+ xkb_context *m_context = nullptr;
+ xkb_compose_table *m_composeTable = nullptr;
+ xkb_compose_state *m_composeState = nullptr;
+ QObject *m_focusObject = nullptr;
+ struct xkb_context *m_XkbContext = nullptr;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontextmain.cpp b/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontextmain.cpp
index 6b33df65b9..d062d4fd6a 100644
--- a/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontextmain.cpp
+++ b/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontextmain.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
@@ -61,7 +61,7 @@ QComposeInputContext *QComposePlatformInputContextPlugin::create(const QString &
if (system.compare(system, QLatin1String("compose"), Qt::CaseInsensitive) == 0
|| system.compare(system, QLatin1String("xim"), Qt::CaseInsensitive) == 0)
return new QComposeInputContext;
- return 0;
+ return nullptr;
}
QT_END_NAMESPACE
diff --git a/src/plugins/platforminputcontexts/ibus/ibus.pro b/src/plugins/platforminputcontexts/ibus/ibus.pro
index 52836bb8b6..9ba2297e38 100644
--- a/src/plugins/platforminputcontexts/ibus/ibus.pro
+++ b/src/plugins/platforminputcontexts/ibus/ibus.pro
@@ -1,6 +1,6 @@
TARGET = ibusplatforminputcontextplugin
-QT += dbus gui-private
+QT += dbus gui-private xkbcommon_support-private
SOURCES += $$PWD/qibusplatforminputcontext.cpp \
$$PWD/qibusproxy.cpp \
$$PWD/qibusproxyportal.cpp \
diff --git a/src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp b/src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp
index ca315840e2..f2429f24ff 100644
--- a/src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp
+++ b/src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp
@@ -47,7 +47,11 @@
#include <qpa/qplatformcursor.h>
#include <qpa/qplatformscreen.h>
-#include <qpa/qwindowsysteminterface.h>
+#include <qpa/qwindowsysteminterface_p.h>
+
+#include <QtGui/private/qguiapplication_p.h>
+
+#include <QtXkbCommonSupport/private/qxkbcommon_p.h>
#include "qibusproxy.h"
#include "qibusproxyportal.h"
@@ -217,17 +221,14 @@ void QIBusPlatformInputContext::update(Qt::InputMethodQueries q)
&& (q.testFlag(Qt::ImSurroundingText)
|| q.testFlag(Qt::ImCursorPosition)
|| q.testFlag(Qt::ImAnchorPosition))) {
- QInputMethodQueryEvent srrndTextQuery(Qt::ImSurroundingText);
- QInputMethodQueryEvent cursorPosQuery(Qt::ImCursorPosition);
- QInputMethodQueryEvent anchorPosQuery(Qt::ImAnchorPosition);
- QCoreApplication::sendEvent(input, &srrndTextQuery);
- QCoreApplication::sendEvent(input, &cursorPosQuery);
- QCoreApplication::sendEvent(input, &anchorPosQuery);
+ QInputMethodQueryEvent query(Qt::ImSurroundingText | Qt::ImCursorPosition | Qt::ImAnchorPosition);
+
+ QCoreApplication::sendEvent(input, &query);
- QString surroundingText = srrndTextQuery.value(Qt::ImSurroundingText).toString();
- uint cursorPosition = cursorPosQuery.value(Qt::ImCursorPosition).toUInt();
- uint anchorPosition = anchorPosQuery.value(Qt::ImAnchorPosition).toUInt();
+ QString surroundingText = query.value(Qt::ImSurroundingText).toString();
+ uint cursorPosition = query.value(Qt::ImCursorPosition).toUInt();
+ uint anchorPosition = query.value(Qt::ImAnchorPosition).toUInt();
QIBusText text;
text.text = surroundingText;
@@ -336,14 +337,12 @@ void QIBusPlatformInputContext::forwardKeyEvent(uint keyval, uint keycode, uint
if (!input)
return;
- if (debug)
- qDebug() << "forwardKeyEvent" << keyval << keycode << state;
-
QEvent::Type type = QEvent::KeyPress;
if (state & IBUS_RELEASE_MASK)
type = QEvent::KeyRelease;
state &= ~IBUS_RELEASE_MASK;
+ keycode += 8;
Qt::KeyboardModifiers modifiers = Qt::NoModifier;
if (state & IBUS_SHIFT_MASK)
@@ -355,7 +354,13 @@ void QIBusPlatformInputContext::forwardKeyEvent(uint keyval, uint keycode, uint
if (state & IBUS_META_MASK)
modifiers |= Qt::MetaModifier;
- QKeyEvent event(type, keyval, modifiers, QString(keyval));
+ int qtcode = QXkbCommon::keysymToQtKey(keyval, modifiers);
+ QString text = QXkbCommon::lookupStringNoKeysymTransformations(keyval);
+
+ if (debug)
+ qDebug() << "forwardKeyEvent" << keyval << keycode << state << modifiers << qtcode << text;
+
+ QKeyEvent event(type, qtcode, modifiers, keycode, keyval, state, text);
QCoreApplication::sendEvent(input, &event);
}
@@ -422,9 +427,9 @@ bool QIBusPlatformInputContext::filterEvent(const QEvent *event)
QDBusPendingReply<bool> reply = d->context->ProcessKeyEvent(sym, code - 8, ibusState);
if (m_eventFilterUseSynchronousMode || reply.isFinished()) {
- bool retval = reply.value();
- qCDebug(qtQpaInputMethods) << "filterEvent return" << code << sym << state << retval;
- return retval;
+ bool filtered = reply.value();
+ qCDebug(qtQpaInputMethods) << "filterEvent return" << code << sym << state << filtered;
+ return filtered;
}
Qt::KeyboardModifiers modifiers = keyEvent->modifiers();
@@ -494,23 +499,22 @@ void QIBusPlatformInputContext::filterEventFinished(QDBusPendingCallWatcher *cal
const bool isAutoRepeat = args.at(7).toBool();
// copied from QXcbKeyboard::handleKeyEvent()
- bool retval = reply.value();
- qCDebug(qtQpaInputMethods) << "filterEventFinished return" << code << sym << state << retval;
- if (!retval) {
+ bool filtered = reply.value();
+ qCDebug(qtQpaInputMethods) << "filterEventFinished return" << code << sym << state << filtered;
+ if (!filtered) {
#ifndef QT_NO_CONTEXTMENU
if (type == QEvent::KeyPress && qtcode == Qt::Key_Menu
&& window != NULL) {
const QPoint globalPos = window->screen()->handle()->cursor()->pos();
const QPoint pos = window->mapFromGlobal(globalPos);
-#ifndef QT_NO_CONTEXTMENU
- QWindowSystemInterface::handleContextMenuEvent(window, false, pos,
- globalPos, modifiers);
-#endif
+ QWindowSystemInterfacePrivate::ContextMenuEvent contextMenuEvent(window, false, pos,
+ globalPos, modifiers);
+ QGuiApplicationPrivate::processWindowSystemEvent(&contextMenuEvent);
}
-#endif // QT_NO_CONTEXTMENU
- QWindowSystemInterface::handleExtendedKeyEvent(window, time, type, qtcode, modifiers,
- code, sym, state, string, isAutoRepeat);
-
+#endif
+ QWindowSystemInterfacePrivate::KeyEvent keyEvent(window, time, type, qtcode, modifiers,
+ code, sym, state, string, isAutoRepeat);
+ QGuiApplicationPrivate::processWindowSystemEvent(&keyEvent);
}
call->deleteLater();
}
diff --git a/src/plugins/platforminputcontexts/platforminputcontexts.pro b/src/plugins/platforminputcontexts/platforminputcontexts.pro
index ed6b1b8702..56a39a49e7 100644
--- a/src/plugins/platforminputcontexts/platforminputcontexts.pro
+++ b/src/plugins/platforminputcontexts/platforminputcontexts.pro
@@ -1,10 +1,11 @@
TEMPLATE = subdirs
QT_FOR_CONFIG += gui-private
-qtHaveModule(dbus) {
-!mac:!win32:SUBDIRS += ibus
-}
-
-qtConfig(xcb): SUBDIRS += compose
+qtConfig(xkbcommon) {
+ SUBDIRS += compose
+ qtHaveModule(dbus) {
+ !macos:!win32:SUBDIRS += ibus
+ }
+}
diff --git a/src/plugins/platforms/android/android.pro b/src/plugins/platforms/android/android.pro
index 73db9e93a3..78632a9bea 100644
--- a/src/plugins/platforms/android/android.pro
+++ b/src/plugins/platforms/android/android.pro
@@ -20,6 +20,7 @@ INCLUDEPATH += \
$$QT_SOURCE_TREE/src/3rdparty/android
SOURCES += $$PWD/androidplatformplugin.cpp \
+ $$PWD/androidcontentfileengine.cpp \
$$PWD/androiddeadlockprotector.cpp \
$$PWD/androidjnimain.cpp \
$$PWD/androidjniaccessibility.cpp \
@@ -46,9 +47,11 @@ SOURCES += $$PWD/androidplatformplugin.cpp \
$$PWD/qandroidplatformopenglcontext.cpp \
$$PWD/qandroidplatformforeignwindow.cpp \
$$PWD/qandroideventdispatcher.cpp \
- $$PWD/qandroidplatformoffscreensurface.cpp
+ $$PWD/qandroidplatformoffscreensurface.cpp \
+ $$PWD/qandroidplatformfiledialoghelper.cpp
HEADERS += $$PWD/qandroidplatformintegration.h \
+ $$PWD/androidcontentfileengine.h \
$$PWD/androiddeadlockprotector.h \
$$PWD/androidjnimain.h \
$$PWD/androidjniaccessibility.h \
@@ -75,7 +78,8 @@ HEADERS += $$PWD/qandroidplatformintegration.h \
$$PWD/qandroidplatformopenglcontext.h \
$$PWD/qandroidplatformforeignwindow.h \
$$PWD/qandroideventdispatcher.h \
- $$PWD/qandroidplatformoffscreensurface.h
+ $$PWD/qandroidplatformoffscreensurface.h \
+ $$PWD/qandroidplatformfiledialoghelper.h
qtConfig(android-style-assets): SOURCES += $$PWD/extract.cpp
else: SOURCES += $$PWD/extract-dummy.cpp
diff --git a/src/plugins/platforms/android/androidcontentfileengine.cpp b/src/plugins/platforms/android/androidcontentfileengine.cpp
new file mode 100644
index 0000000000..1444407195
--- /dev/null
+++ b/src/plugins/platforms/android/androidcontentfileengine.cpp
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Volker Krause <vkrause@kde.org>
+** 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 "androidcontentfileengine.h"
+
+#include <private/qjni_p.h>
+#include <private/qjnihelpers_p.h>
+
+#include <QDebug>
+
+AndroidContentFileEngine::AndroidContentFileEngine(const QString &fileName)
+ : QFSFileEngine(fileName)
+{
+}
+
+bool AndroidContentFileEngine::open(QIODevice::OpenMode openMode)
+{
+ QString openModeStr;
+ if (openMode & QFileDevice::ReadOnly) {
+ openModeStr += QLatin1Char('r');
+ }
+ if (openMode & QFileDevice::WriteOnly) {
+ openModeStr += QLatin1Char('w');
+ }
+ if (openMode & QFileDevice::Truncate) {
+ openModeStr += QLatin1Char('t');
+ } else if (openMode & QFileDevice::Append) {
+ openModeStr += QLatin1Char('a');
+ }
+
+ const auto fd = QJNIObjectPrivate::callStaticMethod<jint>("org/qtproject/qt5/android/QtNative",
+ "openFdForContentUrl",
+ "(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)I",
+ QtAndroidPrivate::context(),
+ QJNIObjectPrivate::fromString(fileName(DefaultName)).object(),
+ QJNIObjectPrivate::fromString(openModeStr).object());
+
+ if (fd < 0) {
+ return false;
+ }
+
+ return QFSFileEngine::open(openMode, fd, QFile::AutoCloseHandle);
+}
+
+
+AndroidContentFileEngineHandler::AndroidContentFileEngineHandler() = default;
+AndroidContentFileEngineHandler::~AndroidContentFileEngineHandler() = default;
+
+QAbstractFileEngine* AndroidContentFileEngineHandler::create(const QString &fileName) const
+{
+ if (!fileName.startsWith(QLatin1String("content"))) {
+ return nullptr;
+ }
+
+ return new AndroidContentFileEngine(fileName);
+}
diff --git a/src/plugins/platforms/offscreen/qoffscreenintegration_dummy.cpp b/src/plugins/platforms/android/androidcontentfileengine.h
index 78b289ea49..db3def03d6 100644
--- a/src/plugins/platforms/offscreen/qoffscreenintegration_dummy.cpp
+++ b/src/plugins/platforms/android/androidcontentfileengine.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2019 Volker Krause <vkrause@kde.org>
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
@@ -37,9 +37,24 @@
**
****************************************************************************/
-#include "qoffscreenintegration.h"
+#ifndef ANDROIDCONTENTFILEENGINE_H
+#define ANDROIDCONTENTFILEENGINE_H
-QOffscreenIntegration *QOffscreenIntegration::createOffscreenIntegration()
+#include <private/qfsfileengine_p.h>
+
+class AndroidContentFileEngine : public QFSFileEngine
+{
+public:
+ AndroidContentFileEngine(const QString &fileName);
+ bool open(QIODevice::OpenMode openMode) override;
+};
+
+class AndroidContentFileEngineHandler : public QAbstractFileEngineHandler
{
- return new QOffscreenIntegration;
-}
+public:
+ AndroidContentFileEngineHandler();
+ ~AndroidContentFileEngineHandler();
+ QAbstractFileEngine *create(const QString &fileName) const override;
+};
+
+#endif // ANDROIDCONTENTFILEENGINE_H
diff --git a/src/plugins/platforms/android/androidjniaccessibility.cpp b/src/plugins/platforms/android/androidjniaccessibility.cpp
index 309e41bfd6..d4b7f38bf6 100644
--- a/src/plugins/platforms/android/androidjniaccessibility.cpp
+++ b/src/plugins/platforms/android/androidjniaccessibility.cpp
@@ -329,10 +329,7 @@ if (!clazz) { \
GET_AND_CHECK_STATIC_METHOD(m_setFocusedMethodID, nodeInfoClass, "setFocused", "(Z)V");
GET_AND_CHECK_STATIC_METHOD(m_setScrollableMethodID, nodeInfoClass, "setScrollable", "(Z)V");
GET_AND_CHECK_STATIC_METHOD(m_setVisibleToUserMethodID, nodeInfoClass, "setVisibleToUser", "(Z)V");
-
- if (QtAndroidPrivate::androidSdkVersion() >= 18) {
- GET_AND_CHECK_STATIC_METHOD(m_setTextSelectionMethodID, nodeInfoClass, "setTextSelection", "(II)V");
- }
+ GET_AND_CHECK_STATIC_METHOD(m_setTextSelectionMethodID, nodeInfoClass, "setTextSelection", "(II)V");
return true;
}
diff --git a/src/plugins/platforms/android/androidjnimain.cpp b/src/plugins/platforms/android/androidjnimain.cpp
index 74edfd8356..6ae429b24e 100644
--- a/src/plugins/platforms/android/androidjnimain.cpp
+++ b/src/plugins/platforms/android/androidjnimain.cpp
@@ -49,6 +49,7 @@
#include "androidjniinput.h"
#include "androidjniclipboard.h"
#include "androidjnimenu.h"
+#include "androidcontentfileengine.h"
#include "androiddeadlockprotector.h"
#include "qandroidplatformdialoghelpers.h"
#include "qandroidplatformintegration.h"
@@ -116,6 +117,7 @@ static double m_scaledDensity = 0;
static double m_density = 1.0;
static AndroidAssetsFileEngineHandler *m_androidAssetsFileEngineHandler = nullptr;
+static AndroidContentFileEngineHandler *m_androidContentFileEngineHandler = nullptr;
@@ -445,6 +447,7 @@ static jboolean startQtAndroidPlugin(JNIEnv *env, jobject /*object*/, jstring pa
{
m_androidPlatformIntegration = nullptr;
m_androidAssetsFileEngineHandler = new AndroidAssetsFileEngineHandler();
+ m_androidContentFileEngineHandler = new AndroidContentFileEngineHandler();
m_mainLibraryHnd = nullptr;
{ // Set env. vars
const char *nativeString = env->GetStringUTFChars(environmentString, 0);
@@ -555,6 +558,8 @@ static void quitQtAndroidPlugin(JNIEnv *env, jclass /*clazz*/)
m_androidPlatformIntegration = nullptr;
delete m_androidAssetsFileEngineHandler;
m_androidAssetsFileEngineHandler = nullptr;
+ delete m_androidContentFileEngineHandler;
+ m_androidContentFileEngineHandler = nullptr;
}
static void terminateQt(JNIEnv *env, jclass /*clazz*/)
diff --git a/src/plugins/platforms/android/extract-dummy.cpp b/src/plugins/platforms/android/extract-dummy.cpp
index d07fbe1ba7..fdce8ec64c 100644
--- a/src/plugins/platforms/android/extract-dummy.cpp
+++ b/src/plugins/platforms/android/extract-dummy.cpp
@@ -40,16 +40,6 @@
#include <jni.h>
#include <extract.h>
-extern "C" JNIEXPORT jintArray JNICALL Java_org_qtproject_qt5_android_ExtractStyle_extractNativeChunkInfo(JNIEnv *, jobject, Res_png_9patch*)
-{
- return 0;
-}
-
-extern "C" JNIEXPORT jintArray JNICALL Java_org_qtproject_qt5_android_ExtractStyle_extractChunkInfo(JNIEnv *, jobject, jbyteArray)
-{
- return 0;
-}
-
extern "C" JNIEXPORT jintArray JNICALL Java_org_qtproject_qt5_android_ExtractStyle_extractNativeChunkInfo20(JNIEnv *, jobject, long)
{
return 0;
diff --git a/src/plugins/platforms/android/extract.cpp b/src/plugins/platforms/android/extract.cpp
index 2f2ffa7126..acffa353f1 100644
--- a/src/plugins/platforms/android/extract.cpp
+++ b/src/plugins/platforms/android/extract.cpp
@@ -48,46 +48,6 @@
#define LOG_TAG "extractSyleInfo"
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
-extern "C" JNIEXPORT jintArray JNICALL Java_org_qtproject_qt5_android_ExtractStyle_extractNativeChunkInfo(JNIEnv * env, jobject, Res_png_9patch* chunk)
-{
- Res_png_9patch::deserialize(chunk);
- //printChunkInformation(chunk);
- jintArray result;
- size_t size = 3+chunk->numXDivs+chunk->numYDivs+chunk->numColors;
- result = env->NewIntArray(size);
- if (!result)
- return 0;
-
- jint *data = (jint*)malloc(sizeof(jint)*size);
- size_t pos = 0;
- data[pos++]=chunk->numXDivs;
- data[pos++]=chunk->numYDivs;
- data[pos++]=chunk->numColors;
- for (int x = 0; x <chunk->numXDivs; x ++)
- data[pos++]=chunk->xDivs[x];
- for (int y = 0; y <chunk->numYDivs; y ++)
- data[pos++]=chunk->yDivs[y];
- for (int c = 0; c <chunk->numColors; c ++)
- data[pos++]=chunk->colors[c];
- env->SetIntArrayRegion(result, 0, size, data);
- free(data);
- return result;
-}
-
-extern "C" JNIEXPORT jintArray JNICALL Java_org_qtproject_qt5_android_ExtractStyle_extractChunkInfo(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 (!env->ExceptionCheck())
- return Java_org_qtproject_qt5_android_ExtractStyle_extractNativeChunkInfo(env, obj, static_cast<Res_png_9patch*>(storage));
- else
- env->ExceptionClear();
- return 0;
-}
-
// The following part was shamelessly stolen from ResourceTypes.cpp from Android's sources
/*
* Copyright (C) 2005 The Android Open Source Project
diff --git a/src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp b/src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp
new file mode 100644
index 0000000000..4fb271a75c
--- /dev/null
+++ b/src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp
@@ -0,0 +1,149 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB)
+** 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 "qandroidplatformfiledialoghelper.h"
+
+#include <androidjnimain.h>
+#include <private/qjni_p.h>
+#include <jni.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QtAndroidFileDialogHelper {
+
+#define RESULT_OK -1
+#define REQUEST_CODE 1305 // Arbitrary
+
+QAndroidPlatformFileDialogHelper::QAndroidPlatformFileDialogHelper()
+ : QPlatformFileDialogHelper()
+ , m_selectedFile()
+{
+}
+
+bool QAndroidPlatformFileDialogHelper::handleActivityResult(jint requestCode, jint resultCode, jobject data)
+{
+ if (requestCode != REQUEST_CODE)
+ return false;
+
+ if (resultCode == RESULT_OK) {
+ const QJNIObjectPrivate intent = QJNIObjectPrivate::fromLocalRef(data);
+ const QJNIObjectPrivate uri = intent.callObjectMethod("getData", "()Landroid/net/Uri;");
+ const QString uriStr = uri.callObjectMethod("toString", "()Ljava/lang/String;").toString();
+ m_selectedFile = QUrl(uriStr);
+ Q_EMIT fileSelected(m_selectedFile);
+ Q_EMIT accept();
+ } else {
+ Q_EMIT reject();
+ }
+
+ return true;
+}
+
+bool QAndroidPlatformFileDialogHelper::show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent)
+{
+ Q_UNUSED(windowFlags)
+ Q_UNUSED(windowModality)
+ Q_UNUSED(parent)
+
+ if (options()->fileMode() != QFileDialogOptions::FileMode::ExistingFile)
+ return false;
+
+ QtAndroidPrivate::registerActivityResultListener(this);
+
+ const QJNIObjectPrivate ACTION_OPEN_DOCUMENT = QJNIObjectPrivate::getStaticObjectField("android/content/Intent", "ACTION_OPEN_DOCUMENT", "Ljava/lang/String;");
+ QJNIObjectPrivate intent("android/content/Intent", "(Ljava/lang/String;)V", ACTION_OPEN_DOCUMENT.object());
+ const QJNIObjectPrivate CATEGORY_OPENABLE = QJNIObjectPrivate::getStaticObjectField("android/content/Intent", "CATEGORY_OPENABLE", "Ljava/lang/String;");
+ intent.callObjectMethod("addCategory", "(Ljava/lang/String;)Landroid/content/Intent;", CATEGORY_OPENABLE.object());
+ intent.callObjectMethod("setType", "(Ljava/lang/String;)Landroid/content/Intent;", QJNIObjectPrivate::fromString(QStringLiteral("*/*")).object());
+
+ const QJNIObjectPrivate activity(QtAndroid::activity());
+ activity.callMethod<void>("startActivityForResult", "(Landroid/content/Intent;I)V", intent.object(), REQUEST_CODE);
+
+ return true;
+}
+
+void QAndroidPlatformFileDialogHelper::exec()
+{
+}
+
+void QAndroidPlatformFileDialogHelper::hide()
+{
+}
+
+QString QAndroidPlatformFileDialogHelper::selectedNameFilter() const
+{
+ return QString();
+}
+
+void QAndroidPlatformFileDialogHelper::selectNameFilter(const QString &filter)
+{
+ Q_UNUSED(filter)
+}
+
+void QAndroidPlatformFileDialogHelper::setFilter()
+{
+}
+
+QList<QUrl> QAndroidPlatformFileDialogHelper::selectedFiles() const
+{
+ return {m_selectedFile};
+}
+
+void QAndroidPlatformFileDialogHelper::selectFile(const QUrl &file)
+{
+ Q_UNUSED(file)
+}
+
+QUrl QAndroidPlatformFileDialogHelper::directory() const
+{
+ return QUrl();
+}
+
+void QAndroidPlatformFileDialogHelper::setDirectory(const QUrl &directory)
+{
+ Q_UNUSED(directory)
+}
+
+bool QAndroidPlatformFileDialogHelper::defaultNameFilterDisables() const
+{
+ return false;
+}
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/android/qandroidplatformfiledialoghelper.h b/src/plugins/platforms/android/qandroidplatformfiledialoghelper.h
new file mode 100644
index 0000000000..e445aa2fef
--- /dev/null
+++ b/src/plugins/platforms/android/qandroidplatformfiledialoghelper.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB)
+** 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 QANDROIDPLATFORMFILEDIALOGHELPER_H
+#define QANDROIDPLATFORMFILEDIALOGHELPER_H
+
+#include <jni.h>
+#include <qpa/qplatformdialoghelper.h>
+#include <QtCore/private/qjnihelpers_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QtAndroidFileDialogHelper {
+
+class QAndroidPlatformFileDialogHelper: public QPlatformFileDialogHelper, public QtAndroidPrivate::ActivityResultListener
+{
+ Q_OBJECT
+
+public:
+ QAndroidPlatformFileDialogHelper();
+ void exec() override;
+
+ bool show(Qt::WindowFlags windowFlags,
+ Qt::WindowModality windowModality,
+ QWindow *parent) override;
+ void hide() override;
+
+ QString selectedNameFilter() const override;
+ void selectNameFilter(const QString &filter) override;
+ void setFilter() override;
+ QList<QUrl> selectedFiles() const override;
+ void selectFile(const QUrl &file) override;
+ QUrl directory() const override;
+ void setDirectory(const QUrl &directory) override;
+ bool defaultNameFilterDisables() const override;
+ bool handleActivityResult(jint requestCode, jint resultCode, jobject data) override;
+
+private:
+ QUrl m_selectedFile;
+};
+
+}
+QT_END_NAMESPACE
+
+#endif // QANDROIDPLATFORMFILEDIALOGHELPER_H
diff --git a/src/plugins/platforms/android/qandroidplatformtheme.cpp b/src/plugins/platforms/android/qandroidplatformtheme.cpp
index 94405738f3..7fc68c3d7c 100644
--- a/src/plugins/platforms/android/qandroidplatformtheme.cpp
+++ b/src/plugins/platforms/android/qandroidplatformtheme.cpp
@@ -44,6 +44,7 @@
#include "qandroidplatformmenu.h"
#include "qandroidplatformmenuitem.h"
#include "qandroidplatformdialoghelpers.h"
+#include "qandroidplatformfiledialoghelper.h"
#include <QCoreApplication>
#include <QDebug>
@@ -514,6 +515,8 @@ bool QAndroidPlatformTheme::usePlatformNativeDialog(QPlatformTheme::DialogType t
{
if (type == MessageDialog)
return qEnvironmentVariableIntValue("QT_USE_ANDROID_NATIVE_DIALOGS") == 1;
+ if (type == FileDialog)
+ return true;
return false;
}
@@ -522,6 +525,8 @@ QPlatformDialogHelper *QAndroidPlatformTheme::createPlatformDialogHelper(QPlatfo
switch (type) {
case MessageDialog:
return new QtAndroidDialogHelpers::QAndroidPlatformMessageDialogHelper;
+ case FileDialog:
+ return new QtAndroidFileDialogHelper::QAndroidPlatformFileDialogHelper;
default:
return 0;
}
diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm
index f26263261c..f0ef70e3a3 100644
--- a/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm
+++ b/src/plugins/platforms/cocoa/qcocoaaccessibilityelement.mm
@@ -51,6 +51,19 @@ QT_USE_NAMESPACE
#ifndef QT_NO_ACCESSIBILITY
+/**
+ * Converts between absolute character offsets and line numbers of a
+ * QAccessibleTextInterface. Works in exactly one of two modes:
+ *
+ * - Pass *line == -1 in order to get a line containing character at the given
+ * *offset
+ * - Pass *offset == -1 in order to get the offset of first character of the
+ * given *line
+ *
+ * You can optionally also pass non-NULL `start` and `end`, which will in both
+ * modes be filled with the offset of the first and last characters of the
+ * relevant line.
+ */
static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *offset, NSUInteger *start = 0, NSUInteger *end = 0)
{
Q_ASSERT(*line == -1 || *offset == -1);
@@ -224,7 +237,7 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of
return [attributes autorelease];
}
-- (id)parentElement {
+- (id)accessibilityParent {
QAccessibleInterface *iface = QAccessible::accessibleInterface(axid);
if (!iface || !iface->isValid())
return nil;
@@ -237,7 +250,7 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of
if (QAccessibleInterface *parent = iface->parent()) {
if (parent->role() != QAccessible::Application) {
QAccessible::Id parentId = QAccessible::uniqueId(parent);
- return [QMacAccessibilityElement elementWithId: parentId];
+ return NSAccessibilityUnignoredAncestor([QMacAccessibilityElement elementWithId: parentId]);
}
}
@@ -245,12 +258,18 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of
QPlatformWindow *platformWindow = window->handle();
if (platformWindow) {
QCocoaWindow *win = static_cast<QCocoaWindow*>(platformWindow);
- return qnsview_cast(win->view());
+ return NSAccessibilityUnignoredAncestor(qnsview_cast(win->view()));
}
}
return nil;
}
+- (NSRect)accessibilityFrame {
+ QAccessibleInterface *iface = QAccessible::accessibleInterface(axid);
+ if (!iface || !iface->isValid())
+ return NSZeroRect;
+ return QCocoaScreen::mapToNative(iface->rect());
+}
- (id) minValueAttribute:(QAccessibleInterface*)iface {
if (QAccessibleValueInterface *val = iface->valueInterface())
@@ -285,13 +304,13 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of
id focusedElement = [NSApp accessibilityAttributeValue:NSAccessibilityFocusedUIElementAttribute];
return @([focusedElement isEqual:self]);
} else if ([attribute isEqualToString:NSAccessibilityParentAttribute]) {
- return NSAccessibilityUnignoredAncestor([self parentElement]);
+ return self.accessibilityParent;
} else if ([attribute isEqualToString:NSAccessibilityWindowAttribute]) {
// We're in the same window as our parent.
- return [[self parentElement] accessibilityAttributeValue:NSAccessibilityWindowAttribute];
+ return [[self accessibilityParent] accessibilityAttributeValue:NSAccessibilityWindowAttribute];
} else if ([attribute isEqualToString:NSAccessibilityTopLevelUIElementAttribute]) {
// We're in the same top level element as our parent.
- return [[self parentElement] accessibilityAttributeValue:NSAccessibilityTopLevelUIElementAttribute];
+ return [[self accessibilityParent] accessibilityAttributeValue:NSAccessibilityTopLevelUIElementAttribute];
} else if ([attribute isEqualToString:NSAccessibilityPositionAttribute]) {
// The position in points of the element's lower-left corner in screen-relative coordinates
QPointF qtPosition = QRectF(iface->rect()).bottomLeft();
@@ -347,12 +366,8 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of
return [NSValue valueWithRange: NSMakeRange(0, iface->text(QAccessible::Name).length())];
} else if ([attribute isEqualToString:NSAccessibilityInsertionPointLineNumberAttribute]) {
if (QAccessibleTextInterface *text = iface->textInterface()) {
- int line = 0; // true for all single line edits
- if (iface->state().multiLine) {
- int position = text->cursorPosition();
- convertLineOffset(text, &line, &position);
- }
- return @(line);
+ int position = text->cursorPosition();
+ return [self accessibilityAttributeValue:NSAccessibilityLineForIndexParameterizedAttribute forParameter:@(position)];
}
return nil;
} else if ([attribute isEqualToString:NSAccessibilityMinValueAttribute]) {
@@ -408,8 +423,11 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of
int index = [parameter intValue];
if (index < 0 || index > iface->textInterface()->characterCount())
return nil;
- int line = -1;
- convertLineOffset(iface->textInterface(), &line, &index);
+ int line = 0; // true for all single line edits
+ if (iface->state().multiLine) {
+ line = -1;
+ convertLineOffset(iface->textInterface(), &line, &index);
+ }
return @(line);
}
if ([attribute isEqualToString: NSAccessibilityRangeForLineParameterizedAttribute]) {
diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm
index 78aa98094c..01b4894324 100644
--- a/src/plugins/platforms/cocoa/qcocoabackingstore.mm
+++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm
@@ -292,18 +292,6 @@ void QNSWindowBackingStore::redrawRoundedBottomCorners(CGRect windowRect) const
// ----------------------------------------------------------------------------
-// https://stackoverflow.com/a/52722575/2761869
-template<class R>
-struct backwards_t {
- R r;
- constexpr auto begin() const { using std::rbegin; return rbegin(r); }
- constexpr auto begin() { using std::rbegin; return rbegin(r); }
- constexpr auto end() const { using std::rend; return rend(r); }
- constexpr auto end() { using std::rend; return rend(r); }
-};
-template<class R>
-constexpr backwards_t<R> backwards(R&& r) { return {std::forward<R>(r)}; }
-
QCALayerBackingStore::QCALayerBackingStore(QWindow *window)
: QPlatformBackingStore(window)
{
diff --git a/src/plugins/platforms/cocoa/qcocoaglcontext.mm b/src/plugins/platforms/cocoa/qcocoaglcontext.mm
index d0100d0410..0f8fec0548 100644
--- a/src/plugins/platforms/cocoa/qcocoaglcontext.mm
+++ b/src/plugins/platforms/cocoa/qcocoaglcontext.mm
@@ -86,7 +86,7 @@ QCocoaGLContext::QCocoaGLContext(QOpenGLContext *context)
}
m_context = nativeHandle.value<QCocoaNativeContext>().context();
if (!m_context) {
- qCWarning(lcQpaOpenGLContext, "QCocoaNativeContext's NSOpenGLContext can not be null");
+ qCWarning(lcQpaOpenGLContext, "QCocoaNativeContext's NSOpenGLContext cannot be null");
return;
}
diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.h b/src/plugins/platforms/cocoa/qcocoahelpers.h
index 69aa7937b6..69a1854598 100644
--- a/src/plugins/platforms/cocoa/qcocoahelpers.h
+++ b/src/plugins/platforms/cocoa/qcocoahelpers.h
@@ -176,6 +176,18 @@ T qt_mac_resolveOption(const T &fallback, QWindow *window, const QByteArray &pro
return fallback;
}
+// https://stackoverflow.com/a/52722575/2761869
+template<class R>
+struct backwards_t {
+ R r;
+ constexpr auto begin() const { using std::rbegin; return rbegin(r); }
+ constexpr auto begin() { using std::rbegin; return rbegin(r); }
+ constexpr auto end() const { using std::rend; return rend(r); }
+ constexpr auto end() { using std::rend; return rend(r); }
+};
+template<class R>
+constexpr backwards_t<R> backwards(R&& r) { return {std::forward<R>(r)}; }
+
// -------------------------------------------------------------------------
#if !defined(Q_PROCESSOR_X86_64)
diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm
index d36a7f6d09..c9eafa81d0 100644
--- a/src/plugins/platforms/cocoa/qcocoahelpers.mm
+++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm
@@ -63,7 +63,7 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcQpaWindow, "qt.qpa.window");
Q_LOGGING_CATEGORY(lcQpaDrawing, "qt.qpa.drawing");
Q_LOGGING_CATEGORY(lcQpaMouse, "qt.qpa.input.mouse", QtCriticalMsg);
-Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen");
+Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen", QtCriticalMsg);
//
// Conversion Functions
diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.h b/src/plugins/platforms/cocoa/qcocoaintegration.h
index ecbd19c9a2..bfc3bfe9de 100644
--- a/src/plugins/platforms/cocoa/qcocoaintegration.h
+++ b/src/plugins/platforms/cocoa/qcocoaintegration.h
@@ -61,8 +61,6 @@
QT_BEGIN_NAMESPACE
-class QCocoaScreen;
-
class QCocoaIntegration : public QObject, public QPlatformIntegration
{
Q_OBJECT
@@ -113,9 +111,6 @@ public:
Qt::KeyboardModifiers queryKeyboardModifiers() const override;
QList<int> possibleKeys(const QKeyEvent *event) const override;
- void updateScreens();
- QCocoaScreen *screenForNSScreen(NSScreen *nsScreen);
-
void setToolbar(QWindow *window, NSToolbar *toolbar);
NSToolbar *toolbar(QWindow *window) const;
void clearToolbars();
@@ -143,8 +138,6 @@ private:
QScopedPointer<QCocoaAccessibility> mAccessibility;
#endif
QScopedPointer<QPlatformTheme> mPlatformTheme;
- QList<QCocoaScreen *> mScreens;
- QMacNotificationObserver m_screensObserver;
#ifndef QT_NO_CLIPBOARD
QCocoaClipboard *mCocoaClipboard;
#endif
diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm
index b5d63f8331..08e7447a75 100644
--- a/src/plugins/platforms/cocoa/qcocoaintegration.mm
+++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm
@@ -207,9 +207,7 @@ QCocoaIntegration::QCocoaIntegration(const QStringList &paramList)
// which will resolve to an actual value and result in screen invalidation.
cocoaApplication.presentationOptions = NSApplicationPresentationDefault;
- m_screensObserver = QMacNotificationObserver([NSApplication sharedApplication],
- NSApplicationDidChangeScreenParametersNotification, [&]() { updateScreens(); });
- updateScreens();
+ QCocoaScreen::initializeScreens();
QMacInternalPasteboardMime::initializeMimeTypes();
QCocoaMimeTypes::initializeMimeTypes();
@@ -261,10 +259,7 @@ QCocoaIntegration::~QCocoaIntegration()
QMacInternalPasteboardMime::destroyMimeTypes();
#endif
- // Delete screens in reverse order to avoid crash in case of multiple screens
- while (!mScreens.isEmpty()) {
- QWindowSystemInterface::handleScreenRemoved(mScreens.takeLast());
- }
+ QCocoaScreen::cleanupScreens();
clearToolbars();
}
@@ -279,88 +274,6 @@ QCocoaIntegration::Options QCocoaIntegration::options() const
return mOptions;
}
-/*!
- \brief Synchronizes the screen list, adds new screens, removes deleted ones
-*/
-void QCocoaIntegration::updateScreens()
-{
- NSArray<NSScreen *> *scrs = [NSScreen screens];
- NSMutableArray<NSScreen *> *screens = [NSMutableArray<NSScreen *> arrayWithArray:scrs];
- if ([screens count] == 0)
- if ([NSScreen mainScreen])
- [screens addObject:[NSScreen mainScreen]];
- if ([screens count] == 0)
- return;
- QSet<QCocoaScreen*> remainingScreens = QSet<QCocoaScreen*>::fromList(mScreens);
- QList<QPlatformScreen *> siblings;
- uint screenCount = [screens count];
- for (uint i = 0; i < screenCount; i++) {
- NSScreen* scr = [screens objectAtIndex:i];
- CGDirectDisplayID dpy = scr.qt_displayId;
- // If this screen is a mirror and is not the primary one of the mirror set, ignore it.
- // Exception: The NSScreen API has been observed to a return a screen list with one
- // mirrored, non-primary screen when Qt is running as a startup item. Always use the
- // screen if there's only one screen in the list.
- if (screenCount > 1 && CGDisplayIsInMirrorSet(dpy)) {
- CGDirectDisplayID primary = CGDisplayMirrorsDisplay(dpy);
- if (primary != kCGNullDirectDisplay && primary != dpy)
- continue;
- }
- QCocoaScreen* screen = nullptr;
- foreach (QCocoaScreen* existingScr, mScreens) {
- // NSScreen documentation says do not cache the array returned from [NSScreen screens].
- // However in practice, we can identify a screen by its pointer: if resolution changes,
- // the NSScreen object will be the same instance, just with different values.
- if (existingScr->nativeScreen() == scr) {
- screen = existingScr;
- break;
- }
- }
- if (screen) {
- remainingScreens.remove(screen);
- screen->updateProperties();
- } else {
- screen = new QCocoaScreen(i);
- mScreens.append(screen);
- qCDebug(lcQpaScreen) << "Adding" << screen;
- QWindowSystemInterface::handleScreenAdded(screen);
- }
- siblings << screen;
- }
-
- // Set virtual siblings list. All screens in mScreens are siblings, because we ignored the
- // mirrors. Note that some of the screens we update the siblings list for here may be deleted
- // below, but update anyway to keep the to-be-deleted screens out of the siblings list.
- foreach (QCocoaScreen* screen, mScreens)
- screen->setVirtualSiblings(siblings);
-
- // Now the leftovers in remainingScreens are no longer current, so we can delete them.
- foreach (QCocoaScreen* screen, remainingScreens) {
- mScreens.removeOne(screen);
- // Prevent stale references to NSScreen during destroy
- screen->m_screenIndex = -1;
- qCDebug(lcQpaScreen) << "Removing" << screen;
- QWindowSystemInterface::handleScreenRemoved(screen);
- }
-}
-
-QCocoaScreen *QCocoaIntegration::screenForNSScreen(NSScreen *nsScreen)
-{
- NSUInteger index = [[NSScreen screens] indexOfObject:nsScreen];
- if (index == NSNotFound)
- return nullptr;
-
- if (index >= unsigned(mScreens.count()))
- updateScreens();
-
- for (QCocoaScreen *screen : mScreens) {
- if (screen->nativeScreen() == nsScreen)
- return screen;
- }
-
- return nullptr;
-}
-
bool QCocoaIntegration::hasCapability(QPlatformIntegration::Capability cap) const
{
switch (cap) {
diff --git a/src/plugins/platforms/cocoa/qcocoascreen.h b/src/plugins/platforms/cocoa/qcocoascreen.h
index 9ded98df32..491af2fe9c 100644
--- a/src/plugins/platforms/cocoa/qcocoascreen.h
+++ b/src/plugins/platforms/cocoa/qcocoascreen.h
@@ -48,10 +48,14 @@
QT_BEGIN_NAMESPACE
+class QCocoaIntegration;
+
class QCocoaScreen : public QPlatformScreen
{
public:
- QCocoaScreen(int screenIndex);
+ static void initializeScreens();
+ static void cleanupScreens();
+
~QCocoaScreen();
// ----------------------------------------------------
@@ -61,19 +65,18 @@ public:
QRect availableGeometry() const override { return m_availableGeometry; }
int depth() const override { return m_depth; }
QImage::Format format() const override { return m_format; }
- qreal devicePixelRatio() const override;
+ qreal devicePixelRatio() const override { return m_devicePixelRatio; }
QSizeF physicalSize() const override { return m_physicalSize; }
QDpi logicalDpi() const override { return m_logicalDpi; }
qreal refreshRate() const override { return m_refreshRate; }
QString name() const override { return m_name; }
QPlatformCursor *cursor() const override { return m_cursor; }
QWindow *topLevelAt(const QPoint &point) const override;
- QList<QPlatformScreen *> virtualSiblings() const override { return m_siblings; }
+ QList<QPlatformScreen *> virtualSiblings() const override;
QPlatformScreen::SubpixelAntialiasingType subpixelAntialiasingTypeHint() const override;
// ----------------------------------------------------
- // Additional methods
- void setVirtualSiblings(const QList<QPlatformScreen *> &siblings) { m_siblings = siblings; }
+
NSScreen *nativeScreen() const;
void updateProperties();
@@ -82,14 +85,21 @@ public:
bool isRunningDisplayLink() const;
static QCocoaScreen *primaryScreen();
+ static QCocoaScreen *get(NSScreen *nsScreen);
+ static QCocoaScreen *get(CGDirectDisplayID displayId);
static CGPoint mapToNative(const QPointF &pos, QCocoaScreen *screen = QCocoaScreen::primaryScreen());
static CGRect mapToNative(const QRectF &rect, QCocoaScreen *screen = QCocoaScreen::primaryScreen());
static QPointF mapFromNative(CGPoint pos, QCocoaScreen *screen = QCocoaScreen::primaryScreen());
static QRectF mapFromNative(CGRect rect, QCocoaScreen *screen = QCocoaScreen::primaryScreen());
-public:
- int m_screenIndex;
+private:
+ QCocoaScreen(CGDirectDisplayID displayId);
+ static void add(CGDirectDisplayID displayId);
+ void remove();
+
+ CGDirectDisplayID m_displayId = 0;
+
QRect m_geometry;
QRect m_availableGeometry;
QDpi m_logicalDpi;
@@ -99,11 +109,13 @@ public:
QImage::Format m_format;
QSizeF m_physicalSize;
QCocoaCursor *m_cursor;
- QList<QPlatformScreen *> m_siblings;
+ qreal m_devicePixelRatio;
CVDisplayLinkRef m_displayLink = nullptr;
dispatch_source_t m_displayLinkSource = nullptr;
QAtomicInt m_pendingUpdates;
+
+ friend QDebug operator<<(QDebug debug, const QCocoaScreen *screen);
};
#ifndef QT_NO_DEBUG_STREAM
@@ -116,5 +128,4 @@ QT_END_NAMESPACE
@property(readonly) CGDirectDisplayID qt_displayId;
@end
-#endif
-
+#endif // QCOCOASCREEN_H
diff --git a/src/plugins/platforms/cocoa/qcocoascreen.mm b/src/plugins/platforms/cocoa/qcocoascreen.mm
index 6a5b0e6e3e..392099d083 100644
--- a/src/plugins/platforms/cocoa/qcocoascreen.mm
+++ b/src/plugins/platforms/cocoa/qcocoascreen.mm
@@ -41,6 +41,7 @@
#include "qcocoawindow.h"
#include "qcocoahelpers.h"
+#include "qcocoaintegration.h"
#include <QtCore/qcoreapplication.h>
#include <QtGui/private/qcoregraphics_p.h>
@@ -53,34 +54,104 @@
QT_BEGIN_NAMESPACE
-class QCoreTextFontEngine;
-class QFontEngineFT;
+void QCocoaScreen::initializeScreens()
+{
+ uint32_t displayCount = 0;
+ if (CGGetActiveDisplayList(0, nullptr, &displayCount) != kCGErrorSuccess)
+ qFatal("Failed to get number of active displays");
+
+ CGDirectDisplayID activeDisplays[displayCount];
+ if (CGGetActiveDisplayList(displayCount, &activeDisplays[0], &displayCount) != kCGErrorSuccess)
+ qFatal("Failed to get active displays");
+
+ for (CGDirectDisplayID displayId : activeDisplays)
+ QCocoaScreen::add(displayId);
+
+ CGDisplayRegisterReconfigurationCallback([](CGDirectDisplayID displayId, CGDisplayChangeSummaryFlags flags, void *userInfo) {
+ if (flags & kCGDisplayBeginConfigurationFlag)
+ return; // Wait for changes to apply
+
+ Q_UNUSED(userInfo);
+
+ QCocoaScreen *cocoaScreen = QCocoaScreen::get(displayId);
+
+ if ((flags & kCGDisplayAddFlag) || !cocoaScreen) {
+ if (!CGDisplayIsActive(displayId)) {
+ qCDebug(lcQpaScreen) << "Not adding inactive display" << displayId;
+ return; // Will be added when activated
+ }
+ QCocoaScreen::add(displayId);
+ } else if ((flags & kCGDisplayRemoveFlag) || !CGDisplayIsActive(displayId)) {
+ cocoaScreen->remove();
+ } else {
+ // Detect changes to the primary screen immediately, instead of
+ // waiting for a display reconfigure with kCGDisplaySetMainFlag.
+ // This ensures that any property updates to the other screens
+ // will be in reference to the correct primary screen.
+ QCocoaScreen *mainDisplay = QCocoaScreen::get(CGMainDisplayID());
+ if (QGuiApplication::primaryScreen()->handle() != mainDisplay) {
+ mainDisplay->updateProperties();
+ qCInfo(lcQpaScreen) << "Primary screen changed to" << mainDisplay;
+ QWindowSystemInterface::handlePrimaryScreenChanged(mainDisplay);
+ }
-QCocoaScreen::QCocoaScreen(int screenIndex)
- : QPlatformScreen(), m_screenIndex(screenIndex), m_refreshRate(60.0)
+ if (cocoaScreen == mainDisplay)
+ return; // Already reconfigured
+
+ cocoaScreen->updateProperties();
+ qCInfo(lcQpaScreen) << "Reconfigured" << cocoaScreen;
+ }
+ }, nullptr);
+}
+
+void QCocoaScreen::add(CGDirectDisplayID displayId)
+{
+ QCocoaScreen *cocoaScreen = new QCocoaScreen(displayId);
+ qCInfo(lcQpaScreen) << "Adding" << cocoaScreen;
+ QWindowSystemInterface::handleScreenAdded(cocoaScreen, CGDisplayIsMain(displayId));
+}
+
+QCocoaScreen::QCocoaScreen(CGDirectDisplayID displayId)
+ : QPlatformScreen(), m_displayId(displayId)
{
updateProperties();
m_cursor = new QCocoaCursor;
}
-QCocoaScreen::~QCocoaScreen()
+void QCocoaScreen::cleanupScreens()
{
- delete m_cursor;
+ // Remove screens in reverse order to avoid crash in case of multiple screens
+ for (QScreen *screen : backwards(QGuiApplication::screens()))
+ static_cast<QCocoaScreen*>(screen->handle())->remove();
+}
- CVDisplayLinkRelease(m_displayLink);
- if (m_displayLinkSource)
- dispatch_release(m_displayLinkSource);
+void QCocoaScreen::remove()
+{
+ m_displayId = 0; // Prevent stale references during removal
+
+ // This may result in the application responding to QGuiApplication::screenRemoved
+ // by moving the window to another screen, either by setGeometry, or by setScreen.
+ // If the window isn't moved by the application, Qt will as a fallback move it to
+ // the primary screen via setScreen. Due to the way setScreen works, this won't
+ // actually recreate the window on the new screen, it will just assign the new
+ // QScreen to the window. The associated NSWindow will have an NSScreen determined
+ // by AppKit. AppKit will then move the window to another screen by changing the
+ // geometry, and we will get a callback in QCocoaWindow::windowDidMove and then
+ // QCocoaWindow::windowDidChangeScreen. At that point the window will appear to have
+ // already changed its screen, but that's only true if comparing the Qt screens,
+ // not when comparing the NSScreens.
+ QWindowSystemInterface::handleScreenRemoved(this);
}
-NSScreen *QCocoaScreen::nativeScreen() const
+QCocoaScreen::~QCocoaScreen()
{
- NSArray<NSScreen *> *screens = [NSScreen screens];
+ Q_ASSERT_X(!screen(), "QCocoaScreen", "QScreen should be deleted first");
- // Stale reference, screen configuration has changed
- if (m_screenIndex < 0 || (NSUInteger)m_screenIndex >= [screens count])
- return nil;
+ delete m_cursor;
- return [screens objectAtIndex:m_screenIndex];
+ CVDisplayLinkRelease(m_displayLink);
+ if (m_displayLinkSource)
+ dispatch_release(m_displayLinkSource);
}
static QString displayName(CGDirectDisplayID displayID)
@@ -117,35 +188,37 @@ static QString displayName(CGDirectDisplayID displayID)
void QCocoaScreen::updateProperties()
{
- NSScreen *nsScreen = nativeScreen();
- if (!nsScreen)
- return;
+ Q_ASSERT(m_displayId);
const QRect previousGeometry = m_geometry;
const QRect previousAvailableGeometry = m_availableGeometry;
const QDpi previousLogicalDpi = m_logicalDpi;
const qreal previousRefreshRate = m_refreshRate;
+ // Some properties are only available via NSScreen
+ NSScreen *nsScreen = nativeScreen();
+ Q_ASSERT(nsScreen);
+
// The reference screen for the geometry is always the primary screen
- QRectF primaryScreenGeometry = QRectF::fromCGRect([[NSScreen screens] firstObject].frame);
+ QRectF primaryScreenGeometry = QRectF::fromCGRect(CGDisplayBounds(CGMainDisplayID()));
m_geometry = qt_mac_flip(QRectF::fromCGRect(nsScreen.frame), primaryScreenGeometry).toRect();
m_availableGeometry = qt_mac_flip(QRectF::fromCGRect(nsScreen.visibleFrame), primaryScreenGeometry).toRect();
+ m_devicePixelRatio = nsScreen.backingScaleFactor;
+
m_format = QImage::Format_RGB32;
- m_depth = NSBitsPerPixelFromDepth([nsScreen depth]);
+ m_depth = NSBitsPerPixelFromDepth(nsScreen.depth);
- CGDirectDisplayID dpy = nsScreen.qt_displayId;
- CGSize size = CGDisplayScreenSize(dpy);
+ CGSize size = CGDisplayScreenSize(m_displayId);
m_physicalSize = QSizeF(size.width, size.height);
m_logicalDpi.first = 72;
m_logicalDpi.second = 72;
- CGDisplayModeRef displayMode = CGDisplayCopyDisplayMode(dpy);
+
+ QCFType<CGDisplayModeRef> displayMode = CGDisplayCopyDisplayMode(m_displayId);
float refresh = CGDisplayModeGetRefreshRate(displayMode);
- CGDisplayModeRelease(displayMode);
- if (refresh > 0)
- m_refreshRate = refresh;
+ m_refreshRate = refresh > 0 ? refresh : 60.0;
- m_name = displayName(dpy);
+ m_name = displayName(m_displayId);
const bool didChangeGeometry = m_geometry != previousGeometry || m_availableGeometry != previousAvailableGeometry;
@@ -155,24 +228,6 @@ void QCocoaScreen::updateProperties()
QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen(), m_logicalDpi.first, m_logicalDpi.second);
if (m_refreshRate != previousRefreshRate)
QWindowSystemInterface::handleScreenRefreshRateChange(screen(), m_refreshRate);
-
- qCDebug(lcQpaScreen) << "Updated properties for" << this;
-
- if (didChangeGeometry) {
- // When a screen changes its geometry, AppKit will send us a NSWindowDidMoveNotification
- // for each window, resulting in calls to handleGeometryChange(), but this happens before
- // the NSApplicationDidChangeScreenParametersNotification, so when we map the new geometry
- // (which is correct at that point) to the screen using QCocoaScreen::mapFromNative(), we
- // end up using the stale screen geometry, and the new window geometry we report is wrong.
- // To make sure we finally report the correct window geometry, we need to do another pass
- // of geometry reporting, now that the screen properties have been updates. FIXME: Ideally
- // this would be solved by not caching the screen properties in QCocoaScreen, but that
- // requires more research.
- for (QWindow *window : windows()) {
- if (QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow*>(window->handle()))
- cocoaWindow->handleGeometryChange();
- }
- }
}
// ----------------------- Display link -----------------------
@@ -181,8 +236,10 @@ Q_LOGGING_CATEGORY(lcQpaScreenUpdates, "qt.qpa.screen.updates", QtCriticalMsg);
void QCocoaScreen::requestUpdate()
{
+ Q_ASSERT(m_displayId);
+
if (!m_displayLink) {
- CVDisplayLinkCreateWithCGDisplay(nativeScreen().qt_displayId, &m_displayLink);
+ CVDisplayLinkCreateWithCGDisplay(m_displayId, &m_displayLink);
CVDisplayLinkSetOutputCallback(m_displayLink, [](CVDisplayLinkRef, const CVTimeStamp*,
const CVTimeStamp*, CVOptionFlags, CVOptionFlags*, void* displayLinkContext) -> int {
// FIXME: It would be nice if update requests would include timing info
@@ -269,6 +326,9 @@ struct DeferredDebugHelper
void QCocoaScreen::deliverUpdateRequests()
{
+ if (!m_displayId)
+ return; // Screen removed
+
QMacAutoReleasePool pool;
// The CVDisplayLink callback is a notification that it's a good time to produce a new frame.
@@ -283,7 +343,7 @@ void QCocoaScreen::deliverUpdateRequests()
const int pendingUpdates = ++m_pendingUpdates;
DeferredDebugHelper screenUpdates(lcQpaScreenUpdates());
- qDeferredDebug(screenUpdates) << "display link callback for screen " << m_screenIndex;
+ qDeferredDebug(screenUpdates) << "display link callback for screen " << m_displayId;
if (const int framesAheadOfDelivery = pendingUpdates - 1) {
// If we have more than one update pending it means that a previous display link callback
@@ -370,13 +430,6 @@ bool QCocoaScreen::isRunningDisplayLink() const
// -----------------------------------------------------------
-qreal QCocoaScreen::devicePixelRatio() const
-{
- QMacAutoReleasePool pool;
- NSScreen *nsScreen = nativeScreen();
- return qreal(nsScreen ? [nsScreen backingScaleFactor] : 1.0);
-}
-
QPlatformScreen::SubpixelAntialiasingType QCocoaScreen::subpixelAntialiasingTypeHint() const
{
QPlatformScreen::SubpixelAntialiasingType type = QPlatformScreen::subpixelAntialiasingTypeHint();
@@ -430,7 +483,7 @@ QPixmap QCocoaScreen::grabWindow(WId view, int x, int y, int width, int height)
{
// Determine the grab rect. FIXME: The rect should be bounded by the view's
// geometry, but note that for the pixeltool use case that window will be the
- // desktop widgets's view, which currently gets resized to fit one screen
+ // desktop widget's view, which currently gets resized to fit one screen
// only, since its NSWindow has the NSWindowStyleMaskTitled flag set.
Q_UNUSED(view);
QRect grabRect = QRect(x, y, width, height);
@@ -482,7 +535,7 @@ QPixmap QCocoaScreen::grabWindow(WId view, int x, int y, int width, int height)
for (uint i = 0; i < displayCount; ++i)
dpr = qMax(dpr, images.at(i).devicePixelRatio());
- // Alocate target pixmap and draw each screen's content
+ // Allocate target pixmap and draw each screen's content
qCDebug(lcQpaScreen) << "Create grap pixmap" << grabRect.size() << "at devicePixelRatio" << dpr;
QPixmap windowPixmap(grabRect.size() * dpr);
windowPixmap.setDevicePixelRatio(dpr);
@@ -499,7 +552,57 @@ QPixmap QCocoaScreen::grabWindow(WId view, int x, int y, int width, int height)
*/
QCocoaScreen *QCocoaScreen::primaryScreen()
{
- return static_cast<QCocoaScreen *>(QGuiApplication::primaryScreen()->handle());
+ auto screen = static_cast<QCocoaScreen *>(QGuiApplication::primaryScreen()->handle());
+ Q_ASSERT_X(screen == get(CGMainDisplayID()), "QCocoaScreen",
+ "The application's primary screen should always be in sync with the main display");
+ return screen;
+}
+
+QList<QPlatformScreen*> QCocoaScreen::virtualSiblings() const
+{
+ QList<QPlatformScreen*> siblings;
+
+ // Screens on macOS are always part of the same virtual desktop
+ for (QScreen *screen : QGuiApplication::screens())
+ siblings << screen->handle();
+
+ return siblings;
+}
+
+QCocoaScreen *QCocoaScreen::get(NSScreen *nsScreen)
+{
+ return get(nsScreen.qt_displayId);
+}
+
+QCocoaScreen *QCocoaScreen::get(CGDirectDisplayID displayId)
+{
+ for (QScreen *screen : QGuiApplication::screens()) {
+ QCocoaScreen *cocoaScreen = static_cast<QCocoaScreen*>(screen->handle());
+ if (cocoaScreen->m_displayId == displayId)
+ return cocoaScreen;
+ }
+
+ return nullptr;
+}
+
+NSScreen *QCocoaScreen::nativeScreen() const
+{
+ if (!m_displayId)
+ return nil; // The display has been disconnected
+
+ // A single display may have different displayIds depending on
+ // which GPU is in use or which physical port the display is
+ // connected to. By comparing UUIDs instead of display IDs we
+ // ensure that we always pick up the appropriate NSScreen.
+ QCFType<CFUUIDRef> uuid = CGDisplayCreateUUIDFromDisplayID(m_displayId);
+
+ for (NSScreen *screen in [NSScreen screens]) {
+ if (CGDisplayCreateUUIDFromDisplayID(screen.qt_displayId) == uuid)
+ return screen;
+ }
+
+ qCWarning(lcQpaScreen) << "Could not find NSScreen for display ID" << m_displayId;
+ return nil;
}
CGPoint QCocoaScreen::mapToNative(const QPointF &pos, QCocoaScreen *screen)
@@ -533,11 +636,10 @@ QDebug operator<<(QDebug debug, const QCocoaScreen *screen)
debug.nospace();
debug << "QCocoaScreen(" << (const void *)screen;
if (screen) {
- debug << ", index=" << screen->m_screenIndex;
- debug << ", native=" << screen->nativeScreen();
debug << ", geometry=" << screen->geometry();
debug << ", dpr=" << screen->devicePixelRatio();
debug << ", name=" << screen->name();
+ debug << ", native=" << screen->nativeScreen();
}
debug << ')';
return debug;
diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
index 4982f5ee05..de5cf85854 100644
--- a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
+++ b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
@@ -383,9 +383,9 @@ QT_END_NAMESPACE
}
- (QRectF)geometry {
- if (NSWindow *window = [[item view] window]) {
- if (QCocoaScreen *screen = QCocoaIntegration::instance()->screenForNSScreen([window screen]))
- return screen->mapFromNative([window frame]);
+ if (NSWindow *window = item.view.window) {
+ if (QCocoaScreen *screen = QCocoaScreen::get(window.screen))
+ return screen->mapFromNative(window.frame);
}
return QRectF();
}
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm
index dc7319484e..363a026e71 100644
--- a/src/plugins/platforms/cocoa/qcocoawindow.mm
+++ b/src/plugins/platforms/cocoa/qcocoawindow.mm
@@ -217,6 +217,7 @@ QCocoaWindow::~QCocoaWindow()
}
[m_view release];
+ [m_nsWindow close];
[m_nsWindow release];
}
@@ -618,7 +619,7 @@ void QCocoaWindow::applyWindowState(Qt::WindowStates requestedState)
if (nsWindow.styleMask & NSWindowStyleMaskUtilityWindow
&& newState & (Qt::WindowMinimized | Qt::WindowFullScreen)) {
- qWarning() << window()->type() << "windows can not be made" << newState;
+ qWarning() << window()->type() << "windows cannot be made" << newState;
handleWindowStateChanged(HandleUnconditionally);
return;
}
@@ -1210,17 +1211,17 @@ void QCocoaWindow::windowDidChangeScreen()
return;
// Note: When a window is resized to 0x0 Cocoa will report the window's screen as nil
- auto *currentScreen = QCocoaIntegration::instance()->screenForNSScreen(m_view.window.screen);
+ auto *currentScreen = QCocoaScreen::get(m_view.window.screen);
auto *previousScreen = static_cast<QCocoaScreen*>(screen());
Q_ASSERT_X(!m_view.window.screen || currentScreen,
"QCocoaWindow", "Failed to get QCocoaScreen for NSScreen");
// Note: The previous screen may be the same as the current screen, either because
- // the screen was just reconfigured, which still results in AppKit sending an
- // NSWindowDidChangeScreenNotification, because the previous screen was removed,
+ // a) the screen was just reconfigured, which still results in AppKit sending an
+ // NSWindowDidChangeScreenNotification, b) because the previous screen was removed,
// and we ended up calling QWindow::setScreen to move the window, which doesn't
- // actually move the window to the new screen, or because we've delivered the
+ // actually move the window to the new screen, or c) because we've delivered the
// screen change to the top level window, which will make all the child windows
// of that window report the new screen when requested via QWindow::screen().
// We still need to deliver the screen change in all these cases, as the
diff --git a/src/plugins/platforms/cocoa/qmacclipboard.mm b/src/plugins/platforms/cocoa/qmacclipboard.mm
index ba6cfca219..358a6b49fd 100644
--- a/src/plugins/platforms/cocoa/qmacclipboard.mm
+++ b/src/plugins/platforms/cocoa/qmacclipboard.mm
@@ -489,19 +489,16 @@ QMacPasteboard::retrieveData(const QString &format, QVariant::Type) const
QMacInternalPasteboardMime *c = mimes.at(mime);
QString c_flavor = c->flavorFor(format);
if (!c_flavor.isEmpty()) {
- // Handle text/plain a little differently. Try handling Unicode first.
- bool checkForUtf16 = (c_flavor == QLatin1String("com.apple.traditional-mac-plain-text")
- || c_flavor == QLatin1String("public.utf8-plain-text"));
- if (checkForUtf16 || c_flavor == QLatin1String("public.utf16-plain-text")) {
- // Try to get the NSStringPboardType from NSPasteboard, newlines are mapped
- // correctly (as '\n') in this data. The 'public.utf16-plain-text' type
- // usually maps newlines to '\r' instead.
+ // Converting via PasteboardCopyItemFlavorData below will for some UITs result
+ // in newlines mapping to '\r' instead of '\n'. To work around this we shortcut
+ // the conversion via NSPasteboard's NSStringPboardType if possible.
+ if (c_flavor == QLatin1String("com.apple.traditional-mac-plain-text")
+ || c_flavor == QLatin1String("public.utf8-plain-text")
+ || c_flavor == QLatin1String("public.utf16-plain-text")) {
QString str = qt_mac_get_pasteboardString(paste);
if (!str.isEmpty())
return str;
}
- if (checkForUtf16 && hasFlavor(QLatin1String("public.utf16-plain-text")))
- c_flavor = QLatin1String("public.utf16-plain-text");
QVariant ret;
QList<QByteArray> retList;
diff --git a/src/plugins/platforms/cocoa/qnsview_drawing.mm b/src/plugins/platforms/cocoa/qnsview_drawing.mm
index 116a9a73df..d2e6f848a0 100644
--- a/src/plugins/platforms/cocoa/qnsview_drawing.mm
+++ b/src/plugins/platforms/cocoa/qnsview_drawing.mm
@@ -106,7 +106,7 @@
"_q_mac_wantsLayer", "QT_MAC_WANTS_LAYER");
if (wantsLayer != -1 && [self layerEnabledByMacOS]) {
- qCWarning(lcQpaDrawing) << "Layer-backing can not be explicitly controlled on 10.14 when built against the 10.14 SDK";
+ qCWarning(lcQpaDrawing) << "Layer-backing cannot be explicitly controlled on 10.14 when built against the 10.14 SDK";
return true;
}
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp
index 24f82e7843..24051c352e 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp
@@ -423,7 +423,7 @@ void QEglFSKmsGbmScreen::updateFlipStatus()
if (m_flipPending)
return;
- for (const CloneDestination &d : m_cloneDests) {
+ for (const CloneDestination &d : qAsConst(m_cloneDests)) {
if (d.cloneFlipPending)
return;
}
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/qeglfsvivwlintegration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/qeglfsvivwlintegration.cpp
index 3bdae239cd..296e301f07 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/qeglfsvivwlintegration.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/qeglfsvivwlintegration.cpp
@@ -66,6 +66,11 @@ void QEglFSVivWaylandIntegration::platformInit()
mScreenSize.setWidth(width);
}
+void QEglFSVivWaylandIntegration::platformDestroy()
+{
+ wl_display_destroy(mWaylandDisplay);
+}
+
QSize QEglFSVivWaylandIntegration::screenSize() const
{
return mScreenSize;
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/qeglfsvivwlintegration.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/qeglfsvivwlintegration.h
index 2c49eb6440..bee23dfb3e 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/qeglfsvivwlintegration.h
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_viv_wl/qeglfsvivwlintegration.h
@@ -49,6 +49,7 @@ class QEglFSVivWaylandIntegration : public QEglFSDeviceIntegration
{
public:
void platformInit() override;
+ void platformDestroy() override;
QSize screenSize() const override;
EGLNativeWindowType createNativeWindow(QPlatformWindow *window, const QSize &size, const QSurfaceFormat &format) override;
void destroyNativeWindow(EGLNativeWindowType window) override;
diff --git a/src/plugins/platforms/linuxfb/qlinuxfbintegration.h b/src/plugins/platforms/linuxfb/qlinuxfbintegration.h
index 7a871b3812..af6bd1d630 100644
--- a/src/plugins/platforms/linuxfb/qlinuxfbintegration.h
+++ b/src/plugins/platforms/linuxfb/qlinuxfbintegration.h
@@ -72,7 +72,7 @@ public:
QList<QPlatformScreen *> screens() const;
- QFunctionPointer platformFunction(const QByteArray &function) const Q_DECL_OVERRIDE;
+ QFunctionPointer platformFunction(const QByteArray &function) const override;
private:
void createInputHandlers();
diff --git a/src/plugins/platforms/offscreen/offscreen.pro b/src/plugins/platforms/offscreen/offscreen.pro
index 392ee8bed1..f226132592 100644
--- a/src/plugins/platforms/offscreen/offscreen.pro
+++ b/src/plugins/platforms/offscreen/offscreen.pro
@@ -17,13 +17,10 @@ HEADERS = qoffscreenintegration.h \
OTHER_FILES += offscreen.json
-qtConfig(system-xcb):qtConfig(xlib):qtConfig(opengl):!qtConfig(opengles2) {
+qtConfig(xlib):qtConfig(opengl):!qtConfig(opengles2) {
SOURCES += qoffscreenintegration_x11.cpp
HEADERS += qoffscreenintegration_x11.h
QT += glx_support-private
- system(echo "Using X11 offscreen integration with GLX")
-} else {
- SOURCES += qoffscreenintegration_dummy.cpp
}
PLUGIN_TYPE = platforms
diff --git a/src/plugins/platforms/offscreen/qoffscreenintegration.cpp b/src/plugins/platforms/offscreen/qoffscreenintegration.cpp
index ef3b0dd3ff..869e9228cd 100644
--- a/src/plugins/platforms/offscreen/qoffscreenintegration.cpp
+++ b/src/plugins/platforms/offscreen/qoffscreenintegration.cpp
@@ -67,6 +67,10 @@
#include <qpa/qplatformservices.h>
+#if QT_CONFIG(xlib) && QT_CONFIG(opengl) && !QT_CONFIG(opengles2)
+#include "qoffscreenintegration_x11.h"
+#endif
+
QT_BEGIN_NAMESPACE
class QCoreTextFontEngine;
@@ -220,4 +224,14 @@ QPlatformServices *QOffscreenIntegration::services() const
return m_services.data();
}
+QOffscreenIntegration *QOffscreenIntegration::createOffscreenIntegration()
+{
+#if QT_CONFIG(xlib) && QT_CONFIG(opengl) && !QT_CONFIG(opengles2)
+ QByteArray glx = qgetenv("QT_QPA_OFFSCREEN_NO_GLX");
+ if (glx.isEmpty())
+ return new QOffscreenX11Integration;
+#endif
+ return new QOffscreenIntegration;
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/offscreen/qoffscreenintegration.h b/src/plugins/platforms/offscreen/qoffscreenintegration.h
index fc988126bb..098e726550 100644
--- a/src/plugins/platforms/offscreen/qoffscreenintegration.h
+++ b/src/plugins/platforms/offscreen/qoffscreenintegration.h
@@ -41,6 +41,7 @@
#define QOFFSCREENINTEGRATION_H
#include <qpa/qplatformintegration.h>
+#include <qpa/qplatformnativeinterface.h>
#include <qscopedpointer.h>
diff --git a/src/plugins/platforms/offscreen/qoffscreenintegration_x11.cpp b/src/plugins/platforms/offscreen/qoffscreenintegration_x11.cpp
index 93566220e8..92fc8aa57a 100644
--- a/src/plugins/platforms/offscreen/qoffscreenintegration_x11.cpp
+++ b/src/plugins/platforms/offscreen/qoffscreenintegration_x11.cpp
@@ -41,6 +41,7 @@
#include <QByteArray>
#include <QOpenGLContext>
+#include <QtPlatformHeaders/QGLXNativeContext>
#include <X11/Xlib.h>
#include <GL/glx.h>
@@ -52,16 +53,36 @@
QT_BEGIN_NAMESPACE
-QOffscreenIntegration *QOffscreenIntegration::createOffscreenIntegration()
+class QOffscreenX11Info
{
- return new QOffscreenX11Integration;
-}
+public:
+ QOffscreenX11Info(QOffscreenX11Connection *connection)
+ : m_connection(connection)
+ {
+ }
+
+ Display *display() const {
+ return (Display *)m_connection->display();
+ }
+
+ Window root() const {
+ return DefaultRootWindow(display());
+ }
+
+ int screenNumber() const {
+ return m_connection->screenNumber();
+ }
+
+private:
+ QOffscreenX11Connection *m_connection;
+};
bool QOffscreenX11Integration::hasCapability(QPlatformIntegration::Capability cap) const
{
switch (cap) {
case OpenGL: return true;
case ThreadedOpenGL: return true;
+ case RasterGLSurface: return true;
default: return QOffscreenIntegration::hasCapability(cap);
}
}
@@ -77,6 +98,40 @@ QPlatformOpenGLContext *QOffscreenX11Integration::createPlatformOpenGLContext(QO
return new QOffscreenX11GLXContext(m_connection->x11Info(), context);
}
+QPlatformNativeInterface *QOffscreenX11Integration::nativeInterface() const
+{
+ return const_cast<QOffscreenX11Integration *>(this);
+}
+
+void *QOffscreenX11Integration::nativeResourceForScreen(const QByteArray &resource, QScreen *screen)
+{
+ Q_UNUSED(screen)
+ if (resource.toLower() == QByteArrayLiteral("display") ) {
+ if (!m_connection)
+ m_connection.reset(new QOffscreenX11Connection);
+ return m_connection->display();
+ }
+ return nullptr;
+}
+
+#ifndef QT_NO_OPENGL
+void *QOffscreenX11Integration::nativeResourceForContext(const QByteArray &resource, QOpenGLContext *context) {
+ if (resource.toLower() == QByteArrayLiteral("glxconfig") ) {
+ if (context) {
+ QOffscreenX11GLXContext *glxPlatformContext = static_cast<QOffscreenX11GLXContext *>(context->handle());
+ return glxPlatformContext->glxConfig();
+ }
+ }
+ if (resource.toLower() == QByteArrayLiteral("glxcontext") ) {
+ if (context) {
+ QOffscreenX11GLXContext *glxPlatformContext = static_cast<QOffscreenX11GLXContext *>(context->handle());
+ return glxPlatformContext->glxContext();
+ }
+ }
+ return nullptr;
+}
+#endif
+
QOffscreenX11Connection::QOffscreenX11Connection()
{
XInitThreads();
@@ -93,30 +148,6 @@ QOffscreenX11Connection::~QOffscreenX11Connection()
XCloseDisplay((Display *)m_display);
}
-class QOffscreenX11Info
-{
-public:
- QOffscreenX11Info(QOffscreenX11Connection *connection)
- : m_connection(connection)
- {
- }
-
- Display *display() const {
- return (Display *)m_connection->display();
- }
-
- Window root() const {
- return DefaultRootWindow(display());
- }
-
- int screenNumber() const {
- return m_connection->screenNumber();
- }
-
-private:
- QOffscreenX11Connection *m_connection;
-};
-
QOffscreenX11Info *QOffscreenX11Connection::x11Info()
{
if (!m_x11Info)
@@ -127,11 +158,12 @@ QOffscreenX11Info *QOffscreenX11Connection::x11Info()
class QOffscreenX11GLXContextData
{
public:
- QOffscreenX11Info *x11;
+ QOffscreenX11Info *x11 = nullptr;
QSurfaceFormat format;
- GLXContext context;
- GLXContext shareContext;
- Window window;
+ GLXContext context = nullptr;
+ GLXContext shareContext = nullptr;
+ GLXFBConfig config = nullptr;
+ Window window = 0;
};
static Window createDummyWindow(QOffscreenX11Info *x11, XVisualInfo *visualInfo)
@@ -142,6 +174,7 @@ static Window createDummyWindow(QOffscreenX11Info *x11, XVisualInfo *visualInfo)
a.border_pixel = BlackPixel(x11->display(), x11->screenNumber());
a.colormap = cmap;
+
Window window = XCreateWindow(x11->display(), x11->root(),
0, 0, 100, 100,
0, visualInfo->depth, InputOutput, visualInfo->visual,
@@ -163,14 +196,23 @@ static Window createDummyWindow(QOffscreenX11Info *x11, GLXFBConfig config)
QOffscreenX11GLXContext::QOffscreenX11GLXContext(QOffscreenX11Info *x11, QOpenGLContext *context)
: d(new QOffscreenX11GLXContextData)
{
+
d->x11 = x11;
d->format = context->format();
+ if (d->format.renderableType() == QSurfaceFormat::DefaultRenderableType)
+ d->format.setRenderableType(QSurfaceFormat::OpenGL);
+
+ if (d->format.renderableType() != QSurfaceFormat::OpenGL)
+ return;
+
d->shareContext = 0;
if (context->shareHandle())
d->shareContext = static_cast<QOffscreenX11GLXContext *>(context->shareHandle())->d->context;
GLXFBConfig config = qglx_findConfig(x11->display(), x11->screenNumber(), d->format);
+ d->config = config;
+
if (config) {
d->context = glXCreateNewContext(x11->display(), config, GLX_RGBA_TYPE, d->shareContext, true);
if (!d->context && d->shareContext) {
@@ -199,6 +241,9 @@ QOffscreenX11GLXContext::QOffscreenX11GLXContext(QOffscreenX11Info *x11, QOpenGL
d->window = createDummyWindow(x11, visualInfo);
XFree(visualInfo);
}
+ if (d->context)
+ context->setNativeHandle(QVariant::fromValue<QGLXNativeContext>(QGLXNativeContext(d->context)));
+
}
QOffscreenX11GLXContext::~QOffscreenX11GLXContext()
@@ -251,4 +296,14 @@ bool QOffscreenX11GLXContext::isValid() const
return d->context && d->window;
}
+void *QOffscreenX11GLXContext::glxContext() const
+{
+ return d->context;
+}
+
+void *QOffscreenX11GLXContext::glxConfig() const
+{
+ return d->config;
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/offscreen/qoffscreenintegration_x11.h b/src/plugins/platforms/offscreen/qoffscreenintegration_x11.h
index 5e1c6b799b..5ef51a15a8 100644
--- a/src/plugins/platforms/offscreen/qoffscreenintegration_x11.h
+++ b/src/plugins/platforms/offscreen/qoffscreenintegration_x11.h
@@ -52,12 +52,19 @@ QT_BEGIN_NAMESPACE
class QOffscreenX11Connection;
class QOffscreenX11Info;
-class QOffscreenX11Integration : public QOffscreenIntegration
+class QOffscreenX11Integration : public QOffscreenIntegration, public QPlatformNativeInterface
{
public:
bool hasCapability(QPlatformIntegration::Capability cap) const override;
QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const override;
+ QPlatformNativeInterface *nativeInterface()const override;
+
+ // QPlatformNativeInterface
+ void *nativeResourceForScreen(const QByteArray &resource, QScreen *screen) override;
+#ifndef QT_NO_OPENGL
+ void *nativeResourceForContext(const QByteArray &resource, QOpenGLContext *context) override;
+#endif
private:
mutable QScopedPointer<QOffscreenX11Connection> m_connection;
@@ -97,6 +104,9 @@ public:
bool isSharing() const override;
bool isValid() const override;
+ void *glxConfig() const;
+ void *glxContext() const;
+
private:
QScopedPointer<QOffscreenX11GLXContextData> d;
};
diff --git a/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp b/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp
index e4843cb438..f29e11489b 100644
--- a/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp
+++ b/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp
@@ -381,6 +381,8 @@ void QQnxScreenEventHandler::handlePointerEvent(screen_event_t event)
if (m_lastGlobalMousePoint != globalPoint ||
m_lastLocalMousePoint != localPoint ||
m_lastButtonState != buttons) {
+ if (m_lastButtonState != 0 && buttons == 0)
+ (static_cast<QQnxWindow *>(w->handle()))->handleActivationEvent();
QWindowSystemInterface::handleMouseEvent(w, localPoint, globalPoint, buttons);
qScreenEventDebug() << "Qt mouse, w=" << w << ", (" << localPoint.x() << "," << localPoint.y() << "), b=" << static_cast<int>(buttons);
}
@@ -457,6 +459,9 @@ void QQnxScreenEventHandler::handleTouchEvent(screen_event_t event, int qnxType)
m_lastMouseWindow = qnxWindow;
if (w) {
+ if (qnxType == SCREEN_EVENT_MTOUCH_RELEASE)
+ (static_cast<QQnxWindow *>(w->handle()))->handleActivationEvent();
+
// get size of screen which contains window
QPlatformScreen *platformScreen = QPlatformScreen::platformScreenForWindow(w);
QSizeF screenSize = platformScreen->geometry().size();
diff --git a/src/plugins/platforms/qnx/qqnxwindow.cpp b/src/plugins/platforms/qnx/qqnxwindow.cpp
index 2096bc789e..7644e28b44 100644
--- a/src/plugins/platforms/qnx/qqnxwindow.cpp
+++ b/src/plugins/platforms/qnx/qqnxwindow.cpp
@@ -156,7 +156,8 @@ QQnxWindow::QQnxWindow(QWindow *window, screen_context_t context, bool needRootW
m_visible(false),
m_exposed(true),
m_windowState(Qt::WindowNoState),
- m_mmRendererWindow(0)
+ m_mmRendererWindow(0),
+ m_firstActivateHandled(false)
{
qWindowDebug() << "window =" << window << ", size =" << window->size();
@@ -341,6 +342,14 @@ void QQnxWindow::setVisible(bool visible)
if (visible) {
applyWindowState();
} else {
+ if (showWithoutActivating() && focusable() && m_firstActivateHandled) {
+ m_firstActivateHandled = false;
+ int val = SCREEN_SENSITIVITY_NO_FOCUS;
+ Q_SCREEN_CHECKERROR(
+ screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SENSITIVITY, &val),
+ "Failed to set window sensitivity");
+ }
+
// Flush the context, otherwise it won't disappear immediately
screen_flush_context(m_screenContext, 0);
}
@@ -618,13 +627,56 @@ void QQnxWindow::requestActivateWindow()
void QQnxWindow::setFocus(screen_window_t newFocusWindow)
{
+ screen_window_t temporaryFocusWindow = nullptr;
+
screen_group_t screenGroup = 0;
- screen_get_window_property_pv(nativeHandle(), SCREEN_PROPERTY_GROUP,
- reinterpret_cast<void**>(&screenGroup));
- if (screenGroup) {
- screen_set_group_property_pv(screenGroup, SCREEN_PROPERTY_FOCUS,
- reinterpret_cast<void**>(&newFocusWindow));
+ Q_SCREEN_CHECKERROR(screen_get_window_property_pv(nativeHandle(), SCREEN_PROPERTY_GROUP,
+ reinterpret_cast<void **>(&screenGroup)),
+ "Failed to retrieve window group");
+
+ if (showWithoutActivating() && focusable() && !m_firstActivateHandled) {
+ m_firstActivateHandled = true;
+ int val = SCREEN_SENSITIVITY_TEST;
+ Q_SCREEN_CHECKERROR(
+ screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SENSITIVITY, &val),
+ "Failed to set window sensitivity");
+
+#if _SCREEN_VERSION < _SCREEN_MAKE_VERSION(1, 0, 0)
+ // For older versions of screen, the window may still have group
+ // focus even though it was marked NO_FOCUS when it was hidden.
+ // In that situation, focus has to be given to another window
+ // so that this window can take focus back from it.
+ screen_window_t oldFocusWindow = nullptr;
+ Q_SCREEN_CHECKERROR(
+ screen_get_group_property_pv(screenGroup, SCREEN_PROPERTY_FOCUS,
+ reinterpret_cast<void **>(&oldFocusWindow)),
+ "Failed to retrieve group focus");
+ if (newFocusWindow == oldFocusWindow) {
+ char groupName[256];
+ memset(groupName, 0, sizeof(groupName));
+ Q_SCREEN_CHECKERROR(screen_get_group_property_cv(screenGroup, SCREEN_PROPERTY_NAME,
+ sizeof(groupName) - 1, groupName),
+ "Failed to retrieve group name");
+
+ Q_SCREEN_CHECKERROR(screen_create_window_type(&temporaryFocusWindow,
+ m_screenContext, SCREEN_CHILD_WINDOW),
+ "Failed to create temporary focus window");
+ Q_SCREEN_CHECKERROR(screen_join_window_group(temporaryFocusWindow, groupName),
+ "Temporary focus window failed to join window group");
+ Q_SCREEN_CHECKERROR(
+ screen_set_group_property_pv(screenGroup, SCREEN_PROPERTY_FOCUS,
+ reinterpret_cast<void **>(&temporaryFocusWindow)),
+ "Temporary focus window failed to take focus");
+ screen_flush_context(m_screenContext, 0);
+ }
+#endif
}
+
+ Q_SCREEN_CHECKERROR(screen_set_group_property_pv(screenGroup, SCREEN_PROPERTY_FOCUS,
+ reinterpret_cast<void **>(&newFocusWindow)),
+ "Failed to set group focus");
+
+ screen_destroy_window(temporaryFocusWindow);
}
void QQnxWindow::setWindowState(Qt::WindowStates state)
@@ -711,7 +763,11 @@ void QQnxWindow::initWindow()
screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SWAP_INTERVAL, &val),
"Failed to set swap interval");
- if (window()->flags() & Qt::WindowDoesNotAcceptFocus) {
+ if (showWithoutActivating() || !focusable()) {
+ // NO_FOCUS is temporary for showWithoutActivating (and pop-up) windows.
+ // Using NO_FOCUS ensures that screen doesn't activate the window because
+ // it was just created. Sensitivity will be changed to TEST when the
+ // window is clicked or touched.
val = SCREEN_SENSITIVITY_NO_FOCUS;
Q_SCREEN_CHECKERROR(
screen_set_window_property_iv(m_window, SCREEN_PROPERTY_SENSITIVITY, &val),
@@ -825,4 +881,22 @@ bool QQnxWindow::shouldMakeFullScreen() const
&& (QQnxIntegration::instance()->options() & QQnxIntegration::FullScreenApplication));
}
+
+void QQnxWindow::handleActivationEvent()
+{
+ if (showWithoutActivating() && focusable() && !m_firstActivateHandled)
+ requestActivateWindow();
+}
+
+bool QQnxWindow::showWithoutActivating() const
+{
+ return (window()->flags() & Qt::Popup) == Qt::Popup
+ || window()->property("_q_showWithoutActivating").toBool();
+}
+
+bool QQnxWindow::focusable() const
+{
+ return (window()->flags() & Qt::WindowDoesNotAcceptFocus) != Qt::WindowDoesNotAcceptFocus;
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/qnx/qqnxwindow.h b/src/plugins/platforms/qnx/qqnxwindow.h
index 2895a547b1..20c38cb4b7 100644
--- a/src/plugins/platforms/qnx/qqnxwindow.h
+++ b/src/plugins/platforms/qnx/qqnxwindow.h
@@ -113,6 +113,7 @@ public:
bool shouldMakeFullScreen() const;
void windowPosted();
+ void handleActivationEvent();
protected:
virtual int pixelFormat() const = 0;
@@ -131,6 +132,8 @@ private:
void updateZorder(screen_window_t window, int &zOrder);
void applyWindowState();
void setFocus(screen_window_t newFocusWindow);
+ bool showWithoutActivating() const;
+ bool focusable() const;
screen_window_t m_window;
QSize m_bufferSize;
@@ -152,6 +155,7 @@ private:
QByteArray m_parentGroupName;
bool m_isTopLevel;
+ bool m_firstActivateHandled;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/vnc/qvnc.cpp b/src/plugins/platforms/vnc/qvnc.cpp
index ffe00de2b1..32114c6443 100644
--- a/src/plugins/platforms/vnc/qvnc.cpp
+++ b/src/plugins/platforms/vnc/qvnc.cpp
@@ -600,7 +600,7 @@ void QVncClientCursor::changeCursor(QCursor *widgetCursor, QWindow *window)
cursor = *platformImage.image();
hotspot = platformImage.hotspot();
}
- for (auto client : clients)
+ for (auto client : qAsConst(clients))
client->setDirtyCursor();
}
@@ -638,16 +638,14 @@ void QVncServer::init()
QVncServer::~QVncServer()
{
- for (auto client : clients) {
- delete client;
- }
+ qDeleteAll(clients);
}
void QVncServer::setDirty()
{
- for (auto client : clients) {
+ for (auto client : qAsConst(clients))
client->setDirty(qvnc_screen->dirtyRegion);
- }
+
qvnc_screen->clearDirty();
}
diff --git a/src/plugins/platforms/vnc/qvncscreen.cpp b/src/plugins/platforms/vnc/qvncscreen.cpp
index 67d33de2f0..2eca18fb4d 100644
--- a/src/plugins/platforms/vnc/qvncscreen.cpp
+++ b/src/plugins/platforms/vnc/qvncscreen.cpp
@@ -75,7 +75,7 @@ bool QVncScreen::initialize()
mDepth = 32;
mPhysicalSize = QSizeF(mGeometry.width()/96.*25.4, mGeometry.height()/96.*25.4);
- for (const QString &arg : mArgs) {
+ for (const QString &arg : qAsConst(mArgs)) {
QRegularExpressionMatch match;
if (arg.contains(mmSizeRx, &match)) {
mPhysicalSize = QSizeF(match.captured("width").toDouble(), match.captured("height").toDouble());
diff --git a/src/plugins/platforms/wasm/qtloader.js b/src/plugins/platforms/wasm/qtloader.js
index 37a5308034..ef4a6ec2b9 100644
--- a/src/plugins/platforms/wasm/qtloader.js
+++ b/src/plugins/platforms/wasm/qtloader.js
@@ -50,6 +50,7 @@
// External mode.usage:
//
// var config = {
+// canvasElements : [$("canvas-id")],
// showLoader: function() {
// loader.style.display = 'block'
// canvas.style.display = 'hidden'
@@ -69,6 +70,8 @@
// One or more HTML elements. QtLoader will display loader elements
// on these while loading the applicaton, and replace the loader with a
// canvas on load complete.
+// canvasElements : [canvas-element, ...]
+// One or more canvas elements.
// showLoader : function(status, containerElement)
// Optional loading element constructor function. Implement to create
// a custom loading screen. This function may be called multiple times,
@@ -115,6 +118,14 @@
// "Exited", iff crashed is false.
// exitText
// Abort/exit message.
+// addCanvasElement
+// Add canvas at run-time. Adds a corresponding QScreen,
+// removeCanvasElement
+// Remove canvas at run-time. Removes the corresponding QScreen.
+// resizeCanvasElement
+// Signals to the application that a canvas has been resized.
+// setFontDpi
+// Sets the logical font dpi for the application.
var Module = {}
@@ -146,8 +157,26 @@ function QtLoader(config)
while (element.firstChild) element.removeChild(element.firstChild);
}
- // Set default state handler functions if needed
+ function createCanvas() {
+ var canvas = document.createElement("canvas");
+ canvas.className = "QtCanvas";
+ canvas.style.height = "100%";
+ canvas.style.width = "100%";
+
+ // Set contentEditable in order to enable clipboard events; hide the resulting focus frame.
+ canvas.contentEditable = true;
+ canvas.style.outline = "0px solid transparent";
+ canvas.style.caretColor = "transparent";
+ canvas.style.cursor = "default";
+
+ return canvas;
+ }
+
+ // Set default state handler functions and create canvases if needed
if (config.containerElements !== undefined) {
+
+ config.canvasElements = config.containerElements.map(createCanvas);
+
config.showError = config.showError || function(errorText, container) {
removeChildren(container);
var errorTextElement = document.createElement("text");
@@ -164,12 +193,8 @@ function QtLoader(config)
return loadingText;
};
- config.showCanvas = config.showCanvas || function(container) {
+ config.showCanvas = config.showCanvas || function(canvas, container) {
removeChildren(container);
- var canvas = document.createElement("canvas");
- canvas.className = "QtCanvas"
- canvas.style = "height: 100%; width: 100%;"
- return canvas;
}
config.showExit = config.showExit || function(crashed, exitCode, container) {
@@ -211,6 +236,11 @@ function QtLoader(config)
publicAPI.canLoadApplication = canLoadQt();
publicAPI.status = undefined;
publicAPI.loadEmscriptenModule = loadEmscriptenModule;
+ publicAPI.addCanvasElement = addCanvasElement;
+ publicAPI.removeCanvasElement = removeCanvasElement;
+ publicAPI.resizeCanvasElement = resizeCanvasElement;
+ publicAPI.setFontDpi = setFontDpi;
+ publicAPI.fontDpi = fontDpi;
restartCount = 0;
@@ -312,13 +342,13 @@ function QtLoader(config)
// and is ready to be instantiated. Define the instantiateWasm callback which
// emscripten will call to create the instance.
Module.instantiateWasm = function(imports, successCallback) {
- return WebAssembly.instantiate(wasmModule, imports).then(function(instance) {
- successCallback(instance);
- return instance;
+ WebAssembly.instantiate(wasmModule, imports).then(function(instance) {
+ successCallback(instance, wasmModule);
}, function(error) {
self.error = error;
setStatus("Error");
});
+ return {};
};
Module.locateFile = Module.locateFile || function(filename) {
@@ -378,10 +408,14 @@ function QtLoader(config)
Module.preRun = Module.preRun || []
Module.preRun.push(function() {
for (var [key, value] of Object.entries(config.environment)) {
- Module.ENV[key.toUpperCase()] = value;
+ ENV[key.toUpperCase()] = value;
}
});
+ Module.mainScriptUrlOrBlob = new Blob([emscriptenModuleSource], {type: 'text/javascript'});
+
+ Module.qtCanvasElements = config.canvasElements;
+
config.restart = function() {
// Restart by reloading the page. This will wipe all state which means
@@ -436,19 +470,17 @@ function QtLoader(config)
}
function setCanvasContent() {
- var firstCanvas;
if (config.containerElements === undefined) {
- firstCanvas = config.showCanvas();
- } else {
- for (container of config.containerElements) {
- var canvasElement = config.showCanvas(container);
- container.appendChild(canvasElement);
- }
- firstCanvas = config.containerElements[0].firstChild;
+ if (config.showCanvas !== undefined)
+ config.showCanvas();
+ return;
}
- if (Module.canvas === undefined) {
- Module.canvas = firstCanvas;
+ for (var i = 0; i < config.containerElements.length; ++i) {
+ var container = config.containerElements[i];
+ var canvas = config.canvasElements[i];
+ config.showCanvas(canvas, container);
+ container.appendChild(canvas);
}
}
@@ -510,6 +542,35 @@ function QtLoader(config)
window.setTimeout(function() { handleStatusChange(); }, 0);
}
+ function addCanvasElement(element) {
+ if (publicAPI.status == "Running")
+ Module.qtAddCanvasElement(element);
+ else
+ console.log("Error: addCanvasElement can only be called in the Running state");
+ }
+
+ function removeCanvasElement(element) {
+ if (publicAPI.status == "Running")
+ Module.qtRemoveCanvasElement(element);
+ else
+ console.log("Error: removeCanvasElement can only be called in the Running state");
+ }
+
+ function resizeCanvasElement(element) {
+ if (publicAPI.status == "Running")
+ Module.qtResizeCanvasElement(element);
+ }
+
+ function setFontDpi(dpi) {
+ Module.qtFontDpi = dpi;
+ if (publicAPI.status == "Running")
+ Module.qtSetFontDpi(dpi);
+ }
+
+ function fontDpi() {
+ return Module.qtFontDpi;
+ }
+
setStatus("Created");
return publicAPI;
diff --git a/src/plugins/platforms/wasm/qwasmbackingstore.cpp b/src/plugins/platforms/wasm/qwasmbackingstore.cpp
index 5b40c44807..e8eda2605f 100644
--- a/src/plugins/platforms/wasm/qwasmbackingstore.cpp
+++ b/src/plugins/platforms/wasm/qwasmbackingstore.cpp
@@ -55,6 +55,12 @@ QWasmBackingStore::~QWasmBackingStore()
{
}
+void QWasmBackingStore::destroy()
+{
+ if (m_texture->isCreated())
+ m_texture->destroy();
+}
+
QPaintDevice *QWasmBackingStore::paintDevice()
{
return &m_image;
diff --git a/src/plugins/platforms/wasm/qwasmbackingstore.h b/src/plugins/platforms/wasm/qwasmbackingstore.h
index 6ffa262e3d..4bca83c457 100644
--- a/src/plugins/platforms/wasm/qwasmbackingstore.h
+++ b/src/plugins/platforms/wasm/qwasmbackingstore.h
@@ -44,6 +44,7 @@ class QWasmBackingStore : public QPlatformBackingStore
public:
QWasmBackingStore(QWasmCompositor *compositor, QWindow *window);
~QWasmBackingStore();
+ void destroy();
QPaintDevice *paintDevice() override;
diff --git a/src/plugins/platforms/wasm/qwasmclipboard.cpp b/src/plugins/platforms/wasm/qwasmclipboard.cpp
new file mode 100644
index 0000000000..d4a1e4dd50
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmclipboard.cpp
@@ -0,0 +1,229 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) 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.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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwasmclipboard.h"
+#include "qwasmwindow.h"
+
+#include <emscripten.h>
+#include <emscripten/html5.h>
+#include <emscripten/bind.h>
+
+#include <QCoreApplication>
+#include <qpa/qwindowsysteminterface.h>
+
+using namespace emscripten;
+
+// there has got to be a better way...
+static QByteArray g_clipboardArray;
+static QByteArray g_clipboardFormat;
+
+static val getClipboardData()
+{
+ return val(g_clipboardArray.constData());
+}
+
+static val getClipboardFormat()
+{
+ return val(g_clipboardFormat.constData());
+}
+
+static void pasteClipboardData(emscripten::val format, emscripten::val dataPtr)
+{
+ QString formatString = QString::fromStdString(format.as<std::string>());
+ QByteArray dataArray = QByteArray::fromStdString(dataPtr.as<std::string>());
+ QMimeData *mMimeData = new QMimeData;
+ mMimeData->setData(formatString, dataArray);
+ QWasmClipboard::qWasmClipboardPaste(mMimeData);
+}
+
+static void qClipboardPromiseResolve(emscripten::val something)
+{
+ pasteClipboardData(emscripten::val("text/plain"), something);
+}
+
+static void qClipboardCutTo(val event)
+{
+ if (!QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi) {
+ // Send synthetic Ctrl+X to make the app cut data to Qt's clipboard
+ QWindowSystemInterface::handleKeyEvent<QWindowSystemInterface::SynchronousDelivery>(
+ 0, QEvent::KeyPress, Qt::Key_X, Qt::ControlModifier, "X");
+ }
+
+ val module = val::global("Module");
+ val clipdata = module.call<val>("qtGetClipboardData");
+ val clipFormat = module.call<val>("qtGetClipboardFormat");
+ event["clipboardData"].call<void>("setData", clipFormat, clipdata);
+ event.call<void>("preventDefault");
+}
+
+static void qClipboardCopyTo(val event)
+{
+ if (!QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi) {
+ // Send synthetic Ctrl+C to make the app copy data to Qt's clipboard
+ QWindowSystemInterface::handleKeyEvent<QWindowSystemInterface::SynchronousDelivery>(
+ 0, QEvent::KeyPress, Qt::Key_C, Qt::ControlModifier, "C");
+ }
+
+ val module = val::global("Module");
+ val clipdata = module.call<val>("qtGetClipboardData");
+ val clipFormat = module.call<val>("qtGetClipboardFormat");
+ event["clipboardData"].call<void>("setData", clipFormat, clipdata);
+ event.call<void>("preventDefault");
+}
+
+static void qClipboardPasteTo(val event)
+{
+ bool hasClipboardApi = QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi;
+ val clipdata = hasClipboardApi ?
+ val::global("Module").call<val>("qtGetClipboardData") :
+ event["clipboardData"].call<val>("getData", std::string("text"));
+
+ const std::string data = clipdata.as<std::string>();
+ if (data.length() > 0) {
+ QString qstr = QString::fromStdString(data);
+ QMimeData *mMimeData = new QMimeData;
+ mMimeData->setText(qstr);
+ QWasmClipboard::qWasmClipboardPaste(mMimeData);
+ }
+}
+
+EMSCRIPTEN_BINDINGS(qtClipboardModule) {
+ function("qtGetClipboardData", &getClipboardData);
+ function("qtGetClipboardFormat", &getClipboardFormat);
+ function("qtPasteClipboardData", &pasteClipboardData);
+ function("qtClipboardPromiseResolve", &qClipboardPromiseResolve);
+ function("qtClipboardCutTo", &qClipboardCutTo);
+ function("qtClipboardCopyTo", &qClipboardCopyTo);
+ function("qtClipboardPasteTo", &qClipboardPasteTo);
+}
+
+QWasmClipboard::QWasmClipboard()
+{
+ val clipboard = val::global("navigator")["clipboard"];
+ hasClipboardApi = (!clipboard.isUndefined() && !clipboard["readText"].isUndefined());
+
+ initClipboardEvents();
+}
+
+QWasmClipboard::~QWasmClipboard()
+{
+ g_clipboardArray.clear();
+ g_clipboardFormat.clear();
+}
+
+QMimeData* QWasmClipboard::mimeData(QClipboard::Mode mode)
+{
+ if (mode != QClipboard::Clipboard)
+ return nullptr;
+
+ return QPlatformClipboard::mimeData(mode);
+}
+
+void QWasmClipboard::setMimeData(QMimeData* mimeData, QClipboard::Mode mode)
+{
+ if (mimeData->hasText()) {
+ g_clipboardFormat = mimeData->formats().at(0).toUtf8();
+ g_clipboardArray = mimeData->text().toUtf8();
+ } else if (mimeData->hasHtml()) {
+ g_clipboardFormat =mimeData->formats().at(0).toUtf8();
+ g_clipboardArray = mimeData->html().toUtf8();
+ }
+
+ QPlatformClipboard::setMimeData(mimeData, mode);
+}
+
+bool QWasmClipboard::supportsMode(QClipboard::Mode mode) const
+{
+ return mode == QClipboard::Clipboard;
+}
+
+bool QWasmClipboard::ownsMode(QClipboard::Mode mode) const
+{
+ Q_UNUSED(mode);
+ return false;
+}
+
+void QWasmClipboard::qWasmClipboardPaste(QMimeData *mData)
+{
+ QWasmIntegration::get()->clipboard()->setMimeData(mData, QClipboard::Clipboard);
+
+ QWindowSystemInterface::handleKeyEvent<QWindowSystemInterface::SynchronousDelivery>(
+ 0, QEvent::KeyPress, Qt::Key_V, Qt::ControlModifier, "V");
+}
+
+void QWasmClipboard::initClipboardEvents()
+{
+ if (!hasClipboardApi)
+ return;
+
+ val permissions = val::global("navigator")["permissions"];
+ val readPermissionsMap = val::object();
+ readPermissionsMap.set("name", val("clipboard-read"));
+ permissions.call<val>("query", readPermissionsMap);
+
+ val writePermissionsMap = val::object();
+ writePermissionsMap.set("name", val("clipboard-write"));
+ permissions.call<val>("query", writePermissionsMap);
+}
+
+void QWasmClipboard::installEventHandlers(const QString &canvasId)
+{
+ if (hasClipboardApi)
+ return;
+
+ // Fallback path for browsers which do not support direct clipboard access
+ val canvas = val::global(canvasId.toUtf8().constData());
+ canvas.call<void>("addEventListener", std::string("cut"),
+ val::module_property("qtClipboardCutTo"));
+ canvas.call<void>("addEventListener", std::string("copy"),
+ val::module_property("qtClipboardCopyTo"));
+ canvas.call<void>("addEventListener", std::string("paste"),
+ val::module_property("qtClipboardPasteTo"));
+}
+
+void QWasmClipboard::readTextFromClipboard()
+{
+ if (QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi) {
+ val navigator = val::global("navigator");
+ val textPromise = navigator["clipboard"].call<val>("readText");
+ val readTextResolve = val::global("Module")["qtClipboardPromiseResolve"];
+ textPromise.call<val>("then", readTextResolve);
+ }
+}
+
+void QWasmClipboard::writeTextToClipboard()
+{
+ if (QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi) {
+ val module = val::global("Module");
+ val txt = module.call<val>("qtGetClipboardData");
+ val format = module.call<val>("qtGetClipboardFormat");
+ val navigator = val::global("navigator");
+ navigator["clipboard"].call<void>("writeText", txt.as<std::string>());
+ }
+}
diff --git a/src/plugins/platforms/wasm/qwasmclipboard.h b/src/plugins/platforms/wasm/qwasmclipboard.h
new file mode 100644
index 0000000000..00aae8fead
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmclipboard.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) 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.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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWasmClipboard_H
+#define QWasmClipboard_H
+
+#include <QObject>
+
+#include <qpa/qplatformclipboard.h>
+#include <QMimeData>
+
+#include <emscripten/bind.h>
+
+class QWasmClipboard : public QObject, public QPlatformClipboard
+{
+public:
+ QWasmClipboard();
+ virtual ~QWasmClipboard();
+
+ // QPlatformClipboard methods.
+ QMimeData* mimeData(QClipboard::Mode mode = QClipboard::Clipboard) override;
+ void setMimeData(QMimeData* data, QClipboard::Mode mode = QClipboard::Clipboard) override;
+ bool supportsMode(QClipboard::Mode mode) const override;
+ bool ownsMode(QClipboard::Mode mode) const override;
+
+ static void qWasmClipboardPaste(QMimeData *mData);
+ void initClipboardEvents();
+ void installEventHandlers(const QString &canvasId);
+ bool hasClipboardApi;
+ void readTextFromClipboard();
+ void writeTextToClipboard();
+};
+
+#endif // QWASMCLIPBOARD_H
diff --git a/src/plugins/platforms/wasm/qwasmcompositor.cpp b/src/plugins/platforms/wasm/qwasmcompositor.cpp
index 3dc6b7d2f3..6d211667be 100644
--- a/src/plugins/platforms/wasm/qwasmcompositor.cpp
+++ b/src/plugins/platforms/wasm/qwasmcompositor.cpp
@@ -37,6 +37,7 @@
#include <QtGui/qopenglcontext.h>
#include <QtGui/qopenglfunctions.h>
#include <QtGui/qopengltextureblitter.h>
+#include <QtGui/qoffscreensurface.h>
#include <QtGui/qpainter.h>
#include <private/qpixmapcache_p.h>
@@ -56,8 +57,9 @@ QWasmCompositedWindow::QWasmCompositedWindow()
{
}
-QWasmCompositor::QWasmCompositor()
- : m_frameBuffer(nullptr)
+QWasmCompositor::QWasmCompositor(QWasmScreen *screen)
+ :QObject(screen)
+ , m_frameBuffer(nullptr)
, m_blitter(new QOpenGLTextureBlitter)
, m_needComposit(false)
, m_inFlush(false)
@@ -70,6 +72,28 @@ QWasmCompositor::QWasmCompositor()
QWasmCompositor::~QWasmCompositor()
{
delete m_frameBuffer;
+ destroy();
+}
+
+void QWasmCompositor::destroy()
+{
+ // Destroy OpenGL resources. This is done here in a separate function
+ // which can be called while screen() still returns a valid screen
+ // (which it might not, during destruction). A valid QScreen is
+ // a requirement for QOffscreenSurface on Wasm since the native
+ // context is tied to a single canvas.
+ if (m_context) {
+ QOffscreenSurface offScreenSurface(screen()->screen());
+ offScreenSurface.setFormat(m_context->format());
+ offScreenSurface.create();
+ m_context->makeCurrent(&offScreenSurface);
+ for (QWasmWindow *window : m_windowStack)
+ window->destroy();
+ m_blitter.reset(nullptr);
+ m_context.reset(nullptr);
+ }
+
+ m_isEnabled = false; // prevent frame() from creating a new m_context
}
void QWasmCompositor::setEnabled(bool enabled)
@@ -107,11 +131,6 @@ void QWasmCompositor::removeWindow(QWasmWindow *window)
notifyTopWindowChanged(window);
}
-void QWasmCompositor::setScreen(QWasmScreen *screen)
-{
- m_screen = screen;
-}
-
void QWasmCompositor::setVisible(QWasmWindow *window, bool visible)
{
QWasmCompositedWindow &compositedWindow = m_compositedWindows[window];
@@ -197,7 +216,7 @@ void QWasmCompositor::requestRedraw()
QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest));
}
-QWindow *QWasmCompositor::windowAt(QPoint p, int padding) const
+QWindow *QWasmCompositor::windowAt(QPoint globalPoint, int padding) const
{
int index = m_windowStack.count() - 1;
// qDebug() << "window at" << "point" << p << "window count" << index;
@@ -209,7 +228,7 @@ QWindow *QWasmCompositor::windowAt(QPoint p, int padding) const
QRect geometry = compositedWindow.window->windowFrameGeometry()
.adjusted(-padding, -padding, padding, padding);
- if (compositedWindow.visible && geometry.contains(p))
+ if (compositedWindow.visible && geometry.contains(globalPoint))
return m_windowStack.at(index)->window();
--index;
}
@@ -255,10 +274,13 @@ void QWasmCompositor::blit(QOpenGLTextureBlitter *blitter, QWasmScreen *screen,
void QWasmCompositor::drawWindowContent(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window)
{
QWasmBackingStore *backingStore = window->backingStore();
+ if (!backingStore)
+ return;
QOpenGLTexture const *texture = backingStore->getUpdatedTexture();
-
- blit(blitter, screen, texture, window->geometry());
+ QPoint windowCanvasPosition = window->geometry().topLeft() - screen->geometry().topLeft();
+ QRect windowCanvasGeometry = QRect(windowCanvasPosition, window->geometry().size());
+ blit(blitter, screen, texture, windowCanvasGeometry);
}
QPalette QWasmCompositor::makeWindowPalette()
@@ -654,7 +676,7 @@ void QWasmCompositor::frame()
m_needComposit = false;
- if (m_windowStack.empty() || !m_screen)
+ if (!m_isEnabled || m_windowStack.empty() || !screen())
return;
QWasmWindow *someWindow = nullptr;
@@ -673,17 +695,19 @@ void QWasmCompositor::frame()
if (m_context.isNull()) {
m_context.reset(new QOpenGLContext());
//mContext->setFormat(mScreen->format());
- m_context->setScreen(m_screen->screen());
+ m_context->setScreen(screen()->screen());
m_context->create();
}
- m_context->makeCurrent(someWindow->window());
+ bool ok = m_context->makeCurrent(someWindow->window());
+ if (!ok)
+ return;
if (!m_blitter->isCreated())
m_blitter->create();
- qreal dpr = m_screen->devicePixelRatio();
- glViewport(0, 0, m_screen->geometry().width() * dpr, m_screen->geometry().height() * dpr);
+ qreal dpr = screen()->devicePixelRatio();
+ glViewport(0, 0, screen()->geometry().width() * dpr, screen()->geometry().height() * dpr);
m_context->functions()->glClearColor(0.2, 0.2, 0.2, 1.0);
m_context->functions()->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
@@ -697,7 +721,7 @@ void QWasmCompositor::frame()
if (!compositedWindow.visible)
continue;
- drawWindow(m_blitter.data(), m_screen, window);
+ drawWindow(m_blitter.data(), screen(), window);
}
m_blitter->release();
@@ -719,3 +743,8 @@ void QWasmCompositor::notifyTopWindowChanged(QWasmWindow *window)
requestRedraw();
QWindowSystemInterface::handleWindowActivated(window->window());
}
+
+QWasmScreen *QWasmCompositor::screen()
+{
+ return static_cast<QWasmScreen *>(parent());
+}
diff --git a/src/plugins/platforms/wasm/qwasmcompositor.h b/src/plugins/platforms/wasm/qwasmcompositor.h
index 4e5ed46cec..98f4a79b27 100644
--- a/src/plugins/platforms/wasm/qwasmcompositor.h
+++ b/src/plugins/platforms/wasm/qwasmcompositor.h
@@ -62,8 +62,9 @@ class QWasmCompositor : public QObject
{
Q_OBJECT
public:
- QWasmCompositor();
+ QWasmCompositor(QWasmScreen *screen);
~QWasmCompositor();
+ void destroy();
enum QWasmSubControl {
SC_None = 0x00000000,
@@ -103,7 +104,6 @@ public:
void addWindow(QWasmWindow *window, QWasmWindow *parentWindow = nullptr);
void removeWindow(QWasmWindow *window);
- void setScreen(QWasmScreen *screen);
void setVisible(QWasmWindow *window, bool visible);
void raise(QWasmWindow *window);
@@ -117,7 +117,7 @@ public:
void redrawWindowContent();
void requestRedraw();
- QWindow *windowAt(QPoint p, int padding = 0) const;
+ QWindow *windowAt(QPoint globalPoint, int padding = 0) const;
QWindow *keyWindow() const;
bool event(QEvent *event);
@@ -129,8 +129,7 @@ private slots:
void frame();
private:
- void createFrameBuffer();
- void flushCompletedCallback(int32_t);
+ QWasmScreen *screen();
void notifyTopWindowChanged(QWasmWindow *window);
void drawWindow(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window);
void drawWindowContent(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, QWasmWindow *window);
@@ -142,7 +141,6 @@ private:
QImage *m_frameBuffer;
QScopedPointer<QOpenGLContext> m_context;
QScopedPointer<QOpenGLTextureBlitter> m_blitter;
- QWasmScreen *m_screen;
QHash<QWasmWindow *, QWasmCompositedWindow> m_compositedWindows;
QList<QWasmWindow *> m_windowStack;
diff --git a/src/plugins/platforms/wasm/qwasmcursor.cpp b/src/plugins/platforms/wasm/qwasmcursor.cpp
index 54804a55b3..2b3f37300d 100644
--- a/src/plugins/platforms/wasm/qwasmcursor.cpp
+++ b/src/plugins/platforms/wasm/qwasmcursor.cpp
@@ -28,19 +28,21 @@
****************************************************************************/
#include "qwasmcursor.h"
+#include "qwasmscreen.h"
#include <QtCore/qdebug.h>
+#include <QtGui/qwindow.h>
#include <emscripten/emscripten.h>
+#include <emscripten/bind.h>
void QWasmCursor::changeCursor(QCursor *windowCursor, QWindow *window)
{
- if (windowCursor == nullptr)
+ if (!windowCursor || !window)
+ return;
+ QScreen *screen = window->screen();
+ if (!screen)
return;
-
- // FIXME: The HTML5 plugin sets the cursor on the native canvas; when using multiple windows
- // multiple cursors need to be managed taking mouse postion and stacking into account.
- Q_UNUSED(window);
// Bitmap and custom cursors are not implemented (will fall back to "auto")
if (windowCursor->shape() == Qt::BitmapCursor || windowCursor->shape() >= Qt::CustomCursor)
@@ -51,12 +53,10 @@ void QWasmCursor::changeCursor(QCursor *windowCursor, QWindow *window)
if (htmlCursorName.isEmpty())
htmlCursorName = "auto";
- // Set cursor on the main canvas
- EM_ASM_ARGS({
- if (Module['canvas']) {
- Module['canvas'].style['cursor'] = Pointer_stringify($0);
- }
- }, htmlCursorName.constData());
+ // Set cursor on the canvas
+ QString canvasId = QWasmScreen::get(screen)->canvasId();
+ emscripten::val canvasStyle = emscripten::val::global(canvasId.toUtf8().constData())["style"];
+ canvasStyle.set("cursor", emscripten::val(htmlCursorName.constData()));
}
QByteArray QWasmCursor::cursorShapeToHtml(Qt::CursorShape shape)
diff --git a/src/plugins/platforms/wasm/qwasmeventtranslator.cpp b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp
index 8ab109f03c..3895646b89 100644
--- a/src/plugins/platforms/wasm/qwasmeventtranslator.cpp
+++ b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp
@@ -31,6 +31,7 @@
#include "qwasmeventdispatcher.h"
#include "qwasmcompositor.h"
#include "qwasmintegration.h"
+#include "qwasmclipboard.h"
#include <QtGui/qevent.h>
#include <qpa/qwindowsysteminterface.h>
@@ -39,6 +40,9 @@
#include <QtCore/qobject.h>
#include <QtCore/qdeadlinetimer.h>
+#include <private/qmakearray_p.h>
+#include <QtCore/qnamespace.h>
+
#include <emscripten/bind.h>
#include <iostream>
@@ -46,71 +50,331 @@
QT_BEGIN_NAMESPACE
using namespace emscripten;
+typedef struct emkb2qt {
+ const char *em;
+ unsigned int qt;
+
+ constexpr bool operator <=(const emkb2qt &that) const noexcept
+ {
+ return !(strcmp(that) > 0);
+ }
+
+ bool operator <(const emkb2qt &that) const noexcept
+ {
+ return ::strcmp(em, that.em) < 0;
+ }
+ constexpr int strcmp(const emkb2qt &that, const int i = 0) const
+ {
+ return em[i] == 0 && that.em[i] == 0 ? 0
+ : em[i] == 0 ? -1
+ : that.em[i] == 0 ? 1
+ : em[i] < that.em[i] ? -1
+ : em[i] > that.em[i] ? 1
+ : strcmp(that, i + 1);
+ }
+} emkb2qt_t;
+
+template<unsigned int Qt, char ... EmChar>
+struct Emkb2Qt
+{
+ static constexpr const char storage[sizeof ... (EmChar) + 1] = {EmChar..., '\0'};
+ using Type = emkb2qt_t;
+ static constexpr Type data() noexcept { return Type{storage, Qt}; }
+};
+
+template<unsigned int Qt, char ... EmChar> constexpr char Emkb2Qt<Qt, EmChar...>::storage[];
+
+static constexpr const auto KeyTbl = qMakeArray(
+ QSortedData<
+ Emkb2Qt< Qt::Key_Escape, 'E','s','c','a','p','e' >,
+ Emkb2Qt< Qt::Key_Tab, 'T','a','b' >,
+ Emkb2Qt< Qt::Key_Backspace, 'B','a','c','k','s','p','a','c','e' >,
+ Emkb2Qt< Qt::Key_Return, 'E','n','t','e','r' >,
+ Emkb2Qt< Qt::Key_Insert, 'I','n','s','e','r','t' >,
+ Emkb2Qt< Qt::Key_Delete, 'D','e','l','e','t','e' >,
+ Emkb2Qt< Qt::Key_Pause, 'P','a','u','s','e' >,
+ Emkb2Qt< Qt::Key_Pause, 'C','l','e','a','r' >,
+ Emkb2Qt< Qt::Key_Home, 'H','o','m','e' >,
+ Emkb2Qt< Qt::Key_End, 'E','n','d' >,
+ Emkb2Qt< Qt::Key_Left, 'A','r','r','o','w','L','e','f','t' >,
+ Emkb2Qt< Qt::Key_Up, 'A','r','r','o','w','U','p' >,
+ Emkb2Qt< Qt::Key_Right, 'A','r','r','o','w','R','i','g','h','t' >,
+ Emkb2Qt< Qt::Key_Down, 'A','r','r','o','w','D','o','w','n' >,
+ Emkb2Qt< Qt::Key_PageUp, 'P','a','g','e','U','p' >,
+ Emkb2Qt< Qt::Key_PageDown, 'P','a','g','e','D','o','w','n' >,
+ Emkb2Qt< Qt::Key_Shift, 'S','h','i','f','t' >,
+ Emkb2Qt< Qt::Key_Control, 'C','o','n','t','r','o','l' >,
+ Emkb2Qt< Qt::Key_Meta, 'O','S'>,
+ Emkb2Qt< Qt::Key_Alt, 'A','l','t','L','e','f','t' >,
+ Emkb2Qt< Qt::Key_Alt, 'A','l','t' >,
+ Emkb2Qt< Qt::Key_CapsLock, 'C','a','p','s','L','o','c','k' >,
+ Emkb2Qt< Qt::Key_NumLock, 'N','u','m','L','o','c','k' >,
+ Emkb2Qt< Qt::Key_ScrollLock, 'S','c','r','o','l','l','L','o','c','k' >,
+ Emkb2Qt< Qt::Key_F1, 'F','1' >,
+ Emkb2Qt< Qt::Key_F2, 'F','2' >,
+ Emkb2Qt< Qt::Key_F3, 'F','3' >,
+ Emkb2Qt< Qt::Key_F4, 'F','4' >,
+ Emkb2Qt< Qt::Key_F5, 'F','5' >,
+ Emkb2Qt< Qt::Key_F6, 'F','6' >,
+ Emkb2Qt< Qt::Key_F7, 'F','7' >,
+ Emkb2Qt< Qt::Key_F8, 'F','8' >,
+ Emkb2Qt< Qt::Key_F9, 'F','9' >,
+ Emkb2Qt< Qt::Key_F10, 'F','1','0' >,
+ Emkb2Qt< Qt::Key_F11, 'F','1','1' >,
+ Emkb2Qt< Qt::Key_F12, 'F','1','2' >,
+ Emkb2Qt< Qt::Key_F13, 'F','1','3' >,
+ Emkb2Qt< Qt::Key_F14, 'F','1','4' >,
+ Emkb2Qt< Qt::Key_F15, 'F','1','5' >,
+ Emkb2Qt< Qt::Key_F16, 'F','1','6' >,
+ Emkb2Qt< Qt::Key_F17, 'F','1','7' >,
+ Emkb2Qt< Qt::Key_F18, 'F','1','8' >,
+ Emkb2Qt< Qt::Key_F19, 'F','1','9' >,
+ Emkb2Qt< Qt::Key_F20, 'F','2','0' >,
+ Emkb2Qt< Qt::Key_F21, 'F','2','1' >,
+ Emkb2Qt< Qt::Key_F22, 'F','2','2' >,
+ Emkb2Qt< Qt::Key_F23, 'F','2','3' >,
+ Emkb2Qt< Qt::Key_Space, ' ' >,
+ Emkb2Qt< Qt::Key_Comma, ',' >,
+ Emkb2Qt< Qt::Key_Minus, '-' >,
+ Emkb2Qt< Qt::Key_Period, '.' >,
+ Emkb2Qt< Qt::Key_Slash, '/' >,
+ Emkb2Qt< Qt::Key_0, '0' >,
+ Emkb2Qt< Qt::Key_1, '1' >,
+ Emkb2Qt< Qt::Key_2, '2' >,
+ Emkb2Qt< Qt::Key_3, '3' >,
+ Emkb2Qt< Qt::Key_4, '4' >,
+ Emkb2Qt< Qt::Key_5, '5' >,
+ Emkb2Qt< Qt::Key_6, '6' >,
+ Emkb2Qt< Qt::Key_7, '7' >,
+ Emkb2Qt< Qt::Key_8, '8' >,
+ Emkb2Qt< Qt::Key_9, '9' >,
+ Emkb2Qt< Qt::Key_Semicolon, ';' >,
+ Emkb2Qt< Qt::Key_Equal, '=' >,
+ Emkb2Qt< Qt::Key_A, 'K','e','y','A' >,
+ Emkb2Qt< Qt::Key_B, 'K','e','y','B' >,
+ Emkb2Qt< Qt::Key_C, 'K','e','y','C' >,
+ Emkb2Qt< Qt::Key_D, 'K','e','y','D' >,
+ Emkb2Qt< Qt::Key_E, 'K','e','y','E' >,
+ Emkb2Qt< Qt::Key_F, 'K','e','y','F' >,
+ Emkb2Qt< Qt::Key_G, 'K','e','y','G' >,
+ Emkb2Qt< Qt::Key_H, 'K','e','y','H' >,
+ Emkb2Qt< Qt::Key_I, 'K','e','y','I' >,
+ Emkb2Qt< Qt::Key_J, 'K','e','y','J' >,
+ Emkb2Qt< Qt::Key_K, 'K','e','y','K' >,
+ Emkb2Qt< Qt::Key_L, 'K','e','y','L' >,
+ Emkb2Qt< Qt::Key_M, 'K','e','y','M' >,
+ Emkb2Qt< Qt::Key_N, 'K','e','y','N' >,
+ Emkb2Qt< Qt::Key_O, 'K','e','y','O' >,
+ Emkb2Qt< Qt::Key_P, 'K','e','y','P' >,
+ Emkb2Qt< Qt::Key_Q, 'K','e','y','Q' >,
+ Emkb2Qt< Qt::Key_R, 'K','e','y','R' >,
+ Emkb2Qt< Qt::Key_S, 'K','e','y','S' >,
+ Emkb2Qt< Qt::Key_T, 'K','e','y','T' >,
+ Emkb2Qt< Qt::Key_U, 'K','e','y','U' >,
+ Emkb2Qt< Qt::Key_V, 'K','e','y','V' >,
+ Emkb2Qt< Qt::Key_W, 'K','e','y','W' >,
+ Emkb2Qt< Qt::Key_X, 'K','e','y','X' >,
+ Emkb2Qt< Qt::Key_Y, 'K','e','y','Y' >,
+ Emkb2Qt< Qt::Key_Z, 'K','e','y','Z' >,
+ Emkb2Qt< Qt::Key_BracketLeft, '[' >,
+ Emkb2Qt< Qt::Key_Backslash, '\\' >,
+ Emkb2Qt< Qt::Key_BracketRight, ']' >,
+ Emkb2Qt< Qt::Key_Apostrophe, '\'' >,
+ Emkb2Qt< Qt::Key_QuoteLeft, 'B','a','c','k','q','u','o','t','e' >,
+ Emkb2Qt< Qt::Key_multiply, 'N','u','m','p','a','d','M','u','l','t','i','p','l','y' >,
+ Emkb2Qt< Qt::Key_Minus, 'N','u','m','p','a','d','S','u','b','t','r','a','c','t' >,
+ Emkb2Qt< Qt::Key_Period, 'N','u','m','p','a','d','D','e','c','i','m','a','l' >,
+ Emkb2Qt< Qt::Key_Plus, 'N','u','m','p','a','d','A','d','d' >,
+ Emkb2Qt< Qt::Key_division, 'N','u','m','p','a','d','D','i','v','i','d','e' >,
+ Emkb2Qt< Qt::Key_Equal, 'N','u','m','p','a','d','E','q','u','a','l' >,
+ Emkb2Qt< Qt::Key_0, 'N','u','m','p','a','d','0' >,
+ Emkb2Qt< Qt::Key_1, 'N','u','m','p','a','d','1' >,
+ Emkb2Qt< Qt::Key_2, 'N','u','m','p','a','d','2' >,
+ Emkb2Qt< Qt::Key_3, 'N','u','m','p','a','d','3' >,
+ Emkb2Qt< Qt::Key_4, 'N','u','m','p','a','d','4' >,
+ Emkb2Qt< Qt::Key_5, 'N','u','m','p','a','d','5' >,
+ Emkb2Qt< Qt::Key_6, 'N','u','m','p','a','d','6' >,
+ Emkb2Qt< Qt::Key_7, 'N','u','m','p','a','d','7' >,
+ Emkb2Qt< Qt::Key_8, 'N','u','m','p','a','d','8' >,
+ Emkb2Qt< Qt::Key_9, 'N','u','m','p','a','d','9' >,
+ Emkb2Qt< Qt::Key_Comma, 'N','u','m','p','a','d','C','o','m','m','a' >,
+ Emkb2Qt< Qt::Key_Enter, 'N','u','m','p','a','d','E','n','t','e','r' >,
+ Emkb2Qt< Qt::Key_Paste, 'P','a','s','t','e' >,
+ Emkb2Qt< Qt::Key_AltGr, 'A','l','t','R','i','g','h','t' >,
+ Emkb2Qt< Qt::Key_Help, 'H','e','l','p' >,
+ Emkb2Qt< Qt::Key_Equal, '=' >,
+ Emkb2Qt< Qt::Key_yen, 'I','n','t','l','Y','e','n' >,
+
+ Emkb2Qt< Qt::Key_Exclam, '\x21' >,
+ Emkb2Qt< Qt::Key_QuoteDbl, '\x22' >,
+ Emkb2Qt< Qt::Key_NumberSign, '\x23' >,
+ Emkb2Qt< Qt::Key_Dollar, '\x24' >,
+ Emkb2Qt< Qt::Key_Percent, '\x25' >,
+ Emkb2Qt< Qt::Key_Ampersand, '\x26' >,
+ Emkb2Qt< Qt::Key_ParenLeft, '\x28' >,
+ Emkb2Qt< Qt::Key_ParenRight, '\x29' >,
+ Emkb2Qt< Qt::Key_Asterisk, '\x2a' >,
+ Emkb2Qt< Qt::Key_Plus, '\x2b' >,
+ Emkb2Qt< Qt::Key_Colon, '\x3a' >,
+ Emkb2Qt< Qt::Key_Semicolon, '\x3b' >,
+ Emkb2Qt< Qt::Key_Less, '\x3c' >,
+ Emkb2Qt< Qt::Key_Equal, '\x3d' >,
+ Emkb2Qt< Qt::Key_Greater, '\x3e' >,
+ Emkb2Qt< Qt::Key_Question, '\x3f' >,
+ Emkb2Qt< Qt::Key_At, '\x40' >,
+ Emkb2Qt< Qt::Key_BracketLeft, '\x5b' >,
+ Emkb2Qt< Qt::Key_Backslash, '\x5c' >,
+ Emkb2Qt< Qt::Key_BracketRight, '\x5d' >,
+ Emkb2Qt< Qt::Key_AsciiCircum, '\x5e' >,
+ Emkb2Qt< Qt::Key_Underscore, '\x5f' >,
+ Emkb2Qt< Qt::Key_QuoteLeft, '\x60'>,
+ Emkb2Qt< Qt::Key_BraceLeft, '\x7b'>,
+ Emkb2Qt< Qt::Key_Bar, '\x7c'>,
+ Emkb2Qt< Qt::Key_BraceRight, '\x7d'>,
+ Emkb2Qt< Qt::Key_AsciiTilde, '\x7e'>,
+ Emkb2Qt< Qt::Key_Space, '\x20' >,
+ Emkb2Qt< Qt::Key_Comma, '\x2c' >,
+ Emkb2Qt< Qt::Key_Minus, '\x2d' >,
+ Emkb2Qt< Qt::Key_Period, '\x2e' >,
+ Emkb2Qt< Qt::Key_Slash, '\x2f' >,
+ Emkb2Qt< Qt::Key_Apostrophe, '\x27' >,
+ Emkb2Qt< Qt::Key_Menu, 'C','o','n','t','e','x','t','M','e','n','u' >,
+
+ Emkb2Qt< Qt::Key_Agrave, '\xc3','\xa0' >,
+ Emkb2Qt< Qt::Key_Aacute, '\xc3','\xa1' >,
+ Emkb2Qt< Qt::Key_Acircumflex, '\xc3','\xa2' >,
+ Emkb2Qt< Qt::Key_Adiaeresis, '\xc3','\xa4' >,
+ Emkb2Qt< Qt::Key_AE, '\xc3','\xa6' >,
+ Emkb2Qt< Qt::Key_Atilde, '\xc3','\xa3' >,
+ Emkb2Qt< Qt::Key_Aring, '\xc3','\xa5' >,
+ Emkb2Qt< Qt::Key_Ccedilla, '\xc3','\xa7' >,
+ Emkb2Qt< Qt::Key_Egrave, '\xc3','\xa8' >,
+ Emkb2Qt< Qt::Key_Eacute, '\xc3','\xa9' >,
+ Emkb2Qt< Qt::Key_Ecircumflex, '\xc3','\xaa' >,
+ Emkb2Qt< Qt::Key_Ediaeresis, '\xc3','\xab' >,
+ Emkb2Qt< Qt::Key_Icircumflex, '\xc3','\xae' >,
+ Emkb2Qt< Qt::Key_Idiaeresis, '\xc3','\xaf' >,
+ Emkb2Qt< Qt::Key_Ocircumflex, '\xc3','\xb4' >,
+ Emkb2Qt< Qt::Key_Odiaeresis, '\xc3','\xb6' >,
+ Emkb2Qt< Qt::Key_Ograve, '\xc3','\xb2' >,
+ Emkb2Qt< Qt::Key_Oacute, '\xc3','\xb3' >,
+ Emkb2Qt< Qt::Key_Ooblique, '\xc3','\xb8' >,
+ Emkb2Qt< Qt::Key_Otilde, '\xc3','\xb5' >,
+ Emkb2Qt< Qt::Key_Ucircumflex, '\xc3','\xbb' >,
+ Emkb2Qt< Qt::Key_Udiaeresis, '\xc3','\xbc' >,
+ Emkb2Qt< Qt::Key_Ugrave, '\xc3','\xb9' >,
+ Emkb2Qt< Qt::Key_Uacute, '\xc3','\xba' >,
+ Emkb2Qt< Qt::Key_Ntilde, '\xc3','\xb1' >,
+ Emkb2Qt< Qt::Key_ydiaeresis, '\xc3','\xbf' >
+ >::Data{}
+ );
+
+static constexpr const auto DeadKeyShiftTbl = qMakeArray(
+ QSortedData<
+ // shifted
+ Emkb2Qt< Qt::Key_Agrave, '\xc3','\x80' >,
+ Emkb2Qt< Qt::Key_Aacute, '\xc3','\x81' >,
+ Emkb2Qt< Qt::Key_Acircumflex, '\xc3','\x82' >,
+ Emkb2Qt< Qt::Key_Adiaeresis, '\xc3','\x84' >,
+ Emkb2Qt< Qt::Key_AE, '\xc3','\x86' >,
+ Emkb2Qt< Qt::Key_Atilde, '\xc3','\x83' >,
+ Emkb2Qt< Qt::Key_Aring, '\xc3','\x85' >,
+ Emkb2Qt< Qt::Key_Egrave, '\xc3','\x88' >,
+ Emkb2Qt< Qt::Key_Eacute, '\xc3','\x89' >,
+ Emkb2Qt< Qt::Key_Ecircumflex, '\xc3','\x8a' >,
+ Emkb2Qt< Qt::Key_Ediaeresis, '\xc3','\x8b' >,
+ Emkb2Qt< Qt::Key_Icircumflex, '\xc3','\x8e' >,
+ Emkb2Qt< Qt::Key_Idiaeresis, '\xc3','\x8f' >,
+ Emkb2Qt< Qt::Key_Ocircumflex, '\xc3','\x94' >,
+ Emkb2Qt< Qt::Key_Odiaeresis, '\xc3','\x96' >,
+ Emkb2Qt< Qt::Key_Ograve, '\xc3','\x92' >,
+ Emkb2Qt< Qt::Key_Oacute, '\xc3','\x93' >,
+ Emkb2Qt< Qt::Key_Ooblique, '\xc3','\x98' >,
+ Emkb2Qt< Qt::Key_Otilde, '\xc3','\x95' >,
+ Emkb2Qt< Qt::Key_Ucircumflex, '\xc3','\x9b' >,
+ Emkb2Qt< Qt::Key_Udiaeresis, '\xc3','\x9c' >,
+ Emkb2Qt< Qt::Key_Ugrave, '\xc3','\x99' >,
+ Emkb2Qt< Qt::Key_Uacute, '\xc3','\x9a' >,
+ Emkb2Qt< Qt::Key_Ntilde, '\xc3','\x91' >,
+ Emkb2Qt< Qt::Key_Ccedilla, '\xc3','\x87' >,
+ Emkb2Qt< Qt::Key_ydiaeresis, '\xc3','\x8f' >
+ >::Data{}
+);
+
// macOS CTRL <-> META switching. We most likely want to enable
// the existing switching code in QtGui, but for now do it here.
static bool g_usePlatformMacCtrlMetaSwitching = false;
bool g_useNaturalScrolling = true; // natural scrolling is default on linux/windows
-void setNaturalScrolling(bool use) {
- g_useNaturalScrolling = use;
+static void mouseWheelEvent(emscripten::val event) {
+
+ emscripten::val wheelInterted = event["webkitDirectionInvertedFromDevice"];
+
+ if (wheelInterted.as<bool>()) {
+ g_useNaturalScrolling = true;
+ }
}
-EMSCRIPTEN_BINDINGS(mouse_module) {
- function("setNaturalScrolling", &setNaturalScrolling);
+EMSCRIPTEN_BINDINGS(qtMouseModule) {
+ function("qtMouseWheelEvent", &mouseWheelEvent);
}
-QWasmEventTranslator::QWasmEventTranslator(QObject *parent)
- : QObject(parent)
+QWasmEventTranslator::QWasmEventTranslator(QWasmScreen *screen)
+ : QObject(screen)
, draggedWindow(nullptr)
, lastWindow(nullptr)
, pressedButtons(Qt::NoButton)
, resizeMode(QWasmWindow::ResizeNone)
{
- emscripten_set_keydown_callback(0, (void *)this, 1, &keyboard_cb);
- emscripten_set_keyup_callback(0, (void *)this, 1, &keyboard_cb);
-
- emscripten_set_mousedown_callback(0, (void *)this, 1, &mouse_cb);
- emscripten_set_mouseup_callback(0, (void *)this, 1, &mouse_cb);
- emscripten_set_mousemove_callback(0, (void *)this, 1, &mouse_cb);
-
- emscripten_set_focus_callback(0, (void *)this, 1, &focus_cb);
-
- emscripten_set_wheel_callback(0, (void *)this, 1, &wheel_cb);
-
touchDevice = new QTouchDevice;
touchDevice->setType(QTouchDevice::TouchScreen);
touchDevice->setCapabilities(QTouchDevice::Position | QTouchDevice::Area | QTouchDevice::NormalizedPosition);
QWindowSystemInterface::registerTouchDevice(touchDevice);
- emscripten_set_touchstart_callback("#canvas", (void *)this, 1, &touchCallback);
- emscripten_set_touchend_callback("#canvas", (void *)this, 1, &touchCallback);
- emscripten_set_touchmove_callback("#canvas", (void *)this, 1, &touchCallback);
- emscripten_set_touchcancel_callback("#canvas", (void *)this, 1, &touchCallback);
+ initEventHandlers();
+}
+
+void QWasmEventTranslator::initEventHandlers()
+{
+ QByteArray _canvasId = screen()->canvasId().toUtf8();
+ const char *canvasId = _canvasId.constData();
// The Platform Detect: expand coverage and move as needed
enum Platform {
GenericPlatform,
MacOSPlatform
};
- Platform platform =
- Platform(EM_ASM_INT("if (navigator.platform.includes(\"Mac\")) return 1; return 0;"));
-
+ Platform platform = Platform(emscripten::val::global("navigator")["platform"]
+ .call<bool>("includes", emscripten::val("Mac")));
g_usePlatformMacCtrlMetaSwitching = (platform == MacOSPlatform);
if (platform == MacOSPlatform) {
g_useNaturalScrolling = false; // make this !default on macOS
- EM_ASM(
- if (window.safari !== undefined) {//this only works on safari
- Module["canvas"].addEventListener('wheel', mouseWheelEvent);
- function mouseWheelEvent(e) {
- if (event.webkitDirectionInvertedFromDevice) {
- Module.setNaturalScrolling(event.webkitDirectionInvertedFromDevice);
- }
- }
- }
- );
+
+ if (emscripten::val::global("window")["safari"].isUndefined()) {
+
+ emscripten::val::global(canvasId).call<void>("addEventListener",
+ std::string("wheel"),
+ val::module_property("qtMouseWheelEvent"));
+ }
}
+
+ emscripten_set_keydown_callback(canvasId, (void *)this, 1, &keyboard_cb);
+ emscripten_set_keyup_callback(canvasId, (void *)this, 1, &keyboard_cb);
+
+ emscripten_set_mousedown_callback(canvasId, (void *)this, 1, &mouse_cb);
+ emscripten_set_mouseup_callback(canvasId, (void *)this, 1, &mouse_cb);
+ emscripten_set_mousemove_callback(canvasId, (void *)this, 1, &mouse_cb);
+
+ emscripten_set_focus_callback(canvasId, (void *)this, 1, &focus_cb);
+
+ emscripten_set_wheel_callback(canvasId, (void *)this, 1, &wheel_cb);
+
+ emscripten_set_touchstart_callback(canvasId, (void *)this, 1, &touchCallback);
+ emscripten_set_touchend_callback(canvasId, (void *)this, 1, &touchCallback);
+ emscripten_set_touchmove_callback(canvasId, (void *)this, 1, &touchCallback);
+ emscripten_set_touchcancel_callback(canvasId, (void *)this, 1, &touchCallback);
}
template <typename Event>
@@ -153,132 +417,49 @@ QFlags<Qt::KeyboardModifier> QWasmEventTranslator::translateMouseEventModifier(c
int QWasmEventTranslator::keyboard_cb(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData)
{
- Q_UNUSED(userData)
-
- bool alphanumeric;
- Qt::Key qtKey = translateEmscriptKey(keyEvent, &alphanumeric);
+ QWasmEventTranslator *wasmTranslator = reinterpret_cast<QWasmEventTranslator *>(userData);
+ bool accepted = wasmTranslator->processKeyboard(eventType, keyEvent);
- QEvent::Type keyType = QEvent::None;
- switch (eventType) {
- case EMSCRIPTEN_EVENT_KEYPRESS:
- case EMSCRIPTEN_EVENT_KEYDOWN: //down
- keyType = QEvent::KeyPress;
- break;
- case EMSCRIPTEN_EVENT_KEYUP: //up
- keyType = QEvent::KeyRelease;
- break;
- default:
- break;
- };
-
- if (keyType == QEvent::None)
- return 0;
-
- QString keyText = alphanumeric ? QString(keyEvent->key) : QString();
- bool accepted = QWindowSystemInterface::handleKeyEvent<QWindowSystemInterface::SynchronousDelivery>(
- 0, keyType, qtKey, translateKeyboardEventModifier(keyEvent), keyText);
- QWasmEventDispatcher::maintainTimers();
return accepted ? 1 : 0;
}
-Qt::Key QWasmEventTranslator::translateEmscriptKey(const EmscriptenKeyboardEvent *emscriptKey, bool *outAlphanumeric)
+QWasmScreen *QWasmEventTranslator::screen()
{
- Qt::Key qtKey;
- if (outAlphanumeric)
- *outAlphanumeric = false;
-
- switch (emscriptKey->keyCode) {
- case KeyMultiply: qtKey = Qt::Key_Asterisk; *outAlphanumeric = true; break;
- case KeyAdd: qtKey = Qt::Key_Plus; *outAlphanumeric = true; break;
- case KeyMinus: qtKey = Qt::Key_Minus; *outAlphanumeric = true; break;
- case KeySubtract: qtKey = Qt::Key_Minus; *outAlphanumeric = true; break;
- case KeyDecimal: qtKey = Qt::Key_Period; *outAlphanumeric = true; break;
- case KeyDivide: qtKey = Qt::Key_Slash; *outAlphanumeric = true; break;
- case KeyNumPad0: qtKey = Qt::Key_0; *outAlphanumeric = true; break;
- case KeyNumPad1: qtKey = Qt::Key_1; *outAlphanumeric = true; break;
- case KeyNumPad2: qtKey = Qt::Key_2; *outAlphanumeric = true; break;
- case KeyNumPad3: qtKey = Qt::Key_3; *outAlphanumeric = true; break;
- case KeyNumPad4: qtKey = Qt::Key_4; *outAlphanumeric = true; break;
- case KeyNumPad5: qtKey = Qt::Key_5; *outAlphanumeric = true; break;
- case KeyNumPad6: qtKey = Qt::Key_6; *outAlphanumeric = true; break;
- case KeyNumPad7: qtKey = Qt::Key_7; *outAlphanumeric = true; break;
- case KeyNumPad8: qtKey = Qt::Key_8; *outAlphanumeric = true; break;
- case KeyNumPad9: qtKey = Qt::Key_9; *outAlphanumeric = true; break;
- case KeyComma: qtKey = Qt::Key_Comma; *outAlphanumeric = true; break;
- case KeyPeriod: qtKey = Qt::Key_Period; *outAlphanumeric = true; break;
- case KeySlash: qtKey = Qt::Key_Slash; *outAlphanumeric = true; break;
- case KeySemiColon: qtKey = Qt::Key_Semicolon; *outAlphanumeric = true; break;
- case KeyEquals: qtKey = Qt::Key_Equal; *outAlphanumeric = true; break;
- case KeyOpenBracket: qtKey = Qt::Key_BracketLeft; *outAlphanumeric = true; break;
- case KeyCloseBracket: qtKey = Qt::Key_BracketRight; *outAlphanumeric = true; break;
- case KeyBackSlash: qtKey = Qt::Key_Backslash; *outAlphanumeric = true; break;
- case KeyMeta:
- Q_FALLTHROUGH();
- case KeyMetaRight:
- qtKey = Qt::Key_Meta;
- break;
- case KeyTab: qtKey = Qt::Key_Tab; break;
- case KeyClear: qtKey = Qt::Key_Clear; break;
- case KeyBackSpace: qtKey = Qt::Key_Backspace; break;
- case KeyEnter: qtKey = Qt::Key_Return; break;
- case KeyShift: qtKey = Qt::Key_Shift; break;
- case KeyControl: qtKey = Qt::Key_Control; break;
- case KeyAlt: qtKey = Qt::Key_Alt; break;
- case KeyCapsLock: qtKey = Qt::Key_CapsLock; break;
- case KeyEscape: qtKey = Qt::Key_Escape; break;
- case KeyPageUp: qtKey = Qt::Key_PageUp; break;
- case KeyPageDown: qtKey = Qt::Key_PageDown; break;
- case KeyEnd: qtKey = Qt::Key_End; break;
- case KeyHome: qtKey = Qt::Key_Home; break;
- case KeyLeft: qtKey = Qt::Key_Left; break;
- case KeyUp: qtKey = Qt::Key_Up; break;
- case KeyRight: qtKey = Qt::Key_Right; break;
- case KeyDown: qtKey = Qt::Key_Down; break;
- case KeyBrightnessDown: qtKey = Qt::Key_MonBrightnessDown; break;
- case KeyBrightnessUp: qtKey = Qt::Key_MonBrightnessUp; break;
- case KeyMediaTrackPrevious: qtKey = Qt::Key_MediaPrevious; break;
- case KeyMediaPlayPause: qtKey = Qt::Key_MediaTogglePlayPause; break;
- case KeyMediaTrackNext: qtKey = Qt::Key_MediaNext; break;
- case KeyAudioVolumeMute: qtKey = Qt::Key_VolumeMute; break;
- case KeyAudioVolumeDown: qtKey = Qt::Key_VolumeDown; break;
- case KeyAudioVolumeUp: qtKey = Qt::Key_VolumeUp; break;
- case KeyDelete: qtKey = Qt::Key_Delete; break;
-
- case KeyF1: qtKey = Qt::Key_F1; break;
- case KeyF2: qtKey = Qt::Key_F2; break;
- case KeyF3: qtKey = Qt::Key_F3; break;
- case KeyF4: qtKey = Qt::Key_F4; break;
- case KeyF5: qtKey = Qt::Key_F5; break;
- case KeyF6: qtKey = Qt::Key_F6; break;
- case KeyF7: qtKey = Qt::Key_F7; break;
- case KeyF8: qtKey = Qt::Key_F8; break;
- case KeyF9: qtKey = Qt::Key_F9; break;
- case KeyF10: qtKey = Qt::Key_F10; break;
- case KeyF11: qtKey = Qt::Key_F11; break;
- case KeyF12: qtKey = Qt::Key_F12; break;
- case 124: qtKey = Qt::Key_F13; break;
- case 125: qtKey = Qt::Key_F14; break;
-
- case KeySpace:
- default:
- if (outAlphanumeric)
- *outAlphanumeric = true;
- qtKey = static_cast<Qt::Key>(emscriptKey->keyCode);
- break;
- }
+ return static_cast<QWasmScreen *>(parent());
+}
+
+Qt::Key QWasmEventTranslator::translateEmscriptKey(const EmscriptenKeyboardEvent *emscriptKey)
+{
+ Qt::Key qtKey = Qt::Key_unknown;
+
+ if (qstrncmp(emscriptKey->code, "Key", 3) == 0 || qstrncmp(emscriptKey->code, "Numpad", 6) == 0) {
- // Handle Mac command key. Using event->keyCode as above is
- // no reliable since the codes differ between browsers.
- if (qstrncmp(emscriptKey->key, "Meta", 4) == 0) {
- qtKey = Qt::Key_Meta;
- *outAlphanumeric = false;
+ emkb2qt_t searchKey{emscriptKey->code, 0}; // search emcsripten code
+ auto it1 = std::lower_bound(KeyTbl.cbegin(), KeyTbl.cend(), searchKey);
+ if (it1 != KeyTbl.end() && !(searchKey < *it1)) {
+ qtKey = static_cast<Qt::Key>(it1->qt);
+ }
+ } else if (qstrncmp(emscriptKey->key, "Dead", 4) == 0 ) {
+ emkb2qt_t searchKey1{emscriptKey->code, 0};
+ for (auto it1 = KeyTbl.cbegin(); it1 != KeyTbl.end(); ++it1)
+ if (it1 != KeyTbl.end() && (qstrcmp(searchKey1.em, it1->em) == 0)) {
+ qtKey = static_cast<Qt::Key>(it1->qt);
+ }
}
- if (g_usePlatformMacCtrlMetaSwitching) {
- if (qtKey == Qt::Key_Meta)
- qtKey = Qt::Key_Control;
- else if (qtKey == Qt::Key_Control)
- qtKey = Qt::Key_Meta;
+ if (qtKey == Qt::Key_unknown) {
+ emkb2qt_t searchKey{emscriptKey->key, 0}; // search unicode key
+ auto it1 = std::lower_bound(KeyTbl.cbegin(), KeyTbl.cend(), searchKey);
+ if (it1 != KeyTbl.end() && !(searchKey < *it1)) {
+ qtKey = static_cast<Qt::Key>(it1->qt);
+ }
+ }
+ if (qtKey == Qt::Key_unknown) {//try harder with shifted number keys
+ emkb2qt_t searchKey1{emscriptKey->key, 0};
+ for (auto it1 = KeyTbl.cbegin(); it1 != KeyTbl.end(); ++it1)
+ if (it1 != KeyTbl.end() && (qstrcmp(searchKey1.em, it1->em) == 0)) {
+ qtKey = static_cast<Qt::Key>(it1->qt);
+ }
}
return qtKey;
@@ -363,27 +544,30 @@ void resizeWindow(QWindow *window, QWasmWindow::ResizeMode mode,
void QWasmEventTranslator::processMouse(int eventType, const EmscriptenMouseEvent *mouseEvent)
{
auto timestamp = mouseEvent->timestamp;
- QPoint point(mouseEvent->canvasX, mouseEvent->canvasY);
+ QPoint targetPoint(mouseEvent->targetX, mouseEvent->targetY);
+ QPoint globalPoint = screen()->geometry().topLeft() + targetPoint;
QEvent::Type buttonEventType = QEvent::None;
-
Qt::MouseButton button = translateMouseButton(mouseEvent->button);
Qt::KeyboardModifiers modifiers = translateMouseEventModifier(mouseEvent);
- QWindow *window2 = QWasmIntegration::get()->compositor()->windowAt(point, 5);
- if (window2 != nullptr)
- lastWindow = window2;
+ QWindow *window2 = screen()->compositor()->windowAt(globalPoint, 5);
- QWasmWindow *htmlWindow = static_cast<QWasmWindow*>(window2->handle());
+ if (window2 == nullptr) {
+ window2 = lastWindow;
+ } else {
+ lastWindow = window2;
+ }
- bool interior = window2 && window2->geometry().contains(point);
+ QPoint localPoint = window2->mapFromGlobal(globalPoint);
+ bool interior = window2->geometry().contains(globalPoint);
- QPoint localPoint(point.x() - window2->geometry().x(), point.y() - window2->geometry().y());
+ QWasmWindow *htmlWindow = static_cast<QWasmWindow*>(window2->handle());
switch (eventType) {
case EMSCRIPTEN_EVENT_MOUSEDOWN:
{
if (window2)
- window2->raise();
+ window2->requestActivate();
pressedButtons.setFlag(button);
@@ -391,18 +575,18 @@ void QWasmEventTranslator::processMouse(int eventType, const EmscriptenMouseEven
pressedWindow = window2;
buttonEventType = QEvent::MouseButtonPress;
if (!(htmlWindow->m_windowState & Qt::WindowFullScreen) && !(htmlWindow->m_windowState & Qt::WindowMaximized)) {
- if (htmlWindow && window2->flags().testFlag(Qt::WindowTitleHint) && htmlWindow->isPointOnTitle(point))
+ if (htmlWindow && window2->flags().testFlag(Qt::WindowTitleHint) && htmlWindow->isPointOnTitle(globalPoint))
draggedWindow = window2;
- else if (htmlWindow && htmlWindow->isPointOnResizeRegion(point)) {
+ else if (htmlWindow && htmlWindow->isPointOnResizeRegion(globalPoint)) {
draggedWindow = window2;
- resizeMode = htmlWindow->resizeModeAtPoint(point);
- resizePoint = point;
+ resizeMode = htmlWindow->resizeModeAtPoint(globalPoint);
+ resizePoint = globalPoint;
resizeStartRect = window2->geometry();
}
}
}
- htmlWindow->injectMousePressed(localPoint, point, button, modifiers);
+ htmlWindow->injectMousePressed(localPoint, globalPoint, button, modifiers);
break;
}
case EMSCRIPTEN_EVENT_MOUSEUP:
@@ -422,7 +606,7 @@ void QWasmEventTranslator::processMouse(int eventType, const EmscriptenMouseEven
}
if (oldWindow)
- oldWindow->injectMouseReleased(localPoint, point, button, modifiers);
+ oldWindow->injectMouseReleased(localPoint, globalPoint, button, modifiers);
break;
}
case EMSCRIPTEN_EVENT_MOUSEMOVE: // drag event
@@ -435,7 +619,7 @@ void QWasmEventTranslator::processMouse(int eventType, const EmscriptenMouseEven
}
if (resizeMode != QWasmWindow::ResizeNone && !(htmlWindow->m_windowState & Qt::WindowFullScreen)) {
- QPoint delta = QPoint(mouseEvent->canvasX, mouseEvent->canvasY) - resizePoint;
+ QPoint delta = QPoint(mouseEvent->targetX, mouseEvent->targetY) - resizePoint;
resizeWindow(draggedWindow, resizeMode, resizeStartRect, delta);
}
}
@@ -451,7 +635,7 @@ void QWasmEventTranslator::processMouse(int eventType, const EmscriptenMouseEven
}
if (window2 && interior) {
QWindowSystemInterface::handleMouseEvent<QWindowSystemInterface::SynchronousDelivery>(
- window2, timestamp, localPoint, point, pressedButtons, button, buttonEventType, modifiers);
+ window2, timestamp, localPoint, globalPoint, pressedButtons, button, buttonEventType, modifiers);
}
}
@@ -463,8 +647,8 @@ int QWasmEventTranslator::focus_cb(int /*eventType*/, const EmscriptenFocusEvent
int QWasmEventTranslator::wheel_cb(int eventType, const EmscriptenWheelEvent *wheelEvent, void *userData)
{
Q_UNUSED(eventType)
- Q_UNUSED(userData)
+ QWasmEventTranslator *eventTranslator = static_cast<QWasmEventTranslator *>(userData);
EmscriptenMouseEvent mouseEvent = wheelEvent->mouse;
int scrollFactor = 0;
@@ -483,21 +667,24 @@ int QWasmEventTranslator::wheel_cb(int eventType, const EmscriptenWheelEvent *wh
if (g_useNaturalScrolling) //macOS platform has document oriented scrolling
scrollFactor = -scrollFactor;
- Qt::KeyboardModifiers modifiers = translateMouseEventModifier(&mouseEvent);
+ QWasmEventTranslator *translator = (QWasmEventTranslator*)userData;
+ Qt::KeyboardModifiers modifiers = translator->translateMouseEventModifier(&mouseEvent);
auto timestamp = mouseEvent.timestamp;
- QPoint globalPoint(mouseEvent.canvasX, mouseEvent.canvasY);
-
- QWindow *window2 = QWasmIntegration::get()->compositor()->windowAt(globalPoint, 5);
+ QPoint targetPoint(mouseEvent.targetX, mouseEvent.targetY);
+ QPoint globalPoint = eventTranslator->screen()->geometry().topLeft() + targetPoint;
- QPoint localPoint(globalPoint.x() - window2->geometry().x(), globalPoint.y() - window2->geometry().y());
+ QWindow *window2 = eventTranslator->screen()->compositor()->windowAt(globalPoint, 5);
+ if (!window2)
+ return 0;
+ QPoint localPoint = window2->mapFromGlobal(globalPoint);
QPoint pixelDelta;
if (wheelEvent->deltaY != 0) pixelDelta.setY(wheelEvent->deltaY * scrollFactor);
if (wheelEvent->deltaX != 0) pixelDelta.setX(wheelEvent->deltaX * scrollFactor);
- QWindowSystemInterface::handleWheelEvent(window2, timestamp, localPoint, globalPoint, QPoint(), pixelDelta, modifiers);
-
+ QWindowSystemInterface::handleWheelEvent(window2, timestamp, localPoint,
+ globalPoint, QPoint(), pixelDelta, modifiers);
QWasmEventDispatcher::maintainTimers();
return 1;
@@ -505,6 +692,12 @@ int QWasmEventTranslator::wheel_cb(int eventType, const EmscriptenWheelEvent *wh
int QWasmEventTranslator::touchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData)
{
+ auto translator = reinterpret_cast<QWasmEventTranslator*>(userData);
+ return translator->handleTouch(eventType, touchEvent);
+}
+
+int QWasmEventTranslator::handleTouch(int eventType, const EmscriptenTouchEvent *touchEvent)
+{
QList<QWindowSystemInterface::TouchPoint> touchPointList;
touchPointList.reserve(touchEvent->numTouches);
QWindow *window2;
@@ -513,43 +706,69 @@ int QWasmEventTranslator::touchCallback(int eventType, const EmscriptenTouchEven
const EmscriptenTouchPoint *touches = &touchEvent->touches[i];
- QPoint point(touches->canvasX, touches->canvasY);
- window2 = QWasmIntegration::get()->compositor()->windowAt(point, 5);
+ QPoint targetPoint(touches->targetX, touches->targetY);
+ QPoint globalPoint = screen()->geometry().topLeft() + targetPoint;
+
+ window2 = this->screen()->compositor()->windowAt(globalPoint, 5);
+ if (window2 == nullptr)
+ continue;
QWindowSystemInterface::TouchPoint touchPoint;
- auto cX = point.x();
- auto cY = point.y();
touchPoint.area = QRect(0, 0, 8, 8);
- touchPoint.area.moveCenter(QPointF(cX,cY)); // simulate area
-
touchPoint.id = touches->identifier;
- touchPoint.normalPosition = QPointF(cX / window2->width(), cY / window2->height());
+ touchPoint.pressure = 1.0;
+
+ touchPoint.area.moveCenter(globalPoint);
+
+ const auto tp = pressedTouchIds.constFind(touchPoint.id);
+ if (tp != pressedTouchIds.constEnd())
+ touchPoint.normalPosition = tp.value();
+
+ QPointF localPoint = QPointF(window2->mapFromGlobal(globalPoint));
+ QPointF normalPosition(localPoint.x() / window2->width(),
+ localPoint.y() / window2->height());
+
+ const bool stationaryTouchPoint = (normalPosition == touchPoint.normalPosition);
+ touchPoint.normalPosition = normalPosition;
switch (eventType) {
case EMSCRIPTEN_EVENT_TOUCHSTART:
- touchPoint.state = Qt::TouchPointPressed;
+ if (tp != pressedTouchIds.constEnd()) {
+ touchPoint.state = (stationaryTouchPoint
+ ? Qt::TouchPointStationary
+ : Qt::TouchPointMoved);
+ } else {
+ touchPoint.state = Qt::TouchPointPressed;
+ }
+ pressedTouchIds.insert(touchPoint.id, touchPoint.normalPosition);
+
break;
case EMSCRIPTEN_EVENT_TOUCHEND:
touchPoint.state = Qt::TouchPointReleased;
+ pressedTouchIds.remove(touchPoint.id);
break;
case EMSCRIPTEN_EVENT_TOUCHMOVE:
- touchPoint.state = Qt::TouchPointMoved;
+ touchPoint.state = (stationaryTouchPoint
+ ? Qt::TouchPointStationary
+ : Qt::TouchPointMoved);
+
+ pressedTouchIds.insert(touchPoint.id, touchPoint.normalPosition);
break;
default:
- Q_UNREACHABLE();
+ break;
}
touchPointList.append(touchPoint);
}
- QWasmEventTranslator *wasmEventTranslator = (QWasmEventTranslator*)userData;
QFlags<Qt::KeyboardModifier> keyModifier = translatKeyModifier(touchEvent);
- if (eventType != EMSCRIPTEN_EVENT_TOUCHCANCEL)
- QWindowSystemInterface::handleTouchEvent<QWindowSystemInterface::SynchronousDelivery>(window2, wasmEventTranslator->getTimestamp(), wasmEventTranslator->touchDevice, touchPointList, keyModifier);
- else
- QWindowSystemInterface::handleTouchCancelEvent(window2, wasmEventTranslator->getTimestamp(), wasmEventTranslator->touchDevice, keyModifier);
+ QWindowSystemInterface::handleTouchEvent<QWindowSystemInterface::SynchronousDelivery>(
+ window2, getTimestamp(), touchDevice, touchPointList, keyModifier);
+
+ if (eventType == EMSCRIPTEN_EVENT_TOUCHCANCEL)
+ QWindowSystemInterface::handleTouchCancelEvent(window2, getTimestamp(), touchDevice, keyModifier);
QWasmEventDispatcher::maintainTimers();
return 1;
@@ -560,4 +779,134 @@ quint64 QWasmEventTranslator::getTimestamp()
return QDeadlineTimer::current().deadlineNSecs() / 1000;
}
+Qt::Key QWasmEventTranslator::translateDeadKey(Qt::Key deadKey, Qt::Key accentBaseKey)
+{
+ Qt::Key wasmKey = Qt::Key_unknown;
+ switch (deadKey) {
+#ifdef Q_OS_MACOS
+ case Qt::Key_QuoteLeft: // ` macOS: Key_Dead_Grave
+#else
+ case Qt::Key_O: // ´ Key_Dead_Grave
+#endif
+ wasmKey = graveKeyTable.value(accentBaseKey);
+ break;
+ case Qt::Key_E: // ´ Key_Dead_Acute
+ wasmKey = acuteKeyTable.value(accentBaseKey);
+ break;
+ case Qt::Key_AsciiTilde:
+ case Qt::Key_N:// Key_Dead_Tilde
+ wasmKey = tildeKeyTable.value(accentBaseKey);
+ break;
+#ifndef Q_OS_MACOS
+ case Qt::Key_QuoteLeft:
+#endif
+ case Qt::Key_U:// ¨ Key_Dead_Diaeresis
+ wasmKey = diaeresisKeyTable.value(accentBaseKey);
+ break;
+ case Qt::Key_I:// macOS Key_Dead_Circumflex
+ case Qt::Key_6:// linux
+ case Qt::Key_Apostrophe:// linux
+ wasmKey = circumflexKeyTable.value(accentBaseKey);
+ break;
+ break;
+ default:
+ break;
+ };
+
+ return wasmKey;
+}
+
+bool QWasmEventTranslator::processKeyboard(int eventType, const EmscriptenKeyboardEvent *keyEvent)
+{
+ Qt::Key qtKey = translateEmscriptKey(keyEvent);
+
+ Qt::KeyboardModifiers modifiers = translateKeyboardEventModifier(keyEvent);
+
+ QString keyText;
+ QEvent::Type keyType = QEvent::None;
+ switch (eventType) {
+ case EMSCRIPTEN_EVENT_KEYPRESS:
+ case EMSCRIPTEN_EVENT_KEYDOWN: // down
+ keyType = QEvent::KeyPress;
+
+ if (m_emDeadKey != Qt::Key_unknown) {
+
+ Qt::Key transformedKey = translateDeadKey(m_emDeadKey, qtKey);
+
+ if (transformedKey != Qt::Key_unknown)
+ qtKey = transformedKey;
+
+ if (keyEvent->shiftKey == 0) {
+ for (auto it = KeyTbl.cbegin(); it != KeyTbl.end(); ++it) {
+ if (it != KeyTbl.end() && (qtKey == static_cast<Qt::Key>(it->qt))) {
+ keyText = it->em;
+ m_emDeadKey = Qt::Key_unknown;
+ break;
+ }
+ }
+ } else {
+ for (auto it = DeadKeyShiftTbl.cbegin(); it != DeadKeyShiftTbl.end(); ++it) {
+ if (it != DeadKeyShiftTbl.end() && (qtKey == static_cast<Qt::Key>(it->qt))) {
+ keyText = it->em;
+ m_emDeadKey = Qt::Key_unknown;
+ break;
+ }
+ }
+ }
+ }
+ if (qstrncmp(keyEvent->key, "Dead", 4) == 0 || qtKey == Qt::Key_AltGr) {
+ qtKey = translateEmscriptKey(keyEvent);
+ m_emStickyDeadKey = true;
+ if (keyEvent->shiftKey == 1 && qtKey == Qt::Key_QuoteLeft)
+ qtKey = Qt::Key_AsciiTilde;
+ m_emDeadKey = qtKey;
+ }
+ break;
+ case EMSCRIPTEN_EVENT_KEYUP: // up
+ keyType = QEvent::KeyRelease;
+ if (m_emStickyDeadKey && qtKey != Qt::Key_Alt) {
+ m_emStickyDeadKey = false;
+ }
+ break;
+ default:
+ break;
+ };
+
+ if (keyType == QEvent::None)
+ return 0;
+
+ QFlags<Qt::KeyboardModifier> mods = translateKeyboardEventModifier(keyEvent);
+
+ // Clipboard fallback path: cut/copy/paste are handled by clipboard event
+ // handlers if direct clipboard access is not available.
+ if (!QWasmIntegration::get()->getWasmClipboard()->hasClipboardApi && modifiers & Qt::ControlModifier &&
+ (qtKey == Qt::Key_X || qtKey == Qt::Key_C || qtKey == Qt::Key_V)) {
+ return 0;
+ }
+
+ bool accepted = false;
+
+ if (keyType == QEvent::KeyPress &&
+ mods.testFlag(Qt::ControlModifier)
+ && qtKey == Qt::Key_V) {
+ QWasmIntegration::get()->getWasmClipboard()->readTextFromClipboard();
+ } else {
+ if (keyText.isEmpty())
+ keyText = QString(keyEvent->key);
+ if (keyText.size() > 1)
+ keyText.clear();
+ accepted = QWindowSystemInterface::handleKeyEvent<QWindowSystemInterface::SynchronousDelivery>(
+ 0, keyType, qtKey, modifiers, keyText);
+ }
+ if (keyType == QEvent::KeyPress &&
+ mods.testFlag(Qt::ControlModifier)
+ && qtKey == Qt::Key_C) {
+ QWasmIntegration::get()->getWasmClipboard()->writeTextToClipboard();
+ }
+
+ QWasmEventDispatcher::maintainTimers();
+
+ return accepted;
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wasm/qwasmeventtranslator.h b/src/plugins/platforms/wasm/qwasmeventtranslator.h
index f3dff8e48c..1655b7226a 100644
--- a/src/plugins/platforms/wasm/qwasmeventtranslator.h
+++ b/src/plugins/platforms/wasm/qwasmeventtranslator.h
@@ -36,6 +36,7 @@
#include <emscripten/html5.h>
#include "qwasmwindow.h"
#include <QtGui/qtouchdevice.h>
+#include <QHash>
QT_BEGIN_NAMESPACE
@@ -45,133 +46,9 @@ class QWasmEventTranslator : public QObject
{
Q_OBJECT
- enum KeyCode {
- // numpad
- KeyNumPad0 = 0x60,
- KeyNumPad1 = 0x61,
- KeyNumPad2 = 0x62,
- KeyNumPad3 = 0x63,
- KeyNumPad4 = 0x64,
- KeyNumPad5 = 0x65,
- KeyNumPad6 = 0x66,
- KeyNumPad7 = 0x67,
- KeyNumPad8 = 0x68,
- KeyNumPad9 = 0x69,
- KeyMultiply = 0x6A,
- KeyAdd = 0x6B,
- KeySeparator = 0x6C,
- KeySubtract = 0x6D,
- KeyDecimal = 0x6E,
- KeyDivide = 0x6F,
- KeyMeta = 0x5B,
- KeyMetaRight = 0x5C,
- ////////
- KeyClear = 0x90,
- KeyEnter = 0xD,
- KeyBackSpace = 0x08,
- KeyCancel = 0x03,
- KeyTab = 0x09,
- KeyShift = 0x10,
- KeyControl = 0x11,
- KeyAlt = 0x12,
- KeyPause = 0x13,
- KeyCapsLock = 0x14,
- KeyEscape = 0x1B,
- KeySpace = 0x20,
- KeyPageUp = 0x21,
- KeyPageDown = 0x22,
- KeyEnd = 0x23,
- KeyHome = 0x24,
- KeyLeft = 0x25,
- KeyUp = 0x26,
- KeyRight = 0x27,
- KeyDown = 0x28,
- KeyComma = 0xBC,
- KeyPeriod = 0xBE,
- KeySlash = 0xBF,
- KeyZero = 0x30,
- KeyOne = 0x31,
- KeyTwo = 0x32,
- KeyThree = 0x33,
- KeyFour = 0x34,
- KeyFive = 0x35,
- KeySix = 0x36,
- KeySeven = 0x37,
- KeyEight = 0x38,
- KeyNine = 0x39,
- KeyBrightnessDown = 0xD8,
- KeyBrightnessUp = 0xD9,
- KeyMediaTrackPrevious = 0xB1,
- KeyMediaPlayPause = 0xB3,
- KeyMediaTrackNext = 0xB0,
- KeyAudioVolumeMute = 0xAD,
- KeyAudioVolumeDown = 0xAE,
- KeyAudioVolumeUp = 0xAF,
- KeySemiColon = 0xBA,
- KeyEquals = 0xBB,
- KeyMinus = 0xBD,
- KeyA = 0x41,
- KeyB = 0x42,
- KeyC = 0x43,
- KeyD = 0x44,
- KeyE = 0x45,
- KeyF = 0x46,
- KeyG = 0x47,
- KeyH = 0x48,
- KeyI = 0x49,
- KeyJ = 0x4A,
- KeyK = 0x4B,
- KeyL = 0x4C,
- KeyM = 0x4D,
- KeyN = 0x4E,
- KeyO = 0x4F,
- KeyP = 0x50,
- KeyQ = 0x51,
- KeyR = 0x52,
- KeyS = 0x53,
- KeyT = 0x54,
- KeyU = 0x55,
- KeyV = 0x56,
- KeyW = 0x57,
- KeyX = 0x58,
- KeyY = 0x59,
- KeyZ = 0x5A,
- KeyOpenBracket = 0xDB,
- KeyBackSlash = 0xDC,
- KeyCloseBracket = 0xDD,
- KeyF1 = 0x70,
- KeyF2 = 0x71,
- KeyF3 = 0x72,
- KeyF4 = 0x73,
- KeyF5 = 0x74,
- KeyF6 = 0x75,
- KeyF7 = 0x76,
- KeyF8 = 0x77,
- KeyF9 = 0x78,
- KeyF10 = 0x79,
- KeyF11 = 0x7A,
- KeyF12 = 0x7B,
- KeyDelete = 0x2E,
- KeyNumLock = 0x90,
- KeyScrollLock = 0x91,
- KeyPrintScreen = 0x9A,
- KeyInsert = 0x9B,
- KeyHelp = 0x9C,
- KeyBackQuote = 0xC0,
- KeyQuote = 0xDE,
- KeyFinal = 0x18,
- KeyConvert = 0x1C,
- KeyNonConvert = 0x1D,
- KeyAccept = 0x1E,
- KeyModeChange = 0x1F,
- KeyKana = 0x15,
- KeyKanji = 0x19,
- KeyUndefined = 0x0
- };
-
public:
- explicit QWasmEventTranslator(QObject *parent = 0);
+ explicit QWasmEventTranslator(QWasmScreen *screen);
static int keyboard_cb(int eventType, const EmscriptenKeyboardEvent *keyEvent, void *userData);
static int mouse_cb(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData);
@@ -181,18 +58,62 @@ public:
static int touchCallback(int eventType, const EmscriptenTouchEvent *ev, void *userData);
void processEvents();
+ void initEventHandlers();
+ int handleTouch(int eventType, const EmscriptenTouchEvent *touchEvent);
Q_SIGNALS:
void getWindowAt(const QPoint &point, QWindow **window);
private:
- static Qt::Key translateEmscriptKey(const EmscriptenKeyboardEvent *emscriptKey, bool *outAlphanumretic);
+ QWasmScreen *screen();
+ Qt::Key translateEmscriptKey(const EmscriptenKeyboardEvent *emscriptKey);
template <typename Event>
- static QFlags<Qt::KeyboardModifier> translatKeyModifier(const Event *event);
- static QFlags<Qt::KeyboardModifier> translateKeyboardEventModifier(const EmscriptenKeyboardEvent *keyEvent);
- static QFlags<Qt::KeyboardModifier> translateMouseEventModifier(const EmscriptenMouseEvent *mouseEvent);
- static Qt::MouseButton translateMouseButton(unsigned short button);
+ QFlags<Qt::KeyboardModifier> translatKeyModifier(const Event *event);
+ QFlags<Qt::KeyboardModifier> translateKeyboardEventModifier(const EmscriptenKeyboardEvent *keyEvent);
+ QFlags<Qt::KeyboardModifier> translateMouseEventModifier(const EmscriptenMouseEvent *mouseEvent);
+ Qt::MouseButton translateMouseButton(unsigned short button);
void processMouse(int eventType, const EmscriptenMouseEvent *mouseEvent);
+ bool processKeyboard(int eventType, const EmscriptenKeyboardEvent *keyEvent);
+
+ Qt::Key translateDeadKey(Qt::Key deadKey, Qt::Key accentBaseKey);
+
+ QHash<Qt::Key , Qt::Key> tildeKeyTable { // ~
+ { Qt::Key_A, Qt::Key_Atilde},
+ { Qt::Key_N, Qt::Key_Ntilde},
+ { Qt::Key_O, Qt::Key_Otilde}
+ };
+ QHash<Qt::Key , Qt::Key> graveKeyTable { // `
+ { Qt::Key_A, Qt::Key_Agrave},
+ { Qt::Key_E, Qt::Key_Egrave},
+ { Qt::Key_I, Qt::Key_Igrave},
+ { Qt::Key_O, Qt::Key_Ograve},
+ { Qt::Key_U, Qt::Key_Ugrave}
+ };
+ QHash<Qt::Key , Qt::Key> acuteKeyTable { // '
+ { Qt::Key_A, Qt::Key_Aacute},
+ { Qt::Key_E, Qt::Key_Eacute},
+ { Qt::Key_I, Qt::Key_Iacute},
+ { Qt::Key_O, Qt::Key_Oacute},
+ { Qt::Key_U, Qt::Key_Uacute},
+ { Qt::Key_Y, Qt::Key_Yacute}
+ };
+ QHash<Qt::Key , Qt::Key> diaeresisKeyTable { // umlaut ¨
+ { Qt::Key_A, Qt::Key_Adiaeresis},
+ { Qt::Key_E, Qt::Key_Ediaeresis},
+ { Qt::Key_I, Qt::Key_Idiaeresis},
+ { Qt::Key_O, Qt::Key_Odiaeresis},
+ { Qt::Key_U, Qt::Key_Udiaeresis},
+ { Qt::Key_Y, Qt::Key_ydiaeresis}
+ };
+ QHash<Qt::Key , Qt::Key> circumflexKeyTable { // ^
+ { Qt::Key_A, Qt::Key_Acircumflex},
+ { Qt::Key_E, Qt::Key_Ecircumflex},
+ { Qt::Key_I, Qt::Key_Icircumflex},
+ { Qt::Key_O, Qt::Key_Ocircumflex},
+ { Qt::Key_U, Qt::Key_Ucircumflex}
+ };
+
+ QMap <int, QPointF> pressedTouchIds;
private:
QWindow *draggedWindow;
@@ -205,6 +126,9 @@ private:
QRect resizeStartRect;
QTouchDevice *touchDevice;
quint64 getTimestamp();
+
+ Qt::Key m_emDeadKey = Qt::Key_unknown;
+ bool m_emStickyDeadKey = false;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wasm/qwasmfontdatabase.cpp b/src/plugins/platforms/wasm/qwasmfontdatabase.cpp
index 0c72dfddc4..dc6bb5847e 100644
--- a/src/plugins/platforms/wasm/qwasmfontdatabase.cpp
+++ b/src/plugins/platforms/wasm/qwasmfontdatabase.cpp
@@ -38,9 +38,9 @@ void QWasmFontDatabase::populateFontDatabase()
// Load font file from resources. Currently
// all fonts needs to be bundled with the nexe
// as Qt resources.
- QStringList fontFileNames = QStringList() << QStringLiteral(":/fonts/Vera.ttf")
+ QStringList fontFileNames = QStringList() << QStringLiteral(":/fonts/DejaVuSansMono.ttf")
+ << QStringLiteral(":/fonts/Vera.ttf")
<< QStringLiteral(":/fonts/DejaVuSans.ttf");
-
foreach (const QString &fontFileName, fontFileNames) {
QFile theFont(fontFileName);
if (!theFont.open(QIODevice::ReadOnly))
@@ -82,5 +82,9 @@ void QWasmFontDatabase::releaseHandle(void *handle)
QFreeTypeFontDatabase::releaseHandle(handle);
}
+QFont QWasmFontDatabase::defaultFont() const
+{
+ return QFont(QLatin1String("Bitstream Vera Sans"));
+}
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wasm/qwasmfontdatabase.h b/src/plugins/platforms/wasm/qwasmfontdatabase.h
index 891f12859e..cbd187a022 100644
--- a/src/plugins/platforms/wasm/qwasmfontdatabase.h
+++ b/src/plugins/platforms/wasm/qwasmfontdatabase.h
@@ -44,6 +44,7 @@ public:
QChar::Script script) const override;
QStringList addApplicationFont(const QByteArray &fontData, const QString &fileName) override;
void releaseHandle(void *handle) override;
+ QFont defaultFont() const override;
};
QT_END_NAMESPACE
#endif
diff --git a/src/plugins/platforms/wasm/qwasmintegration.cpp b/src/plugins/platforms/wasm/qwasmintegration.cpp
index 3829043d07..116612c286 100644
--- a/src/plugins/platforms/wasm/qwasmintegration.cpp
+++ b/src/plugins/platforms/wasm/qwasmintegration.cpp
@@ -33,6 +33,9 @@
#include "qwasmcompositor.h"
#include "qwasmopenglcontext.h"
#include "qwasmtheme.h"
+#include "qwasmclipboard.h"
+#include "qwasmservices.h"
+#include "qwasmoffscreensurface.h"
#include "qwasmwindow.h"
#ifndef QT_NO_OPENGL
@@ -46,8 +49,10 @@
#include <QtGui/qscreen.h>
#include <qpa/qwindowsysteminterface.h>
#include <QtCore/qcoreapplication.h>
+#include <qpa/qplatforminputcontextfactory_p.h>
#include <emscripten/bind.h>
+#include <emscripten/val.h>
// this is where EGL headers are pulled in, make sure it is last
#include "qwasmscreen.h"
@@ -55,47 +60,97 @@
using namespace emscripten;
QT_BEGIN_NAMESPACE
-void browserBeforeUnload()
+static void browserBeforeUnload(emscripten::val)
{
QWasmIntegration::QWasmBrowserExit();
}
-EMSCRIPTEN_BINDINGS(my_module)
+static void addCanvasElement(emscripten::val canvas)
{
- function("browserBeforeUnload", &browserBeforeUnload);
+ QString canvasId = QString::fromStdString(canvas["id"].as<std::string>());
+ QWasmIntegration::get()->addScreen(canvasId);
}
-static QWasmIntegration *globalHtml5Integration;
-QWasmIntegration *QWasmIntegration::get() { return globalHtml5Integration; }
+static void removeCanvasElement(emscripten::val canvas)
+{
+ QString canvasId = QString::fromStdString(canvas["id"].as<std::string>());
+ QWasmIntegration::get()->removeScreen(canvasId);
+}
+
+static void resizeCanvasElement(emscripten::val canvas)
+{
+ QString canvasId = QString::fromStdString(canvas["id"].as<std::string>());
+ QWasmIntegration::get()->resizeScreen(canvasId);
+}
+
+static void qtUpdateDpi()
+{
+ QWasmIntegration::get()->updateDpi();
+}
+
+EMSCRIPTEN_BINDINGS(qtQWasmIntegraton)
+{
+ function("qtBrowserBeforeUnload", &browserBeforeUnload);
+ function("qtAddCanvasElement", &addCanvasElement);
+ function("qtRemoveCanvasElement", &removeCanvasElement);
+ function("qtResizeCanvasElement", &resizeCanvasElement);
+ function("qtUpdateDpi", &qtUpdateDpi);
+}
+
+QWasmIntegration *QWasmIntegration::s_instance;
QWasmIntegration::QWasmIntegration()
: m_fontDb(nullptr),
- m_compositor(new QWasmCompositor),
- m_screen(new QWasmScreen(m_compositor)),
- m_eventDispatcher(nullptr)
+ m_desktopServices(nullptr),
+ m_clipboard(new QWasmClipboard)
{
+ s_instance = this;
+
+ // We expect that qtloader.js has populated Module.qtCanvasElements with one or more canvases.
+ // Also check Module.canvas, which may be set if the emscripen or a custom loader is used.
+ emscripten::val qtCanvaseElements = val::module_property("qtCanvasElements");
+ emscripten::val canvas = val::module_property("canvas");
+
+ if (!qtCanvaseElements.isUndefined()) {
+ int screenCount = qtCanvaseElements["length"].as<int>();
+ for (int i = 0; i < screenCount; ++i) {
+ emscripten::val canvas = qtCanvaseElements[i].as<emscripten::val>();
+ QString canvasId = QString::fromStdString(canvas["id"].as<std::string>());
+ addScreen(canvasId);
+ }
+ } else if (!canvas.isUndefined()){
+ QString canvasId = QString::fromStdString(canvas["id"].as<std::string>());
+ addScreen(canvasId);
+ }
- globalHtml5Integration = this;
-
- updateQScreenAndCanvasRenderSize();
- QWindowSystemInterface::handleScreenAdded(m_screen);
- emscripten_set_resize_callback(0, (void *)this, 1, uiEvent_cb);
+ emscripten::val::global("window").set("onbeforeunload", val::module_property("qtBrowserBeforeUnload"));
- m_eventTranslator = new QWasmEventTranslator;
+ // install browser window resize handler
+ auto onWindowResize = [](int eventType, const EmscriptenUiEvent *e, void *userData) -> int {
+ Q_UNUSED(eventType);
+ Q_UNUSED(e);
+ Q_UNUSED(userData);
- EM_ASM(// exit app if browser closes
- window.onbeforeunload = function () {
- Module.browserBeforeUnload();
- };
- );
+ // This resize event is called when the HTML window is resized. Depending
+ // on the page layout the the canvas(es) might also have been resized, so we
+ // update the Qt screen sizes (and canvas render sizes).
+ if (QWasmIntegration *integration = QWasmIntegration::get())
+ integration->resizeAllScreens();
+ return 0;
+ };
+ emscripten_set_resize_callback(nullptr, nullptr, 1, onWindowResize);
}
QWasmIntegration::~QWasmIntegration()
{
- delete m_compositor;
- QWindowSystemInterface::handleScreenRemoved(m_screen);
delete m_fontDb;
- delete m_eventTranslator;
+ delete m_desktopServices;
+
+ for (auto it = m_screens.constBegin(); it != m_screens.constEnd(); ++it)
+ QWindowSystemInterface::handleScreenRemoved(*it);
+ m_screens.clear();
+
+ s_instance = nullptr;
}
void QWasmIntegration::QWasmBrowserExit()
@@ -109,7 +164,7 @@ bool QWasmIntegration::hasCapability(QPlatformIntegration::Capability cap) const
switch (cap) {
case ThreadedPixmaps: return true;
case OpenGL: return true;
- case ThreadedOpenGL: return true;
+ case ThreadedOpenGL: return false;
case RasterGLSurface: return false; // to enable this you need to fix qopenglwidget and quickwidget for wasm
case MultipleWindows: return true;
case WindowManagement: return true;
@@ -119,13 +174,15 @@ bool QWasmIntegration::hasCapability(QPlatformIntegration::Capability cap) const
QPlatformWindow *QWasmIntegration::createPlatformWindow(QWindow *window) const
{
- return new QWasmWindow(window, m_compositor, m_backingStores.value(window));
+ QWasmCompositor *compositor = QWasmScreen::get(window->screen())->compositor();
+ return new QWasmWindow(window, compositor, m_backingStores.value(window));
}
QPlatformBackingStore *QWasmIntegration::createPlatformBackingStore(QWindow *window) const
{
#ifndef QT_NO_OPENGL
- QWasmBackingStore *backingStore = new QWasmBackingStore(m_compositor, window);
+ QWasmCompositor *compositor = QWasmScreen::get(window->screen())->compositor();
+ QWasmBackingStore *backingStore = new QWasmBackingStore(compositor, window);
m_backingStores.insert(window, backingStore);
return backingStore;
#else
@@ -140,6 +197,23 @@ QPlatformOpenGLContext *QWasmIntegration::createPlatformOpenGLContext(QOpenGLCon
}
#endif
+void QWasmIntegration::initialize()
+{
+ QString icStr = QPlatformInputContextFactory::requested();
+ if (!icStr.isNull())
+ m_inputContext.reset(QPlatformInputContextFactory::create(icStr));
+}
+
+QPlatformInputContext *QWasmIntegration::inputContext() const
+{
+ return m_inputContext.data();
+}
+
+QPlatformOffscreenSurface *QWasmIntegration::createPlatformOffscreenSurface(QOffscreenSurface *surface) const
+{
+ return new QWasmOffscrenSurface(surface);
+}
+
QPlatformFontDatabase *QWasmIntegration::fontDatabase() const
{
if (m_fontDb == nullptr)
@@ -155,9 +229,21 @@ QAbstractEventDispatcher *QWasmIntegration::createEventDispatcher() const
QVariant QWasmIntegration::styleHint(QPlatformIntegration::StyleHint hint) const
{
+ if (hint == ShowIsFullScreen)
+ return true;
+
return QPlatformIntegration::styleHint(hint);
}
+Qt::WindowState QWasmIntegration::defaultWindowState(Qt::WindowFlags flags) const
+{
+ // Don't maximize dialogs
+ if (flags & Qt::Dialog & ~Qt::Window)
+ return Qt::WindowNoState;
+
+ return QPlatformIntegration::defaultWindowState(flags);
+}
+
QStringList QWasmIntegration::themeNames() const
{
return QStringList() << QLatin1String("webassembly");
@@ -170,50 +256,53 @@ QPlatformTheme *QWasmIntegration::createPlatformTheme(const QString &name) const
return QPlatformIntegration::createPlatformTheme(name);
}
-int QWasmIntegration::uiEvent_cb(int eventType, const EmscriptenUiEvent *e, void *userData)
+QPlatformServices *QWasmIntegration::services() const
{
- Q_UNUSED(e)
- Q_UNUSED(userData)
-
- if (eventType == EMSCRIPTEN_EVENT_RESIZE) {
- // This resize event is called when the HTML window is resized. Depending
- // on the page layout the the canvas might also have been resized, so we
- // update the Qt screen size (and canvas render size).
- updateQScreenAndCanvasRenderSize();
- }
+ if (m_desktopServices == nullptr)
+ m_desktopServices = new QWasmServices();
+ return m_desktopServices;
+}
- return 0;
+QPlatformClipboard* QWasmIntegration::clipboard() const
+{
+ return m_clipboard;
}
-static void set_canvas_size(double width, double height)
+void QWasmIntegration::addScreen(const QString &canvasId)
{
- EM_ASM_({
- var canvas = Module.canvas;
- canvas.width = $0;
- canvas.height = $1;
- }, width, height);
+ QWasmScreen *screen = new QWasmScreen(canvasId);
+ m_clipboard->installEventHandlers(canvasId);
+ m_screens.insert(canvasId, screen);
+ QWindowSystemInterface::handleScreenAdded(screen);
}
-void QWasmIntegration::updateQScreenAndCanvasRenderSize()
+void QWasmIntegration::removeScreen(const QString &canvasId)
{
- // The HTML canvas has two sizes: the CSS size and the canvas render size.
- // The CSS size is determined according to standard CSS rules, while the
- // render size is set using the "width" and "height" attributes. The render
- // size must be set manually and is not auto-updated on CSS size change.
- // Setting the render size to a value larger than the CSS size enables high-dpi
- // rendering.
+ QWasmScreen *exScreen = m_screens.take(canvasId);
+ exScreen->destroy(); // clean up before deleting the screen
+ QWindowSystemInterface::handleScreenRemoved(exScreen);
+}
- double css_width;
- double css_height;
- emscripten_get_element_css_size(0, &css_width, &css_height);
- QSizeF cssSize(css_width, css_height);
+void QWasmIntegration::resizeScreen(const QString &canvasId)
+{
+ m_screens.value(canvasId)->updateQScreenAndCanvasRenderSize();
+}
- QWasmScreen *screen = QWasmIntegration::get()->m_screen;
- QSizeF canvasSize = cssSize * screen->devicePixelRatio();
+void QWasmIntegration::updateDpi()
+{
+ emscripten::val dpi = emscripten::val::module_property("qtFontDpi");
+ if (dpi.isUndefined())
+ return;
+ qreal dpiValue = dpi.as<qreal>();
+ for (QWasmScreen *screen : m_screens)
+ QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen->screen(), dpiValue, dpiValue);
+}
- set_canvas_size(canvasSize.width(), canvasSize.height());
- screen->setGeometry(QRect(QPoint(0, 0), cssSize.toSize()));
- QWasmIntegration::get()->m_compositor->redrawWindowContent();
+void QWasmIntegration::resizeAllScreens()
+{
+ qDebug() << "resizeAllScreens";
+ for (QWasmScreen *screen : m_screens)
+ screen->updateQScreenAndCanvasRenderSize();
}
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wasm/qwasmintegration.h b/src/plugins/platforms/wasm/qwasmintegration.h
index ebc3d9d431..2102f5c226 100644
--- a/src/plugins/platforms/wasm/qwasmintegration.h
+++ b/src/plugins/platforms/wasm/qwasmintegration.h
@@ -34,6 +34,7 @@
#include <qpa/qplatformintegration.h>
#include <qpa/qplatformscreen.h>
+#include <qpa/qplatforminputcontext.h>
#include <QtCore/qhash.h>
@@ -49,6 +50,8 @@ class QWasmEventDispatcher;
class QWasmScreen;
class QWasmCompositor;
class QWasmBackingStore;
+class QWasmClipboard;
+class QWasmServices;
class QWasmIntegration : public QObject, public QPlatformIntegration
{
@@ -63,28 +66,39 @@ public:
#ifndef QT_NO_OPENGL
QPlatformOpenGLContext *createPlatformOpenGLContext(QOpenGLContext *context) const override;
#endif
+ QPlatformOffscreenSurface *createPlatformOffscreenSurface(QOffscreenSurface *surface) const override;
QPlatformFontDatabase *fontDatabase() const override;
QAbstractEventDispatcher *createEventDispatcher() const override;
QVariant styleHint(QPlatformIntegration::StyleHint hint) const override;
+ Qt::WindowState defaultWindowState(Qt::WindowFlags flags) const override;
QStringList themeNames() const override;
QPlatformTheme *createPlatformTheme(const QString &name) const override;
+ QPlatformServices *services() const override;
+ QPlatformClipboard *clipboard() const override;
+ void initialize() override;
+ QPlatformInputContext *inputContext() const override;
- static QWasmIntegration *get();
- QWasmScreen *screen() { return m_screen; }
- QWasmCompositor *compositor() { return m_compositor; }
- QWasmEventTranslator *eventTranslator() { return m_eventTranslator; }
+ QWasmClipboard *getWasmClipboard() { return m_clipboard; }
+ static QWasmIntegration *get() { return s_instance; }
static void QWasmBrowserExit();
- static void updateQScreenAndCanvasRenderSize();
+
+ void addScreen(const QString &canvasId);
+ void removeScreen(const QString &canvasId);
+ void resizeScreen(const QString &canvasId);
+ void resizeAllScreens();
+ void updateDpi();
private:
mutable QWasmFontDatabase *m_fontDb;
- QWasmCompositor *m_compositor;
- mutable QWasmScreen *m_screen;
- mutable QWasmEventTranslator *m_eventTranslator;
- mutable QWasmEventDispatcher *m_eventDispatcher;
- static int uiEvent_cb(int eventType, const EmscriptenUiEvent *e, void *userData);
+ mutable QWasmServices *m_desktopServices;
mutable QHash<QWindow *, QWasmBackingStore *> m_backingStores;
+
+ QHash<QString, QWasmScreen *> m_screens;
+ mutable QWasmClipboard *m_clipboard;
+ qreal m_fontDpi = -1;
+ mutable QScopedPointer<QPlatformInputContext> m_inputContext;
+ static QWasmIntegration *s_instance;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wasm/qwasmoffscreensurface.cpp b/src/plugins/platforms/wasm/qwasmoffscreensurface.cpp
new file mode 100644
index 0000000000..a205e5ddea
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmoffscreensurface.cpp
@@ -0,0 +1,41 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) 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.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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwasmoffscreensurface.h"
+
+QWasmOffscrenSurface::QWasmOffscrenSurface(QOffscreenSurface *offscreenSurface)
+ :QPlatformOffscreenSurface(offscreenSurface)
+{
+
+}
+
+QWasmOffscrenSurface::~QWasmOffscrenSurface()
+{
+
+}
diff --git a/src/plugins/platforms/wasm/qwasmoffscreensurface.h b/src/plugins/platforms/wasm/qwasmoffscreensurface.h
new file mode 100644
index 0000000000..9d3e805be0
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmoffscreensurface.h
@@ -0,0 +1,49 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) 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.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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWASMOFFSCREENSURFACE_H
+#define QWASMOFFSCREENSURFACE_H
+
+#include <qpa/qplatformoffscreensurface.h>
+
+QT_BEGIN_NAMESPACE
+
+class QOffscreenSurface;
+class QWasmOffscrenSurface : public QPlatformOffscreenSurface
+{
+public:
+ explicit QWasmOffscrenSurface(QOffscreenSurface *offscreenSurface);
+ ~QWasmOffscrenSurface();
+private:
+
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/platforms/wasm/qwasmopenglcontext.cpp b/src/plugins/platforms/wasm/qwasmopenglcontext.cpp
index 73af3d1878..62087f54bd 100644
--- a/src/plugins/platforms/wasm/qwasmopenglcontext.cpp
+++ b/src/plugins/platforms/wasm/qwasmopenglcontext.cpp
@@ -28,7 +28,7 @@
****************************************************************************/
#include "qwasmopenglcontext.h"
-
+#include "qwasmintegration.h"
#include <EGL/egl.h>
QT_BEGIN_NAMESPACE
@@ -37,47 +37,46 @@ QWasmOpenGLContext::QWasmOpenGLContext(const QSurfaceFormat &format)
: m_requestedFormat(format)
{
m_requestedFormat.setRenderableType(QSurfaceFormat::OpenGLES);
+
+ // if we set one, we need to set the other as well since in webgl, these are tied together
+ if (format.depthBufferSize() < 0 && format.stencilBufferSize() > 0)
+ m_requestedFormat.setDepthBufferSize(16);
+
+ if (format.stencilBufferSize() < 0 && format.depthBufferSize() > 0)
+ m_requestedFormat.setStencilBufferSize(8);
+
}
QWasmOpenGLContext::~QWasmOpenGLContext()
{
- if (m_context)
+ if (m_context) {
emscripten_webgl_destroy_context(m_context);
+ m_context = 0;
+ }
}
-void QWasmOpenGLContext::maybeRecreateEmscriptenContext(QPlatformSurface *surface)
+bool QWasmOpenGLContext::maybeCreateEmscriptenContext(QPlatformSurface *surface)
{
- // Native emscripten contexts are tied to a single surface. Recreate
- // the context if the surface is changed.
- if (surface != m_surface) {
- m_surface = surface;
-
- // Destroy existing context
- if (m_context)
- emscripten_webgl_destroy_context(m_context);
-
- // Create new context
- const char *canvasId = 0; // (use default canvas) FIXME: get the actual canvas from the surface.
- m_context = createEmscriptenContext(canvasId, m_requestedFormat);
-
- // Register context-lost callback.
- auto callback = [](int eventType, const void *reserved, void *userData) -> EM_BOOL
- {
- Q_UNUSED(eventType);
- Q_UNUSED(reserved);
- // The application may get contex-lost if e.g. moved to the background. Set
- // m_contextLost which will make isValid() return false. Application code will
- // then detect this and recrate the the context, resulting in a new QWasmOpenGLContext
- // instance.
- reinterpret_cast<QWasmOpenGLContext *>(userData)->m_contextLost = true;
- return true;
- };
- bool capture = true;
- emscripten_set_webglcontextlost_callback(canvasId, this, capture, callback);
- }
+ // Native emscripten/WebGL contexts are tied to a single screen/canvas. The first
+ // call to this function creates a native canvas for the given screen, subsequent
+ // calls verify that the surface is on/off the same screen.
+ QPlatformScreen *screen = surface->screen();
+ if (m_context && !screen)
+ return false; // Alternative: return true to support makeCurrent on QOffScreenSurface with
+ // no screen. However, Qt likes to substitute QGuiApplication::primaryScreen()
+ // for null screens, which foils this plan.
+ if (!screen)
+ return false;
+ if (m_context)
+ return m_screen == screen;
+
+ QString canvasId = QWasmScreen::get(screen)->canvasId();
+ m_context = createEmscriptenContext(canvasId, m_requestedFormat);
+ m_screen = screen;
+ return true;
}
-EMSCRIPTEN_WEBGL_CONTEXT_HANDLE QWasmOpenGLContext::createEmscriptenContext(const char *canvasId, QSurfaceFormat format)
+EMSCRIPTEN_WEBGL_CONTEXT_HANDLE QWasmOpenGLContext::createEmscriptenContext(const QString &canvasId, QSurfaceFormat format)
{
EmscriptenWebGLContextAttributes attributes;
emscripten_webgl_init_context_attributes(&attributes); // Populate with default attributes
@@ -91,12 +90,16 @@ EMSCRIPTEN_WEBGL_CONTEXT_HANDLE QWasmOpenGLContext::createEmscriptenContext(cons
attributes.majorVersion = 2;
}
+ // WebGL doesn't allow separate attach buffers to STENCIL_ATTACHMENT and DEPTH_ATTACHMENT
+ // we need both or none
+ bool useDepthStencil = (format.depthBufferSize() > 0 || format.stencilBufferSize() > 0);
+
// WebGL offers enable/disable control but not size control for these
attributes.alpha = format.alphaBufferSize() > 0;
- attributes.depth = format.depthBufferSize() > 0;
- attributes.stencil = format.stencilBufferSize() > 0;
+ attributes.depth = useDepthStencil;
+ attributes.stencil = useDepthStencil;
- EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context = emscripten_webgl_create_context(canvasId, &attributes);
+ EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context = emscripten_webgl_create_context(canvasId.toLocal8Bit().constData(), &attributes);
return context;
}
@@ -113,7 +116,9 @@ GLuint QWasmOpenGLContext::defaultFramebufferObject(QPlatformSurface *surface) c
bool QWasmOpenGLContext::makeCurrent(QPlatformSurface *surface)
{
- maybeRecreateEmscriptenContext(surface);
+ bool ok = maybeCreateEmscriptenContext(surface);
+ if (!ok)
+ return false;
return emscripten_webgl_make_context_current(m_context) == EMSCRIPTEN_RESULT_SUCCESS;
}
@@ -136,7 +141,9 @@ bool QWasmOpenGLContext::isSharing() const
bool QWasmOpenGLContext::isValid() const
{
- return (m_contextLost == false);
+ // Note: we get isValid() calls before we see the surface and can
+ // create a native context, so no context is also a valid state.
+ return !m_context || !emscripten_is_webgl_context_lost(m_context);
}
QFunctionPointer QWasmOpenGLContext::getProcAddress(const char *procName)
diff --git a/src/plugins/platforms/wasm/qwasmopenglcontext.h b/src/plugins/platforms/wasm/qwasmopenglcontext.h
index 9123100479..d27007e8ea 100644
--- a/src/plugins/platforms/wasm/qwasmopenglcontext.h
+++ b/src/plugins/platforms/wasm/qwasmopenglcontext.h
@@ -34,6 +34,7 @@
QT_BEGIN_NAMESPACE
+class QPlatformScreen;
class QWasmOpenGLContext : public QPlatformOpenGLContext
{
public:
@@ -50,12 +51,11 @@ public:
QFunctionPointer getProcAddress(const char *procName) override;
private:
- void maybeRecreateEmscriptenContext(QPlatformSurface *surface);
- static EMSCRIPTEN_WEBGL_CONTEXT_HANDLE createEmscriptenContext(const char *canvasId, QSurfaceFormat format);
+ bool maybeCreateEmscriptenContext(QPlatformSurface *surface);
+ static EMSCRIPTEN_WEBGL_CONTEXT_HANDLE createEmscriptenContext(const QString &canvasId, QSurfaceFormat format);
- bool m_contextLost = false;
QSurfaceFormat m_requestedFormat;
- QPlatformSurface *m_surface = nullptr;
+ QPlatformScreen *m_screen = nullptr;
EMSCRIPTEN_WEBGL_CONTEXT_HANDLE m_context = 0;
};
diff --git a/src/plugins/platforms/wasm/qwasmscreen.cpp b/src/plugins/platforms/wasm/qwasmscreen.cpp
index 93e9906ffc..f2eabfa486 100644
--- a/src/plugins/platforms/wasm/qwasmscreen.cpp
+++ b/src/plugins/platforms/wasm/qwasmscreen.cpp
@@ -29,7 +29,11 @@
#include "qwasmscreen.h"
#include "qwasmwindow.h"
+#include "qwasmeventtranslator.h"
#include "qwasmcompositor.h"
+#include "qwasmintegration.h"
+#include <emscripten/bind.h>
+#include <emscripten/val.h>
#include <QtEglSupport/private/qeglconvenience_p.h>
#ifndef QT_NO_OPENGL
@@ -43,17 +47,48 @@
QT_BEGIN_NAMESPACE
-QWasmScreen::QWasmScreen(QWasmCompositor *compositor)
- : m_compositor(compositor)
- , m_depth(32)
- , m_format(QImage::Format_RGB32)
+QWasmScreen::QWasmScreen(const QString &canvasId)
+ : m_canvasId(canvasId)
+
{
- m_compositor->setScreen(this);
+ m_compositor = new QWasmCompositor(this);
+ m_eventTranslator = new QWasmEventTranslator(this);
+ updateQScreenAndCanvasRenderSize();
}
QWasmScreen::~QWasmScreen()
{
+ destroy();
+}
+
+void QWasmScreen::destroy()
+{
+ m_compositor->destroy();
+}
+
+QWasmScreen *QWasmScreen::get(QPlatformScreen *screen)
+{
+ return static_cast<QWasmScreen *>(screen);
+}
+QWasmScreen *QWasmScreen::get(QScreen *screen)
+{
+ return get(screen->handle());
+}
+
+QWasmCompositor *QWasmScreen::compositor()
+{
+ return m_compositor;
+}
+
+QWasmEventTranslator *QWasmScreen::eventTranslator()
+{
+ return m_eventTranslator;
+}
+
+QString QWasmScreen::canvasId() const
+{
+ return m_canvasId;
}
QRect QWasmScreen::geometry() const
@@ -71,18 +106,32 @@ QImage::Format QWasmScreen::format() const
return m_format;
}
+QDpi QWasmScreen::logicalDpi() const
+{
+ emscripten::val dpi = emscripten::val::module_property("qtFontDpi");
+ if (!dpi.isUndefined()) {
+ qreal dpiValue = dpi.as<qreal>();
+ return QDpi(dpiValue, dpiValue);
+ }
+ const qreal defaultDpi = 96;
+ return QDpi(defaultDpi, defaultDpi);
+}
+
qreal QWasmScreen::devicePixelRatio() const
{
// FIXME: The effective device pixel ratio may be different from the
// HTML window dpr if the OpenGL driver/GPU allocates a less than
// full resolution surface. Use emscripten_webgl_get_drawing_buffer_size()
// and compute the dpr instead.
- double htmlWindowDpr = EM_ASM_DOUBLE({
- return window.devicePixelRatio;
- });
+ double htmlWindowDpr = emscripten::val::global("window")["devicePixelRatio"].as<double>();
return qreal(htmlWindowDpr);
}
+QString QWasmScreen::name() const
+{
+ return m_canvasId;
+}
+
QPlatformCursor *QWasmScreen::cursor() const
{
return const_cast<QWasmCursor *>(&m_cursor);
@@ -115,4 +164,31 @@ void QWasmScreen::setGeometry(const QRect &rect)
resizeMaximizedWindows();
}
+void QWasmScreen::updateQScreenAndCanvasRenderSize()
+{
+ // The HTML canvas has two sizes: the CSS size and the canvas render size.
+ // The CSS size is determined according to standard CSS rules, while the
+ // render size is set using the "width" and "height" attributes. The render
+ // size must be set manually and is not auto-updated on CSS size change.
+ // Setting the render size to a value larger than the CSS size enables high-dpi
+ // rendering.
+
+ QByteArray canvasId = m_canvasId.toUtf8();
+ double css_width;
+ double css_height;
+ emscripten_get_element_css_size(canvasId.constData(), &css_width, &css_height);
+ QSizeF cssSize(css_width, css_height);
+
+ QSizeF canvasSize = cssSize * devicePixelRatio();
+ emscripten::val canvas = emscripten::val::global(canvasId.constData());
+ canvas.set("width", canvasSize.width());
+ canvas.set("height", canvasSize.height());
+
+ emscripten::val rect = canvas.call<emscripten::val>("getBoundingClientRect");
+ QPoint position(rect["left"].as<int>(), rect["top"].as<int>());
+
+ setGeometry(QRect(position, cssSize.toSize()));
+ m_compositor->redrawWindowContent();
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wasm/qwasmscreen.h b/src/plugins/platforms/wasm/qwasmscreen.h
index 3891db77bb..fcf693681c 100644
--- a/src/plugins/platforms/wasm/qwasmscreen.h
+++ b/src/plugins/platforms/wasm/qwasmscreen.h
@@ -43,20 +43,30 @@ class QPlatformOpenGLContext;
class QWasmWindow;
class QWasmBackingStore;
class QWasmCompositor;
+class QWasmEventTranslator;
class QOpenGLContext;
class QWasmScreen : public QObject, public QPlatformScreen
{
Q_OBJECT
public:
-
- QWasmScreen(QWasmCompositor *compositor);
+ QWasmScreen(const QString &canvasId);
~QWasmScreen();
+ void destroy();
+
+ static QWasmScreen *get(QPlatformScreen *screen);
+ static QWasmScreen *get(QScreen *screen);
+ QString canvasId() const;
+
+ QWasmCompositor *compositor();
+ QWasmEventTranslator *eventTranslator();
QRect geometry() const override;
int depth() const override;
QImage::Format format() const override;
+ QDpi logicalDpi() const override;
qreal devicePixelRatio() const override;
+ QString name() const override;
QPlatformCursor *cursor() const override;
void resizeMaximizedWindows();
@@ -64,17 +74,18 @@ public:
QWindow *topLevelAt(const QPoint &p) const override;
void invalidateSize();
+ void updateQScreenAndCanvasRenderSize();
public slots:
void setGeometry(const QRect &rect);
-protected:
private:
- QWasmCompositor *m_compositor;
-
+ QString m_canvasId;
+ QWasmCompositor *m_compositor = nullptr;
+ QWasmEventTranslator *m_eventTranslator = nullptr;
QRect m_geometry = QRect(0, 0, 100, 100);
- int m_depth;
- QImage::Format m_format;
+ int m_depth = 32;
+ QImage::Format m_format = QImage::Format_RGB32;
QWasmCursor m_cursor;
};
diff --git a/src/plugins/platforms/wasm/qwasmservices.cpp b/src/plugins/platforms/wasm/qwasmservices.cpp
new file mode 100644
index 0000000000..9328b8c065
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmservices.cpp
@@ -0,0 +1,45 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) 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.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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwasmservices.h"
+#include <QtCore/QUrl>
+#include <QtCore/QDebug>
+
+#include <emscripten/val.h>
+
+QT_BEGIN_NAMESPACE
+
+bool QWasmServices::openUrl(const QUrl &url)
+{
+ QByteArray utf8Url = url.toString().toUtf8();
+ emscripten::val::global("window").call<void>("open", emscripten::val(utf8Url.constData()), emscripten::val("_blank"));
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wasm/qwasmservices.h b/src/plugins/platforms/wasm/qwasmservices.h
new file mode 100644
index 0000000000..3b37f21f82
--- /dev/null
+++ b/src/plugins/platforms/wasm/qwasmservices.h
@@ -0,0 +1,45 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) 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.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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWASMDESKTOPSERVICES_H
+#define QWASMDESKTOPSERVICES_H
+
+#include <qpa/qplatformservices.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWasmServices : public QPlatformServices
+{
+public:
+ bool openUrl(const QUrl &url) override;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWASMDESKTOPSERVICES_H
diff --git a/src/plugins/platforms/wasm/qwasmtheme.cpp b/src/plugins/platforms/wasm/qwasmtheme.cpp
index a7f2db3bd3..978d60d686 100644
--- a/src/plugins/platforms/wasm/qwasmtheme.cpp
+++ b/src/plugins/platforms/wasm/qwasmtheme.cpp
@@ -29,15 +29,22 @@
#include "qwasmtheme.h"
#include <QtCore/qvariant.h>
+#include <QFontDatabase>
QT_BEGIN_NAMESPACE
QWasmTheme::QWasmTheme()
{
+ QFontDatabase fdb;
+ for (auto family : fdb.families())
+ if (fdb.isFixedPitch(family))
+ fixedFont = new QFont(family);
}
QWasmTheme::~QWasmTheme()
{
+ if (fixedFont)
+ delete fixedFont;
}
QVariant QWasmTheme::themeHint(ThemeHint hint) const
@@ -47,4 +54,12 @@ QVariant QWasmTheme::themeHint(ThemeHint hint) const
return QPlatformTheme::themeHint(hint);
}
+const QFont *QWasmTheme::font(Font type) const
+{
+ if (type == QPlatformTheme::FixedFont) {
+ return fixedFont;
+ }
+ return nullptr;
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wasm/qwasmtheme.h b/src/plugins/platforms/wasm/qwasmtheme.h
index e4cc06e049..7123a1f3d4 100644
--- a/src/plugins/platforms/wasm/qwasmtheme.h
+++ b/src/plugins/platforms/wasm/qwasmtheme.h
@@ -31,6 +31,7 @@
#define QWASMTHEME_H
#include <qpa/qplatformtheme.h>
+#include <QtGui/QFont>
QT_BEGIN_NAMESPACE
@@ -49,6 +50,8 @@ public:
~QWasmTheme();
QVariant themeHint(ThemeHint hint) const override;
+ const QFont *font(Font type) const override;
+ QFont *fixedFont = nullptr;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wasm/qwasmwindow.cpp b/src/plugins/platforms/wasm/qwasmwindow.cpp
index 25a0190053..594db65cfd 100644
--- a/src/plugins/platforms/wasm/qwasmwindow.cpp
+++ b/src/plugins/platforms/wasm/qwasmwindow.cpp
@@ -65,6 +65,12 @@ QWasmWindow::~QWasmWindow()
m_compositor->removeWindow(this);
}
+void QWasmWindow::destroy()
+{
+ if (m_backingStore)
+ m_backingStore->destroy();
+}
+
void QWasmWindow::initialize()
{
QRect rect = windowGeometry();
@@ -198,8 +204,10 @@ void QWasmWindow::injectMouseReleased(const QPoint &local, const QPoint &global,
if (!hasTitleBar() || button != Qt::LeftButton)
return;
- if (closeButtonRect().contains(global) && m_activeControl == QWasmCompositor::SC_TitleBarCloseButton)
+ if (closeButtonRect().contains(global) && m_activeControl == QWasmCompositor::SC_TitleBarCloseButton) {
window()->close();
+ return;
+ }
if (maxButtonRect().contains(global) && m_activeControl == QWasmCompositor::SC_TitleBarMaxButton) {
window()->setWindowState(Qt::WindowMaximized);
diff --git a/src/plugins/platforms/wasm/qwasmwindow.h b/src/plugins/platforms/wasm/qwasmwindow.h
index cbbce99aeb..a098172649 100644
--- a/src/plugins/platforms/wasm/qwasmwindow.h
+++ b/src/plugins/platforms/wasm/qwasmwindow.h
@@ -58,6 +58,7 @@ public:
QWasmWindow(QWindow *w, QWasmCompositor *compositor, QWasmBackingStore *backingStore);
~QWasmWindow();
+ void destroy();
void initialize() override;
diff --git a/src/plugins/platforms/wasm/wasm.pro b/src/plugins/platforms/wasm/wasm.pro
index eaaba53aa2..c28df8f893 100644
--- a/src/plugins/platforms/wasm/wasm.pro
+++ b/src/plugins/platforms/wasm/wasm.pro
@@ -18,7 +18,10 @@ SOURCES = \
qwasmcompositor.cpp \
qwasmcursor.cpp \
qwasmopenglcontext.cpp \
- qwasmtheme.cpp
+ qwasmtheme.cpp \
+ qwasmclipboard.cpp \
+ qwasmservices.cpp \
+ qwasmoffscreensurface.cpp
HEADERS = \
qwasmintegration.h \
@@ -31,11 +34,15 @@ HEADERS = \
qwasmstylepixmaps_p.h \
qwasmcursor.h \
qwasmopenglcontext.h \
- qwasmtheme.h
+ qwasmtheme.h \
+ qwasmclipboard.h \
+ qwasmservices.h \
+ qwasmoffscreensurface.h
wasmfonts.files = \
../../../3rdparty/wasm/Vera.ttf \
- ../../../3rdparty/wasm/DejaVuSans.ttf
+ ../../../3rdparty/wasm/DejaVuSans.ttf \
+ ../../../3rdparty/wasm/DejaVuSansMono.ttf
wasmfonts.prefix = /fonts
wasmfonts.base = ../../../3rdparty/wasm
RESOURCES += wasmfonts
diff --git a/src/plugins/platforms/wasm/wasm_shell.html b/src/plugins/platforms/wasm/wasm_shell.html
index 67bfcdfbdc..a118c217f3 100644
--- a/src/plugins/platforms/wasm/wasm_shell.html
+++ b/src/plugins/platforms/wasm/wasm_shell.html
@@ -3,31 +3,36 @@
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <title>APPNAME</title>
+ <title>@APPNAME@</title>
<style>
html, body { padding: 0; margin : 0; overflow:hidden; height: 100% }
/* the canvas *must not* have any border or padding, or mouse coords will be wrong */
- canvas { border: 0px none; background-color: white; height:100%; width:100%; }
+ canvas { border: 0px none; background-color: white; height:100%; width:100%; }
+ /* The contenteditable property is set to true for the canvas in order to support
+ clipboard events. Hide the resulting focus frame and set the cursor back to
+ the default cursor. */
+ canvas { outline: 0px solid transparent; caret-color: transparent; cursor:default }
</style>
</head>
<body onload="init()">
- <figure style="overflow:visible;" id="spinner">
+ <figure style="overflow:visible;" id="qtspinner">
<center style="margin-top:1.5em; line-height:150%">
<img src="qtlogo.svg"; width=320; height=200; style="display:block"> </img>
- <strong>Qt for WebAssembly: APPNAME</strong>
- <div id="status"></div>
+ <strong>Qt for WebAssembly: @APPNAME@</strong>
+ <div id="qtstatus"></div>
<noscript>JavaScript is disabled. Please enable JavaScript to use this application.</noscript>
</center>
</figure>
- <canvas id="canvas" oncontextmenu="event.preventDefault()"></canvas>
+ <canvas id="qtcanvas" oncontextmenu="event.preventDefault()" contenteditable="true"></canvas>
<script type='text/javascript'>
function init() {
- var spinner = document.getElementById('spinner');
- var canvas = document.getElementById('canvas');
- var status = document.getElementById('status')
+ var spinner = document.querySelector('#qtspinner');
+ var canvas = document.querySelector('#qtcanvas');
+ var status = document.querySelector('#qtstatus')
var qtLoader = QtLoader({
+ canvasElements : [canvas],
showLoader: function(loaderStatus) {
spinner.style.display = 'block';
canvas.style.display = 'none';
@@ -50,10 +55,9 @@
showCanvas: function() {
spinner.style.display = 'none';
canvas.style.display = 'block';
- return canvas;
},
});
- qtLoader.loadEmscriptenModule("APPNAME");
+ qtLoader.loadEmscriptenModule("@APPNAME@");
}
</script>
<script type="text/javascript" src="qtloader.js"></script>
diff --git a/src/plugins/platforms/windows/qwindowsinputcontext.cpp b/src/plugins/platforms/windows/qwindowsinputcontext.cpp
index 61f971143d..8054f9fe83 100644
--- a/src/plugins/platforms/windows/qwindowsinputcontext.cpp
+++ b/src/plugins/platforms/windows/qwindowsinputcontext.cpp
@@ -452,7 +452,7 @@ static inline QTextFormat standardFormat(StandardFormat format)
const QPalette palette = QGuiApplication::palette();
const QColor background = palette.text().color();
result.setBackground(QBrush(background));
- result.setForeground(palette.background());
+ result.setForeground(palette.window());
break;
}
}
diff --git a/src/plugins/platforms/windows/qwindowsnativeinterface.cpp b/src/plugins/platforms/windows/qwindowsnativeinterface.cpp
index ed945ec4b1..9003e94c56 100644
--- a/src/plugins/platforms/windows/qwindowsnativeinterface.cpp
+++ b/src/plugins/platforms/windows/qwindowsnativeinterface.cpp
@@ -277,6 +277,8 @@ QFunctionPointer QWindowsNativeInterface::platformFunction(const QByteArray &fun
return QFunctionPointer(QWindowsWindow::setTouchWindowTouchTypeStatic);
if (function == QWindowsWindowFunctions::setHasBorderInFullScreenIdentifier())
return QFunctionPointer(QWindowsWindow::setHasBorderInFullScreenStatic);
+ if (function == QWindowsWindowFunctions::setHasBorderInFullScreenDefaultIdentifier())
+ return QFunctionPointer(QWindowsWindow::setHasBorderInFullScreenDefault);
if (function == QWindowsWindowFunctions::setWindowActivationBehaviorIdentifier())
return QFunctionPointer(QWindowsNativeInterface::setWindowActivationBehavior);
if (function == QWindowsWindowFunctions::isTabletModeIdentifier())
diff --git a/src/plugins/platforms/windows/qwindowstabletsupport.cpp b/src/plugins/platforms/windows/qwindowstabletsupport.cpp
index fa209f09c4..44b94d044d 100644
--- a/src/plugins/platforms/windows/qwindowstabletsupport.cpp
+++ b/src/plugins/platforms/windows/qwindowstabletsupport.cpp
@@ -435,6 +435,27 @@ bool QWindowsTabletSupport::translateTabletProximityEvent(WPARAM /* wParam */, L
m_currentDevice = m_devices.size();
m_devices.push_back(tabletInit(uniqueId, cursorType));
}
+
+ /**
+ * We should check button map for changes on every proximity event, not
+ * only during initialization phase.
+ *
+ * WARNING: in 2016 there were some Wacom table drivers, which could mess up
+ * button mapping if the remapped button was pressed, while the
+ * application **didn't have input focus**. This bug is somehow
+ * related to the fact that Wacom drivers allow user to configure
+ * per-application button-mappings. If the bug shows up again,
+ * just move this button-map fetching into initialization block.
+ *
+ * See https://bugs.kde.org/show_bug.cgi?id=359561
+ */
+ BYTE logicalButtons[32];
+ memset(logicalButtons, 0, 32);
+ m_winTab32DLL.wTInfo(WTI_CURSORS + currentCursor, CSR_SYSBTNMAP, &logicalButtons);
+ m_devices[m_currentDevice].buttonsMap[0x1] = logicalButtons[0];
+ m_devices[m_currentDevice].buttonsMap[0x2] = logicalButtons[1];
+ m_devices[m_currentDevice].buttonsMap[0x4] = logicalButtons[2];
+
m_devices[m_currentDevice].currentPointerType = pointerType(currentCursor);
m_state = PenProximity;
qCDebug(lcQpaTablet) << "enter proximity for device #"
@@ -446,6 +467,52 @@ bool QWindowsTabletSupport::translateTabletProximityEvent(WPARAM /* wParam */, L
return true;
}
+Qt::MouseButton buttonValueToEnum(DWORD button,
+ const QWindowsTabletDeviceData &tdd) {
+
+ enum : unsigned {
+ leftButtonValue = 0x1,
+ middleButtonValue = 0x2,
+ rightButtonValue = 0x4,
+ doubleClickButtonValue = 0x7
+ };
+
+ button = tdd.buttonsMap.value(button);
+
+ return button == leftButtonValue ? Qt::LeftButton :
+ button == rightButtonValue ? Qt::RightButton :
+ button == doubleClickButtonValue ? Qt::MiddleButton :
+ button == middleButtonValue ? Qt::MiddleButton :
+ button ? Qt::LeftButton /* fallback item */ :
+ Qt::NoButton;
+}
+
+Qt::MouseButtons convertTabletButtons(DWORD btnNew,
+ const QWindowsTabletDeviceData &tdd) {
+
+ Qt::MouseButtons buttons = Qt::NoButton;
+ for (unsigned int i = 0; i < 3; i++) {
+ unsigned int btn = 0x1 << i;
+
+ if (btn & btnNew) {
+ Qt::MouseButton convertedButton =
+ buttonValueToEnum(btn, tdd);
+
+ buttons |= convertedButton;
+
+ /**
+ * If a button that is present in hardware input is
+ * mapped to a Qt::NoButton, it means that it is going
+ * to be eaten by the driver, for example by its
+ * "Pan/Scroll" feature. Therefore we shouldn't handle
+ * any of the events associated to it. We'll just return
+ * Qt::NoButtons here.
+ */
+ }
+ }
+ return buttons;
+}
+
bool QWindowsTabletSupport::translateTabletPacketEvent()
{
static PACKET localPacketBuf[TabletPacketQSize]; // our own tablet packet queue.
@@ -552,9 +619,12 @@ bool QWindowsTabletSupport::translateTabletPacketEvent()
<< tiltY << "tanP:" << tangentialPressure << "rotation:" << rotation;
}
+ Qt::MouseButtons buttons =
+ convertTabletButtons(packet.pkButtons, m_devices.at(m_currentDevice));
+
QWindowSystemInterface::handleTabletEvent(target, packet.pkTime, QPointF(localPos), globalPosF,
currentDevice, currentPointer,
- static_cast<Qt::MouseButtons>(packet.pkButtons),
+ buttons,
pressureNew, tiltX, tiltY,
tangentialPressure, rotation, z,
uniqueId,
diff --git a/src/plugins/platforms/windows/qwindowstabletsupport.h b/src/plugins/platforms/windows/qwindowstabletsupport.h
index d91701d6a5..8f97982308 100644
--- a/src/plugins/platforms/windows/qwindowstabletsupport.h
+++ b/src/plugins/platforms/windows/qwindowstabletsupport.h
@@ -45,6 +45,7 @@
#include <QtCore/qvector.h>
#include <QtCore/qpoint.h>
+#include <QtCore/qhash.h>
#include <wintab.h>
@@ -100,6 +101,7 @@ struct QWindowsTabletDeviceData
qint64 uniqueId = 0;
int currentDevice = 0;
int currentPointerType = 0;
+ QHash<quint8, quint8> buttonsMap;
};
#ifndef QT_NO_DEBUG_STREAM
diff --git a/src/plugins/platforms/windows/qwindowstheme.cpp b/src/plugins/platforms/windows/qwindowstheme.cpp
index 6dd3e6ed56..a6b9781252 100644
--- a/src/plugins/platforms/windows/qwindowstheme.cpp
+++ b/src/plugins/platforms/windows/qwindowstheme.cpp
@@ -284,24 +284,24 @@ static inline QPalette systemPalette()
result.setColor(QPalette::Link, Qt::blue);
result.setColor(QPalette::LinkVisited, Qt::magenta);
result.setColor(QPalette::Inactive, QPalette::Button, result.button().color());
- result.setColor(QPalette::Inactive, QPalette::Window, result.background().color());
+ result.setColor(QPalette::Inactive, QPalette::Window, result.window().color());
result.setColor(QPalette::Inactive, QPalette::Light, result.light().color());
result.setColor(QPalette::Inactive, QPalette::Dark, result.dark().color());
if (result.midlight() == result.button())
result.setColor(QPalette::Midlight, result.button().color().lighter(110));
- if (result.background() != result.base()) {
+ if (result.window() != result.base()) {
result.setColor(QPalette::Inactive, QPalette::Highlight, result.color(QPalette::Inactive, QPalette::Window));
result.setColor(QPalette::Inactive, QPalette::HighlightedText, result.color(QPalette::Inactive, QPalette::Text));
}
const QColor disabled =
- mixColors(result.foreground().color(), result.button().color());
+ mixColors(result.windowText().color(), result.button().color());
- result.setColorGroup(QPalette::Disabled, result.foreground(), result.button(),
+ result.setColorGroup(QPalette::Disabled, result.windowText(), result.button(),
result.light(), result.dark(), result.mid(),
result.text(), result.brightText(), result.base(),
- result.background());
+ result.window());
result.setColor(QPalette::Disabled, QPalette::WindowText, disabled);
result.setColor(QPalette::Disabled, QPalette::Text, disabled);
result.setColor(QPalette::Disabled, QPalette::ButtonText, disabled);
@@ -310,7 +310,7 @@ static inline QPalette systemPalette()
result.setColor(QPalette::Disabled, QPalette::HighlightedText,
getSysColor(COLOR_HIGHLIGHTTEXT));
result.setColor(QPalette::Disabled, QPalette::Base,
- result.background().color());
+ result.window().color());
return result;
}
@@ -333,7 +333,7 @@ static inline QPalette toolTipPalette(const QPalette &systemPalette)
result.setColor(QPalette::All, QPalette::ToolTipBase, tipBgColor);
result.setColor(QPalette::All, QPalette::ToolTipText, tipTextColor);
const QColor disabled =
- mixColors(result.foreground().color(), result.button().color());
+ mixColors(result.windowText().color(), result.button().color());
result.setColor(QPalette::Disabled, QPalette::WindowText, disabled);
result.setColor(QPalette::Disabled, QPalette::Text, disabled);
result.setColor(QPalette::Disabled, QPalette::ToolTipText, disabled);
@@ -858,7 +858,8 @@ QPixmap QWindowsFileIconEngine::filePixmap(const QSize &size, QIcon::Mode, QIcon
int iIcon = (useDefaultFolderIcon && defaultFolderIIcon >= 0) ? defaultFolderIIcon
: **dirIconEntryCache.object(filePath);
if (iIcon) {
- QPixmapCache::find(dirIconPixmapCacheKey(iIcon, iconSize, requestedImageListSize), pixmap);
+ QPixmapCache::find(dirIconPixmapCacheKey(iIcon, iconSize, requestedImageListSize),
+ &pixmap);
if (pixmap.isNull()) // Let's keep both caches in sync
dirIconEntryCache.remove(filePath);
else
@@ -889,7 +890,7 @@ QPixmap QWindowsFileIconEngine::filePixmap(const QSize &size, QIcon::Mode, QIcon
//using the unique icon index provided by windows save us from duplicate keys
key = dirIconPixmapCacheKey(info.iIcon, iconSize, requestedImageListSize);
- QPixmapCache::find(key, pixmap);
+ QPixmapCache::find(key, &pixmap);
if (!pixmap.isNull()) {
QMutexLocker locker(&mx);
dirIconEntryCache.insert(filePath, FakePointer<int>::create(info.iIcon));
diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp
index eb521dac52..38d0f4d979 100644
--- a/src/plugins/platforms/windows/qwindowswindow.cpp
+++ b/src/plugins/platforms/windows/qwindowswindow.cpp
@@ -1258,6 +1258,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;
QWindowsWindow::QWindowsWindow(QWindow *aWindow, const QWindowsWindowData &data) :
QWindowsBaseWindow(aWindow),
@@ -1295,7 +1296,7 @@ QWindowsWindow::QWindowsWindow(QWindow *aWindow, const QWindowsWindowData &data)
if (aWindow->isTopLevel())
setWindowIcon(aWindow->icon());
- if (aWindow->property(hasBorderInFullScreenProperty).toBool())
+ if (m_borderInFullScreenDefault || aWindow->property(hasBorderInFullScreenProperty).toBool())
setFlag(HasBorderInFullScreen);
clearFlag(WithinCreate);
}
@@ -2954,6 +2955,11 @@ void QWindowsWindow::setHasBorderInFullScreenStatic(QWindow *window, bool border
window->setProperty(hasBorderInFullScreenProperty, QVariant(border));
}
+void QWindowsWindow::setHasBorderInFullScreenDefault(bool border)
+{
+ m_borderInFullScreenDefault = border;
+}
+
void QWindowsWindow::setHasBorderInFullScreen(bool border)
{
if (testFlag(HasBorderInFullScreen) == border)
diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h
index ce67e46df3..958564aa86 100644
--- a/src/plugins/platforms/windows/qwindowswindow.h
+++ b/src/plugins/platforms/windows/qwindowswindow.h
@@ -340,6 +340,7 @@ public:
static void setTouchWindowTouchTypeStatic(QWindow *window, QWindowsWindowFunctions::TouchWindowTouchTypes touchTypes);
void registerTouchWindow(QWindowsWindowFunctions::TouchWindowTouchTypes touchTypes = QWindowsWindowFunctions::NormalTouch);
static void setHasBorderInFullScreenStatic(QWindow *window, bool border);
+ static void setHasBorderInFullScreenDefault(bool border);
void setHasBorderInFullScreen(bool border);
static QString formatWindowTitle(const QString &title);
@@ -385,6 +386,7 @@ private:
// note: intentionally not using void * in order to avoid breaking x86
VkSurfaceKHR m_vkSurface = 0;
#endif
+ static bool m_borderInFullScreenDefault;
};
#ifndef QT_NO_DEBUG_STREAM
diff --git a/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp b/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp
index 6ddcec0256..8d958aae94 100644
--- a/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp
+++ b/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp
@@ -459,7 +459,7 @@ static QPixmap qt_patternForAlpha(uchar alpha, int screen)
% HexString<uchar>(alpha)
% HexString<int>(screen);
- if (!QPixmapCache::find(key, pm)) {
+ if (!QPixmapCache::find(key, &pm)) {
// #### why not use a mono image here????
QImage pattern(DITHER_SIZE, DITHER_SIZE, QImage::Format_ARGB32);
pattern.fill(0xffffffff);
diff --git a/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11_p.h b/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11_p.h
index 9b01c0a3fc..bc82351283 100644
--- a/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11_p.h
+++ b/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11_p.h
@@ -116,7 +116,7 @@ protected:
friend GC qt_x11_get_brush_gc(QPainter *);
private:
- Q_DISABLE_COPY(QX11PaintEngine)
+ Q_DISABLE_COPY_MOVE(QX11PaintEngine)
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/qxcbbackingstore.cpp b/src/plugins/platforms/xcb/qxcbbackingstore.cpp
index f9240a45cc..741317d766 100644
--- a/src/plugins/platforms/xcb/qxcbbackingstore.cpp
+++ b/src/plugins/platforms/xcb/qxcbbackingstore.cpp
@@ -641,17 +641,17 @@ void QXcbBackingStoreImage::flushPixmap(const QRegion &region, bool fullRegion)
xcb_subimage.bit_order = m_xcb_image->bit_order;
const bool needsByteSwap = xcb_subimage.byte_order != m_xcb_image->byte_order;
+ // Ensure that we don't send more than maxPutImageRequestDataBytes per request.
+ const auto maxPutImageRequestDataBytes = connection()->maxRequestDataBytes(sizeof(xcb_put_image_request_t));
for (const QRect &rect : region) {
- // We must make sure that each request is not larger than max_req_size.
- // Each request takes req_size + m_xcb_image->stride * height bytes.
- static const uint32_t req_size = sizeof(xcb_put_image_request_t);
- const uint32_t max_req_size = xcb_get_maximum_request_length(xcb_connection());
- const int rows_per_put = (max_req_size - req_size) / m_xcb_image->stride;
+ const quint32 stride = round_up_scanline(rect.width() * m_qimage.depth(), xcb_subimage.scanline_pad) >> 3;
+ const int rows_per_put = maxPutImageRequestDataBytes / stride;
// This assert could trigger if a single row has more pixels than fit in
- // a single PutImage request. However, max_req_size is guaranteed to be
- // at least 16384 bytes. That should be enough for quite large images.
+ // a single PutImage request. In the absence of the BIG-REQUESTS extension
+ // the theoretical maximum lengths of maxPutImageRequestDataBytes can be
+ // roughly 256kB.
Q_ASSERT(rows_per_put > 0);
// If we upload the whole image in a single chunk, the result might be
@@ -666,9 +666,10 @@ void QXcbBackingStoreImage::flushPixmap(const QRegion &region, bool fullRegion)
while (height > 0) {
const int rows = std::min(height, rows_per_put);
const QRect subRect(x, y, width, rows);
- const quint32 stride = round_up_scanline(width * m_qimage.depth(), xcb_subimage.scanline_pad) >> 3;
const QImage subImage = native_sub_image(&m_flushBuffer, stride, m_qimage, subRect, needsByteSwap);
+ Q_ASSERT(static_cast<size_t>(subImage.sizeInBytes()) <= maxPutImageRequestDataBytes);
+
xcb_subimage.width = width;
xcb_subimage.height = rows;
xcb_subimage.data = const_cast<uint8_t *>(subImage.constBits());
diff --git a/src/plugins/platforms/xcb/qxcbclipboard.cpp b/src/plugins/platforms/xcb/qxcbclipboard.cpp
index ac8b029916..2cb6720d40 100644
--- a/src/plugins/platforms/xcb/qxcbclipboard.cpp
+++ b/src/plugins/platforms/xcb/qxcbclipboard.cpp
@@ -240,8 +240,8 @@ QXcbClipboard::QXcbClipboard(QXcbConnection *c)
xcb_xfixes_select_selection_input_checked(xcb_connection(), m_owner, atom(QXcbAtom::CLIPBOARD), mask);
}
- // change property protocol request is 24 bytes
- m_increment = (xcb_get_maximum_request_length(xcb_connection()) * 4) - 24;
+ // xcb_change_property_request_t and xcb_get_property_request_t are the same size
+ m_maxPropertyRequestDataBytes = connection()->maxRequestDataBytes(sizeof(xcb_change_property_request_t));
}
QXcbClipboard::~QXcbClipboard()
@@ -486,7 +486,7 @@ xcb_atom_t QXcbClipboard::sendSelection(QMimeData *d, xcb_atom_t target, xcb_win
if (m_clipboard_closing)
allow_incr = false;
- if (data.size() > m_increment && allow_incr) {
+ if (data.size() > m_maxPropertyRequestDataBytes && allow_incr) {
long bytes = data.size();
xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, window, property,
atom(QXcbAtom::INCR), 32, 1, (const void *)&bytes);
@@ -496,7 +496,7 @@ xcb_atom_t QXcbClipboard::sendSelection(QMimeData *d, xcb_atom_t target, xcb_win
}
// make sure we can perform the XChangeProperty in a single request
- if (data.size() > m_increment)
+ if (data.size() > m_maxPropertyRequestDataBytes)
return XCB_NONE; // ### perhaps use several XChangeProperty calls w/ PropModeAppend?
int dataSize = data.size() / (dataFormat / 8);
// use a single request to transfer data
@@ -678,17 +678,8 @@ void QXcbClipboard::handleXFixesSelectionRequest(xcb_xfixes_selection_notify_eve
emitChanged(mode);
}
-
-static inline int maxSelectionIncr(xcb_connection_t *c)
-{
- int l = xcb_get_maximum_request_length(c);
- return (l > 65536 ? 65536*4 : l*4) - 100;
-}
-
bool QXcbClipboard::clipboardReadProperty(xcb_window_t win, xcb_atom_t property, bool deleteProperty, QByteArray *buffer, int *size, xcb_atom_t *type, int *format)
{
- int maxsize = maxSelectionIncr(xcb_connection());
- ulong bytes_left; // bytes_after
xcb_atom_t dummy_type;
int dummy_format;
@@ -705,7 +696,8 @@ bool QXcbClipboard::clipboardReadProperty(xcb_window_t win, xcb_atom_t property,
}
*type = reply->type;
*format = reply->format;
- bytes_left = reply->bytes_after;
+
+ auto bytes_left = reply->bytes_after;
int offset = 0, buffer_offset = 0;
@@ -720,7 +712,8 @@ bool QXcbClipboard::clipboardReadProperty(xcb_window_t win, xcb_atom_t property,
while (bytes_left) {
// more to read...
- reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), false, win, property, XCB_GET_PROPERTY_TYPE_ANY, offset, maxsize/4);
+ reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), false, win, property,
+ XCB_GET_PROPERTY_TYPE_ANY, offset, m_maxPropertyRequestDataBytes / 4);
if (!reply || reply->type == XCB_NONE)
break;
diff --git a/src/plugins/platforms/xcb/qxcbclipboard.h b/src/plugins/platforms/xcb/qxcbclipboard.h
index 26d3b3b395..51ae0dc1ee 100644
--- a/src/plugins/platforms/xcb/qxcbclipboard.h
+++ b/src/plugins/platforms/xcb/qxcbclipboard.h
@@ -113,7 +113,7 @@ public:
xcb_window_t getSelectionOwner(xcb_atom_t atom) const;
QByteArray getSelection(xcb_atom_t selection, xcb_atom_t target, xcb_atom_t property, xcb_timestamp_t t = 0);
- int increment() const { return m_increment; }
+ int increment() const { return m_maxPropertyRequestDataBytes; }
int clipboardTimeout() const { return clipboard_timeout; }
void removeTransaction(xcb_window_t window) { m_transactions.remove(window); }
@@ -137,7 +137,7 @@ private:
static const int clipboard_timeout;
- int m_increment = 0;
+ int m_maxPropertyRequestDataBytes = 0;
bool m_clipboard_closing = false;
xcb_timestamp_t m_incr_receive_time = 0;
diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp
index 0d71a5a552..f98b2dfe3a 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection.cpp
@@ -132,6 +132,14 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra
if (!m_startupId.isNull())
qunsetenv("DESKTOP_STARTUP_ID");
+ const int focusInDelay = 100;
+ m_focusInTimer.setSingleShot(true);
+ m_focusInTimer.setInterval(focusInDelay);
+ m_focusInTimer.callOnTimeout([]() {
+ // No FocusIn events for us, proceed with FocusOut normally.
+ QWindowSystemInterface::handleWindowActivated(nullptr, Qt::ActiveWindowFocusReason);
+ });
+
sync();
}
@@ -731,11 +739,6 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
m_glIntegration->handleXcbEvent(event, response_type);
}
-void QXcbConnection::addPeekFunc(PeekFunc f)
-{
- m_peekFuncs.append(f);
-}
-
void QXcbConnection::setFocusWindow(QWindow *w)
{
m_focusWindow = w ? static_cast<QXcbWindow *>(w->handle()) : nullptr;
@@ -1015,15 +1018,6 @@ void QXcbConnection::processXcbEvents(QEventLoop::ProcessEventsFlags flags)
if (compressEvent(event))
continue;
- auto isWaitingFor = [=](PeekFunc peekFunc) {
- // These callbacks return true if the event is what they were
- // waiting for, remove them from the list in that case.
- return peekFunc(this, event);
- };
- m_peekFuncs.erase(std::remove_if(m_peekFuncs.begin(), m_peekFuncs.end(),
- isWaitingFor),
- m_peekFuncs.end());
-
handleXcbEvent(event);
// The lock-based solution used to free the lock inside this loop,
@@ -1032,12 +1026,6 @@ void QXcbConnection::processXcbEvents(QEventLoop::ProcessEventsFlags flags)
m_eventQueue->flushBufferedEvents();
}
- // Indicate with a null event that the event the callbacks are waiting for
- // is not in the queue currently.
- for (PeekFunc f : qAsConst(m_peekFuncs))
- f(this, nullptr);
- m_peekFuncs.clear();
-
xcb_flush(xcb_connection());
}
diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h
index 15537fede4..bbfa252881 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.h
+++ b/src/plugins/platforms/xcb/qxcbconnection.h
@@ -43,6 +43,7 @@
#include <xcb/xcb.h>
#include <xcb/randr.h>
+#include <QtCore/QTimer>
#include <QtGui/private/qtguiglobal_p.h>
#include "qxcbexport.h"
#include <QHash>
@@ -183,9 +184,6 @@ public:
QXcbWindowEventListener *windowEventListenerFromId(xcb_window_t id);
QXcbWindow *platformWindowFromId(xcb_window_t id);
- typedef bool (*PeekFunc)(QXcbConnection *, xcb_generic_event_t *);
- void addPeekFunc(PeekFunc f);
-
inline xcb_timestamp_t time() const { return m_time; }
inline void setTime(xcb_timestamp_t t) { if (timeGreaterThan(t, m_time)) m_time = t; }
@@ -247,6 +245,8 @@ public:
void flush() { xcb_flush(xcb_connection()); }
void processXcbEvents(QEventLoop::ProcessEventsFlags flags);
+ QTimer &focusInTimer() { return m_focusInTimer; }
+
protected:
bool event(QEvent *e) override;
@@ -366,8 +366,6 @@ private:
WindowMapper m_mapper;
- QVector<PeekFunc> m_peekFuncs;
-
Qt::MouseButtons m_buttonState = 0;
Qt::MouseButton m_button = Qt::NoButton;
@@ -388,6 +386,8 @@ private:
friend class QXcbEventQueue;
QByteArray m_xdgCurrentDesktop;
+ QTimer m_focusInTimer;
+
};
#if QT_CONFIG(xcb_xinput)
#if QT_CONFIG(tabletevent)
diff --git a/src/plugins/platforms/xcb/qxcbconnection_basic.cpp b/src/plugins/platforms/xcb/qxcbconnection_basic.cpp
index af72285135..9a028e5a7e 100644
--- a/src/plugins/platforms/xcb/qxcbconnection_basic.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection_basic.cpp
@@ -134,6 +134,7 @@ QXcbBasicConnection::QXcbBasicConnection(const char *displayName)
m_setup = xcb_get_setup(m_xcbConnection);
m_xcbAtom.initialize(m_xcbConnection);
+ m_maximumRequestLength = xcb_get_maximum_request_length(m_xcbConnection);
xcb_extension_t *extensions[] = {
&xcb_shm_id, &xcb_xfixes_id, &xcb_randr_id, &xcb_shape_id, &xcb_sync_id,
@@ -178,6 +179,14 @@ QXcbBasicConnection::~QXcbBasicConnection()
}
}
+size_t QXcbBasicConnection::maxRequestDataBytes(size_t requestSize) const
+{
+ if (hasBigRequest())
+ requestSize += 4; // big-request encoding adds 4 bytes
+
+ return m_maximumRequestLength * 4 - requestSize;
+}
+
xcb_atom_t QXcbBasicConnection::internAtom(const char *name)
{
if (!name || *name == 0)
@@ -199,6 +208,11 @@ QByteArray QXcbBasicConnection::atomName(xcb_atom_t atom)
return QByteArray();
}
+bool QXcbBasicConnection::hasBigRequest() const
+{
+ return m_maximumRequestLength > m_setup->maximum_request_length;
+}
+
#if QT_CONFIG(xcb_xinput)
// Starting from the xcb version 1.9.3 struct xcb_ge_event_t has changed:
// - "pad0" became "extension"
diff --git a/src/plugins/platforms/xcb/qxcbconnection_basic.h b/src/plugins/platforms/xcb/qxcbconnection_basic.h
index 6e407a5f80..b979129cba 100644
--- a/src/plugins/platforms/xcb/qxcbconnection_basic.h
+++ b/src/plugins/platforms/xcb/qxcbconnection_basic.h
@@ -74,6 +74,8 @@ public:
}
const xcb_setup_t *setup() const { return m_setup; }
+ size_t maxRequestDataBytes(size_t requestSize) const;
+
inline xcb_atom_t atom(QXcbAtom::Atom qatom) const { return m_xcbAtom.atom(qatom); }
QXcbAtom::Atom qatom(xcb_atom_t atom) const { return m_xcbAtom.qatom(atom); }
xcb_atom_t internAtom(const char *name);
@@ -95,6 +97,7 @@ public:
bool hasShmFd() const { return m_hasShmFd; }
bool hasXSync() const { return m_hasXSync; }
bool hasXinerama() const { return m_hasXinerama; }
+ bool hasBigRequest() const;
#if QT_CONFIG(xcb_xinput)
bool isAtLeastXI21() const { return m_xi2Enabled && m_xi2Minor >= 1; }
@@ -153,6 +156,8 @@ private:
uint32_t m_xfixesFirstEvent = 0;
uint32_t m_xrandrFirstEvent = 0;
uint32_t m_xkbFirstEvent = 0;
+
+ uint32_t m_maximumRequestLength = 0;
};
#define Q_XCB_REPLY_CONNECTION_ARG(connection, ...) connection
diff --git a/src/plugins/platforms/xcb/qxcbconnection_screens.cpp b/src/plugins/platforms/xcb/qxcbconnection_screens.cpp
index 4e631beb25..9ba71ada37 100644
--- a/src/plugins/platforms/xcb/qxcbconnection_screens.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection_screens.cpp
@@ -210,7 +210,7 @@ void QXcbConnection::updateScreen(QXcbScreen *screen, const xcb_randr_output_cha
const int idx = m_screens.indexOf(screen);
if (idx > 0) {
qAsConst(m_screens).first()->setPrimary(false);
- m_screens.swap(0, idx);
+ m_screens.swapItemsAt(0, idx);
}
screen->virtualDesktop()->setPrimaryScreen(screen);
QWindowSystemInterface::handlePrimaryScreenChanged(screen);
@@ -262,7 +262,7 @@ void QXcbConnection::destroyScreen(QXcbScreen *screen)
newPrimary->setPrimary(true);
const int idx = m_screens.indexOf(newPrimary);
if (idx > 0)
- m_screens.swap(0, idx);
+ m_screens.swapItemsAt(0, idx);
QWindowSystemInterface::handlePrimaryScreenChanged(newPrimary);
}
diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
index 450706fc53..bc09fe2f91 100644
--- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
@@ -641,7 +641,7 @@ void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindo
qreal nx = -1.0, ny = -1.0;
qreal w = 0.0, h = 0.0;
bool majorAxisIsY = touchPoint.area.height() > touchPoint.area.width();
- for (const TouchDeviceData::ValuatorClassInfo vci : dev->valuatorInfo) {
+ for (const TouchDeviceData::ValuatorClassInfo &vci : qAsConst(dev->valuatorInfo)) {
double value;
if (!xi2GetValuatorValueIfSet(xiDeviceEvent, vci.number, &value))
continue;
@@ -827,7 +827,7 @@ bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab)
| XCB_INPUT_XI_EVENT_MASK_TOUCH_UPDATE
| XCB_INPUT_XI_EVENT_MASK_TOUCH_END;
- for (int id : m_xiMasterPointerIds) {
+ 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,
@@ -845,7 +845,7 @@ bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab)
free(reply);
}
} else { // ungrab
- for (int id : m_xiMasterPointerIds) {
+ for (int id : qAsConst(m_xiMasterPointerIds)) {
auto cookie = xcb_input_xi_ungrab_device_checked(xcb_connection(), XCB_CURRENT_TIME, id);
xcb_generic_error_t *error = xcb_request_check(xcb_connection(), cookie);
if (error) {
diff --git a/src/plugins/platforms/xcb/qxcbeventqueue.cpp b/src/plugins/platforms/xcb/qxcbeventqueue.cpp
index 4ca73e3048..759ee3cc95 100644
--- a/src/plugins/platforms/xcb/qxcbeventqueue.cpp
+++ b/src/plugins/platforms/xcb/qxcbeventqueue.cpp
@@ -264,7 +264,7 @@ qint32 QXcbEventQueue::generatePeekerId()
bool QXcbEventQueue::removePeekerId(qint32 peekerId)
{
- const auto it = m_peekerToNode.find(peekerId);
+ const auto it = m_peekerToNode.constFind(peekerId);
if (it == m_peekerToNode.constEnd()) {
qCWarning(lcQpaXcb, "failed to remove unknown peeker id: %d", peekerId);
return false;
@@ -283,7 +283,7 @@ bool QXcbEventQueue::peekEventQueue(PeekerCallback peeker, void *peekerData,
const bool peekerIdProvided = peekerId != -1;
auto peekerToNodeIt = m_peekerToNode.find(peekerId);
- if (peekerIdProvided && peekerToNodeIt == m_peekerToNode.constEnd()) {
+ if (peekerIdProvided && peekerToNodeIt == m_peekerToNode.end()) {
qCWarning(lcQpaXcb, "failed to find index for unknown peeker id: %d", peekerId);
return false;
}
@@ -343,7 +343,7 @@ bool QXcbEventQueue::peekEventQueue(PeekerCallback peeker, void *peekerData,
// Before updating, make sure that a peeker callback did not remove
// the peeker id.
peekerToNodeIt = m_peekerToNode.find(peekerId);
- if (peekerToNodeIt != m_peekerToNode.constEnd())
+ if (peekerToNodeIt != m_peekerToNode.end())
*peekerToNodeIt = node; // id still in the cache, update node
}
diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp
index ed9e87a036..95ca40fc95 100644
--- a/src/plugins/platforms/xcb/qxcbintegration.cpp
+++ b/src/plugins/platforms/xcb/qxcbintegration.cpp
@@ -357,6 +357,8 @@ void QXcbIntegration::initialize()
m_inputContext.reset(QPlatformInputContextFactory::create(icStr));
if (!m_inputContext && icStr != defaultInputContext && icStr != QLatin1String("none"))
m_inputContext.reset(QPlatformInputContextFactory::create(defaultInputContext));
+
+ defaultConnection()->keyboard()->initialize();
}
void QXcbIntegration::moveToScreen(QWindow *window, int screen)
@@ -548,6 +550,7 @@ void QXcbIntegration::beep() const
return;
xcb_connection_t *connection = static_cast<QXcbScreen *>(screen)->xcb_connection();
xcb_bell(connection, 0);
+ xcb_flush(connection);
}
bool QXcbIntegration::nativePaintingEnabled() const
diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.cpp b/src/plugins/platforms/xcb/qxcbkeyboard.cpp
index c5dc7b21ad..d0e02ecdd1 100644
--- a/src/plugins/platforms/xcb/qxcbkeyboard.cpp
+++ b/src/plugins/platforms/xcb/qxcbkeyboard.cpp
@@ -39,7 +39,6 @@
#include "qxcbkeyboard.h"
#include "qxcbwindow.h"
#include "qxcbscreen.h"
-#include "qxcbxkbcommon.h"
#include <qpa/qwindowsysteminterface.h>
#include <qpa/qplatforminputcontext.h>
@@ -49,404 +48,20 @@
#include <QtCore/QMetaEnum>
#include <private/qguiapplication_p.h>
-#include <private/qmakearray_p.h>
-#include <xkbcommon/xkbcommon-keysyms.h>
+#if QT_CONFIG(xkb)
+#include <xkbcommon/xkbcommon-x11.h>
+#endif
#if QT_CONFIG(xcb_xinput)
#include <xcb/xinput.h>
#endif
-QT_BEGIN_NAMESPACE
-
-typedef struct xkb2qt
-{
- unsigned int xkb;
- unsigned int qt;
-
- constexpr bool operator <=(const xkb2qt &that) const noexcept
- {
- return xkb <= that.xkb;
- }
-
- constexpr bool operator <(const xkb2qt &that) const noexcept
- {
- return xkb < that.xkb;
- }
-} xkb2qt_t;
-template<std::size_t Xkb, std::size_t Qt>
-struct Xkb2Qt
-{
- using Type = xkb2qt_t;
- static constexpr Type data() noexcept { return Type{Xkb, Qt}; }
-};
-
-static constexpr const auto KeyTbl = qMakeArray(
- QSortedData<
- // misc keys
-
- Xkb2Qt<XKB_KEY_Escape, Qt::Key_Escape>,
- Xkb2Qt<XKB_KEY_Tab, Qt::Key_Tab>,
- Xkb2Qt<XKB_KEY_ISO_Left_Tab, Qt::Key_Backtab>,
- Xkb2Qt<XKB_KEY_BackSpace, Qt::Key_Backspace>,
- Xkb2Qt<XKB_KEY_Return, Qt::Key_Return>,
- Xkb2Qt<XKB_KEY_Insert, Qt::Key_Insert>,
- Xkb2Qt<XKB_KEY_Delete, Qt::Key_Delete>,
- Xkb2Qt<XKB_KEY_Clear, Qt::Key_Delete>,
- Xkb2Qt<XKB_KEY_Pause, Qt::Key_Pause>,
- Xkb2Qt<XKB_KEY_Print, Qt::Key_Print>,
- Xkb2Qt<0x1005FF60, Qt::Key_SysReq>, // hardcoded Sun SysReq
- Xkb2Qt<0x1007ff00, Qt::Key_SysReq>, // hardcoded X386 SysReq
-
- // cursor movement
-
- Xkb2Qt<XKB_KEY_Home, Qt::Key_Home>,
- Xkb2Qt<XKB_KEY_End, Qt::Key_End>,
- Xkb2Qt<XKB_KEY_Left, Qt::Key_Left>,
- Xkb2Qt<XKB_KEY_Up, Qt::Key_Up>,
- Xkb2Qt<XKB_KEY_Right, Qt::Key_Right>,
- Xkb2Qt<XKB_KEY_Down, Qt::Key_Down>,
- Xkb2Qt<XKB_KEY_Prior, Qt::Key_PageUp>,
- Xkb2Qt<XKB_KEY_Next, Qt::Key_PageDown>,
-
- // modifiers
-
- Xkb2Qt<XKB_KEY_Shift_L, Qt::Key_Shift>,
- Xkb2Qt<XKB_KEY_Shift_R, Qt::Key_Shift>,
- Xkb2Qt<XKB_KEY_Shift_Lock, Qt::Key_Shift>,
- Xkb2Qt<XKB_KEY_Control_L, Qt::Key_Control>,
- Xkb2Qt<XKB_KEY_Control_R, Qt::Key_Control>,
- Xkb2Qt<XKB_KEY_Meta_L, Qt::Key_Meta>,
- Xkb2Qt<XKB_KEY_Meta_R, Qt::Key_Meta>,
- Xkb2Qt<XKB_KEY_Alt_L, Qt::Key_Alt>,
- Xkb2Qt<XKB_KEY_Alt_R, Qt::Key_Alt>,
- Xkb2Qt<XKB_KEY_Caps_Lock, Qt::Key_CapsLock>,
- Xkb2Qt<XKB_KEY_Num_Lock, Qt::Key_NumLock>,
- Xkb2Qt<XKB_KEY_Scroll_Lock, Qt::Key_ScrollLock>,
- Xkb2Qt<XKB_KEY_Super_L, Qt::Key_Super_L>,
- Xkb2Qt<XKB_KEY_Super_R, Qt::Key_Super_R>,
- Xkb2Qt<XKB_KEY_Menu, Qt::Key_Menu>,
- Xkb2Qt<XKB_KEY_Hyper_L, Qt::Key_Hyper_L>,
- Xkb2Qt<XKB_KEY_Hyper_R, Qt::Key_Hyper_R>,
- Xkb2Qt<XKB_KEY_Help, Qt::Key_Help>,
- Xkb2Qt<0x1000FF74, Qt::Key_Backtab>, // hardcoded HP backtab
- Xkb2Qt<0x1005FF10, Qt::Key_F11>, // hardcoded Sun F36 (labeled F11)
- Xkb2Qt<0x1005FF11, Qt::Key_F12>, // hardcoded Sun F37 (labeled F12)
-
- // numeric and function keypad keys
-
- Xkb2Qt<XKB_KEY_KP_Space, Qt::Key_Space>,
- Xkb2Qt<XKB_KEY_KP_Tab, Qt::Key_Tab>,
- Xkb2Qt<XKB_KEY_KP_Enter, Qt::Key_Enter>,
- //Xkb2Qt<XKB_KEY_KP_F1, Qt::Key_F1>,
- //Xkb2Qt<XKB_KEY_KP_F2, Qt::Key_F2>,
- //Xkb2Qt<XKB_KEY_KP_F3, Qt::Key_F3>,
- //Xkb2Qt<XKB_KEY_KP_F4, Qt::Key_F4>,
- Xkb2Qt<XKB_KEY_KP_Home, Qt::Key_Home>,
- Xkb2Qt<XKB_KEY_KP_Left, Qt::Key_Left>,
- Xkb2Qt<XKB_KEY_KP_Up, Qt::Key_Up>,
- Xkb2Qt<XKB_KEY_KP_Right, Qt::Key_Right>,
- Xkb2Qt<XKB_KEY_KP_Down, Qt::Key_Down>,
- Xkb2Qt<XKB_KEY_KP_Prior, Qt::Key_PageUp>,
- Xkb2Qt<XKB_KEY_KP_Next, Qt::Key_PageDown>,
- Xkb2Qt<XKB_KEY_KP_End, Qt::Key_End>,
- Xkb2Qt<XKB_KEY_KP_Begin, Qt::Key_Clear>,
- Xkb2Qt<XKB_KEY_KP_Insert, Qt::Key_Insert>,
- Xkb2Qt<XKB_KEY_KP_Delete, Qt::Key_Delete>,
- Xkb2Qt<XKB_KEY_KP_Equal, Qt::Key_Equal>,
- Xkb2Qt<XKB_KEY_KP_Multiply, Qt::Key_Asterisk>,
- Xkb2Qt<XKB_KEY_KP_Add, Qt::Key_Plus>,
- Xkb2Qt<XKB_KEY_KP_Separator, Qt::Key_Comma>,
- Xkb2Qt<XKB_KEY_KP_Subtract, Qt::Key_Minus>,
- Xkb2Qt<XKB_KEY_KP_Decimal, Qt::Key_Period>,
- Xkb2Qt<XKB_KEY_KP_Divide, Qt::Key_Slash>,
-
- // special non-XF86 function keys
-
- Xkb2Qt<XKB_KEY_Undo, Qt::Key_Undo>,
- Xkb2Qt<XKB_KEY_Redo, Qt::Key_Redo>,
- Xkb2Qt<XKB_KEY_Find, Qt::Key_Find>,
- Xkb2Qt<XKB_KEY_Cancel, Qt::Key_Cancel>,
-
- // International input method support keys
-
- // International & multi-key character composition
- Xkb2Qt<XKB_KEY_ISO_Level3_Shift, Qt::Key_AltGr>,
- Xkb2Qt<XKB_KEY_Multi_key, Qt::Key_Multi_key>,
- Xkb2Qt<XKB_KEY_Codeinput, Qt::Key_Codeinput>,
- Xkb2Qt<XKB_KEY_SingleCandidate, Qt::Key_SingleCandidate>,
- Xkb2Qt<XKB_KEY_MultipleCandidate, Qt::Key_MultipleCandidate>,
- Xkb2Qt<XKB_KEY_PreviousCandidate, Qt::Key_PreviousCandidate>,
-
- // Misc Functions
- Xkb2Qt<XKB_KEY_Mode_switch, Qt::Key_Mode_switch>,
- Xkb2Qt<XKB_KEY_script_switch, Qt::Key_Mode_switch>,
-
- // Japanese keyboard support
- Xkb2Qt<XKB_KEY_Kanji, Qt::Key_Kanji>,
- Xkb2Qt<XKB_KEY_Muhenkan, Qt::Key_Muhenkan>,
- //Xkb2Qt<XKB_KEY_Henkan_Mode, Qt::Key_Henkan_Mode>,
- Xkb2Qt<XKB_KEY_Henkan_Mode, Qt::Key_Henkan>,
- Xkb2Qt<XKB_KEY_Henkan, Qt::Key_Henkan>,
- Xkb2Qt<XKB_KEY_Romaji, Qt::Key_Romaji>,
- Xkb2Qt<XKB_KEY_Hiragana, Qt::Key_Hiragana>,
- Xkb2Qt<XKB_KEY_Katakana, Qt::Key_Katakana>,
- Xkb2Qt<XKB_KEY_Hiragana_Katakana, Qt::Key_Hiragana_Katakana>,
- Xkb2Qt<XKB_KEY_Zenkaku, Qt::Key_Zenkaku>,
- Xkb2Qt<XKB_KEY_Hankaku, Qt::Key_Hankaku>,
- Xkb2Qt<XKB_KEY_Zenkaku_Hankaku, Qt::Key_Zenkaku_Hankaku>,
- Xkb2Qt<XKB_KEY_Touroku, Qt::Key_Touroku>,
- Xkb2Qt<XKB_KEY_Massyo, Qt::Key_Massyo>,
- Xkb2Qt<XKB_KEY_Kana_Lock, Qt::Key_Kana_Lock>,
- Xkb2Qt<XKB_KEY_Kana_Shift, Qt::Key_Kana_Shift>,
- Xkb2Qt<XKB_KEY_Eisu_Shift, Qt::Key_Eisu_Shift>,
- Xkb2Qt<XKB_KEY_Eisu_toggle, Qt::Key_Eisu_toggle>,
- //Xkb2Qt<XKB_KEY_Kanji_Bangou, Qt::Key_Kanji_Bangou>,
- //Xkb2Qt<XKB_KEY_Zen_Koho, Qt::Key_Zen_Koho>,
- //Xkb2Qt<XKB_KEY_Mae_Koho, Qt::Key_Mae_Koho>,
- Xkb2Qt<XKB_KEY_Kanji_Bangou, Qt::Key_Codeinput>,
- Xkb2Qt<XKB_KEY_Zen_Koho, Qt::Key_MultipleCandidate>,
- Xkb2Qt<XKB_KEY_Mae_Koho, Qt::Key_PreviousCandidate>,
-
- // Korean keyboard support
- Xkb2Qt<XKB_KEY_Hangul, Qt::Key_Hangul>,
- Xkb2Qt<XKB_KEY_Hangul_Start, Qt::Key_Hangul_Start>,
- Xkb2Qt<XKB_KEY_Hangul_End, Qt::Key_Hangul_End>,
- Xkb2Qt<XKB_KEY_Hangul_Hanja, Qt::Key_Hangul_Hanja>,
- Xkb2Qt<XKB_KEY_Hangul_Jamo, Qt::Key_Hangul_Jamo>,
- Xkb2Qt<XKB_KEY_Hangul_Romaja, Qt::Key_Hangul_Romaja>,
- //Xkb2Qt<XKB_KEY_Hangul_Codeinput, Qt::Key_Hangul_Codeinput>,
- Xkb2Qt<XKB_KEY_Hangul_Codeinput, Qt::Key_Codeinput>,
- Xkb2Qt<XKB_KEY_Hangul_Jeonja, Qt::Key_Hangul_Jeonja>,
- Xkb2Qt<XKB_KEY_Hangul_Banja, Qt::Key_Hangul_Banja>,
- Xkb2Qt<XKB_KEY_Hangul_PreHanja, Qt::Key_Hangul_PreHanja>,
- Xkb2Qt<XKB_KEY_Hangul_PostHanja, Qt::Key_Hangul_PostHanja>,
- //Xkb2Qt<XKB_KEY_Hangul_SingleCandidate,Qt::Key_Hangul_SingleCandidate>,
- //Xkb2Qt<XKB_KEY_Hangul_MultipleCandidate,Qt::Key_Hangul_MultipleCandidate>,
- //Xkb2Qt<XKB_KEY_Hangul_PreviousCandidate,Qt::Key_Hangul_PreviousCandidate>,
- Xkb2Qt<XKB_KEY_Hangul_SingleCandidate, Qt::Key_SingleCandidate>,
- Xkb2Qt<XKB_KEY_Hangul_MultipleCandidate,Qt::Key_MultipleCandidate>,
- Xkb2Qt<XKB_KEY_Hangul_PreviousCandidate,Qt::Key_PreviousCandidate>,
- Xkb2Qt<XKB_KEY_Hangul_Special, Qt::Key_Hangul_Special>,
- //Xkb2Qt<XKB_KEY_Hangul_switch, Qt::Key_Hangul_switch>,
- Xkb2Qt<XKB_KEY_Hangul_switch, Qt::Key_Mode_switch>,
-
- // dead keys
- Xkb2Qt<XKB_KEY_dead_grave, Qt::Key_Dead_Grave>,
- Xkb2Qt<XKB_KEY_dead_acute, Qt::Key_Dead_Acute>,
- Xkb2Qt<XKB_KEY_dead_circumflex, Qt::Key_Dead_Circumflex>,
- Xkb2Qt<XKB_KEY_dead_tilde, Qt::Key_Dead_Tilde>,
- Xkb2Qt<XKB_KEY_dead_macron, Qt::Key_Dead_Macron>,
- Xkb2Qt<XKB_KEY_dead_breve, Qt::Key_Dead_Breve>,
- Xkb2Qt<XKB_KEY_dead_abovedot, Qt::Key_Dead_Abovedot>,
- Xkb2Qt<XKB_KEY_dead_diaeresis, Qt::Key_Dead_Diaeresis>,
- Xkb2Qt<XKB_KEY_dead_abovering, Qt::Key_Dead_Abovering>,
- Xkb2Qt<XKB_KEY_dead_doubleacute, Qt::Key_Dead_Doubleacute>,
- Xkb2Qt<XKB_KEY_dead_caron, Qt::Key_Dead_Caron>,
- Xkb2Qt<XKB_KEY_dead_cedilla, Qt::Key_Dead_Cedilla>,
- Xkb2Qt<XKB_KEY_dead_ogonek, Qt::Key_Dead_Ogonek>,
- Xkb2Qt<XKB_KEY_dead_iota, Qt::Key_Dead_Iota>,
- Xkb2Qt<XKB_KEY_dead_voiced_sound, Qt::Key_Dead_Voiced_Sound>,
- Xkb2Qt<XKB_KEY_dead_semivoiced_sound, Qt::Key_Dead_Semivoiced_Sound>,
- Xkb2Qt<XKB_KEY_dead_belowdot, Qt::Key_Dead_Belowdot>,
- Xkb2Qt<XKB_KEY_dead_hook, Qt::Key_Dead_Hook>,
- Xkb2Qt<XKB_KEY_dead_horn, Qt::Key_Dead_Horn>,
- Xkb2Qt<XKB_KEY_dead_stroke, Qt::Key_Dead_Stroke>,
- Xkb2Qt<XKB_KEY_dead_abovecomma, Qt::Key_Dead_Abovecomma>,
- Xkb2Qt<XKB_KEY_dead_abovereversedcomma, Qt::Key_Dead_Abovereversedcomma>,
- Xkb2Qt<XKB_KEY_dead_doublegrave, Qt::Key_Dead_Doublegrave>,
- Xkb2Qt<XKB_KEY_dead_belowring, Qt::Key_Dead_Belowring>,
- Xkb2Qt<XKB_KEY_dead_belowmacron, Qt::Key_Dead_Belowmacron>,
- Xkb2Qt<XKB_KEY_dead_belowcircumflex, Qt::Key_Dead_Belowcircumflex>,
- Xkb2Qt<XKB_KEY_dead_belowtilde, Qt::Key_Dead_Belowtilde>,
- Xkb2Qt<XKB_KEY_dead_belowbreve, Qt::Key_Dead_Belowbreve>,
- Xkb2Qt<XKB_KEY_dead_belowdiaeresis, Qt::Key_Dead_Belowdiaeresis>,
- Xkb2Qt<XKB_KEY_dead_invertedbreve, Qt::Key_Dead_Invertedbreve>,
- Xkb2Qt<XKB_KEY_dead_belowcomma, Qt::Key_Dead_Belowcomma>,
- Xkb2Qt<XKB_KEY_dead_currency, Qt::Key_Dead_Currency>,
- Xkb2Qt<XKB_KEY_dead_a, Qt::Key_Dead_a>,
- Xkb2Qt<XKB_KEY_dead_A, Qt::Key_Dead_A>,
- Xkb2Qt<XKB_KEY_dead_e, Qt::Key_Dead_e>,
- Xkb2Qt<XKB_KEY_dead_E, Qt::Key_Dead_E>,
- Xkb2Qt<XKB_KEY_dead_i, Qt::Key_Dead_i>,
- Xkb2Qt<XKB_KEY_dead_I, Qt::Key_Dead_I>,
- Xkb2Qt<XKB_KEY_dead_o, Qt::Key_Dead_o>,
- Xkb2Qt<XKB_KEY_dead_O, Qt::Key_Dead_O>,
- Xkb2Qt<XKB_KEY_dead_u, Qt::Key_Dead_u>,
- Xkb2Qt<XKB_KEY_dead_U, Qt::Key_Dead_U>,
- Xkb2Qt<XKB_KEY_dead_small_schwa, Qt::Key_Dead_Small_Schwa>,
- Xkb2Qt<XKB_KEY_dead_capital_schwa, Qt::Key_Dead_Capital_Schwa>,
- Xkb2Qt<XKB_KEY_dead_greek, Qt::Key_Dead_Greek>,
- Xkb2Qt<XKB_KEY_dead_lowline, Qt::Key_Dead_Lowline>,
- Xkb2Qt<XKB_KEY_dead_aboveverticalline, Qt::Key_Dead_Aboveverticalline>,
- Xkb2Qt<XKB_KEY_dead_belowverticalline, Qt::Key_Dead_Belowverticalline>,
- Xkb2Qt<XKB_KEY_dead_longsolidusoverlay, Qt::Key_Dead_Longsolidusoverlay>,
-
- // Special keys from X.org - This include multimedia keys,
- // wireless/bluetooth/uwb keys, special launcher keys, etc.
- Xkb2Qt<XKB_KEY_XF86Back, Qt::Key_Back>,
- Xkb2Qt<XKB_KEY_XF86Forward, Qt::Key_Forward>,
- Xkb2Qt<XKB_KEY_XF86Stop, Qt::Key_Stop>,
- Xkb2Qt<XKB_KEY_XF86Refresh, Qt::Key_Refresh>,
- Xkb2Qt<XKB_KEY_XF86Favorites, Qt::Key_Favorites>,
- Xkb2Qt<XKB_KEY_XF86AudioMedia, Qt::Key_LaunchMedia>,
- Xkb2Qt<XKB_KEY_XF86OpenURL, Qt::Key_OpenUrl>,
- Xkb2Qt<XKB_KEY_XF86HomePage, Qt::Key_HomePage>,
- Xkb2Qt<XKB_KEY_XF86Search, Qt::Key_Search>,
- Xkb2Qt<XKB_KEY_XF86AudioLowerVolume, Qt::Key_VolumeDown>,
- Xkb2Qt<XKB_KEY_XF86AudioMute, Qt::Key_VolumeMute>,
- Xkb2Qt<XKB_KEY_XF86AudioRaiseVolume, Qt::Key_VolumeUp>,
- Xkb2Qt<XKB_KEY_XF86AudioPlay, Qt::Key_MediaPlay>,
- Xkb2Qt<XKB_KEY_XF86AudioStop, Qt::Key_MediaStop>,
- Xkb2Qt<XKB_KEY_XF86AudioPrev, Qt::Key_MediaPrevious>,
- Xkb2Qt<XKB_KEY_XF86AudioNext, Qt::Key_MediaNext>,
- Xkb2Qt<XKB_KEY_XF86AudioRecord, Qt::Key_MediaRecord>,
- Xkb2Qt<XKB_KEY_XF86AudioPause, Qt::Key_MediaPause>,
- Xkb2Qt<XKB_KEY_XF86Mail, Qt::Key_LaunchMail>,
- Xkb2Qt<XKB_KEY_XF86MyComputer, Qt::Key_Launch0>, // ### Qt 6: remap properly
- Xkb2Qt<XKB_KEY_XF86Calculator, Qt::Key_Launch1>,
- Xkb2Qt<XKB_KEY_XF86Memo, Qt::Key_Memo>,
- Xkb2Qt<XKB_KEY_XF86ToDoList, Qt::Key_ToDoList>,
- Xkb2Qt<XKB_KEY_XF86Calendar, Qt::Key_Calendar>,
- Xkb2Qt<XKB_KEY_XF86PowerDown, Qt::Key_PowerDown>,
- Xkb2Qt<XKB_KEY_XF86ContrastAdjust, Qt::Key_ContrastAdjust>,
- Xkb2Qt<XKB_KEY_XF86Standby, Qt::Key_Standby>,
- Xkb2Qt<XKB_KEY_XF86MonBrightnessUp, Qt::Key_MonBrightnessUp>,
- Xkb2Qt<XKB_KEY_XF86MonBrightnessDown, Qt::Key_MonBrightnessDown>,
- Xkb2Qt<XKB_KEY_XF86KbdLightOnOff, Qt::Key_KeyboardLightOnOff>,
- Xkb2Qt<XKB_KEY_XF86KbdBrightnessUp, Qt::Key_KeyboardBrightnessUp>,
- Xkb2Qt<XKB_KEY_XF86KbdBrightnessDown, Qt::Key_KeyboardBrightnessDown>,
- Xkb2Qt<XKB_KEY_XF86PowerOff, Qt::Key_PowerOff>,
- Xkb2Qt<XKB_KEY_XF86WakeUp, Qt::Key_WakeUp>,
- Xkb2Qt<XKB_KEY_XF86Eject, Qt::Key_Eject>,
- Xkb2Qt<XKB_KEY_XF86ScreenSaver, Qt::Key_ScreenSaver>,
- Xkb2Qt<XKB_KEY_XF86WWW, Qt::Key_WWW>,
- Xkb2Qt<XKB_KEY_XF86Sleep, Qt::Key_Sleep>,
- Xkb2Qt<XKB_KEY_XF86LightBulb, Qt::Key_LightBulb>,
- Xkb2Qt<XKB_KEY_XF86Shop, Qt::Key_Shop>,
- Xkb2Qt<XKB_KEY_XF86History, Qt::Key_History>,
- Xkb2Qt<XKB_KEY_XF86AddFavorite, Qt::Key_AddFavorite>,
- Xkb2Qt<XKB_KEY_XF86HotLinks, Qt::Key_HotLinks>,
- Xkb2Qt<XKB_KEY_XF86BrightnessAdjust, Qt::Key_BrightnessAdjust>,
- Xkb2Qt<XKB_KEY_XF86Finance, Qt::Key_Finance>,
- Xkb2Qt<XKB_KEY_XF86Community, Qt::Key_Community>,
- Xkb2Qt<XKB_KEY_XF86AudioRewind, Qt::Key_AudioRewind>,
- Xkb2Qt<XKB_KEY_XF86BackForward, Qt::Key_BackForward>,
- Xkb2Qt<XKB_KEY_XF86ApplicationLeft, Qt::Key_ApplicationLeft>,
- Xkb2Qt<XKB_KEY_XF86ApplicationRight, Qt::Key_ApplicationRight>,
- Xkb2Qt<XKB_KEY_XF86Book, Qt::Key_Book>,
- Xkb2Qt<XKB_KEY_XF86CD, Qt::Key_CD>,
- Xkb2Qt<XKB_KEY_XF86Calculater, Qt::Key_Calculator>,
- Xkb2Qt<XKB_KEY_XF86Clear, Qt::Key_Clear>,
- Xkb2Qt<XKB_KEY_XF86ClearGrab, Qt::Key_ClearGrab>,
- Xkb2Qt<XKB_KEY_XF86Close, Qt::Key_Close>,
- Xkb2Qt<XKB_KEY_XF86Copy, Qt::Key_Copy>,
- Xkb2Qt<XKB_KEY_XF86Cut, Qt::Key_Cut>,
- Xkb2Qt<XKB_KEY_XF86Display, Qt::Key_Display>,
- Xkb2Qt<XKB_KEY_XF86DOS, Qt::Key_DOS>,
- Xkb2Qt<XKB_KEY_XF86Documents, Qt::Key_Documents>,
- Xkb2Qt<XKB_KEY_XF86Excel, Qt::Key_Excel>,
- Xkb2Qt<XKB_KEY_XF86Explorer, Qt::Key_Explorer>,
- Xkb2Qt<XKB_KEY_XF86Game, Qt::Key_Game>,
- Xkb2Qt<XKB_KEY_XF86Go, Qt::Key_Go>,
- Xkb2Qt<XKB_KEY_XF86iTouch, Qt::Key_iTouch>,
- Xkb2Qt<XKB_KEY_XF86LogOff, Qt::Key_LogOff>,
- Xkb2Qt<XKB_KEY_XF86Market, Qt::Key_Market>,
- Xkb2Qt<XKB_KEY_XF86Meeting, Qt::Key_Meeting>,
- Xkb2Qt<XKB_KEY_XF86MenuKB, Qt::Key_MenuKB>,
- Xkb2Qt<XKB_KEY_XF86MenuPB, Qt::Key_MenuPB>,
- Xkb2Qt<XKB_KEY_XF86MySites, Qt::Key_MySites>,
- Xkb2Qt<XKB_KEY_XF86New, Qt::Key_New>,
- Xkb2Qt<XKB_KEY_XF86News, Qt::Key_News>,
- Xkb2Qt<XKB_KEY_XF86OfficeHome, Qt::Key_OfficeHome>,
- Xkb2Qt<XKB_KEY_XF86Open, Qt::Key_Open>,
- Xkb2Qt<XKB_KEY_XF86Option, Qt::Key_Option>,
- Xkb2Qt<XKB_KEY_XF86Paste, Qt::Key_Paste>,
- Xkb2Qt<XKB_KEY_XF86Phone, Qt::Key_Phone>,
- Xkb2Qt<XKB_KEY_XF86Reply, Qt::Key_Reply>,
- Xkb2Qt<XKB_KEY_XF86Reload, Qt::Key_Reload>,
- Xkb2Qt<XKB_KEY_XF86RotateWindows, Qt::Key_RotateWindows>,
- Xkb2Qt<XKB_KEY_XF86RotationPB, Qt::Key_RotationPB>,
- Xkb2Qt<XKB_KEY_XF86RotationKB, Qt::Key_RotationKB>,
- Xkb2Qt<XKB_KEY_XF86Save, Qt::Key_Save>,
- Xkb2Qt<XKB_KEY_XF86Send, Qt::Key_Send>,
- Xkb2Qt<XKB_KEY_XF86Spell, Qt::Key_Spell>,
- Xkb2Qt<XKB_KEY_XF86SplitScreen, Qt::Key_SplitScreen>,
- Xkb2Qt<XKB_KEY_XF86Support, Qt::Key_Support>,
- Xkb2Qt<XKB_KEY_XF86TaskPane, Qt::Key_TaskPane>,
- Xkb2Qt<XKB_KEY_XF86Terminal, Qt::Key_Terminal>,
- Xkb2Qt<XKB_KEY_XF86Tools, Qt::Key_Tools>,
- Xkb2Qt<XKB_KEY_XF86Travel, Qt::Key_Travel>,
- Xkb2Qt<XKB_KEY_XF86Video, Qt::Key_Video>,
- Xkb2Qt<XKB_KEY_XF86Word, Qt::Key_Word>,
- Xkb2Qt<XKB_KEY_XF86Xfer, Qt::Key_Xfer>,
- Xkb2Qt<XKB_KEY_XF86ZoomIn, Qt::Key_ZoomIn>,
- Xkb2Qt<XKB_KEY_XF86ZoomOut, Qt::Key_ZoomOut>,
- Xkb2Qt<XKB_KEY_XF86Away, Qt::Key_Away>,
- Xkb2Qt<XKB_KEY_XF86Messenger, Qt::Key_Messenger>,
- Xkb2Qt<XKB_KEY_XF86WebCam, Qt::Key_WebCam>,
- Xkb2Qt<XKB_KEY_XF86MailForward, Qt::Key_MailForward>,
- Xkb2Qt<XKB_KEY_XF86Pictures, Qt::Key_Pictures>,
- Xkb2Qt<XKB_KEY_XF86Music, Qt::Key_Music>,
- Xkb2Qt<XKB_KEY_XF86Battery, Qt::Key_Battery>,
- Xkb2Qt<XKB_KEY_XF86Bluetooth, Qt::Key_Bluetooth>,
- Xkb2Qt<XKB_KEY_XF86WLAN, Qt::Key_WLAN>,
- Xkb2Qt<XKB_KEY_XF86UWB, Qt::Key_UWB>,
- Xkb2Qt<XKB_KEY_XF86AudioForward, Qt::Key_AudioForward>,
- Xkb2Qt<XKB_KEY_XF86AudioRepeat, Qt::Key_AudioRepeat>,
- Xkb2Qt<XKB_KEY_XF86AudioRandomPlay, Qt::Key_AudioRandomPlay>,
- Xkb2Qt<XKB_KEY_XF86Subtitle, Qt::Key_Subtitle>,
- Xkb2Qt<XKB_KEY_XF86AudioCycleTrack, Qt::Key_AudioCycleTrack>,
- Xkb2Qt<XKB_KEY_XF86Time, Qt::Key_Time>,
- Xkb2Qt<XKB_KEY_XF86Select, Qt::Key_Select>,
- Xkb2Qt<XKB_KEY_XF86View, Qt::Key_View>,
- Xkb2Qt<XKB_KEY_XF86TopMenu, Qt::Key_TopMenu>,
- Xkb2Qt<XKB_KEY_XF86Red, Qt::Key_Red>,
- Xkb2Qt<XKB_KEY_XF86Green, Qt::Key_Green>,
- Xkb2Qt<XKB_KEY_XF86Yellow, Qt::Key_Yellow>,
- Xkb2Qt<XKB_KEY_XF86Blue, Qt::Key_Blue>,
- Xkb2Qt<XKB_KEY_XF86Bluetooth, Qt::Key_Bluetooth>,
- Xkb2Qt<XKB_KEY_XF86Suspend, Qt::Key_Suspend>,
- Xkb2Qt<XKB_KEY_XF86Hibernate, Qt::Key_Hibernate>,
- Xkb2Qt<XKB_KEY_XF86TouchpadToggle, Qt::Key_TouchpadToggle>,
- Xkb2Qt<XKB_KEY_XF86TouchpadOn, Qt::Key_TouchpadOn>,
- Xkb2Qt<XKB_KEY_XF86TouchpadOff, Qt::Key_TouchpadOff>,
- Xkb2Qt<XKB_KEY_XF86AudioMicMute, Qt::Key_MicMute>,
- Xkb2Qt<XKB_KEY_XF86Launch0, Qt::Key_Launch2>, // ### Qt 6: remap properly
- Xkb2Qt<XKB_KEY_XF86Launch1, Qt::Key_Launch3>,
- Xkb2Qt<XKB_KEY_XF86Launch2, Qt::Key_Launch4>,
- Xkb2Qt<XKB_KEY_XF86Launch3, Qt::Key_Launch5>,
- Xkb2Qt<XKB_KEY_XF86Launch4, Qt::Key_Launch6>,
- Xkb2Qt<XKB_KEY_XF86Launch5, Qt::Key_Launch7>,
- Xkb2Qt<XKB_KEY_XF86Launch6, Qt::Key_Launch8>,
- Xkb2Qt<XKB_KEY_XF86Launch7, Qt::Key_Launch9>,
- Xkb2Qt<XKB_KEY_XF86Launch8, Qt::Key_LaunchA>,
- Xkb2Qt<XKB_KEY_XF86Launch9, Qt::Key_LaunchB>,
- Xkb2Qt<XKB_KEY_XF86LaunchA, Qt::Key_LaunchC>,
- Xkb2Qt<XKB_KEY_XF86LaunchB, Qt::Key_LaunchD>,
- Xkb2Qt<XKB_KEY_XF86LaunchC, Qt::Key_LaunchE>,
- Xkb2Qt<XKB_KEY_XF86LaunchD, Qt::Key_LaunchF>,
- Xkb2Qt<XKB_KEY_XF86LaunchE, Qt::Key_LaunchG>,
- Xkb2Qt<XKB_KEY_XF86LaunchF, Qt::Key_LaunchH>
- >::Data{}
-);
-
-// Possible modifier states.
-static const Qt::KeyboardModifiers ModsTbl[] = {
- Qt::NoModifier, // 0
- Qt::ShiftModifier, // 1
- Qt::ControlModifier, // 2
- Qt::ControlModifier | Qt::ShiftModifier, // 3
- Qt::AltModifier, // 4
- Qt::AltModifier | Qt::ShiftModifier, // 5
- Qt::AltModifier | Qt::ControlModifier, // 6
- Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier, // 7
- Qt::NoModifier // Fall-back to raw Key_*, for non-latin1 kb layouts
-};
+QT_BEGIN_NAMESPACE
Qt::KeyboardModifiers QXcbKeyboard::translateModifiers(int s) const
{
- Qt::KeyboardModifiers ret = 0;
+ Qt::KeyboardModifiers ret = Qt::NoModifier;
if (s & XCB_MOD_MASK_SHIFT)
ret |= Qt::ShiftModifier;
if (s & XCB_MOD_MASK_CONTROL)
@@ -473,7 +88,7 @@ static xcb_keysym_t getUnshiftedXKey(xcb_keysym_t unshifted, xcb_keysym_t shifte
xcb_keysym_t xlower;
xcb_keysym_t xupper;
- xkbcommon_XConvertCase(unshifted, &xlower, &xupper);
+ QXkbCommon::xkbcommon_XConvertCase(unshifted, &xlower, &xupper);
if (xlower != xupper // Check if symbol is cased
&& unshifted == xupper) { // Unshifted must be upper case
@@ -805,7 +420,12 @@ void QXcbKeyboard::updateKeymap()
updateXKBMods();
- checkForLatinLayout();
+ QXkbCommon::verifyHasLatinLayout(m_xkbKeymap.get());
+}
+
+QList<int> QXcbKeyboard::possibleKeys(const QKeyEvent *event) const
+{
+ return QXkbCommon::possibleKeys(m_xkbState.get(), event, m_superAsMeta, m_hyperAsMeta);
}
#if QT_CONFIG(xkb)
@@ -918,272 +538,6 @@ void QXcbKeyboard::updateXKBMods()
xkb_mods.mod5 = xkb_keymap_mod_get_index(m_xkbKeymap.get(), "Mod5");
}
-static bool isLatin(xkb_keysym_t sym)
-{
- return ((sym >= 'a' && sym <= 'z') || (sym >= 'A' && sym <= 'Z'));
-}
-
-void QXcbKeyboard::checkForLatinLayout() const
-{
- const xkb_layout_index_t layoutCount = xkb_keymap_num_layouts(m_xkbKeymap.get());
- const xcb_keycode_t minKeycode = xkb_keymap_min_keycode(m_xkbKeymap.get());
- const xcb_keycode_t maxKeycode = xkb_keymap_max_keycode(m_xkbKeymap.get());
-
- const xkb_keysym_t *keysyms = nullptr;
- int nrLatinKeys = 0;
- for (xkb_layout_index_t layout = 0; layout < layoutCount; ++layout) {
- for (xcb_keycode_t code = minKeycode; code < maxKeycode; ++code) {
- xkb_keymap_key_get_syms_by_level(m_xkbKeymap.get(), code, layout, 0, &keysyms);
- if (keysyms && isLatin(keysyms[0]))
- nrLatinKeys++;
- if (nrLatinKeys > 10) // arbitrarily chosen threshold
- return;
- }
- }
- // This means that lookupLatinKeysym() will not find anything and latin
- // key shortcuts might not work. This is a bug in the affected desktop
- // environment. Usually can be solved via system settings by adding e.g. 'us'
- // layout to the list of seleced layouts, or by using command line, "setxkbmap
- // -layout rus,en". The position of latin key based layout in the list of the
- // selected layouts is irrelevant. Properly functioning desktop environments
- // handle this behind the scenes, even if no latin key based layout has been
- // explicitly listed in the selected layouts.
- qCWarning(lcQpaKeyboard, "no keyboard layouts with latin keys present");
-}
-
-xkb_keysym_t QXcbKeyboard::lookupLatinKeysym(xkb_keycode_t keycode) const
-{
- xkb_layout_index_t layout;
- xkb_keysym_t sym = XKB_KEY_NoSymbol;
- const xkb_layout_index_t layoutCount = xkb_keymap_num_layouts_for_key(m_xkbKeymap.get(), keycode);
- const xkb_layout_index_t currentLayout = xkb_state_key_get_layout(m_xkbState.get(), keycode);
- // Look at user layouts in the order in which they are defined in system
- // settings to find a latin keysym.
- for (layout = 0; layout < layoutCount; ++layout) {
- if (layout == currentLayout)
- continue;
- const xkb_keysym_t *syms;
- xkb_level_index_t level = xkb_state_key_get_level(m_xkbState.get(), keycode, layout);
- if (xkb_keymap_key_get_syms_by_level(m_xkbKeymap.get(), keycode, layout, level, &syms) != 1)
- continue;
- if (isLatin(syms[0])) {
- sym = syms[0];
- break;
- }
- }
-
- if (sym == XKB_KEY_NoSymbol)
- return sym;
-
- xkb_mod_mask_t latchedMods = xkb_state_serialize_mods(m_xkbState.get(), XKB_STATE_MODS_LATCHED);
- xkb_mod_mask_t lockedMods = xkb_state_serialize_mods(m_xkbState.get(), XKB_STATE_MODS_LOCKED);
-
- // Check for uniqueness, consider the following setup:
- // setxkbmap -layout us,ru,us -variant dvorak,, -option 'grp:ctrl_alt_toggle' (set 'ru' as active).
- // In this setup, the user would expect to trigger a ctrl+q shortcut by pressing ctrl+<physical x key>,
- // because "US dvorak" is higher up in the layout settings list. This check verifies that an obtained
- // 'sym' can not be acquired by any other layout higher up in the user's layout list. If it can be acquired
- // then the obtained key is not unique. This prevents ctrl+<physical q key> from generating a ctrl+q
- // shortcut in the above described setup. We don't want ctrl+<physical x key> and ctrl+<physical q key> to
- // generate the same shortcut event in this case.
- const xcb_keycode_t minKeycode = xkb_keymap_min_keycode(m_xkbKeymap.get());
- const xcb_keycode_t maxKeycode = xkb_keymap_max_keycode(m_xkbKeymap.get());
- ScopedXKBState state(xkb_state_new(m_xkbKeymap.get()));
- for (xkb_layout_index_t prevLayout = 0; prevLayout < layout; ++prevLayout) {
- xkb_state_update_mask(state.get(), 0, latchedMods, lockedMods, 0, 0, prevLayout);
- for (xcb_keycode_t code = minKeycode; code < maxKeycode; ++code) {
- xkb_keysym_t prevSym = xkb_state_key_get_one_sym(state.get(), code);
- if (prevSym == sym) {
- sym = XKB_KEY_NoSymbol;
- break;
- }
- }
- }
-
- return sym;
-}
-
-static const char *qtKeyName(int qtKey)
-{
- int keyEnumIndex = qt_getQtMetaObject()->indexOfEnumerator("Key");
- QMetaEnum keyEnum = qt_getQtMetaObject()->enumerator(keyEnumIndex);
- return keyEnum.valueToKey(qtKey);
-}
-
-QList<int> QXcbKeyboard::possibleKeys(const QKeyEvent *event) const
-{
- // turn off the modifier bits which doesn't participate in shortcuts
- Qt::KeyboardModifiers notNeeded = Qt::KeypadModifier | Qt::GroupSwitchModifier;
- Qt::KeyboardModifiers modifiers = event->modifiers() &= ~notNeeded;
- // create a fresh kb state and test against the relevant modifier combinations
- struct xkb_state *kb_state = xkb_state_new(m_xkbKeymap.get());
- if (!kb_state) {
- qWarning("QXcbKeyboard: failed to compile xkb keymap!");
- return QList<int>();
- }
- // get kb state from the master xkb_state and update the temporary kb_state
- xkb_layout_index_t lockedLayout = xkb_state_serialize_layout(m_xkbState.get(), XKB_STATE_LAYOUT_LOCKED);
- xkb_mod_mask_t latchedMods = xkb_state_serialize_mods(m_xkbState.get(), XKB_STATE_MODS_LATCHED);
- xkb_mod_mask_t lockedMods = xkb_state_serialize_mods(m_xkbState.get(), XKB_STATE_MODS_LOCKED);
- xkb_mod_mask_t depressedMods = xkb_state_serialize_mods(m_xkbState.get(), XKB_STATE_MODS_DEPRESSED);
-
- xkb_state_update_mask(kb_state, depressedMods, latchedMods, lockedMods, 0, 0, lockedLayout);
- quint32 keycode = event->nativeScanCode();
- // handle shortcuts for level three and above
- xkb_layout_index_t layoutIndex = xkb_state_key_get_layout(kb_state, keycode);
- xkb_level_index_t levelIndex = 0;
- if (layoutIndex != XKB_LAYOUT_INVALID) {
- levelIndex = xkb_state_key_get_level(kb_state, keycode, layoutIndex);
- if (levelIndex == XKB_LEVEL_INVALID)
- levelIndex = 0;
- }
- if (levelIndex <= 1)
- xkb_state_update_mask(kb_state, 0, latchedMods, lockedMods, 0, 0, lockedLayout);
-
- xkb_keysym_t sym = xkb_state_key_get_one_sym(kb_state, keycode);
- if (sym == XKB_KEY_NoSymbol) {
- xkb_state_unref(kb_state);
- return QList<int>();
- }
-
- QList<int> result;
- int baseQtKey = keysymToQtKey(sym, modifiers, kb_state, keycode);
- if (baseQtKey)
- result += (baseQtKey + modifiers);
-
- xkb_mod_index_t shiftMod = xkb_keymap_mod_get_index(m_xkbKeymap.get(), "Shift");
- xkb_mod_index_t altMod = xkb_keymap_mod_get_index(m_xkbKeymap.get(), "Alt");
- xkb_mod_index_t controlMod = xkb_keymap_mod_get_index(m_xkbKeymap.get(), "Control");
- xkb_mod_index_t metaMod = xkb_keymap_mod_get_index(m_xkbKeymap.get(), "Meta");
-
- Q_ASSERT(shiftMod < 32);
- Q_ASSERT(altMod < 32);
- Q_ASSERT(controlMod < 32);
-
- xkb_mod_mask_t depressed;
- int qtKey = 0;
- // obtain a list of possible shortcuts for the given key event
- for (uint i = 1; i < sizeof(ModsTbl) / sizeof(*ModsTbl) ; ++i) {
- Qt::KeyboardModifiers neededMods = ModsTbl[i];
- if ((modifiers & neededMods) == neededMods) {
- if (i == 8) {
- if (isLatin(baseQtKey))
- continue;
- // add a latin key as a fall back key
- sym = lookupLatinKeysym(keycode);
- } else {
- depressed = 0;
- if (neededMods & Qt::AltModifier)
- depressed |= (1 << altMod);
- if (neededMods & Qt::ShiftModifier)
- depressed |= (1 << shiftMod);
- if (neededMods & Qt::ControlModifier)
- depressed |= (1 << controlMod);
- if (metaMod < 32 && neededMods & Qt::MetaModifier)
- depressed |= (1 << metaMod);
- xkb_state_update_mask(kb_state, depressed, latchedMods, lockedMods, 0, 0, lockedLayout);
- sym = xkb_state_key_get_one_sym(kb_state, keycode);
- }
- if (sym == XKB_KEY_NoSymbol)
- continue;
-
- Qt::KeyboardModifiers mods = modifiers & ~neededMods;
- qtKey = keysymToQtKey(sym, mods, kb_state, keycode);
- if (!qtKey || qtKey == baseQtKey)
- continue;
-
- // catch only more specific shortcuts, i.e. Ctrl+Shift+= also generates Ctrl++ and +,
- // but Ctrl++ is more specific than +, so we should skip the last one
- bool ambiguous = false;
- for (int shortcut : qAsConst(result)) {
- if (int(shortcut & ~Qt::KeyboardModifierMask) == qtKey && (shortcut & mods) == mods) {
- ambiguous = true;
- break;
- }
- }
- if (ambiguous)
- continue;
-
- result += (qtKey + mods);
- }
- }
- xkb_state_unref(kb_state);
- return result;
-}
-
-int QXcbKeyboard::keysymToQtKey(xcb_keysym_t keysym, Qt::KeyboardModifiers modifiers,
- struct xkb_state *state, xcb_keycode_t code) const
-{
- int qtKey = 0;
-
- // lookup from direct mapping
- if (keysym >= XKB_KEY_F1 && keysym <= XKB_KEY_F35) {
- // function keys
- qtKey = Qt::Key_F1 + (keysym - XKB_KEY_F1);
- } else if (keysym >= XKB_KEY_KP_0 && keysym <= XKB_KEY_KP_9) {
- // numeric keypad keys
- qtKey = Qt::Key_0 + (keysym - XKB_KEY_KP_0);
- } else if (isLatin(keysym)) {
- qtKey = xkbcommon_xkb_keysym_to_upper(keysym);
- } else {
- // check if we have a direct mapping
- xkb2qt_t searchKey{keysym, 0};
- auto it = std::lower_bound(KeyTbl.cbegin(), KeyTbl.cend(), searchKey);
- if (it != KeyTbl.end() && !(searchKey < *it))
- qtKey = it->qt;
- }
-
- QString text;
- bool fromUnicode = qtKey == 0;
- if (fromUnicode) { // lookup from unicode
- if (modifiers & Qt::ControlModifier) {
- // Control modifier changes the text to ASCII control character, therefore we
- // can't use this text to map keysym to a qt key. We can use the same keysym
- // (it is not affectd by transformation) to obtain untransformed text. For details
- // see "Appendix A. Default Symbol Transformations" in the XKB specification.
- text = lookupStringNoKeysymTransformations(keysym);
- } else {
- text = lookupString(state, code);
- }
- if (!text.isEmpty()) {
- if (text.unicode()->isDigit()) {
- // Ensures that also non-latin digits are mapped to corresponding qt keys,
- // e.g CTRL + Û² (arabic two), is mapped to CTRL + Qt::Key_2.
- qtKey = Qt::Key_0 + text.unicode()->digitValue();
- } else {
- qtKey = text.unicode()->toUpper().unicode();
- }
- }
- }
-
- if (rmod_masks.meta) {
- // translate Super/Hyper keys to Meta if we're using them as the MetaModifier
- if (rmod_masks.meta == rmod_masks.super && (qtKey == Qt::Key_Super_L
- || qtKey == Qt::Key_Super_R)) {
- qtKey = Qt::Key_Meta;
- } else if (rmod_masks.meta == rmod_masks.hyper && (qtKey == Qt::Key_Hyper_L
- || qtKey == Qt::Key_Hyper_R)) {
- qtKey = Qt::Key_Meta;
- }
- }
-
- if (Q_UNLIKELY(lcQpaKeyboard().isDebugEnabled())) {
- char keysymName[64];
- xkb_keysym_get_name(keysym, keysymName, sizeof(keysymName));
- QString keysymInHex = QString(QStringLiteral("0x%1")).arg(keysym, 0, 16);
- if (qtKeyName(qtKey)) {
- qCDebug(lcQpaKeyboard).nospace() << "keysym: " << keysymName << "("
- << keysymInHex << ") mapped to Qt::" << qtKeyName(qtKey) << " | text: " << text
- << " | qt key: " << qtKey << " mapped from unicode number: " << fromUnicode;
- } else {
- qCDebug(lcQpaKeyboard).nospace() << "no Qt::Key for keysym: " << keysymName
- << "(" << keysymInHex << ") | text: " << text << " | qt key: " << qtKey;
- }
- }
-
- return qtKey;
-}
-
QXcbKeyboard::QXcbKeyboard(QXcbConnection *connection)
: QXcbObject(connection)
{
@@ -1211,6 +565,12 @@ QXcbKeyboard::~QXcbKeyboard()
xcb_key_symbols_free(m_key_symbols);
}
+void QXcbKeyboard::initialize()
+{
+ auto inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext();
+ QXkbCommon::setXkbContext(inputContext, m_xkbContext.get());
+}
+
void QXcbKeyboard::selectEvents()
{
#if QT_CONFIG(xkb)
@@ -1514,6 +874,12 @@ void QXcbKeyboard::resolveMaskConflicts()
rmod_masks.meta = rmod_masks.hyper;
}
}
+
+ // translate Super/Hyper keys to Meta if we're using them as the MetaModifier
+ if (rmod_masks.meta && rmod_masks.meta == rmod_masks.super)
+ m_superAsMeta = true;
+ if (rmod_masks.meta && rmod_masks.meta == rmod_masks.hyper)
+ m_hyperAsMeta = true;
}
void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type, xcb_keycode_t code,
@@ -1529,7 +895,7 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type,
if (type == QEvent::KeyPress)
targetWindow->updateNetWmUserTime(time);
- ScopedXKBState sendEventState;
+ QXkbCommon::ScopedXKBState sendEventState;
if (fromSendEvent) {
// Have a temporary keyboard state filled in from state
// this way we allow for synthetic events to have different state
@@ -1546,30 +912,13 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type,
struct xkb_state *xkbState = fromSendEvent ? sendEventState.get() : m_xkbState.get();
xcb_keysym_t sym = xkb_state_key_get_one_sym(xkbState, code);
- QString text = lookupString(xkbState, code);
+ QString text = QXkbCommon::lookupString(xkbState, code);
Qt::KeyboardModifiers modifiers = translateModifiers(state);
- if (sym >= XKB_KEY_KP_Space && sym <= XKB_KEY_KP_9)
+ if (QXkbCommon::isKeypad(sym))
modifiers |= Qt::KeypadModifier;
- // Note 1: All standard key sequences on linux (as defined in platform theme)
- // that use a latin character also contain a control modifier, which is why
- // checking for Qt::ControlModifier is sufficient here. It is possible to
- // override QPlatformTheme::keyBindings() and provide custom sequences for
- // QKeySequence::StandardKey. Custom sequences probably should respect this
- // convention (alternatively, we could test against other modifiers here).
- // Note 2: The possibleKeys() shorcut mechanism is not affected by this value
- // adjustment and does its own thing.
- xcb_keysym_t latinKeysym = XKB_KEY_NoSymbol;
- if (modifiers & Qt::ControlModifier) {
- // With standard shortcuts we should prefer a latin character, this is
- // in checks like "event == QKeySequence::Copy".
- if (!isLatin(sym))
- latinKeysym = lookupLatinKeysym(code);
- }
-
- int qtcode = keysymToQtKey(latinKeysym != XKB_KEY_NoSymbol ? latinKeysym : sym,
- modifiers, xkbState, code);
+ int qtcode = QXkbCommon::keysymToQtKey(sym, modifiers, xkbState, code, m_superAsMeta, m_hyperAsMeta);
if (type == QEvent::KeyPress) {
if (m_isAutoRepeat && m_autoRepeatCode != code)
@@ -1611,28 +960,6 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type,
}
}
-QString QXcbKeyboard::lookupString(struct xkb_state *state, xcb_keycode_t code) const
-{
- QVarLengthArray<char, 32> chars(32);
- const int size = xkb_state_key_get_utf8(state, code, chars.data(), chars.size());
- if (Q_UNLIKELY(size + 1 > chars.size())) { // +1 for NUL
- chars.resize(size + 1);
- xkb_state_key_get_utf8(state, code, chars.data(), chars.size());
- }
- return QString::fromUtf8(chars.constData(), size);
-}
-
-QString QXcbKeyboard::lookupStringNoKeysymTransformations(xkb_keysym_t keysym) const
-{
- QVarLengthArray<char, 32> chars(32);
- const int size = xkb_keysym_to_utf8(keysym, chars.data(), chars.size());
- if (Q_UNLIKELY(size > chars.size())) {
- chars.resize(size);
- xkb_keysym_to_utf8(keysym, chars.data(), chars.size());
- }
- return QString::fromUtf8(chars.constData(), size);
-}
-
static bool fromSendEvent(const void *event)
{
// From X11 protocol: Every event contains an 8-bit type code. The most
diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.h b/src/plugins/platforms/xcb/qxcbkeyboard.h
index f8490592b7..e35c82ad24 100644
--- a/src/plugins/platforms/xcb/qxcbkeyboard.h
+++ b/src/plugins/platforms/xcb/qxcbkeyboard.h
@@ -50,16 +50,12 @@
#endif
#include <xkbcommon/xkbcommon.h>
-#if QT_CONFIG(xkb)
-#include <xkbcommon/xkbcommon-x11.h>
-#endif
+#include <QtXkbCommonSupport/private/qxkbcommon_p.h>
#include <QEvent>
QT_BEGIN_NAMESPACE
-class QWindow;
-
class QXcbKeyboard : public QXcbObject
{
public:
@@ -67,6 +63,7 @@ public:
~QXcbKeyboard();
+ void initialize();
void selectEvents();
void handleKeyPressEvent(const xcb_key_press_event_t *event);
@@ -75,7 +72,7 @@ public:
Qt::KeyboardModifiers translateModifiers(int s) const;
void updateKeymap(xcb_mapping_notify_event_t *event);
void updateKeymap();
- QList<int> possibleKeys(const QKeyEvent *e) const;
+ QList<int> possibleKeys(const QKeyEvent *event) const;
// when XKEYBOARD not present on the X server
void updateXKBMods();
@@ -96,10 +93,6 @@ protected:
quint16 state, xcb_timestamp_t time, bool fromSendEvent);
void resolveMaskConflicts();
- QString lookupString(struct xkb_state *state, xcb_keycode_t code) const;
- QString lookupStringNoKeysymTransformations(xkb_keysym_t keysym) const;
- int keysymToQtKey(xcb_keysym_t keysym, Qt::KeyboardModifiers modifiers,
- struct xkb_state *state, xcb_keycode_t code) const;
typedef QMap<xcb_keysym_t, int> KeysymModifierMap;
struct xkb_keymap *keymapFromCore(const KeysymModifierMap &keysymMods);
@@ -111,9 +104,6 @@ protected:
void updateVModMapping();
void updateVModToRModMapping();
- xkb_keysym_t lookupLatinKeysym(xkb_keycode_t keycode) const;
- void checkForLatinLayout() const;
-
private:
bool m_config = false;
bool m_isAutoRepeat = false;
@@ -148,22 +138,12 @@ private:
int core_device_id;
#endif
- struct XKBStateDeleter {
- void operator()(struct xkb_state *state) const { return xkb_state_unref(state); }
- };
- struct XKBKeymapDeleter {
- void operator()(struct xkb_keymap *keymap) const { return xkb_keymap_unref(keymap); }
- };
- struct XKBContextDeleter {
- void operator()(struct xkb_context *context) const { return xkb_context_unref(context); }
- };
- using ScopedXKBState = std::unique_ptr<struct xkb_state, XKBStateDeleter>;
- using ScopedXKBKeymap = std::unique_ptr<struct xkb_keymap, XKBKeymapDeleter>;
- using ScopedXKBContext = std::unique_ptr<struct xkb_context, XKBContextDeleter>;
+ QXkbCommon::ScopedXKBState m_xkbState;
+ QXkbCommon::ScopedXKBKeymap m_xkbKeymap;
+ QXkbCommon::ScopedXKBContext m_xkbContext;
- ScopedXKBState m_xkbState;
- ScopedXKBKeymap m_xkbKeymap;
- ScopedXKBContext m_xkbContext;
+ bool m_superAsMeta = false;
+ bool m_hyperAsMeta = false;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp
index 6f3584f509..899081e752 100644
--- a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp
+++ b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp
@@ -304,7 +304,7 @@ QPlatformNativeInterface::NativeResourceForWindowFunction QXcbNativeInterface::n
QPlatformNativeInterface::NativeResourceForBackingStoreFunction QXcbNativeInterface::nativeResourceFunctionForBackingStore(const QByteArray &resource)
{
const QByteArray lowerCaseResource = resource.toLower();
- NativeResourceForBackingStoreFunction func = handlerNativeResourceFunctionForBackingStore(resource);
+ NativeResourceForBackingStoreFunction func = handlerNativeResourceFunctionForBackingStore(lowerCaseResource);
return func;
}
diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp
index 39e83e0451..a5a2aeb9aa 100644
--- a/src/plugins/platforms/xcb/qxcbscreen.cpp
+++ b/src/plugins/platforms/xcb/qxcbscreen.cpp
@@ -147,7 +147,7 @@ void QXcbVirtualDesktop::setPrimaryScreen(QPlatformScreen *s)
{
const int idx = m_screens.indexOf(s);
Q_ASSERT(idx > -1);
- m_screens.swap(0, idx);
+ m_screens.swapItemsAt(0, idx);
}
QXcbXSettings *QXcbVirtualDesktop::xSettings() const
diff --git a/src/plugins/platforms/xcb/qxcbsessionmanager.h b/src/plugins/platforms/xcb/qxcbsessionmanager.h
index 0ad9445361..79c587b38d 100644
--- a/src/plugins/platforms/xcb/qxcbsessionmanager.h
+++ b/src/plugins/platforms/xcb/qxcbsessionmanager.h
@@ -85,8 +85,6 @@ public:
private:
QEventLoop *m_eventLoop;
-
- Q_DISABLE_COPY(QXcbSessionManager)
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp
index 3bfcbf2adb..610eca06a5 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.cpp
+++ b/src/plugins/platforms/xcb/qxcbwindow.cpp
@@ -843,40 +843,12 @@ void QXcbWindow::doFocusIn()
QWindowSystemInterface::handleWindowActivated(w, Qt::ActiveWindowFocusReason);
}
-static bool focusInPeeker(QXcbConnection *connection, xcb_generic_event_t *event)
-{
- if (!event) {
- // FocusIn event is not in the queue, proceed with FocusOut normally.
- QWindowSystemInterface::handleWindowActivated(nullptr, Qt::ActiveWindowFocusReason);
- return true;
- }
- uint response_type = event->response_type & ~0x80;
- if (response_type == XCB_FOCUS_IN) {
- // Ignore focus events that are being sent only because the pointer is over
- // our window, even if the input focus is in a different window.
- xcb_focus_in_event_t *e = (xcb_focus_in_event_t *) event;
- if (e->detail != XCB_NOTIFY_DETAIL_POINTER)
- return true;
- }
-
- /* We are also interested in XEMBED_FOCUS_IN events */
- if (response_type == XCB_CLIENT_MESSAGE) {
- xcb_client_message_event_t *cme = (xcb_client_message_event_t *)event;
- if (cme->type == connection->atom(QXcbAtom::_XEMBED)
- && cme->data.data32[1] == XEMBED_FOCUS_IN)
- return true;
- }
-
- return false;
-}
-
void QXcbWindow::doFocusOut()
{
connection()->setFocusWindow(nullptr);
relayFocusToModalWindow();
// Do not set the active window to nullptr if there is a FocusIn coming.
- // The FocusIn handler will update QXcbConnection::setFocusWindow() accordingly.
- connection()->addPeekFunc(focusInPeeker);
+ connection()->focusInTimer().start();
}
struct QtMotifWmHints {
@@ -2264,6 +2236,8 @@ void QXcbWindow::handleFocusInEvent(const xcb_focus_in_event_t *event)
// our window, even if the input focus is in a different window.
if (event->detail == XCB_NOTIFY_DETAIL_POINTER)
return;
+
+ connection()->focusInTimer().stop();
doFocusIn();
}
@@ -2491,6 +2465,7 @@ void QXcbWindow::handleXEmbedMessage(const xcb_client_message_event_t *event)
xcbScreen()->windowShown(this);
break;
case XEMBED_FOCUS_IN:
+ connection()->focusInTimer().stop();
Qt::FocusReason reason;
switch (event->data.data32[2]) {
case XEMBED_FOCUS_FIRST:
diff --git a/src/plugins/platforms/xcb/qxcbxkbcommon.h b/src/plugins/platforms/xcb/qxcbxkbcommon.h
deleted file mode 100644
index 422c0c0f12..0000000000
--- a/src/plugins/platforms/xcb/qxcbxkbcommon.h
+++ /dev/null
@@ -1,233 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 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$
-**
-****************************************************************************/
-
-/* XConvertCase was copied from src/3rdparty/xkbcommon/src/keysym.c,
- which contains the following license information:
-
- Copyright 1985, 1987, 1990, 1998 The Open Group
-
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of this software and associated documentation files (the "Software"),
- to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sublicense,
- and/or sell copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
- Except as contained in this notice, the names of the authors or their
- institutions shall not be used in advertising or otherwise to promote the
- sale, use or other dealings in this Software without prior written
- authorization from the authors.
-
-
-
- Copyright © 2009 Dan Nicholson
-
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of this software and associated documentation files (the "Software"),
- to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sublicense,
- and/or sell copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice (including the next
- paragraph) shall be included in all copies or substantial portions of the
- Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- DEALINGS IN THE SOFTWARE.
-*/
-
-/*
- The following code modifications were applied:
-
- XConvertCase() was renamed to xkbcommon_XConvertCase(), to not confuse it
- with Xlib's XConvertCase().
-
- UCSConvertCase() was renamed to qt_UCSConvertCase() and function's body was
- replaced to use Qt APIs for doing case conversion, which should give us better
- results instead of using the less complete version from keysym.c
-*/
-
-#include <xkbcommon/xkbcommon.h>
-#include <QtCore/QChar>
-
-QT_BEGIN_NAMESPACE
-
-static void qt_UCSConvertCase(uint32_t code, xkb_keysym_t *lower, xkb_keysym_t *upper)
-{
- *lower = QChar::toLower(code);
- *upper = QChar::toUpper(code);
-}
-
-void xkbcommon_XConvertCase(xkb_keysym_t sym, xkb_keysym_t *lower, xkb_keysym_t *upper)
-{
- /* Latin 1 keysym */
- if (sym < 0x100) {
- qt_UCSConvertCase(sym, lower, upper);
- return;
- }
-
- /* Unicode keysym */
- if ((sym & 0xff000000) == 0x01000000) {
- qt_UCSConvertCase((sym & 0x00ffffff), lower, upper);
- *upper |= 0x01000000;
- *lower |= 0x01000000;
- return;
- }
-
- /* Legacy keysym */
-
- *lower = sym;
- *upper = sym;
-
- switch (sym >> 8) {
- case 1: /* Latin 2 */
- /* Assume the KeySym is a legal value (ignore discontinuities) */
- if (sym == XKB_KEY_Aogonek)
- *lower = XKB_KEY_aogonek;
- else if (sym >= XKB_KEY_Lstroke && sym <= XKB_KEY_Sacute)
- *lower += (XKB_KEY_lstroke - XKB_KEY_Lstroke);
- else if (sym >= XKB_KEY_Scaron && sym <= XKB_KEY_Zacute)
- *lower += (XKB_KEY_scaron - XKB_KEY_Scaron);
- else if (sym >= XKB_KEY_Zcaron && sym <= XKB_KEY_Zabovedot)
- *lower += (XKB_KEY_zcaron - XKB_KEY_Zcaron);
- else if (sym == XKB_KEY_aogonek)
- *upper = XKB_KEY_Aogonek;
- else if (sym >= XKB_KEY_lstroke && sym <= XKB_KEY_sacute)
- *upper -= (XKB_KEY_lstroke - XKB_KEY_Lstroke);
- else if (sym >= XKB_KEY_scaron && sym <= XKB_KEY_zacute)
- *upper -= (XKB_KEY_scaron - XKB_KEY_Scaron);
- else if (sym >= XKB_KEY_zcaron && sym <= XKB_KEY_zabovedot)
- *upper -= (XKB_KEY_zcaron - XKB_KEY_Zcaron);
- else if (sym >= XKB_KEY_Racute && sym <= XKB_KEY_Tcedilla)
- *lower += (XKB_KEY_racute - XKB_KEY_Racute);
- else if (sym >= XKB_KEY_racute && sym <= XKB_KEY_tcedilla)
- *upper -= (XKB_KEY_racute - XKB_KEY_Racute);
- break;
- case 2: /* Latin 3 */
- /* Assume the KeySym is a legal value (ignore discontinuities) */
- if (sym >= XKB_KEY_Hstroke && sym <= XKB_KEY_Hcircumflex)
- *lower += (XKB_KEY_hstroke - XKB_KEY_Hstroke);
- else if (sym >= XKB_KEY_Gbreve && sym <= XKB_KEY_Jcircumflex)
- *lower += (XKB_KEY_gbreve - XKB_KEY_Gbreve);
- else if (sym >= XKB_KEY_hstroke && sym <= XKB_KEY_hcircumflex)
- *upper -= (XKB_KEY_hstroke - XKB_KEY_Hstroke);
- else if (sym >= XKB_KEY_gbreve && sym <= XKB_KEY_jcircumflex)
- *upper -= (XKB_KEY_gbreve - XKB_KEY_Gbreve);
- else if (sym >= XKB_KEY_Cabovedot && sym <= XKB_KEY_Scircumflex)
- *lower += (XKB_KEY_cabovedot - XKB_KEY_Cabovedot);
- else if (sym >= XKB_KEY_cabovedot && sym <= XKB_KEY_scircumflex)
- *upper -= (XKB_KEY_cabovedot - XKB_KEY_Cabovedot);
- break;
- case 3: /* Latin 4 */
- /* Assume the KeySym is a legal value (ignore discontinuities) */
- if (sym >= XKB_KEY_Rcedilla && sym <= XKB_KEY_Tslash)
- *lower += (XKB_KEY_rcedilla - XKB_KEY_Rcedilla);
- else if (sym >= XKB_KEY_rcedilla && sym <= XKB_KEY_tslash)
- *upper -= (XKB_KEY_rcedilla - XKB_KEY_Rcedilla);
- else if (sym == XKB_KEY_ENG)
- *lower = XKB_KEY_eng;
- else if (sym == XKB_KEY_eng)
- *upper = XKB_KEY_ENG;
- else if (sym >= XKB_KEY_Amacron && sym <= XKB_KEY_Umacron)
- *lower += (XKB_KEY_amacron - XKB_KEY_Amacron);
- else if (sym >= XKB_KEY_amacron && sym <= XKB_KEY_umacron)
- *upper -= (XKB_KEY_amacron - XKB_KEY_Amacron);
- break;
- case 6: /* Cyrillic */
- /* Assume the KeySym is a legal value (ignore discontinuities) */
- if (sym >= XKB_KEY_Serbian_DJE && sym <= XKB_KEY_Serbian_DZE)
- *lower -= (XKB_KEY_Serbian_DJE - XKB_KEY_Serbian_dje);
- else if (sym >= XKB_KEY_Serbian_dje && sym <= XKB_KEY_Serbian_dze)
- *upper += (XKB_KEY_Serbian_DJE - XKB_KEY_Serbian_dje);
- else if (sym >= XKB_KEY_Cyrillic_YU && sym <= XKB_KEY_Cyrillic_HARDSIGN)
- *lower -= (XKB_KEY_Cyrillic_YU - XKB_KEY_Cyrillic_yu);
- else if (sym >= XKB_KEY_Cyrillic_yu && sym <= XKB_KEY_Cyrillic_hardsign)
- *upper += (XKB_KEY_Cyrillic_YU - XKB_KEY_Cyrillic_yu);
- break;
- case 7: /* Greek */
- /* Assume the KeySym is a legal value (ignore discontinuities) */
- if (sym >= XKB_KEY_Greek_ALPHAaccent && sym <= XKB_KEY_Greek_OMEGAaccent)
- *lower += (XKB_KEY_Greek_alphaaccent - XKB_KEY_Greek_ALPHAaccent);
- else if (sym >= XKB_KEY_Greek_alphaaccent && sym <= XKB_KEY_Greek_omegaaccent &&
- sym != XKB_KEY_Greek_iotaaccentdieresis &&
- sym != XKB_KEY_Greek_upsilonaccentdieresis)
- *upper -= (XKB_KEY_Greek_alphaaccent - XKB_KEY_Greek_ALPHAaccent);
- else if (sym >= XKB_KEY_Greek_ALPHA && sym <= XKB_KEY_Greek_OMEGA)
- *lower += (XKB_KEY_Greek_alpha - XKB_KEY_Greek_ALPHA);
- else if (sym >= XKB_KEY_Greek_alpha && sym <= XKB_KEY_Greek_omega &&
- sym != XKB_KEY_Greek_finalsmallsigma)
- *upper -= (XKB_KEY_Greek_alpha - XKB_KEY_Greek_ALPHA);
- break;
- case 0x13: /* Latin 9 */
- if (sym == XKB_KEY_OE)
- *lower = XKB_KEY_oe;
- else if (sym == XKB_KEY_oe)
- *upper = XKB_KEY_OE;
- else if (sym == XKB_KEY_Ydiaeresis)
- *lower = XKB_KEY_ydiaeresis;
- break;
- }
-}
-
-xkb_keysym_t xkbcommon_xkb_keysym_to_upper(xkb_keysym_t ks)
-{
- xkb_keysym_t lower, upper;
-
- xkbcommon_XConvertCase(ks, &lower, &upper);
-
- return upper;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/xcb_qpa_lib.pro b/src/plugins/platforms/xcb/xcb_qpa_lib.pro
index db3d6629b3..34c671c8c7 100644
--- a/src/plugins/platforms/xcb/xcb_qpa_lib.pro
+++ b/src/plugins/platforms/xcb/xcb_qpa_lib.pro
@@ -6,7 +6,8 @@ QT += \
core-private gui-private \
service_support-private theme_support-private \
fontdatabase_support-private \
- edid_support-private
+ edid_support-private \
+ xkbcommon_support-private
qtHaveModule(linuxaccessibility_support-private): \
QT += linuxaccessibility_support-private
@@ -52,7 +53,6 @@ HEADERS = \
qxcbimage.h \
qxcbxsettings.h \
qxcbsystemtraytracker.h \
- qxcbxkbcommon.h \
qxcbeventqueue.h \
qxcbeventdispatcher.h \
qxcbconnection_basic.h \
diff --git a/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.h b/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.h
index 4c5f474595..3497c6a6f1 100644
--- a/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.h
+++ b/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.h
@@ -84,7 +84,7 @@ public:
private:
QScopedPointer<QXdgDesktopPortalThemePrivate> d_ptr;
- Q_DISABLE_COPY(QXdgDesktopPortalTheme)
+ Q_DISABLE_COPY_MOVE(QXdgDesktopPortalTheme)
};
QT_END_NAMESPACE
diff --git a/src/plugins/printsupport/cups/qcupsprintengine_p.h b/src/plugins/printsupport/cups/qcupsprintengine_p.h
index 2a1a83b9d7..c021b0c643 100644
--- a/src/plugins/printsupport/cups/qcupsprintengine_p.h
+++ b/src/plugins/printsupport/cups/qcupsprintengine_p.h
@@ -77,7 +77,7 @@ public:
// end reimplementations QPdfPrintEngine
private:
- Q_DISABLE_COPY(QCupsPrintEngine)
+ Q_DISABLE_COPY_MOVE(QCupsPrintEngine)
};
class QCupsPrintEnginePrivate : public QPdfPrintEnginePrivate
@@ -91,7 +91,7 @@ public:
void closePrintDevice() override;
private:
- Q_DISABLE_COPY(QCupsPrintEnginePrivate)
+ Q_DISABLE_COPY_MOVE(QCupsPrintEnginePrivate)
void changePrinter(const QString &newPrinter);
void setPageSize(const QPageSize &pageSize);
diff --git a/src/plugins/printsupport/windows/qwindowsprintersupport.h b/src/plugins/printsupport/windows/qwindowsprintersupport.h
index 4267701145..400701628e 100644
--- a/src/plugins/printsupport/windows/qwindowsprintersupport.h
+++ b/src/plugins/printsupport/windows/qwindowsprintersupport.h
@@ -46,7 +46,7 @@ QT_BEGIN_NAMESPACE
class QWindowsPrinterSupport : public QPlatformPrinterSupport
{
- Q_DISABLE_COPY(QWindowsPrinterSupport)
+ Q_DISABLE_COPY_MOVE(QWindowsPrinterSupport)
public:
QWindowsPrinterSupport();
~QWindowsPrinterSupport() override;
diff --git a/src/plugins/sqldrivers/mysql/qsql_mysql.cpp b/src/plugins/sqldrivers/mysql/qsql_mysql.cpp
index f2ae3fbc47..febbe58506 100644
--- a/src/plugins/sqldrivers/mysql/qsql_mysql.cpp
+++ b/src/plugins/sqldrivers/mysql/qsql_mysql.cpp
@@ -197,6 +197,7 @@ protected:
QSqlRecord record() const override;
void virtual_hook(int id, void *data) override;
bool nextResult() override;
+ void detachFromResultSet() override;
#if MYSQL_VERSION_ID >= 40108
bool prepare(const QString &stmt) override;
@@ -804,6 +805,15 @@ int QMYSQLResult::numRowsAffected()
return d->rowsAffected;
}
+void QMYSQLResult::detachFromResultSet()
+{
+ Q_D(QMYSQLResult);
+
+ if (d->preparedQuery) {
+ mysql_stmt_free_result(d->stmt);
+ }
+}
+
QVariant QMYSQLResult::lastInsertId() const
{
Q_D(const QMYSQLResult);
@@ -1541,7 +1551,7 @@ QSqlIndex QMYSQLDriver::primaryIndex(const QString& tablename) const
QSqlQuery i(createResult());
QString stmt(QLatin1String("show index from %1;"));
QSqlRecord fil = record(tablename);
- i.exec(stmt.arg(tablename));
+ i.exec(stmt.arg(escapeIdentifier(tablename, QSqlDriver::TableName)));
while (i.isActive() && i.next()) {
if (i.value(2).toString() == QLatin1String("PRIMARY")) {
idx.append(fil.field(i.value(4).toString()));
diff --git a/src/plugins/sqldrivers/odbc/qsql_odbc.cpp b/src/plugins/sqldrivers/odbc/qsql_odbc.cpp
index 1fbbcd0ef1..7f98efccba 100644
--- a/src/plugins/sqldrivers/odbc/qsql_odbc.cpp
+++ b/src/plugins/sqldrivers/odbc/qsql_odbc.cpp
@@ -345,7 +345,7 @@ static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type,
{
int nativeCode = -1;
QString message = qODBCWarn(p, &nativeCode);
- return QSqlError(QLatin1String("QODBC3: ") + err, qODBCWarn(p), type,
+ return QSqlError(QLatin1String("QODBC3: ") + err, message, type,
nativeCode != -1 ? QString::number(nativeCode) : QString());
}
diff --git a/src/plugins/sqldrivers/psql/main.cpp b/src/plugins/sqldrivers/psql/main.cpp
index c5d546f6ff..a0862a238a 100644
--- a/src/plugins/sqldrivers/psql/main.cpp
+++ b/src/plugins/sqldrivers/psql/main.cpp
@@ -61,11 +61,9 @@ QPSQLDriverPlugin::QPSQLDriverPlugin()
QSqlDriver* QPSQLDriverPlugin::create(const QString &name)
{
- if (name == QLatin1String("QPSQL") || name == QLatin1String("QPSQL7")) {
- QPSQLDriver* driver = new QPSQLDriver();
- return driver;
- }
- return 0;
+ if (name == QLatin1String("QPSQL") || name == QLatin1String("QPSQL7"))
+ return new QPSQLDriver;
+ return nullptr;
}
QT_END_NAMESPACE
diff --git a/src/plugins/sqldrivers/psql/qsql_psql.cpp b/src/plugins/sqldrivers/psql/qsql_psql.cpp
index 7ad9db1ea8..c1be91cb22 100644
--- a/src/plugins/sqldrivers/psql/qsql_psql.cpp
+++ b/src/plugins/sqldrivers/psql/qsql_psql.cpp
@@ -42,7 +42,7 @@
#include <qcoreapplication.h>
#include <qvariant.h>
#include <qdatetime.h>
-#include <qregexp.h>
+#include <qregularexpression.h>
#include <qsqlerror.h>
#include <qsqlfield.h>
#include <qsqlindex.h>
@@ -53,6 +53,9 @@
#include <qlocale.h>
#include <QtSql/private/qsqlresult_p.h>
#include <QtSql/private/qsqldriver_p.h>
+#include <QtCore/private/qlocale_tools_p.h>
+
+#include <queue>
#include <libpq-fe.h>
#include <pg_config.h>
@@ -133,7 +136,7 @@ protected:
bool nextResult() override;
QVariant data(int i) override;
bool isNull(int field) override;
- bool reset (const QString &query) override;
+ bool reset(const QString &query) override;
int size() override;
int numRowsAffected() override;
QSqlRecord record() const override;
@@ -147,10 +150,10 @@ class QPSQLDriverPrivate final : public QSqlDriverPrivate
Q_DECLARE_PUBLIC(QPSQLDriver)
public:
QPSQLDriverPrivate() : QSqlDriverPrivate(),
- connection(0),
+ connection(nullptr),
isUtf8(false),
pro(QPSQLDriver::Version6),
- sn(0),
+ sn(nullptr),
pendingNotifyCheck(false),
hasBackslashEscape(false),
stmtCount(0),
@@ -187,11 +190,13 @@ public:
void QPSQLDriverPrivate::appendTables(QStringList &tl, QSqlQuery &t, QChar type)
{
- QString query = QString::fromLatin1("select pg_class.relname, pg_namespace.nspname from pg_class "
- "left join pg_namespace on (pg_class.relnamespace = pg_namespace.oid) "
- "where (pg_class.relkind = '%1') and (pg_class.relname !~ '^Inv') "
- "and (pg_class.relname !~ '^pg_') "
- "and (pg_namespace.nspname != 'information_schema')").arg(type);
+ const QString query =
+ QStringLiteral("SELECT pg_class.relname, pg_namespace.nspname FROM pg_class "
+ "LEFT JOIN pg_namespace ON (pg_class.relnamespace = pg_namespace.oid) "
+ "WHERE (pg_class.relkind = '") + type +
+ QStringLiteral("') AND (pg_class.relname !~ '^Inv') "
+ "AND (pg_class.relname !~ '^pg_') "
+ "AND (pg_namespace.nspname != 'information_schema')");
t.exec(query);
while (t.next()) {
QString schema = t.value(1).toString();
@@ -294,10 +299,10 @@ public:
Q_DECLARE_SQLDRIVER_PRIVATE(QPSQLDriver)
QPSQLResultPrivate(QPSQLResult *q, const QPSQLDriver *drv)
: QSqlResultPrivate(q, drv),
- result(0),
+ result(nullptr),
+ stmtId(InvalidStatementId),
currentSize(-1),
canFetchMoreRows(false),
- stmtId(InvalidStatementId),
preparedQueriesEnabled(false)
{ }
@@ -305,25 +310,25 @@ public:
void deallocatePreparedStmt();
PGresult *result;
- QList<PGresult*> nextResultSets;
+ std::queue<PGresult*> nextResultSets;
+ QString preparedStmtId;
+ StatementId stmtId;
int currentSize;
bool canFetchMoreRows;
- StatementId stmtId;
bool preparedQueriesEnabled;
- QString preparedStmtId;
bool processResults();
};
-static QSqlError qMakeError(const QString& err, QSqlError::ErrorType type,
- const QPSQLDriverPrivate *p, PGresult* result = 0)
+static QSqlError qMakeError(const QString &err, QSqlError::ErrorType type,
+ const QPSQLDriverPrivate *p, PGresult *result = nullptr)
{
const char *s = PQerrorMessage(p->connection);
QString msg = p->isUtf8 ? QString::fromUtf8(s) : QString::fromLocal8Bit(s);
QString errorCode;
if (result) {
- errorCode = QString::fromLatin1(PQresultErrorField(result, PG_DIAG_SQLSTATE));
- msg += QString::fromLatin1("(%1)").arg(errorCode);
+ errorCode = QString::fromLatin1(PQresultErrorField(result, PG_DIAG_SQLSTATE));
+ msg += QString::fromLatin1("(%1)").arg(errorCode);
}
return QSqlError(QLatin1String("QPSQL: ") + err, msg, type, errorCode);
}
@@ -423,7 +428,7 @@ static QVariant::Type qDecodePSQLType(int t)
void QPSQLResultPrivate::deallocatePreparedStmt()
{
- const QString stmt = QLatin1String("DEALLOCATE ") + preparedStmtId;
+ const QString stmt = QStringLiteral("DEALLOCATE ") + preparedStmtId;
PGresult *result = drv_d_func()->exec(stmt);
if (PQresultStatus(result) != PGRES_COMMAND_OK)
@@ -432,7 +437,7 @@ void QPSQLResultPrivate::deallocatePreparedStmt()
preparedStmtId.clear();
}
-QPSQLResult::QPSQLResult(const QPSQLDriver* db)
+QPSQLResult::QPSQLResult(const QPSQLDriver *db)
: QSqlResult(*new QPSQLResultPrivate(this, db))
{
Q_D(QPSQLResult);
@@ -460,8 +465,10 @@ void QPSQLResult::cleanup()
if (d->result)
PQclear(d->result);
d->result = nullptr;
- while (!d->nextResultSets.isEmpty())
- PQclear(d->nextResultSets.takeFirst());
+ while (!d->nextResultSets.empty()) {
+ PQclear(d->nextResultSets.front());
+ d->nextResultSets.pop();
+ }
if (d->stmtId != InvalidStatementId) {
if (d->drv_d_func())
d->drv_d_func()->finishQuery(d->stmtId);
@@ -622,7 +629,11 @@ bool QPSQLResult::nextResult()
if (d->result)
PQclear(d->result);
- d->result = d->nextResultSets.isEmpty() ? nullptr : d->nextResultSets.takeFirst();
+ d->result = nullptr;
+ if (!d->nextResultSets.empty()) {
+ d->result = d->nextResultSets.front();
+ d->nextResultSets.pop();
+ }
return d->processResults();
}
@@ -646,34 +657,37 @@ QVariant QPSQLResult::data(int i)
return d->drv_d_func()->isUtf8 ? QString::fromUtf8(val) : QString::fromLatin1(val);
case QVariant::LongLong:
if (val[0] == '-')
- return QString::fromLatin1(val).toLongLong();
+ return QByteArray::fromRawData(val, qstrlen(val)).toLongLong();
else
- return QString::fromLatin1(val).toULongLong();
+ return QByteArray::fromRawData(val, qstrlen(val)).toULongLong();
case QVariant::Int:
return atoi(val);
case QVariant::Double: {
if (ptype == QNUMERICOID) {
- if (numericalPrecisionPolicy() != QSql::HighPrecision) {
- QVariant retval;
- bool convert;
- double dbl=QString::fromLatin1(val).toDouble(&convert);
- if (numericalPrecisionPolicy() == QSql::LowPrecisionInt64)
- retval = (qlonglong)dbl;
- else if (numericalPrecisionPolicy() == QSql::LowPrecisionInt32)
- retval = (int)dbl;
- else if (numericalPrecisionPolicy() == QSql::LowPrecisionDouble)
- retval = dbl;
- if (!convert)
- return QVariant();
- return retval;
- }
- return QString::fromLatin1(val);
+ if (numericalPrecisionPolicy() == QSql::HighPrecision)
+ return QString::fromLatin1(val);
+ }
+ bool ok;
+ double dbl = qstrtod(val, nullptr, &ok);
+ if (!ok) {
+ if (qstricmp(val, "NaN") == 0)
+ dbl = qQNaN();
+ else if (qstricmp(val, "Infinity") == 0)
+ dbl = qInf();
+ else if (qstricmp(val, "-Infinity") == 0)
+ dbl = -qInf();
+ else
+ return QVariant();
}
- if (qstricmp(val, "Infinity") == 0)
- return qInf();
- if (qstricmp(val, "-Infinity") == 0)
- return -qInf();
- return QString::fromLatin1(val).toDouble();
+ if (ptype == QNUMERICOID) {
+ if (numericalPrecisionPolicy() == QSql::LowPrecisionInt64)
+ return QVariant((qlonglong)dbl);
+ else if (numericalPrecisionPolicy() == QSql::LowPrecisionInt32)
+ return QVariant((int)dbl);
+ else if (numericalPrecisionPolicy() == QSql::LowPrecisionDouble)
+ return QVariant(dbl);
+ }
+ return dbl;
}
case QVariant::Date:
if (val[0] == '\0') {
@@ -701,7 +715,7 @@ QVariant QPSQLResult::data(int i)
#if QT_CONFIG(datestring)
if (dtval.length() < 10) {
return QVariant(QDateTime());
- } else {
+ } else {
QChar sign = dtval[dtval.size() - 3];
if (sign == QLatin1Char('-') || sign == QLatin1Char('+')) dtval += QLatin1String(":00");
return QVariant(QDateTime::fromString(dtval, Qt::ISODate).toLocalTime());
@@ -731,7 +745,7 @@ bool QPSQLResult::isNull(int field)
return PQgetisnull(d->result, currentRow, field);
}
-bool QPSQLResult::reset (const QString& query)
+bool QPSQLResult::reset(const QString &query)
{
Q_D(QPSQLResult);
cleanup();
@@ -754,7 +768,7 @@ bool QPSQLResult::reset (const QString& query)
if (!isForwardOnly()) {
// Fetch all result sets right away
while (PGresult *nextResultSet = d->drv_d_func()->getResult(d->stmtId))
- d->nextResultSets.append(nextResultSet);
+ d->nextResultSets.push(nextResultSet);
}
return d->processResults();
}
@@ -768,7 +782,8 @@ int QPSQLResult::size()
int QPSQLResult::numRowsAffected()
{
Q_D(const QPSQLResult);
- return QString::fromLatin1(PQcmdTuples(d->result)).toInt();
+ const char *tuples = PQcmdTuples(d->result);
+ return QByteArray::fromRawData(tuples, qstrlen(tuples)).toInt();
}
QVariant QPSQLResult::lastInsertId() const
@@ -777,7 +792,7 @@ QVariant QPSQLResult::lastInsertId() const
if (d->drv_d_func()->pro >= QPSQLDriver::Version8_1) {
QSqlQuery qry(driver()->createResult());
// Most recent sequence value obtained from nextval
- if (qry.exec(QLatin1String("SELECT lastval();")) && qry.next())
+ if (qry.exec(QStringLiteral("SELECT lastval();")) && qry.next())
return qry.value(0);
} else if (isActive()) {
Oid id = PQoidValue(d->result);
@@ -857,7 +872,6 @@ QSqlRecord QPSQLResult::record() const
void QPSQLResult::virtual_hook(int id, void *data)
{
Q_ASSERT(data);
-
QSqlResult::virtual_hook(id, data);
}
@@ -868,15 +882,13 @@ static QString qCreateParamString(const QVector<QVariant> &boundValues, const QS
QString params;
QSqlField f;
- for (int i = 0; i < boundValues.count(); ++i) {
- const QVariant &val = boundValues.at(i);
-
+ for (const QVariant &val : boundValues) {
f.setType(val.type());
if (val.isNull())
f.clear();
else
f.setValue(val);
- if(!params.isNull())
+ if (!params.isNull())
params.append(QLatin1String(", "));
params.append(driver->formatValue(f));
}
@@ -886,7 +898,7 @@ static QString qCreateParamString(const QVector<QVariant> &boundValues, const QS
QString qMakePreparedStmtId()
{
static QBasicAtomicInt qPreparedStmtCount = Q_BASIC_ATOMIC_INITIALIZER(0);
- QString id = QLatin1String("qpsqlpstmt_") + QString::number(qPreparedStmtCount.fetchAndAddRelaxed(1) + 1, 16);
+ QString id = QStringLiteral("qpsqlpstmt_") + QString::number(qPreparedStmtCount.fetchAndAddRelaxed(1) + 1, 16);
return id;
}
@@ -902,7 +914,7 @@ bool QPSQLResult::prepare(const QString &query)
d->deallocatePreparedStmt();
const QString stmtId = qMakePreparedStmtId();
- const QString stmt = QString::fromLatin1("PREPARE %1 AS ").arg(stmtId).append(d->positionalToNamedBinding(query));
+ const QString stmt = QStringLiteral("PREPARE %1 AS ").arg(stmtId).append(d->positionalToNamedBinding(query));
PGresult *result = d->drv_d_func()->exec(stmt);
@@ -930,9 +942,9 @@ bool QPSQLResult::exec()
QString stmt;
const QString params = qCreateParamString(boundValues(), driver());
if (params.isEmpty())
- stmt = QString::fromLatin1("EXECUTE %1").arg(d->preparedStmtId);
+ stmt = QStringLiteral("EXECUTE %1").arg(d->preparedStmtId);
else
- stmt = QString::fromLatin1("EXECUTE %1 (%2)").arg(d->preparedStmtId, params);
+ stmt = QStringLiteral("EXECUTE %1 (%2)").arg(d->preparedStmtId, params);
d->stmtId = d->drv_d_func()->sendQuery(stmt);
if (d->stmtId == InvalidStatementId) {
@@ -948,7 +960,7 @@ bool QPSQLResult::exec()
if (!isForwardOnly()) {
// Fetch all result sets right away
while (PGresult *nextResultSet = d->drv_d_func()->getResult(d->stmtId))
- d->nextResultSets.append(nextResultSet);
+ d->nextResultSets.push(nextResultSet);
}
return d->processResults();
}
@@ -957,7 +969,7 @@ bool QPSQLResult::exec()
bool QPSQLDriverPrivate::setEncodingUtf8()
{
- PGresult* result = exec("SET CLIENT_ENCODING TO 'UNICODE'");
+ PGresult *result = exec("SET CLIENT_ENCODING TO 'UNICODE'");
int status = PQresultStatus(result);
PQclear(result);
return status == PGRES_COMMAND_OK;
@@ -965,7 +977,7 @@ bool QPSQLDriverPrivate::setEncodingUtf8()
void QPSQLDriverPrivate::setDatestyle()
{
- PGresult* result = exec("SET DATESTYLE TO 'ISO'");
+ PGresult *result = exec("SET DATESTYLE TO 'ISO'");
int status = PQresultStatus(result);
if (status != PGRES_COMMAND_OK)
qWarning("%s", PQerrorMessage(connection));
@@ -994,7 +1006,7 @@ void QPSQLDriverPrivate::detectBackslashEscape()
hasBackslashEscape = true;
} else {
hasBackslashEscape = false;
- PGresult* result = exec(QLatin1String("SELECT '\\\\' x"));
+ PGresult *result = exec(QStringLiteral("SELECT '\\\\' x"));
int status = PQresultStatus(result);
if (status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK)
if (QString::fromLatin1(PQgetvalue(result, 0, 0)) == QLatin1String("\\"))
@@ -1072,20 +1084,21 @@ static QPSQLDriver::Protocol qMakePSQLVersion(int vMaj, int vMin)
static QPSQLDriver::Protocol qFindPSQLVersion(const QString &versionString)
{
- const QRegExp rx(QStringLiteral("(\\d+)(?:\\.(\\d+))?"));
- if (rx.indexIn(versionString) != -1) {
+ const QRegularExpression rx(QStringLiteral("(\\d+)(?:\\.(\\d+))?"));
+ const QRegularExpressionMatch match = rx.match(versionString);
+ if (match.hasMatch()) {
// Beginning with PostgreSQL version 10, a major release is indicated by
// increasing the first part of the version, e.g. 10 to 11.
// Before version 10, a major release was indicated by increasing either
// the first or second part of the version number, e.g. 9.5 to 9.6.
- int vMaj = rx.cap(1).toInt();
+ int vMaj = match.capturedRef(1).toInt();
int vMin;
if (vMaj >= 10) {
vMin = 0;
} else {
- if (rx.cap(2).isEmpty())
+ if (match.capturedRef(2).isEmpty())
return QPSQLDriver::VersionUnknown;
- vMin = rx.cap(2).toInt();
+ vMin = match.capturedRef(2).toInt();
}
return qMakePSQLVersion(vMaj, vMin);
}
@@ -1096,7 +1109,7 @@ static QPSQLDriver::Protocol qFindPSQLVersion(const QString &versionString)
QPSQLDriver::Protocol QPSQLDriverPrivate::getPSQLVersion()
{
QPSQLDriver::Protocol serverVersion = QPSQLDriver::Version6;
- PGresult* result = exec("select version()");
+ PGresult *result = exec("SELECT version()");
int status = PQresultStatus(result);
if (status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK) {
serverVersion = qFindPSQLVersion(
@@ -1202,12 +1215,12 @@ static QString qQuote(QString s)
return s;
}
-bool QPSQLDriver::open(const QString & db,
- const QString & user,
- const QString & password,
- const QString & host,
- int port,
- const QString& connOpts)
+bool QPSQLDriver::open(const QString &db,
+ const QString &user,
+ const QString &password,
+ const QString &host,
+ int port,
+ const QString &connOpts)
{
Q_D(QPSQLDriver);
if (isOpen())
@@ -1236,7 +1249,7 @@ bool QPSQLDriver::open(const QString & db,
setLastError(qMakeError(tr("Unable to connect"), QSqlError::ConnectionError, d));
setOpenError(true);
PQfinish(d->connection);
- d->connection = 0;
+ d->connection = nullptr;
return false;
}
@@ -1260,12 +1273,12 @@ void QPSQLDriver::close()
if (d->sn) {
disconnect(d->sn, SIGNAL(activated(int)), this, SLOT(_q_handleNotification(int)));
delete d->sn;
- d->sn = 0;
+ d->sn = nullptr;
}
if (d->connection)
PQfinish(d->connection);
- d->connection = 0;
+ d->connection = nullptr;
setOpen(false);
setOpenError(false);
}
@@ -1283,7 +1296,7 @@ bool QPSQLDriver::beginTransaction()
qWarning("QPSQLDriver::beginTransaction: Database not open");
return false;
}
- PGresult* res = d->exec("BEGIN");
+ PGresult *res = d->exec("BEGIN");
if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) {
setLastError(qMakeError(tr("Could not begin transaction"),
QSqlError::TransactionError, d, res));
@@ -1301,7 +1314,7 @@ bool QPSQLDriver::commitTransaction()
qWarning("QPSQLDriver::commitTransaction: Database not open");
return false;
}
- PGresult* res = d->exec("COMMIT");
+ PGresult *res = d->exec("COMMIT");
bool transaction_failed = false;
@@ -1330,7 +1343,7 @@ bool QPSQLDriver::rollbackTransaction()
qWarning("QPSQLDriver::rollbackTransaction: Database not open");
return false;
}
- PGresult* res = d->exec("ROLLBACK");
+ PGresult *res = d->exec("ROLLBACK");
if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) {
setLastError(qMakeError(tr("Could not rollback transaction"),
QSqlError::TransactionError, d, res));
@@ -1355,8 +1368,8 @@ QStringList QPSQLDriver::tables(QSql::TableType type) const
if (type & QSql::Views)
const_cast<QPSQLDriverPrivate*>(d)->appendTables(tl, t, QLatin1Char('v'));
if (type & QSql::SystemTables) {
- t.exec(QLatin1String("select relname from pg_class where (relkind = 'r') "
- "and (relname like 'pg_%') "));
+ t.exec(QStringLiteral("SELECT relname FROM pg_class WHERE (relkind = 'r') "
+ "AND (relname LIKE 'pg_%') "));
while (t.next())
tl.append(t.value(0).toString());
}
@@ -1373,7 +1386,7 @@ static void qSplitTableName(QString &tablename, QString &schema)
tablename = tablename.mid(dot + 1);
}
-QSqlIndex QPSQLDriver::primaryIndex(const QString& tablename) const
+QSqlIndex QPSQLDriver::primaryIndex(const QString &tablename) const
{
QSqlIndex idx(tablename);
if (!isOpen())
@@ -1383,31 +1396,23 @@ QSqlIndex QPSQLDriver::primaryIndex(const QString& tablename) const
QString tbl = tablename;
QString schema;
qSplitTableName(tbl, schema);
-
- if (isIdentifierEscaped(tbl, QSqlDriver::TableName))
- tbl = stripDelimiters(tbl, QSqlDriver::TableName);
- else
- tbl = std::move(tbl).toLower();
-
- if (isIdentifierEscaped(schema, QSqlDriver::TableName))
- schema = stripDelimiters(schema, QSqlDriver::TableName);
- else
- schema = std::move(schema).toLower();
-
- QString stmt = QLatin1String("SELECT pg_attribute.attname, pg_attribute.atttypid::int, "
- "pg_class.relname "
- "FROM pg_attribute, pg_class "
- "WHERE %1 pg_class.oid IN "
- "(SELECT indexrelid FROM pg_index WHERE indisprimary = true AND indrelid IN "
- "(SELECT oid FROM pg_class WHERE relname = '%2')) "
- "AND pg_attribute.attrelid = pg_class.oid "
- "AND pg_attribute.attisdropped = false "
- "ORDER BY pg_attribute.attnum");
+ schema = stripDelimiters(schema, QSqlDriver::TableName);
+ tbl = stripDelimiters(tbl, QSqlDriver::TableName);
+
+ QString stmt = QStringLiteral("SELECT pg_attribute.attname, pg_attribute.atttypid::int, "
+ "pg_class.relname "
+ "FROM pg_attribute, pg_class "
+ "WHERE %1 pg_class.oid IN "
+ "(SELECT indexrelid FROM pg_index WHERE indisprimary = true AND indrelid IN "
+ "(SELECT oid FROM pg_class WHERE relname = '%2')) "
+ "AND pg_attribute.attrelid = pg_class.oid "
+ "AND pg_attribute.attisdropped = false "
+ "ORDER BY pg_attribute.attnum");
if (schema.isEmpty())
- stmt = stmt.arg(QLatin1String("pg_table_is_visible(pg_class.oid) AND"));
+ stmt = stmt.arg(QStringLiteral("pg_table_is_visible(pg_class.oid) AND"));
else
- stmt = stmt.arg(QString::fromLatin1("pg_class.relnamespace = (select oid from "
- "pg_namespace where pg_namespace.nspname = '%1') AND").arg(schema));
+ stmt = stmt.arg(QStringLiteral("pg_class.relnamespace = (SELECT oid FROM "
+ "pg_namespace WHERE pg_namespace.nspname = '%1') AND").arg(schema));
i.exec(stmt.arg(tbl));
while (i.isActive() && i.next()) {
@@ -1418,7 +1423,7 @@ QSqlIndex QPSQLDriver::primaryIndex(const QString& tablename) const
return idx;
}
-QSqlRecord QPSQLDriver::record(const QString& tablename) const
+QSqlRecord QPSQLDriver::record(const QString &tablename) const
{
QSqlRecord info;
if (!isOpen())
@@ -1427,34 +1432,26 @@ QSqlRecord QPSQLDriver::record(const QString& tablename) const
QString tbl = tablename;
QString schema;
qSplitTableName(tbl, schema);
-
- if (isIdentifierEscaped(tbl, QSqlDriver::TableName))
- tbl = stripDelimiters(tbl, QSqlDriver::TableName);
- else
- tbl = std::move(tbl).toLower();
-
- if (isIdentifierEscaped(schema, QSqlDriver::TableName))
- schema = stripDelimiters(schema, QSqlDriver::TableName);
- else
- schema = std::move(schema).toLower();
-
- QString stmt = QLatin1String("select pg_attribute.attname, pg_attribute.atttypid::int, "
- "pg_attribute.attnotnull, pg_attribute.attlen, pg_attribute.atttypmod, "
- "pg_attrdef.adsrc "
- "from pg_class, pg_attribute "
- "left join pg_attrdef on (pg_attrdef.adrelid = "
- "pg_attribute.attrelid and pg_attrdef.adnum = pg_attribute.attnum) "
- "where %1 "
- "and pg_class.relname = '%2' "
- "and pg_attribute.attnum > 0 "
- "and pg_attribute.attrelid = pg_class.oid "
- "and pg_attribute.attisdropped = false "
- "order by pg_attribute.attnum");
+ schema = stripDelimiters(schema, QSqlDriver::TableName);
+ tbl = stripDelimiters(tbl, QSqlDriver::TableName);
+
+ QString stmt = QStringLiteral("SELECT pg_attribute.attname, pg_attribute.atttypid::int, "
+ "pg_attribute.attnotnull, pg_attribute.attlen, pg_attribute.atttypmod, "
+ "pg_attrdef.adsrc "
+ "FROM pg_class, pg_attribute "
+ "LEFT JOIN pg_attrdef ON (pg_attrdef.adrelid = "
+ "pg_attribute.attrelid AND pg_attrdef.adnum = pg_attribute.attnum) "
+ "WHERE %1 "
+ "AND pg_class.relname = '%2' "
+ "AND pg_attribute.attnum > 0 "
+ "AND pg_attribute.attrelid = pg_class.oid "
+ "AND pg_attribute.attisdropped = false "
+ "ORDER BY pg_attribute.attnum");
if (schema.isEmpty())
- stmt = stmt.arg(QLatin1String("pg_table_is_visible(pg_class.oid)"));
+ stmt = stmt.arg(QStringLiteral("pg_table_is_visible(pg_class.oid)"));
else
- stmt = stmt.arg(QString::fromLatin1("pg_class.relnamespace = (select oid from "
- "pg_namespace where pg_namespace.nspname = '%1')").arg(schema));
+ stmt = stmt.arg(QStringLiteral("pg_class.relnamespace = (SELECT oid FROM "
+ "pg_namespace WHERE pg_namespace.nspname = '%1')").arg(schema));
QSqlQuery query(createResult());
query.exec(stmt.arg(tbl));
@@ -1496,9 +1493,10 @@ inline void assignSpecialPsqlFloatValue(FloatType val, QString *target)
QString QPSQLDriver::formatValue(const QSqlField &field, bool trimStrings) const
{
Q_D(const QPSQLDriver);
+ const auto nullStr = [](){ return QStringLiteral("NULL"); };
QString r;
if (field.isNull()) {
- r = QLatin1String("NULL");
+ r = nullStr();
} else {
switch (int(field.type())) {
case QVariant::DateTime:
@@ -1507,14 +1505,14 @@ QString QPSQLDriver::formatValue(const QSqlField &field, bool trimStrings) const
// we force the value to be considered with a timezone information, and we force it to be UTC
// this is safe since postgresql stores only the UTC value and not the timezone offset (only used
// while parsing), so we have correct behavior in both case of with timezone and without tz
- r = QLatin1String("TIMESTAMP WITH TIME ZONE ") + QLatin1Char('\'') +
- QLocale::c().toString(field.value().toDateTime().toUTC(), QLatin1String("yyyy-MM-ddThh:mm:ss.zzz")) +
+ r = QStringLiteral("TIMESTAMP WITH TIME ZONE ") + QLatin1Char('\'') +
+ QLocale::c().toString(field.value().toDateTime().toUTC(), QStringLiteral("yyyy-MM-ddThh:mm:ss.zzz")) +
QLatin1Char('Z') + QLatin1Char('\'');
} else {
- r = QLatin1String("NULL");
+ r = nullStr();
}
#else
- r = QLatin1String("NULL");
+ r = nullStr();
#endif // datestring
break;
case QVariant::Time:
@@ -1524,19 +1522,19 @@ QString QPSQLDriver::formatValue(const QSqlField &field, bool trimStrings) const
} else
#endif
{
- r = QLatin1String("NULL");
+ r = nullStr();
}
break;
case QVariant::String:
r = QSqlDriver::formatValue(field, trimStrings);
if (d->hasBackslashEscape)
- r.replace(QLatin1String("\\"), QLatin1String("\\\\"));
+ r.replace(QLatin1Char('\\'), QLatin1String("\\\\"));
break;
case QVariant::Bool:
if (field.value().toBool())
- r = QLatin1String("TRUE");
+ r = QStringLiteral("TRUE");
else
- r = QLatin1String("FALSE");
+ r = QStringLiteral("FALSE");
break;
case QVariant::ByteArray: {
QByteArray ba(field.value().toByteArray());
@@ -1576,7 +1574,7 @@ QString QPSQLDriver::formatValue(const QSqlField &field, bool trimStrings) const
QString QPSQLDriver::escapeIdentifier(const QString &identifier, IdentifierType) const
{
QString res = identifier;
- if(!identifier.isEmpty() && !identifier.startsWith(QLatin1Char('"')) && !identifier.endsWith(QLatin1Char('"')) ) {
+ if (!identifier.isEmpty() && !identifier.startsWith(QLatin1Char('"')) && !identifier.endsWith(QLatin1Char('"')) ) {
res.replace(QLatin1Char('"'), QLatin1String("\"\""));
res.prepend(QLatin1Char('"')).append(QLatin1Char('"'));
res.replace(QLatin1Char('.'), QLatin1String("\".\""));
@@ -1615,7 +1613,7 @@ bool QPSQLDriver::subscribeToNotification(const QString &name)
// Add the name to the list of subscriptions here so that QSQLDriverPrivate::exec knows
// to check for notifications immediately after executing the LISTEN
d->seid << name;
- QString query = QLatin1String("LISTEN ") + escapeIdentifier(name, QSqlDriver::TableName);
+ QString query = QStringLiteral("LISTEN ") + escapeIdentifier(name, QSqlDriver::TableName);
PGresult *result = d->exec(query);
if (PQresultStatus(result) != PGRES_COMMAND_OK) {
d->seid.removeLast();
@@ -1651,7 +1649,7 @@ bool QPSQLDriver::unsubscribeFromNotification(const QString &name)
return false;
}
- QString query = QLatin1String("UNLISTEN ") + escapeIdentifier(name, QSqlDriver::TableName);
+ QString query = QStringLiteral("UNLISTEN ") + escapeIdentifier(name, QSqlDriver::TableName);
PGresult *result = d->exec(query);
if (PQresultStatus(result) != PGRES_COMMAND_OK) {
setLastError(qMakeError(tr("Unable to unsubscribe"), QSqlError::StatementError, d, result));
@@ -1665,7 +1663,7 @@ bool QPSQLDriver::unsubscribeFromNotification(const QString &name)
if (d->seid.isEmpty()) {
disconnect(d->sn, SIGNAL(activated(int)), this, SLOT(_q_handleNotification(int)));
delete d->sn;
- d->sn = 0;
+ d->sn = nullptr;
}
return true;
@@ -1683,8 +1681,8 @@ void QPSQLDriver::_q_handleNotification(int)
d->pendingNotifyCheck = false;
PQconsumeInput(d->connection);
- PGnotify *notify = 0;
- while((notify = PQnotifies(d->connection)) != 0) {
+ PGnotify *notify = nullptr;
+ while ((notify = PQnotifies(d->connection)) != nullptr) {
QString name(QLatin1String(notify->relname));
if (d->seid.contains(name)) {
QString payload;
diff --git a/src/plugins/sqldrivers/psql/qsql_psql_p.h b/src/plugins/sqldrivers/psql/qsql_psql_p.h
index 7e1849d857..99e0b5f60f 100644
--- a/src/plugins/sqldrivers/psql/qsql_psql_p.h
+++ b/src/plugins/sqldrivers/psql/qsql_psql_p.h
@@ -96,22 +96,22 @@ public:
UnknownLaterVersion = 100000
};
- explicit QPSQLDriver(QObject *parent=0);
- explicit QPSQLDriver(PGconn *conn, QObject *parent=0);
+ explicit QPSQLDriver(QObject *parent = nullptr);
+ explicit QPSQLDriver(PGconn *conn, QObject *parent = nullptr);
~QPSQLDriver();
bool hasFeature(DriverFeature f) const override;
- bool open(const QString & db,
- const QString & user,
- const QString & password,
- const QString & host,
+ bool open(const QString &db,
+ const QString &user,
+ const QString &password,
+ const QString &host,
int port,
- const QString& connOpts) override;
+ const QString &connOpts) override;
bool isOpen() const override;
void close() override;
QSqlResult *createResult() const override;
QStringList tables(QSql::TableType) const override;
- QSqlIndex primaryIndex(const QString& tablename) const override;
- QSqlRecord record(const QString& tablename) const override;
+ QSqlIndex primaryIndex(const QString &tablename) const override;
+ QSqlRecord record(const QString &tablename) const override;
Protocol protocol() const;
QVariant handle() const override;
diff --git a/src/plugins/styles/android/qandroidstyle_p.h b/src/plugins/styles/android/qandroidstyle_p.h
index 3faa08afb9..6cb30a2f79 100644
--- a/src/plugins/styles/android/qandroidstyle_p.h
+++ b/src/plugins/styles/android/qandroidstyle_p.h
@@ -371,7 +371,7 @@ public:
void unpolish(QWidget *widget);
private:
- Q_DISABLE_COPY(QAndroidStyle)
+ Q_DISABLE_COPY_MOVE(QAndroidStyle)
static ItemType qtControl(QStyle::ComplexControl control);
static ItemType qtControl(QStyle::ContentsType contentsType);
static ItemType qtControl(QStyle::ControlElement controlElement);
diff --git a/src/plugins/styles/mac/qmacstyle_mac.mm b/src/plugins/styles/mac/qmacstyle_mac.mm
index ce08725684..35b2fa50ac 100644
--- a/src/plugins/styles/mac/qmacstyle_mac.mm
+++ b/src/plugins/styles/mac/qmacstyle_mac.mm
@@ -4081,7 +4081,7 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter
titleRect.width());
const auto text = p->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight, titleRect.width());
- proxy()->drawItemText(p, titleRect, Qt::AlignCenter | Qt::TextShowMnemonic, dwOpt->palette,
+ proxy()->drawItemText(p, titleRect, Qt::AlignCenter, dwOpt->palette,
dwOpt->state & State_Enabled, text, QPalette::WindowText);
}
p->restore();
@@ -6123,8 +6123,9 @@ QSize QMacStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt,
switch (ct) {
#if QT_CONFIG(spinbox)
case CT_SpinBox:
- if (qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
- const int buttonWidth = 20; // FIXME Use subControlRect()
+ if (const QStyleOptionSpinBox *vopt = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
+ const bool hasButtons = (vopt->buttonSymbols != QAbstractSpinBox::NoButtons);
+ const int buttonWidth = hasButtons ? proxy()->subControlRect(CC_SpinBox, vopt, SC_SpinBoxUp, widget).width() : 0;
sz += QSize(buttonWidth, 0);
}
break;
diff --git a/src/plugins/styles/mac/qmacstyle_mac_p.h b/src/plugins/styles/mac/qmacstyle_mac_p.h
index d6874001d3..88f104cccf 100644
--- a/src/plugins/styles/mac/qmacstyle_mac_p.h
+++ b/src/plugins/styles/mac/qmacstyle_mac_p.h
@@ -115,7 +115,7 @@ public:
const QWidget *widget = 0) const;
private:
- Q_DISABLE_COPY(QMacStyle)
+ Q_DISABLE_COPY_MOVE(QMacStyle)
Q_DECLARE_PRIVATE(QMacStyle)
};
diff --git a/src/plugins/styles/windowsvista/qwindowsvistastyle.cpp b/src/plugins/styles/windowsvista/qwindowsvistastyle.cpp
index 771552a121..8a3ae17b1d 100644
--- a/src/plugins/styles/windowsvista/qwindowsvistastyle.cpp
+++ b/src/plugins/styles/windowsvista/qwindowsvistastyle.cpp
@@ -632,7 +632,7 @@ void QWindowsVistaStyle::drawPrimitive(PrimitiveElement element, const QStyleOpt
{
QPen pen = painter->pen();
int margin = 3;
- painter->setPen(option->palette.background().color().darker(114));
+ painter->setPen(option->palette.window().color().darker(114));
if (option->state & State_Horizontal) {
int x1 = option->rect.center().x();
painter->drawLine(QPoint(x1, option->rect.top() + margin), QPoint(x1, option->rect.bottom() - margin));
@@ -704,7 +704,7 @@ void QWindowsVistaStyle::drawPrimitive(PrimitiveElement element, const QStyleOpt
if (sectionSize.width() > 0 && sectionSize.height() > 0) {
QString key = QString::fromLatin1("qvdelegate-%1-%2-%3-%4-%5").arg(sectionSize.width())
.arg(sectionSize.height()).arg(selected).arg(active).arg(hover);
- if (!QPixmapCache::find(key, pixmap)) {
+ if (!QPixmapCache::find(key, &pixmap)) {
pixmap = QPixmap(sectionSize);
pixmap.fill(Qt::transparent);
@@ -1053,7 +1053,7 @@ void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption
}
QString name = QString::fromLatin1("qiprogress-%1-%2").arg(pixmapSize.width()).arg(pixmapSize.height());
QPixmap pixmap;
- if (!QPixmapCache::find(name, pixmap)) {
+ if (!QPixmapCache::find(name, &pixmap)) {
QImage image(pixmapSize, QImage::Format_ARGB32);
image.fill(Qt::transparent);
QPainter imagePainter(&image);
@@ -1363,7 +1363,7 @@ void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption
case CE_ToolBar:
if (const QStyleOptionToolBar *toolbar = qstyleoption_cast<const QStyleOptionToolBar *>(option)) {
QPalette pal = option->palette;
- pal.setColor(QPalette::Dark, option->palette.background().color().darker(130));
+ pal.setColor(QPalette::Dark, option->palette.window().color().darker(130));
QStyleOptionToolBar copyOpt = *toolbar;
copyOpt.palette = pal;
QWindowsStyle::drawControl(element, &copyOpt, painter, widget);
@@ -1388,8 +1388,8 @@ void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption
painter->translate(-rect.left() + 1, -rect.top());
}
- painter->setBrush(option->palette.background().color().darker(110));
- painter->setPen(option->palette.background().color().darker(130));
+ painter->setBrush(option->palette.window().color().darker(110));
+ painter->setPen(option->palette.window().color().darker(130));
painter->drawRect(rect.adjusted(0, 1, -1, -3));
int buttonMargin = 4;
diff --git a/src/plugins/styles/windowsvista/qwindowsvistastyle_p.h b/src/plugins/styles/windowsvista/qwindowsvistastyle_p.h
index 0ebb0eb41a..43a2a670f8 100644
--- a/src/plugins/styles/windowsvista/qwindowsvistastyle_p.h
+++ b/src/plugins/styles/windowsvista/qwindowsvistastyle_p.h
@@ -99,7 +99,7 @@ public:
QPalette standardPalette() const override;
private:
- Q_DISABLE_COPY(QWindowsVistaStyle)
+ Q_DISABLE_COPY_MOVE(QWindowsVistaStyle)
Q_DECLARE_PRIVATE(QWindowsVistaStyle)
friend class QStyleFactory;
};
diff --git a/src/plugins/styles/windowsvista/qwindowsxpstyle.cpp b/src/plugins/styles/windowsvista/qwindowsxpstyle.cpp
index 4b583e13d3..90026e5bf0 100644
--- a/src/plugins/styles/windowsvista/qwindowsxpstyle.cpp
+++ b/src/plugins/styles/windowsvista/qwindowsxpstyle.cpp
@@ -511,8 +511,8 @@ QRegion QWindowsXPStylePrivate::region(XPThemeData &themeData)
if (numBytes == 0)
return QRegion();
- char *buf = new char[numBytes];
- if (buf == 0)
+ char *buf = new (std::nothrow) char[numBytes];
+ if (!buf)
return QRegion();
RGNDATA *rd = reinterpret_cast<RGNDATA*>(buf);
@@ -740,7 +740,8 @@ bool QWindowsXPStylePrivate::drawBackgroundDirectly(HDC dc, XPThemeData &themeDa
{
QPainter *painter = themeData.painter;
- const QPointF redirectionDelta(painter->deviceMatrix().dx(), painter->deviceMatrix().dy());
+ const auto deviceTransform = painter->deviceTransform();
+ const QPointF redirectionDelta(deviceTransform.dx(), deviceTransform.dy());
const QRect area = scaleRect(QRectF(themeData.rect), additionalDevicePixelRatio).translated(redirectionDelta).toRect();
QRegion sysRgn = painter->paintEngine()->systemClip();
@@ -835,7 +836,7 @@ bool QWindowsXPStylePrivate::drawBackgroundThruNativeBuffer(XPThemeData &themeDa
alphaType = data.alphaType;
potentialInvalidAlpha = data.hadInvalidAlpha;
- haveCachedPixmap = QPixmapCache::find(pixmapCacheKey, cachedPixmap);
+ haveCachedPixmap = QPixmapCache::find(pixmapCacheKey, &cachedPixmap);
#ifdef DEBUG_XP_STYLE
char buf[25];
diff --git a/src/plugins/styles/windowsvista/qwindowsxpstyle_p.h b/src/plugins/styles/windowsvista/qwindowsxpstyle_p.h
index 7e9f4ddda6..0f70105b0e 100644
--- a/src/plugins/styles/windowsvista/qwindowsxpstyle_p.h
+++ b/src/plugins/styles/windowsvista/qwindowsxpstyle_p.h
@@ -96,7 +96,7 @@ public:
const QWidget *widget = nullptr) const override;
private:
- Q_DISABLE_COPY(QWindowsXPStyle)
+ Q_DISABLE_COPY_MOVE(QWindowsXPStyle)
Q_DECLARE_PRIVATE(QWindowsXPStyle)
friend class QStyleFactory;
};