From 99b705bb1729fe7dacd124851d4fc931547e1dc1 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 19 Nov 2012 15:29:09 +0200 Subject: [PATCH] simple-shm: honour wl_buffer.release Change simple-shm to properly process the wl_buffer.release event, and not reuse a buffer until it is released by the server, as specified in the protocol. In case the server has not released the buffer, but signals that it has been shown (frame callback), allocate a second buffer. Simple-shm will now automatically do double-buffering if needed. Signed-off-by: Pekka Paalanen --- clients/simple-shm.c | 113 +++++++++++++++++++++++++++++++++---------- 1 file changed, 87 insertions(+), 26 deletions(-) diff --git a/clients/simple-shm.c b/clients/simple-shm.c index a09ec910..5bc26bf7 100644 --- a/clients/simple-shm.c +++ b/clients/simple-shm.c @@ -42,22 +42,39 @@ struct display { uint32_t formats; }; +struct buffer { + struct wl_buffer *buffer; + void *shm_data; + int busy; +}; + struct window { struct display *display; int width, height; struct wl_surface *surface; struct wl_shell_surface *shell_surface; - struct wl_buffer *buffer; - void *shm_data; + struct buffer buffers[2]; + struct buffer *prev_buffer; struct wl_callback *callback; }; -static struct wl_buffer * -create_shm_buffer(struct display *display, - int width, int height, uint32_t format, void **data_out) +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_buffer(struct display *display, struct buffer *buffer, + int width, int height, uint32_t format) { struct wl_shm_pool *pool; - struct wl_buffer *buffer; int fd, size, stride; void *data; @@ -68,25 +85,27 @@ create_shm_buffer(struct display *display, if (fd < 0) { fprintf(stderr, "creating a buffer file for %d B failed: %m\n", size); - return NULL; + return -1; } data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (data == MAP_FAILED) { fprintf(stderr, "mmap failed: %m\n"); close(fd); - return NULL; + return -1; } pool = wl_shm_create_pool(display->shm, fd, size); - buffer = wl_shm_pool_create_buffer(pool, 0, - width, height, stride, format); + buffer->buffer = wl_shm_pool_create_buffer(pool, 0, + width, height, + stride, format); + wl_buffer_add_listener(buffer->buffer, &buffer_listener, buffer); wl_shm_pool_destroy(pool); close(fd); - *data_out = data; + buffer->shm_data = data; - return buffer; + return 0; } static void @@ -117,18 +136,10 @@ static struct window * create_window(struct display *display, int width, int height) { struct window *window; - - window = malloc(sizeof *window); - - window->buffer = create_shm_buffer(display, - width, height, - WL_SHM_FORMAT_XRGB8888, - &window->shm_data); - if (!window->buffer) { - free(window); + window = calloc(1, sizeof *window); + if (!window) return NULL; - } window->callback = NULL; window->display = display; @@ -155,12 +166,45 @@ destroy_window(struct window *window) if (window->callback) wl_callback_destroy(window->callback); - wl_buffer_destroy(window->buffer); + if (window->buffers[0].buffer) + wl_buffer_destroy(window->buffers[0].buffer); + if (window->buffers[1].buffer) + wl_buffer_destroy(window->buffers[1].buffer); + wl_shell_surface_destroy(window->shell_surface); wl_surface_destroy(window->surface); free(window); } +static struct buffer * +window_next_buffer(struct window *window) +{ + struct buffer *buffer; + int ret = 0; + + if (!window->buffers[0].busy) + buffer = &window->buffers[0]; + else if (!window->buffers[1].busy) + buffer = &window->buffers[1]; + else + return NULL; + + if (!buffer->buffer) { + ret = create_shm_buffer(window->display, buffer, + window->width, window->height, + WL_SHM_FORMAT_XRGB8888); + + if (ret < 0) + return NULL; + + /* paint the padding */ + memset(buffer->shm_data, 0xff, + window->width * window->height * 4); + } + + return buffer; +} + static void paint_pixels(void *image, int padding, int width, int height, uint32_t time) { @@ -213,8 +257,23 @@ static void redraw(void *data, struct wl_callback *callback, uint32_t time) { struct window *window = data; + struct buffer *buffer; + + buffer = window_next_buffer(window); + if (!buffer) { + fprintf(stderr, + !callback ? "Failed to create the first buffer.\n" : + "Both buffers busy at redraw(). Server bug?\n"); + abort(); + } + + paint_pixels(buffer->shm_data, 20, window->width, window->height, time); + + if (window->prev_buffer != buffer) { + wl_surface_attach(window->surface, buffer->buffer, 0, 0); + window->prev_buffer = buffer; + } - paint_pixels(window->shm_data, 20, window->width, window->height, time); wl_surface_damage(window->surface, 20, 20, window->width - 40, window->height - 40); @@ -224,6 +283,7 @@ redraw(void *data, struct wl_callback *callback, uint32_t time) window->callback = wl_surface_frame(window->surface); wl_callback_add_listener(window->callback, &frame_listener, window); wl_surface_commit(window->surface); + buffer->busy = 1; } static const struct wl_callback_listener frame_listener = { @@ -340,8 +400,9 @@ main(int argc, char **argv) sigint.sa_flags = SA_RESETHAND; sigaction(SIGINT, &sigint, NULL); - memset(window->shm_data, 0xff, window->width * window->height * 4); - wl_surface_attach(window->surface, window->buffer, 0, 0); + /* Initialise damage to full surface, so the padding gets painted */ + wl_surface_damage(window->surface, 0, 0, + window->width, window->height); redraw(window, NULL, 0);