1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3#include "qcomposeplatforminputcontext.h"
4
5#include <QtCore/QCoreApplication>
6#include <QtCore/qvarlengtharray.h>
7#include <QtGui/QKeyEvent>
8#include <QtGui/QGuiApplication>
9
10#include <locale.h>
11
12QT_BEGIN_NAMESPACE
13
14Q_LOGGING_CATEGORY(lcXkbCompose, "qt.xkb.compose")
15
16QComposeInputContext::QComposeInputContext()
17{
18 setObjectName(QStringLiteral("QComposeInputContext"));
19 qCDebug(lcXkbCompose, "using xkb compose input context");
20}
21
22QComposeInputContext::~QComposeInputContext()
23{
24 xkb_compose_state_unref(state: m_composeState);
25 xkb_compose_table_unref(table: m_composeTable);
26}
27
28void QComposeInputContext::ensureInitialized()
29{
30 if (m_initialized)
31 return;
32
33 if (!m_XkbContext) {
34 qCWarning(lcXkbCompose) << "error: xkb context has not been set on" << metaObject()->className();
35 return;
36 }
37
38 m_initialized = true;
39 // Get locale from user env settings, see also
40 // https://xkbcommon.org/doc/current/group__compose.html#compose-locale
41 const char *locale = getenv(name: "LC_ALL");
42 if (!locale || !*locale)
43 locale = getenv(name: "LC_CTYPE");
44 if (!locale || !*locale)
45 locale = getenv(name: "LANG");
46 if (!locale || !*locale)
47 locale = "C";
48 qCDebug(lcXkbCompose) << "detected locale:" << locale;
49
50 m_composeTable = xkb_compose_table_new_from_locale(context: m_XkbContext, locale, flags: XKB_COMPOSE_COMPILE_NO_FLAGS);
51 if (m_composeTable)
52 m_composeState = xkb_compose_state_new(table: m_composeTable, flags: XKB_COMPOSE_STATE_NO_FLAGS);
53
54 if (!m_composeTable) {
55 qCWarning(lcXkbCompose, "failed to create compose table");
56 return;
57 }
58 if (!m_composeState) {
59 qCWarning(lcXkbCompose, "failed to create compose state");
60 return;
61 }
62}
63
64bool QComposeInputContext::filterEvent(const QEvent *event)
65{
66 auto keyEvent = static_cast<const QKeyEvent *>(event);
67 if (keyEvent->type() != QEvent::KeyPress)
68 return false;
69
70 if (!inputMethodAccepted())
71 return false;
72
73 // lazy initialization - we don't want to do this on an app startup
74 ensureInitialized();
75
76 if (!m_composeTable || !m_composeState)
77 return false;
78
79 xkb_compose_state_feed(state: m_composeState, keysym: keyEvent->nativeVirtualKey());
80
81 switch (xkb_compose_state_get_status(state: m_composeState)) {
82 case XKB_COMPOSE_COMPOSING:
83 return true;
84 case XKB_COMPOSE_CANCELLED:
85 reset();
86 return false;
87 case XKB_COMPOSE_COMPOSED:
88 {
89 const int size = xkb_compose_state_get_utf8(state: m_composeState, buffer: nullptr, size: 0);
90 QVarLengthArray<char, 32> buffer(size + 1);
91 xkb_compose_state_get_utf8(state: m_composeState, buffer: buffer.data(), size: buffer.size());
92 QString composedText = QString::fromUtf8(utf8: buffer.constData());
93
94 QInputMethodEvent event;
95 event.setCommitString(commitString: composedText);
96
97 if (!m_focusObject && qApp)
98 m_focusObject = qApp->focusObject();
99
100 if (m_focusObject)
101 QCoreApplication::sendEvent(receiver: m_focusObject, event: &event);
102 else
103 qCWarning(lcXkbCompose, "no focus object");
104
105 reset();
106 return true;
107 }
108 case XKB_COMPOSE_NOTHING:
109 return false;
110 default:
111 Q_UNREACHABLE_RETURN(false);
112 }
113}
114
115bool QComposeInputContext::isValid() const
116{
117 return true;
118}
119
120void QComposeInputContext::setFocusObject(QObject *object)
121{
122 m_focusObject = object;
123}
124
125void QComposeInputContext::reset()
126{
127 if (m_composeState)
128 xkb_compose_state_reset(state: m_composeState);
129}
130
131void QComposeInputContext::update(Qt::InputMethodQueries q)
132{
133 QPlatformInputContext::update(q);
134}
135
136QT_END_NAMESPACE
137
138#include "moc_qcomposeplatforminputcontext.cpp"
139

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