1 /****************************************************************************
2 Copyright © 2020 Adrien Faveraux <ad1rie3@hotmail.fr>
3 
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) version 3, or any
8 later version accepted by the membership of KDE e.V. (or its
9 successor approved by the membership of KDE e.V.), which shall
10 act as a proxy defined in Section 6 of version 3 of the license.
11 
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 Lesser General Public License for more details.
16 
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library.  If not, see <http://www.gnu.org/licenses/>.
19 ****************************************************************************/
20 #include "display.h"
21 #include "wayland/client.h"
22 #include "wayland/global.h"
23 #include "wayland/resource.h"
24 
25 #include "seat_p.h"
26 #include "surface.h"
27 #include "surface_p.h"
28 
29 #include "text_input_v2_p.h"
30 
31 namespace Wrapland::Server
32 {
33 
34 const struct zwp_text_input_manager_v2_interface TextInputManagerV2::Private::s_interface = {
35     resourceDestroyCallback,
36     cb<getTextInputCallback>,
37 };
38 
Private(Display * display,TextInputManagerV2 * q)39 TextInputManagerV2::Private::Private(Display* display, TextInputManagerV2* q)
40     : TextInputManagerV2Global(q, display, &zwp_text_input_manager_v2_interface, &s_interface)
41 {
42     create();
43 }
44 
getTextInputCallback(TextInputManagerV2Bind * bind,uint32_t id,wl_resource * wlSeat)45 void TextInputManagerV2::Private::getTextInputCallback(TextInputManagerV2Bind* bind,
46                                                        uint32_t id,
47                                                        wl_resource* wlSeat)
48 {
49     auto seat = SeatGlobal::handle(wlSeat);
50 
51     auto textInput = new TextInputV2(bind->client()->handle(), bind->version(), id);
52 
53     textInput->d_ptr->seat = seat;
54 
55     seat->d_ptr->text_inputs.register_device(textInput);
56 }
57 
TextInputManagerV2(Display * display,QObject * parent)58 TextInputManagerV2::TextInputManagerV2(Display* display, QObject* parent)
59     : QObject(parent)
60     , d_ptr(new Private(display, this))
61 {
62 }
63 
64 TextInputManagerV2::~TextInputManagerV2() = default;
65 
66 const struct zwp_text_input_v2_interface TextInputV2::Private::s_interface = {
67     destroyCallback,
68     enableCallback,
69     disableCallback,
70     showInputPanelCallback,
71     hideInputPanelCallback,
72     setSurroundingTextCallback,
73     setContentTypeCallback,
74     setCursorRectangleCallback,
75     setPreferredLanguageCallback,
76     updateStateCallback,
77 };
78 
Private(Client * client,uint32_t version,uint32_t id,TextInputV2 * q)79 TextInputV2::Private::Private(Client* client, uint32_t version, uint32_t id, TextInputV2* q)
80     : Wayland::Resource<TextInputV2>(client,
81                                      version,
82                                      id,
83                                      &zwp_text_input_v2_interface,
84                                      &s_interface,
85                                      q)
86 {
87 }
88 
enable(Surface * s)89 void TextInputV2::Private::enable(Surface* s)
90 {
91     surface = QPointer<Surface>(s);
92     enabled = true;
93     Q_EMIT handle()->enabledChanged();
94 }
95 
disable()96 void TextInputV2::Private::disable()
97 {
98     surface.clear();
99     enabled = false;
100     Q_EMIT handle()->enabledChanged();
101 }
102 
sendEnter(Surface * surface,quint32 serial)103 void TextInputV2::Private::sendEnter(Surface* surface, quint32 serial)
104 {
105     if (!surface) {
106         return;
107     }
108     send<zwp_text_input_v2_send_enter>(serial, surface->d_ptr->resource());
109 }
110 
sendLeave(quint32 serial,Surface * surface)111 void TextInputV2::Private::sendLeave(quint32 serial, Surface* surface)
112 {
113     if (!surface) {
114         return;
115     }
116     send<zwp_text_input_v2_send_leave>(serial, surface->d_ptr->resource());
117 }
118 
preEdit(const QByteArray & text,const QByteArray & commit)119 void TextInputV2::Private::preEdit(const QByteArray& text, const QByteArray& commit)
120 {
121     send<zwp_text_input_v2_send_preedit_string>(text.constData(), commit.constData());
122 }
123 
commit(const QByteArray & text)124 void TextInputV2::Private::commit(const QByteArray& text)
125 {
126     send<zwp_text_input_v2_send_commit_string>(text.constData());
127 }
128 
keysymPressed(quint32 keysym,Qt::KeyboardModifiers modifiers)129 void TextInputV2::Private::keysymPressed(quint32 keysym,
130                                          [[maybe_unused]] Qt::KeyboardModifiers modifiers)
131 {
132     send<zwp_text_input_v2_send_keysym>(
133         seat ? seat->timestamp() : 0, keysym, WL_KEYBOARD_KEY_STATE_PRESSED, 0);
134 }
135 
keysymReleased(quint32 keysym,Qt::KeyboardModifiers modifiers)136 void TextInputV2::Private::keysymReleased(quint32 keysym,
137                                           [[maybe_unused]] Qt::KeyboardModifiers modifiers)
138 {
139     send<zwp_text_input_v2_send_keysym>(
140         seat ? seat->timestamp() : 0, keysym, WL_KEYBOARD_KEY_STATE_RELEASED, 0);
141 }
142 
deleteSurroundingText(quint32 beforeLength,quint32 afterLength)143 void TextInputV2::Private::deleteSurroundingText(quint32 beforeLength, quint32 afterLength)
144 {
145     send<zwp_text_input_v2_send_delete_surrounding_text>(beforeLength, afterLength);
146 }
147 
setCursorPosition(qint32 index,qint32 anchor)148 void TextInputV2::Private::setCursorPosition(qint32 index, qint32 anchor)
149 {
150     send<zwp_text_input_v2_send_cursor_position>(index, anchor);
151 }
152 
setTextDirection(Qt::LayoutDirection direction)153 void TextInputV2::Private::setTextDirection(Qt::LayoutDirection direction)
154 {
155     zwp_text_input_v2_text_direction wlDirection = ZWP_TEXT_INPUT_V2_TEXT_DIRECTION_AUTO;
156     switch (direction) {
157     case Qt::LeftToRight:
158         wlDirection = ZWP_TEXT_INPUT_V2_TEXT_DIRECTION_LTR;
159         break;
160     case Qt::RightToLeft:
161         wlDirection = ZWP_TEXT_INPUT_V2_TEXT_DIRECTION_RTL;
162         break;
163     case Qt::LayoutDirectionAuto:
164         wlDirection = ZWP_TEXT_INPUT_V2_TEXT_DIRECTION_AUTO;
165         break;
166     default:
167         Q_UNREACHABLE();
168         break;
169     }
170     send<zwp_text_input_v2_send_text_direction>(wlDirection);
171 }
172 
setPreEditCursor(qint32 index)173 void TextInputV2::Private::setPreEditCursor(qint32 index)
174 {
175     send<zwp_text_input_v2_send_preedit_cursor>(index);
176 }
177 
sendInputPanelState()178 void TextInputV2::Private::sendInputPanelState()
179 {
180     send<zwp_text_input_v2_send_input_panel_state>(
181         inputPanelVisible ? ZWP_TEXT_INPUT_V2_INPUT_PANEL_VISIBILITY_VISIBLE
182                           : ZWP_TEXT_INPUT_V2_INPUT_PANEL_VISIBILITY_HIDDEN,
183         overlappedSurfaceArea.x(),
184         overlappedSurfaceArea.y(),
185         overlappedSurfaceArea.width(),
186         overlappedSurfaceArea.height());
187 }
188 
sendLanguage()189 void TextInputV2::Private::sendLanguage()
190 {
191     send<zwp_text_input_v2_send_language>(language.constData());
192 }
193 
enableCallback(wl_client * wlClient,wl_resource * wlResource,wl_resource * surface)194 void TextInputV2::Private::enableCallback([[maybe_unused]] wl_client* wlClient,
195                                           wl_resource* wlResource,
196                                           wl_resource* surface)
197 {
198     auto priv = handle(wlResource)->d_ptr;
199     priv->enable(Wayland::Resource<Surface>::handle(surface));
200 }
201 
disableCallback(wl_client * wlClient,wl_resource * wlResource,wl_resource * surface)202 void TextInputV2::Private::disableCallback([[maybe_unused]] wl_client* wlClient,
203                                            wl_resource* wlResource,
204                                            [[maybe_unused]] wl_resource* surface)
205 {
206     auto priv = handle(wlResource)->d_ptr;
207     priv->disable();
208 }
209 
updateStateCallback(wl_client * wlClient,wl_resource * wlResource,uint32_t serial,uint32_t reason)210 void TextInputV2::Private::updateStateCallback([[maybe_unused]] wl_client* wlClient,
211                                                wl_resource* wlResource,
212                                                [[maybe_unused]] uint32_t serial,
213                                                uint32_t reason)
214 {
215     auto priv = handle(wlResource)->d_ptr;
216 
217     // TODO(unknown author): use other reason values reason
218     if (reason == ZWP_TEXT_INPUT_V2_UPDATE_STATE_RESET) {
219         Q_EMIT priv->handle()->requestReset();
220     }
221 }
222 
showInputPanelCallback(wl_client * wlClient,wl_resource * wlResource)223 void TextInputV2::Private::showInputPanelCallback([[maybe_unused]] wl_client* wlClient,
224                                                   wl_resource* wlResource)
225 {
226     auto priv = handle(wlResource)->d_ptr;
227     Q_EMIT priv->handle()->requestShowInputPanel();
228 }
229 
hideInputPanelCallback(wl_client * wlClient,wl_resource * wlResource)230 void TextInputV2::Private::hideInputPanelCallback([[maybe_unused]] wl_client* wlClient,
231                                                   wl_resource* wlResource)
232 {
233     auto priv = handle(wlResource)->d_ptr;
234     Q_EMIT priv->handle()->requestHideInputPanel();
235 }
236 
setSurroundingTextCallback(wl_client * wlClient,wl_resource * wlResource,const char * text,int32_t cursor,int32_t anchor)237 void TextInputV2::Private::setSurroundingTextCallback([[maybe_unused]] wl_client* wlClient,
238                                                       wl_resource* wlResource,
239                                                       const char* text,
240                                                       int32_t cursor,
241                                                       int32_t anchor)
242 {
243     auto priv = handle(wlResource)->d_ptr;
244 
245     priv->surroundingText = QByteArray(text);
246     priv->surroundingTextCursorPosition = cursor;
247     priv->surroundingTextSelectionAnchor = anchor;
248 
249     Q_EMIT priv->handle()->surroundingTextChanged();
250 }
251 
convertContentHint(uint32_t hint)252 TextInputV2::ContentHints convertContentHint(uint32_t hint)
253 {
254     const auto hints = zwp_text_input_v2_content_hint(hint);
255     TextInputV2::ContentHints ret = TextInputV2::ContentHint::None;
256 
257     if (hints & ZWP_TEXT_INPUT_V2_CONTENT_HINT_AUTO_COMPLETION) {
258         ret |= TextInputV2::ContentHint::AutoCompletion;
259     }
260     if (hints & ZWP_TEXT_INPUT_V2_CONTENT_HINT_AUTO_CORRECTION) {
261         ret |= TextInputV2::ContentHint::AutoCorrection;
262     }
263     if (hints & ZWP_TEXT_INPUT_V2_CONTENT_HINT_AUTO_CAPITALIZATION) {
264         ret |= TextInputV2::ContentHint::AutoCapitalization;
265     }
266     if (hints & ZWP_TEXT_INPUT_V2_CONTENT_HINT_LOWERCASE) {
267         ret |= TextInputV2::ContentHint::LowerCase;
268     }
269     if (hints & ZWP_TEXT_INPUT_V2_CONTENT_HINT_UPPERCASE) {
270         ret |= TextInputV2::ContentHint::UpperCase;
271     }
272     if (hints & ZWP_TEXT_INPUT_V2_CONTENT_HINT_TITLECASE) {
273         ret |= TextInputV2::ContentHint::TitleCase;
274     }
275     if (hints & ZWP_TEXT_INPUT_V2_CONTENT_HINT_HIDDEN_TEXT) {
276         ret |= TextInputV2::ContentHint::HiddenText;
277     }
278     if (hints & ZWP_TEXT_INPUT_V2_CONTENT_HINT_SENSITIVE_DATA) {
279         ret |= TextInputV2::ContentHint::SensitiveData;
280     }
281     if (hints & ZWP_TEXT_INPUT_V2_CONTENT_HINT_LATIN) {
282         ret |= TextInputV2::ContentHint::Latin;
283     }
284     if (hints & ZWP_TEXT_INPUT_V2_CONTENT_HINT_MULTILINE) {
285         ret |= TextInputV2::ContentHint::MultiLine;
286     }
287     return ret;
288 }
289 
convertContentPurpose(uint32_t purpose)290 TextInputV2::ContentPurpose convertContentPurpose(uint32_t purpose)
291 {
292     const auto wlPurpose = zwp_text_input_v2_content_purpose(purpose);
293 
294     switch (wlPurpose) {
295     case ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_ALPHA:
296         return TextInputV2::ContentPurpose::Alpha;
297     case ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_DIGITS:
298         return TextInputV2::ContentPurpose::Digits;
299     case ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_NUMBER:
300         return TextInputV2::ContentPurpose::Number;
301     case ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_PHONE:
302         return TextInputV2::ContentPurpose::Phone;
303     case ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_URL:
304         return TextInputV2::ContentPurpose::Url;
305     case ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_EMAIL:
306         return TextInputV2::ContentPurpose::Email;
307     case ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_NAME:
308         return TextInputV2::ContentPurpose::Name;
309     case ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_PASSWORD:
310         return TextInputV2::ContentPurpose::Password;
311     case ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_DATE:
312         return TextInputV2::ContentPurpose::Date;
313     case ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_TIME:
314         return TextInputV2::ContentPurpose::Time;
315     case ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_DATETIME:
316         return TextInputV2::ContentPurpose::DateTime;
317     case ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_TERMINAL:
318         return TextInputV2::ContentPurpose::Terminal;
319     case ZWP_TEXT_INPUT_V2_CONTENT_PURPOSE_NORMAL:
320     default:
321         return TextInputV2::ContentPurpose::Normal;
322     }
323 }
324 
setContentTypeCallback(wl_client * wlClient,wl_resource * wlResource,uint32_t hint,uint32_t purpose)325 void TextInputV2::Private::setContentTypeCallback([[maybe_unused]] wl_client* wlClient,
326                                                   wl_resource* wlResource,
327                                                   uint32_t hint,
328                                                   uint32_t purpose)
329 {
330     auto priv = handle(wlResource)->d_ptr;
331     const auto contentHints = convertContentHint(hint);
332     const auto contentPurpose = convertContentPurpose(purpose);
333 
334     if (contentHints != priv->contentHints || contentPurpose != priv->contentPurpose) {
335         priv->contentHints = contentHints;
336         priv->contentPurpose = contentPurpose;
337         Q_EMIT priv->handle()->contentTypeChanged();
338     }
339 }
340 
setCursorRectangleCallback(wl_client * wlClient,wl_resource * wlResource,int32_t x,int32_t y,int32_t width,int32_t height)341 void TextInputV2::Private::setCursorRectangleCallback([[maybe_unused]] wl_client* wlClient,
342                                                       wl_resource* wlResource,
343                                                       int32_t x,
344                                                       int32_t y,
345                                                       int32_t width,
346                                                       int32_t height)
347 {
348     auto priv = handle(wlResource)->d_ptr;
349     const QRect rect = QRect(x, y, width, height);
350 
351     if (priv->cursorRectangle != rect) {
352         priv->cursorRectangle = rect;
353         Q_EMIT priv->handle()->cursorRectangleChanged(priv->cursorRectangle);
354     }
355 }
356 
setPreferredLanguageCallback(wl_client * wlClient,wl_resource * wlResource,const char * language)357 void TextInputV2::Private::setPreferredLanguageCallback([[maybe_unused]] wl_client* wlClient,
358                                                         wl_resource* wlResource,
359                                                         const char* language)
360 {
361     auto priv = handle(wlResource)->d_ptr;
362     const QByteArray preferredLanguage = QByteArray(language);
363 
364     if (priv->preferredLanguage != preferredLanguage) {
365         priv->preferredLanguage = preferredLanguage;
366         Q_EMIT priv->handle()->preferredLanguageChanged(priv->preferredLanguage);
367     }
368 }
369 
TextInputV2(Client * client,uint32_t version,uint32_t id)370 TextInputV2::TextInputV2(Client* client, uint32_t version, uint32_t id)
371     : QObject(nullptr)
372     , d_ptr(new Private(client, version, id, this))
373 {
374 }
375 
preferredLanguage() const376 QByteArray TextInputV2::preferredLanguage() const
377 {
378     return d_ptr->preferredLanguage;
379 }
380 
contentHints() const381 TextInputV2::ContentHints TextInputV2::contentHints() const
382 {
383     return d_ptr->contentHints;
384 }
385 
contentPurpose() const386 TextInputV2::ContentPurpose TextInputV2::contentPurpose() const
387 {
388 
389     return d_ptr->contentPurpose;
390 }
391 
surroundingText() const392 QByteArray TextInputV2::surroundingText() const
393 {
394     return d_ptr->surroundingText;
395 }
396 
surroundingTextCursorPosition() const397 qint32 TextInputV2::surroundingTextCursorPosition() const
398 {
399     return d_ptr->surroundingTextCursorPosition;
400 }
401 
surroundingTextSelectionAnchor() const402 qint32 TextInputV2::surroundingTextSelectionAnchor() const
403 {
404     return d_ptr->surroundingTextSelectionAnchor;
405 }
406 
preEdit(const QByteArray & text,const QByteArray & commit)407 void TextInputV2::preEdit(const QByteArray& text, const QByteArray& commit)
408 {
409     d_ptr->preEdit(text, commit);
410 }
411 
commit(const QByteArray & text)412 void TextInputV2::commit(const QByteArray& text)
413 {
414     d_ptr->commit(text);
415 }
416 
keysymPressed(quint32 keysym,Qt::KeyboardModifiers modifiers)417 void TextInputV2::keysymPressed(quint32 keysym, Qt::KeyboardModifiers modifiers)
418 {
419     d_ptr->keysymPressed(keysym, modifiers);
420 }
421 
keysymReleased(quint32 keysym,Qt::KeyboardModifiers modifiers)422 void TextInputV2::keysymReleased(quint32 keysym, Qt::KeyboardModifiers modifiers)
423 {
424     d_ptr->keysymReleased(keysym, modifiers);
425 }
426 
deleteSurroundingText(quint32 beforeLength,quint32 afterLength)427 void TextInputV2::deleteSurroundingText(quint32 beforeLength, quint32 afterLength)
428 {
429     d_ptr->deleteSurroundingText(beforeLength, afterLength);
430 }
431 
setCursorPosition(qint32 index,qint32 anchor)432 void TextInputV2::setCursorPosition(qint32 index, qint32 anchor)
433 {
434     d_ptr->setCursorPosition(index, anchor);
435 }
436 
setTextDirection(Qt::LayoutDirection direction)437 void TextInputV2::setTextDirection(Qt::LayoutDirection direction)
438 {
439     d_ptr->setTextDirection(direction);
440 }
441 
setPreEditCursor(qint32 index)442 void TextInputV2::setPreEditCursor(qint32 index)
443 {
444     d_ptr->setPreEditCursor(index);
445 }
446 
setInputPanelState(bool visible,const QRect & overlappedSurfaceArea)447 void TextInputV2::setInputPanelState(bool visible, const QRect& overlappedSurfaceArea)
448 {
449     if (d_ptr->inputPanelVisible == visible
450         && d_ptr->overlappedSurfaceArea == overlappedSurfaceArea) {
451         // not changed
452         return;
453     }
454     d_ptr->inputPanelVisible = visible;
455     d_ptr->overlappedSurfaceArea = overlappedSurfaceArea;
456     d_ptr->sendInputPanelState();
457 }
458 
setLanguage(const QByteArray & languageTag)459 void TextInputV2::setLanguage(const QByteArray& languageTag)
460 {
461     if (d_ptr->language == languageTag) {
462         // not changed
463         return;
464     }
465     d_ptr->language = languageTag;
466     d_ptr->sendLanguage();
467 }
468 
surface() const469 QPointer<Surface> TextInputV2::surface() const
470 {
471     return d_ptr->surface;
472 }
473 
cursorRectangle() const474 QRect TextInputV2::cursorRectangle() const
475 {
476     return d_ptr->cursorRectangle;
477 }
478 
isEnabled() const479 bool TextInputV2::isEnabled() const
480 {
481     return d_ptr->enabled;
482 }
483 
client() const484 Client* TextInputV2::client() const
485 {
486     return d_ptr->client()->handle();
487 }
488 
489 }
490