blob: cf4517c7a7bc7051049333679ea20ae854466c67 [file] [log] [blame]
Pekka Paalanenef2b5922014-09-23 22:08:49 -04001/*
2 * Copyright © 2011 Benjamin Franzke
3 * Copyright © 2010 Intel Corporation
4 * Copyright © 2014 Collabora, Ltd.
5 *
Bryce Harrington1f6b0d12015-06-10 22:48:59 -07006 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
Pekka Paalanenef2b5922014-09-23 22:08:49 -040012 *
Bryce Harrington1f6b0d12015-06-10 22:48:59 -070013 * The above copyright notice and this permission notice (including the next
14 * paragraph) shall be included in all copies or substantial portions of the
15 * Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 * DEALINGS IN THE SOFTWARE.
Pekka Paalanenef2b5922014-09-23 22:08:49 -040024 */
25
26#include <config.h>
27
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <stdbool.h>
32#include <assert.h>
33#include <unistd.h>
34#include <sys/mman.h>
35#include <signal.h>
36#include <time.h>
37
38#include <wayland-client.h>
Jon Cruz35b2eaa2015-06-15 15:37:08 -070039#include "shared/helpers.h"
Jon Cruz4678bab2015-06-15 15:37:07 -070040#include "shared/os-compatibility.h"
Pekka Paalanenef2b5922014-09-23 22:08:49 -040041#include "presentation_timing-client-protocol.h"
42
Pekka Paalanenef2b5922014-09-23 22:08:49 -040043enum run_mode {
44 RUN_MODE_FEEDBACK,
45 RUN_MODE_FEEDBACK_IDLE,
46 RUN_MODE_PRESENT,
47};
48
Pekka Paalanen4d012bc2015-02-06 13:50:37 +020049static const char * const run_mode_name[] = {
50 [RUN_MODE_FEEDBACK] = "feedback",
51 [RUN_MODE_FEEDBACK_IDLE] = "feedback-idle",
52 [RUN_MODE_PRESENT] = "low-lat present",
53};
54
Pekka Paalanenef2b5922014-09-23 22:08:49 -040055struct output {
56 struct wl_output *output;
57 uint32_t name;
58 struct wl_list link;
59};
60
61struct display {
62 struct wl_display *display;
63 struct wl_registry *registry;
64 struct wl_compositor *compositor;
65 struct wl_shell *shell;
66
67 struct wl_shm *shm;
68 uint32_t formats;
69
70 struct presentation *presentation;
71 clockid_t clk_id;
72
73 struct wl_list output_list; /* struct output::link */
74};
75
76struct feedback {
77 struct window *window;
78 unsigned frame_no;
79 struct presentation_feedback *feedback;
80 struct timespec commit;
81 struct timespec target;
82 uint32_t frame_stamp;
83 struct wl_list link;
84 struct timespec present;
85};
86
87struct buffer {
88 struct wl_buffer *buffer;
89 void *shm_data;
90 int busy;
91};
92
93struct window {
94 struct display *display;
95 int width, height;
96 enum run_mode mode;
97 struct wl_surface *surface;
98 struct wl_shell_surface *shell_surface;
99
100 struct buffer *buffers;
101 int num_buffers;
102 int next;
103 int refresh_nsec;
104
105 struct wl_callback *callback;
106 struct wl_list feedback_list;
107
108 struct feedback *received_feedback;
109};
110
111#define NSEC_PER_SEC 1000000000
112
113static void
114buffer_release(void *data, struct wl_buffer *buffer)
115{
116 struct buffer *mybuf = data;
117
118 mybuf->busy = 0;
119}
120
121static const struct wl_buffer_listener buffer_listener = {
122 buffer_release
123};
124
125static int
126create_shm_buffers(struct display *display, struct buffer **buffers,
127 int num_buffers, int width, int height, uint32_t format)
128{
129 struct buffer *bufs;
130 struct wl_shm_pool *pool;
131 int fd, size, stride, offset;
132 void *data;
133 int i;
134
135 stride = width * 4;
136 size = stride * height * num_buffers;
137
138 fd = os_create_anonymous_file(size);
139 if (fd < 0) {
140 fprintf(stderr, "creating a buffer file for %d B failed: %m\n",
141 size);
142 return -1;
143 }
144
145 data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
146 if (data == MAP_FAILED) {
147 fprintf(stderr, "mmap failed: %m\n");
148 close(fd);
149 return -1;
150 }
151
152 pool = wl_shm_create_pool(display->shm, fd, size);
153 offset = 0;
154
155 bufs = calloc(num_buffers, sizeof(*bufs));
156 assert(bufs);
157
158 for (i = 0; i < num_buffers; i++) {
159 bufs[i].buffer = wl_shm_pool_create_buffer(pool, offset,
160 width, height,
161 stride, format);
162 assert(bufs[i].buffer);
163 wl_buffer_add_listener(bufs[i].buffer,
164 &buffer_listener, &bufs[i]);
165
166 bufs[i].shm_data = (char *)data + offset;
167 offset += stride * height;
168 }
169
170 wl_shm_pool_destroy(pool);
171 close(fd);
172
173 *buffers = bufs;
174
175 return 0;
176}
177
178static void
179handle_ping(void *data, struct wl_shell_surface *shell_surface,
180 uint32_t serial)
181{
182 wl_shell_surface_pong(shell_surface, serial);
183}
184
185static void
186handle_configure(void *data, struct wl_shell_surface *shell_surface,
187 uint32_t edges, int32_t width, int32_t height)
188{
189}
190
191static void
192handle_popup_done(void *data, struct wl_shell_surface *shell_surface)
193{
194}
195
196static const struct wl_shell_surface_listener shell_surface_listener = {
197 handle_ping,
198 handle_configure,
199 handle_popup_done
200};
201
202static struct window *
203create_window(struct display *display, int width, int height,
204 enum run_mode mode)
205{
206 struct window *window;
Pekka Paalanen4d012bc2015-02-06 13:50:37 +0200207 char title[128];
Pekka Paalanenef2b5922014-09-23 22:08:49 -0400208 int ret;
209
Pekka Paalanen4d012bc2015-02-06 13:50:37 +0200210 snprintf(title, sizeof(title),
211 "presentation-shm: %s", run_mode_name[mode]);
212
Pekka Paalanenef2b5922014-09-23 22:08:49 -0400213 window = calloc(1, sizeof *window);
214 if (!window)
215 return NULL;
216
217 window->mode = mode;
218 window->callback = NULL;
219 wl_list_init(&window->feedback_list);
220 window->display = display;
221 window->width = width;
222 window->height = height;
223 window->surface = wl_compositor_create_surface(display->compositor);
224 window->shell_surface = wl_shell_get_shell_surface(display->shell,
225 window->surface);
226
227 if (window->shell_surface)
228 wl_shell_surface_add_listener(window->shell_surface,
229 &shell_surface_listener, window);
230
Pekka Paalanen4d012bc2015-02-06 13:50:37 +0200231 wl_shell_surface_set_title(window->shell_surface, title);
Pekka Paalanenef2b5922014-09-23 22:08:49 -0400232
233 wl_shell_surface_set_toplevel(window->shell_surface);
234
235 window->num_buffers = 60;
236 window->refresh_nsec = NSEC_PER_SEC / 60; /* 60 Hz guess */
237 window->next = 0;
238 ret = create_shm_buffers(window->display,
239 &window->buffers, window->num_buffers,
240 window->width, window->height,
241 WL_SHM_FORMAT_XRGB8888);
242 assert(ret == 0);
243
244 return window;
245}
246
247static void
248destroy_feedback(struct feedback *feedback)
249{
250 if (feedback->feedback)
251 presentation_feedback_destroy(feedback->feedback);
252
253 wl_list_remove(&feedback->link);
254 free(feedback);
255}
256
257static void
258destroy_window(struct window *window)
259{
260 int i;
261
262 while (!wl_list_empty(&window->feedback_list)) {
263 struct feedback *f;
264
265 f = wl_container_of(window->feedback_list.next, f, link);
266 printf("clean up feedback %u\n", f->frame_no);
267 destroy_feedback(f);
268 }
269
270 if (window->callback)
271 wl_callback_destroy(window->callback);
272
273 wl_shell_surface_destroy(window->shell_surface);
274 wl_surface_destroy(window->surface);
275
276 for (i = 0; i < window->num_buffers; i++)
277 wl_buffer_destroy(window->buffers[i].buffer);
278 /* munmap(window->buffers[0].shm_data, size); */
279 free(window->buffers);
280
281 free(window);
282}
283
284static struct buffer *
285window_next_buffer(struct window *window)
286{
287 struct buffer *buf = &window->buffers[window->next];
288
289 window->next = (window->next + 1) % window->num_buffers;
290
291 return buf;
292}
293
294static void
295paint_pixels(void *image, int width, int height, uint32_t phase)
296{
297 const int halfh = height / 2;
298 const int halfw = width / 2;
299 uint32_t *pixel = image;
300 int y, or;
301 double ang = M_PI * 2.0 / 1000000.0 * phase;
302 double s = sin(ang);
303 double c = cos(ang);
304
305 /* squared radii thresholds */
306 or = (halfw < halfh ? halfw : halfh) - 16;
307 or *= or;
308
309 for (y = 0; y < height; y++) {
310 int x;
311 int oy = y - halfh;
312 int y2 = oy * oy;
313
314 for (x = 0; x < width; x++) {
315 int ox = x - halfw;
316 uint32_t v = 0xff000000;
317 double rx, ry;
318
319 if (ox * ox + y2 > or) {
320 if (ox * oy > 0)
321 *pixel++ = 0xff000000;
322 else
323 *pixel++ = 0xffffffff;
324 continue;
325 }
326
327 rx = c * ox + s * oy;
328 ry = -s * ox + c * oy;
329
330 if (rx < 0.0)
331 v |= 0x00ff0000;
332 if (ry < 0.0)
333 v |= 0x0000ff00;
334 if ((rx < 0.0) == (ry < 0.0))
335 v |= 0x000000ff;
336
337 *pixel++ = v;
338 }
339 }
340}
341
342static void
343feedback_sync_output(void *data,
344 struct presentation_feedback *presentation_feedback,
345 struct wl_output *output)
346{
347 /* not interested */
348}
349
350static char *
351pflags_to_str(uint32_t flags, char *str, unsigned len)
352{
353 static const struct {
354 uint32_t flag;
355 char sym;
356 } desc[] = {
Pekka Paalanen63495862014-12-17 16:20:42 +0200357 { PRESENTATION_FEEDBACK_KIND_VSYNC, 's' },
358 { PRESENTATION_FEEDBACK_KIND_HW_CLOCK, 'c' },
359 { PRESENTATION_FEEDBACK_KIND_HW_COMPLETION, 'e' },
360 { PRESENTATION_FEEDBACK_KIND_ZERO_COPY, 'z' },
Pekka Paalanenef2b5922014-09-23 22:08:49 -0400361 };
362 unsigned i;
363
364 *str = '\0';
365 if (len < ARRAY_LENGTH(desc) + 1)
366 return str;
367
368 for (i = 0; i < ARRAY_LENGTH(desc); i++)
369 str[i] = flags & desc[i].flag ? desc[i].sym : '_';
370 str[ARRAY_LENGTH(desc)] = '\0';
371
372 return str;
373}
374
375static uint32_t
376timespec_to_ms(const struct timespec *ts)
377{
378 return (uint32_t)ts->tv_sec * 1000 + ts->tv_nsec / 1000000;
379}
380
381static void
382timespec_from_proto(struct timespec *tm, uint32_t tv_sec_hi,
383 uint32_t tv_sec_lo, uint32_t tv_nsec)
384{
385 tm->tv_sec = ((uint64_t)tv_sec_hi << 32) + tv_sec_lo;
386 tm->tv_nsec = tv_nsec;
387}
388
389static int
390timespec_diff_to_usec(const struct timespec *a, const struct timespec *b)
391{
392 time_t secs = a->tv_sec - b->tv_sec;
393 long nsec = a->tv_nsec - b->tv_nsec;
394
395 return secs * 1000000 + nsec / 1000;
396}
397
398static void
399feedback_presented(void *data,
400 struct presentation_feedback *presentation_feedback,
401 uint32_t tv_sec_hi,
402 uint32_t tv_sec_lo,
403 uint32_t tv_nsec,
404 uint32_t refresh_nsec,
405 uint32_t seq_hi,
406 uint32_t seq_lo,
407 uint32_t flags)
408{
409 struct feedback *feedback = data;
410 struct window *window = feedback->window;
411 struct feedback *prev_feedback = window->received_feedback;
412 uint64_t seq = ((uint64_t)seq_hi << 32) + seq_lo;
413 const struct timespec *prevpresent;
414 uint32_t commit, present;
415 uint32_t f2c, c2p, f2p;
416 int p2p, t2p;
417 char flagstr[10];
418
419 timespec_from_proto(&feedback->present, tv_sec_hi, tv_sec_lo, tv_nsec);
420 commit = timespec_to_ms(&feedback->commit);
421 present = timespec_to_ms(&feedback->present);
422
423 if (prev_feedback)
424 prevpresent = &prev_feedback->present;
425 else
426 prevpresent = &feedback->present;
427
428 f2c = commit - feedback->frame_stamp;
429 c2p = present - commit;
430 f2p = present - feedback->frame_stamp;
431 p2p = timespec_diff_to_usec(&feedback->present, prevpresent);
432 t2p = timespec_diff_to_usec(&feedback->present, &feedback->target);
433
434 switch (window->mode) {
435 case RUN_MODE_PRESENT:
436 printf("%6u: c2p %4u ms, p2p %5d us, t2p %6d us, [%s] "
437 "seq %" PRIu64 "\n", feedback->frame_no, c2p,
438 p2p, t2p,
439 pflags_to_str(flags, flagstr, sizeof(flagstr)), seq);
440 break;
441 case RUN_MODE_FEEDBACK:
442 case RUN_MODE_FEEDBACK_IDLE:
443 printf("%6u: f2c %2u ms, c2p %2u ms, f2p %2u ms, p2p %5d us, "
444 "t2p %6d, [%s], seq %" PRIu64 "\n", feedback->frame_no,
445 f2c, c2p, f2p, p2p, t2p,
446 pflags_to_str(flags, flagstr, sizeof(flagstr)), seq);
447 }
448
449 if (window->received_feedback)
450 destroy_feedback(window->received_feedback);
451 window->received_feedback = feedback;
452}
453
454static void
455feedback_discarded(void *data,
456 struct presentation_feedback *presentation_feedback)
457{
458 struct feedback *feedback = data;
459
460 printf("discarded %u\n", feedback->frame_no);
461
462 destroy_feedback(feedback);
463}
464
465static const struct presentation_feedback_listener feedback_listener = {
466 feedback_sync_output,
467 feedback_presented,
468 feedback_discarded
469};
470
471static void
472window_create_feedback(struct window *window, uint32_t frame_stamp)
473{
474 static unsigned seq;
475 struct presentation *pres = window->display->presentation;
476 struct feedback *feedback;
477
478 seq++;
479
480 if (!pres)
481 return;
482
483 feedback = calloc(1, sizeof *feedback);
484 if (!feedback)
485 return;
486
487 feedback->window = window;
488 feedback->feedback = presentation_feedback(pres, window->surface);
489 presentation_feedback_add_listener(feedback->feedback,
490 &feedback_listener, feedback);
491
492 feedback->frame_no = seq;
493 clock_gettime(window->display->clk_id, &feedback->commit);
494 feedback->frame_stamp = frame_stamp;
495 feedback->target = feedback->commit;
496
497 wl_list_insert(&window->feedback_list, &feedback->link);
498}
499
500static void
501window_commit_next(struct window *window)
502{
503 struct buffer *buffer;
504
505 buffer = window_next_buffer(window);
506 assert(buffer);
507
508 wl_surface_attach(window->surface, buffer->buffer, 0, 0);
509 wl_surface_damage(window->surface, 0, 0, window->width, window->height);
510 wl_surface_commit(window->surface);
511 buffer->busy = 1;
512}
513
514static const struct wl_callback_listener frame_listener_mode_feedback;
515
516static void
517redraw_mode_feedback(void *data, struct wl_callback *callback, uint32_t time)
518{
519 struct window *window = data;
520
521 if (callback && window->mode == RUN_MODE_FEEDBACK_IDLE)
522 sleep(1);
523
524 if (callback)
525 wl_callback_destroy(callback);
526
527 window->callback = wl_surface_frame(window->surface);
528 wl_callback_add_listener(window->callback,
529 &frame_listener_mode_feedback, window);
530
531 window_create_feedback(window, time);
532 window_commit_next(window);
533}
534
535static const struct wl_callback_listener frame_listener_mode_feedback = {
536 redraw_mode_feedback
537};
538
539static const struct presentation_feedback_listener feedkick_listener;
540
541static void
542window_feedkick(struct window *window)
543{
544 struct presentation *pres = window->display->presentation;
545 struct presentation_feedback *fback;
546
547 fback = presentation_feedback(pres, window->surface);
548 presentation_feedback_add_listener(fback, &feedkick_listener, window);
549}
550
551static void
552feedkick_presented(void *data,
553 struct presentation_feedback *presentation_feedback,
554 uint32_t tv_sec_hi,
555 uint32_t tv_sec_lo,
556 uint32_t tv_nsec,
557 uint32_t refresh_nsec,
558 uint32_t seq_hi,
559 uint32_t seq_lo,
560 uint32_t flags)
561{
562 struct window *window = data;
563
564 presentation_feedback_destroy(presentation_feedback);
565 window->refresh_nsec = refresh_nsec;
566
567 switch (window->mode) {
568 case RUN_MODE_PRESENT:
569 window_create_feedback(window, 0);
570 window_feedkick(window);
571 window_commit_next(window);
572 break;
573 case RUN_MODE_FEEDBACK:
574 case RUN_MODE_FEEDBACK_IDLE:
575 assert(0 && "bad mode");
576 }
577}
578
579static void
580feedkick_discarded(void *data,
581 struct presentation_feedback *presentation_feedback)
582{
583 struct window *window = data;
584
585 presentation_feedback_destroy(presentation_feedback);
586
587 switch (window->mode) {
588 case RUN_MODE_PRESENT:
589 window_create_feedback(window, 0);
590 window_feedkick(window);
591 window_commit_next(window);
592 break;
593 case RUN_MODE_FEEDBACK:
594 case RUN_MODE_FEEDBACK_IDLE:
595 assert(0 && "bad mode");
596 }
597}
598
599static const struct presentation_feedback_listener feedkick_listener = {
600 feedback_sync_output,
601 feedkick_presented,
602 feedkick_discarded
603};
604
605static void
606firstdraw_mode_burst(struct window *window)
607{
608 switch (window->mode) {
609 case RUN_MODE_PRESENT:
610 window_create_feedback(window, 0);
611 break;
612 case RUN_MODE_FEEDBACK:
613 case RUN_MODE_FEEDBACK_IDLE:
614 assert(0 && "bad mode");
615 }
616
617 window_feedkick(window);
618 window_commit_next(window);
619}
620
621static void
622window_prerender(struct window *window)
623{
624 int i;
625 int timefactor = 1000000 / window->num_buffers;
626
627 for (i = 0; i < window->num_buffers; i++) {
628 struct buffer *buf = &window->buffers[i];
629
630 if (buf->busy)
631 fprintf(stderr, "wl_buffer id %u) busy\n",
632 wl_proxy_get_id(
633 (struct wl_proxy *)buf->buffer));
634
635 paint_pixels(buf->shm_data, window->width, window->height,
636 i * timefactor);
637 }
638}
639
640static void
641output_destroy(struct output *o)
642{
643 wl_output_destroy(o->output);
644 wl_list_remove(&o->link);
645 free(o);
646}
647
648static void
649display_add_output(struct display *d, uint32_t name, uint32_t version)
650{
651 struct output *o;
652
653 o = calloc(1, sizeof(*o));
654 assert(o);
655
656 o->output = wl_registry_bind(d->registry, name,
657 &wl_output_interface, 1);
658 o->name = name;
659 wl_list_insert(&d->output_list, &o->link);
660}
661
662static void
663presentation_clock_id(void *data, struct presentation *presentation,
664 uint32_t clk_id)
665{
666 struct display *d = data;
667
668 d->clk_id = clk_id;
669}
670
671static const struct presentation_listener presentation_listener = {
672 presentation_clock_id
673};
674
675static void
676shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
677{
678 struct display *d = data;
679
680 d->formats |= (1 << format);
681}
682
683static const struct wl_shm_listener shm_listener = {
684 shm_format
685};
686
687static void
688registry_handle_global(void *data, struct wl_registry *registry,
689 uint32_t name, const char *interface, uint32_t version)
690{
691 struct display *d = data;
692
693 if (strcmp(interface, "wl_compositor") == 0) {
694 d->compositor =
695 wl_registry_bind(registry,
696 name, &wl_compositor_interface, 1);
697 } else if (strcmp(interface, "wl_shell") == 0) {
698 d->shell = wl_registry_bind(registry,
699 name, &wl_shell_interface, 1);
700 } else if (strcmp(interface, "wl_shm") == 0) {
701 d->shm = wl_registry_bind(registry,
702 name, &wl_shm_interface, 1);
703 wl_shm_add_listener(d->shm, &shm_listener, d);
704 } else if (strcmp(interface, "wl_output") == 0) {
705 display_add_output(d, name, version);
706 } else if (strcmp(interface, "presentation") == 0) {
707 d->presentation =
708 wl_registry_bind(registry,
709 name, &presentation_interface, 1);
710 presentation_add_listener(d->presentation,
711 &presentation_listener, d);
712 }
713}
714
715static void
716registry_handle_global_remove(void *data, struct wl_registry *registry,
717 uint32_t name)
718{
719 struct display *d = data;
720 struct output *output, *otmp;
721
722 wl_list_for_each_safe(output, otmp, &d->output_list, link) {
723 if (output->name != name)
724 continue;
725
726 output_destroy(output);
727 }
728}
729
730static const struct wl_registry_listener registry_listener = {
731 registry_handle_global,
732 registry_handle_global_remove
733};
734
735static struct display *
736create_display(void)
737{
738 struct display *display;
739
740 display = malloc(sizeof *display);
741 if (display == NULL) {
742 fprintf(stderr, "out of memory\n");
743 exit(1);
744 }
745 display->display = wl_display_connect(NULL);
746 assert(display->display);
747
748 display->formats = 0;
749 display->clk_id = -1;
750 wl_list_init(&display->output_list);
751 display->registry = wl_display_get_registry(display->display);
752 wl_registry_add_listener(display->registry,
753 &registry_listener, display);
754 wl_display_roundtrip(display->display);
755 if (display->shm == NULL) {
756 fprintf(stderr, "No wl_shm global\n");
757 exit(1);
758 }
759
760 wl_display_roundtrip(display->display);
761
762 if (!(display->formats & (1 << WL_SHM_FORMAT_XRGB8888))) {
763 fprintf(stderr, "WL_SHM_FORMAT_XRGB32 not available\n");
764 exit(1);
765 }
766
767 wl_display_get_fd(display->display);
768
769 return display;
770}
771
772static void
773destroy_display(struct display *display)
774{
775 while (!wl_list_empty(&display->output_list)) {
776 struct output *o;
777
778 o = wl_container_of(display->output_list.next, o, link);
779 output_destroy(o);
780 }
781
782 if (display->shm)
783 wl_shm_destroy(display->shm);
784
785 if (display->shell)
786 wl_shell_destroy(display->shell);
787
788 if (display->compositor)
789 wl_compositor_destroy(display->compositor);
790
791 wl_registry_destroy(display->registry);
792 wl_display_flush(display->display);
793 wl_display_disconnect(display->display);
794 free(display);
795}
796
797static int running = 1;
798
799static void
800signal_int(int signum)
801{
802 running = 0;
803}
804
805static void
806usage(const char *prog, int exit_code)
807{
808 fprintf(stderr, "Usage: %s [mode] [options]\n"
809 "where 'mode' is one of\n"
810 " -f\trun in feedback mode (default)\n"
811 " -i\trun in feedback-idle mode; sleep 1s between frames\n"
812 " -p\trun in low-latency presentation mode\n"
813 "and 'options' may include\n",
814 prog);
815
816 fprintf(stderr, "Printed timing statistics, depending on mode:\n"
817 " commit sequence number\n"
818 " f2c: time from frame callback timestamp to commit\n"
819 " c2p: time from commit to presentation\n"
820 " f2p: time from frame callback timestamp to presentation\n"
821 " p2p: time from previous presentation to this one\n"
822 " t2p: time from target timestamp to presentation\n"
823 " seq: MSC\n");
824
825
826 exit(exit_code);
827}
828
829int
830main(int argc, char **argv)
831{
832 struct sigaction sigint;
833 struct display *display;
834 struct window *window;
835 int ret = 0;
836 enum run_mode mode = RUN_MODE_FEEDBACK;
837 int i;
838
839 for (i = 1; i < argc; i++) {
840 if (strcmp("-f", argv[i]) == 0)
841 mode = RUN_MODE_FEEDBACK;
842 else if (strcmp("-i", argv[i]) == 0)
843 mode = RUN_MODE_FEEDBACK_IDLE;
844 else if (strcmp("-p", argv[i]) == 0)
845 mode = RUN_MODE_PRESENT;
846 else
847 usage(argv[0], EXIT_FAILURE);
848 }
849
850 display = create_display();
851 window = create_window(display, 250, 250, mode);
852 if (!window)
853 return 1;
854
855 sigint.sa_handler = signal_int;
856 sigemptyset(&sigint.sa_mask);
857 sigint.sa_flags = SA_RESETHAND;
858 sigaction(SIGINT, &sigint, NULL);
859
860 window_prerender(window);
861
862 switch (mode) {
863 case RUN_MODE_FEEDBACK:
864 case RUN_MODE_FEEDBACK_IDLE:
865 redraw_mode_feedback(window, NULL, 0);
866 break;
867 case RUN_MODE_PRESENT:
868 firstdraw_mode_burst(window);
869 break;
870 }
871
872 while (running && ret != -1)
873 ret = wl_display_dispatch(display->display);
874
875 fprintf(stderr, "presentation-shm exiting\n");
876 destroy_window(window);
877 destroy_display(display);
878
879 return 0;
880}