diff options
Diffstat (limited to 'src/plugins')
98 files changed, 2371 insertions, 2949 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 847479047f..a09ae72cb5 100644 --- a/src/plugins/bearer/qnetworksession_impl.cpp +++ b/src/plugins/bearer/qnetworksession_impl.cpp @@ -82,8 +82,6 @@ Q_SIGNALS: void forcedSessionClose(const QNetworkConfiguration &config); }; -#include "qnetworksession_impl.moc" - Q_GLOBAL_STATIC(QNetworkSessionManagerPrivate, sessionManager); void QNetworkSessionPrivateImpl::syncStateWithInterface() @@ -432,3 +430,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/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..57fe7c2fa2 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,102 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ - #include "qcomposeplatforminputcontext.h" #include <QtCore/QCoreApplication> #include <QtGui/QKeyEvent> -#include <QtCore/QDebug> -#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()) - return false; + xkb_compose_state_feed(m_composeState, keyEvent->nativeVirtualKey()); - keysym = 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()); - int nCompose = 0; - while (nCompose < QT_KEYSEQUENCE_MAX_LEN && m_composeBuffer[nCompose] != 0) - nCompose++; + QInputMethodEvent event; + event.setCommitString(composedText); + QCoreApplication::sendEvent(m_focusObject, &event); - if (nCompose == QT_KEYSEQUENCE_MAX_LEN) { reset(); - nCompose = 0; - } - - m_composeBuffer[nCompose] = keysym; - // check sequence - if (checkComposeTable()) return true; - - return false; + } + case XKB_COMPOSE_NOTHING: + return false; + default: + Q_UNREACHABLE(); + return false; + } } bool QComposeInputContext::isValid() const @@ -175,7 +146,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 +155,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 b891407c44..a78a62337f 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> @@ -512,6 +513,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; } @@ -520,6 +523,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/qcocoaglcontext.mm b/src/plugins/platforms/cocoa/qcocoaglcontext.mm index 93d95b38ea..fe1fc31553 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/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index 50adbad518..298d11fe08 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; } diff --git a/src/plugins/platforms/cocoa/qnsview_drawing.mm b/src/plugins/platforms/cocoa/qnsview_drawing.mm index 490dbe8c4c..cb1799b039 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/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..2db7723ae2 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,12 @@ // "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. var Module = {} @@ -146,8 +155,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 +191,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 +234,9 @@ function QtLoader(config) publicAPI.canLoadApplication = canLoadQt(); publicAPI.status = undefined; publicAPI.loadEmscriptenModule = loadEmscriptenModule; + publicAPI.addCanvasElement = addCanvasElement; + publicAPI.removeCanvasElement = removeCanvasElement; + publicAPI.resizeCanvasElement = resizeCanvasElement; restartCount = 0; @@ -312,13 +338,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) { @@ -382,6 +408,10 @@ function QtLoader(config) } }); + 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 +466,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 +538,25 @@ 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); + } + setStatus("Created"); return publicAPI; 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..e6a69c4814 100644 --- a/src/plugins/platforms/wasm/qwasmcompositor.cpp +++ b/src/plugins/platforms/wasm/qwasmcompositor.cpp @@ -56,8 +56,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) @@ -107,11 +108,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 +193,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 +205,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; } @@ -654,7 +650,7 @@ void QWasmCompositor::frame() m_needComposit = false; - if (m_windowStack.empty() || !m_screen) + if (m_windowStack.empty() || !screen()) return; QWasmWindow *someWindow = nullptr; @@ -673,7 +669,7 @@ 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(); } @@ -682,8 +678,8 @@ void QWasmCompositor::frame() 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 +693,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 +715,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..3104573073 100644 --- a/src/plugins/platforms/wasm/qwasmcompositor.h +++ b/src/plugins/platforms/wasm/qwasmcompositor.h @@ -62,7 +62,7 @@ class QWasmCompositor : public QObject { Q_OBJECT public: - QWasmCompositor(); + QWasmCompositor(QWasmScreen *screen); ~QWasmCompositor(); enum QWasmSubControl { @@ -103,7 +103,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 +116,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 +128,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 +140,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..f4ca49997a 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,336 @@ 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() +{ + qDebug() << "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); + + emscripten_set_resize_callback(nullptr, (void *)this, 1, uiEvent_cb); // Note: handles browser window resize + } template <typename Event> @@ -153,132 +422,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; - // 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; + if (qstrncmp(emscriptKey->code, "Key", 3) == 0 || qstrncmp(emscriptKey->code, "Numpad", 6) == 0) { + + 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 +549,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; - - QWasmWindow *htmlWindow = static_cast<QWasmWindow*>(window2->handle()); + QWindow *window2 = screen()->compositor()->windowAt(globalPoint, 5); + if (window2 == nullptr) + return; + 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) + if (window2) { window2->raise(); + if (!window2->isActive()) + window2->requestActivate(); + } pressedButtons.setFlag(button); @@ -391,18 +580,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 +611,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 @@ -451,7 +640,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 +652,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 +672,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); + QPoint targetPoint(mouseEvent.targetX, mouseEvent.targetY); + QPoint globalPoint = eventTranslator->screen()->geometry().topLeft() + targetPoint; - QWindow *window2 = QWasmIntegration::get()->compositor()->windowAt(globalPoint, 5); - - 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 +697,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,46 +711,72 @@ 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; + return 0; } quint64 QWasmEventTranslator::getTimestamp() @@ -560,4 +784,149 @@ 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; +} + +int QWasmEventTranslator::uiEvent_cb(int eventType, const EmscriptenUiEvent *e, void *userData) +{ + Q_UNUSED(e) + QWasmEventTranslator *eventTranslator = static_cast<QWasmEventTranslator *>(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). + eventTranslator->screen()->updateQScreenAndCanvasRenderSize(); + } + + return 0; +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/wasm/qwasmeventtranslator.h b/src/plugins/platforms/wasm/qwasmeventtranslator.h index f3dff8e48c..d6043072ba 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); @@ -180,19 +57,65 @@ public: static int touchCallback(int eventType, const EmscriptenTouchEvent *ev, void *userData); + static int uiEvent_cb(int eventType, const EmscriptenUiEvent *e, 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 +128,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/qwasmintegration.cpp b/src/plugins/platforms/wasm/qwasmintegration.cpp index 3829043d07..e601d553f2 100644 --- a/src/plugins/platforms/wasm/qwasmintegration.cpp +++ b/src/plugins/platforms/wasm/qwasmintegration.cpp @@ -33,6 +33,8 @@ #include "qwasmcompositor.h" #include "qwasmopenglcontext.h" #include "qwasmtheme.h" +#include "qwasmclipboard.h" +#include "qwasmservices.h" #include "qwasmwindow.h" #ifndef QT_NO_OPENGL @@ -55,47 +57,76 @@ 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); +} -QWasmIntegration::QWasmIntegration() - : m_fontDb(nullptr), - m_compositor(new QWasmCompositor), - m_screen(new QWasmScreen(m_compositor)), - m_eventDispatcher(nullptr) +static void resizeCanvasElement(emscripten::val canvas) { + QString canvasId = QString::fromStdString(canvas["id"].as<std::string>()); + QWasmIntegration::get()->resizeScreen(canvasId); +} - globalHtml5Integration = this; +EMSCRIPTEN_BINDINGS(qtQWasmIntegraton) +{ + function("qtBrowserBeforeUnload", &browserBeforeUnload); + function("qtAddCanvasElement", &addCanvasElement); + function("qtRemoveCanvasElement", &removeCanvasElement); + function("qtResizeCanvasElement", &resizeCanvasElement); +} - updateQScreenAndCanvasRenderSize(); - QWindowSystemInterface::handleScreenAdded(m_screen); - emscripten_set_resize_callback(0, (void *)this, 1, uiEvent_cb); +QWasmIntegration *QWasmIntegration::s_instance; - m_eventTranslator = new QWasmEventTranslator; +QWasmIntegration::QWasmIntegration() + : m_fontDb(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); + } - EM_ASM(// exit app if browser closes - window.onbeforeunload = function () { - Module.browserBeforeUnload(); - }; - ); + emscripten::val::global("window").set("onbeforeunload", val::module_property("qtBrowserBeforeUnload")); } 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 +140,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 +150,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 @@ -155,9 +188,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 +215,34 @@ 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(); - } - - return 0; + if (m_desktopServices == nullptr) + m_desktopServices = new QWasmServices(); + return m_desktopServices; } -static void set_canvas_size(double width, double height) +QPlatformClipboard* QWasmIntegration::clipboard() const { - EM_ASM_({ - var canvas = Module.canvas; - canvas.width = $0; - canvas.height = $1; - }, width, height); + return m_clipboard; } -void QWasmIntegration::updateQScreenAndCanvasRenderSize() +void QWasmIntegration::addScreen(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. - - double css_width; - double css_height; - emscripten_get_element_css_size(0, &css_width, &css_height); - QSizeF cssSize(css_width, css_height); + QWasmScreen *screen = new QWasmScreen(canvasId); + m_clipboard->installEventHandlers(canvasId); + m_screens.insert(canvasId, screen); + QWindowSystemInterface::handleScreenAdded(screen); +} - QWasmScreen *screen = QWasmIntegration::get()->m_screen; - QSizeF canvasSize = cssSize * screen->devicePixelRatio(); +void QWasmIntegration::removeScreen(const QString &canvasId) +{ + QWindowSystemInterface::handleScreenRemoved(m_screens.take(canvasId)); +} - set_canvas_size(canvasSize.width(), canvasSize.height()); - screen->setGeometry(QRect(QPoint(0, 0), cssSize.toSize())); - QWasmIntegration::get()->m_compositor->redrawWindowContent(); +void QWasmIntegration::resizeScreen(const QString &canvasId) +{ + m_screens.value(canvasId)->updateQScreenAndCanvasRenderSize(); } QT_END_NAMESPACE diff --git a/src/plugins/platforms/wasm/qwasmintegration.h b/src/plugins/platforms/wasm/qwasmintegration.h index ebc3d9d431..11d8d0f7f5 100644 --- a/src/plugins/platforms/wasm/qwasmintegration.h +++ b/src/plugins/platforms/wasm/qwasmintegration.h @@ -49,6 +49,8 @@ class QWasmEventDispatcher; class QWasmScreen; class QWasmCompositor; class QWasmBackingStore; +class QWasmClipboard; +class QWasmServices; class QWasmIntegration : public QObject, public QPlatformIntegration { @@ -66,25 +68,28 @@ public: 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; + QWasmClipboard *getWasmClipboard() { return m_clipboard; } - static QWasmIntegration *get(); - QWasmScreen *screen() { return m_screen; } - QWasmCompositor *compositor() { return m_compositor; } - QWasmEventTranslator *eventTranslator() { return m_eventTranslator; } - + 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); 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; + static QWasmIntegration *s_instance; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/wasm/qwasmopenglcontext.cpp b/src/plugins/platforms/wasm/qwasmopenglcontext.cpp index 73af3d1878..ae43e2ebf0 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 @@ -57,7 +57,7 @@ void QWasmOpenGLContext::maybeRecreateEmscriptenContext(QPlatformSurface *surfac emscripten_webgl_destroy_context(m_context); // Create new context - const char *canvasId = 0; // (use default canvas) FIXME: get the actual canvas from the surface. + const QString canvasId = QWasmScreen::get(surface->screen())->canvasId(); m_context = createEmscriptenContext(canvasId, m_requestedFormat); // Register context-lost callback. @@ -73,11 +73,11 @@ void QWasmOpenGLContext::maybeRecreateEmscriptenContext(QPlatformSurface *surfac return true; }; bool capture = true; - emscripten_set_webglcontextlost_callback(canvasId, this, capture, callback); + emscripten_set_webglcontextlost_callback(canvasId.toLocal8Bit().constData(), this, capture, callback); } } -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 @@ -96,7 +96,7 @@ EMSCRIPTEN_WEBGL_CONTEXT_HANDLE QWasmOpenGLContext::createEmscriptenContext(cons attributes.depth = format.depthBufferSize() > 0; attributes.stencil = format.stencilBufferSize() > 0; - 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; } diff --git a/src/plugins/platforms/wasm/qwasmopenglcontext.h b/src/plugins/platforms/wasm/qwasmopenglcontext.h index 9123100479..126b596a7e 100644 --- a/src/plugins/platforms/wasm/qwasmopenglcontext.h +++ b/src/plugins/platforms/wasm/qwasmopenglcontext.h @@ -51,7 +51,7 @@ public: private: void maybeRecreateEmscriptenContext(QPlatformSurface *surface); - static EMSCRIPTEN_WEBGL_CONTEXT_HANDLE createEmscriptenContext(const char *canvasId, QSurfaceFormat format); + static EMSCRIPTEN_WEBGL_CONTEXT_HANDLE createEmscriptenContext(const QString &canvasId, QSurfaceFormat format); bool m_contextLost = false; QSurfaceFormat m_requestedFormat; diff --git a/src/plugins/platforms/wasm/qwasmscreen.cpp b/src/plugins/platforms/wasm/qwasmscreen.cpp index 93e9906ffc..a26cafa900 100644 --- a/src/plugins/platforms/wasm/qwasmscreen.cpp +++ b/src/plugins/platforms/wasm/qwasmscreen.cpp @@ -29,7 +29,10 @@ #include "qwasmscreen.h" #include "qwasmwindow.h" +#include "qwasmeventtranslator.h" #include "qwasmcompositor.h" +#include <emscripten/bind.h> +#include <emscripten/val.h> #include <QtEglSupport/private/qeglconvenience_p.h> #ifndef QT_NO_OPENGL @@ -43,12 +46,13 @@ 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() @@ -56,6 +60,31 @@ QWasmScreen::~QWasmScreen() } +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 { return m_geometry; @@ -77,12 +106,15 @@ qreal QWasmScreen::devicePixelRatio() const // 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 +147,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..82d2a83edb 100644 --- a/src/plugins/platforms/wasm/qwasmscreen.h +++ b/src/plugins/platforms/wasm/qwasmscreen.h @@ -43,20 +43,28 @@ 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(); + 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; qreal devicePixelRatio() const override; + QString name() const override; QPlatformCursor *cursor() const override; void resizeMaximizedWindows(); @@ -64,17 +72,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/qwasmwindow.cpp b/src/plugins/platforms/wasm/qwasmwindow.cpp index 25a0190053..39797cb09d 100644 --- a/src/plugins/platforms/wasm/qwasmwindow.cpp +++ b/src/plugins/platforms/wasm/qwasmwindow.cpp @@ -198,8 +198,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/wasm.pro b/src/plugins/platforms/wasm/wasm.pro index eaaba53aa2..9b98445c68 100644 --- a/src/plugins/platforms/wasm/wasm.pro +++ b/src/plugins/platforms/wasm/wasm.pro @@ -18,7 +18,9 @@ SOURCES = \ qwasmcompositor.cpp \ qwasmcursor.cpp \ qwasmopenglcontext.cpp \ - qwasmtheme.cpp + qwasmtheme.cpp \ + qwasmclipboard.cpp \ + qwasmservices.cpp HEADERS = \ qwasmintegration.h \ @@ -31,7 +33,9 @@ HEADERS = \ qwasmstylepixmaps_p.h \ qwasmcursor.h \ qwasmopenglcontext.h \ - qwasmtheme.h + qwasmtheme.h \ + qwasmclipboard.h \ + qwasmservices.h wasmfonts.files = \ ../../../3rdparty/wasm/Vera.ttf \ diff --git a/src/plugins/platforms/wasm/wasm_shell.html b/src/plugins/platforms/wasm/wasm_shell.html index 67bfcdfbdc..f7c856d63d 100644 --- a/src/plugins/platforms/wasm/wasm_shell.html +++ b/src/plugins/platforms/wasm/wasm_shell.html @@ -7,27 +7,32 @@ <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> + <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,7 +55,6 @@ showCanvas: function() { spinner.style.display = 'none'; canvas.style.display = 'block'; - return canvas; }, }); qtLoader.loadEmscriptenModule("APPNAME"); diff --git a/src/plugins/platforms/windows/qwindowsinputcontext.cpp b/src/plugins/platforms/windows/qwindowsinputcontext.cpp index 8adcf56b11..878f55e56b 100644 --- a/src/plugins/platforms/windows/qwindowsinputcontext.cpp +++ b/src/plugins/platforms/windows/qwindowsinputcontext.cpp @@ -445,7 +445,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/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 0376e363f3..338e594c7b 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -1183,6 +1183,7 @@ QWindowCreationContext::QWindowCreationContext(const QWindow *w, 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), @@ -1220,7 +1221,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); } @@ -2819,6 +2820,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 2675990cf1..0d8096ddfa 100644 --- a/src/plugins/platforms/windows/qwindowswindow.h +++ b/src/plugins/platforms/windows/qwindowswindow.h @@ -342,6 +342,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); @@ -387,6 +388,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 ®ion, 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 ®ion, 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..06d1000993 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -132,6 +132,12 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra if (!m_startupId.isNull()) qunsetenv("DESKTOP_STARTUP_ID"); + m_focusInTimer.setSingleShot(true); + m_focusInTimer.callOnTimeout([]() { + // No FocusIn events for us, proceed with FocusOut normally. + QWindowSystemInterface::handleWindowActivated(nullptr, Qt::ActiveWindowFocusReason); + }); + sync(); } @@ -731,11 +737,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 +1016,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 +1024,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 91fd612cde..e4da207b00 100644 --- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp @@ -637,7 +637,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; @@ -823,7 +823,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, @@ -841,7 +841,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 acec0486c2..82a36c0727 100644 --- a/src/plugins/platforms/xcb/qxcbeventqueue.cpp +++ b/src/plugins/platforms/xcb/qxcbeventqueue.cpp @@ -262,7 +262,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; @@ -281,7 +281,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; } @@ -341,7 +341,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..a70c7db923 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) 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 57dbdc9bec..0fa0e8cd7b 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..9382488b74 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(400); } 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 0745e917a2..d0e05c1e20 100644 --- a/src/plugins/styles/mac/qmacstyle_mac.mm +++ b/src/plugins/styles/mac/qmacstyle_mac.mm @@ -4116,7 +4116,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(); 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, ©Opt, 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; }; |