File indexing completed on 2024-11-10 04:57:35
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"