1 /*
2 * Copyright © 2020 Manuel Stoeckl
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 "common.h"
27 #include "main.h"
28 #include "parsing.h"
29 #include "util.h"
30
31 #include "protocol_functions.h"
32
33 #include <errno.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/mman.h>
38 #include <unistd.h>
39
40 struct msgtransfer {
41 struct test_state *src;
42 struct test_state *dst;
43 };
44
45 /* Override the libc clock_gettime, so we can test presentation-time
46 * protocol. Note: the video drivers sometimes call this function. */
clock_gettime(clockid_t clock_id,struct timespec * tp)47 int clock_gettime(clockid_t clock_id, struct timespec *tp)
48 {
49 /* Assume every call costs 1ns */
50 time_value += 1;
51
52 if (clock_id == CLOCK_REALTIME) {
53 tp->tv_sec = (int64_t)(time_value / 1000000000uLL);
54 tp->tv_nsec = (int64_t)(time_value % 1000000000uLL);
55 } else {
56 tp->tv_sec = (int64_t)((time_value + local_time_offset) /
57 1000000000uLL);
58 tp->tv_nsec = (int64_t)((time_value + local_time_offset) %
59 1000000000uLL);
60 }
61 return 0;
62 }
63
print_pass(bool pass)64 static void print_pass(bool pass)
65 {
66 fprintf(stdout, "%s\n", pass ? "PASS" : "FAIL");
67 }
68
make_filled_pattern(size_t size,uint32_t contents)69 static char *make_filled_pattern(size_t size, uint32_t contents)
70 {
71 uint32_t *mem = calloc(size, 1);
72 for (size_t i = 0; i < size / 4; i++) {
73 mem[i] = contents;
74 }
75 return (char *)mem;
76 }
77
make_filled_file(size_t size,const char * contents)78 static int make_filled_file(size_t size, const char *contents)
79 {
80 int fd = create_anon_file();
81 ftruncate(fd, (off_t)size);
82
83 uint32_t *mem = (uint32_t *)mmap(
84 NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
85 memcpy(mem, contents, size);
86 munmap(mem, size);
87 return fd;
88 }
89
check_file_contents(int fd,size_t size,const char * contents)90 static bool check_file_contents(int fd, size_t size, const char *contents)
91 {
92 if (fd == -1) {
93 return false;
94 }
95
96 off_t fsize = lseek(fd, 0, SEEK_END);
97 if (fsize != (off_t)size) {
98 wp_error("fd size mismatch: %zu %zu\n", fsize, size);
99 return -1;
100 }
101
102 uint32_t *mem = (uint32_t *)mmap(
103 NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
104 if (mem == MAP_FAILED) {
105 wp_error("Failed to map file");
106 return -1;
107 }
108 bool match = memcmp(mem, contents, size) == 0;
109 munmap(mem, size);
110 return match;
111 }
get_only_fd_from_msg(const struct test_state * s)112 static int get_only_fd_from_msg(const struct test_state *s)
113 {
114
115 if (s->rcvd && s->rcvd[s->nrcvd - 1].nfds == 1) {
116 return s->rcvd[s->nrcvd - 1].fds[0];
117 } else {
118 return -1;
119 }
120 }
get_fd_from_nth_to_last_msg(const struct test_state * s,int nth)121 static int get_fd_from_nth_to_last_msg(const struct test_state *s, int nth)
122 {
123 if (!s->rcvd || s->nrcvd < nth) {
124 return -1;
125 }
126 const struct msg *m = &s->rcvd[s->nrcvd - nth];
127 if (m->nfds != 1) {
128 return -1;
129 }
130 return m->fds[0];
131 }
132
msg_send_handler(struct transfer_states * ts,struct test_state * src,struct test_state * dst)133 static void msg_send_handler(struct transfer_states *ts, struct test_state *src,
134 struct test_state *dst)
135 {
136 struct msg m;
137 m.data = ts->msg_space;
138 m.fds = ts->fd_space;
139 m.len = (int)ts->msg_size;
140 m.nfds = (int)ts->fd_size;
141 for (int i = 0; i < m.nfds; i++) {
142 m.fds[i] = dup(m.fds[i]);
143 if (m.fds[i] == -1) {
144 wp_error("Invalid fd provided");
145 }
146 }
147 send_protocol_msg(src, dst, m);
148 memset(ts->msg_space, 0, sizeof(ts->msg_space));
149 memset(ts->fd_space, 0, sizeof(ts->fd_space));
150 }
setup_tstate(struct transfer_states * ts)151 static int setup_tstate(struct transfer_states *ts)
152 {
153 memset(ts, 0, sizeof(*ts));
154 ts->send = msg_send_handler;
155 ts->comp = calloc(1, sizeof(struct test_state));
156 ts->app = calloc(1, sizeof(struct test_state));
157 if (!ts->comp || !ts->app) {
158 goto fail_alloc;
159 }
160 if (setup_state(ts->comp, true, true) == -1) {
161 goto fail_comp_setup;
162 }
163 if (setup_state(ts->app, false, true) == -1) {
164 goto fail_app_setup;
165 }
166 return 0;
167
168 fail_app_setup:
169 cleanup_state(ts->app);
170 fail_comp_setup:
171 cleanup_state(ts->comp);
172 fail_alloc:
173 free(ts->comp);
174 free(ts->app);
175 return -1;
176 }
cleanup_tstate(struct transfer_states * ts)177 static void cleanup_tstate(struct transfer_states *ts)
178 {
179 cleanup_state(ts->comp);
180 cleanup_state(ts->app);
181 free(ts->comp);
182 free(ts->app);
183 }
184
test_fixed_shm_buffer_copy()185 static bool test_fixed_shm_buffer_copy()
186 {
187 fprintf(stdout, "\n shm_pool+buffer test\n");
188
189 struct transfer_states T;
190 if (setup_tstate(&T) == -1) {
191 wp_error("Test setup failed");
192 return true;
193 }
194 bool pass = true;
195
196 char *testpat = make_filled_pattern(16384, 0xFEDCBA98);
197 int fd = make_filled_file(16384, testpat);
198 int ret_fd = -1;
199
200 struct wp_objid display = {0x1}, registry = {0x2}, shm = {0x3},
201 compositor = {0x4}, pool = {0x5}, buffer = {0x6},
202 surface = {0x7};
203
204 send_wl_display_req_get_registry(&T, display, registry);
205 send_wl_registry_evt_global(&T, registry, 1, "wl_shm", 1);
206 send_wl_registry_evt_global(&T, registry, 2, "wl_compositor", 1);
207 send_wl_registry_req_bind(&T, registry, 1, "wl_shm", 1, shm);
208 send_wl_registry_req_bind(
209 &T, registry, 2, "wl_compositor", 1, compositor);
210 send_wl_shm_req_create_pool(&T, shm, pool, fd, 16384);
211 ret_fd = get_only_fd_from_msg(T.comp);
212 send_wl_shm_pool_req_create_buffer(
213 &T, pool, buffer, 0, 64, 64, 256, 0x30334258);
214 send_wl_compositor_req_create_surface(&T, compositor, surface);
215 send_wl_surface_req_attach(&T, surface, buffer, 0, 0);
216 send_wl_surface_req_damage(&T, surface, 0, 0, 64, 64);
217 send_wl_surface_req_commit(&T, surface);
218
219 /* confirm receipt of fd with the correct contents; if not,
220 * reject */
221 if (ret_fd == -1) {
222 wp_error("Fd not passed through");
223 pass = false;
224 goto end;
225 }
226 pass = check_file_contents(ret_fd, 16384, testpat);
227 if (!pass) {
228 wp_error("Failed to transfer file");
229 }
230 end:
231 free(testpat);
232 checked_close(fd);
233 cleanup_tstate(&T);
234
235 print_pass(pass);
236 return pass;
237 }
238
test_fixed_shm_screencopy_copy()239 static bool test_fixed_shm_screencopy_copy()
240 {
241 fprintf(stdout, "\n screencopy test\n");
242
243 struct transfer_states T;
244 if (setup_tstate(&T) == -1) {
245 wp_error("Test setup failed");
246 return true;
247 }
248 bool pass = true;
249
250 char *testpat_orig = make_filled_pattern(16384, 0xFEDCBA98);
251 char *testpat_screen = make_filled_pattern(16384, 0x77557755);
252 int fd = make_filled_file(16384, testpat_orig);
253 int ret_fd = -1;
254
255 struct wp_objid display = {0x1}, registry = {0x2}, shm = {0x3},
256 output = {0x4}, pool = {0x5}, buffer = {0x6},
257 frame = {0x7}, screencopy = {0x8};
258
259 send_wl_display_req_get_registry(&T, display, registry);
260 send_wl_registry_evt_global(&T, registry, 1, "wl_shm", 1);
261 send_wl_registry_evt_global(&T, registry, 2, "wl_output", 1);
262 send_wl_registry_evt_global(
263 &T, registry, 3, "zwlr_screencopy_manager_v1", 1);
264 send_wl_registry_req_bind(&T, registry, 1, "wl_shm", 1, shm);
265 send_wl_registry_req_bind(&T, registry, 2, "wl_output", 1, output);
266 send_wl_registry_req_bind(&T, registry, 3, "zwlr_screencopy_manager_v1",
267 1, screencopy);
268 send_wl_shm_req_create_pool(&T, shm, pool, fd, 16384);
269 ret_fd = get_only_fd_from_msg(T.comp);
270 if (ret_fd == -1) {
271 wp_error("Fd not passed through");
272 pass = false;
273 goto end;
274 }
275 send_zwlr_screencopy_manager_v1_req_capture_output(
276 &T, screencopy, frame, 0, output);
277 send_zwlr_screencopy_frame_v1_evt_buffer(&T, frame, 0, 64, 64, 16384);
278 send_wl_shm_pool_req_create_buffer(
279 &T, pool, buffer, 0, 64, 64, 256, 0x30334258);
280 send_zwlr_screencopy_frame_v1_req_copy(&T, frame, buffer);
281
282 uint32_t *mem = (uint32_t *)mmap(NULL, 16384, PROT_READ | PROT_WRITE,
283 MAP_SHARED, ret_fd, 0);
284 memcpy(mem, testpat_screen, 16384);
285 munmap(mem, 16384);
286
287 send_zwlr_screencopy_frame_v1_evt_flags(&T, frame, 0);
288 send_zwlr_screencopy_frame_v1_evt_ready(&T, frame, 0, 12345, 600000000);
289
290 /* confirm receipt of fd with the correct contents; if not,
291 * reject */
292 if (ret_fd == -1) {
293 wp_error("Fd not passed through");
294 pass = false;
295 goto end;
296 }
297 pass = check_file_contents(fd, 16384, testpat_screen);
298 if (!pass) {
299 wp_error("Failed to transfer file");
300 }
301 end:
302 free(testpat_screen);
303 free(testpat_orig);
304 checked_close(fd);
305 cleanup_tstate(&T);
306
307 print_pass(pass);
308 return pass;
309 }
310
test_fixed_keymap_copy()311 static bool test_fixed_keymap_copy()
312 {
313 fprintf(stdout, "\n Keymap test\n");
314 struct transfer_states T;
315 if (setup_tstate(&T) == -1) {
316 wp_error("Test setup failed");
317 return true;
318 }
319 bool pass = true;
320
321 char *testpat = make_filled_pattern(16384, 0xFEDCBA98);
322 int fd = make_filled_file(16384, testpat);
323 int ret_fd = -1;
324
325 struct wp_objid display = {0x1}, registry = {0x2}, seat = {0x3},
326 keyboard = {0x4};
327
328 send_wl_display_req_get_registry(&T, display, registry);
329 send_wl_registry_evt_global(&T, registry, 1, "wl_seat", 7);
330 send_wl_registry_req_bind(&T, registry, 1, "wl_seat", 7, seat);
331 send_wl_seat_evt_capabilities(&T, seat, 3);
332 send_wl_seat_req_get_keyboard(&T, seat, keyboard);
333 send_wl_keyboard_evt_keymap(&T, keyboard, 1, fd, 16384);
334 ret_fd = get_only_fd_from_msg(T.app);
335
336 /* confirm receipt of fd with the correct contents; if not,
337 * reject */
338 if (ret_fd == -1) {
339 wp_error("Fd not passed through");
340 pass = false;
341 goto end;
342 }
343 pass = check_file_contents(ret_fd, 16384, testpat);
344 if (!pass) {
345 wp_error("Failed to transfer file");
346 }
347
348 end:
349 free(testpat);
350 checked_close(fd);
351 cleanup_tstate(&T);
352
353 print_pass(pass);
354 return pass;
355 }
356
357 #define DMABUF_FORMAT 875713112
358
create_dmabuf(void)359 static int create_dmabuf(void)
360 {
361 struct render_data rd;
362 memset(&rd, 0, sizeof(rd));
363 rd.drm_fd = -1;
364 rd.av_disabled = true;
365
366 const size_t test_width = 256;
367 const size_t test_height = 384;
368 const size_t test_cpp = 4;
369 const size_t test_size = test_width * test_height * test_cpp;
370 const struct dmabuf_slice_data slice_data = {
371 .width = (uint32_t)test_width,
372 .height = (uint32_t)test_height,
373 .format = DMABUF_FORMAT,
374 .num_planes = 1,
375 .modifier = 0,
376 .offsets = {0, 0, 0, 0},
377 .strides = {(uint32_t)(test_width * test_cpp), 0, 0, 0},
378 .using_planes = {true, false, false, false},
379 };
380
381 int dmafd = -1;
382 if (init_render_data(&rd) == -1) {
383 return -1;
384 }
385 struct gbm_bo *bo = make_dmabuf(&rd, test_size, &slice_data);
386 if (!bo) {
387 goto end;
388 }
389
390 void *map_handle = NULL;
391 void *data = map_dmabuf(bo, true, &map_handle, NULL, NULL);
392 if (!data) {
393 destroy_dmabuf(bo);
394 goto end;
395 }
396 /* TODO: the best test pattern is a colored gradient, so we can
397 * check whether the copy flips things or not */
398 memset(data, 0x80, test_size);
399 unmap_dmabuf(bo, map_handle);
400
401 dmafd = export_dmabuf(bo);
402 if (dmafd == -1) {
403 goto end;
404 }
405
406 end:
407 destroy_dmabuf(bo);
408 cleanup_render_data(&rd);
409
410 return dmafd;
411 }
412
413 enum dmabuf_copy_type {
414 COPY_LINUX_DMABUF,
415 COPY_LINUX_DMABUF_INDIR,
416 COPY_DRM_PRIME,
417 COPY_WLR_EXPORT,
418 };
419
test_fixed_dmabuf_copy(enum dmabuf_copy_type type)420 static bool test_fixed_dmabuf_copy(enum dmabuf_copy_type type)
421 {
422 const char *const types[] = {"linux-dmabuf", "linux-dmabuf-indir",
423 "drm-prime", "wlr-export"};
424 fprintf(stdout, "\n DMABUF test, %s\n", types[(int)type]);
425
426 int dmabufd = create_dmabuf();
427 const int width = 256, height = 384;
428 if (dmabufd == -1) {
429 return true;
430 }
431 struct transfer_states T;
432 if (setup_tstate(&T) == -1) {
433 wp_error("Test setup failed");
434 return true;
435 }
436 bool pass = true;
437 int ret_fd = -1;
438
439 switch (type) {
440 case COPY_LINUX_DMABUF: {
441 struct wp_objid display = {0x1}, registry = {0x2},
442 linux_dmabuf = {0x3}, compositor = {0x4},
443 params = {0x5}, buffer = {0x6}, surface = {0x7};
444
445 send_wl_display_req_get_registry(&T, display, registry);
446 send_wl_registry_evt_global(
447 &T, registry, 1, "zwp_linux_dmabuf_v1", 1);
448 send_wl_registry_evt_global(
449 &T, registry, 2, "wl_compositor", 1);
450 send_wl_registry_req_bind(&T, registry, 1,
451 "zwp_linux_dmabuf_v1", 1, linux_dmabuf);
452 send_wl_registry_req_bind(&T, registry, 12, "wl_compositor", 1,
453 compositor);
454 send_zwp_linux_dmabuf_v1_evt_modifier(
455 &T, linux_dmabuf, DMABUF_FORMAT, 0, 0);
456 send_zwp_linux_dmabuf_v1_req_create_params(
457 &T, linux_dmabuf, params);
458 send_zwp_linux_buffer_params_v1_req_add(
459 &T, params, dmabufd, 0, 0, 256 * 4, 0, 0);
460 send_zwp_linux_buffer_params_v1_req_create_immed(
461 &T, params, buffer, 256, 384, DMABUF_FORMAT, 0);
462 /* this message + previous, after reordering, are treated as one
463 * bundle; if that is fixed, this will break, and 1 should
464 * become 2 */
465 ret_fd = get_fd_from_nth_to_last_msg(T.comp, 1);
466 send_zwp_linux_buffer_params_v1_req_destroy(&T, params);
467 send_wl_compositor_req_create_surface(&T, compositor, surface);
468 send_wl_surface_req_attach(&T, surface, buffer, 0, 0);
469 send_wl_surface_req_damage(&T, surface, 0, 0, 64, 64);
470 send_wl_surface_req_commit(&T, surface);
471 } break;
472 case COPY_LINUX_DMABUF_INDIR: {
473 struct wp_objid display = {0x1}, registry = {0x2},
474 linux_dmabuf = {0x3}, compositor = {0x4},
475 params = {0x5}, buffer = {0x6}, surface = {0x7};
476
477 send_wl_display_req_get_registry(&T, display, registry);
478 send_wl_registry_evt_global(
479 &T, registry, 1, "zwp_linux_dmabuf_v1", 1);
480 send_wl_registry_evt_global(
481 &T, registry, 2, "wl_compositor", 1);
482 send_wl_registry_req_bind(&T, registry, 1,
483 "zwp_linux_dmabuf_v1", 1, linux_dmabuf);
484 send_wl_registry_req_bind(&T, registry, 12, "wl_compositor", 1,
485 compositor);
486 send_zwp_linux_dmabuf_v1_evt_modifier(
487 &T, linux_dmabuf, DMABUF_FORMAT, 0, 0);
488 send_zwp_linux_dmabuf_v1_req_create_params(
489 &T, linux_dmabuf, params);
490 send_zwp_linux_buffer_params_v1_req_add(
491 &T, params, dmabufd, 0, 0, 256 * 4, 0, 0);
492 send_zwp_linux_buffer_params_v1_req_create(
493 &T, params, 256, 384, DMABUF_FORMAT, 0);
494 /* this message + previous, after reordering, are treated as one
495 * bundle; if that is fixed, this will break, and 1 should
496 * become 2 */
497 ret_fd = get_fd_from_nth_to_last_msg(T.comp, 1);
498 send_zwp_linux_buffer_params_v1_evt_created(&T, params, buffer);
499 send_zwp_linux_buffer_params_v1_req_destroy(&T, params);
500 send_wl_compositor_req_create_surface(&T, compositor, surface);
501 send_wl_surface_req_attach(&T, surface, buffer, 0, 0);
502 send_wl_surface_req_damage(&T, surface, 0, 0, 64, 64);
503 send_wl_surface_req_commit(&T, surface);
504 } break;
505 case COPY_DRM_PRIME: {
506 struct wp_objid display = {0x1}, registry = {0x2},
507 wl_drm = {0x3}, compositor = {0x4},
508 buffer = {0x5}, surface = {0x6};
509
510 send_wl_display_req_get_registry(&T, display, registry);
511 send_wl_registry_evt_global(&T, registry, 1, "wl_drm", 1);
512 send_wl_registry_evt_global(
513 &T, registry, 2, "wl_compositor", 1);
514 send_wl_registry_req_bind(&T, registry, 1, "wl_drm", 1, wl_drm);
515 send_wl_registry_req_bind(&T, registry, 12, "wl_compositor", 1,
516 compositor);
517
518 send_wl_drm_evt_device(&T, wl_drm, "/dev/dri/renderD128");
519 send_wl_drm_evt_format(&T, wl_drm, DMABUF_FORMAT);
520 send_wl_drm_evt_capabilities(&T, wl_drm, 1);
521 send_wl_drm_req_create_prime_buffer(&T, wl_drm, buffer, dmabufd,
522 width, height, DMABUF_FORMAT, 0, width * 4, 0,
523 0, 0, 0);
524
525 ret_fd = get_fd_from_nth_to_last_msg(T.comp, 1);
526 send_wl_compositor_req_create_surface(&T, compositor, surface);
527 send_wl_surface_req_attach(&T, surface, buffer, 0, 0);
528 send_wl_surface_req_damage(&T, surface, 0, 0, 64, 64);
529 send_wl_surface_req_commit(&T, surface);
530 } break;
531
532 case COPY_WLR_EXPORT: {
533 /* note: here the compositor creates and sends fd to client */
534
535 struct wp_objid display = {0x1}, registry = {0x2},
536 export_manager = {0x3}, output = {0x4},
537 dmabuf_frame = {0x5};
538
539 send_wl_display_req_get_registry(&T, display, registry);
540 send_wl_registry_evt_global(&T, registry, 1,
541 "zwlr_export_dmabuf_manager_v1", 1);
542 send_wl_registry_evt_global(&T, registry, 2, "wl_output", 1);
543 send_wl_registry_req_bind(&T, registry, 1,
544 "zwlr_export_dmabuf_manager_v1", 1,
545 export_manager);
546 send_wl_registry_req_bind(
547 &T, registry, 12, "wl_output", 1, output);
548
549 send_zwlr_export_dmabuf_manager_v1_req_capture_output(
550 &T, export_manager, dmabuf_frame, 1, output);
551 send_zwlr_export_dmabuf_frame_v1_evt_frame(&T, dmabuf_frame,
552 width, height, 0, 0, 0, 1, DMABUF_FORMAT, 0, 0,
553 1);
554 send_zwlr_export_dmabuf_frame_v1_evt_object(&T, dmabuf_frame, 0,
555 dmabufd, width * height * 4, 0, width * 4, 0);
556 ret_fd = get_only_fd_from_msg(T.app);
557 send_zwlr_export_dmabuf_frame_v1_evt_ready(
558 &T, dmabuf_frame, 555555, 555555555, 333333333);
559 } break;
560 }
561
562 if (ret_fd == -1) {
563 wp_error("Fd not passed through");
564 pass = false;
565 goto end;
566 }
567 // TODO: verify that the FD contents are correct
568
569 end:
570 checked_close(dmabufd);
571 /* todo: the drm_fd may be dup'd by libgbm but not freed */
572 cleanup_tstate(&T);
573
574 print_pass(pass);
575 return pass;
576 }
577
578 enum data_device_type {
579 DDT_WAYLAND,
580 DDT_GTK_PRIMARY,
581 DDT_PRIMARY,
582 DDT_WLR,
583 };
584 static const char *const data_device_type_strs[] = {"wayland main",
585 "gtk primary selection", "primary selection",
586 "wlroots data control"};
587
588 /* Confirm that wl_data_offer.receive creates a pipe matching the input */
test_data_offer(enum data_device_type type)589 static bool test_data_offer(enum data_device_type type)
590 {
591 fprintf(stdout, "\n Data offer test: %s\n",
592 data_device_type_strs[type]);
593 struct transfer_states T;
594 if (setup_tstate(&T) == -1) {
595 wp_error("Test setup failed");
596 return true;
597 }
598 bool pass = true;
599
600 int src_pipe[2];
601 pipe(src_pipe);
602 int ret_fd = -1;
603
604 struct wp_objid display = {0x1}, registry = {0x2}, ddman = {0x3},
605 seat = {0x4}, ddev = {0x5}, offer = {0xff000001};
606
607 send_wl_display_req_get_registry(&T, display, registry);
608 send_wl_registry_evt_global(&T, registry, 1, "wl_seat", 7);
609 send_wl_registry_req_bind(&T, registry, 1, "wl_seat", 7, seat);
610 switch (type) {
611 case DDT_WAYLAND:
612 send_wl_registry_evt_global(
613 &T, registry, 2, "wl_data_device_manager", 3);
614 send_wl_registry_req_bind(&T, registry, 2,
615 "wl_data_device_manager", 3, ddman);
616 send_wl_data_device_manager_req_get_data_device(
617 &T, ddman, ddev, seat);
618 send_wl_data_device_evt_data_offer(&T, ddev, offer);
619 send_wl_data_offer_evt_offer(
620 &T, offer, "text/plain;charset=utf-8");
621 send_wl_data_device_evt_selection(&T, ddev, offer);
622 send_wl_data_offer_req_receive(&T, offer,
623 "text/plain;charset=utf-8", src_pipe[1]);
624 break;
625 case DDT_GTK_PRIMARY:
626 send_wl_registry_evt_global(&T, registry, 2,
627 "gtk_primary_selection_device_manager", 1);
628 send_wl_registry_req_bind(&T, registry, 2,
629 "gtk_primary_selection_device_manager", 1,
630 ddman);
631 send_gtk_primary_selection_device_manager_req_get_device(
632 &T, ddman, ddev, seat);
633 send_gtk_primary_selection_device_evt_data_offer(
634 &T, ddev, offer);
635 send_gtk_primary_selection_offer_evt_offer(
636 &T, offer, "text/plain;charset=utf-8");
637 send_gtk_primary_selection_device_evt_selection(
638 &T, ddev, offer);
639 send_gtk_primary_selection_offer_req_receive(&T, offer,
640 "text/plain;charset=utf-8", src_pipe[1]);
641 break;
642 case DDT_PRIMARY:
643 send_wl_registry_evt_global(&T, registry, 2,
644 "zwp_primary_selection_device_manager_v1", 1);
645 send_wl_registry_req_bind(&T, registry, 2,
646 "zwp_primary_selection_device_manager_v1", 1,
647 ddman);
648 send_zwp_primary_selection_device_manager_v1_req_get_device(
649 &T, ddman, ddev, seat);
650 send_zwp_primary_selection_device_v1_evt_data_offer(
651 &T, ddev, offer);
652 send_zwp_primary_selection_offer_v1_evt_offer(
653 &T, offer, "text/plain;charset=utf-8");
654 send_zwp_primary_selection_device_v1_evt_selection(
655 &T, ddev, offer);
656 send_zwp_primary_selection_offer_v1_req_receive(&T, offer,
657 "text/plain;charset=utf-8", src_pipe[1]);
658 break;
659 case DDT_WLR:
660 send_wl_registry_evt_global(&T, registry, 2,
661 "zwlr_data_control_manager_v1", 1);
662 send_wl_registry_req_bind(&T, registry, 2,
663 "zwlr_data_control_manager_v1", 1, ddman);
664 send_zwlr_data_control_manager_v1_req_get_data_device(
665 &T, ddman, ddev, seat);
666 send_zwlr_data_control_device_v1_evt_data_offer(
667 &T, ddev, offer);
668 send_zwlr_data_control_offer_v1_evt_offer(
669 &T, offer, "text/plain;charset=utf-8");
670 send_zwlr_data_control_device_v1_evt_selection(&T, ddev, offer);
671 send_zwlr_data_control_offer_v1_req_receive(&T, offer,
672 "text/plain;charset=utf-8", src_pipe[1]);
673 break;
674 }
675 ret_fd = get_only_fd_from_msg(T.comp);
676
677 /* confirm receipt of fd with the correct contents; if not,
678 * reject */
679 if (ret_fd == -1) {
680 wp_error("Fd not passed through");
681 pass = false;
682 goto end;
683 }
684 uint8_t tmp = 0xab;
685 if (write(ret_fd, &tmp, 1) != 1) {
686 wp_error("Fd not writable");
687 pass = false;
688 goto end;
689 }
690 end:
691
692 checked_close(src_pipe[0]);
693 checked_close(src_pipe[1]);
694 cleanup_tstate(&T);
695
696 print_pass(pass);
697 return pass;
698 }
699
700 /* Confirm that wl_data_source.data_offer creates a pipe matching the input */
test_data_source(enum data_device_type type)701 static bool test_data_source(enum data_device_type type)
702 {
703 fprintf(stdout, "\n Data source test: %s\n",
704 data_device_type_strs[type]);
705 struct transfer_states T;
706 if (setup_tstate(&T) == -1) {
707 wp_error("Test setup failed");
708 return true;
709 }
710 bool pass = true;
711
712 int dst_pipe[2];
713 pipe(dst_pipe);
714 int ret_fd = -1;
715
716 struct wp_objid display = {0x1}, registry = {0x2}, ddman = {0x3},
717 seat = {0x4}, ddev = {0x5}, dsource = {0x6};
718
719 send_wl_display_req_get_registry(&T, display, registry);
720 send_wl_registry_evt_global(&T, registry, 1, "wl_seat", 7);
721 send_wl_registry_req_bind(&T, registry, 1, "wl_seat", 7, seat);
722 switch (type) {
723 case DDT_WAYLAND:
724 send_wl_registry_evt_global(
725 &T, registry, 2, "wl_data_device_manager", 1);
726 send_wl_registry_req_bind(&T, registry, 2,
727 "wl_data_device_manager", 1, ddman);
728 send_wl_data_device_manager_req_get_data_device(
729 &T, ddman, ddev, seat);
730 send_wl_data_device_manager_req_create_data_source(
731 &T, ddman, dsource);
732 send_wl_data_source_req_offer(
733 &T, dsource, "text/plain;charset=utf-8");
734 send_wl_data_device_req_set_selection(&T, ddev, dsource, 9999);
735 send_wl_data_source_evt_send(&T, dsource,
736 "text/plain;charset=utf-8", dst_pipe[0]);
737 break;
738 case DDT_GTK_PRIMARY:
739 send_wl_registry_evt_global(&T, registry, 2,
740 "gtk_primary_selection_device_manager", 1);
741 send_wl_registry_req_bind(&T, registry, 2,
742 "gtk_primary_selection_device_manager", 1,
743 ddman);
744 send_gtk_primary_selection_device_manager_req_get_device(
745 &T, ddman, ddev, seat);
746 send_gtk_primary_selection_device_manager_req_create_source(
747 &T, ddman, dsource);
748 send_gtk_primary_selection_source_req_offer(
749 &T, dsource, "text/plain;charset=utf-8");
750 send_gtk_primary_selection_device_req_set_selection(
751 &T, ddev, dsource, 9999);
752 send_gtk_primary_selection_source_evt_send(&T, dsource,
753 "text/plain;charset=utf-8", dst_pipe[0]);
754 break;
755 case DDT_PRIMARY:
756 send_wl_registry_evt_global(&T, registry, 2,
757 "zwp_primary_selection_device_manager_v1", 1);
758 send_wl_registry_req_bind(&T, registry, 2,
759 "zwp_primary_selection_device_manager_v1", 1,
760 ddman);
761 send_zwp_primary_selection_device_manager_v1_req_get_device(
762 &T, ddman, ddev, seat);
763 send_zwp_primary_selection_device_manager_v1_req_create_source(
764 &T, ddman, dsource);
765 send_zwp_primary_selection_source_v1_req_offer(
766 &T, dsource, "text/plain;charset=utf-8");
767 send_zwp_primary_selection_device_v1_req_set_selection(
768 &T, ddev, dsource, 9999);
769 send_zwp_primary_selection_source_v1_evt_send(&T, dsource,
770 "text/plain;charset=utf-8", dst_pipe[0]);
771 break;
772 case DDT_WLR:
773 send_wl_registry_evt_global(&T, registry, 2,
774 "zwlr_data_control_manager_v1", 1);
775 send_wl_registry_req_bind(&T, registry, 2,
776 "zwlr_data_control_manager_v1", 1, ddman);
777 send_zwlr_data_control_manager_v1_req_get_data_device(
778 &T, ddman, ddev, seat);
779 send_zwlr_data_control_manager_v1_req_create_data_source(
780 &T, ddman, dsource);
781 send_zwlr_data_control_source_v1_req_offer(
782 &T, dsource, "text/plain;charset=utf-8");
783 send_zwlr_data_control_device_v1_req_set_selection(
784 &T, ddev, dsource);
785 send_zwlr_data_control_source_v1_evt_send(&T, dsource,
786 "text/plain;charset=utf-8", dst_pipe[0]);
787 break;
788 }
789 ret_fd = get_only_fd_from_msg(T.app);
790
791 /* confirm receipt of fd with the correct contents; if not,
792 * reject */
793 if (ret_fd == -1) {
794 wp_error("Fd not passed through");
795 pass = false;
796 goto end;
797 }
798 /* todo: check readable */
799 end:
800
801 checked_close(dst_pipe[0]);
802 checked_close(dst_pipe[1]);
803 cleanup_tstate(&T);
804
805 print_pass(pass);
806 return pass;
807 }
808
809 /* Check that gamma_control copies the input file */
test_gamma_control()810 static bool test_gamma_control()
811 {
812 fprintf(stdout, "\n Gamma control test\n");
813 struct transfer_states T;
814 if (setup_tstate(&T) == -1) {
815 wp_error("Test setup failed");
816 return true;
817 }
818 bool pass = true;
819
820 int ret_fd = -1;
821
822 char *testpat = make_filled_pattern(1024, 0x12345678);
823 int fd = make_filled_file(1024, testpat);
824
825 struct wp_objid display = {0x1}, registry = {0x2},
826 gamma_manager = {0x3}, output = {0x4},
827 gamma_control = {0x5};
828
829 send_wl_display_req_get_registry(&T, display, registry);
830 send_wl_registry_evt_global(
831 &T, registry, 1, "zwlr_gamma_control_manager_v1", 1);
832 send_wl_registry_req_bind(&T, registry, 1,
833 "zwlr_gamma_control_manager_v1", 1, gamma_manager);
834 send_wl_registry_evt_global(&T, registry, 1, "wl_output", 3);
835 send_wl_registry_req_bind(&T, registry, 1, "wl_output", 3, output);
836 send_zwlr_gamma_control_manager_v1_req_get_gamma_control(
837 &T, gamma_manager, gamma_control, output);
838 send_zwlr_gamma_control_v1_evt_gamma_size(&T, gamma_control, 1024);
839 send_zwlr_gamma_control_v1_req_set_gamma(&T, gamma_control, fd);
840
841 ret_fd = get_only_fd_from_msg(T.comp);
842
843 /* confirm receipt of fd with the correct contents; if not,
844 * reject */
845 if (ret_fd == -1) {
846 wp_error("Fd not passed through");
847 pass = false;
848 goto end;
849 }
850 pass = check_file_contents(ret_fd, 1024, testpat);
851 if (!pass) {
852 wp_error("Failed to transfer file");
853 }
854 end:
855
856 free(testpat);
857 checked_close(fd);
858 cleanup_tstate(&T);
859
860 print_pass(pass);
861 return pass;
862 }
863
864 /* Check that gamma_control copies the input file */
test_presentation_time()865 static bool test_presentation_time()
866 {
867 fprintf(stdout, "\n Presentation time test\n");
868 struct transfer_states T;
869 if (setup_tstate(&T) == -1) {
870 wp_error("Test setup failed");
871 return true;
872 }
873 bool pass = true;
874
875 struct wp_objid display = {0x1}, registry = {0x2}, presentation = {0x3},
876 compositor = {0x4}, surface = {0x5}, feedback = {0x6};
877 T.app->local_time_offset = 500;
878 T.comp->local_time_offset = 600;
879
880 send_wl_display_req_get_registry(&T, display, registry);
881 send_wl_registry_evt_global(&T, registry, 1, "wp_presentation", 1);
882 send_wl_registry_evt_global(&T, registry, 2, "wl_compositor", 1);
883 send_wl_registry_req_bind(
884 &T, registry, 1, "wp_presentation", 1, presentation);
885 /* todo: run another branch with CLOCK_REALTIME */
886 send_wp_presentation_evt_clock_id(&T, presentation, CLOCK_MONOTONIC);
887 send_wl_registry_req_bind(
888 &T, registry, 12, "wl_compositor", 1, compositor);
889 send_wl_compositor_req_create_surface(&T, compositor, surface);
890
891 send_wl_surface_req_damage(&T, surface, 0, 0, 64, 64);
892
893 send_wp_presentation_req_feedback(&T, presentation, surface, feedback);
894 send_wl_surface_req_commit(&T, surface);
895 send_wp_presentation_feedback_evt_presented(
896 &T, feedback, 0, 30, 120000, 16666666, 0, 0, 7);
897 const struct msg *const last_msg = &T.app->rcvd[T.app->nrcvd - 1];
898 uint32_t tv_sec_hi = last_msg->data[2], tv_sec_lo = last_msg->data[3],
899 tv_nsec = last_msg->data[4];
900 if (tv_nsec != 120000 + T.app->local_time_offset -
901 T.comp->local_time_offset) {
902 wp_error("Time translation failed %d %d %d", tv_sec_hi,
903 tv_sec_lo, tv_nsec);
904 pass = false;
905 goto end;
906 }
907
908 /* look at timestamp */
909 if (!pass) {
910 goto end;
911 }
912 end:
913 cleanup_tstate(&T);
914
915 print_pass(pass);
916 return pass;
917 }
918
919 /* Check whether the video encoding feature can replicate a uniform
920 * color image */
test_fixed_video_color_copy(enum video_coding_fmt fmt,bool hw)921 static bool test_fixed_video_color_copy(enum video_coding_fmt fmt, bool hw)
922 {
923 (void)fmt;
924 (void)hw;
925 /* todo: back out if no dmabuf support or no video support */
926 return true;
927 }
928
929 log_handler_func_t log_funcs[2] = {test_log_handler, test_log_handler};
main(int argc,char ** argv)930 int main(int argc, char **argv)
931 {
932 (void)argc;
933 (void)argv;
934
935 set_initial_fds();
936
937 int ntest = 20;
938 int nsuccess = 0;
939 nsuccess += test_fixed_shm_buffer_copy();
940 nsuccess += test_fixed_shm_screencopy_copy();
941 nsuccess += test_fixed_keymap_copy();
942 nsuccess += test_fixed_dmabuf_copy(COPY_LINUX_DMABUF);
943 nsuccess += test_fixed_dmabuf_copy(COPY_LINUX_DMABUF_INDIR);
944 nsuccess += test_fixed_dmabuf_copy(COPY_DRM_PRIME);
945 nsuccess += test_fixed_dmabuf_copy(COPY_WLR_EXPORT);
946 nsuccess += test_data_offer(DDT_WAYLAND);
947 nsuccess += test_data_offer(DDT_PRIMARY);
948 nsuccess += test_data_offer(DDT_GTK_PRIMARY);
949 nsuccess += test_data_offer(DDT_WLR);
950 nsuccess += test_data_source(DDT_WAYLAND);
951 nsuccess += test_data_source(DDT_PRIMARY);
952 nsuccess += test_data_source(DDT_GTK_PRIMARY);
953 nsuccess += test_data_source(DDT_WLR);
954 nsuccess += test_gamma_control();
955 nsuccess += test_presentation_time();
956 nsuccess += test_fixed_video_color_copy(VIDEO_H264, false);
957 nsuccess += test_fixed_video_color_copy(VIDEO_H264, true);
958 nsuccess += test_fixed_video_color_copy(VIDEO_VP9, false);
959 // TODO: add tests for handling of common errors, e.g. invalid fd,
960 // or type confusion
961
962 fprintf(stdout, "\n%d of %d cases passed\n", nsuccess, ntest);
963
964 check_unclosed_fds();
965
966 return (nsuccess == ntest) ? EXIT_SUCCESS : EXIT_FAILURE;
967 }
968