1 /*
2 * Copyright © 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, [email protected]
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the
13 * next paragraph) shall be included in all copies or substantial
14 * portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 */
25
26 #include <stdlib.h>
27 #include <assert.h>
28 #include <errno.h>
29 #include <string.h>
30 #include <stdio.h>
31 #include <sys/un.h>
32 #include <time.h>
33 #include <unistd.h>
34
35 #include "wayland-client.h"
36 #include "wayland-server.h"
37 #include "test-runner.h"
38
39 #define ARRAY_LENGTH(a) (sizeof(a) / sizeof(a)[0])
40
41 /* Ensure the connection doesn't fail due to lack of XDG_RUNTIME_DIR. */
42 static const char *
require_xdg_runtime_dir(void)43 require_xdg_runtime_dir(void)
44 {
45 char *val = getenv("XDG_RUNTIME_DIR");
46 assert(val && val[0] == '/' && "set $XDG_RUNTIME_DIR to run this test");
47
48 return val;
49 }
50
51 struct expected_compositor_message {
52 enum wl_protocol_logger_type type;
53 const char *class;
54 int opcode;
55 const char *message_name;
56 int args_count;
57 };
58
59 struct compositor {
60 struct wl_display *display;
61 struct wl_event_loop *loop;
62 struct wl_protocol_logger *logger;
63
64 struct expected_compositor_message *expected_msg;
65 int expected_msg_count;
66 int actual_msg_count;
67 struct wl_client *client;
68 };
69
70 struct expected_client_message {
71 enum wl_client_message_type type;
72 enum wl_client_message_discarded_reason discarded_reason;
73 const char *class;
74 int opcode;
75 const char *message_name;
76 int args_count;
77 };
78
79 struct client {
80 struct wl_display *display;
81 struct wl_callback *cb;
82 struct wl_client_observer *sequence_observer;
83 struct wl_client_observer *stderr_logger;
84
85 struct expected_client_message *expected_msg;
86 int expected_msg_count;
87 int actual_msg_count;
88 };
89
90 #define ASSERT_LT(arg1, arg2, ...) \
91 if (arg1 >= arg2) \
92 fprintf(stderr, __VA_ARGS__); \
93 assert(arg1 < arg2)
94
95 #define ASSERT_EQ(arg1, arg2, ...) \
96 if (arg1 != arg2) \
97 fprintf(stderr, __VA_ARGS__); \
98 assert(arg1 == arg2)
99
100 #define ASSERT_STR_EQ(arg1, arg2, ...) \
101 if (strcmp(arg1, arg2) != 0) \
102 fprintf(stderr, __VA_ARGS__); \
103 assert(strcmp(arg1, arg2) == 0)
104
105 static void
compositor_sequence_observer_func(void * user_data,enum wl_protocol_logger_type actual_type,const struct wl_protocol_logger_message * actual_msg)106 compositor_sequence_observer_func(
107 void *user_data, enum wl_protocol_logger_type actual_type,
108 const struct wl_protocol_logger_message *actual_msg)
109 {
110 struct compositor *c = user_data;
111 struct expected_compositor_message *expected_msg;
112 int actual_msg_count = c->actual_msg_count++;
113 char details_msg[256];
114
115 c->client = wl_resource_get_client(actual_msg->resource);
116
117 if (!c->expected_msg)
118 return;
119
120 ASSERT_LT(actual_msg_count, c->expected_msg_count,
121 "actual count %d exceeds expected count %d\n",
122 actual_msg_count, c->expected_msg_count);
123
124 expected_msg = &c->expected_msg[actual_msg_count];
125
126 snprintf(details_msg, sizeof details_msg,
127 "compositor msg %d of %d actual [%d, '%s', %d, '%s', %d] vs "
128 "expected [%d, '%s', %d, '%s', %d]\n",
129 c->actual_msg_count, c->expected_msg_count, actual_type,
130 wl_resource_get_class(actual_msg->resource),
131 actual_msg->message_opcode, actual_msg->message->name,
132 actual_msg->arguments_count, expected_msg->type,
133 expected_msg->class, expected_msg->opcode,
134 expected_msg->message_name, expected_msg->args_count);
135
136 ASSERT_EQ(expected_msg->type, actual_type, "type mismatch: %s",
137 details_msg);
138 ASSERT_STR_EQ(expected_msg->class,
139 wl_resource_get_class(actual_msg->resource),
140 "class mismatch: %s", details_msg);
141 ASSERT_EQ(expected_msg->opcode, actual_msg->message_opcode,
142 "opcode mismatch: %s", details_msg);
143 ASSERT_STR_EQ(expected_msg->message_name, actual_msg->message->name,
144 "message name mismatch: %s", details_msg);
145 ASSERT_EQ(expected_msg->args_count, actual_msg->arguments_count,
146 "arg count mismatch: %s", details_msg);
147 }
148
149 static void
client_sequence_observer_func(void * user_data,enum wl_client_message_type actual_type,const struct wl_client_observed_message * actual_msg)150 client_sequence_observer_func(
151 void *user_data, enum wl_client_message_type actual_type,
152 const struct wl_client_observed_message *actual_msg)
153 {
154 struct client *c = user_data;
155 struct expected_client_message *expected_msg;
156 int actual_msg_count = c->actual_msg_count++;
157 char details_msg[256];
158
159 if (!c->expected_msg)
160 return;
161
162 ASSERT_LT(actual_msg_count, c->expected_msg_count,
163 "actual count %d exceeds expected count %d\n",
164 actual_msg_count, c->expected_msg_count);
165 expected_msg = &c->expected_msg[actual_msg_count];
166
167 snprintf(details_msg, sizeof details_msg,
168 "client msg %d of %d actual [%d, %d, '%s', %d, '%s', %d] vs "
169 "expected [%d, %d, '%s', %d, '%s', %d]\n",
170 c->actual_msg_count, c->expected_msg_count, actual_type,
171 actual_msg->discarded_reason,
172 wl_proxy_get_class(actual_msg->proxy),
173 actual_msg->message_opcode, actual_msg->message->name,
174 actual_msg->arguments_count, expected_msg->type,
175 expected_msg->discarded_reason, expected_msg->class,
176 expected_msg->opcode, expected_msg->message_name,
177 expected_msg->args_count);
178
179 ASSERT_EQ(expected_msg->type, actual_type, "type mismatch: %s",
180 details_msg);
181 ASSERT_EQ(expected_msg->discarded_reason, actual_msg->discarded_reason,
182 "discarded reason mismatch: %s", details_msg);
183 ASSERT_STR_EQ(expected_msg->class,
184 wl_proxy_get_class(actual_msg->proxy),
185 "class mismatch: %s", details_msg);
186 ASSERT_EQ(expected_msg->opcode, actual_msg->message_opcode,
187 "opcode mismatch: %s", details_msg);
188 ASSERT_STR_EQ(expected_msg->message_name, actual_msg->message->name,
189 "message name mismatch: %s", details_msg);
190 ASSERT_EQ(expected_msg->args_count, actual_msg->arguments_count,
191 "arg count mismatch: %s", details_msg);
192 }
193
194 // A slightly simplified version of get_next_argument() from src/connection.c
195 static const char *
get_next_argument_type(const char * signature,char * type)196 get_next_argument_type(const char *signature, char *type)
197 {
198 for (; *signature; ++signature) {
199 assert(strchr("iufsonah?", *signature) != NULL);
200 switch (*signature) {
201 case 'i':
202 case 'u':
203 case 'f':
204 case 's':
205 case 'o':
206 case 'n':
207 case 'a':
208 case 'h':
209 *type = *signature;
210 return signature + 1;
211 case '?':
212 break;
213 }
214 }
215 *type = 0;
216 return signature;
217 }
218
219 // This duplicates what the internal wl_closure_print function does, and can be
220 // used as a starting point for a client or server that wants to log messages.
221 static void
client_log_to_stderr_demo(void * user_data,enum wl_client_message_type type,const struct wl_client_observed_message * message)222 client_log_to_stderr_demo(void *user_data, enum wl_client_message_type type,
223 const struct wl_client_observed_message *message)
224 {
225 int i;
226 char arg_type;
227 const char *signature = message->message->signature;
228 const union wl_argument *args = message->arguments;
229 struct wl_proxy *arg_proxy;
230 const char *arg_class;
231 struct timespec tp;
232 unsigned long long time;
233 FILE *f;
234 char *buffer;
235 size_t buffer_length;
236
237 f = open_memstream(&buffer, &buffer_length);
238 if (f == NULL)
239 return;
240
241 clock_gettime(CLOCK_REALTIME, &tp);
242 time = (tp.tv_sec * 1000000LL) + (tp.tv_nsec / 1000);
243
244 // Note: server logger will be given message->resource, and should
245 // use wl_resource_get_class and wl_resolurce_get_id.
246 fprintf(f, "[%7llu.%03llu] %s%s%s%s%s@%u.%s(", time / 1000, time % 1000,
247 (message->discarded_reason_str ? "discarded[" : ""),
248 (message->discarded_reason_str ? message->discarded_reason_str
249 : ""),
250 (message->discarded_reason_str ? "] " : ""),
251 (type == WL_CLIENT_MESSAGE_REQUEST) ? " -> " : "",
252 wl_proxy_get_class(message->proxy),
253 wl_proxy_get_id(message->proxy), message->message->name);
254
255 for (i = 0; i < message->arguments_count; i++) {
256 signature = get_next_argument_type(signature, &arg_type);
257 if (i > 0)
258 fprintf(f, ", ");
259
260 switch (arg_type) {
261 case 'u':
262 fprintf(f, "%u", args[i].u);
263 break;
264 case 'i':
265 fprintf(f, "%d", args[i].i);
266 break;
267 case 'f':
268 fprintf(f, "%f", wl_fixed_to_double(args[i].f));
269 break;
270 case 's':
271 if (args[i].s)
272 fprintf(f, "\"%s\"", args[i].s);
273 else
274 fprintf(f, "nil");
275 break;
276 case 'o':
277 if (args[i].o) {
278 // Note: server logger should instead use
279 // wl_resource_from_object, and then
280 // wl_resource_get_class and
281 // wl_resource_get_id.
282 arg_proxy = wl_proxy_from_object(args[i].o);
283 arg_class = wl_proxy_get_class(arg_proxy);
284
285 fprintf(f, "%s@%u",
286 arg_class ? arg_class : "[unknown]",
287 wl_proxy_get_id(arg_proxy));
288 } else {
289 fprintf(f, "nil");
290 }
291 break;
292 case 'n':
293 fprintf(f, "new id %s@",
294 (message->message->types[i])
295 ? message->message->types[i]->name
296 : "[unknown]");
297 if (args[i].n != 0)
298 fprintf(f, "%u", args[i].n);
299 else
300 fprintf(f, "nil");
301 break;
302 case 'a':
303 fprintf(f, "array");
304 break;
305 case 'h':
306 fprintf(f, "fd %d", args[i].h);
307 break;
308 }
309 }
310
311 fprintf(f, ")\n");
312
313 if (fclose(f) == 0) {
314 fprintf(stderr, "%s", buffer);
315 free(buffer);
316 }
317 }
318
319 static void
callback_done(void * data,struct wl_callback * cb,uint32_t time)320 callback_done(void *data, struct wl_callback *cb, uint32_t time)
321 {
322 wl_callback_destroy(cb);
323 }
324
325 static const struct wl_callback_listener callback_listener = {
326 callback_done,
327 };
328
329 static void
logger_setup(struct compositor * compositor,struct client * client)330 logger_setup(struct compositor *compositor, struct client *client)
331 {
332 const char *socket;
333
334 require_xdg_runtime_dir();
335
336 compositor->display = wl_display_create();
337 compositor->loop = wl_display_get_event_loop(compositor->display);
338 socket = wl_display_add_socket_auto(compositor->display);
339
340 compositor->logger = wl_display_add_protocol_logger(
341 compositor->display, compositor_sequence_observer_func,
342 compositor);
343
344 client->display = wl_display_connect(socket);
345 client->sequence_observer = wl_display_create_client_observer(
346 client->display, client_sequence_observer_func, client);
347 client->stderr_logger = wl_display_create_client_observer(
348 client->display, client_log_to_stderr_demo, client);
349 }
350
351 static void
logger_teardown(struct compositor * compositor,struct client * client)352 logger_teardown(struct compositor *compositor, struct client *client)
353 {
354 wl_client_observer_destroy(client->sequence_observer);
355 wl_client_observer_destroy(client->stderr_logger);
356 wl_display_disconnect(client->display);
357
358 wl_client_destroy(compositor->client);
359 wl_protocol_logger_destroy(compositor->logger);
360 wl_display_destroy(compositor->display);
361 }
362
TEST(logger)363 TEST(logger)
364 {
365 test_set_timeout(1);
366
367 struct expected_compositor_message compositor_messages[] = {
368 {
369 .type = WL_PROTOCOL_LOGGER_REQUEST,
370 .class = "wl_display",
371 .opcode = 0,
372 .message_name = "sync",
373 .args_count = 1,
374 },
375 {
376 .type = WL_PROTOCOL_LOGGER_EVENT,
377 .class = "wl_callback",
378 .opcode = 0,
379 .message_name = "done",
380 .args_count = 1,
381 },
382 {
383 .type = WL_PROTOCOL_LOGGER_EVENT,
384 .class = "wl_display",
385 .opcode = 1,
386 .message_name = "delete_id",
387 .args_count = 1,
388 },
389 };
390 struct expected_client_message client_messages[] = {
391 {
392 .type = WL_CLIENT_MESSAGE_REQUEST,
393 .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED,
394 .class = "wl_display",
395 .opcode = 0,
396 .message_name = "sync",
397 .args_count = 1,
398 },
399 {
400 .type = WL_CLIENT_MESSAGE_EVENT,
401 .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED,
402 .class = "wl_display",
403 .opcode = 1,
404 .message_name = "delete_id",
405 .args_count = 1,
406 },
407 {
408 .type = WL_CLIENT_MESSAGE_EVENT,
409 .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED,
410 .class = "wl_callback",
411 .opcode = 0,
412 .message_name = "done",
413 .args_count = 1,
414 },
415 };
416 struct compositor compositor = { 0 };
417 struct client client = { 0 };
418
419 logger_setup(&compositor, &client);
420
421 compositor.expected_msg = &compositor_messages[0];
422 compositor.expected_msg_count = ARRAY_LENGTH(compositor_messages);
423
424 client.expected_msg = &client_messages[0];
425 client.expected_msg_count = ARRAY_LENGTH(client_messages);
426
427 client.cb = wl_display_sync(client.display);
428 wl_callback_add_listener(client.cb, &callback_listener, NULL);
429 wl_display_flush(client.display);
430
431 while (compositor.actual_msg_count < compositor.expected_msg_count) {
432 wl_event_loop_dispatch(compositor.loop, -1);
433 wl_display_flush_clients(compositor.display);
434 }
435
436 while (client.actual_msg_count < client.expected_msg_count) {
437 wl_display_dispatch(client.display);
438 }
439
440 logger_teardown(&compositor, &client);
441 }
442
TEST(client_discards_if_dead_on_dispatch)443 TEST(client_discards_if_dead_on_dispatch)
444 {
445 test_set_timeout(1);
446
447 struct expected_client_message client_messages[] = {
448 {
449 .type = WL_CLIENT_MESSAGE_REQUEST,
450 .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED,
451 .class = "wl_display",
452 .opcode = 0,
453 .message_name = "sync",
454 .args_count = 1,
455 },
456 {
457 .type = WL_CLIENT_MESSAGE_EVENT,
458 .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED,
459 .class = "wl_display",
460 .opcode = 1,
461 .message_name = "delete_id",
462 .args_count = 1,
463 },
464 {
465 .type = WL_CLIENT_MESSAGE_EVENT,
466 .discarded_reason =
467 WL_CLIENT_MESSAGE_DISCARD_DEAD_PROXY_ON_DISPATCH,
468 .class = "wl_callback",
469 .opcode = 0,
470 .message_name = "done",
471 .args_count = 1,
472 },
473 };
474 struct compositor compositor = { 0 };
475 struct client client = { 0 };
476
477 logger_setup(&compositor, &client);
478
479 compositor.expected_msg_count = 3;
480
481 client.expected_msg = &client_messages[0];
482 client.expected_msg_count = ARRAY_LENGTH(client_messages);
483
484 client.cb = wl_display_sync(client.display);
485 wl_callback_add_listener(client.cb, &callback_listener, NULL);
486 wl_display_flush(client.display);
487
488 while (compositor.actual_msg_count < compositor.expected_msg_count) {
489 wl_event_loop_dispatch(compositor.loop, -1);
490 wl_display_flush_clients(compositor.display);
491 }
492
493 wl_display_prepare_read(client.display);
494 wl_display_read_events(client.display);
495
496 // To get a WL_CLIENT_MESSAGE_DISCARD_DEAD_PROXY_ON_DISPATCH, we
497 // destroy the callback after reading client events, but before
498 // dispatching them.
499 wl_callback_destroy(client.cb);
500
501 while (client.actual_msg_count < client.expected_msg_count) {
502 wl_display_dispatch(client.display);
503 }
504
505 logger_teardown(&compositor, &client);
506 }
507
TEST(client_discards_if_no_listener_on_dispatch)508 TEST(client_discards_if_no_listener_on_dispatch)
509 {
510 test_set_timeout(1);
511
512 struct expected_client_message client_messages[] = {
513 {
514 .type = WL_CLIENT_MESSAGE_REQUEST,
515 .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED,
516 .class = "wl_display",
517 .opcode = 0,
518 .message_name = "sync",
519 .args_count = 1,
520 },
521 {
522 .type = WL_CLIENT_MESSAGE_EVENT,
523 .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED,
524 .class = "wl_display",
525 .opcode = 1,
526 .message_name = "delete_id",
527 .args_count = 1,
528 },
529 {
530 .type = WL_CLIENT_MESSAGE_EVENT,
531 .discarded_reason =
532 WL_CLIENT_MESSAGE_DISCARD_NO_LISTENER_ON_DISPATCH,
533 .class = "wl_callback",
534 .opcode = 0,
535 .message_name = "done",
536 .args_count = 1,
537 },
538 };
539 struct compositor compositor = { 0 };
540 struct client client = { 0 };
541
542 logger_setup(&compositor, &client);
543
544 compositor.expected_msg_count = 3;
545
546 client.expected_msg = &client_messages[0];
547 client.expected_msg_count = ARRAY_LENGTH(client_messages);
548
549 client.cb = wl_display_sync(client.display);
550 wl_display_flush(client.display);
551
552 while (compositor.actual_msg_count < compositor.expected_msg_count) {
553 wl_event_loop_dispatch(compositor.loop, -1);
554 wl_display_flush_clients(compositor.display);
555 }
556
557 while (client.actual_msg_count < client.expected_msg_count) {
558 wl_display_dispatch(client.display);
559 }
560
561 wl_callback_destroy(client.cb);
562
563 logger_teardown(&compositor, &client);
564 }
565
TEST(client_discards_if_invalid_id_on_demarshal)566 TEST(client_discards_if_invalid_id_on_demarshal)
567 {
568 test_set_timeout(1);
569
570 struct expected_client_message client_messages[] = {
571 {
572 .type = WL_CLIENT_MESSAGE_REQUEST,
573 .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED,
574 .class = "wl_display",
575 .opcode = 0,
576 .message_name = "sync",
577 .args_count = 1,
578 },
579 {
580 .type = WL_CLIENT_MESSAGE_EVENT,
581 .discarded_reason =
582 WL_CLIENT_MESSAGE_DISCARD_UNKNOWN_ID_ON_DEMARSHAL,
583 .class = "[unknown]",
584 .opcode = 0,
585 .message_name = "[event 0, 0 fds, 12 bytes]",
586 .args_count = 0,
587 },
588 {
589 .type = WL_CLIENT_MESSAGE_EVENT,
590 .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED,
591 .class = "wl_display",
592 .opcode = 1,
593 .message_name = "delete_id",
594 .args_count = 1,
595 },
596 };
597 struct compositor compositor = { 0 };
598 struct client client = { 0 };
599
600 logger_setup(&compositor, &client);
601
602 compositor.expected_msg_count = 3;
603
604 client.expected_msg = &client_messages[0];
605 client.expected_msg_count = ARRAY_LENGTH(client_messages);
606
607 client.cb = wl_display_sync(client.display);
608 wl_display_flush(client.display);
609
610 while (compositor.actual_msg_count < compositor.expected_msg_count) {
611 wl_event_loop_dispatch(compositor.loop, -1);
612 wl_display_flush_clients(compositor.display);
613 }
614
615 // To get a WL_CLIENT_MESSAGE_DISCARD_UNKNOWN_ID_ON_DEMARSHAL, we
616 // destroy the callback before reading and dispatching client events.
617 wl_callback_destroy(client.cb);
618
619 while (client.actual_msg_count < client.expected_msg_count) {
620 wl_display_dispatch(client.display);
621 }
622
623 logger_teardown(&compositor, &client);
624 }
625
626 static const struct wl_keyboard_interface keyboard_interface = { 0 };
627
628 static void
seat_get_pointer(struct wl_client * client,struct wl_resource * resource,uint32_t id)629 seat_get_pointer(struct wl_client *client, struct wl_resource *resource,
630 uint32_t id)
631 {
632 assert(false && "Not expected to be called by client.");
633 }
634
635 static void
seat_get_keyboard(struct wl_client * client,struct wl_resource * resource,uint32_t id)636 seat_get_keyboard(struct wl_client *client, struct wl_resource *resource,
637 uint32_t id)
638 {
639 struct wl_resource *keyboard_res;
640
641 keyboard_res =
642 wl_resource_create(client, &wl_keyboard_interface,
643 wl_resource_get_version(resource), id);
644 wl_resource_set_implementation(keyboard_res, &keyboard_interface, NULL,
645 NULL);
646
647 wl_keyboard_send_key(keyboard_res, 0, 0, 0, 0);
648 }
649
650 static void
seat_get_touch(struct wl_client * client,struct wl_resource * resource,uint32_t id)651 seat_get_touch(struct wl_client *client, struct wl_resource *resource,
652 uint32_t id)
653 {
654 assert(false && "Not expected to be called by client.");
655 }
656
657 static void
seat_release(struct wl_client * client,struct wl_resource * resource)658 seat_release(struct wl_client *client, struct wl_resource *resource)
659 {
660 wl_resource_destroy(resource);
661 }
662
663 static const struct wl_seat_interface seat_interface = {
664 &seat_get_pointer,
665 &seat_get_keyboard,
666 &seat_get_touch,
667 &seat_release,
668 };
669
670 static void
bind_seat(struct wl_client * client,void * data,uint32_t vers,uint32_t id)671 bind_seat(struct wl_client *client, void *data, uint32_t vers, uint32_t id)
672 {
673 struct wl_resource *seat_res;
674
675 seat_res = wl_resource_create(client, &wl_seat_interface, vers, id);
676 wl_resource_set_implementation(seat_res, &seat_interface, NULL, NULL);
677 }
678
679 static void
registry_seat_listener_handle_global(void * data,struct wl_registry * registry,uint32_t id,const char * intf,uint32_t ver)680 registry_seat_listener_handle_global(void *data, struct wl_registry *registry,
681 uint32_t id, const char *intf,
682 uint32_t ver)
683 {
684 uint32_t *seat_id_ptr = data;
685
686 if (strcmp(intf, wl_seat_interface.name) == 0) {
687 *seat_id_ptr = id;
688 }
689 }
690
691 static const struct wl_registry_listener registry_seat_listener = {
692 registry_seat_listener_handle_global, NULL
693 };
694
TEST(client_discards_if_zombie_on_demarshal)695 TEST(client_discards_if_zombie_on_demarshal)
696 {
697 test_set_timeout(1);
698
699 struct expected_client_message client_messages[] = {
700 {
701 .type = WL_CLIENT_MESSAGE_REQUEST,
702 .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED,
703 .class = "wl_display",
704 .opcode = 1,
705 .message_name = "get_registry",
706 .args_count = 1,
707 },
708 {
709 .type = WL_CLIENT_MESSAGE_EVENT,
710 .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED,
711 .class = "wl_registry",
712 .opcode = 0,
713 .message_name = "global",
714 .args_count = 3,
715 },
716 {
717 .type = WL_CLIENT_MESSAGE_REQUEST,
718 .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED,
719 .class = "wl_registry",
720 .opcode = 0,
721 .message_name = "bind",
722 .args_count = 4,
723 },
724 {
725 .type = WL_CLIENT_MESSAGE_REQUEST,
726 .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED,
727 .class = "wl_seat",
728 .opcode = 1,
729 .message_name = "get_keyboard",
730 .args_count = 1,
731 },
732 {
733 .type = WL_CLIENT_MESSAGE_REQUEST,
734 .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED,
735 .class = "wl_keyboard",
736 .opcode = 0,
737 .message_name = "release",
738 .args_count = 0,
739 },
740 {
741 .type = WL_CLIENT_MESSAGE_REQUEST,
742 .discarded_reason = WL_CLIENT_MESSAGE_NOT_DISCARDED,
743 .class = "wl_seat",
744 .opcode = 3,
745 .message_name = "release",
746 .args_count = 0,
747 },
748 {
749 .type = WL_CLIENT_MESSAGE_EVENT,
750 .discarded_reason =
751 WL_CLIENT_MESSAGE_DISCARD_UNKNOWN_ID_ON_DEMARSHAL,
752 .class = "[zombie]",
753 .opcode = 3,
754 .message_name = "[event 3, 0 fds, 24 bytes]",
755 .args_count = 0,
756 },
757 };
758
759 struct compositor compositor = { 0 };
760 struct client client = { 0 };
761 struct wl_global *g_keyboard;
762 struct wl_registry *registry;
763 struct wl_seat *seat;
764 struct wl_keyboard *keyboard;
765 int32_t seat_id;
766
767 logger_setup(&compositor, &client);
768
769 client.expected_msg = &client_messages[0];
770 client.expected_msg_count = ARRAY_LENGTH(client_messages);
771
772 g_keyboard = wl_global_create(compositor.display, &wl_seat_interface,
773 5, &compositor.display, bind_seat);
774
775 registry = wl_display_get_registry(client.display);
776 wl_registry_add_listener(registry, ®istry_seat_listener, &seat_id);
777 wl_display_flush(client.display);
778
779 compositor.actual_msg_count = 0;
780 compositor.expected_msg_count = 2;
781
782 while (compositor.actual_msg_count < compositor.expected_msg_count) {
783 wl_event_loop_dispatch(compositor.loop, -1);
784 wl_display_flush_clients(compositor.display);
785 }
786
787 wl_display_dispatch(client.display);
788
789 seat = wl_registry_bind(registry, seat_id, &wl_seat_interface, 5);
790 keyboard = wl_seat_get_keyboard(seat);
791 wl_display_flush(client.display);
792
793 compositor.actual_msg_count = 0;
794 compositor.expected_msg_count = 3;
795
796 while (compositor.actual_msg_count < compositor.expected_msg_count) {
797 wl_event_loop_dispatch(compositor.loop, -1);
798 wl_display_flush_clients(compositor.display);
799 }
800
801 wl_keyboard_release(keyboard);
802 wl_seat_release(seat);
803
804 wl_display_dispatch(client.display);
805
806 wl_registry_destroy(registry);
807
808 wl_global_destroy(g_keyboard);
809
810 logger_teardown(&compositor, &client);
811 }
812