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