1 /*
2 SPDX-FileCopyrightText: 2021 Roman Gilg <subdiff@gmail.com>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only
5 */
6 #include <QtTest>
7
8 #include "../../src/client/compositor.h"
9 #include "../../src/client/connection_thread.h"
10 #include "../../src/client/event_queue.h"
11 #include "../../src/client/input_method_v2.h"
12 #include "../../src/client/registry.h"
13 #include "../../src/client/seat.h"
14 #include "../../src/client/surface.h"
15
16 #include "../../server/compositor.h"
17 #include "../../server/display.h"
18 #include "../../server/input_method_v2.h"
19 #include "../../server/seat.h"
20 #include "../../server/surface.h"
21
22 #include <linux/input.h>
23
24 class input_method_v2_test : public QObject
25 {
26 Q_OBJECT
27 private Q_SLOTS:
28 void init();
29 void cleanup();
30
31 void test_activate();
32 void test_surrounding_text();
33 void test_content_hints_data();
34 void test_content_hints();
35 void test_content_purpose_data();
36 void test_content_purpose();
37 void test_commit();
38 void test_popup_surface();
39 void test_keyboard_grab();
40
41 private:
42 Wrapland::Client::input_method_v2* get_input_method();
43
44 struct {
45 Wrapland::Server::Display* display{nullptr};
46 Wrapland::Server::Compositor* compositor{nullptr};
47 Wrapland::Server::Seat* seat{nullptr};
48 Wrapland::Server::text_input_manager_v3* text_input{nullptr};
49 Wrapland::Server::input_method_manager_v2* input_method{nullptr};
50 } server;
51
52 struct client {
53 Wrapland::Client::ConnectionThread* connection{nullptr};
54 Wrapland::Client::EventQueue* queue{nullptr};
55 Wrapland::Client::Registry* registry{nullptr};
56 Wrapland::Client::Compositor* compositor{nullptr};
57 Wrapland::Client::Seat* seat{nullptr};
58 Wrapland::Client::text_input_manager_v3* text_input{nullptr};
59 Wrapland::Client::input_method_manager_v2* input_method{nullptr};
60 QThread* thread{nullptr};
61 } client1;
62 };
63
64 constexpr auto socket_name = "wrapland-test-input-method-v2-0";
65
init()66 void input_method_v2_test::init()
67 {
68 qRegisterMetaType<Wrapland::Server::Surface*>();
69
70 delete server.display;
71 server.display = new Wrapland::Server::Display(this);
72 server.display->setSocketName(std::string(socket_name));
73 server.display->start();
74
75 server.display->createShm();
76 server.seat = server.display->createSeat();
77 server.seat->setHasKeyboard(true);
78
79 server.compositor = server.display->createCompositor();
80 server.text_input = server.display->createTextInputManagerV3();
81 server.input_method = server.display->createInputMethodManagerV2();
82
83 // setup connection
84 client1.connection = new Wrapland::Client::ConnectionThread;
85 QSignalSpy connectedSpy(client1.connection,
86 &Wrapland::Client::ConnectionThread::establishedChanged);
87 QVERIFY(connectedSpy.isValid());
88 client1.connection->setSocketName(socket_name);
89
90 client1.thread = new QThread(this);
91 client1.connection->moveToThread(client1.thread);
92 client1.thread->start();
93
94 client1.connection->establishConnection();
95 QVERIFY(connectedSpy.count() || connectedSpy.wait());
96 QCOMPARE(connectedSpy.count(), 1);
97
98 client1.queue = new Wrapland::Client::EventQueue(this);
99 client1.queue->setup(client1.connection);
100
101 Wrapland::Client::Registry registry;
102 QSignalSpy interfacesAnnouncedSpy(®istry, &Wrapland::Client::Registry::interfacesAnnounced);
103 QVERIFY(interfacesAnnouncedSpy.isValid());
104 registry.setEventQueue(client1.queue);
105 registry.create(client1.connection);
106 QVERIFY(registry.isValid());
107 registry.setup();
108 QVERIFY(interfacesAnnouncedSpy.wait());
109
110 client1.seat = registry.createSeat(
111 registry.interface(Wrapland::Client::Registry::Interface::Seat).name,
112 registry.interface(Wrapland::Client::Registry::Interface::Seat).version,
113 this);
114 QVERIFY(client1.seat->isValid());
115
116 client1.compositor = registry.createCompositor(
117 registry.interface(Wrapland::Client::Registry::Interface::Compositor).name,
118 registry.interface(Wrapland::Client::Registry::Interface::Compositor).version,
119 this);
120 QVERIFY(client1.compositor->isValid());
121
122 client1.text_input = registry.createTextInputManagerV3(
123 registry.interface(Wrapland::Client::Registry::Interface::TextInputManagerV3).name,
124 registry.interface(Wrapland::Client::Registry::Interface::TextInputManagerV3).version,
125 this);
126 QVERIFY(client1.text_input->isValid());
127
128 client1.input_method = registry.createInputMethodManagerV2(
129 registry.interface(Wrapland::Client::Registry::Interface::InputMethodManagerV2).name,
130 registry.interface(Wrapland::Client::Registry::Interface::InputMethodManagerV2).version,
131 this);
132 QVERIFY(client1.input_method->isValid());
133 }
134
cleanup()135 void input_method_v2_test::cleanup()
136 {
137 #define CLEANUP(variable) \
138 if (variable) { \
139 delete variable; \
140 variable = nullptr; \
141 }
142 CLEANUP(client1.input_method)
143 CLEANUP(client1.text_input)
144 CLEANUP(client1.seat)
145 CLEANUP(client1.compositor)
146 CLEANUP(client1.queue)
147 if (client1.connection) {
148 client1.connection->deleteLater();
149 client1.connection = nullptr;
150 }
151 if (client1.thread) {
152 client1.thread->quit();
153 client1.thread->wait();
154 delete client1.thread;
155 client1.thread = nullptr;
156 }
157
158 CLEANUP(server.input_method)
159 CLEANUP(server.text_input)
160 CLEANUP(server.compositor)
161 CLEANUP(server.seat)
162 CLEANUP(server.display)
163 #undef CLEANUP
164 }
165
get_input_method()166 Wrapland::Client::input_method_v2* input_method_v2_test::get_input_method()
167 {
168 return client1.input_method->get_input_method(client1.seat);
169 }
170
test_activate()171 void input_method_v2_test::test_activate()
172 {
173 // This test verifies that activation is done correctly.
174 QSignalSpy input_method_spy(server.seat, &Wrapland::Server::Seat::input_method_v2_changed);
175 QVERIFY(input_method_spy.isValid());
176
177 auto input_method = std::unique_ptr<Wrapland::Client::input_method_v2>(get_input_method());
178 QVERIFY(input_method);
179 QVERIFY(input_method_spy.wait());
180
181 auto server_input_method = server.seat->get_input_method_v2();
182 QVERIFY(server_input_method);
183
184 QSignalSpy done_spy(input_method.get(), &Wrapland::Client::input_method_v2::done);
185 QVERIFY(done_spy.isValid());
186
187 // Now activate.
188 server_input_method->set_active(true);
189 server_input_method->done();
190
191 QVERIFY(done_spy.wait());
192 QCOMPARE(input_method->state().active, true);
193
194 // Now deactivate.
195 server_input_method->set_active(false);
196 server_input_method->done();
197
198 QVERIFY(done_spy.wait());
199 QCOMPARE(input_method->state().active, false);
200 }
201
test_surrounding_text()202 void input_method_v2_test::test_surrounding_text()
203 {
204 // This test verifies that surrounding text is sent correctly.
205 QSignalSpy input_method_spy(server.seat, &Wrapland::Server::Seat::input_method_v2_changed);
206 QVERIFY(input_method_spy.isValid());
207
208 auto input_method = std::unique_ptr<Wrapland::Client::input_method_v2>(get_input_method());
209 QVERIFY(input_method);
210 QVERIFY(input_method_spy.wait());
211
212 auto server_input_method = server.seat->get_input_method_v2();
213 QVERIFY(server_input_method);
214
215 QSignalSpy done_spy(input_method.get(), &Wrapland::Client::input_method_v2::done);
216 QVERIFY(done_spy.isValid());
217
218 QVERIFY(input_method->state().surrounding_text.data.empty());
219 QCOMPARE(input_method->state().surrounding_text.cursor_position, 0);
220 QCOMPARE(input_method->state().surrounding_text.selection_anchor, 0);
221 QCOMPARE(input_method->state().surrounding_text.change_cause,
222 Wrapland::Client::text_input_v3_change_cause::other);
223
224 // Now activate and send surrounding text.
225 auto const surrounding_text = "100 €, 100 $";
226
227 server_input_method->set_active(true);
228 server_input_method->set_surrounding_text(
229 surrounding_text, 5, 6, Wrapland::Server::text_input_v3_change_cause::input_method);
230 server_input_method->done();
231
232 QVERIFY(done_spy.wait());
233 QCOMPARE(input_method->state().active, true);
234 QCOMPARE(input_method->state().surrounding_text.data, surrounding_text);
235 QCOMPARE(input_method->state().surrounding_text.cursor_position, 5);
236 QCOMPARE(input_method->state().surrounding_text.selection_anchor, 6);
237 QCOMPARE(input_method->state().surrounding_text.change_cause,
238 Wrapland::Client::text_input_v3_change_cause::input_method);
239
240 // Now deactivate.
241 server_input_method->set_active(false);
242 server_input_method->done();
243
244 // Not active anymore but surrounding text data still available.
245 QVERIFY(done_spy.wait());
246 QCOMPARE(input_method->state().active, false);
247 QCOMPARE(input_method->state().surrounding_text.data, surrounding_text);
248 QCOMPARE(input_method->state().surrounding_text.cursor_position, 5);
249 QCOMPARE(input_method->state().surrounding_text.selection_anchor, 6);
250 QCOMPARE(input_method->state().surrounding_text.change_cause,
251 Wrapland::Client::text_input_v3_change_cause::input_method);
252
253 // Activate again but do not set surrounding text.
254 server_input_method->set_active(true);
255 server_input_method->done();
256
257 // Active again and state reset now.
258 QVERIFY(done_spy.wait());
259 QVERIFY(input_method->state().surrounding_text.data.empty());
260 QCOMPARE(input_method->state().surrounding_text.cursor_position, 0);
261 QCOMPARE(input_method->state().surrounding_text.selection_anchor, 0);
262 QCOMPARE(input_method->state().surrounding_text.change_cause,
263 Wrapland::Client::text_input_v3_change_cause::other);
264 }
265
266 using server_hint = Wrapland::Server::text_input_v3_content_hint;
267 using server_hints = Wrapland::Server::text_input_v3_content_hints;
268 using client_hint = Wrapland::Client::text_input_v3_content_hint;
269 using client_hints = Wrapland::Client::text_input_v3_content_hints;
270
271 using server_purpose = Wrapland::Server::text_input_v3_content_purpose;
272 using client_purpose = Wrapland::Client::text_input_v3_content_purpose;
273
test_content_hints_data()274 void input_method_v2_test::test_content_hints_data()
275 {
276 QTest::addColumn<client_hints>("clthints");
277 QTest::addColumn<server_hints>("srvhints");
278
279 QTest::newRow("completion") << client_hints(client_hint::completion)
280 << server_hints(server_hint::completion);
281 QTest::newRow("Correction") << client_hints(client_hint::spellcheck)
282 << server_hints(server_hint::spellcheck);
283 QTest::newRow("Capitalization") << client_hints(client_hint::auto_capitalization)
284 << server_hints(server_hint::auto_capitalization);
285 QTest::newRow("lowercase") << client_hints(client_hint::lowercase)
286 << server_hints(server_hint::lowercase);
287 QTest::newRow("uppercase") << client_hints(client_hint::uppercase)
288 << server_hints(server_hint::uppercase);
289 QTest::newRow("titlecase") << client_hints(client_hint::titlecase)
290 << server_hints(server_hint::titlecase);
291 QTest::newRow("hidden_text") << client_hints(client_hint::hidden_text)
292 << server_hints(server_hint::hidden_text);
293 QTest::newRow("sensitive_data")
294 << client_hints(client_hint::sensitive_data) << server_hints(server_hint::sensitive_data);
295 QTest::newRow("latin") << client_hints(client_hint::latin) << server_hints(server_hint::latin);
296 QTest::newRow("multiline") << client_hints(client_hint::multiline)
297 << server_hints(server_hint::multiline);
298
299 QTest::newRow("autos") << (client_hint::completion | client_hint::spellcheck
300 | client_hint::auto_capitalization)
301 << (server_hint::completion | server_hint::spellcheck
302 | server_hint::auto_capitalization);
303
304 // all has combinations which don't make sense - for example both lowercase and uppercase.
305 QTest::newRow("all") << (client_hint::completion | client_hint::spellcheck
306 | client_hint::auto_capitalization | client_hint::lowercase
307 | client_hint::uppercase | client_hint::titlecase
308 | client_hint::hidden_text | client_hint::sensitive_data
309 | client_hint::latin | client_hint::multiline)
310 << (server_hint::completion | server_hint::spellcheck
311 | server_hint::auto_capitalization | server_hint::lowercase
312 | server_hint::uppercase | server_hint::titlecase
313 | server_hint::hidden_text | server_hint::sensitive_data
314 | server_hint::latin | server_hint::multiline);
315 }
316
test_content_hints()317 void input_method_v2_test::test_content_hints()
318 {
319 // This test verifies that content hints are sent correctly.
320 QSignalSpy input_method_spy(server.seat, &Wrapland::Server::Seat::input_method_v2_changed);
321 QVERIFY(input_method_spy.isValid());
322
323 auto input_method = std::unique_ptr<Wrapland::Client::input_method_v2>(get_input_method());
324 QVERIFY(input_method);
325 QVERIFY(input_method_spy.wait());
326
327 auto server_input_method = server.seat->get_input_method_v2();
328 QVERIFY(server_input_method);
329
330 QSignalSpy done_spy(input_method.get(), &Wrapland::Client::input_method_v2::done);
331 QVERIFY(done_spy.isValid());
332
333 QCOMPARE(input_method->state().content.hints, client_hints());
334
335 // Now activate and send content hints.
336 server_input_method->set_active(true);
337 QFETCH(server_hints, srvhints);
338 QFETCH(client_hints, clthints);
339 server_input_method->set_content_type(srvhints, server_purpose::normal);
340 server_input_method->done();
341
342 QVERIFY(done_spy.wait());
343 QCOMPARE(input_method->state().active, true);
344 QCOMPARE(input_method->state().content.hints, clthints);
345
346 // Now deactivate.
347 server_input_method->set_active(false);
348 server_input_method->done();
349
350 // Not active anymore but content hints data still available.
351 QVERIFY(done_spy.wait());
352 QCOMPARE(input_method->state().active, false);
353 QCOMPARE(input_method->state().content.hints, clthints);
354
355 // Activate again but do not set content hints.
356 server_input_method->set_active(true);
357 server_input_method->done();
358
359 // Active again and state reset now.
360 QVERIFY(done_spy.wait());
361 QCOMPARE(input_method->state().content.hints, client_hints());
362 }
363
test_content_purpose_data()364 void input_method_v2_test::test_content_purpose_data()
365 {
366 QTest::addColumn<client_purpose>("cltpurpose");
367 QTest::addColumn<server_purpose>("srvpurpose");
368
369 QTest::newRow("alpha") << client_purpose::alpha << server_purpose::alpha;
370 QTest::newRow("digits") << client_purpose::digits << server_purpose::digits;
371 QTest::newRow("number") << client_purpose::number << server_purpose::number;
372 QTest::newRow("phone") << client_purpose::phone << server_purpose::phone;
373 QTest::newRow("url") << client_purpose::url << server_purpose::url;
374 QTest::newRow("email") << client_purpose::email << server_purpose::email;
375 QTest::newRow("name") << client_purpose::name << server_purpose::name;
376 QTest::newRow("password") << client_purpose::password << server_purpose::password;
377 QTest::newRow("date") << client_purpose::date << server_purpose::date;
378 QTest::newRow("time") << client_purpose::time << server_purpose::time;
379 QTest::newRow("datetime") << client_purpose::datetime << server_purpose::datetime;
380 QTest::newRow("terminal") << client_purpose::terminal << server_purpose::terminal;
381 }
382
test_content_purpose()383 void input_method_v2_test::test_content_purpose()
384 {
385 // This test verifies that the content purpose is sent correctly.
386 QSignalSpy input_method_spy(server.seat, &Wrapland::Server::Seat::input_method_v2_changed);
387 QVERIFY(input_method_spy.isValid());
388
389 auto input_method = std::unique_ptr<Wrapland::Client::input_method_v2>(get_input_method());
390 QVERIFY(input_method);
391 QVERIFY(input_method_spy.wait());
392
393 auto server_input_method = server.seat->get_input_method_v2();
394 QVERIFY(server_input_method);
395
396 QSignalSpy done_spy(input_method.get(), &Wrapland::Client::input_method_v2::done);
397 QVERIFY(done_spy.isValid());
398
399 QCOMPARE(input_method->state().content.purpose, client_purpose::normal);
400
401 // Now activate and send content hints.
402 server_input_method->set_active(true);
403 QFETCH(server_purpose, srvpurpose);
404 QFETCH(client_purpose, cltpurpose);
405 server_input_method->set_content_type(server_hints(), srvpurpose);
406 server_input_method->done();
407
408 QVERIFY(done_spy.wait());
409 QCOMPARE(input_method->state().active, true);
410 QCOMPARE(input_method->state().content.purpose, cltpurpose);
411
412 // Now deactivate.
413 server_input_method->set_active(false);
414 server_input_method->done();
415
416 // Not active anymore but content hints data still available.
417 QVERIFY(done_spy.wait());
418 QCOMPARE(input_method->state().active, false);
419 QCOMPARE(input_method->state().content.purpose, cltpurpose);
420
421 // Activate again but do not set content hints.
422 server_input_method->set_active(true);
423 server_input_method->done();
424
425 // Active again and state reset now.
426 QVERIFY(done_spy.wait());
427 QCOMPARE(input_method->state().content.purpose, client_purpose::normal);
428 }
429
test_commit()430 void input_method_v2_test::test_commit()
431 {
432 // This test verifies that commit string, preedit and delete surrounding text is sent correctly.
433 QSignalSpy input_method_spy(server.seat, &Wrapland::Server::Seat::input_method_v2_changed);
434 QVERIFY(input_method_spy.isValid());
435
436 auto input_method = std::unique_ptr<Wrapland::Client::input_method_v2>(get_input_method());
437 QVERIFY(input_method);
438 QVERIFY(input_method_spy.wait());
439
440 auto server_input_method = server.seat->get_input_method_v2();
441 QVERIFY(server_input_method);
442
443 QSignalSpy done_spy(input_method.get(), &Wrapland::Client::input_method_v2::done);
444 QVERIFY(done_spy.isValid());
445
446 QCOMPARE(input_method->state().content.purpose, client_purpose::normal);
447
448 // Now activate and send surrounding text to indicate support for it.
449 server_input_method->set_active(true);
450 server_input_method->set_surrounding_text(
451 "", 0, 0, Wrapland::Server::text_input_v3_change_cause::other);
452 server_input_method->done();
453
454 QVERIFY(done_spy.wait());
455 QCOMPARE(input_method->state().active, true);
456 QVERIFY(input_method->state().surrounding_text.data.empty());
457 QCOMPARE(input_method->state().surrounding_text.cursor_position, 0);
458 QCOMPARE(input_method->state().surrounding_text.selection_anchor, 0);
459 QCOMPARE(input_method->state().surrounding_text.change_cause,
460 Wrapland::Client::text_input_v3_change_cause::other);
461
462 QSignalSpy commit_spy(server_input_method, &Wrapland::Server::input_method_v2::state_committed);
463 QVERIFY(commit_spy.isValid());
464
465 // Now send data.
466 auto commit_text = "commit string text";
467 auto preedit_text = "preedit string text";
468 input_method->commit_string(commit_text);
469 input_method->set_preedit_string(preedit_text, 1, 2);
470 input_method->commit();
471
472 QCOMPARE(server_input_method->state().preedit_string.update, false);
473 QCOMPARE(server_input_method->state().preedit_string.data, "");
474 QCOMPARE(server_input_method->state().preedit_string.cursor_begin, 0);
475 QCOMPARE(server_input_method->state().preedit_string.cursor_end, 0);
476 QCOMPARE(server_input_method->state().commit_string.update, false);
477 QCOMPARE(server_input_method->state().commit_string.data, "");
478 QCOMPARE(server_input_method->state().delete_surrounding_text.update, false);
479 QCOMPARE(server_input_method->state().delete_surrounding_text.before_length, 0);
480 QCOMPARE(server_input_method->state().delete_surrounding_text.after_length, 0);
481
482 QVERIFY(commit_spy.wait());
483 QCOMPARE(server_input_method->state().preedit_string.update, true);
484 QCOMPARE(server_input_method->state().preedit_string.data, preedit_text);
485 QCOMPARE(server_input_method->state().preedit_string.cursor_begin, 1);
486 QCOMPARE(server_input_method->state().preedit_string.cursor_end, 2);
487 QCOMPARE(server_input_method->state().commit_string.update, true);
488 QCOMPARE(server_input_method->state().commit_string.data, commit_text);
489 QCOMPARE(server_input_method->state().delete_surrounding_text.update, false);
490 QCOMPARE(server_input_method->state().delete_surrounding_text.before_length, 0);
491 QCOMPARE(server_input_method->state().delete_surrounding_text.after_length, 0);
492
493 // Now deactivate.
494 server_input_method->set_active(false);
495 server_input_method->done();
496
497 // Not active anymore but data is still available.
498 QVERIFY(done_spy.wait());
499 QCOMPARE(server_input_method->state().preedit_string.update, true);
500 QCOMPARE(server_input_method->state().preedit_string.data, preedit_text);
501 QCOMPARE(server_input_method->state().preedit_string.cursor_begin, 1);
502 QCOMPARE(server_input_method->state().preedit_string.cursor_end, 2);
503 QCOMPARE(server_input_method->state().commit_string.update, true);
504 QCOMPARE(server_input_method->state().commit_string.data, commit_text);
505 QCOMPARE(server_input_method->state().delete_surrounding_text.update, false);
506 QCOMPARE(server_input_method->state().delete_surrounding_text.before_length, 0);
507 QCOMPARE(server_input_method->state().delete_surrounding_text.after_length, 0);
508
509 // Activate again. Data is reset immediately.
510 server_input_method->set_active(true);
511 QCOMPARE(server_input_method->state().preedit_string.update, false);
512 QCOMPARE(server_input_method->state().preedit_string.data, "");
513 QCOMPARE(server_input_method->state().preedit_string.cursor_begin, 0);
514 QCOMPARE(server_input_method->state().preedit_string.cursor_end, 0);
515 QCOMPARE(server_input_method->state().commit_string.update, false);
516 QCOMPARE(server_input_method->state().commit_string.data, "");
517 QCOMPARE(server_input_method->state().delete_surrounding_text.update, false);
518 QCOMPARE(server_input_method->state().delete_surrounding_text.before_length, 0);
519 QCOMPARE(server_input_method->state().delete_surrounding_text.after_length, 0);
520
521 // Set surrounding text for testing deletion of surrounding text.
522 auto const surrounding_text = "100 €, 100 $";
523
524 server_input_method->set_active(true);
525 server_input_method->set_surrounding_text(
526 surrounding_text, 5, 6, Wrapland::Server::text_input_v3_change_cause::input_method);
527 server_input_method->done();
528
529 QVERIFY(done_spy.wait());
530 QCOMPARE(input_method->state().active, true);
531 QCOMPARE(input_method->state().surrounding_text.data, surrounding_text);
532 QCOMPARE(input_method->state().surrounding_text.cursor_position, 5);
533 QCOMPARE(input_method->state().surrounding_text.selection_anchor, 6);
534 QCOMPARE(input_method->state().surrounding_text.change_cause,
535 Wrapland::Client::text_input_v3_change_cause::input_method);
536
537 input_method->delete_surrounding_text(1, 2);
538 input_method->commit();
539
540 QVERIFY(commit_spy.wait());
541 QCOMPARE(server_input_method->state().preedit_string.update, false);
542 QCOMPARE(server_input_method->state().preedit_string.data, "");
543 QCOMPARE(server_input_method->state().preedit_string.cursor_begin, 0);
544 QCOMPARE(server_input_method->state().preedit_string.cursor_end, 0);
545 QCOMPARE(server_input_method->state().commit_string.update, false);
546 QCOMPARE(server_input_method->state().commit_string.data, "");
547 QCOMPARE(server_input_method->state().delete_surrounding_text.update, true);
548 QCOMPARE(server_input_method->state().delete_surrounding_text.before_length, 1);
549 QCOMPARE(server_input_method->state().delete_surrounding_text.after_length, 2);
550 }
551
test_popup_surface()552 void input_method_v2_test::test_popup_surface()
553 {
554 // This test verifies that the popup surface works as expected.
555 auto surface = std::unique_ptr<Wrapland::Client::Surface>(client1.compositor->createSurface());
556 QSignalSpy input_method_spy(server.seat, &Wrapland::Server::Seat::input_method_v2_changed);
557 QVERIFY(input_method_spy.isValid());
558
559 auto input_method = std::unique_ptr<Wrapland::Client::input_method_v2>(get_input_method());
560 QVERIFY(input_method);
561 QVERIFY(input_method_spy.wait());
562
563 auto server_input_method = server.seat->get_input_method_v2();
564 QVERIFY(server_input_method);
565
566 QSignalSpy popup_spy(server_input_method,
567 &Wrapland::Server::input_method_v2::popup_surface_created);
568 QVERIFY(popup_spy.isValid());
569
570 auto popup = std::unique_ptr<Wrapland::Client::input_popup_surface_v2>(
571 input_method->get_input_popup_surface(surface.get()));
572
573 QVERIFY(popup_spy.wait());
574 auto server_popup
575 = popup_spy.first().first().value<Wrapland::Server::input_method_popup_surface_v2*>();
576
577 QSignalSpy rect_spy(popup.get(),
578 &Wrapland::Client::input_popup_surface_v2::text_input_rectangle_changed);
579 QVERIFY(rect_spy.isValid());
580
581 auto rect = QRect(1, 2, 3, 4);
582 server_popup->set_text_input_rectangle(rect);
583 QVERIFY(rect_spy.wait());
584
585 QCOMPARE(popup->text_input_rectangle(), rect);
586 }
587
test_keyboard_grab()588 void input_method_v2_test::test_keyboard_grab()
589 {
590 // This test verifies that a keyboard grab works as expected.
591 auto surface = std::unique_ptr<Wrapland::Client::Surface>(client1.compositor->createSurface());
592 QSignalSpy input_method_spy(server.seat, &Wrapland::Server::Seat::input_method_v2_changed);
593 QVERIFY(input_method_spy.isValid());
594
595 auto input_method = std::unique_ptr<Wrapland::Client::input_method_v2>(get_input_method());
596 QVERIFY(input_method);
597 QVERIFY(input_method_spy.wait());
598
599 auto server_input_method = server.seat->get_input_method_v2();
600 QVERIFY(server_input_method);
601
602 QSignalSpy grab_spy(server_input_method, &Wrapland::Server::input_method_v2::keyboard_grabbed);
603 QVERIFY(grab_spy.isValid());
604
605 auto grab = std::unique_ptr<Wrapland::Client::input_method_keyboard_grab_v2>(
606 input_method->grab_keyboard());
607
608 QVERIFY(grab_spy.wait());
609 auto server_grab
610 = grab_spy.first().first().value<Wrapland::Server::input_method_keyboard_grab_v2*>();
611
612 QSignalSpy keymap_spy(grab.get(),
613 &Wrapland::Client::input_method_keyboard_grab_v2::keymap_changed);
614 QVERIFY(keymap_spy.isValid());
615
616 server_grab->set_keymap("foo");
617 QVERIFY(keymap_spy.wait());
618
619 auto fd = keymap_spy.first().first().toInt();
620 QVERIFY(fd != -1);
621 QCOMPARE(keymap_spy.first().last().value<quint32>(), 3u);
622
623 QFile file;
624 QVERIFY(file.open(fd, QIODevice::ReadOnly));
625 auto address = reinterpret_cast<char*>(file.map(0, keymap_spy.first().last().value<quint32>()));
626 QVERIFY(address);
627 QCOMPARE(qstrcmp(address, "foo"), 0);
628 file.close();
629
630 QSignalSpy key_spy(grab.get(), &Wrapland::Client::input_method_keyboard_grab_v2::key_changed);
631 QVERIFY(key_spy.isValid());
632
633 server_grab->press_key(1, KEY_K);
634 QVERIFY(key_spy.wait());
635
636 QCOMPARE(key_spy.first().at(0).value<uint32_t>(), KEY_K);
637 QCOMPARE(key_spy.first().at(1).value<Wrapland::Client::Keyboard::KeyState>(),
638 Wrapland::Client::Keyboard::KeyState::Pressed);
639 QCOMPARE(key_spy.first().at(2).value<uint32_t>(), 1);
640
641 QSignalSpy modifiers_spy(grab.get(),
642 &Wrapland::Client::input_method_keyboard_grab_v2::modifiers_changed);
643 QVERIFY(modifiers_spy.isValid());
644
645 server_grab->update_modifiers(1, 2, 3, 4);
646 QVERIFY(modifiers_spy.wait());
647
648 QCOMPARE(modifiers_spy.first().at(0).value<uint32_t>(), 1);
649 QCOMPARE(modifiers_spy.first().at(1).value<uint32_t>(), 2);
650 QCOMPARE(modifiers_spy.first().at(2).value<uint32_t>(), 3);
651 QCOMPARE(modifiers_spy.first().at(3).value<uint32_t>(), 4);
652
653 QSignalSpy repeat_spy(grab.get(),
654 &Wrapland::Client::input_method_keyboard_grab_v2::repeat_changed);
655 QVERIFY(repeat_spy.isValid());
656
657 server_grab->set_repeat_info(1, 2);
658 QVERIFY(repeat_spy.wait());
659
660 QCOMPARE(grab->repeat_rate(), 1);
661 QCOMPARE(grab->repeat_delay(), 2);
662 }
663
664 QTEST_GUILESS_MAIN(input_method_v2_test)
665 #include "input_method_v2.moc"
666