diff --git a/protocol/wayland.xml b/protocol/wayland.xml index 5118b5ba..9e95d343 100644 --- a/protocol/wayland.xml +++ b/protocol/wayland.xml @@ -135,6 +135,46 @@ + + + The wl_shm_pool object encapsulates a piece of memory shared + between the compsitor and client. Through the wl_shm_pool + object, the client can allocate shared memory wl_buffer objects. + The objects will share the same underlying mapped memory. + Reusing the mapped memory avoids the setup/teardown overhead and + is useful when interactively resizing a surface or for many + small buffers. + + + + + Create a wl_buffer from the pool. The buffer is created a + offset bytes into the pool and has width and height as + specified. The stride arguments specifies the number of bytes + from beginning of one row to the beginning of the next. The + format is the pixel format of the buffer and must be one of + those advertised through the wl_shm.format event. + + A buffer will keep a reference to the pool it was created from + so it is valid to destroy the pool immediatedly after creating + a buffer from it. + + + + + + + + + + + + + Destroy the pool. + + + + Support for shared memory buffers. @@ -151,22 +191,17 @@ - - - Transfer a shm buffer to the server. The allocated buffer - would include at least stride * height bytes starting at the - beginning of fd. The file descriptor is transferred over the - socket using AF_UNIX magical features. width, height, stride - and format describe the respective properties of the pixel - data contained in the buffer. + + + This creates wl_shm_pool object, which can be used to create + shared memory based wl_buffer objects. The server will mmap + size bytes of the passed fd, to use as backing memory for then + pool. - + - - - - + diff --git a/src/wayland-shm.c b/src/wayland-shm.c index a1ad0f73..8f026d5c 100644 --- a/src/wayland-shm.c +++ b/src/wayland-shm.c @@ -33,21 +33,40 @@ #include "wayland-server.h" +struct wl_shm_pool { + struct wl_resource resource; + int refcount; + char *data; + int size; +}; + struct wl_shm_buffer { struct wl_buffer buffer; int32_t stride; uint32_t format; void *data; + struct wl_shm_pool *pool; }; +static void +shm_pool_unref(struct wl_shm_pool *pool) +{ + pool->refcount--; + if (pool->refcount) + return; + + munmap(pool->data, pool->size); + free(pool); +} + static void destroy_buffer(struct wl_resource *resource) { struct wl_shm_buffer *buffer = container_of(resource, struct wl_shm_buffer, buffer.resource); - munmap(buffer->data, buffer->stride * buffer->buffer.height); - + if (buffer->pool) + shm_pool_unref(buffer->pool); free(buffer); } @@ -61,43 +80,14 @@ static const struct wl_buffer_interface shm_buffer_interface = { shm_buffer_destroy }; -static struct wl_shm_buffer * -wl_shm_buffer_init(struct wl_client *client, uint32_t id, - int32_t width, int32_t height, - int32_t stride, uint32_t format, void *data) -{ - struct wl_shm_buffer *buffer; - - buffer = calloc(1, sizeof *buffer); - if (buffer == NULL) - return NULL; - - buffer->buffer.width = width; - buffer->buffer.height = height; - buffer->format = format; - buffer->stride = stride; - buffer->data = data; - - buffer->buffer.resource.object.id = id; - buffer->buffer.resource.object.interface = &wl_buffer_interface; - buffer->buffer.resource.object.implementation = (void (**)(void)) - &shm_buffer_interface; - - buffer->buffer.resource.data = buffer; - buffer->buffer.resource.client = client; - buffer->buffer.resource.destroy = destroy_buffer; - - return buffer; -} - static void -shm_create_buffer(struct wl_client *client, struct wl_resource *resource, - uint32_t id, int fd, int32_t width, int32_t height, - int32_t stride, uint32_t format) +shm_pool_create_buffer(struct wl_client *client, struct wl_resource *resource, + uint32_t id, int32_t offset, + int32_t width, int32_t height, + int32_t stride, uint32_t format) { + struct wl_shm_pool *pool = resource->data; struct wl_shm_buffer *buffer; - void *data; - switch (format) { case WL_SHM_FORMAT_ARGB8888: @@ -107,43 +97,111 @@ shm_create_buffer(struct wl_client *client, struct wl_resource *resource, wl_resource_post_error(resource, WL_SHM_ERROR_INVALID_FORMAT, "invalid format"); - close(fd); return; } - if (width < 0 || height < 0 || stride < width) { + if (offset < 0 || width <= 0 || height <= 0 || stride < width || + INT32_MAX / stride <= height || + offset > pool->size - stride * height) { wl_resource_post_error(resource, WL_SHM_ERROR_INVALID_STRIDE, "invalid width, height or stride (%dx%d, %u)", width, height, stride); + return; + } + + buffer = malloc(sizeof *buffer); + if (buffer == NULL) { + wl_resource_post_no_memory(resource); + return; + } + + buffer->buffer.width = width; + buffer->buffer.height = height; + buffer->format = format; + buffer->stride = stride; + buffer->data = pool->data + offset; + buffer->pool = pool; + pool->refcount++; + + buffer->buffer.resource.object.id = id; + buffer->buffer.resource.object.interface = &wl_buffer_interface; + buffer->buffer.resource.object.implementation = (void (**)(void)) + &shm_buffer_interface; + + buffer->buffer.resource.data = buffer; + buffer->buffer.resource.client = resource->client; + buffer->buffer.resource.destroy = destroy_buffer; + + wl_client_add_resource(client, &buffer->buffer.resource); +} + +static void +destroy_pool(struct wl_resource *resource) +{ + struct wl_shm_pool *pool = resource->data; + + shm_pool_unref(pool); +} + +static void +shm_pool_destroy(struct wl_client *client, struct wl_resource *resource) +{ + wl_resource_destroy(resource, 0); +} + +struct wl_shm_pool_interface shm_pool_interface = { + shm_pool_create_buffer, + shm_pool_destroy +}; + +static void +shm_create_pool(struct wl_client *client, struct wl_resource *resource, + uint32_t id, int fd, int32_t size) +{ + struct wl_shm_pool *pool; + + pool = malloc(sizeof *pool); + if (pool == NULL) { + wl_resource_post_no_memory(resource); close(fd); return; } - data = mmap(NULL, stride * height, - PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (size <= 0) { + wl_resource_post_error(resource, + WL_SHM_ERROR_INVALID_STRIDE, + "invalid size (%d)", size); + close(fd); + return; + } + pool->refcount = 1; + pool->size = size; + pool->data = mmap(NULL, size, + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); close(fd); - if (data == MAP_FAILED) { + if (pool->data == MAP_FAILED) { wl_resource_post_error(resource, WL_SHM_ERROR_INVALID_FD, "failed mmap fd %d", fd); return; } - buffer = wl_shm_buffer_init(client, id, - width, height, stride, format, data); - if (buffer == NULL) { - munmap(data, stride * height); - wl_resource_post_no_memory(resource); - return; - } + pool->resource.object.id = id; + pool->resource.object.interface = &wl_shm_pool_interface; + pool->resource.object.implementation = + (void (**)(void)) &shm_pool_interface; - wl_client_add_resource(client, &buffer->buffer.resource); + pool->resource.data = pool; + pool->resource.client = client; + pool->resource.destroy = destroy_pool; + + wl_client_add_resource(client, &pool->resource); } static const struct wl_shm_interface shm_interface = { - shm_create_buffer + shm_create_pool }; static void