summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforminputcontexts
diff options
context:
space:
mode:
authorGatis Paeglis <gatis.paeglis@digia.com>2013-03-01 12:55:42 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-03-21 15:26:24 +0100
commit24c10b0b8d7673f2d5e04766b7e3e0e65f771127 (patch)
treed430145296d294490ce92ea64326db521af3b61c /src/plugins/platforminputcontexts
parentfb9cff6e88699b57b5dbc05d79c7958a2deacd71 (diff)
Introducing QComposeInputContext
When switching from Xlib to xcb platform plugin it was agreed that XIM is deprecated. Users should be using QT_IM_MODULE to load input context plugin for a more advance input method framework support. The proposed solution is to parse the compose file directly from Qt. This approach removes the overhead of communication protocols used in Xlib and/or IBUS. TableGenerator class follows [1]. The compose file is searched for in the following order: 1) If the environment variable $XCOMPOSEFILE is set, its value is used as the name of the Compose file. 2) If the user’s home directory has a file named .XCompose, it is used as the Compose file. 3) The system provided compose file is used by mapping the locale to a compose file from the list in /usr/share/X11/locale/compose.dir. Regarding step 3 - TableGenerator searches in hard-coded locations for system-provided compose files. Here I have introcuded a new environment variable QTCOMPOSE which can be used to prepend an extra location to be searched. [1] http://www.x.org/archive/X11R7.7/doc/man/man5/Compose.5.xhtml Task-number: QTBUG-28183 Change-Id: I76dcfd454f3acc23db98192a3673c1ab2af4425f Reviewed-by: Samuel Rødal <samuel.rodal@digia.com>
Diffstat (limited to 'src/plugins/platforminputcontexts')
-rw-r--r--src/plugins/platforminputcontexts/compose/compose.json3
-rw-r--r--src/plugins/platforminputcontexts/compose/compose.pro20
-rw-r--r--src/plugins/platforminputcontexts/compose/generator/qtablegenerator.cpp402
-rw-r--r--src/plugins/platforminputcontexts/compose/generator/qtablegenerator.h128
-rw-r--r--src/plugins/platforminputcontexts/compose/main.cpp70
-rw-r--r--src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp268
-rw-r--r--src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.h85
-rw-r--r--src/plugins/platforminputcontexts/compose/xkbcommon_workaround.h105
-rw-r--r--src/plugins/platforminputcontexts/platforminputcontexts.pro6
9 files changed, 1087 insertions, 0 deletions
diff --git a/src/plugins/platforminputcontexts/compose/compose.json b/src/plugins/platforminputcontexts/compose/compose.json
new file mode 100644
index 0000000000..2daf89ed30
--- /dev/null
+++ b/src/plugins/platforminputcontexts/compose/compose.json
@@ -0,0 +1,3 @@
+{
+ "Keys": [ "compose" ]
+}
diff --git a/src/plugins/platforminputcontexts/compose/compose.pro b/src/plugins/platforminputcontexts/compose/compose.pro
new file mode 100644
index 0000000000..6387a47a4c
--- /dev/null
+++ b/src/plugins/platforminputcontexts/compose/compose.pro
@@ -0,0 +1,20 @@
+TARGET = composeplatforminputcontextplugin
+
+PLUGIN_TYPE = platforminputcontexts
+PLUGIN_CLASS_NAME = QComposePlatformInputContextPlugin
+load(qt_plugin)
+
+QT += gui-private
+
+LIBS += $$QMAKE_LIBS_XKBCOMMON
+QMAKE_CXXFLAGS += $$QMAKE_CFLAGS_XKBCOMMON
+
+SOURCES += $$PWD/main.cpp \
+ $$PWD/qcomposeplatforminputcontext.cpp \
+ $$PWD/generator/qtablegenerator.cpp \
+
+HEADERS += $$PWD/qcomposeplatforminputcontext.h \
+ $$PWD/generator/qtablegenerator.h \
+ $$PWD/xkbcommon_workaround.h \
+
+OTHER_FILES += $$PWD/compose.json
diff --git a/src/plugins/platforminputcontexts/compose/generator/qtablegenerator.cpp b/src/plugins/platforminputcontexts/compose/generator/qtablegenerator.cpp
new file mode 100644
index 0000000000..5941936aec
--- /dev/null
+++ b/src/plugins/platforminputcontexts/compose/generator/qtablegenerator.cpp
@@ -0,0 +1,402 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtablegenerator.h"
+
+#include <QtCore/QRegularExpression>
+#include <QtCore/QByteArray>
+#include <QtCore/QTextCodec>
+#include <QtCore/QDebug>
+#include <QtCore/QStringList>
+#include <QtCore/QString>
+
+#include <xkbcommon/xkbcommon.h>
+#include <xkbcommon_workaround.h>
+
+#include <X11/keysym.h>
+
+//#define DEBUG_GENERATOR
+
+TableGenerator::TableGenerator() : m_state(NoErrors),
+ m_systemComposeDir(QString())
+{
+ initPossibleLocations();
+ findComposeFile();
+ orderComposeTable();
+#ifdef DEBUG_GENERATOR
+ printComposeTable();
+#endif
+}
+
+void TableGenerator::initPossibleLocations()
+{
+ // AFAICT there is no way to know the exact location
+ // of the compose files. It depends on how Xlib was configured
+ // on a specific platform. During the "./configure" process
+ // xlib generates a config.h file which contains a bunch of defines,
+ // including XLOCALEDIR which points to the location of the compose file dir.
+ // To add an extra system path use the QTCOMPOSE environment variable
+ if (qEnvironmentVariableIsSet("QTCOMPOSE")) {
+ m_possibleLocations.append(QString(qgetenv("QTCOMPOSE")));
+ }
+ m_possibleLocations.append(QStringLiteral("/usr/share/X11/locale"));
+ m_possibleLocations.append(QStringLiteral("/usr/lib/X11/locale"));
+}
+
+void TableGenerator::findComposeFile()
+{
+ bool found = false;
+ // check if XCOMPOSEFILE points to a Compose file
+ if (qEnvironmentVariableIsSet("XCOMPOSEFILE")) {
+ QString composeFile(qgetenv("XCOMPOSEFILE"));
+ if (composeFile.endsWith(QLatin1String("Compose")))
+ found = processFile(composeFile);
+ else
+ qWarning("Qt Warning: XCOMPOSEFILE doesn't point to a valid Compose file");
+#ifdef DEBUG_GENERATOR
+ if (found)
+ qDebug() << "Using Compose file from: " << composeFile;
+#endif
+ }
+
+ // check if user’s home directory has a file named .XCompose
+ if (!found && cleanState()) {
+ QString composeFile = qgetenv("HOME") + QStringLiteral("/.XCompose");
+ if (QFile(composeFile).exists())
+ found = processFile(composeFile);
+#ifdef DEBUG_GENERATOR
+ if (found)
+ qDebug() << "Using Compose file from: " << composeFile;
+#endif
+ }
+
+ // check for the system provided compose files
+ if (!found && cleanState()) {
+ readLocaleMappings();
+
+ if (cleanState()) {
+
+ QString table = m_localeToTable.value(locale().toUpper());
+ if (table.isEmpty())
+ // no table mappings for the system's locale in the compose.dir
+ m_state = UnsupportedLocale;
+ else
+ found = processFile(systemComposeDir() + QLatin1String("/") + table);
+#ifdef DEBUG_GENERATOR
+ if (found)
+ qDebug() << "Using Compose file from: " <<
+ systemComposeDir() + QLatin1String("/") + table;
+#endif
+ }
+ }
+
+ if (found && m_composeTable.isEmpty())
+ m_state = EmptyTable;
+
+ if (!found)
+ m_state = MissingComposeFile;
+}
+
+bool TableGenerator::findSystemComposeDir()
+{
+ bool found = false;
+ for (int i = 0; i < m_possibleLocations.size(); ++i) {
+ QString path = m_possibleLocations.at(i);
+ if (QFile(path + QLatin1String("/compose.dir")).exists()) {
+ 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);
+}
+
+void TableGenerator::readLocaleMappings()
+{
+ QFile mappings(systemComposeDir() + QLatin1String("/compose.dir"));
+ if (mappings.exists()) {
+ mappings.open(QIODevice::ReadOnly);
+ QTextStream in(&mappings);
+ // formating of compose.dir has some inconsistencies
+ while (!in.atEnd()) {
+ QString line = in.readLine();
+ if (!line.startsWith("#") && line.size() != 0 &&
+ line.at(0).isLower()) {
+
+ QStringList pair = line.split(QRegExp(QLatin1String("\\s+")));
+ QString table = pair.at(0);
+ if (table.endsWith(QLatin1String(":")))
+ table.remove(table.size() - 1, 1);
+
+ m_localeToTable.insert(pair.at(1).toUpper(), table);
+ }
+ }
+ mappings.close();
+ }
+}
+
+bool TableGenerator::processFile(QString composeFileName)
+{
+ QFile composeFile(composeFileName);
+ if (composeFile.exists()) {
+ 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()
+{
+}
+
+QList<QComposeTableElement> TableGenerator::composeTable() const
+{
+ return m_composeTable;
+}
+
+void TableGenerator::parseComposeFile(QFile *composeFile)
+{
+#ifdef DEBUG_GENERATOR
+ qDebug() << "TableGenerator::parseComposeFile: " << composeFile->fileName();
+#endif
+ QTextStream in(composeFile);
+
+ while (!in.atEnd()) {
+ QString line = in.readLine();
+ if (line.startsWith(QLatin1String("<"))) {
+ parseKeySequence(line);
+ } else if (line.startsWith(QLatin1String("include"))) {
+ parseIncludeInstruction(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"), locale());
+ line.replace(QLatin1String("%S"), systemComposeDir());
+
+ processFile(line);
+}
+
+ushort TableGenerator::keysymToUtf8(uint32_t sym)
+{
+ QByteArray chars;
+ int bytes;
+ chars.resize(8);
+
+ if (needWorkaround(sym)) {
+ uint32_t codepoint;
+ if (sym == XKB_KEY_KP_Space)
+ codepoint = XKB_KEY_space & 0x7f;
+ else
+ codepoint = sym & 0x7f;
+
+ bytes = utf32_to_utf8(codepoint, chars.data());
+ } else {
+ 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
+ const QChar *ch = QString(chars.data()).unicode();
+ return ch->unicode();
+}
+
+uint32_t TableGenerator::stringToKeysym(QString keysymName)
+{
+ uint32_t keysym;
+ const char *name = keysymName.toLatin1().constData();
+
+ if ((keysym = xkb_keysym_from_name(name, (xkb_keysym_flags)0)) == XKB_KEY_NoSymbol)
+ qWarning() << QString("Qt Warrning - invalid keysym: %1").arg(keysymName);
+
+ return keysym;
+}
+
+void TableGenerator::parseKeySequence(QString line)
+{
+ // we are interested in the lines with the following format:
+ // <Multi_key> <numbersign> <S> : "♬" U266c # BEAMED SIXTEENTH NOTE
+ int keysEnd = line.indexOf(QLatin1String(":"));
+ QString keys = line.left(keysEnd).trimmed();
+
+ // find the key sequence
+ QString regexp = QStringLiteral("<[^>]+>");
+ QRegularExpression reg(regexp);
+ QRegularExpressionMatchIterator i = reg.globalMatch(keys);
+ QStringList keyList;
+ while (i.hasNext()) {
+ QRegularExpressionMatch match = i.next();
+ QString word = match.captured(0);
+ keyList << word;
+ }
+
+ QComposeTableElement elem;
+ QString quote = QStringLiteral("\"");
+ // 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".
+ int composeValueIndex = line.indexOf(quote, keysEnd) + 1;
+ const QChar valueType(line.at(composeValueIndex));
+
+ if (valueType == '\\' && line.at(composeValueIndex + 1).isDigit()) {
+ // handle octal and hex code values
+ QChar detectBase(line.at(composeValueIndex + 2));
+ QString codeValue = line.mid(composeValueIndex + 1, line.lastIndexOf(quote) - composeValueIndex - 1);
+ if (detectBase == 'x') {
+ // hexadecimal character code
+ elem.value = keysymToUtf8(codeValue.toUInt(0, 16));
+ } else {
+ // octal character code
+ QString hexStr = QString::number(codeValue.toUInt(0, 8), 16);
+ elem.value = keysymToUtf8(hexStr.toUInt(0, 16));
+ }
+ } else {
+ // handle direct text encoded in the locale
+ elem.value = valueType.unicode();
+ }
+
+ // find the comment
+ int commnetIndex = line.lastIndexOf(quote) + 1;
+ elem.comment = line.mid(commnetIndex).trimmed();
+
+ // Convert to X11 keysym
+ int count = keyList.length();
+ for (int i = 0; i < QT_KEYSEQUENCE_MAX_LEN; i++) {
+ if (i < count) {
+ QString keysym = keyList.at(i);
+ keysym.remove(keysym.length() - 1, 1);
+ keysym.remove(0, 1);
+
+ if (keysym == QLatin1String("dead_inverted_breve"))
+ keysym = QStringLiteral("dead_invertedbreve");
+ else if (keysym == QLatin1String("dead_double_grave"))
+ keysym = QStringLiteral("dead_doublegrave");
+
+ elem.keys[i] = stringToKeysym(keysym);
+ } else {
+ elem.keys[i] = 0;
+ }
+ }
+ m_composeTable.append(elem);
+}
+
+void TableGenerator::printComposeTable() const
+{
+ if (composeTable().isEmpty())
+ return;
+
+ QString output;
+ QComposeTableElement elem;
+ QString comma = QStringLiteral(",");
+ int tableSize = m_composeTable.size();
+ for (int i = 0; i < tableSize; ++i) {
+ elem = m_composeTable.at(i);
+ output.append(QLatin1String("{ {"));
+ for (int j = 0; j < QT_KEYSEQUENCE_MAX_LEN; j++) {
+ output.append(QString(QLatin1String("0x%1, ")).arg(QString::number(elem.keys[j],16)));
+ }
+ // take care of the trailing comma
+ if (i == tableSize - 1)
+ comma = QStringLiteral("");
+ output.append(QString(QLatin1String("}, 0x%1, \"\" }%2 // %3 \n"))
+ .arg(QString::number(elem.value,16))
+ .arg(comma)
+ .arg(elem.comment));
+ }
+
+ qDebug() << "output: \n" << output;
+}
+
+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
+ qStableSort(m_composeTable.begin(), m_composeTable.end(), Compare());
+}
+
diff --git a/src/plugins/platforminputcontexts/compose/generator/qtablegenerator.h b/src/plugins/platforminputcontexts/compose/generator/qtablegenerator.h
new file mode 100644
index 0000000000..11e7b2b422
--- /dev/null
+++ b/src/plugins/platforminputcontexts/compose/generator/qtablegenerator.h
@@ -0,0 +1,128 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QTABLEGENERATOR_H
+#define QTABLEGENERATOR_H
+
+#include <QtCore/QList>
+#include <QtCore/QFile>
+#include <QtCore/QMap>
+#include <QtCore/QString>
+
+#define QT_KEYSEQUENCE_MAX_LEN 6
+
+struct QComposeTableElement {
+ uint keys[QT_KEYSEQUENCE_MAX_LEN];
+ uint value;
+ QString comment;
+};
+
+class Compare
+{
+public:
+ bool operator () (const QComposeTableElement &lhs, const uint rhs[QT_KEYSEQUENCE_MAX_LEN])
+ {
+ for (size_t i = 0; i < QT_KEYSEQUENCE_MAX_LEN; i++) {
+ if (lhs.keys[i] != rhs[i])
+ return (lhs.keys[i] < rhs[i]);
+ }
+ return false;
+ }
+
+ bool operator () (const QComposeTableElement &lhs, const QComposeTableElement &rhs)
+ {
+ for (size_t i = 0; i < QT_KEYSEQUENCE_MAX_LEN; i++) {
+ if (lhs.keys[i] != rhs.keys[i])
+ return (lhs.keys[i] < rhs.keys[i]);
+ }
+ return false;
+ }
+};
+
+class TableGenerator
+{
+
+public:
+ enum TableState
+ {
+ UnsupportedLocale,
+ EmptyTable,
+ UnknownSystemComposeDir,
+ MissingComposeFile,
+ NoErrors
+ };
+
+ TableGenerator();
+ ~TableGenerator();
+
+ void parseComposeFile(QFile *composeFile);
+ void printComposeTable() const;
+ void orderComposeTable();
+
+ QList<QComposeTableElement> composeTable() const;
+ TableState tableState() const { return m_state; }
+
+protected:
+ bool processFile(QString composeFileName);
+ void parseKeySequence(QString line);
+ void parseIncludeInstruction(QString line);
+
+ void findComposeFile();
+ bool findSystemComposeDir();
+ QString systemComposeDir();
+
+ ushort keysymToUtf8(uint32_t sym);
+ uint32_t stringToKeysym(QString keysymName);
+
+ void readLocaleMappings();
+ void initPossibleLocations();
+ bool cleanState() const { return ((m_state & NoErrors) == NoErrors); }
+ QString locale() const;
+
+private:
+ QList<QComposeTableElement> m_composeTable;
+ QMap<QString, QString> m_localeToTable;
+ TableState m_state;
+ QString m_systemComposeDir;
+ QList<QString> m_possibleLocations;
+};
+
+#endif // QTABLEGENERATOR_H
diff --git a/src/plugins/platforminputcontexts/compose/main.cpp b/src/plugins/platforminputcontexts/compose/main.cpp
new file mode 100644
index 0000000000..728c60caf5
--- /dev/null
+++ b/src/plugins/platforminputcontexts/compose/main.cpp
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qpa/qplatforminputcontextplugin_p.h>
+
+#include <QtCore/QStringList>
+
+#include "qcomposeplatforminputcontext.h"
+
+QT_BEGIN_NAMESPACE
+
+class QComposePlatformInputContextPlugin : public QPlatformInputContextPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QPlatformInputContextFactoryInterface" FILE "compose.json")
+
+public:
+ QComposeInputContext *create(const QString &, const QStringList &);
+};
+
+QComposeInputContext *QComposePlatformInputContextPlugin::create(const QString &system, const QStringList &paramList)
+{
+ Q_UNUSED(paramList);
+
+ if (system.compare(system, QStringLiteral("compose"), Qt::CaseInsensitive) == 0)
+ return new QComposeInputContext;
+ return 0;
+}
+
+QT_END_NAMESPACE
+
+#include "main.moc"
diff --git a/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp b/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp
new file mode 100644
index 0000000000..433c9eec37
--- /dev/null
+++ b/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp
@@ -0,0 +1,268 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcomposeplatforminputcontext.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtGui/QKeyEvent>
+#include <QtCore/QDebug>
+
+QT_BEGIN_NAMESPACE
+
+//#define DEBUG_COMPOSING
+
+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
+};
+
+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
+};
+
+QComposeInputContext::QComposeInputContext()
+{
+ TableGenerator reader;
+ m_tableState = reader.tableState();
+
+ if ((m_tableState & TableGenerator::NoErrors) == TableGenerator::NoErrors) {
+ m_composeTable = reader.composeTable();
+ clearComposeBuffer();
+ }
+}
+
+bool QComposeInputContext::filterEvent(const QEvent *event)
+{
+ // if there were errors when generating the compose table input
+ // context should not try to filter anything, simply return false
+ if ((m_tableState & TableGenerator::NoErrors) != TableGenerator::NoErrors)
+ return false;
+
+ QKeyEvent *keyEvent = (QKeyEvent *)event;
+ // should pass only the key presses
+ if (keyEvent->type() != QEvent::KeyPress) {
+ return false;
+ }
+
+ int keyval = keyEvent->key();
+ int keysym = 0;
+
+ if (ignoreKey(keyval))
+ return false;
+
+ QString text = keyEvent->text();
+ if (!composeKey(keyval) && text.isEmpty())
+ return false;
+
+ keysym = keyEvent->nativeVirtualKey();
+
+ int nCompose = 0;
+ while (m_composeBuffer[nCompose] != 0 && nCompose < QT_KEYSEQUENCE_MAX_LEN)
+ nCompose++;
+
+ if (nCompose == QT_KEYSEQUENCE_MAX_LEN) {
+ reset();
+ nCompose = 0;
+ }
+
+ m_composeBuffer[nCompose] = keysym;
+ // check sequence
+ if (checkComposeTable())
+ return true;
+
+ return false;
+}
+
+bool QComposeInputContext::isValid() const
+{
+ return true;
+}
+
+void QComposeInputContext::setFocusObject(QObject *object)
+{
+ m_focusObject = object;
+}
+
+void QComposeInputContext::reset()
+{
+ clearComposeBuffer();
+}
+
+void QComposeInputContext::update(Qt::InputMethodQueries q)
+{
+ QPlatformInputContext::update(q);
+}
+
+static bool isDuplicate(const QComposeTableElement &lhs, const QComposeTableElement &rhs)
+{
+ for (size_t i = 0; i < QT_KEYSEQUENCE_MAX_LEN; i++) {
+ if (lhs.keys[i] != rhs.keys[i])
+ return false;
+ }
+ return true;
+}
+
+bool QComposeInputContext::checkComposeTable()
+{
+ QList<QComposeTableElement>::iterator it =
+ qLowerBound(m_composeTable.begin(), m_composeTable.end(), m_composeBuffer, Compare());
+
+ // prevent dereferencing an 'end' iterator, which would result in a crash
+ if (it == m_composeTable.end())
+ 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.end()) {
+ 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
new file mode 100644
index 0000000000..1ced2f8ded
--- /dev/null
+++ b/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOMPOSEPLATFORMINPUTCONTEXT_H
+#define QCOMPOSEPLATFORMINPUTCONTEXT_H
+
+#include <qpa/qplatforminputcontext.h>
+
+#include <QtCore/QList>
+
+#include "generator/qtablegenerator.h"
+
+QT_BEGIN_NAMESPACE
+
+class QEvent;
+
+class QComposeInputContext : public QPlatformInputContext
+{
+ Q_OBJECT
+
+public:
+ QComposeInputContext();
+ ~QComposeInputContext();
+
+ bool isValid() const;
+ void setFocusObject(QObject *object);
+ void reset();
+ void update(Qt::InputMethodQueries);
+ bool filterEvent(const QEvent *event);
+
+protected:
+ void clearComposeBuffer();
+ bool ignoreKey(int keyval) const;
+ bool composeKey(int keyval) const;
+ bool checkComposeTable();
+ void commitText(uint character) const;
+
+private:
+ QObject *m_focusObject;
+ QList<QComposeTableElement> m_composeTable;
+ uint m_composeBuffer[QT_KEYSEQUENCE_MAX_LEN + 1];
+ TableGenerator::TableState m_tableState;
+};
+
+QT_END_NAMESPACE
+
+#endif // QCOMPOSEPLATFORMINPUTCONTEXT_H
diff --git a/src/plugins/platforminputcontexts/compose/xkbcommon_workaround.h b/src/plugins/platforminputcontexts/compose/xkbcommon_workaround.h
new file mode 100644
index 0000000000..58ce143978
--- /dev/null
+++ b/src/plugins/platforminputcontexts/compose/xkbcommon_workaround.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef XKBCOMMON_WORKAROUND_H
+#define XKBCOMMON_WORKAROUND_H
+
+// Function utf32_to_utf8() is borrowed from the libxkbcommon library,
+// file keysym-utf.c. The workaround should be removed once the fix from
+// https://bugs.freedesktop.org/show_bug.cgi?id=56780 gets released.
+static int utf32_to_utf8(uint32_t unichar, char *buffer)
+{
+ int count, shift, length;
+ uint8_t head;
+
+ if (unichar <= 0x007f) {
+ buffer[0] = unichar;
+ buffer[1] = '\0';
+ return 2;
+ }
+ else if (unichar <= 0x07FF) {
+ length = 2;
+ head = 0xc0;
+ }
+ else if (unichar <= 0xffff) {
+ length = 3;
+ head = 0xe0;
+ }
+ else if (unichar <= 0x1fffff) {
+ length = 4;
+ head = 0xf0;
+ }
+ else if (unichar <= 0x3ffffff) {
+ length = 5;
+ head = 0xf8;
+ }
+ else {
+ length = 6;
+ head = 0xfc;
+ }
+
+ for (count = length - 1, shift = 0; count > 0; count--, shift += 6)
+ buffer[count] = 0x80 | ((unichar >> shift) & 0x3f);
+
+ buffer[0] = head | ((unichar >> shift) & 0x3f);
+ buffer[length] = '\0';
+
+ return length + 1;
+}
+
+static bool needWorkaround(uint32_t sym)
+{
+ /* patch encoding botch */
+ if (sym == XKB_KEY_KP_Space)
+ return true;
+
+ /* special keysyms */
+ if ((sym >= XKB_KEY_BackSpace && sym <= XKB_KEY_Clear) ||
+ (sym >= XKB_KEY_KP_Multiply && sym <= XKB_KEY_KP_9) ||
+ sym == XKB_KEY_Return || sym == XKB_KEY_Escape ||
+ sym == XKB_KEY_Delete || sym == XKB_KEY_KP_Tab ||
+ sym == XKB_KEY_KP_Enter || sym == XKB_KEY_KP_Equal)
+ return true;
+
+ return false;
+}
+
+#endif // XKBCOMMON_WORKAROUND_H
diff --git a/src/plugins/platforminputcontexts/platforminputcontexts.pro b/src/plugins/platforminputcontexts/platforminputcontexts.pro
index 7b3c6e9c36..fb58de5edc 100644
--- a/src/plugins/platforminputcontexts/platforminputcontexts.pro
+++ b/src/plugins/platforminputcontexts/platforminputcontexts.pro
@@ -1,4 +1,10 @@
TEMPLATE = subdirs
+
qtHaveModule(dbus) {
!mac:!win32:SUBDIRS += ibus maliit
}
+
+unix:!macx:contains(QT_CONFIG, xkbcommon): {
+ SUBDIRS += compose
+}
+