File indexing completed on 2024-11-10 04:56:22
0001 /* 0002 SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez <aleixpol@kde.org> 0003 SPDX-FileCopyrightText: 2020 Bhushan Shah <bshah@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0006 */ 0007 // Qt 0008 #include <QHash> 0009 #include <QSignalSpy> 0010 #include <QTest> 0011 #include <QThread> 0012 0013 #include "../../../tests/fakeoutput.h" 0014 0015 // WaylandServer 0016 #include "wayland/compositor.h" 0017 #include "wayland/display.h" 0018 #include "wayland/inputmethod_v1.h" 0019 #include "wayland/output.h" 0020 #include "wayland/seat.h" 0021 0022 #include "KWayland/Client/compositor.h" 0023 #include "KWayland/Client/connection_thread.h" 0024 #include "KWayland/Client/event_queue.h" 0025 #include "KWayland/Client/keyboard.h" 0026 #include "KWayland/Client/output.h" 0027 #include "KWayland/Client/registry.h" 0028 #include "KWayland/Client/seat.h" 0029 #include "KWayland/Client/surface.h" 0030 0031 #include "qwayland-input-method-unstable-v1.h" 0032 #include "qwayland-server-text-input-unstable-v1.h" 0033 0034 #include <linux/input-event-codes.h> 0035 0036 using namespace KWin; 0037 0038 class InputPanelSurface : public QObject, public QtWayland::zwp_input_panel_surface_v1 0039 { 0040 Q_OBJECT 0041 public: 0042 InputPanelSurface(::zwp_input_panel_surface_v1 *t) 0043 : QtWayland::zwp_input_panel_surface_v1(t) 0044 { 0045 } 0046 }; 0047 0048 class InputPanel : public QtWayland::zwp_input_panel_v1 0049 { 0050 public: 0051 InputPanel(struct ::wl_registry *registry, int id, int version) 0052 : QtWayland::zwp_input_panel_v1(registry, id, version) 0053 { 0054 } 0055 0056 InputPanelSurface *panelForSurface(KWayland::Client::Surface *surface) 0057 { 0058 auto panelSurface = new InputPanelSurface(get_input_panel_surface(*surface)); 0059 QObject::connect(surface, &QObject::destroyed, panelSurface, &QObject::deleteLater); 0060 return panelSurface; 0061 } 0062 }; 0063 0064 class InputMethodV1Context : public QObject, public QtWayland::zwp_input_method_context_v1 0065 { 0066 Q_OBJECT 0067 public: 0068 quint32 contentPurpose() 0069 { 0070 return imPurpose; 0071 } 0072 quint32 contentHints() 0073 { 0074 return imHint; 0075 } 0076 Q_SIGNALS: 0077 void content_type_changed(); 0078 void invoke_action(quint32 button, quint32 index); 0079 void preferred_language(QString lang); 0080 void surrounding_text(QString lang, quint32 cursor, quint32 anchor); 0081 void reset(); 0082 0083 protected: 0084 void zwp_input_method_context_v1_content_type(uint32_t hint, uint32_t purpose) override 0085 { 0086 imHint = hint; 0087 imPurpose = purpose; 0088 Q_EMIT content_type_changed(); 0089 } 0090 void zwp_input_method_context_v1_invoke_action(uint32_t button, uint32_t index) override 0091 { 0092 Q_EMIT invoke_action(button, index); 0093 } 0094 void zwp_input_method_context_v1_preferred_language(const QString &language) override 0095 { 0096 Q_EMIT preferred_language(language); 0097 } 0098 void zwp_input_method_context_v1_surrounding_text(const QString &text, uint32_t cursor, uint32_t anchor) override 0099 { 0100 Q_EMIT surrounding_text(text, cursor, anchor); 0101 } 0102 void zwp_input_method_context_v1_reset() override 0103 { 0104 Q_EMIT reset(); 0105 } 0106 0107 private: 0108 quint32 imHint = 0; 0109 quint32 imPurpose = 0; 0110 }; 0111 0112 class InputMethodV1 : public QObject, public QtWayland::zwp_input_method_v1 0113 { 0114 Q_OBJECT 0115 public: 0116 InputMethodV1(struct ::wl_registry *registry, int id, int version) 0117 : QtWayland::zwp_input_method_v1(registry, id, version) 0118 { 0119 } 0120 InputMethodV1Context *context() 0121 { 0122 return m_context; 0123 } 0124 0125 Q_SIGNALS: 0126 void activated(); 0127 void deactivated(); 0128 0129 protected: 0130 void zwp_input_method_v1_activate(struct ::zwp_input_method_context_v1 *context) override 0131 { 0132 m_context = new InputMethodV1Context(); 0133 m_context->init(context); 0134 Q_EMIT activated(); 0135 }; 0136 void zwp_input_method_v1_deactivate(struct ::zwp_input_method_context_v1 *context) override 0137 { 0138 delete m_context; 0139 m_context = nullptr; 0140 Q_EMIT deactivated(); 0141 }; 0142 0143 private: 0144 InputMethodV1Context *m_context; 0145 }; 0146 0147 class TestInputMethodInterface : public QObject 0148 { 0149 Q_OBJECT 0150 public: 0151 TestInputMethodInterface() 0152 { 0153 } 0154 ~TestInputMethodInterface() override; 0155 0156 private Q_SLOTS: 0157 void initTestCase(); 0158 void testAdd(); 0159 void testActivate(); 0160 void testContext(); 0161 void testGrabkeyboard(); 0162 void testContentHints_data(); 0163 void testContentHints(); 0164 void testContentPurpose_data(); 0165 void testContentPurpose(); 0166 void testKeyboardGrab(); 0167 0168 private: 0169 KWayland::Client::ConnectionThread *m_connection; 0170 KWayland::Client::EventQueue *m_queue; 0171 KWayland::Client::Compositor *m_clientCompositor; 0172 KWayland::Client::Seat *m_clientSeat = nullptr; 0173 KWayland::Client::Output *m_output = nullptr; 0174 0175 InputMethodV1 *m_inputMethod; 0176 InputPanel *m_inputPanel; 0177 QThread *m_thread; 0178 KWin::Display m_display; 0179 SeatInterface *m_seat; 0180 CompositorInterface *m_serverCompositor; 0181 std::unique_ptr<FakeOutput> m_outputHandle; 0182 std::unique_ptr<OutputInterface> m_outputInterface; 0183 0184 KWin::InputMethodV1Interface *m_inputMethodIface; 0185 KWin::InputPanelV1Interface *m_inputPanelIface; 0186 0187 QList<SurfaceInterface *> m_surfaces; 0188 }; 0189 0190 static const QString s_socketName = QStringLiteral("kwin-wayland-server-inputmethod-test-0"); 0191 0192 void TestInputMethodInterface::initTestCase() 0193 { 0194 m_display.addSocketName(s_socketName); 0195 m_display.start(); 0196 QVERIFY(m_display.isRunning()); 0197 0198 m_seat = new SeatInterface(&m_display, this); 0199 m_serverCompositor = new CompositorInterface(&m_display, this); 0200 m_inputMethodIface = new InputMethodV1Interface(&m_display, this); 0201 m_inputPanelIface = new InputPanelV1Interface(&m_display, this); 0202 0203 m_outputHandle = std::make_unique<FakeOutput>(); 0204 m_outputInterface = std::make_unique<OutputInterface>(&m_display, m_outputHandle.get()); 0205 0206 connect(m_serverCompositor, &CompositorInterface::surfaceCreated, this, [this](SurfaceInterface *surface) { 0207 m_surfaces += surface; 0208 }); 0209 0210 // setup connection 0211 m_connection = new KWayland::Client::ConnectionThread; 0212 QSignalSpy connectedSpy(m_connection, &KWayland::Client::ConnectionThread::connected); 0213 m_connection->setSocketName(s_socketName); 0214 0215 m_thread = new QThread(this); 0216 m_connection->moveToThread(m_thread); 0217 m_thread->start(); 0218 0219 m_connection->initConnection(); 0220 QVERIFY(connectedSpy.wait()); 0221 QVERIFY(!m_connection->connections().isEmpty()); 0222 0223 m_queue = new KWayland::Client::EventQueue(this); 0224 QVERIFY(!m_queue->isValid()); 0225 m_queue->setup(m_connection); 0226 QVERIFY(m_queue->isValid()); 0227 0228 auto registry = new KWayland::Client::Registry(this); 0229 QSignalSpy interfacesSpy(registry, &KWayland::Client::Registry::interfacesAnnounced); 0230 connect(registry, &KWayland::Client::Registry::outputAnnounced, this, [this, registry](quint32 name, quint32 version) { 0231 m_output = new KWayland::Client::Output(this); 0232 m_output->setup(registry->bindOutput(name, version)); 0233 }); 0234 connect(registry, &KWayland::Client::Registry::interfaceAnnounced, this, [this, registry](const QByteArray &interface, quint32 name, quint32 version) { 0235 if (interface == "zwp_input_panel_v1") { 0236 m_inputPanel = new InputPanel(registry->registry(), name, version); 0237 } else if (interface == "zwp_input_method_v1") { 0238 m_inputMethod = new InputMethodV1(registry->registry(), name, version); 0239 } 0240 }); 0241 connect(registry, &KWayland::Client::Registry::seatAnnounced, this, [this, registry](quint32 name, quint32 version) { 0242 m_clientSeat = registry->createSeat(name, version); 0243 }); 0244 registry->setEventQueue(m_queue); 0245 QSignalSpy compositorSpy(registry, &KWayland::Client::Registry::compositorAnnounced); 0246 registry->create(m_connection->display()); 0247 QVERIFY(registry->isValid()); 0248 registry->setup(); 0249 wl_display_flush(m_connection->display()); 0250 0251 QVERIFY(compositorSpy.wait()); 0252 m_clientCompositor = registry->createCompositor(compositorSpy.first().first().value<quint32>(), compositorSpy.first().last().value<quint32>(), this); 0253 QVERIFY(m_clientCompositor->isValid()); 0254 0255 QVERIFY(interfacesSpy.count() || interfacesSpy.wait()); 0256 0257 QSignalSpy surfaceSpy(m_serverCompositor, &CompositorInterface::surfaceCreated); 0258 for (int i = 0; i < 3; ++i) { 0259 m_clientCompositor->createSurface(this); 0260 } 0261 QVERIFY(surfaceSpy.count() < 3 && surfaceSpy.wait(200)); 0262 QVERIFY(m_surfaces.count() == 3); 0263 QVERIFY(m_inputPanel); 0264 QVERIFY(m_output); 0265 } 0266 0267 TestInputMethodInterface::~TestInputMethodInterface() 0268 { 0269 if (m_queue) { 0270 delete m_queue; 0271 m_queue = nullptr; 0272 } 0273 if (m_thread) { 0274 m_thread->quit(); 0275 m_thread->wait(); 0276 delete m_thread; 0277 m_thread = nullptr; 0278 } 0279 delete m_inputPanel; 0280 delete m_inputMethod; 0281 delete m_inputMethodIface; 0282 delete m_inputPanelIface; 0283 m_connection->deleteLater(); 0284 m_connection = nullptr; 0285 } 0286 0287 void TestInputMethodInterface::testAdd() 0288 { 0289 QSignalSpy panelSpy(m_inputPanelIface, &InputPanelV1Interface::inputPanelSurfaceAdded); 0290 QPointer<InputPanelSurfaceV1Interface> panelSurfaceIface; 0291 connect(m_inputPanelIface, &InputPanelV1Interface::inputPanelSurfaceAdded, this, [&panelSurfaceIface](InputPanelSurfaceV1Interface *surface) { 0292 panelSurfaceIface = surface; 0293 }); 0294 0295 auto surface = m_clientCompositor->createSurface(this); 0296 auto panelSurface = m_inputPanel->panelForSurface(surface); 0297 0298 QVERIFY(panelSpy.wait() || panelSurfaceIface); 0299 Q_ASSERT(panelSurfaceIface); 0300 Q_ASSERT(panelSurfaceIface->surface() == m_surfaces.constLast()); 0301 0302 QSignalSpy panelTopLevelSpy(panelSurfaceIface, &InputPanelSurfaceV1Interface::topLevel); 0303 panelSurface->set_toplevel(*m_output, InputPanelSurface::position_center_bottom); 0304 QVERIFY(panelTopLevelSpy.wait()); 0305 } 0306 0307 void TestInputMethodInterface::testActivate() 0308 { 0309 QVERIFY(m_inputMethodIface); 0310 QSignalSpy inputMethodActivateSpy(m_inputMethod, &InputMethodV1::activated); 0311 QSignalSpy inputMethodDeactivateSpy(m_inputMethod, &InputMethodV1::deactivated); 0312 0313 // before sending activate the context should be null 0314 QVERIFY(!m_inputMethodIface->context()); 0315 0316 // send activate now 0317 m_inputMethodIface->sendActivate(); 0318 QVERIFY(inputMethodActivateSpy.wait()); 0319 QCOMPARE(inputMethodActivateSpy.count(), 1); 0320 QVERIFY(m_inputMethodIface->context()); 0321 0322 // send deactivate and verify server interface resets context 0323 m_inputMethodIface->sendDeactivate(); 0324 QVERIFY(inputMethodDeactivateSpy.wait()); 0325 QCOMPARE(inputMethodActivateSpy.count(), 1); 0326 QVERIFY(!m_inputMethodIface->context()); 0327 } 0328 0329 void TestInputMethodInterface::testContext() 0330 { 0331 QVERIFY(m_inputMethodIface); 0332 QSignalSpy inputMethodActivateSpy(m_inputMethod, &InputMethodV1::activated); 0333 QSignalSpy inputMethodDeactivateSpy(m_inputMethod, &InputMethodV1::deactivated); 0334 0335 // before sending activate the context should be null 0336 QVERIFY(!m_inputMethodIface->context()); 0337 0338 // send activate now 0339 m_inputMethodIface->sendActivate(); 0340 QVERIFY(inputMethodActivateSpy.wait()); 0341 QCOMPARE(inputMethodActivateSpy.count(), 1); 0342 0343 KWin::InputMethodContextV1Interface *serverContext = m_inputMethodIface->context(); 0344 QVERIFY(serverContext); 0345 0346 InputMethodV1Context *imContext = m_inputMethod->context(); 0347 QVERIFY(imContext); 0348 0349 quint32 serial = 1; 0350 0351 // commit some text 0352 QSignalSpy commitStringSpy(serverContext, &KWin::InputMethodContextV1Interface::commitString); 0353 imContext->commit_string(serial, "hello"); 0354 QVERIFY(commitStringSpy.wait()); 0355 QCOMPARE(commitStringSpy.count(), serial); 0356 QCOMPARE(commitStringSpy.last().at(0).value<quint32>(), serial); 0357 QCOMPARE(commitStringSpy.last().at(1).value<QString>(), "hello"); 0358 serial++; 0359 0360 // preedit styling event 0361 QSignalSpy preeditStylingSpy(serverContext, &KWin::InputMethodContextV1Interface::preeditStyling); 0362 // protocol does not document 3rd argument mean in much details (styling) 0363 imContext->preedit_styling(0, 5, 1); 0364 QVERIFY(preeditStylingSpy.wait()); 0365 QCOMPARE(preeditStylingSpy.count(), 1); 0366 QCOMPARE(preeditStylingSpy.last().at(0).value<quint32>(), 0); 0367 QCOMPARE(preeditStylingSpy.last().at(1).value<quint32>(), 5); 0368 QCOMPARE(preeditStylingSpy.last().at(2).value<quint32>(), 1); 0369 0370 // preedit cursor event 0371 QSignalSpy preeditCursorSpy(serverContext, &KWin::InputMethodContextV1Interface::preeditCursor); 0372 imContext->preedit_cursor(3); 0373 QVERIFY(preeditCursorSpy.wait()); 0374 QCOMPARE(preeditCursorSpy.count(), 1); 0375 QCOMPARE(preeditCursorSpy.last().at(0).value<quint32>(), 3); 0376 0377 // commit preedit_string 0378 QSignalSpy preeditStringSpy(serverContext, &KWin::InputMethodContextV1Interface::preeditString); 0379 imContext->preedit_string(serial, "hello", "kde"); 0380 QVERIFY(preeditStringSpy.wait()); 0381 QCOMPARE(preeditStringSpy.count(), 1); 0382 QCOMPARE(preeditStringSpy.last().at(0).value<quint32>(), serial); 0383 QCOMPARE(preeditStringSpy.last().at(1).value<QString>(), "hello"); 0384 QCOMPARE(preeditStringSpy.last().at(2).value<QString>(), "kde"); 0385 serial++; 0386 0387 // delete surrounding text 0388 QSignalSpy deleteSurroundingSpy(serverContext, &KWin::InputMethodContextV1Interface::deleteSurroundingText); 0389 imContext->delete_surrounding_text(0, 5); 0390 QVERIFY(deleteSurroundingSpy.wait()); 0391 QCOMPARE(deleteSurroundingSpy.count(), 1); 0392 QCOMPARE(deleteSurroundingSpy.last().at(0).value<quint32>(), 0); 0393 QCOMPARE(deleteSurroundingSpy.last().at(1).value<quint32>(), 5); 0394 0395 // set cursor position 0396 QSignalSpy cursorPositionSpy(serverContext, &KWin::InputMethodContextV1Interface::cursorPosition); 0397 imContext->cursor_position(2, 4); 0398 QVERIFY(cursorPositionSpy.wait()); 0399 QCOMPARE(cursorPositionSpy.count(), 1); 0400 QCOMPARE(cursorPositionSpy.last().at(0).value<quint32>(), 2); 0401 QCOMPARE(cursorPositionSpy.last().at(1).value<quint32>(), 4); 0402 0403 // invoke action 0404 QSignalSpy invokeActionSpy(imContext, &InputMethodV1Context::invoke_action); 0405 serverContext->sendInvokeAction(3, 5); 0406 QVERIFY(invokeActionSpy.wait()); 0407 QCOMPARE(invokeActionSpy.count(), 1); 0408 QCOMPARE(invokeActionSpy.last().at(0).value<quint32>(), 3); 0409 QCOMPARE(invokeActionSpy.last().at(1).value<quint32>(), 5); 0410 0411 // preferred language 0412 QSignalSpy preferredLanguageSpy(imContext, &InputMethodV1Context::preferred_language); 0413 serverContext->sendPreferredLanguage("gu_IN"); 0414 QVERIFY(preferredLanguageSpy.wait()); 0415 QCOMPARE(preferredLanguageSpy.count(), 1); 0416 QCOMPARE(preferredLanguageSpy.last().at(0).value<QString>(), "gu_IN"); 0417 0418 // surrounding text 0419 QSignalSpy surroundingTextSpy(imContext, &InputMethodV1Context::surrounding_text); 0420 serverContext->sendSurroundingText("Hello Plasma!", 2, 4); 0421 QVERIFY(surroundingTextSpy.wait()); 0422 QCOMPARE(surroundingTextSpy.count(), 1); 0423 QCOMPARE(surroundingTextSpy.last().at(0).value<QString>(), "Hello Plasma!"); 0424 QCOMPARE(surroundingTextSpy.last().at(1).value<quint32>(), 2); 0425 QCOMPARE(surroundingTextSpy.last().at(2).value<quint32>(), 4); 0426 0427 // reset 0428 QSignalSpy resetSpy(imContext, &InputMethodV1Context::reset); 0429 serverContext->sendReset(); 0430 QVERIFY(resetSpy.wait()); 0431 QCOMPARE(resetSpy.count(), 1); 0432 0433 // send deactivate and verify server interface resets context 0434 m_inputMethodIface->sendDeactivate(); 0435 QVERIFY(inputMethodDeactivateSpy.wait()); 0436 QCOMPARE(inputMethodActivateSpy.count(), 1); 0437 QVERIFY(!m_inputMethodIface->context()); 0438 QVERIFY(!m_inputMethod->context()); 0439 } 0440 0441 void TestInputMethodInterface::testGrabkeyboard() 0442 { 0443 QVERIFY(m_inputMethodIface); 0444 QSignalSpy inputMethodActivateSpy(m_inputMethod, &InputMethodV1::activated); 0445 QSignalSpy inputMethodDeactivateSpy(m_inputMethod, &InputMethodV1::deactivated); 0446 0447 // before sending activate the context should be null 0448 QVERIFY(!m_inputMethodIface->context()); 0449 0450 // send activate now 0451 m_inputMethodIface->sendActivate(); 0452 QVERIFY(inputMethodActivateSpy.wait()); 0453 QCOMPARE(inputMethodActivateSpy.count(), 1); 0454 0455 KWin::InputMethodContextV1Interface *serverContext = m_inputMethodIface->context(); 0456 QVERIFY(serverContext); 0457 0458 InputMethodV1Context *imContext = m_inputMethod->context(); 0459 QVERIFY(imContext); 0460 0461 QSignalSpy keyEventSpy(serverContext, &KWin::InputMethodContextV1Interface::key); 0462 imContext->key(0, 123, 56, 1); 0463 QEXPECT_FAIL("", "We should be not get key event if keyboard is not grabbed", Continue); 0464 QVERIFY(!keyEventSpy.wait(200)); 0465 0466 QSignalSpy modifierEventSpy(serverContext, &KWin::InputMethodContextV1Interface::modifiers); 0467 imContext->modifiers(1234, 0, 0, 0, 0); 0468 QEXPECT_FAIL("", "We should be not get modifiers event if keyboard is not grabbed", Continue); 0469 QVERIFY(!modifierEventSpy.wait(200)); 0470 0471 // grab the keyboard 0472 wl_keyboard *keyboard = imContext->grab_keyboard(); 0473 QVERIFY(keyboard); 0474 0475 // TODO: add more tests about keyboard grab here 0476 0477 // send deactivate and verify server interface resets context 0478 m_inputMethodIface->sendDeactivate(); 0479 QVERIFY(inputMethodDeactivateSpy.wait()); 0480 QCOMPARE(inputMethodActivateSpy.count(), 1); 0481 QVERIFY(!m_inputMethodIface->context()); 0482 QVERIFY(!m_inputMethod->context()); 0483 } 0484 0485 void TestInputMethodInterface::testContentHints_data() 0486 { 0487 QTest::addColumn<KWin::TextInputContentHints>("serverHints"); 0488 QTest::addColumn<quint32>("imHint"); 0489 QTest::addRow("Spellcheck") << TextInputContentHints(TextInputContentHint::AutoCorrection) 0490 << quint32(QtWaylandServer::zwp_text_input_v1::content_hint_auto_correction); 0491 QTest::addRow("AutoCapital") << TextInputContentHints(TextInputContentHint::AutoCapitalization) 0492 << quint32(QtWaylandServer::zwp_text_input_v1::content_hint_auto_capitalization); 0493 QTest::addRow("Lowercase") << TextInputContentHints(TextInputContentHint::LowerCase) << quint32(QtWaylandServer::zwp_text_input_v1::content_hint_lowercase); 0494 QTest::addRow("Uppercase") << TextInputContentHints(TextInputContentHint::UpperCase) << quint32(QtWaylandServer::zwp_text_input_v1::content_hint_uppercase); 0495 QTest::addRow("Titlecase") << TextInputContentHints(TextInputContentHint::TitleCase) << quint32(QtWaylandServer::zwp_text_input_v1::content_hint_titlecase); 0496 QTest::addRow("HiddenText") << TextInputContentHints(TextInputContentHint::HiddenText) 0497 << quint32(QtWaylandServer::zwp_text_input_v1::content_hint_hidden_text); 0498 QTest::addRow("SensitiveData") << TextInputContentHints(TextInputContentHint::SensitiveData) 0499 << quint32(QtWaylandServer::zwp_text_input_v1::content_hint_sensitive_data); 0500 QTest::addRow("Latin") << TextInputContentHints(TextInputContentHint::Latin) << quint32(QtWaylandServer::zwp_text_input_v1::content_hint_latin); 0501 QTest::addRow("Multiline") << TextInputContentHints(TextInputContentHint::MultiLine) << quint32(QtWaylandServer::zwp_text_input_v1::content_hint_multiline); 0502 QTest::addRow("Auto") << TextInputContentHints(TextInputContentHint::AutoCorrection | TextInputContentHint::AutoCapitalization) 0503 << quint32(QtWaylandServer::zwp_text_input_v1::content_hint_auto_correction 0504 | QtWaylandServer::zwp_text_input_v1::content_hint_auto_capitalization); 0505 } 0506 0507 void TestInputMethodInterface::testContentHints() 0508 { 0509 QVERIFY(m_inputMethodIface); 0510 QSignalSpy inputMethodActivateSpy(m_inputMethod, &InputMethodV1::activated); 0511 QSignalSpy inputMethodDeactivateSpy(m_inputMethod, &InputMethodV1::deactivated); 0512 0513 // before sending activate the context should be null 0514 QVERIFY(!m_inputMethodIface->context()); 0515 0516 // send activate now 0517 m_inputMethodIface->sendActivate(); 0518 QVERIFY(inputMethodActivateSpy.wait()); 0519 QCOMPARE(inputMethodActivateSpy.count(), 1); 0520 0521 KWin::InputMethodContextV1Interface *serverContext = m_inputMethodIface->context(); 0522 QVERIFY(serverContext); 0523 0524 InputMethodV1Context *imContext = m_inputMethod->context(); 0525 QVERIFY(imContext); 0526 0527 QSignalSpy contentTypeChangedSpy(imContext, &InputMethodV1Context::content_type_changed); 0528 0529 QFETCH(KWin::TextInputContentHints, serverHints); 0530 serverContext->sendContentType(serverHints, KWin::TextInputContentPurpose::Normal); 0531 QVERIFY(contentTypeChangedSpy.wait()); 0532 QCOMPARE(contentTypeChangedSpy.count(), 1); 0533 QEXPECT_FAIL("SensitiveData", "SensitiveData content hint need fixing", Continue); 0534 QTEST(imContext->contentHints(), "imHint"); 0535 0536 // send deactivate and verify server interface resets context 0537 m_inputMethodIface->sendDeactivate(); 0538 QVERIFY(inputMethodDeactivateSpy.wait()); 0539 QCOMPARE(inputMethodActivateSpy.count(), 1); 0540 QVERIFY(!m_inputMethodIface->context()); 0541 QVERIFY(!m_inputMethod->context()); 0542 } 0543 0544 void TestInputMethodInterface::testContentPurpose_data() 0545 { 0546 QTest::addColumn<KWin::TextInputContentPurpose>("serverPurpose"); 0547 QTest::addColumn<quint32>("imPurpose"); 0548 0549 QTest::newRow("Alpha") << TextInputContentPurpose::Alpha << quint32(QtWaylandServer::zwp_text_input_v1::content_purpose_alpha); 0550 QTest::newRow("Digits") << TextInputContentPurpose::Digits << quint32(QtWaylandServer::zwp_text_input_v1::content_purpose_digits); 0551 QTest::newRow("Number") << TextInputContentPurpose::Number << quint32(QtWaylandServer::zwp_text_input_v1::content_purpose_number); 0552 QTest::newRow("Phone") << TextInputContentPurpose::Phone << quint32(QtWaylandServer::zwp_text_input_v1::content_purpose_phone); 0553 QTest::newRow("Url") << TextInputContentPurpose::Url << quint32(QtWaylandServer::zwp_text_input_v1::content_purpose_url); 0554 QTest::newRow("Email") << TextInputContentPurpose::Email << quint32(QtWaylandServer::zwp_text_input_v1::content_purpose_email); 0555 QTest::newRow("Name") << TextInputContentPurpose::Name << quint32(QtWaylandServer::zwp_text_input_v1::content_purpose_name); 0556 QTest::newRow("Password") << TextInputContentPurpose::Password << quint32(QtWaylandServer::zwp_text_input_v1::content_purpose_password); 0557 QTest::newRow("Date") << TextInputContentPurpose::Date << quint32(QtWaylandServer::zwp_text_input_v1::content_purpose_date); 0558 QTest::newRow("Time") << TextInputContentPurpose::Time << quint32(QtWaylandServer::zwp_text_input_v1::content_purpose_time); 0559 QTest::newRow("DateTime") << TextInputContentPurpose::DateTime << quint32(QtWaylandServer::zwp_text_input_v1::content_purpose_datetime); 0560 QTest::newRow("Terminal") << TextInputContentPurpose::Terminal << quint32(QtWaylandServer::zwp_text_input_v1::content_purpose_terminal); 0561 QTest::newRow("Normal") << TextInputContentPurpose::Normal << quint32(QtWaylandServer::zwp_text_input_v1::content_purpose_normal); 0562 QTest::newRow("Pin") << TextInputContentPurpose::Pin << quint32(QtWaylandServer::zwp_text_input_v1::content_purpose_password); 0563 } 0564 0565 void TestInputMethodInterface::testContentPurpose() 0566 { 0567 QVERIFY(m_inputMethodIface); 0568 QSignalSpy inputMethodActivateSpy(m_inputMethod, &InputMethodV1::activated); 0569 QSignalSpy inputMethodDeactivateSpy(m_inputMethod, &InputMethodV1::deactivated); 0570 0571 // before sending activate the context should be null 0572 QVERIFY(!m_inputMethodIface->context()); 0573 0574 // send activate now 0575 m_inputMethodIface->sendActivate(); 0576 QVERIFY(inputMethodActivateSpy.wait()); 0577 QCOMPARE(inputMethodActivateSpy.count(), 1); 0578 0579 KWin::InputMethodContextV1Interface *serverContext = m_inputMethodIface->context(); 0580 QVERIFY(serverContext); 0581 0582 InputMethodV1Context *imContext = m_inputMethod->context(); 0583 QVERIFY(imContext); 0584 0585 QSignalSpy contentTypeChangedSpy(imContext, &InputMethodV1Context::content_type_changed); 0586 0587 QFETCH(KWin::TextInputContentPurpose, serverPurpose); 0588 serverContext->sendContentType(KWin::TextInputContentHints(KWin::TextInputContentHint::None), serverPurpose); 0589 QVERIFY(contentTypeChangedSpy.wait()); 0590 QCOMPARE(contentTypeChangedSpy.count(), 1); 0591 QEXPECT_FAIL("Pin", "Pin should return content_purpose_password", Continue); 0592 QTEST(imContext->contentPurpose(), "imPurpose"); 0593 0594 // send deactivate and verify server interface resets context 0595 m_inputMethodIface->sendDeactivate(); 0596 QVERIFY(inputMethodDeactivateSpy.wait()); 0597 QCOMPARE(inputMethodActivateSpy.count(), 1); 0598 QVERIFY(!m_inputMethodIface->context()); 0599 QVERIFY(!m_inputMethod->context()); 0600 } 0601 0602 void TestInputMethodInterface::testKeyboardGrab() 0603 { 0604 QVERIFY(m_inputMethodIface); 0605 QSignalSpy inputMethodActivateSpy(m_inputMethod, &InputMethodV1::activated); 0606 0607 m_inputMethodIface->sendActivate(); 0608 QVERIFY(inputMethodActivateSpy.wait()); 0609 0610 QSignalSpy keyboardGrabSpy(m_inputMethodIface->context(), &InputMethodContextV1Interface::keyboardGrabRequested); 0611 InputMethodV1Context *imContext = m_inputMethod->context(); 0612 QVERIFY(imContext); 0613 KWayland::Client::Keyboard *keyboard = new KWayland::Client::Keyboard(this); 0614 keyboard->setup(imContext->grab_keyboard()); 0615 QVERIFY(keyboard->isValid()); 0616 QVERIFY(keyboardGrabSpy.count() || keyboardGrabSpy.wait()); 0617 0618 QSignalSpy keyboardSpy(keyboard, &KWayland::Client::Keyboard::keyChanged); 0619 m_inputMethodIface->context()->keyboardGrab()->sendKey(0, 0, KEY_F1, KeyboardKeyState::Pressed); 0620 m_inputMethodIface->context()->keyboardGrab()->sendKey(0, 0, KEY_F1, KeyboardKeyState::Released); 0621 keyboardSpy.wait(); 0622 QCOMPARE(keyboardSpy.count(), 2); 0623 0624 m_inputMethodIface->sendDeactivate(); 0625 } 0626 0627 QTEST_GUILESS_MAIN(TestInputMethodInterface) 0628 #include "test_inputmethod_interface.moc"