mirror of
https://gitlab.freedesktop.org/wayland/weston.git
synced 2026-05-07 06:08:05 +02:00
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:
commit
019ea7a932
8 changed files with 578 additions and 134 deletions
|
|
@ -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`.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
254
tests/x11-backend-test.c
Normal 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;
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue