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 (mCurrentWindow && mCurrentWindow->handle()) {
193 if (mCurrentWindow.data() != window || !inputMethodAccepted()) {
194 auto *surface = static_cast<QWaylandWindow *>(mCurrentWindow->handle())->wlSurface();
195 if (surface)
196 inputInterface->disableSurface(surface);
197 mCurrentWindow.clear();
198 }
199 }
200
201 if (window && window->handle() && inputMethodAccepted()) {
202 if (mCurrentWindow.data() != window) {
203 auto *surface = static_cast<QWaylandWindow *>(window->handle())->wlSurface();
204 if (surface) {
205 inputInterface->enableSurface(surface);
206 mCurrentWindow = window;
207 }
208 }
209 inputInterface->updateState(queries: Qt::ImQueryAll, flags: QWaylandTextInputInterface::update_state_enter);
210 }
211}
212
213QWaylandTextInputInterface *QWaylandInputContext::textInput() const
214{
215 return mDisplay->defaultInputDevice() ? mDisplay->defaultInputDevice()->textInput() : nullptr;
216}
217
218#if QT_CONFIG(xkbcommon)
219
220void QWaylandInputContext::ensureInitialized()
221{
222 if (m_initialized)
223 return;
224
225 if (!m_XkbContext) {
226 qCWarning(qLcQpaInputMethods) << "error: xkb context has not been set on" << metaObject()->className();
227 return;
228 }
229
230 m_initialized = true;
231 const char *const locale = setlocale(LC_CTYPE, locale: nullptr);
232 qCDebug(qLcQpaInputMethods) << "detected locale (LC_CTYPE):" << locale;
233
234 m_composeTable = xkb_compose_table_new_from_locale(context: m_XkbContext, locale, flags: XKB_COMPOSE_COMPILE_NO_FLAGS);
235 if (m_composeTable)
236 m_composeState = xkb_compose_state_new(table: m_composeTable, flags: XKB_COMPOSE_STATE_NO_FLAGS);
237
238 if (!m_composeTable) {
239 qCWarning(qLcQpaInputMethods, "failed to create compose table");
240 return;
241 }
242 if (!m_composeState) {
243 qCWarning(qLcQpaInputMethods, "failed to create compose state");
244 return;
245 }
246}
247
248bool QWaylandInputContext::filterEvent(const QEvent *event)
249{
250 auto keyEvent = static_cast<const QKeyEvent *>(event);
251 if (keyEvent->type() != QEvent::KeyPress)
252 return false;
253
254 if (!inputMethodAccepted())
255 return false;
256
257 // lazy initialization - we don't want to do this on an app startup
258 ensureInitialized();
259
260 if (!m_composeTable || !m_composeState)
261 return false;
262
263 xkb_compose_state_feed(state: m_composeState, keysym: keyEvent->nativeVirtualKey());
264
265 switch (xkb_compose_state_get_status(state: m_composeState)) {
266 case XKB_COMPOSE_COMPOSING:
267 return true;
268 case XKB_COMPOSE_CANCELLED:
269 reset();
270 return false;
271 case XKB_COMPOSE_COMPOSED:
272 {
273 const int size = xkb_compose_state_get_utf8(state: m_composeState, buffer: nullptr, size: 0);
274 QVarLengthArray<char, 32> buffer(size + 1);
275 xkb_compose_state_get_utf8(state: m_composeState, buffer: buffer.data(), size: buffer.size());
276 QString composedText = QString::fromUtf8(utf8: buffer.constData());
277
278 QInputMethodEvent event;
279 event.setCommitString(commitString: composedText);
280
281 if (!m_focusObject && qApp)
282 m_focusObject = qApp->focusObject();
283
284 if (m_focusObject)
285 QCoreApplication::sendEvent(receiver: m_focusObject, event: &event);
286 else
287 qCWarning(qLcQpaInputMethods, "no focus object");
288
289 reset();
290 return true;
291 }
292 case XKB_COMPOSE_NOTHING:
293 return false;
294 default:
295 Q_UNREACHABLE_RETURN(false);
296 }
297}
298
299#endif
300
301}
302
303QT_END_NAMESPACE
304
305#include "moc_qwaylandinputcontext_p.cpp"
306

Provided by KDAB

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

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