3957863667
The printf() format specifier "%m" is a glibc extension to print the string returned by strerror(errno). While supported by other libraries (e.g. uClibc and musl), it is not widely portable. In Weston code the format string is often passed to a logging function that calls other syscalls before the conversion of "%m" takes place. If one of such syscall modifies the value in errno, the conversion of "%m" will incorrectly report the error string corresponding to the new value of errno. Remove all the occurrences of the specifier "%m" in Weston code by using directly the string returned by strerror(errno). While there, fix some minor indentation issue. Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
912 lines
21 KiB
C
912 lines
21 KiB
C
/*
|
|
* Copyright © 2011 Benjamin Franzke
|
|
* Copyright © 2010 Intel Corporation
|
|
* Copyright © 2014 Collabora, Ltd.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice (including the next
|
|
* paragraph) shall be included in all copies or substantial portions of the
|
|
* Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdbool.h>
|
|
#include <assert.h>
|
|
#include <unistd.h>
|
|
#include <sys/mman.h>
|
|
#include <signal.h>
|
|
#include <time.h>
|
|
#include <errno.h>
|
|
|
|
#include <wayland-client.h>
|
|
#include "shared/helpers.h"
|
|
#include <libweston/zalloc.h>
|
|
#include "shared/timespec-util.h"
|
|
#include "shared/os-compatibility.h"
|
|
#include "presentation-time-client-protocol.h"
|
|
|
|
enum run_mode {
|
|
RUN_MODE_FEEDBACK,
|
|
RUN_MODE_FEEDBACK_IDLE,
|
|
RUN_MODE_PRESENT,
|
|
};
|
|
|
|
static const char * const run_mode_name[] = {
|
|
[RUN_MODE_FEEDBACK] = "feedback",
|
|
[RUN_MODE_FEEDBACK_IDLE] = "feedback-idle",
|
|
[RUN_MODE_PRESENT] = "low-lat present",
|
|
};
|
|
|
|
struct output {
|
|
struct wl_output *output;
|
|
uint32_t name;
|
|
struct wl_list link;
|
|
};
|
|
|
|
struct display {
|
|
struct wl_display *display;
|
|
struct wl_registry *registry;
|
|
struct wl_compositor *compositor;
|
|
struct wl_shell *shell;
|
|
|
|
struct wl_shm *shm;
|
|
uint32_t formats;
|
|
|
|
struct wp_presentation *presentation;
|
|
clockid_t clk_id;
|
|
|
|
struct wl_list output_list; /* struct output::link */
|
|
};
|
|
|
|
struct feedback {
|
|
struct window *window;
|
|
unsigned frame_no;
|
|
struct wp_presentation_feedback *feedback;
|
|
struct timespec commit;
|
|
struct timespec target;
|
|
uint32_t frame_stamp;
|
|
struct wl_list link;
|
|
struct timespec present;
|
|
};
|
|
|
|
struct buffer {
|
|
struct wl_buffer *buffer;
|
|
void *shm_data;
|
|
int busy;
|
|
};
|
|
|
|
struct window {
|
|
struct display *display;
|
|
int width, height;
|
|
enum run_mode mode;
|
|
struct wl_surface *surface;
|
|
struct wl_shell_surface *shell_surface;
|
|
|
|
struct buffer *buffers;
|
|
int num_buffers;
|
|
int next;
|
|
int refresh_nsec;
|
|
int commit_delay_msecs;
|
|
|
|
struct wl_callback *callback;
|
|
struct wl_list feedback_list;
|
|
|
|
struct feedback *received_feedback;
|
|
};
|
|
|
|
#define NSEC_PER_SEC 1000000000
|
|
|
|
static void
|
|
buffer_release(void *data, struct wl_buffer *buffer)
|
|
{
|
|
struct buffer *mybuf = data;
|
|
|
|
mybuf->busy = 0;
|
|
}
|
|
|
|
static const struct wl_buffer_listener buffer_listener = {
|
|
buffer_release
|
|
};
|
|
|
|
static int
|
|
create_shm_buffers(struct display *display, struct buffer **buffers,
|
|
int num_buffers, int width, int height, uint32_t format)
|
|
{
|
|
struct buffer *bufs;
|
|
struct wl_shm_pool *pool;
|
|
int fd, size, stride, offset;
|
|
void *data;
|
|
int i;
|
|
|
|
stride = width * 4;
|
|
size = stride * height * num_buffers;
|
|
|
|
fd = os_create_anonymous_file(size);
|
|
if (fd < 0) {
|
|
fprintf(stderr, "creating a buffer file for %d B failed: %s\n",
|
|
size, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
|
if (data == MAP_FAILED) {
|
|
fprintf(stderr, "mmap failed: %s\n", strerror(errno));
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
pool = wl_shm_create_pool(display->shm, fd, size);
|
|
offset = 0;
|
|
|
|
bufs = calloc(num_buffers, sizeof(*bufs));
|
|
assert(bufs);
|
|
|
|
for (i = 0; i < num_buffers; i++) {
|
|
bufs[i].buffer = wl_shm_pool_create_buffer(pool, offset,
|
|
width, height,
|
|
stride, format);
|
|
assert(bufs[i].buffer);
|
|
wl_buffer_add_listener(bufs[i].buffer,
|
|
&buffer_listener, &bufs[i]);
|
|
|
|
bufs[i].shm_data = (char *)data + offset;
|
|
offset += stride * height;
|
|
}
|
|
|
|
wl_shm_pool_destroy(pool);
|
|
close(fd);
|
|
|
|
*buffers = bufs;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
handle_ping(void *data, struct wl_shell_surface *shell_surface,
|
|
uint32_t serial)
|
|
{
|
|
wl_shell_surface_pong(shell_surface, serial);
|
|
}
|
|
|
|
static void
|
|
handle_configure(void *data, struct wl_shell_surface *shell_surface,
|
|
uint32_t edges, int32_t width, int32_t height)
|
|
{
|
|
}
|
|
|
|
static void
|
|
handle_popup_done(void *data, struct wl_shell_surface *shell_surface)
|
|
{
|
|
}
|
|
|
|
static const struct wl_shell_surface_listener shell_surface_listener = {
|
|
handle_ping,
|
|
handle_configure,
|
|
handle_popup_done
|
|
};
|
|
|
|
static struct window *
|
|
create_window(struct display *display, int width, int height,
|
|
enum run_mode mode, int commit_delay_msecs)
|
|
{
|
|
struct window *window;
|
|
char title[128];
|
|
int ret;
|
|
|
|
snprintf(title, sizeof(title),
|
|
"presentation-shm: %s [Delay %i msecs]", run_mode_name[mode],
|
|
commit_delay_msecs);
|
|
|
|
window = zalloc(sizeof *window);
|
|
if (!window)
|
|
return NULL;
|
|
|
|
window->commit_delay_msecs = commit_delay_msecs;
|
|
window->mode = mode;
|
|
window->callback = NULL;
|
|
wl_list_init(&window->feedback_list);
|
|
window->display = display;
|
|
window->width = width;
|
|
window->height = height;
|
|
window->surface = wl_compositor_create_surface(display->compositor);
|
|
window->shell_surface = wl_shell_get_shell_surface(display->shell,
|
|
window->surface);
|
|
|
|
if (window->shell_surface)
|
|
wl_shell_surface_add_listener(window->shell_surface,
|
|
&shell_surface_listener, window);
|
|
|
|
wl_shell_surface_set_title(window->shell_surface, title);
|
|
|
|
wl_shell_surface_set_toplevel(window->shell_surface);
|
|
|
|
window->num_buffers = 60;
|
|
window->refresh_nsec = NSEC_PER_SEC / 60; /* 60 Hz guess */
|
|
window->next = 0;
|
|
ret = create_shm_buffers(window->display,
|
|
&window->buffers, window->num_buffers,
|
|
window->width, window->height,
|
|
WL_SHM_FORMAT_XRGB8888);
|
|
assert(ret == 0);
|
|
|
|
return window;
|
|
}
|
|
|
|
static void
|
|
destroy_feedback(struct feedback *feedback)
|
|
{
|
|
if (feedback->feedback)
|
|
wp_presentation_feedback_destroy(feedback->feedback);
|
|
|
|
wl_list_remove(&feedback->link);
|
|
free(feedback);
|
|
}
|
|
|
|
static void
|
|
destroy_window(struct window *window)
|
|
{
|
|
int i;
|
|
|
|
while (!wl_list_empty(&window->feedback_list)) {
|
|
struct feedback *f;
|
|
|
|
f = wl_container_of(window->feedback_list.next, f, link);
|
|
printf("clean up feedback %u\n", f->frame_no);
|
|
destroy_feedback(f);
|
|
}
|
|
|
|
if (window->callback)
|
|
wl_callback_destroy(window->callback);
|
|
|
|
wl_shell_surface_destroy(window->shell_surface);
|
|
wl_surface_destroy(window->surface);
|
|
|
|
for (i = 0; i < window->num_buffers; i++)
|
|
wl_buffer_destroy(window->buffers[i].buffer);
|
|
/* munmap(window->buffers[0].shm_data, size); */
|
|
free(window->buffers);
|
|
|
|
free(window);
|
|
}
|
|
|
|
static struct buffer *
|
|
window_next_buffer(struct window *window)
|
|
{
|
|
struct buffer *buf = &window->buffers[window->next];
|
|
|
|
window->next = (window->next + 1) % window->num_buffers;
|
|
|
|
return buf;
|
|
}
|
|
|
|
static void
|
|
paint_pixels(void *image, int width, int height, uint32_t phase)
|
|
{
|
|
const int halfh = height / 2;
|
|
const int halfw = width / 2;
|
|
uint32_t *pixel = image;
|
|
int y, or;
|
|
double ang = M_PI * 2.0 / 1000000.0 * phase;
|
|
double s = sin(ang);
|
|
double c = cos(ang);
|
|
|
|
/* squared radii thresholds */
|
|
or = (halfw < halfh ? halfw : halfh) - 16;
|
|
or *= or;
|
|
|
|
for (y = 0; y < height; y++) {
|
|
int x;
|
|
int oy = y - halfh;
|
|
int y2 = oy * oy;
|
|
|
|
for (x = 0; x < width; x++) {
|
|
int ox = x - halfw;
|
|
uint32_t v = 0xff000000;
|
|
double rx, ry;
|
|
|
|
if (ox * ox + y2 > or) {
|
|
if (ox * oy > 0)
|
|
*pixel++ = 0xff000000;
|
|
else
|
|
*pixel++ = 0xffffffff;
|
|
continue;
|
|
}
|
|
|
|
rx = c * ox + s * oy;
|
|
ry = -s * ox + c * oy;
|
|
|
|
if (rx < 0.0)
|
|
v |= 0x00ff0000;
|
|
if (ry < 0.0)
|
|
v |= 0x0000ff00;
|
|
if ((rx < 0.0) == (ry < 0.0))
|
|
v |= 0x000000ff;
|
|
|
|
*pixel++ = v;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
feedback_sync_output(void *data,
|
|
struct wp_presentation_feedback *presentation_feedback,
|
|
struct wl_output *output)
|
|
{
|
|
/* not interested */
|
|
}
|
|
|
|
static char *
|
|
pflags_to_str(uint32_t flags, char *str, unsigned len)
|
|
{
|
|
static const struct {
|
|
uint32_t flag;
|
|
char sym;
|
|
} desc[] = {
|
|
{ WP_PRESENTATION_FEEDBACK_KIND_VSYNC, 's' },
|
|
{ WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK, 'c' },
|
|
{ WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION, 'e' },
|
|
{ WP_PRESENTATION_FEEDBACK_KIND_ZERO_COPY, 'z' },
|
|
};
|
|
unsigned i;
|
|
|
|
*str = '\0';
|
|
if (len < ARRAY_LENGTH(desc) + 1)
|
|
return str;
|
|
|
|
for (i = 0; i < ARRAY_LENGTH(desc); i++)
|
|
str[i] = flags & desc[i].flag ? desc[i].sym : '_';
|
|
str[ARRAY_LENGTH(desc)] = '\0';
|
|
|
|
return str;
|
|
}
|
|
|
|
static uint32_t
|
|
timespec_to_ms(const struct timespec *ts)
|
|
{
|
|
return (uint32_t)ts->tv_sec * 1000 + ts->tv_nsec / 1000000;
|
|
}
|
|
|
|
static int
|
|
timespec_diff_to_usec(const struct timespec *a, const struct timespec *b)
|
|
{
|
|
time_t secs = a->tv_sec - b->tv_sec;
|
|
long nsec = a->tv_nsec - b->tv_nsec;
|
|
|
|
return secs * 1000000 + nsec / 1000;
|
|
}
|
|
|
|
static void
|
|
feedback_presented(void *data,
|
|
struct wp_presentation_feedback *presentation_feedback,
|
|
uint32_t tv_sec_hi,
|
|
uint32_t tv_sec_lo,
|
|
uint32_t tv_nsec,
|
|
uint32_t refresh_nsec,
|
|
uint32_t seq_hi,
|
|
uint32_t seq_lo,
|
|
uint32_t flags)
|
|
{
|
|
struct feedback *feedback = data;
|
|
struct window *window = feedback->window;
|
|
struct feedback *prev_feedback = window->received_feedback;
|
|
uint64_t seq = ((uint64_t)seq_hi << 32) + seq_lo;
|
|
const struct timespec *prevpresent;
|
|
uint32_t commit, present;
|
|
uint32_t f2c, c2p, f2p;
|
|
int p2p, t2p;
|
|
char flagstr[10];
|
|
|
|
timespec_from_proto(&feedback->present, tv_sec_hi, tv_sec_lo, tv_nsec);
|
|
commit = timespec_to_ms(&feedback->commit);
|
|
present = timespec_to_ms(&feedback->present);
|
|
|
|
if (prev_feedback)
|
|
prevpresent = &prev_feedback->present;
|
|
else
|
|
prevpresent = &feedback->present;
|
|
|
|
f2c = commit - feedback->frame_stamp;
|
|
c2p = present - commit;
|
|
f2p = present - feedback->frame_stamp;
|
|
p2p = timespec_diff_to_usec(&feedback->present, prevpresent);
|
|
t2p = timespec_diff_to_usec(&feedback->present, &feedback->target);
|
|
|
|
switch (window->mode) {
|
|
case RUN_MODE_PRESENT:
|
|
printf("%6u: c2p %4u ms, p2p %5d us, t2p %6d us, [%s] "
|
|
"seq %" PRIu64 "\n", feedback->frame_no, c2p,
|
|
p2p, t2p,
|
|
pflags_to_str(flags, flagstr, sizeof(flagstr)), seq);
|
|
break;
|
|
case RUN_MODE_FEEDBACK:
|
|
case RUN_MODE_FEEDBACK_IDLE:
|
|
printf("%6u: f2c %2u ms, c2p %2u ms, f2p %2u ms, p2p %5d us, "
|
|
"t2p %6d, [%s], seq %" PRIu64 "\n", feedback->frame_no,
|
|
f2c, c2p, f2p, p2p, t2p,
|
|
pflags_to_str(flags, flagstr, sizeof(flagstr)), seq);
|
|
}
|
|
|
|
if (window->received_feedback)
|
|
destroy_feedback(window->received_feedback);
|
|
window->received_feedback = feedback;
|
|
}
|
|
|
|
static void
|
|
feedback_discarded(void *data,
|
|
struct wp_presentation_feedback *presentation_feedback)
|
|
{
|
|
struct feedback *feedback = data;
|
|
|
|
printf("discarded %u\n", feedback->frame_no);
|
|
|
|
destroy_feedback(feedback);
|
|
}
|
|
|
|
static const struct wp_presentation_feedback_listener feedback_listener = {
|
|
feedback_sync_output,
|
|
feedback_presented,
|
|
feedback_discarded
|
|
};
|
|
|
|
static void
|
|
window_emulate_rendering(struct window *window)
|
|
{
|
|
struct timespec delay;
|
|
int ret;
|
|
|
|
if (window->commit_delay_msecs <= 0)
|
|
return;
|
|
|
|
delay.tv_sec = window->commit_delay_msecs / 1000;
|
|
delay.tv_nsec = (window->commit_delay_msecs % 1000) * 1000000;
|
|
|
|
ret = nanosleep(&delay, NULL);
|
|
if (ret)
|
|
printf("nanosleep failed: %s\n", strerror(errno));
|
|
}
|
|
|
|
static void
|
|
window_create_feedback(struct window *window, uint32_t frame_stamp)
|
|
{
|
|
static unsigned seq;
|
|
struct wp_presentation *pres = window->display->presentation;
|
|
struct feedback *feedback;
|
|
|
|
seq++;
|
|
|
|
if (!pres)
|
|
return;
|
|
|
|
feedback = zalloc(sizeof *feedback);
|
|
if (!feedback)
|
|
return;
|
|
|
|
feedback->window = window;
|
|
feedback->feedback = wp_presentation_feedback(pres, window->surface);
|
|
wp_presentation_feedback_add_listener(feedback->feedback,
|
|
&feedback_listener, feedback);
|
|
|
|
feedback->frame_no = seq;
|
|
|
|
clock_gettime(window->display->clk_id, &feedback->commit);
|
|
feedback->frame_stamp = frame_stamp;
|
|
feedback->target = feedback->commit;
|
|
|
|
wl_list_insert(&window->feedback_list, &feedback->link);
|
|
}
|
|
|
|
static void
|
|
window_commit_next(struct window *window)
|
|
{
|
|
struct buffer *buffer;
|
|
|
|
buffer = window_next_buffer(window);
|
|
assert(buffer);
|
|
|
|
wl_surface_attach(window->surface, buffer->buffer, 0, 0);
|
|
wl_surface_damage(window->surface, 0, 0, window->width, window->height);
|
|
wl_surface_commit(window->surface);
|
|
buffer->busy = 1;
|
|
}
|
|
|
|
static const struct wl_callback_listener frame_listener_mode_feedback;
|
|
|
|
static void
|
|
redraw_mode_feedback(void *data, struct wl_callback *callback, uint32_t time)
|
|
{
|
|
struct window *window = data;
|
|
|
|
if (callback && window->mode == RUN_MODE_FEEDBACK_IDLE)
|
|
sleep(1);
|
|
|
|
if (callback)
|
|
wl_callback_destroy(callback);
|
|
|
|
window_emulate_rendering(window);
|
|
|
|
window->callback = wl_surface_frame(window->surface);
|
|
wl_callback_add_listener(window->callback,
|
|
&frame_listener_mode_feedback, window);
|
|
|
|
window_create_feedback(window, time);
|
|
window_commit_next(window);
|
|
}
|
|
|
|
static const struct wl_callback_listener frame_listener_mode_feedback = {
|
|
redraw_mode_feedback
|
|
};
|
|
|
|
static const struct wp_presentation_feedback_listener feedkick_listener;
|
|
|
|
static void
|
|
window_feedkick(struct window *window)
|
|
{
|
|
struct wp_presentation *pres = window->display->presentation;
|
|
struct wp_presentation_feedback *fback;
|
|
|
|
fback = wp_presentation_feedback(pres, window->surface);
|
|
wp_presentation_feedback_add_listener(fback, &feedkick_listener,
|
|
window);
|
|
}
|
|
|
|
static void
|
|
feedkick_presented(void *data,
|
|
struct wp_presentation_feedback *presentation_feedback,
|
|
uint32_t tv_sec_hi,
|
|
uint32_t tv_sec_lo,
|
|
uint32_t tv_nsec,
|
|
uint32_t refresh_nsec,
|
|
uint32_t seq_hi,
|
|
uint32_t seq_lo,
|
|
uint32_t flags)
|
|
{
|
|
struct window *window = data;
|
|
|
|
wp_presentation_feedback_destroy(presentation_feedback);
|
|
window->refresh_nsec = refresh_nsec;
|
|
|
|
switch (window->mode) {
|
|
case RUN_MODE_PRESENT:
|
|
window_emulate_rendering(window);
|
|
window_create_feedback(window, 0);
|
|
window_feedkick(window);
|
|
window_commit_next(window);
|
|
break;
|
|
case RUN_MODE_FEEDBACK:
|
|
case RUN_MODE_FEEDBACK_IDLE:
|
|
assert(0 && "bad mode");
|
|
}
|
|
}
|
|
|
|
static void
|
|
feedkick_discarded(void *data,
|
|
struct wp_presentation_feedback *presentation_feedback)
|
|
{
|
|
struct window *window = data;
|
|
|
|
wp_presentation_feedback_destroy(presentation_feedback);
|
|
|
|
switch (window->mode) {
|
|
case RUN_MODE_PRESENT:
|
|
window_emulate_rendering(window);
|
|
window_create_feedback(window, 0);
|
|
window_feedkick(window);
|
|
window_commit_next(window);
|
|
break;
|
|
case RUN_MODE_FEEDBACK:
|
|
case RUN_MODE_FEEDBACK_IDLE:
|
|
assert(0 && "bad mode");
|
|
}
|
|
}
|
|
|
|
static const struct wp_presentation_feedback_listener feedkick_listener = {
|
|
feedback_sync_output,
|
|
feedkick_presented,
|
|
feedkick_discarded
|
|
};
|
|
|
|
static void
|
|
firstdraw_mode_burst(struct window *window)
|
|
{
|
|
window_emulate_rendering(window);
|
|
|
|
switch (window->mode) {
|
|
case RUN_MODE_PRESENT:
|
|
window_create_feedback(window, 0);
|
|
break;
|
|
case RUN_MODE_FEEDBACK:
|
|
case RUN_MODE_FEEDBACK_IDLE:
|
|
assert(0 && "bad mode");
|
|
}
|
|
|
|
window_feedkick(window);
|
|
window_commit_next(window);
|
|
}
|
|
|
|
static void
|
|
window_prerender(struct window *window)
|
|
{
|
|
int i;
|
|
int timefactor = 1000000 / window->num_buffers;
|
|
|
|
for (i = 0; i < window->num_buffers; i++) {
|
|
struct buffer *buf = &window->buffers[i];
|
|
|
|
if (buf->busy)
|
|
fprintf(stderr, "wl_buffer id %u) busy\n",
|
|
wl_proxy_get_id(
|
|
(struct wl_proxy *)buf->buffer));
|
|
|
|
paint_pixels(buf->shm_data, window->width, window->height,
|
|
i * timefactor);
|
|
}
|
|
}
|
|
|
|
static void
|
|
output_destroy(struct output *o)
|
|
{
|
|
wl_output_destroy(o->output);
|
|
wl_list_remove(&o->link);
|
|
free(o);
|
|
}
|
|
|
|
static void
|
|
display_add_output(struct display *d, uint32_t name, uint32_t version)
|
|
{
|
|
struct output *o;
|
|
|
|
o = zalloc(sizeof(*o));
|
|
assert(o);
|
|
|
|
o->output = wl_registry_bind(d->registry, name,
|
|
&wl_output_interface, 1);
|
|
o->name = name;
|
|
wl_list_insert(&d->output_list, &o->link);
|
|
}
|
|
|
|
static void
|
|
presentation_clock_id(void *data, struct wp_presentation *presentation,
|
|
uint32_t clk_id)
|
|
{
|
|
struct display *d = data;
|
|
|
|
d->clk_id = clk_id;
|
|
}
|
|
|
|
static const struct wp_presentation_listener presentation_listener = {
|
|
presentation_clock_id
|
|
};
|
|
|
|
static void
|
|
shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
|
|
{
|
|
struct display *d = data;
|
|
|
|
d->formats |= (1 << format);
|
|
}
|
|
|
|
static const struct wl_shm_listener shm_listener = {
|
|
shm_format
|
|
};
|
|
|
|
static void
|
|
registry_handle_global(void *data, struct wl_registry *registry,
|
|
uint32_t name, const char *interface, uint32_t version)
|
|
{
|
|
struct display *d = data;
|
|
|
|
if (strcmp(interface, "wl_compositor") == 0) {
|
|
d->compositor =
|
|
wl_registry_bind(registry,
|
|
name, &wl_compositor_interface, 1);
|
|
} else if (strcmp(interface, "wl_shell") == 0) {
|
|
d->shell = wl_registry_bind(registry,
|
|
name, &wl_shell_interface, 1);
|
|
} else if (strcmp(interface, "wl_shm") == 0) {
|
|
d->shm = wl_registry_bind(registry,
|
|
name, &wl_shm_interface, 1);
|
|
wl_shm_add_listener(d->shm, &shm_listener, d);
|
|
} else if (strcmp(interface, "wl_output") == 0) {
|
|
display_add_output(d, name, version);
|
|
} else if (strcmp(interface, wp_presentation_interface.name) == 0) {
|
|
d->presentation =
|
|
wl_registry_bind(registry,
|
|
name, &wp_presentation_interface, 1);
|
|
wp_presentation_add_listener(d->presentation,
|
|
&presentation_listener, d);
|
|
}
|
|
}
|
|
|
|
static void
|
|
registry_handle_global_remove(void *data, struct wl_registry *registry,
|
|
uint32_t name)
|
|
{
|
|
struct display *d = data;
|
|
struct output *output, *otmp;
|
|
|
|
wl_list_for_each_safe(output, otmp, &d->output_list, link) {
|
|
if (output->name != name)
|
|
continue;
|
|
|
|
output_destroy(output);
|
|
}
|
|
}
|
|
|
|
static const struct wl_registry_listener registry_listener = {
|
|
registry_handle_global,
|
|
registry_handle_global_remove
|
|
};
|
|
|
|
static struct display *
|
|
create_display(void)
|
|
{
|
|
struct display *display;
|
|
|
|
display = malloc(sizeof *display);
|
|
if (display == NULL) {
|
|
fprintf(stderr, "out of memory\n");
|
|
exit(1);
|
|
}
|
|
display->display = wl_display_connect(NULL);
|
|
assert(display->display);
|
|
|
|
display->formats = 0;
|
|
display->clk_id = -1;
|
|
wl_list_init(&display->output_list);
|
|
display->registry = wl_display_get_registry(display->display);
|
|
wl_registry_add_listener(display->registry,
|
|
®istry_listener, display);
|
|
wl_display_roundtrip(display->display);
|
|
if (display->shm == NULL) {
|
|
fprintf(stderr, "No wl_shm global\n");
|
|
exit(1);
|
|
}
|
|
|
|
wl_display_roundtrip(display->display);
|
|
|
|
if (!(display->formats & (1 << WL_SHM_FORMAT_XRGB8888))) {
|
|
fprintf(stderr, "WL_SHM_FORMAT_XRGB32 not available\n");
|
|
exit(1);
|
|
}
|
|
|
|
wl_display_get_fd(display->display);
|
|
|
|
return display;
|
|
}
|
|
|
|
static void
|
|
destroy_display(struct display *display)
|
|
{
|
|
while (!wl_list_empty(&display->output_list)) {
|
|
struct output *o;
|
|
|
|
o = wl_container_of(display->output_list.next, o, link);
|
|
output_destroy(o);
|
|
}
|
|
|
|
if (display->shm)
|
|
wl_shm_destroy(display->shm);
|
|
|
|
if (display->shell)
|
|
wl_shell_destroy(display->shell);
|
|
|
|
if (display->compositor)
|
|
wl_compositor_destroy(display->compositor);
|
|
|
|
wl_registry_destroy(display->registry);
|
|
wl_display_flush(display->display);
|
|
wl_display_disconnect(display->display);
|
|
free(display);
|
|
}
|
|
|
|
static int running = 1;
|
|
|
|
static void
|
|
signal_int(int signum)
|
|
{
|
|
running = 0;
|
|
}
|
|
|
|
static void
|
|
usage(const char *prog, int exit_code)
|
|
{
|
|
fprintf(stderr, "Usage: %s [mode] [options]\n"
|
|
"where 'mode' is one of\n"
|
|
" -f\t\trun in feedback mode (default)\n"
|
|
" -i\t\trun in feedback-idle mode; sleep 1s between frames\n"
|
|
" -p\t\trun in low-latency presentation mode\n"
|
|
"and 'options' may include\n"
|
|
" -d msecs\temulate the time used for rendering by a delay \n"
|
|
"\t\tof the given milliseconds before commit\n\n",
|
|
prog);
|
|
|
|
fprintf(stderr, "Printed timing statistics, depending on mode:\n"
|
|
" commit sequence number\n"
|
|
" f2c: time from frame callback timestamp to commit\n"
|
|
" c2p: time from commit to presentation\n"
|
|
" f2p: time from frame callback timestamp to presentation\n"
|
|
" p2p: time from previous presentation to this one\n"
|
|
" t2p: time from target timestamp to presentation\n"
|
|
" seq: MSC\n");
|
|
|
|
|
|
exit(exit_code);
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
struct sigaction sigint;
|
|
struct display *display;
|
|
struct window *window;
|
|
int ret = 0;
|
|
enum run_mode mode = RUN_MODE_FEEDBACK;
|
|
int i;
|
|
int commit_delay_msecs = 0;
|
|
|
|
for (i = 1; i < argc; i++) {
|
|
if (strcmp("-f", argv[i]) == 0)
|
|
mode = RUN_MODE_FEEDBACK;
|
|
else if (strcmp("-i", argv[i]) == 0)
|
|
mode = RUN_MODE_FEEDBACK_IDLE;
|
|
else if (strcmp("-p", argv[i]) == 0)
|
|
mode = RUN_MODE_PRESENT;
|
|
else if ((strcmp("-d", argv[i]) == 0) && (i + 1 < argc)) {
|
|
i++;
|
|
commit_delay_msecs = atoi(argv[i]);
|
|
}
|
|
else
|
|
usage(argv[0], EXIT_FAILURE);
|
|
}
|
|
|
|
display = create_display();
|
|
window = create_window(display, 250, 250, mode, commit_delay_msecs);
|
|
if (!window)
|
|
return 1;
|
|
|
|
sigint.sa_handler = signal_int;
|
|
sigemptyset(&sigint.sa_mask);
|
|
sigint.sa_flags = SA_RESETHAND;
|
|
sigaction(SIGINT, &sigint, NULL);
|
|
|
|
window_prerender(window);
|
|
|
|
switch (mode) {
|
|
case RUN_MODE_FEEDBACK:
|
|
case RUN_MODE_FEEDBACK_IDLE:
|
|
redraw_mode_feedback(window, NULL, 0);
|
|
break;
|
|
case RUN_MODE_PRESENT:
|
|
firstdraw_mode_burst(window);
|
|
break;
|
|
}
|
|
|
|
while (running && ret != -1)
|
|
ret = wl_display_dispatch(display->display);
|
|
|
|
fprintf(stderr, "presentation-shm exiting\n");
|
|
destroy_window(window);
|
|
destroy_display(display);
|
|
|
|
return 0;
|
|
}
|