Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qwaylandtextinputv1.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 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// Qt-Security score:significant reason:default
4#include <qpa/qplatforminputcontext.h>
5
7
10#include "qwaylandinputmethodeventbuilder_p.h"
11
12#include <QtCore/qloggingcategory.h>
13#include <QtGui/QGuiApplication>
14#include <QtGui/private/qguiapplication_p.h>
15#include <QtGui/private/qhighdpiscaling_p.h>
16#include <QtGui/qpa/qplatformintegration.h>
17#include <QtGui/qevent.h>
18#include <QtGui/qwindow.h>
19#include <QTextCharFormat>
20#include <QList>
21#include <QRectF>
22#include <QLocale>
23
25
26namespace QtWaylandClient {
27
28namespace {
29
30const Qt::InputMethodQueries supportedQueries1 = Qt::ImEnabled |
31 Qt::ImSurroundingText |
32 Qt::ImCursorPosition |
33 Qt::ImAnchorPosition |
34 Qt::ImHints |
35 Qt::ImCursorRectangle |
36 Qt::ImPreferredLanguage;
37}
38
39QWaylandTextInputv1::QWaylandTextInputv1(QWaylandDisplay *display, struct ::zwp_text_input_v1 *text_input)
40 : QtWayland::zwp_text_input_v1(text_input)
41{
42 Q_UNUSED(display);
43}
44
46{
47 if (m_resetCallback)
48 wl_callback_destroy(m_resetCallback);
49 zwp_text_input_v1_destroy(object());
50}
51
53{
54 m_builder.reset();
55 m_preeditCommit = QString();
56 updateState(Qt::ImQueryAll, QWaylandTextInputInterface::update_state_reset);
57}
58
60{
61 if (QObject *o = QGuiApplication::focusObject()) {
62 QInputMethodEvent event;
63 event.setCommitString(m_preeditCommit);
64 QCoreApplication::sendEvent(o, &event);
65 }
66
67 reset();
68}
69
70const wl_callback_listener QWaylandTextInputv1::callbackListener = {
72};
73
74void QWaylandTextInputv1::resetCallback(void *data, wl_callback *, uint32_t)
75{
76 QWaylandTextInputv1 *self = static_cast<QWaylandTextInputv1*>(data);
77
78 if (self->m_resetCallback) {
79 wl_callback_destroy(self->m_resetCallback);
80 self->m_resetCallback = nullptr;
81 }
82}
83
84void QWaylandTextInputv1::updateState(Qt::InputMethodQueries queries, uint32_t flags)
85{
86 if (!QGuiApplication::focusObject())
87 return;
88
89 if (!QGuiApplication::focusWindow() || !QGuiApplication::focusWindow()->handle())
90 return;
91
92 auto *window = static_cast<QWaylandWindow *>(QGuiApplication::focusWindow()->handle());
93 auto *surface = window->wlSurface();
94 if (!surface || (surface != m_surface))
95 return;
96
97 queries &= supportedQueries1;
98
99 // Surrounding text, cursor and anchor positions are transferred together
100 if ((queries & Qt::ImSurroundingText) || (queries & Qt::ImCursorPosition) || (queries & Qt::ImAnchorPosition))
101 queries |= Qt::ImSurroundingText | Qt::ImCursorPosition | Qt::ImAnchorPosition;
102
103 QInputMethodQueryEvent event(queries);
104 QCoreApplication::sendEvent(QGuiApplication::focusObject(), &event);
105
106 if ((queries & Qt::ImSurroundingText) || (queries & Qt::ImCursorPosition) || (queries & Qt::ImAnchorPosition)) {
107 QString text = event.value(Qt::ImSurroundingText).toString();
108 int cursor = event.value(Qt::ImCursorPosition).toInt();
109 int anchor = event.value(Qt::ImAnchorPosition).toInt();
110
111 // Make sure text is not too big
112 if (text.toUtf8().size() > 2048) {
113 int c = qAbs(cursor - anchor) <= 512 ? qMin(cursor, anchor) + qAbs(cursor - anchor) / 2: cursor;
114
115 const int offset = c - qBound(0, c, 512 - qMin(text.size() - c, 256));
116 text = text.mid(offset + c - 256, 512);
117 cursor -= offset;
118 anchor -= offset;
119 }
120
121 set_surrounding_text(text, QWaylandInputMethodEventBuilder::indexToWayland(text, cursor), QWaylandInputMethodEventBuilder::indexToWayland(text, anchor));
122 }
123
124 if (queries & Qt::ImHints) {
125 QWaylandInputMethodContentType contentType = QWaylandInputMethodContentType::convert(static_cast<Qt::InputMethodHints>(event.value(Qt::ImHints).toInt()));
126 set_content_type(contentType.hint, contentType.purpose);
127 }
128
129 if (queries & Qt::ImCursorRectangle) {
130 const QRect &cRect = event.value(Qt::ImCursorRectangle).toRect();
131 const QRect &windowRect = QGuiApplication::inputMethod()->inputItemTransform().mapRect(cRect);
132 const QRect &surfaceRect = QHighDpi::toNativePixels(windowRect, QGuiApplication::focusWindow());
133 set_cursor_rectangle(surfaceRect.x(), surfaceRect.y(), surfaceRect.width(), surfaceRect.height());
134 }
135
136 if (queries & Qt::ImPreferredLanguage) {
137 const QString &language = event.value(Qt::ImPreferredLanguage).toString();
138 set_preferred_language(language);
139 }
140
142 QtWayland::zwp_text_input_v1::reset();
143 else
144 commit_state(m_serial);
145}
146
148{
149 // Not supported yet
150}
151
153{
154 return m_inputPanelVisible;
155}
156
158{
159 return m_keyboardRectangle;
160}
161
163{
164 return m_locale;
165}
166
168{
169 return m_inputDirection;
170}
171
173{
174 m_surface = surface;
175
176 updateState(Qt::ImQueryAll, QWaylandTextInputInterface::update_state_reset);
177}
178
180{
181 m_surface = nullptr;
182}
183
185{
186 const QList<QByteArray> modifiersMap = QByteArray::fromRawData(static_cast<const char*>(map->data), map->size).split('\0');
187
188 m_modifiersMap.clear();
189
190 for (const QByteArray &modifier : modifiersMap) {
191 if (modifier == "Shift")
192 m_modifiersMap.append(Qt::ShiftModifier);
193 else if (modifier == "Control")
194 m_modifiersMap.append(Qt::ControlModifier);
195 else if (modifier == "Alt")
196 m_modifiersMap.append(Qt::AltModifier);
197 else if (modifier == "Mod1")
198 m_modifiersMap.append(Qt::AltModifier);
199 else if (modifier == "Mod4")
200 m_modifiersMap.append(Qt::MetaModifier);
201 else
202 m_modifiersMap.append(Qt::NoModifier);
203 }
204}
205
207{
208 const bool inputPanelVisible = (visible == 1);
209 if (m_inputPanelVisible != inputPanelVisible) {
210 m_inputPanelVisible = inputPanelVisible;
211 QGuiApplicationPrivate::platformIntegration()->inputContext()->emitInputPanelVisibleChanged();
212 }
213}
214
215void QWaylandTextInputv1::zwp_text_input_v1_preedit_string(uint32_t serial, const QString &text, const QString &commit)
216{
217 m_serial = serial;
218
219 if (m_resetCallback) {
220 qCDebug(qLcQpaInputMethods()) << "discard preedit_string: reset not confirmed";
221 m_builder.reset();
222 return;
223 }
224
225 if (!QGuiApplication::focusObject())
226 return;
227
228 QInputMethodEvent *event = m_builder.buildPreedit(text);
229
230 m_builder.reset();
231 m_preeditCommit = commit;
232
233 QCoreApplication::sendEvent(QGuiApplication::focusObject(), event);
234 delete event;
235}
236
237void QWaylandTextInputv1::zwp_text_input_v1_preedit_styling(uint32_t index, uint32_t length, uint32_t style)
238{
239 m_builder.addPreeditStyling(index, length, style);
240}
241
243{
244 m_builder.setPreeditCursor(index);
245}
246
247void QWaylandTextInputv1::zwp_text_input_v1_commit_string(uint32_t serial, const QString &text)
248{
249 m_serial = serial;
250
251 if (m_resetCallback) {
252 qCDebug(qLcQpaInputMethods()) << "discard commit_string: reset not confirmed";
253 m_builder.reset();
254 return;
255 }
256
257 if (!QGuiApplication::focusObject())
258 return;
259
260 // When committing the text, the preeditString needs to be reset, to prevent it to be
261 // send again in the commit() function
262 m_preeditCommit.clear();
263
264 QInputMethodEvent *event = m_builder.buildCommit(text);
265
266 m_builder.reset();
267
268 QCoreApplication::sendEvent(QGuiApplication::focusObject(), event);
269 delete event;
270}
271
272void QWaylandTextInputv1::zwp_text_input_v1_cursor_position(int32_t index, int32_t anchor)
273{
274 m_builder.setCursorPosition(index, anchor);
275}
276
277void QWaylandTextInputv1::zwp_text_input_v1_delete_surrounding_text(int32_t before_length, uint32_t after_length)
278{
279 //before_length is negative, but the builder expects it to be positive
280 m_builder.setDeleteSurroundingText(-before_length, after_length);
281}
282
283void QWaylandTextInputv1::zwp_text_input_v1_keysym(uint32_t serial, uint32_t time, uint32_t sym, uint32_t state, uint32_t modifiers)
284{
285 m_serial = serial;
286
287#if QT_CONFIG(xkbcommon)
288 if (m_resetCallback) {
289 qCDebug(qLcQpaInputMethods()) << "discard keysym: reset not confirmed";
290 return;
291 }
292
293 if (!QGuiApplication::focusWindow())
294 return;
295
296 Qt::KeyboardModifiers qtModifiers = modifiersToQtModifiers(modifiers);
297
298 QEvent::Type type = state == WL_KEYBOARD_KEY_STATE_PRESSED ? QEvent::KeyPress : QEvent::KeyRelease;
299 QString text = QXkbCommon::lookupStringNoKeysymTransformations(sym);
300 int qtkey = QXkbCommon::keysymToQtKey(sym, qtModifiers);
301
302 QWindowSystemInterface::handleKeyEvent(QGuiApplication::focusWindow(),
303 time, type, qtkey, qtModifiers, text);
304#else
305 Q_UNUSED(time);
306 Q_UNUSED(sym);
307 Q_UNUSED(state);
308 Q_UNUSED(modifiers);
309#endif
310}
311
312void QWaylandTextInputv1::zwp_text_input_v1_language(uint32_t serial, const QString &language)
313{
314 m_serial = serial;
315
316 if (m_resetCallback) {
317 qCDebug(qLcQpaInputMethods()) << "discard language: reset not confirmed";
318 return;
319 }
320
321 const QLocale locale(language);
322 if (m_locale != locale) {
323 m_locale = locale;
324 QGuiApplicationPrivate::platformIntegration()->inputContext()->emitLocaleChanged();
325 }
326}
327
328void QWaylandTextInputv1::zwp_text_input_v1_text_direction(uint32_t serial, uint32_t direction)
329{
330 m_serial = serial;
331
332 if (m_resetCallback) {
333 qCDebug(qLcQpaInputMethods()) << "discard text_direction: reset not confirmed";
334 return;
335 }
336
337 const Qt::LayoutDirection inputDirection = (direction == text_direction_auto) ? Qt::LayoutDirectionAuto :
338 (direction == text_direction_ltr) ? Qt::LeftToRight :
339 (direction == text_direction_rtl) ? Qt::RightToLeft : Qt::LayoutDirectionAuto;
340 if (m_inputDirection != inputDirection) {
341 m_inputDirection = inputDirection;
342 QGuiApplicationPrivate::platformIntegration()->inputContext()->emitInputDirectionChanged(m_inputDirection);
343 }
344}
345
346Qt::KeyboardModifiers QWaylandTextInputv1::modifiersToQtModifiers(uint32_t modifiers)
347{
348 Qt::KeyboardModifiers ret = Qt::NoModifier;
349 for (int i = 0; i < m_modifiersMap.size(); ++i) {
350 if (modifiers & (1 << i)) {
351 ret |= m_modifiersMap[i];
352 }
353 }
354 return ret;
355}
356
357}
358
359QT_END_NAMESPACE
void zwp_text_input_v1_language(uint32_t serial, const QString &language) override
void zwp_text_input_v1_keysym(uint32_t serial, uint32_t time, uint32_t sym, uint32_t state, uint32_t modifiers) override
void zwp_text_input_v1_enter(struct ::wl_surface *surface) override
void zwp_text_input_v1_input_panel_state(uint32_t state) override
Qt::LayoutDirection inputDirection() const override
void zwp_text_input_v1_cursor_position(int32_t index, int32_t anchor) override
void zwp_text_input_v1_modifiers_map(wl_array *map) override
void zwp_text_input_v1_preedit_string(uint32_t serial, const QString &text, const QString &commit) override
void zwp_text_input_v1_commit_string(uint32_t serial, const QString &text) override
void zwp_text_input_v1_delete_surrounding_text(int32_t before_length, uint32_t after_length) override
void updateState(Qt::InputMethodQueries queries, uint32_t flags) override
void zwp_text_input_v1_preedit_cursor(int32_t index) override
void zwp_text_input_v1_text_direction(uint32_t serial, uint32_t direction) override
void zwp_text_input_v1_preedit_styling(uint32_t index, uint32_t length, uint32_t style) override
void setCursorInsidePreedit(int cursor) override
Combined button and popup list for selecting options.