diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index eb6fb0b4..eeed7899 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -43,7 +43,7 @@ include:
# API changes. If you need new features from ci-templates you must bump
# this to the current SHA you require from the ci-templates repo, however
# be aware that you may need to account for API changes when doing so.
- ref: f210ea84576f756816da37908771edcee14ef7e6
+ ref: 48c2c583a865bd59be21e8938df247faf460099c
file:
- '/templates/debian.yml'
- '/templates/freebsd.yml'
@@ -306,11 +306,11 @@ armv7-release-debian-build:
.os-freebsd:
variables:
BUILD_OS: freebsd
- FDO_DISTRIBUTION_VERSION: "13.2"
+ FDO_DISTRIBUTION_VERSION: "14.3"
FDO_DISTRIBUTION_PACKAGES: 'libxslt meson ninja pkgconf expat libffi libepoll-shim libxml2'
# bump this tag every time you change something which requires rebuilding the
# base image
- FDO_DISTRIBUTION_TAG: "2023-08-02.0"
+ FDO_DISTRIBUTION_TAG: "2025-07-20.0"
# Don't build documentation since installing the required tools massively
# increases the VM image (and therefore container) size.
MESON_ARGS: "--fatal-meson-warnings -Dwerror=true -Ddocumentation=false"
diff --git a/cursor/wayland-cursor.c b/cursor/wayland-cursor.c
index 89ecc9a1..2e21db73 100644
--- a/cursor/wayland-cursor.c
+++ b/cursor/wayland-cursor.c
@@ -398,7 +398,7 @@ wl_cursor_theme_load(const char *name, int size, struct wl_shm *shm)
return NULL;
if (size < 0 || (size > 0 && INT_MAX / size / 4 < size))
- return NULL;
+ goto err;
if (!name)
name = "default";
@@ -409,7 +409,7 @@ wl_cursor_theme_load(const char *name, int size, struct wl_shm *shm)
theme->pool = shm_pool_create(shm, size * size * 4);
if (!theme->pool)
- goto out_error_pool;
+ goto err;
xcursor_load_theme(name, size, load_callback, theme);
@@ -421,7 +421,7 @@ wl_cursor_theme_load(const char *name, int size, struct wl_shm *shm)
return theme;
-out_error_pool:
+err:
free(theme);
return NULL;
}
diff --git a/doc/publican/sources/Color.xml b/doc/publican/sources/Color.xml
new file mode 100644
index 00000000..ceee779e
--- /dev/null
+++ b/doc/publican/sources/Color.xml
@@ -0,0 +1,139 @@
+
+
+%BOOK_ENTITIES;
+]>
+
+
+ Color management
+
+
+ Overview
+
+
+ Color management in Wayland considers only displays. All pictures in
+ Wayland are always display-referred, meaning that the pixel values are
+ intended as-is for some specific display where they would produce the
+ light emissions (stimuli) the picture's
+ author desired. Wayland does not support displaying "raw" camera or
+ scanner images as they are not display-referred, nor are they even
+ pictures without complex and subjective processing.
+
+
+ Stimuli — the picture itself — are only half of the picture reproduction.
+ The other half is the environment where a display is viewed. A striking
+ example is comparing a brightly lit office to a dark movie theater, the
+ stimuli required to produce a good reading of the picture is greatly
+ different. Therefore display-referred does not include only the display
+ but the viewing environment as well.
+
+
+ Window systems have been very well capable of operating without any
+ explicit consideration to color management. This is because there used to
+ be the implicit assumption of the standard display, the sRGB display,
+ which all computer monitors implemented, more or less. The viewing
+ environment was and still is accounted by adjusting the display and/or the
+ room to produce a workable experience. Pictures are authored on a computer
+ system by drawing, painting and adjusting the picture until it looks right
+ on the author's monitor. This implicitly builds the standard display and
+ environment assumption into the picture data. Deviations from the sRGB
+ specification were minor enough that they often did not matter if not in a
+ professional context like the printing industry. Displaying video material
+ required some more attention to the details, because video and television
+ standards differ enough from the sRGB display. What really made explicit
+ color management a hard requirement for entertainment is the coming of
+ wide color gamut (WCG) and high dynamic range (HDR) materials and
+ displays.
+
+
+ The color management design in Wayland follows the general Wayland design
+ principles: compositors tell clients what would be the optimal thing to
+ do, clients tell the compositors what kind of pictures they are actually
+ producing, and then compositors display those pictures the best they can.
+
+
+
+
+ Protocol Interfaces
+
+
+ Color management interfaces in Wayland and divided into two protocols:
+ color-management
+ and
+ color-representation.
+ They are designed to work together, but they can also be used
+ independently when the other one is not needed.
+
+
+
+ Color-management
+
+
+ Color management protocol has two main purposes. First, it puts the
+ responsibility of color management on the compositor. This means that
+ clients do not necessarily need to care about color management at all,
+ and can display just fine by using the traditional standard display
+ assumption even when the actual display is wildly different. Clients
+ can also choose to target some other assumed display and let the
+ compositor handle it, or they can explicitly render for the actual
+ display at hand. Second, when the window system has multiple different
+ monitors, and a wl_surface happens to span more than one monitor, the
+ compositor can display the surface content correctly on all spanned
+ monitors simultaneously, as much as physically possible.
+
+
+ Color-management protocol concentrates on colorimetry: when you have a
+ pixel with RGB values, what stimulus do those values represent. The
+ stimulus definition follows the CIE 1931 two-degree observer model. Some
+ core concepts here are color primaries, white point, transfer function,
+ and dynamic range. The viewing environment is represented in an
+ extremely simplified way as the reference white luminance. The
+ connection between pixel RGB values and stimulus plus viewing
+ environment is recorded in an image description
+ object. Clients can create image description objects and tag
+ wl_surfaces with them, to indicate what kind of surface
+ content there will be. Clients can also ask what image description the
+ compositor would prefer to have on the wl_surface, and that
+ preference can change over time, e.g. when the wl_surface
+ is moved from one
+ wl_output to another. Following the compositor's preference
+ may provide advantages in image quality and power consumption.
+
+
+ Image description objects can come in two flavors: parametric and
+ ICC-based. The above was written with parametric image descriptions in
+ mind, and they have first-class support for HDR. ICC-based image
+ descriptions are wrapping an ICC profile and have no other data. ICC
+ profiles are the standard tool for standard dynamic range (SDR) display
+ color management. This means the capabilities between the two flavors
+ differ, and one cannot always be replaced by the other. Compositor
+ support for each flavor is optional.
+
+
+
+
+ Color-representation
+
+
+ Color-representation protocol deals with (potentially sub-sampled)
+ YCbCr-RGB conversion, quantization range, and the inclusion of alpha in
+ the RGB color channels, a.k.a. pre-multiplication. There are several
+ different specifications on how an YCbCr-like (including ICtCp) signal,
+ with chroma sub-sampling or not, is created from a full-resolution RGB
+ image. Again, a client can tag a wl_surface with
+ color-representation metadata to tell the compositor what kind of pixel
+ data will be displayed through the wl_surface.
+
+
+ The main purpose of color-representation is to correctly off-load the
+ YCbCr-RGB conversion to the compositor, which can then opportunistically
+ off-load it further to very power-efficient fixed-function circuitry in
+ a display controller. This can significantly reduce power consumption
+ when watching videos compared to using a GPU for the same, and on some
+ embedded hardware platforms it is a hard requirement for processing high
+ resolution video.
+
+
+
+
diff --git a/doc/publican/sources/Wayland.xml b/doc/publican/sources/Wayland.xml
index 0457c15c..7593097e 100644
--- a/doc/publican/sources/Wayland.xml
+++ b/doc/publican/sources/Wayland.xml
@@ -12,6 +12,7 @@
+
diff --git a/doc/publican/sources/meson.build b/doc/publican/sources/meson.build
index 52f3a681..a53b3890 100644
--- a/doc/publican/sources/meson.build
+++ b/doc/publican/sources/meson.build
@@ -54,6 +54,7 @@ publican_sources = [
'Protocol.xml',
'Xwayland.xml',
'Compositors.xml',
+ 'Color.xml',
'Client.xml',
'Server.xml'
]
diff --git a/meson.build b/meson.build
index a3126b63..ce386a4c 100644
--- a/meson.build
+++ b/meson.build
@@ -1,6 +1,6 @@
project(
'wayland', 'c',
- version: '1.23.91',
+ version: '1.24.90',
license: 'MIT',
meson_version: '>= 0.57.0',
default_options: [
@@ -46,6 +46,7 @@ have_funcs = [
'memfd_create',
'mremap',
'strndup',
+ 'gettid',
]
foreach f: have_funcs
config_h.set('HAVE_' + f.underscorify().to_upper(), cc.has_function(f))
diff --git a/src/connection.c b/src/connection.c
index 593f52f3..2d1e8d1d 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -26,6 +26,8 @@
#define _GNU_SOURCE
+#include "../config.h"
+
#include
#include
#include
@@ -1491,11 +1493,56 @@ wl_closure_queue(struct wl_closure *closure, struct wl_connection *connection)
return result;
}
+bool
+wl_check_env_token(const char *env, const char *token)
+{
+ const char *ptr = env;
+ size_t token_len;
+
+ if (env == NULL)
+ return false;
+
+ token_len = strlen(token);
+
+ // Scan the string for comma-separated tokens and look for a match.
+ while (true) {
+ const char *end;
+ size_t len;
+
+ // Skip over any leading separators.
+ while (*ptr == ',')
+ ptr++;
+
+ if (*ptr == '\x00')
+ return false;
+
+ end = strchr(ptr + 1, ',');
+
+ // If there isn't another separarator, then the rest of the string
+ // is one token.
+ if (end == NULL)
+ return (strcmp(ptr, token) == 0);
+
+ len = end - ptr;
+ if (len == token_len && memcmp(ptr, token, len) == 0) {
+ return true;
+ }
+
+ // Skip to the next token.
+ ptr += len;
+ }
+
+ return false;
+}
+
void
wl_closure_print(struct wl_closure *closure, struct wl_object *target,
int send, int discarded, uint32_t (*n_parse)(union wl_argument *arg),
const char *queue_name, int color)
{
+#if defined(HAVE_GETTID)
+ static int include_tid = -1;
+#endif // defined(HAVE_GETTID)
int i;
struct argument_details arg;
const char *signature = closure->message->signature;
@@ -1516,6 +1563,18 @@ wl_closure_print(struct wl_closure *closure, struct wl_object *target,
color ? WL_DEBUG_COLOR_GREEN : "",
time / 1000, time % 1000);
+#if defined(HAVE_GETTID)
+ if (include_tid < 0) {
+ include_tid = wl_check_env_token(getenv("WAYLAND_DEBUG"), "thread_id");
+ }
+
+ if (include_tid) {
+ fprintf(f, "%sTID#%d ",
+ color ? WL_DEBUG_COLOR_CYAN : "",
+ (int) gettid());
+ }
+#endif
+
if (queue_name) {
fprintf(f, "%s{%s} ",
color ? WL_DEBUG_COLOR_YELLOW : "",
diff --git a/src/scanner.c b/src/scanner.c
index 1b71e60c..4efbc0a4 100644
--- a/src/scanner.c
+++ b/src/scanner.c
@@ -2045,6 +2045,7 @@ int main(int argc, char *argv[])
bool strict = false;
bool fail = false;
int opt;
+ int io_err;
enum {
CLIENT_HEADER,
SERVER_HEADER,
@@ -2213,7 +2214,10 @@ int main(int argc, char *argv[])
}
free_protocol(&protocol);
- fclose(input);
+ io_err = fflush(NULL) || ferror(stdout) || ferror(input);
+ if (io_err)
+ fprintf(stderr, "I/O error during processing\n");
- return 0;
+ fclose(input);
+ return io_err;
}
diff --git a/src/wayland-client-core.h b/src/wayland-client-core.h
index 970e6254..e0523e49 100644
--- a/src/wayland-client-core.h
+++ b/src/wayland-client-core.h
@@ -268,9 +268,16 @@ int
wl_display_dispatch_queue_pending(struct wl_display *display,
struct wl_event_queue *queue);
+int
+wl_display_dispatch_queue_pending_single(struct wl_display *display,
+ struct wl_event_queue *queue);
+
int
wl_display_dispatch_pending(struct wl_display *display);
+int
+wl_display_dispatch_pending_single(struct wl_display *display);
+
int
wl_display_get_error(struct wl_display *display);
diff --git a/src/wayland-client.c b/src/wayland-client.c
index fe14a6b1..ed686b5c 100644
--- a/src/wayland-client.c
+++ b/src/wayland-client.c
@@ -1236,7 +1236,7 @@ wl_display_connect_to_fd(int fd)
no_color = getenv("NO_COLOR");
force_color = getenv("FORCE_COLOR");
debug = getenv("WAYLAND_DEBUG");
- if (debug && (strstr(debug, "client") || strstr(debug, "1"))) {
+ if (debug && (wl_check_env_token(debug, "client") || wl_check_env_token(debug, "1"))) {
debug_client = 1;
if (isatty(fileno(stderr)))
debug_color = 1;
@@ -1578,6 +1578,28 @@ queue_event(struct wl_display *display, int len)
id = p[0];
opcode = p[1] & 0xffff;
size = p[1] >> 16;
+
+ /*
+ * If the message is larger than the maximum size of the
+ * connection buffer, the connection buffer will fill to
+ * its max size and stay there, with no message ever
+ * successfully being processed. If the user of
+ * libwayland-client uses a level-triggered event loop,
+ * this will cause the client to enter a loop that
+ * consumes CPU. To avoid this, immediately drop the
+ * connection. Since the maximum size of a message should
+ * not depend on the max buffer size chosen by the client,
+ * always compare the message size against the
+ * limit enforced by libwayland 1.22 and below (4096),
+ * rather than the actual value the client chose.
+ */
+ if (size > WL_MAX_MESSAGE_SIZE) {
+ wl_log("Message length %u exceeds limit %d\n",
+ size, WL_MAX_MESSAGE_SIZE);
+ errno = E2BIG;
+ return -1;
+ }
+
if (len < size)
return 0;
@@ -1860,6 +1882,34 @@ err:
return -1;
}
+
+static int
+dispatch_queue_single(struct wl_display *display, struct wl_event_queue *queue)
+{
+ if (display->last_error)
+ goto err;
+
+ while (!wl_list_empty(&display->display_queue.event_list)) {
+ dispatch_event(display, &display->display_queue);
+ if (display->last_error)
+ goto err;
+ }
+
+ if (!wl_list_empty(&queue->event_list)) {
+ dispatch_event(display, queue);
+ if (display->last_error)
+ goto err;
+ return 1;
+ } else {
+ return 0;
+ }
+
+err:
+ errno = display->last_error;
+
+ return -1;
+}
+
/** Prepare to read events from the display's file descriptor to a queue
*
* \param display The display context object
@@ -2190,6 +2240,34 @@ wl_display_dispatch_queue_pending(struct wl_display *display,
return ret;
}
+/** Dispatch at most one pending event in an event queue
+ *
+ * \param display The display context object
+ * \param queue The event queue to dispatch
+ * \return The number of dispatched events (0 or 1) on success or -1 on failure
+ *
+ * Dispatch at most one pending event for objects assigned to the given
+ * event queue. On failure -1 is returned and errno set appropriately.
+ * If there are no events queued, this function returns immediately.
+ *
+ * \memberof wl_display
+ * \since 1.25.0
+ */
+WL_EXPORT int
+wl_display_dispatch_queue_pending_single(struct wl_display *display,
+ struct wl_event_queue *queue)
+{
+ int ret;
+
+ pthread_mutex_lock(&display->mutex);
+
+ ret = dispatch_queue_single(display, queue);
+
+ pthread_mutex_unlock(&display->mutex);
+
+ return ret;
+}
+
/** Process incoming events
*
* \param display The display context object
@@ -2250,6 +2328,25 @@ wl_display_dispatch_pending(struct wl_display *display)
&display->default_queue);
}
+/** Dispatch at most one pending event in the default event queue.
+ *
+ * \param display The display context object
+ * \return The number of dispatched events (0 or 1) on success or -1 on failure
+ *
+ * Dispatch at most one pending event for objects assigned to the default
+ * event queue. On failure -1 is returned and errno set appropriately.
+ * If there are no events queued, this function returns immediately.
+ *
+ * \memberof wl_display
+ * \since 1.25.0
+ */
+WL_EXPORT int
+wl_display_dispatch_pending_single(struct wl_display *display)
+{
+ return wl_display_dispatch_queue_pending_single(display,
+ &display->default_queue);
+}
+
/** Retrieve the last error that occurred on a display
*
* \param display The display context object
diff --git a/src/wayland-private.h b/src/wayland-private.h
index 9aace67d..d0e4cfc6 100644
--- a/src/wayland-private.h
+++ b/src/wayland-private.h
@@ -49,6 +49,9 @@
#define WL_CLOSURE_MAX_ARGS 20
#define WL_BUFFER_DEFAULT_SIZE_POT 12
#define WL_BUFFER_DEFAULT_MAX_SIZE (1 << WL_BUFFER_DEFAULT_SIZE_POT)
+#if WL_BUFFER_DEFAULT_MAX_SIZE < WL_MAX_MESSAGE_SIZE
+# error default buffer cannot hold maximum-sized message
+#endif
#define WL_DEBUG_COLOR_RESET "\e[0m"
#define WL_DEBUG_COLOR_RED "\e[31m"
@@ -234,6 +237,9 @@ wl_closure_send(struct wl_closure *closure, struct wl_connection *connection);
int
wl_closure_queue(struct wl_closure *closure, struct wl_connection *connection);
+bool
+wl_check_env_token(const char *env, const char *token);
+
void
wl_closure_print(struct wl_closure *closure,
struct wl_object *target, int send, int discarded,
diff --git a/src/wayland-server.c b/src/wayland-server.c
index 15667644..c81d98f1 100644
--- a/src/wayland-server.c
+++ b/src/wayland-server.c
@@ -398,6 +398,29 @@ wl_client_connection_data(int fd, uint32_t mask, void *data)
wl_connection_copy(connection, p, sizeof p);
opcode = p[1] & 0xffff;
size = p[1] >> 16;
+
+ /*
+ * If the message is larger than the maximum size of the
+ * connection buffer, the connection buffer will fill to
+ * its max size and stay there, with no message ever
+ * successfully being processed. Since libwayland-server
+ * uses level-triggered epoll, it will cause the server to
+ * enter a loop that consumes CPU. To avoid this,
+ * immediately disconnect the client with a protocol
+ * error. Since the maximum size of a message should not
+ * depend on the buffer size chosen by the compositor,
+ * always compare the message size against the
+ * limit enforced by libwayland 1.22 and below (4096),
+ * rather than the actual value the compositor chose.
+ */
+ if (size > WL_MAX_MESSAGE_SIZE) {
+ wl_resource_post_error(client->display_resource,
+ WL_DISPLAY_ERROR_INVALID_METHOD,
+ "message length %u exceeds %d",
+ size, WL_MAX_MESSAGE_SIZE);
+ break;
+ }
+
if (len < size)
break;
@@ -1175,7 +1198,7 @@ wl_display_create(void)
no_color = getenv("NO_COLOR");
force_color = getenv("FORCE_COLOR");
debug = getenv("WAYLAND_DEBUG");
- if (debug && (strstr(debug, "server") || strstr(debug, "1"))) {
+ if (debug && (wl_check_env_token(debug, "server") || wl_check_env_token(debug, "1"))) {
debug_server = 1;
if (isatty(fileno(stderr)))
debug_color = 1;
diff --git a/src/wayland-util.h b/src/wayland-util.h
index 4540f040..98c72fde 100644
--- a/src/wayland-util.h
+++ b/src/wayland-util.h
@@ -90,6 +90,14 @@ extern "C" {
*/
struct wl_object;
+/**
+ * The maximum size of a protocol message.
+ *
+ * If a message size exceeds this value, the connection will be dropped.
+ * Servers will send an invalid_method error before disconnecting.
+ */
+#define WL_MAX_MESSAGE_SIZE 4096
+
/**
* Protocol message signature
*
diff --git a/tests/display-test.c b/tests/display-test.c
index 89606c73..fe78b521 100644
--- a/tests/display-test.c
+++ b/tests/display-test.c
@@ -1695,6 +1695,75 @@ TEST(global_remove)
display_destroy(d);
}
+static void
+dispatch_single_read_events(struct wl_display *d)
+{
+ if (wl_display_prepare_read(d) < 0) {
+ return;
+ }
+
+ int ret = 0;
+ do {
+ ret = wl_display_flush(d);
+ } while (ret < 0 && (errno == EINTR || errno == EAGAIN));
+ assert(ret >= 0);
+
+ struct pollfd pfd[1];
+ pfd[0].fd = wl_display_get_fd(d);
+ pfd[0].events = POLLIN;
+
+ do {
+ ret = poll(pfd, 1, -1);
+ } while (ret < 0 && errno == EINTR);
+ assert(ret > 0);
+
+ wl_display_read_events(d);
+}
+
+static void
+dispatch_single_client(void)
+{
+ struct client *c = client_connect();
+
+ assert(wl_display_dispatch_pending_single(c->wl_display) == 0);
+
+ struct wl_registry *registry = wl_display_get_registry(c->wl_display);
+
+ dispatch_single_read_events(c->wl_display);
+
+ // [1815110.061] {Default Queue} wl_registry#3.global(1, "test", 1)
+ assert(wl_display_dispatch_pending_single(c->wl_display) == 1);
+
+ dispatch_single_read_events(c->wl_display);
+
+ // [1815110.067] {Default Queue} wl_registry#3.global(2, "wl_seat", 1)
+ assert(wl_display_dispatch_pending_single(c->wl_display) == 1);
+
+ // No more events
+ assert(wl_display_dispatch_pending_single(c->wl_display) == 0);
+
+ wl_registry_destroy(registry);
+
+ client_disconnect(c);
+}
+
+TEST(dispatch_single)
+{
+ struct display *d = display_create();
+
+ struct wl_global *global = wl_global_create(d->wl_display,
+ &wl_seat_interface,
+ 1, d, bind_seat);
+
+ client_create_noarg(d, dispatch_single_client);
+
+ display_run(d);
+
+ wl_global_destroy(global);
+
+ display_destroy(d);
+}
+
static void
terminate_display(void *arg)
{