22#include "KWayland/Client/compositor.h"
23#include "KWayland/Client/connection_thread.h"
24#include "KWayland/Client/event_queue.h"
25#include "KWayland/Client/keyboard.h"
26#include "KWayland/Client/output.h"
27#include "KWayland/Client/registry.h"
28#include "KWayland/Client/seat.h"
29#include "KWayland/Client/surface.h"
31#include "qwayland-input-method-unstable-v1.h"
32#include "qwayland-server-text-input-unstable-v1.h"
34#include <linux/input-event-codes.h>
43 :
QtWayland::zwp_input_panel_surface_v1(t)
59 QObject::connect(surface, &QObject::destroyed, panelSurface, &QObject::deleteLater);
109 quint32 imPurpose = 0;
161 void testGrabkeyboard();
162 void testContentHints_data();
163 void testContentHints();
164 void testContentPurpose_data();
165 void testContentPurpose();
166 void testKeyboardGrab();
169 KWayland::Client::ConnectionThread *m_connection;
170 KWayland::Client::EventQueue *m_queue;
171 KWayland::Client::Compositor *m_clientCompositor;
172 KWayland::Client::Seat *m_clientSeat =
nullptr;
173 KWayland::Client::Output *m_output =
nullptr;
181 std::unique_ptr<FakeOutput> m_outputHandle;
182 std::unique_ptr<OutputInterface> m_outputInterface;
187 QList<SurfaceInterface *> m_surfaces;
190static const QString s_socketName = QStringLiteral(
"kwin-wayland-server-inputmethod-test-0");
192void TestInputMethodInterface::initTestCase()
203 m_outputHandle = std::make_unique<FakeOutput>();
204 m_outputInterface = std::make_unique<OutputInterface>(&m_display, m_outputHandle.get());
207 m_surfaces += surface;
211 m_connection =
new KWayland::Client::ConnectionThread;
212 QSignalSpy connectedSpy(m_connection, &KWayland::Client::ConnectionThread::connected);
213 m_connection->setSocketName(s_socketName);
215 m_thread =
new QThread(
this);
216 m_connection->moveToThread(m_thread);
219 m_connection->initConnection();
220 QVERIFY(connectedSpy.wait());
221 QVERIFY(!m_connection->connections().isEmpty());
223 m_queue =
new KWayland::Client::EventQueue(
this);
224 QVERIFY(!m_queue->isValid());
225 m_queue->setup(m_connection);
226 QVERIFY(m_queue->isValid());
228 auto registry =
new KWayland::Client::Registry(
this);
229 QSignalSpy interfacesSpy(registry, &KWayland::Client::Registry::interfacesAnnounced);
230 connect(registry, &KWayland::Client::Registry::outputAnnounced,
this, [
this, registry](quint32 name, quint32
version) {
231 m_output =
new KWayland::Client::Output(
this);
234 connect(registry, &KWayland::Client::Registry::interfaceAnnounced,
this, [
this, registry](
const QByteArray &interface, quint32 name, quint32
version) {
235 if (interface ==
"zwp_input_panel_v1") {
237 }
else if (interface ==
"zwp_input_method_v1") {
241 connect(registry, &KWayland::Client::Registry::seatAnnounced,
this, [
this, registry](quint32 name, quint32
version) {
245 QSignalSpy compositorSpy(registry, &KWayland::Client::Registry::compositorAnnounced);
246 registry->create(m_connection->display());
249 wl_display_flush(m_connection->display());
251 QVERIFY(compositorSpy.wait());
252 m_clientCompositor =
registry->createCompositor(compositorSpy.first().first().value<quint32>(), compositorSpy.first().last().value<quint32>(),
this);
253 QVERIFY(m_clientCompositor->isValid());
255 QVERIFY(interfacesSpy.count() || interfacesSpy.wait());
258 for (
int i = 0; i < 3; ++i) {
259 m_clientCompositor->createSurface(
this);
261 QVERIFY(surfaceSpy.count() < 3 && surfaceSpy.wait(200));
262 QVERIFY(m_surfaces.count() == 3);
263 QVERIFY(m_inputPanel);
280 delete m_inputMethod;
281 delete m_inputMethodIface;
282 delete m_inputPanelIface;
283 m_connection->deleteLater();
284 m_connection =
nullptr;
287void TestInputMethodInterface::testAdd()
290 QPointer<InputPanelSurfaceV1Interface> panelSurfaceIface;
292 panelSurfaceIface = surface;
295 auto surface = m_clientCompositor->createSurface(
this);
298 QVERIFY(panelSpy.wait() || panelSurfaceIface);
299 Q_ASSERT(panelSurfaceIface);
300 Q_ASSERT(panelSurfaceIface->surface() == m_surfaces.constLast());
303 panelSurface->set_toplevel(*m_output, InputPanelSurface::position_center_bottom);
304 QVERIFY(panelTopLevelSpy.wait());
307void TestInputMethodInterface::testActivate()
309 QVERIFY(m_inputMethodIface);
314 QVERIFY(!m_inputMethodIface->
context());
318 QVERIFY(inputMethodActivateSpy.wait());
319 QCOMPARE(inputMethodActivateSpy.count(), 1);
320 QVERIFY(m_inputMethodIface->
context());
324 QVERIFY(inputMethodDeactivateSpy.wait());
325 QCOMPARE(inputMethodActivateSpy.count(), 1);
326 QVERIFY(!m_inputMethodIface->
context());
329void TestInputMethodInterface::testContext()
331 QVERIFY(m_inputMethodIface);
336 QVERIFY(!m_inputMethodIface->
context());
340 QVERIFY(inputMethodActivateSpy.wait());
341 QCOMPARE(inputMethodActivateSpy.count(), 1);
344 QVERIFY(serverContext);
353 imContext->commit_string(serial,
"hello");
354 QVERIFY(commitStringSpy.wait());
355 QCOMPARE(commitStringSpy.count(), serial);
356 QCOMPARE(commitStringSpy.last().at(0).value<quint32>(), serial);
357 QCOMPARE(commitStringSpy.last().at(1).value<QString>(),
"hello");
363 imContext->preedit_styling(0, 5, 1);
364 QVERIFY(preeditStylingSpy.wait());
365 QCOMPARE(preeditStylingSpy.count(), 1);
366 QCOMPARE(preeditStylingSpy.last().at(0).value<quint32>(), 0);
367 QCOMPARE(preeditStylingSpy.last().at(1).value<quint32>(), 5);
368 QCOMPARE(preeditStylingSpy.last().at(2).value<quint32>(), 1);
372 imContext->preedit_cursor(3);
373 QVERIFY(preeditCursorSpy.wait());
374 QCOMPARE(preeditCursorSpy.count(), 1);
375 QCOMPARE(preeditCursorSpy.last().at(0).value<quint32>(), 3);
379 imContext->preedit_string(serial,
"hello",
"kde");
380 QVERIFY(preeditStringSpy.wait());
381 QCOMPARE(preeditStringSpy.count(), 1);
382 QCOMPARE(preeditStringSpy.last().at(0).value<quint32>(), serial);
383 QCOMPARE(preeditStringSpy.last().at(1).value<QString>(),
"hello");
384 QCOMPARE(preeditStringSpy.last().at(2).value<QString>(),
"kde");
389 imContext->delete_surrounding_text(0, 5);
390 QVERIFY(deleteSurroundingSpy.wait());
391 QCOMPARE(deleteSurroundingSpy.count(), 1);
392 QCOMPARE(deleteSurroundingSpy.last().at(0).value<quint32>(), 0);
393 QCOMPARE(deleteSurroundingSpy.last().at(1).value<quint32>(), 5);
397 imContext->cursor_position(2, 4);
398 QVERIFY(cursorPositionSpy.wait());
399 QCOMPARE(cursorPositionSpy.count(), 1);
400 QCOMPARE(cursorPositionSpy.last().at(0).value<quint32>(), 2);
401 QCOMPARE(cursorPositionSpy.last().at(1).value<quint32>(), 4);
406 QVERIFY(invokeActionSpy.wait());
407 QCOMPARE(invokeActionSpy.count(), 1);
408 QCOMPARE(invokeActionSpy.last().at(0).value<quint32>(), 3);
409 QCOMPARE(invokeActionSpy.last().at(1).value<quint32>(), 5);
414 QVERIFY(preferredLanguageSpy.wait());
415 QCOMPARE(preferredLanguageSpy.count(), 1);
416 QCOMPARE(preferredLanguageSpy.last().at(0).value<QString>(),
"gu_IN");
421 QVERIFY(surroundingTextSpy.wait());
422 QCOMPARE(surroundingTextSpy.count(), 1);
423 QCOMPARE(surroundingTextSpy.last().at(0).value<QString>(),
"Hello Plasma!");
424 QCOMPARE(surroundingTextSpy.last().at(1).value<quint32>(), 2);
425 QCOMPARE(surroundingTextSpy.last().at(2).value<quint32>(), 4);
430 QVERIFY(resetSpy.wait());
431 QCOMPARE(resetSpy.count(), 1);
435 QVERIFY(inputMethodDeactivateSpy.wait());
436 QCOMPARE(inputMethodActivateSpy.count(), 1);
437 QVERIFY(!m_inputMethodIface->
context());
438 QVERIFY(!m_inputMethod->
context());
441void TestInputMethodInterface::testGrabkeyboard()
443 QVERIFY(m_inputMethodIface);
448 QVERIFY(!m_inputMethodIface->
context());
452 QVERIFY(inputMethodActivateSpy.wait());
453 QCOMPARE(inputMethodActivateSpy.count(), 1);
456 QVERIFY(serverContext);
462 imContext->key(0, 123, 56, 1);
463 QEXPECT_FAIL(
"",
"We should be not get key event if keyboard is not grabbed", Continue);
464 QVERIFY(!keyEventSpy.wait(200));
467 imContext->modifiers(1234, 0, 0, 0, 0);
468 QEXPECT_FAIL(
"",
"We should be not get modifiers event if keyboard is not grabbed", Continue);
469 QVERIFY(!modifierEventSpy.wait(200));
472 wl_keyboard *keyboard = imContext->grab_keyboard();
479 QVERIFY(inputMethodDeactivateSpy.wait());
480 QCOMPARE(inputMethodActivateSpy.count(), 1);
481 QVERIFY(!m_inputMethodIface->
context());
482 QVERIFY(!m_inputMethod->
context());
485void TestInputMethodInterface::testContentHints_data()
487 QTest::addColumn<KWin::TextInputContentHints>(
"serverHints");
488 QTest::addColumn<quint32>(
"imHint");
489 QTest::addRow(
"Spellcheck") << TextInputContentHints(TextInputContentHint::AutoCorrection)
490 << quint32(QtWaylandServer::zwp_text_input_v1::content_hint_auto_correction);
491 QTest::addRow(
"AutoCapital") << TextInputContentHints(TextInputContentHint::AutoCapitalization)
492 << quint32(QtWaylandServer::zwp_text_input_v1::content_hint_auto_capitalization);
493 QTest::addRow(
"Lowercase") << TextInputContentHints(TextInputContentHint::LowerCase) << quint32(QtWaylandServer::zwp_text_input_v1::content_hint_lowercase);
494 QTest::addRow(
"Uppercase") << TextInputContentHints(TextInputContentHint::UpperCase) << quint32(QtWaylandServer::zwp_text_input_v1::content_hint_uppercase);
495 QTest::addRow(
"Titlecase") << TextInputContentHints(TextInputContentHint::TitleCase) << quint32(QtWaylandServer::zwp_text_input_v1::content_hint_titlecase);
496 QTest::addRow(
"HiddenText") << TextInputContentHints(TextInputContentHint::HiddenText)
497 << quint32(QtWaylandServer::zwp_text_input_v1::content_hint_hidden_text);
498 QTest::addRow(
"SensitiveData") << TextInputContentHints(TextInputContentHint::SensitiveData)
499 << quint32(QtWaylandServer::zwp_text_input_v1::content_hint_sensitive_data);
500 QTest::addRow(
"Latin") << TextInputContentHints(TextInputContentHint::Latin) << quint32(QtWaylandServer::zwp_text_input_v1::content_hint_latin);
501 QTest::addRow(
"Multiline") << TextInputContentHints(TextInputContentHint::MultiLine) << quint32(QtWaylandServer::zwp_text_input_v1::content_hint_multiline);
502 QTest::addRow(
"Auto") << TextInputContentHints(TextInputContentHint::AutoCorrection | TextInputContentHint::AutoCapitalization)
503 << quint32(QtWaylandServer::zwp_text_input_v1::content_hint_auto_correction
504 | QtWaylandServer::zwp_text_input_v1::content_hint_auto_capitalization);
507void TestInputMethodInterface::testContentHints()
509 QVERIFY(m_inputMethodIface);
514 QVERIFY(!m_inputMethodIface->
context());
518 QVERIFY(inputMethodActivateSpy.wait());
519 QCOMPARE(inputMethodActivateSpy.count(), 1);
522 QVERIFY(serverContext);
529 QFETCH(KWin::TextInputContentHints, serverHints);
531 QVERIFY(contentTypeChangedSpy.wait());
532 QCOMPARE(contentTypeChangedSpy.count(), 1);
533 QEXPECT_FAIL(
"SensitiveData",
"SensitiveData content hint need fixing", Continue);
538 QVERIFY(inputMethodDeactivateSpy.wait());
539 QCOMPARE(inputMethodActivateSpy.count(), 1);
540 QVERIFY(!m_inputMethodIface->
context());
541 QVERIFY(!m_inputMethod->
context());
544void TestInputMethodInterface::testContentPurpose_data()
546 QTest::addColumn<KWin::TextInputContentPurpose>(
"serverPurpose");
547 QTest::addColumn<quint32>(
"imPurpose");
549 QTest::newRow(
"Alpha") << TextInputContentPurpose::Alpha << quint32(QtWaylandServer::zwp_text_input_v1::content_purpose_alpha);
550 QTest::newRow(
"Digits") << TextInputContentPurpose::Digits << quint32(QtWaylandServer::zwp_text_input_v1::content_purpose_digits);
551 QTest::newRow(
"Number") << TextInputContentPurpose::Number << quint32(QtWaylandServer::zwp_text_input_v1::content_purpose_number);
552 QTest::newRow(
"Phone") << TextInputContentPurpose::Phone << quint32(QtWaylandServer::zwp_text_input_v1::content_purpose_phone);
553 QTest::newRow(
"Url") << TextInputContentPurpose::Url << quint32(QtWaylandServer::zwp_text_input_v1::content_purpose_url);
554 QTest::newRow(
"Email") << TextInputContentPurpose::Email << quint32(QtWaylandServer::zwp_text_input_v1::content_purpose_email);
555 QTest::newRow(
"Name") << TextInputContentPurpose::Name << quint32(QtWaylandServer::zwp_text_input_v1::content_purpose_name);
556 QTest::newRow(
"Password") << TextInputContentPurpose::Password << quint32(QtWaylandServer::zwp_text_input_v1::content_purpose_password);
557 QTest::newRow(
"Date") << TextInputContentPurpose::Date << quint32(QtWaylandServer::zwp_text_input_v1::content_purpose_date);
558 QTest::newRow(
"Time") << TextInputContentPurpose::Time << quint32(QtWaylandServer::zwp_text_input_v1::content_purpose_time);
559 QTest::newRow(
"DateTime") << TextInputContentPurpose::DateTime << quint32(QtWaylandServer::zwp_text_input_v1::content_purpose_datetime);
560 QTest::newRow(
"Terminal") << TextInputContentPurpose::Terminal << quint32(QtWaylandServer::zwp_text_input_v1::content_purpose_terminal);
561 QTest::newRow(
"Normal") << TextInputContentPurpose::Normal << quint32(QtWaylandServer::zwp_text_input_v1::content_purpose_normal);
562 QTest::newRow(
"Pin") << TextInputContentPurpose::Pin << quint32(QtWaylandServer::zwp_text_input_v1::content_purpose_password);
565void TestInputMethodInterface::testContentPurpose()
567 QVERIFY(m_inputMethodIface);
572 QVERIFY(!m_inputMethodIface->
context());
576 QVERIFY(inputMethodActivateSpy.wait());
577 QCOMPARE(inputMethodActivateSpy.count(), 1);
580 QVERIFY(serverContext);
589 QVERIFY(contentTypeChangedSpy.wait());
590 QCOMPARE(contentTypeChangedSpy.count(), 1);
591 QEXPECT_FAIL(
"Pin",
"Pin should return content_purpose_password", Continue);
596 QVERIFY(inputMethodDeactivateSpy.wait());
597 QCOMPARE(inputMethodActivateSpy.count(), 1);
598 QVERIFY(!m_inputMethodIface->
context());
599 QVERIFY(!m_inputMethod->
context());
602void TestInputMethodInterface::testKeyboardGrab()
604 QVERIFY(m_inputMethodIface);
608 QVERIFY(inputMethodActivateSpy.wait());
613 KWayland::Client::Keyboard *keyboard =
new KWayland::Client::Keyboard(
this);
614 keyboard->setup(imContext->grab_keyboard());
615 QVERIFY(keyboard->isValid());
616 QVERIFY(keyboardGrabSpy.count() || keyboardGrabSpy.wait());
618 QSignalSpy keyboardSpy(keyboard, &KWayland::Client::Keyboard::keyChanged);
622 QCOMPARE(keyboardSpy.count(), 2);
628#include "test_inputmethod_interface.moc"
void preferred_language(QString lang)
void surrounding_text(QString lang, quint32 cursor, quint32 anchor)
void zwp_input_method_context_v1_content_type(uint32_t hint, uint32_t purpose) override
void zwp_input_method_context_v1_invoke_action(uint32_t button, uint32_t index) override
void content_type_changed()
void zwp_input_method_context_v1_reset() override
void invoke_action(quint32 button, quint32 index)
void zwp_input_method_context_v1_surrounding_text(const QString &text, uint32_t cursor, uint32_t anchor) override
void zwp_input_method_context_v1_preferred_language(const QString &language) override
void surfaceCreated(KWin::SurfaceInterface *surface)
Class holding the Wayland server display loop.
bool addSocketName(const QString &name=QString())
void deleteSurroundingText(qint32 index, quint32 length)
InputMethodGrabV1 * keyboardGrab() const
void sendSurroundingText(const QString &text, quint32 cursor, quint32 anchor)
void preeditCursor(qint32 index)
void sendInvokeAction(quint32 button, quint32 index)
void preeditString(quint32 serial, const QString &text, const QString &commit)
void key(quint32 serial, quint32 time, quint32 key, bool pressed)
void modifiers(quint32 serial, quint32 mods_depressed, quint32 mods_latched, quint32 mods_locked, quint32 group)
void cursorPosition(qint32 index, qint32 anchor)
void sendContentType(KWin::TextInputContentHints hint, KWin::TextInputContentPurpose purpose)
void keyboardGrabRequested(InputMethodGrabV1 *keyboardGrab)
void sendPreferredLanguage(const QString &language)
void preeditStyling(quint32 index, quint32 length, quint32 style)
void commitString(quint32 serial, const QString &text)
Represents a Seat on the Wayland Display.
Resource representing a wl_surface.
KWayland::Client::Registry * registry