Qt 6.x
The Qt SDK
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
qwaylandtextinputv4.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
6
7#include <QtWaylandCompositor/QWaylandCompositor>
8#include <QtWaylandCompositor/private/qwaylandseat_p.h>
9
10#include "qwaylandsurface.h"
11#include "qwaylandview.h"
13
14#include <QGuiApplication>
15#include <QInputMethodEvent>
16#include <qpa/qwindowsysteminterface.h>
17
18#if QT_CONFIG(xkbcommon)
19#include <QtGui/private/qxkbcommon_p.h>
20#endif
21
23
24Q_DECLARE_LOGGING_CATEGORY(qLcWaylandCompositorTextInput)
25
27{
28}
29
31{
32 qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO;
33
34 Qt::InputMethodQueries queries;
35
36 if (hints != other.hints)
37 queries |= Qt::ImHints;
38 if (cursorRectangle != other.cursorRectangle)
39 queries |= Qt::ImCursorRectangle;
40 if (surroundingText != other.surroundingText)
42 if (cursorPosition != other.cursorPosition)
44 if (anchorPosition != other.anchorPosition)
46
47 return queries;
48}
49
51
52 Qt::InputMethodQueries queries;
53
54 if ((other.changedState & Qt::ImHints) && hints != other.hints) {
55 hints = other.hints;
56 queries |= Qt::ImHints;
57 }
58
59 if ((other.changedState & Qt::ImCursorRectangle) && cursorRectangle != other.cursorRectangle) {
60 cursorRectangle = other.cursorRectangle;
61 queries |= Qt::ImCursorRectangle;
62 }
63
64 if ((other.changedState & Qt::ImSurroundingText) && surroundingText != other.surroundingText) {
65 surroundingText = other.surroundingText;
67 }
68
69 if ((other.changedState & Qt::ImCursorPosition) && cursorPosition != other.cursorPosition) {
70 cursorPosition = other.cursorPosition;
72 }
73
74 if ((other.changedState & Qt::ImAnchorPosition) && anchorPosition != other.anchorPosition) {
75 anchorPosition = other.anchorPosition;
77 }
78
79 return queries;
80}
81
84 , currentState(new QWaylandTextInputV4ClientState)
85 , pendingState(new QWaylandTextInputV4ClientState)
86{
87}
88
90{
92 qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO;
93
94 if (!focusResource || !focusResource->handle)
95 return;
96
97 bool needsDone = false;
98
99 const QString &newPreeditString = event->preeditString();
100
101 // Current cursor shape is only line. It means both cursorBegin
102 // and cursorEnd will be the same values.
103 int32_t preeditCursorPos = newPreeditString.length();
104
105 if (event->replacementLength() > 0 || event->replacementStart() < 0) {
106 if (event->replacementStart() <= 0 && (event->replacementLength() >= -event->replacementStart())) {
107 const int selectionStart = qMin(currentState->cursorPosition, currentState->anchorPosition);
108 const int selectionEnd = qMax(currentState->cursorPosition, currentState->anchorPosition);
109 const int before = QWaylandInputMethodEventBuilder::indexToWayland(currentState->surroundingText, -event->replacementStart(), selectionStart + event->replacementStart());
110 const int after = QWaylandInputMethodEventBuilder::indexToWayland(currentState->surroundingText, event->replacementLength() + event->replacementStart(), selectionEnd);
111 send_delete_surrounding_text(focusResource->handle, before, after);
112 needsDone = true;
113 } else {
114 qCWarning(qLcWaylandCompositorTextInput) << "Not yet supported case of replacement. Start:" << event->replacementStart() << "length:" << event->replacementLength();
115 }
116 preeditCursorPos = event->replacementStart() + event->replacementLength();
117 }
118
119 if (currentPreeditString != newPreeditString) {
120 currentPreeditString = newPreeditString;
121 send_preedit_string(focusResource->handle, currentPreeditString, preeditCursorPos, preeditCursorPos);
122 needsDone = true;
123 }
124 if (!event->commitString().isEmpty()) {
125 send_commit_string(focusResource->handle, event->commitString());
126 needsDone = true;
127 }
128
129 if (needsDone)
130 send_done(focusResource->handle, serial);
131}
132
133
135{
136 qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO;
137
139
140 if (!focusResource || !focusResource->handle)
141 return;
142
143 send_commit_string(focusResource->handle, event->text());
144
145 send_done(focusResource->handle, serial);
146}
147
149{
150 qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO << property;
151
152 switch (property) {
153 case Qt::ImHints:
154 return QVariant(static_cast<int>(currentState->hints));
157 case Qt::ImFont:
158 // Not supported
159 return QVariant();
161 qCDebug(qLcWaylandCompositorTextInput) << currentState->cursorPosition;
164 qCDebug(qLcWaylandCompositorTextInput) << currentState->surroundingText;
170 // Not supported
171 return QVariant();
173 qCDebug(qLcWaylandCompositorTextInput) << currentState->anchorPosition;
176 // We assume the surrounding text is our whole document for now
179 if (argument.isValid())
183 if (argument.isValid())
186
187 default:
188 return QVariant();
189 }
190}
191
193{
194 qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO;
196
197 if (focusResource && focus) {
198 // sync before leave
199 // IBUS commits by itself but qtvirtualkeyboard doesn't
200 // And when handling chinese input, it is required to commit
201 // before leaving the focus.
202 if (qgetenv("QT_IM_MODULE") != QByteArrayLiteral("ibus")
203 || qApp->inputMethod()->locale().language() == QLocale::Chinese) {
204 qApp->inputMethod()->commit();
205 }
206
207 qApp->inputMethod()->hide();
208 inputPanelVisible = false;
209 send_leave(focusResource->handle, focus->resource());
211 }
212
213 if (focus != surface)
215
216 Resource *resource = surface ? resourceMap().value(surface->waylandClient()) : 0;
217 if (resource && surface) {
218 send_enter(resource->handle, surface->resource());
219
220 if (focus != surface)
222 }
223
224 focus = surface;
225 focusResource = resource;
226}
227
229{
230 qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO;
231
232 Q_UNUSED(resource);
233}
234
236{
237 qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO;
238
239 if (focusResource == resource)
240 focusResource = nullptr;
241}
242
244{
245 qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO;
246
247 wl_resource_destroy(resource->handle);
248}
249
251{
252 qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO;
253
255
257
258 enabledSurfaces.insert(resource, focus);
259 emit q->surfaceEnabled(focus);
260
261 serial = 0;
262 inputPanelVisible = true;
263 qApp->inputMethod()->show();
264}
265
266void QWaylandTextInputV4Private::zwp_text_input_v4_disable(QtWaylandServer::zwp_text_input_v4::Resource *resource)
267{
268 qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO;
269
271
273 emit q->surfaceDisabled(s);
274
275 // When reselecting a word by setFocus
276 if (qgetenv("QT_IM_MODULE") != QByteArrayLiteral("ibus")
277 || qApp->inputMethod()->locale().language() == QLocale::Chinese) {
278 qApp->inputMethod()->commit();
279 }
280 qApp->inputMethod()->reset();
282}
283
284void QWaylandTextInputV4Private::zwp_text_input_v4_set_cursor_rectangle(Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height)
285{
286 qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO << x << y << width << height;
287
289
290 if (resource != focusResource)
291 return;
292
294
296}
297
299{
300 qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO;
301
303
304 if (resource != focusResource) {
305 qCDebug(qLcWaylandCompositorTextInput) << "OBS: Disabled surface!!";
306 return;
307 }
308
309 serial = serial < UINT_MAX ? serial + 1U : 0U;
310
311 // Just increase serials and ignore empty commits
313 qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO << "pendingState is not changed";
314 return;
315 }
316
317 // Selection starts.
318 // But since qtvirtualkeyboard with hunspell does not reset its preedit string,
319 // compositor forces to reset inputMethod.
322 qApp->inputMethod()->reset();
323
324 // Enable reselection
325 // This is a workaround to make qtvirtualkeyboad's state empty by clearing State::InputMethodClick.
327 qApp->inputMethod()->invokeAction(QInputMethod::Click, pendingState->cursorPosition);
328
329 Qt::InputMethodQueries queries = currentState->mergeChanged(*pendingState.data());
331
332 if (queries) {
333 qCDebug(qLcWaylandCompositorTextInput) << "QInputMethod::update() after commit with" << queries;
334
335 qApp->inputMethod()->update(queries);
336 }
337}
338
339void QWaylandTextInputV4Private::zwp_text_input_v4_set_content_type(Resource *resource, uint32_t hint, uint32_t purpose)
340{
341 qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO << hint << purpose;
342
343 if (resource != focusResource)
344 return;
345
347
348 if ((hint & content_hint_completion) == 0)
350 if ((hint & content_hint_auto_capitalization) == 0)
352 if ((hint & content_hint_lowercase) != 0)
354 if ((hint & content_hint_uppercase) != 0)
356 if ((hint & content_hint_hidden_text) != 0)
358 if ((hint & content_hint_sensitive_data) != 0)
360 if ((hint & content_hint_latin) != 0)
362 if ((hint & content_hint_multiline) != 0)
364
365 switch (purpose) {
366 case content_purpose_normal:
367 break;
368 case content_purpose_alpha:
370 break;
371 case content_purpose_digits:
373 break;
374 case content_purpose_number:
376 break;
377 case content_purpose_phone:
379 break;
380 case content_purpose_url:
382 break;
383 case content_purpose_email:
385 break;
386 case content_purpose_name:
387 case content_purpose_password:
388 break;
389 case content_purpose_date:
391 break;
392 case content_purpose_time:
394 break;
395 case content_purpose_datetime:
397 break;
398 case content_purpose_terminal:
399 default:
400 break;
401 }
402
403 qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO << pendingState->hints;
404
406}
407
408void QWaylandTextInputV4Private::zwp_text_input_v4_set_surrounding_text(Resource *resource, const QString &text, int32_t cursor, int32_t anchor)
409{
410 qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO << text << cursor << anchor;
411
412 if (resource != focusResource)
413 return;
414
418
420}
421
423{
424 qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO;
425
426 Q_UNUSED(resource);
427 Q_UNUSED(cause);
428}
429
432{
433 connect(&d_func()->focusDestroyListener, &QWaylandDestroyListener::fired,
434 this, &QWaylandTextInputV4::focusSurfaceDestroyed);
435}
436
438{
439}
440
442{
444
445 d->sendInputMethodEvent(event);
446}
447
449{
451
452 d->sendKeyEvent(event);
453}
454
456{
457 const Q_D(QWaylandTextInputV4);
458
459 return d->inputMethodQuery(property, argument);
460}
461
463{
464 const Q_D(QWaylandTextInputV4);
465
466 return d->focus;
467}
468
470{
472
473 d->setFocus(surface);
474}
475
476void QWaylandTextInputV4::focusSurfaceDestroyed(void *)
477{
478 qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO;
479
481
482 d->focusDestroyListener.reset();
483
484 d->focus = nullptr;
485 d->focusResource = nullptr;
486}
487
489{
490 qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO;
491
492 const Q_D(QWaylandTextInputV4);
493
494 return d->enabledSurfaces.values().contains(surface);
495}
496
497void QWaylandTextInputV4::add(::wl_client *client, uint32_t id, int version)
498{
499 qCDebug(qLcWaylandCompositorTextInput) << Q_FUNC_INFO;
500
502
503 d->add(client, id, version);
504}
505
507{
508 return QWaylandTextInputV4Private::interface();
509}
510
512{
513 return QWaylandTextInputV4Private::interfaceName();
514}
515
\inmodule QtCore
Definition qbytearray.h:57
T take(const Key &key)
Removes the item with the key from the hash and returns the value associated with it.
Definition qhash.h:975
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1283
The QInputMethodEvent class provides parameters for input method events.
Definition qevent.h:624
The QKeyEvent class describes a key event.
Definition qevent.h:423
@ Chinese
Definition qlocale.h:100
\inmodule QtCore\reentrant
Definition qrect.h:30
T * data() const noexcept
Returns the value of the pointer referenced by this object.
void reset(T *other=nullptr) noexcept(noexcept(Cleanup::cleanup(std::declval< T * >())))
Deletes the existing object it is pointing to (if any), and sets its pointer to other.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
QString right(qsizetype n) const
Returns a substring that contains the n rightmost characters of the string.
Definition qstring.cpp:5180
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1107
QString mid(qsizetype position, qsizetype n=-1) const
Returns a string that contains n characters of this string, starting at the specified position index.
Definition qstring.cpp:5204
QString left(qsizetype n) const
Returns a substring that contains the n leftmost characters of the string.
Definition qstring.cpp:5161
qsizetype length() const
Returns the number of characters in this string.
Definition qstring.h:187
\inmodule QtCore
Definition qvariant.h:64
\qmltype WaylandCompositor \instantiates QWaylandCompositor \inqmlmodule QtWayland....
void fired(void *data)
void listenForDestruction(struct wl_resource *resource)
static int indexFromWayland(const QString &text, int length, int base=0)
static int indexToWayland(const QString &text, int length, int base=0)
\inmodule QtWaylandCompositor
\qmltype WaylandSurface \instantiates QWaylandSurface \inqmlmodule QtWayland.Compositor
struct wl_resource * resource() const
Returns the Wayland resource corresponding to this QWaylandSurface.
::wl_client * waylandClient() const
Holds the wl_client using this QWaylandSurface.
Qt::InputMethodQueries mergeChanged(const QWaylandTextInputV4ClientState &other)
Qt::InputMethodQueries updatedQueries(const QWaylandTextInputV4ClientState &other) const
QScopedPointer< QWaylandTextInputV4ClientState > pendingState
void zwp_text_input_v4_disable(Resource *resource) override
void zwp_text_input_v4_set_content_type(Resource *resource, uint32_t hint, uint32_t purpose) override
void zwp_text_input_v4_set_text_change_cause(Resource *resource, uint32_t cause) override
QHash< Resource *, QWaylandSurface * > enabledSurfaces
void sendKeyEvent(QKeyEvent *event)
QScopedPointer< QWaylandTextInputV4ClientState > currentState
void zwp_text_input_v4_destroy(Resource *resource) override
void zwp_text_input_v4_destroy_resource(Resource *resource) override
void setFocus(QWaylandSurface *surface)
void zwp_text_input_v4_commit(Resource *resource) override
QVariant inputMethodQuery(Qt::InputMethodQuery property, QVariant argument) const
void zwp_text_input_v4_enable(Resource *resource) override
void zwp_text_input_v4_set_cursor_rectangle(Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) override
QWaylandDestroyListener focusDestroyListener
QWaylandTextInputV4Private(QWaylandCompositor *compositor)
void zwp_text_input_v4_set_surrounding_text(Resource *resource, const QString &text, int32_t cursor, int32_t anchor) override
void sendInputMethodEvent(QInputMethodEvent *event)
void zwp_text_input_v4_bind_resource(Resource *resource) override
void sendKeyEvent(QKeyEvent *event)
void setFocus(QWaylandSurface *surface)
static QByteArray interfaceName()
QWaylandSurface * focus() const
void add(::wl_client *client, uint32_t id, int version)
void sendInputMethodEvent(QInputMethodEvent *event)
QWaylandTextInputV4(QWaylandObject *container, QWaylandCompositor *compositor)
bool isSurfaceEnabled(QWaylandSurface *surface) const
static const struct wl_interface * interface()
QVariant inputMethodQuery(Qt::InputMethodQuery property, QVariant argument) const
QString text
QCursor cursor
Combined button and popup list for selecting options.
InputMethodQuery
@ ImMaximumTextLength
@ ImTextBeforeCursor
@ ImSurroundingText
@ ImCursorPosition
@ ImCurrentSelection
@ ImAbsolutePosition
@ ImFont
@ ImAnchorPosition
@ ImCursorRectangle
@ ImHints
@ ImTextAfterCursor
@ ImhPreferUppercase
@ ImhUrlCharactersOnly
@ ImhPreferLowercase
@ ImhTime
@ ImhFormattedNumbersOnly
@ ImhNone
@ ImhMultiLine
@ ImhLatinOnly
@ ImhUppercaseOnly
@ ImhNoPredictiveText
@ ImhDigitsOnly
@ ImhSensitiveData
@ ImhLowercaseOnly
@ ImhEmailCharactersOnly
@ ImhHiddenText
@ ImhNoAutoUppercase
@ ImhDialableCharactersOnly
@ ImhDate
#define QByteArrayLiteral(str)
Definition qbytearray.h:52
#define Q_FUNC_INFO
#define qApp
#define qCWarning(category,...)
#define qCDebug(category,...)
#define Q_DECLARE_LOGGING_CATEGORY(name)
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
constexpr T qAbs(const T &t)
Definition qnumeric.h:328
static QOpenGLCompositor * compositor
GLint GLint GLint GLint GLint x
[0]
GLint GLsizei GLsizei height
GLint GLsizei width
GLint y
struct _cl_event * event
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLdouble s
[6]
Definition qopenglext.h:235
static QT_BEGIN_NAMESPACE QVariant hint(QPlatformIntegration::StyleHint h)
Q_CORE_EXPORT QByteArray qgetenv(const char *varName)
#define emit
#define Q_UNUSED(x)
const char property[13]
Definition qwizard.cpp:101
connect(quitButton, &QPushButton::clicked, &app, &QCoreApplication::quit, Qt::QueuedConnection)
QSharedPointer< T > other(t)
[5]
QDBusArgument argument