1/****************************************************************************
2**
3** Copyright (C) 2019 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the plugins of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39#include "qcomposeplatforminputcontext.h"
40
41#include <QtCore/QCoreApplication>
42#include <QtGui/QKeyEvent>
43#include <QtGui/QGuiApplication>
44
45#include <locale.h>
46
47QT_BEGIN_NAMESPACE
48
49Q_LOGGING_CATEGORY(lcXkbCompose, "qt.xkb.compose")
50
51QComposeInputContext::QComposeInputContext()
52{
53 setObjectName(QStringLiteral("QComposeInputContext"));
54 qCDebug(lcXkbCompose, "using xkb compose input context");
55}
56
57QComposeInputContext::~QComposeInputContext()
58{
59 xkb_compose_state_unref(state: m_composeState);
60 xkb_compose_table_unref(table: m_composeTable);
61}
62
63void QComposeInputContext::ensureInitialized()
64{
65 if (m_initialized)
66 return;
67
68 if (!m_XkbContext) {
69 qCWarning(lcXkbCompose) << "error: xkb context has not been set on" << metaObject()->className();
70 return;
71 }
72
73 m_initialized = true;
74 // Get locale from user env settings, see also
75 // https://xkbcommon.org/doc/current/group__compose.html#compose-locale
76 const char *locale = getenv(name: "LC_ALL");
77 if (!locale || !*locale)
78 locale = getenv(name: "LC_CTYPE");
79 if (!locale || !*locale)
80 locale = getenv(name: "LANG");
81 if (!locale || !*locale)
82 locale = "C";
83 qCDebug(lcXkbCompose) << "detected locale:" << locale;
84
85 m_composeTable = xkb_compose_table_new_from_locale(context: m_XkbContext, locale, flags: XKB_COMPOSE_COMPILE_NO_FLAGS);
86 if (m_composeTable)
87 m_composeState = xkb_compose_state_new(table: m_composeTable, flags: XKB_COMPOSE_STATE_NO_FLAGS);
88
89 if (!m_composeTable) {
90 qCWarning(lcXkbCompose, "failed to create compose table");
91 return;
92 }
93 if (!m_composeState) {
94 qCWarning(lcXkbCompose, "failed to create compose state");
95 return;
96 }
97}
98
99bool QComposeInputContext::filterEvent(const QEvent *event)
100{
101 auto keyEvent = static_cast<const QKeyEvent *>(event);
102 if (keyEvent->type() != QEvent::KeyPress)
103 return false;
104
105 if (!inputMethodAccepted())
106 return false;
107
108 // lazy initialization - we don't want to do this on an app startup
109 ensureInitialized();
110
111 if (!m_composeTable || !m_composeState)
112 return false;
113
114 xkb_compose_state_feed(state: m_composeState, keysym: keyEvent->nativeVirtualKey());
115
116 switch (xkb_compose_state_get_status(state: m_composeState)) {
117 case XKB_COMPOSE_COMPOSING:
118 return true;
119 case XKB_COMPOSE_CANCELLED:
120 reset();
121 return false;
122 case XKB_COMPOSE_COMPOSED:
123 {
124 const int size = xkb_compose_state_get_utf8(state: m_composeState, buffer: nullptr, size: 0);
125 QVarLengthArray<char, 32> buffer(size + 1);
126 xkb_compose_state_get_utf8(state: m_composeState, buffer: buffer.data(), size: buffer.size());
127 QString composedText = QString::fromUtf8(str: buffer.constData());
128
129 QInputMethodEvent event;
130 event.setCommitString(commitString: composedText);
131
132 if (!m_focusObject && qApp)
133 m_focusObject = qApp->focusObject();
134
135 if (m_focusObject)
136 QCoreApplication::sendEvent(receiver: m_focusObject, event: &event);
137 else
138 qCWarning(lcXkbCompose, "no focus object");
139
140 reset();
141 return true;
142 }
143 case XKB_COMPOSE_NOTHING:
144 return false;
145 default:
146 Q_UNREACHABLE();
147 return false;
148 }
149}
150
151bool QComposeInputContext::isValid() const
152{
153 return true;
154}
155
156void QComposeInputContext::setFocusObject(QObject *object)
157{
158 m_focusObject = object;
159}
160
161void QComposeInputContext::reset()
162{
163 if (m_composeState)
164 xkb_compose_state_reset(state: m_composeState);
165}
166
167void QComposeInputContext::update(Qt::InputMethodQueries q)
168{
169 QPlatformInputContext::update(q);
170}
171
172QT_END_NAMESPACE
173

source code of qtbase/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp