KWaylandServer

textinput_v3_interface.cpp
1 /*
2  SPDX-FileCopyrightText: 2020 Bhushan Shah <[email protected]>
3  SPDX-FileCopyrightText: 2018 Roman Glig <[email protected]>
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 
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 
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 
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 
105 TextInputManagerV3InterfacePrivate::TextInputManagerV3InterfacePrivate(TextInputManagerV3Interface *_q, Display *display)
106  : QtWaylandServer::zwp_text_input_manager_v3(*display, s_version)
107  , q(_q)
108 {
109 }
110 
111 void TextInputManagerV3InterfacePrivate::zwp_text_input_manager_v3_destroy(Resource *resource)
112 {
113  wl_resource_destroy(resource->handle);
114 }
115 
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 
127 TextInputManagerV3Interface::TextInputManagerV3Interface(Display *display, QObject *parent)
128  : QObject(parent)
129  , d(new TextInputManagerV3InterfacePrivate(this, display))
130 {
131 }
132 
133 TextInputManagerV3Interface::~TextInputManagerV3Interface() = default;
134 
135 TextInputV3InterfacePrivate::TextInputV3InterfacePrivate(SeatInterface *seat, TextInputV3Interface *_q)
136  : seat(seat)
137  , q(_q)
138 {
139 }
140 
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 
147 void TextInputV3InterfacePrivate::zwp_text_input_v3_destroy(Resource *resource)
148 {
149  // drop resource from the serial hash
150  serialHash.remove(resource);
151 }
152 
153 void TextInputV3InterfacePrivate::sendEnter(SurfaceInterface *s)
154 {
155  if (!s) {
156  return;
157  }
159  const auto clientResources = textInputsForClient(s->client());
160  for (auto resource : clientResources) {
161  send_enter(resource->handle, s->resource());
162  }
163 }
164 
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 
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 
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 
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 
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 
222 QList<TextInputV3InterfacePrivate::Resource *> TextInputV3InterfacePrivate::textInputsForClient(ClientConnection *client) const
223 {
224  return resourceMap().values(client->client());
225 }
226 
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 
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 
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 
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 
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 
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 
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 
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 
339 TextInputV3Interface::TextInputV3Interface(SeatInterface *seat)
340  : QObject(seat)
341  , d(new TextInputV3InterfacePrivate(seat, this))
342 {
343 }
344 
345 TextInputV3Interface::~TextInputV3Interface() = default;
346 
348 {
349  return d->contentHints;
350 }
351 
352 TextInputContentPurpose TextInputV3Interface::contentPurpose() const
353 {
354  return d->contentPurpose;
355 }
356 
358 {
359  return d->surroundingText;
360 }
361 
363 {
364  return d->surroundingTextCursorPosition;
365 }
366 
368 {
369  return d->surroundingTextSelectionAnchor;
370 }
371 
372 void TextInputV3Interface::deleteSurroundingText(quint32 beforeLength, quint32 afterLength)
373 {
374  d->deleteSurroundingText(beforeLength, afterLength);
375 }
376 
377 void TextInputV3Interface::sendPreEditString(const QString &text, quint32 cursorBegin, quint32 cursorEnd)
378 {
379  d->sendPreEdit(text, cursorBegin, cursorEnd);
380 }
381 
383 {
384  d->commitString(text);
385 }
386 
388 {
389  d->done();
390 }
391 
393 {
394  return d->surface;
395 }
396 
398 {
399  return d->cursorRectangle;
400 }
401 
403 {
404  return d->enabled;
405 }
406 
407 }
QPointer< SurfaceInterface > surface() const
TextInputContentPurpose contentPurpose() const
TextInputContentHints contentHints() const
TextInputContentHints contentHints() const
void deleteSurroundingText(quint32 beforeLength, quint32 afterLength)
Notify when the text around the current cursor position should be deleted.
void sendPreEditString(const QString &text, quint32 cursorBegin, quint32 cursorEnd)
Send preEditString to the client.
QPointer< SurfaceInterface > surface() const
void commitString(const QString &text)
Notify when text should be inserted into the editor widget.
qint32 surroundingTextSelectionAnchor() const
The byte offset of the selection anchor within the surroundingText.
void done()
Notify when changes and state requested by sendPreEditString, commitString and deleteSurroundingText ...
qint32 surroundingTextSelectionAnchor() const
The byte offset of the selection anchor within the surroundingText.
QObject * parent() const const
TextInputContentPurpose contentPurpose() const
Q_EMITQ_EMIT
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sat Oct 16 2021 23:10:15 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.