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