diff --git a/meson.build b/meson.build index 7ad5ac06..ba0f9985 100644 --- a/meson.build +++ b/meson.build @@ -30,6 +30,7 @@ compiler_flags = [ '-Wstrict-prototypes', '-Wmissing-prototypes', '-fvisibility=hidden', + '-D_FILE_OFFSET_BITS=64', ] cc = meson.get_compiler('c') diff --git a/protocol/generate-shm-formats.py b/protocol/generate-shm-formats.py index c56642e1..763076f5 100755 --- a/protocol/generate-shm-formats.py +++ b/protocol/generate-shm-formats.py @@ -105,8 +105,10 @@ formats = {} for ident, val in idents.items(): formats[drm_format_to_wl(ident)] = val.lower() # Special case for ARGB8888 and XRGB8888 -formats["argb8888"] = "0" -formats["xrgb8888"] = "1" +for (i, j) in enumerate(("argb8888", "xrgb8888")): + formats[j + "_new"] = formats[j] + descriptions[j + "_new"] = descriptions[j] + formats[j] = str(i) print("Loaded {} formats from drm_fourcc.h".format(len(formats)), file=sys.stderr) diff --git a/protocol/wayland.xml b/protocol/wayland.xml index de6756b4..0a85e659 100644 --- a/protocol/wayland.xml +++ b/protocol/wayland.xml @@ -212,7 +212,7 @@ - + The wl_shm_pool object encapsulates a piece of memory shared between the compositor and client. Through the wl_shm_pool @@ -233,9 +233,16 @@ 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 + A buffer will keep a reference to the pool it was created from, so it is valid to destroy the pool immediately after creating a buffer from it. + + The offset must not be negative. If it is negative, an + invalid_stride error is raised. This limits offsets to a + maximum of 2^31 - 1 bytes. + + The version of the returned buffer is 1, regardless of the + version of the pool from which it was created. @@ -267,6 +274,11 @@ file descriptor passed at creation time. It is the client's responsibility to ensure that the file is at least as big as the new pool size. + + If the pool was created using version 3 or later of the wl_shm + interface, this request cannot be called once any buffers have + been created. Attempting to do so will result in an + already_mapped error. @@ -292,27 +304,35 @@ + This describes the memory layout of an individual pixel. - All renderers should support argb8888 and xrgb8888 but any other + All compositors must support argb8888 and xrgb8888. Compositors + supporting wl_shm v3 must support argb8888_new and xrgb8888_new + as well, even with version 1 wl_shm_pool objects. All other formats are optional and may not be supported by the particular renderer in use. The drm format codes match the macros defined in drm_fourcc.h, except argb8888 and xrgb8888. The formats actually supported by the compositor - will be reported by the format event. + will be reported by the format event, and are not limited to those in + this enum. Starting with version 2, argb8888_new and xrgb8888_new values + are provided, which match the corresponding values in drm_fourcc.h. - For all wl_shm formats and unless specified in another protocol - extension, pre-multiplied alpha is used for pixel values. + All wl_shm formats use pre-multiplied alpha for pixel values, unless + another protocol extension states otherwise. + + @@ -462,7 +482,12 @@ The pool can be used to create shared memory based buffer objects. The server will mmap size bytes of the passed file - descriptor, to use as backing memory for the pool. + descriptor, to use as backing memory for the pool. If it is + unable to do so, an invalid_fd error is raised. + + The size must be greater than zero. Sizes less or equal to zero + raise an invalid_stride error. This prevents the creation of + pools larger than 2^31 - 1 bytes. @@ -488,6 +513,35 @@ Objects created via this interface remain unaffected. + + + + + Create a new wl_shm_pool object. + + The pool can be used to create shared memory based buffer + objects. The server will mmap size bytes of the passed file + descriptor at the provided offset to use as backing memory for + the pool. If it is unable to do so, an invalid_fd error is + raised. + + This request provides a strict superset of the functionality + provided by create_pool. In particular, calling this request + with offset_lo and offset_hi set to 0 is equivalent to calling + create_pool. + + If the size is zero or greater than 2^31 - 1, an invalid_stride + error is raised. Compositors are not allowed to impose + arbitrary limits on the offset, beyond those imposed by the + operating system. In particular, the offset may be negative + when cast to an off_t, and may be larger than 2^32 - 1. + + + + + + + diff --git a/src/wayland-shm.c b/src/wayland-shm.c index 3ac4add2..5f41d4f8 100644 --- a/src/wayland-shm.c +++ b/src/wayland-shm.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -67,8 +68,8 @@ struct wl_shm_pool { #ifndef MREMAP_MAYMOVE /* The following three fields are needed for mremap() emulation. */ int mmap_fd; - int mmap_flags; - int mmap_prot; +#else + bool mapped_yet; #endif bool sigbus_is_impossible; }; @@ -91,7 +92,7 @@ struct wl_shm_buffer { int32_t width, height; int32_t stride; uint32_t format; - int offset; + int32_t offset; struct wl_shm_pool *pool; }; @@ -111,7 +112,7 @@ shm_pool_grow_mapping(struct wl_shm_pool *pool) #else data = wl_os_mremap_maymove(pool->mmap_fd, pool->data, &pool->size, pool->new_size, pool->mmap_prot, - pool->mmap_flags); + MAP_SHARED); if (pool->size != 0 && pool->resource != NULL) { wl_resource_post_error(pool->resource, WL_SHM_ERROR_INVALID_FD, @@ -164,7 +165,8 @@ shm_pool_unref(struct wl_shm_pool *pool, bool external) munmap(pool->data, pool->size); #ifndef MREMAP_MAYMOVE - close(pool->mmap_fd); + if (pool->mmap_fd != -1) + close(pool->mmap_fd); #endif free(pool); } @@ -214,22 +216,15 @@ static const struct wl_buffer_interface shm_buffer_interface = { }; static bool -format_is_supported(struct wl_client *client, uint32_t format) +extra_format_is_supported(struct wl_client *client, uint32_t format) { struct wl_display *display = wl_client_get_display(client); - struct wl_array *formats; + struct wl_array *formats = wl_display_get_additional_shm_formats(display); uint32_t *p; - switch (format) { - case WL_SHM_FORMAT_ARGB8888: - case WL_SHM_FORMAT_XRGB8888: - return true; - default: - formats = wl_display_get_additional_shm_formats(display); - wl_array_for_each(p, formats) - if (*p == format) - return true; - } + wl_array_for_each(p, formats) + if (*p == format) + return true; return false; } @@ -254,7 +249,18 @@ shm_pool_create_buffer(struct wl_client *client, struct wl_resource *resource, struct wl_shm_pool *pool = wl_resource_get_user_data(resource); struct wl_shm_buffer *buffer; - if (!format_is_supported(client, format)) { + switch (format) { + case WL_SHM_FORMAT_ARGB8888: + case WL_SHM_FORMAT_ARGB8888_NEW: + format = WL_SHM_FORMAT_ARGB8888; + break; + case WL_SHM_FORMAT_XRGB8888: + case WL_SHM_FORMAT_XRGB8888_NEW: + format = WL_SHM_FORMAT_XRGB8888; + break; + default: + if (extra_format_is_supported(client, format)) + break; wl_resource_post_error(resource, WL_SHM_ERROR_INVALID_FORMAT, "invalid format 0x%x", format); @@ -266,11 +272,21 @@ shm_pool_create_buffer(struct wl_client *client, struct wl_resource *resource, 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); + "invalid offset, width, height or stride " + "(%" PRIi32 ", %" PRIi32 "x%" PRIi32 ", %" PRIu32 ")", + offset, width, height, stride); return; } + if (wl_resource_get_version(resource) > 2) { +#ifndef MREMAP_MAYMOVE + close(pool->mmap_fd); + pool->mmap_fd = -1; +#else + pool->mapped_yet = true; +#endif + } + buffer = zalloc(sizeof *buffer); if (buffer == NULL) { wl_client_post_no_memory(client); @@ -327,6 +343,7 @@ shm_pool_resize(struct wl_client *client, struct wl_resource *resource, int32_t size) { struct wl_shm_pool *pool = wl_resource_get_user_data(resource); + uint32_t version = wl_resource_get_version(resource); if (size < pool->size) { wl_resource_post_error(resource, @@ -335,6 +352,23 @@ shm_pool_resize(struct wl_client *client, struct wl_resource *resource, return; } + assert(pool->internal_refcount > 0); + assert(pool->external_refcount >= 0); + if (version > 2 && +#ifndef MREMAP_MAYMOVE + pool->mmap_fd == -1 +#else + pool->mapped_yet +#endif + ) { + wl_resource_post_error( + resource, + WL_SHM_ERROR_ALREADY_MAPPED, + "Cannot resize a pool of version greater than 1 " + "once it has been mapped."); + return; + } + pool->new_size = size; /* If the compositor has taken references on this pool it @@ -353,23 +387,28 @@ static const struct wl_shm_pool_interface shm_pool_interface = { }; static void -shm_create_pool(struct wl_client *client, struct wl_resource *resource, - uint32_t id, int fd, int32_t size) +shm_create_pool2(struct wl_client *client, struct wl_resource *resource, + uint32_t id, int fd, uint32_t size, uint32_t offset_low, + uint32_t offset_high) { struct wl_shm_pool *pool; struct stat statbuf; + uint64_t offset; + uint32_t version; int seals; - int prot; - int flags; uint32_t version; - if (size <= 0) { + if (size <= 0 || size > (uint32_t) INT32_MAX) { wl_resource_post_error(resource, WL_SHM_ERROR_INVALID_STRIDE, - "invalid size (%d)", size); + "invalid size (%" PRIu32 ")", size); goto err_close; } + static_assert((uint64_t)(off_t) UINT64_MAX == UINT64_MAX, + "libwayland-server must be built with 64-bit (or more) off_t"); + version = wl_resource_get_version(resource); + offset = (uint64_t) offset_high << 32 | (uint64_t) offset_low; pool = zalloc(sizeof *pool); if (pool == NULL) { wl_client_post_no_memory(client); @@ -393,9 +432,7 @@ shm_create_pool(struct wl_client *client, struct wl_resource *resource, pool->external_refcount = 0; pool->size = size; pool->new_size = size; - prot = PROT_READ | PROT_WRITE; - flags = MAP_SHARED; - pool->data = mmap(NULL, size, prot, flags, fd, 0); + pool->data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, (off_t) offset); if (pool->data == MAP_FAILED) { wl_resource_post_error(resource, WL_SHM_ERROR_INVALID_FD, "failed mmap fd %d: %s", fd, @@ -405,8 +442,6 @@ shm_create_pool(struct wl_client *client, struct wl_resource *resource, #ifndef MREMAP_MAYMOVE /* We may need to keep the fd, prot and flags to emulate mremap(). */ pool->mmap_fd = fd; - pool->mmap_prot = prot; - pool->mmap_flags = flags; #else close(fd); #endif @@ -439,9 +474,17 @@ shm_release(struct wl_client *client, struct wl_resource *resource) wl_resource_destroy(resource); } +static void +shm_create_pool(struct wl_client *client, struct wl_resource *resource, + uint32_t id, int fd, int32_t size) +{ + shm_create_pool2(client, resource, id, fd, (uint32_t) size, 0, 0); +} + static const struct wl_shm_interface shm_interface = { - shm_create_pool, - shm_release, + .create_pool = shm_create_pool, + .release = shm_release, + .create_pool2 = shm_create_pool2, }; static void @@ -463,6 +506,8 @@ bind_shm(struct wl_client *client, wl_shm_send_format(resource, WL_SHM_FORMAT_ARGB8888); wl_shm_send_format(resource, WL_SHM_FORMAT_XRGB8888); + wl_shm_send_format(resource, WL_SHM_FORMAT_ARGB8888_NEW); + wl_shm_send_format(resource, WL_SHM_FORMAT_XRGB8888_NEW); additional_formats = wl_display_get_additional_shm_formats(display); wl_array_for_each(p, additional_formats)