50 Qt::InputMethodQueries queries;
52 if ((other.changedState & Qt::ImHints) && hints != other.hints) {
54 queries |= Qt::ImHints;
57 if ((other.changedState & Qt::ImCursorRectangle) && cursorRectangle != other.cursorRectangle) {
58 cursorRectangle = other.cursorRectangle;
59 queries |= Qt::ImCursorRectangle;
62 if ((other.changedState & Qt::ImSurroundingText) && surroundingText != other.surroundingText) {
63 surroundingText = other.surroundingText;
64 queries |= Qt::ImSurroundingText | Qt::ImCurrentSelection;
67 if ((other.changedState & Qt::ImCursorPosition) && cursorPosition != other.cursorPosition) {
69 queries |= Qt::ImCursorPosition | Qt::ImCurrentSelection;
72 if ((other.changedState & Qt::ImAnchorPosition) && anchorPosition != other.anchorPosition) {
74 queries |= Qt::ImAnchorPosition | Qt::ImCurrentSelection;
77 if ((other.changedState & Qt::ImPreferredLanguage) && preferredLanguage != other.preferredLanguage) {
78 preferredLanguage = other.preferredLanguage;
79 queries |= Qt::ImPreferredLanguage;
92void QWaylandTextInputPrivate::sendInputMethodEvent(QInputMethodEvent *event)
94 Q_Q(QWaylandTextInput);
96 if (!focusResource || !focusResource->handle)
99 QWaylandTextInputClientState afterCommit;
101 afterCommit.surroundingText = currentState->surroundingText;
102 afterCommit.cursorPosition = qMin(currentState->cursorPosition, currentState->anchorPosition);
105 afterCommit.surroundingText.remove(afterCommit.cursorPosition, qAbs(currentState->cursorPosition - currentState->anchorPosition));
107 if (event->replacementLength() > 0 || event->replacementStart() != 0) {
109 afterCommit.cursorPosition = qBound(0, afterCommit.cursorPosition + event->replacementStart(), afterCommit.surroundingText.size());
110 afterCommit.surroundingText.remove(afterCommit.cursorPosition,
111 qMin(event->replacementLength(),
112 afterCommit.surroundingText.size() - afterCommit.cursorPosition));
114 if (event->replacementStart() <= 0 && (event->replacementLength() >= -event->replacementStart())) {
115 const int selectionStart = qMin(currentState->cursorPosition, currentState->anchorPosition);
116 const int selectionEnd = qMax(currentState->cursorPosition, currentState->anchorPosition);
117 const int before = QWaylandInputMethodEventBuilder::indexToWayland(currentState->surroundingText, -event->replacementStart(), selectionStart + event->replacementStart());
118 const int after = QWaylandInputMethodEventBuilder::indexToWayland(currentState->surroundingText, event->replacementLength() + event->replacementStart(), selectionEnd);
119 send_delete_surrounding_text(focusResource->handle, before, after);
122 qWarning() <<
"Not yet supported case of replacement. Start:" << event->replacementStart() <<
"length:" << event->replacementLength();
127 afterCommit.surroundingText.insert(afterCommit.cursorPosition, event->commitString());
128 afterCommit.cursorPosition += event->commitString().size();
129 afterCommit.anchorPosition = afterCommit.cursorPosition;
131 for (
const QInputMethodEvent::Attribute &attribute : event->attributes()) {
132 if (attribute.type == QInputMethodEvent::Selection) {
133 afterCommit.cursorPosition = attribute.start;
134 afterCommit.anchorPosition = attribute.length;
135 int cursor = QWaylandInputMethodEventBuilder::indexToWayland(afterCommit.surroundingText, qAbs(attribute.start - afterCommit.cursorPosition), qMin(attribute.start, afterCommit.cursorPosition));
136 int anchor = QWaylandInputMethodEventBuilder::indexToWayland(afterCommit.surroundingText, qAbs(attribute.length - afterCommit.cursorPosition), qMin(attribute.length, afterCommit.cursorPosition));
137 send_cursor_position(focusResource->handle,
138 attribute.start < afterCommit.cursorPosition ? -cursor : cursor,
139 attribute.length < afterCommit.cursorPosition ? -anchor : anchor);
142 send_commit_string(focusResource->handle, event->commitString());
143 for (
const QInputMethodEvent::Attribute &attribute : event->attributes()) {
144 if (attribute.type == QInputMethodEvent::Cursor) {
145 int index = QWaylandInputMethodEventBuilder::indexToWayland(event->preeditString(), attribute.start);
146 send_preedit_cursor(focusResource->handle, index);
147 }
else if (attribute.type == QInputMethodEvent::TextFormat) {
148 int start = QWaylandInputMethodEventBuilder::indexToWayland(event->preeditString(), attribute.start);
149 int length = QWaylandInputMethodEventBuilder::indexToWayland(event->preeditString(), attribute.length, attribute.start);
151 send_preedit_styling(focusResource->handle, start, length, preedit_style_default);
154 send_preedit_string(focusResource->handle, event->preeditString(), event->preeditString());
156 Qt::InputMethodQueries queries = currentState->updatedQueries(afterCommit);
157 currentState->surroundingText = afterCommit.surroundingText;
158 currentState->cursorPosition = afterCommit.cursorPosition;
159 currentState->anchorPosition = afterCommit.anchorPosition;
162 qCDebug(qLcWaylandCompositorInputMethods) <<
"QInputMethod::update() after QInputMethodEvent" << queries;
164 emit q->updateInputMethod(queries);
168void QWaylandTextInputPrivate::sendKeyEvent(QKeyEvent *event)
170 if (!focusResource || !focusResource->handle)
174 const auto &qtMods = event->modifiers();
175 if (qtMods & Qt::ShiftModifier)
176 mods |= shiftModifierMask;
177 if (qtMods & Qt::ControlModifier)
178 mods |= controlModifierMask;
179 if (qtMods & Qt::AltModifier)
180 mods |= altModifierMask;
181 if (qtMods & Qt::MetaModifier)
182 mods |= metaModifierMask;
184#if QT_CONFIG(xkbcommon)
185 const auto keysyms = QXkbCommon::toKeysym(event);
186 for (xkb_keysym_t keysym : keysyms) {
187 send_keysym(focusResource->handle, event->timestamp(), keysym,
188 event->type() == QEvent::KeyPress ? WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED,
196void QWaylandTextInputPrivate::sendInputPanelState()
198 if (!focusResource || !focusResource->handle)
201 QInputMethod *inputMethod = qApp->inputMethod();
202 const QRectF& keyboardRect = inputMethod->keyboardRectangle();
203 const QRectF& sceneInputRect = inputMethod->inputItemTransform().mapRect(inputMethod->inputItemRectangle());
204 const QRectF& localRect = sceneInputRect.intersected(keyboardRect).translated(-sceneInputRect.topLeft());
206 send_input_panel_state(focusResource->handle,
207 inputMethod->isVisible() ? input_panel_visibility_visible : input_panel_visibility_hidden,
208 localRect.x(), localRect.y(), localRect.width(), localRect.height());
231QVariant QWaylandTextInputPrivate::inputMethodQuery(Qt::InputMethodQuery property, QVariant argument)
const
235 return QVariant(
static_cast<
int>(currentState->hints));
236 case Qt::ImCursorRectangle:
237 return currentState->cursorRectangle;
241 case Qt::ImCursorPosition:
242 return currentState->cursorPosition;
243 case Qt::ImSurroundingText:
244 return currentState->surroundingText;
245 case Qt::ImCurrentSelection:
246 return currentState->surroundingText.mid(qMin(currentState->cursorPosition, currentState->anchorPosition),
247 qAbs(currentState->anchorPosition - currentState->cursorPosition));
248 case Qt::ImMaximumTextLength:
251 case Qt::ImAnchorPosition:
252 return currentState->anchorPosition;
253 case Qt::ImAbsolutePosition:
255 return currentState->cursorPosition;
256 case Qt::ImTextAfterCursor:
257 if (argument.isValid())
258 return currentState->surroundingText.mid(currentState->cursorPosition, argument.toInt());
259 return currentState->surroundingText.mid(currentState->cursorPosition);
260 case Qt::ImTextBeforeCursor:
261 if (argument.isValid())
262 return currentState->surroundingText.left(currentState->cursorPosition).right(argument.toInt());
263 return currentState->surroundingText.left(currentState->cursorPosition);
264 case Qt::ImPreferredLanguage:
265 return currentState->preferredLanguage;
272void QWaylandTextInputPrivate::setFocus(QWaylandSurface *surface)
274 Q_Q(QWaylandTextInput);
276 if (focusResource && focus != surface) {
277 uint32_t serial = compositor->nextSerial();
278 send_leave(focusResource->handle, serial, focus->resource());
279 focusDestroyListener.reset();
282 Resource *resource = surface ? resourceMap().value(surface->waylandClient()) : 0;
284 if (resource && (focus != surface || focusResource != resource)) {
285 uint32_t serial = compositor->nextSerial();
286 currentState.reset(
new QWaylandTextInputClientState);
287 pendingState.reset(
new QWaylandTextInputClientState);
288 send_enter(resource->handle, serial, surface->resource());
289 focusResource = resource;
290 sendInputPanelState();
293 focusDestroyListener.listenForDestruction(surface->resource());
294 if (inputPanelVisible && q->isSurfaceEnabled(surface))
295 qApp->inputMethod()->show();
298 focusResource = resource;
381void QWaylandTextInputPrivate::zwp_text_input_v2_update_state(Resource *resource, uint32_t serial, uint32_t flags)
383 Q_Q(QWaylandTextInput);
385 qCDebug(qLcWaylandCompositorInputMethods) <<
"update_state" << serial << flags;
387 if (resource != focusResource)
390 if (flags == update_state_reset || flags == update_state_enter) {
391 qCDebug(qLcWaylandCompositorInputMethods) <<
"QInputMethod::reset()";
392 qApp->inputMethod()->reset();
395 this->serial = serial;
397 Qt::InputMethodQueries queries;
398 if (flags == update_state_change) {
399 queries = currentState->mergeChanged(*pendingState);
401 queries = pendingState->updatedQueries(*currentState);
402 currentState.swap(pendingState);
405 pendingState.reset(
new QWaylandTextInputClientState);
408 qCDebug(qLcWaylandCompositorInputMethods) <<
"QInputMethod::update()" << queries;
410 emit q->updateInputMethod(queries);
414void QWaylandTextInputPrivate::zwp_text_input_v2_set_content_type(Resource *resource, uint32_t hint, uint32_t purpose)
416 if (resource != focusResource)
419 pendingState->hints = Qt::ImhNone;
421 if ((hint & content_hint_auto_completion) == 0
422 && (hint & content_hint_auto_correction) == 0)
423 pendingState->hints |= Qt::ImhNoPredictiveText;
424 if ((hint & content_hint_auto_capitalization) == 0)
425 pendingState->hints |= Qt::ImhNoAutoUppercase;
426 if ((hint & content_hint_lowercase) != 0)
427 pendingState->hints |= Qt::ImhPreferLowercase;
428 if ((hint & content_hint_uppercase) != 0)
429 pendingState->hints |= Qt::ImhPreferUppercase;
430 if ((hint & content_hint_hidden_text) != 0)
431 pendingState->hints |= Qt::ImhHiddenText;
432 if ((hint & content_hint_sensitive_data) != 0)
433 pendingState->hints |= Qt::ImhSensitiveData;
434 if ((hint & content_hint_latin) != 0)
435 pendingState->hints |= Qt::ImhLatinOnly;
436 if ((hint & content_hint_multiline) != 0)
437 pendingState->hints |= Qt::ImhMultiLine;
440 case content_purpose_normal:
442 case content_purpose_alpha:
443 pendingState->hints |= Qt::ImhUppercaseOnly | Qt::ImhLowercaseOnly;
445 case content_purpose_digits:
446 pendingState->hints |= Qt::ImhDigitsOnly;
448 case content_purpose_number:
449 pendingState->hints |= Qt::ImhFormattedNumbersOnly;
451 case content_purpose_phone:
452 pendingState->hints |= Qt::ImhDialableCharactersOnly;
454 case content_purpose_url:
455 pendingState->hints |= Qt::ImhUrlCharactersOnly;
457 case content_purpose_email:
458 pendingState->hints |= Qt::ImhEmailCharactersOnly;
460 case content_purpose_name:
461 case content_purpose_password:
463 case content_purpose_date:
464 pendingState->hints |= Qt::ImhDate;
466 case content_purpose_time:
467 pendingState->hints |= Qt::ImhTime;
469 case content_purpose_datetime:
470 pendingState->hints |= Qt::ImhDate | Qt::ImhTime;
472 case content_purpose_terminal:
477 pendingState->changedState |= Qt::ImHints;
503 : QWaylandCompositorExtensionTemplate(container, *
new QWaylandTextInputPrivate(compositor))
505 connect(&d_func()->focusDestroyListener, &QWaylandDestroyListener::fired,
508 connect(qApp->inputMethod(), &QInputMethod::visibleChanged,
510 connect(qApp->inputMethod(), &QInputMethod::keyboardRectangleChanged,
512 connect(qApp->inputMethod(), &QInputMethod::inputDirectionChanged,
514 connect(qApp->inputMethod(), &QInputMethod::localeChanged,
615 Q_D(QWaylandTextInput);
617 const QList<QByteArray> modifiers = modifiersMap.split(
'\0');
619 int numModifiers = modifiers.size();
620 if (modifiers.last().isEmpty())
623 for (
int i = 0; i < numModifiers; ++i) {
624 const auto modString = modifiers.at(i);
625 if (modString == XKB_MOD_NAME_SHIFT)
626 d->shiftModifierMask = 1 << i;
627 else if (modString == XKB_MOD_NAME_CTRL)
628 d->controlModifierMask = 1 << i;
629 else if (modString == XKB_MOD_NAME_ALT)
630 d->altModifierMask = 1 << i;
631 else if (modString == XKB_MOD_NAME_LOGO)
632 d->metaModifierMask = 1 << i;
634 qCDebug(qLcWaylandCompositorInputMethods) <<
"unsupported modifier name " << modString;
636 d->sendModifiersMap(modifiersMap);