49 Qt::InputMethodQueries queries;
51 if ((other.changedState & Qt::ImHints) && hints != other.hints) {
53 queries |= Qt::ImHints;
56 if ((other.changedState & Qt::ImCursorRectangle) && cursorRectangle != other.cursorRectangle) {
57 cursorRectangle = other.cursorRectangle;
58 queries |= Qt::ImCursorRectangle;
61 if ((other.changedState & Qt::ImSurroundingText) && surroundingText != other.surroundingText) {
62 surroundingText = other.surroundingText;
63 queries |= Qt::ImSurroundingText | Qt::ImCurrentSelection;
66 if ((other.changedState & Qt::ImCursorPosition) && cursorPosition != other.cursorPosition) {
68 queries |= Qt::ImCursorPosition | Qt::ImCurrentSelection;
71 if ((other.changedState & Qt::ImAnchorPosition) && anchorPosition != other.anchorPosition) {
73 queries |= Qt::ImAnchorPosition | Qt::ImCurrentSelection;
76 if ((other.changedState & Qt::ImPreferredLanguage) && preferredLanguage != other.preferredLanguage) {
77 preferredLanguage = other.preferredLanguage;
78 queries |= Qt::ImPreferredLanguage;
91void QWaylandTextInputPrivate::sendInputMethodEvent(QInputMethodEvent *event)
93 Q_Q(QWaylandTextInput);
95 if (!focusResource || !focusResource->handle)
98 QWaylandTextInputClientState afterCommit;
100 afterCommit.surroundingText = currentState->surroundingText;
101 afterCommit.cursorPosition = qMin(currentState->cursorPosition, currentState->anchorPosition);
104 afterCommit.surroundingText.remove(afterCommit.cursorPosition, qAbs(currentState->cursorPosition - currentState->anchorPosition));
106 if (event->replacementLength() > 0 || event->replacementStart() != 0) {
108 afterCommit.cursorPosition = qBound(0, afterCommit.cursorPosition + event->replacementStart(), afterCommit.surroundingText.size());
109 afterCommit.surroundingText.remove(afterCommit.cursorPosition,
110 qMin(event->replacementLength(),
111 afterCommit.surroundingText.size() - afterCommit.cursorPosition));
113 if (event->replacementStart() <= 0 && (event->replacementLength() >= -event->replacementStart())) {
114 const int selectionStart = qMin(currentState->cursorPosition, currentState->anchorPosition);
115 const int selectionEnd = qMax(currentState->cursorPosition, currentState->anchorPosition);
116 const int before = QWaylandInputMethodEventBuilder::indexToWayland(currentState->surroundingText, -event->replacementStart(), selectionStart + event->replacementStart());
117 const int after = QWaylandInputMethodEventBuilder::indexToWayland(currentState->surroundingText, event->replacementLength() + event->replacementStart(), selectionEnd);
118 send_delete_surrounding_text(focusResource->handle, before, after);
121 qWarning() <<
"Not yet supported case of replacement. Start:" << event->replacementStart() <<
"length:" << event->replacementLength();
126 afterCommit.surroundingText.insert(afterCommit.cursorPosition, event->commitString());
127 afterCommit.cursorPosition += event->commitString().size();
128 afterCommit.anchorPosition = afterCommit.cursorPosition;
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(afterCommit.surroundingText, qAbs(attribute.start - afterCommit.cursorPosition), qMin(attribute.start, afterCommit.cursorPosition));
135 int anchor = QWaylandInputMethodEventBuilder::indexToWayland(afterCommit.surroundingText, qAbs(attribute.length - afterCommit.cursorPosition), qMin(attribute.length, afterCommit.cursorPosition));
136 send_cursor_position(focusResource->handle,
137 attribute.start < afterCommit.cursorPosition ? -cursor : cursor,
138 attribute.length < afterCommit.cursorPosition ? -anchor : anchor);
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(event->preeditString(), attribute.start);
145 send_preedit_cursor(focusResource->handle, index);
146 }
else if (attribute.type == QInputMethodEvent::TextFormat) {
147 int start = QWaylandInputMethodEventBuilder::indexToWayland(event->preeditString(), attribute.start);
148 int length = QWaylandInputMethodEventBuilder::indexToWayland(event->preeditString(), attribute.length, attribute.start);
150 send_preedit_styling(focusResource->handle, start, length, preedit_style_default);
153 send_preedit_string(focusResource->handle, event->preeditString(), event->preeditString());
155 Qt::InputMethodQueries queries = currentState->updatedQueries(afterCommit);
156 currentState->surroundingText = afterCommit.surroundingText;
157 currentState->cursorPosition = afterCommit.cursorPosition;
158 currentState->anchorPosition = afterCommit.anchorPosition;
161 qCDebug(qLcWaylandCompositorInputMethods) <<
"QInputMethod::update() after QInputMethodEvent" << queries;
163 emit q->updateInputMethod(queries);
167void QWaylandTextInputPrivate::sendKeyEvent(QKeyEvent *event)
169 if (!focusResource || !focusResource->handle)
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;
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,
194void QWaylandTextInputPrivate::sendInputPanelState()
196 if (!focusResource || !focusResource->handle)
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(keyboardRect).translated(-sceneInputRect.topLeft());
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());
229QVariant QWaylandTextInputPrivate::inputMethodQuery(Qt::InputMethodQuery property, QVariant argument)
const
233 return QVariant(
static_cast<
int>(currentState->hints));
234 case Qt::ImCursorRectangle:
235 return currentState->cursorRectangle;
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(qMin(currentState->cursorPosition, currentState->anchorPosition),
245 qAbs(currentState->anchorPosition - currentState->cursorPosition));
246 case Qt::ImMaximumTextLength:
249 case Qt::ImAnchorPosition:
250 return currentState->anchorPosition;
251 case Qt::ImAbsolutePosition:
253 return currentState->cursorPosition;
254 case Qt::ImTextAfterCursor:
255 if (argument.isValid())
256 return currentState->surroundingText.mid(currentState->cursorPosition, argument.toInt());
257 return currentState->surroundingText.mid(currentState->cursorPosition);
258 case Qt::ImTextBeforeCursor:
259 if (argument.isValid())
260 return currentState->surroundingText.left(currentState->cursorPosition).right(argument.toInt());
261 return currentState->surroundingText.left(currentState->cursorPosition);
262 case Qt::ImPreferredLanguage:
263 return currentState->preferredLanguage;
270void QWaylandTextInputPrivate::setFocus(QWaylandSurface *surface)
272 Q_Q(QWaylandTextInput);
274 if (focusResource && focus != surface) {
275 uint32_t serial = compositor->nextSerial();
276 send_leave(focusResource->handle, serial, focus->resource());
277 focusDestroyListener.reset();
280 Resource *resource = surface ? resourceMap().value(surface->waylandClient()) : 0;
282 if (resource && (focus != surface || focusResource != resource)) {
283 uint32_t serial = compositor->nextSerial();
284 currentState.reset(
new QWaylandTextInputClientState);
285 pendingState.reset(
new QWaylandTextInputClientState);
286 send_enter(resource->handle, serial, surface->resource());
287 focusResource = resource;
288 sendInputPanelState();
291 focusDestroyListener.listenForDestruction(surface->resource());
292 if (inputPanelVisible && q->isSurfaceEnabled(surface))
293 qApp->inputMethod()->show();
296 focusResource = resource;
377void QWaylandTextInputPrivate::zwp_text_input_v2_update_state(Resource *resource, uint32_t serial, uint32_t flags)
379 Q_Q(QWaylandTextInput);
381 qCDebug(qLcWaylandCompositorInputMethods) <<
"update_state" << serial << flags;
383 if (resource != focusResource)
386 if (flags == update_state_reset || flags == update_state_enter) {
387 qCDebug(qLcWaylandCompositorInputMethods) <<
"QInputMethod::reset()";
388 qApp->inputMethod()->reset();
391 this->serial = serial;
393 Qt::InputMethodQueries queries;
394 if (flags == update_state_change) {
395 queries = currentState->mergeChanged(*pendingState);
397 queries = pendingState->updatedQueries(*currentState);
398 currentState.swap(pendingState);
401 pendingState.reset(
new QWaylandTextInputClientState);
404 qCDebug(qLcWaylandCompositorInputMethods) <<
"QInputMethod::update()" << queries;
406 emit q->updateInputMethod(queries);
410void QWaylandTextInputPrivate::zwp_text_input_v2_set_content_type(Resource *resource, uint32_t hint, uint32_t purpose)
412 if (resource != focusResource)
415 pendingState->hints = Qt::ImhNone;
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;
436 case content_purpose_normal:
438 case content_purpose_alpha:
439 pendingState->hints |= Qt::ImhUppercaseOnly | Qt::ImhLowercaseOnly;
441 case content_purpose_digits:
442 pendingState->hints |= Qt::ImhDigitsOnly;
444 case content_purpose_number:
445 pendingState->hints |= Qt::ImhFormattedNumbersOnly;
447 case content_purpose_phone:
448 pendingState->hints |= Qt::ImhDialableCharactersOnly;
450 case content_purpose_url:
451 pendingState->hints |= Qt::ImhUrlCharactersOnly;
453 case content_purpose_email:
454 pendingState->hints |= Qt::ImhEmailCharactersOnly;
456 case content_purpose_name:
457 case content_purpose_password:
459 case content_purpose_date:
460 pendingState->hints |= Qt::ImhDate;
462 case content_purpose_time:
463 pendingState->hints |= Qt::ImhTime;
465 case content_purpose_datetime:
466 pendingState->hints |= Qt::ImhDate | Qt::ImhTime;
468 case content_purpose_terminal:
473 pendingState->changedState |= Qt::ImHints;
499 : QWaylandCompositorExtensionTemplate(container, *
new QWaylandTextInputPrivate(compositor))
501 connect(&d_func()->focusDestroyListener, &QWaylandDestroyListener::fired,
502 this, &QWaylandTextInput::focusSurfaceDestroyed);
504 connect(qApp->inputMethod(), &QInputMethod::visibleChanged,
505 this, &QWaylandTextInput::sendInputPanelState);
506 connect(qApp->inputMethod(), &QInputMethod::keyboardRectangleChanged,
507 this, &QWaylandTextInput::sendInputPanelState);
508 connect(qApp->inputMethod(), &QInputMethod::inputDirectionChanged,
509 this, &QWaylandTextInput::sendTextDirection);
510 connect(qApp->inputMethod(), &QInputMethod::localeChanged,
511 this, &QWaylandTextInput::sendLocale);
611 Q_D(QWaylandTextInput);
613 const QList<QByteArray> modifiers = modifiersMap.split(
'\0');
615 int numModifiers = modifiers.size();
616 if (modifiers.last().isEmpty())
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;
630 qCDebug(qLcWaylandCompositorInputMethods) <<
"unsupported modifier name " << modString;
632 d->sendModifiersMap(modifiersMap);