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-v0-client-protocol.h>
13
14namespace KWayland
15{
16namespace Client
17{
18class TextInputUnstableV0::Private : public TextInput::Private
19{
20public:
21 Private(TextInputUnstableV0 *q, Seat *seat);
22
23 void setup(wl_text_input *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<wl_text_input, wl_text_input_destroy> textinputunstablev0;
37
38private:
39 static void enterCallaback(void *data, wl_text_input *wl_text_input, wl_surface *surface);
40 static void leaveCallback(void *data, wl_text_input *wl_text_input);
41 static void modifiersMapCallback(void *data, wl_text_input *wl_text_input, wl_array *map);
42 static void inputPanelStateCallback(void *data, wl_text_input *wl_text_input, uint32_t state);
43 static void preeditStringCallback(void *data, wl_text_input *wl_text_input, uint32_t serial, const char *text, const char *commit);
44 static void preeditStylingCallback(void *data, wl_text_input *wl_text_input, uint32_t index, uint32_t length, uint32_t style);
45 static void preeditCursorCallback(void *data, wl_text_input *wl_text_input, int32_t index);
46 static void commitStringCallback(void *data, wl_text_input *wl_text_input, uint32_t serial, const char *text);
47 static void cursorPositionCallback(void *data, wl_text_input *wl_text_input, int32_t index, int32_t anchor);
48 static void deleteSurroundingTextCallback(void *data, wl_text_input *wl_text_input, int32_t index, uint32_t length);
49 static void keysymCallback(void *data, wl_text_input *wl_text_input, uint32_t serial, uint32_t time, uint32_t sym, uint32_t state, uint32_t modifiers);
50 static void languageCallback(void *data, wl_text_input *wl_text_input, uint32_t serial, const char *language);
51 static void textDirectionCallback(void *data, wl_text_input *wl_text_input, uint32_t serial, uint32_t direction);
52
53 TextInputUnstableV0 *q;
54
55 static const wl_text_input_listener s_listener;
56};
57
58const wl_text_input_listener TextInputUnstableV0::Private::s_listener = {.enter: enterCallaback,
59 .leave: leaveCallback,
60 .modifiers_map: modifiersMapCallback,
61 .input_panel_state: inputPanelStateCallback,
62 .preedit_string: preeditStringCallback,
63 .preedit_styling: preeditStylingCallback,
64 .preedit_cursor: preeditCursorCallback,
65 .commit_string: commitStringCallback,
66 .cursor_position: cursorPositionCallback,
67 .delete_surrounding_text: deleteSurroundingTextCallback,
68 .keysym: keysymCallback,
69 .language: languageCallback,
70 .text_direction: textDirectionCallback};
71
72void TextInputUnstableV0::Private::enterCallaback(void *data, wl_text_input *wl_text_input, wl_surface *surface)
73{
74 auto t = reinterpret_cast<TextInputUnstableV0::Private *>(data);
75 Q_ASSERT(t->textinputunstablev0 == wl_text_input);
76 t->enteredSurface = Surface::get(native: surface);
77 Q_EMIT t->q->entered();
78}
79
80void TextInputUnstableV0::Private::leaveCallback(void *data, wl_text_input *wl_text_input)
81{
82 auto t = reinterpret_cast<TextInputUnstableV0::Private *>(data);
83 Q_ASSERT(t->textinputunstablev0 == wl_text_input);
84 t->enteredSurface = nullptr;
85 Q_EMIT t->q->left();
86}
87
88void TextInputUnstableV0::Private::modifiersMapCallback(void *data, wl_text_input *wl_text_input, wl_array *map)
89{
90 Q_UNUSED(map)
91 auto t = reinterpret_cast<TextInputUnstableV0::Private *>(data);
92 Q_ASSERT(t->textinputunstablev0 == wl_text_input);
93 // TODO: implement
94}
95
96void TextInputUnstableV0::Private::inputPanelStateCallback(void *data, wl_text_input *wl_text_input, uint32_t state)
97{
98 auto t = reinterpret_cast<TextInputUnstableV0::Private *>(data);
99 Q_ASSERT(t->textinputunstablev0 == wl_text_input);
100 if (t->inputPanelVisible != state) {
101 t->inputPanelVisible = state;
102 Q_EMIT t->q->inputPanelStateChanged();
103 }
104}
105
106void TextInputUnstableV0::Private::preeditStringCallback(void *data, wl_text_input *wl_text_input, uint32_t serial, const char *text, const char *commit)
107{
108 Q_UNUSED(serial)
109 auto t = reinterpret_cast<TextInputUnstableV0::Private *>(data);
110 Q_ASSERT(t->textinputunstablev0 == wl_text_input);
111 t->pendingPreEdit.commitText = QByteArray(commit);
112 t->pendingPreEdit.text = QByteArray(text);
113 if (!t->pendingPreEdit.cursorSet) {
114 t->pendingPreEdit.cursor = t->pendingPreEdit.text.length();
115 }
116 t->currentPreEdit = t->pendingPreEdit;
117 t->pendingPreEdit = TextInput::Private::PreEdit();
118 Q_EMIT t->q->composingTextChanged();
119}
120
121void TextInputUnstableV0::Private::preeditStylingCallback(void *data, wl_text_input *wl_text_input, uint32_t index, uint32_t length, uint32_t style)
122{
123 Q_UNUSED(index)
124 Q_UNUSED(length)
125 Q_UNUSED(style)
126 // TODO: implement
127 auto t = reinterpret_cast<TextInputUnstableV0::Private *>(data);
128 Q_ASSERT(t->textinputunstablev0 == wl_text_input);
129}
130
131void TextInputUnstableV0::Private::preeditCursorCallback(void *data, wl_text_input *wl_text_input, int32_t index)
132{
133 auto t = reinterpret_cast<TextInputUnstableV0::Private *>(data);
134 Q_ASSERT(t->textinputunstablev0 == wl_text_input);
135 t->pendingPreEdit.cursor = index;
136 t->pendingPreEdit.cursorSet = true;
137}
138
139void TextInputUnstableV0::Private::commitStringCallback(void *data, wl_text_input *wl_text_input, uint32_t serial, const char *text)
140{
141 Q_UNUSED(serial)
142 auto t = reinterpret_cast<TextInputUnstableV0::Private *>(data);
143 Q_ASSERT(t->textinputunstablev0 == wl_text_input);
144 t->pendingCommit.text = QByteArray(text);
145 t->currentCommit = t->pendingCommit;
146 // TODO: what are the proper values it should be set to?
147 t->pendingCommit = TextInput::Private::Commit();
148 t->pendingCommit.deleteSurrounding.beforeLength = 0;
149 t->pendingCommit.deleteSurrounding.afterLength = 0;
150 Q_EMIT t->q->committed();
151}
152
153void TextInputUnstableV0::Private::cursorPositionCallback(void *data, wl_text_input *wl_text_input, int32_t index, int32_t anchor)
154{
155 auto t = reinterpret_cast<TextInputUnstableV0::Private *>(data);
156 Q_ASSERT(t->textinputunstablev0 == wl_text_input);
157 t->pendingCommit.cursor = index;
158 t->pendingCommit.anchor = anchor;
159}
160
161void TextInputUnstableV0::Private::deleteSurroundingTextCallback(void *data, wl_text_input *wl_text_input, int32_t index, uint32_t length)
162{
163 auto t = reinterpret_cast<TextInputUnstableV0::Private *>(data);
164 Q_ASSERT(t->textinputunstablev0 == wl_text_input);
165 t->pendingCommit.deleteSurrounding.beforeLength = qAbs(t: index);
166 t->pendingCommit.deleteSurrounding.afterLength = length - t->pendingCommit.deleteSurrounding.beforeLength;
167}
168
169void TextInputUnstableV0::Private::keysymCallback(void *data,
170 wl_text_input *wl_text_input,
171 uint32_t serial,
172 uint32_t time,
173 uint32_t sym,
174 uint32_t wlState,
175 uint32_t modifiers)
176{
177 Q_UNUSED(serial)
178 // TODO: add support for modifiers
179 Q_UNUSED(modifiers)
180 auto t = reinterpret_cast<TextInputUnstableV0::Private *>(data);
181 Q_ASSERT(t->textinputunstablev0 == wl_text_input);
182 TextInput::KeyState state;
183 switch (wlState) {
184 case WL_KEYBOARD_KEY_STATE_RELEASED:
185 state = TextInput::KeyState::Released;
186 break;
187 case WL_KEYBOARD_KEY_STATE_PRESSED:
188 state = TextInput::KeyState::Pressed;
189 break;
190 default:
191 // invalid
192 return;
193 }
194 Q_EMIT t->q->keyEvent(xkbKeySym: sym, state, modifiers: Qt::KeyboardModifiers(), time);
195}
196
197void TextInputUnstableV0::Private::languageCallback(void *data, wl_text_input *wl_text_input, uint32_t serial, const char *language)
198{
199 Q_UNUSED(serial)
200 auto t = reinterpret_cast<TextInputUnstableV0::Private *>(data);
201 Q_ASSERT(t->textinputunstablev0 == wl_text_input);
202 if (qstrcmp(str1: t->language, str2: language) != 0) {
203 t->language = QByteArray(language);
204 Q_EMIT t->q->languageChanged();
205 }
206}
207
208void TextInputUnstableV0::Private::textDirectionCallback(void *data, wl_text_input *wl_text_input, uint32_t serial, uint32_t wlDirection)
209{
210 Q_UNUSED(serial)
211 auto t = reinterpret_cast<TextInputUnstableV0::Private *>(data);
212 Q_ASSERT(t->textinputunstablev0 == wl_text_input);
213 Qt::LayoutDirection direction;
214 switch (wlDirection) {
215 case WL_TEXT_INPUT_TEXT_DIRECTION_LTR:
216 direction = Qt::LeftToRight;
217 break;
218 case WL_TEXT_INPUT_TEXT_DIRECTION_RTL:
219 direction = Qt::RightToLeft;
220 break;
221 case WL_TEXT_INPUT_TEXT_DIRECTION_AUTO:
222 direction = Qt::LayoutDirectionAuto;
223 break;
224 default:
225 // invalid
226 return;
227 }
228 if (direction != t->textDirection) {
229 t->textDirection = direction;
230 Q_EMIT t->q->textDirectionChanged();
231 }
232}
233
234TextInputUnstableV0::Private::Private(TextInputUnstableV0 *q, Seat *seat)
235 : TextInput::Private(seat)
236 , q(q)
237{
238}
239
240void TextInputUnstableV0::Private::setup(wl_text_input *ti)
241{
242 Q_ASSERT(ti);
243 Q_ASSERT(!textinputunstablev0);
244 textinputunstablev0.setup(pointer: ti);
245 wl_text_input_add_listener(wl_text_input: ti, listener: &s_listener, data: this);
246}
247
248bool TextInputUnstableV0::Private::isValid() const
249{
250 return textinputunstablev0.isValid();
251}
252
253void TextInputUnstableV0::Private::enable(Surface *surface)
254{
255 wl_text_input_activate(wl_text_input: textinputunstablev0, seat: *seat, surface: *surface);
256}
257
258void TextInputUnstableV0::Private::disable(Surface *surface)
259{
260 Q_UNUSED(surface)
261 wl_text_input_deactivate(wl_text_input: textinputunstablev0, seat: *seat);
262}
263
264void TextInputUnstableV0::Private::showInputPanel()
265{
266 wl_text_input_show_input_panel(wl_text_input: textinputunstablev0);
267}
268
269void TextInputUnstableV0::Private::hideInputPanel()
270{
271 wl_text_input_hide_input_panel(wl_text_input: textinputunstablev0);
272}
273
274void TextInputUnstableV0::Private::setCursorRectangle(const QRect &rect)
275{
276 wl_text_input_set_cursor_rectangle(wl_text_input: textinputunstablev0, x: rect.x(), y: rect.y(), width: rect.width(), height: rect.height());
277}
278
279void TextInputUnstableV0::Private::setPreferredLanguage(const QString &lang)
280{
281 wl_text_input_set_preferred_language(wl_text_input: textinputunstablev0, language: lang.toUtf8().constData());
282}
283
284void TextInputUnstableV0::Private::setSurroundingText(const QString &text, quint32 cursor, quint32 anchor)
285{
286 const QStringView strView(text);
287
288 wl_text_input_set_surrounding_text(wl_text_input: textinputunstablev0,
289 text: text.toUtf8().constData(),
290 cursor: strView.left(n: cursor).toUtf8().length(),
291 anchor: strView.left(n: anchor).toUtf8().length());
292}
293
294void TextInputUnstableV0::Private::reset()
295{
296 wl_text_input_reset(wl_text_input: textinputunstablev0);
297}
298
299void TextInputUnstableV0::Private::setContentType(ContentHints hints, ContentPurpose purpose)
300{
301 uint32_t wlHints = 0;
302 uint32_t wlPurpose = 0;
303 if (hints.testFlag(flag: ContentHint::AutoCompletion)) {
304 wlHints |= WL_TEXT_INPUT_CONTENT_HINT_AUTO_COMPLETION;
305 }
306 if (hints.testFlag(flag: ContentHint::AutoCorrection)) {
307 wlHints |= WL_TEXT_INPUT_CONTENT_HINT_AUTO_CORRECTION;
308 }
309 if (hints.testFlag(flag: ContentHint::AutoCapitalization)) {
310 wlHints |= WL_TEXT_INPUT_CONTENT_HINT_AUTO_CAPITALIZATION;
311 }
312 if (hints.testFlag(flag: ContentHint::LowerCase)) {
313 wlHints |= WL_TEXT_INPUT_CONTENT_HINT_LOWERCASE;
314 }
315 if (hints.testFlag(flag: ContentHint::UpperCase)) {
316 wlHints |= WL_TEXT_INPUT_CONTENT_HINT_UPPERCASE;
317 }
318 if (hints.testFlag(flag: ContentHint::TitleCase)) {
319 wlHints |= WL_TEXT_INPUT_CONTENT_HINT_TITLECASE;
320 }
321 if (hints.testFlag(flag: ContentHint::HiddenText)) {
322 wlHints |= WL_TEXT_INPUT_CONTENT_HINT_HIDDEN_TEXT;
323 }
324 if (hints.testFlag(flag: ContentHint::SensitiveData)) {
325 wlHints |= WL_TEXT_INPUT_CONTENT_HINT_SENSITIVE_DATA;
326 }
327 if (hints.testFlag(flag: ContentHint::Latin)) {
328 wlHints |= WL_TEXT_INPUT_CONTENT_HINT_LATIN;
329 }
330 if (hints.testFlag(flag: ContentHint::MultiLine)) {
331 wlHints |= WL_TEXT_INPUT_CONTENT_HINT_MULTILINE;
332 }
333 switch (purpose) {
334 case ContentPurpose::Normal:
335 wlPurpose = WL_TEXT_INPUT_CONTENT_PURPOSE_NORMAL;
336 break;
337 case ContentPurpose::Alpha:
338 wlPurpose = WL_TEXT_INPUT_CONTENT_PURPOSE_ALPHA;
339 break;
340 case ContentPurpose::Digits:
341 wlPurpose = WL_TEXT_INPUT_CONTENT_PURPOSE_DIGITS;
342 break;
343 case ContentPurpose::Number:
344 wlPurpose = WL_TEXT_INPUT_CONTENT_PURPOSE_NUMBER;
345 break;
346 case ContentPurpose::Phone:
347 wlPurpose = WL_TEXT_INPUT_CONTENT_PURPOSE_PHONE;
348 break;
349 case ContentPurpose::Url:
350 wlPurpose = WL_TEXT_INPUT_CONTENT_PURPOSE_URL;
351 break;
352 case ContentPurpose::Email:
353 wlPurpose = WL_TEXT_INPUT_CONTENT_PURPOSE_EMAIL;
354 break;
355 case ContentPurpose::Name:
356 wlPurpose = WL_TEXT_INPUT_CONTENT_PURPOSE_NAME;
357 break;
358 case ContentPurpose::Password:
359 wlPurpose = WL_TEXT_INPUT_CONTENT_PURPOSE_PASSWORD;
360 break;
361 case ContentPurpose::Date:
362 wlPurpose = WL_TEXT_INPUT_CONTENT_PURPOSE_DATE;
363 break;
364 case ContentPurpose::Time:
365 wlPurpose = WL_TEXT_INPUT_CONTENT_PURPOSE_TIME;
366 break;
367 case ContentPurpose::DateTime:
368 wlPurpose = WL_TEXT_INPUT_CONTENT_PURPOSE_DATETIME;
369 break;
370 case ContentPurpose::Terminal:
371 wlPurpose = WL_TEXT_INPUT_CONTENT_PURPOSE_TERMINAL;
372 break;
373 }
374 wl_text_input_set_content_type(wl_text_input: textinputunstablev0, hint: wlHints, purpose: wlPurpose);
375}
376
377TextInputUnstableV0::TextInputUnstableV0(Seat *seat, QObject *parent)
378 : TextInput(new Private(this, seat), parent)
379{
380}
381
382TextInputUnstableV0::~TextInputUnstableV0()
383{
384 release();
385}
386
387TextInputUnstableV0::Private *TextInputUnstableV0::d_func() const
388{
389 return reinterpret_cast<Private *>(d.data());
390}
391
392void TextInputUnstableV0::setup(wl_text_input *textinputunstablev0)
393{
394 Q_D();
395 d->setup(textinputunstablev0);
396}
397
398void TextInputUnstableV0::release()
399{
400 Q_D();
401 d->textinputunstablev0.release();
402}
403
404void TextInputUnstableV0::destroy()
405{
406 Q_D();
407 d->textinputunstablev0.destroy();
408}
409
410TextInputUnstableV0::operator wl_text_input *()
411{
412 Q_D();
413 return d->textinputunstablev0;
414}
415
416TextInputUnstableV0::operator wl_text_input *() const
417{
418 Q_D();
419 return d->textinputunstablev0;
420}
421
422class TextInputManagerUnstableV0::Private : public TextInputManager::Private
423{
424public:
425 Private() = default;
426
427 void release() override;
428 void destroy() override;
429 bool isValid() override;
430 void setupV0(wl_text_input_manager *ti) override;
431 TextInput *createTextInput(Seat *seat, QObject *parent = nullptr) override;
432 using TextInputManager::Private::operator zwp_text_input_manager_v2 *; // overriding only one overload results in a compiler warning. This tells GCC we're
433 // doing it deliberately
434 operator wl_text_input_manager *() override
435 {
436 return textinputmanagerunstablev0;
437 }
438 operator wl_text_input_manager *() const override
439 {
440 return textinputmanagerunstablev0;
441 }
442
443 WaylandPointer<wl_text_input_manager, wl_text_input_manager_destroy> textinputmanagerunstablev0;
444};
445
446void TextInputManagerUnstableV0::Private::release()
447{
448 textinputmanagerunstablev0.release();
449}
450
451void TextInputManagerUnstableV0::Private::destroy()
452{
453 textinputmanagerunstablev0.destroy();
454}
455
456bool TextInputManagerUnstableV0::Private::isValid()
457{
458 return textinputmanagerunstablev0.isValid();
459}
460
461TextInputManagerUnstableV0::TextInputManagerUnstableV0(QObject *parent)
462 : TextInputManager(new Private, parent)
463{
464}
465
466TextInputManagerUnstableV0::Private *TextInputManagerUnstableV0::d_func() const
467{
468 return reinterpret_cast<Private *>(d.data());
469}
470
471TextInputManagerUnstableV0::~TextInputManagerUnstableV0()
472{
473 release();
474}
475
476void TextInputManagerUnstableV0::Private::setupV0(wl_text_input_manager *ti)
477{
478 Q_ASSERT(ti);
479 Q_ASSERT(!textinputmanagerunstablev0);
480 textinputmanagerunstablev0.setup(pointer: ti);
481}
482
483TextInput *TextInputManagerUnstableV0::Private::createTextInput(Seat *seat, QObject *parent)
484{
485 Q_ASSERT(isValid());
486 TextInputUnstableV0 *t = new TextInputUnstableV0(seat, parent);
487 auto w = wl_text_input_manager_create_text_input(wl_text_input_manager: textinputmanagerunstablev0);
488 if (queue) {
489 queue->addProxy(proxy: w);
490 }
491 t->setup(w);
492 return t;
493}
494
495}
496}
497

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