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