KWaylandServer

textinput_v2_interface.cpp
1 /*
2  SPDX-FileCopyrightText: 2016 Martin Gräßlin <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5 */
6 #include "display.h"
7 #include "seat_interface_p.h"
8 #include "surface_interface_p.h"
9 #include "textinput_v2_interface_p.h"
10 
11 namespace KWaylandServer
12 {
13 static const quint32 s_version = 1;
14 
15 // helpers
16 static TextInputContentHints convertContentHint(uint32_t hint)
17 {
18  const auto hints = zwp_text_input_v2_content_hint(hint);
19  TextInputContentHints ret = TextInputContentHint::None;
20 
21  if (hints & QtWaylandServer::zwp_text_input_v2::content_hint_auto_completion) {
22  ret |= TextInputContentHint::AutoCompletion;
23  }
24  if (hints & QtWaylandServer::zwp_text_input_v2::content_hint_auto_correction) {
25  ret |= TextInputContentHint::AutoCorrection;
26  }
27  if (hints & QtWaylandServer::zwp_text_input_v2::content_hint_auto_capitalization) {
28  ret |= TextInputContentHint::AutoCapitalization;
29  }
30  if (hints & QtWaylandServer::zwp_text_input_v2::content_hint_lowercase) {
31  ret |= TextInputContentHint::LowerCase;
32  }
33  if (hints & QtWaylandServer::zwp_text_input_v2::content_hint_uppercase) {
34  ret |= TextInputContentHint::UpperCase;
35  }
36  if (hints & QtWaylandServer::zwp_text_input_v2::content_hint_titlecase) {
37  ret |= TextInputContentHint::TitleCase;
38  }
39  if (hints & QtWaylandServer::zwp_text_input_v2::content_hint_hidden_text) {
40  ret |= TextInputContentHint::HiddenText;
41  }
42  if (hints & QtWaylandServer::zwp_text_input_v2::content_hint_sensitive_data) {
43  ret |= TextInputContentHint::SensitiveData;
44  }
45  if (hints & QtWaylandServer::zwp_text_input_v2::content_hint_latin) {
46  ret |= TextInputContentHint::Latin;
47  }
48  if (hints & QtWaylandServer::zwp_text_input_v2::content_hint_multiline) {
49  ret |= TextInputContentHint::MultiLine;
50  }
51  return ret;
52 }
53 
54 static TextInputContentPurpose convertContentPurpose(uint32_t purpose)
55 {
56  const auto wlPurpose = QtWaylandServer::zwp_text_input_v2::content_purpose(purpose);
57 
58  switch (wlPurpose) {
59  case QtWaylandServer::zwp_text_input_v2::content_purpose_alpha:
60  return TextInputContentPurpose::Alpha;
61  case QtWaylandServer::zwp_text_input_v2::content_purpose_digits:
62  return TextInputContentPurpose::Digits;
63  case QtWaylandServer::zwp_text_input_v2::content_purpose_number:
64  return TextInputContentPurpose::Number;
65  case QtWaylandServer::zwp_text_input_v2::content_purpose_phone:
66  return TextInputContentPurpose::Phone;
67  case QtWaylandServer::zwp_text_input_v2::content_purpose_url:
68  return TextInputContentPurpose::Url;
69  case QtWaylandServer::zwp_text_input_v2::content_purpose_email:
70  return TextInputContentPurpose::Email;
71  case QtWaylandServer::zwp_text_input_v2::content_purpose_name:
72  return TextInputContentPurpose::Name;
73  case QtWaylandServer::zwp_text_input_v2::content_purpose_password:
74  return TextInputContentPurpose::Password;
75  case QtWaylandServer::zwp_text_input_v2::content_purpose_date:
76  return TextInputContentPurpose::Date;
77  case QtWaylandServer::zwp_text_input_v2::content_purpose_time:
78  return TextInputContentPurpose::Time;
79  case QtWaylandServer::zwp_text_input_v2::content_purpose_datetime:
80  return TextInputContentPurpose::DateTime;
81  case QtWaylandServer::zwp_text_input_v2::content_purpose_terminal:
82  return TextInputContentPurpose::Terminal;
83  case QtWaylandServer::zwp_text_input_v2::content_purpose_normal:
84  return TextInputContentPurpose::Normal;
85  default:
86  return TextInputContentPurpose::Normal;
87  }
88 }
89 
90 TextInputManagerV2InterfacePrivate::TextInputManagerV2InterfacePrivate(TextInputManagerV2Interface *_q, Display *display)
91  : QtWaylandServer::zwp_text_input_manager_v2(*display, s_version)
92  , q(_q)
93 {
94 }
95 
96 class EnabledEmitter
97 {
98 public:
99  EnabledEmitter(TextInputV2Interface *q)
100  : q(q)
101  , m_wasEnabled(q->isEnabled())
102  {
103  }
104  ~EnabledEmitter()
105  {
106  if (m_wasEnabled != q->isEnabled()) {
107  Q_EMIT q->enabledChanged();
108  }
109  }
110 
111 private:
112  TextInputV2Interface *q;
113  const bool m_wasEnabled;
114 };
115 
116 void TextInputManagerV2InterfacePrivate::zwp_text_input_manager_v2_destroy(Resource *resource)
117 {
118  wl_resource_destroy(resource->handle);
119 }
120 
121 void TextInputManagerV2InterfacePrivate::zwp_text_input_manager_v2_get_text_input(Resource *resource, uint32_t id, wl_resource *seat)
122 {
123  SeatInterface *s = SeatInterface::get(seat);
124  if (!s) {
125  wl_resource_post_error(resource->handle, 0, "Invalid seat");
126  return;
127  }
128 
129  TextInputV2InterfacePrivate *textInputPrivate = TextInputV2InterfacePrivate::get(s->textInputV2());
130  textInputPrivate->add(resource->client(), id, resource->version());
131 }
132 
133 TextInputManagerV2Interface::TextInputManagerV2Interface(Display *display, QObject *parent)
134  : QObject(parent)
135  , d(new TextInputManagerV2InterfacePrivate(this, display))
136 {
137 }
138 
139 TextInputManagerV2Interface::~TextInputManagerV2Interface() = default;
140 
141 void TextInputV2InterfacePrivate::sendEnter(SurfaceInterface *newSurface, quint32 serial)
142 {
143  EnabledEmitter emitter(q);
144  if (surface) {
145  sendLeave(serial, newSurface);
146  }
147 
148  surface = newSurface;
149  if (surface) {
150  const auto clientResources = textInputsForClient(newSurface->client());
151  for (auto resource : clientResources) {
152  send_enter(resource->handle, serial, newSurface->resource());
153  }
154  }
155 }
156 
157 void TextInputV2InterfacePrivate::sendLeave(quint32 serial, SurfaceInterface *leavingSurface)
158 {
159  if (leavingSurface != surface || !leavingSurface) {
160  return;
161  }
162 
163  EnabledEmitter emitter(q);
164  surface.clear();
165  const auto clientResources = textInputsForClient(leavingSurface->client());
166  for (auto resource : clientResources) {
167  send_leave(resource->handle, serial, leavingSurface->resource());
168  }
169 }
170 
171 void TextInputV2InterfacePrivate::preEdit(const QString &text, const QString &commit)
172 {
173  if (!surface) {
174  return;
175  }
176 
177  const auto clientResources = textInputsForClient(surface->client());
178  for (auto resource : clientResources) {
179  send_preedit_string(resource->handle, text, commit);
180  }
181 }
182 
183 void TextInputV2InterfacePrivate::commitString(const QString &text)
184 {
185  if (!surface) {
186  return;
187  }
188  const QList<Resource *> textInputs = textInputsForClient(surface->client());
189  for (auto resource : textInputs) {
190  send_commit_string(resource->handle, text);
191  }
192 }
193 
194 void TextInputV2InterfacePrivate::keysymPressed(quint32 keysym, quint32 modifiers)
195 {
196  if (!surface) {
197  return;
198  }
199 
200  const QList<Resource *> textInputs = textInputsForClient(surface->client());
201  for (auto resource : textInputs) {
202  send_keysym(resource->handle, seat ? seat->timestamp() : 0, keysym, WL_KEYBOARD_KEY_STATE_PRESSED, modifiers);
203  }
204 }
205 
206 void TextInputV2InterfacePrivate::keysymReleased(quint32 keysym, quint32 modifiers)
207 {
208  if (!surface) {
209  return;
210  }
211 
212  const QList<Resource *> textInputs = textInputsForClient(surface->client());
213  for (auto resource : textInputs) {
214  send_keysym(resource->handle, seat ? seat->timestamp() : 0, keysym, WL_KEYBOARD_KEY_STATE_RELEASED, modifiers);
215  }
216 }
217 
218 void TextInputV2InterfacePrivate::deleteSurroundingText(quint32 beforeLength, quint32 afterLength)
219 {
220  if (!surface) {
221  return;
222  }
223  const QList<Resource *> textInputs = textInputsForClient(surface->client());
224  for (auto resource : textInputs) {
225  send_delete_surrounding_text(resource->handle, beforeLength, afterLength);
226  }
227 }
228 
229 void TextInputV2InterfacePrivate::setCursorPosition(qint32 index, qint32 anchor)
230 {
231  if (!surface) {
232  return;
233  }
234  const QList<Resource *> textInputs = textInputsForClient(surface->client());
235  for (auto resource : textInputs) {
236  send_cursor_position(resource->handle, index, anchor);
237  }
238 }
239 
240 void TextInputV2InterfacePrivate::setTextDirection(Qt::LayoutDirection direction)
241 {
242  if (!surface) {
243  return;
244  }
245  text_direction wlDirection;
246  switch (direction) {
247  case Qt::LeftToRight:
248  wlDirection = text_direction::text_direction_ltr;
249  break;
250  case Qt::RightToLeft:
251  wlDirection = text_direction::text_direction_rtl;
252  break;
254  wlDirection = text_direction::text_direction_auto;
255  break;
256  default:
257  Q_UNREACHABLE();
258  break;
259  }
260  const QList<Resource *> textInputs = textInputsForClient(surface->client());
261  for (auto resource : textInputs) {
262  send_text_direction(resource->handle, wlDirection);
263  }
264 }
265 
266 void TextInputV2InterfacePrivate::setPreEditCursor(qint32 index)
267 {
268  if (!surface) {
269  return;
270  }
271  const QList<Resource *> textInputs = textInputsForClient(surface->client());
272  for (auto resource : textInputs) {
273  send_preedit_cursor(resource->handle, index);
274  }
275 }
276 
277 void TextInputV2InterfacePrivate::sendInputPanelState()
278 {
279  if (!surface) {
280  return;
281  }
282  const QList<Resource *> textInputs = textInputsForClient(surface->client());
283  for (auto resource : textInputs) {
284  send_input_panel_state(resource->handle,
285  inputPanelVisible ? ZWP_TEXT_INPUT_V2_INPUT_PANEL_VISIBILITY_VISIBLE : ZWP_TEXT_INPUT_V2_INPUT_PANEL_VISIBILITY_HIDDEN,
286  overlappedSurfaceArea.x(),
287  overlappedSurfaceArea.y(),
288  overlappedSurfaceArea.width(),
289  overlappedSurfaceArea.height());
290  }
291 }
292 
293 void TextInputV2InterfacePrivate::sendLanguage()
294 {
295  if (!surface) {
296  return;
297  }
298  const QList<Resource *> textInputs = textInputsForClient(surface->client());
299  for (auto resource : textInputs) {
300  send_language(resource->handle, language);
301  }
302 }
303 
304 void TextInputV2InterfacePrivate::sendModifiersMap()
305 {
306  if (!surface) {
307  return;
308  }
309  const QList<Resource *> textInputs = textInputsForClient(surface->client());
310  for (auto resource : textInputs) {
311  send_modifiers_map(resource->handle, modifiersMap);
312  }
313 }
314 
315 TextInputV2InterfacePrivate::TextInputV2InterfacePrivate(SeatInterface *seat, TextInputV2Interface *_q)
316  : seat(seat)
317  , q(_q)
318 {
319 }
320 
321 void TextInputV2InterfacePrivate::zwp_text_input_v2_enable(Resource *resource, wl_resource *s)
322 {
323  Q_UNUSED(resource)
324  EnabledEmitter emitter(q);
325  auto enabledSurface = SurfaceInterface::get(s);
326  m_enabledSurfaces.insert(enabledSurface);
327 }
328 
329 void TextInputV2InterfacePrivate::zwp_text_input_v2_disable(Resource *resource, wl_resource *s)
330 {
331  Q_UNUSED(resource)
332  EnabledEmitter emitter(q);
333  auto disabledSurface = SurfaceInterface::get(s);
334  m_enabledSurfaces.remove(disabledSurface);
335 
336  if (disabledSurface == surface) {
337  q->setInputPanelState(false, {0, 0, 0, 0});
338  }
339 }
340 
341 void TextInputV2InterfacePrivate::zwp_text_input_v2_update_state(Resource *resource, uint32_t serial, uint32_t reason)
342 {
343  Q_UNUSED(resource)
344  Q_EMIT q->stateUpdated(serial, TextInputV2Interface::UpdateReason(reason));
345 }
346 
347 void TextInputV2InterfacePrivate::zwp_text_input_v2_hide_input_panel(Resource *resource)
348 {
349  Q_UNUSED(resource)
350  Q_EMIT q->requestHideInputPanel();
351 }
352 
353 void TextInputV2InterfacePrivate::zwp_text_input_v2_set_surrounding_text(Resource *resource, const QString &text, int32_t cursor, int32_t anchor)
354 {
355  Q_UNUSED(resource)
356  surroundingText = text;
357  surroundingTextCursorPosition = cursor;
358  surroundingTextSelectionAnchor = anchor;
359  Q_EMIT q->surroundingTextChanged();
360 }
361 
362 void TextInputV2InterfacePrivate::zwp_text_input_v2_set_content_type(Resource *resource, uint32_t hint, uint32_t purpose)
363 {
364  Q_UNUSED(resource)
365  const auto contentHints = convertContentHint(hint);
366  const auto contentPurpose = convertContentPurpose(purpose);
367  if (this->contentHints != contentHints || this->contentPurpose != contentPurpose) {
368  this->contentHints = contentHints;
369  this->contentPurpose = contentPurpose;
370  Q_EMIT q->contentTypeChanged();
371  }
372 }
373 
374 void TextInputV2InterfacePrivate::zwp_text_input_v2_set_cursor_rectangle(Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height)
375 {
376  Q_UNUSED(resource)
377  const QRect rect = QRect(x, y, width, height);
378  if (cursorRectangle != rect) {
379  cursorRectangle = rect;
380  Q_EMIT q->cursorRectangleChanged(cursorRectangle);
381  }
382 }
383 
384 void TextInputV2InterfacePrivate::zwp_text_input_v2_set_preferred_language(Resource *resource, const QString &language)
385 {
386  Q_UNUSED(resource)
387  if (preferredLanguage != language) {
388  preferredLanguage = language;
389  Q_EMIT q->preferredLanguageChanged(preferredLanguage);
390  }
391 }
392 
393 void TextInputV2InterfacePrivate::zwp_text_input_v2_show_input_panel(Resource *resource)
394 {
395  Q_UNUSED(resource)
396  Q_EMIT q->requestShowInputPanel();
397 }
398 
399 QList<TextInputV2InterfacePrivate::Resource *> TextInputV2InterfacePrivate::textInputsForClient(ClientConnection *client) const
400 {
401  return resourceMap().values(client->client());
402 }
403 
404 TextInputV2Interface::TextInputV2Interface(SeatInterface *seat)
405  : QObject(seat)
406  , d(new TextInputV2InterfacePrivate(seat, this))
407 {
408 }
409 
410 TextInputV2Interface::~TextInputV2Interface() = default;
411 
412 QString TextInputV2Interface::preferredLanguage() const
413 {
414  return d->preferredLanguage;
415 }
416 
417 TextInputContentHints TextInputV2Interface::contentHints() const
418 {
419  return d->contentHints;
420 }
421 
423 {
424  return d->contentPurpose;
425 }
426 
427 QString TextInputV2Interface::surroundingText() const
428 {
429  return d->surroundingText;
430 }
431 
432 qint32 TextInputV2Interface::surroundingTextCursorPosition() const
433 {
434  return d->surroundingTextCursorPosition;
435 }
436 
437 qint32 TextInputV2Interface::surroundingTextSelectionAnchor() const
438 {
439  return d->surroundingTextSelectionAnchor;
440 }
441 
442 void TextInputV2Interface::preEdit(const QString &text, const QString &commit)
443 {
444  d->preEdit(text, commit);
445 }
446 
447 void TextInputV2Interface::commitString(const QString &text)
448 {
449  d->commitString(text);
450 }
451 
452 void TextInputV2Interface::keysymPressed(quint32 keysym, quint32 modifiers)
453 {
454  d->keysymPressed(keysym, modifiers);
455 }
456 
457 void TextInputV2Interface::keysymReleased(quint32 keysym, quint32 modifiers)
458 {
459  d->keysymReleased(keysym, modifiers);
460 }
461 
462 void TextInputV2Interface::deleteSurroundingText(quint32 beforeLength, quint32 afterLength)
463 {
464  d->deleteSurroundingText(beforeLength, afterLength);
465 }
466 
467 void TextInputV2Interface::setCursorPosition(qint32 index, qint32 anchor)
468 {
469  d->setCursorPosition(index, anchor);
470 }
471 
472 void TextInputV2Interface::setTextDirection(Qt::LayoutDirection direction)
473 {
474  d->setTextDirection(direction);
475 }
476 
477 void TextInputV2Interface::setPreEditCursor(qint32 index)
478 {
479  d->setPreEditCursor(index);
480 }
481 
482 void TextInputV2Interface::setInputPanelState(bool visible, const QRect &overlappedSurfaceArea)
483 {
484  if (d->inputPanelVisible == visible && d->overlappedSurfaceArea == overlappedSurfaceArea) {
485  // not changed
486  return;
487  }
488  d->inputPanelVisible = visible;
489  d->overlappedSurfaceArea = overlappedSurfaceArea;
490  d->sendInputPanelState();
491 }
492 
493 void TextInputV2Interface::setLanguage(const QString &languageTag)
494 {
495  if (d->language == languageTag) {
496  // not changed
497  return;
498  }
499  d->language = languageTag;
500  d->sendLanguage();
501 }
502 
503 void TextInputV2Interface::setModifiersMap(const QByteArray &modifiersMap)
504 {
505  if (d->modifiersMap == modifiersMap) {
506  // not changed
507  return;
508  }
509  d->modifiersMap = modifiersMap;
510  d->sendModifiersMap();
511 }
512 
513 QPointer<SurfaceInterface> TextInputV2Interface::surface() const
514 {
515  return d->surface;
516 }
517 
518 QRect TextInputV2Interface::cursorRectangle() const
519 {
520  return d->cursorRectangle;
521 }
522 
523 bool TextInputV2Interface::isEnabled() const
524 {
525  return d->surface && d->m_enabledSurfaces.contains(d->surface);
526 }
527 
528 }
TextInputContentPurpose contentPurpose() const
LayoutDirection
bool contains(const QRect &rectangle, bool proper) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sat Oct 23 2021 23:08:28 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.