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
qwaylandqttextinputmethod.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3// Qt-Security score:critical reason:network-protocol
4
7
8#include <QtGui/qevent.h>
9#include <QtGui/qguiapplication.h>
10#include <QtGui/qinputmethod.h>
11#include <QtGui/qcolor.h>
12#include <QtGui/qtextformat.h>
13
14#include <QtCore/qelapsedtimer.h>
15
16#include <QtWaylandCompositor/qwaylandcompositor.h>
17#include <QtWaylandCompositor/qwaylandsurface.h>
18
20
21QWaylandQtTextInputMethodPrivate::QWaylandQtTextInputMethodPrivate(QWaylandCompositor *c)
22 : compositor(c)
23{
24}
25
26void QWaylandQtTextInputMethodPrivate::text_input_method_v1_enable(Resource *resource, struct ::wl_resource *surface)
27{
28 Q_Q(QWaylandQtTextInputMethod);
29 if (this->resource == resource) {
30 QWaylandSurface *waylandSurface = QWaylandSurface::fromResource(surface);
31 if (surface != nullptr) {
32 enabledSurfaces[resource] = waylandSurface;
33 emit q->surfaceEnabled(waylandSurface);
34 }
35 }
36}
37
38void QWaylandQtTextInputMethodPrivate::text_input_method_v1_disable(Resource *resource, struct ::wl_resource *surface)
39{
40 Q_Q(QWaylandQtTextInputMethod);
41 if (this->resource == resource) {
42 QWaylandSurface *waylandSurface = QWaylandSurface::fromResource(surface);
43 QWaylandSurface *enabledSurface = enabledSurfaces.take(resource);
44
45 if (Q_UNLIKELY(enabledSurface != waylandSurface))
46 qCWarning(qLcWaylandCompositorInputMethods) << "Disabled surface does not match the one currently enabled";
47
48 emit q->surfaceEnabled(waylandSurface);
49 }
50}
51
52void QWaylandQtTextInputMethodPrivate::text_input_method_v1_destroy(Resource *resource)
53{
54 if (this->resource == resource)
55 wl_resource_destroy(resource->handle);
56}
57
58void QWaylandQtTextInputMethodPrivate::text_input_method_v1_reset(Resource *resource)
59{
60 if (this->resource == resource)
61 qApp->inputMethod()->reset();
62}
63
64void QWaylandQtTextInputMethodPrivate::text_input_method_v1_commit(Resource *resource)
65{
66 if (this->resource == resource)
67 qApp->inputMethod()->commit();
68}
69
70void QWaylandQtTextInputMethodPrivate::text_input_method_v1_show_input_panel(Resource *resource)
71{
72 if (this->resource == resource)
73 qApp->inputMethod()->show();
74}
75
76void QWaylandQtTextInputMethodPrivate::text_input_method_v1_hide_input_panel(Resource *resource)
77{
78 if (this->resource == resource)
79 qApp->inputMethod()->hide();
80}
81
82void QWaylandQtTextInputMethodPrivate::text_input_method_v1_invoke_action(Resource *resource, int32_t type, int32_t cursorPosition)
83{
84 if (this->resource == resource)
85 qApp->inputMethod()->invokeAction(QInputMethod::Action(type), cursorPosition);
86}
87
88void QWaylandQtTextInputMethodPrivate::text_input_method_v1_update_cursor_rectangle(Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height)
89{
90 if (this->resource == resource)
91 cursorRectangle = QRect(x, y, width, height);
92}
93
94void QWaylandQtTextInputMethodPrivate::text_input_method_v1_start_update(Resource *resource, int32_t queries)
95{
96 if (this->resource == resource)
97 updatingQueries = Qt::InputMethodQueries(queries);
98}
99
100void QWaylandQtTextInputMethodPrivate::text_input_method_v1_update_hints(Resource *resource, int32_t hints)
101{
102 if (this->resource == resource)
103 this->hints = Qt::InputMethodHints(hints);
104}
105
106void QWaylandQtTextInputMethodPrivate::text_input_method_v1_update_anchor_position(Resource *resource, int32_t anchorPosition)
107{
108 if (this->resource == resource)
109 this->anchorPosition = anchorPosition;
110}
111
112void QWaylandQtTextInputMethodPrivate::text_input_method_v1_update_cursor_position(Resource *resource, int32_t cursorPosition)
113{
114 if (this->resource == resource)
115 this->cursorPosition = cursorPosition;
116}
117
118void QWaylandQtTextInputMethodPrivate::text_input_method_v1_update_surrounding_text(Resource *resource, const QString &surroundingText, int32_t surroundingTextOffset)
119{
120 if (this->resource == resource) {
121 this->surroundingText = surroundingText;
122 this->surroundingTextOffset = surroundingTextOffset;
123 }
124}
125
126void QWaylandQtTextInputMethodPrivate::text_input_method_v1_update_absolute_position(Resource *resource, int32_t absolutePosition)
127{
128 if (this->resource == resource)
129 this->absolutePosition = absolutePosition;
130}
131
132void QWaylandQtTextInputMethodPrivate::text_input_method_v1_update_preferred_language(Resource *resource, const QString &preferredLanguage)
133{
134 if (this->resource == resource)
135 this->preferredLanguage = preferredLanguage;
136}
137
138void QWaylandQtTextInputMethodPrivate::text_input_method_v1_end_update(Resource *resource)
139{
140 Q_Q(QWaylandQtTextInputMethod);
141 if (this->resource == resource && updatingQueries != 0) {
142 Qt::InputMethodQueries queries = updatingQueries;
143 updatingQueries = Qt::InputMethodQueries();
144 emit q->updateInputMethod(queries);
145 }
146}
147
148void QWaylandQtTextInputMethodPrivate::text_input_method_v1_acknowledge_input_method(Resource *resource)
149{
150 if (this->resource == resource)
151 waitingForSync = false;
152}
153
154QWaylandQtTextInputMethod::QWaylandQtTextInputMethod(QWaylandObject *container, QWaylandCompositor *compositor)
155 : QWaylandCompositorExtensionTemplate(container, *new QWaylandQtTextInputMethodPrivate(compositor))
156{
157 connect(&d_func()->focusDestroyListener, &QWaylandDestroyListener::fired,
158 this, &QWaylandQtTextInputMethod::focusSurfaceDestroyed);
159
160 connect(qGuiApp->inputMethod(), &QInputMethod::visibleChanged, this, &QWaylandQtTextInputMethod::sendVisibleChanged);
161 connect(qGuiApp->inputMethod(), &QInputMethod::keyboardRectangleChanged, this, &QWaylandQtTextInputMethod::sendKeyboardRectangleChanged);
162 connect(qGuiApp->inputMethod(), &QInputMethod::inputDirectionChanged, this, &QWaylandQtTextInputMethod::sendInputDirectionChanged);
163 connect(qGuiApp->inputMethod(), &QInputMethod::localeChanged, this, &QWaylandQtTextInputMethod::sendLocaleChanged);
164}
165
166
170
171void QWaylandQtTextInputMethod::focusSurfaceDestroyed()
172{
173 Q_D(QWaylandQtTextInputMethod);
174 d->focusDestroyListener.reset();
175 d->waitingForSync = false;
176 d->resource = nullptr;
177 d->focusedSurface = nullptr;
178}
179
181{
182 Q_D(const QWaylandQtTextInputMethod);
183 return d->focusedSurface;
184}
185
186QVariant QWaylandQtTextInputMethod::inputMethodQuery(Qt::InputMethodQuery property, QVariant argument) const
187{
188 Q_D(const QWaylandQtTextInputMethod);
189 switch (property) {
190 case Qt::ImHints:
191 return int(d->hints);
192 case Qt::ImCursorRectangle:
193 return d->cursorRectangle;
194 case Qt::ImCursorPosition:
195 return d->cursorPosition;
196 case Qt::ImSurroundingText:
197 return d->surroundingText;
198 case Qt::ImAbsolutePosition:
199 return d->absolutePosition;
200 case Qt::ImCurrentSelection:
201 return d->surroundingText.mid(qMin(d->cursorPosition, d->anchorPosition),
202 qAbs(d->anchorPosition - d->cursorPosition));
203 case Qt::ImAnchorPosition:
204 return d->anchorPosition;
205 case Qt::ImTextAfterCursor:
206 if (argument.isValid())
207 return d->surroundingText.mid(d->cursorPosition, argument.toInt());
208 return d->surroundingText.mid(d->cursorPosition);
209 case Qt::ImTextBeforeCursor:
210 if (argument.isValid())
211 return d->surroundingText.left(d->cursorPosition).right(argument.toInt());
212 return d->surroundingText.left(d->cursorPosition);
213 case Qt::ImPreferredLanguage:
214 return d->preferredLanguage;
215
216 default:
217 return QVariant();
218 }
219}
220
222{
223 Q_D(QWaylandQtTextInputMethod);
224 if (d->resource == nullptr || d->resource->handle == nullptr)
225 return;
226
227 d->send_key(d->resource->handle,
228 int(event->type()),
229 event->key(),
230 event->modifiers(),
231 event->isAutoRepeat(),
232 event->count(),
233 event->nativeScanCode(),
234 event->nativeVirtualKey(),
235 event->nativeModifiers(),
236 event->text());
237}
238
239void QWaylandQtTextInputMethod::sendInputMethodEvent(QInputMethodEvent *event)
240{
241 Q_D(QWaylandQtTextInputMethod);
242 if (d->resource == nullptr || d->resource->handle == nullptr || d->compositor == nullptr)
243 return;
244
245 if (d->updatingQueries != 0) {
246 qCWarning(qLcWaylandCompositorInputMethods) << "Input method event sent while client is updating. Ignored.";
247 return;
248 }
249
250 Q_ASSERT(!d->waitingForSync);
251
252 QString oldSurroundText = d->surroundingText;
253 int oldCursorPosition = d->cursorPosition;
254 int oldAnchorPosition = d->anchorPosition;
255 int oldAbsolutePosition = d->absolutePosition;
256 QRect oldCursorRectangle = d->cursorRectangle;
257 QString oldPreferredLanguage = d->preferredLanguage;
258 Qt::InputMethodHints oldHints = d->hints;
259
260 uint serial = d->compositor->nextSerial(); // ### Not needed if we block on this?
261 d->send_start_input_method_event(d->resource->handle, serial, d->surroundingTextOffset);
262 for (const QInputMethodEvent::Attribute &attribute : event->attributes()) {
263 switch (attribute.type) {
264 case QInputMethodEvent::TextFormat:
265 {
266 auto properties = attribute.value.value<QTextFormat>().properties();
267 if (properties.size() != 2 || properties.firstKey() != QTextFormat::FontUnderline || properties.lastKey() != QTextFormat::TextUnderlineStyle) {
268 qCWarning(qLcWaylandCompositorInputMethods()) << "Only underline text formats currently supported";
269 }
270
271 d->send_input_method_event_attribute(d->resource->handle,
272 serial,
273 attribute.type,
274 attribute.start,
275 attribute.length,
276 QString());
277 break;
278 }
279 case QInputMethodEvent::Cursor:
280 d->cursorPosition = attribute.start;
281 d->send_input_method_event_attribute(d->resource->handle,
282 serial,
283 attribute.type,
284 attribute.start,
285 attribute.length,
286 attribute.value.typeId() == QMetaType::QColor ? attribute.value.value<QColor>().name() : QString());
287 break;
288 case QInputMethodEvent::Language: // ### What is the type of value? Is it string?
289 Q_FALLTHROUGH();
290 case QInputMethodEvent::Ruby:
291 d->send_input_method_event_attribute(d->resource->handle,
292 serial,
293 attribute.type,
294 attribute.start,
295 attribute.length,
296 attribute.value.toString());
297 break;
298 case QInputMethodEvent::Selection:
299 d->send_input_method_event_attribute(d->resource->handle,
300 serial,
301 attribute.type,
302 attribute.start,
303 attribute.length,
304 QString());
305 break;
306 case QInputMethodEvent::MimeData:
307 qCWarning(qLcWaylandCompositorInputMethods())
308 << "Ignoring QInputMethodEvent::MimeData";
309 continue;
310 }
311 }
312
313 d->waitingForSync = true;
314 d->send_end_input_method_event(d->resource->handle,
315 serial,
316 event->commitString(),
317 event->preeditString(),
318 event->replacementStart(),
319 event->replacementLength());
320
321 static const qint64 acknowledgeTimeoutMs = qEnvironmentVariable("QT_WAYLAND_QTTEXTINPUTMETHOD_TIMEOUT",
322 QLatin1String("1000")).toInt();
323 QElapsedTimer timer;
324 timer.start();
325 while (d->waitingForSync) {
326 if (acknowledgeTimeoutMs > 0 && timer.hasExpired(acknowledgeTimeoutMs)) {
327 qCWarning(qLcWaylandCompositorInputMethods)
328 << "Timed out waiting for acknowledge_input_method()";
329 d->waitingForSync = false;
330 break;
331 }
332 d->compositor->processWaylandEvents();
333 // We might get into a situation where the client is waiting for us to
334 // until we confirm the frame is rendered until that he cannot answer
335 // our input events.
336 QCoreApplication::processEvents();
337 }
338
339 Qt::InputMethodQueries queries;
340 if (d->surroundingText != oldSurroundText)
341 queries |= Qt::ImSurroundingText;
342 if (d->cursorPosition != oldCursorPosition)
343 queries |= Qt::ImCursorPosition;
344 if (d->anchorPosition != oldAnchorPosition)
345 queries |= Qt::ImAnchorPosition;
346 if (d->absolutePosition != oldAbsolutePosition)
347 queries |= Qt::ImAbsolutePosition;
348 if (d->cursorRectangle != oldCursorRectangle)
349 queries |= Qt::ImCursorRectangle;
350 if (d->preferredLanguage != oldPreferredLanguage)
351 queries |= Qt::ImPreferredLanguage;
352 if (d->hints != oldHints)
353 queries |= Qt::ImHints;
354 if (queries != 0)
355 emit updateInputMethod(queries);
356}
357
358bool QWaylandQtTextInputMethod::isSurfaceEnabled(QWaylandSurface *surface) const
359{
360 Q_D(const QWaylandQtTextInputMethod);
361 return d->enabledSurfaces.values().contains(surface);
362}
363
364void QWaylandQtTextInputMethod::setFocus(QWaylandSurface *surface)
365{
366 Q_D(QWaylandQtTextInputMethod);
367
368 QWaylandQtTextInputMethodPrivate::Resource *resource = surface != nullptr ? d->resourceMap().value(surface->waylandClient()) : nullptr;
369 if (d->resource == resource && d->focusedSurface == surface) // same client, same surface
370 return;
371
372 if (d->resource != nullptr && d->focusedSurface != nullptr) {
373 d->send_leave(d->resource->handle, d->focusedSurface->resource());
374 d->focusDestroyListener.reset();
375 }
376
377 d->resource = resource;
378 d->focusedSurface = surface;
379
380 if (d->resource != nullptr && d->focusedSurface != nullptr) {
381 d->surroundingText.clear();
382 d->cursorPosition = 0;
383 d->anchorPosition = 0;
384 d->absolutePosition = 0;
385 d->cursorRectangle = QRect();
386 d->preferredLanguage.clear();
387 d->hints = Qt::InputMethodHints();
388 d->send_enter(d->resource->handle, d->focusedSurface->resource());
389 sendInputDirectionChanged();
390 sendLocaleChanged();
391 sendInputDirectionChanged();
392 d->focusDestroyListener.listenForDestruction(surface->resource());
393 if (d->inputPanelVisible && d->enabledSurfaces.values().contains(surface))
394 qGuiApp->inputMethod()->show();
395 }
396}
397
398void QWaylandQtTextInputMethod::sendLocaleChanged()
399{
400 Q_D(QWaylandQtTextInputMethod);
401 if (d->resource == nullptr || d->resource->handle == nullptr)
402 return;
403
404 d->send_locale_changed(d->resource->handle, qGuiApp->inputMethod()->locale().bcp47Name());
405}
406
407void QWaylandQtTextInputMethod::sendInputDirectionChanged()
408{
409 Q_D(QWaylandQtTextInputMethod);
410 if (d->resource == nullptr || d->resource->handle == nullptr)
411 return;
412
413 d->send_input_direction_changed(d->resource->handle, int(qGuiApp->inputMethod()->inputDirection()));
414}
415
416void QWaylandQtTextInputMethod::sendKeyboardRectangleChanged()
417{
418 Q_D(QWaylandQtTextInputMethod);
419 if (d->resource == nullptr || d->resource->handle == nullptr)
420 return;
421
422 QRectF keyboardRectangle = qGuiApp->inputMethod()->keyboardRectangle();
423 d->send_keyboard_rectangle_changed(d->resource->handle,
424 wl_fixed_from_double(keyboardRectangle.x()),
425 wl_fixed_from_double(keyboardRectangle.y()),
426 wl_fixed_from_double(keyboardRectangle.width()),
427 wl_fixed_from_double(keyboardRectangle.height()));
428}
429
430void QWaylandQtTextInputMethod::sendVisibleChanged()
431{
432 Q_D(QWaylandQtTextInputMethod);
433 if (d->resource == nullptr || d->resource->handle == nullptr)
434 return;
435
436 d->send_visible_changed(d->resource->handle, int(qGuiApp->inputMethod()->isVisible()));
437}
438
439void QWaylandQtTextInputMethod::add(::wl_client *client, uint32_t id, int version)
440{
441 Q_D(QWaylandQtTextInputMethod);
442 d->add(client, id, version);
443}
444
445const struct wl_interface *QWaylandQtTextInputMethod::interface()
446{
447 return QWaylandQtTextInputMethodPrivate::interface();
448}
449
450QByteArray QWaylandQtTextInputMethod::interfaceName()
451{
452 return QWaylandQtTextInputMethodPrivate::interfaceName();
453}
454
455QT_END_NAMESPACE
456
457#include "moc_qwaylandqttextinputmethod.cpp"
static const struct wl_interface * interface()
bool isSurfaceEnabled(QWaylandSurface *surface) const
void setFocus(QWaylandSurface *surface)
void add(::wl_client *client, uint32_t id, int version)
QVariant inputMethodQuery(Qt::InputMethodQuery property, QVariant argument) const
QWaylandSurface * focusedSurface() const
void sendInputMethodEvent(QInputMethodEvent *event)
Combined button and popup list for selecting options.