File indexing completed on 2024-11-10 04:56:23
0001 /* 0002 SPDX-FileCopyrightText: 2020 Bhushan Shah <bshah@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0005 */ 0006 0007 #include <QSignalSpy> 0008 #include <QTest> 0009 #include <QThread> 0010 0011 #include "wayland/compositor.h" 0012 #include "wayland/display.h" 0013 #include "wayland/seat.h" 0014 #include "wayland/surface.h" 0015 #include "wayland/textinput_v3.h" 0016 0017 #include "KWayland/Client/compositor.h" 0018 #include "KWayland/Client/connection_thread.h" 0019 #include "KWayland/Client/event_queue.h" 0020 #include "KWayland/Client/registry.h" 0021 #include "KWayland/Client/seat.h" 0022 #include "KWayland/Client/surface.h" 0023 0024 #include "qwayland-text-input-unstable-v3.h" 0025 0026 using namespace KWin; 0027 0028 Q_DECLARE_METATYPE(QtWayland::zwp_text_input_v3::content_purpose) 0029 Q_DECLARE_METATYPE(QtWayland::zwp_text_input_v3::content_hint) 0030 0031 class TextInputV3 : public QObject, public QtWayland::zwp_text_input_v3 0032 { 0033 Q_OBJECT 0034 Q_SIGNALS: 0035 void surface_enter(wl_surface *surface); 0036 void surface_leave(wl_surface *surface); 0037 void commit_string(const QString &text); 0038 void delete_surrounding_text(quint32 before_length, quint32 after_length); 0039 void preedit_string(const QString &text, quint32 cursor_begin, quint32 cursor_end); 0040 void done(quint32 serial); 0041 0042 public: 0043 ~TextInputV3() override 0044 { 0045 destroy(); 0046 } 0047 void zwp_text_input_v3_enter(struct ::wl_surface *surface) override 0048 { 0049 Q_EMIT surface_enter(surface); 0050 } 0051 void zwp_text_input_v3_leave(struct ::wl_surface *surface) override 0052 { 0053 Q_EMIT surface_leave(surface); 0054 } 0055 void zwp_text_input_v3_commit_string(const QString &text) override 0056 { 0057 commitText = text; 0058 } 0059 void zwp_text_input_v3_delete_surrounding_text(uint32_t before_length, uint32_t after_length) override 0060 { 0061 before = before_length; 0062 after = after_length; 0063 } 0064 void zwp_text_input_v3_done(uint32_t serial) override 0065 { 0066 Q_EMIT commit_string(commitText); 0067 Q_EMIT preedit_string(preeditText, cursorBegin, cursorEnd); 0068 Q_EMIT delete_surrounding_text(before, after); 0069 Q_EMIT done(serial); 0070 } 0071 void zwp_text_input_v3_preedit_string(const QString &text, int32_t cursor_begin, int32_t cursor_end) override 0072 { 0073 preeditText = text; 0074 cursorBegin = cursor_begin; 0075 cursorEnd = cursor_end; 0076 } 0077 0078 private: 0079 QString preeditText; 0080 QString commitText; 0081 uint32_t cursorBegin, cursorEnd; 0082 uint32_t before, after; 0083 }; 0084 0085 class TextInputManagerV3 : public QtWayland::zwp_text_input_manager_v3 0086 { 0087 public: 0088 ~TextInputManagerV3() override 0089 { 0090 destroy(); 0091 } 0092 }; 0093 0094 class TestTextInputV3Interface : public QObject 0095 { 0096 Q_OBJECT 0097 0098 public: 0099 ~TestTextInputV3Interface() override; 0100 0101 private Q_SLOTS: 0102 void initTestCase(); 0103 void testEnableDisable(); 0104 void testEvents(); 0105 void testContentPurpose_data(); 0106 void testContentPurpose(); 0107 void testContentHints_data(); 0108 void testContentHints(); 0109 void testMultipleTextinputs(); 0110 0111 private: 0112 KWayland::Client::ConnectionThread *m_connection; 0113 KWayland::Client::EventQueue *m_queue; 0114 KWayland::Client::Compositor *m_clientCompositor; 0115 KWayland::Client::Seat *m_clientSeat = nullptr; 0116 0117 SeatInterface *m_seat; 0118 QThread *m_thread; 0119 KWin::Display m_display; 0120 TextInputV3 *m_clientTextInputV3; 0121 CompositorInterface *m_serverCompositor; 0122 TextInputV3Interface *m_serverTextInputV3; 0123 TextInputManagerV3 *m_clientTextInputManagerV3; 0124 0125 quint32 m_totalCommits = 0; 0126 }; 0127 0128 static const QString s_socketName = QStringLiteral("kwin-wayland-server-text-input-v3-test-0"); 0129 0130 void TestTextInputV3Interface::initTestCase() 0131 { 0132 m_display.addSocketName(s_socketName); 0133 m_display.start(); 0134 QVERIFY(m_display.isRunning()); 0135 0136 m_seat = new SeatInterface(&m_display, this); 0137 m_seat->setHasKeyboard(true); 0138 0139 m_serverCompositor = new CompositorInterface(&m_display, this); 0140 new TextInputManagerV3Interface(&m_display); 0141 0142 m_connection = new KWayland::Client::ConnectionThread; 0143 QSignalSpy connectedSpy(m_connection, &KWayland::Client::ConnectionThread::connected); 0144 m_connection->setSocketName(s_socketName); 0145 0146 m_thread = new QThread(this); 0147 m_connection->moveToThread(m_thread); 0148 m_thread->start(); 0149 0150 m_connection->initConnection(); 0151 QVERIFY(connectedSpy.wait()); 0152 QVERIFY(!m_connection->connections().isEmpty()); 0153 0154 m_queue = new KWayland::Client::EventQueue(this); 0155 QVERIFY(!m_queue->isValid()); 0156 m_queue->setup(m_connection); 0157 QVERIFY(m_queue->isValid()); 0158 0159 auto registry = new KWayland::Client::Registry(this); 0160 connect(registry, &KWayland::Client::Registry::interfaceAnnounced, this, [this, registry](const QByteArray &interface, quint32 id, quint32 version) { 0161 if (interface == QByteArrayLiteral("zwp_text_input_manager_v3")) { 0162 m_clientTextInputManagerV3 = new TextInputManagerV3(); 0163 m_clientTextInputManagerV3->init(*registry, id, version); 0164 } 0165 }); 0166 0167 connect(registry, &KWayland::Client::Registry::seatAnnounced, this, [this, registry](quint32 name, quint32 version) { 0168 m_clientSeat = registry->createSeat(name, version); 0169 }); 0170 0171 QSignalSpy allAnnouncedSpy(registry, &KWayland::Client::Registry::interfaceAnnounced); 0172 QSignalSpy compositorSpy(registry, &KWayland::Client::Registry::compositorAnnounced); 0173 QSignalSpy shmSpy(registry, &KWayland::Client::Registry::shmAnnounced); 0174 registry->setEventQueue(m_queue); 0175 registry->create(m_connection->display()); 0176 QVERIFY(registry->isValid()); 0177 registry->setup(); 0178 QVERIFY(allAnnouncedSpy.wait()); 0179 0180 m_clientCompositor = registry->createCompositor(compositorSpy.first().first().value<quint32>(), compositorSpy.first().last().value<quint32>(), this); 0181 QVERIFY(m_clientCompositor->isValid()); 0182 // create a text input v3 0183 m_clientTextInputV3 = new TextInputV3(); 0184 m_clientTextInputV3->init(m_clientTextInputManagerV3->get_text_input(*m_clientSeat)); 0185 QVERIFY(m_clientTextInputV3); 0186 } 0187 0188 TestTextInputV3Interface::~TestTextInputV3Interface() 0189 { 0190 if (m_clientTextInputV3) { 0191 delete m_clientTextInputV3; 0192 m_clientTextInputV3 = nullptr; 0193 } 0194 if (m_clientTextInputManagerV3) { 0195 delete m_clientTextInputManagerV3; 0196 m_clientTextInputManagerV3 = nullptr; 0197 } 0198 if (m_queue) { 0199 delete m_queue; 0200 m_queue = nullptr; 0201 } 0202 if (m_thread) { 0203 m_thread->quit(); 0204 m_thread->wait(); 0205 delete m_thread; 0206 m_thread = nullptr; 0207 } 0208 m_connection->deleteLater(); 0209 m_connection = nullptr; 0210 } 0211 0212 // Ensures that enable disable events don't fire without commit 0213 void TestTextInputV3Interface::testEnableDisable() 0214 { 0215 // create a surface 0216 QSignalSpy serverSurfaceCreatedSpy(m_serverCompositor, &CompositorInterface::surfaceCreated); 0217 std::unique_ptr<KWayland::Client::Surface> clientSurface(m_clientCompositor->createSurface(this)); 0218 QVERIFY(serverSurfaceCreatedSpy.wait()); 0219 SurfaceInterface *serverSurface = serverSurfaceCreatedSpy.first().first().value<SurfaceInterface *>(); 0220 QVERIFY(serverSurface); 0221 0222 m_serverTextInputV3 = m_seat->textInputV3(); 0223 QVERIFY(m_serverTextInputV3); 0224 0225 QSignalSpy focusedSurfaceChangedSpy(m_seat, &SeatInterface::focusedTextInputSurfaceChanged); 0226 QSignalSpy textInputEnabledSpy(m_serverTextInputV3, &TextInputV3Interface::enabledChanged); 0227 QSignalSpy cursorRectangleChangedSpy(m_serverTextInputV3, &TextInputV3Interface::cursorRectangleChanged); 0228 QSignalSpy enableRequestedSpy(m_serverTextInputV3, &TextInputV3Interface::enableRequested); 0229 0230 QSignalSpy surfaceEnterSpy(m_clientTextInputV3, &TextInputV3::surface_enter); 0231 QSignalSpy surfaceLeaveSpy(m_clientTextInputV3, &TextInputV3::surface_leave); 0232 0233 // Enter the textinput 0234 0235 QCOMPARE(focusedSurfaceChangedSpy.count(), 0); 0236 0237 // Make sure that entering surface does not trigger the text input 0238 m_seat->setFocusedTextInputSurface(serverSurface); 0239 QVERIFY(surfaceEnterSpy.wait()); 0240 QCOMPARE(surfaceEnterSpy.count(), 1); 0241 QCOMPARE(focusedSurfaceChangedSpy.count(), 1); 0242 QCOMPARE(textInputEnabledSpy.count(), 0); 0243 0244 // Now enable the textInput, we should not get event just yet 0245 m_clientTextInputV3->enable(); 0246 m_clientTextInputV3->set_cursor_rectangle(0, 0, 20, 20); 0247 m_clientTextInputV3->set_surrounding_text("KDE Plasma Desktop", 0, 3); 0248 QCOMPARE(textInputEnabledSpy.count(), 0); 0249 QCOMPARE(cursorRectangleChangedSpy.count(), 0); 0250 0251 // after we do commit we should get event 0252 m_clientTextInputV3->commit(); 0253 QVERIFY(textInputEnabledSpy.wait()); 0254 m_totalCommits++; 0255 0256 QCOMPARE(enableRequestedSpy.count(), 0); 0257 QCOMPARE(textInputEnabledSpy.count(), 1); 0258 QCOMPARE(cursorRectangleChangedSpy.count(), 1); 0259 QCOMPARE(m_serverTextInputV3->cursorRectangle(), QRect(0, 0, 20, 20)); 0260 QCOMPARE(m_serverTextInputV3->surroundingText(), QString("KDE Plasma Desktop")); 0261 QCOMPARE(m_serverTextInputV3->surroundingTextCursorPosition(), 0); 0262 QCOMPARE(m_serverTextInputV3->surroundingTextSelectionAnchor(), 3); 0263 0264 // Do another enable when it's already enabled. 0265 m_clientTextInputV3->enable(); 0266 m_clientTextInputV3->set_cursor_rectangle(0, 0, 20, 20); 0267 m_clientTextInputV3->set_surrounding_text("KDE Plasma Desktop", 0, 3); 0268 m_clientTextInputV3->commit(); 0269 QVERIFY(enableRequestedSpy.wait()); 0270 QCOMPARE(textInputEnabledSpy.count(), 1); 0271 QCOMPARE(cursorRectangleChangedSpy.count(), 1); 0272 QCOMPARE(m_serverTextInputV3->cursorRectangle(), QRect(0, 0, 20, 20)); 0273 QCOMPARE(m_serverTextInputV3->surroundingText(), QString("KDE Plasma Desktop")); 0274 QCOMPARE(m_serverTextInputV3->surroundingTextCursorPosition(), 0); 0275 QCOMPARE(m_serverTextInputV3->surroundingTextSelectionAnchor(), 3); 0276 m_totalCommits++; 0277 0278 // disabling we should not get the event 0279 m_clientTextInputV3->disable(); 0280 QCOMPARE(textInputEnabledSpy.count(), 1); 0281 0282 // after we do commit we should get event 0283 m_clientTextInputV3->commit(); 0284 QVERIFY(textInputEnabledSpy.wait()); 0285 QCOMPARE(textInputEnabledSpy.count(), 2); 0286 m_totalCommits++; 0287 0288 // Lets try leaving the surface and make sure event propogage 0289 m_seat->setFocusedTextInputSurface(nullptr); 0290 QVERIFY(surfaceLeaveSpy.wait()); 0291 QCOMPARE(surfaceLeaveSpy.count(), 1); 0292 } 0293 0294 void TestTextInputV3Interface::testEvents() 0295 { 0296 // create a surface 0297 QSignalSpy serverSurfaceCreatedSpy(m_serverCompositor, &CompositorInterface::surfaceCreated); 0298 std::unique_ptr<KWayland::Client::Surface> clientSurface(m_clientCompositor->createSurface(this)); 0299 QVERIFY(serverSurfaceCreatedSpy.wait()); 0300 SurfaceInterface *serverSurface = serverSurfaceCreatedSpy.first().first().value<SurfaceInterface *>(); 0301 QVERIFY(serverSurface); 0302 0303 m_serverTextInputV3 = m_seat->textInputV3(); 0304 QVERIFY(m_serverTextInputV3); 0305 0306 QSignalSpy focusedSurfaceChangedSpy(m_seat, &SeatInterface::focusedTextInputSurfaceChanged); 0307 QSignalSpy textInputEnabledSpy(m_serverTextInputV3, &TextInputV3Interface::enabledChanged); 0308 QSignalSpy doneSpy(m_clientTextInputV3, &TextInputV3::done); 0309 0310 // Enter the textinput 0311 QCOMPARE(focusedSurfaceChangedSpy.count(), 0); 0312 0313 // Make sure that entering surface does not trigger the text input 0314 m_seat->setFocusedTextInputSurface(serverSurface); 0315 // FIXME: somehow this triggers BEFORE setFocusedTextInputSurface returns :( 0316 // QVERIFY(focusedSurfaceChangedSpy.wait()); 0317 QCOMPARE(focusedSurfaceChangedSpy.count(), 1); 0318 0319 // Now enable the textInput 0320 m_clientTextInputV3->enable(); 0321 m_clientTextInputV3->commit(); 0322 m_totalCommits++; 0323 QVERIFY(textInputEnabledSpy.wait()); 0324 QVERIFY(doneSpy.wait()); 0325 QCOMPARE(doneSpy.count(), 1); 0326 0327 QSignalSpy preEditSpy(m_clientTextInputV3, &TextInputV3::preedit_string); 0328 QSignalSpy commitStringSpy(m_clientTextInputV3, &TextInputV3::commit_string); 0329 QSignalSpy deleteSurroundingSpy(m_clientTextInputV3, &TextInputV3::delete_surrounding_text); 0330 0331 m_serverTextInputV3->sendPreEditString("Hello KDE community!", 1, 2); 0332 m_serverTextInputV3->deleteSurroundingText(6, 10); 0333 m_serverTextInputV3->commitString("Plasma"); 0334 m_serverTextInputV3->done(); 0335 0336 QVERIFY(doneSpy.wait()); 0337 QCOMPARE(doneSpy.count(), 2); 0338 QCOMPARE(preEditSpy.count(), 1); 0339 QCOMPARE(commitStringSpy.count(), 1); 0340 QCOMPARE(deleteSurroundingSpy.count(), 1); 0341 0342 QCOMPARE(preEditSpy.last().at(0).value<QString>(), "Hello KDE community!"); 0343 QCOMPARE(preEditSpy.last().at(1).value<quint32>(), 1); 0344 QCOMPARE(preEditSpy.last().at(2).value<quint32>(), 2); 0345 QCOMPARE(commitStringSpy.last().at(0).value<QString>(), "Plasma"); 0346 QCOMPARE(deleteSurroundingSpy.last().at(0).value<quint32>(), 6); 0347 QCOMPARE(deleteSurroundingSpy.last().at(1).value<quint32>(), 10); 0348 0349 // zwp_text_input_v3.done event have serial of total commits 0350 QCOMPARE(doneSpy.last().at(0).value<quint32>(), m_totalCommits); 0351 0352 // Now disable the textInput 0353 m_clientTextInputV3->disable(); 0354 m_clientTextInputV3->commit(); 0355 m_totalCommits++; 0356 QVERIFY(textInputEnabledSpy.wait()); 0357 } 0358 0359 void TestTextInputV3Interface::testContentPurpose_data() 0360 { 0361 QTest::addColumn<QtWayland::zwp_text_input_v3::content_purpose>("clientPurpose"); 0362 QTest::addColumn<KWin::TextInputContentPurpose>("serverPurpose"); 0363 0364 QTest::newRow("Alpha") << QtWayland::zwp_text_input_v3::content_purpose_alpha << TextInputContentPurpose::Alpha; 0365 QTest::newRow("Digits") << QtWayland::zwp_text_input_v3::content_purpose_digits << TextInputContentPurpose::Digits; 0366 QTest::newRow("Number") << QtWayland::zwp_text_input_v3::content_purpose_number << TextInputContentPurpose::Number; 0367 QTest::newRow("Phone") << QtWayland::zwp_text_input_v3::content_purpose_phone << TextInputContentPurpose::Phone; 0368 QTest::newRow("Url") << QtWayland::zwp_text_input_v3::content_purpose_url << TextInputContentPurpose::Url; 0369 QTest::newRow("Email") << QtWayland::zwp_text_input_v3::content_purpose_email << TextInputContentPurpose::Email; 0370 QTest::newRow("Name") << QtWayland::zwp_text_input_v3::content_purpose_name << TextInputContentPurpose::Name; 0371 QTest::newRow("Password") << QtWayland::zwp_text_input_v3::content_purpose_password << TextInputContentPurpose::Password; 0372 QTest::newRow("Pin") << QtWayland::zwp_text_input_v3::content_purpose_pin << TextInputContentPurpose::Pin; 0373 QTest::newRow("Date") << QtWayland::zwp_text_input_v3::content_purpose_date << TextInputContentPurpose::Date; 0374 QTest::newRow("Time") << QtWayland::zwp_text_input_v3::content_purpose_time << TextInputContentPurpose::Time; 0375 QTest::newRow("DateTime") << QtWayland::zwp_text_input_v3::content_purpose_datetime << TextInputContentPurpose::DateTime; 0376 QTest::newRow("Terminal") << QtWayland::zwp_text_input_v3::content_purpose_terminal << TextInputContentPurpose::Terminal; 0377 } 0378 0379 void TestTextInputV3Interface::testContentPurpose() 0380 { 0381 // create a surface 0382 QSignalSpy serverSurfaceCreatedSpy(m_serverCompositor, &CompositorInterface::surfaceCreated); 0383 std::unique_ptr<KWayland::Client::Surface> clientSurface(m_clientCompositor->createSurface(this)); 0384 QVERIFY(serverSurfaceCreatedSpy.wait()); 0385 SurfaceInterface *serverSurface = serverSurfaceCreatedSpy.first().first().value<SurfaceInterface *>(); 0386 QVERIFY(serverSurface); 0387 0388 m_serverTextInputV3 = m_seat->textInputV3(); 0389 QVERIFY(m_serverTextInputV3); 0390 0391 QSignalSpy focusedSurfaceChangedSpy(m_seat, &SeatInterface::focusedTextInputSurfaceChanged); 0392 QSignalSpy textInputEnabledSpy(m_serverTextInputV3, &TextInputV3Interface::enabledChanged); 0393 0394 // Enter the textinput 0395 QCOMPARE(focusedSurfaceChangedSpy.count(), 0); 0396 0397 // Make sure that entering surface does not trigger the text input 0398 m_seat->setFocusedTextInputSurface(serverSurface); 0399 // FIXME: somehow this triggers BEFORE setFocusedTextInputSurface returns :( 0400 // QVERIFY(focusedSurfaceChangedSpy.wait()); 0401 QCOMPARE(focusedSurfaceChangedSpy.count(), 1); 0402 0403 // Now enable the textInput 0404 m_clientTextInputV3->enable(); 0405 m_clientTextInputV3->commit(); 0406 QVERIFY(textInputEnabledSpy.wait()); 0407 m_totalCommits++; 0408 0409 // Default should be normal content purpose 0410 QCOMPARE(m_serverTextInputV3->contentPurpose(), TextInputContentPurpose::Normal); 0411 0412 QSignalSpy contentTypeChangedSpy(m_serverTextInputV3, &TextInputV3Interface::contentTypeChanged); 0413 0414 QFETCH(QtWayland::zwp_text_input_v3::content_purpose, clientPurpose); 0415 m_clientTextInputV3->enable(); 0416 m_clientTextInputV3->set_content_type(QtWayland::zwp_text_input_v3::content_hint_none, clientPurpose); 0417 m_clientTextInputV3->commit(); 0418 QVERIFY(contentTypeChangedSpy.wait()); 0419 QTEST(m_serverTextInputV3->contentPurpose(), "serverPurpose"); 0420 m_totalCommits++; 0421 0422 // Setting same thing should not trigger update 0423 m_clientTextInputV3->enable(); 0424 m_clientTextInputV3->set_content_type(QtWayland::zwp_text_input_v3::content_hint_none, clientPurpose); 0425 m_clientTextInputV3->commit(); 0426 QVERIFY(!contentTypeChangedSpy.wait(100)); 0427 m_totalCommits++; 0428 0429 // unset to normal 0430 m_clientTextInputV3->enable(); 0431 m_clientTextInputV3->set_content_type(QtWayland::zwp_text_input_v3::content_hint_none, QtWayland::zwp_text_input_v3::content_purpose_normal); 0432 m_clientTextInputV3->commit(); 0433 QVERIFY(contentTypeChangedSpy.wait()); 0434 m_totalCommits++; 0435 QCOMPARE(m_serverTextInputV3->contentPurpose(), TextInputContentPurpose::Normal); 0436 0437 // Now disable the textInput 0438 m_clientTextInputV3->disable(); 0439 m_clientTextInputV3->commit(); 0440 m_totalCommits++; 0441 QVERIFY(textInputEnabledSpy.wait()); 0442 } 0443 0444 void TestTextInputV3Interface::testContentHints_data() 0445 { 0446 QTest::addColumn<quint32>("clientHint"); 0447 QTest::addColumn<KWin::TextInputContentHints>("serverHints"); 0448 0449 QTest::addRow("Spellcheck") << quint32(QtWayland::zwp_text_input_v3::content_hint_spellcheck) 0450 << TextInputContentHints(TextInputContentHint::AutoCorrection); 0451 QTest::addRow("Completion") << quint32(QtWayland::zwp_text_input_v3::content_hint_completion) 0452 << TextInputContentHints(TextInputContentHint::AutoCompletion); 0453 QTest::addRow("AutoCapital") << quint32(QtWayland::zwp_text_input_v3::content_hint_auto_capitalization) 0454 << TextInputContentHints(TextInputContentHint::AutoCapitalization); 0455 QTest::addRow("Lowercase") << quint32(QtWayland::zwp_text_input_v3::content_hint_lowercase) << TextInputContentHints(TextInputContentHint::LowerCase); 0456 QTest::addRow("Uppercase") << quint32(QtWayland::zwp_text_input_v3::content_hint_uppercase) << TextInputContentHints(TextInputContentHint::UpperCase); 0457 QTest::addRow("Titlecase") << quint32(QtWayland::zwp_text_input_v3::content_hint_titlecase) << TextInputContentHints(TextInputContentHint::TitleCase); 0458 QTest::addRow("HiddenText") << quint32(QtWayland::zwp_text_input_v3::content_hint_hidden_text) << TextInputContentHints(TextInputContentHint::HiddenText); 0459 QTest::addRow("SensitiveData") << quint32(QtWayland::zwp_text_input_v3::content_hint_sensitive_data) 0460 << TextInputContentHints(TextInputContentHint::SensitiveData); 0461 QTest::addRow("Latin") << quint32(QtWayland::zwp_text_input_v3::content_hint_latin) << TextInputContentHints(TextInputContentHint::Latin); 0462 QTest::addRow("Multiline") << quint32(QtWayland::zwp_text_input_v3::content_hint_multiline) << TextInputContentHints(TextInputContentHint::MultiLine); 0463 QTest::addRow("Auto") << quint32(QtWayland::zwp_text_input_v3::content_hint_completion | QtWayland::zwp_text_input_v3::content_hint_spellcheck 0464 | QtWayland::zwp_text_input_v3::content_hint_auto_capitalization) 0465 << TextInputContentHints(TextInputContentHint::AutoCompletion | TextInputContentHint::AutoCorrection 0466 | TextInputContentHint::AutoCapitalization); 0467 } 0468 0469 void TestTextInputV3Interface::testContentHints() 0470 { 0471 // create a surface 0472 QSignalSpy serverSurfaceCreatedSpy(m_serverCompositor, &CompositorInterface::surfaceCreated); 0473 std::unique_ptr<KWayland::Client::Surface> clientSurface(m_clientCompositor->createSurface(this)); 0474 QVERIFY(serverSurfaceCreatedSpy.wait()); 0475 SurfaceInterface *serverSurface = serverSurfaceCreatedSpy.first().first().value<SurfaceInterface *>(); 0476 QVERIFY(serverSurface); 0477 0478 m_serverTextInputV3 = m_seat->textInputV3(); 0479 QVERIFY(m_serverTextInputV3); 0480 0481 QSignalSpy focusedSurfaceChangedSpy(m_seat, &SeatInterface::focusedTextInputSurfaceChanged); 0482 QSignalSpy textInputEnabledSpy(m_serverTextInputV3, &TextInputV3Interface::enabledChanged); 0483 0484 // Enter the textinput 0485 QCOMPARE(focusedSurfaceChangedSpy.count(), 0); 0486 0487 // Make sure that entering surface does not trigger the text input 0488 m_seat->setFocusedTextInputSurface(serverSurface); 0489 // FIXME: somehow this triggers BEFORE setFocusedTextInputSurface returns :( 0490 // QVERIFY(focusedSurfaceChangedSpy.wait()); 0491 QCOMPARE(focusedSurfaceChangedSpy.count(), 1); 0492 0493 // Now enable the textInput 0494 m_clientTextInputV3->enable(); 0495 m_clientTextInputV3->commit(); 0496 QVERIFY(textInputEnabledSpy.wait()); 0497 m_totalCommits++; 0498 0499 QCOMPARE(m_serverTextInputV3->contentHints(), TextInputContentHint::None); 0500 0501 // Now disable the textInput 0502 m_clientTextInputV3->disable(); 0503 m_clientTextInputV3->commit(); 0504 QVERIFY(textInputEnabledSpy.wait()); 0505 m_totalCommits++; 0506 0507 QSignalSpy contentTypeChangedSpy(m_serverTextInputV3, &TextInputV3Interface::contentTypeChanged); 0508 0509 QFETCH(quint32, clientHint); 0510 m_clientTextInputV3->enable(); 0511 m_clientTextInputV3->set_content_type(clientHint, QtWayland::zwp_text_input_v3::content_purpose_normal); 0512 m_clientTextInputV3->commit(); 0513 QVERIFY(contentTypeChangedSpy.wait()); 0514 QTEST(m_serverTextInputV3->contentHints(), "serverHints"); 0515 m_totalCommits++; 0516 0517 // Setting same thing should not trigger update 0518 m_clientTextInputV3->enable(); 0519 m_clientTextInputV3->set_content_type(clientHint, QtWayland::zwp_text_input_v3::content_purpose_normal); 0520 m_clientTextInputV3->commit(); 0521 QVERIFY(!contentTypeChangedSpy.wait(100)); 0522 m_totalCommits++; 0523 0524 // unset to normal 0525 m_clientTextInputV3->enable(); 0526 m_clientTextInputV3->set_content_type(QtWayland::zwp_text_input_v3::content_hint_none, QtWayland::zwp_text_input_v3::content_purpose_normal); 0527 m_clientTextInputV3->commit(); 0528 QVERIFY(contentTypeChangedSpy.wait()); 0529 m_totalCommits++; 0530 0531 // Now disable the textInput 0532 m_clientTextInputV3->disable(); 0533 m_clientTextInputV3->commit(); 0534 QVERIFY(textInputEnabledSpy.wait()); 0535 m_totalCommits++; 0536 } 0537 0538 void TestTextInputV3Interface::testMultipleTextinputs() 0539 { 0540 // create two more text inputs 0541 TextInputV3 *ti1 = new TextInputV3(); 0542 ti1->init(m_clientTextInputManagerV3->get_text_input(*m_clientSeat)); 0543 QVERIFY(ti1); 0544 0545 TextInputV3 *ti2 = new TextInputV3(); 0546 ti2->init(m_clientTextInputManagerV3->get_text_input(*m_clientSeat)); 0547 QVERIFY(ti2); 0548 0549 // create a surface 0550 QSignalSpy serverSurfaceCreatedSpy(m_serverCompositor, &CompositorInterface::surfaceCreated); 0551 std::unique_ptr<KWayland::Client::Surface> clientSurface(m_clientCompositor->createSurface(this)); 0552 QVERIFY(serverSurfaceCreatedSpy.wait()); 0553 SurfaceInterface *serverSurface = serverSurfaceCreatedSpy.first().first().value<SurfaceInterface *>(); 0554 QVERIFY(serverSurface); 0555 0556 QSignalSpy focusedSurfaceChangedSpy(m_seat, &SeatInterface::focusedTextInputSurfaceChanged); 0557 // Make sure that entering surface does not trigger the text input 0558 m_seat->setFocusedTextInputSurface(serverSurface); 0559 QCOMPARE(focusedSurfaceChangedSpy.count(), 1); 0560 0561 m_serverTextInputV3 = m_seat->textInputV3(); 0562 QVERIFY(m_serverTextInputV3); 0563 QVERIFY(!m_serverTextInputV3->isEnabled()); 0564 0565 QSignalSpy doneSpy1(ti1, &TextInputV3::done); 0566 QSignalSpy committedSpy(m_serverTextInputV3, &TextInputV3Interface::stateCommitted); 0567 // Enable ti1 0568 ti1->enable(); 0569 ti1->commit(); 0570 QVERIFY(committedSpy.wait()); 0571 QCOMPARE(committedSpy.last().at(0).value<quint32>(), 1); 0572 QVERIFY(m_serverTextInputV3->isEnabled()); 0573 QVERIFY(doneSpy1.wait()); 0574 0575 // Send another three commits on ti1 0576 ti1->enable(); 0577 ti1->set_surrounding_text("hello", 0, 1); 0578 ti1->commit(); 0579 QVERIFY(committedSpy.wait()); 0580 QCOMPARE(committedSpy.last().at(0).value<quint32>(), 2); 0581 QVERIFY(m_serverTextInputV3->isEnabled()); 0582 QVERIFY(doneSpy1.wait()); 0583 0584 ti1->enable(); 0585 ti1->set_content_type(QtWayland::zwp_text_input_v3::content_hint_none, QtWayland::zwp_text_input_v3::content_purpose_normal); 0586 ti1->commit(); 0587 QVERIFY(committedSpy.wait()); 0588 QCOMPARE(committedSpy.last().at(0).value<quint32>(), 3); 0589 QVERIFY(m_serverTextInputV3->isEnabled()); 0590 QVERIFY(doneSpy1.wait()); 0591 0592 // at this point total commit count to ti1 is 3 0593 QSignalSpy doneSpy2(ti2, &TextInputV3::done); 0594 0595 m_serverTextInputV3->commitString("Hello"); 0596 m_serverTextInputV3->done(); 0597 QVERIFY(doneSpy1.wait()); 0598 0599 // zwp_text_input_v3.done event have serial of total commits 0600 QCOMPARE(doneSpy1.last().at(0).value<quint32>(), 3); 0601 0602 // now ti1 is at 4 commit, while ti2 is still 0 0603 ti1->disable(); 0604 ti1->commit(); 0605 QVERIFY(committedSpy.wait()); 0606 QCOMPARE(committedSpy.last().at(0).value<quint32>(), 4); 0607 QVERIFY(!m_serverTextInputV3->isEnabled()); 0608 0609 // first commit to ti2 0610 ti2->enable(); 0611 ti2->commit(); 0612 QVERIFY(committedSpy.wait()); 0613 QCOMPARE(committedSpy.last().at(0).value<quint32>(), 1); 0614 QVERIFY(m_serverTextInputV3->isEnabled()); 0615 0616 // send commit string 0617 m_serverTextInputV3->commitString("Hello world"); 0618 m_serverTextInputV3->done(); 0619 QVERIFY(doneSpy2.wait()); 0620 0621 // ti2 is at one commit 0622 QCOMPARE(doneSpy2.last().at(0).value<quint32>(), 1); 0623 ti2->disable(); 0624 ti2->commit(); 0625 QVERIFY(committedSpy.wait()); 0626 QCOMPARE(committedSpy.last().at(0).value<quint32>(), 2); 0627 QVERIFY(!m_serverTextInputV3->isEnabled()); 0628 0629 // now re-enable the ti1 and verify sending commits to t2 hasn't affected it's serial 0630 // Enable ti1 : 5 commits now 0631 ti1->enable(); 0632 ti1->commit(); 0633 QVERIFY(committedSpy.wait()); 0634 QCOMPARE(committedSpy.last().at(0).value<quint32>(), 5); 0635 QVERIFY(m_serverTextInputV3->isEnabled()); 0636 0637 // send done signal 0638 m_serverTextInputV3->commitString("Hello"); 0639 m_serverTextInputV3->done(); 0640 QVERIFY(doneSpy1.wait()); 0641 QCOMPARE(doneSpy1.last().at(0).value<quint32>(), 5); 0642 0643 // cleanup 0644 if (ti1) { 0645 delete ti1; 0646 ti1 = nullptr; 0647 } 0648 if (ti2) { 0649 delete ti2; 0650 ti2 = nullptr; 0651 } 0652 } 0653 0654 QTEST_GUILESS_MAIN(TestTextInputV3Interface) 0655 0656 #include "test_textinputv3_interface.moc"