File indexing completed on 2024-05-19 16:35:31

0001 /*
0002     SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 #include "display.h"
0007 #include "seat_interface_p.h"
0008 #include "surface_interface_p.h"
0009 #include "textinput_v2_interface_p.h"
0010 
0011 namespace KWaylandServer
0012 {
0013 
0014 namespace
0015 {
0016 const quint32 s_version = 1;
0017 
0018 // helpers
0019 TextInputContentHints convertContentHint(uint32_t hint)
0020 {
0021     const auto hints = zwp_text_input_v2_content_hint(hint);
0022     TextInputContentHints ret = TextInputContentHint::None;
0023 
0024     if (hints & QtWaylandServer::zwp_text_input_v2::content_hint_auto_completion) {
0025         ret |= TextInputContentHint::AutoCompletion;
0026     }
0027     if (hints & QtWaylandServer::zwp_text_input_v2::content_hint_auto_correction) {
0028         ret |= TextInputContentHint::AutoCorrection;
0029     }
0030     if (hints & QtWaylandServer::zwp_text_input_v2::content_hint_auto_capitalization) {
0031         ret |= TextInputContentHint::AutoCapitalization;
0032     }
0033     if (hints & QtWaylandServer::zwp_text_input_v2::content_hint_lowercase) {
0034         ret |= TextInputContentHint::LowerCase;
0035     }
0036     if (hints & QtWaylandServer::zwp_text_input_v2::content_hint_uppercase) {
0037         ret |= TextInputContentHint::UpperCase;
0038     }
0039     if (hints & QtWaylandServer::zwp_text_input_v2::content_hint_titlecase) {
0040         ret |= TextInputContentHint::TitleCase;
0041     }
0042     if (hints & QtWaylandServer::zwp_text_input_v2::content_hint_hidden_text) {
0043         ret |= TextInputContentHint::HiddenText;
0044     }
0045     if (hints & QtWaylandServer::zwp_text_input_v2::content_hint_sensitive_data) {
0046         ret |= TextInputContentHint::SensitiveData;
0047     }
0048     if (hints & QtWaylandServer::zwp_text_input_v2::content_hint_latin) {
0049         ret |= TextInputContentHint::Latin;
0050     }
0051     if (hints & QtWaylandServer::zwp_text_input_v2::content_hint_multiline) {
0052         ret |= TextInputContentHint::MultiLine;
0053     }
0054     return ret;
0055 }
0056 
0057 TextInputContentPurpose convertContentPurpose(uint32_t purpose)
0058 {
0059     const auto wlPurpose = QtWaylandServer::zwp_text_input_v2::content_purpose(purpose);
0060 
0061     switch (wlPurpose) {
0062     case QtWaylandServer::zwp_text_input_v2::content_purpose_alpha:
0063         return TextInputContentPurpose::Alpha;
0064     case QtWaylandServer::zwp_text_input_v2::content_purpose_digits:
0065         return TextInputContentPurpose::Digits;
0066     case QtWaylandServer::zwp_text_input_v2::content_purpose_number:
0067         return TextInputContentPurpose::Number;
0068     case QtWaylandServer::zwp_text_input_v2::content_purpose_phone:
0069         return TextInputContentPurpose::Phone;
0070     case QtWaylandServer::zwp_text_input_v2::content_purpose_url:
0071         return TextInputContentPurpose::Url;
0072     case QtWaylandServer::zwp_text_input_v2::content_purpose_email:
0073         return TextInputContentPurpose::Email;
0074     case QtWaylandServer::zwp_text_input_v2::content_purpose_name:
0075         return TextInputContentPurpose::Name;
0076     case QtWaylandServer::zwp_text_input_v2::content_purpose_password:
0077         return TextInputContentPurpose::Password;
0078     case QtWaylandServer::zwp_text_input_v2::content_purpose_date:
0079         return TextInputContentPurpose::Date;
0080     case QtWaylandServer::zwp_text_input_v2::content_purpose_time:
0081         return TextInputContentPurpose::Time;
0082     case QtWaylandServer::zwp_text_input_v2::content_purpose_datetime:
0083         return TextInputContentPurpose::DateTime;
0084     case QtWaylandServer::zwp_text_input_v2::content_purpose_terminal:
0085         return TextInputContentPurpose::Terminal;
0086     case QtWaylandServer::zwp_text_input_v2::content_purpose_normal:
0087         return TextInputContentPurpose::Normal;
0088     default:
0089         return TextInputContentPurpose::Normal;
0090     }
0091 }
0092 
0093 class EnabledEmitter
0094 {
0095 public:
0096     EnabledEmitter(TextInputV2Interface *q)
0097         : q(q)
0098         , m_wasEnabled(q->isEnabled())
0099     {
0100     }
0101     ~EnabledEmitter()
0102     {
0103         if (m_wasEnabled != q->isEnabled()) {
0104             Q_EMIT q->enabledChanged();
0105         }
0106     }
0107 
0108 private:
0109     TextInputV2Interface *q;
0110     const bool m_wasEnabled;
0111 };
0112 
0113 }
0114 
0115 TextInputManagerV2InterfacePrivate::TextInputManagerV2InterfacePrivate(TextInputManagerV2Interface *_q, Display *display)
0116     : QtWaylandServer::zwp_text_input_manager_v2(*display, s_version)
0117     , q(_q)
0118 {
0119 }
0120 
0121 void TextInputManagerV2InterfacePrivate::zwp_text_input_manager_v2_destroy(Resource *resource)
0122 {
0123     wl_resource_destroy(resource->handle);
0124 }
0125 
0126 void TextInputManagerV2InterfacePrivate::zwp_text_input_manager_v2_get_text_input(Resource *resource, uint32_t id, wl_resource *seat)
0127 {
0128     SeatInterface *s = SeatInterface::get(seat);
0129     if (!s) {
0130         wl_resource_post_error(resource->handle, 0, "Invalid  seat");
0131         return;
0132     }
0133 
0134     TextInputV2InterfacePrivate *textInputPrivate = TextInputV2InterfacePrivate::get(s->textInputV2());
0135     auto *textInputResource = textInputPrivate->add(resource->client(), id, resource->version());
0136     // Send enter to this new text input object if the surface is already focused.
0137     const quint32 serial = s->display()->nextSerial();
0138     if (textInputPrivate->surface && textInputPrivate->surface->client()->client() == resource->client()) {
0139         textInputPrivate->send_enter(textInputResource->handle, serial, textInputPrivate->surface->resource());
0140     }
0141 }
0142 
0143 TextInputManagerV2Interface::TextInputManagerV2Interface(Display *display, QObject *parent)
0144     : QObject(parent)
0145     , d(new TextInputManagerV2InterfacePrivate(this, display))
0146 {
0147 }
0148 
0149 TextInputManagerV2Interface::~TextInputManagerV2Interface() = default;
0150 
0151 void TextInputV2InterfacePrivate::sendEnter(SurfaceInterface *newSurface, quint32 serial)
0152 {
0153     EnabledEmitter emitter(q);
0154     // It should be always synchronized with SeatInterface::focusedTextInputSurface.
0155     Q_ASSERT(!surface && newSurface);
0156     surface = newSurface;
0157     const auto clientResources = textInputsForClient(newSurface->client());
0158     for (auto resource : clientResources) {
0159         send_enter(resource->handle, serial, newSurface->resource());
0160     }
0161 }
0162 
0163 void TextInputV2InterfacePrivate::sendLeave(quint32 serial, SurfaceInterface *leavingSurface)
0164 {
0165     // It should be always synchronized with SeatInterface::focusedTextInputSurface.
0166     Q_ASSERT(leavingSurface && surface == leavingSurface);
0167     EnabledEmitter emitter(q);
0168     surface.clear();
0169     const auto clientResources = textInputsForClient(leavingSurface->client());
0170     for (auto resource : clientResources) {
0171         send_leave(resource->handle, serial, leavingSurface->resource());
0172     }
0173 }
0174 
0175 void TextInputV2InterfacePrivate::preEdit(const QString &text, const QString &commit)
0176 {
0177     if (!surface) {
0178         return;
0179     }
0180 
0181     const auto clientResources = textInputsForClient(surface->client());
0182     for (auto resource : clientResources) {
0183         send_preedit_string(resource->handle, text, commit);
0184     }
0185 }
0186 
0187 void TextInputV2InterfacePrivate::preEditStyling(uint32_t index, uint32_t length, uint32_t style)
0188 {
0189     if (!surface) {
0190         return;
0191     }
0192 
0193     const auto clientResources = textInputsForClient(surface->client());
0194     for (auto resource : clientResources) {
0195         send_preedit_styling(resource->handle, index, length, style);
0196     }
0197 }
0198 
0199 void TextInputV2InterfacePrivate::commitString(const QString &text)
0200 {
0201     if (!surface) {
0202         return;
0203     }
0204     const QList<Resource *> textInputs = textInputsForClient(surface->client());
0205     for (auto resource : textInputs) {
0206         send_commit_string(resource->handle, text);
0207     }
0208 }
0209 
0210 void TextInputV2InterfacePrivate::keysymPressed(quint32 keysym, quint32 modifiers)
0211 {
0212     if (!surface) {
0213         return;
0214     }
0215 
0216     const QList<Resource *> textInputs = textInputsForClient(surface->client());
0217     for (auto resource : textInputs) {
0218         send_keysym(resource->handle, seat ? seat->timestamp().count() : 0, keysym, WL_KEYBOARD_KEY_STATE_PRESSED, modifiers);
0219     }
0220 }
0221 
0222 void TextInputV2InterfacePrivate::keysymReleased(quint32 keysym, quint32 modifiers)
0223 {
0224     if (!surface) {
0225         return;
0226     }
0227 
0228     const QList<Resource *> textInputs = textInputsForClient(surface->client());
0229     for (auto resource : textInputs) {
0230         send_keysym(resource->handle, seat ? seat->timestamp().count() : 0, keysym, WL_KEYBOARD_KEY_STATE_RELEASED, modifiers);
0231     }
0232 }
0233 
0234 void TextInputV2InterfacePrivate::deleteSurroundingText(quint32 beforeLength, quint32 afterLength)
0235 {
0236     if (!surface) {
0237         return;
0238     }
0239     const QList<Resource *> textInputs = textInputsForClient(surface->client());
0240     for (auto resource : textInputs) {
0241         send_delete_surrounding_text(resource->handle, beforeLength, afterLength);
0242     }
0243 }
0244 
0245 void TextInputV2InterfacePrivate::setCursorPosition(qint32 index, qint32 anchor)
0246 {
0247     if (!surface) {
0248         return;
0249     }
0250     const QList<Resource *> textInputs = textInputsForClient(surface->client());
0251     for (auto resource : textInputs) {
0252         send_cursor_position(resource->handle, index, anchor);
0253     }
0254 }
0255 
0256 void TextInputV2InterfacePrivate::setTextDirection(Qt::LayoutDirection direction)
0257 {
0258     if (!surface) {
0259         return;
0260     }
0261     text_direction wlDirection;
0262     switch (direction) {
0263     case Qt::LeftToRight:
0264         wlDirection = text_direction::text_direction_ltr;
0265         break;
0266     case Qt::RightToLeft:
0267         wlDirection = text_direction::text_direction_rtl;
0268         break;
0269     case Qt::LayoutDirectionAuto:
0270         wlDirection = text_direction::text_direction_auto;
0271         break;
0272     default:
0273         Q_UNREACHABLE();
0274         break;
0275     }
0276     const QList<Resource *> textInputs = textInputsForClient(surface->client());
0277     for (auto resource : textInputs) {
0278         send_text_direction(resource->handle, wlDirection);
0279     }
0280 }
0281 
0282 void TextInputV2InterfacePrivate::setPreEditCursor(qint32 index)
0283 {
0284     if (!surface) {
0285         return;
0286     }
0287     const QList<Resource *> textInputs = textInputsForClient(surface->client());
0288     for (auto resource : textInputs) {
0289         send_preedit_cursor(resource->handle, index);
0290     }
0291 }
0292 
0293 void TextInputV2InterfacePrivate::sendInputPanelState()
0294 {
0295     if (!surface) {
0296         return;
0297     }
0298     const QList<Resource *> textInputs = textInputsForClient(surface->client());
0299     for (auto resource : textInputs) {
0300         send_input_panel_state(resource->handle,
0301                                inputPanelVisible ? ZWP_TEXT_INPUT_V2_INPUT_PANEL_VISIBILITY_VISIBLE : ZWP_TEXT_INPUT_V2_INPUT_PANEL_VISIBILITY_HIDDEN,
0302                                overlappedSurfaceArea.x(),
0303                                overlappedSurfaceArea.y(),
0304                                overlappedSurfaceArea.width(),
0305                                overlappedSurfaceArea.height());
0306     }
0307 }
0308 
0309 void TextInputV2InterfacePrivate::sendLanguage()
0310 {
0311     if (!surface) {
0312         return;
0313     }
0314     const QList<Resource *> textInputs = textInputsForClient(surface->client());
0315     for (auto resource : textInputs) {
0316         send_language(resource->handle, language);
0317     }
0318 }
0319 
0320 void TextInputV2InterfacePrivate::sendModifiersMap()
0321 {
0322     if (!surface) {
0323         return;
0324     }
0325     const QList<Resource *> textInputs = textInputsForClient(surface->client());
0326     for (auto resource : textInputs) {
0327         send_modifiers_map(resource->handle, modifiersMap);
0328     }
0329 }
0330 
0331 TextInputV2InterfacePrivate::TextInputV2InterfacePrivate(SeatInterface *seat, TextInputV2Interface *_q)
0332     : seat(seat)
0333     , q(_q)
0334 {
0335 }
0336 
0337 void TextInputV2InterfacePrivate::zwp_text_input_v2_enable(Resource *resource, wl_resource *s)
0338 {
0339     EnabledEmitter emitter(q);
0340     auto enabledSurface = SurfaceInterface::get(s);
0341     if (m_enabledSurfaces.contains(enabledSurface)) {
0342         return;
0343     }
0344     m_enabledSurfaces.insert(enabledSurface);
0345     QObject::connect(enabledSurface, &SurfaceInterface::aboutToBeDestroyed, q, [this, enabledSurface] {
0346         EnabledEmitter emitter(q);
0347         m_enabledSurfaces.remove(enabledSurface);
0348     });
0349 }
0350 
0351 void TextInputV2InterfacePrivate::zwp_text_input_v2_disable(Resource *resource, wl_resource *s)
0352 {
0353     EnabledEmitter emitter(q);
0354     auto disabledSurface = SurfaceInterface::get(s);
0355     QObject::disconnect(disabledSurface, &SurfaceInterface::aboutToBeDestroyed, q, nullptr);
0356     m_enabledSurfaces.remove(disabledSurface);
0357     if (disabledSurface == surface) {
0358         q->setInputPanelState(false, {0, 0, 0, 0});
0359     }
0360 }
0361 
0362 void TextInputV2InterfacePrivate::zwp_text_input_v2_update_state(Resource *resource, uint32_t serial, uint32_t reason)
0363 {
0364     Q_EMIT q->stateUpdated(serial, TextInputV2Interface::UpdateReason(reason));
0365 }
0366 
0367 void TextInputV2InterfacePrivate::zwp_text_input_v2_hide_input_panel(Resource *resource)
0368 {
0369     Q_EMIT q->requestHideInputPanel();
0370 }
0371 
0372 void TextInputV2InterfacePrivate::zwp_text_input_v2_set_surrounding_text(Resource *resource, const QString &text, int32_t cursor, int32_t anchor)
0373 {
0374     surroundingText = text;
0375     surroundingTextCursorPosition = cursor;
0376     surroundingTextSelectionAnchor = anchor;
0377     Q_EMIT q->surroundingTextChanged();
0378 }
0379 
0380 void TextInputV2InterfacePrivate::zwp_text_input_v2_set_content_type(Resource *resource, uint32_t hint, uint32_t purpose)
0381 {
0382     const auto contentHints = convertContentHint(hint);
0383     const auto contentPurpose = convertContentPurpose(purpose);
0384     if (this->contentHints != contentHints || this->contentPurpose != contentPurpose) {
0385         this->contentHints = contentHints;
0386         this->contentPurpose = contentPurpose;
0387         Q_EMIT q->contentTypeChanged();
0388     }
0389 }
0390 
0391 void TextInputV2InterfacePrivate::zwp_text_input_v2_set_cursor_rectangle(Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height)
0392 {
0393     const QRect rect = QRect(x, y, width, height);
0394     if (cursorRectangle != rect) {
0395         cursorRectangle = rect;
0396         Q_EMIT q->cursorRectangleChanged(cursorRectangle);
0397     }
0398 }
0399 
0400 void TextInputV2InterfacePrivate::zwp_text_input_v2_set_preferred_language(Resource *resource, const QString &language)
0401 {
0402     if (preferredLanguage != language) {
0403         preferredLanguage = language;
0404         Q_EMIT q->preferredLanguageChanged(preferredLanguage);
0405     }
0406 }
0407 
0408 void TextInputV2InterfacePrivate::zwp_text_input_v2_show_input_panel(Resource *resource)
0409 {
0410     Q_EMIT q->requestShowInputPanel();
0411 }
0412 
0413 QList<TextInputV2InterfacePrivate::Resource *> TextInputV2InterfacePrivate::textInputsForClient(ClientConnection *client) const
0414 {
0415     return resourceMap().values(client->client());
0416 }
0417 
0418 TextInputV2Interface::TextInputV2Interface(SeatInterface *seat)
0419     : QObject(seat)
0420     , d(new TextInputV2InterfacePrivate(seat, this))
0421 {
0422 }
0423 
0424 TextInputV2Interface::~TextInputV2Interface() = default;
0425 
0426 QString TextInputV2Interface::preferredLanguage() const
0427 {
0428     return d->preferredLanguage;
0429 }
0430 
0431 TextInputContentHints TextInputV2Interface::contentHints() const
0432 {
0433     return d->contentHints;
0434 }
0435 
0436 TextInputContentPurpose KWaylandServer::TextInputV2Interface::contentPurpose() const
0437 {
0438     return d->contentPurpose;
0439 }
0440 
0441 QString TextInputV2Interface::surroundingText() const
0442 {
0443     return d->surroundingText;
0444 }
0445 
0446 qint32 TextInputV2Interface::surroundingTextCursorPosition() const
0447 {
0448     return d->surroundingTextCursorPosition;
0449 }
0450 
0451 qint32 TextInputV2Interface::surroundingTextSelectionAnchor() const
0452 {
0453     return d->surroundingTextSelectionAnchor;
0454 }
0455 
0456 void TextInputV2Interface::preEdit(const QString &text, const QString &commit)
0457 {
0458     d->preEdit(text, commit);
0459 }
0460 
0461 void TextInputV2Interface::preEditStyling(uint32_t index, uint32_t length, uint32_t style)
0462 {
0463     d->preEditStyling(index, length, style);
0464 }
0465 
0466 void TextInputV2Interface::commitString(const QString &text)
0467 {
0468     d->commitString(text);
0469 }
0470 
0471 void TextInputV2Interface::keysymPressed(quint32 keysym, quint32 modifiers)
0472 {
0473     d->keysymPressed(keysym, modifiers);
0474 }
0475 
0476 void TextInputV2Interface::keysymReleased(quint32 keysym, quint32 modifiers)
0477 {
0478     d->keysymReleased(keysym, modifiers);
0479 }
0480 
0481 void TextInputV2Interface::deleteSurroundingText(quint32 beforeLength, quint32 afterLength)
0482 {
0483     d->deleteSurroundingText(beforeLength, afterLength);
0484 }
0485 
0486 void TextInputV2Interface::setCursorPosition(qint32 index, qint32 anchor)
0487 {
0488     d->setCursorPosition(index, anchor);
0489 }
0490 
0491 void TextInputV2Interface::setTextDirection(Qt::LayoutDirection direction)
0492 {
0493     d->setTextDirection(direction);
0494 }
0495 
0496 void TextInputV2Interface::setPreEditCursor(qint32 index)
0497 {
0498     d->setPreEditCursor(index);
0499 }
0500 
0501 void TextInputV2Interface::setInputPanelState(bool visible, const QRect &overlappedSurfaceArea)
0502 {
0503     if (d->inputPanelVisible == visible && d->overlappedSurfaceArea == overlappedSurfaceArea) {
0504         // not changed
0505         return;
0506     }
0507     d->inputPanelVisible = visible;
0508     d->overlappedSurfaceArea = overlappedSurfaceArea;
0509     d->sendInputPanelState();
0510 }
0511 
0512 void TextInputV2Interface::setLanguage(const QString &languageTag)
0513 {
0514     if (d->language == languageTag) {
0515         // not changed
0516         return;
0517     }
0518     d->language = languageTag;
0519     d->sendLanguage();
0520 }
0521 
0522 void TextInputV2Interface::setModifiersMap(const QByteArray &modifiersMap)
0523 {
0524     if (d->modifiersMap == modifiersMap) {
0525         // not changed
0526         return;
0527     }
0528     d->modifiersMap = modifiersMap;
0529     d->sendModifiersMap();
0530 }
0531 
0532 QPointer<SurfaceInterface> TextInputV2Interface::surface() const
0533 {
0534     if (!d->surface) {
0535         return nullptr;
0536     }
0537 
0538     if (!d->resourceMap().contains(d->surface->client()->client())) {
0539         return nullptr;
0540     }
0541 
0542     return d->surface;
0543 }
0544 
0545 QRect TextInputV2Interface::cursorRectangle() const
0546 {
0547     return d->cursorRectangle;
0548 }
0549 
0550 bool TextInputV2Interface::isEnabled() const
0551 {
0552     return d->surface && d->m_enabledSurfaces.contains(d->surface);
0553 }
0554 
0555 bool TextInputV2Interface::clientSupportsTextInput(ClientConnection *client) const
0556 {
0557     return client && d->resourceMap().contains(*client);
0558 }
0559 }