Merge branch 'x11_without_mitshm' into 'main'

backend-x11: Support pixman rendering without MIT-SHM (allow remote X11)

See merge request wayland/weston!2049
This commit is contained in:
Martin Lopatář 2026-05-05 14:07:09 +00:00
commit 019ea7a932
8 changed files with 578 additions and 134 deletions

View file

@ -148,7 +148,7 @@ This is an example of a plugin test that just logs a line:
Client tests
^^^^^^^^^^^^
Plugin tests must have a fixture setup function that calls
Client tests must have a fixture setup function that calls
:func:`weston_test_harness_execute_as_client`. All test cases must be
defined with :c:func:`TEST` or :c:func:`TEST_P`, and each such function must
return a value from :type:`test_result_code`.

View file

@ -112,6 +112,8 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd
struct fdstr x11_wm_socket = FDSTR_INIT;
struct fdstr display_pipe = FDSTR_INIT;
char *xserver = NULL;
char *extra_args = NULL;
struct weston_string_array extra_args_array = { 0 };
struct weston_config *config = wet_get_config(wxw->compositor);
struct weston_config_section *section;
struct wl_client *client;
@ -154,6 +156,10 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd
section = weston_config_get_section(config, "xwayland", NULL, NULL);
weston_config_section_get_string(section, "path",
&xserver, XSERVER_PATH);
weston_config_section_get_string(section, "extra-args", &extra_args, NULL);
if (extra_args)
extra_args_array = weston_parse_space_separated_list(extra_args);
custom_env_init_from_environ(&child_env);
custom_env_set_env_var(&child_env, "WAYLAND_SOCKET", wayland_socket.str1);
@ -170,6 +176,9 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd
custom_env_add_arg(&child_env, x11_wm_socket.str1);
custom_env_add_arg(&child_env, "-terminate");
for (size_t i = 0; i < extra_args_array.len; i++)
custom_env_add_arg(&child_env, extra_args_array.array[i]);
wxw->process =
wet_client_launch(wxw->compositor, &child_env,
no_cloexec_fds, num_no_cloexec_fds,
@ -208,6 +217,10 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd
handle_display_fd, wxw);
free(xserver);
if (extra_args)
free(extra_args);
if (extra_args_array.array)
weston_string_array_fini(&extra_args_array);
return client;
@ -216,6 +229,10 @@ err_proc:
wl_list_remove(&wxw->process->link);
err:
free(xserver);
if (extra_args)
free(extra_args);
if (extra_args_array.array)
weston_string_array_fini(&extra_args_array);
fdstr_close_all(&display_pipe);
fdstr_close_all(&x11_wm_socket);
fdstr_close_all(&wayland_socket);

View file

@ -95,6 +95,7 @@ struct x11_backend {
uint8_t xkb_event_base;
int fullscreen;
int no_input;
bool has_shm;
int has_net_wm_state_fullscreen;
@ -139,7 +140,10 @@ struct x11_output {
struct weston_mode native;
struct wl_event_source *finish_frame_timer;
const struct pixel_format_info *pfmt;
xcb_gc_t gc;
bool use_shm;
xcb_shm_seg_t segment;
weston_renderbuffer_t renderbuffer;
int shm_id;
@ -148,6 +152,10 @@ struct x11_output {
int32_t scale;
bool resize_pending;
bool window_resized;
/* Scratch buffer for non-SHM Pixman rendering */
uint8_t *image_scratch_buf;
uint32_t image_scratch_size;
};
struct window_delete_data {
@ -156,7 +164,7 @@ struct window_delete_data {
};
static void
x11_destroy(struct weston_backend *backend);
x11_destroy(struct weston_backend *base);
static inline struct x11_head *
to_x11_head(struct weston_head *base)
@ -170,8 +178,7 @@ static void
x11_output_destroy(struct weston_output *base);
static int
x11_output_init_shm(struct x11_backend *b, struct x11_output *output,
const struct pixel_format_info *pfmt);
x11_output_init_pixman(struct x11_output *output);
static inline struct x11_output *
to_x11_output(struct weston_output *base)
@ -479,72 +486,92 @@ x11_output_repaint_vulkan(struct weston_output *output_base)
return 0;
}
static void
set_clip_for_output(struct weston_output *output_base, pixman_region32_t *region)
static int
x11_output_repaint_pixman_shm(struct x11_output *output, xcb_rectangle_t *output_rects, int nrects)
{
struct x11_output *output = to_x11_output(output_base);
struct x11_backend *b;
pixman_region32_t transformed_region;
pixman_box32_t *rects;
xcb_rectangle_t *output_rects;
xcb_connection_t *conn = output->backend->conn;
xcb_void_cookie_t cookie;
int nrects, i;
xcb_generic_error_t *err;
if (!output)
return;
b = output->backend;
pixman_region32_init(&transformed_region);
weston_region_global_to_output(&transformed_region,
output_base,
region);
rects = pixman_region32_rectangles(&transformed_region, &nrects);
output_rects = calloc(nrects, sizeof(xcb_rectangle_t));
if (output_rects == NULL) {
pixman_region32_fini(&transformed_region);
return;
}
for (i = 0; i < nrects; i++) {
output_rects[i].x = rects[i].x1;
output_rects[i].y = rects[i].y1;
output_rects[i].width = rects[i].x2 - rects[i].x1;
output_rects[i].height = rects[i].y2 - rects[i].y1;
}
pixman_region32_fini(&transformed_region);
cookie = xcb_set_clip_rectangles_checked(b->conn, XCB_CLIP_ORDERING_UNSORTED,
output->gc,
0, 0, nrects,
output_rects);
err = xcb_request_check(b->conn, cookie);
cookie = xcb_set_clip_rectangles_checked(
conn, XCB_CLIP_ORDERING_UNSORTED, output->gc,
0, 0, nrects, output_rects
);
err = xcb_request_check(conn, cookie);
if (err != NULL) {
weston_log("Failed to set clip rects, err: %d\n", err->error_code);
free(err);
return -1;
}
free(output_rects);
cookie = xcb_shm_put_image_checked(
conn, output->window, output->gc,
output->base.current_mode->width, output->base.current_mode->height, 0, 0,
output->base.current_mode->width, output->base.current_mode->height, 0, 0,
output->depth, XCB_IMAGE_FORMAT_Z_PIXMAP, 0, output->segment, 0
);
err = xcb_request_check(conn, cookie);
if (err != NULL) {
weston_log("Failed to put shm image, err: %d\n", err->error_code);
free(err);
return -1;
}
return 0;
}
static int
x11_output_repaint_pixman_putimage(struct x11_output *output, xcb_rectangle_t *output_rects, int nrects)
{
int i, dst_stride, lines_per_chunk, y, chunk_y, chunk_h, rect_w, rect_h;
int bytes_pp = output->pfmt->bpp / 8;
int32_t src_stride = output->base.current_mode->width * bytes_pp;
uint8_t *src, *dst;
for (i = 0; i < nrects; i++) {
rect_w = output_rects[i].width;
rect_h = output_rects[i].height;
/* X11 ZPixmap scanlines must be padded to a multiple of 4 bytes */
dst_stride = (rect_w * bytes_pp + 3) & ~3;
if (output->image_scratch_size == 0 || dst_stride == 0)
continue;
lines_per_chunk = output->image_scratch_size / dst_stride;
if (lines_per_chunk == 0)
lines_per_chunk = 1;
for (chunk_y = 0; chunk_y < rect_h; chunk_y += lines_per_chunk) {
chunk_h = MIN(rect_h - chunk_y, lines_per_chunk);
for (y = 0; y < chunk_h; y++) {
src = output->buf + (output_rects[i].y + chunk_y + y) * src_stride + (output_rects[i].x * bytes_pp);
dst = output->image_scratch_buf + y * dst_stride;
memcpy(dst, src, rect_w * bytes_pp);
}
xcb_put_image(output->backend->conn, XCB_IMAGE_FORMAT_Z_PIXMAP,
output->window, output->gc,
rect_w, chunk_h,
output_rects[i].x, output_rects[i].y + chunk_y,
0, output->depth, dst_stride * chunk_h, output->image_scratch_buf
);
}
}
return 0;
}
static int
x11_output_repaint_shm(struct weston_output *output_base)
x11_output_repaint_pixman(struct weston_output *output_base)
{
struct x11_output *output = to_x11_output(output_base);
struct weston_compositor *ec;
struct x11_backend *b;
xcb_void_cookie_t cookie;
xcb_generic_error_t *err;
pixman_region32_t damage;
assert(output);
ec = output->base.compositor;
b = output->backend;
struct weston_compositor *ec = output->base.compositor;
pixman_region32_t damage, transformed_region;
int nrects, i;
pixman_box32_t *rects;
xcb_rectangle_t *output_rects;
int result;
pixman_region32_init(&damage);
@ -552,26 +579,32 @@ x11_output_repaint_shm(struct weston_output *output_base)
ec->renderer->repaint_output(output_base, &damage, output->renderbuffer);
set_clip_for_output(output_base, &damage);
pixman_region32_init(&transformed_region);
weston_region_global_to_output(&transformed_region, output_base, &damage);
rects = pixman_region32_rectangles(&transformed_region, &nrects);
output_rects = xcalloc(nrects, sizeof(xcb_rectangle_t));
for (i = 0; i < nrects; i++) {
output_rects[i].x = (int16_t) rects[i].x1;
output_rects[i].y = (int16_t) rects[i].y1;
output_rects[i].width = rects[i].x2 - rects[i].x1;
output_rects[i].height = rects[i].y2 - rects[i].y1;
}
pixman_region32_fini(&transformed_region);
result = (output->use_shm)
? x11_output_repaint_pixman_shm(output, output_rects, nrects)
: x11_output_repaint_pixman_putimage(output, output_rects, nrects);
free(output_rects);
pixman_region32_fini(&damage);
cookie = xcb_shm_put_image_checked(b->conn, output->window, output->gc,
output_base->current_mode->width,
output_base->current_mode->height,
0, 0,
output_base->current_mode->width,
output_base->current_mode->height,
0, 0, output->depth, XCB_IMAGE_FORMAT_Z_PIXMAP,
0, output->segment, 0);
err = xcb_request_check(b->conn, cookie);
if (err != NULL) {
weston_log("Failed to put shm image, err: %d\n", err->error_code);
free(err);
}
weston_output_arm_frame_timer(output_base, output->finish_frame_timer);
return 0;
return result;
}
static int
@ -585,7 +618,7 @@ finish_frame_handler(void *data)
}
static void
x11_output_deinit_shm(struct x11_backend *b, struct x11_output *output)
x11_output_deinit_pixman(struct x11_backend *b, struct x11_output *output)
{
xcb_void_cookie_t cookie;
xcb_generic_error_t *err;
@ -593,13 +626,21 @@ x11_output_deinit_shm(struct x11_backend *b, struct x11_output *output)
b->compositor->renderer->destroy_renderbuffer(output->renderbuffer);
output->renderbuffer = NULL;
cookie = xcb_shm_detach_checked(b->conn, output->segment);
err = xcb_request_check(b->conn, cookie);
if (err) {
weston_log("xcb_shm_detach failed, error %d\n", err->error_code);
free(err);
if (output->use_shm) {
cookie = xcb_shm_detach_checked(b->conn, output->segment);
err = xcb_request_check(b->conn, cookie);
if (err) {
weston_log("xcb_shm_detach failed, error %d\n", err->error_code);
free(err);
}
shmdt(output->buf);
} else {
free(output->buf);
free(output->image_scratch_buf);
output->image_scratch_size = 0;
}
shmdt(output->buf);
output->buf = NULL;
}
static void
@ -753,24 +794,14 @@ get_depth_of_visual(xcb_screen_t *screen,
}
static const struct pixel_format_info *
x11_output_get_shm_pixel_format(struct x11_output *output)
x11_output_get_pixman_pixel_format(struct x11_output *output)
{
struct x11_backend *b = output->backend;
xcb_visualtype_t *visual_type;
xcb_screen_t *screen;
xcb_format_iterator_t fmt;
const xcb_query_extension_reply_t *ext;
int bitsperpixel = 0;
/* Check if SHM is available */
ext = xcb_get_extension_data(b->conn, &xcb_shm_id);
if (ext == NULL || !ext->present) {
/* SHM is missing */
weston_log("SHM extension is not available\n");
errno = ENOENT;
return NULL;
}
screen = x11_compositor_get_default_screen(b);
visual_type = find_visual_by_id(screen, screen->root_visual);
if (!visual_type) {
@ -801,16 +832,16 @@ x11_output_get_shm_pixel_format(struct x11_output *output)
visual_type->red_mask == 0xff0000 &&
visual_type->green_mask == 0x00ff00 &&
visual_type->blue_mask == 0x0000ff) {
weston_log("Will use x8r8g8b8 format for SHM surfaces\n");
weston_log("Will use x8r8g8b8 format for pixman surfaces\n");
return pixel_format_get_info_by_pixman(PIXMAN_x8r8g8b8);
} else if (bitsperpixel == 16 &&
visual_type->red_mask == 0x00f800 &&
visual_type->green_mask == 0x0007e0 &&
visual_type->blue_mask == 0x00001f) {
weston_log("Will use r5g6b5 format for SHM surfaces\n");
weston_log("Will use r5g6b5 format for pixman surfaces\n");
return pixel_format_get_info_by_pixman(PIXMAN_r5g6b5);
} else {
weston_log("Can't find appropriate format for SHM pixmap\n");
weston_log("Can't find appropriate format for pixman pixmap\n");
errno = ENOTSUP;
return NULL;
}
@ -820,15 +851,11 @@ static bool
x11_rb_discarded_cb(weston_renderbuffer_t rb, void *data)
{
struct x11_output *output = (struct x11_output *) data;
const struct pixel_format_info *pfmt;
if (output->base.compositor->renderer->type == WESTON_RENDERER_PIXMAN) {
x11_output_deinit_shm(output->backend, output);
pfmt = x11_output_get_shm_pixel_format(output);
if (!pfmt)
return false;
if (x11_output_init_shm(output->backend, output, pfmt) < 0) {
weston_log("Failed to initialize SHM for the X11 output\n");
x11_output_deinit_pixman(output->backend, output);
if (x11_output_init_pixman(output) < 0) {
weston_log("Failed to initialize pixman renderer for the X11 output\n");
return false;
}
}
@ -836,49 +863,135 @@ x11_rb_discarded_cb(weston_renderbuffer_t rb, void *data)
return true;
}
static int
x11_output_init_shm(struct x11_backend *b, struct x11_output *output,
const struct pixel_format_info *pfmt)
static void
x11_output_init_pixman_shm(struct x11_output *output, size_t size)
{
struct weston_renderer *renderer = output->base.compositor->renderer;
int bitsperpixel = pfmt->bpp;
size_t size = output->base.current_mode->width *
output->base.current_mode->height * (bitsperpixel / 8);
int stride = output->base.current_mode->width * (bitsperpixel / 8);
xcb_connection_t *conn = output->backend->conn;
xcb_void_cookie_t cookie;
xcb_generic_error_t *err;
/* Create SHM segment and attach it */
output->shm_id = shmget(IPC_PRIVATE, size, IPC_CREAT | S_IRWXU);
if (output->shm_id == -1) {
weston_log("x11shm: failed to allocate SHM segment\n");
return -1;
weston_log("x11shm: failed to allocate SHM segment of size %zu: %s\n",
size, strerror(errno));
return;
}
output->buf = shmat(output->shm_id, NULL, 0 /* read/write */);
if (-1 == (long)output->buf) {
output->buf = shmat(output->shm_id, NULL, 0);
if (output->buf == (void *)-1) {
weston_log("x11shm: failed to attach SHM segment\n");
return -1;
goto err_free;
}
output->segment = xcb_generate_id(b->conn);
cookie = xcb_shm_attach_checked(b->conn, output->segment, output->shm_id, 1);
err = xcb_request_check(b->conn, cookie);
output->segment = xcb_generate_id(conn);
cookie = xcb_shm_attach_checked(conn, output->segment, output->shm_id, 1);
err = xcb_request_check(conn, cookie);
if (err) {
weston_log("x11shm: xcb_shm_attach error %d, op code %d, resource id %d\n",
err->error_code, err->major_code, err->minor_code);
free(err);
shmdt(output->buf);
goto err_free;
}
output->use_shm = true;
goto release_segment;
err_free:
output->buf = NULL;
release_segment:
shmctl(output->shm_id, IPC_RMID, NULL);
}
static void
x11_output_init_pixman_putimage(struct x11_output *output, size_t size)
{
uint32_t max_req_len, max_payload, min_stride;
output->buf = malloc(size);
if (output->buf == NULL) {
weston_log("x11: failed to allocate buffer for XPutImage of size %zu\n", size);
return;
}
max_req_len = xcb_get_maximum_request_length(output->backend->conn) * 4;
max_payload = max_req_len > 32 ? max_req_len - 32 : 0;
min_stride = (output->base.current_mode->width * (output->pfmt->bpp / 8) + 3) & ~3;
if (max_payload < min_stride) {
weston_log("x11: X11 server max request length is too small to transfer a single line\n");
goto error_free;
}
output->image_scratch_size = size;
if (max_payload > 0 && max_payload < output->image_scratch_size) {
output->image_scratch_size = max_payload;
}
output->image_scratch_buf = malloc(output->image_scratch_size);
if (output->image_scratch_buf == NULL) {
weston_log("x11: failed to allocate buffer for XPutImage of size %u\n",
output->image_scratch_size);
goto error_free;
}
return;
error_free:
free(output->buf);
output->buf = NULL;
}
static int
x11_output_init_pixman(struct x11_output *output)
{
struct weston_renderer *renderer = output->base.compositor->renderer;
int stride = output->base.current_mode->width * (output->pfmt->bpp / 8);
size_t size = stride * output->base.current_mode->height;
const char *env_val;
bool force_shm, force_putimage;
env_val = getenv("WESTON_X11_ONLY_SHM");
force_shm = env_val && strcmp(env_val, "1") == 0;
env_val = getenv("WESTON_X11_NO_SHM");
force_putimage = env_val && strcmp(env_val, "1") == 0;
if (output->backend->has_shm && !force_putimage) {
x11_output_init_pixman_shm(output, size);
if (output->buf != NULL)
weston_log("x11: using MIT-SHM\n");
else if (!force_shm)
weston_log("x11: MIT-SHM not available, falling back to XPutImage\n");
}
if (output->buf == NULL && force_shm) {
weston_log("x11: MIT-SHM not available, not falling back to XPutImage as requested\n");
return -1;
}
shmctl(output->shm_id, IPC_RMID, NULL);
if (output->buf == NULL) {
x11_output_init_pixman_putimage(output, size);
if (output->buf == NULL)
return -1;
weston_log("x11: using XPutImage\n");
}
/* Now create pixman image */
output->renderbuffer =
renderer->create_renderbuffer(&output->base, pfmt, output->buf,
renderer->create_renderbuffer(&output->base, output->pfmt, output->buf,
stride, x11_rb_discarded_cb,
output);
output->gc = xcb_generate_id(b->conn);
xcb_create_gc(b->conn, output->gc, output->window, 0, NULL);
output->gc = xcb_generate_id(output->backend->conn);
xcb_create_gc(output->backend->conn, output->gc, output->window, 0, NULL);
return 0;
}
@ -960,7 +1073,7 @@ x11_output_disable(struct weston_output *base)
switch (renderer->type) {
case WESTON_RENDERER_PIXMAN:
x11_output_deinit_shm(backend, output);
x11_output_deinit_pixman(backend, output);
renderer->pixman->output_destroy(&output->base);
break;
case WESTON_RENDERER_GL:
@ -998,11 +1111,10 @@ x11_output_enable(struct weston_output *base)
const struct weston_renderer *renderer = base->compositor->renderer;
struct x11_output *output = to_x11_output(base);
const struct weston_mode *mode = output->base.current_mode;
struct x11_backend *b;
struct pixman_renderer_output_options options;
assert(output);
b = output->backend;
assert(output);
struct x11_backend *b = output->backend;
static const char name[] = "Weston Compositor";
static const char class[] = "weston-1\0Weston Compositor";
@ -1021,7 +1133,7 @@ x11_output_enable(struct weston_output *base)
0
};
if (!b->no_input)
if (!b->no_input)
values[0] |=
XCB_EVENT_MASK_KEY_PRESS |
XCB_EVENT_MASK_KEY_RELEASE |
@ -1103,27 +1215,28 @@ x11_output_enable(struct weston_output *base)
switch (renderer->type) {
case WESTON_RENDERER_PIXMAN: {
const struct pixman_renderer_output_options options = {
options = (struct pixman_renderer_output_options) {
.use_shadow = true,
.fb_size = {
.width = mode->width,
.height = mode->height
},
.format = x11_output_get_shm_pixel_format(output)
.format = x11_output_get_pixman_pixel_format(output)
};
if (!options.format)
output->pfmt = options.format;
if (!output->pfmt)
goto err;
if (renderer->pixman->output_create(&output->base, &options) < 0) {
weston_log("Failed to create pixman renderer for output\n");
goto err;
}
if (x11_output_init_shm(b, output, options.format) < 0) {
weston_log("Failed to initialize SHM for the X11 output\n");
if (x11_output_init_pixman(output) < 0) {
weston_log("Failed to initialize pixman renderer for the X11 output\n");
renderer->pixman->output_destroy(&output->base);
goto err;
}
output->base.repaint = x11_output_repaint_shm;
output->base.repaint = x11_output_repaint_pixman;
break;
}
case WESTON_RENDERER_GL: {
@ -1959,6 +2072,9 @@ x11_backend_create(struct weston_compositor *compositor,
{
struct x11_backend *b;
struct wl_event_loop *loop;
const xcb_query_extension_reply_t *ext;
xcb_shm_query_version_cookie_t query_ext_cookie;
xcb_shm_query_version_reply_t *reply;
int ret;
b = zalloc(sizeof *b);
@ -1981,6 +2097,22 @@ x11_backend_create(struct weston_compositor *compositor,
b->conn = XGetXCBConnection(b->dpy);
XSetEventQueueOwner(b->dpy, XCBOwnsEventQueue);
b->has_shm = false;
ext = xcb_get_extension_data(b->conn, &xcb_shm_id);
if (ext != NULL && ext->present) {
query_ext_cookie = xcb_shm_query_version(b->conn);
reply = xcb_shm_query_version_reply(b->conn, query_ext_cookie, NULL);
if (reply != NULL) {
weston_log("SHM extension has version %d.%d\n", reply->major_version, reply->minor_version);
b->has_shm = true;
free(reply);
} else {
weston_log("SHM extension has unknown version\n");
}
} else {
weston_log("SHM extension is not available\n");
}
if (xcb_connection_has_error(b->conn))
goto err_xdisplay;

View file

@ -193,6 +193,7 @@ compositor_setup_defaults_(struct compositor_setup *setup,
.renderer = WESTON_RENDERER_NOOP,
.shell = SHELL_TEST_DESKTOP,
.xwayland = false,
.xwayland_extra_args = NULL,
.width = 320,
.height = 240,
.scale = 1,
@ -490,6 +491,14 @@ weston_ini_setup_(struct compositor_setup *setup, ...)
weston_ini = open_ini_file(setup);
test_assert_ptr_not_null(weston_ini);
/* Write xwayland section if extra args are specified */
if (setup->xwayland_extra_args) {
ret = fprintf(weston_ini, "[xwayland]\n");
test_assert_int_ge(ret, 0);
ret = fprintf(weston_ini, "extra-args=%s\n", setup->xwayland_extra_args);
test_assert_int_ge(ret, 0);
}
va_start(entry_list, setup);
write_cfg(entry_list, weston_ini);
va_end(entry_list);

View file

@ -75,6 +75,8 @@ struct compositor_setup {
enum shell_type shell;
/** Whether to enable xwayland support. */
bool xwayland;
/** Extra arguments to pass to Xwayland, or NULL for none. */
const char *xwayland_extra_args;
/** Default output width. */
unsigned width;
/** Default output height. */
@ -113,6 +115,7 @@ compositor_setup_defaults_(struct compositor_setup *setup,
* - renderer: noop
* - shell: test desktop shell
* - xwayland: no
* - xwayland_extra_args: none
* - width: 320
* - height: 240
* - refresh: 0 (repaint only on demand)

View file

@ -292,6 +292,16 @@ if get_option('shell-lua')
]
endif
xvfb = find_program('Xvfb', required: false)
if get_option('backend-x11') and xvfb.found()
tests += [
{
'name': 'x11-backend',
'dep_objs': [ dep_x11_xcb ],
},
]
endif
test_config_h = configuration_data()
test_config_h.set_quoted('WESTON_TEST_REFERENCE_PATH', meson.current_source_dir() + '/reference')
test_config_h.set_quoted('WESTON_MODULE_MAP', env_modmap)

254
tests/x11-backend-test.c Normal file
View file

@ -0,0 +1,254 @@
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include <signal.h>
#include <xcb/xcb.h>
#include "shared/helpers.h"
#include "weston-test-runner.h"
#include "weston-test-client-helper.h"
#include "weston-test-assert.h"
#define XVFB_DISPLAY "99"
struct setup_args {
struct fixture_metadata meta;
bool xvfb_allow_shm;
bool weston_x11_only_shm;
bool weston_x11_no_shm;
bool expect_fail;
};
static const struct setup_args my_setup_args[] = {
{
/* server DOESN'T support MIT-SHM
* => automatic fallback to XPutImage */
.meta.name = "xvfb_shm_off__env_shm_default",
.xvfb_allow_shm = false,
.weston_x11_only_shm = false,
.weston_x11_no_shm = false,
},
{
/* server DOESN'T support SHM, but forced by WESTON_X11_ONLY_SHM=1
* => expected FAIL */
.meta.name = "xvfb_shm_off__env_shm_only",
.xvfb_allow_shm = false,
.weston_x11_only_shm = true,
.weston_x11_no_shm = false,
.expect_fail = true,
},
{
/* server DOESN'T support MIT-SHM (also forbidden by WESTON_X11_NO_SHM=1)
* => automatic fallback to XPutImage */
.meta.name = "xvfb_shm_off__env_shm_no",
.xvfb_allow_shm = false,
.weston_x11_only_shm = false,
.weston_x11_no_shm = true,
},
{
/* server supports MIT-SHM
* => use MIT-SHM */
.meta.name = "xvfb_shm_on__env_shm_default",
.xvfb_allow_shm = true,
.weston_x11_only_shm = false,
.weston_x11_no_shm = false,
},
{
/* server supports MIT-SHM (also forced by WESTON_X11_ONLY_SHM=1)
* => use MIT-SHM */
.meta.name = "xvfb_shm_on__env_shm_only",
.xvfb_allow_shm = true,
.weston_x11_only_shm = true,
.weston_x11_no_shm = false,
},
{
/* server supports SHM, but forbidden by WESTON_X11_NO_SHM=1
* => use XPutImage */
.meta.name = "xvfb_shm_on__env_shm_no",
.xvfb_allow_shm = true,
.weston_x11_only_shm = false,
.weston_x11_no_shm = true,
},
};
static enum test_result_code
fixture_setup(struct weston_test_harness *harness, const struct setup_args *arg)
{
struct compositor_setup setup;
pid_t pid_xvfb;
char *mitshm_ext_arg, *weston_x11_only_shm, *weston_x11_no_shm;
int retries, status;
enum test_result_code result, ret;
weston_x11_only_shm = arg->weston_x11_only_shm ? "1" : "0";
weston_x11_no_shm = arg->weston_x11_no_shm ? "1" : "0";
testlog("Test fixture: %s (Xvfb MIT-SHM extension %s, WESTON_X11_NO_SHM=%s, WESTON_X11_ONLY_SHM=%s)\n",
arg->meta.name, arg->xvfb_allow_shm ? "ON" : "OFF",
weston_x11_no_shm, weston_x11_only_shm);
pid_xvfb = fork();
assert(pid_xvfb >= 0);
if (pid_xvfb == 0) {
prctl(PR_SET_PDEATHSIG, SIGTERM);
mitshm_ext_arg = arg->xvfb_allow_shm ? "+extension" : "-extension";
execlp("Xvfb", "Xvfb", ":" XVFB_DISPLAY,
"-screen", "0", "640x480x24",
mitshm_ext_arg, "MIT-SHM",
"-terminate", NULL);
fprintf(stderr, "Error: Xvfb cannot be started: %s\n",
strerror(errno));
return RESULT_HARD_ERROR;
}
retries = 0;
while (access("/tmp/.X11-unix/X" XVFB_DISPLAY, F_OK) != 0) {
if (retries == 50) {
fprintf(stderr, "Error: Timeout waiting for Xvfb socket\n");
return RESULT_HARD_ERROR;
}
if (waitpid(pid_xvfb, &status, WNOHANG) > 0) {
fprintf(stderr, "Error: Xvfb process died unexpectedly during startup\n");
return RESULT_HARD_ERROR;
}
usleep(100000); // 100 ms
retries++;
}
setenv("DISPLAY", ":" XVFB_DISPLAY, 1);
setenv("WESTON_X11_ONLY_SHM", weston_x11_only_shm, 1);
setenv("WESTON_X11_NO_SHM", weston_x11_no_shm, 1);
compositor_setup_defaults(&setup);
setup.backend = WESTON_BACKEND_X11;
setup.renderer = WESTON_RENDERER_PIXMAN;
result = weston_test_harness_execute_as_client(harness, &setup);
testlog("Test %s result: %d\n", arg->meta.name, result);
if (arg->expect_fail) {
ret = (result != RESULT_OK) ? RESULT_SKIP : RESULT_FAIL;
} else {
ret = result;
}
kill(pid_xvfb, SIGTERM);
waitpid(pid_xvfb, NULL, 0);
return ret;
}
DECLARE_FIXTURE_SETUP_WITH_ARG(fixture_setup, my_setup_args, meta)
static void
get_x11_screen_pixels(int16_t x, int16_t y, uint16_t width, uint16_t height, uint32_t *out_pixels)
{
xcb_connection_t *conn;
const xcb_setup_t *xsetup;
xcb_screen_t *screen;
xcb_query_tree_cookie_t qt_cookie;
xcb_query_tree_reply_t *qt_reply;
xcb_window_t weston_win;
xcb_get_image_cookie_t img_cookie;
xcb_get_image_reply_t *img_reply;
conn = xcb_connect(":" XVFB_DISPLAY, NULL);
assert(conn && !xcb_connection_has_error(conn));
xsetup = xcb_get_setup(conn);
screen = xcb_setup_roots_iterator(xsetup).data;
qt_cookie = xcb_query_tree(conn, screen->root);
qt_reply = xcb_query_tree_reply(conn, qt_cookie, NULL);
assert(qt_reply != NULL && qt_reply->children_len > 0);
weston_win = xcb_query_tree_children(qt_reply)[qt_reply->children_len - 1];
img_cookie = xcb_get_image(conn, XCB_IMAGE_FORMAT_Z_PIXMAP, weston_win,
x, y, width, height, ~0);
img_reply = xcb_get_image_reply(conn, img_cookie, NULL);
assert(img_reply != NULL);
memcpy(out_pixels, xcb_get_image_data(img_reply), width * height * 4);
free(img_reply);
free(qt_reply);
xcb_disconnect(conn);
}
static void
draw_buffer_to_wayland(struct client *client, struct buffer *buf)
{
int frame_done = 0;
client->surface = create_test_surface(client);
client->surface->buffer = buf;
client->surface->width = buf->buf->width;
client->surface->height = buf->buf->height;
move_client(client, 0, 0);
wl_surface_set_buffer_scale(client->surface->wl_surface, 1);
wl_surface_set_buffer_transform(client->surface->wl_surface, WL_OUTPUT_TRANSFORM_NORMAL);
wl_surface_attach(client->surface->wl_surface, client->surface->buffer->proxy, 0, 0);
wl_surface_damage(client->surface->wl_surface, 0, 0, client->surface->width, client->surface->height);
frame_callback_set(client->surface->wl_surface, &frame_done);
wl_surface_commit(client->surface->wl_surface);
frame_callback_wait(client, &frame_done);
}
TEST(x11_backend)
{
const struct setup_args *args = &my_setup_args[get_test_fixture_index()];
struct client *client;
struct buffer *img_buffer;
pixman_image_t *x11_img;
uint32_t *x11_pixels;
struct rectangle clip;
struct range fuzz = { .a = -5, .b = 4 };
int width, height;
bool match;
testlog("Test: %s (Xvfb MIT-SHM extension %s, WESTON_X11_NO_SHM=%s, WESTON_X11_ONLY_SHM=%s)\n",
args->meta.name, args->xvfb_allow_shm ? "ON" : "OFF",
getenv("WESTON_X11_NO_SHM"), getenv("WESTON_X11_ONLY_SHM"));
client = create_client();
width = client->output->width;
height = client->output->height;
img_buffer = client_buffer_from_image_file(client, "chocolate-cake", 1);
draw_buffer_to_wayland(client, img_buffer);
x11_pixels = malloc(width * height * 4);
get_x11_screen_pixels(0, 0, width, height, x11_pixels);
x11_img = pixman_image_create_bits(PIXMAN_x8r8g8b8, width, height,
x11_pixels, width * 4);
clip.x = 0;
clip.y = 0;
clip.width = MIN(img_buffer->buf->width, width);
clip.height = MIN(img_buffer->buf->height, height);
match = check_images_match(img_buffer->image, x11_img, &clip, &fuzz);
pixman_image_unref(x11_img);
free(x11_pixels);
client_destroy(client);
return match ? RESULT_OK : RESULT_FAIL;
}

View file

@ -48,8 +48,24 @@
#include "weston-test-assert.h"
#include "xcb-client-helper.h"
struct setup_args {
struct fixture_metadata meta;
const char *extra_args;
};
static const struct setup_args my_setup_args[] = {
{
.extra_args = "-extension MIT-SHM",
.meta.name = "MIT-SHM-disabled"
},
{
.extra_args = "+extension MIT-SHM",
.meta.name = "MIT-SHM-enabled"
},
};
static enum test_result_code
fixture_setup(struct weston_test_harness *harness)
fixture_setup(struct weston_test_harness *harness, const struct setup_args *arg)
{
struct compositor_setup setup;
@ -59,11 +75,14 @@ fixture_setup(struct weston_test_harness *harness)
setup.shell = SHELL_TEST_DESKTOP;
setup.refresh = HIGHEST_OUTPUT_REFRESH;
setup.xwayland = true;
setup.xwayland_extra_args = arg->extra_args;
setup.logging_scopes = "log,xwm-wm-x11";
weston_ini_setup(&setup, NULL);
return weston_test_harness_execute_as_client(harness, &setup);
}
DECLARE_FIXTURE_SETUP(fixture_setup);
DECLARE_FIXTURE_SETUP_WITH_ARG(fixture_setup, my_setup_args, meta);
static char *
get_x11_window_name(struct window_x11 *window, xcb_drawable_t win)