1// Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4
5#include "qwaylandinputcontext_p.h"
6
7#include <QtGui/QGuiApplication>
8#include <QtGui/QTextCharFormat>
9#include <QtGui/QWindow>
10#include <QtCore/QVarLengthArray>
11
12#include "qwaylanddisplay_p.h"
13#include "qwaylandinputdevice_p.h"
14#include "qwaylandwindow_p.h"
15
16#if QT_CONFIG(xkbcommon)
17#include <locale.h>
18#endif
19
20QT_BEGIN_NAMESPACE
21
22Q_LOGGING_CATEGORY(qLcQpaInputMethods, "qt.qpa.input.methods")
23
24namespace QtWaylandClient {
25
26QWaylandInputContext::QWaylandInputContext(QWaylandDisplay *display)
27 : mDisplay(display)
28{
29}
30
31QWaylandInputContext::~QWaylandInputContext()
32{
33}
34
35bool QWaylandInputContext::isValid() const
36{
37 return mDisplay->textInputManagerv2() != nullptr || mDisplay->textInputManagerv1() != nullptr || mDisplay->textInputManagerv3() != nullptr;
38}
39
40void QWaylandInputContext::reset()
41{
42 qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO;
43#if QT_CONFIG(xkbcommon)
44 if (m_composeState)
45 xkb_compose_state_reset(state: m_composeState);
46#endif
47
48 QPlatformInputContext::reset();
49
50 QWaylandTextInputInterface *inputInterface = textInput();
51 if (!inputInterface)
52 return;
53
54 inputInterface->reset();
55}
56
57void QWaylandInputContext::commit()
58{
59 qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO;
60
61 QWaylandTextInputInterface *inputInterface = textInput();
62 if (!inputInterface)
63 return;
64
65 inputInterface->commit();
66}
67
68static ::wl_surface *surfaceForWindow(QWindow *window)
69{
70 if (!window || !window->handle())
71 return nullptr;
72
73 auto *waylandWindow = static_cast<QWaylandWindow *>(window->handle());
74 return waylandWindow->wlSurface();
75}
76
77void QWaylandInputContext::update(Qt::InputMethodQueries queries)
78{
79 qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO << queries;
80
81 QWaylandTextInputInterface *inputInterface = textInput();
82 if (!QGuiApplication::focusObject() || !inputInterface)
83 return;
84
85 auto *currentSurface = surfaceForWindow(window: mCurrentWindow);
86
87 if (currentSurface && !inputMethodAccepted()) {
88 inputInterface->disableSurface(surface: currentSurface);
89 mCurrentWindow.clear();
90 } else if (!currentSurface && inputMethodAccepted()) {
91 QWindow *window = QGuiApplication::focusWindow();
92 if (auto *focusSurface = surfaceForWindow(window)) {
93 inputInterface->enableSurface(surface: focusSurface);
94 mCurrentWindow = window;
95 }
96 }
97
98 inputInterface->updateState(queries, flags: QWaylandTextInputInterface::update_state_change);
99}
100
101void QWaylandInputContext::invokeAction(QInputMethod::Action action, int cursorPostion)
102{
103 QWaylandTextInputInterface *inputInterface = textInput();
104 if (!inputInterface)
105 return;
106
107 if (action == QInputMethod::Click)
108 inputInterface->setCursorInsidePreedit(cursorPostion);
109}
110
111void QWaylandInputContext::showInputPanel()
112{
113 qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO;
114
115 QWaylandTextInputInterface *inputInterface = textInput();
116 if (!inputInterface)
117 return;
118
119 inputInterface->showInputPanel();
120}
121
122void QWaylandInputContext::hideInputPanel()
123{
124 qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO;
125
126 QWaylandTextInputInterface *inputInterface = textInput();
127 if (!inputInterface)
128 return;
129
130 inputInterface->hideInputPanel();
131}
132
133bool QWaylandInputContext::isInputPanelVisible() const
134{
135 qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO;
136
137 QWaylandTextInputInterface *inputInterface = textInput();
138 if (!inputInterface)
139 return QPlatformInputContext::isInputPanelVisible();
140
141 return inputInterface->isInputPanelVisible();
142}
143
144QRectF QWaylandInputContext::keyboardRect() const
145{
146 qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO;
147
148 QWaylandTextInputInterface *inputInterface = textInput();
149 if (!inputInterface)
150 return QPlatformInputContext::keyboardRect();
151
152 return inputInterface->keyboardRect();
153}
154
155QLocale QWaylandInputContext::locale() const
156{
157 qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO;
158
159 QWaylandTextInputInterface *inputInterface = textInput();
160 if (!inputInterface)
161 return QPlatformInputContext::locale();
162
163 return inputInterface->locale();
164}
165
166Qt::LayoutDirection QWaylandInputContext::inputDirection() const
167{
168 qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO;
169
170 QWaylandTextInputInterface *inputInterface = textInput();
171 if (!inputInterface)
172 return QPlatformInputContext::inputDirection();
173
174 return inputInterface->inputDirection();
175}
176
177void QWaylandInputContext::setFocusObject(QObject *object)
178{
179 qCDebug(qLcQpaInputMethods) << Q_FUNC_INFO;
180#if QT_CONFIG(xkbcommon)
181 m_focusObject = object;
182#else
183 Q_UNUSED(object);
184#endif
185
186 QWaylandTextInputInterface *inputInterface = textInput();
187 if (!inputInterface)
188 return;
189
190 QWindow *window = QGuiApplication::focusWindow();
191
192 if (window && window->handle()) {
193 if (mCurrentWindow.data() != window) {
194 if (!inputMethodAccepted()) {
195 auto *surface = static_cast<QWaylandWindow *>(window->handle())->wlSurface();
196 if (surface)
197 inputInterface->disableSurface(surface);
198 mCurrentWindow.clear();
199 } else {
200 auto *surface = static_cast<QWaylandWindow *>(window->handle())->wlSurface();
201 if (surface) {
202 inputInterface->enableSurface(surface);
203 mCurrentWindow = window;
204 } else {
205 mCurrentWindow.clear();
206 }
207 }
208 }
209 if (mCurrentWindow)
210 inputInterface->updateState(queries: Qt::ImQueryAll, flags: QWaylandTextInputInterface::update_state_enter);
211 return;
212 }
213
214 if (mCurrentWindow)
215 mCurrentWindow.clear();
216}
217
218QWaylandTextInputInterface *QWaylandInputContext::textInput() const
219{
220 return mDisplay->defaultInputDevice() ? mDisplay->defaultInputDevice()->textInput() : nullptr;
221}
222
223#if QT_CONFIG(xkbcommon)
224
225void QWaylandInputContext::ensureInitialized()
226{
227 if (m_initialized)
228 return;
229
230 if (!m_XkbContext) {
231 qCWarning(qLcQpaInputMethods) << "error: xkb context has not been set on" << metaObject()->className();
232 return;
233 }
234
235 m_initialized = true;
236 const char *const locale = setlocale(LC_CTYPE, locale: nullptr);
237 qCDebug(qLcQpaInputMethods) << "detected locale (LC_CTYPE):" << locale;
238
239 m_composeTable = xkb_compose_table_new_from_locale(context: m_XkbContext, locale, flags: XKB_COMPOSE_COMPILE_NO_FLAGS);
240 if (m_composeTable)
241 m_composeState = xkb_compose_state_new(table: m_composeTable, flags: XKB_COMPOSE_STATE_NO_FLAGS);
242
243 if (!m_composeTable) {
244 qCWarning(qLcQpaInputMethods, "failed to create compose table");
245 return;
246 }
247 if (!m_composeState) {
248 qCWarning(qLcQpaInputMethods, "failed to create compose state");
249 return;
250 }
251}
252
253bool QWaylandInputContext::filterEvent(const QEvent *event)
254{
255 auto keyEvent = static_cast<const QKeyEvent *>(event);
256 if (keyEvent->type() != QEvent::KeyPress)
257 return false;
258
259 if (!inputMethodAccepted())
260 return false;
261
262 // lazy initialization - we don't want to do this on an app startup
263 ensureInitialized();
264
265 if (!m_composeTable || !m_composeState)
266 return false;
267
268 xkb_compose_state_feed(state: m_composeState, keysym: keyEvent->nativeVirtualKey());
269
270 switch (xkb_compose_state_get_status(state: m_composeState)) {
271 case XKB_COMPOSE_COMPOSING:
272 return true;
273 case XKB_COMPOSE_CANCELLED:
274 reset();
275 return false;
276 case XKB_COMPOSE_COMPOSED:
277 {
278 const int size = xkb_compose_state_get_utf8(state: m_composeState, buffer: nullptr, size: 0);
279 QVarLengthArray<char, 32> buffer(size + 1);
280 xkb_compose_state_get_utf8(state: m_composeState, buffer: buffer.data(), size: buffer.size());
281 QString composedText = QString::fromUtf8(utf8: buffer.constData());
282
283 QInputMethodEvent event;
284 event.setCommitString(commitString: composedText);
285
286 if (!m_focusObject && qApp)
287 m_focusObject = qApp->focusObject();
288
289 if (m_focusObject)
290 QCoreApplication::sendEvent(receiver: m_focusObject, event: &event);
291 else
292 qCWarning(qLcQpaInputMethods, "no focus object");
293
294 reset();
295 return true;
296 }
297 case XKB_COMPOSE_NOTHING:
298 return false;
299 default:
300 Q_UNREACHABLE_RETURN(false);
301 }
302}
303
304#endif
305
306}
307
308QT_END_NAMESPACE
309
310#include "moc_qwaylandinputcontext_p.cpp"
311

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtwayland/src/client/qwaylandinputcontext.cpp