1 /*
2     SPDX-FileCopyrightText: 2020 Bhushan Shah <bshah@kde.org>
3     SPDX-FileCopyrightText: 2018 Roman Glig <subdiff@gmail.com>
4 
5     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6 */
7 
8 #include "display.h"
9 #include "seat_interface.h"
10 #include "surface_interface_p.h"
11 #include "textinput_v3_interface_p.h"
12 
13 namespace KWaylandServer
14 {
15 static const quint32 s_version = 1;
16 
convertContentHint(uint32_t hint)17 static TextInputContentHints convertContentHint(uint32_t hint)
18 {
19     const auto hints = zwp_text_input_v3_content_hint(hint);
20     TextInputContentHints ret = TextInputContentHint::None;
21 
22     if (hints & QtWaylandServer::zwp_text_input_v3::content_hint_completion) {
23         ret |= TextInputContentHint::AutoCompletion;
24     }
25     if (hints & QtWaylandServer::zwp_text_input_v3::content_hint_auto_capitalization) {
26         ret |= TextInputContentHint::AutoCapitalization;
27     }
28     if (hints & QtWaylandServer::zwp_text_input_v3::content_hint_lowercase) {
29         ret |= TextInputContentHint::LowerCase;
30     }
31     if (hints & QtWaylandServer::zwp_text_input_v3::content_hint_uppercase) {
32         ret |= TextInputContentHint::UpperCase;
33     }
34     if (hints & QtWaylandServer::zwp_text_input_v3::content_hint_titlecase) {
35         ret |= TextInputContentHint::TitleCase;
36     }
37     if (hints & QtWaylandServer::zwp_text_input_v3::content_hint_hidden_text) {
38         ret |= TextInputContentHint::HiddenText;
39     }
40     if (hints & QtWaylandServer::zwp_text_input_v3::content_hint_sensitive_data) {
41         ret |= TextInputContentHint::SensitiveData;
42     }
43     if (hints & QtWaylandServer::zwp_text_input_v3::content_hint_latin) {
44         ret |= TextInputContentHint::Latin;
45     }
46     if (hints & QtWaylandServer::zwp_text_input_v3::content_hint_multiline) {
47         ret |= TextInputContentHint::MultiLine;
48     }
49     if (hints & QtWaylandServer::zwp_text_input_v3::content_hint_spellcheck) {
50         ret |= TextInputContentHint::AutoCorrection;
51     }
52     return ret;
53 }
54 
convertContentPurpose(uint32_t purpose)55 static TextInputContentPurpose convertContentPurpose(uint32_t purpose)
56 {
57     const auto wlPurpose = QtWaylandServer::zwp_text_input_v3::content_purpose(purpose);
58 
59     switch (wlPurpose) {
60     case QtWaylandServer::zwp_text_input_v3::content_purpose_alpha:
61         return TextInputContentPurpose::Alpha;
62     case QtWaylandServer::zwp_text_input_v3::content_purpose_digits:
63         return TextInputContentPurpose::Digits;
64     case QtWaylandServer::zwp_text_input_v3::content_purpose_number:
65         return TextInputContentPurpose::Number;
66     case QtWaylandServer::zwp_text_input_v3::content_purpose_phone:
67         return TextInputContentPurpose::Phone;
68     case QtWaylandServer::zwp_text_input_v3::content_purpose_url:
69         return TextInputContentPurpose::Url;
70     case QtWaylandServer::zwp_text_input_v3::content_purpose_email:
71         return TextInputContentPurpose::Email;
72     case QtWaylandServer::zwp_text_input_v3::content_purpose_name:
73         return TextInputContentPurpose::Name;
74     case QtWaylandServer::zwp_text_input_v3::content_purpose_password:
75         return TextInputContentPurpose::Password;
76     case QtWaylandServer::zwp_text_input_v3::content_purpose_pin:
77         return TextInputContentPurpose::Pin;
78     case QtWaylandServer::zwp_text_input_v3::content_purpose_date:
79         return TextInputContentPurpose::Date;
80     case QtWaylandServer::zwp_text_input_v3::content_purpose_time:
81         return TextInputContentPurpose::Time;
82     case QtWaylandServer::zwp_text_input_v3::content_purpose_datetime:
83         return TextInputContentPurpose::DateTime;
84     case QtWaylandServer::zwp_text_input_v3::content_purpose_terminal:
85         return TextInputContentPurpose::Terminal;
86     case QtWaylandServer::zwp_text_input_v3::content_purpose_normal:
87         return TextInputContentPurpose::Normal;
88     default:
89         return TextInputContentPurpose::Normal;
90     }
91 }
92 
convertChangeCause(uint32_t cause)93 static TextInputChangeCause convertChangeCause(uint32_t cause)
94 {
95     const auto wlCause = QtWaylandServer::zwp_text_input_v3::change_cause(cause);
96     switch (wlCause) {
97     case QtWaylandServer::zwp_text_input_v3::change_cause_input_method:
98         return TextInputChangeCause::InputMethod;
99     case QtWaylandServer::zwp_text_input_v3::change_cause_other:
100     default:
101         return TextInputChangeCause::Other;
102     }
103 }
104 
TextInputManagerV3InterfacePrivate(TextInputManagerV3Interface * _q,Display * display)105 TextInputManagerV3InterfacePrivate::TextInputManagerV3InterfacePrivate(TextInputManagerV3Interface *_q, Display *display)
106     : QtWaylandServer::zwp_text_input_manager_v3(*display, s_version)
107     , q(_q)
108 {
109 }
110 
zwp_text_input_manager_v3_destroy(Resource * resource)111 void TextInputManagerV3InterfacePrivate::zwp_text_input_manager_v3_destroy(Resource *resource)
112 {
113     wl_resource_destroy(resource->handle);
114 }
115 
zwp_text_input_manager_v3_get_text_input(Resource * resource,uint32_t id,wl_resource * seat)116 void TextInputManagerV3InterfacePrivate::zwp_text_input_manager_v3_get_text_input(Resource *resource, uint32_t id, wl_resource *seat)
117 {
118     SeatInterface *s = SeatInterface::get(seat);
119     if (!s) {
120         wl_resource_post_error(resource->handle, 0, "Invalid seat");
121         return;
122     }
123     TextInputV3InterfacePrivate *textInputPrivate = TextInputV3InterfacePrivate::get(s->textInputV3());
124     textInputPrivate->add(resource->client(), id, resource->version());
125 }
126 
TextInputManagerV3Interface(Display * display,QObject * parent)127 TextInputManagerV3Interface::TextInputManagerV3Interface(Display *display, QObject *parent)
128     : QObject(parent)
129     , d(new TextInputManagerV3InterfacePrivate(this, display))
130 {
131 }
132 
133 TextInputManagerV3Interface::~TextInputManagerV3Interface() = default;
134 
TextInputV3InterfacePrivate(SeatInterface * seat,TextInputV3Interface * _q)135 TextInputV3InterfacePrivate::TextInputV3InterfacePrivate(SeatInterface *seat, TextInputV3Interface *_q)
136     : seat(seat)
137     , q(_q)
138 {
139 }
140 
zwp_text_input_v3_bind_resource(Resource * resource)141 void TextInputV3InterfacePrivate::zwp_text_input_v3_bind_resource(Resource *resource)
142 {
143     // we initialize the serial for the resource to be 0
144     serialHash.insert(resource, 0);
145 }
146 
zwp_text_input_v3_destroy(Resource * resource)147 void TextInputV3InterfacePrivate::zwp_text_input_v3_destroy(Resource *resource)
148 {
149     // drop resource from the serial hash
150     serialHash.remove(resource);
151 }
152 
sendEnter(SurfaceInterface * s)153 void TextInputV3InterfacePrivate::sendEnter(SurfaceInterface *s)
154 {
155     if (!s) {
156         return;
157     }
158     surface = QPointer<SurfaceInterface>(s);
159     const auto clientResources = textInputsForClient(s->client());
160     for (auto resource : clientResources) {
161         send_enter(resource->handle, s->resource());
162     }
163 }
164 
sendLeave(SurfaceInterface * s)165 void TextInputV3InterfacePrivate::sendLeave(SurfaceInterface *s)
166 {
167     if (!s) {
168         return;
169     }
170     surface.clear();
171     const auto clientResources = textInputsForClient(s->client());
172     for (auto resource : clientResources) {
173         send_leave(resource->handle, s->resource());
174     }
175 }
176 
sendPreEdit(const QString & text,const quint32 cursorBegin,const quint32 cursorEnd)177 void TextInputV3InterfacePrivate::sendPreEdit(const QString &text, const quint32 cursorBegin, const quint32 cursorEnd)
178 {
179     if (!surface) {
180         return;
181     }
182     const QList<Resource *> textInputs = textInputsForClient(surface->client());
183     for (auto resource : textInputs) {
184         send_preedit_string(resource->handle, text, cursorBegin, cursorEnd);
185     }
186 }
187 
commitString(const QString & text)188 void TextInputV3InterfacePrivate::commitString(const QString &text)
189 {
190     if (!surface) {
191         return;
192     }
193     const QList<Resource *> textInputs = textInputsForClient(surface->client());
194     for (auto resource : textInputs) {
195         send_commit_string(resource->handle, text);
196     }
197 }
198 
deleteSurroundingText(quint32 before,quint32 after)199 void TextInputV3InterfacePrivate::deleteSurroundingText(quint32 before, quint32 after)
200 {
201     if (!surface) {
202         return;
203     }
204     const QList<Resource *> textInputs = textInputsForClient(surface->client());
205     for (auto resource : textInputs) {
206         send_delete_surrounding_text(resource->handle, before, after);
207     }
208 }
209 
done()210 void TextInputV3InterfacePrivate::done()
211 {
212     if (!surface) {
213         return;
214     }
215     const QList<Resource *> textInputs = textInputsForClient(surface->client());
216     for (auto resource : textInputs) {
217         // zwp_text_input_v3.done takes the serial argument which is equal to number of commit requests issued
218         send_done(resource->handle, serialHash[resource]);
219     }
220 }
221 
textInputsForClient(ClientConnection * client) const222 QList<TextInputV3InterfacePrivate::Resource *> TextInputV3InterfacePrivate::textInputsForClient(ClientConnection *client) const
223 {
224     return resourceMap().values(client->client());
225 }
226 
zwp_text_input_v3_enable(Resource * resource)227 void TextInputV3InterfacePrivate::zwp_text_input_v3_enable(Resource *resource)
228 {
229     // reset pending state to default
230     Q_UNUSED(resource)
231     defaultPending();
232     pending.enabled = true;
233 }
234 
zwp_text_input_v3_disable(Resource * resource)235 void TextInputV3InterfacePrivate::zwp_text_input_v3_disable(Resource *resource)
236 {
237     // reset pending state to default
238     Q_UNUSED(resource)
239     defaultPending();
240 }
241 
zwp_text_input_v3_set_surrounding_text(Resource * resource,const QString & text,int32_t cursor,int32_t anchor)242 void TextInputV3InterfacePrivate::zwp_text_input_v3_set_surrounding_text(Resource *resource, const QString &text, int32_t cursor, int32_t anchor)
243 {
244     Q_UNUSED(resource)
245     // zwp_text_input_v3_set_surrounding_text is no-op if enabled request is not pending
246     if (!pending.enabled) {
247         return;
248     }
249     pending.surroundingText = text;
250     pending.surroundingTextCursorPosition = cursor;
251     pending.surroundingTextSelectionAnchor = anchor;
252 }
253 
zwp_text_input_v3_set_content_type(Resource * resource,uint32_t hint,uint32_t purpose)254 void TextInputV3InterfacePrivate::zwp_text_input_v3_set_content_type(Resource *resource, uint32_t hint, uint32_t purpose)
255 {
256     Q_UNUSED(resource)
257     // zwp_text_input_v3_set_content_type is no-op if enabled request is not pending
258     if (!pending.enabled) {
259         return;
260     }
261     pending.contentHints = convertContentHint(hint);
262     pending.contentPurpose = convertContentPurpose(purpose);
263 }
264 
zwp_text_input_v3_set_cursor_rectangle(Resource * resource,int32_t x,int32_t y,int32_t width,int32_t height)265 void TextInputV3InterfacePrivate::zwp_text_input_v3_set_cursor_rectangle(Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height)
266 {
267     Q_UNUSED(resource)
268     // zwp_text_input_v3_set_cursor_rectangle is no-op if enabled request is not pending
269     if (!pending.enabled) {
270         return;
271     }
272     pending.cursorRectangle = QRect(x, y, width, height);
273 }
274 
zwp_text_input_v3_set_text_change_cause(Resource * resource,uint32_t cause)275 void TextInputV3InterfacePrivate::zwp_text_input_v3_set_text_change_cause(Resource *resource, uint32_t cause)
276 {
277     Q_UNUSED(resource)
278     // zwp_text_input_v3_set_text_change_cause is no-op if enabled request is not pending
279     if (!pending.enabled) {
280         return;
281     }
282     pending.surroundingTextChangeCause = convertChangeCause(cause);
283 }
284 
zwp_text_input_v3_commit(Resource * resource)285 void TextInputV3InterfacePrivate::zwp_text_input_v3_commit(Resource *resource)
286 {
287     serialHash[resource]++;
288 
289     if (enabled != pending.enabled) {
290         enabled = pending.enabled;
291         Q_EMIT q->enabledChanged();
292     }
293 
294     if (surroundingTextChangeCause != pending.surroundingTextChangeCause) {
295         surroundingTextChangeCause = pending.surroundingTextChangeCause;
296         pending.surroundingTextChangeCause = TextInputChangeCause::InputMethod;
297     }
298 
299     if (contentHints != pending.contentHints || contentPurpose != pending.contentPurpose) {
300         contentHints = pending.contentHints;
301         contentPurpose = pending.contentPurpose;
302         if (enabled) {
303             Q_EMIT q->contentTypeChanged();
304         }
305     }
306 
307     if (cursorRectangle != pending.cursorRectangle) {
308         cursorRectangle = pending.cursorRectangle;
309         if (enabled) {
310             Q_EMIT q->cursorRectangleChanged(cursorRectangle);
311         }
312     }
313 
314     if (surroundingText != pending.surroundingText || surroundingTextCursorPosition != pending.surroundingTextCursorPosition
315         || surroundingTextSelectionAnchor != pending.surroundingTextSelectionAnchor) {
316         surroundingText = pending.surroundingText;
317         surroundingTextCursorPosition = pending.surroundingTextCursorPosition;
318         surroundingTextSelectionAnchor = pending.surroundingTextSelectionAnchor;
319         if (enabled) {
320             Q_EMIT q->surroundingTextChanged();
321         }
322     }
323 
324     Q_EMIT q->stateCommitted(serialHash[resource]);
325 }
326 
defaultPending()327 void TextInputV3InterfacePrivate::defaultPending()
328 {
329     pending.cursorRectangle = QRect();
330     pending.surroundingTextChangeCause = TextInputChangeCause::InputMethod;
331     pending.contentHints = TextInputContentHints(TextInputContentHint::None);
332     pending.contentPurpose = TextInputContentPurpose::Normal;
333     pending.enabled = false;
334     pending.surroundingText = QString();
335     pending.surroundingTextCursorPosition = 0;
336     pending.surroundingTextSelectionAnchor = 0;
337 }
338 
TextInputV3Interface(SeatInterface * seat)339 TextInputV3Interface::TextInputV3Interface(SeatInterface *seat)
340     : QObject(seat)
341     , d(new TextInputV3InterfacePrivate(seat, this))
342 {
343 }
344 
345 TextInputV3Interface::~TextInputV3Interface() = default;
346 
contentHints() const347 TextInputContentHints TextInputV3Interface::contentHints() const
348 {
349     return d->contentHints;
350 }
351 
contentPurpose() const352 TextInputContentPurpose TextInputV3Interface::contentPurpose() const
353 {
354     return d->contentPurpose;
355 }
356 
surroundingText() const357 QString TextInputV3Interface::surroundingText() const
358 {
359     return d->surroundingText;
360 }
361 
surroundingTextCursorPosition() const362 qint32 TextInputV3Interface::surroundingTextCursorPosition() const
363 {
364     return d->surroundingTextCursorPosition;
365 }
366 
surroundingTextSelectionAnchor() const367 qint32 TextInputV3Interface::surroundingTextSelectionAnchor() const
368 {
369     return d->surroundingTextSelectionAnchor;
370 }
371 
deleteSurroundingText(quint32 beforeLength,quint32 afterLength)372 void TextInputV3Interface::deleteSurroundingText(quint32 beforeLength, quint32 afterLength)
373 {
374     d->deleteSurroundingText(beforeLength, afterLength);
375 }
376 
sendPreEditString(const QString & text,quint32 cursorBegin,quint32 cursorEnd)377 void TextInputV3Interface::sendPreEditString(const QString &text, quint32 cursorBegin, quint32 cursorEnd)
378 {
379     d->sendPreEdit(text, cursorBegin, cursorEnd);
380 }
381 
commitString(const QString & text)382 void TextInputV3Interface::commitString(const QString &text)
383 {
384     d->commitString(text);
385 }
386 
done()387 void TextInputV3Interface::done()
388 {
389     d->done();
390 }
391 
surface() const392 QPointer<SurfaceInterface> TextInputV3Interface::surface() const
393 {
394     return d->surface;
395 }
396 
cursorRectangle() const397 QRect TextInputV3Interface::cursorRectangle() const
398 {
399     return d->cursorRectangle;
400 }
401 
isEnabled() const402 bool TextInputV3Interface::isEnabled() const
403 {
404     return d->enabled;
405 }
406 
407 }
408