1// Copyright (C) 2017-2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qwaylandtextinput.h"
5#include "qwaylandtextinput_p.h"
6
7#include <QtWaylandCompositor/QWaylandCompositor>
8#include <QtWaylandCompositor/private/qwaylandseat_p.h>
9
10#include "qwaylandsurface.h"
11#include "qwaylandview.h"
12#include "qwaylandinputmethodeventbuilder_p.h"
13#include "qwaylandinputmethodcontrol.h"
14
15#include <QGuiApplication>
16#include <QInputMethodEvent>
17
18#if QT_CONFIG(xkbcommon)
19#include <QtGui/private/qxkbcommon_p.h>
20#endif
21
22QT_BEGIN_NAMESPACE
23
24QWaylandTextInputClientState::QWaylandTextInputClientState()
25{
26}
27
28Qt::InputMethodQueries QWaylandTextInputClientState::updatedQueries(const QWaylandTextInputClientState &other) const
29{
30 Qt::InputMethodQueries queries;
31
32 if (hints != other.hints)
33 queries |= Qt::ImHints;
34 if (cursorRectangle != other.cursorRectangle)
35 queries |= Qt::ImCursorRectangle;
36 if (surroundingText != other.surroundingText)
37 queries |= Qt::ImSurroundingText | Qt::ImCurrentSelection;
38 if (cursorPosition != other.cursorPosition)
39 queries |= Qt::ImCursorPosition | Qt::ImCurrentSelection;
40 if (anchorPosition != other.anchorPosition)
41 queries |= Qt::ImAnchorPosition | Qt::ImCurrentSelection;
42 if (preferredLanguage != other.preferredLanguage)
43 queries |= Qt::ImPreferredLanguage;
44
45 return queries;
46}
47
48Qt::InputMethodQueries QWaylandTextInputClientState::mergeChanged(const QWaylandTextInputClientState &other) {
49 Qt::InputMethodQueries queries;
50
51 if ((other.changedState & Qt::ImHints) && hints != other.hints) {
52 hints = other.hints;
53 queries |= Qt::ImHints;
54 }
55
56 if ((other.changedState & Qt::ImCursorRectangle) && cursorRectangle != other.cursorRectangle) {
57 cursorRectangle = other.cursorRectangle;
58 queries |= Qt::ImCursorRectangle;
59 }
60
61 if ((other.changedState & Qt::ImSurroundingText) && surroundingText != other.surroundingText) {
62 surroundingText = other.surroundingText;
63 queries |= Qt::ImSurroundingText | Qt::ImCurrentSelection;
64 }
65
66 if ((other.changedState & Qt::ImCursorPosition) && cursorPosition != other.cursorPosition) {
67 cursorPosition = other.cursorPosition;
68 queries |= Qt::ImCursorPosition | Qt::ImCurrentSelection;
69 }
70
71 if ((other.changedState & Qt::ImAnchorPosition) && anchorPosition != other.anchorPosition) {
72 anchorPosition = other.anchorPosition;
73 queries |= Qt::ImAnchorPosition | Qt::ImCurrentSelection;
74 }
75
76 if ((other.changedState & Qt::ImPreferredLanguage) && preferredLanguage != other.preferredLanguage) {
77 preferredLanguage = other.preferredLanguage;
78 queries |= Qt::ImPreferredLanguage;
79 }
80
81 return queries;
82}
83
84QWaylandTextInputPrivate::QWaylandTextInputPrivate(QWaylandCompositor *compositor)
85 : compositor(compositor)
86 , currentState(new QWaylandTextInputClientState)
87 , pendingState(new QWaylandTextInputClientState)
88{
89}
90
91void QWaylandTextInputPrivate::sendInputMethodEvent(QInputMethodEvent *event)
92{
93 Q_Q(QWaylandTextInput);
94
95 if (!focusResource || !focusResource->handle)
96 return;
97
98 QWaylandTextInputClientState afterCommit;
99
100 afterCommit.surroundingText = currentState->surroundingText;
101 afterCommit.cursorPosition = qMin(a: currentState->cursorPosition, b: currentState->anchorPosition);
102
103 // Remove selection
104 afterCommit.surroundingText.remove(i: afterCommit.cursorPosition, len: qAbs(t: currentState->cursorPosition - currentState->anchorPosition));
105
106 if (event->replacementLength() > 0 || event->replacementStart() != 0) {
107 // Remove replacement
108 afterCommit.cursorPosition = qBound(min: 0, val: afterCommit.cursorPosition + event->replacementStart(), max: afterCommit.surroundingText.size());
109 afterCommit.surroundingText.remove(i: afterCommit.cursorPosition,
110 len: qMin(a: event->replacementLength(),
111 b: afterCommit.surroundingText.size() - afterCommit.cursorPosition));
112
113 if (event->replacementStart() <= 0 && (event->replacementLength() >= -event->replacementStart())) {
114 const int selectionStart = qMin(a: currentState->cursorPosition, b: currentState->anchorPosition);
115 const int selectionEnd = qMax(a: currentState->cursorPosition, b: currentState->anchorPosition);
116 const int before = QWaylandInputMethodEventBuilder::indexToWayland(text: currentState->surroundingText, length: -event->replacementStart(), base: selectionStart + event->replacementStart());
117 const int after = QWaylandInputMethodEventBuilder::indexToWayland(text: currentState->surroundingText, length: event->replacementLength() + event->replacementStart(), base: selectionEnd);
118 send_delete_surrounding_text(focusResource->handle, before, after);
119 } else {
120 // TODO: Implement this case
121 qWarning() << "Not yet supported case of replacement. Start:" << event->replacementStart() << "length:" << event->replacementLength();
122 }
123 }
124
125 // Insert commit string
126 afterCommit.surroundingText.insert(i: afterCommit.cursorPosition, s: event->commitString());
127 afterCommit.cursorPosition += event->commitString().size();
128 afterCommit.anchorPosition = afterCommit.cursorPosition;
129
130 for (const QInputMethodEvent::Attribute &attribute : event->attributes()) {
131 if (attribute.type == QInputMethodEvent::Selection) {
132 afterCommit.cursorPosition = attribute.start;
133 afterCommit.anchorPosition = attribute.length;
134 int cursor = QWaylandInputMethodEventBuilder::indexToWayland(text: afterCommit.surroundingText, length: qAbs(t: attribute.start - afterCommit.cursorPosition), base: qMin(a: attribute.start, b: afterCommit.cursorPosition));
135 int anchor = QWaylandInputMethodEventBuilder::indexToWayland(text: afterCommit.surroundingText, length: qAbs(t: attribute.length - afterCommit.cursorPosition), base: qMin(a: attribute.length, b: afterCommit.cursorPosition));
136 send_cursor_position(focusResource->handle,
137 attribute.start < afterCommit.cursorPosition ? -cursor : cursor,
138 attribute.length < afterCommit.cursorPosition ? -anchor : anchor);
139 }
140 }
141 send_commit_string(focusResource->handle, event->commitString());
142 for (const QInputMethodEvent::Attribute &attribute : event->attributes()) {
143 if (attribute.type == QInputMethodEvent::Cursor) {
144 int index = QWaylandInputMethodEventBuilder::indexToWayland(text: event->preeditString(), length: attribute.start);
145 send_preedit_cursor(focusResource->handle, index);
146 } else if (attribute.type == QInputMethodEvent::TextFormat) {
147 int start = QWaylandInputMethodEventBuilder::indexToWayland(text: event->preeditString(), length: attribute.start);
148 int length = QWaylandInputMethodEventBuilder::indexToWayland(text: event->preeditString(), length: attribute.length, base: attribute.start);
149 // TODO add support for different stylesQWaylandTextInput
150 send_preedit_styling(focusResource->handle, start, length, preedit_style_default);
151 }
152 }
153 send_preedit_string(focusResource->handle, event->preeditString(), event->preeditString());
154
155 Qt::InputMethodQueries queries = currentState->updatedQueries(other: afterCommit);
156 currentState->surroundingText = afterCommit.surroundingText;
157 currentState->cursorPosition = afterCommit.cursorPosition;
158 currentState->anchorPosition = afterCommit.anchorPosition;
159
160 if (queries) {
161 qCDebug(qLcWaylandCompositorInputMethods) << "QInputMethod::update() after QInputMethodEvent" << queries;
162
163 emit q->updateInputMethod(queries);
164 }
165}
166
167void QWaylandTextInputPrivate::sendKeyEvent(QKeyEvent *event)
168{
169 if (!focusResource || !focusResource->handle)
170 return;
171
172 uint mods = 0;
173 const auto &qtMods = event->modifiers();
174 if (qtMods & Qt::ShiftModifier)
175 mods |= shiftModifierMask;
176 if (qtMods & Qt::ControlModifier)
177 mods |= controlModifierMask;
178 if (qtMods & Qt::AltModifier)
179 mods |= altModifierMask;
180 if (qtMods & Qt::MetaModifier)
181 mods |= metaModifierMask;
182
183#if QT_CONFIG(xkbcommon)
184 for (xkb_keysym_t keysym : QXkbCommon::toKeysym(event)) {
185 send_keysym(focusResource->handle, event->timestamp(), keysym,
186 event->type() == QEvent::KeyPress ? WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED,
187 mods);
188 }
189#else
190 Q_UNUSED(event);
191#endif
192}
193
194void QWaylandTextInputPrivate::sendInputPanelState()
195{
196 if (!focusResource || !focusResource->handle)
197 return;
198
199 QInputMethod *inputMethod = qApp->inputMethod();
200 const QRectF& keyboardRect = inputMethod->keyboardRectangle();
201 const QRectF& sceneInputRect = inputMethod->inputItemTransform().mapRect(inputMethod->inputItemRectangle());
202 const QRectF& localRect = sceneInputRect.intersected(r: keyboardRect).translated(p: -sceneInputRect.topLeft());
203
204 send_input_panel_state(focusResource->handle,
205 inputMethod->isVisible() ? input_panel_visibility_visible : input_panel_visibility_hidden,
206 localRect.x(), localRect.y(), localRect.width(), localRect.height());
207}
208
209void QWaylandTextInputPrivate::sendTextDirection()
210{
211 if (!focusResource || !focusResource->handle)
212 return;
213
214 const Qt::LayoutDirection direction = qApp->inputMethod()->inputDirection();
215 send_text_direction(focusResource->handle,
216 (direction == Qt::LeftToRight) ? text_direction_ltr :
217 (direction == Qt::RightToLeft) ? text_direction_rtl : text_direction_auto);
218}
219
220void QWaylandTextInputPrivate::sendLocale()
221{
222 if (!focusResource || !focusResource->handle)
223 return;
224
225 const QLocale locale = qApp->inputMethod()->locale();
226 send_language(focusResource->handle, locale.bcp47Name());
227}
228
229QVariant QWaylandTextInputPrivate::inputMethodQuery(Qt::InputMethodQuery property, QVariant argument) const
230{
231 switch (property) {
232 case Qt::ImHints:
233 return QVariant(static_cast<int>(currentState->hints));
234 case Qt::ImCursorRectangle:
235 return currentState->cursorRectangle;
236 case Qt::ImFont:
237 // Not supported
238 return QVariant();
239 case Qt::ImCursorPosition:
240 return currentState->cursorPosition;
241 case Qt::ImSurroundingText:
242 return currentState->surroundingText;
243 case Qt::ImCurrentSelection:
244 return currentState->surroundingText.mid(position: qMin(a: currentState->cursorPosition, b: currentState->anchorPosition),
245 n: qAbs(t: currentState->anchorPosition - currentState->cursorPosition));
246 case Qt::ImMaximumTextLength:
247 // Not supported
248 return QVariant();
249 case Qt::ImAnchorPosition:
250 return currentState->anchorPosition;
251 case Qt::ImAbsolutePosition:
252 // We assume the surrounding text is our whole document for now
253 return currentState->cursorPosition;
254 case Qt::ImTextAfterCursor:
255 if (argument.isValid())
256 return currentState->surroundingText.mid(position: currentState->cursorPosition, n: argument.toInt());
257 return currentState->surroundingText.mid(position: currentState->cursorPosition);
258 case Qt::ImTextBeforeCursor:
259 if (argument.isValid())
260 return currentState->surroundingText.left(n: currentState->cursorPosition).right(n: argument.toInt());
261 return currentState->surroundingText.left(n: currentState->cursorPosition);
262 case Qt::ImPreferredLanguage:
263 return currentState->preferredLanguage;
264
265 default:
266 return QVariant();
267 }
268}
269
270void QWaylandTextInputPrivate::setFocus(QWaylandSurface *surface)
271{
272 Q_Q(QWaylandTextInput);
273
274 if (focusResource && focus != surface) {
275 uint32_t serial = compositor->nextSerial();
276 send_leave(focusResource->handle, serial, focus->resource());
277 focusDestroyListener.reset();
278 }
279
280 Resource *resource = surface ? resourceMap().value(surface->waylandClient()) : 0;
281
282 if (resource && (focus != surface || focusResource != resource)) {
283 uint32_t serial = compositor->nextSerial();
284 currentState.reset(p: new QWaylandTextInputClientState);
285 pendingState.reset(p: new QWaylandTextInputClientState);
286 send_enter(resource->handle, serial, surface->resource());
287 focusResource = resource;
288 sendInputPanelState();
289 sendLocale();
290 sendTextDirection();
291 focusDestroyListener.listenForDestruction(resource: surface->resource());
292 if (inputPanelVisible && q->isSurfaceEnabled(surface))
293 qApp->inputMethod()->show();
294 }
295
296 focusResource = resource;
297 focus = surface;
298}
299
300void QWaylandTextInputPrivate::sendModifiersMap(const QByteArray &modifiersMap)
301{
302 send_modifiers_map(focusResource->handle, modifiersMap);
303}
304
305#if !QT_CONFIG(xkbcommon)
306#define XKB_MOD_NAME_SHIFT "Shift"
307#define XKB_MOD_NAME_CTRL "Control"
308#define XKB_MOD_NAME_ALT "Mod1"
309#define XKB_MOD_NAME_LOGO "Mod4"
310#endif
311void QWaylandTextInputPrivate::zwp_text_input_v2_bind_resource(Resource *resource)
312{
313 QByteArray modifiers = XKB_MOD_NAME_SHIFT + QByteArray(1, '\0');
314 modifiers += XKB_MOD_NAME_CTRL + QByteArray(1, '\0');
315 modifiers += XKB_MOD_NAME_ALT + QByteArray(1, '\0');
316 modifiers += XKB_MOD_NAME_LOGO + QByteArray(1, '\0');
317 send_modifiers_map(resource->handle, modifiers);
318}
319
320void QWaylandTextInputPrivate::zwp_text_input_v2_destroy_resource(Resource *resource)
321{
322 if (focusResource == resource)
323 focusResource = nullptr;
324}
325
326void QWaylandTextInputPrivate::zwp_text_input_v2_destroy(Resource *resource)
327{
328 wl_resource_destroy(resource->handle);
329}
330
331void QWaylandTextInputPrivate::zwp_text_input_v2_enable(Resource *resource, wl_resource *surface)
332{
333 Q_Q(QWaylandTextInput);
334
335 QWaylandSurface *s = QWaylandSurface::fromResource(resource: surface);
336 enabledSurfaces.insert(resource, s);
337
338 QWaylandInputMethodControl *control = s->inputMethodControl();
339 if (control)
340 control->updateTextInput();
341
342 emit q->surfaceEnabled(surface: s);
343}
344
345void QWaylandTextInputPrivate::zwp_text_input_v2_disable(QtWaylandServer::zwp_text_input_v2::Resource *resource, wl_resource *)
346{
347 Q_Q(QWaylandTextInput);
348
349 QWaylandSurface *s = enabledSurfaces.take(resource);
350 emit q->surfaceDisabled(surface: s);
351}
352
353void QWaylandTextInputPrivate::zwp_text_input_v2_show_input_panel(Resource *)
354{
355 inputPanelVisible = true;
356
357 qApp->inputMethod()->show();
358}
359
360void QWaylandTextInputPrivate::zwp_text_input_v2_hide_input_panel(Resource *)
361{
362 inputPanelVisible = false;
363
364 qApp->inputMethod()->hide();
365}
366
367void QWaylandTextInputPrivate::zwp_text_input_v2_set_cursor_rectangle(Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height)
368{
369 if (resource != focusResource)
370 return;
371
372 pendingState->cursorRectangle = QRect(x, y, width, height);
373
374 pendingState->changedState |= Qt::ImCursorRectangle;
375}
376
377void QWaylandTextInputPrivate::zwp_text_input_v2_update_state(Resource *resource, uint32_t serial, uint32_t flags)
378{
379 Q_Q(QWaylandTextInput);
380
381 qCDebug(qLcWaylandCompositorInputMethods) << "update_state" << serial << flags;
382
383 if (resource != focusResource)
384 return;
385
386 if (flags == update_state_reset || flags == update_state_enter) {
387 qCDebug(qLcWaylandCompositorInputMethods) << "QInputMethod::reset()";
388 qApp->inputMethod()->reset();
389 }
390
391 this->serial = serial;
392
393 Qt::InputMethodQueries queries;
394 if (flags == update_state_change) {
395 queries = currentState->mergeChanged(other: *pendingState);
396 } else {
397 queries = pendingState->updatedQueries(other: *currentState);
398 currentState.swap(u&: pendingState);
399 }
400
401 pendingState.reset(p: new QWaylandTextInputClientState);
402
403 if (queries) {
404 qCDebug(qLcWaylandCompositorInputMethods) << "QInputMethod::update()" << queries;
405
406 emit q->updateInputMethod(queries);
407 }
408}
409
410void QWaylandTextInputPrivate::zwp_text_input_v2_set_content_type(Resource *resource, uint32_t hint, uint32_t purpose)
411{
412 if (resource != focusResource)
413 return;
414
415 pendingState->hints = Qt::ImhNone;
416
417 if ((hint & content_hint_auto_completion) == 0
418 && (hint & content_hint_auto_correction) == 0)
419 pendingState->hints |= Qt::ImhNoPredictiveText;
420 if ((hint & content_hint_auto_capitalization) == 0)
421 pendingState->hints |= Qt::ImhNoAutoUppercase;
422 if ((hint & content_hint_lowercase) != 0)
423 pendingState->hints |= Qt::ImhPreferLowercase;
424 if ((hint & content_hint_uppercase) != 0)
425 pendingState->hints |= Qt::ImhPreferUppercase;
426 if ((hint & content_hint_hidden_text) != 0)
427 pendingState->hints |= Qt::ImhHiddenText;
428 if ((hint & content_hint_sensitive_data) != 0)
429 pendingState->hints |= Qt::ImhSensitiveData;
430 if ((hint & content_hint_latin) != 0)
431 pendingState->hints |= Qt::ImhLatinOnly;
432 if ((hint & content_hint_multiline) != 0)
433 pendingState->hints |= Qt::ImhMultiLine;
434
435 switch (purpose) {
436 case content_purpose_normal:
437 break;
438 case content_purpose_alpha:
439 pendingState->hints |= Qt::ImhUppercaseOnly | Qt::ImhLowercaseOnly;
440 break;
441 case content_purpose_digits:
442 pendingState->hints |= Qt::ImhDigitsOnly;
443 break;
444 case content_purpose_number:
445 pendingState->hints |= Qt::ImhFormattedNumbersOnly;
446 break;
447 case content_purpose_phone:
448 pendingState->hints |= Qt::ImhDialableCharactersOnly;
449 break;
450 case content_purpose_url:
451 pendingState->hints |= Qt::ImhUrlCharactersOnly;
452 break;
453 case content_purpose_email:
454 pendingState->hints |= Qt::ImhEmailCharactersOnly;
455 break;
456 case content_purpose_name:
457 case content_purpose_password:
458 break;
459 case content_purpose_date:
460 pendingState->hints |= Qt::ImhDate;
461 break;
462 case content_purpose_time:
463 pendingState->hints |= Qt::ImhTime;
464 break;
465 case content_purpose_datetime:
466 pendingState->hints |= Qt::ImhDate | Qt::ImhTime;
467 break;
468 case content_purpose_terminal:
469 default:
470 break;
471 }
472
473 pendingState->changedState |= Qt::ImHints;
474}
475
476void QWaylandTextInputPrivate::zwp_text_input_v2_set_preferred_language(Resource *resource, const QString &language)
477{
478 if (resource != focusResource)
479 return;
480
481 pendingState->preferredLanguage = language;
482
483 pendingState->changedState |= Qt::ImPreferredLanguage;
484}
485
486void QWaylandTextInputPrivate::zwp_text_input_v2_set_surrounding_text(Resource *resource, const QString &text, int32_t cursor, int32_t anchor)
487{
488 if (resource != focusResource)
489 return;
490
491 pendingState->surroundingText = text;
492 pendingState->cursorPosition = QWaylandInputMethodEventBuilder::indexFromWayland(text, length: cursor);
493 pendingState->anchorPosition = QWaylandInputMethodEventBuilder::indexFromWayland(text, length: anchor);
494
495 pendingState->changedState |= Qt::ImSurroundingText | Qt::ImCursorPosition | Qt::ImAnchorPosition;
496}
497
498QWaylandTextInput::QWaylandTextInput(QWaylandObject *container, QWaylandCompositor *compositor)
499 : QWaylandCompositorExtensionTemplate(container, *new QWaylandTextInputPrivate(compositor))
500{
501 connect(sender: &d_func()->focusDestroyListener, signal: &QWaylandDestroyListener::fired,
502 context: this, slot: &QWaylandTextInput::focusSurfaceDestroyed);
503
504 connect(qApp->inputMethod(), signal: &QInputMethod::visibleChanged,
505 context: this, slot: &QWaylandTextInput::sendInputPanelState);
506 connect(qApp->inputMethod(), signal: &QInputMethod::keyboardRectangleChanged,
507 context: this, slot: &QWaylandTextInput::sendInputPanelState);
508 connect(qApp->inputMethod(), signal: &QInputMethod::inputDirectionChanged,
509 context: this, slot: &QWaylandTextInput::sendTextDirection);
510 connect(qApp->inputMethod(), signal: &QInputMethod::localeChanged,
511 context: this, slot: &QWaylandTextInput::sendLocale);
512}
513
514QWaylandTextInput::~QWaylandTextInput()
515{
516}
517
518void QWaylandTextInput::sendInputMethodEvent(QInputMethodEvent *event)
519{
520 Q_D(QWaylandTextInput);
521
522 d->sendInputMethodEvent(event);
523}
524
525void QWaylandTextInput::sendKeyEvent(QKeyEvent *event)
526{
527 Q_D(QWaylandTextInput);
528
529 d->sendKeyEvent(event);
530}
531
532void QWaylandTextInput::sendInputPanelState()
533{
534 Q_D(QWaylandTextInput);
535
536 d->sendInputPanelState();
537}
538
539void QWaylandTextInput::sendTextDirection()
540{
541 Q_D(QWaylandTextInput);
542
543 d->sendTextDirection();
544}
545
546void QWaylandTextInput::sendLocale()
547{
548 Q_D(QWaylandTextInput);
549
550 d->sendLocale();
551}
552
553QVariant QWaylandTextInput::inputMethodQuery(Qt::InputMethodQuery property, QVariant argument) const
554{
555 const Q_D(QWaylandTextInput);
556
557 return d->inputMethodQuery(property, argument);
558}
559
560QWaylandSurface *QWaylandTextInput::focus() const
561{
562 const Q_D(QWaylandTextInput);
563
564 return d->focus;
565}
566
567void QWaylandTextInput::setFocus(QWaylandSurface *surface)
568{
569 Q_D(QWaylandTextInput);
570
571 d->setFocus(surface);
572}
573
574void QWaylandTextInput::focusSurfaceDestroyed(void *)
575{
576 Q_D(QWaylandTextInput);
577
578 d->focusDestroyListener.reset();
579
580 d->focus = nullptr;
581 d->focusResource = nullptr;
582}
583
584bool QWaylandTextInput::isSurfaceEnabled(QWaylandSurface *surface) const
585{
586 const Q_D(QWaylandTextInput);
587
588 return d->enabledSurfaces.values().contains(surface);
589}
590
591void QWaylandTextInput::add(::wl_client *client, uint32_t id, int version)
592{
593 Q_D(QWaylandTextInput);
594
595 d->add(client, id, version);
596}
597
598const wl_interface *QWaylandTextInput::interface()
599{
600 return QWaylandTextInputPrivate::interface();
601}
602
603QByteArray QWaylandTextInput::interfaceName()
604{
605 return QWaylandTextInputPrivate::interfaceName();
606}
607
608
609void QWaylandTextInput::sendModifiersMap(const QByteArray &modifiersMap)
610{
611 Q_D(QWaylandTextInput);
612
613 const QList<QByteArray> modifiers = modifiersMap.split(sep: '\0');
614
615 int numModifiers = modifiers.size();
616 if (modifiers.last().isEmpty())
617 numModifiers--;
618
619 for (int i = 0; i < numModifiers; ++i) {
620 const auto modString = modifiers.at(i);
621 if (modString == XKB_MOD_NAME_SHIFT)
622 d->shiftModifierMask = 1 << i;
623 else if (modString == XKB_MOD_NAME_CTRL)
624 d->controlModifierMask = 1 << i;
625 else if (modString == XKB_MOD_NAME_ALT)
626 d->altModifierMask = 1 << i;
627 else if (modString == XKB_MOD_NAME_LOGO)
628 d->metaModifierMask = 1 << i;
629 else
630 qCDebug(qLcWaylandCompositorInputMethods) << "unsupported modifier name " << modString;
631 }
632 d->sendModifiersMap(modifiersMap);
633}
634
635QT_END_NAMESPACE
636
637#include "moc_qwaylandtextinput.cpp"
638

source code of qtwayland/src/compositor/extensions/qwaylandtextinput.cpp