From 0126a5b4fc03c3ac7f023d87303bf09f7d494ac1 Mon Sep 17 00:00:00 2001 From: Olivier Fourdan Date: Mon, 8 Dec 2025 11:33:30 +0100 Subject: [PATCH] backend-headless: Add an option to enable a fake seat When running headless, weston will not expose a wl_seat. This was removed with commit a1046adc ("compositor-headless: do not create a seat"). However, some applications, namely GTK3 based, will log a warning when there is no wl_seat: | gdk_seat_get_keyboard assertion GDK_IS_SEAT(seat) failed While this is arguably a bug in GTK3 which should not complain with a legit setup, that breaks the CI of those projects when using Weston, while most of the other Wayland compositors will create a fake seat when running headless, making weston the odd ball there. This changes adds a new option "--fake-seat" that will instruct weston to create a seat when running headless. The default remains not to create a seat though, so backward compatibility is preserved. This partially reverts commit a1046adc6603054187dc10ecaeee0ce271fcedec. See-also: https://gitlab.freedesktop.org/ofourdan/xwayland-run/-/issues/12 Signed-off-by: Olivier Fourdan --- frontend/main.c | 2 ++ include/libweston/backend-headless.h | 5 ++++ libweston/backend-headless/headless.c | 33 +++++++++++++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/frontend/main.c b/frontend/main.c index 4dca72314..c74961406 100644 --- a/frontend/main.c +++ b/frontend/main.c @@ -771,6 +771,7 @@ usage(int error_code) " --use-vulkan\t\tUse the Vulkan renderer (deprecated alias for --renderer=vulkan)\n" " --no-outputs\t\tDo not create any virtual outputs\n" " --refresh-rate=RATE\tThe output refresh rate (in mHz)\n" + " --fake-seat\t\tUse a fake seat for compatibility\n" "\n"); #endif @@ -4328,6 +4329,7 @@ load_headless_backend(struct weston_compositor *c, { WESTON_OPTION_STRING, "transform", 0, &transform }, { WESTON_OPTION_BOOLEAN, "no-outputs", 0, &no_outputs }, { WESTON_OPTION_INTEGER, "refresh-rate", 0, &config.refresh }, + { WESTON_OPTION_BOOLEAN, "fake-seat", 0, &config.fake_seat }, }; config.refresh = -1; diff --git a/include/libweston/backend-headless.h b/include/libweston/backend-headless.h index ed26d88ad..ce5ec9f3f 100644 --- a/include/libweston/backend-headless.h +++ b/include/libweston/backend-headless.h @@ -49,6 +49,11 @@ struct weston_headless_backend_config { * mHz to 1,000,000 mHz. 0 is a special value that triggers repaints * only on capture requests, not on damages. */ int refresh; + + /** Create a fake seat, some clients may complain without a wl_seat. + * The default is not to create a wl_seat. + */ + bool fake_seat; }; #ifdef __cplusplus diff --git a/libweston/backend-headless/headless.c b/libweston/backend-headless/headless.c index fcef6fab4..3109e13df 100644 --- a/libweston/backend-headless/headless.c +++ b/libweston/backend-headless/headless.c @@ -70,6 +70,8 @@ struct headless_backend { int refresh; bool repaint_only_on_capture; + + bool use_fake_seat; }; struct headless_head { @@ -598,6 +600,25 @@ headless_head_destroy(struct weston_head *base) free(head); } +static bool +headless_input_create(struct headless_backend *b) +{ + weston_seat_init(&b->fake_seat, b->compositor, "default"); + + weston_seat_init_pointer(&b->fake_seat); + + if (weston_seat_init_keyboard(&b->fake_seat, NULL) < 0) + return false; + + return true; +} + +static void +headless_input_destroy(struct headless_backend *b) +{ + weston_seat_release(&b->fake_seat); +} + static void headless_destroy(struct weston_backend *backend) { @@ -615,6 +636,9 @@ headless_destroy(struct weston_backend *backend) if (b->theme) theme_destroy(b->theme); + if (b->use_fake_seat) + headless_input_destroy(b); + free(b->formats); free(b); @@ -646,6 +670,12 @@ headless_backend_create(struct weston_compositor *compositor, b->base.supported_presentation_clocks = WESTON_PRESENTATION_CLOCKS_SOFTWARE; + if (config->fake_seat) { + b->use_fake_seat = headless_input_create(b); + if (!b->use_fake_seat) + goto err_free; + } + b->base.destroy = headless_destroy; b->base.create_output = headless_output_create; @@ -744,6 +774,8 @@ err_input: if (b->theme) theme_destroy(b->theme); err_free: + if (b->use_fake_seat) + headless_input_destroy(b); wl_list_remove(&b->base.link); free(b); return NULL; @@ -753,6 +785,7 @@ static void config_init_to_defaults(struct weston_headless_backend_config *config) { config->refresh = DEFAULT_OUTPUT_REPAINT_REFRESH; + config->fake_seat = FALSE; } WL_EXPORT int