File indexing completed on 2024-05-19 05:32:45

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