1/*
2 SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5*/
6#include "event_queue.h"
7#include "seat.h"
8#include "surface.h"
9#include "textinput_p.h"
10#include "wayland_pointer_p.h"
11
12#include <wayland-text-input-v2-client-protocol.h>
13
14namespace KWayland
15{
16namespace Client
17{
18class TextInputUnstableV2::Private : public TextInput::Private
19{
20public:
21 Private(TextInputUnstableV2 *q, Seat *seat);
22
23 void setup(zwp_text_input_v2 *textinputmanagerunstablev0);
24
25 bool isValid() const override;
26 void enable(Surface *surface) override;
27 void disable(Surface *surface) override;
28 void showInputPanel() override;
29 void hideInputPanel() override;
30 void setCursorRectangle(const QRect &rect) override;
31 void setPreferredLanguage(const QString &lang) override;
32 void setSurroundingText(const QString &text, quint32 cursor, quint32 anchor) override;
33 void reset() override;
34 void setContentType(ContentHints hint, ContentPurpose purpose) override;
35
36 WaylandPointer<zwp_text_input_v2, zwp_text_input_v2_destroy> textinputunstablev2;
37
38private:
39 static void enterCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, uint32_t serial, wl_surface *surface);
40 static void leaveCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, uint32_t serial, wl_surface *surface);
41 static void inputPanelStateCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, uint32_t state, int32_t x, int32_t y, int32_t width, int32_t height);
42 static void preeditStringCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, const char *text, const char *commit);
43 static void preeditStylingCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, uint32_t index, uint32_t length, uint32_t style);
44 static void preeditCursorCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, int32_t index);
45 static void commitStringCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, const char *text);
46 static void cursorPositionCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, int32_t index, int32_t anchor);
47 static void deleteSurroundingTextCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, uint32_t before_length, uint32_t after_length);
48 static void modifiersMapCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, wl_array *map);
49 static void keysymCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, uint32_t time, uint32_t sym, uint32_t state, uint32_t modifiers);
50 static void languageCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, const char *language);
51 static void textDirectionCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, uint32_t direction);
52 static void configureSurroundingTextCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, int32_t before_cursor, int32_t after_cursor);
53 static void inputMethodChangedCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, uint32_t serial, uint32_t flags);
54
55 TextInputUnstableV2 *q;
56
57 static const zwp_text_input_v2_listener s_listener;
58};
59
60const zwp_text_input_v2_listener TextInputUnstableV2::Private::s_listener = {.enter: enterCallback,
61 .leave: leaveCallback,
62 .input_panel_state: inputPanelStateCallback,
63 .preedit_string: preeditStringCallback,
64 .preedit_styling: preeditStylingCallback,
65 .preedit_cursor: preeditCursorCallback,
66 .commit_string: commitStringCallback,
67 .cursor_position: cursorPositionCallback,
68 .delete_surrounding_text: deleteSurroundingTextCallback,
69 .modifiers_map: modifiersMapCallback,
70 .keysym: keysymCallback,
71 .language: languageCallback,
72 .text_direction: textDirectionCallback,
73 .configure_surrounding_text: configureSurroundingTextCallback,
74 .input_method_changed: inputMethodChangedCallback};
75
76void TextInputUnstableV2::Private::enterCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, uint32_t serial, wl_surface *surface)
77{
78 auto t = reinterpret_cast<TextInputUnstableV2::Private *>(data);
79 Q_ASSERT(t->textinputunstablev2 == zwp_text_input_v2);
80 t->latestSerial = serial;
81 t->enteredSurface = Surface::get(native: surface);
82 Q_EMIT t->q->entered();
83}
84
85void TextInputUnstableV2::Private::leaveCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, uint32_t serial, wl_surface *surface)
86{
87 Q_UNUSED(surface)
88 auto t = reinterpret_cast<TextInputUnstableV2::Private *>(data);
89 Q_ASSERT(t->textinputunstablev2 == zwp_text_input_v2);
90 t->enteredSurface = nullptr;
91 t->latestSerial = serial;
92 Q_EMIT t->q->left();
93}
94
95void TextInputUnstableV2::Private::inputPanelStateCallback(void *data,
96 zwp_text_input_v2 *zwp_text_input_v2,
97 uint32_t state,
98 int32_t x,
99 int32_t y,
100 int32_t width,
101 int32_t height)
102{
103 Q_UNUSED(x)
104 Q_UNUSED(y)
105 Q_UNUSED(width)
106 Q_UNUSED(height)
107 auto t = reinterpret_cast<TextInputUnstableV2::Private *>(data);
108 Q_ASSERT(t->textinputunstablev2 == zwp_text_input_v2);
109 // TODO: add rect
110 if (t->inputPanelVisible != state) {
111 t->inputPanelVisible = state;
112 Q_EMIT t->q->inputPanelStateChanged();
113 }
114}
115
116void TextInputUnstableV2::Private::preeditStringCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, const char *text, const char *commit)
117{
118 auto t = reinterpret_cast<TextInputUnstableV2::Private *>(data);
119 Q_ASSERT(t->textinputunstablev2 == zwp_text_input_v2);
120 t->pendingPreEdit.commitText = QByteArray(commit);
121 t->pendingPreEdit.text = QByteArray(text);
122 if (!t->pendingPreEdit.cursorSet) {
123 t->pendingPreEdit.cursor = t->pendingPreEdit.text.length();
124 }
125 t->currentPreEdit = t->pendingPreEdit;
126 t->pendingPreEdit = TextInput::Private::PreEdit();
127 Q_EMIT t->q->composingTextChanged();
128}
129
130void TextInputUnstableV2::Private::preeditStylingCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, uint32_t index, uint32_t length, uint32_t style)
131{
132 Q_UNUSED(index)
133 Q_UNUSED(length)
134 Q_UNUSED(style)
135 // TODO: implement
136 auto t = reinterpret_cast<TextInputUnstableV2::Private *>(data);
137 Q_ASSERT(t->textinputunstablev2 == zwp_text_input_v2);
138}
139
140void TextInputUnstableV2::Private::preeditCursorCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, int32_t index)
141{
142 auto t = reinterpret_cast<TextInputUnstableV2::Private *>(data);
143 Q_ASSERT(t->textinputunstablev2 == zwp_text_input_v2);
144 t->pendingPreEdit.cursor = index;
145 t->pendingPreEdit.cursorSet = true;
146}
147
148void TextInputUnstableV2::Private::commitStringCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, const char *text)
149{
150 auto t = reinterpret_cast<TextInputUnstableV2::Private *>(data);
151 Q_ASSERT(t->textinputunstablev2 == zwp_text_input_v2);
152 t->pendingCommit.text = QByteArray(text);
153 t->currentCommit = t->pendingCommit;
154 // TODO: what are the proper values it should be set to?
155 t->pendingCommit = TextInput::Private::Commit();
156 t->pendingCommit.deleteSurrounding.beforeLength = 0;
157 t->pendingCommit.deleteSurrounding.afterLength = 0;
158 Q_EMIT t->q->committed();
159}
160
161void TextInputUnstableV2::Private::cursorPositionCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, int32_t index, int32_t anchor)
162{
163 auto t = reinterpret_cast<TextInputUnstableV2::Private *>(data);
164 Q_ASSERT(t->textinputunstablev2 == zwp_text_input_v2);
165 t->pendingCommit.cursor = index;
166 t->pendingCommit.anchor = anchor;
167}
168
169void TextInputUnstableV2::Private::deleteSurroundingTextCallback(void *data,
170 zwp_text_input_v2 *zwp_text_input_v2,
171 uint32_t before_length,
172 uint32_t after_length)
173{
174 auto t = reinterpret_cast<TextInputUnstableV2::Private *>(data);
175 Q_ASSERT(t->textinputunstablev2 == zwp_text_input_v2);
176 t->pendingCommit.deleteSurrounding.beforeLength = before_length;
177 t->pendingCommit.deleteSurrounding.afterLength = after_length;
178}
179
180void TextInputUnstableV2::Private::modifiersMapCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, wl_array *map)
181{
182 // TODO: implement
183 Q_UNUSED(map)
184 auto t = reinterpret_cast<TextInputUnstableV2::Private *>(data);
185 Q_ASSERT(t->textinputunstablev2 == zwp_text_input_v2);
186}
187
188void TextInputUnstableV2::Private::keysymCallback(void *data,
189 zwp_text_input_v2 *zwp_text_input_v2,
190 uint32_t time,
191 uint32_t sym,
192 uint32_t wlState,
193 uint32_t modifiers)
194{
195 // TODO: add support for modifiers
196 Q_UNUSED(modifiers)
197 auto t = reinterpret_cast<TextInputUnstableV2::Private *>(data);
198 Q_ASSERT(t->textinputunstablev2 == zwp_text_input_v2);
199 TextInput::KeyState state;
200 switch (wlState) {
201 case WL_KEYBOARD_KEY_STATE_RELEASED:
202 state = TextInput::KeyState::Released;
203 break;
204 case WL_KEYBOARD_KEY_STATE_PRESSED:
205 state = TextInput::KeyState::Pressed;
206 break;
207 default:
208 // invalid
209 return;
210 }
211 Q_EMIT t->q->keyEvent(xkbKeySym: sym, state, modifiers: Qt::KeyboardModifiers(), time);
212}
213
214void TextInputUnstableV2::Private::languageCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, const char *language)
215{
216 auto t = reinterpret_cast<TextInputUnstableV2::Private *>(data);
217 Q_ASSERT(t->textinputunstablev2 == zwp_text_input_v2);
218 if (qstrcmp(str1: t->language, str2: language) != 0) {
219 t->language = QByteArray(language);
220 Q_EMIT t->q->languageChanged();
221 }
222}
223
224void TextInputUnstableV2::Private::textDirectionCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, uint32_t wlDirection)
225{
226 auto t = reinterpret_cast<TextInputUnstableV2::Private *>(data);
227 Q_ASSERT(t->textinputunstablev2 == zwp_text_input_v2);
228 Qt::LayoutDirection direction;
229 switch (wlDirection) {
230 case ZWP_TEXT_INPUT_V2_TEXT_DIRECTION_LTR:
231 direction = Qt::LeftToRight;
232 break;
233 case ZWP_TEXT_INPUT_V2_TEXT_DIRECTION_RTL:
234 direction = Qt::RightToLeft;
235 break;
236 case ZWP_TEXT_INPUT_V2_TEXT_DIRECTION_AUTO:
237 direction = Qt::LayoutDirectionAuto;
238 break;
239 default:
240 // invalid
241 return;
242 }
243 if (direction != t->textDirection) {
244 t->textDirection = direction;
245 Q_EMIT t->q->textDirectionChanged();
246 }
247}
248
249void TextInputUnstableV2::Private::configureSurroundingTextCallback(void *data,
250 zwp_text_input_v2 *zwp_text_input_v2,
251 int32_t before_cursor,
252 int32_t after_cursor)
253{
254 // TODO: implement
255 Q_UNUSED(before_cursor)
256 Q_UNUSED(after_cursor)
257 auto t = reinterpret_cast<TextInputUnstableV2::Private *>(data);
258 Q_ASSERT(t->textinputunstablev2 == zwp_text_input_v2);
259}
260
261void TextInputUnstableV2::Private::inputMethodChangedCallback(void *data, zwp_text_input_v2 *zwp_text_input_v2, uint32_t serial, uint32_t flags)
262{
263 Q_UNUSED(serial)
264 Q_UNUSED(flags)
265 // TODO: implement
266 auto t = reinterpret_cast<TextInputUnstableV2::Private *>(data);
267 Q_ASSERT(t->textinputunstablev2 == zwp_text_input_v2);
268}
269
270TextInputUnstableV2::Private::Private(TextInputUnstableV2 *q, Seat *seat)
271 : TextInput::Private(seat)
272 , q(q)
273{
274}
275
276void TextInputUnstableV2::Private::setup(zwp_text_input_v2 *ti)
277{
278 Q_ASSERT(ti);
279 Q_ASSERT(!textinputunstablev2);
280 textinputunstablev2.setup(pointer: ti);
281 zwp_text_input_v2_add_listener(zwp_text_input_v2: ti, listener: &s_listener, data: this);
282}
283
284bool TextInputUnstableV2::Private::isValid() const
285{
286 return textinputunstablev2.isValid();
287}
288
289void TextInputUnstableV2::Private::enable(Surface *surface)
290{
291 zwp_text_input_v2_enable(zwp_text_input_v2: textinputunstablev2, surface: *surface);
292}
293
294void TextInputUnstableV2::Private::disable(Surface *surface)
295{
296 zwp_text_input_v2_disable(zwp_text_input_v2: textinputunstablev2, surface: *surface);
297}
298
299void TextInputUnstableV2::Private::showInputPanel()
300{
301 zwp_text_input_v2_show_input_panel(zwp_text_input_v2: textinputunstablev2);
302}
303
304void TextInputUnstableV2::Private::hideInputPanel()
305{
306 zwp_text_input_v2_hide_input_panel(zwp_text_input_v2: textinputunstablev2);
307}
308
309void TextInputUnstableV2::Private::setCursorRectangle(const QRect &rect)
310{
311 zwp_text_input_v2_set_cursor_rectangle(zwp_text_input_v2: textinputunstablev2, x: rect.x(), y: rect.y(), width: rect.width(), height: rect.height());
312}
313
314void TextInputUnstableV2::Private::setPreferredLanguage(const QString &lang)
315{
316 zwp_text_input_v2_set_preferred_language(zwp_text_input_v2: textinputunstablev2, language: lang.toUtf8().constData());
317}
318
319void TextInputUnstableV2::Private::setSurroundingText(const QString &text, quint32 cursor, quint32 anchor)
320{
321 const QStringView strView(text);
322
323 zwp_text_input_v2_set_surrounding_text(zwp_text_input_v2: textinputunstablev2,
324 text: text.toUtf8().constData(),
325 cursor: strView.left(n: cursor).toUtf8().length(),
326 anchor: strView.left(n: anchor).toUtf8().length());
327}
328
329void TextInputUnstableV2::Private::reset()
330{
331 zwp_text_input_v2_update_state(zwp_text_input_v2: textinputunstablev2, serial: latestSerial, reason: ZWP_TEXT_INPUT_V2_UPDATE_STATE_RESET);
332}
333
334void TextInputUnstableV2::Private::setContentType(ContentHints hints, ContentPurpose purpose)
335{
336 uint32_t wlHints = 0;
337 uint32_t wlPurpose = 0;
338 if (hints.testFlag(flag: ContentHint::AutoCompletion)) {
339 wlHints |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_AUTO_COMPLETION;
340 }
341 if (hints.testFlag(flag: ContentHint::AutoCorrection)) {
342 wlHints |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_AUTO_CORRECTION;
343 }
344 if (hints.testFlag(flag: ContentHint::AutoCapitalization)) {
345 wlHints |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_AUTO_CAPITALIZATION;
346 }
347 if (hints.testFlag(flag: ContentHint::LowerCase)) {
348 wlHints |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_LOWERCASE;
349 }
350 if (hints.testFlag(flag: ContentHint::UpperCase)) {
351 wlHints |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_UPPERCASE;
352 }
353 if (hints.testFlag(flag: ContentHint::TitleCase)) {
354 wlHints |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_TITLECASE;
355 }
356 if (hints.testFlag(flag: ContentHint::HiddenText)) {
357 wlHints |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_HIDDEN_TEXT;
358 }
359 if (hints.testFlag(flag: ContentHint::SensitiveData)) {
360 wlHints |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_SENSITIVE_DATA;
361 }
362 if (hints.testFlag(flag: ContentHint::Latin)) {
363 wlHints |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_LATIN;
364 }
365 if (hints.testFlag(flag: ContentHint::MultiLine)) {
366 wlHints |= ZWP_TEXT_INPUT_V2_CONTENT_HINT_MULTILINE;
367 }
368 switch (purpose) {
369 case ContentPurpose::Normal:
370 wlPurpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_NORMAL;
371 break;
372 case ContentPurpose::Alpha:
373 wlPurpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_ALPHA;
374 break;
375 case ContentPurpose::Digits:
376 wlPurpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_DIGITS;
377 break;
378 case ContentPurpose::Number:
379 wlPurpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_NUMBER;
380 break;
381 case ContentPurpose::Phone:
382 wlPurpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_PHONE;
383 break;
384 case ContentPurpose::Url:
385 wlPurpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_URL;
386 break;
387 case ContentPurpose::Email:
388 wlPurpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_EMAIL;
389 break;
390 case ContentPurpose::Name:
391 wlPurpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_NAME;
392 break;
393 case ContentPurpose::Password:
394 wlPurpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_PASSWORD;
395 break;
396 case ContentPurpose::Date:
397 wlPurpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_DATE;
398 break;
399 case ContentPurpose::Time:
400 wlPurpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_TIME;
401 break;
402 case ContentPurpose::DateTime:
403 wlPurpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_DATETIME;
404 break;
405 case ContentPurpose::Terminal:
406 wlPurpose = ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_TERMINAL;
407 break;
408 }
409 zwp_text_input_v2_set_content_type(zwp_text_input_v2: textinputunstablev2, hint: wlHints, purpose: wlPurpose);
410}
411
412TextInputUnstableV2::TextInputUnstableV2(Seat *seat, QObject *parent)
413 : TextInput(new Private(this, seat), parent)
414{
415}
416
417TextInputUnstableV2::~TextInputUnstableV2()
418{
419 release();
420}
421
422TextInputUnstableV2::Private *TextInputUnstableV2::d_func() const
423{
424 return reinterpret_cast<Private *>(d.data());
425}
426
427void TextInputUnstableV2::setup(zwp_text_input_v2 *textinputunstablev2)
428{
429 Q_D();
430 d->setup(textinputunstablev2);
431}
432
433void TextInputUnstableV2::release()
434{
435 Q_D();
436 d->textinputunstablev2.release();
437}
438
439void TextInputUnstableV2::destroy()
440{
441 Q_D();
442 d->textinputunstablev2.destroy();
443}
444
445TextInputUnstableV2::operator zwp_text_input_v2 *()
446{
447 Q_D();
448 return d->textinputunstablev2;
449}
450
451TextInputUnstableV2::operator zwp_text_input_v2 *() const
452{
453 Q_D();
454 return d->textinputunstablev2;
455}
456
457class TextInputManagerUnstableV2::Private : public TextInputManager::Private
458{
459public:
460 Private() = default;
461
462 void release() override;
463 void destroy() override;
464 bool isValid() override;
465 void setupV2(zwp_text_input_manager_v2 *ti) override;
466 TextInput *createTextInput(Seat *seat, QObject *parent = nullptr) override;
467 using TextInputManager::Private::operator wl_text_input_manager *;
468 operator zwp_text_input_manager_v2 *() override
469 {
470 return textinputmanagerunstablev2;
471 }
472 operator zwp_text_input_manager_v2 *() const override
473 {
474 return textinputmanagerunstablev2;
475 }
476
477 WaylandPointer<zwp_text_input_manager_v2, zwp_text_input_manager_v2_destroy> textinputmanagerunstablev2;
478};
479
480void TextInputManagerUnstableV2::Private::release()
481{
482 textinputmanagerunstablev2.release();
483}
484
485void TextInputManagerUnstableV2::Private::destroy()
486{
487 textinputmanagerunstablev2.destroy();
488}
489
490bool TextInputManagerUnstableV2::Private::isValid()
491{
492 return textinputmanagerunstablev2.isValid();
493}
494
495TextInputManagerUnstableV2::TextInputManagerUnstableV2(QObject *parent)
496 : TextInputManager(new Private, parent)
497{
498}
499
500TextInputManagerUnstableV2::~TextInputManagerUnstableV2() = default;
501
502void TextInputManagerUnstableV2::Private::setupV2(zwp_text_input_manager_v2 *ti)
503{
504 Q_ASSERT(ti);
505 Q_ASSERT(!textinputmanagerunstablev2);
506 textinputmanagerunstablev2.setup(pointer: ti);
507}
508
509TextInput *TextInputManagerUnstableV2::Private::createTextInput(Seat *seat, QObject *parent)
510{
511 Q_ASSERT(isValid());
512 TextInputUnstableV2 *t = new TextInputUnstableV2(seat, parent);
513 auto w = zwp_text_input_manager_v2_get_text_input(zwp_text_input_manager_v2: textinputmanagerunstablev2, seat: *seat);
514 if (queue) {
515 queue->addProxy(proxy: w);
516 }
517 t->setup(w);
518 return t;
519}
520
521}
522}
523

source code of kwayland/src/client/textinput_v2.cpp