File indexing completed on 2024-11-10 04:57:35
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_p.h" 0008 #include "surface_p.h" 0009 #include "textinput_v2_p.h" 0010 0011 namespace KWin 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 = QtWaylandServer::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 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 } 0560 0561 #include "moc_textinput_v2.cpp"