From 2833c28ff15216b9cfcf46e2e719266f7d7d1a46 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 1 Feb 2022 23:16:03 +0100 Subject: [PATCH 001/609] build: re-open main for regular development --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 7b80214d..afadcb2c 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project('weston', 'c', - version: '10.0.0', + version: '10.0.90', default_options: [ 'warning_level=3', 'c_std=gnu99', From 7ca7c14553e565f68e74773b50e26951a9b228a0 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 7 Dec 2021 15:52:58 +0000 Subject: [PATCH 002/609] backend-drm: Rewrite zpos-sorting list insertion It's possible to write this with a few less twisty special cases. Tested manually with a randomly-distributed input tree as well as manually trying to hit special cases around first/last entries. Signed-off-by: Daniel Stone --- libweston/backend-drm/state-propose.c | 30 +++++++-------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index 72e4db93..062f4978 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -67,7 +67,7 @@ static void drm_output_add_zpos_plane(struct drm_plane *plane, struct wl_list *planes) { struct drm_backend *b = plane->backend; - struct drm_plane_zpos *h_plane; + struct drm_plane_zpos *tmp; struct drm_plane_zpos *plane_zpos; plane_zpos = zalloc(sizeof(*plane_zpos)); @@ -79,30 +79,14 @@ drm_output_add_zpos_plane(struct drm_plane *plane, struct wl_list *planes) drm_debug(b, "\t\t\t\t[plane] plane %d added to candidate list\n", plane->plane_id); - if (wl_list_empty(planes)) { - wl_list_insert(planes, &plane_zpos->link); - return; - } - - h_plane = wl_container_of(planes->next, h_plane, link); - if (h_plane->plane->zpos_max >= plane->zpos_max) { - wl_list_insert(planes->prev, &plane_zpos->link); - } else { - struct drm_plane_zpos *p_zpos = NULL; - - if (wl_list_length(planes) == 1) { - wl_list_insert(planes->prev, &plane_zpos->link); - return; - } - - wl_list_for_each(p_zpos, planes, link) { - if (p_zpos->plane->zpos_max > - plane_zpos->plane->zpos_max) - break; + wl_list_for_each(tmp, planes, link) { + if (tmp->plane->zpos_max > plane_zpos->plane->zpos_max) { + wl_list_insert(tmp->link.prev, &plane_zpos->link); + break; } - - wl_list_insert(p_zpos->link.prev, &plane_zpos->link); } + if (plane_zpos->link.next == NULL) + wl_list_insert(planes->prev, &plane_zpos->link); } static void From 6609840479934a1145372e8a850592eeffbdaf83 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 7 Dec 2021 15:55:42 +0000 Subject: [PATCH 003/609] backend-drm: Pre-sort plane list by zpos Rather than constructing a zpos-sorted list every time, just have plane_list be pre-sorted. Signed-off-by: Daniel Stone --- libweston/backend-drm/drm.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index 42787702..102fa2d7 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -760,7 +760,7 @@ init_pixman(struct drm_backend *b) static struct drm_plane * drm_plane_create(struct drm_backend *b, const drmModePlane *kplane) { - struct drm_plane *plane; + struct drm_plane *plane, *tmp; drmModeObjectProperties *props; uint64_t *zpos_range_values; @@ -817,7 +817,15 @@ drm_plane_create(struct drm_backend *b, const drmModePlane *kplane) goto err_props; weston_plane_init(&plane->base, b->compositor, 0, 0); - wl_list_insert(&b->plane_list, &plane->link); + + wl_list_for_each(tmp, &b->plane_list, link) { + if (tmp->zpos_max > plane->zpos_max) { + wl_list_insert(tmp->link.prev, &plane->link); + break; + } + } + if (plane->link.next == NULL) + wl_list_insert(b->plane_list.prev, &plane->link); return plane; From af42fc1e336748b4e401e4441729dd79914425e6 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 7 Dec 2021 15:58:49 +0000 Subject: [PATCH 004/609] backend-drm: Assign plane_idx by plane list order Signed-off-by: Daniel Stone --- libweston/backend-drm/drm-internal.h | 1 - libweston/backend-drm/drm.c | 5 ++++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index 55e9414b..86d7368e 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -276,7 +276,6 @@ struct drm_backend { int min_height, max_height; struct wl_list plane_list; - uint32_t next_plane_idx; void *repaint_data; diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index 102fa2d7..203b8a4e 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -771,7 +771,6 @@ drm_plane_create(struct drm_backend *b, const drmModePlane *kplane) } plane->backend = b; - plane->plane_idx = b->next_plane_idx++; plane->state_cur = drm_plane_state_alloc(NULL, plane); plane->state_cur->complete = true; plane->possible_crtcs = kplane->possible_crtcs; @@ -919,6 +918,7 @@ create_sprites(struct drm_backend *b) drmModePlane *kplane; struct drm_plane *drm_plane; uint32_t i; + uint32_t next_plane_idx = 0; kplane_res = drmModeGetPlaneResources(b->drm.fd); if (!kplane_res) { weston_log("failed to get plane resources: %s\n", @@ -942,6 +942,9 @@ create_sprites(struct drm_backend *b) &b->compositor->primary_plane); } + wl_list_for_each (drm_plane, &b->plane_list, link) + drm_plane->plane_idx = next_plane_idx++; + drmModeFreePlaneResources(kplane_res); } From 075c4ac286e3b625e65b435f095d191fb9796333 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 16 Dec 2021 16:16:42 +0000 Subject: [PATCH 005/609] backend-drm: Don't try to use planes without GBM GBM is how we import all our client content into DRM FBs, so don't try anything other than renderer-only without it. Signed-off-by: Daniel Stone --- libweston/backend-drm/state-propose.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index 062f4978..df495020 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -940,6 +940,12 @@ drm_output_propose_state(struct weston_output *output_base, force_renderer = true; } + if (!b->gbm) { + drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane " + "(GBM not available)\n", ev); + force_renderer = true; + } + if (!weston_view_has_valid_buffer(ev)) { drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane " "(no buffer available)\n", ev); @@ -1085,7 +1091,7 @@ drm_assign_planes(struct weston_output *output_base, void *repaint_data) drm_debug(b, "\t[repaint] preparing state for output %s (%lu)\n", output_base->name, (unsigned long) output_base->id); - if (!b->sprites_are_broken && !output->virtual) { + if (!b->sprites_are_broken && !output->virtual && b->gbm) { drm_debug(b, "\t[repaint] trying planes-only build state\n"); state = drm_output_propose_state(output_base, pending_state, mode); if (!state) { From d5ec9a1a1d1e295803cb45135c8f3a5adbe278cc Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 7 Dec 2021 16:20:29 +0000 Subject: [PATCH 006/609] backend-drm: Don't try to steal other-output special planes Each output is hardcoded to the use of a single 'special' (primary or cursor) plane; make sure we don't try to steal them from other outputs which might not be happy to discover that we've taken it off them. Signed-off-by: Daniel Stone --- libweston/backend-drm/state-propose.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index df495020..65b230f0 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -697,6 +697,16 @@ drm_output_prepare_plane_view(struct drm_output_state *state, /* assemble a list with possible candidates */ wl_list_for_each(plane, &b->plane_list, link) { + if (plane->type == WDRM_PLANE_TYPE_CURSOR && + plane != output->cursor_plane) { + continue; + } + + if (plane->type == WDRM_PLANE_TYPE_PRIMARY && + plane != output->scanout_plane) { + continue; + } + if (!drm_plane_is_available(plane, output)) continue; From 66244856e228902b62587c9171374e85c9cf769d Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 7 Dec 2021 16:26:55 +0000 Subject: [PATCH 007/609] backend-drm: Don't try to import SHM buffers as drm_fb It won't work. Signed-off-by: Daniel Stone --- libweston/backend-drm/state-propose.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index 65b230f0..8e355511 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -681,7 +681,7 @@ drm_output_prepare_plane_view(struct drm_output_state *state, struct weston_buffer *buffer; struct wl_shm_buffer *shmbuf; - struct drm_fb *fb; + struct drm_fb *fb = NULL; wl_list_init(&zpos_candidate_list); @@ -691,7 +691,8 @@ drm_output_prepare_plane_view(struct drm_output_state *state, buffer = ev->surface->buffer_ref.buffer; shmbuf = wl_shm_buffer_get(buffer->resource); - fb = drm_fb_get_from_view(state, ev, try_view_on_plane_failure_reasons); + if (!shmbuf) + fb = drm_fb_get_from_view(state, ev, try_view_on_plane_failure_reasons); if (!shmbuf && !fb) return NULL; From ca4c2865e9adf7470a470fe08a8a7372f4d17e4e Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 7 Dec 2021 16:42:02 +0000 Subject: [PATCH 008/609] backend-drm: Early-out for cursor plane format testing If we have a SHM buffer, it can only go into a cursor plane - and only then if it's of the right format. Signed-off-by: Daniel Stone --- libweston/backend-drm/state-propose.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index 8e355511..f5f63feb 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -691,15 +691,25 @@ drm_output_prepare_plane_view(struct drm_output_state *state, buffer = ev->surface->buffer_ref.buffer; shmbuf = wl_shm_buffer_get(buffer->resource); - if (!shmbuf) + if (shmbuf) { + if (!output->cursor_plane) + return NULL; + if (wl_shm_buffer_get_format(shmbuf) != WL_SHM_FORMAT_ARGB8888) { + drm_debug(b, "\t\t\t\t[view] not placing view %p on " + "plane; SHM buffers must be ARGB8888 for " + "cursor view", ev); + return NULL; + } + } else { fb = drm_fb_get_from_view(state, ev, try_view_on_plane_failure_reasons); + } if (!shmbuf && !fb) return NULL; /* assemble a list with possible candidates */ wl_list_for_each(plane, &b->plane_list, link) { if (plane->type == WDRM_PLANE_TYPE_CURSOR && - plane != output->cursor_plane) { + (plane != output->cursor_plane || !shmbuf)) { continue; } @@ -749,15 +759,6 @@ drm_output_prepare_plane_view(struct drm_output_state *state, continue; } - if (plane->type == WDRM_PLANE_TYPE_CURSOR && - (!shmbuf || wl_shm_buffer_get_format(shmbuf) != WL_SHM_FORMAT_ARGB8888)) { - drm_debug(b, "\t\t\t\t[plane] not adding plane %d, type cursor to " - "candidate list: cursor planes only support ARGB8888" - "wl_shm buffers and the view buffer is of another type\n", - plane->plane_id); - continue; - } - if (plane->type != WDRM_PLANE_TYPE_CURSOR && (!fb || !(fb->plane_mask & (1 << plane->plane_idx)))) { *try_view_on_plane_failure_reasons |= From 6aec64b2f76f920afb7401fbdebbfda9efcffd0c Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 7 Dec 2021 16:45:39 +0000 Subject: [PATCH 009/609] backend-drm: Early-out for too-large SHM/cursor buffers We know what our limit is for cursor planes, so don't try to assign a view which is too large. Signed-off-by: Daniel Stone --- libweston/backend-drm/state-propose.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index f5f63feb..9ac6bde3 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -239,7 +239,6 @@ drm_output_prepare_cursor_view(struct drm_output_state *output_state, struct drm_plane *plane = output->cursor_plane; struct drm_plane_state *plane_state; bool needs_update = false; - struct weston_buffer *buffer = ev->surface->buffer_ref.buffer; const char *p_name = drm_output_get_plane_type_name(plane); assert(!b->cursors_are_broken); @@ -271,16 +270,6 @@ drm_output_prepare_cursor_view(struct drm_output_state *output_state, goto err; } - if (buffer->width > b->cursor_width || - buffer->height > b->cursor_height) { - drm_debug(b, "\t\t\t\t[%s] not assigning view %p to %s plane " - "(surface buffer (%dx%d) larger than permitted" - " (%dx%d))\n", p_name, ev, p_name, - buffer->width, buffer->height, - b->cursor_width, b->cursor_height); - goto err; - } - if (plane_state->src_x != 0 || plane_state->src_y != 0 || plane_state->src_w > (unsigned) b->cursor_width << 16 || plane_state->src_h > (unsigned) b->cursor_height << 16 || @@ -694,12 +683,21 @@ drm_output_prepare_plane_view(struct drm_output_state *state, if (shmbuf) { if (!output->cursor_plane) return NULL; + if (wl_shm_buffer_get_format(shmbuf) != WL_SHM_FORMAT_ARGB8888) { drm_debug(b, "\t\t\t\t[view] not placing view %p on " "plane; SHM buffers must be ARGB8888 for " "cursor view", ev); return NULL; } + + if (buffer->width > b->cursor_width || + buffer->height > b->cursor_height) { + drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane " + "(buffer (%dx%d) too large for cursor plane)", + ev, buffer->width, buffer->height); + return NULL; + } } else { fb = drm_fb_get_from_view(state, ev, try_view_on_plane_failure_reasons); } From 9c6a069435acf51db5654dcebe8e2d3fdfbed6b8 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 7 Dec 2021 16:50:58 +0000 Subject: [PATCH 010/609] backend-drm: Early-out for non-SHM buffers in renderer-only mode If we're in renderer-only mode, we can only use the renderer and the cursor plane. Don't even try to import client buffers as it makes no sense. Signed-off-by: Daniel Stone --- libweston/backend-drm/state-propose.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index 9ac6bde3..df6b143d 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -699,10 +699,16 @@ drm_output_prepare_plane_view(struct drm_output_state *state, return NULL; } } else { + if (mode == DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY) { + drm_debug(b, "\t\t\t\t[view] not assigning view %p " + "to plane: renderer-only mode\n", ev); + return NULL; + } + fb = drm_fb_get_from_view(state, ev, try_view_on_plane_failure_reasons); + if (!fb) + return NULL; } - if (!shmbuf && !fb) - return NULL; /* assemble a list with possible candidates */ wl_list_for_each(plane, &b->plane_list, link) { @@ -748,15 +754,6 @@ drm_output_prepare_plane_view(struct drm_output_state *state, } } - if (mode == DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY && - (plane->type == WDRM_PLANE_TYPE_OVERLAY || - plane->type == WDRM_PLANE_TYPE_PRIMARY)) { - drm_debug(b, "\t\t\t\t[plane] not adding plane %d to " - "candidate list: renderer-only mode\n", - plane->plane_id); - continue; - } - if (plane->type != WDRM_PLANE_TYPE_CURSOR && (!fb || !(fb->plane_mask & (1 << plane->plane_idx)))) { *try_view_on_plane_failure_reasons |= From 26c2f9a65f1fe984ed6d8c187375a6513527caa4 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 7 Dec 2021 17:30:39 +0000 Subject: [PATCH 011/609] backend-drm: Don't try cursor buffers for client planes For better or worse, cursor planes can only be used by uploaded SHM buffers right now, so ignore them when we're calculating the acceptable plane mask for client dmabufs. Signed-off-by: Daniel Stone --- libweston/backend-drm/fb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libweston/backend-drm/fb.c b/libweston/backend-drm/fb.c index ffe2cc52..8630ebad 100644 --- a/libweston/backend-drm/fb.c +++ b/libweston/backend-drm/fb.c @@ -581,6 +581,9 @@ drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev, /* Check if this buffer can ever go on any planes. If it can't, we have * no reason to ever have a drm_fb, so we fail it here. */ wl_list_for_each(plane, &b->plane_list, link) { + /* only SHM buffers can go into cursor planes */ + if (plane->type == WDRM_PLANE_TYPE_CURSOR) + continue; if (drm_fb_compatible_with_plane(fb, plane)) fb->plane_mask |= (1 << plane->plane_idx); } From 23257c073fa944bb9d50a8818bfd1253501e17a1 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 7 Dec 2021 17:32:01 +0000 Subject: [PATCH 012/609] backend-drm: Minor comment rewording Signed-off-by: Daniel Stone --- libweston/backend-drm/state-propose.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index df6b143d..83f6417b 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -726,18 +726,18 @@ drm_output_prepare_plane_view(struct drm_output_state *state, continue; if (drm_output_check_plane_has_view_assigned(plane, state)) { - drm_debug(b, "\t\t\t\t[plane] not adding plane %d to" - " candidate list: view already assigned " - "to a plane\n", plane->plane_id); + drm_debug(b, "\t\t\t\t[plane] not trying plane %d: " + "another view already assigned", + plane->plane_id); continue; } if (plane->zpos_min >= current_lowest_zpos) { - drm_debug(b, "\t\t\t\t[plane] not adding plane %d to " - "candidate list: minimum zpos (%"PRIu64") " - "plane's above current lowest zpos " - "(%"PRIu64")\n", plane->plane_id, - plane->zpos_min, current_lowest_zpos); + drm_debug(b, "\t\t\t\t[plane] not trying plane %d: " + "plane's minimum zpos (%"PRIu64") above " + "current lowest zpos (%"PRIu64")\n", + plane->plane_id, plane->zpos_min, + current_lowest_zpos); continue; } @@ -758,9 +758,8 @@ drm_output_prepare_plane_view(struct drm_output_state *state, (!fb || !(fb->plane_mask & (1 << plane->plane_idx)))) { *try_view_on_plane_failure_reasons |= FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE; - drm_debug(b, "\t\t\t\t[plane] not adding plane %d to " - "candidate list: invalid pixel format\n", - plane->plane_id); + drm_debug(b, "\t\t\t\t[plane] not trying plane %d: " + "invalid pixel format\n", plane->plane_id); continue; } From e5ad3c886581aff658ee2db5fff35ba6791138bd Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 7 Dec 2021 17:32:14 +0000 Subject: [PATCH 013/609] backend-drm: Remove separate zpos_plane list When we introduced support for variable zpos, we did so by filtering the list of acceptable planes and then creating a separate zpos-ordered list. Now that the planes are already zpos-sorted in the backend list, and we have more early filtering, we can replace this with a single plane-list walk. Signed-off-by: Daniel Stone --- libweston/backend-drm/drm-internal.h | 10 ---- libweston/backend-drm/state-propose.c | 82 ++++++--------------------- 2 files changed, 18 insertions(+), 74 deletions(-) diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index 86d7368e..bbc86a17 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -394,16 +394,6 @@ struct drm_output_state { struct wl_list plane_list; }; -/** - * An instance of this class is created each time we believe we have a plane - * suitable to be used by a view as a direct scan-out. The list is initialized - * and populated locally. - */ -struct drm_plane_zpos { - struct drm_plane *plane; - struct wl_list link; /**< :candidate_plane_zpos_list */ -}; - /** * Plane state holds the dynamic state for a plane: where it is positioned, * and which buffer it is currently displaying. diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index 83f6417b..e816b5c2 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -63,39 +63,6 @@ drm_propose_state_mode_to_string(enum drm_output_propose_state_mode mode) return drm_output_propose_state_mode_as_string[mode]; } -static void -drm_output_add_zpos_plane(struct drm_plane *plane, struct wl_list *planes) -{ - struct drm_backend *b = plane->backend; - struct drm_plane_zpos *tmp; - struct drm_plane_zpos *plane_zpos; - - plane_zpos = zalloc(sizeof(*plane_zpos)); - if (!plane_zpos) - return; - - plane_zpos->plane = plane; - - drm_debug(b, "\t\t\t\t[plane] plane %d added to candidate list\n", - plane->plane_id); - - wl_list_for_each(tmp, planes, link) { - if (tmp->plane->zpos_max > plane_zpos->plane->zpos_max) { - wl_list_insert(tmp->link.prev, &plane_zpos->link); - break; - } - } - if (plane_zpos->link.next == NULL) - wl_list_insert(planes->prev, &plane_zpos->link); -} - -static void -drm_output_destroy_zpos_plane(struct drm_plane_zpos *plane_zpos) -{ - wl_list_remove(&plane_zpos->link); - free(plane_zpos); -} - static bool drm_output_check_plane_has_view_assigned(struct drm_plane *plane, struct drm_output_state *output_state) @@ -665,14 +632,12 @@ drm_output_prepare_plane_view(struct drm_output_state *state, struct drm_plane_state *ps = NULL; struct drm_plane *plane; - struct drm_plane_zpos *p_zpos, *p_zpos_next; - struct wl_list zpos_candidate_list; struct weston_buffer *buffer; struct wl_shm_buffer *shmbuf; struct drm_fb *fb = NULL; - wl_list_init(&zpos_candidate_list); + uint32_t possible_plane_mask = 0; /* check view for valid buffer, doesn't make sense to even try */ if (!weston_view_has_valid_buffer(ev)) @@ -698,6 +663,8 @@ drm_output_prepare_plane_view(struct drm_output_state *state, ev, buffer->width, buffer->height); return NULL; } + + possible_plane_mask = (1 << output->cursor_plane->plane_idx); } else { if (mode == DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY) { drm_debug(b, "\t\t\t\t[view] not assigning view %p " @@ -708,10 +675,23 @@ drm_output_prepare_plane_view(struct drm_output_state *state, fb = drm_fb_get_from_view(state, ev, try_view_on_plane_failure_reasons); if (!fb) return NULL; + + possible_plane_mask = fb->plane_mask; } /* assemble a list with possible candidates */ wl_list_for_each(plane, &b->plane_list, link) { + const char *p_name = drm_output_get_plane_type_name(plane); + uint64_t zpos; + + if (possible_plane_mask == 0) + break; + + if (!(possible_plane_mask & (1 << plane->plane_idx))) + continue; + + possible_plane_mask &= ~(1 << plane->plane_idx); + if (plane->type == WDRM_PLANE_TYPE_CURSOR && (plane != output->cursor_plane || !shmbuf)) { continue; @@ -754,30 +734,6 @@ drm_output_prepare_plane_view(struct drm_output_state *state, } } - if (plane->type != WDRM_PLANE_TYPE_CURSOR && - (!fb || !(fb->plane_mask & (1 << plane->plane_idx)))) { - *try_view_on_plane_failure_reasons |= - FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE; - drm_debug(b, "\t\t\t\t[plane] not trying plane %d: " - "invalid pixel format\n", plane->plane_id); - continue; - } - - drm_output_add_zpos_plane(plane, &zpos_candidate_list); - } - - /* go over the potential candidate list and try to find a possible - * plane suitable for \c ev; start with the highest zpos value of a - * plane to maximize our chances, but do note we pass the zpos value - * based on current tracked value by \c current_lowest_zpos_in_use */ - while (!wl_list_empty(&zpos_candidate_list)) { - struct drm_plane_zpos *head_p_zpos = - wl_container_of(zpos_candidate_list.next, - head_p_zpos, link); - struct drm_plane *plane = head_p_zpos->plane; - const char *p_name = drm_output_get_plane_type_name(plane); - uint64_t zpos; - if (current_lowest_zpos == DRM_PLANE_ZPOS_INVALID_PLANE) zpos = plane->zpos_max; else @@ -789,7 +745,6 @@ drm_output_prepare_plane_view(struct drm_output_state *state, ps = drm_output_try_view_on_plane(plane, state, ev, mode, fb, zpos); - drm_output_destroy_zpos_plane(head_p_zpos); if (ps) { drm_debug(b, "\t\t\t\t[view] view %p has been placed to " "%s plane with computed zpos %"PRIu64"\n", @@ -798,9 +753,8 @@ drm_output_prepare_plane_view(struct drm_output_state *state, } } - wl_list_for_each_safe(p_zpos, p_zpos_next, &zpos_candidate_list, link) - drm_output_destroy_zpos_plane(p_zpos); - + /* if we have a plane state, it has its own ref to the fb; if not then + * we drop ours here */ drm_fb_unref(fb); return ps; } From ae60745b6102c9c877feb06074e3c5c9c7a90f5f Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 7 Dec 2021 17:37:45 +0000 Subject: [PATCH 014/609] backend-drm: Move cursors_are_broken test earlier No point trying to place a cursor buffer on a plane when we can't do cursor planes. Signed-off-by: Daniel Stone --- libweston/backend-drm/state-propose.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index e816b5c2..39aa9fbc 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -406,11 +406,6 @@ drm_output_try_view_on_plane(struct drm_plane *plane, switch (plane->type) { case WDRM_PLANE_TYPE_CURSOR: - if (b->cursors_are_broken) { - availability = NO_PLANES_ACCEPTED; - goto out; - } - ps = drm_output_prepare_cursor_view(state, ev, zpos); if (ps) availability = PLACED_ON_PLANE; @@ -646,7 +641,7 @@ drm_output_prepare_plane_view(struct drm_output_state *state, buffer = ev->surface->buffer_ref.buffer; shmbuf = wl_shm_buffer_get(buffer->resource); if (shmbuf) { - if (!output->cursor_plane) + if (!output->cursor_plane || b->cursors_are_broken) return NULL; if (wl_shm_buffer_get_format(shmbuf) != WL_SHM_FORMAT_ARGB8888) { From 0ecd6c3d3312d9e56122ef02e29a06acb1172fa4 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 7 Dec 2021 17:39:19 +0000 Subject: [PATCH 015/609] backend-drm: Move renderer-only vs. scanout_plane test earlier No point trying to put something on the scanout plane in mixed mode. Signed-off-by: Daniel Stone --- libweston/backend-drm/state-propose.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index 39aa9fbc..39220fbd 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -431,11 +431,6 @@ drm_output_try_view_on_plane(struct drm_plane *plane, availability = PLACED_ON_PLANE; break; case WDRM_PLANE_TYPE_PRIMARY: - if (mode != DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY) { - availability = NO_PLANES_ACCEPTED; - goto out; - } - ps = drm_output_prepare_scanout_view(state, ev, mode, fb, zpos); if (ps) @@ -693,7 +688,8 @@ drm_output_prepare_plane_view(struct drm_output_state *state, } if (plane->type == WDRM_PLANE_TYPE_PRIMARY && - plane != output->scanout_plane) { + (plane != output->scanout_plane || + mode != DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY)) { continue; } From b3d7df5c3e58cd3b0d8928ad3cb70ba80fb95708 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 7 Dec 2021 17:43:55 +0000 Subject: [PATCH 016/609] backend-drm: Move plane-type-specific checks to switch statement This makes it a bit more clear and easy to follow, rather than diving through if nesting. Signed-off-by: Daniel Stone --- libweston/backend-drm/state-propose.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index 39220fbd..a022bc1b 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -682,15 +682,24 @@ drm_output_prepare_plane_view(struct drm_output_state *state, possible_plane_mask &= ~(1 << plane->plane_idx); - if (plane->type == WDRM_PLANE_TYPE_CURSOR && - (plane != output->cursor_plane || !shmbuf)) { - continue; - } - - if (plane->type == WDRM_PLANE_TYPE_PRIMARY && - (plane != output->scanout_plane || - mode != DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY)) { - continue; + switch (plane->type) { + case WDRM_PLANE_TYPE_CURSOR: + assert(shmbuf); + assert(plane == output->cursor_plane); + break; + case WDRM_PLANE_TYPE_PRIMARY: + assert(fb); + if (plane != output->scanout_plane) + continue; + if (mode != DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY) + continue; + break; + case WDRM_PLANE_TYPE_OVERLAY: + assert(fb); + assert(mode != DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY); + break; + default: + assert(false && "unknown plane type"); } if (!drm_plane_is_available(plane, output)) From 5e41b44b10107b8395f4cb36ddc4dcedd46d6408 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 7 Dec 2021 17:46:26 +0000 Subject: [PATCH 017/609] backend-drm: Change cursor checks to asserts We shouldn't get down into trying to place a view on a cursor plane if these checks are not met, so change them to asserts rather than early returns. Signed-off-by: Daniel Stone --- libweston/backend-drm/state-propose.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index a022bc1b..a2bbe6ec 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -209,24 +209,15 @@ drm_output_prepare_cursor_view(struct drm_output_state *output_state, const char *p_name = drm_output_get_plane_type_name(plane); assert(!b->cursors_are_broken); - - if (!plane) - return NULL; - - if (!plane->state_cur->complete) - return NULL; - - if (plane->state_cur->output && plane->state_cur->output != output) - return NULL; + assert(plane); + assert(plane->state_cur->complete); + assert(!plane->state_cur->output || plane->state_cur->output == output); /* We use GBM to import SHM buffers. */ - if (b->gbm == NULL) - return NULL; + assert(b->gbm); plane_state = drm_output_state_get_plane(output_state, plane); - - if (plane_state && plane_state->fb) - return NULL; + assert(!plane_state->fb); /* We can't scale with the legacy API, and we don't try to account for * simple cropping/translation in cursor_bo_update. */ From dc0de9ee2b24d4d99ee4e6ff527efd9541dd2948 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 9 Dec 2021 16:14:02 +0000 Subject: [PATCH 018/609] backend-drm: Move overlay vs. primary plane check earlier For views which cover the entire output, we always attempt to place them on the primary plane, to avoid a situation where we place a fullscreen view into an overlay plane and then have to disable the primary plane, which doesn't always work. Move this check earlier, so we don't consider overlay planes to be candidates for fullscreen views. This check should be changed in future to only filter for opaque views, but that's for another time. Signed-off-by: Daniel Stone --- libweston/backend-drm/state-propose.c | 30 +++++++++++---------------- 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index a2bbe6ec..1c144276 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -378,9 +378,6 @@ drm_output_try_view_on_plane(struct drm_plane *plane, struct drm_fb *fb, uint64_t zpos) { struct drm_backend *b = state->pending_state->backend; - struct weston_output *wet_output = &state->output->base; - bool view_matches_entire_output, scanout_has_view_assigned; - struct drm_plane *scanout_plane = state->output->scanout_plane; struct drm_plane_state *ps = NULL; const char *p_name = drm_output_get_plane_type_name(plane); struct weston_surface *surface = ev->surface; @@ -402,20 +399,6 @@ drm_output_try_view_on_plane(struct drm_plane *plane, availability = PLACED_ON_PLANE; break; case WDRM_PLANE_TYPE_OVERLAY: - /* do not attempt to place it in the overlay if we don't have - * anything in the scanout/primary and the view doesn't cover - * the entire output */ - view_matches_entire_output = - weston_view_matches_output_entirely(ev, wet_output); - scanout_has_view_assigned = - drm_output_check_plane_has_view_assigned(scanout_plane, - state); - - if (view_matches_entire_output && !scanout_has_view_assigned) { - availability = NO_PLANES_ACCEPTED; - goto out; - } - ps = drm_output_prepare_overlay_view(plane, state, ev, mode, fb, zpos); if (ps) @@ -432,7 +415,6 @@ drm_output_try_view_on_plane(struct drm_plane *plane, break; } -out: switch (availability) { case NO_PLANES: /* set initial to this catch-all case, such that @@ -618,6 +600,7 @@ drm_output_prepare_plane_view(struct drm_output_state *state, struct wl_shm_buffer *shmbuf; struct drm_fb *fb = NULL; + bool view_matches_entire_output, scanout_has_view_assigned; uint32_t possible_plane_mask = 0; /* check view for valid buffer, doesn't make sense to even try */ @@ -660,6 +643,12 @@ drm_output_prepare_plane_view(struct drm_output_state *state, possible_plane_mask = fb->plane_mask; } + view_matches_entire_output = + weston_view_matches_output_entirely(ev, &output->base); + scanout_has_view_assigned = + drm_output_check_plane_has_view_assigned(output->scanout_plane, + state); + /* assemble a list with possible candidates */ wl_list_for_each(plane, &b->plane_list, link) { const char *p_name = drm_output_get_plane_type_name(plane); @@ -688,6 +677,11 @@ drm_output_prepare_plane_view(struct drm_output_state *state, case WDRM_PLANE_TYPE_OVERLAY: assert(fb); assert(mode != DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY); + /* if the view covers the whole output, put it in the + * scanout plane, not overlay */ + if (view_matches_entire_output && + !scanout_has_view_assigned) + continue; break; default: assert(false && "unknown plane type"); From 6b828c7b5748871712170c7d8a164e08099c21b1 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 9 Dec 2021 16:16:40 +0000 Subject: [PATCH 019/609] backend-drm: Don't try non-fullscreen views on the primary plane You'd think this would go without saying, but no, we just sort of buried that. Signed-off-by: Daniel Stone --- libweston/backend-drm/state-propose.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index 1c144276..8b361f1a 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -308,15 +308,6 @@ drm_output_prepare_scanout_view(struct drm_output_state *output_state, assert(b->atomic_modeset); assert(mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY); - /* Check the view spans exactly the output size, calculated in the - * logical co-ordinate space. */ - if (!weston_view_matches_output_entirely(ev, &output->base)) { - drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: " - " view does not match output entirely\n", - p_name, ev, p_name); - return NULL; - } - /* If the surface buffer has an in-fence fd, but the plane doesn't * support fences, we can't place the buffer on this plane. */ if (ev->surface->acquire_fence_fd >= 0 && @@ -673,6 +664,8 @@ drm_output_prepare_plane_view(struct drm_output_state *state, continue; if (mode != DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY) continue; + if (!view_matches_entire_output) + continue; break; case WDRM_PLANE_TYPE_OVERLAY: assert(fb); From 1b34c5cd807252c478e5eb6a7c90dd983be4361e Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 9 Dec 2021 16:18:19 +0000 Subject: [PATCH 020/609] backend-drm: Remove unnecessary check in prepare_scanout_view We already guarantee this from the caller. Signed-off-by: Daniel Stone --- libweston/backend-drm/state-propose.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index 8b361f1a..c2d6d3eb 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -307,6 +307,7 @@ drm_output_prepare_scanout_view(struct drm_output_state *output_state, assert(!b->sprites_are_broken); assert(b->atomic_modeset); assert(mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY); + assert(fb); /* If the surface buffer has an in-fence fd, but the plane doesn't * support fences, we can't place the buffer on this plane. */ @@ -317,12 +318,6 @@ drm_output_prepare_scanout_view(struct drm_output_state *output_state, return NULL; } - if (!fb) { - drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: " - " couldn't get fb\n", p_name, ev, p_name); - return NULL; - } - state = drm_output_state_get_plane(output_state, scanout_plane); /* The only way we can already have a buffer in the scanout plane is From 2dd3af3c223a7c4a149b9d263f0724bec648f45a Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 9 Dec 2021 16:20:49 +0000 Subject: [PATCH 021/609] backend-drm: Move IN_FENCE_FD check to common code No need for this to be specialised within both overlay and scanout plane paths. Signed-off-by: Daniel Stone --- libweston/backend-drm/state-propose.c | 30 ++++++++------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index c2d6d3eb..c721a3a9 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -112,18 +112,6 @@ drm_output_prepare_overlay_view(struct drm_plane *plane, goto out; } - /* If the surface buffer has an in-fence fd, but the plane - * doesn't support fences, we can't place the buffer on this - * plane. */ - if (ev->surface->acquire_fence_fd >= 0 && - plane->props[WDRM_PLANE_IN_FENCE_FD].prop_id == 0) { - drm_debug(b, "\t\t\t\t[overlay] not placing view %p on overlay: " - "no in-fence support\n", ev); - drm_plane_state_put_back(state); - state = NULL; - goto out; - } - /* We hold one reference for the lifetime of this function; from * calling drm_fb_get_from_view() in drm_output_prepare_plane_view(), * so, we take another reference here to live within the state. */ @@ -309,15 +297,6 @@ drm_output_prepare_scanout_view(struct drm_output_state *output_state, assert(mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY); assert(fb); - /* If the surface buffer has an in-fence fd, but the plane doesn't - * support fences, we can't place the buffer on this plane. */ - if (ev->surface->acquire_fence_fd >= 0 && - scanout_plane->props[WDRM_PLANE_IN_FENCE_FD].prop_id == 0) { - drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: " - "no in-fence support\n", p_name, ev, p_name); - return NULL; - } - state = drm_output_state_get_plane(output_state, scanout_plane); /* The only way we can already have a buffer in the scanout plane is @@ -694,6 +673,15 @@ drm_output_prepare_plane_view(struct drm_output_state *state, continue; } + /* If the surface buffer has an in-fence fd, but the plane doesn't + * support fences, we can't place the buffer on this plane. */ + if (ev->surface->acquire_fence_fd >= 0 && + plane->props[WDRM_PLANE_IN_FENCE_FD].prop_id == 0) { + drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: " + "no in-fence support\n", p_name, ev, p_name); + return NULL; + } + if (mode == DRM_OUTPUT_PROPOSE_STATE_MIXED) { assert(scanout_state != NULL); if (scanout_state->zpos >= plane->zpos_max) { From e1114228f5ba6cddd302d98e65e2c641a614674a Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 9 Dec 2021 16:23:08 +0000 Subject: [PATCH 022/609] backend-drm: Remove unused enum At some point this got hobbled, such that NO_PLANES and NO_PLANES_ACCEPTED became the same thing, so we can just check if the returned plane_state is NULL or not. Signed-off-by: Daniel Stone --- libweston/backend-drm/state-propose.c | 29 +++++---------------------- 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index c721a3a9..233ba7af 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -346,11 +346,6 @@ drm_output_try_view_on_plane(struct drm_plane *plane, struct drm_plane_state *ps = NULL; const char *p_name = drm_output_get_plane_type_name(plane); struct weston_surface *surface = ev->surface; - enum { - NO_PLANES, /* generic err-handle */ - NO_PLANES_ACCEPTED, - PLACED_ON_PLANE, - } availability = NO_PLANES; /* sanity checks in case we over/underflow zpos or pass incorrect * values */ @@ -360,38 +355,21 @@ drm_output_try_view_on_plane(struct drm_plane *plane, switch (plane->type) { case WDRM_PLANE_TYPE_CURSOR: ps = drm_output_prepare_cursor_view(state, ev, zpos); - if (ps) - availability = PLACED_ON_PLANE; break; case WDRM_PLANE_TYPE_OVERLAY: ps = drm_output_prepare_overlay_view(plane, state, ev, mode, fb, zpos); - if (ps) - availability = PLACED_ON_PLANE; break; case WDRM_PLANE_TYPE_PRIMARY: ps = drm_output_prepare_scanout_view(state, ev, mode, fb, zpos); - if (ps) - availability = PLACED_ON_PLANE; break; default: assert(0); break; } - switch (availability) { - case NO_PLANES: - /* set initial to this catch-all case, such that - * prepare_cursor/overlay/scanout() should have/contain the - * reason for failling */ - break; - case NO_PLANES_ACCEPTED: - drm_debug(b, "\t\t\t\t[plane] plane %d refusing to " - "place view %p in %s\n", - plane->plane_id, ev, p_name); - break; - case PLACED_ON_PLANE: + if (ps) { /* Take a reference on the buffer so that we don't release it * back to the client until we're done with it; cursor buffers * don't require a reference since we copy them. */ @@ -406,7 +384,10 @@ drm_output_try_view_on_plane(struct drm_plane *plane, weston_buffer_release_reference(&ps->fb_ref.release, surface->buffer_release_ref.buffer_release); } - break; + } else { + drm_debug(b, "\t\t\t\t[plane] plane %d refusing to " + "place view %p in %s\n", + plane->plane_id, ev, p_name); } From 873e32137ebbe9df0f216ef76da987d05866bda9 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 9 Dec 2021 16:25:14 +0000 Subject: [PATCH 023/609] backend-drm: Remove unnecessary check for fb Signed-off-by: Daniel Stone --- libweston/backend-drm/state-propose.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index 233ba7af..d15f6146 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -90,12 +90,7 @@ drm_output_prepare_overlay_view(struct drm_plane *plane, assert(!b->sprites_are_broken); assert(b->atomic_modeset); - - if (!fb) { - drm_debug(b, "\t\t\t\t[overlay] not placing view %p on overlay: " - " couldn't get fb\n", ev); - return NULL; - } + assert(fb); state = drm_output_state_get_plane(output_state, plane); /* we can't have a 'pending' framebuffer as never set one before reaching here */ From 81e74ff33443e2a30b2e9b4cbf0cec71109080c6 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 9 Dec 2021 16:41:14 +0000 Subject: [PATCH 024/609] backend-drm: Don't take buffer-release reference for cursor views We just copy the SHM buffer straight into a separately-allocated GBM BO, so no need to take a reference on the buffer itself or keep it from being released. All drm_output_try_view_on_plane really does at this point is to call the prepare_*_view function for the requisite plane type, and take a ref on the weston_buffer from the client. Given that we don't need to keep the client buffer alive, we can short-circuit drm_output_try_view_on_plane, and instead just call drm_output_prepare_cursor_view directly when we have a cursor plane. This also makes it easier to just remove drm_output_try_view_on_plane in following patches when we merge the overlay/scanout plane path into one. Doing so gives us two clearly-separated paths: one for copying a SHM client buffer into a cursor, and another for directly scanning out client content. Signed-off-by: Daniel Stone --- libweston/backend-drm/state-propose.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index d15f6146..7e5b93ce 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -348,9 +348,6 @@ drm_output_try_view_on_plane(struct drm_plane *plane, zpos != DRM_PLANE_ZPOS_INVALID_PLANE); switch (plane->type) { - case WDRM_PLANE_TYPE_CURSOR: - ps = drm_output_prepare_cursor_view(state, ev, zpos); - break; case WDRM_PLANE_TYPE_OVERLAY: ps = drm_output_prepare_overlay_view(plane, state, ev, mode, fb, zpos); @@ -680,8 +677,12 @@ drm_output_prepare_plane_view(struct drm_output_state *state, "from candidate list, type: %s\n", plane->plane_id, p_name); - ps = drm_output_try_view_on_plane(plane, state, ev, - mode, fb, zpos); + if (plane->type == WDRM_PLANE_TYPE_CURSOR) { + ps = drm_output_prepare_cursor_view(state, ev, zpos); + } else { + ps = drm_output_try_view_on_plane(plane, state, ev, + mode, fb, zpos); + } if (ps) { drm_debug(b, "\t\t\t\t[view] view %p has been placed to " "%s plane with computed zpos %"PRIu64"\n", From 0ace8b66afdf4d923b123dba282581c6c40afff4 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 9 Dec 2021 16:46:39 +0000 Subject: [PATCH 025/609] backend-drm: Unify overlay/primary view->plane code There's no real reason for these to be separate now that the eligibility checks have been moved up so we don't call them unless it makes sense. Signed-off-by: Daniel Stone --- libweston/backend-drm/state-propose.c | 195 ++++++-------------------- 1 file changed, 46 insertions(+), 149 deletions(-) diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index 7e5b93ce..8697170f 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -76,70 +76,78 @@ drm_output_check_plane_has_view_assigned(struct drm_plane *plane, } static struct drm_plane_state * -drm_output_prepare_overlay_view(struct drm_plane *plane, - struct drm_output_state *output_state, - struct weston_view *ev, - enum drm_output_propose_state_mode mode, - struct drm_fb *fb, uint64_t zpos) +drm_output_try_view_on_plane(struct drm_plane *plane, + struct drm_output_state *output_state, + struct weston_view *ev, + enum drm_output_propose_state_mode mode, + struct drm_fb *fb, uint64_t zpos) { struct drm_output *output = output_state->output; struct weston_compositor *ec = output->base.compositor; + struct weston_surface *surface = ev->surface; struct drm_backend *b = to_drm_backend(ec); struct drm_plane_state *state = NULL; - int ret; assert(!b->sprites_are_broken); assert(b->atomic_modeset); assert(fb); + assert(mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY || + (mode == DRM_OUTPUT_PROPOSE_STATE_MIXED && + plane->type == WDRM_PLANE_TYPE_OVERLAY)); state = drm_output_state_get_plane(output_state, plane); /* we can't have a 'pending' framebuffer as never set one before reaching here */ assert(!state->fb); - - state->ev = ev; state->output = output; if (!drm_plane_state_coords_for_view(state, ev, zpos)) { - drm_debug(b, "\t\t\t\t[overlay] not placing view %p on overlay: " + drm_debug(b, "\t\t\t\t[view] not placing view %p on plane: " "unsuitable transform\n", ev); - drm_plane_state_put_back(state); - state = NULL; goto out; } + /* Should've been ensured by weston_view_matches_entire_output. */ + if (plane->type == WDRM_PLANE_TYPE_PRIMARY) { + assert(state->dest_x == 0 && state->dest_y == 0 && + state->dest_w == (unsigned) output->base.current_mode->width && + state->dest_h == (unsigned) output->base.current_mode->height); + } + /* We hold one reference for the lifetime of this function; from * calling drm_fb_get_from_view() in drm_output_prepare_plane_view(), * so, we take another reference here to live within the state. */ + state->ev = ev; state->fb = drm_fb_ref(fb); - state->in_fence_fd = ev->surface->acquire_fence_fd; /* In planes-only mode, we don't have an incremental state to * test against, so we just hope it'll work. */ - if (mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY) { - drm_debug(b, "\t\t\t[overlay] provisionally placing " - "view %p on overlay %lu in planes-only mode\n", + if (mode != DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY && + drm_pending_state_test(output_state->pending_state) != 0) { + drm_debug(b, "\t\t\t[view] not placing view %p on plane %lu: " + "atomic test failed\n", ev, (unsigned long) plane->plane_id); goto out; } - ret = drm_pending_state_test(output_state->pending_state); - if (ret == 0) { - drm_debug(b, "\t\t\t[overlay] provisionally placing " - "view %p on overlay %d in mixed mode\n", - ev, plane->plane_id); - goto out; - } - - drm_debug(b, "\t\t\t[overlay] not placing view %p on overlay %lu " - "in mixed mode: kernel test failed\n", + drm_debug(b, "\t\t\t[view] provisionally placing view %p on plane %lu\n", ev, (unsigned long) plane->plane_id); - drm_plane_state_put_back(state); - state = NULL; + /* Take a reference on the buffer so that we don't release it + * back to the client until we're done with it; cursor buffers + * don't require a reference since we copy them. */ + assert(state->fb_ref.buffer.buffer == NULL); + assert(state->fb_ref.release.buffer_release == NULL); + weston_buffer_reference(&state->fb_ref.buffer, + surface->buffer_ref.buffer); + weston_buffer_release_reference(&state->fb_ref.release, + surface->buffer_release_ref.buffer_release); -out: return state; + +out: + drm_plane_state_put_back(state); + return NULL; } #ifdef BUILD_DRM_GBM @@ -275,117 +283,6 @@ drm_output_prepare_cursor_view(struct drm_output_state *output_state, } #endif -static struct drm_plane_state * -drm_output_prepare_scanout_view(struct drm_output_state *output_state, - struct weston_view *ev, - enum drm_output_propose_state_mode mode, - struct drm_fb *fb, uint64_t zpos) -{ - struct drm_output *output = output_state->output; - struct drm_backend *b = to_drm_backend(output->base.compositor); - struct drm_plane *scanout_plane = output->scanout_plane; - struct drm_plane_state *state; - const char *p_name = drm_output_get_plane_type_name(scanout_plane); - - assert(!b->sprites_are_broken); - assert(b->atomic_modeset); - assert(mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY); - assert(fb); - - state = drm_output_state_get_plane(output_state, scanout_plane); - - /* The only way we can already have a buffer in the scanout plane is - * if we are in mixed mode, or if a client buffer has already been - * placed into scanout. The former case will never call into here, - * and in the latter case, the view must have been marked as occluded, - * meaning we should never have ended up here. */ - assert(!state->fb); - - /* take another reference here to live within the state */ - state->fb = drm_fb_ref(fb); - state->ev = ev; - state->output = output; - if (!drm_plane_state_coords_for_view(state, ev, zpos)) { - drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: " - "unsuitable transform\n", p_name, ev, p_name); - goto err; - } - - if (state->dest_x != 0 || state->dest_y != 0 || - state->dest_w != (unsigned) output->base.current_mode->width || - state->dest_h != (unsigned) output->base.current_mode->height) { - drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: " - " invalid plane state\n", p_name, ev, p_name); - goto err; - } - - state->in_fence_fd = ev->surface->acquire_fence_fd; - - /* In plane-only mode, we don't need to test the state now, as we - * will only test it once at the end. */ - return state; - -err: - drm_plane_state_put_back(state); - return NULL; -} - -static struct drm_plane_state * -drm_output_try_view_on_plane(struct drm_plane *plane, - struct drm_output_state *state, - struct weston_view *ev, - enum drm_output_propose_state_mode mode, - struct drm_fb *fb, uint64_t zpos) -{ - struct drm_backend *b = state->pending_state->backend; - struct drm_plane_state *ps = NULL; - const char *p_name = drm_output_get_plane_type_name(plane); - struct weston_surface *surface = ev->surface; - - /* sanity checks in case we over/underflow zpos or pass incorrect - * values */ - assert(zpos <= plane->zpos_max || - zpos != DRM_PLANE_ZPOS_INVALID_PLANE); - - switch (plane->type) { - case WDRM_PLANE_TYPE_OVERLAY: - ps = drm_output_prepare_overlay_view(plane, state, ev, mode, - fb, zpos); - break; - case WDRM_PLANE_TYPE_PRIMARY: - ps = drm_output_prepare_scanout_view(state, ev, mode, - fb, zpos); - break; - default: - assert(0); - break; - } - - if (ps) { - /* Take a reference on the buffer so that we don't release it - * back to the client until we're done with it; cursor buffers - * don't require a reference since we copy them. */ - assert(ps->fb_ref.buffer.buffer == NULL); - assert(ps->fb_ref.release.buffer_release == NULL); - if (ps->plane->type == WDRM_PLANE_TYPE_CURSOR) { - assert(ps->fb->type == BUFFER_CURSOR); - } else if (fb->type == BUFFER_CLIENT || fb->type == BUFFER_DMABUF) { - assert(ps->fb == fb); - weston_buffer_reference(&ps->fb_ref.buffer, - surface->buffer_ref.buffer); - weston_buffer_release_reference(&ps->fb_ref.release, - surface->buffer_release_ref.buffer_release); - } - } else { - drm_debug(b, "\t\t\t\t[plane] plane %d refusing to " - "place view %p in %s\n", - plane->plane_id, ev, p_name); - } - - - return ps; -} - static void drm_output_check_zpos_plane_states(struct drm_output_state *state) { @@ -521,12 +418,12 @@ dmabuf_feedback_maybe_update(struct drm_backend *b, struct weston_view *ev, } static struct drm_plane_state * -drm_output_prepare_plane_view(struct drm_output_state *state, - struct weston_view *ev, - enum drm_output_propose_state_mode mode, - struct drm_plane_state *scanout_state, - uint64_t current_lowest_zpos, - uint32_t *try_view_on_plane_failure_reasons) +drm_output_find_plane_for_view(struct drm_output_state *state, + struct weston_view *ev, + enum drm_output_propose_state_mode mode, + struct drm_plane_state *scanout_state, + uint64_t current_lowest_zpos, + uint32_t *try_view_on_plane_failure_reasons) { struct drm_output *output = state->output; struct drm_backend *b = to_drm_backend(output->base.compositor); @@ -883,10 +780,10 @@ drm_output_propose_state(struct weston_output *output_base, if (!force_renderer) { drm_debug(b, "\t\t\t[plane] started with zpos %"PRIu64"\n", current_lowest_zpos); - ps = drm_output_prepare_plane_view(state, ev, mode, - scanout_state, - current_lowest_zpos, - &pnode->try_view_on_plane_failure_reasons); + ps = drm_output_find_plane_for_view(state, ev, mode, + scanout_state, + current_lowest_zpos, + &pnode->try_view_on_plane_failure_reasons); /* If we were able to place the view in a plane, set * failure reasons to none. */ if (ps) From a2c5709e71c9c4200c7d66b03ee0ed73af1d2462 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 9 Dec 2021 16:54:16 +0000 Subject: [PATCH 026/609] backend-drm: Pass paint node through to plane_state find This lets us clean up a bit of code. Signed-off-by: Daniel Stone --- libweston/backend-drm/state-propose.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index 8697170f..6feeade0 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -419,11 +419,10 @@ dmabuf_feedback_maybe_update(struct drm_backend *b, struct weston_view *ev, static struct drm_plane_state * drm_output_find_plane_for_view(struct drm_output_state *state, - struct weston_view *ev, + struct weston_paint_node *pnode, enum drm_output_propose_state_mode mode, struct drm_plane_state *scanout_state, - uint64_t current_lowest_zpos, - uint32_t *try_view_on_plane_failure_reasons) + uint64_t current_lowest_zpos) { struct drm_output *output = state->output; struct drm_backend *b = to_drm_backend(output->base.compositor); @@ -431,6 +430,7 @@ drm_output_find_plane_for_view(struct drm_output_state *state, struct drm_plane_state *ps = NULL; struct drm_plane *plane; + struct weston_view *ev = pnode->view; struct weston_buffer *buffer; struct wl_shm_buffer *shmbuf; struct drm_fb *fb = NULL; @@ -471,7 +471,8 @@ drm_output_find_plane_for_view(struct drm_output_state *state, return NULL; } - fb = drm_fb_get_from_view(state, ev, try_view_on_plane_failure_reasons); + fb = drm_fb_get_from_view(state, ev, + &pnode->try_view_on_plane_failure_reasons); if (!fb) return NULL; @@ -780,10 +781,9 @@ drm_output_propose_state(struct weston_output *output_base, if (!force_renderer) { drm_debug(b, "\t\t\t[plane] started with zpos %"PRIu64"\n", current_lowest_zpos); - ps = drm_output_find_plane_for_view(state, ev, mode, + ps = drm_output_find_plane_for_view(state, pnode, mode, scanout_state, - current_lowest_zpos, - &pnode->try_view_on_plane_failure_reasons); + current_lowest_zpos); /* If we were able to place the view in a plane, set * failure reasons to none. */ if (ps) From 30de9386242b5f7f5c8d9d4d6959783c5cf5aa6a Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 9 Dec 2021 16:55:37 +0000 Subject: [PATCH 027/609] backend-drm: Add more view-to-plane failure states Specifically log if there were no suitable planes for us to use, or if we tried to place it on a plane but were told no by the kernel. Signed-off-by: Daniel Stone --- libweston/backend-drm/drm-internal.h | 2 ++ libweston/backend-drm/state-propose.c | 33 ++++++++++++++++++++------- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index bbc86a17..0331e1de 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -236,6 +236,8 @@ enum try_view_on_plane_failure_reasons { FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE = (1 << 1), FAILURE_REASONS_DMABUF_MODIFIER_INVALID = (1 << 2), FAILURE_REASONS_ADD_FB_FAILED = (1 << 3), + FAILURE_REASONS_NO_PLANES_AVAILABLE = (1 << 4), + FAILURE_REASONS_PLANES_REJECTED = (1 << 5), }; /** diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index 6feeade0..b998268c 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -438,20 +438,30 @@ drm_output_find_plane_for_view(struct drm_output_state *state, bool view_matches_entire_output, scanout_has_view_assigned; uint32_t possible_plane_mask = 0; + pnode->try_view_on_plane_failure_reasons = FAILURE_REASONS_NONE; + /* check view for valid buffer, doesn't make sense to even try */ - if (!weston_view_has_valid_buffer(ev)) - return ps; + if (!weston_view_has_valid_buffer(ev)) { + pnode->try_view_on_plane_failure_reasons |= + FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE; + return NULL; + } buffer = ev->surface->buffer_ref.buffer; shmbuf = wl_shm_buffer_get(buffer->resource); if (shmbuf) { - if (!output->cursor_plane || b->cursors_are_broken) + if (!output->cursor_plane || b->cursors_are_broken) { + pnode->try_view_on_plane_failure_reasons |= + FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE; return NULL; + } if (wl_shm_buffer_get_format(shmbuf) != WL_SHM_FORMAT_ARGB8888) { drm_debug(b, "\t\t\t\t[view] not placing view %p on " "plane; SHM buffers must be ARGB8888 for " "cursor view", ev); + pnode->try_view_on_plane_failure_reasons |= + FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE; return NULL; } @@ -460,6 +470,8 @@ drm_output_find_plane_for_view(struct drm_output_state *state, drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane " "(buffer (%dx%d) too large for cursor plane)", ev, buffer->width, buffer->height); + pnode->try_view_on_plane_failure_reasons |= + FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE; return NULL; } @@ -581,12 +593,22 @@ drm_output_find_plane_for_view(struct drm_output_state *state, ps = drm_output_try_view_on_plane(plane, state, ev, mode, fb, zpos); } + if (ps) { drm_debug(b, "\t\t\t\t[view] view %p has been placed to " "%s plane with computed zpos %"PRIu64"\n", ev, p_name, zpos); break; } + + pnode->try_view_on_plane_failure_reasons |= + FAILURE_REASONS_PLANES_REJECTED; + } + + if (!ps && + pnode->try_view_on_plane_failure_reasons == FAILURE_REASONS_NONE) { + pnode->try_view_on_plane_failure_reasons |= + FAILURE_REASONS_NO_PLANES_AVAILABLE; } /* if we have a plane state, it has its own ref to the fb; if not then @@ -784,11 +806,6 @@ drm_output_propose_state(struct weston_output *output_base, ps = drm_output_find_plane_for_view(state, pnode, mode, scanout_state, current_lowest_zpos); - /* If we were able to place the view in a plane, set - * failure reasons to none. */ - if (ps) - pnode->try_view_on_plane_failure_reasons = - FAILURE_REASONS_NONE; } else { /* We are forced to place the view in the renderer, set * the failure reason accordingly. */ From b0ed4a2e3b16e14df395f854b905a37aea5d9924 Mon Sep 17 00:00:00 2001 From: Manuel Stoeckl Date: Tue, 4 Jan 2022 14:32:44 -0500 Subject: [PATCH 028/609] gl-renderer: add support for (a|x)bgr16161616 shm formats These formats are useful because they are often easier to produce on CPU than half-float formats, and abgr16161616 has both >= 10bpc color channels and adequate alpha, unlike abgr2101010. The 16-bpc textures created from buffers with these formats require the GL_EXT_texture_norm16 extension. As WL_SHM_FORMAT_ABGR16161616 was introduced in libwayland 1.20, update Weston's build requirements and CI. The formats also needed to be registered in the pixel format table, and defined in a fallback path if recent libdrm is not available. Signed-off-by: Manuel Stoeckl --- .gitlab-ci.yml | 2 +- .gitlab-ci/build-deps.sh | 2 +- libweston/pixel-formats.c | 9 +++++++ libweston/renderer-gl/gl-renderer-internal.h | 1 + libweston/renderer-gl/gl-renderer.c | 26 ++++++++++++++++++++ meson.build | 4 +-- shared/weston-drm-fourcc.h | 8 ++++++ 7 files changed, 48 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 02cfaa0d..4210e007 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -43,7 +43,7 @@ variables: FDO_UPSTREAM_REPO: wayland/weston FDO_REPO_SUFFIX: "$BUILD_OS/$BUILD_ARCH" - FDO_DISTRIBUTION_TAG: '2021-11-25.0-dmabuf-feedback' + FDO_DISTRIBUTION_TAG: '2022-01-22.0-for-axbgr16161616' include: diff --git a/.gitlab-ci/build-deps.sh b/.gitlab-ci/build-deps.sh index bce36e92..f707aa59 100755 --- a/.gitlab-ci/build-deps.sh +++ b/.gitlab-ci/build-deps.sh @@ -94,7 +94,7 @@ fi # Build and install Wayland; keep this version in sync with our dependency # in meson.build. -git clone --branch 1.18.0 --depth=1 https://gitlab.freedesktop.org/wayland/wayland +git clone --branch 1.20.0 --depth=1 https://gitlab.freedesktop.org/wayland/wayland cd wayland git show -s HEAD mkdir build diff --git a/libweston/pixel-formats.c b/libweston/pixel-formats.c index 9064cb66..56ccaf6a 100644 --- a/libweston/pixel-formats.c +++ b/libweston/pixel-formats.c @@ -332,6 +332,15 @@ static const struct pixel_format_info pixel_format_table[] = { BITS_RGBA_FIXED(10, 10, 10, 2), .opaque_substitute = DRM_FORMAT_BGRX1010102, }, + { + DRM_FORMAT(XBGR16161616), + BITS_RGBA_FIXED(16, 16, 16, 0), + }, + { + DRM_FORMAT(ABGR16161616), + BITS_RGBA_FIXED(16, 16, 16, 16), + .opaque_substitute = DRM_FORMAT_XBGR16161616, + }, { DRM_FORMAT(YUYV), SAMPLER_TYPE(EGL_TEXTURE_Y_XUXV_WL), diff --git a/libweston/renderer-gl/gl-renderer-internal.h b/libweston/renderer-gl/gl-renderer-internal.h index 72101b47..a225b61c 100644 --- a/libweston/renderer-gl/gl-renderer-internal.h +++ b/libweston/renderer-gl/gl-renderer-internal.h @@ -156,6 +156,7 @@ struct gl_renderer { bool has_texture_type_2_10_10_10_rev; bool has_gl_texture_rg; + bool has_texture_norm16; struct gl_shader *current_shader; struct gl_shader *fallback_shader; diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index a5f5eae4..8cc7a7c2 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -1822,6 +1822,7 @@ gl_format_from_internal(GLenum internal_format) return GL_RED_EXT; case GL_RG8_EXT: return GL_RG_EXT; + case GL_RGBA16_EXT: case GL_RGBA16F: case GL_RGB10_A2: return GL_RGBA; @@ -2035,6 +2036,24 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer, gl_pixel_type = GL_HALF_FLOAT; es->is_opaque = true; break; + case WL_SHM_FORMAT_ABGR16161616: + if (!gr->has_texture_norm16) + goto unsupported; + gs->shader_variant = SHADER_VARIANT_RGBA; + pitch = wl_shm_buffer_get_stride(shm_buffer) / 8; + gl_format[0] = GL_RGBA16_EXT; + gl_pixel_type = GL_UNSIGNED_SHORT; + es->is_opaque = false; + break; + case WL_SHM_FORMAT_XBGR16161616: + if (!gr->has_texture_norm16) + goto unsupported; + gs->shader_variant = SHADER_VARIANT_RGBX; + pitch = wl_shm_buffer_get_stride(shm_buffer) / 8; + gl_format[0] = GL_RGBA16_EXT; + gl_pixel_type = GL_UNSIGNED_SHORT; + es->is_opaque = true; + break; #endif case WL_SHM_FORMAT_YUV420: gs->shader_variant = SHADER_VARIANT_Y_U_V; @@ -3766,6 +3785,10 @@ gl_renderer_display_create(struct weston_compositor *ec, wl_display_add_shm_format(ec->wl_display, WL_SHM_FORMAT_ABGR16161616F); wl_display_add_shm_format(ec->wl_display, WL_SHM_FORMAT_XBGR16161616F); } + if (gr->has_texture_norm16) { + wl_display_add_shm_format(ec->wl_display, WL_SHM_FORMAT_ABGR16161616); + wl_display_add_shm_format(ec->wl_display, WL_SHM_FORMAT_XBGR16161616); + } #endif if (gr->gl_supports_color_transforms) @@ -3942,6 +3965,9 @@ gl_renderer_setup(struct weston_compositor *ec, EGLSurface egl_surface) weston_check_egl_extension(extensions, "GL_EXT_texture_type_2_10_10_10_REV")) gr->has_texture_type_2_10_10_10_rev = true; + if (weston_check_egl_extension(extensions, "GL_EXT_texture_norm16")) + gr->has_texture_norm16 = true; + if (gr->gl_version >= gr_gl_version(3, 0) || weston_check_egl_extension(extensions, "GL_EXT_texture_rg")) gr->has_gl_texture_rg = true; diff --git a/meson.build b/meson.build index afadcb2c..a20716be 100644 --- a/meson.build +++ b/meson.build @@ -145,8 +145,8 @@ if get_option('deprecated-wl-shell') config_h.set('HAVE_DEPRECATED_WL_SHELL', '1') endif -dep_wayland_server = dependency('wayland-server', version: '>= 1.18.0') -dep_wayland_client = dependency('wayland-client', version: '>= 1.18.0') +dep_wayland_server = dependency('wayland-server', version: '>= 1.20.0') +dep_wayland_client = dependency('wayland-client', version: '>= 1.20.0') dep_pixman = dependency('pixman-1', version: '>= 0.25.2') dep_libinput = dependency('libinput', version: '>= 0.8.0') dep_libevdev = dependency('libevdev') diff --git a/shared/weston-drm-fourcc.h b/shared/weston-drm-fourcc.h index 41b86ed8..0a013f79 100644 --- a/shared/weston-drm-fourcc.h +++ b/shared/weston-drm-fourcc.h @@ -38,4 +38,12 @@ #define DRM_FORMAT_XYUV8888 fourcc_code('X', 'Y', 'U', 'V') /* [31:0] X:Y:Cb:Cr 8:8:8:8 little endian */ #endif +#ifndef DRM_FORMAT_XBGR16161616 +#define DRM_FORMAT_XBGR16161616 fourcc_code('X', 'B', '4', '8') /* [63:0] x:B:G:R 16:16:16:16 little endian */ +#endif + +#ifndef DRM_FORMAT_ABGR16161616 +#define DRM_FORMAT_ABGR16161616 fourcc_code('A', 'B', '4', '8') /* [63:0] A:B:G:R 16:16:16:16 little endian */ +#endif + #endif From 8a1849db8a3e1d1e3bc5da33d5b78f600fe19fe7 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Fri, 30 Apr 2021 21:53:16 +0300 Subject: [PATCH 029/609] kiosk-shell: Check if app_ids have been set after initial commit Some applications would set-up the app_id after the initial commit (without a buffer) which is too late to correctly assign the application to the corresponding output set-up in the configuration file. This patch fixes that by checking one more time, after a buffer has been attached, if indeed there's an output with an app_id set. Fixes: #469 Signed-off-by: Marius Vlad --- kiosk-shell/kiosk-shell.c | 25 ++++++++++++++++++++++++- kiosk-shell/kiosk-shell.h | 2 ++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/kiosk-shell/kiosk-shell.c b/kiosk-shell/kiosk-shell.c index 5995baea..477c85c3 100644 --- a/kiosk-shell/kiosk-shell.c +++ b/kiosk-shell/kiosk-shell.c @@ -173,8 +173,10 @@ kiosk_shell_surface_find_best_output(struct kiosk_shell_surface *shsurf) app_id = weston_desktop_surface_get_app_id(shsurf->desktop_surface); if (app_id) { wl_list_for_each(shoutput, &shsurf->shell->output_list, link) { - if (kiosk_shell_output_has_app_id(shoutput, app_id)) + if (kiosk_shell_output_has_app_id(shoutput, app_id)) { + shsurf->appid_output_assigned = true; return shoutput->output; + } } } @@ -354,6 +356,7 @@ kiosk_shell_surface_create(struct kiosk_shell *shell, shsurf->desktop_surface = desktop_surface; shsurf->view = view; shsurf->shell = shell; + shsurf->appid_output_assigned = false; weston_desktop_surface_set_user_data(desktop_surface, shsurf); @@ -721,6 +724,8 @@ desktop_surface_committed(struct weston_desktop_surface *desktop_surface, weston_desktop_surface_get_user_data(desktop_surface); struct weston_surface *surface = weston_desktop_surface_get_surface(desktop_surface); + const char *app_id = + weston_desktop_surface_get_app_id(desktop_surface); bool is_resized; bool is_fullscreen; @@ -729,6 +734,24 @@ desktop_surface_committed(struct weston_desktop_surface *desktop_surface, if (surface->width == 0) return; + if (!shsurf->appid_output_assigned && app_id) { + struct weston_output *output = NULL; + + /* reset previous output being set in _added() as the output is + * being cached */ + shsurf->output = NULL; + output = kiosk_shell_surface_find_best_output(shsurf); + + kiosk_shell_surface_set_output(shsurf, output); + weston_desktop_surface_set_size(shsurf->desktop_surface, + shsurf->output->width, + shsurf->output->height); + /* even if we couldn't find an appid set for a particular + * output still flag the shsurf as to a avoid changing the + * output every time */ + shsurf->appid_output_assigned = true; + } + /* TODO: When the top-level surface is committed with a new size after an * output resize, sometimes the view appears scaled. What state are we not * updating? diff --git a/kiosk-shell/kiosk-shell.h b/kiosk-shell/kiosk-shell.h index 9f680806..070ba1ab 100644 --- a/kiosk-shell/kiosk-shell.h +++ b/kiosk-shell/kiosk-shell.h @@ -73,6 +73,8 @@ struct kiosk_shell_surface { int32_t x; int32_t y; } xwayland; + + bool appid_output_assigned; }; struct kiosk_shell_seat { From f3ad5939255daf0d39c9d53605e678f4e0ece968 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Thu, 9 Dec 2021 12:22:53 +0200 Subject: [PATCH 030/609] kiosk-shell: Don't occlude shsurf on other outputs This adds an additional check to make sure the current focus surface is on the same output as the surface that is going to be activated. This is necessary in order to avoid placing the currently focused one in the inactive layer, which shouldn't happen in situations where the new surface is going to be placed on a different output than the currently focused one. Signed-off-by: Marius Vlad --- kiosk-shell/kiosk-shell.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/kiosk-shell/kiosk-shell.c b/kiosk-shell/kiosk-shell.c index 477c85c3..b63116c8 100644 --- a/kiosk-shell/kiosk-shell.c +++ b/kiosk-shell/kiosk-shell.c @@ -390,8 +390,10 @@ kiosk_shell_surface_activate(struct kiosk_shell_surface *shsurf, /* removes it from the normal_layer and move it to inactive * one, without occluding the top-level window if the new one - * is a child to that */ - if (!shsurf->parent) { + * is a child to that. Also, do not occlude another view + * (currently focused one) on a different output when activating + * a new one. */ + if (!shsurf->parent && (shsurf->output == current_focus->output)) { weston_layer_entry_remove(¤t_focus->view->layer_link); weston_layer_entry_insert(&shsurf->shell->inactive_layer.view_list, ¤t_focus->view->layer_link); From f3221832c504fe2d3bb0aa7c2ecc1d55a0154e0a Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Wed, 22 Dec 2021 16:52:12 +0200 Subject: [PATCH 031/609] kiosk-shell: Favor out views on same output In multiple output cases, finding the succesor from the inactive layer might result in picking the wrong view when there are multiple views being stacked in the inactive layer. This adds two additional checks to favor views on the same output as the one being destroyed/removed. Signed-off-by: Marius Vlad --- kiosk-shell/kiosk-shell.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/kiosk-shell/kiosk-shell.c b/kiosk-shell/kiosk-shell.c index b63116c8..14857ecc 100644 --- a/kiosk-shell/kiosk-shell.c +++ b/kiosk-shell/kiosk-shell.c @@ -650,12 +650,18 @@ find_focus_successor(struct weston_layer *layer, struct weston_view *top_view = NULL; struct weston_view *view; + /* we need to take into account that the surface being destroyed it not * always the same as the focus_surface, which could result in picking * and *activating* the wrong window, so avoid returning a view for * that case. A particular case is when a top-level child window, would - * pick a parent window below the focused_surface. */ - if (focused_surface != shsurf->view->surface) + * pick a parent window below the focused_surface. + * + * Apply that only on the same output to avoid incorrectly returning an + * invalid/empty view, which could happen if the view being destroyed + * is on a output different than the focused_surface output */ + if (focused_surface && focused_surface != shsurf->view->surface && + shsurf->output == focused_surface->output) return top_view; wl_list_for_each(view, &layer->view_list.link, layer_link.link) { @@ -665,6 +671,10 @@ find_focus_successor(struct weston_layer *layer, if (!view->is_mapped || view == shsurf->view) continue; + /* pick views only on the same output */ + if (view->output != shsurf->output) + continue; + view_shsurf = get_kiosk_shell_surface(view->surface); if (!view_shsurf) continue; From 933290e6eadb37a984dba78562f1e499a5f41679 Mon Sep 17 00:00:00 2001 From: Robert Mader Date: Fri, 31 Dec 2021 15:49:34 +0100 Subject: [PATCH 032/609] libweston/compositor: Cache buffer damage for synced subsurfaces The spec states: > Because buffer transformation changes and damage requests may be > interleaved in the protocol stream, it is impossible to determine > the actual mapping between surface and buffer damage until > wl_surface.commit time. Therefore, compositors wishing to take both > kinds of damage into account will have to accumulate damage from the > two requests separately and only transform from one to the other after > receiving the wl_surface.commit. For subsurfaces in sync mode, arguably the same is the case until the cached state gets applied eventually. Thus, in order to keep complexity to a sane level, just accumulate buffer damage and convert it only when the cached state gets applied. This mirrors how other compositors like Mutter implement cached damage and what the spec arguably should demand. Closes https://gitlab.freedesktop.org/wayland/weston/-/issues/446 Signed-off-by: Robert Mader --- libweston/compositor.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libweston/compositor.c b/libweston/compositor.c index 1670c500..452d32f7 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -4211,6 +4211,11 @@ weston_subsurface_commit_to_cache(struct weston_subsurface *sub) &surface->pending.damage_surface); pixman_region32_clear(&surface->pending.damage_surface); + pixman_region32_union(&sub->cached.damage_buffer, + &sub->cached.damage_buffer, + &surface->pending.damage_buffer); + pixman_region32_clear(&surface->pending.damage_buffer); + if (surface->pending.newly_attached) { sub->cached.newly_attached = 1; weston_surface_state_set_buffer(&sub->cached, @@ -4233,8 +4238,6 @@ weston_subsurface_commit_to_cache(struct weston_subsurface *sub) sub->cached.sx += surface->pending.sx; sub->cached.sy += surface->pending.sy; - apply_damage_buffer(&sub->cached.damage_surface, surface, &surface->pending); - sub->cached.buffer_viewport.changed |= surface->pending.buffer_viewport.changed; sub->cached.buffer_viewport.buffer = From dc3b3493259ee55df2429af30d1ddf9e5e9a45d2 Mon Sep 17 00:00:00 2001 From: Robert Mader Date: Tue, 25 Jan 2022 17:56:46 +0100 Subject: [PATCH 033/609] tests: Add test for synced subsurfaces and buffer damage Changing `wl_surface_damage()` to `wl_surface_damage_buffer()` should not have an effect on the existing tests. The new test will fail without the commit "libweston/compositor: Cache buffer damage for synced subsurfaces" Signed-off-by: Robert Mader --- .../subsurface_sync_damage_buffer-00.png | Bin 0 -> 825 bytes .../subsurface_sync_damage_buffer-01.png | Bin 0 -> 858 bytes .../subsurface_sync_damage_buffer-02.png | Bin 0 -> 857 bytes tests/subsurface-shot-test.c | 69 +++++++++++++++++- 4 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 tests/reference/subsurface_sync_damage_buffer-00.png create mode 100644 tests/reference/subsurface_sync_damage_buffer-01.png create mode 100644 tests/reference/subsurface_sync_damage_buffer-02.png diff --git a/tests/reference/subsurface_sync_damage_buffer-00.png b/tests/reference/subsurface_sync_damage_buffer-00.png new file mode 100644 index 0000000000000000000000000000000000000000..f127154cb68edf8e0148ac0de00ea5ca360b371b GIT binary patch literal 825 zcmeAS@N?(olHy`uVBq!ia0y~yU~~YoKX5Ps$$$P@Hb9Ck$=lt9;Xep2*t>i(0|V1L zPZ!6KiaBquI`T3(inusVQ%IX=T;|45HlbP8=FQXRsjEf4JYCvf{FeQ~mpO6_G3p5% z!Uipz5{FoL5}Dcz7@cP{DEb@_P@})_w^?!lpMKXZQ%~7*ecrqRZn;6=rU3{BqO9ET rIHtOaxnLsufW{b6PNij%+Fi(0|PU^ zr;B4q#hkZyHu4^F5Mg!f(Kt5cahZ>wwO)IX!Xn1x!l)T1{i{yCN;f}yx{7Jmxy9@Z zzd0oivG61^wHYuv&uCEeIUt~xz#(kVG62G{Zu}aRIo~e|8?8HSRdp!7pph)+d{)0U z-Dmmp7o7LEzOA>?-~PDlkA?W4@g&W{b$5?kKvC?qzx_ZDADGQeR+Qc_ssD6d*kB5I hx(7J9rfODhL8pZOKY^Kr!PC{xWt~$(69BBr`2GL@ literal 0 HcmV?d00001 diff --git a/tests/reference/subsurface_sync_damage_buffer-02.png b/tests/reference/subsurface_sync_damage_buffer-02.png new file mode 100644 index 0000000000000000000000000000000000000000..9ef82e06c45ebdd1e1ad0689517c71ef17752649 GIT binary patch literal 857 zcmeAS@N?(olHy`uVBq!ia0y~yU~~YoKX5Ps$$$P@Hb9Ck$=lt9;Xep2*t>i(0|PUk zr;B4q#hkZyHu4^F5Mg!f(Kt3G`BWieUVD)SyHZig1b^+cPm|*|{krMimage, color); wl_surface_attach(surface, buf->proxy, 0, 0); - wl_surface_damage(surface, 0, 0, width, height); + wl_surface_damage_buffer(surface, 0, 0, width, height); wl_surface_commit(surface); return buf; @@ -213,3 +213,70 @@ TEST(subsurface_z_order) wl_subcompositor_destroy(subco); client_destroy(client); } + +TEST(subsurface_sync_damage_buffer) +{ + struct client *client; + struct wl_subcompositor *subco; + struct buffer *bufs[2] = { 0 }; + struct wl_surface *surf[2] = { 0 }; + struct wl_subsurface *sub[2] = { 0 }; + struct rectangle clip = { 40, 40, 280, 200 }; + int fail = 0; + unsigned i; + pixman_color_t red; + pixman_color_t blue; + pixman_color_t green; + + color_rgb888(&red, 255, 0, 0); + color_rgb888(&blue, 0, 0, 255); + color_rgb888(&green, 0, 255, 0); + + client = create_client_and_test_surface(100, 50, 100, 100); + assert(client); + subco = get_subcompositor(client); + + /* move the pointer clearly away from our screenshooting area */ + weston_test_move_pointer(client->test->weston_test, 0, 1, 0, 2, 30); + + /* make the parent surface red */ + surf[0] = client->surface->wl_surface; + client->surface->wl_surface = NULL; /* we stole it and destroy it */ + bufs[0] = surface_commit_color(client, surf[0], &red, 100, 100); + /* sub[0] is not used */ + + fail += check_screen(client, "subsurface_sync_damage_buffer", 0, &clip, 0); + + /* create a blue sub-surface above red */ + surf[1] = wl_compositor_create_surface(client->wl_compositor); + sub[1] = wl_subcompositor_get_subsurface(subco, surf[1], surf[0]); + bufs[1] = surface_commit_color(client, surf[1], &blue, 100, 100); + + wl_subsurface_set_position(sub[1], 20, 20); + wl_surface_commit(surf[0]); + + fail += check_screen(client, "subsurface_sync_damage_buffer", 1, &clip, 1); + + buffer_destroy(bufs[1]); + bufs[1] = surface_commit_color(client, surf[1], &green, 100, 100); + wl_surface_commit(surf[0]); + + fail += check_screen(client, "subsurface_sync_damage_buffer", 2, &clip, 2); + + assert(fail == 0); + + for (i = 0; i < ARRAY_LENGTH(sub); i++) + if (sub[i]) + wl_subsurface_destroy(sub[i]); + + for (i = 0; i < ARRAY_LENGTH(surf); i++) + if (surf[i]) + wl_surface_destroy(surf[i]); + + for (i = 0; i < ARRAY_LENGTH(bufs); i++) + if (bufs[i]) + buffer_destroy(bufs[i]); + + wl_subcompositor_destroy(subco); + client_destroy(client); +} From 66374d48f17bdd2089e84ee8282c5f31974df452 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Wed, 26 Jan 2022 09:33:30 -0600 Subject: [PATCH 034/609] compositor: Remove desktop zoom Zoom is a neat trick, but in its current form it's very hard to test and maintain. It also causes output damage to scale outside of the output's boundaries, which leads to an extra clipping step that's only necessary when zoom is enabled. Remove it to simplify desktop-shell and compositor. Signed-off-by: Derek Foreman --- desktop-shell/shell.c | 73 ----------- include/libweston/libweston.h | 21 ---- libweston/backend-drm/drm.c | 29 ++--- libweston/backend.h | 3 - libweston/compositor.c | 37 ++---- libweston/meson.build | 1 - libweston/renderer-gl/gl-renderer.c | 2 +- libweston/zoom.c | 184 ---------------------------- 8 files changed, 18 insertions(+), 332 deletions(-) delete mode 100644 libweston/zoom.c diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 4ca78979..4bf4eaba 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -3421,71 +3421,6 @@ surface_opacity_binding(struct weston_pointer *pointer, weston_surface_damage(surface); } -static void -do_zoom(struct weston_seat *seat, const struct timespec *time, uint32_t key, - uint32_t axis, double value) -{ - struct weston_compositor *compositor = seat->compositor; - struct weston_pointer *pointer = weston_seat_get_pointer(seat); - struct weston_output *output; - float increment; - - if (!pointer) { - weston_log("Zoom hotkey pressed but seat '%s' contains no pointer.\n", seat->seat_name); - return; - } - - wl_list_for_each(output, &compositor->output_list, link) { - if (pixman_region32_contains_point(&output->region, - wl_fixed_to_double(pointer->x), - wl_fixed_to_double(pointer->y), - NULL)) { - if (key == KEY_PAGEUP) - increment = output->zoom.increment; - else if (key == KEY_PAGEDOWN) - increment = -output->zoom.increment; - else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) - /* For every pixel zoom 20th of a step */ - increment = output->zoom.increment * - -value / 20.0; - else - increment = 0; - - output->zoom.level += increment; - - if (output->zoom.level < 0.0) - output->zoom.level = 0.0; - else if (output->zoom.level > output->zoom.max_level) - output->zoom.level = output->zoom.max_level; - - if (!output->zoom.active) { - if (output->zoom.level <= 0.0) - continue; - weston_output_activate_zoom(output, seat); - } - - output->zoom.spring_z.target = output->zoom.level; - - weston_output_update_zoom(output); - } - } -} - -static void -zoom_axis_binding(struct weston_pointer *pointer, const struct timespec *time, - struct weston_pointer_axis_event *event, - void *data) -{ - do_zoom(pointer->seat, time, 0, event->axis, event->value); -} - -static void -zoom_key_binding(struct weston_keyboard *keyboard, const struct timespec *time, - uint32_t key, void *data) -{ - do_zoom(keyboard->seat, time, key, 0, 0); -} - static void terminate_binding(struct weston_keyboard *keyboard, const struct timespec *time, uint32_t key, void *data) @@ -5022,14 +4957,6 @@ shell_add_bindings(struct weston_compositor *ec, struct desktop_shell *shell) MODIFIER_SUPER | MODIFIER_ALT, surface_opacity_binding, NULL); - weston_compositor_add_axis_binding(ec, WL_POINTER_AXIS_VERTICAL_SCROLL, - mod, zoom_axis_binding, - NULL); - - weston_compositor_add_key_binding(ec, KEY_PAGEUP, mod, - zoom_key_binding, NULL); - weston_compositor_add_key_binding(ec, KEY_PAGEDOWN, mod, - zoom_key_binding, NULL); weston_compositor_add_key_binding(ec, KEY_M, mod | MODIFIER_SHIFT, maximize_binding, NULL); weston_compositor_add_key_binding(ec, KEY_F, mod | MODIFIER_SHIFT, diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index d99dc763..90350c9e 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -150,21 +150,6 @@ struct weston_spring { uint32_t clip; }; -struct weston_output_zoom { - bool active; - float increment; - float level; - float max_level; - float trans_x, trans_y; - struct { - double x, y; - } current; - struct weston_seat *seat; - struct weston_animation animation_z; - struct weston_spring spring_z; - struct wl_listener motion_listener; -}; - /* bit compatible with drm definitions. */ enum dpms_enum { WESTON_DPMS_ON, @@ -324,7 +309,6 @@ struct weston_output { /** For cancelling the idle_repaint callback on output destruction. */ struct wl_event_source *idle_repaint_source; - struct weston_output_zoom zoom; int dirty; struct wl_signal frame_signal; struct wl_signal destroy_signal; /**< sent when disabled */ @@ -1876,11 +1860,6 @@ void weston_compositor_exit_with_code(struct weston_compositor *compositor, int exit_code); void -weston_output_update_zoom(struct weston_output *output); -void -weston_output_activate_zoom(struct weston_output *output, - struct weston_seat *seat); -void weston_output_add_destroy_listener(struct weston_output *output, struct wl_listener *listener); struct wl_listener * diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index 203b8a4e..be647deb 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -414,27 +414,14 @@ drm_output_render(struct drm_output_state *state, pixman_region32_t *damage) pixman_region32_init(&scanout_damage); pixman_region32_copy(&scanout_damage, damage); - if (output->base.zoom.active) { - pixman_region32_t clip; - - weston_matrix_transform_region(&scanout_damage, - &output->base.matrix, - &scanout_damage); - pixman_region32_init_rect(&clip, 0, 0, - output->base.width, - output->base.height); - pixman_region32_intersect(&scanout_damage, &scanout_damage, &clip); - pixman_region32_fini(&clip); - } else { - pixman_region32_translate(&scanout_damage, - -output->base.x, -output->base.y); - weston_transformed_region(output->base.width, - output->base.height, - output->base.transform, - output->base.current_scale, - &scanout_damage, - &scanout_damage); - } + pixman_region32_translate(&scanout_damage, + -output->base.x, -output->base.y); + weston_transformed_region(output->base.width, + output->base.height, + output->base.transform, + output->base.current_scale, + &scanout_damage, + &scanout_damage); assert(scanout_state->damage_blob_id == 0); diff --git a/libweston/backend.h b/libweston/backend.h index 3ae59a6c..af0d6671 100644 --- a/libweston/backend.h +++ b/libweston/backend.h @@ -154,9 +154,6 @@ weston_output_damage(struct weston_output *output); void weston_output_release(struct weston_output *output); -void -weston_output_init_zoom(struct weston_output *output); - void weston_output_finish_frame(struct weston_output *output, const struct timespec *stamp, diff --git a/libweston/compositor.c b/libweston/compositor.c index 452d32f7..bb2aa62d 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -6064,11 +6064,8 @@ weston_compositor_reflow_outputs(struct weston_compositor *compositor, * \param region The region to be transformed in-place. * * This takes a region in the global coordinate system, and takes into account - * output position, transform and scale, and the zoom, and converts the region - * into output pixel coordinates in the framebuffer. - * - * Uses floating-point operations if zoom is active, which may round to expand - * the region. + * output position, transform and scale, and converts the region into output + * pixel coordinates in the framebuffer. * * \internal * \ingroup output @@ -6077,34 +6074,19 @@ WL_EXPORT void weston_output_region_from_global(struct weston_output *output, pixman_region32_t *region) { - if (output->zoom.active) { - weston_matrix_transform_region(region, &output->matrix, region); - } else { - pixman_region32_translate(region, -output->x, -output->y); - weston_transformed_region(output->width, output->height, - output->transform, - output->current_scale, - region, region); - } + pixman_region32_translate(region, -output->x, -output->y); + weston_transformed_region(output->width, output->height, + output->transform, + output->current_scale, + region, region); } static void weston_output_update_matrix(struct weston_output *output) { - float magnification; - weston_matrix_init(&output->matrix); weston_matrix_translate(&output->matrix, -output->x, -output->y, 0); - if (output->zoom.active) { - magnification = 1 / (1 - output->zoom.spring_z.current); - weston_output_update_zoom(output); - weston_matrix_translate(&output->matrix, -output->zoom.trans_x, - -output->zoom.trans_y, 0); - weston_matrix_scale(&output->matrix, magnification, - magnification, 1.0); - } - switch (output->transform) { case WL_OUTPUT_TRANSFORM_FLIPPED: case WL_OUTPUT_TRANSFORM_FLIPPED_90: @@ -6682,8 +6664,8 @@ weston_output_create_heads_string(struct weston_output *output) * Output coordinates are calculated and each new output is by default * assigned to the right of previous one. * - * Sets up the transformation, zoom, and geometry of the output using - * the properties that need to be configured by the compositor. + * Sets up the transformation, and geometry of the output using the + * properties that need to be configured by the compositor. * * Establishes a repaint timer for the output with the relevant display * object's event loop. See output_repaint_timer_handler(). @@ -6762,7 +6744,6 @@ weston_output_enable(struct weston_output *output) wl_signal_init(&output->destroy_signal); weston_output_transform_scale_init(output, output->transform, output->scale); - weston_output_init_zoom(output); weston_output_init_geometry(output, x, y); weston_output_damage(output); diff --git a/libweston/meson.build b/libweston/meson.build index 257d6950..186ae4a5 100644 --- a/libweston/meson.build +++ b/libweston/meson.build @@ -35,7 +35,6 @@ srcs_libweston = [ 'weston-log-flight-rec.c', 'weston-log.c', 'weston-direct-display.c', - 'zoom.c', linux_dmabuf_unstable_v1_protocol_c, linux_dmabuf_unstable_v1_server_protocol_h, linux_explicit_synchronization_unstable_v1_protocol_c, diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 8cc7a7c2..501a6802 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -1068,7 +1068,7 @@ draw_paint_node(struct weston_paint_node *pnode, glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - if (pnode->view->transform.enabled || pnode->output->zoom.active || + if (pnode->view->transform.enabled || pnode->output->current_scale != pnode->surface->buffer_viewport.buffer.scale) filter = GL_LINEAR; else diff --git a/libweston/zoom.c b/libweston/zoom.c deleted file mode 100644 index 064d6a84..00000000 --- a/libweston/zoom.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright © 2012 Scott Moreau - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "config.h" - -#include -#include -#include -#include - -#include -#include "backend.h" -#include "libweston-internal.h" -#include "text-cursor-position-server-protocol.h" -#include "shared/helpers.h" - -static void -weston_zoom_frame_z(struct weston_animation *animation, - struct weston_output *output, - const struct timespec *time) -{ - if (animation->frame_counter <= 1) - output->zoom.spring_z.timestamp = *time; - - weston_spring_update(&output->zoom.spring_z, time); - - if (output->zoom.spring_z.current > output->zoom.max_level) - output->zoom.spring_z.current = output->zoom.max_level; - else if (output->zoom.spring_z.current < 0.0) - output->zoom.spring_z.current = 0.0; - - if (weston_spring_done(&output->zoom.spring_z)) { - if (output->zoom.active && output->zoom.level <= 0.0) { - output->zoom.active = false; - output->zoom.seat = NULL; - weston_output_disable_planes_decr(output); - wl_list_remove(&output->zoom.motion_listener.link); - } - output->zoom.spring_z.current = output->zoom.level; - wl_list_remove(&animation->link); - wl_list_init(&animation->link); - } - - output->dirty = 1; - weston_output_damage(output); -} - -static void -zoom_area_center_from_point(struct weston_output *output, - double *x, double *y) -{ - float level = output->zoom.spring_z.current; - - *x = (*x - output->x) * level + output->width / 2.; - *y = (*y - output->y) * level + output->height / 2.; -} - -static void -weston_output_update_zoom_transform(struct weston_output *output) -{ - double x = output->zoom.current.x; /* global pointer coords */ - double y = output->zoom.current.y; - float level; - - level = output->zoom.spring_z.current; - - if (!output->zoom.active || level > output->zoom.max_level || - level == 0.0f) - return; - - zoom_area_center_from_point(output, &x, &y); - - output->zoom.trans_x = x - output->width / 2; - output->zoom.trans_y = y - output->height / 2; - - if (output->zoom.trans_x < 0) - output->zoom.trans_x = 0; - if (output->zoom.trans_y < 0) - output->zoom.trans_y = 0; - if (output->zoom.trans_x > level * output->width) - output->zoom.trans_x = level * output->width; - if (output->zoom.trans_y > level * output->height) - output->zoom.trans_y = level * output->height; -} - -static void -weston_zoom_transition(struct weston_output *output) -{ - if (output->zoom.level != output->zoom.spring_z.current) { - output->zoom.spring_z.target = output->zoom.level; - if (wl_list_empty(&output->zoom.animation_z.link)) { - output->zoom.animation_z.frame_counter = 0; - wl_list_insert(output->animation_list.prev, - &output->zoom.animation_z.link); - } - } - - output->dirty = 1; - weston_output_damage(output); -} - -WL_EXPORT void -weston_output_update_zoom(struct weston_output *output) -{ - struct weston_seat *seat = output->zoom.seat; - struct weston_pointer *pointer = weston_seat_get_pointer(seat); - - if (!pointer) - return; - - assert(output->zoom.active); - - output->zoom.current.x = wl_fixed_to_double(pointer->x); - output->zoom.current.y = wl_fixed_to_double(pointer->y); - - weston_zoom_transition(output); - weston_output_update_zoom_transform(output); -} - -static void -motion(struct wl_listener *listener, void *data) -{ - struct weston_output_zoom *zoom = - container_of(listener, struct weston_output_zoom, motion_listener); - struct weston_output *output = - container_of(zoom, struct weston_output, zoom); - - weston_output_update_zoom(output); -} - -WL_EXPORT void -weston_output_activate_zoom(struct weston_output *output, - struct weston_seat *seat) -{ - struct weston_pointer *pointer = weston_seat_get_pointer(seat); - - if (!pointer || output->zoom.active) - return; - - output->zoom.active = true; - output->zoom.seat = seat; - weston_output_disable_planes_incr(output); - wl_signal_add(&pointer->motion_signal, - &output->zoom.motion_listener); -} - -WL_EXPORT void -weston_output_init_zoom(struct weston_output *output) -{ - output->zoom.active = false; - output->zoom.seat = NULL; - output->zoom.increment = 0.07; - output->zoom.max_level = 0.95; - output->zoom.level = 0.0; - output->zoom.trans_x = 0.0; - output->zoom.trans_y = 0.0; - weston_spring_init(&output->zoom.spring_z, 250.0, 0.0, 0.0); - output->zoom.spring_z.friction = 1000; - output->zoom.animation_z.frame = weston_zoom_frame_z; - wl_list_init(&output->zoom.animation_z.link); - output->zoom.motion_listener.notify = motion; -} From 83927bb0e678626bcc037e6880820531d7b90a4f Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Thu, 6 Jan 2022 09:27:21 -0600 Subject: [PATCH 035/609] launcher-logind: Remove systemd-logind support Many years ago (2014) systemd-logind was brought into libsystemd. We've supported old versions of systemd-logind ever since. Let's remove support for old versions of systemd-logind before the merge for a tiny code simplification. Signed-off-by: Derek Foreman --- libweston/launcher-logind.c | 25 +------------------------ libweston/meson.build | 9 ++------- 2 files changed, 3 insertions(+), 31 deletions(-) diff --git a/libweston/launcher-logind.c b/libweston/launcher-logind.c index 3fca1dff..77f43815 100644 --- a/libweston/launcher-logind.c +++ b/libweston/launcher-logind.c @@ -695,29 +695,6 @@ launcher_logind_release_control(struct launcher_logind *wl) } } -static int -weston_sd_session_get_vt(const char *sid, unsigned int *out) -{ -#ifdef HAVE_SYSTEMD_LOGIN_209 - return sd_session_get_vt(sid, out); -#else - int r; - char *tty; - - r = sd_session_get_tty(sid, &tty); - if (r < 0) - return r; - - r = sscanf(tty, "tty%u", out); - free(tty); - - if (r != 1) - return -EINVAL; - - return 0; -#endif -} - static int launcher_logind_activate(struct launcher_logind *wl) { @@ -803,7 +780,7 @@ launcher_logind_connect(struct weston_launcher **out, struct weston_compositor * r = sd_seat_can_tty(t); free(t); if (r > 0) { - r = weston_sd_session_get_vt(wl->sid, &wl->vtnr); + r = sd_session_get_vt(wl->sid, &wl->vtnr); if (r < 0) { weston_log("logind: session not running on a VT\n"); goto err_session; diff --git a/libweston/meson.build b/libweston/meson.build index 186ae4a5..e7afabbc 100644 --- a/libweston/meson.build +++ b/libweston/meson.build @@ -156,13 +156,8 @@ endif systemd_dep = dependency('', required: false) if get_option('launcher-logind') systemd_dep = dependency('libsystemd', version: '>= 209', required: false) - if systemd_dep.found() - config_h.set('HAVE_SYSTEMD_LOGIN_209', '1') - else - systemd_dep = dependency('libsystemd-login', version: '>= 198', required: false) - if not systemd_dep.found() - error('logind support requires libsystemd or libsystemd-login but neither was found. Or, you can use \'-Dlauncher-logind=false\'') - endif + if not systemd_dep.found() + error('logind support requires libsystemd >= 209. Or, you can use \'-Dlauncher-logind=false\'') endif dbus_dep = dependency('dbus-1', version: '>= 1.6', required: false) From 773bcf9097d3ba1d1fd7121ce20ef12736e0cace Mon Sep 17 00:00:00 2001 From: Veeresh Kadasani Date: Mon, 4 Jan 2021 17:46:19 +0530 Subject: [PATCH 036/609] man: Document available debug bindings. Fixes: #398 Debug bindings were not documented in weston-bindings so document them. Signed-off-by: Veeresh Kadasani --- man/weston-bindings.man | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/man/weston-bindings.man b/man/weston-bindings.man index e88a9e85..fac38be0 100644 --- a/man/weston-bindings.man +++ b/man/weston-bindings.man @@ -126,7 +126,42 @@ The combination \fBmod + Shift + Space\fR begins a debug binding. Debug bindings are completed by pressing an additional key. For example, pressing F may toggle texture mesh wireframes with the GL renderer. (In fact, most debug effects can be disabled again by repeating the command.) -Debug bindings are often tied to specific backends. +Debug bindings are often tied to specific backends. Below are the debug bindings available. + +.RS +- KEY_D : +.RS 4 +Subscribe for flight recorder. +.RE +- KEY_C : +.RS 4 +Enable/Disable cursor planes. +.RE +- KEY_V : +.RS 4 +Enable/Disable overlay planes. +.RE +- KEY_Q : +.RS 4 +Start VAAPI recorder. +.RE +- KEY_W : +.RS 4 +Switch to gl-renderer from pixman. +.RE +- KEY_S : +.RS 4 +Enable fragment debugging for gl-renderer. +.RE +- KEY_F : +.RS 4 +Enable fan debugging for gl-renderer. +.RE +- KEY_R : +.RS 4 +Enable repaint debugging for pixman: +.RE +.RE .SH "SEE ALSO" .BR weston (1), From 7e70f9016aac1f4c73c600818d228bb625fd4027 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 1 Feb 2022 23:23:33 +0100 Subject: [PATCH 037/609] clients: drop weston-info Users should rely on wayland-info from wayland-utils [1] instead. We've been printing a deprecation since 85382d394af9 ("clients: deprecate weston-info"), so users should be aware already. [1]: https://gitlab.freedesktop.org/wayland/wayland-utils/ Signed-off-by: Simon Ser --- README.md | 2 +- clients/meson.build | 15 - clients/weston-info.c | 1890 ----------------------------------------- 3 files changed, 1 insertion(+), 1906 deletions(-) delete mode 100644 clients/weston-info.c diff --git a/README.md b/README.md index bbc93b45..77552ca6 100644 --- a/README.md +++ b/README.md @@ -345,7 +345,7 @@ would be roughly like this: + desktop shell + ivi-shell + fullscreen shell - + weston-info (deprecated), weston-terminal, etc. we install by default + + weston-terminal, etc. we install by default + screen-share - weston demos (not parallel-installable) diff --git a/clients/meson.build b/clients/meson.build index 362f7fe3..55d7bf65 100644 --- a/clients/meson.build +++ b/clients/meson.build @@ -217,21 +217,6 @@ tools_list = [ ], 'deps': [ dep_wayland_client ] }, - { - 'name': 'info', - 'sources': [ - 'weston-info.c', - presentation_time_client_protocol_h, - presentation_time_protocol_c, - linux_dmabuf_unstable_v1_client_protocol_h, - linux_dmabuf_unstable_v1_protocol_c, - tablet_unstable_v2_client_protocol_h, - tablet_unstable_v2_protocol_c, - xdg_output_unstable_v1_client_protocol_h, - xdg_output_unstable_v1_protocol_c, - ], - 'deps': [ dep_wayland_client, dep_libshared ] - }, { 'name': 'terminal', 'sources': [ 'terminal.c' ], diff --git a/clients/weston-info.c b/clients/weston-info.c deleted file mode 100644 index 97725273..00000000 --- a/clients/weston-info.c +++ /dev/null @@ -1,1890 +0,0 @@ -/* - * Copyright © 2012 Philipp Brüschweiler - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "shared/helpers.h" -#include "shared/os-compatibility.h" -#include "shared/xalloc.h" -#include -#include "presentation-time-client-protocol.h" -#include "linux-dmabuf-unstable-v1-client-protocol.h" -#include "tablet-unstable-v2-client-protocol.h" -#include "xdg-output-unstable-v1-client-protocol.h" - -typedef void (*print_info_t)(void *info); -typedef void (*destroy_info_t)(void *info); - -struct global_info { - struct wl_list link; - - uint32_t id; - uint32_t version; - char *interface; - - print_info_t print; - destroy_info_t destroy; -}; - -struct output_mode { - struct wl_list link; - - uint32_t flags; - int32_t width, height; - int32_t refresh; -}; - -struct output_info { - struct global_info global; - struct wl_list global_link; - - struct wl_output *output; - - int32_t version; - - struct { - int32_t x, y; - int32_t scale; - int32_t physical_width, physical_height; - enum wl_output_subpixel subpixel; - enum wl_output_transform output_transform; - char *make; - char *model; - } geometry; - - struct wl_list modes; -}; - -struct shm_format { - struct wl_list link; - - uint32_t format; -}; - -struct shm_info { - struct global_info global; - struct wl_shm *shm; - - struct wl_list formats; -}; - -struct linux_dmabuf_modifier { - struct wl_list link; - - uint32_t format; - uint64_t modifier; -}; - -struct linux_dmabuf_info { - struct global_info global; - struct zwp_linux_dmabuf_v1 *dmabuf; - - struct wl_list modifiers; -}; - -struct seat_info { - struct global_info global; - struct wl_list global_link; - struct wl_seat *seat; - struct weston_info *info; - - struct wl_keyboard *keyboard; - uint32_t capabilities; - char *name; - - int32_t repeat_rate; - int32_t repeat_delay; -}; - -struct tablet_v2_path { - struct wl_list link; - char *path; -}; - -struct tablet_tool_info { - struct wl_list link; - struct zwp_tablet_tool_v2 *tool; - - uint64_t hardware_serial; - uint64_t hardware_id_wacom; - enum zwp_tablet_tool_v2_type type; - - bool has_tilt; - bool has_pressure; - bool has_distance; - bool has_rotation; - bool has_slider; - bool has_wheel; -}; - -struct tablet_pad_group_info { - struct wl_list link; - struct zwp_tablet_pad_group_v2 *group; - - uint32_t modes; - size_t button_count; - int *buttons; - size_t strips; - size_t rings; -}; - -struct tablet_pad_info { - struct wl_list link; - struct zwp_tablet_pad_v2 *pad; - - uint32_t buttons; - struct wl_list paths; - struct wl_list groups; -}; - -struct tablet_info { - struct wl_list link; - struct zwp_tablet_v2 *tablet; - - char *name; - uint32_t vid, pid; - struct wl_list paths; -}; - -struct tablet_seat_info { - struct wl_list link; - - struct zwp_tablet_seat_v2 *seat; - struct seat_info *seat_info; - - struct wl_list tablets; - struct wl_list tools; - struct wl_list pads; -}; - -struct tablet_v2_info { - struct global_info global; - struct zwp_tablet_manager_v2 *manager; - struct weston_info *info; - - struct wl_list seats; -}; - -struct xdg_output_v1_info { - struct wl_list link; - - struct zxdg_output_v1 *xdg_output; - struct output_info *output; - - struct { - int32_t x, y; - int32_t width, height; - } logical; - - char *name, *description; -}; - -struct xdg_output_manager_v1_info { - struct global_info global; - struct zxdg_output_manager_v1 *manager; - struct weston_info *info; - - struct wl_list outputs; -}; - -struct presentation_info { - struct global_info global; - struct wp_presentation *presentation; - - clockid_t clk_id; -}; - -struct weston_info { - struct wl_display *display; - struct wl_registry *registry; - - struct wl_list infos; - bool roundtrip_needed; - - /* required for tablet-unstable-v2 */ - struct wl_list seats; - struct tablet_v2_info *tablet_info; - - /* required for xdg-output-unstable-v1 */ - struct wl_list outputs; - struct xdg_output_manager_v1_info *xdg_output_manager_v1_info; -}; - -static void -print_global_info(void *data) -{ - struct global_info *global = data; - - printf("interface: '%s', version: %u, name: %u\n", - global->interface, global->version, global->id); -} - -static void -init_global_info(struct weston_info *info, - struct global_info *global, uint32_t id, - const char *interface, uint32_t version) -{ - global->id = id; - global->version = version; - global->interface = xstrdup(interface); - - wl_list_insert(info->infos.prev, &global->link); -} - -static void -print_output_info(void *data) -{ - struct output_info *output = data; - struct output_mode *mode; - const char *subpixel_orientation; - const char *transform; - - print_global_info(data); - - switch (output->geometry.subpixel) { - case WL_OUTPUT_SUBPIXEL_UNKNOWN: - subpixel_orientation = "unknown"; - break; - case WL_OUTPUT_SUBPIXEL_NONE: - subpixel_orientation = "none"; - break; - case WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB: - subpixel_orientation = "horizontal rgb"; - break; - case WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR: - subpixel_orientation = "horizontal bgr"; - break; - case WL_OUTPUT_SUBPIXEL_VERTICAL_RGB: - subpixel_orientation = "vertical rgb"; - break; - case WL_OUTPUT_SUBPIXEL_VERTICAL_BGR: - subpixel_orientation = "vertical bgr"; - break; - default: - fprintf(stderr, "unknown subpixel orientation %u\n", - output->geometry.subpixel); - subpixel_orientation = "unexpected value"; - break; - } - - switch (output->geometry.output_transform) { - case WL_OUTPUT_TRANSFORM_NORMAL: - transform = "normal"; - break; - case WL_OUTPUT_TRANSFORM_90: - transform = "90°"; - break; - case WL_OUTPUT_TRANSFORM_180: - transform = "180°"; - break; - case WL_OUTPUT_TRANSFORM_270: - transform = "270°"; - break; - case WL_OUTPUT_TRANSFORM_FLIPPED: - transform = "flipped"; - break; - case WL_OUTPUT_TRANSFORM_FLIPPED_90: - transform = "flipped 90°"; - break; - case WL_OUTPUT_TRANSFORM_FLIPPED_180: - transform = "flipped 180°"; - break; - case WL_OUTPUT_TRANSFORM_FLIPPED_270: - transform = "flipped 270°"; - break; - default: - fprintf(stderr, "unknown output transform %u\n", - output->geometry.output_transform); - transform = "unexpected value"; - break; - } - - printf("\tx: %d, y: %d,", - output->geometry.x, output->geometry.y); - if (output->version >= 2) - printf(" scale: %d,", output->geometry.scale); - printf("\n"); - - printf("\tphysical_width: %d mm, physical_height: %d mm,\n", - output->geometry.physical_width, - output->geometry.physical_height); - printf("\tmake: '%s', model: '%s',\n", - output->geometry.make, output->geometry.model); - printf("\tsubpixel_orientation: %s, output_transform: %s,\n", - subpixel_orientation, transform); - - wl_list_for_each(mode, &output->modes, link) { - printf("\tmode:\n"); - - printf("\t\twidth: %d px, height: %d px, refresh: %.3f Hz,\n", - mode->width, mode->height, - (float) mode->refresh / 1000); - - printf("\t\tflags:"); - if (mode->flags & WL_OUTPUT_MODE_CURRENT) - printf(" current"); - if (mode->flags & WL_OUTPUT_MODE_PREFERRED) - printf(" preferred"); - printf("\n"); - } -} - -static char -bits2graph(uint32_t value, unsigned bitoffset) -{ - int c = (value >> bitoffset) & 0xff; - - if (isgraph(c) || isspace(c)) - return c; - - return '?'; -} - -static void -fourcc2str(uint32_t format, char *str, int len) -{ - int i; - - assert(len >= 5); - - for (i = 0; i < 4; i++) - str[i] = bits2graph(format, i * 8); - str[i] = '\0'; -} - -static void -print_shm_info(void *data) -{ - char str[5]; - struct shm_info *shm = data; - struct shm_format *format; - - print_global_info(data); - - printf("\tformats:"); - - wl_list_for_each(format, &shm->formats, link) - switch (format->format) { - case WL_SHM_FORMAT_ARGB8888: - printf(" ARGB8888"); - break; - case WL_SHM_FORMAT_XRGB8888: - printf(" XRGB8888"); - break; - case WL_SHM_FORMAT_RGB565: - printf(" RGB565"); - break; - default: - fourcc2str(format->format, str, sizeof(str)); - printf(" '%s'(0x%08x)", str, format->format); - break; - } - - printf("\n"); -} - -static void -print_linux_dmabuf_info(void *data) -{ - char str[5]; - struct linux_dmabuf_info *dmabuf = data; - struct linux_dmabuf_modifier *modifier; - - print_global_info(data); - - printf("\tformats:"); - - wl_list_for_each(modifier, &dmabuf->modifiers, link) { - fourcc2str(modifier->format, str, sizeof(str)); - printf("\n\t'%s'(0x%08x), modifier: 0x%016"PRIx64, str, modifier->format, modifier->modifier); - } - - printf("\n"); -} - -static void -print_seat_info(void *data) -{ - struct seat_info *seat = data; - - print_global_info(data); - - printf("\tname: %s\n", seat->name); - printf("\tcapabilities:"); - - if (seat->capabilities & WL_SEAT_CAPABILITY_POINTER) - printf(" pointer"); - if (seat->capabilities & WL_SEAT_CAPABILITY_KEYBOARD) - printf(" keyboard"); - if (seat->capabilities & WL_SEAT_CAPABILITY_TOUCH) - printf(" touch"); - - printf("\n"); - - if (seat->repeat_rate > 0) - printf("\tkeyboard repeat rate: %d\n", seat->repeat_rate); - if (seat->repeat_delay > 0) - printf("\tkeyboard repeat delay: %d\n", seat->repeat_delay); -} - -static void -keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, - uint32_t format, int fd, uint32_t size) -{ - /* Just so we don’t leak the keymap fd */ - close(fd); -} - -static void -keyboard_handle_enter(void *data, struct wl_keyboard *keyboard, - uint32_t serial, struct wl_surface *surface, - struct wl_array *keys) -{ -} - -static void -keyboard_handle_leave(void *data, struct wl_keyboard *keyboard, - uint32_t serial, struct wl_surface *surface) -{ -} - -static void -keyboard_handle_key(void *data, struct wl_keyboard *keyboard, - uint32_t serial, uint32_t time, uint32_t key, - uint32_t state) -{ -} - -static void -keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard, - uint32_t serial, uint32_t mods_depressed, - uint32_t mods_latched, uint32_t mods_locked, - uint32_t group) -{ -} - -static void -keyboard_handle_repeat_info(void *data, struct wl_keyboard *keyboard, - int32_t rate, int32_t delay) -{ - struct seat_info *seat = data; - - seat->repeat_rate = rate; - seat->repeat_delay = delay; -} - -static const struct wl_keyboard_listener keyboard_listener = { - keyboard_handle_keymap, - keyboard_handle_enter, - keyboard_handle_leave, - keyboard_handle_key, - keyboard_handle_modifiers, - keyboard_handle_repeat_info, -}; - -static void -seat_handle_capabilities(void *data, struct wl_seat *wl_seat, - enum wl_seat_capability caps) -{ - struct seat_info *seat = data; - - seat->capabilities = caps; - - /* we want listen for repeat_info from wl_keyboard, but only - * do so if the seat info is >= 4 and if we actually have a - * keyboard */ - if (seat->global.version < 4) - return; - - if (caps & WL_SEAT_CAPABILITY_KEYBOARD) { - seat->keyboard = wl_seat_get_keyboard(seat->seat); - wl_keyboard_add_listener(seat->keyboard, &keyboard_listener, - seat); - - seat->info->roundtrip_needed = true; - } -} - -static void -seat_handle_name(void *data, struct wl_seat *wl_seat, - const char *name) -{ - struct seat_info *seat = data; - seat->name = xstrdup(name); -} - -static const struct wl_seat_listener seat_listener = { - seat_handle_capabilities, - seat_handle_name, -}; - -static void -destroy_seat_info(void *data) -{ - struct seat_info *seat = data; - - wl_seat_destroy(seat->seat); - - if (seat->name != NULL) - free(seat->name); - - if (seat->keyboard) - wl_keyboard_destroy(seat->keyboard); - - wl_list_remove(&seat->global_link); -} - -static const char * -tablet_tool_type_to_str(enum zwp_tablet_tool_v2_type type) -{ - switch (type) { - case ZWP_TABLET_TOOL_V2_TYPE_PEN: - return "pen"; - case ZWP_TABLET_TOOL_V2_TYPE_ERASER: - return "eraser"; - case ZWP_TABLET_TOOL_V2_TYPE_BRUSH: - return "brush"; - case ZWP_TABLET_TOOL_V2_TYPE_PENCIL: - return "pencil"; - case ZWP_TABLET_TOOL_V2_TYPE_AIRBRUSH: - return "airbrush"; - case ZWP_TABLET_TOOL_V2_TYPE_FINGER: - return "finger"; - case ZWP_TABLET_TOOL_V2_TYPE_MOUSE: - return "mouse"; - case ZWP_TABLET_TOOL_V2_TYPE_LENS: - return "lens"; - } - - return "Unknown type"; -} - -static void -print_tablet_tool_info(const struct tablet_tool_info *info) -{ - printf("\t\ttablet_tool: %s\n", tablet_tool_type_to_str(info->type)); - if (info->hardware_serial) { - printf("\t\t\thardware serial: %" PRIx64 "\n", info->hardware_serial); - } - if (info->hardware_id_wacom) { - printf("\t\t\thardware wacom: %" PRIx64 "\n", info->hardware_id_wacom); - } - - printf("\t\t\tcapabilities:"); - - if (info->has_tilt) { - printf(" tilt"); - } - if (info->has_pressure) { - printf(" pressure"); - } - if (info->has_distance) { - printf(" distance"); - } - if (info->has_rotation) { - printf(" rotation"); - } - if (info->has_slider) { - printf(" slider"); - } - if (info->has_wheel) { - printf(" wheel"); - } - printf("\n"); -} - -static void -destroy_tablet_tool_info(struct tablet_tool_info *info) -{ - wl_list_remove(&info->link); - zwp_tablet_tool_v2_destroy(info->tool); - free(info); -} - -static void -print_tablet_pad_group_info(const struct tablet_pad_group_info *info) -{ - size_t i; - printf("\t\t\tgroup:\n"); - printf("\t\t\t\tmodes: %u\n", info->modes); - printf("\t\t\t\tstrips: %zu\n", info->strips); - printf("\t\t\t\trings: %zu\n", info->rings); - printf("\t\t\t\tbuttons:"); - - for (i = 0; i < info->button_count; ++i) { - printf(" %d", info->buttons[i]); - } - - printf("\n"); -} - -static void -destroy_tablet_pad_group_info(struct tablet_pad_group_info *info) -{ - wl_list_remove(&info->link); - zwp_tablet_pad_group_v2_destroy(info->group); - - if (info->buttons) { - free(info->buttons); - } - free(info); -} - -static void -print_tablet_pad_info(const struct tablet_pad_info *info) -{ - const struct tablet_v2_path *path; - const struct tablet_pad_group_info *group; - - printf("\t\tpad:\n"); - printf("\t\t\tbuttons: %u\n", info->buttons); - - wl_list_for_each(path, &info->paths, link) { - printf("\t\t\tpath: %s\n", path->path); - } - - wl_list_for_each(group, &info->groups, link) { - print_tablet_pad_group_info(group); - } -} - -static void -destroy_tablet_pad_info(struct tablet_pad_info *info) -{ - struct tablet_v2_path *path; - struct tablet_v2_path *tmp_path; - struct tablet_pad_group_info *group; - struct tablet_pad_group_info *tmp_group; - - wl_list_remove(&info->link); - zwp_tablet_pad_v2_destroy(info->pad); - - wl_list_for_each_safe(path, tmp_path, &info->paths, link) { - wl_list_remove(&path->link); - free(path->path); - free(path); - } - - wl_list_for_each_safe(group, tmp_group, &info->groups, link) { - destroy_tablet_pad_group_info(group); - } - - free(info); -} - -static void -print_tablet_info(const struct tablet_info *info) -{ - const struct tablet_v2_path *path; - - printf("\t\ttablet: %s\n", info->name); - printf("\t\t\tvendor: %u\n", info->vid); - printf("\t\t\tproduct: %u\n", info->pid); - - wl_list_for_each(path, &info->paths, link) { - printf("\t\t\tpath: %s\n", path->path); - } -} - -static void -destroy_tablet_info(struct tablet_info *info) -{ - struct tablet_v2_path *path; - struct tablet_v2_path *tmp; - - wl_list_remove(&info->link); - zwp_tablet_v2_destroy(info->tablet); - - if (info->name) { - free(info->name); - } - - wl_list_for_each_safe(path, tmp, &info->paths, link) { - wl_list_remove(&path->link); - free(path->path); - free(path); - } - - free(info); -} - -static void -print_tablet_seat_info(const struct tablet_seat_info *info) -{ - const struct tablet_info *tablet; - const struct tablet_pad_info *pad; - const struct tablet_tool_info *tool; - - printf("\ttablet_seat: %s\n", info->seat_info->name); - - wl_list_for_each(tablet, &info->tablets, link) { - print_tablet_info(tablet); - } - - wl_list_for_each(pad, &info->pads, link) { - print_tablet_pad_info(pad); - } - - wl_list_for_each(tool, &info->tools, link) { - print_tablet_tool_info(tool); - } -} - -static void -destroy_tablet_seat_info(struct tablet_seat_info *info) -{ - struct tablet_info *tablet; - struct tablet_info *tmp_tablet; - struct tablet_pad_info *pad; - struct tablet_pad_info *tmp_pad; - struct tablet_tool_info *tool; - struct tablet_tool_info *tmp_tool; - - wl_list_remove(&info->link); - zwp_tablet_seat_v2_destroy(info->seat); - - wl_list_for_each_safe(tablet, tmp_tablet, &info->tablets, link) { - destroy_tablet_info(tablet); - } - - wl_list_for_each_safe(pad, tmp_pad, &info->pads, link) { - destroy_tablet_pad_info(pad); - } - - wl_list_for_each_safe(tool, tmp_tool, &info->tools, link) { - destroy_tablet_tool_info(tool); - } - - free(info); -} - -static void -print_tablet_v2_info(void *data) -{ - struct tablet_v2_info *info = data; - struct tablet_seat_info *seat; - print_global_info(data); - - wl_list_for_each(seat, &info->seats, link) { - /* Skip tablet_seats without a tablet, they are irrelevant */ - if (wl_list_empty(&seat->pads) && - wl_list_empty(&seat->tablets) && - wl_list_empty(&seat->tools)) { - continue; - } - - print_tablet_seat_info(seat); - } -} - -static void -destroy_tablet_v2_info(void *data) -{ - struct tablet_v2_info *info = data; - struct tablet_seat_info *seat; - struct tablet_seat_info *tmp; - - zwp_tablet_manager_v2_destroy(info->manager); - - wl_list_for_each_safe(seat, tmp, &info->seats, link) { - destroy_tablet_seat_info(seat); - } -} - -static void -handle_tablet_v2_tablet_tool_done(void *data, struct zwp_tablet_tool_v2 *tool) -{ - /* don't bother waiting for this; there's no good reason a - * compositor will wait more than one roundtrip before sending - * these initial events. */ -} - -static void -handle_tablet_v2_tablet_tool_removed(void *data, struct zwp_tablet_tool_v2 *tool) -{ - /* don't bother waiting for this; we never make any request either way. */ -} - -static void -handle_tablet_v2_tablet_tool_type(void *data, struct zwp_tablet_tool_v2 *tool, - uint32_t tool_type) -{ - struct tablet_tool_info *info = data; - info->type = tool_type; -} - -static void -handle_tablet_v2_tablet_tool_hardware_serial(void *data, - struct zwp_tablet_tool_v2 *tool, - uint32_t serial_hi, - uint32_t serial_lo) -{ - struct tablet_tool_info *info = data; - - info->hardware_serial = ((uint64_t) serial_hi) << 32 | - (uint64_t) serial_lo; -} - -static void -handle_tablet_v2_tablet_tool_hardware_id_wacom(void *data, - struct zwp_tablet_tool_v2 *tool, - uint32_t id_hi, uint32_t id_lo) -{ - struct tablet_tool_info *info = data; - - info->hardware_id_wacom = ((uint64_t) id_hi) << 32 | (uint64_t) id_lo; -} - -static void -handle_tablet_v2_tablet_tool_capability(void *data, - struct zwp_tablet_tool_v2 *tool, - uint32_t capability) -{ - struct tablet_tool_info *info = data; - enum zwp_tablet_tool_v2_capability cap = capability; - - switch(cap) { - case ZWP_TABLET_TOOL_V2_CAPABILITY_TILT: - info->has_tilt = true; - break; - case ZWP_TABLET_TOOL_V2_CAPABILITY_PRESSURE: - info->has_pressure = true; - break; - case ZWP_TABLET_TOOL_V2_CAPABILITY_DISTANCE: - info->has_distance = true; - break; - case ZWP_TABLET_TOOL_V2_CAPABILITY_ROTATION: - info->has_rotation = true; - break; - case ZWP_TABLET_TOOL_V2_CAPABILITY_SLIDER: - info->has_slider = true; - break; - case ZWP_TABLET_TOOL_V2_CAPABILITY_WHEEL: - info->has_wheel = true; - break; - } -} - -static void -handle_tablet_v2_tablet_tool_proximity_in(void *data, - struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, - uint32_t serial, struct zwp_tablet_v2 *tablet, - struct wl_surface *surface) -{ - -} - -static void -handle_tablet_v2_tablet_tool_proximity_out(void *data, - struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2) -{ - -} - -static void -handle_tablet_v2_tablet_tool_down(void *data, - struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, - uint32_t serial) -{ - -} - -static void -handle_tablet_v2_tablet_tool_up(void *data, - struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2) -{ - -} - - -static void -handle_tablet_v2_tablet_tool_motion(void *data, - struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, - wl_fixed_t x, - wl_fixed_t y) -{ - -} - -static void -handle_tablet_v2_tablet_tool_pressure(void *data, - struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, - uint32_t pressure) -{ - -} - -static void -handle_tablet_v2_tablet_tool_distance(void *data, - struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, - uint32_t distance) -{ - -} - -static void -handle_tablet_v2_tablet_tool_tilt(void *data, - struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, - wl_fixed_t tilt_x, - wl_fixed_t tilt_y) -{ - -} - -static void -handle_tablet_v2_tablet_tool_rotation(void *data, - struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, - wl_fixed_t degrees) -{ - -} - -static void -handle_tablet_v2_tablet_tool_slider(void *data, - struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, - int32_t position) -{ - -} - -static void -handle_tablet_v2_tablet_tool_wheel(void *data, - struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, - wl_fixed_t degrees, - int32_t clicks) -{ - -} - -static void -handle_tablet_v2_tablet_tool_button(void *data, - struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, - uint32_t serial, - uint32_t button, - uint32_t state) -{ - -} - -static void -handle_tablet_v2_tablet_tool_frame(void *data, - struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, - uint32_t time) -{ - -} - -static const struct zwp_tablet_tool_v2_listener tablet_tool_listener = { - .removed = handle_tablet_v2_tablet_tool_removed, - .done = handle_tablet_v2_tablet_tool_done, - .type = handle_tablet_v2_tablet_tool_type, - .hardware_serial = handle_tablet_v2_tablet_tool_hardware_serial, - .hardware_id_wacom = handle_tablet_v2_tablet_tool_hardware_id_wacom, - .capability = handle_tablet_v2_tablet_tool_capability, - - .proximity_in = handle_tablet_v2_tablet_tool_proximity_in, - .proximity_out = handle_tablet_v2_tablet_tool_proximity_out, - .down = handle_tablet_v2_tablet_tool_down, - .up = handle_tablet_v2_tablet_tool_up, - - .motion = handle_tablet_v2_tablet_tool_motion, - .pressure = handle_tablet_v2_tablet_tool_pressure, - .distance = handle_tablet_v2_tablet_tool_distance, - .tilt = handle_tablet_v2_tablet_tool_tilt, - .rotation = handle_tablet_v2_tablet_tool_rotation, - .slider = handle_tablet_v2_tablet_tool_slider, - .wheel = handle_tablet_v2_tablet_tool_wheel, - .button = handle_tablet_v2_tablet_tool_button, - .frame = handle_tablet_v2_tablet_tool_frame, -}; - -static void add_tablet_v2_tablet_tool_info(void *data, - struct zwp_tablet_seat_v2 *tablet_seat_v2, - struct zwp_tablet_tool_v2 *tool) -{ - struct tablet_seat_info *tablet_seat = data; - struct tablet_tool_info *tool_info = xzalloc(sizeof *tool_info); - - tool_info->tool = tool; - wl_list_insert(&tablet_seat->tools, &tool_info->link); - - zwp_tablet_tool_v2_add_listener(tool, &tablet_tool_listener, tool_info); -} - -static void -handle_tablet_v2_tablet_pad_group_mode_switch(void *data, - struct zwp_tablet_pad_group_v2 *zwp_tablet_pad_group_v2, - uint32_t time, uint32_t serial, uint32_t mode) -{ - /* This shouldn't ever happen */ -} - -static void -handle_tablet_v2_tablet_pad_group_done(void *data, - struct zwp_tablet_pad_group_v2 *group) -{ - /* don't bother waiting for this; there's no good reason a - * compositor will wait more than one roundtrip before sending - * these initial events. */ -} - -static void -handle_tablet_v2_tablet_pad_group_modes(void *data, - struct zwp_tablet_pad_group_v2 *group, - uint32_t modes) -{ - struct tablet_pad_group_info *info = data; - info->modes = modes; -} - -static void -handle_tablet_v2_tablet_pad_group_buttons(void *data, - struct zwp_tablet_pad_group_v2 *group, - struct wl_array *buttons) -{ - struct tablet_pad_group_info *info = data; - - info->button_count = buttons->size / sizeof(int); - info->buttons = xzalloc(buttons->size); - memcpy(info->buttons, buttons->data, buttons->size); -} - -static void -handle_tablet_v2_tablet_pad_group_ring(void *data, - struct zwp_tablet_pad_group_v2 *group, - struct zwp_tablet_pad_ring_v2 *ring) -{ - struct tablet_pad_group_info *info = data; - ++info->rings; - - zwp_tablet_pad_ring_v2_destroy(ring); -} - -static void -handle_tablet_v2_tablet_pad_group_strip(void *data, - struct zwp_tablet_pad_group_v2 *group, - struct zwp_tablet_pad_strip_v2 *strip) -{ - struct tablet_pad_group_info *info = data; - ++info->strips; - - zwp_tablet_pad_strip_v2_destroy(strip); -} - -static const struct zwp_tablet_pad_group_v2_listener tablet_pad_group_listener = { - .buttons = handle_tablet_v2_tablet_pad_group_buttons, - .modes = handle_tablet_v2_tablet_pad_group_modes, - .ring = handle_tablet_v2_tablet_pad_group_ring, - .strip = handle_tablet_v2_tablet_pad_group_strip, - .done = handle_tablet_v2_tablet_pad_group_done, - .mode_switch = handle_tablet_v2_tablet_pad_group_mode_switch, -}; - -static void -handle_tablet_v2_tablet_pad_group(void *data, - struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2, - struct zwp_tablet_pad_group_v2 *pad_group) -{ - struct tablet_pad_info *pad_info = data; - struct tablet_pad_group_info *group = xzalloc(sizeof *group); - - wl_list_insert(&pad_info->groups, &group->link); - group->group = pad_group; - zwp_tablet_pad_group_v2_add_listener(pad_group, - &tablet_pad_group_listener, group); -} - -static void -handle_tablet_v2_tablet_pad_path(void *data, struct zwp_tablet_pad_v2 *pad, - const char *path) -{ - struct tablet_pad_info *pad_info = data; - struct tablet_v2_path *path_elem = xzalloc(sizeof *path_elem); - path_elem->path = xstrdup(path); - - wl_list_insert(&pad_info->paths, &path_elem->link); -} - -static void -handle_tablet_v2_tablet_pad_buttons(void *data, struct zwp_tablet_pad_v2 *pad, - uint32_t buttons) -{ - struct tablet_pad_info *pad_info = data; - - pad_info->buttons = buttons; -} - -static void -handle_tablet_v2_tablet_pad_done(void *data, struct zwp_tablet_pad_v2 *pad) -{ - /* don't bother waiting for this; there's no good reason a - * compositor will wait more than one roundtrip before sending - * these initial events. */ -} - -static void -handle_tablet_v2_tablet_pad_removed(void *data, struct zwp_tablet_pad_v2 *pad) -{ - /* don't bother waiting for this; We never make any request that's not - * allowed to be issued either way. */ -} - -static void -handle_tablet_v2_tablet_pad_button(void *data, struct zwp_tablet_pad_v2 *pad, - uint32_t time, uint32_t button, uint32_t state) -{ - /* we don't have a surface, so this can't ever happen */ -} - -static void -handle_tablet_v2_tablet_pad_enter(void *data, struct zwp_tablet_pad_v2 *pad, - uint32_t serial, - struct zwp_tablet_v2 *tablet, - struct wl_surface *surface) -{ - /* we don't have a surface, so this can't ever happen */ -} - -static void -handle_tablet_v2_tablet_pad_leave(void *data, struct zwp_tablet_pad_v2 *pad, - uint32_t serial, struct wl_surface *surface) -{ - /* we don't have a surface, so this can't ever happen */ -} - -static const struct zwp_tablet_pad_v2_listener tablet_pad_listener = { - .group = handle_tablet_v2_tablet_pad_group, - .path = handle_tablet_v2_tablet_pad_path, - .buttons = handle_tablet_v2_tablet_pad_buttons, - .done = handle_tablet_v2_tablet_pad_done, - .removed = handle_tablet_v2_tablet_pad_removed, - .button = handle_tablet_v2_tablet_pad_button, - .enter = handle_tablet_v2_tablet_pad_enter, - .leave = handle_tablet_v2_tablet_pad_leave, -}; - -static void add_tablet_v2_tablet_pad_info(void *data, - struct zwp_tablet_seat_v2 *tablet_seat_v2, - struct zwp_tablet_pad_v2 *pad) -{ - struct tablet_seat_info *tablet_seat = data; - struct tablet_pad_info *pad_info = xzalloc(sizeof *pad_info); - - wl_list_init(&pad_info->paths); - wl_list_init(&pad_info->groups); - pad_info->pad = pad; - wl_list_insert(&tablet_seat->pads, &pad_info->link); - - zwp_tablet_pad_v2_add_listener(pad, &tablet_pad_listener, pad_info); -} - -static void -handle_tablet_v2_tablet_name(void *data, struct zwp_tablet_v2 *zwp_tablet_v2, - const char *name) -{ - struct tablet_info *tablet_info = data; - tablet_info->name = xstrdup(name); -} - -static void -handle_tablet_v2_tablet_path(void *data, struct zwp_tablet_v2 *zwp_tablet_v2, - const char *path) -{ - struct tablet_info *tablet_info = data; - struct tablet_v2_path *path_elem = xzalloc(sizeof *path_elem); - path_elem->path = xstrdup(path); - - wl_list_insert(&tablet_info->paths, &path_elem->link); -} - -static void -handle_tablet_v2_tablet_id(void *data, struct zwp_tablet_v2 *zwp_tablet_v2, - uint32_t vid, uint32_t pid) -{ - struct tablet_info *tablet_info = data; - - tablet_info->vid = vid; - tablet_info->pid = pid; -} - -static void -handle_tablet_v2_tablet_done(void *data, struct zwp_tablet_v2 *zwp_tablet_v2) -{ - /* don't bother waiting for this; there's no good reason a - * compositor will wait more than one roundtrip before sending - * these initial events. */ -} - -static void -handle_tablet_v2_tablet_removed(void *data, struct zwp_tablet_v2 *zwp_tablet_v2) -{ - /* don't bother waiting for this; We never make any request that's not - * allowed to be issued either way. */ -} - -static const struct zwp_tablet_v2_listener tablet_listener = { - .name = handle_tablet_v2_tablet_name, - .id = handle_tablet_v2_tablet_id, - .path = handle_tablet_v2_tablet_path, - .done = handle_tablet_v2_tablet_done, - .removed = handle_tablet_v2_tablet_removed -}; - -static void -add_tablet_v2_tablet_info(void *data, struct zwp_tablet_seat_v2 *tablet_seat_v2, - struct zwp_tablet_v2 *tablet) -{ - struct tablet_seat_info *tablet_seat = data; - struct tablet_info *tablet_info = xzalloc(sizeof *tablet_info); - - wl_list_init(&tablet_info->paths); - tablet_info->tablet = tablet; - wl_list_insert(&tablet_seat->tablets, &tablet_info->link); - - zwp_tablet_v2_add_listener(tablet, &tablet_listener, tablet_info); -} - -static const struct zwp_tablet_seat_v2_listener tablet_seat_listener = { - .tablet_added = add_tablet_v2_tablet_info, - .pad_added = add_tablet_v2_tablet_pad_info, - .tool_added = add_tablet_v2_tablet_tool_info, -}; - -static void -add_tablet_seat_info(struct tablet_v2_info *tablet_info, struct seat_info *seat) -{ - struct tablet_seat_info *tablet_seat = xzalloc(sizeof *tablet_seat); - - wl_list_insert(&tablet_info->seats, &tablet_seat->link); - tablet_seat->seat = zwp_tablet_manager_v2_get_tablet_seat( - tablet_info->manager, seat->seat); - zwp_tablet_seat_v2_add_listener(tablet_seat->seat, - &tablet_seat_listener, tablet_seat); - - wl_list_init(&tablet_seat->pads); - wl_list_init(&tablet_seat->tablets); - wl_list_init(&tablet_seat->tools); - tablet_seat->seat_info = seat; - - tablet_info->info->roundtrip_needed = true; -} - -static void -add_tablet_v2_info(struct weston_info *info, uint32_t id, uint32_t version) -{ - struct seat_info *seat; - struct tablet_v2_info *tablet = xzalloc(sizeof *tablet); - - wl_list_init(&tablet->seats); - tablet->info = info; - - init_global_info(info, &tablet->global, id, - zwp_tablet_manager_v2_interface.name, version); - tablet->global.print = print_tablet_v2_info; - tablet->global.destroy = destroy_tablet_v2_info; - - tablet->manager = wl_registry_bind(info->registry, - id, &zwp_tablet_manager_v2_interface, 1); - - wl_list_for_each(seat, &info->seats, global_link) { - add_tablet_seat_info(tablet, seat); - } - - info->tablet_info = tablet; -} - -static void -destroy_xdg_output_v1_info(struct xdg_output_v1_info *info) -{ - wl_list_remove(&info->link); - zxdg_output_v1_destroy(info->xdg_output); - free(info->name); - free(info->description); - free(info); -} - -static void -print_xdg_output_v1_info(const struct xdg_output_v1_info *info) -{ - printf("\txdg_output_v1\n"); - printf("\t\toutput: %d\n", info->output->global.id); - if (info->name) - printf("\t\tname: '%s'\n", info->name); - if (info->description) - printf("\t\tdescription: '%s'\n", info->description); - printf("\t\tlogical_x: %d, logical_y: %d\n", - info->logical.x, info->logical.y); - printf("\t\tlogical_width: %d, logical_height: %d\n", - info->logical.width, info->logical.height); -} - -static void -print_xdg_output_manager_v1_info(void *data) -{ - struct xdg_output_manager_v1_info *info = data; - struct xdg_output_v1_info *output; - - print_global_info(data); - - wl_list_for_each(output, &info->outputs, link) - print_xdg_output_v1_info(output); -} - -static void -destroy_xdg_output_manager_v1_info(void *data) -{ - struct xdg_output_manager_v1_info *info = data; - struct xdg_output_v1_info *output, *tmp; - - zxdg_output_manager_v1_destroy(info->manager); - - wl_list_for_each_safe(output, tmp, &info->outputs, link) - destroy_xdg_output_v1_info(output); -} - -static void -handle_xdg_output_v1_logical_position(void *data, struct zxdg_output_v1 *output, - int32_t x, int32_t y) -{ - struct xdg_output_v1_info *xdg_output = data; - xdg_output->logical.x = x; - xdg_output->logical.y = y; -} - -static void -handle_xdg_output_v1_logical_size(void *data, struct zxdg_output_v1 *output, - int32_t width, int32_t height) -{ - struct xdg_output_v1_info *xdg_output = data; - xdg_output->logical.width = width; - xdg_output->logical.height = height; -} - -static void -handle_xdg_output_v1_done(void *data, struct zxdg_output_v1 *output) -{ - /* Don't bother waiting for this; there's no good reason a - * compositor will wait more than one roundtrip before sending - * these initial events. */ -} - -static void -handle_xdg_output_v1_name(void *data, struct zxdg_output_v1 *output, - const char *name) -{ - struct xdg_output_v1_info *xdg_output = data; - xdg_output->name = strdup(name); -} - -static void -handle_xdg_output_v1_description(void *data, struct zxdg_output_v1 *output, - const char *description) -{ - struct xdg_output_v1_info *xdg_output = data; - xdg_output->description = strdup(description); -} - -static const struct zxdg_output_v1_listener xdg_output_v1_listener = { - .logical_position = handle_xdg_output_v1_logical_position, - .logical_size = handle_xdg_output_v1_logical_size, - .done = handle_xdg_output_v1_done, - .name = handle_xdg_output_v1_name, - .description = handle_xdg_output_v1_description, -}; - -static void -add_xdg_output_v1_info(struct xdg_output_manager_v1_info *manager_info, - struct output_info *output) -{ - struct xdg_output_v1_info *xdg_output = xzalloc(sizeof *xdg_output); - - wl_list_insert(&manager_info->outputs, &xdg_output->link); - xdg_output->xdg_output = zxdg_output_manager_v1_get_xdg_output( - manager_info->manager, output->output); - zxdg_output_v1_add_listener(xdg_output->xdg_output, - &xdg_output_v1_listener, xdg_output); - - xdg_output->output = output; - - manager_info->info->roundtrip_needed = true; -} - -static void -add_xdg_output_manager_v1_info(struct weston_info *info, uint32_t id, - uint32_t version) -{ - struct output_info *output; - struct xdg_output_manager_v1_info *manager = xzalloc(sizeof *manager); - - wl_list_init(&manager->outputs); - manager->info = info; - - init_global_info(info, &manager->global, id, - zxdg_output_manager_v1_interface.name, version); - manager->global.print = print_xdg_output_manager_v1_info; - manager->global.destroy = destroy_xdg_output_manager_v1_info; - - manager->manager = wl_registry_bind(info->registry, id, - &zxdg_output_manager_v1_interface, version > 2 ? 2 : version); - - wl_list_for_each(output, &info->outputs, global_link) - add_xdg_output_v1_info(manager, output); - - info->xdg_output_manager_v1_info = manager; -} - -static void -add_seat_info(struct weston_info *info, uint32_t id, uint32_t version) -{ - struct seat_info *seat = xzalloc(sizeof *seat); - - /* required to set roundtrip_needed to true in capabilities - * handler */ - seat->info = info; - - init_global_info(info, &seat->global, id, "wl_seat", version); - seat->global.print = print_seat_info; - seat->global.destroy = destroy_seat_info; - - seat->seat = wl_registry_bind(info->registry, - id, &wl_seat_interface, MIN(version, 4)); - wl_seat_add_listener(seat->seat, &seat_listener, seat); - - seat->repeat_rate = seat->repeat_delay = -1; - - info->roundtrip_needed = true; - wl_list_insert(&info->seats, &seat->global_link); - - if (info->tablet_info) { - add_tablet_seat_info(info->tablet_info, seat); - } -} - -static void -shm_handle_format(void *data, struct wl_shm *wl_shm, uint32_t format) -{ - struct shm_info *shm = data; - struct shm_format *shm_format = xzalloc(sizeof *shm_format); - - wl_list_insert(&shm->formats, &shm_format->link); - shm_format->format = format; -} - -static const struct wl_shm_listener shm_listener = { - shm_handle_format, -}; - -static void -destroy_shm_info(void *data) -{ - struct shm_info *shm = data; - struct shm_format *format, *tmp; - - wl_list_for_each_safe(format, tmp, &shm->formats, link) { - wl_list_remove(&format->link); - free(format); - } - - wl_shm_destroy(shm->shm); -} - -static void -add_shm_info(struct weston_info *info, uint32_t id, uint32_t version) -{ - struct shm_info *shm = xzalloc(sizeof *shm); - - init_global_info(info, &shm->global, id, "wl_shm", version); - shm->global.print = print_shm_info; - shm->global.destroy = destroy_shm_info; - - wl_list_init(&shm->formats); - - shm->shm = wl_registry_bind(info->registry, - id, &wl_shm_interface, 1); - wl_shm_add_listener(shm->shm, &shm_listener, shm); - - info->roundtrip_needed = true; -} - -static void -linux_dmabuf_handle_format(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1, uint32_t format) -{ - /* This is a deprecated event, don’t use it. */ -} - -static void -linux_dmabuf_handle_modifier(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1, uint32_t format, uint32_t modifier_hi, uint32_t modifier_lo) -{ - struct linux_dmabuf_info *dmabuf = data; - struct linux_dmabuf_modifier *linux_dmabuf_modifier = xzalloc(sizeof *linux_dmabuf_modifier); - - wl_list_insert(&dmabuf->modifiers, &linux_dmabuf_modifier->link); - linux_dmabuf_modifier->format = format; - linux_dmabuf_modifier->modifier = ((uint64_t)modifier_hi) << 32 | modifier_lo; -} - -static const struct zwp_linux_dmabuf_v1_listener linux_dmabuf_listener = { - linux_dmabuf_handle_format, - linux_dmabuf_handle_modifier, -}; - -static void -destroy_linux_dmabuf_info(void *data) -{ - struct linux_dmabuf_info *dmabuf = data; - struct linux_dmabuf_modifier *modifier, *tmp; - - wl_list_for_each_safe(modifier, tmp, &dmabuf->modifiers, link) { - wl_list_remove(&modifier->link); - free(modifier); - } - - zwp_linux_dmabuf_v1_destroy(dmabuf->dmabuf); -} - -static void -add_linux_dmabuf_info(struct weston_info *info, uint32_t id, uint32_t version) -{ - struct linux_dmabuf_info *dmabuf = xzalloc(sizeof *dmabuf); - - init_global_info(info, &dmabuf->global, id, "zwp_linux_dmabuf_v1", version); - dmabuf->global.print = print_linux_dmabuf_info; - dmabuf->global.destroy = destroy_linux_dmabuf_info; - - wl_list_init(&dmabuf->modifiers); - - if (version >= 3) { - dmabuf->dmabuf = wl_registry_bind(info->registry, - id, &zwp_linux_dmabuf_v1_interface, 3); - zwp_linux_dmabuf_v1_add_listener(dmabuf->dmabuf, &linux_dmabuf_listener, dmabuf); - - info->roundtrip_needed = true; - } -} - -static void -output_handle_geometry(void *data, struct wl_output *wl_output, - int32_t x, int32_t y, - int32_t physical_width, int32_t physical_height, - int32_t subpixel, - const char *make, const char *model, - int32_t output_transform) -{ - struct output_info *output = data; - - output->geometry.x = x; - output->geometry.y = y; - output->geometry.physical_width = physical_width; - output->geometry.physical_height = physical_height; - output->geometry.subpixel = subpixel; - output->geometry.make = xstrdup(make); - output->geometry.model = xstrdup(model); - output->geometry.output_transform = output_transform; -} - -static void -output_handle_mode(void *data, struct wl_output *wl_output, - uint32_t flags, int32_t width, int32_t height, - int32_t refresh) -{ - struct output_info *output = data; - struct output_mode *mode = xmalloc(sizeof *mode); - - mode->flags = flags; - mode->width = width; - mode->height = height; - mode->refresh = refresh; - - wl_list_insert(output->modes.prev, &mode->link); -} - -static void -output_handle_done(void *data, struct wl_output *wl_output) -{ - /* don't bother waiting for this; there's no good reason a - * compositor will wait more than one roundtrip before sending - * these initial events. */ -} - -static void -output_handle_scale(void *data, struct wl_output *wl_output, - int32_t scale) -{ - struct output_info *output = data; - - output->geometry.scale = scale; -} - -static const struct wl_output_listener output_listener = { - output_handle_geometry, - output_handle_mode, - output_handle_done, - output_handle_scale, -}; - -static void -destroy_output_info(void *data) -{ - struct output_info *output = data; - struct output_mode *mode, *tmp; - - wl_output_destroy(output->output); - - if (output->geometry.make != NULL) - free(output->geometry.make); - if (output->geometry.model != NULL) - free(output->geometry.model); - - wl_list_for_each_safe(mode, tmp, &output->modes, link) { - wl_list_remove(&mode->link); - free(mode); - } -} - -static void -add_output_info(struct weston_info *info, uint32_t id, uint32_t version) -{ - struct output_info *output = xzalloc(sizeof *output); - - init_global_info(info, &output->global, id, "wl_output", version); - output->global.print = print_output_info; - output->global.destroy = destroy_output_info; - - output->version = MIN(version, 2); - output->geometry.scale = 1; - wl_list_init(&output->modes); - - output->output = wl_registry_bind(info->registry, id, - &wl_output_interface, output->version); - wl_output_add_listener(output->output, &output_listener, - output); - - info->roundtrip_needed = true; - wl_list_insert(&info->outputs, &output->global_link); - - if (info->xdg_output_manager_v1_info) - add_xdg_output_v1_info(info->xdg_output_manager_v1_info, - output); -} - -static void -destroy_presentation_info(void *info) -{ - struct presentation_info *prinfo = info; - - wp_presentation_destroy(prinfo->presentation); -} - -static const char * -clock_name(clockid_t clk_id) -{ - static const char *names[] = { - [CLOCK_REALTIME] = "CLOCK_REALTIME", - [CLOCK_MONOTONIC] = "CLOCK_MONOTONIC", - [CLOCK_MONOTONIC_RAW] = "CLOCK_MONOTONIC_RAW", - [CLOCK_REALTIME_COARSE] = "CLOCK_REALTIME_COARSE", - [CLOCK_MONOTONIC_COARSE] = "CLOCK_MONOTONIC_COARSE", -#ifdef CLOCK_BOOTTIME - [CLOCK_BOOTTIME] = "CLOCK_BOOTTIME", -#endif - }; - - if (clk_id < 0 || (unsigned)clk_id >= ARRAY_LENGTH(names)) - return "unknown"; - - return names[clk_id]; -} - -static void -print_presentation_info(void *info) -{ - struct presentation_info *prinfo = info; - - print_global_info(info); - - printf("\tpresentation clock id: %d (%s)\n", - prinfo->clk_id, clock_name(prinfo->clk_id)); -} - -static void -presentation_handle_clock_id(void *data, struct wp_presentation *presentation, - uint32_t clk_id) -{ - struct presentation_info *prinfo = data; - - prinfo->clk_id = clk_id; -} - -static const struct wp_presentation_listener presentation_listener = { - presentation_handle_clock_id -}; - -static void -add_presentation_info(struct weston_info *info, uint32_t id, uint32_t version) -{ - struct presentation_info *prinfo = xzalloc(sizeof *prinfo); - - init_global_info(info, &prinfo->global, id, - wp_presentation_interface.name, version); - prinfo->global.print = print_presentation_info; - prinfo->global.destroy = destroy_presentation_info; - - prinfo->clk_id = -1; - prinfo->presentation = wl_registry_bind(info->registry, id, - &wp_presentation_interface, 1); - wp_presentation_add_listener(prinfo->presentation, - &presentation_listener, prinfo); - - info->roundtrip_needed = true; -} - -static void -destroy_global_info(void *data) -{ -} - -static void -add_global_info(struct weston_info *info, uint32_t id, - const char *interface, uint32_t version) -{ - struct global_info *global = xzalloc(sizeof *global); - - init_global_info(info, global, id, interface, version); - global->print = print_global_info; - global->destroy = destroy_global_info; -} - -static void -global_handler(void *data, struct wl_registry *registry, uint32_t id, - const char *interface, uint32_t version) -{ - struct weston_info *info = data; - - if (!strcmp(interface, "wl_seat")) - add_seat_info(info, id, version); - else if (!strcmp(interface, "wl_shm")) - add_shm_info(info, id, version); - else if (!strcmp(interface, "zwp_linux_dmabuf_v1")) - add_linux_dmabuf_info(info, id, version); - else if (!strcmp(interface, "wl_output")) - add_output_info(info, id, version); - else if (!strcmp(interface, wp_presentation_interface.name)) - add_presentation_info(info, id, version); - else if (!strcmp(interface, zwp_tablet_manager_v2_interface.name)) - add_tablet_v2_info(info, id, version); - else if (!strcmp(interface, zxdg_output_manager_v1_interface.name)) - add_xdg_output_manager_v1_info(info, id, version); - else - add_global_info(info, id, interface, version); -} - -static void -global_remove_handler(void *data, struct wl_registry *registry, uint32_t name) -{ -} - -static const struct wl_registry_listener registry_listener = { - global_handler, - global_remove_handler -}; - -static void -print_infos(struct wl_list *infos) -{ - struct global_info *info; - - wl_list_for_each(info, infos, link) - info->print(info); -} - -static void -destroy_info(void *data) -{ - struct global_info *global = data; - - global->destroy(data); - wl_list_remove(&global->link); - free(global->interface); - free(data); -} - -static void -destroy_infos(struct wl_list *infos) -{ - struct global_info *info, *tmp; - wl_list_for_each_safe(info, tmp, infos, link) - destroy_info(info); -} - -int -main(int argc, char **argv) -{ - struct weston_info info; - - info.display = wl_display_connect(NULL); - if (!info.display) { - fprintf(stderr, "failed to create display: %s\n", - strerror(errno)); - return -1; - } - - fprintf(stderr, "\n"); - fprintf(stderr, "*** Please use wayland-info instead\n"); - fprintf(stderr, "*** weston-info is deprecated and will be removed in a future version\n"); - fprintf(stderr, "\n"); - - info.tablet_info = NULL; - info.xdg_output_manager_v1_info = NULL; - wl_list_init(&info.infos); - wl_list_init(&info.seats); - wl_list_init(&info.outputs); - - info.registry = wl_display_get_registry(info.display); - wl_registry_add_listener(info.registry, ®istry_listener, &info); - - do { - info.roundtrip_needed = false; - wl_display_roundtrip(info.display); - } while (info.roundtrip_needed); - - print_infos(&info.infos); - destroy_infos(&info.infos); - - wl_registry_destroy(info.registry); - wl_display_disconnect(info.display); - - return 0; -} From 2ab726b421745a14703654aeec6f0085b5e500a6 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Mon, 31 Jan 2022 12:40:18 +0200 Subject: [PATCH 038/609] weston.ini.man: Clarify what startup-animation means This isn't about opening new windows but the start-up animation that desktop-shell does when it is being brought on/started. Signed-off-by: Marius Vlad --- man/weston.ini.man | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/weston.ini.man b/man/weston.ini.man index 5877425c..18b5b2e1 100644 --- a/man/weston.ini.man +++ b/man/weston.ini.man @@ -422,7 +422,7 @@ sets the effect used when closing windows (string). Can be By default, the fade animation is used. .TP 7 .BI "startup-animation=" fade -sets the effect used for opening new windows (string). Can be +sets the effect used by desktop-shell when starting up (string). Can be .B fade, .B none. By default, the fade animation is used. From 73b17da7d55edbd7f6812a8fad1ac1637c098985 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Fri, 4 Feb 2022 12:12:03 +0200 Subject: [PATCH 039/609] meson.build: Bump libweston major version With commit 'compositor: Remove desktop zoom' weston_output_zoom was removed from weston_output thus needing a libweston major bump. Signed-off-by: Marius Vlad Suggested-by: Pekka Paalanen --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index a20716be..1c73e130 100644 --- a/meson.build +++ b/meson.build @@ -10,7 +10,7 @@ project('weston', license: 'MIT/Expat', ) -libweston_major = 10 +libweston_major = 11 # libweston_revision is manufactured to follow the autotools build's # library file naming, thanks to libtool From 8b04534c76390cda00059e804b6f3f0bf92a56b8 Mon Sep 17 00:00:00 2001 From: Robert Mader Date: Wed, 26 Jan 2022 22:04:52 +0100 Subject: [PATCH 040/609] libweston/compositor: Do not map subsurfaces without buffer We can end in `subsurface_committed()` in different scenarios without the surface having an attached buffer. While setting the mapped state to `true` in that case doesn't matter for that (sub)surface itself, it triggers its own child subsurfaces to get mapped when they shouldn't. Closes https://gitlab.freedesktop.org/wayland/weston/-/issues/426 Signed-off-by: Robert Mader --- libweston/compositor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libweston/compositor.c b/libweston/compositor.c index bb2aa62d..e44f65ee 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -4364,7 +4364,7 @@ subsurface_committed(struct weston_surface *surface, int32_t dx, int32_t dy) */ if (!weston_surface_is_mapped(surface)) { - surface->is_mapped = true; + surface->is_mapped = surface->buffer_ref.buffer != NULL; /* Cannot call weston_view_update_transform(), * because that would call it also for the parent surface, From c83f0a153927d8d775e093b6a8a99bd42e16a755 Mon Sep 17 00:00:00 2001 From: Robert Mader Date: Wed, 26 Jan 2022 12:53:31 +0100 Subject: [PATCH 041/609] tests: Add test for subsurfaces mapping hierachies Test different scenarios where child subsurfaces of unmapped subsurfaces would get mapped. This test will fail in various ways without the commit "libweston/compositor: Do not map subsurfaces without buffer" Also try to test potential regressions of that patch. Signed-off-by: Robert Mader --- .../reference/subsurface_empty_mapping-00.png | Bin 0 -> 825 bytes .../reference/subsurface_empty_mapping-01.png | Bin 0 -> 887 bytes tests/subsurface-shot-test.c | 129 ++++++++++++++++++ 3 files changed, 129 insertions(+) create mode 100644 tests/reference/subsurface_empty_mapping-00.png create mode 100644 tests/reference/subsurface_empty_mapping-01.png diff --git a/tests/reference/subsurface_empty_mapping-00.png b/tests/reference/subsurface_empty_mapping-00.png new file mode 100644 index 0000000000000000000000000000000000000000..f127154cb68edf8e0148ac0de00ea5ca360b371b GIT binary patch literal 825 zcmeAS@N?(olHy`uVBq!ia0y~yU~~YoKX5Ps$$$P@Hb9Ck$=lt9;Xep2*t>i(0|V1L zPZ!6KiaBquI`T3(inusVQ%IX=T;|45HlbP8=FQXRsjEf4JYCvf{FeQ~mpO6_G3p5% z!Uipz5{FoL5}Dcz7@cP{DEb@_P@})_w^?!lpMKXZQ%~7*ecrqRZn;6=rU3{BqO9ET rIHtOaxnLsufW{b6PNij%+Fi(0|T>; zr;B4q#hkZyHs&675Mgz^7+@G;`MhYwowqAwR!dtJF`FDbe9HJIqkiO{H`7XU#236w z{>G4C&*(g(LDA=cfLa2Fut5u_#32@*M5Z4^3a+25v=P|zcmHh{{K++_YU9fzz0l&CA0`d zP}yAD1y9Vj{q2`O{{ca}tB6tDnm{r-UW|ZA1@J literal 0 HcmV?d00001 diff --git a/tests/subsurface-shot-test.c b/tests/subsurface-shot-test.c index f9b967ea..16534497 100644 --- a/tests/subsurface-shot-test.c +++ b/tests/subsurface-shot-test.c @@ -280,3 +280,132 @@ TEST(subsurface_sync_damage_buffer) wl_subcompositor_destroy(subco); client_destroy(client); } + +TEST(subsurface_empty_mapping) +{ + struct client *client; + struct wl_subcompositor *subco; + struct wp_viewporter *viewporter; + struct buffer *bufs[3] = { 0 }; + struct wl_surface *surf[3] = { 0 }; + struct wl_subsurface *sub[3] = { 0 }; + struct wp_viewport *viewport; + struct rectangle clip = { 40, 40, 280, 200 }; + int fail = 0; + unsigned i; + pixman_color_t red; + pixman_color_t blue; + pixman_color_t green; + + color_rgb888(&red, 255, 0, 0); + color_rgb888(&blue, 0, 0, 255); + color_rgb888(&green, 0, 255, 0); + + client = create_client_and_test_surface(100, 50, 100, 100); + assert(client); + subco = get_subcompositor(client); + viewporter = bind_to_singleton_global(client, + &wp_viewporter_interface, 1); + + /* move the pointer clearly away from our screenshooting area */ + weston_test_move_pointer(client->test->weston_test, 0, 1, 0, 2, 30); + + /* make the parent surface red */ + surf[0] = client->surface->wl_surface; + client->surface->wl_surface = NULL; /* we stole it and destroy it */ + bufs[0] = surface_commit_color(client, surf[0], &red, 100, 100); + /* sub[0] is not used */ + + fail += check_screen(client, "subsurface_empty_mapping", 0, &clip, 0); + + /* create an empty subsurface on top */ + surf[1] = wl_compositor_create_surface(client->wl_compositor); + sub[1] = wl_subcompositor_get_subsurface(subco, surf[1], surf[0]); + wl_subsurface_set_desync (sub[1]); + + wl_subsurface_set_position(sub[1], 20, 20); + wl_surface_commit(surf[0]); + + fail += check_screen(client, "subsurface_empty_mapping", 0, &clip, 1); + + /* create a green subsurface on top */ + surf[2] = wl_compositor_create_surface(client->wl_compositor); + sub[2] = wl_subcompositor_get_subsurface(subco, surf[2], surf[1]); + wl_subsurface_set_desync (sub[2]); + bufs[2] = surface_commit_color(client, surf[2], &green, 100, 100); + + wl_subsurface_set_position(sub[2], 20, 20); + wl_surface_commit(surf[1]); + + fail += check_screen(client, "subsurface_empty_mapping", 0, &clip, 2); + + wl_surface_attach(surf[1], NULL, 0, 0); + wl_surface_commit(surf[1]); + + fail += check_screen(client, "subsurface_empty_mapping", 0, &clip, 3); + + wl_surface_set_buffer_scale (surf[1], 1); + wl_surface_commit(surf[1]); + + fail += check_screen(client, "subsurface_empty_mapping", 0, &clip, 4); + + viewport = wp_viewporter_get_viewport(viewporter, surf[1]); + wp_viewport_set_destination(viewport, 5, 5); + wl_surface_commit(surf[1]); + + fail += check_screen(client, "subsurface_empty_mapping", 0, &clip, 5); + + wp_viewport_set_destination(viewport, -1, -1); + wl_surface_commit(surf[1]); + + fail += check_screen(client, "subsurface_empty_mapping", 0, &clip, 6); + + /* map the previously empty middle surface with a blue buffer */ + bufs[1] = surface_commit_color(client, surf[1], &blue, 100, 100); + + fail += check_screen(client, "subsurface_empty_mapping", 1, &clip, 7); + + /* try to trigger a recomputation of the buffer size with the + * shm-buffer potentially being released already */ + wl_surface_set_buffer_scale (surf[1], 1); + wl_surface_commit(surf[1]); + + fail += check_screen(client, "subsurface_empty_mapping", 1, &clip, 8); + + /* try more */ + wp_viewport_set_destination(viewport, 100, 100); + wl_surface_commit(surf[1]); + + fail += check_screen(client, "subsurface_empty_mapping", 1, &clip, 9); + + /* unmap the middle surface again to ensure recursive unmapping */ + wl_surface_attach(surf[1], NULL, 0, 0); + wl_surface_commit(surf[1]); + + fail += check_screen(client, "subsurface_empty_mapping", 0, &clip, 10); + + /* remap middle surface to ensure recursive mapping */ + bufs[1] = surface_commit_color(client, surf[1], &blue, 100, 100); + + fail += check_screen(client, "subsurface_empty_mapping", 1, &clip, 11); + + assert(fail == 0); + + wp_viewport_destroy(viewport); + + for (i = 0; i < ARRAY_LENGTH(sub); i++) + if (sub[i]) + wl_subsurface_destroy(sub[i]); + + for (i = 0; i < ARRAY_LENGTH(surf); i++) + if (surf[i]) + wl_surface_destroy(surf[i]); + + for (i = 0; i < ARRAY_LENGTH(bufs); i++) + if (bufs[i]) + buffer_destroy(bufs[i]); + + wp_viewporter_destroy(viewporter); + wl_subcompositor_destroy(subco); + client_destroy(client); +} From 0b5c75f540084441c6e97df2c89513ca1b7ec33f Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Mon, 7 Feb 2022 14:36:18 +0200 Subject: [PATCH 042/609] backend-drm/state-propose: Missing some newlines It would look much better if the debug is printed separately. Signed-off-by: Marius Vlad --- libweston/backend-drm/state-propose.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index b998268c..9086cbc4 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -459,7 +459,7 @@ drm_output_find_plane_for_view(struct drm_output_state *state, if (wl_shm_buffer_get_format(shmbuf) != WL_SHM_FORMAT_ARGB8888) { drm_debug(b, "\t\t\t\t[view] not placing view %p on " "plane; SHM buffers must be ARGB8888 for " - "cursor view", ev); + "cursor view\n", ev); pnode->try_view_on_plane_failure_reasons |= FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE; return NULL; @@ -468,7 +468,7 @@ drm_output_find_plane_for_view(struct drm_output_state *state, if (buffer->width > b->cursor_width || buffer->height > b->cursor_height) { drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane " - "(buffer (%dx%d) too large for cursor plane)", + "(buffer (%dx%d) too large for cursor plane)\n", ev, buffer->width, buffer->height); pnode->try_view_on_plane_failure_reasons |= FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE; @@ -542,7 +542,7 @@ drm_output_find_plane_for_view(struct drm_output_state *state, if (drm_output_check_plane_has_view_assigned(plane, state)) { drm_debug(b, "\t\t\t\t[plane] not trying plane %d: " - "another view already assigned", + "another view already assigned\n", plane->plane_id); continue; } From b03195a8f7f7fd274844cd4c97341263b7869650 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 10 Jan 2022 01:59:04 +0100 Subject: [PATCH 043/609] screen-share: Name the seat "screen-share" Name the seat created by the screen share plugin "screen-share" instead of "default", to make it easier to recognize. No functional change intended. Signed-off-by: Marek Vasut --- compositor/screen-share.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compositor/screen-share.c b/compositor/screen-share.c index 8906c6ad..10173b92 100644 --- a/compositor/screen-share.c +++ b/compositor/screen-share.c @@ -368,7 +368,7 @@ ss_seat_create(struct shared_output *so, uint32_t id) if (seat == NULL) return NULL; - weston_seat_init(&seat->base, so->output->compositor, "default"); + weston_seat_init(&seat->base, so->output->compositor, "screen-share"); seat->output = so; seat->id = id; seat->parent.seat = wl_registry_bind(so->parent.registry, id, From ce8ead4bf7384cab3bbd5581aa6014a8078147af Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Mon, 7 Feb 2022 09:26:03 +0000 Subject: [PATCH 044/609] debug: Show client PID in debug protocol stream Make it slightly easier to disambiguate clients when we log the protocol stream from the server side. Signed-off-by: Daniel Stone --- compositor/main.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compositor/main.c b/compositor/main.c index 322f2ff5..87523517 100644 --- a/compositor/main.c +++ b/compositor/main.c @@ -270,6 +270,8 @@ protocol_log_fn(void *user_data, size_t logsize; char timestr[128]; struct wl_resource *res = message->resource; + struct wl_client *client = wl_resource_get_client(res); + pid_t pid = 0; const char *signature = message->message->signature; int i; char type; @@ -281,10 +283,12 @@ protocol_log_fn(void *user_data, if (!fp) return; + wl_client_get_credentials(client, &pid, NULL, NULL); + weston_log_scope_timestamp(protocol_scope, timestr, sizeof timestr); fprintf(fp, "%s ", timestr); - fprintf(fp, "client %p %s ", wl_resource_get_client(res), + fprintf(fp, "client %p (PID %d) %s ", client, pid, direction == WL_PROTOCOL_LOGGER_REQUEST ? "rq" : "ev"); fprintf(fp, "%s@%u.%s(", wl_resource_get_class(res), From 08dbd29e3333d4041cbdb1a3c36590ebc554547b Mon Sep 17 00:00:00 2001 From: Leandro Ribeiro Date: Mon, 7 Feb 2022 21:26:59 -0300 Subject: [PATCH 045/609] gitlab-ci: compile Linux image with support to VGEM Add VGEM to the Linux image that runs in the CI. There are tests that we plan to add in the future that need this. This brings a complication, as we already have VKMS in the image. The order in which DRM devices are loaded is not always the same, so the node they receive is non-deterministic. Until now we were sure that VKMS (the virtual device we use to run the DRM-backend tests in the CI) would be in "/dev/dri/card0", but now we can't be sure. To deal with this problem we find the node of each device using a one-liner shell script. This commit also updates the documentation section that describes specificities of DRM-backend tests in our test suite. Signed-off-by: Leandro Ribeiro --- .gitlab-ci.yml | 2 +- .gitlab-ci/build-deps.sh | 3 ++- .gitlab-ci/virtme-scripts/run-weston-tests.sh | 9 ++++++++- doc/sphinx/toc/test-suite.rst | 17 +++++++++++++---- 4 files changed, 24 insertions(+), 7 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4210e007..e68b5741 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -43,7 +43,7 @@ variables: FDO_UPSTREAM_REPO: wayland/weston FDO_REPO_SUFFIX: "$BUILD_OS/$BUILD_ARCH" - FDO_DISTRIBUTION_TAG: '2022-01-22.0-for-axbgr16161616' + FDO_DISTRIBUTION_TAG: '2022-02-07.00-add-vgem-to-ci' include: diff --git a/.gitlab-ci/build-deps.sh b/.gitlab-ci/build-deps.sh index f707aa59..67e06701 100755 --- a/.gitlab-ci/build-deps.sh +++ b/.gitlab-ci/build-deps.sh @@ -75,7 +75,8 @@ if [[ -n "$KERNEL_DEFCONFIG" ]]; then --enable CONFIG_DRM \ --enable CONFIG_DRM_KMS_HELPER \ --enable CONFIG_DRM_KMS_FB_HELPER \ - --enable CONFIG_DRM_VKMS + --enable CONFIG_DRM_VKMS \ + --enable CONFIG_DRM_VGEM make ARCH=${LINUX_ARCH} oldconfig make ARCH=${LINUX_ARCH} diff --git a/.gitlab-ci/virtme-scripts/run-weston-tests.sh b/.gitlab-ci/virtme-scripts/run-weston-tests.sh index 66da7b33..a45f9668 100755 --- a/.gitlab-ci/virtme-scripts/run-weston-tests.sh +++ b/.gitlab-ci/virtme-scripts/run-weston-tests.sh @@ -7,8 +7,15 @@ chmod -R 0700 /tmp # set environment variables to run Weston tests export XDG_RUNTIME_DIR=/tmp/tests -export WESTON_TEST_SUITE_DRM_DEVICE=card0 export LIBSEAT_BACKEND=seatd +# In our test suite, we use VKMS to run DRM-backend tests. The order in which +# devices are loaded is not predictable, so the DRM node that VKMS takes can +# change across each boot. That's why we have this one-liner shell script to get +# the appropriate node for VKMS. +export WESTON_TEST_SUITE_DRM_DEVICE=$(basename /sys/devices/platform/vkms/drm/card*) +# To run tests in the CI that exercise the zwp_linux_dmabuf_v1 implementation in +# Weston, we use VGEM to allocate buffers. +export WESTON_TEST_SUITE_ALLOC_DEVICE=$(basename /sys/devices/platform/vgem/drm/card*) # ninja test depends on meson, and meson itself looks for its modules on folder # $HOME/.local/lib/pythonX.Y/site-packages (the Python version may differ). diff --git a/doc/sphinx/toc/test-suite.rst b/doc/sphinx/toc/test-suite.rst index f7803292..65083988 100644 --- a/doc/sphinx/toc/test-suite.rst +++ b/doc/sphinx/toc/test-suite.rst @@ -218,10 +218,19 @@ DRM-backend tests DRM-backend tests require a DRM device, so they are a special case. To select a device the test suite will simply look at the environment variable -``WESTON_TEST_SUITE_DRM_DEVICE``. So the first thing the user has to do in order -to run DRM-backend tests is to set this environment variable with the card that -should run the tests. For instance, in order to run DRM-backend tests with -``card0`` we need to run ``export WESTON_TEST_SUITE_DRM_DEVICE=card0``. +``WESTON_TEST_SUITE_DRM_DEVICE``. In Weston's CI, we set this variable to the +DRM node that VKMS takes (``cardX`` - X can change across each bot, as the order +in which devices are loaded is not predictable). + +**IMPORTANT**: our DRM-backend tests are written specifically to run on top of +VKMS (KMS driver created to be used by headless machines in test suites, so it +aims to be more configurable and predictable than real hardware). We don't +guarantee that these tests will work on real hardware. + +But if users want to run DRM-backend tests using real hardware anyway, the first +thing they need to do is to set this environment variable with the DRM node of +the card that should run the tests. For instance, in order to run DRM-backend +tests with ``card0`` we need to run ``export WESTON_TEST_SUITE_DRM_DEVICE=card0``. Note that the card should not be in use by a desktop environment (or any other program that requires master status), as there can only be one user at a time From 2e2ad02d5c0c49712345a5ea6c4c5c8f373e47e2 Mon Sep 17 00:00:00 2001 From: Vitaly Prosyak Date: Thu, 9 Sep 2021 13:28:47 -0400 Subject: [PATCH 046/609] libweston: add definition of color mapping function Introduce 3D LUT definition as part of Weston color transform struct. A 3D LUT is a LUT containing entries for each possible RGB triplets. Co-authored-by: Pekka Paalanen Signed-off-by: Vitaly Prosyak --- libweston/color.h | 76 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/libweston/color.h b/libweston/color.h index d2137af8..4667fcad 100644 --- a/libweston/color.h +++ b/libweston/color.h @@ -1,5 +1,6 @@ /* * Copyright 2021 Collabora, Ltd. + * Copyright 2021 Advanced Micro Devices, Inc. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -98,6 +99,79 @@ struct weston_color_curve { } u; }; +/** Type or formula for a color mapping */ +enum weston_color_mapping_type { + /** Identity function, no-op */ + WESTON_COLOR_MAPPING_TYPE_IDENTITY = 0, + + /** 3D-dimensional look-up table */ + WESTON_COLOR_MAPPING_TYPE_3D_LUT, +}; + +/** + * A three-dimensional look-up table + * + * A 3D LUT is a three-dimensional array where each element is an RGB triplet. + * A 3D LUT is usually an approximation of some arbitrary color mapping + * function that cannot be represented in any simpler form. The array contains + * samples from the approximated function, and values between samples are + * estimated by interpolation. The array is accessed with three indices, one + * for each input dimension (color channel). + * + * Color channel values in the range [0.0, 1.0] are mapped linearly to + * 3D LUT indices such that 0.0 maps exactly to the first element and 1.0 maps + * exactly to the last element in each dimension. + * + * This object represents a 3D LUT and offers an interface for realizing it + * as a data array with a custom size. + */ +struct weston_color_mapping_3dlut { + /** + * Create a 3D LUT data array + * + * \param xform This color transformation object. + * \param values Memory to hold the resulting data array. + * \param len The number of elements in each dimension. + * + * The array \c values must be at least 3 * len * len * len elements + * in size. + * + * Given the red index ri, green index gi and blue index bi, the + * corresponding array element index + * + * i = 3 * (len * len * bi + len * gi + ri) + c + * + * where + * + * c = 0 for red output value, + * c = 1 for green output value, and + * c = 2 for blue output value + */ + void + (*fill_in)(struct weston_color_transform *xform, + float *values, unsigned len); + + /** Optimal 3D LUT size along each dimension */ + unsigned optimal_len; +}; + +/** + * Color mapping function + * + * This object can represent a 3D LUT to do a color space conversion + * + */ +struct weston_color_mapping { + /** Which member of 'u' defines the color mapping type */ + enum weston_color_mapping_type type; + + /** Parameters for the color mapping function */ + union { + /* identity: no parameters */ + struct weston_color_mapping_3dlut lut3d; + } u; +}; + /** * Describes a color transformation formula * @@ -124,7 +198,7 @@ struct weston_color_transform { struct weston_color_curve pre_curve; /** Step 3: color mapping */ - /* TBD: e.g. a 3D LUT or a matrix */ + struct weston_color_mapping mapping; /** Step 4: color curve after color mapping */ /* struct weston_color_curve post_curve; */ From cda130e4b0849fce0ba285c67167b94971f21d1e Mon Sep 17 00:00:00 2001 From: Vitaly Prosyak Date: Thu, 9 Sep 2021 14:22:43 -0400 Subject: [PATCH 047/609] gl-renderer: add declaration of color mapping function Introduce shader color mapping identity and 3D LUT. Shader requirements struct uses union for color mapping to prepare the place for 3x3 matrix. Signed-off-by: Vitaly Prosyak --- libweston/renderer-gl/gl-renderer-internal.h | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer-internal.h b/libweston/renderer-gl/gl-renderer-internal.h index a225b61c..b8230d8f 100644 --- a/libweston/renderer-gl/gl-renderer-internal.h +++ b/libweston/renderer-gl/gl-renderer-internal.h @@ -2,6 +2,7 @@ * Copyright © 2019 Collabora, Ltd. * Copyright © 2019 Harish Krupo * Copyright © 2019 Intel Corporation + * Copyright 2021 Advanced Micro Devices, Inc. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -56,6 +57,12 @@ enum gl_shader_color_curve { SHADER_COLOR_CURVE_LUT_3x1D, }; +/* Keep the following in sync with fragment.glsl. */ +enum gl_shader_color_mapping { + SHADER_COLOR_MAPPING_IDENTITY = 0, + SHADER_COLOR_MAPPING_3DLUT, +}; + /** GL shader requirements key * * This structure is used as a binary blob key for building and searching @@ -70,13 +77,14 @@ struct gl_shader_requirements unsigned variant:4; /* enum gl_shader_texture_variant */ bool input_is_premult:1; bool green_tint:1; - unsigned color_pre_curve:1; /* enum gl_shader_color_curve */ + unsigned color_pre_curve:1; /* enum gl_shader_color_curve */ + unsigned color_mapping:1; /* enum gl_shader_color_mapping */ /* * The total size of all bitfields plus pad_bits_ must fill up exactly * how many bytes the compiler allocates for them together. */ - unsigned pad_bits_:25; + unsigned pad_bits_:24; }; static_assert(sizeof(struct gl_shader_requirements) == 4 /* total bitfield size in bytes */, @@ -96,6 +104,12 @@ struct gl_shader_config { GLuint input_tex[GL_SHADER_INPUT_TEX_MAX]; GLuint color_pre_curve_lut_tex; GLfloat color_pre_curve_lut_scale_offset[2]; + union { + struct { + GLuint tex; + GLfloat scale_offset[2]; + } lut3d; + } color_mapping; }; struct gl_renderer { From 93c6180c71139abd6c179eba8f2ac799ecbe87ed Mon Sep 17 00:00:00 2001 From: Vitaly Prosyak Date: Wed, 22 Sep 2021 13:59:40 -0400 Subject: [PATCH 048/609] gl-renderer: shaders implementation of color mapping function The following GL extensions provide support for shaders CM: -GL_OES_texture_float_linear makes GL_RGB32F linear filterable. -GL ES 3.0 provides Texture3D support in GL API. -GL_OES_texture_3D provides sampler3D support in ESSL 1.00. If abovesaid is supported then renderer sets flag WESTON_CAP_COLOR_OPS which means that all fields in struct weston_color_transform are supported, for example, 1DLUT and 3DLUT. Use GL_OES_texture_3D to implement 3DLUT function which uses trilinear interpolation for pixel processing or bypass as is. Quote from https://nick-shaw.github.io/cinematiccolor/luts-and-transforms.html "3D LUTs have long been embraced by color scientists and are one of the tools commonly used for gamut mapping. In fact, 3D LUTs are used within ICC profiles to model the complex device behaviors necessary for accurate color image reproduction". Quote from https://developer.nvidia.com/gpugems/gpugems2/part-iii-high-quality-rendering/ chapter-24-using-lookup-tables-accelerate-color is about interpolation: "By generating intermediate results based on a weighted average of the eight corners of the bounding cube, this algorithm is typically sufficient for color processing, and it is implemented in graphics hardware". Signed-off-by: Vitaly Prosyak --- libweston/renderer-gl/fragment.glsl | 44 +++++++++ libweston/renderer-gl/gl-renderer.c | 3 +- .../gl-shader-config-color-transformation.c | 98 ++++++++++++++++++- libweston/renderer-gl/gl-shaders.c | 60 +++++++++++- 4 files changed, 198 insertions(+), 7 deletions(-) diff --git a/libweston/renderer-gl/fragment.glsl b/libweston/renderer-gl/fragment.glsl index 63a20cd6..c49a6cd4 100644 --- a/libweston/renderer-gl/fragment.glsl +++ b/libweston/renderer-gl/fragment.glsl @@ -2,6 +2,7 @@ * Copyright 2012 Intel Corporation * Copyright 2015,2019,2021 Collabora, Ltd. * Copyright 2016 NVIDIA Corporation + * Copyright 2021 Advanced Micro Devices, Inc. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -46,10 +47,18 @@ #define SHADER_COLOR_CURVE_IDENTITY 0 #define SHADER_COLOR_CURVE_LUT_3x1D 1 +/* enum gl_shader_color_mapping */ +#define SHADER_COLOR_MAPPING_IDENTITY 0 +#define SHADER_COLOR_MAPPING_3DLUT 1 + #if DEF_VARIANT == SHADER_VARIANT_EXTERNAL #extension GL_OES_EGL_image_external : require #endif +#if DEF_COLOR_MAPPING == SHADER_COLOR_MAPPING_3DLUT +#extension GL_OES_texture_3D : require +#endif + #ifdef GL_FRAGMENT_PRECISION_HIGH #define HIGHPRECISION highp #else @@ -66,6 +75,7 @@ compile_const int c_variant = DEF_VARIANT; compile_const bool c_input_is_premult = DEF_INPUT_IS_PREMULT; compile_const bool c_green_tint = DEF_GREEN_TINT; compile_const int c_color_pre_curve = DEF_COLOR_PRE_CURVE; +compile_const int c_color_mapping = DEF_COLOR_MAPPING; vec4 yuva2rgba(vec4 yuva) @@ -110,6 +120,11 @@ uniform vec4 unicolor; uniform HIGHPRECISION sampler2D color_pre_curve_lut_2d; uniform HIGHPRECISION vec2 color_pre_curve_lut_scale_offset; +#if DEF_COLOR_MAPPING == SHADER_COLOR_MAPPING_3DLUT +uniform HIGHPRECISION sampler3D color_mapping_lut_3d; +uniform HIGHPRECISION vec2 color_mapping_lut_scale_offset; +#endif + vec4 sample_input_texture() { @@ -168,6 +183,12 @@ lut_texcoord(float x, vec2 scale_offset) return x * scale_offset.s + scale_offset.t; } +vec3 +lut_texcoord(vec3 pos, vec2 scale_offset) +{ + return pos * scale_offset.s + scale_offset.t; +} + /* * Sample a 1D LUT which is a single row of a 2D texture. The 2D texture has * four rows so that the centers of texels have precise y-coordinates. @@ -199,6 +220,28 @@ color_pre_curve(vec3 color) } } +vec3 +sample_color_mapping_lut_3d(vec3 color) +{ + vec3 pos, ret = vec3(0.0, 0.0, 0.0); +#if DEF_COLOR_MAPPING == SHADER_COLOR_MAPPING_3DLUT + pos = lut_texcoord(color, color_mapping_lut_scale_offset); + ret = texture3D(color_mapping_lut_3d, pos).rgb; +#endif + return ret; +} + +vec3 +color_mapping(vec3 color) +{ + if (c_color_mapping == SHADER_COLOR_MAPPING_IDENTITY) + return color; + else if (c_color_mapping == SHADER_COLOR_MAPPING_3DLUT) + return sample_color_mapping_lut_3d(color); + else /* Never reached, bad c_color_mapping. */ + return vec3(1.0, 0.3, 1.0); +} + vec4 color_pipeline(vec4 color) { @@ -206,6 +249,7 @@ color_pipeline(vec4 color) color.a *= alpha; color.rgb = color_pre_curve(color.rgb); + color.rgb = color_mapping(color.rgb); return color; } diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 501a6802..e7cd06a9 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -3977,7 +3977,8 @@ gl_renderer_setup(struct weston_compositor *ec, EGLSurface egl_surface) if (gr->gl_version >= gr_gl_version(3, 0) && weston_check_egl_extension(extensions, "GL_OES_texture_float_linear") && - weston_check_egl_extension(extensions, "GL_EXT_color_buffer_half_float")) { + weston_check_egl_extension(extensions, "GL_EXT_color_buffer_half_float") && + weston_check_egl_extension(extensions, "GL_OES_texture_3D")) { gr->gl_supports_color_transforms = true; } diff --git a/libweston/renderer-gl/gl-shader-config-color-transformation.c b/libweston/renderer-gl/gl-shader-config-color-transformation.c index 21a45653..7f858dd1 100644 --- a/libweston/renderer-gl/gl-shader-config-color-transformation.c +++ b/libweston/renderer-gl/gl-shader-config-color-transformation.c @@ -1,5 +1,6 @@ /* * Copyright 2021 Collabora, Ltd. + * Copyright 2021 Advanced Micro Devices, Inc. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -44,11 +45,22 @@ struct gl_renderer_color_curve { float offset; }; +struct gl_renderer_color_mapping { + enum gl_shader_color_mapping type; + union { + struct { + GLuint tex3d; + float scale; + float offset; + } lut3d; + }; +} ; + struct gl_renderer_color_transform { struct weston_color_transform *owner; struct wl_listener destroy_listener; - struct gl_renderer_color_curve pre_curve; + struct gl_renderer_color_mapping mapping; }; static void @@ -58,10 +70,19 @@ gl_renderer_color_curve_fini(struct gl_renderer_color_curve *gl_curve) glDeleteTextures(1, &gl_curve->tex); } +static void +gl_renderer_color_mapping_fini(struct gl_renderer_color_mapping *gl_mapping) +{ + if (gl_mapping->type == SHADER_COLOR_MAPPING_3DLUT && + gl_mapping->lut3d.tex3d) + glDeleteTextures(1, &gl_mapping->lut3d.tex3d); +} + static void gl_renderer_color_transform_destroy(struct gl_renderer_color_transform *gl_xform) { gl_renderer_color_curve_fini(&gl_xform->pre_curve); + gl_renderer_color_mapping_fini(&gl_xform->mapping); wl_list_remove(&gl_xform->destroy_listener.link); free(gl_xform); } @@ -152,6 +173,47 @@ gl_color_curve_lut_3x1d(struct gl_renderer_color_curve *gl_curve, return true; } +static bool +gl_3d_lut(struct gl_renderer_color_transform *gl_xform, + struct weston_color_transform *xform) +{ + + GLuint tex3d; + float *lut; + const unsigned dim_size = xform->mapping.u.lut3d.optimal_len; + + lut = calloc(3 * dim_size * dim_size * dim_size, sizeof *lut); + if (!lut) + return false; + + xform->mapping.u.lut3d.fill_in(xform, lut, dim_size); + + glActiveTexture(GL_TEXTURE0); + glGenTextures(1, &tex3d); + glBindTexture(GL_TEXTURE_3D, tex3d); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, 0); + glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, 0); + glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0); + glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB32F, dim_size, dim_size, dim_size, 0, + GL_RGB, GL_FLOAT, lut); + + glBindTexture(GL_TEXTURE_3D, 0); + gl_xform->mapping.type = SHADER_COLOR_MAPPING_3DLUT; + gl_xform->mapping.lut3d.tex3d = tex3d; + gl_xform->mapping.lut3d.scale = (float)(dim_size - 1) / dim_size; + gl_xform->mapping.lut3d.offset = 0.5f / dim_size; + + free(lut); + + return true; +} + + static const struct gl_renderer_color_transform * gl_renderer_color_transform_from(struct weston_color_transform *xform) { @@ -160,6 +222,7 @@ gl_renderer_color_transform_from(struct weston_color_transform *xform) .pre_curve.tex = 0, .pre_curve.scale = 0.0f, .pre_curve.offset = 0.0f, + .mapping.type = SHADER_COLOR_MAPPING_IDENTITY, }; struct gl_renderer_color_transform *gl_xform; bool ok = false; @@ -190,6 +253,19 @@ gl_renderer_color_transform_from(struct weston_color_transform *xform) break; } + if (!ok) { + gl_renderer_color_transform_destroy(gl_xform); + return NULL; + } + switch (xform->mapping.type) { + case WESTON_COLOR_MAPPING_TYPE_IDENTITY: + gl_xform->mapping = no_op_gl_xform.mapping; + ok = true; + break; + case WESTON_COLOR_MAPPING_TYPE_3D_LUT: + ok = gl_3d_lut(gl_xform, xform); + break; + } if (!ok) { gl_renderer_color_transform_destroy(gl_xform); return NULL; @@ -203,6 +279,7 @@ gl_shader_config_set_color_transform(struct gl_shader_config *sconf, struct weston_color_transform *xform) { const struct gl_renderer_color_transform *gl_xform; + bool ret = false; gl_xform = gl_renderer_color_transform_from(xform); if (!gl_xform) @@ -213,5 +290,22 @@ gl_shader_config_set_color_transform(struct gl_shader_config *sconf, sconf->color_pre_curve_lut_scale_offset[0] = gl_xform->pre_curve.scale; sconf->color_pre_curve_lut_scale_offset[1] = gl_xform->pre_curve.offset; - return true; + sconf->req.color_mapping = gl_xform->mapping.type; + switch (gl_xform->mapping.type) { + case SHADER_COLOR_MAPPING_3DLUT: + sconf->color_mapping.lut3d.tex = gl_xform->mapping.lut3d.tex3d; + sconf->color_mapping.lut3d.scale_offset[0] = + gl_xform->mapping.lut3d.scale; + sconf->color_mapping.lut3d.scale_offset[1] = + gl_xform->mapping.lut3d.offset; + assert(sconf->color_mapping.lut3d.scale_offset[0] > 0.0); + assert(sconf->color_mapping.lut3d.scale_offset[1] > 0.0); + ret = true; + break; + case SHADER_COLOR_MAPPING_IDENTITY: + ret = true; + break; + } + + return ret; } diff --git a/libweston/renderer-gl/gl-shaders.c b/libweston/renderer-gl/gl-shaders.c index 97f288c0..f46386f5 100644 --- a/libweston/renderer-gl/gl-shaders.c +++ b/libweston/renderer-gl/gl-shaders.c @@ -4,6 +4,7 @@ * Copyright 2016 NVIDIA Corporation * Copyright 2019 Harish Krupo * Copyright 2019 Intel Corporation + * Copyright 2021 Advanced Micro Devices, Inc. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -60,6 +61,12 @@ struct gl_shader { GLint color_uniform; GLint color_pre_curve_lut_2d_uniform; GLint color_pre_curve_lut_scale_offset_uniform; + union { + struct { + GLint tex_uniform; + GLint scale_offset_uniform; + } lut3d; + } color_mapping; struct wl_list link; /* gl_renderer::shader_list */ struct timespec last_used; }; @@ -97,6 +104,19 @@ gl_shader_color_curve_to_string(enum gl_shader_color_curve kind) return "!?!?"; /* never reached */ } +static const char * +gl_shader_color_mapping_to_string(enum gl_shader_color_mapping kind) +{ + switch (kind) { +#define CASERET(x) case x: return #x; + CASERET(SHADER_COLOR_MAPPING_IDENTITY) + CASERET(SHADER_COLOR_MAPPING_3DLUT) +#undef CASERET + } + + return "!?!?"; /* never reached */ +} + static void dump_program_with_line_numbers(int count, const char **sources) { @@ -162,9 +182,10 @@ create_shader_description_string(const struct gl_shader_requirements *req) int size; char *str; - size = asprintf(&str, "%s %s %cinput_is_premult %cgreen", + size = asprintf(&str, "%s %s %s %cinput_is_premult %cgreen", gl_shader_texture_variant_to_string(req->variant), gl_shader_color_curve_to_string(req->color_pre_curve), + gl_shader_color_mapping_to_string(req->color_mapping), req->input_is_premult ? '+' : '-', req->green_tint ? '+' : '-'); if (size < 0) @@ -182,10 +203,12 @@ create_shader_config_string(const struct gl_shader_requirements *req) "#define DEF_GREEN_TINT %s\n" "#define DEF_INPUT_IS_PREMULT %s\n" "#define DEF_COLOR_PRE_CURVE %s\n" + "#define DEF_COLOR_MAPPING %s\n" "#define DEF_VARIANT %s\n", req->green_tint ? "true" : "false", req->input_is_premult ? "true" : "false", gl_shader_color_curve_to_string(req->color_pre_curve), + gl_shader_color_mapping_to_string(req->color_mapping), gl_shader_texture_variant_to_string(req->variant)); if (size < 0) return NULL; @@ -268,6 +291,16 @@ gl_shader_create(struct gl_renderer *gr, shader->color_pre_curve_lut_scale_offset_uniform = glGetUniformLocation(shader->program, "color_pre_curve_lut_scale_offset"); + switch(requirements->color_mapping) { + case SHADER_COLOR_MAPPING_3DLUT: + shader->color_mapping.lut3d.tex_uniform = + glGetUniformLocation(shader->program, "color_mapping_lut_3d"); + shader->color_mapping.lut3d.scale_offset_uniform = + glGetUniformLocation(shader->program,"color_mapping_lut_scale_offset"); + break; + case SHADER_COLOR_MAPPING_IDENTITY: + break; + } free(conf); wl_list_insert(&gr->shader_list, &shader->link); @@ -376,6 +409,7 @@ gl_renderer_create_fallback_shader(struct gl_renderer *gr) .variant = SHADER_VARIANT_SOLID, .input_is_premult = true, .color_pre_curve = SHADER_COLOR_CURVE_IDENTITY, + .color_mapping = SHADER_COLOR_MAPPING_IDENTITY, }; struct gl_shader *shader; @@ -497,9 +531,8 @@ gl_shader_load_config(struct gl_shader *shader, glTexParameteri(in_tgt, GL_TEXTURE_MAG_FILTER, in_filter); } - /* Fixed texture unit for color_pre_curve LUT */ + /* Fixed texture unit for color_pre_curve LUT if it is available */ i = GL_SHADER_INPUT_TEX_MAX; - glActiveTexture(GL_TEXTURE0 + i); switch (sconf->req.color_pre_curve) { case SHADER_COLOR_CURVE_IDENTITY: assert(sconf->color_pre_curve_lut_tex == 0); @@ -508,13 +541,32 @@ gl_shader_load_config(struct gl_shader *shader, assert(sconf->color_pre_curve_lut_tex != 0); assert(shader->color_pre_curve_lut_2d_uniform != -1); assert(shader->color_pre_curve_lut_scale_offset_uniform != -1); - + glActiveTexture(GL_TEXTURE0 + i); glBindTexture(GL_TEXTURE_2D, sconf->color_pre_curve_lut_tex); glUniform1i(shader->color_pre_curve_lut_2d_uniform, i); + i++; glUniform2fv(shader->color_pre_curve_lut_scale_offset_uniform, 1, sconf->color_pre_curve_lut_scale_offset); break; } + + switch (sconf->req.color_mapping) { + case SHADER_COLOR_MAPPING_IDENTITY: + break; + case SHADER_COLOR_MAPPING_3DLUT: + assert(shader->color_mapping.lut3d.tex_uniform != -1); + assert(sconf->color_mapping.lut3d.tex != 0); + assert(shader->color_mapping.lut3d.scale_offset_uniform != -1); + glActiveTexture(GL_TEXTURE0 + i); + glBindTexture(GL_TEXTURE_3D, sconf->color_mapping.lut3d.tex); + glUniform1i(shader->color_mapping.lut3d.tex_uniform, i); + glUniform2fv(shader->color_mapping.lut3d.scale_offset_uniform, + 1, sconf->color_mapping.lut3d.scale_offset); + break; + default: + assert(false); + break; + } } bool From 494ff5b23b52e449a8eaa7dc8c6d01fbff82d44a Mon Sep 17 00:00:00 2001 From: Vitaly Prosyak Date: Wed, 22 Sep 2021 18:56:02 -0400 Subject: [PATCH 049/609] color-lcms: introduce cmlcms_category, EOTF and INV EOTF 1. The cmlcms_category is used to identify the purpose of transform: - CMLCMS_CATEGORY_INPUT_TO_BLEND - CMLCMS_CATEGORY_BLEND_TO_OUTPUT - CMLCMS_CATEGORY_INPUT_TO_OUTPUT 2. Added following fields to cmlcms_color_profile: - output_eotf - If the profile does support being an output profile and it is used as an output then this field represents a light linearizing transfer function and it can not be null. The field is null only if the profile is not usable as an output profile. The field is set when cmlcms_color_profile is created. - vcgt - VCGT tag cached from output profile, it could be null if not exist - output_inv_eotf_vcgt - if the profile does support being an output profile and it is used as an output then this field represents a concatenation of inverse EOTF + VCGT, if the tag exists and it can not be null. 3. Added field cmsHTRANSFORM to cmlcms_color_transform. It is used to store LCMS optimized pipeline. Signed-off-by: Vitaly Prosyak --- libweston/color-lcms/color-lcms.h | 65 +++++++++++++++++++++++++++- libweston/color-lcms/color-profile.c | 3 ++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/libweston/color-lcms/color-lcms.h b/libweston/color-lcms/color-lcms.h index dd5c38fc..9d859b08 100644 --- a/libweston/color-lcms/color-lcms.h +++ b/libweston/color-lcms/color-lcms.h @@ -59,6 +59,58 @@ struct cmlcms_color_profile { cmsHPROFILE profile; struct cmlcms_md5_sum md5sum; + + /** + * If the profile does support being an output profile and it is used as an + * output then this field represents a light linearizing transfer function + * and it can not be null. The field is null only if the profile is not + * usable as an output profile. The field is set when cmlcms_color_profile + * is created. + */ + cmsToneCurve *output_eotf[3]; + + /** + * If the profile does support being an output profile and it is used as an + * output then this field represents a concatenation of inverse EOTF + VCGT, + * if the tag exists and it can not be null. + * VCGT is part of monitor calibration which means: even though we must + * apply VCGT in the compositor, we pretend that it happens inside the + * monitor. This is how the classic color management and ICC profiles work. + * The ICC profile (ignoring the VCGT tag) characterizes the output which + * is VCGT + monitor behavior. The field is null only if the profile is not + * usable as an output profile. The field is set when cmlcms_color_profile + * is created. + */ + cmsToneCurve *output_inv_eotf_vcgt[3]; + + /** + * VCGT tag cached from output profile, it could be null if not exist + */ + cmsToneCurve *vcgt[3]; +}; + +/** + * Type of LCMS transforms + */ +enum cmlcms_category { + /** + * Uses combination of input profile with output profile, but + * without INV EOTF or with additional EOTF in the transform pipeline + * input→blend = input profile + output profile + output EOTF + */ + CMLCMS_CATEGORY_INPUT_TO_BLEND = 0, + + /** + * Uses INV EOTF only concatenated with VCGT tag if present + * blend→output = output inverse EOTF + VCGT + */ + CMLCMS_CATEGORY_BLEND_TO_OUTPUT, + + /** + * Transform uses input profile and output profile as is + * input→output = input profile + output profile + VCGT + */ + CMLCMS_CATEGORY_INPUT_TO_OUTPUT, }; static inline struct cmlcms_color_profile * @@ -100,8 +152,19 @@ struct cmlcms_color_transform { struct cmlcms_color_transform_search_param search_key; - /* for EOTF types */ + /* for EOTF types It would be deprecated */ cmsToneCurve *curve; + + /** + * 3D LUT color mapping part of the transformation, if needed. + * For category CMLCMS_CATEGORY_INPUT_TO_OUTPUT it includes pre-curve and + * post-curve. + * For category CMLCMS_CATEGORY_INPUT_TO_BLEND it includes pre-curve. + * For category CMLCMS_CATEGORY_BLEND_TO_OUTPUT and when identity it is + * not used + */ + cmsHTRANSFORM cmap_3dlut; + }; static inline struct cmlcms_color_transform * diff --git a/libweston/color-lcms/color-profile.c b/libweston/color-lcms/color-profile.c index 8884a071..46ef46bf 100644 --- a/libweston/color-lcms/color-profile.c +++ b/libweston/color-lcms/color-profile.c @@ -106,6 +106,9 @@ static void cmlcms_color_profile_destroy(struct cmlcms_color_profile *cprof) { wl_list_remove(&cprof->link); + cmsFreeToneCurveTriple(cprof->vcgt); + cmsFreeToneCurveTriple(cprof->output_eotf); + cmsFreeToneCurveTriple(cprof->output_inv_eotf_vcgt); cmsCloseProfile(cprof->profile); free(cprof->base.description); free(cprof); From a92fa34d1d60cf30bdcc1e97da5c3d43678f0316 Mon Sep 17 00:00:00 2001 From: Vitaly Prosyak Date: Wed, 22 Sep 2021 19:17:05 -0400 Subject: [PATCH 050/609] color-lcms: add wrapper API for refcounting cmlcms_color_profile It is used for convenience when profile is cached. Signed-off-by: Vitaly Prosyak --- libweston/color-lcms/color-lcms.h | 6 ++++++ libweston/color-lcms/color-profile.c | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/libweston/color-lcms/color-lcms.h b/libweston/color-lcms/color-lcms.h index 9d859b08..05fa940a 100644 --- a/libweston/color-lcms/color-lcms.h +++ b/libweston/color-lcms/color-lcms.h @@ -180,4 +180,10 @@ cmlcms_color_transform_get(struct weston_color_manager_lcms *cm, void cmlcms_color_transform_destroy(struct cmlcms_color_transform *xform); +struct cmlcms_color_profile * +ref_cprof(struct cmlcms_color_profile *cprof); + +void +unref_cprof(struct cmlcms_color_profile *cprof); + #endif /* WESTON_COLOR_LCMS_H */ diff --git a/libweston/color-lcms/color-profile.c b/libweston/color-lcms/color-profile.c index 46ef46bf..0d11439d 100644 --- a/libweston/color-lcms/color-profile.c +++ b/libweston/color-lcms/color-profile.c @@ -114,6 +114,25 @@ cmlcms_color_profile_destroy(struct cmlcms_color_profile *cprof) free(cprof); } +struct cmlcms_color_profile * +ref_cprof(struct cmlcms_color_profile *cprof) +{ + if (!cprof) + return NULL; + + weston_color_profile_ref(&cprof->base); + return cprof; +} + +void +unref_cprof(struct cmlcms_color_profile *cprof) +{ + if (!cprof) + return; + + weston_color_profile_unref(&cprof->base); +} + static char * make_icc_file_description(cmsHPROFILE profile, const struct cmlcms_md5_sum *md5sum, From 19f318692e58bb084a7664caf43decf90f7f28c8 Mon Sep 17 00:00:00 2001 From: Vitaly Prosyak Date: Wed, 29 Sep 2021 18:22:07 -0400 Subject: [PATCH 051/609] color-lcms: introduce sRGB stock profile The stock profile would be used when client or output do not provide any profile or unaware of color management. Signed-off-by: Vitaly Prosyak --- libweston/color-lcms/color-lcms.c | 6 +++++ libweston/color-lcms/color-lcms.h | 7 +++++ libweston/color-lcms/color-profile.c | 40 +++++++++++++++++++++++++++- 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/libweston/color-lcms/color-lcms.c b/libweston/color-lcms/color-lcms.c index 18340a39..6e9d9fa5 100644 --- a/libweston/color-lcms/color-lcms.c +++ b/libweston/color-lcms/color-lcms.c @@ -165,6 +165,10 @@ cmlcms_init(struct weston_color_manager *cm_base) cmsSetLogErrorHandlerTHR(cm->lcms_ctx, lcms_error_logger); + if (!cmlcms_create_stock_profile(cm)) { + weston_log("color-lcms: error: cmlcms_create_stock_profile failed\n"); + return false; + } weston_log("LittleCMS %d initialized.\n", cmsGetEncodedCMMversion()); return true; @@ -175,6 +179,8 @@ cmlcms_destroy(struct weston_color_manager *cm_base) { struct weston_color_manager_lcms *cm = get_cmlcms(cm_base); + if (cm->sRGB_profile) + cmlcms_color_profile_destroy(cm->sRGB_profile); assert(wl_list_empty(&cm->color_transform_list)); assert(wl_list_empty(&cm->color_profile_list)); diff --git a/libweston/color-lcms/color-lcms.h b/libweston/color-lcms/color-lcms.h index 05fa940a..eb34847c 100644 --- a/libweston/color-lcms/color-lcms.h +++ b/libweston/color-lcms/color-lcms.h @@ -39,6 +39,7 @@ struct weston_color_manager_lcms { struct wl_list color_transform_list; /* cmlcms_color_transform::link */ struct wl_list color_profile_list; /* cmlcms_color_profile::link */ + struct cmlcms_color_profile *sRGB_profile; /* stock profile */ }; static inline struct weston_color_manager_lcms * @@ -186,4 +187,10 @@ ref_cprof(struct cmlcms_color_profile *cprof); void unref_cprof(struct cmlcms_color_profile *cprof); +bool +cmlcms_create_stock_profile(struct weston_color_manager_lcms *cm); + +void +cmlcms_color_profile_destroy(struct cmlcms_color_profile *cprof); + #endif /* WESTON_COLOR_LCMS_H */ diff --git a/libweston/color-lcms/color-profile.c b/libweston/color-lcms/color-profile.c index 0d11439d..3f477100 100644 --- a/libweston/color-lcms/color-profile.c +++ b/libweston/color-lcms/color-profile.c @@ -102,7 +102,7 @@ cmlcms_color_profile_create(struct weston_color_manager_lcms *cm, return cprof; } -static void +void cmlcms_color_profile_destroy(struct cmlcms_color_profile *cprof) { wl_list_remove(&cprof->link); @@ -153,6 +153,44 @@ make_icc_file_description(cmsHPROFILE profile, return desc; } +/** + * + * Build stock profile which available for clients unaware of color management + */ +bool +cmlcms_create_stock_profile(struct weston_color_manager_lcms *cm) +{ + cmsHPROFILE profile; + struct cmlcms_md5_sum md5sum; + char *desc = NULL; + + profile = cmsCreate_sRGBProfileTHR(cm->lcms_ctx); + if (!profile) { + weston_log("color-lcms: error: cmsCreate_sRGBProfileTHR failed\n"); + return false; + } + if (!cmsMD5computeID(profile)) { + weston_log("Failed to compute MD5 for ICC profile\n"); + goto err_close; + } + + cmsGetHeaderProfileID(profile, md5sum.bytes); + desc = make_icc_file_description(profile, &md5sum, "sRGB stock"); + if (!desc) + goto err_close; + + cm->sRGB_profile = cmlcms_color_profile_create(cm, profile, desc, NULL); + if (!cm->sRGB_profile) + goto err_close; + + return true; + +err_close: + free(desc); + cmsCloseProfile(profile); + return false; +} + bool cmlcms_get_color_profile_from_icc(struct weston_color_manager *cm_base, const void *icc_data, From 19913366e84b794cd63fa7c8ec00045b7edbacd8 Mon Sep 17 00:00:00 2001 From: Vitaly Prosyak Date: Wed, 29 Sep 2021 18:11:10 -0400 Subject: [PATCH 052/609] color-lcms: add new fields for transform search parameter Add to search parameter cmlcms_category, input and output profiles, and render intent for output which would be used for both profiles. Add common function setup_search_param for every category. Signed-off-by: Vitaly Prosyak --- libweston/color-lcms/color-lcms.c | 74 ++++++++++++++++++++++++++----- libweston/color-lcms/color-lcms.h | 6 ++- 2 files changed, 67 insertions(+), 13 deletions(-) diff --git a/libweston/color-lcms/color-lcms.c b/libweston/color-lcms/color-lcms.c index 6e9d9fa5..e50e0bfd 100644 --- a/libweston/color-lcms/color-lcms.c +++ b/libweston/color-lcms/color-lcms.c @@ -33,6 +33,57 @@ #include "color-lcms.h" #include "shared/helpers.h" +static cmsUInt32Number +cmlcms_get_render_intent(enum cmlcms_category cat, + struct weston_surface *surface, + struct weston_output *output) +{ + /* + * TODO: Take into account client provided content profile, + * output profile, and the category of the wanted color + * transformation. + */ + cmsUInt32Number intent = INTENT_RELATIVE_COLORIMETRIC; + return intent; +} + +static void +setup_search_param(enum cmlcms_category cat, + struct weston_surface *surface, + struct weston_output *output, + struct cmlcms_color_profile *stock_sRGB_profile, + struct cmlcms_color_transform_search_param *search_param) +{ + struct cmlcms_color_profile *input_profile = NULL; + struct cmlcms_color_profile *output_profile = NULL; + + /* + * TODO: un-comment when declare color_profile in struct weston_surface + */ + /* if (surface && surface->color_profile) + input_profile = get_cprof(surface->color_profile); */ + if (output && output->color_profile) + output_profile = get_cprof(output->color_profile); + + search_param->category = cat; + switch (cat) { + case CMLCMS_CATEGORY_INPUT_TO_BLEND: + case CMLCMS_CATEGORY_INPUT_TO_OUTPUT: + search_param->input_profile = + input_profile ? input_profile : stock_sRGB_profile; + search_param->output_profile = + output_profile ? output_profile : stock_sRGB_profile; + break; + case CMLCMS_CATEGORY_BLEND_TO_OUTPUT: + search_param->output_profile = + output_profile ? output_profile : stock_sRGB_profile; + break; + } + search_param->intent_output = cmlcms_get_render_intent(cat, surface, + output); +} + + static void cmlcms_destroy_color_transform(struct weston_color_transform *xform_base) { @@ -57,9 +108,8 @@ cmlcms_get_surface_color_transform(struct weston_color_manager *cm_base, }; struct cmlcms_color_transform *xform; - /* TODO: use output color profile */ - if (output->color_profile) - return false; + setup_search_param(CMLCMS_CATEGORY_INPUT_TO_BLEND, surface, output, + cm->sRGB_profile, ¶m); xform = cmlcms_color_transform_get(cm, ¶m); if (!xform) @@ -86,9 +136,8 @@ cmlcms_get_output_color_transform(struct weston_color_manager *cm_base, }; struct cmlcms_color_transform *xform; - /* TODO: use output color profile */ - if (output->color_profile) - return false; + setup_search_param(CMLCMS_CATEGORY_BLEND_TO_OUTPUT, NULL, output, + cm->sRGB_profile, ¶m); xform = cmlcms_color_transform_get(cm, ¶m); if (!xform) @@ -104,10 +153,12 @@ cmlcms_get_sRGB_to_output_color_transform(struct weston_color_manager *cm_base, struct weston_color_transform **xform_out) { /* Assumes output color space is sRGB SDR */ + struct weston_color_manager_lcms *cm = get_cmlcms(cm_base); - /* TODO: use output color profile */ - if (output->color_profile) - return false; + struct cmlcms_color_transform_search_param param = {}; + + setup_search_param(CMLCMS_CATEGORY_INPUT_TO_OUTPUT, NULL, output, + cm->sRGB_profile, ¶m); /* Identity transform */ *xform_out = NULL; @@ -127,9 +178,8 @@ cmlcms_get_sRGB_to_blend_color_transform(struct weston_color_manager *cm_base, }; struct cmlcms_color_transform *xform; - /* TODO: use output color profile */ - if (output->color_profile) - return false; + setup_search_param(CMLCMS_CATEGORY_INPUT_TO_BLEND, NULL, output, + cm->sRGB_profile, ¶m); xform = cmlcms_color_transform_get(cm, ¶m); if (!xform) diff --git a/libweston/color-lcms/color-lcms.h b/libweston/color-lcms/color-lcms.h index eb34847c..fabaeadb 100644 --- a/libweston/color-lcms/color-lcms.h +++ b/libweston/color-lcms/color-lcms.h @@ -143,6 +143,11 @@ enum cmlcms_color_transform_type { struct cmlcms_color_transform_search_param { enum cmlcms_color_transform_type type; + + enum cmlcms_category category; + struct cmlcms_color_profile *input_profile; + struct cmlcms_color_profile *output_profile; + cmsUInt32Number intent_output; /* selected intent from output profile */ }; struct cmlcms_color_transform { @@ -165,7 +170,6 @@ struct cmlcms_color_transform { * not used */ cmsHTRANSFORM cmap_3dlut; - }; static inline struct cmlcms_color_transform * From 37e0d54cc9a1a6569e9fc5582cfc2c4d8deb109e Mon Sep 17 00:00:00 2001 From: Vitaly Prosyak Date: Wed, 22 Sep 2021 21:21:27 -0400 Subject: [PATCH 053/609] color-lcms: add matches parameters based on category Use category, intent and output and input profiles for comparison. Signed-off-by: Vitaly Prosyak --- libweston/color-lcms/color-transform.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libweston/color-lcms/color-transform.c b/libweston/color-lcms/color-transform.c index 713be189..7eb5d6ec 100644 --- a/libweston/color-lcms/color-transform.c +++ b/libweston/color-lcms/color-transform.c @@ -133,6 +133,14 @@ transform_matches_params(const struct cmlcms_color_transform *xform, if (xform->search_key.type != param->type) return false; + if (xform->search_key.category != param->category) + return false; + + if (xform->search_key.intent_output != param->intent_output || + xform->search_key.output_profile != param->output_profile || + xform->search_key.input_profile != param->input_profile) + return false; + return true; } From c199aade3ffe2285de89aa9613557862c3c21c98 Mon Sep 17 00:00:00 2001 From: Vitaly Prosyak Date: Sat, 27 Nov 2021 20:44:26 -0500 Subject: [PATCH 054/609] color-lcms: linearization of an arbitrary color profile Graeme sketched a linearization method there: https://lists.freedesktop.org/archives/wayland-devel/2019-March/040171.html Sebastian prototyped there: https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/14/commits Thanks to Pekka for great simplifications in implementation, like the xyz_dot_prod() Quote: "should help untangle lots of the multiplications and summations by saying we are computing dot products, etc". The approach was validated using matrix-shaper and cLUT type of profiles. If profile is matrix-shaper type then an optimization is applied. The extracted EOTF is inverted and concatenated with VCGT, if it is availible. Introduce function cmlcms_reasonable_1D_points which would be shared between linearization method and number of points in 1DLUT for the transform. Co-authored-by: Pekka Paalanen Co-authored-by: Sebastian Wick Signed-off-by: Vitaly Prosyak --- libweston/color-lcms/color-lcms.h | 11 ++ libweston/color-lcms/color-profile.c | 224 +++++++++++++++++++++++++ libweston/color-lcms/color-transform.c | 11 ++ 3 files changed, 246 insertions(+) diff --git a/libweston/color-lcms/color-lcms.h b/libweston/color-lcms/color-lcms.h index fabaeadb..ed6dce0a 100644 --- a/libweston/color-lcms/color-lcms.h +++ b/libweston/color-lcms/color-lcms.h @@ -197,4 +197,15 @@ cmlcms_create_stock_profile(struct weston_color_manager_lcms *cm); void cmlcms_color_profile_destroy(struct cmlcms_color_profile *cprof); +bool +retrieve_eotf_and_output_inv_eotf(cmsContext lcms_ctx, + cmsHPROFILE hProfile, + cmsToneCurve *output_eotf[3], + cmsToneCurve *output_inv_eotf_vcgt[3], + cmsToneCurve *vcgt[3], + unsigned int num_points); + +unsigned int +cmlcms_reasonable_1D_points(void); + #endif /* WESTON_COLOR_LCMS_H */ diff --git a/libweston/color-lcms/color-profile.c b/libweston/color-lcms/color-profile.c index 3f477100..d726493a 100644 --- a/libweston/color-lcms/color-profile.c +++ b/libweston/color-lcms/color-profile.c @@ -36,6 +36,222 @@ #include "shared/helpers.h" #include "shared/string-helpers.h" +struct xyz_arr_flt { + float v[3]; +}; + +static double +xyz_dot_prod(const struct xyz_arr_flt a, const struct xyz_arr_flt b) +{ + return (double)a.v[0] * b.v[0] + + (double)a.v[1] * b.v[1] + + (double)a.v[2] * b.v[2]; +} + +/** + * Graeme sketched a linearization method there: + * https://lists.freedesktop.org/archives/wayland-devel/2019-March/040171.html + */ +static bool +build_eotf_from_clut_profile(cmsContext lcms_ctx, + cmsHPROFILE profile, + cmsToneCurve *output_eotf[3], + int num_points) +{ + int ch, point; + float *curve_array[3]; + float *red = NULL; + cmsHPROFILE xyz_profile = NULL; + cmsHTRANSFORM transform_rgb_to_xyz = NULL; + bool ret = false; + const float div = num_points - 1; + + red = malloc(sizeof(float) * num_points * 3); + if (!red) + goto release; + + curve_array[0] = red; + curve_array[1] = red + num_points; + curve_array[2] = red + 2 * num_points; + + xyz_profile = cmsCreateXYZProfile(); + if (!xyz_profile) + goto release; + + transform_rgb_to_xyz = cmsCreateTransform(profile, TYPE_RGB_FLT, + xyz_profile, TYPE_XYZ_FLT, + INTENT_ABSOLUTE_COLORIMETRIC, + 0); + if (!transform_rgb_to_xyz) + goto release; + + for (ch = 0; ch < 3; ch++) { + struct xyz_arr_flt prim_xyz_max; + struct xyz_arr_flt prim_xyz; + double xyz_square_magnitude; + float rgb[3] = { 0.0f, 0.0f, 0.0f }; + + rgb[ch] = 1.0f; + cmsDoTransform(transform_rgb_to_xyz, rgb, prim_xyz_max.v, 1); + + /** + * Calculate xyz square of magnitude uses single channel 100% and + * others are zero. + */ + xyz_square_magnitude = xyz_dot_prod(prim_xyz_max, prim_xyz_max); + /** + * Build rgb tone curves + */ + for (point = 0; point < num_points; point++) { + rgb[ch] = (float)point / div; + cmsDoTransform(transform_rgb_to_xyz, rgb, prim_xyz.v, 1); + curve_array[ch][point] = xyz_dot_prod(prim_xyz, + prim_xyz_max) / + xyz_square_magnitude; + } + + /** + * Create LCMS object of rgb tone curves and validate whether + * monotonic + */ + output_eotf[ch] = cmsBuildTabulatedToneCurveFloat(lcms_ctx, + num_points, + curve_array[ch]); + if (!output_eotf[ch]) + goto release; + if (!cmsIsToneCurveMonotonic(output_eotf[ch])) { + /** + * It is interesting to see how this profile was created. + * We assume that such a curve could not be used for linearization + * of arbitrary profile. + */ + goto release; + } + } + ret = true; + +release: + if (transform_rgb_to_xyz) + cmsDeleteTransform(transform_rgb_to_xyz); + if (xyz_profile) + cmsCloseProfile(xyz_profile); + free(red); + if (ret == false) + cmsFreeToneCurveTriple(output_eotf); + + return ret; +} + +/** + * Concatenation of two monotonic tone curves. + * LCMS API cmsJoinToneCurve does y = Y^-1(X(t)), + * but want to have y = Y^(X(t)) + */ +static cmsToneCurve * +lcmsJoinToneCurve(cmsContext context_id, const cmsToneCurve *X, + const cmsToneCurve *Y, unsigned int resulting_points) +{ + cmsToneCurve *out = NULL; + float t, x; + float *res = NULL; + unsigned int i; + + res = zalloc(resulting_points * sizeof(float)); + if (res == NULL) + goto error; + + for (i = 0; i < resulting_points; i++) { + t = (float)i / (resulting_points - 1); + x = cmsEvalToneCurveFloat(X, t); + res[i] = cmsEvalToneCurveFloat(Y, x); + } + + out = cmsBuildTabulatedToneCurveFloat(context_id, resulting_points, res); + +error: + if (res != NULL) + free(res); + + return out; +} +/** + * Extract EOTF from matrix-shaper and cLUT profiles, + * then invert and concatenate with 'vcgt' curve if it + * is available. + */ +bool +retrieve_eotf_and_output_inv_eotf(cmsContext lcms_ctx, + cmsHPROFILE hProfile, + cmsToneCurve *output_eotf[3], + cmsToneCurve *output_inv_eotf_vcgt[3], + cmsToneCurve *vcgt[3], + unsigned int num_points) +{ + cmsToneCurve *curve = NULL; + const cmsToneCurve * const *vcgt_curves; + unsigned i; + cmsTagSignature tags[] = { + cmsSigRedTRCTag, cmsSigGreenTRCTag, cmsSigBlueTRCTag + }; + + if (cmsIsMatrixShaper(hProfile)) { + /** + * Optimization for matrix-shaper profile + * May have 1DLUT->3x3->3x3->1DLUT, 1DLUT->3x3->1DLUT + */ + for (i = 0 ; i < 3; i++) { + curve = cmsReadTag(hProfile, tags[i]); + if (!curve) + goto fail; + output_eotf[i] = cmsDupToneCurve(curve); + if (!output_eotf[i]) + goto fail; + } + } else { + /** + * Linearization of cLUT profile may have 1DLUT->3DLUT->1DLUT, + * 1DLUT->3DLUT, 3DLUT + */ + if (!build_eotf_from_clut_profile(lcms_ctx, hProfile, + output_eotf, num_points)) + goto fail; + } + /** + * If the caller looking for eotf only then return early. + * It could be used for input profile when identity case: EOTF + INV_EOTF + * in pipeline only. + */ + if (output_inv_eotf_vcgt == NULL) + return true; + + for (i = 0; i < 3; i++) { + curve = cmsReverseToneCurve(output_eotf[i]); + if (!curve) + goto fail; + output_inv_eotf_vcgt[i] = curve; + } + vcgt_curves = cmsReadTag(hProfile, cmsSigVcgtTag); + if (vcgt_curves && vcgt_curves[0] && vcgt_curves[1] && vcgt_curves[2]) { + for (i = 0; i < 3; i++) { + curve = lcmsJoinToneCurve(lcms_ctx, + output_inv_eotf_vcgt[i], + vcgt_curves[i], num_points); + if (!curve) + goto fail; + cmsFreeToneCurve(output_inv_eotf_vcgt[i]); + output_inv_eotf_vcgt[i] = curve; + if (vcgt) + vcgt[i] = cmsDupToneCurve(vcgt_curves[i]); + } + } + return true; + +fail: + cmsFreeToneCurveTriple(output_eotf); + cmsFreeToneCurveTriple(output_inv_eotf_vcgt); + return false; +} + /* FIXME: sync with spec! */ static bool validate_icc_profile(cmsHPROFILE profile, char **errmsg) @@ -183,6 +399,14 @@ cmlcms_create_stock_profile(struct weston_color_manager_lcms *cm) if (!cm->sRGB_profile) goto err_close; + if (!retrieve_eotf_and_output_inv_eotf(cm->lcms_ctx, + cm->sRGB_profile->profile, + cm->sRGB_profile->output_eotf, + cm->sRGB_profile->output_inv_eotf_vcgt, + cm->sRGB_profile->vcgt, + cmlcms_reasonable_1D_points())) + goto err_close; + return true; err_close: diff --git a/libweston/color-lcms/color-transform.c b/libweston/color-lcms/color-transform.c index 7eb5d6ec..ae53e287 100644 --- a/libweston/color-lcms/color-transform.c +++ b/libweston/color-lcms/color-transform.c @@ -39,6 +39,17 @@ struct tone_curve_def { cmsFloat64Number params[5]; }; +/** + * The method is used in linearization of an arbitrary color profile + * when EOTF is retrieved we want to know a generic way to decide the number + * of points + */ +unsigned int +cmlcms_reasonable_1D_points(void) +{ + return 1024; +} + /* * LCMS uses the required number of 'params' based on 'cmstype', the parametric * tone curve number. LCMS honors negative 'cmstype' as inverse function. From 6099c0e24bb58d35cab694623498915c0084bbac Mon Sep 17 00:00:00 2001 From: Vitaly Prosyak Date: Sat, 27 Nov 2021 22:01:06 -0500 Subject: [PATCH 055/609] color-lcms: LCMS transform for color mapping Use 3D LUT for color mapping. For category CMLCMS_CATEGORY_INPUT_TO_BLEND use transform which has 3 profiles: input, output and light linearizing transfer function. For category CMLCMS_CATEGORY_INPUT_TO_OUTPUT use input and output profiles +VCGT. For category CMLCMS_CATEGORY_BLEND_TO_OUTPUT use output inverse EOTF + VCGT. Co-authored-by: Pekka Paalanen Signed-off-by: Vitaly Prosyak --- libweston/color-lcms/color-lcms.c | 49 +++--- libweston/color-lcms/color-lcms.h | 14 -- libweston/color-lcms/color-transform.c | 229 ++++++++++++++++++------- 3 files changed, 195 insertions(+), 97 deletions(-) diff --git a/libweston/color-lcms/color-lcms.c b/libweston/color-lcms/color-lcms.c index e50e0bfd..5e3b52e5 100644 --- a/libweston/color-lcms/color-lcms.c +++ b/libweston/color-lcms/color-lcms.c @@ -99,13 +99,7 @@ cmlcms_get_surface_color_transform(struct weston_color_manager *cm_base, struct weston_surface_color_transform *surf_xform) { struct weston_color_manager_lcms *cm = get_cmlcms(cm_base); - struct cmlcms_color_transform_search_param param = { - /* - * Assumes both content and output color spaces are sRGB SDR. - * This defines the blending space as optical sRGB SDR. - */ - .type = CMLCMS_TYPE_EOTF_sRGB, - }; + struct cmlcms_color_transform_search_param param = {}; struct cmlcms_color_transform *xform; setup_search_param(CMLCMS_CATEGORY_INPUT_TO_BLEND, surface, output, @@ -116,7 +110,15 @@ cmlcms_get_surface_color_transform(struct weston_color_manager *cm_base, return false; surf_xform->transform = &xform->base; - surf_xform->identity_pipeline = true; + /* + * When we introduce LCMS plug-in we can precisely answer this question + * by examining the color pipeline using precision parameters. For now + * we just compare if it is same pointer or not. + */ + if (xform->search_key.input_profile == xform->search_key.output_profile) + surf_xform->identity_pipeline = true; + else + surf_xform->identity_pipeline = false; return true; } @@ -127,13 +129,7 @@ cmlcms_get_output_color_transform(struct weston_color_manager *cm_base, struct weston_color_transform **xform_out) { struct weston_color_manager_lcms *cm = get_cmlcms(cm_base); - struct cmlcms_color_transform_search_param param = { - /* - * Assumes blending space is optical sRGB SDR and - * output color space is sRGB SDR. - */ - .type = CMLCMS_TYPE_EOTF_sRGB_INV, - }; + struct cmlcms_color_transform_search_param param = {}; struct cmlcms_color_transform *xform; setup_search_param(CMLCMS_CATEGORY_BLEND_TO_OUTPUT, NULL, output, @@ -152,16 +148,24 @@ cmlcms_get_sRGB_to_output_color_transform(struct weston_color_manager *cm_base, struct weston_output *output, struct weston_color_transform **xform_out) { - /* Assumes output color space is sRGB SDR */ struct weston_color_manager_lcms *cm = get_cmlcms(cm_base); - struct cmlcms_color_transform_search_param param = {}; + struct cmlcms_color_transform *xform; setup_search_param(CMLCMS_CATEGORY_INPUT_TO_OUTPUT, NULL, output, cm->sRGB_profile, ¶m); - - /* Identity transform */ - *xform_out = NULL; + /* + * Create a color transformation when output profile is not stock + * sRGB profile. + */ + if (param.output_profile != cm->sRGB_profile) { + xform = cmlcms_color_transform_get(cm, ¶m); + if (!xform) + return false; + *xform_out = &xform->base; + } else { + *xform_out = NULL; /* Identity transform */ + } return true; } @@ -172,10 +176,7 @@ cmlcms_get_sRGB_to_blend_color_transform(struct weston_color_manager *cm_base, struct weston_color_transform **xform_out) { struct weston_color_manager_lcms *cm = get_cmlcms(cm_base); - struct cmlcms_color_transform_search_param param = { - /* Assumes blending space is optical sRGB SDR */ - .type = CMLCMS_TYPE_EOTF_sRGB, - }; + struct cmlcms_color_transform_search_param param = {}; struct cmlcms_color_transform *xform; setup_search_param(CMLCMS_CATEGORY_INPUT_TO_BLEND, NULL, output, diff --git a/libweston/color-lcms/color-lcms.h b/libweston/color-lcms/color-lcms.h index ed6dce0a..b7291764 100644 --- a/libweston/color-lcms/color-lcms.h +++ b/libweston/color-lcms/color-lcms.h @@ -131,19 +131,8 @@ cmlcms_get_color_profile_from_icc(struct weston_color_manager *cm, void cmlcms_destroy_color_profile(struct weston_color_profile *cprof_base); -/* - * Perhaps a placeholder, until we get actual color spaces involved and - * see how this would work better. - */ -enum cmlcms_color_transform_type { - CMLCMS_TYPE_EOTF_sRGB = 0, - CMLCMS_TYPE_EOTF_sRGB_INV, - CMLCMS_TYPE__END, -}; struct cmlcms_color_transform_search_param { - enum cmlcms_color_transform_type type; - enum cmlcms_category category; struct cmlcms_color_profile *input_profile; struct cmlcms_color_profile *output_profile; @@ -158,9 +147,6 @@ struct cmlcms_color_transform { struct cmlcms_color_transform_search_param search_key; - /* for EOTF types It would be deprecated */ - cmsToneCurve *curve; - /** * 3D LUT color mapping part of the transformation, if needed. * For category CMLCMS_CATEGORY_INPUT_TO_OUTPUT it includes pre-curve and diff --git a/libweston/color-lcms/color-transform.c b/libweston/color-lcms/color-transform.c index ae53e287..c7b45639 100644 --- a/libweston/color-lcms/color-transform.c +++ b/libweston/color-lcms/color-transform.c @@ -33,12 +33,6 @@ #include "color-lcms.h" #include "shared/helpers.h" -/* Arguments to cmsBuildParametricToneCurve() */ -struct tone_curve_def { - cmsInt32Number cmstype; - cmsFloat64Number params[5]; -}; - /** * The method is used in linearization of an arbitrary color profile * when EOTF is retrieved we want to know a generic way to decide the number @@ -50,42 +44,87 @@ cmlcms_reasonable_1D_points(void) return 1024; } -/* - * LCMS uses the required number of 'params' based on 'cmstype', the parametric - * tone curve number. LCMS honors negative 'cmstype' as inverse function. - * These are LCMS built-in parametric tone curves. - */ -static const struct tone_curve_def predefined_eotf_curves[] = { - [CMLCMS_TYPE_EOTF_sRGB] = { - .cmstype = 4, - .params = { 2.4, 1. / 1.055, 0.055 / 1.055, 1. / 12.92, 0.04045 }, - }, - [CMLCMS_TYPE_EOTF_sRGB_INV] = { - .cmstype = -4, - .params = { 2.4, 1. / 1.055, 0.055 / 1.055, 1. / 12.92, 0.04045 }, - }, -}; +static unsigned int +cmlcms_reasonable_3D_points(void) +{ + return 33; +} static void -cmlcms_fill_in_tone_curve(struct weston_color_transform *xform_base, - float *values, unsigned len) +fill_in_curves(cmsToneCurve *curves[3], float *values, unsigned len) { - struct cmlcms_color_transform *xform = get_xform(xform_base); float *R_lut = values; float *G_lut = R_lut + len; float *B_lut = G_lut + len; unsigned i; - cmsFloat32Number x, y; - - assert(xform->curve != NULL); - assert(len > 1); + cmsFloat32Number x; for (i = 0; i < len; i++) { x = (double)i / (len - 1); - y = cmsEvalToneCurveFloat(xform->curve, x); - R_lut[i] = y; - G_lut[i] = y; - B_lut[i] = y; + R_lut[i] = cmsEvalToneCurveFloat(curves[0], x); + G_lut[i] = cmsEvalToneCurveFloat(curves[1], x); + B_lut[i] = cmsEvalToneCurveFloat(curves[2], x); + } +} + +static void +cmlcms_fill_in_pre_curve(struct weston_color_transform *xform_base, + float *values, unsigned len) +{ + struct cmlcms_color_transform *xform = get_xform(xform_base); + + assert(xform->search_key.category == CMLCMS_CATEGORY_BLEND_TO_OUTPUT); + + assert(len > 1); + + fill_in_curves(xform->search_key.output_profile->output_inv_eotf_vcgt, + values, len); +} + +/** + * Clamp value to [0.0, 1.0], except pass NaN through. + * + * This function is not intended for hiding NaN. + */ +static float +ensure_unorm(float v) +{ + if (v <= 0.0f) + return 0.0f; + if (v > 1.0f) + return 1.0f; + return v; +} + +static void +cmlcms_fill_in_3dlut(struct weston_color_transform *xform_base, + float *lut, unsigned int len) +{ + struct cmlcms_color_transform *xform = get_xform(xform_base); + float rgb_in[3]; + float rgb_out[3]; + unsigned int index; + unsigned int value_b, value_r, value_g; + float divider = len - 1; + + assert(xform->search_key.category == CMLCMS_CATEGORY_INPUT_TO_BLEND || + xform->search_key.category == CMLCMS_CATEGORY_INPUT_TO_OUTPUT); + + for (value_b = 0; value_b < len; value_b++) { + for (value_g = 0; value_g < len; value_g++) { + for (value_r = 0; value_r < len; value_r++) { + rgb_in[0] = (float)value_r / divider; + rgb_in[1] = (float)value_g / divider; + rgb_in[2] = (float)value_b / divider; + + cmsDoTransform(xform->cmap_3dlut, rgb_in, rgb_out, 1); + + index = 3 * (value_r + len * (value_g + len * value_b)); + lut[index ] = ensure_unorm(rgb_out[0]); + lut[index + 1] = ensure_unorm(rgb_out[1]); + lut[index + 2] = ensure_unorm(rgb_out[2]); + } + } } } @@ -93,57 +132,129 @@ void cmlcms_color_transform_destroy(struct cmlcms_color_transform *xform) { wl_list_remove(&xform->link); - if (xform->curve) - cmsFreeToneCurve(xform->curve); + + if (xform->cmap_3dlut) + cmsDeleteTransform(xform->cmap_3dlut); + + unref_cprof(xform->search_key.input_profile); + unref_cprof(xform->search_key.output_profile); free(xform); } +static bool +xform_set_cmap_3dlut(struct cmlcms_color_transform *xform, + cmsHPROFILE input_profile, + cmsHPROFILE output_profile, + cmsToneCurve *curves[3], + cmsUInt32Number intent) +{ + struct weston_color_manager_lcms *cm = get_cmlcms(xform->base.cm); + cmsHPROFILE arr_prof[3] = { input_profile, output_profile, NULL }; + int num_profiles = 2; + + if (curves[0]) { + arr_prof[2] = cmsCreateLinearizationDeviceLinkTHR(cm->lcms_ctx, + cmsSigRgbData, + curves); + if (!arr_prof[2]) + return false; + + num_profiles = 3; + } + + xform->cmap_3dlut = cmsCreateMultiprofileTransformTHR(cm->lcms_ctx, + arr_prof, + num_profiles, + TYPE_RGB_FLT, + TYPE_RGB_FLT, + intent, + 0); + if (!xform->cmap_3dlut) { + cmsCloseProfile(arr_prof[2]); + weston_log("color-lcms error: fail cmsCreateMultiprofileTransformTHR.\n"); + return false; + } + xform->base.mapping.type = WESTON_COLOR_MAPPING_TYPE_3D_LUT; + xform->base.mapping.u.lut3d.fill_in = cmlcms_fill_in_3dlut; + xform->base.mapping.u.lut3d.optimal_len = + cmlcms_reasonable_3D_points(); + cmsCloseProfile(arr_prof[2]); + + return true; +} + static struct cmlcms_color_transform * cmlcms_color_transform_create(struct weston_color_manager_lcms *cm, - const struct cmlcms_color_transform_search_param *param) + const struct cmlcms_color_transform_search_param *search_param) { + struct cmlcms_color_profile *input_profile = search_param->input_profile; + struct cmlcms_color_profile *output_profile = search_param->output_profile; struct cmlcms_color_transform *xform; - const struct tone_curve_def *tonedef; - - if (param->type < 0 || param->type >= CMLCMS_TYPE__END) { - weston_log("color-lcms error: bad color transform type in %s.\n", - __func__); - return NULL; - } - tonedef = &predefined_eotf_curves[param->type]; + bool ok = false; xform = zalloc(sizeof *xform); if (!xform) return NULL; - xform->curve = cmsBuildParametricToneCurve(cm->lcms_ctx, - tonedef->cmstype, - tonedef->params); - if (xform->curve == NULL) { - weston_log("color-lcms error: failed to build parametric tone curve.\n"); - free(xform); - return NULL; + weston_color_transform_init(&xform->base, &cm->base); + wl_list_init(&xform->link); + xform->search_key = *search_param; + xform->search_key.input_profile = ref_cprof(input_profile); + xform->search_key.output_profile = ref_cprof(output_profile); + + /* Ensure the linearization etc. have been extracted. */ + if (!output_profile->output_eotf[0]) { + if (!retrieve_eotf_and_output_inv_eotf(cm->lcms_ctx, + output_profile->profile, + output_profile->output_eotf, + output_profile->output_inv_eotf_vcgt, + output_profile->vcgt, + cmlcms_reasonable_1D_points())) + goto error; } - weston_color_transform_init(&xform->base, &cm->base); - xform->search_key = *param; + switch (search_param->category) { + case CMLCMS_CATEGORY_INPUT_TO_BLEND: + /* Use EOTF to linearize the result. */ + ok = xform_set_cmap_3dlut(xform, input_profile->profile, + output_profile->profile, + output_profile->output_eotf, + search_param->intent_output); + break; - xform->base.pre_curve.type = WESTON_COLOR_CURVE_TYPE_LUT_3x1D; - xform->base.pre_curve.u.lut_3x1d.fill_in = cmlcms_fill_in_tone_curve; - xform->base.pre_curve.u.lut_3x1d.optimal_len = 256; + case CMLCMS_CATEGORY_INPUT_TO_OUTPUT: + /* Apply also VCGT if it exists. */ + ok = xform_set_cmap_3dlut(xform, input_profile->profile, + output_profile->profile, + output_profile->vcgt, + search_param->intent_output); + break; - wl_list_insert(&cm->color_transform_list, &xform->link); + case CMLCMS_CATEGORY_BLEND_TO_OUTPUT: + xform->base.pre_curve.type = WESTON_COLOR_CURVE_TYPE_LUT_3x1D; + xform->base.pre_curve.u.lut_3x1d.fill_in = cmlcms_fill_in_pre_curve; + xform->base.pre_curve.u.lut_3x1d.optimal_len = + cmlcms_reasonable_1D_points(); + ok = true; + break; + } + if (!ok) + goto error; + + wl_list_insert(&cm->color_transform_list, &xform->link); return xform; + +error: + cmlcms_color_transform_destroy(xform); + weston_log("CM cmlcms_color_transform_create failed\n"); + return NULL; } static bool transform_matches_params(const struct cmlcms_color_transform *xform, const struct cmlcms_color_transform_search_param *param) { - if (xform->search_key.type != param->type) - return false; - if (xform->search_key.category != param->category) return false; From 264a18f01ab3d6ea7ed4893c6562e18cc30c23ab Mon Sep 17 00:00:00 2001 From: Vitaly Prosyak Date: Mon, 29 Nov 2021 18:34:09 -0500 Subject: [PATCH 056/609] tests: shared color processing functions Added pixel pipeline processing as following: tone curve(EOTF) + 3x3 matrix + tone curve(INV_EOTF) Co-authored-by: Pekka Paalanen Signed-off-by: Vitaly Prosyak --- tests/color_util.c | 174 +++++++++++++++++++++++++++++++++++++++++++-- tests/color_util.h | 48 ++++++++++++- 2 files changed, 213 insertions(+), 9 deletions(-) diff --git a/tests/color_util.c b/tests/color_util.c index 52250781..511b8d9e 100644 --- a/tests/color_util.c +++ b/tests/color_util.c @@ -23,18 +23,90 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - #include "config.h" #include #include "color_util.h" #include +#include +#include +#include "shared/helpers.h" + +struct color_tone_curve { + enum transfer_fn fn; + enum transfer_fn inv_fn; + + /* LCMS2 API */ + int internal_type; + double param[5]; +}; + +const struct color_tone_curve arr_curves[] = { + { + .fn = TRANSFER_FN_SRGB_EOTF, + .inv_fn = TRANSFER_FN_SRGB_EOTF_INVERSE, + .internal_type = 4, + .param = { 2.4, 1. / 1.055, 0.055 / 1.055, 1. / 12.92, 0.04045 } , + }, + { + .fn = TRANSFER_FN_ADOBE_RGB_EOTF, + .inv_fn = TRANSFER_FN_ADOBE_RGB_EOTF_INVERSE, + .internal_type = 1, + .param = { 563./256., 0.0, 0.0, 0.0 , 0.0 } , + }, + { + .fn = TRANSFER_FN_POWER2_4_EOTF, + .inv_fn = TRANSFER_FN_POWER2_4_EOTF_INVERSE, + .internal_type = 1, + .param = { 2.4, 0.0, 0.0, 0.0 , 0.0 } , + } + +}; + +bool +find_tone_curve_type(enum transfer_fn fn, int *type, double params[5]) +{ + const int size_arr = ARRAY_LENGTH(arr_curves); + const struct color_tone_curve *curve; + for (curve = &arr_curves[0]; curve < &arr_curves[size_arr]; curve++ ) { + if (curve->fn == fn ) + *type = curve->internal_type; + else if (curve->inv_fn == fn) + *type = -curve->internal_type; + else + continue; + + memcpy(params, curve->param, sizeof(curve->param)); + return true; + } + + return false; +} + +/** + * NaN comes out as is + *This function is not intended for hiding NaN. + */ static float -sRGB_EOTF(float e) +ensure_unit_range(float v) { - assert(e >= 0.0f); - assert(e <= 1.0f); + const float tol = 1e-5f; + const float lim_lo = -tol; + const float lim_hi = 1.0f + tol; + assert(v >= lim_lo); + if (v < 0.0f) + return 0.0f; + assert(v <= lim_hi); + if (v > 1.0f) + return 1.0f; + return v; +} + +static float +sRGB_EOTF(float e) +{ + e = ensure_unit_range(e); if (e <= 0.04045) return e / 12.92; else @@ -44,15 +116,40 @@ sRGB_EOTF(float e) static float sRGB_EOTF_inv(float o) { - assert(o >= 0.0f); - assert(o <= 1.0f); - + o = ensure_unit_range(o); if (o <= 0.04045 / 12.92) return o * 12.92; else return pow(o, 1.0 / 2.4) * 1.055 - 0.055; } +static float +AdobeRGB_EOTF(float e) +{ + e = ensure_unit_range(e); + return pow(e, 563./256.); +} + +static float +AdobeRGB_EOTF_inv(float o) +{ + o = ensure_unit_range(o); + return pow(o, 256./563.); +} + +static float +Power2_4_EOTF(float e) +{ + e = ensure_unit_range(e); + return pow(e, 2.4); +} + +static float +Power2_4_EOTF_inv(float o) +{ + o = ensure_unit_range(o); + return pow(o, 1./2.4); +} void sRGB_linearize(struct color_float *cf) @@ -62,6 +159,35 @@ sRGB_linearize(struct color_float *cf) cf->b = sRGB_EOTF(cf->b); } +static float +apply_tone_curve(enum transfer_fn fn, float r) +{ + float ret = 0; + + switch(fn) { + case TRANSFER_FN_SRGB_EOTF: + ret = sRGB_EOTF(r); + break; + case TRANSFER_FN_SRGB_EOTF_INVERSE: + ret = sRGB_EOTF_inv(r); + break; + case TRANSFER_FN_ADOBE_RGB_EOTF: + ret = AdobeRGB_EOTF(r); + break; + case TRANSFER_FN_ADOBE_RGB_EOTF_INVERSE: + ret = AdobeRGB_EOTF_inv(r); + break; + case TRANSFER_FN_POWER2_4_EOTF: + ret = Power2_4_EOTF(r); + break; + case TRANSFER_FN_POWER2_4_EOTF_INVERSE: + ret = Power2_4_EOTF_inv(r); + break; + } + + return ret; +} + void sRGB_delinearize(struct color_float *cf) { @@ -82,3 +208,37 @@ a8r8g8b8_to_float(uint32_t v) return cf; } + +void +process_pixel_using_pipeline(enum transfer_fn pre_curve, + const struct lcmsMAT3 *mat, + enum transfer_fn post_curve, + const struct color_float *in, + struct color_float *out) +{ + int i, j; + float rgb_in[3]; + float out_blend[3]; + float tmp; + + rgb_in[0] = in->r; + rgb_in[1] = in->g; + rgb_in[2] = in->b; + + for (i = 0; i < 3; i++) + rgb_in[i] = apply_tone_curve(pre_curve, rgb_in[i]); + + for (i = 0; i < 3; i++) { + tmp = 0.0f; + for (j = 0; j < 3; j++) + tmp += rgb_in[j] * mat->v[j].n[i]; + out_blend[i] = tmp; + } + + for (i = 0; i < 3; i++) + out_blend[i] = apply_tone_curve(post_curve, out_blend[i]); + + out->r = out_blend[0]; + out->g = out_blend[1]; + out->b = out_blend[2]; +} diff --git a/tests/color_util.h b/tests/color_util.h index a9cfd02d..96f50049 100644 --- a/tests/color_util.h +++ b/tests/color_util.h @@ -25,18 +25,62 @@ */ #include - +#include struct color_float { float r, g, b, a; }; +struct lcmsVEC3 { + float n[3]; +}; + +struct lcmsMAT3 { + struct lcmsVEC3 v[3]; +}; + +enum transfer_fn { + TRANSFER_FN_SRGB_EOTF, + TRANSFER_FN_SRGB_EOTF_INVERSE, + TRANSFER_FN_ADOBE_RGB_EOTF, + TRANSFER_FN_ADOBE_RGB_EOTF_INVERSE, + TRANSFER_FN_POWER2_4_EOTF, + TRANSFER_FN_POWER2_4_EOTF_INVERSE, +}; + +/* + * A helper to lay out a matrix in the natural writing order in code + * instead of needing to transpose in your mind every time you read it. + * The matrix is laid out as written: + * ⎡ a11 a12 a13 ⎤ + * ⎢ a21 a22 a23 ⎥ + * ⎣ a31 a32 a33 ⎦ + * where the first digit is row and the second digit is column. + */ +#define LCMSMAT3(a11, a12, a13, \ + a21, a22, a23, \ + a31, a32, a33) ((struct lcmsMAT3) \ + { /* Each vector is a column => looks like a transpose */ \ + .v[0] = { .n = { a11, a21, a31} }, \ + .v[1] = { .n = { a12, a22, a32} }, \ + .v[2] = { .n = { a13, a23, a33} }, \ + }) + void sRGB_linearize(struct color_float *cf); void sRGB_delinearize(struct color_float *cf); - struct color_float a8r8g8b8_to_float(uint32_t v); + +bool +find_tone_curve_type(enum transfer_fn fn, int *type, double params[5]); + +void +process_pixel_using_pipeline(enum transfer_fn pre_curve, + const struct lcmsMAT3 *mat, + enum transfer_fn post_curve, + const struct color_float *in, + struct color_float *out); From fe35ca2d68b3414e2cffdf743ad4227aab639973 Mon Sep 17 00:00:00 2001 From: Vitaly Prosyak Date: Tue, 25 Jan 2022 22:26:03 -0500 Subject: [PATCH 057/609] tests: color shaper-matrix test 1. Use fixture_setup to set the generated by LCMS output profile based on given chromaticities and white points. The following list of well known chromaticities: - sRGB - adobe RGB - bt2020 and white point is D65. Use INTENT_ABSOLUTE_COLORIMETRIC to avoid BPC. Input profile is always sRGB and it is used internally by Weston as stock profile. 2. Use these hardcoded matrixes as part of pipeline 1DLUT->3x3->1DLUT. The diagnostic code to retrieve the transform matrix is availble into test in the comments. The conversion matrixes generated for the following cases: - sRGB to sRGB (unity) - sRGB to adobeRGB - sRGB to BT2020 3. Compare GPU shaders(gl texture3D) vs manual pipeline calculation Use different max tolerable error per transform. There are comments how number of points in 3DLUT is related to tolerance. Tolerance depends more on the 1D LUT used for the inv EOTF than the tested 3D LUT size: 9x9x9, 17x17x17, 33x33x33, 127x127x127. 4. Enable build matrix-shaper test if color-management-lcms is enabled. Co-authored-by: Pekka Paalanen Signed-off-by: Vitaly Prosyak --- tests/color-shaper-matrix-test.c | 470 +++++++++++++++++++++++++++ tests/meson.build | 12 + tests/reference/shaper_matrix-00.png | Bin 0 -> 402 bytes tests/reference/shaper_matrix-01.png | Bin 0 -> 506 bytes tests/reference/shaper_matrix-02.png | Bin 0 -> 730 bytes 5 files changed, 482 insertions(+) create mode 100644 tests/color-shaper-matrix-test.c create mode 100644 tests/reference/shaper_matrix-00.png create mode 100644 tests/reference/shaper_matrix-01.png create mode 100644 tests/reference/shaper_matrix-02.png diff --git a/tests/color-shaper-matrix-test.c b/tests/color-shaper-matrix-test.c new file mode 100644 index 00000000..0c04e070 --- /dev/null +++ b/tests/color-shaper-matrix-test.c @@ -0,0 +1,470 @@ +/* + * Copyright 2021 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "config.h" + +#include + +#include "weston-test-client-helper.h" +#include "weston-test-fixture-compositor.h" +#include "color_util.h" +#include +#include +#include + +struct lcms_pipeline { + /** + * Color space name + */ + const char *color_space; + /** + * Chromaticities for output profile + */ + cmsCIExyYTRIPLE prim_output; + /** + * tone curve enum + */ + enum transfer_fn pre_fn; + /** + * Transform matrix from sRGB to target chromaticities in prim_output + */ + struct lcmsMAT3 mat; + /** + * tone curve enum + */ + enum transfer_fn post_fn; + /** + * 2/255 or 3/255 maximum possible error, where 255 is 8 bit max value + */ + int tolerance; +}; + +static const int WINDOW_WIDTH = 256; +static const int WINDOW_HEIGHT = 24; + +static cmsCIExyY wp_d65 = { 0.31271, 0.32902, 1.0 }; + +struct setup_args { + struct fixture_metadata meta; + struct lcms_pipeline pipeline; +}; + +/* + * Using currently destination gamut bigger than source. + * Using https://www.colour-science.org/ we can extract conversion matrix: + * import colour + * colour.matrix_RGB_to_RGB(colour.RGB_COLOURSPACES['sRGB'], colour.RGB_COLOURSPACES['Adobe RGB (1998)'], None) + * colour.matrix_RGB_to_RGB(colour.RGB_COLOURSPACES['sRGB'], colour.RGB_COLOURSPACES['ITU-R BT.2020'], None) + */ +const struct setup_args arr_setup[] = { + { + .meta.name = "sRGB->sRGB unity", + .pipeline = { + .color_space = "sRGB", + .prim_output = { + .Red = { 0.640, 0.330, 1.0 }, + .Green = { 0.300, 0.600, 1.0 }, + .Blue = { 0.150, 0.060, 1.0 } + }, + .pre_fn = TRANSFER_FN_SRGB_EOTF, + .mat = LCMSMAT3(1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0), + .post_fn = TRANSFER_FN_SRGB_EOTF_INVERSE, + .tolerance = 0 + } + }, + { + .meta.name = "sRGB->adobeRGB", + .pipeline = { + .color_space = "adobeRGB", + .prim_output = { + .Red = { 0.640, 0.330, 1.0 }, + .Green = { 0.210, 0.710, 1.0 }, + .Blue = { 0.150, 0.060, 1.0 } + }, + .pre_fn = TRANSFER_FN_SRGB_EOTF, + .mat = LCMSMAT3(0.715119, 0.284881, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.041169, 0.958831), + .post_fn = TRANSFER_FN_ADOBE_RGB_EOTF_INVERSE, + .tolerance = 1 + /* + * Tolerance depends more on the 1D LUT used for the + * inv EOTF than the tested 3D LUT size: + * 9x9x9, 17x17x17, 33x33x33, 127x127x127 + */ + } + }, + { + .meta.name = "sRGB->bt2020", + .pipeline = { + .color_space = "bt2020", + .prim_output = { + .Red = { 0.708, 0.292, 1.0 }, + .Green = { 0.170, 0.797, 1.0 }, + .Blue = { 0.131, 0.046, 1.0 } + }, + .pre_fn = TRANSFER_FN_SRGB_EOTF, + .mat = LCMSMAT3(0.627402, 0.329292, 0.043306, + 0.069095, 0.919544, 0.011360, + 0.016394, 0.088028, 0.895578), + /* this is equivalent to BT.1886 with zero black level */ + .post_fn = TRANSFER_FN_POWER2_4_EOTF_INVERSE, + .tolerance = 5 + /* + * TODO: when we add power-law in the curve enumeration + * in GL-renderer, then we should fix the tolerance + * as the error should reduce a lot. + */ + } + } +}; + +struct image_header { + int width; + int height; + int stride; + int depth; + pixman_format_code_t pix_format; + uint32_t *data; +}; + +static void +get_image_prop(struct buffer *buf, struct image_header *header) +{ + header->width = pixman_image_get_width(buf->image); + header->height = pixman_image_get_height(buf->image); + header->stride = pixman_image_get_stride(buf->image); + header->depth = pixman_image_get_depth(buf->image); + header->pix_format = pixman_image_get_format (buf->image); + header->data = pixman_image_get_data(buf->image); +} + +static void +gen_ramp_rgb(const struct image_header *header, int bitwidth, int width_bar) +{ + static const int hue[][3] = { + { 1, 1, 1 }, /* White */ + { 1, 1, 0 }, /* Yellow */ + { 0, 1, 1 }, /* Cyan */ + { 0, 1, 0 }, /* Green */ + { 1, 0, 1 }, /* Magenta */ + { 1, 0, 0 }, /* Red */ + { 0, 0, 1 }, /* Blue */ + }; + const int num_hues = ARRAY_LENGTH(hue); + + float val_max; + int x, y; + int hue_index; + float value; + unsigned char r, g, b; + uint32_t *pixel; + + float n_steps = width_bar - 1; + + val_max = (1 << bitwidth) - 1; + + for (y = 0; y < header->height; y++) { + hue_index = (y * num_hues) / (header->height - 1); + hue_index = MIN(hue_index, num_hues - 1); + + for (x = 0; x < header->width; x++) { + struct color_float rgb = { 0, 0, 0 }; + + value = (float)x / (float)(header->width - 1); + + if (width_bar > 1) + value = floor(value * n_steps) / n_steps; + + if (hue[hue_index][0]) + rgb.r = value; + if (hue[hue_index][1]) + rgb.g = value; + if (hue[hue_index][2]) + rgb.b = value; + + sRGB_delinearize(&rgb); + + r = round(rgb.r * val_max); + g = round(rgb.g * val_max); + b = round(rgb.b * val_max); + + pixel = header->data + (y * header->stride / 4) + x; + *pixel = (255U << 24) | (r << 16) | (g << 8) | b; + } + } +} + +static cmsHPROFILE +build_lcms_profile_output(const struct lcms_pipeline *pipeline) +{ + cmsToneCurve *arr_curves[3]; + cmsHPROFILE hRGB; + int type_inverse_tone_curve; + double inverse_tone_curve_param[5]; + + assert(find_tone_curve_type(pipeline->post_fn, &type_inverse_tone_curve, + inverse_tone_curve_param)); + + /* + * We are creating output profile and therefore we can use the following: + * calling semantics: + * cmsBuildParametricToneCurve(type_inverse_tone_curve, inverse_tone_curve_param) + * The function find_tone_curve_type sets the type of curve positive if it + * is tone curve and negative if it is inverse. When we create an ICC + * profile we should use a tone curve, the inversion is done by LCMS + * when the profile is used for output. + */ + + arr_curves[0] = arr_curves[1] = arr_curves[2] = + cmsBuildParametricToneCurve(NULL, + (-1) * type_inverse_tone_curve, + inverse_tone_curve_param); + + assert(arr_curves[0]); + hRGB = cmsCreateRGBProfileTHR(NULL, &wp_d65, + &pipeline->prim_output, arr_curves); + assert(hRGB); + + cmsFreeToneCurve(arr_curves[0]); + return hRGB; +} + +static char * +build_output_icc_profile(const struct lcms_pipeline *pipe) +{ + char *profile_name = NULL; + cmsHPROFILE profile = NULL; + char *wd; + int ret; + bool saved; + + wd = realpath(".", NULL); + assert(wd); + ret = asprintf(&profile_name, "%s/matrix-shaper-test-%s.icm", wd, + pipe->color_space); + assert(ret > 0); + + profile = build_lcms_profile_output(pipe); + assert(profile); + + saved = cmsSaveProfileToFile(profile, profile_name); + assert(saved); + + cmsCloseProfile(profile); + + return profile_name; +} + +static enum test_result_code +fixture_setup(struct weston_test_harness *harness, const struct setup_args *arg) +{ + struct compositor_setup setup; + char *file_name; + + compositor_setup_defaults(&setup); + setup.renderer = RENDERER_GL; + setup.backend = WESTON_BACKEND_HEADLESS; + setup.width = WINDOW_WIDTH; + setup.height = WINDOW_HEIGHT; + setup.shell = SHELL_TEST_DESKTOP; + + file_name = build_output_icc_profile(&arg->pipeline); + if (!file_name) + return RESULT_HARD_ERROR; + + weston_ini_setup(&setup, + cfgln("[core]"), + cfgln("color-management=true"), + cfgln("[output]"), + cfgln("name=headless"), + cfgln("icc_profile=%s", file_name)); + + free(file_name); + + return weston_test_harness_execute_as_client(harness, &setup); +} +DECLARE_FIXTURE_SETUP_WITH_ARG(fixture_setup, arr_setup, meta); + +static bool +compare_float(float ref, float dst, int x, const char *chan, + float *max_diff, float max_allow_diff) +{ +#if 0 + /* + * This file can be loaded in Octave for visualization. + * + * S = load('compare_float_dump.txt'); + * + * rvec = S(S(:,1)==114, 2:3); + * gvec = S(S(:,1)==103, 2:3); + * bvec = S(S(:,1)==98, 2:3); + * + * figure + * subplot(3, 1, 1); + * plot(rvec(:,1), rvec(:,2) .* 255, 'r'); + * subplot(3, 1, 2); + * plot(gvec(:,1), gvec(:,2) .* 255, 'g'); + * subplot(3, 1, 3); + * plot(bvec(:,1), bvec(:,2) .* 255, 'b'); + */ + static FILE *fp = NULL; + + if (!fp) + fp = fopen("compare_float_dump.txt", "w"); + fprintf(fp, "%d %d %f\n", chan[0], x, dst - ref); + fflush(fp); +#endif + + float diff = fabsf(ref - dst); + + if (diff > *max_diff) + *max_diff = diff; + + if (diff <= max_allow_diff) + return true; + + testlog("x=%d %s: ref %f != dst %f, delta %f\n", + x, chan, ref, dst, dst - ref); + + return false; +} + +static bool +process_pipeline_comparison(const struct image_header *src, + const struct image_header *shot, + const struct setup_args * arg) +{ + const float max_pixel_value = 255.0; + struct color_float max_diff_pipeline = { 0.0f, 0.0f, 0.0f, 0.0f }; + float max_allow_diff = arg->pipeline.tolerance / max_pixel_value; + float max_err = 0; + float f_max_err = 0; + bool ok = true; + uint32_t *row_ptr, *row_ptr_shot; + int y, x; + struct color_float pix_src; + struct color_float pix_src_pipeline; + struct color_float pix_shot; + + for (y = 0; y < src->height; y++) { + row_ptr = (uint32_t*)((uint8_t*)src->data + (src->stride * y)); + row_ptr_shot = (uint32_t*)((uint8_t*)shot->data + (shot->stride * y)); + + for (x = 0; x < src->width; x++) { + pix_src = a8r8g8b8_to_float(row_ptr[x]); + pix_shot = a8r8g8b8_to_float(row_ptr_shot[x]); + /* do pipeline processing */ + process_pixel_using_pipeline(arg->pipeline.pre_fn, + &arg->pipeline.mat, + arg->pipeline.post_fn, + &pix_src, &pix_src_pipeline); + /* check if pipeline matches to shader variant */ + ok &= compare_float(pix_src_pipeline.r, pix_shot.r, x,"r", + &max_diff_pipeline.r, max_allow_diff); + ok &= compare_float(pix_src_pipeline.g, pix_shot.g, x, "g", + &max_diff_pipeline.g, max_allow_diff); + ok &= compare_float(pix_src_pipeline.b, pix_shot.b, x, "b", + &max_diff_pipeline.b, max_allow_diff); + } + } + max_err = max_diff_pipeline.r; + if (max_err < max_diff_pipeline.g) + max_err = max_diff_pipeline.g; + if (max_err < max_diff_pipeline.b) + max_err = max_diff_pipeline.b; + + f_max_err = max_pixel_value * max_err; + + testlog("%s %s %s tol_req %d, tol_cal %f, max diff: r=%f, g=%f, b=%f\n", + __func__, ok == true? "SUCCESS":"FAILURE", + arg->meta.name, arg->pipeline.tolerance, f_max_err, + max_diff_pipeline.r, max_diff_pipeline.g, max_diff_pipeline.b); + + return ok; +} + +static bool +check_process_pattern_ex(struct buffer *src, struct buffer *shot, + const struct setup_args * arg) +{ + struct image_header header_src; + struct image_header header_shot; + bool ok; + + get_image_prop(src, &header_src); + get_image_prop(shot, &header_shot); + + /* no point to compare different images */ + assert(header_src.width == header_shot.width); + assert(header_src.height == header_shot.height); + + ok = process_pipeline_comparison(&header_src, &header_shot, arg); + + return ok; +} + +/* + * Test that matrix-shaper profile does CM correctly, it is used color ramp pattern + */ +TEST(shaper_matrix) +{ + const int width = WINDOW_WIDTH; + const int height = WINDOW_HEIGHT; + const int bitwidth = 8; + const int width_bar = 32; + + struct client *client; + struct buffer *buf; + struct buffer *shot; + struct wl_surface *surface; + struct image_header image; + bool match; + int seq_no = get_test_fixture_index(); + + client = create_client_and_test_surface(0, 0, width, height); + assert(client); + surface = client->surface->wl_surface; + + buf = create_shm_buffer_a8r8g8b8(client, width, height); + get_image_prop(buf, &image); + gen_ramp_rgb(&image, bitwidth, width_bar); + + wl_surface_attach(surface, buf->proxy, 0, 0); + wl_surface_damage(surface, 0, 0, width, height); + wl_surface_commit(surface); + + shot = capture_screenshot_of_output(client); + assert(shot); + + match = verify_image(shot, "shaper_matrix", seq_no, NULL, seq_no); + assert(check_process_pattern_ex(buf, shot, &arr_setup[seq_no])); + assert(match); + buffer_destroy(shot); + buffer_destroy(buf); + client_destroy(client); +} diff --git a/tests/meson.build b/tests/meson.build index d8e96e77..2d464ddc 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -226,6 +226,18 @@ tests = [ }, ] +if get_option('color-management-lcms') + dep_lcms2 = dependency('lcms2', version: '>= 2.9', required: false) + if not dep_lcms2.found() + error('color-management-lcms tests require lcms2 which was not found. Or, you can use \'-Dcolor-management-lcms=false\'.') + endif + tests += { + 'name': 'color-shaper-matrix', + 'dep_objs': [ dep_libm, dep_lcms2 ] + } +endif + + tests_standalone = [ ['config-parser', [], [ dep_zucmain ]], ['matrix', [], [ dep_libm, dep_matrix_c ]], diff --git a/tests/reference/shaper_matrix-00.png b/tests/reference/shaper_matrix-00.png new file mode 100644 index 0000000000000000000000000000000000000000..3671b0eedf99645588d31e74644a9e1544f01e69 GIT binary patch literal 402 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K5BsiFWN>$PlW!{@x)B4rYP@|pPl2{~Z1fvQb3 zu7He4Xk1`&2V%it28YZz@$BUn7qnkou>0bI+1D4WzPO!y&Yq>strFMv pn@#`l-wlUO7cl(i0r~@Eja{q>S9+~T2`ebbJzf1=);T3K0RRmjtrGwM literal 0 HcmV?d00001 diff --git a/tests/reference/shaper_matrix-01.png b/tests/reference/shaper_matrix-01.png new file mode 100644 index 0000000000000000000000000000000000000000..6f600083bb7a0d13ee7b142125ceb37be91b8eca GIT binary patch literal 506 zcmVz5?y6LPwe(rNJxOcGCE zulzvCsnh-gC9U}#T2xv;K#P;#046ma0F#>973fQ^Vg9XHgKmm7=pxjhvtr#jVIFJH zNvJ^!EEUBXv?z9wBjmLIKnd+1@Xo0bmU`s_|3X?zM#w8I_^7tl16;xfS%aF~cmPal zY=M4b3-rg<575FE$mGTYU{d1&FsZ3rzx1Z}Fn<9Jx`PIN(lfQ#KzFW~#~O47rzvRx z4N4_5xX7t7uPjlE0)MV3mozn&P)_HNyR5c=uRa^~=z$X2Pa3pXgPP=c08DD?8ua5% zPhreI;WvOujR(M_re=Wz7D!-$1QtkafdsaP^VkB3Es*d#IVvHm)0!VBA=`r%sJ)~$ wuUwK_OIq`eN71(kwm>E~9srXX4}eL{4+$I$6GVC$g8%>k07*qoM6N<$g8cN}&Hw-a literal 0 HcmV?d00001 diff --git a/tests/reference/shaper_matrix-02.png b/tests/reference/shaper_matrix-02.png new file mode 100644 index 0000000000000000000000000000000000000000..0abca0be8725e6f1feb8a72c1c9ce4de973c9a09 GIT binary patch literal 730 zcmV<00ww*4P)65>lKvl0{{epZ6=2ciGPf1o7iJjIt%oU{Xh6%YduSQ;?^fu(`#mznkcL-Y1q z@~!LDwi&Ll4IZF%y|ht&h5SLN!thi6(RIE9Z)O!j2S}uzfJX@mVaas9exyMwESZ<+ zP|}+&=??iI(R+$dfWXp-0SGLO(F*jZf0g`udzJj(_C@mNiZ|3>Pq1`oMLbA&!`%{Fb?_*ml{fq)q)^V9 zr+m-)jXu|~K?7?e1|YCBMk`RWH_2D~rFr;FB;ME$adJE}9c;}VY|stzH;21R@H&SjaC%8R*rsMy9Bfc* zHJ6_ue;`dI+2JIUC1zV;exiKvM+1RH5Cafc8slTQ?~-p_FA1%I>&JghtX2C{c&P^b zVbgBiLDrbVMcO1k3IX4VPkb(hCA0aOZ~W@81S+Ui<5!mL#u0(sJIk0-sAnVCD4X!nNrMEQKFh9%(YB;cKW zk_74rN@gVzFUc%1*D8TUGb|wq_(uznNER+oU~R+z1eQh&KwxS70|GIP=Etk+H2?qr M07*qoM6N<$f@S_u-~a#s literal 0 HcmV?d00001 From 3e6ef529f812ee1d21f7cf92279e29421cf87200 Mon Sep 17 00:00:00 2001 From: Robert Mader Date: Thu, 17 Feb 2022 11:35:30 +0100 Subject: [PATCH 058/609] clients/simple-dmabuf-*: Increase buffer limit to four In certain situations these clients crash a lot due to the low buffer limit. Four buffers is also what EGL allows without blocking and what is arguably the upper limit of what a compositor should demand. Signed-off-by: Robert Mader --- clients/simple-dmabuf-egl.c | 2 +- clients/simple-dmabuf-feedback.c | 2 +- clients/simple-dmabuf-v4l.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clients/simple-dmabuf-egl.c b/clients/simple-dmabuf-egl.c index 33df4cf9..ef0d9de6 100644 --- a/clients/simple-dmabuf-egl.c +++ b/clients/simple-dmabuf-egl.c @@ -131,7 +131,7 @@ struct buffer { int release_fence_fd; }; -#define NUM_BUFFERS 3 +#define NUM_BUFFERS 4 struct window { struct display *display; diff --git a/clients/simple-dmabuf-feedback.c b/clients/simple-dmabuf-feedback.c index dd2e64a5..2a729a62 100644 --- a/clients/simple-dmabuf-feedback.c +++ b/clients/simple-dmabuf-feedback.c @@ -47,7 +47,7 @@ #include #include -#define NUM_BUFFERS 3 +#define NUM_BUFFERS 4 /* We have to hack the DRM-backend to pretend that planes of the underlying * hardware don't support this format. If you change the value of this constant, diff --git a/clients/simple-dmabuf-v4l.c b/clients/simple-dmabuf-v4l.c index 85dd7a3d..a19570f9 100644 --- a/clients/simple-dmabuf-v4l.c +++ b/clients/simple-dmabuf-v4l.c @@ -127,7 +127,7 @@ struct buffer { int data_offsets[VIDEO_MAX_PLANES]; }; -#define NUM_BUFFERS 3 +#define NUM_BUFFERS 4 struct window { struct display *display; From 2ac6b6b084a877adde64db7faff2ed22eb3ea97a Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 8 Feb 2022 22:39:42 +0000 Subject: [PATCH 059/609] tests: Add dependency on screenshooter client protocol Given that the test-helper code relies on the screenshooter protocol, make sure it's available for us to build, and the dependency ensures we build in order. Fixes: #588 Signed-off-by: Daniel Stone --- tests/meson.build | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/meson.build b/tests/meson.build index 2d464ddc..222091cd 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -29,8 +29,9 @@ lib_test_client = static_library( 'weston-test-client-helper.c', 'weston-test-fixture-compositor.c', weston_test_client_protocol_h, - weston_screenshooter_protocol_c, weston_test_protocol_c, + weston_screenshooter_client_protocol_h, + weston_screenshooter_protocol_c, viewporter_client_protocol_h, viewporter_protocol_c, 'color_util.h', From 4012062228489ad96c5db685e8abe938e2c06809 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 15 Feb 2022 13:37:32 +0200 Subject: [PATCH 060/609] tests: add rgb[] alias in color_float Individual struct fields are inconvenient to index into, yet most operations on a color just repeat the same for each of RGB channel. Being able to index into RGB avoids repeating the same code for each channel. Alpha channel is left as separate, since it is almost never handled the same as RGB. The union keeps the old .r, .g and .b addressing working. The static asserts ensure the aliasing is correct. For demonstration, two simple functions in color_util.c are converted. Unfortunately initializers need to be corrected everywhere. Field .a is not explicitly initialized because it is unused in these cases. This change should make code easier to read. This change requires gnu99 or c11 standard. gnu99 is already the default in top-level meson.build. Signed-off-by: Pekka Paalanen --- tests/alpha-blending-test.c | 2 +- tests/color-shaper-matrix-test.c | 4 ++-- tests/color_util.c | 24 ++++++++++++++++++------ tests/color_util.h | 15 ++++++++++++++- 4 files changed, 35 insertions(+), 10 deletions(-) diff --git a/tests/alpha-blending-test.c b/tests/alpha-blending-test.c index e2916be9..b3f8bdee 100644 --- a/tests/alpha-blending-test.c +++ b/tests/alpha-blending-test.c @@ -299,7 +299,7 @@ check_blend_pattern(struct buffer *bg, struct buffer *fg, struct buffer *shot, uint32_t *bg_row = get_middle_row(bg); uint32_t *fg_row = get_middle_row(fg); uint32_t *shot_row = get_middle_row(shot); - struct color_float max_diff = { 0.0f, 0.0f, 0.0f, 0.0f }; + struct color_float max_diff = { .rgb = { 0.0f, 0.0f, 0.0f } }; bool ret = true; int x; diff --git a/tests/color-shaper-matrix-test.c b/tests/color-shaper-matrix-test.c index 0c04e070..331336a3 100644 --- a/tests/color-shaper-matrix-test.c +++ b/tests/color-shaper-matrix-test.c @@ -193,7 +193,7 @@ gen_ramp_rgb(const struct image_header *header, int bitwidth, int width_bar) hue_index = MIN(hue_index, num_hues - 1); for (x = 0; x < header->width; x++) { - struct color_float rgb = { 0, 0, 0 }; + struct color_float rgb = { .rgb = { 0, 0, 0 } }; value = (float)x / (float)(header->width - 1); @@ -360,7 +360,7 @@ process_pipeline_comparison(const struct image_header *src, const struct setup_args * arg) { const float max_pixel_value = 255.0; - struct color_float max_diff_pipeline = { 0.0f, 0.0f, 0.0f, 0.0f }; + struct color_float max_diff_pipeline = { .rgb = { 0.0f, 0.0f, 0.0f } }; float max_allow_diff = arg->pipeline.tolerance / max_pixel_value; float max_err = 0; float f_max_err = 0; diff --git a/tests/color_util.c b/tests/color_util.c index 511b8d9e..bc5ca801 100644 --- a/tests/color_util.c +++ b/tests/color_util.c @@ -29,8 +29,18 @@ #include #include #include +#include #include "shared/helpers.h" +static_assert(sizeof(struct color_float) == 4 * sizeof(float), + "unexpected padding in struct color_float"); +static_assert(offsetof(struct color_float, r) == offsetof(struct color_float, rgb[COLOR_CHAN_R]), + "unexpected offset for struct color_float::r"); +static_assert(offsetof(struct color_float, g) == offsetof(struct color_float, rgb[COLOR_CHAN_G]), + "unexpected offset for struct color_float::g"); +static_assert(offsetof(struct color_float, b) == offsetof(struct color_float, rgb[COLOR_CHAN_B]), + "unexpected offset for struct color_float::b"); + struct color_tone_curve { enum transfer_fn fn; enum transfer_fn inv_fn; @@ -154,9 +164,10 @@ Power2_4_EOTF_inv(float o) void sRGB_linearize(struct color_float *cf) { - cf->r = sRGB_EOTF(cf->r); - cf->g = sRGB_EOTF(cf->g); - cf->b = sRGB_EOTF(cf->b); + int i; + + for (i = 0; i < COLOR_CHAN_NUM; i++) + cf->rgb[i] = sRGB_EOTF(cf->rgb[i]); } static float @@ -191,9 +202,10 @@ apply_tone_curve(enum transfer_fn fn, float r) void sRGB_delinearize(struct color_float *cf) { - cf->r = sRGB_EOTF_inv(cf->r); - cf->g = sRGB_EOTF_inv(cf->g); - cf->b = sRGB_EOTF_inv(cf->b); + int i; + + for (i = 0; i < COLOR_CHAN_NUM; i++) + cf->rgb[i] = sRGB_EOTF_inv(cf->rgb[i]); } struct color_float diff --git a/tests/color_util.h b/tests/color_util.h index 96f50049..f822d00a 100644 --- a/tests/color_util.h +++ b/tests/color_util.h @@ -27,8 +27,21 @@ #include #include +enum color_chan_index { + COLOR_CHAN_R = 0, + COLOR_CHAN_G, + COLOR_CHAN_B, + COLOR_CHAN_NUM +}; + struct color_float { - float r, g, b, a; + union { + float rgb[COLOR_CHAN_NUM]; + struct { + float r, g, b; + }; + }; + float a; }; struct lcmsVEC3 { From 29d4472e13573ad3247eb3b1056286bc6d6e882f Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 15 Feb 2022 14:51:26 +0200 Subject: [PATCH 061/609] tests: use color_float rgb[] alias more Iterate over rgb[] array instead of repeating the code for .r, .g and .b. Also in process_pipeline_comparison() f_max_err variable is dropped since it was not used much. This should make the code easier to read. Signed-off-by: Pekka Paalanen --- tests/alpha-blending-test.c | 26 ++++++++++--------- tests/color-shaper-matrix-test.c | 43 ++++++++++++++++---------------- tests/color_util.c | 23 ++++++----------- 3 files changed, 42 insertions(+), 50 deletions(-) diff --git a/tests/alpha-blending-test.c b/tests/alpha-blending-test.c index b3f8bdee..1a315828 100644 --- a/tests/alpha-blending-test.c +++ b/tests/alpha-blending-test.c @@ -109,14 +109,14 @@ premult_color(uint32_t a, uint32_t r, uint32_t g, uint32_t b) static void unpremult_float(struct color_float *cf) { + int i; + if (cf->a == 0.0f) { - cf->r = 0.0f; - cf->g = 0.0f; - cf->b = 0.0f; + for (i = 0; i < COLOR_CHAN_NUM; i++) + cf->rgb[i] = 0.0f; } else { - cf->r /= cf->a; - cf->g /= cf->a; - cf->b /= cf->a; + for (i = 0; i < COLOR_CHAN_NUM; i++) + cf->rgb[i] /= cf->a; } } @@ -218,11 +218,13 @@ verify_sRGB_blend_a8r8g8b8(uint32_t bg32, uint32_t fg32, uint32_t dst32, int x, struct color_float *max_diff, enum blend_space space) { + const char *const chan_name[COLOR_CHAN_NUM] = { "r", "g", "b" }; struct color_float bg = a8r8g8b8_to_float(bg32); struct color_float fg = a8r8g8b8_to_float(fg32); struct color_float dst = a8r8g8b8_to_float(dst32); struct color_float ref; bool ok = true; + int i; unpremult_float(&bg); unpremult_float(&fg); @@ -233,16 +235,16 @@ verify_sRGB_blend_a8r8g8b8(uint32_t bg32, uint32_t fg32, uint32_t dst32, sRGB_linearize(&fg); } - ref.r = (1.0f - fg.a) * bg.r + fg.a * fg.r; - ref.g = (1.0f - fg.a) * bg.g + fg.a * fg.g; - ref.b = (1.0f - fg.a) * bg.b + fg.a * fg.b; + for (i = 0; i < COLOR_CHAN_NUM; i++) + ref.rgb[i] = (1.0f - fg.a) * bg.rgb[i] + fg.a * fg.rgb[i]; if (space == BLEND_LINEAR) sRGB_delinearize(&ref); - ok = compare_float(ref.r, dst.r, x, "r", &max_diff->r) && ok; - ok = compare_float(ref.g, dst.g, x, "g", &max_diff->g) && ok; - ok = compare_float(ref.b, dst.b, x, "b", &max_diff->b) && ok; + for (i = 0; i < COLOR_CHAN_NUM; i++) { + ok = compare_float(ref.rgb[i], dst.rgb[i], x, + chan_name[i], &max_diff->rgb[i]) && ok; + } return ok; } diff --git a/tests/color-shaper-matrix-test.c b/tests/color-shaper-matrix-test.c index 331336a3..3c354874 100644 --- a/tests/color-shaper-matrix-test.c +++ b/tests/color-shaper-matrix-test.c @@ -166,7 +166,7 @@ get_image_prop(struct buffer *buf, struct image_header *header) static void gen_ramp_rgb(const struct image_header *header, int bitwidth, int width_bar) { - static const int hue[][3] = { + static const int hue[][COLOR_CHAN_NUM] = { { 1, 1, 1 }, /* White */ { 1, 1, 0 }, /* Yellow */ { 0, 1, 1 }, /* Cyan */ @@ -180,6 +180,7 @@ gen_ramp_rgb(const struct image_header *header, int bitwidth, int width_bar) float val_max; int x, y; int hue_index; + int chan; float value; unsigned char r, g, b; uint32_t *pixel; @@ -200,12 +201,10 @@ gen_ramp_rgb(const struct image_header *header, int bitwidth, int width_bar) if (width_bar > 1) value = floor(value * n_steps) / n_steps; - if (hue[hue_index][0]) - rgb.r = value; - if (hue[hue_index][1]) - rgb.g = value; - if (hue[hue_index][2]) - rgb.b = value; + for (chan = 0; chan < COLOR_CHAN_NUM; chan++) { + if (hue[hue_index][chan]) + rgb.rgb[chan] = value; + } sRGB_delinearize(&rgb); @@ -359,14 +358,15 @@ process_pipeline_comparison(const struct image_header *src, const struct image_header *shot, const struct setup_args * arg) { + const char *const chan_name[COLOR_CHAN_NUM] = { "r", "g", "b" }; const float max_pixel_value = 255.0; struct color_float max_diff_pipeline = { .rgb = { 0.0f, 0.0f, 0.0f } }; float max_allow_diff = arg->pipeline.tolerance / max_pixel_value; - float max_err = 0; - float f_max_err = 0; + float max_err = 0.0f; bool ok = true; uint32_t *row_ptr, *row_ptr_shot; int y, x; + int chan; struct color_float pix_src; struct color_float pix_src_pipeline; struct color_float pix_shot; @@ -383,26 +383,25 @@ process_pipeline_comparison(const struct image_header *src, &arg->pipeline.mat, arg->pipeline.post_fn, &pix_src, &pix_src_pipeline); + /* check if pipeline matches to shader variant */ - ok &= compare_float(pix_src_pipeline.r, pix_shot.r, x,"r", - &max_diff_pipeline.r, max_allow_diff); - ok &= compare_float(pix_src_pipeline.g, pix_shot.g, x, "g", - &max_diff_pipeline.g, max_allow_diff); - ok &= compare_float(pix_src_pipeline.b, pix_shot.b, x, "b", - &max_diff_pipeline.b, max_allow_diff); + for (chan = 0; chan < COLOR_CHAN_NUM; chan++) { + ok &= compare_float(pix_src_pipeline.rgb[chan], + pix_shot.rgb[chan], + x, chan_name[chan], + &max_diff_pipeline.rgb[chan], + max_allow_diff); + } } } - max_err = max_diff_pipeline.r; - if (max_err < max_diff_pipeline.g) - max_err = max_diff_pipeline.g; - if (max_err < max_diff_pipeline.b) - max_err = max_diff_pipeline.b; - f_max_err = max_pixel_value * max_err; + for (chan = 0; chan < COLOR_CHAN_NUM; chan++) + max_err = MAX(max_err, max_diff_pipeline.rgb[chan]); testlog("%s %s %s tol_req %d, tol_cal %f, max diff: r=%f, g=%f, b=%f\n", __func__, ok == true? "SUCCESS":"FAILURE", - arg->meta.name, arg->pipeline.tolerance, f_max_err, + arg->meta.name, arg->pipeline.tolerance, + max_err * max_pixel_value, max_diff_pipeline.r, max_diff_pipeline.g, max_diff_pipeline.b); return ok; diff --git a/tests/color_util.c b/tests/color_util.c index bc5ca801..aa24372d 100644 --- a/tests/color_util.c +++ b/tests/color_util.c @@ -229,28 +229,19 @@ process_pixel_using_pipeline(enum transfer_fn pre_curve, struct color_float *out) { int i, j; - float rgb_in[3]; - float out_blend[3]; + struct color_float cf; float tmp; - rgb_in[0] = in->r; - rgb_in[1] = in->g; - rgb_in[2] = in->b; - - for (i = 0; i < 3; i++) - rgb_in[i] = apply_tone_curve(pre_curve, rgb_in[i]); + for (i = 0; i < COLOR_CHAN_NUM; i++) + cf.rgb[i] = apply_tone_curve(pre_curve, in->rgb[i]); for (i = 0; i < 3; i++) { tmp = 0.0f; for (j = 0; j < 3; j++) - tmp += rgb_in[j] * mat->v[j].n[i]; - out_blend[i] = tmp; + tmp += cf.rgb[j] * mat->v[j].n[i]; + out->rgb[i] = tmp; } - for (i = 0; i < 3; i++) - out_blend[i] = apply_tone_curve(post_curve, out_blend[i]); - - out->r = out_blend[0]; - out->g = out_blend[1]; - out->b = out_blend[2]; + for (i = 0; i < COLOR_CHAN_NUM; i++) + out->rgb[i] = apply_tone_curve(post_curve, out->rgb[i]); } From 351e6a4b21c7983c8427edc19d9ecfc453031010 Mon Sep 17 00:00:00 2001 From: Takuro Ashie Date: Thu, 17 Feb 2022 15:42:05 +0900 Subject: [PATCH 062/609] Don't send compositor's global key bindings to the input method Although weston_compositor_run_key_binding() is called when the current keyboard grab is default_grab or input_method_grab, swallowing the key event is processed only on default_grab. As a result key events that should be swallowed are sent to the input method unexpectedly. For example, when a user press `Super + s` on weston-editor to take a screen shot, `s` will be unexpectedly entered to the text area. I confirmed such behaviour with weston-simple-im and fcitx5-5.0.10. It doesn't occur with weston-keyboard because it doesn't install keyboard grab. Signed-off-by: Takuro Ashie --- libweston/bindings.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libweston/bindings.c b/libweston/bindings.c index 2ca999a3..526379ab 100644 --- a/libweston/bindings.c +++ b/libweston/bindings.c @@ -310,8 +310,8 @@ weston_compositor_run_key_binding(struct weston_compositor *compositor, /* If this was a key binding and it didn't * install a keyboard grab, install one now to * swallow the key press. */ - if (keyboard->grab == - &keyboard->default_grab) + if (keyboard->grab == &keyboard->default_grab || + keyboard->grab == &keyboard->input_method_grab) install_binding_grab(keyboard, time, key, From 89587db3cba43a057714a4d4e5593ae56939f789 Mon Sep 17 00:00:00 2001 From: James Le Cuirot Date: Fri, 4 Feb 2022 23:43:50 +0000 Subject: [PATCH 063/609] meson.build: Fix -Dbackend-default=auto following fbdev deprecation Signed-off-by: James Le Cuirot --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 1c73e130..d761903e 100644 --- a/meson.build +++ b/meson.build @@ -121,7 +121,7 @@ config_h.set10('TEST_GL_RENDERER', get_option('test-gl-renderer')) backend_default = get_option('backend-default') if backend_default == 'auto' - foreach b : [ 'headless', 'fbdev', 'x11', 'wayland', 'drm' ] + foreach b : [ 'headless', 'x11', 'wayland', 'drm' ] if get_option('backend-' + b) backend_default = b endif From 8e2c67c317ae048ce8b8530ea1e63499c0e9ece6 Mon Sep 17 00:00:00 2001 From: nerdopolis Date: Fri, 24 Dec 2021 19:47:02 -0500 Subject: [PATCH 064/609] clients/desktop-shell: Add a displayname= option for launchers Signed-off-by: n3rdopolis --- clients/desktop-shell.c | 18 +++++++++++++----- man/weston.ini.man | 3 +++ weston.ini.in | 4 ++++ 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/clients/desktop-shell.c b/clients/desktop-shell.c index fb53069e..02c1ae5c 100644 --- a/clients/desktop-shell.c +++ b/clients/desktop-shell.c @@ -142,6 +142,7 @@ struct panel_launcher { cairo_surface_t *icon; int focused, pressed; char *path; + char *displayname; struct wl_list link; struct wl_array envp; struct wl_array argv; @@ -277,7 +278,7 @@ panel_launcher_motion_handler(struct widget *widget, struct input *input, { struct panel_launcher *launcher = data; - widget_set_tooltip(widget, basename((char *)launcher->path), x, y); + widget_set_tooltip(widget, launcher->displayname, x, y); return CURSOR_LEFT_PTR; } @@ -579,6 +580,7 @@ panel_destroy_launcher(struct panel_launcher *launcher) wl_array_release(&launcher->envp); free(launcher->path); + free(launcher->displayname); cairo_surface_destroy(launcher->icon); @@ -678,7 +680,7 @@ load_icon_or_fallback(const char *icon) } static void -panel_add_launcher(struct panel *panel, const char *icon, const char *path) +panel_add_launcher(struct panel *panel, const char *icon, const char *path, const char *displayname) { struct panel_launcher *launcher; char *start, *p, *eq, **ps; @@ -687,6 +689,7 @@ panel_add_launcher(struct panel *panel, const char *icon, const char *path) launcher = xzalloc(sizeof *launcher); launcher->icon = load_icon_or_fallback(icon); launcher->path = xstrdup(path); + launcher->displayname = xstrdup(displayname); wl_array_init(&launcher->envp); wl_array_init(&launcher->argv); @@ -1447,7 +1450,7 @@ static void panel_add_launchers(struct panel *panel, struct desktop *desktop) { struct weston_config_section *s; - char *icon, *path; + char *icon, *path, *displayname; const char *name; int count; @@ -1459,9 +1462,12 @@ panel_add_launchers(struct panel *panel, struct desktop *desktop) weston_config_section_get_string(s, "icon", &icon, NULL); weston_config_section_get_string(s, "path", &path, NULL); + weston_config_section_get_string(s, "displayname", &displayname, NULL); + if (displayname == NULL) + displayname = xstrdup(basename(path)); if (icon != NULL && path != NULL) { - panel_add_launcher(panel, icon, path); + panel_add_launcher(panel, icon, path, displayname); count++; } else { fprintf(stderr, "invalid launcher section\n"); @@ -1469,6 +1475,7 @@ panel_add_launchers(struct panel *panel, struct desktop *desktop) free(icon); free(path); + free(displayname); } if (count == 0) { @@ -1477,7 +1484,8 @@ panel_add_launchers(struct panel *panel, struct desktop *desktop) /* add default launcher */ panel_add_launcher(panel, name, - BINDIR "/weston-terminal"); + BINDIR "/weston-terminal", + "Terminal"); free(name); } } diff --git a/man/weston.ini.man b/man/weston.ini.man index 18b5b2e1..9f97a90c 100644 --- a/man/weston.ini.man +++ b/man/weston.ini.man @@ -461,6 +461,9 @@ There can be multiple launcher sections, one for each launcher. .BI "icon=" icon sets the path to icon image (string). Svg images are not currently supported. .TP 7 +.BI "displayname=" displayname +sets the display name of the launcher that appears in the tooltip. +.TP 7 .BI "path=" program sets the path to the program that is run by clicking on this launcher (string). It is possible to pass arguments and environment variables to the program. For diff --git a/weston.ini.in b/weston.ini.in index 011b1942..bb53f377 100644 --- a/weston.ini.in +++ b/weston.ini.in @@ -24,18 +24,22 @@ startup-animation=fade [launcher] icon=/usr/share/icons/gnome/24x24/apps/utilities-terminal.png path=/usr/bin/gnome-terminal +displayname=Gnome Terminal [launcher] icon=/usr/share/icons/gnome/24x24/apps/utilities-terminal.png path=@bindir@/weston-terminal +displayname=Weston Terminal [launcher] icon=/usr/share/icons/hicolor/24x24/apps/google-chrome.png path=/usr/bin/google-chrome +displayname=Google Chome [launcher] icon=/usr/share/icons/gnome/24x24/apps/arts.png path=@bindir@/weston-flower +displayname=Weston Flower [input-method] path=@libexecdir@/weston-keyboard From 32a790f774966b397e65c1534d3a4501bbcd1b85 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 17 Feb 2022 13:57:51 +0200 Subject: [PATCH 065/609] shared: add WESTON_EXPORT_FOR_TESTS This is a new function exporting macro that intends to make writing unit tests in the Weston test suite easier. A test needs to access a private function to be able to verify its behavior. Previously we have used things like putting such functions in a separate .c file and then building that file into the corresponding test. That is a bit awkward and can lead to proliferation of arbitrary .c files for no good reason. It may also require pre-processor magic, and sometimes copying chunks of code causing a risk of deviating the code being tested from the code actually used. This patch proposes another approach: a private export from a DSO. Except, private exports do not really exist, and this is just a normal export with a specific C macro, and omitting the function from public headers. Once exported, a test program can link the DSO during build, be that a shared library or even a plugin, use the private header declaring the function, and simply call the function in the test. The declaration of WESTON_EXPORT_FOR_TESTS is in shared/helpers.h so that it is available to all components equally while still not being in a public header. Other places that were considered: - include/libweston/libweston.h is a public header, but external users should not know about the macro. - libweston/libweston-private.h is too private and not available to all components, particularly color-lcms plugin. - libweston/backend.h is not appropriate for color-lcms plugin either. Signed-off-by: Pekka Paalanen --- shared/helpers.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/shared/helpers.h b/shared/helpers.h index 1688b8ea..7b722096 100644 --- a/shared/helpers.h +++ b/shared/helpers.h @@ -159,6 +159,18 @@ do { \ tmp___; }) #endif +/** Private symbol export for tests + * + * Symbols tagged with this are private libweston functions that are exported + * only for the test suite to allow unit testing. Nothing else internal or + * external to libweston is allowed to use these exports. + * + * Therefore, the ABI exported with this tag is completely unversioned, and + * is allowed to break at any time without any indication or version bump. + * This may happen in all git branches, including stable release branches. + */ +#define WESTON_EXPORT_FOR_TESTS __attribute__ ((visibility("default"))) + #ifdef __cplusplus } #endif From e2ee2b56f90788e6969a8aa8f3402edfcbde2cb0 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 17 Feb 2022 14:37:03 +0200 Subject: [PATCH 066/609] tests: make vertex-clip use WESTON_EXPORT_FOR_TESTS This is probably the simplest case to demonstrate how to use WESTON_EXPORT_FOR_TESTS. Previously, vertex-clip test re-built vertex-clipping.c for itself. Now it directly links in gl-renderer.so instead as that is where vexter-clipping.c gets built into for actual use. This probably will not work for any installed program, but luckily tests are never installed, so Meson makes sure the DSO is found. Unfortunately we cannot remove the definition of dep_vertex_clipping yet, because clients/cliptest.c needs it. This makes vertex-clip test depend on GL-renderer, but that is where the code is really used. Signed-off-by: Pekka Paalanen --- libweston/vertex-clipping.c | 7 ++++--- tests/meson.build | 13 +++++++++---- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/libweston/vertex-clipping.c b/libweston/vertex-clipping.c index a71e7336..3a05c7bd 100644 --- a/libweston/vertex-clipping.c +++ b/libweston/vertex-clipping.c @@ -26,9 +26,10 @@ #include #include +#include "shared/helpers.h" #include "vertex-clipping.h" -float +WESTON_EXPORT_FOR_TESTS float float_difference(float a, float b) { /* http://www.altdevblogaday.com/2012/02/22/comparing-floating-point-numbers-2012-edition/ */ @@ -282,7 +283,7 @@ clip_polygon_bottom(struct clip_context *ctx, const struct polygon8 *src, #define min(a, b) (((a) > (b)) ? (b) : (a)) #define clip(x, a, b) min(max(x, a), b) -int +WESTON_EXPORT_FOR_TESTS int clip_simple(struct clip_context *ctx, struct polygon8 *surf, float *ex, @@ -296,7 +297,7 @@ clip_simple(struct clip_context *ctx, return surf->n; } -int +WESTON_EXPORT_FOR_TESTS int clip_transformed(struct clip_context *ctx, struct polygon8 *surf, float *ex, diff --git a/tests/meson.build b/tests/meson.build index 222091cd..5b1f0418 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -204,10 +204,6 @@ tests = [ input_timestamps_unstable_v1_protocol_c, ], }, - { - 'name': 'vertex-clip', - 'dep_objs': dep_vertex_clipping, - }, { 'name': 'viewporter', }, { 'name': 'viewporter-shot', }, { @@ -227,6 +223,14 @@ tests = [ }, ] +if get_option('renderer-gl') + tests += { + 'name': 'vertex-clip', + 'link_with': plugin_gl, + } + +endif + if get_option('color-management-lcms') dep_lcms2 = dependency('lcms2', version: '>= 2.9', required: false) if not dep_lcms2.found() @@ -338,6 +342,7 @@ foreach t : tests build_by_default: true, include_directories: common_inc, dependencies: t_deps, + link_with: t.get('link_with', []), install: false, ) From 87f2d09f1838c672b08913014cff198989b0c127 Mon Sep 17 00:00:00 2001 From: Vitaly Prosyak Date: Tue, 1 Mar 2022 22:49:32 -0500 Subject: [PATCH 067/609] color-lcms: Always use cmsContext for LCMS API which has THR suffix Fix a typo. No CM functional change, just redirect LCMS error into created cmsContext which output into weston log. Signed-off-by: Vitaly Prosyak --- libweston/color-lcms/color-profile.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/libweston/color-lcms/color-profile.c b/libweston/color-lcms/color-profile.c index d726493a..b03bd0e6 100644 --- a/libweston/color-lcms/color-profile.c +++ b/libweston/color-lcms/color-profile.c @@ -74,14 +74,15 @@ build_eotf_from_clut_profile(cmsContext lcms_ctx, curve_array[1] = red + num_points; curve_array[2] = red + 2 * num_points; - xyz_profile = cmsCreateXYZProfile(); + xyz_profile = cmsCreateXYZProfileTHR(lcms_ctx); if (!xyz_profile) goto release; - transform_rgb_to_xyz = cmsCreateTransform(profile, TYPE_RGB_FLT, - xyz_profile, TYPE_XYZ_FLT, - INTENT_ABSOLUTE_COLORIMETRIC, - 0); + transform_rgb_to_xyz = cmsCreateTransformTHR(lcms_ctx, profile, + TYPE_RGB_FLT, xyz_profile, + TYPE_XYZ_FLT, + INTENT_ABSOLUTE_COLORIMETRIC, + 0); if (!transform_rgb_to_xyz) goto release; From a96dfc70985480b22a1f8b98548267f2b5bccc74 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Fri, 23 Jul 2021 11:36:56 -0500 Subject: [PATCH 068/609] launchers: remove launchers Moving forward we're going to be supporting libseat and logind as our only launchers. We're doing this to reduce our maintenance burden, and security impact. Libseat supports all our existing use cases, and seatd can replace weston-launch so we no longer have to carry a setuid-root program. This patch removes weston-launch, and launcher-direct, leaving only libseat and logind. Signed-off-by: Derek Foreman --- .gitlab-ci.yml | 2 - .gitlab-ci/build-deps.sh | 5 +- README.md | 12 - doc/sphinx/toc/running-weston.rst | 7 +- libweston/backend-drm/drm.c | 7 +- libweston/backend-fbdev/fbdev.c | 5 +- libweston/launcher-direct.c | 370 ------------ libweston/launcher-impl.h | 2 - libweston/launcher-util.c | 2 - libweston/launcher-weston-launch.c | 396 ------------- libweston/meson.build | 21 - libweston/weston-launch.c | 917 ----------------------------- libweston/weston-launch.h | 51 -- man/weston-bindings.man | 1 - man/weston-drm.man | 28 - man/weston.ini.man | 5 +- man/weston.man | 12 +- meson.build | 4 + meson_options.txt | 7 - 19 files changed, 16 insertions(+), 1838 deletions(-) delete mode 100644 libweston/launcher-direct.c delete mode 100644 libweston/launcher-weston-launch.c delete mode 100644 libweston/weston-launch.c delete mode 100644 libweston/weston-launch.h diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e68b5741..46239fca 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -284,7 +284,6 @@ aarch64-debian-container_prep: -Dtest-skip-is-failure=true -Dlauncher-libseat=true -Ddeprecated-backend-fbdev=true - -Ddeprecated-weston-launch=true after_script: - ninja -C "$BUILDDIR" coverage-html > "$BUILDDIR/meson-logs/ninja-coverage-html.txt" - ninja -C "$BUILDDIR" coverage-xml @@ -338,7 +337,6 @@ docs-build: -Dpipewire=false -Dwerror=true -Dlauncher-libseat=true - -Ddeprecated-weston-launch=true x86_64-debian-no-gl-build: extends: diff --git a/.gitlab-ci/build-deps.sh b/.gitlab-ci/build-deps.sh index 67e06701..158ff7c4 100755 --- a/.gitlab-ci/build-deps.sh +++ b/.gitlab-ci/build-deps.sh @@ -143,9 +143,8 @@ ninja ${NINJAFLAGS} -C build install cd .. rm -rf pipewire-src -# seatd lets us avoid the pain of handling VTs manually through weston-launch -# or open-coding TTY assignment within Weston. We use this for our tests using -# the DRM backend. +# seatd lets us avoid the pain of open-coding TTY assignment within Weston. +# We use this for our tests using the DRM backend. git clone --depth=1 --branch 0.6.1 https://git.sr.ht/~kennylevinsen/seatd cd seatd meson build -Dauto_features=disabled \ diff --git a/README.md b/README.md index 77552ca6..2d093825 100644 --- a/README.md +++ b/README.md @@ -93,13 +93,6 @@ the available configuration options and display backends. It can also be configured through a file on disk; more information on this can be found through `man weston.ini`. -In some special cases, such as when running remotely or without logind's session -control, Weston may not be able to run directly from a text console. In these -situations, you can instead execute the `weston-launch` helper, which will gain -privileged access to input and output devices by running as root, then granting -access to the main Weston binary running as your user. Running Weston this way -is not recommended unless necessary. - Documentation ============= @@ -308,11 +301,6 @@ Details: - xwayland ??? -- weston-launch is still with libweston even though it can only launch - Weston and nothing else. We would like to allow it to launch any compositor, - but since it gives by design root access to input devices and DRM, how can - we restrict it to intended programs? - There are still many more details to be decided. diff --git a/doc/sphinx/toc/running-weston.rst b/doc/sphinx/toc/running-weston.rst index e14ec55d..715bff42 100644 --- a/doc/sphinx/toc/running-weston.rst +++ b/doc/sphinx/toc/running-weston.rst @@ -91,17 +91,14 @@ You can start Weston from a VT assuming that there's a seat manager supported by backend to be used by ``libseat`` can optionally be selected with ``$LIBSEAT_BACKEND``. If ``libseat`` and ``seatd`` are both installed, but ``seatd`` is not already running, it can be started with ``sudo -- seatd -g -video``. If no seat manager supported by ``libseat`` is available, you can use -the ``weston-launch`` application that can handle VT switching. +video``. Another way of launching Weston is via ssh or a serial terminal. The simplest option here is to use the ``libseat`` launcher with ``seatd``. The process for setting that up is identical to the one described above, where one just need to ensure that ``seatd`` is running with the appropriate arguments, after which one can just run ``weston``. Another option, is to rely on logind and start weston -as systemd user service: :ref:`weston-user-service`. Alternatively and as a last -resort, one can run Weston as root, specifying the tty to use on the command -line: If TTY 2 is active, one would run ``weston --tty 2`` as root. +as systemd user service: :ref:`weston-user-service`. Running Weston on a different seat on a stand-alone back-end ------------------------------------------------------------ diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index be647deb..387184ef 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -3017,13 +3017,12 @@ drm_backend_create(struct weston_compositor *compositor, if (parse_gbm_format(config->gbm_format, DRM_FORMAT_XRGB8888, &b->gbm_format) < 0) goto err_compositor; - /* Check if we run drm-backend using weston-launch */ + /* Check if we run drm-backend using a compatible launcher */ compositor->launcher = weston_launcher_connect(compositor, config->tty, seat_id, true); if (compositor->launcher == NULL) { - weston_log("fatal: drm backend should be run using " - "weston-launch binary, or your system should " - "provide the logind D-Bus API.\n"); + weston_log("fatal: your system should either provide the " + "logind D-Bus API, or use seatd.\n"); goto err_compositor; } diff --git a/libweston/backend-fbdev/fbdev.c b/libweston/backend-fbdev/fbdev.c index 7dfea6fb..38a9b4c3 100644 --- a/libweston/backend-fbdev/fbdev.c +++ b/libweston/backend-fbdev/fbdev.c @@ -926,9 +926,8 @@ fbdev_backend_create(struct weston_compositor *compositor, compositor->launcher = weston_launcher_connect(compositor, param->tty, seat_id, false); if (!compositor->launcher) { - weston_log("fatal: fbdev backend should be run using " - "weston-launch binary, or your system should " - "provide the logind D-Bus API.\n"); + weston_log("fatal: your system should either provide the " + "logind D-Bus API, or use seatd.\n"); goto out_udev; } diff --git a/libweston/launcher-direct.c b/libweston/launcher-direct.c deleted file mode 100644 index c04ba857..00000000 --- a/libweston/launcher-direct.c +++ /dev/null @@ -1,370 +0,0 @@ -/* - * Copyright © 2012 Benjamin Franzke - * Copyright © 2013 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "config.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "launcher-impl.h" - -#define DRM_MAJOR 226 - -#ifndef KDSKBMUTE -#define KDSKBMUTE 0x4B51 -#endif - -/* major()/minor() */ -#ifdef MAJOR_IN_MKDEV -#include -#endif -#ifdef MAJOR_IN_SYSMACROS -#include -#endif - -#ifdef BUILD_DRM_COMPOSITOR - -#include - -static inline int -is_drm_master(int drm_fd) -{ - drm_magic_t magic; - - return drmGetMagic(drm_fd, &magic) == 0 && - drmAuthMagic(drm_fd, magic) == 0; -} - -#else - -static inline int -drmDropMaster(int drm_fd) -{ - return 0; -} - -static inline int -drmSetMaster(int drm_fd) -{ - return 0; -} - -static inline int -is_drm_master(int drm_fd) -{ - return 0; -} - -#endif - -struct launcher_direct { - struct weston_launcher base; - struct weston_compositor *compositor; - int kb_mode, tty, drm_fd; - struct wl_event_source *vt_source; -}; - -static int -vt_handler(int signal_number, void *data) -{ - struct launcher_direct *launcher = data; - struct weston_compositor *compositor = launcher->compositor; - - if (compositor->session_active) { - compositor->session_active = false; - wl_signal_emit(&compositor->session_signal, compositor); - drmDropMaster(launcher->drm_fd); - ioctl(launcher->tty, VT_RELDISP, 1); - } else { - ioctl(launcher->tty, VT_RELDISP, VT_ACKACQ); - drmSetMaster(launcher->drm_fd); - compositor->session_active = true; - wl_signal_emit(&compositor->session_signal, compositor); - } - - return 1; -} - -static int -setup_tty(struct launcher_direct *launcher, int tty) -{ - struct wl_event_loop *loop; - struct vt_mode mode = { 0 }; - struct stat buf; - char tty_device[32] =""; - int ret, kd_mode; - - if (geteuid() != 0) - return -1; - - if (tty == 0) { - launcher->tty = dup(tty); - if (launcher->tty == -1) { - weston_log("couldn't dup stdin: %s\n", - strerror(errno)); - return -1; - } - } else { - snprintf(tty_device, sizeof tty_device, "/dev/tty%d", tty); - launcher->tty = open(tty_device, O_RDWR | O_CLOEXEC); - if (launcher->tty == -1) { - weston_log("couldn't open tty %s: %s\n", tty_device, - strerror(errno)); - return -1; - } - } - - if (fstat(launcher->tty, &buf) == -1 || - major(buf.st_rdev) != TTY_MAJOR || minor(buf.st_rdev) == 0) { - weston_log("%s not a vt\n", tty_device); - weston_log("if running weston from ssh, " - "use --tty to specify a tty\n"); - goto err_close; - } - - ret = ioctl(launcher->tty, KDGETMODE, &kd_mode); - if (ret) { - weston_log("failed to get VT mode: %s\n", strerror(errno)); - return -1; - } - if (kd_mode != KD_TEXT) { - weston_log("%s is already in graphics mode, " - "is another display server running?\n", tty_device); - } - - ioctl(launcher->tty, VT_ACTIVATE, minor(buf.st_rdev)); - ioctl(launcher->tty, VT_WAITACTIVE, minor(buf.st_rdev)); - - if (ioctl(launcher->tty, KDGKBMODE, &launcher->kb_mode)) { - weston_log("failed to read keyboard mode: %s\n", - strerror(errno)); - goto err_close; - } - - if (ioctl(launcher->tty, KDSKBMUTE, 1) && - ioctl(launcher->tty, KDSKBMODE, K_OFF)) { - weston_log("failed to set K_OFF keyboard mode: %s\n", - strerror(errno)); - goto err_close; - } - - ret = ioctl(launcher->tty, KDSETMODE, KD_GRAPHICS); - if (ret) { - weston_log("failed to set KD_GRAPHICS mode on tty: %s\n", - strerror(errno)); - goto err_close; - } - - /* - * SIGRTMIN is used as global VT-acquire+release signal. Note that - * SIGRT* must be tested on runtime, as their exact values are not - * known at compile-time. POSIX requires 32 of them to be available. - */ - if (SIGRTMIN > SIGRTMAX) { - weston_log("not enough RT signals available: %u-%u\n", - SIGRTMIN, SIGRTMAX); - ret = -EINVAL; - goto err_close; - } - - mode.mode = VT_PROCESS; - mode.relsig = SIGRTMIN; - mode.acqsig = SIGRTMIN; - if (ioctl(launcher->tty, VT_SETMODE, &mode) < 0) { - weston_log("failed to take control of vt handling\n"); - goto err_close; - } - - loop = wl_display_get_event_loop(launcher->compositor->wl_display); - launcher->vt_source = - wl_event_loop_add_signal(loop, SIGRTMIN, vt_handler, launcher); - if (!launcher->vt_source) { - weston_log("failed to add SIGRTMIN signal\n"); - goto err_close; - } - - return 0; - - err_close: - close(launcher->tty); - return -1; -} - -static int -launcher_direct_open(struct weston_launcher *launcher_base, const char *path, int flags) -{ - struct launcher_direct *launcher = wl_container_of(launcher_base, launcher, base); - struct stat s; - int fd; - - fd = open(path, flags | O_CLOEXEC); - if (fd == -1) { - weston_log("couldn't open: %s! error=%s\n", path, strerror(errno)); - return -1; - } - - if (geteuid() != 0) { - weston_log("WARNING! Succeeded opening %s as non-root user." - " This implies your device can be spied on.\n", - path); - } - - if (fstat(fd, &s) == -1) { - weston_log("couldn't fstat: %s! error=%s\n", path, strerror(errno)); - close(fd); - return -1; - } - - if (major(s.st_rdev) == DRM_MAJOR) { - launcher->drm_fd = fd; - if (!is_drm_master(fd)) { - weston_log("drm fd not master\n"); - close(fd); - return -1; - } - } - - return fd; -} - -static void -launcher_direct_close(struct weston_launcher *launcher_base, int fd) -{ - close(fd); -} - -static void -launcher_direct_restore(struct weston_launcher *launcher_base) -{ - struct launcher_direct *launcher = wl_container_of(launcher_base, launcher, base); - struct vt_mode mode = { 0 }; - - if (ioctl(launcher->tty, KDSKBMUTE, 0) && - ioctl(launcher->tty, KDSKBMODE, launcher->kb_mode)) - weston_log("failed to restore kb mode: %s\n", - strerror(errno)); - - if (ioctl(launcher->tty, KDSETMODE, KD_TEXT)) - weston_log("failed to set KD_TEXT mode on tty: %s\n", - strerror(errno)); - - /* We have to drop master before we switch the VT back in - * VT_AUTO, so we don't risk switching to a VT with another - * display server, that will then fail to set drm master. */ - drmDropMaster(launcher->drm_fd); - - mode.mode = VT_AUTO; - if (ioctl(launcher->tty, VT_SETMODE, &mode) < 0) - weston_log("could not reset vt handling! error=%s\n", - strerror(errno)); -} - -static int -launcher_direct_activate_vt(struct weston_launcher *launcher_base, int vt) -{ - struct launcher_direct *launcher = wl_container_of(launcher_base, launcher, base); - return ioctl(launcher->tty, VT_ACTIVATE, vt); -} - -static int -launcher_direct_connect(struct weston_launcher **out, struct weston_compositor *compositor, - int tty, const char *seat_id, bool sync_drm) -{ - struct launcher_direct *launcher; - struct stat buf; - - launcher = zalloc(sizeof(*launcher)); - if (launcher == NULL) { - weston_log("failed to alloc for launcher\n"); - return -ENOMEM; - } - - launcher->base.iface = &launcher_direct_iface; - launcher->compositor = compositor; - - /* Checking the existance of /dev/tty0 and verifying it's a TTY - * device, as kernels compiled with CONFIG_VT=0 do not create these - * devices. */ - if (stat("/dev/tty0", &buf) == 0 && - strcmp("seat0", seat_id) == 0 && major(buf.st_rdev) == TTY_MAJOR) { - if (setup_tty(launcher, tty) == -1) { - free(launcher); - return -1; - } - } else { - launcher->tty = -1; - } - - * (struct launcher_direct **) out = launcher; - return 0; -} - -static void -launcher_direct_destroy(struct weston_launcher *launcher_base) -{ - struct launcher_direct *launcher = wl_container_of(launcher_base, launcher, base); - - if (launcher->tty >= 0) { - launcher_direct_restore(&launcher->base); - wl_event_source_remove(launcher->vt_source); - close(launcher->tty); - } - - free(launcher); -} - -static int -launcher_direct_get_vt(struct weston_launcher *base) -{ - struct launcher_direct *launcher = wl_container_of(base, launcher, base); - struct stat s; - if (fstat(launcher->tty, &s) < 0) { - weston_log("couldn't fstat launcher tty: %s\n", strerror(errno)); - return -1; - } - - return minor(s.st_rdev); -} - -const struct launcher_interface launcher_direct_iface = { - .name = "direct", - .connect = launcher_direct_connect, - .destroy = launcher_direct_destroy, - .open = launcher_direct_open, - .close = launcher_direct_close, - .activate_vt = launcher_direct_activate_vt, - .get_vt = launcher_direct_get_vt, -}; diff --git a/libweston/launcher-impl.h b/libweston/launcher-impl.h index 6bcbbc54..26038998 100644 --- a/libweston/launcher-impl.h +++ b/libweston/launcher-impl.h @@ -47,5 +47,3 @@ struct weston_launcher { extern const struct launcher_interface launcher_libseat_iface; extern const struct launcher_interface launcher_logind_iface; -extern const struct launcher_interface launcher_weston_launch_iface; -extern const struct launcher_interface launcher_direct_iface; diff --git a/libweston/launcher-util.c b/libweston/launcher-util.c index b2219b68..9f5ce243 100644 --- a/libweston/launcher-util.c +++ b/libweston/launcher-util.c @@ -43,8 +43,6 @@ static const struct launcher_interface *ifaces[] = { #ifdef HAVE_SYSTEMD_LOGIN &launcher_logind_iface, #endif - &launcher_weston_launch_iface, - &launcher_direct_iface, NULL, }; diff --git a/libweston/launcher-weston-launch.c b/libweston/launcher-weston-launch.c deleted file mode 100644 index 109ac486..00000000 --- a/libweston/launcher-weston-launch.c +++ /dev/null @@ -1,396 +0,0 @@ -/* - * Copyright © 2012 Benjamin Franzke - * Copyright © 2013 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "config.h" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include "weston-launch.h" -#include "launcher-impl.h" -#include "shared/string-helpers.h" - -#define DRM_MAJOR 226 - -#ifndef KDSKBMUTE -#define KDSKBMUTE 0x4B51 -#endif - -#ifdef BUILD_DRM_COMPOSITOR - -#include - -#else - -static inline int -drmDropMaster(int drm_fd) -{ - return 0; -} - -static inline int -drmSetMaster(int drm_fd) -{ - return 0; -} - -#endif - -/* major()/minor() */ -#ifdef MAJOR_IN_MKDEV -#include -#endif -#ifdef MAJOR_IN_SYSMACROS -#include -#endif - -union cmsg_data { unsigned char b[4]; int fd; }; - -struct launcher_weston_launch { - struct weston_launcher base; - struct weston_compositor *compositor; - int fd; - struct wl_event_source *source; - - int kb_mode, tty, drm_fd; - int deferred_deactivate; -}; - -static ssize_t -launcher_weston_launch_send(int sockfd, void *buf, size_t buflen) -{ - ssize_t len; - - do { - len = send(sockfd, buf, buflen, 0); - } while (len < 0 && errno == EINTR); - - return len; -} - -static void -handle_deactivate(struct launcher_weston_launch *launcher) -{ - int reply; - - launcher->compositor->session_active = false; - wl_signal_emit(&launcher->compositor->session_signal, - launcher->compositor); - - reply = WESTON_LAUNCHER_DEACTIVATE_DONE; - launcher_weston_launch_send(launcher->fd, &reply, sizeof reply); -} - -static void -idle_deactivate(void *data) -{ - struct launcher_weston_launch *launcher = data; - - if (launcher->deferred_deactivate) { - launcher->deferred_deactivate = 0; - handle_deactivate((struct launcher_weston_launch*)data); - } -} - -static int -launcher_weston_launch_open(struct weston_launcher *launcher_base, - const char *path, int flags) -{ - struct launcher_weston_launch *launcher = wl_container_of(launcher_base, launcher, base); - int n; - struct msghdr msg; - struct cmsghdr *cmsg; - struct iovec iov; - union cmsg_data *data; - char control[CMSG_SPACE(sizeof data->fd)]; - ssize_t len; - struct weston_launcher_open *message; - struct { int id; int ret; } event; - - n = sizeof(*message) + strlen(path) + 1; - message = malloc(n); - if (!message) - return -1; - - message->header.opcode = WESTON_LAUNCHER_OPEN; - message->flags = flags; - strcpy(message->path, path); - - launcher_weston_launch_send(launcher->fd, message, n); - free(message); - - memset(&msg, 0, sizeof msg); - iov.iov_base = &event; - iov.iov_len = sizeof event; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = control; - - while (1) { - msg.msg_controllen = sizeof control; - - do { - len = recvmsg(launcher->fd, &msg, MSG_CMSG_CLOEXEC); - } while (len < 0 && errno == EINTR); - - // Only OPEN_REPLY and up to one DEACTIVATE message should be possible here - if ((len == sizeof event) && (event.id == WESTON_LAUNCHER_OPEN_REPLY)) - break; - - if ((len == sizeof event.id) && (event.id == WESTON_LAUNCHER_DEACTIVATE) && (launcher->deferred_deactivate == 0)) { - wl_event_loop_add_idle(wl_display_get_event_loop(launcher->compositor->wl_display), idle_deactivate, launcher); - launcher->deferred_deactivate = 1; - } else { - weston_log("unexpected event %d (len=%zd) from weston-launch\n", event.id, len); - return -1; - } - } - - if (event.ret < 0) - return -1; - - cmsg = CMSG_FIRSTHDR(&msg); - if (!cmsg || - cmsg->cmsg_level != SOL_SOCKET || - cmsg->cmsg_type != SCM_RIGHTS) { - fprintf(stderr, "invalid control message\n"); - return -1; - } - - data = (union cmsg_data *) CMSG_DATA(cmsg); - if (data->fd == -1) { - fprintf(stderr, "missing drm fd in socket request\n"); - return -1; - } - - return data->fd; -} - -static void -launcher_weston_launch_close(struct weston_launcher *launcher_base, int fd) -{ - close(fd); -} - -static void -launcher_weston_launch_restore(struct weston_launcher *launcher_base) -{ - struct launcher_weston_launch *launcher = wl_container_of(launcher_base, launcher, base); - struct vt_mode mode = { 0 }; - - if (ioctl(launcher->tty, KDSKBMUTE, 0) && - ioctl(launcher->tty, KDSKBMODE, launcher->kb_mode)) - weston_log("failed to restore kb mode: %s\n", - strerror(errno)); - - if (ioctl(launcher->tty, KDSETMODE, KD_TEXT)) - weston_log("failed to set KD_TEXT mode on tty: %s\n", - strerror(errno)); - - /* We have to drop master before we switch the VT back in - * VT_AUTO, so we don't risk switching to a VT with another - * display server, that will then fail to set drm master. */ - drmDropMaster(launcher->drm_fd); - - mode.mode = VT_AUTO; - if (ioctl(launcher->tty, VT_SETMODE, &mode) < 0) - weston_log("could not reset vt handling\n"); -} - -static int -launcher_weston_launch_data(int fd, uint32_t mask, void *data) -{ - struct launcher_weston_launch *launcher = data; - int len, ret; - - if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR)) { - weston_log("launcher socket closed, exiting\n"); - /* Normally the weston-launch will reset the tty, but - * in this case it died or something, so do it here so - * we don't end up with a stuck vt. */ - launcher_weston_launch_restore(&launcher->base); - exit(-1); - } - - if (launcher->deferred_deactivate) { - launcher->deferred_deactivate = 0; - handle_deactivate(launcher); - return 1; - } - - do { - len = recv(launcher->fd, &ret, sizeof ret, 0); - } while (len < 0 && errno == EINTR); - - switch (ret) { - case WESTON_LAUNCHER_ACTIVATE: - launcher->compositor->session_active = true; - wl_signal_emit(&launcher->compositor->session_signal, - launcher->compositor); - break; - case WESTON_LAUNCHER_DEACTIVATE: - handle_deactivate(launcher); - break; - default: - weston_log("unexpected event from weston-launch\n"); - break; - } - - return 1; -} - -static int -launcher_weston_launch_activate_vt(struct weston_launcher *launcher_base, int vt) -{ - struct launcher_weston_launch *launcher = wl_container_of(launcher_base, launcher, base); - return ioctl(launcher->tty, VT_ACTIVATE, vt); -} - -static int -launcher_weston_environment_get_fd(const char *env) -{ - char *e; - int fd, flags; - - e = getenv(env); - if (!e || !safe_strtoint(e, &fd)) { - weston_log("could not get launcher fd from env\n"); - return -1; - } - - flags = fcntl(fd, F_GETFD); - if (flags == -1) { - weston_log("could not get fd flags!, env: %s, error: %s\n", - env, strerror(errno)); - return -1; - } - - fcntl(fd, F_SETFD, flags | FD_CLOEXEC); - unsetenv(env); - - return fd; -} - - -static int -launcher_weston_launch_connect(struct weston_launcher **out, struct weston_compositor *compositor, - int tty, const char *seat_id, bool sync_drm) -{ - struct launcher_weston_launch *launcher; - struct wl_event_loop *loop; - - launcher = malloc(sizeof *launcher); - if (launcher == NULL) - return -ENOMEM; - - launcher->base.iface = &launcher_weston_launch_iface; - launcher->compositor = compositor; - launcher->drm_fd = -1; - launcher->deferred_deactivate = 0; - launcher->fd = launcher_weston_environment_get_fd("WESTON_LAUNCHER_SOCK"); - if (launcher->fd == -1) { - free(launcher); - return -1; - } - - launcher->tty = launcher_weston_environment_get_fd("WESTON_TTY_FD"); - /* We don't get a chance to read out the original kb - * mode for the tty, so just hard code K_UNICODE here - * in case we have to clean if weston-launch dies. */ - launcher->kb_mode = K_UNICODE; - - loop = wl_display_get_event_loop(compositor->wl_display); - launcher->source = wl_event_loop_add_fd(loop, launcher->fd, - WL_EVENT_READABLE, - launcher_weston_launch_data, - launcher); - if (launcher->source == NULL) { - free(launcher); - weston_log("failed to get weston-launcher socket fd event source\n"); - return -ENOMEM; - } - - * (struct launcher_weston_launch **) out = launcher; - - return 0; -} - -static void -launcher_weston_launch_destroy(struct weston_launcher *launcher_base) -{ - struct launcher_weston_launch *launcher = wl_container_of(launcher_base, launcher, base); - - if (launcher->fd != -1) { - close(launcher->fd); - wl_event_source_remove(launcher->source); - } else { - launcher_weston_launch_restore(&launcher->base); - } - - if (launcher->tty >= 0) - close(launcher->tty); - - free(launcher); -} - -static int -launcher_weston_launch_get_vt(struct weston_launcher *base) -{ - struct launcher_weston_launch *launcher = wl_container_of(base, launcher, base); - struct stat s; - if (fstat(launcher->tty, &s) < 0) { - weston_log("could not fstat launcher tty: %s\n", strerror(errno)); - return -1; - } - - return minor(s.st_rdev); -} - -const struct launcher_interface launcher_weston_launch_iface = { - .name = "weston_launch", - .connect = launcher_weston_launch_connect, - .destroy = launcher_weston_launch_destroy, - .open = launcher_weston_launch_open, - .close = launcher_weston_launch_close, - .activate_vt = launcher_weston_launch_activate_vt, - .get_vt = launcher_weston_launch_get_vt, -}; diff --git a/libweston/meson.build b/libweston/meson.build index e7afabbc..ca660e9a 100644 --- a/libweston/meson.build +++ b/libweston/meson.build @@ -143,9 +143,7 @@ pkgconfig.generate( ) srcs_session_helper = [ - 'launcher-direct.c', 'launcher-util.c', - 'launcher-weston-launch.c', ] deps_session_helper = [ dep_libweston_private_h ] @@ -222,25 +220,6 @@ dep_vertex_clipping = declare_dependency( include_directories: include_directories('.') ) -if get_option('deprecated-weston-launch') - warning('weston-launch is deprecated and will be removed in a future release. Please migrate to libseat and seatd-launch.') - dep_pam = cc.find_library('pam') - - if not cc.has_function('pam_open_session', dependencies: dep_pam) - error('pam_open_session not found for weston-launch') - endif - - executable( - 'weston-launch', - 'weston-launch.c', - dependencies: [dep_pam, systemd_dep, dep_libdrm], - include_directories: common_inc, - install: true - ) - - meson.add_install_script('echo', 'REMINDER: You are installing weston-launch, please make it setuid-root.') -endif - subdir('color-lcms') subdir('renderer-gl') subdir('backend-drm') diff --git a/libweston/weston-launch.c b/libweston/weston-launch.c deleted file mode 100644 index c43ed7b2..00000000 --- a/libweston/weston-launch.c +++ /dev/null @@ -1,917 +0,0 @@ -/* - * Copyright © 2012 Benjamin Franzke - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#ifdef HAVE_SYSTEMD_LOGIN -#include -#endif - -#include "weston-launch.h" - -#define DRM_MAJOR 226 - -#ifndef KDSKBMUTE -#define KDSKBMUTE 0x4B51 -#endif - -#ifndef EVIOCREVOKE -#define EVIOCREVOKE _IOW('E', 0x91, int) -#endif - -#define MAX_ARGV_SIZE 256 - -#ifdef BUILD_DRM_COMPOSITOR - -#include - -#else - -static inline int -drmDropMaster(int drm_fd) -{ - return 0; -} - -static inline int -drmSetMaster(int drm_fd) -{ - return 0; -} - -#endif - -/* major()/minor() */ -#ifdef MAJOR_IN_MKDEV -# include -#endif -#ifdef MAJOR_IN_SYSMACROS -# include -#endif - -struct weston_launch { - struct pam_conv pc; - pam_handle_t *ph; - int tty; - int ttynr; - int sock[2]; - int drm_fd; - int last_input_fd; - int kb_mode; - struct passwd *pw; - - int signalfd; - - pid_t child; - int verbose; - char *new_user; -}; - -union cmsg_data { unsigned char b[4]; int fd; }; - -static gid_t * -read_groups(int *ngroups) -{ - int n; - gid_t *groups; - - n = getgroups(0, NULL); - - if (n < 0) { - fprintf(stderr, "Unable to retrieve groups: %s\n", - strerror(errno)); - return NULL; - } - - groups = malloc(n * sizeof(gid_t)); - if (!groups) - return NULL; - - if (getgroups(n, groups) < 0) { - fprintf(stderr, "Unable to retrieve groups: %s\n", - strerror(errno)); - free(groups); - return NULL; - } - - *ngroups = n; - return groups; -} - -static bool -weston_launch_allowed(struct weston_launch *wl) -{ - struct group *gr; - gid_t *groups; - int ngroups; -#ifdef HAVE_SYSTEMD_LOGIN - char *session, *seat; - int err; -#endif - - if (getuid() == 0) - return true; - - gr = getgrnam("weston-launch"); - if (gr) { - groups = read_groups(&ngroups); - if (groups && ngroups > 0) { - while (ngroups--) { - if (groups[ngroups] == gr->gr_gid) { - free(groups); - return true; - } - } - free(groups); - } - } - -#ifdef HAVE_SYSTEMD_LOGIN - err = sd_pid_get_session(getpid(), &session); - if (err == 0 && session) { - if (sd_session_is_active(session) && - sd_session_get_seat(session, &seat) == 0) { - free(seat); - free(session); - return true; - } - free(session); - } -#endif - - return false; -} - -static int -pam_conversation_fn(int msg_count, - const struct pam_message **messages, - struct pam_response **responses, - void *user_data) -{ - return PAM_SUCCESS; -} - -static int -setup_pam(struct weston_launch *wl) -{ - int err; - - wl->pc.conv = pam_conversation_fn; - wl->pc.appdata_ptr = wl; - - err = pam_start("login", wl->pw->pw_name, &wl->pc, &wl->ph); - if (err != PAM_SUCCESS) { - fprintf(stderr, "failed to start pam transaction: %d: %s\n", - err, pam_strerror(wl->ph, err)); - return -1; - } - - err = pam_set_item(wl->ph, PAM_TTY, ttyname(wl->tty)); - if (err != PAM_SUCCESS) { - fprintf(stderr, "failed to set PAM_TTY item: %d: %s\n", - err, pam_strerror(wl->ph, err)); - return -1; - } - - err = pam_open_session(wl->ph, 0); - if (err != PAM_SUCCESS) { - fprintf(stderr, "failed to open pam session: %d: %s\n", - err, pam_strerror(wl->ph, err)); - return -1; - } - - return 0; -} - -static int -setup_launcher_socket(struct weston_launch *wl) -{ - if (socketpair(AF_LOCAL, SOCK_SEQPACKET, 0, wl->sock) < 0) { - fprintf(stderr, "weston: socketpair failed: %s\n", - strerror(errno)); - return -1; - } - - if (fcntl(wl->sock[0], F_SETFD, FD_CLOEXEC) < 0) { - fprintf(stderr, "weston: fcntl failed: %s\n", - strerror(errno)); - return -1; - } - - return 0; -} - -static int -setup_signals(struct weston_launch *wl) -{ - int ret; - sigset_t mask; - struct sigaction sa; - - memset(&sa, 0, sizeof sa); - sa.sa_handler = SIG_DFL; - sa.sa_flags = SA_NOCLDSTOP | SA_RESTART; - ret = sigaction(SIGCHLD, &sa, NULL); - assert(ret == 0); - - sa.sa_handler = SIG_IGN; - sa.sa_flags = 0; - sigaction(SIGHUP, &sa, NULL); - - ret = sigemptyset(&mask); - assert(ret == 0); - sigaddset(&mask, SIGCHLD); - sigaddset(&mask, SIGINT); - sigaddset(&mask, SIGTERM); - sigaddset(&mask, SIGUSR1); - sigaddset(&mask, SIGUSR2); - ret = sigprocmask(SIG_BLOCK, &mask, NULL); - assert(ret == 0); - - wl->signalfd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC); - if (wl->signalfd < 0) - return -errno; - - return 0; -} - -static void -setenv_fd(const char *env, int fd) -{ - char buf[32]; - - snprintf(buf, sizeof buf, "%d", fd); - setenv(env, buf, 1); -} - -static int -open_tty_by_number(int ttynr) -{ - int ret; - char filename[16]; - - ret = snprintf(filename, sizeof filename, "/dev/tty%d", ttynr); - if (ret < 0) - return -1; - - return open(filename, O_RDWR | O_NOCTTY); -} - -static int -send_reply(struct weston_launch *wl, int reply) -{ - int len; - - do { - len = send(wl->sock[0], &reply, sizeof reply, 0); - } while (len < 0 && errno == EINTR); - - return len; -} - -static int -handle_open(struct weston_launch *wl, struct msghdr *msg, ssize_t len) -{ - int fd = -1, ret = -1; - char control[CMSG_SPACE(sizeof(fd))]; - struct cmsghdr *cmsg; - struct stat s; - struct msghdr nmsg; - struct iovec iov; - struct weston_launcher_open *message; - union cmsg_data *data; - - message = msg->msg_iov->iov_base; - if ((size_t)len < sizeof(*message)) - goto err0; - - /* Ensure path is null-terminated */ - ((char *) message)[len-1] = '\0'; - - fd = open(message->path, message->flags); - if (fd < 0) { - fprintf(stderr, "Error opening device %s: %s\n", - message->path, strerror(errno)); - goto err0; - } - - if (fstat(fd, &s) < 0) { - close(fd); - fd = -1; - fprintf(stderr, "Failed to stat %s\n", message->path); - goto err0; - } - - if (major(s.st_rdev) != INPUT_MAJOR && - major(s.st_rdev) != DRM_MAJOR) { - close(fd); - fd = -1; - fprintf(stderr, "Device %s is not an input or drm device\n", - message->path); - goto err0; - } - -err0: - memset(&nmsg, 0, sizeof nmsg); - nmsg.msg_iov = &iov; - nmsg.msg_iovlen = 1; - if (fd != -1) { - nmsg.msg_control = control; - nmsg.msg_controllen = sizeof control; - cmsg = CMSG_FIRSTHDR(&nmsg); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); - data = (union cmsg_data *) CMSG_DATA(cmsg); - data->fd = fd; - nmsg.msg_controllen = cmsg->cmsg_len; - ret = 0; - } - struct { int reply_id; int ret; } reply_iov_data = { WESTON_LAUNCHER_OPEN_REPLY, ret }; - iov.iov_base = &reply_iov_data; - iov.iov_len = sizeof reply_iov_data; - - if (wl->verbose) - fprintf(stderr, "weston-launch: opened %s: ret: %d, fd: %d\n", - message->path, ret, fd); - do { - len = sendmsg(wl->sock[0], &nmsg, 0); - } while (len < 0 && errno == EINTR); - - if (len < 0) - return -1; - - if (fd != -1 && major(s.st_rdev) == DRM_MAJOR) - wl->drm_fd = fd; - if (fd != -1 && major(s.st_rdev) == INPUT_MAJOR && - wl->last_input_fd < fd) - wl->last_input_fd = fd; - - return 0; -} - -static void -close_input_fds(struct weston_launch *wl) -{ - struct stat s; - int fd; - - for (fd = 3; fd <= wl->last_input_fd; fd++) { - if (fstat(fd, &s) == 0 && major(s.st_rdev) == INPUT_MAJOR) { - /* EVIOCREVOKE may fail if the kernel doesn't - * support it, but all we can do is ignore it. */ - ioctl(fd, EVIOCREVOKE, 0); - close(fd); - } - } -} - -static int -handle_socket_msg(struct weston_launch *wl) -{ - char control[CMSG_SPACE(sizeof(int))]; - char buf[BUFSIZ]; - struct msghdr msg; - struct iovec iov; - int ret = -1; - ssize_t len; - struct weston_launcher_message *message; - - memset(&msg, 0, sizeof(msg)); - iov.iov_base = buf; - iov.iov_len = sizeof buf; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = control; - msg.msg_controllen = sizeof control; - - do { - len = recvmsg(wl->sock[0], &msg, 0); - } while (len < 0 && errno == EINTR); - - if (len < 1) - return -1; - - message = (void *) buf; - switch (message->opcode) { - case WESTON_LAUNCHER_OPEN: - ret = handle_open(wl, &msg, len); - break; - case WESTON_LAUNCHER_DEACTIVATE_DONE: - close_input_fds(wl); - drmDropMaster(wl->drm_fd); - ioctl(wl->tty, VT_RELDISP, 1); - break; - } - - return ret; -} - -static void -quit(struct weston_launch *wl, int status) -{ - struct vt_mode mode = { 0 }; - int err; - int oldtty; - - close(wl->signalfd); - close(wl->sock[0]); - - if (wl->new_user) { - err = pam_close_session(wl->ph, 0); - if (err) - fprintf(stderr, "pam_close_session failed: %d: %s\n", - err, pam_strerror(wl->ph, err)); - pam_end(wl->ph, err); - } - - /* - * Get a fresh handle to the tty as the previous one is in - * hang-up state since weston (the controlling process for - * the tty) exit at this point. Reopen before closing the - * file descriptor to avoid a potential race condition. - * - * A similar fix exists in logind, see: - * https://github.com/systemd/systemd/pull/990 - */ - oldtty = wl->tty; - wl->tty = open_tty_by_number(wl->ttynr); - close(oldtty); - - if (ioctl(wl->tty, KDSKBMUTE, 0) && - ioctl(wl->tty, KDSKBMODE, wl->kb_mode)) - fprintf(stderr, "failed to restore keyboard mode: %s\n", - strerror(errno)); - - if (ioctl(wl->tty, KDSETMODE, KD_TEXT)) - fprintf(stderr, "failed to set KD_TEXT mode on tty: %s\n", - strerror(errno)); - - /* We have to drop master before we switch the VT back in - * VT_AUTO, so we don't risk switching to a VT with another - * display server, that will then fail to set drm master. */ - drmDropMaster(wl->drm_fd); - - mode.mode = VT_AUTO; - if (ioctl(wl->tty, VT_SETMODE, &mode) < 0) - fprintf(stderr, "could not reset vt handling\n"); - - if (wl->tty != STDIN_FILENO) - close(wl->tty); - - exit(status); -} - -static int -handle_signal(struct weston_launch *wl) -{ - struct signalfd_siginfo sig; - int pid, status, ret; - - if (read(wl->signalfd, &sig, sizeof sig) != sizeof sig) { - fprintf(stderr, "weston: reading signalfd failed: %s\n", - strerror(errno)); - return -1; - } - - switch (sig.ssi_signo) { - case SIGCHLD: - pid = waitpid(-1, &status, 0); - if (pid == wl->child) { - wl->child = 0; - if (WIFEXITED(status)) - ret = WEXITSTATUS(status); - else if (WIFSIGNALED(status)) - /* - * If weston dies because of signal N, we - * return 10+N. This is distinct from - * weston-launch dying because of a signal - * (128+N). - */ - ret = 10 + WTERMSIG(status); - else - ret = 0; - quit(wl, ret); - } - break; - case SIGTERM: - case SIGINT: - if (!wl->child) - break; - - if (wl->verbose) - fprintf(stderr, "weston-launch: sending %s to pid %d\n", - strsignal(sig.ssi_signo), wl->child); - - kill(wl->child, sig.ssi_signo); - break; - case SIGUSR1: - send_reply(wl, WESTON_LAUNCHER_DEACTIVATE); - break; - case SIGUSR2: - ioctl(wl->tty, VT_RELDISP, VT_ACKACQ); - drmSetMaster(wl->drm_fd); - send_reply(wl, WESTON_LAUNCHER_ACTIVATE); - break; - default: - return -1; - } - - return 0; -} - -static int -setup_tty(struct weston_launch *wl, const char *tty) -{ - struct stat buf; - struct vt_mode mode = { 0 }; - char *t; - - if (!wl->new_user) { - wl->tty = STDIN_FILENO; - } else if (tty) { - t = ttyname(STDIN_FILENO); - if (t && strcmp(t, tty) == 0) - wl->tty = STDIN_FILENO; - else - wl->tty = open(tty, O_RDWR | O_NOCTTY); - } else { - int tty0 = open("/dev/tty0", O_WRONLY | O_CLOEXEC); - - if (tty0 < 0) { - fprintf(stderr, "weston: could not open tty0: %s\n", - strerror(errno)); - return -1; - } - - if (ioctl(tty0, VT_OPENQRY, &wl->ttynr) < 0 || wl->ttynr == -1) - { - fprintf(stderr, "weston: failed to find non-opened console: %s\n", - strerror(errno)); - return -1; - } - - wl->tty = open_tty_by_number(wl->ttynr); - close(tty0); - } - - if (wl->tty < 0) { - fprintf(stderr, "weston: failed to open tty: %s\n", - strerror(errno)); - return -1; - } - - if (fstat(wl->tty, &buf) == -1 || - major(buf.st_rdev) != TTY_MAJOR || minor(buf.st_rdev) == 0) { - fprintf(stderr, "weston: weston-launch must be run from a virtual terminal\n"); - return -1; - } - - if (!wl->new_user || tty) { - if (fstat(wl->tty, &buf) < 0) { - fprintf(stderr, "weston: stat %s failed: %s\n", tty, - strerror(errno)); - return -1; - } - - if (major(buf.st_rdev) != TTY_MAJOR) { - fprintf(stderr, - "weston: invalid tty device: %s\n", tty); - return -1; - } - - wl->ttynr = minor(buf.st_rdev); - } - - if (ioctl(wl->tty, VT_ACTIVATE, wl->ttynr) < 0) { - fprintf(stderr, - "weston: failed to activate VT: %s\n", - strerror(errno)); - return -1; - } - - if (ioctl(wl->tty, VT_WAITACTIVE, wl->ttynr) < 0) { - fprintf(stderr, - "weston: failed to wait for VT to be active: %s\n", - strerror(errno)); - return -1; - } - - if (ioctl(wl->tty, KDGKBMODE, &wl->kb_mode)) { - fprintf(stderr, - "weston: failed to get current keyboard mode: %s\n", - strerror(errno)); - return -1; - } - - if (ioctl(wl->tty, KDSKBMUTE, 1) && - ioctl(wl->tty, KDSKBMODE, K_OFF)) { - fprintf(stderr, - "weston: failed to set K_OFF keyboard mode: %s\n", - strerror(errno)); - return -1; - } - - if (ioctl(wl->tty, KDSETMODE, KD_GRAPHICS)) { - fprintf(stderr, - "weston: failed to set KD_GRAPHICS mode on tty: %s\n", - strerror(errno)); - return -1; - } - - mode.mode = VT_PROCESS; - mode.relsig = SIGUSR1; - mode.acqsig = SIGUSR2; - if (ioctl(wl->tty, VT_SETMODE, &mode) < 0) { - fprintf(stderr, - "weston: failed to take control of vt handling %s\n", - strerror(errno)); - return -1; - } - - return 0; -} - -static int -setup_session(struct weston_launch *wl, char **child_argv) -{ - char **env; - char *term; - int i; - - if (wl->tty != STDIN_FILENO) { - if (setsid() < 0) { - fprintf(stderr, "weston: setsid failed %s\n", - strerror(errno)); - exit(EXIT_FAILURE); - } - if (ioctl(wl->tty, TIOCSCTTY, 0) < 0) { - fprintf(stderr, "TIOCSCTTY failed - tty is in use %s\n", - strerror(errno)); - exit(EXIT_FAILURE); - } - } - - term = getenv("TERM"); - clearenv(); - if (term) - setenv("TERM", term, 1); - setenv("USER", wl->pw->pw_name, 1); - setenv("LOGNAME", wl->pw->pw_name, 1); - setenv("HOME", wl->pw->pw_dir, 1); - setenv("SHELL", wl->pw->pw_shell, 1); - - env = pam_getenvlist(wl->ph); - if (env) { - for (i = 0; env[i]; ++i) { - if (putenv(env[i]) != 0) - fprintf(stderr, "putenv %s failed\n", env[i]); - } - free(env); - } - - /* - * We open a new session, so it makes sense - * to run a new login shell - */ - child_argv[0] = "/bin/sh"; - child_argv[1] = "-l"; - child_argv[2] = "-c"; - child_argv[3] = "exec " BINDIR "/weston \"$@\""; - child_argv[4] = "weston"; - return 5; -} - -static void -drop_privileges(struct weston_launch *wl) -{ - if (setgid(wl->pw->pw_gid) < 0 || -#ifdef HAVE_INITGROUPS - initgroups(wl->pw->pw_name, wl->pw->pw_gid) < 0 || -#endif - setuid(wl->pw->pw_uid) < 0) { - fprintf(stderr, "weston: dropping privileges failed %s\n", - strerror(errno)); - exit(EXIT_FAILURE); - } -} - -static void -launch_compositor(struct weston_launch *wl, int argc, char *argv[]) -{ - char *child_argv[MAX_ARGV_SIZE]; - sigset_t mask; - int o, i; - - if (wl->verbose) - printf("weston-launch: spawned weston with pid: %d\n", getpid()); - if (wl->new_user) { - o = setup_session(wl, child_argv); - } else { - child_argv[0] = BINDIR "/weston"; - o = 1; - } - for (i = 0; i < argc; ++i) - child_argv[o + i] = argv[i]; - child_argv[o + i] = NULL; - - if (geteuid() == 0) - drop_privileges(wl); - - setenv_fd("WESTON_TTY_FD", wl->tty); - setenv_fd("WESTON_LAUNCHER_SOCK", wl->sock[1]); - - unsetenv("DISPLAY"); - - /* Do not give our signal mask to the new process. */ - sigemptyset(&mask); - sigaddset(&mask, SIGTERM); - sigaddset(&mask, SIGCHLD); - sigaddset(&mask, SIGINT); - sigprocmask(SIG_UNBLOCK, &mask, NULL); - - - execv(child_argv[0], child_argv); - fprintf(stderr, "weston: exec failed: %s\n", strerror(errno)); - exit(EXIT_FAILURE); -} - -static void -help(const char *name) -{ - fprintf(stderr, "Usage: %s [args...] [-- [weston args..]]\n", name); - fprintf(stderr, " -u, --user Start session as specified username,\n" - " e.g. -u joe, requires root.\n"); - fprintf(stderr, " -t, --tty Start session on alternative tty,\n" - " e.g. -t /dev/tty4, requires -u option.\n"); - fprintf(stderr, " -v, --verbose Be verbose\n"); - fprintf(stderr, " -h, --help Display this help message\n"); -} - -int -main(int argc, char *argv[]) -{ - struct weston_launch wl; - int i, c; - char *tty = NULL; - struct option opts[] = { - { "user", required_argument, NULL, 'u' }, - { "tty", required_argument, NULL, 't' }, - { "verbose", no_argument, NULL, 'v' }, - { "help", no_argument, NULL, 'h' }, - { 0, 0, NULL, 0 } - }; - - memset(&wl, 0, sizeof wl); - - while ((c = getopt_long(argc, argv, "u:t:vh", opts, &i)) != -1) { - switch (c) { - case 'u': - wl.new_user = optarg; - if (getuid() != 0) { - fprintf(stderr, "weston: Permission denied. -u allowed for root only\n"); - exit(EXIT_FAILURE); - } - break; - case 't': - tty = optarg; - break; - case 'v': - wl.verbose = 1; - break; - case 'h': - help("weston-launch"); - exit(EXIT_FAILURE); - default: - exit(EXIT_FAILURE); - } - } - - if ((argc - optind) > (MAX_ARGV_SIZE - 6)) { - fprintf(stderr, - "weston: Too many arguments to pass to weston: %s\n", - strerror(E2BIG)); - exit(EXIT_FAILURE); - } - - if (tty && !wl.new_user) { - fprintf(stderr, "weston: -t/--tty option requires -u/--user option as well\n"); - exit(EXIT_FAILURE); - } - - if (wl.new_user) - wl.pw = getpwnam(wl.new_user); - else - wl.pw = getpwuid(getuid()); - if (wl.pw == NULL) { - fprintf(stderr, "weston: failed to get username: %s\n", - strerror(errno)); - exit(EXIT_FAILURE); - } - - if (!weston_launch_allowed(&wl)) { - fprintf(stderr, "Permission denied. You should either:\n" -#ifdef HAVE_SYSTEMD_LOGIN - " - run from an active and local (systemd) session.\n" -#else - " - enable systemd session support for weston-launch.\n" -#endif - " - or add yourself to the 'weston-launch' group.\n"); - exit(EXIT_FAILURE); - } - - if (setup_tty(&wl, tty) < 0) - exit(EXIT_FAILURE); - - if (wl.new_user && setup_pam(&wl) < 0) - exit(EXIT_FAILURE); - - if (setup_launcher_socket(&wl) < 0) - exit(EXIT_FAILURE); - - if (setup_signals(&wl) < 0) - exit(EXIT_FAILURE); - - wl.child = fork(); - if (wl.child == -1) { - fprintf(stderr, "weston: fork failed %s\n", strerror(errno)); - exit(EXIT_FAILURE); - } - - if (wl.child == 0) - launch_compositor(&wl, argc - optind, argv + optind); - - close(wl.sock[1]); - - while (1) { - struct pollfd fds[2]; - int n; - - fds[0].fd = wl.sock[0]; - fds[0].events = POLLIN; - fds[1].fd = wl.signalfd; - fds[1].events = POLLIN; - - n = poll(fds, 2, -1); - if (n < 0) { - fprintf(stderr, "poll failed: %s\n", strerror(errno)); - return -1; - } - if (fds[0].revents & POLLIN) - handle_socket_msg(&wl); - if (fds[1].revents) - handle_signal(&wl); - } - - return 0; -} diff --git a/libweston/weston-launch.h b/libweston/weston-launch.h deleted file mode 100644 index 72954019..00000000 --- a/libweston/weston-launch.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright © 2012 Benjamin Franzke - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _WESTON_LAUNCH_H_ -#define _WESTON_LAUNCH_H_ - -enum weston_launcher_opcode { - WESTON_LAUNCHER_OPEN, -}; - -enum weston_launcher_event { - WESTON_LAUNCHER_ACTIVATE, - WESTON_LAUNCHER_DEACTIVATE, - WESTON_LAUNCHER_DEACTIVATE_DONE, - // This event is followed by an fd handle - WESTON_LAUNCHER_OPEN_REPLY, -}; - -struct weston_launcher_message { - int opcode; -}; - -struct weston_launcher_open { - struct weston_launcher_message header; - int flags; - char path[0]; -}; - -#endif diff --git a/man/weston-bindings.man b/man/weston-bindings.man index fac38be0..2e47ccc9 100644 --- a/man/weston-bindings.man +++ b/man/weston-bindings.man @@ -165,7 +165,6 @@ Enable repaint debugging for pixman: .SH "SEE ALSO" .BR weston (1), -.BR weston-launch (1), .BR weston-drm (7), .BR weston.ini (5), .BR xkeyboard-config (7) diff --git a/man/weston-drm.man b/man/weston-drm.man index 01a336e1..ced4f106 100644 --- a/man/weston-drm.man +++ b/man/weston-drm.man @@ -2,8 +2,6 @@ .SH NAME weston-drm \- the DRM backend for Weston .SH SYNOPSIS -.B weston-launch -.LP .B weston --backend=drm-backend.so . .\" *************************************************************** @@ -33,19 +31,6 @@ the first DRM device returned by .BR udev (7). Combining multiple graphics devices is not supported yet. -The DRM backend relies on -.B weston-launch -for managing input device access and DRM master status, so that -.B weston -can be run without root privileges. On switching away from the -virtual terminal (VT) hosting Weston, all input devices are closed and -the DRM master capability is dropped, so that other servers, -including -.BR Xorg (1), -can run on other VTs. On switching back to Weston's VT, input devices -and DRM master are re-acquired through the parent process -.BR weston-launch . - The DRM backend also supports virtual outputs that are transmitted over an RTP session as a series of JPEG images (RTP payload type 26) to a remote client. Virtual outputs are configured in the @@ -218,23 +203,10 @@ The minimum libinput verbosity level to be printed to Weston's log. Valid values are .BR debug ", " info ", and " error ". Default is " info . .TP -.B WESTON_TTY_FD -The file descriptor (integer) of the opened tty where -.B weston -will run. Set by -.BR weston-launch . -.TP -.B WESTON_LAUNCHER_SOCK -The file descriptor (integer) where -.B weston-launch -is listening. Automatically set by -.BR weston-launch . -.TP .B XDG_SEAT The seat Weston will start on, unless overridden on the command line. . .\" *************************************************************** .SH "SEE ALSO" .BR weston (1) -.\".BR weston-launch (1), .\".BR weston.ini (5) diff --git a/man/weston.ini.man b/man/weston.ini.man index 9f97a90c..98403e48 100644 --- a/man/weston.ini.man +++ b/man/weston.ini.man @@ -482,10 +482,7 @@ currently only recognized by the drm and x11 backends. .BI "name=" name sets a name for the output (string). The backend uses the name to identify the output. All X11 output names start with a letter X. All -Wayland output names start with the letters WL. The available -output names for DRM backend are listed in the -.B "weston-launch(1)" -output. +Wayland output names start with the letters WL. Examples of usage: .PP .RS 10 diff --git a/man/weston.man b/man/weston.man index c453a7d3..584bfb5a 100644 --- a/man/weston.man +++ b/man/weston.man @@ -17,14 +17,6 @@ Weston supports fundamentally different graphical user interface paradigms via shell plugins. Two plugins are provided: the desktop shell, and the tablet shell. -When weston is started as the first windowing system (i.e. not under X nor -under another Wayland server), it should be done with the command -.B weston-launch -to set up proper privileged access to devices. If your system supports -the logind D-Bus API and the support has been built into weston as well, -it is possible to start weston with just -.BR weston . - Weston also supports X clients via .BR XWayland ", see below." . @@ -361,9 +353,9 @@ http://wayland.freedesktop.org/ .\" *************************************************************** .SH EXAMPLES .IP "Launch Weston with the DRM backend on a VT" -weston-launch +weston .IP "Launch Weston with the DRM backend and XWayland support" -weston-launch -- --xwayland +weston --xwayland .IP "Launch Weston (wayland-1) nested in another Weston instance (wayland-0)" WAYLAND_DISPLAY=wayland-0 weston -Swayland-1 .IP "From an X terminal, launch Weston with the x11 backend" diff --git a/meson.build b/meson.build index d761903e..210bfa39 100644 --- a/meson.build +++ b/meson.build @@ -27,6 +27,10 @@ else error('Bad versions in meson.build: libweston_major is too low') endif +if not (get_option('launcher-logind') or get_option('launcher-libseat')) + error('At least one launcher must be enabled') +endif + dir_prefix = get_option('prefix') dir_bin = join_paths(dir_prefix, get_option('bindir')) dir_data = join_paths(dir_prefix, get_option('datadir')) diff --git a/meson_options.txt b/meson_options.txt index 8a527d74..453715cb 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -65,13 +65,6 @@ option( description: 'Weston renderer: EGL / OpenGL ES 2.x' ) -option( - 'deprecated-weston-launch', - type: 'boolean', - value: false, - description: 'Deprecated weston launcher for systems without logind' -) - option( 'xwayland', type: 'boolean', From 2c91c7025079ba5384eb37bc0b35863afcfeda8b Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Wed, 2 Mar 2022 10:51:15 -0600 Subject: [PATCH 069/609] launchers: Remove --tty option This doesn't work with any of the launchers we've kept. Remove the option and all the bits that handle it. Signed-off-by: Derek Foreman --- compositor/main.c | 4 ---- include/libweston/backend-drm.h | 5 +---- include/libweston/backend-fbdev.h | 3 +-- libweston/backend-drm/drm.c | 3 +-- libweston/backend-fbdev/fbdev.c | 3 +-- libweston/launcher-impl.h | 2 +- libweston/launcher-libseat.c | 2 +- libweston/launcher-logind.c | 7 +------ libweston/launcher-util.c | 4 ++-- libweston/launcher-util.h | 2 +- man/weston-drm.man | 5 ----- 11 files changed, 10 insertions(+), 30 deletions(-) diff --git a/compositor/main.c b/compositor/main.c index 87523517..6fda035a 100644 --- a/compositor/main.c +++ b/compositor/main.c @@ -689,7 +689,6 @@ usage(int error_code) fprintf(out, "Options for drm-backend.so:\n\n" " --seat=SEAT\t\tThe seat that weston should run on, instead of the seat defined in XDG_SEAT\n" - " --tty=TTY\t\tThe tty to use\n" " --drm-device=CARD\tThe DRM device to use, e.g. \"card0\".\n" " --use-pixman\t\tUse the pixman (CPU) renderer\n" " --current-mode\tPrefer current KMS mode over EDID preferred mode\n" @@ -699,7 +698,6 @@ usage(int error_code) #if defined(BUILD_FBDEV_COMPOSITOR) fprintf(out, "Options for fbdev-backend.so:\n\n" - " --tty=TTY\t\tThe tty to use\n" " --device=DEVICE\tThe framebuffer device to use\n" " --seat=SEAT\t\tThe seat that weston should run on, instead of the seat defined in XDG_SEAT\n" "\n"); @@ -2639,7 +2637,6 @@ load_drm_backend(struct weston_compositor *c, const struct weston_option options[] = { { WESTON_OPTION_STRING, "seat", 0, &config.seat_id }, - { WESTON_OPTION_INTEGER, "tty", 0, &config.tty }, { WESTON_OPTION_STRING, "drm-device", 0, &config.specific_device }, { WESTON_OPTION_BOOLEAN, "current-mode", 0, &wet->drm_use_current_mode }, { WESTON_OPTION_BOOLEAN, "use-pixman", 0, &config.use_pixman }, @@ -2882,7 +2879,6 @@ load_fbdev_backend(struct weston_compositor *c, int ret = 0; const struct weston_option fbdev_options[] = { - { WESTON_OPTION_INTEGER, "tty", 0, &config.tty }, { WESTON_OPTION_STRING, "device", 0, &config.device }, { WESTON_OPTION_STRING, "seat", 0, &config.seat_id }, }; diff --git a/include/libweston/backend-drm.h b/include/libweston/backend-drm.h index af2da4aa..f85aabdb 100644 --- a/include/libweston/backend-drm.h +++ b/include/libweston/backend-drm.h @@ -35,7 +35,7 @@ extern "C" { #endif -#define WESTON_DRM_BACKEND_CONFIG_VERSION 4 +#define WESTON_DRM_BACKEND_CONFIG_VERSION 5 struct libinput_device; @@ -171,9 +171,6 @@ weston_drm_virtual_output_get_api(struct weston_compositor *compositor) struct weston_drm_backend_config { struct weston_backend_config base; - /** The tty to be used. Set to 0 to use the current tty. */ - int tty; - /** Whether to use the pixman renderer instead of the OpenGL ES renderer. */ bool use_pixman; diff --git a/include/libweston/backend-fbdev.h b/include/libweston/backend-fbdev.h index 4dbdce72..3ed6b0a0 100644 --- a/include/libweston/backend-fbdev.h +++ b/include/libweston/backend-fbdev.h @@ -34,14 +34,13 @@ extern "C" { #include -#define WESTON_FBDEV_BACKEND_CONFIG_VERSION 2 +#define WESTON_FBDEV_BACKEND_CONFIG_VERSION 3 struct libinput_device; struct weston_fbdev_backend_config { struct weston_backend_config base; - int tty; char *device; /** Callback used to configure input devices. diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index 387184ef..83297cd5 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -3018,8 +3018,7 @@ drm_backend_create(struct weston_compositor *compositor, goto err_compositor; /* Check if we run drm-backend using a compatible launcher */ - compositor->launcher = weston_launcher_connect(compositor, config->tty, - seat_id, true); + compositor->launcher = weston_launcher_connect(compositor, seat_id, true); if (compositor->launcher == NULL) { weston_log("fatal: your system should either provide the " "logind D-Bus API, or use seatd.\n"); diff --git a/libweston/backend-fbdev/fbdev.c b/libweston/backend-fbdev/fbdev.c index 38a9b4c3..0ec5af0c 100644 --- a/libweston/backend-fbdev/fbdev.c +++ b/libweston/backend-fbdev/fbdev.c @@ -924,7 +924,7 @@ fbdev_backend_create(struct weston_compositor *compositor, wl_signal_add(&compositor->session_signal, &backend->session_listener); compositor->launcher = - weston_launcher_connect(compositor, param->tty, seat_id, false); + weston_launcher_connect(compositor, seat_id, false); if (!compositor->launcher) { weston_log("fatal: your system should either provide the " "logind D-Bus API, or use seatd.\n"); @@ -968,7 +968,6 @@ out_compositor: static void config_init_to_defaults(struct weston_fbdev_backend_config *config) { - config->tty = 0; /* default to current tty */ config->device = NULL; config->seat_id = NULL; } diff --git a/libweston/launcher-impl.h b/libweston/launcher-impl.h index 26038998..954dc6de 100644 --- a/libweston/launcher-impl.h +++ b/libweston/launcher-impl.h @@ -32,7 +32,7 @@ struct weston_launcher; struct launcher_interface { char *name; int (* connect) (struct weston_launcher **launcher_out, struct weston_compositor *compositor, - int tty, const char *seat_id, bool sync_drm); + const char *seat_id, bool sync_drm); void (* destroy) (struct weston_launcher *launcher); int (* open) (struct weston_launcher *launcher, const char *path, int flags); void (* close) (struct weston_launcher *launcher, int fd); diff --git a/libweston/launcher-libseat.c b/libweston/launcher-libseat.c index 6aa8ad0f..58e22d39 100644 --- a/libweston/launcher-libseat.c +++ b/libweston/launcher-libseat.c @@ -181,7 +181,7 @@ libseat_event(int fd, uint32_t mask, void *data) static int seat_open(struct weston_launcher **out, struct weston_compositor *compositor, - int tty, const char *seat_id, bool sync_drm) + const char *seat_id, bool sync_drm) { struct launcher_libseat *wl; struct wl_event_loop *event_loop; diff --git a/libweston/launcher-logind.c b/libweston/launcher-logind.c index 77f43815..7ebf906b 100644 --- a/libweston/launcher-logind.c +++ b/libweston/launcher-logind.c @@ -736,7 +736,7 @@ launcher_logind_get_session(char **session) static int launcher_logind_connect(struct weston_launcher **out, struct weston_compositor *compositor, - int tty, const char *seat_id, bool sync_drm) + const char *seat_id, bool sync_drm) { struct launcher_logind *wl; struct wl_event_loop *loop; @@ -784,11 +784,6 @@ launcher_logind_connect(struct weston_launcher **out, struct weston_compositor * if (r < 0) { weston_log("logind: session not running on a VT\n"); goto err_session; - } else if (tty > 0 && wl->vtnr != (unsigned int )tty) { - weston_log("logind: requested VT --tty=%d differs from real session VT %u\n", - tty, wl->vtnr); - r = -EINVAL; - goto err_session; } } else if (r < 0) { weston_log("logind: could not determine if seat %s has ttys or not", t); diff --git a/libweston/launcher-util.c b/libweston/launcher-util.c index 9f5ce243..b277606d 100644 --- a/libweston/launcher-util.c +++ b/libweston/launcher-util.c @@ -47,7 +47,7 @@ static const struct launcher_interface *ifaces[] = { }; WL_EXPORT struct weston_launcher * -weston_launcher_connect(struct weston_compositor *compositor, int tty, +weston_launcher_connect(struct weston_compositor *compositor, const char *seat_id, bool sync_drm) { const struct launcher_interface **it; @@ -57,7 +57,7 @@ weston_launcher_connect(struct weston_compositor *compositor, int tty, struct weston_launcher *launcher; weston_log("Trying %s launcher...\n", iface->name); - if (iface->connect(&launcher, compositor, tty, seat_id, sync_drm) == 0) + if (iface->connect(&launcher, compositor, seat_id, sync_drm) == 0) return launcher; } diff --git a/libweston/launcher-util.h b/libweston/launcher-util.h index dd7b7702..2ee7ceb5 100644 --- a/libweston/launcher-util.h +++ b/libweston/launcher-util.h @@ -33,7 +33,7 @@ struct weston_launcher; struct weston_launcher * -weston_launcher_connect(struct weston_compositor *compositor, int tty, +weston_launcher_connect(struct weston_compositor *compositor, const char *seat_id, bool sync_drm); void diff --git a/man/weston-drm.man b/man/weston-drm.man index ced4f106..d01860cf 100644 --- a/man/weston-drm.man +++ b/man/weston-drm.man @@ -186,11 +186,6 @@ Use graphics and input devices designated for seat instead of the seat defined in the environment variable .BR XDG_SEAT ". If neither is specified, seat0 will be assumed." .TP -\fB\-\-tty\fR=\fIx\fR -Launch Weston on tty -.I x -instead of using the current tty. -.TP .B \-\-continue\-without\-input Allow Weston to start without input devices. Used for testing purposes. . From 952a9516626a2f48eba2e9c33b212b36021648cf Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 4 Mar 2022 11:20:16 +0200 Subject: [PATCH 070/609] build: enable libseat support by default Now that launcher-direct and weston-launch are gone, libseat takes their place. Enable libseat support by default to give users a hint in case they miss either of those. People who used to get launcher-logind when libseat support was disabled will now be using logind through libseat. This should not cause any regressions, and if it does, we want to hear about them, because the separate logind-launcher is also planned to be deprecated in the future. Disabling logind-launcher by default is left for when it actually gets deprecated. In case someone does not have libseat available but do have logind running, they can just disable libseat support. Signed-off-by: Pekka Paalanen --- meson_options.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson_options.txt b/meson_options.txt index 453715cb..b6ee962b 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -161,7 +161,7 @@ option( option( 'launcher-libseat', type: 'boolean', - value: false, + value: true, description: 'Compositor: support libseat' ) From c26326bfb112784acf18879de20bc75d484b9c2f Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 4 Mar 2022 11:41:42 +0200 Subject: [PATCH 071/609] doc: expand on libseat via ssh Give a little more details about how running Weston via ssh or serial terminal is best done, now that launcher-direct and weston-launch are gone. Hopefully the removal of launcher-direct also makes less people run Weston as root, when seatd is the privileged process. Running 'weston' as root might still work through libseat's builtin backend without seatd. Signed-off-by: Pekka Paalanen --- doc/sphinx/toc/running-weston.rst | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/doc/sphinx/toc/running-weston.rst b/doc/sphinx/toc/running-weston.rst index 715bff42..81897fc2 100644 --- a/doc/sphinx/toc/running-weston.rst +++ b/doc/sphinx/toc/running-weston.rst @@ -93,12 +93,18 @@ backend to be used by ``libseat`` can optionally be selected with ``seatd`` is not already running, it can be started with ``sudo -- seatd -g video``. -Another way of launching Weston is via ssh or a serial terminal. The simplest -option here is to use the ``libseat`` launcher with ``seatd``. The process for +Launching Weston via ssh or a serial terminal is best with the ``libseat`` +launcher and ``seatd``. Logind will refuse to give access to local seats from +remote connections directly. The process for setting that up is identical to the one described above, where one just need to ensure that ``seatd`` is running with the appropriate arguments, after which one -can just run ``weston``. Another option, is to rely on logind and start weston -as systemd user service: :ref:`weston-user-service`. +can just run ``weston``. ``seatd`` will lend out the current VT, and if you want +to run on a different VT you need to ``chvt`` first. Make sure nothing will try +to take over the seat or VT via logind at the same time in case logind is +running. + +If you want to rely on logind, you can start weston as a systemd user service: +:ref:`weston-user-service`. Running Weston on a different seat on a stand-alone back-end ------------------------------------------------------------ From 4fb095eca137dcc14ffb81b350e70b6fb45f6350 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 1 Mar 2022 14:00:23 +0200 Subject: [PATCH 072/609] doc: running on different seat with libseat When using the libseat launcher, there is one more detail to take care: stop libseat from managing the VT. A normal user does not have permissions to manage a VT, so launching would just fail. In this use case you also do not want to be managing the VT, because your normal desktop is already owning the seat associated with the VT. Signed-off-by: Pekka Paalanen --- doc/sphinx/toc/running-weston.rst | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/doc/sphinx/toc/running-weston.rst b/doc/sphinx/toc/running-weston.rst index 81897fc2..15babf9d 100644 --- a/doc/sphinx/toc/running-weston.rst +++ b/doc/sphinx/toc/running-weston.rst @@ -174,7 +174,14 @@ Then, weston can be run by selecting the DRM-backend and the seat ``seat-insecur :: - ./weston -Bdrm-backend.so --seat=seat-insecure + SEATD_VTBOUND=0 ./weston -Bdrm-backend.so --seat=seat-insecure + +This assumes you are using the libseat launcher of Weston with the "builtin" +backend of libseat. Libseat automatically falls back to the builtin backend if +``seatd`` is not running and a ``logind`` service is not running or refuses. +You can also force it with ``LIBSEAT_BACKEND=builtin`` if needed. +``SEATD_VTBOUND=0`` tells libseat that there is no VT associated with the +chosen seat. If everything went well you should see weston be up-and-running on an output connected to that DRM device. From 78c94d07199b11e85cb7e047f9e0a62bbf610fda Mon Sep 17 00:00:00 2001 From: Michael Olbrich Date: Mon, 30 Mar 2020 13:59:03 +0200 Subject: [PATCH 073/609] libweston: explicitly cancel start_drag if no matching input device is found Otherwise, the client will assume that dragging is in progress and remains in that state forever. This can happen when weston processes the mouse up event just before the start_drag() arrives. Signed-off-by: Michael Olbrich --- libweston/data-device.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/libweston/data-device.c b/libweston/data-device.c index 6ac6f65f..3c627346 100644 --- a/libweston/data-device.c +++ b/libweston/data-device.c @@ -1057,13 +1057,17 @@ data_device_start_drag(struct wl_client *client, struct wl_resource *resource, touch->focus && touch->focus->surface == origin; - if (!is_pointer_grab && !is_touch_grab) - return; - /* FIXME: Check that the data source type array isn't empty. */ if (source_resource) source = wl_resource_get_user_data(source_resource); + + if (!is_pointer_grab && !is_touch_grab) { + if (source) + wl_data_source_send_cancelled(source->resource); + return; + } + if (icon_resource) icon = wl_resource_get_user_data(icon_resource); From 5ba7ae2937286e53cb5a1e3fde39a3f347a32209 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 4 Mar 2022 12:15:27 +0200 Subject: [PATCH 074/609] tests: preserve ivi runner section Everywhere else where use this trick, we also have 'used' in the attributes, except here. Make this consistent. Fixes: https://gitlab.freedesktop.org/wayland/weston/-/issues/517 Signed-off-by: Pekka Paalanen --- tests/ivi-layout-test-plugin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ivi-layout-test-plugin.c b/tests/ivi-layout-test-plugin.c index 75ce1436..8b8bdc8d 100644 --- a/tests/ivi-layout-test-plugin.c +++ b/tests/ivi-layout-test-plugin.c @@ -53,7 +53,7 @@ struct runner_test { static void runner_func_##name(struct test_context *); \ \ const struct runner_test runner_test_##name \ - __attribute__ ((section ("plugin_test_section"))) = \ + __attribute__ ((used, section ("plugin_test_section"))) = \ { \ #name, runner_func_##name \ }; \ From e6b8f5a5e40cd6c0b934e8ae079c86d5193efa96 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Wed, 5 Jan 2022 16:58:40 -0600 Subject: [PATCH 075/609] remove wl_shell This has been deprecated long enough, I think it's time to say goodbye. Signed-off-by: Derek Foreman --- libweston-desktop/internal.h | 4 - libweston-desktop/libweston-desktop.c | 19 - libweston-desktop/meson.build | 1 - libweston-desktop/wl-shell.c | 497 -------------------------- meson.build | 6 - meson_options.txt | 7 - 6 files changed, 534 deletions(-) delete mode 100644 libweston-desktop/wl-shell.c diff --git a/libweston-desktop/internal.h b/libweston-desktop/internal.h index 2606d279..8fdd4a28 100644 --- a/libweston-desktop/internal.h +++ b/libweston-desktop/internal.h @@ -234,10 +234,6 @@ weston_desktop_xdg_wm_base_create(struct weston_desktop *desktop, struct wl_global * weston_desktop_xdg_shell_v6_create(struct weston_desktop *desktop, struct wl_display *display); -struct wl_global * -weston_desktop_wl_shell_create(struct weston_desktop *desktop, - struct wl_display *display); - void weston_desktop_xwayland_init(struct weston_desktop *desktop); void diff --git a/libweston-desktop/libweston-desktop.c b/libweston-desktop/libweston-desktop.c index fdfe21bf..ea6fbebe 100644 --- a/libweston-desktop/libweston-desktop.c +++ b/libweston-desktop/libweston-desktop.c @@ -42,7 +42,6 @@ struct weston_desktop { void *user_data; struct wl_global *xdg_wm_base; /* Stable protocol xdg_shell replaces xdg_shell_unstable_v6 */ struct wl_global *xdg_shell_v6; /* Unstable xdg_shell_unstable_v6 protocol. */ - struct wl_global *wl_shell; }; void @@ -77,22 +76,6 @@ weston_desktop_create(struct weston_compositor *compositor, return NULL; } -#ifdef HAVE_DEPRECATED_WL_SHELL - weston_log("Warning: support for deprecated wl_shell interface is " - "enabled. Please migrate legacy clients to xdg-shell.\n"); - desktop->wl_shell = - weston_desktop_wl_shell_create(desktop, display); - if (desktop->wl_shell == NULL) { - weston_desktop_destroy(desktop); - return NULL; - } -#else - weston_log("Note: support for the deprecated wl_shell interface is " - "disabled. If a legacy client still needs it, it can be " - "re-enabled by passing -Ddeprecated-wl-shell=true to Meson " - "when building Weston.\n"); -#endif - weston_desktop_xwayland_init(desktop); return desktop; @@ -106,8 +89,6 @@ weston_desktop_destroy(struct weston_desktop *desktop) weston_desktop_xwayland_fini(desktop); - if (desktop->wl_shell != NULL) - wl_global_destroy(desktop->wl_shell); if (desktop->xdg_shell_v6 != NULL) wl_global_destroy(desktop->xdg_shell_v6); if (desktop->xdg_wm_base != NULL) diff --git a/libweston-desktop/meson.build b/libweston-desktop/meson.build index 0a45d941..72e77a06 100644 --- a/libweston-desktop/meson.build +++ b/libweston-desktop/meson.build @@ -4,7 +4,6 @@ srcs_libdesktop = [ 'seat.c', 'surface.c', 'xwayland.c', - 'wl-shell.c', 'xdg-shell.c', 'xdg-shell-v6.c', xdg_shell_unstable_v6_server_protocol_h, diff --git a/libweston-desktop/wl-shell.c b/libweston-desktop/wl-shell.c deleted file mode 100644 index 9efec89b..00000000 --- a/libweston-desktop/wl-shell.c +++ /dev/null @@ -1,497 +0,0 @@ -/* - * Copyright © 2010-2012 Intel Corporation - * Copyright © 2011-2012 Collabora, Ltd. - * Copyright © 2013 Raspberry Pi Foundation - * Copyright © 2016 Quentin "Sardem FF7" Glidic - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#include "config.h" - -#include - -#include - -#include -#include - -#include -#include "internal.h" - -#define WD_WL_SHELL_PROTOCOL_VERSION 1 - -enum weston_desktop_wl_shell_surface_state { - NONE, - TOPLEVEL, - MAXIMIZED, - FULLSCREEN, - TRANSIENT, - POPUP, -}; - -struct weston_desktop_wl_shell_surface { - struct wl_resource *resource; - struct weston_desktop *desktop; - struct wl_display *display; - struct weston_desktop_surface *surface; - struct weston_desktop_surface *parent; - bool added; - struct weston_desktop_seat *popup_seat; - enum weston_desktop_wl_shell_surface_state state; - struct wl_listener wl_surface_resource_destroy_listener; -}; - -static void -weston_desktop_wl_shell_surface_set_size(struct weston_desktop_surface *dsurface, - void *user_data, - int32_t width, int32_t height) -{ - struct weston_desktop_wl_shell_surface *surface = user_data; - struct weston_surface *wsurface = - weston_desktop_surface_get_surface(surface->surface); - - if ((wsurface->width == width && wsurface->height == height) || - (width == 0 && height == 0)) - return; - - wl_shell_surface_send_configure(surface->resource, - WL_SHELL_SURFACE_RESIZE_NONE, - width, height); -} - -static void -weston_desktop_wl_shell_surface_maybe_ungrab(struct weston_desktop_wl_shell_surface *surface) -{ - if (surface->state != POPUP || - !weston_desktop_surface_get_grab(surface->surface)) - return; - - weston_desktop_surface_popup_ungrab(surface->surface, - surface->popup_seat); - surface->popup_seat = NULL; -} - -static void -weston_desktop_wl_shell_surface_committed(struct weston_desktop_surface *dsurface, - void *user_data, - int32_t sx, int32_t sy) -{ - struct weston_desktop_wl_shell_surface *surface = user_data; - struct weston_surface *wsurface = - weston_desktop_surface_get_surface(dsurface); - - if (wsurface->buffer_ref.buffer == NULL) - weston_desktop_wl_shell_surface_maybe_ungrab(surface); - - if (surface->added) - weston_desktop_api_committed(surface->desktop, surface->surface, - sx, sy); -} - -static void -weston_desktop_wl_shell_surface_ping(struct weston_desktop_surface *dsurface, - uint32_t serial, void *user_data) -{ - struct weston_desktop_wl_shell_surface *surface = user_data; - - wl_shell_surface_send_ping(surface->resource, serial); -} - -static void -weston_desktop_wl_shell_surface_close(struct weston_desktop_surface *dsurface, - void *user_data) -{ - struct weston_desktop_wl_shell_surface *surface = user_data; - - if (surface->state == POPUP) - wl_shell_surface_send_popup_done(surface->resource); -} - -static bool -weston_desktop_wl_shell_surface_get_maximized(struct weston_desktop_surface *dsurface, - void *user_data) -{ - struct weston_desktop_wl_shell_surface *surface = user_data; - - return surface->state == MAXIMIZED; -} - -static bool -weston_desktop_wl_shell_surface_get_fullscreen(struct weston_desktop_surface *dsurface, - void *user_data) -{ - struct weston_desktop_wl_shell_surface *surface = user_data; - - return surface->state == FULLSCREEN; -} - -static void -weston_desktop_wl_shell_change_state(struct weston_desktop_wl_shell_surface *surface, - enum weston_desktop_wl_shell_surface_state state, - struct weston_desktop_surface *parent, - int32_t x, int32_t y) -{ - bool to_add = (parent == NULL); - - assert(state != NONE); - - if (to_add && surface->added) { - surface->state = state; - return; - } - - if (surface->state != state) { - if (surface->state == POPUP) - weston_desktop_wl_shell_surface_maybe_ungrab(surface); - - if (to_add) { - weston_desktop_surface_unset_relative_to(surface->surface); - weston_desktop_api_surface_added(surface->desktop, - surface->surface); - } else if (surface->added) { - weston_desktop_api_surface_removed(surface->desktop, - surface->surface); - } - - surface->state = state; - surface->added = to_add; - } - - if (parent != NULL) - weston_desktop_surface_set_relative_to(surface->surface, parent, - x, y, false); -} - -static void -weston_desktop_wl_shell_surface_destroy(struct weston_desktop_surface *dsurface, - void *user_data) -{ - struct weston_desktop_wl_shell_surface *surface = user_data; - - wl_list_remove(&surface->wl_surface_resource_destroy_listener.link); - - weston_desktop_wl_shell_surface_maybe_ungrab(surface); - weston_desktop_surface_unset_relative_to(surface->surface); - if (surface->added) - weston_desktop_api_surface_removed(surface->desktop, - surface->surface); - - free(surface); -} - -static void -weston_desktop_wl_shell_surface_protocol_pong(struct wl_client *wl_client, - struct wl_resource *resource, - uint32_t serial) -{ - struct weston_desktop_surface *surface = wl_resource_get_user_data(resource); - - weston_desktop_client_pong(weston_desktop_surface_get_client(surface), serial); -} - -static void -weston_desktop_wl_shell_surface_protocol_move(struct wl_client *wl_client, - struct wl_resource *resource, - struct wl_resource *seat_resource, - uint32_t serial) -{ - struct weston_desktop_surface *dsurface = - wl_resource_get_user_data(resource); - struct weston_seat *seat = - wl_resource_get_user_data(seat_resource); - struct weston_desktop_wl_shell_surface *surface = - weston_desktop_surface_get_implementation_data(dsurface); - - if (seat == NULL) - return; - - weston_desktop_api_move(surface->desktop, dsurface, seat, serial); -} - -static void -weston_desktop_wl_shell_surface_protocol_resize(struct wl_client *wl_client, - struct wl_resource *resource, - struct wl_resource *seat_resource, - uint32_t serial, - enum wl_shell_surface_resize edges) -{ - struct weston_desktop_surface *dsurface = - wl_resource_get_user_data(resource); - struct weston_seat *seat = wl_resource_get_user_data(seat_resource); - struct weston_desktop_wl_shell_surface *surface = - weston_desktop_surface_get_implementation_data(dsurface); - enum weston_desktop_surface_edge surf_edges = - (enum weston_desktop_surface_edge) edges; - - if (seat == NULL) - return; - - weston_desktop_api_resize(surface->desktop, dsurface, seat, serial, surf_edges); -} - -static void -weston_desktop_wl_shell_surface_protocol_set_toplevel(struct wl_client *wl_client, - struct wl_resource *resource) -{ - struct weston_desktop_surface *dsurface = - wl_resource_get_user_data(resource); - struct weston_desktop_wl_shell_surface *surface = - weston_desktop_surface_get_implementation_data(dsurface); - - weston_desktop_wl_shell_change_state(surface, TOPLEVEL, NULL, 0, 0); - if (surface->parent == NULL) - return; - surface->parent = NULL; - weston_desktop_api_set_parent(surface->desktop, surface->surface, NULL); -} - -static void -weston_desktop_wl_shell_surface_protocol_set_transient(struct wl_client *wl_client, - struct wl_resource *resource, - struct wl_resource *parent_resource, - int32_t x, int32_t y, - enum wl_shell_surface_transient flags) -{ - struct weston_desktop_surface *dsurface = - wl_resource_get_user_data(resource); - struct weston_surface *wparent = - wl_resource_get_user_data(parent_resource); - struct weston_desktop_surface *parent; - struct weston_desktop_wl_shell_surface *surface = - weston_desktop_surface_get_implementation_data(dsurface); - - if (!weston_surface_is_desktop_surface(wparent)) - return; - - parent = weston_surface_get_desktop_surface(wparent); - if (flags & WL_SHELL_SURFACE_TRANSIENT_INACTIVE) { - weston_desktop_wl_shell_change_state(surface, TRANSIENT, parent, - x, y); - } else { - weston_desktop_wl_shell_change_state(surface, TOPLEVEL, NULL, - 0, 0); - surface->parent = parent; - weston_desktop_api_set_parent(surface->desktop, - surface->surface, parent); - } -} - -static void -weston_desktop_wl_shell_surface_protocol_set_fullscreen(struct wl_client *wl_client, - struct wl_resource *resource, - enum wl_shell_surface_fullscreen_method method, - uint32_t framerate, - struct wl_resource *output_resource) -{ - struct weston_desktop_surface *dsurface = - wl_resource_get_user_data(resource); - struct weston_desktop_wl_shell_surface *surface = - weston_desktop_surface_get_implementation_data(dsurface); - struct weston_output *output = NULL; - - if (output_resource != NULL) - output = weston_head_from_resource(output_resource)->output; - - weston_desktop_wl_shell_change_state(surface, FULLSCREEN, NULL, 0, 0); - weston_desktop_api_fullscreen_requested(surface->desktop, dsurface, - true, output); -} - -static void -weston_desktop_wl_shell_surface_protocol_set_popup(struct wl_client *wl_client, - struct wl_resource *resource, - struct wl_resource *seat_resource, - uint32_t serial, - struct wl_resource *parent_resource, - int32_t x, int32_t y, - enum wl_shell_surface_transient flags) -{ - struct weston_desktop_surface *dsurface = - wl_resource_get_user_data(resource); - struct weston_seat *wseat = wl_resource_get_user_data(seat_resource); - struct weston_desktop_seat *seat = weston_desktop_seat_from_seat(wseat); - struct weston_surface *parent = - wl_resource_get_user_data(parent_resource); - struct weston_desktop_surface *parent_surface; - struct weston_desktop_wl_shell_surface *surface = - weston_desktop_surface_get_implementation_data(dsurface); - - /* Check that if we have a valid wseat we also got a valid desktop seat */ - if (wseat != NULL && seat == NULL) { - wl_client_post_no_memory(wl_client); - return; - } - - if (!weston_surface_is_desktop_surface(parent)) - return; - - parent_surface = weston_surface_get_desktop_surface(parent); - - weston_desktop_wl_shell_change_state(surface, POPUP, - parent_surface, x, y); - weston_desktop_surface_popup_grab(surface->surface, seat, serial); - surface->popup_seat = seat; -} - -static void -weston_desktop_wl_shell_surface_protocol_set_maximized(struct wl_client *wl_client, - struct wl_resource *resource, - struct wl_resource *output_resource) -{ - struct weston_desktop_surface *dsurface = - wl_resource_get_user_data(resource); - struct weston_desktop_wl_shell_surface *surface = - weston_desktop_surface_get_implementation_data(dsurface); - - weston_desktop_wl_shell_change_state(surface, MAXIMIZED, NULL, 0, 0); - weston_desktop_api_maximized_requested(surface->desktop, dsurface, true); -} - -static void -weston_desktop_wl_shell_surface_protocol_set_title(struct wl_client *wl_client, - struct wl_resource *resource, - const char *title) -{ - struct weston_desktop_surface *surface = - wl_resource_get_user_data(resource); - - weston_desktop_surface_set_title(surface, title); -} - -static void -weston_desktop_wl_shell_surface_protocol_set_class(struct wl_client *wl_client, - struct wl_resource *resource, - const char *class_) -{ - struct weston_desktop_surface *surface = - wl_resource_get_user_data(resource); - - weston_desktop_surface_set_app_id(surface, class_); -} - - -static const struct wl_shell_surface_interface weston_desktop_wl_shell_surface_implementation = { - .pong = weston_desktop_wl_shell_surface_protocol_pong, - .move = weston_desktop_wl_shell_surface_protocol_move, - .resize = weston_desktop_wl_shell_surface_protocol_resize, - .set_toplevel = weston_desktop_wl_shell_surface_protocol_set_toplevel, - .set_transient = weston_desktop_wl_shell_surface_protocol_set_transient, - .set_fullscreen = weston_desktop_wl_shell_surface_protocol_set_fullscreen, - .set_popup = weston_desktop_wl_shell_surface_protocol_set_popup, - .set_maximized = weston_desktop_wl_shell_surface_protocol_set_maximized, - .set_title = weston_desktop_wl_shell_surface_protocol_set_title, - .set_class = weston_desktop_wl_shell_surface_protocol_set_class, -}; - -static const struct weston_desktop_surface_implementation weston_desktop_wl_shell_surface_internal_implementation = { - .set_size = weston_desktop_wl_shell_surface_set_size, - .committed = weston_desktop_wl_shell_surface_committed, - .ping = weston_desktop_wl_shell_surface_ping, - .close = weston_desktop_wl_shell_surface_close, - - .get_maximized = weston_desktop_wl_shell_surface_get_maximized, - .get_fullscreen = weston_desktop_wl_shell_surface_get_fullscreen, - - .destroy = weston_desktop_wl_shell_surface_destroy, -}; - -static void -wl_surface_resource_destroyed(struct wl_listener *listener, - void *data) -{ - struct weston_desktop_wl_shell_surface *surface = - wl_container_of(listener, surface, - wl_surface_resource_destroy_listener); - - /* the wl_shell_surface spec says that wl_shell_surfaces are to be - * destroyed automatically when the wl_surface is destroyed. */ - weston_desktop_surface_destroy(surface->surface); -} - -static void -weston_desktop_wl_shell_protocol_get_shell_surface(struct wl_client *wl_client, - struct wl_resource *resource, - uint32_t id, - struct wl_resource *surface_resource) -{ - struct weston_desktop_client *client = wl_resource_get_user_data(resource); - struct weston_surface *wsurface = wl_resource_get_user_data(surface_resource); - struct weston_desktop_wl_shell_surface *surface; - - - if (weston_surface_set_role(wsurface, "wl_shell_surface", resource, WL_SHELL_ERROR_ROLE) < 0) - return; - - surface = zalloc(sizeof(struct weston_desktop_wl_shell_surface)); - if (surface == NULL) { - wl_client_post_no_memory(wl_client); - return; - } - - surface->desktop = weston_desktop_client_get_desktop(client); - surface->display = weston_desktop_get_display(surface->desktop); - - surface->surface = - weston_desktop_surface_create(surface->desktop, client, wsurface, - &weston_desktop_wl_shell_surface_internal_implementation, - surface); - if (surface->surface == NULL) { - free(surface); - return; - } - - surface->wl_surface_resource_destroy_listener.notify = - wl_surface_resource_destroyed; - wl_resource_add_destroy_listener(wsurface->resource, - &surface->wl_surface_resource_destroy_listener); - - surface->resource = - weston_desktop_surface_add_resource(surface->surface, - &wl_shell_surface_interface, - &weston_desktop_wl_shell_surface_implementation, - id, NULL); -} - - -static const struct wl_shell_interface weston_desktop_wl_shell_implementation = { - .get_shell_surface = weston_desktop_wl_shell_protocol_get_shell_surface, -}; - -static void -weston_desktop_wl_shell_bind(struct wl_client *client, void *data, - uint32_t version, uint32_t id) -{ - struct weston_desktop *desktop = data; - - weston_desktop_client_create(desktop, client, NULL, &wl_shell_interface, - &weston_desktop_wl_shell_implementation, - version, id); -} - -struct wl_global * -weston_desktop_wl_shell_create(struct weston_desktop *desktop, - struct wl_display *display) -{ - return wl_global_create(display, - &wl_shell_interface, - WD_WL_SHELL_PROTOCOL_VERSION, desktop, - weston_desktop_wl_shell_bind); -} diff --git a/meson.build b/meson.build index 210bfa39..182d3f7d 100644 --- a/meson.build +++ b/meson.build @@ -143,12 +143,6 @@ if dep_xkbcommon.version().version_compare('>= 0.5.0') config_h.set('HAVE_XKBCOMMON_COMPOSE', '1') endif -if get_option('deprecated-wl-shell') - warning('Support for the deprecated wl_shell interface is enabled.') - warning('This feature will be removed in a future version.') - config_h.set('HAVE_DEPRECATED_WL_SHELL', '1') -endif - dep_wayland_server = dependency('wayland-server', version: '>= 1.20.0') dep_wayland_client = dependency('wayland-client', version: '>= 1.20.0') dep_pixman = dependency('pixman-1', version: '>= 0.25.2') diff --git a/meson_options.txt b/meson_options.txt index b6ee962b..b9340dc2 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -131,13 +131,6 @@ option( description: 'Weston desktop shell: default helper client selection' ) -option( - 'deprecated-wl-shell', - type: 'boolean', - value: false, - description: 'Enable the deprecated wl_shell protocol' -) - option( 'color-management-lcms', type: 'boolean', From 7cae2a1fb0aeec24ca33ac4c7cbb268f77095cb5 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Wed, 5 Jan 2022 17:10:58 -0600 Subject: [PATCH 076/609] backend-wayland: Stop supporting wl_shell This has been deprecated for a long time, so let's stop supporting it entirely. Signed-off-by: Derek Foreman --- libweston/backend-wayland/wayland.c | 87 ++--------------------------- 1 file changed, 4 insertions(+), 83 deletions(-) diff --git a/libweston/backend-wayland/wayland.c b/libweston/backend-wayland/wayland.c index 5c3d8e9a..a3bbfbf1 100644 --- a/libweston/backend-wayland/wayland.c +++ b/libweston/backend-wayland/wayland.c @@ -76,7 +76,6 @@ struct wayland_backend { struct wl_display *wl_display; struct wl_registry *registry; struct wl_compositor *compositor; - struct wl_shell *shell; struct xdg_wm_base *xdg_wm_base; struct zwp_fullscreen_shell_v1 *fshell; struct wl_shm *shm; @@ -112,7 +111,6 @@ struct wayland_output { struct wl_output *output; uint32_t global_id; - struct wl_shell_surface *shell_surface; struct xdg_surface *xdg_surface; struct xdg_toplevel *xdg_toplevel; int configure_width, configure_height; @@ -692,11 +690,6 @@ wayland_backend_destroy_output_surface(struct wayland_output *output) output->parent.xdg_surface = NULL; } - if (output->parent.shell_surface) { - wl_shell_surface_destroy(output->parent.shell_surface); - output->parent.shell_surface = NULL; - } - wl_surface_destroy(output->parent.surface); output->parent.surface = NULL; } @@ -763,8 +756,6 @@ wayland_output_destroy(struct weston_output *base) free(output); } -static const struct wl_shell_surface_listener shell_surface_listener; - #ifdef ENABLE_EGL static int wayland_output_init_gl_renderer(struct wayland_output *output) @@ -928,8 +919,6 @@ wayland_output_set_windowed(struct wayland_output *output) if (output->parent.xdg_toplevel) { xdg_toplevel_unset_fullscreen(output->parent.xdg_toplevel); - } else if (output->parent.shell_surface) { - wl_shell_surface_set_toplevel(output->parent.shell_surface); } else { abort(); } @@ -939,7 +928,6 @@ wayland_output_set_windowed(struct wayland_output *output) static void wayland_output_set_fullscreen(struct wayland_output *output, - enum wl_shell_surface_fullscreen_method method, uint32_t framerate, struct wl_output *target) { if (output->frame) { @@ -951,9 +939,6 @@ wayland_output_set_fullscreen(struct wayland_output *output, if (output->parent.xdg_toplevel) { xdg_toplevel_set_fullscreen(output->parent.xdg_toplevel, target); - } else if (output->parent.shell_surface) { - wl_shell_surface_set_fullscreen(output->parent.shell_surface, - method, framerate, target); } else { abort(); } @@ -1080,7 +1065,7 @@ wayland_output_switch_mode(struct weston_output *output_base, b = to_wayland_backend(output_base->compositor); - if (output->parent.xdg_surface || output->parent.shell_surface || !b->parent.fshell) + if (output->parent.xdg_surface || !b->parent.fshell) return -1; mode = wayland_output_choose_mode(output, mode); @@ -1221,20 +1206,6 @@ wayland_backend_create_output_surface(struct wayland_output *output) weston_log("wayland-backend: Using xdg_wm_base\n"); } - else if (b->parent.shell) { - output->parent.shell_surface = - wl_shell_get_shell_surface(b->parent.shell, - output->parent.surface); - if (!output->parent.shell_surface) { - wl_surface_destroy(output->parent.surface); - return -1; - } - - wl_shell_surface_add_listener(output->parent.shell_surface, - &shell_surface_listener, output); - - weston_log("wayland-backend: Using wl_shell\n"); - } return 0; } @@ -1295,13 +1266,9 @@ wayland_output_enable(struct weston_output *base) output->parent.draw_initial_frame = true; } - } else { - wayland_output_set_fullscreen(output, - WL_SHELL_SURFACE_FULLSCREEN_METHOD_DRIVER, - output->mode.refresh, output->parent.output); } } else if (b->fullscreen) { - wayland_output_set_fullscreen(output, 0, 0, NULL); + wayland_output_set_fullscreen(output, 0, NULL); } else { wayland_output_set_windowed(output); } @@ -1564,13 +1531,10 @@ wayland_output_setup_fullscreen(struct wayland_output *output, return -1; /* What should size be set if conditional is false? */ - if (b->parent.xdg_wm_base || b->parent.shell) { + if (b->parent.xdg_wm_base) { if (output->parent.xdg_toplevel) xdg_toplevel_set_fullscreen(output->parent.xdg_toplevel, output->parent.output); - else if (output->parent.shell_surface) - wl_shell_surface_set_fullscreen(output->parent.shell_surface, - 0, 0, NULL); wl_display_roundtrip(b->parent.wl_display); @@ -1594,36 +1558,6 @@ err_set_size: return -1; } -static void -shell_surface_ping(void *data, struct wl_shell_surface *shell_surface, - uint32_t serial) -{ - wl_shell_surface_pong(shell_surface, serial); -} - -static void -shell_surface_configure(void *data, struct wl_shell_surface *shell_surface, - uint32_t edges, int32_t width, int32_t height) -{ - struct wayland_output *output = data; - - output->parent.configure_width = width; - output->parent.configure_height = height; - - /* FIXME: implement resizing */ -} - -static void -shell_surface_popup_done(void *data, struct wl_shell_surface *shell_surface) -{ -} - -static const struct wl_shell_surface_listener shell_surface_listener = { - shell_surface_ping, - shell_surface_configure, - shell_surface_popup_done -}; - /* Events received from the wayland-server this compositor is client of: */ /* parent input interface */ @@ -1801,9 +1735,6 @@ input_handle_button(void *data, struct wl_pointer *pointer, if (input->output->parent.xdg_toplevel) xdg_toplevel_move(input->output->parent.xdg_toplevel, input->parent.seat, serial); - else if (input->output->parent.shell_surface) - wl_shell_surface_move(input->output->parent.shell_surface, - input->parent.seat, serial); frame_status_clear(input->output->frame, FRAME_STATUS_MOVE); return; @@ -2157,9 +2088,6 @@ input_handle_touch_down(void *data, struct wl_touch *wl_touch, if (output->parent.xdg_toplevel) xdg_toplevel_move(output->parent.xdg_toplevel, input->parent.seat, serial); - else if (output->parent.shell_surface) - wl_shell_surface_move(output->parent.shell_surface, - input->parent.seat, serial); frame_status_clear(output->frame, FRAME_STATUS_MOVE); return; @@ -2671,10 +2599,6 @@ registry_handle_global(void *data, struct wl_registry *registry, uint32_t name, &xdg_wm_base_interface, 1); xdg_wm_base_add_listener(b->parent.xdg_wm_base, &wm_base_listener, b); - } else if (strcmp(interface, "wl_shell") == 0) { - b->parent.shell = - wl_registry_bind(registry, name, - &wl_shell_interface, 1); } else if (strcmp(interface, "zwp_fullscreen_shell_v1") == 0) { b->parent.fshell = wl_registry_bind(registry, name, @@ -2763,9 +2687,6 @@ wayland_destroy(struct weston_compositor *ec) if (b->parent.xdg_wm_base) xdg_wm_base_destroy(b->parent.xdg_wm_base); - if (b->parent.shell) - wl_shell_destroy(b->parent.shell); - if (b->parent.fshell) zwp_fullscreen_shell_v1_release(b->parent.fshell); @@ -2833,7 +2754,7 @@ fullscreen_binding(struct weston_keyboard *keyboard, return; if (input->output->frame) - wayland_output_set_fullscreen(input->output, 0, 0, NULL); + wayland_output_set_fullscreen(input->output, 0, NULL); else wayland_output_set_windowed(input->output); From d40cedc8af9a42e1f6746fb58f4556080c6ff133 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Wed, 23 Feb 2022 17:06:23 +0200 Subject: [PATCH 077/609] desktop-shell: Remove wl_shell_surface::resize enum And use the ones provided by libweston-desktop, as they are one and the same. Signed-off-by: Marius Vlad --- desktop-shell/shell.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 4bf4eaba..38a09e1a 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -1622,16 +1622,16 @@ resize_grab_motion(struct weston_pointer_grab *grab, pointer->x, pointer->y, &to_x, &to_y); width = resize->width; - if (resize->edges & WL_SHELL_SURFACE_RESIZE_LEFT) { + if (resize->edges & WESTON_DESKTOP_SURFACE_EDGE_LEFT) { width += wl_fixed_to_int(from_x - to_x); - } else if (resize->edges & WL_SHELL_SURFACE_RESIZE_RIGHT) { + } else if (resize->edges & WESTON_DESKTOP_SURFACE_EDGE_RIGHT) { width += wl_fixed_to_int(to_x - from_x); } height = resize->height; - if (resize->edges & WL_SHELL_SURFACE_RESIZE_TOP) { + if (resize->edges & WESTON_DESKTOP_SURFACE_EDGE_TOP) { height += wl_fixed_to_int(from_y - to_y); - } else if (resize->edges & WL_SHELL_SURFACE_RESIZE_BOTTOM) { + } else if (resize->edges & WESTON_DESKTOP_SURFACE_EDGE_BOTTOM) { height += wl_fixed_to_int(to_y - from_y); } @@ -1707,9 +1707,9 @@ surface_resize(struct shell_surface *shsurf, { struct weston_resize_grab *resize; const unsigned resize_topbottom = - WL_SHELL_SURFACE_RESIZE_TOP | WL_SHELL_SURFACE_RESIZE_BOTTOM; + WESTON_DESKTOP_SURFACE_EDGE_TOP | WESTON_DESKTOP_SURFACE_EDGE_BOTTOM; const unsigned resize_leftright = - WL_SHELL_SURFACE_RESIZE_LEFT | WL_SHELL_SURFACE_RESIZE_RIGHT; + WESTON_DESKTOP_SURFACE_EDGE_LEFT | WESTON_DESKTOP_SURFACE_EDGE_RIGHT; const unsigned resize_any = resize_topbottom | resize_leftright; struct weston_geometry geometry; @@ -1719,7 +1719,7 @@ surface_resize(struct shell_surface *shsurf, return 0; /* Check for invalid edge combinations. */ - if (edges == WL_SHELL_SURFACE_RESIZE_NONE || edges > resize_any || + if (edges == WESTON_DESKTOP_SURFACE_EDGE_NONE || edges > resize_any || (edges & resize_topbottom) == resize_topbottom || (edges & resize_leftright) == resize_leftright) return 0; @@ -2546,9 +2546,9 @@ desktop_surface_committed(struct weston_desktop_surface *desktop_surface, sy = 0; } - if (shsurf->resize_edges & WL_SHELL_SURFACE_RESIZE_LEFT) + if (shsurf->resize_edges & WESTON_DESKTOP_SURFACE_EDGE_LEFT) sx = shsurf->last_width - surface->width; - if (shsurf->resize_edges & WL_SHELL_SURFACE_RESIZE_TOP) + if (shsurf->resize_edges & WESTON_DESKTOP_SURFACE_EDGE_TOP) sy = shsurf->last_height - surface->height; weston_view_to_global_float(shsurf->view, 0, 0, &from_x, &from_y); @@ -3374,18 +3374,18 @@ resize_binding(struct weston_pointer *pointer, const struct timespec *time, &x, &y); if (x < surface->width / 3) - edges |= WL_SHELL_SURFACE_RESIZE_LEFT; + edges |= WESTON_DESKTOP_SURFACE_EDGE_LEFT; else if (x < 2 * surface->width / 3) edges |= 0; else - edges |= WL_SHELL_SURFACE_RESIZE_RIGHT; + edges |= WESTON_DESKTOP_SURFACE_EDGE_RIGHT; if (y < surface->height / 3) - edges |= WL_SHELL_SURFACE_RESIZE_TOP; + edges |= WESTON_DESKTOP_SURFACE_EDGE_TOP; else if (y < 2 * surface->height / 3) edges |= 0; else - edges |= WL_SHELL_SURFACE_RESIZE_BOTTOM; + edges |= WESTON_DESKTOP_SURFACE_EDGE_BOTTOM; surface_resize(shsurf, pointer, edges); } From e9fe66a91c75d6bb856fe4910be57b89b53a7c2f Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Wed, 9 Mar 2022 01:29:55 +0200 Subject: [PATCH 078/609] weston-log: Extract helper for generating a time stamp As we might be needing it for other scopes extract the time stamp genration into a helper. Signed-off-by: Marius Vlad --- compositor/main.c | 36 ++---------------------- include/libweston/weston-log.h | 2 ++ libweston/weston-log.c | 50 ++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 33 deletions(-) diff --git a/compositor/main.c b/compositor/main.c index 6fda035a..3b15a959 100644 --- a/compositor/main.c +++ b/compositor/main.c @@ -134,37 +134,6 @@ static struct weston_log_scope *log_scope; static struct weston_log_scope *protocol_scope; static int cached_tm_mday = -1; -static char * -weston_log_timestamp(char *buf, size_t len) -{ - struct timeval tv; - struct tm *brokendown_time; - char datestr[128]; - char timestr[128]; - - gettimeofday(&tv, NULL); - - brokendown_time = localtime(&tv.tv_sec); - if (brokendown_time == NULL) { - snprintf(buf, len, "%s", "[(NULL)localtime] "); - return buf; - } - - memset(datestr, 0, sizeof(datestr)); - if (brokendown_time->tm_mday != cached_tm_mday) { - strftime(datestr, sizeof(datestr), "Date: %Y-%m-%d %Z\n", - brokendown_time); - cached_tm_mday = brokendown_time->tm_mday; - } - - strftime(timestr, sizeof(timestr), "%H:%M:%S", brokendown_time); - /* if datestr is empty it prints only timestr*/ - snprintf(buf, len, "%s[%s.%03li]", datestr, - timestr, (tv.tv_usec / 1000)); - - return buf; -} - static void custom_handler(const char *fmt, va_list arg) { @@ -172,7 +141,7 @@ custom_handler(const char *fmt, va_list arg) weston_log_scope_printf(log_scope, "%s libwayland: ", weston_log_timestamp(timestr, - sizeof(timestr))); + sizeof(timestr), &cached_tm_mday)); weston_log_scope_vprintf(log_scope, fmt, arg); } @@ -218,7 +187,8 @@ vlog(const char *fmt, va_list ap) if (weston_log_scope_is_enabled(log_scope)) { int len_va; char *log_timestamp = weston_log_timestamp(timestr, - sizeof(timestr)); + sizeof(timestr), + &cached_tm_mday); len_va = vasprintf(&str, fmt, ap); if (len_va >= 0) { len = weston_log_scope_printf(log_scope, "%s %s", diff --git a/include/libweston/weston-log.h b/include/libweston/weston-log.h index aeb7768b..1786dea0 100644 --- a/include/libweston/weston-log.h +++ b/include/libweston/weston-log.h @@ -109,6 +109,8 @@ weston_log_subscription_complete(struct weston_log_subscription *sub); char * weston_log_scope_timestamp(struct weston_log_scope *scope, char *buf, size_t len); +char * +weston_log_timestamp(char *buf, size_t len, int *cached_tm_mday); void weston_log_subscriber_destroy(struct weston_log_subscriber *subscriber); diff --git a/libweston/weston-log.c b/libweston/weston-log.c index 276fde26..5e7f3521 100644 --- a/libweston/weston-log.c +++ b/libweston/weston-log.c @@ -913,6 +913,56 @@ weston_log_scope_timestamp(struct weston_log_scope *scope, return buf; } +/** Returns a timestamp useful for adding it to a log scope. + * + * @example + * char timestr[128]; + * static int cached_dm = -1; + * char *time_buff = weston_log_timestamp(timestr, sizeof(timestr), &cached_dm); + * weston_log_scope_printf(log_scope, "%s %s", time_buff, other_data); + * + * @param buf a user-supplied buffer + * @param len user-supplied length of the buffer + * @param cached_tm_mday a cached day of the month, as an integer. Setting this + * pointer different from NULL, to an integer value other than was retrieved as + * current day of the month, would add an additional line under the form of + * 'Date: Y-m-d Z\n'. Setting the pointer to NULL would not print any date, nor + * if the value matches the current day of month. Helps identify logs that + * spawn multiple days, while still having a shorter time stamp format. + * @ingroup log + */ +WL_EXPORT char * +weston_log_timestamp(char *buf, size_t len, int *cached_tm_mday) +{ + struct timeval tv; + struct tm *brokendown_time; + char datestr[128]; + char timestr[128]; + + gettimeofday(&tv, NULL); + + brokendown_time = localtime(&tv.tv_sec); + if (brokendown_time == NULL) { + snprintf(buf, len, "%s", "[(NULL)localtime] "); + return buf; + } + + memset(datestr, 0, sizeof(datestr)); + if (cached_tm_mday && brokendown_time->tm_mday != *cached_tm_mday) { + strftime(datestr, sizeof(datestr), "Date: %Y-%m-%d %Z\n", + brokendown_time); + *cached_tm_mday = brokendown_time->tm_mday; + } + + strftime(timestr, sizeof(timestr), "%H:%M:%S", brokendown_time); + /* if datestr is empty it prints only timestr*/ + snprintf(buf, len, "%s[%s.%03li]", datestr, + timestr, (tv.tv_usec / 1000)); + + return buf; +} + + void weston_log_subscriber_release(struct weston_log_subscriber *subscriber) { From c19cf3d684f2c3244368d81f75f87f71a5c6e8cb Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Wed, 9 Mar 2022 12:06:33 +0200 Subject: [PATCH 079/609] libweston: Enable logging for libseat launcher The built-in backend of libseat requires users to enable a logging level in order for libseat to start writing out log messages. For that to happen we split out the info and error log level messages into the compositor's log scope, while debug level messages go into a dedicated scope. With that, this patch brings in a new scope, called libseat-debug, which users need to explicity create a subscription for it as to retrieve/have access to debug message coming out of libseat. Note that by default we have a subscription for the log-scope so any errors/info from libseat would be displayed to the user. Signed-off-by: Marius Vlad --- include/libweston/libweston.h | 1 + libweston/compositor.c | 7 +++++ libweston/launcher-libseat.c | 56 +++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+) diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 90350c9e..849afd57 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -1166,6 +1166,7 @@ struct weston_compositor { struct weston_log_context *weston_log_ctx; struct weston_log_scope *debug_scene; struct weston_log_scope *timeline; + struct weston_log_scope *libseat_debug; struct content_protection *content_protection; }; diff --git a/libweston/compositor.c b/libweston/compositor.c index e44f65ee..955e5534 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -7834,6 +7834,10 @@ weston_compositor_create(struct wl_display *display, weston_timeline_create_subscription, weston_timeline_destroy_subscription, ec); + ec->libseat_debug = + weston_compositor_add_log_scope(ec, "libseat-debug", + "libseat debug messages\n", + NULL, NULL, NULL); return ec; fail: @@ -8215,6 +8219,9 @@ weston_compositor_destroy(struct weston_compositor *compositor) weston_log_scope_destroy(compositor->timeline); compositor->timeline = NULL; + weston_log_scope_destroy(compositor->libseat_debug); + compositor->libseat_debug = NULL; + if (compositor->default_dmabuf_feedback) { weston_dmabuf_feedback_destroy(compositor->default_dmabuf_feedback); weston_dmabuf_feedback_format_table_destroy(compositor->dmabuf_feedback_format_table); diff --git a/libweston/launcher-libseat.c b/libweston/launcher-libseat.c index 58e22d39..8c0abb9a 100644 --- a/libweston/launcher-libseat.c +++ b/libweston/launcher-libseat.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +41,8 @@ #include #include +#include +#include "weston-log-internal.h" #include "backend.h" #include "dbus.h" #include "launcher-impl.h" @@ -60,6 +63,11 @@ struct launcher_libseat { struct wl_list devices; }; +/* debug messages go into a dedicated libseat-debug scope, while info and err + * log level messages go into the log_scope, which the compositor has a + * subscription by default*/ +static struct weston_log_scope *libseat_debug_scope = NULL; + static struct launcher_libseat_device * find_device_by_fd(struct launcher_libseat *wl, int fd) { @@ -179,6 +187,44 @@ libseat_event(int fd, uint32_t mask, void *data) return 1; } +static void +log_libseat_info_err(const char *fmt, va_list ap) +{ + /* these all have been set-up by the compositor and use the 'log' scope */ + weston_vlog(fmt, ap); + weston_log_continue("\n"); +} + +static void +log_libseat_debug(const char *fmt, va_list ap) +{ + int len_va; + char *str; + const char *oom = "Out of memory"; + + if (!weston_log_scope_is_enabled(libseat_debug_scope)) + return; + + len_va = vasprintf(&str, fmt, ap); + if (len_va >= 0) { + weston_log_scope_printf(libseat_debug_scope, "%s\n", str); + free(str); + } else { + weston_log_scope_printf(libseat_debug_scope, "%s\n", oom); + } +} + +static void log_libseat(enum libseat_log_level level, + const char *fmt, va_list ap) +{ + if (level == LIBSEAT_LOG_LEVEL_DEBUG) { + log_libseat_debug(fmt, ap); + return; + } + + log_libseat_info_err(fmt, ap); +} + static int seat_open(struct weston_launcher **out, struct weston_compositor *compositor, const char *seat_id, bool sync_drm) @@ -195,6 +241,13 @@ seat_open(struct weston_launcher **out, struct weston_compositor *compositor, wl->compositor = compositor; wl_list_init(&wl->devices); + libseat_debug_scope = compositor->libseat_debug; + assert(libseat_debug_scope); + libseat_set_log_handler(log_libseat); + + /* includes (all) other log levels available <= LOG_LEVEL_DEBUG */ + libseat_set_log_level(LIBSEAT_LOG_LEVEL_DEBUG); + wl->seat = libseat_open_seat(&seat_listener, wl); if (wl->seat == NULL) { weston_log("libseat: could not open seat\n"); @@ -231,6 +284,9 @@ seat_close(struct weston_launcher *launcher) { struct launcher_libseat *wl = wl_container_of(launcher, wl, base); + libseat_debug_scope = NULL; + libseat_set_log_handler(NULL); + if (wl->seat != NULL) { libseat_close_seat(wl->seat); } From f81aacdf2f69a371bc8a3970baec88c21dc862cc Mon Sep 17 00:00:00 2001 From: Robert Mader Date: Thu, 17 Feb 2022 13:49:01 +0100 Subject: [PATCH 080/609] pixel-formats: Add support for 64bbp float RGB formats These are supported by some other compositors already. Add them to the list so `weston-simple-dmabuf-feedback` reports them correctly. Signed-off-by: Robert Mader --- libweston/pixel-formats.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/libweston/pixel-formats.c b/libweston/pixel-formats.c index 56ccaf6a..822cc144 100644 --- a/libweston/pixel-formats.c +++ b/libweston/pixel-formats.c @@ -64,6 +64,12 @@ .bits.b = b_, \ .bits.a = a_, \ .component_type = PIXEL_COMPONENT_TYPE_FIXED +#define BITS_RGBA_FLOAT(r_, g_, b_, a_) \ + .bits.r = r_, \ + .bits.g = g_, \ + .bits.b = b_, \ + .bits.a = a_, \ + .component_type = PIXEL_COMPONENT_TYPE_FLOAT #define PIXMAN_FMT(fmt) .pixman_format = (PIXMAN_ ## fmt) @@ -341,6 +347,24 @@ static const struct pixel_format_info pixel_format_table[] = { BITS_RGBA_FIXED(16, 16, 16, 16), .opaque_substitute = DRM_FORMAT_XBGR16161616, }, + { + DRM_FORMAT(XBGR16161616F), + BITS_RGBA_FLOAT(16, 16, 16, 0), + }, + { + DRM_FORMAT(ABGR16161616F), + BITS_RGBA_FLOAT(16, 16, 16, 16), + .opaque_substitute = DRM_FORMAT_XBGR16161616F, + }, + { + DRM_FORMAT(XRGB16161616F), + BITS_RGBA_FLOAT(16, 16, 16, 0), + }, + { + DRM_FORMAT(ARGB16161616F), + BITS_RGBA_FLOAT(16, 16, 16, 16), + .opaque_substitute = DRM_FORMAT_XRGB16161616F, + }, { DRM_FORMAT(YUYV), SAMPLER_TYPE(EGL_TEXTURE_Y_XUXV_WL), From 26698535625ebf971fa73b9c35f6a9fd9a5b77e3 Mon Sep 17 00:00:00 2001 From: Robert Mader Date: Wed, 16 Feb 2022 00:23:53 +0100 Subject: [PATCH 081/609] clients/simple-dmabuf-feedback: Add fallback print method for unknown formats Using `pixel_format_get_info()` can result in formats being reported as `UNKNOWN` when used on compositors other than Weston. As `weston-simple-dmabuf-feedback` somewhat succeeds `wayland-info` as tool for `zwp_linux_dmabuf_v1` debugging from version 4 on, copy the approach from the later for these cases. Signed-off-by: Robert Mader --- clients/simple-dmabuf-feedback.c | 41 ++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/clients/simple-dmabuf-feedback.c b/clients/simple-dmabuf-feedback.c index 2a729a62..9a66a981 100644 --- a/clients/simple-dmabuf-feedback.c +++ b/clients/simple-dmabuf-feedback.c @@ -26,6 +26,7 @@ #include "config.h" #include +#include #include #include #include @@ -1050,18 +1051,54 @@ dmabuf_feedback_tranche_formats(void *data, } } +static char +bits2graph(uint32_t value, unsigned bitoffset) +{ + int c = (value >> bitoffset) & 0xff; + + if (isgraph(c) || isspace(c)) + return c; + + return '?'; +} + +static void +fourcc2str(uint32_t format, char *str, int len) +{ + int i; + + assert(len >= 5); + + for (i = 0; i < 4; i++) + str[i] = bits2graph(format, i * 8); + str[i] = '\0'; +} + static void print_tranche_format_modifier(uint32_t format, uint64_t modifier) { const struct pixel_format_info *fmt_info; + char *format_str; char *mod_name; + int len; - fmt_info = pixel_format_get_info(format); mod_name = pixel_format_get_modifier(modifier); + fmt_info = pixel_format_get_info(format); + + if (fmt_info) { + len = asprintf(&format_str, "%s", fmt_info->drm_format_name); + } else { + char fourcc_str[5]; + + fourcc2str(format, fourcc_str, sizeof(fourcc_str)); + len = asprintf(&format_str, "0x%08x (%s)", format, fourcc_str); + } + assert(len > 0); fprintf(stderr, "│ ├────────tranche format/modifier pair - format %s, modifier %s\n", - fmt_info ? fmt_info->drm_format_name : "UNKNOWN", mod_name); + format_str, mod_name); + free(format_str); free(mod_name); } From f58a3a7e1d2f87778aeb9c895bd0a4a3b6eef099 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 11 Mar 2022 14:37:43 +0200 Subject: [PATCH 082/609] include: drop unused config-parser.h types Apparently these are not used anywhere. Garbage-collect them, and trim some empty lines. Signed-off-by: Pekka Paalanen --- include/libweston/config-parser.h | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/include/libweston/config-parser.h b/include/libweston/config-parser.h index d82197bf..343ff53d 100644 --- a/include/libweston/config-parser.h +++ b/include/libweston/config-parser.h @@ -35,26 +35,6 @@ extern "C" { #define WESTON_CONFIG_FILE_ENV_VAR "WESTON_CONFIG_FILE" -enum config_key_type { - CONFIG_KEY_INTEGER, /* typeof data = int */ - CONFIG_KEY_UNSIGNED_INTEGER, /* typeof data = unsigned int */ - CONFIG_KEY_STRING, /* typeof data = char* */ - CONFIG_KEY_BOOLEAN /* typeof data = int */ -}; - -struct config_key { - const char *name; - enum config_key_type type; - void *data; -}; - -struct config_section { - const char *name; - const struct config_key *keys; - int num_keys; - void (*done)(void *data); -}; - enum weston_option_type { WESTON_OPTION_INTEGER, WESTON_OPTION_UNSIGNED_INTEGER, @@ -121,10 +101,8 @@ int weston_config_next_section(struct weston_config *config, struct weston_config_section **section, const char **name); - #ifdef __cplusplus } #endif #endif /* CONFIGPARSER_H */ - From 0a38fc7e75d870c93f9cf00205e77f5e186cd9b2 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 11 Mar 2022 14:55:36 +0200 Subject: [PATCH 083/609] shared: fix WL_EXPORT style in config-parser.c This was the only file in Weston using WL_EXPORT on its own line. Fix the style to follow everything else. Signed-off-by: Pekka Paalanen --- shared/config-parser.c | 36 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/shared/config-parser.c b/shared/config-parser.c index ed120d50..c19baa0d 100644 --- a/shared/config-parser.c +++ b/shared/config-parser.c @@ -129,8 +129,7 @@ config_section_get_entry(struct weston_config_section *section, return NULL; } -WL_EXPORT -struct weston_config_section * +WL_EXPORT struct weston_config_section * weston_config_get_section(struct weston_config *config, const char *section, const char *key, const char *value) { @@ -152,8 +151,7 @@ weston_config_get_section(struct weston_config *config, const char *section, return NULL; } -WL_EXPORT -int +WL_EXPORT int weston_config_section_get_int(struct weston_config_section *section, const char *key, int32_t *value, int32_t default_value) @@ -175,8 +173,7 @@ weston_config_section_get_int(struct weston_config_section *section, return 0; } -WL_EXPORT -int +WL_EXPORT int weston_config_section_get_uint(struct weston_config_section *section, const char *key, uint32_t *value, uint32_t default_value) @@ -212,8 +209,7 @@ weston_config_section_get_uint(struct weston_config_section *section, return 0; } -WL_EXPORT -int +WL_EXPORT int weston_config_section_get_color(struct weston_config_section *section, const char *key, uint32_t *color, uint32_t default_color) @@ -250,8 +246,7 @@ weston_config_section_get_color(struct weston_config_section *section, return 0; } -WL_EXPORT -int +WL_EXPORT int weston_config_section_get_double(struct weston_config_section *section, const char *key, double *value, double default_value) @@ -276,8 +271,7 @@ weston_config_section_get_double(struct weston_config_section *section, return 0; } -WL_EXPORT -int +WL_EXPORT int weston_config_section_get_string(struct weston_config_section *section, const char *key, char **value, const char *default_value) @@ -299,8 +293,7 @@ weston_config_section_get_string(struct weston_config_section *section, return 0; } -WL_EXPORT -int +WL_EXPORT int weston_config_section_get_bool(struct weston_config_section *section, const char *key, bool *value, bool default_value) @@ -327,8 +320,7 @@ weston_config_section_get_bool(struct weston_config_section *section, return 0; } -WL_EXPORT -const char * +WL_EXPORT const char * weston_config_get_name_from_env(void) { const char *name; @@ -389,8 +381,7 @@ section_add_entry(struct weston_config_section *section, return entry; } -WL_EXPORT -struct weston_config * +WL_EXPORT struct weston_config * weston_config_parse(const char *name) { FILE *fp; @@ -471,15 +462,13 @@ weston_config_parse(const char *name) return config; } -WL_EXPORT -const char * +WL_EXPORT const char * weston_config_get_full_path(struct weston_config *config) { return config == NULL ? NULL : config->path; } -WL_EXPORT -int +WL_EXPORT int weston_config_next_section(struct weston_config *config, struct weston_config_section **section, const char **name) @@ -502,8 +491,7 @@ weston_config_next_section(struct weston_config *config, return 1; } -WL_EXPORT -void +WL_EXPORT void weston_config_destroy(struct weston_config *config) { struct weston_config_section *s, *next_s; From 3e94836a631e5f4ec34b205acfeafcfccf6ed518 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 11 Mar 2022 17:05:04 +0200 Subject: [PATCH 084/609] tests: add get_double in config-parser tests weston_config_section_get_double() was not covered with tests before. This patch follows the testing style already present in the file. Cannot use ZUC_ASSERT_EQ() here, because that would convert the values to integers before comparison. Luckily, simple strict equality comparison works here, because we are testing conversion to float, not the results of lossy calculations. Signed-off-by: Pekka Paalanen --- tests/config-parser-test.c | 199 +++++++++++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) diff --git a/tests/config-parser-test.c b/tests/config-parser-test.c index 583c83f2..626c01d9 100644 --- a/tests/config-parser-test.c +++ b/tests/config-parser-test.c @@ -32,6 +32,7 @@ #include #include #include +#include #include @@ -119,6 +120,13 @@ static struct zuc_fixture config_test_t1 = { "zero=0\n" "negative=-42\n" "flag=false\n" + "real=4.667\n" + "negreal=-3.2\n" + "expval=24.687E+15\n" + "negexpval=-3e-2\n" + "notanumber=nan\n" + "empty=\n" + "tiny=0.0000000000000000000000000000000000000063548\n" "\n" "[colors]\n" "none=0x00000000\n" @@ -600,6 +608,197 @@ ZUC_TEST_F(config_test_t1, test027, data) ZUC_ASSERT_EQ(ERANGE, errno); } +ZUC_TEST_F(config_test_t1, get_double_number, data) +{ + int r; + double n; + struct weston_config_section *section; + struct weston_config *config = data; + + errno = 0; + section = weston_config_get_section(config, "bar", NULL, NULL); + r = weston_config_section_get_double(section, "number", &n, 600.0); + + ZUC_ASSERT_EQ(0, r); + ZUC_ASSERT_TRUE(5252.0 == n); + ZUC_ASSERT_EQ(0, errno); +} + +ZUC_TEST_F(config_test_t1, get_double_missing, data) +{ + int r; + double n; + struct weston_config_section *section; + struct weston_config *config = data; + + section = weston_config_get_section(config, "bar", NULL, NULL); + r = weston_config_section_get_double(section, "+++", &n, 600.0); + + ZUC_ASSERT_EQ(-1, r); + ZUC_ASSERT_TRUE(600.0 == n); + ZUC_ASSERT_EQ(ENOENT, errno); +} + +ZUC_TEST_F(config_test_t1, get_double_zero, data) +{ + int r; + double n; + struct weston_config_section *section; + struct weston_config *config = data; + + errno = 0; + section = weston_config_get_section(config, "bar", NULL, NULL); + r = weston_config_section_get_double(section, "zero", &n, 600.0); + + ZUC_ASSERT_EQ(0, r); + ZUC_ASSERT_TRUE(0.0 == n); + ZUC_ASSERT_EQ(0, errno); +} + +ZUC_TEST_F(config_test_t1, get_double_negative, data) +{ + int r; + double n; + struct weston_config_section *section; + struct weston_config *config = data; + + errno = 0; + section = weston_config_get_section(config, "bar", NULL, NULL); + r = weston_config_section_get_double(section, "negative", &n, 600.0); + + ZUC_ASSERT_EQ(0, r); + ZUC_ASSERT_TRUE(-42.0 == n); + ZUC_ASSERT_EQ(0, errno); +} + +ZUC_TEST_F(config_test_t1, get_double_flag, data) +{ + int r; + double n; + struct weston_config_section *section; + struct weston_config *config = data; + + errno = 0; + section = weston_config_get_section(config, "bar", NULL, NULL); + r = weston_config_section_get_double(section, "flag", &n, 600.0); + + ZUC_ASSERT_EQ(-1, r); + ZUC_ASSERT_TRUE(600.0 == n); + ZUC_ASSERT_EQ(EINVAL, errno); +} + +ZUC_TEST_F(config_test_t1, get_double_real, data) +{ + int r; + double n; + struct weston_config_section *section; + struct weston_config *config = data; + + errno = 0; + section = weston_config_get_section(config, "bar", NULL, NULL); + r = weston_config_section_get_double(section, "real", &n, 600.0); + + ZUC_ASSERT_EQ(0, r); + ZUC_ASSERT_TRUE(4.667 == n); + ZUC_ASSERT_EQ(0, errno); +} + +ZUC_TEST_F(config_test_t1, get_double_negreal, data) +{ + int r; + double n; + struct weston_config_section *section; + struct weston_config *config = data; + + errno = 0; + section = weston_config_get_section(config, "bar", NULL, NULL); + r = weston_config_section_get_double(section, "negreal", &n, 600.0); + + ZUC_ASSERT_EQ(0, r); + ZUC_ASSERT_TRUE(-3.2 == n); + ZUC_ASSERT_EQ(0, errno); +} + +ZUC_TEST_F(config_test_t1, get_double_expval, data) +{ + int r; + double n; + struct weston_config_section *section; + struct weston_config *config = data; + + errno = 0; + section = weston_config_get_section(config, "bar", NULL, NULL); + r = weston_config_section_get_double(section, "expval", &n, 600.0); + + ZUC_ASSERT_EQ(0, r); + ZUC_ASSERT_TRUE(24.687e+15 == n); + ZUC_ASSERT_EQ(0, errno); +} + +ZUC_TEST_F(config_test_t1, get_double_negexpval, data) +{ + int r; + double n; + struct weston_config_section *section; + struct weston_config *config = data; + + errno = 0; + section = weston_config_get_section(config, "bar", NULL, NULL); + r = weston_config_section_get_double(section, "negexpval", &n, 600.0); + + ZUC_ASSERT_EQ(0, r); + ZUC_ASSERT_TRUE(-3e-2 == n); + ZUC_ASSERT_EQ(0, errno); +} + +ZUC_TEST_F(config_test_t1, get_double_notanumber, data) +{ + int r; + double n; + struct weston_config_section *section; + struct weston_config *config = data; + + errno = 0; + section = weston_config_get_section(config, "bar", NULL, NULL); + r = weston_config_section_get_double(section, "notanumber", &n, 600.0); + + ZUC_ASSERT_EQ(0, r); + ZUC_ASSERT_TRUE(isnan(n)); + ZUC_ASSERT_EQ(0, errno); +} + +ZUC_TEST_F(config_test_t1, get_double_empty, data) +{ + int r; + double n; + struct weston_config_section *section; + struct weston_config *config = data; + + errno = 0; + section = weston_config_get_section(config, "bar", NULL, NULL); + r = weston_config_section_get_double(section, "empty", &n, 600.0); + + ZUC_ASSERT_EQ(0, r); + ZUC_ASSERT_TRUE(0.0 == n); + ZUC_ASSERT_EQ(0, errno); +} + +ZUC_TEST_F(config_test_t1, get_double_tiny, data) +{ + int r; + double n; + struct weston_config_section *section; + struct weston_config *config = data; + + errno = 0; + section = weston_config_get_section(config, "bar", NULL, NULL); + r = weston_config_section_get_double(section, "tiny", &n, 600.0); + + ZUC_ASSERT_EQ(0, r); + ZUC_ASSERT_TRUE(6.3548e-39 == n); + ZUC_ASSERT_EQ(0, errno); +} + ZUC_TEST_F(config_test_t2, doesnt_parse, data) { struct weston_config *config = data; From d284ab0322d6a532b6c790cfc8f76ec47f232325 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Tue, 8 Mar 2022 15:30:04 +0200 Subject: [PATCH 085/609] pipewire,remoting,tests: Replace asprintf w/ str_printf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have a string helper which wraps asprintf(). Uses that one because it clears out the destination string, but also it won't return the number of bytes unlinke asprintf(). Fixes warnings like: warning: ignoring return value of ‘asprintf’ declared with attribute ‘warn_unused_result’. Signed-off-by: Marius Vlad --- pipewire/pipewire-plugin.c | 3 ++- remoting/remoting-plugin.c | 3 ++- tests/weston-test-fixture-compositor.c | 37 ++++++++++++++------------ 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/pipewire/pipewire-plugin.c b/pipewire/pipewire-plugin.c index b194f4c6..23cea2e8 100644 --- a/pipewire/pipewire-plugin.c +++ b/pipewire/pipewire-plugin.c @@ -29,6 +29,7 @@ #include "backend.h" #include "libweston-internal.h" #include "shared/timespec-util.h" +#include "shared/string-helpers.h" #include #include @@ -550,7 +551,7 @@ pipewire_output_create(struct weston_compositor *c, char *name) output->pipewire = pipewire; wl_list_insert(pipewire->output_list.prev, &output->link); - asprintf(&remoting_name, "%s-%s", connector_name, name); + str_printf(&remoting_name, "%s-%s", connector_name, name); weston_head_init(head, remoting_name); weston_head_set_subpixel(head, WL_OUTPUT_SUBPIXEL_NONE); weston_head_set_monitor_strings(head, make, model, serial_number); diff --git a/remoting/remoting-plugin.c b/remoting/remoting-plugin.c index e5f5ca4a..7d1d00f4 100644 --- a/remoting/remoting-plugin.c +++ b/remoting/remoting-plugin.c @@ -47,6 +47,7 @@ #include "shared/helpers.h" #include "shared/timespec-util.h" #include "shared/weston-drm-fourcc.h" +#include "shared/string-helpers.h" #include "backend.h" #include "libweston-internal.h" @@ -777,7 +778,7 @@ remoting_output_create(struct weston_compositor *c, char *name) output->remoting = remoting; wl_list_insert(remoting->output_list.prev, &output->link); - asprintf(&remoting_name, "%s-%s", connector_name, name); + str_printf(&remoting_name, "%s-%s", connector_name, name); weston_head_init(head, remoting_name); weston_head_set_subpixel(head, WL_OUTPUT_SUBPIXEL_NONE); weston_head_set_monitor_strings(head, make, model, serial_number); diff --git a/tests/weston-test-fixture-compositor.c b/tests/weston-test-fixture-compositor.c index d4ad369d..64325fb1 100644 --- a/tests/weston-test-fixture-compositor.c +++ b/tests/weston-test-fixture-compositor.c @@ -38,6 +38,7 @@ #include #include "shared/helpers.h" +#include "shared/string-helpers.h" #include "weston-test-fixture-compositor.h" #include "weston.h" #include "test-config.h" @@ -116,7 +117,8 @@ get_lock_path(void) return NULL; } - if (asprintf(&lock_path, "%s/%s", env_path, suffix) == -1) + str_printf(&lock_path, "%s/%s", env_path, suffix); + if (!lock_path) return NULL; return lock_path; @@ -345,10 +347,10 @@ execute_compositor(const struct compositor_setup *setup, prog_args_init(&args); /* argv[0] */ - asprintf(&tmp, "weston-%s", setup->testset_name); + str_printf(&tmp, "weston-%s", setup->testset_name); prog_args_take(&args, tmp); - asprintf(&tmp, "--backend=%s", backend_to_str(setup->backend)); + str_printf(&tmp, "--backend=%s", backend_to_str(setup->backend)); prog_args_take(&args, tmp); if (setup->backend == WESTON_BACKEND_DRM) { @@ -362,7 +364,7 @@ execute_compositor(const struct compositor_setup *setup, ret = RESULT_SKIP; goto out; } - asprintf(&tmp, "--drm-device=%s", drm_device); + str_printf(&tmp, "--drm-device=%s", drm_device); prog_args_take(&args, tmp); prog_args_take(&args, strdup("--seat=weston-test-seat")); @@ -379,36 +381,36 @@ execute_compositor(const struct compositor_setup *setup, /* Test suite needs the debug protocol to be able to take screenshots */ prog_args_take(&args, strdup("--debug")); - asprintf(&tmp, "--socket=%s", setup->testset_name); + str_printf(&tmp, "--socket=%s", setup->testset_name); prog_args_take(&args, tmp); - asprintf(&tmp, "--modules=%s%s%s", TESTSUITE_PLUGIN_PATH, - setup->extra_module ? "," : "", - setup->extra_module ? setup->extra_module : ""); + str_printf(&tmp, "--modules=%s%s%s", TESTSUITE_PLUGIN_PATH, + setup->extra_module ? "," : "", + setup->extra_module ? setup->extra_module : ""); prog_args_take(&args, tmp); if (setup->backend != WESTON_BACKEND_DRM && setup->backend != WESTON_BACKEND_FBDEV) { - asprintf(&tmp, "--width=%d", setup->width); + str_printf(&tmp, "--width=%d", setup->width); prog_args_take(&args, tmp); - asprintf(&tmp, "--height=%d", setup->height); + str_printf(&tmp, "--height=%d", setup->height); prog_args_take(&args, tmp); } if (setup->scale != 1) { - asprintf(&tmp, "--scale=%d", setup->scale); + str_printf(&tmp, "--scale=%d", setup->scale); prog_args_take(&args, tmp); } if (setup->transform != WL_OUTPUT_TRANSFORM_NORMAL) { - asprintf(&tmp, "--transform=%s", - transform_to_str(setup->transform)); + str_printf(&tmp, "--transform=%s", + transform_to_str(setup->transform)); prog_args_take(&args, tmp); } if (setup->config_file) { - asprintf(&tmp, "--config=%s", setup->config_file); + str_printf(&tmp, "--config=%s", setup->config_file); prog_args_take(&args, tmp); free(setup->config_file); } else { @@ -419,11 +421,11 @@ execute_compositor(const struct compositor_setup *setup, if (ctmp) prog_args_take(&args, strdup(ctmp)); - asprintf(&tmp, "--shell=%s", shell_to_str(setup->shell)); + str_printf(&tmp, "--shell=%s", shell_to_str(setup->shell)); prog_args_take(&args, tmp); if (setup->logging_scopes) { - asprintf(&tmp, "--logger-scopes=%s", setup->logging_scopes); + str_printf(&tmp, "--logger-scopes=%s", setup->logging_scopes); prog_args_take(&args, tmp); } @@ -472,7 +474,8 @@ open_ini_file(struct compositor_setup *setup) wd = realpath(".", NULL); assert(wd); - if (asprintf(&tmp_path, "%s/%s.ini", wd, setup->testset_name) == -1) { + str_printf(&tmp_path, "%s/%s.ini", wd, setup->testset_name); + if (!tmp_path) { fprintf(stderr, "Fail formatting Weston.ini file name.\n"); goto out; } From 79f73d22478dbc0ef38795a4b02ba0d0f8febeb2 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 11 Mar 2022 11:23:01 +0200 Subject: [PATCH 086/609] man: move gbm-format from weston.ini(5) to weston-drm(7) This option is used only with the DRM-backend. Options in weston.ini(5) should be either generic or for backends that do not have their own man page yet. Signed-off-by: Pekka Paalanen --- man/weston-drm.man | 9 +++++++++ man/weston.ini.man | 10 ---------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/man/weston-drm.man b/man/weston-drm.man index d01860cf..b425a3b0 100644 --- a/man/weston-drm.man +++ b/man/weston-drm.man @@ -43,6 +43,15 @@ section of . The DRM backend uses the following entries from .BR weston.ini . +.SS Section core +.TP +\fBgbm-format\fR=\fIformat\fR +sets the GBM format used for the framebuffer for the GBM backend. Can be +.B xrgb8888, +.B xrgb2101010, +.B rgb565. +By default, xrgb8888 is used. + .SS Section output .TP \fBname\fR=\fIconnector\fR diff --git a/man/weston.ini.man b/man/weston.ini.man index 98403e48..b39b51ee 100644 --- a/man/weston.ini.man +++ b/man/weston.ini.man @@ -148,16 +148,6 @@ target vertical blank, increasing output latency. The default value is 7 milliseconds. The allowed range is from -10 to 1000 milliseconds. Using a negative value will force the compositor to always miss the target vblank. .TP 7 -.BI "gbm-format="format -sets the GBM format used for the framebuffer for the GBM backend. Can be -.B xrgb8888, -.B xrgb2101010, -.B rgb565. -By default, xrgb8888 is used. -.RS -.PP -.RE -.TP 7 .BI "idle-time="seconds sets Weston's idle timeout in seconds. This idle timeout is the time after which Weston will enter an "inactive" mode and screen will fade to From 1f5e19fab309c7383b91873a34bf783de3be41a2 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 11 Mar 2022 11:23:01 +0200 Subject: [PATCH 087/609] man: move pageflip-timeout from weston.ini(5) to weston-drm(7) This option is used only with the DRM-backend. Options in weston.ini(5) should be either generic or for backends that do not have their own man page yet. Signed-off-by: Pekka Paalanen --- man/weston-drm.man | 5 +++++ man/weston.ini.man | 5 ----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/man/weston-drm.man b/man/weston-drm.man index b425a3b0..1adada57 100644 --- a/man/weston-drm.man +++ b/man/weston-drm.man @@ -51,6 +51,11 @@ sets the GBM format used for the framebuffer for the GBM backend. Can be .B xrgb2101010, .B rgb565. By default, xrgb8888 is used. +.TP +\fBpageflip-timeout\fR=\fImilliseconds\fR +sets Weston's pageflip timeout in milliseconds. This sets a timer to exit +gracefully with a log message and an exit code of 1 in case the DRM driver is +non-responsive. Setting it to 0 disables this feature. .SS Section output .TP diff --git a/man/weston.ini.man b/man/weston.ini.man index b39b51ee..d7fc42b3 100644 --- a/man/weston.ini.man +++ b/man/weston.ini.man @@ -167,11 +167,6 @@ set to 300 seconds. .BI "require-input=" true require an input device for launch .TP 7 -.BI "pageflip-timeout="milliseconds -sets Weston's pageflip timeout in milliseconds. This sets a timer to exit -gracefully with a log message and an exit code of 1 in case the DRM driver is -non-responsive. Setting it to 0 disables this feature. -.TP 7 .BI "wait-for-debugger=" true Raises SIGSTOP before initializing the compositor. This allows the user to attach with a debugger and continue execution by sending SIGCONT. This is From ce059cffdb0cbf80b06da2553be2d2f5e4fec085 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 11 Mar 2022 11:40:44 +0200 Subject: [PATCH 088/609] man: expand on gbm-format Since 62a9436417eb4e1ba53f5c54ef9a0e8b5a4eb53f the gbm-format option has recognized all pixel formats listed in libweston/pixel-formats.c. Clarify what pixel formats can be used. Signed-off-by: Pekka Paalanen --- man/weston-drm.man | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/man/weston-drm.man b/man/weston-drm.man index 1adada57..017124aa 100644 --- a/man/weston-drm.man +++ b/man/weston-drm.man @@ -46,11 +46,20 @@ The DRM backend uses the following entries from .SS Section core .TP \fBgbm-format\fR=\fIformat\fR -sets the GBM format used for the framebuffer for the GBM backend. Can be -.B xrgb8888, -.B xrgb2101010, -.B rgb565. -By default, xrgb8888 is used. +Sets the default pixel format for DRM KMS framebuffers. +.IR Format " can be" +.BR xrgb8888 ", " xrgb2101010 ", " rgb565 +or others. Weston recognizes the names of most pixel formats defined by +the kernel DRM subsystem in +.B drm_fourcc.h +header without the DRM_FORMAT_ prefix. +The actually supported pixel formats depend on the DRM driver and hardware, +and the renderer used. Using Pixman-renderer, DRM-backend supports only +.BR xrgb8888 " and " rgb565 . +The formats supported with GL-renderer depend on the EGL and OpenGL ES 2 or 3 +implementations. The names are case-insensitive. + +.RB "If not specified, " xrgb8888 " is used." .TP \fBpageflip-timeout\fR=\fImilliseconds\fR sets Weston's pageflip timeout in milliseconds. This sets a timer to exit From bfefe8e8d49500309ef3b8879cb06c6c60b04bbd Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 11 Mar 2022 12:02:03 +0200 Subject: [PATCH 089/609] man: add gbm-format in output section Looks like at least from 2016 onwards the gbm-format option has also been recognized in an output section. Time to document that. Signed-off-by: Pekka Paalanen --- man/weston-drm.man | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/man/weston-drm.man b/man/weston-drm.man index 017124aa..5947714c 100644 --- a/man/weston-drm.man +++ b/man/weston-drm.man @@ -59,7 +59,8 @@ and the renderer used. Using Pixman-renderer, DRM-backend supports only The formats supported with GL-renderer depend on the EGL and OpenGL ES 2 or 3 implementations. The names are case-insensitive. -.RB "If not specified, " xrgb8888 " is used." +.RB "If not specified, " xrgb8888 " is used. See also " gbm-format " in" +.BR output " section." .TP \fBpageflip-timeout\fR=\fImilliseconds\fR sets Weston's pageflip timeout in milliseconds. This sets a timer to exit @@ -131,6 +132,12 @@ and possibly flipped. Possible values are .BR flipped ", " flipped-rotate-90 ", " flipped-rotate-180 ", and " .BR flipped-rotate-270 . .TP +\fBgbm-format\fR=\fIformat\fR +Set the DRM KMS framebuffer format for this specific output. If not set, +.RB "the value from " gbm-format " option in " core " section is used." +.RI "For the possible values for " format " see " +.BR gbm-format " option in " core " section." +.TP \fBpixman-shadow\fR=\fIboolean\fR If using the Pixman-renderer, use shadow framebuffers. Defaults to .BR true . From 18df9108ea2ed6fa1ac82ce1d146dedfa9652d88 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 11 Mar 2022 12:05:38 +0200 Subject: [PATCH 090/609] man: replace tablet shell with kiosk shell Tablet shell was removed in 873b515aeee77ff072f5ae42fef34cab76f8bf11 in 2013. Time to remove the hopefully last reference to it. We also gained kiosk shell in the mean time, so mention that instead. Yes, it's a bit of a lie, because we also have ivi-shell and fullscreen-shell, but I heard they might be on their way out, so I didn't add them here. Would be nice to add kiosk-shell in the SHELLS section too, but in this patch I am only concerned about dropping the tablet shell reference. Signed-off-by: Pekka Paalanen --- man/weston.man | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/weston.man b/man/weston.man index 584bfb5a..654c0dae 100644 --- a/man/weston.man +++ b/man/weston.man @@ -14,7 +14,7 @@ modesetting via DRM), as an X client, or inside another Wayland server instance. Weston supports fundamentally different graphical user interface paradigms via -shell plugins. Two plugins are provided: the desktop shell, and the tablet +shell plugins. Two plugins are provided: the desktop shell, and the kiosk shell. Weston also supports X clients via From b3ba1becba806a3557b8acbf8c42f1782cffdea2 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 15 Mar 2022 11:17:56 +0200 Subject: [PATCH 091/609] libweston: remove fbdev backend Fbdev backend was deprecated in the Weston 10.0.0 release with 6338dbd5816689b2f08f48b359a972e16ff038d8. Before that, I suggested already in 2019 to remove it, but it was too soon then. Now it seems the final voices asking for fbdev to be kept have been satisfied, see the linked issue. Fbdev-backend uses a kernel graphics UAPI (fbdev) which is sub-par for a Wayland compositor: you cannot do GPU accelerated graphics in any reasonable way, no hotplug support, multi-output support is tedious, and so on. Most importantly, Linux has deprecated fbdev a long time ago due to the UAPI fitting modern systems and use cases very poorly, but cannot get rid of it if any users remain. Let's do here what we can to reduce fbdev usage. I am doing color management related additions to libweston which require adding checks to every backend. One backend less is less churn to write and review. Libweston major version has already been bumped to 11, so the next release will be Weston 11, without fbdev. enum weston_compositor_backend entries change their numerical values. Fixes: https://gitlab.freedesktop.org/wayland/weston/-/issues/581 Signed-off-by: Pekka Paalanen --- .gitlab-ci.yml | 1 - README.md | 4 +- compositor/main.c | 61 -- doc/sphinx/toc/running-weston.rst | 3 +- include/libweston/backend-fbdev.h | 69 -- include/libweston/libweston.h | 1 - include/libweston/meson.build | 1 - libweston/backend-fbdev/fbdev.c | 996 ------------------------- libweston/backend-fbdev/meson.build | 33 - libweston/compositor.c | 1 - libweston/meson.build | 1 - man/weston.ini.man | 1 - meson_options.txt | 8 +- tests/weston-test-fixture-compositor.c | 11 +- 14 files changed, 4 insertions(+), 1187 deletions(-) delete mode 100644 include/libweston/backend-fbdev.h delete mode 100644 libweston/backend-fbdev/fbdev.c delete mode 100644 libweston/backend-fbdev/meson.build diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 46239fca..51636501 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -283,7 +283,6 @@ aarch64-debian-container_prep: -Dwerror=true -Dtest-skip-is-failure=true -Dlauncher-libseat=true - -Ddeprecated-backend-fbdev=true after_script: - ninja -C "$BUILDDIR" coverage-html > "$BUILDDIR/meson-logs/ninja-coverage-html.txt" - ninja -C "$BUILDDIR" coverage-xml diff --git a/README.md b/README.md index 2d093825..5613ed3e 100644 --- a/README.md +++ b/README.md @@ -287,7 +287,7 @@ Details: - Child process execution and management will be outside of libweston. -- The different backends (drm, fbdev, x11, etc) will be an internal +- The different backends (drm, x11, etc) will be an internal detail of libweston. Libweston will not support third party backends. However, hosting programs need to handle backend-specific configuration due to differences in behaviour and @@ -325,8 +325,6 @@ would be roughly like this: - xwayland (depends on X11/xcb libs) -- fbdev-backend (depends on libudev...) - - rdp-backend (depends on freerdp) - weston (the executable, not parallel-installable): diff --git a/compositor/main.c b/compositor/main.c index 3b15a959..91b3d43f 100644 --- a/compositor/main.c +++ b/compositor/main.c @@ -58,7 +58,6 @@ #include #include #include -#include #include #include #include @@ -620,9 +619,6 @@ usage(int error_code) #if defined(BUILD_DRM_COMPOSITOR) "\t\t\t\tdrm-backend.so\n" #endif -#if defined(BUILD_FBDEV_COMPOSITOR) - "\t\t\t\tfbdev-backend.so\n" -#endif #if defined(BUILD_HEADLESS_COMPOSITOR) "\t\t\t\theadless-backend.so\n" #endif @@ -665,14 +661,6 @@ usage(int error_code) " --continue-without-input\tAllow the compositor to start without input devices\n\n"); #endif -#if defined(BUILD_FBDEV_COMPOSITOR) - fprintf(out, - "Options for fbdev-backend.so:\n\n" - " --device=DEVICE\tThe framebuffer device to use\n" - " --seat=SEAT\t\tThe seat that weston should run on, instead of the seat defined in XDG_SEAT\n" - "\n"); -#endif - #if defined(BUILD_HEADLESS_COMPOSITOR) fprintf(out, "Options for headless-backend.so:\n\n" @@ -2822,53 +2810,6 @@ load_rdp_backend(struct weston_compositor *c, return ret; } -static int -fbdev_backend_output_configure(struct weston_output *output) -{ - struct weston_config *wc = wet_get_config(output->compositor); - struct weston_config_section *section; - - section = weston_config_get_section(wc, "output", "name", "fbdev"); - - if (wet_output_set_transform(output, section, - WL_OUTPUT_TRANSFORM_NORMAL, - UINT32_MAX) < 0) { - return -1; - } - - weston_output_set_scale(output, 1); - - return 0; -} - -static int -load_fbdev_backend(struct weston_compositor *c, - int *argc, char **argv, struct weston_config *wc) -{ - struct weston_fbdev_backend_config config = {{ 0, }}; - int ret = 0; - - const struct weston_option fbdev_options[] = { - { WESTON_OPTION_STRING, "device", 0, &config.device }, - { WESTON_OPTION_STRING, "seat", 0, &config.seat_id }, - }; - - parse_options(fbdev_options, ARRAY_LENGTH(fbdev_options), argc, argv); - - config.base.struct_version = WESTON_FBDEV_BACKEND_CONFIG_VERSION; - config.base.struct_size = sizeof(struct weston_fbdev_backend_config); - config.configure_device = configure_input_device; - - wet_set_simple_head_configurator(c, fbdev_backend_output_configure); - - /* load the actual wayland backend and configure it */ - ret = weston_compositor_load_backend(c, WESTON_BACKEND_FBDEV, - &config.base); - - free(config.device); - return ret; -} - static int x11_backend_output_configure(struct weston_output *output) { @@ -3114,8 +3055,6 @@ load_backend(struct weston_compositor *compositor, const char *backend, return load_headless_backend(compositor, argc, argv, config); else if (strstr(backend, "rdp-backend.so")) return load_rdp_backend(compositor, argc, argv, config); - else if (strstr(backend, "fbdev-backend.so")) - return load_fbdev_backend(compositor, argc, argv, config); else if (strstr(backend, "drm-backend.so")) return load_drm_backend(compositor, argc, argv, config); else if (strstr(backend, "x11-backend.so")) diff --git a/doc/sphinx/toc/running-weston.rst b/doc/sphinx/toc/running-weston.rst index 15babf9d..39e95f1b 100644 --- a/doc/sphinx/toc/running-weston.rst +++ b/doc/sphinx/toc/running-weston.rst @@ -6,7 +6,7 @@ underlying environment where it runs on. Ultimately, the back-end is responsible for handling the input and generate an output. Weston, as a libweston user, can be run on different back-ends, including nested, by using the wayland backend, but also on X11 or on a stand-alone back-end like -DRM/KMS and now deprecated fbdev. +DRM/KMS. In most cases, people should allow Weston to choose the backend automatically as it will produce the best results. That happens for instance when running @@ -28,7 +28,6 @@ Available back-ends: * **x11** -- run as a x11 application, nested in a X11 display server instance * **rdp** -- run as an RDP server without local input or output * **headless** -- run without input or output, useful for test suite -* **fbdev** -- run stand-alone on fbdev/evdev (deprecated) The job of gathering all the surfaces (windows) being displayed on an output and stitching them together is performed by a *renderer*. By doing so, it is diff --git a/include/libweston/backend-fbdev.h b/include/libweston/backend-fbdev.h deleted file mode 100644 index 3ed6b0a0..00000000 --- a/include/libweston/backend-fbdev.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright © 2016 Benoit Gschwind - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef WESTON_COMPOSITOR_FBDEV_H -#define WESTON_COMPOSITOR_FBDEV_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -#include - -#define WESTON_FBDEV_BACKEND_CONFIG_VERSION 3 - -struct libinput_device; - -struct weston_fbdev_backend_config { - struct weston_backend_config base; - - char *device; - - /** Callback used to configure input devices. - * - * This function will be called by the backend when a new input device - * needs to be configured. - * If NULL the device will use the default configuration. - */ - void (*configure_device)(struct weston_compositor *compositor, - struct libinput_device *device); - - /** The seat to be used for input and output. - * - * If seat_id is NULL, the seat is taken from XDG_SEAT environment - * variable. If neither is set, "seat0" is used. The backend will - * take ownership of the seat_id pointer and will free it on - * backend destruction. - */ - char *seat_id; -}; - -#ifdef __cplusplus -} -#endif - -#endif /* WESTON_COMPOSITOR_FBDEV_H */ diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 849afd57..18c1f8af 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -1842,7 +1842,6 @@ weston_compositor_add_destroy_listener_once(struct weston_compositor *compositor enum weston_compositor_backend { WESTON_BACKEND_DRM, - WESTON_BACKEND_FBDEV, WESTON_BACKEND_HEADLESS, WESTON_BACKEND_RDP, WESTON_BACKEND_WAYLAND, diff --git a/include/libweston/meson.build b/include/libweston/meson.build index 1ad459bb..8ae10011 100644 --- a/include/libweston/meson.build +++ b/include/libweston/meson.build @@ -12,7 +12,6 @@ install_headers( ) backend_drm_h = files('backend-drm.h') -backend_fbdev_h = files('backend-fbdev.h') backend_headless_h = files('backend-headless.h') backend_rdp_h = files('backend-rdp.h') backend_wayland_h = files('backend-wayland.h') diff --git a/libweston/backend-fbdev/fbdev.c b/libweston/backend-fbdev/fbdev.c deleted file mode 100644 index 0ec5af0c..00000000 --- a/libweston/backend-fbdev/fbdev.c +++ /dev/null @@ -1,996 +0,0 @@ -/* - * Copyright © 2008-2011 Kristian Høgsberg - * Copyright © 2011 Intel Corporation - * Copyright © 2012 Raspberry Pi Foundation - * Copyright © 2013 Philip Withnall - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "shared/helpers.h" -#include -#include -#include "launcher-util.h" -#include "pixman-renderer.h" -#include "libinput-seat.h" -#include "presentation-time-server-protocol.h" - -struct fbdev_backend { - struct weston_backend base; - struct weston_compositor *compositor; - uint32_t prev_state; - - struct udev *udev; - struct udev_input input; - uint32_t output_transform; - struct wl_listener session_listener; -}; - -struct fbdev_screeninfo { - unsigned int x_resolution; /* pixels, visible area */ - unsigned int y_resolution; /* pixels, visible area */ - unsigned int width_mm; /* visible screen width in mm */ - unsigned int height_mm; /* visible screen height in mm */ - unsigned int bits_per_pixel; - - size_t buffer_length; /* length of frame buffer memory in bytes */ - size_t line_length; /* length of a line in bytes */ - char id[16]; /* screen identifier */ - - pixman_format_code_t pixel_format; /* frame buffer pixel format */ - unsigned int refresh_rate; /* Hertz */ -}; - -struct fbdev_head { - struct weston_head base; - - /* Frame buffer details. */ - char *device; - struct fbdev_screeninfo fb_info; -}; - -struct fbdev_output { - struct fbdev_backend *backend; - struct weston_output base; - - struct weston_mode mode; - struct wl_event_source *finish_frame_timer; - - /* framebuffer mmap details */ - size_t buffer_length; - void *fb; - - /* pixman details. */ - pixman_image_t *hw_surface; -}; - -static const char default_seat[] = "seat0"; - -static inline struct fbdev_head * -to_fbdev_head(struct weston_head *base) -{ - return container_of(base, struct fbdev_head, base); -} - -static inline struct fbdev_output * -to_fbdev_output(struct weston_output *base) -{ - return container_of(base, struct fbdev_output, base); -} - -static inline struct fbdev_backend * -to_fbdev_backend(struct weston_compositor *base) -{ - return container_of(base->backend, struct fbdev_backend, base); -} - -static struct fbdev_head * -fbdev_output_get_head(struct fbdev_output *output) -{ - if (wl_list_length(&output->base.head_list) != 1) - return NULL; - - return container_of(output->base.head_list.next, - struct fbdev_head, base.output_link); -} - -static int -fbdev_output_start_repaint_loop(struct weston_output *output) -{ - struct timespec ts; - - weston_compositor_read_presentation_clock(output->compositor, &ts); - weston_output_finish_frame(output, &ts, WP_PRESENTATION_FEEDBACK_INVALID); - - return 0; -} - -static int -fbdev_output_repaint(struct weston_output *base, pixman_region32_t *damage, - void *repaint_data) -{ - struct fbdev_output *output = to_fbdev_output(base); - struct weston_compositor *ec = output->base.compositor; - - /* Repaint the damaged region onto the back buffer. */ - pixman_renderer_output_set_buffer(base, output->hw_surface); - ec->renderer->repaint_output(base, damage); - - /* Update the damage region. */ - pixman_region32_subtract(&ec->primary_plane.damage, - &ec->primary_plane.damage, damage); - - /* Schedule the end of the frame. We do not sync this to the frame - * buffer clock because users who want that should be using the DRM - * compositor. FBIO_WAITFORVSYNC blocks and FB_ACTIVATE_VBL requires - * panning, which is broken in most kernel drivers. - * - * Finish the frame synchronised to the specified refresh rate. The - * refresh rate is given in mHz and the interval in ms. */ - wl_event_source_timer_update(output->finish_frame_timer, - 1000000 / output->mode.refresh); - - return 0; -} - -static int -finish_frame_handler(void *data) -{ - struct fbdev_output *output = data; - struct timespec ts; - - weston_compositor_read_presentation_clock(output->base.compositor, &ts); - weston_output_finish_frame(&output->base, &ts, 0); - - return 1; -} - -static pixman_format_code_t -calculate_pixman_format(struct fb_var_screeninfo *vinfo, - struct fb_fix_screeninfo *finfo) -{ - /* Calculate the pixman format supported by the frame buffer from the - * buffer's metadata. Return 0 if no known pixman format is supported - * (since this has depth 0 it's guaranteed to not conflict with any - * actual pixman format). - * - * Documentation on the vinfo and finfo structures: - * http://www.mjmwired.net/kernel/Documentation/fb/api.txt - * - * TODO: Try a bit harder to support other formats, including setting - * the preferred format in the hardware. */ - int type; - - weston_log("Calculating pixman format from:\n" - STAMP_SPACE " - type: %i (aux: %i)\n" - STAMP_SPACE " - visual: %i\n" - STAMP_SPACE " - bpp: %i (grayscale: %i)\n" - STAMP_SPACE " - red: offset: %i, length: %i, MSB: %i\n" - STAMP_SPACE " - green: offset: %i, length: %i, MSB: %i\n" - STAMP_SPACE " - blue: offset: %i, length: %i, MSB: %i\n" - STAMP_SPACE " - transp: offset: %i, length: %i, MSB: %i\n", - finfo->type, finfo->type_aux, finfo->visual, - vinfo->bits_per_pixel, vinfo->grayscale, - vinfo->red.offset, vinfo->red.length, vinfo->red.msb_right, - vinfo->green.offset, vinfo->green.length, - vinfo->green.msb_right, - vinfo->blue.offset, vinfo->blue.length, - vinfo->blue.msb_right, - vinfo->transp.offset, vinfo->transp.length, - vinfo->transp.msb_right); - - /* We only handle packed formats at the moment. */ - if (finfo->type != FB_TYPE_PACKED_PIXELS) - return 0; - - /* We only handle true-colour frame buffers at the moment. */ - switch(finfo->visual) { - case FB_VISUAL_TRUECOLOR: - case FB_VISUAL_DIRECTCOLOR: - if (vinfo->grayscale != 0) - return 0; - break; - default: - return 0; - } - - /* We only support formats with MSBs on the left. */ - if (vinfo->red.msb_right != 0 || vinfo->green.msb_right != 0 || - vinfo->blue.msb_right != 0) - return 0; - - /* Work out the format type from the offsets. We only support RGBA, ARGB - * and ABGR at the moment. */ - type = PIXMAN_TYPE_OTHER; - - if ((vinfo->transp.offset >= vinfo->red.offset || - vinfo->transp.length == 0) && - vinfo->red.offset >= vinfo->green.offset && - vinfo->green.offset >= vinfo->blue.offset) - type = PIXMAN_TYPE_ARGB; - else if (vinfo->red.offset >= vinfo->green.offset && - vinfo->green.offset >= vinfo->blue.offset && - vinfo->blue.offset >= vinfo->transp.offset) - type = PIXMAN_TYPE_RGBA; - else if (vinfo->transp.offset >= vinfo->blue.offset && - vinfo->blue.offset >= vinfo->green.offset && - vinfo->green.offset >= vinfo->red.offset) - type = PIXMAN_TYPE_ABGR; - - if (type == PIXMAN_TYPE_OTHER) - return 0; - - /* Build the format. */ - return PIXMAN_FORMAT(vinfo->bits_per_pixel, type, - vinfo->transp.length, - vinfo->red.length, - vinfo->green.length, - vinfo->blue.length); -} - -static int -calculate_refresh_rate(struct fb_var_screeninfo *vinfo) -{ - uint64_t quot; - - /* Calculate monitor refresh rate. Default is 60 Hz. Units are mHz. */ - quot = (vinfo->upper_margin + vinfo->lower_margin + vinfo->yres); - quot *= (vinfo->left_margin + vinfo->right_margin + vinfo->xres); - quot *= vinfo->pixclock; - - if (quot > 0) { - uint64_t refresh_rate; - - refresh_rate = 1000000000000000LLU / quot; - if (refresh_rate > 200000) - refresh_rate = 200000; /* cap at 200 Hz */ - - if (refresh_rate >= 1000) /* at least 1 Hz */ - return refresh_rate; - } - - return 60 * 1000; /* default to 60 Hz */ -} - -static int -fbdev_query_screen_info(int fd, struct fbdev_screeninfo *info) -{ - struct fb_var_screeninfo varinfo; - struct fb_fix_screeninfo fixinfo; - - /* Probe the device for screen information. */ - if (ioctl(fd, FBIOGET_FSCREENINFO, &fixinfo) < 0 || - ioctl(fd, FBIOGET_VSCREENINFO, &varinfo) < 0) { - return -1; - } - - /* Store the pertinent data. */ - info->x_resolution = varinfo.xres; - info->y_resolution = varinfo.yres; - info->width_mm = varinfo.width; - info->height_mm = varinfo.height; - info->bits_per_pixel = varinfo.bits_per_pixel; - - info->buffer_length = fixinfo.smem_len; - info->line_length = fixinfo.line_length; - strncpy(info->id, fixinfo.id, sizeof(info->id)); - info->id[sizeof(info->id)-1] = '\0'; - - info->pixel_format = calculate_pixman_format(&varinfo, &fixinfo); - info->refresh_rate = calculate_refresh_rate(&varinfo); - - if (info->pixel_format == 0) { - weston_log("Frame buffer uses an unsupported format.\n"); - return -1; - } - - return 1; -} - -static int -fbdev_set_screen_info(int fd, struct fbdev_screeninfo *info) -{ - struct fb_var_screeninfo varinfo; - - /* Grab the current screen information. */ - if (ioctl(fd, FBIOGET_VSCREENINFO, &varinfo) < 0) { - return -1; - } - - /* Update the information. */ - varinfo.xres = info->x_resolution; - varinfo.yres = info->y_resolution; - varinfo.width = info->width_mm; - varinfo.height = info->height_mm; - varinfo.bits_per_pixel = info->bits_per_pixel; - - /* Try to set up an ARGB (x8r8g8b8) pixel format. */ - varinfo.grayscale = 0; - varinfo.transp.offset = 24; - varinfo.transp.length = 0; - varinfo.transp.msb_right = 0; - varinfo.red.offset = 16; - varinfo.red.length = 8; - varinfo.red.msb_right = 0; - varinfo.green.offset = 8; - varinfo.green.length = 8; - varinfo.green.msb_right = 0; - varinfo.blue.offset = 0; - varinfo.blue.length = 8; - varinfo.blue.msb_right = 0; - - /* Set the device's screen information. */ - if (ioctl(fd, FBIOPUT_VSCREENINFO, &varinfo) < 0) { - return -1; - } - - return 1; -} - -static int -fbdev_wakeup_screen(int fd, struct fbdev_screeninfo *info) -{ - struct fb_var_screeninfo varinfo; - - /* Grab the current screen information. */ - if (ioctl(fd, FBIOGET_VSCREENINFO, &varinfo) < 0) { - return -1; - } - - /* force the framebuffer to wake up */ - varinfo.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE; - - /* Set the device's screen information. */ - if (ioctl(fd, FBIOPUT_VSCREENINFO, &varinfo) < 0) { - return -1; - } - - return 1; -} - -/* Returns an FD for the frame buffer device. */ -static int -fbdev_frame_buffer_open(const char *fb_dev, - struct fbdev_screeninfo *screen_info) -{ - int fd = -1; - - weston_log("Opening fbdev frame buffer.\n"); - - /* Open the frame buffer device. */ - fd = open(fb_dev, O_RDWR | O_CLOEXEC); - if (fd < 0) { - weston_log("Failed to open frame buffer device ‘%s’: %s\n", - fb_dev, strerror(errno)); - return -1; - } - - /* Grab the screen info. */ - if (fbdev_query_screen_info(fd, screen_info) < 0) { - weston_log("Failed to get frame buffer info: %s\n", - strerror(errno)); - - close(fd); - return -1; - } - - /* Attempt to wake up the framebuffer device, needed for secondary - * framebuffer devices */ - if (fbdev_wakeup_screen(fd, screen_info) < 0) { - weston_log("Failed to activate framebuffer display. " - "Attempting to open output anyway.\n"); - } - - - return fd; -} - -/* Closes the FD on success or failure. */ -static int -fbdev_frame_buffer_map(struct fbdev_output *output, int fd) -{ - struct fbdev_head *head; - int retval = -1; - - head = fbdev_output_get_head(output); - - weston_log("Mapping fbdev frame buffer.\n"); - - /* Map the frame buffer. Write-only mode, since we don't want to read - * anything back (because it's slow). */ - output->buffer_length = head->fb_info.buffer_length; - output->fb = mmap(NULL, output->buffer_length, - PROT_WRITE, MAP_SHARED, fd, 0); - if (output->fb == MAP_FAILED) { - weston_log("Failed to mmap frame buffer: %s\n", - strerror(errno)); - output->fb = NULL; - goto out_close; - } - - /* Create a pixman image to wrap the memory mapped frame buffer. */ - output->hw_surface = - pixman_image_create_bits(head->fb_info.pixel_format, - head->fb_info.x_resolution, - head->fb_info.y_resolution, - output->fb, - head->fb_info.line_length); - if (output->hw_surface == NULL) { - weston_log("Failed to create surface for frame buffer.\n"); - goto out_unmap; - } - - /* Success! */ - retval = 0; - -out_unmap: - if (retval != 0 && output->fb != NULL) { - munmap(output->fb, output->buffer_length); - output->fb = NULL; - } - -out_close: - if (fd >= 0) - close(fd); - - return retval; -} - -static void -fbdev_frame_buffer_unmap(struct fbdev_output *output) -{ - if (!output->fb) { - assert(!output->hw_surface); - return; - } - - weston_log("Unmapping fbdev frame buffer.\n"); - - if (output->hw_surface) - pixman_image_unref(output->hw_surface); - output->hw_surface = NULL; - - if (munmap(output->fb, output->buffer_length) < 0) - weston_log("Failed to munmap frame buffer: %s\n", - strerror(errno)); - - output->fb = NULL; -} - - -static int -fbdev_output_attach_head(struct weston_output *output_base, - struct weston_head *head_base) -{ - struct fbdev_output *output = to_fbdev_output(output_base); - struct fbdev_head *head = to_fbdev_head(head_base); - - /* Clones not supported. */ - if (!wl_list_empty(&output->base.head_list)) - return -1; - - /* only one static mode in list */ - output->mode.flags = WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED; - output->mode.width = head->fb_info.x_resolution; - output->mode.height = head->fb_info.y_resolution; - output->mode.refresh = head->fb_info.refresh_rate; - wl_list_init(&output->base.mode_list); - wl_list_insert(&output->base.mode_list, &output->mode.link); - output->base.current_mode = &output->mode; - - return 0; -} - -static void fbdev_output_destroy(struct weston_output *base); - -static int -fbdev_output_enable(struct weston_output *base) -{ - struct fbdev_output *output = to_fbdev_output(base); - struct fbdev_backend *backend = to_fbdev_backend(base->compositor); - struct fbdev_head *head; - int fb_fd; - struct wl_event_loop *loop; - const struct pixman_renderer_output_options options = { - .use_shadow = true, - }; - - head = fbdev_output_get_head(output); - - /* Create the frame buffer. */ - fb_fd = fbdev_frame_buffer_open(head->device, &head->fb_info); - if (fb_fd < 0) { - weston_log("Creating frame buffer failed.\n"); - return -1; - } - - if (fbdev_frame_buffer_map(output, fb_fd) < 0) { - weston_log("Mapping frame buffer failed.\n"); - return -1; - } - - output->base.start_repaint_loop = fbdev_output_start_repaint_loop; - output->base.repaint = fbdev_output_repaint; - - if (pixman_renderer_output_create(&output->base, &options) < 0) - goto out_hw_surface; - - loop = wl_display_get_event_loop(backend->compositor->wl_display); - output->finish_frame_timer = - wl_event_loop_add_timer(loop, finish_frame_handler, output); - - weston_log("fbdev output %d×%d px\n", - output->mode.width, output->mode.height); - weston_log_continue(STAMP_SPACE "guessing %d Hz and 96 dpi\n", - output->mode.refresh / 1000); - - return 0; - -out_hw_surface: - fbdev_frame_buffer_unmap(output); - - return -1; -} - -static int -fbdev_output_disable(struct weston_output *base) -{ - struct fbdev_output *output = to_fbdev_output(base); - - if (!base->enabled) - return 0; - - wl_event_source_remove(output->finish_frame_timer); - output->finish_frame_timer = NULL; - - pixman_renderer_output_destroy(&output->base); - fbdev_frame_buffer_unmap(output); - - return 0; -} - -static struct fbdev_head * -fbdev_head_create(struct fbdev_backend *backend, const char *device) -{ - struct fbdev_head *head; - int fb_fd; - - head = zalloc(sizeof *head); - if (!head) - return NULL; - - head->device = strdup(device); - - /* Create the frame buffer. */ - fb_fd = fbdev_frame_buffer_open(head->device, &head->fb_info); - if (fb_fd < 0) { - weston_log("Creating frame buffer head failed.\n"); - goto out_free; - } - close(fb_fd); - - weston_head_init(&head->base, "fbdev"); - weston_head_set_connection_status(&head->base, true); - weston_head_set_monitor_strings(&head->base, "unknown", - head->fb_info.id, NULL); - weston_head_set_subpixel(&head->base, WL_OUTPUT_SUBPIXEL_UNKNOWN); - weston_head_set_physical_size(&head->base, head->fb_info.width_mm, - head->fb_info.height_mm); - - weston_compositor_add_head(backend->compositor, &head->base); - - weston_log("Created head '%s' for device %s (%s)\n", - head->base.name, head->device, head->base.model); - - return head; - -out_free: - free(head->device); - free(head); - - return NULL; -} - -static void -fbdev_head_destroy(struct fbdev_head *head) -{ - weston_head_release(&head->base); - free(head->device); - free(head); -} - -static struct weston_output * -fbdev_output_create(struct weston_compositor *compositor, - const char *name) -{ - struct fbdev_output *output; - - weston_log("Creating fbdev output.\n"); - - output = zalloc(sizeof *output); - if (output == NULL) - return NULL; - - output->backend = to_fbdev_backend(compositor); - - weston_output_init(&output->base, compositor, name); - - output->base.destroy = fbdev_output_destroy; - output->base.disable = fbdev_output_disable; - output->base.enable = fbdev_output_enable; - output->base.attach_head = fbdev_output_attach_head; - - weston_compositor_add_pending_output(&output->base, compositor); - - return &output->base; -} - -static void -fbdev_output_destroy(struct weston_output *base) -{ - struct fbdev_output *output = to_fbdev_output(base); - - weston_log("Destroying fbdev output.\n"); - - fbdev_output_disable(base); - - /* Remove the output. */ - weston_output_release(&output->base); - - free(output); -} - -/* strcmp()-style return values. */ -static int -compare_screen_info (const struct fbdev_screeninfo *a, - const struct fbdev_screeninfo *b) -{ - if (a->x_resolution == b->x_resolution && - a->y_resolution == b->y_resolution && - a->width_mm == b->width_mm && - a->height_mm == b->height_mm && - a->bits_per_pixel == b->bits_per_pixel && - a->pixel_format == b->pixel_format && - a->refresh_rate == b->refresh_rate) - return 0; - - return 1; -} - -static int -fbdev_output_reenable(struct fbdev_backend *backend, - struct weston_output *base) -{ - struct fbdev_output *output = to_fbdev_output(base); - struct fbdev_head *head; - struct fbdev_screeninfo new_screen_info; - int fb_fd; - - head = fbdev_output_get_head(output); - - weston_log("Re-enabling fbdev output.\n"); - assert(output->base.enabled); - - /* Create the frame buffer. */ - fb_fd = fbdev_frame_buffer_open(head->device, &new_screen_info); - if (fb_fd < 0) { - weston_log("Creating frame buffer failed.\n"); - return -1; - } - - /* Check whether the frame buffer details have changed since we were - * disabled. */ - if (compare_screen_info(&head->fb_info, &new_screen_info) != 0) { - /* Perform a mode-set to restore the old mode. */ - if (fbdev_set_screen_info(fb_fd, &head->fb_info) < 0) { - weston_log("Failed to restore mode settings. " - "Attempting to re-open output anyway.\n"); - } - - close(fb_fd); - - /* Disable and enable the output so that resources depending on - * the frame buffer X/Y resolution (such as the shadow buffer) - * are re-initialised. */ - fbdev_output_disable(&output->base); - return fbdev_output_enable(&output->base); - } - - /* Map the device if it has the same details as before. */ - if (fbdev_frame_buffer_map(output, fb_fd) < 0) { - weston_log("Mapping frame buffer failed.\n"); - return -1; - } - - return 0; -} - -static void -fbdev_backend_destroy(struct weston_compositor *base) -{ - struct fbdev_backend *backend = to_fbdev_backend(base); - struct weston_head *head, *next; - - udev_input_destroy(&backend->input); - - /* Destroy the output. */ - weston_compositor_shutdown(base); - - wl_list_for_each_safe(head, next, &base->head_list, compositor_link) - fbdev_head_destroy(to_fbdev_head(head)); - - /* Chain up. */ - weston_launcher_destroy(base->launcher); - - udev_unref(backend->udev); - - free(backend); -} - -static void -session_notify(struct wl_listener *listener, void *data) -{ - struct weston_compositor *compositor = data; - struct fbdev_backend *backend = to_fbdev_backend(compositor); - struct weston_output *output; - - if (compositor->session_active) { - weston_log("entering VT\n"); - compositor->state = backend->prev_state; - - wl_list_for_each(output, &compositor->output_list, link) { - fbdev_output_reenable(backend, output); - } - - weston_compositor_damage_all(compositor); - - udev_input_enable(&backend->input); - } else { - weston_log("leaving VT\n"); - udev_input_disable(&backend->input); - - wl_list_for_each(output, &compositor->output_list, link) { - fbdev_frame_buffer_unmap(to_fbdev_output(output)); - } - - backend->prev_state = compositor->state; - weston_compositor_offscreen(compositor); - - /* If we have a repaint scheduled (from the idle handler), make - * sure we cancel that so we don't try to pageflip when we're - * vt switched away. The OFFSCREEN state will prevent - * further attempts at repainting. When we switch - * back, we schedule a repaint, which will process - * pending frame callbacks. */ - - wl_list_for_each(output, - &compositor->output_list, link) { - output->repaint_needed = false; - } - } -} - -static char * -find_framebuffer_device(struct fbdev_backend *b, const char *seat) -{ - struct udev_enumerate *e; - struct udev_list_entry *entry; - const char *path, *device_seat, *id; - char *fb_device_path = NULL; - struct udev_device *device, *fb_device, *pci; - - e = udev_enumerate_new(b->udev); - udev_enumerate_add_match_subsystem(e, "graphics"); - udev_enumerate_add_match_sysname(e, "fb[0-9]*"); - - udev_enumerate_scan_devices(e); - fb_device = NULL; - udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) { - bool is_boot_vga = false; - - path = udev_list_entry_get_name(entry); - device = udev_device_new_from_syspath(b->udev, path); - if (!device) - continue; - device_seat = udev_device_get_property_value(device, "ID_SEAT"); - if (!device_seat) - device_seat = default_seat; - if (strcmp(device_seat, seat)) { - udev_device_unref(device); - continue; - } - - pci = udev_device_get_parent_with_subsystem_devtype(device, - "pci", NULL); - if (pci) { - id = udev_device_get_sysattr_value(pci, "boot_vga"); - if (id && !strcmp(id, "1")) - is_boot_vga = true; - } - - /* If a framebuffer device was found, and this device isn't - * the boot-VGA device, don't use it. */ - if (!is_boot_vga && fb_device) { - udev_device_unref(device); - continue; - } - - /* There can only be one boot_vga device. Try to use it - * at all costs. */ - if (is_boot_vga) { - if (fb_device) - udev_device_unref(fb_device); - fb_device = device; - break; - } - - /* Per the (!is_boot_vga && fb_device) test above, only - * trump existing saved devices with boot-VGA devices, so if - * the test ends up here, this must be the first device seen. */ - assert(!fb_device); - fb_device = device; - } - - udev_enumerate_unref(e); - - if (fb_device) { - fb_device_path = strdup(udev_device_get_devnode(fb_device)); - udev_device_unref(fb_device); - } - - return fb_device_path; -} - -static struct fbdev_backend * -fbdev_backend_create(struct weston_compositor *compositor, - struct weston_fbdev_backend_config *param) -{ - struct fbdev_backend *backend; - const char *seat_id = default_seat; - const char *session_seat; - - session_seat = getenv("XDG_SEAT"); - if (session_seat) - seat_id = session_seat; - if (param->seat_id) - seat_id = param->seat_id; - - weston_log("initializing fbdev backend\n"); - weston_log("warning: the fbdev backend is deprecated, please migrate " - "to the DRM backend\n"); - - backend = zalloc(sizeof *backend); - if (backend == NULL) - return NULL; - - backend->compositor = compositor; - compositor->backend = &backend->base; - if (weston_compositor_set_presentation_clock_software( - compositor) < 0) - goto out_compositor; - - backend->udev = udev_new(); - if (backend->udev == NULL) { - weston_log("Failed to initialize udev context.\n"); - goto out_compositor; - } - - if (!param->device) - param->device = find_framebuffer_device(backend, seat_id); - if (!param->device) { - weston_log("fatal: no framebuffer devices detected.\n"); - goto out_udev; - } - - /* Set up the TTY. */ - backend->session_listener.notify = session_notify; - wl_signal_add(&compositor->session_signal, - &backend->session_listener); - compositor->launcher = - weston_launcher_connect(compositor, seat_id, false); - if (!compositor->launcher) { - weston_log("fatal: your system should either provide the " - "logind D-Bus API, or use seatd.\n"); - goto out_udev; - } - - backend->base.destroy = fbdev_backend_destroy; - backend->base.create_output = fbdev_output_create; - - backend->prev_state = WESTON_COMPOSITOR_ACTIVE; - - weston_setup_vt_switch_bindings(compositor); - - if (pixman_renderer_init(compositor) < 0) - goto out_launcher; - - if (!fbdev_head_create(backend, param->device)) - goto out_launcher; - - free(param->device); - - udev_input_init(&backend->input, compositor, backend->udev, - seat_id, param->configure_device); - - return backend; - -out_launcher: - free(param->device); - weston_launcher_destroy(compositor->launcher); - -out_udev: - udev_unref(backend->udev); - -out_compositor: - weston_compositor_shutdown(compositor); - free(backend); - - return NULL; -} - -static void -config_init_to_defaults(struct weston_fbdev_backend_config *config) -{ - config->device = NULL; - config->seat_id = NULL; -} - -WL_EXPORT int -weston_backend_init(struct weston_compositor *compositor, - struct weston_backend_config *config_base) -{ - struct fbdev_backend *b; - struct weston_fbdev_backend_config config = {{ 0, }}; - - if (config_base == NULL || - config_base->struct_version != WESTON_FBDEV_BACKEND_CONFIG_VERSION || - config_base->struct_size > sizeof(struct weston_fbdev_backend_config)) { - weston_log("fbdev backend config structure is invalid\n"); - return -1; - } - - config_init_to_defaults(&config); - memcpy(&config, config_base, config_base->struct_size); - - b = fbdev_backend_create(compositor, &config); - if (b == NULL) - return -1; - return 0; -} diff --git a/libweston/backend-fbdev/meson.build b/libweston/backend-fbdev/meson.build deleted file mode 100644 index 563ad6c5..00000000 --- a/libweston/backend-fbdev/meson.build +++ /dev/null @@ -1,33 +0,0 @@ -if not get_option('deprecated-backend-fbdev') - subdir_done() -endif - -warning('Support for the deprecated fbdev backend is enabled.') -warning('This feature will be removed in a future version.') - -config_h.set('BUILD_FBDEV_COMPOSITOR', '1') - -srcs_fbdev = [ - 'fbdev.c', - presentation_time_server_protocol_h, -] - -deps_fbdev = [ - dep_libweston_private, - dep_session_helper, - dep_libinput_backend, - dependency('libudev', version: '>= 136'), -] - -plugin_fbdev = shared_library( - 'fbdev-backend', - srcs_fbdev, - include_directories: common_inc, - dependencies: deps_fbdev, - name_prefix: '', - install: true, - install_dir: dir_module_libweston -) -env_modmap += 'fbdev-backend.so=@0@;'.format(plugin_fbdev.full_path()) - -install_headers(backend_fbdev_h, subdir: dir_include_libweston_install) diff --git a/libweston/compositor.c b/libweston/compositor.c index 955e5534..2c1dcf0d 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -8261,7 +8261,6 @@ weston_compositor_get_user_data(struct weston_compositor *compositor) static const char * const backend_map[] = { [WESTON_BACKEND_DRM] = "drm-backend.so", - [WESTON_BACKEND_FBDEV] = "fbdev-backend.so", [WESTON_BACKEND_HEADLESS] = "headless-backend.so", [WESTON_BACKEND_RDP] = "rdp-backend.so", [WESTON_BACKEND_WAYLAND] = "wayland-backend.so", diff --git a/libweston/meson.build b/libweston/meson.build index ca660e9a..7638b1c1 100644 --- a/libweston/meson.build +++ b/libweston/meson.build @@ -223,7 +223,6 @@ dep_vertex_clipping = declare_dependency( subdir('color-lcms') subdir('renderer-gl') subdir('backend-drm') -subdir('backend-fbdev') subdir('backend-headless') subdir('backend-rdp') subdir('backend-wayland') diff --git a/man/weston.ini.man b/man/weston.ini.man index d7fc42b3..8aa0d1c2 100644 --- a/man/weston.ini.man +++ b/man/weston.ini.man @@ -130,7 +130,6 @@ directory are: .RS 10 .nf .BR drm-backend.so -.BR fbdev-backend.so .BR headless-backend.so .BR rdp-backend.so .BR wayland-backend.so diff --git a/meson_options.txt b/meson_options.txt index b9340dc2..90ab3832 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -44,16 +44,10 @@ option( value: true, description: 'Weston backend: X11 (nested)' ) -option( - 'deprecated-backend-fbdev', - type: 'boolean', - value: false, - description: 'Weston backend: fbdev (deprecated)' -) option( 'backend-default', type: 'combo', - choices: [ 'auto', 'drm', 'wayland', 'x11', 'fbdev', 'headless' ], + choices: [ 'auto', 'drm', 'wayland', 'x11', 'headless' ], value: 'drm', description: 'Default backend when no parent display server detected' ) diff --git a/tests/weston-test-fixture-compositor.c b/tests/weston-test-fixture-compositor.c index 64325fb1..87ff6bf1 100644 --- a/tests/weston-test-fixture-compositor.c +++ b/tests/weston-test-fixture-compositor.c @@ -202,7 +202,6 @@ backend_to_str(enum weston_compositor_backend b) { static const char * const names[] = { [WESTON_BACKEND_DRM] = "drm-backend.so", - [WESTON_BACKEND_FBDEV] = "fbdev-backend.so", [WESTON_BACKEND_HEADLESS] = "headless-backend.so", [WESTON_BACKEND_RDP] = "rdp-backend.so", [WESTON_BACKEND_WAYLAND] = "wayland-backend.so", @@ -302,13 +301,6 @@ execute_compositor(const struct compositor_setup *setup, } #endif -#ifndef BUILD_FBDEV_COMPOSITOR - if (setup->backend == WESTON_BACKEND_FBDEV) { - fprintf(stderr, "fbdev-backend required but not built, skipping.\n"); - return RESULT_SKIP; - } -#endif - #ifndef BUILD_RDP_COMPOSITOR if (setup->backend == WESTON_BACKEND_RDP) { fprintf(stderr, "RDP-backend required but not built, skipping.\n"); @@ -389,8 +381,7 @@ execute_compositor(const struct compositor_setup *setup, setup->extra_module ? setup->extra_module : ""); prog_args_take(&args, tmp); - if (setup->backend != WESTON_BACKEND_DRM && - setup->backend != WESTON_BACKEND_FBDEV) { + if (setup->backend != WESTON_BACKEND_DRM) { str_printf(&tmp, "--width=%d", setup->width); prog_args_take(&args, tmp); From cdfe94b105eeb9d92cb95dba7d7c8d7dff5ab5ee Mon Sep 17 00:00:00 2001 From: nerdopolis Date: Thu, 6 Jan 2022 00:19:49 -0500 Subject: [PATCH 092/609] launcher-logind: Don't check wl->vtnr before returning it. Signed-off-by: n3rdopolis --- libweston/launcher-logind.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/libweston/launcher-logind.c b/libweston/launcher-logind.c index 7ebf906b..2b3efecd 100644 --- a/libweston/launcher-logind.c +++ b/libweston/launcher-logind.c @@ -853,9 +853,6 @@ static int launcher_logind_get_vt(struct weston_launcher *launcher) { struct launcher_logind *wl = wl_container_of(launcher, wl, base); - if (wl->vtnr <= 0) { - return -EINVAL; - } return wl->vtnr; } From f48cf18a1624561d544bdf1b0e22cd4829ae4023 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 29 Mar 2022 15:35:58 +0100 Subject: [PATCH 093/609] compositor: Fix harmless potential buffer overflow We could overflow a local buffer if there were more than ten million concurrently active displays within the current user's session. This seems vanishingly unlikely, and harmless, but does at least squash a compiler warning emitted by gcc 12+. Signed-off-by: Daniel Stone --- compositor/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compositor/main.c b/compositor/main.c index 91b3d43f..56de28d0 100644 --- a/compositor/main.c +++ b/compositor/main.c @@ -829,7 +829,7 @@ handle_primary_client_destroyed(struct wl_listener *listener, void *data) static int weston_create_listening_socket(struct wl_display *display, const char *socket_name) { - char name_candidate[16]; + char name_candidate[32]; if (socket_name) { if (wl_display_add_socket(display, socket_name)) { From c8a2fb7a4097b6586bd09424cd85a43cd82d0787 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 19 Jan 2022 17:36:31 +0000 Subject: [PATCH 094/609] pixel-formats: Add XYUV8888 format XYUV8888 support was added to gl-renderer in 30104bd89ad7, but not to pixel-formats. Oops. Signed-off-by: Daniel Stone --- libweston/pixel-formats.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libweston/pixel-formats.c b/libweston/pixel-formats.c index 822cc144..5e4cb1ae 100644 --- a/libweston/pixel-formats.c +++ b/libweston/pixel-formats.c @@ -505,6 +505,9 @@ static const struct pixel_format_info pixel_format_table[] = { .num_planes = 3, .chroma_order = ORDER_VU, }, + { + DRM_FORMAT(XYUV8888), + }, }; WL_EXPORT const struct pixel_format_info * From 7059ec7807bc7b8a3ad925946cc944788384280e Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Mon, 17 Jan 2022 14:20:09 +0000 Subject: [PATCH 095/609] desktop-shell: Explicitly destroy black views on shutdown desktop_shell_removed() won't get called when we shut down, so explicitly destroy the fullscreen black view. Signed-off-by: Daniel Stone --- desktop-shell/shell.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 38a09e1a..9016bce2 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -3644,7 +3644,6 @@ lower_fullscreen_layer(struct desktop_shell *shell, weston_layer_entry_remove(&shsurf->fullscreen.black_view->layer_link); wl_list_init(&shsurf->fullscreen.black_view->layer_link.link); weston_view_damage_below(shsurf->fullscreen.black_view); - } /* Lower the view to the workspace layer */ @@ -4847,6 +4846,7 @@ desktop_shell_destroy_views_on_layer(struct weston_layer *layer) wl_list_for_each_safe(view, view_next, &layer->view_list.link, layer_link.link) { struct shell_surface *shsurf = get_shell_surface(view->surface); + /* fullscreen_layer is special as it would have a view with an * additional black_view created and added to its layer_link * fullscreen view. See shell_ensure_fullscreen_black_view() @@ -4856,7 +4856,7 @@ desktop_shell_destroy_views_on_layer(struct weston_layer *layer) * we do it in desktop_surface_removed() */ if (shsurf) desktop_shell_destroy_surface(shsurf); - else + else if (is_black_surface_view(view, NULL)) weston_surface_destroy(view->surface); } From b77c2374ee81f058e5f4ea20b43614b878236fbb Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Mon, 17 Jan 2022 13:54:21 +0000 Subject: [PATCH 096/609] shell: Rename solid_color_surface to weston_curtain create_solid_color_surface actually returns a weston_view that it creates internally. Since weston_solid_color_view is long and dull, rename it to weston_curtain. Signed-off-by: Daniel Stone --- desktop-shell/shell.c | 4 ++-- kiosk-shell/kiosk-shell.c | 11 +++++------ shared/shell-utils.c | 6 +++--- shared/shell-utils.h | 8 ++++---- tests/safe-signal-output-removal-test.c | 5 ++--- 5 files changed, 16 insertions(+), 18 deletions(-) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 9016bce2..0597261a 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -2069,6 +2069,7 @@ create_black_surface(struct weston_compositor *ec, float x, float y, int w, int h) { struct weston_solid_color_surface surface_data = {}; + struct weston_view *view; surface_data.surface_committed = black_surface_committed; surface_data.get_label = black_surface_get_label; @@ -2078,8 +2079,7 @@ create_black_surface(struct weston_compositor *ec, surface_data.g = 0; surface_data.b = 0; - struct weston_view *view = - create_solid_color_surface(ec, &surface_data, x, y, w, h); + view = weston_curtain_create(ec, &surface_data, x, y, w, h); return view; } diff --git a/kiosk-shell/kiosk-shell.c b/kiosk-shell/kiosk-shell.c index 14857ecc..9f2c1fa6 100644 --- a/kiosk-shell/kiosk-shell.c +++ b/kiosk-shell/kiosk-shell.c @@ -480,6 +480,7 @@ static void kiosk_shell_output_recreate_background(struct kiosk_shell_output *shoutput) { struct kiosk_shell *shell = shoutput->shell; + struct weston_compositor *ec = shell->compositor; struct weston_output *output = shoutput->output; struct weston_config_section *shell_section = NULL; uint32_t bg_color = 0x0; @@ -505,12 +506,10 @@ kiosk_shell_output_recreate_background(struct kiosk_shell_output *shoutput) solid_surface.surface_committed = NULL; solid_surface.surface_private = NULL; - shoutput->background_view = - create_solid_color_surface(shoutput->shell->compositor, - &solid_surface, - output->x, output->y, - output->width, - output->height); + shoutput->background_view = weston_curtain_create(ec, &solid_surface, + output->x, output->y, + output->width, + output->height); weston_surface_set_role(shoutput->background_view->surface, "kiosk-shell-background", NULL, 0); diff --git a/shared/shell-utils.c b/shared/shell-utils.c index a301eefc..57b54d63 100644 --- a/shared/shell-utils.c +++ b/shared/shell-utils.c @@ -139,9 +139,9 @@ surface_get_label(struct weston_surface *surface, char *buf, size_t len) } struct weston_view * -create_solid_color_surface(struct weston_compositor *compositor, - struct weston_solid_color_surface *ss, - float x, float y, int w, int h) +weston_curtain_create(struct weston_compositor *compositor, + struct weston_solid_color_surface *ss, + float x, float y, int w, int h) { struct weston_surface *surface = NULL; struct weston_view *view; diff --git a/shared/shell-utils.h b/shared/shell-utils.h index b25de1a0..0bfd480c 100644 --- a/shared/shell-utils.h +++ b/shared/shell-utils.h @@ -26,7 +26,7 @@ #include "shared/helpers.h" #include -/* parameter for create_solid_color_surface() */ +/* parameter for weston_curtain_create() */ struct weston_solid_color_surface { int (*get_label)(struct weston_surface *es, char *buf, size_t len); void (*surface_committed)(struct weston_surface *es, int32_t sx, int32_t sy); @@ -53,6 +53,6 @@ surface_get_label(struct weston_surface *surface, char *buf, size_t len); /* helper to create a view w/ a color */ struct weston_view * -create_solid_color_surface(struct weston_compositor *compositor, - struct weston_solid_color_surface *ss, - float x, float y, int w, int h); +weston_curtain_create(struct weston_compositor *compositor, + struct weston_solid_color_surface *ss, + float x, float y, int w, int h); diff --git a/tests/safe-signal-output-removal-test.c b/tests/safe-signal-output-removal-test.c index 0a747307..3aacbe84 100644 --- a/tests/safe-signal-output-removal-test.c +++ b/tests/safe-signal-output-removal-test.c @@ -89,9 +89,8 @@ output_create_view(struct test_output *t_output) solid_surface.surface_committed = NULL; solid_surface.surface_private = NULL; - t_output->view = - create_solid_color_surface(t_output->compositor, - &solid_surface, 0, 0, 320, 240); + t_output->view = weston_curtain_create(t_output->compositor, + &solid_surface, 0, 0, 320, 240); weston_view_set_output(t_output->view, t_output->output); /* weston_compositor_remove_output() has to be patched with From 3a298b0b05c015682e4da129bed1bb85e5bb7a86 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Mon, 17 Jan 2022 13:58:16 +0000 Subject: [PATCH 097/609] shell: Rename weston_solid_color_surface to weston_curtain_params The name implied that it was a surface in and of itself, rather than parameters used by a helper to create a surface and view. Rename it now that we have weston_curtain as a name, and clean up initialisers. Signed-off-by: Daniel Stone --- desktop-shell/shell.c | 18 +++++++----------- kiosk-shell/kiosk-shell.c | 16 ++++++++-------- shared/shell-utils.c | 10 +++++----- shared/shell-utils.h | 4 ++-- tests/safe-signal-output-removal-test.c | 18 ++++++++---------- 5 files changed, 30 insertions(+), 36 deletions(-) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 0597261a..3eee0bc6 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -2068,18 +2068,14 @@ create_black_surface(struct weston_compositor *ec, struct weston_view *fs_view, float x, float y, int w, int h) { - struct weston_solid_color_surface surface_data = {}; + struct weston_curtain_params curtain_params = { + .r = 0.0, .g = 0.0, .b = 0.0, + .surface_committed = black_surface_committed, + .get_label = black_surface_get_label, + .surface_private = fs_view, + }; struct weston_view *view; - - surface_data.surface_committed = black_surface_committed; - surface_data.get_label = black_surface_get_label; - surface_data.surface_private = fs_view; - - surface_data.r = 0; - surface_data.g = 0; - surface_data.b = 0; - - view = weston_curtain_create(ec, &surface_data, x, y, w, h); + view = weston_curtain_create(ec, &curtain_params, x, y, w, h); return view; } diff --git a/kiosk-shell/kiosk-shell.c b/kiosk-shell/kiosk-shell.c index 9f2c1fa6..245cde86 100644 --- a/kiosk-shell/kiosk-shell.c +++ b/kiosk-shell/kiosk-shell.c @@ -484,7 +484,7 @@ kiosk_shell_output_recreate_background(struct kiosk_shell_output *shoutput) struct weston_output *output = shoutput->output; struct weston_config_section *shell_section = NULL; uint32_t bg_color = 0x0; - struct weston_solid_color_surface solid_surface = {}; + struct weston_curtain_params curtain_params = {}; if (shoutput->background_view) weston_surface_destroy(shoutput->background_view->surface); @@ -498,15 +498,15 @@ kiosk_shell_output_recreate_background(struct kiosk_shell_output *shoutput) weston_config_section_get_color(shell_section, "background-color", &bg_color, 0x00000000); - solid_surface.r = ((bg_color >> 16) & 0xff) / 255.0; - solid_surface.g = ((bg_color >> 8) & 0xff) / 255.0; - solid_surface.b = ((bg_color >> 0) & 0xff) / 255.0; + curtain_params.r = ((bg_color >> 16) & 0xff) / 255.0; + curtain_params.g = ((bg_color >> 8) & 0xff) / 255.0; + curtain_params.b = ((bg_color >> 0) & 0xff) / 255.0; - solid_surface.get_label = kiosk_shell_background_surface_get_label; - solid_surface.surface_committed = NULL; - solid_surface.surface_private = NULL; + curtain_params.get_label = kiosk_shell_background_surface_get_label; + curtain_params.surface_committed = NULL; + curtain_params.surface_private = NULL; - shoutput->background_view = weston_curtain_create(ec, &solid_surface, + shoutput->background_view = weston_curtain_create(ec, &curtain_params, output->x, output->y, output->width, output->height); diff --git a/shared/shell-utils.c b/shared/shell-utils.c index 57b54d63..b4d93e43 100644 --- a/shared/shell-utils.c +++ b/shared/shell-utils.c @@ -140,7 +140,7 @@ surface_get_label(struct weston_surface *surface, char *buf, size_t len) struct weston_view * weston_curtain_create(struct weston_compositor *compositor, - struct weston_solid_color_surface *ss, + struct weston_curtain_params *params, float x, float y, int w, int h) { struct weston_surface *surface = NULL; @@ -158,11 +158,11 @@ weston_curtain_create(struct weston_compositor *compositor, return NULL; } - surface->committed = ss->surface_committed; - surface->committed_private = ss->surface_private; + surface->committed = params->surface_committed; + surface->committed_private = params->surface_private; - weston_surface_set_color(surface, ss->r, ss->g, ss->b, 1.0); - weston_surface_set_label_func(surface, ss->get_label); + weston_surface_set_color(surface, params->r, params->g, params->b, 1.0); + weston_surface_set_label_func(surface, params->get_label); pixman_region32_fini(&surface->opaque); pixman_region32_init_rect(&surface->opaque, 0, 0, w, h); pixman_region32_fini(&surface->input); diff --git a/shared/shell-utils.h b/shared/shell-utils.h index 0bfd480c..9932957b 100644 --- a/shared/shell-utils.h +++ b/shared/shell-utils.h @@ -27,7 +27,7 @@ #include /* parameter for weston_curtain_create() */ -struct weston_solid_color_surface { +struct weston_curtain_params { int (*get_label)(struct weston_surface *es, char *buf, size_t len); void (*surface_committed)(struct weston_surface *es, int32_t sx, int32_t sy); void *surface_private; @@ -54,5 +54,5 @@ surface_get_label(struct weston_surface *surface, char *buf, size_t len); */ struct weston_view * weston_curtain_create(struct weston_compositor *compositor, - struct weston_solid_color_surface *ss, + struct weston_curtain_params *ss, float x, float y, int w, int h); diff --git a/tests/safe-signal-output-removal-test.c b/tests/safe-signal-output-removal-test.c index 3aacbe84..a9f9dbca 100644 --- a/tests/safe-signal-output-removal-test.c +++ b/tests/safe-signal-output-removal-test.c @@ -79,18 +79,16 @@ notify_output_destroy(struct wl_listener *listener, void *data) static void output_create_view(struct test_output *t_output) { - struct weston_solid_color_surface solid_surface = {}; - - solid_surface.r = 0.5; - solid_surface.g = 0.5; - solid_surface.b = 0.5; - - solid_surface.get_label = NULL; - solid_surface.surface_committed = NULL; - solid_surface.surface_private = NULL; + struct weston_curtain_params curtain_params = { + .r = 0.5, .g = 0.5, .b = 0.5, + .get_label = NULL, + .surface_committed = NULL, + .surface_private = NULL, + }; t_output->view = weston_curtain_create(t_output->compositor, - &solid_surface, 0, 0, 320, 240); + &curtain_params, + 0, 0, 320, 240); weston_view_set_output(t_output->view, t_output->output); /* weston_compositor_remove_output() has to be patched with From d21563360a511e3d0434a8e87e23db83d85d8bb2 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Mon, 17 Jan 2022 14:05:06 +0000 Subject: [PATCH 098/609] shell: Move weston_curtain_create params into the struct Given that we have a struct for argument params, we might as well use it rather than have them split between the struct and native params. For consistency between the implementations, this also includes a shift from float to int positioning for the base offset within the compositor's global co-ordinate space. Signed-off-by: Daniel Stone --- desktop-shell/shell.c | 5 +++-- kiosk-shell/kiosk-shell.c | 10 ++++++---- shared/shell-utils.c | 13 +++++++------ shared/shell-utils.h | 4 ++-- tests/safe-signal-output-removal-test.c | 5 +++-- 5 files changed, 21 insertions(+), 16 deletions(-) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 3eee0bc6..cf594161 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -2070,12 +2070,13 @@ create_black_surface(struct weston_compositor *ec, { struct weston_curtain_params curtain_params = { .r = 0.0, .g = 0.0, .b = 0.0, + .x = x, .y = y, + .width = w, .height = h, .surface_committed = black_surface_committed, .get_label = black_surface_get_label, .surface_private = fs_view, }; - struct weston_view *view; - view = weston_curtain_create(ec, &curtain_params, x, y, w, h); + struct weston_view *view = weston_curtain_create(ec, &curtain_params); return view; } diff --git a/kiosk-shell/kiosk-shell.c b/kiosk-shell/kiosk-shell.c index 245cde86..2df92a71 100644 --- a/kiosk-shell/kiosk-shell.c +++ b/kiosk-shell/kiosk-shell.c @@ -502,14 +502,16 @@ kiosk_shell_output_recreate_background(struct kiosk_shell_output *shoutput) curtain_params.g = ((bg_color >> 8) & 0xff) / 255.0; curtain_params.b = ((bg_color >> 0) & 0xff) / 255.0; + curtain_params.x = output->x; + curtain_params.y = output->y; + curtain_params.width = output->width; + curtain_params.height = output->height; + curtain_params.get_label = kiosk_shell_background_surface_get_label; curtain_params.surface_committed = NULL; curtain_params.surface_private = NULL; - shoutput->background_view = weston_curtain_create(ec, &curtain_params, - output->x, output->y, - output->width, - output->height); + shoutput->background_view = weston_curtain_create(ec, &curtain_params); weston_surface_set_role(shoutput->background_view->surface, "kiosk-shell-background", NULL, 0); diff --git a/shared/shell-utils.c b/shared/shell-utils.c index b4d93e43..51255a5b 100644 --- a/shared/shell-utils.c +++ b/shared/shell-utils.c @@ -140,8 +140,7 @@ surface_get_label(struct weston_surface *surface, char *buf, size_t len) struct weston_view * weston_curtain_create(struct weston_compositor *compositor, - struct weston_curtain_params *params, - float x, float y, int w, int h) + struct weston_curtain_params *params) { struct weston_surface *surface = NULL; struct weston_view *view; @@ -164,12 +163,14 @@ weston_curtain_create(struct weston_compositor *compositor, weston_surface_set_color(surface, params->r, params->g, params->b, 1.0); weston_surface_set_label_func(surface, params->get_label); pixman_region32_fini(&surface->opaque); - pixman_region32_init_rect(&surface->opaque, 0, 0, w, h); + pixman_region32_init_rect(&surface->opaque, 0, 0, + params->width, params->height); pixman_region32_fini(&surface->input); - pixman_region32_init_rect(&surface->input, 0, 0, w, h); + pixman_region32_init_rect(&surface->input, 0, 0, + params->width, params->height); - weston_surface_set_size(surface, w, h); - weston_view_set_position(view, x, y); + weston_surface_set_size(surface, params->width, params->height); + weston_view_set_position(view, params->x, params->y); return view; } diff --git a/shared/shell-utils.h b/shared/shell-utils.h index 9932957b..f8c508ff 100644 --- a/shared/shell-utils.h +++ b/shared/shell-utils.h @@ -32,6 +32,7 @@ struct weston_curtain_params { void (*surface_committed)(struct weston_surface *es, int32_t sx, int32_t sy); void *surface_private; float r, g, b; + int x, y, width, height; }; struct weston_output * @@ -54,5 +55,4 @@ surface_get_label(struct weston_surface *surface, char *buf, size_t len); */ struct weston_view * weston_curtain_create(struct weston_compositor *compositor, - struct weston_curtain_params *ss, - float x, float y, int w, int h); + struct weston_curtain_params *params); diff --git a/tests/safe-signal-output-removal-test.c b/tests/safe-signal-output-removal-test.c index a9f9dbca..422e91bf 100644 --- a/tests/safe-signal-output-removal-test.c +++ b/tests/safe-signal-output-removal-test.c @@ -81,14 +81,15 @@ output_create_view(struct test_output *t_output) { struct weston_curtain_params curtain_params = { .r = 0.5, .g = 0.5, .b = 0.5, + .x = 0, .y = 0, + .width = 320, .height = 240, .get_label = NULL, .surface_committed = NULL, .surface_private = NULL, }; t_output->view = weston_curtain_create(t_output->compositor, - &curtain_params, - 0, 0, 320, 240); + &curtain_params); weston_view_set_output(t_output->view, t_output->output); /* weston_compositor_remove_output() has to be patched with From e81b8d7cc92c2bf88c3afd25445022857d9310a8 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 18 Jan 2022 18:17:17 +0000 Subject: [PATCH 099/609] shell: Add alpha to weston_curtain_create Not all solid-colour views want to be opaque: sometimes we use them with non-opaque alpha values in order to shade views underneath them. Signed-off-by: Daniel Stone --- desktop-shell/shell.c | 2 +- kiosk-shell/kiosk-shell.c | 1 + shared/shell-utils.c | 13 ++++++++++--- shared/shell-utils.h | 2 +- tests/safe-signal-output-removal-test.c | 2 +- 5 files changed, 14 insertions(+), 6 deletions(-) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index cf594161..4c238efc 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -2069,7 +2069,7 @@ create_black_surface(struct weston_compositor *ec, float x, float y, int w, int h) { struct weston_curtain_params curtain_params = { - .r = 0.0, .g = 0.0, .b = 0.0, + .r = 0.0, .g = 0.0, .b = 0.0, .a = 1.0, .x = x, .y = y, .width = w, .height = h, .surface_committed = black_surface_committed, diff --git a/kiosk-shell/kiosk-shell.c b/kiosk-shell/kiosk-shell.c index 2df92a71..1f98b631 100644 --- a/kiosk-shell/kiosk-shell.c +++ b/kiosk-shell/kiosk-shell.c @@ -501,6 +501,7 @@ kiosk_shell_output_recreate_background(struct kiosk_shell_output *shoutput) curtain_params.r = ((bg_color >> 16) & 0xff) / 255.0; curtain_params.g = ((bg_color >> 8) & 0xff) / 255.0; curtain_params.b = ((bg_color >> 0) & 0xff) / 255.0; + curtain_params.a = 1.0; curtain_params.x = output->x; curtain_params.y = output->y; diff --git a/shared/shell-utils.c b/shared/shell-utils.c index 51255a5b..d4505f73 100644 --- a/shared/shell-utils.c +++ b/shared/shell-utils.c @@ -160,11 +160,18 @@ weston_curtain_create(struct weston_compositor *compositor, surface->committed = params->surface_committed; surface->committed_private = params->surface_private; - weston_surface_set_color(surface, params->r, params->g, params->b, 1.0); + weston_surface_set_color(surface, + params->r, params->g, params->b, params->a); weston_surface_set_label_func(surface, params->get_label); + pixman_region32_fini(&surface->opaque); - pixman_region32_init_rect(&surface->opaque, 0, 0, - params->width, params->height); + if (params->a == 1.0) { + pixman_region32_init_rect(&surface->opaque, 0, 0, + params->width, params->height); + } else { + pixman_region32_init(&surface->opaque); + } + pixman_region32_fini(&surface->input); pixman_region32_init_rect(&surface->input, 0, 0, params->width, params->height); diff --git a/shared/shell-utils.h b/shared/shell-utils.h index f8c508ff..c5e648f1 100644 --- a/shared/shell-utils.h +++ b/shared/shell-utils.h @@ -31,7 +31,7 @@ struct weston_curtain_params { int (*get_label)(struct weston_surface *es, char *buf, size_t len); void (*surface_committed)(struct weston_surface *es, int32_t sx, int32_t sy); void *surface_private; - float r, g, b; + float r, g, b, a; int x, y, width, height; }; diff --git a/tests/safe-signal-output-removal-test.c b/tests/safe-signal-output-removal-test.c index 422e91bf..57268120 100644 --- a/tests/safe-signal-output-removal-test.c +++ b/tests/safe-signal-output-removal-test.c @@ -80,7 +80,7 @@ static void output_create_view(struct test_output *t_output) { struct weston_curtain_params curtain_params = { - .r = 0.5, .g = 0.5, .b = 0.5, + .r = 0.5, .g = 0.5, .b = 0.5, .a = 1.0, .x = 0, .y = 0, .width = 320, .height = 240, .get_label = NULL, From 791e8b1c5f4603413da6f452d44c58b7bd794c69 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 18 Jan 2022 18:18:53 +0000 Subject: [PATCH 100/609] desktop-shell: Fix opaque region co-ordinate confusion Opaque regions are in surface co-ordinate space, not global co-ordinate space, so the region should be anchored to (0,0). Signed-off-by: Daniel Stone --- desktop-shell/shell.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 4c238efc..c46f0ba4 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -586,7 +586,7 @@ create_focus_surface(struct weston_compositor *ec, weston_view_set_position(fsurf->view, output->x, output->y); weston_surface_set_color(surface, 0.0, 0.0, 0.0, 1.0); pixman_region32_fini(&surface->opaque); - pixman_region32_init_rect(&surface->opaque, output->x, output->y, + pixman_region32_init_rect(&surface->opaque, 0, 0, output->width, output->height); pixman_region32_fini(&surface->input); pixman_region32_init(&surface->input); From de0cd532647c671b9a6fde15511e9abb5f299402 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 18 Jan 2022 18:19:39 +0000 Subject: [PATCH 101/609] desktop-shell: Remove redundant geometry dirty call Dirtying the geometry only sets a flag on the view saying that the geometry is dirty, so we don't need to do it twice back-to-back. Signed-off-by: Daniel Stone --- desktop-shell/shell.c | 1 - 1 file changed, 1 deletion(-) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index c46f0ba4..8f0935a7 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -2098,7 +2098,6 @@ shell_ensure_fullscreen_black_view(struct shell_surface *shsurf) output->width, output->height); - weston_view_geometry_dirty(shsurf->fullscreen.black_view); weston_layer_entry_remove(&shsurf->fullscreen.black_view->layer_link); weston_layer_entry_insert(&shsurf->view->layer_link, &shsurf->fullscreen.black_view->layer_link); From 64ef87554b2e72c49f4e90f6c7947fe98e089695 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 18 Jan 2022 18:28:42 +0000 Subject: [PATCH 102/609] desktop-shell: Clean up fullscreen black view code Rationalise it a little bit so we don't need pre-declarations of static functions, and the order of creation more closely matches the others, including making the same calls to explicitly set the output. Doing this makes the behaviour match the other users of the same code pattern. In making them the same, we make desktop-shell code a little less magically divergent where people might wonder what the correct pattern is to use. After we have moved all users to a uniform pattern, later commits are then able to migrate these users to common helper code, which reduces code duplication, improves code clarity as it is no longer necessary to wonder about the exact semantics of every special-case user of this common pattern, and makes it easier to make interface changes which improve and clarify the patterns which are prevalent throughout the desktop-shell code. Signed-off-by: Daniel Stone --- desktop-shell/shell.c | 68 +++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 41 deletions(-) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 8f0935a7..6d77c383 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -2061,24 +2061,21 @@ black_surface_get_label(struct weston_surface *surface, char *buf, size_t len) } static void -black_surface_committed(struct weston_surface *es, int32_t sx, int32_t sy); +black_surface_committed(struct weston_surface *es, int32_t sx, int32_t sy) +{ +} -static struct weston_view * -create_black_surface(struct weston_compositor *ec, - struct weston_view *fs_view, - float x, float y, int w, int h) +static bool +is_black_surface_view(struct weston_view *view, struct weston_view **fs_view) { - struct weston_curtain_params curtain_params = { - .r = 0.0, .g = 0.0, .b = 0.0, .a = 1.0, - .x = x, .y = y, - .width = w, .height = h, - .surface_committed = black_surface_committed, - .get_label = black_surface_get_label, - .surface_private = fs_view, - }; - struct weston_view *view = weston_curtain_create(ec, &curtain_params); + struct weston_surface *surface = view->surface; - return view; + if (surface->committed == black_surface_committed) { + if (fs_view) + *fs_view = surface->committed_private; + return true; + } + return false; } static void @@ -2086,17 +2083,26 @@ shell_ensure_fullscreen_black_view(struct shell_surface *shsurf) { struct weston_surface *surface = weston_desktop_surface_get_surface(shsurf->desktop_surface); + struct weston_compositor *ec = surface->compositor; struct weston_output *output = shsurf->fullscreen_output; + struct weston_curtain_params curtain_params = { + .r = 0.0, .g = 0.0, .b = 0.0, .a = 1.0, + .x = output->x, .y = output->y, + .width = output->width, .height = output->height, + .surface_committed = black_surface_committed, + .get_label = black_surface_get_label, + .surface_private = shsurf->view, + }; assert(weston_desktop_surface_get_fullscreen(shsurf->desktop_surface)); - if (!shsurf->fullscreen.black_view) + if (!shsurf->fullscreen.black_view) { shsurf->fullscreen.black_view = - create_black_surface(surface->compositor, - shsurf->view, - output->x, output->y, - output->width, - output->height); + weston_curtain_create(ec, &curtain_params); + } + + weston_view_set_output(shsurf->fullscreen.black_view, output); + shsurf->fullscreen.black_view->is_mapped = true; weston_layer_entry_remove(&shsurf->fullscreen.black_view->layer_link); weston_layer_entry_insert(&shsurf->view->layer_link, @@ -2104,7 +2110,6 @@ shell_ensure_fullscreen_black_view(struct shell_surface *shsurf) weston_view_geometry_dirty(shsurf->fullscreen.black_view); weston_surface_damage(surface); - shsurf->fullscreen.black_view->is_mapped = true; shsurf->state.lowered = false; } @@ -3726,25 +3731,6 @@ activate(struct desktop_shell *shell, struct weston_view *view, } } -/* no-op func for checking black surface */ -static void -black_surface_committed(struct weston_surface *es, int32_t sx, int32_t sy) -{ -} - -static bool -is_black_surface_view(struct weston_view *view, struct weston_view **fs_view) -{ - struct weston_surface *surface = view->surface; - - if (surface->committed == black_surface_committed) { - if (fs_view) - *fs_view = surface->committed_private; - return true; - } - return false; -} - static void activate_binding(struct weston_seat *seat, struct desktop_shell *shell, From bd9b0676dd29672b64cabc5f07a33540e23bdceb Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 18 Jan 2022 18:59:33 +0000 Subject: [PATCH 103/609] shell: Make input capture optional for curtains desktop-shell's focus surfaces want to reuse this, but they don't want to capture the input, instead allowing it to fall through. Signed-off-by: Daniel Stone --- desktop-shell/shell.c | 1 + kiosk-shell/kiosk-shell.c | 2 ++ shared/shell-utils.c | 8 ++++++-- shared/shell-utils.h | 1 + 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 6d77c383..645ac57a 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -2092,6 +2092,7 @@ shell_ensure_fullscreen_black_view(struct shell_surface *shsurf) .surface_committed = black_surface_committed, .get_label = black_surface_get_label, .surface_private = shsurf->view, + .capture_input = true, }; assert(weston_desktop_surface_get_fullscreen(shsurf->desktop_surface)); diff --git a/kiosk-shell/kiosk-shell.c b/kiosk-shell/kiosk-shell.c index 1f98b631..c8c2276d 100644 --- a/kiosk-shell/kiosk-shell.c +++ b/kiosk-shell/kiosk-shell.c @@ -508,6 +508,8 @@ kiosk_shell_output_recreate_background(struct kiosk_shell_output *shoutput) curtain_params.width = output->width; curtain_params.height = output->height; + curtain_params.capture_input = true; + curtain_params.get_label = kiosk_shell_background_surface_get_label; curtain_params.surface_committed = NULL; curtain_params.surface_private = NULL; diff --git a/shared/shell-utils.c b/shared/shell-utils.c index d4505f73..58342f68 100644 --- a/shared/shell-utils.c +++ b/shared/shell-utils.c @@ -173,8 +173,12 @@ weston_curtain_create(struct weston_compositor *compositor, } pixman_region32_fini(&surface->input); - pixman_region32_init_rect(&surface->input, 0, 0, - params->width, params->height); + if (params->capture_input) { + pixman_region32_init_rect(&surface->input, 0, 0, + params->width, params->height); + } else { + pixman_region32_init(&surface->input); + } weston_surface_set_size(surface, params->width, params->height); weston_view_set_position(view, params->x, params->y); diff --git a/shared/shell-utils.h b/shared/shell-utils.h index c5e648f1..33d93caa 100644 --- a/shared/shell-utils.h +++ b/shared/shell-utils.h @@ -33,6 +33,7 @@ struct weston_curtain_params { void *surface_private; float r, g, b, a; int x, y, width, height; + bool capture_input; }; struct weston_output * From e031397e09d8380d2c3281c1b95d34cc01f36da1 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 18 Jan 2022 19:00:33 +0000 Subject: [PATCH 104/609] desktop-shell: Reuse curtains for focus animations Just as we do for fullscreen backgrounds, reuse the curtain infrastructure for focus animations. This introduces a small functional change, in that the surface's output is no longer directly assigned. Instead, we call weston_view_set_output() ourselves. As setting the weston_view's position (done from the common helper function of weston_curtain_create which has been introduced in previous commits) will call weston_view_set_position(), the view's geometry will be dirtied as a result. When the geometry of a weston_view is dirty, it is marked to be updated, which will occur whilst traversing the view list during output repaint. This occurs for every view which is currently assigned to a layer; when building the view list, any view reachable through the view list whose geometry is dirty will have its position recalculated and an output assigned. Doing so results in the surface's current output being updated. It is believed that there is no functional impact from the weston_surface not having a primary output assigned between creation and output repaint being called. Signed-off-by: Daniel Stone --- desktop-shell/shell.c | 47 +++++++++++-------------------------------- 1 file changed, 12 insertions(+), 35 deletions(-) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 645ac57a..de8f374b 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -537,16 +537,10 @@ get_focus_surface(struct weston_surface *surface) return NULL; } -static bool -is_focus_surface (struct weston_surface *es) -{ - return (es->committed == focus_surface_committed); -} - static bool is_focus_view (struct weston_view *view) { - return is_focus_surface (view->surface); + return (view->surface->committed == focus_surface_committed); } static struct focus_surface * @@ -554,43 +548,26 @@ create_focus_surface(struct weston_compositor *ec, struct weston_output *output) { struct focus_surface *fsurf = NULL; - struct weston_surface *surface = NULL; + struct weston_curtain_params curtain_params = { + .r = 0.0, .g = 0.0, .b = 0.0, .a = 1.0, + .x = output->x, .y = output->y, + .width = output->width, .height = output->height, + .surface_committed = focus_surface_committed, + .get_label = focus_surface_get_label, + .surface_private = NULL, + .capture_input = false, + }; fsurf = malloc(sizeof *fsurf); if (!fsurf) return NULL; - fsurf->surface = weston_surface_create(ec); - surface = fsurf->surface; - if (surface == NULL) { - free(fsurf); - return NULL; - } - - surface->committed = focus_surface_committed; - surface->output = output; - surface->is_mapped = true; - surface->committed_private = fsurf; - weston_surface_set_label_func(surface, focus_surface_get_label); + curtain_params.surface_private = fsurf; - fsurf->view = weston_view_create(surface); - if (fsurf->view == NULL) { - weston_surface_destroy(surface); - free(fsurf); - return NULL; - } + fsurf->view = weston_curtain_create(ec, &curtain_params); weston_view_set_output(fsurf->view, output); fsurf->view->is_mapped = true; - weston_surface_set_size(surface, output->width, output->height); - weston_view_set_position(fsurf->view, output->x, output->y); - weston_surface_set_color(surface, 0.0, 0.0, 0.0, 1.0); - pixman_region32_fini(&surface->opaque); - pixman_region32_init_rect(&surface->opaque, 0, 0, - output->width, output->height); - pixman_region32_fini(&surface->input); - pixman_region32_init(&surface->input); - wl_list_init(&fsurf->workspace_transform.link); return fsurf; From dc0f73bcac9d485d73e437173f99050bc3c9e552 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Mon, 17 Jan 2022 14:24:03 +0000 Subject: [PATCH 105/609] shell: Encapsulate weston_curtain in its own struct This will allow us to create a solid weston_buffer as well, since we need to store that separately. Signed-off-by: Daniel Stone --- desktop-shell/shell.c | 67 +++++++++++++------------ desktop-shell/shell.h | 3 +- kiosk-shell/kiosk-shell.c | 22 ++++---- kiosk-shell/kiosk-shell.h | 2 +- shared/shell-utils.c | 43 ++++++++++++---- shared/shell-utils.h | 11 ++-- tests/safe-signal-output-removal-test.c | 12 ++--- 7 files changed, 93 insertions(+), 67 deletions(-) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index de8f374b..641254d3 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -121,7 +121,7 @@ struct shell_surface { struct { struct weston_transform transform; /* matrix from x, y */ - struct weston_view *black_view; + struct weston_curtain *black_view; } fullscreen; struct weston_transform workspace_transform; @@ -564,9 +564,9 @@ create_focus_surface(struct weston_compositor *ec, curtain_params.surface_private = fsurf; - fsurf->view = weston_curtain_create(ec, &curtain_params); - weston_view_set_output(fsurf->view, output); - fsurf->view->is_mapped = true; + fsurf->curtain = weston_curtain_create(ec, &curtain_params); + weston_view_set_output(fsurf->curtain->view, output); + fsurf->curtain->view->is_mapped = true; wl_list_init(&fsurf->workspace_transform.link); @@ -576,7 +576,7 @@ create_focus_surface(struct weston_compositor *ec, static void focus_surface_destroy(struct focus_surface *fsurf) { - weston_surface_destroy(fsurf->surface); + weston_curtain_destroy(fsurf->curtain); free(fsurf); } @@ -651,8 +651,8 @@ focus_state_surface_destroy(struct wl_listener *listener, void *data) weston_view_animation_destroy(state->ws->focus_animation); state->ws->focus_animation = weston_fade_run( - state->ws->fsurf_front->view, - state->ws->fsurf_front->view->alpha, 0.0, 300, + state->ws->fsurf_front->curtain->view, + state->ws->fsurf_front->curtain->view->alpha, 0.0, 300, focus_animation_done, state->ws); } @@ -804,19 +804,19 @@ animate_focus_change(struct desktop_shell *shell, struct workspace *ws, ws->fsurf_front = create_focus_surface(shell->compositor, output); if (ws->fsurf_front == NULL) return; - ws->fsurf_front->view->alpha = 0.0; + ws->fsurf_front->curtain->view->alpha = 0.0; ws->fsurf_back = create_focus_surface(shell->compositor, output); if (ws->fsurf_back == NULL) { focus_surface_destroy(ws->fsurf_front); return; } - ws->fsurf_back->view->alpha = 0.0; + ws->fsurf_back->curtain->view->alpha = 0.0; focus_surface_created = true; } else { - weston_layer_entry_remove(&ws->fsurf_front->view->layer_link); - weston_layer_entry_remove(&ws->fsurf_back->view->layer_link); + weston_layer_entry_remove(&ws->fsurf_front->curtain->view->layer_link); + weston_layer_entry_remove(&ws->fsurf_back->curtain->view->layer_link); } if (ws->focus_animation) { @@ -826,29 +826,29 @@ animate_focus_change(struct desktop_shell *shell, struct workspace *ws, if (to) weston_layer_entry_insert(&to->layer_link, - &ws->fsurf_front->view->layer_link); + &ws->fsurf_front->curtain->view->layer_link); else if (from) weston_layer_entry_insert(&ws->layer.view_list, - &ws->fsurf_front->view->layer_link); + &ws->fsurf_front->curtain->view->layer_link); if (focus_surface_created) { ws->focus_animation = weston_fade_run( - ws->fsurf_front->view, - ws->fsurf_front->view->alpha, 0.4, 300, + ws->fsurf_front->curtain->view, + ws->fsurf_front->curtain->view->alpha, 0.4, 300, focus_animation_done, ws); } else if (from) { weston_layer_entry_insert(&from->layer_link, - &ws->fsurf_back->view->layer_link); + &ws->fsurf_back->curtain->view->layer_link); ws->focus_animation = weston_stable_fade_run( - ws->fsurf_front->view, 0.0, - ws->fsurf_back->view, 0.4, + ws->fsurf_front->curtain->view, 0.0, + ws->fsurf_back->curtain->view, 0.4, focus_animation_done, ws); } else if (to) { weston_layer_entry_insert(&ws->layer.view_list, - &ws->fsurf_back->view->layer_link); + &ws->fsurf_back->curtain->view->layer_link); ws->focus_animation = weston_stable_fade_run( - ws->fsurf_front->view, 0.0, - ws->fsurf_back->view, 0.4, + ws->fsurf_front->curtain->view, 0.0, + ws->fsurf_back->curtain->view, 0.4, focus_animation_done, ws); } } @@ -1930,7 +1930,7 @@ unset_fullscreen(struct shell_surface *shsurf) wl_list_init(&shsurf->fullscreen.transform.link); if (shsurf->fullscreen.black_view) - weston_surface_destroy(shsurf->fullscreen.black_view->surface); + weston_curtain_destroy(shsurf->fullscreen.black_view); shsurf->fullscreen.black_view = NULL; if (shsurf->saved_position_valid) @@ -2071,6 +2071,7 @@ shell_ensure_fullscreen_black_view(struct shell_surface *shsurf) .surface_private = shsurf->view, .capture_input = true, }; + struct weston_view *view; assert(weston_desktop_surface_get_fullscreen(shsurf->desktop_surface)); @@ -2078,14 +2079,14 @@ shell_ensure_fullscreen_black_view(struct shell_surface *shsurf) shsurf->fullscreen.black_view = weston_curtain_create(ec, &curtain_params); } + view = shsurf->fullscreen.black_view->view; - weston_view_set_output(shsurf->fullscreen.black_view, output); - shsurf->fullscreen.black_view->is_mapped = true; + weston_view_set_output(view, output); + view->is_mapped = true; - weston_layer_entry_remove(&shsurf->fullscreen.black_view->layer_link); - weston_layer_entry_insert(&shsurf->view->layer_link, - &shsurf->fullscreen.black_view->layer_link); - weston_view_geometry_dirty(shsurf->fullscreen.black_view); + weston_layer_entry_remove(&view->layer_link); + weston_layer_entry_insert(&shsurf->view->layer_link, &view->layer_link); + weston_view_geometry_dirty(view); weston_surface_damage(surface); shsurf->state.lowered = false; @@ -2337,7 +2338,7 @@ desktop_surface_removed(struct weston_desktop_surface *desktop_surface, } if (shsurf->fullscreen.black_view) - weston_surface_destroy(shsurf->fullscreen.black_view->surface); + weston_curtain_destroy(shsurf->fullscreen.black_view); weston_surface_set_label_func(surface, NULL); weston_desktop_surface_set_user_data(shsurf->desktop_surface, NULL); @@ -3620,9 +3621,9 @@ lower_fullscreen_layer(struct desktop_shell *shell, * in the fullscreen layer. */ if (weston_desktop_surface_get_fullscreen(shsurf->desktop_surface)) { /* Hide the black view */ - weston_layer_entry_remove(&shsurf->fullscreen.black_view->layer_link); - wl_list_init(&shsurf->fullscreen.black_view->layer_link.link); - weston_view_damage_below(shsurf->fullscreen.black_view); + weston_layer_entry_remove(&shsurf->fullscreen.black_view->view->layer_link); + wl_list_init(&shsurf->fullscreen.black_view->view->layer_link.link); + weston_view_damage_below(shsurf->fullscreen.black_view->view); } /* Lower the view to the workspace layer */ @@ -4323,7 +4324,7 @@ switcher_next(struct switcher *switcher) shsurf = get_shell_surface(switcher->current->surface); if (shsurf && weston_desktop_surface_get_fullscreen(shsurf->desktop_surface)) - shsurf->fullscreen.black_view->alpha = 1.0; + shsurf->fullscreen.black_view->view->alpha = 1.0; } static void diff --git a/desktop-shell/shell.h b/desktop-shell/shell.h index b06b9066..dc4fc9c0 100644 --- a/desktop-shell/shell.h +++ b/desktop-shell/shell.h @@ -91,8 +91,7 @@ struct exposay { }; struct focus_surface { - struct weston_surface *surface; - struct weston_view *view; + struct weston_curtain *curtain; struct weston_transform workspace_transform; }; diff --git a/kiosk-shell/kiosk-shell.c b/kiosk-shell/kiosk-shell.c index c8c2276d..66aa3910 100644 --- a/kiosk-shell/kiosk-shell.c +++ b/kiosk-shell/kiosk-shell.c @@ -486,8 +486,8 @@ kiosk_shell_output_recreate_background(struct kiosk_shell_output *shoutput) uint32_t bg_color = 0x0; struct weston_curtain_params curtain_params = {}; - if (shoutput->background_view) - weston_surface_destroy(shoutput->background_view->surface); + if (shoutput->curtain) + weston_curtain_destroy(shoutput->curtain); if (!output) return; @@ -514,18 +514,18 @@ kiosk_shell_output_recreate_background(struct kiosk_shell_output *shoutput) curtain_params.surface_committed = NULL; curtain_params.surface_private = NULL; - shoutput->background_view = weston_curtain_create(ec, &curtain_params); + shoutput->curtain = weston_curtain_create(ec, &curtain_params); - weston_surface_set_role(shoutput->background_view->surface, + weston_surface_set_role(shoutput->curtain->view->surface, "kiosk-shell-background", NULL, 0); weston_layer_entry_insert(&shell->background_layer.view_list, - &shoutput->background_view->layer_link); + &shoutput->curtain->view->layer_link); - shoutput->background_view->is_mapped = true; - shoutput->background_view->surface->is_mapped = true; - shoutput->background_view->surface->output = output; - weston_view_set_output(shoutput->background_view, output); + shoutput->curtain->view->is_mapped = true; + shoutput->curtain->view->surface->is_mapped = true; + shoutput->curtain->view->surface->output = output; + weston_view_set_output(shoutput->curtain->view, output); } static void @@ -534,8 +534,8 @@ kiosk_shell_output_destroy(struct kiosk_shell_output *shoutput) shoutput->output = NULL; shoutput->output_destroy_listener.notify = NULL; - if (shoutput->background_view) - weston_surface_destroy(shoutput->background_view->surface); + if (shoutput->curtain) + weston_curtain_destroy(shoutput->curtain); wl_list_remove(&shoutput->output_destroy_listener.link); wl_list_remove(&shoutput->link); diff --git a/kiosk-shell/kiosk-shell.h b/kiosk-shell/kiosk-shell.h index 070ba1ab..3eb82cd9 100644 --- a/kiosk-shell/kiosk-shell.h +++ b/kiosk-shell/kiosk-shell.h @@ -88,7 +88,7 @@ struct kiosk_shell_seat { struct kiosk_shell_output { struct weston_output *output; struct wl_listener output_destroy_listener; - struct weston_view *background_view; + struct weston_curtain *curtain; struct kiosk_shell *shell; struct wl_list link; diff --git a/shared/shell-utils.c b/shared/shell-utils.c index 58342f68..7a2f1df5 100644 --- a/shared/shell-utils.c +++ b/shared/shell-utils.c @@ -138,28 +138,31 @@ surface_get_label(struct weston_surface *surface, char *buf, size_t len) c ? " of " : "", c ?: ""); } -struct weston_view * +struct weston_curtain * weston_curtain_create(struct weston_compositor *compositor, struct weston_curtain_params *params) { + struct weston_curtain *curtain; struct weston_surface *surface = NULL; struct weston_view *view; + curtain = zalloc(sizeof(*curtain)); + if (curtain == NULL) + goto err; + surface = weston_surface_create(compositor); - if (surface == NULL) { - weston_log("no memory\n"); - return NULL; - } + if (surface == NULL) + goto err_curtain; + view = weston_view_create(surface); - if (view == NULL) { - weston_log("no memory\n"); - weston_surface_destroy(surface); - return NULL; - } + if (view == NULL) + goto err_surface; surface->committed = params->surface_committed; surface->committed_private = params->surface_private; + curtain->view = view; + weston_surface_set_color(surface, params->r, params->g, params->b, params->a); weston_surface_set_label_func(surface, params->get_label); @@ -183,5 +186,23 @@ weston_curtain_create(struct weston_compositor *compositor, weston_surface_set_size(surface, params->width, params->height); weston_view_set_position(view, params->x, params->y); - return view; + return curtain; + +err_surface: + weston_surface_destroy(surface); +err_curtain: + free(curtain); +err: + weston_log("no memory\n"); + return NULL; +} + +void +weston_curtain_destroy(struct weston_curtain *curtain) +{ + struct weston_surface *surface = curtain->view->surface; + + weston_view_destroy(curtain->view); + weston_surface_destroy(surface); + free(curtain); } diff --git a/shared/shell-utils.h b/shared/shell-utils.h index 33d93caa..8e2e5cb3 100644 --- a/shared/shell-utils.h +++ b/shared/shell-utils.h @@ -36,6 +36,10 @@ struct weston_curtain_params { bool capture_input; }; +struct weston_curtain { + struct weston_view *view; +}; + struct weston_output * get_default_output(struct weston_compositor *compositor); @@ -52,8 +56,9 @@ surface_subsurfaces_boundingbox(struct weston_surface *surface, int32_t *x, int surface_get_label(struct weston_surface *surface, char *buf, size_t len); -/* helper to create a view w/ a color - */ -struct weston_view * +/* helper to create a view w/ a color */ +struct weston_curtain * weston_curtain_create(struct weston_compositor *compositor, struct weston_curtain_params *params); +void +weston_curtain_destroy(struct weston_curtain *curtain); diff --git a/tests/safe-signal-output-removal-test.c b/tests/safe-signal-output-removal-test.c index 57268120..6eb28085 100644 --- a/tests/safe-signal-output-removal-test.c +++ b/tests/safe-signal-output-removal-test.c @@ -38,7 +38,7 @@ struct test_output { struct weston_compositor *compositor; struct weston_output *output; struct wl_listener output_destroy_listener; - struct weston_view *view; + struct weston_curtain *curtain; }; static enum test_result_code @@ -60,8 +60,8 @@ output_destroy(struct test_output *t_output) t_output->output = NULL; t_output->output_destroy_listener.notify = NULL; - if (t_output->view) - weston_surface_destroy(t_output->view->surface); + if (t_output->curtain) + weston_curtain_destroy(t_output->curtain); wl_list_remove(&t_output->output_destroy_listener.link); free(t_output); @@ -88,9 +88,9 @@ output_create_view(struct test_output *t_output) .surface_private = NULL, }; - t_output->view = weston_curtain_create(t_output->compositor, - &curtain_params); - weston_view_set_output(t_output->view, t_output->output); + t_output->curtain = weston_curtain_create(t_output->compositor, + &curtain_params); + weston_view_set_output(t_output->curtain->view, t_output->output); /* weston_compositor_remove_output() has to be patched with * weston_signal_emit_mutable() to avoid signal corruption */ From 15a553053a35c68212b9d99927261cc0d7a8c473 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 18 Jan 2022 23:01:56 +0000 Subject: [PATCH 106/609] desktop-shell: Reuse curtains for fades Use the common infrastructure we have, rather than open-coding again. In changing to the common weston_curtain infrastructure which was introduced in a previous commit, there are two small functional derivations. After adding the view to a layer, we explicitly call weston_view_geometry_dirty(). This is believed to have no functional impact, as weston_views have their geometry-dirty flag set when they are created. weston_surface_damage() is also called to ensure that the surface is scheduled for a repaint. As there is currently no good common mechanic in the common code to ensure that output repaint will occur, due to the damage propagating outwards from the weston_surface, we are forced to call this to predictably ensure that the output will be repainted after we have made this change to the scene graph which should result in the user observing the new content being repainted. It is possible that these changes are not strictly required in order for the correct behaviour to occur. However, it is felt that explicitly adding these calls is perhaps the least fragile way to ensure that the behaviour continues to be correct and breakage is not observed, especially with both view mapping and surface damage identified as areas for future work which could be beneficial to Weston. If it is felt that these calls can be removed, then this is certainly possible at a later stage. Lastly, there are many users within desktop-shell of the common pattern of creating a weston_curtain and inserting it into the scene graph to be painted. These users have been an area of both theoretical concern and real-life observed fragility and breakage recently. By making each user follow a common and predictable pattern of usage, each user is no longer its own special case. This should make it easier to make the desktop-shell codebase more robust, not only simplifying the codebase, but improving observed behaviour. In doing this, it is hoped that future structural and interface changes become easier to make, allowing us to improve some of the more difficult corners of the libweston API. Signed-off-by: Daniel Stone --- desktop-shell/shell.c | 86 ++++++++++++++++++++++--------------------- desktop-shell/shell.h | 2 +- 2 files changed, 45 insertions(+), 43 deletions(-) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 641254d3..2b3ec429 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -3847,8 +3847,8 @@ shell_fade_done_for_output(struct weston_view_animation *animation, void *data) shell_output->fade.animation = NULL; switch (shell_output->fade.type) { case FADE_IN: - weston_surface_destroy(shell_output->fade.view->surface); - shell_output->fade.view = NULL; + weston_curtain_destroy(shell_output->fade.curtain); + shell_output->fade.curtain = NULL; break; case FADE_OUT: lock(shell); @@ -3858,33 +3858,35 @@ shell_fade_done_for_output(struct weston_view_animation *animation, void *data) } } -static struct weston_view * -shell_fade_create_surface_for_output(struct desktop_shell *shell, struct shell_output *shell_output) +static struct weston_curtain * +shell_fade_create_view_for_output(struct desktop_shell *shell, + struct shell_output *shell_output) { struct weston_compositor *compositor = shell->compositor; - struct weston_surface *surface; - struct weston_view *view; + struct weston_output *output = shell_output->output; + struct weston_curtain_params curtain_params = { + .r = 0.0, .g = 0.0, .b = 0.0, .a = 1.0, + .x = output->x, .y = output->y, + .width = output->width, .height = output->height, + .surface_committed = black_surface_committed, + .get_label = black_surface_get_label, + .surface_private = shell_output, + .capture_input = false, + }; + struct weston_curtain *curtain; - surface = weston_surface_create(compositor); - if (!surface) - return NULL; + curtain = weston_curtain_create(compositor, &curtain_params); + assert(curtain); - view = weston_view_create(surface); - if (!view) { - weston_surface_destroy(surface); - return NULL; - } + weston_view_set_output(curtain->view, output); + curtain->view->is_mapped = true; - weston_surface_set_size(surface, shell_output->output->width, shell_output->output->height); - weston_view_set_position(view, shell_output->output->x, shell_output->output->y); - weston_surface_set_color(surface, 0.0, 0.0, 0.0, 1.0); weston_layer_entry_insert(&compositor->fade_layer.view_list, - &view->layer_link); - pixman_region32_init(&surface->input); - surface->is_mapped = true; - view->is_mapped = true; + &curtain->view->layer_link); + weston_view_geometry_dirty(curtain->view); + weston_surface_damage(curtain->view->surface); - return view; + return curtain; } static void @@ -3909,28 +3911,29 @@ shell_fade(struct desktop_shell *shell, enum fade_type type) wl_list_for_each(shell_output, &shell->output_list, link) { shell_output->fade.type = type; - if (shell_output->fade.view == NULL) { - shell_output->fade.view = shell_fade_create_surface_for_output(shell, shell_output); - if (!shell_output->fade.view) + if (shell_output->fade.curtain == NULL) { + shell_output->fade.curtain = + shell_fade_create_view_for_output(shell, shell_output); + if (!shell_output->fade.curtain) continue; - shell_output->fade.view->alpha = 1.0 - tint; - weston_view_update_transform(shell_output->fade.view); + shell_output->fade.curtain->view->alpha = 1.0 - tint; + weston_view_update_transform(shell_output->fade.curtain->view); } - if (shell_output->fade.view->output == NULL) { + if (shell_output->fade.curtain->view->output == NULL) { /* If the black view gets a NULL output, we lost the * last output and we'll just cancel the fade. This * happens when you close the last window under the * X11 or Wayland backends. */ shell->locked = false; - weston_surface_destroy(shell_output->fade.view->surface); - shell_output->fade.view = NULL; + weston_curtain_destroy(shell_output->fade.curtain); + shell_output->fade.curtain = NULL; } else if (shell_output->fade.animation) { weston_fade_update(shell_output->fade.animation, tint); } else { shell_output->fade.animation = - weston_fade_run(shell_output->fade.view, + weston_fade_run(shell_output->fade.curtain->view, 1.0 - tint, tint, 300.0, shell_fade_done_for_output, shell_output); } @@ -3950,8 +3953,8 @@ do_shell_fade_startup(void *data) "unexpected fade-in animation type %d\n", shell->startup_animation_type); wl_list_for_each(shell_output, &shell->output_list, link) { - weston_surface_destroy(shell_output->fade.view->surface); - shell_output->fade.view = NULL; + weston_curtain_destroy(shell_output->fade.curtain); + shell_output->fade.curtain = NULL; } } } @@ -4002,18 +4005,19 @@ shell_fade_init(struct desktop_shell *shell) return; wl_list_for_each(shell_output, &shell->output_list, link) { - if (shell_output->fade.view != NULL) { + if (shell_output->fade.curtain != NULL) { weston_log("%s: warning: fade surface already exists\n", __func__); continue; } - shell_output->fade.view = shell_fade_create_surface_for_output(shell, shell_output); - if (!shell_output->fade.view) + shell_output->fade.curtain = + shell_fade_create_view_for_output(shell, shell_output); + if (!shell_output->fade.curtain) continue; - weston_view_update_transform(shell_output->fade.view); - weston_surface_damage(shell_output->fade.view->surface); + weston_view_update_transform(shell_output->fade.curtain->view); + weston_surface_damage(shell_output->fade.curtain->view->surface); loop = wl_display_get_event_loop(shell->compositor->wl_display); shell_output->fade.startup_timer = @@ -4667,10 +4671,8 @@ shell_output_destroy(struct shell_output *shell_output) shell_output->fade.animation = NULL; } - if (shell_output->fade.view) { - /* destroys the view as well */ - weston_surface_destroy(shell_output->fade.view->surface); - } + if (shell_output->fade.curtain) + weston_curtain_destroy(shell_output->fade.curtain); if (shell_output->fade.startup_timer) wl_event_source_remove(shell_output->fade.startup_timer); diff --git a/desktop-shell/shell.h b/desktop-shell/shell.h index dc4fc9c0..01af4aaf 100644 --- a/desktop-shell/shell.h +++ b/desktop-shell/shell.h @@ -120,7 +120,7 @@ struct shell_output { struct wl_listener background_surface_listener; struct { - struct weston_view *view; + struct weston_curtain *curtain; struct weston_view_animation *animation; enum fade_type type; struct wl_event_source *startup_timer; From 6cb2526b675be0c7af2840e6bd951fef64486d02 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 23 Feb 2022 13:24:49 +0000 Subject: [PATCH 107/609] Move shell-utils to its own directory shell-utils contains a number of helpers which are currently in use by both desktop-shell and kiosk-shell. In order to extend this use to fullscreen-shell as well (which can benefit from reusing the weston_curtain infrastructure to be able to create solid-colour views which may or may not be opaque, as well as one function within fullscreen-shell which was copied wholesale to shell-utils), we need to create a separate Meson dependency object, and avoid the existing pattern of including the source from shared/ within the source list for each shell. This requires creating a new top-level directory for these shared helper functions which are required by each shell, but are not part of libweston in and of itself. shell-utils depends on libweston-desktop; libweston-desktop depends on libweston; libweston depends on shared. Thus it is not possible to expose a dependency object from the shared/ directory which declares a dependency on the libweston-desktop dependency, as Meson processes directories in order and resolves variable references as they are parsed. In order to break this deadlock, this commit creates a new top-level directory called 'shell-utils' containing only this file, which can be parsed by Meson after libweston-desktop (making the libweston-desktop Meson dependency variable available to the build file to declare a dependency on that), but before the shells (making the new Meson depenendency object available to each shell which wishes to use it). This commit contains no functional changes to any observable code. Signed-off-by: Daniel Stone --- desktop-shell/meson.build | 2 +- desktop-shell/shell.c | 2 +- kiosk-shell/kiosk-shell.c | 2 +- kiosk-shell/meson.build | 2 +- meson.build | 1 + shell-utils/meson.build | 5 +++++ {shared => shell-utils}/shell-utils.c | 2 +- {shared => shell-utils}/shell-utils.h | 0 tests/meson.build | 3 +-- tests/safe-signal-output-removal-test.c | 2 +- 10 files changed, 13 insertions(+), 8 deletions(-) create mode 100644 shell-utils/meson.build rename {shared => shell-utils}/shell-utils.c (99%) rename {shared => shell-utils}/shell-utils.h (100%) diff --git a/desktop-shell/meson.build b/desktop-shell/meson.build index cc7215cb..c6f374ee 100644 --- a/desktop-shell/meson.build +++ b/desktop-shell/meson.build @@ -5,7 +5,6 @@ if get_option('shell-desktop') 'shell.c', 'exposay.c', 'input-panel.c', - '../shared/shell-utils.c', weston_desktop_shell_server_protocol_h, weston_desktop_shell_protocol_c, input_method_unstable_v1_server_protocol_h, @@ -17,6 +16,7 @@ if get_option('shell-desktop') dep_libshared, dep_lib_desktop, dep_libweston_public, + dep_shell_utils, ] plugin_shell_desktop = shared_library( 'desktop-shell', diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 2b3ec429..ce8b18ec 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -41,8 +41,8 @@ #include "weston-desktop-shell-server-protocol.h" #include #include "shared/helpers.h" -#include "shared/shell-utils.h" #include "shared/timespec-util.h" +#include "shell-utils.h" #include #define DEFAULT_NUM_WORKSPACES 1 diff --git a/kiosk-shell/kiosk-shell.c b/kiosk-shell/kiosk-shell.c index 66aa3910..950e5811 100644 --- a/kiosk-shell/kiosk-shell.c +++ b/kiosk-shell/kiosk-shell.c @@ -33,7 +33,7 @@ #include "kiosk-shell-grab.h" #include "compositor/weston.h" #include "shared/helpers.h" -#include "shared/shell-utils.h" +#include "shell-utils.h" #include diff --git a/kiosk-shell/meson.build b/kiosk-shell/meson.build index c3a37da2..95d94721 100644 --- a/kiosk-shell/meson.build +++ b/kiosk-shell/meson.build @@ -2,7 +2,6 @@ if get_option('shell-kiosk') srcs_shell_kiosk = [ 'kiosk-shell.c', 'kiosk-shell-grab.c', - '../shared/shell-utils.c', weston_desktop_shell_server_protocol_h, weston_desktop_shell_protocol_c, input_method_unstable_v1_server_protocol_h, @@ -14,6 +13,7 @@ if get_option('shell-kiosk') dep_libshared, dep_lib_desktop, dep_libweston_public, + dep_shell_utils, ] plugin_shell_kiosk = shared_library( 'kiosk-shell', diff --git a/meson.build b/meson.build index 182d3f7d..67917dea 100644 --- a/meson.build +++ b/meson.build @@ -171,6 +171,7 @@ subdir('libweston') subdir('libweston-desktop') subdir('xwayland') subdir('compositor') +subdir('shell-utils') subdir('desktop-shell') subdir('fullscreen-shell') subdir('ivi-shell') diff --git a/shell-utils/meson.build b/shell-utils/meson.build new file mode 100644 index 00000000..02ae9001 --- /dev/null +++ b/shell-utils/meson.build @@ -0,0 +1,5 @@ +dep_shell_utils = declare_dependency( + sources: 'shell-utils.c', + include_directories: include_directories('.'), + dependencies: dep_lib_desktop +) diff --git a/shared/shell-utils.c b/shell-utils/shell-utils.c similarity index 99% rename from shared/shell-utils.c rename to shell-utils/shell-utils.c index 7a2f1df5..51089328 100644 --- a/shared/shell-utils.c +++ b/shell-utils/shell-utils.c @@ -25,7 +25,7 @@ */ #include "config.h" -#include "shared/shell-utils.h" +#include "shell-utils.h" #include struct weston_output * diff --git a/shared/shell-utils.h b/shell-utils/shell-utils.h similarity index 100% rename from shared/shell-utils.h rename to shell-utils/shell-utils.h diff --git a/tests/meson.build b/tests/meson.build index 5b1f0418..da78b407 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -217,9 +217,8 @@ tests = [ { 'name': 'safe-signal-output-removal', 'sources': [ 'safe-signal-output-removal-test.c', - '../shared/shell-utils.c', ], - 'dep_objs': [ dep_lib_desktop ] + 'dep_objs': [ dep_lib_desktop, dep_shell_utils ] }, ] diff --git a/tests/safe-signal-output-removal-test.c b/tests/safe-signal-output-removal-test.c index 6eb28085..596ca498 100644 --- a/tests/safe-signal-output-removal-test.c +++ b/tests/safe-signal-output-removal-test.c @@ -30,7 +30,7 @@ #include #include "../shared/signal.h" -#include "../shared/shell-utils.h" +#include "shell-utils.h" #include "weston-test-client-helper.h" #include "weston-test-fixture-compositor.h" From 577a832f41cc8025ef443809b85a1e36752c477b Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 18 Jan 2022 23:07:05 +0000 Subject: [PATCH 108/609] test/desktop-shell: Use weston_curtain Use the helper we have for these, rather than open-coding. This commit is not believed to result in any functional changes. Signed-off-by: Daniel Stone --- tests/meson.build | 2 +- tests/weston-test-desktop-shell.c | 48 +++++++++++++------------------ 2 files changed, 21 insertions(+), 29 deletions(-) diff --git a/tests/meson.build b/tests/meson.build index da78b407..889b55f4 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -2,7 +2,7 @@ plugin_test_shell_desktop = shared_library( 'weston-test-desktop-shell', 'weston-test-desktop-shell.c', include_directories: common_inc, - dependencies: [ dep_lib_desktop, dep_libweston_public, dep_libexec_weston ], + dependencies: [ dep_lib_desktop, dep_libweston_public, dep_libexec_weston, dep_shell_utils ], name_prefix: '', install: false ) diff --git a/tests/weston-test-desktop-shell.c b/tests/weston-test-desktop-shell.c index 91654baa..e6e208e6 100644 --- a/tests/weston-test-desktop-shell.c +++ b/tests/weston-test-desktop-shell.c @@ -39,14 +39,14 @@ #include "compositor/weston.h" #include #include "shared/helpers.h" +#include "shell-utils.h" #include struct desktest_shell { struct wl_listener compositor_destroy_listener; struct weston_desktop *desktop; struct weston_layer background_layer; - struct weston_surface *background_surface; - struct weston_view *background_view; + struct weston_curtain *background; struct weston_layer layer; struct weston_view *view; }; @@ -174,8 +174,7 @@ shell_destroy(struct wl_listener *listener, void *data) wl_list_remove(&dts->compositor_destroy_listener.link); weston_desktop_destroy(dts->desktop); - weston_view_destroy(dts->background_view); - weston_surface_destroy(dts->background_surface); + weston_curtain_destroy(dts->background); weston_layer_fini(&dts->layer); weston_layer_fini(&dts->background_layer); @@ -188,6 +187,14 @@ wet_shell_init(struct weston_compositor *ec, int *argc, char *argv[]) { struct desktest_shell *dts; + struct weston_curtain_params background_params = { + .r = 0.16, .g = 0.32, .b = 0.48, .a = 1.0, + .x = 0, .y = 0, .width = 2000, .height = 2000, + .capture_input = true, + .surface_committed = NULL, + .get_label = background_get_label, + .surface_private = NULL, + }; dts = zalloc(sizeof *dts); if (!dts) @@ -208,28 +215,16 @@ wet_shell_init(struct weston_compositor *ec, weston_layer_set_position(&dts->background_layer, WESTON_LAYER_POSITION_BACKGROUND); - dts->background_surface = weston_surface_create(ec); - if (dts->background_surface == NULL) + dts->background = weston_curtain_create(ec, &background_params); + if (dts->background == NULL) goto out_free; - - dts->background_view = weston_view_create(dts->background_surface); - if (dts->background_view == NULL) - goto out_surface; - - weston_surface_set_role(dts->background_surface, + weston_surface_set_role(dts->background->view->surface, "test-desktop background", NULL, 0); - weston_surface_set_label_func(dts->background_surface, - background_get_label); - weston_surface_set_color(dts->background_surface, 0.16, 0.32, 0.48, 1.); - pixman_region32_fini(&dts->background_surface->opaque); - pixman_region32_init_rect(&dts->background_surface->opaque, 0, 0, 2000, 2000); - pixman_region32_fini(&dts->background_surface->input); - pixman_region32_init_rect(&dts->background_surface->input, 0, 0, 2000, 2000); - - weston_surface_set_size(dts->background_surface, 2000, 2000); - weston_view_set_position(dts->background_view, 0, 0); - weston_layer_entry_insert(&dts->background_layer.view_list, &dts->background_view->layer_link); - weston_view_update_transform(dts->background_view); + + weston_view_set_position(dts->background->view, 0, 0); + weston_layer_entry_insert(&dts->background_layer.view_list, + &dts->background->view->layer_link); + weston_view_update_transform(dts->background->view); dts->desktop = weston_desktop_create(ec, &shell_desktop_api, dts); if (dts->desktop == NULL) @@ -240,10 +235,7 @@ wet_shell_init(struct weston_compositor *ec, return 0; out_view: - weston_view_destroy(dts->background_view); - -out_surface: - weston_surface_destroy(dts->background_surface); + weston_curtain_destroy(dts->background); out_free: wl_list_remove(&dts->compositor_destroy_listener.link); From b94d69b926f060a48a4ba070c4b416aa9a15e90b Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 23 Feb 2022 13:31:26 +0000 Subject: [PATCH 109/609] fullscreen-shell: Link with shell-utils helpers Unlike desktop-shell and kiosk-shell, the fullscreen-shell does not link with the common shell-utils helpers. This is largely because fullscreen-shell is largely in 'maintenance mode', seeing little more than occasional bug fixes or changes required to accommodate new interfaces. This commit adds a dependency from fullscreen-shell to use the shell-utils helper, in order to allow fullscreen-shell to use the new weston_curtain infrastructure, rather than continuing to open-code the common pattern of creating a surface and view consisting only of a solid colour for the background of fullscreen surfaces which do not wholly cover the output. In doing this, the 'surface_subsurfaces_boundingbox()' function is removed, as this has been duplicated between the fullscreen-shell and the common helper 'library'. There is no functional change within this commit, as the two functions were identical, other than a change to the comment which identifies a known bug within this helper. Signed-off-by: Daniel Stone --- fullscreen-shell/fullscreen-shell.c | 36 +---------------------------- fullscreen-shell/meson.build | 3 ++- 2 files changed, 3 insertions(+), 36 deletions(-) diff --git a/fullscreen-shell/fullscreen-shell.c b/fullscreen-shell/fullscreen-shell.c index 6975f65e..e122bf8a 100644 --- a/fullscreen-shell/fullscreen-shell.c +++ b/fullscreen-shell/fullscreen-shell.c @@ -37,6 +37,7 @@ #include "compositor/weston.h" #include "fullscreen-shell-unstable-v1-server-protocol.h" #include "shared/helpers.h" +#include "shell-utils.h" struct fullscreen_shell { struct wl_client *client; @@ -373,41 +374,6 @@ restore_output_mode(struct weston_output *output) weston_output_mode_switch_to_native(output); } -/* - * Returns the bounding box of a surface and all its sub-surfaces, - * in surface-local coordinates. */ -static void -surface_subsurfaces_boundingbox(struct weston_surface *surface, int32_t *x, - int32_t *y, int32_t *w, int32_t *h) { - pixman_region32_t region; - pixman_box32_t *box; - struct weston_subsurface *subsurface; - - pixman_region32_init_rect(®ion, 0, 0, - surface->width, - surface->height); - - wl_list_for_each(subsurface, &surface->subsurface_list, parent_link) { - pixman_region32_union_rect(®ion, ®ion, - subsurface->position.x, - subsurface->position.y, - subsurface->surface->width, - subsurface->surface->height); - } - - box = pixman_region32_extents(®ion); - if (x) - *x = box->x1; - if (y) - *y = box->y1; - if (w) - *w = box->x2 - box->x1; - if (h) - *h = box->y2 - box->y1; - - pixman_region32_fini(®ion); -} - static void fs_output_center_view(struct fs_output *fsout) { diff --git a/fullscreen-shell/meson.build b/fullscreen-shell/meson.build index 02a6c6f1..4ecac5bc 100644 --- a/fullscreen-shell/meson.build +++ b/fullscreen-shell/meson.build @@ -4,9 +4,10 @@ if get_option('shell-fullscreen') fullscreen_shell_unstable_v1_server_protocol_h, fullscreen_shell_unstable_v1_protocol_c, ] - deps_shell_fullscreen=[ + deps_shell_fullscreen = [ dep_libweston_public, dep_libexec_weston, + dep_shell_utils, ] shared_library( 'fullscreen-shell', From c3415aed231e0982268e4c9b7a048e2d175b5fa5 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 18 Jan 2022 23:13:18 +0000 Subject: [PATCH 110/609] fullscreen-shell: Use weston_curtain for black view Use the common helper provided by the shell-utils helper dependency, rather than rolling our own. This commit currently introduces no functional change to fullscreen-shell, as the 'curtain' provided by shell-utils behaves identically to the previous solid-color surface created by fullscreen-shell, given the parameters provided to weston_curtain_create(). However, now that a common weston_curtain implementation is being used rather than an open-coded variant, future changes to the implementation of weston_curtain will result in changes to this code called by fullscreen-shell, although it is intended that these will not result in any user-visible behavioural changes. Signed-off-by: Daniel Stone --- fullscreen-shell/fullscreen-shell.c | 58 ++++++++++++----------------- 1 file changed, 24 insertions(+), 34 deletions(-) diff --git a/fullscreen-shell/fullscreen-shell.c b/fullscreen-shell/fullscreen-shell.c index e122bf8a..c02ac1c3 100644 --- a/fullscreen-shell/fullscreen-shell.c +++ b/fullscreen-shell/fullscreen-shell.c @@ -81,7 +81,7 @@ struct fs_output { struct weston_surface *surface; struct wl_listener surface_destroyed; struct weston_view *view; - struct weston_view *black_view; + struct weston_curtain *curtain; struct weston_transform transform; /* matrix from x, y */ int presented_for_mode; @@ -227,37 +227,27 @@ black_surface_committed(struct weston_surface *es, int32_t sx, int32_t sy) { } -static struct weston_view * -create_black_surface(struct weston_compositor *ec, struct fs_output *fsout, - float x, float y, int w, int h) +static struct weston_curtain * +create_curtain(struct weston_compositor *ec, struct fs_output *fsout, + float x, float y, int w, int h) { - struct weston_surface *surface = NULL; - struct weston_view *view; + struct weston_curtain_params curtain_params = { + .r = 0.0, .g = 0.0, .b = 0.0, .a = 1.0, + .x = x, .y = y, .width = w, .height = h, + .surface_committed = black_surface_committed, + .get_label = NULL, + .surface_private = fsout, + .capture_input = true, + }; + struct weston_curtain *curtain; - surface = weston_surface_create(ec); - if (surface == NULL) { - weston_log("no memory\n"); - return NULL; - } - view = weston_view_create(surface); - if (!view) { - weston_surface_destroy(surface); + curtain = weston_curtain_create(ec, &curtain_params); + if (!curtain) { weston_log("no memory\n"); return NULL; } - surface->committed = black_surface_committed; - surface->committed_private = fsout; - weston_surface_set_color(surface, 0.0f, 0.0f, 0.0f, 1.0f); - pixman_region32_fini(&surface->opaque); - pixman_region32_init_rect(&surface->opaque, 0, 0, w, h); - pixman_region32_fini(&surface->input); - pixman_region32_init_rect(&surface->input, 0, 0, w, h); - - weston_surface_set_size(surface, w, h); - weston_view_set_position(view, x, y); - - return view; + return curtain; } static void @@ -334,13 +324,13 @@ fs_output_create(struct fullscreen_shell *shell, struct weston_output *output) fsout->surface_destroyed.notify = surface_destroyed; fsout->pending.surface_destroyed.notify = pending_surface_destroyed; - fsout->black_view = create_black_surface(shell->compositor, fsout, - output->x, output->y, - output->width, output->height); - fsout->black_view->surface->is_mapped = true; - fsout->black_view->is_mapped = true; + fsout->curtain = create_curtain(shell->compositor, fsout, + output->x, output->y, + output->width, output->height); + fsout->curtain->view->surface->is_mapped = true; + fsout->curtain->view->is_mapped = true; weston_layer_entry_insert(&shell->layer.view_list, - &fsout->black_view->layer_link); + &fsout->curtain->view->layer_link); wl_list_init(&fsout->transform.link); if (!wl_list_empty(&shell->default_surface_list)) { @@ -486,10 +476,10 @@ fs_output_configure_simple(struct fs_output *fsout, break; } - weston_view_set_position(fsout->black_view, + weston_view_set_position(fsout->curtain->view, fsout->output->x - surf_x, fsout->output->y - surf_y); - weston_surface_set_size(fsout->black_view->surface, + weston_surface_set_size(fsout->curtain->view->surface, fsout->output->width, fsout->output->height); } From c448b938f788013dd659b908fa1b0ff54b188031 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Tue, 16 Nov 2021 16:37:04 +0100 Subject: [PATCH 111/609] backend-drm: always get pending_state from backend The pending_state is already stored in the backend and can be directly retrieved from there. This avoids involving the compositor in passing state between the repaint phases for a single backend. Signed-off-by: Michael Tretter --- libweston/backend-drm/drm-virtual.c | 7 ++++++- libweston/backend-drm/drm.c | 20 ++++++++++++-------- libweston/backend-drm/state-propose.c | 2 +- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/libweston/backend-drm/drm-virtual.c b/libweston/backend-drm/drm-virtual.c index 597e71c1..1a383a54 100644 --- a/libweston/backend-drm/drm-virtual.c +++ b/libweston/backend-drm/drm-virtual.c @@ -188,14 +188,18 @@ drm_virtual_output_repaint(struct weston_output *output_base, pixman_region32_t *damage, void *repaint_data) { - struct drm_pending_state *pending_state = repaint_data; struct drm_output_state *state = NULL; struct drm_output *output = to_drm_output(output_base); struct drm_plane *scanout_plane = output->scanout_plane; struct drm_plane_state *scanout_state; + struct drm_pending_state *pending_state; + struct drm_backend *backend; assert(output->virtual); + backend = output->backend; + pending_state = backend->repaint_data; + if (output->disable_pending || output->destroy_pending) goto err; @@ -336,6 +340,7 @@ drm_virtual_output_create(struct weston_compositor *c, char *name) } output->virtual = true; + output->backend = b; output->gbm_bo_flags = GBM_BO_USE_LINEAR | GBM_BO_USE_RENDERING; weston_output_init(&output->base, c, name); diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index 83297cd5..0bd8f1a3 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -445,13 +445,17 @@ drm_output_repaint(struct weston_output *output_base, pixman_region32_t *damage, void *repaint_data) { - struct drm_pending_state *pending_state = repaint_data; struct drm_output *output = to_drm_output(output_base); struct drm_output_state *state = NULL; struct drm_plane_state *scanout_state; + struct drm_pending_state *pending_state; + struct drm_backend *backend; assert(!output->virtual); + backend = output->backend; + pending_state = backend->repaint_data; + if (output->disable_pending || output->destroy_pending) goto err; @@ -610,20 +614,20 @@ static void * drm_repaint_begin(struct weston_compositor *compositor) { struct drm_backend *b = to_drm_backend(compositor); - struct drm_pending_state *ret; + struct drm_pending_state *pending_state; - ret = drm_pending_state_alloc(b); - b->repaint_data = ret; + pending_state = drm_pending_state_alloc(b); + b->repaint_data = pending_state; if (weston_log_scope_is_enabled(b->debug)) { char *dbg = weston_compositor_print_scene_graph(compositor); drm_debug(b, "[repaint] Beginning repaint; pending_state %p\n", - ret); + b->repaint_data); drm_debug(b, "%s", dbg); free(dbg); } - return ret; + return NULL; } /** @@ -639,7 +643,7 @@ static int drm_repaint_flush(struct weston_compositor *compositor, void *repaint_data) { struct drm_backend *b = to_drm_backend(compositor); - struct drm_pending_state *pending_state = repaint_data; + struct drm_pending_state *pending_state = b->repaint_data; int ret; ret = drm_pending_state_apply(pending_state); @@ -662,7 +666,7 @@ static void drm_repaint_cancel(struct weston_compositor *compositor, void *repaint_data) { struct drm_backend *b = to_drm_backend(compositor); - struct drm_pending_state *pending_state = repaint_data; + struct drm_pending_state *pending_state = b->repaint_data; drm_pending_state_free(pending_state); drm_debug(b, "[repaint] cancel pending_state %p\n", pending_state); diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index 9086cbc4..d85ef0da 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -892,7 +892,7 @@ void drm_assign_planes(struct weston_output *output_base, void *repaint_data) { struct drm_backend *b = to_drm_backend(output_base->compositor); - struct drm_pending_state *pending_state = repaint_data; + struct drm_pending_state *pending_state = b->repaint_data; struct drm_output *output = to_drm_output(output_base); struct drm_output_state *state = NULL; struct drm_plane_state *plane_state; From 6ee6e76a0c767ff7d55e9df27c839f976d75b66f Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Wed, 3 Nov 2021 11:14:11 +0100 Subject: [PATCH 112/609] compositor: remove repaint_data from compositor The repaint_data is entirely backend specific. Moreover, it is only used by the drm backend, while other backends ignore the repaint data. There will always be only one repaint active, thus, there is no need to pass the repaint data from the outside. The repaint_data breaks with the multi-backend series, which calls repaint begin for all backends to get the repaint_data. The repaint_data of the last backend will then be passed to all other backend. At the moment, this works, because the drm backend is the only backend that implements the begin_repaint call. Another option would be to track the repaint data per backend in the compositor, but actually, it the backend needs to track state across the calls, it's its own responsibility. Signed-off-by: Michael Tretter --- include/libweston/libweston.h | 6 ++---- libweston/backend-drm/drm-internal.h | 2 +- libweston/backend-drm/drm-virtual.c | 3 +-- libweston/backend-drm/drm.c | 12 ++++-------- libweston/backend-drm/state-propose.c | 2 +- libweston/backend-headless/headless.c | 3 +-- libweston/backend-rdp/rdp.c | 3 +-- libweston/backend-wayland/wayland.c | 6 ++---- libweston/backend-x11/x11.c | 6 ++---- libweston/backend.h | 12 +++--------- libweston/compositor.c | 22 +++++++++------------- 11 files changed, 27 insertions(+), 50 deletions(-) diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 18c1f8af..22cd81fc 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -336,11 +336,9 @@ struct weston_output { bool allow_protection; int (*start_repaint_loop)(struct weston_output *output); - int (*repaint)(struct weston_output *output, - pixman_region32_t *damage, - void *repaint_data); + int (*repaint)(struct weston_output *output, pixman_region32_t *damage); void (*destroy)(struct weston_output *output); - void (*assign_planes)(struct weston_output *output, void *repaint_data); + void (*assign_planes)(struct weston_output *output); int (*switch_mode)(struct weston_output *output, struct weston_mode *mode); /* backlight values are on 0-255 range, where higher is brighter */ diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index 0331e1de..df704926 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -790,7 +790,7 @@ void drm_plane_reset_state(struct drm_plane *plane); void -drm_assign_planes(struct weston_output *output_base, void *repaint_data); +drm_assign_planes(struct weston_output *output_base); bool drm_plane_is_available(struct drm_plane *plane, struct drm_output *output); diff --git a/libweston/backend-drm/drm-virtual.c b/libweston/backend-drm/drm-virtual.c index 1a383a54..3be2feb3 100644 --- a/libweston/backend-drm/drm-virtual.c +++ b/libweston/backend-drm/drm-virtual.c @@ -185,8 +185,7 @@ drm_virtual_output_submit_frame(struct drm_output *output, static int drm_virtual_output_repaint(struct weston_output *output_base, - pixman_region32_t *damage, - void *repaint_data) + pixman_region32_t *damage) { struct drm_output_state *state = NULL; struct drm_output *output = to_drm_output(output_base); diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index 0bd8f1a3..225d47e2 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -441,9 +441,7 @@ drm_output_render(struct drm_output_state *state, pixman_region32_t *damage) } static int -drm_output_repaint(struct weston_output *output_base, - pixman_region32_t *damage, - void *repaint_data) +drm_output_repaint(struct weston_output *output_base, pixman_region32_t *damage) { struct drm_output *output = to_drm_output(output_base); struct drm_output_state *state = NULL; @@ -610,7 +608,7 @@ finish_frame: * a new pending_state structure to own any output state created by individual * output repaint functions until the repaint is flushed or cancelled. */ -static void * +static void drm_repaint_begin(struct weston_compositor *compositor) { struct drm_backend *b = to_drm_backend(compositor); @@ -626,8 +624,6 @@ drm_repaint_begin(struct weston_compositor *compositor) drm_debug(b, "%s", dbg); free(dbg); } - - return NULL; } /** @@ -640,7 +636,7 @@ drm_repaint_begin(struct weston_compositor *compositor) * state will be freed. */ static int -drm_repaint_flush(struct weston_compositor *compositor, void *repaint_data) +drm_repaint_flush(struct weston_compositor *compositor) { struct drm_backend *b = to_drm_backend(compositor); struct drm_pending_state *pending_state = b->repaint_data; @@ -663,7 +659,7 @@ drm_repaint_flush(struct weston_compositor *compositor, void *repaint_data) * held across the repaint cycle should be discarded. */ static void -drm_repaint_cancel(struct weston_compositor *compositor, void *repaint_data) +drm_repaint_cancel(struct weston_compositor *compositor) { struct drm_backend *b = to_drm_backend(compositor); struct drm_pending_state *pending_state = b->repaint_data; diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index d85ef0da..953e3a9f 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -889,7 +889,7 @@ err: } void -drm_assign_planes(struct weston_output *output_base, void *repaint_data) +drm_assign_planes(struct weston_output *output_base) { struct drm_backend *b = to_drm_backend(output_base->compositor); struct drm_pending_state *pending_state = b->repaint_data; diff --git a/libweston/backend-headless/headless.c b/libweston/backend-headless/headless.c index 5c4e4ccb..5f2f2898 100644 --- a/libweston/backend-headless/headless.c +++ b/libweston/backend-headless/headless.c @@ -122,8 +122,7 @@ finish_frame_handler(void *data) static int headless_output_repaint(struct weston_output *output_base, - pixman_region32_t *damage, - void *repaint_data) + pixman_region32_t *damage) { struct headless_output *output = to_headless_output(output_base); struct weston_compositor *ec = output->base.compositor; diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index 8e333f4c..2e869662 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -326,8 +326,7 @@ rdp_output_start_repaint_loop(struct weston_output *output) } static int -rdp_output_repaint(struct weston_output *output_base, pixman_region32_t *damage, - void *repaint_data) +rdp_output_repaint(struct weston_output *output_base, pixman_region32_t *damage) { struct rdp_output *output = container_of(output_base, struct rdp_output, base); struct weston_compositor *ec = output->base.compositor; diff --git a/libweston/backend-wayland/wayland.c b/libweston/backend-wayland/wayland.c index a3bbfbf1..825faed3 100644 --- a/libweston/backend-wayland/wayland.c +++ b/libweston/backend-wayland/wayland.c @@ -524,8 +524,7 @@ wayland_output_start_repaint_loop(struct weston_output *output_base) #ifdef ENABLE_EGL static int wayland_output_repaint_gl(struct weston_output *output_base, - pixman_region32_t *damage, - void *repaint_data) + pixman_region32_t *damage) { struct wayland_output *output = to_wayland_output(output_base); struct weston_compositor *ec = output->base.compositor; @@ -636,8 +635,7 @@ wayland_shm_buffer_attach(struct wayland_shm_buffer *sb) static int wayland_output_repaint_pixman(struct weston_output *output_base, - pixman_region32_t *damage, - void *repaint_data) + pixman_region32_t *damage) { struct wayland_output *output = to_wayland_output(output_base); struct wayland_backend *b = diff --git a/libweston/backend-x11/x11.c b/libweston/backend-x11/x11.c index 5c82584b..38c72379 100644 --- a/libweston/backend-x11/x11.c +++ b/libweston/backend-x11/x11.c @@ -417,8 +417,7 @@ x11_output_start_repaint_loop(struct weston_output *output) static int x11_output_repaint_gl(struct weston_output *output_base, - pixman_region32_t *damage, - void *repaint_data) + pixman_region32_t *damage) { struct x11_output *output = to_x11_output(output_base); struct weston_compositor *ec = output->base.compositor; @@ -486,8 +485,7 @@ set_clip_for_output(struct weston_output *output_base, pixman_region32_t *region static int x11_output_repaint_shm(struct weston_output *output_base, - pixman_region32_t *damage, - void *repaint_data) + pixman_region32_t *damage) { struct x11_output *output = to_x11_output(output_base); struct weston_compositor *ec = output->base.compositor; diff --git a/libweston/backend.h b/libweston/backend.h index af0d6671..4b19b399 100644 --- a/libweston/backend.h +++ b/libweston/backend.h @@ -46,27 +46,21 @@ struct weston_backend { * Returns an opaque pointer, which the backend may use as private * data referring to the repaint cycle. */ - void * (*repaint_begin)(struct weston_compositor *compositor); + void (*repaint_begin)(struct weston_compositor *compositor); /** Cancel a repaint sequence * * Cancels a repaint sequence, when an error has occurred during * one output's repaint; see repaint_begin. - * - * @param repaint_data Data returned by repaint_begin */ - void (*repaint_cancel)(struct weston_compositor *compositor, - void *repaint_data); + void (*repaint_cancel)(struct weston_compositor *compositor); /** Conclude a repaint sequence * * Called on successful completion of a repaint sequence; see * repaint_begin. - * - * @param repaint_data Data returned by repaint_begin */ - int (*repaint_flush)(struct weston_compositor *compositor, - void *repaint_data); + int (*repaint_flush)(struct weston_compositor *compositor); /** Allocate a new output * diff --git a/libweston/compositor.c b/libweston/compositor.c index 2c1dcf0d..34631716 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -2865,7 +2865,7 @@ weston_output_take_feedback_list(struct weston_output *output, } static int -weston_output_repaint(struct weston_output *output, void *repaint_data) +weston_output_repaint(struct weston_output *output) { struct weston_compositor *ec = output->compositor; struct weston_paint_node *pnode; @@ -2905,7 +2905,7 @@ weston_output_repaint(struct weston_output *output, void *repaint_data) output->desired_protection = highest_requested; if (output->assign_planes && !output->disable_planes) { - output->assign_planes(output, repaint_data); + output->assign_planes(output); } else { wl_list_for_each(pnode, &output->paint_node_z_order_list, z_order_link) { @@ -2940,7 +2940,7 @@ weston_output_repaint(struct weston_output *output, void *repaint_data) if (output->dirty) weston_output_update_matrix(output); - r = output->repaint(output, &output_damage, repaint_data); + r = output->repaint(output, &output_damage); pixman_region32_fini(&output_damage); @@ -2976,8 +2976,7 @@ weston_output_schedule_repaint_reset(struct weston_output *output) } static int -weston_output_maybe_repaint(struct weston_output *output, struct timespec *now, - void *repaint_data) +weston_output_maybe_repaint(struct weston_output *output, struct timespec *now) { struct weston_compositor *compositor = output->compositor; int ret = 0; @@ -3007,7 +3006,7 @@ weston_output_maybe_repaint(struct weston_output *output, struct timespec *now, * something schedules a successful repaint later. As repainting may * take some time, re-read our clock as a courtesy to the next * output. */ - ret = weston_output_repaint(output, repaint_data); + ret = weston_output_repaint(output); weston_compositor_read_presentation_clock(compositor, now); if (ret != 0) goto err; @@ -3065,29 +3064,26 @@ output_repaint_timer_handler(void *data) struct weston_compositor *compositor = data; struct weston_output *output; struct timespec now; - void *repaint_data = NULL; int ret = 0; weston_compositor_read_presentation_clock(compositor, &now); compositor->last_repaint_start = now; if (compositor->backend->repaint_begin) - repaint_data = compositor->backend->repaint_begin(compositor); + compositor->backend->repaint_begin(compositor); wl_list_for_each(output, &compositor->output_list, link) { - ret = weston_output_maybe_repaint(output, &now, repaint_data); + ret = weston_output_maybe_repaint(output, &now); if (ret) break; } if (ret == 0) { if (compositor->backend->repaint_flush) - ret = compositor->backend->repaint_flush(compositor, - repaint_data); + ret = compositor->backend->repaint_flush(compositor); } else { if (compositor->backend->repaint_cancel) - compositor->backend->repaint_cancel(compositor, - repaint_data); + compositor->backend->repaint_cancel(compositor); } if (ret != 0) { From edef87469648b1c14f6049e7aa2b81c547794fc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Meier?= Date: Tue, 5 Apr 2022 18:33:40 +0200 Subject: [PATCH 113/609] libbacklight: Fix backlight never gets initialized MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In 913d7c15f79da33aa97100286df5d8f88731e252 stricter error checking was introduced to the strtol call, which broke reading backlight values. Since every sysfs backlight file ends with a newline. As noted in a comment in the previous MR to prevent damaged pointers after calling asprintf, replace every asprintf call with str_printf. Previous-MR: https://gitlab.freedesktop.org/wayland/weston/-/merge_requests/543 Signed-off-by: Sören Meier --- libweston/backend-drm/libbacklight.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/libweston/backend-drm/libbacklight.c b/libweston/backend-drm/libbacklight.c index 4bbc6db4..ca7f2d68 100644 --- a/libweston/backend-drm/libbacklight.c +++ b/libweston/backend-drm/libbacklight.c @@ -53,8 +53,10 @@ static long backlight_get(struct backlight *backlight, char *node) int fd, value; long ret; - if (asprintf(&path, "%s/%s", backlight->path, node) < 0) + str_printf(&path, "%s/%s", backlight->path, node); + if (!path) return -ENOMEM; + fd = open(path, O_RDONLY); if (fd < 0) { ret = -1; @@ -67,6 +69,9 @@ static long backlight_get(struct backlight *backlight, char *node) goto out; } + if (buffer[ret - 1] == '\n') + buffer[ret - 1] = '\0'; + if (!safe_strtoint(buffer, &value)) { ret = -1; goto out; @@ -103,7 +108,8 @@ long backlight_set_brightness(struct backlight *backlight, long brightness) int fd; long ret; - if (asprintf(&path, "%s/%s", backlight->path, "brightness") < 0) + str_printf(&path, "%s/%s", backlight->path, "brightness"); + if (!path) return -ENOMEM; fd = open(path, O_RDWR); @@ -118,7 +124,8 @@ long backlight_set_brightness(struct backlight *backlight, long brightness) goto out; } - if (asprintf(&buffer, "%ld", brightness) < 0) { + str_printf(&buffer, "%ld", brightness); + if (!buffer) { ret = -1; goto out; } @@ -171,7 +178,8 @@ struct backlight *backlight_init(struct udev_device *drm_device, if (!syspath) return NULL; - if (asprintf(&path, "%s/%s", syspath, "device") < 0) + str_printf(&path, "%s/%s", syspath, "device"); + if (!path) return NULL; ret = readlink(path, buffer, sizeof(buffer) - 1); @@ -214,11 +222,13 @@ struct backlight *backlight_init(struct udev_device *drm_device, if (entry->d_name[0] == '.') continue; - if (asprintf(&backlight_path, "%s/%s", "/sys/class/backlight", - entry->d_name) < 0) + str_printf(&backlight_path, "%s/%s", "/sys/class/backlight", + entry->d_name); + if (!backlight_path) goto err; - if (asprintf(&path, "%s/%s", backlight_path, "type") < 0) { + str_printf(&path, "%s/%s", backlight_path, "type"); + if (!path) { free(backlight_path); goto err; } @@ -255,7 +265,8 @@ struct backlight *backlight_init(struct udev_device *drm_device, free (path); - if (asprintf(&path, "%s/%s", backlight_path, "device") < 0) + str_printf(&path, "%s/%s", backlight_path, "device"); + if (!path) goto err; ret = readlink(path, buffer, sizeof(buffer) - 1); From 869cab49389cdef798ae286a310c62681b078973 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Wed, 23 Mar 2022 15:48:05 -0500 Subject: [PATCH 114/609] xwayland: Simplify HAVE_XWAYLAND_LISTENFD usage We can use it just once to define a string instead of having preprocessor conditionals sprinkled about the code. Signed-off-by: Derek Foreman --- compositor/xwayland.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/compositor/xwayland.c b/compositor/xwayland.c index 8cdf0555..06ca4919 100644 --- a/compositor/xwayland.c +++ b/compositor/xwayland.c @@ -36,6 +36,12 @@ #include #include "shared/helpers.h" +#ifdef HAVE_XWAYLAND_LISTENFD +# define LISTEN_STR "-listenfd" +#else +# define LISTEN_STR "-listen" +#endif + struct wet_xwayland { struct weston_compositor *compositor; const struct weston_xwayland_api *api; @@ -123,22 +129,13 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd xserver, display, "-rootless", -#ifdef HAVE_XWAYLAND_LISTENFD - "-listenfd", abstract_fd_str, - "-listenfd", unix_fd_str, -#else - "-listen", abstract_fd_str, - "-listen", unix_fd_str, -#endif + LISTEN_STR, abstract_fd_str, + LISTEN_STR, unix_fd_str, "-wm", wm_fd_str, "-terminate", NULL) < 0) weston_log("exec of '%s %s -rootless " -#ifdef HAVE_XWAYLAND_LISTENFD - "-listenfd %s -listenfd %s " -#else - "-listen %s -listen %s " -#endif + LISTEN_STR " %s " LISTEN_STR " %s " "-wm %s -terminate' failed: %s\n", xserver, display, abstract_fd_str, unix_fd_str, wm_fd_str, From 0ff4e478cd47c2fa873a8d270676ed5730a77d6e Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Tue, 22 Mar 2022 10:34:33 -0500 Subject: [PATCH 115/609] rdp: Fix comment regarding meaning of 120 This URL has changed Signed-off-by: Derek Foreman --- libweston/backend-rdp/rdp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index 2e869662..35c18600 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -1064,7 +1064,7 @@ xf_mouseEvent(rdpInput *input, UINT16 flags, UINT16 x, UINT16 y) * The RDP specs says the lower bits of flags contains the "the number of rotation * units the mouse wheel was rotated". * - * https://blogs.msdn.microsoft.com/oldnewthing/20130123-00/?p=5473 explains the 120 value + * https://devblogs.microsoft.com/oldnewthing/20130123-00/?p=5473 explains the 120 value */ value = -(flags & 0xff) / 120.0; if (flags & PTR_FLAGS_WHEEL_NEGATIVE) From 2f71b3c3deae1fbed60165624b538b891debf085 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Mon, 11 Apr 2022 08:22:31 -0500 Subject: [PATCH 116/609] compositor: Stop trapping SIGQUIT We've been trapping SIGQUIT for a "clean shutdown" since commit 3cad436a However, sources such as: http://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html indicate we probably shouldn't be trapping it at all, as the intent of SIGQUIT is to leave a core file and debug artifacts from the run. We should perform the minimal amount of clean up to ensure the system isn't left in an unusable state - but these days that's performed by other software such as logind. We can safely stop trapping SIGQUIT entirely. Signed-off-by: Derek Foreman --- compositor/main.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/compositor/main.c b/compositor/main.c index 56de28d0..62a7c48b 100644 --- a/compositor/main.c +++ b/compositor/main.c @@ -3181,7 +3181,7 @@ wet_main(int argc, char *argv[], const struct weston_testsuite_data *test_data) int ret = EXIT_FAILURE; char *cmdline; struct wl_display *display; - struct wl_event_source *signals[4]; + struct wl_event_source *signals[3]; struct wl_event_loop *loop; int i, fd; char *backend = NULL; @@ -3303,14 +3303,12 @@ wet_main(int argc, char *argv[], const struct weston_testsuite_data *test_data) display); signals[1] = wl_event_loop_add_signal(loop, SIGINT, on_term_signal, display); - signals[2] = wl_event_loop_add_signal(loop, SIGQUIT, on_term_signal, - display); wl_list_init(&wet.child_process_list); - signals[3] = wl_event_loop_add_signal(loop, SIGCHLD, sigchld_handler, + signals[2] = wl_event_loop_add_signal(loop, SIGCHLD, sigchld_handler, &wet); - if (!signals[0] || !signals[1] || !signals[2] || !signals[3]) + if (!signals[0] || !signals[1] || !signals[2]) goto out_signals; /* Xwayland uses SIGUSR1 for communicating with weston. Since some From f32bcfef42a0b355b47a4c0d4d5a782d0eb7b628 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Fri, 1 Apr 2022 10:09:19 -0500 Subject: [PATCH 117/609] compositor: Use sigaction to trap SIGINT signalfd interacts badly with gdb's signal trapping - when hitting ctrl-c in a debugger attached to weston, weston will receive the signal. This results in weston exiting cleanly when the intent was to use gdb to interfere with its operation. Trapping SIGINT was introduced in commit 50dc6989 which ensured we would call wl_display_terminate() on SIGINT or SIGTERM to clean up our socket. Killing weston with SIGINT is quite common for several developers, so it's important to preserve this clean shutdown behaviour, so we can't naively stop trapping SIGINT entirely. Instead, use the sigaction() function to trap SIGINT, and have the SIGINT handler send weston SIGUSR2 (SIGUSR1 is already used by xwayland). SIGUSR2 can be trapped in the proper wayland way via wl_event_loop_add_signal(). This way we can properly break our event loop and clean up on SIGINT, but we can also have gdb intercept SIGINT. There are other ways around this, but I'm hoping this one allows people to continue using ctrl-c to stop weston, and doesn't require additional project specific gdb knowledge. Signed-off-by: Derek Foreman --- compositor/main.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/compositor/main.c b/compositor/main.c index 62a7c48b..cba8dfd5 100644 --- a/compositor/main.c +++ b/compositor/main.c @@ -3175,6 +3175,12 @@ weston_log_subscribe_to_scopes(struct weston_log_context *log_ctx, weston_log_setup_scopes(log_ctx, flight_rec, flight_rec_scopes); } +static void +sigint_helper(int sig) +{ + raise(SIGUSR2); +} + WL_EXPORT int wet_main(int argc, char *argv[], const struct weston_testsuite_data *test_data) { @@ -3211,6 +3217,7 @@ wet_main(int argc, char *argv[], const struct weston_testsuite_data *test_data) struct weston_log_subscriber *logger = NULL; struct weston_log_subscriber *flight_rec = NULL; sigset_t mask; + struct sigaction action; bool wait_for_debugger = false; struct wl_protocol_logger *protologger = NULL; @@ -3301,13 +3308,27 @@ wet_main(int argc, char *argv[], const struct weston_testsuite_data *test_data) loop = wl_display_get_event_loop(display); signals[0] = wl_event_loop_add_signal(loop, SIGTERM, on_term_signal, display); - signals[1] = wl_event_loop_add_signal(loop, SIGINT, on_term_signal, + signals[1] = wl_event_loop_add_signal(loop, SIGUSR2, on_term_signal, display); wl_list_init(&wet.child_process_list); signals[2] = wl_event_loop_add_signal(loop, SIGCHLD, sigchld_handler, &wet); + /* When debugging weston, if use wl_event_loop_add_signal() to catch + * SIGINT, the debugger can't catch it, and attempting to stop + * weston from within the debugger results in weston exiting + * cleanly. + * + * Instead, use the sigaction() function, which sets up the signal + * in a way that gdb can successfully catch, but have the handler + * for SIGINT send SIGUSR2 (xwayland uses SIGUSR1), which we catch + * via wl_event_loop_add_signal(). + */ + action.sa_handler = sigint_helper; + sigemptyset(&action.sa_mask); + action.sa_flags = 0; + sigaction(SIGINT, &action, NULL); if (!signals[0] || !signals[1] || !signals[2]) goto out_signals; From bb1d19dc5e99a65bd1aa250342778a1db21c6093 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Thu, 7 Apr 2022 11:04:44 -0500 Subject: [PATCH 118/609] compositor: Launch clients in their own session When we launch clients they currently stay in the same session as weston, and share its controlling terminal. This means hitting ctrl-c in weston's controlling terminal will send SIGINT to the clients as well. It also means SIGHUP will be propagated to our launched clients if weston's controlling terminal is closed. While generally not harmful, this behaviour is not beneficial, and is present by default and not by design. Problems arise when launching weston in a debugger, as a ctrl-c sent to the debugger will be propagated not only to the debugger, but all the child processes sharing weston's session. This results in weston-desktop-shell being killed by the ctrl-c that was intended to stop weston for debugging. If weston-desktop-shell is killed within 30 second of startup, it will result weston performing a clean shutdown. This clean shutdown can make debugging a little too surprising. Ostensibly, clients launched via weston_client_launch will be wayland clients that terminate cleanly on their own if weston is killed, so there should be no need for them to remain in weston's session to catch ctrl-c from its controlling terminal. Nor should they need a controlling terminal for their general operation. Use setsid() to move them to their own session, devoid of controlling terminal, to make using a debugger a little less confusing in some cases. Signed-off-by: Derek Foreman --- compositor/main.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/compositor/main.c b/compositor/main.c index cba8dfd5..1b7e01fe 100644 --- a/compositor/main.c +++ b/compositor/main.c @@ -427,6 +427,13 @@ weston_client_launch(struct weston_compositor *compositor, } if (pid == 0) { + /* Put the client in a new session so it won't catch signals + * intended for the parent. Sharing a session can be + * confusing when launching weston under gdb, as the ctrl-c + * intended for gdb will pass to the child, and weston + * will cleanly shut down when the child exits. + */ + setsid(); child_client_exec(sv[1], path); _exit(-1); } From ca979aa219f83d11a12d87b2903eae9c2b93259c Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Tue, 12 Apr 2022 16:57:43 -0500 Subject: [PATCH 119/609] desktop-shell: Fix incorrect use of black_surface_get_label By some dark magic this accidentally doesn't crash because there are zeroes in the right places at startup, but black_surface_get_label() very much expects surface_private to be a view. Let's hand craft a new bespoke label function for this use. Signed-off-by: Derek Foreman --- desktop-shell/shell.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index ce8b18ec..0afd574d 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -3858,6 +3858,16 @@ shell_fade_done_for_output(struct weston_view_animation *animation, void *data) } } +static int +fade_surface_get_label(struct weston_surface *surface, + char *buf, size_t len) +{ + struct shell_output *output = surface->committed_private; + + return snprintf(buf, len, "desktop shell fade surface for %s", + output->output->name); +} + static struct weston_curtain * shell_fade_create_view_for_output(struct desktop_shell *shell, struct shell_output *shell_output) @@ -3869,7 +3879,7 @@ shell_fade_create_view_for_output(struct desktop_shell *shell, .x = output->x, .y = output->y, .width = output->width, .height = output->height, .surface_committed = black_surface_committed, - .get_label = black_surface_get_label, + .get_label = fade_surface_get_label, .surface_private = shell_output, .capture_input = false, }; From 23205b6b56638d5aebc22df5c778c781afee8521 Mon Sep 17 00:00:00 2001 From: James Hilliard Date: Fri, 8 Apr 2022 16:39:06 -0600 Subject: [PATCH 120/609] protocol/meson.build: install content-protection protocol xml Since this is expected to be used by applications outside of weston we should install it. Signed-off-by: James Hilliard --- protocol/meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/protocol/meson.build b/protocol/meson.build index 7d869dad..3ff640e1 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -6,6 +6,7 @@ dir_wp_base = dep_wp.get_pkgconfig_variable('pkgdatadir') install_data( [ + 'weston-content-protection.xml', 'weston-debug.xml', 'weston-direct-display.xml', ], From 516d2c020784e71a392553c63f1526e187aada75 Mon Sep 17 00:00:00 2001 From: Hideyuki Nagase Date: Wed, 16 Mar 2022 16:07:27 -0500 Subject: [PATCH 121/609] rdp: fix leak when listener implantation fails We've already allocated the listener by the time we hit this failure, so we must exit through the path that frees it. Co-authored-by: Steve Pronovost Co-authored-by: Brenton DeGeer Signed-off-by: Hideyuki Nagase Signed-off-by: Steve Pronovost Signed-off-by: Brenton DeGeer --- libweston/backend-rdp/rdp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index 35c18600..358302f5 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -1342,7 +1342,7 @@ rdp_backend_create(struct weston_compositor *compositor, } if (rdp_implant_listener(b, b->listener) < 0) - goto err_compositor; + goto err_listener; } else { /* get the socket from RDP_FD var */ fd_str = getenv("RDP_FD"); From 87bded8b54564edaecf9b44dbd0f0dc0bf33f426 Mon Sep 17 00:00:00 2001 From: Hideyuki Nagase Date: Fri, 11 Mar 2022 11:36:01 -0600 Subject: [PATCH 122/609] rdp: split off rdp.h Refactor some of rdp.c into a header file. Co-authored-by: Steve Pronovost Co-authored-by: Brenton DeGeer Signed-off-by: Hideyuki Nagase Signed-off-by: Steve Pronovost Signed-off-by: Brenton DeGeer --- libweston/backend-rdp/rdp.c | 92 +------------------------ libweston/backend-rdp/rdp.h | 129 ++++++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 90 deletions(-) create mode 100644 libweston/backend-rdp/rdp.h diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index 358302f5..218e2fcb 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -32,104 +32,16 @@ #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "rdp.h" + #include #include -#include "shared/helpers.h" #include "shared/timespec-util.h" #include #include #include "pixman-renderer.h" -#define MAX_FREERDP_FDS 32 -#define DEFAULT_AXIS_STEP_DISTANCE 10 -#define RDP_MODE_FREQ 60 * 1000 -#define DEFAULT_PIXEL_FORMAT PIXEL_FORMAT_BGRA32 - -struct rdp_output; - -struct rdp_backend { - struct weston_backend base; - struct weston_compositor *compositor; - - freerdp_listener *listener; - struct wl_event_source *listener_events[MAX_FREERDP_FDS]; - struct rdp_output *output; - - char *server_cert; - char *server_key; - char *rdp_key; - int tls_enabled; - int no_clients_resize; - int force_no_compression; -}; - -enum peer_item_flags { - RDP_PEER_ACTIVATED = (1 << 0), - RDP_PEER_OUTPUT_ENABLED = (1 << 1), -}; - -struct rdp_peers_item { - int flags; - freerdp_peer *peer; - struct weston_seat *seat; - - struct wl_list link; -}; - -struct rdp_head { - struct weston_head base; -}; - -struct rdp_output { - struct weston_output base; - struct wl_event_source *finish_frame_timer; - pixman_image_t *shadow_surface; - - struct wl_list peers; -}; - -struct rdp_peer_context { - rdpContext _p; - - struct rdp_backend *rdpBackend; - struct wl_event_source *events[MAX_FREERDP_FDS]; - RFX_CONTEXT *rfx_context; - wStream *encode_stream; - RFX_RECT *rfx_rects; - NSC_CONTEXT *nsc_context; - - struct rdp_peers_item item; -}; -typedef struct rdp_peer_context RdpPeerContext; - -static inline struct rdp_head * -to_rdp_head(struct weston_head *base) -{ - return container_of(base, struct rdp_head, base); -} - -static inline struct rdp_output * -to_rdp_output(struct weston_output *base) -{ - return container_of(base, struct rdp_output, base); -} - -static inline struct rdp_backend * -to_rdp_backend(struct weston_compositor *base) -{ - return container_of(base->backend, struct rdp_backend, base); -} - static void rdp_peer_refresh_rfx(pixman_region32_t *damage, pixman_image_t *image, freerdp_peer *peer) { diff --git a/libweston/backend-rdp/rdp.h b/libweston/backend-rdp/rdp.h new file mode 100644 index 00000000..96807f6e --- /dev/null +++ b/libweston/backend-rdp/rdp.h @@ -0,0 +1,129 @@ +/* + * Copyright © 2013 Hardening + * Copyright © 2020 Microsoft + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef RDP_H +#define RDP_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "backend.h" + +#include "shared/helpers.h" + +#define MAX_FREERDP_FDS 32 +#define DEFAULT_AXIS_STEP_DISTANCE 10 +#define RDP_MODE_FREQ 60 * 1000 +#define DEFAULT_PIXEL_FORMAT PIXEL_FORMAT_BGRA32 + +struct rdp_output; + +struct rdp_backend { + struct weston_backend base; + struct weston_compositor *compositor; + + freerdp_listener *listener; + struct wl_event_source *listener_events[MAX_FREERDP_FDS]; + struct rdp_output *output; + + char *server_cert; + char *server_key; + char *rdp_key; + int tls_enabled; + int no_clients_resize; + int force_no_compression; +}; + +enum peer_item_flags { + RDP_PEER_ACTIVATED = (1 << 0), + RDP_PEER_OUTPUT_ENABLED = (1 << 1), +}; + +struct rdp_peers_item { + int flags; + freerdp_peer *peer; + struct weston_seat *seat; + + struct wl_list link; +}; + +struct rdp_head { + struct weston_head base; +}; + +struct rdp_output { + struct weston_output base; + struct wl_event_source *finish_frame_timer; + pixman_image_t *shadow_surface; + + struct wl_list peers; +}; + +struct rdp_peer_context { + rdpContext _p; + + struct rdp_backend *rdpBackend; + struct wl_event_source *events[MAX_FREERDP_FDS]; + RFX_CONTEXT *rfx_context; + wStream *encode_stream; + RFX_RECT *rfx_rects; + NSC_CONTEXT *nsc_context; + + struct rdp_peers_item item; +}; +typedef struct rdp_peer_context RdpPeerContext; + +static inline struct rdp_head * +to_rdp_head(struct weston_head *base) +{ + return container_of(base, struct rdp_head, base); +} + +static inline struct rdp_output * +to_rdp_output(struct weston_output *base) +{ + return container_of(base, struct rdp_output, base); +} + +static inline struct rdp_backend * +to_rdp_backend(struct weston_compositor *base) +{ + return container_of(base->backend, struct rdp_backend, base); +} + +#endif From f2d6d21eec2f2a7fc7c855a4157227cc2d3648d6 Mon Sep 17 00:00:00 2001 From: shierote Date: Tue, 29 Mar 2022 10:22:59 +0900 Subject: [PATCH 123/609] libweston: correct argument name in the handler of wl_data_source.accept Signed-off-by: Taishi Eguchi --- libweston/clipboard.c | 2 +- libweston/data-device.c | 2 +- xwayland/dnd.c | 2 +- xwayland/selection.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libweston/clipboard.c b/libweston/clipboard.c index 7d60351a..a82d8fe8 100644 --- a/libweston/clipboard.c +++ b/libweston/clipboard.c @@ -110,7 +110,7 @@ clipboard_source_data(int fd, uint32_t mask, void *data) static void clipboard_source_accept(struct weston_data_source *source, - uint32_t time, const char *mime_type) + uint32_t serial, const char *mime_type) { } diff --git a/libweston/data-device.c b/libweston/data-device.c index 3c627346..fe997ba8 100644 --- a/libweston/data-device.c +++ b/libweston/data-device.c @@ -1239,7 +1239,7 @@ destroy_data_source(struct wl_resource *resource) static void client_source_accept(struct weston_data_source *source, - uint32_t time, const char *mime_type) + uint32_t serial, const char *mime_type) { wl_data_source_send_target(source->resource, mime_type); } diff --git a/xwayland/dnd.c b/xwayland/dnd.c index aea0845f..01918e16 100644 --- a/xwayland/dnd.c +++ b/xwayland/dnd.c @@ -51,7 +51,7 @@ struct dnd_data_source { static void data_source_accept(struct weston_data_source *base, - uint32_t time, const char *mime_type) + uint32_t serial, const char *mime_type) { struct dnd_data_source *source = (struct dnd_data_source *) base; xcb_client_message_event_t client_message; diff --git a/xwayland/selection.c b/xwayland/selection.c index c4845f20..b68988ec 100644 --- a/xwayland/selection.c +++ b/xwayland/selection.c @@ -152,7 +152,7 @@ struct x11_data_source { static void data_source_accept(struct weston_data_source *source, - uint32_t time, const char *mime_type) + uint32_t serial, const char *mime_type) { } From cd7801aa958342e26d2ed39f6271583220b6ba27 Mon Sep 17 00:00:00 2001 From: Maciej Pijanowski Date: Mon, 19 Oct 2020 18:58:35 +0200 Subject: [PATCH 124/609] screen-share: use compositor->read_format with renderer->read_pixels() The pixel format was hardcoded to PIXMAN_a8r8g8b8. All other renderer->read_pixels() calls in weston use dynamic format selection via the compositor->read_format instead. The problem was spotted on the ARM devices with Mali-400 GPU. The visual effect of the problem was black screen on the remote display, when using screen-share with the VNC or RDP backends. Related wayland-devel thread: https://lists.freedesktop.org/archives/wayland-devel/2020-September/041624.html Signed-off-by: Maciej Pijanowski --- compositor/screen-share.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compositor/screen-share.c b/compositor/screen-share.c index 10173b92..0c2f413d 100644 --- a/compositor/screen-share.c +++ b/compositor/screen-share.c @@ -882,8 +882,8 @@ shared_output_repainted(struct wl_listener *listener, void *data) y_orig = y; so->output->compositor->renderer->read_pixels( - so->output, PIXMAN_a8r8g8b8, so->tmp_data, - x, y_orig, width, height); + so->output, so->output->compositor->read_format, + so->tmp_data, x, y_orig, width, height); damaged_image = pixman_image_create_bits(PIXMAN_a8r8g8b8, width, height, From 924e79f4f254bf17caba5d5bb0c6196039296ddf Mon Sep 17 00:00:00 2001 From: Michael Olbrich Date: Wed, 17 Mar 2021 13:58:29 +0100 Subject: [PATCH 125/609] ivi-shell: emit created notification earlier for desktop surfaces Without this, the earliest signal the ivi controller receives for a new surface is configure_desktop_changed signal. And this is not emitted until the surface has the first buffer attached. By emitting the signal during surface creation, the controller is able to set the initial width and height for the first configure. Signed-off-by: Michael Olbrich --- ivi-shell/ivi-layout-shell.h | 3 ++- ivi-shell/ivi-layout.c | 16 +++++++++++++--- ivi-shell/ivi-shell.c | 4 +--- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/ivi-shell/ivi-layout-shell.h b/ivi-shell/ivi-layout-shell.h index 63f66fa9..1c10a8ba 100644 --- a/ivi-shell/ivi-layout-shell.h +++ b/ivi-shell/ivi-layout-shell.h @@ -44,7 +44,8 @@ ivi_layout_desktop_surface_configure(struct ivi_layout_surface *ivisurf, int32_t width, int32_t height); struct ivi_layout_surface* -ivi_layout_desktop_surface_create(struct weston_surface *wl_surface); +ivi_layout_desktop_surface_create(struct weston_surface *wl_surface, + struct weston_desktop_surface *surface); void ivi_layout_surface_configure(struct ivi_layout_surface *ivisurf, diff --git a/ivi-shell/ivi-layout.c b/ivi-shell/ivi-layout.c index 0f0f4f8a..e685e356 100644 --- a/ivi-shell/ivi-layout.c +++ b/ivi-shell/ivi-layout.c @@ -1882,7 +1882,6 @@ ivi_layout_surface_set_id(struct ivi_layout_surface *ivisurf, ivisurf->id_surface = id_surface; - wl_signal_emit(&layout->surface_notification.created, ivisurf); wl_signal_emit(&layout->surface_notification.configure_changed, ivisurf); @@ -1979,9 +1978,20 @@ ivi_layout_desktop_surface_configure(struct ivi_layout_surface *ivisurf, } struct ivi_layout_surface* -ivi_layout_desktop_surface_create(struct weston_surface *wl_surface) +ivi_layout_desktop_surface_create(struct weston_surface *wl_surface, + struct weston_desktop_surface *surface) { - return surface_create(wl_surface, IVI_INVALID_ID); + struct ivi_layout *layout = get_instance(); + struct ivi_layout_surface *ivisurf; + + ivisurf = surface_create(wl_surface, IVI_INVALID_ID); + + if (ivisurf) { + ivisurf->weston_desktop_surface = surface; + wl_signal_emit(&layout->surface_notification.created, ivisurf); + } + + return ivisurf; } void diff --git a/ivi-shell/ivi-shell.c b/ivi-shell/ivi-shell.c index 5ec1349e..eb6bbbe7 100644 --- a/ivi-shell/ivi-shell.c +++ b/ivi-shell/ivi-shell.c @@ -489,13 +489,11 @@ desktop_surface_added(struct weston_desktop_surface *surface, struct weston_surface *weston_surf = weston_desktop_surface_get_surface(surface); - layout_surface = ivi_layout_desktop_surface_create(weston_surf); + layout_surface = ivi_layout_desktop_surface_create(weston_surf, surface); if (!layout_surface) { return; } - layout_surface->weston_desktop_surface = surface; - ivisurf = zalloc(sizeof *ivisurf); if (!ivisurf) { return; From 9203910b9a3a04b1d916e56afda566f49eafceb1 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 11 Apr 2022 14:46:05 +0300 Subject: [PATCH 126/609] man: clean up weston.ini mark-up Try to reduce the cargo-culted directives that do not seem to have any effect on the output. I verified that are no changes by doing before this patch $ man -Tpdf man/weston.ini.5 > ref.pdf and then after this patch $ ninja && man -Tpdf man/weston.ini.5 > test.pdf and looking at $ diffpdf ref.pdf test.pdf I used PDF as the format for comparisons, because it can express much more typesetting features than plain text. Signed-off-by: Pekka Paalanen --- man/weston.ini.man | 65 ++++------------------------------------------ 1 file changed, 5 insertions(+), 60 deletions(-) diff --git a/man/weston.ini.man b/man/weston.ini.man index 8aa0d1c2..096b5cfc 100644 --- a/man/weston.ini.man +++ b/man/weston.ini.man @@ -87,7 +87,6 @@ integer, and boolean. Strings must not be quoted, do not support any escape sequences, and run till the end of the line. Integers can be given in decimal (e.g. 123), octal (e.g. 0173), and hexadecimal (e.g. 0x7b) form. Boolean values can be only 'true' or 'false'. -.RE .SH "CORE SECTION" The .B core @@ -108,7 +107,6 @@ directory are: .TP 7 .BI "xwayland=" true ask Weston to load the XWayland module (boolean). -.RE .TP 7 .BI "modules=" cms-colord.so,screen-share.so specifies the modules to load (string). Available modules in the @@ -159,9 +157,6 @@ means that if both weston.ini and command line define this idle-timeout time, the one specified in the command-line will be used. On the other hand, if none of these sets the value, default idle timeout will be set to 300 seconds. -.RS -.PP -.RE .TP 7 .BI "require-input=" true require an input device for launch @@ -192,7 +187,6 @@ Enables pixman-based rendering for all outputs on backends that support it. Boolean, defaults to .BR false . There is also a command line option to do the same. -.RE .TP 7 .BI "color-management=" true Enables color management and requires using GL-renderer. @@ -315,17 +309,17 @@ Weston will use the new calibration immediately. The program is invoked as: .PP .RS 10 +.nf .I calibration_helper syspath m1 m2 m3 m4 m5 m6 +.fi .RE -.RS -.PP +.IP .RI "where " syspath is the udev sys path for the device and .IR m1 " through " m6 are the calibration matrix elements in libinput's .BR LIBINPUT_CALIBRATION_MATRIX " udev property format." The sys path is an absolute path and starts with the sys mount point. -.RE .SH "SHELL SECTION" The @@ -438,7 +432,6 @@ sets the cursor theme (string). .TP 7 .BI "cursor-size=" 24 sets the cursor size (unsigned integer). -.RE .SH "LAUNCHER SECTION" There can be multiple launcher sections, one for each launcher. .TP 7 @@ -458,7 +451,6 @@ example: path=GDK_BACKEND=wayland gnome-terminal --full-screen .in .fi -.PP .SH "OUTPUT SECTION" There can be multiple output sections, each corresponding to one output. It is currently only recognized by the drm and x11 backends. @@ -477,12 +469,10 @@ Examples of usage: .BR "WL1 " "Wayland backend, Wayland window no.1" .fi .RE -.RS -.PP +.IP See .B "weston-drm(7)" for more details. -.RE .TP 7 .BI "mode=" mode sets the output mode (string). The mode parameter is handled differently @@ -493,7 +483,6 @@ The DRM backend accepts different modes, along with an option of a modeline stri See .B "weston-drm(7)" for examples of modes-formats supported by DRM backend. -.RE .TP 7 .BI "transform=" normal How you have rotated your monitor from its normal orientation (string). @@ -522,12 +511,9 @@ multiplier, to make them readable. Applications that do support their own output scaling can draw their content in high resolution, in which case they avoid compositor scaling. Weston will not scale the output of such applications, and they are not affected by this multiplier. -.RE -.RS -.PP +.IP An integer, 1 by default, typically configured as 2 or higher when needed, denoting the scaling multiplier for the output. -.RE .TP 7 .BI "icc_profile=" file If option @@ -535,7 +521,6 @@ If option is true, load the given ICC file as the output color profile. This works only on DRM, headless, wayland, and x11 backends, and for remoting and pipewire outputs. -.RE .TP 7 .BI "seat=" name The logical seat name that this output should be associated with. If this @@ -544,7 +529,6 @@ set on it. The expectation is that this functionality will be used in a multiheaded environment with a single compositor for multiple output and input configurations. The default seat is called "default" and will always be present. This seat can be constrained like any other. -.RE .TP 7 .BI "allow_hdcp=" true Allows HDCP support for this output. If set to true, HDCP can be tried for the @@ -553,82 +537,58 @@ default, HDCP support is always allowed for an output. The content-protection can actually be realized, only if the hardware (source and sink) support HDCP, and the backend has the implementation of content-protection protocol. Currently, HDCP is supported by drm-backend. -.RE .TP 7 .BI "app-ids=" app-id[,app_id]* A comma separated list of the IDs of applications to place on this output. These IDs should match the application IDs as set with the xdg_shell.set_app_id request. Currently, this option is supported by kiosk-shell. -.RE .SH "INPUT-METHOD SECTION" .TP 7 .BI "path=" "@weston_libexecdir@/weston-keyboard" sets the path of the on screen keyboard input method (string). -.RE -.RE .TP 7 .BI "overlay-keyboard=" false sets weston-keyboard as overlay panel. -.RE -.RE .SH "KEYBOARD SECTION" This section contains the following keys: .TP 7 .BI "keymap_rules=" "evdev" sets the keymap rules file (string). Used to map layout and model to input device. -.RE -.RE .TP 7 .BI "keymap_model=" "pc105" sets the keymap model (string). See the Models section in .B "xkeyboard-config(7)." -.RE -.RE .TP 7 .BI "keymap_layout=" "us,de,gb" sets the comma separated list of keyboard layout codes (string). See the Layouts section in .B "xkeyboard-config(7)." -.RE -.RE .TP 7 .BI "keymap_variant=" "euro,,intl" sets the comma separated list of keyboard layout variants (string). The number of variants must be the same as the number of layouts above. See the Layouts section in .B "xkeyboard-config(7)." -.RE -.RE .TP 7 .BI "keymap_options=" "grp:alt_shift_toggle,grp_led:scroll" sets the keymap options (string). See the Options section in .B "xkeyboard-config(7)." -.RE -.RE .TP 7 .BI "repeat-rate=" "40" sets the rate of repeating keys in characters per second (unsigned integer) -.RE -.RE .TP 7 .BI "repeat-delay=" "400" sets the delay in milliseconds since key down until repeating starts (unsigned integer) -.RE -.RE .TP 7 .BI "numlock-on=" "false" sets the default state of the numlock on weston startup for the backends which support it. -.RE -.RE .TP 7 .BI "vt-switching=" "true" Whether to allow the use of Ctrl+Alt+Fn key combinations to switch away from the compositor's virtual console. -.RE -.RE .SH "TERMINAL SECTION" Contains settings for the weston terminal application (weston-terminal). It allows to customize the font and shell of the command line interface. @@ -636,49 +596,34 @@ allows to customize the font and shell of the command line interface. .BI "font=" "DejaVu Sans Mono" sets the font of the terminal (string). For a good experience it is recommended to use monospace fonts. In case the font is not found, the default one is used. -.RE -.RE .TP 7 .BI "font-size=" "14" sets the size of the terminal font (unsigned integer). -.RE -.RE .TP 7 .BI "term=" "xterm-256color" The terminal shell (string). Sets the $TERM variable. -.RE -.RE .SH "XWAYLAND SECTION" .TP 7 .BI "path=" "@xserver_path@" sets the path to the xserver to run (string). -.RE -.RE .SH "SCREEN-SHARE SECTION" .TP 7 .BI "command=" "@weston_bindir@/weston --backend=rdp-backend.so \ --shell=fullscreen-shell.so --no-clients-resize" sets the command to start a fullscreen-shell server for screen sharing (string). -.RE -.RE .TP 7 .BI "start-on-startup=" "false" If set to true, start screen sharing of all outputs available on Weston startup. Set to false by default. -.RE -.RE .SH "AUTOLAUNCH SECTION" .TP 7 .BI "path=" "/usr/bin/echo" Path to an executable file to run after startup. This file is executed in parallel to Weston, so it does not have to immediately exit. Defaults to empty. -.RE .TP 7 .BI "watch=" "false" If set to true, quit Weston after the auto-launched executable exits. Set to false by default. -.RE -.RE .SH "SEE ALSO" .BR weston (1), .BR weston-bindings (7), From e1a111e1b72ae3b1ec26591bd2c0cf24377e5d87 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 14 Apr 2022 15:40:36 +0300 Subject: [PATCH 127/609] man: add section delimiters in weston.ini These added lines are comments (do not affect output) that make it easier to browse this file and find the section headings. Removal of the preceding empty lines on two of the section headers remove a blank line from PDF output. The blank line was kind of nice, but presumably .SH should add any necessary blanks before a new section heading. Now all the section headings have the same vertical space to the preceding content in PDF. Signed-off-by: Pekka Paalanen --- man/weston.ini.man | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/man/weston.ini.man b/man/weston.ini.man index 096b5cfc..98396027 100644 --- a/man/weston.ini.man +++ b/man/weston.ini.man @@ -1,15 +1,18 @@ .\" shorthand for double quote that works everywhere. .ds q \N'34' .TH weston.ini 5 "2019-03-26" "Weston @version@" +.\"--------------------------------------------------------------------- .SH NAME weston.ini \- configuration file for .B Weston \- the reference Wayland compositor +.\"--------------------------------------------------------------------- .SH INTRODUCTION .B Weston obtains configuration from its command line parameters and the configuration file described here. +.\"--------------------------------------------------------------------- .SH DESCRIPTION .B Weston uses a configuration file called @@ -87,6 +90,7 @@ integer, and boolean. Strings must not be quoted, do not support any escape sequences, and run till the end of the line. Integers can be given in decimal (e.g. 123), octal (e.g. 0173), and hexadecimal (e.g. 0x7b) form. Boolean values can be only 'true' or 'false'. +.\"--------------------------------------------------------------------- .SH "CORE SECTION" The .B core @@ -200,7 +204,7 @@ spaces and perform monitor profiling, and tone mapping required to enable HDR video modes. This extended functionality comes at the cost of heavier image processing and sometimes a loss of some hardware off-loading features like composite-bypass. - +.\"--------------------------------------------------------------------- .SH "LIBINPUT SECTION" The .B libinput @@ -320,7 +324,7 @@ is the udev sys path for the device and are the calibration matrix elements in libinput's .BR LIBINPUT_CALIBRATION_MATRIX " udev property format." The sys path is an absolute path and starts with the sys mount point. - +.\"--------------------------------------------------------------------- .SH "SHELL SECTION" The .B shell @@ -432,6 +436,7 @@ sets the cursor theme (string). .TP 7 .BI "cursor-size=" 24 sets the cursor size (unsigned integer). +.\"--------------------------------------------------------------------- .SH "LAUNCHER SECTION" There can be multiple launcher sections, one for each launcher. .TP 7 @@ -451,6 +456,7 @@ example: path=GDK_BACKEND=wayland gnome-terminal --full-screen .in .fi +.\"--------------------------------------------------------------------- .SH "OUTPUT SECTION" There can be multiple output sections, each corresponding to one output. It is currently only recognized by the drm and x11 backends. @@ -542,6 +548,7 @@ of content-protection protocol. Currently, HDCP is supported by drm-backend. A comma separated list of the IDs of applications to place on this output. These IDs should match the application IDs as set with the xdg_shell.set_app_id request. Currently, this option is supported by kiosk-shell. +.\"--------------------------------------------------------------------- .SH "INPUT-METHOD SECTION" .TP 7 .BI "path=" "@weston_libexecdir@/weston-keyboard" @@ -549,6 +556,7 @@ sets the path of the on screen keyboard input method (string). .TP 7 .BI "overlay-keyboard=" false sets weston-keyboard as overlay panel. +.\"--------------------------------------------------------------------- .SH "KEYBOARD SECTION" This section contains the following keys: .TP 7 @@ -589,6 +597,7 @@ support it. .BI "vt-switching=" "true" Whether to allow the use of Ctrl+Alt+Fn key combinations to switch away from the compositor's virtual console. +.\"--------------------------------------------------------------------- .SH "TERMINAL SECTION" Contains settings for the weston terminal application (weston-terminal). It allows to customize the font and shell of the command line interface. @@ -602,10 +611,12 @@ sets the size of the terminal font (unsigned integer). .TP 7 .BI "term=" "xterm-256color" The terminal shell (string). Sets the $TERM variable. +.\"--------------------------------------------------------------------- .SH "XWAYLAND SECTION" .TP 7 .BI "path=" "@xserver_path@" sets the path to the xserver to run (string). +.\"--------------------------------------------------------------------- .SH "SCREEN-SHARE SECTION" .TP 7 .BI "command=" "@weston_bindir@/weston --backend=rdp-backend.so \ @@ -615,6 +626,7 @@ sets the command to start a fullscreen-shell server for screen sharing (string). .BI "start-on-startup=" "false" If set to true, start screen sharing of all outputs available on Weston startup. Set to false by default. +.\"--------------------------------------------------------------------- .SH "AUTOLAUNCH SECTION" .TP 7 .BI "path=" "/usr/bin/echo" @@ -624,6 +636,7 @@ parallel to Weston, so it does not have to immediately exit. Defaults to empty. .BI "watch=" "false" If set to true, quit Weston after the auto-launched executable exits. Set to false by default. +.\"--------------------------------------------------------------------- .SH "SEE ALSO" .BR weston (1), .BR weston-bindings (7), From c2f4201ed2a86a4889417ea750cb75c2e96dcc57 Mon Sep 17 00:00:00 2001 From: Dominique Martinet Date: Mon, 11 Apr 2022 19:52:27 +0900 Subject: [PATCH 128/609] xwayland: use -displayfd instead of USR1 to signal readiness We want to wait for Xwayland to be ready before issuing it blocking requests, but relying on USR1 is a bit unsafe: - we can't ascertain the signal originated from Xwayland - if weston is started as PID1 (e.g. in its own container), then Xwayland will not send SIGUSR1 and X11 connections will be stuck forever: https://gitlab.freedesktop.org/xorg/xserver/-/issues/1312 Creating a pipe and using -displayfd, even if we don't care about the display value itself, is safe and works for all cases Signed-off-by: Dominique Martinet --- compositor/xwayland.c | 79 +++++++++++++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 26 deletions(-) diff --git a/compositor/xwayland.c b/compositor/xwayland.c index 06ca4919..17f53b89 100644 --- a/compositor/xwayland.c +++ b/compositor/xwayland.c @@ -35,6 +35,7 @@ #include "compositor/weston.h" #include #include "shared/helpers.h" +#include "shared/os-compatibility.h" #ifdef HAVE_XWAYLAND_LISTENFD # define LISTEN_STR "-listenfd" @@ -46,24 +47,44 @@ struct wet_xwayland { struct weston_compositor *compositor; const struct weston_xwayland_api *api; struct weston_xwayland *xwayland; - struct wl_event_source *sigusr1_source; + struct wl_event_source *display_fd_source; struct wl_client *client; int wm_fd; struct weston_process process; }; static int -handle_sigusr1(int signal_number, void *data) +handle_display_fd(int fd, uint32_t mask, void *data) { struct wet_xwayland *wxw = data; + char buf[64]; + ssize_t n; + + /* xwayland exited before being ready, don't finish initialization, + * the process watcher will cleanup */ + if (!(mask & WL_EVENT_READABLE)) + goto out; + + /* Xwayland writes to the pipe twice, so if we close it too early + * it's possible the second write will fail and Xwayland shuts down. + * Make sure we read until end of line marker to avoid this. */ + n = read(fd, buf, sizeof buf); + if (n < 0 && errno != EAGAIN) { + weston_log("read from Xwayland display_fd failed: %s\n", + strerror(errno)); + goto out; + } + /* Returning 1 here means recheck and call us again if required. */ + if (n <= 0 || (n > 0 && buf[n - 1] != '\n')) + return 1; - /* We'd be safer if we actually had the struct - * signalfd_siginfo from the signalfd data and could verify - * this came from Xwayland.*/ wxw->api->xserver_loaded(wxw->xwayland, wxw->client, wxw->wm_fd); - wl_event_source_remove(wxw->sigusr1_source); - return 1; +out: + wl_event_source_remove(wxw->display_fd_source); + close(fd); + + return 0; } static pid_t @@ -72,10 +93,12 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd struct wet_xwayland *wxw = user_data; pid_t pid; char s[12], abstract_fd_str[12], unix_fd_str[12], wm_fd_str[12]; - int sv[2], wm[2], fd; + char display_fd_str[12]; + int sv[2], wm[2], fd, display_fd[2]; char *xserver = NULL; struct weston_config *config = wet_get_config(wxw->compositor); struct weston_config_section *section; + struct wl_event_loop *loop; if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) { weston_log("wl connection socketpair failed\n"); @@ -87,6 +110,16 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd return 1; } + if (pipe(display_fd) < 0) { + weston_log("pipe creation for displayfd failed\n"); + return 1; + } + + if (os_fd_set_cloexec(display_fd[0]) != 0) { + weston_log("failed setting remaining end of displayfd as cloexec\n"); + return 1; + } + pid = fork(); switch (pid) { case 0: @@ -110,27 +143,20 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd if (fd < 0) goto fail; snprintf(wm_fd_str, sizeof wm_fd_str, "%d", fd); + snprintf(display_fd_str, sizeof display_fd_str, "%d", display_fd[1]); section = weston_config_get_section(config, "xwayland", NULL, NULL); weston_config_section_get_string(section, "path", &xserver, XSERVER_PATH); - /* Ignore SIGUSR1 in the child, which will make the X - * server send SIGUSR1 to the parent (weston) when - * it's done with initialization. During - * initialization the X server will round trip and - * block on the wayland compositor, so avoid making - * blocking requests (like xcb_connect_to_fd) until - * it's done with that. */ - signal(SIGUSR1, SIG_IGN); - if (execl(xserver, xserver, display, "-rootless", LISTEN_STR, abstract_fd_str, LISTEN_STR, unix_fd_str, + "-displayfd", display_fd_str, "-wm", wm_fd_str, "-terminate", NULL) < 0) @@ -150,6 +176,16 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd close(wm[1]); wxw->wm_fd = wm[0]; + /* During initialization the X server will round trip + * and block on the wayland compositor, so avoid making + * blocking requests (like xcb_connect_to_fd) until + * it's done with that. */ + close(display_fd[1]); + loop = wl_display_get_event_loop(wxw->compositor->wl_display); + wxw->display_fd_source = + wl_event_loop_add_fd(loop, display_fd[0], WL_EVENT_READABLE, + handle_display_fd, wxw); + wxw->process.pid = pid; wet_watch_process(wxw->compositor, &wxw->process); break; @@ -167,12 +203,8 @@ xserver_cleanup(struct weston_process *process, int status) { struct wet_xwayland *wxw = container_of(process, struct wet_xwayland, process); - struct wl_event_loop *loop = - wl_display_get_event_loop(wxw->compositor->wl_display); wxw->api->xserver_exited(wxw->xwayland, status); - wxw->sigusr1_source = wl_event_loop_add_signal(loop, SIGUSR1, - handle_sigusr1, wxw); wxw->client = NULL; } @@ -182,7 +214,6 @@ wet_load_xwayland(struct weston_compositor *comp) const struct weston_xwayland_api *api; struct weston_xwayland *xwayland; struct wet_xwayland *wxw; - struct wl_event_loop *loop; if (weston_compositor_load_xwayland(comp) < 0) return -1; @@ -210,9 +241,5 @@ wet_load_xwayland(struct weston_compositor *comp) if (api->listen(xwayland, wxw, spawn_xserver) < 0) return -1; - loop = wl_display_get_event_loop(comp->wl_display); - wxw->sigusr1_source = wl_event_loop_add_signal(loop, SIGUSR1, - handle_sigusr1, wxw); - return 0; } From 8fb529bc31951cbd58a0eb1c1761975c01fed456 Mon Sep 17 00:00:00 2001 From: Hideyuki Nagase Date: Thu, 10 Mar 2022 08:51:34 -0600 Subject: [PATCH 129/609] input: Fix bug in idle inhibition Server generated key repeats are ignored - and they don't generate matching release events. We early return to avoid generating events for them. We also need to push the idle inhibition counting after that early return to prevent breaking idle inhibition with unmatched events. Co-authored-by: Steve Pronovost Co-authored-by: Brenton DeGeer Signed-off-by: Hideyuki Nagase Signed-off-by: Steve Pronovost Signed-off-by: Brenton DeGeer --- libweston/input.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/libweston/input.c b/libweston/input.c index 6fb4bed3..7c8559ae 100644 --- a/libweston/input.c +++ b/libweston/input.c @@ -2197,12 +2197,6 @@ notify_key(struct weston_seat *seat, const struct timespec *time, uint32_t key, struct weston_keyboard_grab *grab = keyboard->grab; uint32_t *k, *end; - if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { - weston_compositor_idle_inhibit(compositor); - } else { - weston_compositor_idle_release(compositor); - } - end = keyboard->keys.data + keyboard->keys.size; for (k = keyboard->keys.data; k < end; k++) { if (*k == key) { @@ -2218,6 +2212,12 @@ notify_key(struct weston_seat *seat, const struct timespec *time, uint32_t key, *k = key; } + if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { + weston_compositor_idle_inhibit(compositor); + } else { + weston_compositor_idle_release(compositor); + } + if (grab == &keyboard->default_grab || grab == &keyboard->input_method_grab) { weston_compositor_run_key_binding(compositor, keyboard, time, From e99ed2ad36befe9157eeef54a7458d5da212802a Mon Sep 17 00:00:00 2001 From: Weng Xuetian Date: Mon, 18 Apr 2022 22:41:02 -0700 Subject: [PATCH 130/609] Defer launch input method with wl_event_loop_add_idle. Input method process may also be a XIM server which requires the correct DISPLAY to be set after xwayland start up. This helps input method to work for both wayland and Xwayland. Signed-off-by: Weng Xuetian --- compositor/text-backend.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/compositor/text-backend.c b/compositor/text-backend.c index 5cd994cb..94955bff 100644 --- a/compositor/text-backend.c +++ b/compositor/text-backend.c @@ -949,7 +949,7 @@ input_method_init_seat(struct weston_seat *seat) seat->input_method->focus_listener_initialized = true; } -static void launch_input_method(struct text_backend *text_backend); +static void launch_input_method(void *data); static void respawn_input_method_process(struct text_backend *text_backend) @@ -989,8 +989,10 @@ input_method_client_notifier(struct wl_listener *listener, void *data) } static void -launch_input_method(struct text_backend *text_backend) +launch_input_method(void *data) { + struct text_backend *text_backend = data; + if (!text_backend->input_method.path) return; @@ -1093,6 +1095,7 @@ text_backend_init(struct weston_compositor *ec) { struct text_backend *text_backend; struct weston_seat *seat; + struct wl_event_loop *loop; text_backend = zalloc(sizeof(*text_backend)); if (text_backend == NULL) @@ -1110,7 +1113,8 @@ text_backend_init(struct weston_compositor *ec) text_input_manager_create(ec); - launch_input_method(text_backend); + loop = wl_display_get_event_loop(ec->wl_display); + wl_event_loop_add_idle(loop, launch_input_method, text_backend); return text_backend; } From 8aa1c30bf1703b9d68be2721956befa4dcee4c79 Mon Sep 17 00:00:00 2001 From: Hideyuki Nagase Date: Thu, 10 Mar 2022 10:41:37 -0600 Subject: [PATCH 131/609] xwayland: Honour the XCURSOR_THEME environment variable Toy toolkit apps already do this since commit 807cd2e589 Co-authored-by: Steve Pronovost Co-authored-by: Brenton DeGeer Signed-off-by: Hideyuki Nagase Signed-off-by: Steve Pronovost Signed-off-by: Brenton DeGeer --- xwayland/window-manager.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xwayland/window-manager.c b/xwayland/window-manager.c index ef8d92b0..f5b61c42 100644 --- a/xwayland/window-manager.c +++ b/xwayland/window-manager.c @@ -346,6 +346,7 @@ xcb_cursor_library_load_cursor(struct weston_wm *wm, const char *file) xcb_cursor_t cursor; XcursorImages *images; char *v = NULL; + char *theme; int size = 0; if (!file) @@ -358,7 +359,9 @@ xcb_cursor_library_load_cursor(struct weston_wm *wm, const char *file) if (!size) size = 32; - images = XcursorLibraryLoadImages (file, NULL, size); + theme = getenv("XCURSOR_THEME"); + + images = XcursorLibraryLoadImages(file, theme, size); if (!images) return -1; From 3bedb704549c23e7c564a8e67a867f8b505b5299 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Thu, 7 Apr 2022 20:04:25 +0300 Subject: [PATCH 132/609] touch-calibration: Clean-up if touch calibrator has been enabled Seems that we're still missing layer clean-ups, with the touch calibrator being one of them. Call the appropriate function when shutting down the compositor instance. Fixes: #603 Signed-off-by: Marius Vlad --- libweston/compositor.c | 3 +++ libweston/libweston-internal.h | 2 ++ libweston/touch-calibration.c | 10 ++++++++++ 3 files changed, 15 insertions(+) diff --git a/libweston/compositor.c b/libweston/compositor.c index 34631716..cabd0d45 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -7852,6 +7852,9 @@ weston_compositor_shutdown(struct weston_compositor *ec) wl_event_source_remove(ec->idle_source); wl_event_source_remove(ec->repaint_timer); + if (ec->touch_calibration) + weston_compositor_destroy_touch_calibrator(ec); + /* Destroy all outputs associated with this compositor */ wl_list_for_each_safe(output, next, &ec->output_list, link) output->destroy(output); diff --git a/libweston/libweston-internal.h b/libweston/libweston-internal.h index 7c30706f..02c4ad3b 100644 --- a/libweston/libweston-internal.h +++ b/libweston/libweston-internal.h @@ -389,6 +389,8 @@ const uint64_t * weston_drm_format_get_modifiers(const struct weston_drm_format *format, unsigned int *count_out); +void +weston_compositor_destroy_touch_calibrator(struct weston_compositor *compositor); /** * paint node * diff --git a/libweston/touch-calibration.c b/libweston/touch-calibration.c index 9dd99bba..11e59386 100644 --- a/libweston/touch-calibration.c +++ b/libweston/touch-calibration.c @@ -674,6 +674,16 @@ bind_touch_calibration(struct wl_client *client, } } +void +weston_compositor_destroy_touch_calibrator(struct weston_compositor *ec) +{ + /* TODO: handle weston_compositor::touch_calibrator destruction + * see + * https://gitlab.freedesktop.org/wayland/weston/-/merge_requests/819#note_1345191 + */ + weston_layer_fini(&ec->calibrator_layer); +} + /** Advertise touch_calibration support * * \param compositor The compositor to init for. From 9e20730e044532bf3f870944fb4b70d3eac570dd Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Mon, 8 Mar 2021 23:48:43 +0200 Subject: [PATCH 133/609] screen-share: Start screen sharing even if no pointer is found With commit e825fe38, we no longer display the pointer if no movement is detected, which will cause screen share to fail to start if that is the case. There could be also legitimate cases where there's no pointer, so let's allow screen share to function in those cases as well. Makes uses of previous helper methods to find a proper output to share in case we don't have an pointer. Re-uses the shell utils functions. Signed-off-by: Marius Vlad --- compositor/meson.build | 1 + compositor/screen-share.c | 18 +++++++++++------- meson.build | 2 +- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/compositor/meson.build b/compositor/meson.build index 8a54ea99..427167e7 100644 --- a/compositor/meson.build +++ b/compositor/meson.build @@ -78,6 +78,7 @@ if get_option('screenshare') deps_screenshare = [ dep_libexec_weston, dep_libshared, + dep_shell_utils, dep_libweston_public, dep_libweston_private_h, # XXX: https://gitlab.freedesktop.org/wayland/weston/issues/292 dep_wayland_client, diff --git a/compositor/screen-share.c b/compositor/screen-share.c index 0c2f413d..c79bc3c4 100644 --- a/compositor/screen-share.c +++ b/compositor/screen-share.c @@ -47,6 +47,7 @@ #include "shared/helpers.h" #include "shared/os-compatibility.h" #include "shared/timespec-util.h" +#include "shell-utils/shell-utils.h" #include "fullscreen-shell-unstable-v1-client-protocol.h" struct shared_output { @@ -1144,16 +1145,19 @@ share_output_binding(struct weston_keyboard *keyboard, struct screen_share *ss = data; pointer = weston_seat_get_pointer(keyboard->seat); - if (!pointer) { - weston_log("Cannot pick output: Seat does not have pointer\n"); - return; + if (pointer) { + output = weston_output_find(pointer->seat->compositor, + wl_fixed_to_int(pointer->x), + wl_fixed_to_int(pointer->y)); + } else { + output = get_focused_output(keyboard->seat->compositor); + if (!output) + output = get_default_output(keyboard->seat->compositor); } - output = weston_output_find(pointer->seat->compositor, - wl_fixed_to_int(pointer->x), - wl_fixed_to_int(pointer->y)); if (!output) { - weston_log("Cannot pick output: Pointer not on any output\n"); + weston_log("Cannot pick output: Pointer not on any output, " + "or no focused/default output found\n"); return; } diff --git a/meson.build b/meson.build index 67917dea..82796740 100644 --- a/meson.build +++ b/meson.build @@ -170,8 +170,8 @@ subdir('shared') subdir('libweston') subdir('libweston-desktop') subdir('xwayland') -subdir('compositor') subdir('shell-utils') +subdir('compositor') subdir('desktop-shell') subdir('fullscreen-shell') subdir('ivi-shell') From 08ee337e4dfa83f0e952a9d01728e617a2445938 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Wed, 13 Oct 2021 17:54:03 +0300 Subject: [PATCH 134/609] screen-share: Add a compositor destroy listener Trivial fix to clean itself on compositor tear-down. While at it properly free the command string. Fixes #298 Signed-off-by: Marius Vlad --- compositor/screen-share.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/compositor/screen-share.c b/compositor/screen-share.c index c79bc3c4..60f25556 100644 --- a/compositor/screen-share.c +++ b/compositor/screen-share.c @@ -115,9 +115,7 @@ struct ss_shm_buffer { struct screen_share { struct weston_compositor *compositor; - /* XXX: missing compositor destroy listener - * https://gitlab.freedesktop.org/wayland/weston/issues/298 - */ + struct wl_listener compositor_destroy_listener; char *command; }; @@ -1164,6 +1162,18 @@ share_output_binding(struct weston_keyboard *keyboard, weston_output_share(output, ss->command); } +static void +compositor_destroy_listener(struct wl_listener *listener, void *data) +{ + struct screen_share *ss = + wl_container_of(listener, ss, compositor_destroy_listener); + + wl_list_remove(&ss->compositor_destroy_listener.link); + + free(ss->command); + free(ss); +} + WL_EXPORT int wet_module_init(struct weston_compositor *compositor, int *argc, char *argv[]) @@ -1179,11 +1189,16 @@ wet_module_init(struct weston_compositor *compositor, return -1; ss->compositor = compositor; + wl_list_init(&ss->compositor_destroy_listener.link); + + ss->compositor_destroy_listener.notify = compositor_destroy_listener; + wl_signal_add(&compositor->destroy_signal, &ss->compositor_destroy_listener); + config = wet_get_config(compositor); section = weston_config_get_section(config, "screen-share", NULL, NULL); - weston_config_section_get_string(section, "command", &ss->command, ""); + weston_config_section_get_string(section, "command", &ss->command, NULL); weston_compositor_add_key_binding(compositor, KEY_S, MODIFIER_CTRL | MODIFIER_ALT, From af18a0c4c9bab4178c04dffb8edd2cc5cba6b47a Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Thu, 14 Oct 2021 01:46:02 +0300 Subject: [PATCH 135/609] screen-share: Avoid bit-shifting large values Found while running with b_sanitize=undefined, which yields: runtime error: shift exponent 909199186 is too large for 32-bit type 'int' Converts the shm_formats to a boolean and checks for the correct pixel format it directly, instead of trying to gather them all in an array and then later on to do the check. Signed-off-by: Marius Vlad --- compositor/screen-share.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/compositor/screen-share.c b/compositor/screen-share.c index 60f25556..b820278f 100644 --- a/compositor/screen-share.c +++ b/compositor/screen-share.c @@ -60,7 +60,7 @@ struct shared_output { struct wl_registry *registry; struct wl_compositor *compositor; struct wl_shm *shm; - uint32_t shm_formats; + bool shm_formats_has_xrgb; struct zwp_fullscreen_shell_v1 *fshell; struct wl_output *output; struct wl_surface *surface; @@ -701,7 +701,8 @@ shm_handle_format(void *data, struct wl_shm *wl_shm, uint32_t format) { struct shared_output *so = data; - so->parent.shm_formats |= (1 << format); + if (format == WL_SHM_FORMAT_XRGB8888) + so->parent.shm_formats_has_xrgb = true; } struct wl_shm_listener shm_listener = { @@ -967,7 +968,7 @@ shared_output_create(struct weston_output *output, int parent_fd) /* Get SHM formats */ wl_display_roundtrip(so->parent.display); - if (!(so->parent.shm_formats & (1 << WL_SHM_FORMAT_XRGB8888))) { + if (!so->parent.shm_formats_has_xrgb) { weston_log("Screen share failed: " "WL_SHM_FORMAT_XRGB8888 not available\n"); goto err_display; From 0a8e3cbc4ad82ca1c4a92591ea5dcc8a47b47f35 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Fri, 12 Nov 2021 16:24:21 +0200 Subject: [PATCH 136/609] screen-share: Document that --no-config option should be passed With commit 'screen-share: Add option to start screen sharing on weston star' an section option has been added to start screen-sharing by default on all outputs. This has the side-effect of attempting to start screen-share on the same (RDP) output performing the screen-share. That happens due to re-loading the screen-share module once more. This patch recommends users to use --no-config option or alternatively, use a different configuration file to avoid that. Signed-off-by: Marius Vlad --- man/weston.ini.man | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/man/weston.ini.man b/man/weston.ini.man index 98396027..35c70e6f 100644 --- a/man/weston.ini.man +++ b/man/weston.ini.man @@ -620,13 +620,20 @@ sets the path to the xserver to run (string). .SH "SCREEN-SHARE SECTION" .TP 7 .BI "command=" "@weston_bindir@/weston --backend=rdp-backend.so \ ---shell=fullscreen-shell.so --no-clients-resize" +--shell=fullscreen-shell.so --no-clients-resize --no-config" sets the command to start a fullscreen-shell server for screen sharing (string). .TP 7 .BI "start-on-startup=" "false" If set to true, start screen sharing of all outputs available on Weston startup. Set to false by default. .\"--------------------------------------------------------------------- +Set to false by default. When using this option make sure you enable --no-config +to avoid re-loading the screen-share module and implictly trigger screen-sharing +for the RDP output already performing the screen share. Alternatively, you could +also supply a different configuration file, by using --config /path/to/config/file, +and make sure that the configuration file doesn't load the screen-share module. +.RE +.RE .SH "AUTOLAUNCH SECTION" .TP 7 .BI "path=" "/usr/bin/echo" From 4887f1a7aad767a74e36dcb3dda97fd61ba8e8a7 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 22 Nov 2021 14:41:23 +0100 Subject: [PATCH 137/609] build: add Meson fallback for wayland-protocols This allows easily co-developing a Wayland protocol and Weston. Example setup: ln -s subprojects/wayland-protocols /path/to/wayland-protocols meson configure build/ --force-fallback-for=wayland-protocols Signed-off-by: Simon Ser --- meson.build | 2 +- protocol/meson.build | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/meson.build b/meson.build index 82796740..8498ec1f 100644 --- a/meson.build +++ b/meson.build @@ -6,7 +6,7 @@ project('weston', 'c_std=gnu99', 'b_lundef=true', ], - meson_version: '>= 0.52.1', + meson_version: '>= 0.54.0', license: 'MIT/Expat', ) diff --git a/protocol/meson.build b/protocol/meson.build index 3ff640e1..efc1281f 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -1,8 +1,9 @@ dep_scanner = dependency('wayland-scanner', native: true) prog_scanner = find_program(dep_scanner.get_pkgconfig_variable('wayland_scanner')) -dep_wp = dependency('wayland-protocols', version: '>= 1.24') -dir_wp_base = dep_wp.get_pkgconfig_variable('pkgdatadir') +dep_wp = dependency('wayland-protocols', version: '>= 1.24', + fallback: ['wayland-protocols', 'wayland_protocols']) +dir_wp_base = dep_wp.get_variable(pkgconfig: 'pkgdatadir', internal: 'pkgdatadir') install_data( [ From 04e055832713ffe69a311fef297353ccf50bb6be Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Thu, 14 Apr 2022 12:25:43 -0500 Subject: [PATCH 138/609] rdp: Allow disabling RemoteFX codec There are currently compatibility issues between FreeRDP's implementation of the RemoteFX codec and Microsoft's implementation. Perhaps this will be fixed in the future and this option can go away, but for now it's necessary to have a way to disable the codec if the windows client is going to be connecting to a weston server. Signed-off-by: Derek Foreman --- compositor/main.c | 4 ++++ include/libweston/backend-rdp.h | 1 + libweston/backend-rdp/rdp.c | 4 +++- libweston/backend-rdp/rdp.h | 1 + man/weston-rdp.man | 5 +++++ 5 files changed, 14 insertions(+), 1 deletion(-) diff --git a/compositor/main.c b/compositor/main.c index 1b7e01fe..dfc3d4bd 100644 --- a/compositor/main.c +++ b/compositor/main.c @@ -2774,6 +2774,7 @@ weston_rdp_backend_config_init(struct weston_rdp_backend_config *config) config->env_socket = 0; config->no_clients_resize = 0; config->force_no_compression = 0; + config->remotefx_codec = true; } static int @@ -2782,6 +2783,7 @@ load_rdp_backend(struct weston_compositor *c, { struct weston_rdp_backend_config config = {{ 0, }}; int ret = 0; + bool no_remotefx_codec = false; struct wet_output_config *parsed_options = wet_init_parsed_options(c); if (!parsed_options) @@ -2800,9 +2802,11 @@ load_rdp_backend(struct weston_compositor *c, { WESTON_OPTION_STRING, "rdp-tls-cert", 0, &config.server_cert }, { WESTON_OPTION_STRING, "rdp-tls-key", 0, &config.server_key }, { WESTON_OPTION_BOOLEAN, "force-no-compression", 0, &config.force_no_compression }, + { WESTON_OPTION_BOOLEAN, "no-remotefx-codec", 0, &no_remotefx_codec }, }; parse_options(rdp_options, ARRAY_LENGTH(rdp_options), argc, argv); + config.remotefx_codec = !no_remotefx_codec; wet_set_simple_head_configurator(c, rdp_backend_output_configure); diff --git a/include/libweston/backend-rdp.h b/include/libweston/backend-rdp.h index b3542507..2c55818b 100644 --- a/include/libweston/backend-rdp.h +++ b/include/libweston/backend-rdp.h @@ -66,6 +66,7 @@ struct weston_rdp_backend_config { int env_socket; int no_clients_resize; int force_no_compression; + bool remotefx_codec; }; #ifdef __cplusplus diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index 218e2fcb..07bf1fe2 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -1141,7 +1141,7 @@ rdp_peer_init(freerdp_peer *client, struct rdp_backend *b) settings->OsMinorType = OSMINORTYPE_PSEUDO_XSERVER; settings->ColorDepth = 32; settings->RefreshRect = TRUE; - settings->RemoteFxCodec = TRUE; + settings->RemoteFxCodec = b->remotefx_codec; settings->NSCodec = TRUE; settings->FrameMarkerCommandEnabled = TRUE; settings->SurfaceFrameMarkerEnabled = TRUE; @@ -1220,6 +1220,7 @@ rdp_backend_create(struct weston_compositor *compositor, b->rdp_key = config->rdp_key ? strdup(config->rdp_key) : NULL; b->no_clients_resize = config->no_clients_resize; b->force_no_compression = config->force_no_compression; + b->remotefx_codec = config->remotefx_codec; compositor->backend = &b->base; @@ -1308,6 +1309,7 @@ config_init_to_defaults(struct weston_rdp_backend_config *config) config->env_socket = 0; config->no_clients_resize = 0; config->force_no_compression = 0; + config->remotefx_codec = true; } WL_EXPORT int diff --git a/libweston/backend-rdp/rdp.h b/libweston/backend-rdp/rdp.h index 96807f6e..65e6f0fa 100644 --- a/libweston/backend-rdp/rdp.h +++ b/libweston/backend-rdp/rdp.h @@ -67,6 +67,7 @@ struct rdp_backend { int tls_enabled; int no_clients_resize; int force_no_compression; + bool remotefx_codec; }; enum peer_item_flags { diff --git a/man/weston-rdp.man b/man/weston-rdp.man index f6cdd1de..1d6ccfd0 100644 --- a/man/weston-rdp.man +++ b/man/weston-rdp.man @@ -43,6 +43,11 @@ By default when a client connects on the RDP backend, it will instruct weston to resize to the dimensions of the client's announced resolution. When this option is set, weston will force the client to resize to its own resolution. .TP +\fB\-\-no-remotefx-codec +The RemoteFX compression codec is enabled by default, but it may be necessary +to disable it to work around incompatabilities between implementations. This +option may be removed in the future when all known issues are resolved. +.TP \fB\-\-rdp4\-key\fR=\fIfile\fR The file containing the RSA key for doing RDP security. As RDP security is known to be insecure, this option should be avoided in production. From 6129cbd8806e0c6fffcf59767150f12700881fad Mon Sep 17 00:00:00 2001 From: Hideyuki Nagase Date: Fri, 11 Mar 2022 13:29:11 -0600 Subject: [PATCH 139/609] rdp: Improved rdp logging infrastructure Add some logging helper functions along with two log scopes for debug and extremely verbose debugging information. Also add tangentially related logging for the synchronize event, so the debug stream isn't empty right now. The vast majority of verbose usage will come later. Co-authored-by: Steve Pronovost Co-authored-by: Brenton DeGeer Signed-off-by: Hideyuki Nagase Signed-off-by: Steve Pronovost Signed-off-by: Brenton DeGeer --- libweston/backend-rdp/meson.build | 7 ++- libweston/backend-rdp/rdp.c | 46 ++++++++++++++++-- libweston/backend-rdp/rdp.h | 16 +++++++ libweston/backend-rdp/rdputil.c | 77 +++++++++++++++++++++++++++++++ 4 files changed, 141 insertions(+), 5 deletions(-) create mode 100644 libweston/backend-rdp/rdputil.c diff --git a/libweston/backend-rdp/meson.build b/libweston/backend-rdp/meson.build index 4f34fdbb..d080391d 100644 --- a/libweston/backend-rdp/meson.build +++ b/libweston/backend-rdp/meson.build @@ -19,9 +19,14 @@ deps_rdp = [ dep_frdp, dep_wpr, ] +srcs_rdp = [ + 'rdp.c', + 'rdputil.c', +] + plugin_rdp = shared_library( 'rdp-backend', - 'rdp.c', + srcs_rdp, include_directories: common_inc, dependencies: deps_rdp, name_prefix: '', diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index 07bf1fe2..bd4dd1d6 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -307,6 +307,7 @@ static int rdp_switch_mode(struct weston_output *output, struct weston_mode *target_mode) { struct rdp_output *rdpOutput = container_of(output, struct rdp_output, base); + struct rdp_backend *rdpBackend = to_rdp_backend(output->compositor); struct rdp_peers_item *rdpPeer; rdpSettings *settings; pixman_image_t *new_shadow_buffer; @@ -315,7 +316,7 @@ rdp_switch_mode(struct weston_output *output, struct weston_mode *target_mode) local_mode = ensure_matching_mode(output, target_mode); if (!local_mode) { - weston_log("mode %dx%d not available\n", target_mode->width, target_mode->height); + rdp_debug(rdpBackend, "mode %dx%d not available\n", target_mode->width, target_mode->height); return -ENOENT; } @@ -524,6 +525,16 @@ rdp_destroy(struct weston_compositor *ec) if (b->listener_events[i]) wl_event_source_remove(b->listener_events[i]); + if (b->debug) { + weston_log_scope_destroy(b->debug); + b->debug = NULL; + } + + if (b->verbose) { + weston_log_scope_destroy(b->verbose); + b->verbose = NULL; + } + weston_compositor_shutdown(ec); wl_list_for_each_safe(base, next, &ec->head_list, compositor_link) @@ -824,7 +835,7 @@ xf_peer_activate(freerdp_peer* client) } if (b->force_no_compression && settings->CompressionEnabled) { - weston_log("Forcing compression off\n"); + rdp_debug(b, "Forcing compression off\n"); settings->CompressionEnabled = FALSE; } @@ -867,7 +878,7 @@ xf_peer_activate(freerdp_peer* client) return TRUE; /* when here it's the first reactivation, we need to setup a little more */ - weston_log("kbd_layout:0x%x kbd_type:0x%x kbd_subType:0x%x kbd_functionKeys:0x%x\n", + rdp_debug(b, "kbd_layout:0x%x kbd_type:0x%x kbd_subType:0x%x kbd_functionKeys:0x%x\n", settings->KeyboardLayout, settings->KeyboardType, settings->KeyboardSubType, settings->KeyboardFunctionKey); @@ -1021,10 +1032,18 @@ xf_input_synchronize_event(rdpInput *input, UINT32 flags) { freerdp_peer *client = input->context->peer; RdpPeerContext *peerCtx = (RdpPeerContext *)input->context; + struct rdp_backend *b = peerCtx->rdpBackend; struct rdp_output *output = peerCtx->rdpBackend->output; pixman_box32_t box; pixman_region32_t damage; + rdp_debug_verbose(b, "RDP backend: %s ScrLk:%d, NumLk:%d, CapsLk:%d, KanaLk:%d\n", + __func__, + flags & KBD_SYNC_SCROLL_LOCK ? 1 : 0, + flags & KBD_SYNC_NUM_LOCK ? 1 : 0, + flags & KBD_SYNC_CAPS_LOCK ? 1 : 0, + flags & KBD_SYNC_KANA_LOCK ? 1 : 0); + /* sends a full refresh */ box.x1 = 0; box.y1 = 0; @@ -1083,7 +1102,11 @@ xf_input_keyboard_event(rdpInput *input, UINT16 flags, UINT16 code) static BOOL xf_input_unicode_keyboard_event(rdpInput *input, UINT16 flags, UINT16 code) { - weston_log("Client sent a unicode keyboard event (flags:0x%X code:0x%X)\n", flags, code); + RdpPeerContext *peerContext = (RdpPeerContext *)input->context; + struct rdp_backend *b = peerContext->rdpBackend; + + rdp_debug(b, "Client sent a unicode keyboard event (flags:0x%X code:0x%X)\n", flags, code); + return TRUE; } @@ -1222,6 +1245,17 @@ rdp_backend_create(struct weston_compositor *compositor, b->force_no_compression = config->force_no_compression; b->remotefx_codec = config->remotefx_codec; + b->debug = weston_compositor_add_log_scope(compositor, + "rdp-backend", + "Debug messages from RDP backend\n", + NULL, NULL, NULL); + b->verbose = weston_compositor_add_log_scope(compositor, + "rdp-backend-verbose", + "Verbose debug messages from RDP backend\n", + NULL, NULL, NULL); + + /* After here, rdp_debug() is ready to be used */ + compositor->backend = &b->base; /* activate TLS only if certificate/key are available */ @@ -1291,6 +1325,10 @@ err_compositor: weston_compositor_shutdown(compositor); err_free_strings: + if (b->debug) + weston_log_scope_destroy(b->debug); + if (b->verbose) + weston_log_scope_destroy(b->verbose); free(b->rdp_key); free(b->server_cert); free(b->server_key); diff --git a/libweston/backend-rdp/rdp.h b/libweston/backend-rdp/rdp.h index 65e6f0fa..2ebf50f6 100644 --- a/libweston/backend-rdp/rdp.h +++ b/libweston/backend-rdp/rdp.h @@ -45,6 +45,7 @@ #include "backend.h" #include "shared/helpers.h" +#include "shared/string-helpers.h" #define MAX_FREERDP_FDS 32 #define DEFAULT_AXIS_STEP_DISTANCE 10 @@ -60,6 +61,8 @@ struct rdp_backend { freerdp_listener *listener; struct wl_event_source *listener_events[MAX_FREERDP_FDS]; struct rdp_output *output; + struct weston_log_scope *debug; + struct weston_log_scope *verbose; char *server_cert; char *server_key; @@ -109,6 +112,19 @@ struct rdp_peer_context { }; typedef struct rdp_peer_context RdpPeerContext; +#define rdp_debug_verbose(b, ...) \ + rdp_debug_print(b->verbose, false, __VA_ARGS__) +#define rdp_debug_verbose_continue(b, ...) \ + rdp_debug_print(b->verbose, true, __VA_ARGS__) +#define rdp_debug(b, ...) \ + rdp_debug_print(b->debug, false, __VA_ARGS__) +#define rdp_debug_continue(b, ...) \ + rdp_debug_print(b->debug, true, __VA_ARGS__) + +/* rdputil.c */ +void +rdp_debug_print(struct weston_log_scope *log_scope, bool cont, char *fmt, ...); + static inline struct rdp_head * to_rdp_head(struct weston_head *base) { diff --git a/libweston/backend-rdp/rdputil.c b/libweston/backend-rdp/rdputil.c new file mode 100644 index 00000000..6b9678f9 --- /dev/null +++ b/libweston/backend-rdp/rdputil.c @@ -0,0 +1,77 @@ +/* + * Copyright © 2020 Microsoft + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rdp.h" + +static int cached_tm_mday = -1; + +void rdp_debug_print(struct weston_log_scope *log_scope, bool cont, char *fmt, ...) +{ + char timestr[128]; + int len_va; + char *str; + + if (!log_scope || !weston_log_scope_is_enabled(log_scope)) + return; + + va_list ap; + va_start(ap, fmt); + + if (cont) { + weston_log_scope_vprintf(log_scope, fmt, ap); + goto end; + } + + weston_log_timestamp(timestr, sizeof(timestr), &cached_tm_mday); + len_va = vasprintf(&str, fmt, ap); + if (len_va >= 0) { + weston_log_scope_printf(log_scope, "%s %s", + timestr, str); + free(str); + } else { + const char *oom = "Out of memory"; + + weston_log_scope_printf(log_scope, "%s %s", + timestr, oom); + } +end: + va_end(ap); +} From efd6aae915605c5d3113c7bf09f11afc34a036f9 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 14 Jan 2022 00:19:42 +0000 Subject: [PATCH 140/609] gl-renderer: Remove unnecessary dmabuf conditional We can't try to attach a dmabuf that we never imported. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index e7cd06a9..7ed86afb 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -2821,12 +2821,6 @@ gl_renderer_attach_dmabuf(struct weston_surface *surface, GLenum target; int i; - if (!gr->has_dmabuf_import) { - linux_dmabuf_buffer_send_server_error(dmabuf, - "EGL dmabuf import not supported"); - return; - } - buffer->width = dmabuf->attributes.width; buffer->height = dmabuf->attributes.height; From f8ac6f940fa229adbb17023a6840eeca1da64bbd Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 14 Jan 2022 00:22:20 +0000 Subject: [PATCH 141/609] gl-renderer: Remove outdated comment The comment about needing to have destroyed images is somewhat less true now that we actively avoid doing so. Signed-off-by: Daniel Stone Fixes: 0b51b02c5e67 ("gl-renderer: Don't re-import dmabufs") --- libweston/renderer-gl/gl-renderer.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 7ed86afb..321e67a8 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -2843,14 +2843,6 @@ gl_renderer_attach_dmabuf(struct weston_surface *surface, gs->direct_display = dmabuf->direct_display; surface->is_opaque = dmabuf_is_opaque(dmabuf); - /* - * We try to always hold an imported EGLImage from the dmabuf - * to prevent the client from preventing re-imports. But, we also - * need to re-import every time the contents may change because - * GL driver's caching may need flushing. - * - * Here we release the cache reference which has to be final. - */ if (dmabuf->direct_display) return; From 12675ed19f93df13597152da3a024a1882669148 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 14 Jan 2022 00:03:24 +0000 Subject: [PATCH 142/609] renderer: Add buffer to flush_damage We already have the buffer in the caller, and every no-op implementation will want to access the buffer. So might as well pass it. Signed-off-by: Daniel Stone --- include/libweston/libweston.h | 3 ++- libweston/compositor.c | 7 ++++--- libweston/noop-renderer.c | 3 ++- libweston/pixman-renderer.c | 3 ++- libweston/renderer-gl/gl-renderer.c | 12 ++++++++---- 5 files changed, 18 insertions(+), 10 deletions(-) diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 22cd81fc..76784a4f 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -926,7 +926,8 @@ struct weston_renderer { uint32_t width, uint32_t height); void (*repaint_output)(struct weston_output *output, pixman_region32_t *output_damage); - void (*flush_damage)(struct weston_surface *surface); + void (*flush_damage)(struct weston_surface *surface, + struct weston_buffer *buffer); void (*attach)(struct weston_surface *es, struct weston_buffer *buffer); void (*surface_set_color)(struct weston_surface *surface, float red, float green, diff --git a/libweston/compositor.c b/libweston/compositor.c index cabd0d45..27d46c02 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -2553,9 +2553,10 @@ weston_output_damage(struct weston_output *output) static void surface_flush_damage(struct weston_surface *surface) { - if (surface->buffer_ref.buffer && - wl_shm_buffer_get(surface->buffer_ref.buffer->resource)) - surface->compositor->renderer->flush_damage(surface); + struct weston_buffer *buffer = surface->buffer_ref.buffer; + + if (buffer && wl_shm_buffer_get(buffer->resource)) + surface->compositor->renderer->flush_damage(surface, buffer); if (pixman_region32_not_empty(&surface->damage)) TL_POINT(surface->compositor, "core_flush_damage", TLP_SURFACE(surface), diff --git a/libweston/noop-renderer.c b/libweston/noop-renderer.c index d86e7f0b..0c9964f3 100644 --- a/libweston/noop-renderer.c +++ b/libweston/noop-renderer.c @@ -47,7 +47,8 @@ noop_renderer_repaint_output(struct weston_output *output, } static void -noop_renderer_flush_damage(struct weston_surface *surface) +noop_renderer_flush_damage(struct weston_surface *surface, + struct weston_buffer *buffer) { } diff --git a/libweston/pixman-renderer.c b/libweston/pixman-renderer.c index 754adce2..271bc198 100644 --- a/libweston/pixman-renderer.c +++ b/libweston/pixman-renderer.c @@ -598,7 +598,8 @@ pixman_renderer_repaint_output(struct weston_output *output, } static void -pixman_renderer_flush_damage(struct weston_surface *surface) +pixman_renderer_flush_damage(struct weston_surface *surface, + struct weston_buffer *buffer) { /* No-op for pixman renderer */ } diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 321e67a8..800de5ec 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -1832,12 +1832,12 @@ gl_format_from_internal(GLenum internal_format) } static void -gl_renderer_flush_damage(struct weston_surface *surface) +gl_renderer_flush_damage(struct weston_surface *surface, + struct weston_buffer *buffer) { const struct weston_testsuite_quirks *quirks = &surface->compositor->test_data.test_quirks; struct gl_surface_state *gs = get_surface_state(surface); - struct weston_buffer *buffer = gs->buffer_ref.buffer; struct weston_view *view; bool texture_used; pixman_box32_t *rectangles; @@ -3081,7 +3081,7 @@ gl_renderer_surface_copy_content(struct weston_surface *surface, *(uint32_t *)target = pack_color(format, gs->color); return 0; case BUFFER_TYPE_SHM: - gl_renderer_flush_damage(surface); + gl_renderer_flush_damage(surface, gs->buffer_ref.buffer); /* fall through */ case BUFFER_TYPE_EGL: break; @@ -3228,7 +3228,11 @@ gl_renderer_create_surface(struct weston_surface *surface) if (surface->buffer_ref.buffer) { gl_renderer_attach(surface, surface->buffer_ref.buffer); - gl_renderer_flush_damage(surface); + if (surface->buffer_ref.buffer->resource && + wl_shm_buffer_get(surface->buffer_ref.buffer->resource)) { + gl_renderer_flush_damage(surface, + surface->buffer_ref.buffer); + } } return 0; From fec0400886c6a0d1ab668a155615e6533ec4b881 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 14 Jan 2022 00:05:46 +0000 Subject: [PATCH 143/609] gl-renderer: Drop unnecessary NULL check All the callers of flush_damage guarantee we'll have a buffer. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 800de5ec..4de92a37 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -1844,12 +1844,11 @@ gl_renderer_flush_damage(struct weston_surface *surface, uint8_t *data; int i, j, n; + assert(buffer); + pixman_region32_union(&gs->texture_damage, &gs->texture_damage, &surface->damage); - if (!buffer) - return; - /* Avoid upload, if the texture won't be used this time. * We still accumulate the damage in texture_damage, and * hold the reference to the buffer, in case the surface From ca9bb01fe6f65dad022593859681f8a0b22cc09e Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 14 Jan 2022 00:52:26 +0000 Subject: [PATCH 144/609] renderers: Set buffer properties earlier When we first see a buffer attached, we create a weston_buffer for it. The weston_buffer doesn't contain any useful information in and of itself; that's left to renderers to populate later. Switch this to doing it in the core at the first opportunity, at least for SHM and dmabuf buffers; EGL buffers will follow in the next commit. Signed-off-by: Daniel Stone --- libweston/compositor.c | 15 ++++++++++++++- libweston/pixman-renderer.c | 4 ---- libweston/renderer-gl/gl-renderer.c | 15 --------------- libweston/screenshooter.c | 4 ---- 4 files changed, 14 insertions(+), 24 deletions(-) diff --git a/libweston/compositor.c b/libweston/compositor.c index 27d46c02..610f46c1 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -2388,6 +2388,8 @@ WL_EXPORT struct weston_buffer * weston_buffer_from_resource(struct wl_resource *resource) { struct weston_buffer *buffer; + struct wl_shm_buffer *shm; + struct linux_dmabuf_buffer *dmabuf; struct wl_listener *listener; listener = wl_resource_get_destroy_listener(resource, @@ -2404,9 +2406,20 @@ weston_buffer_from_resource(struct wl_resource *resource) buffer->resource = resource; wl_signal_init(&buffer->destroy_signal); buffer->destroy_listener.notify = weston_buffer_destroy_handler; - buffer->y_inverted = 1; wl_resource_add_destroy_listener(resource, &buffer->destroy_listener); + if ((shm = wl_shm_buffer_get(buffer->resource))) { + buffer->shm_buffer = shm; + buffer->width = wl_shm_buffer_get_width(shm); + buffer->height = wl_shm_buffer_get_height(shm); + buffer->y_inverted = true; + } else if ((dmabuf = linux_dmabuf_buffer_get(buffer->resource))) { + buffer->width = dmabuf->attributes.width; + buffer->height = dmabuf->attributes.height; + buffer->y_inverted = + !(dmabuf->attributes.flags & ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT); + } + return buffer; } diff --git a/libweston/pixman-renderer.c b/libweston/pixman-renderer.c index 271bc198..90a1248f 100644 --- a/libweston/pixman-renderer.c +++ b/libweston/pixman-renderer.c @@ -666,10 +666,6 @@ pixman_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) es->is_opaque = pixel_format_is_opaque(pixel_info); - buffer->shm_buffer = shm_buffer; - buffer->width = wl_shm_buffer_get_width(shm_buffer); - buffer->height = wl_shm_buffer_get_height(shm_buffer); - ps->image = pixman_image_create_bits(pixel_info->pixman_format, buffer->width, buffer->height, wl_shm_buffer_get_data(shm_buffer), diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 4de92a37..d82df49d 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -1965,10 +1965,6 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer, int num_planes; bool using_glesv2 = gr->gl_version < gr_gl_version(3, 0); - buffer->shm_buffer = shm_buffer; - buffer->width = wl_shm_buffer_get_width(shm_buffer); - buffer->height = wl_shm_buffer_get_height(shm_buffer); - num_planes = 1; gs->offset[0] = 0; gs->hsub[0] = 1; @@ -2820,17 +2816,6 @@ gl_renderer_attach_dmabuf(struct weston_surface *surface, GLenum target; int i; - buffer->width = dmabuf->attributes.width; - buffer->height = dmabuf->attributes.height; - - /* - * GL-renderer uses the OpenGL convention of texture coordinates, where - * the origin is at bottom-left. Because dmabuf buffers have the origin - * at top-left, we must invert the Y_INVERT flag to get the image right. - */ - buffer->y_inverted = - !(dmabuf->attributes.flags & ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT); - for (i = 0; i < gs->num_images; i++) egl_image_unref(gs->images[i]); gs->num_images = 0; diff --git a/libweston/screenshooter.c b/libweston/screenshooter.c index fedc4805..4205412f 100644 --- a/libweston/screenshooter.c +++ b/libweston/screenshooter.c @@ -186,10 +186,6 @@ weston_screenshooter_shoot(struct weston_output *output, return -1; } - buffer->shm_buffer = wl_shm_buffer_get(buffer->resource); - buffer->width = wl_shm_buffer_get_width(buffer->shm_buffer); - buffer->height = wl_shm_buffer_get_height(buffer->shm_buffer); - if (buffer->width < output->current_mode->width || buffer->height < output->current_mode->height) { done(data, WESTON_SCREENSHOOTER_BAD_BUFFER); From f49d6f47f3c2eb55f846ba9dc2e1e09ba3c86ea5 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 21 Jan 2022 15:19:05 +0000 Subject: [PATCH 145/609] gl-renderer: Reject unknown-format dmabufs Make sure we only import dmabufs where the underlying pixel_format is known: if we can't reason about the buffer content, we're not entirely likely to be able to display it well. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index d82df49d..bf47dd66 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -2626,6 +2626,9 @@ import_dmabuf(struct gl_renderer *gr, struct dmabuf_image *image; GLenum target; + if (!pixel_format_get_info(dmabuf->attributes.format)) + return NULL; + image = dmabuf_image_create(); image->dmabuf = dmabuf; @@ -2876,6 +2879,9 @@ populate_supported_formats(struct weston_compositor *ec, return 0; for (i = 0; i < num_formats; i++) { + if (!pixel_format_get_info(formats[i])) + continue; + fmt = weston_drm_format_array_add_format(supported_formats, formats[i]); if (!fmt) { From 1d5f8af82e975aa94aea41b3fc8d4270663b4720 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 14 Jan 2022 01:02:21 +0000 Subject: [PATCH 146/609] gl-renderer: Add hook to fill weston_buffer for EGL Rather than only filling weston_buffer information when we first come to use it, add an explicit hook so we can fill the dimensions the first time the buffer's attached. Signed-off-by: Daniel Stone --- compositor/weston-screenshooter.c | 3 ++- include/libweston/libweston.h | 7 +++++-- libweston/compositor.c | 17 ++++++++++++++-- libweston/renderer-gl/gl-renderer.c | 31 +++++++++++++++++++++-------- 4 files changed, 45 insertions(+), 13 deletions(-) diff --git a/compositor/weston-screenshooter.c b/compositor/weston-screenshooter.c index 18b2dca4..9b437d3c 100644 --- a/compositor/weston-screenshooter.c +++ b/compositor/weston-screenshooter.c @@ -68,8 +68,9 @@ screenshooter_take_shot(struct wl_client *client, { struct weston_output *output = weston_head_from_resource(output_resource)->output; + struct weston_compositor *ec = output->compositor; struct weston_buffer *buffer = - weston_buffer_from_resource(buffer_resource); + weston_buffer_from_resource(ec, buffer_resource); if (buffer == NULL) { wl_resource_post_no_memory(resource); diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 76784a4f..974df395 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -934,7 +934,6 @@ struct weston_renderer { float blue, float alpha); void (*destroy)(struct weston_compositor *ec); - /** See weston_surface_get_content_size() */ void (*surface_get_content_size)(struct weston_surface *surface, int *width, int *height); @@ -951,6 +950,9 @@ struct weston_renderer { const struct weston_drm_format_array * (*get_supported_formats)(struct weston_compositor *ec); + + bool (*fill_buffer_info)(struct weston_compositor *ec, + struct weston_buffer *buffer); }; enum weston_capability { @@ -1818,7 +1820,8 @@ weston_surface_copy_content(struct weston_surface *surface, int width, int height); struct weston_buffer * -weston_buffer_from_resource(struct wl_resource *resource); +weston_buffer_from_resource(struct weston_compositor *ec, + struct wl_resource *resource); void weston_compositor_get_time(struct timespec *time); diff --git a/libweston/compositor.c b/libweston/compositor.c index 610f46c1..e9188422 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -2385,7 +2385,8 @@ weston_buffer_destroy_handler(struct wl_listener *listener, void *data) } WL_EXPORT struct weston_buffer * -weston_buffer_from_resource(struct wl_resource *resource) +weston_buffer_from_resource(struct weston_compositor *ec, + struct wl_resource *resource) { struct weston_buffer *buffer; struct wl_shm_buffer *shm; @@ -2418,9 +2419,20 @@ weston_buffer_from_resource(struct wl_resource *resource) buffer->height = dmabuf->attributes.height; buffer->y_inverted = !(dmabuf->attributes.flags & ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT); + } else { + /* Only taken for legacy EGL buffers */ + if (!ec->renderer->fill_buffer_info || + !ec->renderer->fill_buffer_info(ec, buffer)) { + goto fail; + } } return buffer; + +fail: + wl_list_remove(&buffer->destroy_listener.link); + free(buffer); + return NULL; } static void @@ -3440,10 +3452,11 @@ surface_attach(struct wl_client *client, struct wl_resource *buffer_resource, int32_t sx, int32_t sy) { struct weston_surface *surface = wl_resource_get_user_data(resource); + struct weston_compositor *ec = surface->compositor; struct weston_buffer *buffer = NULL; if (buffer_resource) { - buffer = weston_buffer_from_resource(buffer_resource); + buffer = weston_buffer_from_resource(ec, buffer_resource); if (buffer == NULL) { wl_client_post_no_memory(client); return; diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index bf47dd66..72cb3111 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -2153,6 +2153,28 @@ unsupported: } } +static bool +gl_renderer_fill_buffer_info(struct weston_compositor *ec, + struct weston_buffer *buffer) +{ + struct gl_renderer *gr = get_renderer(ec); + bool ret = true; + + buffer->legacy_buffer = (struct wl_buffer *)buffer->resource; + ret &= gr->query_buffer(gr->egl_display, buffer->legacy_buffer, + EGL_WIDTH, &buffer->width); + ret &= gr->query_buffer(gr->egl_display, buffer->legacy_buffer, + EGL_HEIGHT, &buffer->height); + + /* Assume scanout co-ordinate space i.e. (0,0) is top-left + * if the query fails */ + buffer->y_inverted = true; + gr->query_buffer(gr->egl_display, buffer->legacy_buffer, + EGL_WAYLAND_Y_INVERTED_WL, &buffer->y_inverted); + + return ret; +} + static void gl_renderer_attach_egl(struct weston_surface *es, struct weston_buffer *buffer, uint32_t format) @@ -2164,14 +2186,6 @@ gl_renderer_attach_egl(struct weston_surface *es, struct weston_buffer *buffer, GLenum target; int i, num_planes; - buffer->legacy_buffer = (struct wl_buffer *)buffer->resource; - gr->query_buffer(gr->egl_display, buffer->legacy_buffer, - EGL_WIDTH, &buffer->width); - gr->query_buffer(gr->egl_display, buffer->legacy_buffer, - EGL_HEIGHT, &buffer->height); - gr->query_buffer(gr->egl_display, buffer->legacy_buffer, - EGL_WAYLAND_Y_INVERTED_WL, &buffer->y_inverted); - for (i = 0; i < gs->num_images; i++) { egl_image_unref(gs->images[i]); gs->images[i] = NULL; @@ -3674,6 +3688,7 @@ gl_renderer_display_create(struct weston_compositor *ec, gr->base.surface_get_content_size = gl_renderer_surface_get_content_size; gr->base.surface_copy_content = gl_renderer_surface_copy_content; + gr->base.fill_buffer_info = gl_renderer_fill_buffer_info; if (gl_renderer_setup_egl_display(gr, options->egl_native_display) < 0) goto fail; From 34cd0d114f23e75ddecaf7d60f904af0e99a17ad Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 14 Jan 2022 01:09:51 +0000 Subject: [PATCH 147/609] weston_buffer: Add type field Rather than open-coding various resource -> type accessors, just stick a type enum in the buffer struct. Signed-off-by: Daniel Stone --- include/libweston/libweston.h | 8 ++++++++ libweston/compositor.c | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 974df395..5cb5c60a 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -1177,10 +1177,18 @@ struct weston_buffer { struct wl_signal destroy_signal; struct wl_listener destroy_listener; + enum { + WESTON_BUFFER_SHM, + WESTON_BUFFER_DMABUF, + WESTON_BUFFER_RENDERER_OPAQUE, + } type; + union { struct wl_shm_buffer *shm_buffer; + void *dmabuf; void *legacy_buffer; }; + int32_t width, height; uint32_t busy_count; int y_inverted; diff --git a/libweston/compositor.c b/libweston/compositor.c index e9188422..d24fd030 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -2410,11 +2410,14 @@ weston_buffer_from_resource(struct weston_compositor *ec, wl_resource_add_destroy_listener(resource, &buffer->destroy_listener); if ((shm = wl_shm_buffer_get(buffer->resource))) { + buffer->type = WESTON_BUFFER_SHM; buffer->shm_buffer = shm; buffer->width = wl_shm_buffer_get_width(shm); buffer->height = wl_shm_buffer_get_height(shm); buffer->y_inverted = true; } else if ((dmabuf = linux_dmabuf_buffer_get(buffer->resource))) { + buffer->type = WESTON_BUFFER_DMABUF; + buffer->dmabuf = dmabuf; buffer->width = dmabuf->attributes.width; buffer->height = dmabuf->attributes.height; buffer->y_inverted = @@ -2425,6 +2428,7 @@ weston_buffer_from_resource(struct weston_compositor *ec, !ec->renderer->fill_buffer_info(ec, buffer)) { goto fail; } + buffer->type = WESTON_BUFFER_RENDERER_OPAQUE; } return buffer; From fef8bb275cf83ea4ff71d1d405402b5567ab5567 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 14 Jan 2022 01:36:54 +0000 Subject: [PATCH 148/609] weston_buffer: Make use of weston_buffer->type Rather than calling accessors (wl_shm_buffer_get etc) to figure out which type our buffer is, just look in the structure. Signed-off-by: Daniel Stone --- libweston/backend-drm/fb.c | 13 +++---- libweston/compositor.c | 42 +++++++++----------- libweston/noop-renderer.c | 5 +-- libweston/pixman-renderer.c | 6 +-- libweston/renderer-gl/gl-renderer.c | 59 ++++++++++++++++------------- libweston/screenshooter.c | 2 +- 6 files changed, 61 insertions(+), 66 deletions(-) diff --git a/libweston/backend-drm/fb.c b/libweston/backend-drm/fb.c index 8630ebad..718095c9 100644 --- a/libweston/backend-drm/fb.c +++ b/libweston/backend-drm/fb.c @@ -522,7 +522,6 @@ drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev, struct weston_buffer *buffer = ev->surface->buffer_ref.buffer; struct drm_buffer_fb *buf_fb; bool is_opaque = weston_view_is_opaque(ev, &ev->transform.boundingbox); - struct linux_dmabuf_buffer *dmabuf; struct drm_fb *fb; struct drm_plane *plane; @@ -550,20 +549,16 @@ drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev, buf_fb->buffer_destroy_listener.notify = drm_fb_handle_buffer_destroy; wl_signal_add(&buffer->destroy_signal, &buf_fb->buffer_destroy_listener); - if (wl_shm_buffer_get(buffer->resource)) - goto unsuitable; - /* GBM is used for dmabuf import as well as from client wl_buffer. */ if (!b->gbm) goto unsuitable; - dmabuf = linux_dmabuf_buffer_get(buffer->resource); - if (dmabuf) { - fb = drm_fb_get_from_dmabuf(dmabuf, b, is_opaque, + if (buffer->type == WESTON_BUFFER_DMABUF) { + fb = drm_fb_get_from_dmabuf(buffer->dmabuf, b, is_opaque, &buf_fb->failure_reasons); if (!fb) goto unsuitable; - } else { + } else if (buffer->type == WESTON_BUFFER_RENDERER_OPAQUE) { struct gbm_bo *bo; bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_WL_BUFFER, @@ -576,6 +571,8 @@ drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev, gbm_bo_destroy(bo); goto unsuitable; } + } else { + goto unsuitable; } /* Check if this buffer can ever go on any planes. If it can't, we have diff --git a/libweston/compositor.c b/libweston/compositor.c index d24fd030..b2ecaa89 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -2584,7 +2584,7 @@ surface_flush_damage(struct weston_surface *surface) { struct weston_buffer *buffer = surface->buffer_ref.buffer; - if (buffer && wl_shm_buffer_get(buffer->resource)) + if (buffer && buffer->type == WESTON_BUFFER_SHM) surface->compositor->renderer->flush_damage(surface, buffer); if (pixman_region32_not_empty(&surface->damage)) @@ -3978,14 +3978,7 @@ surface_commit(struct wl_client *client, struct wl_resource *resource) return; } - /* We support fences for both wp_linux_dmabuf and opaque EGL - * buffers, as mandated by minor version 2 of the - * zwp_linux_explicit_synchronization_v1 protocol. Since - * renderers that support fences currently only support these - * two buffer types plus SHM buffers, we can just check for the - * SHM buffer case here. - */ - if (wl_shm_buffer_get(surface->pending.buffer->resource)) { + if (surface->pending.buffer->type == WESTON_BUFFER_SHM) { fd_clear(&surface->pending.acquire_fence_fd); wl_resource_post_error(surface->synchronization_resource, ZWP_LINUX_SURFACE_SYNCHRONIZATION_V1_ERROR_UNSUPPORTED_BUFFER, @@ -7473,30 +7466,30 @@ static void debug_scene_view_print_buffer(FILE *fp, struct weston_view *view) { struct weston_buffer *buffer = view->surface->buffer_ref.buffer; - struct wl_shm_buffer *shm; - struct linux_dmabuf_buffer *dmabuf; + struct wl_shm_buffer *shm = buffer->shm_buffer; + struct linux_dmabuf_buffer *dmabuf = buffer->dmabuf; const struct pixel_format_info *pixel_info = NULL; + uint32_t _format; + uint64_t modifier; + char *modifier_name; if (!buffer) { fprintf(fp, "\t\t[buffer not available]\n"); return; } - shm = wl_shm_buffer_get(buffer->resource); - if (shm) { - uint32_t _format = wl_shm_buffer_get_format(shm); + switch (buffer->type) { + case WESTON_BUFFER_SHM: + _format = wl_shm_buffer_get_format(shm); pixel_info = pixel_format_get_info_shm(_format); fprintf(fp, "\t\tSHM buffer\n"); fprintf(fp, "\t\t\tformat: 0x%lx %s\n", (unsigned long) _format, pixel_info ? pixel_info->drm_format_name : "UNKNOWN"); - return; - } - - dmabuf = linux_dmabuf_buffer_get(buffer->resource); - if (dmabuf) { - uint64_t modifier = dmabuf->attributes.modifier[0]; - char *modifier_name = pixel_format_get_modifier(modifier); + break; + case WESTON_BUFFER_DMABUF: + modifier = dmabuf->attributes.modifier[0]; + modifier_name = pixel_format_get_modifier(modifier); pixel_info = pixel_format_get_info(dmabuf->attributes.format); fprintf(fp, "\t\tdmabuf buffer\n"); fprintf(fp, "\t\t\tformat: 0x%lx %s\n", @@ -7506,10 +7499,11 @@ debug_scene_view_print_buffer(FILE *fp, struct weston_view *view) fprintf(fp, "\t\t\tmodifier: %s\n", modifier_name ? modifier_name : "Failed to convert to a modifier name"); free(modifier_name); - return; + break; + default: + fprintf(fp, "\t\tEGL buffer\n"); + break; } - - fprintf(fp, "\t\tEGL buffer\n"); } static void diff --git a/libweston/noop-renderer.c b/libweston/noop-renderer.c index 0c9964f3..0fb33e4e 100644 --- a/libweston/noop-renderer.c +++ b/libweston/noop-renderer.c @@ -63,13 +63,12 @@ noop_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) if (!buffer) return; - shm_buffer = wl_shm_buffer_get(buffer->resource); - - if (!shm_buffer) { + if (buffer->type != WESTON_BUFFER_SHM) { weston_log("No-op renderer supports only SHM buffers\n"); return; } + shm_buffer = buffer->shm_buffer; data = wl_shm_buffer_get_data(shm_buffer); stride = wl_shm_buffer_get_stride(shm_buffer); width = wl_shm_buffer_get_width(shm_buffer); diff --git a/libweston/pixman-renderer.c b/libweston/pixman-renderer.c index 90a1248f..be2df1d5 100644 --- a/libweston/pixman-renderer.c +++ b/libweston/pixman-renderer.c @@ -644,15 +644,15 @@ pixman_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) if (!buffer) return; - shm_buffer = wl_shm_buffer_get(buffer->resource); - - if (! shm_buffer) { + if (buffer->type != WESTON_BUFFER_SHM) { weston_log("Pixman renderer supports only SHM buffers\n"); weston_buffer_reference(&ps->buffer_ref, NULL); weston_buffer_release_reference(&ps->buffer_release_ref, NULL); return; } + shm_buffer = buffer->shm_buffer; + pixel_info = pixel_format_get_info_shm(wl_shm_buffer_get_format(shm_buffer)); if (!pixel_info || !pixman_format_supported_source(pixel_info->pixman_format)) { weston_log("Unsupported SHM buffer format 0x%x\n", diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 72cb3111..2b9c11d9 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -888,7 +888,7 @@ ensure_surface_buffer_is_ready(struct gl_renderer *gr, assert(gr->has_native_fence_sync); /* We should only get a fence for non-SHM buffers, since surface * commit would have failed otherwise. */ - assert(wl_shm_buffer_get(buffer->resource) == NULL); + assert(buffer->type != WESTON_BUFFER_SHM); attribs[1] = dup(surface->acquire_fence_fd); if (attribs[1] == -1) { @@ -2938,8 +2938,6 @@ gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) struct weston_compositor *ec = es->compositor; struct gl_renderer *gr = get_renderer(ec); struct gl_surface_state *gs = get_surface_state(es); - struct wl_shm_buffer *shm_buffer; - struct linux_dmabuf_buffer *dmabuf; EGLint format; int i; @@ -2962,30 +2960,38 @@ gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) return; } - shm_buffer = wl_shm_buffer_get(buffer->resource); - - if (shm_buffer) - gl_renderer_attach_shm(es, buffer, shm_buffer); - else if (gr->has_bind_display && - gr->query_buffer(gr->egl_display, (void *)buffer->resource, - EGL_TEXTURE_FORMAT, &format)) - gl_renderer_attach_egl(es, buffer, format); - else if ((dmabuf = linux_dmabuf_buffer_get(buffer->resource))) - gl_renderer_attach_dmabuf(es, buffer, dmabuf); - else { - weston_log("unhandled buffer type!\n"); - if (gr->has_bind_display) { - weston_log("eglQueryWaylandBufferWL failed\n"); - gl_renderer_print_egl_error_state(); + switch (buffer->type) { + case WESTON_BUFFER_SHM: + gl_renderer_attach_shm(es, buffer, buffer->shm_buffer); + return; + case WESTON_BUFFER_DMABUF: + gl_renderer_attach_dmabuf(es, buffer, buffer->dmabuf); + return; + case WESTON_BUFFER_RENDERER_OPAQUE: + if (!gr->has_bind_display || + !gr->query_buffer(gr->egl_display, + buffer->legacy_buffer, + EGL_TEXTURE_FORMAT, &format)) { + break; } - weston_buffer_reference(&gs->buffer_ref, NULL); - weston_buffer_release_reference(&gs->buffer_release_ref, NULL); - gs->buffer_type = BUFFER_TYPE_NULL; - gs->y_inverted = true; - es->is_opaque = false; - weston_buffer_send_server_error(buffer, - "disconnecting due to unhandled buffer type"); + gl_renderer_attach_egl(es, buffer, format); + return; + default: + break; + } + + weston_log("unhandled buffer type!\n"); + if (gr->has_bind_display) { + weston_log("eglQueryWaylandBufferWL failed\n"); + gl_renderer_print_egl_error_state(); } + weston_buffer_reference(&gs->buffer_ref, NULL); + weston_buffer_release_reference(&gs->buffer_release_ref, NULL); + gs->buffer_type = BUFFER_TYPE_NULL; + gs->y_inverted = true; + es->is_opaque = false; + weston_buffer_send_server_error(buffer, + "disconnecting due to unhandled buffer type"); } static void @@ -3232,8 +3238,7 @@ gl_renderer_create_surface(struct weston_surface *surface) if (surface->buffer_ref.buffer) { gl_renderer_attach(surface, surface->buffer_ref.buffer); - if (surface->buffer_ref.buffer->resource && - wl_shm_buffer_get(surface->buffer_ref.buffer->resource)) { + if (surface->buffer_ref.buffer->type == WESTON_BUFFER_SHM) { gl_renderer_flush_damage(surface, surface->buffer_ref.buffer); } diff --git a/libweston/screenshooter.c b/libweston/screenshooter.c index 4205412f..101a80a3 100644 --- a/libweston/screenshooter.c +++ b/libweston/screenshooter.c @@ -181,7 +181,7 @@ weston_screenshooter_shoot(struct weston_output *output, { struct screenshooter_frame_listener *l; - if (!wl_shm_buffer_get(buffer->resource)) { + if (buffer->type != WESTON_BUFFER_SHM) { done(data, WESTON_SCREENSHOOTER_BAD_BUFFER); return -1; } From 6dcf3eac1f9688adee8f5715dae326f9db014d90 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 14 Jan 2022 01:55:50 +0000 Subject: [PATCH 149/609] weston_buffer: Add pixel format and modifier info Promote these to weston_buffer rather than burying them in the renderers and backends. Signed-off-by: Daniel Stone --- include/libweston/libweston.h | 3 +++ libweston/compositor.c | 19 ++++++++++++++++ libweston/renderer-gl/gl-renderer.c | 35 ++++++++++++++++++++++++++++- 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 5cb5c60a..4be9f8d4 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -1193,6 +1193,9 @@ struct weston_buffer { uint32_t busy_count; int y_inverted; void *backend_private; + + const struct pixel_format_info *pixel_format; + uint64_t format_modifier; }; struct weston_buffer_reference { diff --git a/libweston/compositor.c b/libweston/compositor.c index b2ecaa89..4e276b4e 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -52,6 +52,7 @@ #include #include #include +#include #include "timeline.h" @@ -2415,11 +2416,25 @@ weston_buffer_from_resource(struct weston_compositor *ec, buffer->width = wl_shm_buffer_get_width(shm); buffer->height = wl_shm_buffer_get_height(shm); buffer->y_inverted = true; + /* wl_shm might create a buffer with an unknown format, so check + * and reject */ + buffer->pixel_format = + pixel_format_get_info_shm(wl_shm_buffer_get_format(shm)); + buffer->format_modifier = DRM_FORMAT_MOD_LINEAR; + + if (!buffer->pixel_format) + goto fail; } else if ((dmabuf = linux_dmabuf_buffer_get(buffer->resource))) { buffer->type = WESTON_BUFFER_DMABUF; buffer->dmabuf = dmabuf; buffer->width = dmabuf->attributes.width; buffer->height = dmabuf->attributes.height; + buffer->pixel_format = + pixel_format_get_info(dmabuf->attributes.format); + /* dmabuf import should assure we don't create a buffer with an + * unknown format */ + assert(buffer->pixel_format); + buffer->format_modifier = dmabuf->attributes.modifier[0]; buffer->y_inverted = !(dmabuf->attributes.flags & ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT); } else { @@ -2431,6 +2446,10 @@ weston_buffer_from_resource(struct weston_compositor *ec, buffer->type = WESTON_BUFFER_RENDERER_OPAQUE; } + /* Don't accept any formats we can't reason about: the importer should + * make sure this never happens */ + assert(buffer->pixel_format); + return buffer; fail: diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 2b9c11d9..a87ac466 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -2158,6 +2158,8 @@ gl_renderer_fill_buffer_info(struct weston_compositor *ec, struct weston_buffer *buffer) { struct gl_renderer *gr = get_renderer(ec); + EGLint format; + uint32_t fourcc; bool ret = true; buffer->legacy_buffer = (struct wl_buffer *)buffer->resource; @@ -2165,6 +2167,37 @@ gl_renderer_fill_buffer_info(struct weston_compositor *ec, EGL_WIDTH, &buffer->width); ret &= gr->query_buffer(gr->egl_display, buffer->legacy_buffer, EGL_HEIGHT, &buffer->height); + ret &= gr->query_buffer(gr->egl_display, buffer->legacy_buffer, + EGL_TEXTURE_FORMAT, &format); + if (!ret) + return false; + + /* The legacy EGL buffer interface only describes the channels we can + * sample from; not their depths or order. Take a stab at something + * which might be representative. Pessimise extremely hard for + * TEXTURE_EXTERNAL_OES. */ + switch (format) { + case EGL_TEXTURE_RGB: + fourcc = DRM_FORMAT_XRGB8888; + break; + case EGL_TEXTURE_EXTERNAL_WL: + case EGL_TEXTURE_RGBA: + fourcc = DRM_FORMAT_ARGB8888; + break; + case EGL_TEXTURE_Y_XUXV_WL: + fourcc = DRM_FORMAT_YUYV; + break; + case EGL_TEXTURE_Y_UV_WL: + fourcc = DRM_FORMAT_NV12; + break; + case EGL_TEXTURE_Y_U_V_WL: + fourcc = DRM_FORMAT_YUV420; + break; + } + + buffer->pixel_format = pixel_format_get_info(fourcc); + assert(buffer->pixel_format); + buffer->format_modifier = DRM_FORMAT_MOD_INVALID; /* Assume scanout co-ordinate space i.e. (0,0) is top-left * if the query fails */ @@ -2172,7 +2205,7 @@ gl_renderer_fill_buffer_info(struct weston_compositor *ec, gr->query_buffer(gr->egl_display, buffer->legacy_buffer, EGL_WAYLAND_Y_INVERTED_WL, &buffer->y_inverted); - return ret; + return true; } static void From 4f88b2655e0d2f953970ecd40bcecfb95e4c6799 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Sat, 15 Jan 2022 17:36:02 +0000 Subject: [PATCH 150/609] weston_buffer: Change y_inverted to explicit origin enum y_inverted meant that the buffer's origin was (0,0), and non-inverted meant that the buffer's origin was (0,height). In practice, every buffer was 'inverted' into our natural co-ordinate space that we use everywhere. Switch to using an explicit origin enum to make this more clear. Signed-off-by: Daniel Stone --- include/libweston/libweston.h | 5 ++++- libweston/compositor.c | 8 +++++--- libweston/renderer-gl/gl-renderer.c | 14 +++++++++----- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 4be9f8d4..3576426f 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -1191,7 +1191,10 @@ struct weston_buffer { int32_t width, height; uint32_t busy_count; - int y_inverted; + enum { + ORIGIN_TOP_LEFT, /* buffer content starts at (0,0) */ + ORIGIN_BOTTOM_LEFT, /* buffer content starts at (0, height) */ + } buffer_origin; void *backend_private; const struct pixel_format_info *pixel_format; diff --git a/libweston/compositor.c b/libweston/compositor.c index 4e276b4e..51e55ef2 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -2415,7 +2415,7 @@ weston_buffer_from_resource(struct weston_compositor *ec, buffer->shm_buffer = shm; buffer->width = wl_shm_buffer_get_width(shm); buffer->height = wl_shm_buffer_get_height(shm); - buffer->y_inverted = true; + buffer->buffer_origin = ORIGIN_TOP_LEFT; /* wl_shm might create a buffer with an unknown format, so check * and reject */ buffer->pixel_format = @@ -2435,8 +2435,10 @@ weston_buffer_from_resource(struct weston_compositor *ec, * unknown format */ assert(buffer->pixel_format); buffer->format_modifier = dmabuf->attributes.modifier[0]; - buffer->y_inverted = - !(dmabuf->attributes.flags & ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT); + if (dmabuf->attributes.flags & ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT) + buffer->buffer_origin = ORIGIN_BOTTOM_LEFT; + else + buffer->buffer_origin = ORIGIN_TOP_LEFT; } else { /* Only taken for legacy EGL buffers */ if (!ec->renderer->fill_buffer_info || diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index a87ac466..bf4435b6 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -2160,6 +2160,7 @@ gl_renderer_fill_buffer_info(struct weston_compositor *ec, struct gl_renderer *gr = get_renderer(ec); EGLint format; uint32_t fourcc; + EGLint y_inverted; bool ret = true; buffer->legacy_buffer = (struct wl_buffer *)buffer->resource; @@ -2201,9 +2202,12 @@ gl_renderer_fill_buffer_info(struct weston_compositor *ec, /* Assume scanout co-ordinate space i.e. (0,0) is top-left * if the query fails */ - buffer->y_inverted = true; - gr->query_buffer(gr->egl_display, buffer->legacy_buffer, - EGL_WAYLAND_Y_INVERTED_WL, &buffer->y_inverted); + ret = gr->query_buffer(gr->egl_display, buffer->legacy_buffer, + EGL_WAYLAND_Y_INVERTED_WL, &y_inverted); + if (!ret || y_inverted) + buffer->buffer_origin = ORIGIN_TOP_LEFT; + else + buffer->buffer_origin = ORIGIN_BOTTOM_LEFT; return true; } @@ -2281,7 +2285,7 @@ gl_renderer_attach_egl(struct weston_surface *es, struct weston_buffer *buffer, gs->pitch = buffer->width; gs->height = buffer->height; gs->buffer_type = BUFFER_TYPE_EGL; - gs->y_inverted = buffer->y_inverted; + gs->y_inverted = (buffer->buffer_origin == ORIGIN_TOP_LEFT); } static void @@ -2873,7 +2877,7 @@ gl_renderer_attach_dmabuf(struct weston_surface *surface, gs->pitch = buffer->width; gs->height = buffer->height; gs->buffer_type = BUFFER_TYPE_EGL; - gs->y_inverted = buffer->y_inverted; + gs->y_inverted = (buffer->buffer_origin == ORIGIN_TOP_LEFT); gs->direct_display = dmabuf->direct_display; surface->is_opaque = dmabuf_is_opaque(dmabuf); From 7506cf5240450620349b04085cd43492107b1ff2 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 14 Jan 2022 02:10:22 +0000 Subject: [PATCH 151/609] gl-renderer: Simplify surface->is_opaque We already have the format; no need to go chasing it again. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index bf4435b6..99d80df6 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -2846,19 +2846,6 @@ gl_renderer_import_dmabuf(struct weston_compositor *ec, return true; } -static bool -dmabuf_is_opaque(struct linux_dmabuf_buffer *dmabuf) -{ - const struct pixel_format_info *info; - - info = pixel_format_get_info(dmabuf->attributes.format & - ~DRM_FORMAT_BIG_ENDIAN); - if (!info) - return false; - - return pixel_format_is_opaque(info); -} - static void gl_renderer_attach_dmabuf(struct weston_surface *surface, struct weston_buffer *buffer, @@ -2879,7 +2866,7 @@ gl_renderer_attach_dmabuf(struct weston_surface *surface, gs->buffer_type = BUFFER_TYPE_EGL; gs->y_inverted = (buffer->buffer_origin == ORIGIN_TOP_LEFT); gs->direct_display = dmabuf->direct_display; - surface->is_opaque = dmabuf_is_opaque(dmabuf); + surface->is_opaque = pixel_format_is_opaque(buffer->pixel_format); if (dmabuf->direct_display) return; From 0a8802404cd7597bf6f414646a39b00bf0975e6f Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 14 Jan 2022 02:01:24 +0000 Subject: [PATCH 152/609] scene-graph: Use weston_buffer's format/modifier info to print Now that we have this generically available, use it rather than calling into per-buffer getters. Signed-off-by: Daniel Stone --- libweston/compositor.c | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/libweston/compositor.c b/libweston/compositor.c index 51e55ef2..8d20305d 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -7487,11 +7487,6 @@ static void debug_scene_view_print_buffer(FILE *fp, struct weston_view *view) { struct weston_buffer *buffer = view->surface->buffer_ref.buffer; - struct wl_shm_buffer *shm = buffer->shm_buffer; - struct linux_dmabuf_buffer *dmabuf = buffer->dmabuf; - const struct pixel_format_info *pixel_info = NULL; - uint32_t _format; - uint64_t modifier; char *modifier_name; if (!buffer) { @@ -7501,30 +7496,27 @@ debug_scene_view_print_buffer(FILE *fp, struct weston_view *view) switch (buffer->type) { case WESTON_BUFFER_SHM: - _format = wl_shm_buffer_get_format(shm); - pixel_info = pixel_format_get_info_shm(_format); fprintf(fp, "\t\tSHM buffer\n"); - fprintf(fp, "\t\t\tformat: 0x%lx %s\n", - (unsigned long) _format, - pixel_info ? pixel_info->drm_format_name : "UNKNOWN"); break; case WESTON_BUFFER_DMABUF: - modifier = dmabuf->attributes.modifier[0]; - modifier_name = pixel_format_get_modifier(modifier); - pixel_info = pixel_format_get_info(dmabuf->attributes.format); fprintf(fp, "\t\tdmabuf buffer\n"); - fprintf(fp, "\t\t\tformat: 0x%lx %s\n", - (unsigned long) dmabuf->attributes.format, - pixel_info ? pixel_info->drm_format_name : "UNKNOWN"); - - fprintf(fp, "\t\t\tmodifier: %s\n", modifier_name ? modifier_name : - "Failed to convert to a modifier name"); - free(modifier_name); break; default: - fprintf(fp, "\t\tEGL buffer\n"); + fprintf(fp, "\t\tEGL buffer:\n"); + fprintf(fp, "\t\t\t[format may be inaccurate]\n"); break; } + + fprintf(fp, "\t\t\tformat: 0x%lx %s\n", + (unsigned long) buffer->pixel_format, + buffer->pixel_format ? + buffer->pixel_format->drm_format_name : "UNKNOWN"); + + modifier_name = pixel_format_get_modifier(buffer->format_modifier); + fprintf(fp, "\t\t\tmodifier: %s\n", + modifier_name ? + modifier_name : "Failed to convert to a modifier name"); + free(modifier_name); } static void From 1d9c62b50dcc9602dfad6b6a48ff02c290d4149e Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 21 Jan 2022 11:05:26 +0000 Subject: [PATCH 153/609] weston_buffer: Print more buffer information in scene-graph Try to print out as much information as we can about the buffer. Signed-off-by: Daniel Stone --- libweston/compositor.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/libweston/compositor.c b/libweston/compositor.c index 8d20305d..36b6b9db 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -7501,22 +7501,37 @@ debug_scene_view_print_buffer(FILE *fp, struct weston_view *view) case WESTON_BUFFER_DMABUF: fprintf(fp, "\t\tdmabuf buffer\n"); break; - default: + case WESTON_BUFFER_RENDERER_OPAQUE: fprintf(fp, "\t\tEGL buffer:\n"); fprintf(fp, "\t\t\t[format may be inaccurate]\n"); break; } - fprintf(fp, "\t\t\tformat: 0x%lx %s\n", - (unsigned long) buffer->pixel_format, - buffer->pixel_format ? - buffer->pixel_format->drm_format_name : "UNKNOWN"); + if (buffer->busy_count > 0) { + fprintf(fp, "\t\t\t[%d references may use buffer content]\n", + buffer->busy_count); + } else { + fprintf(fp, "\t\t\t[buffer has been released to client]\n"); + } + + if (buffer->pixel_format) { + fprintf(fp, "\t\t\tformat: 0x%lx %s\n", + (unsigned long) buffer->pixel_format->format, + buffer->pixel_format->drm_format_name); + } else { + fprintf(fp, "\t\t\t[unknown format]\n"); + } modifier_name = pixel_format_get_modifier(buffer->format_modifier); fprintf(fp, "\t\t\tmodifier: %s\n", modifier_name ? modifier_name : "Failed to convert to a modifier name"); free(modifier_name); + + fprintf(fp, "\t\t\twidth: %d, height: %d\n", + buffer->width, buffer->height); + if (buffer->buffer_origin == ORIGIN_BOTTOM_LEFT) + fprintf(fp, "\t\t\tbottom-left origin\n"); } static void From 2dcfe723be7178d314176a4968ea9131e207279d Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 14 Jan 2022 02:09:32 +0000 Subject: [PATCH 154/609] backend-drm: Make use of weston_buffer format and type Just pull it from the structure rather than pulling it in externally. Signed-off-by: Daniel Stone --- libweston/backend-drm/state-propose.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index 953e3a9f..8e6c0ed8 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -169,7 +169,6 @@ cursor_bo_update(struct drm_plane_state *plane_state, struct weston_view *ev) int i; assert(buffer && buffer->shm_buffer); - assert(buffer->shm_buffer == wl_shm_buffer_get(buffer->resource)); assert(buffer->width <= b->cursor_width); assert(buffer->height <= b->cursor_height); @@ -432,7 +431,6 @@ drm_output_find_plane_for_view(struct drm_output_state *state, struct weston_view *ev = pnode->view; struct weston_buffer *buffer; - struct wl_shm_buffer *shmbuf; struct drm_fb *fb = NULL; bool view_matches_entire_output, scanout_has_view_assigned; @@ -448,15 +446,16 @@ drm_output_find_plane_for_view(struct drm_output_state *state, } buffer = ev->surface->buffer_ref.buffer; - shmbuf = wl_shm_buffer_get(buffer->resource); - if (shmbuf) { + if (buffer->type == WESTON_BUFFER_SHM) { if (!output->cursor_plane || b->cursors_are_broken) { pnode->try_view_on_plane_failure_reasons |= FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE; return NULL; } - if (wl_shm_buffer_get_format(shmbuf) != WL_SHM_FORMAT_ARGB8888) { + /* Even though this is a SHM buffer, pixel_format stores the + * format code as DRM FourCC */ + if (buffer->pixel_format->format != DRM_FORMAT_ARGB8888) { drm_debug(b, "\t\t\t\t[view] not placing view %p on " "plane; SHM buffers must be ARGB8888 for " "cursor view\n", ev); @@ -512,7 +511,7 @@ drm_output_find_plane_for_view(struct drm_output_state *state, switch (plane->type) { case WDRM_PLANE_TYPE_CURSOR: - assert(shmbuf); + assert(buffer->shm_buffer); assert(plane == output->cursor_plane); break; case WDRM_PLANE_TYPE_PRIMARY: @@ -959,7 +958,7 @@ drm_assign_planes(struct weston_output *output_base) */ if (b->use_pixman || (weston_view_has_valid_buffer(ev) && - (!wl_shm_buffer_get(ev->surface->buffer_ref.buffer->resource) || + (ev->surface->buffer_ref.buffer->type != WESTON_BUFFER_SHM || (ev->surface->width <= b->cursor_width && ev->surface->height <= b->cursor_height)))) ev->surface->keep_buffer = true; From 231a67ff8c6718c4bef4ddea47d32511b90bcc89 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Mon, 17 Jan 2022 18:48:24 +0000 Subject: [PATCH 155/609] drm-backend: Refactor unpleasant keep_buffer if tree Break a giant if statement out into a more legible grouping. Signed-off-by: Daniel Stone --- libweston/backend-drm/state-propose.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index 8e6c0ed8..f14631c6 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -956,14 +956,20 @@ drm_assign_planes(struct weston_output *output_base) * renderer and since the pixman renderer keeps a reference * to the buffer anyway, there is no side effects. */ - if (b->use_pixman || - (weston_view_has_valid_buffer(ev) && - (ev->surface->buffer_ref.buffer->type != WESTON_BUFFER_SHM || - (ev->surface->width <= b->cursor_width && - ev->surface->height <= b->cursor_height)))) - ev->surface->keep_buffer = true; - else - ev->surface->keep_buffer = false; + ev->surface->keep_buffer = false; + if (weston_view_has_valid_buffer(ev)) { + struct weston_buffer *buffer = + ev->surface->buffer_ref.buffer; + if (b->use_pixman) + ev->surface->keep_buffer = true; + else if (buffer->type == WESTON_BUFFER_DMABUF || + buffer->type == WESTON_BUFFER_RENDERER_OPAQUE) + ev->surface->keep_buffer = true; + else if (buffer->type == WESTON_BUFFER_SHM && + (ev->surface->width <= b->cursor_width && + ev->surface->height <= b->cursor_height)) + ev->surface->keep_buffer = true; + } /* This is a bit unpleasant, but lacking a temporary place to * hang a plane off the view, we have to do a nested walk. From e9c792ed643e2681b748681175732c42f4ecdfda Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 21 Jan 2022 18:50:05 +0000 Subject: [PATCH 156/609] backend-drm: More failure reasons Signed-off-by: Daniel Stone --- libweston/backend-drm/drm-internal.h | 18 ++++++++++++------ libweston/backend-drm/fb.c | 26 +++++++++++++++++++++----- libweston/backend-drm/state-propose.c | 5 ++++- 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index df704926..f9d21e3b 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -232,12 +232,18 @@ enum wdrm_crtc_property { */ enum try_view_on_plane_failure_reasons { FAILURE_REASONS_NONE = 0, - FAILURE_REASONS_FORCE_RENDERER = (1 << 0), - FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE = (1 << 1), - FAILURE_REASONS_DMABUF_MODIFIER_INVALID = (1 << 2), - FAILURE_REASONS_ADD_FB_FAILED = (1 << 3), - FAILURE_REASONS_NO_PLANES_AVAILABLE = (1 << 4), - FAILURE_REASONS_PLANES_REJECTED = (1 << 5), + FAILURE_REASONS_FORCE_RENDERER = 1 << 0, + FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE = 1 << 1, + FAILURE_REASONS_DMABUF_MODIFIER_INVALID = 1 << 2, + FAILURE_REASONS_ADD_FB_FAILED = 1 << 3, + FAILURE_REASONS_NO_PLANES_AVAILABLE = 1 << 4, + FAILURE_REASONS_PLANES_REJECTED = 1 << 5, + FAILURE_REASONS_INADEQUATE_CONTENT_PROTECTION = 1 << 6, + FAILURE_REASONS_INCOMPATIBLE_TRANSFORM = 1 << 7, + FAILURE_REASONS_NO_BUFFER = 1 << 8, + FAILURE_REASONS_BUFFER_TYPE = 1 << 9, + FAILURE_REASONS_GLOBAL_ALPHA = 1 << 10, + FAILURE_REASONS_NO_GBM = 1 << 11, }; /** diff --git a/libweston/backend-drm/fb.c b/libweston/backend-drm/fb.c index 718095c9..e563b52e 100644 --- a/libweston/backend-drm/fb.c +++ b/libweston/backend-drm/fb.c @@ -525,18 +525,29 @@ drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev, struct drm_fb *fb; struct drm_plane *plane; - if (ev->alpha != 1.0f) + if (ev->alpha != 1.0f) { + *try_view_on_plane_failure_reasons |= + FAILURE_REASONS_GLOBAL_ALPHA; return NULL; + } - if (!drm_view_transform_supported(ev, &output->base)) + if (!drm_view_transform_supported(ev, &output->base)) { + *try_view_on_plane_failure_reasons |= + FAILURE_REASONS_INCOMPATIBLE_TRANSFORM; return NULL; + } if (ev->surface->protection_mode == WESTON_SURFACE_PROTECTION_MODE_ENFORCED && - ev->surface->desired_protection > output->base.current_protection) + ev->surface->desired_protection > output->base.current_protection) { + *try_view_on_plane_failure_reasons |= + FAILURE_REASONS_INADEQUATE_CONTENT_PROTECTION; return NULL; + } - if (!buffer) + if (!buffer) { + *try_view_on_plane_failure_reasons |= FAILURE_REASONS_NO_BUFFER; return NULL; + } if (buffer->backend_private) { buf_fb = buffer->backend_private; @@ -550,8 +561,10 @@ drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev, wl_signal_add(&buffer->destroy_signal, &buf_fb->buffer_destroy_listener); /* GBM is used for dmabuf import as well as from client wl_buffer. */ - if (!b->gbm) + if (!b->gbm) { + *try_view_on_plane_failure_reasons |= FAILURE_REASONS_NO_GBM; goto unsuitable; + } if (buffer->type == WESTON_BUFFER_DMABUF) { fb = drm_fb_get_from_dmabuf(buffer->dmabuf, b, is_opaque, @@ -568,10 +581,13 @@ drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev, fb = drm_fb_get_from_bo(bo, b, is_opaque, BUFFER_CLIENT); if (!fb) { + *try_view_on_plane_failure_reasons |= + (1 << FAILURE_REASONS_ADD_FB_FAILED); gbm_bo_destroy(bo); goto unsuitable; } } else { + *try_view_on_plane_failure_reasons |= FAILURE_REASONS_BUFFER_TYPE; goto unsuitable; } diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index f14631c6..5acaa809 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -484,8 +484,11 @@ drm_output_find_plane_for_view(struct drm_output_state *state, fb = drm_fb_get_from_view(state, ev, &pnode->try_view_on_plane_failure_reasons); - if (!fb) + if (!fb) { + drm_debug(b, "\t\t\t[view] couldn't get FB for view: 0x%lx\n", + (unsigned long) pnode->try_view_on_plane_failure_reasons); return NULL; + } possible_plane_mask = fb->plane_mask; } From 7b3efabd88f7a2e95287f4f074eba9ac7ff59ede Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 14 Jan 2022 02:33:03 +0000 Subject: [PATCH 157/609] weston_buffer: Prepare for buffer to outlive resource We currently allow a weston_buffer to outlive the underlying wl_buffer iff the renderer/backend has cached it. Currently the 'is this buffer valid?' test relies on looking for the validity of the weston_buffer itself; shift these tests to looking at the validity of the underlying resource. Signed-off-by: Daniel Stone --- libweston/compositor.c | 6 +++++- libweston/data-device.c | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/libweston/compositor.c b/libweston/compositor.c index 36b6b9db..afb8556d 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -1983,7 +1983,11 @@ weston_view_is_opaque(struct weston_view *ev, pixman_region32_t *region) WL_EXPORT bool weston_view_has_valid_buffer(struct weston_view *ev) { - return ev->surface->buffer_ref.buffer != NULL; + if (!ev->surface->buffer_ref.buffer) + return false; + if (!ev->surface->buffer_ref.buffer->resource) + return false; + return true; } /** Check if the view matches the entire output diff --git a/libweston/data-device.c b/libweston/data-device.c index fe997ba8..bc2661c0 100644 --- a/libweston/data-device.c +++ b/libweston/data-device.c @@ -420,6 +420,7 @@ drag_surface_configure(struct weston_drag *drag, assert((pointer != NULL && touch == NULL) || (pointer == NULL && touch != NULL)); + /* XXX: Why are we checking for a valid buffer here too ... ? */ if (!weston_surface_is_mapped(es) && es->buffer_ref.buffer) { if (pointer && pointer->sprite && weston_view_is_mapped(pointer->sprite)) From d2a858e879028db8065e8210e6c932c09a452a43 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 2 Feb 2022 16:51:45 +0000 Subject: [PATCH 158/609] gl-renderer: Prepare for buffer to outlive resource Make sure we don't die if we're asked to flush the damage on a SHM buffer which has subsequently been destroyed. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 99d80df6..aef37499 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -1849,6 +1849,12 @@ gl_renderer_flush_damage(struct weston_surface *surface, pixman_region32_union(&gs->texture_damage, &gs->texture_damage, &surface->damage); + /* This can happen if a SHM wl_buffer gets destroyed before we flush + * damage, because wayland-server just nukes the wl_shm_buffer from + * underneath us */ + if (!buffer->shm_buffer) + return; + /* Avoid upload, if the texture won't be used this time. * We still accumulate the damage in texture_damage, and * hold the reference to the buffer, in case the surface From 7e90433079e08aafd560810817e60c46485a4bd2 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 14 Jan 2022 06:33:21 +0000 Subject: [PATCH 159/609] weston_buffer: Hold lifetime for resource/backend usage Keep the weston_buffer alive for as long as at least one of the underlying wl_buffer or a backend usage exists. Signed-off-by: Daniel Stone --- include/libweston/libweston.h | 1 - libweston/compositor.c | 43 ++++++++++++++++------------------- 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 3576426f..1fe3e4a5 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -1203,7 +1203,6 @@ struct weston_buffer { struct weston_buffer_reference { struct weston_buffer *buffer; - struct wl_listener destroy_listener; }; struct weston_buffer_viewport { diff --git a/libweston/compositor.c b/libweston/compositor.c index afb8556d..8561eed0 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -2385,6 +2385,12 @@ weston_buffer_destroy_handler(struct wl_listener *listener, void *data) struct weston_buffer *buffer = container_of(listener, struct weston_buffer, destroy_listener); + buffer->resource = NULL; + buffer->shm_buffer = NULL; + + if (buffer->busy_count > 0) + return; + weston_signal_emit_mutable(&buffer->destroy_signal, buffer); free(buffer); } @@ -2464,39 +2470,30 @@ fail: return NULL; } -static void -weston_buffer_reference_handle_destroy(struct wl_listener *listener, - void *data) -{ - struct weston_buffer_reference *ref = - container_of(listener, struct weston_buffer_reference, - destroy_listener); - - assert((struct weston_buffer *)data == ref->buffer); - ref->buffer = NULL; -} - WL_EXPORT void weston_buffer_reference(struct weston_buffer_reference *ref, struct weston_buffer *buffer) { - if (ref->buffer && buffer != ref->buffer) { - ref->buffer->busy_count--; - if (ref->buffer->busy_count == 0) { + if (buffer == ref->buffer) + return; + + if (ref->buffer && --ref->buffer->busy_count == 0) { + if (ref->buffer->resource) { assert(wl_resource_get_client(ref->buffer->resource)); wl_buffer_send_release(ref->buffer->resource); + } else { + weston_signal_emit_mutable(&ref->buffer->destroy_signal, + ref->buffer); + free(ref->buffer); } - wl_list_remove(&ref->destroy_listener.link); - } - - if (buffer && buffer != ref->buffer) { - buffer->busy_count++; - wl_signal_add(&buffer->destroy_signal, - &ref->destroy_listener); } ref->buffer = buffer; - ref->destroy_listener.notify = weston_buffer_reference_handle_destroy; + + if (!ref->buffer) + return; + + ref->buffer->busy_count++; } static void From fdc7b9c352c1089c5823b568c1a31cbc91fb251d Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Sat, 15 Jan 2022 03:12:53 +0000 Subject: [PATCH 160/609] weston_buffer: Add mode to weston_buffer_reference Add a mode argument to weston_buffer_reference which indicates whether a buffer's storage may/will be accessed, or whether the underlying storage will no longer be accessed, e.g. because it has been copied. This will be used to retain a pointer to the weston_buffer whilst being able to send a release event to the client. Signed-off-by: Daniel Stone --- include/libweston/libweston.h | 7 +++++++ libweston/backend-drm/state-helpers.c | 17 ++++++++++++++--- libweston/backend-drm/state-propose.c | 3 ++- libweston/compositor.c | 27 ++++++++++++++++++++------- libweston/libweston-internal.h | 3 ++- libweston/pixman-renderer.c | 13 +++++++++---- libweston/renderer-gl/gl-renderer.c | 13 +++++++++---- 7 files changed, 63 insertions(+), 20 deletions(-) diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 1fe3e4a5..2f60dc8e 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -1201,8 +1201,15 @@ struct weston_buffer { uint64_t format_modifier; }; +enum weston_buffer_reference_type { + BUFFER_REF_NONE, + BUFFER_MAY_BE_ACCESSED, + BUFFER_WILL_NOT_BE_ACCESSED, +}; + struct weston_buffer_reference { struct weston_buffer *buffer; + enum weston_buffer_reference_type type; }; struct weston_buffer_viewport { diff --git a/libweston/backend-drm/state-helpers.c b/libweston/backend-drm/state-helpers.c index 75a2a18c..245ee087 100644 --- a/libweston/backend-drm/state-helpers.c +++ b/libweston/backend-drm/state-helpers.c @@ -93,7 +93,8 @@ drm_plane_state_free(struct drm_plane_state *state, bool force) if (force || state != state->plane->state_cur) { drm_fb_unref(state->fb); - weston_buffer_reference(&state->fb_ref.buffer, NULL); + weston_buffer_reference(&state->fb_ref.buffer, NULL, + BUFFER_WILL_NOT_BE_ACCESSED); weston_buffer_release_reference(&state->fb_ref.release, NULL); free(state); } @@ -135,10 +136,20 @@ drm_plane_state_duplicate(struct drm_output_state *state_output, * buffer, then we must also transfer the reference on the client * buffer. */ if (src->fb) { + struct weston_buffer *buffer; + dst->fb = drm_fb_ref(src->fb); memset(&dst->fb_ref, 0, sizeof(dst->fb_ref)); - weston_buffer_reference(&dst->fb_ref.buffer, - src->fb_ref.buffer.buffer); + + if (src->fb->type == BUFFER_CLIENT || + src->fb->type == BUFFER_DMABUF) { + buffer = src->fb_ref.buffer.buffer; + } else { + buffer = NULL; + } + weston_buffer_reference(&dst->fb_ref.buffer, buffer, + buffer ? BUFFER_MAY_BE_ACCESSED : + BUFFER_WILL_NOT_BE_ACCESSED); weston_buffer_release_reference(&dst->fb_ref.release, src->fb_ref.release.buffer_release); } else { diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index 5acaa809..799d4825 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -139,7 +139,8 @@ drm_output_try_view_on_plane(struct drm_plane *plane, assert(state->fb_ref.buffer.buffer == NULL); assert(state->fb_ref.release.buffer_release == NULL); weston_buffer_reference(&state->fb_ref.buffer, - surface->buffer_ref.buffer); + surface->buffer_ref.buffer, + BUFFER_MAY_BE_ACCESSED); weston_buffer_release_reference(&state->fb_ref.release, surface->buffer_release_ref.buffer_release); diff --git a/libweston/compositor.c b/libweston/compositor.c index 8561eed0..62eff5f1 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -2334,7 +2334,8 @@ weston_surface_destroy(struct weston_surface *surface) weston_surface_state_fini(&surface->pending); - weston_buffer_reference(&surface->buffer_ref, NULL); + weston_buffer_reference(&surface->buffer_ref, NULL, + BUFFER_WILL_NOT_BE_ACCESSED); weston_buffer_release_reference(&surface->buffer_release_ref, NULL); pixman_region32_fini(&surface->damage); @@ -2472,8 +2473,11 @@ fail: WL_EXPORT void weston_buffer_reference(struct weston_buffer_reference *ref, - struct weston_buffer *buffer) + struct weston_buffer *buffer, + enum weston_buffer_reference_type type) { + assert(buffer != NULL || type == BUFFER_WILL_NOT_BE_ACCESSED); + if (buffer == ref->buffer) return; @@ -2562,7 +2566,9 @@ static void weston_surface_attach(struct weston_surface *surface, struct weston_buffer *buffer) { - weston_buffer_reference(&surface->buffer_ref, buffer); + weston_buffer_reference(&surface->buffer_ref, buffer, + buffer ? BUFFER_MAY_BE_ACCESSED : + BUFFER_WILL_NOT_BE_ACCESSED); if (!buffer) { if (weston_surface_is_mapped(surface)) @@ -2699,7 +2705,9 @@ output_accumulate_damage(struct weston_output *output) * clients to use single-buffering. */ if (!pnode->surface->keep_buffer) { - weston_buffer_reference(&pnode->surface->buffer_ref, NULL); + weston_buffer_reference(&pnode->surface->buffer_ref, + NULL, + BUFFER_WILL_NOT_BE_ACCESSED); weston_buffer_release_reference( &pnode->surface->buffer_release_ref, NULL); } @@ -4226,7 +4234,8 @@ weston_subsurface_commit_from_cache(struct weston_subsurface *sub) struct weston_surface *surface = sub->surface; weston_surface_commit_state(surface, &sub->cached); - weston_buffer_reference(&sub->cached_buffer_ref, NULL); + weston_buffer_reference(&sub->cached_buffer_ref, NULL, + BUFFER_WILL_NOT_BE_ACCESSED); weston_surface_commit_subsurface_order(surface); @@ -4263,7 +4272,10 @@ weston_subsurface_commit_to_cache(struct weston_subsurface *sub) weston_surface_state_set_buffer(&sub->cached, surface->pending.buffer); weston_buffer_reference(&sub->cached_buffer_ref, - surface->pending.buffer); + surface->pending.buffer, + surface->pending.buffer ? + BUFFER_MAY_BE_ACCESSED : + BUFFER_WILL_NOT_BE_ACCESSED); weston_presentation_feedback_discard_list( &sub->cached.feedback_list); /* zwp_surface_synchronization_v1.set_acquire_fence */ @@ -4845,7 +4857,8 @@ weston_subsurface_destroy(struct weston_subsurface *sub) weston_subsurface_unlink_parent(sub); weston_surface_state_fini(&sub->cached); - weston_buffer_reference(&sub->cached_buffer_ref, NULL); + weston_buffer_reference(&sub->cached_buffer_ref, NULL, + BUFFER_WILL_NOT_BE_ACCESSED); sub->surface->committed = NULL; sub->surface->committed_private = NULL; diff --git a/libweston/libweston-internal.h b/libweston/libweston-internal.h index 02c4ad3b..c40ba12c 100644 --- a/libweston/libweston-internal.h +++ b/libweston/libweston-internal.h @@ -50,7 +50,8 @@ weston_buffer_send_server_error(struct weston_buffer *buffer, const char *msg); void weston_buffer_reference(struct weston_buffer_reference *ref, - struct weston_buffer *buffer); + struct weston_buffer *buffer, + enum weston_buffer_reference_type type); void weston_buffer_release_move(struct weston_buffer_release_reference *dest, diff --git a/libweston/pixman-renderer.c b/libweston/pixman-renderer.c index be2df1d5..32d28349 100644 --- a/libweston/pixman-renderer.c +++ b/libweston/pixman-renderer.c @@ -627,7 +627,9 @@ pixman_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) struct wl_shm_buffer *shm_buffer; const struct pixel_format_info *pixel_info; - weston_buffer_reference(&ps->buffer_ref, buffer); + weston_buffer_reference(&ps->buffer_ref, buffer, + buffer ? BUFFER_MAY_BE_ACCESSED : + BUFFER_WILL_NOT_BE_ACCESSED); weston_buffer_release_reference(&ps->buffer_release_ref, es->buffer_release_ref.buffer_release); @@ -646,7 +648,8 @@ pixman_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) if (buffer->type != WESTON_BUFFER_SHM) { weston_log("Pixman renderer supports only SHM buffers\n"); - weston_buffer_reference(&ps->buffer_ref, NULL); + weston_buffer_reference(&ps->buffer_ref, NULL, + BUFFER_WILL_NOT_BE_ACCESSED); weston_buffer_release_reference(&ps->buffer_release_ref, NULL); return; } @@ -657,7 +660,8 @@ pixman_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) if (!pixel_info || !pixman_format_supported_source(pixel_info->pixman_format)) { weston_log("Unsupported SHM buffer format 0x%x\n", wl_shm_buffer_get_format(shm_buffer)); - weston_buffer_reference(&ps->buffer_ref, NULL); + weston_buffer_reference(&ps->buffer_ref, NULL, + BUFFER_WILL_NOT_BE_ACCESSED); weston_buffer_release_reference(&ps->buffer_release_ref, NULL); weston_buffer_send_server_error(buffer, "disconnecting due to unhandled buffer type"); @@ -693,7 +697,8 @@ pixman_renderer_surface_state_destroy(struct pixman_surface_state *ps) pixman_image_unref(ps->image); ps->image = NULL; } - weston_buffer_reference(&ps->buffer_ref, NULL); + weston_buffer_reference(&ps->buffer_ref, NULL, + BUFFER_WILL_NOT_BE_ACCESSED); weston_buffer_release_reference(&ps->buffer_release_ref, NULL); free(ps); } diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index aef37499..b442caaf 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -1931,7 +1931,8 @@ done: pixman_region32_init(&gs->texture_damage); gs->needs_full_upload = false; - weston_buffer_reference(&gs->buffer_ref, NULL); + weston_buffer_reference(&gs->buffer_ref, buffer, + BUFFER_WILL_NOT_BE_ACCESSED); weston_buffer_release_reference(&gs->buffer_release_ref, NULL); } @@ -2971,7 +2972,9 @@ gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) EGLint format; int i; - weston_buffer_reference(&gs->buffer_ref, buffer); + weston_buffer_reference(&gs->buffer_ref, buffer, + buffer ? BUFFER_MAY_BE_ACCESSED : + BUFFER_WILL_NOT_BE_ACCESSED); weston_buffer_release_reference(&gs->buffer_release_ref, es->buffer_release_ref.buffer_release); @@ -3015,7 +3018,8 @@ gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) weston_log("eglQueryWaylandBufferWL failed\n"); gl_renderer_print_egl_error_state(); } - weston_buffer_reference(&gs->buffer_ref, NULL); + weston_buffer_reference(&gs->buffer_ref, NULL, + BUFFER_WILL_NOT_BE_ACCESSED); weston_buffer_release_reference(&gs->buffer_release_ref, NULL); gs->buffer_type = BUFFER_TYPE_NULL; gs->y_inverted = true; @@ -3199,7 +3203,8 @@ surface_state_destroy(struct gl_surface_state *gs, struct gl_renderer *gr) for (i = 0; i < gs->num_images; i++) egl_image_unref(gs->images[i]); - weston_buffer_reference(&gs->buffer_ref, NULL); + weston_buffer_reference(&gs->buffer_ref, NULL, + BUFFER_WILL_NOT_BE_ACCESSED); weston_buffer_release_reference(&gs->buffer_release_ref, NULL); pixman_region32_fini(&gs->texture_damage); free(gs); From a42908204f86d7afcbcf0ebe9e7acb39576982bf Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Sat, 15 Jan 2022 02:12:32 +0000 Subject: [PATCH 161/609] weston_buffer: Separate buffer release from lifetime In the original conception, a weston_buffer_reference indicated that the underlying contents of the wl_buffer would or could be accessed, so wl_buffer.release must not be sent until the last reference was released, as the compositor may still use it. This meant that renderers or backends which copied the buffer content - such as the GL renderer with SHM buffers - could only send a buffer release event to the client by 'losing' the buffer reference altogether. The main side effect is that `weston-debug scene-graph` could not show any information at all about SHM buffers when using the GL renderer, but it also meant that renderers and backends grew increasingly exotic structures to cache information about the buffer. Now that we have an additional buffer-reference mode (still referring to the weston_buffer/wl_buffer, but not going to access its content), we can allow the weston_buffer_reference and weston_buffer to live as long as the buffer itself, even if we do send a release event. This will enable a bunch of backend and renderer deduplication, as well as finally making scene-graph more useful. Signed-off-by: Daniel Stone --- include/libweston/libweston.h | 1 + libweston/compositor.c | 54 ++++++++++++++++++++++++++--------- 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 2f60dc8e..bfa592d0 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -1191,6 +1191,7 @@ struct weston_buffer { int32_t width, height; uint32_t busy_count; + uint32_t passive_count; enum { ORIGIN_TOP_LEFT, /* buffer content starts at (0,0) */ ORIGIN_BOTTOM_LEFT, /* buffer content starts at (0, height) */ diff --git a/libweston/compositor.c b/libweston/compositor.c index 62eff5f1..179fe5be 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -2389,7 +2389,7 @@ weston_buffer_destroy_handler(struct wl_listener *listener, void *data) buffer->resource = NULL; buffer->shm_buffer = NULL; - if (buffer->busy_count > 0) + if (buffer->busy_count + buffer->passive_count > 0) return; weston_signal_emit_mutable(&buffer->destroy_signal, buffer); @@ -2476,28 +2476,56 @@ weston_buffer_reference(struct weston_buffer_reference *ref, struct weston_buffer *buffer, enum weston_buffer_reference_type type) { + struct weston_buffer_reference old_ref = *ref; + assert(buffer != NULL || type == BUFFER_WILL_NOT_BE_ACCESSED); - if (buffer == ref->buffer) + if (buffer == ref->buffer && type == ref->type) return; - if (ref->buffer && --ref->buffer->busy_count == 0) { - if (ref->buffer->resource) { - assert(wl_resource_get_client(ref->buffer->resource)); - wl_buffer_send_release(ref->buffer->resource); - } else { - weston_signal_emit_mutable(&ref->buffer->destroy_signal, - ref->buffer); - free(ref->buffer); - } + /* First ref the incoming buffer, so we keep positive refcount */ + if (buffer) { + if (type == BUFFER_MAY_BE_ACCESSED) + buffer->busy_count++; + else + buffer->passive_count++; } ref->buffer = buffer; + ref->type = type; - if (!ref->buffer) + /* Now drop refs to the old buffer, if any */ + if (!old_ref.buffer) return; - ref->buffer->busy_count++; + ref = NULL; /* will no longer be accessed */ + + if (old_ref.type == BUFFER_MAY_BE_ACCESSED) { + assert(old_ref.buffer->busy_count > 0); + old_ref.buffer->busy_count--; + + /* If the wl_buffer lives, then hold on to the weston_buffer, + * but send a release event to the client */ + if (old_ref.buffer->busy_count == 0 && + old_ref.buffer->resource) { + assert(wl_resource_get_client(old_ref.buffer->resource)); + wl_buffer_send_release(old_ref.buffer->resource); + } + } else if (old_ref.type == BUFFER_WILL_NOT_BE_ACCESSED) { + assert(old_ref.buffer->passive_count > 0); + old_ref.buffer->passive_count--; + } else { + assert(!"unknown buffer ref type"); + } + + /* If the wl_buffer has gone and this was the last ref, destroy the + * weston_buffer, since we'll never need it again */ + if (old_ref.buffer->busy_count + old_ref.buffer->passive_count == 0 && + !old_ref.buffer->resource) { + weston_signal_emit_mutable(&old_ref.buffer->destroy_signal, + old_ref.buffer); + free(old_ref.buffer); + } } static void From 7a27f6cbe4480c777932964c309f97c84246d56a Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Sat, 15 Jan 2022 02:18:32 +0000 Subject: [PATCH 162/609] compositor: Downgrade rather than drop buffer reference when copied When the renderer/backend indicate that they do not need a surface's buffer content to be preserved, most often because they have copied it, simply downgrade the buffer reference to 'will not access', rather than drop the buffer reference altogether. Signed-off-by: Daniel Stone --- libweston/compositor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libweston/compositor.c b/libweston/compositor.c index 179fe5be..ce6b5266 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -2734,7 +2734,7 @@ output_accumulate_damage(struct weston_output *output) */ if (!pnode->surface->keep_buffer) { weston_buffer_reference(&pnode->surface->buffer_ref, - NULL, + pnode->surface->buffer_ref.buffer, BUFFER_WILL_NOT_BE_ACCESSED); weston_buffer_release_reference( &pnode->surface->buffer_release_ref, NULL); From 43715ff0c05d7c89f07a7b267e78aeb48791617b Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Sat, 15 Jan 2022 17:47:35 +0000 Subject: [PATCH 163/609] weston_buffer: Add solid buffer type Currently solid-colour displays (e.g. the background for fullscreen views) is implemented by a special-case weston_surface which has no buffer attached, with a special punch-through renderer callback to set the colour. Replace this with a weston_buffer type explicitly specifying the solid colour, which helps us eliminate yet more special cases in renderers and backends. This is not handled yet in any renderer or backend, however it is also not used by anything yet. Following commits add support to the renderers and backends. Signed-off-by: Daniel Stone --- include/libweston/libweston.h | 11 +++++++ libweston/compositor.c | 56 +++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index bfa592d0..52e3bac2 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -1181,12 +1181,16 @@ struct weston_buffer { WESTON_BUFFER_SHM, WESTON_BUFFER_DMABUF, WESTON_BUFFER_RENDERER_OPAQUE, + WESTON_BUFFER_SOLID, } type; union { struct wl_shm_buffer *shm_buffer; void *dmabuf; void *legacy_buffer; + struct { + float r, g, b, a; + } solid; }; int32_t width, height; @@ -1768,6 +1772,13 @@ weston_surface_create(struct weston_compositor *compositor); struct weston_view * weston_view_create(struct weston_surface *surface); +struct weston_buffer_reference * +weston_buffer_create_solid_rgba(struct weston_compositor *compositor, + float r, float g, float b, float a); + +void +weston_buffer_destroy_solid(struct weston_buffer_reference *buffer_ref); + void weston_view_destroy(struct weston_view *view); diff --git a/libweston/compositor.c b/libweston/compositor.c index ce6b5266..bb41881b 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -2590,6 +2590,56 @@ weston_buffer_release_move(struct weston_buffer_release_reference *dest, weston_buffer_release_reference(src, NULL); } +WL_EXPORT struct weston_buffer_reference * +weston_buffer_create_solid_rgba(struct weston_compositor *compositor, + float r, float g, float b, float a) +{ + struct weston_buffer_reference *ret = zalloc(sizeof(*ret)); + + if (!ret) + return NULL; + + ret->buffer = zalloc(sizeof(*ret->buffer)); + if (!ret->buffer) { + free(ret); + return NULL; + } + + wl_signal_init(&ret->buffer->destroy_signal); + ret->buffer->type = WESTON_BUFFER_SOLID; + ret->buffer->width = 1; + ret->buffer->height = 1; + ret->buffer->buffer_origin = ORIGIN_TOP_LEFT; + ret->buffer->solid.r = r; + ret->buffer->solid.g = g; + ret->buffer->solid.b = b; + ret->buffer->solid.a = a; + + if (a == 1.0) { + ret->buffer->pixel_format = + pixel_format_get_info_shm(WL_SHM_FORMAT_XRGB8888); + } else { + ret->buffer->pixel_format = + pixel_format_get_info_shm(WL_SHM_FORMAT_ARGB8888); + } + ret->buffer->format_modifier = DRM_FORMAT_MOD_LINEAR; + + weston_buffer_reference(ret, ret->buffer, BUFFER_MAY_BE_ACCESSED); + + return ret; +} + +WL_EXPORT void +weston_buffer_destroy_solid(struct weston_buffer_reference *buffer_ref) +{ + assert(buffer_ref); + assert(buffer_ref->buffer); + assert(buffer_ref->type == BUFFER_MAY_BE_ACCESSED); + assert(buffer_ref->buffer->type == WESTON_BUFFER_SOLID); + weston_buffer_reference(buffer_ref, NULL, BUFFER_WILL_NOT_BE_ACCESSED); + free(buffer_ref); +} + static void weston_surface_attach(struct weston_surface *surface, struct weston_buffer *buffer) @@ -7543,6 +7593,12 @@ debug_scene_view_print_buffer(FILE *fp, struct weston_view *view) case WESTON_BUFFER_DMABUF: fprintf(fp, "\t\tdmabuf buffer\n"); break; + case WESTON_BUFFER_SOLID: + fprintf(fp, "\t\tsolid-colour buffer\n"); + fprintf(fp, "\t\t\t[R %f, G %f, B %f, A %f]\n", + buffer->solid.r, buffer->solid.g, buffer->solid.b, + buffer->solid.a); + break; case WESTON_BUFFER_RENDERER_OPAQUE: fprintf(fp, "\t\tEGL buffer:\n"); fprintf(fp, "\t\t\t[format may be inaccurate]\n"); From 465f4a250cbe1d2debd70f889085950b9efbf15e Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Sat, 15 Jan 2022 17:51:25 +0000 Subject: [PATCH 164/609] gl-renderer: Support solid-colour weston_buffers Same as surface_set_color. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 44 ++++++++++++++++++----------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index b442caaf..557c0c1d 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -2963,6 +2963,23 @@ out: return ret; } +static void +gl_renderer_surface_set_color(struct weston_surface *surface, + float red, float green, float blue, float alpha) +{ + struct gl_surface_state *gs = get_surface_state(surface); + + gs->color[0] = red; + gs->color[1] = green; + gs->color[2] = blue; + gs->color[3] = alpha; + gs->buffer_type = BUFFER_TYPE_SOLID; + gs->pitch = 1; + gs->height = 1; + + gs->shader_variant = SHADER_VARIANT_SOLID; +} + static void gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) { @@ -3009,6 +3026,16 @@ gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) } gl_renderer_attach_egl(es, buffer, format); return; + case WESTON_BUFFER_SOLID: + gl_renderer_surface_set_color(es, + buffer->solid.r, + buffer->solid.g, + buffer->solid.b, + buffer->solid.a); + weston_buffer_reference(&gs->buffer_ref, NULL, + BUFFER_WILL_NOT_BE_ACCESSED); + weston_buffer_release_reference(&gs->buffer_release_ref, NULL); + return; default: break; } @@ -3028,23 +3055,6 @@ gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) "disconnecting due to unhandled buffer type"); } -static void -gl_renderer_surface_set_color(struct weston_surface *surface, - float red, float green, float blue, float alpha) -{ - struct gl_surface_state *gs = get_surface_state(surface); - - gs->color[0] = red; - gs->color[1] = green; - gs->color[2] = blue; - gs->color[3] = alpha; - gs->buffer_type = BUFFER_TYPE_SOLID; - gs->pitch = 1; - gs->height = 1; - - gs->shader_variant = SHADER_VARIANT_SOLID; -} - static void gl_renderer_surface_get_content_size(struct weston_surface *surface, int *width, int *height) From d82d74e71378ef3672e93d5cf6f28a7f25e02c42 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Sat, 15 Jan 2022 17:56:15 +0000 Subject: [PATCH 165/609] pixman-renderer: Support solid-colour weston_buffers Just implemented via the same mechanism as surface_set_color. Signed-off-by: Daniel Stone --- libweston/pixman-renderer.c | 52 +++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/libweston/pixman-renderer.c b/libweston/pixman-renderer.c index 32d28349..361ffa61 100644 --- a/libweston/pixman-renderer.c +++ b/libweston/pixman-renderer.c @@ -620,6 +620,26 @@ buffer_state_handle_buffer_destroy(struct wl_listener *listener, void *data) ps->buffer_destroy_listener.notify = NULL; } +static void +pixman_renderer_surface_set_color(struct weston_surface *es, + float red, float green, float blue, float alpha) +{ + struct pixman_surface_state *ps = get_surface_state(es); + pixman_color_t color; + + color.red = red * 0xffff; + color.green = green * 0xffff; + color.blue = blue * 0xffff; + color.alpha = alpha * 0xffff; + + if (ps->image) { + pixman_image_unref(ps->image); + ps->image = NULL; + } + + ps->image = pixman_image_create_solid_fill(&color); +} + static void pixman_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) { @@ -646,6 +666,18 @@ pixman_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) if (!buffer) return; + if (buffer->type == WESTON_BUFFER_SOLID) { + pixman_renderer_surface_set_color(es, + buffer->solid.r, + buffer->solid.g, + buffer->solid.b, + buffer->solid.a); + weston_buffer_reference(&ps->buffer_ref, NULL, + BUFFER_WILL_NOT_BE_ACCESSED); + weston_buffer_release_reference(&ps->buffer_release_ref, NULL); + return; + } + if (buffer->type != WESTON_BUFFER_SHM) { weston_log("Pixman renderer supports only SHM buffers\n"); weston_buffer_reference(&ps->buffer_ref, NULL, @@ -752,26 +784,6 @@ pixman_renderer_create_surface(struct weston_surface *surface) return 0; } -static void -pixman_renderer_surface_set_color(struct weston_surface *es, - float red, float green, float blue, float alpha) -{ - struct pixman_surface_state *ps = get_surface_state(es); - pixman_color_t color; - - color.red = red * 0xffff; - color.green = green * 0xffff; - color.blue = blue * 0xffff; - color.alpha = alpha * 0xffff; - - if (ps->image) { - pixman_image_unref(ps->image); - ps->image = NULL; - } - - ps->image = pixman_image_create_solid_fill(&color); -} - static void pixman_renderer_destroy(struct weston_compositor *ec) { From 493a4c013e59aa782ed4394219d7987f383c4196 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Mon, 17 Jan 2022 14:56:24 +0000 Subject: [PATCH 166/609] noop-render: Allow solid-color buffers Refactor the buffer-type check slightly so we can handle solid-color buffers, which we do exactly nothing with. Signed-off-by: Daniel Stone --- libweston/noop-renderer.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/libweston/noop-renderer.c b/libweston/noop-renderer.c index 0fb33e4e..a7a1d5df 100644 --- a/libweston/noop-renderer.c +++ b/libweston/noop-renderer.c @@ -63,7 +63,14 @@ noop_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) if (!buffer) return; - if (buffer->type != WESTON_BUFFER_SHM) { + switch (buffer->type) { + case WESTON_BUFFER_SOLID: + /* no-op, early exit */ + return; + case WESTON_BUFFER_SHM: + /* fine */ + break; + default: weston_log("No-op renderer supports only SHM buffers\n"); return; } From b38b735e20efec218f78c034be64a3fe77a89c28 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Mon, 17 Jan 2022 18:53:50 +0000 Subject: [PATCH 167/609] backend-drm: Remove Pixman conditional for keep_buffer The Pixman renderer keeps its own reference to buffers when attached to surfaces, through its surface state: just use that instead. Signed-off-by: Daniel Stone --- libweston/backend-drm/state-propose.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index 799d4825..ecb49b71 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -964,10 +964,8 @@ drm_assign_planes(struct weston_output *output_base) if (weston_view_has_valid_buffer(ev)) { struct weston_buffer *buffer = ev->surface->buffer_ref.buffer; - if (b->use_pixman) - ev->surface->keep_buffer = true; - else if (buffer->type == WESTON_BUFFER_DMABUF || - buffer->type == WESTON_BUFFER_RENDERER_OPAQUE) + if (buffer->type == WESTON_BUFFER_DMABUF || + buffer->type == WESTON_BUFFER_RENDERER_OPAQUE) ev->surface->keep_buffer = true; else if (buffer->type == WESTON_BUFFER_SHM && (ev->surface->width <= b->cursor_width && From 82b646728c74f86a42398628d1de0478cab334da Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Mon, 17 Jan 2022 19:03:33 +0000 Subject: [PATCH 168/609] backend-drm: Handle solid-colour buffers in state propose When we're checking to see if a view is suitable to go on a plane, check for (and reject) solid-colour buffers. Signed-off-by: Daniel Stone --- libweston/backend-drm/state-propose.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index ecb49b71..790db3ec 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -447,7 +447,11 @@ drm_output_find_plane_for_view(struct drm_output_state *state, } buffer = ev->surface->buffer_ref.buffer; - if (buffer->type == WESTON_BUFFER_SHM) { + if (buffer->type == WESTON_BUFFER_SOLID) { + pnode->try_view_on_plane_failure_reasons |= + FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE; + return NULL; + } else if (buffer->type == WESTON_BUFFER_SHM) { if (!output->cursor_plane || b->cursors_are_broken) { pnode->try_view_on_plane_failure_reasons |= FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE; @@ -773,6 +777,17 @@ drm_output_propose_state(struct weston_output *output_base, force_renderer = true; } + /* We can support this with the 'CRTC background colour' property, + * if it is fullscreen (i.e. we disable the primary plane), and + * opaque (as it is only shown in the absence of any covering + * plane, not as a replacement for the primary plane per se). */ + if (ev->surface->buffer_ref.buffer && + ev->surface->buffer_ref.buffer->type == WESTON_BUFFER_SOLID) { + drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane " + "(solid-colour surface)\n", ev); + force_renderer = true; + } + if (pnode->surf_xform.transform != NULL || !pnode->surf_xform.identity_pipeline) { drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane " From 4d426ab6b1d645efca59493249d575e1c0b5c05e Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Mon, 17 Jan 2022 14:48:19 +0000 Subject: [PATCH 169/609] shell: Explicitly use solid weston_buffers Rather than punching through to set the surface as a solid colour, attach an actual weston_buffer to it instead. This becomes the first user of attaching non-client-generated buffers to a weston_surface. As a result, it is necessary to introduce a function which will allow compositors and shells to attach a buffer to a surface. weston_surface_attach_solid() is therefore introduced as a special-purpose helper which will attach a solid buffer to a weston_surface. It is not intended as a general-purpose mechanism to allow compositors to attach client-generated buffers to surfaces, as doing so would have unknown effects on this core part of the compositor itself. Signed-off-by: Daniel Stone --- include/libweston/libweston.h | 5 ++++ libweston/compositor.c | 55 +++++++++++++++++++++++++---------- shell-utils/shell-utils.c | 28 ++++++++++-------- shell-utils/shell-utils.h | 1 + 4 files changed, 62 insertions(+), 27 deletions(-) diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 52e3bac2..09bc8bdd 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -1776,6 +1776,11 @@ struct weston_buffer_reference * weston_buffer_create_solid_rgba(struct weston_compositor *compositor, float r, float g, float b, float a); +void +weston_surface_attach_solid(struct weston_surface *surface, + struct weston_buffer_reference *buffer_ref, + int w, int h); + void weston_buffer_destroy_solid(struct weston_buffer_reference *buffer_ref); diff --git a/libweston/compositor.c b/libweston/compositor.c index bb41881b..2a84d56e 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -2595,40 +2595,65 @@ weston_buffer_create_solid_rgba(struct weston_compositor *compositor, float r, float g, float b, float a) { struct weston_buffer_reference *ret = zalloc(sizeof(*ret)); + struct weston_buffer *buffer; if (!ret) return NULL; - ret->buffer = zalloc(sizeof(*ret->buffer)); - if (!ret->buffer) { + buffer = zalloc(sizeof(*buffer)); + if (!buffer) { free(ret); return NULL; } - wl_signal_init(&ret->buffer->destroy_signal); - ret->buffer->type = WESTON_BUFFER_SOLID; - ret->buffer->width = 1; - ret->buffer->height = 1; - ret->buffer->buffer_origin = ORIGIN_TOP_LEFT; - ret->buffer->solid.r = r; - ret->buffer->solid.g = g; - ret->buffer->solid.b = b; - ret->buffer->solid.a = a; + wl_signal_init(&buffer->destroy_signal); + buffer->type = WESTON_BUFFER_SOLID; + buffer->width = 1; + buffer->height = 1; + buffer->buffer_origin = ORIGIN_TOP_LEFT; + buffer->solid.r = r; + buffer->solid.g = g; + buffer->solid.b = b; + buffer->solid.a = a; if (a == 1.0) { - ret->buffer->pixel_format = + buffer->pixel_format = pixel_format_get_info_shm(WL_SHM_FORMAT_XRGB8888); } else { - ret->buffer->pixel_format = + buffer->pixel_format = pixel_format_get_info_shm(WL_SHM_FORMAT_ARGB8888); } - ret->buffer->format_modifier = DRM_FORMAT_MOD_LINEAR; + buffer->format_modifier = DRM_FORMAT_MOD_LINEAR; - weston_buffer_reference(ret, ret->buffer, BUFFER_MAY_BE_ACCESSED); + weston_buffer_reference(ret, buffer, BUFFER_MAY_BE_ACCESSED); return ret; } +WL_EXPORT void +weston_surface_attach_solid(struct weston_surface *surface, + struct weston_buffer_reference *buffer_ref, + int w, int h) +{ + struct weston_buffer *buffer = buffer_ref->buffer; + + assert(buffer); + assert(buffer->type == WESTON_BUFFER_SOLID); + weston_buffer_reference(&surface->buffer_ref, buffer, + BUFFER_MAY_BE_ACCESSED); + surface->compositor->renderer->attach(surface, buffer); + + weston_surface_set_size(surface, w, h); + + pixman_region32_fini(&surface->opaque); + if (buffer->solid.a == 1.0) { + surface->is_opaque = true; + pixman_region32_init_rect(&surface->opaque, 0, 0, w, h); + } else { + pixman_region32_init(&surface->opaque); + } +} + WL_EXPORT void weston_buffer_destroy_solid(struct weston_buffer_reference *buffer_ref) { diff --git a/shell-utils/shell-utils.c b/shell-utils/shell-utils.c index 51089328..e57ec8ac 100644 --- a/shell-utils/shell-utils.c +++ b/shell-utils/shell-utils.c @@ -144,6 +144,7 @@ weston_curtain_create(struct weston_compositor *compositor, { struct weston_curtain *curtain; struct weston_surface *surface = NULL; + struct weston_buffer_reference *buffer_ref; struct weston_view *view; curtain = zalloc(sizeof(*curtain)); @@ -158,22 +159,23 @@ weston_curtain_create(struct weston_compositor *compositor, if (view == NULL) goto err_surface; - surface->committed = params->surface_committed; - surface->committed_private = params->surface_private; + buffer_ref = weston_buffer_create_solid_rgba(compositor, + params->r, + params->g, + params->b, + params->a); + if (buffer_ref == NULL) + goto err_view; curtain->view = view; + curtain->buffer_ref = buffer_ref; - weston_surface_set_color(surface, - params->r, params->g, params->b, params->a); weston_surface_set_label_func(surface, params->get_label); + surface->committed = params->surface_committed; + surface->committed_private = params->surface_private; - pixman_region32_fini(&surface->opaque); - if (params->a == 1.0) { - pixman_region32_init_rect(&surface->opaque, 0, 0, - params->width, params->height); - } else { - pixman_region32_init(&surface->opaque); - } + weston_surface_attach_solid(surface, buffer_ref, params->width, + params->height); pixman_region32_fini(&surface->input); if (params->capture_input) { @@ -183,11 +185,12 @@ weston_curtain_create(struct weston_compositor *compositor, pixman_region32_init(&surface->input); } - weston_surface_set_size(surface, params->width, params->height); weston_view_set_position(view, params->x, params->y); return curtain; +err_view: + weston_view_destroy(view); err_surface: weston_surface_destroy(surface); err_curtain: @@ -204,5 +207,6 @@ weston_curtain_destroy(struct weston_curtain *curtain) weston_view_destroy(curtain->view); weston_surface_destroy(surface); + weston_buffer_destroy_solid(curtain->buffer_ref); free(curtain); } diff --git a/shell-utils/shell-utils.h b/shell-utils/shell-utils.h index 8e2e5cb3..f326eade 100644 --- a/shell-utils/shell-utils.h +++ b/shell-utils/shell-utils.h @@ -38,6 +38,7 @@ struct weston_curtain_params { struct weston_curtain { struct weston_view *view; + struct weston_buffer_reference *buffer_ref; }; struct weston_output * From b5605ccd2613eb6f34a33f1096f1a4b297391c55 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 18 Jan 2022 17:48:30 +0000 Subject: [PATCH 170/609] libweston: Remove weston_surface_set_color Don't do this; instead, create a solid-colour buffer and attach it to the surface explicitly. Signed-off-by: Daniel Stone --- include/libweston/libweston.h | 7 ------- libweston/compositor.c | 8 -------- libweston/noop-renderer.c | 7 ------- libweston/pixman-renderer.c | 1 - libweston/renderer-gl/gl-renderer.c | 1 - 5 files changed, 24 deletions(-) diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 09bc8bdd..58097205 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -929,9 +929,6 @@ struct weston_renderer { void (*flush_damage)(struct weston_surface *surface, struct weston_buffer *buffer); void (*attach)(struct weston_surface *es, struct weston_buffer *buffer); - void (*surface_set_color)(struct weston_surface *surface, - float red, float green, - float blue, float alpha); void (*destroy)(struct weston_compositor *ec); /** See weston_surface_get_content_size() */ @@ -1977,10 +1974,6 @@ struct weston_view_animation * weston_slide_run(struct weston_view *view, float start, float stop, weston_view_animation_done_func_t done, void *data); -void -weston_surface_set_color(struct weston_surface *surface, - float red, float green, float blue, float alpha); - void weston_surface_destroy(struct weston_surface *surface); diff --git a/libweston/compositor.c b/libweston/compositor.c index 2a84d56e..50598ef3 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -630,14 +630,6 @@ weston_surface_create(struct weston_compositor *compositor) return surface; } -WL_EXPORT void -weston_surface_set_color(struct weston_surface *surface, - float red, float green, float blue, float alpha) -{ - surface->compositor->renderer->surface_set_color(surface, red, green, blue, alpha); - surface->is_opaque = !(alpha < 1.0); -} - WL_EXPORT void weston_view_to_global_float(struct weston_view *view, float sx, float sy, float *x, float *y) diff --git a/libweston/noop-renderer.c b/libweston/noop-renderer.c index a7a1d5df..a105595f 100644 --- a/libweston/noop-renderer.c +++ b/libweston/noop-renderer.c @@ -96,12 +96,6 @@ noop_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) buffer->height = height; } -static void -noop_renderer_surface_set_color(struct weston_surface *surface, - float red, float green, float blue, float alpha) -{ -} - static void noop_renderer_destroy(struct weston_compositor *ec) { @@ -122,7 +116,6 @@ noop_renderer_init(struct weston_compositor *ec) renderer->repaint_output = noop_renderer_repaint_output; renderer->flush_damage = noop_renderer_flush_damage; renderer->attach = noop_renderer_attach; - renderer->surface_set_color = noop_renderer_surface_set_color; renderer->destroy = noop_renderer_destroy; ec->renderer = renderer; diff --git a/libweston/pixman-renderer.c b/libweston/pixman-renderer.c index 361ffa61..636d9d91 100644 --- a/libweston/pixman-renderer.c +++ b/libweston/pixman-renderer.c @@ -881,7 +881,6 @@ pixman_renderer_init(struct weston_compositor *ec) renderer->base.repaint_output = pixman_renderer_repaint_output; renderer->base.flush_damage = pixman_renderer_flush_damage; renderer->base.attach = pixman_renderer_attach; - renderer->base.surface_set_color = pixman_renderer_surface_set_color; renderer->base.destroy = pixman_renderer_destroy; renderer->base.surface_get_content_size = pixman_renderer_surface_get_content_size; diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 557c0c1d..b29b4ad3 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -3733,7 +3733,6 @@ gl_renderer_display_create(struct weston_compositor *ec, gr->base.repaint_output = gl_renderer_repaint_output; gr->base.flush_damage = gl_renderer_flush_damage; gr->base.attach = gl_renderer_attach; - gr->base.surface_set_color = gl_renderer_surface_set_color; gr->base.destroy = gl_renderer_destroy; gr->base.surface_get_content_size = gl_renderer_surface_get_content_size; From 509398dc256d5105d308a905b4c2a8997ae47021 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Fri, 18 Mar 2022 11:42:17 +0200 Subject: [PATCH 171/609] desktop-shell: Avoid spurious configure events for xdg-shell activation This is a minor re-work of how we de-activate and activate the surfaces in desktop-shell. As activate() is being used for handling keyboard input events, this basically rectifies that such that activation happens only if the passed surface is different from the currently focused surface. Fixes: #593 Signed-off-by: Marius Vlad --- desktop-shell/shell.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 0afd574d..e4f84ec6 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -3678,16 +3678,18 @@ activate(struct desktop_shell *shell, struct weston_view *view, weston_view_activate_input(view, seat, flags); - if (shseat && shseat->focused_surface) { + if (shseat && shseat->focused_surface && + shseat->focused_surface != main_surface) { struct shell_surface *current_focus = get_shell_surface(shseat->focused_surface); assert(current_focus); shell_surface_deactivate(current_focus); } - if (shseat) + if (shseat && shseat->focused_surface != main_surface) { + shell_surface_activate(shsurf); shseat->focused_surface = main_surface; - shell_surface_activate(shsurf); + } state = ensure_focus_state(shell, seat); if (state == NULL) From 7724c5ea380da617883ce8f644276e75f7e5bb7f Mon Sep 17 00:00:00 2001 From: Leandro Ribeiro Date: Mon, 24 Jan 2022 18:35:03 -0300 Subject: [PATCH 172/609] clients/simple-dmabuf-feedback: do not use buffer before compositor's response This fixes an issue when running simple-dmabuf-feedback: "wl_display@1: error 1: invalid arguments for wl_surface@3.attach". As we are not using create_immed request from zwp_linux_dmabuf_v1, we can't start to use a dma-buf buffer before we process compositor's event telling us that the creation succeeded. This was causing problems in the following scenario: 1. buffer is marked to be recreated (because of dma-buf feedback); 2. in buffer_release() event, we destroy the buffer and recreate it; 3. after we recreate it, roundtrip is not called, as we don't want to block during the drawing loop; 4. buffer status is not being properly tracked, so we are trying to use a buffer before receiving the event from the compositor telling us that the creation succeeded. To fix this, this patch improves buffer status tracking. Now we only pick a buffer in the drawing loop when it is available. Also, if we have no buffers available we perform a roundtrip and try again, as we may have recreated all of them but still didn't have the chance to process compositor's events telling us that creation succeeded. Signed-off-by: Leandro Ribeiro --- clients/simple-dmabuf-feedback.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/clients/simple-dmabuf-feedback.c b/clients/simple-dmabuf-feedback.c index 9a66a981..946dcb10 100644 --- a/clients/simple-dmabuf-feedback.c +++ b/clients/simple-dmabuf-feedback.c @@ -141,7 +141,11 @@ struct display { struct buffer { struct window *window; struct wl_buffer *buffer; - bool busy; + enum { + NOT_CREATED, + IN_USE, + AVAILABLE + } status; bool recreate; int dmabuf_fds[4]; struct gbm_bo *bo; @@ -470,7 +474,7 @@ buffer_release(void *data, struct wl_buffer *buffer) { struct buffer *buf = data; - buf->busy = false; + buf->status = AVAILABLE; if (buf->recreate) buffer_recreate(buf); @@ -486,6 +490,7 @@ create_succeeded(void *data, struct zwp_linux_buffer_params_v1 *params, { struct buffer *buf = data; + buf->status = AVAILABLE; buf->buffer = new_buffer; wl_buffer_add_listener(buf->buffer, &buffer_listener, buf); zwp_linux_buffer_params_v1_destroy(params); @@ -517,6 +522,7 @@ create_dmabuf_buffer(struct window *window, struct buffer *buf, uint32_t width, struct zwp_linux_buffer_params_v1 *params; int i; + buf->status = NOT_CREATED; buf->window = window; buf->width = width; buf->height = height; @@ -574,8 +580,22 @@ window_next_buffer(struct window *window) unsigned int i; for (i = 0; i < NUM_BUFFERS; i++) - if (!window->buffers[i].busy) + if (window->buffers[i].status == AVAILABLE) return &window->buffers[i]; + + /* In this client, we sometimes have to recreate the buffers. As we are + * not using the create_immed request from zwp_linux_dmabuf_v1, we need + * to wait an event from the server (what leads to create_succeeded() + * being called in this client). So if all buffers are busy, it may be + * the case in which all the buffers were recreated but the server still + * didn't send the events. This is very unlikely to happen, but a + * roundtrip() guarantees that we receive and process the events. */ + wl_display_roundtrip(window->display->display); + + for (i = 0; i < NUM_BUFFERS; i++) + if (window->buffers[i].status == AVAILABLE) + return &window->buffers[i]; + return NULL; } @@ -640,7 +660,7 @@ redraw(void *data, struct wl_callback *callback, uint32_t time) window->callback = wl_surface_frame(window->surface); wl_callback_add_listener(window->callback, &frame_listener, window); wl_surface_commit(window->surface); - buf->busy = true; + buf->status = IN_USE; region = wl_compositor_create_region(window->display->compositor); wl_region_add(region, 0, 0, window->display->output.width, From 778c0683c0c5e96f61744bfa4712b9ce780eead2 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 23 Jan 2022 09:32:47 +0100 Subject: [PATCH 173/609] clients/simple-dmabuf-feedback: use presentation-time Print a message when presentation switches to/from zero-copy mode. This makes it easier to understand whether the compositor DMA-BUF feedback was effective. Signed-off-by: Simon Ser --- clients/meson.build | 2 + clients/simple-dmabuf-feedback.c | 68 ++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/clients/meson.build b/clients/meson.build index 55d7bf65..53783f15 100644 --- a/clients/meson.build +++ b/clients/meson.build @@ -60,6 +60,8 @@ simple_clients = [ '../libweston/pixel-formats.c', linux_dmabuf_unstable_v1_client_protocol_h, linux_dmabuf_unstable_v1_protocol_c, + presentation_time_client_protocol_h, + presentation_time_protocol_c, xdg_shell_client_protocol_h, xdg_shell_protocol_c, ], diff --git a/clients/simple-dmabuf-feedback.c b/clients/simple-dmabuf-feedback.c index 946dcb10..d953f389 100644 --- a/clients/simple-dmabuf-feedback.c +++ b/clients/simple-dmabuf-feedback.c @@ -40,6 +40,7 @@ #include #include "xdg-shell-client-protocol.h" #include "linux-dmabuf-unstable-v1-client-protocol.h" +#include "presentation-time-client-protocol.h" #include #include @@ -134,6 +135,7 @@ struct display { struct output output; struct xdg_wm_base *wm_base; struct zwp_linux_dmabuf_v1 *dmabuf; + struct wp_presentation *presentation; struct gbm_device *gbm_device; struct egl egl; }; @@ -165,7 +167,9 @@ struct window { struct xdg_surface *xdg_surface; struct xdg_toplevel *xdg_toplevel; struct wl_callback *callback; + struct wp_presentation_feedback *presentation_feedback; bool wait_for_configure; + bool presented_zero_copy; uint32_t n_redraws; struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback_obj; struct dmabuf_feedback dmabuf_feedback, pending_dmabuf_feedback; @@ -638,6 +642,7 @@ render(struct buffer *buffer) } static const struct wl_callback_listener frame_listener; +static const struct wp_presentation_feedback_listener presentation_feedback_listener; static void redraw(void *data, struct wl_callback *callback, uint32_t time) @@ -659,6 +664,18 @@ redraw(void *data, struct wl_callback *callback, uint32_t time) window->callback = wl_surface_frame(window->surface); wl_callback_add_listener(window->callback, &frame_listener, window); + + if (window->presentation_feedback) + wp_presentation_feedback_destroy(window->presentation_feedback); + if (window->display->presentation) { + window->presentation_feedback = + wp_presentation_feedback(window->display->presentation, + window->surface); + wp_presentation_feedback_add_listener(window->presentation_feedback, + &presentation_feedback_listener, + window); + } + wl_surface_commit(window->surface); buf->status = IN_USE; @@ -675,6 +692,48 @@ static const struct wl_callback_listener frame_listener = { redraw }; +static void presentation_feedback_handle_sync_output(void *data, + struct wp_presentation_feedback *feedback, + struct wl_output *output) +{ +} + +static void presentation_feedback_handle_presented(void *data, + struct wp_presentation_feedback *feedback, + uint32_t tv_sec_hi, + uint32_t tv_sec_lo, + uint32_t tv_nsec, + uint32_t refresh, + uint32_t seq_hi, + uint32_t seq_lo, + uint32_t flags) +{ + struct window *window = data; + bool zero_copy = flags & WP_PRESENTATION_FEEDBACK_KIND_ZERO_COPY; + + if (zero_copy && !window->presented_zero_copy) { + fprintf(stderr, "Presenting in zero-copy mode\n"); + } + if (!zero_copy && window->presented_zero_copy) { + fprintf(stderr, "Stopped presenting in zero-copy mode\n"); + } + + window->presented_zero_copy = zero_copy; + wp_presentation_feedback_destroy(feedback); +} + +static void presentation_feedback_handle_discarded(void *data, + struct wp_presentation_feedback *feedback) +{ + wp_presentation_feedback_destroy(feedback); +} + +static const struct wp_presentation_feedback_listener presentation_feedback_listener = { + .sync_output = presentation_feedback_handle_sync_output, + .presented = presentation_feedback_handle_presented, + .discarded = presentation_feedback_handle_discarded, +}; + static void xdg_surface_handle_configure(void *data, struct xdg_surface *surface, uint32_t serial) @@ -811,6 +870,8 @@ destroy_window(struct window *window) if (window->callback) wl_callback_destroy(window->callback); + if (window->presentation_feedback) + wp_presentation_feedback_destroy(window->presentation_feedback); for (i = 0; i < NUM_BUFFERS; i++) if (window->buffers[i].buffer) @@ -1334,6 +1395,10 @@ registry_handle_global(void *data, struct wl_registry *registry, d->dmabuf = wl_registry_bind(registry, id, &zwp_linux_dmabuf_v1_interface, MIN(version, 4)); + } else if (strcmp(interface, "wp_presentation") == 0) { + d->presentation = wl_registry_bind(registry, id, + &wp_presentation_interface, + 1); } } @@ -1358,6 +1423,9 @@ destroy_display(struct display *display) if (display->egl.display != EGL_NO_DISPLAY) eglTerminate(display->egl.display); + if (display->presentation) + wp_presentation_destroy(display->presentation); + zwp_linux_dmabuf_v1_destroy(display->dmabuf); xdg_wm_base_destroy(display->wm_base); From 7f10997d92fad04b8c42856536e39c7f70ee874f Mon Sep 17 00:00:00 2001 From: Hideyuki Nagase Date: Tue, 15 Mar 2022 17:12:01 -0500 Subject: [PATCH 174/609] rdp: add mouse input debug Add extremely verbose mouse input debugging. Co-authored-by: Steve Pronovost Co-authored-by: Brenton DeGeer Signed-off-by: Hideyuki Nagase Signed-off-by: Steve Pronovost Signed-off-by: Brenton DeGeer --- libweston/backend-rdp/rdp.c | 38 +++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index bd4dd1d6..86d8f81f 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -945,6 +945,40 @@ xf_peer_post_connect(freerdp_peer *client) return TRUE; } +static void +dump_mouseinput(RdpPeerContext *peerContext, UINT16 flags, UINT16 x, UINT16 y, bool is_ex) +{ + struct rdp_backend *b = peerContext->rdpBackend; + + rdp_debug_verbose(b, "RDP mouse input%s: (%d, %d): flags:%x: ", is_ex ? "_ex" : "", x, y, flags); + if (is_ex) { + if (flags & PTR_XFLAGS_DOWN) + rdp_debug_verbose_continue(b, "DOWN "); + if (flags & PTR_XFLAGS_BUTTON1) + rdp_debug_verbose_continue(b, "XBUTTON1 "); + if (flags & PTR_XFLAGS_BUTTON2) + rdp_debug_verbose_continue(b, "XBUTTON2 "); + } else { + if (flags & PTR_FLAGS_WHEEL) + rdp_debug_verbose_continue(b, "WHEEL "); + if (flags & PTR_FLAGS_WHEEL_NEGATIVE) + rdp_debug_verbose_continue(b, "WHEEL_NEGATIVE "); + if (flags & PTR_FLAGS_HWHEEL) + rdp_debug_verbose_continue(b, "HWHEEL "); + if (flags & PTR_FLAGS_MOVE) + rdp_debug_verbose_continue(b, "MOVE "); + if (flags & PTR_FLAGS_DOWN) + rdp_debug_verbose_continue(b, "DOWN "); + if (flags & PTR_FLAGS_BUTTON1) + rdp_debug_verbose_continue(b, "BUTTON1 "); + if (flags & PTR_FLAGS_BUTTON2) + rdp_debug_verbose_continue(b, "BUTTON2 "); + if (flags & PTR_FLAGS_BUTTON3) + rdp_debug_verbose_continue(b, "BUTTON3 "); + } + rdp_debug_verbose_continue(b, "\n"); +} + static BOOL xf_mouseEvent(rdpInput *input, UINT16 flags, UINT16 x, UINT16 y) { @@ -954,6 +988,8 @@ xf_mouseEvent(rdpInput *input, UINT16 flags, UINT16 x, UINT16 y) bool need_frame = false; struct timespec time; + dump_mouseinput(peerContext, flags, x, y, false); + if (flags & PTR_FLAGS_MOVE) { output = peerContext->rdpBackend->output; if (x < output->base.width && y < output->base.height) { @@ -1017,6 +1053,8 @@ xf_extendedMouseEvent(rdpInput *input, UINT16 flags, UINT16 x, UINT16 y) struct rdp_output *output; struct timespec time; + dump_mouseinput(peerContext, flags, x, y, true); + output = peerContext->rdpBackend->output; if (x < output->base.width && y < output->base.height) { weston_compositor_get_time(&time); From 873ec15412e9b549eeaa6bc294ecef87d0e12f82 Mon Sep 17 00:00:00 2001 From: Hideyuki Nagase Date: Wed, 16 Mar 2022 10:54:07 -0500 Subject: [PATCH 175/609] rdp: Fix up xf_extendedMouseEvent These events carry the 4th and 5th mouse buttons, so we should propagate them. We also need to use pointer frames to ensure the buttons are properly paired with the pointer co-ordinates. Unfortunately, there is no way in RDP to determine if a mouse event and an extended mouse event should be in the same pointer frame, so this is the best we can do. We also enable extended mouse events so they'll be used. Co-authored-by: Steve Pronovost Co-authored-by: Brenton DeGeer Signed-off-by: Hideyuki Nagase Signed-off-by: Steve Pronovost Signed-off-by: Brenton DeGeer --- libweston/backend-rdp/rdp.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index 86d8f81f..9f1ccdb5 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -1050,17 +1050,35 @@ static BOOL xf_extendedMouseEvent(rdpInput *input, UINT16 flags, UINT16 x, UINT16 y) { RdpPeerContext *peerContext = (RdpPeerContext *)input->context; + uint32_t button = 0; + bool need_frame = false; struct rdp_output *output; struct timespec time; dump_mouseinput(peerContext, flags, x, y, true); + if (flags & PTR_XFLAGS_BUTTON1) + button = BTN_SIDE; + else if (flags & PTR_XFLAGS_BUTTON2) + button = BTN_EXTRA; + + if (button) { + weston_compositor_get_time(&time); + notify_button(peerContext->item.seat, &time, button, + (flags & PTR_XFLAGS_DOWN) ? WL_POINTER_BUTTON_STATE_PRESSED : WL_POINTER_BUTTON_STATE_RELEASED); + need_frame = true; + } + output = peerContext->rdpBackend->output; if (x < output->base.width && y < output->base.height) { weston_compositor_get_time(&time); notify_motion_absolute(peerContext->item.seat, &time, x, y); + need_frame = true; } + if (need_frame) + notify_pointer_frame(peerContext->item.seat); + return TRUE; } @@ -1206,6 +1224,7 @@ rdp_peer_init(freerdp_peer *client, struct rdp_backend *b) settings->NSCodec = TRUE; settings->FrameMarkerCommandEnabled = TRUE; settings->SurfaceFrameMarkerEnabled = TRUE; + settings->HasExtendedMouseEvent = TRUE; client->Capabilities = xf_peer_capabilities; client->PostConnect = xf_peer_post_connect; From 13e62c9d18b7850dfa4e7a15f1dcb02c7d99ee07 Mon Sep 17 00:00:00 2001 From: Hideyuki Nagase Date: Tue, 15 Mar 2022 16:40:15 -0500 Subject: [PATCH 176/609] rdp: validate button state Discard and log duplicate release or press events. Log and discard invalid button ids. This prevents buggy clients from causing problems in weston's internal state - like breaking idle inhibition button counting. Co-authored-by: Steve Pronovost Co-authored-by: Brenton DeGeer Signed-off-by: Hideyuki Nagase Signed-off-by: Steve Pronovost Signed-off-by: Brenton DeGeer --- libweston/backend-rdp/rdp.c | 39 +++++++++++++++++++++++++++++++++++++ libweston/backend-rdp/rdp.h | 2 ++ 2 files changed, 41 insertions(+) diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index 9f1ccdb5..a7fb59c3 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -979,6 +979,33 @@ dump_mouseinput(RdpPeerContext *peerContext, UINT16 flags, UINT16 x, UINT16 y, b rdp_debug_verbose_continue(b, "\n"); } +static void +rdp_validate_button_state(RdpPeerContext *peerContext, bool pressed, uint32_t *button) +{ + struct rdp_backend *b = peerContext->rdpBackend; + uint32_t index; + + if (*button < BTN_LEFT || *button > BTN_EXTRA) { + weston_log("RDP client posted invalid button event\n"); + goto ignore; + } + + index = *button - BTN_LEFT; + assert(index < ARRAY_LENGTH(peerContext->button_state)); + + if (pressed == peerContext->button_state[index]) { + rdp_debug_verbose(b, "%s: inconsistent button state button:%d (index:%d) pressed:%d\n", + __func__, *button, index, pressed); + goto ignore; + } else { + peerContext->button_state[index] = pressed; + } + return; +ignore: + /* ignore button input */ + *button = 0; +} + static BOOL xf_mouseEvent(rdpInput *input, UINT16 flags, UINT16 x, UINT16 y) { @@ -1007,6 +1034,12 @@ xf_mouseEvent(rdpInput *input, UINT16 flags, UINT16 x, UINT16 y) else if (flags & PTR_FLAGS_BUTTON3) button = BTN_MIDDLE; + if (button) { + rdp_validate_button_state(peerContext, + flags & PTR_FLAGS_DOWN ? true : false, + &button); + } + if (button) { weston_compositor_get_time(&time); notify_button(peerContext->item.seat, &time, button, @@ -1062,6 +1095,12 @@ xf_extendedMouseEvent(rdpInput *input, UINT16 flags, UINT16 x, UINT16 y) else if (flags & PTR_XFLAGS_BUTTON2) button = BTN_EXTRA; + if (button) { + rdp_validate_button_state(peerContext, + flags & PTR_XFLAGS_DOWN ? true : false, + &button); + } + if (button) { weston_compositor_get_time(&time); notify_button(peerContext->item.seat, &time, button, diff --git a/libweston/backend-rdp/rdp.h b/libweston/backend-rdp/rdp.h index 2ebf50f6..31e56d96 100644 --- a/libweston/backend-rdp/rdp.h +++ b/libweston/backend-rdp/rdp.h @@ -109,6 +109,8 @@ struct rdp_peer_context { NSC_CONTEXT *nsc_context; struct rdp_peers_item item; + + bool button_state[5]; }; typedef struct rdp_peer_context RdpPeerContext; From 2f9319cef611a1c2925e65d63e1068b8f80a531b Mon Sep 17 00:00:00 2001 From: Brenton DeGeer Date: Tue, 15 Mar 2022 12:24:42 -0500 Subject: [PATCH 177/609] rdp: Allow specifying a listener fd on the command line We already have a way for a single RDP client connection to be passed from a parent process to a child using a combination of environment variable (RDP_FD) and env var (--env-socket) This patch allows a bound socket fd (as opposed to a client connection) to be established in a parent process and provided to the rdp backend. WSLg uses this to set up an AF_VSOCK socket for communication between a Windows RDP client and a weston compositor running under a hypervisor. Co-authored-by: Hideyuki Nagase Co-authored-by: Steve Pronovost Signed-off-by: Hideyuki Nagase Signed-off-by: Steve Pronovost Signed-off-by: Brenton DeGeer --- compositor/main.c | 3 +++ include/libweston/backend-rdp.h | 1 + libweston/backend-rdp/rdp.c | 41 +++++++++++++++++++++++---------- libweston/backend-rdp/rdp.h | 1 + man/weston-rdp.man | 6 +++++ 5 files changed, 40 insertions(+), 12 deletions(-) diff --git a/compositor/main.c b/compositor/main.c index dfc3d4bd..7bfcbc2d 100644 --- a/compositor/main.c +++ b/compositor/main.c @@ -688,6 +688,7 @@ usage(int error_code) " --width=WIDTH\t\tWidth of desktop\n" " --height=HEIGHT\tHeight of desktop\n" " --env-socket\t\tUse socket defined in RDP_FD env variable as peer connection\n" + " --external-listener-fd=FD\tUse socket as listener connection\n" " --address=ADDR\tThe address to bind\n" " --port=PORT\t\tThe port to listen on\n" " --no-clients-resize\tThe RDP peers will be forced to the size of the desktop\n" @@ -2772,6 +2773,7 @@ weston_rdp_backend_config_init(struct weston_rdp_backend_config *config) config->server_cert = NULL; config->server_key = NULL; config->env_socket = 0; + config->external_listener_fd = -1; config->no_clients_resize = 0; config->force_no_compression = 0; config->remotefx_codec = true; @@ -2793,6 +2795,7 @@ load_rdp_backend(struct weston_compositor *c, const struct weston_option rdp_options[] = { { WESTON_OPTION_BOOLEAN, "env-socket", 0, &config.env_socket }, + { WESTON_OPTION_INTEGER, "external-listener-fd", 0, &config.external_listener_fd }, { WESTON_OPTION_INTEGER, "width", 0, &parsed_options->width }, { WESTON_OPTION_INTEGER, "height", 0, &parsed_options->height }, { WESTON_OPTION_STRING, "address", 0, &config.bind_address }, diff --git a/include/libweston/backend-rdp.h b/include/libweston/backend-rdp.h index 2c55818b..d9f56b93 100644 --- a/include/libweston/backend-rdp.h +++ b/include/libweston/backend-rdp.h @@ -67,6 +67,7 @@ struct weston_rdp_backend_config { int no_clients_resize; int force_no_compression; bool remotefx_codec; + int external_listener_fd; }; #ifdef __cplusplus diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index a7fb59c3..97d0244f 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -1354,14 +1354,27 @@ rdp_backend_create(struct weston_compositor *compositor, compositor->backend = &b->base; - /* activate TLS only if certificate/key are available */ if (config->server_cert && config->server_key) { - weston_log("TLS support activated\n"); b->server_cert = strdup(config->server_cert); b->server_key = strdup(config->server_key); if (!b->server_cert || !b->server_key) goto err_free_strings; - b->tls_enabled = 1; + } + + /* if we are listening for client connections on an external listener + * fd, we don't need to enforce TLS or RDP security, since FreeRDP + * will consider it to be a local connection */ + fd = config->external_listener_fd; + if (fd < 0) { + if (!b->rdp_key && (!b->server_cert || !b->server_key)) { + weston_log("the RDP compositor requires keys and an optional certificate for RDP or TLS security (" + "--rdp4-key or --rdp-tls-cert/--rdp-tls-key)\n"); + goto err_free_strings; + } + if (b->server_cert && b->server_key) { + b->tls_enabled = 1; + rdp_debug(b, "TLS support activated\n"); + } } if (weston_compositor_set_presentation_clock_software(compositor) < 0) @@ -1379,9 +1392,18 @@ rdp_backend_create(struct weston_compositor *compositor, b->listener = freerdp_listener_new(); b->listener->PeerAccepted = rdp_incoming_peer; b->listener->param4 = b; - if (!b->listener->Open(b->listener, config->bind_address, config->port)) { - weston_log("unable to bind rdp socket\n"); - goto err_listener; + if (fd >= 0) { + rdp_debug(b, "Using external fd for incoming connections: %d\n", fd); + + if (!b->listener->OpenFromSocket(b->listener, fd)) { + weston_log("RDP unable to use external listener fd: %d\n", fd); + goto err_listener; + } + } else { + if (!b->listener->Open(b->listener, config->bind_address, config->port)) { + weston_log("RDP unable to bind socket\n"); + goto err_listener; + } } if (rdp_implant_listener(b, b->listener) < 0) @@ -1444,6 +1466,7 @@ config_init_to_defaults(struct weston_rdp_backend_config *config) config->no_clients_resize = 0; config->force_no_compression = 0; config->remotefx_codec = true; + config->external_listener_fd = -1; } WL_EXPORT int @@ -1470,12 +1493,6 @@ weston_backend_init(struct weston_compositor *compositor, config_init_to_defaults(&config); memcpy(&config, config_base, config_base->struct_size); - if (!config.rdp_key && (!config.server_cert || !config.server_key)) { - weston_log("the RDP compositor requires keys and an optional certificate for RDP or TLS security (" - "--rdp4-key or --rdp-tls-cert/--rdp-tls-key)\n"); - return -1; - } - b = rdp_backend_create(compositor, &config); if (b == NULL) return -1; diff --git a/libweston/backend-rdp/rdp.h b/libweston/backend-rdp/rdp.h index 31e56d96..e25082b6 100644 --- a/libweston/backend-rdp/rdp.h +++ b/libweston/backend-rdp/rdp.h @@ -71,6 +71,7 @@ struct rdp_backend { int no_clients_resize; int force_no_compression; bool remotefx_codec; + int external_listener_fd; }; enum peer_item_flags { diff --git a/man/weston-rdp.man b/man/weston-rdp.man index 1d6ccfd0..8900a6ca 100644 --- a/man/weston-rdp.man +++ b/man/weston-rdp.man @@ -59,6 +59,12 @@ to ship a file containing a certificate. \fB\-\-rdp\-tls\-cert\fR=\fIfile\fR The file containing the certificate for doing TLS security. To have TLS security you also need to ship a key file. +.TP +\fB\-\-external\-listener\-fd\fR=\fIfd\fR +Specifies a file descriptor inherited from the process that launched weston +to be listened on for client connections. Only local (such as AF_VSOCK) +sockets should be used, as this will be considered to be a local connection +by the RDP backend, and TLS and RDP security will be bypassed. .\" *************************************************************** From bd214edf2689ac98add81928e18ad3e16f8ff140 Mon Sep 17 00:00:00 2001 From: Hideyuki Nagase Date: Tue, 29 Mar 2022 12:42:30 -0500 Subject: [PATCH 178/609] rdp: Calculate frame times from mode refresh rate Instead of hard coding a 16ms refresh interval, use the refresh rate from the current mode to determine when the next frame should be. Currently, we still hard code the refresh rate, so this will end up with roughly the same value we've been using, but in the future we'll allow setting it via command line. Co-authored-by: Steve Pronovost Co-authored-by: Brenton DeGeer Signed-off-by: Hideyuki Nagase Signed-off-by: Steve Pronovost Signed-off-by: Brenton DeGeer --- libweston/backend-rdp/rdp.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index 97d0244f..b3dacb58 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -243,6 +243,24 @@ rdp_output_repaint(struct weston_output *output_base, pixman_region32_t *damage) struct rdp_output *output = container_of(output_base, struct rdp_output, base); struct weston_compositor *ec = output->base.compositor; struct rdp_peers_item *outputPeer; + struct timespec now, target; + int refresh_nsec = millihz_to_nsec(output_base->current_mode->refresh); + int refresh_msec = refresh_nsec / 1000000; + int next_frame_delta; + + /* Calculate the time we should complete this frame such that frames + are spaced out by the specified monitor refresh. Note that our timer + mechanism only has msec precision, so we won't exactly hit our + target refresh rate. + */ + weston_compositor_read_presentation_clock(ec, &now); + + timespec_add_nsec(&target, &output_base->frame_time, refresh_nsec); + + next_frame_delta = (int)timespec_sub_to_msec(&target, &now); + if (next_frame_delta < 1 || next_frame_delta > refresh_msec) { + next_frame_delta = refresh_msec; + } pixman_renderer_output_set_buffer(output_base, output->shadow_surface); ec->renderer->repaint_output(&output->base, damage); @@ -260,7 +278,7 @@ rdp_output_repaint(struct weston_output *output_base, pixman_region32_t *damage) pixman_region32_subtract(&ec->primary_plane.damage, &ec->primary_plane.damage, damage); - wl_event_source_timer_update(output->finish_frame_timer, 16); + wl_event_source_timer_update(output->finish_frame_timer, next_frame_delta); return 0; } From cf5ddd05cb5f5b75f5218de51ff830c1c2f7563d Mon Sep 17 00:00:00 2001 From: Hideyuki Nagase Date: Thu, 17 Mar 2022 12:08:42 -0500 Subject: [PATCH 179/609] rdp: Allow configuring the refresh rate We currently hardcode a 60Hz update rate for the rdp backend. In some cases it may be useful to override this to increase the rate for a faster monitor, or to decrease it to reduce network traffic. Co-authored-by: Steve Pronovost Co-authored-by: Brenton DeGeer Signed-off-by: Hideyuki Nagase Signed-off-by: Steve Pronovost Signed-off-by: Brenton DeGeer --- compositor/main.c | 6 ++++++ include/libweston/backend-rdp.h | 2 ++ libweston/backend-rdp/rdp.c | 11 ++++++++--- libweston/backend-rdp/rdp.h | 2 +- man/weston-rdp.man | 13 +++++++++++++ 5 files changed, 30 insertions(+), 4 deletions(-) diff --git a/compositor/main.c b/compositor/main.c index 7bfcbc2d..96a99097 100644 --- a/compositor/main.c +++ b/compositor/main.c @@ -2777,6 +2777,7 @@ weston_rdp_backend_config_init(struct weston_rdp_backend_config *config) config->no_clients_resize = 0; config->force_no_compression = 0; config->remotefx_codec = true; + config->refresh_rate = RDP_DEFAULT_FREQ; } static int @@ -2784,6 +2785,7 @@ load_rdp_backend(struct weston_compositor *c, int *argc, char *argv[], struct weston_config *wc) { struct weston_rdp_backend_config config = {{ 0, }}; + struct weston_config_section *section; int ret = 0; bool no_remotefx_codec = false; @@ -2812,6 +2814,10 @@ load_rdp_backend(struct weston_compositor *c, config.remotefx_codec = !no_remotefx_codec; wet_set_simple_head_configurator(c, rdp_backend_output_configure); + section = weston_config_get_section(wc, "rdp", NULL, NULL); + weston_config_section_get_int(section, "refresh-rate", + &config.refresh_rate, + RDP_DEFAULT_FREQ); ret = weston_compositor_load_backend(c, WESTON_BACKEND_RDP, &config.base); diff --git a/include/libweston/backend-rdp.h b/include/libweston/backend-rdp.h index d9f56b93..e88cd856 100644 --- a/include/libweston/backend-rdp.h +++ b/include/libweston/backend-rdp.h @@ -34,6 +34,7 @@ extern "C" { #include #define WESTON_RDP_OUTPUT_API_NAME "weston_rdp_output_api_v1" +#define RDP_DEFAULT_FREQ 60 struct weston_rdp_output_api { /** Initialize a RDP output with specified width and height. @@ -68,6 +69,7 @@ struct weston_rdp_backend_config { int force_no_compression; bool remotefx_codec; int external_listener_fd; + int refresh_rate; }; #ifdef __cplusplus diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index b3dacb58..e0990f18 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -311,6 +311,7 @@ rdp_insert_new_mode(struct weston_output *output, int width, int height, int rat static struct weston_mode * ensure_matching_mode(struct weston_output *output, struct weston_mode *target) { + struct rdp_backend *b = to_rdp_backend(output->compositor); struct weston_mode *local; wl_list_for_each(local, &output->mode_list, link) { @@ -318,7 +319,7 @@ ensure_matching_mode(struct weston_output *output, struct weston_mode *target) return local; } - return rdp_insert_new_mode(output, target->width, target->height, RDP_MODE_FREQ); + return rdp_insert_new_mode(output, target->width, target->height, b->rdp_monitor_refresh_rate); } static int @@ -379,6 +380,7 @@ rdp_output_set_size(struct weston_output *base, int width, int height) { struct rdp_output *output = to_rdp_output(base); + struct rdp_backend *rdpBackend = to_rdp_backend(base->compositor); struct weston_head *head; struct weston_mode *currentMode; struct weston_mode initMode; @@ -399,8 +401,7 @@ rdp_output_set_size(struct weston_output *base, initMode.flags = WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED; initMode.width = width; initMode.height = height; - initMode.refresh = RDP_MODE_FREQ; - + initMode.refresh = rdpBackend->rdp_monitor_refresh_rate; currentMode = ensure_matching_mode(&output->base, &initMode); if (!currentMode) return -1; @@ -1370,6 +1371,9 @@ rdp_backend_create(struct weston_compositor *compositor, /* After here, rdp_debug() is ready to be used */ + b->rdp_monitor_refresh_rate = config->refresh_rate * 1000; + rdp_debug(b, "RDP backend: WESTON_RDP_MONITOR_REFRESH_RATE: %d\n", b->rdp_monitor_refresh_rate); + compositor->backend = &b->base; if (config->server_cert && config->server_key) { @@ -1485,6 +1489,7 @@ config_init_to_defaults(struct weston_rdp_backend_config *config) config->force_no_compression = 0; config->remotefx_codec = true; config->external_listener_fd = -1; + config->refresh_rate = RDP_DEFAULT_FREQ; } WL_EXPORT int diff --git a/libweston/backend-rdp/rdp.h b/libweston/backend-rdp/rdp.h index e25082b6..9b32ab4c 100644 --- a/libweston/backend-rdp/rdp.h +++ b/libweston/backend-rdp/rdp.h @@ -49,7 +49,6 @@ #define MAX_FREERDP_FDS 32 #define DEFAULT_AXIS_STEP_DISTANCE 10 -#define RDP_MODE_FREQ 60 * 1000 #define DEFAULT_PIXEL_FORMAT PIXEL_FORMAT_BGRA32 struct rdp_output; @@ -72,6 +71,7 @@ struct rdp_backend { int force_no_compression; bool remotefx_codec; int external_listener_fd; + int rdp_monitor_refresh_rate; }; enum peer_item_flags { diff --git a/man/weston-rdp.man b/man/weston-rdp.man index 8900a6ca..a596826f 100644 --- a/man/weston-rdp.man +++ b/man/weston-rdp.man @@ -24,6 +24,19 @@ backend will announce security options based on which files have been given. The RDP backend is multi-seat aware, so if two clients connect on the backend, they will get their own seat. +.\" *************************************************************** +.SH CONFIGURATION +. +The RDP backend uses the following entries from +.BR weston.ini . +.SS Section rdp +.TP +\fBrefresh-rate\fR=\fIrate\fR +Specifies the desktop redraw rate in Hz. If unspecified, the default is 60Hz. Changing +this may be useful if you have a faster than 60Hz display, or if you want to reduce updates to +reduce network traffic. + + .\" *************************************************************** .SH OPTIONS . From ce09c7835c35c6463d4b7301470cf195deddaabb Mon Sep 17 00:00:00 2001 From: Hideyuki Nagase Date: Tue, 22 Mar 2022 10:48:29 -0500 Subject: [PATCH 180/609] rdp: refactor scrollwheel code We move this into a function for when we add horizontal wheel support later. Co-authored-by: Steve Pronovost Co-authored-by: Brenton DeGeer Signed-off-by: Hideyuki Nagase Signed-off-by: Steve Pronovost Signed-off-by: Brenton DeGeer --- libweston/backend-rdp/rdp.c | 49 +++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index e0990f18..c7690459 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -1025,6 +1025,33 @@ ignore: *button = 0; } +static void +rdp_notify_wheel_scroll(RdpPeerContext *peerContext, UINT16 flags) +{ + struct weston_pointer_axis_event weston_event; + double value; + struct timespec time; + + /* DEFAULT_AXIS_STEP_DISTANCE is stolen from compositor-x11.c + * The RDP specs says the lower bits of flags contains the "the number of rotation + * units the mouse wheel was rotated". + * + * https://devblogs.microsoft.com/oldnewthing/20130123-00/?p=5473 explains the 120 value + */ + value = -(flags & 0xff) / 120.0; + if (flags & PTR_FLAGS_WHEEL_NEGATIVE) + value = -value; + + weston_event.axis = WL_POINTER_AXIS_VERTICAL_SCROLL; + weston_event.value = DEFAULT_AXIS_STEP_DISTANCE * value; + weston_event.discrete = (int)value; + weston_event.has_discrete = true; + + weston_compositor_get_time(&time); + + notify_axis(peerContext->item.seat, &time, &weston_event); +} + static BOOL xf_mouseEvent(rdpInput *input, UINT16 flags, UINT16 x, UINT16 y) { @@ -1068,27 +1095,7 @@ xf_mouseEvent(rdpInput *input, UINT16 flags, UINT16 x, UINT16 y) } if (flags & PTR_FLAGS_WHEEL) { - struct weston_pointer_axis_event weston_event; - double value; - - /* DEFAULT_AXIS_STEP_DISTANCE is stolen from compositor-x11.c - * The RDP specs says the lower bits of flags contains the "the number of rotation - * units the mouse wheel was rotated". - * - * https://devblogs.microsoft.com/oldnewthing/20130123-00/?p=5473 explains the 120 value - */ - value = -(flags & 0xff) / 120.0; - if (flags & PTR_FLAGS_WHEEL_NEGATIVE) - value = -value; - - weston_event.axis = WL_POINTER_AXIS_VERTICAL_SCROLL; - weston_event.value = DEFAULT_AXIS_STEP_DISTANCE * value; - weston_event.discrete = (int)value; - weston_event.has_discrete = true; - - weston_compositor_get_time(&time); - - notify_axis(peerContext->item.seat, &time, &weston_event); + rdp_notify_wheel_scroll(peerContext, flags); need_frame = true; } From 4e907a67e5537e502e9666e52e6897c2623f2291 Mon Sep 17 00:00:00 2001 From: Hideyuki Nagase Date: Tue, 22 Mar 2022 11:13:46 -0500 Subject: [PATCH 181/609] rdp: Add high precision scrolling Co-authored-by: Steve Pronovost Co-authored-by: Brenton DeGeer Signed-off-by: Hideyuki Nagase Signed-off-by: Steve Pronovost Signed-off-by: Brenton DeGeer --- libweston/backend-rdp/rdp.c | 76 ++++++++++++++++++++++++++++--------- libweston/backend-rdp/rdp.h | 3 ++ 2 files changed, 61 insertions(+), 18 deletions(-) diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index c7690459..ad411926 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -1025,31 +1025,71 @@ ignore: *button = 0; } -static void +static bool rdp_notify_wheel_scroll(RdpPeerContext *peerContext, UINT16 flags) { struct weston_pointer_axis_event weston_event; + struct rdp_backend *b = peerContext->rdpBackend; + int ivalue; double value; struct timespec time; - - /* DEFAULT_AXIS_STEP_DISTANCE is stolen from compositor-x11.c - * The RDP specs says the lower bits of flags contains the "the number of rotation - * units the mouse wheel was rotated". - * - * https://devblogs.microsoft.com/oldnewthing/20130123-00/?p=5473 explains the 120 value - */ - value = -(flags & 0xff) / 120.0; + int *accumWheelRotationPrecise; + int *accumWheelRotationDiscrete; + + /* + * The RDP specs says the lower bits of flags contains the "the number of rotation + * units the mouse wheel was rotated". + */ + ivalue = ((int)flags & 0x000000ff); if (flags & PTR_FLAGS_WHEEL_NEGATIVE) - value = -value; + ivalue = (0xff - ivalue) * -1; + + /* + * Flip the scroll direction as the RDP direction is inverse of X/Wayland + * for vertical scroll + */ + ivalue *= -1; + + accumWheelRotationPrecise = &peerContext->verticalAccumWheelRotationPrecise; + accumWheelRotationDiscrete = &peerContext->verticalAccumWheelRotationDiscrete; + + /* + * Accumulate the wheel increments. + * + * Every 12 wheel increments, we will send an update to our Wayland + * clients with an updated value for the wheel for smooth scrolling. + * + * Every 120 wheel increments, we tick one discrete wheel click. + * + * https://devblogs.microsoft.com/oldnewthing/20130123-00/?p=5473 explains the 120 value + */ + *accumWheelRotationPrecise += ivalue; + *accumWheelRotationDiscrete += ivalue; + rdp_debug_verbose(b, "wheel: rawValue:%d accumPrecise:%d accumDiscrete %d\n", + ivalue, *accumWheelRotationPrecise, *accumWheelRotationDiscrete); + + if (abs(*accumWheelRotationPrecise) >= 12) { + value = (double)(*accumWheelRotationPrecise / 12); + + weston_event.axis = WL_POINTER_AXIS_VERTICAL_SCROLL; + weston_event.value = value; + weston_event.discrete = *accumWheelRotationDiscrete / 120; + weston_event.has_discrete = true; + + rdp_debug_verbose(b, "wheel: value:%f discrete:%d\n", + weston_event.value, weston_event.discrete); + + weston_compositor_get_time(&time); + + notify_axis(peerContext->item.seat, &time, &weston_event); - weston_event.axis = WL_POINTER_AXIS_VERTICAL_SCROLL; - weston_event.value = DEFAULT_AXIS_STEP_DISTANCE * value; - weston_event.discrete = (int)value; - weston_event.has_discrete = true; + *accumWheelRotationPrecise %= 12; + *accumWheelRotationDiscrete %= 120; - weston_compositor_get_time(&time); + return true; + } - notify_axis(peerContext->item.seat, &time, &weston_event); + return false; } static BOOL @@ -1095,8 +1135,8 @@ xf_mouseEvent(rdpInput *input, UINT16 flags, UINT16 x, UINT16 y) } if (flags & PTR_FLAGS_WHEEL) { - rdp_notify_wheel_scroll(peerContext, flags); - need_frame = true; + if (rdp_notify_wheel_scroll(peerContext, flags)) + need_frame = true; } if (need_frame) diff --git a/libweston/backend-rdp/rdp.h b/libweston/backend-rdp/rdp.h index 9b32ab4c..1d0228bc 100644 --- a/libweston/backend-rdp/rdp.h +++ b/libweston/backend-rdp/rdp.h @@ -112,6 +112,9 @@ struct rdp_peer_context { struct rdp_peers_item item; bool button_state[5]; + + int verticalAccumWheelRotationPrecise; + int verticalAccumWheelRotationDiscrete; }; typedef struct rdp_peer_context RdpPeerContext; From 806e8248097f3787d3812fa1b553f2068edf303b Mon Sep 17 00:00:00 2001 From: Hideyuki Nagase Date: Tue, 22 Mar 2022 11:15:56 -0500 Subject: [PATCH 182/609] rdp: Add horizontal scroll support Co-authored-by: Steve Pronovost Co-authored-by: Brenton DeGeer Signed-off-by: Hideyuki Nagase Signed-off-by: Steve Pronovost Signed-off-by: Brenton DeGeer --- libweston/backend-rdp/rdp.c | 25 +++++++++++++++++++------ libweston/backend-rdp/rdp.h | 3 +++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index ad411926..b77fb1ab 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -1026,7 +1026,7 @@ ignore: } static bool -rdp_notify_wheel_scroll(RdpPeerContext *peerContext, UINT16 flags) +rdp_notify_wheel_scroll(RdpPeerContext *peerContext, UINT16 flags, uint32_t axis) { struct weston_pointer_axis_event weston_event; struct rdp_backend *b = peerContext->rdpBackend; @@ -1048,10 +1048,16 @@ rdp_notify_wheel_scroll(RdpPeerContext *peerContext, UINT16 flags) * Flip the scroll direction as the RDP direction is inverse of X/Wayland * for vertical scroll */ - ivalue *= -1; + if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) { + ivalue *= -1; - accumWheelRotationPrecise = &peerContext->verticalAccumWheelRotationPrecise; - accumWheelRotationDiscrete = &peerContext->verticalAccumWheelRotationDiscrete; + accumWheelRotationPrecise = &peerContext->verticalAccumWheelRotationPrecise; + accumWheelRotationDiscrete = &peerContext->verticalAccumWheelRotationDiscrete; + } + else { + accumWheelRotationPrecise = &peerContext->horizontalAccumWheelRotationPrecise; + accumWheelRotationDiscrete = &peerContext->horizontalAccumWheelRotationDiscrete; + } /* * Accumulate the wheel increments. @@ -1071,7 +1077,7 @@ rdp_notify_wheel_scroll(RdpPeerContext *peerContext, UINT16 flags) if (abs(*accumWheelRotationPrecise) >= 12) { value = (double)(*accumWheelRotationPrecise / 12); - weston_event.axis = WL_POINTER_AXIS_VERTICAL_SCROLL; + weston_event.axis = axis; weston_event.value = value; weston_event.discrete = *accumWheelRotationDiscrete / 120; weston_event.has_discrete = true; @@ -1134,8 +1140,14 @@ xf_mouseEvent(rdpInput *input, UINT16 flags, UINT16 x, UINT16 y) need_frame = true; } + /* Per RDP spec, if both PTRFLAGS_WHEEL and PTRFLAGS_HWHEEL are specified + * then PTRFLAGS_WHEEL takes precedent + */ if (flags & PTR_FLAGS_WHEEL) { - if (rdp_notify_wheel_scroll(peerContext, flags)) + if (rdp_notify_wheel_scroll(peerContext, flags, WL_POINTER_AXIS_VERTICAL_SCROLL)) + need_frame = true; + } else if (flags & PTR_FLAGS_HWHEEL) { + if (rdp_notify_wheel_scroll(peerContext, flags, WL_POINTER_AXIS_HORIZONTAL_SCROLL)) need_frame = true; } @@ -1330,6 +1342,7 @@ rdp_peer_init(freerdp_peer *client, struct rdp_backend *b) settings->FrameMarkerCommandEnabled = TRUE; settings->SurfaceFrameMarkerEnabled = TRUE; settings->HasExtendedMouseEvent = TRUE; + settings->HasHorizontalWheel = TRUE; client->Capabilities = xf_peer_capabilities; client->PostConnect = xf_peer_post_connect; diff --git a/libweston/backend-rdp/rdp.h b/libweston/backend-rdp/rdp.h index 1d0228bc..b60c81db 100644 --- a/libweston/backend-rdp/rdp.h +++ b/libweston/backend-rdp/rdp.h @@ -115,6 +115,9 @@ struct rdp_peer_context { int verticalAccumWheelRotationPrecise; int verticalAccumWheelRotationDiscrete; + int horizontalAccumWheelRotationPrecise; + int horizontalAccumWheelRotationDiscrete; + }; typedef struct rdp_peer_context RdpPeerContext; From 2eb59129604093a0a98829256d9fd4bc7eb5983c Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Tue, 26 Apr 2022 09:17:25 -0500 Subject: [PATCH 183/609] rdp: Don't bother trying to pick an optimal keyboard model name Nothing actually cares about this string, and pc105 will do just fine. Signed-off-by: Derek Foreman --- libweston/backend-rdp/rdp.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index b77fb1ab..3067ab90 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -812,18 +812,6 @@ struct rdp_to_xkb_keyboard_layout rdp_keyboards[] = { {0x00000000, 0, 0}, }; -/* taken from 2.2.7.1.6 Input Capability Set (TS_INPUT_CAPABILITYSET) */ -static const char *rdp_keyboard_types[] = { - "", /* 0: unused */ - "", /* 1: IBM PC/XT or compatible (83-key) keyboard */ - "", /* 2: Olivetti "ICO" (102-key) keyboard */ - "", /* 3: IBM PC/AT (84-key) or similar keyboard */ - "pc102",/* 4: IBM enhanced (101- or 102-key) keyboard */ - "", /* 5: Nokia 1050 and similar keyboards */ - "", /* 6: Nokia 9140 and similar keyboards */ - "" /* 7: Japanese keyboard */ -}; - static BOOL xf_peer_activate(freerdp_peer* client) { @@ -902,8 +890,7 @@ xf_peer_activate(freerdp_peer* client) settings->KeyboardFunctionKey); memset(&xkbRuleNames, 0, sizeof(xkbRuleNames)); - if (settings->KeyboardType <= 7) - xkbRuleNames.model = rdp_keyboard_types[settings->KeyboardType]; + xkbRuleNames.model = "pc105"; for (i = 0; rdp_keyboards[i].rdpLayoutCode; i++) { if (rdp_keyboards[i].rdpLayoutCode == settings->KeyboardLayout) { xkbRuleNames.layout = rdp_keyboards[i].xkbLayout; From 06472fb136489caf65a15e342a8c899f0f495af5 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 26 Apr 2022 17:02:07 +0100 Subject: [PATCH 184/609] desktop-shell: Delete Exposay Exposay was done as a showcase for what we could do with Weston and an efficient compositing pipeline. This was mostly with the old vendor-specific Raspberry Pi backend which could actually process that many surfaces bypassing the GPU. Given enough bitrot, Exposay is now pretty exemplary as what _not_ to do in a Weston shell - particularly the way it manipulates existing weston_views rather than create its own non-destructive stack. As it's annoying technical debt, a terrible example to others, and not a very compelling showcase in 2022, just delete it. Signed-off-by: Daniel Stone --- desktop-shell/exposay.c | 737 -------------------------------------- desktop-shell/meson.build | 1 - desktop-shell/shell.c | 13 - desktop-shell/shell.h | 53 --- 4 files changed, 804 deletions(-) delete mode 100644 desktop-shell/exposay.c diff --git a/desktop-shell/exposay.c b/desktop-shell/exposay.c deleted file mode 100644 index c7c064b5..00000000 --- a/desktop-shell/exposay.c +++ /dev/null @@ -1,737 +0,0 @@ -/* - * Copyright © 2010-2012 Intel Corporation - * Copyright © 2011-2012 Collabora, Ltd. - * Copyright © 2013 Raspberry Pi Foundation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#include "config.h" - -#include -#include - -#include "shell.h" -#include "shared/helpers.h" - -struct exposay_surface { - struct desktop_shell *shell; - struct exposay_output *eoutput; - struct weston_surface *surface; - struct weston_view *view; - struct wl_listener view_destroy_listener; - struct wl_list link; - - int x; - int y; - int width; - int height; - double scale; - - int row; - int column; - - /* The animations only apply a transformation for their own lifetime, - * and don't have an option to indefinitely maintain the - * transformation in a steady state - so, we apply our own once the - * animation has finished. */ - struct weston_transform transform; -}; - -static void exposay_set_state(struct desktop_shell *shell, - enum exposay_target_state state, - struct weston_seat *seat); -static void exposay_check_state(struct desktop_shell *shell); - -static void -exposay_surface_destroy(struct exposay_surface *esurface) -{ - wl_list_remove(&esurface->link); - wl_list_remove(&esurface->view_destroy_listener.link); - - if (esurface->shell->exposay.focus_current == esurface->view) - esurface->shell->exposay.focus_current = NULL; - if (esurface->shell->exposay.focus_prev == esurface->view) - esurface->shell->exposay.focus_prev = NULL; - - free(esurface); -} - -static void -exposay_in_flight_inc(struct desktop_shell *shell) -{ - shell->exposay.in_flight++; -} - -static void -exposay_in_flight_dec(struct desktop_shell *shell) -{ - if (--shell->exposay.in_flight > 0) - return; - - exposay_check_state(shell); -} - -static void -exposay_animate_in_done(struct weston_view_animation *animation, void *data) -{ - struct exposay_surface *esurface = data; - - wl_list_insert(&esurface->view->geometry.transformation_list, - &esurface->transform.link); - weston_matrix_init(&esurface->transform.matrix); - weston_matrix_scale(&esurface->transform.matrix, - esurface->scale, esurface->scale, 1.0f); - weston_matrix_translate(&esurface->transform.matrix, - esurface->x - esurface->view->geometry.x, - esurface->y - esurface->view->geometry.y, - 0); - - weston_view_geometry_dirty(esurface->view); - weston_compositor_schedule_repaint(esurface->view->surface->compositor); - - exposay_in_flight_dec(esurface->shell); -} - -static void -exposay_animate_in(struct exposay_surface *esurface) -{ - exposay_in_flight_inc(esurface->shell); - - weston_move_scale_run(esurface->view, - esurface->x - esurface->view->geometry.x, - esurface->y - esurface->view->geometry.y, - 1.0, esurface->scale, 0, - exposay_animate_in_done, esurface); -} - -static void -exposay_animate_out_done(struct weston_view_animation *animation, void *data) -{ - struct exposay_surface *esurface = data; - struct desktop_shell *shell = esurface->shell; - - exposay_surface_destroy(esurface); - - exposay_in_flight_dec(shell); -} - -static void -exposay_animate_out(struct exposay_surface *esurface) -{ - exposay_in_flight_inc(esurface->shell); - - /* Remove the static transformation set up by - * exposay_transform_in_done(). */ - wl_list_remove(&esurface->transform.link); - weston_view_geometry_dirty(esurface->view); - - weston_move_scale_run(esurface->view, - esurface->x - esurface->view->geometry.x, - esurface->y - esurface->view->geometry.y, - 1.0, esurface->scale, 1, - exposay_animate_out_done, esurface); -} - -static void -exposay_highlight_surface(struct desktop_shell *shell, - struct exposay_surface *esurface) -{ - struct weston_view *view = esurface->view; - - if (shell->exposay.focus_current == view) - return; - - shell->exposay.row_current = esurface->row; - shell->exposay.column_current = esurface->column; - shell->exposay.cur_output = esurface->eoutput; - - activate(shell, view, shell->exposay.seat, - WESTON_ACTIVATE_FLAG_NONE); - shell->exposay.focus_current = view; -} - -static int -exposay_is_animating(struct desktop_shell *shell) -{ - if (shell->exposay.state_cur == EXPOSAY_LAYOUT_INACTIVE || - shell->exposay.state_cur == EXPOSAY_LAYOUT_OVERVIEW) - return 0; - - return (shell->exposay.in_flight > 0); -} - -static void -exposay_pick(struct desktop_shell *shell, int x, int y) -{ - struct exposay_surface *esurface; - - if (exposay_is_animating(shell)) - return; - - wl_list_for_each(esurface, &shell->exposay.surface_list, link) { - if (x < esurface->x || x > esurface->x + esurface->width) - continue; - if (y < esurface->y || y > esurface->y + esurface->height) - continue; - - exposay_highlight_surface(shell, esurface); - return; - } -} - -static void -handle_view_destroy(struct wl_listener *listener, void *data) -{ - struct exposay_surface *esurface = container_of(listener, - struct exposay_surface, - view_destroy_listener); - - exposay_surface_destroy(esurface); -} - -/* Compute each surface size and then inner pad (10% of surface size). - * After that, it's necessary to recompute surface size (90% of its - * original size). Also, each surface can't be bigger than half the - * exposay area width and height. - */ -static void -exposay_surface_and_inner_pad_size(pixman_rectangle32_t exposay_area, struct exposay_output *eoutput) -{ - if (exposay_area.height < exposay_area.width) - eoutput->surface_size = exposay_area.height / eoutput->grid_size; - else - eoutput->surface_size = exposay_area.width / eoutput->grid_size; - - eoutput->padding_inner = eoutput->surface_size / 10; - eoutput->surface_size -= eoutput->padding_inner; - - if ((uint32_t)eoutput->surface_size > (exposay_area.width / 2)) - eoutput->surface_size = exposay_area.width / 2; - if ((uint32_t)eoutput->surface_size > (exposay_area.height / 2)) - eoutput->surface_size = exposay_area.height / 2; -} - -/* Compute the exposay top/left margin in order to centralize it */ -static void -exposay_margin_size(struct desktop_shell *shell, pixman_rectangle32_t exposay_area, - int row_size, int column_size, int *left_margin, int *top_margin) -{ - (*left_margin) = exposay_area.x + (exposay_area.width - row_size) / 2; - (*top_margin) = exposay_area.y + (exposay_area.height - column_size) / 2; -} - -/* Pretty lame layout for now; just tries to make a square. Should take - * aspect ratio into account really. Also needs to be notified of surface - * addition and removal and adjust layout/animate accordingly. - * - * Lay the grid out as square as possible, losing surfaces from the - * bottom row if required. Start with fixed padding of a 10% margin - * around the outside, and maximise the area made available to surfaces - * after this. Also, add an inner padding between surfaces that varies - * with the surface size (10% of its size). - * - * If we can't make a square grid, add one extra row at the bottom which - * will have a smaller number of columns. - */ -static enum exposay_layout_state -exposay_layout(struct desktop_shell *shell, struct shell_output *shell_output) -{ - struct workspace *workspace = shell->exposay.workspace; - struct weston_output *output = shell_output->output; - struct exposay_output *eoutput = &shell_output->eoutput; - struct weston_view *view; - struct exposay_surface *esurface, *highlight = NULL; - pixman_rectangle32_t exposay_area; - int pad, row_size, column_size, left_margin, top_margin; - int last_row_size, last_row_margin_increase; - int populated_rows; - int i; - - eoutput->num_surfaces = 0; - wl_list_for_each(view, &workspace->layer.view_list.link, layer_link.link) { - if (!get_shell_surface(view->surface)) - continue; - if (view->output != output) - continue; - eoutput->num_surfaces++; - } - - if (eoutput->num_surfaces == 0) { - eoutput->grid_size = 0; - eoutput->padding_inner = 0; - eoutput->surface_size = 0; - return EXPOSAY_LAYOUT_OVERVIEW; - } - - /* Get exposay area and position, taking into account - * the shell panel position and size */ - get_output_work_area(shell, output, &exposay_area); - - /* Compute grid size */ - eoutput->grid_size = floor(sqrtf(eoutput->num_surfaces)); - if (pow(eoutput->grid_size, 2) != eoutput->num_surfaces) - eoutput->grid_size++; - - /* Compute each surface size and the inner padding between them */ - exposay_surface_and_inner_pad_size(exposay_area, eoutput); - - /* Compute each row/column size */ - pad = eoutput->surface_size + eoutput->padding_inner; - row_size = (pad * eoutput->grid_size) - eoutput->padding_inner; - /* We may have empty rows that should be desconsidered to compute - * column size */ - populated_rows = ceil(eoutput->num_surfaces / (float) eoutput->grid_size); - column_size = (pad * populated_rows) - eoutput->padding_inner; - - /* The last row size can be different, since it may have less surfaces - * than the grid size. Also, its margin may be increased to centralize - * its surfaces, in the case where we don't have a perfect grid. */ - last_row_size = ((eoutput->num_surfaces % eoutput->grid_size) * pad) - eoutput->padding_inner; - if (eoutput->num_surfaces % eoutput->grid_size) - last_row_margin_increase = (row_size - last_row_size) / 2; - else - last_row_margin_increase = 0; - - /* Compute a top/left margin to centralize the exposay */ - exposay_margin_size(shell, exposay_area, row_size, column_size, &left_margin, &top_margin); - - i = 0; - wl_list_for_each(view, &workspace->layer.view_list.link, layer_link.link) { - - if (!get_shell_surface(view->surface)) - continue; - if (view->output != output) - continue; - - esurface = malloc(sizeof(*esurface)); - if (!esurface) { - exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, - shell->exposay.seat); - break; - } - - wl_list_insert(&shell->exposay.surface_list, &esurface->link); - esurface->shell = shell; - esurface->eoutput = eoutput; - esurface->view = view; - - esurface->row = i / eoutput->grid_size; - esurface->column = i % eoutput->grid_size; - - esurface->x = left_margin + (pad * esurface->column); - esurface->y = top_margin + (pad * esurface->row); - - /* If this is the last row, increase left margin (it sums 0 if - * we have a perfect square) to centralize the surfaces */ - if (eoutput->num_surfaces / eoutput->grid_size == esurface->row) - esurface->x += last_row_margin_increase; - - if (view->surface->width > view->surface->height) - esurface->scale = eoutput->surface_size / (float) view->surface->width; - else - esurface->scale = eoutput->surface_size / (float) view->surface->height; - esurface->width = view->surface->width * esurface->scale; - esurface->height = view->surface->height * esurface->scale; - - /* Surfaces are usually rectangular, but their exposay surfaces - * are square. centralize them in their own square */ - if (esurface->width > esurface->height) - esurface->y += (esurface->width - esurface->height) / 2; - else - esurface->x += (esurface->height - esurface->width) / 2; - - if (shell->exposay.focus_current == esurface->view) - highlight = esurface; - - exposay_animate_in(esurface); - - /* We want our destroy handler to be after the animation - * destroy handler in the list, this way when the view is - * destroyed, the animation can safely call the animation - * completion callback before we free the esurface in our - * destroy handler. - */ - esurface->view_destroy_listener.notify = handle_view_destroy; - wl_signal_add(&view->destroy_signal, &esurface->view_destroy_listener); - - i++; - } - - if (highlight) { - shell->exposay.focus_current = NULL; - exposay_highlight_surface(shell, highlight); - } - - weston_compositor_schedule_repaint(shell->compositor); - - return EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW; -} - -static void -exposay_focus(struct weston_pointer_grab *grab) -{ -} - -static void -exposay_motion(struct weston_pointer_grab *grab, - const struct timespec *time, - struct weston_pointer_motion_event *event) -{ - struct desktop_shell *shell = - container_of(grab, struct desktop_shell, exposay.grab_ptr); - - weston_pointer_move(grab->pointer, event); - - exposay_pick(shell, - wl_fixed_to_int(grab->pointer->x), - wl_fixed_to_int(grab->pointer->y)); -} - -static void -exposay_button(struct weston_pointer_grab *grab, const struct timespec *time, - uint32_t button, uint32_t state_w) -{ - struct desktop_shell *shell = - container_of(grab, struct desktop_shell, exposay.grab_ptr); - struct weston_seat *seat = grab->pointer->seat; - enum wl_pointer_button_state state = state_w; - - if (button != BTN_LEFT) - return; - - /* Store the surface we clicked on, and don't do anything if we end up - * releasing on a different surface. */ - if (state == WL_POINTER_BUTTON_STATE_PRESSED) { - shell->exposay.clicked = shell->exposay.focus_current; - return; - } - - if (shell->exposay.focus_current == shell->exposay.clicked) - exposay_set_state(shell, EXPOSAY_TARGET_SWITCH, seat); - else - shell->exposay.clicked = NULL; -} - -static void -exposay_axis(struct weston_pointer_grab *grab, - const struct timespec *time, - struct weston_pointer_axis_event *event) -{ -} - -static void -exposay_axis_source(struct weston_pointer_grab *grab, uint32_t source) -{ -} - -static void -exposay_frame(struct weston_pointer_grab *grab) -{ -} - -static void -exposay_pointer_grab_cancel(struct weston_pointer_grab *grab) -{ - struct desktop_shell *shell = - container_of(grab, struct desktop_shell, exposay.grab_ptr); - - exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, shell->exposay.seat); -} - -static const struct weston_pointer_grab_interface exposay_ptr_grab = { - exposay_focus, - exposay_motion, - exposay_button, - exposay_axis, - exposay_axis_source, - exposay_frame, - exposay_pointer_grab_cancel, -}; - -static int -exposay_maybe_move(struct desktop_shell *shell, int row, int column) -{ - struct exposay_surface *esurface; - - wl_list_for_each(esurface, &shell->exposay.surface_list, link) { - if (esurface->eoutput != shell->exposay.cur_output || - esurface->row != row || esurface->column != column) - continue; - - exposay_highlight_surface(shell, esurface); - return 1; - } - - return 0; -} - -static void -exposay_key(struct weston_keyboard_grab *grab, const struct timespec *time, - uint32_t key, uint32_t state_w) -{ - struct weston_seat *seat = grab->keyboard->seat; - struct desktop_shell *shell = - container_of(grab, struct desktop_shell, exposay.grab_kbd); - enum wl_keyboard_key_state state = state_w; - - if (state != WL_KEYBOARD_KEY_STATE_RELEASED) - return; - - switch (key) { - case KEY_ESC: - exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, seat); - break; - case KEY_ENTER: - exposay_set_state(shell, EXPOSAY_TARGET_SWITCH, seat); - break; - case KEY_UP: - exposay_maybe_move(shell, shell->exposay.row_current - 1, - shell->exposay.column_current); - break; - case KEY_DOWN: - /* Special case for trying to move to the bottom row when it - * has fewer items than all the others. */ - if (!exposay_maybe_move(shell, shell->exposay.row_current + 1, - shell->exposay.column_current) && - shell->exposay.row_current < (shell->exposay.cur_output->grid_size - 1)) { - exposay_maybe_move(shell, shell->exposay.row_current + 1, - (shell->exposay.cur_output->num_surfaces % - shell->exposay.cur_output->grid_size) - 1); - } - break; - case KEY_LEFT: - exposay_maybe_move(shell, shell->exposay.row_current, - shell->exposay.column_current - 1); - break; - case KEY_RIGHT: - exposay_maybe_move(shell, shell->exposay.row_current, - shell->exposay.column_current + 1); - break; - case KEY_TAB: - /* Try to move right, then down (and to the leftmost column), - * then if all else fails, to the top left. */ - if (!exposay_maybe_move(shell, shell->exposay.row_current, - shell->exposay.column_current + 1) && - !exposay_maybe_move(shell, shell->exposay.row_current + 1, 0)) - exposay_maybe_move(shell, 0, 0); - break; - default: - break; - } -} - -static void -exposay_modifier(struct weston_keyboard_grab *grab, uint32_t serial, - uint32_t mods_depressed, uint32_t mods_latched, - uint32_t mods_locked, uint32_t group) -{ - struct desktop_shell *shell = - container_of(grab, struct desktop_shell, exposay.grab_kbd); - struct weston_seat *seat = (struct weston_seat *) grab->keyboard->seat; - - /* We want to know when mod has been pressed and released. - * FIXME: There is a problem here: if mod is pressed, then a key - * is pressed and released, then mod is released, we will treat that - * as if only mod had been pressed and released. */ - if (seat->modifier_state) { - if (seat->modifier_state == shell->binding_modifier) { - shell->exposay.mod_pressed = true; - } else { - shell->exposay.mod_invalid = true; - } - } else { - if (shell->exposay.mod_pressed && !shell->exposay.mod_invalid) - exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, seat); - - shell->exposay.mod_invalid = false; - shell->exposay.mod_pressed = false; - } - - return; -} - -static void -exposay_cancel(struct weston_keyboard_grab *grab) -{ - struct desktop_shell *shell = - container_of(grab, struct desktop_shell, exposay.grab_kbd); - - exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, shell->exposay.seat); -} - -static const struct weston_keyboard_grab_interface exposay_kbd_grab = { - exposay_key, - exposay_modifier, - exposay_cancel, -}; - -/** - * Called when the transition from overview -> inactive has completed. - */ -static enum exposay_layout_state -exposay_set_inactive(struct desktop_shell *shell) -{ - struct weston_seat *seat = shell->exposay.seat; - struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat); - struct weston_pointer *pointer = weston_seat_get_pointer(seat); - - if (pointer) - weston_pointer_end_grab(pointer); - - if (keyboard) { - weston_keyboard_end_grab(keyboard); - if (keyboard->input_method_resource) - keyboard->grab = &keyboard->input_method_grab; - } - - return EXPOSAY_LAYOUT_INACTIVE; -} - -/** - * Begins the transition from overview to inactive. */ -static enum exposay_layout_state -exposay_transition_inactive(struct desktop_shell *shell, int switch_focus) -{ - struct exposay_surface *esurface; - - /* Call activate() before we start the animations to avoid - * animating back the old state and then immediately transitioning - * to the new. */ - if (switch_focus && shell->exposay.focus_current) - activate(shell, shell->exposay.focus_current, - shell->exposay.seat, - WESTON_ACTIVATE_FLAG_CONFIGURE); - else if (shell->exposay.focus_prev) - activate(shell, shell->exposay.focus_prev, - shell->exposay.seat, - WESTON_ACTIVATE_FLAG_CONFIGURE); - - wl_list_for_each(esurface, &shell->exposay.surface_list, link) - exposay_animate_out(esurface); - weston_compositor_schedule_repaint(shell->compositor); - - return EXPOSAY_LAYOUT_ANIMATE_TO_INACTIVE; -} - -static enum exposay_layout_state -exposay_transition_active(struct desktop_shell *shell) -{ - struct weston_seat *seat = shell->exposay.seat; - struct weston_pointer *pointer = weston_seat_get_pointer(seat); - struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat); - struct shell_output *shell_output; - bool animate = false; - - shell->exposay.workspace = get_current_workspace(shell); - shell->exposay.focus_prev = get_default_view(keyboard->focus); - shell->exposay.focus_current = get_default_view(keyboard->focus); - shell->exposay.clicked = NULL; - wl_list_init(&shell->exposay.surface_list); - - lower_fullscreen_layer(shell, NULL); - shell->exposay.grab_kbd.interface = &exposay_kbd_grab; - weston_keyboard_start_grab(keyboard, - &shell->exposay.grab_kbd); - weston_keyboard_set_focus(keyboard, NULL); - - shell->exposay.grab_ptr.interface = &exposay_ptr_grab; - if (pointer) { - weston_pointer_start_grab(pointer, - &shell->exposay.grab_ptr); - weston_pointer_clear_focus(pointer); - } - wl_list_for_each(shell_output, &shell->output_list, link) { - enum exposay_layout_state state; - - state = exposay_layout(shell, shell_output); - - if (state == EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW) - animate = true; - } - - return animate ? EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW - : EXPOSAY_LAYOUT_OVERVIEW; -} - -static void -exposay_check_state(struct desktop_shell *shell) -{ - enum exposay_layout_state state_new = shell->exposay.state_cur; - int do_switch = 0; - - /* Don't do anything whilst animations are running, just store up - * target state changes and only act on them when the animations have - * completed. */ - if (exposay_is_animating(shell)) - return; - - switch (shell->exposay.state_target) { - case EXPOSAY_TARGET_OVERVIEW: - switch (shell->exposay.state_cur) { - case EXPOSAY_LAYOUT_OVERVIEW: - goto out; - case EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW: - state_new = EXPOSAY_LAYOUT_OVERVIEW; - break; - default: - state_new = exposay_transition_active(shell); - break; - } - break; - - case EXPOSAY_TARGET_SWITCH: - do_switch = 1; /* fallthrough */ - case EXPOSAY_TARGET_CANCEL: - switch (shell->exposay.state_cur) { - case EXPOSAY_LAYOUT_INACTIVE: - goto out; - case EXPOSAY_LAYOUT_ANIMATE_TO_INACTIVE: - state_new = exposay_set_inactive(shell); - break; - default: - state_new = exposay_transition_inactive(shell, do_switch); - break; - } - - break; - } - -out: - shell->exposay.state_cur = state_new; -} - -static void -exposay_set_state(struct desktop_shell *shell, enum exposay_target_state state, - struct weston_seat *seat) -{ - shell->exposay.state_target = state; - shell->exposay.seat = seat; - exposay_check_state(shell); -} - -void -exposay_binding(struct weston_keyboard *keyboard, enum weston_keyboard_modifier modifier, - void *data) -{ - struct desktop_shell *shell = data; - - exposay_set_state(shell, EXPOSAY_TARGET_OVERVIEW, keyboard->seat); -} diff --git a/desktop-shell/meson.build b/desktop-shell/meson.build index c6f374ee..8c9754e4 100644 --- a/desktop-shell/meson.build +++ b/desktop-shell/meson.build @@ -3,7 +3,6 @@ if get_option('shell-desktop') srcs_shell_desktop = [ 'shell.c', - 'exposay.c', 'input-panel.c', weston_desktop_shell_server_protocol_h, weston_desktop_shell_protocol_c, diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index e4f84ec6..0b686b92 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -490,11 +490,6 @@ shell_configuration(struct desktop_shell *shell) shell->binding_modifier = get_modifier(s); free(s); - weston_config_section_get_string(section, - "exposay-modifier", &s, "none"); - shell->exposay_modifier = get_modifier(s); - free(s); - weston_config_section_get_string(section, "animation", &s, "none"); shell->win_animation_type = get_animation_type(s); free(s); @@ -4917,11 +4912,6 @@ shell_add_bindings(struct weston_compositor *ec, struct desktop_shell *shell) weston_compositor_add_key_binding(ec, KEY_BRIGHTNESSUP, 0, backlight_binding, ec); - /* configurable bindings */ - if (shell->exposay_modifier) - weston_compositor_add_modifier_binding(ec, shell->exposay_modifier, - exposay_binding, shell); - mod = shell->binding_modifier; if (!mod) return; @@ -5048,9 +5038,6 @@ wet_shell_init(struct weston_compositor *ec, shell_configuration(shell); - shell->exposay.state_cur = EXPOSAY_LAYOUT_INACTIVE; - shell->exposay.state_target = EXPOSAY_TARGET_CANCEL; - for (i = 0; i < shell->workspaces.num; i++) { pws = wl_array_add(&shell->workspaces.array, sizeof *pws); if (pws == NULL) diff --git a/desktop-shell/shell.h b/desktop-shell/shell.h index 01af4aaf..f4cb40fd 100644 --- a/desktop-shell/shell.h +++ b/desktop-shell/shell.h @@ -45,51 +45,6 @@ enum fade_type { FADE_OUT }; -enum exposay_target_state { - EXPOSAY_TARGET_OVERVIEW, /* show all windows */ - EXPOSAY_TARGET_CANCEL, /* return to normal, same focus */ - EXPOSAY_TARGET_SWITCH, /* return to normal, switch focus */ -}; - -enum exposay_layout_state { - EXPOSAY_LAYOUT_INACTIVE = 0, /* normal desktop */ - EXPOSAY_LAYOUT_ANIMATE_TO_INACTIVE, /* in transition to normal */ - EXPOSAY_LAYOUT_OVERVIEW, /* show all windows */ - EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW, /* in transition to all windows */ -}; - -struct exposay_output { - int num_surfaces; - int grid_size; - int surface_size; - int padding_inner; -}; - -struct exposay { - /* XXX: Make these exposay_surfaces. */ - struct weston_view *focus_prev; - struct weston_view *focus_current; - struct weston_view *clicked; - struct workspace *workspace; - struct weston_seat *seat; - - struct wl_list surface_list; - - struct weston_keyboard_grab grab_kbd; - struct weston_pointer_grab grab_ptr; - - enum exposay_target_state state_target; - enum exposay_layout_state state_cur; - int in_flight; /* number of animations still running */ - - int row_current; - int column_current; - struct exposay_output *cur_output; - - bool mod_pressed; - bool mod_invalid; -}; - struct focus_surface { struct weston_curtain *curtain; struct weston_transform workspace_transform; @@ -109,7 +64,6 @@ struct workspace { struct shell_output { struct desktop_shell *shell; struct weston_output *output; - struct exposay_output eoutput; struct wl_listener destroy_listener; struct wl_list link; @@ -195,11 +149,8 @@ struct desktop_shell { struct wl_list surfaces; } input_panel; - struct exposay exposay; - bool allow_zap; uint32_t binding_modifier; - uint32_t exposay_modifier; enum animation_type win_animation_type; enum animation_type win_close_animation_type; enum animation_type startup_animation_type; @@ -245,10 +196,6 @@ void activate(struct desktop_shell *shell, struct weston_view *view, struct weston_seat *seat, uint32_t flags); -void -exposay_binding(struct weston_keyboard *keyboard, - enum weston_keyboard_modifier modifier, - void *data); int input_panel_setup(struct desktop_shell *shell); void From d2a8165bb69566284bf118713042843348652fb9 Mon Sep 17 00:00:00 2001 From: Hideyuki Nagase Date: Wed, 27 Apr 2022 14:16:06 -0500 Subject: [PATCH 185/609] rdp: Add Persian keyboard map Co-authored-by: Steve Pronovost Co-authored-by: Brenton DeGeer Signed-off-by: Hideyuki Nagase Signed-off-by: Steve Pronovost Signed-off-by: Brenton DeGeer --- libweston/backend-rdp/rdp.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index 3067ab90..edf154c6 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -42,6 +42,11 @@ #include #include "pixman-renderer.h" +/* This can be removed when we bump FreeRDP dependency past 3.0.0 in the future */ +#ifndef KBD_PERSIAN +#define KBD_PERSIAN 0x50429 +#endif + static void rdp_peer_refresh_rfx(pixman_region32_t *damage, pixman_image_t *image, freerdp_peer *peer) { @@ -754,6 +759,7 @@ struct rdp_to_xkb_keyboard_layout rdp_keyboards[] = { {KBD_LATVIAN, "lv", 0}, {KBD_LITHUANIAN_IBM, "lt", "ibm"}, {KBD_FARSI, "af", 0}, + {KBD_PERSIAN, "af", "basic"}, {KBD_VIETNAMESE, "vn", 0}, {KBD_ARMENIAN_EASTERN, "am", 0}, {KBD_AZERI_LATIN, 0, 0}, From 08f5edfe78e5761e473648877720c667af9f3dca Mon Sep 17 00:00:00 2001 From: Hideyuki Nagase Date: Wed, 27 Apr 2022 14:17:02 -0500 Subject: [PATCH 186/609] rdp: Fix Brazilian keyboard map Use the common abnt2 variant, instead of the niche nativo one. Co-authored-by: Steve Pronovost Co-authored-by: Brenton DeGeer Signed-off-by: Hideyuki Nagase Signed-off-by: Steve Pronovost Signed-off-by: Brenton DeGeer --- libweston/backend-rdp/rdp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index edf154c6..d1d3c5c5 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -811,7 +811,7 @@ struct rdp_to_xkb_keyboard_layout rdp_keyboards[] = { {KBD_IRISH, 0, 0}, {KBD_BOSNIAN_CYRILLIC, "ba", "us"}, {KBD_UNITED_STATES_DVORAK, "us", "dvorak"}, - {KBD_PORTUGUESE_BRAZILIAN_ABNT2, "br", "nativo"}, + {KBD_PORTUGUESE_BRAZILIAN_ABNT2, "br", "abnt2"}, {KBD_CANADIAN_MULTILINGUAL_STANDARD, "ca", "multix"}, {KBD_GAELIC, "ie", "CloGaelach"}, From e3a4552ecc81213f4aa2d5f871ad50186c8ee90b Mon Sep 17 00:00:00 2001 From: Hideyuki Nagase Date: Wed, 27 Apr 2022 14:22:10 -0500 Subject: [PATCH 187/609] rdp: Fix Farsi keyboard map Co-authored-by: Steve Pronovost Co-authored-by: Brenton DeGeer Signed-off-by: Hideyuki Nagase Signed-off-by: Steve Pronovost Signed-off-by: Brenton DeGeer --- libweston/backend-rdp/rdp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index d1d3c5c5..97c8ce9d 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -758,7 +758,7 @@ struct rdp_to_xkb_keyboard_layout rdp_keyboards[] = { {KBD_ESTONIAN, "ee", 0}, {KBD_LATVIAN, "lv", 0}, {KBD_LITHUANIAN_IBM, "lt", "ibm"}, - {KBD_FARSI, "af", 0}, + {KBD_FARSI, "ir", "pes"}, {KBD_PERSIAN, "af", "basic"}, {KBD_VIETNAMESE, "vn", 0}, {KBD_ARMENIAN_EASTERN, "am", 0}, From a29bcb70310b2fb13336156f895ffc4ced3598eb Mon Sep 17 00:00:00 2001 From: Hideyuki Nagase Date: Thu, 10 Mar 2022 16:18:57 -0600 Subject: [PATCH 188/609] rdp: change japanese keyboard input from kana to alphabetical Co-authored-by: Steve Pronovost Co-authored-by: Brenton DeGeer Signed-off-by: Hideyuki Nagase Signed-off-by: Steve Pronovost Signed-off-by: Brenton DeGeer --- libweston/backend-rdp/rdp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index 97c8ce9d..d79a9cec 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -732,7 +732,7 @@ struct rdp_to_xkb_keyboard_layout rdp_keyboards[] = { {KBD_ITALIAN, "it", 0}, {KBD_ITALIAN_142, "it", "nodeadkeys"}, {KBD_JAPANESE, "jp", 0}, - {KBD_JAPANESE_INPUT_SYSTEM_MS_IME2002, "jp", "kana"}, + {KBD_JAPANESE_INPUT_SYSTEM_MS_IME2002, "jp", 0}, {KBD_KOREAN, "kr", 0}, {KBD_KOREAN_INPUT_SYSTEM_IME_2000, "kr", "kr104"}, {KBD_DUTCH, "nl", 0}, From 4d5605b3a0fc7a5cd3bab6414e92e0727e2f3c56 Mon Sep 17 00:00:00 2001 From: Hideyuki Nagase Date: Tue, 22 Mar 2022 11:57:42 -0500 Subject: [PATCH 189/609] rdp: refactor xkbRuleNames code This code will eventually be used by RAIL as well, so let's split it out now. Co-authored-by: Steve Pronovost Co-authored-by: Brenton DeGeer Signed-off-by: Hideyuki Nagase Signed-off-by: Steve Pronovost Signed-off-by: Brenton DeGeer --- libweston/backend-rdp/rdp.c | 39 +++++++++++++++++++++++++------------ libweston/backend-rdp/rdp.h | 3 +++ 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index d79a9cec..9ca9ddf7 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -818,6 +818,29 @@ struct rdp_to_xkb_keyboard_layout rdp_keyboards[] = { {0x00000000, 0, 0}, }; +void +convert_rdp_keyboard_to_xkb_rule_names(UINT32 KeyboardType, + UINT32 KeyboardSubType, + UINT32 KeyboardLayout, + struct xkb_rule_names *xkbRuleNames) +{ + int i; + + memset(xkbRuleNames, 0, sizeof(*xkbRuleNames)); + xkbRuleNames->model = "pc105"; + for (i = 0; rdp_keyboards[i].rdpLayoutCode; i++) { + if (rdp_keyboards[i].rdpLayoutCode == KeyboardLayout) { + xkbRuleNames->layout = rdp_keyboards[i].xkbLayout; + xkbRuleNames->variant = rdp_keyboards[i].xkbVariant; + break; + } + } + + weston_log("%s: matching model=%s layout=%s variant=%s options=%s\n", + __func__, xkbRuleNames->model, xkbRuleNames->layout, + xkbRuleNames->variant, xkbRuleNames->options); +} + static BOOL xf_peer_activate(freerdp_peer* client) { @@ -830,7 +853,6 @@ xf_peer_activate(freerdp_peer* client) struct xkb_rule_names xkbRuleNames; struct xkb_keymap *keymap; struct weston_output *weston_output; - int i; pixman_box32_t box; pixman_region32_t damage; char seat_name[50]; @@ -895,17 +917,10 @@ xf_peer_activate(freerdp_peer* client) settings->KeyboardLayout, settings->KeyboardType, settings->KeyboardSubType, settings->KeyboardFunctionKey); - memset(&xkbRuleNames, 0, sizeof(xkbRuleNames)); - xkbRuleNames.model = "pc105"; - for (i = 0; rdp_keyboards[i].rdpLayoutCode; i++) { - if (rdp_keyboards[i].rdpLayoutCode == settings->KeyboardLayout) { - xkbRuleNames.layout = rdp_keyboards[i].xkbLayout; - xkbRuleNames.variant = rdp_keyboards[i].xkbVariant; - weston_log("%s: matching layout=%s variant=%s\n", __FUNCTION__, - xkbRuleNames.layout, xkbRuleNames.variant); - break; - } - } + convert_rdp_keyboard_to_xkb_rule_names(settings->KeyboardType, + settings->KeyboardSubType, + settings->KeyboardLayout, + &xkbRuleNames); keymap = NULL; if (xkbRuleNames.layout) { diff --git a/libweston/backend-rdp/rdp.h b/libweston/backend-rdp/rdp.h index b60c81db..a102b008 100644 --- a/libweston/backend-rdp/rdp.h +++ b/libweston/backend-rdp/rdp.h @@ -134,6 +134,9 @@ typedef struct rdp_peer_context RdpPeerContext; void rdp_debug_print(struct weston_log_scope *log_scope, bool cont, char *fmt, ...); +void +convert_rdp_keyboard_to_xkb_rule_names(UINT32 KeyboardType, UINT32 KeyboardSubType, UINT32 KeyboardLayout, struct xkb_rule_names *xkbRuleNames); + static inline struct rdp_head * to_rdp_head(struct weston_head *base) { From 5d939bc63617f99571c54ab3e8c027a3830ac106 Mon Sep 17 00:00:00 2001 From: Hideyuki Nagase Date: Wed, 27 Apr 2022 14:32:09 -0500 Subject: [PATCH 190/609] rdp: Korean keyboard support Korean keyboards are keyboard type 8: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getkeyboardtype While type 8 is not explicitly mentioned in the RDP documentation, it can be sent over the wire. Let's support the variants we can. Co-authored-by: Steve Pronovost Co-authored-by: Brenton DeGeer Signed-off-by: Hideyuki Nagase Signed-off-by: Steve Pronovost Signed-off-by: Brenton DeGeer --- libweston/backend-rdp/rdp.c | 57 +++++++++++++++++++++++++++++++++++-- libweston/backend-rdp/rdp.h | 14 +++++++++ 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index 9ca9ddf7..7ce0c719 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -836,6 +836,20 @@ convert_rdp_keyboard_to_xkb_rule_names(UINT32 KeyboardType, } } + /* Korean keyboard support (KeyboardType 8, LangID 0x412) */ + if (KeyboardType == KBD_TYPE_KOREAN && ((KeyboardLayout & 0xFFFF) == 0x412)) { + /* TODO: PC/AT 101 Enhanced Korean Keyboard (Type B) and (Type C) are not supported yet + because default Xkb settings for Korean layout don't have corresponding + configuration. + (Type B): KeyboardSubType:4: rctrl_hangul/ratl_hanja + (Type C): KeyboardSubType:5: shift_space_hangul/crtl_space_hanja + */ + if (KeyboardSubType == 0 || + KeyboardSubType == 3) /* PC/AT 101 Enhanced Korean Keyboard (Type A) */ + xkbRuleNames->variant = "kr104"; /* kr(ralt_hangul)/kr(rctrl_hanja) */ + else if (KeyboardSubType == 6) /* PC/AT 103 Enhanced Korean Keyboard */ + xkbRuleNames->variant = "kr106"; /* kr(hw_keys) */ + } weston_log("%s: matching model=%s layout=%s variant=%s options=%s\n", __func__, xkbRuleNames->model, xkbRuleNames->layout, xkbRuleNames->variant, xkbRuleNames->options); @@ -1244,7 +1258,9 @@ xf_input_keyboard_event(rdpInput *input, UINT16 flags, UINT16 code) { uint32_t scan_code, vk_code, full_code; enum wl_keyboard_key_state keyState; + freerdp_peer *client = input->context->peer; RdpPeerContext *peerContext = (RdpPeerContext *)input->context; + bool send_release_key = false; int notify = 0; struct timespec time; @@ -1264,9 +1280,37 @@ xf_input_keyboard_event(rdpInput *input, UINT16 flags, UINT16 code) if (flags & KBD_FLAGS_EXTENDED) full_code |= KBD_FLAGS_EXTENDED; - vk_code = GetVirtualKeyCodeFromVirtualScanCode(full_code, 4); - if (flags & KBD_FLAGS_EXTENDED) - vk_code |= KBDEXT; + /* Korean keyboard support: + * WinPR's GetVirtualKeyCodeFromVirtualScanCode() can't handle hangul/hanja keys + * hanja and hangeul keys are only present on Korean 103 keyboard (Type 8:SubType 6) */ + if (client->settings->KeyboardType == 8 && + client->settings->KeyboardSubType == 6 && + ((full_code == (KBD_FLAGS_EXTENDED | ATKBD_RET_HANJA)) || + (full_code == (KBD_FLAGS_EXTENDED | ATKBD_RET_HANGEUL)))) { + if (full_code == (KBD_FLAGS_EXTENDED | ATKBD_RET_HANJA)) + vk_code = VK_HANJA; + else if (full_code == (KBD_FLAGS_EXTENDED | ATKBD_RET_HANGEUL)) + vk_code = VK_HANGUL; + /* From Linux's keyboard driver at drivers/input/keyboard/atkbd.c */ + /* + * HANGEUL and HANJA keys do not send release events so we need to + * generate such events ourselves + */ + /* Similarly, for RDP there is no release for those 2 Korean keys, + * thus generate release right after press. */ + if (keyState != WL_KEYBOARD_KEY_STATE_PRESSED) { + weston_log("RDP: Received invalid key release\n"); + return TRUE; + } + send_release_key = true; + } else { + vk_code = GetVirtualKeyCodeFromVirtualScanCode(full_code, client->settings->KeyboardType); + } + /* Korean keyboard support */ + /* WinPR's GetKeycodeFromVirtualKeyCode() expects no extended bit for VK_HANGUL and VK_HANJA */ + if (vk_code != VK_HANGUL && vk_code != VK_HANJA) + if (flags & KBD_FLAGS_EXTENDED) + vk_code |= KBDEXT; scan_code = GetKeycodeFromVirtualKeyCode(vk_code, KEYCODE_TYPE_EVDEV); @@ -1275,6 +1319,13 @@ xf_input_keyboard_event(rdpInput *input, UINT16 flags, UINT16 code) weston_compositor_get_time(&time); notify_key(peerContext->item.seat, &time, scan_code - 8, keyState, STATE_UPDATE_AUTOMATIC); + + if (send_release_key) { + notify_key(peerContext->item.seat, &time, + scan_code - 8, + WL_KEYBOARD_KEY_STATE_RELEASED, + STATE_UPDATE_AUTOMATIC); + } } return TRUE; diff --git a/libweston/backend-rdp/rdp.h b/libweston/backend-rdp/rdp.h index a102b008..0ead5eb9 100644 --- a/libweston/backend-rdp/rdp.h +++ b/libweston/backend-rdp/rdp.h @@ -51,6 +51,20 @@ #define DEFAULT_AXIS_STEP_DISTANCE 10 #define DEFAULT_PIXEL_FORMAT PIXEL_FORMAT_BGRA32 +/* https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getkeyboardtype + * defines a keyboard type that isn't currently defined in FreeRDP, but is + * available for RDP connections */ +#ifndef KBD_TYPE_KOREAN +#define KBD_TYPE_KOREAN 8 +#endif + +/* WinPR's GetVirtualKeyCodeFromVirtualScanCode() can't handle hangul/hanja keys */ +/* 0x1f1 and 0x1f2 keys are only exists on Korean 103 keyboard (Type 8:SubType 6) */ + +/* From Linux's keyboard driver at drivers/input/keyboard/atkbd.c */ +#define ATKBD_RET_HANJA 0xf1 +#define ATKBD_RET_HANGEUL 0xf2 + struct rdp_output; struct rdp_backend { From 6515df13337a7d3e11cc636cc004acf6f03b6c1d Mon Sep 17 00:00:00 2001 From: Hideyuki Nagase Date: Wed, 27 Apr 2022 14:33:17 -0500 Subject: [PATCH 191/609] rdp: Support using Japanese layouts with US keyboards When RDP indicates that a Japanese keyboard layout is used without a Japanese 106/109 keyboard (keyboard type 7), use the "us" layout, since the "jp" layout in xkb expects the Japanese 106/109 keyboard layout. Co-authored-by: Steve Pronovost Co-authored-by: Brenton DeGeer Signed-off-by: Hideyuki Nagase Signed-off-by: Steve Pronovost Signed-off-by: Brenton DeGeer --- libweston/backend-rdp/rdp.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index 7ce0c719..bde4653f 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -849,7 +849,15 @@ convert_rdp_keyboard_to_xkb_rule_names(UINT32 KeyboardType, xkbRuleNames->variant = "kr104"; /* kr(ralt_hangul)/kr(rctrl_hanja) */ else if (KeyboardSubType == 6) /* PC/AT 103 Enhanced Korean Keyboard */ xkbRuleNames->variant = "kr106"; /* kr(hw_keys) */ + } else if (KeyboardType != KBD_TYPE_JAPANESE && ((KeyboardLayout & 0xFFFF) == 0x411)) { + /* when Japanese keyboard layout is used without a Japanese 106/109 + * keyboard (keyboard type 7), use the "us" layout, since the "jp" + * layout in xkb expects the Japanese 106/109 keyboard layout. + */ + xkbRuleNames->layout = "us"; + xkbRuleNames->variant = 0; } + weston_log("%s: matching model=%s layout=%s variant=%s options=%s\n", __func__, xkbRuleNames->model, xkbRuleNames->layout, xkbRuleNames->variant, xkbRuleNames->options); From b6fc6b2a8d781cd22f4b56c5394976e20f835aef Mon Sep 17 00:00:00 2001 From: Hideyuki Nagase Date: Wed, 16 Mar 2022 13:21:33 -0500 Subject: [PATCH 192/609] rdp: sync keylocks on synchronize event Synchronize events carry keylock status, so we should update it. Co-authored-by: Steve Pronovost Co-authored-by: Brenton DeGeer Signed-off-by: Hideyuki Nagase Signed-off-by: Steve Pronovost Signed-off-by: Brenton DeGeer --- libweston/backend-rdp/rdp.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index bde4653f..61b920f2 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -1237,6 +1237,7 @@ xf_input_synchronize_event(rdpInput *input, UINT32 flags) RdpPeerContext *peerCtx = (RdpPeerContext *)input->context; struct rdp_backend *b = peerCtx->rdpBackend; struct rdp_output *output = peerCtx->rdpBackend->output; + struct weston_keyboard *keyboard; pixman_box32_t box; pixman_region32_t damage; @@ -1247,6 +1248,19 @@ xf_input_synchronize_event(rdpInput *input, UINT32 flags) flags & KBD_SYNC_CAPS_LOCK ? 1 : 0, flags & KBD_SYNC_KANA_LOCK ? 1 : 0); + keyboard = weston_seat_get_keyboard(peerCtx->item.seat); + if (keyboard) { + uint32_t value = 0; + + if (flags & KBD_SYNC_NUM_LOCK) + value |= WESTON_NUM_LOCK; + if (flags & KBD_SYNC_CAPS_LOCK) + value |= WESTON_CAPS_LOCK; + weston_keyboard_set_locks(keyboard, + WESTON_NUM_LOCK | WESTON_CAPS_LOCK, + value); + } + /* sends a full refresh */ box.x1 = 0; box.y1 = 0; From 69a59359fa8590074717e211d332c1077d8f277f Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Thu, 28 Apr 2022 13:28:26 +0300 Subject: [PATCH 193/609] pixman-renderer: Unref the pixman image when wl_shm_buffer is gone Even if the weston_buffer_reference is still alive in situations like when we have closing animations, the underyling buffer (wl_shm_buffer) is no longer available. Call the appropriate destroy handler to invalidate the pixman image and avoid touch the shm_buffer. Fixes: #613 Signed-off-by: Marius Vlad --- libweston/pixman-renderer.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/libweston/pixman-renderer.c b/libweston/pixman-renderer.c index 636d9d91..d93e7ed2 100644 --- a/libweston/pixman-renderer.c +++ b/libweston/pixman-renderer.c @@ -36,6 +36,7 @@ #include "color.h" #include "pixel-formats.h" #include "shared/helpers.h" +#include "shared/signal.h" #include @@ -489,6 +490,19 @@ draw_paint_node(struct weston_paint_node *pnode, if (!ps->image) return; + /* if we still have a reference, but the underlying buffer is no longer + * available signal that we should unref image_t as well. This happens + * when using close animations, with the reference surviving the + * animation while the underlying buffer went away as the client was + * terminated. This is a particular use-case and should probably be + * refactored to provide some analogue with the GL-renderer (as in, to + * still maintain the buffer and let the compositor dispose of it). */ + if (ps->buffer_ref.buffer && !ps->buffer_ref.buffer->shm_buffer) { + pixman_image_unref(ps->image); + ps->image = NULL; + return; + } + pixman_region32_init(&repaint); pixman_region32_intersect(&repaint, &pnode->view->transform.boundingbox, damage); From 01ef3746a2b0219135cab89480cdd4d91f4f95f7 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Wed, 27 Apr 2022 17:07:57 +0300 Subject: [PATCH 194/609] simple-egl: Add start as maximized Just like start as fullscreen, let us add a start as maximized as well. It tests out the maximized state and with clients geometry checks. Signed-off-by: Marius Vlad --- clients/simple-egl.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/clients/simple-egl.c b/clients/simple-egl.c index a0f418a5..4c3ad682 100644 --- a/clients/simple-egl.c +++ b/clients/simple-egl.c @@ -404,6 +404,8 @@ create_surface(struct window *window) if (window->fullscreen) xdg_toplevel_set_fullscreen(window->xdg_toplevel, NULL); + else if (window->maximized) + xdg_toplevel_set_maximized(window->xdg_toplevel); } static void @@ -806,6 +808,7 @@ usage(int error_code) fprintf(stderr, "Usage: simple-egl [OPTIONS]\n\n" " -d \tBuffer swap delay in microseconds\n" " -f\tRun in fullscreen mode\n" + " -m\tRun in maximized mode\n" " -o\tCreate an opaque surface\n" " -s\tUse a 16 bpp EGL config\n" " -b\tDon't sync to compositor redraw (eglSwapInterval 0)\n" @@ -836,6 +839,8 @@ main(int argc, char **argv) window.delay = atoi(argv[++i]); else if (strcmp("-f", argv[i]) == 0) window.fullscreen = 1; + else if (strcmp("-m", argv[i]) == 0) + window.maximized = 1; else if (strcmp("-o", argv[i]) == 0) window.opaque = 1; else if (strcmp("-s", argv[i]) == 0) From cc877d4b779bab1ebd67663dbfe7f3b26cdddfd8 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Thu, 28 Apr 2022 10:14:00 +0300 Subject: [PATCH 195/609] libweston-desktop: Replace buffer with geometry A previous patch modified this for fullscreen, but missed out the maximized state. As we check the geometry rather than the buffer dimensions use the same terminology. Signed-off-by: Marius Vlad --- libweston-desktop/xdg-shell.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libweston-desktop/xdg-shell.c b/libweston-desktop/xdg-shell.c index ff76c39c..6cbf55e4 100644 --- a/libweston-desktop/xdg-shell.c +++ b/libweston-desktop/xdg-shell.c @@ -713,7 +713,7 @@ weston_desktop_xdg_toplevel_committed(struct weston_desktop_xdg_toplevel *toplev wl_resource_post_error(client_resource, XDG_WM_BASE_ERROR_INVALID_SURFACE_STATE, - "xdg_surface buffer (%" PRIi32 " x %" PRIi32 ") " + "xdg_surface geometry (%" PRIi32 " x %" PRIi32 ") " "does not match the configured maximized state (%" PRIi32 " x %" PRIi32 ")", geometry.width, geometry.height, toplevel->next.size.width, From c15699b7f811d21528f04085655943fe75d2a79b Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Wed, 27 Apr 2022 17:11:58 +0300 Subject: [PATCH 196/609] simple-egl: Remove uneeded check display->wm_base is checked right after handling registry object, and with it the globals, so there's no to perform and additional check for xwm_base. Signed-off-by: Marius Vlad --- clients/simple-egl.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/clients/simple-egl.c b/clients/simple-egl.c index 4c3ad682..93ff3e59 100644 --- a/clients/simple-egl.c +++ b/clients/simple-egl.c @@ -399,9 +399,6 @@ create_surface(struct window *window) if (!window->frame_sync) eglSwapInterval(display->egl.dpy, 0); - if (!display->wm_base) - return; - if (window->fullscreen) xdg_toplevel_set_fullscreen(window->xdg_toplevel, NULL); else if (window->maximized) From 0277046a1da6058ed574b5b788ebd2a2592a84ff Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Fri, 29 Apr 2022 12:28:17 +0300 Subject: [PATCH 197/609] simple-egl: Defer EGL surface/window creation Rather than creating the wl_egl_window at the same time as wl_surface, do it after we get the first configure event. With it, we also defer eglMakeCurrent() as according to the spec, the first time a OpenGL or OpenGL ES context is made current, the viewport and scissor dimensions are set to the size of the draw surface. This is particulary important when attempting to start simple-egl either as fullscreen or as maximized, as not doing so will either incorrectly commit a buffer with the original dimensions, and later on to resize to the correct dimensions (which is the case for fullscreen), or it will terminate the wayland connection abruptly due to xdg-shell protocol violation, with a mismatch for the client's geometry (the case for maximized). Signed-off-by: Marius Vlad Suggested-by: Daniel Stone --- clients/simple-egl.c | 51 ++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/clients/simple-egl.c b/clients/simple-egl.c index 93ff3e59..e48dbc69 100644 --- a/clients/simple-egl.c +++ b/clients/simple-egl.c @@ -262,6 +262,19 @@ init_gl(struct window *window) GLuint frag, vert; GLuint program; GLint status; + EGLBoolean ret; + + window->native = wl_egl_window_create(window->surface, + window->geometry.width, + window->geometry.height); + window->egl_surface = + weston_platform_create_egl_surface(window->display->egl.dpy, + window->display->egl.conf, + window->native, NULL); + + ret = eglMakeCurrent(window->display->egl.dpy, window->egl_surface, + window->egl_surface, window->display->egl.ctx); + assert(ret == EGL_TRUE); frag = create_shader(window, frag_shader_text, GL_FRAGMENT_SHADER); vert = create_shader(window, vert_shader_text, GL_VERTEX_SHADER); @@ -362,19 +375,9 @@ static void create_surface(struct window *window) { struct display *display = window->display; - EGLBoolean ret; window->surface = wl_compositor_create_surface(display->compositor); - window->native = - wl_egl_window_create(window->surface, - window->geometry.width, - window->geometry.height); - window->egl_surface = - weston_platform_create_egl_surface(display->egl.dpy, - display->egl.conf, - window->native, NULL); - window->xdg_surface = xdg_wm_base_get_xdg_surface(display->wm_base, window->surface); xdg_surface_add_listener(window->xdg_surface, @@ -392,10 +395,6 @@ create_surface(struct window *window) window->wait_for_configure = true; wl_surface_commit(window->surface); - ret = eglMakeCurrent(window->display->egl.dpy, window->egl_surface, - window->egl_surface, window->display->egl.ctx); - assert(ret == EGL_TRUE); - if (!window->frame_sync) eglSwapInterval(display->egl.dpy, 0); @@ -866,7 +865,17 @@ main(int argc, char **argv) init_egl(&display, &window); create_surface(&window); - init_gl(&window); + + /* we already have wait_for_configure set after create_surface() */ + while (running && ret != -1 && window.wait_for_configure) { + ret = wl_display_dispatch(display.display); + + /* wait until xdg_surface::configure acks the new dimensions */ + if (window.wait_for_configure) + continue; + + init_gl(&window); + } display.cursor_surface = wl_compositor_create_surface(display.compositor); @@ -876,17 +885,9 @@ main(int argc, char **argv) sigint.sa_flags = SA_RESETHAND; sigaction(SIGINT, &sigint, NULL); - /* The mainloop here is a little subtle. Redrawing will cause - * EGL to read events so we can just call - * wl_display_dispatch_pending() to handle any events that got - * queued up as a side effect. */ while (running && ret != -1) { - if (window.wait_for_configure) { - ret = wl_display_dispatch(display.display); - } else { - ret = wl_display_dispatch_pending(display.display); - redraw(&window, NULL, 0); - } + ret = wl_display_dispatch_pending(display.display); + redraw(&window, NULL, 0); } fprintf(stderr, "simple-egl exiting\n"); From 054aaa5a8b4b8a7ba9404eb35d86a1b4f992be15 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Thu, 28 Apr 2022 10:32:15 +0300 Subject: [PATCH 198/609] simple-egl: Move set_fullscreen/set_maximized before initial commit Rather than setting the fullscreen/maximized before initial wl_surface.commit, make it part of the initial window state. Signed-off-by: Marius Vlad Suggested-by: Pekka Paalanen --- clients/simple-egl.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/clients/simple-egl.c b/clients/simple-egl.c index e48dbc69..2c7059c0 100644 --- a/clients/simple-egl.c +++ b/clients/simple-egl.c @@ -392,16 +392,16 @@ create_surface(struct window *window) xdg_toplevel_set_app_id(window->xdg_toplevel, "org.freedesktop.weston.simple-egl"); + if (window->fullscreen) + xdg_toplevel_set_fullscreen(window->xdg_toplevel, NULL); + else if (window->maximized) + xdg_toplevel_set_maximized(window->xdg_toplevel); + window->wait_for_configure = true; wl_surface_commit(window->surface); if (!window->frame_sync) eglSwapInterval(display->egl.dpy, 0); - - if (window->fullscreen) - xdg_toplevel_set_fullscreen(window->xdg_toplevel, NULL); - else if (window->maximized) - xdg_toplevel_set_maximized(window->xdg_toplevel); } static void From 5f9b68d68f80769ebdb3fe9fbd9cb42eae03bc0f Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 8 Jul 2021 14:47:54 +0300 Subject: [PATCH 199/609] libweston: introduce weston_eotf_mode This is the switch to turn HDR mode on. The values in the enumeration come straight from CTA-861-G standard. Monitors advertise support for some of the HDR modes in their EDID, and I am not aware of any other way to detect if a HDR mode actually works or not. Different monitors may support different and multiple modes. Different modes may look different. Therefore the high-level choice of how to drive a HDR video sink is left for the Weston frontend to decide. All the details like what HDR metadata to use are left for the color manager. This commit adds the libweston API for backends to advertise support and for frontends to choose a mode. Backend and frontend implementations follow in other commits. The frontend API does not limit the EOTF mode to the supported ones to allow experimentation and overriding EDID. Signed-off-by: Pekka Paalanen --- include/libweston/libweston.h | 46 +++++++++++++++ libweston/backend.h | 4 ++ libweston/color.c | 17 ++++++ libweston/color.h | 3 + libweston/compositor.c | 102 ++++++++++++++++++++++++++++++++++ 5 files changed, 172 insertions(+) diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 58097205..f881b94f 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -207,6 +207,40 @@ struct weston_testsuite_data { void *test_private_data; }; +/** EOTF mode for outputs and heads + * + * A list of EOTF modes for driving displays, defined by CTA-861-G for + * Dynamic Range and Mastering InfoFrame. + * + * On heads, a bitmask of one or more entries shows which modes are claimed + * supported. + * + * On outputs, the mode to be used for driving the video sink. + * + * For traditional non-HDR sRGB, use WESTON_EOTF_MODE_SDR. + */ +enum weston_eotf_mode { + /** Invalid EOTF mode, or none supported. */ + WESTON_EOTF_MODE_NONE = 0, + + /** Traditional gamma, SDR luminance range */ + WESTON_EOTF_MODE_SDR = 0x01, + + /** Traditional gamma, HDR luminance range */ + WESTON_EOTF_MODE_TRADITIONAL_HDR = 0x02, + + /** Preceptual quantizer, SMPTE ST 2084 */ + WESTON_EOTF_MODE_ST2084 = 0x04, + + /** Hybrid log-gamma, ITU-R BT.2100 */ + WESTON_EOTF_MODE_HLG = 0x08, +}; + +/** Bitmask of all defined EOTF modes */ +#define WESTON_EOTF_MODE_ALL_MASK \ + ((uint32_t)(WESTON_EOTF_MODE_SDR | WESTON_EOTF_MODE_TRADITIONAL_HDR | \ + WESTON_EOTF_MODE_ST2084 | WESTON_EOTF_MODE_HLG)) + /** Represents a head, usually a display connector * * \rst @@ -243,6 +277,7 @@ struct weston_head { char *name; /**< head name, e.g. connector name */ bool connected; /**< is physically connected */ bool non_desktop; /**< non-desktop display, e.g. HMD */ + uint32_t supported_eotf_mask; /**< supported weston_eotf_mode bits */ /** Current content protection status */ enum weston_hdcp_protection current_protection; @@ -361,6 +396,7 @@ struct weston_output { struct weston_color_transform *from_sRGB_to_blend; struct weston_color_transform *from_blend_to_output; bool from_blend_to_output_by_backend; + enum weston_eotf_mode eotf_mode; int (*enable)(struct weston_output *output); int (*disable)(struct weston_output *output); @@ -2107,6 +2143,13 @@ bool weston_output_set_color_profile(struct weston_output *output, struct weston_color_profile *cprof); +void +weston_output_set_eotf_mode(struct weston_output *output, + enum weston_eotf_mode eotf_mode); + +enum weston_eotf_mode +weston_output_get_eotf_mode(const struct weston_output *output); + void weston_output_init(struct weston_output *output, struct weston_compositor *compositor, @@ -2121,6 +2164,9 @@ weston_output_enable(struct weston_output *output); void weston_output_disable(struct weston_output *output); +uint32_t +weston_output_get_supported_eotf_modes(struct weston_output *output); + void weston_compositor_flush_heads_changed(struct weston_compositor *compositor); diff --git a/libweston/backend.h b/libweston/backend.h index 4b19b399..26624cf9 100644 --- a/libweston/backend.h +++ b/libweston/backend.h @@ -136,6 +136,10 @@ weston_head_set_subpixel(struct weston_head *head, void weston_head_set_transform(struct weston_head *head, uint32_t transform); +void +weston_head_set_supported_eotf_mask(struct weston_head *head, + uint32_t eotf_mask); + /* weston_output */ void diff --git a/libweston/color.c b/libweston/color.c index eb1d45eb..c2605fd7 100644 --- a/libweston/color.c +++ b/libweston/color.c @@ -297,3 +297,20 @@ out_close: close(fd); return cprof; } + +/** Get a string naming the EOTF mode + * + * \internal + */ +WL_EXPORT const char * +weston_eotf_mode_to_str(enum weston_eotf_mode e) +{ + switch (e) { + case WESTON_EOTF_MODE_NONE: return "(none)"; + case WESTON_EOTF_MODE_SDR: return "SDR"; + case WESTON_EOTF_MODE_TRADITIONAL_HDR: return "traditional gamma HDR"; + case WESTON_EOTF_MODE_ST2084: return "ST2084"; + case WESTON_EOTF_MODE_HLG: return "HLG"; + } + return "???"; +} diff --git a/libweston/color.h b/libweston/color.h index 4667fcad..4db42e71 100644 --- a/libweston/color.h +++ b/libweston/color.h @@ -379,4 +379,7 @@ weston_color_manager_noop_create(struct weston_compositor *compositor); struct weston_color_manager * weston_color_manager_create(struct weston_compositor *compositor); +const char * +weston_eotf_mode_to_str(enum weston_eotf_mode e); + #endif /* WESTON_COLOR_H */ diff --git a/libweston/compositor.c b/libweston/compositor.c index 50598ef3..b7944b84 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -5437,6 +5437,7 @@ weston_head_init(struct weston_head *head, const char *name) wl_list_init(&head->resource_list); wl_list_init(&head->xdg_output_resource_list); head->name = strdup(name); + head->supported_eotf_mask = WESTON_EOTF_MODE_SDR; head->current_protection = WESTON_HDCP_DISABLE; } @@ -5962,6 +5963,31 @@ weston_head_set_connection_status(struct weston_head *head, bool connected) weston_head_set_device_changed(head); } +/** Store the set of supported EOTF modes + * + * \param head The head to modify. + * \param eotf_mask A bit mask with the possible bits or'ed together from + * enum weston_eotf_mode. + * + * This may set the device_changed flag. + * + * \ingroup head + * \internal + */ +WL_EXPORT void +weston_head_set_supported_eotf_mask(struct weston_head *head, + uint32_t eotf_mask) +{ + assert((eotf_mask & ~WESTON_EOTF_MODE_ALL_MASK) == 0); + + if (head->supported_eotf_mask == eotf_mask) + return; + + head->supported_eotf_mask = eotf_mask; + + weston_head_set_device_changed(head); +} + static void weston_output_compute_protection(struct weston_output *output) { @@ -6714,6 +6740,50 @@ weston_output_set_color_profile(struct weston_output *output, return true; } +/** Set EOTF mode on an output + * + * \param output The output to modify, must be in disabled state. + * \param eotf_mode The EOTF mode to set. + * + * Setting the output EOTF mode is used for turning HDR on/off. There are + * multiple modes for HDR on, see enum weston_eotf_mode. This is the high level + * choice on how to drive a video sink (monitor), either in the traditional + * SDR mode or in one of the HDR modes. + * + * After attaching heads to an output, you can find out the possibly supported + * EOTF modes with weston_output_get_supported_eotf_modes(). + * + * This function does not check whether the given eotf_mode is actually + * supported on the output. Enabling an output with an unsupported EOTF mode + * has undefined visual results. + * + * The initial EOTF mode is SDR. + * + * \ingroup output + */ +WL_EXPORT void +weston_output_set_eotf_mode(struct weston_output *output, + enum weston_eotf_mode eotf_mode) +{ + assert(!output->enabled); + + output->eotf_mode = eotf_mode; +} + +/** Get EOTF mode of an output + * + * \param output The output to query. + * \return The EOTF mode. + * + * \sa weston_output_set_eotf_mode + * \ingroup output + */ +WL_EXPORT enum weston_eotf_mode +weston_output_get_eotf_mode(const struct weston_output *output) +{ + return output->eotf_mode; +} + /** Initializes a weston_output object with enough data so ** an output can be configured. * @@ -6741,6 +6811,7 @@ weston_output_init(struct weston_output *output, wl_list_init(&output->link); wl_signal_init(&output->user_destroy_signal); output->enabled = false; + output->eotf_mode = WESTON_EOTF_MODE_SDR; output->desired_protection = WESTON_HDCP_DISABLE; output->allow_protection = true; @@ -6903,6 +6974,9 @@ weston_output_enable(struct weston_output *output) wl_list_init(&output->paint_node_list); wl_list_init(&output->paint_node_z_order_list); + weston_log("Output '%s' attempts EOTF mode: %s\n", output->name, + weston_eotf_mode_to_str(output->eotf_mode)); + if (!weston_output_set_color_transforms(output)) return -1; @@ -7222,6 +7296,34 @@ weston_output_allow_protection(struct weston_output *output, output->allow_protection = allow_protection; } +/** Get supported EOTF modes as a bit mask + * + * \param output The output to query. + * \return A bit mask with values from enum weston_eotf_mode or'ed together. + * + * Returns the bit mask of the EOTF modes that all the currently attached + * heads claim to support. Adding or removing heads may change the result. + * An output can be queried regrdless of whether it is enabled or disabled. + * + * If no heads are attached, no EOTF modes are deemed supported. + * + * \ingroup output + */ +WL_EXPORT uint32_t +weston_output_get_supported_eotf_modes(struct weston_output *output) +{ + uint32_t eotf_modes = WESTON_EOTF_MODE_ALL_MASK; + struct weston_head *head; + + if (wl_list_empty(&output->head_list)) + return WESTON_EOTF_MODE_NONE; + + wl_list_for_each(head, &output->head_list, output_link) + eotf_modes = eotf_modes & head->supported_eotf_mask; + + return eotf_modes; +} + static void xdg_output_unlist(struct wl_resource *resource) { From 1e9b1a104734bcaafd4497127de45df13285be75 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Wed, 16 Mar 2022 14:27:55 +0200 Subject: [PATCH 200/609] backend-headless: support all EOTF modes The headless backend does not display to anything, so it doesn't care what the EOTF mode is. To allow testing compositor internal behavior, claim to support all EOTF modes. Signed-off-by: Pekka Paalanen --- libweston/backend-headless/headless.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libweston/backend-headless/headless.c b/libweston/backend-headless/headless.c index 5f2f2898..7165ce40 100644 --- a/libweston/backend-headless/headless.c +++ b/libweston/backend-headless/headless.c @@ -361,6 +361,8 @@ headless_head_create(struct weston_compositor *compositor, weston_head_init(&head->base, name); weston_head_set_connection_status(&head->base, true); + weston_head_set_supported_eotf_mask(&head->base, + WESTON_EOTF_MODE_ALL_MASK); /* Ideally all attributes of the head would be set here, so that the * user has all the information when deciding to create outputs. From 46c0383c14d57a99d6a4ba715aead578c5293b7b Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 12 Jul 2021 14:23:11 +0300 Subject: [PATCH 201/609] color-noop: supports only SDR EOTF mode The no-op color manager will not support any other EOTF mode than SDR. Other modes would require it to set up color transformations. Signed-off-by: Pekka Paalanen --- libweston/color-noop.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/libweston/color-noop.c b/libweston/color-noop.c index 4b0a0c38..0b67d5a6 100644 --- a/libweston/color-noop.c +++ b/libweston/color-noop.c @@ -35,6 +35,18 @@ struct weston_color_manager_noop { struct weston_color_manager base; }; +static bool +check_output_eotf_mode(struct weston_output *output) +{ + if (output->eotf_mode == WESTON_EOTF_MODE_SDR) + return true; + + weston_log("Error: color manager no-op does not support EOTF mode %s of output %s.\n", + weston_eotf_mode_to_str(output->eotf_mode), + output->name); + return false; +} + static struct weston_color_manager_noop * get_cmnoop(struct weston_color_manager *cm_base) { @@ -74,6 +86,9 @@ cmnoop_get_surface_color_transform(struct weston_color_manager *cm_base, /* TODO: Assert surface has no colorspace set */ assert(output->color_profile == NULL); + if (!check_output_eotf_mode(output)) + return false; + /* Identity transform */ surf_xform->transform = NULL; surf_xform->identity_pipeline = true; @@ -88,6 +103,9 @@ cmnoop_get_output_color_transform(struct weston_color_manager *cm_base, { assert(output->color_profile == NULL); + if (!check_output_eotf_mode(output)) + return false; + /* Identity transform */ *xform_out = NULL; @@ -101,6 +119,9 @@ cmnoop_get_sRGB_to_output_color_transform(struct weston_color_manager *cm_base, { assert(output->color_profile == NULL); + if (!check_output_eotf_mode(output)) + return false; + /* Identity transform */ *xform_out = NULL; @@ -114,6 +135,9 @@ cmnoop_get_sRGB_to_blend_color_transform(struct weston_color_manager *cm_base, { assert(output->color_profile == NULL); + if (!check_output_eotf_mode(output)) + return false; + /* Identity transform */ *xform_out = NULL; From 271c11e9dc54dfa15e2412d096eae3ade2c44fff Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 12 Jul 2021 14:27:55 +0300 Subject: [PATCH 202/609] color-lcms: todo for eotf_mode A reminder that this variable needs to be taken into account when crafting color transformations. Signed-off-by: Pekka Paalanen --- libweston/color-lcms/color-lcms.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libweston/color-lcms/color-lcms.c b/libweston/color-lcms/color-lcms.c index 5e3b52e5..23d2b79d 100644 --- a/libweston/color-lcms/color-lcms.c +++ b/libweston/color-lcms/color-lcms.c @@ -102,6 +102,8 @@ cmlcms_get_surface_color_transform(struct weston_color_manager *cm_base, struct cmlcms_color_transform_search_param param = {}; struct cmlcms_color_transform *xform; + /* TODO: take weston_output::eotf_mode into account */ + setup_search_param(CMLCMS_CATEGORY_INPUT_TO_BLEND, surface, output, cm->sRGB_profile, ¶m); @@ -132,6 +134,8 @@ cmlcms_get_output_color_transform(struct weston_color_manager *cm_base, struct cmlcms_color_transform_search_param param = {}; struct cmlcms_color_transform *xform; + /* TODO: take weston_output::eotf_mode into account */ + setup_search_param(CMLCMS_CATEGORY_BLEND_TO_OUTPUT, NULL, output, cm->sRGB_profile, ¶m); @@ -152,6 +156,8 @@ cmlcms_get_sRGB_to_output_color_transform(struct weston_color_manager *cm_base, struct cmlcms_color_transform_search_param param = {}; struct cmlcms_color_transform *xform; + /* TODO: take weston_output::eotf_mode into account */ + setup_search_param(CMLCMS_CATEGORY_INPUT_TO_OUTPUT, NULL, output, cm->sRGB_profile, ¶m); /* @@ -179,6 +185,8 @@ cmlcms_get_sRGB_to_blend_color_transform(struct weston_color_manager *cm_base, struct cmlcms_color_transform_search_param param = {}; struct cmlcms_color_transform *xform; + /* TODO: take weston_output::eotf_mode into account */ + setup_search_param(CMLCMS_CATEGORY_INPUT_TO_BLEND, NULL, output, cm->sRGB_profile, ¶m); From 6914064066b95cd65883c2c987c5f80f0a48b910 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 2 Jul 2021 16:38:09 +0300 Subject: [PATCH 203/609] backend-drm: add HDR_OUTPUT_METADATA definitions These are fallback definitions in case libdrm is not new enough. They are copied from libdrm 2.4.107. struct hdr_output_metadata defines the contents of the blob to be used with the connector property "HDR_OUTPUT_METADATA". This is needed for programming a HDR mode in KMS. This headers need to be excluded from Doxygen, because Doxygen chokes on the kerneldoc markup. Signed-off-by: Pekka Paalanen --- doc/sphinx/doxygen.ini.in | 2 +- libweston/backend-drm/libdrm-updates.h | 124 +++++++++++++++++++++++++ libweston/backend-drm/meson.build | 5 + 3 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 libweston/backend-drm/libdrm-updates.h diff --git a/doc/sphinx/doxygen.ini.in b/doc/sphinx/doxygen.ini.in index bfe77e8e..76b5b363 100644 --- a/doc/sphinx/doxygen.ini.in +++ b/doc/sphinx/doxygen.ini.in @@ -888,7 +888,7 @@ EXCLUDE_SYMLINKS = NO # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* -EXCLUDE_PATTERNS = +EXCLUDE_PATTERNS = */backend-drm/libdrm-updates.h # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the diff --git a/libweston/backend-drm/libdrm-updates.h b/libweston/backend-drm/libdrm-updates.h new file mode 100644 index 00000000..73a24b67 --- /dev/null +++ b/libweston/backend-drm/libdrm-updates.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2007 Dave Airlie + * Copyright (c) 2007 Jakob Bornecrantz + * Copyright (c) 2008 Red Hat Inc. + * Copyright (c) 2007-2008 Tungsten Graphics, Inc., Cedar Park, TX., USA + * Copyright (c) 2007-2008 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#pragma once + +#include + +/* + * This header contains fallback definitions copied from libdrm upstream to + * avoid hard-depending on too new libdrm. + */ + +#if !HAVE_LIBDRM_HDR + +/** + * struct hdr_metadata_infoframe - HDR Metadata Infoframe Data. + * + * HDR Metadata Infoframe as per CTA 861.G spec. This is expected + * to match exactly with the spec. + * + * Userspace is expected to pass the metadata information as per + * the format described in this structure. + */ +struct hdr_metadata_infoframe { + /** + * @eotf: Electro-Optical Transfer Function (EOTF) + * used in the stream. + */ + __u8 eotf; + /** + * @metadata_type: Static_Metadata_Descriptor_ID. + */ + __u8 metadata_type; + /** + * @display_primaries: Color Primaries of the Data. + * These are coded as unsigned 16-bit values in units of + * 0.00002, where 0x0000 represents zero and 0xC350 + * represents 1.0000. + * @display_primaries.x: X cordinate of color primary. + * @display_primaries.y: Y cordinate of color primary. + */ + struct { + __u16 x, y; + } display_primaries[3]; + /** + * @white_point: White Point of Colorspace Data. + * These are coded as unsigned 16-bit values in units of + * 0.00002, where 0x0000 represents zero and 0xC350 + * represents 1.0000. + * @white_point.x: X cordinate of whitepoint of color primary. + * @white_point.y: Y cordinate of whitepoint of color primary. + */ + struct { + __u16 x, y; + } white_point; + /** + * @max_display_mastering_luminance: Max Mastering Display Luminance. + * This value is coded as an unsigned 16-bit value in units of 1 cd/m2, + * where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2. + */ + __u16 max_display_mastering_luminance; + /** + * @min_display_mastering_luminance: Min Mastering Display Luminance. + * This value is coded as an unsigned 16-bit value in units of + * 0.0001 cd/m2, where 0x0001 represents 0.0001 cd/m2 and 0xFFFF + * represents 6.5535 cd/m2. + */ + __u16 min_display_mastering_luminance; + /** + * @max_cll: Max Content Light Level. + * This value is coded as an unsigned 16-bit value in units of 1 cd/m2, + * where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2. + */ + __u16 max_cll; + /** + * @max_fall: Max Frame Average Light Level. + * This value is coded as an unsigned 16-bit value in units of 1 cd/m2, + * where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2. + */ + __u16 max_fall; +}; + +/** + * struct hdr_output_metadata - HDR output metadata + * + * Metadata Information to be passed from userspace + */ +struct hdr_output_metadata { + /** + * @metadata_type: Static_Metadata_Descriptor_ID. + */ + __u32 metadata_type; + /** + * @hdmi_metadata_type1: HDR Metadata Infoframe. + */ + union { + struct hdr_metadata_infoframe hdmi_metadata_type1; + }; +}; + +#endif /* !HAVE_LIBDRM_HDR */ diff --git a/libweston/backend-drm/meson.build b/libweston/backend-drm/meson.build index 23db9127..9e7c0cff 100644 --- a/libweston/backend-drm/meson.build +++ b/libweston/backend-drm/meson.build @@ -19,6 +19,11 @@ dep_backlight = declare_dependency( config_h.set('BUILD_DRM_COMPOSITOR', '1') +config_h.set10( + 'HAVE_LIBDRM_HDR', + dep_libdrm.version().version_compare('>= 2.4.104') +) + srcs_drm = [ 'drm.c', 'fb.c', From 1d17e4991f37feb67e63fbde67b5e02a44058f16 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 12 Jul 2021 17:30:06 +0300 Subject: [PATCH 204/609] backend-drm: check for HDR_OUTPUT_METADATA Check whether HDR_OUTPUT_METADATA property exists on a KMS connector. If yes, pretend that EDID claims support for all EOTF modes and update the head supported EOTFs mask accordingly. If not, then only SDR is possible. Parsing EDID to take monitor capabilities into account is left for later. HDR mode cannot be set without HDR_OUTPUT_METADATA. Signed-off-by: Pekka Paalanen --- libweston/backend-drm/drm-internal.h | 1 + libweston/backend-drm/drm.c | 9 +++++++ libweston/backend-drm/kms.c | 3 +++ libweston/backend-drm/modes.c | 25 ++++++++++++++++++-- libweston/color.c | 35 ++++++++++++++++++++++++++++ libweston/color.h | 3 +++ 6 files changed, 74 insertions(+), 2 deletions(-) diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index f9d21e3b..6471357f 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -186,6 +186,7 @@ enum wdrm_connector_property { WDRM_CONNECTOR_CONTENT_PROTECTION, WDRM_CONNECTOR_HDCP_CONTENT_TYPE, WDRM_CONNECTOR_PANEL_ORIENTATION, + WDRM_CONNECTOR_HDR_OUTPUT_METADATA, WDRM_CONNECTOR__COUNT }; diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index 225d47e2..d7e91e35 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -2053,12 +2053,21 @@ drm_connector_fini(struct drm_connector *connector) static void drm_head_log_info(struct drm_head *head, const char *msg) { + char *eotf_list; + if (head->base.connected) { weston_log("DRM: head '%s' %s, connector %d is connected, " "EDID make '%s', model '%s', serial '%s'\n", head->base.name, msg, head->connector.connector_id, head->base.make, head->base.model, head->base.serial_number ?: ""); + eotf_list = weston_eotf_mask_to_str(head->base.supported_eotf_mask); + if (eotf_list) { + weston_log_continue(STAMP_SPACE + "Supported EOTF modes: %s\n", + eotf_list); + } + free(eotf_list); } else { weston_log("DRM: head '%s' %s, connector %d is disconnected.\n", head->base.name, msg, head->connector.connector_id); diff --git a/libweston/backend-drm/kms.c b/libweston/backend-drm/kms.c index 780d0070..7ec274a2 100644 --- a/libweston/backend-drm/kms.c +++ b/libweston/backend-drm/kms.c @@ -143,6 +143,9 @@ const struct drm_property_info connector_props[] = { .enum_values = panel_orientation_enums, .num_enum_values = WDRM_PANEL_ORIENTATION__COUNT, }, + [WDRM_CONNECTOR_HDR_OUTPUT_METADATA] = { + .name = "HDR_OUTPUT_METADATA", + }, }; const struct drm_property_info crtc_props[] = { diff --git a/libweston/backend-drm/modes.c b/libweston/backend-drm/modes.c index a071375b..1c9d1068 100644 --- a/libweston/backend-drm/modes.c +++ b/libweston/backend-drm/modes.c @@ -304,6 +304,8 @@ edid_parse(struct drm_edid *edid, const uint8_t *data, size_t length) * \param[out] make The monitor make (PNP ID). * \param[out] model The monitor model (name). * \param[out] serial_number The monitor serial number. + * \param[out] eotf_mask The monitor supported EOTF modes, combination of + * enum weston_eotf_mode bits. * * Each of \c *make, \c *model and \c *serial_number are set only if the * information is found in the EDID. The pointers they are set to must not @@ -315,7 +317,8 @@ find_and_parse_output_edid(struct drm_head *head, drmModeObjectPropertiesPtr props, const char **make, const char **model, - const char **serial_number) + const char **serial_number, + uint32_t *eotf_mask) { drmModePropertyBlobPtr edid_blob = NULL; uint32_t blob_id; @@ -344,6 +347,21 @@ find_and_parse_output_edid(struct drm_head *head, *serial_number = head->edid.serial_number; } drmModeFreePropertyBlob(edid_blob); + + /* TODO: parse this from EDID */ + *eotf_mask = WESTON_EOTF_MODE_ALL_MASK; +} + +static void +prune_eotf_modes_by_kms_support(struct drm_head *head, uint32_t *eotf_mask) +{ + const struct drm_property_info *info; + + /* Without the KMS property, cannot do anything but SDR. */ + + info = &head->connector.props[WDRM_CONNECTOR_HDR_OUTPUT_METADATA]; + if (info->prop_id == 0) + *eotf_mask = WESTON_EOTF_MODE_SDR; } static uint32_t @@ -515,9 +533,12 @@ update_head_from_connector(struct drm_head *head) const char *make = "unknown"; const char *model = "unknown"; const char *serial_number = "unknown"; + uint32_t eotf_mask = WESTON_EOTF_MODE_SDR; - find_and_parse_output_edid(head, props, &make, &model, &serial_number); + find_and_parse_output_edid(head, props, &make, &model, &serial_number, &eotf_mask); weston_head_set_monitor_strings(&head->base, make, model, serial_number); + prune_eotf_modes_by_kms_support(head, &eotf_mask); + weston_head_set_supported_eotf_mask(&head->base, eotf_mask); weston_head_set_non_desktop(&head->base, check_non_desktop(connector, props)); weston_head_set_subpixel(&head->base, diff --git a/libweston/color.c b/libweston/color.c index c2605fd7..27991901 100644 --- a/libweston/color.c +++ b/libweston/color.c @@ -314,3 +314,38 @@ weston_eotf_mode_to_str(enum weston_eotf_mode e) } return "???"; } + +/** A list of EOTF modes as a string + * + * \param eotf_mask Bitwise-or'd enum weston_eotf_mode values. + * \return Comma separated names of the listed EOTF modes. Must be free()'d by + * the caller. + */ +WL_EXPORT char * +weston_eotf_mask_to_str(uint32_t eotf_mask) +{ + FILE *fp; + char *str = NULL; + size_t size = 0; + unsigned i; + const char *sep = ""; + + fp = open_memstream(&str, &size); + if (!fp) + return NULL; + + for (i = 0; eotf_mask; i++) { + uint32_t bitmask = 1u << i; + + if (eotf_mask & bitmask) { + fprintf(fp, "%s%s", sep, + weston_eotf_mode_to_str(bitmask)); + sep = ", "; + } + + eotf_mask &= ~bitmask; + } + fclose(fp); + + return str; +} diff --git a/libweston/color.h b/libweston/color.h index 4db42e71..7c1335fa 100644 --- a/libweston/color.h +++ b/libweston/color.h @@ -382,4 +382,7 @@ weston_color_manager_create(struct weston_compositor *compositor); const char * weston_eotf_mode_to_str(enum weston_eotf_mode e); +char * +weston_eotf_mask_to_str(uint32_t eotf_mask); + #endif /* WESTON_COLOR_H */ From 5151f9fe9e9bacc6860931af60adba28cfd431a7 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Wed, 14 Jul 2021 15:01:48 +0300 Subject: [PATCH 205/609] backend-drm: program HDR_OUTPUT_METADATA Program the connector property HDR_OUTPUT_METADATA based on the EOTF mode of the output. For now, this changes only the EOTF. The colorimetry and luminance are left undefined, to be filled in by later patches. This should still be enough to put a video sink into HDR mode, albeit the response is probably unknown. drm_output keeps track of the currently existing blob id. If the blob contents need to be re-created, this blob would be destroyed and the field set to zero. In this patch, there is no provision for runtime changing of HDR metadata, so there is no code doing that. Destroying the blob at arbitrary times is not a problem, because the kernel keeps a reference to the data as long as the blob id remains with KMS. Signed-off-by: Pekka Paalanen --- libweston/backend-drm/drm-internal.h | 2 + libweston/backend-drm/drm.c | 77 ++++++++++++++++++++++++++++ libweston/backend-drm/kms.c | 10 +++- libweston/backend-drm/modes.c | 2 +- 4 files changed, 89 insertions(+), 2 deletions(-) diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index 6471357f..706dfcab 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -549,6 +549,8 @@ struct drm_output { uint32_t gbm_format; uint32_t gbm_bo_flags; + uint32_t hdr_output_metadata_blob_id; + /* Plane being displayed directly on the CRTC */ struct drm_plane *scanout_plane; diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index d7e91e35..46b809be 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -51,6 +51,7 @@ #include #include #include "drm-internal.h" +#include "libdrm-updates.h" #include "shared/helpers.h" #include "shared/timespec-util.h" #include "shared/string-helpers.h" @@ -440,6 +441,71 @@ drm_output_render(struct drm_output_state *state, pixman_region32_t *damage) pixman_region32_fini(&scanout_damage); } +static int +drm_output_ensure_hdr_output_metadata_blob(struct drm_output *output) +{ + struct hdr_output_metadata meta; + uint32_t blob_id = 0; + int ret; + + if (output->hdr_output_metadata_blob_id) + return 0; + + /* + * Set up the data for Dynamic Range and Mastering InfoFrame, + * CTA-861-G, a.k.a the static HDR metadata. + */ + + memset(&meta, 0, sizeof meta); + + meta.metadata_type = 0; /* Static Metadata Type 1 */ + + /* Duplicated field in UABI struct */ + meta.hdmi_metadata_type1.metadata_type = meta.metadata_type; + + switch (output->base.eotf_mode) { + case WESTON_EOTF_MODE_NONE: + assert(0 && "bad eotf_mode: none"); + return -1; + case WESTON_EOTF_MODE_SDR: + /* + * Do not send any static HDR metadata. Video sinks should + * respond by switching to traditional SDR mode. If they + * do not, the kernel should fix that up. + */ + assert(output->hdr_output_metadata_blob_id == 0); + return 0; + case WESTON_EOTF_MODE_TRADITIONAL_HDR: + meta.hdmi_metadata_type1.eotf = 1; /* from CTA-861-G */ + break; + case WESTON_EOTF_MODE_ST2084: + meta.hdmi_metadata_type1.eotf = 2; /* from CTA-861-G */ + break; + case WESTON_EOTF_MODE_HLG: + meta.hdmi_metadata_type1.eotf = 3; /* from CTA-861-G */ + break; + } + + if (meta.hdmi_metadata_type1.eotf == 0) { + assert(0 && "bad eotf_mode"); + return -1; + } + + /* The other fields are intentionally left as zeroes. */ + + ret = drmModeCreatePropertyBlob(output->backend->drm.fd, + &meta, sizeof meta, &blob_id); + if (ret != 0) { + weston_log("Error: failed to create KMS blob for HDR metadata on output '%s': %s\n", + output->base.name, strerror(-ret)); + return -1; + } + + output->hdr_output_metadata_blob_id = blob_id; + + return 0; +} + static int drm_output_repaint(struct weston_output *output_base, pixman_region32_t *damage) { @@ -474,6 +540,9 @@ drm_output_repaint(struct weston_output *output_base, pixman_region32_t *damage) else state->protection = WESTON_HDCP_DISABLE; + if (drm_output_ensure_hdr_output_metadata_blob(output) < 0) + goto err; + drm_output_render(state, damage); scanout_state = drm_output_state_get_plane(state, output->scanout_plane); @@ -1845,6 +1914,12 @@ drm_output_deinit(struct weston_output *base) drm_output_deinit_planes(output); drm_output_detach_crtc(output); + + if (output->hdr_output_metadata_blob_id) { + drmModeDestroyPropertyBlob(b->drm.fd, + output->hdr_output_metadata_blob_id); + output->hdr_output_metadata_blob_id = 0; + } } static void @@ -1879,6 +1954,8 @@ drm_output_destroy(struct weston_output *base) assert(!output->state_last); drm_output_state_free(output->state_cur); + assert(output->hdr_output_metadata_blob_id == 0); + free(output); } diff --git a/libweston/backend-drm/kms.c b/libweston/backend-drm/kms.c index 7ec274a2..6e2d29b2 100644 --- a/libweston/backend-drm/kms.c +++ b/libweston/backend-drm/kms.c @@ -973,10 +973,18 @@ drm_output_apply_state_atomic(struct drm_output_state *state, WDRM_CONNECTOR_CRTC_ID, 0); } - wl_list_for_each(head, &output->base.head_list, base.output_link) + wl_list_for_each(head, &output->base.head_list, base.output_link) { drm_connector_set_hdcp_property(&head->connector, state->protection, req); + if (drm_connector_has_prop(&head->connector, + WDRM_CONNECTOR_HDR_OUTPUT_METADATA)) { + ret |= connector_add_prop(req, &head->connector, + WDRM_CONNECTOR_HDR_OUTPUT_METADATA, + output->hdr_output_metadata_blob_id); + } + } + if (ret != 0) { weston_log("couldn't set atomic CRTC/connector state\n"); return ret; diff --git a/libweston/backend-drm/modes.c b/libweston/backend-drm/modes.c index 1c9d1068..f6b4248f 100644 --- a/libweston/backend-drm/modes.c +++ b/libweston/backend-drm/modes.c @@ -360,7 +360,7 @@ prune_eotf_modes_by_kms_support(struct drm_head *head, uint32_t *eotf_mask) /* Without the KMS property, cannot do anything but SDR. */ info = &head->connector.props[WDRM_CONNECTOR_HDR_OUTPUT_METADATA]; - if (info->prop_id == 0) + if (!head->backend->atomic_modeset || info->prop_id == 0) *eotf_mask = WESTON_EOTF_MODE_SDR; } From 33d553f83381fbdbbf75c2e008b4947a3abb5257 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 16 Jul 2021 17:43:43 +0300 Subject: [PATCH 206/609] compositor: add eotf-mode weston.ini option This per-output option allows to choose one of the HDR video modes. Signed-off-by: Pekka Paalanen --- compositor/main.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++ man/weston.ini.man | 19 ++++++++++++ 2 files changed, 95 insertions(+) diff --git a/compositor/main.c b/compositor/main.c index 96a99097..9a90885e 100644 --- a/compositor/main.c +++ b/compositor/main.c @@ -1316,6 +1316,73 @@ wet_output_set_color_profile(struct weston_output *output, return ok ? 0 : -1; } +static int +wet_output_set_eotf_mode(struct weston_output *output, + struct weston_config_section *section) +{ + static const struct { + const char *name; + enum weston_eotf_mode eotf_mode; + } modes[] = { + { "sdr", WESTON_EOTF_MODE_SDR }, + { "hdr-gamma", WESTON_EOTF_MODE_TRADITIONAL_HDR }, + { "st2084", WESTON_EOTF_MODE_ST2084 }, + { "hlg", WESTON_EOTF_MODE_HLG }, + }; + struct wet_compositor *compositor; + enum weston_eotf_mode eotf_mode = WESTON_EOTF_MODE_SDR; + char *str = NULL; + unsigned i; + + compositor = to_wet_compositor(output->compositor); + + if (section) { + weston_config_section_get_string(section, "eotf-mode", + &str, NULL); + } + + if (!str) { + /* The default SDR mode is always supported. */ + assert(weston_output_get_supported_eotf_modes(output) & eotf_mode); + weston_output_set_eotf_mode(output, eotf_mode); + return 0; + } + + for (i = 0; i < ARRAY_LENGTH(modes); i++) + if (strcmp(str, modes[i].name) == 0) + break; + + if (i == ARRAY_LENGTH(modes)) { + weston_log("Error in config for output '%s': '%s' is not a valid EOTF mode. Try one of:", + output->name, str); + for (i = 0; i < ARRAY_LENGTH(modes); i++) + weston_log_continue(" %s", modes[i].name); + weston_log_continue("\n"); + return -1; + } + eotf_mode = modes[i].eotf_mode; + + if ((weston_output_get_supported_eotf_modes(output) & eotf_mode) == 0) { + weston_log("Error: output '%s' does not support EOTF mode %s.\n", + output->name, str); + free(str); + return -1; + } + + if (eotf_mode != WESTON_EOTF_MODE_SDR && + !compositor->use_color_manager) { + weston_log("Error: EOTF mode %s on output '%s' requires color-management=true in weston.ini\n", + str, output->name); + free(str); + return -1; + } + + weston_output_set_eotf_mode(output, eotf_mode); + + free(str); + return 0; +} + static void allow_content_protection(struct weston_output *output, struct weston_config_section *section) @@ -1839,6 +1906,9 @@ drm_backend_output_configure(struct weston_output *output, allow_content_protection(output, section); + if (wet_output_set_eotf_mode(output, section) < 0) + return -1; + return 0; } @@ -2655,6 +2725,12 @@ headless_backend_output_configure(struct weston_output *output) .scale = 1, .transform = WL_OUTPUT_TRANSFORM_NORMAL }; + struct weston_config *wc = wet_get_config(output->compositor); + struct weston_config_section *section; + + section = weston_config_get_section(wc, "output", "name", output->name); + if (wet_output_set_eotf_mode(output, section) < 0) + return -1; return wet_configure_windowed_output_from_config(output, &defaults); } diff --git a/man/weston.ini.man b/man/weston.ini.man index 35c70e6f..d622e9b6 100644 --- a/man/weston.ini.man +++ b/man/weston.ini.man @@ -548,6 +548,25 @@ of content-protection protocol. Currently, HDCP is supported by drm-backend. A comma separated list of the IDs of applications to place on this output. These IDs should match the application IDs as set with the xdg_shell.set_app_id request. Currently, this option is supported by kiosk-shell. +.TP 7 +.BI "eotf-mode=" sdr +Sets the EOTF mode on the output. This is used for choosing between standard +dynamic range (SDR) mode and the various high dynamic range (HDR) modes. The +display driver, the graphics card, and the video sink (monitor) need to support +the chosen mode, otherwise the result is undefined. +The mode can be one of the following strings: +.PP +.RS 10 +.nf +.BR "sdr " "traditional gamma, SDR" +.BR "hdr-gamma " "traditional gamma, HDR" +.BR "st2084 " "SMPTE ST 2084, a.k.a Perceptual Quantizer" +.BR "hlg " "Hybrid Log-Gamma (ITU-R BT.2100)" +.fi +.RE +.IP +Defaults to +.BR sdr ". Non-SDR modes require " "color-management=true" . .\"--------------------------------------------------------------------- .SH "INPUT-METHOD SECTION" .TP 7 From e6a9e3c4ee39cc70a84a1572cdece4acdf069928 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 4 Mar 2022 17:08:52 +0200 Subject: [PATCH 207/609] backend-drm: default to XRGB2101010 for HDR Trying to do HDR with XRGB8888 is a bit like using RGB565 on SDR: you get visible color quantization and banding in gradients (without dithering which Weston does not implement yet, and might not work too well for HDR anyway). Therefore, on any HDR mode, default output framebuffer format to 10 bpc instead of 8 bpc. Ideally we'd also optionally try 16F or 16 bpc formats, but automatic fallbacks for those are more complicated to arrange. You can still configure 16F or 16 bpc manually. This patch also moves the default format setting from drm_output_set_gbm_format() to drm_output_enable(), because setting the default now requires eotf_mode. Frontends may call set_gbm_format() first and set eotf_mode next. This does create an awkward situation for outputs that a frontend disables and re-enables. This patch here makes sure that the old output configuration remains, but changing eotf_mode may not change the default format. One needs to call set_gbm_format(NULL) to re-evaluate the default format. Resetting the format on drm_output_deinit() would lose the current setting. DRM_FORMAT_INVALID was introduced in libdrm 2.4.95 which we already hard-depend on. Signed-off-by: Pekka Paalanen --- libweston/backend-drm/drm.c | 14 +++++++++++--- man/weston-drm.man | 7 +++++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index 46b809be..b55696c9 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -1416,10 +1416,10 @@ drm_output_set_gbm_format(struct weston_output *base, const char *gbm_format) { struct drm_output *output = to_drm_output(base); - struct drm_backend *b = to_drm_backend(base->compositor); - if (parse_gbm_format(gbm_format, b->gbm_format, &output->gbm_format) == -1) - output->gbm_format = b->gbm_format; + if (parse_gbm_format(gbm_format, + DRM_FORMAT_INVALID, &output->gbm_format) == -1) + output->gbm_format = DRM_FORMAT_INVALID; } static void @@ -1855,6 +1855,13 @@ drm_output_enable(struct weston_output *base) assert(!output->virtual); + if (output->gbm_format == DRM_FORMAT_INVALID) { + if (output->base.eotf_mode != WESTON_EOTF_MODE_SDR) + output->gbm_format = DRM_FORMAT_XRGB2101010; + else + output->gbm_format = b->gbm_format; + } + ret = drm_output_attach_crtc(output); if (ret < 0) return -1; @@ -2301,6 +2308,7 @@ drm_output_create(struct weston_compositor *compositor, const char *name) output->backend = b; output->crtc = NULL; + output->gbm_format = DRM_FORMAT_INVALID; #ifdef BUILD_DRM_GBM output->gbm_bo_flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING; #endif diff --git a/man/weston-drm.man b/man/weston-drm.man index 5947714c..48fd0c8e 100644 --- a/man/weston-drm.man +++ b/man/weston-drm.man @@ -57,7 +57,8 @@ The actually supported pixel formats depend on the DRM driver and hardware, and the renderer used. Using Pixman-renderer, DRM-backend supports only .BR xrgb8888 " and " rgb565 . The formats supported with GL-renderer depend on the EGL and OpenGL ES 2 or 3 -implementations. The names are case-insensitive. +implementations. The names are case-insensitive. This setting applies only to +.RB "outputs in SDR mode, see " eotf-mode " in " weston.ini (5). .RB "If not specified, " xrgb8888 " is used. See also " gbm-format " in" .BR output " section." @@ -134,9 +135,11 @@ and possibly flipped. Possible values are .TP \fBgbm-format\fR=\fIformat\fR Set the DRM KMS framebuffer format for this specific output. If not set, -.RB "the value from " gbm-format " option in " core " section is used." +.RB "the value from " gbm-format " option in " core " section is used" +.RB "for SDR mode outputs and " xrgb2101010 " for other modes." .RI "For the possible values for " format " see " .BR gbm-format " option in " core " section." +.RB "For SDR mode, see " eotf-mode " in " weston.ini (7). .TP \fBpixman-shadow\fR=\fIboolean\fR If using the Pixman-renderer, use shadow framebuffers. Defaults to From 61227652038efc2c2afb33c3609b28f28215f71a Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 2 May 2022 19:36:07 +0200 Subject: [PATCH 208/609] clients/simple-dmabuf-feedback: prettify output - Use more consistent style, e.g. the tree structure uses the same indentation level throughout - Swap format name and code for consistency with modifiers - Use constants for ASCII art (taken from drm_info) Signed-off-by: Simon Ser --- clients/simple-dmabuf-feedback.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/clients/simple-dmabuf-feedback.c b/clients/simple-dmabuf-feedback.c index d953f389..f78f0125 100644 --- a/clients/simple-dmabuf-feedback.c +++ b/clients/simple-dmabuf-feedback.c @@ -49,6 +49,11 @@ #include #include +#define L_LINE "│ " +#define L_VAL "├───" +#define L_LAST "└───" +#define L_GAP " " + #define NUM_BUFFERS 4 /* We have to hack the DRM-backend to pretend that planes of the underlying @@ -1062,8 +1067,7 @@ dmabuf_feedback_main_device(void *data, drm_node = get_drm_node(feedback->main_device, false); assert(drm_node && "error: failed to retrieve drm node"); - fprintf(stderr, "compositor sent main_device event for dma-buf feedback - %s\n", - drm_node); + fprintf(stderr, "feedback: main device %s\n", drm_node); if (!window->card_fd) { window->card_fd = open(drm_node, O_RDWR | O_CLOEXEC); @@ -1172,12 +1176,12 @@ print_tranche_format_modifier(uint32_t format, uint64_t modifier) char fourcc_str[5]; fourcc2str(format, fourcc_str, sizeof(fourcc_str)); - len = asprintf(&format_str, "0x%08x (%s)", format, fourcc_str); + len = asprintf(&format_str, "%s (0x%08x)", fourcc_str, format); } assert(len > 0); - fprintf(stderr, "│ ├────────tranche format/modifier pair - format %s, modifier %s\n", - format_str, mod_name); + fprintf(stderr, L_LINE L_VAL " format %s, modifier %s\n", + format_str, mod_name); free(format_str); free(mod_name); @@ -1193,14 +1197,14 @@ print_dmabuf_feedback_tranche(struct dmabuf_feedback_tranche *tranche) drm_node = get_drm_node(tranche->target_device, tranche->is_scanout_tranche); assert(drm_node && "error: could not retrieve drm node"); - fprintf(stderr, "├──────target_device for tranche - %s\n", drm_node); - fprintf(stderr, "│ └scanout tranche? %s\n", tranche->is_scanout_tranche ? "yes" : "no"); + fprintf(stderr, L_VAL " tranche: target device %s, %s\n", + drm_node, tranche->is_scanout_tranche ? "scanout" : "no flags"); wl_array_for_each(fmt, &tranche->formats.arr) wl_array_for_each(mod, &fmt->modifiers) print_tranche_format_modifier(fmt->format, *mod); - fprintf(stderr, "│ └end of tranche\n"); + fprintf(stderr, L_LINE L_LAST " end of tranche\n"); } static void @@ -1278,7 +1282,7 @@ dmabuf_feedback_done(void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_fee struct dmabuf_feedback_tranche *tranche; unsigned int i; - fprintf(stderr, "└end of dma-buf feedback\n\n"); + fprintf(stderr, L_LAST " end of dma-buf feedback\n\n"); /* The first time that we receive dma-buf feedback for a surface it * contains only the renderer tranche. We pick the INITIAL_BUFFER_FORMAT From 6c0524fd80281c01df2d495a878182f58513cf52 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 28 Apr 2022 15:25:00 +0300 Subject: [PATCH 209/609] libweston: add struct weston_output_color_outcome This new struct collects all the things that a color manager needs to set up when any colorimetry aspect of an output changes. The intention is to make the color manager API less verbose. In this first step, the new struct is added and replaces the fields in weston_output. The intention is for the following color manager API changes to dynamically allocate this structure. Unfortunately, until that actually happens, we need a temporary way to allocate it. That is weston_output::colorout_, which will be removed in the next patch. This keeps the patches more palatable for review at the cost of some back-and-forth in code changes. This is a pure refactoring, no functional changes. Signed-off-by: Pekka Paalanen --- include/libweston/libweston.h | 28 ++++++++++++++++--- libweston/color.h | 3 +++ libweston/compositor.c | 42 +++++++++++++++++++---------- libweston/pixman-renderer.c | 2 +- libweston/renderer-gl/gl-renderer.c | 23 ++++++++++------ 5 files changed, 72 insertions(+), 26 deletions(-) diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index f881b94f..09bb4b4c 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -283,6 +283,26 @@ struct weston_head { enum weston_hdcp_protection current_protection; }; +/** Output properties derived from its color characteristics and profile + * + * These are constructed by a color manager. + * + * A weston_output_color_outcome owns (a reference to) everything it contains. + * + * \ingroup output + * \internal + */ +struct weston_output_color_outcome { + /** sRGB to output color space transformation */ + struct weston_color_transform *from_sRGB_to_output; + + /** sRGB to blending color space transformation */ + struct weston_color_transform *from_sRGB_to_blend; + + /** Blending to output color space transformation */ + struct weston_color_transform *from_blend_to_output; +}; + /** Content producer for heads * * \rst @@ -392,12 +412,14 @@ struct weston_output { int scale; struct weston_color_profile *color_profile; - struct weston_color_transform *from_sRGB_to_output; - struct weston_color_transform *from_sRGB_to_blend; - struct weston_color_transform *from_blend_to_output; bool from_blend_to_output_by_backend; enum weston_eotf_mode eotf_mode; + /* XXX: temporary allocation to be removed in the next commit */ + struct weston_output_color_outcome colorout_; + + struct weston_output_color_outcome *color_outcome; + int (*enable)(struct weston_output *output); int (*disable)(struct weston_output *output); diff --git a/libweston/color.h b/libweston/color.h index 7c1335fa..b5064587 100644 --- a/libweston/color.h +++ b/libweston/color.h @@ -385,4 +385,7 @@ weston_eotf_mode_to_str(enum weston_eotf_mode e); char * weston_eotf_mask_to_str(uint32_t eotf_mask); +void +weston_output_color_outcome_destroy(struct weston_output_color_outcome **pco); + #endif /* WESTON_COLOR_H */ diff --git a/libweston/compositor.c b/libweston/compositor.c index b7944b84..8c77bacf 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -6463,15 +6463,24 @@ weston_output_transform_coordinate(struct weston_output *output, *y = p.f[1] / p.f[3]; } -static void -weston_output_reset_color_transforms(struct weston_output *output) +WL_EXPORT void +weston_output_color_outcome_destroy(struct weston_output_color_outcome **pco) { - weston_color_transform_unref(output->from_sRGB_to_output); - output->from_sRGB_to_output = NULL; - weston_color_transform_unref(output->from_sRGB_to_blend); - output->from_sRGB_to_blend = NULL; - weston_color_transform_unref(output->from_blend_to_output); - output->from_blend_to_output = NULL; + struct weston_output_color_outcome *co = *pco; + + if (!co) + return; + + weston_color_transform_unref(co->from_sRGB_to_output); + co->from_sRGB_to_output = NULL; + weston_color_transform_unref(co->from_sRGB_to_blend); + co->from_sRGB_to_blend = NULL; + weston_color_transform_unref(co->from_blend_to_output); + co->from_blend_to_output = NULL; + + /* XXX: added in the next commit */ + /* free(co); */ + *pco = NULL; } static bool @@ -6498,11 +6507,15 @@ weston_output_set_color_transforms(struct weston_output *output) return false; } - weston_output_reset_color_transforms(output); - output->from_blend_to_output = blend_to_output; + weston_output_color_outcome_destroy(&output->color_outcome); + + /* XXX: temporary allocation to be removed in the next commit */ + output->color_outcome = &output->colorout_; + + output->color_outcome->from_blend_to_output = blend_to_output; + output->color_outcome->from_sRGB_to_output = sRGB_to_output; + output->color_outcome->from_sRGB_to_blend = sRGB_to_blend; output->from_blend_to_output_by_backend = false; - output->from_sRGB_to_output = sRGB_to_output; - output->from_sRGB_to_blend = sRGB_to_blend; weston_log("Output '%s' using color profile: %s\n", output->name, weston_color_profile_get_description(output->color_profile)); @@ -6567,7 +6580,7 @@ weston_compositor_remove_output(struct weston_output *output) weston_view_assign_output(view); } - weston_output_reset_color_transforms(output); + weston_output_color_outcome_destroy(&output->color_outcome); weston_presentation_feedback_discard_list(&output->feedback_list); @@ -6986,7 +6999,7 @@ weston_output_enable(struct weston_output *output) */ if (output->enable(output) < 0) { weston_log("Enabling output \"%s\" failed.\n", output->name); - weston_output_reset_color_transforms(output); + weston_output_color_outcome_destroy(&output->color_outcome); return -1; } @@ -7146,6 +7159,7 @@ weston_output_release(struct weston_output *output) weston_compositor_remove_output(output); weston_color_profile_unref(output->color_profile); + assert(output->color_outcome == NULL); pixman_region32_fini(&output->region); wl_list_remove(&output->link); diff --git a/libweston/pixman-renderer.c b/libweston/pixman-renderer.c index d93e7ed2..17e5d17f 100644 --- a/libweston/pixman-renderer.c +++ b/libweston/pixman-renderer.c @@ -582,7 +582,7 @@ pixman_renderer_repaint_output(struct weston_output *output, pixman_region32_t hw_damage; assert(output->from_blend_to_output_by_backend || - output->from_blend_to_output == NULL); + output->color_outcome->from_blend_to_output == NULL); if (!po->hw_buffer) { po->hw_extra_damage = NULL; diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index b29b4ad3..201bc7fa 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -740,6 +740,7 @@ triangle_fan_debug(struct gl_renderer *gr, static int color_idx = 0; struct gl_shader_config alt; const GLfloat *col; + struct weston_color_transform *ctransf; static const GLfloat color[][4] = { { 1.0, 0.0, 0.0, 1.0 }, { 0.0, 1.0, 0.0, 1.0 }, @@ -758,8 +759,8 @@ triangle_fan_debug(struct gl_renderer *gr, .unicolor = { col[0], col[1], col[2], col[3] }, }; - if (!gl_shader_config_set_color_transform(&alt, - output->from_sRGB_to_blend)) { + ctransf = output->color_outcome->from_sRGB_to_blend; + if (!gl_shader_config_set_color_transform(&alt, ctransf)) { weston_log("GL-renderer: %s failed to generate a color transformation.\n", __func__); return; @@ -932,6 +933,7 @@ static void censor_override(struct gl_shader_config *sconf, struct weston_output *output) { + struct weston_color_transform *ctransf; struct gl_shader_config alt = { .req = { .variant = SHADER_VARIANT_SOLID, @@ -942,8 +944,8 @@ censor_override(struct gl_shader_config *sconf, .unicolor = { 0.40, 0.0, 0.0, 1.0 }, }; - if (!gl_shader_config_set_color_transform(&alt, - output->from_sRGB_to_blend)) { + ctransf = output->color_outcome->from_sRGB_to_blend; + if (!gl_shader_config_set_color_transform(&alt, ctransf)) { weston_log("GL-renderer: %s failed to generate a color transformation.\n", __func__); } @@ -1307,6 +1309,7 @@ draw_output_borders(struct weston_output *output, }, .view_alpha = 1.0f, }; + struct weston_color_transform *ctransf; struct gl_output_state *go = get_output_state(output); struct gl_renderer *gr = get_renderer(output->compositor); struct gl_border_image *top, *bottom, *left, *right; @@ -1315,7 +1318,8 @@ draw_output_borders(struct weston_output *output, if (border_status == BORDER_STATUS_CLEAN) return; /* Clean. Nothing to do. */ - if (!gl_shader_config_set_color_transform(&sconf, output->from_sRGB_to_output)) { + ctransf = output->color_outcome->from_sRGB_to_output; + if (!gl_shader_config_set_color_transform(&sconf, ctransf)) { weston_log("GL-renderer: %s failed to generate a color transformation.\n", __func__); return; } @@ -1550,13 +1554,15 @@ blit_shadow_to_output(struct weston_output *output, struct gl_renderer *gr = get_renderer(output->compositor); double width = output->current_mode->width; double height = output->current_mode->height; + struct weston_color_transform *ctransf; pixman_box32_t *rects; int n_rects; int i; pixman_region32_t translated_damage; GLfloat verts[4 * 2]; - if (!gl_shader_config_set_color_transform(&sconf, output->from_blend_to_output)) { + ctransf = output->color_outcome->from_blend_to_output; + if (!gl_shader_config_set_color_transform(&sconf, ctransf)) { weston_log("GL-renderer: %s failed to generate a color transformation.\n", __func__); return; } @@ -1624,7 +1630,8 @@ gl_renderer_repaint_output(struct weston_output *output, struct weston_paint_node *pnode; assert(output->from_blend_to_output_by_backend || - output->from_blend_to_output == NULL || shadow_exists(go)); + output->color_outcome->from_blend_to_output == NULL || + shadow_exists(go)); if (use_output(output) < 0) return; @@ -3439,7 +3446,7 @@ gl_renderer_output_create(struct weston_output *output, go->begin_render_sync = EGL_NO_SYNC_KHR; go->end_render_sync = EGL_NO_SYNC_KHR; - if ((output->from_blend_to_output != NULL && + if ((output->color_outcome->from_blend_to_output != NULL && output->from_blend_to_output_by_backend == false) || quirks->gl_force_full_redraw_of_shadow_fb) { assert(gr->gl_supports_color_transforms); From dfba19abde95abe9a51c58725066450edf172003 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 29 Apr 2022 11:20:34 +0300 Subject: [PATCH 210/609] color: simplify color manager API with weston_output_color_outcome I am going to need to add yet another output property to be set by a color manager: HDR Static Metadata Type 1. With the old color manager API design, I would have needed to add the fourth function pointer to be called always in the same group as the previous three. This seemed more convoluted than it needs to be. Therefore collapse the three existing function pointers in the API into just one that is resposible for setting up all three things. Signed-off-by: Pekka Paalanen --- include/libweston/libweston.h | 3 -- libweston/color-lcms/color-lcms.c | 52 +++++++++++++++++++-------- libweston/color-noop.c | 60 ++++++++----------------------- libweston/color.h | 52 +++++---------------------- libweston/compositor.c | 37 +++++-------------- 5 files changed, 70 insertions(+), 134 deletions(-) diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 09bb4b4c..2eec0be3 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -415,9 +415,6 @@ struct weston_output { bool from_blend_to_output_by_backend; enum weston_eotf_mode eotf_mode; - /* XXX: temporary allocation to be removed in the next commit */ - struct weston_output_color_outcome colorout_; - struct weston_output_color_outcome *color_outcome; int (*enable)(struct weston_output *output); diff --git a/libweston/color-lcms/color-lcms.c b/libweston/color-lcms/color-lcms.c index 23d2b79d..e3668832 100644 --- a/libweston/color-lcms/color-lcms.c +++ b/libweston/color-lcms/color-lcms.c @@ -126,11 +126,10 @@ cmlcms_get_surface_color_transform(struct weston_color_manager *cm_base, } static bool -cmlcms_get_output_color_transform(struct weston_color_manager *cm_base, - struct weston_output *output, - struct weston_color_transform **xform_out) +cmlcms_get_blend_to_output_color_transform(struct weston_color_manager_lcms *cm, + struct weston_output *output, + struct weston_color_transform **xform_out) { - struct weston_color_manager_lcms *cm = get_cmlcms(cm_base); struct cmlcms_color_transform_search_param param = {}; struct cmlcms_color_transform *xform; @@ -148,11 +147,10 @@ cmlcms_get_output_color_transform(struct weston_color_manager *cm_base, } static bool -cmlcms_get_sRGB_to_output_color_transform(struct weston_color_manager *cm_base, +cmlcms_get_sRGB_to_output_color_transform(struct weston_color_manager_lcms *cm, struct weston_output *output, struct weston_color_transform **xform_out) { - struct weston_color_manager_lcms *cm = get_cmlcms(cm_base); struct cmlcms_color_transform_search_param param = {}; struct cmlcms_color_transform *xform; @@ -177,11 +175,10 @@ cmlcms_get_sRGB_to_output_color_transform(struct weston_color_manager *cm_base, } static bool -cmlcms_get_sRGB_to_blend_color_transform(struct weston_color_manager *cm_base, +cmlcms_get_sRGB_to_blend_color_transform(struct weston_color_manager_lcms *cm, struct weston_output *output, struct weston_color_transform **xform_out) { - struct weston_color_manager_lcms *cm = get_cmlcms(cm_base); struct cmlcms_color_transform_search_param param = {}; struct cmlcms_color_transform *xform; @@ -198,6 +195,36 @@ cmlcms_get_sRGB_to_blend_color_transform(struct weston_color_manager *cm_base, return true; } +static struct weston_output_color_outcome * +cmlcms_create_output_color_outcome(struct weston_color_manager *cm_base, + struct weston_output *output) +{ + struct weston_color_manager_lcms *cm = get_cmlcms(cm_base); + struct weston_output_color_outcome *co; + + co = zalloc(sizeof *co); + if (!co) + return NULL; + + if (!cmlcms_get_blend_to_output_color_transform(cm, output, + &co->from_blend_to_output)) + goto out_fail; + + if (!cmlcms_get_sRGB_to_blend_color_transform(cm, output, + &co->from_sRGB_to_blend)) + goto out_fail; + + if (!cmlcms_get_sRGB_to_output_color_transform(cm, output, + &co->from_sRGB_to_output)) + goto out_fail; + + return co; + +out_fail: + weston_output_color_outcome_destroy(&co); + return NULL; +} + static void lcms_error_logger(cmsContext context_id, cmsUInt32Number error_code, @@ -264,13 +291,8 @@ weston_color_manager_create(struct weston_compositor *compositor) cm->base.destroy_color_profile = cmlcms_destroy_color_profile; cm->base.get_color_profile_from_icc = cmlcms_get_color_profile_from_icc; cm->base.destroy_color_transform = cmlcms_destroy_color_transform; - cm->base.get_surface_color_transform = - cmlcms_get_surface_color_transform; - cm->base.get_output_color_transform = cmlcms_get_output_color_transform; - cm->base.get_sRGB_to_output_color_transform = - cmlcms_get_sRGB_to_output_color_transform; - cm->base.get_sRGB_to_blend_color_transform = - cmlcms_get_sRGB_to_blend_color_transform; + cm->base.get_surface_color_transform = cmlcms_get_surface_color_transform; + cm->base.create_output_color_outcome = cmlcms_create_output_color_outcome; wl_list_init(&cm->color_transform_list); wl_list_init(&cm->color_profile_list); diff --git a/libweston/color-noop.c b/libweston/color-noop.c index 0b67d5a6..2e1461f3 100644 --- a/libweston/color-noop.c +++ b/libweston/color-noop.c @@ -96,52 +96,27 @@ cmnoop_get_surface_color_transform(struct weston_color_manager *cm_base, return true; } -static bool -cmnoop_get_output_color_transform(struct weston_color_manager *cm_base, - struct weston_output *output, - struct weston_color_transform **xform_out) +static struct weston_output_color_outcome * +cmnoop_create_output_color_outcome(struct weston_color_manager *cm_base, + struct weston_output *output) { - assert(output->color_profile == NULL); - - if (!check_output_eotf_mode(output)) - return false; - - /* Identity transform */ - *xform_out = NULL; + struct weston_output_color_outcome *co; - return true; -} - -static bool -cmnoop_get_sRGB_to_output_color_transform(struct weston_color_manager *cm_base, - struct weston_output *output, - struct weston_color_transform **xform_out) -{ assert(output->color_profile == NULL); if (!check_output_eotf_mode(output)) - return false; - - /* Identity transform */ - *xform_out = NULL; - - return true; -} - -static bool -cmnoop_get_sRGB_to_blend_color_transform(struct weston_color_manager *cm_base, - struct weston_output *output, - struct weston_color_transform **xform_out) -{ - assert(output->color_profile == NULL); + return NULL; - if (!check_output_eotf_mode(output)) - return false; + co = zalloc(sizeof *co); + if (!co) + return NULL; - /* Identity transform */ - *xform_out = NULL; + /* Identity transform on everything */ + co->from_blend_to_output = NULL; + co->from_sRGB_to_blend = NULL; + co->from_sRGB_to_output = NULL; - return true; + return co; } static bool @@ -177,13 +152,8 @@ weston_color_manager_noop_create(struct weston_compositor *compositor) cm->base.destroy_color_profile = cmnoop_destroy_color_profile; cm->base.get_color_profile_from_icc = cmnoop_get_color_profile_from_icc; cm->base.destroy_color_transform = cmnoop_destroy_color_transform; - cm->base.get_surface_color_transform = - cmnoop_get_surface_color_transform; - cm->base.get_output_color_transform = cmnoop_get_output_color_transform; - cm->base.get_sRGB_to_output_color_transform = - cmnoop_get_sRGB_to_output_color_transform; - cm->base.get_sRGB_to_blend_color_transform = - cmnoop_get_sRGB_to_blend_color_transform; + cm->base.get_surface_color_transform = cmnoop_get_surface_color_transform; + cm->base.create_output_color_outcome = cmnoop_create_output_color_outcome; return &cm->base; } diff --git a/libweston/color.h b/libweston/color.h index b5064587..b8b30d5b 100644 --- a/libweston/color.h +++ b/libweston/color.h @@ -297,53 +297,19 @@ struct weston_color_manager { struct weston_output *output, struct weston_surface_color_transform *surf_xform); - /** Get output's blending space to output transformation + /** Compute derived color properties for an output * * \param cm The color manager. - * \param output The output for the destination color space. - * \param xform_out Pointer for storing the weston_color_transform. - * \return True on success, false on failure. - * - * The callee is responsible for increasing the reference count on the - * weston_color_transform it stores via xform_out. On failure, xform_out - * is untouched. - */ - bool - (*get_output_color_transform)(struct weston_color_manager *cm, - struct weston_output *output, - struct weston_color_transform **xform_out); - - /** Get sRGB to output transformation - * - * \param cm The color manager. - * \param output The output for the destination color space. - * \param xform_out Pointer for storing the weston_color_transform. - * \return True on success, false on failure. - * - * The callee is responsible for increasing the reference count on the - * weston_color_transform it stores via xform_out. On failure, xform_out - * is untouched. - */ - bool - (*get_sRGB_to_output_color_transform)(struct weston_color_manager *cm, - struct weston_output *output, - struct weston_color_transform **xform_out); - - /** Get sRGB to output's blending space transformation - * - * \param cm The color manager. - * \param output The output for the destination blending color space. - * \param xform_out Pointer for storing the weston_color_transform. - * \return True on success, false on failure. + * \param output The output. + * \return A new color_outcome object on success, NULL on failure. * - * The callee is responsible for increasing the reference count on the - * weston_color_transform it stores via xform_out. On failure, xform_out - * is untouched. + * The callee (color manager) must inspect the weston_output (color + * profile, EOTF mode, etc.) and create a fully populated + * weston_output_color_outcome object. */ - bool - (*get_sRGB_to_blend_color_transform)(struct weston_color_manager *cm, - struct weston_output *output, - struct weston_color_transform **xform_out); + struct weston_output_color_outcome * + (*create_output_color_outcome)(struct weston_color_manager *cm, + struct weston_output *output); }; void diff --git a/libweston/compositor.c b/libweston/compositor.c index 8c77bacf..000fb69e 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -6472,49 +6472,30 @@ weston_output_color_outcome_destroy(struct weston_output_color_outcome **pco) return; weston_color_transform_unref(co->from_sRGB_to_output); - co->from_sRGB_to_output = NULL; weston_color_transform_unref(co->from_sRGB_to_blend); - co->from_sRGB_to_blend = NULL; weston_color_transform_unref(co->from_blend_to_output); - co->from_blend_to_output = NULL; - /* XXX: added in the next commit */ - /* free(co); */ + free(co); *pco = NULL; } static bool -weston_output_set_color_transforms(struct weston_output *output) +weston_output_set_color_outcome(struct weston_output *output) { struct weston_color_manager *cm = output->compositor->color_manager; - struct weston_color_transform *blend_to_output = NULL; - struct weston_color_transform *sRGB_to_output = NULL; - struct weston_color_transform *sRGB_to_blend = NULL; - bool ok; - - ok = cm->get_output_color_transform(cm, output, &blend_to_output); - ok = ok && cm->get_sRGB_to_output_color_transform(cm, output, - &sRGB_to_output); - ok = ok && cm->get_sRGB_to_blend_color_transform(cm, output, - &sRGB_to_blend); - if (!ok) { + struct weston_output_color_outcome *colorout; + + colorout = cm->create_output_color_outcome(cm, output); + if (!colorout) { weston_log("Creating color transformation for output \"%s\" failed.\n", output->name); - weston_color_transform_unref(blend_to_output); - weston_color_transform_unref(sRGB_to_output); - weston_color_transform_unref(sRGB_to_blend); return false; } weston_output_color_outcome_destroy(&output->color_outcome); + output->color_outcome = colorout; - /* XXX: temporary allocation to be removed in the next commit */ - output->color_outcome = &output->colorout_; - - output->color_outcome->from_blend_to_output = blend_to_output; - output->color_outcome->from_sRGB_to_output = sRGB_to_output; - output->color_outcome->from_sRGB_to_blend = sRGB_to_blend; output->from_blend_to_output_by_backend = false; weston_log("Output '%s' using color profile: %s\n", output->name, @@ -6734,7 +6715,7 @@ weston_output_set_color_profile(struct weston_output *output, output->color_profile = weston_color_profile_ref(cprof); if (output->enabled) { - if (!weston_output_set_color_transforms(output)) { + if (!weston_output_set_color_outcome(output)) { /* Failed, roll back */ weston_color_profile_unref(output->color_profile); output->color_profile = old; @@ -6990,7 +6971,7 @@ weston_output_enable(struct weston_output *output) weston_log("Output '%s' attempts EOTF mode: %s\n", output->name, weston_eotf_mode_to_str(output->eotf_mode)); - if (!weston_output_set_color_transforms(output)) + if (!weston_output_set_color_outcome(output)) return -1; /* Enable the output (set up the crtc or create a From e3b6d040172161af4cb902f1e99dd924eda10f94 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 29 Apr 2022 11:50:37 +0300 Subject: [PATCH 211/609] color-lcms: refactor away setup_seach_param() To me, the use of setup_search_param() makes the code harder to understand than it needs to be. Replacing that function with open-coding the struct cmlcms_color_transform_search_param initialization makes it more clear that: - get_surface_color_transform is the only one that actually uses a surface to initialize it - get_blend_to_output does not use an input profile at all - get_sRGB_to_output and get_sRGB_to_blend hardcode the sRGB profile like they should Signed-off-by: Pekka Paalanen --- libweston/color-lcms/color-lcms.c | 82 ++++++++++++++----------------- 1 file changed, 36 insertions(+), 46 deletions(-) diff --git a/libweston/color-lcms/color-lcms.c b/libweston/color-lcms/color-lcms.c index e3668832..005cbc9d 100644 --- a/libweston/color-lcms/color-lcms.c +++ b/libweston/color-lcms/color-lcms.c @@ -47,43 +47,16 @@ cmlcms_get_render_intent(enum cmlcms_category cat, return intent; } -static void -setup_search_param(enum cmlcms_category cat, - struct weston_surface *surface, - struct weston_output *output, - struct cmlcms_color_profile *stock_sRGB_profile, - struct cmlcms_color_transform_search_param *search_param) +static struct cmlcms_color_profile * +get_cprof_or_stock_sRGB(struct weston_color_manager_lcms *cm, + struct weston_color_profile *cprof_base) { - struct cmlcms_color_profile *input_profile = NULL; - struct cmlcms_color_profile *output_profile = NULL; - - /* - * TODO: un-comment when declare color_profile in struct weston_surface - */ - /* if (surface && surface->color_profile) - input_profile = get_cprof(surface->color_profile); */ - if (output && output->color_profile) - output_profile = get_cprof(output->color_profile); - - search_param->category = cat; - switch (cat) { - case CMLCMS_CATEGORY_INPUT_TO_BLEND: - case CMLCMS_CATEGORY_INPUT_TO_OUTPUT: - search_param->input_profile = - input_profile ? input_profile : stock_sRGB_profile; - search_param->output_profile = - output_profile ? output_profile : stock_sRGB_profile; - break; - case CMLCMS_CATEGORY_BLEND_TO_OUTPUT: - search_param->output_profile = - output_profile ? output_profile : stock_sRGB_profile; - break; - } - search_param->intent_output = cmlcms_get_render_intent(cat, surface, - output); + if (cprof_base) + return get_cprof(cprof_base); + else + return cm->sRGB_profile; } - static void cmlcms_destroy_color_transform(struct weston_color_transform *xform_base) { @@ -99,13 +72,17 @@ cmlcms_get_surface_color_transform(struct weston_color_manager *cm_base, struct weston_surface_color_transform *surf_xform) { struct weston_color_manager_lcms *cm = get_cmlcms(cm_base); - struct cmlcms_color_transform_search_param param = {}; struct cmlcms_color_transform *xform; /* TODO: take weston_output::eotf_mode into account */ - setup_search_param(CMLCMS_CATEGORY_INPUT_TO_BLEND, surface, output, - cm->sRGB_profile, ¶m); + struct cmlcms_color_transform_search_param param = { + .category = CMLCMS_CATEGORY_INPUT_TO_BLEND, + .input_profile = get_cprof_or_stock_sRGB(cm, NULL /* TODO: surface->color_profile */), + .output_profile = get_cprof_or_stock_sRGB(cm, output->color_profile), + }; + param.intent_output = cmlcms_get_render_intent(param.category, + surface, output); xform = cmlcms_color_transform_get(cm, ¶m); if (!xform) @@ -130,13 +107,17 @@ cmlcms_get_blend_to_output_color_transform(struct weston_color_manager_lcms *cm, struct weston_output *output, struct weston_color_transform **xform_out) { - struct cmlcms_color_transform_search_param param = {}; struct cmlcms_color_transform *xform; /* TODO: take weston_output::eotf_mode into account */ - setup_search_param(CMLCMS_CATEGORY_BLEND_TO_OUTPUT, NULL, output, - cm->sRGB_profile, ¶m); + struct cmlcms_color_transform_search_param param = { + .category = CMLCMS_CATEGORY_BLEND_TO_OUTPUT, + .input_profile = NULL, + .output_profile = get_cprof_or_stock_sRGB(cm, output->color_profile), + }; + param.intent_output = cmlcms_get_render_intent(param.category, + NULL, output); xform = cmlcms_color_transform_get(cm, ¶m); if (!xform) @@ -151,13 +132,18 @@ cmlcms_get_sRGB_to_output_color_transform(struct weston_color_manager_lcms *cm, struct weston_output *output, struct weston_color_transform **xform_out) { - struct cmlcms_color_transform_search_param param = {}; struct cmlcms_color_transform *xform; /* TODO: take weston_output::eotf_mode into account */ - setup_search_param(CMLCMS_CATEGORY_INPUT_TO_OUTPUT, NULL, output, - cm->sRGB_profile, ¶m); + struct cmlcms_color_transform_search_param param = { + .category = CMLCMS_CATEGORY_INPUT_TO_OUTPUT, + .input_profile = cm->sRGB_profile, + .output_profile = get_cprof_or_stock_sRGB(cm, output->color_profile), + }; + param.intent_output = cmlcms_get_render_intent(param.category, + NULL, output); + /* * Create a color transformation when output profile is not stock * sRGB profile. @@ -179,13 +165,17 @@ cmlcms_get_sRGB_to_blend_color_transform(struct weston_color_manager_lcms *cm, struct weston_output *output, struct weston_color_transform **xform_out) { - struct cmlcms_color_transform_search_param param = {}; struct cmlcms_color_transform *xform; /* TODO: take weston_output::eotf_mode into account */ - setup_search_param(CMLCMS_CATEGORY_INPUT_TO_BLEND, NULL, output, - cm->sRGB_profile, ¶m); + struct cmlcms_color_transform_search_param param = { + .category = CMLCMS_CATEGORY_INPUT_TO_BLEND, + .input_profile = cm->sRGB_profile, + .output_profile = get_cprof_or_stock_sRGB(cm, output->color_profile), + }; + param.intent_output = cmlcms_get_render_intent(param.category, + NULL, output); xform = cmlcms_color_transform_get(cm, ¶m); if (!xform) From e3b95f2d27f2cd016c734259ef6a5bf46b13471d Mon Sep 17 00:00:00 2001 From: Hideyuki Nagase Date: Mon, 9 May 2022 08:16:59 -0500 Subject: [PATCH 212/609] rdp: Add hebrew standard layout Co-authored-by: Steve Pronovost Co-authored-by: Brenton DeGeer Signed-off-by: Hideyuki Nagase Signed-off-by: Steve Pronovost Signed-off-by: Brenton DeGeer --- libweston/backend-rdp/rdp.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index 61b920f2..4791d3db 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -42,10 +42,13 @@ #include #include "pixman-renderer.h" -/* This can be removed when we bump FreeRDP dependency past 3.0.0 in the future */ +/* These can be removed when we bump FreeRDP dependency past 3.0.0 in the future */ #ifndef KBD_PERSIAN #define KBD_PERSIAN 0x50429 #endif +#ifndef KBD_HEBREW_STANDARD +#define KBD_HEBREW_STANDARD 0x2040D +#endif static void rdp_peer_refresh_rfx(pixman_region32_t *damage, pixman_image_t *image, freerdp_peer *peer) @@ -726,6 +729,7 @@ struct rdp_to_xkb_keyboard_layout rdp_keyboards[] = { {KBD_FINNISH, "fi", 0}, {KBD_FRENCH, "fr", 0}, {KBD_HEBREW, "il", 0}, + {KBD_HEBREW_STANDARD, "il", "basic"}, {KBD_HUNGARIAN, "hu", 0}, {KBD_HUNGARIAN_101_KEY, "hu", "standard"}, {KBD_ICELANDIC, "is", 0}, From 8a776be92502fa1ab178832c687d1a95b7a3d1ed Mon Sep 17 00:00:00 2001 From: Hideyuki Nagase Date: Mon, 9 May 2022 08:18:56 -0500 Subject: [PATCH 213/609] rdp: Add US international keyboard layout Co-authored-by: Steve Pronovost Co-authored-by: Brenton DeGeer Signed-off-by: Hideyuki Nagase Signed-off-by: Steve Pronovost Signed-off-by: Brenton DeGeer --- libweston/backend-rdp/rdp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index 4791d3db..f37aa6f8 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -723,6 +723,7 @@ struct rdp_to_xkb_keyboard_layout rdp_keyboards[] = { {KBD_GREEK_319, "gr", "extended"}, {KBD_GREEK_POLYTONIC, "gr", "polytonic"}, {KBD_US, "us", 0}, + {KBD_UNITED_STATES_INTERNATIONAL, "us", "intl"}, {KBD_US_ENGLISH_TABLE_FOR_IBM_ARABIC_238_L, "ara", "buckwalter"}, {KBD_SPANISH, "es", 0}, {KBD_SPANISH_VARIATION, "es", "nodeadkeys"}, From fed2ee51f24effe0caa927463d4c795e1f7df553 Mon Sep 17 00:00:00 2001 From: Erik Faye-Lund Date: Thu, 5 May 2022 12:29:51 +0200 Subject: [PATCH 214/609] simple-egl: clean up unused callback Unused since 45ee1f9ef7fab76c3287ab6f6a695357107b5f87, which itself removes code that was unused since 1e65840b6103197a684a551925a58fb1ae6c40e3. Signed-off-by: Erik Faye-Lund --- clients/simple-egl.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/clients/simple-egl.c b/clients/simple-egl.c index 2c7059c0..3d5e9226 100644 --- a/clients/simple-egl.c +++ b/clients/simple-egl.c @@ -95,7 +95,6 @@ struct window { struct xdg_surface *xdg_surface; struct xdg_toplevel *xdg_toplevel; EGLSurface egl_surface; - struct wl_callback *callback; int fullscreen, maximized, opaque, buffer_size, frame_sync, delay; bool wait_for_configure; }; @@ -421,15 +420,11 @@ destroy_surface(struct window *window) if (window->xdg_surface) xdg_surface_destroy(window->xdg_surface); wl_surface_destroy(window->surface); - - if (window->callback) - wl_callback_destroy(window->callback); } static void -redraw(void *data, struct wl_callback *callback, uint32_t time) +redraw(struct window *window) { - struct window *window = data; struct display *display = window->display; static const GLfloat verts[3][2] = { { -0.5, -0.5 }, @@ -454,14 +449,8 @@ redraw(void *data, struct wl_callback *callback, uint32_t time) EGLint buffer_age = 0; struct timeval tv; - assert(window->callback == callback); - window->callback = NULL; - - if (callback) - wl_callback_destroy(callback); - gettimeofday(&tv, NULL); - time = tv.tv_sec * 1000 + tv.tv_usec / 1000; + uint32_t time = tv.tv_sec * 1000 + tv.tv_usec / 1000; if (window->frames == 0) window->benchmark_time = time; if (time - window->benchmark_time > (benchmark_interval * 1000)) { @@ -887,7 +876,7 @@ main(int argc, char **argv) while (running && ret != -1) { ret = wl_display_dispatch_pending(display.display); - redraw(&window, NULL, 0); + redraw(&window); } fprintf(stderr, "simple-egl exiting\n"); From 70353dace36f8f3e28337145fcb75463ac1db60e Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Fri, 11 Dec 2020 22:23:58 +0200 Subject: [PATCH 215/609] desktop-shell: Refuse to set a surface to maximized For a surface that is already fullscreen making it maximized means to exit fullscreen then set to it maximized. Instead of doing it, refuse to do anything until the user explicitly performs that operation. With this approach we follow other DE (desktop environments) which would not perform any operation until the user exits fullscreen state. Fixes #321 Signed-off-by: Marius Vlad --- desktop-shell/shell.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 0b686b92..53d804ca 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -2685,6 +2685,9 @@ set_maximized(struct shell_surface *shsurf, bool maximized) weston_desktop_surface_get_surface(shsurf->desktop_surface); int32_t width = 0, height = 0; + if (weston_desktop_surface_get_fullscreen(desktop_surface)) + return; + if (maximized) { struct weston_output *output; From 29d480813ad64cfde3f1c8bd64310db08bdfba28 Mon Sep 17 00:00:00 2001 From: Robert Mader Date: Sun, 24 Apr 2022 02:36:58 +0200 Subject: [PATCH 216/609] backend-drm: Add failure reasons for failing gbm_bo_import And add it to the list of failures triggering a resend of dmabuf feedback scanout tranches. Closes https://gitlab.freedesktop.org/wayland/weston/-/issues/614 Signed-off-by: Robert Mader --- libweston/backend-drm/drm-internal.h | 1 + libweston/backend-drm/fb.c | 6 +++++- libweston/backend-drm/state-propose.c | 3 ++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index 706dfcab..23da81c0 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -245,6 +245,7 @@ enum try_view_on_plane_failure_reasons { FAILURE_REASONS_BUFFER_TYPE = 1 << 9, FAILURE_REASONS_GLOBAL_ALPHA = 1 << 10, FAILURE_REASONS_NO_GBM = 1 << 11, + FAILURE_REASONS_GBM_BO_IMPORT_FAILED = 1 << 12, }; /** diff --git a/libweston/backend-drm/fb.c b/libweston/backend-drm/fb.c index e563b52e..328d1f85 100644 --- a/libweston/backend-drm/fb.c +++ b/libweston/backend-drm/fb.c @@ -276,8 +276,12 @@ drm_fb_get_from_dmabuf(struct linux_dmabuf_buffer *dmabuf, fb->bo = gbm_bo_import(backend->gbm, GBM_BO_IMPORT_FD_MODIFIER, &import_mod, GBM_BO_USE_SCANOUT); - if (!fb->bo) + if (!fb->bo) { + if (try_view_on_plane_failure_reasons) + *try_view_on_plane_failure_reasons |= + FAILURE_REASONS_GBM_BO_IMPORT_FAILED; goto err_free; + } fb->width = dmabuf->attributes.width; fb->height = dmabuf->attributes.height; diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index 790db3ec..49911d98 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -340,7 +340,8 @@ dmabuf_feedback_maybe_update(struct drm_backend *b, struct weston_view *ev, if (try_view_on_plane_failure_reasons & (FAILURE_REASONS_ADD_FB_FAILED | FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE | - FAILURE_REASONS_DMABUF_MODIFIER_INVALID)) + FAILURE_REASONS_DMABUF_MODIFIER_INVALID | + FAILURE_REASONS_GBM_BO_IMPORT_FAILED)) action_needed |= ACTION_NEEDED_ADD_SCANOUT_TRANCHE; assert(action_needed != (ACTION_NEEDED_REMOVE_SCANOUT_TRANCHE | From 34f7e01c2bb2cd19be00f7dcd1340390e1bf901a Mon Sep 17 00:00:00 2001 From: Robert Mader Date: Wed, 4 May 2022 17:57:27 +0200 Subject: [PATCH 217/609] clients/simple-dmabuf-feedback: use time instead of redraws Weston uses a timeout of 2 seconds before it sends scanout tranches to clients in order to not trigger excessive buffer reallocations in clients. `simple-dmabuf-feedback` in turn counts redraws (200) before exiting. That doesn't work on e.g. 144Hz screens, thus use a timer here as well. Signed-off-by: Robert Mader --- clients/simple-dmabuf-feedback.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/clients/simple-dmabuf-feedback.c b/clients/simple-dmabuf-feedback.c index f78f0125..66249b82 100644 --- a/clients/simple-dmabuf-feedback.c +++ b/clients/simple-dmabuf-feedback.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "shared/helpers.h" #include "shared/platform.h" @@ -175,7 +176,6 @@ struct window { struct wp_presentation_feedback *presentation_feedback; bool wait_for_configure; bool presented_zero_copy; - uint32_t n_redraws; struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback_obj; struct dmabuf_feedback dmabuf_feedback, pending_dmabuf_feedback; int card_fd; @@ -689,8 +689,6 @@ redraw(void *data, struct wl_callback *callback, uint32_t time) window->display->output.height); wl_surface_set_opaque_region(window->surface, region); wl_region_destroy(region); - - window->n_redraws++; } static const struct wl_callback_listener frame_listener = { @@ -1512,6 +1510,9 @@ main(int argc, char **argv) struct display *display; struct window *window; int ret = 0; + struct timespec start_time, current_time; + const time_t MAX_TIME_SECONDS = 3; + time_t delta_time = 0; fprintf(stderr, "This client was written with the purpose of manually test " \ "Weston's dma-buf feedback implementation. See main() " \ @@ -1520,9 +1521,14 @@ main(int argc, char **argv) display = create_display(); window = create_window(display); + clock_gettime(CLOCK_MONOTONIC, &start_time); + redraw(window, NULL, 0); - while (ret != -1 && window->n_redraws < 200) + while (ret != -1 && delta_time < MAX_TIME_SECONDS) { ret = wl_display_dispatch(display->display); + clock_gettime(CLOCK_MONOTONIC, ¤t_time); + delta_time = current_time.tv_sec - start_time.tv_sec; + } destroy_window(window); destroy_display(display); From 53a221ccaaaa888a0efcda0ecadc45a51f03ac92 Mon Sep 17 00:00:00 2001 From: Robert Mader Date: Wed, 4 May 2022 18:04:55 +0200 Subject: [PATCH 218/609] libweston/linux-dmabuf: create surface feedback on demand Unconditionally creating a surface feedback for each surface creates unnecessary overhead and noise in the logs. Thus create it when the first surface feedback resource for a surface is requested and delete it again once all those resources have been destroyed. Signed-off-by: Robert Mader --- libweston/compositor.c | 36 ++------------------- libweston/linux-dmabuf.c | 67 +++++++++++++++++++++++++++++++++++----- 2 files changed, 62 insertions(+), 41 deletions(-) diff --git a/libweston/compositor.c b/libweston/compositor.c index 000fb69e..1567f7f7 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -4189,54 +4189,22 @@ static const struct wl_surface_interface surface_interface = { surface_damage_buffer }; -static int -create_surface_dmabuf_feedback(struct weston_compositor *ec, - struct weston_surface *surface) -{ - struct weston_dmabuf_feedback_tranche *tranche; - dev_t main_device = ec->default_dmabuf_feedback->main_device; - uint32_t flags = 0; - - surface->dmabuf_feedback = weston_dmabuf_feedback_create(main_device); - if (!surface->dmabuf_feedback) - return -1; - - tranche = weston_dmabuf_feedback_tranche_create(surface->dmabuf_feedback, - ec->dmabuf_feedback_format_table, - main_device, flags, - RENDERER_PREF); - if (!tranche) { - weston_dmabuf_feedback_destroy(surface->dmabuf_feedback); - surface->dmabuf_feedback = NULL; - return -1; - } - - return 0; -} - static void compositor_create_surface(struct wl_client *client, struct wl_resource *resource, uint32_t id) { struct weston_compositor *ec = wl_resource_get_user_data(resource); struct weston_surface *surface; - int ret; surface = weston_surface_create(ec); if (surface == NULL) goto err; - if (ec->default_dmabuf_feedback) { - ret = create_surface_dmabuf_feedback(ec, surface); - if (ret < 0) - goto err_dmabuf_feedback; - } - surface->resource = wl_resource_create(client, &wl_surface_interface, wl_resource_get_version(resource), id); if (surface->resource == NULL) - goto err_dmabuf_feedback; + goto err_res; wl_resource_set_implementation(surface->resource, &surface_interface, surface, destroy_surface); @@ -4244,7 +4212,7 @@ compositor_create_surface(struct wl_client *client, return; -err_dmabuf_feedback: +err_res: weston_surface_destroy(surface); err: wl_resource_post_no_memory(resource); diff --git a/libweston/linux-dmabuf.c b/libweston/linux-dmabuf.c index 66702a4c..21de498d 100644 --- a/libweston/linux-dmabuf.c +++ b/libweston/linux-dmabuf.c @@ -684,6 +684,7 @@ weston_dmabuf_feedback_destroy(struct weston_dmabuf_feedback *dmabuf_feedback) wl_resource_for_each_safe(res, res_tmp, &dmabuf_feedback->resource_list) { wl_list_remove(wl_resource_get_link(res)); wl_list_init(wl_resource_get_link(res)); + wl_resource_set_user_data(res, NULL); } free(dmabuf_feedback); @@ -786,6 +787,7 @@ weston_dmabuf_feedback_send_all(struct weston_dmabuf_feedback *dmabuf_feedback, { struct wl_resource *res; + assert(!wl_list_empty(&dmabuf_feedback->resource_list)); wl_resource_for_each(res, &dmabuf_feedback->resource_list) weston_dmabuf_feedback_send(dmabuf_feedback, format_table, res, false); @@ -794,7 +796,16 @@ weston_dmabuf_feedback_send_all(struct weston_dmabuf_feedback *dmabuf_feedback, static void dmabuf_feedback_resource_destroy(struct wl_resource *resource) { + struct weston_surface *surface = + wl_resource_get_user_data(resource); + wl_list_remove(wl_resource_get_link(resource)); + + if (surface && + wl_list_empty(&surface->dmabuf_feedback->resource_list)) { + weston_dmabuf_feedback_destroy(surface->dmabuf_feedback); + surface->dmabuf_feedback = NULL; + } } static void @@ -810,7 +821,8 @@ zwp_linux_dmabuf_feedback_implementation = { static struct wl_resource * dmabuf_feedback_resource_create(struct wl_resource *dmabuf_resource, - struct wl_client *client, uint32_t dmabuf_feedback_id) + struct wl_client *client, uint32_t dmabuf_feedback_id, + struct weston_surface *surface) { struct wl_resource *dmabuf_feedback_res; uint32_t version; @@ -826,7 +838,7 @@ dmabuf_feedback_resource_create(struct wl_resource *dmabuf_resource, wl_list_init(wl_resource_get_link(dmabuf_feedback_res)); wl_resource_set_implementation(dmabuf_feedback_res, &zwp_linux_dmabuf_feedback_implementation, - NULL, dmabuf_feedback_resource_destroy); + surface, dmabuf_feedback_resource_destroy); return dmabuf_feedback_res; } @@ -842,7 +854,8 @@ linux_dmabuf_get_default_feedback(struct wl_client *client, dmabuf_feedback_resource = dmabuf_feedback_resource_create(dmabuf_resource, - client, dmabuf_feedback_id); + client, dmabuf_feedback_id, + NULL); if (!dmabuf_feedback_resource) { wl_resource_post_no_memory(dmabuf_resource); return; @@ -853,22 +866,55 @@ linux_dmabuf_get_default_feedback(struct wl_client *client, dmabuf_feedback_resource, true); } +static int +create_surface_dmabuf_feedback(struct weston_compositor *ec, + struct weston_surface *surface) +{ + struct weston_dmabuf_feedback_tranche *tranche; + dev_t main_device = ec->default_dmabuf_feedback->main_device; + uint32_t flags = 0; + + surface->dmabuf_feedback = weston_dmabuf_feedback_create(main_device); + if (!surface->dmabuf_feedback) + return -1; + + tranche = weston_dmabuf_feedback_tranche_create(surface->dmabuf_feedback, + ec->dmabuf_feedback_format_table, + main_device, flags, + RENDERER_PREF); + if (!tranche) { + weston_dmabuf_feedback_destroy(surface->dmabuf_feedback); + surface->dmabuf_feedback = NULL; + return -1; + } + + return 0; +} + static void linux_dmabuf_get_per_surface_feedback(struct wl_client *client, struct wl_resource *dmabuf_resource, uint32_t dmabuf_feedback_id, struct wl_resource *surface_resource) { + struct weston_compositor *compositor = + wl_resource_get_user_data(dmabuf_resource); struct weston_surface *surface = wl_resource_get_user_data(surface_resource); struct wl_resource *dmabuf_feedback_resource; + int ret; dmabuf_feedback_resource = dmabuf_feedback_resource_create(dmabuf_resource, - client, dmabuf_feedback_id); - if (!dmabuf_feedback_resource) { - wl_resource_post_no_memory(dmabuf_resource); - return; + client, dmabuf_feedback_id, + surface); + if (!dmabuf_feedback_resource) + goto err; + + if (!surface->dmabuf_feedback) { + ret = create_surface_dmabuf_feedback(compositor, surface); + if (ret < 0) + goto err_feedback; } /* Surface dma-buf feedback is dynamic and may need to be resent to @@ -879,6 +925,13 @@ linux_dmabuf_get_per_surface_feedback(struct wl_client *client, weston_dmabuf_feedback_send(surface->dmabuf_feedback, surface->compositor->dmabuf_feedback_format_table, dmabuf_feedback_resource, true); + return; + +err_feedback: + wl_resource_set_user_data(dmabuf_feedback_resource, NULL); + wl_resource_destroy(dmabuf_feedback_resource); +err: + wl_resource_post_no_memory(dmabuf_resource); } /** Get the linux_dmabuf_buffer from a wl_buffer resource From 0d8e94af61f11e5f6137fec1ed1fa18fed5e9e3e Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Mon, 7 Feb 2022 10:59:41 +0200 Subject: [PATCH 219/609] libweston: Rename weston_surface_destroy() to weston_surface_unref() Make it obvious that weston_surface has a reference counting happening and destruction of the weston_surface happens when the last weston_surface reference has been accounted for. Signed-off-by: Marius Vlad --- desktop-shell/shell.c | 4 ++-- include/libweston/libweston.h | 2 +- libweston/compositor.c | 8 ++++---- shell-utils/shell-utils.c | 4 ++-- tests/surface-global-test.c | 2 +- tests/surface-test.c | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 53d804ca..0609b86d 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -2352,7 +2352,7 @@ desktop_surface_removed(struct weston_desktop_surface *desktop_surface, fade_out_done, shsurf); return; } else { - weston_surface_destroy(surface); + weston_surface_unref(surface); } } @@ -4830,7 +4830,7 @@ desktop_shell_destroy_views_on_layer(struct weston_layer *layer) if (shsurf) desktop_shell_destroy_surface(shsurf); else if (is_black_surface_view(view, NULL)) - weston_surface_destroy(view->surface); + weston_surface_unref(view->surface); } weston_layer_fini(layer); diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 2eec0be3..c695c250 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -2030,7 +2030,7 @@ weston_slide_run(struct weston_view *view, float start, float stop, weston_view_animation_done_func_t done, void *data); void -weston_surface_destroy(struct weston_surface *surface); +weston_surface_unref(struct weston_surface *surface); int weston_output_mode_switch_to_temporary(struct weston_output *output, diff --git a/libweston/compositor.c b/libweston/compositor.c index 1567f7f7..91425fb1 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -2296,7 +2296,7 @@ weston_view_destroy(struct weston_view *view) } WL_EXPORT void -weston_surface_destroy(struct weston_surface *surface) +weston_surface_unref(struct weston_surface *surface) { struct wl_resource *cb, *next; struct weston_view *ev, *nv; @@ -2358,7 +2358,7 @@ destroy_surface(struct wl_resource *resource) /* Set the resource to NULL, since we don't want to leave a * dangling pointer if the surface was refcounted and survives - * the weston_surface_destroy() call. */ + * the weston_surface_unref() call. */ surface->resource = NULL; if (surface->viewport_resource) @@ -2369,7 +2369,7 @@ destroy_surface(struct wl_resource *resource) NULL); } - weston_surface_destroy(surface); + weston_surface_unref(surface); } static void @@ -4213,7 +4213,7 @@ compositor_create_surface(struct wl_client *client, return; err_res: - weston_surface_destroy(surface); + weston_surface_unref(surface); err: wl_resource_post_no_memory(resource); } diff --git a/shell-utils/shell-utils.c b/shell-utils/shell-utils.c index e57ec8ac..2bf1d3bd 100644 --- a/shell-utils/shell-utils.c +++ b/shell-utils/shell-utils.c @@ -192,7 +192,7 @@ weston_curtain_create(struct weston_compositor *compositor, err_view: weston_view_destroy(view); err_surface: - weston_surface_destroy(surface); + weston_surface_unref(surface); err_curtain: free(curtain); err: @@ -206,7 +206,7 @@ weston_curtain_destroy(struct weston_curtain *curtain) struct weston_surface *surface = curtain->view->surface; weston_view_destroy(curtain->view); - weston_surface_destroy(surface); + weston_surface_unref(surface); weston_buffer_destroy_solid(curtain->buffer_ref); free(curtain); } diff --git a/tests/surface-global-test.c b/tests/surface-global-test.c index a9e4ff9d..63393986 100644 --- a/tests/surface-global-test.c +++ b/tests/surface-global-test.c @@ -90,5 +90,5 @@ PLUGIN_TEST(surface_to_from_global) assert(ix == 0 && iy == 0); /* Destroys all views too. */ - weston_surface_destroy(surface); + weston_surface_unref(surface); } diff --git a/tests/surface-test.c b/tests/surface-test.c index 68288a86..1a1db287 100644 --- a/tests/surface-test.c +++ b/tests/surface-test.c @@ -70,5 +70,5 @@ PLUGIN_TEST(surface_transform) assert(x == 200 && y == 340); /* Destroys all views too. */ - weston_surface_destroy(surface); + weston_surface_unref(surface); } From d3ed2eb34533e901f4d02c45058318bc3f922cb2 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Mon, 7 Feb 2022 11:05:14 +0200 Subject: [PATCH 220/609] libweston: Assert if ref-count balance is wrong Calling weston_surface_unref() one time too many could be a sign we haven't correctly increased the ref count for it. Also, if we don't have a surface being passed, do no attempt to use it. Signed-off-by: Marius Vlad --- libweston/compositor.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libweston/compositor.c b/libweston/compositor.c index 91425fb1..fe92a462 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -2303,6 +2303,10 @@ weston_surface_unref(struct weston_surface *surface) struct weston_pointer_constraint *constraint, *next_constraint; struct weston_paint_node *pnode, *pntmp; + if (!surface) + return; + + assert(surface->ref_count > 0); if (--surface->ref_count > 0) return; From bd8314078de81cf19bfc75e23c12d730dc18c63e Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Mon, 14 Feb 2022 22:42:22 +0200 Subject: [PATCH 221/609] libweston, desktop-shell: Add a wrapper for weston_surface reference Similar to how we do it with drm_fb ref counts, increase a reference count and return the same object. Plug-in in desktop-shell when we map up the view in order to survive a weston_surface destruction. Astute readers will notice that this patch removes weston_view_destroy() while keeping the balance between removing and adding a weston_surface_unref() call in desktop_shell_destroy_surface(). The reason is to let weston_surface_unref() handle destruction on its own. If multiple references are taken, then weston_surface_unref() doesn't destroy the view, it just decreases the reference, with a latter call to weston_surface_unref() to determine if the view should be destroyed as well. This situation happens if we have close animation enabled, were we have more than one reference taken: one when mapping the view/surface and when when the surface itself was created, (what we call, a weak reference). If only a single reference is taken (for instance if we don't have close animations enabled) then this weston_surface_unref() call is inert as that reference is not set-up, leaving libweston to handle the view destruction. Following that with a weston_view_destroy() explicit call would cause a UAF as the view was previous destroyed by a weston_surface_unref() call. A side-effect of not keeping the weston_view_destroy() call would happen when tearing down the compositor. If close animations are enabled, weston_surface_unref() would not destroy the view, and because weston_view_destroy() no longer exists, we would still have the view in the other layers by the time we check-up if layers have views present. Signed-off-by: Marius Vlad --- desktop-shell/shell.c | 10 ++++++---- include/libweston/libweston.h | 3 +++ libweston/compositor.c | 10 ++++++++++ 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 0609b86d..9957e016 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -101,6 +101,7 @@ struct shell_surface { struct weston_desktop_surface *desktop_surface; struct weston_view *view; + struct weston_surface *wsurface_anim_fade; int32_t last_width, last_height; struct desktop_shell *shell; @@ -263,8 +264,8 @@ desktop_shell_destroy_surface(struct shell_surface *shsurf) wl_list_remove(&shsurf->children_link); wl_signal_emit(&shsurf->destroy_signal, shsurf); + weston_surface_unref(shsurf->wsurface_anim_fade); - weston_view_destroy(shsurf->view); if (shsurf->output_destroy_listener.notify) { wl_list_remove(&shsurf->output_destroy_listener.link); shsurf->output_destroy_listener.notify = NULL; @@ -2351,8 +2352,6 @@ desktop_surface_removed(struct weston_desktop_surface *desktop_surface, weston_fade_run(shsurf->view, 1.0, 0.0, 300.0, fade_out_done, shsurf); return; - } else { - weston_surface_unref(surface); } } @@ -2475,8 +2474,11 @@ desktop_surface_committed(struct weston_desktop_surface *desktop_surface, if (!weston_surface_is_mapped(surface)) { map(shell, shsurf, sx, sy); surface->is_mapped = true; + /* as we need to survive the weston_surface destruction we'll + * need to take another reference */ if (shsurf->shell->win_close_animation_type == ANIMATION_FADE) - ++surface->ref_count; + shsurf->wsurface_anim_fade = + weston_surface_ref(surface); return; } diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index c695c250..18677c19 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -2029,6 +2029,9 @@ struct weston_view_animation * weston_slide_run(struct weston_view *view, float start, float stop, weston_view_animation_done_func_t done, void *data); +struct weston_surface * +weston_surface_ref(struct weston_surface *surface); + void weston_surface_unref(struct weston_surface *surface); diff --git a/libweston/compositor.c b/libweston/compositor.c index fe92a462..b45f7f05 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -2295,6 +2295,16 @@ weston_view_destroy(struct weston_view *view) free(view); } +WL_EXPORT struct weston_surface * +weston_surface_ref(struct weston_surface *surface) +{ + assert(surface->ref_count < INT32_MAX && + surface->ref_count > 0); + + surface->ref_count++; + return surface; +} + WL_EXPORT void weston_surface_unref(struct weston_surface *surface) { From 9cf602840d0c49d882d84b6cfedfd44274f1c966 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Mon, 14 Feb 2022 22:57:08 +0200 Subject: [PATCH 222/609] desktop-shell: Create a distinct view for the fade-out close anim Creates a distinct view, separated from the one created by libweston-desktop, in order to avoid a potential ownership fight with libweston-desktop upon destroying the view. Upon weston_desktop_surface destruction, libweston-desktop inflicts damage on the view it creates, so we need the view to be alive at that time. This wasn't such an issue before because we had different destruction paths but with commit 'desktop-shell: Do not leave views in layers upon shell destruction' all of the destruction paths now land in the same spot + handle compositor tear down. Note as we still use the same weston_surface we'll keep the previous construct where we were taking a reference to keep it alive. The original view is destroyed when releasing the ownership, while for the view created in this patch we handle the destruction directly upon compositor tear down. Signed-off-by: Marius Vlad --- desktop-shell/shell.c | 57 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 9957e016..377611ff 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -102,6 +102,7 @@ struct shell_surface { struct weston_desktop_surface *desktop_surface; struct weston_view *view; struct weston_surface *wsurface_anim_fade; + struct weston_view *wview_anim_fade; int32_t last_width, last_height; struct desktop_shell *shell; @@ -195,6 +196,10 @@ struct shell_seat { }; +static struct weston_view * +shell_fade_create_fade_out_view(struct shell_surface *shsurf, + struct weston_surface *surface); + static struct desktop_shell * shell_surface_get_shell(struct shell_surface *shsurf); @@ -2229,8 +2234,8 @@ fade_out_done(struct weston_view_animation *animation, void *data) loop = wl_display_get_event_loop(shsurf->shell->compositor->wl_display); - if (weston_view_is_mapped(shsurf->view)) { - weston_view_unmap(shsurf->view); + if (weston_view_is_mapped(shsurf->wview_anim_fade)) { + weston_view_unmap(shsurf->wview_anim_fade); wl_event_loop_add_idle(loop, fade_out_done_idle_cb, shsurf); } } @@ -2349,8 +2354,25 @@ desktop_surface_removed(struct weston_desktop_surface *desktop_surface, pixman_region32_init(&surface->pending.input); pixman_region32_fini(&surface->input); pixman_region32_init(&surface->input); - weston_fade_run(shsurf->view, 1.0, 0.0, 300.0, + + /* its location might have changed, but also might've + * migrated to a different output, so re-compute this + * as the animation requires having the same output as + * the view */ + weston_view_set_output(shsurf->wview_anim_fade, + shsurf->view->output); + weston_view_set_position(shsurf->wview_anim_fade, + shsurf->view->geometry.x, + shsurf->view->geometry.y); + + weston_layer_entry_insert(&shsurf->view->layer_link, + &shsurf->wview_anim_fade->layer_link); + + /* unmap the "original" view */ + weston_view_unmap(shsurf->view); + weston_fade_run(shsurf->wview_anim_fade, 1.0, 0.0, 300.0, fade_out_done, shsurf); + return; } } @@ -2476,9 +2498,13 @@ desktop_surface_committed(struct weston_desktop_surface *desktop_surface, surface->is_mapped = true; /* as we need to survive the weston_surface destruction we'll * need to take another reference */ - if (shsurf->shell->win_close_animation_type == ANIMATION_FADE) + if (shsurf->shell->win_close_animation_type == ANIMATION_FADE) { shsurf->wsurface_anim_fade = weston_surface_ref(surface); + shsurf->wview_anim_fade = + shell_fade_create_fade_out_view(shsurf, surface); + } + return; } @@ -3901,6 +3927,29 @@ shell_fade_create_view_for_output(struct desktop_shell *shell, return curtain; } +static struct weston_view * +shell_fade_create_fade_out_view(struct shell_surface *shsurf, + struct weston_surface *surface) +{ + struct weston_view *view; + struct weston_output *woutput; + + view = weston_view_create(surface); + if (!view) + return NULL; + + woutput = get_focused_output(surface->compositor); + /* set the initial position and output just in case we happen to not + * move it around and just destroy it */ + weston_view_set_output(view, woutput); + weston_view_set_position(view, + shsurf->view->geometry.x, + shsurf->view->geometry.y); + view->is_mapped = true; + + return view; +} + static void shell_fade(struct desktop_shell *shell, enum fade_type type) { From be5b6f9cdc9fe7a8b21f6a085845fac7972e5dd0 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Thu, 5 May 2022 15:38:14 +0300 Subject: [PATCH 223/609] desktop-shell: Rename destroy_layer functions No functional change. Makes it obvious that we also call weston_layer_fini(). Signed-off-by: Marius Vlad --- desktop-shell/shell.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 377611ff..f4b6853d 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -855,7 +855,7 @@ animate_focus_change(struct desktop_shell *shell, struct workspace *ws, } static void -desktop_shell_destroy_views_on_layer(struct weston_layer *layer); +desktop_shell_destroy_layer(struct weston_layer *layer); static void workspace_destroy(struct workspace *ws) @@ -870,7 +870,7 @@ workspace_destroy(struct workspace *ws) if (ws->fsurf_back) focus_surface_destroy(ws->fsurf_back); - desktop_shell_destroy_views_on_layer(&ws->layer); + desktop_shell_destroy_layer(&ws->layer); free(ws); } @@ -4863,7 +4863,7 @@ setup_output_destroy_handler(struct weston_compositor *ec, } static void -desktop_shell_destroy_views_on_layer(struct weston_layer *layer) +desktop_shell_destroy_layer(struct weston_layer *layer) { struct weston_view *view, *view_next; @@ -4929,12 +4929,12 @@ shell_destroy(struct wl_listener *listener, void *data) workspace_destroy(*ws); wl_array_release(&shell->workspaces.array); - desktop_shell_destroy_views_on_layer(&shell->panel_layer); - desktop_shell_destroy_views_on_layer(&shell->background_layer); - desktop_shell_destroy_views_on_layer(&shell->lock_layer); - desktop_shell_destroy_views_on_layer(&shell->input_panel_layer); - desktop_shell_destroy_views_on_layer(&shell->minimized_layer); - desktop_shell_destroy_views_on_layer(&shell->fullscreen_layer); + desktop_shell_destroy_layer(&shell->panel_layer); + desktop_shell_destroy_layer(&shell->background_layer); + desktop_shell_destroy_layer(&shell->lock_layer); + desktop_shell_destroy_layer(&shell->input_panel_layer); + desktop_shell_destroy_layer(&shell->minimized_layer); + desktop_shell_destroy_layer(&shell->fullscreen_layer); free(shell->client); free(shell); From c41cdcabb48eb763bbff78e5d0433b6a193db62d Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Thu, 5 May 2022 15:32:27 +0300 Subject: [PATCH 224/609] desktop-shell: Migrate surface_unlink_view Moving weston_desktop_surface_unlink_view() to desktop_shell_destroy_surface() makes sure we don't leak the underlying weston_desktop_view when tearing/shutting down the compositor. Signed-off-by: Marius Vlad --- desktop-shell/shell.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index f4b6853d..3f82b5c2 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -267,6 +267,7 @@ desktop_shell_destroy_surface(struct shell_surface *shsurf) wl_list_init(&shsurf_child->children_link); } wl_list_remove(&shsurf->children_link); + weston_desktop_surface_unlink_view(shsurf->view); wl_signal_emit(&shsurf->destroy_signal, shsurf); weston_surface_unref(shsurf->wsurface_anim_fade); @@ -2345,7 +2346,6 @@ desktop_surface_removed(struct weston_desktop_surface *desktop_surface, weston_desktop_surface_set_user_data(shsurf->desktop_surface, NULL); shsurf->desktop_surface = NULL; - weston_desktop_surface_unlink_view(shsurf->view); if (weston_surface_is_mapped(surface) && shsurf->shell->win_close_animation_type == ANIMATION_FADE) { From d03f01377a4b5e7c80c81957141a57f2319300aa Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Fri, 18 Mar 2022 22:49:29 +0200 Subject: [PATCH 225/609] desktop-shell: Check for a valid desktop_surface This patch fixes the following trace: #0 0x7f07d1bcecfa in weston_desktop_surface_get_surface ../libweston-desktop/surface.c:585 #1 0x7f07d1bfc9b8 in move_grab_motion ../desktop-shell/shell.c:1499 #2 0x7f07e539f841 in notify_motion ../libweston/input.c:1794 #3 0x7f07e1e8ace4 in handle_pointer_motion ../libweston/libinput-device.c:132 #4 0x7f07e1e8cad5 in evdev_device_process_event ../libweston/libinput-device.c:535 #5 0x7f07e1e89311 in udev_input_process_event ../libweston/libinput-seat.c:208 #6 0x7f07e1e8932f in process_event ../libweston/libinput-seat.c:218 #7 0x7f07e1e8935f in process_events ../libweston/libinput-seat.c:228 #8 0x7f07e1e8940a in udev_input_dispatch ../libweston/libinput-seat.c:239 #9 0x7f07e1e89437 in libinput_source_dispatch ../libweston/libinput-seat.c:249 #10 0x7f07e53122b1 in wl_event_loop_dispatch ../src/event-loop.c:1027 #11 0x7f07e5310114 in wl_display_run ../src/wayland-server.c:1408 #12 0x7f07e579c7ba in wet_main ../compositor/main.c:3589 #13 0x555611d6b17d in main ../compositor/executable.c:33 #14 0x7f07e55be7fc in __libc_start_main ../csu/libc-start.c:332 #15 0x555611d6b099 in _start (/home/mvlad/install-amd64/bin/weston+0x1099) A highly unlikely, but still valid operation, is to close/destroy the window while still having it grabbed and moved around, basically having an in-flight destruction of grabbed moving window. Another situation would be that the client terminates abruptly (crashing for instance), while being dragged which might take down the compositor. This could happen for both touch/pointer grab operations and could cause a NULL pointer access while accessing desktop_surface when being used to retrieve the underlying weston_surface. With this patch we check for a valid desktop_surface and return early if that's not the case. Signed-off-by: Marius Vlad --- desktop-shell/shell.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 3f82b5c2..15ea3223 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -1360,7 +1360,7 @@ touch_move_grab_motion(struct weston_touch_grab *grab, int dx = wl_fixed_to_int(grab->touch->grab_x + move->dx); int dy = wl_fixed_to_int(grab->touch->grab_y + move->dy); - if (!shsurf || !move->active) + if (!shsurf || !shsurf->desktop_surface || !move->active) return; es = weston_desktop_surface_get_surface(shsurf->desktop_surface); @@ -1492,7 +1492,7 @@ move_grab_motion(struct weston_pointer_grab *grab, int cx, cy; weston_pointer_move(pointer, event); - if (!shsurf) + if (!shsurf || !shsurf->desktop_surface) return; surface = weston_desktop_surface_get_surface(shsurf->desktop_surface); From 299f87f0739ac3c73d71fb4000f18d455fcf4a4b Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Thu, 31 Mar 2022 23:20:45 +0300 Subject: [PATCH 226/609] desktop-shell: Clarify weston_view destruction at tear down This documents the fact that other views are handled implictly by libweston-desktop, and we shouldn't attempt to destroy indiscriminately views that aren't created by desktop-shell. Signed-off-by: Marius Vlad --- desktop-shell/shell.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 15ea3223..54469d3a 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -4875,9 +4875,17 @@ desktop_shell_destroy_layer(struct weston_layer *layer) * additional black_view created and added to its layer_link * fullscreen view. See shell_ensure_fullscreen_black_view() * - * As that black_view it is not a weston_desktop_surface - * we can't have a shsurf for it so we just destroy it like - * we do it in desktop_surface_removed() */ + * Note that we do not choose to destroy all other potential + * views we find in the layer, but instead we explicitly verify + * if the view in question was explicitly created by + * desktop-shell, rather than libweston-desktop (in + * desktop_surface_added()). + * + * This is particularly important because libweston-desktop + * could create additional views, which are managed implicitly, + * but which are still being added to the layer list. + * + */ if (shsurf) desktop_shell_destroy_surface(shsurf); else if (is_black_surface_view(view, NULL)) From db06aea1714facb3e1b789f18f3a5bf44adc0b96 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Fri, 13 May 2022 07:28:33 -0500 Subject: [PATCH 227/609] desktop-shell: Check height instead of checking width a second time Fix an apparent copy and paste error in resize code. I'm not sure anything sets the relevant callback that would lead to height being different than width, so there's no easy way to demonstrate a bug, but this change appears to rectify the intent of the code. Reported-by: Hideyuki Nagase Signed-off-by: Derek Foreman --- desktop-shell/shell.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 54469d3a..4641c030 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -1626,8 +1626,8 @@ resize_grab_motion(struct weston_pointer_grab *grab, width = max_size.width; if (height < min_size.height) height = min_size.height; - else if (max_size.width > 0 && width > max_size.width) - width = max_size.width; + else if (max_size.height > 0 && height > max_size.height) + height = max_size.height; weston_desktop_surface_set_size(shsurf->desktop_surface, width, height); } From 9b0b5b57ddca97fe2b563903bb7bd1308b31200f Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Thu, 12 May 2022 17:39:50 +0300 Subject: [PATCH 228/609] noop-renderer: Remove volatile and use compiler attribute clang-13 complains about bitwise xor assigments like the following: ../libweston/noop-renderer.c:62:25: warning: variable 'unused' set but not used [-Wunused-but-set-variable] volatile unsigned char unused = 0; Use the __attribute__((unused)) instead. Suggested-by: Pekka Paalanen Signed-off-by: Marius Vlad --- libweston/noop-renderer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libweston/noop-renderer.c b/libweston/noop-renderer.c index a105595f..e89200b3 100644 --- a/libweston/noop-renderer.c +++ b/libweston/noop-renderer.c @@ -58,7 +58,7 @@ noop_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) struct wl_shm_buffer *shm_buffer; uint8_t *data; uint32_t size, i, width, height, stride; - volatile unsigned char unused = 0; /* volatile so it's not optimized out */ + __attribute__((unused)) int unused = 0; if (!buffer) return; From ab42159bf307b0794156676e8451b0ffc89039b4 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Fri, 13 May 2022 20:11:46 +0300 Subject: [PATCH 229/609] desktop-shell: Add missing weston_view_destroy() This fixes the following weston_view leak at compositor shutdown: #0 0x7f4250247987 in __interceptor_calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:154 #1 0x7f424fd37aa3 in zalloc ../include/libweston/zalloc.h:38 #2 0x7f424fd3a05f in weston_view_create ../libweston/compositor.c:386 #3 0x7f423be7be6a in weston_desktop_surface_create_desktop_view ../libweston-desktop/surface.c:364 #4 0x7f423be7c0a8 in weston_desktop_surface_create_view ../libweston-desktop/surface.c:404 #5 0x7f423beae91d in desktop_surface_added ../desktop-shell/shell.c:2273 #6 0x7f423be77db1 in weston_desktop_api_surface_added ../libweston-desktop/libweston-desktop.c:138 #7 0x7f423be80c73 in weston_desktop_xdg_toplevel_ensure_added ../libweston-desktop/xdg-shell.c:362 #8 0x7f423be8207a in weston_desktop_xdg_toplevel_committed ../libweston-desktop/xdg-shell.c:697 #9 0x7f423be84d45 in weston_desktop_xdg_surface_committed ../libweston-desktop/xdg-shell.c:1374 #10 0x7f423be7b382 in weston_desktop_surface_surface_committed ../libweston-desktop/surface.c:174 #11 0x7f424fd378a6 in wl_signal_emit /home/mvlad/install-amd64/include/wayland-server-core.h:481 #12 0x7f424fd510e2 in weston_surface_commit_state ../libweston/compositor.c:4062 #13 0x7f424fd51161 in weston_surface_commit ../libweston/compositor.c:4068 #14 0x7f424fd516ef in surface_commit ../libweston/compositor.c:4146 #15 0x7f424fb597e9 (/usr/lib/x86_64-linux-gnu/libffi.so.8+0x77e9) With commit 'libweston, desktop-shell: Add a wrapper for weston_surface reference' we've removed an explicit weston_view_destroy() call due to a UAF which would've happen if we had close animations enabled, upon terminating a client. In that patch I've incorrectly wrote this happened when animations are off, but in fact it happened when they're on, see the following trace: READ of size 8 at 0x616000026498 thread T0 #0 0x7f757fba8797 in weston_signal_emit_mutable ../shared/signal.c:52 #1 0x7f757fb4bba1 in weston_view_destroy ../libweston/compositor.c:2269 #2 0x7f756bca89c0 in desktop_shell_destroy_surface ../desktop-shell/shell.c:275 #3 0x7f756bcb379e in fade_out_done_idle_cb ../desktop-shell/shell.c:2228 #4 0x7f757faec1da in wl_event_loop_dispatch_idle ../src/event-loop.c:969 #5 0x7f757faec31d in wl_event_loop_dispatch ../src/event-loop.c:1032 #6 0x7f757faea114 in wl_display_run ../src/wayland-server.c:1408 #7 0x7f757ff777ba in wet_main ../compositor/main.c:3589 #8 0x55f765c8d17d in main ../compositor/executable.c:33 #9 0x7f757fd997fc in __libc_start_main ../csu/libc-start.c:332 #10 0x55f765c8d099 in _start (/home/mvlad/install-amd64/bin/weston+0x1099) 0x616000026498 is located 24 bytes inside of 608-byte region [0x616000026480,0x6160000266e0) freed by thread T0 here: #0 0x7f758004c4d7 in __interceptor_free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:127 #1 0x7f757fb4bdc8 in weston_view_destroy ../libweston/compositor.c:2295 #2 0x7f757fb4c14d in weston_surface_unref ../libweston/compositor.c:2334 #3 0x7f756bca898b in desktop_shell_destroy_surface ../desktop-shell/shell.c:273 #4 0x7f756bcb379e in fade_out_done_idle_cb ../desktop-shell/shell.c:2228 #5 0x7f757faec1da in wl_event_loop_dispatch_idle ../src/event-loop.c:969 This patch re-introduces it to avoid leaking the view upon compositor shutdown, but it does it in tandem with weston_desktop_surface_unlink_view(), (which was added in a later patch) and before weston_surface_unref() call. This way we should be safe to terminate/close clients with additional views created by libweston-desktop (pop-ups), but also in other different situations. Verified it in the following circumstances: - terminating a client with close animation on - terminating a client with close animations off - shutting down the compositor with clients running, with and without close animations - terminating top-level clients with additional pop-ups with both with and without close animations Signed-off-by: Marius Vlad --- desktop-shell/shell.c | 1 + 1 file changed, 1 insertion(+) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 4641c030..72b06463 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -268,6 +268,7 @@ desktop_shell_destroy_surface(struct shell_surface *shsurf) } wl_list_remove(&shsurf->children_link); weston_desktop_surface_unlink_view(shsurf->view); + weston_view_destroy(shsurf->view); wl_signal_emit(&shsurf->destroy_signal, shsurf); weston_surface_unref(shsurf->wsurface_anim_fade); From 2a2eeb6a33ba5b711873412a5dcf1ce00ccd158f Mon Sep 17 00:00:00 2001 From: Robert Mader Date: Thu, 12 May 2022 17:11:17 +0200 Subject: [PATCH 230/609] libweston: Silence compiler warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Silences: `warning: ‘fourcc’ may be used uninitialized [-Wmaybe-uninitialized]` Signed-off-by: Robert Mader --- libweston/renderer-gl/gl-renderer.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 201bc7fa..3ab96109 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -2208,6 +2208,8 @@ gl_renderer_fill_buffer_info(struct weston_compositor *ec, case EGL_TEXTURE_Y_U_V_WL: fourcc = DRM_FORMAT_YUV420; break; + default: + assert(0 && "not reached"); } buffer->pixel_format = pixel_format_get_info(fourcc); From 564828fb96d6c65daef9f1e9e76cb965d3b7ec00 Mon Sep 17 00:00:00 2001 From: Robert Mader Date: Thu, 12 May 2022 17:14:11 +0200 Subject: [PATCH 231/609] rdp: Silence compiler warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This value is always `NULL`. Silences: `warning: ‘%s’ directive argument is null [-Wformat-overflow=]` Signed-off-by: Robert Mader --- libweston/backend-rdp/rdp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index f37aa6f8..8b69ae48 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -863,9 +863,9 @@ convert_rdp_keyboard_to_xkb_rule_names(UINT32 KeyboardType, xkbRuleNames->variant = 0; } - weston_log("%s: matching model=%s layout=%s variant=%s options=%s\n", + weston_log("%s: matching model=%s layout=%s variant=%s\n", __func__, xkbRuleNames->model, xkbRuleNames->layout, - xkbRuleNames->variant, xkbRuleNames->options); + xkbRuleNames->variant); } static BOOL From 0c65b23848257067bb4f6dbc7c2e076211b67aec Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 19 Jan 2022 00:14:51 +0000 Subject: [PATCH 232/609] libweston: Move renderer interface to internal header No-one should be implementing an external renderer, so move the interface out of the public header. Signed-off-by: Daniel Stone --- include/libweston/libweston.h | 33 -------------------------------- libweston/libweston-internal.h | 35 ++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 33 deletions(-) diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 18677c19..65c58428 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -974,39 +974,6 @@ struct weston_plane { struct weston_drm_format_array; -struct weston_renderer { - int (*read_pixels)(struct weston_output *output, - pixman_format_code_t format, void *pixels, - uint32_t x, uint32_t y, - uint32_t width, uint32_t height); - void (*repaint_output)(struct weston_output *output, - pixman_region32_t *output_damage); - void (*flush_damage)(struct weston_surface *surface, - struct weston_buffer *buffer); - void (*attach)(struct weston_surface *es, struct weston_buffer *buffer); - void (*destroy)(struct weston_compositor *ec); - - /** See weston_surface_get_content_size() */ - void (*surface_get_content_size)(struct weston_surface *surface, - int *width, int *height); - - /** See weston_surface_copy_content() */ - int (*surface_copy_content)(struct weston_surface *surface, - void *target, size_t size, - int src_x, int src_y, - int width, int height); - - /** See weston_compositor_import_dmabuf() */ - bool (*import_dmabuf)(struct weston_compositor *ec, - struct linux_dmabuf_buffer *buffer); - - const struct weston_drm_format_array * - (*get_supported_formats)(struct weston_compositor *ec); - - bool (*fill_buffer_info)(struct weston_compositor *ec, - struct weston_buffer *buffer); -}; - enum weston_capability { /* backend/renderer supports arbitrary rotation */ WESTON_CAP_ROTATION_ANY = 0x0001, diff --git a/libweston/libweston-internal.h b/libweston/libweston-internal.h index c40ba12c..e6a84942 100644 --- a/libweston/libweston-internal.h +++ b/libweston/libweston-internal.h @@ -43,6 +43,41 @@ #include #include "color.h" +/* compositor <-> renderer interface */ + +struct weston_renderer { + int (*read_pixels)(struct weston_output *output, + pixman_format_code_t format, void *pixels, + uint32_t x, uint32_t y, + uint32_t width, uint32_t height); + void (*repaint_output)(struct weston_output *output, + pixman_region32_t *output_damage); + void (*flush_damage)(struct weston_surface *surface, + struct weston_buffer *buffer); + void (*attach)(struct weston_surface *es, struct weston_buffer *buffer); + void (*destroy)(struct weston_compositor *ec); + + /** See weston_surface_get_content_size() */ + void (*surface_get_content_size)(struct weston_surface *surface, + int *width, int *height); + + /** See weston_surface_copy_content() */ + int (*surface_copy_content)(struct weston_surface *surface, + void *target, size_t size, + int src_x, int src_y, + int width, int height); + + /** See weston_compositor_import_dmabuf() */ + bool (*import_dmabuf)(struct weston_compositor *ec, + struct linux_dmabuf_buffer *buffer); + + const struct weston_drm_format_array * + (*get_supported_formats)(struct weston_compositor *ec); + + bool (*fill_buffer_info)(struct weston_compositor *ec, + struct weston_buffer *buffer); +}; + /* weston_buffer */ void From 72fc647a968a3990fc9c83febc26736fb15e2c30 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 13 Jan 2022 23:57:48 +0000 Subject: [PATCH 233/609] gl-renderer: Split buffer state away from surface state gl_surface_state has a bunch of members which are tied to the input buffer, rather than the surface per se. Split them out into a separate gl_buffer_state member as a first step towards sanitising its use and lifetime. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 328 +++++++++++++++------------- 1 file changed, 176 insertions(+), 152 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 3ab96109..77ff41ee 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -168,11 +168,9 @@ struct yuv_format_descriptor { struct yuv_plane_descriptor plane[4]; }; -struct gl_surface_state { +struct gl_buffer_state { GLfloat color[4]; - GLuint textures[3]; - int num_textures; bool needs_full_upload; pixman_region32_t texture_damage; @@ -186,8 +184,6 @@ struct gl_surface_state { int num_images; enum gl_shader_texture_variant shader_variant; - struct weston_buffer_reference buffer_ref; - struct weston_buffer_release_reference buffer_release_ref; enum buffer_type buffer_type; int pitch; /* in pixels */ int height; /* in pixels */ @@ -198,13 +194,25 @@ struct gl_surface_state { int offset[3]; /* offset per plane */ int hsub[3]; /* horizontal subsampling per plane */ int vsub[3]; /* vertical subsampling per plane */ +}; +struct gl_surface_state { struct weston_surface *surface; + struct gl_buffer_state buffer; + + /* These buffer references should really be attached to paint nodes + * rather than either buffer or surface state */ + struct weston_buffer_reference buffer_ref; + struct weston_buffer_release_reference buffer_release_ref; + /* Whether this surface was used in the current output repaint. Used only in the context of a gl_renderer_repaint_output call. */ bool used_in_output_repaint; + GLuint textures[3]; + int num_textures; + struct wl_listener surface_destroy_listener; struct wl_listener renderer_destroy_listener; }; @@ -568,6 +576,7 @@ texture_region(struct weston_view *ev, pixman_region32_t *surf_region) { struct gl_surface_state *gs = get_surface_state(ev->surface); + struct gl_buffer_state *gb = &gs->buffer; struct weston_compositor *ec = ev->surface->compositor; struct gl_renderer *gr = get_renderer(ec); GLfloat *v, inv_width, inv_height; @@ -593,8 +602,8 @@ texture_region(struct weston_view *ev, v = wl_array_add(&gr->vertices, nrects * nsurf * 8 * 4 * sizeof *v); vtxcnt = wl_array_add(&gr->vtxcnt, nrects * nsurf * sizeof *vtxcnt); - inv_width = 1.0 / gs->pitch; - inv_height = 1.0 / gs->height; + inv_width = 1.0 / gb->pitch; + inv_height = 1.0 / gb->height; for (i = 0; i < nrects; i++) { pixman_box32_t *rect = &rects[i]; @@ -634,10 +643,10 @@ texture_region(struct weston_view *ev, sx, sy, &bx, &by); *(v++) = bx * inv_width; - if (gs->y_inverted) { + if (gb->y_inverted) { *(v++) = by * inv_height; } else { - *(v++) = (gs->height - by) * inv_height; + *(v++) = (gb->height - by) * inv_height; } } @@ -967,6 +976,7 @@ maybe_censor_override(struct gl_shader_config *sconf, struct weston_view *ev) { struct gl_surface_state *gs = get_surface_state(ev->surface); + struct gl_buffer_state *gb = &gs->buffer; bool recording_censor = (output->disable_planes > 0) && (ev->surface->desired_protection > WESTON_HDCP_DISABLE); @@ -974,7 +984,7 @@ maybe_censor_override(struct gl_shader_config *sconf, bool unprotected_censor = (ev->surface->desired_protection > output->current_protection); - if (gs->direct_display) { + if (gb->direct_display) { censor_override(sconf, output); return; } @@ -993,14 +1003,15 @@ static void gl_shader_config_set_input_textures(struct gl_shader_config *sconf, struct gl_surface_state *gs) { + struct gl_buffer_state *gb = &gs->buffer; int i; - sconf->req.variant = gs->shader_variant; + sconf->req.variant = gb->shader_variant; sconf->req.input_is_premult = - gl_shader_texture_variant_can_be_premult(gs->shader_variant); + gl_shader_texture_variant_can_be_premult(gb->shader_variant); for (i = 0; i < 4; i++) - sconf->unicolor[i] = gs->color[i]; + sconf->unicolor[i] = gb->color[i]; assert(gs->num_textures <= GL_SHADER_INPUT_TEX_MAX); for (i = 0; i < gs->num_textures; i++) @@ -1042,6 +1053,7 @@ draw_paint_node(struct weston_paint_node *pnode, { struct gl_renderer *gr = get_renderer(pnode->surface->compositor); struct gl_surface_state *gs = get_surface_state(pnode->surface); + struct gl_buffer_state *gb = &gs->buffer; /* repaint bounding region in global coordinates: */ pixman_region32_t repaint; /* opaque region in surface coordinates: */ @@ -1054,7 +1066,7 @@ draw_paint_node(struct weston_paint_node *pnode, /* In case of a runtime switch of renderers, we may not have received * an attach for this surface since the switch. In that case we don't * have a valid buffer or a proper shader set up so skip rendering. */ - if (gs->shader_variant == SHADER_VARIANT_NONE && !gs->direct_display) + if (gb->shader_variant == SHADER_VARIANT_NONE && !gb->direct_display) return; pixman_region32_init(&repaint); @@ -1845,6 +1857,7 @@ gl_renderer_flush_damage(struct weston_surface *surface, const struct weston_testsuite_quirks *quirks = &surface->compositor->test_data.test_quirks; struct gl_surface_state *gs = get_surface_state(surface); + struct gl_buffer_state *gb = &gs->buffer; struct weston_view *view; bool texture_used; pixman_box32_t *rectangles; @@ -1853,8 +1866,8 @@ gl_renderer_flush_damage(struct weston_surface *surface, assert(buffer); - pixman_region32_union(&gs->texture_damage, - &gs->texture_damage, &surface->damage); + pixman_region32_union(&gb->texture_damage, + &gb->texture_damage, &surface->damage); /* This can happen if a SHM wl_buffer gets destroyed before we flush * damage, because wayland-server just nukes the wl_shm_buffer from @@ -1877,36 +1890,36 @@ gl_renderer_flush_damage(struct weston_surface *surface, if (!texture_used) return; - if (!pixman_region32_not_empty(&gs->texture_damage) && - !gs->needs_full_upload) + if (!pixman_region32_not_empty(&gb->texture_damage) && + !gb->needs_full_upload) goto done; data = wl_shm_buffer_get_data(buffer->shm_buffer); glActiveTexture(GL_TEXTURE0); - if (gs->needs_full_upload || quirks->gl_force_full_upload) { + if (gb->needs_full_upload || quirks->gl_force_full_upload) { glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, 0); glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, 0); wl_shm_buffer_begin_access(buffer->shm_buffer); for (j = 0; j < gs->num_textures; j++) { glBindTexture(GL_TEXTURE_2D, gs->textures[j]); glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, - gs->pitch / gs->hsub[j]); + gb->pitch / gb->hsub[j]); glTexImage2D(GL_TEXTURE_2D, 0, - gs->gl_format[j], - gs->pitch / gs->hsub[j], - buffer->height / gs->vsub[j], + gb->gl_format[j], + gb->pitch / gb->hsub[j], + buffer->height / gb->vsub[j], 0, - gl_format_from_internal(gs->gl_format[j]), - gs->gl_pixel_type, - data + gs->offset[j]); + gl_format_from_internal(gb->gl_format[j]), + gb->gl_pixel_type, + data + gb->offset[j]); } wl_shm_buffer_end_access(buffer->shm_buffer); goto done; } - rectangles = pixman_region32_rectangles(&gs->texture_damage, &n); + rectangles = pixman_region32_rectangles(&gb->texture_damage, &n); wl_shm_buffer_begin_access(buffer->shm_buffer); for (i = 0; i < n; i++) { pixman_box32_t r; @@ -1916,27 +1929,27 @@ gl_renderer_flush_damage(struct weston_surface *surface, for (j = 0; j < gs->num_textures; j++) { glBindTexture(GL_TEXTURE_2D, gs->textures[j]); glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, - gs->pitch / gs->hsub[j]); + gb->pitch / gb->hsub[j]); glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, - r.x1 / gs->hsub[j]); + r.x1 / gb->hsub[j]); glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, - r.y1 / gs->hsub[j]); + r.y1 / gb->hsub[j]); glTexSubImage2D(GL_TEXTURE_2D, 0, - r.x1 / gs->hsub[j], - r.y1 / gs->vsub[j], - (r.x2 - r.x1) / gs->hsub[j], - (r.y2 - r.y1) / gs->vsub[j], - gl_format_from_internal(gs->gl_format[j]), - gs->gl_pixel_type, - data + gs->offset[j]); + r.x1 / gb->hsub[j], + r.y1 / gb->vsub[j], + (r.x2 - r.x1) / gb->hsub[j], + (r.y2 - r.y1) / gb->vsub[j], + gl_format_from_internal(gb->gl_format[j]), + gb->gl_pixel_type, + data + gb->offset[j]); } } wl_shm_buffer_end_access(buffer->shm_buffer); done: - pixman_region32_fini(&gs->texture_damage); - pixman_region32_init(&gs->texture_damage); - gs->needs_full_upload = false; + pixman_region32_fini(&gb->texture_damage); + pixman_region32_init(&gb->texture_damage); + gb->needs_full_upload = false; weston_buffer_reference(&gs->buffer_ref, buffer, BUFFER_WILL_NOT_BE_ACCESSED); @@ -1949,7 +1962,8 @@ ensure_textures(struct gl_surface_state *gs, GLenum target, int num_textures) int i; if (num_textures <= gs->num_textures) { - glDeleteTextures(gs->num_textures - num_textures, &gs->textures[num_textures]); + glDeleteTextures(gs->num_textures - num_textures, + &gs->textures[num_textures]); gs->num_textures = num_textures; return; } @@ -1973,6 +1987,7 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer, struct weston_compositor *ec = es->compositor; struct gl_renderer *gr = get_renderer(ec); struct gl_surface_state *gs = get_surface_state(es); + struct gl_buffer_state *gb = &gs->buffer; GLenum gl_format[3] = {0, 0, 0}; GLenum gl_pixel_type; int pitch; @@ -1980,27 +1995,27 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer, bool using_glesv2 = gr->gl_version < gr_gl_version(3, 0); num_planes = 1; - gs->offset[0] = 0; - gs->hsub[0] = 1; - gs->vsub[0] = 1; + gb->offset[0] = 0; + gb->hsub[0] = 1; + gb->vsub[0] = 1; switch (wl_shm_buffer_get_format(shm_buffer)) { case WL_SHM_FORMAT_XRGB8888: - gs->shader_variant = SHADER_VARIANT_RGBX; + gb->shader_variant = SHADER_VARIANT_RGBX; pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; gl_format[0] = GL_BGRA_EXT; gl_pixel_type = GL_UNSIGNED_BYTE; es->is_opaque = true; break; case WL_SHM_FORMAT_ARGB8888: - gs->shader_variant = SHADER_VARIANT_RGBA; + gb->shader_variant = SHADER_VARIANT_RGBA; pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; gl_format[0] = GL_BGRA_EXT; gl_pixel_type = GL_UNSIGNED_BYTE; es->is_opaque = false; break; case WL_SHM_FORMAT_RGB565: - gs->shader_variant = SHADER_VARIANT_RGBX; + gb->shader_variant = SHADER_VARIANT_RGBX; pitch = wl_shm_buffer_get_stride(shm_buffer) / 2; gl_format[0] = GL_RGB; gl_pixel_type = GL_UNSIGNED_SHORT_5_6_5; @@ -2011,7 +2026,7 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer, if (!gr->has_texture_type_2_10_10_10_rev) { goto unsupported; } - gs->shader_variant = SHADER_VARIANT_RGBA; + gb->shader_variant = SHADER_VARIANT_RGBA; pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; gl_format[0] = using_glesv2 ? GL_RGBA : GL_RGB10_A2; gl_pixel_type = GL_UNSIGNED_INT_2_10_10_10_REV_EXT; @@ -2021,7 +2036,7 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer, if (!gr->has_texture_type_2_10_10_10_rev) { goto unsupported; } - gs->shader_variant = SHADER_VARIANT_RGBX; + gb->shader_variant = SHADER_VARIANT_RGBX; pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; gl_format[0] = using_glesv2 ? GL_RGBA : GL_RGB10_A2; gl_pixel_type = GL_UNSIGNED_INT_2_10_10_10_REV_EXT; @@ -2030,7 +2045,7 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer, case WL_SHM_FORMAT_ABGR16161616F: if (!gr->gl_supports_color_transforms) goto unsupported; - gs->shader_variant = SHADER_VARIANT_RGBA; + gb->shader_variant = SHADER_VARIANT_RGBA; pitch = wl_shm_buffer_get_stride(shm_buffer) / 8; gl_format[0] = GL_RGBA16F; gl_pixel_type = GL_HALF_FLOAT; @@ -2039,7 +2054,7 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer, case WL_SHM_FORMAT_XBGR16161616F: if (!gr->gl_supports_color_transforms) goto unsupported; - gs->shader_variant = SHADER_VARIANT_RGBX; + gb->shader_variant = SHADER_VARIANT_RGBX; pitch = wl_shm_buffer_get_stride(shm_buffer) / 8; gl_format[0] = GL_RGBA16F; gl_pixel_type = GL_HALF_FLOAT; @@ -2048,7 +2063,7 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer, case WL_SHM_FORMAT_ABGR16161616: if (!gr->has_texture_norm16) goto unsupported; - gs->shader_variant = SHADER_VARIANT_RGBA; + gb->shader_variant = SHADER_VARIANT_RGBA; pitch = wl_shm_buffer_get_stride(shm_buffer) / 8; gl_format[0] = GL_RGBA16_EXT; gl_pixel_type = GL_UNSIGNED_SHORT; @@ -2057,7 +2072,7 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer, case WL_SHM_FORMAT_XBGR16161616: if (!gr->has_texture_norm16) goto unsupported; - gs->shader_variant = SHADER_VARIANT_RGBX; + gb->shader_variant = SHADER_VARIANT_RGBX; pitch = wl_shm_buffer_get_stride(shm_buffer) / 8; gl_format[0] = GL_RGBA16_EXT; gl_pixel_type = GL_UNSIGNED_SHORT; @@ -2065,18 +2080,18 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer, break; #endif case WL_SHM_FORMAT_YUV420: - gs->shader_variant = SHADER_VARIANT_Y_U_V; + gb->shader_variant = SHADER_VARIANT_Y_U_V; pitch = wl_shm_buffer_get_stride(shm_buffer); gl_pixel_type = GL_UNSIGNED_BYTE; num_planes = 3; - gs->offset[1] = gs->offset[0] + (pitch / gs->hsub[0]) * - (buffer->height / gs->vsub[0]); - gs->hsub[1] = 2; - gs->vsub[1] = 2; - gs->offset[2] = gs->offset[1] + (pitch / gs->hsub[1]) * - (buffer->height / gs->vsub[1]); - gs->hsub[2] = 2; - gs->vsub[2] = 2; + gb->offset[1] = gb->offset[0] + (pitch / gb->hsub[0]) * + (buffer->height / gb->vsub[0]); + gb->hsub[1] = 2; + gb->vsub[1] = 2; + gb->offset[2] = gb->offset[1] + (pitch / gb->hsub[1]) * + (buffer->height / gb->vsub[1]); + gb->hsub[2] = 2; + gb->vsub[2] = 2; if (gr->has_gl_texture_rg) { gl_format[0] = GL_R8_EXT; gl_format[1] = GL_R8_EXT; @@ -2092,29 +2107,29 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer, pitch = wl_shm_buffer_get_stride(shm_buffer); gl_pixel_type = GL_UNSIGNED_BYTE; num_planes = 2; - gs->offset[1] = gs->offset[0] + (pitch / gs->hsub[0]) * - (buffer->height / gs->vsub[0]); - gs->hsub[1] = 2; - gs->vsub[1] = 2; + gb->offset[1] = gb->offset[0] + (pitch / gb->hsub[0]) * + (buffer->height / gb->vsub[0]); + gb->hsub[1] = 2; + gb->vsub[1] = 2; if (gr->has_gl_texture_rg) { - gs->shader_variant = SHADER_VARIANT_Y_UV; + gb->shader_variant = SHADER_VARIANT_Y_UV; gl_format[0] = GL_R8_EXT; gl_format[1] = GL_RG8_EXT; } else { - gs->shader_variant = SHADER_VARIANT_Y_XUXV; + gb->shader_variant = SHADER_VARIANT_Y_XUXV; gl_format[0] = GL_LUMINANCE; gl_format[1] = GL_LUMINANCE_ALPHA; } es->is_opaque = true; break; case WL_SHM_FORMAT_YUYV: - gs->shader_variant = SHADER_VARIANT_Y_XUXV; + gb->shader_variant = SHADER_VARIANT_Y_XUXV; pitch = wl_shm_buffer_get_stride(shm_buffer) / 2; gl_pixel_type = GL_UNSIGNED_BYTE; num_planes = 2; - gs->offset[1] = 0; - gs->hsub[1] = 2; - gs->vsub[1] = 1; + gb->offset[1] = 0; + gb->hsub[1] = 2; + gb->vsub[1] = 1; if (gr->has_gl_texture_rg) gl_format[0] = GL_RG8_EXT; else @@ -2127,7 +2142,7 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer, * [31:0] X:Y:Cb:Cr 8:8:8:8 little endian * a:b: g: r in SHADER_VARIANT_XYUV */ - gs->shader_variant = SHADER_VARIANT_XYUV; + gb->shader_variant = SHADER_VARIANT_XYUV; pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; gl_format[0] = GL_RGBA; gl_pixel_type = GL_UNSIGNED_BYTE; @@ -2143,23 +2158,23 @@ unsupported: /* Only allocate a texture if it doesn't match existing one. * If a switch from DRM allocated buffer to a SHM buffer is * happening, we need to allocate a new texture buffer. */ - if (pitch != gs->pitch || - buffer->height != gs->height || - gl_format[0] != gs->gl_format[0] || - gl_format[1] != gs->gl_format[1] || - gl_format[2] != gs->gl_format[2] || - gl_pixel_type != gs->gl_pixel_type || - gs->buffer_type != BUFFER_TYPE_SHM) { - gs->pitch = pitch; - gs->height = buffer->height; - gs->gl_format[0] = gl_format[0]; - gs->gl_format[1] = gl_format[1]; - gs->gl_format[2] = gl_format[2]; - gs->gl_pixel_type = gl_pixel_type; - gs->buffer_type = BUFFER_TYPE_SHM; - gs->needs_full_upload = true; - gs->y_inverted = true; - gs->direct_display = false; + if (pitch != gb->pitch || + buffer->height != gb->height || + gl_format[0] != gb->gl_format[0] || + gl_format[1] != gb->gl_format[1] || + gl_format[2] != gb->gl_format[2] || + gl_pixel_type != gb->gl_pixel_type || + gb->buffer_type != BUFFER_TYPE_SHM) { + gb->pitch = pitch; + gb->height = buffer->height; + gb->gl_format[0] = gl_format[0]; + gb->gl_format[1] = gl_format[1]; + gb->gl_format[2] = gl_format[2]; + gb->gl_pixel_type = gl_pixel_type; + gb->buffer_type = BUFFER_TYPE_SHM; + gb->needs_full_upload = true; + gb->y_inverted = true; + gb->direct_display = false; gs->surface = es; @@ -2235,13 +2250,14 @@ gl_renderer_attach_egl(struct weston_surface *es, struct weston_buffer *buffer, struct weston_compositor *ec = es->compositor; struct gl_renderer *gr = get_renderer(ec); struct gl_surface_state *gs = get_surface_state(es); + struct gl_buffer_state *gb = &gs->buffer; EGLint attribs[5]; GLenum target; int i, num_planes; - for (i = 0; i < gs->num_images; i++) { - egl_image_unref(gs->images[i]); - gs->images[i] = NULL; + for (i = 0; i < gb->num_images; i++) { + egl_image_unref(gb->images[i]); + gb->images[i] = NULL; } es->is_opaque = false; switch (format) { @@ -2251,31 +2267,31 @@ gl_renderer_attach_egl(struct weston_surface *es, struct weston_buffer *buffer, case EGL_TEXTURE_RGBA: default: num_planes = 1; - gs->shader_variant = SHADER_VARIANT_RGBA; + gb->shader_variant = SHADER_VARIANT_RGBA; break; case EGL_TEXTURE_EXTERNAL_WL: num_planes = 1; - gs->shader_variant = SHADER_VARIANT_EXTERNAL; + gb->shader_variant = SHADER_VARIANT_EXTERNAL; break; case EGL_TEXTURE_Y_UV_WL: num_planes = 2; - gs->shader_variant = SHADER_VARIANT_Y_UV; + gb->shader_variant = SHADER_VARIANT_Y_UV; es->is_opaque = true; break; case EGL_TEXTURE_Y_U_V_WL: num_planes = 3; - gs->shader_variant = SHADER_VARIANT_Y_U_V; + gb->shader_variant = SHADER_VARIANT_Y_U_V; es->is_opaque = true; break; case EGL_TEXTURE_Y_XUXV_WL: num_planes = 2; - gs->shader_variant = SHADER_VARIANT_Y_XUXV; + gb->shader_variant = SHADER_VARIANT_Y_XUXV; es->is_opaque = true; break; } - gs->num_images = num_planes; - target = gl_shader_texture_variant_get_target(gs->shader_variant); + gb->num_images = num_planes; + target = gl_shader_texture_variant_get_target(gb->shader_variant); ensure_textures(gs, target, num_planes); for (i = 0; i < num_planes; i++) { attribs[0] = EGL_WAYLAND_PLANE_WL; @@ -2284,24 +2300,24 @@ gl_renderer_attach_egl(struct weston_surface *es, struct weston_buffer *buffer, attribs[3] = EGL_TRUE; attribs[4] = EGL_NONE; - gs->images[i] = egl_image_create(gr, + gb->images[i] = egl_image_create(gr, EGL_WAYLAND_BUFFER_WL, buffer->legacy_buffer, attribs); - if (!gs->images[i]) { + if (!gb->images[i]) { weston_log("failed to create img for plane %d\n", i); continue; } glActiveTexture(GL_TEXTURE0 + i); glBindTexture(target, gs->textures[i]); - gr->image_target_texture_2d(target, gs->images[i]->image); + gr->image_target_texture_2d(target, gb->images[i]->image); } - gs->pitch = buffer->width; - gs->height = buffer->height; - gs->buffer_type = BUFFER_TYPE_EGL; - gs->y_inverted = (buffer->buffer_origin == ORIGIN_TOP_LEFT); + gb->pitch = buffer->width; + gb->height = buffer->height; + gb->buffer_type = BUFFER_TYPE_EGL; + gb->y_inverted = (buffer->buffer_origin == ORIGIN_TOP_LEFT); } static void @@ -2869,19 +2885,20 @@ gl_renderer_attach_dmabuf(struct weston_surface *surface, { struct gl_renderer *gr = get_renderer(surface->compositor); struct gl_surface_state *gs = get_surface_state(surface); + struct gl_buffer_state *gb = &gs->buffer; struct dmabuf_image *image; GLenum target; int i; - for (i = 0; i < gs->num_images; i++) - egl_image_unref(gs->images[i]); - gs->num_images = 0; + for (i = 0; i < gb->num_images; i++) + egl_image_unref(gb->images[i]); + gb->num_images = 0; - gs->pitch = buffer->width; - gs->height = buffer->height; - gs->buffer_type = BUFFER_TYPE_EGL; - gs->y_inverted = (buffer->buffer_origin == ORIGIN_TOP_LEFT); - gs->direct_display = dmabuf->direct_display; + gb->pitch = buffer->width; + gb->height = buffer->height; + gb->buffer_type = BUFFER_TYPE_EGL; + gb->y_inverted = (buffer->buffer_origin == ORIGIN_TOP_LEFT); + gb->direct_display = dmabuf->direct_display; surface->is_opaque = pixel_format_is_opaque(buffer->pixel_format); if (dmabuf->direct_display) @@ -2892,19 +2909,19 @@ gl_renderer_attach_dmabuf(struct weston_surface *surface, /* The dmabuf_image should have been created during the import */ assert(image != NULL); - gs->num_images = image->num_images; - for (i = 0; i < gs->num_images; ++i) - gs->images[i] = egl_image_ref(image->images[i]); + gb->num_images = image->num_images; + for (i = 0; i < gb->num_images; ++i) + gb->images[i] = egl_image_ref(image->images[i]); target = gl_shader_texture_variant_get_target(image->shader_variant); - ensure_textures(gs, target, gs->num_images); - for (i = 0; i < gs->num_images; ++i) { + ensure_textures(gs, target, gb->num_images); + for (i = 0; i < gb->num_images; ++i) { glActiveTexture(GL_TEXTURE0 + i); glBindTexture(target, gs->textures[i]); - gr->image_target_texture_2d(target, gs->images[i]->image); + gr->image_target_texture_2d(target, gb->images[i]->image); } - gs->shader_variant = image->shader_variant; + gb->shader_variant = image->shader_variant; } static const struct weston_drm_format_array * @@ -2977,16 +2994,17 @@ gl_renderer_surface_set_color(struct weston_surface *surface, float red, float green, float blue, float alpha) { struct gl_surface_state *gs = get_surface_state(surface); + struct gl_buffer_state *gb = &gs->buffer; - gs->color[0] = red; - gs->color[1] = green; - gs->color[2] = blue; - gs->color[3] = alpha; - gs->buffer_type = BUFFER_TYPE_SOLID; - gs->pitch = 1; - gs->height = 1; + gb->color[0] = red; + gb->color[1] = green; + gb->color[2] = blue; + gb->color[3] = alpha; + gb->buffer_type = BUFFER_TYPE_SOLID; + gb->pitch = 1; + gb->height = 1; - gs->shader_variant = SHADER_VARIANT_SOLID; + gb->shader_variant = SHADER_VARIANT_SOLID; } static void @@ -2995,6 +3013,7 @@ gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) struct weston_compositor *ec = es->compositor; struct gl_renderer *gr = get_renderer(ec); struct gl_surface_state *gs = get_surface_state(es); + struct gl_buffer_state *gb = &gs->buffer; EGLint format; int i; @@ -3005,16 +3024,16 @@ gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) es->buffer_release_ref.buffer_release); if (!buffer) { - for (i = 0; i < gs->num_images; i++) { - egl_image_unref(gs->images[i]); - gs->images[i] = NULL; + for (i = 0; i < gb->num_images; i++) { + egl_image_unref(gb->images[i]); + gb->images[i] = NULL; } - gs->num_images = 0; + gb->num_images = 0; glDeleteTextures(gs->num_textures, gs->textures); gs->num_textures = 0; - gs->buffer_type = BUFFER_TYPE_NULL; - gs->y_inverted = true; - gs->direct_display = false; + gb->buffer_type = BUFFER_TYPE_NULL; + gb->y_inverted = true; + gb->direct_display = false; es->is_opaque = false; return; } @@ -3057,8 +3076,8 @@ gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) weston_buffer_reference(&gs->buffer_ref, NULL, BUFFER_WILL_NOT_BE_ACCESSED); weston_buffer_release_reference(&gs->buffer_release_ref, NULL); - gs->buffer_type = BUFFER_TYPE_NULL; - gs->y_inverted = true; + gb->buffer_type = BUFFER_TYPE_NULL; + gb->y_inverted = true; es->is_opaque = false; weston_buffer_send_server_error(buffer, "disconnecting due to unhandled buffer type"); @@ -3069,13 +3088,14 @@ gl_renderer_surface_get_content_size(struct weston_surface *surface, int *width, int *height) { struct gl_surface_state *gs = get_surface_state(surface); + struct gl_buffer_state *gb = &gs->buffer; - if (gs->buffer_type == BUFFER_TYPE_NULL) { + if (gb->buffer_type == BUFFER_TYPE_NULL) { *width = 0; *height = 0; } else { - *width = gs->pitch; - *height = gs->height; + *width = gb->pitch; + *height = gb->height; } } @@ -3129,6 +3149,7 @@ gl_renderer_surface_copy_content(struct weston_surface *surface, const GLenum gl_format = GL_RGBA; /* PIXMAN_a8b8g8r8 little-endian */ struct gl_renderer *gr = get_renderer(surface->compositor); struct gl_surface_state *gs = get_surface_state(surface); + struct gl_buffer_state *gb = &gs->buffer; int cw, ch; GLuint fbo; GLuint tex; @@ -3137,11 +3158,11 @@ gl_renderer_surface_copy_content(struct weston_surface *surface, gl_renderer_surface_get_content_size(surface, &cw, &ch); - switch (gs->buffer_type) { + switch (gb->buffer_type) { case BUFFER_TYPE_NULL: return -1; case BUFFER_TYPE_SOLID: - *(uint32_t *)target = pack_color(format, gs->color); + *(uint32_t *)target = pack_color(format, gb->color); return 0; case BUFFER_TYPE_SHM: gl_renderer_flush_damage(surface, gs->buffer_ref.buffer); @@ -3172,7 +3193,7 @@ gl_renderer_surface_copy_content(struct weston_surface *surface, glViewport(0, 0, cw, ch); glDisable(GL_BLEND); - if (gs->y_inverted) + if (gb->y_inverted) ARRAY_COPY(sconf.projection.d, projmat_normal); else ARRAY_COPY(sconf.projection.d, projmat_yinvert); @@ -3210,6 +3231,7 @@ out: static void surface_state_destroy(struct gl_surface_state *gs, struct gl_renderer *gr) { + struct gl_buffer_state *gb = &gs->buffer; int i; wl_list_remove(&gs->surface_destroy_listener.link); @@ -3219,13 +3241,13 @@ surface_state_destroy(struct gl_surface_state *gs, struct gl_renderer *gr) glDeleteTextures(gs->num_textures, gs->textures); - for (i = 0; i < gs->num_images; i++) - egl_image_unref(gs->images[i]); + for (i = 0; i < gb->num_images; i++) + egl_image_unref(gb->images[i]); weston_buffer_reference(&gs->buffer_ref, NULL, BUFFER_WILL_NOT_BE_ACCESSED); weston_buffer_release_reference(&gs->buffer_release_ref, NULL); - pixman_region32_fini(&gs->texture_damage); + pixman_region32_fini(&gb->texture_damage); free(gs); } @@ -3261,23 +3283,25 @@ static int gl_renderer_create_surface(struct weston_surface *surface) { struct gl_surface_state *gs; + struct gl_buffer_state *gb; struct gl_renderer *gr = get_renderer(surface->compositor); gs = zalloc(sizeof *gs); if (gs == NULL) return -1; + gb = &gs->buffer; /* A buffer is never attached to solid color surfaces, yet * they still go through texcoord computations. Do not divide * by zero there. */ - gs->pitch = 1; - gs->y_inverted = true; - gs->direct_display = false; + gb->pitch = 1; + gb->y_inverted = true; + gb->direct_display = false; gs->surface = surface; - pixman_region32_init(&gs->texture_damage); + pixman_region32_init(&gb->texture_damage); surface->renderer_state = gs; gs->surface_destroy_listener.notify = From 8f5674359077456c9cbacc39b5d660b2c37bfd48 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 19 Jan 2022 01:30:12 +0000 Subject: [PATCH 234/609] gl-renderer: Change surface_set_color to attach_solid No functional change. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 77ff41ee..edc9ec76 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -2990,21 +2990,25 @@ out: } static void -gl_renderer_surface_set_color(struct weston_surface *surface, - float red, float green, float blue, float alpha) +gl_renderer_attach_solid(struct weston_surface *surface, + struct weston_buffer *buffer) { struct gl_surface_state *gs = get_surface_state(surface); struct gl_buffer_state *gb = &gs->buffer; - gb->color[0] = red; - gb->color[1] = green; - gb->color[2] = blue; - gb->color[3] = alpha; + gb->color[0] = buffer->solid.r; + gb->color[1] = buffer->solid.g; + gb->color[2] = buffer->solid.b; + gb->color[3] = buffer->solid.a; gb->buffer_type = BUFFER_TYPE_SOLID; gb->pitch = 1; gb->height = 1; gb->shader_variant = SHADER_VARIANT_SOLID; + + weston_buffer_reference(&gs->buffer_ref, NULL, + BUFFER_WILL_NOT_BE_ACCESSED); + weston_buffer_release_reference(&gs->buffer_release_ref, NULL); } static void @@ -3055,14 +3059,7 @@ gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) gl_renderer_attach_egl(es, buffer, format); return; case WESTON_BUFFER_SOLID: - gl_renderer_surface_set_color(es, - buffer->solid.r, - buffer->solid.g, - buffer->solid.b, - buffer->solid.a); - weston_buffer_reference(&gs->buffer_ref, NULL, - BUFFER_WILL_NOT_BE_ACCESSED); - weston_buffer_release_reference(&gs->buffer_release_ref, NULL); + gl_renderer_attach_solid(es, buffer); return; default: break; From 45194614398fa6cc01c085c2db894cc4dc2aeb38 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 19 Jan 2022 02:22:06 +0000 Subject: [PATCH 235/609] gl-renderer: Move EGL buffer error checks into attach_egl Makes it more consistent with the others, and also easier to return success/fail. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index edc9ec76..7ea176a8 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -2244,14 +2244,14 @@ gl_renderer_fill_buffer_info(struct weston_compositor *ec, } static void -gl_renderer_attach_egl(struct weston_surface *es, struct weston_buffer *buffer, - uint32_t format) +gl_renderer_attach_egl(struct weston_surface *es, struct weston_buffer *buffer) { struct weston_compositor *ec = es->compositor; struct gl_renderer *gr = get_renderer(ec); struct gl_surface_state *gs = get_surface_state(es); struct gl_buffer_state *gb = &gs->buffer; EGLint attribs[5]; + EGLint format; GLenum target; int i, num_planes; @@ -2260,6 +2260,15 @@ gl_renderer_attach_egl(struct weston_surface *es, struct weston_buffer *buffer, gb->images[i] = NULL; } es->is_opaque = false; + + if (!gr->has_bind_display || + !gr->query_buffer(gr->egl_display, buffer->legacy_buffer, + EGL_TEXTURE_FORMAT, &format)) { + weston_log("eglQueryWaylandBufferWL failed\n"); + gl_renderer_print_egl_error_state(); + return; + } + switch (format) { case EGL_TEXTURE_RGB: es->is_opaque = true; @@ -3014,11 +3023,8 @@ gl_renderer_attach_solid(struct weston_surface *surface, static void gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) { - struct weston_compositor *ec = es->compositor; - struct gl_renderer *gr = get_renderer(ec); struct gl_surface_state *gs = get_surface_state(es); struct gl_buffer_state *gb = &gs->buffer; - EGLint format; int i; weston_buffer_reference(&gs->buffer_ref, buffer, @@ -3050,13 +3056,7 @@ gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) gl_renderer_attach_dmabuf(es, buffer, buffer->dmabuf); return; case WESTON_BUFFER_RENDERER_OPAQUE: - if (!gr->has_bind_display || - !gr->query_buffer(gr->egl_display, - buffer->legacy_buffer, - EGL_TEXTURE_FORMAT, &format)) { - break; - } - gl_renderer_attach_egl(es, buffer, format); + gl_renderer_attach_egl(es, buffer); return; case WESTON_BUFFER_SOLID: gl_renderer_attach_solid(es, buffer); @@ -3066,10 +3066,6 @@ gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) } weston_log("unhandled buffer type!\n"); - if (gr->has_bind_display) { - weston_log("eglQueryWaylandBufferWL failed\n"); - gl_renderer_print_egl_error_state(); - } weston_buffer_reference(&gs->buffer_ref, NULL, BUFFER_WILL_NOT_BE_ACCESSED); weston_buffer_release_reference(&gs->buffer_release_ref, NULL); From bb624754f10e5c03c544c8f606a4f9812c08bcb2 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 19 Jan 2022 02:24:39 +0000 Subject: [PATCH 236/609] gl-renderer: Don't leak EGLImages on import fail This only happens for the legacy renderer, but still, might as well clean up after ourselves when we can't import a secondary plane. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 7ea176a8..903c9d3c 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -2315,7 +2315,9 @@ gl_renderer_attach_egl(struct weston_surface *es, struct weston_buffer *buffer) attribs); if (!gb->images[i]) { weston_log("failed to create img for plane %d\n", i); - continue; + while (--i >= 0) + egl_image_unref(gb->images[i]); + return; } glActiveTexture(GL_TEXTURE0 + i); From 428ae215e85c5fe5b223bbfd8dc154f6cd3b501e Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 19 Jan 2022 02:27:15 +0000 Subject: [PATCH 237/609] gl-renderer: Add return value to attach handlers It's good to know if we succeeded or failed to import our buffers. This will also later make for a more smooth transition when we start returning a gl_buffer_state from them. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 44 ++++++++++++++++++----------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 903c9d3c..7fc3ee79 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -1980,7 +1980,7 @@ ensure_textures(struct gl_surface_state *gs, GLenum target, int num_textures) glBindTexture(target, 0); } -static void +static bool gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer, struct wl_shm_buffer *shm_buffer) { @@ -2152,7 +2152,7 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer, unsupported: weston_log("warning: unknown or unsupported shm buffer format: %08x\n", wl_shm_buffer_get_format(shm_buffer)); - return; + return false; } /* Only allocate a texture if it doesn't match existing one. @@ -2180,6 +2180,8 @@ unsupported: ensure_textures(gs, GL_TEXTURE_2D, num_planes); } + + return true; } static bool @@ -2243,7 +2245,7 @@ gl_renderer_fill_buffer_info(struct weston_compositor *ec, return true; } -static void +static bool gl_renderer_attach_egl(struct weston_surface *es, struct weston_buffer *buffer) { struct weston_compositor *ec = es->compositor; @@ -2266,7 +2268,7 @@ gl_renderer_attach_egl(struct weston_surface *es, struct weston_buffer *buffer) EGL_TEXTURE_FORMAT, &format)) { weston_log("eglQueryWaylandBufferWL failed\n"); gl_renderer_print_egl_error_state(); - return; + return false; } switch (format) { @@ -2317,7 +2319,7 @@ gl_renderer_attach_egl(struct weston_surface *es, struct weston_buffer *buffer) weston_log("failed to create img for plane %d\n", i); while (--i >= 0) egl_image_unref(gb->images[i]); - return; + return false; } glActiveTexture(GL_TEXTURE0 + i); @@ -2329,6 +2331,8 @@ gl_renderer_attach_egl(struct weston_surface *es, struct weston_buffer *buffer) gb->height = buffer->height; gb->buffer_type = BUFFER_TYPE_EGL; gb->y_inverted = (buffer->buffer_origin == ORIGIN_TOP_LEFT); + + return true; } static void @@ -2889,7 +2893,7 @@ gl_renderer_import_dmabuf(struct weston_compositor *ec, return true; } -static void +static bool gl_renderer_attach_dmabuf(struct weston_surface *surface, struct weston_buffer *buffer, struct linux_dmabuf_buffer *dmabuf) @@ -2913,7 +2917,7 @@ gl_renderer_attach_dmabuf(struct weston_surface *surface, surface->is_opaque = pixel_format_is_opaque(buffer->pixel_format); if (dmabuf->direct_display) - return; + return true; image = linux_dmabuf_buffer_get_user_data(dmabuf); @@ -2933,6 +2937,8 @@ gl_renderer_attach_dmabuf(struct weston_surface *surface, } gb->shader_variant = image->shader_variant; + + return true; } static const struct weston_drm_format_array * @@ -3000,7 +3006,7 @@ out: return ret; } -static void +static bool gl_renderer_attach_solid(struct weston_surface *surface, struct weston_buffer *buffer) { @@ -3020,6 +3026,8 @@ gl_renderer_attach_solid(struct weston_surface *surface, weston_buffer_reference(&gs->buffer_ref, NULL, BUFFER_WILL_NOT_BE_ACCESSED); weston_buffer_release_reference(&gs->buffer_release_ref, NULL); + + return true; } static void @@ -3027,6 +3035,7 @@ gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) { struct gl_surface_state *gs = get_surface_state(es); struct gl_buffer_state *gb = &gs->buffer; + bool ret = false; int i; weston_buffer_reference(&gs->buffer_ref, buffer, @@ -3052,21 +3061,24 @@ gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) switch (buffer->type) { case WESTON_BUFFER_SHM: - gl_renderer_attach_shm(es, buffer, buffer->shm_buffer); - return; + ret = gl_renderer_attach_shm(es, buffer, buffer->shm_buffer); + break; case WESTON_BUFFER_DMABUF: - gl_renderer_attach_dmabuf(es, buffer, buffer->dmabuf); - return; + ret = gl_renderer_attach_dmabuf(es, buffer, buffer->dmabuf); + break; case WESTON_BUFFER_RENDERER_OPAQUE: - gl_renderer_attach_egl(es, buffer); - return; + ret = gl_renderer_attach_egl(es, buffer); + break; case WESTON_BUFFER_SOLID: - gl_renderer_attach_solid(es, buffer); - return; + ret = gl_renderer_attach_solid(es, buffer); + break; default: break; } + if (ret) + return; + weston_log("unhandled buffer type!\n"); weston_buffer_reference(&gs->buffer_ref, NULL, BUFFER_WILL_NOT_BE_ACCESSED); From 0cdf576c923e7df2bd4df3d6e13b644637511bf7 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 19 Jan 2022 02:30:11 +0000 Subject: [PATCH 238/609] gl-renderer: Convert attach to flat-return style Deduplicate the no-buffer and the import-fail case, and try to fall through where we can. This will make it easier to shift the buffer reference change later, so the attach subhandlers can reference the old buffer when checking for compatibility. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 32 ++++++++++++++--------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 7fc3ee79..6415a2da 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -3044,20 +3044,8 @@ gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) weston_buffer_release_reference(&gs->buffer_release_ref, es->buffer_release_ref.buffer_release); - if (!buffer) { - for (i = 0; i < gb->num_images; i++) { - egl_image_unref(gb->images[i]); - gb->images[i] = NULL; - } - gb->num_images = 0; - glDeleteTextures(gs->num_textures, gs->textures); - gs->num_textures = 0; - gb->buffer_type = BUFFER_TYPE_NULL; - gb->y_inverted = true; - gb->direct_display = false; - es->is_opaque = false; - return; - } + if (!buffer) + goto out; switch (buffer->type) { case WESTON_BUFFER_SHM: @@ -3079,15 +3067,25 @@ gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) if (ret) return; - weston_log("unhandled buffer type!\n"); weston_buffer_reference(&gs->buffer_ref, NULL, BUFFER_WILL_NOT_BE_ACCESSED); weston_buffer_release_reference(&gs->buffer_release_ref, NULL); + weston_log("unhandled buffer type!\n"); + weston_buffer_send_server_error(buffer, + "disconnecting due to unhandled buffer type"); + +out: + for (i = 0; i < gb->num_images; i++) { + egl_image_unref(gb->images[i]); + gb->images[i] = NULL; + } + gb->num_images = 0; + glDeleteTextures(gs->num_textures, gs->textures); + gs->num_textures = 0; gb->buffer_type = BUFFER_TYPE_NULL; gb->y_inverted = true; + gb->direct_display = false; es->is_opaque = false; - weston_buffer_send_server_error(buffer, - "disconnecting due to unhandled buffer type"); } static void From 77e1a042205eb05b339d6580eae3575135df1425 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 19 Jan 2022 02:38:16 +0000 Subject: [PATCH 239/609] gl-renderer: Remove extraneous parameters from attach The handlers can chase the details of the buffers themselves. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 6415a2da..dca0b9f2 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -1981,13 +1981,13 @@ ensure_textures(struct gl_surface_state *gs, GLenum target, int num_textures) } static bool -gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer, - struct wl_shm_buffer *shm_buffer) +gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) { struct weston_compositor *ec = es->compositor; struct gl_renderer *gr = get_renderer(ec); struct gl_surface_state *gs = get_surface_state(es); struct gl_buffer_state *gb = &gs->buffer; + struct wl_shm_buffer *shm_buffer = buffer->shm_buffer; GLenum gl_format[3] = {0, 0, 0}; GLenum gl_pixel_type; int pitch; @@ -2895,12 +2895,12 @@ gl_renderer_import_dmabuf(struct weston_compositor *ec, static bool gl_renderer_attach_dmabuf(struct weston_surface *surface, - struct weston_buffer *buffer, - struct linux_dmabuf_buffer *dmabuf) + struct weston_buffer *buffer) { struct gl_renderer *gr = get_renderer(surface->compositor); struct gl_surface_state *gs = get_surface_state(surface); struct gl_buffer_state *gb = &gs->buffer; + struct linux_dmabuf_buffer *dmabuf = buffer->dmabuf; struct dmabuf_image *image; GLenum target; int i; @@ -3049,10 +3049,10 @@ gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) switch (buffer->type) { case WESTON_BUFFER_SHM: - ret = gl_renderer_attach_shm(es, buffer, buffer->shm_buffer); + ret = gl_renderer_attach_shm(es, buffer); break; case WESTON_BUFFER_DMABUF: - ret = gl_renderer_attach_dmabuf(es, buffer, buffer->dmabuf); + ret = gl_renderer_attach_dmabuf(es, buffer); break; case WESTON_BUFFER_RENDERER_OPAQUE: ret = gl_renderer_attach_egl(es, buffer); From 1a65c1b8b1556e2ff915bb2acef475e83e050606 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 19 Jan 2022 02:44:44 +0000 Subject: [PATCH 240/609] gl-renderer: Shift buffer reference later in attach Allow the various attach handlers to access the existing buffer, only referencing the new buffer when they have successfully attached. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index dca0b9f2..d989c112 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -3023,10 +3023,6 @@ gl_renderer_attach_solid(struct weston_surface *surface, gb->shader_variant = SHADER_VARIANT_SOLID; - weston_buffer_reference(&gs->buffer_ref, NULL, - BUFFER_WILL_NOT_BE_ACCESSED); - weston_buffer_release_reference(&gs->buffer_release_ref, NULL); - return true; } @@ -3038,12 +3034,6 @@ gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) bool ret = false; int i; - weston_buffer_reference(&gs->buffer_ref, buffer, - buffer ? BUFFER_MAY_BE_ACCESSED : - BUFFER_WILL_NOT_BE_ACCESSED); - weston_buffer_release_reference(&gs->buffer_release_ref, - es->buffer_release_ref.buffer_release); - if (!buffer) goto out; @@ -3064,17 +3054,24 @@ gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) break; } - if (ret) - return; + if (!ret) { + weston_log("unhandled buffer type!\n"); + weston_buffer_send_server_error(buffer, + "disconnecting due to unhandled buffer type"); + goto out; + } + + weston_buffer_reference(&gs->buffer_ref, buffer, + BUFFER_MAY_BE_ACCESSED); + weston_buffer_release_reference(&gs->buffer_release_ref, + es->buffer_release_ref.buffer_release); + return; +out: weston_buffer_reference(&gs->buffer_ref, NULL, BUFFER_WILL_NOT_BE_ACCESSED); weston_buffer_release_reference(&gs->buffer_release_ref, NULL); - weston_log("unhandled buffer type!\n"); - weston_buffer_send_server_error(buffer, - "disconnecting due to unhandled buffer type"); -out: for (i = 0; i < gb->num_images; i++) { egl_image_unref(gb->images[i]); gb->images[i] = NULL; From 70874428d673b5ad93055f9d7f75afbb9ebce8df Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 19 Jan 2022 02:47:05 +0000 Subject: [PATCH 241/609] gl-renderer: Make attach_shm return early on cache hit If we can reuse the textures we already have, just return early, rather than putting all the work in a large indented body. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 44 +++++++++++++++-------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index d989c112..fa6dc2f9 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -2158,29 +2158,31 @@ unsupported: /* Only allocate a texture if it doesn't match existing one. * If a switch from DRM allocated buffer to a SHM buffer is * happening, we need to allocate a new texture buffer. */ - if (pitch != gb->pitch || - buffer->height != gb->height || - gl_format[0] != gb->gl_format[0] || - gl_format[1] != gb->gl_format[1] || - gl_format[2] != gb->gl_format[2] || - gl_pixel_type != gb->gl_pixel_type || - gb->buffer_type != BUFFER_TYPE_SHM) { - gb->pitch = pitch; - gb->height = buffer->height; - gb->gl_format[0] = gl_format[0]; - gb->gl_format[1] = gl_format[1]; - gb->gl_format[2] = gl_format[2]; - gb->gl_pixel_type = gl_pixel_type; - gb->buffer_type = BUFFER_TYPE_SHM; - gb->needs_full_upload = true; - gb->y_inverted = true; - gb->direct_display = false; - - gs->surface = es; - - ensure_textures(gs, GL_TEXTURE_2D, num_planes); + if (pitch == gb->pitch && + buffer->height == gb->height && + gl_format[0] == gb->gl_format[0] && + gl_format[1] == gb->gl_format[1] && + gl_format[2] == gb->gl_format[2] && + gl_pixel_type == gb->gl_pixel_type && + gb->buffer_type == BUFFER_TYPE_SHM) { + return true; } + gb->pitch = pitch; + gb->height = buffer->height; + gb->gl_format[0] = gl_format[0]; + gb->gl_format[1] = gl_format[1]; + gb->gl_format[2] = gl_format[2]; + gb->gl_pixel_type = gl_pixel_type; + gb->buffer_type = BUFFER_TYPE_SHM; + gb->needs_full_upload = true; + gb->y_inverted = true; + gb->direct_display = false; + + gs->surface = es; + + ensure_textures(gs, GL_TEXTURE_2D, num_planes); + return true; } From c6af9c8581a99c1638e3f59713b9b4dce225edba Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 19 Jan 2022 02:54:06 +0000 Subject: [PATCH 242/609] gl-renderer: Remove gl_buffer_state.height Low-hanging fruit: just get this from the weston_buffer. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index fa6dc2f9..d37f7acd 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -186,7 +186,6 @@ struct gl_buffer_state { enum buffer_type buffer_type; int pitch; /* in pixels */ - int height; /* in pixels */ bool y_inverted; bool direct_display; @@ -577,6 +576,7 @@ texture_region(struct weston_view *ev, { struct gl_surface_state *gs = get_surface_state(ev->surface); struct gl_buffer_state *gb = &gs->buffer; + struct weston_buffer *buffer = gs->buffer_ref.buffer; struct weston_compositor *ec = ev->surface->compositor; struct gl_renderer *gr = get_renderer(ec); GLfloat *v, inv_width, inv_height; @@ -603,7 +603,7 @@ texture_region(struct weston_view *ev, vtxcnt = wl_array_add(&gr->vtxcnt, nrects * nsurf * sizeof *vtxcnt); inv_width = 1.0 / gb->pitch; - inv_height = 1.0 / gb->height; + inv_height = 1.0 / buffer->height; for (i = 0; i < nrects; i++) { pixman_box32_t *rect = &rects[i]; @@ -646,7 +646,7 @@ texture_region(struct weston_view *ev, if (gb->y_inverted) { *(v++) = by * inv_height; } else { - *(v++) = (gb->height - by) * inv_height; + *(v++) = (buffer->height - by) * inv_height; } } @@ -1988,6 +1988,7 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) struct gl_surface_state *gs = get_surface_state(es); struct gl_buffer_state *gb = &gs->buffer; struct wl_shm_buffer *shm_buffer = buffer->shm_buffer; + struct weston_buffer *old_buffer = gs->buffer_ref.buffer; GLenum gl_format[3] = {0, 0, 0}; GLenum gl_pixel_type; int pitch; @@ -2158,8 +2159,9 @@ unsupported: /* Only allocate a texture if it doesn't match existing one. * If a switch from DRM allocated buffer to a SHM buffer is * happening, we need to allocate a new texture buffer. */ - if (pitch == gb->pitch && - buffer->height == gb->height && + if (old_buffer && + pitch == gb->pitch && + buffer->height == old_buffer->height && gl_format[0] == gb->gl_format[0] && gl_format[1] == gb->gl_format[1] && gl_format[2] == gb->gl_format[2] && @@ -2169,7 +2171,6 @@ unsupported: } gb->pitch = pitch; - gb->height = buffer->height; gb->gl_format[0] = gl_format[0]; gb->gl_format[1] = gl_format[1]; gb->gl_format[2] = gl_format[2]; @@ -2330,7 +2331,6 @@ gl_renderer_attach_egl(struct weston_surface *es, struct weston_buffer *buffer) } gb->pitch = buffer->width; - gb->height = buffer->height; gb->buffer_type = BUFFER_TYPE_EGL; gb->y_inverted = (buffer->buffer_origin == ORIGIN_TOP_LEFT); @@ -2912,7 +2912,6 @@ gl_renderer_attach_dmabuf(struct weston_surface *surface, gb->num_images = 0; gb->pitch = buffer->width; - gb->height = buffer->height; gb->buffer_type = BUFFER_TYPE_EGL; gb->y_inverted = (buffer->buffer_origin == ORIGIN_TOP_LEFT); gb->direct_display = dmabuf->direct_display; @@ -3021,7 +3020,6 @@ gl_renderer_attach_solid(struct weston_surface *surface, gb->color[3] = buffer->solid.a; gb->buffer_type = BUFFER_TYPE_SOLID; gb->pitch = 1; - gb->height = 1; gb->shader_variant = SHADER_VARIANT_SOLID; @@ -3099,7 +3097,7 @@ gl_renderer_surface_get_content_size(struct weston_surface *surface, *height = 0; } else { *width = gb->pitch; - *height = gb->height; + *height = gs->buffer_ref.buffer->height; } } From 90dbf4522d8d37ebbec40cc57d5bbd812659a6ed Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 19 Jan 2022 02:57:40 +0000 Subject: [PATCH 243/609] gl-renderer: Remove gl_buffer_state.y_inverted It's just a shadow of weston_buffer.buffer_origin, which also has a slightly more descriptive name. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index d37f7acd..ef67e059 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -186,7 +186,6 @@ struct gl_buffer_state { enum buffer_type buffer_type; int pitch; /* in pixels */ - bool y_inverted; bool direct_display; /* Extension needed for SHM YUV texture */ @@ -643,7 +642,7 @@ texture_region(struct weston_view *ev, sx, sy, &bx, &by); *(v++) = bx * inv_width; - if (gb->y_inverted) { + if (buffer->buffer_origin == ORIGIN_TOP_LEFT) { *(v++) = by * inv_height; } else { *(v++) = (buffer->height - by) * inv_height; @@ -2177,7 +2176,6 @@ unsupported: gb->gl_pixel_type = gl_pixel_type; gb->buffer_type = BUFFER_TYPE_SHM; gb->needs_full_upload = true; - gb->y_inverted = true; gb->direct_display = false; gs->surface = es; @@ -2332,7 +2330,6 @@ gl_renderer_attach_egl(struct weston_surface *es, struct weston_buffer *buffer) gb->pitch = buffer->width; gb->buffer_type = BUFFER_TYPE_EGL; - gb->y_inverted = (buffer->buffer_origin == ORIGIN_TOP_LEFT); return true; } @@ -2913,7 +2910,6 @@ gl_renderer_attach_dmabuf(struct weston_surface *surface, gb->pitch = buffer->width; gb->buffer_type = BUFFER_TYPE_EGL; - gb->y_inverted = (buffer->buffer_origin == ORIGIN_TOP_LEFT); gb->direct_display = dmabuf->direct_display; surface->is_opaque = pixel_format_is_opaque(buffer->pixel_format); @@ -3080,7 +3076,6 @@ out: glDeleteTextures(gs->num_textures, gs->textures); gs->num_textures = 0; gb->buffer_type = BUFFER_TYPE_NULL; - gb->y_inverted = true; gb->direct_display = false; es->is_opaque = false; } @@ -3152,6 +3147,7 @@ gl_renderer_surface_copy_content(struct weston_surface *surface, struct gl_renderer *gr = get_renderer(surface->compositor); struct gl_surface_state *gs = get_surface_state(surface); struct gl_buffer_state *gb = &gs->buffer; + struct weston_buffer *buffer = gs->buffer_ref.buffer; int cw, ch; GLuint fbo; GLuint tex; @@ -3167,7 +3163,7 @@ gl_renderer_surface_copy_content(struct weston_surface *surface, *(uint32_t *)target = pack_color(format, gb->color); return 0; case BUFFER_TYPE_SHM: - gl_renderer_flush_damage(surface, gs->buffer_ref.buffer); + gl_renderer_flush_damage(surface, buffer); /* fall through */ case BUFFER_TYPE_EGL: break; @@ -3195,7 +3191,7 @@ gl_renderer_surface_copy_content(struct weston_surface *surface, glViewport(0, 0, cw, ch); glDisable(GL_BLEND); - if (gb->y_inverted) + if (buffer->buffer_origin == ORIGIN_TOP_LEFT) ARRAY_COPY(sconf.projection.d, projmat_normal); else ARRAY_COPY(sconf.projection.d, projmat_yinvert); @@ -3298,7 +3294,6 @@ gl_renderer_create_surface(struct weston_surface *surface) * by zero there. */ gb->pitch = 1; - gb->y_inverted = true; gb->direct_display = false; gs->surface = surface; From 907c9d1ffda894af460e92edbd96fd5371d20304 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 19 Jan 2022 03:00:10 +0000 Subject: [PATCH 244/609] gl-renderer: Clarify comment Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index ef67e059..3d67b420 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -174,9 +174,7 @@ struct gl_buffer_state { bool needs_full_upload; pixman_region32_t texture_damage; - /* These are only used by SHM surfaces to detect when we need - * to do a full upload to specify a new internal texture - * format */ + /* Only needed between attach() and flush_damage() */ GLenum gl_format[3]; GLenum gl_pixel_type; From 5fdb5fdd907f70177c9426880687b12246551a47 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 19 Jan 2022 03:02:47 +0000 Subject: [PATCH 245/609] gl-renderer: Don't match texture width to input pitch This was only used for what was presumably an attempt at an optimisation, to force the texture's pitch in pixels to match the SHM buffer. This is really unlikely to have ever made a difference, given the alignments GPUs demand. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 3d67b420..1a25ec91 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -175,6 +175,7 @@ struct gl_buffer_state { pixman_region32_t texture_damage; /* Only needed between attach() and flush_damage() */ + int pitch; GLenum gl_format[3]; GLenum gl_pixel_type; @@ -183,7 +184,6 @@ struct gl_buffer_state { enum gl_shader_texture_variant shader_variant; enum buffer_type buffer_type; - int pitch; /* in pixels */ bool direct_display; /* Extension needed for SHM YUV texture */ @@ -572,7 +572,6 @@ texture_region(struct weston_view *ev, pixman_region32_t *surf_region) { struct gl_surface_state *gs = get_surface_state(ev->surface); - struct gl_buffer_state *gb = &gs->buffer; struct weston_buffer *buffer = gs->buffer_ref.buffer; struct weston_compositor *ec = ev->surface->compositor; struct gl_renderer *gr = get_renderer(ec); @@ -599,7 +598,7 @@ texture_region(struct weston_view *ev, v = wl_array_add(&gr->vertices, nrects * nsurf * 8 * 4 * sizeof *v); vtxcnt = wl_array_add(&gr->vtxcnt, nrects * nsurf * sizeof *vtxcnt); - inv_width = 1.0 / gb->pitch; + inv_width = 1.0 / buffer->width; inv_height = 1.0 / buffer->height; for (i = 0; i < nrects; i++) { @@ -1905,7 +1904,7 @@ gl_renderer_flush_damage(struct weston_surface *surface, gb->pitch / gb->hsub[j]); glTexImage2D(GL_TEXTURE_2D, 0, gb->gl_format[j], - gb->pitch / gb->hsub[j], + buffer->width / gb->hsub[j], buffer->height / gb->vsub[j], 0, gl_format_from_internal(gb->gl_format[j]), @@ -2153,11 +2152,13 @@ unsupported: return false; } + gb->pitch = pitch; + /* Only allocate a texture if it doesn't match existing one. * If a switch from DRM allocated buffer to a SHM buffer is * happening, we need to allocate a new texture buffer. */ if (old_buffer && - pitch == gb->pitch && + buffer->width == old_buffer->width && buffer->height == old_buffer->height && gl_format[0] == gb->gl_format[0] && gl_format[1] == gb->gl_format[1] && @@ -2167,7 +2168,6 @@ unsupported: return true; } - gb->pitch = pitch; gb->gl_format[0] = gl_format[0]; gb->gl_format[1] = gl_format[1]; gb->gl_format[2] = gl_format[2]; @@ -2326,7 +2326,6 @@ gl_renderer_attach_egl(struct weston_surface *es, struct weston_buffer *buffer) gr->image_target_texture_2d(target, gb->images[i]->image); } - gb->pitch = buffer->width; gb->buffer_type = BUFFER_TYPE_EGL; return true; @@ -2906,7 +2905,6 @@ gl_renderer_attach_dmabuf(struct weston_surface *surface, egl_image_unref(gb->images[i]); gb->num_images = 0; - gb->pitch = buffer->width; gb->buffer_type = BUFFER_TYPE_EGL; gb->direct_display = dmabuf->direct_display; surface->is_opaque = pixel_format_is_opaque(buffer->pixel_format); @@ -3013,7 +3011,6 @@ gl_renderer_attach_solid(struct weston_surface *surface, gb->color[2] = buffer->solid.b; gb->color[3] = buffer->solid.a; gb->buffer_type = BUFFER_TYPE_SOLID; - gb->pitch = 1; gb->shader_variant = SHADER_VARIANT_SOLID; @@ -3089,7 +3086,7 @@ gl_renderer_surface_get_content_size(struct weston_surface *surface, *width = 0; *height = 0; } else { - *width = gb->pitch; + *width = gs->buffer_ref.buffer->width; *height = gs->buffer_ref.buffer->height; } } @@ -3291,7 +3288,6 @@ gl_renderer_create_surface(struct weston_surface *surface) * they still go through texcoord computations. Do not divide * by zero there. */ - gb->pitch = 1; gb->direct_display = false; gs->surface = surface; From 21c65d7c9bafb5d2535fe60bb537054bbaf3a1ab Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 19 Jan 2022 03:06:00 +0000 Subject: [PATCH 246/609] gl-renderer: Remove gl_buffer_state.buffer_type We can just get this from the weston_buffer. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 33 +++++++++-------------------- 1 file changed, 10 insertions(+), 23 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 1a25ec91..68acab0f 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -106,13 +106,6 @@ struct gl_output_state { struct gl_fbo_texture shadow; }; -enum buffer_type { - BUFFER_TYPE_NULL, - BUFFER_TYPE_SOLID, /* internal solid color surfaces without a buffer */ - BUFFER_TYPE_SHM, - BUFFER_TYPE_EGL -}; - struct gl_renderer; struct egl_image { @@ -183,7 +176,6 @@ struct gl_buffer_state { int num_images; enum gl_shader_texture_variant shader_variant; - enum buffer_type buffer_type; bool direct_display; /* Extension needed for SHM YUV texture */ @@ -2164,7 +2156,7 @@ unsupported: gl_format[1] == gb->gl_format[1] && gl_format[2] == gb->gl_format[2] && gl_pixel_type == gb->gl_pixel_type && - gb->buffer_type == BUFFER_TYPE_SHM) { + old_buffer->type == WESTON_BUFFER_SHM) { return true; } @@ -2172,7 +2164,6 @@ unsupported: gb->gl_format[1] = gl_format[1]; gb->gl_format[2] = gl_format[2]; gb->gl_pixel_type = gl_pixel_type; - gb->buffer_type = BUFFER_TYPE_SHM; gb->needs_full_upload = true; gb->direct_display = false; @@ -2326,8 +2317,6 @@ gl_renderer_attach_egl(struct weston_surface *es, struct weston_buffer *buffer) gr->image_target_texture_2d(target, gb->images[i]->image); } - gb->buffer_type = BUFFER_TYPE_EGL; - return true; } @@ -2905,7 +2894,6 @@ gl_renderer_attach_dmabuf(struct weston_surface *surface, egl_image_unref(gb->images[i]); gb->num_images = 0; - gb->buffer_type = BUFFER_TYPE_EGL; gb->direct_display = dmabuf->direct_display; surface->is_opaque = pixel_format_is_opaque(buffer->pixel_format); @@ -3010,7 +2998,6 @@ gl_renderer_attach_solid(struct weston_surface *surface, gb->color[1] = buffer->solid.g; gb->color[2] = buffer->solid.b; gb->color[3] = buffer->solid.a; - gb->buffer_type = BUFFER_TYPE_SOLID; gb->shader_variant = SHADER_VARIANT_SOLID; @@ -3070,7 +3057,6 @@ out: gb->num_images = 0; glDeleteTextures(gs->num_textures, gs->textures); gs->num_textures = 0; - gb->buffer_type = BUFFER_TYPE_NULL; gb->direct_display = false; es->is_opaque = false; } @@ -3080,9 +3066,9 @@ gl_renderer_surface_get_content_size(struct weston_surface *surface, int *width, int *height) { struct gl_surface_state *gs = get_surface_state(surface); - struct gl_buffer_state *gb = &gs->buffer; + struct weston_buffer *buffer = gs->buffer_ref.buffer; - if (gb->buffer_type == BUFFER_TYPE_NULL) { + if (!buffer) { *width = 0; *height = 0; } else { @@ -3149,18 +3135,19 @@ gl_renderer_surface_copy_content(struct weston_surface *surface, GLenum status; int ret = -1; + assert(buffer); + gl_renderer_surface_get_content_size(surface, &cw, &ch); - switch (gb->buffer_type) { - case BUFFER_TYPE_NULL: - return -1; - case BUFFER_TYPE_SOLID: + switch (buffer->type) { + case WESTON_BUFFER_SOLID: *(uint32_t *)target = pack_color(format, gb->color); return 0; - case BUFFER_TYPE_SHM: + case WESTON_BUFFER_SHM: gl_renderer_flush_damage(surface, buffer); /* fall through */ - case BUFFER_TYPE_EGL: + case WESTON_BUFFER_DMABUF: + case WESTON_BUFFER_RENDERER_OPAQUE: break; } From 193de3c2cffbae2ae908bcac92a510a815b8a10a Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 19 Jan 2022 03:08:02 +0000 Subject: [PATCH 247/609] renderer: Remove get_content_size hook Now that we can reliably access buffer dimensions from weston_buffer, and gl-renderer isn't doing strange things with buffer widths, just use that. The renderer interface is now unused and can be deleted. Signed-off-by: Daniel Stone --- libweston/compositor.c | 13 ++++++------- libweston/libweston-internal.h | 4 ---- libweston/pixman-renderer.c | 17 ----------------- libweston/renderer-gl/gl-renderer.c | 21 ++------------------- 4 files changed, 8 insertions(+), 47 deletions(-) diff --git a/libweston/compositor.c b/libweston/compositor.c index b45f7f05..c0b8f428 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -4580,8 +4580,7 @@ weston_surface_set_label_func(struct weston_surface *surface, * * Retrieves the raw surface content size in pixels for the given surface. * This is the whole content size in buffer pixels. If the surface - * has no content or the renderer does not implement this feature, - * zeroes are returned. + * has no content, zeroes are returned. * * This function is used to determine the buffer size needed for * a weston_surface_copy_content() call. @@ -4590,15 +4589,15 @@ WL_EXPORT void weston_surface_get_content_size(struct weston_surface *surface, int *width, int *height) { - struct weston_renderer *rer = surface->compositor->renderer; + struct weston_buffer *buffer = surface->buffer_ref.buffer; - if (!rer->surface_get_content_size) { + if (buffer) { + *width = buffer->width; + *height = buffer->height; + } else { *width = 0; *height = 0; - return; } - - rer->surface_get_content_size(surface, width, height); } /** Get the bounding box of a surface and its subsurfaces diff --git a/libweston/libweston-internal.h b/libweston/libweston-internal.h index e6a84942..6a189275 100644 --- a/libweston/libweston-internal.h +++ b/libweston/libweston-internal.h @@ -57,10 +57,6 @@ struct weston_renderer { void (*attach)(struct weston_surface *es, struct weston_buffer *buffer); void (*destroy)(struct weston_compositor *ec); - /** See weston_surface_get_content_size() */ - void (*surface_get_content_size)(struct weston_surface *surface, - int *width, int *height); - /** See weston_surface_copy_content() */ int (*surface_copy_content)(struct weston_surface *surface, void *target, size_t size, diff --git a/libweston/pixman-renderer.c b/libweston/pixman-renderer.c index 17e5d17f..64d6bd17 100644 --- a/libweston/pixman-renderer.c +++ b/libweston/pixman-renderer.c @@ -810,21 +810,6 @@ pixman_renderer_destroy(struct weston_compositor *ec) ec->renderer = NULL; } -static void -pixman_renderer_surface_get_content_size(struct weston_surface *surface, - int *width, int *height) -{ - struct pixman_surface_state *ps = get_surface_state(surface); - - if (ps->image) { - *width = pixman_image_get_width(ps->image); - *height = pixman_image_get_height(ps->image); - } else { - *width = 0; - *height = 0; - } -} - static int pixman_renderer_surface_copy_content(struct weston_surface *surface, void *target, size_t size, @@ -896,8 +881,6 @@ pixman_renderer_init(struct weston_compositor *ec) renderer->base.flush_damage = pixman_renderer_flush_damage; renderer->base.attach = pixman_renderer_attach; renderer->base.destroy = pixman_renderer_destroy; - renderer->base.surface_get_content_size = - pixman_renderer_surface_get_content_size; renderer->base.surface_copy_content = pixman_renderer_surface_copy_content; ec->renderer = &renderer->base; diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 68acab0f..0c784537 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -3061,22 +3061,6 @@ out: es->is_opaque = false; } -static void -gl_renderer_surface_get_content_size(struct weston_surface *surface, - int *width, int *height) -{ - struct gl_surface_state *gs = get_surface_state(surface); - struct weston_buffer *buffer = gs->buffer_ref.buffer; - - if (!buffer) { - *width = 0; - *height = 0; - } else { - *width = gs->buffer_ref.buffer->width; - *height = gs->buffer_ref.buffer->height; - } -} - static uint32_t pack_color(pixman_format_code_t format, float *c) { @@ -3137,7 +3121,8 @@ gl_renderer_surface_copy_content(struct weston_surface *surface, assert(buffer); - gl_renderer_surface_get_content_size(surface, &cw, &ch); + cw = buffer->width; + ch = buffer->height; switch (buffer->type) { case WESTON_BUFFER_SOLID: @@ -3745,8 +3730,6 @@ gl_renderer_display_create(struct weston_compositor *ec, gr->base.flush_damage = gl_renderer_flush_damage; gr->base.attach = gl_renderer_attach; gr->base.destroy = gl_renderer_destroy; - gr->base.surface_get_content_size = - gl_renderer_surface_get_content_size; gr->base.surface_copy_content = gl_renderer_surface_copy_content; gr->base.fill_buffer_info = gl_renderer_fill_buffer_info; From c9253c00129ea2e266299f3a71868649fc28a937 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 19 Jan 2022 03:11:40 +0000 Subject: [PATCH 248/609] renderer: Set surface->is_opaque in the core No need for the renderers to do this now that we know what all of the formats are. Signed-off-by: Daniel Stone --- libweston/compositor.c | 4 ++++ libweston/pixman-renderer.c | 2 -- libweston/renderer-gl/gl-renderer.c | 20 -------------------- 3 files changed, 4 insertions(+), 22 deletions(-) diff --git a/libweston/compositor.c b/libweston/compositor.c index c0b8f428..a9f043ba 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -2656,6 +2656,7 @@ weston_surface_attach_solid(struct weston_surface *surface, surface->is_opaque = true; pixman_region32_init_rect(&surface->opaque, 0, 0, w, h); } else { + surface->is_opaque = false; pixman_region32_init(&surface->opaque); } } @@ -2688,6 +2689,9 @@ weston_surface_attach(struct weston_surface *surface, weston_surface_calculate_size_from_buffer(surface); weston_presentation_feedback_discard_list(&surface->feedback_list); + + if (buffer) + surface->is_opaque = pixel_format_is_opaque(buffer->pixel_format); } /** weston_compositor_damage_all diff --git a/libweston/pixman-renderer.c b/libweston/pixman-renderer.c index 64d6bd17..c1ed560f 100644 --- a/libweston/pixman-renderer.c +++ b/libweston/pixman-renderer.c @@ -714,8 +714,6 @@ pixman_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) return; } - es->is_opaque = pixel_format_is_opaque(pixel_info); - ps->image = pixman_image_create_bits(pixel_info->pixman_format, buffer->width, buffer->height, wl_shm_buffer_get_data(shm_buffer), diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 0c784537..8abbce71 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -1994,21 +1994,18 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; gl_format[0] = GL_BGRA_EXT; gl_pixel_type = GL_UNSIGNED_BYTE; - es->is_opaque = true; break; case WL_SHM_FORMAT_ARGB8888: gb->shader_variant = SHADER_VARIANT_RGBA; pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; gl_format[0] = GL_BGRA_EXT; gl_pixel_type = GL_UNSIGNED_BYTE; - es->is_opaque = false; break; case WL_SHM_FORMAT_RGB565: gb->shader_variant = SHADER_VARIANT_RGBX; pitch = wl_shm_buffer_get_stride(shm_buffer) / 2; gl_format[0] = GL_RGB; gl_pixel_type = GL_UNSIGNED_SHORT_5_6_5; - es->is_opaque = true; break; #if __BYTE_ORDER == __LITTLE_ENDIAN case WL_SHM_FORMAT_ABGR2101010: @@ -2019,7 +2016,6 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; gl_format[0] = using_glesv2 ? GL_RGBA : GL_RGB10_A2; gl_pixel_type = GL_UNSIGNED_INT_2_10_10_10_REV_EXT; - es->is_opaque = false; break; case WL_SHM_FORMAT_XBGR2101010: if (!gr->has_texture_type_2_10_10_10_rev) { @@ -2029,7 +2025,6 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; gl_format[0] = using_glesv2 ? GL_RGBA : GL_RGB10_A2; gl_pixel_type = GL_UNSIGNED_INT_2_10_10_10_REV_EXT; - es->is_opaque = true; break; case WL_SHM_FORMAT_ABGR16161616F: if (!gr->gl_supports_color_transforms) @@ -2038,7 +2033,6 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) pitch = wl_shm_buffer_get_stride(shm_buffer) / 8; gl_format[0] = GL_RGBA16F; gl_pixel_type = GL_HALF_FLOAT; - es->is_opaque = false; break; case WL_SHM_FORMAT_XBGR16161616F: if (!gr->gl_supports_color_transforms) @@ -2047,7 +2041,6 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) pitch = wl_shm_buffer_get_stride(shm_buffer) / 8; gl_format[0] = GL_RGBA16F; gl_pixel_type = GL_HALF_FLOAT; - es->is_opaque = true; break; case WL_SHM_FORMAT_ABGR16161616: if (!gr->has_texture_norm16) @@ -2056,7 +2049,6 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) pitch = wl_shm_buffer_get_stride(shm_buffer) / 8; gl_format[0] = GL_RGBA16_EXT; gl_pixel_type = GL_UNSIGNED_SHORT; - es->is_opaque = false; break; case WL_SHM_FORMAT_XBGR16161616: if (!gr->has_texture_norm16) @@ -2065,7 +2057,6 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) pitch = wl_shm_buffer_get_stride(shm_buffer) / 8; gl_format[0] = GL_RGBA16_EXT; gl_pixel_type = GL_UNSIGNED_SHORT; - es->is_opaque = true; break; #endif case WL_SHM_FORMAT_YUV420: @@ -2090,7 +2081,6 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) gl_format[1] = GL_LUMINANCE; gl_format[2] = GL_LUMINANCE; } - es->is_opaque = true; break; case WL_SHM_FORMAT_NV12: pitch = wl_shm_buffer_get_stride(shm_buffer); @@ -2109,7 +2099,6 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) gl_format[0] = GL_LUMINANCE; gl_format[1] = GL_LUMINANCE_ALPHA; } - es->is_opaque = true; break; case WL_SHM_FORMAT_YUYV: gb->shader_variant = SHADER_VARIANT_Y_XUXV; @@ -2124,7 +2113,6 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) else gl_format[0] = GL_LUMINANCE_ALPHA; gl_format[1] = GL_BGRA_EXT; - es->is_opaque = true; break; case WL_SHM_FORMAT_XYUV8888: /* @@ -2135,7 +2123,6 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; gl_format[0] = GL_RGBA; gl_pixel_type = GL_UNSIGNED_BYTE; - es->is_opaque = true; break; default: unsupported: @@ -2251,7 +2238,6 @@ gl_renderer_attach_egl(struct weston_surface *es, struct weston_buffer *buffer) egl_image_unref(gb->images[i]); gb->images[i] = NULL; } - es->is_opaque = false; if (!gr->has_bind_display || !gr->query_buffer(gr->egl_display, buffer->legacy_buffer, @@ -2263,7 +2249,6 @@ gl_renderer_attach_egl(struct weston_surface *es, struct weston_buffer *buffer) switch (format) { case EGL_TEXTURE_RGB: - es->is_opaque = true; /* fallthrough */ case EGL_TEXTURE_RGBA: default: @@ -2277,17 +2262,14 @@ gl_renderer_attach_egl(struct weston_surface *es, struct weston_buffer *buffer) case EGL_TEXTURE_Y_UV_WL: num_planes = 2; gb->shader_variant = SHADER_VARIANT_Y_UV; - es->is_opaque = true; break; case EGL_TEXTURE_Y_U_V_WL: num_planes = 3; gb->shader_variant = SHADER_VARIANT_Y_U_V; - es->is_opaque = true; break; case EGL_TEXTURE_Y_XUXV_WL: num_planes = 2; gb->shader_variant = SHADER_VARIANT_Y_XUXV; - es->is_opaque = true; break; } @@ -2895,7 +2877,6 @@ gl_renderer_attach_dmabuf(struct weston_surface *surface, gb->num_images = 0; gb->direct_display = dmabuf->direct_display; - surface->is_opaque = pixel_format_is_opaque(buffer->pixel_format); if (dmabuf->direct_display) return true; @@ -3058,7 +3039,6 @@ out: glDeleteTextures(gs->num_textures, gs->textures); gs->num_textures = 0; gb->direct_display = false; - es->is_opaque = false; } static uint32_t From 57c34139d36074804c8b2e112aeb53cbfa927ee2 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 19 Jan 2022 17:56:26 +0000 Subject: [PATCH 249/609] gl-renderer: Don't modify buffer_state in attach At the moment, attach_shm() will modify the gl_buffer_state in place, then compare it and see if it differs enough to require a new one. That rather mixes up the old and new worlds, so quite explicitly build up a shadow gl_buffer_state with variables first before we change the one which already exists. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 78 +++++++++++++++++------------ 1 file changed, 45 insertions(+), 33 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 8abbce71..eae23efd 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -1974,35 +1974,36 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) struct weston_compositor *ec = es->compositor; struct gl_renderer *gr = get_renderer(ec); struct gl_surface_state *gs = get_surface_state(es); - struct gl_buffer_state *gb = &gs->buffer; + struct gl_buffer_state *gb; struct wl_shm_buffer *shm_buffer = buffer->shm_buffer; struct weston_buffer *old_buffer = gs->buffer_ref.buffer; GLenum gl_format[3] = {0, 0, 0}; GLenum gl_pixel_type; + enum gl_shader_texture_variant shader_variant; int pitch; + int offset[3] = { 0, 0, 0 }; + int hsub[3] = { 1, 0, 0 }; + int vsub[3] = { 1, 0, 0 }; int num_planes; bool using_glesv2 = gr->gl_version < gr_gl_version(3, 0); num_planes = 1; - gb->offset[0] = 0; - gb->hsub[0] = 1; - gb->vsub[0] = 1; switch (wl_shm_buffer_get_format(shm_buffer)) { case WL_SHM_FORMAT_XRGB8888: - gb->shader_variant = SHADER_VARIANT_RGBX; + shader_variant = SHADER_VARIANT_RGBX; pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; gl_format[0] = GL_BGRA_EXT; gl_pixel_type = GL_UNSIGNED_BYTE; break; case WL_SHM_FORMAT_ARGB8888: - gb->shader_variant = SHADER_VARIANT_RGBA; + shader_variant = SHADER_VARIANT_RGBA; pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; gl_format[0] = GL_BGRA_EXT; gl_pixel_type = GL_UNSIGNED_BYTE; break; case WL_SHM_FORMAT_RGB565: - gb->shader_variant = SHADER_VARIANT_RGBX; + shader_variant = SHADER_VARIANT_RGBX; pitch = wl_shm_buffer_get_stride(shm_buffer) / 2; gl_format[0] = GL_RGB; gl_pixel_type = GL_UNSIGNED_SHORT_5_6_5; @@ -2012,7 +2013,7 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) if (!gr->has_texture_type_2_10_10_10_rev) { goto unsupported; } - gb->shader_variant = SHADER_VARIANT_RGBA; + shader_variant = SHADER_VARIANT_RGBA; pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; gl_format[0] = using_glesv2 ? GL_RGBA : GL_RGB10_A2; gl_pixel_type = GL_UNSIGNED_INT_2_10_10_10_REV_EXT; @@ -2021,7 +2022,7 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) if (!gr->has_texture_type_2_10_10_10_rev) { goto unsupported; } - gb->shader_variant = SHADER_VARIANT_RGBX; + shader_variant = SHADER_VARIANT_RGBX; pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; gl_format[0] = using_glesv2 ? GL_RGBA : GL_RGB10_A2; gl_pixel_type = GL_UNSIGNED_INT_2_10_10_10_REV_EXT; @@ -2029,7 +2030,7 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) case WL_SHM_FORMAT_ABGR16161616F: if (!gr->gl_supports_color_transforms) goto unsupported; - gb->shader_variant = SHADER_VARIANT_RGBA; + shader_variant = SHADER_VARIANT_RGBA; pitch = wl_shm_buffer_get_stride(shm_buffer) / 8; gl_format[0] = GL_RGBA16F; gl_pixel_type = GL_HALF_FLOAT; @@ -2037,7 +2038,7 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) case WL_SHM_FORMAT_XBGR16161616F: if (!gr->gl_supports_color_transforms) goto unsupported; - gb->shader_variant = SHADER_VARIANT_RGBX; + shader_variant = SHADER_VARIANT_RGBX; pitch = wl_shm_buffer_get_stride(shm_buffer) / 8; gl_format[0] = GL_RGBA16F; gl_pixel_type = GL_HALF_FLOAT; @@ -2045,7 +2046,7 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) case WL_SHM_FORMAT_ABGR16161616: if (!gr->has_texture_norm16) goto unsupported; - gb->shader_variant = SHADER_VARIANT_RGBA; + shader_variant = SHADER_VARIANT_RGBA; pitch = wl_shm_buffer_get_stride(shm_buffer) / 8; gl_format[0] = GL_RGBA16_EXT; gl_pixel_type = GL_UNSIGNED_SHORT; @@ -2053,25 +2054,25 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) case WL_SHM_FORMAT_XBGR16161616: if (!gr->has_texture_norm16) goto unsupported; - gb->shader_variant = SHADER_VARIANT_RGBX; + shader_variant = SHADER_VARIANT_RGBX; pitch = wl_shm_buffer_get_stride(shm_buffer) / 8; gl_format[0] = GL_RGBA16_EXT; gl_pixel_type = GL_UNSIGNED_SHORT; break; #endif case WL_SHM_FORMAT_YUV420: - gb->shader_variant = SHADER_VARIANT_Y_U_V; + shader_variant = SHADER_VARIANT_Y_U_V; pitch = wl_shm_buffer_get_stride(shm_buffer); gl_pixel_type = GL_UNSIGNED_BYTE; num_planes = 3; - gb->offset[1] = gb->offset[0] + (pitch / gb->hsub[0]) * - (buffer->height / gb->vsub[0]); - gb->hsub[1] = 2; - gb->vsub[1] = 2; - gb->offset[2] = gb->offset[1] + (pitch / gb->hsub[1]) * - (buffer->height / gb->vsub[1]); - gb->hsub[2] = 2; - gb->vsub[2] = 2; + offset[1] = offset[0] + (pitch / hsub[0]) * + (buffer->height / vsub[0]); + hsub[1] = 2; + vsub[1] = 2; + offset[2] = offset[1] + (pitch / hsub[1]) * + (buffer->height / vsub[1]); + hsub[2] = 2; + vsub[2] = 2; if (gr->has_gl_texture_rg) { gl_format[0] = GL_R8_EXT; gl_format[1] = GL_R8_EXT; @@ -2086,28 +2087,28 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) pitch = wl_shm_buffer_get_stride(shm_buffer); gl_pixel_type = GL_UNSIGNED_BYTE; num_planes = 2; - gb->offset[1] = gb->offset[0] + (pitch / gb->hsub[0]) * - (buffer->height / gb->vsub[0]); - gb->hsub[1] = 2; - gb->vsub[1] = 2; + offset[1] = offset[0] + (pitch / hsub[0]) * + (buffer->height / vsub[0]); + hsub[1] = 2; + vsub[1] = 2; if (gr->has_gl_texture_rg) { - gb->shader_variant = SHADER_VARIANT_Y_UV; + shader_variant = SHADER_VARIANT_Y_UV; gl_format[0] = GL_R8_EXT; gl_format[1] = GL_RG8_EXT; } else { - gb->shader_variant = SHADER_VARIANT_Y_XUXV; + shader_variant = SHADER_VARIANT_Y_XUXV; gl_format[0] = GL_LUMINANCE; gl_format[1] = GL_LUMINANCE_ALPHA; } break; case WL_SHM_FORMAT_YUYV: - gb->shader_variant = SHADER_VARIANT_Y_XUXV; + shader_variant = SHADER_VARIANT_Y_XUXV; pitch = wl_shm_buffer_get_stride(shm_buffer) / 2; gl_pixel_type = GL_UNSIGNED_BYTE; num_planes = 2; - gb->offset[1] = 0; - gb->hsub[1] = 2; - gb->vsub[1] = 1; + offset[1] = 0; + hsub[1] = 2; + vsub[1] = 1; if (gr->has_gl_texture_rg) gl_format[0] = GL_RG8_EXT; else @@ -2119,7 +2120,7 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) * [31:0] X:Y:Cb:Cr 8:8:8:8 little endian * a:b: g: r in SHADER_VARIANT_XYUV */ - gb->shader_variant = SHADER_VARIANT_XYUV; + shader_variant = SHADER_VARIANT_XYUV; pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; gl_format[0] = GL_RGBA; gl_pixel_type = GL_UNSIGNED_BYTE; @@ -2131,7 +2132,10 @@ unsupported: return false; } + gb = &gs->buffer; gb->pitch = pitch; + gb->shader_variant = shader_variant; + memcpy(gb->offset, offset, sizeof(offset)); /* Only allocate a texture if it doesn't match existing one. * If a switch from DRM allocated buffer to a SHM buffer is @@ -2139,6 +2143,12 @@ unsupported: if (old_buffer && buffer->width == old_buffer->width && buffer->height == old_buffer->height && + hsub[0] == gb->hsub[0] && + hsub[1] == gb->hsub[1] && + hsub[2] == gb->hsub[2] && + vsub[0] == gb->vsub[0] && + vsub[1] == gb->vsub[1] && + vsub[2] == gb->vsub[2] && gl_format[0] == gb->gl_format[0] && gl_format[1] == gb->gl_format[1] && gl_format[2] == gb->gl_format[2] && @@ -2147,6 +2157,8 @@ unsupported: return true; } + memcpy(gb->hsub, hsub, sizeof(hsub)); + memcpy(gb->vsub, vsub, sizeof(vsub)); gb->gl_format[0] = gl_format[0]; gb->gl_format[1] = gl_format[1]; gb->gl_format[2] = gl_format[2]; From 8544a4d09bdc0bb601e785b84cac80f9652f4606 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 19 Jan 2022 17:59:50 +0000 Subject: [PATCH 250/609] weston_buffer: Move direct_display out of gl-renderer Just make it a generic buffer attribute, not hidden away in GL. Signed-off-by: Daniel Stone --- include/libweston/libweston.h | 2 ++ libweston/compositor.c | 4 ++++ libweston/renderer-gl/gl-renderer.c | 18 ++++++------------ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 65c58428..f529ee22 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -1219,6 +1219,8 @@ struct weston_buffer { ORIGIN_TOP_LEFT, /* buffer content starts at (0,0) */ ORIGIN_BOTTOM_LEFT, /* buffer content starts at (0, height) */ } buffer_origin; + bool direct_display; + void *backend_private; const struct pixel_format_info *pixel_format; diff --git a/libweston/compositor.c b/libweston/compositor.c index a9f043ba..b37a93bd 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -2444,6 +2444,7 @@ weston_buffer_from_resource(struct weston_compositor *ec, } else if ((dmabuf = linux_dmabuf_buffer_get(buffer->resource))) { buffer->type = WESTON_BUFFER_DMABUF; buffer->dmabuf = dmabuf; + buffer->direct_display = dmabuf->direct_display; buffer->width = dmabuf->attributes.width; buffer->height = dmabuf->attributes.height; buffer->pixel_format = @@ -7729,6 +7730,9 @@ debug_scene_view_print_buffer(FILE *fp, struct weston_view *view) buffer->width, buffer->height); if (buffer->buffer_origin == ORIGIN_BOTTOM_LEFT) fprintf(fp, "\t\t\tbottom-left origin\n"); + + if (buffer->direct_display) + fprintf(fp, "\t\t\tdirect-display buffer (no renderer access)\n"); } static void diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index eae23efd..8272ce88 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -176,8 +176,6 @@ struct gl_buffer_state { int num_images; enum gl_shader_texture_variant shader_variant; - bool direct_display; - /* Extension needed for SHM YUV texture */ int offset[3]; /* offset per plane */ int hsub[3]; /* horizontal subsampling per plane */ @@ -964,7 +962,7 @@ maybe_censor_override(struct gl_shader_config *sconf, struct weston_view *ev) { struct gl_surface_state *gs = get_surface_state(ev->surface); - struct gl_buffer_state *gb = &gs->buffer; + struct weston_buffer *buffer = gs->buffer_ref.buffer; bool recording_censor = (output->disable_planes > 0) && (ev->surface->desired_protection > WESTON_HDCP_DISABLE); @@ -972,7 +970,7 @@ maybe_censor_override(struct gl_shader_config *sconf, bool unprotected_censor = (ev->surface->desired_protection > output->current_protection); - if (gb->direct_display) { + if (buffer->direct_display) { censor_override(sconf, output); return; } @@ -1042,6 +1040,7 @@ draw_paint_node(struct weston_paint_node *pnode, struct gl_renderer *gr = get_renderer(pnode->surface->compositor); struct gl_surface_state *gs = get_surface_state(pnode->surface); struct gl_buffer_state *gb = &gs->buffer; + struct weston_buffer *buffer = gs->buffer_ref.buffer; /* repaint bounding region in global coordinates: */ pixman_region32_t repaint; /* opaque region in surface coordinates: */ @@ -1054,7 +1053,8 @@ draw_paint_node(struct weston_paint_node *pnode, /* In case of a runtime switch of renderers, we may not have received * an attach for this surface since the switch. In that case we don't * have a valid buffer or a proper shader set up so skip rendering. */ - if (gb->shader_variant == SHADER_VARIANT_NONE && !gb->direct_display) + if (gb->shader_variant == SHADER_VARIANT_NONE && + !buffer->direct_display) return; pixman_region32_init(&repaint); @@ -2164,7 +2164,6 @@ unsupported: gb->gl_format[2] = gl_format[2]; gb->gl_pixel_type = gl_pixel_type; gb->needs_full_upload = true; - gb->direct_display = false; gs->surface = es; @@ -2888,9 +2887,7 @@ gl_renderer_attach_dmabuf(struct weston_surface *surface, egl_image_unref(gb->images[i]); gb->num_images = 0; - gb->direct_display = dmabuf->direct_display; - - if (dmabuf->direct_display) + if (buffer->direct_display) return true; image = linux_dmabuf_buffer_get_user_data(dmabuf); @@ -3050,7 +3047,6 @@ out: gb->num_images = 0; glDeleteTextures(gs->num_textures, gs->textures); gs->num_textures = 0; - gb->direct_display = false; } static uint32_t @@ -3252,8 +3248,6 @@ gl_renderer_create_surface(struct weston_surface *surface) * they still go through texcoord computations. Do not divide * by zero there. */ - gb->direct_display = false; - gs->surface = surface; pixman_region32_init(&gb->texture_damage); From 8b167a1703308d3026ddd9888e125027eb6dc7aa Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 20 Jan 2022 16:05:03 +0000 Subject: [PATCH 251/609] gl-renderer: Store EGL buffer state in weston_buffer Introduce a renderer_private hook for weston_buffer, and use this to store a copy of the gl_buffer_state for EGL buffers (i.e. non-dmabuf, via EGL_WL_bind_wayland_display). As part of this, we create the EGLImage along with the weston_buffer information, and just take a reference to it each time it is attached. If you have bisected a failure to update surface content to this commit, it very likely means that your EGL implementation requires images to be recreated rather than only rebound in order to have their content updated, which is contrary to specification. Signed-off-by: Daniel Stone --- include/libweston/libweston.h | 1 + libweston/renderer-gl/gl-renderer.c | 161 +++++++++++++++++----------- 2 files changed, 100 insertions(+), 62 deletions(-) diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index f529ee22..bd5ac8c6 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -1221,6 +1221,7 @@ struct weston_buffer { } buffer_origin; bool direct_display; + void *renderer_private; void *backend_private; const struct pixel_format_info *pixel_format; diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 8272ce88..ba5f3b63 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -180,6 +180,8 @@ struct gl_buffer_state { int offset[3]; /* offset per plane */ int hsub[3]; /* horizontal subsampling per plane */ int vsub[3]; /* vertical subsampling per plane */ + + struct wl_listener destroy_listener; }; struct gl_surface_state { @@ -1944,6 +1946,32 @@ done: weston_buffer_release_reference(&gs->buffer_release_ref, NULL); } +static void +destroy_buffer_state(struct gl_buffer_state *gb) +{ + int i; + + for (i = 0; i < gb->num_images; i++) + egl_image_unref(gb->images[i]); + + wl_list_remove(&gb->destroy_listener.link); + + free(gb); +} + +static void +handle_buffer_destroy(struct wl_listener *listener, void *data) +{ + struct weston_buffer *buffer = data; + struct gl_buffer_state *gb = + container_of(listener, struct gl_buffer_state, destroy_listener); + + assert(gb == buffer->renderer_private); + buffer->renderer_private = NULL; + + destroy_buffer_state(gb); +} + static void ensure_textures(struct gl_surface_state *gs, GLenum target, int num_textures) { @@ -2177,10 +2205,15 @@ gl_renderer_fill_buffer_info(struct weston_compositor *ec, struct weston_buffer *buffer) { struct gl_renderer *gr = get_renderer(ec); + struct gl_buffer_state *gb = zalloc(sizeof(*gb)); EGLint format; uint32_t fourcc; EGLint y_inverted; bool ret = true; + int i; + + if (!gb) + return false; buffer->legacy_buffer = (struct wl_buffer *)buffer->resource; ret &= gr->query_buffer(gr->egl_display, buffer->legacy_buffer, @@ -2189,8 +2222,11 @@ gl_renderer_fill_buffer_info(struct weston_compositor *ec, EGL_HEIGHT, &buffer->height); ret &= gr->query_buffer(gr->egl_display, buffer->legacy_buffer, EGL_TEXTURE_FORMAT, &format); - if (!ret) - return false; + if (!ret) { + weston_log("eglQueryWaylandBufferWL failed\n"); + gl_renderer_print_egl_error_state(); + goto err_free; + } /* The legacy EGL buffer interface only describes the channels we can * sample from; not their depths or order. Take a stab at something @@ -2199,19 +2235,33 @@ gl_renderer_fill_buffer_info(struct weston_compositor *ec, switch (format) { case EGL_TEXTURE_RGB: fourcc = DRM_FORMAT_XRGB8888; + gb->num_images = 1; + gb->shader_variant = SHADER_VARIANT_RGBA; break; - case EGL_TEXTURE_EXTERNAL_WL: case EGL_TEXTURE_RGBA: fourcc = DRM_FORMAT_ARGB8888; + gb->num_images = 1; + gb->shader_variant = SHADER_VARIANT_RGBA; + break; + case EGL_TEXTURE_EXTERNAL_WL: + fourcc = DRM_FORMAT_ARGB8888; + gb->num_images = 1; + gb->shader_variant = SHADER_VARIANT_EXTERNAL; break; case EGL_TEXTURE_Y_XUXV_WL: fourcc = DRM_FORMAT_YUYV; + gb->num_images = 2; + gb->shader_variant = SHADER_VARIANT_Y_XUXV; break; case EGL_TEXTURE_Y_UV_WL: fourcc = DRM_FORMAT_NV12; + gb->num_images = 2; + gb->shader_variant = SHADER_VARIANT_Y_UV; break; case EGL_TEXTURE_Y_U_V_WL: fourcc = DRM_FORMAT_YUV420; + gb->num_images = 2; + gb->shader_variant = SHADER_VARIANT_Y_U_V; break; default: assert(0 && "not reached"); @@ -2230,7 +2280,33 @@ gl_renderer_fill_buffer_info(struct weston_compositor *ec, else buffer->buffer_origin = ORIGIN_BOTTOM_LEFT; + for (i = 0; i < gb->num_images; i++) { + const EGLint attribs[] = { + EGL_WAYLAND_PLANE_WL, i, + EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, + EGL_NONE + }; + + gb->images[i] = egl_image_create(gr, EGL_WAYLAND_BUFFER_WL, + buffer->legacy_buffer, + attribs); + if (!gb->images[i]) { + weston_log("couldn't create EGLImage for plane %d\n", i); + goto err_img; + } + } + + buffer->renderer_private = gb; + gb->destroy_listener.notify = handle_buffer_destroy; + wl_signal_add(&buffer->destroy_signal, &gb->destroy_listener); return true; + +err_img: + while (--i >= 0) + egl_image_unref(gb->images[i]); +err_free: + free(gb); + return false; } static bool @@ -2239,72 +2315,33 @@ gl_renderer_attach_egl(struct weston_surface *es, struct weston_buffer *buffer) struct weston_compositor *ec = es->compositor; struct gl_renderer *gr = get_renderer(ec); struct gl_surface_state *gs = get_surface_state(es); - struct gl_buffer_state *gb = &gs->buffer; - EGLint attribs[5]; - EGLint format; + struct gl_buffer_state *gb; GLenum target; - int i, num_planes; + int i; - for (i = 0; i < gb->num_images; i++) { - egl_image_unref(gb->images[i]); - gb->images[i] = NULL; - } + assert(buffer->renderer_private); - if (!gr->has_bind_display || - !gr->query_buffer(gr->egl_display, buffer->legacy_buffer, - EGL_TEXTURE_FORMAT, &format)) { - weston_log("eglQueryWaylandBufferWL failed\n"); - gl_renderer_print_egl_error_state(); - return false; + /* The old gl_buffer_state stored in the gl_surface_state might still + * hold a ref to some other EGLImages: make sure we drop those. */ + for (i = 0; i < gs->buffer.num_images; i++) { + egl_image_unref(gs->buffer.images[i]); + gs->buffer.images[i] = NULL; } - switch (format) { - case EGL_TEXTURE_RGB: - /* fallthrough */ - case EGL_TEXTURE_RGBA: - default: - num_planes = 1; - gb->shader_variant = SHADER_VARIANT_RGBA; - break; - case EGL_TEXTURE_EXTERNAL_WL: - num_planes = 1; - gb->shader_variant = SHADER_VARIANT_EXTERNAL; - break; - case EGL_TEXTURE_Y_UV_WL: - num_planes = 2; - gb->shader_variant = SHADER_VARIANT_Y_UV; - break; - case EGL_TEXTURE_Y_U_V_WL: - num_planes = 3; - gb->shader_variant = SHADER_VARIANT_Y_U_V; - break; - case EGL_TEXTURE_Y_XUXV_WL: - num_planes = 2; - gb->shader_variant = SHADER_VARIANT_Y_XUXV; - break; - } + /* Copy over our gl_buffer_state */ + memcpy(&gs->buffer, buffer->renderer_private, sizeof(gs->buffer)); + gb = &gs->buffer; - gb->num_images = num_planes; target = gl_shader_texture_variant_get_target(gb->shader_variant); - ensure_textures(gs, target, num_planes); - for (i = 0; i < num_planes; i++) { - attribs[0] = EGL_WAYLAND_PLANE_WL; - attribs[1] = i; - attribs[2] = EGL_IMAGE_PRESERVED_KHR; - attribs[3] = EGL_TRUE; - attribs[4] = EGL_NONE; - - gb->images[i] = egl_image_create(gr, - EGL_WAYLAND_BUFFER_WL, - buffer->legacy_buffer, - attribs); - if (!gb->images[i]) { - weston_log("failed to create img for plane %d\n", i); - while (--i >= 0) - egl_image_unref(gb->images[i]); - return false; - } - + ensure_textures(gs, target, gb->num_images); + for (i = 0; i < gb->num_images; i++) { + /* The gl_buffer_state stored in buffer->renderer_private lives + * as long as the buffer does, and holds a ref to the EGLImage + * for this buffer; in copying to the gl_buffer_state inlined + * as part of gl_surface_state, we take an extra ref, which is + * destroyed on different attachments, e.g. at the start of + * this function. */ + gb->images[i] = egl_image_ref(gb->images[i]); glActiveTexture(GL_TEXTURE0 + i); glBindTexture(target, gs->textures[i]); gr->image_target_texture_2d(target, gb->images[i]->image); From 56dc4b8aaa38b8f1ff46db41ec3ea8b06a38d309 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 20 Jan 2022 16:40:50 +0000 Subject: [PATCH 252/609] gl-renderer: Remove unused dmabuf import_type We don't need this now we don't try to reimport them on attach. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index ba5f3b63..59b56838 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -114,11 +114,6 @@ struct egl_image { int refcount; }; -enum import_type { - IMPORT_TYPE_INVALID, - IMPORT_TYPE_DIRECT, - IMPORT_TYPE_GL_CONVERSION -}; struct dmabuf_image { struct linux_dmabuf_buffer *dmabuf; @@ -126,7 +121,6 @@ struct dmabuf_image { struct egl_image *images[3]; struct wl_list link; - enum import_type import_type; enum gl_shader_texture_variant shader_variant; }; @@ -2749,7 +2743,6 @@ import_dmabuf(struct gl_renderer *gr, if (egl_image) { image->num_images = 1; image->images[0] = egl_image; - image->import_type = IMPORT_TYPE_DIRECT; target = choose_texture_target(gr, &dmabuf->attributes); switch (target) { @@ -2764,7 +2757,6 @@ import_dmabuf(struct gl_renderer *gr, dmabuf_image_destroy(image); return NULL; } - image->import_type = IMPORT_TYPE_GL_CONVERSION; } return image; From acc3762506ff5cf88c3f5a2fb3c3115f86a1400d Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 20 Jan 2022 17:40:06 +0000 Subject: [PATCH 253/609] gl-renderer: Store dmabuf buffer state in weston_buffer Similarly to EGL buffers, store the gl_buffer_state for a dmabuf buffer inside weston_buffer, rather than on the linux_dmabuf_buffer. This slightly simplifies our gl_buffer_state handling, and will be used later to eliminate the egl_image refcounting. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 165 ++++++++++++---------------- 1 file changed, 72 insertions(+), 93 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 59b56838..84ec4d02 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -114,16 +114,6 @@ struct egl_image { int refcount; }; - -struct dmabuf_image { - struct linux_dmabuf_buffer *dmabuf; - int num_images; - struct egl_image *images[3]; - struct wl_list link; - - enum gl_shader_texture_variant shader_variant; -}; - struct dmabuf_format { uint32_t format; struct wl_list link; @@ -403,32 +393,6 @@ egl_image_unref(struct egl_image *image) return 0; } -static struct dmabuf_image* -dmabuf_image_create(void) -{ - struct dmabuf_image *img; - - img = zalloc(sizeof *img); - wl_list_init(&img->link); - - return img; -} - -static void -dmabuf_image_destroy(struct dmabuf_image *image) -{ - int i; - - for (i = 0; i < image->num_images; ++i) - egl_image_unref(image->images[i]); - - if (image->dmabuf) - linux_dmabuf_buffer_set_user_data(image->dmabuf, NULL, NULL); - - wl_list_remove(&image->link); - free(image); -} - #define max(a, b) (((a) > (b)) ? (a) : (b)) #define min(a, b) (((a) > (b)) ? (b) : (a)) @@ -2347,9 +2311,11 @@ gl_renderer_attach_egl(struct weston_surface *es, struct weston_buffer *buffer) static void gl_renderer_destroy_dmabuf(struct linux_dmabuf_buffer *dmabuf) { - struct dmabuf_image *image = linux_dmabuf_buffer_get_user_data(dmabuf); + struct gl_buffer_state *gb = + linux_dmabuf_buffer_get_user_data(dmabuf); - dmabuf_image_destroy(image); + linux_dmabuf_buffer_set_user_data(dmabuf, NULL, NULL); + destroy_buffer_state(gb); } static struct egl_image * @@ -2573,14 +2539,13 @@ import_dmabuf_single_plane(struct gl_renderer *gr, } static bool -import_yuv_dmabuf(struct gl_renderer *gr, - struct dmabuf_image *image) +import_yuv_dmabuf(struct gl_renderer *gr, struct gl_buffer_state *gb, + struct dmabuf_attributes *attributes) { unsigned i; int j; int ret; struct yuv_format_descriptor *format = NULL; - struct dmabuf_attributes *attributes = &image->dmabuf->attributes; char fmt[4]; for (i = 0; i < ARRAY_LENGTH(yuv_formats); ++i) { @@ -2607,31 +2572,32 @@ import_yuv_dmabuf(struct gl_renderer *gr, } for (j = 0; j < format->output_planes; ++j) { - image->images[j] = import_dmabuf_single_plane(gr, attributes, - &format->plane[j]); - if (!image->images[j]) { - while (j) { - ret = egl_image_unref(image->images[--j]); + gb->images[j] = import_dmabuf_single_plane(gr, attributes, + &format->plane[j]); + if (!gb->images[j]) { + while (--j >= 0) { + ret = egl_image_unref(gb->images[j]); assert(ret == 0); + gb->images[j] = NULL; } return false; } } - image->num_images = format->output_planes; + gb->num_images = format->output_planes; switch (format->texture_type) { case TEXTURE_Y_XUXV_WL: - image->shader_variant = SHADER_VARIANT_Y_XUXV; + gb->shader_variant = SHADER_VARIANT_Y_XUXV; break; case TEXTURE_Y_UV_WL: - image->shader_variant = SHADER_VARIANT_Y_UV; + gb->shader_variant = SHADER_VARIANT_Y_UV; break; case TEXTURE_Y_U_V_WL: - image->shader_variant = SHADER_VARIANT_Y_U_V; + gb->shader_variant = SHADER_VARIANT_Y_U_V; break; case TEXTURE_XYUV_WL: - image->shader_variant = SHADER_VARIANT_XYUV; + gb->shader_variant = SHADER_VARIANT_XYUV; break; default: assert(false); @@ -2725,41 +2691,46 @@ choose_texture_target(struct gl_renderer *gr, } } -static struct dmabuf_image * +static struct gl_buffer_state * import_dmabuf(struct gl_renderer *gr, struct linux_dmabuf_buffer *dmabuf) { struct egl_image *egl_image; - struct dmabuf_image *image; - GLenum target; + struct gl_buffer_state *gb = zalloc(sizeof(*gb)); if (!pixel_format_get_info(dmabuf->attributes.format)) return NULL; - image = dmabuf_image_create(); - image->dmabuf = dmabuf; + gb = zalloc(sizeof(*gb)); + if (!gb) + return NULL; + + wl_list_init(&gb->destroy_listener.link); egl_image = import_simple_dmabuf(gr, &dmabuf->attributes); if (egl_image) { - image->num_images = 1; - image->images[0] = egl_image; - target = choose_texture_target(gr, &dmabuf->attributes); + GLenum target = choose_texture_target(gr, &dmabuf->attributes); + + gb->num_images = 1; + gb->images[0] = egl_image; switch (target) { case GL_TEXTURE_2D: - image->shader_variant = SHADER_VARIANT_RGBA; + gb->shader_variant = SHADER_VARIANT_RGBA; break; default: - image->shader_variant = SHADER_VARIANT_EXTERNAL; - } - } else { - if (!import_yuv_dmabuf(gr, image)) { - dmabuf_image_destroy(image); - return NULL; + gb->shader_variant = SHADER_VARIANT_EXTERNAL; } + + return gb; } - return image; + if (!import_yuv_dmabuf(gr, gb, &dmabuf->attributes)) { + destroy_buffer_state(gb); + return NULL; + } + + return gb; } static void @@ -2868,7 +2839,7 @@ gl_renderer_import_dmabuf(struct weston_compositor *ec, struct linux_dmabuf_buffer *dmabuf) { struct gl_renderer *gr = get_renderer(ec); - struct dmabuf_image *image; + struct gl_buffer_state *gb; int i; assert(gr->has_dmabuf_import); @@ -2889,12 +2860,11 @@ gl_renderer_import_dmabuf(struct weston_compositor *ec, if (dmabuf->attributes.flags & ~ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT) return false; - image = import_dmabuf(gr, dmabuf); - if (!image) + gb = import_dmabuf(gr, dmabuf); + if (!gb) return false; - wl_list_insert(&gr->dmabuf_images, &image->link); - linux_dmabuf_buffer_set_user_data(dmabuf, image, + linux_dmabuf_buffer_set_user_data(dmabuf, gb, gl_renderer_destroy_dmabuf); return true; @@ -2906,38 +2876,52 @@ gl_renderer_attach_dmabuf(struct weston_surface *surface, { struct gl_renderer *gr = get_renderer(surface->compositor); struct gl_surface_state *gs = get_surface_state(surface); - struct gl_buffer_state *gb = &gs->buffer; + struct gl_buffer_state *gb; struct linux_dmabuf_buffer *dmabuf = buffer->dmabuf; - struct dmabuf_image *image; GLenum target; int i; - for (i = 0; i < gb->num_images; i++) - egl_image_unref(gb->images[i]); - gb->num_images = 0; + for (i = 0; i < gs->buffer.num_images; i++) + egl_image_unref(gs->buffer.images[i]); + gs->buffer.num_images = 0; if (buffer->direct_display) return true; - image = linux_dmabuf_buffer_get_user_data(dmabuf); - - /* The dmabuf_image should have been created during the import */ - assert(image != NULL); + /* Thanks to linux-dmabuf being totally independent of libweston, + * the first time a dmabuf is attached, the gl_buffer_state will + * only be set as userdata on the dmabuf, not on the weston_buffer. + * When this happens, steal it away into the weston_buffer. */ + if (!buffer->renderer_private) { + gb = linux_dmabuf_buffer_get_user_data(dmabuf); + assert(gb); + linux_dmabuf_buffer_set_user_data(dmabuf, NULL, NULL); + buffer->renderer_private = gb; + gb->destroy_listener.notify = handle_buffer_destroy; + wl_signal_add(&buffer->destroy_signal, &gb->destroy_listener); + } - gb->num_images = image->num_images; + assert(buffer->renderer_private); + assert(linux_dmabuf_buffer_get_user_data(dmabuf) == NULL); + gb = buffer->renderer_private; + + /* The gl_buffer_state stored in the weston_buffer holds a reference + * on the EGLImages; we take another ref when we copy into the + * gl_surface_state so we don't lose track of anything. This is + * temporary and will be removed when gl_surface_state starts + * referencing rather than inlining the gl_buffer_state. */ + memcpy(&gs->buffer, gb, sizeof(*gb)); for (i = 0; i < gb->num_images; ++i) - gb->images[i] = egl_image_ref(image->images[i]); + gs->buffer.images[i] = egl_image_ref(gs->buffer.images[i]); - target = gl_shader_texture_variant_get_target(image->shader_variant); + target = gl_shader_texture_variant_get_target(gs->buffer.shader_variant); ensure_textures(gs, target, gb->num_images); - for (i = 0; i < gb->num_images; ++i) { + for (i = 0; i < gs->buffer.num_images; ++i) { glActiveTexture(GL_TEXTURE0 + i); glBindTexture(target, gs->textures[i]); - gr->image_target_texture_2d(target, gb->images[i]->image); + gr->image_target_texture_2d(target, gs->buffer.images[i]->image); } - gb->shader_variant = image->shader_variant; - return true; } @@ -3610,7 +3594,6 @@ static void gl_renderer_destroy(struct weston_compositor *ec) { struct gl_renderer *gr = get_renderer(ec); - struct dmabuf_image *image, *next; struct dmabuf_format *format, *next_format; wl_signal_emit(&gr->destroy_signal, gr); @@ -3627,9 +3610,6 @@ gl_renderer_destroy(struct weston_compositor *ec) EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - wl_list_for_each_safe(image, next, &gr->dmabuf_images, link) - dmabuf_image_destroy(image); - wl_list_for_each_safe(format, next_format, &gr->dmabuf_formats, link) dmabuf_format_destroy(format); @@ -3783,7 +3763,6 @@ gl_renderer_display_create(struct weston_compositor *ec, if (gr->has_native_fence_sync && gr->has_wait_sync) ec->capabilities |= WESTON_CAP_EXPLICIT_SYNC; - wl_list_init(&gr->dmabuf_images); if (gr->has_dmabuf_import) { gr->base.import_dmabuf = gl_renderer_import_dmabuf; gr->base.get_supported_formats = gl_renderer_get_supported_formats; From 3297d10287a0d8003ef0ca89581246891ad99ce4 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 20 Jan 2022 18:14:07 +0000 Subject: [PATCH 254/609] gl-renderer: Cache gl_buffer_state on the weston_buffer ... apart from SHM. EGL and dmabuf buffers already have a gl_buffer_state created for them when we first attach the weston_buffer. By turning gl_surface_state::buffer into a pointer, we can just reference rather than inline the gl_buffer_state. SHM buffers are special, in that we don't keep individual copies of them within the GL renderer. Instead, the GL surface has a texture allocated with a shadow copy of the most up-to-date surface content. Handle this by allocating and destroying gl_buffer_state every time we need to respecify textures or somehow meaningfully change the parameters. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 172 ++++++++++++++-------------- 1 file changed, 87 insertions(+), 85 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 84ec4d02..131e6cea 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -171,7 +171,7 @@ struct gl_buffer_state { struct gl_surface_state { struct weston_surface *surface; - struct gl_buffer_state buffer; + struct gl_buffer_state *buffer; /* These buffer references should really be attached to paint nodes * rather than either buffer or surface state */ @@ -358,14 +358,6 @@ egl_image_create(struct gl_renderer *gr, EGLenum target, return img; } -static struct egl_image* -egl_image_ref(struct egl_image *image) -{ - image->refcount++; - - return image; -} - static int egl_image_unref(struct egl_image *image) { @@ -949,7 +941,7 @@ static void gl_shader_config_set_input_textures(struct gl_shader_config *sconf, struct gl_surface_state *gs) { - struct gl_buffer_state *gb = &gs->buffer; + struct gl_buffer_state *gb = gs->buffer; int i; sconf->req.variant = gb->shader_variant; @@ -999,7 +991,7 @@ draw_paint_node(struct weston_paint_node *pnode, { struct gl_renderer *gr = get_renderer(pnode->surface->compositor); struct gl_surface_state *gs = get_surface_state(pnode->surface); - struct gl_buffer_state *gb = &gs->buffer; + struct gl_buffer_state *gb = gs->buffer; struct weston_buffer *buffer = gs->buffer_ref.buffer; /* repaint bounding region in global coordinates: */ pixman_region32_t repaint; @@ -1805,14 +1797,14 @@ gl_renderer_flush_damage(struct weston_surface *surface, const struct weston_testsuite_quirks *quirks = &surface->compositor->test_data.test_quirks; struct gl_surface_state *gs = get_surface_state(surface); - struct gl_buffer_state *gb = &gs->buffer; + struct gl_buffer_state *gb = gs->buffer; struct weston_view *view; bool texture_used; pixman_box32_t *rectangles; uint8_t *data; int i, j, n; - assert(buffer); + assert(buffer && gb); pixman_region32_union(&gb->texture_damage, &gb->texture_damage, &surface->damage); @@ -1912,6 +1904,7 @@ destroy_buffer_state(struct gl_buffer_state *gb) for (i = 0; i < gb->num_images; i++) egl_image_unref(gb->images[i]); + pixman_region32_fini(&gb->texture_damage); wl_list_remove(&gb->destroy_listener.link); free(gb); @@ -2118,31 +2111,44 @@ unsupported: return false; } - gb = &gs->buffer; - gb->pitch = pitch; - gb->shader_variant = shader_variant; - memcpy(gb->offset, offset, sizeof(offset)); - - /* Only allocate a texture if it doesn't match existing one. - * If a switch from DRM allocated buffer to a SHM buffer is - * happening, we need to allocate a new texture buffer. */ - if (old_buffer && + /* If this surface previously had a SHM buffer, its gl_buffer_state will + * be speculatively retained. Check to see if we can reuse it rather + * than allocating a new one. */ + assert(!gs->buffer || + (old_buffer && old_buffer->type == WESTON_BUFFER_SHM)); + if (gs->buffer && buffer->width == old_buffer->width && buffer->height == old_buffer->height && - hsub[0] == gb->hsub[0] && - hsub[1] == gb->hsub[1] && - hsub[2] == gb->hsub[2] && - vsub[0] == gb->vsub[0] && - vsub[1] == gb->vsub[1] && - vsub[2] == gb->vsub[2] && - gl_format[0] == gb->gl_format[0] && - gl_format[1] == gb->gl_format[1] && - gl_format[2] == gb->gl_format[2] && - gl_pixel_type == gb->gl_pixel_type && - old_buffer->type == WESTON_BUFFER_SHM) { + hsub[0] == gs->buffer->hsub[0] && + hsub[1] == gs->buffer->hsub[1] && + hsub[2] == gs->buffer->hsub[2] && + vsub[0] == gs->buffer->vsub[0] && + vsub[1] == gs->buffer->vsub[1] && + vsub[2] == gs->buffer->vsub[2] && + gl_format[0] == gs->buffer->gl_format[0] && + gl_format[1] == gs->buffer->gl_format[1] && + gl_format[2] == gs->buffer->gl_format[2] && + gl_pixel_type == gs->buffer->gl_pixel_type) { + gs->buffer->pitch = pitch; + gs->buffer->shader_variant = shader_variant; + memcpy(gs->buffer->offset, offset, sizeof(offset)); return true; } + if (gs->buffer) + destroy_buffer_state(gs->buffer); + gs->buffer = NULL; + + gb = zalloc(sizeof(*gb)); + if (!gb) + return false; + + wl_list_init(&gb->destroy_listener.link); + pixman_region32_init(&gb->texture_damage); + + gb->pitch = pitch; + gb->shader_variant = shader_variant; + memcpy(gb->offset, offset, sizeof(offset)); memcpy(gb->hsub, hsub, sizeof(hsub)); memcpy(gb->vsub, vsub, sizeof(vsub)); gb->gl_format[0] = gl_format[0]; @@ -2151,6 +2157,7 @@ unsupported: gb->gl_pixel_type = gl_pixel_type; gb->needs_full_upload = true; + gs->buffer = gb; gs->surface = es; ensure_textures(gs, GL_TEXTURE_2D, num_planes); @@ -2173,6 +2180,8 @@ gl_renderer_fill_buffer_info(struct weston_compositor *ec, if (!gb) return false; + pixman_region32_init(&gb->texture_damage); + buffer->legacy_buffer = (struct wl_buffer *)buffer->resource; ret &= gr->query_buffer(gr->egl_display, buffer->legacy_buffer, EGL_WIDTH, &buffer->width); @@ -2273,33 +2282,17 @@ gl_renderer_attach_egl(struct weston_surface *es, struct weston_buffer *buffer) struct weston_compositor *ec = es->compositor; struct gl_renderer *gr = get_renderer(ec); struct gl_surface_state *gs = get_surface_state(es); - struct gl_buffer_state *gb; + struct gl_buffer_state *gb = buffer->renderer_private; GLenum target; int i; - assert(buffer->renderer_private); + assert(gb); - /* The old gl_buffer_state stored in the gl_surface_state might still - * hold a ref to some other EGLImages: make sure we drop those. */ - for (i = 0; i < gs->buffer.num_images; i++) { - egl_image_unref(gs->buffer.images[i]); - gs->buffer.images[i] = NULL; - } - - /* Copy over our gl_buffer_state */ - memcpy(&gs->buffer, buffer->renderer_private, sizeof(gs->buffer)); - gb = &gs->buffer; + gs->buffer = gb; target = gl_shader_texture_variant_get_target(gb->shader_variant); ensure_textures(gs, target, gb->num_images); for (i = 0; i < gb->num_images; i++) { - /* The gl_buffer_state stored in buffer->renderer_private lives - * as long as the buffer does, and holds a ref to the EGLImage - * for this buffer; in copying to the gl_buffer_state inlined - * as part of gl_surface_state, we take an extra ref, which is - * destroyed on different attachments, e.g. at the start of - * this function. */ - gb->images[i] = egl_image_ref(gb->images[i]); glActiveTexture(GL_TEXTURE0 + i); glBindTexture(target, gs->textures[i]); gr->image_target_texture_2d(target, gb->images[i]->image); @@ -2705,6 +2698,7 @@ import_dmabuf(struct gl_renderer *gr, if (!gb) return NULL; + pixman_region32_init(&gb->texture_damage); wl_list_init(&gb->destroy_listener.link); egl_image = import_simple_dmabuf(gr, &dmabuf->attributes); @@ -2881,10 +2875,6 @@ gl_renderer_attach_dmabuf(struct weston_surface *surface, GLenum target; int i; - for (i = 0; i < gs->buffer.num_images; i++) - egl_image_unref(gs->buffer.images[i]); - gs->buffer.num_images = 0; - if (buffer->direct_display) return true; @@ -2905,21 +2895,14 @@ gl_renderer_attach_dmabuf(struct weston_surface *surface, assert(linux_dmabuf_buffer_get_user_data(dmabuf) == NULL); gb = buffer->renderer_private; - /* The gl_buffer_state stored in the weston_buffer holds a reference - * on the EGLImages; we take another ref when we copy into the - * gl_surface_state so we don't lose track of anything. This is - * temporary and will be removed when gl_surface_state starts - * referencing rather than inlining the gl_buffer_state. */ - memcpy(&gs->buffer, gb, sizeof(*gb)); - for (i = 0; i < gb->num_images; ++i) - gs->buffer.images[i] = egl_image_ref(gs->buffer.images[i]); + gs->buffer = gb; - target = gl_shader_texture_variant_get_target(gs->buffer.shader_variant); + target = gl_shader_texture_variant_get_target(gb->shader_variant); ensure_textures(gs, target, gb->num_images); - for (i = 0; i < gs->buffer.num_images; ++i) { + for (i = 0; i < gb->num_images; ++i) { glActiveTexture(GL_TEXTURE0 + i); glBindTexture(target, gs->textures[i]); - gr->image_target_texture_2d(target, gs->buffer.images[i]->image); + gr->image_target_texture_2d(target, gb->images[i]->image); } return true; @@ -2995,7 +2978,19 @@ gl_renderer_attach_solid(struct weston_surface *surface, struct weston_buffer *buffer) { struct gl_surface_state *gs = get_surface_state(surface); - struct gl_buffer_state *gb = &gs->buffer; + struct gl_buffer_state *gb = buffer->renderer_private; + + if (gb) { + gs->buffer = gb; + return true; + } + + gb = zalloc(sizeof(*gb)); + pixman_region32_init(&gb->texture_damage); + + buffer->renderer_private = gb; + gb->destroy_listener.notify = handle_buffer_destroy; + wl_signal_add(&buffer->destroy_signal, &gb->destroy_listener); gb->color[0] = buffer->solid.r; gb->color[1] = buffer->solid.g; @@ -3004,6 +2999,8 @@ gl_renderer_attach_solid(struct weston_surface *surface, gb->shader_variant = SHADER_VARIANT_SOLID; + gs->buffer = gb; + return true; } @@ -3011,9 +3008,23 @@ static void gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) { struct gl_surface_state *gs = get_surface_state(es); - struct gl_buffer_state *gb = &gs->buffer; bool ret = false; - int i; + + /* SHM buffers are a little special in that they are allocated + * per-surface rather than per-buffer, because we keep a shadow + * copy of the SHM data in a GL texture; for these we need to + * destroy the buffer state when we're switching to another + * buffer type. For all the others, the gl_buffer_state comes + * from the weston_buffer itself, and will only be destroyed + * along with it. */ + if (gs->buffer && gs->buffer_ref.buffer->type == WESTON_BUFFER_SHM) { + if (!buffer || buffer->type != WESTON_BUFFER_SHM) { + destroy_buffer_state(gs->buffer); + gs->buffer = NULL; + } + } else { + gs->buffer = NULL; + } if (!buffer) goto out; @@ -3049,15 +3060,11 @@ gl_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) return; out: + assert(!gs->buffer); weston_buffer_reference(&gs->buffer_ref, NULL, BUFFER_WILL_NOT_BE_ACCESSED); weston_buffer_release_reference(&gs->buffer_release_ref, NULL); - for (i = 0; i < gb->num_images; i++) { - egl_image_unref(gb->images[i]); - gb->images[i] = NULL; - } - gb->num_images = 0; glDeleteTextures(gs->num_textures, gs->textures); gs->num_textures = 0; } @@ -3112,7 +3119,7 @@ gl_renderer_surface_copy_content(struct weston_surface *surface, const GLenum gl_format = GL_RGBA; /* PIXMAN_a8b8g8r8 little-endian */ struct gl_renderer *gr = get_renderer(surface->compositor); struct gl_surface_state *gs = get_surface_state(surface); - struct gl_buffer_state *gb = &gs->buffer; + struct gl_buffer_state *gb = gs->buffer; struct weston_buffer *buffer = gs->buffer_ref.buffer; int cw, ch; GLuint fbo; @@ -3197,9 +3204,6 @@ out: static void surface_state_destroy(struct gl_surface_state *gs, struct gl_renderer *gr) { - struct gl_buffer_state *gb = &gs->buffer; - int i; - wl_list_remove(&gs->surface_destroy_listener.link); wl_list_remove(&gs->renderer_destroy_listener.link); @@ -3207,13 +3211,14 @@ surface_state_destroy(struct gl_surface_state *gs, struct gl_renderer *gr) glDeleteTextures(gs->num_textures, gs->textures); - for (i = 0; i < gb->num_images; i++) - egl_image_unref(gb->images[i]); + if (gs->buffer && gs->buffer_ref.buffer->type == WESTON_BUFFER_SHM) + destroy_buffer_state(gs->buffer); + gs->buffer = NULL; weston_buffer_reference(&gs->buffer_ref, NULL, BUFFER_WILL_NOT_BE_ACCESSED); weston_buffer_release_reference(&gs->buffer_release_ref, NULL); - pixman_region32_fini(&gb->texture_damage); + free(gs); } @@ -3249,13 +3254,11 @@ static int gl_renderer_create_surface(struct weston_surface *surface) { struct gl_surface_state *gs; - struct gl_buffer_state *gb; struct gl_renderer *gr = get_renderer(surface->compositor); gs = zalloc(sizeof *gs); if (gs == NULL) return -1; - gb = &gs->buffer; /* A buffer is never attached to solid color surfaces, yet * they still go through texcoord computations. Do not divide @@ -3263,7 +3266,6 @@ gl_renderer_create_surface(struct weston_surface *surface) */ gs->surface = surface; - pixman_region32_init(&gb->texture_damage); surface->renderer_state = gs; gs->surface_destroy_listener.notify = From 62c0f1621c744bd6bff21feaadf89b9b5cbb8616 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 20 Jan 2022 18:23:26 +0000 Subject: [PATCH 255/609] gl-renderer: Delete egl_image wrapper Now that EGLImages are strongly associated with a gl_buffer_state, which has a lifetime strictly bounded by a weston_buffer, we don't need to have an egl_image wrapper having its own separate refcounting anymore. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 103 ++++++++-------------------- 1 file changed, 27 insertions(+), 76 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 131e6cea..1c71c6d8 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -108,12 +108,6 @@ struct gl_output_state { struct gl_renderer; -struct egl_image { - struct gl_renderer *renderer; - EGLImageKHR image; - int refcount; -}; - struct dmabuf_format { uint32_t format; struct wl_list link; @@ -146,6 +140,8 @@ struct yuv_format_descriptor { }; struct gl_buffer_state { + struct gl_renderer *gr; + GLfloat color[4]; bool needs_full_upload; @@ -156,7 +152,7 @@ struct gl_buffer_state { GLenum gl_format[3]; GLenum gl_pixel_type; - struct egl_image* images[3]; + EGLImageKHR images[3]; int num_images; enum gl_shader_texture_variant shader_variant; @@ -338,53 +334,6 @@ timeline_submit_render_sync(struct gl_renderer *gr, wl_list_insert(&go->timeline_render_point_list, &trp->link); } -static struct egl_image* -egl_image_create(struct gl_renderer *gr, EGLenum target, - EGLClientBuffer buffer, const EGLint *attribs) -{ - struct egl_image *img; - - img = zalloc(sizeof *img); - img->renderer = gr; - img->refcount = 1; - img->image = gr->create_image(gr->egl_display, EGL_NO_CONTEXT, - target, buffer, attribs); - - if (img->image == EGL_NO_IMAGE_KHR) { - free(img); - return NULL; - } - - return img; -} - -static int -egl_image_unref(struct egl_image *image) -{ - struct gl_renderer *gr; - - /* in multi-planar cases, egl_image_create() might fail on an - * intermediary step resulting in egl_image being NULL. In order to go - * over all successful ones, and avoid leaking one of them (the last - * one), we'll have to guard against it -- until we'll have a correct - * way of disposing of any previous created images. - */ - if (!image) - return 0; - - gr = image->renderer; - assert(image->refcount > 0); - - image->refcount--; - if (image->refcount > 0) - return image->refcount; - - gr->destroy_image(gr->egl_display, image->image); - free(image); - - return 0; -} - #define max(a, b) (((a) > (b)) ? (a) : (b)) #define min(a, b) (((a) > (b)) ? (b) : (a)) @@ -1902,7 +1851,7 @@ destroy_buffer_state(struct gl_buffer_state *gb) int i; for (i = 0; i < gb->num_images; i++) - egl_image_unref(gb->images[i]); + gb->gr->destroy_image(gb->gr->egl_display, gb->images[i]); pixman_region32_fini(&gb->texture_damage); wl_list_remove(&gb->destroy_listener.link); @@ -2142,6 +2091,7 @@ unsupported: gb = zalloc(sizeof(*gb)); if (!gb) return false; + gb->gr = gr; wl_list_init(&gb->destroy_listener.link); pixman_region32_init(&gb->texture_damage); @@ -2180,6 +2130,7 @@ gl_renderer_fill_buffer_info(struct weston_compositor *ec, if (!gb) return false; + gb->gr = gr; pixman_region32_init(&gb->texture_damage); buffer->legacy_buffer = (struct wl_buffer *)buffer->resource; @@ -2254,10 +2205,12 @@ gl_renderer_fill_buffer_info(struct weston_compositor *ec, EGL_NONE }; - gb->images[i] = egl_image_create(gr, EGL_WAYLAND_BUFFER_WL, + gb->images[i] = gr->create_image(gr->egl_display, + EGL_NO_CONTEXT, + EGL_WAYLAND_BUFFER_WL, buffer->legacy_buffer, attribs); - if (!gb->images[i]) { + if (gb->images[i] == EGL_NO_IMAGE_KHR) { weston_log("couldn't create EGLImage for plane %d\n", i); goto err_img; } @@ -2270,7 +2223,7 @@ gl_renderer_fill_buffer_info(struct weston_compositor *ec, err_img: while (--i >= 0) - egl_image_unref(gb->images[i]); + gr->destroy_image(gb->gr->egl_display, gb->images[i]); err_free: free(gb); return false; @@ -2295,7 +2248,7 @@ gl_renderer_attach_egl(struct weston_surface *es, struct weston_buffer *buffer) for (i = 0; i < gb->num_images; i++) { glActiveTexture(GL_TEXTURE0 + i); glBindTexture(target, gs->textures[i]); - gr->image_target_texture_2d(target, gb->images[i]->image); + gr->image_target_texture_2d(target, gb->images[i]); } return true; @@ -2311,11 +2264,10 @@ gl_renderer_destroy_dmabuf(struct linux_dmabuf_buffer *dmabuf) destroy_buffer_state(gb); } -static struct egl_image * +static EGLImageKHR import_simple_dmabuf(struct gl_renderer *gr, struct dmabuf_attributes *attributes) { - struct egl_image *image; EGLint attribs[52]; int atti = 0; bool has_modifier; @@ -2407,10 +2359,8 @@ import_simple_dmabuf(struct gl_renderer *gr, attribs[atti++] = EGL_NONE; - image = egl_image_create(gr, EGL_LINUX_DMA_BUF_EXT, NULL, - attribs); - - return image; + return gr->create_image(gr->egl_display, EGL_NO_CONTEXT, + EGL_LINUX_DMA_BUF_EXT, NULL, attribs); } struct yuv_format_descriptor yuv_formats[] = { @@ -2502,13 +2452,13 @@ struct yuv_format_descriptor yuv_formats[] = { } }; -static struct egl_image * +static EGLImageKHR import_dmabuf_single_plane(struct gl_renderer *gr, const struct dmabuf_attributes *attributes, struct yuv_plane_descriptor *descriptor) { struct dmabuf_attributes plane; - struct egl_image *image; + EGLImageKHR image; char fmt[4]; plane.width = attributes->width / descriptor->width_divisor; @@ -2521,7 +2471,7 @@ import_dmabuf_single_plane(struct gl_renderer *gr, plane.modifier[0] = attributes->modifier[descriptor->plane_index]; image = import_simple_dmabuf(gr, &plane); - if (!image) { + if (image == EGL_NO_IMAGE_KHR) { weston_log("Failed to import plane %d as %.4s\n", descriptor->plane_index, dump_format(descriptor->format, fmt)); @@ -2537,7 +2487,6 @@ import_yuv_dmabuf(struct gl_renderer *gr, struct gl_buffer_state *gb, { unsigned i; int j; - int ret; struct yuv_format_descriptor *format = NULL; char fmt[4]; @@ -2567,10 +2516,10 @@ import_yuv_dmabuf(struct gl_renderer *gr, struct gl_buffer_state *gb, for (j = 0; j < format->output_planes; ++j) { gb->images[j] = import_dmabuf_single_plane(gr, attributes, &format->plane[j]); - if (!gb->images[j]) { + if (gb->images[j] == EGL_NO_IMAGE_KHR) { while (--j >= 0) { - ret = egl_image_unref(gb->images[j]); - assert(ret == 0); + gr->destroy_image(gb->gr->egl_display, + gb->images[j]); gb->images[j] = NULL; } return false; @@ -2688,7 +2637,7 @@ static struct gl_buffer_state * import_dmabuf(struct gl_renderer *gr, struct linux_dmabuf_buffer *dmabuf) { - struct egl_image *egl_image; + EGLImageKHR egl_image; struct gl_buffer_state *gb = zalloc(sizeof(*gb)); if (!pixel_format_get_info(dmabuf->attributes.format)) @@ -2698,11 +2647,12 @@ import_dmabuf(struct gl_renderer *gr, if (!gb) return NULL; + gb->gr = gr; pixman_region32_init(&gb->texture_damage); wl_list_init(&gb->destroy_listener.link); egl_image = import_simple_dmabuf(gr, &dmabuf->attributes); - if (egl_image) { + if (egl_image != EGL_NO_IMAGE_KHR) { GLenum target = choose_texture_target(gr, &dmabuf->attributes); gb->num_images = 1; @@ -2902,7 +2852,7 @@ gl_renderer_attach_dmabuf(struct weston_surface *surface, for (i = 0; i < gb->num_images; ++i) { glActiveTexture(GL_TEXTURE0 + i); glBindTexture(target, gs->textures[i]); - gr->image_target_texture_2d(target, gb->images[i]->image); + gr->image_target_texture_2d(target, gb->images[i]); } return true; @@ -2977,6 +2927,7 @@ static bool gl_renderer_attach_solid(struct weston_surface *surface, struct weston_buffer *buffer) { + struct gl_renderer *gr = get_renderer(surface->compositor); struct gl_surface_state *gs = get_surface_state(surface); struct gl_buffer_state *gb = buffer->renderer_private; @@ -2986,8 +2937,8 @@ gl_renderer_attach_solid(struct weston_surface *surface, } gb = zalloc(sizeof(*gb)); + gb->gr = gr; pixman_region32_init(&gb->texture_damage); - buffer->renderer_private = gb; gb->destroy_listener.notify = handle_buffer_destroy; wl_signal_add(&buffer->destroy_signal, &gb->destroy_listener); From 48159366300b4f8fad9c442288b505424f49e5f2 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 20 Jan 2022 18:49:05 +0000 Subject: [PATCH 256/609] gl-renderer: Allocate textures per-buffer, not per-surface Now that the gl_buffer_state owns everything related to buffers, move the textures from there rather than living on the surface, to join the EGLImage and/or SHM params. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 62 ++++++++++++++--------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 1c71c6d8..4cfe431e 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -161,6 +161,9 @@ struct gl_buffer_state { int hsub[3]; /* horizontal subsampling per plane */ int vsub[3]; /* vertical subsampling per plane */ + GLuint textures[3]; + int num_textures; + struct wl_listener destroy_listener; }; @@ -178,9 +181,6 @@ struct gl_surface_state { Used only in the context of a gl_renderer_repaint_output call. */ bool used_in_output_repaint; - GLuint textures[3]; - int num_textures; - struct wl_listener surface_destroy_listener; struct wl_listener renderer_destroy_listener; }; @@ -900,9 +900,9 @@ gl_shader_config_set_input_textures(struct gl_shader_config *sconf, for (i = 0; i < 4; i++) sconf->unicolor[i] = gb->color[i]; - assert(gs->num_textures <= GL_SHADER_INPUT_TEX_MAX); - for (i = 0; i < gs->num_textures; i++) - sconf->input_tex[i] = gs->textures[i]; + assert(gb->num_textures <= GL_SHADER_INPUT_TEX_MAX); + for (i = 0; i < gb->num_textures; i++) + sconf->input_tex[i] = gb->textures[i]; for (; i < GL_SHADER_INPUT_TEX_MAX; i++) sconf->input_tex[i] = 0; } @@ -1791,8 +1791,8 @@ gl_renderer_flush_damage(struct weston_surface *surface, glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, 0); glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, 0); wl_shm_buffer_begin_access(buffer->shm_buffer); - for (j = 0; j < gs->num_textures; j++) { - glBindTexture(GL_TEXTURE_2D, gs->textures[j]); + for (j = 0; j < gb->num_textures; j++) { + glBindTexture(GL_TEXTURE_2D, gb->textures[j]); glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, gb->pitch / gb->hsub[j]); glTexImage2D(GL_TEXTURE_2D, 0, @@ -1815,8 +1815,8 @@ gl_renderer_flush_damage(struct weston_surface *surface, r = weston_surface_to_buffer_rect(surface, rectangles[i]); - for (j = 0; j < gs->num_textures; j++) { - glBindTexture(GL_TEXTURE_2D, gs->textures[j]); + for (j = 0; j < gb->num_textures; j++) { + glBindTexture(GL_TEXTURE_2D, gb->textures[j]); glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, gb->pitch / gb->hsub[j]); glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, @@ -1850,6 +1850,8 @@ destroy_buffer_state(struct gl_buffer_state *gb) { int i; + glDeleteTextures(gb->num_textures, gb->textures); + for (i = 0; i < gb->num_images; i++) gb->gr->destroy_image(gb->gr->egl_display, gb->images[i]); @@ -1873,26 +1875,21 @@ handle_buffer_destroy(struct wl_listener *listener, void *data) } static void -ensure_textures(struct gl_surface_state *gs, GLenum target, int num_textures) +ensure_textures(struct gl_buffer_state *gb, GLenum target, int num_textures) { int i; - if (num_textures <= gs->num_textures) { - glDeleteTextures(gs->num_textures - num_textures, - &gs->textures[num_textures]); - gs->num_textures = num_textures; - return; - } + assert(gb->num_textures == 0); glActiveTexture(GL_TEXTURE0); - for (i = gs->num_textures; i < num_textures; i++) { - glGenTextures(1, &gs->textures[i]); - glBindTexture(target, gs->textures[i]); + for (i = 0; i < num_textures; i++) { + glGenTextures(1, &gb->textures[i]); + glBindTexture(target, gb->textures[i]); glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } - gs->num_textures = num_textures; + gb->num_textures = num_textures; glBindTexture(target, 0); } @@ -2110,7 +2107,7 @@ unsupported: gs->buffer = gb; gs->surface = es; - ensure_textures(gs, GL_TEXTURE_2D, num_planes); + ensure_textures(gb, GL_TEXTURE_2D, num_planes); return true; } @@ -2123,6 +2120,7 @@ gl_renderer_fill_buffer_info(struct weston_compositor *ec, struct gl_buffer_state *gb = zalloc(sizeof(*gb)); EGLint format; uint32_t fourcc; + GLenum target; EGLint y_inverted; bool ret = true; int i; @@ -2216,6 +2214,9 @@ gl_renderer_fill_buffer_info(struct weston_compositor *ec, } } + target = gl_shader_texture_variant_get_target(gb->shader_variant); + ensure_textures(gb, target, gb->num_images); + buffer->renderer_private = gb; gb->destroy_listener.notify = handle_buffer_destroy; wl_signal_add(&buffer->destroy_signal, &gb->destroy_listener); @@ -2244,10 +2245,9 @@ gl_renderer_attach_egl(struct weston_surface *es, struct weston_buffer *buffer) gs->buffer = gb; target = gl_shader_texture_variant_get_target(gb->shader_variant); - ensure_textures(gs, target, gb->num_images); for (i = 0; i < gb->num_images; i++) { glActiveTexture(GL_TEXTURE0 + i); - glBindTexture(target, gs->textures[i]); + glBindTexture(target, gb->textures[i]); gr->image_target_texture_2d(target, gb->images[i]); } @@ -2488,6 +2488,7 @@ import_yuv_dmabuf(struct gl_renderer *gr, struct gl_buffer_state *gb, unsigned i; int j; struct yuv_format_descriptor *format = NULL; + GLenum target; char fmt[4]; for (i = 0; i < ARRAY_LENGTH(yuv_formats); ++i) { @@ -2545,6 +2546,9 @@ import_yuv_dmabuf(struct gl_renderer *gr, struct gl_buffer_state *gb, assert(false); } + target = gl_shader_texture_variant_get_target(gb->shader_variant); + ensure_textures(gb, target, gb->num_images); + return true; } @@ -2666,6 +2670,8 @@ import_dmabuf(struct gl_renderer *gr, gb->shader_variant = SHADER_VARIANT_EXTERNAL; } + ensure_textures(gb, target, gb->num_images); + return gb; } @@ -2848,10 +2854,9 @@ gl_renderer_attach_dmabuf(struct weston_surface *surface, gs->buffer = gb; target = gl_shader_texture_variant_get_target(gb->shader_variant); - ensure_textures(gs, target, gb->num_images); for (i = 0; i < gb->num_images; ++i) { glActiveTexture(GL_TEXTURE0 + i); - glBindTexture(target, gs->textures[i]); + glBindTexture(target, gb->textures[i]); gr->image_target_texture_2d(target, gb->images[i]); } @@ -3015,9 +3020,6 @@ out: weston_buffer_reference(&gs->buffer_ref, NULL, BUFFER_WILL_NOT_BE_ACCESSED); weston_buffer_release_reference(&gs->buffer_release_ref, NULL); - - glDeleteTextures(gs->num_textures, gs->textures); - gs->num_textures = 0; } static uint32_t @@ -3160,8 +3162,6 @@ surface_state_destroy(struct gl_surface_state *gs, struct gl_renderer *gr) gs->surface->renderer_state = NULL; - glDeleteTextures(gs->num_textures, gs->textures); - if (gs->buffer && gs->buffer_ref.buffer->type == WESTON_BUFFER_SHM) destroy_buffer_state(gs->buffer); gs->buffer = NULL; From 2327daf96b343ecf1ae5af1c4600b7acb7337a50 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Tue, 17 May 2022 17:49:35 +0300 Subject: [PATCH 257/609] desktop-shell: Handle weston_curtain destruction This fixes the following leaks for weston_curtain/weston_buffer_create_solid_rgba when shutting down the compositor: #0 0x7f9170372987 in __interceptor_calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:154 #1 0x7f915bfeb8b7 in zalloc ../include/libweston/zalloc.h:38 #2 0x7f915bfec71d in weston_curtain_create ../shell-utils/shell-utils.c:150 #3 0x7f915bfd9e51 in shell_ensure_fullscreen_black_view ../desktop-shell/shell.c:2082 #4 0x7f915bfda2a9 in shell_configure_fullscreen ../desktop-shell/shell.c:2118 #5 0x7f915bfdc72d in desktop_surface_committed ../desktop-shell/shell.c:2538 #6 0x7f915bfa3ef5 in weston_desktop_api_committed ../libweston-desktop/libweston-desktop.c:159 #7 0x7f915bfae778 in weston_desktop_xdg_toplevel_committed ../libweston-desktop/xdg-shell.c:746 #8 0x7f915bfb0d45 in weston_desktop_xdg_surface_committed ../libweston-desktop/xdg-shell.c:1374 #9 0x7f915bfa7382 in weston_desktop_surface_surface_committed ../libweston-desktop/surface.c:174 #10 0x7f916fe628a6 in wl_signal_emit /home/mvlad/install-amd64/include/wayland-server-core.h:481 #11 0x7f916fe7c0e2 in weston_surface_commit_state ../libweston/compositor.c:4062 #12 0x7f916fe7c161 in weston_surface_commit ../libweston/compositor.c:4068 #13 0x7f916fe7c6ef in surface_commit ../libweston/compositor.c:4146 #14 0x7f916fc847e9 (/usr/lib/x86_64-linux-gnu/libffi.so.8+0x77e9) #0 0x7f9170372987 in __interceptor_calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:154 #1 0x7f916fe62aa3 in zalloc ../include/libweston/zalloc.h:38 #2 0x7f916fe7398d in weston_buffer_create_solid_rgba ../libweston/compositor.c:2603 #3 0x7f915bfec879 in weston_curtain_create ../shell-utils/shell-utils.c:162 #4 0x7f915bfd9e51 in shell_ensure_fullscreen_black_view ../desktop-shell/shell.c:2082 #5 0x7f915bfda2a9 in shell_configure_fullscreen ../desktop-shell/shell.c:2118 #6 0x7f915bfdc72d in desktop_surface_committed ../desktop-shell/shell.c:2538 #7 0x7f915bfa3ef5 in weston_desktop_api_committed ../libweston-desktop/libweston-desktop.c:159 #8 0x7f915bfae778 in weston_desktop_xdg_toplevel_committed ../libweston-desktop/xdg-shell.c:746 #9 0x7f915bfb0d45 in weston_desktop_xdg_surface_committed ../libweston-desktop/xdg-shell.c:1374 #10 0x7f915bfa7382 in weston_desktop_surface_surface_committed ../libweston-desktop/surface.c:174 #11 0x7f916fe628a6 in wl_signal_emit /home/mvlad/install-amd64/include/wayland-server-core.h:481 #12 0x7f916fe7c0e2 in weston_surface_commit_state ../libweston/compositor.c:4062 #13 0x7f916fe7c161 in weston_surface_commit ../libweston/compositor.c:4068 #14 0x7f916fe7c6ef in surface_commit ../libweston/compositor.c:4146 #15 0x7f916fc847e9 (/usr/lib/x86_64-linux-gnu/libffi.so.8+0x77e9) We do not migrate the weston_curtain destruction from desktop_surface_removed() to desktop_shell_destroy_surface() because we'd want have the curtain removed before the animation starts. If we were to move the black view destruction, *and* only handle it from desktop_shell_destroy_surface() the animation runs but the black curtain will be removed right at the end, effectively diminishing the effect of the animations. To this end, we keep both of the two worlds, if the client terminates on its own, we keep the same animation effect, but if the compositor is shutting down we destroy it immediately. We remove wl_list_for_each_safe() and instead loop each time to avoid using a stale pointer iterator which could cause a UAF as the shsurf would be free'ed. Suggested-by: Pekka Paalanen Signed-off-by: Marius Vlad --- desktop-shell/shell.c | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 72b06463..8e3ec134 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -262,6 +262,9 @@ desktop_shell_destroy_surface(struct shell_surface *shsurf) { struct shell_surface *shsurf_child, *tmp; + if (shsurf->fullscreen.black_view) + weston_curtain_destroy(shsurf->fullscreen.black_view); + wl_list_for_each_safe(shsurf_child, tmp, &shsurf->children_list, children_link) { wl_list_remove(&shsurf_child->children_link); wl_list_init(&shsurf_child->children_link); @@ -2340,8 +2343,10 @@ desktop_surface_removed(struct weston_desktop_surface *desktop_surface, shseat->focused_surface = NULL; } - if (shsurf->fullscreen.black_view) + if (shsurf->fullscreen.black_view) { weston_curtain_destroy(shsurf->fullscreen.black_view); + shsurf->fullscreen.black_view = NULL; + } weston_surface_set_label_func(surface, NULL); weston_desktop_surface_set_user_data(shsurf->desktop_surface, NULL); @@ -4866,11 +4871,11 @@ setup_output_destroy_handler(struct weston_compositor *ec, static void desktop_shell_destroy_layer(struct weston_layer *layer) { - struct weston_view *view, *view_next; + struct weston_view *view; + bool removed; - wl_list_for_each_safe(view, view_next, &layer->view_list.link, layer_link.link) { - struct shell_surface *shsurf = - get_shell_surface(view->surface); + do { + removed = false; /* fullscreen_layer is special as it would have a view with an * additional black_view created and added to its layer_link @@ -4886,12 +4891,22 @@ desktop_shell_destroy_layer(struct weston_layer *layer) * could create additional views, which are managed implicitly, * but which are still being added to the layer list. * + * We avoid using wl_list_for_each_safe() as it can't handle + * removal of the next item in the list, so with this approach + * we restart the loop as long as we keep removing views from + * the list. */ - if (shsurf) - desktop_shell_destroy_surface(shsurf); - else if (is_black_surface_view(view, NULL)) - weston_surface_unref(view->surface); - } + wl_list_for_each(view, &layer->view_list.link, layer_link.link) { + struct shell_surface *shsurf = + get_shell_surface(view->surface); + if (shsurf) { + desktop_shell_destroy_surface(shsurf); + removed = true; + break; + } + } + + } while (removed); weston_layer_fini(layer); } From ccb0d4f7ce0a503477ac23be0d33f3025b3375cb Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 17 May 2022 14:55:27 +0300 Subject: [PATCH 258/609] gl-renderer: pass gr to gl_renderer_log_extensions() Plumb struct gl_renderer all the way through to gl_renderer_log_extensions(). In the future, the extension lists will be printed into a debug scope specifically, and it will get the debug scope from gr. Signed-off-by: Pekka Paalanen --- libweston/renderer-gl/egl-glue.c | 5 ++--- libweston/renderer-gl/gl-renderer-internal.h | 3 ++- libweston/renderer-gl/gl-renderer.c | 15 ++++++++------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/libweston/renderer-gl/egl-glue.c b/libweston/renderer-gl/egl-glue.c index 013172af..c9e8172a 100644 --- a/libweston/renderer-gl/egl-glue.c +++ b/libweston/renderer-gl/egl-glue.c @@ -477,7 +477,7 @@ gl_renderer_set_egl_device(struct gl_renderer *gr) return; } - gl_renderer_log_extensions("EGL device extensions", extensions); + gl_renderer_log_extensions(gr, "EGL device extensions", extensions); /* Try to query the render node using EGL_DRM_RENDER_NODE_FILE_EXT */ if (weston_check_egl_extension(extensions, "EGL_EXT_device_drm_render_node")) @@ -573,8 +573,7 @@ gl_renderer_setup_egl_client_extensions(struct gl_renderer *gr) return 0; } - gl_renderer_log_extensions("EGL client extensions", - extensions); + gl_renderer_log_extensions(gr, "EGL client extensions", extensions); if (weston_check_egl_extension(extensions, "EGL_EXT_device_query")) { gr->query_display_attrib = diff --git a/libweston/renderer-gl/gl-renderer-internal.h b/libweston/renderer-gl/gl-renderer-internal.h index b8230d8f..db541921 100644 --- a/libweston/renderer-gl/gl-renderer-internal.h +++ b/libweston/renderer-gl/gl-renderer-internal.h @@ -213,7 +213,8 @@ void gl_renderer_print_egl_error_state(void); void -gl_renderer_log_extensions(const char *name, const char *extensions); +gl_renderer_log_extensions(struct gl_renderer *gr, + const char *name, const char *extensions); void log_egl_config_info(EGLDisplay egldpy, EGLConfig eglconfig); diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 4cfe431e..fd7b28fb 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -3241,7 +3241,8 @@ gl_renderer_create_surface(struct weston_surface *surface) } void -gl_renderer_log_extensions(const char *name, const char *extensions) +gl_renderer_log_extensions(struct gl_renderer *gr, + const char *name, const char *extensions) { const char *p, *end; int l; @@ -3264,7 +3265,7 @@ gl_renderer_log_extensions(const char *name, const char *extensions) } static void -log_egl_info(EGLDisplay egldpy) +log_egl_info(struct gl_renderer *gr, EGLDisplay egldpy) { const char *str; @@ -3278,11 +3279,11 @@ log_egl_info(EGLDisplay egldpy) weston_log("EGL client APIs: %s\n", str ? str : "(null)"); str = eglQueryString(egldpy, EGL_EXTENSIONS); - gl_renderer_log_extensions("EGL extensions", str ? str : "(null)"); + gl_renderer_log_extensions(gr, "EGL extensions", str ? str : "(null)"); } static void -log_gl_info(void) +log_gl_info(struct gl_renderer *gr) { const char *str; @@ -3299,7 +3300,7 @@ log_gl_info(void) weston_log("GL renderer: %s\n", str ? str : "(null)"); str = (char *)glGetString(GL_EXTENSIONS); - gl_renderer_log_extensions("GL extensions", str ? str : "(null)"); + gl_renderer_log_extensions(gr, "GL extensions", str ? str : "(null)"); } static void @@ -3686,7 +3687,7 @@ gl_renderer_display_create(struct weston_compositor *ec, weston_drm_format_array_init(&gr->supported_formats); - log_egl_info(gr->egl_display); + log_egl_info(gr, gr->egl_display); ec->renderer = &gr->base; @@ -3919,7 +3920,7 @@ gl_renderer_setup(struct weston_compositor *ec, EGLSurface egl_surface) } gr->gl_version = get_gl_version(); - log_gl_info(); + log_gl_info(gr); gr->image_target_texture_2d = (void *) eglGetProcAddress("glEGLImageTargetTexture2DOES"); From 092115786e5b735a62c5aff95b4c8e815cf4defd Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 17 May 2022 15:15:47 +0300 Subject: [PATCH 259/609] gl-renderer: move extension lists away from log Print all EGL and OpenGL extension lists into a new log scope "gl-renderer" instead of the usual log. These lists cluttered the log while they were very rarely actually useful. Sometimes they might be interesting, so make them still available through the new log scope. Signed-off-by: Pekka Paalanen --- libweston/renderer-gl/gl-renderer-internal.h | 1 + libweston/renderer-gl/gl-renderer.c | 26 ++++++++++++++------ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer-internal.h b/libweston/renderer-gl/gl-renderer-internal.h index db541921..d84d9d37 100644 --- a/libweston/renderer-gl/gl-renderer-internal.h +++ b/libweston/renderer-gl/gl-renderer-internal.h @@ -115,6 +115,7 @@ struct gl_shader_config { struct gl_renderer { struct weston_renderer base; struct weston_compositor *compositor; + struct weston_log_scope *renderer_scope; bool fragment_shader_debug; bool fan_debug; diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index fd7b28fb..843da8f1 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -3248,20 +3248,25 @@ gl_renderer_log_extensions(struct gl_renderer *gr, int l; int len; - l = weston_log("%s:", name); + if (!weston_log_scope_is_enabled(gr->renderer_scope)) + return; + + l = weston_log_scope_printf(gr->renderer_scope, "%s:", name); p = extensions; while (*p) { end = strchrnul(p, ' '); len = end - p; - if (l + len > 78) - l = weston_log_continue("\n" STAMP_SPACE "%.*s", - len, p); - else - l += weston_log_continue(" %.*s", len, p); + if (l + len > 78) { + l = weston_log_scope_printf(gr->renderer_scope, + "\n %.*s", len, p); + } else { + l += weston_log_scope_printf(gr->renderer_scope, + " %.*s", len, p); + } for (p = end; isspace(*p); p++) ; } - weston_log_continue("\n"); + weston_log_scope_printf(gr->renderer_scope, "\n"); } static void @@ -3585,6 +3590,7 @@ gl_renderer_destroy(struct weston_compositor *ec) weston_binding_destroy(gr->fan_binding); weston_log_scope_destroy(gr->shader_scope); + weston_log_scope_destroy(gr->renderer_scope); free(gr); } @@ -3667,6 +3673,11 @@ gl_renderer_display_create(struct weston_compositor *ec, wl_list_init(&gr->shader_list); gr->platform = options->egl_platform; + gr->renderer_scope = weston_compositor_add_log_scope(ec, "gl-renderer", + "GL-renderer verbose messages\n", NULL, NULL, gr); + if (!gr->renderer_scope) + goto fail; + gr->shader_scope = gl_shader_scope_create(gr); if (!gr->shader_scope) goto fail; @@ -3798,6 +3809,7 @@ fail_terminate: eglTerminate(gr->egl_display); fail: weston_log_scope_destroy(gr->shader_scope); + weston_log_scope_destroy(gr->renderer_scope); free(gr); ec->renderer = NULL; return -1; From b383f52d3169920bd71b6b2855032aca17556c15 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 17 May 2022 15:46:03 +0300 Subject: [PATCH 260/609] gl_renderer: print more GL ES feature flags This is a human readable replacement for printing out the list of all available GL extensions that doesn't happen anymore by default. Signed-off-by: Pekka Paalanen --- libweston/renderer-gl/gl-renderer.c | 15 +++++++++++++-- shared/string-helpers.h | 6 ++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 843da8f1..f0dc73ee 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -57,6 +57,7 @@ #include "shared/fd-util.h" #include "shared/helpers.h" #include "shared/platform.h" +#include "shared/string-helpers.h" #include "shared/timespec-util.h" #include "shared/weston-drm-fourcc.h" #include "shared/weston-egl-ext.h" @@ -4000,10 +4001,20 @@ gl_renderer_setup(struct weston_compositor *ec, EGLSurface egl_surface) weston_log("GL ES %d.%d - renderer features:\n", gr_gl_version_major(gr->gl_version), gr_gl_version_minor(gr->gl_version)); - weston_log_continue(STAMP_SPACE "read-back format: %s\n", - ec->read_format == PIXMAN_a8r8g8b8 ? "BGRA" : "RGBA"); weston_log_continue(STAMP_SPACE "EGL Wayland extension: %s\n", gr->has_bind_display ? "yes" : "no"); + weston_log_continue(STAMP_SPACE "read-back format: %s\n", + ec->read_format == PIXMAN_a8r8g8b8 ? "BGRA" : "RGBA"); + weston_log_continue(STAMP_SPACE "wl_shm 10 bpc formats: %s\n", + yesno(gr->has_texture_type_2_10_10_10_rev)); + weston_log_continue(STAMP_SPACE "wl_shm 16 bpc formats: %s\n", + yesno(gr->has_texture_norm16)); + weston_log_continue(STAMP_SPACE "wl_shm half-float formats: %s\n", + yesno(gr->gl_supports_color_transforms)); + weston_log_continue(STAMP_SPACE "internal R and RG formats: %s\n", + yesno(gr->has_gl_texture_rg)); + weston_log_continue(STAMP_SPACE "OES_EGL_image_external: %s\n", + yesno(gr->has_egl_image_external)); return 0; } diff --git a/shared/string-helpers.h b/shared/string-helpers.h index e7174b21..302cfa81 100644 --- a/shared/string-helpers.h +++ b/shared/string-helpers.h @@ -95,4 +95,10 @@ str_printf(char **str_out, const char *fmt, ...) *str_out = NULL; } +static inline const char * +yesno(bool cond) +{ + return cond ? "yes" : "no"; +} + #endif /* WESTON_STRING_HELPERS_H */ From f3bf7a0d5a654cb577f5dfc36d60af9372f0b2e9 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 17 May 2022 16:01:37 +0300 Subject: [PATCH 261/609] gl-renderer: add error messages for missing EGL platforms Found by inspection, looks like these cases could use an explicit error message. Signed-off-by: Pekka Paalanen --- libweston/renderer-gl/egl-glue.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libweston/renderer-gl/egl-glue.c b/libweston/renderer-gl/egl-glue.c index c9e8172a..d62629f0 100644 --- a/libweston/renderer-gl/egl-glue.c +++ b/libweston/renderer-gl/egl-glue.c @@ -593,8 +593,10 @@ gl_renderer_setup_egl_client_extensions(struct gl_renderer *gr) weston_log("warning: EGL_EXT_platform_base not supported.\n"); /* Surfaceless is unusable without platform_base extension */ - if (gr->platform == EGL_PLATFORM_SURFACELESS_MESA) + if (gr->platform == EGL_PLATFORM_SURFACELESS_MESA) { + weston_log("Error: EGL surfaceless platform cannot be used.\n"); return -1; + } return 0; } @@ -614,6 +616,7 @@ gl_renderer_setup_egl_client_extensions(struct gl_renderer *gr) /* at this point we definitely have some platform extensions but * haven't found the supplied platform, so chances are it's * not supported. */ + weston_log("Error: EGL does not support %s platform.\n", extension_suffix); return -1; } From 2f115047dedab03744f0452d53eda4e5ae9ea295 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 17 May 2022 16:06:22 +0300 Subject: [PATCH 262/609] gl-renderer: log rendering device Feels like this might be nice to log. The failure case is not fatal, so say it's a warning only. Signed-off-by: Pekka Paalanen --- libweston/renderer-gl/egl-glue.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libweston/renderer-gl/egl-glue.c b/libweston/renderer-gl/egl-glue.c index d62629f0..1e8f9a43 100644 --- a/libweston/renderer-gl/egl-glue.c +++ b/libweston/renderer-gl/egl-glue.c @@ -490,8 +490,10 @@ gl_renderer_set_egl_device(struct gl_renderer *gr) gr->drm_device = gr->query_device_string(gr->egl_device, EGL_DRM_DEVICE_FILE_EXT); - if (!gr->drm_device) - weston_log("failed to query DRM device from EGL\n"); + if (gr->drm_device) + weston_log("Using rendering device: %s\n", gr->drm_device); + else + weston_log("warning: failed to query rendering device from EGL\n"); } int From e884e7c7b86a294afd09c25377f0a809c6bc7a42 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Wed, 18 May 2022 13:33:14 +0300 Subject: [PATCH 263/609] gl_renderer: log EGL features Log EGL features similar to how GL ES features are logged: listing just the ones weston tests for. This replaces some log messages from gl-renderer.c that become redundant or belong with EGL better. has_native_fence_sync and has_wait_sync are not logged, because missing them already logs warnings. Signed-off-by: Pekka Paalanen --- libweston/renderer-gl/egl-glue.c | 21 +++++++++++++++++++++ libweston/renderer-gl/gl-renderer.c | 6 ------ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/libweston/renderer-gl/egl-glue.c b/libweston/renderer-gl/egl-glue.c index 1e8f9a43..8a0381c4 100644 --- a/libweston/renderer-gl/egl-glue.c +++ b/libweston/renderer-gl/egl-glue.c @@ -31,6 +31,7 @@ #include "shared/helpers.h" #include "shared/platform.h" +#include "shared/string-helpers.h" #include "gl-renderer.h" #include "gl-renderer-internal.h" @@ -743,5 +744,25 @@ gl_renderer_setup_egl_extensions(struct weston_compositor *ec) "to missing EGL_KHR_wait_sync extension\n"); } + weston_log("EGL features:\n"); + weston_log_continue(STAMP_SPACE "EGL Wayland extension: %s\n", + yesno(gr->has_bind_display)); + weston_log_continue(STAMP_SPACE "context priority: %s\n", + yesno(gr->has_context_priority)); + weston_log_continue(STAMP_SPACE "buffer age: %s\n", + yesno(gr->has_egl_buffer_age)); + weston_log_continue(STAMP_SPACE "partial update: %s\n", + yesno(gr->has_egl_partial_update)); + weston_log_continue(STAMP_SPACE "swap buffers with damage: %s\n", + yesno(gr->swap_buffers_with_damage)); + weston_log_continue(STAMP_SPACE "configless context: %s\n", + yesno(gr->has_configless_context)); + weston_log_continue(STAMP_SPACE "surfaceless context: %s\n", + yesno(gr->has_surfaceless_context)); + weston_log_continue(STAMP_SPACE "dmabuf support: %s\n", + gr->has_dmabuf_import ? + (gr->has_dmabuf_import_modifiers ? "modifiers" : "legacy") : + "no"); + return 0; } diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index f0dc73ee..34fde3a3 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -3750,12 +3750,8 @@ gl_renderer_display_create(struct weston_compositor *ec, wl_list_init(&gr->dmabuf_formats); if (gr->has_surfaceless_context) { - weston_log("EGL_KHR_surfaceless_context available\n"); gr->dummy_surface = EGL_NO_SURFACE; } else { - weston_log("EGL_KHR_surfaceless_context unavailable. " - "Trying PbufferSurface\n"); - if (gl_renderer_create_pbuffer_surface(gr) < 0) goto fail_with_error; } @@ -4001,8 +3997,6 @@ gl_renderer_setup(struct weston_compositor *ec, EGLSurface egl_surface) weston_log("GL ES %d.%d - renderer features:\n", gr_gl_version_major(gr->gl_version), gr_gl_version_minor(gr->gl_version)); - weston_log_continue(STAMP_SPACE "EGL Wayland extension: %s\n", - gr->has_bind_display ? "yes" : "no"); weston_log_continue(STAMP_SPACE "read-back format: %s\n", ec->read_format == PIXMAN_a8r8g8b8 ? "BGRA" : "RGBA"); weston_log_continue(STAMP_SPACE "wl_shm 10 bpc formats: %s\n", From 3bdc29b9342bd0884c338ad51e317214e405c945 Mon Sep 17 00:00:00 2001 From: Hideyuki Nagase Date: Fri, 29 Apr 2022 07:22:41 -0500 Subject: [PATCH 264/609] rdp: Add cross thread work queues FreeRDP has some features that start new threads and run callback functions in them. We need a way to punt work from these threads back to the compositor thread. Co-authored-by: Steve Pronovost Co-authored-by: Brenton DeGeer Signed-off-by: Hideyuki Nagase Signed-off-by: Steve Pronovost Signed-off-by: Brenton DeGeer --- libweston/backend-rdp/meson.build | 4 + libweston/backend-rdp/rdp.c | 12 +++ libweston/backend-rdp/rdp.h | 50 ++++++++++ libweston/backend-rdp/rdputil.c | 151 ++++++++++++++++++++++++++++++ meson_options.txt | 6 ++ 5 files changed, 223 insertions(+) diff --git a/libweston/backend-rdp/meson.build b/libweston/backend-rdp/meson.build index d080391d..ac19a4cd 100644 --- a/libweston/backend-rdp/meson.build +++ b/libweston/backend-rdp/meson.build @@ -4,6 +4,10 @@ endif config_h.set('BUILD_RDP_COMPOSITOR', '1') +if get_option('rdp-thread-check') + config_h.set('ENABLE_RDP_THREAD_CHECK', '1') +endif + dep_frdp = dependency('freerdp2', version: '>= 2.2.0', required: false) if not dep_frdp.found() error('RDP-backend requires freerdp >= 2.2.0 which was not found. Or, you can use \'-Dbackend-rdp=false\'.') diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index 8b69ae48..6d2b63f3 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "rdp.h" @@ -621,6 +622,10 @@ rdp_peer_context_new(freerdp_peer* client, RdpPeerContext* context) context->item.peer = client; context->item.flags = RDP_PEER_OUTPUT_ENABLED; + context->loop_task_event_source_fd = -1; + context->loop_task_event_source = NULL; + wl_list_init(&context->loop_task_list); + context->rfx_context = rfx_context_new(TRUE); if (!context->rfx_context) return FALSE; @@ -661,6 +666,8 @@ rdp_peer_context_free(freerdp_peer* client, RdpPeerContext* context) wl_event_source_remove(context->events[i]); } + rdp_destroy_dispatch_task_event_source(context); + if (context->item.flags & RDP_PEER_ACTIVATED) { weston_seat_release_keyboard(context->item.seat); weston_seat_release_pointer(context->item.seat); @@ -1459,6 +1466,10 @@ rdp_peer_init(freerdp_peer *client, struct rdp_backend *b) peerCtx->events[i] = 0; wl_list_insert(&b->output->peers, &peerCtx->item.link); + + if (!rdp_initialize_dispatch_task_event_source(peerCtx)) + goto error_initialize; + return 0; error_initialize: @@ -1498,6 +1509,7 @@ rdp_backend_create(struct weston_compositor *compositor, if (b == NULL) return NULL; + b->compositor_tid = gettid(); b->compositor = compositor; b->base.destroy = rdp_destroy; b->base.create_output = rdp_output_create; diff --git a/libweston/backend-rdp/rdp.h b/libweston/backend-rdp/rdp.h index 0ead5eb9..1e4e51eb 100644 --- a/libweston/backend-rdp/rdp.h +++ b/libweston/backend-rdp/rdp.h @@ -86,6 +86,7 @@ struct rdp_backend { bool remotefx_codec; int external_listener_fd; int rdp_monitor_refresh_rate; + pid_t compositor_tid; }; enum peer_item_flags { @@ -132,9 +133,32 @@ struct rdp_peer_context { int horizontalAccumWheelRotationPrecise; int horizontalAccumWheelRotationDiscrete; + /* list of outstanding event_source sent from FreeRDP thread to display loop.*/ + int loop_task_event_source_fd; + struct wl_event_source *loop_task_event_source; + pthread_mutex_t loop_task_list_mutex; + struct wl_list loop_task_list; /* struct rdp_loop_task::link */ + }; + typedef struct rdp_peer_context RdpPeerContext; +typedef void (*rdp_loop_task_func_t)(bool freeOnly, void *data); + +struct rdp_loop_task { + struct wl_list link; + RdpPeerContext *peerCtx; + rdp_loop_task_func_t func; +}; + +#ifdef ENABLE_RDP_THREAD_CHECK +#define ASSERT_COMPOSITOR_THREAD(b) assert_compositor_thread(b) +#define ASSERT_NOT_COMPOSITOR_THREAD(b) assert_not_compositor_thread(b) +#else +#define ASSERT_COMPOSITOR_THREAD(b) (void)b +#define ASSERT_NOT_COMPOSITOR_THREAD(b) (void)b +#endif /* ENABLE_RDP_THREAD_CHECK */ + #define rdp_debug_verbose(b, ...) \ rdp_debug_print(b->verbose, false, __VA_ARGS__) #define rdp_debug_verbose_continue(b, ...) \ @@ -151,6 +175,32 @@ rdp_debug_print(struct weston_log_scope *log_scope, bool cont, char *fmt, ...); void convert_rdp_keyboard_to_xkb_rule_names(UINT32 KeyboardType, UINT32 KeyboardSubType, UINT32 KeyboardLayout, struct xkb_rule_names *xkbRuleNames); +#ifdef ENABLE_RDP_THREAD_CHECK +void +assert_compositor_thread(struct rdp_backend *b); + +void +assert_not_compositor_thread(struct rdp_backend *b); +#endif /* ENABLE_RDP_THREAD_CHECK */ + +bool +rdp_event_loop_add_fd(struct wl_event_loop *loop, + int fd, uint32_t mask, + wl_event_loop_fd_func_t func, + void *data, + struct wl_event_source **event_source); + +void +rdp_dispatch_task_to_display_loop(RdpPeerContext *peerCtx, + rdp_loop_task_func_t func, + struct rdp_loop_task *task); + +bool +rdp_initialize_dispatch_task_event_source(RdpPeerContext *peerCtx); + +void +rdp_destroy_dispatch_task_event_source(RdpPeerContext *peerCtx); + static inline struct rdp_head * to_rdp_head(struct weston_head *base) { diff --git a/libweston/backend-rdp/rdputil.c b/libweston/backend-rdp/rdputil.c index 6b9678f9..296dd7fb 100644 --- a/libweston/backend-rdp/rdputil.c +++ b/libweston/backend-rdp/rdputil.c @@ -75,3 +75,154 @@ void rdp_debug_print(struct weston_log_scope *log_scope, bool cont, char *fmt, . end: va_end(ap); } + +#ifdef ENABLE_RDP_THREAD_CHECK +void +assert_compositor_thread(struct rdp_backend *b) +{ + assert(b->compositor_tid == gettid()); +} + +void +assert_not_compositor_thread(struct rdp_backend *b) +{ + assert(b->compositor_tid != gettid()); +} +#endif /* ENABLE_RDP_THREAD_CHECK */ + +bool +rdp_event_loop_add_fd(struct wl_event_loop *loop, + int fd, uint32_t mask, + wl_event_loop_fd_func_t func, + void *data, struct wl_event_source **event_source) +{ + *event_source = wl_event_loop_add_fd(loop, fd, 0, func, data); + if (!*event_source) { + weston_log("%s: wl_event_loop_add_fd failed.\n", __func__); + return false; + } + + wl_event_source_fd_update(*event_source, mask); + return true; +} + +void +rdp_dispatch_task_to_display_loop(RdpPeerContext *peerCtx, + rdp_loop_task_func_t func, + struct rdp_loop_task *task) +{ + /* this function is ONLY used to queue the task from FreeRDP thread, + * and the task to be processed at wayland display loop thread. */ + ASSERT_NOT_COMPOSITOR_THREAD(peerCtx->rdpBackend); + + task->peerCtx = peerCtx; + task->func = func; + + pthread_mutex_lock(&peerCtx->loop_task_list_mutex); + /* this inserts at head */ + wl_list_insert(&peerCtx->loop_task_list, &task->link); + pthread_mutex_unlock(&peerCtx->loop_task_list_mutex); + + eventfd_write(peerCtx->loop_task_event_source_fd, 1); +} + +static int +rdp_dispatch_task(int fd, uint32_t mask, void *arg) +{ + RdpPeerContext *peerCtx = (RdpPeerContext *)arg; + struct rdp_loop_task *task, *tmp; + eventfd_t dummy; + + /* this must be called back at wayland display loop thread */ + ASSERT_COMPOSITOR_THREAD(peerCtx->rdpBackend); + + eventfd_read(peerCtx->loop_task_event_source_fd, &dummy); + + pthread_mutex_lock(&peerCtx->loop_task_list_mutex); + /* dequeue the first task which is at last, so use reverse. */ + assert(!wl_list_empty(&peerCtx->loop_task_list)); + wl_list_for_each_reverse_safe(task, tmp, &peerCtx->loop_task_list, link) { + wl_list_remove(&task->link); + break; + } + pthread_mutex_unlock(&peerCtx->loop_task_list_mutex); + + /* Dispatch and task will be freed by caller. */ + task->func(false, task); + + return 0; +} + +bool +rdp_initialize_dispatch_task_event_source(RdpPeerContext *peerCtx) +{ + struct rdp_backend *b = peerCtx->rdpBackend; + struct wl_event_loop *loop; + bool ret; + + if (pthread_mutex_init(&peerCtx->loop_task_list_mutex, NULL) == -1) { + weston_log("%s: pthread_mutex_init failed. %s\n", __func__, strerror(errno)); + goto error_mutex; + } + + assert(peerCtx->loop_task_event_source_fd == -1); + peerCtx->loop_task_event_source_fd = eventfd(0, EFD_SEMAPHORE | EFD_CLOEXEC); + if (peerCtx->loop_task_event_source_fd == -1) { + weston_log("%s: eventfd(EFD_SEMAPHORE) failed. %s\n", __func__, strerror(errno)); + goto error_event_source_fd; + } + + assert(wl_list_empty(&peerCtx->loop_task_list)); + + loop = wl_display_get_event_loop(b->compositor->wl_display); + assert(peerCtx->loop_task_event_source == NULL); + + ret = rdp_event_loop_add_fd(loop, + peerCtx->loop_task_event_source_fd, + WL_EVENT_READABLE, rdp_dispatch_task, + peerCtx, + &peerCtx->loop_task_event_source); + if (!ret) + goto error_event_loop_add_fd; + + return true; + +error_event_loop_add_fd: + close(peerCtx->loop_task_event_source_fd); + peerCtx->loop_task_event_source_fd = -1; + +error_event_source_fd: + pthread_mutex_destroy(&peerCtx->loop_task_list_mutex); + +error_mutex: + return false; +} + +void +rdp_destroy_dispatch_task_event_source(RdpPeerContext *peerCtx) +{ + struct rdp_loop_task *task, *tmp; + + /* This function must be called all virtual channel thread at FreeRDP is terminated, + * that ensures no more incoming tasks. */ + + if (peerCtx->loop_task_event_source) { + wl_event_source_remove(peerCtx->loop_task_event_source); + peerCtx->loop_task_event_source = NULL; + } + + wl_list_for_each_reverse_safe(task, tmp, &peerCtx->loop_task_list, link) { + wl_list_remove(&task->link); + /* inform caller task is not really scheduled prior to context destruction, + * inform them to clean them up. */ + task->func(true /* freeOnly */, task); + } + assert(wl_list_empty(&peerCtx->loop_task_list)); + + if (peerCtx->loop_task_event_source_fd != -1) { + close(peerCtx->loop_task_event_source_fd); + peerCtx->loop_task_event_source_fd = -1; + } + + pthread_mutex_destroy(&peerCtx->loop_task_list_mutex); +} diff --git a/meson_options.txt b/meson_options.txt index 90ab3832..d5e832d7 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -26,6 +26,12 @@ option( value: true, description: 'Weston backend: RDP remote screensharing' ) +option( + 'rdp-thread-check', + type: 'boolean', + value: false, + description: 'Aggressive thread sanity checks for the RDP backend' +) option( 'screenshare', type: 'boolean', From 252771d9aae0833d0354e92ec112c962aa459a47 Mon Sep 17 00:00:00 2001 From: Hideyuki Nagase Date: Tue, 10 May 2022 12:28:58 -0500 Subject: [PATCH 265/609] rdp: add virtual channel support RDP exposes certain features (audio, clipboard, RAIL) through a facility called "virtual channels". Set up the communications framework for using these. Co-authored-by: Steve Pronovost Co-authored-by: Brenton DeGeer Signed-off-by: Hideyuki Nagase Signed-off-by: Steve Pronovost Signed-off-by: Brenton DeGeer --- libweston/backend-rdp/meson.build | 6 ++++ libweston/backend-rdp/rdp.c | 47 +++++++++++++++++++++++++++---- libweston/backend-rdp/rdp.h | 5 +++- 3 files changed, 52 insertions(+), 6 deletions(-) diff --git a/libweston/backend-rdp/meson.build b/libweston/backend-rdp/meson.build index ac19a4cd..7da92469 100644 --- a/libweston/backend-rdp/meson.build +++ b/libweston/backend-rdp/meson.build @@ -13,6 +13,11 @@ if not dep_frdp.found() error('RDP-backend requires freerdp >= 2.2.0 which was not found. Or, you can use \'-Dbackend-rdp=false\'.') endif +dep_frdp_server = dependency('freerdp-server2', version: '>= 2.2.0', required: false) +if not dep_frdp_server.found() + error('RDP-backend requires freerdp-server2 >= 2.2.0 which was not found. Or, you can use \'-Dbackend-rdp=false\'.') +endif + dep_wpr = dependency('winpr2', version: '>= 2.2.0', required: false) if not dep_wpr.found() error('RDP-backend requires winpr >= 2.2.0 which was not found. Or, you can use \'-Dbackend-rdp=false\'.') @@ -21,6 +26,7 @@ endif deps_rdp = [ dep_libweston_private, dep_frdp, + dep_frdp_server, dep_wpr, ] srcs_rdp = [ diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index 6d2b63f3..3f104d25 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -51,6 +51,8 @@ #define KBD_HEBREW_STANDARD 0x2040D #endif +extern PWtsApiFunctionTable FreeRDP_InitWtsApi(void); + static void rdp_peer_refresh_rfx(pixman_region32_t *damage, pixman_image_t *image, freerdp_peer *peer) { @@ -656,16 +658,20 @@ out_error_stream: static void rdp_peer_context_free(freerdp_peer* client, RdpPeerContext* context) { - int i; + unsigned i; if (!context) return; wl_list_remove(&context->item.link); - for (i = 0; i < MAX_FREERDP_FDS; i++) { + + for (i = 0; i < ARRAY_LENGTH(context->events); i++) { if (context->events[i]) wl_event_source_remove(context->events[i]); } + if (context->vcm) + WTSCloseServer(context->vcm); + rdp_destroy_dispatch_task_event_source(context); if (context->item.flags & RDP_PEER_ACTIVATED) { @@ -686,11 +692,21 @@ static int rdp_client_activity(int fd, uint32_t mask, void *data) { freerdp_peer* client = (freerdp_peer *)data; + RdpPeerContext *peerCtx = (RdpPeerContext *)client->context; if (!client->CheckFileDescriptor(client)) { weston_log("unable to checkDescriptor for %p\n", client); goto out_clean; } + + if (peerCtx && peerCtx->vcm) + { + if (!WTSVirtualChannelManagerCheckFileDescriptor(peerCtx->vcm)) { + weston_log("failed to check FreeRDP WTS VC file descriptor for %p\n", client); + goto out_clean; + } + } + return 0; out_clean: @@ -1394,7 +1410,7 @@ static int rdp_peer_init(freerdp_peer *client, struct rdp_backend *b) { int rcount = 0; - void *rfds[MAX_FREERDP_FDS]; + void *rfds[MAX_FREERDP_FDS + 1]; /* +1 for WTSVirtualChannelManagerGetFileDescriptor. */ int i, fd; struct wl_event_loop *loop; rdpSettings *settings; @@ -1455,6 +1471,15 @@ rdp_peer_init(freerdp_peer *client, struct rdp_backend *b) goto error_initialize; } + PWtsApiFunctionTable fn = FreeRDP_InitWtsApi(); + WTSRegisterWtsApiFunctionTable(fn); + peerCtx->vcm = WTSOpenServerA((LPSTR)peerCtx); + if (peerCtx->vcm) { + WTSVirtualChannelManagerGetFileDescriptor(peerCtx->vcm, rfds, &rcount); + } else { + weston_log("WTSOpenServer is failed! continue without virtual channel.\n"); + } + loop = wl_display_get_event_loop(b->compositor->wl_display); for (i = 0; i < rcount; i++) { fd = (int)(long)(rfds[i]); @@ -1462,16 +1487,28 @@ rdp_peer_init(freerdp_peer *client, struct rdp_backend *b) peerCtx->events[i] = wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE, rdp_client_activity, client); } - for ( ; i < MAX_FREERDP_FDS; i++) + for ( ; i < (int)ARRAY_LENGTH(peerCtx->events); i++) peerCtx->events[i] = 0; wl_list_insert(&b->output->peers, &peerCtx->item.link); if (!rdp_initialize_dispatch_task_event_source(peerCtx)) - goto error_initialize; + goto error_dispatch_initialize; return 0; +error_dispatch_initialize: + for (i = 0; i < (int)ARRAY_LENGTH(peerCtx->events); i++) { + if (peerCtx->events[i]) { + wl_event_source_remove(peerCtx->events[i]); + peerCtx->events[i] = NULL; + } + } + if (peerCtx->vcm) { + WTSCloseServer(peerCtx->vcm); + peerCtx->vcm = NULL; + } + error_initialize: client->Close(client); return -1; diff --git a/libweston/backend-rdp/rdp.h b/libweston/backend-rdp/rdp.h index 1e4e51eb..ea28919f 100644 --- a/libweston/backend-rdp/rdp.h +++ b/libweston/backend-rdp/rdp.h @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -118,7 +119,7 @@ struct rdp_peer_context { rdpContext _p; struct rdp_backend *rdpBackend; - struct wl_event_source *events[MAX_FREERDP_FDS]; + struct wl_event_source *events[MAX_FREERDP_FDS + 1]; /* +1 for WTSVirtualChannelManagerGetFileDescriptor */ RFX_CONTEXT *rfx_context; wStream *encode_stream; RFX_RECT *rfx_rects; @@ -133,6 +134,8 @@ struct rdp_peer_context { int horizontalAccumWheelRotationPrecise; int horizontalAccumWheelRotationDiscrete; + HANDLE vcm; + /* list of outstanding event_source sent from FreeRDP thread to display loop.*/ int loop_task_event_source_fd; struct wl_event_source *loop_task_event_source; From 297ad403d6e4416c9fc969cce316832947dd8fd4 Mon Sep 17 00:00:00 2001 From: Hideyuki Nagase Date: Tue, 10 May 2022 13:16:55 -0500 Subject: [PATCH 266/609] rdp: Add clipboard redirection support Allow clipboard pasting in and out of an RDP session. Co-authored-by: Steve Pronovost Co-authored-by: Brenton DeGeer Signed-off-by: Hideyuki Nagase Signed-off-by: Steve Pronovost Signed-off-by: Brenton DeGeer --- libweston/backend-rdp/meson.build | 1 + libweston/backend-rdp/rdp.c | 44 + libweston/backend-rdp/rdp.h | 30 + libweston/backend-rdp/rdpclip.c | 1744 +++++++++++++++++++++++++++++ libweston/backend-rdp/rdputil.c | 36 + 5 files changed, 1855 insertions(+) create mode 100644 libweston/backend-rdp/rdpclip.c diff --git a/libweston/backend-rdp/meson.build b/libweston/backend-rdp/meson.build index 7da92469..d43eb651 100644 --- a/libweston/backend-rdp/meson.build +++ b/libweston/backend-rdp/meson.build @@ -31,6 +31,7 @@ deps_rdp = [ ] srcs_rdp = [ 'rdp.c', + 'rdpclip.c', 'rdputil.c', ] diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index 3f104d25..24e98c98 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -555,6 +555,16 @@ rdp_destroy(struct weston_compositor *ec) if (b->listener_events[i]) wl_event_source_remove(b->listener_events[i]); + if (b->clipboard_debug) { + weston_log_scope_destroy(b->clipboard_debug); + b->clipboard_debug = NULL; + } + + if (b->clipboard_verbose) { + weston_log_scope_destroy(b->clipboard_verbose); + b->clipboard_verbose = NULL; + } + if (b->debug) { weston_log_scope_destroy(b->debug); b->debug = NULL; @@ -669,6 +679,8 @@ rdp_peer_context_free(freerdp_peer* client, RdpPeerContext* context) wl_event_source_remove(context->events[i]); } + rdp_clipboard_destroy(context); + if (context->vcm) WTSCloseServer(context->vcm); @@ -924,6 +936,13 @@ xf_peer_activate(freerdp_peer* client) settings->CompressionEnabled = FALSE; } + if (settings->RedirectClipboard) { + if (!peerCtx->vcm) { + weston_log("Virtual channel is required for clipboard\n"); + goto error_exit; + } + } + if (output->base.width != (int)settings->DesktopWidth || output->base.height != (int)settings->DesktopHeight) { @@ -995,6 +1014,11 @@ xf_peer_activate(freerdp_peer* client) xkb_keymap_unref(keymap); weston_seat_init_pointer(peersItem->seat); + /* Initialize RDP clipboard after seat is initialized */ + if (settings->RedirectClipboard) + if (rdp_clipboard_init(client) != 0) + goto error_exit; + peersItem->flags |= RDP_PEER_ACTIVATED; /* disable pointer on the client side */ @@ -1014,6 +1038,12 @@ xf_peer_activate(freerdp_peer* client) pixman_region32_fini(&damage); return TRUE; + +error_exit: + + rdp_clipboard_destroy(peerCtx); + + return FALSE; } static BOOL @@ -1450,6 +1480,7 @@ rdp_peer_init(freerdp_peer *client, struct rdp_backend *b) settings->NSCodec = TRUE; settings->FrameMarkerCommandEnabled = TRUE; settings->SurfaceFrameMarkerEnabled = TRUE; + settings->RedirectClipboard = TRUE; settings->HasExtendedMouseEvent = TRUE; settings->HasHorizontalWheel = TRUE; @@ -1569,6 +1600,15 @@ rdp_backend_create(struct weston_compositor *compositor, b->rdp_monitor_refresh_rate = config->refresh_rate * 1000; rdp_debug(b, "RDP backend: WESTON_RDP_MONITOR_REFRESH_RATE: %d\n", b->rdp_monitor_refresh_rate); + b->clipboard_debug = weston_log_ctx_add_log_scope(b->compositor->weston_log_ctx, + "rdp-backend-clipboard", + "Debug messages from RDP backend clipboard\n", + NULL, NULL, NULL); + b->clipboard_verbose = weston_log_ctx_add_log_scope(b->compositor->weston_log_ctx, + "rdp-backend-clipboard-verbose", + "Debug messages from RDP backend clipboard\n", + NULL, NULL, NULL); + compositor->backend = &b->base; if (config->server_cert && config->server_key) { @@ -1660,6 +1700,10 @@ err_compositor: weston_compositor_shutdown(compositor); err_free_strings: + if (b->clipboard_debug) + weston_log_scope_destroy(b->clipboard_debug); + if (b->clipboard_verbose) + weston_log_scope_destroy(b->clipboard_verbose); if (b->debug) weston_log_scope_destroy(b->debug); if (b->verbose) diff --git a/libweston/backend-rdp/rdp.h b/libweston/backend-rdp/rdp.h index ea28919f..e2fc6abf 100644 --- a/libweston/backend-rdp/rdp.h +++ b/libweston/backend-rdp/rdp.h @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -78,6 +79,9 @@ struct rdp_backend { struct weston_log_scope *debug; struct weston_log_scope *verbose; + struct weston_log_scope *clipboard_debug; + struct weston_log_scope *clipboard_verbose; + char *server_cert; char *server_key; char *rdp_key; @@ -142,6 +146,13 @@ struct rdp_peer_context { pthread_mutex_t loop_task_list_mutex; struct wl_list loop_task_list; /* struct rdp_loop_task::link */ + /* Clipboard support */ + CliprdrServerContext *clipboard_server_context; + + struct rdp_clipboard_data_source *clipboard_client_data_source; + struct rdp_clipboard_data_source *clipboard_inflight_client_data_source; + + struct wl_listener clipboard_selection_listener; }; typedef struct rdp_peer_context RdpPeerContext; @@ -171,10 +182,22 @@ struct rdp_loop_task { #define rdp_debug_continue(b, ...) \ rdp_debug_print(b->debug, true, __VA_ARGS__) +#define rdp_debug_clipboard_verbose(b, ...) \ + rdp_debug_print(b->clipboard_verbose, false, __VA_ARGS__) +#define rdp_debug_clipboard_verbose_continue(b, ...) \ + rdp_debug_print(b->clipboard_verbose, true, __VA_ARGS__) +#define rdp_debug_clipboard(b, ...) \ + rdp_debug_print(b->clipboard_debug, false, __VA_ARGS__) +#define rdp_debug_clipboard_continue(b, ...) \ + rdp_debug_print(b->clipboard_debug, true, __VA_ARGS__) + /* rdputil.c */ void rdp_debug_print(struct weston_log_scope *log_scope, bool cont, char *fmt, ...); +int +rdp_wl_array_read_fd(struct wl_array *array, int fd); + void convert_rdp_keyboard_to_xkb_rule_names(UINT32 KeyboardType, UINT32 KeyboardSubType, UINT32 KeyboardLayout, struct xkb_rule_names *xkbRuleNames); @@ -204,6 +227,13 @@ rdp_initialize_dispatch_task_event_source(RdpPeerContext *peerCtx); void rdp_destroy_dispatch_task_event_source(RdpPeerContext *peerCtx); +/* rdpclip.c */ +int +rdp_clipboard_init(freerdp_peer *client); + +void +rdp_clipboard_destroy(RdpPeerContext *peerCtx); + static inline struct rdp_head * to_rdp_head(struct weston_head *base) { diff --git a/libweston/backend-rdp/rdpclip.c b/libweston/backend-rdp/rdpclip.c new file mode 100644 index 00000000..c8955e8c --- /dev/null +++ b/libweston/backend-rdp/rdpclip.c @@ -0,0 +1,1744 @@ +/* + * Copyright © 2020 Microsoft + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rdp.h" + +#include "libweston-internal.h" + +/* From MSDN, RegisterClipboardFormat API. + Registered clipboard formats are identified by values in the range 0xC000 through 0xFFFF. */ +#define CF_PRIVATE_RTF 49309 /* fake format ID for "Rich Text Format". */ +#define CF_PRIVATE_HTML 49405 /* fake format ID for "HTML Format".*/ + + /* 1 2 3 4 5 6 7 8 */ + /*01234567890 1 2345678901234 5 67890123456 7 89012345678901234567890 1 234567890123456789012 3 4*/ +static const char rdp_clipboard_html_header[] = "Version:0.9\r\nStartHTML:-1\r\nEndHTML:-1\r\nStartFragment:00000000\r\nEndFragment:00000000\r\n"; +#define RDP_CLIPBOARD_FRAGMENT_START_OFFSET (53) //---------------------------------------------------------+ | +#define RDP_CLIPBOARD_FRAGMENT_END_OFFSET (75) //-----------------------------------------------------------------------------------+ + +/* + * https://docs.microsoft.com/en-us/windows/win32/dataxchg/html-clipboard-format + * + * The fragment should be preceded and followed by the HTML comments and + * (no space allowed between the !-- and the text) to conveniently + * indicate where the fragment starts and ends. + */ +static const char rdp_clipboard_html_fragment_start[] = "\r\n"; +static const char rdp_clipboard_html_fragment_end[] = "\r\n"; + +struct rdp_clipboard_data_source; + +typedef bool (*pfn_process_data)(struct rdp_clipboard_data_source *source, bool is_send); + +struct rdp_clipboard_supported_format { + uint32_t format_id; + char *format_name; + char *mime_type; + pfn_process_data pfn; +}; + +static bool +clipboard_process_text_utf8(struct rdp_clipboard_data_source *source, bool is_send); + +static bool +clipboard_process_text_raw(struct rdp_clipboard_data_source *source, bool is_send); + +static bool +clipboard_process_bmp(struct rdp_clipboard_data_source *source , bool is_send); + +static bool +clipboard_process_html(struct rdp_clipboard_data_source *source, bool is_send); + +/* TODO: need to support to 1:n or m:n format conversion. + * For example, CF_UNICODETEXT to "UTF8_STRING" as well as "text/plain;charset=utf-8". + */ +struct rdp_clipboard_supported_format clipboard_supported_formats[] = { + { CF_UNICODETEXT, NULL, "text/plain;charset=utf-8", clipboard_process_text_utf8 }, + { CF_TEXT, NULL, "STRING", clipboard_process_text_raw }, + { CF_DIB, NULL, "image/bmp", clipboard_process_bmp }, + { CF_PRIVATE_RTF, "Rich Text Format", "text/rtf", clipboard_process_text_raw }, + { CF_PRIVATE_HTML, "HTML Format", "text/html", clipboard_process_html }, +}; +#define RDP_NUM_CLIPBOARD_FORMATS ARRAY_LENGTH(clipboard_supported_formats) + +enum rdp_clipboard_data_source_state { + RDP_CLIPBOARD_SOURCE_ALLOCATED = 0, + RDP_CLIPBOARD_SOURCE_FORMATLIST_READY, /* format list obtained from provider */ + RDP_CLIPBOARD_SOURCE_PUBLISHED, /* availablity of some or no clipboard data notified to consumer */ + RDP_CLIPBOARD_SOURCE_REQUEST_DATA, /* data request sent to provider */ + RDP_CLIPBOARD_SOURCE_RECEIVED_DATA, /* data was received from provider, waiting data to be dispatched to consumer */ + RDP_CLIPBOARD_SOURCE_TRANSFERING, /* transfering data to consumer */ + RDP_CLIPBOARD_SOURCE_TRANSFERRED, /* completed transfering data to consumer */ + RDP_CLIPBOARD_SOURCE_CANCEL_PENDING, /* data transfer cancel requested */ + RDP_CLIPBOARD_SOURCE_CANCELED, /* data transfer canceled */ + RDP_CLIPBOARD_SOURCE_RETRY, /* retry later */ + RDP_CLIPBOARD_SOURCE_FAILED, /* failure occured */ +}; + +struct rdp_clipboard_data_source { + struct weston_data_source base; + struct rdp_loop_task task_base; + struct wl_event_source *transfer_event_source; /* used for read/write with pipe */ + struct wl_array data_contents; + void *context; + int refcount; + int data_source_fd; + int format_index; + enum rdp_clipboard_data_source_state state; + uint32_t data_response_fail_count; + uint32_t inflight_write_count; + void *inflight_data_to_write; + size_t inflight_data_size; + bool is_data_processed; + void *processed_data_start; + uint32_t processed_data_size; + bool processed_data_is_send; + bool is_canceled; + uint32_t client_format_id_table[RDP_NUM_CLIPBOARD_FORMATS]; +}; + +struct rdp_clipboard_data_request { + struct rdp_loop_task task_base; + RdpPeerContext *ctx; + uint32_t requested_format_index; +}; + +static char * +clipboard_data_source_state_to_string(struct rdp_clipboard_data_source *source) +{ + if (!source) + return "null"; + + switch (source->state) { + case RDP_CLIPBOARD_SOURCE_ALLOCATED: + return "allocated"; + case RDP_CLIPBOARD_SOURCE_FORMATLIST_READY: + return "format list ready"; + case RDP_CLIPBOARD_SOURCE_PUBLISHED: + return "published"; + case RDP_CLIPBOARD_SOURCE_REQUEST_DATA: + return "request data"; + case RDP_CLIPBOARD_SOURCE_RECEIVED_DATA: + return "received data"; + case RDP_CLIPBOARD_SOURCE_TRANSFERING: + return "transferring"; + case RDP_CLIPBOARD_SOURCE_TRANSFERRED: + return "transferred"; + case RDP_CLIPBOARD_SOURCE_CANCEL_PENDING: + return "cancel pending"; + case RDP_CLIPBOARD_SOURCE_CANCELED: + return "canceled"; + case RDP_CLIPBOARD_SOURCE_RETRY: + return "retry"; + case RDP_CLIPBOARD_SOURCE_FAILED: + return "failed"; + } + assert(false); + return "unknown"; +} + +static bool +clipboard_process_text_utf8(struct rdp_clipboard_data_source *source, bool is_send) +{ + freerdp_peer *client = (freerdp_peer *)source->context; + RdpPeerContext *ctx = (RdpPeerContext *)client->context; + struct rdp_backend *b = ctx->rdpBackend; + struct wl_array data_contents; + + wl_array_init(&data_contents); + + assert(!source->is_data_processed); + + if (is_send) { + char *data = source->data_contents.data; + size_t data_size, data_size_in_char; + + /* Linux to Windows (convert utf-8 to UNICODE) */ + /* Include terminating NULL in size */ + assert((source->data_contents.size + 1) <= source->data_contents.alloc); + data[source->data_contents.size] = '\0'; + source->data_contents.size++; + + /* obtain size in UNICODE */ + data_size = MultiByteToWideChar(CP_UTF8, 0, + data, + source->data_contents.size, + NULL, 0); + if (data_size < 1) + goto error_return; + + data_size *= 2; // convert to size in bytes. + if (!wl_array_add(&data_contents, data_size)) + goto error_return; + + /* convert to UNICODE */ + data_size_in_char = MultiByteToWideChar(CP_UTF8, 0, + data, + source->data_contents.size, + data_contents.data, + data_size); + assert(data_contents.size == (data_size_in_char * 2)); + } else { + /* Windows to Linux (UNICODE to utf-8) */ + size_t data_size; + LPWSTR data = source->data_contents.data; + size_t data_size_in_char = source->data_contents.size / 2; + + /* Windows's data has trailing chars, which Linux doesn't expect. */ + while (data_size_in_char && + ((data[data_size_in_char-1] == L'\0') || (data[data_size_in_char-1] == L'\n'))) + data_size_in_char -= 1; + if (!data_size_in_char) + goto error_return; + + /* obtain size in utf-8 */ + data_size = WideCharToMultiByte(CP_UTF8, 0, + source->data_contents.data, + data_size_in_char, + NULL, 0, + NULL, NULL); + if (data_size < 1) + goto error_return; + + if (!wl_array_add(&data_contents, data_size)) + goto error_return; + + /* convert to utf-8 */ + data_size = WideCharToMultiByte(CP_UTF8, 0, + source->data_contents.data, + data_size_in_char, + data_contents.data, + data_size, + NULL, NULL); + assert(data_contents.size == data_size); + } + + /* swap the data_contents with new one */ + wl_array_release(&source->data_contents); + source->data_contents = data_contents; + source->is_data_processed = true; + source->processed_data_start = source->data_contents.data; + source->processed_data_size = source->data_contents.size; + rdp_debug_clipboard_verbose(b, "RDP %s (%p:%s): %s (%u bytes)\n", + __func__, source, clipboard_data_source_state_to_string(source), + is_send ? "send" : "receive", (uint32_t)source->data_contents.size); + + return true; + +error_return: + source->state = RDP_CLIPBOARD_SOURCE_FAILED; + weston_log("RDP %s FAILED (%p:%s): %s (%u bytes)\n", + __func__, source, clipboard_data_source_state_to_string(source), + is_send ? "send" : "receive", (uint32_t)source->data_contents.size); + + wl_array_release(&data_contents); + + return false; +} + +static bool +clipboard_process_text_raw(struct rdp_clipboard_data_source *source, bool is_send) +{ + freerdp_peer *client = (freerdp_peer *)source->context; + RdpPeerContext *ctx = (RdpPeerContext *)client->context; + struct rdp_backend *b = ctx->rdpBackend; + char *data = source->data_contents.data; + size_t data_size = source->data_contents.size; + + assert(!source->is_data_processed); + + if (is_send) { + /* Linux to Windows */ + /* Include terminating NULL in size */ + assert(data_size + 1 <= source->data_contents.alloc); + data[data_size] = '\0'; + source->data_contents.size++; + } else { + /* Windows to Linux */ + /* Windows's data has trailing chars, which Linux doesn't expect. */ + while (data_size && ((data[data_size-1] == '\0') || (data[data_size-1] == '\n'))) + data_size -= 1; + source->data_contents.size = data_size; + } + source->is_data_processed = true; + source->processed_data_start = source->data_contents.data; + source->processed_data_size = source->data_contents.size; + rdp_debug_clipboard_verbose(b, "RDP %s (%p): %s (%u bytes)\n", + __func__, source, is_send ? "send" : "receive", (uint32_t)source->data_contents.size); + + return true; +} + +/* based off sample code at https://docs.microsoft.com/en-us/troubleshoot/cpp/add-html-code-clipboard + But this missing a lot of corner cases, it must be rewritten with use of proper HTML parser */ +/* TODO: This doesn't work for converting HTML from Firefox in Wayland mode to Windows in certain cases, + because Firefox sends "...", thus + this needs to property strip meta header and convert to the Windows clipboard style HTML. */ +static bool +clipboard_process_html(struct rdp_clipboard_data_source *source, bool is_send) +{ + freerdp_peer *client = (freerdp_peer *)source->context; + RdpPeerContext *ctx = (RdpPeerContext *)client->context; + struct rdp_backend *b = ctx->rdpBackend; + struct wl_array data_contents; + char *cur = source->data_contents.data; + + assert(!source->is_data_processed); + + /* We're tresting the contents as a string for now, so null + * terminate it so strstr can't run off the end. However, we + * don't increase data_contents.size because we don't want + * to affect the content. */ + assert(source->data_contents.size + 1 <= source->data_contents.alloc); + ((char *)(source->data_contents.data))[source->data_contents.size] = '\0'; + + wl_array_init(&data_contents); + cur = strstr(cur, "data_contents.size - + (cur - (char *)source->data_contents.data); + + /* Windows's data has trailing chars, which Linux doesn't expect. */ + while (data_size && ((cur[data_size-1] == '\0') || (cur[data_size-1] == '\n'))) + data_size -= 1; + + if (!data_size) + goto error_return; + + if (!wl_array_add(&data_contents, data_size+1)) /* +1 for null */ + goto error_return; + + memcpy(data_contents.data, cur, data_size); + ((char *)(data_contents.data))[data_size] = '\0'; + data_contents.size = data_size; + } else { + /* Linux to Windows */ + char *last, *buf; + uint32_t fragment_start, fragment_end; + + if (!wl_array_add(&data_contents, source->data_contents.size+200)) + goto error_return; + + buf = data_contents.data; + strcpy(buf, rdp_clipboard_html_header); + last = cur; + cur = strstr(cur, "' */ + strncat(buf, last, cur-last); + last = cur; + fragment_start = strlen(buf); + strcat(buf, rdp_clipboard_html_fragment_start); + cur = strstr(cur, "data_contents); + source->data_contents = data_contents; + source->is_data_processed = true; + source->processed_data_start = source->data_contents.data; + source->processed_data_size = source->data_contents.size; + rdp_debug_clipboard_verbose(b, "RDP %s (%p:%s): %s (%u bytes)\n", + __func__, source, clipboard_data_source_state_to_string(source), + is_send ? "send" : "receive", (uint32_t)source->data_contents.size); + + return true; + +error_return: + source->state = RDP_CLIPBOARD_SOURCE_FAILED; + weston_log("RDP %s FAILED (%p:%s): %s (%u bytes)\n", + __func__, source, clipboard_data_source_state_to_string(source), + is_send ? "send" : "receive", (uint32_t)source->data_contents.size); + + wl_array_release(&data_contents); + + return false; +} + +#define DIB_HEADER_MARKER ((WORD) ('M' << 8) | 'B') +#define DIB_WIDTH_BYTES(bits) ((((bits) + 31) & ~31) >> 3) + +static bool +clipboard_process_bmp(struct rdp_clipboard_data_source *source, bool is_send) +{ + freerdp_peer *client = (freerdp_peer *)source->context; + RdpPeerContext *ctx = (RdpPeerContext *)client->context; + struct rdp_backend *b = ctx->rdpBackend; + BITMAPFILEHEADER *bmfh = NULL; + BITMAPINFOHEADER *bmih = NULL; + uint32_t color_table_size = 0; + struct wl_array data_contents; + + assert(!source->is_data_processed); + + wl_array_init(&data_contents); + + if (is_send) { + /* Linux to Windows (remove BITMAPFILEHEADER) */ + if (source->data_contents.size <= sizeof(*bmfh)) + goto error_return; + + bmfh = source->data_contents.data; + bmih = (BITMAPINFOHEADER *)(bmfh + 1); + + source->is_data_processed = true; + source->processed_data_start = bmih; + source->processed_data_size = source->data_contents.size - sizeof(*bmfh); + } else { + /* Windows to Linux (insert BITMAPFILEHEADER) */ + BITMAPFILEHEADER _bmfh = {}; + + if (source->data_contents.size <= sizeof(*bmih)) + goto error_return; + + bmih = source->data_contents.data; + bmfh = &_bmfh; + if (bmih->biCompression == BI_BITFIELDS) + color_table_size = sizeof(RGBQUAD) * 3; + else + color_table_size = sizeof(RGBQUAD) * bmih->biClrUsed; + + bmfh->bfType = DIB_HEADER_MARKER; + bmfh->bfOffBits = sizeof(*bmfh) + bmih->biSize + color_table_size; + if (bmih->biSizeImage) + bmfh->bfSize = bmfh->bfOffBits + bmih->biSizeImage; + else if (bmih->biCompression == BI_BITFIELDS || bmih->biCompression == BI_RGB) + bmfh->bfSize = bmfh->bfOffBits + + (DIB_WIDTH_BYTES(bmih->biWidth * bmih->biBitCount) * abs(bmih->biHeight)); + else + goto error_return; + + /* source data must have enough size as described in its own bitmap header */ + if (source->data_contents.size < (bmfh->bfSize - sizeof(*bmfh))) + goto error_return; + + if (!wl_array_add(&data_contents, bmfh->bfSize)) + goto error_return; + assert(data_contents.size == bmfh->bfSize); + + /* copy generated BITMAPFILEHEADER */ + memcpy(data_contents.data, bmfh, sizeof(*bmfh)); + /* copy rest of bitmap data from source */ + memcpy((char *)data_contents.data + sizeof(*bmfh), + source->data_contents.data, bmfh->bfSize - sizeof(*bmfh)); + + /* swap the data_contents with new one */ + wl_array_release(&source->data_contents); + source->data_contents = data_contents; + source->is_data_processed = true; + source->processed_data_start = source->data_contents.data; + source->processed_data_size = source->data_contents.size; + } + + rdp_debug_clipboard_verbose(b, "RDP %s (%p:%s): %s (%d bytes)\n", + __func__, source, clipboard_data_source_state_to_string(source), + is_send ? "send" : "receive", + (UINT32)source->data_contents.size); + + return true; + +error_return: + source->state = RDP_CLIPBOARD_SOURCE_FAILED; + weston_log("RDP %s FAILED (%p:%s): %s (%d bytes)\n", + __func__, source, clipboard_data_source_state_to_string(source), + is_send ? "send" : "receive", (UINT32)source->data_contents.size); + + wl_array_release(&data_contents); + + return false; +} + +static char * +clipboard_format_id_to_string(UINT32 formatId, bool is_server_format_id) +{ + switch (formatId) { + case CF_RAW: + return "CF_RAW"; + case CF_TEXT: + return "CF_TEXT"; + case CF_BITMAP: + return "CF_BITMAP"; + case CF_METAFILEPICT: + return "CF_METAFILEPICT"; + case CF_SYLK: + return "CF_SYLK"; + case CF_DIF: + return "CF_DIF"; + case CF_TIFF: + return "CF_TIFF"; + case CF_OEMTEXT: + return "CF_OEMTEXT"; + case CF_DIB: + return "CF_DIB"; + case CF_PALETTE: + return "CF_PALETTE"; + case CF_PENDATA: + return "CF_PENDATA"; + case CF_RIFF: + return "CF_RIFF"; + case CF_WAVE: + return "CF_WAVE"; + case CF_UNICODETEXT: + return "CF_UNICODETEXT"; + case CF_ENHMETAFILE: + return "CF_ENHMETAFILE"; + case CF_HDROP: + return "CF_HDROP"; + case CF_LOCALE: + return "CF_LOCALE"; + case CF_DIBV5: + return "CF_DIBV5"; + + case CF_OWNERDISPLAY: + return "CF_OWNERDISPLAY"; + case CF_DSPTEXT: + return "CF_DSPTEXT"; + case CF_DSPBITMAP: + return "CF_DSPBITMAP"; + case CF_DSPMETAFILEPICT: + return "CF_DSPMETAFILEPICT"; + case CF_DSPENHMETAFILE: + return "CF_DSPENHMETAFILE"; + } + + if (formatId >= CF_PRIVATEFIRST && formatId <= CF_PRIVATELAST) + return "CF_PRIVATE"; + + if (formatId >= CF_GDIOBJFIRST && formatId <= CF_GDIOBJLAST) + return "CF_GDIOBJ"; + + if (is_server_format_id) { + if (formatId == CF_PRIVATE_HTML) + return "CF_PRIVATE_HTML"; + + if (formatId == CF_PRIVATE_RTF) + return "CF_PRIVATE_RTF"; + } else { + /* From MSDN, RegisterClipboardFormat API. + Registered clipboard formats are identified by values in the range 0xC000 through 0xFFFF. */ + if (formatId >= 0xC000 && formatId <= 0xFFFF) + return "Client side Registered Clipboard Format"; + } + + return "Unknown format"; +} + +/* find supported index in supported format table by format id from client */ +static int +clipboard_find_supported_format_by_format_id(UINT32 format_id) +{ + unsigned int i; + + for (i = 0; i < RDP_NUM_CLIPBOARD_FORMATS; i++) { + struct rdp_clipboard_supported_format *format = &clipboard_supported_formats[i]; + + if (format_id == format->format_id) + return i; + } + return -1; +} + +/* find supported index in supported format table by format id and name from client */ +static int +clipboard_find_supported_format_by_format_id_and_name(UINT32 format_id, const char *format_name) +{ + unsigned int i; + + for (i = 0; i < RDP_NUM_CLIPBOARD_FORMATS; i++) { + struct rdp_clipboard_supported_format *format = &clipboard_supported_formats[i]; + + /* when our supported format table has format name, only format name must match, + format id provided from client is ignored (but it may be saved by caller for future use. + When our supported format table doesn't have format name, only format id must match, + format name (if provided from client) is ignored */ + if ((format->format_name == NULL && format_id == format->format_id) || + (format->format_name && format_name && strcmp(format_name, format->format_name) == 0)) + return i; + } + return -1; +} + +/* find supported index in supported format table by mime */ +static int +clipboard_find_supported_format_by_mime_type(const char *mime_type) +{ + unsigned int i; + + for (i = 0; i < RDP_NUM_CLIPBOARD_FORMATS; i++) { + struct rdp_clipboard_supported_format *format = &clipboard_supported_formats[i]; + + if (strcmp(mime_type, format->mime_type) == 0) + return i; + } + return -1; +} + +static bool +clipboard_process_source(struct rdp_clipboard_data_source *source, bool is_send) +{ + if (source->is_data_processed) { + assert(source->processed_data_is_send == is_send); + return true; + } + + source->processed_data_start = NULL; + source->processed_data_size = 0; + + if (clipboard_supported_formats[source->format_index].pfn) + return clipboard_supported_formats[source->format_index].pfn(source, is_send); + + /* No processor, so just set up pointer and length for raw data */ + source->is_data_processed = true; + source->processed_data_start = source->data_contents.data; + source->processed_data_size = source->data_contents.size; + source->processed_data_is_send = is_send; + return true; +} + +static void +clipboard_data_source_unref(struct rdp_clipboard_data_source *source) +{ + freerdp_peer *client = (freerdp_peer *)source->context; + RdpPeerContext *ctx = (RdpPeerContext *)client->context; + struct rdp_backend *b = ctx->rdpBackend; + char **p; + + ASSERT_COMPOSITOR_THREAD(b); + + assert(source->refcount); + source->refcount--; + + rdp_debug_clipboard(b, "RDP %s (%p:%s): refcount:%d\n", + __func__, source, + clipboard_data_source_state_to_string(source), + source->refcount); + + if (source->refcount > 0) + return; + + if (source->transfer_event_source) + wl_event_source_remove(source->transfer_event_source); + + if (source->data_source_fd != -1) + close(source->data_source_fd); + + if (!wl_list_empty(&source->base.destroy_signal.listener_list)) + wl_signal_emit(&source->base.destroy_signal, + &source->base); + + wl_array_release(&source->data_contents); + + wl_array_for_each(p, &source->base.mime_types) + free(*p); + + wl_array_release(&source->base.mime_types); + + free(source); +} + +/******************************************\ + * FreeRDP format data response functions * +\******************************************/ + +/* Inform client data request is succeeded with data */ +static void +clipboard_client_send_format_data_response(RdpPeerContext *ctx, struct rdp_clipboard_data_source *source) +{ + struct rdp_backend *b = ctx->rdpBackend; + CLIPRDR_FORMAT_DATA_RESPONSE formatDataResponse = {}; + + assert(source->is_data_processed); + rdp_debug_clipboard(b, "Client: %s (%p:%s) format_index:%d %s (%d bytes)\n", + __func__, source, + clipboard_data_source_state_to_string(source), + source->format_index, + clipboard_supported_formats[source->format_index].mime_type, + source->processed_data_size); + + formatDataResponse.msgType = CB_FORMAT_DATA_RESPONSE; + formatDataResponse.msgFlags = CB_RESPONSE_OK; + formatDataResponse.dataLen = source->processed_data_size; + formatDataResponse.requestedFormatData = source->processed_data_start; + ctx->clipboard_server_context->ServerFormatDataResponse(ctx->clipboard_server_context, &formatDataResponse); + /* if here failed to send response, what can we do ? */ +} + +/* Inform client data request has failed */ +static void +clipboard_client_send_format_data_response_fail(RdpPeerContext *ctx, struct rdp_clipboard_data_source *source) +{ + struct rdp_backend *b = ctx->rdpBackend; + CLIPRDR_FORMAT_DATA_RESPONSE formatDataResponse = {}; + + rdp_debug_clipboard(b, "Client: %s (%p:%s)\n", + __func__, source, + clipboard_data_source_state_to_string(source)); + + if (source) { + source->state = RDP_CLIPBOARD_SOURCE_FAILED; + source->data_response_fail_count++; + } + + formatDataResponse.msgType = CB_FORMAT_DATA_RESPONSE; + formatDataResponse.msgFlags = CB_RESPONSE_FAIL; + formatDataResponse.dataLen = 0; + formatDataResponse.requestedFormatData = NULL; + ctx->clipboard_server_context->ServerFormatDataResponse(ctx->clipboard_server_context, &formatDataResponse); + /* if here failed to send response, what can we do ? */ +} + +/***************************************\ + * Compositor file descritor callbacks * +\***************************************/ + +/* Send server clipboard data to client when server side application sent them via pipe. */ +static int +clipboard_data_source_read(int fd, uint32_t mask, void *arg) +{ + struct rdp_clipboard_data_source *source = (struct rdp_clipboard_data_source *)arg; + freerdp_peer *client = (freerdp_peer *)source->context; + RdpPeerContext *ctx = (RdpPeerContext *)client->context; + struct rdp_backend *b = ctx->rdpBackend; + int len; + bool failed = true; + + rdp_debug_clipboard_verbose(b, "RDP %s (%p:%s) fd:%d\n", + __func__, source, + clipboard_data_source_state_to_string(source), fd); + + ASSERT_COMPOSITOR_THREAD(b); + + assert(source->data_source_fd == fd); + assert(source->refcount == 1); + + /* event source is not removed here, but it will be removed when read is completed, + until it's completed this function will be called whenever next chunk of data is + available for read in pipe. */ + assert(source->transfer_event_source); + + source->state = RDP_CLIPBOARD_SOURCE_TRANSFERING; + + len = rdp_wl_array_read_fd(&source->data_contents, fd); + if (len < 0) { + source->state = RDP_CLIPBOARD_SOURCE_FAILED; + weston_log("RDP %s (%p:%s) read failed (%s)\n", + __func__, source, + clipboard_data_source_state_to_string(source), + strerror(errno)); + goto error_exit; + } + + if (len > 0) { + rdp_debug_clipboard_verbose(b, "RDP %s (%p:%s) read (%zu bytes)\n", + __func__, source, + clipboard_data_source_state_to_string(source), + source->data_contents.size); + /* continue to read next batch */ + return 0; + } + + /* len == 0, all data from source is read, so completed. */ + source->state = RDP_CLIPBOARD_SOURCE_TRANSFERRED; + rdp_debug_clipboard(b, "RDP %s (%p:%s): read completed (%ld bytes)\n", + __func__, source, + clipboard_data_source_state_to_string(source), + source->data_contents.size); + if (!source->data_contents.size) + goto error_exit; + /* process data before sending to client */ + if (!clipboard_process_source(source, true)) + goto error_exit; + + clipboard_client_send_format_data_response(ctx, source); + failed = false; + +error_exit: + if (failed) + clipboard_client_send_format_data_response_fail(ctx, source); + + /* make sure this is the last reference, so event source is removed at unref */ + assert(source->refcount == 1); + clipboard_data_source_unref(source); + return 0; +} + +/* client's reply with error for data request, clean up */ +static int +clipboard_data_source_fail(int fd, uint32_t mask, void *arg) +{ + struct rdp_clipboard_data_source *source = (struct rdp_clipboard_data_source *)arg; + freerdp_peer *client = (freerdp_peer *)source->context; + RdpPeerContext *ctx = (RdpPeerContext *)client->context; + struct rdp_backend *b = ctx->rdpBackend; + + rdp_debug_clipboard_verbose(b, "RDP %s (%p:%s) fd:%d\n", __func__, + source, clipboard_data_source_state_to_string(source), fd); + + ASSERT_COMPOSITOR_THREAD(b); + + assert(source->data_source_fd == fd); + /* this data source must be tracked as inflight */ + assert(source == ctx->clipboard_inflight_client_data_source); + + wl_event_source_remove(source->transfer_event_source); + source->transfer_event_source = NULL; + + /* if data was received, but failed for another reason then keep data + * and format index for future request, otherwise data is purged at + * last reference release. */ + if (!source->data_contents.size) { + /* data has been never received, thus must be empty. */ + assert(source->data_contents.size == 0); + assert(source->data_contents.alloc == 0); + assert(source->data_contents.data == NULL); + /* clear previous requested format so it can be requested later again. */ + source->format_index = -1; + } + + /* data has never been sent to write(), thus must be no inflight write. */ + assert(source->inflight_write_count == 0); + assert(source->inflight_data_to_write == NULL); + assert(source->inflight_data_size == 0); + /* data never has been sent to write(), so must not be processed. */ + assert(source->is_data_processed == FALSE); + /* close fd to server clipboard stop pulling data. */ + close(source->data_source_fd); + source->data_source_fd = -1; + /* clear inflight data source from client to server. */ + ctx->clipboard_inflight_client_data_source = NULL; + clipboard_data_source_unref(source); + + return 0; +} + +/* Send client's clipboard data to the requesting application at server side */ +static int +clipboard_data_source_write(int fd, uint32_t mask, void *arg) +{ + struct rdp_clipboard_data_source *source = (struct rdp_clipboard_data_source *)arg; + freerdp_peer *client = (freerdp_peer *)source->context; + RdpPeerContext *ctx = (RdpPeerContext *)client->context; + struct rdp_backend *b = ctx->rdpBackend; + void *data_to_write; + size_t data_size; + ssize_t size; + + rdp_debug_clipboard_verbose(b, "RDP %s (%p:%s) fd:%d\n", __func__, + source, + clipboard_data_source_state_to_string(source), + fd); + + ASSERT_COMPOSITOR_THREAD(b); + + assert(source->data_source_fd == fd); + /* this data source must be tracked as inflight */ + assert(source == ctx->clipboard_inflight_client_data_source); + + if (source->is_canceled) { + /* if source is being canceled, this must be the last reference */ + assert(source->refcount == 1); + source->state = RDP_CLIPBOARD_SOURCE_CANCELED; + rdp_debug_clipboard_verbose(b, "RDP %s (%p:%s) canceled\n", + __func__, source, + clipboard_data_source_state_to_string(source)); + goto fail; + } + + if (!source->data_contents.data || !source->data_contents.size) { + assert(source->refcount > 1); + weston_log("RDP %s (%p:%s) no data received from client\n", + __func__, source, + clipboard_data_source_state_to_string(source)); + goto fail; + } + + assert(source->refcount > 1); + if (source->inflight_data_to_write) { + assert(source->inflight_data_size); + rdp_debug_clipboard_verbose(b, "RDP %s (%p:%s) transfer in chunck, count:%d\n", + __func__, source, + clipboard_data_source_state_to_string(source), + source->inflight_write_count); + data_to_write = source->inflight_data_to_write; + data_size = source->inflight_data_size; + } else { + fcntl(source->data_source_fd, F_SETFL, O_WRONLY | O_NONBLOCK); + clipboard_process_source(source, false); + data_to_write = source->processed_data_start; + data_size = source->processed_data_size; + } + while (data_to_write && data_size) { + source->state = RDP_CLIPBOARD_SOURCE_TRANSFERING; + do { + size = write(source->data_source_fd, data_to_write, data_size); + } while (size == -1 && errno == EINTR); + + if (size <= 0) { + if (errno != EAGAIN) { + source->state = RDP_CLIPBOARD_SOURCE_FAILED; + weston_log("RDP %s (%p:%s) write failed %s\n", + __func__, source, + clipboard_data_source_state_to_string(source), strerror(errno)); + break; + } + /* buffer is full, wait until data_source_fd is writable again */ + source->inflight_data_to_write = data_to_write; + source->inflight_data_size = data_size; + source->inflight_write_count++; + return 0; + } else { + assert(data_size >= (size_t)size); + data_size -= size; + data_to_write = (char *)data_to_write + size; + rdp_debug_clipboard_verbose(b, "RDP %s (%p:%s) wrote %ld bytes, remaining %ld bytes\n", + __func__, source, + clipboard_data_source_state_to_string(source), + size, data_size); + if (!data_size) { + source->state = RDP_CLIPBOARD_SOURCE_TRANSFERRED; + rdp_debug_clipboard(b, "RDP %s (%p:%s) write completed (%ld bytes)\n", + __func__, source, + clipboard_data_source_state_to_string(source), + source->data_contents.size); + } + } + } + +fail: + /* Here write is either completed, canceled or failed, so close the pipe. */ + close(source->data_source_fd); + source->data_source_fd = -1; + /* and remove the event source */ + wl_event_source_remove(source->transfer_event_source); + source->transfer_event_source = NULL; + source->inflight_write_count = 0; + source->inflight_data_to_write = NULL; + source->inflight_data_size = 0; + ctx->clipboard_inflight_client_data_source = NULL; + clipboard_data_source_unref(source); + + return 0; +} + +/***********************************\ + * Clipboard data-device callbacks * +\***********************************/ + +/* data-device informs the given data format is accepted */ +static void +clipboard_data_source_accept(struct weston_data_source *base, + uint32_t time, const char *mime_type) +{ + struct rdp_clipboard_data_source *source = (struct rdp_clipboard_data_source *)base; + freerdp_peer *client = (freerdp_peer *)source->context; + RdpPeerContext *ctx = (RdpPeerContext *)client->context; + struct rdp_backend *b = ctx->rdpBackend; + + rdp_debug_clipboard(b, "RDP %s (%p:%s) mime-type:\"%s\"\n", + __func__, source, + clipboard_data_source_state_to_string(source), + mime_type); +} + +/* data-device informs the application requested the specified format data + * in given data_source (= client's clipboard) */ +static void +clipboard_data_source_send(struct weston_data_source *base, + const char *mime_type, int32_t fd) +{ + struct rdp_clipboard_data_source *source = (struct rdp_clipboard_data_source *)base; + freerdp_peer *client = (freerdp_peer *)source->context; + RdpPeerContext *ctx = (RdpPeerContext *)client->context; + struct rdp_backend *b = ctx->rdpBackend; + struct weston_seat *seat = ctx->item.seat; + struct wl_event_loop *loop = wl_display_get_event_loop(seat->compositor->wl_display); + CLIPRDR_FORMAT_DATA_REQUEST formatDataRequest = {}; + int index; + + rdp_debug_clipboard(b, "RDP %s (%p:%s) fd:%d, mime-type:\"%s\"\n", + __func__, source, + clipboard_data_source_state_to_string(source), + fd, mime_type); + + ASSERT_COMPOSITOR_THREAD(b); + + if (ctx->clipboard_inflight_client_data_source) { + /* Here means server side (Linux application) request clipboard data, + but server hasn't completed with previous request yet. + If this happens, punt to idle loop and reattempt. */ + weston_log("\n\n\nRDP %s new (%p:%s:fd %d) vs prev (%p:%s:fd %d): outstanding RDP data request (client to server)\n\n\n", + __func__, source, clipboard_data_source_state_to_string(source), fd, + ctx->clipboard_inflight_client_data_source, + clipboard_data_source_state_to_string(ctx->clipboard_inflight_client_data_source), + ctx->clipboard_inflight_client_data_source->data_source_fd); + if (source == ctx->clipboard_inflight_client_data_source) { + /* when new source and previous source is same, update fd with new one and retry */ + source->state = RDP_CLIPBOARD_SOURCE_RETRY; + ctx->clipboard_inflight_client_data_source->data_source_fd = fd; + return; + } else { + source->state = RDP_CLIPBOARD_SOURCE_FAILED; + goto error_return_close_fd; + } + } + + if (source->base.mime_types.size == 0) { + source->state = RDP_CLIPBOARD_SOURCE_TRANSFERRED; + rdp_debug_clipboard(b, "RDP %s (%p:%s) source has no data\n", + __func__, source, clipboard_data_source_state_to_string(source)); + goto error_return_close_fd; + } + + index = clipboard_find_supported_format_by_mime_type(mime_type); + if (index >= 0 && /* check supported by this RDP bridge */ + source->client_format_id_table[index]) { /* check supported by current data source from client */ + ctx->clipboard_inflight_client_data_source = source; + source->refcount++; // reference while request inflight. + source->data_source_fd = fd; + assert(source->inflight_write_count == 0); + assert(source->inflight_data_to_write == NULL); + assert(source->inflight_data_size == 0); + if (index == source->format_index) { + bool ret; + + /* data is already in data_contents, no need to pull from client */ + assert(source->transfer_event_source == NULL); + source->state = RDP_CLIPBOARD_SOURCE_RECEIVED_DATA; + rdp_debug_clipboard_verbose(b, "RDP %s (%p:%s) data in cache \"%s\" index:%d formatId:%d %s\n", + __func__, source, + clipboard_data_source_state_to_string(source), + mime_type, index, + source->client_format_id_table[index], + clipboard_format_id_to_string(source->client_format_id_table[index], + false)); + + ret = rdp_event_loop_add_fd(loop, source->data_source_fd, WL_EVENT_WRITABLE, + clipboard_data_source_write, source, + &source->transfer_event_source); + if (!ret) { + source->state = RDP_CLIPBOARD_SOURCE_FAILED; + weston_log("RDP %s (%p:%s) wl_event_loop_add_fd failed\n", + __func__, source, clipboard_data_source_state_to_string(source)); + goto error_return_unref_source; + } + } else { + /* purge cached data */ + wl_array_release(&source->data_contents); + wl_array_init(&source->data_contents); + source->is_data_processed = false; + /* update requesting format property */ + source->format_index = index; + /* request clipboard data from client */ + formatDataRequest.msgType = CB_FORMAT_DATA_REQUEST; + formatDataRequest.dataLen = 4; + formatDataRequest.requestedFormatId = source->client_format_id_table[index]; + source->state = RDP_CLIPBOARD_SOURCE_REQUEST_DATA; + rdp_debug_clipboard(b, "RDP %s (%p:%s) request data \"%s\" index:%d formatId:%d %s\n", + __func__, source, clipboard_data_source_state_to_string(source), mime_type, index, + formatDataRequest.requestedFormatId, + clipboard_format_id_to_string(formatDataRequest.requestedFormatId, false)); + if (ctx->clipboard_server_context->ServerFormatDataRequest(ctx->clipboard_server_context, &formatDataRequest) != 0) + goto error_return_unref_source; + } + } else { + source->state = RDP_CLIPBOARD_SOURCE_FAILED; + weston_log("RDP %s (%p:%s) specified format \"%s\" index:%d formatId:%d is not supported by client\n", + __func__, source, clipboard_data_source_state_to_string(source), + mime_type, index, source->client_format_id_table[index]); + goto error_return_close_fd; + } + + return; + +error_return_unref_source: + source->data_source_fd = -1; + assert(source->inflight_write_count == 0); + assert(source->inflight_data_to_write == NULL); + assert(source->inflight_data_size == 0); + assert(ctx->clipboard_inflight_client_data_source == source); + ctx->clipboard_inflight_client_data_source = NULL; + clipboard_data_source_unref(source); + +error_return_close_fd: + close(fd); +} + +/* data-device informs the given data source is not longer referenced by compositor */ +static void +clipboard_data_source_cancel(struct weston_data_source *base) +{ + struct rdp_clipboard_data_source *source = (struct rdp_clipboard_data_source *)base; + freerdp_peer *client = (freerdp_peer *)source->context; + RdpPeerContext *ctx = (RdpPeerContext *)client->context; + struct rdp_backend *b = ctx->rdpBackend; + + rdp_debug_clipboard(b, "RDP %s (%p:%s)\n", + __func__, source, + clipboard_data_source_state_to_string(source)); + + ASSERT_COMPOSITOR_THREAD(b); + + if (source == ctx->clipboard_inflight_client_data_source) { + source->is_canceled = true; + source->state = RDP_CLIPBOARD_SOURCE_CANCEL_PENDING; + rdp_debug_clipboard(b, "RDP %s (%p:%s): still inflight - refcount:%d\n", + __func__, source, + clipboard_data_source_state_to_string(source), + source->refcount); + assert(source->refcount > 1); + return; + } + /* everything outside of the base has to be cleaned up */ + source->state = RDP_CLIPBOARD_SOURCE_CANCELED; + rdp_debug_clipboard_verbose(b, "RDP %s (%p:%s) - refcount:%d\n", + __func__, source, + clipboard_data_source_state_to_string(source), + source->refcount); + assert(source->refcount == 1); + assert(source->transfer_event_source == NULL); + wl_array_release(&source->data_contents); + wl_array_init(&source->data_contents); + source->is_data_processed = false; + source->format_index = -1; + memset(source->client_format_id_table, 0, sizeof(source->client_format_id_table)); + source->inflight_write_count = 0; + source->inflight_data_to_write = NULL; + source->inflight_data_size = 0; + if (source->data_source_fd != -1) { + close(source->data_source_fd); + source->data_source_fd = -1; + } +} + +/**********************************\ + * Compositor idle loop callbacks * +\**********************************/ + +/* Publish client's available clipboard formats to compositor (make them visible to applications in server) */ +static void +clipboard_data_source_publish(bool freeOnly, void *arg) +{ + struct rdp_clipboard_data_source *source = wl_container_of(arg, source, task_base); + freerdp_peer *client = (freerdp_peer *)source->context; + RdpPeerContext *ctx = (RdpPeerContext *)client->context; + struct rdp_backend *b = ctx->rdpBackend; + struct rdp_clipboard_data_source *source_prev; + + rdp_debug_clipboard(b, "RDP %s (%p:%s)\n", + __func__, source, clipboard_data_source_state_to_string(source)); + + ASSERT_COMPOSITOR_THREAD(b); + + /* here is going to publish new data, if previous data from client is still referenced, + unref it after selection */ + source_prev = ctx->clipboard_client_data_source; + if (!freeOnly) { + ctx->clipboard_client_data_source = source; + source->transfer_event_source = NULL; + source->base.accept = clipboard_data_source_accept; + source->base.send = clipboard_data_source_send; + source->base.cancel = clipboard_data_source_cancel; + source->state = RDP_CLIPBOARD_SOURCE_PUBLISHED; + weston_seat_set_selection(ctx->item.seat, &source->base, + wl_display_next_serial(b->compositor->wl_display)); + } else { + ctx->clipboard_client_data_source = NULL; + clipboard_data_source_unref(source); + } + + if (source_prev) + clipboard_data_source_unref(source_prev); +} + +/* Request the specified clipboard data from data-device at server side */ +static void +clipboard_data_source_request(bool freeOnly, void *arg) +{ + struct rdp_clipboard_data_request *request = wl_container_of(arg, request, task_base); + RdpPeerContext *ctx = request->ctx; + struct rdp_backend *b = ctx->rdpBackend; + struct weston_seat *seat = ctx->item.seat; + struct weston_data_source *selection_data_source = seat->selection_data_source; + struct wl_event_loop *loop = wl_display_get_event_loop(seat->compositor->wl_display); + struct rdp_clipboard_data_source *source = NULL; + int p[2] = {}; + const char *requested_mime_type, **mime_type; + int index; + bool found_requested_format; + bool ret; + + ASSERT_COMPOSITOR_THREAD(b); + + if (freeOnly) + goto error_exit_free_request; + + index = request->requested_format_index; + assert(index >= 0 && index < (int)RDP_NUM_CLIPBOARD_FORMATS); + requested_mime_type = clipboard_supported_formats[index].mime_type; + rdp_debug_clipboard(b, "RDP %s (base:%p) requested mime type:\"%s\"\n", + __func__, selection_data_source, requested_mime_type); + + found_requested_format = FALSE; + wl_array_for_each(mime_type, &selection_data_source->mime_types) { + rdp_debug_clipboard(b, "RDP %s (base:%p) available formats: %s\n", + __func__, selection_data_source, *mime_type); + if (strcmp(requested_mime_type, *mime_type) == 0) { + found_requested_format = true; + break; + } + } + if (!found_requested_format) { + rdp_debug_clipboard(b, "RDP %s (base:%p) requested format not found format:\"%s\"\n", + __func__, selection_data_source, requested_mime_type); + goto error_exit_response_fail; + } + + source = zalloc(sizeof *source); + if (!source) + goto error_exit_response_fail; + + /* By now, the server side data availablity is already notified + to client by clipboard_set_selection(). */ + source->state = RDP_CLIPBOARD_SOURCE_PUBLISHED; + rdp_debug_clipboard(b, "RDP %s (%p:%s) for (base:%p)\n", + __func__, source, + clipboard_data_source_state_to_string(source), + selection_data_source); + wl_signal_init(&source->base.destroy_signal); + wl_array_init(&source->base.mime_types); + wl_array_init(&source->data_contents); + source->is_data_processed = false; + source->context = ctx->item.peer; + source->refcount = 1; // decremented when data sent to client. + source->data_source_fd = -1; + source->format_index = index; + + if (pipe2(p, O_CLOEXEC) == -1) + goto error_exit_free_source; + + source->data_source_fd = p[0]; + + rdp_debug_clipboard_verbose(b, "RDP %s (%p:%s) pipe write:%d -> read:%d\n", + __func__, source, + clipboard_data_source_state_to_string(source), + p[1], p[0]); + + /* Request data from data source */ + source->state = RDP_CLIPBOARD_SOURCE_REQUEST_DATA; + selection_data_source->send(selection_data_source, requested_mime_type, p[1]); + /* p[1] should be closed by data source */ + + ret = rdp_event_loop_add_fd(loop, p[0], WL_EVENT_READABLE, + clipboard_data_source_read, source, + &source->transfer_event_source); + if (!ret) { + source->state = RDP_CLIPBOARD_SOURCE_FAILED; + weston_log("RDP %s (%p:%s) wl_event_loop_add_fd failed.\n", + __func__, source, + clipboard_data_source_state_to_string(source)); + goto error_exit_free_source; + } + + free(request); + + return; + +error_exit_free_source: + assert(source->refcount == 1); + clipboard_data_source_unref(source); +error_exit_response_fail: + clipboard_client_send_format_data_response_fail(ctx, NULL); +error_exit_free_request: + free(request); +} + +/*************************************\ + * Compositor notification callbacks * +\*************************************/ + +/* Compositor notify new clipboard data is going to be copied to clipboard, and its supported formats */ +static void +clipboard_set_selection(struct wl_listener *listener, void *data) +{ + RdpPeerContext *ctx = + container_of(listener, RdpPeerContext, clipboard_selection_listener); + struct rdp_backend *b = ctx->rdpBackend; + struct weston_seat *seat = data; + struct weston_data_source *selection_data_source = seat->selection_data_source; + struct rdp_clipboard_data_source *data_source; + CLIPRDR_FORMAT_LIST formatList = {}; + CLIPRDR_FORMAT format[RDP_NUM_CLIPBOARD_FORMATS] = {}; + const char **mime_type; + int index, num_supported_format = 0, num_avail_format = 0; + + rdp_debug_clipboard(b, "RDP %s (base:%p)\n", __func__, selection_data_source); + + ASSERT_COMPOSITOR_THREAD(b); + + if (selection_data_source == NULL) { + return; + } + + if (selection_data_source->accept == clipboard_data_source_accept) { + /* Callback for our data source. */ + return; + } + + /* another data source (from server side) gets selected, + no longer need previous data from client. */ + if (ctx->clipboard_client_data_source) { + data_source = ctx->clipboard_client_data_source; + ctx->clipboard_client_data_source = NULL; + clipboard_data_source_unref(data_source); + } + + wl_array_for_each(mime_type, &selection_data_source->mime_types) { + rdp_debug_clipboard(b, "RDP %s (base:%p) available formats[%d]: %s\n", + __func__, selection_data_source, num_avail_format, *mime_type); + num_avail_format++; + } + + /* check supported clipboard formats */ + wl_array_for_each(mime_type, &selection_data_source->mime_types) { + index = clipboard_find_supported_format_by_mime_type(*mime_type); + if (index >= 0) { + CLIPRDR_FORMAT *f = &format[num_supported_format]; + + f->formatId = clipboard_supported_formats[index].format_id; + f->formatName = clipboard_supported_formats[index].format_name; + rdp_debug_clipboard(b, "RDP %s (base:%p) supported formats[%d]: %d: %s\n", + __func__, + selection_data_source, + num_supported_format, + f->formatId, + f->formatName ? + f->formatName : + clipboard_format_id_to_string(f->formatId, true)); + num_supported_format++; + } + } + + if (num_supported_format) { + /* let client knows formats are available in server clipboard */ + formatList.msgType = CB_FORMAT_LIST; + formatList.numFormats = num_supported_format; + formatList.formats = &format[0]; + ctx->clipboard_server_context->ServerFormatList(ctx->clipboard_server_context, &formatList); + } else { + rdp_debug_clipboard(b, "RDP %s (base:%p) no supported formats\n", __func__, selection_data_source); + } + + return; +} + +/*********************\ + * FreeRDP callbacks * +\*********************/ + +/* client reports the path of temp folder */ +static UINT +clipboard_client_temp_directory(CliprdrServerContext *context, const CLIPRDR_TEMP_DIRECTORY *tempDirectory) +{ + freerdp_peer *client = (freerdp_peer *)context->custom; + RdpPeerContext *ctx = (RdpPeerContext *)client->context; + struct rdp_backend *b = ctx->rdpBackend; + + rdp_debug_clipboard(b, "Client: %s %s\n", __func__, tempDirectory->szTempDir); + return 0; +} + +/* client reports thier clipboard capabilities */ +static UINT +clipboard_client_capabilities(CliprdrServerContext *context, const CLIPRDR_CAPABILITIES *capabilities) +{ + freerdp_peer *client = (freerdp_peer *)context->custom; + RdpPeerContext *ctx = (RdpPeerContext *)client->context; + struct rdp_backend *b = ctx->rdpBackend; + + rdp_debug_clipboard(b, "Client: clipboard capabilities: cCapabilitiesSet:%d\n", capabilities->cCapabilitiesSets); + for (uint32_t i = 0; i < capabilities->cCapabilitiesSets; i++) { + CLIPRDR_CAPABILITY_SET *capabilitySets = &capabilities->capabilitySets[i]; + CLIPRDR_GENERAL_CAPABILITY_SET *generalCapabilitySet = (CLIPRDR_GENERAL_CAPABILITY_SET *)capabilitySets; + + switch (capabilitySets->capabilitySetType) { + case CB_CAPSTYPE_GENERAL: + rdp_debug_clipboard(b, "Client: clipboard capabilities[%d]: General\n", i); + rdp_debug_clipboard(b, " Version:%d\n", generalCapabilitySet->version); + rdp_debug_clipboard(b, " GeneralFlags:0x%x\n", generalCapabilitySet->generalFlags); + if (generalCapabilitySet->generalFlags & CB_USE_LONG_FORMAT_NAMES) + rdp_debug_clipboard(b, " CB_USE_LONG_FORMAT_NAMES\n"); + if (generalCapabilitySet->generalFlags & CB_STREAM_FILECLIP_ENABLED) + rdp_debug_clipboard(b, " CB_STREAM_FILECLIP_ENABLED\n"); + if (generalCapabilitySet->generalFlags & CB_FILECLIP_NO_FILE_PATHS) + rdp_debug_clipboard(b, " CB_FILECLIP_NO_FILE_PATHS\n"); + if (generalCapabilitySet->generalFlags & CB_CAN_LOCK_CLIPDATA) + rdp_debug_clipboard(b, " CB_CAN_LOCK_CLIPDATA\n"); + break; + default: + return -1; + } + } + return 0; +} + +/* client reports the supported format list in client's clipboard */ +static UINT +clipboard_client_format_list(CliprdrServerContext *context, const CLIPRDR_FORMAT_LIST *formatList) +{ + CLIPRDR_FORMAT_LIST_RESPONSE formatListResponse = {}; + freerdp_peer *client = (freerdp_peer *)context->custom; + RdpPeerContext *ctx = (RdpPeerContext *)client->context; + struct rdp_backend *b = ctx->rdpBackend; + struct rdp_clipboard_data_source *source = NULL; + char **p, *s; + + ASSERT_NOT_COMPOSITOR_THREAD(b); + + rdp_debug_clipboard(b, "Client: %s clipboard format list: numFormats:%d\n", __func__, formatList->numFormats); + for (uint32_t i = 0; i < formatList->numFormats; i++) { + CLIPRDR_FORMAT *format = &formatList->formats[i]; + + rdp_debug_clipboard(b, "Client: %s clipboard formats[%d]: formatId:%d, formatName:%s\n", + __func__, i, format->formatId, + format->formatName ? format->formatName : clipboard_format_id_to_string(format->formatId, false)); + } + + source = zalloc(sizeof *source); + if (!source) + goto fail; + + source->state = RDP_CLIPBOARD_SOURCE_ALLOCATED; + rdp_debug_clipboard(b, "Client: %s (%p:%s) allocated\n", + __func__, source, clipboard_data_source_state_to_string(source)); + wl_signal_init(&source->base.destroy_signal); + wl_array_init(&source->base.mime_types); + wl_array_init(&source->data_contents); + source->context = client; + source->refcount = 1; // decremented when another source is selected. + source->data_source_fd = -1; + source->format_index = -1; + + for (uint32_t i = 0; i < formatList->numFormats; i++) { + CLIPRDR_FORMAT *format = &formatList->formats[i]; + int index = clipboard_find_supported_format_by_format_id_and_name(format->formatId, format->formatName); + + if (index >= 0) { + /* save format id given from client, client can handle its own format id for private format. */ + source->client_format_id_table[index] = format->formatId; + s = strdup(clipboard_supported_formats[index].mime_type); + if (s) { + p = wl_array_add(&source->base.mime_types, sizeof *p); + if (p) { + rdp_debug_clipboard(b, "Client: %s (%p:%s) mine_type:\"%s\" index:%d formatId:%d\n", + __func__, source, + clipboard_data_source_state_to_string(source), + s, index, format->formatId); + *p = s; + } else { + rdp_debug_clipboard(b, "Client: %s (%p:%s) wl_array_add failed\n", + __func__, source, + clipboard_data_source_state_to_string(source)); + free(s); + } + } else { + rdp_debug_clipboard(b, "Client: %s (%p:%s) strdup failed\n", + __func__, source, + clipboard_data_source_state_to_string(source)); + } + } + } + + if (formatList->numFormats != 0 && + source->base.mime_types.size == 0) { + rdp_debug_clipboard(b, "Client: %s (%p:%s) no formats are supported\n", + __func__, source, + clipboard_data_source_state_to_string(source)); + } + + source->state = RDP_CLIPBOARD_SOURCE_FORMATLIST_READY; + rdp_dispatch_task_to_display_loop(ctx, clipboard_data_source_publish, &source->task_base); + +fail: + formatListResponse.msgType = CB_FORMAT_LIST_RESPONSE; + formatListResponse.msgFlags = source ? CB_RESPONSE_OK : CB_RESPONSE_FAIL; + formatListResponse.dataLen = 0; + if (ctx->clipboard_server_context->ServerFormatListResponse(ctx->clipboard_server_context, &formatListResponse) != 0) { + source->state = RDP_CLIPBOARD_SOURCE_FAILED; + weston_log("Client: %s (%p:%s) ServerFormatListResponse failed\n", + __func__, source, + clipboard_data_source_state_to_string(source)); + return -1; + } + return 0; +} + +/* client responded with clipboard data asked by server */ +static UINT +clipboard_client_format_data_response(CliprdrServerContext *context, const CLIPRDR_FORMAT_DATA_RESPONSE *formatDataResponse) +{ + freerdp_peer *client = (freerdp_peer *)context->custom; + RdpPeerContext *ctx = (RdpPeerContext *)client->context; + struct rdp_backend *b = ctx->rdpBackend; + struct wl_event_loop *loop = wl_display_get_event_loop(b->compositor->wl_display); + struct rdp_clipboard_data_source *source = ctx->clipboard_inflight_client_data_source; + bool success = false; + bool ret; + + rdp_debug_clipboard(b, "Client: %s (%p:%s) flags:%d dataLen:%d\n", + __func__, source, + clipboard_data_source_state_to_string(source), + formatDataResponse->msgFlags, + formatDataResponse->dataLen); + + ASSERT_NOT_COMPOSITOR_THREAD(b); + + if (!source) { + rdp_debug_clipboard(b, "Client: %s client send data without server asking. protocol error", __func__); + return -1; + } + + if (source->transfer_event_source || (source->inflight_write_count != 0)) { + /* here means client responded more than once for single data request */ + source->state = RDP_CLIPBOARD_SOURCE_FAILED; + weston_log("Client: %s (%p:%s) middle of write loop:%p, %d\n", + __func__, source, clipboard_data_source_state_to_string(source), + source->transfer_event_source, source->inflight_write_count); + return -1; + } + + if (formatDataResponse->msgFlags == CB_RESPONSE_OK) { + /* Recieved data from client, cache to data source */ + if (wl_array_add(&source->data_contents, formatDataResponse->dataLen+1)) { + memcpy(source->data_contents.data, + formatDataResponse->requestedFormatData, + formatDataResponse->dataLen); + source->data_contents.size = formatDataResponse->dataLen; + /* regardless data type, make sure it ends with NULL */ + ((char *)source->data_contents.data)[source->data_contents.size] = '\0'; + /* data is ready, waiting to be written to destination */ + source->state = RDP_CLIPBOARD_SOURCE_RECEIVED_DATA; + success = true; + } else { + source->state = RDP_CLIPBOARD_SOURCE_FAILED; + } + } else { + source->state = RDP_CLIPBOARD_SOURCE_FAILED; + source->data_response_fail_count++; + } + rdp_debug_clipboard_verbose(b, "Client: %s (%p:%s) fail count:%d\n", + __func__, source, + clipboard_data_source_state_to_string(source), + source->data_response_fail_count); + + assert(source->transfer_event_source == NULL); + ret = rdp_event_loop_add_fd(loop, source->data_source_fd, WL_EVENT_WRITABLE, + success ? clipboard_data_source_write : clipboard_data_source_fail, + source, &source->transfer_event_source); + if (!ret) { + source->state = RDP_CLIPBOARD_SOURCE_FAILED; + weston_log("Client: %s (%p:%s) rdp_event_loop_add_fd failed\n", + __func__, source, clipboard_data_source_state_to_string(source)); + return -1; + } + + return 0; +} + +/* client responded on the format list sent by server */ +static UINT +clipboard_client_format_list_response(CliprdrServerContext *context, + const CLIPRDR_FORMAT_LIST_RESPONSE *formatListResponse) +{ + freerdp_peer *client = (freerdp_peer *)context->custom; + RdpPeerContext *ctx = (RdpPeerContext *)client->context; + struct rdp_backend *b = ctx->rdpBackend; + + rdp_debug_clipboard(b, "Client: %s msgFlags:0x%x\n", __func__, formatListResponse->msgFlags); + ASSERT_NOT_COMPOSITOR_THREAD(b); + return 0; +} + +/* client requested the data of specificed format in server clipboard */ +static UINT +clipboard_client_format_data_request(CliprdrServerContext *context, + const CLIPRDR_FORMAT_DATA_REQUEST *formatDataRequest) +{ + freerdp_peer *client = (freerdp_peer *)context->custom; + RdpPeerContext *ctx = (RdpPeerContext *)client->context; + struct rdp_backend *b = ctx->rdpBackend; + struct rdp_clipboard_data_request *request; + int index; + + rdp_debug_clipboard(b, "Client: %s requestedFormatId:%d - %s\n", + __func__, formatDataRequest->requestedFormatId, + clipboard_format_id_to_string(formatDataRequest->requestedFormatId, true)); + + ASSERT_NOT_COMPOSITOR_THREAD(b); + + /* Make sure clients requested the format we knew */ + index = clipboard_find_supported_format_by_format_id(formatDataRequest->requestedFormatId); + if (index < 0) { + weston_log("Client: %s client requests data format the server never reported in format list response. protocol error.\n", __func__); + goto error_return; + } + + request = zalloc(sizeof(*request)); + if (!request) { + weston_log("zalloc failed\n"); + goto error_return; + } + request->ctx = ctx; + request->requested_format_index = index; + rdp_dispatch_task_to_display_loop(ctx, clipboard_data_source_request, &request->task_base); + + return 0; + +error_return: + /* send FAIL response to client */ + clipboard_client_send_format_data_response_fail(ctx, NULL); + return 0; +} + +/********************\ + * Public functions * +\********************/ + +int +rdp_clipboard_init(freerdp_peer *client) +{ + RdpPeerContext *ctx = (RdpPeerContext *)client->context; + struct rdp_backend *b = ctx->rdpBackend; + struct weston_seat *seat = ctx->item.seat; + CliprdrServerContext *clip_ctx; + + assert(seat); + + ASSERT_COMPOSITOR_THREAD(b); + + ctx->clipboard_server_context = cliprdr_server_context_new(ctx->vcm); + if (!ctx->clipboard_server_context) + goto error; + + clip_ctx = ctx->clipboard_server_context; + clip_ctx->custom = (void *)client; + clip_ctx->TempDirectory = clipboard_client_temp_directory; + clip_ctx->ClientCapabilities = clipboard_client_capabilities; + clip_ctx->ClientFormatList = clipboard_client_format_list; + clip_ctx->ClientFormatListResponse = clipboard_client_format_list_response; + /* clip_ctx->ClientLockClipboardData */ + /* clip_ctx->ClientUnlockClipboardData */ + clip_ctx->ClientFormatDataRequest = clipboard_client_format_data_request; + clip_ctx->ClientFormatDataResponse = clipboard_client_format_data_response; + /* clip_ctxClientFileContentsRequest */ + /* clip_ctx->ClientFileContentsResponse */ + clip_ctx->useLongFormatNames = FALSE; /* ASCII8 format name only (No Windows-style 2 bytes Unicode). */ + clip_ctx->streamFileClipEnabled = FALSE; + clip_ctx->fileClipNoFilePaths = FALSE; + clip_ctx->canLockClipData = TRUE; + if (clip_ctx->Start(ctx->clipboard_server_context) != 0) + goto error; + + ctx->clipboard_selection_listener.notify = clipboard_set_selection; + wl_signal_add(&seat->selection_signal, + &ctx->clipboard_selection_listener); + + return 0; + +error: + if (ctx->clipboard_server_context) { + cliprdr_server_context_free(ctx->clipboard_server_context); + ctx->clipboard_server_context = NULL; + } + + return -1; +} + +void +rdp_clipboard_destroy(RdpPeerContext *ctx) +{ + struct rdp_clipboard_data_source *data_source; + struct rdp_backend *b = ctx->rdpBackend; + + ASSERT_COMPOSITOR_THREAD(b); + + if (ctx->clipboard_selection_listener.notify) { + wl_list_remove(&ctx->clipboard_selection_listener.link); + ctx->clipboard_selection_listener.notify = NULL; + } + + if (ctx->clipboard_inflight_client_data_source) { + data_source = ctx->clipboard_inflight_client_data_source; + ctx->clipboard_inflight_client_data_source = NULL; + clipboard_data_source_unref(data_source); + } + + if (ctx->clipboard_client_data_source) { + data_source = ctx->clipboard_client_data_source; + ctx->clipboard_client_data_source = NULL; + clipboard_data_source_unref(data_source); + } + + if (ctx->clipboard_server_context) { + ctx->clipboard_server_context->Stop(ctx->clipboard_server_context); + cliprdr_server_context_free(ctx->clipboard_server_context); + ctx->clipboard_server_context = NULL; + } +} diff --git a/libweston/backend-rdp/rdputil.c b/libweston/backend-rdp/rdputil.c index 296dd7fb..8b49b8e2 100644 --- a/libweston/backend-rdp/rdputil.c +++ b/libweston/backend-rdp/rdputil.c @@ -226,3 +226,39 @@ rdp_destroy_dispatch_task_event_source(RdpPeerContext *peerCtx) pthread_mutex_destroy(&peerCtx->loop_task_list_mutex); } + +/* This is a little tricky - it makes sure there's always at least + * one spare byte in the array in case the caller needs to add a + * null terminator to it. We can't just null terminate the array + * here, because some callers won't want that - and some won't + * like having an odd number of bytes. + */ +int +rdp_wl_array_read_fd(struct wl_array *array, int fd) +{ + int len, size; + char *data; + + /* Make sure we have at least 1024 bytes of space left */ + if (array->alloc - array->size < 1024) { + if (!wl_array_add(array, 1024)) { + errno = ENOMEM; + return -1; + } + array->size -= 1024; + } + data = (char *)array->data + array->size; + /* Leave one char in case the caller needs space for a + * null terminator */ + size = array->alloc - array->size - 1; + do { + len = read(fd, data, size); + } while (len == -1 && errno == EINTR); + + if (len == -1) + return -1; + + array->size += len; + + return len; +} From 89d0d90306f9323cbfa904a7e0e777b701ed16ff Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 27 Apr 2022 23:37:48 +0100 Subject: [PATCH 267/609] pixel-formats: Add GL types for 16bpc formats Carried over from gl-renderer's equivalent code. Signed-off-by: Daniel Stone --- libweston/pixel-formats.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/libweston/pixel-formats.c b/libweston/pixel-formats.c index 5e4cb1ae..2373add6 100644 --- a/libweston/pixel-formats.c +++ b/libweston/pixel-formats.c @@ -48,6 +48,7 @@ #include #include #include +#include #define GL_FORMAT(fmt) .gl_format = (fmt) #define GL_TYPE(type) .gl_type = (type) #define SAMPLER_TYPE(type) .sampler_type = (type) @@ -341,20 +342,36 @@ static const struct pixel_format_info pixel_format_table[] = { { DRM_FORMAT(XBGR16161616), BITS_RGBA_FIXED(16, 16, 16, 0), +#if __BYTE_ORDER__ == __LITTLE_ENDIAN + GL_FORMAT(GL_RGBA16_EXT), + GL_TYPE(GL_UNSIGNED_SHORT), +#endif }, { DRM_FORMAT(ABGR16161616), BITS_RGBA_FIXED(16, 16, 16, 16), .opaque_substitute = DRM_FORMAT_XBGR16161616, +#if __BYTE_ORDER__ == __LITTLE_ENDIAN + GL_FORMAT(GL_RGBA16_EXT), + GL_TYPE(GL_UNSIGNED_SHORT), +#endif }, { DRM_FORMAT(XBGR16161616F), BITS_RGBA_FLOAT(16, 16, 16, 0), +#if __BYTE_ORDER__ == __LITTLE_ENDIAN + GL_FORMAT(GL_RGBA16F), + GL_TYPE(GL_HALF_FLOAT), +#endif }, { DRM_FORMAT(ABGR16161616F), BITS_RGBA_FLOAT(16, 16, 16, 16), .opaque_substitute = DRM_FORMAT_XBGR16161616F, +#if __BYTE_ORDER__ == __LITTLE_ENDIAN + GL_FORMAT(GL_RGBA16F), + GL_TYPE(GL_HALF_FLOAT), +#endif }, { DRM_FORMAT(XRGB16161616F), From d696f8df1f5ce8446fdb5c26f8d7ed154a088fbc Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 27 Apr 2022 00:53:49 +0100 Subject: [PATCH 268/609] gl-renderer: Use ARRAY_COPY for buffer state We've got a nice shiny ARRAY_COPY macro, so use it rather than memcpy or hand-unrolled assignments. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 34fde3a3..2078d155 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -2096,12 +2096,10 @@ unsupported: gb->pitch = pitch; gb->shader_variant = shader_variant; - memcpy(gb->offset, offset, sizeof(offset)); - memcpy(gb->hsub, hsub, sizeof(hsub)); - memcpy(gb->vsub, vsub, sizeof(vsub)); - gb->gl_format[0] = gl_format[0]; - gb->gl_format[1] = gl_format[1]; - gb->gl_format[2] = gl_format[2]; + ARRAY_COPY(gb->offset, offset); + ARRAY_COPY(gb->hsub, hsub); + ARRAY_COPY(gb->vsub, vsub); + ARRAY_COPY(gb->gl_format, gl_format); gb->gl_pixel_type = gl_pixel_type; gb->needs_full_upload = true; From 2e6827d70e496f7f6a30a375ad371abe4fc787d1 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 27 Apr 2022 01:10:31 +0100 Subject: [PATCH 269/609] gl-renderer: Ensure SHM buffer format stays the same Rather than checking all the pixel-format components which are currently duplicated inside gl-renderer, just check for equality of the pixel format itself, which will become useful as we remove some of the duplicate content. This means that the texture storage will now be reallocated when clients switch between pixel formats which could've had compatible GL storage (e.g. XRGB <-> ARGB) on the same surface. However this does not seem like a case worth optimising, and simplifies the code somewhat. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 2078d155..488619be 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -2066,18 +2066,8 @@ unsupported: if (gs->buffer && buffer->width == old_buffer->width && buffer->height == old_buffer->height && - hsub[0] == gs->buffer->hsub[0] && - hsub[1] == gs->buffer->hsub[1] && - hsub[2] == gs->buffer->hsub[2] && - vsub[0] == gs->buffer->vsub[0] && - vsub[1] == gs->buffer->vsub[1] && - vsub[2] == gs->buffer->vsub[2] && - gl_format[0] == gs->buffer->gl_format[0] && - gl_format[1] == gs->buffer->gl_format[1] && - gl_format[2] == gs->buffer->gl_format[2] && - gl_pixel_type == gs->buffer->gl_pixel_type) { + buffer->pixel_format == old_buffer->pixel_format) { gs->buffer->pitch = pitch; - gs->buffer->shader_variant = shader_variant; memcpy(gs->buffer->offset, offset, sizeof(offset)); return true; } From 38f933dd463bd6639156a31c0185976e11bd3304 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 28 Apr 2022 15:15:04 +0100 Subject: [PATCH 270/609] gl-renderer: Move GL compatibility workarounds out of per-format Instead of checking for each format whether we need compatibility workarounds for GL implementations not supporting ES3.x or when GL_EXT_texture_rg isn't present, have each format declare the ideal case and fix it up later. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 59 +++++++++++++++++------------ 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 488619be..10d178a0 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -1911,6 +1911,7 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) int hsub[3] = { 1, 0, 0 }; int vsub[3] = { 1, 0, 0 }; int num_planes; + unsigned int i; bool using_glesv2 = gr->gl_version < gr_gl_version(3, 0); num_planes = 1; @@ -1941,7 +1942,7 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) } shader_variant = SHADER_VARIANT_RGBA; pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; - gl_format[0] = using_glesv2 ? GL_RGBA : GL_RGB10_A2; + gl_format[0] = GL_RGB10_A2; gl_pixel_type = GL_UNSIGNED_INT_2_10_10_10_REV_EXT; break; case WL_SHM_FORMAT_XBGR2101010: @@ -1950,7 +1951,7 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) } shader_variant = SHADER_VARIANT_RGBX; pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; - gl_format[0] = using_glesv2 ? GL_RGBA : GL_RGB10_A2; + gl_format[0] = GL_RGB10_A2; gl_pixel_type = GL_UNSIGNED_INT_2_10_10_10_REV_EXT; break; case WL_SHM_FORMAT_ABGR16161616F: @@ -1999,17 +2000,12 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) (buffer->height / vsub[1]); hsub[2] = 2; vsub[2] = 2; - if (gr->has_gl_texture_rg) { - gl_format[0] = GL_R8_EXT; - gl_format[1] = GL_R8_EXT; - gl_format[2] = GL_R8_EXT; - } else { - gl_format[0] = GL_LUMINANCE; - gl_format[1] = GL_LUMINANCE; - gl_format[2] = GL_LUMINANCE; - } + gl_format[0] = GL_R8_EXT; + gl_format[1] = GL_R8_EXT; + gl_format[2] = GL_R8_EXT; break; case WL_SHM_FORMAT_NV12: + shader_variant = SHADER_VARIANT_Y_UV; pitch = wl_shm_buffer_get_stride(shm_buffer); gl_pixel_type = GL_UNSIGNED_BYTE; num_planes = 2; @@ -2017,15 +2013,8 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) (buffer->height / vsub[0]); hsub[1] = 2; vsub[1] = 2; - if (gr->has_gl_texture_rg) { - shader_variant = SHADER_VARIANT_Y_UV; - gl_format[0] = GL_R8_EXT; - gl_format[1] = GL_RG8_EXT; - } else { - shader_variant = SHADER_VARIANT_Y_XUXV; - gl_format[0] = GL_LUMINANCE; - gl_format[1] = GL_LUMINANCE_ALPHA; - } + gl_format[0] = GL_R8_EXT; + gl_format[1] = GL_RG8_EXT; break; case WL_SHM_FORMAT_YUYV: shader_variant = SHADER_VARIANT_Y_XUXV; @@ -2035,10 +2024,7 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) offset[1] = 0; hsub[1] = 2; vsub[1] = 1; - if (gr->has_gl_texture_rg) - gl_format[0] = GL_RG8_EXT; - else - gl_format[0] = GL_LUMINANCE_ALPHA; + gl_format[0] = GL_RG8_EXT; gl_format[1] = GL_BGRA_EXT; break; case WL_SHM_FORMAT_XYUV8888: @@ -2058,6 +2044,31 @@ unsupported: return false; } + for (i = 0; i < ARRAY_LENGTH(gb->gl_format); i++) { + /* Fall back to GL_RGBA for 10bpc formats on ES2 */ + if (using_glesv2 && gl_format[i] == GL_RGB10_A2) { + assert(gl_pixel_type == GL_UNSIGNED_INT_2_10_10_10_REV_EXT); + gl_format[i] = GL_RGBA; + } + + /* Fall back to old luminance-based formats if we don't have + * GL_EXT_texture_rg, which requires different sampling for + * two-component formats. */ + if (!gr->has_gl_texture_rg && gl_format[i] == GL_R8_EXT) { + assert(gl_pixel_type == GL_UNSIGNED_BYTE); + assert(shader_variant == SHADER_VARIANT_Y_U_V || + shader_variant == SHADER_VARIANT_Y_UV); + gl_format[i] = GL_LUMINANCE; + } + if (!gr->has_gl_texture_rg && gl_format[i] == GL_RG8_EXT) { + assert(gl_pixel_type == GL_UNSIGNED_BYTE); + assert(shader_variant == SHADER_VARIANT_Y_UV || + shader_variant == SHADER_VARIANT_Y_XUXV); + shader_variant = SHADER_VARIANT_Y_XUXV; + gl_format[i] = GL_LUMINANCE_ALPHA; + } + } + /* If this surface previously had a SHM buffer, its gl_buffer_state will * be speculatively retained. Check to see if we can reuse it rather * than allocating a new one. */ From badd774c2845d25cfcbf69775aa486b84363d30c Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 27 Apr 2022 23:49:46 +0100 Subject: [PATCH 271/609] gl-renderer: Use pixel-formats GL format for single-planar formats pixel-formats already stores the gl_format, at least for single-planar formats; use that instead of storing our own copies. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 10d178a0..4a935eec 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -1792,6 +1792,7 @@ gl_renderer_flush_damage(struct weston_surface *surface, glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, 0); glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, 0); wl_shm_buffer_begin_access(buffer->shm_buffer); + for (j = 0; j < gb->num_textures; j++) { glBindTexture(GL_TEXTURE_2D, gb->textures[j]); glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, @@ -1915,25 +1916,21 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) bool using_glesv2 = gr->gl_version < gr_gl_version(3, 0); num_planes = 1; + gl_format[0] = buffer->pixel_format->gl_format; + gl_pixel_type = buffer->pixel_format->gl_type; switch (wl_shm_buffer_get_format(shm_buffer)) { case WL_SHM_FORMAT_XRGB8888: shader_variant = SHADER_VARIANT_RGBX; pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; - gl_format[0] = GL_BGRA_EXT; - gl_pixel_type = GL_UNSIGNED_BYTE; break; case WL_SHM_FORMAT_ARGB8888: shader_variant = SHADER_VARIANT_RGBA; pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; - gl_format[0] = GL_BGRA_EXT; - gl_pixel_type = GL_UNSIGNED_BYTE; break; case WL_SHM_FORMAT_RGB565: shader_variant = SHADER_VARIANT_RGBX; pitch = wl_shm_buffer_get_stride(shm_buffer) / 2; - gl_format[0] = GL_RGB; - gl_pixel_type = GL_UNSIGNED_SHORT_5_6_5; break; #if __BYTE_ORDER == __LITTLE_ENDIAN case WL_SHM_FORMAT_ABGR2101010: @@ -1942,8 +1939,6 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) } shader_variant = SHADER_VARIANT_RGBA; pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; - gl_format[0] = GL_RGB10_A2; - gl_pixel_type = GL_UNSIGNED_INT_2_10_10_10_REV_EXT; break; case WL_SHM_FORMAT_XBGR2101010: if (!gr->has_texture_type_2_10_10_10_rev) { @@ -1951,40 +1946,30 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) } shader_variant = SHADER_VARIANT_RGBX; pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; - gl_format[0] = GL_RGB10_A2; - gl_pixel_type = GL_UNSIGNED_INT_2_10_10_10_REV_EXT; break; case WL_SHM_FORMAT_ABGR16161616F: if (!gr->gl_supports_color_transforms) goto unsupported; shader_variant = SHADER_VARIANT_RGBA; pitch = wl_shm_buffer_get_stride(shm_buffer) / 8; - gl_format[0] = GL_RGBA16F; - gl_pixel_type = GL_HALF_FLOAT; break; case WL_SHM_FORMAT_XBGR16161616F: if (!gr->gl_supports_color_transforms) goto unsupported; shader_variant = SHADER_VARIANT_RGBX; pitch = wl_shm_buffer_get_stride(shm_buffer) / 8; - gl_format[0] = GL_RGBA16F; - gl_pixel_type = GL_HALF_FLOAT; break; case WL_SHM_FORMAT_ABGR16161616: if (!gr->has_texture_norm16) goto unsupported; shader_variant = SHADER_VARIANT_RGBA; pitch = wl_shm_buffer_get_stride(shm_buffer) / 8; - gl_format[0] = GL_RGBA16_EXT; - gl_pixel_type = GL_UNSIGNED_SHORT; break; case WL_SHM_FORMAT_XBGR16161616: if (!gr->has_texture_norm16) goto unsupported; shader_variant = SHADER_VARIANT_RGBX; pitch = wl_shm_buffer_get_stride(shm_buffer) / 8; - gl_format[0] = GL_RGBA16_EXT; - gl_pixel_type = GL_UNSIGNED_SHORT; break; #endif case WL_SHM_FORMAT_YUV420: From 2c40260397003bc56042d74279b8ab18f2de6e1b Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 28 Apr 2022 00:18:42 +0100 Subject: [PATCH 272/609] gl-renderer: Remove unsupported-SHM-format fallback Clients can't reach this because libwayland-server already checks whether the format is supported. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 4a935eec..e8d34df9 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -1934,40 +1934,26 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) break; #if __BYTE_ORDER == __LITTLE_ENDIAN case WL_SHM_FORMAT_ABGR2101010: - if (!gr->has_texture_type_2_10_10_10_rev) { - goto unsupported; - } shader_variant = SHADER_VARIANT_RGBA; pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; break; case WL_SHM_FORMAT_XBGR2101010: - if (!gr->has_texture_type_2_10_10_10_rev) { - goto unsupported; - } shader_variant = SHADER_VARIANT_RGBX; pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; break; case WL_SHM_FORMAT_ABGR16161616F: - if (!gr->gl_supports_color_transforms) - goto unsupported; shader_variant = SHADER_VARIANT_RGBA; pitch = wl_shm_buffer_get_stride(shm_buffer) / 8; break; case WL_SHM_FORMAT_XBGR16161616F: - if (!gr->gl_supports_color_transforms) - goto unsupported; shader_variant = SHADER_VARIANT_RGBX; pitch = wl_shm_buffer_get_stride(shm_buffer) / 8; break; case WL_SHM_FORMAT_ABGR16161616: - if (!gr->has_texture_norm16) - goto unsupported; shader_variant = SHADER_VARIANT_RGBA; pitch = wl_shm_buffer_get_stride(shm_buffer) / 8; break; case WL_SHM_FORMAT_XBGR16161616: - if (!gr->has_texture_norm16) - goto unsupported; shader_variant = SHADER_VARIANT_RGBX; pitch = wl_shm_buffer_get_stride(shm_buffer) / 8; break; @@ -2023,7 +2009,6 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) gl_pixel_type = GL_UNSIGNED_BYTE; break; default: -unsupported: weston_log("warning: unknown or unsupported shm buffer format: %08x\n", wl_shm_buffer_get_format(shm_buffer)); return false; From d37d73a9a07728f26555d4fbf3c93b438fc87c80 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 28 Apr 2022 00:20:06 +0100 Subject: [PATCH 273/609] gl-renderer: Use DRM format codes instead of SHM Since the buffer now has a pixel_format hook, just use that consistently instead of the SHM code. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index e8d34df9..478a631b 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -1919,46 +1919,46 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) gl_format[0] = buffer->pixel_format->gl_format; gl_pixel_type = buffer->pixel_format->gl_type; - switch (wl_shm_buffer_get_format(shm_buffer)) { - case WL_SHM_FORMAT_XRGB8888: + switch (buffer->pixel_format->format) { + case DRM_FORMAT_XRGB8888: shader_variant = SHADER_VARIANT_RGBX; pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; break; - case WL_SHM_FORMAT_ARGB8888: + case DRM_FORMAT_ARGB8888: shader_variant = SHADER_VARIANT_RGBA; pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; break; - case WL_SHM_FORMAT_RGB565: + case DRM_FORMAT_RGB565: shader_variant = SHADER_VARIANT_RGBX; pitch = wl_shm_buffer_get_stride(shm_buffer) / 2; break; #if __BYTE_ORDER == __LITTLE_ENDIAN - case WL_SHM_FORMAT_ABGR2101010: + case DRM_FORMAT_ABGR2101010: shader_variant = SHADER_VARIANT_RGBA; pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; break; - case WL_SHM_FORMAT_XBGR2101010: + case DRM_FORMAT_XBGR2101010: shader_variant = SHADER_VARIANT_RGBX; pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; break; - case WL_SHM_FORMAT_ABGR16161616F: + case DRM_FORMAT_ABGR16161616F: shader_variant = SHADER_VARIANT_RGBA; pitch = wl_shm_buffer_get_stride(shm_buffer) / 8; break; - case WL_SHM_FORMAT_XBGR16161616F: + case DRM_FORMAT_XBGR16161616F: shader_variant = SHADER_VARIANT_RGBX; pitch = wl_shm_buffer_get_stride(shm_buffer) / 8; break; - case WL_SHM_FORMAT_ABGR16161616: + case DRM_FORMAT_ABGR16161616: shader_variant = SHADER_VARIANT_RGBA; pitch = wl_shm_buffer_get_stride(shm_buffer) / 8; break; - case WL_SHM_FORMAT_XBGR16161616: + case DRM_FORMAT_XBGR16161616: shader_variant = SHADER_VARIANT_RGBX; pitch = wl_shm_buffer_get_stride(shm_buffer) / 8; break; #endif - case WL_SHM_FORMAT_YUV420: + case DRM_FORMAT_YUV420: shader_variant = SHADER_VARIANT_Y_U_V; pitch = wl_shm_buffer_get_stride(shm_buffer); gl_pixel_type = GL_UNSIGNED_BYTE; @@ -1975,7 +1975,7 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) gl_format[1] = GL_R8_EXT; gl_format[2] = GL_R8_EXT; break; - case WL_SHM_FORMAT_NV12: + case DRM_FORMAT_NV12: shader_variant = SHADER_VARIANT_Y_UV; pitch = wl_shm_buffer_get_stride(shm_buffer); gl_pixel_type = GL_UNSIGNED_BYTE; @@ -1987,7 +1987,7 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) gl_format[0] = GL_R8_EXT; gl_format[1] = GL_RG8_EXT; break; - case WL_SHM_FORMAT_YUYV: + case DRM_FORMAT_YUYV: shader_variant = SHADER_VARIANT_Y_XUXV; pitch = wl_shm_buffer_get_stride(shm_buffer) / 2; gl_pixel_type = GL_UNSIGNED_BYTE; @@ -1998,7 +1998,7 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) gl_format[0] = GL_RG8_EXT; gl_format[1] = GL_BGRA_EXT; break; - case WL_SHM_FORMAT_XYUV8888: + case DRM_FORMAT_XYUV8888: /* * [31:0] X:Y:Cb:Cr 8:8:8:8 little endian * a:b: g: r in SHADER_VARIANT_XYUV From c54eace91d746395e5ed7d464420161b08312f9c Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 28 Apr 2022 14:43:39 +0100 Subject: [PATCH 274/609] pixel-formats: Rename addfb_legacy_depth 'depth' isn't actually used to determine the bit depth of meaningful components generally, but specifically to determine whether we can use the legacy drmModeAddFB (pre-AddFB2) with those formats. Rename the member to make it more clear what it's used for. Signed-off-by: Daniel Stone --- libweston/backend-drm/fb.c | 6 +++--- libweston/pixel-formats.c | 10 +++++----- libweston/pixel-formats.h | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/libweston/backend-drm/fb.c b/libweston/backend-drm/fb.c index 328d1f85..202c4818 100644 --- a/libweston/backend-drm/fb.c +++ b/libweston/backend-drm/fb.c @@ -98,7 +98,7 @@ drm_fb_addfb(struct drm_backend *b, struct drm_fb *fb) /* Legacy AddFB can't always infer the format from depth/bpp alone, so * check if our format is one of the lucky ones. */ - if (!fb->format->depth || !fb->format->bpp) + if (!fb->format->addfb_legacy_depth || !fb->format->bpp) return ret; /* Cannot fall back to AddFB for multi-planar formats either. */ @@ -106,7 +106,7 @@ drm_fb_addfb(struct drm_backend *b, struct drm_fb *fb) return ret; ret = drmModeAddFB(fb->fd, fb->width, fb->height, - fb->format->depth, fb->format->bpp, + fb->format->addfb_legacy_depth, fb->format->bpp, fb->strides[0], fb->handles[0], &fb->fb_id); return ret; } @@ -134,7 +134,7 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height, goto err_fb; } - if (!fb->format->depth || !fb->format->bpp) { + if (!fb->format->addfb_legacy_depth || !fb->format->bpp) { weston_log("format 0x%lx is not compatible with dumb buffers\n", (unsigned long) format); goto err_fb; diff --git a/libweston/pixel-formats.c b/libweston/pixel-formats.c index 2373add6..1e155ee6 100644 --- a/libweston/pixel-formats.c +++ b/libweston/pixel-formats.c @@ -129,7 +129,7 @@ static const struct pixel_format_info pixel_format_table[] = { { DRM_FORMAT(XRGB1555), BITS_RGBA_FIXED(5, 5, 5, 0), - .depth = 15, + .addfb_legacy_depth = 15, .bpp = 16, }, { @@ -175,7 +175,7 @@ static const struct pixel_format_info pixel_format_table[] = { { DRM_FORMAT(RGB565), BITS_RGBA_FIXED(5, 6, 5, 0), - .depth = 16, + .addfb_legacy_depth = 16, .bpp = 16, # if __BYTE_ORDER == __LITTLE_ENDIAN GL_FORMAT(GL_RGB), @@ -200,7 +200,7 @@ static const struct pixel_format_info pixel_format_table[] = { { DRM_FORMAT(XRGB8888), BITS_RGBA_FIXED(8, 8, 8, 0), - .depth = 24, + .addfb_legacy_depth = 24, .bpp = 32, GL_FORMAT(GL_BGRA_EXT), GL_TYPE(GL_UNSIGNED_BYTE), @@ -214,7 +214,7 @@ static const struct pixel_format_info pixel_format_table[] = { DRM_FORMAT(ARGB8888), BITS_RGBA_FIXED(8, 8, 8, 8), .opaque_substitute = DRM_FORMAT_XRGB8888, - .depth = 32, + .addfb_legacy_depth = 32, .bpp = 32, GL_FORMAT(GL_BGRA_EXT), GL_TYPE(GL_UNSIGNED_BYTE), @@ -288,7 +288,7 @@ static const struct pixel_format_info pixel_format_table[] = { { DRM_FORMAT(XRGB2101010), BITS_RGBA_FIXED(10, 10, 10, 0), - .depth = 30, + .addfb_legacy_depth = 30, .bpp = 32, #if __BYTE_ORDER == __LITTLE_ENDIAN PIXMAN_FMT(x2r10g10b10), diff --git a/libweston/pixel-formats.h b/libweston/pixel-formats.h index 0b1a5f58..f1204a9f 100644 --- a/libweston/pixel-formats.h +++ b/libweston/pixel-formats.h @@ -76,7 +76,7 @@ struct pixel_format_info { /** If set, this format can be used with the legacy drmModeAddFB() * function (not AddFB2), using this and the bpp member. */ - int depth; + int addfb_legacy_depth; /** See 'depth' member above. */ int bpp; From 2ade128ae28e72952c5cb221e5c617f5eb7606ef Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 28 Apr 2022 00:35:55 +0100 Subject: [PATCH 275/609] pixel-formats: Fill in bpp everywhere Doing this allows us to get rid of some open-coded per-format calculations inside gl-renderer. Signed-off-by: Daniel Stone --- libweston/pixel-formats.c | 38 ++++++++++++++++++++++++++++++++++++++ libweston/pixel-formats.h | 3 ++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/libweston/pixel-formats.c b/libweston/pixel-formats.c index 1e155ee6..16b95e20 100644 --- a/libweston/pixel-formats.c +++ b/libweston/pixel-formats.c @@ -85,24 +85,29 @@ static const struct pixel_format_info pixel_format_table[] = { { DRM_FORMAT(XRGB4444), BITS_RGBA_FIXED(4, 4, 4, 0), + .bpp = 16, }, { DRM_FORMAT(ARGB4444), BITS_RGBA_FIXED(4, 4, 4, 4), + .bpp = 16, .opaque_substitute = DRM_FORMAT_XRGB4444, }, { DRM_FORMAT(XBGR4444), BITS_RGBA_FIXED(4, 4, 4, 0), + .bpp = 16, }, { DRM_FORMAT(ABGR4444), BITS_RGBA_FIXED(4, 4, 4, 4), + .bpp = 16, .opaque_substitute = DRM_FORMAT_XBGR4444, }, { DRM_FORMAT(RGBX4444), BITS_RGBA_FIXED(4, 4, 4, 0), + .bpp = 16, # if __BYTE_ORDER == __LITTLE_ENDIAN GL_FORMAT(GL_RGBA), GL_TYPE(GL_UNSIGNED_SHORT_4_4_4_4), @@ -111,6 +116,7 @@ static const struct pixel_format_info pixel_format_table[] = { { DRM_FORMAT(RGBA4444), BITS_RGBA_FIXED(4, 4, 4, 4), + .bpp = 16, .opaque_substitute = DRM_FORMAT_RGBX4444, # if __BYTE_ORDER == __LITTLE_ENDIAN GL_FORMAT(GL_RGBA), @@ -120,10 +126,12 @@ static const struct pixel_format_info pixel_format_table[] = { { DRM_FORMAT(BGRX4444), BITS_RGBA_FIXED(4, 4, 4, 0), + .bpp = 16, }, { DRM_FORMAT(BGRA4444), BITS_RGBA_FIXED(4, 4, 4, 4), + .bpp = 16, .opaque_substitute = DRM_FORMAT_BGRX4444, }, { @@ -135,20 +143,24 @@ static const struct pixel_format_info pixel_format_table[] = { { DRM_FORMAT(ARGB1555), BITS_RGBA_FIXED(5, 5, 5, 1), + .bpp = 16, .opaque_substitute = DRM_FORMAT_XRGB1555, }, { DRM_FORMAT(XBGR1555), BITS_RGBA_FIXED(5, 5, 5, 0), + .bpp = 16, }, { DRM_FORMAT(ABGR1555), BITS_RGBA_FIXED(5, 5, 5, 1), + .bpp = 16, .opaque_substitute = DRM_FORMAT_XBGR1555, }, { DRM_FORMAT(RGBX5551), BITS_RGBA_FIXED(5, 5, 5, 0), + .bpp = 16, # if __BYTE_ORDER == __LITTLE_ENDIAN GL_FORMAT(GL_RGBA), GL_TYPE(GL_UNSIGNED_SHORT_5_5_5_1), @@ -157,6 +169,7 @@ static const struct pixel_format_info pixel_format_table[] = { { DRM_FORMAT(RGBA5551), BITS_RGBA_FIXED(5, 5, 5, 1), + .bpp = 16, .opaque_substitute = DRM_FORMAT_RGBX5551, # if __BYTE_ORDER == __LITTLE_ENDIAN GL_FORMAT(GL_RGBA), @@ -166,10 +179,12 @@ static const struct pixel_format_info pixel_format_table[] = { { DRM_FORMAT(BGRX5551), BITS_RGBA_FIXED(5, 5, 5, 0), + .bpp = 16, }, { DRM_FORMAT(BGRA5551), BITS_RGBA_FIXED(5, 5, 5, 1), + .bpp = 16, .opaque_substitute = DRM_FORMAT_BGRX5551, }, { @@ -186,14 +201,17 @@ static const struct pixel_format_info pixel_format_table[] = { { DRM_FORMAT(BGR565), BITS_RGBA_FIXED(5, 6, 5, 0), + .bpp = 16, }, { DRM_FORMAT(RGB888), BITS_RGBA_FIXED(8, 8, 8, 0), + .bpp = 24, }, { DRM_FORMAT(BGR888), BITS_RGBA_FIXED(8, 8, 8, 0), + .bpp = 24, GL_FORMAT(GL_RGB), GL_TYPE(GL_UNSIGNED_BYTE), }, @@ -227,6 +245,7 @@ static const struct pixel_format_info pixel_format_table[] = { { DRM_FORMAT(XBGR8888), BITS_RGBA_FIXED(8, 8, 8, 0), + .bpp = 32, GL_FORMAT(GL_RGBA), GL_TYPE(GL_UNSIGNED_BYTE), #if __BYTE_ORDER == __LITTLE_ENDIAN @@ -238,6 +257,7 @@ static const struct pixel_format_info pixel_format_table[] = { { DRM_FORMAT(ABGR8888), BITS_RGBA_FIXED(8, 8, 8, 8), + .bpp = 32, .opaque_substitute = DRM_FORMAT_XBGR8888, GL_FORMAT(GL_RGBA), GL_TYPE(GL_UNSIGNED_BYTE), @@ -250,6 +270,7 @@ static const struct pixel_format_info pixel_format_table[] = { { DRM_FORMAT(RGBX8888), BITS_RGBA_FIXED(8, 8, 8, 0), + .bpp = 32, #if __BYTE_ORDER == __LITTLE_ENDIAN PIXMAN_FMT(r8g8b8x8), #else @@ -259,6 +280,7 @@ static const struct pixel_format_info pixel_format_table[] = { { DRM_FORMAT(RGBA8888), BITS_RGBA_FIXED(8, 8, 8, 8), + .bpp = 32, .opaque_substitute = DRM_FORMAT_RGBX8888, #if __BYTE_ORDER == __LITTLE_ENDIAN PIXMAN_FMT(r8g8b8a8), @@ -269,6 +291,7 @@ static const struct pixel_format_info pixel_format_table[] = { { DRM_FORMAT(BGRX8888), BITS_RGBA_FIXED(8, 8, 8, 0), + .bpp = 32, #if __BYTE_ORDER == __LITTLE_ENDIAN PIXMAN_FMT(b8g8r8x8), #else @@ -278,6 +301,7 @@ static const struct pixel_format_info pixel_format_table[] = { { DRM_FORMAT(BGRA8888), BITS_RGBA_FIXED(8, 8, 8, 8), + .bpp = 32, .opaque_substitute = DRM_FORMAT_BGRX8888, #if __BYTE_ORDER == __LITTLE_ENDIAN PIXMAN_FMT(b8g8r8a8), @@ -297,6 +321,7 @@ static const struct pixel_format_info pixel_format_table[] = { { DRM_FORMAT(ARGB2101010), BITS_RGBA_FIXED(10, 10, 10, 2), + .bpp = 32, .opaque_substitute = DRM_FORMAT_XRGB2101010, #if __BYTE_ORDER == __LITTLE_ENDIAN PIXMAN_FMT(a2r10g10b10), @@ -305,6 +330,7 @@ static const struct pixel_format_info pixel_format_table[] = { { DRM_FORMAT(XBGR2101010), BITS_RGBA_FIXED(10, 10, 10, 0), + .bpp = 32, # if __BYTE_ORDER == __LITTLE_ENDIAN GL_FORMAT(GL_RGBA), GL_TYPE(GL_UNSIGNED_INT_2_10_10_10_REV_EXT), @@ -314,6 +340,7 @@ static const struct pixel_format_info pixel_format_table[] = { { DRM_FORMAT(ABGR2101010), BITS_RGBA_FIXED(10, 10, 10, 2), + .bpp = 32, .opaque_substitute = DRM_FORMAT_XBGR2101010, # if __BYTE_ORDER == __LITTLE_ENDIAN GL_FORMAT(GL_RGBA), @@ -324,24 +351,29 @@ static const struct pixel_format_info pixel_format_table[] = { { DRM_FORMAT(RGBX1010102), BITS_RGBA_FIXED(10, 10, 10, 0), + .bpp = 32, }, { DRM_FORMAT(RGBA1010102), BITS_RGBA_FIXED(10, 10, 10, 2), + .bpp = 32, .opaque_substitute = DRM_FORMAT_RGBX1010102, }, { DRM_FORMAT(BGRX1010102), BITS_RGBA_FIXED(10, 10, 10, 0), + .bpp = 32, }, { DRM_FORMAT(BGRA1010102), BITS_RGBA_FIXED(10, 10, 10, 2), + .bpp = 32, .opaque_substitute = DRM_FORMAT_BGRX1010102, }, { DRM_FORMAT(XBGR16161616), BITS_RGBA_FIXED(16, 16, 16, 0), + .bpp = 64, #if __BYTE_ORDER__ == __LITTLE_ENDIAN GL_FORMAT(GL_RGBA16_EXT), GL_TYPE(GL_UNSIGNED_SHORT), @@ -350,6 +382,7 @@ static const struct pixel_format_info pixel_format_table[] = { { DRM_FORMAT(ABGR16161616), BITS_RGBA_FIXED(16, 16, 16, 16), + .bpp = 64, .opaque_substitute = DRM_FORMAT_XBGR16161616, #if __BYTE_ORDER__ == __LITTLE_ENDIAN GL_FORMAT(GL_RGBA16_EXT), @@ -359,6 +392,7 @@ static const struct pixel_format_info pixel_format_table[] = { { DRM_FORMAT(XBGR16161616F), BITS_RGBA_FLOAT(16, 16, 16, 0), + .bpp = 64, #if __BYTE_ORDER__ == __LITTLE_ENDIAN GL_FORMAT(GL_RGBA16F), GL_TYPE(GL_HALF_FLOAT), @@ -367,6 +401,7 @@ static const struct pixel_format_info pixel_format_table[] = { { DRM_FORMAT(ABGR16161616F), BITS_RGBA_FLOAT(16, 16, 16, 16), + .bpp = 64, .opaque_substitute = DRM_FORMAT_XBGR16161616F, #if __BYTE_ORDER__ == __LITTLE_ENDIAN GL_FORMAT(GL_RGBA16F), @@ -376,10 +411,12 @@ static const struct pixel_format_info pixel_format_table[] = { { DRM_FORMAT(XRGB16161616F), BITS_RGBA_FLOAT(16, 16, 16, 0), + .bpp = 64, }, { DRM_FORMAT(ARGB16161616F), BITS_RGBA_FLOAT(16, 16, 16, 16), + .bpp = 64, .opaque_substitute = DRM_FORMAT_XRGB16161616F, }, { @@ -524,6 +561,7 @@ static const struct pixel_format_info pixel_format_table[] = { }, { DRM_FORMAT(XYUV8888), + .bpp = 32, }, }; diff --git a/libweston/pixel-formats.h b/libweston/pixel-formats.h index f1204a9f..6e8aafde 100644 --- a/libweston/pixel-formats.h +++ b/libweston/pixel-formats.h @@ -78,7 +78,8 @@ struct pixel_format_info { * function (not AddFB2), using this and the bpp member. */ int addfb_legacy_depth; - /** See 'depth' member above. */ + /** Number of bits required to store a single pixel, for + * single-planar formats. */ int bpp; /** Horizontal subsampling; if non-zero, divide the width by this From e08df66bd3dc5aad7f8b92be819c5e15603c4181 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 28 Apr 2022 00:43:24 +0100 Subject: [PATCH 276/609] gl-renderer: Use pixel-formats data for RGB formats No need to open-code bits per pixel and shader variants. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 62 ++++++++--------------------- 1 file changed, 16 insertions(+), 46 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 478a631b..7000920a 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -1911,53 +1911,12 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) int offset[3] = { 0, 0, 0 }; int hsub[3] = { 1, 0, 0 }; int vsub[3] = { 1, 0, 0 }; - int num_planes; unsigned int i; + int num_planes = 1; + int bpp; bool using_glesv2 = gr->gl_version < gr_gl_version(3, 0); - num_planes = 1; - gl_format[0] = buffer->pixel_format->gl_format; - gl_pixel_type = buffer->pixel_format->gl_type; - switch (buffer->pixel_format->format) { - case DRM_FORMAT_XRGB8888: - shader_variant = SHADER_VARIANT_RGBX; - pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; - break; - case DRM_FORMAT_ARGB8888: - shader_variant = SHADER_VARIANT_RGBA; - pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; - break; - case DRM_FORMAT_RGB565: - shader_variant = SHADER_VARIANT_RGBX; - pitch = wl_shm_buffer_get_stride(shm_buffer) / 2; - break; -#if __BYTE_ORDER == __LITTLE_ENDIAN - case DRM_FORMAT_ABGR2101010: - shader_variant = SHADER_VARIANT_RGBA; - pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; - break; - case DRM_FORMAT_XBGR2101010: - shader_variant = SHADER_VARIANT_RGBX; - pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; - break; - case DRM_FORMAT_ABGR16161616F: - shader_variant = SHADER_VARIANT_RGBA; - pitch = wl_shm_buffer_get_stride(shm_buffer) / 8; - break; - case DRM_FORMAT_XBGR16161616F: - shader_variant = SHADER_VARIANT_RGBX; - pitch = wl_shm_buffer_get_stride(shm_buffer) / 8; - break; - case DRM_FORMAT_ABGR16161616: - shader_variant = SHADER_VARIANT_RGBA; - pitch = wl_shm_buffer_get_stride(shm_buffer) / 8; - break; - case DRM_FORMAT_XBGR16161616: - shader_variant = SHADER_VARIANT_RGBX; - pitch = wl_shm_buffer_get_stride(shm_buffer) / 8; - break; -#endif case DRM_FORMAT_YUV420: shader_variant = SHADER_VARIANT_Y_U_V; pitch = wl_shm_buffer_get_stride(shm_buffer); @@ -2009,9 +1968,20 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) gl_pixel_type = GL_UNSIGNED_BYTE; break; default: - weston_log("warning: unknown or unsupported shm buffer format: %08x\n", - wl_shm_buffer_get_format(shm_buffer)); - return false; + assert(pixel_format_get_plane_count(buffer->pixel_format) == 1); + + if (pixel_format_is_opaque(buffer->pixel_format)) + shader_variant = SHADER_VARIANT_RGBX; + else + shader_variant = SHADER_VARIANT_RGBA; + + bpp = buffer->pixel_format->bpp; + assert(bpp > 0 && !(bpp & 7)); + pitch = wl_shm_buffer_get_stride(shm_buffer) / (bpp / 8); + + gl_format[0] = buffer->pixel_format->gl_format; + gl_pixel_type = buffer->pixel_format->gl_type; + break; } for (i = 0; i < ARRAY_LENGTH(gb->gl_format); i++) { From 4161948da9bf490f094987f10be4777671b6894d Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 28 Apr 2022 01:00:34 +0100 Subject: [PATCH 277/609] pixel-formats: Add hsub and vsub helpers We already had these with effective width and height, but they're useful externally as well. Pull them out to a helper. Signed-off-by: Daniel Stone --- libweston/pixel-formats.c | 34 ++++++++++++++++++++++++---------- libweston/pixel-formats.h | 30 ++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 10 deletions(-) diff --git a/libweston/pixel-formats.c b/libweston/pixel-formats.c index 16b95e20..8d3d49a3 100644 --- a/libweston/pixel-formats.c +++ b/libweston/pixel-formats.c @@ -654,16 +654,34 @@ pixel_format_get_info_by_opaque_substitute(uint32_t format) return NULL; } +WL_EXPORT unsigned int +pixel_format_hsub(const struct pixel_format_info *info, + unsigned int plane) +{ + /* We don't support any formats where the first plane is subsampled. */ + if (plane == 0 || info->hsub == 0) + return 1; + + return info->hsub; +} + +WL_EXPORT unsigned int +pixel_format_vsub(const struct pixel_format_info *info, + unsigned int plane) +{ + /* We don't support any formats where the first plane is subsampled. */ + if (plane == 0 || info->vsub == 0) + return 1; + + return info->vsub; +} + WL_EXPORT unsigned int pixel_format_width_for_plane(const struct pixel_format_info *info, unsigned int plane, unsigned int width) { - /* We don't support any formats where the first plane is subsampled. */ - if (plane == 0 || !info->hsub) - return width; - - return width / info->hsub; + return width / pixel_format_hsub(info, plane); } WL_EXPORT unsigned int @@ -671,11 +689,7 @@ pixel_format_height_for_plane(const struct pixel_format_info *info, unsigned int plane, unsigned int height) { - /* We don't support any formats where the first plane is subsampled. */ - if (plane == 0 || !info->vsub) - return height; - - return height / info->vsub; + return height / pixel_format_vsub(info, plane); } #ifdef HAVE_HUMAN_FORMAT_MODIFIER diff --git a/libweston/pixel-formats.h b/libweston/pixel-formats.h index 6e8aafde..c14a3ed4 100644 --- a/libweston/pixel-formats.h +++ b/libweston/pixel-formats.h @@ -248,6 +248,36 @@ pixel_format_get_opaque_substitute(const struct pixel_format_info *format); const struct pixel_format_info * pixel_format_get_info_by_opaque_substitute(uint32_t format); +/** + * Return the horizontal subsampling factor for a given plane + * + * When horizontal subsampling is effective, a sampler bound to a secondary + * plane must bind the sampler with a smaller effective width. This function + * returns the subsampling factor to use for the given plane. + * + * @param format Pixel format info structure + * @param plane Zero-indexed plane number + * @returns Horizontal subsampling factor for the given plane + */ +unsigned int +pixel_format_hsub(const struct pixel_format_info *format, + unsigned int plane); + +/** + * Return the vertical subsampling factor for a given plane + * + * When vertical subsampling is effective, a sampler bound to a secondary + * plane must bind the sampler with a smaller effective height. This function + * returns the subsampling factor to use for the given plane. + * + * @param format Pixel format info structure + * @param plane Zero-indexed plane number + * @returns Vertical subsampling factor for the given plane + */ +unsigned int +pixel_format_vsub(const struct pixel_format_info *format, + unsigned int plane); + /** * Return the effective sampling width for a given plane * From 742f10f32c2ee265a3d3b7b512a724ebb72dc276 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 28 Apr 2022 14:34:43 +0100 Subject: [PATCH 278/609] gl-renderer: Use vsub for y offset in SHM If we're doing partial uploads from SHM buffers, we need to use the vertical subsampling factor rather than the horizontal for secondary planes. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 7000920a..2d9f1917 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -1824,7 +1824,7 @@ gl_renderer_flush_damage(struct weston_surface *surface, glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, r.x1 / gb->hsub[j]); glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, - r.y1 / gb->hsub[j]); + r.y1 / gb->vsub[j]); glTexSubImage2D(GL_TEXTURE_2D, 0, r.x1 / gb->hsub[j], r.y1 / gb->vsub[j], From c2cfadfce9c0b024183333efcd6db19f5859e626 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 28 Apr 2022 01:01:23 +0100 Subject: [PATCH 279/609] gl-renderer: Use hsub and vsub from pixel-formats No need to open-code everything here. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 51 +++++++++++------------------ 1 file changed, 19 insertions(+), 32 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 2d9f1917..7f3bb01f 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -159,8 +159,6 @@ struct gl_buffer_state { /* Extension needed for SHM YUV texture */ int offset[3]; /* offset per plane */ - int hsub[3]; /* horizontal subsampling per plane */ - int vsub[3]; /* vertical subsampling per plane */ GLuint textures[3]; int num_textures; @@ -1794,13 +1792,16 @@ gl_renderer_flush_damage(struct weston_surface *surface, wl_shm_buffer_begin_access(buffer->shm_buffer); for (j = 0; j < gb->num_textures; j++) { + int hsub = pixel_format_hsub(buffer->pixel_format, j); + int vsub = pixel_format_vsub(buffer->pixel_format, j); + glBindTexture(GL_TEXTURE_2D, gb->textures[j]); glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, - gb->pitch / gb->hsub[j]); + gb->pitch / hsub); glTexImage2D(GL_TEXTURE_2D, 0, gb->gl_format[j], - buffer->width / gb->hsub[j], - buffer->height / gb->vsub[j], + buffer->width / hsub, + buffer->height / vsub, 0, gl_format_from_internal(gb->gl_format[j]), gb->gl_pixel_type, @@ -1818,18 +1819,19 @@ gl_renderer_flush_damage(struct weston_surface *surface, r = weston_surface_to_buffer_rect(surface, rectangles[i]); for (j = 0; j < gb->num_textures; j++) { + int hsub = pixel_format_hsub(buffer->pixel_format, j); + int vsub = pixel_format_vsub(buffer->pixel_format, j); + glBindTexture(GL_TEXTURE_2D, gb->textures[j]); glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, - gb->pitch / gb->hsub[j]); - glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, - r.x1 / gb->hsub[j]); - glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, - r.y1 / gb->vsub[j]); + gb->pitch / hsub); + glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, r.x1 / hsub); + glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, r.y1 / vsub); glTexSubImage2D(GL_TEXTURE_2D, 0, - r.x1 / gb->hsub[j], - r.y1 / gb->vsub[j], - (r.x2 - r.x1) / gb->hsub[j], - (r.y2 - r.y1) / gb->vsub[j], + r.x1 / hsub, + r.y1 / vsub, + (r.x2 - r.x1) / hsub, + (r.y2 - r.y1) / vsub, gl_format_from_internal(gb->gl_format[j]), gb->gl_pixel_type, data + gb->offset[j]); @@ -1909,8 +1911,6 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) enum gl_shader_texture_variant shader_variant; int pitch; int offset[3] = { 0, 0, 0 }; - int hsub[3] = { 1, 0, 0 }; - int vsub[3] = { 1, 0, 0 }; unsigned int i; int num_planes = 1; int bpp; @@ -1922,14 +1922,8 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) pitch = wl_shm_buffer_get_stride(shm_buffer); gl_pixel_type = GL_UNSIGNED_BYTE; num_planes = 3; - offset[1] = offset[0] + (pitch / hsub[0]) * - (buffer->height / vsub[0]); - hsub[1] = 2; - vsub[1] = 2; - offset[2] = offset[1] + (pitch / hsub[1]) * - (buffer->height / vsub[1]); - hsub[2] = 2; - vsub[2] = 2; + offset[1] = offset[0] + pitch * buffer->height; + offset[2] = offset[1] + (pitch / 2) * (buffer->height / 2); gl_format[0] = GL_R8_EXT; gl_format[1] = GL_R8_EXT; gl_format[2] = GL_R8_EXT; @@ -1939,10 +1933,7 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) pitch = wl_shm_buffer_get_stride(shm_buffer); gl_pixel_type = GL_UNSIGNED_BYTE; num_planes = 2; - offset[1] = offset[0] + (pitch / hsub[0]) * - (buffer->height / vsub[0]); - hsub[1] = 2; - vsub[1] = 2; + offset[1] = offset[0] + pitch * buffer->height; gl_format[0] = GL_R8_EXT; gl_format[1] = GL_RG8_EXT; break; @@ -1952,8 +1943,6 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) gl_pixel_type = GL_UNSIGNED_BYTE; num_planes = 2; offset[1] = 0; - hsub[1] = 2; - vsub[1] = 1; gl_format[0] = GL_RG8_EXT; gl_format[1] = GL_BGRA_EXT; break; @@ -2038,8 +2027,6 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) gb->pitch = pitch; gb->shader_variant = shader_variant; ARRAY_COPY(gb->offset, offset); - ARRAY_COPY(gb->hsub, hsub); - ARRAY_COPY(gb->vsub, vsub); ARRAY_COPY(gb->gl_format, gl_format); gb->gl_pixel_type = gl_pixel_type; gb->needs_full_upload = true; From 820f3ae86620cbfc871f5bc870e1a884ad04d550 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 28 Apr 2022 01:21:15 +0100 Subject: [PATCH 280/609] gl-renderer: Add support for WL_SHM_FORMAT_YUV444 We support this as an explicit YUV fallback path in gl-renderer's dmabuf EGLImage import path, so might as well support it in the SHM path, given it's just YUV420 with no subsampling. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 6 +++++- tests/yuv-buffer-test.c | 32 ++++++++++++++++------------- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 7f3bb01f..7fcf1f9d 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -1918,12 +1918,15 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) switch (buffer->pixel_format->format) { case DRM_FORMAT_YUV420: + case DRM_FORMAT_YUV444: shader_variant = SHADER_VARIANT_Y_U_V; pitch = wl_shm_buffer_get_stride(shm_buffer); gl_pixel_type = GL_UNSIGNED_BYTE; num_planes = 3; offset[1] = offset[0] + pitch * buffer->height; - offset[2] = offset[1] + (pitch / 2) * (buffer->height / 2); + offset[2] = offset[1] + + (pitch / pixel_format_hsub(buffer->pixel_format, 1)) * + (buffer->height / pixel_format_vsub(buffer->pixel_format, 1)); gl_format[0] = GL_R8_EXT; gl_format[1] = GL_R8_EXT; gl_format[2] = GL_R8_EXT; @@ -3693,6 +3696,7 @@ gl_renderer_display_create(struct weston_compositor *ec, wl_display_add_shm_format(ec->wl_display, WL_SHM_FORMAT_RGB565); wl_display_add_shm_format(ec->wl_display, WL_SHM_FORMAT_YUV420); + wl_display_add_shm_format(ec->wl_display, WL_SHM_FORMAT_YUV444); wl_display_add_shm_format(ec->wl_display, WL_SHM_FORMAT_NV12); wl_display_add_shm_format(ec->wl_display, WL_SHM_FORMAT_YUYV); wl_display_add_shm_format(ec->wl_display, WL_SHM_FORMAT_XYUV8888); diff --git a/tests/yuv-buffer-test.c b/tests/yuv-buffer-test.c index ad0b298b..eb17c680 100644 --- a/tests/yuv-buffer-test.c +++ b/tests/yuv-buffer-test.c @@ -150,12 +150,13 @@ x8r8g8b8_to_ycbcr8_bt601(uint32_t xrgb, * plane 0: Y plane, [7:0] Y * plane 1: Cb plane, [7:0] Cb * plane 2: Cr plane, [7:0] Cr - * 2x2 subsampled Cb (1) and Cr (2) planes + * YUV420: 2x2 subsampled Cb (1) and Cr (2) planes + * YUV444: no subsampling */ static struct yuv_buffer * -yuv420_create_buffer(struct client *client, - uint32_t drm_format, - pixman_image_t *rgb_image) +y_u_v_create_buffer(struct client *client, + uint32_t drm_format, + pixman_image_t *rgb_image) { struct yuv_buffer *buf; size_t bytes; @@ -172,27 +173,29 @@ yuv420_create_buffer(struct client *client, uint8_t *u_row; uint8_t *v_row; uint32_t argb; + int sub = (drm_format == DRM_FORMAT_YUV420) ? 2 : 1; - assert(drm_format == DRM_FORMAT_YUV420); + assert(drm_format == DRM_FORMAT_YUV420 || + drm_format == DRM_FORMAT_YUV444); width = pixman_image_get_width(rgb_image); height = pixman_image_get_height(rgb_image); rgb_pixels = pixman_image_get_data(rgb_image); rgb_stride_bytes = pixman_image_get_stride(rgb_image); - /* Full size Y, quarter U and V */ - bytes = width * height + (width / 2) * (height / 2) * 2; + /* Full size Y plus quarter U and V */ + bytes = width * height + (width / sub) * (height / sub) * 2; buf = yuv_buffer_create(client, bytes, width, height, width, drm_format); y_base = buf->data; u_base = y_base + width * height; - v_base = u_base + (width / 2) * (height / 2); + v_base = u_base + (width / sub) * (height / sub); for (y = 0; y < height; y++) { rgb_row = rgb_pixels + (y / 2 * 2) * rgb_stride_bytes; y_row = y_base + y * width; - u_row = u_base + (y / 2) * (width / 2); - v_row = v_base + (y / 2) * (width / 2); + u_row = u_base + (y / sub) * (width / sub); + v_row = v_base + (y / sub) * (width / sub); for (x = 0; x < width; x++) { /* @@ -207,10 +210,10 @@ yuv420_create_buffer(struct client *client, * do the necessary filtering/averaging/siting or * alternate Cb/Cr rows. */ - if ((y & 1) == 0 && (x & 1) == 0) { + if ((y & (sub - 1)) == 0 && (x & (sub - 1)) == 0) { x8r8g8b8_to_ycbcr8_bt601(argb, y_row + x, - u_row + x / 2, - v_row + x / 2); + u_row + x / sub, + v_row + x / sub); } else { x8r8g8b8_to_ycbcr8_bt601(argb, y_row + x, NULL, NULL); @@ -435,7 +438,8 @@ show_window_with_yuv(struct client *client, struct yuv_buffer *buf) static const struct yuv_case yuv_cases[] = { #define FMT(x) DRM_FORMAT_ ##x, #x - { FMT(YUV420), yuv420_create_buffer }, + { FMT(YUV420), y_u_v_create_buffer }, + { FMT(YUV444), y_u_v_create_buffer }, { FMT(NV12), nv12_create_buffer }, { FMT(YUYV), yuyv_create_buffer }, { FMT(XYUV8888), xyuv8888_create_buffer }, From 32ee42d26122f512762b0e4680f9f1307ce5f3eb Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 28 Apr 2022 01:33:15 +0100 Subject: [PATCH 281/609] gl-renderer: Remove useless texture_type enum This was just a duplicate of shader variants, for historical reasons. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 37 ++++++----------------------- 1 file changed, 7 insertions(+), 30 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 7fcf1f9d..2eeefcbb 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -125,18 +125,11 @@ struct yuv_plane_descriptor { int plane_index; }; -enum texture_type { - TEXTURE_Y_XUXV_WL, - TEXTURE_Y_UV_WL, - TEXTURE_Y_U_V_WL, - TEXTURE_XYUV_WL -}; - struct yuv_format_descriptor { uint32_t format; int input_planes; int output_planes; - enum texture_type texture_type; + enum gl_shader_texture_variant shader_variant; struct yuv_plane_descriptor plane[4]; }; @@ -2298,7 +2291,7 @@ struct yuv_format_descriptor yuv_formats[] = { .format = DRM_FORMAT_YUYV, .input_planes = 1, .output_planes = 2, - .texture_type = TEXTURE_Y_XUXV_WL, + .shader_variant = SHADER_VARIANT_Y_XUXV, {{ .width_divisor = 1, .height_divisor = 1, @@ -2314,7 +2307,7 @@ struct yuv_format_descriptor yuv_formats[] = { .format = DRM_FORMAT_NV12, .input_planes = 2, .output_planes = 2, - .texture_type = TEXTURE_Y_UV_WL, + .shader_variant = SHADER_VARIANT_Y_UV, {{ .width_divisor = 1, .height_divisor = 1, @@ -2330,7 +2323,7 @@ struct yuv_format_descriptor yuv_formats[] = { .format = DRM_FORMAT_YUV420, .input_planes = 3, .output_planes = 3, - .texture_type = TEXTURE_Y_U_V_WL, + .shader_variant = SHADER_VARIANT_Y_U_V, {{ .width_divisor = 1, .height_divisor = 1, @@ -2351,7 +2344,7 @@ struct yuv_format_descriptor yuv_formats[] = { .format = DRM_FORMAT_YUV444, .input_planes = 3, .output_planes = 3, - .texture_type = TEXTURE_Y_U_V_WL, + .shader_variant = SHADER_VARIANT_Y_U_V, {{ .width_divisor = 1, .height_divisor = 1, @@ -2372,7 +2365,7 @@ struct yuv_format_descriptor yuv_formats[] = { .format = DRM_FORMAT_XYUV8888, .input_planes = 1, .output_planes = 1, - .texture_type = TEXTURE_XYUV_WL, + .shader_variant = SHADER_VARIANT_XYUV, {{ .width_divisor = 1, .height_divisor = 1, @@ -2458,23 +2451,7 @@ import_yuv_dmabuf(struct gl_renderer *gr, struct gl_buffer_state *gb, } gb->num_images = format->output_planes; - - switch (format->texture_type) { - case TEXTURE_Y_XUXV_WL: - gb->shader_variant = SHADER_VARIANT_Y_XUXV; - break; - case TEXTURE_Y_UV_WL: - gb->shader_variant = SHADER_VARIANT_Y_UV; - break; - case TEXTURE_Y_U_V_WL: - gb->shader_variant = SHADER_VARIANT_Y_U_V; - break; - case TEXTURE_XYUV_WL: - gb->shader_variant = SHADER_VARIANT_XYUV; - break; - default: - assert(false); - } + gb->shader_variant = format->shader_variant; target = gl_shader_texture_variant_get_target(gb->shader_variant); ensure_textures(gb, target, gb->num_images); From 1db2fbef619779d2a2928bc508b2890edd46da4d Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 28 Apr 2022 01:44:19 +0100 Subject: [PATCH 282/609] pixel-formats: Add internal-only format flag Add a new hide_from_clients flag which, if set, specifies that the format is only for internal information and processing, and should not be advertised for clients. This will be used for formats like R8 and GR88, which are not useful for client buffers, but are used internally to implement YUV -> RGB conversion. Signed-off-by: Daniel Stone --- libweston/compositor.c | 4 ++-- libweston/pixel-formats.h | 4 ++++ libweston/renderer-gl/gl-renderer.c | 5 ++++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/libweston/compositor.c b/libweston/compositor.c index b37a93bd..cd053d1a 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -2439,7 +2439,7 @@ weston_buffer_from_resource(struct weston_compositor *ec, pixel_format_get_info_shm(wl_shm_buffer_get_format(shm)); buffer->format_modifier = DRM_FORMAT_MOD_LINEAR; - if (!buffer->pixel_format) + if (!buffer->pixel_format || buffer->pixel_format->hide_from_clients) goto fail; } else if ((dmabuf = linux_dmabuf_buffer_get(buffer->resource))) { buffer->type = WESTON_BUFFER_DMABUF; @@ -2451,7 +2451,7 @@ weston_buffer_from_resource(struct weston_compositor *ec, pixel_format_get_info(dmabuf->attributes.format); /* dmabuf import should assure we don't create a buffer with an * unknown format */ - assert(buffer->pixel_format); + assert(buffer->pixel_format && !buffer->pixel_format->hide_from_clients); buffer->format_modifier = dmabuf->attributes.modifier[0]; if (dmabuf->attributes.flags & ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT) buffer->buffer_origin = ORIGIN_BOTTOM_LEFT; diff --git a/libweston/pixel-formats.h b/libweston/pixel-formats.h index c14a3ed4..bbd1bff8 100644 --- a/libweston/pixel-formats.h +++ b/libweston/pixel-formats.h @@ -42,6 +42,10 @@ struct pixel_format_info { /** The DRM format name without the DRM_FORMAT_ prefix. */ const char *drm_format_name; + /** If true, is only for internal use and should not be advertised to + * clients to allow them to create buffers of this format. */ + bool hide_from_clients; + /** If non-zero, number of planes in base (non-modified) format. */ int num_planes; diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 2eeefcbb..31bb74ac 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -2796,7 +2796,10 @@ populate_supported_formats(struct weston_compositor *ec, return 0; for (i = 0; i < num_formats; i++) { - if (!pixel_format_get_info(formats[i])) + const struct pixel_format_info *info = + pixel_format_get_info(formats[i]); + + if (!info || info->hide_from_clients) continue; fmt = weston_drm_format_array_add_format(supported_formats, From 727c4ef6fb83506a6f7b29f518405b94a79426a0 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 28 Apr 2022 01:45:30 +0100 Subject: [PATCH 283/609] pixel-formats: Add R8 and GR88 These formats are used within gl-renderer to do YUV -> RGB colourspace conversion. Signed-off-by: Daniel Stone --- libweston/pixel-formats.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/libweston/pixel-formats.c b/libweston/pixel-formats.c index 8d3d49a3..5793621b 100644 --- a/libweston/pixel-formats.c +++ b/libweston/pixel-formats.c @@ -82,6 +82,22 @@ * colour channels, are not supported. */ static const struct pixel_format_info pixel_format_table[] = { + { + DRM_FORMAT(R8), + BITS_RGBA_FIXED(8, 0, 0, 0), + .bpp = 8, + .hide_from_clients = true, + GL_FORMAT(GL_R8_EXT), + GL_TYPE(GL_UNSIGNED_BYTE), + }, + { + DRM_FORMAT(GR88), + BITS_RGBA_FIXED(8, 8, 0, 0), + .bpp = 16, + .hide_from_clients = true, + GL_FORMAT(GL_RG8_EXT), + GL_TYPE(GL_UNSIGNED_BYTE), + }, { DRM_FORMAT(XRGB4444), BITS_RGBA_FIXED(4, 4, 4, 0), From 16548139036bf72c87d49f6b7e611244e73bb5d6 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 28 Apr 2022 01:51:10 +0100 Subject: [PATCH 284/609] gl-renderer: Get YUV plane count from pixel-formats We already have this elsewhere. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 31bb74ac..85edd7db 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -127,7 +127,6 @@ struct yuv_plane_descriptor { struct yuv_format_descriptor { uint32_t format; - int input_planes; int output_planes; enum gl_shader_texture_variant shader_variant; struct yuv_plane_descriptor plane[4]; @@ -2289,7 +2288,6 @@ import_simple_dmabuf(struct gl_renderer *gr, struct yuv_format_descriptor yuv_formats[] = { { .format = DRM_FORMAT_YUYV, - .input_planes = 1, .output_planes = 2, .shader_variant = SHADER_VARIANT_Y_XUXV, {{ @@ -2305,7 +2303,6 @@ struct yuv_format_descriptor yuv_formats[] = { }} }, { .format = DRM_FORMAT_NV12, - .input_planes = 2, .output_planes = 2, .shader_variant = SHADER_VARIANT_Y_UV, {{ @@ -2321,7 +2318,6 @@ struct yuv_format_descriptor yuv_formats[] = { }} }, { .format = DRM_FORMAT_YUV420, - .input_planes = 3, .output_planes = 3, .shader_variant = SHADER_VARIANT_Y_U_V, {{ @@ -2342,7 +2338,6 @@ struct yuv_format_descriptor yuv_formats[] = { }} }, { .format = DRM_FORMAT_YUV444, - .input_planes = 3, .output_planes = 3, .shader_variant = SHADER_VARIANT_Y_U_V, {{ @@ -2363,7 +2358,6 @@ struct yuv_format_descriptor yuv_formats[] = { }} }, { .format = DRM_FORMAT_XYUV8888, - .input_planes = 1, .output_planes = 1, .shader_variant = SHADER_VARIANT_XYUV, {{ @@ -2411,6 +2405,8 @@ import_yuv_dmabuf(struct gl_renderer *gr, struct gl_buffer_state *gb, unsigned i; int j; struct yuv_format_descriptor *format = NULL; + const struct pixel_format_info *info; + int plane_count; GLenum target; char fmt[4]; @@ -2428,11 +2424,15 @@ import_yuv_dmabuf(struct gl_renderer *gr, struct gl_buffer_state *gb, return false; } - if (attributes->n_planes != format->input_planes) { + info = pixel_format_get_info(attributes->format); + assert(info); + plane_count = pixel_format_get_plane_count(info); + + if (attributes->n_planes != plane_count) { weston_log("%.4s dmabuf must contain %d plane%s (%d provided)\n", dump_format(format->format, fmt), - format->input_planes, - (format->input_planes > 1) ? "s" : "", + plane_count, + (plane_count > 1) ? "s" : "", attributes->n_planes); return false; } From 1a86963d5150aa4a8315dc806b8621e115d90dd8 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 28 Apr 2022 01:53:05 +0100 Subject: [PATCH 285/609] gl-renderer: Get YUV subsampling from pixel-formats We already have the subsampling levels in pixel-formats - no need to type it out again here. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 34 ++++++----------------------- 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 85edd7db..35aec3b0 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -119,8 +119,6 @@ struct dmabuf_format { }; struct yuv_plane_descriptor { - int width_divisor; - int height_divisor; uint32_t format; int plane_index; }; @@ -2291,13 +2289,9 @@ struct yuv_format_descriptor yuv_formats[] = { .output_planes = 2, .shader_variant = SHADER_VARIANT_Y_XUXV, {{ - .width_divisor = 1, - .height_divisor = 1, .format = DRM_FORMAT_GR88, .plane_index = 0 }, { - .width_divisor = 2, - .height_divisor = 1, .format = DRM_FORMAT_ARGB8888, .plane_index = 0 }} @@ -2306,13 +2300,9 @@ struct yuv_format_descriptor yuv_formats[] = { .output_planes = 2, .shader_variant = SHADER_VARIANT_Y_UV, {{ - .width_divisor = 1, - .height_divisor = 1, .format = DRM_FORMAT_R8, .plane_index = 0 }, { - .width_divisor = 2, - .height_divisor = 2, .format = DRM_FORMAT_GR88, .plane_index = 1 }} @@ -2321,18 +2311,12 @@ struct yuv_format_descriptor yuv_formats[] = { .output_planes = 3, .shader_variant = SHADER_VARIANT_Y_U_V, {{ - .width_divisor = 1, - .height_divisor = 1, .format = DRM_FORMAT_R8, .plane_index = 0 }, { - .width_divisor = 2, - .height_divisor = 2, .format = DRM_FORMAT_R8, .plane_index = 1 }, { - .width_divisor = 2, - .height_divisor = 2, .format = DRM_FORMAT_R8, .plane_index = 2 }} @@ -2341,18 +2325,12 @@ struct yuv_format_descriptor yuv_formats[] = { .output_planes = 3, .shader_variant = SHADER_VARIANT_Y_U_V, {{ - .width_divisor = 1, - .height_divisor = 1, .format = DRM_FORMAT_R8, .plane_index = 0 }, { - .width_divisor = 1, - .height_divisor = 1, .format = DRM_FORMAT_R8, .plane_index = 1 }, { - .width_divisor = 1, - .height_divisor = 1, .format = DRM_FORMAT_R8, .plane_index = 2 }} @@ -2361,8 +2339,6 @@ struct yuv_format_descriptor yuv_formats[] = { .output_planes = 1, .shader_variant = SHADER_VARIANT_XYUV, {{ - .width_divisor = 1, - .height_divisor = 1, .format = DRM_FORMAT_XBGR8888, .plane_index = 0 }} @@ -2371,15 +2347,19 @@ struct yuv_format_descriptor yuv_formats[] = { static EGLImageKHR import_dmabuf_single_plane(struct gl_renderer *gr, + const struct pixel_format_info *info, + int idx, const struct dmabuf_attributes *attributes, struct yuv_plane_descriptor *descriptor) { struct dmabuf_attributes plane; EGLImageKHR image; char fmt[4]; + int hsub = pixel_format_hsub(info, idx); + int vsub = pixel_format_vsub(info, idx); - plane.width = attributes->width / descriptor->width_divisor; - plane.height = attributes->height / descriptor->height_divisor; + plane.width = attributes->width / hsub; + plane.height = attributes->height / vsub; plane.format = descriptor->format; plane.n_planes = 1; plane.fd[0] = attributes->fd[descriptor->plane_index]; @@ -2438,7 +2418,7 @@ import_yuv_dmabuf(struct gl_renderer *gr, struct gl_buffer_state *gb, } for (j = 0; j < format->output_planes; ++j) { - gb->images[j] = import_dmabuf_single_plane(gr, attributes, + gb->images[j] = import_dmabuf_single_plane(gr, info, j, attributes, &format->plane[j]); if (gb->images[j] == EGL_NO_IMAGE_KHR) { while (--j >= 0) { From 18a31a6af822ad0ddb321d6fef997249643f2a91 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 28 Apr 2022 02:25:20 +0100 Subject: [PATCH 286/609] gl-renderer: Remove special-cased YUV SHM formats Now that we can pull everything we need from pixel-formats, go one step further and reuse the same YUV format descriptors we use to emulate dmabuf/EGLImage imports for SHM. This eliminates all special-case YUV/SHM handling. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 238 +++++++++++++++------------- 1 file changed, 125 insertions(+), 113 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 35aec3b0..2ca9961c 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -139,17 +139,15 @@ struct gl_buffer_state { pixman_region32_t texture_damage; /* Only needed between attach() and flush_damage() */ - int pitch; - GLenum gl_format[3]; + int pitch; /* plane 0 pitch in pixels */ GLenum gl_pixel_type; + GLenum gl_format[3]; + int offset[3]; /* per-plane pitch in bytes */ EGLImageKHR images[3]; int num_images; enum gl_shader_texture_variant shader_variant; - /* Extension needed for SHM YUV texture */ - int offset[3]; /* offset per plane */ - GLuint textures[3]; int num_textures; @@ -240,6 +238,69 @@ shadow_exists(const struct gl_output_state *go) return go->shadow.fbo != 0; } +struct yuv_format_descriptor yuv_formats[] = { + { + .format = DRM_FORMAT_YUYV, + .output_planes = 2, + .shader_variant = SHADER_VARIANT_Y_XUXV, + {{ + .format = DRM_FORMAT_GR88, + .plane_index = 0 + }, { + .format = DRM_FORMAT_ARGB8888, + .plane_index = 0 + }} + }, { + .format = DRM_FORMAT_NV12, + .output_planes = 2, + .shader_variant = SHADER_VARIANT_Y_UV, + {{ + .format = DRM_FORMAT_R8, + .plane_index = 0 + }, { + .format = DRM_FORMAT_GR88, + .plane_index = 1 + }} + }, { + .format = DRM_FORMAT_YUV420, + .output_planes = 3, + .shader_variant = SHADER_VARIANT_Y_U_V, + {{ + .format = DRM_FORMAT_R8, + .plane_index = 0 + }, { + .format = DRM_FORMAT_R8, + .plane_index = 1 + }, { + .format = DRM_FORMAT_R8, + .plane_index = 2 + }} + }, { + .format = DRM_FORMAT_YUV444, + .output_planes = 3, + .shader_variant = SHADER_VARIANT_Y_U_V, + {{ + .format = DRM_FORMAT_R8, + .plane_index = 0 + }, { + .format = DRM_FORMAT_R8, + .plane_index = 1 + }, { + .format = DRM_FORMAT_R8, + .plane_index = 2 + }} + }, { + .format = DRM_FORMAT_XYUV8888, + .output_planes = 1, + .shader_variant = SHADER_VARIANT_XYUV, + {{ + .format = DRM_FORMAT_XBGR8888, + .plane_index = 0 + }} + } +}; + + static void timeline_render_point_destroy(struct timeline_render_point *trp) { @@ -1901,69 +1962,82 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer) enum gl_shader_texture_variant shader_variant; int pitch; int offset[3] = { 0, 0, 0 }; + unsigned int num_planes; unsigned int i; - int num_planes = 1; - int bpp; bool using_glesv2 = gr->gl_version < gr_gl_version(3, 0); + const struct yuv_format_descriptor *yuv = NULL; + + /* When sampling YUV input textures and converting to RGB by hand, we + * have to bind to each plane separately, with a different format. For + * example, YUYV will have a single wl_shm input plane, but be bound as + * two planes within gl-renderer, one as GR88 and one as ARGB8888. + * + * The yuv_formats array gives us this translation. + */ + for (i = 0; i < ARRAY_LENGTH(yuv_formats); ++i) { + if (yuv_formats[i].format == buffer->pixel_format->format) { + yuv = &yuv_formats[i]; + break; + } + } - switch (buffer->pixel_format->format) { - case DRM_FORMAT_YUV420: - case DRM_FORMAT_YUV444: - shader_variant = SHADER_VARIANT_Y_U_V; - pitch = wl_shm_buffer_get_stride(shm_buffer); - gl_pixel_type = GL_UNSIGNED_BYTE; - num_planes = 3; - offset[1] = offset[0] + pitch * buffer->height; - offset[2] = offset[1] + - (pitch / pixel_format_hsub(buffer->pixel_format, 1)) * - (buffer->height / pixel_format_vsub(buffer->pixel_format, 1)); - gl_format[0] = GL_R8_EXT; - gl_format[1] = GL_R8_EXT; - gl_format[2] = GL_R8_EXT; - break; - case DRM_FORMAT_NV12: - shader_variant = SHADER_VARIANT_Y_UV; - pitch = wl_shm_buffer_get_stride(shm_buffer); - gl_pixel_type = GL_UNSIGNED_BYTE; - num_planes = 2; - offset[1] = offset[0] + pitch * buffer->height; - gl_format[0] = GL_R8_EXT; - gl_format[1] = GL_RG8_EXT; - break; - case DRM_FORMAT_YUYV: - shader_variant = SHADER_VARIANT_Y_XUXV; - pitch = wl_shm_buffer_get_stride(shm_buffer) / 2; - gl_pixel_type = GL_UNSIGNED_BYTE; - num_planes = 2; - offset[1] = 0; - gl_format[0] = GL_RG8_EXT; - gl_format[1] = GL_BGRA_EXT; - break; - case DRM_FORMAT_XYUV8888: - /* - * [31:0] X:Y:Cb:Cr 8:8:8:8 little endian - * a:b: g: r in SHADER_VARIANT_XYUV + if (yuv) { + unsigned int out; + unsigned int shm_plane_count; + int shm_offset[3] = { 0 }; + int bpp = buffer->pixel_format->bpp; + + /* XXX: Pitch here is given in pixel units, whereas offset is + * given in byte units. This is fragile and will break with + * new formats. */ - shader_variant = SHADER_VARIANT_XYUV; - pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; - gl_format[0] = GL_RGBA; + if (!bpp) + bpp = pixel_format_get_info(yuv->plane[0].format)->bpp; + pitch = wl_shm_buffer_get_stride(shm_buffer) / (bpp / 8); + + /* well, they all are so far ... */ gl_pixel_type = GL_UNSIGNED_BYTE; - break; - default: + shader_variant = yuv->shader_variant; + + /* pre-compute all plane offsets in shm buffer */ + shm_plane_count = pixel_format_get_plane_count(buffer->pixel_format); + assert(shm_plane_count <= ARRAY_LENGTH(shm_offset)); + for (i = 1; i < shm_plane_count; i++) { + int hsub, vsub; + + hsub = pixel_format_hsub(buffer->pixel_format, i - 1); + vsub = pixel_format_vsub(buffer->pixel_format, i - 1); + shm_offset[i] = shm_offset[i - 1] + + ((pitch / hsub) * (buffer->height / vsub)); + } + + num_planes = yuv->output_planes; + for (out = 0; out < num_planes; out++) { + const struct pixel_format_info *sub_info = + pixel_format_get_info(yuv->plane[out].format); + + assert(sub_info); + assert(yuv->plane[out].plane_index < (int) shm_plane_count); + + gl_format[out] = sub_info->gl_format; + offset[out] = shm_offset[yuv->plane[out].plane_index]; + } + } else { + int bpp = buffer->pixel_format->bpp; + assert(pixel_format_get_plane_count(buffer->pixel_format) == 1); + num_planes = 1; if (pixel_format_is_opaque(buffer->pixel_format)) shader_variant = SHADER_VARIANT_RGBX; else shader_variant = SHADER_VARIANT_RGBA; - bpp = buffer->pixel_format->bpp; assert(bpp > 0 && !(bpp & 7)); pitch = wl_shm_buffer_get_stride(shm_buffer) / (bpp / 8); gl_format[0] = buffer->pixel_format->gl_format; gl_pixel_type = buffer->pixel_format->gl_type; - break; } for (i = 0; i < ARRAY_LENGTH(gb->gl_format); i++) { @@ -2283,68 +2357,6 @@ import_simple_dmabuf(struct gl_renderer *gr, EGL_LINUX_DMA_BUF_EXT, NULL, attribs); } -struct yuv_format_descriptor yuv_formats[] = { - { - .format = DRM_FORMAT_YUYV, - .output_planes = 2, - .shader_variant = SHADER_VARIANT_Y_XUXV, - {{ - .format = DRM_FORMAT_GR88, - .plane_index = 0 - }, { - .format = DRM_FORMAT_ARGB8888, - .plane_index = 0 - }} - }, { - .format = DRM_FORMAT_NV12, - .output_planes = 2, - .shader_variant = SHADER_VARIANT_Y_UV, - {{ - .format = DRM_FORMAT_R8, - .plane_index = 0 - }, { - .format = DRM_FORMAT_GR88, - .plane_index = 1 - }} - }, { - .format = DRM_FORMAT_YUV420, - .output_planes = 3, - .shader_variant = SHADER_VARIANT_Y_U_V, - {{ - .format = DRM_FORMAT_R8, - .plane_index = 0 - }, { - .format = DRM_FORMAT_R8, - .plane_index = 1 - }, { - .format = DRM_FORMAT_R8, - .plane_index = 2 - }} - }, { - .format = DRM_FORMAT_YUV444, - .output_planes = 3, - .shader_variant = SHADER_VARIANT_Y_U_V, - {{ - .format = DRM_FORMAT_R8, - .plane_index = 0 - }, { - .format = DRM_FORMAT_R8, - .plane_index = 1 - }, { - .format = DRM_FORMAT_R8, - .plane_index = 2 - }} - }, { - .format = DRM_FORMAT_XYUV8888, - .output_planes = 1, - .shader_variant = SHADER_VARIANT_XYUV, - {{ - .format = DRM_FORMAT_XBGR8888, - .plane_index = 0 - }} - } -}; - static EGLImageKHR import_dmabuf_single_plane(struct gl_renderer *gr, const struct pixel_format_info *info, From 67fc71214dbfdc0fb6d06b3ffbe14ac35021bfd6 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 18 May 2022 13:32:35 +0100 Subject: [PATCH 287/609] gl-renderer: Add comment for yuv_format_descriptor Try to describe this and yuv_plane_descriptor as well, since they're not blindingly obvious. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 2ca9961c..53072216 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -118,6 +118,16 @@ struct dmabuf_format { int num_modifiers; }; +/* + * yuv_format_descriptor and yuv_plane_descriptor describe the translation + * between YUV and RGB formats. When native YUV sampling is not available, we + * bind each YUV plane as one or more RGB plane and convert in the shader. + * This structure describes the mapping: output_planes is the number of + * RGB images we need to bind, each of which has a yuv_plane_descriptor + * describing the GL format and the input (YUV) plane index to bind. + * + * The specified shader_variant is then used to sample. + */ struct yuv_plane_descriptor { uint32_t format; int plane_index; From f36d77a199a6398444f7ae6d1002dad2f65ca679 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 28 Apr 2022 02:49:33 +0100 Subject: [PATCH 288/609] gl-renderer: Don't use TEXTURE_EXTERNAL for multi-planar formats There's just no good reason to do this. The query entrypoints already tell us if we need to use GL_TEXTURE_EXTERNAL_OES for a particular format/modifier. We also have RGB -> YUV fallbacks which should be able to work well with TEXTURE_2D. TEXTURE_EXTERNAL pessimises quite hard, forcing GPU-side reloads as well as bad filtering. Allowing multi-planar formats to use TEXTURE_2D should thus result in performance and quality improvements. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 53072216..e0047882 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -2522,7 +2522,7 @@ choose_texture_target(struct gl_renderer *gr, for (i = 0; i < format->num_modifiers; ++i) { if (format->modifiers[i] == attributes->modifier[0]) { - if(format->external_only[i]) + if (format->external_only[i]) return GL_TEXTURE_EXTERNAL_OES; else return GL_TEXTURE_2D; @@ -2530,9 +2530,6 @@ choose_texture_target(struct gl_renderer *gr, } } - if (attributes->n_planes > 1) - return GL_TEXTURE_EXTERNAL_OES; - switch (attributes->format & ~DRM_FORMAT_BIG_ENDIAN) { case DRM_FORMAT_YUYV: case DRM_FORMAT_YVYU: From c3d84293b90a1338004da0f7f0b9eeb9bd65d5b3 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 19 May 2022 23:26:47 +0100 Subject: [PATCH 289/609] gl-renderer: Fix plane count for legacy YUV420 images It's three planes, not two. Signed-off-by: Daniel Stone Fixes: 8b167a1703 ("gl-renderer: Store EGL buffer state in weston_buffer") --- libweston/renderer-gl/gl-renderer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index e0047882..f4b75e96 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -2180,7 +2180,7 @@ gl_renderer_fill_buffer_info(struct weston_compositor *ec, break; case EGL_TEXTURE_Y_U_V_WL: fourcc = DRM_FORMAT_YUV420; - gb->num_images = 2; + gb->num_images = 3; gb->shader_variant = SHADER_VARIANT_Y_U_V; break; default: From dfaba9f107360d512b3bd0dd9ed4c710812d0126 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 19 May 2022 23:04:02 +0100 Subject: [PATCH 290/609] gl-renderer: Use common value for maximum plane index Most everything else only supports 3 planes, and we don't support any four-plane YUV formats, so. Signed-off-by: Daniel Stone --- libweston/renderer-gl/gl-renderer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index f4b75e96..b0a49563 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -137,7 +137,7 @@ struct yuv_format_descriptor { uint32_t format; int output_planes; enum gl_shader_texture_variant shader_variant; - struct yuv_plane_descriptor plane[4]; + struct yuv_plane_descriptor plane[3]; }; struct gl_buffer_state { From a55bd6798e84d7633bc7bc2dc5b5a7d814c449a3 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Mon, 23 May 2022 13:45:22 +0100 Subject: [PATCH 291/609] clients: Delete gears It doesn't and can't build, because it depends on cairo-gl. We already have simple-egl which shows how to use EGL/GLESv2 on Wayland. Signed-off-by: Daniel Stone --- clients/gears.c | 504 ------------------------------------------------ 1 file changed, 504 deletions(-) delete mode 100644 clients/gears.c diff --git a/clients/gears.c b/clients/gears.c deleted file mode 100644 index ef19a480..00000000 --- a/clients/gears.c +++ /dev/null @@ -1,504 +0,0 @@ -/* - * Copyright © 2008 Kristian Høgsberg - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include "window.h" - -struct gears { - struct window *window; - struct widget *widget; - - struct display *d; - - EGLDisplay display; - EGLDisplay config; - EGLContext context; - GLfloat angle; - - struct { - GLfloat rotx; - GLfloat roty; - } view; - - int button_down; - int last_x, last_y; - - GLint gear_list[3]; - int fullscreen; - int frames; - uint32_t last_fps; -}; - -struct gear_template { - GLfloat material[4]; - GLfloat inner_radius; - GLfloat outer_radius; - GLfloat width; - GLint teeth; - GLfloat tooth_depth; -}; - -static const struct gear_template gear_templates[] = { - { { 0.8, 0.1, 0.0, 1.0 }, 1.0, 4.0, 1.0, 20, 0.7 }, - { { 0.0, 0.8, 0.2, 1.0 }, 0.5, 2.0, 2.0, 10, 0.7 }, - { { 0.2, 0.2, 1.0, 1.0 }, 1.3, 2.0, 0.5, 10, 0.7 }, -}; - -static GLfloat light_pos[4] = {5.0, 5.0, 10.0, 0.0}; - -static void die(const char *msg) -{ - fprintf(stderr, "%s", msg); - exit(EXIT_FAILURE); -} - -static void -make_gear(const struct gear_template *t) -{ - GLint i; - GLfloat r0, r1, r2; - GLfloat angle, da; - GLfloat u, v, len; - - glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, t->material); - - r0 = t->inner_radius; - r1 = t->outer_radius - t->tooth_depth / 2.0; - r2 = t->outer_radius + t->tooth_depth / 2.0; - - da = 2.0 * M_PI / t->teeth / 4.0; - - glShadeModel(GL_FLAT); - - glNormal3f(0.0, 0.0, 1.0); - - /* draw front face */ - glBegin(GL_QUAD_STRIP); - for (i = 0; i <= t->teeth; i++) { - angle = i * 2.0 * M_PI / t->teeth; - glVertex3f(r0 * cos(angle), r0 * sin(angle), t->width * 0.5); - glVertex3f(r1 * cos(angle), r1 * sin(angle), t->width * 0.5); - if (i < t->teeth) { - glVertex3f(r0 * cos(angle), r0 * sin(angle), t->width * 0.5); - glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), t->width * 0.5); - } - } - glEnd(); - - /* draw front sides of teeth */ - glBegin(GL_QUADS); - da = 2.0 * M_PI / t->teeth / 4.0; - for (i = 0; i < t->teeth; i++) { - angle = i * 2.0 * M_PI / t->teeth; - - glVertex3f(r1 * cos(angle), r1 * sin(angle), t->width * 0.5); - glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), t->width * 0.5); - glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), t->width * 0.5); - glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), t->width * 0.5); - } - glEnd(); - - glNormal3f(0.0, 0.0, -1.0); - - /* draw back face */ - glBegin(GL_QUAD_STRIP); - for (i = 0; i <= t->teeth; i++) { - angle = i * 2.0 * M_PI / t->teeth; - glVertex3f(r1 * cos(angle), r1 * sin(angle), -t->width * 0.5); - glVertex3f(r0 * cos(angle), r0 * sin(angle), -t->width * 0.5); - if (i < t->teeth) { - glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -t->width * 0.5); - glVertex3f(r0 * cos(angle), r0 * sin(angle), -t->width * 0.5); - } - } - glEnd(); - - /* draw back sides of teeth */ - glBegin(GL_QUADS); - da = 2.0 * M_PI / t->teeth / 4.0; - for (i = 0; i < t->teeth; i++) { - angle = i * 2.0 * M_PI / t->teeth; - - glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -t->width * 0.5); - glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -t->width * 0.5); - glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -t->width * 0.5); - glVertex3f(r1 * cos(angle), r1 * sin(angle), -t->width * 0.5); - } - glEnd(); - - /* draw outward faces of teeth */ - glBegin(GL_QUAD_STRIP); - for (i = 0; i < t->teeth; i++) { - angle = i * 2.0 * M_PI / t->teeth; - - glVertex3f(r1 * cos(angle), r1 * sin(angle), t->width * 0.5); - glVertex3f(r1 * cos(angle), r1 * sin(angle), -t->width * 0.5); - u = r2 * cos(angle + da) - r1 * cos(angle); - v = r2 * sin(angle + da) - r1 * sin(angle); - len = sqrt(u * u + v * v); - u /= len; - v /= len; - glNormal3f(v, -u, 0.0); - glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), t->width * 0.5); - glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -t->width * 0.5); - glNormal3f(cos(angle), sin(angle), 0.0); - glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), t->width * 0.5); - glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -t->width * 0.5); - u = r1 * cos(angle + 3 * da) - r2 * cos(angle + 2 * da); - v = r1 * sin(angle + 3 * da) - r2 * sin(angle + 2 * da); - glNormal3f(v, -u, 0.0); - glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), t->width * 0.5); - glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -t->width * 0.5); - glNormal3f(cos(angle), sin(angle), 0.0); - } - - glVertex3f(r1 * cos(0), r1 * sin(0), t->width * 0.5); - glVertex3f(r1 * cos(0), r1 * sin(0), -t->width * 0.5); - - glEnd(); - - glShadeModel(GL_SMOOTH); - - /* draw inside radius cylinder */ - glBegin(GL_QUAD_STRIP); - for (i = 0; i <= t->teeth; i++) { - angle = i * 2.0 * M_PI / t->teeth; - glNormal3f(-cos(angle), -sin(angle), 0.0); - glVertex3f(r0 * cos(angle), r0 * sin(angle), -t->width * 0.5); - glVertex3f(r0 * cos(angle), r0 * sin(angle), t->width * 0.5); - } - glEnd(); -} - -static void -update_fps(struct gears *gears, uint32_t time) -{ - long diff_ms; - static bool first_call = true; - - if (first_call) { - gears->last_fps = time; - first_call = false; - } else - gears->frames++; - - diff_ms = time - gears->last_fps; - - if (diff_ms > 5000) { - float seconds = diff_ms / 1000.0; - float fps = gears->frames / seconds; - - printf("%d frames in %6.3f seconds = %6.3f FPS\n", gears->frames, seconds, fps); - fflush(stdout); - - gears->frames = 0; - gears->last_fps = time; - } -} - -static void -frame_callback(void *data, struct wl_callback *callback, uint32_t time) -{ - struct gears *gears = data; - - update_fps(gears, time); - - gears->angle = (GLfloat) (time % 8192) * 360 / 8192.0; - - window_schedule_redraw(gears->window); - - if (callback) - wl_callback_destroy(callback); -} - -static const struct wl_callback_listener listener = { - frame_callback -}; - -static int -motion_handler(struct widget *widget, struct input *input, - uint32_t time, float x, float y, void *data) -{ - struct gears *gears = data; - int offset_x, offset_y; - float step = 0.5; - - if (gears->button_down) { - offset_x = x - gears->last_x; - offset_y = y - gears->last_y; - gears->last_x = x; - gears->last_y = y; - gears->view.roty += offset_x * step; - gears->view.rotx += offset_y * step; - if (gears->view.roty >= 360) - gears->view.roty = gears->view.roty - 360; - if (gears->view.roty <= 0) - gears->view.roty = gears->view.roty + 360; - if (gears->view.rotx >= 360) - gears->view.rotx = gears->view.rotx - 360; - if (gears->view.rotx <= 0) - gears->view.rotx = gears->view.rotx + 360; - } - - return CURSOR_LEFT_PTR; -} - -static void -button_handler(struct widget *widget, struct input *input, - uint32_t time, uint32_t button, - enum wl_pointer_button_state state, void *data) -{ - struct gears *gears = data; - - if (button == BTN_LEFT) { - if (state == WL_POINTER_BUTTON_STATE_PRESSED) { - gears->button_down = 1; - input_get_position(input, - &gears->last_x, &gears->last_y); - } else { - gears->button_down = 0; - } - } -} - -static void -redraw_handler(struct widget *widget, void *data) -{ - struct rectangle window_allocation; - struct rectangle allocation; - struct wl_callback *callback; - struct gears *gears = data; - - widget_get_allocation(gears->widget, &allocation); - window_get_allocation(gears->window, &window_allocation); - - if (display_acquire_window_surface(gears->d, - gears->window, - gears->context) < 0) { - die("Unable to acquire window surface, " - "compiled without cairo-egl?\n"); - } - - glViewport(allocation.x, - window_allocation.height - allocation.height - allocation.y, - allocation.width, allocation.height); - glScissor(allocation.x, - window_allocation.height - allocation.height - allocation.y, - allocation.width, allocation.height); - - glEnable(GL_SCISSOR_TEST); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - glPushMatrix(); - - glTranslatef(0.0, 0.0, -50); - - glRotatef(gears->view.rotx, 1.0, 0.0, 0.0); - glRotatef(gears->view.roty, 0.0, 1.0, 0.0); - - glPushMatrix(); - glTranslatef(-3.0, -2.0, 0.0); - glRotatef(gears->angle, 0.0, 0.0, 1.0); - glCallList(gears->gear_list[0]); - glPopMatrix(); - - glPushMatrix(); - glTranslatef(3.1, -2.0, 0.0); - glRotatef(-2.0 * gears->angle - 9.0, 0.0, 0.0, 1.0); - glCallList(gears->gear_list[1]); - glPopMatrix(); - - glPushMatrix(); - glTranslatef(-3.1, 4.2, 0.0); - glRotatef(-2.0 * gears->angle - 25.0, 0.0, 0.0, 1.0); - glCallList(gears->gear_list[2]); - glPopMatrix(); - - glPopMatrix(); - - glFlush(); - - display_release_window_surface(gears->d, gears->window); - - callback = wl_surface_frame(window_get_wl_surface(gears->window)); - wl_callback_add_listener(callback, &listener, gears); -} - -static void -resize_handler(struct widget *widget, - int32_t width, int32_t height, void *data) -{ - struct gears *gears = data; - int32_t size, big, small; - - /* Constrain child size to be square and at least 300x300 */ - if (width < height) { - small = width; - big = height; - } else { - small = height; - big = width; - } - - if (gears->fullscreen) - size = small; - else - size = big; - - widget_set_size(gears->widget, size, size); -} - -static void -keyboard_focus_handler(struct window *window, - struct input *device, void *data) -{ - window_schedule_redraw(window); -} - -static void -fullscreen_handler(struct window *window, void *data) -{ - struct gears *gears = data; - - gears->fullscreen ^= 1; - window_set_fullscreen(window, gears->fullscreen); -} - -static struct gears * -gears_create(struct display *display) -{ - const int width = 450, height = 500; - struct gears *gears; - int i; - - gears = zalloc(sizeof *gears); - gears->d = display; - gears->window = window_create(display); - gears->widget = window_frame_create(gears->window, gears); - window_set_title(gears->window, "Wayland Gears"); - window_set_appid(gears->window, "org.freedesktop.weston.wayland-gears"); - - gears->display = display_get_egl_display(gears->d); - if (gears->display == NULL) - die("failed to create egl display\n"); - - eglBindAPI(EGL_OPENGL_API); - - gears->config = display_get_argb_egl_config(gears->d); - - gears->context = eglCreateContext(gears->display, gears->config, - EGL_NO_CONTEXT, NULL); - if (gears->context == NULL) - die("failed to create context\n"); - - if (!eglMakeCurrent(gears->display, NULL, NULL, gears->context)) - die("failed to make context current\n"); - - for (i = 0; i < 3; i++) { - gears->gear_list[i] = glGenLists(1); - glNewList(gears->gear_list[i], GL_COMPILE); - make_gear(&gear_templates[i]); - glEndList(); - } - - gears->button_down = 0; - gears->last_x = 0; - gears->last_y = 0; - - gears->view.rotx = 20.0; - gears->view.roty = 30.0; - - printf("Warning: FPS count is limited by the wayland compositor or monitor refresh rate\n"); - - glEnable(GL_NORMALIZE); - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glFrustum(-1.0, 1.0, -1.0, 1.0, 5.0, 200.0); - glMatrixMode(GL_MODELVIEW); - - glLightfv(GL_LIGHT0, GL_POSITION, light_pos); - glEnable(GL_CULL_FACE); - glEnable(GL_LIGHTING); - glEnable(GL_LIGHT0); - glEnable(GL_DEPTH_TEST); - glClearColor(0, 0, 0, 0.92); - - window_set_user_data(gears->window, gears); - widget_set_resize_handler(gears->widget, resize_handler); - widget_set_redraw_handler(gears->widget, redraw_handler); - widget_set_button_handler(gears->widget, button_handler); - widget_set_motion_handler(gears->widget, motion_handler); - window_set_keyboard_focus_handler(gears->window, - keyboard_focus_handler); - window_set_fullscreen_handler(gears->window, fullscreen_handler); - - window_schedule_resize(gears->window, width, height); - - return gears; -} - -static void -gears_destroy(struct gears *gears) -{ - widget_destroy(gears->widget); - window_destroy(gears->window); - free(gears); -} - -int main(int argc, char *argv[]) -{ - struct display *d; - struct gears *gears; - - d = display_create(&argc, argv); - if (d == NULL) { - fprintf(stderr, "failed to create display: %s\n", - strerror(errno)); - return -1; - } - gears = gears_create(d); - display_run(d); - - gears_destroy(gears); - display_destroy(d); - - return 0; -} From 46a6b5b448fc49b7909245a248d0135fbe914f11 Mon Sep 17 00:00:00 2001 From: Robert Mader Date: Mon, 25 Apr 2022 18:33:12 +0200 Subject: [PATCH 292/609] clients/simple-dmabuf-feedback: Support multi-tranche feedbacks Compositors may choose to send multiple scanout or non-scanout tranches. So instead of assuming that the first respective tranche contains the format/modifier we're looking for, check all tranches. While on it, make sure that in case a compositor sends scanout tranches on the initial feedback, `pick_format_from_scanout_tranche()` does not unintentionally pick `INITIAL_BUFFER_FORMAT`. Signed-off-by: Robert Mader --- clients/simple-dmabuf-feedback.c | 47 ++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/clients/simple-dmabuf-feedback.c b/clients/simple-dmabuf-feedback.c index 66249b82..a54e7941 100644 --- a/clients/simple-dmabuf-feedback.c +++ b/clients/simple-dmabuf-feedback.c @@ -1223,7 +1223,7 @@ dmabuf_feedback_tranche_done(void *data, dmabuf_feedback_tranche_init(&feedback->pending_tranche); } -static void +static bool pick_initial_format_from_renderer_tranche(struct window *window, struct dmabuf_feedback_tranche *tranche) { @@ -1237,13 +1237,12 @@ pick_initial_format_from_renderer_tranche(struct window *window, window->format.format = fmt->format; wl_array_copy(&window->format.modifiers, &fmt->modifiers); - return; + return true; } - - assert(0 && "error: INITIAL_BUFFER_FORMAT not supported by the hardware"); + return false; } -static void +static bool pick_format_from_scanout_tranche(struct window *window, struct dmabuf_feedback_tranche *tranche) { @@ -1252,8 +1251,9 @@ pick_format_from_scanout_tranche(struct window *window, wl_array_for_each(fmt, &tranche->formats.arr) { - /* Ignore format that we're already using. */ - if (fmt->format == window->format.format) + /* Ignore the format that we want to pick from the render + * tranche. */ + if (fmt->format == INITIAL_BUFFER_FORMAT) continue; /* Format should be supported by the compositor. */ @@ -1267,10 +1267,9 @@ pick_format_from_scanout_tranche(struct window *window, window->format.format = fmt->format; wl_array_copy(&window->format.modifiers, &fmt->modifiers); - return; + return true; } - - assert(0 && "error: no valid pair of format/modifier in the scanout tranche"); + return false; } static void @@ -1278,25 +1277,37 @@ dmabuf_feedback_done(void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_fee { struct window *window = data; struct dmabuf_feedback_tranche *tranche; + bool got_scanout_tranche = false; unsigned int i; fprintf(stderr, L_LAST " end of dma-buf feedback\n\n"); /* The first time that we receive dma-buf feedback for a surface it - * contains only the renderer tranche. We pick the INITIAL_BUFFER_FORMAT + * contains only the renderer tranches. We pick the INITIAL_BUFFER_FORMAT * from there. Then the compositor should detect that the format is * unsupported by the underlying hardware (not actually, but you should - * have faked this in the DRM-backend) and send the scanout tranche. We - * use the formats/modifiers of the scanout tranche to reallocate our + * have faked this in the DRM-backend) and send the scanout tranches. We + * use the formats/modifiers of the scanout tranches to reallocate our * buffers. */ wl_array_for_each(tranche, &window->pending_dmabuf_feedback.tranches) { if (tranche->is_scanout_tranche) { - pick_format_from_scanout_tranche(window, tranche); - for (i = 0; i < NUM_BUFFERS; i++) - window->buffers[i].recreate = true; - break; + got_scanout_tranche = true; + if (pick_format_from_scanout_tranche(window, tranche)) { + for (i = 0; i < NUM_BUFFERS; i++) + window->buffers[i].recreate = true; + break; + } } - pick_initial_format_from_renderer_tranche(window, tranche); + if (pick_initial_format_from_renderer_tranche(window, tranche)) + break; + } + + if (got_scanout_tranche) { + assert(window->format.format != INITIAL_BUFFER_FORMAT && + "error: no valid pair of format/modifier in the scanout tranches"); + } else { + assert(window->format.format == INITIAL_BUFFER_FORMAT && + "error: INITIAL_BUFFER_FORMAT not supported by the hardware"); } dmabuf_feedback_fini(&window->dmabuf_feedback); From 572ad2d8a9159a4c8b66dc67f7d000441548fa6b Mon Sep 17 00:00:00 2001 From: Robert Mader Date: Thu, 12 May 2022 18:36:41 +0200 Subject: [PATCH 293/609] clients/simple-dmabuf-*: Use gbm_bo_create_with_modifiers2 It is used in Mesa. Lets switch to it as well in order to provide good examples and encourage proper API usage. Signed-off-by: Robert Mader --- clients/meson.build | 4 ++++ clients/simple-dmabuf-egl.c | 10 ++++++++++ clients/simple-dmabuf-feedback.c | 23 ++++++++++++++++++----- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/clients/meson.build b/clients/meson.build index 53783f15..6056cae5 100644 --- a/clients/meson.build +++ b/clients/meson.build @@ -35,6 +35,10 @@ dep_toytoolkit = declare_dependency( link_with: lib_toytoolkit, dependencies: deps_toytoolkit, ) +dep_gbm = dependency('gbm', required: false) +if dep_gbm.found() and dep_gbm.version().version_compare('>= 21.3') + config_h.set('HAVE_GBM_BO_CREATE_WITH_MODIFIERS2', '1') +endif simple_clients_enabled = get_option('simple-clients') simple_build_all = simple_clients_enabled.contains('all') diff --git a/clients/simple-dmabuf-egl.c b/clients/simple-dmabuf-egl.c index ef0d9de6..16d47ba9 100644 --- a/clients/simple-dmabuf-egl.c +++ b/clients/simple-dmabuf-egl.c @@ -343,12 +343,22 @@ create_dmabuf_buffer(struct display *display, struct buffer *buffer, #ifdef HAVE_GBM_MODIFIERS if (display->modifiers_count > 0) { +#ifdef HAVE_GBM_BO_CREATE_WITH_MODIFIERS2 + buffer->bo = gbm_bo_create_with_modifiers2(display->gbm.device, + buffer->width, + buffer->height, + buffer->format, + display->modifiers, + display->modifiers_count, + GBM_BO_USE_RENDERING); +#else buffer->bo = gbm_bo_create_with_modifiers(display->gbm.device, buffer->width, buffer->height, buffer->format, display->modifiers, display->modifiers_count); +#endif if (buffer->bo) buffer->modifier = gbm_bo_get_modifier(buffer->bo); } diff --git a/clients/simple-dmabuf-feedback.c b/clients/simple-dmabuf-feedback.c index a54e7941..f0d6febf 100644 --- a/clients/simple-dmabuf-feedback.c +++ b/clients/simple-dmabuf-feedback.c @@ -180,6 +180,7 @@ struct window { struct dmabuf_feedback dmabuf_feedback, pending_dmabuf_feedback; int card_fd; struct drm_format format; + uint32_t bo_flags; struct buffer buffers[NUM_BUFFERS]; }; @@ -461,7 +462,7 @@ buffer_free(struct buffer *buf) static void create_dmabuf_buffer(struct window *window, struct buffer *buf, uint32_t width, uint32_t height, uint32_t format, unsigned int count_modifiers, - uint64_t *modifiers); + uint64_t *modifiers, uint32_t bo_flags); static void buffer_recreate(struct buffer *buf) @@ -474,7 +475,7 @@ buffer_recreate(struct buffer *buf) create_dmabuf_buffer(window, buf, width, height, window->format.format, window->format.modifiers.size / sizeof(uint64_t), - window->format.modifiers.data); + window->format.modifiers.data, window->bo_flags); buf->recreate = false; } @@ -524,7 +525,7 @@ static const struct zwp_linux_buffer_params_v1_listener params_listener = { static void create_dmabuf_buffer(struct window *window, struct buffer *buf, uint32_t width, uint32_t height, uint32_t format, unsigned int count_modifiers, - uint64_t *modifiers) + uint64_t *modifiers, uint32_t bo_flags) { struct display *display = window->display; static uint32_t flags = 0; @@ -539,10 +540,18 @@ create_dmabuf_buffer(struct window *window, struct buffer *buf, uint32_t width, #ifdef HAVE_GBM_MODIFIERS if (count_modifiers > 0) { +#ifdef HAVE_GBM_BO_CREATE_WITH_MODIFIERS2 + buf->bo = gbm_bo_create_with_modifiers2(display->gbm_device, + buf->width, buf->height, + format, modifiers, + count_modifiers, + bo_flags); +#else buf->bo = gbm_bo_create_with_modifiers(display->gbm_device, buf->width, buf->height, format, modifiers, count_modifiers); +#endif if (buf->bo) buf->modifier = gbm_bo_get_modifier(buf->bo); } @@ -551,7 +560,7 @@ create_dmabuf_buffer(struct window *window, struct buffer *buf, uint32_t width, if (!buf->bo) { buf->bo = gbm_bo_create(display->gbm_device, buf->width, buf->height, buf->format, - GBM_BO_USE_RENDERING); + bo_flags); buf->modifier = DRM_FORMAT_MOD_INVALID; } @@ -938,7 +947,7 @@ create_window(struct display *display) create_dmabuf_buffer(window, &window->buffers[i], width, height, window->format.format, window->format.modifiers.size / sizeof(uint64_t), - window->format.modifiers.data); + window->format.modifiers.data, window->bo_flags); window->xdg_surface = xdg_wm_base_get_xdg_surface(display->wm_base, @@ -1237,6 +1246,8 @@ pick_initial_format_from_renderer_tranche(struct window *window, window->format.format = fmt->format; wl_array_copy(&window->format.modifiers, &fmt->modifiers); + window->bo_flags = GBM_BO_USE_RENDERING; + return true; } return false; @@ -1267,6 +1278,8 @@ pick_format_from_scanout_tranche(struct window *window, window->format.format = fmt->format; wl_array_copy(&window->format.modifiers, &fmt->modifiers); + window->bo_flags = GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT; + return true; } return false; From 2df71c6dd7f82a07f4ae6a8be69e7f94dd2cdec1 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Fri, 20 May 2022 07:35:14 -0500 Subject: [PATCH 294/609] rdp: Make thread checks unconfigurable Instead of a meson option or hidden define, just run these checks always. It is not Weston's style to add build options for specific asserts, and currently weston's codebase is expected to always run with asserts enabled. Signed-off-by: Derek Foreman --- libweston/backend-rdp/meson.build | 4 ---- libweston/backend-rdp/rdp.h | 10 ---------- libweston/backend-rdp/rdpclip.c | 30 +++++++++++++++--------------- libweston/backend-rdp/rdputil.c | 6 ++---- 4 files changed, 17 insertions(+), 33 deletions(-) diff --git a/libweston/backend-rdp/meson.build b/libweston/backend-rdp/meson.build index d43eb651..5794987d 100644 --- a/libweston/backend-rdp/meson.build +++ b/libweston/backend-rdp/meson.build @@ -4,10 +4,6 @@ endif config_h.set('BUILD_RDP_COMPOSITOR', '1') -if get_option('rdp-thread-check') - config_h.set('ENABLE_RDP_THREAD_CHECK', '1') -endif - dep_frdp = dependency('freerdp2', version: '>= 2.2.0', required: false) if not dep_frdp.found() error('RDP-backend requires freerdp >= 2.2.0 which was not found. Or, you can use \'-Dbackend-rdp=false\'.') diff --git a/libweston/backend-rdp/rdp.h b/libweston/backend-rdp/rdp.h index e2fc6abf..7fb42118 100644 --- a/libweston/backend-rdp/rdp.h +++ b/libweston/backend-rdp/rdp.h @@ -165,14 +165,6 @@ struct rdp_loop_task { rdp_loop_task_func_t func; }; -#ifdef ENABLE_RDP_THREAD_CHECK -#define ASSERT_COMPOSITOR_THREAD(b) assert_compositor_thread(b) -#define ASSERT_NOT_COMPOSITOR_THREAD(b) assert_not_compositor_thread(b) -#else -#define ASSERT_COMPOSITOR_THREAD(b) (void)b -#define ASSERT_NOT_COMPOSITOR_THREAD(b) (void)b -#endif /* ENABLE_RDP_THREAD_CHECK */ - #define rdp_debug_verbose(b, ...) \ rdp_debug_print(b->verbose, false, __VA_ARGS__) #define rdp_debug_verbose_continue(b, ...) \ @@ -201,13 +193,11 @@ rdp_wl_array_read_fd(struct wl_array *array, int fd); void convert_rdp_keyboard_to_xkb_rule_names(UINT32 KeyboardType, UINT32 KeyboardSubType, UINT32 KeyboardLayout, struct xkb_rule_names *xkbRuleNames); -#ifdef ENABLE_RDP_THREAD_CHECK void assert_compositor_thread(struct rdp_backend *b); void assert_not_compositor_thread(struct rdp_backend *b); -#endif /* ENABLE_RDP_THREAD_CHECK */ bool rdp_event_loop_add_fd(struct wl_event_loop *loop, diff --git a/libweston/backend-rdp/rdpclip.c b/libweston/backend-rdp/rdpclip.c index c8955e8c..1ac8588c 100644 --- a/libweston/backend-rdp/rdpclip.c +++ b/libweston/backend-rdp/rdpclip.c @@ -661,7 +661,7 @@ clipboard_data_source_unref(struct rdp_clipboard_data_source *source) struct rdp_backend *b = ctx->rdpBackend; char **p; - ASSERT_COMPOSITOR_THREAD(b); + assert_compositor_thread(b); assert(source->refcount); source->refcount--; @@ -764,7 +764,7 @@ clipboard_data_source_read(int fd, uint32_t mask, void *arg) __func__, source, clipboard_data_source_state_to_string(source), fd); - ASSERT_COMPOSITOR_THREAD(b); + assert_compositor_thread(b); assert(source->data_source_fd == fd); assert(source->refcount == 1); @@ -832,7 +832,7 @@ clipboard_data_source_fail(int fd, uint32_t mask, void *arg) rdp_debug_clipboard_verbose(b, "RDP %s (%p:%s) fd:%d\n", __func__, source, clipboard_data_source_state_to_string(source), fd); - ASSERT_COMPOSITOR_THREAD(b); + assert_compositor_thread(b); assert(source->data_source_fd == fd); /* this data source must be tracked as inflight */ @@ -886,7 +886,7 @@ clipboard_data_source_write(int fd, uint32_t mask, void *arg) clipboard_data_source_state_to_string(source), fd); - ASSERT_COMPOSITOR_THREAD(b); + assert_compositor_thread(b); assert(source->data_source_fd == fd); /* this data source must be tracked as inflight */ @@ -1018,7 +1018,7 @@ clipboard_data_source_send(struct weston_data_source *base, clipboard_data_source_state_to_string(source), fd, mime_type); - ASSERT_COMPOSITOR_THREAD(b); + assert_compositor_thread(b); if (ctx->clipboard_inflight_client_data_source) { /* Here means server side (Linux application) request clipboard data, @@ -1134,7 +1134,7 @@ clipboard_data_source_cancel(struct weston_data_source *base) __func__, source, clipboard_data_source_state_to_string(source)); - ASSERT_COMPOSITOR_THREAD(b); + assert_compositor_thread(b); if (source == ctx->clipboard_inflight_client_data_source) { source->is_canceled = true; @@ -1185,7 +1185,7 @@ clipboard_data_source_publish(bool freeOnly, void *arg) rdp_debug_clipboard(b, "RDP %s (%p:%s)\n", __func__, source, clipboard_data_source_state_to_string(source)); - ASSERT_COMPOSITOR_THREAD(b); + assert_compositor_thread(b); /* here is going to publish new data, if previous data from client is still referenced, unref it after selection */ @@ -1225,7 +1225,7 @@ clipboard_data_source_request(bool freeOnly, void *arg) bool found_requested_format; bool ret; - ASSERT_COMPOSITOR_THREAD(b); + assert_compositor_thread(b); if (freeOnly) goto error_exit_free_request; @@ -1331,7 +1331,7 @@ clipboard_set_selection(struct wl_listener *listener, void *data) rdp_debug_clipboard(b, "RDP %s (base:%p)\n", __func__, selection_data_source); - ASSERT_COMPOSITOR_THREAD(b); + assert_compositor_thread(b); if (selection_data_source == NULL) { return; @@ -1450,7 +1450,7 @@ clipboard_client_format_list(CliprdrServerContext *context, const CLIPRDR_FORMAT struct rdp_clipboard_data_source *source = NULL; char **p, *s; - ASSERT_NOT_COMPOSITOR_THREAD(b); + assert_not_compositor_thread(b); rdp_debug_clipboard(b, "Client: %s clipboard format list: numFormats:%d\n", __func__, formatList->numFormats); for (uint32_t i = 0; i < formatList->numFormats; i++) { @@ -1548,7 +1548,7 @@ clipboard_client_format_data_response(CliprdrServerContext *context, const CLIPR formatDataResponse->msgFlags, formatDataResponse->dataLen); - ASSERT_NOT_COMPOSITOR_THREAD(b); + assert_not_compositor_thread(b); if (!source) { rdp_debug_clipboard(b, "Client: %s client send data without server asking. protocol error", __func__); @@ -1612,7 +1612,7 @@ clipboard_client_format_list_response(CliprdrServerContext *context, struct rdp_backend *b = ctx->rdpBackend; rdp_debug_clipboard(b, "Client: %s msgFlags:0x%x\n", __func__, formatListResponse->msgFlags); - ASSERT_NOT_COMPOSITOR_THREAD(b); + assert_not_compositor_thread(b); return 0; } @@ -1631,7 +1631,7 @@ clipboard_client_format_data_request(CliprdrServerContext *context, __func__, formatDataRequest->requestedFormatId, clipboard_format_id_to_string(formatDataRequest->requestedFormatId, true)); - ASSERT_NOT_COMPOSITOR_THREAD(b); + assert_not_compositor_thread(b); /* Make sure clients requested the format we knew */ index = clipboard_find_supported_format_by_format_id(formatDataRequest->requestedFormatId); @@ -1671,7 +1671,7 @@ rdp_clipboard_init(freerdp_peer *client) assert(seat); - ASSERT_COMPOSITOR_THREAD(b); + assert_compositor_thread(b); ctx->clipboard_server_context = cliprdr_server_context_new(ctx->vcm); if (!ctx->clipboard_server_context) @@ -1717,7 +1717,7 @@ rdp_clipboard_destroy(RdpPeerContext *ctx) struct rdp_clipboard_data_source *data_source; struct rdp_backend *b = ctx->rdpBackend; - ASSERT_COMPOSITOR_THREAD(b); + assert_compositor_thread(b); if (ctx->clipboard_selection_listener.notify) { wl_list_remove(&ctx->clipboard_selection_listener.link); diff --git a/libweston/backend-rdp/rdputil.c b/libweston/backend-rdp/rdputil.c index 8b49b8e2..9aca3139 100644 --- a/libweston/backend-rdp/rdputil.c +++ b/libweston/backend-rdp/rdputil.c @@ -76,7 +76,6 @@ end: va_end(ap); } -#ifdef ENABLE_RDP_THREAD_CHECK void assert_compositor_thread(struct rdp_backend *b) { @@ -88,7 +87,6 @@ assert_not_compositor_thread(struct rdp_backend *b) { assert(b->compositor_tid != gettid()); } -#endif /* ENABLE_RDP_THREAD_CHECK */ bool rdp_event_loop_add_fd(struct wl_event_loop *loop, @@ -113,7 +111,7 @@ rdp_dispatch_task_to_display_loop(RdpPeerContext *peerCtx, { /* this function is ONLY used to queue the task from FreeRDP thread, * and the task to be processed at wayland display loop thread. */ - ASSERT_NOT_COMPOSITOR_THREAD(peerCtx->rdpBackend); + assert_not_compositor_thread(peerCtx->rdpBackend); task->peerCtx = peerCtx; task->func = func; @@ -134,7 +132,7 @@ rdp_dispatch_task(int fd, uint32_t mask, void *arg) eventfd_t dummy; /* this must be called back at wayland display loop thread */ - ASSERT_COMPOSITOR_THREAD(peerCtx->rdpBackend); + assert_compositor_thread(peerCtx->rdpBackend); eventfd_read(peerCtx->loop_task_event_source_fd, &dummy); From f8ca7847372cc98c452d007f2a289b8cf5fe2531 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Thu, 26 May 2022 12:43:18 -0500 Subject: [PATCH 295/609] rdp: Fix some accidental style errors in new clipboard code I missed a few things when this was initially merged. No functional changes. Signed-off-by: Derek Foreman --- libweston/backend-rdp/rdpclip.c | 54 +++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/libweston/backend-rdp/rdpclip.c b/libweston/backend-rdp/rdpclip.c index 1ac8588c..f92bf7a0 100644 --- a/libweston/backend-rdp/rdpclip.c +++ b/libweston/backend-rdp/rdpclip.c @@ -201,7 +201,7 @@ clipboard_process_text_utf8(struct rdp_clipboard_data_source *source, bool is_se if (data_size < 1) goto error_return; - data_size *= 2; // convert to size in bytes. + data_size *= 2; /* convert to size in bytes. */ if (!wl_array_add(&data_contents, data_size)) goto error_return; @@ -254,16 +254,20 @@ clipboard_process_text_utf8(struct rdp_clipboard_data_source *source, bool is_se source->processed_data_start = source->data_contents.data; source->processed_data_size = source->data_contents.size; rdp_debug_clipboard_verbose(b, "RDP %s (%p:%s): %s (%u bytes)\n", - __func__, source, clipboard_data_source_state_to_string(source), - is_send ? "send" : "receive", (uint32_t)source->data_contents.size); + __func__, source, + clipboard_data_source_state_to_string(source), + is_send ? "send" : "receive", + (uint32_t)source->data_contents.size); return true; error_return: source->state = RDP_CLIPBOARD_SOURCE_FAILED; weston_log("RDP %s FAILED (%p:%s): %s (%u bytes)\n", - __func__, source, clipboard_data_source_state_to_string(source), - is_send ? "send" : "receive", (uint32_t)source->data_contents.size); + __func__, source, + clipboard_data_source_state_to_string(source), + is_send ? "send" : "receive", + (uint32_t)source->data_contents.size); wl_array_release(&data_contents); @@ -298,7 +302,9 @@ clipboard_process_text_raw(struct rdp_clipboard_data_source *source, bool is_sen source->processed_data_start = source->data_contents.data; source->processed_data_size = source->data_contents.size; rdp_debug_clipboard_verbose(b, "RDP %s (%p): %s (%u bytes)\n", - __func__, source, is_send ? "send" : "receive", (uint32_t)source->data_contents.size); + __func__, source, + is_send ? "send" : "receive", + (uint32_t)source->data_contents.size); return true; } @@ -319,7 +325,7 @@ clipboard_process_html(struct rdp_clipboard_data_source *source, bool is_send) assert(!source->is_data_processed); - /* We're tresting the contents as a string for now, so null + /* We're treating the contents as a string for now, so null * terminate it so strstr can't run off the end. However, we * don't increase data_contents.size because we don't want * to affect the content. */ @@ -387,7 +393,8 @@ clipboard_process_html(struct rdp_clipboard_data_source *source, bool is_send) cur = buf + RDP_CLIPBOARD_FRAGMENT_END_OFFSET; sprintf(cur, "%08u", fragment_end); *(cur+8) = '\r'; - data_contents.size = strlen(buf)+1; /* +1 to null terminate. */ + + data_contents.size = strlen(buf) + 1; /* +1 to null terminate. */ } /* swap the data_contents with new one */ @@ -489,9 +496,10 @@ clipboard_process_bmp(struct rdp_clipboard_data_source *source, bool is_send) } rdp_debug_clipboard_verbose(b, "RDP %s (%p:%s): %s (%d bytes)\n", - __func__, source, clipboard_data_source_state_to_string(source), - is_send ? "send" : "receive", - (UINT32)source->data_contents.size); + __func__, source, + clipboard_data_source_state_to_string(source), + is_send ? "send" : "receive", + (uint32_t)source->data_contents.size); return true; @@ -499,7 +507,7 @@ error_return: source->state = RDP_CLIPBOARD_SOURCE_FAILED; weston_log("RDP %s FAILED (%p:%s): %s (%d bytes)\n", __func__, source, clipboard_data_source_state_to_string(source), - is_send ? "send" : "receive", (UINT32)source->data_contents.size); + is_send ? "send" : "receive", (uint32_t)source->data_contents.size); wl_array_release(&data_contents); @@ -936,7 +944,8 @@ clipboard_data_source_write(int fd, uint32_t mask, void *arg) source->state = RDP_CLIPBOARD_SOURCE_FAILED; weston_log("RDP %s (%p:%s) write failed %s\n", __func__, source, - clipboard_data_source_state_to_string(source), strerror(errno)); + clipboard_data_source_state_to_string(source), + strerror(errno)); break; } /* buffer is full, wait until data_source_fd is writable again */ @@ -969,6 +978,7 @@ fail: /* and remove the event source */ wl_event_source_remove(source->transfer_event_source); source->transfer_event_source = NULL; + /* and reset the inflight transfer state. */ source->inflight_write_count = 0; source->inflight_data_to_write = NULL; source->inflight_data_size = 0; @@ -1051,7 +1061,7 @@ clipboard_data_source_send(struct weston_data_source *base, if (index >= 0 && /* check supported by this RDP bridge */ source->client_format_id_table[index]) { /* check supported by current data source from client */ ctx->clipboard_inflight_client_data_source = source; - source->refcount++; // reference while request inflight. + source->refcount++; /* reference while request inflight. */ source->data_source_fd = fd; assert(source->inflight_write_count == 0); assert(source->inflight_data_to_write == NULL); @@ -1075,8 +1085,9 @@ clipboard_data_source_send(struct weston_data_source *base, &source->transfer_event_source); if (!ret) { source->state = RDP_CLIPBOARD_SOURCE_FAILED; - weston_log("RDP %s (%p:%s) wl_event_loop_add_fd failed\n", - __func__, source, clipboard_data_source_state_to_string(source)); + weston_log("RDP %s (%p:%s) rdp_event_loop_add_fd failed\n", + __func__, source, + clipboard_data_source_state_to_string(source)); goto error_return_unref_source; } } else { @@ -1267,7 +1278,7 @@ clipboard_data_source_request(bool freeOnly, void *arg) wl_array_init(&source->data_contents); source->is_data_processed = false; source->context = ctx->item.peer; - source->refcount = 1; // decremented when data sent to client. + source->refcount = 1; /* decremented when data sent to client. */ source->data_source_fd = -1; source->format_index = index; @@ -1291,7 +1302,7 @@ clipboard_data_source_request(bool freeOnly, void *arg) &source->transfer_event_source); if (!ret) { source->state = RDP_CLIPBOARD_SOURCE_FAILED; - weston_log("RDP %s (%p:%s) wl_event_loop_add_fd failed.\n", + weston_log("RDP %s (%p:%s) rdp_event_loop_add_fd failed.\n", __func__, source, clipboard_data_source_state_to_string(source)); goto error_exit_free_source; @@ -1385,8 +1396,6 @@ clipboard_set_selection(struct wl_listener *listener, void *data) } else { rdp_debug_clipboard(b, "RDP %s (base:%p) no supported formats\n", __func__, selection_data_source); } - - return; } /*********************\ @@ -1467,12 +1476,13 @@ clipboard_client_format_list(CliprdrServerContext *context, const CLIPRDR_FORMAT source->state = RDP_CLIPBOARD_SOURCE_ALLOCATED; rdp_debug_clipboard(b, "Client: %s (%p:%s) allocated\n", - __func__, source, clipboard_data_source_state_to_string(source)); + __func__, source, + clipboard_data_source_state_to_string(source)); wl_signal_init(&source->base.destroy_signal); wl_array_init(&source->base.mime_types); wl_array_init(&source->data_contents); source->context = client; - source->refcount = 1; // decremented when another source is selected. + source->refcount = 1; /* decremented when another source is selected. */ source->data_source_fd = -1; source->format_index = -1; From c8db957a0b3f762e89782990af7259c38c030a5b Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Tue, 17 May 2022 16:16:24 -0500 Subject: [PATCH 296/609] rdp: Add audio support Allow the front end to register audio setup and teardown functions. These functions should use FreeRDP's rdpsnd_server_context or audin_server_context and set up their own handler threads. The backend remains mostly ignorant to any audio details beyond setting up and tearing down. Signed-off-by: Derek Foreman --- include/libweston/backend-rdp.h | 9 ++++++++ libweston/backend-rdp/rdp.c | 39 +++++++++++++++++++++++++++++++-- libweston/backend-rdp/rdp.h | 8 +++++++ 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/include/libweston/backend-rdp.h b/include/libweston/backend-rdp.h index e88cd856..c9eb1609 100644 --- a/include/libweston/backend-rdp.h +++ b/include/libweston/backend-rdp.h @@ -57,6 +57,11 @@ weston_rdp_output_get_api(struct weston_compositor *compositor) #define WESTON_RDP_BACKEND_CONFIG_VERSION 2 +typedef void *(*rdp_audio_in_setup)(struct weston_compositor *c, void *vcm); +typedef void (*rdp_audio_in_teardown)(void *audio_private); +typedef void *(*rdp_audio_out_setup)(struct weston_compositor *c, void *vcm); +typedef void (*rdp_audio_out_teardown)(void *audio_private); + struct weston_rdp_backend_config { struct weston_backend_config base; char *bind_address; @@ -70,6 +75,10 @@ struct weston_rdp_backend_config { bool remotefx_codec; int external_listener_fd; int refresh_rate; + rdp_audio_in_setup audio_in_setup; + rdp_audio_in_teardown audio_in_teardown; + rdp_audio_out_setup audio_out_setup; + rdp_audio_out_teardown audio_out_teardown; }; #ifdef __cplusplus diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index 24e98c98..e78b26fa 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -668,10 +668,14 @@ out_error_stream: static void rdp_peer_context_free(freerdp_peer* client, RdpPeerContext* context) { + struct rdp_backend *b; unsigned i; + if (!context) return; + b = context->rdpBackend; + wl_list_remove(&context->item.link); for (i = 0; i < ARRAY_LENGTH(context->events); i++) { @@ -679,6 +683,12 @@ rdp_peer_context_free(freerdp_peer* client, RdpPeerContext* context) wl_event_source_remove(context->events[i]); } + if (context->audio_in_private) + b->audio_in_teardown(context->audio_in_private); + + if (context->audio_out_private) + b->audio_out_teardown(context->audio_out_private); + rdp_clipboard_destroy(context); if (context->vcm) @@ -936,11 +946,22 @@ xf_peer_activate(freerdp_peer* client) settings->CompressionEnabled = FALSE; } - if (settings->RedirectClipboard) { + settings->AudioPlayback = b->audio_out_setup && b->audio_out_teardown; + settings->AudioCapture = b->audio_in_setup && b->audio_in_teardown; + + if (settings->RedirectClipboard || + settings->AudioPlayback || + settings->AudioCapture) { if (!peerCtx->vcm) { - weston_log("Virtual channel is required for clipboard\n"); + weston_log("Virtual channel is required for clipboard, audio playback/capture\n"); goto error_exit; } + /* Audio setup will return NULL on failure, and we'll proceed without audio */ + if (settings->AudioPlayback) + peerCtx->audio_out_private = b->audio_out_setup(b->compositor, peerCtx->vcm); + + if (settings->AudioCapture) + peerCtx->audio_in_private = b->audio_in_setup(b->compositor, peerCtx->vcm); } if (output->base.width != (int)settings->DesktopWidth || @@ -1043,6 +1064,12 @@ error_exit: rdp_clipboard_destroy(peerCtx); + if (settings->AudioPlayback && peerCtx->audio_out_private) + b->audio_out_teardown(peerCtx->audio_out_private); + + if (settings->AudioCapture && peerCtx->audio_in_private) + b->audio_in_teardown(peerCtx->audio_in_private); + return FALSE; } @@ -1585,6 +1612,10 @@ rdp_backend_create(struct weston_compositor *compositor, b->no_clients_resize = config->no_clients_resize; b->force_no_compression = config->force_no_compression; b->remotefx_codec = config->remotefx_codec; + b->audio_in_setup = config->audio_in_setup; + b->audio_in_teardown = config->audio_in_teardown; + b->audio_out_setup = config->audio_out_setup; + b->audio_out_teardown = config->audio_out_teardown; b->debug = weston_compositor_add_log_scope(compositor, "rdp-backend", @@ -1729,6 +1760,10 @@ config_init_to_defaults(struct weston_rdp_backend_config *config) config->remotefx_codec = true; config->external_listener_fd = -1; config->refresh_rate = RDP_DEFAULT_FREQ; + config->audio_in_setup = NULL; + config->audio_in_teardown = NULL; + config->audio_out_setup = NULL; + config->audio_out_teardown = NULL; } WL_EXPORT int diff --git a/libweston/backend-rdp/rdp.h b/libweston/backend-rdp/rdp.h index 7fb42118..abc03a13 100644 --- a/libweston/backend-rdp/rdp.h +++ b/libweston/backend-rdp/rdp.h @@ -92,6 +92,11 @@ struct rdp_backend { int external_listener_fd; int rdp_monitor_refresh_rate; pid_t compositor_tid; + + rdp_audio_in_setup audio_in_setup; + rdp_audio_in_teardown audio_in_teardown; + rdp_audio_out_setup audio_out_setup; + rdp_audio_out_teardown audio_out_teardown; }; enum peer_item_flags { @@ -149,6 +154,9 @@ struct rdp_peer_context { /* Clipboard support */ CliprdrServerContext *clipboard_server_context; + void *audio_in_private; + void *audio_out_private; + struct rdp_clipboard_data_source *clipboard_client_data_source; struct rdp_clipboard_data_source *clipboard_inflight_client_data_source; From 3696d9b6a13d07a39080e7412e778d3e90cd6c48 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 26 Apr 2022 11:47:07 +0300 Subject: [PATCH 297/609] libweston: add basic output color characteristics API This adds color_chracteristics field in weston_output. This field is intended to be set by compositor frontends and read by color managers. Color managers can use this information when choosing the output color space and dynamic range, particularly when no ICC profile has been set. This is most useful for HDR outputs, where the HDR static metadata for PQ mode or the display luminance parameters for HLG mode can be based on color_characteristics. The fields of weston_color_characteristics mirror the information available in EDID. However, using EDID information as-is has several caveats, so the decision to use EDID for this is left for the frontend and ultimately to the end user. There are no defined ranges or validity checks for this data. The color manager will have to validate the values against whatever it is using them for. Signed-off-by: Pekka Paalanen --- include/libweston/libweston.h | 65 +++++++++++++++++++++++++++++++++++ libweston/compositor.c | 44 ++++++++++++++++++++++++ 2 files changed, 109 insertions(+) diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index bd5ac8c6..01c4e4d7 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -241,6 +241,63 @@ enum weston_eotf_mode { ((uint32_t)(WESTON_EOTF_MODE_SDR | WESTON_EOTF_MODE_TRADITIONAL_HDR | \ WESTON_EOTF_MODE_ST2084 | WESTON_EOTF_MODE_HLG)) +/** CIE 1931 xy chromaticity coordinates */ +struct weston_CIExy { + float x; + float y; +}; + +enum weston_color_characteristics_groups { + /** weston_color_characteristics::primary is set */ + WESTON_COLOR_CHARACTERISTICS_GROUP_PRIMARIES = 0x01, + + /** weston_color_characteristics::white is set */ + WESTON_COLOR_CHARACTERISTICS_GROUP_WHITE = 0x02, + + /** weston_color_characteristics::max_luminance is set */ + WESTON_COLOR_CHARACTERISTICS_GROUP_MAXL = 0x04, + + /** weston_color_characteristics::min_luminance is set */ + WESTON_COLOR_CHARACTERISTICS_GROUP_MINL = 0x08, + + /** weston_color_characteristics::maxFALL is set */ + WESTON_COLOR_CHARACTERISTICS_GROUP_MAXFALL = 0x10, + + /** all valid bits */ + WESTON_COLOR_CHARACTERISTICS_GROUP_ALL_MASK = 0x1f +}; + +/** Basic display color characteristics + * + * This is a simple description of a display or output (monitor) color + * characteristics. The parameters can be found in EDID, with caveats. They + * are particularly useful with HDR monitors. + */ +struct weston_color_characteristics { + /** Which fields are valid + * + * A bitmask of values from enum weston_color_characteristics_groups. + */ + uint32_t group_mask; + + /* EOTF is tracked externally with enum weston_eotf_mode */ + + /** Chromaticities of the primaries */ + struct weston_CIExy primary[3]; + + /** White point chromaticity */ + struct weston_CIExy white; + + /** Display's desired maximum content peak luminance, cd/m² */ + float max_luminance; + + /** Display's desired minimum content luminance, cd/m² */ + float min_luminance; + + /** Display's desired maximum frame-average light level, cd/m² */ + float maxFALL; +}; + /** Represents a head, usually a display connector * * \rst @@ -414,6 +471,7 @@ struct weston_output { struct weston_color_profile *color_profile; bool from_blend_to_output_by_backend; enum weston_eotf_mode eotf_mode; + struct weston_color_characteristics color_characteristics; struct weston_output_color_outcome *color_outcome; @@ -2142,6 +2200,13 @@ weston_output_set_eotf_mode(struct weston_output *output, enum weston_eotf_mode weston_output_get_eotf_mode(const struct weston_output *output); +void +weston_output_set_color_characteristics(struct weston_output *output, + const struct weston_color_characteristics *cc); + +const struct weston_color_characteristics * +weston_output_get_color_characteristics(struct weston_output *output); + void weston_output_init(struct weston_output *output, struct weston_compositor *compositor, diff --git a/libweston/compositor.c b/libweston/compositor.c index cd053d1a..e5c4bc70 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -6764,6 +6764,50 @@ weston_output_get_eotf_mode(const struct weston_output *output) return output->eotf_mode; } +/** Set display or monitor basic color characteristics + * + * \param output The output to modify, must be in disabled state. + * \param cc The new characteristics to set, or NULL to unset everything. + * + * This sets the metadata that describes the color characteristics of the + * output in a very simple manner. If a non-NULL color profile is set for the + * output, that will always take precedence. + * + * The initial value has everything unset. + * + * This function is meant to be used by compositor frontends. + * + * \ingroup output + * \sa weston_output_set_color_profile + */ +WL_EXPORT void +weston_output_set_color_characteristics(struct weston_output *output, + const struct weston_color_characteristics *cc) +{ + assert(!output->enabled); + + if (cc) + output->color_characteristics = *cc; + else + output->color_characteristics.group_mask = 0; +} + +/** Get display or monitor basic color characteristics + * + * \param output The output to query. + * \return Pointer to the metadata stored in weston_output. + * + * This function is meant to be used by color manager modules. + * + * \ingroup output + * \sa weston_output_set_color_characteristics + */ +WL_EXPORT const struct weston_color_characteristics * +weston_output_get_color_characteristics(struct weston_output *output) +{ + return &output->color_characteristics; +} + /** Initializes a weston_output object with enough data so ** an output can be configured. * From 518d72a37b31eb3a891a378894c5eae8009ac281 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Wed, 16 Mar 2022 13:25:13 +0200 Subject: [PATCH 298/609] compositor: add color_characteristics weston.ini option This adds an option to program basic display color characteristics from weston.ini. In the future there will be a way to set this information from EDID, but because EDID is unreliable that will probably not be the default. An ICC profile will likely override most or all of this. The main reason to add this option is to be able to characterise HDR monitors. An 'output' section can have a key 'color_characteristics' (string) set to a name. The name refers to any 'color_characteristics' section with 'name' set to the same string. The 'name' key of a 'color_characteristics' section cannot contain a colon ':'. Names with colon in 'output' section key 'color_characteristics' value are reserved for future use, e.g. to indicate that the metadata is to be taken from EDID instead of a weston.ini section. Signed-off-by: Pekka Paalanen --- compositor/main.c | 161 +++++++++++++++++++++++++++++++++++- compositor/weston-private.h | 34 ++++++++ man/weston.ini.man | 70 ++++++++++++++++ 3 files changed, 264 insertions(+), 1 deletion(-) create mode 100644 compositor/weston-private.h diff --git a/compositor/main.c b/compositor/main.c index 9a90885e..b9c740f3 100644 --- a/compositor/main.c +++ b/compositor/main.c @@ -1,7 +1,7 @@ /* * Copyright © 2010-2011 Intel Corporation * Copyright © 2008-2011 Kristian Høgsberg - * Copyright © 2012-2018 Collabora, Ltd. + * Copyright © 2012-2018,2022 Collabora, Ltd. * Copyright © 2010-2011 Benjamin Franzke * Copyright © 2013 Jason Ekstrand * Copyright © 2017, 2018 General Electric Company @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -54,6 +55,7 @@ #include "git-version.h" #include #include "weston.h" +#include "weston-private.h" #include #include @@ -1383,6 +1385,156 @@ wet_output_set_eotf_mode(struct weston_output *output, return 0; } +struct wet_color_characteristics_keys { + const char *name; + enum weston_color_characteristics_groups group; + float minval; + float maxval; +}; + +#define COLOR_CHARAC_NAME "color_characteristics" + +static int +parse_color_characteristics(struct weston_color_characteristics *cc_out, + struct weston_config_section *section) +{ + static const struct wet_color_characteristics_keys keys[] = { + { "red_x", WESTON_COLOR_CHARACTERISTICS_GROUP_PRIMARIES, 0.0f, 1.0f }, + { "red_y", WESTON_COLOR_CHARACTERISTICS_GROUP_PRIMARIES, 0.0f, 1.0f }, + { "green_x", WESTON_COLOR_CHARACTERISTICS_GROUP_PRIMARIES, 0.0f, 1.0f }, + { "green_y", WESTON_COLOR_CHARACTERISTICS_GROUP_PRIMARIES, 0.0f, 1.0f }, + { "blue_x", WESTON_COLOR_CHARACTERISTICS_GROUP_PRIMARIES, 0.0f, 1.0f }, + { "blue_y", WESTON_COLOR_CHARACTERISTICS_GROUP_PRIMARIES, 0.0f, 1.0f }, + { "white_x", WESTON_COLOR_CHARACTERISTICS_GROUP_WHITE, 0.0f, 1.0f }, + { "white_y", WESTON_COLOR_CHARACTERISTICS_GROUP_WHITE, 0.0f, 1.0f }, + { "max_L", WESTON_COLOR_CHARACTERISTICS_GROUP_MAXL, 0.0f, 1e5f }, + { "min_L", WESTON_COLOR_CHARACTERISTICS_GROUP_MINL, 0.0f, 1e5f }, + { "maxFALL", WESTON_COLOR_CHARACTERISTICS_GROUP_MAXFALL, 0.0f, 1e5f }, + }; + static const char *msgpfx = "Config error in weston.ini [" COLOR_CHARAC_NAME "]"; + struct weston_color_characteristics cc = {}; + float *const keyvalp[ARRAY_LENGTH(keys)] = { + /* These must be in the same order as keys[]. */ + &cc.primary[0].x, &cc.primary[0].y, + &cc.primary[1].x, &cc.primary[1].y, + &cc.primary[2].x, &cc.primary[2].y, + &cc.white.x, &cc.white.y, + &cc.max_luminance, + &cc.min_luminance, + &cc.maxFALL, + }; + bool found[ARRAY_LENGTH(keys)] = {}; + uint32_t missing_group_mask = 0; + unsigned i; + char *section_name; + int ret = 0; + + weston_config_section_get_string(section, "name", + §ion_name, ""); + if (strchr(section_name, ':') != NULL) { + ret = -1; + weston_log("%s name=%s: reserved name. Do not use ':' character in the name.\n", + msgpfx, section_name); + } + + /* Parse keys if they exist */ + for (i = 0; i < ARRAY_LENGTH(keys); i++) { + double value; + + if (weston_config_section_get_double(section, keys[i].name, + &value, NAN) == 0) { + float f = value; + + found[i] = true; + + /* Range check, NaN shall not pass. */ + if (f >= keys[i].minval && f <= keys[i].maxval) { + /* Key found, parsed, and good value. */ + *keyvalp[i] = f; + continue; + } + + ret = -1; + weston_log("%s name=%s: %s value %f is outside of the range %f - %f.\n", + msgpfx, section_name, keys[i].name, value, + keys[i].minval, keys[i].maxval); + continue; + } + + if (errno == EINVAL) { + found[i] = true; + ret = -1; + weston_log("%s name=%s: failed to parse the value of key %s.\n", + msgpfx, section_name, keys[i].name); + } + } + + /* Collect set and unset groups */ + for (i = 0; i < ARRAY_LENGTH(keys); i++) { + uint32_t group = keys[i].group; + + if (found[i]) + cc.group_mask |= group; + else + missing_group_mask |= group; + } + + /* Ensure groups are given fully or not at all. */ + for (i = 0; i < ARRAY_LENGTH(keys); i++) { + uint32_t group = keys[i].group; + + if ((cc.group_mask & group) && (missing_group_mask & group)) { + ret = -1; + weston_log("%s name=%s: group %d key %s is %s. " + "You must set either none or all keys of a group.\n", + msgpfx, section_name, ffs(group), keys[i].name, + found[i] ? "set" : "missing"); + } + } + + free(section_name); + + if (ret == 0) + *cc_out = cc; + + return ret; +} + +WESTON_EXPORT_FOR_TESTS int +wet_output_set_color_characteristics(struct weston_output *output, + struct weston_config *wc, + struct weston_config_section *section) +{ + char *cc_name = NULL; + struct weston_config_section *cc_section; + struct weston_color_characteristics cc; + + weston_config_section_get_string(section, COLOR_CHARAC_NAME, + &cc_name, NULL); + if (!cc_name) + return 0; + + cc_section = weston_config_get_section(wc, COLOR_CHARAC_NAME, + "name", cc_name); + if (!cc_section) { + weston_log("Config error in weston.ini, output %s: " + "no [" COLOR_CHARAC_NAME "] section with 'name=%s' found.\n", + output->name, cc_name); + goto out_error; + } + + if (parse_color_characteristics(&cc, cc_section) < 0) + goto out_error; + + weston_output_set_color_characteristics(output, &cc); + free(cc_name); + return 0; + +out_error: + free(cc_name); + return -1; +} + static void allow_content_protection(struct weston_output *output, struct weston_config_section *section) @@ -1909,6 +2061,10 @@ drm_backend_output_configure(struct weston_output *output, if (wet_output_set_eotf_mode(output, section) < 0) return -1; + if (wet_output_set_color_characteristics(output, + wet->config, section) < 0) + return -1; + return 0; } @@ -2732,6 +2888,9 @@ headless_backend_output_configure(struct weston_output *output) if (wet_output_set_eotf_mode(output, section) < 0) return -1; + if (wet_output_set_color_characteristics(output, wc, section) < 0) + return -1; + return wet_configure_windowed_output_from_config(output, &defaults); } diff --git a/compositor/weston-private.h b/compositor/weston-private.h new file mode 100644 index 00000000..01b37dcf --- /dev/null +++ b/compositor/weston-private.h @@ -0,0 +1,34 @@ +/* + * Copyright 2022 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include +#include + +int +wet_output_set_color_characteristics(struct weston_output *output, + struct weston_config *wc, + struct weston_config_section *section); diff --git a/man/weston.ini.man b/man/weston.ini.man index d622e9b6..d9b17d85 100644 --- a/man/weston.ini.man +++ b/man/weston.ini.man @@ -567,6 +567,13 @@ The mode can be one of the following strings: .IP Defaults to .BR sdr ". Non-SDR modes require " "color-management=true" . +.TP 7 +.BI "color_characteristics=" name +Sets the basic output color characteristics by loading the parameters from the +.B color_characteristics +section with the key +.BI "name=" name +\&. If an ICC profile is also set, the ICC profile takes precedence. .\"--------------------------------------------------------------------- .SH "INPUT-METHOD SECTION" .TP 7 @@ -663,6 +670,69 @@ parallel to Weston, so it does not have to immediately exit. Defaults to empty. If set to true, quit Weston after the auto-launched executable exits. Set to false by default. .\"--------------------------------------------------------------------- +.SH "COLOR_CHARACTERISTICS SECTION" +Each +.B color_characteristics +section records one set of basic display or monitor color characterisation +parameters. The parameters are defined in CTA-861-H specification as Static +Metadata Type 1, and they can also be found in EDID. The parameters are +divided into groups. Each group must be given either fully or not at all. +.PP +Each section should be named with +.B name +key by which it can be referenced from other sections. A metadata section is +just a collection of parameter values and does nothing on its own. It has an +effect only when referenced from elsewhere. +.PP +See +.BR output " section key " color_characteristics . +.TP 7 +.BI "name=" name +An arbitrary name for this section. You can choose any name you want as long as +it does not contain the colon +.RB ( : ) +character. Names with at least one colon are reserved. +.SS Primaries group +.TP 7 +.BI "red_x=" x +.TQ +.BI "red_y=" y +.TQ +.BI "green_x=" x +.TQ +.BI "green_y=" y +.TQ +.BI "blue_x=" x +.TQ +.BI "blue_y=" y +The CIE 1931 xy chromaticity coordinates of the display primaries. +These floating point values must reside between 0.0 and 1.0, inclusive. +.SS White point group +.TP 7 +.BI "white_x=" x +.TQ +.BI "white_y=" y +The CIE 1931 xy chromaticity coordinates of the display white point. +These floating point values must reside between 0.0 and 1.0, inclusive. +.SS Independent parameters +Each parameter listed here has its own group and therefore can be given +alone. +.TP 7 +.BI "max_L=" L +Display's desired maximum content luminance (peak) +.IR L \~cd/m², +a floating point value in the range 0.0\(en100000.0. +.TP 7 +.BI "min_L=" L +Display's desired minimum content luminance +.IR L \~cd/m², +a floating point value in the range 0.0\(en100000.0. +.TP 7 +.BI "maxFALL=" L +Display's desired maximum frame-average light level +.IR L \~cd/m², +a floating point value in the range 0.0\(en100000.0. +.\"--------------------------------------------------------------------- .SH "SEE ALSO" .BR weston (1), .BR weston-bindings (7), From cea53a90d48fa8215ad7979f1139b24bad4ca190 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 23 Jul 2021 17:35:22 +0300 Subject: [PATCH 299/609] libweston: add HDR metadata to weston_output This adds hdr_meta field in weston_output_color_outcome. This field is intended to be set by color manager modules, and read by backends which will send the information to the video sink in SMPTE ST 2084 mode a.k.a Perceptual Quantizer HDR system. Such metadata is essential in ST 2084 mode for the video sink to produce a good picture. The validation of the data and the group split is based on the HDR Static Metata Type 1 definition in CTA-861-G specification. Signed-off-by: Pekka Paalanen --- include/libweston/libweston.h | 60 +++++++++++++++++++++++ libweston/backend.h | 5 ++ libweston/color-lcms/color-lcms.c | 2 + libweston/color-noop.c | 2 + libweston/compositor.c | 80 ++++++++++++++++++++++++++++++- 5 files changed, 148 insertions(+), 1 deletion(-) diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 01c4e4d7..ceaca6a9 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -247,6 +247,63 @@ struct weston_CIExy { float y; }; +enum weston_hdr_metadata_type1_groups { + /** weston_hdr_metadata_type1::primary is set */ + WESTON_HDR_METADATA_TYPE1_GROUP_PRIMARIES = 0x01, + + /** weston_hdr_metadata_type1::white is set */ + WESTON_HDR_METADATA_TYPE1_GROUP_WHITE = 0x02, + + /** weston_hdr_metadata_type1::maxDML is set */ + WESTON_HDR_METADATA_TYPE1_GROUP_MAXDML = 0x04, + + /** weston_hdr_metadata_type1::minDML is set */ + WESTON_HDR_METADATA_TYPE1_GROUP_MINDML = 0x08, + + /** weston_hdr_metadata_type1::maxCLL is set */ + WESTON_HDR_METADATA_TYPE1_GROUP_MAXCLL = 0x10, + + /** weston_hdr_metadata_type1::maxFALL is set */ + WESTON_HDR_METADATA_TYPE1_GROUP_MAXFALL = 0x20, + + /** all valid bits */ + WESTON_HDR_METADATA_TYPE1_GROUP_ALL_MASK = 0x3f +}; + +/** HDR static metadata type 1 + * + * The fields are defined by CTA-861-G except here they use float encoding. + * + * In Weston used only with HDR display modes. + */ +struct weston_hdr_metadata_type1 { + /** Which fields are valid + * + * A bitmask of values from enum weston_hdr_metadata_type1_groups. + */ + uint32_t group_mask; + + /* EOTF is tracked externally with enum weston_eotf_mode */ + + /** Chromaticities of the primaries, in any order */ + struct weston_CIExy primary[3]; + + /** White point chromaticity */ + struct weston_CIExy white; + + /** Maximum display mastering luminance, 1 - 65535 cd/m² */ + float maxDML; + + /** Minimum display mastering luminance, 0.0001 - 6.5535 cd/m² */ + float minDML; + + /** Maximum content light level, 1 - 65535 cd/m² */ + float maxCLL; + + /** Maximum frame-average light level, 1 - 65535 cd/m² */ + float maxFALL; +}; + enum weston_color_characteristics_groups { /** weston_color_characteristics::primary is set */ WESTON_COLOR_CHARACTERISTICS_GROUP_PRIMARIES = 0x01, @@ -358,6 +415,9 @@ struct weston_output_color_outcome { /** Blending to output color space transformation */ struct weston_color_transform *from_blend_to_output; + + /** HDR Static Metadata Type 1 for WESTON_EOTF_MODE_ST2084 */ + struct weston_hdr_metadata_type1 hdr_meta; }; /** Content producer for heads diff --git a/libweston/backend.h b/libweston/backend.h index 26624cf9..2374b17b 100644 --- a/libweston/backend.h +++ b/libweston/backend.h @@ -32,6 +32,8 @@ #ifndef LIBWESTON_BACKEND_INTERNAL_H #define LIBWESTON_BACKEND_INTERNAL_H +struct weston_hdr_metadata_type1; + struct weston_backend { void (*destroy)(struct weston_compositor *compositor); @@ -173,6 +175,9 @@ void weston_output_region_from_global(struct weston_output *output, pixman_region32_t *region); +const struct weston_hdr_metadata_type1 * +weston_output_get_hdr_metadata_type1(const struct weston_output *output); + /* weston_seat */ void diff --git a/libweston/color-lcms/color-lcms.c b/libweston/color-lcms/color-lcms.c index 005cbc9d..f8d44530 100644 --- a/libweston/color-lcms/color-lcms.c +++ b/libweston/color-lcms/color-lcms.c @@ -208,6 +208,8 @@ cmlcms_create_output_color_outcome(struct weston_color_manager *cm_base, &co->from_sRGB_to_output)) goto out_fail; + co->hdr_meta.group_mask = 0; + return co; out_fail: diff --git a/libweston/color-noop.c b/libweston/color-noop.c index 2e1461f3..4bd1e762 100644 --- a/libweston/color-noop.c +++ b/libweston/color-noop.c @@ -116,6 +116,8 @@ cmnoop_create_output_color_outcome(struct weston_color_manager *cm_base, co->from_sRGB_to_blend = NULL; co->from_sRGB_to_output = NULL; + co->hdr_meta.group_mask = 0; + return co; } diff --git a/libweston/compositor.c b/libweston/compositor.c index e5c4bc70..47cd4ff9 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -6449,6 +6449,57 @@ weston_output_transform_coordinate(struct weston_output *output, *y = p.f[1] / p.f[3]; } +static bool +validate_float_range(float val, float min, float max) +{ + return val >= min && val <= max; +} + +/* Based on CTA-861-G, HDR static metadata type 1 */ +static bool +weston_hdr_metadata_type1_validate(const struct weston_hdr_metadata_type1 *md) +{ + unsigned i; + + if (md->group_mask & WESTON_HDR_METADATA_TYPE1_GROUP_PRIMARIES) { + for (i = 0; i < ARRAY_LENGTH(md->primary); i++) { + if (!validate_float_range(md->primary[i].x, 0.0, 1.0)) + return false; + if (!validate_float_range(md->primary[i].y, 0.0, 1.0)) + return false; + } + } + + if (md->group_mask & WESTON_HDR_METADATA_TYPE1_GROUP_WHITE) { + if (!validate_float_range(md->white.x, 0.0, 1.0)) + return false; + if (!validate_float_range(md->white.y, 0.0, 1.0)) + return false; + } + + if (md->group_mask & WESTON_HDR_METADATA_TYPE1_GROUP_MAXDML) { + if (!validate_float_range(md->maxDML, 1.0, 65535.0)) + return false; + } + + if (md->group_mask & WESTON_HDR_METADATA_TYPE1_GROUP_MINDML) { + if (!validate_float_range(md->minDML, 0.0001, 6.5535)) + return false; + } + + if (md->group_mask & WESTON_HDR_METADATA_TYPE1_GROUP_MAXCLL) { + if (!validate_float_range(md->maxCLL, 1.0, 65535.0)) + return false; + } + + if (md->group_mask & WESTON_HDR_METADATA_TYPE1_GROUP_MAXFALL) { + if (!validate_float_range(md->maxFALL, 1.0, 65535.0)) + return false; + } + + return true; +} + WL_EXPORT void weston_output_color_outcome_destroy(struct weston_output_color_outcome **pco) { @@ -6475,10 +6526,15 @@ weston_output_set_color_outcome(struct weston_output *output) if (!colorout) { weston_log("Creating color transformation for output \"%s\" failed.\n", output->name); - return false; } + if (!weston_hdr_metadata_type1_validate(&colorout->hdr_meta)) { + weston_log("Internal color manager error creating Metadata Type 1 for output \"%s\".\n", + output->name); + goto out_error; + } + weston_output_color_outcome_destroy(&output->color_outcome); output->color_outcome = colorout; @@ -6488,6 +6544,11 @@ weston_output_set_color_outcome(struct weston_output *output) weston_color_profile_get_description(output->color_profile)); return true; + +out_error: + weston_output_color_outcome_destroy(&colorout); + + return false; } /** Removes output from compositor's list of enabled outputs @@ -6764,6 +6825,23 @@ weston_output_get_eotf_mode(const struct weston_output *output) return output->eotf_mode; } +/** Get HDR static metadata type 1 + * + * \param output The output to query. + * \return Pointer to the metadata stored in weston_output. + * + * This function is meant to be used by libweston backends. + * + * \ingroup output + * \internal + */ +WL_EXPORT const struct weston_hdr_metadata_type1 * +weston_output_get_hdr_metadata_type1(const struct weston_output *output) +{ + assert(output->color_outcome); + return &output->color_outcome->hdr_meta; +} + /** Set display or monitor basic color characteristics * * \param output The output to modify, must be in disabled state. From e108c1a2fe33d91d70a5e8b00bf3114e2885ab69 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 3 May 2022 16:01:25 +0300 Subject: [PATCH 300/609] color-lcms: color characteristics into HDR metadata This is the beginnings of creating composited content HDR metadata for the ST2084 HDR mode. The immediate goal is to allow essentially setting the HDR metadata from weston.ini, so that it can be experimented with. Setting an output ICC profile will stop weston.ini metadata from taking effect, but using an ICC profile in HDR mode is an open question anyway. maxDML, maxCLL, and minDML are set based on the assumption that we want to make use of the full sink/monitor dynamic range. This also adds several TODOs about how we should handle output profiles, basic output color characteristics, and HDR metadata. Implementing these properly will take more thought and effort. Signed-off-by: Pekka Paalanen --- libweston/color-lcms/color-lcms.c | 112 +++++++++++++++++++++++++++++- 1 file changed, 110 insertions(+), 2 deletions(-) diff --git a/libweston/color-lcms/color-lcms.c b/libweston/color-lcms/color-lcms.c index f8d44530..97e6d6ab 100644 --- a/libweston/color-lcms/color-lcms.c +++ b/libweston/color-lcms/color-lcms.c @@ -27,6 +27,7 @@ #include "config.h" #include +#include #include #include "color.h" @@ -185,6 +186,102 @@ cmlcms_get_sRGB_to_blend_color_transform(struct weston_color_manager_lcms *cm, return true; } +static float +meta_clamp(float value, const char *valname, float min, float max, + struct weston_output *output) +{ + float ret = value; + + /* Paranoia against NaN */ + if (!(ret >= min)) + ret = min; + + if (!(ret <= max)) + ret = max; + + if (ret != value) { + weston_log("output '%s' clamping %s value from %f to %f.\n", + output->name, valname, value, ret); + } + + return ret; +} + +static bool +cmlcms_get_hdr_meta(struct weston_output *output, + struct weston_hdr_metadata_type1 *hdr_meta) +{ + const struct weston_color_characteristics *cc; + + hdr_meta->group_mask = 0; + + /* Only SMPTE ST 2084 mode uses HDR Static Metadata Type 1 */ + if (weston_output_get_eotf_mode(output) != WESTON_EOTF_MODE_ST2084) + return true; + + /* ICC profile overrides color characteristics */ + if (output->color_profile) { + /* + * TODO: extract characteristics from profile? + * Get dynamic range from weston_color_characteristics? + */ + + return true; + } + + cc = weston_output_get_color_characteristics(output); + + /* Target content chromaticity */ + if (cc->group_mask & WESTON_COLOR_CHARACTERISTICS_GROUP_PRIMARIES) { + unsigned i; + + for (i = 0; i < 3; i++) { + hdr_meta->primary[i].x = meta_clamp(cc->primary[i].x, + "primary", 0.0, 1.0, + output); + hdr_meta->primary[i].y = meta_clamp(cc->primary[i].y, + "primary", 0.0, 1.0, + output); + } + hdr_meta->group_mask |= WESTON_HDR_METADATA_TYPE1_GROUP_PRIMARIES; + } + + /* Target content white point */ + if (cc->group_mask & WESTON_COLOR_CHARACTERISTICS_GROUP_WHITE) { + hdr_meta->white.x = meta_clamp(cc->white.x, "white", + 0.0, 1.0, output); + hdr_meta->white.y = meta_clamp(cc->white.y, "white", + 0.0, 1.0, output); + hdr_meta->group_mask |= WESTON_HDR_METADATA_TYPE1_GROUP_WHITE; + } + + /* Target content peak and max mastering luminance */ + if (cc->group_mask & WESTON_COLOR_CHARACTERISTICS_GROUP_MAXL) { + hdr_meta->maxDML = meta_clamp(cc->max_luminance, "maxDML", + 1.0, 65535.0, output); + hdr_meta->maxCLL = meta_clamp(cc->max_luminance, "maxCLL", + 1.0, 65535.0, output); + hdr_meta->group_mask |= WESTON_HDR_METADATA_TYPE1_GROUP_MAXDML; + hdr_meta->group_mask |= WESTON_HDR_METADATA_TYPE1_GROUP_MAXCLL; + } + + /* Target content min mastering luminance */ + if (cc->group_mask & WESTON_COLOR_CHARACTERISTICS_GROUP_MINL) { + hdr_meta->minDML = meta_clamp(cc->min_luminance, "minDML", + 0.0001, 6.5535, output); + hdr_meta->group_mask |= WESTON_HDR_METADATA_TYPE1_GROUP_MINDML; + } + + /* Target content max frame-average luminance */ + if (cc->group_mask & WESTON_COLOR_CHARACTERISTICS_GROUP_MAXFALL) { + hdr_meta->maxFALL = meta_clamp(cc->maxFALL, "maxFALL", + 1.0, 65535.0, output); + hdr_meta->group_mask |= WESTON_HDR_METADATA_TYPE1_GROUP_MAXFALL; + } + + return true; +} + static struct weston_output_color_outcome * cmlcms_create_output_color_outcome(struct weston_color_manager *cm_base, struct weston_output *output) @@ -196,6 +293,19 @@ cmlcms_create_output_color_outcome(struct weston_color_manager *cm_base, if (!co) return NULL; + if (!cmlcms_get_hdr_meta(output, &co->hdr_meta)) + goto out_fail; + + /* + * TODO: if output->color_profile is NULL, maybe manufacture a + * profile from weston_color_characteristics if it has enough + * information? + * Or let the frontend decide to call a "create a profile from + * characteristics" API? + */ + + /* TODO: take container color space into account */ + if (!cmlcms_get_blend_to_output_color_transform(cm, output, &co->from_blend_to_output)) goto out_fail; @@ -208,8 +318,6 @@ cmlcms_create_output_color_outcome(struct weston_color_manager *cm_base, &co->from_sRGB_to_output)) goto out_fail; - co->hdr_meta.group_mask = 0; - return co; out_fail: From e13e64c4e0d72a19c8812c749a704a7b7dabcef2 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Wed, 16 Mar 2022 14:26:17 +0200 Subject: [PATCH 301/609] tests: add color-metadata-parsing Check that weston.ini settings to eotf-mode and basic color characteristics are correctly parsed. Signed-off-by: Pekka Paalanen --- tests/color-metadata-parsing-test.c | 120 ++++++++++++++++++++++++++++ tests/meson.build | 11 ++- 2 files changed, 127 insertions(+), 4 deletions(-) create mode 100644 tests/color-metadata-parsing-test.c diff --git a/tests/color-metadata-parsing-test.c b/tests/color-metadata-parsing-test.c new file mode 100644 index 00000000..cc1325f1 --- /dev/null +++ b/tests/color-metadata-parsing-test.c @@ -0,0 +1,120 @@ +/* + * Copyright 2022 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "config.h" + +#include +#include + +#include "weston-test-client-helper.h" +#include "weston-test-fixture-compositor.h" +#include "backend.h" + +static enum test_result_code +fixture_setup(struct weston_test_harness *harness) +{ + struct compositor_setup setup; + + compositor_setup_defaults(&setup); + setup.renderer = RENDERER_GL; + setup.shell = SHELL_TEST_DESKTOP; + + weston_ini_setup(&setup, + cfgln("[output]"), + cfgln("name=headless"), + cfgln("color_characteristics=my-awesome-color"), + cfgln("eotf-mode=st2084"), + + cfgln("[color_characteristics]"), + cfgln("name=my-awesome-color"), + cfgln("maxFALL=1000"), + cfgln("red_x=0.9999"), + cfgln("red_y=0.3"), + cfgln("blue_x=0.1"), + cfgln("blue_y=0.11"), + cfgln("green_x=0.1771"), + cfgln("green_y=0.80001"), + cfgln("white_x=0.313"), + cfgln("white_y=0.323"), + cfgln("min_L=0.0001"), + cfgln("max_L=65535.0"), + + cfgln("[core]"), + cfgln("color-management=true")); + + return weston_test_harness_execute_as_plugin(harness, &setup); +} +DECLARE_FIXTURE_SETUP(fixture_setup); + +PLUGIN_TEST(color_characteristics_from_weston_ini) +{ + struct weston_output *output = NULL; + struct weston_output *it; + enum weston_eotf_mode mode; + const struct weston_color_characteristics *cc; + const struct weston_hdr_metadata_type1 *hdr_meta; + + wl_list_for_each(it, &compositor->output_list, link) { + if (strcmp(it->name, "headless") == 0) { + output = it; + break; + } + } + + assert(output); + + mode = weston_output_get_eotf_mode(output); + assert(mode == WESTON_EOTF_MODE_ST2084); + + cc = weston_output_get_color_characteristics(output); + assert(cc->group_mask == WESTON_COLOR_CHARACTERISTICS_GROUP_ALL_MASK); + assert(cc->primary[0].x == 0.9999f); + assert(cc->primary[0].y == 0.3f); + assert(cc->primary[1].x == 0.1771f); + assert(cc->primary[1].y == 0.80001f); + assert(cc->primary[2].x == 0.1f); + assert(cc->primary[2].y == 0.11f); + assert(cc->white.x == 0.313f); + assert(cc->white.y == 0.323f); + assert(cc->min_luminance == 0.0001f); + assert(cc->max_luminance == 65535.0f); + assert(cc->maxFALL == 1000.0f); + + /* The below is color manager policy. */ + hdr_meta = weston_output_get_hdr_metadata_type1(output); + assert(hdr_meta->group_mask == WESTON_HDR_METADATA_TYPE1_GROUP_ALL_MASK); + assert(hdr_meta->primary[0].x == 0.9999f); + assert(hdr_meta->primary[0].y == 0.3f); + assert(hdr_meta->primary[1].x == 0.1771f); + assert(hdr_meta->primary[1].y == 0.80001f); + assert(hdr_meta->primary[2].x == 0.1f); + assert(hdr_meta->primary[2].y == 0.11f); + assert(hdr_meta->white.x == 0.313f); + assert(hdr_meta->white.y == 0.323f); + assert(hdr_meta->minDML == 0.0001f); + assert(hdr_meta->maxDML == 65535.0f); + assert(hdr_meta->maxCLL == 65535.0f); + assert(hdr_meta->maxFALL == 1000.0f); +} diff --git a/tests/meson.build b/tests/meson.build index 889b55f4..83523920 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -235,10 +235,13 @@ if get_option('color-management-lcms') if not dep_lcms2.found() error('color-management-lcms tests require lcms2 which was not found. Or, you can use \'-Dcolor-management-lcms=false\'.') endif - tests += { - 'name': 'color-shaper-matrix', - 'dep_objs': [ dep_libm, dep_lcms2 ] - } + tests += [ + { + 'name': 'color-shaper-matrix', + 'dep_objs': [ dep_libm, dep_lcms2 ] + }, + { 'name': 'color-metadata-parsing' }, + ] endif From ccb4c383d73c5d951e9652b6da6c0a9295b86539 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 17 Mar 2022 13:13:14 +0200 Subject: [PATCH 302/609] tests: add color-metadata-errors test 'color_characteristics_config_error' test ensures that all code paths in parse_color_characteristics() and wet_output_set_color_characteristics() get exercised. The return value and logged error messages are checked. Other cases test the weston_hdr_metadata_type1 validation. These are for the sake of test coverage, but also an example of how to test a function from main.c, and how to capture messages from weston_log(). Signed-off-by: Pekka Paalanen --- libweston/compositor.c | 2 +- libweston/libweston-internal.h | 5 + tests/color-metadata-errors-test.c | 338 +++++++++++++++++++++++++++++ tests/meson.build | 4 + 4 files changed, 348 insertions(+), 1 deletion(-) create mode 100644 tests/color-metadata-errors-test.c diff --git a/libweston/compositor.c b/libweston/compositor.c index 47cd4ff9..c532414e 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -6516,7 +6516,7 @@ weston_output_color_outcome_destroy(struct weston_output_color_outcome **pco) *pco = NULL; } -static bool +WESTON_EXPORT_FOR_TESTS bool weston_output_set_color_outcome(struct weston_output *output) { struct weston_color_manager *cm = output->compositor->color_manager; diff --git a/libweston/libweston-internal.h b/libweston/libweston-internal.h index 6a189275..7178ca70 100644 --- a/libweston/libweston-internal.h +++ b/libweston/libweston-internal.h @@ -462,4 +462,9 @@ weston_view_find_paint_node(struct weston_view *view, int wl_data_device_manager_init(struct wl_display *display); +/* Exclusively for unit tests */ + +bool +weston_output_set_color_outcome(struct weston_output *output); + #endif diff --git a/tests/color-metadata-errors-test.c b/tests/color-metadata-errors-test.c new file mode 100644 index 00000000..61ce33de --- /dev/null +++ b/tests/color-metadata-errors-test.c @@ -0,0 +1,338 @@ +/* + * Copyright 2022 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include + +#include "weston-test-client-helper.h" +#include "weston-test-fixture-compositor.h" + +#include "weston-private.h" +#include "libweston-internal.h" +#include "backend.h" +#include "color.h" + +struct config_testcase { + bool has_characteristics_key; + const char *output_characteristics_name; + const char *characteristics_name; + const char *red_x; + const char *green_y; + const char *white_y; + const char *min_L; + int expected_retval; + const char *expected_error; +}; + +static const struct config_testcase config_cases[] = { + { + false, "fred", "fred", "red_x=0.9", "green_y=0.8", "white_y=0.323", "min_L=1e-4", 0, + "" + }, + { + true, "fred", "fred", "red_x=0.9", "green_y= 0.8 ", "white_y=0.323", "min_L=1e-4", 0, + "" + }, + { + true, "fred", "fred", "red_x=0.9", "green_y= 0.8 ", "white_y=0.323", "", 0, + "" + }, + { + true, "notexisting", "fred", "red_x=0.9", "green_y=0.8", "white_y=0.323", "min_L=1e-4", -1, + "Config error in weston.ini, output mockoutput: no [color_characteristics] section with 'name=notexisting' found.\n" + }, + { + true, "fr:ed", "fr:ed", "red_x=0.9", "green_y=0.8", "white_y=0.323", "min_L=1e-4", -1, + "Config error in weston.ini [color_characteristics] name=fr:ed: reserved name. Do not use ':' character in the name.\n" + }, + { + true, "fred", "fred", "red_x=-5", "green_y=1.01", "white_y=0.323", "min_L=1e-4", -1, + "Config error in weston.ini [color_characteristics] name=fred: red_x value -5.000000 is outside of the range 0.000000 - 1.000000.\n" + "Config error in weston.ini [color_characteristics] name=fred: green_y value 1.010000 is outside of the range 0.000000 - 1.000000.\n" + }, + { + true, "fred", "fred", "red_x=haahaa", "green_y=-", "white_y=0.323", "min_L=1e-4", -1, + "Config error in weston.ini [color_characteristics] name=fred: failed to parse the value of key red_x.\n" + "Config error in weston.ini [color_characteristics] name=fred: failed to parse the value of key green_y.\n" + }, + { + true, "fred", "fred", "", "", "white_y=0.323", "min_L=1e-4", -1, + "Config error in weston.ini [color_characteristics] name=fred: group 1 key red_x is missing. You must set either none or all keys of a group.\n" + "Config error in weston.ini [color_characteristics] name=fred: group 1 key red_y is set. You must set either none or all keys of a group.\n" + "Config error in weston.ini [color_characteristics] name=fred: group 1 key green_x is set. You must set either none or all keys of a group.\n" + "Config error in weston.ini [color_characteristics] name=fred: group 1 key green_y is missing. You must set either none or all keys of a group.\n" + "Config error in weston.ini [color_characteristics] name=fred: group 1 key blue_x is set. You must set either none or all keys of a group.\n" + "Config error in weston.ini [color_characteristics] name=fred: group 1 key blue_y is set. You must set either none or all keys of a group.\n" + }, + { + true, "fred", "fred", "red_x=0.9", "green_y=0.8", "", "min_L=1e-4", -1, + "Config error in weston.ini [color_characteristics] name=fred: group 2 key white_x is set. You must set either none or all keys of a group.\n" + "Config error in weston.ini [color_characteristics] name=fred: group 2 key white_y is missing. You must set either none or all keys of a group.\n" + }, +}; + +static FILE *logfile; + +static int +logger(const char *fmt, va_list arg) +{ + return vfprintf(logfile, fmt, arg); +} + +static int +no_logger(const char *fmt, va_list arg) +{ + return 0; +} + +static struct weston_config * +create_config(const struct config_testcase *t) +{ + struct compositor_setup setup; + struct weston_config *wc; + + compositor_setup_defaults(&setup); + weston_ini_setup(&setup, + cfgln("[output]"), + cfgln("name=mockoutput"), + t->has_characteristics_key ? + cfgln("color_characteristics=%s", t->output_characteristics_name) : + cfgln(""), + cfgln("eotf-mode=st2084"), + + cfgln("[color_characteristics]"), + cfgln("name=%s", t->characteristics_name), + cfgln("maxFALL=1000"), + cfgln("%s", t->red_x), + cfgln("red_y=0.3"), + cfgln("blue_x=0.1"), + cfgln("blue_y=0.11"), + cfgln("green_x=0.1771"), + cfgln("%s", t->green_y), + cfgln("white_x=0.313"), + cfgln("%s", t->white_y), + cfgln("%s", t->min_L), + cfgln("max_L=65535.0"), + + cfgln("[core]"), + cfgln("color-management=true")); + + wc = weston_config_parse(setup.config_file); + free(setup.config_file); + + return wc; +} + +/* + * Manufacture various weston.ini and check what + * wet_output_set_color_characteristics() says. Tests for the return value and + * the error messages logged. + */ +TEST_P(color_characteristics_config_error, config_cases) +{ + const struct config_testcase *t = data; + struct weston_config *wc; + struct weston_config_section *section; + int retval; + char *logbuf; + size_t logsize; + struct weston_output mock_output = {}; + + weston_output_init(&mock_output, NULL, "mockoutput"); + + logfile = open_memstream(&logbuf, &logsize); + weston_log_set_handler(logger, logger); + + wc = create_config(t); + section = weston_config_get_section(wc, "output", "name", "mockoutput"); + assert(section); + + retval = wet_output_set_color_characteristics(&mock_output, wc, section); + + assert(fclose(logfile) == 0); + logfile = NULL; + + testlog("retval %d, logs:\n%s\n", retval, logbuf); + + assert(retval == t->expected_retval); + assert(strcmp(logbuf, t->expected_error) == 0); + + weston_config_destroy(wc); + free(logbuf); + weston_output_release(&mock_output); +} + +/* Setting NULL resets group_mask */ +TEST(weston_output_set_color_characteristics_null) +{ + struct weston_output mock_output = {}; + + weston_output_init(&mock_output, NULL, "mockoutput"); + + mock_output.color_characteristics.group_mask = 1; + weston_output_set_color_characteristics(&mock_output, NULL); + assert(mock_output.color_characteristics.group_mask == 0); + + weston_output_release(&mock_output); +} + +struct value_testcase { + unsigned field_index; + float value; + bool retval; +}; + +static const struct value_testcase value_cases[] = { + { 0, 0.0, true }, + { 0, 1.0, true }, + { 0, -0.001, false }, + { 0, 1.01, false }, + { 0, NAN, false }, + { 0, HUGE_VALF, false }, + { 0, -HUGE_VALF, false }, + { 1, -1.0, false }, + { 2, 2.0, false }, + { 3, 2.0, false }, + { 4, 2.0, false }, + { 5, 2.0, false }, + { 6, 2.0, false }, + { 7, 2.0, false }, + { 8, 0.99, false }, + { 8, 65535.1, false }, + { 9, 0.000099, false }, + { 9, 6.55351, false }, + { 10, 0.99, false }, + { 10, 65535.1, false }, + { 11, 0.99, false }, + { 11, 65535.1, false }, +}; + +struct mock_color_manager { + struct weston_color_manager base; + struct weston_hdr_metadata_type1 *test_hdr_meta; +}; + +static struct weston_output_color_outcome * +mock_create_output_color_outcome(struct weston_color_manager *cm_base, + struct weston_output *output) +{ + struct mock_color_manager *cm = container_of(cm_base, typeof(*cm), base); + struct weston_output_color_outcome *co; + + co = zalloc(sizeof *co); + assert(co); + + co->hdr_meta = *cm->test_hdr_meta; + + return co; +} + +/* + * Modify one value in a known good metadata structure, and see how + * validation reacts to it. + */ +TEST_P(hdr_metadata_type1_errors, value_cases) +{ + struct value_testcase *t = data; + struct weston_hdr_metadata_type1 meta = { + .group_mask = WESTON_HDR_METADATA_TYPE1_GROUP_ALL_MASK, + .primary[0] = { 0.6650, 0.3261 }, + .primary[1] = { 0.2890, 0.6435 }, + .primary[2] = { 0.1491, 0.0507 }, + .white = { 0.3134, 0.3291 }, + .maxDML = 600.0, + .minDML = 0.0001, + .maxCLL = 600.0, + .maxFALL = 400.0, + }; + float *fields[] = { + &meta.primary[0].x, &meta.primary[0].y, + &meta.primary[1].x, &meta.primary[1].y, + &meta.primary[2].x, &meta.primary[2].y, + &meta.white.x, &meta.white.y, + &meta.maxDML, &meta.minDML, + &meta.maxCLL, &meta.maxFALL, + }; + struct mock_color_manager mock_cm = { + .base.create_output_color_outcome = mock_create_output_color_outcome, + .test_hdr_meta = &meta, + }; + struct weston_compositor mock_compositor = { + .color_manager = &mock_cm.base, + }; + struct weston_output mock_output = {}; + bool ret; + + weston_log_set_handler(no_logger, no_logger); + + weston_output_init(&mock_output, &mock_compositor, "mockoutput"); + + assert(t->field_index < ARRAY_LENGTH(fields)); + *fields[t->field_index] = t->value; + ret = weston_output_set_color_outcome(&mock_output); + assert(ret == t->retval); + + weston_output_color_outcome_destroy(&mock_output.color_outcome); + weston_output_release(&mock_output); +} + +/* Unflagged members are ignored in validity check */ +TEST(hdr_metadata_type1_ignore_unflagged) +{ + /* All values invalid, but also empty mask so none actually used. */ + struct weston_hdr_metadata_type1 meta = { + .group_mask = 0, + .primary[0] = { -1.0, -1.0 }, + .primary[1] = { -1.0, -1.0 }, + .primary[2] = { -1.0, -1.0 }, + .white = { -1.0, -1.0 }, + .maxDML = -1.0, + .minDML = -1.0, + .maxCLL = -1.0, + .maxFALL = -1.0, + }; + struct mock_color_manager mock_cm = { + .base.create_output_color_outcome = mock_create_output_color_outcome, + .test_hdr_meta = &meta, + }; + struct weston_compositor mock_compositor = { + .color_manager = &mock_cm.base, + }; + struct weston_output mock_output = {}; + bool ret; + + weston_log_set_handler(no_logger, no_logger); + + weston_output_init(&mock_output, &mock_compositor, "mockoutput"); + + ret = weston_output_set_color_outcome(&mock_output); + assert(ret); + + weston_output_color_outcome_destroy(&mock_output.color_outcome); + weston_output_release(&mock_output); +} diff --git a/tests/meson.build b/tests/meson.build index 83523920..1ebd39c7 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -127,6 +127,10 @@ tests = [ }, { 'name': 'bad-buffer', }, { 'name': 'buffer-transforms', }, + { + 'name': 'color-metadata-errors', + 'dep_objs': dep_libexec_weston, + }, { 'name': 'color-manager', }, { 'name': 'devices', }, { From c4fedd503fe6b43dd373affcf55127194978ac79 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 9 May 2022 13:59:16 +0300 Subject: [PATCH 303/609] backend-drm: move code to kms-color.c This creates a new file for KMS related color code, to avoid making drm.c even longer. The moved code was just added in 5151f9fe9e9bacc6860931af60adba28cfd431a7 so the new file copyrights are written based on that. Signed-off-by: Pekka Paalanen --- libweston/backend-drm/drm-internal.h | 3 + libweston/backend-drm/drm.c | 66 ------------------- libweston/backend-drm/kms-color.c | 99 ++++++++++++++++++++++++++++ libweston/backend-drm/meson.build | 1 + 4 files changed, 103 insertions(+), 66 deletions(-) create mode 100644 libweston/backend-drm/kms-color.c diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index 23da81c0..87049de5 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -726,6 +726,9 @@ drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend, void drm_output_set_cursor_view(struct drm_output *output, struct weston_view *ev); +int +drm_output_ensure_hdr_output_metadata_blob(struct drm_output *output); + #ifdef BUILD_DRM_GBM extern struct drm_fb * drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev, diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index b55696c9..5625d7cf 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -51,7 +51,6 @@ #include #include #include "drm-internal.h" -#include "libdrm-updates.h" #include "shared/helpers.h" #include "shared/timespec-util.h" #include "shared/string-helpers.h" @@ -441,71 +440,6 @@ drm_output_render(struct drm_output_state *state, pixman_region32_t *damage) pixman_region32_fini(&scanout_damage); } -static int -drm_output_ensure_hdr_output_metadata_blob(struct drm_output *output) -{ - struct hdr_output_metadata meta; - uint32_t blob_id = 0; - int ret; - - if (output->hdr_output_metadata_blob_id) - return 0; - - /* - * Set up the data for Dynamic Range and Mastering InfoFrame, - * CTA-861-G, a.k.a the static HDR metadata. - */ - - memset(&meta, 0, sizeof meta); - - meta.metadata_type = 0; /* Static Metadata Type 1 */ - - /* Duplicated field in UABI struct */ - meta.hdmi_metadata_type1.metadata_type = meta.metadata_type; - - switch (output->base.eotf_mode) { - case WESTON_EOTF_MODE_NONE: - assert(0 && "bad eotf_mode: none"); - return -1; - case WESTON_EOTF_MODE_SDR: - /* - * Do not send any static HDR metadata. Video sinks should - * respond by switching to traditional SDR mode. If they - * do not, the kernel should fix that up. - */ - assert(output->hdr_output_metadata_blob_id == 0); - return 0; - case WESTON_EOTF_MODE_TRADITIONAL_HDR: - meta.hdmi_metadata_type1.eotf = 1; /* from CTA-861-G */ - break; - case WESTON_EOTF_MODE_ST2084: - meta.hdmi_metadata_type1.eotf = 2; /* from CTA-861-G */ - break; - case WESTON_EOTF_MODE_HLG: - meta.hdmi_metadata_type1.eotf = 3; /* from CTA-861-G */ - break; - } - - if (meta.hdmi_metadata_type1.eotf == 0) { - assert(0 && "bad eotf_mode"); - return -1; - } - - /* The other fields are intentionally left as zeroes. */ - - ret = drmModeCreatePropertyBlob(output->backend->drm.fd, - &meta, sizeof meta, &blob_id); - if (ret != 0) { - weston_log("Error: failed to create KMS blob for HDR metadata on output '%s': %s\n", - output->base.name, strerror(-ret)); - return -1; - } - - output->hdr_output_metadata_blob_id = blob_id; - - return 0; -} - static int drm_output_repaint(struct weston_output *output_base, pixman_region32_t *damage) { diff --git a/libweston/backend-drm/kms-color.c b/libweston/backend-drm/kms-color.c new file mode 100644 index 00000000..ae479754 --- /dev/null +++ b/libweston/backend-drm/kms-color.c @@ -0,0 +1,99 @@ +/* + * Copyright 2021-2022 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include +#include + +#include "drm-internal.h" +#include "libdrm-updates.h" + +int +drm_output_ensure_hdr_output_metadata_blob(struct drm_output *output) +{ + struct hdr_output_metadata meta; + uint32_t blob_id = 0; + int ret; + + if (output->hdr_output_metadata_blob_id) + return 0; + + /* + * Set up the data for Dynamic Range and Mastering InfoFrame, + * CTA-861-G, a.k.a the static HDR metadata. + */ + + memset(&meta, 0, sizeof meta); + + meta.metadata_type = 0; /* Static Metadata Type 1 */ + + /* Duplicated field in UABI struct */ + meta.hdmi_metadata_type1.metadata_type = meta.metadata_type; + + switch (output->base.eotf_mode) { + case WESTON_EOTF_MODE_NONE: + assert(0 && "bad eotf_mode: none"); + return -1; + case WESTON_EOTF_MODE_SDR: + /* + * Do not send any static HDR metadata. Video sinks should + * respond by switching to traditional SDR mode. If they + * do not, the kernel should fix that up. + */ + assert(output->hdr_output_metadata_blob_id == 0); + return 0; + case WESTON_EOTF_MODE_TRADITIONAL_HDR: + meta.hdmi_metadata_type1.eotf = 1; /* from CTA-861-G */ + break; + case WESTON_EOTF_MODE_ST2084: + meta.hdmi_metadata_type1.eotf = 2; /* from CTA-861-G */ + break; + case WESTON_EOTF_MODE_HLG: + meta.hdmi_metadata_type1.eotf = 3; /* from CTA-861-G */ + break; + } + + if (meta.hdmi_metadata_type1.eotf == 0) { + assert(0 && "bad eotf_mode"); + return -1; + } + + /* The other fields are intentionally left as zeroes. */ + + ret = drmModeCreatePropertyBlob(output->backend->drm.fd, + &meta, sizeof meta, &blob_id); + if (ret != 0) { + weston_log("Error: failed to create KMS blob for HDR metadata on output '%s': %s\n", + output->base.name, strerror(-ret)); + return -1; + } + + output->hdr_output_metadata_blob_id = blob_id; + + return 0; +} diff --git a/libweston/backend-drm/meson.build b/libweston/backend-drm/meson.build index 9e7c0cff..e913f4b9 100644 --- a/libweston/backend-drm/meson.build +++ b/libweston/backend-drm/meson.build @@ -29,6 +29,7 @@ srcs_drm = [ 'fb.c', 'modes.c', 'kms.c', + 'kms-color.c', 'state-helpers.c', 'state-propose.c', linux_dmabuf_unstable_v1_protocol_c, From c217453c85aaba7a8cde7cb3ddbd8eba59d1fba9 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 22 Jul 2021 16:33:51 +0300 Subject: [PATCH 304/609] backend-drm: forward HDR metadata Forward the HDR Static Metadata Type 1 to the video sink. This makes the sink aware of our video content parameters and may be able to produce a better picture. This type of metadata is used only with the ST 2084 HDR mode a.k.a PQ. Signed-off-by: Pekka Paalanen --- libweston/backend-drm/kms-color.c | 78 ++++++++++++++++++++++++++++++- libweston/backend-drm/meson.build | 1 + 2 files changed, 77 insertions(+), 2 deletions(-) diff --git a/libweston/backend-drm/kms-color.c b/libweston/backend-drm/kms-color.c index ae479754..8219c94c 100644 --- a/libweston/backend-drm/kms-color.c +++ b/libweston/backend-drm/kms-color.c @@ -29,13 +29,86 @@ #include #include #include +#include #include "drm-internal.h" #include "libdrm-updates.h" +static inline uint16_t +color_xy_to_u16(float v) +{ + assert(v >= 0.0f); + assert(v <= 1.0f); + /* + * CTA-861-G + * 6.9.1 Static Metadata Type 1 + * chromaticity coordinate encoding + */ + return (uint16_t)round(v * 50000.0); +} + +static inline uint16_t +nits_to_u16(float nits) +{ + assert(nits >= 1.0f); + assert(nits <= 65535.0f); + /* + * CTA-861-G + * 6.9.1 Static Metadata Type 1 + * max display mastering luminance, max content light level, + * max frame-average light level + */ + return (uint16_t)round(nits); +} + +static inline uint16_t +nits_to_u16_dark(float nits) +{ + assert(nits >= 0.0001f); + assert(nits <= 6.5535f); + /* + * CTA-861-G + * 6.9.1 Static Metadata Type 1 + * min display mastering luminance + */ + return (uint16_t)round(nits * 10000.0); +} + +static void +weston_hdr_metadata_type1_to_kms(struct hdr_metadata_infoframe *dst, + const struct weston_hdr_metadata_type1 *src) +{ + if (src->group_mask & WESTON_HDR_METADATA_TYPE1_GROUP_PRIMARIES) { + unsigned i; + + for (i = 0; i < 3; i++) { + dst->display_primaries[i].x = color_xy_to_u16(src->primary[i].x); + dst->display_primaries[i].y = color_xy_to_u16(src->primary[i].y); + } + } + + if (src->group_mask & WESTON_HDR_METADATA_TYPE1_GROUP_WHITE) { + dst->white_point.x = color_xy_to_u16(src->white.x); + dst->white_point.y = color_xy_to_u16(src->white.y); + } + + if (src->group_mask & WESTON_HDR_METADATA_TYPE1_GROUP_MAXDML) + dst->max_display_mastering_luminance = nits_to_u16(src->maxDML); + + if (src->group_mask & WESTON_HDR_METADATA_TYPE1_GROUP_MINDML) + dst->min_display_mastering_luminance = nits_to_u16_dark(src->minDML); + + if (src->group_mask & WESTON_HDR_METADATA_TYPE1_GROUP_MAXCLL) + dst->max_cll = nits_to_u16(src->maxCLL); + + if (src->group_mask & WESTON_HDR_METADATA_TYPE1_GROUP_MAXFALL) + dst->max_fall = nits_to_u16(src->maxFALL); +} + int drm_output_ensure_hdr_output_metadata_blob(struct drm_output *output) { + const struct weston_hdr_metadata_type1 *src; struct hdr_output_metadata meta; uint32_t blob_id = 0; int ret; @@ -43,6 +116,8 @@ drm_output_ensure_hdr_output_metadata_blob(struct drm_output *output) if (output->hdr_output_metadata_blob_id) return 0; + src = weston_output_get_hdr_metadata_type1(&output->base); + /* * Set up the data for Dynamic Range and Mastering InfoFrame, * CTA-861-G, a.k.a the static HDR metadata. @@ -72,6 +147,7 @@ drm_output_ensure_hdr_output_metadata_blob(struct drm_output *output) break; case WESTON_EOTF_MODE_ST2084: meta.hdmi_metadata_type1.eotf = 2; /* from CTA-861-G */ + weston_hdr_metadata_type1_to_kms(&meta.hdmi_metadata_type1, src); break; case WESTON_EOTF_MODE_HLG: meta.hdmi_metadata_type1.eotf = 3; /* from CTA-861-G */ @@ -83,8 +159,6 @@ drm_output_ensure_hdr_output_metadata_blob(struct drm_output *output) return -1; } - /* The other fields are intentionally left as zeroes. */ - ret = drmModeCreatePropertyBlob(output->backend->drm.fd, &meta, sizeof meta, &blob_id); if (ret != 0) { diff --git a/libweston/backend-drm/meson.build b/libweston/backend-drm/meson.build index e913f4b9..cb2ebda0 100644 --- a/libweston/backend-drm/meson.build +++ b/libweston/backend-drm/meson.build @@ -38,6 +38,7 @@ srcs_drm = [ ] deps_drm = [ + dep_libm, dep_libdl, dep_libweston_private, dep_session_helper, From 8ebebb20ef5825a4183c09e65cb90d6f2d5db0a6 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 6 May 2022 13:57:12 +0300 Subject: [PATCH 305/609] drm-backend: add color_outcome / HDR metadata serial Output color profile may be changed in flight. Output basic color characteristics and EOTF mode cannot yet be changed in flight, but it is reasonable to assume they could in the future. Therefore the color outcome data may change in flight as well, which is the basis for HDR metadata, which needs to be updated as well. Track the changes to color outcome data with a serial number. DRM-backend checks the serial number to see if it needs to re-create the HDR metadata blob. This allows the changes to propagate all the way to KMS. The code added here is more of a reminder of what should happen than a tested path. Signed-off-by: Pekka Paalanen --- include/libweston/libweston.h | 1 + libweston/backend-drm/drm-internal.h | 1 + libweston/backend-drm/kms-color.c | 7 ++++++- libweston/compositor.c | 1 + 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index ceaca6a9..c361d76a 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -534,6 +534,7 @@ struct weston_output { struct weston_color_characteristics color_characteristics; struct weston_output_color_outcome *color_outcome; + uint64_t color_outcome_serial; int (*enable)(struct weston_output *output); int (*disable)(struct weston_output *output); diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index 87049de5..1afbaf52 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -551,6 +551,7 @@ struct drm_output { uint32_t gbm_bo_flags; uint32_t hdr_output_metadata_blob_id; + uint64_t ackd_color_outcome_serial; /* Plane being displayed directly on the CRTC */ struct drm_plane *scanout_plane; diff --git a/libweston/backend-drm/kms-color.c b/libweston/backend-drm/kms-color.c index 8219c94c..476480d5 100644 --- a/libweston/backend-drm/kms-color.c +++ b/libweston/backend-drm/kms-color.c @@ -113,7 +113,8 @@ drm_output_ensure_hdr_output_metadata_blob(struct drm_output *output) uint32_t blob_id = 0; int ret; - if (output->hdr_output_metadata_blob_id) + if (output->hdr_output_metadata_blob_id && + output->ackd_color_outcome_serial == output->base.color_outcome_serial) return 0; src = weston_output_get_hdr_metadata_type1(&output->base); @@ -167,7 +168,11 @@ drm_output_ensure_hdr_output_metadata_blob(struct drm_output *output) return -1; } + drmModeDestroyPropertyBlob(output->backend->drm.fd, + output->hdr_output_metadata_blob_id); + output->hdr_output_metadata_blob_id = blob_id; + output->ackd_color_outcome_serial = output->base.color_outcome_serial; return 0; } diff --git a/libweston/compositor.c b/libweston/compositor.c index c532414e..45502e18 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -6537,6 +6537,7 @@ weston_output_set_color_outcome(struct weston_output *output) weston_output_color_outcome_destroy(&output->color_outcome); output->color_outcome = colorout; + output->color_outcome_serial++; output->from_blend_to_output_by_backend = false; From 7412a01437e7a739a989e5125e107b8bc6c1edf6 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Mon, 30 May 2022 00:14:13 +0300 Subject: [PATCH 306/609] backend-drm: Retrieve reason if dmabuf import failed As we could have situations where dmabuf import failed when attempting to figure it the framebuffer is scanout-capable, make sure we also have a way to store that information. Otherwise, we could end up NULL-dereferencing, as we don't provide a valid storage for it. Further more, with this, we also print out the reason why it failed, to aid in further debugging. Observed on platforms where GBM_BO_HANDLE failed + in combination w/ direct-display proto extension. Signed-off-by: Marius Vlad --- libweston/backend-drm/fb.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libweston/backend-drm/fb.c b/libweston/backend-drm/fb.c index 202c4818..71dc1914 100644 --- a/libweston/backend-drm/fb.c +++ b/libweston/backend-drm/fb.c @@ -454,14 +454,15 @@ drm_can_scanout_dmabuf(struct weston_compositor *ec, struct drm_fb *fb; struct drm_backend *b = to_drm_backend(ec); bool ret = false; + uint32_t try_reason = 0x0; - fb = drm_fb_get_from_dmabuf(dmabuf, b, true, NULL); + fb = drm_fb_get_from_dmabuf(dmabuf, b, true, &try_reason); if (fb) ret = true; drm_fb_unref(fb); - drm_debug(b, "[dmabuf] dmabuf %p, import test %s\n", dmabuf, - ret ? "succeeded" : "failed"); + drm_debug(b, "[dmabuf] dmabuf %p, import test %s, with reason 0x%x\n", dmabuf, + ret ? "succeeded" : "failed", try_reason); return ret; } From e67a0cb57c8e6a8322465bb997afaabba74375c7 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 31 May 2022 13:08:01 +0300 Subject: [PATCH 307/609] gl-renderer: fix double-alloc of gl_buffer_state Obviously the first allocation is always leaked, there is a second zalloc() right below. Fix the leak. Found by code inspection. Signed-off-by: Pekka Paalanen --- libweston/renderer-gl/gl-renderer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index b0a49563..58f076d2 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -2548,7 +2548,7 @@ import_dmabuf(struct gl_renderer *gr, struct linux_dmabuf_buffer *dmabuf) { EGLImageKHR egl_image; - struct gl_buffer_state *gb = zalloc(sizeof(*gb)); + struct gl_buffer_state *gb; if (!pixel_format_get_info(dmabuf->attributes.format)) return NULL; From 4090f0eb6ffe850e70196ca0f2388911ccc6994f Mon Sep 17 00:00:00 2001 From: Robert Mader Date: Wed, 25 May 2022 15:25:52 +0200 Subject: [PATCH 308/609] clients/simple-egl: Use INT32_MAX for opaque region Setting the opaque region correctly is common source of error for clients that simply want to express that a whole surface is opaque. This is especially true once buffer_scale and buffer_transform come into play, as unlike for damage, where buffer_damage is the encouraged and user friendly way today, opaque regions are always in logical coordinates. As faulty opaque regions don't have a visual impact in these cases but only increase resource consumption, these errors often remain for long times. See https://gitlab.freedesktop.org/gstreamer/gstreamer/-/commit/1e2bc681712d62081f49e8e74723a596d1578a34 for one of many examples. Give an easy example how to set the opaque region in a conformant and reliable way. Signed-off-by: Robert Mader --- clients/simple-egl.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/clients/simple-egl.c b/clients/simple-egl.c index 3d5e9226..53055cc0 100644 --- a/clients/simple-egl.c +++ b/clients/simple-egl.c @@ -497,9 +497,7 @@ redraw(struct window *window) if (window->opaque || window->fullscreen) { region = wl_compositor_create_region(window->display->compositor); - wl_region_add(region, 0, 0, - window->geometry.width, - window->geometry.height); + wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX); wl_surface_set_opaque_region(window->surface, region); wl_region_destroy(region); } else { From 009625c29712255e7f353e0dcf7ad258ab942323 Mon Sep 17 00:00:00 2001 From: Robert Mader Date: Tue, 24 May 2022 11:13:05 +0200 Subject: [PATCH 309/609] clients/simple-egl: Rename buffer_size to buffer_bpp `buffer_size` usually refers to `wl_buffer` size in the Wayland world. Signed-off-by: Robert Mader --- clients/simple-egl.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/clients/simple-egl.c b/clients/simple-egl.c index 53055cc0..88892614 100644 --- a/clients/simple-egl.c +++ b/clients/simple-egl.c @@ -95,7 +95,7 @@ struct window { struct xdg_surface *xdg_surface; struct xdg_toplevel *xdg_toplevel; EGLSurface egl_surface; - int fullscreen, maximized, opaque, buffer_size, frame_sync, delay; + int fullscreen, maximized, opaque, buffer_bpp, frame_sync, delay; bool wait_for_configure; }; @@ -154,7 +154,7 @@ init_egl(struct display *display, struct window *window) EGLConfig *configs; EGLBoolean ret; - if (window->opaque || window->buffer_size == 16) + if (window->opaque || window->buffer_bpp == 16) config_attribs[9] = 0; display->egl.dpy = @@ -178,13 +178,13 @@ init_egl(struct display *display, struct window *window) assert(ret && n >= 1); for (i = 0; i < n; i++) { - EGLint buffer_size, red_size; + EGLint buffer_bpp, red_size; eglGetConfigAttrib(display->egl.dpy, - configs[i], EGL_BUFFER_SIZE, &buffer_size); + configs[i], EGL_BUFFER_SIZE, &buffer_bpp); eglGetConfigAttrib(display->egl.dpy, configs[i], EGL_RED_SIZE, &red_size); - if ((window->buffer_size == 0 || - window->buffer_size == buffer_size) && red_size < 10) { + if ((window->buffer_bpp == 0 || + window->buffer_bpp == buffer_bpp) && red_size < 10) { display->egl.conf = configs[i]; break; } @@ -192,7 +192,7 @@ init_egl(struct display *display, struct window *window) free(configs); if (display->egl.conf == NULL) { fprintf(stderr, "did not find config with buffer size %d\n", - window->buffer_size); + window->buffer_bpp); exit(EXIT_FAILURE); } @@ -813,7 +813,7 @@ main(int argc, char **argv) window.geometry.width = 250; window.geometry.height = 250; window.window_size = window.geometry; - window.buffer_size = 0; + window.buffer_bpp = 0; window.frame_sync = 1; window.delay = 0; @@ -827,7 +827,7 @@ main(int argc, char **argv) else if (strcmp("-o", argv[i]) == 0) window.opaque = 1; else if (strcmp("-s", argv[i]) == 0) - window.buffer_size = 16; + window.buffer_bpp = 16; else if (strcmp("-b", argv[i]) == 0) window.frame_sync = 0; else if (strcmp("-h", argv[i]) == 0) From 0b2369bb4abae0c228e1ddd1fef7f258aa471aae Mon Sep 17 00:00:00 2001 From: Robert Mader Date: Tue, 24 May 2022 11:18:47 +0200 Subject: [PATCH 310/609] clients/simple-egl: Rename geometry to buffer_size To reflect more clearly that we use it for `wl_buffer` coordinates. Signed-off-by: Robert Mader --- clients/simple-egl.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/clients/simple-egl.c b/clients/simple-egl.c index 88892614..c2b8b7c8 100644 --- a/clients/simple-egl.c +++ b/clients/simple-egl.c @@ -82,7 +82,7 @@ struct geometry { struct window { struct display *display; - struct geometry geometry, window_size; + struct geometry buffer_size, window_size; struct { GLuint rotation_uniform; GLuint pos; @@ -264,8 +264,8 @@ init_gl(struct window *window) EGLBoolean ret; window->native = wl_egl_window_create(window->surface, - window->geometry.width, - window->geometry.height); + window->buffer_size.width, + window->buffer_size.height); window->egl_surface = weston_platform_create_egl_surface(window->display->egl.dpy, window->display->egl.conf, @@ -347,16 +347,16 @@ handle_toplevel_configure(void *data, struct xdg_toplevel *toplevel, window->window_size.width = width; window->window_size.height = height; } - window->geometry.width = width; - window->geometry.height = height; + window->buffer_size.width = width; + window->buffer_size.height = height; } else if (!window->fullscreen && !window->maximized) { - window->geometry = window->window_size; + window->buffer_size = window->window_size; } if (window->native) wl_egl_window_resize(window->native, - window->geometry.width, - window->geometry.height, 0, 0); + window->buffer_size.width, + window->buffer_size.height, 0, 0); } static void @@ -472,7 +472,7 @@ redraw(struct window *window) eglQuerySurface(display->egl.dpy, window->egl_surface, EGL_BUFFER_AGE_EXT, &buffer_age); - glViewport(0, 0, window->geometry.width, window->geometry.height); + glViewport(0, 0, window->buffer_size.width, window->buffer_size.height); glUniformMatrix4fv(window->gl.rotation_uniform, 1, GL_FALSE, (GLfloat *) rotation); @@ -505,10 +505,10 @@ redraw(struct window *window) } if (display->swap_buffers_with_damage && buffer_age > 0) { - rect[0] = window->geometry.width / 4 - 1; - rect[1] = window->geometry.height / 4 - 1; - rect[2] = window->geometry.width / 2 + 2; - rect[3] = window->geometry.height / 2 + 2; + rect[0] = window->buffer_size.width / 4 - 1; + rect[1] = window->buffer_size.height / 4 - 1; + rect[2] = window->buffer_size.width / 2 + 2; + rect[3] = window->buffer_size.height / 2 + 2; display->swap_buffers_with_damage(display->egl.dpy, window->egl_surface, rect, 1); @@ -810,9 +810,9 @@ main(int argc, char **argv) window.display = &display; display.window = &window; - window.geometry.width = 250; - window.geometry.height = 250; - window.window_size = window.geometry; + window.buffer_size.width = 250; + window.buffer_size.height = 250; + window.window_size = window.buffer_size; window.buffer_bpp = 0; window.frame_sync = 1; window.delay = 0; From 62ab6891db9763dd44228228aea43c116f5718e1 Mon Sep 17 00:00:00 2001 From: Robert Mader Date: Tue, 24 May 2022 01:31:48 +0200 Subject: [PATCH 311/609] clients/simple-egl: Handle buffer scale and transform Buffer scale is common enough in the modern desktop space to expect average GL clients to handle it. Thus lets include it into our main example client. While on it, also handle buffer transforms. It's essentially free for GL clients in terms of computing power but may increase the chance that Wayland compositors are able to hit scanout fast paths. Thus having an example client for it is likely valueabel for client and compositor developers. Signed-off-by: Robert Mader --- clients/meson.build | 7 +- clients/simple-egl.c | 349 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 335 insertions(+), 21 deletions(-) diff --git a/clients/meson.build b/clients/meson.build index 6056cae5..8e6446a8 100644 --- a/clients/meson.build +++ b/clients/meson.build @@ -131,7 +131,12 @@ simple_clients = [ ivi_application_client_protocol_h, ivi_application_protocol_c, ], - 'dep_objs': [ dep_wayland_client, dep_libshared, dep_libm ], + 'dep_objs': [ + dep_libm, + dep_libshared, + dep_matrix_c, + dep_wayland_client, + ], 'deps': [ 'egl', 'wayland-egl', 'glesv2', 'wayland-cursor' ], 'options': [ 'renderer-gl' ] }, diff --git a/clients/simple-egl.c b/clients/simple-egl.c index c2b8b7c8..05a0ffcb 100644 --- a/clients/simple-egl.c +++ b/clients/simple-egl.c @@ -46,9 +46,11 @@ #include #include +#include #include "shared/helpers.h" #include "shared/platform.h" #include "shared/weston-egl-ext.h" +#include "shared/xalloc.h" struct window; struct seat; @@ -73,6 +75,8 @@ struct display { } egl; struct window *window; + struct wl_list output_list; /* struct output::link */ + PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC swap_buffers_with_damage; }; @@ -82,7 +86,13 @@ struct geometry { struct window { struct display *display; - struct geometry buffer_size, window_size; + struct geometry window_size; + struct geometry logical_size; + struct geometry buffer_size; + int32_t buffer_scale; + enum wl_output_transform buffer_transform; + bool needs_buffer_geometry_update; + struct { GLuint rotation_uniform; GLuint pos; @@ -97,6 +107,22 @@ struct window { EGLSurface egl_surface; int fullscreen, maximized, opaque, buffer_bpp, frame_sync, delay; bool wait_for_configure; + + struct wl_list window_output_list; /* struct window_output::link */ +}; + +struct output { + struct display *display; + struct wl_output *wl_output; + uint32_t name; + struct wl_list link; /* struct display::output_list */ + enum wl_output_transform transform; + int32_t scale; +}; + +struct window_output { + struct output *output; + struct wl_list link; /* struct window::window_output_list */ }; static const char *vert_shader_text = @@ -347,16 +373,13 @@ handle_toplevel_configure(void *data, struct xdg_toplevel *toplevel, window->window_size.width = width; window->window_size.height = height; } - window->buffer_size.width = width; - window->buffer_size.height = height; + window->logical_size.width = width; + window->logical_size.height = height; } else if (!window->fullscreen && !window->maximized) { - window->buffer_size = window->window_size; + window->logical_size = window->window_size; } - if (window->native) - wl_egl_window_resize(window->native, - window->buffer_size.width, - window->buffer_size.height, 0, 0); + window->needs_buffer_geometry_update = true; } static void @@ -370,12 +393,80 @@ static const struct xdg_toplevel_listener xdg_toplevel_listener = { handle_toplevel_close, }; +static void +add_window_output(struct window *window, struct wl_output *wl_output) +{ + struct output *output; + struct output *output_found = NULL; + struct window_output *window_output; + + wl_list_for_each(output, &window->display->output_list, link) { + if (output->wl_output == wl_output) { + output_found = output; + break; + } + } + + if (!output_found) + return; + + window_output = xmalloc(sizeof *window_output); + window_output->output = output_found; + + wl_list_insert(window->window_output_list.prev, &window_output->link); + window->needs_buffer_geometry_update = true; +} + +static void +destroy_window_output(struct window *window, struct wl_output *wl_output) +{ + struct window_output *window_output; + struct window_output *window_output_found = NULL; + + wl_list_for_each(window_output, &window->window_output_list, link) { + if (window_output->output->wl_output == wl_output) { + window_output_found = window_output; + break; + } + } + + if (window_output_found) { + wl_list_remove(&window_output_found->link); + free(window_output_found); + window->needs_buffer_geometry_update = true; + } +} + +static void +surface_enter(void *data, + struct wl_surface *wl_surface, struct wl_output *wl_output) +{ + struct window *window = data; + + add_window_output(window, wl_output); +} + +static void +surface_leave(void *data, + struct wl_surface *wl_surface, struct wl_output *wl_output) +{ + struct window *window = data; + + destroy_window_output(window, wl_output); +} + +static const struct wl_surface_listener surface_listener = { + surface_enter, + surface_leave +}; + static void create_surface(struct window *window) { struct display *display = window->display; window->surface = wl_compositor_create_surface(display->compositor); + wl_surface_add_listener(window->surface, &surface_listener, window); window->xdg_surface = xdg_wm_base_get_xdg_surface(display->wm_base, window->surface); @@ -422,6 +513,90 @@ destroy_surface(struct window *window) wl_surface_destroy(window->surface); } +static int32_t +compute_buffer_scale(struct window *window) +{ + struct window_output *window_output; + int32_t scale = 1; + + wl_list_for_each(window_output, &window->window_output_list, link) { + if (window_output->output->scale > scale) + scale = window_output->output->scale; + } + + return scale; +} + +static enum wl_output_transform +compute_buffer_transform(struct window *window) +{ + struct window_output *window_output; + enum wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; + + wl_list_for_each(window_output, &window->window_output_list, link) { + /* If the surface spans over multiple outputs the optimal + * transform value can be ambiguous. Thus just return the value + * from the oldest entered output. + */ + transform = window_output->output->transform; + break; + } + + return transform; +} + +static void +update_buffer_geometry(struct window *window) +{ + enum wl_output_transform new_buffer_transform; + int32_t new_buffer_scale; + struct geometry new_buffer_size; + + new_buffer_transform = compute_buffer_transform(window); + if (window->buffer_transform != new_buffer_transform) { + window->buffer_transform = new_buffer_transform; + wl_surface_set_buffer_transform(window->surface, + window->buffer_transform); + } + + new_buffer_scale = compute_buffer_scale(window); + if (window->buffer_scale != new_buffer_scale) { + window->buffer_scale = new_buffer_scale; + wl_surface_set_buffer_scale(window->surface, + window->buffer_scale); + } + + switch (window->buffer_transform) { + case WL_OUTPUT_TRANSFORM_NORMAL: + case WL_OUTPUT_TRANSFORM_180: + case WL_OUTPUT_TRANSFORM_FLIPPED: + case WL_OUTPUT_TRANSFORM_FLIPPED_180: + new_buffer_size.width = window->logical_size.width; + new_buffer_size.height = window->logical_size.height; + break; + case WL_OUTPUT_TRANSFORM_90: + case WL_OUTPUT_TRANSFORM_270: + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + case WL_OUTPUT_TRANSFORM_FLIPPED_270: + new_buffer_size.width = window->logical_size.height; + new_buffer_size.height = window->logical_size.width; + break; + } + + new_buffer_size.width *= window->buffer_scale; + new_buffer_size.height *= window->buffer_scale; + + if (window->buffer_size.width != new_buffer_size.width || + window->buffer_size.height != new_buffer_size.height) { + window->buffer_size = new_buffer_size; + wl_egl_window_resize(window->native, + window->buffer_size.width, + window->buffer_size.height, 0, 0); + } + + window->needs_buffer_geometry_update = false; +} + static void redraw(struct window *window) { @@ -437,18 +612,16 @@ redraw(struct window *window) { 0, 0, 1 } }; GLfloat angle; - GLfloat rotation[4][4] = { - { 1, 0, 0, 0 }, - { 0, 1, 0, 0 }, - { 0, 0, 1, 0 }, - { 0, 0, 0, 1 } - }; + struct weston_matrix rotation; static const uint32_t speed_div = 5, benchmark_interval = 5; struct wl_region *region; EGLint rect[4]; EGLint buffer_age = 0; struct timeval tv; + if (window->needs_buffer_geometry_update) + update_buffer_geometry(window); + gettimeofday(&tv, NULL); uint32_t time = tv.tv_sec * 1000 + tv.tv_usec / 1000; if (window->frames == 0) @@ -462,11 +635,42 @@ redraw(struct window *window) window->frames = 0; } - angle = (time / speed_div) % 360 * M_PI / 180.0; - rotation[0][0] = cos(angle); - rotation[0][2] = sin(angle); - rotation[2][0] = -sin(angle); - rotation[2][2] = cos(angle); + weston_matrix_init(&rotation); + angle = ((time - window->benchmark_time) / speed_div) % 360 * M_PI / 180.0; + rotation.d[0] = cos(angle); + rotation.d[2] = sin(angle); + rotation.d[8] = -sin(angle); + rotation.d[10] = cos(angle); + + switch (window->buffer_transform) { + case WL_OUTPUT_TRANSFORM_FLIPPED: + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + case WL_OUTPUT_TRANSFORM_FLIPPED_180: + case WL_OUTPUT_TRANSFORM_FLIPPED_270: + weston_matrix_scale(&rotation, -1, 1, 1); + break; + default: + break; + } + + switch (window->buffer_transform) { + default: + case WL_OUTPUT_TRANSFORM_NORMAL: + case WL_OUTPUT_TRANSFORM_FLIPPED: + break; + case WL_OUTPUT_TRANSFORM_90: + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + weston_matrix_rotate_xy(&rotation, 0, 1); + break; + case WL_OUTPUT_TRANSFORM_180: + case WL_OUTPUT_TRANSFORM_FLIPPED_180: + weston_matrix_rotate_xy(&rotation, -1, 0); + break; + case WL_OUTPUT_TRANSFORM_270: + case WL_OUTPUT_TRANSFORM_FLIPPED_270: + weston_matrix_rotate_xy(&rotation, 0, -1); + break; + } if (display->swap_buffers_with_damage) eglQuerySurface(display->egl.dpy, window->egl_surface, @@ -475,7 +679,7 @@ redraw(struct window *window) glViewport(0, 0, window->buffer_size.width, window->buffer_size.height); glUniformMatrix4fv(window->gl.rotation_uniform, 1, GL_FALSE, - (GLfloat *) rotation); + (GLfloat *) rotation.d); if (window->opaque || window->fullscreen) glClearColor(0.0, 0.0, 0.0, 1); @@ -732,6 +936,92 @@ static const struct xdg_wm_base_listener wm_base_listener = { xdg_wm_base_ping, }; +static void +display_handle_geometry(void *data, + struct wl_output *wl_output, + int32_t x, int32_t y, + int32_t physical_width, + int32_t physical_height, + int32_t subpixel, + const char *make, + const char *model, + int32_t transform) +{ + struct output *output = data; + + output->transform = transform; + output->display->window->needs_buffer_geometry_update = true; +} + +static void +display_handle_mode(void *data, + struct wl_output *wl_output, + uint32_t flags, + int32_t width, + int32_t height, + int32_t refresh) +{ +} + +static void +display_handle_done(void *data, + struct wl_output *wl_output) +{ +} + +static void +display_handle_scale(void *data, + struct wl_output *wl_output, + int32_t scale) +{ + struct output *output = data; + + output->scale = scale; + output->display->window->needs_buffer_geometry_update = true; +} + +static const struct wl_output_listener output_listener = { + display_handle_geometry, + display_handle_mode, + display_handle_done, + display_handle_scale +}; + +static void +display_add_output(struct display *d, uint32_t name) +{ + struct output *output; + + output = xzalloc(sizeof *output); + output->display = d; + output->scale = 1; + output->wl_output = + wl_registry_bind(d->registry, name, &wl_output_interface, 2); + output->name = name; + wl_list_insert(d->output_list.prev, &output->link); + + wl_output_add_listener(output->wl_output, &output_listener, output); +} + +static void +display_destroy_output(struct display *d, struct output *output) +{ + destroy_window_output(d->window, output->wl_output); + wl_output_destroy(output->wl_output); + wl_list_remove(&output->link); + free(output); +} + +static void +display_destroy_outputs(struct display *d) +{ + struct output *tmp; + struct output *output; + + wl_list_for_each_safe(output, tmp, &d->output_list, link) + display_destroy_output(d, output); +} + static void registry_handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) @@ -765,6 +1055,8 @@ registry_handle_global(void *data, struct wl_registry *registry, fprintf(stderr, "unable to load default left pointer\n"); // TODO: abort ? } + } else if (strcmp(interface, "wl_output") == 0 && version >= 2) { + display_add_output(d, name); } } @@ -772,6 +1064,15 @@ static void registry_handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { + struct display *d = data; + struct output *output; + + wl_list_for_each(output, &d->output_list, link) { + if (output->name == name) { + display_destroy_output(d, output); + break; + } + } } static const struct wl_registry_listener registry_listener = { @@ -813,10 +1114,16 @@ main(int argc, char **argv) window.buffer_size.width = 250; window.buffer_size.height = 250; window.window_size = window.buffer_size; + window.buffer_scale = 1; + window.buffer_transform = WL_OUTPUT_TRANSFORM_NORMAL; + window.needs_buffer_geometry_update = false; window.buffer_bpp = 0; window.frame_sync = 1; window.delay = 0; + wl_list_init(&display.output_list); + wl_list_init(&window.window_output_list); + for (i = 1; i < argc; i++) { if (strcmp("-d", argv[i]) == 0 && i+1 < argc) window.delay = atoi(argv[++i]); @@ -884,6 +1191,8 @@ main(int argc, char **argv) wl_surface_destroy(display.cursor_surface); out_no_xdg_shell: + display_destroy_outputs(&display); + if (display.cursor_theme) wl_cursor_theme_destroy(display.cursor_theme); From a1e5d46d9145b418e91994963434bff4a3c4cb66 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 27 May 2022 14:02:14 +0300 Subject: [PATCH 312/609] tests: delete the manual matrix-test This test program was useful a decade ago when weston_matrix_invert() was being developed. It was a manual test program that ran for a certain number of seconds and required human interpretation of numbers to see if results were acceptable or not. Hence it was foundamentally unsuitable for CI. The way it generated random matrices for inversion testing was also very naive, and it used the determinant value to determine invertability which is completely bogus. This made it also a bad test for correctness. Much better speed and correctness testing is implemented in https://gitlab.freedesktop.org/pq/fourbyfour with documented testing procedures. It has a copy of the weston_matrix implementation. Signed-off-by: Pekka Paalanen --- tests/matrix-test.c | 424 -------------------------------------------- tests/meson.build | 1 - 2 files changed, 425 deletions(-) delete mode 100644 tests/matrix-test.c diff --git a/tests/matrix-test.c b/tests/matrix-test.c deleted file mode 100644 index 03b92162..00000000 --- a/tests/matrix-test.c +++ /dev/null @@ -1,424 +0,0 @@ -/* - * Copyright © 2012 Collabora, Ltd. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include - -#include - -struct inverse_matrix { - double LU[16]; /* column-major */ - unsigned perm[4]; /* permutation */ -}; - -static struct timespec begin_time; - -static void -reset_timer(void) -{ - clock_gettime(CLOCK_MONOTONIC, &begin_time); -} - -static double -read_timer(void) -{ - struct timespec t; - - clock_gettime(CLOCK_MONOTONIC, &t); - return (double)(t.tv_sec - begin_time.tv_sec) + - 1e-9 * (t.tv_nsec - begin_time.tv_nsec); -} - -static double -det3x3(const float *c0, const float *c1, const float *c2) -{ - return (double) - c0[0] * c1[1] * c2[2] + - c1[0] * c2[1] * c0[2] + - c2[0] * c0[1] * c1[2] - - c0[2] * c1[1] * c2[0] - - c1[2] * c2[1] * c0[0] - - c2[2] * c0[1] * c1[0]; -} - -static double -determinant(const struct weston_matrix *m) -{ - double det = 0; -#if 1 - /* develop on last row */ - det -= m->d[3 + 0 * 4] * det3x3(&m->d[4], &m->d[8], &m->d[12]); - det += m->d[3 + 1 * 4] * det3x3(&m->d[0], &m->d[8], &m->d[12]); - det -= m->d[3 + 2 * 4] * det3x3(&m->d[0], &m->d[4], &m->d[12]); - det += m->d[3 + 3 * 4] * det3x3(&m->d[0], &m->d[4], &m->d[8]); -#else - /* develop on first row */ - det += m->d[0 + 0 * 4] * det3x3(&m->d[5], &m->d[9], &m->d[13]); - det -= m->d[0 + 1 * 4] * det3x3(&m->d[1], &m->d[9], &m->d[13]); - det += m->d[0 + 2 * 4] * det3x3(&m->d[1], &m->d[5], &m->d[13]); - det -= m->d[0 + 3 * 4] * det3x3(&m->d[1], &m->d[5], &m->d[9]); -#endif - return det; -} - -static void -print_permutation_matrix(const struct inverse_matrix *m) -{ - const unsigned *p = m->perm; - const char *row[4] = { - "1 0 0 0\n", - "0 1 0 0\n", - "0 0 1 0\n", - "0 0 0 1\n" - }; - - printf(" P =\n%s%s%s%s", row[p[0]], row[p[1]], row[p[2]], row[p[3]]); -} - -static void -print_LU_decomposition(const struct inverse_matrix *m) -{ - unsigned r, c; - - printf(" L " - " U\n"); - for (r = 0; r < 4; ++r) { - double v; - - for (c = 0; c < 4; ++c) { - if (c < r) - v = m->LU[r + c * 4]; - else if (c == r) - v = 1.0; - else - v = 0.0; - printf(" %12.6f", v); - } - - printf(" | "); - - for (c = 0; c < 4; ++c) { - if (c >= r) - v = m->LU[r + c * 4]; - else - v = 0.0; - printf(" %12.6f", v); - } - printf("\n"); - } -} - -static void -print_inverse_data_matrix(const struct inverse_matrix *m) -{ - unsigned r, c; - - for (r = 0; r < 4; ++r) { - for (c = 0; c < 4; ++c) - printf(" %12.6f", m->LU[r + c * 4]); - printf("\n"); - } - - printf("permutation: "); - for (r = 0; r < 4; ++r) - printf(" %u", m->perm[r]); - printf("\n"); -} - -static void -print_matrix(const struct weston_matrix *m) -{ - unsigned r, c; - - for (r = 0; r < 4; ++r) { - for (c = 0; c < 4; ++c) - printf(" %14.6e", m->d[r + c * 4]); - printf("\n"); - } -} - -static double -frand(void) -{ - double r = random(); - return r / (double)(RAND_MAX / 2) - 1.0f; -} - -static void -randomize_matrix(struct weston_matrix *m) -{ - unsigned i; - for (i = 0; i < 16; ++i) -#if 1 - m->d[i] = frand() * exp(10.0 * frand()); -#else - m->d[i] = frand(); -#endif -} - -/* Take a matrix, compute inverse, multiply together - * and subtract the identity matrix to get the error matrix. - * Return the largest absolute value from the error matrix. - */ -static double -test_inverse(struct weston_matrix *m) -{ - unsigned i; - struct inverse_matrix q; - double errsup = 0.0; - - if (matrix_invert(q.LU, q.perm, m) != 0) - return INFINITY; - - for (i = 0; i < 4; ++i) - inverse_transform(q.LU, q.perm, &m->d[i * 4]); - - m->d[0] -= 1.0f; - m->d[5] -= 1.0f; - m->d[10] -= 1.0f; - m->d[15] -= 1.0f; - - for (i = 0; i < 16; ++i) { - double err = fabs(m->d[i]); - if (err > errsup) - errsup = err; - } - - return errsup; -} - -enum { - TEST_OK, - TEST_NOT_INVERTIBLE_OK, - TEST_FAIL, - TEST_COUNT -}; - -static int -test(void) -{ - struct weston_matrix m; - double det, errsup; - - randomize_matrix(&m); - det = determinant(&m); - - errsup = test_inverse(&m); - if (errsup < 1e-6) - return TEST_OK; - - if (fabs(det) < 1e-5 && isinf(errsup)) - return TEST_NOT_INVERTIBLE_OK; - - printf("test fail, det: %g, error sup: %g\n", det, errsup); - - return TEST_FAIL; -} - -static int running; -static void -stopme(int n) -{ - running = 0; -} - -static void -test_loop_precision(void) -{ - int counts[TEST_COUNT] = { 0 }; - - printf("\nRunning a test loop for 10 seconds...\n"); - running = 1; - alarm(10); - while (running) { - counts[test()]++; - } - - printf("tests: %d ok, %d not invertible but ok, %d failed.\n" - "Total: %d iterations.\n", - counts[TEST_OK], counts[TEST_NOT_INVERTIBLE_OK], - counts[TEST_FAIL], - counts[TEST_OK] + counts[TEST_NOT_INVERTIBLE_OK] + - counts[TEST_FAIL]); -} - -static void __attribute__((noinline)) -test_loop_speed_matrixvector(void) -{ - struct weston_matrix m; - struct weston_vector v = { { 0.5, 0.5, 0.5, 1.0 } }; - unsigned long count = 0; - double t; - - printf("\nRunning 3 s test on weston_matrix_transform()...\n"); - - weston_matrix_init(&m); - - running = 1; - alarm(3); - reset_timer(); - while (running) { - weston_matrix_transform(&m, &v); - count++; - } - t = read_timer(); - - printf("%lu iterations in %f seconds, avg. %.1f ns/iter.\n", - count, t, 1e9 * t / count); -} - -static void __attribute__((noinline)) -test_loop_speed_inversetransform(void) -{ - struct weston_matrix m; - struct inverse_matrix inv; - struct weston_vector v = { { 0.5, 0.5, 0.5, 1.0 } }; - unsigned long count = 0; - double t; - - printf("\nRunning 3 s test on inverse_transform()...\n"); - - weston_matrix_init(&m); - matrix_invert(inv.LU, inv.perm, &m); - - running = 1; - alarm(3); - reset_timer(); - while (running) { - inverse_transform(inv.LU, inv.perm, v.f); - count++; - } - t = read_timer(); - - printf("%lu iterations in %f seconds, avg. %.1f ns/iter.\n", - count, t, 1e9 * t / count); -} - -static void __attribute__((noinline)) -test_loop_speed_invert(void) -{ - struct weston_matrix m; - struct inverse_matrix inv; - unsigned long count = 0; - double t; - - printf("\nRunning 3 s test on matrix_invert()...\n"); - - weston_matrix_init(&m); - - running = 1; - alarm(3); - reset_timer(); - while (running) { - matrix_invert(inv.LU, inv.perm, &m); - count++; - } - t = read_timer(); - - printf("%lu iterations in %f seconds, avg. %.1f ns/iter.\n", - count, t, 1e9 * t / count); -} - -static void __attribute__((noinline)) -test_loop_speed_invert_explicit(void) -{ - struct weston_matrix m; - unsigned long count = 0; - double t; - - printf("\nRunning 3 s test on weston_matrix_invert()...\n"); - - weston_matrix_init(&m); - - running = 1; - alarm(3); - reset_timer(); - while (running) { - weston_matrix_invert(&m, &m); - count++; - } - t = read_timer(); - - printf("%lu iterations in %f seconds, avg. %.1f ns/iter.\n", - count, t, 1e9 * t / count); -} - -int main(void) -{ - struct sigaction ding; - struct weston_matrix M; - struct inverse_matrix Q; - int ret; - double errsup; - double det; - - ding.sa_handler = stopme; - sigemptyset(&ding.sa_mask); - ding.sa_flags = 0; - sigaction(SIGALRM, &ding, NULL); - - srandom(13); - - M.d[0] = 3.0; M.d[4] = 17.0; M.d[8] = 10.0; M.d[12] = 0.0; - M.d[1] = 2.0; M.d[5] = 4.0; M.d[9] = -2.0; M.d[13] = 0.0; - M.d[2] = 6.0; M.d[6] = 18.0; M.d[10] = -12; M.d[14] = 0.0; - M.d[3] = 0.0; M.d[7] = 0.0; M.d[11] = 0.0; M.d[15] = 1.0; - - ret = matrix_invert(Q.LU, Q.perm, &M); - printf("ret = %d\n", ret); - printf("det = %g\n\n", determinant(&M)); - - if (ret != 0) - return 1; - - print_inverse_data_matrix(&Q); - printf("P * A = L * U\n"); - print_permutation_matrix(&Q); - print_LU_decomposition(&Q); - - - printf("a random matrix:\n"); - randomize_matrix(&M); - det = determinant(&M); - print_matrix(&M); - errsup = test_inverse(&M); - printf("\nThe matrix multiplied by its inverse, error:\n"); - print_matrix(&M); - printf("max abs error: %g, original determinant %g\n", errsup, det); - - test_loop_precision(); - test_loop_speed_matrixvector(); - test_loop_speed_inversetransform(); - test_loop_speed_invert(); - test_loop_speed_invert_explicit(); - - return 0; -} diff --git a/tests/meson.build b/tests/meson.build index 1ebd39c7..8bb656f3 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -251,7 +251,6 @@ endif tests_standalone = [ ['config-parser', [], [ dep_zucmain ]], - ['matrix', [], [ dep_libm, dep_matrix_c ]], ['timespec', [], [ dep_zucmain ]], ['zuc', [ From 8bbd1a995be59485cb7078d3c6c6bbf9f9ae00c6 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 27 May 2022 14:12:58 +0300 Subject: [PATCH 313/609] libweston: remove UNIT_TEST This #define was used only by the matrix-test program, which was removed in the previous commit. Remove it as unused and fold away MATRIX_TEST_EXPORT. Signed-off-by: Pekka Paalanen --- include/libweston/matrix.h | 13 ------------- shared/matrix.c | 9 ++------- tests/meson.build | 2 -- 3 files changed, 2 insertions(+), 22 deletions(-) diff --git a/include/libweston/matrix.h b/include/libweston/matrix.h index be4d4eb0..95a262bb 100644 --- a/include/libweston/matrix.h +++ b/include/libweston/matrix.h @@ -65,19 +65,6 @@ int weston_matrix_invert(struct weston_matrix *inverse, const struct weston_matrix *matrix); -#ifdef UNIT_TEST -# define MATRIX_TEST_EXPORT WL_EXPORT - -int -matrix_invert(double *A, unsigned *p, const struct weston_matrix *matrix); - -void -inverse_transform(const double *LU, const unsigned *p, float *v); - -#else -# define MATRIX_TEST_EXPORT static -#endif - #ifdef __cplusplus } #endif diff --git a/shared/matrix.c b/shared/matrix.c index 4e8d6b40..0df22840 100644 --- a/shared/matrix.c +++ b/shared/matrix.c @@ -31,12 +31,7 @@ #include #include -#ifdef UNIT_TEST -#define WL_EXPORT -#else #include -#endif - #include @@ -169,7 +164,7 @@ find_pivot(double *column, unsigned k) * LU decomposition, forward and back substitution: Chapter 3. */ -MATRIX_TEST_EXPORT inline int +static int matrix_invert(double *A, unsigned *p, const struct weston_matrix *matrix) { unsigned i, j, k; @@ -204,7 +199,7 @@ matrix_invert(double *A, unsigned *p, const struct weston_matrix *matrix) return 0; } -MATRIX_TEST_EXPORT inline void +static void inverse_transform(const double *LU, const unsigned *p, float *v) { /* Solve A * x = v, when we have P * A = L * U. diff --git a/tests/meson.build b/tests/meson.build index 8bb656f3..190255a7 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -341,7 +341,6 @@ foreach t : tests t_name, t_sources, c_args: [ - '-DUNIT_TEST', '-DTHIS_TEST_NAME="' + t_name + '"', ], build_by_default: true, @@ -385,7 +384,6 @@ foreach t : tests_standalone exe_t = executable( 'test-@0@'.format(t.get(0)), srcs_t, - c_args: [ '-DUNIT_TEST' ], build_by_default: true, include_directories: common_inc, dependencies: deps_t, From 892421a93e6957a416f52a315c4fdb66753053cc Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 30 May 2022 16:09:16 +0300 Subject: [PATCH 314/609] tests: add matrix-test for CI This a new matrix inversion test written from scratch to be suitable for running in CI: quick to run and automatically detects success/failure. This all is a result of what I learnt while working on https://gitlab.freedesktop.org/pq/fourbyfour Computing the residual error with infinity norm comes straight from fourbyfour documentation on how to evaluate matrix inversion error. Most of the hard-coded test matrices have been generated with fourbyfour project as well, as it contains the generator code. The matrices are hard-coded here also to make testing faster, but primarily because the generator code needs BLAS and LAPACK, and having those as Weston dependencies would be far too much just for this. Now, if someone wants to modify weston_matrix stuff, we should at least detect matrix inversion and multiplication bugs. Signed-off-by: Pekka Paalanen --- tests/matrix-test.c | 271 ++++++++++++++++++++++++++++++++++++++++++++ tests/meson.build | 4 + 2 files changed, 275 insertions(+) create mode 100644 tests/matrix-test.c diff --git a/tests/matrix-test.c b/tests/matrix-test.c new file mode 100644 index 00000000..11c840da --- /dev/null +++ b/tests/matrix-test.c @@ -0,0 +1,271 @@ +/* + * Copyright 2022 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include "weston-test-client-helper.h" + +/* + * A helper to lay out a matrix in the natural writing order in code + * instead of needing to transpose in your mind every time you read it. + * The matrix is laid out as written: + * ⎡ a11 a12 a13 a14 ⎤ + * ⎢ a21 a22 a23 a24 ⎥ + * ⎢ a31 a32 a33 a34 ⎥ + * ⎣ a41 a42 a43 a44 ⎦ + * where the first digit is row and the second digit is column. + * + * The type field is set to the most pessimistic case possible so that if + * weston_matrix_invert() ever gets special-case code paths, we don't take + * them. + */ +#define MAT(a11, a12, a13, a14, \ + a21, a22, a23, a24, \ + a31, a32, a33, a34, \ + a41, a42, a43, a44) ((struct weston_matrix) \ + { \ + .d[0] = a11, .d[4] = a12, .d[ 8] = a13, .d[12] = a14, \ + .d[1] = a21, .d[5] = a22, .d[ 9] = a23, .d[13] = a24, \ + .d[2] = a31, .d[6] = a32, .d[10] = a33, .d[14] = a34, \ + .d[3] = a41, .d[7] = a42, .d[11] = a43, .d[15] = a44, \ + .type = WESTON_MATRIX_TRANSFORM_TRANSLATE | \ + WESTON_MATRIX_TRANSFORM_SCALE | \ + WESTON_MATRIX_TRANSFORM_ROTATE | \ + WESTON_MATRIX_TRANSFORM_OTHER, \ + }) + +static const struct weston_matrix IDENTITY = + MAT(1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); + +static void +subtract_matrix(struct weston_matrix *from, const struct weston_matrix *what) +{ + unsigned i; + + for (i = 0; i < ARRAY_LENGTH(from->d); i++) + from->d[i] -= what->d[i]; +} + +static void +print_matrix(const struct weston_matrix *m) +{ + unsigned r, c; + + for (r = 0; r < 4; ++r) { + for (c = 0; c < 4; ++c) + testlog(" %14.6e", m->d[r + c * 4]); + testlog("\n"); + } +} + +/* + * Matrix infinity norm + * http://www.netlib.org/lapack/lug/node75.html + */ +static double +matrix_inf_norm(const struct weston_matrix *mat) +{ + unsigned row; + double infnorm = -1.0; + + for (row = 0; row < 4; row++) { + unsigned col; + double sum = 0.0; + + for (col = 0; col < 4; col++) + sum += fabs(mat->d[col * 4 + row]); + + if (infnorm < sum) + infnorm = sum; + } + + return infnorm; +} + +struct test_matrix { + /* the matrix to test */ + struct weston_matrix M; + + /* + * Residual error limit; inf norm(M * inv(M) - I) < err_limit + * The residual error as calculated here represents the relative + * error added by transforming a vector with inv(M). + * + * Since weston_matrix stores the inverse matrix in 32-bit floats, + * that limits the precision considerably. + */ + double err_limit; +}; + +static const struct test_matrix matrices[] = { + /* A very trivial case. */ + { + .M = MAT(1, 0, 0, 0, + 0, 2, 0, 0, + 0, 0, 3, 0, + 0, 0, 0, 4), + .err_limit = 0.0, + }, + + /* + * A very likely case in a compositor, being a matrix applying + * just a translation. Surprisingly, fourbyfour-analyze says: + * + * ------------------------------------------------------------------- + * $ ./fourbyfour-analyse 1 0 0 1980 0 1 0 1080 + * Your input matrix A is + * 1 0 0 1980 + * 0 1 0 1080 + * 0 0 1 0 + * 0 0 0 1 + * + * The singular values of A are: 2255.39, 1, 1, 0.000443382 + * The condition number according to 2-norm of A is 5.087e+06. + * + * This means that if you were to solve the linear system Ax=b for vector x, + * in the worst case you would lose 6.7 digits (22.3 bits) of precision. + * The condition number is how much errors in vector b would be amplified + * when solving x even with infinite computational precision. + * + * Compare this to the precision of vectors b and x: + * + * - Single precision floating point has 7.2 digits (24 bits) of precision, + * leaving your result with no correct digits. + * Single precision, matrix A has rank 3 which means that the solution space + * for x has 1 dimension and therefore has many solutions. + * + * - Double precision floating point has 16.0 digits (53 bits) of precision, + * leaving your result with 9.2 correct digits (30 correct bits). + * Double precision, matrix A has full rank which means the solution x is + * unique. + * + * NOTE! The above gives you only an upper limit on errors. + * If the upper limit is low, you can be confident of your computations. But, + * if the upper limit is high, it does not necessarily imply that your + * computations will be doomed. + * ------------------------------------------------------------------- + * + * This is one example where the condition number is highly pessimistic, + * while the actual inversion results in no error at all. + * + * https://gitlab.freedesktop.org/pq/fourbyfour + */ + { + .M = MAT(1, 0, 0, 1980, + 0, 1, 0, 1080, + 0, 0, 1, 0, + 0, 0, 0, 1), + .err_limit = 0.0, + }, + + /* + * The following matrices have been generated with + * fourbyfour-generate using parameters out of a hat as listed below. + * + * If you want to verify the matrices in Octave, type this: + * M = [ ] + * mat = reshape(M, 4, 4) + * det(mat) + * cond(mat) + */ + + /* cond = 1e3 */ + { + .M = MAT(-4.12798022231678357619e-02, -7.93301899046665176529e-02, 2.49367040174418935772e-01, -2.22400462135059429070e-01, + 2.02416121867255743849e-01, -2.25754422240346010187e-02, -2.91283152417864787953e-01, 1.49354988316431153139e-01, + 6.18473094065821293874e-01, 5.81511312950217934548e-02, -1.18363610818063924590e+00, 8.00087538947595322547e-01, + 1.25723127083294305972e-01, 7.72723720984487272290e-02, -3.76023220287807879991e-01, 2.82473279931768073148e-01), + .err_limit = 1e-5, + }, + + /* cond = 1e3, abs det = 15 */ + { + .M = MAT(6.84154939885726509630e+00, -6.87241565273813304060e+00, -2.56772939909334070308e+01, -2.52185055099662420730e+01, + 2.04511561406330022450e+00, -3.67551043874248994925e+00, -1.96421641406619129633e+00, -2.40644091603848320204e+00, + 5.83631095663641819016e+00, -9.31051765621826277197e+00, -1.80402129629135217215e+01, -1.78475057662460052654e+01, + -9.88588496379959025262e+00, 1.49790516545410774540e+01, 2.64975800675967363418e+01, 2.65795891678410747261e+01), + .err_limit = 1e-4, + }, + + /* cond = 700, abs det = 1e-6, invertible regardless of det */ + { + .M = MAT(1.32125189257677579449e-03, -1.67411409720826992453e-01, 1.07940907587735196449e-01, -1.22163309792902186057e-01, + -5.42113793774764013422e-02, 5.30455105336593901733e-01, -2.59607412684229155175e-01, 4.36480803188117993940e-01, + 2.88175168292948129939e-03, -1.85262537685181277736e-01, 1.46265858042118279680e-01, -9.41398969709369287662e-02, + -2.88900393087768159184e-03, 1.57987202530630227448e-01, -1.20781192010860280450e-01, 8.95194304475115387731e-02), + .err_limit = 1e-4, + }, + + /* cond = 1e6, this is a little more challenging */ + { + .M = MAT(-4.41851445093878913983e-01, -5.16386185043831491548e-01, 2.86186055948129847160e-01, -5.79440137716940473211e-01, + 2.49798696238173301154e-01, 2.84965614532234345901e-01, -1.65729639683955931595e-01, 3.12568045963485974248e-01, + 3.15253213984537428161e-01, 3.71270066781250074328e-01, -2.02675623845341434937e-01, 4.19969870491003371971e-01, + 5.60818677658178832424e-01, 6.45373659426444201692e-01, -3.68902466471524526082e-01, 7.13785795079988516498e-01), + .err_limit = 0.02, + }, + + /* cond = 15, abs det = 1e-9, should be well invertible */ + { + .M = MAT(-5.37536200142514660589e-05, 7.92552373388843642288e-03, -3.90554524958281433500e-03, 2.68892064500873568395e-03, + -9.72329428437283989350e-03, 8.32075145342783470404e-03, 6.52648485926096092596e-03, 1.06707947887298994737e-03, + 1.04453728969657322345e-02, -1.03627268579679666927e-02, -3.56835980207569763989e-03, -3.95935925157862422114e-03, + 5.37160838929722633805e-03, 6.13466744624343262009e-05, -1.23695935407398946090e-04, 8.21231194921675112380e-04), + .err_limit = 1e-6, + }, +}; + +TEST_P(matrix_inversion_precision, matrices) +{ + const struct test_matrix *tm = data; + struct weston_matrix rr; + double err; + + /* Compute rr = M * inv(M) */ + weston_matrix_invert(&rr, &tm->M); + weston_matrix_multiply(&rr, &tm->M); + + /* Residual: subtract identity matrix (expected result) */ + subtract_matrix(&rr, &IDENTITY); + + /* + * Infinity norm of the residual is our measure. + * See https://gitlab.freedesktop.org/pq/fourbyfour/-/blob/master/README.d/precision_testing.md + */ + err = matrix_inf_norm(&rr); + testlog("Residual error %g (%.1f bits precision), limit %g.\n", + err, -log2(err), tm->err_limit); + + if (err > tm->err_limit) { + testlog("Error is too high for matrix\n"); + print_matrix(&tm->M); + assert(0); + } +} diff --git a/tests/meson.build b/tests/meson.build index 190255a7..388dcc9a 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -157,6 +157,10 @@ tests = [ linux_explicit_synchronization_unstable_v1_protocol_c, ], }, + { + 'name': 'matrix', + 'dep_objs': [ dep_libm ] + }, { 'name': 'output-damage', }, { 'name': 'output-transforms', }, { 'name': 'plugin-registry', }, From aa507417c29b3e3d499adcef99ac93318fd4b878 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Wed, 4 May 2022 16:37:32 -0500 Subject: [PATCH 315/609] xwm: Fix pasting in some cases I guess this reverts commit 73bdc0ce85583f65632d4de4b167295dbb06ee6d "xwm: Fix fd leak in weston_wm_send_data()" That commit closes the send half of a pipe in weston_wm_send_data, claiming that it's dup()licated later, and we'll leak the fd if we don't close it. That may have been true at the time? But currently that fd is only duplicated by wl_event_loop_add_fd() in its normal operation, and closing our original before that fd handler ever fires results in an EBADF on write, and the data never reaching its intended destination. Worse, by the time that handler is called there might be another use for that fd, and we could push data into it and close it. To provoke the problem, launch an app like FireFox over Xwayland, cut something to the clipboard, then close the app (this is the path where the wm has stored the clipboard contents and the app has gone away). relaunch it and paste the clipboard content back in. clipboard_client_data() will EBADF on write, and the data won't be pasted. Reported-by: Hideyuki Nagase Signed-off-by: Derek Foreman --- xwayland/selection.c | 1 - 1 file changed, 1 deletion(-) diff --git a/xwayland/selection.c b/xwayland/selection.c index b68988ec..0e4120ac 100644 --- a/xwayland/selection.c +++ b/xwayland/selection.c @@ -513,7 +513,6 @@ weston_wm_send_data(struct weston_wm *wm, xcb_atom_t target, const char *mime_ty source = seat->selection_data_source; source->send(source, mime_type, p[1]); - close(p[1]); } static void From b0257e0ffc60fff5914eff8efb96cd1f5883ddc5 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Thu, 26 May 2022 19:38:18 +0300 Subject: [PATCH 316/609] backend-drm: Add GBM_BO_HANDLE as a failure reason MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit And use it to get a feedback event for when adding scanout tranche. With this change, I get back a feedback event for dmabuf-feedback on VC4: ���� tranche: target device /dev/dri/card0, scanout � ���� format ABGR2101010, modifier LINEAR (0x0) � ���� format XBGR2101010, modifier LINEAR (0x0) � ���� format ARGB8888, modifier LINEAR (0x0) � ���� format ABGR8888, modifier LINEAR (0x0) � ���� format XRGB8888, modifier LINEAR (0x0) � ���� format XBGR8888, modifier LINEAR (0x0) � ���� format RGB565, modifier LINEAR (0x0) � ���� format YUV420, modifier LINEAR (0x0) � ���� format YUV422, modifier LINEAR (0x0) � ���� format YVU420, modifier LINEAR (0x0) � ���� format YVU422, modifier LINEAR (0x0) � ���� format NV12, modifier LINEAR (0x0) � ���� format NV12, modifier BROADCOM_SAND128 (0x700000000000004) � ���� format NV16, modifier LINEAR (0x0) � ���� end of tranche Besides that, it can place a fullscreen state of simple-egl on the primary plane, which without this change wasn't possible. Signed-off-by: Marius Vlad --- libweston/backend-drm/drm-internal.h | 1 + libweston/backend-drm/fb.c | 5 ++++- libweston/backend-drm/state-propose.c | 3 ++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index 1afbaf52..14b5248a 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -246,6 +246,7 @@ enum try_view_on_plane_failure_reasons { FAILURE_REASONS_GLOBAL_ALPHA = 1 << 10, FAILURE_REASONS_NO_GBM = 1 << 11, FAILURE_REASONS_GBM_BO_IMPORT_FAILED = 1 << 12, + FAILURE_REASONS_GBM_BO_GET_HANDLE_FAILED = 1 << 13, }; /** diff --git a/libweston/backend-drm/fb.c b/libweston/backend-drm/fb.c index 71dc1914..eb007340 100644 --- a/libweston/backend-drm/fb.c +++ b/libweston/backend-drm/fb.c @@ -315,8 +315,11 @@ drm_fb_get_from_dmabuf(struct linux_dmabuf_buffer *dmabuf, union gbm_bo_handle handle; handle = gbm_bo_get_handle_for_plane(fb->bo, i); - if (handle.s32 == -1) + if (handle.s32 == -1) { + *try_view_on_plane_failure_reasons |= + FAILURE_REASONS_GBM_BO_GET_HANDLE_FAILED; goto err_free; + } fb->handles[i] = handle.u32; } diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index 49911d98..82da1f37 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -341,7 +341,8 @@ dmabuf_feedback_maybe_update(struct drm_backend *b, struct weston_view *ev, (FAILURE_REASONS_ADD_FB_FAILED | FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE | FAILURE_REASONS_DMABUF_MODIFIER_INVALID | - FAILURE_REASONS_GBM_BO_IMPORT_FAILED)) + FAILURE_REASONS_GBM_BO_IMPORT_FAILED | + FAILURE_REASONS_GBM_BO_GET_HANDLE_FAILED)) action_needed |= ACTION_NEEDED_ADD_SCANOUT_TRANCHE; assert(action_needed != (ACTION_NEEDED_REMOVE_SCANOUT_TRANCHE | From ee085015d055b2bb95a595ebaef6cd34f7d494ab Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Wed, 1 Jun 2022 14:40:44 +0300 Subject: [PATCH 317/609] build: drop unused option rdp-thread-check Clean up leftovers from 2df71c6dd7f82a07f4ae6a8be69e7f94dd2cdec1. Signed-off-by: Pekka Paalanen --- meson_options.txt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/meson_options.txt b/meson_options.txt index d5e832d7..90ab3832 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -26,12 +26,6 @@ option( value: true, description: 'Weston backend: RDP remote screensharing' ) -option( - 'rdp-thread-check', - type: 'boolean', - value: false, - description: 'Aggressive thread sanity checks for the RDP backend' -) option( 'screenshare', type: 'boolean', From 8508f93f2b13f34f091914723d1306de7033d046 Mon Sep 17 00:00:00 2001 From: Hideyuki Nagase Date: Wed, 16 Mar 2022 11:26:04 -0500 Subject: [PATCH 318/609] rdp: Update cursor position on most mouse messages The RDP spec says we can trust x, y position on all messages except PTR_FLAGS_WHEEL and PTR_FLAGS_HWHEEL, so let's do that to ensure proper sync with the RDP client. Co-authored-by: Steve Pronovost Co-authored-by: Brenton DeGeer Signed-off-by: Hideyuki Nagase Signed-off-by: Steve Pronovost Signed-off-by: Brenton DeGeer --- libweston/backend-rdp/rdp.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index e78b26fa..716a13e2 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -1224,7 +1224,13 @@ xf_mouseEvent(rdpInput *input, UINT16 flags, UINT16 x, UINT16 y) dump_mouseinput(peerContext, flags, x, y, false); - if (flags & PTR_FLAGS_MOVE) { + /* Per RDP spec, the x,y position is valid on all input mouse messages, + * except for PTR_FLAGS_WHEEL and PTR_FLAGS_HWHEEL event. Take the opportunity + * to resample our x,y position even when PTR_FLAGS_MOVE isn't explicitly set, + * for example a button down/up only notification, to ensure proper sync with + * the RDP client. + */ + if (!(flags & (PTR_FLAGS_WHEEL | PTR_FLAGS_HWHEEL))) { output = peerContext->rdpBackend->output; if (x < output->base.width && y < output->base.height) { weston_compositor_get_time(&time); From fb7b1a412504d3bcf29e01b0d4b96e3f8badc79c Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Wed, 1 Jun 2022 14:52:11 +0300 Subject: [PATCH 319/609] Revert "build: add test-gl-renderer option" This reverts commit 1618697dc3db9e63ed601be7cbc2fb5e84c94b41. The original commit was a workaround for https://gitlab.freedesktop.org/mesa/mesa/-/issues/2219 which was fixed in Mesa: - c7617d8908a970124321ce731b43d5996c3c5775 released as 20.1.0-rc1 - a0e6341fe4417e41cda0b19e4fa7f8bbe4e1dba1 released as 19.3.5 - f27e5d9df5bc9c85d45c2cb1f2a4997b453365fe released as 20.0.0 This workaround should not be necessary anymore, we don't use it in our CI, and it was manual to begin with. Therefore remove it. Signed-off-by: Pekka Paalanen --- meson.build | 2 -- meson_options.txt | 6 ------ tests/weston-test-fixture-compositor.c | 7 ------- 3 files changed, 15 deletions(-) diff --git a/meson.build b/meson.build index 8498ec1f..47324bd8 100644 --- a/meson.build +++ b/meson.build @@ -121,8 +121,6 @@ config_h.set_quoted('LIBEXECDIR', dir_libexec) config_h.set_quoted('MODULEDIR', dir_module_weston) config_h.set_quoted('LIBWESTON_MODULEDIR', dir_module_libweston) -config_h.set10('TEST_GL_RENDERER', get_option('test-gl-renderer')) - backend_default = get_option('backend-default') if backend_default == 'auto' foreach b : [ 'headless', 'x11', 'wayland', 'drm' ] diff --git a/meson_options.txt b/meson_options.txt index 90ab3832..38c746e9 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -210,12 +210,6 @@ option( value: false, description: 'Tests: consider skip to be a failure' ) -option( - 'test-gl-renderer', - type: 'boolean', - value: true, - description: 'Tests: allow running with GL-renderer' -) option( 'doc', type: 'boolean', diff --git a/tests/weston-test-fixture-compositor.c b/tests/weston-test-fixture-compositor.c index 87ff6bf1..420ab8f1 100644 --- a/tests/weston-test-fixture-compositor.c +++ b/tests/weston-test-fixture-compositor.c @@ -329,13 +329,6 @@ execute_compositor(const struct compositor_setup *setup, } #endif -#if !TEST_GL_RENDERER - if (setup->renderer == RENDERER_GL) { - fprintf(stderr, "GL-renderer disabled for tests, skipping.\n"); - return RESULT_SKIP; - } -#endif - prog_args_init(&args); /* argv[0] */ From cc924e8131d4e16bb7515ea7efe4bed5e6d86fc0 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Thu, 2 Jun 2022 16:31:22 -0500 Subject: [PATCH 320/609] libweston-desktop/xwayland: Use correct geometry Looks like a copy and paste error. Signed-off-by: Derek Foreman --- libweston-desktop/xwayland.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libweston-desktop/xwayland.c b/libweston-desktop/xwayland.c index cdb65dfc..9d4d22b4 100644 --- a/libweston-desktop/xwayland.c +++ b/libweston-desktop/xwayland.c @@ -151,7 +151,7 @@ weston_desktop_xwayland_surface_committed(struct weston_desktop_surface *dsurfac if (surface->has_next_geometry) { oldgeom = weston_desktop_surface_get_geometry(surface->surface); sx -= surface->next_geometry.x - oldgeom.x; - sy -= surface->next_geometry.y - oldgeom.x; + sy -= surface->next_geometry.y - oldgeom.y; surface->has_next_geometry = false; weston_desktop_surface_set_geometry(surface->surface, From c0cafde80fec0dcbdf585207d14e3d36531aa2b2 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Wed, 1 Jun 2022 17:21:48 -0500 Subject: [PATCH 321/609] drm: Remove destroy listener from list when fired Looks like we missed this one during the conversion to weston_signal_emit_mutable. Found by running weston under valgrind and running/killing weston-simple-dmabuf-egl Signed-off-by: Derek Foreman --- libweston/backend-drm/fb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libweston/backend-drm/fb.c b/libweston/backend-drm/fb.c index eb007340..1d518df3 100644 --- a/libweston/backend-drm/fb.c +++ b/libweston/backend-drm/fb.c @@ -512,6 +512,8 @@ drm_fb_handle_buffer_destroy(struct wl_listener *listener, void *data) struct drm_buffer_fb *buf_fb = container_of(listener, struct drm_buffer_fb, buffer_destroy_listener); + wl_list_remove(&buf_fb->buffer_destroy_listener.link); + if (buf_fb->fb) { assert(buf_fb->fb->type == BUFFER_CLIENT || buf_fb->fb->type == BUFFER_DMABUF); From 83d1eafd817b40ede7081f81116f64f4b3d358f3 Mon Sep 17 00:00:00 2001 From: Michael Olbrich Date: Wed, 20 Oct 2021 15:08:01 +0200 Subject: [PATCH 322/609] backend-drm: virtual: use the DRM fd from the fb The fb already contains a DRM fd for later use. So just use that one instead of fetching it from the backend. This is necessary if the fbs are allocated on different devices, since otherwise the wrong device might be used to get the fd of the passed fb. Signed-off-by: Michael Olbrich Signed-off-by: Michael Tretter --- libweston/backend-drm/drm-virtual.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libweston/backend-drm/drm-virtual.c b/libweston/backend-drm/drm-virtual.c index 3be2feb3..d3e5b720 100644 --- a/libweston/backend-drm/drm-virtual.c +++ b/libweston/backend-drm/drm-virtual.c @@ -163,11 +163,10 @@ static int drm_virtual_output_submit_frame(struct drm_output *output, struct drm_fb *fb) { - struct drm_backend *b = to_drm_backend(output->base.compositor); int fd, ret; assert(fb->num_planes == 1); - ret = drmPrimeHandleToFD(b->drm.fd, fb->handles[0], DRM_CLOEXEC, &fd); + ret = drmPrimeHandleToFD(fb->fd, fb->handles[0], DRM_CLOEXEC, &fd); if (ret) { weston_log("drmPrimeHandleFD failed, errno=%d\n", errno); return -1; From 00b74293e8b20b3ea19a5f8fa7b89ee00ed0422f Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Mon, 22 Nov 2021 16:50:35 +0100 Subject: [PATCH 323/609] backend-drm: use pixel format to print gbm format The gbm_format is the same as the drm format used by the pixel format. Print the format name using the pixel format in the error message to make the error message easier to understand for humans. Signed-off-by: Michael Tretter --- libweston/backend-drm/drm-gbm.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/libweston/backend-drm/drm-gbm.c b/libweston/backend-drm/drm-gbm.c index d0a4c6ca..3ed96091 100644 --- a/libweston/backend-drm/drm-gbm.c +++ b/libweston/backend-drm/drm-gbm.c @@ -183,6 +183,7 @@ create_gbm_surface(struct gbm_device *gbm, struct drm_output *output) { struct weston_mode *mode = output->base.current_mode; struct drm_plane *plane = output->scanout_plane; + const struct pixel_format_info *pixel_format; struct weston_drm_format *fmt; const uint64_t *modifiers; unsigned int num_modifiers; @@ -190,8 +191,15 @@ create_gbm_surface(struct gbm_device *gbm, struct drm_output *output) fmt = weston_drm_format_array_find_format(&plane->formats, output->gbm_format); if (!fmt) { - weston_log("format 0x%x not supported by output %s\n", - output->gbm_format, output->base.name); + pixel_format = pixel_format_get_info(output->gbm_format); + if (pixel_format) + weston_log("format %s not supported by output %s\n", + pixel_format->drm_format_name, + output->base.name); + else + weston_log("format 0x%x not supported by output %s\n", + output->gbm_format, + output->base.name); return; } From 2860933deded2e8aa30c6e005ebe038b2868a15d Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Tue, 16 Nov 2021 12:34:47 +0100 Subject: [PATCH 324/609] backend-drm: cleanup debugging Get the backend at the beginning of the function instead of retrieving it from another object in the debug statement. This simplifies refactoring, as the debug statement is not affected by changes how the backend is retrieved. Signed-off-by: Michael Tretter --- libweston/backend-drm/kms.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/libweston/backend-drm/kms.c b/libweston/backend-drm/kms.c index 6e2d29b2..b84eb881 100644 --- a/libweston/backend-drm/kms.c +++ b/libweston/backend-drm/kms.c @@ -789,6 +789,7 @@ static int crtc_add_prop(drmModeAtomicReq *req, struct drm_crtc *crtc, enum wdrm_crtc_property prop, uint64_t val) { + struct drm_backend *b = crtc->backend; struct drm_property_info *info = &crtc->props_crtc[prop]; int ret; @@ -797,7 +798,7 @@ crtc_add_prop(drmModeAtomicReq *req, struct drm_crtc *crtc, ret = drmModeAtomicAddProperty(req, crtc->crtc_id, info->prop_id, val); - drm_debug(crtc->backend, "\t\t\t[CRTC:%lu] %lu (%s) -> %llu (0x%llx)\n", + drm_debug(b, "\t\t\t[CRTC:%lu] %lu (%s) -> %llu (0x%llx)\n", (unsigned long) crtc->crtc_id, (unsigned long) info->prop_id, info->name, (unsigned long long) val, (unsigned long long) val); @@ -808,6 +809,7 @@ static int connector_add_prop(drmModeAtomicReq *req, struct drm_connector *connector, enum wdrm_connector_property prop, uint64_t val) { + struct drm_backend *b = connector->backend; struct drm_property_info *info = &connector->props[prop]; uint32_t connector_id = connector->connector_id; int ret; @@ -816,7 +818,7 @@ connector_add_prop(drmModeAtomicReq *req, struct drm_connector *connector, return -1; ret = drmModeAtomicAddProperty(req, connector_id, info->prop_id, val); - drm_debug(connector->backend, "\t\t\t[CONN:%lu] %lu (%s) -> %llu (0x%llx)\n", + drm_debug(b, "\t\t\t[CONN:%lu] %lu (%s) -> %llu (0x%llx)\n", (unsigned long) connector_id, (unsigned long) info->prop_id, info->name, (unsigned long long) val, (unsigned long long) val); @@ -827,6 +829,7 @@ static int plane_add_prop(drmModeAtomicReq *req, struct drm_plane *plane, enum wdrm_plane_property prop, uint64_t val) { + struct drm_backend *b = plane->backend; struct drm_property_info *info = &plane->props[prop]; int ret; @@ -835,7 +838,7 @@ plane_add_prop(drmModeAtomicReq *req, struct drm_plane *plane, ret = drmModeAtomicAddProperty(req, plane->plane_id, info->prop_id, val); - drm_debug(plane->backend, "\t\t\t[PLANE:%lu] %lu (%s) -> %llu (0x%llx)\n", + drm_debug(b, "\t\t\t[PLANE:%lu] %lu (%s) -> %llu (0x%llx)\n", (unsigned long) plane->plane_id, (unsigned long) info->prop_id, info->name, (unsigned long long) val, (unsigned long long) val); @@ -1021,9 +1024,9 @@ drm_output_apply_state_atomic(struct drm_output_state *state, if (plane_state->fb && plane_state->fb->format) pinfo = plane_state->fb->format; - drm_debug(plane->backend, "\t\t\t[PLANE:%lu] FORMAT: %s\n", - (unsigned long) plane->plane_id, - pinfo ? pinfo->drm_format_name : "UNKNOWN"); + drm_debug(b, "\t\t\t[PLANE:%lu] FORMAT: %s\n", + (unsigned long) plane->plane_id, + pinfo ? pinfo->drm_format_name : "UNKNOWN"); if (plane_state->in_fence_fd >= 0) { ret |= plane_add_prop(req, plane, From 0d967bd7f4f8c9ba5f77d6205bad2a0b1ae2d14a Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Mon, 29 Nov 2021 14:21:40 +0100 Subject: [PATCH 325/609] backend-drm: extract device from backend Extract the kms device from the backend to allow a better separation of the backend and the kms device. This will allow to handle multiple kms devices with a single drm backend. Signed-off-by: Michael Tretter --- libweston/backend-drm/drm-gbm.c | 11 +- libweston/backend-drm/drm-internal.h | 70 +++--- libweston/backend-drm/drm-virtual.c | 9 +- libweston/backend-drm/drm.c | 298 +++++++++++++++----------- libweston/backend-drm/fb.c | 39 ++-- libweston/backend-drm/kms-color.c | 5 +- libweston/backend-drm/kms.c | 140 ++++++------ libweston/backend-drm/modes.c | 24 ++- libweston/backend-drm/state-helpers.c | 8 +- libweston/backend-drm/state-propose.c | 50 +++-- 10 files changed, 382 insertions(+), 272 deletions(-) diff --git a/libweston/backend-drm/drm-gbm.c b/libweston/backend-drm/drm-gbm.c index 3ed96091..1799e8da 100644 --- a/libweston/backend-drm/drm-gbm.c +++ b/libweston/backend-drm/drm-gbm.c @@ -118,8 +118,9 @@ drm_backend_create_gl_renderer(struct drm_backend *b) int init_egl(struct drm_backend *b) { - b->gbm = create_gbm_device(b->drm.fd); + struct drm_device *device = b->drm; + b->gbm = create_gbm_device(device->drm.fd); if (!b->gbm) return -1; @@ -145,6 +146,7 @@ static void drm_output_fini_cursor_egl(struct drm_output *output) static int drm_output_init_cursor_egl(struct drm_output *output, struct drm_backend *b) { + struct drm_device *device = b->drm; unsigned int i; /* No point creating cursors if we don't have a plane for them. */ @@ -154,7 +156,7 @@ drm_output_init_cursor_egl(struct drm_output *output, struct drm_backend *b) for (i = 0; i < ARRAY_LENGTH(output->gbm_cursor_fb); i++) { struct gbm_bo *bo; - bo = gbm_bo_create(b->gbm, b->cursor_width, b->cursor_height, + bo = gbm_bo_create(b->gbm, device->cursor_width, device->cursor_height, GBM_FORMAT_ARGB8888, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE); if (!bo) @@ -173,7 +175,7 @@ drm_output_init_cursor_egl(struct drm_output *output, struct drm_backend *b) err: weston_log("cursor buffers unavailable, using gl cursors\n"); - b->cursors_are_broken = true; + device->cursors_are_broken = true; drm_output_fini_cursor_egl(output); return -1; } @@ -317,6 +319,7 @@ drm_output_render_gl(struct drm_output_state *state, pixman_region32_t *damage) static void switch_to_gl_renderer(struct drm_backend *b) { + struct drm_device *device = b->drm; struct drm_output *output; bool dmabuf_support_inited; bool linux_explicit_sync_inited; @@ -330,7 +333,7 @@ switch_to_gl_renderer(struct drm_backend *b) weston_log("Switching to GL renderer\n"); - b->gbm = create_gbm_device(b->drm.fd); + b->gbm = create_gbm_device(device->drm.fd); if (!b->gbm) { weston_log("Failed to create gbm device. " "Aborting renderer switch\n"); diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index 14b5248a..062b8612 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -259,15 +259,8 @@ enum actions_needed_dmabuf_feedback { ACTION_NEEDED_REMOVE_SCANOUT_TRANCHE = (1 << 1), }; -struct drm_backend { - struct weston_backend base; - struct weston_compositor *compositor; - - struct udev *udev; - struct wl_event_source *drm_source; - - struct udev_monitor *udev_monitor; - struct wl_event_source *udev_drm_source; +struct drm_device { + struct drm_backend *backend; struct { int id; @@ -275,50 +268,63 @@ struct drm_backend { char *filename; dev_t devnum; } drm; - struct gbm_device *gbm; - struct wl_listener session_listener; - uint32_t gbm_format; - /* we need these parameters in order to not fail drmModeAddFB2() - * due to out of bounds dimensions, and then mistakenly set - * sprites_are_broken: - */ - int min_width, max_width; - int min_height, max_height; + /* drm_crtc::link */ + struct wl_list crtc_list; struct wl_list plane_list; - void *repaint_data; + /* drm_writeback::link */ + struct wl_list writeback_connector_list; bool state_invalid; - /* drm_crtc::link */ - struct wl_list crtc_list; + bool atomic_modeset; - /* drm_writeback::link */ - struct wl_list writeback_connector_list; + bool aspect_ratio_supported; + + int32_t cursor_width; + int32_t cursor_height; - bool sprites_are_broken; bool cursors_are_broken; + bool sprites_are_broken; - bool atomic_modeset; + void *repaint_data; + + bool fb_modifiers; + + /* we need these parameters in order to not fail drmModeAddFB2() + * due to out of bounds dimensions, and then mistakenly set + * sprites_are_broken: + */ + int min_width, max_width; + int min_height, max_height; +}; + +struct drm_backend { + struct weston_backend base; + struct weston_compositor *compositor; + + struct udev *udev; + struct wl_event_source *drm_source; + + struct udev_monitor *udev_monitor; + struct wl_event_source *udev_drm_source; + + struct drm_device *drm; + struct gbm_device *gbm; + struct wl_listener session_listener; + uint32_t gbm_format; bool use_pixman; bool use_pixman_shadow; struct udev_input input; - int32_t cursor_width; - int32_t cursor_height; - uint32_t pageflip_timeout; bool shutting_down; - bool aspect_ratio_supported; - - bool fb_modifiers; - struct weston_log_scope *debug; }; diff --git a/libweston/backend-drm/drm-virtual.c b/libweston/backend-drm/drm-virtual.c index d3e5b720..24004e14 100644 --- a/libweston/backend-drm/drm-virtual.c +++ b/libweston/backend-drm/drm-virtual.c @@ -92,6 +92,7 @@ drm_virtual_crtc_destroy(struct drm_crtc *crtc) static struct drm_plane * drm_virtual_plane_create(struct drm_backend *b, struct drm_output *output) { + struct drm_device *device = b->drm; struct drm_plane *plane; struct weston_drm_format *fmt; uint64_t mod; @@ -115,7 +116,7 @@ drm_virtual_plane_create(struct drm_backend *b, struct drm_output *output) /* If output supports linear modifier, we add it to the plane. * Otherwise we add DRM_FORMAT_MOD_INVALID, as explicit modifiers * are not supported. */ - if ((output->gbm_bo_flags & GBM_BO_USE_LINEAR) && b->fb_modifiers) + if ((output->gbm_bo_flags & GBM_BO_USE_LINEAR) && device->fb_modifiers) mod = DRM_FORMAT_MOD_LINEAR; else mod = DRM_FORMAT_MOD_INVALID; @@ -124,7 +125,7 @@ drm_virtual_plane_create(struct drm_backend *b, struct drm_output *output) goto err; weston_plane_init(&plane->base, b->compositor, 0, 0); - wl_list_insert(&b->plane_list, &plane->link); + wl_list_insert(&device->plane_list, &plane->link); return plane; @@ -192,11 +193,13 @@ drm_virtual_output_repaint(struct weston_output *output_base, struct drm_plane_state *scanout_state; struct drm_pending_state *pending_state; struct drm_backend *backend; + struct drm_device *device; assert(output->virtual); backend = output->backend; - pending_state = backend->repaint_data; + device = backend->drm; + pending_state = device->repaint_data; if (output->disable_pending || output->destroy_pending) goto err; diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index 5625d7cf..89838afd 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -71,6 +71,7 @@ static const char default_seat[] = "seat0"; static void drm_backend_create_faked_zpos(struct drm_backend *b) { + struct drm_device *device = b->drm; struct drm_plane *plane; uint64_t zpos = 0ULL; uint64_t zpos_min_primary; @@ -78,7 +79,7 @@ drm_backend_create_faked_zpos(struct drm_backend *b) uint64_t zpos_min_cursor; zpos_min_primary = zpos; - wl_list_for_each(plane, &b->plane_list, link) { + wl_list_for_each(plane, &device->plane_list, link) { /* if the property is there, bail out sooner */ if (plane->props[WDRM_PLANE_ZPOS].prop_id != 0) return; @@ -89,14 +90,14 @@ drm_backend_create_faked_zpos(struct drm_backend *b) } zpos_min_overlay = zpos; - wl_list_for_each(plane, &b->plane_list, link) { + wl_list_for_each(plane, &device->plane_list, link) { if (plane->type != WDRM_PLANE_TYPE_OVERLAY) continue; zpos++; } zpos_min_cursor = zpos; - wl_list_for_each(plane, &b->plane_list, link) { + wl_list_for_each(plane, &device->plane_list, link) { if (plane->type != WDRM_PLANE_TYPE_CURSOR) continue; zpos++; @@ -105,7 +106,7 @@ drm_backend_create_faked_zpos(struct drm_backend *b) drm_debug(b, "[drm-backend] zpos property not found. " "Using invented immutable zpos values:\n"); /* assume that invented zpos values are immutable */ - wl_list_for_each(plane, &b->plane_list, link) { + wl_list_for_each(plane, &device->plane_list, link) { if (plane->type == WDRM_PLANE_TYPE_PRIMARY) { plane->zpos_min = zpos_min_primary; plane->zpos_max = zpos_min_primary; @@ -194,9 +195,10 @@ drm_plane_is_available(struct drm_plane *plane, struct drm_output *output) struct drm_crtc * drm_crtc_find(struct drm_backend *b, uint32_t crtc_id) { + struct drm_device *device = b->drm; struct drm_crtc *crtc; - wl_list_for_each(crtc, &b->crtc_list, link) { + wl_list_for_each(crtc, &device->crtc_list, link) { if (crtc->crtc_id == crtc_id) return crtc; } @@ -225,7 +227,7 @@ drm_writeback_find_by_connector(struct drm_backend *backend, uint32_t connector_ { struct drm_writeback *writeback; - wl_list_for_each(writeback, &backend->writeback_connector_list, link) { + wl_list_for_each(writeback, &backend->drm->writeback_connector_list, link) { if (writeback->connector.connector_id == connector_id) return writeback; } @@ -356,6 +358,7 @@ drm_output_render(struct drm_output_state *state, pixman_region32_t *damage) struct drm_property_info *damage_info = &scanout_plane->props[WDRM_PLANE_FB_DAMAGE_CLIPS]; struct drm_backend *b = to_drm_backend(c); + struct drm_device *device = b->drm; struct drm_fb *fb; pixman_region32_t scanout_damage; pixman_box32_t *rects; @@ -433,7 +436,7 @@ drm_output_render(struct drm_output_state *state, pixman_region32_t *damage) * that it will consider the whole plane damaged. While this may * affect efficiency, it should still produce correct results. */ - drmModeCreatePropertyBlob(b->drm.fd, rects, + drmModeCreatePropertyBlob(device->drm.fd, rects, sizeof(*rects) * n_rects, &scanout_state->damage_blob_id); @@ -448,11 +451,13 @@ drm_output_repaint(struct weston_output *output_base, pixman_region32_t *damage) struct drm_plane_state *scanout_state; struct drm_pending_state *pending_state; struct drm_backend *backend; + struct drm_device *device; assert(!output->virtual); backend = output->backend; - pending_state = backend->repaint_data; + device = backend->drm; + pending_state = device->repaint_data; if (output->disable_pending || output->destroy_pending) goto err; @@ -523,6 +528,7 @@ drm_output_start_repaint_loop(struct weston_output *output_base) struct drm_plane *scanout_plane = output->scanout_plane; struct drm_backend *backend = to_drm_backend(output_base->compositor); + struct drm_device *device = backend->drm; struct timespec ts, tnow; struct timespec vbl2now; int64_t refresh_nsec; @@ -544,14 +550,14 @@ drm_output_start_repaint_loop(struct weston_output *output_base) /* Need to smash all state in from scratch; current timings might not * be what we want, page flip might not work, etc. */ - if (backend->state_invalid) + if (device->state_invalid) goto finish_frame; assert(scanout_plane->state_cur->output == output); /* Try to get current msc and timestamp via instant query */ vbl.request.type |= drm_waitvblank_pipe(output->crtc); - ret = drmWaitVBlank(backend->drm.fd, &vbl); + ret = drmWaitVBlank(device->drm.fd, &vbl); /* Error ret or zero timestamp means failure to get valid timestamp */ if ((ret == 0) && (vbl.reply.tval_sec > 0 || vbl.reply.tval_usec > 0)) { @@ -615,15 +621,16 @@ static void drm_repaint_begin(struct weston_compositor *compositor) { struct drm_backend *b = to_drm_backend(compositor); + struct drm_device *device = b->drm; struct drm_pending_state *pending_state; pending_state = drm_pending_state_alloc(b); - b->repaint_data = pending_state; + device->repaint_data = pending_state; if (weston_log_scope_is_enabled(b->debug)) { char *dbg = weston_compositor_print_scene_graph(compositor); drm_debug(b, "[repaint] Beginning repaint; pending_state %p\n", - b->repaint_data); + device->repaint_data); drm_debug(b, "%s", dbg); free(dbg); } @@ -642,7 +649,8 @@ static int drm_repaint_flush(struct weston_compositor *compositor) { struct drm_backend *b = to_drm_backend(compositor); - struct drm_pending_state *pending_state = b->repaint_data; + struct drm_device *device = b->drm; + struct drm_pending_state *pending_state = device->repaint_data; int ret; ret = drm_pending_state_apply(pending_state); @@ -650,7 +658,7 @@ drm_repaint_flush(struct weston_compositor *compositor) weston_log("repaint-flush failed: %s\n", strerror(errno)); drm_debug(b, "[repaint] flushed pending_state %p\n", pending_state); - b->repaint_data = NULL; + device->repaint_data = NULL; return (ret == -EACCES) ? -1 : 0; } @@ -665,11 +673,12 @@ static void drm_repaint_cancel(struct weston_compositor *compositor) { struct drm_backend *b = to_drm_backend(compositor); - struct drm_pending_state *pending_state = b->repaint_data; + struct drm_device *device = b->drm; + struct drm_pending_state *pending_state = device->repaint_data; drm_pending_state_free(pending_state); drm_debug(b, "[repaint] cancel pending_state %p\n", pending_state); - b->repaint_data = NULL; + device->repaint_data = NULL; } static int @@ -682,6 +691,7 @@ drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mo { struct drm_output *output = to_drm_output(output_base); struct drm_backend *b = to_drm_backend(output_base->compositor); + struct drm_device *device = b->drm; struct drm_mode *drm_mode = drm_output_choose_mode(output, mode); if (!drm_mode) { @@ -705,7 +715,7 @@ drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mo * sledgehammer modeswitch first, and only later showing new * content. */ - b->state_invalid = true; + device->state_invalid = true; if (b->use_pixman) { drm_output_fini_pixman(output); @@ -750,6 +760,7 @@ init_pixman(struct drm_backend *b) static struct drm_plane * drm_plane_create(struct drm_backend *b, const drmModePlane *kplane) { + struct drm_device *device = b->drm; struct drm_plane *plane, *tmp; drmModeObjectProperties *props; uint64_t *zpos_range_values; @@ -768,7 +779,7 @@ drm_plane_create(struct drm_backend *b, const drmModePlane *kplane) weston_drm_format_array_init(&plane->formats); - props = drmModeObjectGetProperties(b->drm.fd, kplane->plane_id, + props = drmModeObjectGetProperties(device->drm.fd, kplane->plane_id, DRM_MODE_OBJECT_PLANE); if (!props) { weston_log("couldn't get plane properties\n"); @@ -795,7 +806,7 @@ drm_plane_create(struct drm_backend *b, const drmModePlane *kplane) } if (drm_plane_populate_formats(plane, kplane, props, - b->fb_modifiers) < 0) { + device->fb_modifiers) < 0) { drmModeFreeObjectProperties(props); goto err; } @@ -807,14 +818,14 @@ drm_plane_create(struct drm_backend *b, const drmModePlane *kplane) weston_plane_init(&plane->base, b->compositor, 0, 0); - wl_list_for_each(tmp, &b->plane_list, link) { + wl_list_for_each(tmp, &device->plane_list, link) { if (tmp->zpos_max > plane->zpos_max) { wl_list_insert(tmp->link.prev, &plane->link); break; } } if (plane->link.next == NULL) - wl_list_insert(b->plane_list.prev, &plane->link); + wl_list_insert(&device->plane_list, &plane->link); return plane; @@ -838,9 +849,10 @@ static struct drm_plane * drm_output_find_special_plane(struct drm_backend *b, struct drm_output *output, enum wdrm_plane_type type) { + struct drm_device *device = b->drm; struct drm_plane *plane; - wl_list_for_each(plane, &b->plane_list, link) { + wl_list_for_each(plane, &device->plane_list, link) { struct drm_output *tmp; bool found_elsewhere = false; @@ -881,8 +893,11 @@ drm_output_find_special_plane(struct drm_backend *b, struct drm_output *output, static void drm_plane_destroy(struct drm_plane *plane) { + struct drm_backend *backend = plane->backend; + struct drm_device *device = backend->drm; + if (plane->type == WDRM_PLANE_TYPE_OVERLAY) - drmModeSetPlane(plane->backend->drm.fd, plane->plane_id, + drmModeSetPlane(device->drm.fd, plane->plane_id, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); drm_plane_state_free(plane->state_cur, true); drm_property_info_free(plane->props, WDRM_PLANE__COUNT); @@ -904,12 +919,14 @@ drm_plane_destroy(struct drm_plane *plane) static void create_sprites(struct drm_backend *b) { + struct drm_device *device = b->drm; drmModePlaneRes *kplane_res; drmModePlane *kplane; struct drm_plane *drm_plane; uint32_t i; uint32_t next_plane_idx = 0; - kplane_res = drmModeGetPlaneResources(b->drm.fd); + kplane_res = drmModeGetPlaneResources(device->drm.fd); + if (!kplane_res) { weston_log("failed to get plane resources: %s\n", strerror(errno)); @@ -917,7 +934,7 @@ create_sprites(struct drm_backend *b) } for (i = 0; i < kplane_res->count_planes; i++) { - kplane = drmModeGetPlane(b->drm.fd, kplane_res->planes[i]); + kplane = drmModeGetPlane(device->drm.fd, kplane_res->planes[i]); if (!kplane) continue; @@ -932,7 +949,7 @@ create_sprites(struct drm_backend *b) &b->compositor->primary_plane); } - wl_list_for_each (drm_plane, &b->plane_list, link) + wl_list_for_each (drm_plane, &device->plane_list, link) drm_plane->plane_idx = next_plane_idx++; drmModeFreePlaneResources(kplane_res); @@ -948,9 +965,10 @@ create_sprites(struct drm_backend *b) static void destroy_sprites(struct drm_backend *b) { + struct drm_device *device = b->drm; struct drm_plane *plane, *next; - wl_list_for_each_safe(plane, next, &b->plane_list, link) + wl_list_for_each_safe(plane, next, &device->plane_list, link) drm_plane_destroy(plane); } @@ -1034,7 +1052,8 @@ drm_set_dpms(struct weston_output *output_base, enum dpms_enum level) { struct drm_output *output = to_drm_output(output_base); struct drm_backend *b = to_drm_backend(output_base->compositor); - struct drm_pending_state *pending_state = b->repaint_data; + struct drm_device *device = b->drm; + struct drm_pending_state *pending_state = device->repaint_data; struct drm_output_state *state; int ret; @@ -1254,6 +1273,7 @@ drm_output_attach_head(struct weston_output *output_base, struct weston_head *head_base) { struct drm_backend *b = to_drm_backend(output_base->compositor); + struct drm_device *device = b->drm; if (wl_list_length(&output_base->head_list) >= MAX_CLONED_CONNECTORS) return -1; @@ -1270,7 +1290,7 @@ drm_output_attach_head(struct weston_output *output_base, /* XXX: Doing it globally, what guarantees another output's update * will not clear the flag before this output is updated? */ - b->state_invalid = true; + device->state_invalid = true; weston_output_schedule_repaint(output_base); @@ -1282,6 +1302,7 @@ drm_output_detach_head(struct weston_output *output_base, struct weston_head *head_base) { struct drm_backend *b = to_drm_backend(output_base->compositor); + struct drm_device *device = b->drm; if (!output_base->enabled) return; @@ -1289,7 +1310,7 @@ drm_output_detach_head(struct weston_output *output_base, /* Need to go through modeset to drop connectors that should no longer * be driven. */ /* XXX: Ideally we'd do this per-output, not globally. */ - b->state_invalid = true; + device->state_invalid = true; weston_output_schedule_repaint(output_base); } @@ -1321,7 +1342,8 @@ parse_gbm_format(const char *s, uint32_t default_value, uint32_t *gbm_format) static int drm_head_read_current_setup(struct drm_head *head, struct drm_backend *backend) { - int drm_fd = backend->drm.fd; + struct drm_device *device = backend->drm; + int drm_fd = device->drm.fd; drmModeConnector *conn = head->connector.conn; drmModeEncoder *encoder; drmModeCrtc *crtc; @@ -1371,11 +1393,12 @@ static int drm_output_init_gamma_size(struct drm_output *output) { struct drm_backend *backend = to_drm_backend(output->base.compositor); + struct drm_device *device = backend->drm; drmModeCrtc *crtc; assert(output->base.compositor); assert(output->crtc); - crtc = drmModeGetCrtc(backend->drm.fd, output->crtc->crtc_id); + crtc = drmModeGetCrtc(device->drm.fd, output->crtc->crtc_id); if (!crtc) return -1; @@ -1389,13 +1412,15 @@ drm_output_init_gamma_size(struct drm_output *output) static uint32_t drm_connector_get_possible_crtcs_mask(struct drm_connector *connector) { + struct drm_backend *backend = connector->backend; + struct drm_device *device = backend->drm; uint32_t possible_crtcs = 0; drmModeConnector *conn = connector->conn; drmModeEncoder *encoder; int i; for (i = 0; i < conn->count_encoders; i++) { - encoder = drmModeGetEncoder(connector->backend->drm.fd, + encoder = drmModeGetEncoder(device->drm.fd, conn->encoders[i]); if (!encoder) continue; @@ -1416,6 +1441,7 @@ static struct drm_crtc * drm_output_pick_crtc(struct drm_output *output) { struct drm_backend *backend; + struct drm_device *device; struct weston_head *base; struct drm_head *head; struct drm_crtc *crtc; @@ -1429,6 +1455,7 @@ drm_output_pick_crtc(struct drm_output *output) bool match; backend = to_drm_backend(output->base.compositor); + device = backend->drm; /* This algorithm ignores drmModeEncoder::possible_clones restriction, * because it is more often set wrong than not in the kernel. */ @@ -1447,7 +1474,7 @@ drm_output_pick_crtc(struct drm_output *output) /* Find a crtc that could drive each connector individually at least, * and prefer existing routings. */ - wl_list_for_each(crtc, &backend->crtc_list, link) { + wl_list_for_each(crtc, &device->crtc_list, link) { /* Could the crtc not drive each connector? */ if (!(possible_crtcs & (1 << crtc->pipe))) @@ -1510,7 +1537,7 @@ drm_output_pick_crtc(struct drm_output *output) } /* Otherwise pick any available crtc. */ - wl_list_for_each(crtc, &backend->crtc_list, link) { + wl_list_for_each(crtc, &device->crtc_list, link) { if (!crtc->output) return crtc; } @@ -1524,10 +1551,11 @@ drm_output_pick_crtc(struct drm_output *output) static struct drm_crtc * drm_crtc_create(struct drm_backend *b, uint32_t crtc_id, uint32_t pipe) { + struct drm_device *device = b->drm; struct drm_crtc *crtc; drmModeObjectPropertiesPtr props; - props = drmModeObjectGetProperties(b->drm.fd, crtc_id, + props = drmModeObjectGetProperties(device->drm.fd, crtc_id, DRM_MODE_OBJECT_CRTC); if (!props) { weston_log("failed to get CRTC properties\n"); @@ -1546,7 +1574,7 @@ drm_crtc_create(struct drm_backend *b, uint32_t crtc_id, uint32_t pipe) crtc->output = NULL; /* Add it to the last position of the DRM-backend CRTC list */ - wl_list_insert(b->crtc_list.prev, &crtc->link); + wl_list_insert(device->crtc_list.prev, &crtc->link); ret: drmModeFreeObjectProperties(props); @@ -1584,6 +1612,7 @@ drm_crtc_destroy(struct drm_crtc *crtc) static int drm_backend_create_crtc_list(struct drm_backend *b, drmModeRes *resources) { + struct drm_device *device = b->drm; struct drm_crtc *crtc, *crtc_tmp; int i; @@ -1599,7 +1628,7 @@ drm_backend_create_crtc_list(struct drm_backend *b, drmModeRes *resources) return 0; err: - wl_list_for_each_safe(crtc, crtc_tmp, &b->crtc_list, link) + wl_list_for_each_safe(crtc, crtc_tmp, &device->crtc_list, link) drm_crtc_destroy(crtc); return -1; } @@ -1612,6 +1641,7 @@ static int drm_output_init_planes(struct drm_output *output) { struct drm_backend *b = to_drm_backend(output->base.compositor); + struct drm_device *device = b->drm; output->scanout_plane = drm_output_find_special_plane(b, output, @@ -1637,7 +1667,7 @@ drm_output_init_planes(struct drm_output *output) &output->cursor_plane->base, NULL); else - b->cursors_are_broken = true; + device->cursors_are_broken = true; return 0; } @@ -1649,6 +1679,7 @@ static void drm_output_deinit_planes(struct drm_output *output) { struct drm_backend *b = to_drm_backend(output->base.compositor); + struct drm_device *device = b->drm; /* If the compositor is already shutting down, the planes have already * been destroyed. */ @@ -1660,7 +1691,7 @@ drm_output_deinit_planes(struct drm_output *output) wl_list_remove(&output->cursor_plane->base.link); wl_list_init(&output->cursor_plane->base.link); /* Turn off hardware cursor */ - drmModeSetCursor(b->drm.fd, output->crtc->crtc_id, 0, 0, 0); + drmModeSetCursor(device->drm.fd, output->crtc->crtc_id, 0, 0, 0); } /* With universal planes, the planes are allocated at startup, @@ -1682,6 +1713,7 @@ static struct weston_drm_format_array * get_scanout_formats(struct drm_backend *b) { struct weston_compositor *ec = b->compositor; + struct drm_device *device = b->drm; const struct weston_drm_format_array *renderer_formats; struct weston_drm_format_array *scanout_formats, union_planes_formats; struct drm_plane *plane; @@ -1702,7 +1734,7 @@ get_scanout_formats(struct drm_backend *b) weston_drm_format_array_init(scanout_formats); /* Compute the union of the format/modifiers of the KMS planes */ - wl_list_for_each(plane, &b->plane_list, link) { + wl_list_for_each(plane, &device->plane_list, link) { /* The scanout formats are used by the dma-buf feedback. But for * now cursor planes do not support dma-buf buffers, only wl_shm * buffers. So we skip cursor planes here. */ @@ -1771,13 +1803,14 @@ static void drm_output_detach_crtc(struct drm_output *output) { struct drm_backend *b = output->backend; + struct drm_device *device = b->drm; struct drm_crtc *crtc = output->crtc; crtc->output = NULL; output->crtc = NULL; /* Force resetting unused CRTCs */ - b->state_invalid = true; + device->state_invalid = true; } static int @@ -1847,6 +1880,7 @@ drm_output_deinit(struct weston_output *base) { struct drm_output *output = to_drm_output(base); struct drm_backend *b = to_drm_backend(base->compositor); + struct drm_device *device = b->drm; if (b->use_pixman) drm_output_fini_pixman(output); @@ -1857,7 +1891,7 @@ drm_output_deinit(struct weston_output *base) drm_output_detach_crtc(output); if (output->hdr_output_metadata_blob_id) { - drmModeDestroyPropertyBlob(b->drm.fd, + drmModeDestroyPropertyBlob(device->drm.fd, output->hdr_output_metadata_blob_id); output->hdr_output_metadata_blob_id = 0; } @@ -2003,9 +2037,11 @@ drm_head_get_current_protection(struct drm_head *head) static int drm_connector_update_properties(struct drm_connector *connector) { + struct drm_backend *backend = connector->backend; + struct drm_device *device = backend->drm; drmModeObjectProperties *props; - props = drmModeObjectGetProperties(connector->backend->drm.fd, + props = drmModeObjectGetProperties(device->drm.fd, connector->connector_id, DRM_MODE_OBJECT_CONNECTOR); if (!props) { @@ -2280,6 +2316,7 @@ drm_output_create(struct weston_compositor *compositor, const char *name) static int drm_writeback_create(struct drm_backend *b, drmModeConnector *conn) { + struct drm_device *device = b->drm; struct drm_writeback *writeback; int ret; @@ -2294,7 +2331,7 @@ drm_writeback_create(struct drm_backend *b, drmModeConnector *conn) if (ret < 0) goto err; - wl_list_insert(&b->writeback_connector_list, &writeback->link); + wl_list_insert(&device->writeback_connector_list, &writeback->link); return 0; err: @@ -2357,18 +2394,19 @@ static int drm_backend_discover_connectors(struct drm_backend *b, struct udev_device *drm_device, drmModeRes *resources) { + struct drm_device *device = b->drm; drmModeConnector *conn; int i, ret; - b->min_width = resources->min_width; - b->max_width = resources->max_width; - b->min_height = resources->min_height; - b->max_height = resources->max_height; + device->min_width = resources->min_width; + device->max_width = resources->max_width; + device->min_height = resources->min_height; + device->max_height = resources->max_height; for (i = 0; i < resources->count_connectors; i++) { uint32_t connector_id = resources->connectors[i]; - conn = drmModeGetConnector(b->drm.fd, connector_id); + conn = drmModeGetConnector(device->drm.fd, connector_id); if (!conn) continue; @@ -2394,6 +2432,7 @@ resources_has_connector(drmModeRes *resources, uint32_t connector_id) static void drm_backend_update_connectors(struct drm_backend *b, struct udev_device *drm_device) { + struct drm_device *device = b->drm; drmModeRes *resources; drmModeConnector *conn; struct weston_head *base, *base_next; @@ -2402,7 +2441,7 @@ drm_backend_update_connectors(struct drm_backend *b, struct udev_device *drm_dev uint32_t connector_id; int i, ret; - resources = drmModeGetResources(b->drm.fd); + resources = drmModeGetResources(device->drm.fd); if (!resources) { weston_log("drmModeGetResources failed\n"); return; @@ -2412,7 +2451,7 @@ drm_backend_update_connectors(struct drm_backend *b, struct udev_device *drm_dev for (i = 0; i < resources->count_connectors; i++) { connector_id = resources->connectors[i]; - conn = drmModeGetConnector(b->drm.fd, connector_id); + conn = drmModeGetConnector(device->drm.fd, connector_id); if (!conn) continue; @@ -2452,7 +2491,7 @@ drm_backend_update_connectors(struct drm_backend *b, struct udev_device *drm_dev /* Destroy writeback objects of writeback connectors that have * disappeared. */ wl_list_for_each_safe(writeback, writeback_next, - &b->writeback_connector_list, link) { + &b->drm->writeback_connector_list, link) { connector_id = writeback->connector.connector_id; if (resources_has_connector(resources, connector_id)) @@ -2513,16 +2552,17 @@ drm_backend_update_conn_props(struct drm_backend *b, } static int -udev_event_is_hotplug(struct drm_backend *b, struct udev_device *device) +udev_event_is_hotplug(struct drm_backend *b, struct udev_device *udev_device) { + struct drm_device *device = b->drm; const char *sysnum; const char *val; - sysnum = udev_device_get_sysnum(device); - if (!sysnum || atoi(sysnum) != b->drm.id) + sysnum = udev_device_get_sysnum(udev_device); + if (!sysnum || atoi(sysnum) != device->drm.id) return 0; - val = udev_device_get_property_value(device, "HOTPLUG"); + val = udev_device_get_property_value(udev_device, "HOTPLUG"); if (!val) return 0; @@ -2531,7 +2571,7 @@ udev_event_is_hotplug(struct drm_backend *b, struct udev_device *device) static int udev_event_is_conn_prop_change(struct drm_backend *b, - struct udev_device *device, + struct udev_device *udev_device, uint32_t *connector_id, uint32_t *property_id) @@ -2539,13 +2579,13 @@ udev_event_is_conn_prop_change(struct drm_backend *b, const char *val; int id; - val = udev_device_get_property_value(device, "CONNECTOR"); + val = udev_device_get_property_value(udev_device, "CONNECTOR"); if (!val || !safe_strtoint(val, &id)) return 0; else *connector_id = id; - val = udev_device_get_property_value(device, "PROPERTY"); + val = udev_device_get_property_value(udev_device, "PROPERTY"); if (!val || !safe_strtoint(val, &id)) return 0; else @@ -2579,6 +2619,7 @@ static void drm_destroy(struct weston_compositor *ec) { struct drm_backend *b = to_drm_backend(ec); + struct drm_device *device = b->drm; struct weston_head *base, *next; struct drm_crtc *crtc, *crtc_tmp; struct drm_writeback *writeback, *writeback_tmp; @@ -2596,14 +2637,14 @@ drm_destroy(struct weston_compositor *ec) b->debug = NULL; weston_compositor_shutdown(ec); - wl_list_for_each_safe(crtc, crtc_tmp, &b->crtc_list, link) + wl_list_for_each_safe(crtc, crtc_tmp, &b->drm->crtc_list, link) drm_crtc_destroy(crtc); wl_list_for_each_safe(base, next, &ec->head_list, compositor_link) drm_head_destroy(to_drm_head(base)); wl_list_for_each_safe(writeback, writeback_tmp, - &b->writeback_connector_list, link) + &b->drm->writeback_connector_list, link) drm_writeback_destroy(writeback); #ifdef BUILD_DRM_GBM @@ -2614,10 +2655,10 @@ drm_destroy(struct weston_compositor *ec) udev_monitor_unref(b->udev_monitor); udev_unref(b->udev); - weston_launcher_close(ec->launcher, b->drm.fd); + weston_launcher_close(ec->launcher, device->drm.fd); weston_launcher_destroy(ec->launcher); - free(b->drm.filename); + free(device->drm.filename); free(b); } @@ -2626,6 +2667,7 @@ session_notify(struct wl_listener *listener, void *data) { struct weston_compositor *compositor = data; struct drm_backend *b = to_drm_backend(compositor); + struct drm_device *device = b->drm; struct drm_plane *plane; struct drm_output *output; struct drm_crtc *crtc; @@ -2634,7 +2676,7 @@ session_notify(struct wl_listener *listener, void *data) weston_log("activating session\n"); weston_compositor_wake(compositor); weston_compositor_damage_all(compositor); - b->state_invalid = true; + device->state_invalid = true; udev_input_enable(&b->input); } else { weston_log("deactivating session\n"); @@ -2654,7 +2696,7 @@ session_notify(struct wl_listener *listener, void *data) crtc = output->crtc; output->base.repaint_needed = false; if (output->cursor_plane) - drmModeSetCursor(b->drm.fd, crtc->crtc_id, + drmModeSetCursor(device->drm.fd, crtc->crtc_id, 0, 0, 0); } @@ -2662,10 +2704,10 @@ session_notify(struct wl_listener *listener, void *data) struct drm_output, base.link); crtc = output->crtc; - wl_list_for_each(plane, &b->plane_list, link) { + wl_list_for_each(plane, &device->plane_list, link) { if (plane->type != WDRM_PLANE_TYPE_OVERLAY) continue; - drmModeSetPlane(b->drm.fd, plane->plane_id, crtc->crtc_id, + drmModeSetPlane(device->drm.fd, plane->plane_id, crtc->crtc_id, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); } } @@ -2679,16 +2721,17 @@ session_notify(struct wl_listener *listener, void *data) * the compositor session. * * @param compositor The compositor instance. - * @param device The device being added/removed. + * @param devnum The device being added/removed. * @param added Whether the device is being added (or removed) */ static void drm_device_changed(struct weston_compositor *compositor, - dev_t device, bool added) + dev_t devnum, bool added) { struct drm_backend *b = to_drm_backend(compositor); + struct drm_device *device = b->drm; - if (b->drm.fd < 0 || b->drm.devnum != device || + if (device->drm.fd < 0 || device->drm.devnum != devnum || compositor->session_active == added) return; @@ -2701,18 +2744,20 @@ drm_device_changed(struct weston_compositor *compositor, * sets b->drm.fd and b->drm.filename to the opened device. */ static bool -drm_device_is_kms(struct drm_backend *b, struct udev_device *device) +drm_device_is_kms(struct drm_backend *b, struct udev_device *udev_device) { - const char *filename = udev_device_get_devnode(device); - const char *sysnum = udev_device_get_sysnum(device); - dev_t devnum = udev_device_get_devnum(device); + struct drm_device *device = b->drm; + struct weston_compositor *compositor = b->compositor; + const char *filename = udev_device_get_devnode(udev_device); + const char *sysnum = udev_device_get_sysnum(udev_device); + dev_t devnum = udev_device_get_devnum(udev_device); drmModeRes *res; int id = -1, fd; if (!filename) return false; - fd = weston_launcher_open(b->compositor->launcher, filename, O_RDWR); + fd = weston_launcher_open(compositor->launcher, filename, O_RDWR); if (fd < 0) return false; @@ -2733,14 +2778,14 @@ drm_device_is_kms(struct drm_backend *b, struct udev_device *device) /* We can be called successfully on multiple devices; if we have, * clean up old entries. */ - if (b->drm.fd >= 0) - weston_launcher_close(b->compositor->launcher, b->drm.fd); - free(b->drm.filename); + if (device->drm.fd >= 0) + weston_launcher_close(compositor->launcher, device->drm.fd); + free(device->drm.filename); - b->drm.fd = fd; - b->drm.id = id; - b->drm.filename = strdup(filename); - b->drm.devnum = devnum; + device->drm.fd = fd; + device->drm.id = id; + device->drm.filename = strdup(filename); + device->drm.devnum = devnum; drmModeFreeResources(res); @@ -2766,10 +2811,11 @@ out_fd: static struct udev_device* find_primary_gpu(struct drm_backend *b, const char *seat) { + struct drm_device *device = b->drm; struct udev_enumerate *e; struct udev_list_entry *entry; const char *path, *device_seat, *id; - struct udev_device *device, *drm_device, *pci; + struct udev_device *dev, *drm_device, *pci; e = udev_enumerate_new(b->udev); udev_enumerate_add_match_subsystem(e, "drm"); @@ -2781,18 +2827,18 @@ find_primary_gpu(struct drm_backend *b, const char *seat) bool is_boot_vga = false; path = udev_list_entry_get_name(entry); - device = udev_device_new_from_syspath(b->udev, path); - if (!device) + dev = udev_device_new_from_syspath(b->udev, path); + if (!dev) continue; - device_seat = udev_device_get_property_value(device, "ID_SEAT"); + device_seat = udev_device_get_property_value(dev, "ID_SEAT"); if (!device_seat) device_seat = default_seat; if (strcmp(device_seat, seat)) { - udev_device_unref(device); + udev_device_unref(dev); continue; } - pci = udev_device_get_parent_with_subsystem_devtype(device, + pci = udev_device_get_parent_with_subsystem_devtype(dev, "pci", NULL); if (pci) { id = udev_device_get_sysattr_value(pci, "boot_vga"); @@ -2804,15 +2850,15 @@ find_primary_gpu(struct drm_backend *b, const char *seat) * device isn't our boot-VGA device, we aren't going to use * it. */ if (!is_boot_vga && drm_device) { - udev_device_unref(device); + udev_device_unref(dev); continue; } /* Make sure this device is actually capable of modesetting; - * if this call succeeds, b->drm.{fd,filename} will be set, + * if this call succeeds, device->drm.{fd,filename} will be set, * and any old values freed. */ - if (!drm_device_is_kms(b, device)) { - udev_device_unref(device); + if (!drm_device_is_kms(b, dev)) { + udev_device_unref(dev); continue; } @@ -2821,7 +2867,7 @@ find_primary_gpu(struct drm_backend *b, const char *seat) if (is_boot_vga) { if (drm_device) udev_device_unref(drm_device); - drm_device = device; + drm_device = dev; break; } @@ -2829,12 +2875,12 @@ find_primary_gpu(struct drm_backend *b, const char *seat) * trump existing saved devices with boot-VGA devices, so if * we end up here, this must be the first device we've seen. */ assert(!drm_device); - drm_device = device; + drm_device = dev; } /* If we're returning a device to use, we must have an open FD for * it. */ - assert(!!drm_device == (b->drm.fd >= 0)); + assert(!!drm_device == (device->drm.fd >= 0)); udev_enumerate_unref(e); return drm_device; @@ -2843,25 +2889,26 @@ find_primary_gpu(struct drm_backend *b, const char *seat) static struct udev_device * open_specific_drm_device(struct drm_backend *b, const char *name) { - struct udev_device *device; + struct drm_device *device = b->drm; + struct udev_device *udev_device; - device = udev_device_new_from_subsystem_sysname(b->udev, "drm", name); - if (!device) { + udev_device = udev_device_new_from_subsystem_sysname(b->udev, "drm", name); + if (!udev_device) { weston_log("ERROR: could not open DRM device '%s'\n", name); return NULL; } - if (!drm_device_is_kms(b, device)) { - udev_device_unref(device); + if (!drm_device_is_kms(b, udev_device)) { + udev_device_unref(udev_device); weston_log("ERROR: DRM device '%s' is not a KMS device.\n", name); return NULL; } /* If we're returning a device to use, we must have an open FD for * it. */ - assert(b->drm.fd >= 0); + assert(device->drm.fd >= 0); - return device; + return udev_device; } static void @@ -2869,15 +2916,16 @@ planes_binding(struct weston_keyboard *keyboard, const struct timespec *time, uint32_t key, void *data) { struct drm_backend *b = data; + struct drm_device *device = b->drm; switch (key) { case KEY_C: - b->cursors_are_broken ^= true; + device->cursors_are_broken ^= true; break; case KEY_V: /* We don't support overlay-plane usage with legacy KMS. */ - if (b->atomic_modeset) - b->sprites_are_broken ^= true; + if (device->atomic_modeset) + device->sprites_are_broken ^= true; break; default: break; @@ -2901,17 +2949,19 @@ static void recorder_frame_notify(struct wl_listener *listener, void *data) { struct drm_output *output; + struct drm_device *device; struct drm_backend *b; int fd, ret; output = container_of(listener, struct drm_output, recorder_frame_listener); b = to_drm_backend(output->base.compositor); + device = b->drm; if (!output->recorder) return; - ret = drmPrimeHandleToFD(b->drm.fd, + ret = drmPrimeHandleToFD(device->drm.fd, output->scanout_plane->state_cur->fb->handles[0], DRM_CLOEXEC, &fd); if (ret) { @@ -2932,15 +2982,16 @@ static void * create_recorder(struct drm_backend *b, int width, int height, const char *filename) { + struct drm_device *device = b->drm; int fd; drm_magic_t magic; - fd = open(b->drm.filename, O_RDWR | O_CLOEXEC); + fd = open(device->drm.filename, O_RDWR | O_CLOEXEC); if (fd < 0) return NULL; drmGetMagic(fd, &magic); - drmAuthMagic(b->drm.fd, magic); + drmAuthMagic(device->drm.fd, magic); return vaapi_recorder_create(fd, width, height, filename); } @@ -3007,6 +3058,7 @@ drm_backend_create(struct weston_compositor *compositor, struct weston_drm_backend_config *config) { struct drm_backend *b; + struct drm_device *device; struct udev_device *drm_device; struct wl_event_loop *loop; const char *seat_id = default_seat; @@ -3028,8 +3080,14 @@ drm_backend_create(struct weston_compositor *compositor, if (b == NULL) return NULL; - b->state_invalid = true; - b->drm.fd = -1; + device = zalloc(sizeof *device); + if (device == NULL) + return NULL; + device->state_invalid = true; + device->drm.fd = -1; + device->backend = b; + + b->drm = device; b->compositor = compositor; b->use_pixman = config->use_pixman; @@ -3098,19 +3156,19 @@ drm_backend_create(struct weston_compositor *compositor, weston_setup_vt_switch_bindings(compositor); - res = drmModeGetResources(b->drm.fd); + res = drmModeGetResources(b->drm->drm.fd); if (!res) { weston_log("Failed to get drmModeRes\n"); goto err_udev_dev; } - wl_list_init(&b->crtc_list); + wl_list_init(&b->drm->crtc_list); if (drm_backend_create_crtc_list(b, res) == -1) { weston_log("Failed to create CRTC list for DRM-backend\n"); goto err_create_crtc_list; } - wl_list_init(&b->plane_list); + wl_list_init(&device->plane_list); create_sprites(b); if (udev_input_init(&b->input, @@ -3120,9 +3178,9 @@ drm_backend_create(struct weston_compositor *compositor, goto err_sprite; } - wl_list_init(&b->writeback_connector_list); + wl_list_init(&b->drm->writeback_connector_list); if (drm_backend_discover_connectors(b, drm_device, res) < 0) { - weston_log("Failed to create heads for %s\n", b->drm.filename); + weston_log("Failed to create heads for %s\n", b->drm->drm.filename); goto err_udev_input; } @@ -3133,13 +3191,13 @@ drm_backend_create(struct weston_compositor *compositor, /* A this point we have some idea of whether or not we have a working * cursor plane. */ - if (!b->cursors_are_broken) + if (!device->cursors_are_broken) compositor->capabilities |= WESTON_CAP_CURSOR_PLANE; loop = wl_display_get_event_loop(compositor->wl_display); b->drm_source = - wl_event_loop_add_fd(loop, b->drm.fd, - WL_EVENT_READABLE, on_drm_input, b); + wl_event_loop_add_fd(loop, b->drm->drm.fd, + WL_EVENT_READABLE, on_drm_input, b->drm); b->udev_monitor = udev_monitor_new_from_netlink(b->udev, "udev"); if (b->udev_monitor == NULL) { @@ -3202,7 +3260,7 @@ drm_backend_create(struct weston_compositor *compositor, " synchronization support failed.\n"); } - if (b->atomic_modeset) + if (device->atomic_modeset) if (weston_compositor_enable_content_protection(compositor) < 0) weston_log("Error: initializing content-protection " "support failed.\n"); diff --git a/libweston/backend-drm/fb.c b/libweston/backend-drm/fb.c index 1d518df3..4fa06ce8 100644 --- a/libweston/backend-drm/fb.c +++ b/libweston/backend-drm/fb.c @@ -71,13 +71,14 @@ drm_fb_destroy_dumb(struct drm_fb *fb) static int drm_fb_addfb(struct drm_backend *b, struct drm_fb *fb) { + struct drm_device *device = b->drm; int ret = -EINVAL; uint64_t mods[4] = { }; size_t i; /* If we have a modifier set, we must only use the WithModifiers * entrypoint; we cannot import it through legacy ioctls. */ - if (b->fb_modifiers && fb->modifier != DRM_FORMAT_MOD_INVALID) { + if (device->fb_modifiers && fb->modifier != DRM_FORMAT_MOD_INVALID) { /* KMS demands that if a modifier is set, it must be the same * for all planes. */ for (i = 0; i < ARRAY_LENGTH(mods) && fb->handles[i]; i++) @@ -115,6 +116,7 @@ struct drm_fb * drm_fb_create_dumb(struct drm_backend *b, int width, int height, uint32_t format) { + struct drm_device *device = b->drm; struct drm_fb *fb; int ret; @@ -145,7 +147,7 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height, create_arg.width = width; create_arg.height = height; - ret = drmIoctl(b->drm.fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_arg); + ret = drmIoctl(device->drm.fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_arg); if (ret) goto err_fb; @@ -157,7 +159,7 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height, fb->size = create_arg.size; fb->width = width; fb->height = height; - fb->fd = b->drm.fd; + fb->fd = device->drm.fd; if (drm_fb_addfb(b, fb) != 0) { weston_log("failed to create kms fb: %s\n", strerror(errno)); @@ -171,18 +173,18 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height, goto err_add_fb; fb->map = mmap(NULL, fb->size, PROT_WRITE, - MAP_SHARED, b->drm.fd, map_arg.offset); + MAP_SHARED, device->drm.fd, map_arg.offset); if (fb->map == MAP_FAILED) goto err_add_fb; return fb; err_add_fb: - drmModeRmFB(b->drm.fd, fb->fb_id); + drmModeRmFB(device->drm.fd, fb->fb_id); err_bo: memset(&destroy_arg, 0, sizeof(destroy_arg)); destroy_arg.handle = create_arg.handle; - drmIoctl(b->drm.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg); + drmIoctl(device->drm.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg); err_fb: free(fb); return NULL; @@ -227,6 +229,7 @@ drm_fb_get_from_dmabuf(struct linux_dmabuf_buffer *dmabuf, * of GBM_BO_IMPORT_FD_MODIFIER. */ return NULL; #else + struct drm_device *device = backend->drm; struct drm_fb *fb; int i; struct gbm_import_fd_modifier_data import_mod = { @@ -287,7 +290,7 @@ drm_fb_get_from_dmabuf(struct linux_dmabuf_buffer *dmabuf, fb->height = dmabuf->attributes.height; fb->modifier = dmabuf->attributes.modifier[0]; fb->size = 0; - fb->fd = backend->drm.fd; + fb->fd = device->drm.fd; ARRAY_COPY(fb->strides, dmabuf->attributes.stride); ARRAY_COPY(fb->offsets, dmabuf->attributes.offset); @@ -302,10 +305,10 @@ drm_fb_get_from_dmabuf(struct linux_dmabuf_buffer *dmabuf, if (is_opaque) fb->format = pixel_format_get_opaque_substitute(fb->format); - if (backend->min_width > fb->width || - fb->width > backend->max_width || - backend->min_height > fb->height || - fb->height > backend->max_height) { + if (device->min_width > fb->width || + fb->width > device->max_width || + device->min_height > fb->height || + fb->height > device->max_height) { weston_log("bo geometry out of bounds\n"); goto err_free; } @@ -342,6 +345,7 @@ struct drm_fb * drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend, bool is_opaque, enum drm_fb_type type) { + struct drm_device *device = backend->drm; struct drm_fb *fb = gbm_bo_get_user_data(bo); #ifdef HAVE_GBM_MODIFIERS int i; @@ -359,7 +363,7 @@ drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend, fb->type = type; fb->refcnt = 1; fb->bo = bo; - fb->fd = backend->drm.fd; + fb->fd = device->drm.fd; fb->width = gbm_bo_get_width(bo); fb->height = gbm_bo_get_height(bo); @@ -392,10 +396,10 @@ drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend, if (is_opaque) fb->format = pixel_format_get_opaque_substitute(fb->format); - if (backend->min_width > fb->width || - fb->width > backend->max_width || - backend->min_height > fb->height || - fb->height > backend->max_height) { + if (device->min_width > fb->width || + fb->width > device->max_width || + device->min_height > fb->height || + fb->height > device->max_height) { weston_log("bo geometry out of bounds\n"); goto err_free; } @@ -529,6 +533,7 @@ drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev, { struct drm_output *output = state->output; struct drm_backend *b = to_drm_backend(output->base.compositor); + struct drm_device *device = b->drm; struct weston_buffer *buffer = ev->surface->buffer_ref.buffer; struct drm_buffer_fb *buf_fb; bool is_opaque = weston_view_is_opaque(ev, &ev->transform.boundingbox); @@ -603,7 +608,7 @@ drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev, /* Check if this buffer can ever go on any planes. If it can't, we have * no reason to ever have a drm_fb, so we fail it here. */ - wl_list_for_each(plane, &b->plane_list, link) { + wl_list_for_each(plane, &device->plane_list, link) { /* only SHM buffers can go into cursor planes */ if (plane->type == WDRM_PLANE_TYPE_CURSOR) continue; diff --git a/libweston/backend-drm/kms-color.c b/libweston/backend-drm/kms-color.c index 476480d5..55925665 100644 --- a/libweston/backend-drm/kms-color.c +++ b/libweston/backend-drm/kms-color.c @@ -108,6 +108,7 @@ weston_hdr_metadata_type1_to_kms(struct hdr_metadata_infoframe *dst, int drm_output_ensure_hdr_output_metadata_blob(struct drm_output *output) { + struct drm_device *device = output->backend->drm; const struct weston_hdr_metadata_type1 *src; struct hdr_output_metadata meta; uint32_t blob_id = 0; @@ -160,7 +161,7 @@ drm_output_ensure_hdr_output_metadata_blob(struct drm_output *output) return -1; } - ret = drmModeCreatePropertyBlob(output->backend->drm.fd, + ret = drmModeCreatePropertyBlob(device->drm.fd, &meta, sizeof meta, &blob_id); if (ret != 0) { weston_log("Error: failed to create KMS blob for HDR metadata on output '%s': %s\n", @@ -168,7 +169,7 @@ drm_output_ensure_hdr_output_metadata_blob(struct drm_output *output) return -1; } - drmModeDestroyPropertyBlob(output->backend->drm.fd, + drmModeDestroyPropertyBlob(device->drm.fd, output->hdr_output_metadata_blob_id); output->hdr_output_metadata_blob_id = blob_id; diff --git a/libweston/backend-drm/kms.c b/libweston/backend-drm/kms.c index b84eb881..2b159586 100644 --- a/libweston/backend-drm/kms.c +++ b/libweston/backend-drm/kms.c @@ -288,6 +288,7 @@ drm_property_info_populate(struct drm_backend *b, unsigned int num_infos, drmModeObjectProperties *props) { + struct drm_device *device = b->drm; drmModePropertyRes *prop; unsigned i, j; @@ -314,7 +315,7 @@ drm_property_info_populate(struct drm_backend *b, for (i = 0; i < props->count_props; i++) { unsigned int k; - prop = drmModeGetProperty(b->drm.fd, props->props[i]); + prop = drmModeGetProperty(device->drm.fd, props->props[i]); if (!prop) continue; @@ -436,6 +437,8 @@ drm_plane_populate_formats(struct drm_plane *plane, const drmModePlane *kplane, const drmModeObjectProperties *props, const bool use_modifiers) { + struct drm_backend *backend = plane->backend; + struct drm_device *device = backend->drm; unsigned i, j; drmModePropertyBlobRes *blob = NULL; struct drm_format_modifier_blob *fmt_mod_blob; @@ -454,7 +457,7 @@ drm_plane_populate_formats(struct drm_plane *plane, const drmModePlane *kplane, if (blob_id == 0) goto fallback; - blob = drmModeGetPropertyBlob(plane->backend->drm.fd, blob_id); + blob = drmModeGetPropertyBlob(device->drm.fd, blob_id); if (!blob) goto fallback; @@ -515,12 +518,13 @@ drm_output_set_gamma(struct weston_output *output_base, struct drm_output *output = to_drm_output(output_base); struct drm_backend *backend = to_drm_backend(output->base.compositor); + struct drm_device *device = backend->drm; /* check */ if (output_base->gamma_size != size) return; - rc = drmModeCrtcSetGamma(backend->drm.fd, + rc = drmModeCrtcSetGamma(device->drm.fd, output->crtc->crtc_id, size, r, g, b); if (rc) @@ -539,6 +543,7 @@ drm_output_assign_state(struct drm_output_state *state, { struct drm_output *output = state->output; struct drm_backend *b = to_drm_backend(output->base.compositor); + struct drm_device *device = b->drm; struct drm_plane_state *plane_state; struct drm_head *head; @@ -555,13 +560,13 @@ drm_output_assign_state(struct drm_output_state *state, output->state_cur = state; - if (b->atomic_modeset && mode == DRM_STATE_APPLY_ASYNC) { + if (device->atomic_modeset && mode == DRM_STATE_APPLY_ASYNC) { drm_debug(b, "\t[CRTC:%u] setting pending flip\n", output->crtc->crtc_id); output->atomic_complete_pending = true; } - if (b->atomic_modeset && + if (device->atomic_modeset && state->protection == WESTON_HDCP_DISABLE) wl_list_for_each(head, &output->base.head_list, base.output_link) weston_head_set_content_protection_status(&head->base, @@ -583,7 +588,7 @@ drm_output_assign_state(struct drm_output_state *state, continue; } - if (b->atomic_modeset) + if (device->atomic_modeset) continue; assert(plane->type != WDRM_PLANE_TYPE_OVERLAY); @@ -597,6 +602,7 @@ drm_output_set_cursor(struct drm_output_state *output_state) { struct drm_output *output = output_state->output; struct drm_backend *b = to_drm_backend(output->base.compositor); + struct drm_device *device = b->drm; struct drm_crtc *crtc = output->crtc; struct drm_plane *plane = output->cursor_plane; struct drm_plane_state *state; @@ -612,7 +618,7 @@ drm_output_set_cursor(struct drm_output_state *output_state) if (!state->fb) { pixman_region32_fini(&plane->base.damage); pixman_region32_init(&plane->base.damage); - drmModeSetCursor(b->drm.fd, crtc->crtc_id, 0, 0, 0); + drmModeSetCursor(device->drm.fd, crtc->crtc_id, 0, 0, 0); return; } @@ -621,8 +627,8 @@ drm_output_set_cursor(struct drm_output_state *output_state) handle = output->gbm_cursor_handle[output->current_cursor]; if (plane->state_cur->fb != state->fb) { - if (drmModeSetCursor(b->drm.fd, crtc->crtc_id, handle, - b->cursor_width, b->cursor_height)) { + if (drmModeSetCursor(device->drm.fd, crtc->crtc_id, handle, + device->cursor_width, device->cursor_height)) { weston_log("failed to set cursor: %s\n", strerror(errno)); goto err; @@ -632,7 +638,7 @@ drm_output_set_cursor(struct drm_output_state *output_state) pixman_region32_fini(&plane->base.damage); pixman_region32_init(&plane->base.damage); - if (drmModeMoveCursor(b->drm.fd, crtc->crtc_id, + if (drmModeMoveCursor(device->drm.fd, crtc->crtc_id, state->dest_x, state->dest_y)) { weston_log("failed to move cursor: %s\n", strerror(errno)); goto err; @@ -641,8 +647,8 @@ drm_output_set_cursor(struct drm_output_state *output_state) return; err: - b->cursors_are_broken = true; - drmModeSetCursor(b->drm.fd, crtc->crtc_id, 0, 0, 0); + device->cursors_are_broken = true; + drmModeSetCursor(device->drm.fd, crtc->crtc_id, 0, 0, 0); } static int @@ -650,6 +656,7 @@ drm_output_apply_state_legacy(struct drm_output_state *state) { struct drm_output *output = state->output; struct drm_backend *backend = to_drm_backend(output->base.compositor); + struct drm_device *device = backend->drm; struct drm_plane *scanout_plane = output->scanout_plane; struct drm_crtc *crtc = output->crtc; struct drm_property_info *dpms_prop; @@ -681,14 +688,14 @@ drm_output_apply_state_legacy(struct drm_output_state *state) if (state->dpms != WESTON_DPMS_ON) { if (output->cursor_plane) { - ret = drmModeSetCursor(backend->drm.fd, crtc->crtc_id, + ret = drmModeSetCursor(device->drm.fd, crtc->crtc_id, 0, 0, 0); if (ret) weston_log("drmModeSetCursor failed disable: %s\n", strerror(errno)); } - ret = drmModeSetCrtc(backend->drm.fd, crtc->crtc_id, 0, 0, 0, + ret = drmModeSetCrtc(device->drm.fd, crtc->crtc_id, 0, 0, 0, NULL, 0, NULL); if (ret) weston_log("drmModeSetCrtc failed disabling: %s\n", @@ -722,12 +729,12 @@ drm_output_apply_state_legacy(struct drm_output_state *state) assert(scanout_state->in_fence_fd == -1); mode = to_drm_mode(output->base.current_mode); - if (backend->state_invalid || + if (device->state_invalid || !scanout_plane->state_cur->fb || scanout_plane->state_cur->fb->strides[0] != scanout_state->fb->strides[0]) { - ret = drmModeSetCrtc(backend->drm.fd, crtc->crtc_id, + ret = drmModeSetCrtc(device->drm.fd, crtc->crtc_id, scanout_state->fb->fb_id, 0, 0, connectors, n_conn, @@ -743,7 +750,7 @@ drm_output_apply_state_legacy(struct drm_output_state *state) crtc->crtc_id, scanout_state->plane->plane_id, pinfo ? pinfo->drm_format_name : "UNKNOWN"); - if (drmModePageFlip(backend->drm.fd, crtc->crtc_id, + if (drmModePageFlip(device->drm.fd, crtc->crtc_id, scanout_state->fb->fb_id, DRM_MODE_PAGE_FLIP_EVENT, output) < 0) { weston_log("queueing pageflip failed: %s\n", strerror(errno)); @@ -764,7 +771,7 @@ drm_output_apply_state_legacy(struct drm_output_state *state) if (dpms_prop->prop_id == 0) continue; - ret = drmModeConnectorSetProperty(backend->drm.fd, + ret = drmModeConnectorSetProperty(device->drm.fd, head->connector.connector_id, dpms_prop->prop_id, state->dpms); @@ -1059,6 +1066,7 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state, enum drm_state_apply_mode mode) { struct drm_backend *b = pending_state->backend; + struct drm_device *device = b->drm; struct drm_output_state *output_state, *tmp; struct drm_plane *plane; drmModeAtomicReq *req = drmModeAtomicAlloc(); @@ -1080,7 +1088,7 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state, break; } - if (b->state_invalid) { + if (device->state_invalid) { struct weston_head *head_base; struct drm_head *head; struct drm_crtc *crtc; @@ -1117,7 +1125,7 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state, ret = -1; } - wl_list_for_each(crtc, &b->crtc_list, link) { + wl_list_for_each(crtc, &device->crtc_list, link) { struct drm_property_info *info; drmModeObjectProperties *props; uint64_t active; @@ -1130,7 +1138,7 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state, * off, as the kernel will refuse to generate an event * for an off->off state and fail the commit. */ - props = drmModeObjectGetProperties(b->drm.fd, + props = drmModeObjectGetProperties(device->drm.fd, crtc->crtc_id, DRM_MODE_OBJECT_CRTC); if (!props) { @@ -1153,7 +1161,7 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state, /* Disable all the planes; planes which are being used will * override this state in the output-state application. */ - wl_list_for_each(plane, &b->plane_list, link) { + wl_list_for_each(plane, &device->plane_list, link) { drm_debug(b, "\t\t[atomic] starting with plane %lu disabled\n", (unsigned long) plane->plane_id); plane_add_prop(req, plane, WDRM_PLANE_CRTC_ID, 0); @@ -1176,7 +1184,7 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state, goto out; } - ret = drmModeAtomicCommit(b->drm.fd, req, flags, b); + ret = drmModeAtomicCommit(device->drm.fd, req, flags, b); drm_debug(b, "[atomic] drmModeAtomicCommit\n"); /* Test commits do not take ownership of the state; return @@ -1196,7 +1204,7 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state, link) drm_output_assign_state(output_state, mode); - b->state_invalid = false; + device->state_invalid = false; assert(wl_list_empty(&pending_state->output_list)); @@ -1228,8 +1236,9 @@ int drm_pending_state_test(struct drm_pending_state *pending_state) { struct drm_backend *b = pending_state->backend; + struct drm_device *device = b->drm; - if (b->atomic_modeset) + if (device->atomic_modeset) return drm_pending_state_apply_atomic(pending_state, DRM_STATE_TEST_ONLY); @@ -1249,23 +1258,24 @@ int drm_pending_state_apply(struct drm_pending_state *pending_state) { struct drm_backend *b = pending_state->backend; + struct drm_device *device = b->drm; struct drm_output_state *output_state, *tmp; struct drm_crtc *crtc; - if (b->atomic_modeset) + if (device->atomic_modeset) return drm_pending_state_apply_atomic(pending_state, DRM_STATE_APPLY_ASYNC); - if (b->state_invalid) { + if (device->state_invalid) { /* If we need to reset all our state (e.g. because we've * just started, or just been VT-switched in), explicitly * disable all the CRTCs we aren't using. This also disables * all connectors on these CRTCs, so we don't need to do that * separately with the pre-atomic API. */ - wl_list_for_each(crtc, &b->crtc_list, link) { + wl_list_for_each(crtc, &device->crtc_list, link) { if (crtc->output) continue; - drmModeSetCrtc(b->drm.fd, crtc->crtc_id, 0, 0, 0, + drmModeSetCrtc(device->drm.fd, crtc->crtc_id, 0, 0, 0, NULL, 0, NULL); } } @@ -1288,7 +1298,7 @@ drm_pending_state_apply(struct drm_pending_state *pending_state) weston_output_repaint_failed(&output->base); drm_output_state_free(output->state_cur); output->state_cur = drm_output_state_alloc(output, NULL); - b->state_invalid = true; + device->state_invalid = true; if (!b->use_pixman) { drm_output_fini_egl(output); drm_output_init_egl(output, b); @@ -1296,7 +1306,7 @@ drm_pending_state_apply(struct drm_pending_state *pending_state) } } - b->state_invalid = false; + device->state_invalid = false; assert(wl_list_empty(&pending_state->output_list)); @@ -1315,24 +1325,25 @@ drm_pending_state_apply(struct drm_pending_state *pending_state) int drm_pending_state_apply_sync(struct drm_pending_state *pending_state) { - struct drm_backend *b = pending_state->backend; + struct drm_backend *backend = pending_state->backend; + struct drm_device *device = backend->drm; struct drm_output_state *output_state, *tmp; struct drm_crtc *crtc; - if (b->atomic_modeset) + if (device->atomic_modeset) return drm_pending_state_apply_atomic(pending_state, DRM_STATE_APPLY_SYNC); - if (b->state_invalid) { + if (device->state_invalid) { /* If we need to reset all our state (e.g. because we've * just started, or just been VT-switched in), explicitly * disable all the CRTCs we aren't using. This also disables * all connectors on these CRTCs, so we don't need to do that * separately with the pre-atomic API. */ - wl_list_for_each(crtc, &b->crtc_list, link) { + wl_list_for_each(crtc, &device->crtc_list, link) { if (crtc->output) continue; - drmModeSetCrtc(b->drm.fd, crtc->crtc_id, 0, 0, 0, + drmModeSetCrtc(device->drm.fd, crtc->crtc_id, 0, 0, 0, NULL, 0, NULL); } } @@ -1349,7 +1360,7 @@ drm_pending_state_apply_sync(struct drm_pending_state *pending_state) } } - b->state_invalid = false; + device->state_invalid = false; assert(wl_list_empty(&pending_state->output_list)); @@ -1375,13 +1386,14 @@ page_flip_handler(int fd, unsigned int frame, { struct drm_output *output = data; struct drm_backend *b = to_drm_backend(output->base.compositor); + struct drm_device *device = b->drm; uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_VSYNC | WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION | WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK; drm_output_update_msc(output, frame); - assert(!b->atomic_modeset); + assert(!device->atomic_modeset); assert(output->page_flip_pending); output->page_flip_pending = false; @@ -1393,6 +1405,7 @@ atomic_flip_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, unsigned int crtc_id, void *data) { struct drm_backend *b = data; + struct drm_device *device = b->drm; struct drm_crtc *crtc; struct drm_output *output; uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_VSYNC | @@ -1413,7 +1426,7 @@ atomic_flip_handler(int fd, unsigned int frame, unsigned int sec, drm_output_update_msc(output, frame); drm_debug(b, "[atomic][CRTC:%u] flip processing started\n", crtc_id); - assert(b->atomic_modeset); + assert(device->atomic_modeset); assert(output->atomic_complete_pending); output->atomic_complete_pending = false; @@ -1424,12 +1437,12 @@ atomic_flip_handler(int fd, unsigned int frame, unsigned int sec, int on_drm_input(int fd, uint32_t mask, void *data) { - struct drm_backend *b = data; + struct drm_device *device = data; drmEventContext evctx; memset(&evctx, 0, sizeof evctx); evctx.version = 3; - if (b->atomic_modeset) + if (device->atomic_modeset) evctx.page_flip_handler2 = atomic_flip_handler; else evctx.page_flip_handler = page_flip_handler; @@ -1441,12 +1454,13 @@ on_drm_input(int fd, uint32_t mask, void *data) int init_kms_caps(struct drm_backend *b) { + struct drm_device *device = b->drm; uint64_t cap; int ret; - weston_log("using %s\n", b->drm.filename); + weston_log("using %s\n", device->drm.filename); - ret = drmGetCap(b->drm.fd, DRM_CAP_TIMESTAMP_MONOTONIC, &cap); + ret = drmGetCap(device->drm.fd, DRM_CAP_TIMESTAMP_MONOTONIC, &cap); if (ret != 0 || cap != 1) { weston_log("Error: kernel DRM KMS does not support DRM_CAP_TIMESTAMP_MONOTONIC.\n"); return -1; @@ -1457,43 +1471,43 @@ init_kms_caps(struct drm_backend *b) return -1; } - ret = drmGetCap(b->drm.fd, DRM_CAP_CURSOR_WIDTH, &cap); + ret = drmGetCap(device->drm.fd, DRM_CAP_CURSOR_WIDTH, &cap); if (ret == 0) - b->cursor_width = cap; + device->cursor_width = cap; else - b->cursor_width = 64; + device->cursor_width = 64; - ret = drmGetCap(b->drm.fd, DRM_CAP_CURSOR_HEIGHT, &cap); + ret = drmGetCap(device->drm.fd, DRM_CAP_CURSOR_HEIGHT, &cap); if (ret == 0) - b->cursor_height = cap; + device->cursor_height = cap; else - b->cursor_height = 64; + device->cursor_height = 64; - ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); + ret = drmSetClientCap(device->drm.fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); if (ret) { weston_log("Error: drm card doesn't support universal planes!\n"); return -1; } if (!getenv("WESTON_DISABLE_ATOMIC")) { - ret = drmGetCap(b->drm.fd, DRM_CAP_CRTC_IN_VBLANK_EVENT, &cap); + ret = drmGetCap(device->drm.fd, DRM_CAP_CRTC_IN_VBLANK_EVENT, &cap); if (ret != 0) cap = 0; - ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_ATOMIC, 1); - b->atomic_modeset = ((ret == 0) && (cap == 1)); + ret = drmSetClientCap(device->drm.fd, DRM_CLIENT_CAP_ATOMIC, 1); + device->atomic_modeset = ((ret == 0) && (cap == 1)); } weston_log("DRM: %s atomic modesetting\n", - b->atomic_modeset ? "supports" : "does not support"); + device->atomic_modeset ? "supports" : "does not support"); if (!getenv("WESTON_DISABLE_GBM_MODIFIERS")) { - ret = drmGetCap(b->drm.fd, DRM_CAP_ADDFB2_MODIFIERS, &cap); + ret = drmGetCap(device->drm.fd, DRM_CAP_ADDFB2_MODIFIERS, &cap); if (ret == 0) - b->fb_modifiers = cap; + device->fb_modifiers = cap; } weston_log("DRM: %s GBM modifiers\n", - b->fb_modifiers ? "supports" : "does not support"); + device->fb_modifiers ? "supports" : "does not support"); - drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_WRITEBACK_CONNECTORS, 1); + drmSetClientCap(device->drm.fd, DRM_CLIENT_CAP_WRITEBACK_CONNECTORS, 1); /* * KMS support for hardware planes cannot properly synchronize @@ -1503,13 +1517,13 @@ init_kms_caps(struct drm_backend *b) * to a fraction. For cursors, it's not so bad, so they are * enabled. */ - if (!b->atomic_modeset || getenv("WESTON_FORCE_RENDERER")) - b->sprites_are_broken = true; + if (!device->atomic_modeset || getenv("WESTON_FORCE_RENDERER")) + device->sprites_are_broken = true; - ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_ASPECT_RATIO, 1); - b->aspect_ratio_supported = (ret == 0); + ret = drmSetClientCap(device->drm.fd, DRM_CLIENT_CAP_ASPECT_RATIO, 1); + device->aspect_ratio_supported = (ret == 0); weston_log("DRM: %s picture aspect ratio\n", - b->aspect_ratio_supported ? "supports" : "does not support"); + device->aspect_ratio_supported ? "supports" : "does not support"); return 0; } diff --git a/libweston/backend-drm/modes.c b/libweston/backend-drm/modes.c index f6b4248f..a228572b 100644 --- a/libweston/backend-drm/modes.c +++ b/libweston/backend-drm/modes.c @@ -100,12 +100,13 @@ drm_subpixel_to_wayland(int drm_value) int drm_mode_ensure_blob(struct drm_backend *backend, struct drm_mode *mode) { + struct drm_device *device = backend->drm; int ret; if (mode->blob_id) return 0; - ret = drmModeCreatePropertyBlob(backend->drm.fd, + ret = drmModeCreatePropertyBlob(device->drm.fd, &mode->mode_info, sizeof(mode->mode_info), &mode->blob_id); @@ -320,6 +321,8 @@ find_and_parse_output_edid(struct drm_head *head, const char **serial_number, uint32_t *eotf_mask) { + struct drm_backend *backend = head->backend; + struct drm_device *device = backend->drm; drmModePropertyBlobPtr edid_blob = NULL; uint32_t blob_id; int rc; @@ -331,7 +334,7 @@ find_and_parse_output_edid(struct drm_head *head, if (!blob_id) return; - edid_blob = drmModeGetPropertyBlob(head->backend->drm.fd, blob_id); + edid_blob = drmModeGetPropertyBlob(device->drm.fd, blob_id); if (!edid_blob) return; @@ -360,7 +363,7 @@ prune_eotf_modes_by_kms_support(struct drm_head *head, uint32_t *eotf_mask) /* Without the KMS property, cannot do anything but SDR. */ info = &head->connector.props[WDRM_CONNECTOR_HDR_OUTPUT_METADATA]; - if (!head->backend->atomic_modeset || info->prop_id == 0) + if (!head->backend->drm->atomic_modeset || info->prop_id == 0) *eotf_mask = WESTON_EOTF_MODE_SDR; } @@ -426,8 +429,10 @@ drm_output_add_mode(struct drm_output *output, const drmModeModeInfo *info) static void drm_output_destroy_mode(struct drm_backend *backend, struct drm_mode *mode) { + struct drm_device *device = backend->drm; + if (mode->blob_id) - drmModeDestroyPropertyBlob(backend->drm.fd, mode->blob_id); + drmModeDestroyPropertyBlob(device->drm.fd, mode->blob_id); wl_list_remove(&mode->base.link); free(mode); } @@ -488,15 +493,17 @@ drm_output_choose_mode(struct drm_output *output, enum weston_mode_aspect_ratio src_aspect = WESTON_MODE_PIC_AR_NONE; enum weston_mode_aspect_ratio target_aspect = WESTON_MODE_PIC_AR_NONE; struct drm_backend *b; + struct drm_device *device; b = to_drm_backend(output->base.compositor); + device = b->drm; target_aspect = target_mode->aspect_ratio; src_aspect = output->base.current_mode->aspect_ratio; if (output->base.current_mode->width == target_mode->width && output->base.current_mode->height == target_mode->height && (output->base.current_mode->refresh == target_mode->refresh || target_mode->refresh == 0)) { - if (!b->aspect_ratio_supported || src_aspect == target_aspect) + if (!device->aspect_ratio_supported || src_aspect == target_aspect) return to_drm_mode(output->base.current_mode); } @@ -507,7 +514,7 @@ drm_output_choose_mode(struct drm_output *output, mode->mode_info.vdisplay == target_mode->height) { if (mode->base.refresh == target_mode->refresh || target_mode->refresh == 0) { - if (!b->aspect_ratio_supported || + if (!device->aspect_ratio_supported || src_aspect == target_aspect) return mode; else if (!mode_fall_back) @@ -574,6 +581,7 @@ drm_output_choose_initial_mode(struct drm_backend *backend, const char *modeline, const drmModeModeInfo *current_mode) { + struct drm_device *device = backend->drm; struct drm_mode *preferred = NULL; struct drm_mode *current = NULL; struct drm_mode *configured = NULL; @@ -592,7 +600,7 @@ drm_output_choose_initial_mode(struct drm_backend *backend, if (mode == WESTON_DRM_BACKEND_OUTPUT_PREFERRED && modeline) { n = sscanf(modeline, "%dx%d@%d %u:%u", &width, &height, &refresh, &aspect_width, &aspect_height); - if (backend->aspect_ratio_supported && n == 5) { + if (device->aspect_ratio_supported && n == 5) { if (aspect_width == 4 && aspect_height == 3) aspect_ratio = WESTON_MODE_PIC_AR_4_3; else if (aspect_width == 16 && aspect_height == 9) @@ -623,7 +631,7 @@ drm_output_choose_initial_mode(struct drm_backend *backend, if (width == drm_mode->base.width && height == drm_mode->base.height && (refresh == 0 || refresh == drm_mode->mode_info.vrefresh)) { - if (!backend->aspect_ratio_supported || + if (!device->aspect_ratio_supported || aspect_ratio == drm_mode->base.aspect_ratio) configured = drm_mode; else diff --git a/libweston/backend-drm/state-helpers.c b/libweston/backend-drm/state-helpers.c index 245ee087..d184602d 100644 --- a/libweston/backend-drm/state-helpers.c +++ b/libweston/backend-drm/state-helpers.c @@ -73,6 +73,9 @@ drm_plane_state_alloc(struct drm_output_state *state_output, void drm_plane_state_free(struct drm_plane_state *state, bool force) { + struct drm_backend *backend; + struct drm_device *device; + if (!state) return; @@ -86,7 +89,10 @@ drm_plane_state_free(struct drm_plane_state *state, bool force) * by the kernel, which means we can safely discard it. */ if (state->damage_blob_id != 0) { - drmModeDestroyPropertyBlob(state->plane->backend->drm.fd, + backend = state->plane->backend; + device = backend->drm; + + drmModeDestroyPropertyBlob(device->drm.fd, state->damage_blob_id); state->damage_blob_id = 0; } diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index 82da1f37..f1291711 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -86,10 +86,11 @@ drm_output_try_view_on_plane(struct drm_plane *plane, struct weston_compositor *ec = output->base.compositor; struct weston_surface *surface = ev->surface; struct drm_backend *b = to_drm_backend(ec); + struct drm_device *device = b->drm; struct drm_plane_state *state = NULL; - assert(!b->sprites_are_broken); - assert(b->atomic_modeset); + assert(!device->sprites_are_broken); + assert(device->atomic_modeset); assert(fb); assert(mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY || (mode == DRM_OUTPUT_PROPOSE_STATE_MIXED && @@ -162,16 +163,17 @@ static void cursor_bo_update(struct drm_plane_state *plane_state, struct weston_view *ev) { struct drm_backend *b = plane_state->plane->backend; + struct drm_device *device = b->drm; struct gbm_bo *bo = plane_state->fb->bo; struct weston_buffer *buffer = ev->surface->buffer_ref.buffer; - uint32_t buf[b->cursor_width * b->cursor_height]; + uint32_t buf[device->cursor_width * device->cursor_height]; int32_t stride; uint8_t *s; int i; assert(buffer && buffer->shm_buffer); - assert(buffer->width <= b->cursor_width); - assert(buffer->height <= b->cursor_height); + assert(buffer->width <= device->cursor_width); + assert(buffer->height <= device->cursor_height); memset(buf, 0, sizeof buf); stride = wl_shm_buffer_get_stride(buffer->shm_buffer); @@ -179,7 +181,7 @@ cursor_bo_update(struct drm_plane_state *plane_state, struct weston_view *ev) wl_shm_buffer_begin_access(buffer->shm_buffer); for (i = 0; i < buffer->height; i++) - memcpy(buf + i * b->cursor_width, + memcpy(buf + i * device->cursor_width, s + i * stride, buffer->width * 4); wl_shm_buffer_end_access(buffer->shm_buffer); @@ -194,12 +196,13 @@ drm_output_prepare_cursor_view(struct drm_output_state *output_state, { struct drm_output *output = output_state->output; struct drm_backend *b = to_drm_backend(output->base.compositor); + struct drm_device *device = b->drm; struct drm_plane *plane = output->cursor_plane; struct drm_plane_state *plane_state; bool needs_update = false; const char *p_name = drm_output_get_plane_type_name(plane); - assert(!b->cursors_are_broken); + assert(!device->cursors_are_broken); assert(plane); assert(plane->state_cur->complete); assert(!plane->state_cur->output || plane->state_cur->output == output); @@ -220,8 +223,8 @@ drm_output_prepare_cursor_view(struct drm_output_state *output_state, } if (plane_state->src_x != 0 || plane_state->src_y != 0 || - plane_state->src_w > (unsigned) b->cursor_width << 16 || - plane_state->src_h > (unsigned) b->cursor_height << 16 || + plane_state->src_w > (unsigned) device->cursor_width << 16 || + plane_state->src_h > (unsigned) device->cursor_height << 16 || plane_state->src_w != plane_state->dest_w << 16 || plane_state->src_h != plane_state->dest_h << 16) { drm_debug(b, "\t\t\t\t[%s] not assigning view %p to %s plane " @@ -260,10 +263,10 @@ drm_output_prepare_cursor_view(struct drm_output_state *output_state, * a buffer which is always cursor_width x cursor_height, even if the * surface we want to promote is actually smaller than this. Manually * mangle the plane state to deal with this. */ - plane_state->src_w = b->cursor_width << 16; - plane_state->src_h = b->cursor_height << 16; - plane_state->dest_w = b->cursor_width; - plane_state->dest_h = b->cursor_height; + plane_state->src_w = device->cursor_width << 16; + plane_state->src_h = device->cursor_height << 16; + plane_state->dest_w = device->cursor_width; + plane_state->dest_h = device->cursor_height; drm_debug(b, "\t\t\t\t[%s] provisionally assigned view %p to cursor\n", p_name, ev); @@ -328,7 +331,8 @@ dmabuf_feedback_maybe_update(struct drm_backend *b, struct weston_view *ev, { struct weston_dmabuf_feedback *dmabuf_feedback = ev->surface->dmabuf_feedback; struct weston_dmabuf_feedback_tranche *scanout_tranche; - dev_t scanout_dev = b->drm.devnum; + struct drm_device *device = b->drm; + dev_t scanout_dev = device->drm.devnum; uint32_t scanout_flags = ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT; uint32_t action_needed = ACTION_NEEDED_NONE; struct timespec current_time, delta_time; @@ -428,6 +432,7 @@ drm_output_find_plane_for_view(struct drm_output_state *state, { struct drm_output *output = state->output; struct drm_backend *b = to_drm_backend(output->base.compositor); + struct drm_device *device = b->drm; struct drm_plane_state *ps = NULL; struct drm_plane *plane; @@ -454,7 +459,7 @@ drm_output_find_plane_for_view(struct drm_output_state *state, FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE; return NULL; } else if (buffer->type == WESTON_BUFFER_SHM) { - if (!output->cursor_plane || b->cursors_are_broken) { + if (!output->cursor_plane || device->cursors_are_broken) { pnode->try_view_on_plane_failure_reasons |= FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE; return NULL; @@ -471,8 +476,8 @@ drm_output_find_plane_for_view(struct drm_output_state *state, return NULL; } - if (buffer->width > b->cursor_width || - buffer->height > b->cursor_height) { + if (buffer->width > device->cursor_width || + buffer->height > device->cursor_height) { drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane " "(buffer (%dx%d) too large for cursor plane)\n", ev, buffer->width, buffer->height); @@ -507,7 +512,7 @@ drm_output_find_plane_for_view(struct drm_output_state *state, state); /* assemble a list with possible candidates */ - wl_list_for_each(plane, &b->plane_list, link) { + wl_list_for_each(plane, &device->plane_list, link) { const char *p_name = drm_output_get_plane_type_name(plane); uint64_t zpos; @@ -912,7 +917,8 @@ void drm_assign_planes(struct weston_output *output_base) { struct drm_backend *b = to_drm_backend(output_base->compositor); - struct drm_pending_state *pending_state = b->repaint_data; + struct drm_device *device = b->drm; + struct drm_pending_state *pending_state = device->repaint_data; struct drm_output *output = to_drm_output(output_base); struct drm_output_state *state = NULL; struct drm_plane_state *plane_state; @@ -923,7 +929,7 @@ drm_assign_planes(struct weston_output *output_base) drm_debug(b, "\t[repaint] preparing state for output %s (%lu)\n", output_base->name, (unsigned long) output_base->id); - if (!b->sprites_are_broken && !output->virtual && b->gbm) { + if (!device->sprites_are_broken && !output->virtual && b->gbm) { drm_debug(b, "\t[repaint] trying planes-only build state\n"); state = drm_output_propose_state(output_base, pending_state, mode); if (!state) { @@ -985,8 +991,8 @@ drm_assign_planes(struct weston_output *output_base) buffer->type == WESTON_BUFFER_RENDERER_OPAQUE) ev->surface->keep_buffer = true; else if (buffer->type == WESTON_BUFFER_SHM && - (ev->surface->width <= b->cursor_width && - ev->surface->height <= b->cursor_height)) + (ev->surface->width <= device->cursor_width && + ev->surface->height <= device->cursor_height)) ev->surface->keep_buffer = true; } From d89fcf10cbcbff9b7c28885beb889b8b565802eb Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Tue, 16 Nov 2021 18:34:39 +0100 Subject: [PATCH 326/609] backend-drm: pass device through atomic commit handler The atomic commit is device specific. If we have multiple kms devices, we need to know which device was used for the atomic commit. Pass the device instead of the backend through the atomic commit. Signed-off-by: Michael Tretter --- libweston/backend-drm/kms.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libweston/backend-drm/kms.c b/libweston/backend-drm/kms.c index 2b159586..22d3f1f5 100644 --- a/libweston/backend-drm/kms.c +++ b/libweston/backend-drm/kms.c @@ -1184,7 +1184,7 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state, goto out; } - ret = drmModeAtomicCommit(device->drm.fd, req, flags, b); + ret = drmModeAtomicCommit(device->drm.fd, req, flags, device); drm_debug(b, "[atomic] drmModeAtomicCommit\n"); /* Test commits do not take ownership of the state; return @@ -1404,8 +1404,8 @@ static void atomic_flip_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, unsigned int crtc_id, void *data) { - struct drm_backend *b = data; - struct drm_device *device = b->drm; + struct drm_device *device = data; + struct drm_backend *b = device->backend; struct drm_crtc *crtc; struct drm_output *output; uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_VSYNC | From 101c0f6b8ba9bd79517629ee3313f907750cf273 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Wed, 3 Nov 2021 10:16:10 +0100 Subject: [PATCH 327/609] backend-drm: get the fb using the device instead of the backend The fbs are specific to the device on which they will be displayed. Therefore, we have to tell which device shall be used when we are creating the fb. Signed-off-by: Michael Tretter --- libweston/backend-drm/drm-gbm.c | 5 +++-- libweston/backend-drm/drm-internal.h | 4 ++-- libweston/backend-drm/drm.c | 3 ++- libweston/backend-drm/fb.c | 26 ++++++++++++-------------- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/libweston/backend-drm/drm-gbm.c b/libweston/backend-drm/drm-gbm.c index 1799e8da..afefffbf 100644 --- a/libweston/backend-drm/drm-gbm.c +++ b/libweston/backend-drm/drm-gbm.c @@ -163,7 +163,7 @@ drm_output_init_cursor_egl(struct drm_output *output, struct drm_backend *b) goto err; output->gbm_cursor_fb[i] = - drm_fb_get_from_bo(bo, b, false, BUFFER_CURSOR); + drm_fb_get_from_bo(bo, device, false, BUFFER_CURSOR); if (!output->gbm_cursor_fb[i]) { gbm_bo_destroy(bo); goto err; @@ -291,6 +291,7 @@ drm_output_render_gl(struct drm_output_state *state, pixman_region32_t *damage) { struct drm_output *output = state->output; struct drm_backend *b = to_drm_backend(output->base.compositor); + struct drm_device *device = b->drm; struct gbm_bo *bo; struct drm_fb *ret; @@ -305,7 +306,7 @@ drm_output_render_gl(struct drm_output_state *state, pixman_region32_t *damage) } /* The renderer always produces an opaque image. */ - ret = drm_fb_get_from_bo(bo, b, true, BUFFER_GBM_SURFACE); + ret = drm_fb_get_from_bo(bo, device, true, BUFFER_GBM_SURFACE); if (!ret) { weston_log("failed to get drm_fb for bo\n"); gbm_surface_release_buffer(output->gbm_surface, bo); diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index 062b8612..a551bbf0 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -725,10 +725,10 @@ void drm_fb_unref(struct drm_fb *fb); struct drm_fb * -drm_fb_create_dumb(struct drm_backend *b, int width, int height, +drm_fb_create_dumb(struct drm_device *device, int width, int height, uint32_t format); struct drm_fb * -drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend, +drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_device *device, bool is_opaque, enum drm_fb_type type); void diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index 89838afd..ff29f5a9 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -1160,6 +1160,7 @@ make_connector_name(const drmModeConnector *con) static int drm_output_init_pixman(struct drm_output *output, struct drm_backend *b) { + struct drm_device *device = b->drm; int w = output->base.current_mode->width; int h = output->base.current_mode->height; uint32_t format = output->gbm_format; @@ -1183,7 +1184,7 @@ drm_output_init_pixman(struct drm_output *output, struct drm_backend *b) /* FIXME error checking */ for (i = 0; i < ARRAY_LENGTH(output->dumb); i++) { - output->dumb[i] = drm_fb_create_dumb(b, w, h, format); + output->dumb[i] = drm_fb_create_dumb(device, w, h, format); if (!output->dumb[i]) goto err; diff --git a/libweston/backend-drm/fb.c b/libweston/backend-drm/fb.c index 4fa06ce8..d7df874a 100644 --- a/libweston/backend-drm/fb.c +++ b/libweston/backend-drm/fb.c @@ -69,9 +69,8 @@ drm_fb_destroy_dumb(struct drm_fb *fb) } static int -drm_fb_addfb(struct drm_backend *b, struct drm_fb *fb) +drm_fb_addfb(struct drm_device *device, struct drm_fb *fb) { - struct drm_device *device = b->drm; int ret = -EINVAL; uint64_t mods[4] = { }; size_t i; @@ -113,10 +112,9 @@ drm_fb_addfb(struct drm_backend *b, struct drm_fb *fb) } struct drm_fb * -drm_fb_create_dumb(struct drm_backend *b, int width, int height, +drm_fb_create_dumb(struct drm_device *device, int width, int height, uint32_t format) { - struct drm_device *device = b->drm; struct drm_fb *fb; int ret; @@ -161,7 +159,7 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height, fb->height = height; fb->fd = device->drm.fd; - if (drm_fb_addfb(b, fb) != 0) { + if (drm_fb_addfb(device, fb) != 0) { weston_log("failed to create kms fb: %s\n", strerror(errno)); goto err_bo; } @@ -220,7 +218,7 @@ drm_fb_destroy_dmabuf(struct drm_fb *fb) static struct drm_fb * drm_fb_get_from_dmabuf(struct linux_dmabuf_buffer *dmabuf, - struct drm_backend *backend, bool is_opaque, + struct drm_device *device, bool is_opaque, uint32_t *try_view_on_plane_failure_reasons) { #ifndef HAVE_GBM_FD_IMPORT @@ -229,7 +227,7 @@ drm_fb_get_from_dmabuf(struct linux_dmabuf_buffer *dmabuf, * of GBM_BO_IMPORT_FD_MODIFIER. */ return NULL; #else - struct drm_device *device = backend->drm; + struct drm_backend *backend = device->backend; struct drm_fb *fb; int i; struct gbm_import_fd_modifier_data import_mod = { @@ -326,7 +324,7 @@ drm_fb_get_from_dmabuf(struct linux_dmabuf_buffer *dmabuf, fb->handles[i] = handle.u32; } - if (drm_fb_addfb(backend, fb) != 0) { + if (drm_fb_addfb(device, fb) != 0) { if (try_view_on_plane_failure_reasons) *try_view_on_plane_failure_reasons |= FAILURE_REASONS_ADD_FB_FAILED; @@ -342,10 +340,9 @@ err_free: } struct drm_fb * -drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend, +drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_device *device, bool is_opaque, enum drm_fb_type type) { - struct drm_device *device = backend->drm; struct drm_fb *fb = gbm_bo_get_user_data(bo); #ifdef HAVE_GBM_MODIFIERS int i; @@ -404,7 +401,7 @@ drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend, goto err_free; } - if (drm_fb_addfb(backend, fb) != 0) { + if (drm_fb_addfb(device, fb) != 0) { if (type == BUFFER_GBM_SURFACE) weston_log("failed to create kms fb: %s\n", strerror(errno)); @@ -460,10 +457,11 @@ drm_can_scanout_dmabuf(struct weston_compositor *ec, { struct drm_fb *fb; struct drm_backend *b = to_drm_backend(ec); + struct drm_device *device = b->drm; bool ret = false; uint32_t try_reason = 0x0; - fb = drm_fb_get_from_dmabuf(dmabuf, b, true, &try_reason); + fb = drm_fb_get_from_dmabuf(dmabuf, device, true, &try_reason); if (fb) ret = true; @@ -582,7 +580,7 @@ drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev, } if (buffer->type == WESTON_BUFFER_DMABUF) { - fb = drm_fb_get_from_dmabuf(buffer->dmabuf, b, is_opaque, + fb = drm_fb_get_from_dmabuf(buffer->dmabuf, device, is_opaque, &buf_fb->failure_reasons); if (!fb) goto unsuitable; @@ -594,7 +592,7 @@ drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev, if (!bo) goto unsuitable; - fb = drm_fb_get_from_bo(bo, b, is_opaque, BUFFER_CLIENT); + fb = drm_fb_get_from_bo(bo, device, is_opaque, BUFFER_CLIENT); if (!fb) { *try_view_on_plane_failure_reasons |= (1 << FAILURE_REASONS_ADD_FB_FAILED); From 615a37dc882eb57e5b62b5c9d8ac108b362c84c7 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Mon, 29 Nov 2021 14:12:27 +0100 Subject: [PATCH 328/609] backend-drm: make dma-buf feedback device specific The scanout format for the dma-buf feedback are specific to the kms device that is used for scanout. Therefore, we have to pass the device of the output when retrieving the scanout formats. Signed-off-by: Michael Tretter --- libweston/backend-drm/drm.c | 7 +++---- libweston/backend-drm/state-propose.c | 6 +++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index ff29f5a9..b5a5865e 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -1711,10 +1711,9 @@ drm_output_deinit_planes(struct drm_output *output) } static struct weston_drm_format_array * -get_scanout_formats(struct drm_backend *b) +get_scanout_formats(struct drm_device *device) { - struct weston_compositor *ec = b->compositor; - struct drm_device *device = b->drm; + struct weston_compositor *ec = device->backend->compositor; const struct weston_drm_format_array *renderer_formats; struct weston_drm_format_array *scanout_formats, union_planes_formats; struct drm_plane *plane; @@ -3240,7 +3239,7 @@ drm_backend_create(struct weston_compositor *compositor, * table was already created and populated with * renderer's format/modifier pairs. So now we must * compute the scanout formats indices in the table */ - scanout_formats = get_scanout_formats(b); + scanout_formats = get_scanout_formats(b->drm); if (!scanout_formats) goto err_udev_monitor; ret = weston_dmabuf_feedback_format_table_set_scanout_indices(compositor->dmabuf_feedback_format_table, diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index f1291711..105c79e7 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -326,12 +326,12 @@ drm_output_check_zpos_plane_states(struct drm_output_state *state) } static bool -dmabuf_feedback_maybe_update(struct drm_backend *b, struct weston_view *ev, +dmabuf_feedback_maybe_update(struct drm_device *device, struct weston_view *ev, uint32_t try_view_on_plane_failure_reasons) { struct weston_dmabuf_feedback *dmabuf_feedback = ev->surface->dmabuf_feedback; struct weston_dmabuf_feedback_tranche *scanout_tranche; - struct drm_device *device = b->drm; + struct drm_backend *b = device->backend; dev_t scanout_dev = device->drm.devnum; uint32_t scanout_flags = ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT; uint32_t action_needed = ACTION_NEEDED_NONE; @@ -971,7 +971,7 @@ drm_assign_planes(struct weston_output *output_base) /* Update dmabuf-feedback if needed */ if (ev->surface->dmabuf_feedback) - dmabuf_feedback_maybe_update(b, ev, + dmabuf_feedback_maybe_update(device, ev, pnode->try_view_on_plane_failure_reasons); pnode->try_view_on_plane_failure_reasons = FAILURE_REASONS_NONE; From c4685d94635feee5537f5507982358558174f1cb Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Wed, 3 Nov 2021 12:56:31 +0100 Subject: [PATCH 329/609] backend-drm: attach device to pending state The commits happen per device instead of per backend. The pending state is therefore per device as well. Allow to retrieve the device from the pending state. Signed-off-by: Michael Tretter --- libweston/backend-drm/drm-internal.h | 4 ++-- libweston/backend-drm/drm.c | 9 +++++---- libweston/backend-drm/kms.c | 14 ++++++-------- libweston/backend-drm/state-helpers.c | 6 +++--- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index a551bbf0..030aa4aa 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -388,7 +388,7 @@ struct drm_edid { * output state will complete and be retired separately. */ struct drm_pending_state { - struct drm_backend *backend; + struct drm_device *device; struct wl_list output_list; }; @@ -760,7 +760,7 @@ drm_can_scanout_dmabuf(struct weston_compositor *ec, #endif struct drm_pending_state * -drm_pending_state_alloc(struct drm_backend *backend); +drm_pending_state_alloc(struct drm_device *device); void drm_pending_state_free(struct drm_pending_state *pending_state); struct drm_output_state * diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index b5a5865e..a80406f8 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -272,6 +272,7 @@ drm_output_update_complete(struct drm_output *output, uint32_t flags, unsigned int sec, unsigned int usec) { struct drm_backend *b = to_drm_backend(output->base.compositor); + struct drm_device *device = b->drm; struct drm_plane_state *ps; struct timespec ts; @@ -297,7 +298,7 @@ drm_output_update_complete(struct drm_output *output, uint32_t flags, weston_output_disable(&output->base); return; } else if (output->dpms_off_pending) { - struct drm_pending_state *pending = drm_pending_state_alloc(b); + struct drm_pending_state *pending = drm_pending_state_alloc(device); output->dpms_off_pending = false; drm_output_get_disable_state(pending, output); drm_pending_state_apply_sync(pending); @@ -588,7 +589,7 @@ drm_output_start_repaint_loop(struct weston_output *output_base) assert(!output->page_flip_pending); assert(!output->state_last); - pending_state = drm_pending_state_alloc(backend); + pending_state = drm_pending_state_alloc(device); drm_output_state_duplicate(output->state_cur, pending_state, DRM_OUTPUT_STATE_PRESERVE_PLANES); @@ -624,7 +625,7 @@ drm_repaint_begin(struct weston_compositor *compositor) struct drm_device *device = b->drm; struct drm_pending_state *pending_state; - pending_state = drm_pending_state_alloc(b); + pending_state = drm_pending_state_alloc(device); device->repaint_data = pending_state; if (weston_log_scope_is_enabled(b->debug)) { @@ -1101,7 +1102,7 @@ drm_set_dpms(struct weston_output *output_base, enum dpms_enum level) return; } - pending_state = drm_pending_state_alloc(b); + pending_state = drm_pending_state_alloc(device); drm_output_get_disable_state(pending_state, output); ret = drm_pending_state_apply_sync(pending_state); if (ret != 0) diff --git a/libweston/backend-drm/kms.c b/libweston/backend-drm/kms.c index 22d3f1f5..858d9146 100644 --- a/libweston/backend-drm/kms.c +++ b/libweston/backend-drm/kms.c @@ -1065,8 +1065,8 @@ static int drm_pending_state_apply_atomic(struct drm_pending_state *pending_state, enum drm_state_apply_mode mode) { - struct drm_backend *b = pending_state->backend; - struct drm_device *device = b->drm; + struct drm_device *device = pending_state->device; + struct drm_backend *b = device->backend; struct drm_output_state *output_state, *tmp; struct drm_plane *plane; drmModeAtomicReq *req = drmModeAtomicAlloc(); @@ -1235,8 +1235,7 @@ out: int drm_pending_state_test(struct drm_pending_state *pending_state) { - struct drm_backend *b = pending_state->backend; - struct drm_device *device = b->drm; + struct drm_device *device = pending_state->device; if (device->atomic_modeset) return drm_pending_state_apply_atomic(pending_state, @@ -1257,8 +1256,8 @@ drm_pending_state_test(struct drm_pending_state *pending_state) int drm_pending_state_apply(struct drm_pending_state *pending_state) { - struct drm_backend *b = pending_state->backend; - struct drm_device *device = b->drm; + struct drm_device *device = pending_state->device; + struct drm_backend *b = device->backend; struct drm_output_state *output_state, *tmp; struct drm_crtc *crtc; @@ -1325,8 +1324,7 @@ drm_pending_state_apply(struct drm_pending_state *pending_state) int drm_pending_state_apply_sync(struct drm_pending_state *pending_state) { - struct drm_backend *backend = pending_state->backend; - struct drm_device *device = backend->drm; + struct drm_device *device = pending_state->device; struct drm_output_state *output_state, *tmp; struct drm_crtc *crtc; diff --git a/libweston/backend-drm/state-helpers.c b/libweston/backend-drm/state-helpers.c index d184602d..0e8e45e6 100644 --- a/libweston/backend-drm/state-helpers.c +++ b/libweston/backend-drm/state-helpers.c @@ -438,11 +438,11 @@ drm_output_state_free(struct drm_output_state *state) * Allocate a new, empty, 'pending state' structure to be used across a * repaint cycle or similar. * - * @param backend DRM backend + * @param device DRM device * @returns Newly-allocated pending state structure */ struct drm_pending_state * -drm_pending_state_alloc(struct drm_backend *backend) +drm_pending_state_alloc(struct drm_device *device) { struct drm_pending_state *ret; @@ -450,7 +450,7 @@ drm_pending_state_alloc(struct drm_backend *backend) if (!ret) return NULL; - ret->backend = backend; + ret->device = device; wl_list_init(&ret->output_list); return ret; From 345e705e33181c65c5885fef864e87b712e48bd3 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Wed, 3 Nov 2021 12:56:53 +0100 Subject: [PATCH 330/609] backend-drm: move drm objects from backend to drm device The outputs, heads, crtcs, and connectors are specific to a drm device and not the backend in general. Link them to the device that they belong to to be able to retrieve the respective device. Signed-off-by: Michael Tretter --- libweston/backend-drm/drm-internal.h | 25 +++-- libweston/backend-drm/drm-virtual.c | 30 +++--- libweston/backend-drm/drm.c | 145 ++++++++++++-------------- libweston/backend-drm/fb.c | 3 +- libweston/backend-drm/kms-color.c | 2 +- libweston/backend-drm/kms.c | 29 +++--- libweston/backend-drm/modes.c | 34 +++--- libweston/backend-drm/state-helpers.c | 4 +- libweston/backend-drm/state-propose.c | 3 +- 9 files changed, 133 insertions(+), 142 deletions(-) diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index 030aa4aa..107f91e7 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -466,7 +466,7 @@ struct drm_plane_state { struct drm_plane { struct weston_plane base; - struct drm_backend *backend; + struct drm_device *device; enum wdrm_plane_type type; @@ -488,7 +488,7 @@ struct drm_plane { }; struct drm_connector { - struct drm_backend *backend; + struct drm_device *device; drmModeConnector *conn; uint32_t connector_id; @@ -500,16 +500,15 @@ struct drm_connector { }; struct drm_writeback { - /* drm_backend::writeback_connector_list */ + /* drm_device::writeback_connector_list */ struct wl_list link; - struct drm_backend *backend; + struct drm_device *device; struct drm_connector connector; }; struct drm_head { struct weston_head base; - struct drm_backend *backend; struct drm_connector connector; struct drm_edid edid; @@ -521,9 +520,9 @@ struct drm_head { }; struct drm_crtc { - /* drm_backend::crtc_list */ + /* drm_device::crtc_list */ struct wl_list link; - struct drm_backend *backend; + struct drm_device *device; /* The output driven by the CRTC */ struct drm_output *output; @@ -537,7 +536,7 @@ struct drm_crtc { struct drm_output { struct weston_output base; - struct drm_backend *backend; + struct drm_device *device; struct drm_crtc *crtc; bool page_flip_pending; @@ -625,7 +624,7 @@ drm_output_get_plane_type_name(struct drm_plane *p) } struct drm_crtc * -drm_crtc_find(struct drm_backend *b, uint32_t crtc_id); +drm_crtc_find(struct drm_device *device, uint32_t crtc_id); struct drm_head * drm_head_find_by_connector(struct drm_backend *backend, uint32_t connector_id); @@ -650,7 +649,7 @@ drm_view_transform_supported(struct weston_view *ev, struct weston_output *outpu } int -drm_mode_ensure_blob(struct drm_backend *backend, struct drm_mode *mode); +drm_mode_ensure_blob(struct drm_device *device, struct drm_mode *mode); struct drm_mode * drm_output_choose_mode(struct drm_output *output, @@ -659,7 +658,7 @@ void update_head_from_connector(struct drm_head *head); void -drm_mode_list_destroy(struct drm_backend *backend, struct wl_list *mode_list); +drm_mode_list_destroy(struct drm_device *device, struct wl_list *mode_list); void drm_output_print_modes(struct drm_output *output); @@ -670,7 +669,7 @@ drm_output_set_mode(struct weston_output *base, const char *modeline); void -drm_property_info_populate(struct drm_backend *b, +drm_property_info_populate(struct drm_device *device, const struct drm_property_info *src, struct drm_property_info *info, unsigned int num_infos, @@ -698,7 +697,7 @@ extern const struct drm_property_info connector_props[]; extern const struct drm_property_info crtc_props[]; int -init_kms_caps(struct drm_backend *b); +init_kms_caps(struct drm_device *device); int drm_pending_state_test(struct drm_pending_state *pending_state); diff --git a/libweston/backend-drm/drm-virtual.c b/libweston/backend-drm/drm-virtual.c index 24004e14..d2b4c8e3 100644 --- a/libweston/backend-drm/drm-virtual.c +++ b/libweston/backend-drm/drm-virtual.c @@ -47,7 +47,7 @@ * CRTC's. Also, as this is a fake CRTC, it will not try to populate props. */ static struct drm_crtc * -drm_virtual_crtc_create(struct drm_backend *b, struct drm_output *output) +drm_virtual_crtc_create(struct drm_device *device, struct drm_output *output) { struct drm_crtc *crtc; @@ -55,7 +55,7 @@ drm_virtual_crtc_create(struct drm_backend *b, struct drm_output *output) if (!crtc) return NULL; - crtc->backend = b; + crtc->device = device; crtc->output = output; crtc->crtc_id = 0; @@ -86,13 +86,13 @@ drm_virtual_crtc_destroy(struct drm_crtc *crtc) * * Call drm_virtual_plane_destroy to clean up the plane. * - * @param b DRM compositor backend + * @param device DRM device * @param output Output to create internal plane for */ static struct drm_plane * -drm_virtual_plane_create(struct drm_backend *b, struct drm_output *output) +drm_virtual_plane_create(struct drm_device *device, struct drm_output *output) { - struct drm_device *device = b->drm; + struct drm_backend *b = device->backend; struct drm_plane *plane; struct weston_drm_format *fmt; uint64_t mod; @@ -104,7 +104,7 @@ drm_virtual_plane_create(struct drm_backend *b, struct drm_output *output) } plane->type = WDRM_PLANE_TYPE_PRIMARY; - plane->backend = b; + plane->device = device; plane->state_cur = drm_plane_state_alloc(NULL, plane); plane->state_cur->complete = true; @@ -192,13 +192,11 @@ drm_virtual_output_repaint(struct weston_output *output_base, struct drm_plane *scanout_plane = output->scanout_plane; struct drm_plane_state *scanout_state; struct drm_pending_state *pending_state; - struct drm_backend *backend; struct drm_device *device; assert(output->virtual); - backend = output->backend; - device = backend->drm; + device = output->device; pending_state = device->repaint_data; if (output->disable_pending || output->destroy_pending) @@ -268,7 +266,8 @@ static int drm_virtual_output_enable(struct weston_output *output_base) { struct drm_output *output = to_drm_output(output_base); - struct drm_backend *b = to_drm_backend(output_base->compositor); + struct drm_device *device = output->device; + struct drm_backend *b = device->backend; assert(output->virtual); @@ -282,7 +281,7 @@ drm_virtual_output_enable(struct weston_output *output_base) goto err; } - output->scanout_plane = drm_virtual_plane_create(b, output); + output->scanout_plane = drm_virtual_plane_create(device, output); if (!output->scanout_plane) { weston_log("Failed to find primary plane for output %s\n", output->base.name); @@ -329,19 +328,21 @@ drm_virtual_output_create(struct weston_compositor *c, char *name) { struct drm_output *output; struct drm_backend *b = to_drm_backend(c); + /* Always use the main device for virtual outputs */ + struct drm_device *device = b->drm; output = zalloc(sizeof *output); if (!output) return NULL; - output->crtc = drm_virtual_crtc_create(b, output); + output->device = device; + output->crtc = drm_virtual_crtc_create(device, output); if (!output->crtc) { free(output); return NULL; } output->virtual = true; - output->backend = b; output->gbm_bo_flags = GBM_BO_USE_LINEAR | GBM_BO_USE_RENDERING; weston_output_init(&output->base, c, name); @@ -363,7 +364,8 @@ drm_virtual_output_set_gbm_format(struct weston_output *base, const char *gbm_format) { struct drm_output *output = to_drm_output(base); - struct drm_backend *b = to_drm_backend(base->compositor); + struct drm_device *device = output->device; + struct drm_backend *b = device->backend; if (parse_gbm_format(gbm_format, b->gbm_format, &output->gbm_format) == -1) output->gbm_format = b->gbm_format; diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index a80406f8..b37705fb 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -193,9 +193,8 @@ drm_plane_is_available(struct drm_plane *plane, struct drm_output *output) } struct drm_crtc * -drm_crtc_find(struct drm_backend *b, uint32_t crtc_id) +drm_crtc_find(struct drm_device *device, uint32_t crtc_id) { - struct drm_device *device = b->drm; struct drm_crtc *crtc; wl_list_for_each(crtc, &device->crtc_list, link) { @@ -451,13 +450,11 @@ drm_output_repaint(struct weston_output *output_base, pixman_region32_t *damage) struct drm_output_state *state = NULL; struct drm_plane_state *scanout_state; struct drm_pending_state *pending_state; - struct drm_backend *backend; struct drm_device *device; assert(!output->virtual); - backend = output->backend; - device = backend->drm; + device = output->device; pending_state = device->repaint_data; if (output->disable_pending || output->destroy_pending) @@ -755,13 +752,14 @@ init_pixman(struct drm_backend *b) * Call drm_plane_destroy to clean up the plane. * * @sa drm_output_find_special_plane - * @param b DRM compositor backend + * @param device DRM device * @param kplane DRM plane to create */ static struct drm_plane * -drm_plane_create(struct drm_backend *b, const drmModePlane *kplane) +drm_plane_create(struct drm_device *device, const drmModePlane *kplane) { - struct drm_device *device = b->drm; + struct drm_backend *b = device->backend; + struct weston_compositor *compositor = b->compositor; struct drm_plane *plane, *tmp; drmModeObjectProperties *props; uint64_t *zpos_range_values; @@ -772,7 +770,7 @@ drm_plane_create(struct drm_backend *b, const drmModePlane *kplane) return NULL; } - plane->backend = b; + plane->device = device; plane->state_cur = drm_plane_state_alloc(NULL, plane); plane->state_cur->complete = true; plane->possible_crtcs = kplane->possible_crtcs; @@ -787,7 +785,7 @@ drm_plane_create(struct drm_backend *b, const drmModePlane *kplane) goto err; } - drm_property_info_populate(b, plane_props, plane->props, + drm_property_info_populate(device, plane_props, plane->props, WDRM_PLANE__COUNT, props); plane->type = drm_property_get_value(&plane->props[WDRM_PLANE_TYPE], @@ -817,7 +815,7 @@ drm_plane_create(struct drm_backend *b, const drmModePlane *kplane) if (plane->type == WDRM_PLANE_TYPE__COUNT) goto err_props; - weston_plane_init(&plane->base, b->compositor, 0, 0); + weston_plane_init(&plane->base, compositor, 0, 0); wl_list_for_each(tmp, &device->plane_list, link) { if (tmp->zpos_max > plane->zpos_max) { @@ -842,15 +840,16 @@ err: /** * Find, or create, a special-purpose plane * - * @param b DRM backend + * @param device DRM device * @param output Output to use for plane * @param type Type of plane */ static struct drm_plane * -drm_output_find_special_plane(struct drm_backend *b, struct drm_output *output, +drm_output_find_special_plane(struct drm_device *device, + struct drm_output *output, enum wdrm_plane_type type) { - struct drm_device *device = b->drm; + struct drm_backend *b = device->backend; struct drm_plane *plane; wl_list_for_each(plane, &device->plane_list, link) { @@ -894,8 +893,7 @@ drm_output_find_special_plane(struct drm_backend *b, struct drm_output *output, static void drm_plane_destroy(struct drm_plane *plane) { - struct drm_backend *backend = plane->backend; - struct drm_device *device = backend->drm; + struct drm_device *device = plane->device; if (plane->type == WDRM_PLANE_TYPE_OVERLAY) drmModeSetPlane(device->drm.fd, plane->plane_id, @@ -915,12 +913,12 @@ drm_plane_destroy(struct drm_plane *plane) * * Call destroy_sprites to free these planes. * - * @param b DRM compositor backend + * @param device DRM device */ static void -create_sprites(struct drm_backend *b) +create_sprites(struct drm_device *device) { - struct drm_device *device = b->drm; + struct drm_backend *b = device->backend; drmModePlaneRes *kplane_res; drmModePlane *kplane; struct drm_plane *drm_plane; @@ -939,7 +937,7 @@ create_sprites(struct drm_backend *b) if (!kplane) continue; - drm_plane = drm_plane_create(b, kplane); + drm_plane = drm_plane_create(device, kplane); drmModeFreePlane(kplane); if (!drm_plane) continue; @@ -961,12 +959,11 @@ create_sprites(struct drm_backend *b) * * The counterpart to create_sprites. * - * @param b DRM compositor backend + * @param device DRM device */ static void -destroy_sprites(struct drm_backend *b) +destroy_sprites(struct drm_device *device) { - struct drm_device *device = b->drm; struct drm_plane *plane, *next; wl_list_for_each_safe(plane, next, &device->plane_list, link) @@ -1342,9 +1339,8 @@ parse_gbm_format(const char *s, uint32_t default_value, uint32_t *gbm_format) } static int -drm_head_read_current_setup(struct drm_head *head, struct drm_backend *backend) +drm_head_read_current_setup(struct drm_head *head, struct drm_device *device) { - struct drm_device *device = backend->drm; int drm_fd = device->drm.fd; drmModeConnector *conn = head->connector.conn; drmModeEncoder *encoder; @@ -1414,8 +1410,7 @@ drm_output_init_gamma_size(struct drm_output *output) static uint32_t drm_connector_get_possible_crtcs_mask(struct drm_connector *connector) { - struct drm_backend *backend = connector->backend; - struct drm_device *device = backend->drm; + struct drm_device *device = connector->device; uint32_t possible_crtcs = 0; drmModeConnector *conn = connector->conn; drmModeEncoder *encoder; @@ -1471,7 +1466,7 @@ drm_output_pick_crtc(struct drm_output *output) crtc_id = head->inherited_crtc_id; if (crtc_id > 0 && n < ARRAY_LENGTH(existing_crtc)) - existing_crtc[n++] = drm_crtc_find(backend, crtc_id); + existing_crtc[n++] = drm_crtc_find(device, crtc_id); } /* Find a crtc that could drive each connector individually at least, @@ -1551,9 +1546,8 @@ drm_output_pick_crtc(struct drm_output *output) * all, it adds the object to the DRM-backend CRTC list. */ static struct drm_crtc * -drm_crtc_create(struct drm_backend *b, uint32_t crtc_id, uint32_t pipe) +drm_crtc_create(struct drm_device *device, uint32_t crtc_id, uint32_t pipe) { - struct drm_device *device = b->drm; struct drm_crtc *crtc; drmModeObjectPropertiesPtr props; @@ -1568,9 +1562,9 @@ drm_crtc_create(struct drm_backend *b, uint32_t crtc_id, uint32_t pipe) if (!crtc) goto ret; - drm_property_info_populate(b, crtc_props, crtc->props_crtc, + drm_property_info_populate(device, crtc_props, crtc->props_crtc, WDRM_CRTC__COUNT, props); - crtc->backend = b; + crtc->device = device; crtc->crtc_id = crtc_id; crtc->pipe = pipe; crtc->output = NULL; @@ -1607,14 +1601,13 @@ drm_crtc_destroy(struct drm_crtc *crtc) * The CRTCs are saved in a list of the drm_backend and will keep there until * the fd gets closed. * - * @param b The DRM-backend structure. + * @param device The DRM device structure. * @param resources The DRM resources, it is taken with drmModeGetResources * @return 0 on success (at least one CRTC in the list), -1 on failure. */ static int -drm_backend_create_crtc_list(struct drm_backend *b, drmModeRes *resources) +drm_backend_create_crtc_list(struct drm_device *device, drmModeRes *resources) { - struct drm_device *device = b->drm; struct drm_crtc *crtc, *crtc_tmp; int i; @@ -1622,7 +1615,7 @@ drm_backend_create_crtc_list(struct drm_backend *b, drmModeRes *resources) for (i = 0; i < resources->count_crtcs; i++) { /* Let's create an object for the CRTC and add it to the list */ - crtc = drm_crtc_create(b, resources->crtcs[i], i); + crtc = drm_crtc_create(device, resources->crtcs[i], i); if (!crtc) goto err; } @@ -1646,7 +1639,7 @@ drm_output_init_planes(struct drm_output *output) struct drm_device *device = b->drm; output->scanout_plane = - drm_output_find_special_plane(b, output, + drm_output_find_special_plane(device, output, WDRM_PLANE_TYPE_PRIMARY); if (!output->scanout_plane) { weston_log("Failed to find primary plane for output %s\n", @@ -1661,7 +1654,7 @@ drm_output_init_planes(struct drm_output *output) /* Failing to find a cursor plane is not fatal, as we'll fall back * to software cursor. */ output->cursor_plane = - drm_output_find_special_plane(b, output, + drm_output_find_special_plane(device, output, WDRM_PLANE_TYPE_CURSOR); if (output->cursor_plane) @@ -1803,8 +1796,7 @@ drm_output_attach_crtc(struct drm_output *output) static void drm_output_detach_crtc(struct drm_output *output) { - struct drm_backend *b = output->backend; - struct drm_device *device = b->drm; + struct drm_device *device = output->device; struct drm_crtc *crtc = output->crtc; crtc->output = NULL; @@ -1905,7 +1897,7 @@ static void drm_output_destroy(struct weston_output *base) { struct drm_output *output = to_drm_output(base); - struct drm_backend *b = to_drm_backend(base->compositor); + struct drm_device *device = output->device; assert(!output->virtual); @@ -1920,7 +1912,7 @@ drm_output_destroy(struct weston_output *base) if (output->base.enabled) drm_output_deinit(&output->base); - drm_mode_list_destroy(b, &output->base.mode_list); + drm_mode_list_destroy(device, &output->base.mode_list); if (output->pageflip_timer) wl_event_source_remove(output->pageflip_timer); @@ -2038,8 +2030,7 @@ drm_head_get_current_protection(struct drm_head *head) static int drm_connector_update_properties(struct drm_connector *connector) { - struct drm_backend *backend = connector->backend; - struct drm_device *device = backend->drm; + struct drm_device *device = connector->device; drmModeObjectProperties *props; props = drmModeObjectGetProperties(device->drm.fd, @@ -2070,6 +2061,8 @@ static int drm_connector_assign_connector_info(struct drm_connector *connector, drmModeConnector *conn) { + struct drm_device *device = connector->device; + assert(connector->conn != conn); assert(connector->connector_id == conn->connector_id); @@ -2081,17 +2074,16 @@ drm_connector_assign_connector_info(struct drm_connector *connector, connector->conn = conn; drm_property_info_free(connector->props, WDRM_CONNECTOR__COUNT); - drm_property_info_populate(connector->backend, connector_props, - connector->props, + drm_property_info_populate(device, connector_props, connector->props, WDRM_CONNECTOR__COUNT, connector->props_drm); return 0; } static void -drm_connector_init(struct drm_backend *b, struct drm_connector *connector, +drm_connector_init(struct drm_device *device, struct drm_connector *connector, uint32_t connector_id) { - connector->backend = b; + connector->device = device; connector->connector_id = connector_id; connector->conn = NULL; connector->props_drm = NULL; @@ -2181,7 +2173,7 @@ drm_writeback_update_info(struct drm_writeback *writeback, drmModeConnector *con * Given a DRM connector, create a matching drm_head structure and add it * to Weston's head list. * - * @param backend Weston backend structure + * @param device DRM device structure * @param conn DRM connector object * @param drm_device udev device pointer * @returns 0 on success, -1 on failure @@ -2189,9 +2181,10 @@ drm_writeback_update_info(struct drm_writeback *writeback, drmModeConnector *con * Takes ownership of @c connector on success, not on failure. */ static int -drm_head_create(struct drm_backend *backend, drmModeConnector *conn, +drm_head_create(struct drm_device *device, drmModeConnector *conn, struct udev_device *drm_device) { + struct drm_backend *backend = device->backend; struct drm_head *head; char *name; int ret; @@ -2200,9 +2193,7 @@ drm_head_create(struct drm_backend *backend, drmModeConnector *conn, if (!head) return -1; - head->backend = backend; - - drm_connector_init(backend, &head->connector, conn->connector_id); + drm_connector_init(device, &head->connector, conn->connector_id); name = make_connector_name(conn); if (!name) @@ -2221,7 +2212,7 @@ drm_head_create(struct drm_backend *backend, drmModeConnector *conn, conn->connector_type == DRM_MODE_CONNECTOR_eDP) weston_head_set_internal(&head->base); - if (drm_head_read_current_setup(head, backend) < 0) { + if (drm_head_read_current_setup(head, device) < 0) { weston_log("Failed to retrieve current mode from connector %d.\n", head->connector.connector_id); /* Not fatal. */ @@ -2270,13 +2261,14 @@ static struct weston_output * drm_output_create(struct weston_compositor *compositor, const char *name) { struct drm_backend *b = to_drm_backend(compositor); + struct drm_device *device = b->drm; struct drm_output *output; output = zalloc(sizeof *output); if (output == NULL) return NULL; - output->backend = b; + output->device = device; output->crtc = NULL; output->gbm_format = DRM_FORMAT_INVALID; @@ -2308,25 +2300,24 @@ drm_output_create(struct weston_compositor *compositor, const char *name) * Given a DRM connector of type writeback, create a matching drm_writeback * structure and add it to Weston's writeback list. * - * @param b Weston backend structure + * @param device DRM device structure * @param conn DRM connector object of type writeback * @returns 0 on success, -1 on failure * * Takes ownership of @c connector on success, not on failure. */ static int -drm_writeback_create(struct drm_backend *b, drmModeConnector *conn) +drm_writeback_create(struct drm_device *device, drmModeConnector *conn) { - struct drm_device *device = b->drm; struct drm_writeback *writeback; int ret; writeback = zalloc(sizeof *writeback); assert(writeback); - writeback->backend = b; + writeback->device = device; - drm_connector_init(b, &writeback->connector, conn->connector_id); + drm_connector_init(device, &writeback->connector, conn->connector_id); ret = drm_writeback_update_info(writeback, conn); if (ret < 0) @@ -2355,24 +2346,24 @@ drm_writeback_destroy(struct drm_writeback *writeback) * * The object is then added to the DRM-backend list of heads or writebacks. * - * @param b The DRM-backend structure + * @param device The DRM device structure * @param conn The DRM connector object * @param drm_device udev device pointer * @return 0 on success, -1 on failure */ static int -drm_backend_add_connector(struct drm_backend *b, drmModeConnector *conn, +drm_backend_add_connector(struct drm_device *device, drmModeConnector *conn, struct udev_device *drm_device) { int ret; if (conn->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) { - ret = drm_writeback_create(b, conn); + ret = drm_writeback_create(device, conn); if (ret < 0) weston_log("DRM: failed to create writeback for connector %d.\n", conn->connector_id); } else { - ret = drm_head_create(b, conn, drm_device); + ret = drm_head_create(device, conn, drm_device); if (ret < 0) weston_log("DRM: failed to create head for connector %d.\n", conn->connector_id); @@ -2386,16 +2377,16 @@ drm_backend_add_connector(struct drm_backend *b, drmModeConnector *conn, * * These objects are added to the DRM-backend lists of heads and writebacks. * - * @param b The DRM-backend structure + * @param device The DRM device structure * @param drm_device udev device pointer * @param resources The DRM resources, it is taken with drmModeGetResources * @return 0 on success, -1 on failure */ static int -drm_backend_discover_connectors(struct drm_backend *b, struct udev_device *drm_device, +drm_backend_discover_connectors(struct drm_device *device, + struct udev_device *drm_device, drmModeRes *resources) { - struct drm_device *device = b->drm; drmModeConnector *conn; int i, ret; @@ -2411,7 +2402,7 @@ drm_backend_discover_connectors(struct drm_backend *b, struct udev_device *drm_d if (!conn) continue; - ret = drm_backend_add_connector(b, conn, drm_device); + ret = drm_backend_add_connector(device, conn, drm_device); if (ret < 0) drmModeFreeConnector(conn); } @@ -2468,7 +2459,7 @@ drm_backend_update_connectors(struct drm_backend *b, struct udev_device *drm_dev else if (writeback) ret = drm_writeback_update_info(writeback, conn); else - ret = drm_backend_add_connector(b, conn, drm_device); + ret = drm_backend_add_connector(b->drm, conn, drm_device); if (ret < 0) drmModeFreeConnector(conn); @@ -2632,7 +2623,7 @@ drm_destroy(struct weston_compositor *ec) b->shutting_down = true; - destroy_sprites(b); + destroy_sprites(b->drm); weston_log_scope_destroy(b->debug); b->debug = NULL; @@ -2951,13 +2942,11 @@ recorder_frame_notify(struct wl_listener *listener, void *data) { struct drm_output *output; struct drm_device *device; - struct drm_backend *b; int fd, ret; output = container_of(listener, struct drm_output, recorder_frame_listener); - b = to_drm_backend(output->base.compositor); - device = b->drm; + device = output->device; if (!output->recorder) return; @@ -3130,7 +3119,7 @@ drm_backend_create(struct weston_compositor *compositor, goto err_udev; } - if (init_kms_caps(b) < 0) { + if (init_kms_caps(device) < 0) { weston_log("failed to initialize kms\n"); goto err_udev_dev; } @@ -3164,13 +3153,13 @@ drm_backend_create(struct weston_compositor *compositor, } wl_list_init(&b->drm->crtc_list); - if (drm_backend_create_crtc_list(b, res) == -1) { + if (drm_backend_create_crtc_list(b->drm, res) == -1) { weston_log("Failed to create CRTC list for DRM-backend\n"); goto err_create_crtc_list; } wl_list_init(&device->plane_list); - create_sprites(b); + create_sprites(b->drm); if (udev_input_init(&b->input, compositor, b->udev, seat_id, @@ -3180,7 +3169,7 @@ drm_backend_create(struct weston_compositor *compositor, } wl_list_init(&b->drm->writeback_connector_list); - if (drm_backend_discover_connectors(b, drm_device, res) < 0) { + if (drm_backend_discover_connectors(b->drm, drm_device, res) < 0) { weston_log("Failed to create heads for %s\n", b->drm->drm.filename); goto err_udev_input; } @@ -3290,7 +3279,7 @@ err_drm_source: err_udev_input: udev_input_destroy(&b->input); err_sprite: - destroy_sprites(b); + destroy_sprites(b->drm); err_create_crtc_list: drmModeFreeResources(res); err_udev_dev: diff --git a/libweston/backend-drm/fb.c b/libweston/backend-drm/fb.c index d7df874a..e6881e8a 100644 --- a/libweston/backend-drm/fb.c +++ b/libweston/backend-drm/fb.c @@ -474,7 +474,8 @@ drm_can_scanout_dmabuf(struct weston_compositor *ec, static bool drm_fb_compatible_with_plane(struct drm_fb *fb, struct drm_plane *plane) { - struct drm_backend *b = plane->backend; + struct drm_device *device = plane->device; + struct drm_backend *b = device->backend; struct weston_drm_format *fmt; /* Check whether the format is supported */ diff --git a/libweston/backend-drm/kms-color.c b/libweston/backend-drm/kms-color.c index 55925665..2f9ad945 100644 --- a/libweston/backend-drm/kms-color.c +++ b/libweston/backend-drm/kms-color.c @@ -108,7 +108,7 @@ weston_hdr_metadata_type1_to_kms(struct hdr_metadata_infoframe *dst, int drm_output_ensure_hdr_output_metadata_blob(struct drm_output *output) { - struct drm_device *device = output->backend->drm; + struct drm_device *device = output->device; const struct weston_hdr_metadata_type1 *src; struct hdr_output_metadata meta; uint32_t blob_id = 0; diff --git a/libweston/backend-drm/kms.c b/libweston/backend-drm/kms.c index 858d9146..c7a067dc 100644 --- a/libweston/backend-drm/kms.c +++ b/libweston/backend-drm/kms.c @@ -275,20 +275,19 @@ drm_property_get_range_values(struct drm_property_info *info, * The values given in enum_names are searched for, and stored in the * same-indexed field of the map array. * - * @param b DRM backend object + * @param device DRM device object * @param src DRM property info array to source from * @param info DRM property info array to copy into * @param num_infos Number of entries in the source array * @param props DRM object properties for the object */ void -drm_property_info_populate(struct drm_backend *b, +drm_property_info_populate(struct drm_device *device, const struct drm_property_info *src, struct drm_property_info *info, unsigned int num_infos, drmModeObjectProperties *props) { - struct drm_device *device = b->drm; drmModePropertyRes *prop; unsigned i, j; @@ -437,8 +436,7 @@ drm_plane_populate_formats(struct drm_plane *plane, const drmModePlane *kplane, const drmModeObjectProperties *props, const bool use_modifiers) { - struct drm_backend *backend = plane->backend; - struct drm_device *device = backend->drm; + struct drm_device *device = plane->device; unsigned i, j; drmModePropertyBlobRes *blob = NULL; struct drm_format_modifier_blob *fmt_mod_blob; @@ -796,7 +794,8 @@ static int crtc_add_prop(drmModeAtomicReq *req, struct drm_crtc *crtc, enum wdrm_crtc_property prop, uint64_t val) { - struct drm_backend *b = crtc->backend; + struct drm_device *device = crtc->device; + struct drm_backend *b = device->backend; struct drm_property_info *info = &crtc->props_crtc[prop]; int ret; @@ -816,7 +815,8 @@ static int connector_add_prop(drmModeAtomicReq *req, struct drm_connector *connector, enum wdrm_connector_property prop, uint64_t val) { - struct drm_backend *b = connector->backend; + struct drm_device *device = connector->device; + struct drm_backend *b = device->backend; struct drm_property_info *info = &connector->props[prop]; uint32_t connector_id = connector->connector_id; int ret; @@ -836,7 +836,8 @@ static int plane_add_prop(drmModeAtomicReq *req, struct drm_plane *plane, enum wdrm_plane_property prop, uint64_t val) { - struct drm_backend *b = plane->backend; + struct drm_device *device = plane->device; + struct drm_backend *b = device->backend; struct drm_property_info *info = &plane->props[prop]; int ret; @@ -941,6 +942,7 @@ drm_output_apply_state_atomic(struct drm_output_state *state, { struct drm_output *output = state->output; struct drm_backend *b = to_drm_backend(output->base.compositor); + struct drm_device *device = b->drm; struct drm_crtc *crtc = output->crtc; struct drm_plane_state *plane_state; struct drm_mode *current_mode = to_drm_mode(output->base.current_mode); @@ -957,7 +959,7 @@ drm_output_apply_state_atomic(struct drm_output_state *state, } if (state->dpms == WESTON_DPMS_ON) { - ret = drm_mode_ensure_blob(b, current_mode); + ret = drm_mode_ensure_blob(device, current_mode); if (ret != 0) return ret; @@ -1410,7 +1412,7 @@ atomic_flip_handler(int fd, unsigned int frame, unsigned int sec, WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION | WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK; - crtc = drm_crtc_find(b, crtc_id); + crtc = drm_crtc_find(device, crtc_id); assert(crtc); output = crtc->output; @@ -1450,9 +1452,10 @@ on_drm_input(int fd, uint32_t mask, void *data) } int -init_kms_caps(struct drm_backend *b) +init_kms_caps(struct drm_device *device) { - struct drm_device *device = b->drm; + struct drm_backend *b = device->backend; + struct weston_compositor *compositor = b->compositor; uint64_t cap; int ret; @@ -1464,7 +1467,7 @@ init_kms_caps(struct drm_backend *b) return -1; } - if (weston_compositor_set_presentation_clock(b->compositor, CLOCK_MONOTONIC) < 0) { + if (weston_compositor_set_presentation_clock(compositor, CLOCK_MONOTONIC) < 0) { weston_log("Error: failed to set presentation clock to CLOCK_MONOTONIC.\n"); return -1; } diff --git a/libweston/backend-drm/modes.c b/libweston/backend-drm/modes.c index a228572b..c904b1b2 100644 --- a/libweston/backend-drm/modes.c +++ b/libweston/backend-drm/modes.c @@ -98,9 +98,9 @@ drm_subpixel_to_wayland(int drm_value) } int -drm_mode_ensure_blob(struct drm_backend *backend, struct drm_mode *mode) +drm_mode_ensure_blob(struct drm_device *device, struct drm_mode *mode) { - struct drm_device *device = backend->drm; + struct drm_backend *backend = device->backend; int ret; if (mode->blob_id) @@ -321,8 +321,7 @@ find_and_parse_output_edid(struct drm_head *head, const char **serial_number, uint32_t *eotf_mask) { - struct drm_backend *backend = head->backend; - struct drm_device *device = backend->drm; + struct drm_device *device = head->connector.device; drmModePropertyBlobPtr edid_blob = NULL; uint32_t blob_id; int rc; @@ -363,7 +362,7 @@ prune_eotf_modes_by_kms_support(struct drm_head *head, uint32_t *eotf_mask) /* Without the KMS property, cannot do anything but SDR. */ info = &head->connector.props[WDRM_CONNECTOR_HDR_OUTPUT_METADATA]; - if (!head->backend->drm->atomic_modeset || info->prop_id == 0) + if (!head->connector.device->atomic_modeset || info->prop_id == 0) *eotf_mask = WESTON_EOTF_MODE_SDR; } @@ -427,10 +426,8 @@ drm_output_add_mode(struct drm_output *output, const drmModeModeInfo *info) * Destroys a mode, and removes it from the list. */ static void -drm_output_destroy_mode(struct drm_backend *backend, struct drm_mode *mode) +drm_output_destroy_mode(struct drm_device *device, struct drm_mode *mode) { - struct drm_device *device = backend->drm; - if (mode->blob_id) drmModeDestroyPropertyBlob(device->drm.fd, mode->blob_id); wl_list_remove(&mode->base.link); @@ -439,16 +436,16 @@ drm_output_destroy_mode(struct drm_backend *backend, struct drm_mode *mode) /** Destroy a list of drm_modes * - * @param backend The backend for releasing mode property blobs. + * @param device The device for releasing mode property blobs. * @param mode_list The list linked by drm_mode::base.link. */ void -drm_mode_list_destroy(struct drm_backend *backend, struct wl_list *mode_list) +drm_mode_list_destroy(struct drm_device *device, struct wl_list *mode_list) { struct drm_mode *mode, *next; wl_list_for_each_safe(mode, next, mode_list, base.link) - drm_output_destroy_mode(backend, mode); + drm_output_destroy_mode(device, mode); } void @@ -567,7 +564,7 @@ update_head_from_connector(struct drm_head *head) * Find the most suitable mode to use for initial setup (or reconfiguration on * hotplug etc) for a DRM output. * - * @param backend the DRM backend + * @param device the DRM device * @param output DRM output to choose mode for * @param mode Strategy and preference to use when choosing mode * @param modeline Manually-entered mode (may be NULL) @@ -575,13 +572,12 @@ update_head_from_connector(struct drm_head *head) * @returns A mode from the output's mode list, or NULL if none available */ static struct drm_mode * -drm_output_choose_initial_mode(struct drm_backend *backend, +drm_output_choose_initial_mode(struct drm_device *device, struct drm_output *output, enum weston_drm_backend_output_mode mode, const char *modeline, const drmModeModeInfo *current_mode) { - struct drm_device *device = backend->drm; struct drm_mode *preferred = NULL; struct drm_mode *current = NULL; struct drm_mode *configured = NULL; @@ -744,6 +740,7 @@ drm_output_try_add_mode(struct drm_output *output, const drmModeModeInfo *info) struct weston_mode *base; struct drm_mode *mode = NULL; struct drm_backend *backend; + struct drm_device *device; const drmModeModeInfo *chosen = NULL; assert(info); @@ -758,7 +755,8 @@ drm_output_try_add_mode(struct drm_output *output, const drmModeModeInfo *info) if (chosen == info) { assert(mode); backend = to_drm_backend(output->base.compositor); - drm_output_destroy_mode(backend, mode); + device = backend->drm; + drm_output_destroy_mode(device, mode); chosen = NULL; } @@ -786,6 +784,7 @@ static int drm_output_update_modelist_from_heads(struct drm_output *output) { struct drm_backend *backend = to_drm_backend(output->base.compositor); + struct drm_device *device = backend->drm; struct weston_head *head_base; struct drm_head *head; drmModeConnector *conn; @@ -794,7 +793,7 @@ drm_output_update_modelist_from_heads(struct drm_output *output) assert(!output->base.enabled); - drm_mode_list_destroy(backend, &output->base.mode_list); + drm_mode_list_destroy(device, &output->base.mode_list); wl_list_for_each(head_base, &output->base.head_list, output_link) { head = to_drm_head(head_base); @@ -816,6 +815,7 @@ drm_output_set_mode(struct weston_output *base, { struct drm_output *output = to_drm_output(base); struct drm_backend *b = to_drm_backend(base->compositor); + struct drm_device *device = b->drm; struct drm_head *head = to_drm_head(weston_output_get_first_head(base)); struct drm_mode *current; @@ -826,7 +826,7 @@ drm_output_set_mode(struct weston_output *base, if (drm_output_update_modelist_from_heads(output) < 0) return -1; - current = drm_output_choose_initial_mode(b, output, mode, modeline, + current = drm_output_choose_initial_mode(device, output, mode, modeline, &head->inherited_mode); if (!current) return -1; diff --git a/libweston/backend-drm/state-helpers.c b/libweston/backend-drm/state-helpers.c index 0e8e45e6..4077c8aa 100644 --- a/libweston/backend-drm/state-helpers.c +++ b/libweston/backend-drm/state-helpers.c @@ -73,7 +73,6 @@ drm_plane_state_alloc(struct drm_output_state *state_output, void drm_plane_state_free(struct drm_plane_state *state, bool force) { - struct drm_backend *backend; struct drm_device *device; if (!state) @@ -89,8 +88,7 @@ drm_plane_state_free(struct drm_plane_state *state, bool force) * by the kernel, which means we can safely discard it. */ if (state->damage_blob_id != 0) { - backend = state->plane->backend; - device = backend->drm; + device = state->plane->device; drmModeDestroyPropertyBlob(device->drm.fd, state->damage_blob_id); diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index 105c79e7..b2d0549f 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -162,8 +162,7 @@ out: static void cursor_bo_update(struct drm_plane_state *plane_state, struct weston_view *ev) { - struct drm_backend *b = plane_state->plane->backend; - struct drm_device *device = b->drm; + struct drm_device *device = plane_state->plane->device; struct gbm_bo *bo = plane_state->fb->bo; struct weston_buffer *buffer = ev->surface->buffer_ref.buffer; uint32_t buf[device->cursor_width * device->cursor_height]; From d990c6a939f0fbda038603745eea9b96f53c668f Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Tue, 30 Nov 2021 09:57:53 +0100 Subject: [PATCH 331/609] backend-drm: ignore heads from other devices The compositor lists the heads from all devices, but we must only disable the connectors that belong to the current device. Therefore, other heads must be ignored. Signed-off-by: Michael Tretter --- libweston/backend-drm/kms.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libweston/backend-drm/kms.c b/libweston/backend-drm/kms.c index c7a067dc..35d13b22 100644 --- a/libweston/backend-drm/kms.c +++ b/libweston/backend-drm/kms.c @@ -1112,6 +1112,8 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state, head = to_drm_head(head_base); connector_id = head->connector.connector_id; + if (head->connector.device != device) + continue; drm_debug(b, "\t\t[atomic] disabling inactive head %s\n", head_base->name); From 6e36787dfd9268dabe19c06a9e6a5f4ec7c241b4 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Tue, 8 Mar 2022 15:47:04 +0100 Subject: [PATCH 332/609] backend-drm: handle hotplug events per drm device If Weston receives a hotplug event, it has to check if the hotplug device actually belongs to the drm device before updating the heads of the device. The hotplug event should only remove heads that belong to the device and must not change heads of other devices. Signed-off-by: Michael Tretter --- libweston/backend-drm/drm.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index b37705fb..d2d95599 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -2422,9 +2422,10 @@ resources_has_connector(drmModeRes *resources, uint32_t connector_id) } static void -drm_backend_update_connectors(struct drm_backend *b, struct udev_device *drm_device) +drm_backend_update_connectors(struct drm_device *device, + struct udev_device *drm_device) { - struct drm_device *device = b->drm; + struct drm_backend *b = device->backend; drmModeRes *resources; drmModeConnector *conn; struct weston_head *base, *base_next; @@ -2472,6 +2473,9 @@ drm_backend_update_connectors(struct drm_backend *b, struct udev_device *drm_dev head = to_drm_head(base); connector_id = head->connector.connector_id; + if (head->connector.device != device) + continue; + if (resources_has_connector(resources, connector_id)) continue; @@ -2544,9 +2548,8 @@ drm_backend_update_conn_props(struct drm_backend *b, } static int -udev_event_is_hotplug(struct drm_backend *b, struct udev_device *udev_device) +udev_event_is_hotplug(struct drm_device *device, struct udev_device *udev_device) { - struct drm_device *device = b->drm; const char *sysnum; const char *val; @@ -2595,11 +2598,11 @@ udev_drm_event(int fd, uint32_t mask, void *data) event = udev_monitor_receive_device(b->udev_monitor); - if (udev_event_is_hotplug(b, event)) { + if (udev_event_is_hotplug(b->drm, event)) { if (udev_event_is_conn_prop_change(b, event, &conn_id, &prop_id)) drm_backend_update_conn_props(b, conn_id, prop_id); else - drm_backend_update_connectors(b, event); + drm_backend_update_connectors(b->drm, event); } udev_device_unref(event); From deebfd99e38242eb6ed662c7d8c7fd7e27e4b818 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Wed, 3 Nov 2021 16:36:17 +0100 Subject: [PATCH 333/609] backend-drm: get the drm device from the output If we have multiple drm devices, we cannot use the drm device from the backend, because we would only get the primary device and not the device of the output. Signed-off-by: Michael Tretter --- libweston/backend-drm/drm-gbm.c | 5 ++- libweston/backend-drm/drm.c | 45 ++++++++++++--------------- libweston/backend-drm/fb.c | 2 +- libweston/backend-drm/kms.c | 22 ++++++------- libweston/backend-drm/modes.c | 15 +++------ libweston/backend-drm/state-propose.c | 25 ++++++++------- 6 files changed, 49 insertions(+), 65 deletions(-) diff --git a/libweston/backend-drm/drm-gbm.c b/libweston/backend-drm/drm-gbm.c index afefffbf..c8c84fd1 100644 --- a/libweston/backend-drm/drm-gbm.c +++ b/libweston/backend-drm/drm-gbm.c @@ -146,7 +146,7 @@ static void drm_output_fini_cursor_egl(struct drm_output *output) static int drm_output_init_cursor_egl(struct drm_output *output, struct drm_backend *b) { - struct drm_device *device = b->drm; + struct drm_device *device = output->device; unsigned int i; /* No point creating cursors if we don't have a plane for them. */ @@ -290,8 +290,7 @@ struct drm_fb * drm_output_render_gl(struct drm_output_state *state, pixman_region32_t *damage) { struct drm_output *output = state->output; - struct drm_backend *b = to_drm_backend(output->base.compositor); - struct drm_device *device = b->drm; + struct drm_device *device = output->device; struct gbm_bo *bo; struct drm_fb *ret; diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index d2d95599..af95575c 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -270,8 +270,7 @@ void drm_output_update_complete(struct drm_output *output, uint32_t flags, unsigned int sec, unsigned int usec) { - struct drm_backend *b = to_drm_backend(output->base.compositor); - struct drm_device *device = b->drm; + struct drm_device *device = output->device; struct drm_plane_state *ps; struct timespec ts; @@ -352,13 +351,13 @@ void drm_output_render(struct drm_output_state *state, pixman_region32_t *damage) { struct drm_output *output = state->output; + struct drm_device *device = output->device; struct weston_compositor *c = output->base.compositor; struct drm_plane_state *scanout_state; struct drm_plane *scanout_plane = output->scanout_plane; struct drm_property_info *damage_info = &scanout_plane->props[WDRM_PLANE_FB_DAMAGE_CLIPS]; - struct drm_backend *b = to_drm_backend(c); - struct drm_device *device = b->drm; + struct drm_backend *b = device->backend; struct drm_fb *fb; pixman_region32_t scanout_damage; pixman_box32_t *rects; @@ -524,9 +523,9 @@ drm_output_start_repaint_loop(struct weston_output *output_base) struct drm_output *output = to_drm_output(output_base); struct drm_pending_state *pending_state; struct drm_plane *scanout_plane = output->scanout_plane; - struct drm_backend *backend = - to_drm_backend(output_base->compositor); - struct drm_device *device = backend->drm; + struct drm_device *device = output->device; + struct drm_backend *backend = device->backend; + struct weston_compositor *compositor = backend->compositor; struct timespec ts, tnow; struct timespec vbl2now; int64_t refresh_nsec; @@ -566,7 +565,7 @@ drm_output_start_repaint_loop(struct weston_output *output_base) * Stale ts could happen on Linux 3.17+, so make sure it * is not older than 1 refresh duration since now. */ - weston_compositor_read_presentation_clock(backend->compositor, + weston_compositor_read_presentation_clock(compositor, &tnow); timespec_sub(&vbl2now, &tnow, &ts); refresh_nsec = @@ -688,8 +687,8 @@ static int drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mode) { struct drm_output *output = to_drm_output(output_base); - struct drm_backend *b = to_drm_backend(output_base->compositor); - struct drm_device *device = b->drm; + struct drm_device *device = output->device; + struct drm_backend *b = device->backend; struct drm_mode *drm_mode = drm_output_choose_mode(output, mode); if (!drm_mode) { @@ -1049,8 +1048,7 @@ static void drm_set_dpms(struct weston_output *output_base, enum dpms_enum level) { struct drm_output *output = to_drm_output(output_base); - struct drm_backend *b = to_drm_backend(output_base->compositor); - struct drm_device *device = b->drm; + struct drm_device *device = output->device; struct drm_pending_state *pending_state = device->repaint_data; struct drm_output_state *state; int ret; @@ -1158,7 +1156,7 @@ make_connector_name(const drmModeConnector *con) static int drm_output_init_pixman(struct drm_output *output, struct drm_backend *b) { - struct drm_device *device = b->drm; + struct drm_device *device = output->device; int w = output->base.current_mode->width; int h = output->base.current_mode->height; uint32_t format = output->gbm_format; @@ -1390,8 +1388,7 @@ drm_output_set_seat(struct weston_output *base, static int drm_output_init_gamma_size(struct drm_output *output) { - struct drm_backend *backend = to_drm_backend(output->base.compositor); - struct drm_device *device = backend->drm; + struct drm_device *device = output->device; drmModeCrtc *crtc; assert(output->base.compositor); @@ -1437,8 +1434,9 @@ drm_connector_get_possible_crtcs_mask(struct drm_connector *connector) static struct drm_crtc * drm_output_pick_crtc(struct drm_output *output) { - struct drm_backend *backend; - struct drm_device *device; + struct drm_device *device = output->device; + struct drm_backend *backend = device->backend; + struct weston_compositor *compositor = backend->compositor; struct weston_head *base; struct drm_head *head; struct drm_crtc *crtc; @@ -1451,9 +1449,6 @@ drm_output_pick_crtc(struct drm_output *output) unsigned int i; bool match; - backend = to_drm_backend(output->base.compositor); - device = backend->drm; - /* This algorithm ignores drmModeEncoder::possible_clones restriction, * because it is more often set wrong than not in the kernel. */ @@ -1494,8 +1489,7 @@ drm_output_pick_crtc(struct drm_output *output) * If they did, this is not the best CRTC as it might be needed * for another output we haven't enabled yet. */ match = false; - wl_list_for_each(base, &backend->compositor->head_list, - compositor_link) { + wl_list_for_each(base, &compositor->head_list, compositor_link) { head = to_drm_head(base); if (head->base.output == &output->base) @@ -1636,7 +1630,7 @@ static int drm_output_init_planes(struct drm_output *output) { struct drm_backend *b = to_drm_backend(output->base.compositor); - struct drm_device *device = b->drm; + struct drm_device *device = output->device; output->scanout_plane = drm_output_find_special_plane(device, output, @@ -1674,7 +1668,7 @@ static void drm_output_deinit_planes(struct drm_output *output) { struct drm_backend *b = to_drm_backend(output->base.compositor); - struct drm_device *device = b->drm; + struct drm_device *device = output->device; /* If the compositor is already shutting down, the planes have already * been destroyed. */ @@ -1810,7 +1804,8 @@ static int drm_output_enable(struct weston_output *base) { struct drm_output *output = to_drm_output(base); - struct drm_backend *b = to_drm_backend(base->compositor); + struct drm_device *device = output->device; + struct drm_backend *b = device->backend; int ret; assert(!output->virtual); diff --git a/libweston/backend-drm/fb.c b/libweston/backend-drm/fb.c index e6881e8a..05b4988f 100644 --- a/libweston/backend-drm/fb.c +++ b/libweston/backend-drm/fb.c @@ -532,7 +532,7 @@ drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev, { struct drm_output *output = state->output; struct drm_backend *b = to_drm_backend(output->base.compositor); - struct drm_device *device = b->drm; + struct drm_device *device = output->device; struct weston_buffer *buffer = ev->surface->buffer_ref.buffer; struct drm_buffer_fb *buf_fb; bool is_opaque = weston_view_is_opaque(ev, &ev->transform.boundingbox); diff --git a/libweston/backend-drm/kms.c b/libweston/backend-drm/kms.c index 35d13b22..4b22f69d 100644 --- a/libweston/backend-drm/kms.c +++ b/libweston/backend-drm/kms.c @@ -514,9 +514,7 @@ drm_output_set_gamma(struct weston_output *output_base, { int rc; struct drm_output *output = to_drm_output(output_base); - struct drm_backend *backend = - to_drm_backend(output->base.compositor); - struct drm_device *device = backend->drm; + struct drm_device *device = output->device; /* check */ if (output_base->gamma_size != size) @@ -540,8 +538,8 @@ drm_output_assign_state(struct drm_output_state *state, enum drm_state_apply_mode mode) { struct drm_output *output = state->output; - struct drm_backend *b = to_drm_backend(output->base.compositor); - struct drm_device *device = b->drm; + struct drm_device *device = output->device; + struct drm_backend *b = device->backend; struct drm_plane_state *plane_state; struct drm_head *head; @@ -599,8 +597,7 @@ static void drm_output_set_cursor(struct drm_output_state *output_state) { struct drm_output *output = output_state->output; - struct drm_backend *b = to_drm_backend(output->base.compositor); - struct drm_device *device = b->drm; + struct drm_device *device = output->device; struct drm_crtc *crtc = output->crtc; struct drm_plane *plane = output->cursor_plane; struct drm_plane_state *state; @@ -653,8 +650,8 @@ static int drm_output_apply_state_legacy(struct drm_output_state *state) { struct drm_output *output = state->output; - struct drm_backend *backend = to_drm_backend(output->base.compositor); - struct drm_device *device = backend->drm; + struct drm_device *device = output->device; + struct drm_backend *backend = device->backend; struct drm_plane *scanout_plane = output->scanout_plane; struct drm_crtc *crtc = output->crtc; struct drm_property_info *dpms_prop; @@ -941,8 +938,8 @@ drm_output_apply_state_atomic(struct drm_output_state *state, uint32_t *flags) { struct drm_output *output = state->output; - struct drm_backend *b = to_drm_backend(output->base.compositor); - struct drm_device *device = b->drm; + struct drm_device *device = output->device; + struct drm_backend *b = device->backend; struct drm_crtc *crtc = output->crtc; struct drm_plane_state *plane_state; struct drm_mode *current_mode = to_drm_mode(output->base.current_mode); @@ -1387,8 +1384,7 @@ page_flip_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void *data) { struct drm_output *output = data; - struct drm_backend *b = to_drm_backend(output->base.compositor); - struct drm_device *device = b->drm; + struct drm_device *device = output->device; uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_VSYNC | WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION | WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK; diff --git a/libweston/backend-drm/modes.c b/libweston/backend-drm/modes.c index c904b1b2..ae3a35d8 100644 --- a/libweston/backend-drm/modes.c +++ b/libweston/backend-drm/modes.c @@ -489,11 +489,9 @@ drm_output_choose_mode(struct drm_output *output, struct drm_mode *tmp_mode = NULL, *mode_fall_back = NULL, *mode; enum weston_mode_aspect_ratio src_aspect = WESTON_MODE_PIC_AR_NONE; enum weston_mode_aspect_ratio target_aspect = WESTON_MODE_PIC_AR_NONE; - struct drm_backend *b; struct drm_device *device; - b = to_drm_backend(output->base.compositor); - device = b->drm; + device = output->device; target_aspect = target_mode->aspect_ratio; src_aspect = output->base.current_mode->aspect_ratio; if (output->base.current_mode->width == target_mode->width && @@ -739,8 +737,7 @@ drm_output_try_add_mode(struct drm_output *output, const drmModeModeInfo *info) { struct weston_mode *base; struct drm_mode *mode = NULL; - struct drm_backend *backend; - struct drm_device *device; + struct drm_device *device = output->device; const drmModeModeInfo *chosen = NULL; assert(info); @@ -754,8 +751,6 @@ drm_output_try_add_mode(struct drm_output *output, const drmModeModeInfo *info) if (chosen == info) { assert(mode); - backend = to_drm_backend(output->base.compositor); - device = backend->drm; drm_output_destroy_mode(device, mode); chosen = NULL; } @@ -783,8 +778,7 @@ drm_output_try_add_mode(struct drm_output *output, const drmModeModeInfo *info) static int drm_output_update_modelist_from_heads(struct drm_output *output) { - struct drm_backend *backend = to_drm_backend(output->base.compositor); - struct drm_device *device = backend->drm; + struct drm_device *device = output->device; struct weston_head *head_base; struct drm_head *head; drmModeConnector *conn; @@ -814,8 +808,7 @@ drm_output_set_mode(struct weston_output *base, const char *modeline) { struct drm_output *output = to_drm_output(base); - struct drm_backend *b = to_drm_backend(base->compositor); - struct drm_device *device = b->drm; + struct drm_device *device = output->device; struct drm_head *head = to_drm_head(weston_output_get_first_head(base)); struct drm_mode *current; diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index b2d0549f..83b0e049 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -83,10 +83,9 @@ drm_output_try_view_on_plane(struct drm_plane *plane, struct drm_fb *fb, uint64_t zpos) { struct drm_output *output = output_state->output; - struct weston_compositor *ec = output->base.compositor; struct weston_surface *surface = ev->surface; - struct drm_backend *b = to_drm_backend(ec); - struct drm_device *device = b->drm; + struct drm_device *device = output->device; + struct drm_backend *b = device->backend; struct drm_plane_state *state = NULL; assert(!device->sprites_are_broken); @@ -162,7 +161,8 @@ out: static void cursor_bo_update(struct drm_plane_state *plane_state, struct weston_view *ev) { - struct drm_device *device = plane_state->plane->device; + struct drm_output *output = plane_state->output; + struct drm_device *device = output->device; struct gbm_bo *bo = plane_state->fb->bo; struct weston_buffer *buffer = ev->surface->buffer_ref.buffer; uint32_t buf[device->cursor_width * device->cursor_height]; @@ -194,8 +194,8 @@ drm_output_prepare_cursor_view(struct drm_output_state *output_state, struct weston_view *ev, uint64_t zpos) { struct drm_output *output = output_state->output; - struct drm_backend *b = to_drm_backend(output->base.compositor); - struct drm_device *device = b->drm; + struct drm_device *device = output->device; + struct drm_backend *b = device->backend; struct drm_plane *plane = output->cursor_plane; struct drm_plane_state *plane_state; bool needs_update = false; @@ -430,8 +430,8 @@ drm_output_find_plane_for_view(struct drm_output_state *state, uint64_t current_lowest_zpos) { struct drm_output *output = state->output; - struct drm_backend *b = to_drm_backend(output->base.compositor); - struct drm_device *device = b->drm; + struct drm_device *device = output->device; + struct drm_backend *b = device->backend; struct drm_plane_state *ps = NULL; struct drm_plane *plane; @@ -636,7 +636,8 @@ drm_output_propose_state(struct weston_output *output_base, enum drm_output_propose_state_mode mode) { struct drm_output *output = to_drm_output(output_base); - struct drm_backend *b = to_drm_backend(output->base.compositor); + struct drm_device *device = output->device; + struct drm_backend *b = device->backend; struct weston_paint_node *pnode; struct drm_output_state *state; struct drm_plane_state *scanout_state = NULL; @@ -915,10 +916,10 @@ err: void drm_assign_planes(struct weston_output *output_base) { - struct drm_backend *b = to_drm_backend(output_base->compositor); - struct drm_device *device = b->drm; - struct drm_pending_state *pending_state = device->repaint_data; struct drm_output *output = to_drm_output(output_base); + struct drm_device *device = output->device; + struct drm_backend *b = device->backend; + struct drm_pending_state *pending_state = device->repaint_data; struct drm_output_state *state = NULL; struct drm_plane_state *plane_state; struct weston_paint_node *pnode; From dfceb60274a2eb16f09a75d5a6357f58a94ca700 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Thu, 4 Nov 2021 10:50:38 +0100 Subject: [PATCH 334/609] backend-drm: explicitly pass device to initialization The drm_device is initialized as a side effect of the (badly named) drm_device_is_kms function. Explicitly pass the drm_device to be able to initialize kms devices that are not the main drm device of the drm backend. Signed-off-by: Michael Tretter --- libweston/backend-drm/drm.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index af95575c..aa55fc41 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -2734,9 +2734,9 @@ drm_device_changed(struct weston_compositor *compositor, * sets b->drm.fd and b->drm.filename to the opened device. */ static bool -drm_device_is_kms(struct drm_backend *b, struct udev_device *udev_device) +drm_device_is_kms(struct drm_backend *b, struct drm_device *device, + struct udev_device *udev_device) { - struct drm_device *device = b->drm; struct weston_compositor *compositor = b->compositor; const char *filename = udev_device_get_devnode(udev_device); const char *sysnum = udev_device_get_sysnum(udev_device); @@ -2847,7 +2847,7 @@ find_primary_gpu(struct drm_backend *b, const char *seat) /* Make sure this device is actually capable of modesetting; * if this call succeeds, device->drm.{fd,filename} will be set, * and any old values freed. */ - if (!drm_device_is_kms(b, dev)) { + if (!drm_device_is_kms(b, b->drm, dev)) { udev_device_unref(dev); continue; } @@ -2877,9 +2877,9 @@ find_primary_gpu(struct drm_backend *b, const char *seat) } static struct udev_device * -open_specific_drm_device(struct drm_backend *b, const char *name) +open_specific_drm_device(struct drm_backend *b, struct drm_device *device, + const char *name) { - struct drm_device *device = b->drm; struct udev_device *udev_device; udev_device = udev_device_new_from_subsystem_sysname(b->udev, "drm", name); @@ -2888,7 +2888,7 @@ open_specific_drm_device(struct drm_backend *b, const char *name) return NULL; } - if (!drm_device_is_kms(b, udev_device)) { + if (!drm_device_is_kms(b, device, udev_device)) { udev_device_unref(udev_device); weston_log("ERROR: DRM device '%s' is not a KMS device.\n", name); return NULL; @@ -3109,7 +3109,8 @@ drm_backend_create(struct weston_compositor *compositor, wl_signal_add(&compositor->session_signal, &b->session_listener); if (config->specific_device) - drm_device = open_specific_drm_device(b, config->specific_device); + drm_device = open_specific_drm_device(b, device, + config->specific_device); else drm_device = find_primary_gpu(b, seat_id); if (drm_device == NULL) { From e2426960d479a84ad66ee865404a925d66d9a55b Mon Sep 17 00:00:00 2001 From: Michael Olbrich Date: Mon, 15 Mar 2021 09:43:37 +0100 Subject: [PATCH 335/609] compositor: set transform.opaque for surfaces without alpha channel If surface->is_opaque is set then we can assume that the whole surface is opaque. In the trivial case (no transformation or translation only) this means that transform.boundingbox is exactly the view area and is fully opaque. So it can be used for transform.opaque. This is important because damage calculation uses transform.opaque. Without this, anything underneath a surface without an explicit opaque region but a pixel format without alpha channel is drawn unnecessarily. Signed-off-by: Michael Olbrich --- libweston/compositor.c | 46 +++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/libweston/compositor.c b/libweston/compositor.c index 45502e18..24c6313d 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -1513,15 +1513,20 @@ weston_view_update_transform_disable(struct weston_view *view) view->geometry.x, view->geometry.y); if (view->alpha == 1.0) { - pixman_region32_copy(&view->transform.opaque, - &view->surface->opaque); - if (view->geometry.scissor_enabled) - pixman_region32_intersect(&view->transform.opaque, - &view->transform.opaque, - &view->geometry.scissor); - pixman_region32_translate(&view->transform.opaque, - view->geometry.x, - view->geometry.y); + if (view->surface->is_opaque) { + pixman_region32_copy(&view->transform.opaque, + &view->transform.boundingbox); + } else { + pixman_region32_copy(&view->transform.opaque, + &view->surface->opaque); + if (view->geometry.scissor_enabled) + pixman_region32_intersect(&view->transform.opaque, + &view->transform.opaque, + &view->geometry.scissor); + pixman_region32_translate(&view->transform.opaque, + view->geometry.x, + view->geometry.y); + } } } @@ -1568,15 +1573,20 @@ weston_view_update_transform_enable(struct weston_view *view) if (view->alpha == 1.0 && matrix->type == WESTON_MATRIX_TRANSFORM_TRANSLATE) { - pixman_region32_copy(&view->transform.opaque, - &view->surface->opaque); - if (view->geometry.scissor_enabled) - pixman_region32_intersect(&view->transform.opaque, - &view->transform.opaque, - &view->geometry.scissor); - pixman_region32_translate(&view->transform.opaque, - matrix->d[12], - matrix->d[13]); + if (view->surface->is_opaque) { + pixman_region32_copy(&view->transform.opaque, + &view->transform.boundingbox); + } else { + pixman_region32_copy(&view->transform.opaque, + &view->surface->opaque); + if (view->geometry.scissor_enabled) + pixman_region32_intersect(&view->transform.opaque, + &view->transform.opaque, + &view->geometry.scissor); + pixman_region32_translate(&view->transform.opaque, + matrix->d[12], + matrix->d[13]); + } } return 0; From 81912dc2a69f24c8fbcb43a2bc1f7860f3085c01 Mon Sep 17 00:00:00 2001 From: Michael Olbrich Date: Wed, 10 Mar 2021 09:10:25 +0100 Subject: [PATCH 336/609] compositor: improve opacity handling for scaled surfaces Currently, the opaque is discarded for all transformations other than a simple translation, because correctly transforming the opaque area is not possible in general. However, there is one simple case that is probably the most common one: A fully opaque surface that is translated and scaled. In this case the opaque area is simply the new bounding box. So set the transformed opaque area accordingly. Signed-off-by: Michael Olbrich --- libweston/compositor.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/libweston/compositor.c b/libweston/compositor.c index 24c6313d..128ee232 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -1569,7 +1569,6 @@ weston_view_update_transform_enable(struct weston_view *view) surfbox = pixman_region32_extents(&surfregion); view_compute_bbox(view, surfbox, &view->transform.boundingbox); - pixman_region32_fini(&surfregion); if (view->alpha == 1.0 && matrix->type == WESTON_MATRIX_TRANSFORM_TRANSLATE) { @@ -1587,7 +1586,19 @@ weston_view_update_transform_enable(struct weston_view *view) matrix->d[12], matrix->d[13]); } + } else if (view->alpha == 1.0 && + matrix->type < WESTON_MATRIX_TRANSFORM_ROTATE && + pixman_region32_n_rects(&surfregion) == 1 && + (pixman_region32_equal(&surfregion, &view->surface->opaque) || + view->surface->is_opaque)) { + /* The whole surface is opaque and it is only translated and + * scaled and after applying the scissor, the result is still + * a single rectangle. In this case the boundingbox matches the + * view exactly and can be used as opaque area. */ + pixman_region32_copy(&view->transform.opaque, + &view->transform.boundingbox); } + pixman_region32_fini(&surfregion); return 0; } From 6234cb98d1b2b3b3cd39b18f69ee8cf4aa7efe32 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 2 Jun 2022 11:24:52 +0300 Subject: [PATCH 337/609] gl-renderer: fix performance regression in frag When color management is disabled, the fragment shader was still first ensuring straight alpha and then immediately just going back to pre-multiplied. This is near-impossible for a shader compiler to optimize out, I guess because of the if-statement to handle division by zero. Having view alpha applied in between certainly didn't make it easier. That causes extra fragment computations that are unnecessary. In the issue report this was found to cause a notable performance regression. Fix the performance regression by introducing special-case paths for when straight alpha is not needed. This skips the unnecessary computations. Fixes: https://gitlab.freedesktop.org/wayland/weston/-/issues/623 Fixes: 9a6a4e7032669be727c965ca19e3e30098c892e7 Signed-off-by: Pekka Paalanen --- libweston/renderer-gl/fragment.glsl | 44 ++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/libweston/renderer-gl/fragment.glsl b/libweston/renderer-gl/fragment.glsl index c49a6cd4..e8dae27d 100644 --- a/libweston/renderer-gl/fragment.glsl +++ b/libweston/renderer-gl/fragment.glsl @@ -77,6 +77,10 @@ compile_const bool c_green_tint = DEF_GREEN_TINT; compile_const int c_color_pre_curve = DEF_COLOR_PRE_CURVE; compile_const int c_color_mapping = DEF_COLOR_MAPPING; +compile_const bool c_need_color_pipeline = + c_color_pre_curve != SHADER_COLOR_CURVE_IDENTITY || + c_color_mapping != SHADER_COLOR_MAPPING_IDENTITY; + vec4 yuva2rgba(vec4 yuva) { @@ -245,9 +249,6 @@ color_mapping(vec3 color) vec4 color_pipeline(vec4 color) { - /* View alpha (opacity) */ - color.a *= alpha; - color.rgb = color_pre_curve(color.rgb); color.rgb = color_mapping(color.rgb); @@ -262,18 +263,35 @@ main() /* Electrical (non-linear) RGBA values, may be premult or not */ color = sample_input_texture(); - /* Ensure straight alpha */ - if (c_input_is_premult) { - if (color.a == 0.0) - color.rgb = vec3(0, 0, 0); - else - color.rgb *= 1.0 / color.a; - } + if (c_need_color_pipeline) { + /* Ensure straight alpha */ + if (c_input_is_premult) { + if (color.a == 0.0) + color.rgb = vec3(0, 0, 0); + else + color.rgb *= 1.0 / color.a; + } - color = color_pipeline(color); + color = color_pipeline(color); - /* pre-multiply for blending */ - color.rgb *= color.a; + /* View alpha (opacity) */ + color.a *= alpha; + + /* pre-multiply for blending */ + color.rgb *= color.a; + } else { + /* Fast path for disabled color management */ + + if (c_input_is_premult) { + /* View alpha (opacity) */ + color *= alpha; + } else { + /* View alpha (opacity) */ + color.a *= alpha; + /* pre-multiply for blending */ + color.rgb *= color.a; + } + } if (c_green_tint) color = vec4(0.0, 0.3, 0.0, 0.2) + color * 0.8; From 7fa9b153486edc6aa0ff53b91dad8e3aa6145768 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 20 May 2022 14:29:47 +0300 Subject: [PATCH 338/609] build: consolidate lcms2 dependencies It's bad form to set the same variable in multiple places, and not all of them were even equivalent. Move lcms2 finding to the root level build file only. It is still an optional dependency like before, and the if-not-found checks are still in place where actually needed. Signed-off-by: Pekka Paalanen --- compositor/meson.build | 5 ++--- libweston/color-lcms/meson.build | 1 - meson.build | 2 ++ tests/meson.build | 1 - 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/compositor/meson.build b/compositor/meson.build index 427167e7..eb3c9de5 100644 --- a/compositor/meson.build +++ b/compositor/meson.build @@ -97,18 +97,17 @@ if get_option('screenshare') endif if get_option('color-management-lcms') - config_h.set('HAVE_LCMS', '1') - srcs_lcms = [ 'cms-static.c', 'cms-helper.c', ] - dep_lcms2 = dependency('lcms2', required: false) if not dep_lcms2.found() error('cms-static requires lcms2 which was not found. Or, you can use \'-Dcolor-management-lcms=false\'.') endif + config_h.set('HAVE_LCMS', '1') + plugin_lcms = shared_library( 'cms-static', srcs_lcms, diff --git a/libweston/color-lcms/meson.build b/libweston/color-lcms/meson.build index 86e2871f..4aefd4b3 100644 --- a/libweston/color-lcms/meson.build +++ b/libweston/color-lcms/meson.build @@ -2,7 +2,6 @@ if not get_option('color-management-lcms') subdir_done() endif -dep_lcms2 = dependency('lcms2', version: '>= 2.9', required: false) if not dep_lcms2.found() error('color-lcms plugin requires lcms2 which was not found. Or, you can use \'-Dcolor-management-lcms=false\'.') endif diff --git a/meson.build b/meson.build index 47324bd8..994c7a56 100644 --- a/meson.build +++ b/meson.build @@ -158,6 +158,8 @@ if dep_libdrm_version.version_compare('>=2.4.107') config_h.set('HAVE_HUMAN_FORMAT_MODIFIER', '1') endif +dep_lcms2 = dependency('lcms2', version: '>= 2.9', required: false) + prog_python = import('python').find_installation('python3') files_xxd_py = files('tools/xxd.py') cmd_xxd = [ prog_python, files_xxd_py, '@INPUT@', '@OUTPUT@' ] diff --git a/tests/meson.build b/tests/meson.build index 388dcc9a..4dfd57e9 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -239,7 +239,6 @@ if get_option('renderer-gl') endif if get_option('color-management-lcms') - dep_lcms2 = dependency('lcms2', version: '>= 2.9', required: false) if not dep_lcms2.found() error('color-management-lcms tests require lcms2 which was not found. Or, you can use \'-Dcolor-management-lcms=false\'.') endif From 85738af912be3bfbd4377f305c6d8a981f647b02 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 20 May 2022 14:59:15 +0300 Subject: [PATCH 339/609] tests/color_util: protect header from re-reading Looks like this was forgotten, and I managed to get compiler errors about redeclaring all enums. Signed-off-by: Pekka Paalanen --- tests/color_util.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/color_util.h b/tests/color_util.h index f822d00a..3e8e2abd 100644 --- a/tests/color_util.h +++ b/tests/color_util.h @@ -24,6 +24,8 @@ * SOFTWARE. */ +#pragma once + #include #include From c8195289a73ca54309bcc030a07873364c56df47 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 10 May 2022 11:12:38 +0300 Subject: [PATCH 340/609] tests/color_util: refactor into color_float_apply_curve() Make process_pixel_using_pipeline() slightly easier to read by extracting a meaningful function. Pure refactoring, no changes. Signed-off-by: Pekka Paalanen --- tests/color_util.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/tests/color_util.c b/tests/color_util.c index aa24372d..66a23f3b 100644 --- a/tests/color_util.c +++ b/tests/color_util.c @@ -221,6 +221,17 @@ a8r8g8b8_to_float(uint32_t v) return cf; } +static struct color_float +color_float_apply_curve(enum transfer_fn fn, struct color_float c) +{ + unsigned i; + + for (i = 0; i < COLOR_CHAN_NUM; i++) + c.rgb[i] = apply_tone_curve(fn, c.rgb[i]); + + return c; +} + void process_pixel_using_pipeline(enum transfer_fn pre_curve, const struct lcmsMAT3 *mat, @@ -232,8 +243,7 @@ process_pixel_using_pipeline(enum transfer_fn pre_curve, struct color_float cf; float tmp; - for (i = 0; i < COLOR_CHAN_NUM; i++) - cf.rgb[i] = apply_tone_curve(pre_curve, in->rgb[i]); + cf = color_float_apply_curve(pre_curve, *in); for (i = 0; i < 3; i++) { tmp = 0.0f; @@ -242,6 +252,5 @@ process_pixel_using_pipeline(enum transfer_fn pre_curve, out->rgb[i] = tmp; } - for (i = 0; i < COLOR_CHAN_NUM; i++) - out->rgb[i] = apply_tone_curve(post_curve, out->rgb[i]); + *out = color_float_apply_curve(post_curve, *out); } From 53b126801836114bc627514233152d01c84118cf Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 10 May 2022 11:12:38 +0300 Subject: [PATCH 341/609] tests/color_util: refactor into color_float_apply_matrix() Make process_pixel_using_pipeline() slightly easier to read by extracting a meaningful function. Pure refactoring, no behavioral changes. Compared to previous, flip the scalar multiplication around, so that it matches the mathematical order of matrix-vector multiplication. Also document the layout conventions for lcmsVEC3 and lcmsMAT3. These follow the convention used in LittleCMS for cmsVEC3 and cmsMAT3, and are necessary to understand to review the matrix-vector multiplication for correctness. Signed-off-by: Pekka Paalanen --- tests/color_util.c | 36 +++++++++++++++++++++++++----------- tests/color_util.h | 6 ++++++ 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/tests/color_util.c b/tests/color_util.c index 66a23f3b..44d1e034 100644 --- a/tests/color_util.c +++ b/tests/color_util.c @@ -232,6 +232,29 @@ color_float_apply_curve(enum transfer_fn fn, struct color_float c) return c; } +/* + * Returns the result of the matrix-vector multiplication mat * c. + */ +struct color_float +color_float_apply_matrix(const struct lcmsMAT3 *mat, struct color_float c) +{ + struct color_float result; + unsigned i, j; + + /* + * The matrix has an array of columns, hence i indexes to rows and + * j indexes to columns. + */ + for (i = 0; i < 3; i++) { + result.rgb[i] = 0.0f; + for (j = 0; j < 3; j++) + result.rgb[i] += mat->v[j].n[i] * c.rgb[j]; + } + + result.a = c.a; + return result; +} + void process_pixel_using_pipeline(enum transfer_fn pre_curve, const struct lcmsMAT3 *mat, @@ -239,18 +262,9 @@ process_pixel_using_pipeline(enum transfer_fn pre_curve, const struct color_float *in, struct color_float *out) { - int i, j; struct color_float cf; - float tmp; cf = color_float_apply_curve(pre_curve, *in); - - for (i = 0; i < 3; i++) { - tmp = 0.0f; - for (j = 0; j < 3; j++) - tmp += cf.rgb[j] * mat->v[j].n[i]; - out->rgb[i] = tmp; - } - - *out = color_float_apply_curve(post_curve, *out); + cf = color_float_apply_matrix(mat, cf); + *out = color_float_apply_curve(post_curve, cf); } diff --git a/tests/color_util.h b/tests/color_util.h index 3e8e2abd..3fc21aa4 100644 --- a/tests/color_util.h +++ b/tests/color_util.h @@ -36,6 +36,7 @@ enum color_chan_index { COLOR_CHAN_NUM }; +/* column vector when used in linear algebra */ struct color_float { union { float rgb[COLOR_CHAN_NUM]; @@ -46,11 +47,13 @@ struct color_float { float a; }; +/* column vector */ struct lcmsVEC3 { float n[3]; }; struct lcmsMAT3 { + /* array of columns */ struct lcmsVEC3 v[3]; }; @@ -99,3 +102,6 @@ process_pixel_using_pipeline(enum transfer_fn pre_curve, enum transfer_fn post_curve, const struct color_float *in, struct color_float *out); + +struct color_float +color_float_apply_matrix(const struct lcmsMAT3 *mat, struct color_float c); From 8adbd3d8026d106ac01ce264f62c651316a51ee5 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 10 May 2022 11:34:57 +0300 Subject: [PATCH 342/609] tests/color_util: streamline sRGB_linearize/delinearize Re-use color_float_apply_curve() instead of open-coding it. Maybe makes reading the code a little easier. Signed-off-by: Pekka Paalanen --- tests/color_util.c | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/tests/color_util.c b/tests/color_util.c index 44d1e034..f00a2400 100644 --- a/tests/color_util.c +++ b/tests/color_util.c @@ -161,15 +161,6 @@ Power2_4_EOTF_inv(float o) return pow(o, 1./2.4); } -void -sRGB_linearize(struct color_float *cf) -{ - int i; - - for (i = 0; i < COLOR_CHAN_NUM; i++) - cf->rgb[i] = sRGB_EOTF(cf->rgb[i]); -} - static float apply_tone_curve(enum transfer_fn fn, float r) { @@ -199,15 +190,6 @@ apply_tone_curve(enum transfer_fn fn, float r) return ret; } -void -sRGB_delinearize(struct color_float *cf) -{ - int i; - - for (i = 0; i < COLOR_CHAN_NUM; i++) - cf->rgb[i] = sRGB_EOTF_inv(cf->rgb[i]); -} - struct color_float a8r8g8b8_to_float(uint32_t v) { @@ -232,6 +214,18 @@ color_float_apply_curve(enum transfer_fn fn, struct color_float c) return c; } +void +sRGB_linearize(struct color_float *cf) +{ + *cf = color_float_apply_curve(TRANSFER_FN_SRGB_EOTF, *cf); +} + +void +sRGB_delinearize(struct color_float *cf) +{ + *cf = color_float_apply_curve(TRANSFER_FN_SRGB_EOTF_INVERSE, *cf); +} + /* * Returns the result of the matrix-vector multiplication mat * c. */ From 6fa7ab5d5fa8e1d0130c59025b7c4b54aa9fbb1c Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 10 May 2022 11:49:01 +0300 Subject: [PATCH 343/609] tests/color_util: prettify arr_curves Fix up whitespace and document what this array is for. For the sake of slightly better readability. Signed-off-by: Pekka Paalanen --- tests/color_util.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/tests/color_util.c b/tests/color_util.c index f00a2400..e7aa51f4 100644 --- a/tests/color_util.c +++ b/tests/color_util.c @@ -50,26 +50,26 @@ struct color_tone_curve { double param[5]; }; +/* Mapping from enum transfer_fn to LittleCMS curve parameters. */ const struct color_tone_curve arr_curves[] = { - { - .fn = TRANSFER_FN_SRGB_EOTF, - .inv_fn = TRANSFER_FN_SRGB_EOTF_INVERSE, - .internal_type = 4, - .param = { 2.4, 1. / 1.055, 0.055 / 1.055, 1. / 12.92, 0.04045 } , - }, - { - .fn = TRANSFER_FN_ADOBE_RGB_EOTF, - .inv_fn = TRANSFER_FN_ADOBE_RGB_EOTF_INVERSE, - .internal_type = 1, - .param = { 563./256., 0.0, 0.0, 0.0 , 0.0 } , - }, - { - .fn = TRANSFER_FN_POWER2_4_EOTF, - .inv_fn = TRANSFER_FN_POWER2_4_EOTF_INVERSE, - .internal_type = 1, - .param = { 2.4, 0.0, 0.0, 0.0 , 0.0 } , - } - + { + .fn = TRANSFER_FN_SRGB_EOTF, + .inv_fn = TRANSFER_FN_SRGB_EOTF_INVERSE, + .internal_type = 4, + .param = { 2.4, 1. / 1.055, 0.055 / 1.055, 1. / 12.92, 0.04045 }, + }, + { + .fn = TRANSFER_FN_ADOBE_RGB_EOTF, + .inv_fn = TRANSFER_FN_ADOBE_RGB_EOTF_INVERSE, + .internal_type = 1, + .param = { 563./256., 0.0, 0.0, 0.0 , 0.0 }, + }, + { + .fn = TRANSFER_FN_POWER2_4_EOTF, + .inv_fn = TRANSFER_FN_POWER2_4_EOTF_INVERSE, + .internal_type = 1, + .param = { 2.4, 0.0, 0.0, 0.0 , 0.0 }, + } }; bool From fa477d24076fde3202e939c9dd716481bb84dca6 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 10 May 2022 14:20:58 +0300 Subject: [PATCH 344/609] tests/color_util: add TRANSFER_FN_IDENTITY This will be useful to make a curve in a color pipeline pass-through without needing to special-case skipping the curve. Signed-off-by: Pekka Paalanen --- tests/color_util.c | 3 +++ tests/color_util.h | 1 + 2 files changed, 4 insertions(+) diff --git a/tests/color_util.c b/tests/color_util.c index e7aa51f4..b8c66997 100644 --- a/tests/color_util.c +++ b/tests/color_util.c @@ -167,6 +167,9 @@ apply_tone_curve(enum transfer_fn fn, float r) float ret = 0; switch(fn) { + case TRANSFER_FN_IDENTITY: + ret = r; + break; case TRANSFER_FN_SRGB_EOTF: ret = sRGB_EOTF(r); break; diff --git a/tests/color_util.h b/tests/color_util.h index 3fc21aa4..b4cd1f55 100644 --- a/tests/color_util.h +++ b/tests/color_util.h @@ -58,6 +58,7 @@ struct lcmsMAT3 { }; enum transfer_fn { + TRANSFER_FN_IDENTITY, TRANSFER_FN_SRGB_EOTF, TRANSFER_FN_SRGB_EOTF_INVERSE, TRANSFER_FN_ADOBE_RGB_EOTF, From 141cd3021e250057fdd8d5d558af3a2418f5850b Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 10 May 2022 15:55:36 +0300 Subject: [PATCH 345/609] tests/color_util: add transfer_fn_invert() When defining a color space with a transfer function, this looks up the inverse transfer function without needing to store that separately. Signed-off-by: Pekka Paalanen --- tests/color_util.c | 23 +++++++++++++++++++++++ tests/color_util.h | 3 +++ 2 files changed, 26 insertions(+) diff --git a/tests/color_util.c b/tests/color_util.c index b8c66997..cc4d3fab 100644 --- a/tests/color_util.c +++ b/tests/color_util.c @@ -93,6 +93,29 @@ find_tone_curve_type(enum transfer_fn fn, int *type, double params[5]) return false; } +enum transfer_fn +transfer_fn_invert(enum transfer_fn fn) +{ + switch (fn) { + case TRANSFER_FN_ADOBE_RGB_EOTF: + return TRANSFER_FN_ADOBE_RGB_EOTF_INVERSE; + case TRANSFER_FN_ADOBE_RGB_EOTF_INVERSE: + return TRANSFER_FN_ADOBE_RGB_EOTF; + case TRANSFER_FN_IDENTITY: + return TRANSFER_FN_IDENTITY; + case TRANSFER_FN_POWER2_4_EOTF: + return TRANSFER_FN_POWER2_4_EOTF_INVERSE; + case TRANSFER_FN_POWER2_4_EOTF_INVERSE: + return TRANSFER_FN_POWER2_4_EOTF; + case TRANSFER_FN_SRGB_EOTF: + return TRANSFER_FN_SRGB_EOTF_INVERSE; + case TRANSFER_FN_SRGB_EOTF_INVERSE: + return TRANSFER_FN_SRGB_EOTF; + } + assert(0 && "bad transfer_fn"); + return 0; +} + /** * NaN comes out as is *This function is not intended for hiding NaN. diff --git a/tests/color_util.h b/tests/color_util.h index b4cd1f55..14fd6095 100644 --- a/tests/color_util.h +++ b/tests/color_util.h @@ -106,3 +106,6 @@ process_pixel_using_pipeline(enum transfer_fn pre_curve, struct color_float color_float_apply_matrix(const struct lcmsMAT3 *mat, struct color_float c); + +enum transfer_fn +transfer_fn_invert(enum transfer_fn fn); From c76e4abb604d751763c7b7c51a3b9663a511ffc8 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 10 May 2022 16:26:52 +0300 Subject: [PATCH 346/609] tests/color_util: add lcmsMAT3_invert() Needed to invert device-to-PCS color transformation matrices. Signed-off-by: Pekka Paalanen --- tests/color_util.c | 44 +++++++++++++++++++++++++++++++++++++++++++- tests/color_util.h | 3 +++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/tests/color_util.c b/tests/color_util.c index cc4d3fab..86d426f1 100644 --- a/tests/color_util.c +++ b/tests/color_util.c @@ -23,13 +23,17 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ + #include "config.h" + #include -#include "color_util.h" #include #include #include #include + +#include +#include "color_util.h" #include "shared/helpers.h" static_assert(sizeof(struct color_float) == 4 * sizeof(float), @@ -288,3 +292,41 @@ process_pixel_using_pipeline(enum transfer_fn pre_curve, cf = color_float_apply_matrix(mat, cf); *out = color_float_apply_curve(post_curve, cf); } + +static void +weston_matrix_from_lcmsMAT3(struct weston_matrix *w, const struct lcmsMAT3 *m) +{ + unsigned r, c; + + /* column-major */ + weston_matrix_init(w); + + for (c = 0; c < 3; c++) { + for (r = 0; r < 3; r++) + w->d[c * 4 + r] = m->v[c].n[r]; + } +} + +static void +lcmsMAT3_from_weston_matrix(struct lcmsMAT3 *m, const struct weston_matrix *w) +{ + unsigned r, c; + + for (c = 0; c < 3; c++) { + for (r = 0; r < 3; r++) + m->v[c].n[r] = w->d[c * 4 + r]; + } +} + +void +lcmsMAT3_invert(struct lcmsMAT3 *result, const struct lcmsMAT3 *mat) +{ + struct weston_matrix inv; + struct weston_matrix w; + int ret; + + weston_matrix_from_lcmsMAT3(&w, mat); + ret = weston_matrix_invert(&inv, &w); + assert(ret == 0); + lcmsMAT3_from_weston_matrix(result, &inv); +} diff --git a/tests/color_util.h b/tests/color_util.h index 14fd6095..ec442b80 100644 --- a/tests/color_util.h +++ b/tests/color_util.h @@ -109,3 +109,6 @@ color_float_apply_matrix(const struct lcmsMAT3 *mat, struct color_float c); enum transfer_fn transfer_fn_invert(enum transfer_fn fn); + +void +lcmsMAT3_invert(struct lcmsMAT3 *result, const struct lcmsMAT3 *mat); From 142d8e5125df70f30ba0e8ea939be4ee6644e3d7 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Wed, 18 May 2022 14:54:56 +0300 Subject: [PATCH 347/609] tests/color_util: add RGB diff stat These helpers allow collecting color difference statistics easily. To be used in color-shaper-matrix-test. Signed-off-by: Pekka Paalanen --- tests/color_util.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++ tests/color_util.h | 32 +++++++++++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/tests/color_util.c b/tests/color_util.c index 86d426f1..f51666dd 100644 --- a/tests/color_util.c +++ b/tests/color_util.c @@ -34,6 +34,7 @@ #include #include "color_util.h" +#include "weston-test-runner.h" #include "shared/helpers.h" static_assert(sizeof(struct color_float) == 4 * sizeof(float), @@ -330,3 +331,62 @@ lcmsMAT3_invert(struct lcmsMAT3 *result, const struct lcmsMAT3 *mat) assert(ret == 0); lcmsMAT3_from_weston_matrix(result, &inv); } + +void +scalar_stat_update(struct scalar_stat *stat, double val, struct color_float *pos) +{ + if (stat->count == 0 || stat->min > val) { + stat->min = val; + stat->min_pos = *pos; + } + + if (stat->count == 0 || stat->max < val) { + stat->max = val; + stat->max_pos = *pos; + } + + stat->sum += val; + stat->count++; +} + +float +scalar_stat_avg(const struct scalar_stat *stat) +{ + return stat->sum / stat->count; +} + +#define RGB888_FMT "(%3u, %3u, %3u)" +#define RGB888_VAL(cf) (unsigned)round((cf).r * 255.0), (unsigned)round((cf).g * 255.0), (unsigned)round((cf).b * 255.0) + +void +scalar_stat_print_rgb8bit(const struct scalar_stat *stat) +{ + testlog(" min %8.5f at " RGB888_FMT "\n", stat->min, RGB888_VAL(stat->min_pos)); + testlog(" max %8.5f at " RGB888_FMT "\n", stat->max, RGB888_VAL(stat->max_pos)); + testlog(" avg %8.5f\n", scalar_stat_avg(stat)); +} + +void +scalar_stat_print_float(const struct scalar_stat *stat) +{ + testlog(" min %11.5g at %.5f\n", stat->min, stat->min_pos.r); + testlog(" max %11.5g at %.5f\n", stat->max, stat->max_pos.r); + testlog(" avg %11.5g\n", scalar_stat_avg(stat)); +} + +void +rgb_diff_stat_update(struct rgb_diff_stat *stat, + struct color_float *ref, struct color_float *val) +{ + unsigned i; + double ssd = 0.0; + + for (i = 0; i < COLOR_CHAN_NUM; i++) { + double diff = val->rgb[i] - ref->rgb[i]; + + scalar_stat_update(&stat->rgb[i], diff, ref); + ssd += diff * diff; + } + + scalar_stat_update(&stat->two_norm, sqrt(ssd), ref); +} diff --git a/tests/color_util.h b/tests/color_util.h index ec442b80..6b6f96d7 100644 --- a/tests/color_util.h +++ b/tests/color_util.h @@ -112,3 +112,35 @@ transfer_fn_invert(enum transfer_fn fn); void lcmsMAT3_invert(struct lcmsMAT3 *result, const struct lcmsMAT3 *mat); + +struct scalar_stat { + double min; + struct color_float min_pos; + + double max; + struct color_float max_pos; + + double sum; + unsigned count; +}; + +struct rgb_diff_stat { + struct scalar_stat rgb[COLOR_CHAN_NUM]; + struct scalar_stat two_norm; +}; + +void +scalar_stat_update(struct scalar_stat *stat, double val, struct color_float *pos); + +float +scalar_stat_avg(const struct scalar_stat *stat); + +void +scalar_stat_print_rgb8bit(const struct scalar_stat *stat); + +void +scalar_stat_print_float(const struct scalar_stat *stat); + +void +rgb_diff_stat_update(struct rgb_diff_stat *stat, + struct color_float *ref, struct color_float *val); From 0225453fb13862a7688a4a670fe7e7d3be731b12 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 20 May 2022 15:09:30 +0300 Subject: [PATCH 348/609] tests/color_util: add transfer_fn_name() This helps reporting test results, then you can print the fn by name. Signed-off-by: Pekka Paalanen --- tests/color_util.c | 23 +++++++++++++++++++++++ tests/color_util.h | 3 +++ 2 files changed, 26 insertions(+) diff --git a/tests/color_util.c b/tests/color_util.c index f51666dd..0e0c738a 100644 --- a/tests/color_util.c +++ b/tests/color_util.c @@ -121,6 +121,29 @@ transfer_fn_invert(enum transfer_fn fn) return 0; } +const char * +transfer_fn_name(enum transfer_fn fn) +{ + switch (fn) { + case TRANSFER_FN_ADOBE_RGB_EOTF: + return "AdobeRGB EOTF"; + case TRANSFER_FN_ADOBE_RGB_EOTF_INVERSE: + return "inverse AdobeRGB EOTF"; + case TRANSFER_FN_IDENTITY: + return "identity"; + case TRANSFER_FN_POWER2_4_EOTF: + return "power 2.4"; + case TRANSFER_FN_POWER2_4_EOTF_INVERSE: + return "inverse power 2.4"; + case TRANSFER_FN_SRGB_EOTF: + return "sRGB EOTF"; + case TRANSFER_FN_SRGB_EOTF_INVERSE: + return "inverse sRGB EOTF"; + } + assert(0 && "bad transfer_fn"); + return 0; +} + /** * NaN comes out as is *This function is not intended for hiding NaN. diff --git a/tests/color_util.h b/tests/color_util.h index 6b6f96d7..27d7d3c8 100644 --- a/tests/color_util.h +++ b/tests/color_util.h @@ -110,6 +110,9 @@ color_float_apply_matrix(const struct lcmsMAT3 *mat, struct color_float c); enum transfer_fn transfer_fn_invert(enum transfer_fn fn); +const char * +transfer_fn_name(enum transfer_fn fn); + void lcmsMAT3_invert(struct lcmsMAT3 *result, const struct lcmsMAT3 *mat); From 44c307956762dc16fcd67c955fb6a22890730830 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 20 May 2022 14:07:04 +0300 Subject: [PATCH 349/609] tests: add lcms-util with MPE curves This adds a new test helper library that depends on LittleCMS 2. For starters, the library implements conversion from enum transfer_fn to ICC multiProcessingElements compatible LittleCMS curve object. That conversion allows encoding transfer funtions in ICC files and LittleCMS pipelines with full float32 precision instead of forcing a conversion to a 1D LUT which for power-type curves is surprisingly imprecise. This also adds CI tests to make sure the conversion matches our hand-coded transfer functions. Signed-off-by: Pekka Paalanen --- tests/color_util.c | 2 +- tests/color_util.h | 3 + tests/lcms-util-test.c | 85 ++++++++++++++++++ tests/lcms_util.c | 200 +++++++++++++++++++++++++++++++++++++++++ tests/lcms_util.h | 36 ++++++++ tests/meson.build | 21 ++++- 6 files changed, 345 insertions(+), 2 deletions(-) create mode 100644 tests/lcms-util-test.c create mode 100644 tests/lcms_util.c create mode 100644 tests/lcms_util.h diff --git a/tests/color_util.c b/tests/color_util.c index 0e0c738a..4bbd4071 100644 --- a/tests/color_util.c +++ b/tests/color_util.c @@ -212,7 +212,7 @@ Power2_4_EOTF_inv(float o) return pow(o, 1./2.4); } -static float +float apply_tone_curve(enum transfer_fn fn, float r) { float ret = 0; diff --git a/tests/color_util.h b/tests/color_util.h index 27d7d3c8..e9d931cd 100644 --- a/tests/color_util.h +++ b/tests/color_util.h @@ -97,6 +97,9 @@ a8r8g8b8_to_float(uint32_t v); bool find_tone_curve_type(enum transfer_fn fn, int *type, double params[5]); +float +apply_tone_curve(enum transfer_fn fn, float r); + void process_pixel_using_pipeline(enum transfer_fn pre_curve, const struct lcmsMAT3 *mat, diff --git a/tests/lcms-util-test.c b/tests/lcms-util-test.c new file mode 100644 index 00000000..e7d8b69e --- /dev/null +++ b/tests/lcms-util-test.c @@ -0,0 +1,85 @@ +/* + * Copyright 2022 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "config.h" + +#include +#include + +#include "weston-test-client-helper.h" +#include "color_util.h" +#include "lcms_util.h" + +static void +compare_pipeline_to_transfer_fn(cmsPipeline *pipeline, enum transfer_fn fn, + struct scalar_stat *stat) +{ + const unsigned N = 100000; + unsigned i; + + for (i = 0; i < N; i++) { + float x = (double)i / N; + float ref = apply_tone_curve(fn, x); + float y; + + cmsPipelineEvalFloat(&x, &y, pipeline); + scalar_stat_update(stat, y - ref, &(struct color_float){ .r = x }); + } +} + +static const enum transfer_fn build_MPE_curves_test_set[] = { + TRANSFER_FN_SRGB_EOTF, + TRANSFER_FN_SRGB_EOTF_INVERSE, + TRANSFER_FN_ADOBE_RGB_EOTF, + TRANSFER_FN_ADOBE_RGB_EOTF_INVERSE, + TRANSFER_FN_POWER2_4_EOTF, + TRANSFER_FN_POWER2_4_EOTF_INVERSE, +}; + +TEST_P(build_MPE_curves, build_MPE_curves_test_set) +{ + const enum transfer_fn *fn = data; + const cmsContext ctx = 0; + cmsToneCurve *curve; + cmsStage *stage; + cmsPipeline *pipeline; + struct scalar_stat stat = {}; + + curve = build_MPE_curve(ctx, *fn); + stage = cmsStageAllocToneCurves(ctx, 1, &curve); + cmsFreeToneCurve(curve); + + pipeline = cmsPipelineAlloc(ctx, 1, 1); + cmsPipelineInsertStage(pipeline, cmsAT_END, stage); + + compare_pipeline_to_transfer_fn(pipeline, *fn, &stat); + testlog("Transfer function %s as a segmented curve element, error:\n", + transfer_fn_name(*fn)); + scalar_stat_print_float(&stat); + assert(fabs(stat.max) < 1e-7); + assert(fabs(stat.min) < 1e-7); + + cmsPipelineFree(pipeline); +} diff --git a/tests/lcms_util.c b/tests/lcms_util.c new file mode 100644 index 00000000..d6e05d64 --- /dev/null +++ b/tests/lcms_util.c @@ -0,0 +1,200 @@ +/* + * Copyright 2022 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include + +#include "shared/helpers.h" +#include "color_util.h" +#include "lcms_util.h" + +/* + * MPE tone curves can only use LittleCMS parametric curve types 6-8 and not + * inverses. + * type 6: Y = (aX + b)^g + c; params [g, a, b, c] + * type 7: Y = a log(bX^g + c) + d; params [g, a, b, c, d] + * type 8: Y = a b^(cX + d) + e; params [a, b, c, d, e] + * Additionally, type 0 is sampled segment. + * + * cmsCurveSegment.x1 is the breakpoint stored in ICC files, except for the + * last segment. First segment always begins at -Inf, and last segment always + * ends at Inf. + */ + +static cmsToneCurve * +build_MPE_curve_sRGB(cmsContext ctx) +{ + cmsCurveSegment segments[] = { + { + /* Constant zero segment */ + .x0 = -HUGE_VAL, + .x1 = 0.0, + .Type = 6, + .Params = { 1.0, 0.0, 0.0, 0.0 }, + }, + { + /* Linear segment y = x / 12.92 */ + .x0 = 0.0, + .x1 = 0.04045, + .Type = 0, + .nGridPoints = 2, + .SampledPoints = (float[]){ 0.0, 0.04045 / 12.92 }, + }, + { + /* Power segment y = ((x + 0.055) / 1.055)^2.4 + * which is translated to + * y = (1/1.055 * x + 0.055 / 1.055)^2.4 + 0.0 + */ + .x0 = 0.04045, + .x1 = 1.0, + .Type = 6, + .Params = { 2.4, 1.0 / 1.055, 0.055 / 1.055, 0.0 }, + }, + { + /* Constant one segment */ + .x0 = 1.0, + .x1 = HUGE_VAL, + .Type = 6, + .Params = { 1.0, 0.0, 0.0, 1.0 }, + } + }; + + return cmsBuildSegmentedToneCurve(ctx, ARRAY_LENGTH(segments), segments); +} + +static cmsToneCurve * +build_MPE_curve_sRGB_inv(cmsContext ctx) +{ + cmsCurveSegment segments[] = { + { + /* Constant zero segment */ + .x0 = -HUGE_VAL, + .x1 = 0.0, + .Type = 6, + .Params = { 1.0, 0.0, 0.0, 0.0 }, + }, + { + /* Linear segment y = x * 12.92 */ + .x0 = 0.0, + .x1 = 0.04045 / 12.92, + .Type = 0, + .nGridPoints = 2, + .SampledPoints = (float[]){ 0.0, 0.04045 }, + }, + { + /* Power segment y = 1.055 * x^(1/2.4) - 0.055 + * which is translated to + * y = (1.055^2.4 * x + 0.0)^(1/2.4) - 0.055 + */ + .x0 = 0.04045 / 12.92, + .x1 = 1.0, + .Type = 6, + .Params = { 1.0 / 2.4, pow(1.055, 2.4), 0.0, -0.055 }, + }, + { + /* Constant one segment */ + .x0 = 1.0, + .x1 = HUGE_VAL, + .Type = 6, + .Params = { 1.0, 0.0, 0.0, 1.0 }, + } + }; + + return cmsBuildSegmentedToneCurve(ctx, ARRAY_LENGTH(segments), segments); +} + +static cmsToneCurve * +build_MPE_curve_power(cmsContext ctx, double exponent) +{ + cmsCurveSegment segments[] = { + { + /* Constant zero segment */ + .x0 = -HUGE_VAL, + .x1 = 0.0, + .Type = 6, + .Params = { 1.0, 0.0, 0.0, 0.0 }, + }, + { + /* Power segment y = x^exponent + * which is translated to + * y = (1.0 * x + 0.0)^exponent + 0.0 + */ + .x0 = 0.0, + .x1 = 1.0, + .Type = 6, + .Params = { exponent, 1.0, 0.0, 0.0 }, + }, + { + /* Constant one segment */ + .x0 = 1.0, + .x1 = HUGE_VAL, + .Type = 6, + .Params = { 1.0, 0.0, 0.0, 1.0 }, + } + }; + + return cmsBuildSegmentedToneCurve(ctx, ARRAY_LENGTH(segments), segments); +} + +cmsToneCurve * +build_MPE_curve(cmsContext ctx, enum transfer_fn fn) +{ + switch (fn) { + case TRANSFER_FN_ADOBE_RGB_EOTF: + return build_MPE_curve_power(ctx, 563.0 / 256.0); + case TRANSFER_FN_ADOBE_RGB_EOTF_INVERSE: + return build_MPE_curve_power(ctx, 256.0 / 563.0); + case TRANSFER_FN_POWER2_4_EOTF: + return build_MPE_curve_power(ctx, 2.4); + case TRANSFER_FN_POWER2_4_EOTF_INVERSE: + return build_MPE_curve_power(ctx, 1.0 / 2.4); + case TRANSFER_FN_SRGB_EOTF: + return build_MPE_curve_sRGB(ctx); + case TRANSFER_FN_SRGB_EOTF_INVERSE: + return build_MPE_curve_sRGB_inv(ctx); + default: + assert(0 && "unimplemented MPE curve"); + } + + return NULL; +} + +cmsStage * +build_MPE_curve_stage(cmsContext context_id, enum transfer_fn fn) +{ + cmsToneCurve *c; + cmsStage *stage; + + c = build_MPE_curve(context_id, fn); + stage = cmsStageAllocToneCurves(context_id, 3, + (cmsToneCurve *[3]){ c, c, c }); + assert(stage); + cmsFreeToneCurve(c); + + return stage; +} diff --git a/tests/lcms_util.h b/tests/lcms_util.h new file mode 100644 index 00000000..278d56cd --- /dev/null +++ b/tests/lcms_util.h @@ -0,0 +1,36 @@ +/* + * Copyright 2022 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include + +#include "color_util.h" + +cmsToneCurve * +build_MPE_curve(cmsContext ctx, enum transfer_fn fn); + +cmsStage * +build_MPE_curve_stage(cmsContext context_id, enum transfer_fn fn); diff --git a/tests/meson.build b/tests/meson.build index 4dfd57e9..eb4f9cf2 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -60,6 +60,21 @@ dep_test_client = declare_dependency( ] ) +lib_lcms_util = static_library( + 'lib_lcms_util', + [ 'lcms_util.c' ], + include_directories: common_inc, + dependencies: [ + dep_lcms2, dep_libm + ], + build_by_default: false, + install: false, +) +dep_lcms_util = declare_dependency( + link_with: lib_lcms_util, + dependencies: [ dep_lcms2 ] +) + exe_plugin_test = shared_library( 'test-plugin', 'weston-test.c', @@ -243,11 +258,15 @@ if get_option('color-management-lcms') error('color-management-lcms tests require lcms2 which was not found. Or, you can use \'-Dcolor-management-lcms=false\'.') endif tests += [ + { 'name': 'color-metadata-parsing' }, { 'name': 'color-shaper-matrix', 'dep_objs': [ dep_libm, dep_lcms2 ] }, - { 'name': 'color-metadata-parsing' }, + { + 'name': 'lcms-util', + 'dep_objs': [ dep_lcms_util ] + }, ] endif From 5921a00b3857ef7ee7cdafa741c3a040c0420bb4 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 20 May 2022 16:23:55 +0300 Subject: [PATCH 350/609] tests/lcms_util: add SetTextTags() This function sets some basic text tags to make an ICC file better formed. The code is taken from LittleCMS, https://github.com/mm2/Little-CMS.git git revision lcms2.13.1-28-g6ae2e99 (6ae2e99a3535417ca5c95b602eb61fdd29d294d0) file src/cmsvirt.c. Suggested-by: Vitaly Prosyak Signed-off-by: Pekka Paalanen --- tests/lcms_util.c | 31 +++++++++++++++++++++++++++++++ tests/lcms_util.h | 3 +++ 2 files changed, 34 insertions(+) diff --git a/tests/lcms_util.c b/tests/lcms_util.c index d6e05d64..4e304634 100644 --- a/tests/lcms_util.c +++ b/tests/lcms_util.c @@ -1,5 +1,6 @@ /* * Copyright 2022 Collabora, Ltd. + * Copyright (c) 1998-2022 Marti Maria Saguer * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -198,3 +199,33 @@ build_MPE_curve_stage(cmsContext context_id, enum transfer_fn fn) return stage; } + +/* This function is taken from LittleCMS, pardon the odd style */ +cmsBool +SetTextTags(cmsHPROFILE hProfile, const wchar_t* Description) +{ + cmsMLU *DescriptionMLU, *CopyrightMLU; + cmsBool rc = FALSE; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + + DescriptionMLU = cmsMLUalloc(ContextID, 1); + CopyrightMLU = cmsMLUalloc(ContextID, 1); + + if (DescriptionMLU == NULL || CopyrightMLU == NULL) goto Error; + + if (!cmsMLUsetWide(DescriptionMLU, "en", "US", Description)) goto Error; + if (!cmsMLUsetWide(CopyrightMLU, "en", "US", L"No copyright, use freely")) goto Error; + + if (!cmsWriteTag(hProfile, cmsSigProfileDescriptionTag, DescriptionMLU)) goto Error; + if (!cmsWriteTag(hProfile, cmsSigCopyrightTag, CopyrightMLU)) goto Error; + + rc = TRUE; + +Error: + + if (DescriptionMLU) + cmsMLUfree(DescriptionMLU); + if (CopyrightMLU) + cmsMLUfree(CopyrightMLU); + return rc; +} diff --git a/tests/lcms_util.h b/tests/lcms_util.h index 278d56cd..8808b641 100644 --- a/tests/lcms_util.h +++ b/tests/lcms_util.h @@ -34,3 +34,6 @@ build_MPE_curve(cmsContext ctx, enum transfer_fn fn); cmsStage * build_MPE_curve_stage(cmsContext context_id, enum transfer_fn fn); + +cmsBool +SetTextTags(cmsHPROFILE hProfile, const wchar_t* Description); From 062b6646fff3fe26f288dbe3d16850ed85d74afd Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 12 May 2022 17:20:01 +0300 Subject: [PATCH 351/609] tests/color-shaper-matrix: fix realpath() leak Found with ASan. Signed-off-by: Pekka Paalanen --- tests/color-shaper-matrix-test.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/color-shaper-matrix-test.c b/tests/color-shaper-matrix-test.c index 3c354874..5e6beb3a 100644 --- a/tests/color-shaper-matrix-test.c +++ b/tests/color-shaper-matrix-test.c @@ -275,6 +275,7 @@ build_output_icc_profile(const struct lcms_pipeline *pipe) assert(saved); cmsCloseProfile(profile); + free(wd); return profile_name; } From 6478859b4f190d907b6b0bdc9b41bed4b701dfd0 Mon Sep 17 00:00:00 2001 From: Vitaly Prosyak Date: Sun, 1 May 2022 13:57:10 -0400 Subject: [PATCH 352/609] tests/color-shaper-matrix: prepare for cLUT type profiles We will want to run the same color spaces with different types of ICC profiles. To help with that: 1. Let struct lcms_pipeline define the test color space and transformations and move the tolerance into a new per test case structure. 2. Added profile type: PTYPE_MATRIX_SHAPER, PTYPE_CLUT. PTYPE_MATRIX_SHAPER is the previously implemented type. Co-authored-by: Pekka Paalanen Signed-off-by: Vitaly Prosyak Signed-off-by: Pekka Paalanen --- tests/color-shaper-matrix-test.c | 194 +++++++++++++++++-------------- 1 file changed, 109 insertions(+), 85 deletions(-) diff --git a/tests/color-shaper-matrix-test.c b/tests/color-shaper-matrix-test.c index 5e6beb3a..46e9ac92 100644 --- a/tests/color-shaper-matrix-test.c +++ b/tests/color-shaper-matrix-test.c @@ -55,10 +55,6 @@ struct lcms_pipeline { * tone curve enum */ enum transfer_fn post_fn; - /** - * 2/255 or 3/255 maximum possible error, where 255 is 8 bit max value - */ - int tolerance; }; static const int WINDOW_WIDTH = 256; @@ -66,9 +62,9 @@ static const int WINDOW_HEIGHT = 24; static cmsCIExyY wp_d65 = { 0.31271, 0.32902, 1.0 }; -struct setup_args { - struct fixture_metadata meta; - struct lcms_pipeline pipeline; +enum profile_type { + PTYPE_MATRIX_SHAPER, + PTYPE_CLUT, }; /* @@ -78,69 +74,77 @@ struct setup_args { * colour.matrix_RGB_to_RGB(colour.RGB_COLOURSPACES['sRGB'], colour.RGB_COLOURSPACES['Adobe RGB (1998)'], None) * colour.matrix_RGB_to_RGB(colour.RGB_COLOURSPACES['sRGB'], colour.RGB_COLOURSPACES['ITU-R BT.2020'], None) */ -const struct setup_args arr_setup[] = { - { - .meta.name = "sRGB->sRGB unity", - .pipeline = { - .color_space = "sRGB", - .prim_output = { - .Red = { 0.640, 0.330, 1.0 }, - .Green = { 0.300, 0.600, 1.0 }, - .Blue = { 0.150, 0.060, 1.0 } - }, - .pre_fn = TRANSFER_FN_SRGB_EOTF, - .mat = LCMSMAT3(1.0, 0.0, 0.0, - 0.0, 1.0, 0.0, - 0.0, 0.0, 1.0), - .post_fn = TRANSFER_FN_SRGB_EOTF_INVERSE, - .tolerance = 0 - } + +const struct lcms_pipeline pipeline_sRGB = { + .color_space = "sRGB", + .prim_output = { + .Red = { 0.640, 0.330, 1.0 }, + .Green = { 0.300, 0.600, 1.0 }, + .Blue = { 0.150, 0.060, 1.0 } }, - { - .meta.name = "sRGB->adobeRGB", - .pipeline = { - .color_space = "adobeRGB", - .prim_output = { - .Red = { 0.640, 0.330, 1.0 }, - .Green = { 0.210, 0.710, 1.0 }, - .Blue = { 0.150, 0.060, 1.0 } - }, - .pre_fn = TRANSFER_FN_SRGB_EOTF, - .mat = LCMSMAT3(0.715119, 0.284881, 0.0, - 0.0, 1.0, 0.0, - 0.0, 0.041169, 0.958831), - .post_fn = TRANSFER_FN_ADOBE_RGB_EOTF_INVERSE, - .tolerance = 1 - /* - * Tolerance depends more on the 1D LUT used for the - * inv EOTF than the tested 3D LUT size: - * 9x9x9, 17x17x17, 33x33x33, 127x127x127 - */ - } + .pre_fn = TRANSFER_FN_SRGB_EOTF, + .mat = LCMSMAT3(1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0), + .post_fn = TRANSFER_FN_SRGB_EOTF_INVERSE +}; + +const struct lcms_pipeline pipeline_adobeRGB = { + .color_space = "adobeRGB", + .prim_output = { + .Red = { 0.640, 0.330, 1.0 }, + .Green = { 0.210, 0.710, 1.0 }, + .Blue = { 0.150, 0.060, 1.0 } }, - { - .meta.name = "sRGB->bt2020", - .pipeline = { - .color_space = "bt2020", - .prim_output = { - .Red = { 0.708, 0.292, 1.0 }, - .Green = { 0.170, 0.797, 1.0 }, - .Blue = { 0.131, 0.046, 1.0 } - }, - .pre_fn = TRANSFER_FN_SRGB_EOTF, - .mat = LCMSMAT3(0.627402, 0.329292, 0.043306, - 0.069095, 0.919544, 0.011360, - 0.016394, 0.088028, 0.895578), - /* this is equivalent to BT.1886 with zero black level */ - .post_fn = TRANSFER_FN_POWER2_4_EOTF_INVERSE, - .tolerance = 5 - /* - * TODO: when we add power-law in the curve enumeration - * in GL-renderer, then we should fix the tolerance - * as the error should reduce a lot. - */ - } - } + .pre_fn = TRANSFER_FN_SRGB_EOTF, + .mat = LCMSMAT3( 0.715127, 0.284868, 0.000005, + 0.000001, 0.999995, 0.000004, + -0.000003, 0.041155, 0.958848), + .post_fn = TRANSFER_FN_ADOBE_RGB_EOTF_INVERSE +}; + +const struct lcms_pipeline pipeline_BT2020 = { + .color_space = "bt2020", + .prim_output = { + .Red = { 0.708, 0.292, 1.0 }, + .Green = { 0.170, 0.797, 1.0 }, + .Blue = { 0.131, 0.046, 1.0 } + }, + .pre_fn = TRANSFER_FN_SRGB_EOTF, + .mat = LCMSMAT3(0.627402, 0.329292, 0.043306, + 0.069095, 0.919544, 0.011360, + 0.016394, 0.088028, 0.895578), + /* this is equivalent to BT.1886 with zero black level */ + .post_fn = TRANSFER_FN_POWER2_4_EOTF_INVERSE, +}; + +struct setup_args { + struct fixture_metadata meta; + const struct lcms_pipeline *pipeline; + /** + * 2/255 or 3/255 maximum possible error, where 255 is 8 bit max value + * + * Tolerance depends more on the 1D LUT used for the + * inv EOTF than the tested 3D LUT size: + * 9x9x9, 17x17x17, 33x33x33, 127x127x127 + * + * TODO: when we add power-law in the curve enumeration + * in GL-renderer, then we should fix the tolerance + * as the error should reduce a lot. + */ + int tolerance; + /** + * 3DLUT dimension size + */ + int dim_size; + enum profile_type type; +}; + +static const struct setup_args my_setup_args[] = { + /* name, pipeline, tolerance, dim, profile type */ + { { "sRGB->sRGB" }, &pipeline_sRGB, 0, 0, PTYPE_MATRIX_SHAPER }, + { { "sRGB->adobeRGB" }, &pipeline_adobeRGB, 1, 0, PTYPE_MATRIX_SHAPER }, + { { "sRGB->BT2020" }, &pipeline_BT2020, 5, 0, PTYPE_MATRIX_SHAPER }, }; struct image_header { @@ -219,7 +223,8 @@ gen_ramp_rgb(const struct image_header *header, int bitwidth, int width_bar) } static cmsHPROFILE -build_lcms_profile_output(const struct lcms_pipeline *pipeline) +build_lcms_matrix_shaper_profile_output(cmsContext context_id, + const struct lcms_pipeline *pipeline) { cmsToneCurve *arr_curves[3]; cmsHPROFILE hRGB; @@ -240,12 +245,12 @@ build_lcms_profile_output(const struct lcms_pipeline *pipeline) */ arr_curves[0] = arr_curves[1] = arr_curves[2] = - cmsBuildParametricToneCurve(NULL, + cmsBuildParametricToneCurve(context_id, (-1) * type_inverse_tone_curve, inverse_tone_curve_param); assert(arr_curves[0]); - hRGB = cmsCreateRGBProfileTHR(NULL, &wp_d65, + hRGB = cmsCreateRGBProfileTHR(context_id, &wp_d65, &pipeline->prim_output, arr_curves); assert(hRGB); @@ -253,8 +258,22 @@ build_lcms_profile_output(const struct lcms_pipeline *pipeline) return hRGB; } +static cmsHPROFILE +build_lcms_profile_output(cmsContext context_id, const struct setup_args *arg) +{ + switch (arg->type) { + case PTYPE_MATRIX_SHAPER: + return build_lcms_matrix_shaper_profile_output(context_id, + arg->pipeline); + case PTYPE_CLUT: + return NULL; + } + + return NULL; +} + static char * -build_output_icc_profile(const struct lcms_pipeline *pipe) +build_output_icc_profile(const struct setup_args *arg) { char *profile_name = NULL; cmsHPROFILE profile = NULL; @@ -264,11 +283,15 @@ build_output_icc_profile(const struct lcms_pipeline *pipe) wd = realpath(".", NULL); assert(wd); - ret = asprintf(&profile_name, "%s/matrix-shaper-test-%s.icm", wd, - pipe->color_space); + if (arg->type == PTYPE_MATRIX_SHAPER) + ret = asprintf(&profile_name, "%s/matrix-shaper-test-%s.icm", wd, + arg->pipeline->color_space); + else + ret = asprintf(&profile_name, "%s/cLUT-test-%s.icm", wd, + arg->pipeline->color_space); assert(ret > 0); - profile = build_lcms_profile_output(pipe); + profile = build_lcms_profile_output(NULL, arg); assert(profile); saved = cmsSaveProfileToFile(profile, profile_name); @@ -293,7 +316,7 @@ fixture_setup(struct weston_test_harness *harness, const struct setup_args *arg) setup.height = WINDOW_HEIGHT; setup.shell = SHELL_TEST_DESKTOP; - file_name = build_output_icc_profile(&arg->pipeline); + file_name = build_output_icc_profile(arg); if (!file_name) return RESULT_HARD_ERROR; @@ -308,7 +331,7 @@ fixture_setup(struct weston_test_harness *harness, const struct setup_args *arg) return weston_test_harness_execute_as_client(harness, &setup); } -DECLARE_FIXTURE_SETUP_WITH_ARG(fixture_setup, arr_setup, meta); +DECLARE_FIXTURE_SETUP_WITH_ARG(fixture_setup, my_setup_args, meta); static bool compare_float(float ref, float dst, int x, const char *chan, @@ -362,7 +385,7 @@ process_pipeline_comparison(const struct image_header *src, const char *const chan_name[COLOR_CHAN_NUM] = { "r", "g", "b" }; const float max_pixel_value = 255.0; struct color_float max_diff_pipeline = { .rgb = { 0.0f, 0.0f, 0.0f } }; - float max_allow_diff = arg->pipeline.tolerance / max_pixel_value; + float max_allow_diff = arg->tolerance / max_pixel_value; float max_err = 0.0f; bool ok = true; uint32_t *row_ptr, *row_ptr_shot; @@ -380,9 +403,9 @@ process_pipeline_comparison(const struct image_header *src, pix_src = a8r8g8b8_to_float(row_ptr[x]); pix_shot = a8r8g8b8_to_float(row_ptr_shot[x]); /* do pipeline processing */ - process_pixel_using_pipeline(arg->pipeline.pre_fn, - &arg->pipeline.mat, - arg->pipeline.post_fn, + process_pixel_using_pipeline(arg->pipeline->pre_fn, + &arg->pipeline->mat, + arg->pipeline->post_fn, &pix_src, &pix_src_pipeline); /* check if pipeline matches to shader variant */ @@ -399,11 +422,12 @@ process_pipeline_comparison(const struct image_header *src, for (chan = 0; chan < COLOR_CHAN_NUM; chan++) max_err = MAX(max_err, max_diff_pipeline.rgb[chan]); - testlog("%s %s %s tol_req %d, tol_cal %f, max diff: r=%f, g=%f, b=%f\n", + testlog("%s %s %s tol_req %d, tol_cal %f, max diff: r=%f, g=%f, b=%f %s\n", __func__, ok == true? "SUCCESS":"FAILURE", - arg->meta.name, arg->pipeline.tolerance, + arg->meta.name, arg->tolerance, max_err * max_pixel_value, - max_diff_pipeline.r, max_diff_pipeline.g, max_diff_pipeline.b); + max_diff_pipeline.r, max_diff_pipeline.g, max_diff_pipeline.b, + arg->type == PTYPE_MATRIX_SHAPER ? "matrix-shaper" : "cLUT"); return ok; } @@ -462,7 +486,7 @@ TEST(shaper_matrix) assert(shot); match = verify_image(shot, "shaper_matrix", seq_no, NULL, seq_no); - assert(check_process_pattern_ex(buf, shot, &arr_setup[seq_no])); + assert(check_process_pattern_ex(buf, shot, &my_setup_args[seq_no])); assert(match); buffer_destroy(shot); buffer_destroy(buf); From 276d1ae0249696a3f1d8aa15cafa5ed4e9e91ad1 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 23 May 2022 14:39:06 +0300 Subject: [PATCH 353/609] tests/color-shaper-matrix: add ref image index This makes it easy to re-use existing reference images for further test cases. Signed-off-by: Pekka Paalanen --- tests/color-shaper-matrix-test.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/tests/color-shaper-matrix-test.c b/tests/color-shaper-matrix-test.c index 46e9ac92..e1cbef62 100644 --- a/tests/color-shaper-matrix-test.c +++ b/tests/color-shaper-matrix-test.c @@ -120,6 +120,7 @@ const struct lcms_pipeline pipeline_BT2020 = { struct setup_args { struct fixture_metadata meta; + int ref_image_index; const struct lcms_pipeline *pipeline; /** * 2/255 or 3/255 maximum possible error, where 255 is 8 bit max value @@ -141,10 +142,10 @@ struct setup_args { }; static const struct setup_args my_setup_args[] = { - /* name, pipeline, tolerance, dim, profile type */ - { { "sRGB->sRGB" }, &pipeline_sRGB, 0, 0, PTYPE_MATRIX_SHAPER }, - { { "sRGB->adobeRGB" }, &pipeline_adobeRGB, 1, 0, PTYPE_MATRIX_SHAPER }, - { { "sRGB->BT2020" }, &pipeline_BT2020, 5, 0, PTYPE_MATRIX_SHAPER }, + /* name, ref img, pipeline, tolerance, dim, profile type */ + { { "sRGB->sRGB" }, 0, &pipeline_sRGB, 0, 0, PTYPE_MATRIX_SHAPER }, + { { "sRGB->adobeRGB" }, 1, &pipeline_adobeRGB, 1, 0, PTYPE_MATRIX_SHAPER }, + { { "sRGB->BT2020" }, 2, &pipeline_BT2020, 5, 0, PTYPE_MATRIX_SHAPER }, }; struct image_header { @@ -457,6 +458,8 @@ check_process_pattern_ex(struct buffer *src, struct buffer *shot, */ TEST(shaper_matrix) { + int seq_no = get_test_fixture_index(); + const struct setup_args *arg = &my_setup_args[seq_no]; const int width = WINDOW_WIDTH; const int height = WINDOW_HEIGHT; const int bitwidth = 8; @@ -468,7 +471,6 @@ TEST(shaper_matrix) struct wl_surface *surface; struct image_header image; bool match; - int seq_no = get_test_fixture_index(); client = create_client_and_test_surface(0, 0, width, height); assert(client); @@ -485,8 +487,9 @@ TEST(shaper_matrix) shot = capture_screenshot_of_output(client); assert(shot); - match = verify_image(shot, "shaper_matrix", seq_no, NULL, seq_no); - assert(check_process_pattern_ex(buf, shot, &my_setup_args[seq_no])); + match = verify_image(shot, "shaper_matrix", arg->ref_image_index, + NULL, seq_no); + assert(check_process_pattern_ex(buf, shot, arg)); assert(match); buffer_destroy(shot); buffer_destroy(buf); From 0c5860fafb079b8edba66e4e805d26b563c8e529 Mon Sep 17 00:00:00 2001 From: Vitaly Prosyak Date: Wed, 4 May 2022 19:27:16 -0400 Subject: [PATCH 354/609] tests/color-shaper-matrix: add creation and usage cLUT profiles Added cLUT profile creation to validate linearization algorithm for DToB3 tag (direction dev to PCS). The 3DLUT is built by using raw matrix conversion from dev to XYZ and reverse (XYZ to device). The test uses floating point pipeline, known as unbounded mode of LCMS. The details are described in ICCSpecRevision_02_11_06_Float.pdf The purpose of these new test cases is to keep the GL-renderer 3D LUT path tested even after color-lcms and GL-renderer start using specialized matrix-shaper paths. These also exercise build_eotf_from_clut_profile() in color-lcms, but do not actually verify it. These cases only test that the recovered EOTF and its inverse produce an identity mapping together. BT.2020 is not used in these tests, because the RGB-XYZ conversion matrix does not stay inside [0.0, 1.0] in either direction, which would be a problem for the 3D LUT element in the multiProcessingElement pipelines. Handling that would have been possible, but testing with AdobeRGB color space should suffice while keeping the test code from being even more complicated. roundtrip_verification() tests that we succeed in creating cms pipelines correctly in both directions so that the resulting ICC file is better behaved. The Weston test itself only cares about the BToD direction. Credits to: Vladimir Lachine Graeme Gill Co-authored-by: Pekka Paalanen Signed-off-by: Vitaly Prosyak Signed-off-by: Pekka Paalanen --- tests/color-shaper-matrix-test.c | 218 ++++++++++++++++++++++++++++++- tests/meson.build | 2 +- 2 files changed, 213 insertions(+), 7 deletions(-) diff --git a/tests/color-shaper-matrix-test.c b/tests/color-shaper-matrix-test.c index e1cbef62..1e10f98c 100644 --- a/tests/color-shaper-matrix-test.c +++ b/tests/color-shaper-matrix-test.c @@ -26,13 +26,15 @@ #include "config.h" #include +#include +#include + +#include #include "weston-test-client-helper.h" #include "weston-test-fixture-compositor.h" #include "color_util.h" -#include -#include -#include +#include "lcms_util.h" struct lcms_pipeline { /** @@ -51,6 +53,11 @@ struct lcms_pipeline { * Transform matrix from sRGB to target chromaticities in prim_output */ struct lcmsMAT3 mat; + /** + * matrix from prim_output to XYZ, for example matrix conversion + * sRGB->XYZ, adobeRGB->XYZ, bt2020->XYZ + */ + struct lcmsMAT3 mat2XYZ; /** * tone curve enum */ @@ -86,6 +93,9 @@ const struct lcms_pipeline pipeline_sRGB = { .mat = LCMSMAT3(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0), + .mat2XYZ = LCMSMAT3(0.436037, 0.385124, 0.143039, + 0.222482, 0.716913, 0.060605, + 0.013922, 0.097078, 0.713899), .post_fn = TRANSFER_FN_SRGB_EOTF_INVERSE }; @@ -100,6 +110,9 @@ const struct lcms_pipeline pipeline_adobeRGB = { .mat = LCMSMAT3( 0.715127, 0.284868, 0.000005, 0.000001, 0.999995, 0.000004, -0.000003, 0.041155, 0.958848), + .mat2XYZ = LCMSMAT3(0.609740, 0.205279, 0.149181, + 0.311111, 0.625681, 0.063208, + 0.019469, 0.060879, 0.744552), .post_fn = TRANSFER_FN_ADOBE_RGB_EOTF_INVERSE }; @@ -139,13 +152,18 @@ struct setup_args { */ int dim_size; enum profile_type type; + + /** Two-norm error limit for cLUT DToB->BToD roundtrip */ + float clut_roundtrip_tolerance; }; static const struct setup_args my_setup_args[] = { - /* name, ref img, pipeline, tolerance, dim, profile type */ + /* name, ref img, pipeline, tolerance, dim, profile type, clut tolerance */ { { "sRGB->sRGB" }, 0, &pipeline_sRGB, 0, 0, PTYPE_MATRIX_SHAPER }, { { "sRGB->adobeRGB" }, 1, &pipeline_adobeRGB, 1, 0, PTYPE_MATRIX_SHAPER }, { { "sRGB->BT2020" }, 2, &pipeline_BT2020, 5, 0, PTYPE_MATRIX_SHAPER }, + { { "sRGB->sRGB" }, 0, &pipeline_sRGB, 0, 17, PTYPE_CLUT, 0.0005 }, + { { "sRGB->adobeRGB" }, 1, &pipeline_adobeRGB, 1, 17, PTYPE_CLUT, 0.0065 }, }; struct image_header { @@ -223,6 +241,184 @@ gen_ramp_rgb(const struct image_header *header, int bitwidth, int width_bar) } } +static void +test_roundtrip(uint8_t r, uint8_t g, uint8_t b, cmsPipeline *pip, + struct rgb_diff_stat *stat) +{ + struct color_float in = { .rgb = { r / 255.0, g / 255.0, b / 255.0 } }; + struct color_float out = {}; + + cmsPipelineEvalFloat(in.rgb, out.rgb, pip); + rgb_diff_stat_update(stat, &in, &out); +} + +/* + * Roundtrip verification tests that converting device -> PCS -> device + * results in the original color values close enough. + * + * This ensures that the two pipelines are probably built correctly, and we + * do not have problems with unexpected value clamping or with representing + * (inverse) EOTF curves. + */ +static void +roundtrip_verification(cmsPipeline *DToB, cmsPipeline *BToD, float tolerance) +{ + const char *const chan_name[COLOR_CHAN_NUM] = { "r", "g", "b" }; + unsigned i; + unsigned r, g, b; + struct rgb_diff_stat stat = {}; + cmsPipeline *pip; + + pip = cmsPipelineDup(DToB); + cmsPipelineCat(pip, BToD); + + /* + * Inverse-EOTF is known to have precision problems near zero, so + * sample near zero densely, the rest can be more sparse to run faster. + */ + for (r = 0; r < 256; r += (r < 15) ? 1 : 8) { + for (g = 0; g < 256; g += (g < 15) ? 1 : 8) { + for (b = 0; b < 256; b += (b < 15) ? 1 : 8) + test_roundtrip(r, g, b, pip, &stat); + } + } + + cmsPipelineFree(pip); + + testlog("DToB->BToD roundtrip error statistics (%u samples):\n", + stat.two_norm.count); + for (i = 0; i < COLOR_CHAN_NUM; i++) { + testlog(" ch %s error:\n", chan_name[i]); + scalar_stat_print_rgb8bit(&stat.rgb[i]); + } + testlog(" Two-norm error:\n"); + scalar_stat_print_rgb8bit(&stat.two_norm); + + assert(stat.two_norm.max < tolerance); +} + +static cmsInt32Number +sampler_matrix(const float src[], float dst[], void *cargo) +{ + const struct lcmsMAT3 *mat = cargo; + struct color_float in = { .r = src[0], .g = src[1], .b = src[2] }; + struct color_float cf; + unsigned i; + + cf = color_float_apply_matrix(mat, in); + + for (i = 0; i < COLOR_CHAN_NUM; i++) + dst[i] = cf.rgb[i]; + + return 1; +} + +static cmsStage * +create_cLUT_from_matrix(cmsContext context_id, const struct lcmsMAT3 *mat, int dim_size) +{ + cmsStage *cLUT_stage; + + cLUT_stage = cmsStageAllocCLutFloat(context_id, dim_size, 3, 3, NULL); + cmsStageSampleCLutFloat(cLUT_stage, sampler_matrix, (void *)mat, 0); + + return cLUT_stage; +} + +/* + * Originally the cLUT profile test attempted to use the AToB/BToA tags. Those + * come with serious limitations though: at most uint16 representation for + * values in a LUT which means LUT entry precision is limited and range is + * [0.0, 1.0]. This poses difficulties such as: + * - for AToB, the resulting PCS XYZ values may need to be > 1.0 + * - for BToA, it is easy to fall outside of device color volume meaning that + * out-of-range values are needed in the 3D LUT + * Working around these could require offsetting and scaling of values + * before and after the 3D LUT, and even that may not always be possible. + * + * DToB/BToD tags do not have most of these problems, because there pipelines + * use float32 representation throughout. We have much more precision, and + * we can mostly use negative and greater than 1.0 values. LUT elements + * still clamp their input to [0.0, 1.0] before applying the LUT. This type of + * pipeline is called multiProcessElement (MPE). + * + * MPE also allows us to represent curves in a few analytical forms. These are + * just enough to represent the EOTF curves we have and their inverses, but + * they do not allow encoding extended EOTF curves or their inverses + * (defined for all real numbers by extrapolation, and mirroring for negative + * inputs). Using MPE curves we avoid the precision problems that arise from + * attempting to represent an inverse-EOTF as a LUT. For the precision issue, + * see: https://gitlab.freedesktop.org/pq/color-and-hdr/-/merge_requests/9 + * + * MPE is not a complete remedy, because 3D LUT inputs are still always clamped + * to [0.0, 1.0]. Therefore a 3D LUT cannot represent the inverse of a matrix + * that can produce negative or greater than 1.0 values without further tricks + * (scaling and offsetting) in the pipeline. Rather than implementing that + * complication, we decided to just not test with such matrices. Therefore + * BT.2020 color space is not used in the cLUT test. AdobeRGB is enough. + */ +static cmsHPROFILE +build_lcms_clut_profile_output(cmsContext context_id, + const struct setup_args *arg) +{ + enum transfer_fn inv_eotf_fn = arg->pipeline->post_fn; + enum transfer_fn eotf_fn = transfer_fn_invert(inv_eotf_fn); + cmsHPROFILE hRGB; + cmsPipeline *DToB0, *BToD0; + cmsStage *stage; + cmsStage *stage_inv_eotf; + cmsStage *stage_eotf; + struct lcmsMAT3 mat2XYZ_inv; + + lcmsMAT3_invert(&mat2XYZ_inv, &arg->pipeline->mat2XYZ); + + hRGB = cmsCreateProfilePlaceholder(context_id); + cmsSetProfileVersion(hRGB, 4.3); + cmsSetDeviceClass(hRGB, cmsSigDisplayClass); + cmsSetColorSpace(hRGB, cmsSigRgbData); + cmsSetPCS(hRGB, cmsSigXYZData); + SetTextTags(hRGB, L"cLut profile"); + + stage_eotf = build_MPE_curve_stage(context_id, eotf_fn); + stage_inv_eotf = build_MPE_curve_stage(context_id, inv_eotf_fn); + + /* + * Pipeline from PCS (optical) to device (electrical) + */ + BToD0 = cmsPipelineAlloc(context_id, 3, 3); + + stage = create_cLUT_from_matrix(context_id, &mat2XYZ_inv, arg->dim_size); + cmsPipelineInsertStage(BToD0, cmsAT_END, stage); + cmsPipelineInsertStage(BToD0, cmsAT_END, cmsStageDup(stage_inv_eotf)); + + cmsWriteTag(hRGB, cmsSigBToD0Tag, BToD0); + cmsLinkTag(hRGB, cmsSigBToD1Tag, cmsSigBToD0Tag); + cmsLinkTag(hRGB, cmsSigBToD2Tag, cmsSigBToD0Tag); + cmsLinkTag(hRGB, cmsSigBToD3Tag, cmsSigBToD0Tag); + + /* + * Pipeline from device (electrical) to PCS (optical) + */ + DToB0 = cmsPipelineAlloc(context_id, 3, 3); + + cmsPipelineInsertStage(DToB0, cmsAT_END, cmsStageDup(stage_eotf)); + stage = create_cLUT_from_matrix(context_id, &arg->pipeline->mat2XYZ, arg->dim_size); + cmsPipelineInsertStage(DToB0, cmsAT_END, stage); + + cmsWriteTag(hRGB, cmsSigDToB0Tag, DToB0); + cmsLinkTag(hRGB, cmsSigDToB1Tag, cmsSigDToB0Tag); + cmsLinkTag(hRGB, cmsSigDToB2Tag, cmsSigDToB0Tag); + cmsLinkTag(hRGB, cmsSigDToB3Tag, cmsSigDToB0Tag); + + roundtrip_verification(DToB0, BToD0, arg->clut_roundtrip_tolerance); + + cmsPipelineFree(BToD0); + cmsPipelineFree(DToB0); + cmsStageFree(stage_eotf); + cmsStageFree(stage_inv_eotf); + + return hRGB; +} + static cmsHPROFILE build_lcms_matrix_shaper_profile_output(cmsContext context_id, const struct lcms_pipeline *pipeline) @@ -267,7 +463,7 @@ build_lcms_profile_output(cmsContext context_id, const struct setup_args *arg) return build_lcms_matrix_shaper_profile_output(context_id, arg->pipeline); case PTYPE_CLUT: - return NULL; + return build_lcms_clut_profile_output(context_id, arg); } return NULL; @@ -304,12 +500,22 @@ build_output_icc_profile(const struct setup_args *arg) return profile_name; } +static void +test_lcms_error_logger(cmsContext context_id, + cmsUInt32Number error_code, + const char *text) +{ + testlog("LittleCMS error: %s\n", text); +} + static enum test_result_code fixture_setup(struct weston_test_harness *harness, const struct setup_args *arg) { struct compositor_setup setup; char *file_name; + cmsSetLogErrorHandler(test_lcms_error_logger); + compositor_setup_defaults(&setup); setup.renderer = RENDERER_GL; setup.backend = WESTON_BACKEND_HEADLESS; @@ -456,7 +662,7 @@ check_process_pattern_ex(struct buffer *src, struct buffer *shot, /* * Test that matrix-shaper profile does CM correctly, it is used color ramp pattern */ -TEST(shaper_matrix) +TEST(shaper_matrix_and_cLUT) { int seq_no = get_test_fixture_index(); const struct setup_args *arg = &my_setup_args[seq_no]; diff --git a/tests/meson.build b/tests/meson.build index eb4f9cf2..cc7f8c7b 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -261,7 +261,7 @@ if get_option('color-management-lcms') { 'name': 'color-metadata-parsing' }, { 'name': 'color-shaper-matrix', - 'dep_objs': [ dep_libm, dep_lcms2 ] + 'dep_objs': [ dep_libm, dep_lcms_util ] }, { 'name': 'lcms-util', From 7ceda8cbba22e5d7316ea27ed8835b22be1270b8 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Thu, 2 Jun 2022 13:34:54 +0300 Subject: [PATCH 355/609] gl-renderer: Ensure gl_buffer_state is present for direct-display This patch makes sure we have a gl_buffer_state present when using direct-display protocol extensions (which forbids any GL imports, and assumes a direct path with the display unit to perform a KMS import). Without this patch we would basically have no gl_buffer_state at repaint time because we never manged to create one, as direct-display code path will return much early. Partially fixes gitlab.freedesktop.org/wayland/weston/-/issues/621. Suggested-by: Pekka Paalanen Signed-off-by: Marius Vlad --- libweston/renderer-gl/gl-renderer.c | 65 +++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 17 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 58f076d2..5f1cfab5 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -2726,6 +2726,49 @@ gl_renderer_import_dmabuf(struct weston_compositor *ec, return true; } +static struct gl_buffer_state * +ensure_renderer_gl_buffer_state(struct weston_surface *surface, + struct weston_buffer *buffer) +{ + struct gl_renderer *gr = get_renderer(surface->compositor); + struct gl_surface_state *gs = get_surface_state(surface); + struct gl_buffer_state *gb = buffer->renderer_private; + + if (gb) { + gs->buffer = gb; + return gb; + } + + gb = zalloc(sizeof(*gb)); + gb->gr = gr; + pixman_region32_init(&gb->texture_damage); + buffer->renderer_private = gb; + gb->destroy_listener.notify = handle_buffer_destroy; + wl_signal_add(&buffer->destroy_signal, &gb->destroy_listener); + + gs->buffer = gb; + + return gb; +} + +static void +attach_direct_display_censor_placeholder(struct weston_surface *surface, + struct weston_buffer *buffer) +{ + struct gl_buffer_state *gb; + + gb = ensure_renderer_gl_buffer_state(surface, buffer); + + /* uses the same color as the content-protection placeholder */ + gb->color[0] = 0.40f; + gb->color[1] = 0.0f; + gb->color[2] = 0.0f; + gb->color[3] = 1.0f; + + gb->shader_variant = SHADER_VARIANT_SOLID; +} + + static bool gl_renderer_attach_dmabuf(struct weston_surface *surface, struct weston_buffer *buffer) @@ -2737,8 +2780,10 @@ gl_renderer_attach_dmabuf(struct weston_surface *surface, GLenum target; int i; - if (buffer->direct_display) + if (buffer->direct_display) { + attach_direct_display_censor_placeholder(surface, buffer); return true; + } /* Thanks to linux-dmabuf being totally independent of libweston, * the first time a dmabuf is attached, the gl_buffer_state will @@ -2841,21 +2886,9 @@ static bool gl_renderer_attach_solid(struct weston_surface *surface, struct weston_buffer *buffer) { - struct gl_renderer *gr = get_renderer(surface->compositor); - struct gl_surface_state *gs = get_surface_state(surface); - struct gl_buffer_state *gb = buffer->renderer_private; - - if (gb) { - gs->buffer = gb; - return true; - } + struct gl_buffer_state *gb; - gb = zalloc(sizeof(*gb)); - gb->gr = gr; - pixman_region32_init(&gb->texture_damage); - buffer->renderer_private = gb; - gb->destroy_listener.notify = handle_buffer_destroy; - wl_signal_add(&buffer->destroy_signal, &gb->destroy_listener); + gb = ensure_renderer_gl_buffer_state(surface, buffer); gb->color[0] = buffer->solid.r; gb->color[1] = buffer->solid.g; @@ -2864,8 +2897,6 @@ gl_renderer_attach_solid(struct weston_surface *surface, gb->shader_variant = SHADER_VARIANT_SOLID; - gs->buffer = gb; - return true; } From 0f4b411091719becfb43043a9bf035d7d041d354 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Mon, 6 Jun 2022 13:56:19 -0500 Subject: [PATCH 356/609] ci: Fix cobertura syntax This has somehow stopped working. Copied different syntax from a gitlab example. Signed-off-by: Derek Foreman --- .gitlab-ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 51636501..0841437d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -294,7 +294,9 @@ x86_64-debian-full-build: - .build-options-full artifacts: reports: - cobertura: $BUILDDIR/meson-logs/coverage.xml + coverage_report: + coverage_format: cobertura + path: $BUILDDIR/meson-logs/coverage.xml aarch64-debian-full-build: extends: From 08a821f291cd55b9d2654d15b1da832ead782c96 Mon Sep 17 00:00:00 2001 From: Luigi Santivetti Date: Thu, 2 Jun 2022 01:32:15 +0100 Subject: [PATCH 357/609] gitlab-ci: build libdrm version 2.4.108 from source libdrm with version 2.4.108 provides new functionality for parsing IN_FORMATS blobs. Weston can make use of it and avoid implementing its own logic. At present CI uses Debian 11 (bullseye) which comes with an older version (2.4.104), so we build it from source. Signed-off-by: Luigi Santivetti --- .gitlab-ci.yml | 2 +- .gitlab-ci/build-deps.sh | 11 +++++++++++ .gitlab-ci/debian-install.sh | 1 + 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0841437d..dd8d0fa8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -43,7 +43,7 @@ variables: FDO_UPSTREAM_REPO: wayland/weston FDO_REPO_SUFFIX: "$BUILD_OS/$BUILD_ARCH" - FDO_DISTRIBUTION_TAG: '2022-02-07.00-add-vgem-to-ci' + FDO_DISTRIBUTION_TAG: '2022-06-06.00-build-libdrm-dep' include: diff --git a/.gitlab-ci/build-deps.sh b/.gitlab-ci/build-deps.sh index 158ff7c4..1f85fb8f 100755 --- a/.gitlab-ci/build-deps.sh +++ b/.gitlab-ci/build-deps.sh @@ -130,6 +130,17 @@ ninja ${NINJAFLAGS} -C build install cd .. rm -rf mesa +# Build and install our own version of libdrm. Debian 11 (bullseye) provides +# libdrm 2.4.104 which doesn't have the IN_FORMATS iterator api. We can stop +# building and installing libdrm as soon as we move to Debian 12. +git clone --branch libdrm-2.4.108 --depth=1 https://gitlab.freedesktop.org/mesa/drm.git +cd drm +meson build -Dauto_features=disabled \ + -Dvc4=false -Dfreedreno=false -Detnaviv=false +ninja ${NINJAFLAGS} -C build install +cd .. +rm -rf drm + # PipeWire is used for remoting support. Unlike our other dependencies its # behaviour will be stable, however as a pre-1.0 project its API is not yet # stable, so again we lock it to a fixed version. diff --git a/.gitlab-ci/debian-install.sh b/.gitlab-ci/debian-install.sh index 2c243101..9b9d2a6d 100644 --- a/.gitlab-ci/debian-install.sh +++ b/.gitlab-ci/debian-install.sh @@ -68,6 +68,7 @@ apt-get -y --no-install-recommends install \ libmtdev-dev \ libpam0g-dev \ libpango1.0-dev \ + libpciaccess-dev \ libpixman-1-dev \ libpng-dev \ libpulse-dev \ From a62bf5ff487bb3c970da71637fdade5e26495521 Mon Sep 17 00:00:00 2001 From: Luigi Santivetti Date: Tue, 22 Feb 2022 22:56:42 +0000 Subject: [PATCH 358/609] drm-backend: stop parsing IN_FORMATS blobs, use libdrm instead Before this change the drm-backend in Weston did the work of parsing DRM blobs in order to query IN_FORMATS data, if available. This is also the case for other DRM/KMS clients that use IN_FORMATS (i.e. X). libdrm 2.4.108 with e641e2a6 ("xf86drm: add iterator API for DRM/KMS IN_FORMATS blobs") introduced a dedicated API for querying IN_FORMATS data. Bump the minimum required version to 2.4.108, stop parsing IN_FORMATS in Weston and start using DRM iterators. In addition, remove fallback code for libdrm <2.4.107. Signed-off-by: Luigi Santivetti --- libweston/backend-drm/kms.c | 59 ++++++++++--------------------------- libweston/pixel-formats.c | 10 ------- meson.build | 8 +---- 3 files changed, 16 insertions(+), 61 deletions(-) diff --git a/libweston/backend-drm/kms.c b/libweston/backend-drm/kms.c index 4b22f69d..dbdf72e4 100644 --- a/libweston/backend-drm/kms.c +++ b/libweston/backend-drm/kms.c @@ -414,19 +414,6 @@ drm_property_info_free(struct drm_property_info *info, int num_props) memset(info, 0, sizeof(*info) * num_props); } -static inline uint32_t * -formats_ptr(struct drm_format_modifier_blob *blob) -{ - return (uint32_t *)(((char *)blob) + blob->formats_offset); -} - -static inline struct drm_format_modifier * -modifiers_ptr(struct drm_format_modifier_blob *blob) -{ - return (struct drm_format_modifier *) - (((char *)blob) + blob->modifiers_offset); -} - /** * Populates the plane's formats array, using either the IN_FORMATS blob * property (if available), or the plane's format list if not. @@ -437,13 +424,10 @@ drm_plane_populate_formats(struct drm_plane *plane, const drmModePlane *kplane, const bool use_modifiers) { struct drm_device *device = plane->device; - unsigned i, j; + uint32_t i, blob_id, fmt_prev = DRM_FORMAT_INVALID; + drmModeFormatModifierIterator drm_iter = {0}; + struct weston_drm_format *fmt = NULL; drmModePropertyBlobRes *blob = NULL; - struct drm_format_modifier_blob *fmt_mod_blob; - struct drm_format_modifier *blob_modifiers; - uint32_t *blob_formats; - uint32_t blob_id; - struct weston_drm_format *fmt; int ret = 0; if (!use_modifiers) @@ -459,35 +443,22 @@ drm_plane_populate_formats(struct drm_plane *plane, const drmModePlane *kplane, if (!blob) goto fallback; - fmt_mod_blob = blob->data; - blob_formats = formats_ptr(fmt_mod_blob); - blob_modifiers = modifiers_ptr(fmt_mod_blob); - - assert(kplane->count_formats == fmt_mod_blob->count_formats); + while (drmModeFormatModifierBlobIterNext(blob, &drm_iter)) { + if (fmt_prev != drm_iter.fmt) { + fmt = weston_drm_format_array_add_format(&plane->formats, + drm_iter.fmt); + if (!fmt) { + ret = -1; + goto out; + } - for (i = 0; i < fmt_mod_blob->count_formats; i++) { - fmt = weston_drm_format_array_add_format(&plane->formats, - blob_formats[i]); - if (!fmt) { - ret = -1; - goto out; + fmt_prev = drm_iter.fmt; } - for (j = 0; j < fmt_mod_blob->count_modifiers; j++) { - struct drm_format_modifier *mod = &blob_modifiers[j]; - - if ((i < mod->offset) || (i > mod->offset + 63)) - continue; - if (!(mod->formats & (1 << (i - mod->offset)))) - continue; - - ret = weston_drm_format_add_modifier(fmt, mod->modifier); - if (ret < 0) - goto out; - } + ret = weston_drm_format_add_modifier(fmt, drm_iter.mod); + if (ret < 0) + goto out; - if (fmt->modifiers.size == 0) - weston_drm_format_array_remove_latest_format(&plane->formats); } out: diff --git a/libweston/pixel-formats.c b/libweston/pixel-formats.c index 5793621b..261946c7 100644 --- a/libweston/pixel-formats.c +++ b/libweston/pixel-formats.c @@ -708,7 +708,6 @@ pixel_format_height_for_plane(const struct pixel_format_info *info, return height / pixel_format_vsub(info, plane); } -#ifdef HAVE_HUMAN_FORMAT_MODIFIER WL_EXPORT char * pixel_format_get_modifier(uint64_t modifier) { @@ -748,12 +747,3 @@ pixel_format_get_modifier(uint64_t modifier) return mod_str; } -#else -WL_EXPORT char * -pixel_format_get_modifier(uint64_t modifier) -{ - char *mod_str; - str_printf(&mod_str, "0x%llx", (unsigned long long) modifier); - return mod_str; -} -#endif diff --git a/meson.build b/meson.build index 994c7a56..cc266b5c 100644 --- a/meson.build +++ b/meson.build @@ -148,16 +148,10 @@ dep_libinput = dependency('libinput', version: '>= 0.8.0') dep_libevdev = dependency('libevdev') dep_libm = cc.find_library('m') dep_libdl = cc.find_library('dl') -dep_libdrm = dependency('libdrm', version: '>= 2.4.95') +dep_libdrm = dependency('libdrm', version: '>= 2.4.108') dep_libdrm_headers = dep_libdrm.partial_dependency(compile_args: true) dep_threads = dependency('threads') -dep_libdrm_version = dep_libdrm.version() -if dep_libdrm_version.version_compare('>=2.4.107') - message('Found libdrm with human format modifier support.') - config_h.set('HAVE_HUMAN_FORMAT_MODIFIER', '1') -endif - dep_lcms2 = dependency('lcms2', version: '>= 2.9', required: false) prog_python = import('python').find_installation('python3') From 8b654c47ebd860bc11a57417cb362ef123ca1562 Mon Sep 17 00:00:00 2001 From: Luigi Santivetti Date: Mon, 6 Jun 2022 17:01:46 +0100 Subject: [PATCH 359/609] Revert "backend-drm: add HDR_OUTPUT_METADATA definitions" This reverts commit 6914064066b95cd65883c2c987c5f80f0a48b910. This is a follow-up change of b623fd2a ("drm-backend: stop parsing IN_FORMATS blobs, use libdrm instead"). Weston now has a hard-requirement on libdrm 2.4.108, clean up remaining and unnecessary conditional code. Change 69140640 ("backend-drm: add HDR_OUTPUT_METADATA definitions") is no longer needed and stop including libdrm-updates.h from kms-color.c. Signed-off-by: Luigi Santivetti --- doc/sphinx/doxygen.ini.in | 2 +- libweston/backend-drm/kms-color.c | 1 - libweston/backend-drm/libdrm-updates.h | 124 ------------------------- libweston/backend-drm/meson.build | 5 - 4 files changed, 1 insertion(+), 131 deletions(-) delete mode 100644 libweston/backend-drm/libdrm-updates.h diff --git a/doc/sphinx/doxygen.ini.in b/doc/sphinx/doxygen.ini.in index 76b5b363..bfe77e8e 100644 --- a/doc/sphinx/doxygen.ini.in +++ b/doc/sphinx/doxygen.ini.in @@ -888,7 +888,7 @@ EXCLUDE_SYMLINKS = NO # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* -EXCLUDE_PATTERNS = */backend-drm/libdrm-updates.h +EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the diff --git a/libweston/backend-drm/kms-color.c b/libweston/backend-drm/kms-color.c index 2f9ad945..610b2442 100644 --- a/libweston/backend-drm/kms-color.c +++ b/libweston/backend-drm/kms-color.c @@ -32,7 +32,6 @@ #include #include "drm-internal.h" -#include "libdrm-updates.h" static inline uint16_t color_xy_to_u16(float v) diff --git a/libweston/backend-drm/libdrm-updates.h b/libweston/backend-drm/libdrm-updates.h deleted file mode 100644 index 73a24b67..00000000 --- a/libweston/backend-drm/libdrm-updates.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (c) 2007 Dave Airlie - * Copyright (c) 2007 Jakob Bornecrantz - * Copyright (c) 2008 Red Hat Inc. - * Copyright (c) 2007-2008 Tungsten Graphics, Inc., Cedar Park, TX., USA - * Copyright (c) 2007-2008 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#pragma once - -#include - -/* - * This header contains fallback definitions copied from libdrm upstream to - * avoid hard-depending on too new libdrm. - */ - -#if !HAVE_LIBDRM_HDR - -/** - * struct hdr_metadata_infoframe - HDR Metadata Infoframe Data. - * - * HDR Metadata Infoframe as per CTA 861.G spec. This is expected - * to match exactly with the spec. - * - * Userspace is expected to pass the metadata information as per - * the format described in this structure. - */ -struct hdr_metadata_infoframe { - /** - * @eotf: Electro-Optical Transfer Function (EOTF) - * used in the stream. - */ - __u8 eotf; - /** - * @metadata_type: Static_Metadata_Descriptor_ID. - */ - __u8 metadata_type; - /** - * @display_primaries: Color Primaries of the Data. - * These are coded as unsigned 16-bit values in units of - * 0.00002, where 0x0000 represents zero and 0xC350 - * represents 1.0000. - * @display_primaries.x: X cordinate of color primary. - * @display_primaries.y: Y cordinate of color primary. - */ - struct { - __u16 x, y; - } display_primaries[3]; - /** - * @white_point: White Point of Colorspace Data. - * These are coded as unsigned 16-bit values in units of - * 0.00002, where 0x0000 represents zero and 0xC350 - * represents 1.0000. - * @white_point.x: X cordinate of whitepoint of color primary. - * @white_point.y: Y cordinate of whitepoint of color primary. - */ - struct { - __u16 x, y; - } white_point; - /** - * @max_display_mastering_luminance: Max Mastering Display Luminance. - * This value is coded as an unsigned 16-bit value in units of 1 cd/m2, - * where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2. - */ - __u16 max_display_mastering_luminance; - /** - * @min_display_mastering_luminance: Min Mastering Display Luminance. - * This value is coded as an unsigned 16-bit value in units of - * 0.0001 cd/m2, where 0x0001 represents 0.0001 cd/m2 and 0xFFFF - * represents 6.5535 cd/m2. - */ - __u16 min_display_mastering_luminance; - /** - * @max_cll: Max Content Light Level. - * This value is coded as an unsigned 16-bit value in units of 1 cd/m2, - * where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2. - */ - __u16 max_cll; - /** - * @max_fall: Max Frame Average Light Level. - * This value is coded as an unsigned 16-bit value in units of 1 cd/m2, - * where 0x0001 represents 1 cd/m2 and 0xFFFF represents 65535 cd/m2. - */ - __u16 max_fall; -}; - -/** - * struct hdr_output_metadata - HDR output metadata - * - * Metadata Information to be passed from userspace - */ -struct hdr_output_metadata { - /** - * @metadata_type: Static_Metadata_Descriptor_ID. - */ - __u32 metadata_type; - /** - * @hdmi_metadata_type1: HDR Metadata Infoframe. - */ - union { - struct hdr_metadata_infoframe hdmi_metadata_type1; - }; -}; - -#endif /* !HAVE_LIBDRM_HDR */ diff --git a/libweston/backend-drm/meson.build b/libweston/backend-drm/meson.build index cb2ebda0..bf7ce33b 100644 --- a/libweston/backend-drm/meson.build +++ b/libweston/backend-drm/meson.build @@ -19,11 +19,6 @@ dep_backlight = declare_dependency( config_h.set('BUILD_DRM_COMPOSITOR', '1') -config_h.set10( - 'HAVE_LIBDRM_HDR', - dep_libdrm.version().version_compare('>= 2.4.104') -) - srcs_drm = [ 'drm.c', 'fb.c', From 2afb812d1e854a7f5240cb67b0c7c6cddcc7ba26 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Mon, 6 Jun 2022 14:19:07 -0500 Subject: [PATCH 360/609] shared/cairo-util: Hold onto our pattern reference until we're done This doesn't actually fix a bug - cairo refcounts this. But I really don't like the look of code that drops a reference then continues to use it. While we're here, set a different pattern when we're done so the one we allocated loses its last reference. Signed-off-by: Derek Foreman --- shared/cairo-util.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/shared/cairo-util.c b/shared/cairo-util.c index 3bdf9654..a22610c0 100644 --- a/shared/cairo-util.c +++ b/shared/cairo-util.c @@ -275,7 +275,6 @@ tile_source(cairo_t *cr, cairo_surface_t *surface, pattern = cairo_pattern_create_for_surface (surface); cairo_pattern_set_filter(pattern, CAIRO_FILTER_NEAREST); cairo_set_source(cr, pattern); - cairo_pattern_destroy(pattern); for (i = 0; i < 4; i++) { fx = i & 1; @@ -328,6 +327,9 @@ tile_source(cairo_t *cr, cairo_surface_t *surface, cairo_rectangle(cr, x + width - margin, y + top_margin, margin, height - margin - top_margin); cairo_fill(cr); + + cairo_pattern_destroy(pattern); + cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); } void From da386c827e82529c434add89c6ff7ac0ac65c1e1 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Tue, 7 Jun 2022 11:52:35 -0500 Subject: [PATCH 361/609] rdp: Update to newer FreeRDP release Update to a newer FreeRDP release so we can start cleaning up some of our usage of things that will be deprecated in the next major release. For this, I've simply picked the newest version currently in our CI images. Signed-off-by: Derek Foreman --- libweston/backend-rdp/meson.build | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/libweston/backend-rdp/meson.build b/libweston/backend-rdp/meson.build index 5794987d..2c057066 100644 --- a/libweston/backend-rdp/meson.build +++ b/libweston/backend-rdp/meson.build @@ -4,19 +4,19 @@ endif config_h.set('BUILD_RDP_COMPOSITOR', '1') -dep_frdp = dependency('freerdp2', version: '>= 2.2.0', required: false) +dep_frdp = dependency('freerdp2', version: '>= 2.3.0', required: false) if not dep_frdp.found() - error('RDP-backend requires freerdp >= 2.2.0 which was not found. Or, you can use \'-Dbackend-rdp=false\'.') + error('RDP-backend requires freerdp >= 2.3.0 which was not found. Or, you can use \'-Dbackend-rdp=false\'.') endif -dep_frdp_server = dependency('freerdp-server2', version: '>= 2.2.0', required: false) +dep_frdp_server = dependency('freerdp-server2', version: '>= 2.3.0', required: false) if not dep_frdp_server.found() - error('RDP-backend requires freerdp-server2 >= 2.2.0 which was not found. Or, you can use \'-Dbackend-rdp=false\'.') + error('RDP-backend requires freerdp-server2 >= 2.3.0 which was not found. Or, you can use \'-Dbackend-rdp=false\'.') endif -dep_wpr = dependency('winpr2', version: '>= 2.2.0', required: false) +dep_wpr = dependency('winpr2', version: '>= 2.3.0', required: false) if not dep_wpr.found() - error('RDP-backend requires winpr >= 2.2.0 which was not found. Or, you can use \'-Dbackend-rdp=false\'.') + error('RDP-backend requires winpr >= 2.3.0 which was not found. Or, you can use \'-Dbackend-rdp=false\'.') endif deps_rdp = [ From 5014eb03a3d2671f1e8613a235b287550a7463f1 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Tue, 7 Jun 2022 11:42:09 -0500 Subject: [PATCH 362/609] rdp: Update to new FreeRDP structure layout In an upcoming release the old style will be deprecated, so let's update now. Signed-off-by: Derek Foreman --- libweston/backend-rdp/rdp.c | 40 ++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index 716a13e2..ff3585e5 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -60,7 +60,7 @@ rdp_peer_refresh_rfx(pixman_region32_t *damage, pixman_image_t *image, freerdp_p pixman_box32_t *region, *rects; uint32_t *ptr; RFX_RECT *rfxRect; - rdpUpdate *update = peer->update; + rdpUpdate *update = peer->context->update; SURFACE_BITS_COMMAND cmd = { 0 }; RdpPeerContext *context = (RdpPeerContext *)peer->context; @@ -77,7 +77,7 @@ rdp_peer_refresh_rfx(pixman_region32_t *damage, pixman_image_t *image, freerdp_p cmd.destRight = damage->extents.x2; cmd.destBottom = damage->extents.y2; cmd.bmp.bpp = 32; - cmd.bmp.codecID = peer->settings->RemoteFxCodecId; + cmd.bmp.codecID = peer->context->settings->RemoteFxCodecId; cmd.bmp.width = width; cmd.bmp.height = height; @@ -114,7 +114,7 @@ rdp_peer_refresh_nsc(pixman_region32_t *damage, pixman_image_t *image, freerdp_p { int width, height; uint32_t *ptr; - rdpUpdate *update = peer->update; + rdpUpdate *update = peer->context->update; SURFACE_BITS_COMMAND cmd = { 0 }; RdpPeerContext *context = (RdpPeerContext *)peer->context; @@ -131,7 +131,7 @@ rdp_peer_refresh_nsc(pixman_region32_t *damage, pixman_image_t *image, freerdp_p cmd.destRight = damage->extents.x2; cmd.destBottom = damage->extents.y2; cmd.bmp.bpp = 32; - cmd.bmp.codecID = peer->settings->NSCodecId; + cmd.bmp.codecID = peer->context->settings->NSCodecId; cmd.bmp.width = width; cmd.bmp.height = height; @@ -165,7 +165,7 @@ pixman_image_flipped_subrect(const pixman_box32_t *rect, pixman_image_t *img, BY static void rdp_peer_refresh_raw(pixman_region32_t *region, pixman_image_t *image, freerdp_peer *peer) { - rdpUpdate *update = peer->update; + rdpUpdate *update = peer->context->update; SURFACE_BITS_COMMAND cmd = { 0 }; SURFACE_FRAME_MARKER marker; pixman_box32_t *rect, subrect; @@ -190,7 +190,7 @@ rdp_peer_refresh_raw(pixman_region32_t *region, pixman_image_t *image, freerdp_p cmd.destRight = rect->x2; cmd.bmp.width = (rect->x2 - rect->x1); - heightIncrement = peer->settings->MultifragMaxRequestSize / (16 + cmd.bmp.width * 4); + heightIncrement = peer->context->settings->MultifragMaxRequestSize / (16 + cmd.bmp.width * 4); remainingHeight = rect->y2 - rect->y1; top = rect->y1; @@ -227,7 +227,7 @@ rdp_peer_refresh_region(pixman_region32_t *region, freerdp_peer *peer) { RdpPeerContext *context = (RdpPeerContext *)peer->context; struct rdp_output *output = context->rdpBackend->output; - rdpSettings *settings = peer->settings; + rdpSettings *settings = peer->context->settings; if (settings->RemoteFxCodec) rdp_peer_refresh_rfx(region, output->shadow_surface, peer); @@ -369,7 +369,7 @@ rdp_switch_mode(struct weston_output *output, struct weston_mode *target_mode) rdpOutput->shadow_surface = new_shadow_buffer; wl_list_for_each(rdpPeer, &rdpOutput->peers, link) { - settings = rdpPeer->peer->settings; + settings = rdpPeer->peer->context->settings; if (settings->DesktopWidth == (UINT32)target_mode->width && settings->DesktopHeight == (UINT32)target_mode->height) continue; @@ -380,7 +380,7 @@ rdp_switch_mode(struct weston_output *output, struct weston_mode *target_mode) } else { settings->DesktopWidth = target_mode->width; settings->DesktopHeight = target_mode->height; - rdpPeer->peer->update->DesktopResize(rdpPeer->peer->context); + rdpPeer->peer->context->update->DesktopResize(rdpPeer->peer->context); } } return 0; @@ -643,8 +643,8 @@ rdp_peer_context_new(freerdp_peer* client, RdpPeerContext* context) return FALSE; context->rfx_context->mode = RLGR3; - context->rfx_context->width = client->settings->DesktopWidth; - context->rfx_context->height = client->settings->DesktopHeight; + context->rfx_context->width = client->context->settings->DesktopWidth; + context->rfx_context->height = client->context->settings->DesktopHeight; rfx_context_set_pixel_format(context->rfx_context, DEFAULT_PIXEL_FORMAT); context->nsc_context = nsc_context_new(); @@ -934,7 +934,7 @@ xf_peer_activate(freerdp_peer* client) b = peerCtx->rdpBackend; peersItem = &peerCtx->item; output = b->output; - settings = client->settings; + settings = client->context->settings; if (!settings->SurfaceCommandsEnabled) { weston_log("client doesn't support required SurfaceCommands\n"); @@ -976,7 +976,7 @@ xf_peer_activate(freerdp_peer* client) } else { settings->DesktopWidth = output->base.width; settings->DesktopHeight = output->base.height; - client->update->DesktopResize(client->context); + client->context->update->DesktopResize(client->context); } } else { /* ask weston to adjust size */ @@ -1043,7 +1043,7 @@ xf_peer_activate(freerdp_peer* client) peersItem->flags |= RDP_PEER_ACTIVATED; /* disable pointer on the client side */ - pointer = client->update->pointer; + pointer = client->context->update->pointer; pointer_system.type = SYSPTR_NULL; pointer->PointerSystem(client->context, &pointer_system); @@ -1396,8 +1396,8 @@ xf_input_keyboard_event(rdpInput *input, UINT16 flags, UINT16 code) /* Korean keyboard support: * WinPR's GetVirtualKeyCodeFromVirtualScanCode() can't handle hangul/hanja keys * hanja and hangeul keys are only present on Korean 103 keyboard (Type 8:SubType 6) */ - if (client->settings->KeyboardType == 8 && - client->settings->KeyboardSubType == 6 && + if (client->context->settings->KeyboardType == 8 && + client->context->settings->KeyboardSubType == 6 && ((full_code == (KBD_FLAGS_EXTENDED | ATKBD_RET_HANJA)) || (full_code == (KBD_FLAGS_EXTENDED | ATKBD_RET_HANGEUL)))) { if (full_code == (KBD_FLAGS_EXTENDED | ATKBD_RET_HANJA)) @@ -1417,7 +1417,7 @@ xf_input_keyboard_event(rdpInput *input, UINT16 flags, UINT16 code) } send_release_key = true; } else { - vk_code = GetVirtualKeyCodeFromVirtualScanCode(full_code, client->settings->KeyboardType); + vk_code = GetVirtualKeyCodeFromVirtualScanCode(full_code, client->context->settings->KeyboardType); } /* Korean keyboard support */ /* WinPR's GetKeycodeFromVirtualKeyCode() expects no extended bit for VK_HANGUL and VK_HANJA */ @@ -1488,7 +1488,7 @@ rdp_peer_init(freerdp_peer *client, struct rdp_backend *b) peerCtx = (RdpPeerContext *) client->context; peerCtx->rdpBackend = b; - settings = client->settings; + settings = client->context->settings; /* configure security settings */ if (b->rdp_key) settings->RdpKeyFile = strdup(b->rdp_key); @@ -1521,9 +1521,9 @@ rdp_peer_init(freerdp_peer *client, struct rdp_backend *b) client->PostConnect = xf_peer_post_connect; client->Activate = xf_peer_activate; - client->update->SuppressOutput = (pSuppressOutput)xf_suppress_output; + client->context->update->SuppressOutput = (pSuppressOutput)xf_suppress_output; - input = client->input; + input = client->context->input; input->SynchronizeEvent = xf_input_synchronize_event; input->MouseEvent = xf_mouseEvent; input->ExtendedMouseEvent = xf_extendedMouseEvent; From 982e59a94219aee031cafc2eca40e7047872dabd Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Tue, 7 Jun 2022 11:50:28 -0500 Subject: [PATCH 363/609] rdp: Stop using deprecated functions The get file descriptor functions are being deprecated and a two step process of getting handles and then getting the descriptors for the handles is being used instead. Signed-off-by: Derek Foreman --- libweston/backend-rdp/rdp.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index ff3585e5..8f40e81f 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -606,18 +606,19 @@ static int rdp_implant_listener(struct rdp_backend *b, freerdp_listener* instance) { int i, fd; - int rcount = 0; - void* rfds[MAX_FREERDP_FDS]; + int handle_count = 0; + HANDLE handles[MAX_FREERDP_FDS]; struct wl_event_loop *loop; - if (!instance->GetFileDescriptor(instance, rfds, &rcount)) { - weston_log("Failed to get FreeRDP file descriptor\n"); + handle_count = instance->GetEventHandles(instance, handles, MAX_FREERDP_FDS); + if (!handle_count) { + weston_log("Failed to get FreeRDP handles\n"); return -1; } loop = wl_display_get_event_loop(b->compositor->wl_display); - for (i = 0; i < rcount; i++) { - fd = (int)(long)(rfds[i]); + for (i = 0; i < handle_count; i++) { + fd = GetEventFileDescriptor(handles[i]); b->listener_events[i] = wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE, rdp_listener_activity, instance); } @@ -1472,8 +1473,8 @@ xf_suppress_output(rdpContext *context, BYTE allow, const RECTANGLE_16 *area) static int rdp_peer_init(freerdp_peer *client, struct rdp_backend *b) { - int rcount = 0; - void *rfds[MAX_FREERDP_FDS + 1]; /* +1 for WTSVirtualChannelManagerGetFileDescriptor. */ + int handle_count = 0; + HANDLE handles[MAX_FREERDP_FDS + 1]; /* +1 for virtual channel */ int i, fd; struct wl_event_loop *loop; rdpSettings *settings; @@ -1530,8 +1531,9 @@ rdp_peer_init(freerdp_peer *client, struct rdp_backend *b) input->KeyboardEvent = xf_input_keyboard_event; input->UnicodeKeyboardEvent = xf_input_unicode_keyboard_event; - if (!client->GetFileDescriptor(client, rfds, &rcount)) { - weston_log("unable to retrieve client fds\n"); + handle_count = client->GetEventHandles(client, handles, MAX_FREERDP_FDS); + if (!handle_count) { + weston_log("unable to retrieve client handles\n"); goto error_initialize; } @@ -1539,14 +1541,14 @@ rdp_peer_init(freerdp_peer *client, struct rdp_backend *b) WTSRegisterWtsApiFunctionTable(fn); peerCtx->vcm = WTSOpenServerA((LPSTR)peerCtx); if (peerCtx->vcm) { - WTSVirtualChannelManagerGetFileDescriptor(peerCtx->vcm, rfds, &rcount); + handles[handle_count++] = WTSVirtualChannelManagerGetEventHandle(peerCtx->vcm); } else { weston_log("WTSOpenServer is failed! continue without virtual channel.\n"); } loop = wl_display_get_event_loop(b->compositor->wl_display); - for (i = 0; i < rcount; i++) { - fd = (int)(long)(rfds[i]); + for (i = 0; i < handle_count; i++) { + fd = GetEventFileDescriptor(handles[i]); peerCtx->events[i] = wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE, rdp_client_activity, client); From 54d7682ee844e8bdb2fe9fe9ad445eab30a82999 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 29 Mar 2021 14:05:23 +0200 Subject: [PATCH 364/609] libweston: add opaque backend_id pointer to struct weston_head As a first step towards heterogeneous outputs, add an opaque pointer weston_head::backend_id that will be used by backends to identify their own heads. See: https://gitlab.freedesktop.org/wayland/weston/-/issues/268 Signed-off-by: Philipp Zabel --- include/libweston/libweston.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index c361d76a..c5f6f8cd 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -395,6 +395,9 @@ struct weston_head { /** Current content protection status */ enum weston_hdcp_protection current_protection; + + /** Opaque pointer used by backends to identify heads as theirs */ + const void *backend_id; }; /** Output properties derived from its color characteristics and profile From aab722bb178584e418eb777392773e36f4647c2e Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Tue, 11 May 2021 17:22:50 +0200 Subject: [PATCH 365/609] backend-drm: prepare virtual output API for heterogeneous outputs Stop plugins from overwriting the struct weston_output::destroy vfunc, as that will be used by backends to recognize their outputs. Instead, pass a plugin-specific destroy callback when creating the virtual output. See: https://gitlab.freedesktop.org/wayland/weston/-/issues/268 Signed-off-by: Philipp Zabel --- include/libweston/backend-drm.h | 10 +++++++--- libweston/backend-drm/drm-internal.h | 1 + libweston/backend-drm/drm-virtual.c | 7 ++++++- pipewire/pipewire-plugin.c | 7 +------ remoting/remoting-plugin.c | 7 +------ 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/include/libweston/backend-drm.h b/include/libweston/backend-drm.h index f85aabdb..02038e6f 100644 --- a/include/libweston/backend-drm.h +++ b/include/libweston/backend-drm.h @@ -90,7 +90,7 @@ weston_drm_output_get_api(struct weston_compositor *compositor) return (const struct weston_drm_output_api *)api; } -#define WESTON_DRM_VIRTUAL_OUTPUT_API_NAME "weston_drm_virtual_output_api_v1" +#define WESTON_DRM_VIRTUAL_OUTPUT_API_NAME "weston_drm_virtual_output_api_v2" struct drm_fb; typedef int (*submit_frame_cb)(struct weston_output *output, int fd, @@ -101,12 +101,16 @@ struct weston_drm_virtual_output_api { * This is a low-level function, where the caller is expected to wrap * the weston_output function pointers as necessary to make the virtual * output useful. The caller must set up output make, model, serial, - * physical size, the mode list and current mode. + * physical size, the mode list and current mode. The destroy function + * pointer must not be overwritten, as it is used by the DRM backend to + * recognize its outputs. Instead, an auxiliary destroy callback has to + * be provided as a parameter. * * Returns output on success, NULL on failure. */ struct weston_output* (*create_output)(struct weston_compositor *c, - char *name); + char *name, + void (*destroy_func)(struct weston_output *base)); /** Set pixel format same as drm_output set_gbm_format(). * diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index 107f91e7..77200f2f 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -579,6 +579,7 @@ struct drm_output { struct wl_event_source *pageflip_timer; bool virtual; + void (*virtual_destroy)(struct weston_output *base); submit_frame_cb virtual_submit_frame; }; diff --git a/libweston/backend-drm/drm-virtual.c b/libweston/backend-drm/drm-virtual.c index d2b4c8e3..8f1d3abf 100644 --- a/libweston/backend-drm/drm-virtual.c +++ b/libweston/backend-drm/drm-virtual.c @@ -259,6 +259,9 @@ drm_virtual_output_destroy(struct weston_output *base) drm_output_state_free(output->state_cur); + if (output->virtual_destroy) + output->virtual_destroy(base); + free(output); } @@ -324,7 +327,8 @@ drm_virtual_output_disable(struct weston_output *base) } static struct weston_output * -drm_virtual_output_create(struct weston_compositor *c, char *name) +drm_virtual_output_create(struct weston_compositor *c, char *name, + void (*destroy_func)(struct weston_output *)) { struct drm_output *output; struct drm_backend *b = to_drm_backend(c); @@ -343,6 +347,7 @@ drm_virtual_output_create(struct weston_compositor *c, char *name) } output->virtual = true; + output->virtual_destroy = destroy_func; output->gbm_bo_flags = GBM_BO_USE_LINEAR | GBM_BO_USE_RENDERING; weston_output_init(&output->base, c, name); diff --git a/pipewire/pipewire-plugin.c b/pipewire/pipewire-plugin.c index 23cea2e8..9a913aa3 100644 --- a/pipewire/pipewire-plugin.c +++ b/pipewire/pipewire-plugin.c @@ -67,7 +67,6 @@ struct weston_pipewire { struct pipewire_output { struct weston_output *output; - void (*saved_destroy)(struct weston_output *output); int (*saved_enable)(struct weston_output *output); int (*saved_disable)(struct weston_output *output); int (*saved_start_repaint_loop)(struct weston_output *output); @@ -316,8 +315,6 @@ pipewire_output_destroy(struct weston_output *base_output) free(mode); } - output->saved_destroy(base_output); - pw_stream_destroy(output->stream); wl_list_remove(&output->link); @@ -536,14 +533,12 @@ pipewire_output_create(struct weston_compositor *c, char *name) pw_stream_add_listener(output->stream, &output->stream_listener, &stream_events, output); - output->output = api->create_output(c, name); + output->output = api->create_output(c, name, pipewire_output_destroy); if (!output->output) { weston_log("Cannot create virtual output\n"); goto err; } - output->saved_destroy = output->output->destroy; - output->output->destroy = pipewire_output_destroy; output->saved_enable = output->output->enable; output->output->enable = pipewire_output_enable; output->saved_disable = output->output->disable; diff --git a/remoting/remoting-plugin.c b/remoting/remoting-plugin.c index 7d1d00f4..0af43b5b 100644 --- a/remoting/remoting-plugin.c +++ b/remoting/remoting-plugin.c @@ -95,7 +95,6 @@ static const struct remoted_output_support_gbm_format supported_formats[] = { struct remoted_output { struct weston_output *output; - void (*saved_destroy)(struct weston_output *output); int (*saved_enable)(struct weston_output *output); int (*saved_disable)(struct weston_output *output); int (*saved_start_repaint_loop)(struct weston_output *output); @@ -642,8 +641,6 @@ remoting_output_destroy(struct weston_output *output) free(mode); } - remoted_output->saved_destroy(output); - remoting_gst_pipeline_deinit(remoted_output); remoting_gstpipe_release(&remoted_output->gstpipe); @@ -763,14 +760,12 @@ remoting_output_create(struct weston_compositor *c, char *name) goto err; } - output->output = api->create_output(c, name); + output->output = api->create_output(c, name, remoting_output_destroy); if (!output->output) { weston_log("Can not create virtual output\n"); goto err; } - output->saved_destroy = output->output->destroy; - output->output->destroy = remoting_output_destroy; output->saved_enable = output->output->enable; output->output->enable = remoting_output_enable; output->saved_disable = output->output->disable; From ffc011d6a30d5e772e4f849abc5c60faf3ecb91a Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 11 Mar 2021 11:04:32 +0100 Subject: [PATCH 366/609] backend-drm: check that outputs and heads are in fact ours As a first step towards heterogeneous outputs, ignore other backends' heads and outputs. This is done by checking the destroy callbacks for heads and outputs. See: https://gitlab.freedesktop.org/wayland/weston/-/issues/268 Signed-off-by: Philipp Zabel --- libweston/backend-drm/drm-internal.h | 16 ++++++++++ libweston/backend-drm/drm-virtual.c | 2 +- libweston/backend-drm/drm.c | 42 ++++++++++++++++++--------- libweston/backend-drm/kms.c | 2 ++ libweston/backend-drm/state-propose.c | 2 ++ 5 files changed, 50 insertions(+), 14 deletions(-) diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index 77200f2f..60ab2352 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -584,15 +584,31 @@ struct drm_output { submit_frame_cb virtual_submit_frame; }; +void +drm_head_destroy(struct weston_head *head_base); + static inline struct drm_head * to_drm_head(struct weston_head *base) { + if (base->backend_id != drm_head_destroy) + return NULL; return container_of(base, struct drm_head, base); } +void +drm_output_destroy(struct weston_output *output_base); +void +drm_virtual_output_destroy(struct weston_output *output_base); + static inline struct drm_output * to_drm_output(struct weston_output *base) { + if ( +#ifdef BUILD_DRM_VIRTUAL + base->destroy != drm_virtual_output_destroy && +#endif + base->destroy != drm_output_destroy) + return NULL; return container_of(base, struct drm_output, base); } diff --git a/libweston/backend-drm/drm-virtual.c b/libweston/backend-drm/drm-virtual.c index 8f1d3abf..cf8de76a 100644 --- a/libweston/backend-drm/drm-virtual.c +++ b/libweston/backend-drm/drm-virtual.c @@ -245,7 +245,7 @@ drm_virtual_output_deinit(struct weston_output *base) drm_virtual_crtc_destroy(output->crtc); } -static void +void drm_virtual_output_destroy(struct weston_output *base) { struct drm_output *output = to_drm_output(base); diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index aa55fc41..f06d8c72 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -164,9 +164,6 @@ drm_output_pageflip_timer_create(struct drm_output *output) return 0; } -static void -drm_output_destroy(struct weston_output *output_base); - /** * Returns true if the plane can be used on the given output for its current * repaint cycle. @@ -214,6 +211,8 @@ drm_head_find_by_connector(struct drm_backend *backend, uint32_t connector_id) wl_list_for_each(base, &backend->compositor->head_list, compositor_link) { head = to_drm_head(base); + if (!head) + continue; if (head->connector.connector_id == connector_id) return head; } @@ -451,6 +450,7 @@ drm_output_repaint(struct weston_output *output_base, pixman_region32_t *damage) struct drm_pending_state *pending_state; struct drm_device *device; + assert(output); assert(!output->virtual); device = output->device; @@ -689,8 +689,11 @@ drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mo struct drm_output *output = to_drm_output(output_base); struct drm_device *device = output->device; struct drm_backend *b = device->backend; - struct drm_mode *drm_mode = drm_output_choose_mode(output, mode); + struct drm_mode *drm_mode; + + assert(output); + drm_mode = drm_output_choose_mode(output, mode); if (!drm_mode) { weston_log("%s: invalid resolution %dx%d\n", output_base->name, mode->width, mode->height); @@ -1053,6 +1056,7 @@ drm_set_dpms(struct weston_output *output_base, enum dpms_enum level) struct drm_output_state *state; int ret; + assert(output); assert(!output->virtual); if (output->state_cur->dpms == level) @@ -1491,6 +1495,8 @@ drm_output_pick_crtc(struct drm_output *output) match = false; wl_list_for_each(base, &compositor->head_list, compositor_link) { head = to_drm_head(base); + if (!head) + continue; if (head->base.output == &output->base) continue; @@ -1808,6 +1814,7 @@ drm_output_enable(struct weston_output *base) struct drm_backend *b = device->backend; int ret; + assert(output); assert(!output->virtual); if (output->gbm_format == DRM_FORMAT_INVALID) { @@ -1885,15 +1892,13 @@ drm_output_deinit(struct weston_output *base) } } -static void -drm_head_destroy(struct drm_head *head); - -static void +void drm_output_destroy(struct weston_output *base) { struct drm_output *output = to_drm_output(base); struct drm_device *device = output->device; + assert(output); assert(!output->virtual); if (output->page_flip_pending || output->atomic_complete_pending) { @@ -1927,6 +1932,7 @@ drm_output_disable(struct weston_output *base) { struct drm_output *output = to_drm_output(base); + assert(output); assert(!output->virtual); if (output->page_flip_pending || output->atomic_complete_pending) { @@ -2197,6 +2203,8 @@ drm_head_create(struct drm_device *device, drmModeConnector *conn, weston_head_init(&head->base, name); free(name); + head->base.backend_id = drm_head_destroy; + ret = drm_head_update_info(head, conn); if (ret < 0) goto err_update; @@ -2226,9 +2234,13 @@ err: return -1; } -static void -drm_head_destroy(struct drm_head *head) +void +drm_head_destroy(struct weston_head *base) { + struct drm_head *head = to_drm_head(base); + + assert(head); + weston_head_release(&head->base); drm_connector_fini(&head->connector); @@ -2466,6 +2478,8 @@ drm_backend_update_connectors(struct drm_device *device, wl_list_for_each_safe(base, base_next, &b->compositor->head_list, compositor_link) { head = to_drm_head(base); + if (!head) + continue; connector_id = head->connector.connector_id; if (head->connector.device != device) @@ -2476,7 +2490,7 @@ drm_backend_update_connectors(struct drm_device *device, weston_log("DRM: head '%s' (connector %d) disappeared.\n", head->base.name, connector_id); - drm_head_destroy(head); + drm_head_destroy(base); } /* Destroy writeback objects of writeback connectors that have @@ -2630,8 +2644,10 @@ drm_destroy(struct weston_compositor *ec) wl_list_for_each_safe(crtc, crtc_tmp, &b->drm->crtc_list, link) drm_crtc_destroy(crtc); - wl_list_for_each_safe(base, next, &ec->head_list, compositor_link) - drm_head_destroy(to_drm_head(base)); + wl_list_for_each_safe(base, next, &ec->head_list, compositor_link) { + if (to_drm_head(base)) + drm_head_destroy(base); + } wl_list_for_each_safe(writeback, writeback_tmp, &b->drm->writeback_connector_list, link) diff --git a/libweston/backend-drm/kms.c b/libweston/backend-drm/kms.c index dbdf72e4..d1be5dbe 100644 --- a/libweston/backend-drm/kms.c +++ b/libweston/backend-drm/kms.c @@ -487,6 +487,8 @@ drm_output_set_gamma(struct weston_output *output_base, struct drm_output *output = to_drm_output(output_base); struct drm_device *device = output->device; + assert(output); + /* check */ if (output_base->gamma_size != size) return; diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index 83b0e049..1f4dccc7 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -926,6 +926,8 @@ drm_assign_planes(struct weston_output *output_base) struct weston_plane *primary = &output_base->compositor->primary_plane; enum drm_output_propose_state_mode mode = DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY; + assert(output); + drm_debug(b, "\t[repaint] preparing state for output %s (%lu)\n", output_base->name, (unsigned long) output_base->id); From 5159af060795dda215a26f93b9c6cfd7b5764db1 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 11 Mar 2021 11:31:14 +0100 Subject: [PATCH 367/609] backend-headless: check that outputs and heads are in fact ours As a first step towards heterogeneous outputs, ignore other backends' heads and outputs. This is done by checking the destroy callbacks for heads and outputs. See: https://gitlab.freedesktop.org/wayland/weston/-/issues/268 Signed-off-by: Philipp Zabel --- libweston/backend-headless/headless.c | 48 +++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/libweston/backend-headless/headless.c b/libweston/backend-headless/headless.c index 7165ce40..bf8309f6 100644 --- a/libweston/backend-headless/headless.c +++ b/libweston/backend-headless/headless.c @@ -79,15 +79,25 @@ static const uint32_t headless_formats[] = { DRM_FORMAT_ARGB8888, }; +static void +headless_head_destroy(struct weston_head *base); + static inline struct headless_head * to_headless_head(struct weston_head *base) { + if (base->backend_id != headless_head_destroy) + return NULL; return container_of(base, struct headless_head, base); } +static void +headless_output_destroy(struct weston_output *base); + static inline struct headless_output * to_headless_output(struct weston_output *base) { + if (base->destroy != headless_output_destroy) + return NULL; return container_of(base, struct headless_output, base); } @@ -125,7 +135,11 @@ headless_output_repaint(struct weston_output *output_base, pixman_region32_t *damage) { struct headless_output *output = to_headless_output(output_base); - struct weston_compositor *ec = output->base.compositor; + struct weston_compositor *ec; + + assert(output); + + ec = output->base.compositor; ec->renderer->repaint_output(&output->base, damage); @@ -158,11 +172,15 @@ static int headless_output_disable(struct weston_output *base) { struct headless_output *output = to_headless_output(base); - struct headless_backend *b = to_headless_backend(base->compositor); + struct headless_backend *b; + + assert(output); if (!output->base.enabled) return 0; + b = to_headless_backend(base->compositor); + wl_event_source_remove(output->finish_frame_timer); switch (b->renderer_type) { @@ -184,6 +202,8 @@ headless_output_destroy(struct weston_output *base) { struct headless_output *output = to_headless_output(base); + assert(output); + headless_output_disable(&output->base); weston_output_release(&output->base); @@ -246,10 +266,14 @@ static int headless_output_enable(struct weston_output *base) { struct headless_output *output = to_headless_output(base); - struct headless_backend *b = to_headless_backend(base->compositor); + struct headless_backend *b; struct wl_event_loop *loop; int ret = 0; + assert(output); + + b = to_headless_backend(base->compositor); + loop = wl_display_get_event_loop(b->compositor->wl_display); output->finish_frame_timer = wl_event_loop_add_timer(loop, finish_frame_handler, output); @@ -286,6 +310,9 @@ headless_output_set_size(struct weston_output *base, struct weston_head *head; int output_width, output_height; + if (!output) + return -1; + /* We can only be called once. */ assert(!output->base.current_mode); @@ -360,6 +387,9 @@ headless_head_create(struct weston_compositor *compositor, return -1; weston_head_init(&head->base, name); + + head->base.backend_id = headless_head_destroy; + weston_head_set_connection_status(&head->base, true); weston_head_set_supported_eotf_mask(&head->base, WESTON_EOTF_MODE_ALL_MASK); @@ -375,8 +405,12 @@ headless_head_create(struct weston_compositor *compositor, } static void -headless_head_destroy(struct headless_head *head) +headless_head_destroy(struct weston_head *base) { + struct headless_head *head = to_headless_head(base); + + assert(head); + weston_head_release(&head->base); free(head); } @@ -389,8 +423,10 @@ headless_destroy(struct weston_compositor *ec) weston_compositor_shutdown(ec); - wl_list_for_each_safe(base, next, &ec->head_list, compositor_link) - headless_head_destroy(to_headless_head(base)); + wl_list_for_each_safe(base, next, &ec->head_list, compositor_link) { + if (to_headless_head(base)) + headless_head_destroy(base); + } free(b); } From 5b41ffa9daf115625b58b5c74047bdf15ae33b11 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 11 Mar 2021 11:27:17 +0100 Subject: [PATCH 368/609] backend-rdp: check that outputs and heads are in fact ours As a first step towards heterogeneous outputs, ignore other backends' heads and outputs. This is done by checking the destroy callbacks for heads and outputs. See: https://gitlab.freedesktop.org/wayland/weston/-/issues/268 Signed-off-by: Philipp Zabel --- libweston/backend-rdp/rdp.c | 45 +++++++++++++++++++++++++++++-------- libweston/backend-rdp/rdp.h | 10 +++++++++ 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index 8f40e81f..b0dd2fc4 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -273,6 +273,8 @@ rdp_output_repaint(struct weston_output *output_base, pixman_region32_t *damage) next_frame_delta = refresh_msec; } + assert(output); + pixman_renderer_output_set_buffer(output_base, output->shadow_surface); ec->renderer->repaint_output(&output->base, damage); @@ -344,6 +346,8 @@ rdp_switch_mode(struct weston_output *output, struct weston_mode *target_mode) struct weston_mode *local_mode; const struct pixman_renderer_output_options options = { .use_shadow = true, }; + assert(output); + local_mode = ensure_matching_mode(output, target_mode); if (!local_mode) { rdp_debug(rdpBackend, "mode %dx%d not available\n", target_mode->width, target_mode->height); @@ -396,6 +400,8 @@ rdp_output_set_size(struct weston_output *base, struct weston_mode *currentMode; struct weston_mode initMode; + assert(output); + /* We can only be called once. */ assert(!output->base.current_mode); @@ -433,12 +439,16 @@ static int rdp_output_enable(struct weston_output *base) { struct rdp_output *output = to_rdp_output(base); - struct rdp_backend *b = to_rdp_backend(base->compositor); + struct rdp_backend *b; struct wl_event_loop *loop; const struct pixman_renderer_output_options options = { .use_shadow = true, }; + assert(output); + + b = to_rdp_backend(base->compositor); + output->shadow_surface = pixman_image_create_bits(PIXMAN_x8r8g8b8, output->base.current_mode->width, output->base.current_mode->height, @@ -466,7 +476,11 @@ static int rdp_output_disable(struct weston_output *base) { struct rdp_output *output = to_rdp_output(base); - struct rdp_backend *b = to_rdp_backend(base->compositor); + struct rdp_backend *b; + + assert(output); + + b = to_rdp_backend(base->compositor); if (!output->base.enabled) return 0; @@ -480,11 +494,13 @@ rdp_output_disable(struct weston_output *base) return 0; } -static void +void rdp_output_destroy(struct weston_output *base) { struct rdp_output *output = to_rdp_output(base); + assert(output); + rdp_output_disable(&output->base); weston_output_release(&output->base); @@ -522,15 +538,22 @@ rdp_head_create(struct weston_compositor *compositor, const char *name) return -1; weston_head_init(&head->base, name); + + head->base.backend_id = rdp_head_destroy; + weston_head_set_connection_status(&head->base, true); weston_compositor_add_head(compositor, &head->base); return 0; } -static void -rdp_head_destroy(struct rdp_head *head) +void +rdp_head_destroy(struct weston_head *base) { + struct rdp_head *head = to_rdp_head(base); + + assert(head); + weston_head_release(&head->base); free(head); } @@ -577,8 +600,10 @@ rdp_destroy(struct weston_compositor *ec) weston_compositor_shutdown(ec); - wl_list_for_each_safe(base, next, &ec->head_list, compositor_link) - rdp_head_destroy(to_rdp_head(base)); + wl_list_for_each_safe(base, next, &ec->head_list, compositor_link) { + if (to_rdp_head(base)) + rdp_head_destroy(base); + } freerdp_listener_free(b->listener); @@ -1734,8 +1759,10 @@ err_output: if (b->output) weston_output_release(&b->output->base); err_compositor: - wl_list_for_each_safe(base, next, &compositor->head_list, compositor_link) - rdp_head_destroy(to_rdp_head(base)); + wl_list_for_each_safe(base, next, &compositor->head_list, compositor_link) { + if (to_rdp_head(base)) + rdp_head_destroy(base); + } weston_compositor_shutdown(compositor); err_free_strings: diff --git a/libweston/backend-rdp/rdp.h b/libweston/backend-rdp/rdp.h index abc03a13..d5e13057 100644 --- a/libweston/backend-rdp/rdp.h +++ b/libweston/backend-rdp/rdp.h @@ -232,15 +232,25 @@ rdp_clipboard_init(freerdp_peer *client); void rdp_clipboard_destroy(RdpPeerContext *peerCtx); +void +rdp_head_destroy(struct weston_head *base); + static inline struct rdp_head * to_rdp_head(struct weston_head *base) { + if (base->backend_id != rdp_head_destroy) + return NULL; return container_of(base, struct rdp_head, base); } +void +rdp_output_destroy(struct weston_output *base); + static inline struct rdp_output * to_rdp_output(struct weston_output *base) { + if (base->destroy != rdp_output_destroy) + return NULL; return container_of(base, struct rdp_output, base); } From 69c4cec4f171dd8d1bcce43ec94ce10a94f1eae3 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Tue, 16 Feb 2021 10:16:11 +0100 Subject: [PATCH 369/609] backend-wayland: check that outputs and heads are in fact ours As a first step towards heterogeneous outputs, ignore other backends' heads and outputs. This is done by checking the destroy callbacks for heads and outputs. See: https://gitlab.freedesktop.org/wayland/weston/-/issues/268 Signed-off-by: Philipp Zabel --- libweston/backend-wayland/wayland.c | 80 ++++++++++++++++++++++++----- 1 file changed, 67 insertions(+), 13 deletions(-) diff --git a/libweston/backend-wayland/wayland.c b/libweston/backend-wayland/wayland.c index 825faed3..e98d7d87 100644 --- a/libweston/backend-wayland/wayland.c +++ b/libweston/backend-wayland/wayland.c @@ -231,15 +231,25 @@ struct wayland_input { struct gl_renderer_interface *gl_renderer; +static void +wayland_head_destroy(struct weston_head *base); + static inline struct wayland_head * to_wayland_head(struct weston_head *base) { + if (base->backend_id != wayland_head_destroy) + return NULL; return container_of(base, struct wayland_head, base); } +static void +wayland_output_destroy(struct weston_output *base); + static inline struct wayland_output * to_wayland_output(struct weston_output *base) { + if (base->destroy != wayland_output_destroy) + return NULL; return container_of(base, struct wayland_output, base); } @@ -499,8 +509,11 @@ static int wayland_output_start_repaint_loop(struct weston_output *output_base) { struct wayland_output *output = to_wayland_output(output_base); - struct wayland_backend *wb = - to_wayland_backend(output->base.compositor); + struct wayland_backend *wb; + + assert(output); + + wb = to_wayland_backend(output->base.compositor); /* If this is the initial frame, we need to attach a buffer so that * the compositor can map the surface and include it in its render @@ -527,7 +540,11 @@ wayland_output_repaint_gl(struct weston_output *output_base, pixman_region32_t *damage) { struct wayland_output *output = to_wayland_output(output_base); - struct weston_compositor *ec = output->base.compositor; + struct weston_compositor *ec; + + assert(output); + + ec = output->base.compositor; output->frame_cb = wl_surface_frame(output->parent.surface); wl_callback_add_listener(output->frame_cb, &frame_listener, output); @@ -638,10 +655,13 @@ wayland_output_repaint_pixman(struct weston_output *output_base, pixman_region32_t *damage) { struct wayland_output *output = to_wayland_output(output_base); - struct wayland_backend *b = - to_wayland_backend(output->base.compositor); + struct wayland_backend *b; struct wayland_shm_buffer *sb; + assert(output); + + b = to_wayland_backend(output->base.compositor); + if (output->frame) { if (frame_status(output->frame) & FRAME_STATUS_REPAINT) wl_list_for_each(sb, &output->shm.buffers, link) @@ -709,7 +729,11 @@ static int wayland_output_disable(struct weston_output *base) { struct wayland_output *output = to_wayland_output(base); - struct wayland_backend *b = to_wayland_backend(base->compositor); + struct wayland_backend *b; + + assert(output); + + b = to_wayland_backend(base->compositor); if (!output->base.enabled) return 0; @@ -743,6 +767,8 @@ wayland_output_destroy(struct weston_output *base) { struct wayland_output *output = to_wayland_output(base); + assert(output); + wayland_output_disable(&output->base); weston_output_release(&output->base); @@ -1045,7 +1071,7 @@ static int wayland_output_switch_mode(struct weston_output *output_base, struct weston_mode *mode) { - struct wayland_output *output = to_wayland_output(output_base); + struct wayland_output *output; struct wayland_backend *b; struct wl_surface *old_surface; struct weston_mode *old_mode; @@ -1056,6 +1082,9 @@ wayland_output_switch_mode(struct weston_output *output_base, return -1; } + output = to_wayland_output(output_base); + assert(output); + if (mode == NULL) { weston_log("mode is NULL.\n"); return -1; @@ -1212,10 +1241,14 @@ static int wayland_output_enable(struct weston_output *base) { struct wayland_output *output = to_wayland_output(base); - struct wayland_backend *b = to_wayland_backend(base->compositor); + struct wayland_backend *b; enum mode_status mode_status; int ret = 0; + assert(output); + + b = to_wayland_backend(base->compositor); + wl_list_init(&output->shm.buffers); wl_list_init(&output->shm.free_buffers); @@ -1291,9 +1324,16 @@ static int wayland_output_attach_head(struct weston_output *output_base, struct weston_head *head_base) { - struct wayland_backend *b = to_wayland_backend(output_base->compositor); struct wayland_output *output = to_wayland_output(output_base); struct wayland_head *head = to_wayland_head(head_base); + struct wayland_backend *b; + + assert(output); + + if (!head) + return -1; + + b = to_wayland_backend(output_base->compositor); if (!wl_list_empty(&output->base.head_list)) return -1; @@ -1318,6 +1358,8 @@ wayland_output_detach_head(struct weston_output *output_base, { struct wayland_output *output = to_wayland_output(output_base); + assert(output); + /* Rely on the disable hook if the output was enabled. We do not * support cloned heads, so detaching is guaranteed to disable the * output. @@ -1376,6 +1418,9 @@ wayland_head_create(struct weston_compositor *compositor, const char *name) return NULL; weston_head_init(&head->base, name); + + head->base.backend_id = wayland_head_destroy; + weston_head_set_connection_status(&head->base, true); weston_compositor_add_head(compositor, &head->base); @@ -1423,8 +1468,12 @@ wayland_head_create_for_parent_output(struct weston_compositor *compositor, } static void -wayland_head_destroy(struct wayland_head *head) +wayland_head_destroy(struct weston_head *base) { + struct wayland_head *head = to_wayland_head(base); + + assert(head); + if (head->parent_output) head->parent_output->head = NULL; @@ -1439,6 +1488,9 @@ wayland_output_set_size(struct weston_output *base, int width, int height) struct weston_head *head; int output_width, output_height; + if (!output) + return -1; + /* We can only be called once. */ assert(!output->base.current_mode); @@ -2555,7 +2607,7 @@ wayland_parent_output_destroy(struct wayland_parent_output *output) wl_callback_destroy(output->sync_cb); if (output->head) - wayland_head_destroy(output->head); + wayland_head_destroy(&output->head->base); wl_output_destroy(output->global); free(output->physical.make); @@ -2670,8 +2722,10 @@ wayland_destroy(struct weston_compositor *ec) weston_compositor_shutdown(ec); - wl_list_for_each_safe(base, next, &ec->head_list, compositor_link) - wayland_head_destroy(to_wayland_head(base)); + wl_list_for_each_safe(base, next, &ec->head_list, compositor_link) { + if (to_wayland_head(base)) + wayland_head_destroy(base); + } wl_list_for_each_safe(input, next_input, &b->input_list, link) wayland_input_destroy(input); From 060ef82d93608b4220db93760080451e59e57c95 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Tue, 16 Feb 2021 11:25:47 +0100 Subject: [PATCH 370/609] backend-x11: check that outputs and heads are in fact ours As a first step towards heterogeneous outputs, ignore other backends' heads and outputs. This is done by checking the destroy callbacks for heads and outputs. See: https://gitlab.freedesktop.org/wayland/weston/-/issues/268 Signed-off-by: Philipp Zabel --- libweston/backend-x11/x11.c | 87 +++++++++++++++++++++++++++++-------- 1 file changed, 69 insertions(+), 18 deletions(-) diff --git a/libweston/backend-x11/x11.c b/libweston/backend-x11/x11.c index 38c72379..bd9d641e 100644 --- a/libweston/backend-x11/x11.c +++ b/libweston/backend-x11/x11.c @@ -151,15 +151,25 @@ struct window_delete_data { struct gl_renderer_interface *gl_renderer; +static void +x11_head_destroy(struct weston_head *base); + static inline struct x11_head * to_x11_head(struct weston_head *base) { + if (base->backend_id != x11_head_destroy) + return NULL; return container_of(base, struct x11_head, base); } +static void +x11_output_destroy(struct weston_output *base); + static inline struct x11_output * to_x11_output(struct weston_output *base) { + if (base->destroy != x11_output_destroy) + return NULL; return container_of(base, struct x11_output, base); } @@ -420,7 +430,11 @@ x11_output_repaint_gl(struct weston_output *output_base, pixman_region32_t *damage) { struct x11_output *output = to_x11_output(output_base); - struct weston_compositor *ec = output->base.compositor; + struct weston_compositor *ec; + + assert(output); + + ec = output->base.compositor; ec->renderer->repaint_output(output_base, damage); @@ -435,8 +449,8 @@ static void set_clip_for_output(struct weston_output *output_base, pixman_region32_t *region) { struct x11_output *output = to_x11_output(output_base); - struct weston_compositor *ec = output->base.compositor; - struct x11_backend *b = to_x11_backend(ec); + struct weston_compositor *ec; + struct x11_backend *b; pixman_region32_t transformed_region; pixman_box32_t *rects; xcb_rectangle_t *output_rects; @@ -444,6 +458,12 @@ set_clip_for_output(struct weston_output *output_base, pixman_region32_t *region int nrects, i; xcb_generic_error_t *err; + if (!output) + return; + + ec = output->base.compositor; + b = to_x11_backend(ec); + pixman_region32_init(&transformed_region); pixman_region32_copy(&transformed_region, region); pixman_region32_translate(&transformed_region, @@ -488,11 +508,16 @@ x11_output_repaint_shm(struct weston_output *output_base, pixman_region32_t *damage) { struct x11_output *output = to_x11_output(output_base); - struct weston_compositor *ec = output->base.compositor; - struct x11_backend *b = to_x11_backend(ec); + struct weston_compositor *ec; + struct x11_backend *b; xcb_void_cookie_t cookie; xcb_generic_error_t *err; + assert(output); + + ec = output->base.compositor; + b = to_x11_backend(ec); + pixman_renderer_output_set_buffer(output_base, output->hw_surface); ec->renderer->repaint_output(output_base, damage); @@ -565,13 +590,13 @@ x11_output_set_wm_protocols(struct x11_backend *b, } struct wm_normal_hints { - uint32_t flags; + uint32_t flags; uint32_t pad[4]; int32_t min_width, min_height; int32_t max_width, max_height; - int32_t width_inc, height_inc; - int32_t min_aspect_x, min_aspect_y; - int32_t max_aspect_x, max_aspect_y; + int32_t width_inc, height_inc; + int32_t min_aspect_x, min_aspect_y; + int32_t max_aspect_x, max_aspect_y; int32_t base_width, base_height; int32_t win_gravity; }; @@ -801,12 +826,13 @@ static int x11_output_switch_mode(struct weston_output *base, struct weston_mode *mode) { struct x11_backend *b; - struct x11_output *output; + struct x11_output *output = to_x11_output(base); static uint32_t values[2]; int ret; + assert(output); + b = to_x11_backend(base->compositor); - output = to_x11_output(base); if (mode->width == output->mode.width && mode->height == output->mode.height) @@ -878,7 +904,11 @@ static int x11_output_disable(struct weston_output *base) { struct x11_output *output = to_x11_output(base); - struct x11_backend *backend = to_x11_backend(base->compositor); + struct x11_backend *backend; + + assert(output); + + backend = to_x11_backend(base->compositor); if (!output->base.enabled) return 0; @@ -903,6 +933,8 @@ x11_output_destroy(struct weston_output *base) { struct x11_output *output = to_x11_output(base); + assert(output); + x11_output_disable(&output->base); weston_output_release(&output->base); @@ -913,7 +945,11 @@ static int x11_output_enable(struct weston_output *base) { struct x11_output *output = to_x11_output(base); - struct x11_backend *b = to_x11_backend(base->compositor); + struct x11_backend *b; + + assert(output); + + b = to_x11_backend(base->compositor); static const char name[] = "Weston Compositor"; static const char class[] = "weston-1\0Weston Compositor"; @@ -1077,11 +1113,17 @@ static int x11_output_set_size(struct weston_output *base, int width, int height) { struct x11_output *output = to_x11_output(base); - struct x11_backend *b = to_x11_backend(base->compositor); + struct x11_backend *b; struct weston_head *head; - xcb_screen_t *scrn = b->screen; + xcb_screen_t *scrn; int output_width, output_height; + if (!output) + return -1; + + b = to_x11_backend(base->compositor); + scrn = b->screen; + /* We can only be called once. */ assert(!output->base.current_mode); @@ -1163,6 +1205,9 @@ x11_head_create(struct weston_compositor *compositor, const char *name) return -1; weston_head_init(&head->base, name); + + head->base.backend_id = x11_head_destroy; + weston_head_set_connection_status(&head->base, true); weston_compositor_add_head(compositor, &head->base); @@ -1170,8 +1215,12 @@ x11_head_create(struct weston_compositor *compositor, const char *name) } static void -x11_head_destroy(struct x11_head *head) +x11_head_destroy(struct weston_head *base) { + struct x11_head *head = to_x11_head(base); + + assert(head); + weston_head_release(&head->base); free(head); } @@ -1790,8 +1839,10 @@ x11_destroy(struct weston_compositor *ec) weston_compositor_shutdown(ec); /* destroys outputs, too */ - wl_list_for_each_safe(base, next, &ec->head_list, compositor_link) - x11_head_destroy(to_x11_head(base)); + wl_list_for_each_safe(base, next, &ec->head_list, compositor_link) { + if (to_x11_head(base)) + x11_head_destroy(base); + } XCloseDisplay(backend->dpy); free(backend); From c6e47d177a8408b53b4f335eadfff35d8cdec509 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 29 Mar 2021 13:51:31 +0200 Subject: [PATCH 371/609] libweston: consolidate weston_compositor_create_output(_with_head) Add a struct weston_head parameter to weston_compositor_create_output() and fold weston_compositor_create_output_with_head() into it. See: https://gitlab.freedesktop.org/wayland/weston/-/issues/268 Signed-off-by: Philipp Zabel --- compositor/main.c | 6 ++-- .../toc/libweston/images/create_output.msc | 2 +- include/libweston/libweston.h | 6 +--- libweston/compositor.c | 36 +++++-------------- 4 files changed, 14 insertions(+), 36 deletions(-) diff --git a/compositor/main.c b/compositor/main.c index b9c740f3..747f0b75 100644 --- a/compositor/main.c +++ b/compositor/main.c @@ -1696,8 +1696,8 @@ simple_head_enable(struct wet_compositor *wet, struct weston_head *head) struct weston_output *output; int ret = 0; - output = weston_compositor_create_output_with_head(wet->compositor, - head); + output = weston_compositor_create_output(wet->compositor, head, + head->name); if (!output) { weston_log("Could not create an output for head \"%s\".\n", weston_head_get_name(head)); @@ -2162,7 +2162,7 @@ wet_layoutput_create_output(struct wet_layoutput *lo, const char *name) output->output = weston_compositor_create_output(lo->compositor->compositor, - name); + NULL, name); if (!output->output) { free(output); return NULL; diff --git a/doc/sphinx/toc/libweston/images/create_output.msc b/doc/sphinx/toc/libweston/images/create_output.msc index f20cdb4e..b66b7548 100644 --- a/doc/sphinx/toc/libweston/images/create_output.msc +++ b/doc/sphinx/toc/libweston/images/create_output.msc @@ -11,7 +11,7 @@ msc { --- [label = "Compositor creates an output for a head"]; c box c [label = "Have an existing head to process."]; - c => w [label = "weston_compositor_create_output_with_head()"]; + c => w [label = "weston_compositor_create_output()"]; w => b [label = "weston_backend::create_output()"]; w << b [label = "an empty output, no hw resources"]; w => b [label = "weston_output::attach_head()"]; diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index c5f6f8cd..eb929a1f 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -2228,11 +2228,7 @@ weston_compositor_find_output_by_name(struct weston_compositor *compositor, struct weston_output * weston_compositor_create_output(struct weston_compositor *compositor, - const char *name); - -struct weston_output * -weston_compositor_create_output_with_head(struct weston_compositor *compositor, - struct weston_head *head); + struct weston_head *head, const char *name); void weston_output_destroy(struct weston_output *output); diff --git a/libweston/compositor.c b/libweston/compositor.c index 128ee232..0dedb81f 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -7306,13 +7306,15 @@ weston_compositor_find_output_by_name(struct weston_compositor *compositor, return NULL; } -/** Create a named output +/** Create a named output for an unused head * * \param compositor The compositor. + * \param head The head to attach to the output. * \param name The name for the output. * \return A new \c weston_output, or NULL on failure. * - * This creates a new weston_output that starts with no heads attached. + * This creates a new weston_output that starts with the given head attached. + * The head must not be already attached to another output. * * An output must be configured and it must have at least one head before * it can be enabled. @@ -7321,8 +7323,11 @@ weston_compositor_find_output_by_name(struct weston_compositor *compositor, */ WL_EXPORT struct weston_output * weston_compositor_create_output(struct weston_compositor *compositor, + struct weston_head *head, const char *name) { + struct weston_output *output; + assert(compositor->backend->create_output); if (weston_compositor_find_output_by_name(compositor, name)) { @@ -7331,34 +7336,11 @@ weston_compositor_create_output(struct weston_compositor *compositor, return NULL; } - return compositor->backend->create_output(compositor, name); -} - -/** Create an output for an unused head - * - * \param compositor The compositor. - * \param head The head to attach to the output. - * \return A new \c weston_output, or NULL on failure. - * - * This creates a new weston_output that starts with the given head attached. - * The output inherits the name of the head. The head must not be already - * attached to another output. - * - * An output must be configured before it can be enabled. - * - * \ingroup compositor - */ -WL_EXPORT struct weston_output * -weston_compositor_create_output_with_head(struct weston_compositor *compositor, - struct weston_head *head) -{ - struct weston_output *output; - - output = weston_compositor_create_output(compositor, head->name); + output = compositor->backend->create_output(compositor, name); if (!output) return NULL; - if (weston_output_attach_head(output, head) < 0) { + if (head && weston_output_attach_head(output, head) < 0) { weston_output_destroy(output); return NULL; } From 4938f8f6e518bd27544f12c6b895c63ea112a887 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Wed, 24 Mar 2021 09:08:20 +0100 Subject: [PATCH 372/609] compositor: stop creating outputs without head To support heterogeneous outputs, the output must be created by the same backend as the head(s) it is created for. Solve this by always creating an output with a first head to attach that determines the backend to use. Skip already attached first heads in drm_try_attach(). See: https://gitlab.freedesktop.org/wayland/weston/-/issues/268 Signed-off-by: Philipp Zabel --- compositor/main.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/compositor/main.c b/compositor/main.c index 747f0b75..cbbcee1f 100644 --- a/compositor/main.c +++ b/compositor/main.c @@ -2152,7 +2152,9 @@ wet_output_handle_destroy(struct wl_listener *listener, void *data) } static struct wet_output * -wet_layoutput_create_output(struct wet_layoutput *lo, const char *name) +wet_layoutput_create_output_with_head(struct wet_layoutput *lo, + const char *name, + struct weston_head *head) { struct wet_output *output; @@ -2162,7 +2164,7 @@ wet_layoutput_create_output(struct wet_layoutput *lo, const char *name) output->output = weston_compositor_create_output(lo->compositor->compositor, - NULL, name); + head, name); if (!output->output) { free(output); return NULL; @@ -2315,8 +2317,8 @@ drm_try_attach(struct weston_output *output, { unsigned i; - /* try to attach all heads, this probably succeeds */ - for (i = 0; i < add->n; i++) { + /* try to attach remaining heads, this probably succeeds */ + for (i = 1; i < add->n; i++) { if (!add->heads[i]) continue; @@ -2432,7 +2434,8 @@ drm_process_layoutput(struct wet_compositor *wet, struct wet_layoutput *lo) if (ret < 0) return -1; } - output = wet_layoutput_create_output(lo, name); + output = wet_layoutput_create_output_with_head(lo, name, + lo->add.heads[0]); free(name); name = NULL; From 6393e43357f07228f2fe3d7553d4ece12e50c449 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Wed, 8 Jun 2022 15:13:53 +0300 Subject: [PATCH 373/609] tests: rename color-shaper-matrix-test.c to color-icc-output-test.c The new name better matches the contents of the test. Currently the test creates output ICC profiles with matrix-shaper and cLUT forms, and tests that basic color conversion from input to output color space is correct. The common theme in this test program is to create ICC profiles to be used as output profiles. In the future this can include more kinds of testing, e.g. linear blending. OTOH, this test program will always be limited to SDR because HDR testing probably will not use ICC files. Signed-off-by: Pekka Paalanen --- tests/{color-shaper-matrix-test.c => color-icc-output-test.c} | 0 tests/meson.build | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename tests/{color-shaper-matrix-test.c => color-icc-output-test.c} (100%) diff --git a/tests/color-shaper-matrix-test.c b/tests/color-icc-output-test.c similarity index 100% rename from tests/color-shaper-matrix-test.c rename to tests/color-icc-output-test.c diff --git a/tests/meson.build b/tests/meson.build index cc7f8c7b..920eb9d7 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -258,11 +258,11 @@ if get_option('color-management-lcms') error('color-management-lcms tests require lcms2 which was not found. Or, you can use \'-Dcolor-management-lcms=false\'.') endif tests += [ - { 'name': 'color-metadata-parsing' }, { - 'name': 'color-shaper-matrix', + 'name': 'color-icc-output', 'dep_objs': [ dep_libm, dep_lcms_util ] }, + { 'name': 'color-metadata-parsing' }, { 'name': 'lcms-util', 'dep_objs': [ dep_lcms_util ] From cb38c9c84d2f51ac4475d81f4b97cdf2cb497c31 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Wed, 8 Jun 2022 15:26:10 +0300 Subject: [PATCH 374/609] tests: rename shaper_matrix_and_cLUT to opaque_pixel_conversion This name describes better what this test does. In the future another TEST() for alpha blending will be added. Both of them will be using matrix-shaper and cLUT output profiles. Signed-off-by: Pekka Paalanen --- tests/color-icc-output-test.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/color-icc-output-test.c b/tests/color-icc-output-test.c index 1e10f98c..6c354888 100644 --- a/tests/color-icc-output-test.c +++ b/tests/color-icc-output-test.c @@ -660,9 +660,14 @@ check_process_pattern_ex(struct buffer *src, struct buffer *shot, } /* - * Test that matrix-shaper profile does CM correctly, it is used color ramp pattern + * Test that opaque client pixels produce the expected output when converted + * from the implicit sRGB input to ICC profile described output. + * + * The groundtruth conversion comes from the struct lcms_pipeline definitions. + * The first error source is converting those to ICC files. The second error + * source is Weston. */ -TEST(shaper_matrix_and_cLUT) +TEST(opaque_pixel_conversion) { int seq_no = get_test_fixture_index(); const struct setup_args *arg = &my_setup_args[seq_no]; From 731a2fd45beb63349672bc29433ee9f687dfd2e1 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Wed, 8 Jun 2022 15:30:42 +0300 Subject: [PATCH 375/609] tests/color-icc-output: move gen_ramp_rgb() in the file Move gen_ramp_rgb() down in the file where the TEST() specific code begins. This way we first have a big block of fixture setup code which creates an ICC profile, and the next big block is the actual test client code. gen_ramp_rgb() belongs with the latter. This makes the file structure slightly more logical. This is a pure code move, no changes at all. Signed-off-by: Pekka Paalanen --- tests/color-icc-output-test.c | 110 +++++++++++++++++----------------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/tests/color-icc-output-test.c b/tests/color-icc-output-test.c index 6c354888..0efdcfe2 100644 --- a/tests/color-icc-output-test.c +++ b/tests/color-icc-output-test.c @@ -186,61 +186,6 @@ get_image_prop(struct buffer *buf, struct image_header *header) header->data = pixman_image_get_data(buf->image); } -static void -gen_ramp_rgb(const struct image_header *header, int bitwidth, int width_bar) -{ - static const int hue[][COLOR_CHAN_NUM] = { - { 1, 1, 1 }, /* White */ - { 1, 1, 0 }, /* Yellow */ - { 0, 1, 1 }, /* Cyan */ - { 0, 1, 0 }, /* Green */ - { 1, 0, 1 }, /* Magenta */ - { 1, 0, 0 }, /* Red */ - { 0, 0, 1 }, /* Blue */ - }; - const int num_hues = ARRAY_LENGTH(hue); - - float val_max; - int x, y; - int hue_index; - int chan; - float value; - unsigned char r, g, b; - uint32_t *pixel; - - float n_steps = width_bar - 1; - - val_max = (1 << bitwidth) - 1; - - for (y = 0; y < header->height; y++) { - hue_index = (y * num_hues) / (header->height - 1); - hue_index = MIN(hue_index, num_hues - 1); - - for (x = 0; x < header->width; x++) { - struct color_float rgb = { .rgb = { 0, 0, 0 } }; - - value = (float)x / (float)(header->width - 1); - - if (width_bar > 1) - value = floor(value * n_steps) / n_steps; - - for (chan = 0; chan < COLOR_CHAN_NUM; chan++) { - if (hue[hue_index][chan]) - rgb.rgb[chan] = value; - } - - sRGB_delinearize(&rgb); - - r = round(rgb.r * val_max); - g = round(rgb.g * val_max); - b = round(rgb.b * val_max); - - pixel = header->data + (y * header->stride / 4) + x; - *pixel = (255U << 24) | (r << 16) | (g << 8) | b; - } - } -} - static void test_roundtrip(uint8_t r, uint8_t g, uint8_t b, cmsPipeline *pip, struct rgb_diff_stat *stat) @@ -540,6 +485,61 @@ fixture_setup(struct weston_test_harness *harness, const struct setup_args *arg) } DECLARE_FIXTURE_SETUP_WITH_ARG(fixture_setup, my_setup_args, meta); +static void +gen_ramp_rgb(const struct image_header *header, int bitwidth, int width_bar) +{ + static const int hue[][COLOR_CHAN_NUM] = { + { 1, 1, 1 }, /* White */ + { 1, 1, 0 }, /* Yellow */ + { 0, 1, 1 }, /* Cyan */ + { 0, 1, 0 }, /* Green */ + { 1, 0, 1 }, /* Magenta */ + { 1, 0, 0 }, /* Red */ + { 0, 0, 1 }, /* Blue */ + }; + const int num_hues = ARRAY_LENGTH(hue); + + float val_max; + int x, y; + int hue_index; + int chan; + float value; + unsigned char r, g, b; + uint32_t *pixel; + + float n_steps = width_bar - 1; + + val_max = (1 << bitwidth) - 1; + + for (y = 0; y < header->height; y++) { + hue_index = (y * num_hues) / (header->height - 1); + hue_index = MIN(hue_index, num_hues - 1); + + for (x = 0; x < header->width; x++) { + struct color_float rgb = { .rgb = { 0, 0, 0 } }; + + value = (float)x / (float)(header->width - 1); + + if (width_bar > 1) + value = floor(value * n_steps) / n_steps; + + for (chan = 0; chan < COLOR_CHAN_NUM; chan++) { + if (hue[hue_index][chan]) + rgb.rgb[chan] = value; + } + + sRGB_delinearize(&rgb); + + r = round(rgb.r * val_max); + g = round(rgb.g * val_max); + b = round(rgb.b * val_max); + + pixel = header->data + (y * header->stride / 4) + x; + *pixel = (255U << 24) | (r << 16) | (g << 8) | b; + } + } +} + static bool compare_float(float ref, float dst, int x, const char *chan, float *max_diff, float max_allow_diff) From ae9643f729fddfc63700d3c79682dc0cd30c6cc3 Mon Sep 17 00:00:00 2001 From: Hideyuki Nagase Date: Thu, 10 Mar 2022 12:36:49 -0600 Subject: [PATCH 376/609] build: enable configuration of RDP backend as a default Add RDP to the list of backends we can set as default for use when weston is launched without display/socket environment vars set. Co-authored-by: Steve Pronovost Co-authored-by: Brenton DeGeer Signed-off-by: Hideyuki Nagase Signed-off-by: Steve Pronovost Signed-off-by: Brenton DeGeer --- meson_options.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson_options.txt b/meson_options.txt index 38c746e9..b2c4a50f 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -47,7 +47,7 @@ option( option( 'backend-default', type: 'combo', - choices: [ 'auto', 'drm', 'wayland', 'x11', 'headless' ], + choices: [ 'auto', 'drm', 'wayland', 'x11', 'headless', 'rdp' ], value: 'drm', description: 'Default backend when no parent display server detected' ) From f7541d9e42323ab3227bcf08bbae62421dd1a1bb Mon Sep 17 00:00:00 2001 From: Robert Mader Date: Tue, 14 Jun 2022 12:16:44 +0200 Subject: [PATCH 377/609] clients/simple-egl: Fix angle reset on benchmark interval Commit 62ab6891db976 intended to change the angle calculation so that the a time delta since the first frame would be used instead of the absolute time. That was done in order to ensure the angle would always start with the same value, allowing users to differentiate left and right, which again is needed when testing flipped transforms. However, the `benchmark_time` variable is unsuitable for that purpose as it gets reset on each benchmark interval, abruptly changing the angle. Thus introduce a dedicated variable. Fixes 62ab6891db976 Signed-off-by: Robert Mader --- clients/simple-egl.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/clients/simple-egl.c b/clients/simple-egl.c index 05a0ffcb..c530d8d5 100644 --- a/clients/simple-egl.c +++ b/clients/simple-egl.c @@ -99,7 +99,9 @@ struct window { GLuint col; } gl; - uint32_t benchmark_time, frames; + uint32_t frames; + uint32_t initial_frame_time; + uint32_t benchmark_time; struct wl_egl_window *native; struct wl_surface *surface; struct xdg_surface *xdg_surface; @@ -624,8 +626,10 @@ redraw(struct window *window) gettimeofday(&tv, NULL); uint32_t time = tv.tv_sec * 1000 + tv.tv_usec / 1000; - if (window->frames == 0) + if (window->frames == 0) { + window->initial_frame_time = time; window->benchmark_time = time; + } if (time - window->benchmark_time > (benchmark_interval * 1000)) { printf("%d frames in %d seconds: %f fps\n", window->frames, @@ -636,7 +640,7 @@ redraw(struct window *window) } weston_matrix_init(&rotation); - angle = ((time - window->benchmark_time) / speed_div) % 360 * M_PI / 180.0; + angle = ((time - window->initial_frame_time) / speed_div) % 360 * M_PI / 180.0; rotation.d[0] = cos(angle); rotation.d[2] = sin(angle); rotation.d[8] = -sin(angle); From b878357dfdca5003bb4bf451dcf4a2b9c0bc1084 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Wed, 8 Jun 2022 14:14:12 +0300 Subject: [PATCH 378/609] tests: remove skip() as unused skip() is a left-over from an old test harness design, the comment even refers to automake. Calling skip() cannot do anything good anymore, because it wouldn't print the skips in the TAP report, so it would probably be considered a failure. Delete this unused and nowadays incorrect function, so it doesn't confuse people. Signed-off-by: Pekka Paalanen --- tests/weston-test-client-helper.c | 16 ---------------- tests/weston-test-client-helper.h | 3 --- 2 files changed, 19 deletions(-) diff --git a/tests/weston-test-client-helper.c b/tests/weston-test-client-helper.c index 01c4b804..1b96f8e6 100644 --- a/tests/weston-test-client-helper.c +++ b/tests/weston-test-client-helper.c @@ -870,22 +870,6 @@ static const struct wl_registry_listener registry_listener = { handle_global_remove, }; -void -skip(const char *fmt, ...) -{ - va_list argp; - - va_start(argp, fmt); - vfprintf(stderr, fmt, argp); - va_end(argp); - - /* automake tests uses exit code 77. weston-test-runner will see - * this and use it, and then weston-test's sigchld handler (in the - * weston process) will use that as an exit status, which is what - * ninja will see in the end. */ - exit(77); -} - void expect_protocol_error(struct client *client, const struct wl_interface *intf, diff --git a/tests/weston-test-client-helper.h b/tests/weston-test-client-helper.h index 8e1505d4..b30fdb7f 100644 --- a/tests/weston-test-client-helper.h +++ b/tests/weston-test-client-helper.h @@ -232,9 +232,6 @@ frame_callback_wait_nofail(struct client *client, int *done); #define frame_callback_wait(c, d) assert(frame_callback_wait_nofail((c), (d))) -void -skip(const char *fmt, ...); - void expect_protocol_error(struct client *client, const struct wl_interface *intf, uint32_t code); From 4d2ea5dd0b141dfdd11f1849f6e76e27808316b5 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Wed, 8 Jun 2022 14:32:16 +0300 Subject: [PATCH 379/609] tests: move set_opaque_rect() to client helpers I will be needing it in a new test, so let's share it. For convenience, this also adds client back-pointer in struct surface so that I don't need to pass client explicitly. Signed-off-by: Pekka Paalanen --- tests/alpha-blending-test.c | 17 ++--------------- tests/weston-test-client-helper.c | 12 ++++++++++++ tests/weston-test-client-helper.h | 5 +++++ 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/tests/alpha-blending-test.c b/tests/alpha-blending-test.c index 1a315828..58111b8e 100644 --- a/tests/alpha-blending-test.c +++ b/tests/alpha-blending-test.c @@ -80,19 +80,6 @@ fixture_setup(struct weston_test_harness *harness, const struct setup_args *arg) } DECLARE_FIXTURE_SETUP_WITH_ARG(fixture_setup, my_setup_args, meta); -static void -set_opaque_rect(struct client *client, - struct surface *surface, - const struct rectangle *rect) -{ - struct wl_region *region; - - region = wl_compositor_create_region(client->wl_compositor); - wl_region_add(region, rect->x, rect->y, rect->width, rect->height); - wl_surface_set_opaque_region(surface->wl_surface, region); - wl_region_destroy(region); -} - static uint32_t premult_color(uint32_t a, uint32_t r, uint32_t g, uint32_t b) { @@ -400,8 +387,8 @@ TEST(alpha_blend) client->surface->width = width; client->surface->height = height; client->surface->buffer = bg; /* pass ownership */ - set_opaque_rect(client, client->surface, - &(struct rectangle){ 0, 0, width, height }); + surface_set_opaque_rect(client->surface, + &(struct rectangle){ 0, 0, width, height }); /* foreground blended content */ fg = create_shm_buffer_a8r8g8b8(client, width, height); diff --git a/tests/weston-test-client-helper.c b/tests/weston-test-client-helper.c index 1b96f8e6..30974d61 100644 --- a/tests/weston-test-client-helper.c +++ b/tests/weston-test-client-helper.c @@ -976,6 +976,7 @@ create_test_surface(struct client *client) surface = xzalloc(sizeof *surface); + surface->client = client; surface->wl_surface = wl_compositor_create_surface(client->wl_compositor); assert(surface->wl_surface); @@ -998,6 +999,17 @@ surface_destroy(struct surface *surface) free(surface); } +void +surface_set_opaque_rect(struct surface *surface, const struct rectangle *rect) +{ + struct wl_region *region; + + region = wl_compositor_create_region(surface->client->wl_compositor); + wl_region_add(region, rect->x, rect->y, rect->width, rect->height); + wl_surface_set_opaque_region(surface->wl_surface, region); + wl_region_destroy(region); +} + struct client * create_client_and_test_surface(int x, int y, int width, int height) { diff --git a/tests/weston-test-client-helper.h b/tests/weston-test-client-helper.h index b30fdb7f..dd20d514 100644 --- a/tests/weston-test-client-helper.h +++ b/tests/weston-test-client-helper.h @@ -172,6 +172,8 @@ struct buffer { }; struct surface { + struct client *client; /* not owned */ + struct wl_surface *wl_surface; struct output *output; /* not owned */ int x; @@ -205,6 +207,9 @@ create_test_surface(struct client *client); void surface_destroy(struct surface *surface); +void +surface_set_opaque_rect(struct surface *surface, const struct rectangle *rect); + struct client * create_client_and_test_surface(int x, int y, int width, int height); From d24adbbe258a53d91ed3ec369e850c8c732b8f07 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Wed, 21 Jul 2021 17:45:14 +0300 Subject: [PATCH 380/609] backend-drm: set connector max bpc "max bpc" property is meant for working around faulty sink hardware. Normally it should be set to the maximum possible value so that the kernel driver has full freedom to choose the link bpc without being artificially forced to lower color precision. The default value is 16 because that is a nice round number and more than any link technology I've heard is using today which would be 12. Also offer an API set the value, so that weston.ini could be used in the next patch for sink workaround purposes. Closes: https://gitlab.freedesktop.org/wayland/weston/-/issues/612 Signed-off-by: Pekka Paalanen --- include/libweston/backend-drm.h | 14 ++++++++++++++ libweston/backend-drm/drm-internal.h | 3 +++ libweston/backend-drm/drm.c | 13 +++++++++++++ libweston/backend-drm/kms.c | 27 +++++++++++++++++++++++++++ 4 files changed, 57 insertions(+) diff --git a/include/libweston/backend-drm.h b/include/libweston/backend-drm.h index 02038e6f..548940ab 100644 --- a/include/libweston/backend-drm.h +++ b/include/libweston/backend-drm.h @@ -78,6 +78,20 @@ struct weston_drm_output_api { */ void (*set_seat)(struct weston_output *output, const char *seat); + + /** Set the "max bpc" KMS connector property + * + * The property is used for working around faulty sink hardware like + * monitors or media converters that mishandle the kernel driver + * chosen bits-per-channel on the physical link. When having trouble, + * try a lower value like 8. + * + * The value actually used in KMS is silently clamped to the range the + * KMS driver claims to support. The default value is 16. + * + * This can be set only while the output is disabled. + */ + void (*set_max_bpc)(struct weston_output *output, unsigned max_bpc); }; static inline const struct weston_drm_output_api * diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index 60ab2352..3fc5f9b9 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -187,6 +187,7 @@ enum wdrm_connector_property { WDRM_CONNECTOR_HDCP_CONTENT_TYPE, WDRM_CONNECTOR_PANEL_ORIENTATION, WDRM_CONNECTOR_HDR_OUTPUT_METADATA, + WDRM_CONNECTOR_MAX_BPC, WDRM_CONNECTOR__COUNT }; @@ -559,6 +560,8 @@ struct drm_output { uint32_t hdr_output_metadata_blob_id; uint64_t ackd_color_outcome_serial; + unsigned max_bpc; + /* Plane being displayed directly on the CRTC */ struct drm_plane *scanout_plane; diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index f06d8c72..ffb584ad 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -1389,6 +1389,17 @@ drm_output_set_seat(struct weston_output *base, seat ? seat : ""); } +static void +drm_output_set_max_bpc(struct weston_output *base, unsigned max_bpc) +{ + struct drm_output *output = to_drm_output(base); + + assert(output); + assert(!output->base.enabled); + + output->max_bpc = max_bpc; +} + static int drm_output_init_gamma_size(struct drm_output *output) { @@ -2278,6 +2289,7 @@ drm_output_create(struct weston_compositor *compositor, const char *name) output->device = device; output->crtc = NULL; + output->max_bpc = 16; output->gbm_format = DRM_FORMAT_INVALID; #ifdef BUILD_DRM_GBM output->gbm_bo_flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING; @@ -3055,6 +3067,7 @@ static const struct weston_drm_output_api api = { drm_output_set_mode, drm_output_set_gbm_format, drm_output_set_seat, + drm_output_set_max_bpc, }; static struct drm_backend * diff --git a/libweston/backend-drm/kms.c b/libweston/backend-drm/kms.c index d1be5dbe..f6629a5d 100644 --- a/libweston/backend-drm/kms.c +++ b/libweston/backend-drm/kms.c @@ -146,6 +146,7 @@ const struct drm_property_info connector_props[] = { [WDRM_CONNECTOR_HDR_OUTPUT_METADATA] = { .name = "HDR_OUTPUT_METADATA", }, + [WDRM_CONNECTOR_MAX_BPC] = { .name = "max bpc", }, }; const struct drm_property_info crtc_props[] = { @@ -905,6 +906,30 @@ drm_connector_set_hdcp_property(struct drm_connector *connector, assert(ret == 0); } +static int +drm_connector_set_max_bpc(struct drm_connector *connector, + struct drm_output *output, + drmModeAtomicReq *req) +{ + const struct drm_property_info *info; + uint64_t max_bpc; + uint64_t a, b; + + if (!drm_connector_has_prop(connector, WDRM_CONNECTOR_MAX_BPC)) + return 0; + + info = &connector->props[WDRM_CONNECTOR_MAX_BPC]; + assert(info->flags & DRM_MODE_PROP_RANGE); + assert(info->num_range_values == 2); + a = info->range_values[0]; + b = info->range_values[1]; + assert(a <= b); + + max_bpc = MAX(a, MIN(output->max_bpc, b)); + return connector_add_prop(req, connector, + WDRM_CONNECTOR_MAX_BPC, max_bpc); +} + static int drm_output_apply_state_atomic(struct drm_output_state *state, drmModeAtomicReq *req, @@ -965,6 +990,8 @@ drm_output_apply_state_atomic(struct drm_output_state *state, WDRM_CONNECTOR_HDR_OUTPUT_METADATA, output->hdr_output_metadata_blob_id); } + + ret |= drm_connector_set_max_bpc(&head->connector, output, req); } if (ret != 0) { From 8de94ec9c99557b5a0d99fa8c8eb358948cf855c Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 7 Jun 2022 15:02:55 +0300 Subject: [PATCH 381/609] compositor: add weston.ini option max-bpc For working around hardware limitations as explained in the man page. Now added for completeness' sake without knowing if anyone will ever need this. Signed-off-by: Pekka Paalanen --- compositor/main.c | 4 ++++ man/weston-drm.man | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/compositor/main.c b/compositor/main.c index cbbcee1f..044c9df7 100644 --- a/compositor/main.c +++ b/compositor/main.c @@ -2000,6 +2000,7 @@ drm_backend_output_configure(struct weston_output *output, enum weston_drm_backend_output_mode mode = WESTON_DRM_BACKEND_OUTPUT_PREFERRED; uint32_t transform = WL_OUTPUT_TRANSFORM_NORMAL; + uint32_t max_bpc; char *s; char *modeline = NULL; char *gbm_format = NULL; @@ -2011,6 +2012,9 @@ drm_backend_output_configure(struct weston_output *output, return -1; } + weston_config_section_get_uint(section, "max-bpc", &max_bpc, 16); + api->set_max_bpc(output, max_bpc); + weston_config_section_get_string(section, "mode", &s, "preferred"); if (strcmp(s, "off") == 0) { diff --git a/man/weston-drm.man b/man/weston-drm.man index 48fd0c8e..eaaf311c 100644 --- a/man/weston-drm.man +++ b/man/weston-drm.man @@ -164,6 +164,15 @@ Defaults to false. Note that When a connector is disconnected, there is no EDID information to provide a list of video modes. Therefore a forced output should also have a detailed mode line specified. +.TP +\fBmax-bpc\fR=\fIN\fR +.RB "Set \(dq" "max bpc" "\(dq KMS property to value" +.IR N , +silenty clamped to the hardware driver supported range. This artificially +limits the driver chosen link bits-per-channel which may be useful for working +around sink hardware (e.g. monitor) limitations. The default is 16 which is +practically unlimited. If you need to work around hardware issues, try a lower +value like 8. .SS Section remote-output .TP From 9b82bfae9eaad955f8ccc3d8baedfa94e68ae73e Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 13 Jun 2022 13:45:58 +0300 Subject: [PATCH 382/609] tests/color-icc-output: extract image-iter.h Move the struct image_header and get_image_prop() into a header where we can share these useful helpers between more test code. While doing that, drop the unused field 'depth', rename into image_header_from(), and introduce a helper to get u32 pointer to the beginning of a row. These helpers should make pixel iterating code easier to read and safer (less room for mistakes in address computations, and asserts). Use the shared 'struct image_header' instead of the local one. The intention is to make the code easier to read by using the same helpers everywhere. Width, height and stride use type 'int' because Pixman API uses that too. Signed-off-by: Pekka Paalanen --- tests/color-icc-output-test.c | 79 ++++++++++------------------------- tests/image-iter.h | 75 +++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 57 deletions(-) create mode 100644 tests/image-iter.h diff --git a/tests/color-icc-output-test.c b/tests/color-icc-output-test.c index 0efdcfe2..429a7ce0 100644 --- a/tests/color-icc-output-test.c +++ b/tests/color-icc-output-test.c @@ -34,6 +34,7 @@ #include "weston-test-client-helper.h" #include "weston-test-fixture-compositor.h" #include "color_util.h" +#include "image-iter.h" #include "lcms_util.h" struct lcms_pipeline { @@ -166,26 +167,6 @@ static const struct setup_args my_setup_args[] = { { { "sRGB->adobeRGB" }, 1, &pipeline_adobeRGB, 1, 17, PTYPE_CLUT, 0.0065 }, }; -struct image_header { - int width; - int height; - int stride; - int depth; - pixman_format_code_t pix_format; - uint32_t *data; -}; - -static void -get_image_prop(struct buffer *buf, struct image_header *header) -{ - header->width = pixman_image_get_width(buf->image); - header->height = pixman_image_get_height(buf->image); - header->stride = pixman_image_get_stride(buf->image); - header->depth = pixman_image_get_depth(buf->image); - header->pix_format = pixman_image_get_format (buf->image); - header->data = pixman_image_get_data(buf->image); -} - static void test_roundtrip(uint8_t r, uint8_t g, uint8_t b, cmsPipeline *pip, struct rgb_diff_stat *stat) @@ -486,7 +467,7 @@ fixture_setup(struct weston_test_harness *harness, const struct setup_args *arg) DECLARE_FIXTURE_SETUP_WITH_ARG(fixture_setup, my_setup_args, meta); static void -gen_ramp_rgb(const struct image_header *header, int bitwidth, int width_bar) +gen_ramp_rgb(pixman_image_t *image, int bitwidth, int width_bar) { static const int hue[][COLOR_CHAN_NUM] = { { 1, 1, 1 }, /* White */ @@ -499,6 +480,7 @@ gen_ramp_rgb(const struct image_header *header, int bitwidth, int width_bar) }; const int num_hues = ARRAY_LENGTH(hue); + struct image_header ih = image_header_from(image); float val_max; int x, y; int hue_index; @@ -511,14 +493,15 @@ gen_ramp_rgb(const struct image_header *header, int bitwidth, int width_bar) val_max = (1 << bitwidth) - 1; - for (y = 0; y < header->height; y++) { - hue_index = (y * num_hues) / (header->height - 1); + for (y = 0; y < ih.height; y++) { + hue_index = (y * num_hues) / (ih.height - 1); hue_index = MIN(hue_index, num_hues - 1); - for (x = 0; x < header->width; x++) { + pixel = image_header_get_row_u32(&ih, y); + for (x = 0; x < ih.width; x++, pixel++) { struct color_float rgb = { .rgb = { 0, 0, 0 } }; - value = (float)x / (float)(header->width - 1); + value = (float)x / (float)(ih.width - 1); if (width_bar > 1) value = floor(value * n_steps) / n_steps; @@ -534,7 +517,6 @@ gen_ramp_rgb(const struct image_header *header, int bitwidth, int width_bar) g = round(rgb.g * val_max); b = round(rgb.b * val_max); - pixel = header->data + (y * header->stride / 4) + x; *pixel = (255U << 24) | (r << 16) | (g << 8) | b; } } @@ -585,8 +567,8 @@ compare_float(float ref, float dst, int x, const char *chan, } static bool -process_pipeline_comparison(const struct image_header *src, - const struct image_header *shot, +process_pipeline_comparison(const struct buffer *src_buf, + const struct buffer *shot_buf, const struct setup_args * arg) { const char *const chan_name[COLOR_CHAN_NUM] = { "r", "g", "b" }; @@ -595,18 +577,23 @@ process_pipeline_comparison(const struct image_header *src, float max_allow_diff = arg->tolerance / max_pixel_value; float max_err = 0.0f; bool ok = true; - uint32_t *row_ptr, *row_ptr_shot; + struct image_header ih_src = image_header_from(src_buf->image); + struct image_header ih_shot = image_header_from(shot_buf->image); int y, x; int chan; struct color_float pix_src; struct color_float pix_src_pipeline; struct color_float pix_shot; - for (y = 0; y < src->height; y++) { - row_ptr = (uint32_t*)((uint8_t*)src->data + (src->stride * y)); - row_ptr_shot = (uint32_t*)((uint8_t*)shot->data + (shot->stride * y)); + /* no point to compare different images */ + assert(ih_src.width == ih_shot.width); + assert(ih_src.height == ih_shot.height); + + for (y = 0; y < ih_src.height; y++) { + uint32_t *row_ptr = image_header_get_row_u32(&ih_src, y); + uint32_t *row_ptr_shot = image_header_get_row_u32(&ih_shot, y); - for (x = 0; x < src->width; x++) { + for (x = 0; x < ih_src.width; x++) { pix_src = a8r8g8b8_to_float(row_ptr[x]); pix_shot = a8r8g8b8_to_float(row_ptr_shot[x]); /* do pipeline processing */ @@ -639,26 +626,6 @@ process_pipeline_comparison(const struct image_header *src, return ok; } -static bool -check_process_pattern_ex(struct buffer *src, struct buffer *shot, - const struct setup_args * arg) -{ - struct image_header header_src; - struct image_header header_shot; - bool ok; - - get_image_prop(src, &header_src); - get_image_prop(shot, &header_shot); - - /* no point to compare different images */ - assert(header_src.width == header_shot.width); - assert(header_src.height == header_shot.height); - - ok = process_pipeline_comparison(&header_src, &header_shot, arg); - - return ok; -} - /* * Test that opaque client pixels produce the expected output when converted * from the implicit sRGB input to ICC profile described output. @@ -680,7 +647,6 @@ TEST(opaque_pixel_conversion) struct buffer *buf; struct buffer *shot; struct wl_surface *surface; - struct image_header image; bool match; client = create_client_and_test_surface(0, 0, width, height); @@ -688,8 +654,7 @@ TEST(opaque_pixel_conversion) surface = client->surface->wl_surface; buf = create_shm_buffer_a8r8g8b8(client, width, height); - get_image_prop(buf, &image); - gen_ramp_rgb(&image, bitwidth, width_bar); + gen_ramp_rgb(buf->image, bitwidth, width_bar); wl_surface_attach(surface, buf->proxy, 0, 0); wl_surface_damage(surface, 0, 0, width, height); @@ -700,7 +665,7 @@ TEST(opaque_pixel_conversion) match = verify_image(shot, "shaper_matrix", arg->ref_image_index, NULL, seq_no); - assert(check_process_pattern_ex(buf, shot, arg)); + assert(process_pipeline_comparison(buf, shot, arg)); assert(match); buffer_destroy(shot); buffer_destroy(buf); diff --git a/tests/image-iter.h b/tests/image-iter.h new file mode 100644 index 00000000..ab9b0830 --- /dev/null +++ b/tests/image-iter.h @@ -0,0 +1,75 @@ +/* + * Copyright 2021 Advanced Micro Devices, Inc. + * Copyright 2022 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include +#include +#include + +/** A collection of basic information extracted from a pixman_image_t */ +struct image_header { + int width; + int height; + pixman_format_code_t pixman_format; + + int stride_bytes; + unsigned char *data; +}; + +/** Populate image_header from pixman_image_t */ +static inline struct image_header +image_header_from(pixman_image_t *image) +{ + struct image_header h; + + h.width = pixman_image_get_width(image); + h.height = pixman_image_get_height(image); + h.pixman_format = pixman_image_get_format(image); + h.stride_bytes = pixman_image_get_stride(image); + h.data = (void *)pixman_image_get_data(image); + + return h; +} + +/** Get pointer to the beginning of the row + * + * \param header Header describing the Pixman image. + * \param y Index of the desired row, starting from zero. + * \return Pointer to the first pixel of the row. + * + * Asserts that y is within image height, and that pixel format uses 32 bits + * per pixel. + */ +static inline uint32_t * +image_header_get_row_u32(const struct image_header *header, int y) +{ + assert(y >= 0); + assert(y < header->height); + assert(PIXMAN_FORMAT_BPP(header->pixman_format) == 32); + + return (uint32_t *)(header->data + y * header->stride_bytes); +} From 4eb70a602b5c146a3743f849d09ae4a131acbb03 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 9 Jun 2022 15:47:21 +0300 Subject: [PATCH 383/609] tests/client-helper: use image-iter.h Replace private pixel iterator helpers with the shared ones. No change in behaviour. Signed-off-by: Pekka Paalanen --- tests/weston-test-client-helper.c | 61 ++++++++----------------------- 1 file changed, 15 insertions(+), 46 deletions(-) diff --git a/tests/weston-test-client-helper.c b/tests/weston-test-client-helper.c index 30974d61..a19d5df8 100644 --- a/tests/weston-test-client-helper.c +++ b/tests/weston-test-client-helper.c @@ -41,6 +41,7 @@ #include "shared/xalloc.h" #include #include "weston-test-client-helper.h" +#include "image-iter.h" #define max(a, b) (((a) > (b)) ? (a) : (b)) #define min(a, b) (((a) > (b)) ? (b) : (a)) @@ -1254,29 +1255,6 @@ image_check_get_roi(pixman_image_t *img_a, pixman_image_t *img_b, return box; } -struct image_iterator { - char *data; - int stride; /* bytes */ -}; - -static void -image_iter_init(struct image_iterator *it, pixman_image_t *image) -{ - pixman_format_code_t fmt; - - it->stride = pixman_image_get_stride(image); - it->data = (void *)pixman_image_get_data(image); - - fmt = pixman_image_get_format(image); - assert(PIXMAN_FORMAT_BPP(fmt) == 32); -} - -static uint32_t * -image_iter_get_row(struct image_iterator *it, int y) -{ - return (uint32_t *)(it->data + y * it->stride); -} - struct pixel_diff_stat { struct pixel_diff_stat_channel { int min_diff; @@ -1347,8 +1325,8 @@ check_images_match(pixman_image_t *img_a, pixman_image_t *img_b, { struct range fuzz = range_get(prec); struct pixel_diff_stat diffstat = {}; - struct image_iterator it_a; - struct image_iterator it_b; + struct image_header ih_a = image_header_from(img_a); + struct image_header ih_b = image_header_from(img_b); pixman_box32_t box; int x, y; uint32_t *pix_a; @@ -1356,12 +1334,9 @@ check_images_match(pixman_image_t *img_a, pixman_image_t *img_b, box = image_check_get_roi(img_a, img_b, clip_rect); - image_iter_init(&it_a, img_a); - image_iter_init(&it_b, img_b); - for (y = box.y1; y < box.y2; y++) { - pix_a = image_iter_get_row(&it_a, y) + box.x1; - pix_b = image_iter_get_row(&it_b, y) + box.x1; + pix_a = image_header_get_row_u32(&ih_a, y) + box.x1; + pix_b = image_header_get_row_u32(&ih_b, y) + box.x1; for (x = box.x1; x < box.x2; x++) { if (!fuzzy_match_pixels(*pix_a, *pix_b, @@ -1431,11 +1406,9 @@ visualize_image_difference(pixman_image_t *img_a, pixman_image_t *img_b, struct pixel_diff_stat diffstat = {}; pixman_image_t *diffimg; pixman_image_t *shade; - struct image_iterator it_a; - struct image_iterator it_b; - struct image_iterator it_d; - int width; - int height; + struct image_header ih_a = image_header_from(img_a); + struct image_header ih_b = image_header_from(img_b); + struct image_header ih_d; pixman_box32_t box; int x, y; uint32_t *pix_a; @@ -1443,32 +1416,28 @@ visualize_image_difference(pixman_image_t *img_a, pixman_image_t *img_b, uint32_t *pix_d; pixman_color_t shade_color = { 0, 0, 0, 32768 }; - width = pixman_image_get_width(img_a); - height = pixman_image_get_height(img_a); box = image_check_get_roi(img_a, img_b, clip_rect); diffimg = pixman_image_create_bits_no_clear(PIXMAN_x8r8g8b8, - width, height, NULL, 0); + ih_a.width, ih_a.height, + NULL, 0); + ih_d = image_header_from(diffimg); /* Fill diffimg with a black-shaded copy of img_a, and then fill * the clip_rect area with original img_a. */ shade = pixman_image_create_solid_fill(&shade_color); pixman_image_composite32(PIXMAN_OP_SRC, img_a, shade, diffimg, - 0, 0, 0, 0, 0, 0, width, height); + 0, 0, 0, 0, 0, 0, ih_a.width, ih_a.height); pixman_image_unref(shade); pixman_image_composite32(PIXMAN_OP_SRC, img_a, NULL, diffimg, box.x1, box.y1, 0, 0, box.x1, box.y1, box.x2 - box.x1, box.y2 - box.y1); - image_iter_init(&it_a, img_a); - image_iter_init(&it_b, img_b); - image_iter_init(&it_d, diffimg); - for (y = box.y1; y < box.y2; y++) { - pix_a = image_iter_get_row(&it_a, y) + box.x1; - pix_b = image_iter_get_row(&it_b, y) + box.x1; - pix_d = image_iter_get_row(&it_d, y) + box.x1; + pix_a = image_header_get_row_u32(&ih_a, y) + box.x1; + pix_b = image_header_get_row_u32(&ih_b, y) + box.x1; + pix_d = image_header_get_row_u32(&ih_d, y) + box.x1; for (x = box.x1; x < box.x2; x++) { if (fuzzy_match_pixels(*pix_a, *pix_b, From 791a6be21652f63dfda571796318bd22cec3f915 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 9 Jun 2022 15:53:10 +0300 Subject: [PATCH 384/609] tests: pass image_header to image_check_get_roi() Make use of the new type to shorten the code. No change in behaviour. Signed-off-by: Pekka Paalanen --- tests/weston-test-client-helper.c | 35 ++++++++++++------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/tests/weston-test-client-helper.c b/tests/weston-test-client-helper.c index a19d5df8..22b3c572 100644 --- a/tests/weston-test-client-helper.c +++ b/tests/weston-test-client-helper.c @@ -1201,8 +1201,8 @@ range_get(const struct range *r) /** * Compute the ROI for image comparisons * - * \param img_a An image. - * \param img_b Another image. + * \param ih_a A header for an image. + * \param ih_b A header for another image. * \param clip_rect Explicit ROI, or NULL for using the whole * image area. * @@ -1216,21 +1216,12 @@ range_get(const struct range *r) * The ROI is given as pixman_box32_t, where x2,y2 are non-inclusive. */ static pixman_box32_t -image_check_get_roi(pixman_image_t *img_a, pixman_image_t *img_b, - const struct rectangle *clip_rect) +image_check_get_roi(const struct image_header *ih_a, + const struct image_header *ih_b, + const struct rectangle *clip_rect) { - int width_a; - int width_b; - int height_a; - int height_b; pixman_box32_t box; - width_a = pixman_image_get_width(img_a); - height_a = pixman_image_get_height(img_a); - - width_b = pixman_image_get_width(img_b); - height_b = pixman_image_get_height(img_b); - if (clip_rect) { box.x1 = clip_rect->x; box.y1 = clip_rect->y; @@ -1239,18 +1230,18 @@ image_check_get_roi(pixman_image_t *img_a, pixman_image_t *img_b, } else { box.x1 = 0; box.y1 = 0; - box.x2 = max(width_a, width_b); - box.y2 = max(height_a, height_b); + box.x2 = max(ih_a->width, ih_b->width); + box.y2 = max(ih_a->height, ih_b->height); } assert(box.x1 >= 0); assert(box.y1 >= 0); assert(box.x2 > box.x1); assert(box.y2 > box.y1); - assert(box.x2 <= width_a); - assert(box.x2 <= width_b); - assert(box.y2 <= height_a); - assert(box.y2 <= height_b); + assert(box.x2 <= ih_a->width); + assert(box.x2 <= ih_b->width); + assert(box.y2 <= ih_a->height); + assert(box.y2 <= ih_b->height); return box; } @@ -1332,7 +1323,7 @@ check_images_match(pixman_image_t *img_a, pixman_image_t *img_b, uint32_t *pix_a; uint32_t *pix_b; - box = image_check_get_roi(img_a, img_b, clip_rect); + box = image_check_get_roi(&ih_a, &ih_b, clip_rect); for (y = box.y1; y < box.y2; y++) { pix_a = image_header_get_row_u32(&ih_a, y) + box.x1; @@ -1416,7 +1407,7 @@ visualize_image_difference(pixman_image_t *img_a, pixman_image_t *img_b, uint32_t *pix_d; pixman_color_t shade_color = { 0, 0, 0, 32768 }; - box = image_check_get_roi(img_a, img_b, clip_rect); + box = image_check_get_roi(&ih_a, &ih_b, clip_rect); diffimg = pixman_image_create_bits_no_clear(PIXMAN_x8r8g8b8, ih_a.width, ih_a.height, From 94589497a1d1395e663b64092c96f8e342d3e5ea Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 13 Jun 2022 11:58:04 +0300 Subject: [PATCH 385/609] tests/client-helper: use image_header_from() more These are the last places in weston-test-client-helper.c where using image_header_from() will reduce the code line count and simplify the code a little. No change in behaviour. Signed-off-by: Pekka Paalanen --- tests/weston-test-client-helper.c | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/tests/weston-test-client-helper.c b/tests/weston-test-client-helper.c index 22b3c572..89713e17 100644 --- a/tests/weston-test-client-helper.c +++ b/tests/weston-test-client-helper.c @@ -1464,16 +1464,12 @@ write_image_as_png(pixman_image_t *image, const char *fname) { cairo_surface_t *cairo_surface; cairo_status_t status; - cairo_format_t fmt; + struct image_header ih = image_header_from(image); + cairo_format_t fmt = format_pixman2cairo(ih.pixman_format); - fmt = format_pixman2cairo(pixman_image_get_format(image)); - - cairo_surface = cairo_image_surface_create_for_data( - (void *)pixman_image_get_data(image), - fmt, - pixman_image_get_width(image), - pixman_image_get_height(image), - pixman_image_get_stride(image)); + cairo_surface = cairo_image_surface_create_for_data(ih.data, fmt, + ih.width, ih.height, + ih.stride_bytes); status = cairo_surface_write_to_png(cairo_surface, fname); if (status != CAIRO_STATUS_SUCCESS) { @@ -1492,21 +1488,17 @@ static pixman_image_t * image_convert_to_a8r8g8b8(pixman_image_t *image) { pixman_image_t *ret; - int width; - int height; + struct image_header ih = image_header_from(image); - if (pixman_image_get_format(image) == PIXMAN_a8r8g8b8) + if (ih.pixman_format == PIXMAN_a8r8g8b8) return pixman_image_ref(image); - width = pixman_image_get_width(image); - height = pixman_image_get_height(image); - - ret = pixman_image_create_bits_no_clear(PIXMAN_a8r8g8b8, width, height, - NULL, 0); + ret = pixman_image_create_bits_no_clear(PIXMAN_a8r8g8b8, + ih.width, ih.height, NULL, 0); assert(ret); pixman_image_composite32(PIXMAN_OP_SRC, image, NULL, ret, - 0, 0, 0, 0, 0, 0, width, height); + 0, 0, 0, 0, 0, 0, ih.width, ih.height); return ret; } From 884c5f80e8f0c0a9cb82cf0ca5063fc8d0f909d9 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 13 Jun 2022 12:29:30 +0300 Subject: [PATCH 386/609] tests/yuv-buffer: use image-iter.h for rgb_image Make use of the shared code instead of open-coding everywhere. This should make the code easier to read, and reduce the chance of typos if changes are needed in the future. No change in behaviour. Signed-off-by: Pekka Paalanen --- tests/yuv-buffer-test.c | 107 +++++++++++++++------------------------- 1 file changed, 41 insertions(+), 66 deletions(-) diff --git a/tests/yuv-buffer-test.c b/tests/yuv-buffer-test.c index eb17c680..d7de5367 100644 --- a/tests/yuv-buffer-test.c +++ b/tests/yuv-buffer-test.c @@ -33,6 +33,7 @@ #include "weston-test-client-helper.h" #include "weston-test-fixture-compositor.h" +#include "image-iter.h" #include "shared/os-compatibility.h" #include "shared/weston-drm-fourcc.h" #include "shared/xalloc.h" @@ -158,13 +159,10 @@ y_u_v_create_buffer(struct client *client, uint32_t drm_format, pixman_image_t *rgb_image) { + struct image_header rgb = image_header_from(rgb_image); struct yuv_buffer *buf; size_t bytes; - int width; - int height; int x, y; - void *rgb_pixels; - int rgb_stride_bytes; uint32_t *rgb_row; uint8_t *y_base; uint8_t *u_base; @@ -178,26 +176,23 @@ y_u_v_create_buffer(struct client *client, assert(drm_format == DRM_FORMAT_YUV420 || drm_format == DRM_FORMAT_YUV444); - width = pixman_image_get_width(rgb_image); - height = pixman_image_get_height(rgb_image); - rgb_pixels = pixman_image_get_data(rgb_image); - rgb_stride_bytes = pixman_image_get_stride(rgb_image); - /* Full size Y plus quarter U and V */ - bytes = width * height + (width / sub) * (height / sub) * 2; - buf = yuv_buffer_create(client, bytes, width, height, width, drm_format); + bytes = rgb.width * rgb.height + + (rgb.width / sub) * (rgb.height / sub) * 2; + buf = yuv_buffer_create(client, bytes, rgb.width, rgb.height, + rgb.width, drm_format); y_base = buf->data; - u_base = y_base + width * height; - v_base = u_base + (width / sub) * (height / sub); + u_base = y_base + rgb.width * rgb.height; + v_base = u_base + (rgb.width / sub) * (rgb.height / sub); - for (y = 0; y < height; y++) { - rgb_row = rgb_pixels + (y / 2 * 2) * rgb_stride_bytes; - y_row = y_base + y * width; - u_row = u_base + (y / sub) * (width / sub); - v_row = v_base + (y / sub) * (width / sub); + for (y = 0; y < rgb.height; y++) { + rgb_row = image_header_get_row_u32(&rgb, y / 2 * 2); + y_row = y_base + y * rgb.width; + u_row = u_base + (y / sub) * (rgb.width / sub); + v_row = v_base + (y / sub) * (rgb.width / sub); - for (x = 0; x < width; x++) { + for (x = 0; x < rgb.width; x++) { /* * Sub-sample the source image instead, so that U and V * sub-sampling does not require proper @@ -235,13 +230,10 @@ nv12_create_buffer(struct client *client, uint32_t drm_format, pixman_image_t *rgb_image) { + struct image_header rgb = image_header_from(rgb_image); struct yuv_buffer *buf; size_t bytes; - int width; - int height; int x, y; - void *rgb_pixels; - int rgb_stride_bytes; uint32_t *rgb_row; uint8_t *y_base; uint16_t *uv_base; @@ -253,24 +245,21 @@ nv12_create_buffer(struct client *client, assert(drm_format == DRM_FORMAT_NV12); - width = pixman_image_get_width(rgb_image); - height = pixman_image_get_height(rgb_image); - rgb_pixels = pixman_image_get_data(rgb_image); - rgb_stride_bytes = pixman_image_get_stride(rgb_image); - /* Full size Y, quarter UV */ - bytes = width * height + (width / 2) * (height / 2) * sizeof(uint16_t); - buf = yuv_buffer_create(client, bytes, width, height, width, drm_format); + bytes = rgb.width * rgb.height + + (rgb.width / 2) * (rgb.height / 2) * sizeof(uint16_t); + buf = yuv_buffer_create(client, bytes, rgb.width, rgb.height, + rgb.width, drm_format); y_base = buf->data; - uv_base = (uint16_t *)(y_base + width * height); + uv_base = (uint16_t *)(y_base + rgb.width * rgb.height); - for (y = 0; y < height; y++) { - rgb_row = rgb_pixels + (y / 2 * 2) * rgb_stride_bytes; - y_row = y_base + y * width; - uv_row = uv_base + (y / 2) * (width / 2); + for (y = 0; y < rgb.height; y++) { + rgb_row = image_header_get_row_u32(&rgb, y / 2 * 2); + y_row = y_base + y * rgb.width; + uv_row = uv_base + (y / 2) * (rgb.width / 2); - for (x = 0; x < width; x++) { + for (x = 0; x < rgb.width; x++) { /* * Sub-sample the source image instead, so that U and V * sub-sampling does not require proper @@ -307,13 +296,10 @@ yuyv_create_buffer(struct client *client, uint32_t drm_format, pixman_image_t *rgb_image) { + struct image_header rgb = image_header_from(rgb_image); struct yuv_buffer *buf; size_t bytes; - int width; - int height; int x, y; - void *rgb_pixels; - int rgb_stride_bytes; uint32_t *rgb_row; uint32_t *yuv_base; uint32_t *yuv_row; @@ -323,22 +309,18 @@ yuyv_create_buffer(struct client *client, assert(drm_format == DRM_FORMAT_YUYV); - width = pixman_image_get_width(rgb_image); - height = pixman_image_get_height(rgb_image); - rgb_pixels = pixman_image_get_data(rgb_image); - rgb_stride_bytes = pixman_image_get_stride(rgb_image); - /* Full size Y, horizontally subsampled UV, 2 pixels in 32 bits */ - bytes = width / 2 * height * sizeof(uint32_t); - buf = yuv_buffer_create(client, bytes, width, height, width / 2 * sizeof(uint32_t), drm_format); + bytes = rgb.width / 2 * rgb.height * sizeof(uint32_t); + buf = yuv_buffer_create(client, bytes, rgb.width, rgb.height, + rgb.width / 2 * sizeof(uint32_t), drm_format); yuv_base = buf->data; - for (y = 0; y < height; y++) { - rgb_row = rgb_pixels + (y / 2 * 2) * rgb_stride_bytes; - yuv_row = yuv_base + y * (width / 2); + for (y = 0; y < rgb.height; y++) { + rgb_row = image_header_get_row_u32(&rgb, y / 2 * 2); + yuv_row = yuv_base + y * (rgb.width / 2); - for (x = 0; x < width; x += 2) { + for (x = 0; x < rgb.width; x += 2) { /* * Sub-sample the source image instead, so that U and V * sub-sampling does not require proper @@ -367,13 +349,10 @@ xyuv8888_create_buffer(struct client *client, uint32_t drm_format, pixman_image_t *rgb_image) { + struct image_header rgb = image_header_from(rgb_image); struct yuv_buffer *buf; size_t bytes; - int width; - int height; int x, y; - void *rgb_pixels; - int rgb_stride_bytes; uint32_t *rgb_row; uint32_t *yuv_base; uint32_t *yuv_row; @@ -383,22 +362,18 @@ xyuv8888_create_buffer(struct client *client, assert(drm_format == DRM_FORMAT_XYUV8888); - width = pixman_image_get_width(rgb_image); - height = pixman_image_get_height(rgb_image); - rgb_pixels = pixman_image_get_data(rgb_image); - rgb_stride_bytes = pixman_image_get_stride(rgb_image); - /* Full size, 32 bits per pixel */ - bytes = width * height * sizeof(uint32_t); - buf = yuv_buffer_create(client, bytes, width, height, width * sizeof(uint32_t), drm_format); + bytes = rgb.width * rgb.height * sizeof(uint32_t); + buf = yuv_buffer_create(client, bytes, rgb.width, rgb.height, + rgb.width * sizeof(uint32_t), drm_format); yuv_base = buf->data; - for (y = 0; y < height; y++) { - rgb_row = rgb_pixels + (y / 2 * 2) * rgb_stride_bytes; - yuv_row = yuv_base + y * width; + for (y = 0; y < rgb.height; y++) { + rgb_row = image_header_get_row_u32(&rgb, y / 2 * 2); + yuv_row = yuv_base + y * rgb.width; - for (x = 0; x < width; x++) { + for (x = 0; x < rgb.width; x++) { /* * 2x2 sub-sample the source image to get the same * result as the other YUV variants, so we can use the From 67331be0cd60d4947d6272f9f0cd9aab2f5f6a87 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 13 Jun 2022 12:58:35 +0300 Subject: [PATCH 387/609] tests/internal-screenshot: use image-iter.h Simplify the code by using ready-made helpers. This also change the loop to draw the image row by row rather than column by column. Row by row is more natural as it is linear with the memory layout. No other change in behaviour. Signed-off-by: Pekka Paalanen --- tests/internal-screenshot-test.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/tests/internal-screenshot-test.c b/tests/internal-screenshot-test.c index e6e2962f..1c85660c 100644 --- a/tests/internal-screenshot-test.c +++ b/tests/internal-screenshot-test.c @@ -30,6 +30,7 @@ #include "weston-test-client-helper.h" #include "weston-test-fixture-compositor.h" +#include "image-iter.h" #include "test-config.h" static enum test_result_code @@ -55,30 +56,20 @@ DECLARE_FIXTURE_SETUP(fixture_setup); static void draw_stuff(pixman_image_t *image) { - int w, h; - int stride; /* bytes */ + struct image_header ih = image_header_from(image); int x, y; uint32_t r, g, b; - uint32_t *pixels; - uint32_t *pixel; - pixman_format_code_t fmt; - fmt = pixman_image_get_format(image); - w = pixman_image_get_width(image); - h = pixman_image_get_height(image); - stride = pixman_image_get_stride(image); - pixels = pixman_image_get_data(image); + for (y = 0; y < ih.height; y++) { + uint32_t *pixel = image_header_get_row_u32(&ih, y); - assert(PIXMAN_FORMAT_BPP(fmt) == 32); - - for (x = 0; x < w; x++) - for (y = 0; y < h; y++) { + for (x = 0; x < ih.width; x++, pixel++) { b = x; g = x + y; r = y; - pixel = pixels + (y * stride / 4) + x; *pixel = (255U << 24) | (r << 16) | (g << 8) | b; } + } } TEST(internal_screenshot) From f212a703cf4102b740a63156bf162e0b3e8b6e55 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 13 Jun 2022 13:12:28 +0300 Subject: [PATCH 388/609] tests/alpha-blend: use image-iter.h Simplify the code by using ready-made helpers. No change in behaviour. Signed-off-by: Pekka Paalanen --- tests/alpha-blending-test.c | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/tests/alpha-blending-test.c b/tests/alpha-blending-test.c index 58111b8e..3f7317f3 100644 --- a/tests/alpha-blending-test.c +++ b/tests/alpha-blending-test.c @@ -30,6 +30,7 @@ #include "weston-test-client-helper.h" #include "weston-test-fixture-compositor.h" +#include "image-iter.h" #include "color_util.h" struct setup_args { @@ -110,22 +111,14 @@ unpremult_float(struct color_float *cf) static void fill_alpha_pattern(struct buffer *buf) { - void *pixels; - int stride_bytes; - int w, h; + struct image_header ih = image_header_from(buf->image); int y; - assert(pixman_image_get_format(buf->image) == PIXMAN_a8r8g8b8); + assert(ih.pixman_format == PIXMAN_a8r8g8b8); + assert(ih.width == BLOCK_WIDTH * ALPHA_STEPS); - pixels = pixman_image_get_data(buf->image); - stride_bytes = pixman_image_get_stride(buf->image); - w = pixman_image_get_width(buf->image); - h = pixman_image_get_height(buf->image); - - assert(w == BLOCK_WIDTH * ALPHA_STEPS); - - for (y = 0; y < h; y++) { - uint32_t *row = pixels + y * stride_bytes; + for (y = 0; y < ih.height; y++) { + uint32_t *row = image_header_get_row_u32(&ih, y); uint32_t step; for (step = 0; step < (uint32_t)ALPHA_STEPS; step++) { @@ -140,7 +133,6 @@ fill_alpha_pattern(struct buffer *buf) } } - static bool compare_float(float ref, float dst, int x, const char *chan, float *max_diff) { @@ -269,16 +261,12 @@ pixels_monotonic(const uint32_t *row, int x) static void * get_middle_row(struct buffer *buf) { - const int y = (BLOCK_WIDTH - 1) / 2; /* middle row */ - void *pixels; - int stride_bytes; + struct image_header ih = image_header_from(buf->image); - assert(pixman_image_get_width(buf->image) >= BLOCK_WIDTH * ALPHA_STEPS); - assert(pixman_image_get_height(buf->image) >= BLOCK_WIDTH); + assert(ih.width >= BLOCK_WIDTH * ALPHA_STEPS); + assert(ih.height >= BLOCK_WIDTH); - pixels = pixman_image_get_data(buf->image); - stride_bytes = pixman_image_get_stride(buf->image); - return pixels + y * stride_bytes; + return image_header_get_row_u32(&ih, (BLOCK_WIDTH - 1) / 2); } static bool From 13ead893e2bebdc663bb81c9b424be87ee8abe3f Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 15 Jun 2022 15:50:22 +0100 Subject: [PATCH 389/609] Add weston_surface_has_content() Just a trivial wrapper to tell you whether or not the surface has valid content. Signed-off-by: Daniel Stone --- include/libweston/libweston.h | 3 +++ libweston/compositor.c | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index eb929a1f..552652fa 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -1928,6 +1928,9 @@ weston_surface_attach_solid(struct weston_surface *surface, void weston_buffer_destroy_solid(struct weston_buffer_reference *buffer_ref); +bool +weston_surface_has_content(struct weston_surface *surface); + void weston_view_destroy(struct weston_view *view); diff --git a/libweston/compositor.c b/libweston/compositor.c index 0dedb81f..6b48d74d 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -3609,6 +3609,16 @@ weston_compositor_schedule_repaint(struct weston_compositor *compositor) weston_output_schedule_repaint(output); } +/** + * Returns true if a surface has a buffer attached to it and thus valid + * content available. + */ +WL_EXPORT bool +weston_surface_has_content(struct weston_surface *surface) +{ + return !!surface->buffer_ref.buffer; +} + static void surface_destroy(struct wl_client *client, struct wl_resource *resource) { From d211e3173cf1d1ac920a39f8a66638b74272a372 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 15 Jun 2022 15:56:00 +0100 Subject: [PATCH 390/609] xdg-shell: Use weston_surface_has_content() Now that we've got a wrapper telling us whether or not the surface has content, just use it. Signed-off-by: Daniel Stone --- libweston-desktop/xdg-shell-v6.c | 8 ++++---- libweston-desktop/xdg-shell.c | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/libweston-desktop/xdg-shell-v6.c b/libweston-desktop/xdg-shell-v6.c index 955fccad..ff3a6344 100644 --- a/libweston-desktop/xdg-shell-v6.c +++ b/libweston-desktop/xdg-shell-v6.c @@ -645,11 +645,11 @@ weston_desktop_xdg_toplevel_committed(struct weston_desktop_xdg_toplevel *toplev struct weston_surface *wsurface = weston_desktop_surface_get_surface(toplevel->base.desktop_surface); - if (!wsurface->buffer_ref.buffer && !toplevel->added) { + if (!weston_surface_has_content(wsurface) && !toplevel->added) { weston_desktop_xdg_toplevel_ensure_added(toplevel); return; } - if (!wsurface->buffer_ref.buffer) + if (!weston_surface_has_content(wsurface)) return; struct weston_geometry geometry = @@ -1209,7 +1209,7 @@ weston_desktop_xdg_surface_committed(struct weston_desktop_surface *dsurface, struct weston_surface *wsurface = weston_desktop_surface_get_surface (dsurface); - if (wsurface->buffer_ref.buffer && !surface->configured) { + if (weston_surface_has_content(wsurface) && !surface->configured) { wl_resource_post_error(surface->resource, ZXDG_SURFACE_V6_ERROR_UNCONFIGURED_BUFFER, "xdg_surface has never been configured"); @@ -1397,7 +1397,7 @@ weston_desktop_xdg_shell_protocol_get_xdg_surface(struct wl_client *wl_client, if (surface->resource == NULL) return; - if (wsurface->buffer_ref.buffer != NULL) { + if (weston_surface_has_content(wsurface)) { wl_resource_post_error(surface->resource, ZXDG_SURFACE_V6_ERROR_UNCONFIGURED_BUFFER, "xdg_surface must not have a buffer at creation"); diff --git a/libweston-desktop/xdg-shell.c b/libweston-desktop/xdg-shell.c index 6cbf55e4..4b0509b1 100644 --- a/libweston-desktop/xdg-shell.c +++ b/libweston-desktop/xdg-shell.c @@ -693,11 +693,11 @@ weston_desktop_xdg_toplevel_committed(struct weston_desktop_xdg_toplevel *toplev struct weston_surface *wsurface = weston_desktop_surface_get_surface(toplevel->base.desktop_surface); - if (!wsurface->buffer_ref.buffer && !toplevel->added) { + if (!weston_surface_has_content(wsurface) && !toplevel->added) { weston_desktop_xdg_toplevel_ensure_added(toplevel); return; } - if (!wsurface->buffer_ref.buffer) + if (!weston_surface_has_content(wsurface)) return; struct weston_geometry geometry = @@ -1351,7 +1351,7 @@ weston_desktop_xdg_surface_committed(struct weston_desktop_surface *dsurface, struct weston_surface *wsurface = weston_desktop_surface_get_surface (dsurface); - if (wsurface->buffer_ref.buffer && !surface->configured) { + if (weston_surface_has_content(wsurface) && !surface->configured) { wl_resource_post_error(surface->resource, XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER, "xdg_surface has never been configured"); @@ -1518,7 +1518,7 @@ weston_desktop_xdg_shell_protocol_get_xdg_surface(struct wl_client *wl_client, return; } - if (wsurface->buffer_ref.buffer != NULL) { + if (weston_surface_has_content(wsurface)) { wl_resource_post_error(resource, XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER, "xdg_surface must not have a buffer at creation"); From 888d08d8a533a0c8791b82d79e7f13819f66d3ba Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 15 Jun 2022 15:56:24 +0100 Subject: [PATCH 391/609] desktop-shell: Use weston_surface_has_content() Now we've got a wrapper which can tell us whether or not a surface has content, use it. Signed-off-by: Daniel Stone --- desktop-shell/shell.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 8e3ec134..87bcae3d 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -2124,7 +2124,7 @@ shell_configure_fullscreen(struct shell_surface *shsurf) surface_subsurfaces_boundingbox(surface, &surf_x, &surf_y, &surf_width, &surf_height); - if (surface->buffer_ref.buffer) + if (weston_surface_has_content(surface)) center_on_output(shsurf->view, shsurf->fullscreen_output); } From dd6b5a190e4203b5482239c04e8b58eb6d184c88 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 15 Jun 2022 15:56:41 +0100 Subject: [PATCH 392/609] data-device: Use weston_surface_has_content() Now we've got a wrapper which tells us whether or not the surface has valid content, use it. The 'XXX' comment was removed because it's the same pattern as every other surface-role implementor: if the surface is not mapped but does have valid content, then map it. Signed-off-by: Daniel Stone --- libweston/data-device.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libweston/data-device.c b/libweston/data-device.c index bc2661c0..3506454c 100644 --- a/libweston/data-device.c +++ b/libweston/data-device.c @@ -420,8 +420,7 @@ drag_surface_configure(struct weston_drag *drag, assert((pointer != NULL && touch == NULL) || (pointer == NULL && touch != NULL)); - /* XXX: Why are we checking for a valid buffer here too ... ? */ - if (!weston_surface_is_mapped(es) && es->buffer_ref.buffer) { + if (!weston_surface_is_mapped(es) && weston_surface_has_content(es)) { if (pointer && pointer->sprite && weston_view_is_mapped(pointer->sprite)) list = &pointer->sprite->layer_link; From 51fe874ad4152fceaf2de50db96c1ec5e08bca1b Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 15 Jun 2022 21:11:40 +0100 Subject: [PATCH 393/609] libweston: Use weston_surface_has_content() in core compositor Used when taking the size from a buffer, as well as in subsurface handling. Signed-off-by: Daniel Stone --- libweston/compositor.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libweston/compositor.c b/libweston/compositor.c index 6b48d74d..a176626e 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -2121,7 +2121,7 @@ weston_surface_calculate_size_from_buffer(struct weston_surface *surface) { struct weston_buffer_viewport *vp = &surface->buffer_viewport; - if (!surface->buffer_ref.buffer) { + if (!weston_surface_has_content(surface)) { surface->width_from_buffer = 0; surface->height_from_buffer = 0; return; @@ -4531,7 +4531,7 @@ subsurface_committed(struct weston_surface *surface, int32_t dx, int32_t dy) */ if (!weston_surface_is_mapped(surface)) { - surface->is_mapped = surface->buffer_ref.buffer != NULL; + surface->is_mapped = weston_surface_has_content(surface); /* Cannot call weston_view_update_transform(), * because that would call it also for the parent surface, From 0c69688aa238c3576f99486b177f527f668ee704 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Sun, 12 Jun 2022 20:51:45 +0100 Subject: [PATCH 394/609] libweston: Add weston_surface_map() wrapper Change all instances of surface->is_mapped = true, to a specialised function. Signed-off-by: Daniel Stone --- desktop-shell/input-panel.c | 2 +- desktop-shell/shell.c | 6 ++--- fullscreen-shell/fullscreen-shell.c | 2 +- include/libweston/libweston.h | 3 +++ ivi-shell/ivi-layout.c | 2 +- kiosk-shell/kiosk-shell.c | 4 ++-- libweston-desktop/xwayland.c | 2 +- libweston/compositor.c | 37 ++++++++++++++++------------- libweston/data-device.c | 2 +- libweston/input.c | 2 +- libweston/touch-calibration.c | 2 +- tests/weston-test-desktop-shell.c | 2 +- tests/weston-test.c | 2 +- 13 files changed, 38 insertions(+), 30 deletions(-) diff --git a/desktop-shell/input-panel.c b/desktop-shell/input-panel.c index 0897ffde..6dc2e427 100644 --- a/desktop-shell/input-panel.c +++ b/desktop-shell/input-panel.c @@ -103,8 +103,8 @@ show_input_panel_surface(struct input_panel_surface *ipsurf) &ipsurf->view->layer_link); weston_view_geometry_dirty(ipsurf->view); weston_view_update_transform(ipsurf->view); - ipsurf->surface->is_mapped = true; ipsurf->view->is_mapped = true; + weston_surface_map(ipsurf->surface); weston_surface_damage(ipsurf->surface); if (ipsurf->anim) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 87bcae3d..73dbdce7 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -2501,7 +2501,7 @@ desktop_surface_committed(struct weston_desktop_surface *desktop_surface, if (!weston_surface_is_mapped(surface)) { map(shell, shsurf, sx, sy); - surface->is_mapped = true; + weston_surface_map(surface); /* as we need to survive the weston_surface destruction we'll * need to take another reference */ if (shsurf->shell->win_close_animation_type == ANIMATION_FADE) { @@ -2914,7 +2914,7 @@ configure_static_view(struct weston_view *ev, struct weston_layer *layer, int x, } weston_view_set_position(ev, ev->output->x + x, ev->output->y + y); - ev->surface->is_mapped = true; + weston_surface_map(ev->surface); ev->is_mapped = true; if (wl_list_empty(&ev->layer_link.link)) { @@ -3136,7 +3136,7 @@ lock_surface_committed(struct weston_surface *surface, int32_t sx, int32_t sy) weston_layer_entry_insert(&shell->lock_layer.view_list, &view->layer_link); weston_view_update_transform(view); - surface->is_mapped = true; + weston_surface_map(surface); view->is_mapped = true; shell_fade(shell, FADE_IN); } diff --git a/fullscreen-shell/fullscreen-shell.c b/fullscreen-shell/fullscreen-shell.c index c02ac1c3..dbec02cf 100644 --- a/fullscreen-shell/fullscreen-shell.c +++ b/fullscreen-shell/fullscreen-shell.c @@ -327,7 +327,7 @@ fs_output_create(struct fullscreen_shell *shell, struct weston_output *output) fsout->curtain = create_curtain(shell->compositor, fsout, output->x, output->y, output->width, output->height); - fsout->curtain->view->surface->is_mapped = true; + weston_surface_map(fsout->curtain->view->surface); fsout->curtain->view->is_mapped = true; weston_layer_entry_insert(&shell->layer.view_list, &fsout->curtain->view->layer_link); diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 552652fa..acfad1b3 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -1971,6 +1971,9 @@ weston_view_damage_below(struct weston_view *view); void weston_view_unmap(struct weston_view *view); +void +weston_surface_map(struct weston_surface *surface); + void weston_surface_unmap(struct weston_surface *surface); diff --git a/ivi-shell/ivi-layout.c b/ivi-shell/ivi-layout.c index e685e356..1c42d636 100644 --- a/ivi-shell/ivi-layout.c +++ b/ivi-shell/ivi-layout.c @@ -835,7 +835,7 @@ build_view_list(struct ivi_layout *layout) weston_layer_entry_insert(&layout->layout_layer.view_list, &ivi_view->view->layer_link); - ivi_view->ivisurf->surface->is_mapped = true; + weston_surface_map(ivi_view->ivisurf->surface); ivi_view->view->is_mapped = true; } } diff --git a/kiosk-shell/kiosk-shell.c b/kiosk-shell/kiosk-shell.c index 950e5811..c109e31f 100644 --- a/kiosk-shell/kiosk-shell.c +++ b/kiosk-shell/kiosk-shell.c @@ -523,7 +523,7 @@ kiosk_shell_output_recreate_background(struct kiosk_shell_output *shoutput) &shoutput->curtain->view->layer_link); shoutput->curtain->view->is_mapped = true; - shoutput->curtain->view->surface->is_mapped = true; + weston_surface_map(shoutput->curtain->view->surface); shoutput->curtain->view->surface->output = output; weston_view_set_output(shoutput->curtain->view, output); } @@ -799,7 +799,7 @@ desktop_surface_committed(struct weston_desktop_surface *desktop_surface, struct kiosk_shell_seat *kiosk_seat; shsurf->view->is_mapped = true; - surface->is_mapped = true; + weston_surface_map(surface); kiosk_seat = get_kiosk_shell_seat(seat); if (seat && kiosk_seat) diff --git a/libweston-desktop/xwayland.c b/libweston-desktop/xwayland.c index 9d4d22b4..057fdfd5 100644 --- a/libweston-desktop/xwayland.c +++ b/libweston-desktop/xwayland.c @@ -122,7 +122,7 @@ weston_desktop_xwayland_surface_change_state(struct weston_desktop_xwayland_surf weston_layer_entry_insert(&surface->xwayland->layer.view_list, &surface->view->layer_link); surface->view->is_mapped = true; - wsurface->is_mapped = true; + weston_surface_map(wsurface); } surface->state = state; diff --git a/libweston/compositor.c b/libweston/compositor.c index a176626e..47226f73 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -2261,6 +2261,12 @@ weston_view_unmap(struct weston_view *view) } } +WL_EXPORT void +weston_surface_map(struct weston_surface *surface) +{ + surface->is_mapped = true; +} + WL_EXPORT void weston_surface_unmap(struct weston_surface *surface) { @@ -4529,22 +4535,21 @@ subsurface_committed(struct weston_surface *surface, int32_t dx, int32_t dy) * mapped, parent is not in a visible layer, so this sub-surface * will not be drawn either. */ - - if (!weston_surface_is_mapped(surface)) { - surface->is_mapped = weston_surface_has_content(surface); - - /* Cannot call weston_view_update_transform(), - * because that would call it also for the parent surface, - * which might not be mapped yet. That would lead to - * inconsistent state, where the window could never be - * mapped. - * - * Instead just force the is_mapped flag on, to make - * weston_surface_is_mapped() return true, so that when the - * parent surface does get mapped, this one will get - * included, too. See view_list_add(). - */ - } + if (!weston_surface_is_mapped(surface) && + weston_surface_has_content(surface)) { + weston_surface_map(surface); + } + + /* Cannot call weston_view_update_transform() here, because that would + * call it also for the parent surface, which might not be mapped yet. + * That would lead to inconsistent state, where the window could never + * be mapped. + * + * Instead just force the child surface to appear mapped, to make + * weston_surface_is_mapped() return true, so that when the parent + * surface does get mapped, this one will get included, too. See + * view_list_add(). + */ } static struct weston_subsurface * diff --git a/libweston/data-device.c b/libweston/data-device.c index 3506454c..2042cce4 100644 --- a/libweston/data-device.c +++ b/libweston/data-device.c @@ -431,7 +431,7 @@ drag_surface_configure(struct weston_drag *drag, weston_layer_entry_insert(list, &drag->icon->layer_link); weston_view_update_transform(drag->icon); pixman_region32_clear(&es->pending.input); - es->is_mapped = true; + weston_surface_map(es); drag->icon->is_mapped = true; } diff --git a/libweston/input.c b/libweston/input.c index 7c8559ae..ead4b79e 100644 --- a/libweston/input.c +++ b/libweston/input.c @@ -2697,7 +2697,7 @@ pointer_cursor_surface_committed(struct weston_surface *es, weston_layer_entry_insert(&es->compositor->cursor_layer.view_list, &pointer->sprite->layer_link); weston_view_update_transform(pointer->sprite); - es->is_mapped = true; + weston_surface_map(es); pointer->sprite->is_mapped = true; } } diff --git a/libweston/touch-calibration.c b/libweston/touch-calibration.c index 11e59386..72a114a6 100644 --- a/libweston/touch-calibration.c +++ b/libweston/touch-calibration.c @@ -206,7 +206,7 @@ map_calibrator(struct weston_touch_calibrator *calibrator) calibrator->view->is_mapped = true; calibrator->surface->output = calibrator->output; - calibrator->surface->is_mapped = true; + weston_surface_map(calibrator->surface); weston_output_schedule_repaint(calibrator->output); diff --git a/tests/weston-test-desktop-shell.c b/tests/weston-test-desktop-shell.c index e6e208e6..2fb6d4b2 100644 --- a/tests/weston-test-desktop-shell.c +++ b/tests/weston-test-desktop-shell.c @@ -92,7 +92,7 @@ desktop_surface_committed(struct weston_desktop_surface *desktop_surface, if (weston_surface_is_mapped(surface)) return; - surface->is_mapped = true; + weston_surface_map(surface); weston_layer_entry_insert(&dts->layer.view_list, &dts->view->layer_link); weston_view_set_position(dts->view, 0 - geometry.x, 0 - geometry.y); weston_view_update_transform(dts->view); diff --git a/tests/weston-test.c b/tests/weston-test.c index f8db286b..58f27a04 100644 --- a/tests/weston-test.c +++ b/tests/weston-test.c @@ -164,7 +164,7 @@ test_surface_committed(struct weston_surface *surface, int32_t sx, int32_t sy) weston_view_update_transform(test_surface->view); - test_surface->surface->is_mapped = true; + weston_surface_map(test_surface->surface); test_surface->view->is_mapped = true; } From af7dcdddac4526103268dd9247369aacef47a498 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 1 Jun 2022 17:47:29 +0100 Subject: [PATCH 395/609] desktop-shell: Map surfaces in map() The only caller of map() then manually sets is_mapped = true. Just do it in the function which makes you think that's what it would do. Signed-off-by: Daniel Stone --- desktop-shell/shell.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 73dbdce7..33bbfc16 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -2454,6 +2454,8 @@ map(struct desktop_shell *shell, struct shell_surface *shsurf, weston_view_set_output(shsurf->view, shsurf->output); } + weston_surface_map(surface); + if (!shell->locked) { wl_list_for_each(seat, &compositor->seat_list, link) activate(shell, shsurf->view, seat, @@ -2501,7 +2503,6 @@ desktop_surface_committed(struct weston_desktop_surface *desktop_surface, if (!weston_surface_is_mapped(surface)) { map(shell, shsurf, sx, sy); - weston_surface_map(surface); /* as we need to survive the weston_surface destruction we'll * need to take another reference */ if (shsurf->shell->win_close_animation_type == ANIMATION_FADE) { From 19744a5207cccca079406166cad527575bb4abf9 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 2 Jun 2022 10:10:56 +0100 Subject: [PATCH 396/609] weston-curtain: Always mark surface as mapped The surface always has valid content, hence it can always be mapped. Signed-off-by: Daniel Stone --- fullscreen-shell/fullscreen-shell.c | 1 - kiosk-shell/kiosk-shell.c | 1 - shell-utils/shell-utils.c | 2 ++ 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fullscreen-shell/fullscreen-shell.c b/fullscreen-shell/fullscreen-shell.c index dbec02cf..009ad1e3 100644 --- a/fullscreen-shell/fullscreen-shell.c +++ b/fullscreen-shell/fullscreen-shell.c @@ -327,7 +327,6 @@ fs_output_create(struct fullscreen_shell *shell, struct weston_output *output) fsout->curtain = create_curtain(shell->compositor, fsout, output->x, output->y, output->width, output->height); - weston_surface_map(fsout->curtain->view->surface); fsout->curtain->view->is_mapped = true; weston_layer_entry_insert(&shell->layer.view_list, &fsout->curtain->view->layer_link); diff --git a/kiosk-shell/kiosk-shell.c b/kiosk-shell/kiosk-shell.c index c109e31f..62949ed4 100644 --- a/kiosk-shell/kiosk-shell.c +++ b/kiosk-shell/kiosk-shell.c @@ -523,7 +523,6 @@ kiosk_shell_output_recreate_background(struct kiosk_shell_output *shoutput) &shoutput->curtain->view->layer_link); shoutput->curtain->view->is_mapped = true; - weston_surface_map(shoutput->curtain->view->surface); shoutput->curtain->view->surface->output = output; weston_view_set_output(shoutput->curtain->view, output); } diff --git a/shell-utils/shell-utils.c b/shell-utils/shell-utils.c index 2bf1d3bd..a34e9d00 100644 --- a/shell-utils/shell-utils.c +++ b/shell-utils/shell-utils.c @@ -185,6 +185,8 @@ weston_curtain_create(struct weston_compositor *compositor, pixman_region32_init(&surface->input); } + weston_surface_map(surface); + weston_view_set_position(view, params->x, params->y); return curtain; From c0ff9ed24adc532462f24932bec5cbc5900053a0 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Thu, 2 Jun 2022 10:10:56 +0100 Subject: [PATCH 397/609] test-desktop-shell: Mark weston_curtain views as mapped Make sure we're there when we need them. Signed-off-by: Daniel Stone --- tests/weston-test-desktop-shell.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/weston-test-desktop-shell.c b/tests/weston-test-desktop-shell.c index 2fb6d4b2..eb231e0d 100644 --- a/tests/weston-test-desktop-shell.c +++ b/tests/weston-test-desktop-shell.c @@ -225,6 +225,7 @@ wet_shell_init(struct weston_compositor *ec, weston_layer_entry_insert(&dts->background_layer.view_list, &dts->background->view->layer_link); weston_view_update_transform(dts->background->view); + dts->background->view->is_mapped = true; dts->desktop = weston_desktop_create(ec, &shell_desktop_api, dts); if (dts->desktop == NULL) From f1fe6ec776f77115cadf308530da50994aab3018 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 1 Jun 2022 16:56:11 +0100 Subject: [PATCH 398/609] xdg-shell: Mark xdg_popup surfaces as mapped Keep the surface map state in sync with the buffer state: the surface can be mapped it has a valid buffer, and not if it doesn't. Signed-off-by: Daniel Stone --- libweston-desktop/xdg-shell-v6.c | 8 ++++++++ libweston-desktop/xdg-shell.c | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/libweston-desktop/xdg-shell-v6.c b/libweston-desktop/xdg-shell-v6.c index ff3a6344..13f4213d 100644 --- a/libweston-desktop/xdg-shell-v6.c +++ b/libweston-desktop/xdg-shell-v6.c @@ -841,6 +841,14 @@ weston_desktop_xdg_popup_committed(struct weston_desktop_xdg_popup *popup) popup->committed = true; weston_desktop_xdg_popup_update_position(popup->base.desktop_surface, popup); + + if (!weston_surface_is_mapped(wsurface) && + weston_surface_has_content(wsurface)) { + weston_surface_map(wsurface); + } else if (weston_surface_is_mapped(wsurface) && + !weston_surface_has_content(wsurface)) { + weston_surface_unmap(wsurface); + } } static void diff --git a/libweston-desktop/xdg-shell.c b/libweston-desktop/xdg-shell.c index 4b0509b1..eea00143 100644 --- a/libweston-desktop/xdg-shell.c +++ b/libweston-desktop/xdg-shell.c @@ -962,6 +962,14 @@ weston_desktop_xdg_popup_committed(struct weston_desktop_xdg_popup *popup) popup->committed = true; weston_desktop_xdg_popup_update_position(popup->base.desktop_surface, popup); + + if (!weston_surface_is_mapped(wsurface) && + weston_surface_has_content(wsurface)) { + weston_surface_map(wsurface); + } else if (weston_surface_is_mapped(wsurface) && + !weston_surface_has_content(wsurface)) { + weston_surface_unmap(wsurface); + } } static void From f962b4895891a495e147d795cc47a7af9ca441f6 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 1 Jun 2022 16:56:49 +0100 Subject: [PATCH 399/609] compositor: Only create paint nodes for mapped surfaces/views If a surface or a view is not mapped, then we should not be trying to paint it. Check if this is the case and ensure that we only insert paint nodes for mapped surfaces & views. Signed-off-by: Daniel Stone Fixes: #621 --- include/libweston/libweston.h | 4 ++++ libweston/compositor.c | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index acfad1b3..dbf80b15 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -1311,6 +1311,10 @@ struct weston_compositor { struct weston_log_scope *libseat_debug; struct content_protection *content_protection; + + /* One-time warning about a view appearing in the layer list when it + * or its surface are not mapped. */ + bool warned_about_unmapped_surface_or_view; }; struct weston_buffer { diff --git a/libweston/compositor.c b/libweston/compositor.c index 47226f73..bd1ad5e8 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -2991,6 +2991,26 @@ view_list_add(struct weston_compositor *compositor, struct weston_subsurface *sub; weston_view_update_transform(view); + + /* It is possible for a view to appear in the layer list even though + * the view or the surface is unmapped. This is erroneous but difficult + * to fix. */ + if (!weston_surface_is_mapped(view->surface) || + !weston_view_is_mapped(view) || + !weston_surface_has_content(view->surface)) { + if (!compositor->warned_about_unmapped_surface_or_view) { + weston_log("Detected an unmapped surface or view in " + "the layer list, which should not occur.\n"); + compositor->warned_about_unmapped_surface_or_view = true; + } + + pnode = weston_view_find_paint_node(view, output); + if (pnode) + weston_paint_node_destroy(pnode); + + return; + } + pnode = view_ensure_paint_node(view, output); if (wl_list_empty(&view->surface->subsurface_list)) { From 3e44a6eb3d00a40555e80a4bd0c1cc020c126aec Mon Sep 17 00:00:00 2001 From: Michael Olbrich Date: Tue, 14 Jun 2022 12:00:27 +0200 Subject: [PATCH 400/609] backend-drm: don't try to disable planes on session deactivation This uses the legacy DRM API it incomplete and no longer works anyways. At this point, weston is no longer DRM master, so these calls fail with "Permission denied". So just remove the corresponding code. Signed-off-by: Michael Olbrich --- libweston/backend-drm/drm.c | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index ffb584ad..98515c03 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -2686,9 +2686,7 @@ session_notify(struct wl_listener *listener, void *data) struct weston_compositor *compositor = data; struct drm_backend *b = to_drm_backend(compositor); struct drm_device *device = b->drm; - struct drm_plane *plane; struct drm_output *output; - struct drm_crtc *crtc; if (compositor->session_active) { weston_log("activating session\n"); @@ -2710,24 +2708,8 @@ session_notify(struct wl_listener *listener, void *data) * back, we schedule a repaint, which will process * pending frame callbacks. */ - wl_list_for_each(output, &compositor->output_list, base.link) { - crtc = output->crtc; + wl_list_for_each(output, &compositor->output_list, base.link) output->base.repaint_needed = false; - if (output->cursor_plane) - drmModeSetCursor(device->drm.fd, crtc->crtc_id, - 0, 0, 0); - } - - output = container_of(compositor->output_list.next, - struct drm_output, base.link); - crtc = output->crtc; - - wl_list_for_each(plane, &device->plane_list, link) { - if (plane->type != WDRM_PLANE_TYPE_OVERLAY) - continue; - drmModeSetPlane(device->drm.fd, plane->plane_id, crtc->crtc_id, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - } } } From 78933093a194c0614c0d428db965cf4f29685df9 Mon Sep 17 00:00:00 2001 From: Michael Olbrich Date: Mon, 13 Jun 2022 17:22:22 +0200 Subject: [PATCH 401/609] backend-drm: check that outputs are in fact ours This is a followup to ffc011d6a30d5e772e4f849abc5c60faf3ecb91a ("backend-drm: check that outputs and heads are in fact ours") which missed some places. Signed-off-by: Michael Olbrich --- libweston/backend-drm/drm.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index 98515c03..f67609e8 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -855,7 +855,7 @@ drm_output_find_special_plane(struct drm_device *device, struct drm_plane *plane; wl_list_for_each(plane, &device->plane_list, link) { - struct drm_output *tmp; + struct weston_output *base; bool found_elsewhere = false; if (plane->type != type) @@ -866,7 +866,11 @@ drm_output_find_special_plane(struct drm_device *device, /* On some platforms, primary/cursor planes can roam * between different CRTCs, so make sure we don't claim the * same plane for two outputs. */ - wl_list_for_each(tmp, &b->compositor->output_list, base.link) { + wl_list_for_each(base, &b->compositor->output_list, link) { + struct drm_output *tmp = to_drm_output(base); + if (!tmp) + continue; + if (tmp->cursor_plane == plane || tmp->scanout_plane == plane) { found_elsewhere = true; @@ -2686,7 +2690,7 @@ session_notify(struct wl_listener *listener, void *data) struct weston_compositor *compositor = data; struct drm_backend *b = to_drm_backend(compositor); struct drm_device *device = b->drm; - struct drm_output *output; + struct weston_output *output; if (compositor->session_active) { weston_log("activating session\n"); @@ -2708,8 +2712,9 @@ session_notify(struct wl_listener *listener, void *data) * back, we schedule a repaint, which will process * pending frame callbacks. */ - wl_list_for_each(output, &compositor->output_list, base.link) - output->base.repaint_needed = false; + wl_list_for_each(output, &compositor->output_list, link) + if (to_drm_output(output)) + output->repaint_needed = false; } } @@ -2999,11 +3004,15 @@ recorder_binding(struct weston_keyboard *keyboard, const struct timespec *time, uint32_t key, void *data) { struct drm_backend *b = data; + struct weston_output *base_output; struct drm_output *output; int width, height; - output = container_of(b->compositor->output_list.next, - struct drm_output, base.link); + wl_list_for_each(base_output, &b->compositor->output_list, link) { + output = to_drm_output(base_output); + if (output) + break; + } if (!output->recorder) { if (output->gbm_format != DRM_FORMAT_XRGB8888) { From 0d3e438d080433ed5d203c876e7de6c7f8a14f98 Mon Sep 17 00:00:00 2001 From: Ivan Nikolaenko Date: Mon, 20 Jun 2022 16:03:21 +0300 Subject: [PATCH 402/609] build: fix possible race/error for some backends There is missing dependency on linux-dmabuf-unstable-v1-server-protocol.h header file in backend-headless, backend-drm and backend-x11. That files do not depend on that header, in fact. But by this moment they've had that implicit dependency due to linux-dmabuf.h header. With specific set of meson configure options the protocol header is not generated at the right time, what causes build error in 100% cases using small amount of building threads (from -j1 to -j8). Signed-off-by: Ivan Nikolaenko --- libweston/backend-drm/state-propose.c | 1 + libweston/compositor.c | 1 + libweston/linux-dmabuf.c | 1 + libweston/linux-dmabuf.h | 1 - 4 files changed, 3 insertions(+), 1 deletion(-) diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index 1f4dccc7..1d4943b3 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -41,6 +41,7 @@ #include "color.h" #include "linux-dmabuf.h" #include "presentation-time-server-protocol.h" +#include "linux-dmabuf-unstable-v1-server-protocol.h" enum drm_output_propose_state_mode { DRM_OUTPUT_PROPOSE_STATE_MIXED, /**< mix renderer & planes */ diff --git a/libweston/compositor.c b/libweston/compositor.c index bd1ad5e8..5448e8ac 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -59,6 +59,7 @@ #include #include #include "linux-dmabuf.h" +#include "linux-dmabuf-unstable-v1-server-protocol.h" #include "viewporter-server-protocol.h" #include "presentation-time-server-protocol.h" #include "xdg-output-unstable-v1-server-protocol.h" diff --git a/libweston/linux-dmabuf.c b/libweston/linux-dmabuf.c index 21de498d..b73508e3 100644 --- a/libweston/linux-dmabuf.c +++ b/libweston/linux-dmabuf.c @@ -35,6 +35,7 @@ #include #include "linux-dmabuf.h" +#include "linux-dmabuf-unstable-v1-server-protocol.h" #include "shared/os-compatibility.h" #include "libweston-internal.h" #include "shared/weston-drm-fourcc.h" diff --git a/libweston/linux-dmabuf.h b/libweston/linux-dmabuf.h index 7cae93c5..32a7cb56 100644 --- a/libweston/linux-dmabuf.h +++ b/libweston/linux-dmabuf.h @@ -27,7 +27,6 @@ #define WESTON_LINUX_DMABUF_H #include -#include "linux-dmabuf-unstable-v1-server-protocol.h" #define MAX_DMABUF_PLANES 4 From 40e76fe19da9390b3ca1549f4b378865bb20228f Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Mon, 6 Jun 2022 13:25:27 -0500 Subject: [PATCH 403/609] xwayland: Set non zero default saved window size If a client starts off maximized, clicking the unmaximize button would result in a 0x0 window - basically a blob of decor with no content. Instead, use 512x512 as a totally random default value. Signed-off-by: Derek Foreman --- xwayland/window-manager.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/xwayland/window-manager.c b/xwayland/window-manager.c index f5b61c42..41dddd23 100644 --- a/xwayland/window-manager.c +++ b/xwayland/window-manager.c @@ -1509,6 +1509,12 @@ weston_wm_window_create(struct weston_wm *wm, window->override_redirect = override; window->width = width; window->height = height; + /* Completely arbitrary defaults in case something starts + * maximized and we unmaximize it later - at which point 0 x 0 + * would not be the most useful size. + */ + window->saved_width = 512; + window->saved_height = 512; window->x = x; window->y = y; window->pos_dirty = false; From 93b58c56486ea623d6146113463b01b5ea07f41e Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Mon, 6 Jun 2022 13:30:59 -0500 Subject: [PATCH 404/609] xwayland: Don't move window in response to geometry change if state changed When we leave fullscreen or maximized mode we restore a saved window position. This is expected, but that saved position was saved when window geometry was set to have shadows rendered. Since we restore a saved position that had shadow geometry, we don't want to move the window when we set geometry again, or we'll move away from the intended saved position. I guess this is a counter-proposal to !614 Fixes #454 Signed-off-by: Derek Foreman --- libweston-desktop/xwayland.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/libweston-desktop/xwayland.c b/libweston-desktop/xwayland.c index 057fdfd5..ae6a92e4 100644 --- a/libweston-desktop/xwayland.c +++ b/libweston-desktop/xwayland.c @@ -64,6 +64,7 @@ struct weston_desktop_xwayland_surface { bool committed; bool added; enum weston_desktop_xwayland_surface_state state; + enum weston_desktop_xwayland_surface_state prev_state; }; static void @@ -150,8 +151,16 @@ weston_desktop_xwayland_surface_committed(struct weston_desktop_surface *dsurfac if (surface->has_next_geometry) { oldgeom = weston_desktop_surface_get_geometry(surface->surface); - sx -= surface->next_geometry.x - oldgeom.x; - sy -= surface->next_geometry.y - oldgeom.y; + /* If we're transitioning away from fullscreen or maximized + * we've moved to old saved co-ordinates that were saved + * with window geometry in place, so avoid adajusting by + * the geometry in those cases. + */ + if (surface->state == surface->prev_state) { + sx -= surface->next_geometry.x - oldgeom.x; + sy -= surface->next_geometry.y - oldgeom.y; + } + surface->prev_state = surface->state; surface->has_next_geometry = false; weston_desktop_surface_set_geometry(surface->surface, From 8763f3800e1d6ec6cf696eb6c6274424b60935ab Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Mon, 6 Jun 2022 13:34:12 -0500 Subject: [PATCH 405/609] xwayland: Update net_wm state when we change it. According to the wm-spec we must keep the _NET_WM_STATE property updated to reflect the current state of the window. This has been biting me when firefox starts maximized, then I click the maximize button to toggle to unmaximized state. The next time I mouse over the maximize button (which causes the frame to be re-rendered with the maximized button in a highlighted state) we re-read the window state and weston then believes the window is maximized even though it is being rendered in a not-maximized state. Update the state when we change maximized status so this doesn't happen. Signed-off-by: Derek Foreman --- xwayland/window-manager.c | 1 + 1 file changed, 1 insertion(+) diff --git a/xwayland/window-manager.c b/xwayland/window-manager.c index 41dddd23..dc5f8f9e 100644 --- a/xwayland/window-manager.c +++ b/xwayland/window-manager.c @@ -2176,6 +2176,7 @@ weston_wm_handle_button(struct weston_wm *wm, xcb_generic_event_t *event) if (frame_status(window->frame) & FRAME_STATUS_MAXIMIZE) { window->maximized_horz = !window->maximized_horz; window->maximized_vert = !window->maximized_vert; + weston_wm_window_set_net_wm_state(window); if (weston_wm_window_is_maximized(window)) { window->saved_width = window->width; window->saved_height = window->height; From 107d69f10cf7d74964f289c86e17389d29595439 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Mon, 6 Jun 2022 13:37:50 -0500 Subject: [PATCH 406/609] xwayland: Stop drawing shadows on maximized windows This is especially weird on multi-head setups, but we shouldn't be doing it in any cases. Signed-off-by: Derek Foreman --- xwayland/window-manager.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/xwayland/window-manager.c b/xwayland/window-manager.c index dc5f8f9e..0293e3f2 100644 --- a/xwayland/window-manager.c +++ b/xwayland/window-manager.c @@ -1760,10 +1760,12 @@ weston_wm_window_set_toplevel(struct weston_wm_window *window) xwayland_interface->set_toplevel(window->shsurf); window->width = window->saved_width; window->height = window->saved_height; - if (window->frame) + if (window->frame) { + frame_unset_flag(window->frame, FRAME_FLAG_MAXIMIZED); frame_resize_inside(window->frame, window->width, window->height); + } weston_wm_window_configure(window); } @@ -2780,6 +2782,9 @@ send_configure(struct weston_surface *surface, int32_t width, int32_t height) window->height = new_height; if (window->frame) { + if (weston_wm_window_is_maximized(window)) + frame_set_flag(window->frame, FRAME_FLAG_MAXIMIZED); + frame_resize_inside(window->frame, window->width, window->height); } From 49d6532254fa27e3a28a3fb26d0b9d253d002125 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Tue, 27 Jul 2021 12:55:58 +0300 Subject: [PATCH 407/609] shared/xcb-xwayland: Split into common helpers Avoid duplication of atom retrieval. This is particuarly useful if one would one to reuse atom retrival in other parts, like tests. Signed-off-by: Marius Vlad Suggested-by: Daniel Stone --- shared/meson.build | 20 ++++++ shared/xcb-xwayland.c | 145 ++++++++++++++++++++++++++++++++++++++ shared/xcb-xwayland.h | 104 +++++++++++++++++++++++++++ xwayland/meson.build | 2 +- xwayland/window-manager.c | 115 +----------------------------- xwayland/xwayland.h | 73 +------------------ 6 files changed, 274 insertions(+), 185 deletions(-) create mode 100644 shared/xcb-xwayland.c create mode 100644 shared/xcb-xwayland.h diff --git a/shared/meson.build b/shared/meson.build index 9a4af530..d05e2d46 100644 --- a/shared/meson.build +++ b/shared/meson.build @@ -22,6 +22,26 @@ dep_libshared = declare_dependency( dependencies: deps_libshared ) +xcb_dep = dependency('xcb', required: false) + +xcb_xwayland_srcs = [ + 'xcb-xwayland.c', +] + +lib_xcb_xwayland = static_library( + 'xcb-xwayland', + xcb_xwayland_srcs, + include_directories: common_inc, + dependencies: [ xcb_dep ], + install: false, + build_by_default: false, +) + +dep_xcb_xwayland = declare_dependency( + link_with: lib_xcb_xwayland, + include_directories: public_inc, +) + srcs_cairo_shared = [ 'image-loader.c', 'cairo-util.c', diff --git a/shared/xcb-xwayland.c b/shared/xcb-xwayland.c new file mode 100644 index 00000000..fe801198 --- /dev/null +++ b/shared/xcb-xwayland.c @@ -0,0 +1,145 @@ +/* + * Copyright © 2011 Intel Corporation + * Copyright © 2021 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include "shared/helpers.h" +#include "xcb-xwayland.h" + +const char * +get_atom_name(xcb_connection_t *c, xcb_atom_t atom) +{ + xcb_get_atom_name_cookie_t cookie; + xcb_get_atom_name_reply_t *reply; + xcb_generic_error_t *e; + static char buffer[64]; + + if (atom == XCB_ATOM_NONE) + return "None"; + + cookie = xcb_get_atom_name(c, atom); + reply = xcb_get_atom_name_reply(c, cookie, &e); + + if (reply) { + snprintf(buffer, sizeof buffer, "%.*s", + xcb_get_atom_name_name_length(reply), + xcb_get_atom_name_name(reply)); + } else { + snprintf(buffer, sizeof buffer, "(atom %u)", atom); + } + + free(reply); + + return buffer; +} + +void +x11_get_atoms(xcb_connection_t *connection, struct atom_x11 *atom) +{ + unsigned int i; + +#define F(field) offsetof(struct atom_x11, field) + + static const struct { const char *name; int offset; } atoms[] = { + { "WM_PROTOCOLS", F(wm_protocols) }, + { "WM_NORMAL_HINTS", F(wm_normal_hints) }, + { "WM_TAKE_FOCUS", F(wm_take_focus) }, + { "WM_DELETE_WINDOW", F(wm_delete_window) }, + { "WM_STATE", F(wm_state) }, + { "WM_S0", F(wm_s0) }, + { "WM_CLIENT_MACHINE", F(wm_client_machine) }, + { "_NET_WM_CM_S0", F(net_wm_cm_s0) }, + { "_NET_WM_NAME", F(net_wm_name) }, + { "_NET_WM_PID", F(net_wm_pid) }, + { "_NET_WM_ICON", F(net_wm_icon) }, + { "_NET_WM_STATE", F(net_wm_state) }, + { "_NET_WM_STATE_MAXIMIZED_VERT", F(net_wm_state_maximized_vert) }, + { "_NET_WM_STATE_MAXIMIZED_HORZ", F(net_wm_state_maximized_horz) }, + { "_NET_WM_STATE_FULLSCREEN", F(net_wm_state_fullscreen) }, + { "_NET_WM_USER_TIME", F(net_wm_user_time) }, + { "_NET_WM_ICON_NAME", F(net_wm_icon_name) }, + { "_NET_WM_DESKTOP", F(net_wm_desktop) }, + { "_NET_WM_WINDOW_TYPE", F(net_wm_window_type) }, + + { "_NET_WM_WINDOW_TYPE_DESKTOP", F(net_wm_window_type_desktop) }, + { "_NET_WM_WINDOW_TYPE_DOCK", F(net_wm_window_type_dock) }, + { "_NET_WM_WINDOW_TYPE_TOOLBAR", F(net_wm_window_type_toolbar) }, + { "_NET_WM_WINDOW_TYPE_MENU", F(net_wm_window_type_menu) }, + { "_NET_WM_WINDOW_TYPE_UTILITY", F(net_wm_window_type_utility) }, + { "_NET_WM_WINDOW_TYPE_SPLASH", F(net_wm_window_type_splash) }, + { "_NET_WM_WINDOW_TYPE_DIALOG", F(net_wm_window_type_dialog) }, + { "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", F(net_wm_window_type_dropdown) }, + { "_NET_WM_WINDOW_TYPE_POPUP_MENU", F(net_wm_window_type_popup) }, + { "_NET_WM_WINDOW_TYPE_TOOLTIP", F(net_wm_window_type_tooltip) }, + { "_NET_WM_WINDOW_TYPE_NOTIFICATION", F(net_wm_window_type_notification) }, + { "_NET_WM_WINDOW_TYPE_COMBO", F(net_wm_window_type_combo) }, + { "_NET_WM_WINDOW_TYPE_DND", F(net_wm_window_type_dnd) }, + { "_NET_WM_WINDOW_TYPE_NORMAL", F(net_wm_window_type_normal) }, + + { "_NET_WM_MOVERESIZE", F(net_wm_moveresize) }, + { "_NET_SUPPORTING_WM_CHECK", F(net_supporting_wm_check) }, + { "_NET_SUPPORTED", F(net_supported) }, + { "_NET_ACTIVE_WINDOW", F(net_active_window) }, + { "_MOTIF_WM_HINTS", F(motif_wm_hints) }, + { "CLIPBOARD", F(clipboard) }, + { "CLIPBOARD_MANAGER", F(clipboard_manager) }, + { "TARGETS", F(targets) }, + { "UTF8_STRING", F(utf8_string) }, + { "_WL_SELECTION", F(wl_selection) }, + { "INCR", F(incr) }, + { "TIMESTAMP", F(timestamp) }, + { "MULTIPLE", F(multiple) }, + { "UTF8_STRING" , F(utf8_string) }, + { "COMPOUND_TEXT", F(compound_text) }, + { "TEXT", F(text) }, + { "STRING", F(string) }, + { "WINDOW", F(window) }, + { "WL_SURFACE_ID", F(wl_surface_id) }, + }; + + xcb_intern_atom_cookie_t cookies[ARRAY_LENGTH(atoms)]; + + for (i = 0; i < ARRAY_LENGTH(atoms); i++) + cookies[i] = + xcb_intern_atom(connection, 0, strlen(atoms[i].name), atoms[i].name); + + for (i = 0; i < ARRAY_LENGTH(atoms); i++) { + xcb_intern_atom_reply_t *reply_atom; + reply_atom = xcb_intern_atom_reply(connection, cookies[i], NULL); + assert(reply_atom); + + xcb_atom_t rr_atom = reply_atom->atom; + *(xcb_atom_t *) ((char *) atom + atoms[i].offset) = rr_atom; + + free(reply_atom); + } +} diff --git a/shared/xcb-xwayland.h b/shared/xcb-xwayland.h new file mode 100644 index 00000000..f4f03954 --- /dev/null +++ b/shared/xcb-xwayland.h @@ -0,0 +1,104 @@ +/* + * Copyright © 2011 Intel Corporation + * Copyright © 2021 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include + +#define SEND_EVENT_MASK (0x80) +#define EVENT_TYPE(event) ((event)->response_type & ~SEND_EVENT_MASK) + +struct atom_x11 { + xcb_atom_t wm_protocols; + xcb_atom_t wm_normal_hints; + xcb_atom_t wm_take_focus; + xcb_atom_t wm_delete_window; + xcb_atom_t wm_state; + xcb_atom_t wm_s0; + xcb_atom_t wm_client_machine; + xcb_atom_t net_wm_cm_s0; + xcb_atom_t net_wm_name; + xcb_atom_t net_wm_pid; + xcb_atom_t net_wm_icon; + xcb_atom_t net_wm_state; + xcb_atom_t net_wm_state_maximized_vert; + xcb_atom_t net_wm_state_maximized_horz; + xcb_atom_t net_wm_state_fullscreen; + xcb_atom_t net_wm_user_time; + xcb_atom_t net_wm_icon_name; + xcb_atom_t net_wm_desktop; + xcb_atom_t net_wm_window_type; + xcb_atom_t net_wm_window_type_desktop; + xcb_atom_t net_wm_window_type_dock; + xcb_atom_t net_wm_window_type_toolbar; + xcb_atom_t net_wm_window_type_menu; + xcb_atom_t net_wm_window_type_utility; + xcb_atom_t net_wm_window_type_splash; + xcb_atom_t net_wm_window_type_dialog; + xcb_atom_t net_wm_window_type_dropdown; + xcb_atom_t net_wm_window_type_popup; + xcb_atom_t net_wm_window_type_tooltip; + xcb_atom_t net_wm_window_type_notification; + xcb_atom_t net_wm_window_type_combo; + xcb_atom_t net_wm_window_type_dnd; + xcb_atom_t net_wm_window_type_normal; + xcb_atom_t net_wm_moveresize; + xcb_atom_t net_supporting_wm_check; + xcb_atom_t net_supported; + xcb_atom_t net_active_window; + xcb_atom_t motif_wm_hints; + xcb_atom_t clipboard; + xcb_atom_t clipboard_manager; + xcb_atom_t targets; + xcb_atom_t utf8_string; + xcb_atom_t wl_selection; + xcb_atom_t incr; + xcb_atom_t timestamp; + xcb_atom_t multiple; + xcb_atom_t compound_text; + xcb_atom_t text; + xcb_atom_t string; + xcb_atom_t window; + xcb_atom_t text_plain_utf8; + xcb_atom_t text_plain; + xcb_atom_t xdnd_selection; + xcb_atom_t xdnd_aware; + xcb_atom_t xdnd_enter; + xcb_atom_t xdnd_leave; + xcb_atom_t xdnd_drop; + xcb_atom_t xdnd_status; + xcb_atom_t xdnd_finished; + xcb_atom_t xdnd_type_list; + xcb_atom_t xdnd_action_copy; + xcb_atom_t wl_surface_id; + xcb_atom_t allow_commits; +}; + +const char * +get_atom_name(xcb_connection_t *c, xcb_atom_t atom); + +void +x11_get_atoms(xcb_connection_t *connection, struct atom_x11 *atom); diff --git a/xwayland/meson.build b/xwayland/meson.build index d8482a46..59a78287 100644 --- a/xwayland/meson.build +++ b/xwayland/meson.build @@ -26,7 +26,7 @@ dep_names_xwayland = [ 'cairo-xcb', ] -deps_xwayland = [ dep_libweston_public ] +deps_xwayland = [ dep_libweston_public, dep_xcb_xwayland ] foreach name : dep_names_xwayland d = dependency(name, required: false) diff --git a/xwayland/window-manager.c b/xwayland/window-manager.c index 0293e3f2..ff9eddf7 100644 --- a/xwayland/window-manager.c +++ b/xwayland/window-manager.c @@ -48,6 +48,7 @@ #include "shared/cairo-util.h" #include "hash.h" #include "shared/helpers.h" +#include "shared/xcb-xwayland.h" struct wm_size_hints { uint32_t flags; @@ -268,32 +269,6 @@ wm_lookup_window(struct weston_wm *wm, xcb_window_t hash, return false; } -const char * -get_atom_name(xcb_connection_t *c, xcb_atom_t atom) -{ - xcb_get_atom_name_cookie_t cookie; - xcb_get_atom_name_reply_t *reply; - xcb_generic_error_t *e; - static char buffer[64]; - - if (atom == XCB_ATOM_NONE) - return "None"; - - cookie = xcb_get_atom_name (c, atom); - reply = xcb_get_atom_name_reply (c, cookie, &e); - - if (reply) { - snprintf(buffer, sizeof buffer, "%.*s", - xcb_get_atom_name_name_length (reply), - xcb_get_atom_name_name (reply)); - } else { - snprintf(buffer, sizeof buffer, "(atom %u)", atom); - } - - free(reply); - - return buffer; -} static xcb_cursor_t xcb_cursor_image_load_cursor(struct weston_wm *wm, const XcursorImage *img) @@ -2387,84 +2362,8 @@ weston_wm_get_visual_and_colormap(struct weston_wm *wm) static void weston_wm_get_resources(struct weston_wm *wm) { - -#define F(field) offsetof(struct weston_wm, field) - - static const struct { const char *name; int offset; } atoms[] = { - { "WM_PROTOCOLS", F(atom.wm_protocols) }, - { "WM_NORMAL_HINTS", F(atom.wm_normal_hints) }, - { "WM_TAKE_FOCUS", F(atom.wm_take_focus) }, - { "WM_DELETE_WINDOW", F(atom.wm_delete_window) }, - { "WM_STATE", F(atom.wm_state) }, - { "WM_S0", F(atom.wm_s0) }, - { "WM_CLIENT_MACHINE", F(atom.wm_client_machine) }, - { "_NET_WM_CM_S0", F(atom.net_wm_cm_s0) }, - { "_NET_WM_NAME", F(atom.net_wm_name) }, - { "_NET_WM_PID", F(atom.net_wm_pid) }, - { "_NET_WM_ICON", F(atom.net_wm_icon) }, - { "_NET_WM_STATE", F(atom.net_wm_state) }, - { "_NET_WM_STATE_MAXIMIZED_VERT", F(atom.net_wm_state_maximized_vert) }, - { "_NET_WM_STATE_MAXIMIZED_HORZ", F(atom.net_wm_state_maximized_horz) }, - { "_NET_WM_STATE_FULLSCREEN", F(atom.net_wm_state_fullscreen) }, - { "_NET_WM_USER_TIME", F(atom.net_wm_user_time) }, - { "_NET_WM_ICON_NAME", F(atom.net_wm_icon_name) }, - { "_NET_WM_DESKTOP", F(atom.net_wm_desktop) }, - { "_NET_WM_WINDOW_TYPE", F(atom.net_wm_window_type) }, - - { "_NET_WM_WINDOW_TYPE_DESKTOP", F(atom.net_wm_window_type_desktop) }, - { "_NET_WM_WINDOW_TYPE_DOCK", F(atom.net_wm_window_type_dock) }, - { "_NET_WM_WINDOW_TYPE_TOOLBAR", F(atom.net_wm_window_type_toolbar) }, - { "_NET_WM_WINDOW_TYPE_MENU", F(atom.net_wm_window_type_menu) }, - { "_NET_WM_WINDOW_TYPE_UTILITY", F(atom.net_wm_window_type_utility) }, - { "_NET_WM_WINDOW_TYPE_SPLASH", F(atom.net_wm_window_type_splash) }, - { "_NET_WM_WINDOW_TYPE_DIALOG", F(atom.net_wm_window_type_dialog) }, - { "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", F(atom.net_wm_window_type_dropdown) }, - { "_NET_WM_WINDOW_TYPE_POPUP_MENU", F(atom.net_wm_window_type_popup) }, - { "_NET_WM_WINDOW_TYPE_TOOLTIP", F(atom.net_wm_window_type_tooltip) }, - { "_NET_WM_WINDOW_TYPE_NOTIFICATION", F(atom.net_wm_window_type_notification) }, - { "_NET_WM_WINDOW_TYPE_COMBO", F(atom.net_wm_window_type_combo) }, - { "_NET_WM_WINDOW_TYPE_DND", F(atom.net_wm_window_type_dnd) }, - { "_NET_WM_WINDOW_TYPE_NORMAL", F(atom.net_wm_window_type_normal) }, - - { "_NET_WM_MOVERESIZE", F(atom.net_wm_moveresize) }, - { "_NET_SUPPORTING_WM_CHECK", - F(atom.net_supporting_wm_check) }, - { "_NET_SUPPORTED", F(atom.net_supported) }, - { "_NET_ACTIVE_WINDOW", F(atom.net_active_window) }, - { "_MOTIF_WM_HINTS", F(atom.motif_wm_hints) }, - { "CLIPBOARD", F(atom.clipboard) }, - { "CLIPBOARD_MANAGER", F(atom.clipboard_manager) }, - { "TARGETS", F(atom.targets) }, - { "UTF8_STRING", F(atom.utf8_string) }, - { "_WL_SELECTION", F(atom.wl_selection) }, - { "INCR", F(atom.incr) }, - { "TIMESTAMP", F(atom.timestamp) }, - { "MULTIPLE", F(atom.multiple) }, - { "UTF8_STRING" , F(atom.utf8_string) }, - { "COMPOUND_TEXT", F(atom.compound_text) }, - { "TEXT", F(atom.text) }, - { "STRING", F(atom.string) }, - { "WINDOW", F(atom.window) }, - { "text/plain;charset=utf-8", F(atom.text_plain_utf8) }, - { "text/plain", F(atom.text_plain) }, - { "XdndSelection", F(atom.xdnd_selection) }, - { "XdndAware", F(atom.xdnd_aware) }, - { "XdndEnter", F(atom.xdnd_enter) }, - { "XdndLeave", F(atom.xdnd_leave) }, - { "XdndDrop", F(atom.xdnd_drop) }, - { "XdndStatus", F(atom.xdnd_status) }, - { "XdndFinished", F(atom.xdnd_finished) }, - { "XdndTypeList", F(atom.xdnd_type_list) }, - { "XdndActionCopy", F(atom.xdnd_action_copy) }, - { "_XWAYLAND_ALLOW_COMMITS", F(atom.allow_commits) }, - { "WL_SURFACE_ID", F(atom.wl_surface_id) } - }; -#undef F - xcb_xfixes_query_version_cookie_t xfixes_cookie; xcb_xfixes_query_version_reply_t *xfixes_reply; - xcb_intern_atom_cookie_t cookies[ARRAY_LENGTH(atoms)]; - xcb_intern_atom_reply_t *reply; xcb_render_query_pict_formats_reply_t *formats_reply; xcb_render_query_pict_formats_cookie_t formats_cookie; xcb_render_pictforminfo_t *formats; @@ -2475,17 +2374,7 @@ weston_wm_get_resources(struct weston_wm *wm) formats_cookie = xcb_render_query_pict_formats(wm->conn); - for (i = 0; i < ARRAY_LENGTH(atoms); i++) - cookies[i] = xcb_intern_atom (wm->conn, 0, - strlen(atoms[i].name), - atoms[i].name); - - for (i = 0; i < ARRAY_LENGTH(atoms); i++) { - reply = xcb_intern_atom_reply (wm->conn, cookies[i], NULL); - *(xcb_atom_t *) ((char *) wm + atoms[i].offset) = reply->atom; - free(reply); - } - + x11_get_atoms(wm->conn, &wm->atom); wm->xfixes = xcb_get_extension_data(wm->conn, &xcb_xfixes_id); if (!wm->xfixes || !wm->xfixes->present) weston_log("xfixes not available\n"); diff --git a/xwayland/xwayland.h b/xwayland/xwayland.h index 3ef0404f..df9cab83 100644 --- a/xwayland/xwayland.h +++ b/xwayland/xwayland.h @@ -33,9 +33,7 @@ #include #include #include - -#define SEND_EVENT_MASK (0x80) -#define EVENT_TYPE(event) ((event)->response_type & ~SEND_EVENT_MASK) +#include "shared/xcb-xwayland.h" struct weston_xserver { struct wl_display *wl_display; @@ -94,80 +92,13 @@ struct weston_wm { xcb_window_t dnd_window; xcb_window_t dnd_owner; - struct { - xcb_atom_t wm_protocols; - xcb_atom_t wm_normal_hints; - xcb_atom_t wm_take_focus; - xcb_atom_t wm_delete_window; - xcb_atom_t wm_state; - xcb_atom_t wm_s0; - xcb_atom_t wm_client_machine; - xcb_atom_t net_wm_cm_s0; - xcb_atom_t net_wm_name; - xcb_atom_t net_wm_pid; - xcb_atom_t net_wm_icon; - xcb_atom_t net_wm_state; - xcb_atom_t net_wm_state_maximized_vert; - xcb_atom_t net_wm_state_maximized_horz; - xcb_atom_t net_wm_state_fullscreen; - xcb_atom_t net_wm_user_time; - xcb_atom_t net_wm_icon_name; - xcb_atom_t net_wm_desktop; - xcb_atom_t net_wm_window_type; - xcb_atom_t net_wm_window_type_desktop; - xcb_atom_t net_wm_window_type_dock; - xcb_atom_t net_wm_window_type_toolbar; - xcb_atom_t net_wm_window_type_menu; - xcb_atom_t net_wm_window_type_utility; - xcb_atom_t net_wm_window_type_splash; - xcb_atom_t net_wm_window_type_dialog; - xcb_atom_t net_wm_window_type_dropdown; - xcb_atom_t net_wm_window_type_popup; - xcb_atom_t net_wm_window_type_tooltip; - xcb_atom_t net_wm_window_type_notification; - xcb_atom_t net_wm_window_type_combo; - xcb_atom_t net_wm_window_type_dnd; - xcb_atom_t net_wm_window_type_normal; - xcb_atom_t net_wm_moveresize; - xcb_atom_t net_supporting_wm_check; - xcb_atom_t net_supported; - xcb_atom_t net_active_window; - xcb_atom_t motif_wm_hints; - xcb_atom_t clipboard; - xcb_atom_t clipboard_manager; - xcb_atom_t targets; - xcb_atom_t utf8_string; - xcb_atom_t wl_selection; - xcb_atom_t incr; - xcb_atom_t timestamp; - xcb_atom_t multiple; - xcb_atom_t compound_text; - xcb_atom_t text; - xcb_atom_t string; - xcb_atom_t window; - xcb_atom_t text_plain_utf8; - xcb_atom_t text_plain; - xcb_atom_t xdnd_selection; - xcb_atom_t xdnd_aware; - xcb_atom_t xdnd_enter; - xcb_atom_t xdnd_leave; - xcb_atom_t xdnd_drop; - xcb_atom_t xdnd_status; - xcb_atom_t xdnd_finished; - xcb_atom_t xdnd_type_list; - xcb_atom_t xdnd_action_copy; - xcb_atom_t wl_surface_id; - xcb_atom_t allow_commits; - } atom; + struct atom_x11 atom; }; void dump_property(FILE *fp, struct weston_wm *wm, xcb_atom_t property, xcb_get_property_reply_t *reply); -const char * -get_atom_name(xcb_connection_t *c, xcb_atom_t atom); - void weston_wm_selection_init(struct weston_wm *wm); int From 924b94bc9499bed7fa5f28f72255a7b63b53fb6f Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 3 Jun 2022 13:57:05 +0300 Subject: [PATCH 408/609] gl-renderer: call it view_alpha in frag We always talk about "view alpha", so the name variable in the fragment shader the same. Now it's clear without the comments, making the code easier to read overall. Signed-off-by: Pekka Paalanen --- libweston/renderer-gl/fragment.glsl | 11 ++++------- libweston/renderer-gl/gl-shaders.c | 8 ++++---- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/libweston/renderer-gl/fragment.glsl b/libweston/renderer-gl/fragment.glsl index e8dae27d..908075e6 100644 --- a/libweston/renderer-gl/fragment.glsl +++ b/libweston/renderer-gl/fragment.glsl @@ -119,7 +119,7 @@ uniform sampler2D tex; varying vec2 v_texcoord; uniform sampler2D tex1; uniform sampler2D tex2; -uniform float alpha; +uniform float view_alpha; uniform vec4 unicolor; uniform HIGHPRECISION sampler2D color_pre_curve_lut_2d; uniform HIGHPRECISION vec2 color_pre_curve_lut_scale_offset; @@ -274,8 +274,7 @@ main() color = color_pipeline(color); - /* View alpha (opacity) */ - color.a *= alpha; + color.a *= view_alpha; /* pre-multiply for blending */ color.rgb *= color.a; @@ -283,11 +282,9 @@ main() /* Fast path for disabled color management */ if (c_input_is_premult) { - /* View alpha (opacity) */ - color *= alpha; + color *= view_alpha; } else { - /* View alpha (opacity) */ - color.a *= alpha; + color.a *= view_alpha; /* pre-multiply for blending */ color.rgb *= color.a; } diff --git a/libweston/renderer-gl/gl-shaders.c b/libweston/renderer-gl/gl-shaders.c index f46386f5..66850e71 100644 --- a/libweston/renderer-gl/gl-shaders.c +++ b/libweston/renderer-gl/gl-shaders.c @@ -57,7 +57,7 @@ struct gl_shader { GLuint vertex_shader, fragment_shader; GLint proj_uniform; GLint tex_uniforms[3]; - GLint alpha_uniform; + GLint view_alpha_uniform; GLint color_uniform; GLint color_pre_curve_lut_2d_uniform; GLint color_pre_curve_lut_scale_offset_uniform; @@ -283,7 +283,7 @@ gl_shader_create(struct gl_renderer *gr, shader->tex_uniforms[0] = glGetUniformLocation(shader->program, "tex"); shader->tex_uniforms[1] = glGetUniformLocation(shader->program, "tex1"); shader->tex_uniforms[2] = glGetUniformLocation(shader->program, "tex2"); - shader->alpha_uniform = glGetUniformLocation(shader->program, "alpha"); + shader->view_alpha_uniform = glGetUniformLocation(shader->program, "view_alpha"); shader->color_uniform = glGetUniformLocation(shader->program, "unicolor"); shader->color_pre_curve_lut_2d_uniform = @@ -515,7 +515,7 @@ gl_shader_load_config(struct gl_shader *shader, glUniformMatrix4fv(shader->proj_uniform, 1, GL_FALSE, sconf->projection.d); glUniform4fv(shader->color_uniform, 1, sconf->unicolor); - glUniform1f(shader->alpha_uniform, sconf->view_alpha); + glUniform1f(shader->view_alpha_uniform, sconf->view_alpha); in_tgt = gl_shader_texture_variant_get_target(sconf->req.variant); for (i = 0; i < GL_SHADER_INPUT_TEX_MAX; i++) { @@ -592,7 +592,7 @@ gl_renderer_use_program(struct gl_renderer *gr, shader = gr->fallback_shader; glUseProgram(shader->program); glUniform4fv(shader->color_uniform, 1, fallback_shader_color); - glUniform1f(shader->alpha_uniform, 1.0f); + glUniform1f(shader->view_alpha_uniform, 1.0f); return false; } From 932c374779716622b3b46447d25756934dbabc31 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 3 Jun 2022 14:01:50 +0300 Subject: [PATCH 409/609] gl-renderer: move undo-premult to color_pipeline() Now that we have the if-else ladder to call color_pipeline() only when necessary, and since only color_pipeline() needs undo-premult, move undo-premult into color_pipeline(). This is a small step towards improving code readability. Signed-off-by: Pekka Paalanen --- libweston/renderer-gl/fragment.glsl | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/libweston/renderer-gl/fragment.glsl b/libweston/renderer-gl/fragment.glsl index 908075e6..6271c9cd 100644 --- a/libweston/renderer-gl/fragment.glsl +++ b/libweston/renderer-gl/fragment.glsl @@ -249,6 +249,14 @@ color_mapping(vec3 color) vec4 color_pipeline(vec4 color) { + /* Ensure straight alpha */ + if (c_input_is_premult) { + if (color.a == 0.0) + color.rgb = vec3(0, 0, 0); + else + color.rgb *= 1.0 / color.a; + } + color.rgb = color_pre_curve(color.rgb); color.rgb = color_mapping(color.rgb); @@ -264,15 +272,7 @@ main() color = sample_input_texture(); if (c_need_color_pipeline) { - /* Ensure straight alpha */ - if (c_input_is_premult) { - if (color.a == 0.0) - color.rgb = vec3(0, 0, 0); - else - color.rgb *= 1.0 / color.a; - } - - color = color_pipeline(color); + color = color_pipeline(color); /* Produces straight alpha */ color.a *= view_alpha; From 57d32722a262edf52ab5f520d9e827bcc647653a Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 3 Jun 2022 14:15:01 +0300 Subject: [PATCH 410/609] gl-renderer: simplify main() in frag By moving the application of view_alpha after pre-multiplication we can simplify main() considerably. The cost is that for straight-alpha input or color_pipeline() we might be doing three multiplications more than before. However, a) the cost of running color_pipeline() probably dominates anyway, and b) to get straight-alpha input you have to use a future Wayland extension that probably won't be advertised without color management. So we keep the optimization for the simple case (no color management) while potentially incurring a small cost on the heavy case (with color management). Thanks to Pierre-Yves Mordred for the inspiration in https://gitlab.freedesktop.org/wayland/weston/-/merge_requests/889#note_1411774 Signed-off-by: Pekka Paalanen --- libweston/renderer-gl/fragment.glsl | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/libweston/renderer-gl/fragment.glsl b/libweston/renderer-gl/fragment.glsl index 6271c9cd..7de8d984 100644 --- a/libweston/renderer-gl/fragment.glsl +++ b/libweston/renderer-gl/fragment.glsl @@ -271,24 +271,14 @@ main() /* Electrical (non-linear) RGBA values, may be premult or not */ color = sample_input_texture(); - if (c_need_color_pipeline) { + if (c_need_color_pipeline) color = color_pipeline(color); /* Produces straight alpha */ - color.a *= view_alpha; - - /* pre-multiply for blending */ + /* Ensure pre-multiplied for blending */ + if (!c_input_is_premult || c_need_color_pipeline) color.rgb *= color.a; - } else { - /* Fast path for disabled color management */ - - if (c_input_is_premult) { - color *= view_alpha; - } else { - color.a *= view_alpha; - /* pre-multiply for blending */ - color.rgb *= color.a; - } - } + + color *= view_alpha; if (c_green_tint) color = vec4(0.0, 0.3, 0.0, 0.2) + color * 0.8; From cc69dc447ee9a9fd2fa6894e583fd66c21e958c4 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Mon, 20 Jun 2022 17:51:17 +0300 Subject: [PATCH 411/609] clients/window: Defer closing of window Instead of closing the window directly by calling close_handler() use a deferred task to do that instead. That way we avoid a potential invalid access on a link which was previously removed, due to the fact both window_destroy() and touch_handle_up() traverse the same list. This is an alternative to 841. Fixes: #607. Suggested-by: Pekka Paalanen Reported-by: He Yong Signed-off-by: Marius Vlad --- clients/window.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/clients/window.c b/clients/window.c index a0d988f4..60814a21 100644 --- a/clients/window.c +++ b/clients/window.c @@ -240,6 +240,7 @@ struct window { int redraw_needed; int redraw_task_scheduled; struct task redraw_task; + struct task close_task; int resize_needed; int custom; int focused; @@ -1429,13 +1430,23 @@ window_has_focus(struct window *window) return window->focused; } + +static void +close_task_run(struct task *task, uint32_t events) +{ + struct window *window = container_of(task, struct window, close_task); + window->close_handler(window->user_data); +} + static void window_close(struct window *window) { - if (window->close_handler) - window->close_handler(window->user_data); - else + if (window->close_handler && !window->close_task.run) { + window->close_task.run = close_task_run; + display_defer(window->display, &window->close_task); + } else { display_exit(window->display); + } } struct display * From d902088bfc9678bbd4a57eec64703869fe28d9db Mon Sep 17 00:00:00 2001 From: Hideyuki Nagase Date: Thu, 10 Mar 2022 12:34:17 -0600 Subject: [PATCH 412/609] xwayland: support minimizing Allow minimizing xwayland windows. Co-authored-by: Steve Pronovost Co-authored-by: Brenton DeGeer Signed-off-by: Hideyuki Nagase Signed-off-by: Steve Pronovost Signed-off-by: Brenton DeGeer --- libweston-desktop/xwayland.c | 8 +++++++ shared/xcb-xwayland.c | 1 + shared/xcb-xwayland.h | 1 + xwayland/window-manager.c | 33 ++++++++++++++++++++++++++ xwayland/xwayland-internal-interface.h | 1 + 5 files changed, 44 insertions(+) diff --git a/libweston-desktop/xwayland.c b/libweston-desktop/xwayland.c index ae6a92e4..f40bb5db 100644 --- a/libweston-desktop/xwayland.c +++ b/libweston-desktop/xwayland.c @@ -400,6 +400,13 @@ set_maximized(struct weston_desktop_xwayland_surface *surface) surface->surface, true); } +static void +set_minimized(struct weston_desktop_xwayland_surface *surface) +{ + weston_desktop_api_minimized_requested(surface->desktop, + surface->surface); +} + static void set_pid(struct weston_desktop_xwayland_surface *surface, pid_t pid) { @@ -419,6 +426,7 @@ static const struct weston_desktop_xwayland_interface weston_desktop_xwayland_in .set_title = set_title, .set_window_geometry = set_window_geometry, .set_maximized = set_maximized, + .set_minimized = set_minimized, .set_pid = set_pid, }; diff --git a/shared/xcb-xwayland.c b/shared/xcb-xwayland.c index fe801198..737c82a5 100644 --- a/shared/xcb-xwayland.c +++ b/shared/xcb-xwayland.c @@ -77,6 +77,7 @@ x11_get_atoms(xcb_connection_t *connection, struct atom_x11 *atom) { "WM_STATE", F(wm_state) }, { "WM_S0", F(wm_s0) }, { "WM_CLIENT_MACHINE", F(wm_client_machine) }, + { "WM_CHANGE_STATE", F(wm_change_state) }, { "_NET_WM_CM_S0", F(net_wm_cm_s0) }, { "_NET_WM_NAME", F(net_wm_name) }, { "_NET_WM_PID", F(net_wm_pid) }, diff --git a/shared/xcb-xwayland.h b/shared/xcb-xwayland.h index f4f03954..7b233ede 100644 --- a/shared/xcb-xwayland.h +++ b/shared/xcb-xwayland.h @@ -39,6 +39,7 @@ struct atom_x11 { xcb_atom_t wm_state; xcb_atom_t wm_s0; xcb_atom_t wm_client_machine; + xcb_atom_t wm_change_state; xcb_atom_t net_wm_cm_s0; xcb_atom_t net_wm_name; xcb_atom_t net_wm_pid; diff --git a/xwayland/window-manager.c b/xwayland/window-manager.c index ff9eddf7..c75e0102 100644 --- a/xwayland/window-manager.c +++ b/xwayland/window-manager.c @@ -1057,6 +1057,9 @@ weston_wm_window_create_frame(struct weston_wm_window *window) if (window->decorate & MWM_DECOR_MAXIMIZE) buttons |= FRAME_BUTTON_MAXIMIZE; + if (window->decorate & MWM_DECOR_MINIMIZE) + buttons |= FRAME_BUTTON_MINIMIZE; + window->frame = frame_create(window->wm->theme, window->width, window->height, buttons, window->name, NULL); @@ -1803,6 +1806,27 @@ weston_wm_window_handle_state(struct weston_wm_window *window, } } +static void +weston_wm_window_handle_iconic_state(struct weston_wm_window *window, + xcb_client_message_event_t *client_message) +{ + struct weston_wm *wm = window->wm; + const struct weston_desktop_xwayland_interface *xwayland_interface = + wm->server->compositor->xwayland_interface; + uint32_t iconic_state; + + if (!window->shsurf) + return; + + iconic_state = client_message->data.data32[0]; + + if (iconic_state == ICCCM_ICONIC_STATE) { + window->saved_height = window->height; + window->saved_width = window->width; + xwayland_interface->set_minimized(window->shsurf); + } +} + static void surface_destroy(struct wl_listener *listener, void *data) { @@ -1880,6 +1904,8 @@ weston_wm_handle_client_message(struct weston_wm *wm, weston_wm_window_handle_state(window, client_message); else if (client_message->type == wm->atom.wl_surface_id) weston_wm_window_handle_surface_id(window, client_message); + else if (client_message->type == wm->atom.wm_change_state) + weston_wm_window_handle_iconic_state(window, client_message); } enum cursor_type { @@ -2163,6 +2189,13 @@ weston_wm_handle_button(struct weston_wm *wm, xcb_generic_event_t *event) } frame_status_clear(window->frame, FRAME_STATUS_MAXIMIZE); } + + if (frame_status(window->frame) & FRAME_STATUS_MINIMIZE) { + window->saved_width = window->width; + window->saved_height = window->height; + xwayland_interface->set_minimized(window->shsurf); + frame_status_clear(window->frame, FRAME_STATUS_MINIMIZE); + } } static void diff --git a/xwayland/xwayland-internal-interface.h b/xwayland/xwayland-internal-interface.h index c7dfd19b..a97d13bc 100644 --- a/xwayland/xwayland-internal-interface.h +++ b/xwayland/xwayland-internal-interface.h @@ -59,6 +59,7 @@ struct weston_desktop_xwayland_interface { int32_t x, int32_t y, int32_t width, int32_t height); void (*set_maximized)(struct weston_desktop_xwayland_surface *shsurf); + void (*set_minimized)(struct weston_desktop_xwayland_surface *shsurf); void (*set_pid)(struct weston_desktop_xwayland_surface *shsurf, pid_t pid); }; From 769e4376c6c5af7a330d0bd0c0c846dffdc63e3a Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Mon, 20 Jun 2022 12:26:29 -0500 Subject: [PATCH 413/609] shared/frame: Provide a function to get decoration sizes and use it We need these values to calculate frame extents to properly set _NET_FRAME_EXTENTS, but we don't want to calculate them twice. Break out these bits from frame_resize_inside, and update it to use the new function. Signed-off-by: Derek Foreman --- shared/cairo-util.h | 4 ++++ shared/frame.c | 41 +++++++++++++++++++++++++++-------------- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/shared/cairo-util.h b/shared/cairo-util.h index 6fd11f6b..17f7b4f3 100644 --- a/shared/cairo-util.h +++ b/shared/cairo-util.h @@ -161,6 +161,10 @@ frame_width(struct frame *frame); int32_t frame_height(struct frame *frame); +void +frame_decoration_sizes(struct frame *frame, int32_t *top, int32_t *bottom, + int32_t *left, int32_t *right); + void frame_interior(struct frame *frame, int32_t *x, int32_t *y, int32_t *width, int32_t *height); diff --git a/shared/frame.c b/shared/frame.c index e8a5cad6..cf58d66b 100644 --- a/shared/frame.c +++ b/shared/frame.c @@ -493,27 +493,40 @@ frame_resize(struct frame *frame, int32_t width, int32_t height) } void -frame_resize_inside(struct frame *frame, int32_t width, int32_t height) +frame_decoration_sizes(struct frame *frame, int32_t *top, int32_t *bottom, + int32_t *left, int32_t *right) { struct theme *t = frame->theme; - int decoration_width, decoration_height, titlebar_height; + /* Top may have a titlebar */ if (frame->title || !wl_list_empty(&frame->buttons)) - titlebar_height = t->titlebar_height; + *top = t->titlebar_height; else - titlebar_height = t->width; + *top = t->width; - if (frame->flags & FRAME_FLAG_MAXIMIZED) { - decoration_width = t->width * 2; - decoration_height = t->width + titlebar_height; - } else { - decoration_width = (t->width + t->margin) * 2; - decoration_height = t->width + - titlebar_height + t->margin * 2; - } + /* All other sides have the basic frame thickness */ + *bottom = t->width; + *right = t->width; + *left = t->width; + + if (frame->flags & FRAME_FLAG_MAXIMIZED) + return; + + /* Not maximized, add shadows */ + *top += t->margin; + *bottom += t->margin; + *left += t->margin; + *right += t->margin; +} + +void +frame_resize_inside(struct frame *frame, int32_t width, int32_t height) +{ + int32_t top, bottom, left, right; - frame_resize(frame, width + decoration_width, - height + decoration_height); + frame_decoration_sizes(frame, &top, &bottom, &left, &right); + frame_resize(frame, width + left + right, + height + top + bottom); } int32_t From af5161870831ccf576c31afe26a4b471f4fa8a1e Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Wed, 22 Jun 2022 12:05:23 -0500 Subject: [PATCH 414/609] xwayland/window-manager: Add support for _NET_FRAME_EXTENTS https://specifications.freedesktop.org/wm-spec/1.4/ar01s05.html says "The Window Manager MUST set _NET_FRAME_EXTENTS to the extents of the window's frame", so this is probably something we should be doing. Some programs (such as some versions of Firefox) expect this to be present, and will render popups in wrong locations if it's not. Signed-off-by: Derek Foreman --- shared/xcb-xwayland.c | 1 + shared/xcb-xwayland.h | 1 + xwayland/window-manager.c | 44 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/shared/xcb-xwayland.c b/shared/xcb-xwayland.c index 737c82a5..d72a15a9 100644 --- a/shared/xcb-xwayland.c +++ b/shared/xcb-xwayland.c @@ -78,6 +78,7 @@ x11_get_atoms(xcb_connection_t *connection, struct atom_x11 *atom) { "WM_S0", F(wm_s0) }, { "WM_CLIENT_MACHINE", F(wm_client_machine) }, { "WM_CHANGE_STATE", F(wm_change_state) }, + { "_NET_FRAME_EXTENTS", F(net_frame_extents) }, { "_NET_WM_CM_S0", F(net_wm_cm_s0) }, { "_NET_WM_NAME", F(net_wm_name) }, { "_NET_WM_PID", F(net_wm_pid) }, diff --git a/shared/xcb-xwayland.h b/shared/xcb-xwayland.h index 7b233ede..8157240c 100644 --- a/shared/xcb-xwayland.h +++ b/shared/xcb-xwayland.h @@ -40,6 +40,7 @@ struct atom_x11 { xcb_atom_t wm_s0; xcb_atom_t wm_client_machine; xcb_atom_t wm_change_state; + xcb_atom_t net_frame_extents; xcb_atom_t net_wm_cm_s0; xcb_atom_t net_wm_name; xcb_atom_t net_wm_pid; diff --git a/xwayland/window-manager.c b/xwayland/window-manager.c index c75e0102..0833b617 100644 --- a/xwayland/window-manager.c +++ b/xwayland/window-manager.c @@ -174,6 +174,10 @@ struct weston_wm_window { struct wm_size_hints size_hints; struct motif_wm_hints motif_hints; struct wl_list link; + int decor_top; + int decor_bottom; + int decor_left; + int decor_right; }; static void @@ -1022,6 +1026,38 @@ weston_wm_window_set_wm_state(struct weston_wm_window *window, int32_t state) 2, property); } +static void +weston_wm_window_set_net_frame_extents(struct weston_wm_window *window) +{ + struct weston_wm *wm = window->wm; + uint32_t property[4]; + int top = 0, bottom = 0, left = 0, right = 0; + + if (!window->fullscreen) + frame_decoration_sizes(window->frame, &top, &bottom, &left, &right); + + if (window->decor_top == top && window->decor_bottom == bottom && + window->decor_left == left && window->decor_right == right) + return; + + window->decor_top = top; + window->decor_bottom = bottom; + window->decor_left = left; + window->decor_right = right; + + property[0] = left; + property[1] = right; + property[2] = top; + property[3] = bottom; + xcb_change_property(wm->conn, + XCB_PROP_MODE_REPLACE, + window->id, + wm->atom.net_frame_extents, + XCB_ATOM_CARDINAL, + 32, /* format */ + 4, property); +} + static void weston_wm_window_set_net_wm_state(struct weston_wm_window *window) { @@ -1362,6 +1398,7 @@ weston_wm_window_do_repaint(void *data) weston_wm_window_read_properties(window); weston_wm_window_draw_decoration(window); + weston_wm_window_set_net_frame_extents(window); weston_wm_window_set_pending_state(window); weston_wm_window_set_allow_commits(window, true); } @@ -1498,6 +1535,10 @@ weston_wm_window_create(struct weston_wm *wm, window->pos_dirty = false; window->map_request_x = INT_MIN; /* out of range for valid positions */ window->map_request_y = INT_MIN; /* out of range for valid positions */ + window->decor_top = -1; + window->decor_bottom = -1; + window->decor_left = -1; + window->decor_right = -1; weston_output_weak_ref_init(&window->legacy_fullscreen_output); geometry_reply = xcb_get_geometry_reply(wm->conn, geometry_cookie, NULL); @@ -2507,7 +2548,7 @@ weston_wm_create(struct weston_xserver *wxs, int fd) struct wl_event_loop *loop; xcb_screen_iterator_t s; uint32_t values[1]; - xcb_atom_t supported[6]; + xcb_atom_t supported[7]; wm = zalloc(sizeof *wm); if (wm == NULL) @@ -2561,6 +2602,7 @@ weston_wm_create(struct weston_xserver *wxs, int fd) supported[3] = wm->atom.net_wm_state_maximized_vert; supported[4] = wm->atom.net_wm_state_maximized_horz; supported[5] = wm->atom.net_active_window; + supported[6] = wm->atom.net_frame_extents; xcb_change_property(wm->conn, XCB_PROP_MODE_REPLACE, wm->screen->root, From 2929b6c483fc0f6a6f98656680c1ecd2d0f41787 Mon Sep 17 00:00:00 2001 From: Michael Olbrich Date: Mon, 13 Jun 2022 17:22:22 +0200 Subject: [PATCH 415/609] backend-drm: check that outputs are in fact ours This is another followup to ffc011d6a30d5e772e4f849abc5c60faf3ecb91a ("backend-drm: check that outputs and heads are in fact ours") which missed some places. Signed-off-by: Michael Olbrich --- libweston/backend-drm/kms.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libweston/backend-drm/kms.c b/libweston/backend-drm/kms.c index f6629a5d..54010b97 100644 --- a/libweston/backend-drm/kms.c +++ b/libweston/backend-drm/kms.c @@ -1103,11 +1103,13 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state, wl_list_for_each(head_base, &b->compositor->head_list, compositor_link) { struct drm_property_info *info; + head = to_drm_head(head_base); + if (!head) + continue; if (weston_head_is_enabled(head_base)) continue; - head = to_drm_head(head_base); connector_id = head->connector.connector_id; if (head->connector.device != device) continue; From 59a72dcf638685a0a0276b8178d01e7d75064905 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Thu, 23 Jun 2022 16:35:50 +0300 Subject: [PATCH 416/609] shared/xcb-xwayland: Add missing atoms Particularly important was _XWAYLAND_ALLOW_COMMITS atom which caused some annoying flicker when resizing or hoovering over buttons. This was introduced with 'shared/xcb-xwayland: Split into common helpers' and somehow I missed those atoms. Fixes 49d6532254fa27e3a28a3fb26d0b9d253d002125 Signed-off-by: Marius Vlad --- shared/xcb-xwayland.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/shared/xcb-xwayland.c b/shared/xcb-xwayland.c index d72a15a9..bb207b90 100644 --- a/shared/xcb-xwayland.c +++ b/shared/xcb-xwayland.c @@ -109,6 +109,7 @@ x11_get_atoms(xcb_connection_t *connection, struct atom_x11 *atom) { "_NET_WM_MOVERESIZE", F(net_wm_moveresize) }, { "_NET_SUPPORTING_WM_CHECK", F(net_supporting_wm_check) }, + { "_NET_SUPPORTED", F(net_supported) }, { "_NET_ACTIVE_WINDOW", F(net_active_window) }, { "_MOTIF_WM_HINTS", F(motif_wm_hints) }, @@ -125,6 +126,18 @@ x11_get_atoms(xcb_connection_t *connection, struct atom_x11 *atom) { "TEXT", F(text) }, { "STRING", F(string) }, { "WINDOW", F(window) }, + { "text/plain;charset=utf-8", F(text_plain_utf8) }, + { "text/plain", F(text_plain) }, + { "XdndSelection", F(xdnd_selection) }, + { "XdndAware", F(xdnd_aware) }, + { "XdndEnter", F(xdnd_enter) }, + { "XdndLeave", F(xdnd_leave) }, + { "XdndDrop", F(xdnd_drop) }, + { "XdndStatus", F(xdnd_status) }, + { "XdndFinished", F(xdnd_finished) }, + { "XdndTypeList", F(xdnd_type_list) }, + { "XdndActionCopy", F(xdnd_action_copy) }, + { "_XWAYLAND_ALLOW_COMMITS", F(allow_commits) }, { "WL_SURFACE_ID", F(wl_surface_id) }, }; From 48e8c158eae726be1c1027404021cfd97f9aaed4 Mon Sep 17 00:00:00 2001 From: Michael Olbrich Date: Mon, 13 Jun 2022 15:38:13 +0200 Subject: [PATCH 417/609] compositor: only reflow the outputs if the shell did not move them weston_compositor_reflow_outputs() assumes that all output are positioned from left to right with no gaps in the same order in which they where created. If the shell moves an output with weston_output_move() then this assumption is no longer true. So stop reflowing the outputs in the case. The shell is now responsible for positioning all outputs as needed. Signed-off-by: Michael Olbrich --- include/libweston/libweston.h | 1 + libweston/compositor.c | 22 +++++++++++++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index dbf80b15..6e5ebbca 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -1263,6 +1263,7 @@ struct weston_compositor { struct wl_list plugin_api_list; /* struct weston_plugin_api::link */ uint32_t output_id_pool; + bool output_flow_dirty; struct xkb_rule_names xkb_names; struct xkb_context *xkb_context; diff --git a/libweston/compositor.c b/libweston/compositor.c index 5448e8ac..6282901a 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -6253,6 +6253,9 @@ weston_head_get_destroy_listener(struct weston_head *head, return wl_signal_get(&head->destroy_signal, notify); } +static void +weston_output_set_position(struct weston_output *output, int x, int y); + /* Move other outputs when one is resized so the space remains contiguous. */ static void weston_compositor_reflow_outputs(struct weston_compositor *compositor, @@ -6261,6 +6264,9 @@ weston_compositor_reflow_outputs(struct weston_compositor *compositor, struct weston_output *output; bool start_resizing = false; + if (compositor->output_flow_dirty) + return; + if (!delta_width) return; @@ -6271,7 +6277,7 @@ weston_compositor_reflow_outputs(struct weston_compositor *compositor, } if (start_resizing) { - weston_output_move(output, output->x + delta_width, output->y); + weston_output_set_position(output, output->x + delta_width, output->y); output->dirty = 1; } } @@ -6377,8 +6383,8 @@ weston_output_init_geometry(struct weston_output *output, int x, int y) /** * \ingroup output */ -WL_EXPORT void -weston_output_move(struct weston_output *output, int x, int y) +static void +weston_output_set_position(struct weston_output *output, int x, int y) { struct weston_head *head; struct wl_resource *resource; @@ -6424,6 +6430,16 @@ weston_output_move(struct weston_output *output, int x, int y) } } +/** + * \ingroup output + */ +WL_EXPORT void +weston_output_move(struct weston_output *output, int x, int y) +{ + output->compositor->output_flow_dirty = true; + weston_output_set_position(output, x, y); +} + /** Signal that a pending output is taken into use. * * Removes the output from the pending list and adds it to the compositor's From 61d8238874d9c0ad6530c6826209f87c332627eb Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Wed, 22 Jun 2022 16:16:02 +0100 Subject: [PATCH 418/609] desktop-shell: Remove multiple workspace support It's not the most code ever, but it does make desktop-shell somewhat more complicated for questionable (i.e. no) end-user benefit. When desktop-shell is back in more healthy shape it could potentially be reintroduced, but for now it's just making it more difficult to reason about desktop-shell and fix it. Signed-off-by: Daniel Stone --- desktop-shell/shell.c | 568 +--------------------------------------- desktop-shell/shell.h | 17 +- man/weston-bindings.man | 15 -- man/weston.ini.man | 5 - 4 files changed, 8 insertions(+), 597 deletions(-) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 33bbfc16..c5afd8e1 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -126,8 +126,6 @@ struct shell_surface { struct weston_curtain *black_view; } fullscreen; - struct weston_transform workspace_transform; - struct weston_output *fullscreen_output; struct weston_output *output; struct wl_listener output_destroy_listener; @@ -516,9 +514,6 @@ shell_configuration(struct desktop_shell *shell) weston_config_section_get_string(section, "focus-animation", &s, "none"); shell->focus_animation_type = get_animation_type(s); free(s); - weston_config_section_get_uint(section, "num-workspaces", - &shell->workspaces.num, - DEFAULT_NUM_WORKSPACES); } static int @@ -534,15 +529,6 @@ focus_surface_committed(struct weston_surface *es, int32_t sx, int32_t sy) { } -static struct focus_surface * -get_focus_surface(struct weston_surface *surface) -{ - if (surface->committed == focus_surface_committed) - return surface->committed_private; - else - return NULL; -} - static bool is_focus_view (struct weston_view *view) { @@ -574,8 +560,6 @@ create_focus_surface(struct weston_compositor *ec, weston_view_set_output(fsurf->curtain->view, output); fsurf->curtain->view->is_mapped = true; - wl_list_init(&fsurf->workspace_transform.link); - return fsurf; } @@ -768,21 +752,6 @@ restore_focus_state(struct desktop_shell *shell, struct workspace *ws) } } -static void -replace_focus_state(struct desktop_shell *shell, struct workspace *ws, - struct weston_seat *seat) -{ - struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat); - struct focus_state *state; - - wl_list_for_each(state, &ws->focus_list, link) { - if (state->seat == seat) { - focus_state_set_focus(state, keyboard->focus); - return; - } - } -} - static void drop_focus_state(struct desktop_shell *shell, struct workspace *ws, struct weston_surface *surface) @@ -876,7 +845,6 @@ workspace_destroy(struct workspace *ws) focus_surface_destroy(ws->fsurf_back); desktop_shell_destroy_layer(&ws->layer); - free(ws); } static void @@ -893,14 +861,13 @@ seat_destroyed(struct wl_listener *listener, void *data) wl_list_remove(&state->link); } -static struct workspace * +static void workspace_create(struct desktop_shell *shell) { - struct workspace *ws = malloc(sizeof *ws); - if (ws == NULL) - return NULL; + struct workspace *ws = &shell->workspace; weston_layer_init(&ws->layer, shell->compositor); + weston_layer_set_position(&ws->layer, WESTON_LAYER_POSITION_NORMAL); wl_list_init(&ws->focus_list); wl_list_init(&ws->seat_destroyed_listener.link); @@ -908,343 +875,12 @@ workspace_create(struct desktop_shell *shell) ws->fsurf_front = NULL; ws->fsurf_back = NULL; ws->focus_animation = NULL; - - return ws; -} - -static int -workspace_is_empty(struct workspace *ws) -{ - return wl_list_empty(&ws->layer.view_list.link); -} - -static struct workspace * -get_workspace(struct desktop_shell *shell, unsigned int index) -{ - struct workspace **pws = shell->workspaces.array.data; - assert(index < shell->workspaces.num); - pws += index; - return *pws; } struct workspace * get_current_workspace(struct desktop_shell *shell) { - return get_workspace(shell, shell->workspaces.current); -} - -static void -activate_workspace(struct desktop_shell *shell, unsigned int index) -{ - struct workspace *ws; - - ws = get_workspace(shell, index); - weston_layer_set_position(&ws->layer, WESTON_LAYER_POSITION_NORMAL); - - shell->workspaces.current = index; -} - -static unsigned int -get_output_height(struct weston_output *output) -{ - return abs(output->region.extents.y1 - output->region.extents.y2); -} - -static struct weston_transform * -view_get_transform(struct weston_view *view) -{ - struct focus_surface *fsurf = NULL; - struct shell_surface *shsurf = NULL; - - if (is_focus_view(view)) { - fsurf = get_focus_surface(view->surface); - return &fsurf->workspace_transform; - } - - shsurf = get_shell_surface(view->surface); - if (shsurf) - return &shsurf->workspace_transform; - - return NULL; -} - -static void -view_translate(struct workspace *ws, struct weston_view *view, double d) -{ - struct weston_transform *transform = view_get_transform(view); - - if (!transform) - return; - - if (wl_list_empty(&transform->link)) - wl_list_insert(view->geometry.transformation_list.prev, - &transform->link); - - weston_matrix_init(&transform->matrix); - weston_matrix_translate(&transform->matrix, - 0.0, d, 0.0); - weston_view_geometry_dirty(view); -} - -static void -workspace_translate_out(struct workspace *ws, double fraction) -{ - struct weston_view *view; - unsigned int height; - double d; - - wl_list_for_each(view, &ws->layer.view_list.link, layer_link.link) { - height = get_output_height(view->surface->output); - d = height * fraction; - - view_translate(ws, view, d); - } -} - -static void -workspace_translate_in(struct workspace *ws, double fraction) -{ - struct weston_view *view; - unsigned int height; - double d; - - wl_list_for_each(view, &ws->layer.view_list.link, layer_link.link) { - height = get_output_height(view->surface->output); - - if (fraction > 0) - d = -(height - height * fraction); - else - d = height + height * fraction; - - view_translate(ws, view, d); - } -} - -static void -reverse_workspace_change_animation(struct desktop_shell *shell, - unsigned int index, - struct workspace *from, - struct workspace *to) -{ - shell->workspaces.current = index; - - shell->workspaces.anim_to = to; - shell->workspaces.anim_from = from; - shell->workspaces.anim_dir = -1 * shell->workspaces.anim_dir; - shell->workspaces.anim_timestamp = (struct timespec) { 0 }; - - weston_layer_set_position(&to->layer, WESTON_LAYER_POSITION_NORMAL); - weston_layer_set_position(&from->layer, WESTON_LAYER_POSITION_NORMAL - 1); - - weston_compositor_schedule_repaint(shell->compositor); -} - -static void -workspace_deactivate_transforms(struct workspace *ws) -{ - struct weston_view *view; - struct weston_transform *transform; - - wl_list_for_each(view, &ws->layer.view_list.link, layer_link.link) { - transform = view_get_transform(view); - if (!transform) - continue; - - if (!wl_list_empty(&transform->link)) { - wl_list_remove(&transform->link); - wl_list_init(&transform->link); - } - weston_view_geometry_dirty(view); - } -} - -static void -finish_workspace_change_animation(struct desktop_shell *shell, - struct workspace *from, - struct workspace *to) -{ - struct weston_view *view; - - weston_compositor_schedule_repaint(shell->compositor); - - /* Views that extend past the bottom of the output are still - * visible after the workspace animation ends but before its layer - * is hidden. In that case, we need to damage below those views so - * that the screen is properly repainted. */ - wl_list_for_each(view, &from->layer.view_list.link, layer_link.link) - weston_view_damage_below(view); - - wl_list_remove(&shell->workspaces.animation.link); - workspace_deactivate_transforms(from); - workspace_deactivate_transforms(to); - shell->workspaces.anim_to = NULL; - - weston_layer_unset_position(&shell->workspaces.anim_from->layer); -} - -static void -animate_workspace_change_frame(struct weston_animation *animation, - struct weston_output *output, - const struct timespec *time) -{ - struct desktop_shell *shell = - container_of(animation, struct desktop_shell, - workspaces.animation); - struct workspace *from = shell->workspaces.anim_from; - struct workspace *to = shell->workspaces.anim_to; - int64_t t; - double x, y; - - if (workspace_is_empty(from) && workspace_is_empty(to)) { - finish_workspace_change_animation(shell, from, to); - return; - } - - if (timespec_is_zero(&shell->workspaces.anim_timestamp)) { - if (shell->workspaces.anim_current == 0.0) - shell->workspaces.anim_timestamp = *time; - else - timespec_add_msec(&shell->workspaces.anim_timestamp, - time, - /* Inverse of movement function 'y' below. */ - -(asin(1.0 - shell->workspaces.anim_current) * - DEFAULT_WORKSPACE_CHANGE_ANIMATION_LENGTH * - M_2_PI)); - } - - t = timespec_sub_to_msec(time, &shell->workspaces.anim_timestamp); - - /* - * x = [0, π/2] - * y(x) = sin(x) - */ - x = t * (1.0/DEFAULT_WORKSPACE_CHANGE_ANIMATION_LENGTH) * M_PI_2; - y = sin(x); - - if (t < DEFAULT_WORKSPACE_CHANGE_ANIMATION_LENGTH) { - weston_compositor_schedule_repaint(shell->compositor); - - workspace_translate_out(from, shell->workspaces.anim_dir * y); - workspace_translate_in(to, shell->workspaces.anim_dir * y); - shell->workspaces.anim_current = y; - - weston_compositor_schedule_repaint(shell->compositor); - } - else - finish_workspace_change_animation(shell, from, to); -} - -static void -animate_workspace_change(struct desktop_shell *shell, - unsigned int index, - struct workspace *from, - struct workspace *to) -{ - struct weston_output *output; - - int dir; - - if (index > shell->workspaces.current) - dir = -1; - else - dir = 1; - - shell->workspaces.current = index; - - shell->workspaces.anim_dir = dir; - shell->workspaces.anim_from = from; - shell->workspaces.anim_to = to; - shell->workspaces.anim_current = 0.0; - shell->workspaces.anim_timestamp = (struct timespec) { 0 }; - - output = container_of(shell->compositor->output_list.next, - struct weston_output, link); - wl_list_insert(&output->animation_list, - &shell->workspaces.animation.link); - - weston_layer_set_position(&to->layer, WESTON_LAYER_POSITION_NORMAL); - weston_layer_set_position(&from->layer, WESTON_LAYER_POSITION_NORMAL - 1); - - workspace_translate_in(to, 0); - - restore_focus_state(shell, to); - - weston_compositor_schedule_repaint(shell->compositor); -} - -static void -update_workspace(struct desktop_shell *shell, unsigned int index, - struct workspace *from, struct workspace *to) -{ - shell->workspaces.current = index; - weston_layer_set_position(&to->layer, WESTON_LAYER_POSITION_NORMAL); - weston_layer_unset_position(&from->layer); -} - -static void -change_workspace(struct desktop_shell *shell, unsigned int index) -{ - struct workspace *from; - struct workspace *to; - struct focus_state *state; - - if (index == shell->workspaces.current) - return; - - /* Don't change workspace when there is any fullscreen surfaces. */ - if (!wl_list_empty(&shell->fullscreen_layer.view_list.link)) - return; - - from = get_current_workspace(shell); - to = get_workspace(shell, index); - - if (shell->workspaces.anim_from == to && - shell->workspaces.anim_to == from) { - restore_focus_state(shell, to); - reverse_workspace_change_animation(shell, index, from, to); - return; - } - - if (shell->workspaces.anim_to != NULL) - finish_workspace_change_animation(shell, - shell->workspaces.anim_from, - shell->workspaces.anim_to); - - restore_focus_state(shell, to); - - if (shell->focus_animation_type != ANIMATION_NONE) { - wl_list_for_each(state, &from->focus_list, link) - if (state->keyboard_focus) - animate_focus_change(shell, from, - get_default_view(state->keyboard_focus), NULL); - - wl_list_for_each(state, &to->focus_list, link) - if (state->keyboard_focus) - animate_focus_change(shell, to, - NULL, get_default_view(state->keyboard_focus)); - } - - if (workspace_is_empty(to) && workspace_is_empty(from)) - update_workspace(shell, index, from, to); - else - animate_workspace_change(shell, index, from, to); -} - -static bool -workspace_has_only(struct workspace *ws, struct weston_surface *surface) -{ - struct wl_list *list = &ws->layer.view_list.link; - struct wl_list *e; - - if (wl_list_empty(list)) - return false; - - e = list->next; - - if (e->next != list) - return false; - - return container_of(e, struct weston_view, layer_link.link)->surface == surface; + return &shell->workspace; } static void @@ -1267,68 +903,6 @@ surface_keyboard_focus_lost(struct weston_surface *surface) } } -static void -take_surface_to_workspace_by_seat(struct desktop_shell *shell, - struct weston_seat *seat, - unsigned int index) -{ - struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat); - struct weston_surface *surface; - struct weston_view *view; - struct shell_surface *shsurf; - struct workspace *from; - struct workspace *to; - struct focus_state *state; - - surface = weston_surface_get_main_surface(keyboard->focus); - view = get_default_view(surface); - if (view == NULL || - index == shell->workspaces.current || - is_focus_view(view)) - return; - - from = get_current_workspace(shell); - to = get_workspace(shell, index); - - weston_layer_entry_remove(&view->layer_link); - weston_layer_entry_insert(&to->layer.view_list, &view->layer_link); - - shsurf = get_shell_surface(surface); - if (shsurf != NULL) - shell_surface_update_child_surface_layers(shsurf); - - replace_focus_state(shell, to, seat); - drop_focus_state(shell, from, surface); - - if (shell->workspaces.anim_from == to && - shell->workspaces.anim_to == from) { - reverse_workspace_change_animation(shell, index, from, to); - - return; - } - - if (shell->workspaces.anim_to != NULL) - finish_workspace_change_animation(shell, - shell->workspaces.anim_from, - shell->workspaces.anim_to); - - if (workspace_is_empty(from) && - workspace_has_only(to, surface)) - update_workspace(shell, index, from, to); - else { - if (shsurf != NULL && - wl_list_empty(&shsurf->workspace_transform.link)) - wl_list_insert(&shell->workspaces.anim_sticky_list, - &shsurf->workspace_transform.link); - - animate_workspace_change(shell, index, from, to); - } - - state = ensure_focus_state(shell, seat); - if (state != NULL) - focus_state_set_focus(state, surface); -} - static void touch_move_grab_down(struct weston_touch_grab *grab, const struct timespec *time, @@ -2306,8 +1880,6 @@ desktop_surface_added(struct weston_desktop_surface *desktop_surface, wl_list_init(&shsurf->rotation.transform.link); weston_matrix_init(&shsurf->rotation.rotation); - wl_list_init(&shsurf->workspace_transform.link); - /* * initialize list as well as link. The latter allows to use * wl_list_remove() even when this surface is not in another list. @@ -4570,86 +4142,6 @@ force_kill_binding(struct weston_keyboard *keyboard, kill(pid, SIGKILL); } -static void -workspace_up_binding(struct weston_keyboard *keyboard, - const struct timespec *time, uint32_t key, void *data) -{ - struct desktop_shell *shell = data; - unsigned int new_index = shell->workspaces.current; - - if (shell->locked) - return; - if (new_index != 0) - new_index--; - - change_workspace(shell, new_index); -} - -static void -workspace_down_binding(struct weston_keyboard *keyboard, - const struct timespec *time, uint32_t key, void *data) -{ - struct desktop_shell *shell = data; - unsigned int new_index = shell->workspaces.current; - - if (shell->locked) - return; - if (new_index < shell->workspaces.num - 1) - new_index++; - - change_workspace(shell, new_index); -} - -static void -workspace_f_binding(struct weston_keyboard *keyboard, - const struct timespec *time, uint32_t key, void *data) -{ - struct desktop_shell *shell = data; - unsigned int new_index; - - if (shell->locked) - return; - new_index = key - KEY_F1; - if (new_index >= shell->workspaces.num) - new_index = shell->workspaces.num - 1; - - change_workspace(shell, new_index); -} - -static void -workspace_move_surface_up_binding(struct weston_keyboard *keyboard, - const struct timespec *time, uint32_t key, - void *data) -{ - struct desktop_shell *shell = data; - unsigned int new_index = shell->workspaces.current; - - if (shell->locked) - return; - - if (new_index != 0) - new_index--; - - take_surface_to_workspace_by_seat(shell, keyboard->seat, new_index); -} - -static void -workspace_move_surface_down_binding(struct weston_keyboard *keyboard, - const struct timespec *time, uint32_t key, - void *data) -{ - struct desktop_shell *shell = data; - unsigned int new_index = shell->workspaces.current; - - if (shell->locked) - return; - - if (new_index < shell->workspaces.num - 1) - new_index++; - - take_surface_to_workspace_by_seat(shell, keyboard->seat, new_index); -} - static void shell_reposition_view_on_output_change(struct weston_view *view) { @@ -4703,16 +4195,12 @@ void shell_for_each_layer(struct desktop_shell *shell, shell_for_each_layer_func_t func, void *data) { - struct workspace **ws; - func(shell, &shell->fullscreen_layer, data); func(shell, &shell->panel_layer, data); func(shell, &shell->background_layer, data); func(shell, &shell->lock_layer, data); func(shell, &shell->input_panel_layer, data); - - wl_array_for_each(ws, &shell->workspaces.array) - func(shell, &(*ws)->layer, data); + func(shell, &shell->workspace.layer, data); } static void @@ -4917,7 +4405,6 @@ shell_destroy(struct wl_listener *listener, void *data) { struct desktop_shell *shell = container_of(listener, struct desktop_shell, destroy_listener); - struct workspace **ws; struct shell_output *shell_output, *tmp; struct shell_seat *shseat, *shseat_next; @@ -4950,9 +4437,7 @@ shell_destroy(struct wl_listener *listener, void *data) weston_desktop_destroy(shell->desktop); - wl_array_for_each(ws, &shell->workspaces.array) - workspace_destroy(*ws); - wl_array_release(&shell->workspaces.array); + workspace_destroy(&shell->workspace); desktop_shell_destroy_layer(&shell->panel_layer); desktop_shell_destroy_layer(&shell->background_layer); @@ -4969,7 +4454,6 @@ static void shell_add_bindings(struct weston_compositor *ec, struct desktop_shell *shell) { uint32_t mod; - int i, num_workspace_bindings; if (shell->allow_zap) weston_compositor_add_key_binding(ec, KEY_BACKSPACE, @@ -5026,27 +4510,6 @@ shell_add_bindings(struct weston_compositor *ec, struct desktop_shell *shell) ec); weston_compositor_add_key_binding(ec, KEY_K, mod, force_kill_binding, shell); - weston_compositor_add_key_binding(ec, KEY_UP, mod, - workspace_up_binding, shell); - weston_compositor_add_key_binding(ec, KEY_DOWN, mod, - workspace_down_binding, shell); - weston_compositor_add_key_binding(ec, KEY_UP, mod | MODIFIER_SHIFT, - workspace_move_surface_up_binding, - shell); - weston_compositor_add_key_binding(ec, KEY_DOWN, mod | MODIFIER_SHIFT, - workspace_move_surface_down_binding, - shell); - - /* Add bindings for mod+F[1-6] for workspace 1 to 6. */ - if (shell->workspaces.num > 1) { - num_workspace_bindings = shell->workspaces.num; - if (num_workspace_bindings > 6) - num_workspace_bindings = 6; - for (i = 0; i < num_workspace_bindings; i++) - weston_compositor_add_key_binding(ec, KEY_F1 + i, mod, - workspace_f_binding, - shell); - } weston_install_debug_key_binding(ec, mod); } @@ -5067,8 +4530,6 @@ wet_shell_init(struct weston_compositor *ec, { struct weston_seat *seat; struct desktop_shell *shell; - struct workspace **pws; - unsigned int i; struct wl_event_loop *loop; shell = zalloc(sizeof *shell); @@ -5104,8 +4565,6 @@ wet_shell_init(struct weston_compositor *ec, weston_layer_set_position(&shell->background_layer, WESTON_LAYER_POSITION_BACKGROUND); - wl_array_init(&shell->workspaces.array); - wl_list_init(&shell->workspaces.client_list); wl_list_init(&shell->seat_list); if (input_panel_setup(shell) < 0) @@ -5117,23 +4576,10 @@ wet_shell_init(struct weston_compositor *ec, shell_configuration(shell); - for (i = 0; i < shell->workspaces.num; i++) { - pws = wl_array_add(&shell->workspaces.array, sizeof *pws); - if (pws == NULL) - return -1; - - *pws = workspace_create(shell); - if (*pws == NULL) - return -1; - } - activate_workspace(shell, 0); + workspace_create(shell); weston_layer_init(&shell->minimized_layer, ec); - wl_list_init(&shell->workspaces.anim_sticky_list); - wl_list_init(&shell->workspaces.animation.link); - shell->workspaces.animation.frame = animate_workspace_change_frame; - shell->desktop = weston_desktop_create(ec, &shell_desktop_api, shell); if (!shell->desktop) return -1; diff --git a/desktop-shell/shell.h b/desktop-shell/shell.h index f4cb40fd..e9e123e9 100644 --- a/desktop-shell/shell.h +++ b/desktop-shell/shell.h @@ -47,7 +47,6 @@ enum fade_type { struct focus_surface { struct weston_curtain *curtain; - struct weston_transform workspace_transform; }; struct workspace { @@ -128,21 +127,7 @@ struct desktop_shell { struct weston_surface *lock_surface; struct wl_listener lock_surface_listener; - struct { - struct wl_array array; - unsigned int current; - unsigned int num; - - struct wl_list client_list; - - struct weston_animation animation; - struct wl_list anim_sticky_list; - int anim_dir; - struct timespec anim_timestamp; - double anim_current; - struct workspace *anim_from; - struct workspace *anim_to; - } workspaces; + struct workspace workspace; struct { struct wl_resource *binding; diff --git a/man/weston-bindings.man b/man/weston-bindings.man index 2e47ccc9..1cd39f7d 100644 --- a/man/weston-bindings.man +++ b/man/weston-bindings.man @@ -43,21 +43,6 @@ Zoom desktop in (or out) Switch active window .P .RE -.B mod + Up, mod + Down -.RS 4 -Increment/decrement active workspace number, if there are multiple -.P -.RE -.B mod + Shift + Up, mod + Shift + Down -.RS 4 -Move active window to the succeeding/preceding workspace, if possible -.P -.RE -.B mod + F1/F2/F3/F4/F5/F6 -.RS 4 -Jump to the numbered workspace, if it exists -.P -.RE .B Ctrl + Alt + Backspace .RS 4 If supported, terminate Weston. (Note this combination often is used to hard restart Xorg.) diff --git a/man/weston.ini.man b/man/weston.ini.man index d9b17d85..179e0882 100644 --- a/man/weston.ini.man +++ b/man/weston.ini.man @@ -426,11 +426,6 @@ for windows, controlling the backlight and zooming the desktop. See .BR weston-bindings (7). Possible values: none, ctrl, alt, super (default) .TP 7 -.BI "num-workspaces=" 6 -defines the number of workspaces (unsigned integer). The user can switch -workspaces by using the -binding+F1, F2 keys. If this key is not set, fall back to one workspace. -.TP 7 .BI "cursor-theme=" theme sets the cursor theme (string). .TP 7 From 10403a85ec03aff7deaa2bc9efb848660fea1100 Mon Sep 17 00:00:00 2001 From: Michael Olbrich Date: Thu, 23 Jun 2022 15:32:46 +0200 Subject: [PATCH 419/609] libweston: disable a pending idle_repaint_source when the output is removed Currently the idle_repaint_source is removed when the output is destroyed. This covers the most common case: When a monitor is unplugged then the corresponding DRM output is destroyed and not just disabled. However, outputs can be explicitly disabled by the shell. In this case the output is not removed and idle_repaint() may be called for a removed output. Remove the idle_repaint_source in weston_compositor_remove_output() to fix this. And reset the variable to ensure that the source can be created again. Removing the source in weston_output_release() is now no longer necessary since it calls weston_compositor_remove_output(). Signed-off-by: Michael Olbrich --- libweston/compositor.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libweston/compositor.c b/libweston/compositor.c index 6282901a..64e24741 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -6667,6 +6667,11 @@ weston_compositor_remove_output(struct weston_output *output) assert(output->destroying); assert(output->enabled); + if (output->idle_repaint_source) { + wl_event_source_remove(output->idle_repaint_source); + output->idle_repaint_source = NULL; + } + wl_list_for_each_safe(pnode, pntmp, &output->paint_node_list, output_link) { weston_paint_node_destroy(pnode); @@ -7315,9 +7320,6 @@ weston_output_release(struct weston_output *output) weston_signal_emit_mutable(&output->user_destroy_signal, output); - if (output->idle_repaint_source) - wl_event_source_remove(output->idle_repaint_source); - if (output->enabled) weston_compositor_remove_output(output); From 0774a321c5b233d7ec5e7143d6758a1e5bf37a5c Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 21 Jun 2022 17:33:02 +0100 Subject: [PATCH 420/609] scene-graph: Print when surface/view is not mapped A view shouldn't be mapped if a surface isn't mapped, and it shouldn't be in the scene graph if it isn't mapped either. Print when this happens so you can see more from the debug. Signed-off-by: Daniel Stone --- libweston/compositor.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libweston/compositor.c b/libweston/compositor.c index 64e24741..81a19c32 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -7928,7 +7928,7 @@ debug_scene_view_print(FILE *fp, struct weston_view *view, int view_idx) if (view->surface->resource) { struct wl_resource *resource = view->surface->resource; wl_client_get_credentials(wl_resource_get_client(resource), - &pid, NULL, NULL); + &pid, NULL, NULL); surface_id = wl_resource_get_id(view->surface->resource); } @@ -7940,6 +7940,11 @@ debug_scene_view_print(FILE *fp, struct weston_view *view, int view_idx) view_idx, view->surface->role_name, pid, surface_id, desc, view); + if (!weston_view_is_mapped(view)) + fprintf(fp, "\t[view is not mapped!]\n"); + if (!weston_surface_is_mapped(view->surface)) + fprintf(fp, "\t[surface is not mapped!]\n"); + box = pixman_region32_extents(&view->transform.boundingbox); fprintf(fp, "\t\tposition: (%d, %d) -> (%d, %d)\n", box->x1, box->y1, box->x2, box->y2); From 3ed3700ca3d33ffd5743f944ae3334302b294580 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 21 Jun 2022 17:49:14 +0100 Subject: [PATCH 421/609] kiosk-shell: Don't link desktop-shell protocols We don't need these. Signed-off-by: Daniel Stone --- kiosk-shell/meson.build | 2 -- 1 file changed, 2 deletions(-) diff --git a/kiosk-shell/meson.build b/kiosk-shell/meson.build index 95d94721..22d5cafa 100644 --- a/kiosk-shell/meson.build +++ b/kiosk-shell/meson.build @@ -2,8 +2,6 @@ if get_option('shell-kiosk') srcs_shell_kiosk = [ 'kiosk-shell.c', 'kiosk-shell-grab.c', - weston_desktop_shell_server_protocol_h, - weston_desktop_shell_protocol_c, input_method_unstable_v1_server_protocol_h, input_method_unstable_v1_protocol_c, ] From 9336263d9bc90955e7c91b5443e87666bd2209c7 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 21 Jun 2022 17:48:46 +0100 Subject: [PATCH 422/609] Move libweston-desktop into libweston It's not really useful to have libweston without libweston-desktop. It's also very little code. Merging both into the same DSO will allow us to cut out a bunch of indirection and pain. Signed-off-by: Daniel Stone --- desktop-shell/meson.build | 1 - ivi-shell/meson.build | 1 - kiosk-shell/meson.build | 1 - libweston-desktop/meson.build | 35 ------------------- .../desktop}/client.c | 0 .../desktop}/internal.h | 0 .../desktop}/libweston-desktop.c | 0 libweston/desktop/meson.build | 16 +++++++++ .../desktop}/seat.c | 0 .../desktop}/surface.c | 0 .../desktop}/xdg-shell-v6.c | 0 .../desktop}/xdg-shell.c | 0 .../desktop}/xwayland.c | 0 libweston/meson.build | 16 +++++++++ meson.build | 1 - shell-utils/meson.build | 2 +- tests/meson.build | 4 +-- 17 files changed, 35 insertions(+), 42 deletions(-) delete mode 100644 libweston-desktop/meson.build rename {libweston-desktop => libweston/desktop}/client.c (100%) rename {libweston-desktop => libweston/desktop}/internal.h (100%) rename {libweston-desktop => libweston/desktop}/libweston-desktop.c (100%) create mode 100644 libweston/desktop/meson.build rename {libweston-desktop => libweston/desktop}/seat.c (100%) rename {libweston-desktop => libweston/desktop}/surface.c (100%) rename {libweston-desktop => libweston/desktop}/xdg-shell-v6.c (100%) rename {libweston-desktop => libweston/desktop}/xdg-shell.c (100%) rename {libweston-desktop => libweston/desktop}/xwayland.c (100%) diff --git a/desktop-shell/meson.build b/desktop-shell/meson.build index 8c9754e4..c00d4a8d 100644 --- a/desktop-shell/meson.build +++ b/desktop-shell/meson.build @@ -13,7 +13,6 @@ if get_option('shell-desktop') dep_libm, dep_libexec_weston, dep_libshared, - dep_lib_desktop, dep_libweston_public, dep_shell_utils, ] diff --git a/ivi-shell/meson.build b/ivi-shell/meson.build index b071ca63..8e064fb7 100644 --- a/ivi-shell/meson.build +++ b/ivi-shell/meson.build @@ -15,7 +15,6 @@ if get_option('shell-ivi') dependencies: [ dep_libm, dep_libexec_weston, - dep_lib_desktop, dep_libweston_public ], name_prefix: '', diff --git a/kiosk-shell/meson.build b/kiosk-shell/meson.build index 22d5cafa..0ebea621 100644 --- a/kiosk-shell/meson.build +++ b/kiosk-shell/meson.build @@ -9,7 +9,6 @@ if get_option('shell-kiosk') dep_libm, dep_libexec_weston, dep_libshared, - dep_lib_desktop, dep_libweston_public, dep_shell_utils, ] diff --git a/libweston-desktop/meson.build b/libweston-desktop/meson.build deleted file mode 100644 index 72e77a06..00000000 --- a/libweston-desktop/meson.build +++ /dev/null @@ -1,35 +0,0 @@ -srcs_libdesktop = [ - 'libweston-desktop.c', - 'client.c', - 'seat.c', - 'surface.c', - 'xwayland.c', - 'xdg-shell.c', - 'xdg-shell-v6.c', - xdg_shell_unstable_v6_server_protocol_h, - xdg_shell_unstable_v6_protocol_c, - xdg_shell_server_protocol_h, - xdg_shell_protocol_c, -] -lib_desktop = shared_library( - 'weston-desktop-@0@'.format(libweston_major), - srcs_libdesktop, - include_directories: common_inc, - install: true, - version: '0.0.@0@'.format(libweston_revision), - dependencies: dep_libweston_public -) -dep_lib_desktop = declare_dependency( - link_with: lib_desktop, - dependencies: dep_libweston_public -) - -pkgconfig.generate( - lib_desktop, - filebase: 'libweston-desktop-@0@'.format(libweston_major), - name: 'libweston-desktop', - version: version_weston, - description: 'Desktop shells abstraction library for libweston compositors', - requires_private: [ lib_weston, dep_wayland_server ], - subdirs: dir_include_libweston -) diff --git a/libweston-desktop/client.c b/libweston/desktop/client.c similarity index 100% rename from libweston-desktop/client.c rename to libweston/desktop/client.c diff --git a/libweston-desktop/internal.h b/libweston/desktop/internal.h similarity index 100% rename from libweston-desktop/internal.h rename to libweston/desktop/internal.h diff --git a/libweston-desktop/libweston-desktop.c b/libweston/desktop/libweston-desktop.c similarity index 100% rename from libweston-desktop/libweston-desktop.c rename to libweston/desktop/libweston-desktop.c diff --git a/libweston/desktop/meson.build b/libweston/desktop/meson.build new file mode 100644 index 00000000..4588ad10 --- /dev/null +++ b/libweston/desktop/meson.build @@ -0,0 +1,16 @@ +srcs_libweston += files([ + 'libweston-desktop.c', + 'client.c', + 'seat.c', + 'surface.c', + 'xwayland.c', + 'xdg-shell.c', + 'xdg-shell-v6.c', +]) + +srcs_libweston += [ + xdg_shell_unstable_v6_server_protocol_h, + xdg_shell_unstable_v6_protocol_c, + xdg_shell_server_protocol_h, + xdg_shell_protocol_c, +] diff --git a/libweston-desktop/seat.c b/libweston/desktop/seat.c similarity index 100% rename from libweston-desktop/seat.c rename to libweston/desktop/seat.c diff --git a/libweston-desktop/surface.c b/libweston/desktop/surface.c similarity index 100% rename from libweston-desktop/surface.c rename to libweston/desktop/surface.c diff --git a/libweston-desktop/xdg-shell-v6.c b/libweston/desktop/xdg-shell-v6.c similarity index 100% rename from libweston-desktop/xdg-shell-v6.c rename to libweston/desktop/xdg-shell-v6.c diff --git a/libweston-desktop/xdg-shell.c b/libweston/desktop/xdg-shell.c similarity index 100% rename from libweston-desktop/xdg-shell.c rename to libweston/desktop/xdg-shell.c diff --git a/libweston-desktop/xwayland.c b/libweston/desktop/xwayland.c similarity index 100% rename from libweston-desktop/xwayland.c rename to libweston/desktop/xwayland.c diff --git a/libweston/meson.build b/libweston/meson.build index 7638b1c1..82f81466 100644 --- a/libweston/meson.build +++ b/libweston/meson.build @@ -69,6 +69,8 @@ srcs_libweston = [ weston_direct_display_server_protocol_h, ] +subdir('desktop') + if get_option('renderer-gl') dep_egl = dependency('egl', required: false) if not dep_egl.found() @@ -130,6 +132,20 @@ pkgconfig.generate( subdirs: dir_include_libweston ) +if version_weston.version_compare('>= 11.0.90') + error('Remove libweston-desktop.pc for Weston 12.x') +endif + +pkgconfig.generate( + lib_weston, + filebase: 'libweston-desktop-@0@'.format(libweston_major), + name: 'libweston-desktop', + version: version_weston, + description: 'Desktop shell abstraction library for libweston compositors', + requires_private: deps_for_libweston_users, + subdirs: dir_include_libweston +) + pkgconfig.generate( filebase: 'libweston-@0@-protocols'.format(libweston_major), name: 'libWeston Protocols', diff --git a/meson.build b/meson.build index cc266b5c..780d6b04 100644 --- a/meson.build +++ b/meson.build @@ -162,7 +162,6 @@ subdir('include') subdir('protocol') subdir('shared') subdir('libweston') -subdir('libweston-desktop') subdir('xwayland') subdir('shell-utils') subdir('compositor') diff --git a/shell-utils/meson.build b/shell-utils/meson.build index 02ae9001..88aaca8c 100644 --- a/shell-utils/meson.build +++ b/shell-utils/meson.build @@ -1,5 +1,5 @@ dep_shell_utils = declare_dependency( sources: 'shell-utils.c', include_directories: include_directories('.'), - dependencies: dep_lib_desktop + dependencies: dep_libweston_public, ) diff --git a/tests/meson.build b/tests/meson.build index 920eb9d7..6a5dea41 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -2,7 +2,7 @@ plugin_test_shell_desktop = shared_library( 'weston-test-desktop-shell', 'weston-test-desktop-shell.c', include_directories: common_inc, - dependencies: [ dep_lib_desktop, dep_libweston_public, dep_libexec_weston, dep_shell_utils ], + dependencies: [ dep_libweston_public, dep_libexec_weston, dep_shell_utils ], name_prefix: '', install: false ) @@ -241,7 +241,7 @@ tests = [ 'sources': [ 'safe-signal-output-removal-test.c', ], - 'dep_objs': [ dep_lib_desktop, dep_shell_utils ] + 'dep_objs': [ dep_shell_utils ] }, ] From 0df0dccc84311b0ba5fa255960325525a236c0aa Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Mon, 27 Jun 2022 13:33:42 -0500 Subject: [PATCH 423/609] shared: Make xalloc.h stand alone Make fail_on_null static inline and put it in xalloc.h so we can use the header exclusively instead of having to link with the library for it. This is so we can use xalloc in places (like the RDP backend) without having to bring in libshared. Signed-off-by: Derek Foreman --- shared/meson.build | 1 - shared/xalloc.c | 50 ---------------------------------------------- shared/xalloc.h | 20 +++++++++++++++++-- 3 files changed, 18 insertions(+), 53 deletions(-) delete mode 100644 shared/xalloc.c diff --git a/shared/meson.build b/shared/meson.build index d05e2d46..9b5d7a50 100644 --- a/shared/meson.build +++ b/shared/meson.build @@ -4,7 +4,6 @@ srcs_libshared = [ 'signal.c', 'file-util.c', 'os-compatibility.c', - 'xalloc.c', ] deps_libshared = [dep_wayland_client, dep_wayland_server] diff --git a/shared/xalloc.c b/shared/xalloc.c deleted file mode 100644 index 1cc5c12a..00000000 --- a/shared/xalloc.c +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright © 2008 Kristian Høgsberg - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "config.h" - -#include -#include -#include -#include - -#include "xalloc.h" - -void * -fail_on_null(void *p, size_t size, char *file, int32_t line) -{ - if (p == NULL) { - fprintf(stderr, "[%s] ", program_invocation_short_name); - if (file) - fprintf(stderr, "%s:%d: ", file, line); - fprintf(stderr, "out of memory"); - if (size) - fprintf(stderr, " (%zd)", size); - fprintf(stderr, "\n"); - exit(EXIT_FAILURE); - } - - return p; -} diff --git a/shared/xalloc.h b/shared/xalloc.h index cd39dd8b..15ad1fad 100644 --- a/shared/xalloc.h +++ b/shared/xalloc.h @@ -30,14 +30,30 @@ extern "C" { #endif +#include #include #include #include #include -void * -fail_on_null(void *p, size_t size, char *file, int32_t line); + +static inline void * +fail_on_null(void *p, size_t size, char *file, int32_t line) +{ + if (p == NULL) { + fprintf(stderr, "[%s] ", program_invocation_short_name); + if (file) + fprintf(stderr, "%s:%d: ", file, line); + fprintf(stderr, "out of memory"); + if (size) + fprintf(stderr, " (%zd)", size); + fprintf(stderr, "\n"); + exit(EXIT_FAILURE); + } + + return p; +} #define xmalloc(s) (fail_on_null(malloc(s), (s), __FILE__, __LINE__)) #define xzalloc(s) (fail_on_null(zalloc(s), (s), __FILE__, __LINE__)) From e4100f856db5153625cb137e8e852d4413481a79 Mon Sep 17 00:00:00 2001 From: Hideyuki Nagase Date: Thu, 10 Mar 2022 10:58:22 -0600 Subject: [PATCH 424/609] xwayland: Change layer for xwayland override redirect windows Our positioning of override redirect windows falls apart when an app is on the fullscreen layer, because we end up putting its menus and tooltips beneath it. This patch raises the special override redirect layer to be just below things like on-screen keyboards (and, unfortunately, above things like panels). There is no perfect way to deal with this problem, especially for content like tooltips that don't come with transience hints. In some cases override redirect menus could be better placed by using the parenting/transience information provided with them at map time, and we should probably do that at some point, but that would still leave us with tooltips below full screen applications, and the need for this layer change. based on a patch Co-authored-by: Hideyuki Nagase Co-authored-by: Steve Pronovost Co-authored-by: Brenton DeGeer I changed the layer position and the comments, so: Signed-off-by: Derek Foreman --- libweston/desktop/xwayland.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/libweston/desktop/xwayland.c b/libweston/desktop/xwayland.c index f40bb5db..4e426b11 100644 --- a/libweston/desktop/xwayland.c +++ b/libweston/desktop/xwayland.c @@ -444,10 +444,21 @@ weston_desktop_xwayland_init(struct weston_desktop *desktop) xwayland->client = weston_desktop_client_create(desktop, NULL, NULL, NULL, NULL, 0, 0); weston_layer_init(&xwayland->layer, compositor); - /* We put this layer on top of regular shell surfaces, but hopefully - * below any UI the shell would add */ + /* This is the layer we use for override redirect "windows", which + * ends up used for tooltips and drop down menus, among other things. + * Previously this was WESTON_LAYER_POSITION_NORMAL + 1, but this is + * below the fullscreen layer, so fullscreen apps would be above their + * menus and tooltips. + * + * Moving this to just below the TOP_UI layer ensures visibility at all + * times, with the minor drawback that they could be rendered above + * DESKTOP_UI. + * + * For tooltips with no transient window hints, this is probably the best + * we can do. + */ weston_layer_set_position(&xwayland->layer, - WESTON_LAYER_POSITION_NORMAL + 1); + WESTON_LAYER_POSITION_TOP_UI - 1); compositor->xwayland = xwayland; compositor->xwayland_interface = &weston_desktop_xwayland_interface; From 19278569a3243cf91596155b73eac29692868fc6 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Sat, 25 Jun 2022 16:37:30 +0100 Subject: [PATCH 425/609] noop-renderer: weston_buffer properties are set by the core ca9bb01fe6f6 made it so that we already set shm_buffer, width, height, etc, in the core. There's no need for the renderer to do this. Signed-off-by: Daniel Stone --- libweston/noop-renderer.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/libweston/noop-renderer.c b/libweston/noop-renderer.c index e89200b3..e6ab8ee4 100644 --- a/libweston/noop-renderer.c +++ b/libweston/noop-renderer.c @@ -57,7 +57,7 @@ noop_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) { struct wl_shm_buffer *shm_buffer; uint8_t *data; - uint32_t size, i, width, height, stride; + uint32_t size, i, height, stride; __attribute__((unused)) int unused = 0; if (!buffer) @@ -78,8 +78,7 @@ noop_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) shm_buffer = buffer->shm_buffer; data = wl_shm_buffer_get_data(shm_buffer); stride = wl_shm_buffer_get_stride(shm_buffer); - width = wl_shm_buffer_get_width(shm_buffer); - height = wl_shm_buffer_get_height(shm_buffer); + height = buffer->height; size = stride * height; /* Access the buffer data to make sure the buffer's client gets killed @@ -91,9 +90,6 @@ noop_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) unused ^= data[i]; wl_shm_buffer_end_access(shm_buffer); - buffer->shm_buffer = shm_buffer; - buffer->width = width; - buffer->height = height; } static void From 450ec38d793a0f85d3afb6d6c2e9d59d8b607fd9 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Sat, 25 Jun 2022 16:38:35 +0100 Subject: [PATCH 426/609] noop-renderer: Make sure buffer access doesn't get optimised out noop-renderer needs to actually access the buffer content, to ensure that the bad-buffer test works. This was previously done using a volatile variable, but clang rightly pointed out that the variable access had no effect (since the volatile stack variable was never read from, and the source is not volatile), so 9b0b5b57ddca changed it to be explicitly marked it as unused to suppress the compiler warning. Unfortunately suppressing the warning still leaves the compiler free to optimise out the access. Replace the variable decorations with actually using the result of the read, so we can be really sure that it's never going to be optimised away. Signed-off-by: Daniel Stone --- libweston/noop-renderer.c | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/libweston/noop-renderer.c b/libweston/noop-renderer.c index e6ab8ee4..731d1371 100644 --- a/libweston/noop-renderer.c +++ b/libweston/noop-renderer.c @@ -31,6 +31,11 @@ #include #include "libweston-internal.h" +struct noop_renderer { + struct weston_renderer base; + unsigned char seed; /* see comment in attach() */ +}; + static int noop_renderer_read_pixels(struct weston_output *output, pixman_format_code_t format, void *pixels, @@ -55,10 +60,12 @@ noop_renderer_flush_damage(struct weston_surface *surface, static void noop_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) { + struct noop_renderer *renderer = + wl_container_of(es->compositor->renderer, renderer, base); struct wl_shm_buffer *shm_buffer; uint8_t *data; uint32_t size, i, height, stride; - __attribute__((unused)) int unused = 0; + unsigned char unused = 0; if (!buffer) return; @@ -90,11 +97,18 @@ noop_renderer_attach(struct weston_surface *es, struct weston_buffer *buffer) unused ^= data[i]; wl_shm_buffer_end_access(shm_buffer); + /* Make sure that our unused is actually used, otherwise the compiler + * is free to notice that our reads have no effect and elide them. */ + renderer->seed = unused; } static void noop_renderer_destroy(struct weston_compositor *ec) { + struct noop_renderer *renderer = + wl_container_of(ec->renderer, renderer, base); + + weston_log("no-op renderer SHM seed: %d\n", renderer->seed); free(ec->renderer); ec->renderer = NULL; } @@ -102,18 +116,18 @@ noop_renderer_destroy(struct weston_compositor *ec) WL_EXPORT int noop_renderer_init(struct weston_compositor *ec) { - struct weston_renderer *renderer; + struct noop_renderer *renderer; renderer = zalloc(sizeof *renderer); if (renderer == NULL) return -1; - renderer->read_pixels = noop_renderer_read_pixels; - renderer->repaint_output = noop_renderer_repaint_output; - renderer->flush_damage = noop_renderer_flush_damage; - renderer->attach = noop_renderer_attach; - renderer->destroy = noop_renderer_destroy; - ec->renderer = renderer; + renderer->base.read_pixels = noop_renderer_read_pixels; + renderer->base.repaint_output = noop_renderer_repaint_output; + renderer->base.flush_damage = noop_renderer_flush_damage; + renderer->base.attach = noop_renderer_attach; + renderer->base.destroy = noop_renderer_destroy; + ec->renderer = &renderer->base; return 0; } From ed97387a4ed7de217810f7ce708df142a10fc247 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Sat, 25 Jun 2022 16:59:38 +0100 Subject: [PATCH 427/609] tests: Use test-desktop-shell for devices-test It doesn't need to be using desktop-shell; trying to use it is not sensible as it will try to bind to all the devices we're repeatedly creating and destroying, sometimes lose the race, and fail the test because desktop-shell has died too early. Signed-off-by: Daniel Stone --- tests/devices-test.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/devices-test.c b/tests/devices-test.c index 719f4595..e84273cc 100644 --- a/tests/devices-test.c +++ b/tests/devices-test.c @@ -36,6 +36,8 @@ fixture_setup(struct weston_test_harness *harness) compositor_setup_defaults(&setup); + setup.shell = SHELL_TEST_DESKTOP; + return weston_test_harness_execute_as_client(harness, &setup); } DECLARE_FIXTURE_SETUP(fixture_setup); From d615abdffdb0b3f69c937c577cbd3855ed382a74 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Wed, 22 Jun 2022 14:04:18 -0500 Subject: [PATCH 428/609] shells: Add libweston-desktop API to query position and add to shells We're going to need this to properly send xwayland events later, so add API to get the current x,y co-ordinates of a shell surface and add it to the kiosk and desktop shells. Signed-off-by: Derek Foreman --- desktop-shell/shell.c | 12 ++++++++++++ include/libweston-desktop/libweston-desktop.h | 3 +++ kiosk-shell/kiosk-shell.c | 12 ++++++++++++ libweston/desktop/internal.h | 5 +++++ libweston/desktop/libweston-desktop.c | 11 +++++++++++ 5 files changed, 43 insertions(+) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index c5afd8e1..9ebc9550 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -2451,6 +2451,17 @@ desktop_surface_set_xwayland_position(struct weston_desktop_surface *surface, shsurf->xwayland.is_set = true; } +static void +desktop_surface_get_position(struct weston_desktop_surface *surface, + int32_t *x, int32_t *y, + void *shell_) +{ + struct shell_surface *shsurf = weston_desktop_surface_get_user_data(surface); + + *x = shsurf->view->geometry.x; + *y = shsurf->view->geometry.y; +} + static const struct weston_desktop_api shell_desktop_api = { .struct_size = sizeof(struct weston_desktop_api), .surface_added = desktop_surface_added, @@ -2465,6 +2476,7 @@ static const struct weston_desktop_api shell_desktop_api = { .ping_timeout = desktop_surface_ping_timeout, .pong = desktop_surface_pong, .set_xwayland_position = desktop_surface_set_xwayland_position, + .get_position = desktop_surface_get_position, }; /* ************************ * diff --git a/include/libweston-desktop/libweston-desktop.h b/include/libweston-desktop/libweston-desktop.h index 3e7ac738..313179ef 100644 --- a/include/libweston-desktop/libweston-desktop.h +++ b/include/libweston-desktop/libweston-desktop.h @@ -113,6 +113,9 @@ struct weston_desktop_api { */ void (*set_xwayland_position)(struct weston_desktop_surface *surface, int32_t x, int32_t y, void *user_data); + void (*get_position)(struct weston_desktop_surface *surface, + int32_t *x, int32_t *y, + void *user_data); }; void diff --git a/kiosk-shell/kiosk-shell.c b/kiosk-shell/kiosk-shell.c index 62949ed4..e13742fb 100644 --- a/kiosk-shell/kiosk-shell.c +++ b/kiosk-shell/kiosk-shell.c @@ -951,6 +951,17 @@ desktop_surface_set_xwayland_position(struct weston_desktop_surface *desktop_sur shsurf->xwayland.is_set = true; } +static void +desktop_surface_get_position(struct weston_desktop_surface *desktop_surface, + int32_t *x, int32_t *y, void *shell) +{ + struct kiosk_shell_surface *shsurf = + weston_desktop_surface_get_user_data(desktop_surface); + + *x = shsurf->view->geometry.x; + *y = shsurf->view->geometry.y; +} + static const struct weston_desktop_api kiosk_shell_desktop_api = { .struct_size = sizeof(struct weston_desktop_api), .surface_added = desktop_surface_added, @@ -965,6 +976,7 @@ static const struct weston_desktop_api kiosk_shell_desktop_api = { .ping_timeout = desktop_surface_ping_timeout, .pong = desktop_surface_pong, .set_xwayland_position = desktop_surface_set_xwayland_position, + .get_position = desktop_surface_get_position, }; /* diff --git a/libweston/desktop/internal.h b/libweston/desktop/internal.h index 8fdd4a28..f45b6601 100644 --- a/libweston/desktop/internal.h +++ b/libweston/desktop/internal.h @@ -86,6 +86,11 @@ weston_desktop_api_set_xwayland_position(struct weston_desktop *desktop, struct weston_desktop_surface *surface, int32_t x, int32_t y); +void +weston_desktop_api_get_position(struct weston_desktop *desktop, + struct weston_desktop_surface *surface, + int32_t *x, int32_t *y); + struct weston_desktop_seat * weston_desktop_seat_from_seat(struct weston_seat *wseat); diff --git a/libweston/desktop/libweston-desktop.c b/libweston/desktop/libweston-desktop.c index ea6fbebe..f7ecc709 100644 --- a/libweston/desktop/libweston-desktop.c +++ b/libweston/desktop/libweston-desktop.c @@ -237,3 +237,14 @@ weston_desktop_api_set_xwayland_position(struct weston_desktop *desktop, desktop->api.set_xwayland_position(surface, x, y, desktop->user_data); } + +void +weston_desktop_api_get_position(struct weston_desktop *desktop, + struct weston_desktop_surface *surface, + int32_t *x, int32_t *y) +{ + if (!desktop->api.get_position) + return; + + desktop->api.get_position(surface, x, y, desktop->user_data); +} From 23e3a3285a66a396aded43e25b079a5ca1c6573d Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Wed, 22 Jun 2022 14:08:23 -0500 Subject: [PATCH 429/609] libweston-desktop: Add get_position Plumb the new weston_desktop_api_get_position() through to xwayland. Signed-off-by: Derek Foreman --- libweston/desktop/xwayland.c | 13 +++++++++++++ xwayland/xwayland-internal-interface.h | 3 +++ 2 files changed, 16 insertions(+) diff --git a/libweston/desktop/xwayland.c b/libweston/desktop/xwayland.c index 4e426b11..0b795648 100644 --- a/libweston/desktop/xwayland.c +++ b/libweston/desktop/xwayland.c @@ -413,6 +413,18 @@ set_pid(struct weston_desktop_xwayland_surface *surface, pid_t pid) weston_desktop_surface_set_pid(surface->surface, pid); } +static void +get_position(struct weston_desktop_xwayland_surface *surface, + int32_t *x, int32_t *y) +{ + if (!surface->surface) { + *x = 0; + *y = 0; + return; + } + weston_desktop_api_get_position(surface->desktop, surface->surface, x, y); +} + static const struct weston_desktop_xwayland_interface weston_desktop_xwayland_interface = { .create_surface = create_surface, .set_toplevel = set_toplevel, @@ -428,6 +440,7 @@ static const struct weston_desktop_xwayland_interface weston_desktop_xwayland_in .set_maximized = set_maximized, .set_minimized = set_minimized, .set_pid = set_pid, + .get_position = get_position, }; void diff --git a/xwayland/xwayland-internal-interface.h b/xwayland/xwayland-internal-interface.h index a97d13bc..0e732772 100644 --- a/xwayland/xwayland-internal-interface.h +++ b/xwayland/xwayland-internal-interface.h @@ -61,6 +61,9 @@ struct weston_desktop_xwayland_interface { void (*set_maximized)(struct weston_desktop_xwayland_surface *shsurf); void (*set_minimized)(struct weston_desktop_xwayland_surface *shsurf); void (*set_pid)(struct weston_desktop_xwayland_surface *shsurf, pid_t pid); + void (*get_position)(struct weston_desktop_xwayland_surface *surface, + int32_t *x, int32_t *y); + }; #endif From ea9a01f2e39ccd3e57a87429b7a88744dd6215ad Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Tue, 21 Jun 2022 08:49:59 -0500 Subject: [PATCH 430/609] xwm: Prepare send_configurenotify for non-fullscreen use Currently weston_wm_window_send_configurenotify is only called for fullscreen clients, and it is written to be correct only in that case. Fix it up to handle other cases properly so we can use it for them in a later commit. Synthetic Configure Notify events are relative to the root window, so this means adding our window co-ordinate when necessary. Signed-off-by: Derek Foreman --- xwayland/window-manager.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/xwayland/window-manager.c b/xwayland/window-manager.c index 0833b617..a0a61912 100644 --- a/xwayland/window-manager.c +++ b/xwayland/window-manager.c @@ -675,15 +675,22 @@ weston_wm_window_send_configure_notify(struct weston_wm_window *window) xcb_configure_notify_event_t configure_notify; struct weston_wm *wm = window->wm; int x, y; + int32_t dx = 0, dy = 0; + const struct weston_desktop_xwayland_interface *xwayland_api = + wm->server->compositor->xwayland_interface; weston_wm_window_get_child_position(window, &x, &y); + /* Synthetic ConfigureNotify events must be relative to the root + * window, so get our offset if we're mapped. */ + if (window->shsurf) + xwayland_api->get_position(window->shsurf, &dx, &dy); configure_notify.response_type = XCB_CONFIGURE_NOTIFY; configure_notify.pad0 = 0; configure_notify.event = window->id; configure_notify.window = window->id; configure_notify.above_sibling = XCB_WINDOW_NONE; - configure_notify.x = x; - configure_notify.y = y; + configure_notify.x = x + dx; + configure_notify.y = y + dy; configure_notify.width = window->width; configure_notify.height = window->height; configure_notify.border_width = 0; From cf5aca5a0db845367570e4bc292e3bb730be8fcc Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Tue, 21 Jun 2022 09:09:23 -0500 Subject: [PATCH 431/609] xwm: Generate more synthetic ConfigureNotify events Many programs use this information to help position pop-ups properly, and without it funny things happen. For example, nedit and tkinter apps will position their menus incorrectly either all the time or after an initial window move, firefox may position right-click pop-ups incorrectly depending on other internal state. https://tronche.com/gui/x/icccm/sec-4.html#s-4.1.5 has much detail on how this should work, and the Advice to Implementors section shows that common client practices will break in the face of our miserly handling of ConfigureNotify events. Instead of trying to send it only for configure requests received when a client is in a fullscreen state, send them much more frequently. Fixes #619 Signed-off-by: Derek Foreman --- xwayland/window-manager.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/xwayland/window-manager.c b/xwayland/window-manager.c index a0a61912..7889f871 100644 --- a/xwayland/window-manager.c +++ b/xwayland/window-manager.c @@ -814,6 +814,7 @@ weston_wm_handle_configure_request(struct weston_wm *wm, xcb_generic_event_t *ev weston_wm_configure_window(wm, window->id, mask, values); weston_wm_window_configure_frame(window); + weston_wm_window_send_configure_notify(window); weston_wm_window_schedule_repaint(window); } @@ -1156,6 +1157,7 @@ weston_wm_window_create_frame(struct weston_wm_window *window) width, height); hash_table_insert(wm->window_hash, window->frame_id, window); + weston_wm_window_send_configure_notify(window); } /* @@ -2713,6 +2715,7 @@ weston_wm_window_configure(void *data) values); weston_wm_window_configure_frame(window); + weston_wm_window_send_configure_notify(window); weston_wm_window_schedule_repaint(window); } @@ -2802,6 +2805,7 @@ send_position(struct weston_surface *surface, int32_t x, int32_t y) mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y; weston_wm_configure_window(wm, window->frame_id, mask, values); + weston_wm_window_send_configure_notify(window); xcb_flush(wm->conn); } } From 97f664815d6c27244f710e998868c689dbf84b0e Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 24 Jun 2022 14:53:21 +0100 Subject: [PATCH 432/609] backend-wayland: Don't leak parent output trackers We were only destroying these when the parent display removed the output global. Do it on shutdown too, so we can avoid leaking it. Signed-off-by: Daniel Stone --- libweston/backend-wayland/wayland.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libweston/backend-wayland/wayland.c b/libweston/backend-wayland/wayland.c index e98d7d87..0759ee53 100644 --- a/libweston/backend-wayland/wayland.c +++ b/libweston/backend-wayland/wayland.c @@ -2716,6 +2716,7 @@ wayland_destroy(struct weston_compositor *ec) { struct wayland_backend *b = to_wayland_backend(ec); struct weston_head *base, *next; + struct wayland_parent_output *output, *next_output; struct wayland_input *input, *next_input; wl_event_source_remove(b->parent.wl_source); @@ -2727,6 +2728,9 @@ wayland_destroy(struct weston_compositor *ec) wayland_head_destroy(base); } + wl_list_for_each_safe(output, next_output, &b->parent.output_list, link) + wayland_parent_output_destroy(output); + wl_list_for_each_safe(input, next_input, &b->input_list, link) wayland_input_destroy(input); From 01c57eca43e882790119de5955ff3c56d664a2e8 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Sat, 25 Jun 2022 03:27:42 +0100 Subject: [PATCH 433/609] backend-drm: Don't leak drm_device on shutdown This was introduced in a partial MR, where the later commits in the new multi-GPU MR fully fix it, but the initially cherry-picked ones don't. Signed-off-by: Daniel Stone --- libweston/backend-drm/drm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index f67609e8..ab97b7ba 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -2681,6 +2681,7 @@ drm_destroy(struct weston_compositor *ec) weston_launcher_destroy(ec->launcher); free(device->drm.filename); + free(device); free(b); } From 6bfbfb2e1021841cf261e4ccd82f9a328e96876c Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 24 Jun 2022 11:39:40 +0100 Subject: [PATCH 434/609] toytoolkit: Delete remnants of EGL support This code was all dead, since neither cairo-glesv2 nor the sample nested compositor ever made it to the Meson build. Signed-off-by: Daniel Stone --- clients/nested-client.c | 374 ------------- clients/nested.c | 1139 --------------------------------------- clients/window.c | 361 ------------- clients/window.h | 20 - 4 files changed, 1894 deletions(-) delete mode 100644 clients/nested-client.c delete mode 100644 clients/nested.c diff --git a/clients/nested-client.c b/clients/nested-client.c deleted file mode 100644 index a9e034ef..00000000 --- a/clients/nested-client.c +++ /dev/null @@ -1,374 +0,0 @@ -/* - * Copyright © 2013 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#include "config.h" - -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include "shared/platform.h" - -struct window; -struct seat; - -struct nested_client { - struct wl_display *display; - struct wl_registry *registry; - struct wl_compositor *compositor; - - EGLDisplay egl_display; - EGLContext egl_context; - EGLConfig egl_config; - EGLSurface egl_surface; - struct program *color_program; - - GLuint vert, frag, program; - GLuint rotation; - GLuint pos; - GLuint col; - - struct wl_surface *surface; - struct wl_egl_window *native; - int width, height; -}; - -#define POS 0 -#define COL 1 - -static GLuint -create_shader(const char *source, GLenum shader_type) -{ - GLuint shader; - GLint status; - - shader = glCreateShader(shader_type); - if (shader == 0) - return 0; - - glShaderSource(shader, 1, (const char **) &source, NULL); - glCompileShader(shader); - - glGetShaderiv(shader, GL_COMPILE_STATUS, &status); - if (!status) { - char log[1000]; - GLsizei len; - glGetShaderInfoLog(shader, 1000, &len, log); - fprintf(stderr, "Error: compiling %s: %.*s\n", - shader_type == GL_VERTEX_SHADER ? "vertex" : "fragment", - len, log); - return 0; - } - - return shader; -} - -static void -create_program(struct nested_client *client, - const char *vert, const char *frag) -{ - GLint status; - - client->vert = create_shader(vert, GL_VERTEX_SHADER); - client->frag = create_shader(frag, GL_FRAGMENT_SHADER); - - client->program = glCreateProgram(); - glAttachShader(client->program, client->frag); - glAttachShader(client->program, client->vert); - glBindAttribLocation(client->program, POS, "pos"); - glBindAttribLocation(client->program, COL, "color"); - glLinkProgram(client->program); - - glGetProgramiv(client->program, GL_LINK_STATUS, &status); - if (!status) { - char log[1000]; - GLsizei len; - glGetProgramInfoLog(client->program, 1000, &len, log); - fprintf(stderr, "Error: linking:\n%.*s\n", len, log); - exit(1); - } - - client->rotation = - glGetUniformLocation(client->program, "rotation"); -} - -static const char vertex_shader_text[] = - "uniform mat4 rotation;\n" - "attribute vec4 pos;\n" - "attribute vec4 color;\n" - "varying vec4 v_color;\n" - "void main() {\n" - " gl_Position = rotation * pos;\n" - " v_color = color;\n" - "}\n"; - -static const char color_fragment_shader_text[] = - "precision mediump float;\n" - "varying vec4 v_color;\n" - "void main() {\n" - " gl_FragColor = v_color;\n" - "}\n"; - -static void -render_triangle(struct nested_client *client, uint32_t time) -{ - static const GLfloat verts[3][2] = { - { -0.5, -0.5 }, - { 0.5, -0.5 }, - { 0, 0.5 } - }; - static const GLfloat colors[3][3] = { - { 1, 0, 0 }, - { 0, 1, 0 }, - { 0, 0, 1 } - }; - GLfloat angle; - GLfloat rotation[4][4] = { - { 1, 0, 0, 0 }, - { 0, 1, 0, 0 }, - { 0, 0, 1, 0 }, - { 0, 0, 0, 1 } - }; - static const int32_t speed_div = 5; - static uint32_t start_time = 0; - - if (client->program == 0) - create_program(client, vertex_shader_text, - color_fragment_shader_text); - - if (start_time == 0) - start_time = time; - - angle = ((time - start_time) / speed_div) % 360 * M_PI / 180.0; - rotation[0][0] = cos(angle); - rotation[0][2] = sin(angle); - rotation[2][0] = -sin(angle); - rotation[2][2] = cos(angle); - - glClearColor(0.4, 0.4, 0.4, 1.0); - glClear(GL_COLOR_BUFFER_BIT); - - glUseProgram(client->program); - - glViewport(0, 0, client->width, client->height); - - glUniformMatrix4fv(client->rotation, 1, GL_FALSE, - (GLfloat *) rotation); - - glVertexAttribPointer(POS, 2, GL_FLOAT, GL_FALSE, 0, verts); - glVertexAttribPointer(COL, 3, GL_FLOAT, GL_FALSE, 0, colors); - glEnableVertexAttribArray(POS); - glEnableVertexAttribArray(COL); - - glDrawArrays(GL_TRIANGLES, 0, 3); - - glDisableVertexAttribArray(POS); - glDisableVertexAttribArray(COL); - - glFlush(); -} - -static void -frame_callback(void *data, struct wl_callback *callback, uint32_t time); - -static const struct wl_callback_listener frame_listener = { - frame_callback -}; - -static void -frame_callback(void *data, struct wl_callback *callback, uint32_t time) -{ - struct nested_client *client = data; - - if (callback) - wl_callback_destroy(callback); - - callback = wl_surface_frame(client->surface); - wl_callback_add_listener(callback, &frame_listener, client); - - render_triangle(client, time); - - eglSwapBuffers(client->egl_display, client->egl_surface); -} - -static void -registry_handle_global(void *data, struct wl_registry *registry, - uint32_t name, const char *interface, uint32_t version) -{ - struct nested_client *client = data; - - if (strcmp(interface, "wl_compositor") == 0) { - client->compositor = - wl_registry_bind(registry, name, - &wl_compositor_interface, 1); - } -} - -static void -registry_handle_global_remove(void *data, struct wl_registry *registry, - uint32_t name) -{ -} - -static const struct wl_registry_listener registry_listener = { - registry_handle_global, - registry_handle_global_remove -}; - -static struct nested_client * -nested_client_create(void) -{ - static const EGLint context_attribs[] = { - EGL_CONTEXT_CLIENT_VERSION, 2, - EGL_NONE - }; - - static const EGLint config_attribs[] = { - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - EGL_RED_SIZE, 1, - EGL_GREEN_SIZE, 1, - EGL_BLUE_SIZE, 1, - EGL_ALPHA_SIZE, 1, - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL_NONE - }; - - EGLint major, minor, n; - EGLBoolean ret; - - struct nested_client *client; - - client = malloc(sizeof *client); - if (client == NULL) - return NULL; - - client->width = 250; - client->height = 250; - - client->display = wl_display_connect(NULL); - - client->registry = wl_display_get_registry(client->display); - wl_registry_add_listener(client->registry, - ®istry_listener, client); - - /* get globals */ - wl_display_roundtrip(client->display); - - client->egl_display = - weston_platform_get_egl_display(EGL_PLATFORM_WAYLAND_KHR, - client->display, NULL); - if (client->egl_display == NULL) - return NULL; - - ret = eglInitialize(client->egl_display, &major, &minor); - if (!ret) - return NULL; - ret = eglBindAPI(EGL_OPENGL_ES_API); - if (!ret) - return NULL; - - ret = eglChooseConfig(client->egl_display, config_attribs, - &client->egl_config, 1, &n); - if (!ret || n != 1) - return NULL; - - client->egl_context = eglCreateContext(client->egl_display, - client->egl_config, - EGL_NO_CONTEXT, - context_attribs); - if (!client->egl_context) - return NULL; - - client->surface = wl_compositor_create_surface(client->compositor); - - client->native = wl_egl_window_create(client->surface, - client->width, client->height); - - client->egl_surface = weston_platform_create_egl_surface(client->egl_display, - client->egl_config, - client->native, NULL); - - eglMakeCurrent(client->egl_display, client->egl_surface, - client->egl_surface, client->egl_context); - - wl_egl_window_resize(client->native, - client->width, client->height, 0, 0); - - frame_callback(client, NULL, 0); - - return client; -} - -static void -nested_client_destroy(struct nested_client *client) -{ - eglMakeCurrent(client->egl_display, - EGL_NO_SURFACE, EGL_NO_SURFACE, - EGL_NO_CONTEXT); - - weston_platform_destroy_egl_surface(client->egl_display, - client->egl_surface); - wl_egl_window_destroy(client->native); - - wl_surface_destroy(client->surface); - - if (client->compositor) - wl_compositor_destroy(client->compositor); - - wl_registry_destroy(client->registry); - eglTerminate(client->egl_display); - eglReleaseThread(); - wl_display_flush(client->display); - wl_display_disconnect(client->display); -} - -int -main(int argc, char **argv) -{ - struct nested_client *client; - int ret = 0; - - if (getenv("WAYLAND_SOCKET") == NULL) { - fprintf(stderr, - "must be run by nested, don't run standalone\n"); - return EXIT_FAILURE; - } - - client = nested_client_create(); - if (client == NULL) - return EXIT_FAILURE; - - while (ret != -1) - ret = wl_display_dispatch(client->display); - - nested_client_destroy(client); - - return 0; -} diff --git a/clients/nested.c b/clients/nested.c deleted file mode 100644 index c21ab975..00000000 --- a/clients/nested.c +++ /dev/null @@ -1,1139 +0,0 @@ -/* - * Copyright © 2013 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include -#define WL_HIDE_DEPRECATED -#include - -#include "shared/helpers.h" -#include "shared/xalloc.h" -#include "window.h" - -#include "shared/weston-egl-ext.h" - - -static bool option_blit; - -struct nested { - struct display *display; - struct window *window; - struct widget *widget; - struct wl_display *child_display; - struct task child_task; - - EGLDisplay egl_display; - struct program *texture_program; - - struct wl_list surface_list; - - const struct nested_renderer *renderer; -}; - -struct nested_region { - struct wl_resource *resource; - pixman_region32_t region; -}; - -struct nested_buffer_reference { - struct nested_buffer *buffer; - struct wl_listener destroy_listener; -}; - -struct nested_buffer { - struct wl_resource *resource; - struct wl_signal destroy_signal; - struct wl_listener destroy_listener; - uint32_t busy_count; - - /* A buffer in the parent compositor representing the same - * data. This is created on-demand when the subsurface - * renderer is used */ - struct wl_buffer *parent_buffer; - /* This reference is used to mark when the parent buffer has - * been attached to the subsurface. It will be unrefenced when - * we receive a buffer release event. That way we won't inform - * the client that the buffer is free until the parent - * compositor is also finished with it */ - struct nested_buffer_reference parent_ref; -}; - -struct nested_surface { - struct wl_resource *resource; - struct nested *nested; - EGLImageKHR *image; - struct wl_list link; - - struct wl_list frame_callback_list; - - struct { - /* wl_surface.attach */ - int newly_attached; - struct nested_buffer *buffer; - struct wl_listener buffer_destroy_listener; - - /* wl_surface.frame */ - struct wl_list frame_callback_list; - - /* wl_surface.damage */ - pixman_region32_t damage; - } pending; - - void *renderer_data; -}; - -/* Data used for the blit renderer */ -struct nested_blit_surface { - struct nested_buffer_reference buffer_ref; - GLuint texture; - cairo_surface_t *cairo_surface; -}; - -/* Data used for the subsurface renderer */ -struct nested_ss_surface { - struct widget *widget; - struct wl_surface *surface; - struct wl_subsurface *subsurface; - struct wl_callback *frame_callback; -}; - -struct nested_frame_callback { - struct wl_resource *resource; - struct wl_list link; -}; - -struct nested_renderer { - void (* surface_init)(struct nested_surface *surface); - void (* surface_fini)(struct nested_surface *surface); - void (* render_clients)(struct nested *nested, cairo_t *cr); - void (* surface_attach)(struct nested_surface *surface, - struct nested_buffer *buffer); -}; - -static const struct weston_option nested_options[] = { - { WESTON_OPTION_BOOLEAN, "blit", 'b', &option_blit }, -}; - -static const struct nested_renderer nested_blit_renderer; -static const struct nested_renderer nested_ss_renderer; - -static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC image_target_texture_2d; -static PFNEGLCREATEIMAGEKHRPROC create_image; -static PFNEGLDESTROYIMAGEKHRPROC destroy_image; -static PFNEGLBINDWAYLANDDISPLAYWL bind_display; -static PFNEGLUNBINDWAYLANDDISPLAYWL unbind_display; -static PFNEGLQUERYWAYLANDBUFFERWL query_buffer; -static PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWL create_wayland_buffer_from_image; - -static void -nested_buffer_destroy_handler(struct wl_listener *listener, void *data) -{ - struct nested_buffer *buffer = - container_of(listener, struct nested_buffer, destroy_listener); - - wl_signal_emit(&buffer->destroy_signal, buffer); - - if (buffer->parent_buffer) - wl_buffer_destroy(buffer->parent_buffer); - - free(buffer); -} - -static struct nested_buffer * -nested_buffer_from_resource(struct wl_resource *resource) -{ - struct nested_buffer *buffer; - struct wl_listener *listener; - - listener = - wl_resource_get_destroy_listener(resource, - nested_buffer_destroy_handler); - - if (listener) - return container_of(listener, struct nested_buffer, - destroy_listener); - - buffer = zalloc(sizeof *buffer); - if (buffer == NULL) - return NULL; - - buffer->resource = resource; - wl_signal_init(&buffer->destroy_signal); - buffer->destroy_listener.notify = nested_buffer_destroy_handler; - wl_resource_add_destroy_listener(resource, &buffer->destroy_listener); - - return buffer; -} - -static void -nested_buffer_reference_handle_destroy(struct wl_listener *listener, - void *data) -{ - struct nested_buffer_reference *ref = - container_of(listener, struct nested_buffer_reference, - destroy_listener); - - assert((struct nested_buffer *)data == ref->buffer); - ref->buffer = NULL; -} - -static void -nested_buffer_reference(struct nested_buffer_reference *ref, - struct nested_buffer *buffer) -{ - if (buffer == ref->buffer) - return; - - if (ref->buffer) { - ref->buffer->busy_count--; - if (ref->buffer->busy_count == 0) { - assert(wl_resource_get_client(ref->buffer->resource)); - wl_buffer_send_release(ref->buffer->resource); - } - wl_list_remove(&ref->destroy_listener.link); - } - - if (buffer) { - buffer->busy_count++; - wl_signal_add(&buffer->destroy_signal, - &ref->destroy_listener); - - ref->destroy_listener.notify = - nested_buffer_reference_handle_destroy; - } - - ref->buffer = buffer; -} - -static void -flush_surface_frame_callback_list(struct nested_surface *surface, - uint32_t time) -{ - struct nested_frame_callback *nc, *next; - - wl_list_for_each_safe(nc, next, &surface->frame_callback_list, link) { - wl_callback_send_done(nc->resource, time); - wl_resource_destroy(nc->resource); - } - wl_list_init(&surface->frame_callback_list); - - /* FIXME: toytoolkit need a pre-block handler where we can - * call this. */ - wl_display_flush_clients(surface->nested->child_display); -} - -static void -redraw_handler(struct widget *widget, void *data) -{ - struct nested *nested = data; - cairo_surface_t *surface; - cairo_t *cr; - struct rectangle allocation; - - widget_get_allocation(nested->widget, &allocation); - - surface = window_get_surface(nested->window); - - cr = cairo_create(surface); - cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); - cairo_rectangle(cr, - allocation.x, - allocation.y, - allocation.width, - allocation.height); - cairo_set_source_rgba(cr, 0, 0, 0, 0.8); - cairo_fill(cr); - - nested->renderer->render_clients(nested, cr); - - cairo_destroy(cr); - - cairo_surface_destroy(surface); -} - -static void -keyboard_focus_handler(struct window *window, - struct input *device, void *data) -{ - struct nested *nested = data; - - window_schedule_redraw(nested->window); -} - -static void -handle_child_data(struct task *task, uint32_t events) -{ - struct nested *nested = container_of(task, struct nested, child_task); - struct wl_event_loop *loop; - - loop = wl_display_get_event_loop(nested->child_display); - - wl_event_loop_dispatch(loop, -1); - wl_display_flush_clients(nested->child_display); -} - -struct nested_client { - struct wl_client *client; - pid_t pid; -}; - -static struct nested_client * -launch_client(struct nested *nested, const char *path) -{ - int sv[2]; - pid_t pid; - struct nested_client *client; - - client = malloc(sizeof *client); - if (client == NULL) - return NULL; - - if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) { - fprintf(stderr, "launch_client: " - "socketpair failed while launching '%s': %s\n", - path, strerror(errno)); - free(client); - return NULL; - } - - pid = fork(); - if (pid == -1) { - close(sv[0]); - close(sv[1]); - free(client); - fprintf(stderr, "launch_client: " - "fork failed while launching '%s': %s\n", path, - strerror(errno)); - return NULL; - } - - if (pid == 0) { - int clientfd; - char s[32]; - - /* SOCK_CLOEXEC closes both ends, so we dup the fd to - * get a non-CLOEXEC fd to pass through exec. */ - clientfd = dup(sv[1]); - if (clientfd == -1) { - fprintf(stderr, "compositor: dup failed: %s\n", - strerror(errno)); - exit(-1); - } - - snprintf(s, sizeof s, "%d", clientfd); - setenv("WAYLAND_SOCKET", s, 1); - - execl(path, path, NULL); - - fprintf(stderr, "compositor: executing '%s' failed: %s\n", - path, strerror(errno)); - exit(-1); - } - - close(sv[1]); - - client->client = wl_client_create(nested->child_display, sv[0]); - if (!client->client) { - close(sv[0]); - free(client); - fprintf(stderr, "launch_client: " - "wl_client_create failed while launching '%s'.\n", - path); - return NULL; - } - - client->pid = pid; - - return client; -} - -static void -destroy_surface(struct wl_resource *resource) -{ - struct nested_surface *surface = wl_resource_get_user_data(resource); - struct nested *nested = surface->nested; - struct nested_frame_callback *cb, *next; - - wl_list_for_each_safe(cb, next, - &surface->frame_callback_list, link) - wl_resource_destroy(cb->resource); - - wl_list_for_each_safe(cb, next, - &surface->pending.frame_callback_list, link) - wl_resource_destroy(cb->resource); - - pixman_region32_fini(&surface->pending.damage); - - nested->renderer->surface_fini(surface); - - wl_list_remove(&surface->link); - - free(surface); -} - -static void -surface_destroy(struct wl_client *client, struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static void -surface_attach(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *buffer_resource, int32_t sx, int32_t sy) -{ - struct nested_surface *surface = wl_resource_get_user_data(resource); - struct nested *nested = surface->nested; - struct nested_buffer *buffer = NULL; - - if (buffer_resource) { - int format; - - if (!query_buffer(nested->egl_display, (void *) buffer_resource, - EGL_TEXTURE_FORMAT, &format)) { - wl_resource_post_error(buffer_resource, - WL_DISPLAY_ERROR_INVALID_OBJECT, - "attaching non-egl wl_buffer"); - return; - } - - switch (format) { - case EGL_TEXTURE_RGB: - case EGL_TEXTURE_RGBA: - break; - default: - wl_resource_post_error(buffer_resource, - WL_DISPLAY_ERROR_INVALID_OBJECT, - "invalid format"); - return; - } - - buffer = nested_buffer_from_resource(buffer_resource); - if (buffer == NULL) { - wl_client_post_no_memory(client); - return; - } - } - - if (surface->pending.buffer) - wl_list_remove(&surface->pending.buffer_destroy_listener.link); - - surface->pending.buffer = buffer; - surface->pending.newly_attached = 1; - if (buffer) { - wl_signal_add(&buffer->destroy_signal, - &surface->pending.buffer_destroy_listener); - } -} - -static void -nested_surface_attach(struct nested_surface *surface, - struct nested_buffer *buffer) -{ - struct nested *nested = surface->nested; - - if (surface->image != EGL_NO_IMAGE_KHR) - destroy_image(nested->egl_display, surface->image); - - surface->image = create_image(nested->egl_display, NULL, - EGL_WAYLAND_BUFFER_WL, buffer->resource, - NULL); - if (surface->image == EGL_NO_IMAGE_KHR) { - fprintf(stderr, "failed to create img\n"); - return; - } - - nested->renderer->surface_attach(surface, buffer); -} - -static void -surface_damage(struct wl_client *client, - struct wl_resource *resource, - int32_t x, int32_t y, int32_t width, int32_t height) -{ - struct nested_surface *surface = wl_resource_get_user_data(resource); - - pixman_region32_union_rect(&surface->pending.damage, - &surface->pending.damage, - x, y, width, height); -} - -static void -destroy_frame_callback(struct wl_resource *resource) -{ - struct nested_frame_callback *callback = wl_resource_get_user_data(resource); - - wl_list_remove(&callback->link); - free(callback); -} - -static void -surface_frame(struct wl_client *client, - struct wl_resource *resource, uint32_t id) -{ - struct nested_frame_callback *callback; - struct nested_surface *surface = wl_resource_get_user_data(resource); - - callback = malloc(sizeof *callback); - if (callback == NULL) { - wl_resource_post_no_memory(resource); - return; - } - - callback->resource = wl_resource_create(client, - &wl_callback_interface, 1, id); - wl_resource_set_implementation(callback->resource, NULL, callback, - destroy_frame_callback); - - wl_list_insert(surface->pending.frame_callback_list.prev, - &callback->link); -} - -static void -surface_set_opaque_region(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *region_resource) -{ - fprintf(stderr, "surface_set_opaque_region\n"); -} - -static void -surface_set_input_region(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *region_resource) -{ - fprintf(stderr, "surface_set_input_region\n"); -} - -static void -surface_commit(struct wl_client *client, struct wl_resource *resource) -{ - struct nested_surface *surface = wl_resource_get_user_data(resource); - struct nested *nested = surface->nested; - - /* wl_surface.attach */ - if (surface->pending.newly_attached) - nested_surface_attach(surface, surface->pending.buffer); - - if (surface->pending.buffer) { - wl_list_remove(&surface->pending.buffer_destroy_listener.link); - surface->pending.buffer = NULL; - } - surface->pending.newly_attached = 0; - - /* wl_surface.damage */ - pixman_region32_clear(&surface->pending.damage); - - /* wl_surface.frame */ - wl_list_insert_list(&surface->frame_callback_list, - &surface->pending.frame_callback_list); - wl_list_init(&surface->pending.frame_callback_list); - - /* FIXME: For the subsurface renderer we don't need to - * actually redraw the window. However we do want to cause a - * commit because the subsurface is synchronized. Ideally we - * would just queue the commit */ - window_schedule_redraw(nested->window); -} - -static void -surface_set_buffer_transform(struct wl_client *client, - struct wl_resource *resource, int transform) -{ - fprintf(stderr, "surface_set_buffer_transform\n"); -} - -static const struct wl_surface_interface surface_interface = { - surface_destroy, - surface_attach, - surface_damage, - surface_frame, - surface_set_opaque_region, - surface_set_input_region, - surface_commit, - surface_set_buffer_transform -}; - -static void -surface_handle_pending_buffer_destroy(struct wl_listener *listener, void *data) -{ - struct nested_surface *surface = - container_of(listener, struct nested_surface, - pending.buffer_destroy_listener); - - surface->pending.buffer = NULL; -} - -static void -compositor_create_surface(struct wl_client *client, - struct wl_resource *resource, uint32_t id) -{ - struct nested *nested = wl_resource_get_user_data(resource); - struct nested_surface *surface; - - surface = zalloc(sizeof *surface); - if (surface == NULL) { - wl_resource_post_no_memory(resource); - return; - } - - surface->nested = nested; - - wl_list_init(&surface->frame_callback_list); - - wl_list_init(&surface->pending.frame_callback_list); - surface->pending.buffer_destroy_listener.notify = - surface_handle_pending_buffer_destroy; - pixman_region32_init(&surface->pending.damage); - - display_acquire_window_surface(nested->display, - nested->window, NULL); - - nested->renderer->surface_init(surface); - - display_release_window_surface(nested->display, nested->window); - - surface->resource = - wl_resource_create(client, &wl_surface_interface, 1, id); - - wl_resource_set_implementation(surface->resource, - &surface_interface, surface, - destroy_surface); - - wl_list_insert(nested->surface_list.prev, &surface->link); -} - -static void -destroy_region(struct wl_resource *resource) -{ - struct nested_region *region = wl_resource_get_user_data(resource); - - pixman_region32_fini(®ion->region); - free(region); -} - -static void -region_destroy(struct wl_client *client, struct wl_resource *resource) -{ - wl_resource_destroy(resource); -} - -static void -region_add(struct wl_client *client, struct wl_resource *resource, - int32_t x, int32_t y, int32_t width, int32_t height) -{ - struct nested_region *region = wl_resource_get_user_data(resource); - - pixman_region32_union_rect(®ion->region, ®ion->region, - x, y, width, height); -} - -static void -region_subtract(struct wl_client *client, struct wl_resource *resource, - int32_t x, int32_t y, int32_t width, int32_t height) -{ - struct nested_region *region = wl_resource_get_user_data(resource); - pixman_region32_t rect; - - pixman_region32_init_rect(&rect, x, y, width, height); - pixman_region32_subtract(®ion->region, ®ion->region, &rect); - pixman_region32_fini(&rect); -} - -static const struct wl_region_interface region_interface = { - region_destroy, - region_add, - region_subtract -}; - -static void -compositor_create_region(struct wl_client *client, - struct wl_resource *resource, uint32_t id) -{ - struct nested_region *region; - - region = malloc(sizeof *region); - if (region == NULL) { - wl_resource_post_no_memory(resource); - return; - } - - pixman_region32_init(®ion->region); - - region->resource = - wl_resource_create(client, &wl_region_interface, 1, id); - wl_resource_set_implementation(region->resource, ®ion_interface, - region, destroy_region); -} - -static const struct wl_compositor_interface compositor_interface = { - compositor_create_surface, - compositor_create_region -}; - -static void -compositor_bind(struct wl_client *client, - void *data, uint32_t version, uint32_t id) -{ - struct nested *nested = data; - struct wl_resource *resource; - - resource = wl_resource_create(client, &wl_compositor_interface, - MIN(version, 3), id); - wl_resource_set_implementation(resource, &compositor_interface, - nested, NULL); -} - -static int -nested_init_compositor(struct nested *nested) -{ - const char *extensions; - struct wl_event_loop *loop; - int use_ss_renderer = 0; - int fd, ret; - - wl_list_init(&nested->surface_list); - nested->child_display = wl_display_create(); - loop = wl_display_get_event_loop(nested->child_display); - fd = wl_event_loop_get_fd(loop); - nested->child_task.run = handle_child_data; - display_watch_fd(nested->display, fd, - EPOLLIN, &nested->child_task); - - if (!wl_global_create(nested->child_display, - &wl_compositor_interface, 1, - nested, compositor_bind)) - return -1; - - wl_display_init_shm(nested->child_display); - - nested->egl_display = display_get_egl_display(nested->display); - extensions = eglQueryString(nested->egl_display, EGL_EXTENSIONS); - if (!weston_check_egl_extension(extensions, "EGL_WL_bind_wayland_display")) { - fprintf(stderr, "no EGL_WL_bind_wayland_display extension\n"); - return -1; - } - - bind_display = (void *) eglGetProcAddress("eglBindWaylandDisplayWL"); - unbind_display = (void *) eglGetProcAddress("eglUnbindWaylandDisplayWL"); - create_image = (void *) eglGetProcAddress("eglCreateImageKHR"); - destroy_image = (void *) eglGetProcAddress("eglDestroyImageKHR"); - query_buffer = (void *) eglGetProcAddress("eglQueryWaylandBufferWL"); - image_target_texture_2d = - (void *) eglGetProcAddress("glEGLImageTargetTexture2DOES"); - - ret = bind_display(nested->egl_display, nested->child_display); - if (!ret) { - fprintf(stderr, "failed to bind wl_display\n"); - return -1; - } - - if (display_has_subcompositor(nested->display)) { - const char *func = "eglCreateWaylandBufferFromImageWL"; - const char *ext = "EGL_WL_create_wayland_buffer_from_image"; - - if (weston_check_egl_extension(extensions, ext)) { - create_wayland_buffer_from_image = - (void *) eglGetProcAddress(func); - use_ss_renderer = 1; - } - } - - if (option_blit) - use_ss_renderer = 0; - - if (use_ss_renderer) { - printf("Using subsurfaces to render client surfaces\n"); - nested->renderer = &nested_ss_renderer; - } else { - printf("Using local compositing with blits to " - "render client surfaces\n"); - nested->renderer = &nested_blit_renderer; - } - - return 0; -} - -static struct nested * -nested_create(struct display *display) -{ - struct nested *nested; - - nested = zalloc(sizeof *nested); - if (nested == NULL) - return nested; - - nested->window = window_create(display); - nested->widget = window_frame_create(nested->window, nested); - window_set_title(nested->window, "Wayland Nested"); - window_set_appid(nested->window, - "org.freedesktop.weston.wayland-nested"); - nested->display = display; - - window_set_user_data(nested->window, nested); - widget_set_redraw_handler(nested->widget, redraw_handler); - window_set_keyboard_focus_handler(nested->window, - keyboard_focus_handler); - - nested_init_compositor(nested); - - widget_schedule_resize(nested->widget, 400, 400); - - return nested; -} - -static void -nested_destroy(struct nested *nested) -{ - widget_destroy(nested->widget); - window_destroy(nested->window); - free(nested); -} - -/*** blit renderer ***/ - -static void -blit_surface_init(struct nested_surface *surface) -{ - struct nested_blit_surface *blit_surface = - xzalloc(sizeof *blit_surface); - - glGenTextures(1, &blit_surface->texture); - glBindTexture(GL_TEXTURE_2D, blit_surface->texture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - surface->renderer_data = blit_surface; -} - -static void -blit_surface_fini(struct nested_surface *surface) -{ - struct nested_blit_surface *blit_surface = surface->renderer_data; - - nested_buffer_reference(&blit_surface->buffer_ref, NULL); - - glDeleteTextures(1, &blit_surface->texture); - - free(blit_surface); -} - -static void -blit_frame_callback(void *data, struct wl_callback *callback, uint32_t time) -{ - struct nested *nested = data; - struct nested_surface *surface; - - wl_list_for_each(surface, &nested->surface_list, link) - flush_surface_frame_callback_list(surface, time); - - if (callback) - wl_callback_destroy(callback); -} - -static const struct wl_callback_listener blit_frame_listener = { - blit_frame_callback -}; - -static void -blit_render_clients(struct nested *nested, - cairo_t *cr) -{ - struct nested_surface *s; - struct rectangle allocation; - struct wl_callback *callback; - - widget_get_allocation(nested->widget, &allocation); - - wl_list_for_each(s, &nested->surface_list, link) { - struct nested_blit_surface *blit_surface = s->renderer_data; - - display_acquire_window_surface(nested->display, - nested->window, NULL); - - glBindTexture(GL_TEXTURE_2D, blit_surface->texture); - image_target_texture_2d(GL_TEXTURE_2D, s->image); - - display_release_window_surface(nested->display, - nested->window); - - cairo_set_operator(cr, CAIRO_OPERATOR_OVER); - cairo_set_source_surface(cr, blit_surface->cairo_surface, - allocation.x + 10, - allocation.y + 10); - cairo_rectangle(cr, allocation.x + 10, - allocation.y + 10, - allocation.width - 10, - allocation.height - 10); - - cairo_fill(cr); - } - - callback = wl_surface_frame(window_get_wl_surface(nested->window)); - wl_callback_add_listener(callback, &blit_frame_listener, nested); -} - -static void -blit_surface_attach(struct nested_surface *surface, - struct nested_buffer *buffer) -{ - struct nested *nested = surface->nested; - struct nested_blit_surface *blit_surface = surface->renderer_data; - EGLint width, height; - cairo_device_t *device; - - nested_buffer_reference(&blit_surface->buffer_ref, buffer); - - if (blit_surface->cairo_surface) - cairo_surface_destroy(blit_surface->cairo_surface); - - query_buffer(nested->egl_display, (void *) buffer->resource, - EGL_WIDTH, &width); - query_buffer(nested->egl_display, (void *) buffer->resource, - EGL_HEIGHT, &height); - - device = display_get_cairo_device(nested->display); - blit_surface->cairo_surface = - cairo_gl_surface_create_for_texture(device, - CAIRO_CONTENT_COLOR_ALPHA, - blit_surface->texture, - width, height); -} - -static const struct nested_renderer -nested_blit_renderer = { - .surface_init = blit_surface_init, - .surface_fini = blit_surface_fini, - .render_clients = blit_render_clients, - .surface_attach = blit_surface_attach -}; - -/*** subsurface renderer ***/ - -static void -ss_surface_init(struct nested_surface *surface) -{ - struct nested *nested = surface->nested; - struct wl_compositor *compositor = - display_get_compositor(nested->display); - struct nested_ss_surface *ss_surface = - xzalloc(sizeof *ss_surface); - struct rectangle allocation; - struct wl_region *region; - - ss_surface->widget = - window_add_subsurface(nested->window, - nested, - SUBSURFACE_SYNCHRONIZED); - - widget_set_use_cairo(ss_surface->widget, 0); - - ss_surface->surface = widget_get_wl_surface(ss_surface->widget); - ss_surface->subsurface = widget_get_wl_subsurface(ss_surface->widget); - - /* The toy toolkit gets confused about the pointer position - * when it gets motion events for a subsurface so we'll just - * disable input on it */ - region = wl_compositor_create_region(compositor); - wl_surface_set_input_region(ss_surface->surface, region); - wl_region_destroy(region); - - widget_get_allocation(nested->widget, &allocation); - wl_subsurface_set_position(ss_surface->subsurface, - allocation.x + 10, - allocation.y + 10); - - surface->renderer_data = ss_surface; -} - -static void -ss_surface_fini(struct nested_surface *surface) -{ - struct nested_ss_surface *ss_surface = surface->renderer_data; - - widget_destroy(ss_surface->widget); - - if (ss_surface->frame_callback) - wl_callback_destroy(ss_surface->frame_callback); - - free(ss_surface); -} - -static void -ss_render_clients(struct nested *nested, - cairo_t *cr) -{ - /* The clients are composited by the parent compositor so we - * don't need to do anything here */ -} - -static void -ss_buffer_release(void *data, struct wl_buffer *wl_buffer) -{ - struct nested_buffer *buffer = data; - - nested_buffer_reference(&buffer->parent_ref, NULL); -} - -static struct wl_buffer_listener ss_buffer_listener = { - ss_buffer_release -}; - -static void -ss_frame_callback(void *data, struct wl_callback *callback, uint32_t time) -{ - struct nested_surface *surface = data; - struct nested_ss_surface *ss_surface = surface->renderer_data; - - flush_surface_frame_callback_list(surface, time); - - if (callback) - wl_callback_destroy(callback); - - ss_surface->frame_callback = NULL; -} - -static const struct wl_callback_listener ss_frame_listener = { - ss_frame_callback -}; - -static void -ss_surface_attach(struct nested_surface *surface, - struct nested_buffer *buffer) -{ - struct nested *nested = surface->nested; - struct nested_ss_surface *ss_surface = surface->renderer_data; - struct wl_buffer *parent_buffer; - const pixman_box32_t *rects; - int n_rects, i; - - if (buffer) { - /* Create a representation of the buffer in the parent - * compositor if we haven't already */ - if (buffer->parent_buffer == NULL) { - EGLDisplay *edpy = nested->egl_display; - EGLImageKHR image = surface->image; - - buffer->parent_buffer = - create_wayland_buffer_from_image(edpy, image); - - wl_buffer_add_listener(buffer->parent_buffer, - &ss_buffer_listener, - buffer); - } - - parent_buffer = buffer->parent_buffer; - - /* We'll take a reference to the buffer while the parent - * compositor is using it so that we won't report the release - * event until the parent has also finished with it */ - nested_buffer_reference(&buffer->parent_ref, buffer); - } else { - parent_buffer = NULL; - } - - wl_surface_attach(ss_surface->surface, parent_buffer, 0, 0); - - rects = pixman_region32_rectangles(&surface->pending.damage, &n_rects); - - for (i = 0; i < n_rects; i++) { - const pixman_box32_t *rect = rects + i; - wl_surface_damage(ss_surface->surface, - rect->x1, - rect->y1, - rect->x2 - rect->x1, - rect->y2 - rect->y1); - } - - if (ss_surface->frame_callback) - wl_callback_destroy(ss_surface->frame_callback); - - ss_surface->frame_callback = wl_surface_frame(ss_surface->surface); - wl_callback_add_listener(ss_surface->frame_callback, - &ss_frame_listener, - surface); - - wl_surface_commit(ss_surface->surface); -} - -static const struct nested_renderer -nested_ss_renderer = { - .surface_init = ss_surface_init, - .surface_fini = ss_surface_fini, - .render_clients = ss_render_clients, - .surface_attach = ss_surface_attach -}; - -int -main(int argc, char *argv[]) -{ - struct display *display; - struct nested *nested; - - if (parse_options(nested_options, - ARRAY_LENGTH(nested_options), &argc, argv) > 1) { - printf("Usage: %s [OPTIONS]\n --blit or -b\n", argv[0]); - exit(1); - } - - display = display_create(&argc, argv); - if (display == NULL) { - fprintf(stderr, "failed to create display: %s\n", - strerror(errno)); - return -1; - } - - nested = nested_create(display); - - launch_client(nested, "weston-nested-client"); - - display_run(display); - - nested_destroy(nested); - display_destroy(display); - - return 0; -} diff --git a/clients/window.c b/clients/window.c index 60814a21..0f631a0f 100644 --- a/clients/window.c +++ b/clients/window.c @@ -42,25 +42,6 @@ #include #include -#ifdef HAVE_CAIRO_EGL -#include - -#ifdef USE_CAIRO_GLESV2 -#include -#include -#else -#include -#endif -#include -#include - -#include -#elif !defined(ENABLE_EGL) /* platform.h defines these if EGL is enabled */ -typedef void *EGLDisplay; -typedef void *EGLConfig; -typedef void *EGLContext; -#define EGL_NO_DISPLAY ((EGLDisplay)0) -#endif /* no HAVE_CAIRO_EGL */ #include #ifdef HAVE_XKBCOMMON_COMPOSE @@ -109,10 +90,6 @@ struct display { struct xdg_wm_base *xdg_shell; struct zwp_relative_pointer_manager_v1 *relative_pointer_manager; struct zwp_pointer_constraints_v1 *pointer_constraints; - EGLDisplay dpy; - EGLConfig argb_config; - EGLContext argb_ctx; - cairo_device_t *argb_device; uint32_t serial; int display_fd; @@ -178,18 +155,6 @@ struct toysurface { enum wl_output_transform buffer_transform, int32_t buffer_scale, struct rectangle *server_allocation); - /* - * Make the toysurface current with the given EGL context. - * Returns 0 on success, and negative on failure. - */ - int (*acquire)(struct toysurface *base, EGLContext ctx); - - /* - * Release the toysurface from the EGL context, returning control - * to Cairo. - */ - void (*release)(struct toysurface *base); - /* * Destroy the toysurface, including the Cairo surface, any * backing storage, and the Wayland protocol objects. @@ -543,167 +508,6 @@ buffer_to_surface_size (enum wl_output_transform buffer_transform, int32_t buffe *height /= buffer_scale; } -#ifdef HAVE_CAIRO_EGL - -struct egl_window_surface { - struct toysurface base; - cairo_surface_t *cairo_surface; - struct display *display; - struct wl_surface *surface; - struct wl_egl_window *egl_window; - EGLSurface egl_surface; -}; - -static struct egl_window_surface * -to_egl_window_surface(struct toysurface *base) -{ - return container_of(base, struct egl_window_surface, base); -} - -static cairo_surface_t * -egl_window_surface_prepare(struct toysurface *base, int dx, int dy, - int32_t width, int32_t height, uint32_t flags, - enum wl_output_transform buffer_transform, int32_t buffer_scale) -{ - struct egl_window_surface *surface = to_egl_window_surface(base); - - surface_to_buffer_size (buffer_transform, buffer_scale, &width, &height); - - wl_egl_window_resize(surface->egl_window, width, height, dx, dy); - cairo_gl_surface_set_size(surface->cairo_surface, width, height); - - return cairo_surface_reference(surface->cairo_surface); -} - -static void -egl_window_surface_swap(struct toysurface *base, - enum wl_output_transform buffer_transform, int32_t buffer_scale, - struct rectangle *server_allocation) -{ - struct egl_window_surface *surface = to_egl_window_surface(base); - - cairo_gl_surface_swapbuffers(surface->cairo_surface); - wl_egl_window_get_attached_size(surface->egl_window, - &server_allocation->width, - &server_allocation->height); - - buffer_to_surface_size (buffer_transform, buffer_scale, - &server_allocation->width, - &server_allocation->height); -} - -static int -egl_window_surface_acquire(struct toysurface *base, EGLContext ctx) -{ - struct egl_window_surface *surface = to_egl_window_surface(base); - cairo_device_t *device; - - device = cairo_surface_get_device(surface->cairo_surface); - if (!device) - return -1; - - if (!ctx) { - if (device == surface->display->argb_device) - ctx = surface->display->argb_ctx; - else - assert(0); - } - - cairo_device_flush(device); - cairo_device_acquire(device); - if (!eglMakeCurrent(surface->display->dpy, surface->egl_surface, - surface->egl_surface, ctx)) - fprintf(stderr, "failed to make surface current\n"); - - return 0; -} - -static void -egl_window_surface_release(struct toysurface *base) -{ - struct egl_window_surface *surface = to_egl_window_surface(base); - cairo_device_t *device; - - device = cairo_surface_get_device(surface->cairo_surface); - if (!device) - return; - - if (!eglMakeCurrent(surface->display->dpy, - EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) - fprintf(stderr, "failed to make context current\n"); - - cairo_device_release(device); -} - -static void -egl_window_surface_destroy(struct toysurface *base) -{ - struct egl_window_surface *surface = to_egl_window_surface(base); - struct display *d = surface->display; - - cairo_surface_destroy(surface->cairo_surface); - weston_platform_destroy_egl_surface(d->dpy, surface->egl_surface); - wl_egl_window_destroy(surface->egl_window); - surface->surface = NULL; - - free(surface); -} - -static struct toysurface * -egl_window_surface_create(struct display *display, - struct wl_surface *wl_surface, - uint32_t flags, - struct rectangle *rectangle) -{ - struct egl_window_surface *surface; - - if (display->dpy == EGL_NO_DISPLAY) - return NULL; - - surface = zalloc(sizeof *surface); - if (!surface) - return NULL; - - surface->base.prepare = egl_window_surface_prepare; - surface->base.swap = egl_window_surface_swap; - surface->base.acquire = egl_window_surface_acquire; - surface->base.release = egl_window_surface_release; - surface->base.destroy = egl_window_surface_destroy; - - surface->display = display; - surface->surface = wl_surface; - - surface->egl_window = wl_egl_window_create(surface->surface, - rectangle->width, - rectangle->height); - - surface->egl_surface = - weston_platform_create_egl_surface(display->dpy, - display->argb_config, - surface->egl_window, NULL); - - surface->cairo_surface = - cairo_gl_surface_create_for_egl(display->argb_device, - surface->egl_surface, - rectangle->width, - rectangle->height); - - return &surface->base; -} - -#else - -static struct toysurface * -egl_window_surface_create(struct display *display, - struct wl_surface *wl_surface, - uint32_t flags, - struct rectangle *rectangle) -{ - return NULL; -} - -#endif - struct shm_surface_data { struct wl_buffer *buffer; struct shm_pool *pool; @@ -1157,17 +961,6 @@ shm_surface_swap(struct toysurface *base, surface->current = NULL; } -static int -shm_surface_acquire(struct toysurface *base, EGLContext ctx) -{ - return -1; -} - -static void -shm_surface_release(struct toysurface *base) -{ -} - static void shm_surface_destroy(struct toysurface *base) { @@ -1190,8 +983,6 @@ shm_surface_create(struct display *display, struct wl_surface *wl_surface, surface = xzalloc(sizeof *surface); surface->base.prepare = shm_surface_prepare; surface->base.swap = shm_surface_swap; - surface->base.acquire = shm_surface_acquire; - surface->base.release = shm_surface_release; surface->base.destroy = shm_surface_destroy; surface->display = display; @@ -1461,15 +1252,6 @@ surface_create_surface(struct surface *surface, uint32_t flags) struct display *display = surface->window->display; struct rectangle allocation = surface->allocation; - if (!surface->toysurface && display->dpy && - surface->buffer_type == WINDOW_BUFFER_TYPE_EGL_WINDOW) { - surface->toysurface = - egl_window_surface_create(display, - surface->surface, - flags, - &allocation); - } - if (!surface->toysurface) surface->toysurface = shm_surface_create(display, surface->surface, @@ -5257,11 +5039,6 @@ surface_create(struct window *window) static enum window_buffer_type get_preferred_buffer_type(struct display *display) { -#ifdef HAVE_CAIRO_EGL - if (display->argb_device && !getenv("TOYTOOLKIT_NO_EGL")) - return WINDOW_BUFFER_TYPE_EGL_WINDOW; -#endif - return WINDOW_BUFFER_TYPE_SHM; } @@ -6123,90 +5900,6 @@ static const struct wl_registry_listener registry_listener = { registry_handle_global_remove }; -#ifdef HAVE_CAIRO_EGL -static int -init_egl(struct display *d) -{ - EGLint major, minor; - EGLint n; - -#ifdef USE_CAIRO_GLESV2 -# define GL_BIT EGL_OPENGL_ES2_BIT -#else -# define GL_BIT EGL_OPENGL_BIT -#endif - - static const EGLint argb_cfg_attribs[] = { - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - EGL_RED_SIZE, 1, - EGL_GREEN_SIZE, 1, - EGL_BLUE_SIZE, 1, - EGL_ALPHA_SIZE, 1, - EGL_DEPTH_SIZE, 1, - EGL_RENDERABLE_TYPE, GL_BIT, - EGL_NONE - }; - -#ifdef USE_CAIRO_GLESV2 - static const EGLint context_attribs[] = { - EGL_CONTEXT_CLIENT_VERSION, 2, - EGL_NONE - }; - EGLint api = EGL_OPENGL_ES_API; -#else - EGLint *context_attribs = NULL; - EGLint api = EGL_OPENGL_API; -#endif - - d->dpy = - weston_platform_get_egl_display(EGL_PLATFORM_WAYLAND_KHR, - d->display, NULL); - - if (!eglInitialize(d->dpy, &major, &minor)) { - fprintf(stderr, "failed to initialize EGL\n"); - return -1; - } - - if (!eglBindAPI(api)) { - fprintf(stderr, "failed to bind EGL client API\n"); - return -1; - } - - if (!eglChooseConfig(d->dpy, argb_cfg_attribs, - &d->argb_config, 1, &n) || n != 1) { - fprintf(stderr, "failed to choose argb EGL config\n"); - return -1; - } - - d->argb_ctx = eglCreateContext(d->dpy, d->argb_config, - EGL_NO_CONTEXT, context_attribs); - if (d->argb_ctx == NULL) { - fprintf(stderr, "failed to create EGL context\n"); - return -1; - } - - d->argb_device = cairo_egl_device_create(d->dpy, d->argb_ctx); - if (cairo_device_status(d->argb_device) != CAIRO_STATUS_SUCCESS) { - fprintf(stderr, "failed to get cairo EGL argb device\n"); - return -1; - } - - return 0; -} - -static void -fini_egl(struct display *display) -{ - cairo_device_destroy(display->argb_device); - - eglMakeCurrent(display->dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, - EGL_NO_CONTEXT); - - eglTerminate(display->dpy); - eglReleaseThread(); -} -#endif - static void init_dummy_surface(struct display *display) { @@ -6312,12 +6005,6 @@ display_create(int *argc, char *argv[]) return NULL; } -#ifdef HAVE_CAIRO_EGL - if (init_egl(d) < 0) - fprintf(stderr, "EGL does not seem to work, " - "falling back to software rendering and wl_shm.\n"); -#endif - create_cursors(d); d->theme = theme_create(); @@ -6376,10 +6063,6 @@ display_destroy(struct display *display) theme_destroy(display->theme); destroy_cursors(display); -#ifdef HAVE_CAIRO_EGL - if (display->argb_device) - fini_egl(display); -#endif if (display->relative_pointer_manager) zwp_relative_pointer_manager_v1_destroy(display->relative_pointer_manager); @@ -6445,12 +6128,6 @@ display_has_subcompositor(struct display *display) return display->subcompositor != NULL; } -cairo_device_t * -display_get_cairo_device(struct display *display) -{ - return display->argb_device; -} - struct output * display_get_output(struct display *display) { @@ -6472,12 +6149,6 @@ display_get_serial(struct display *display) return display->serial; } -EGLDisplay -display_get_egl_display(struct display *d) -{ - return d->dpy; -} - struct wl_data_source * display_create_data_source(struct display *display) { @@ -6487,38 +6158,6 @@ display_create_data_source(struct display *display) return NULL; } -EGLConfig -display_get_argb_egl_config(struct display *d) -{ - return d->argb_config; -} - -int -display_acquire_window_surface(struct display *display, - struct window *window, - EGLContext ctx) -{ - struct surface *surface = window->main_surface; - - if (surface->buffer_type != WINDOW_BUFFER_TYPE_EGL_WINDOW) - return -1; - - widget_get_cairo_surface(window->main_surface->widget); - return surface->toysurface->acquire(surface->toysurface, ctx); -} - -void -display_release_window_surface(struct display *display, - struct window *window) -{ - struct surface *surface = window->main_surface; - - if (surface->buffer_type != WINDOW_BUFFER_TYPE_EGL_WINDOW) - return; - - surface->toysurface->release(surface->toysurface); -} - void display_defer(struct display *display, struct task *task) { diff --git a/clients/window.h b/clients/window.h index 7cf82da1..22fd7e23 100644 --- a/clients/window.h +++ b/clients/window.h @@ -71,9 +71,6 @@ display_get_display(struct display *display); int display_has_subcompositor(struct display *display); -cairo_device_t * -display_get_cairo_device(struct display *display); - struct wl_compositor * display_get_compositor(struct display *display); @@ -114,22 +111,6 @@ display_set_output_configure_handler(struct display *display, struct wl_data_source * display_create_data_source(struct display *display); -#ifdef EGL_NO_DISPLAY -EGLDisplay -display_get_egl_display(struct display *d); - -EGLConfig -display_get_argb_egl_config(struct display *d); - -int -display_acquire_window_surface(struct display *display, - struct window *window, - EGLContext ctx); -void -display_release_window_surface(struct display *display, - struct window *window); -#endif - #define SURFACE_OPAQUE 0x01 #define SURFACE_SHM 0x02 @@ -416,7 +397,6 @@ struct wl_subsurface * widget_get_wl_subsurface(struct widget *widget); enum window_buffer_type { - WINDOW_BUFFER_TYPE_EGL_WINDOW, WINDOW_BUFFER_TYPE_SHM, }; From f9e54ab2f80ab1c3c3c1394e3885dd33dcd731a8 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 24 Jun 2022 11:41:33 +0100 Subject: [PATCH 435/609] weston-terminal: Fix some egregious memory leaks Some of the Pango bits still leak, but this takes care of a lot of the worst. Signed-off-by: Daniel Stone --- clients/terminal.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/clients/terminal.c b/clients/terminal.c index b7537aad..b02d7d03 100644 --- a/clients/terminal.c +++ b/clients/terminal.c @@ -3023,13 +3023,19 @@ static void terminal_destroy(struct terminal *terminal) { display_unwatch_fd(terminal->display, terminal->master); - window_destroy(terminal->window); close(terminal->master); + + widget_destroy(terminal->widget); + window_destroy(terminal->window); + wl_list_remove(&terminal->link); if (wl_list_empty(&terminal_list)) display_exit(terminal->display); + free(terminal->data); + free(terminal->data_attr); + free(terminal->tab_ruler); free(terminal->title); free(terminal); } @@ -3128,7 +3134,7 @@ static const struct weston_option terminal_options[] = { int main(int argc, char *argv[]) { struct display *d; - struct terminal *terminal; + struct terminal *terminal, *tmp; const char *config_file; struct sigaction sigpipe; struct weston_config *config; @@ -3183,5 +3189,9 @@ int main(int argc, char *argv[]) display_run(d); + wl_list_for_each_safe(terminal, tmp, &terminal_list, link) + terminal_destroy(terminal); + display_destroy(d); + return 0; } From fc4fb9fb92948da164b91cf1a05b6106921c1335 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 24 Jun 2022 12:45:11 +0100 Subject: [PATCH 436/609] weston-terminal: Make exit path a little more obvious Signed-off-by: Daniel Stone --- clients/terminal.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/clients/terminal.c b/clients/terminal.c index b02d7d03..4f873f8a 100644 --- a/clients/terminal.c +++ b/clients/terminal.c @@ -3054,10 +3054,12 @@ io_handler(struct task *task, uint32_t events) } len = read(terminal->master, buffer, sizeof buffer); - if (len < 0) + if (len < 0) { terminal_destroy(terminal); - else - terminal_data(terminal, buffer, len); + return; + } + + terminal_data(terminal, buffer, len); } static int From d43931080e281401daaccc7c0cecf4e3c739f5f9 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 24 Jun 2022 12:45:35 +0100 Subject: [PATCH 437/609] weston-terminal: Don't leak Cairo fonts The docs say they're ours to unref. Signed-off-by: Daniel Stone --- clients/terminal.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clients/terminal.c b/clients/terminal.c index 4f873f8a..545ddd4f 100644 --- a/clients/terminal.c +++ b/clients/terminal.c @@ -3025,6 +3025,9 @@ terminal_destroy(struct terminal *terminal) display_unwatch_fd(terminal->display, terminal->master); close(terminal->master); + cairo_scaled_font_destroy(terminal->font_bold); + cairo_scaled_font_destroy(terminal->font_normal); + widget_destroy(terminal->widget); window_destroy(terminal->window); From 29c3422e0599236405c8b8507136e07652436bd9 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 24 Jun 2022 12:46:32 +0100 Subject: [PATCH 438/609] cairo-util: Don't leak Pango objects Rework PangoCairo context initialisation, so we don't leak either the Pango layout, or any of the derived objects it creates. Signed-off-by: Daniel Stone --- shared/cairo-util.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/shared/cairo-util.c b/shared/cairo-util.c index a22610c0..df4384dc 100644 --- a/shared/cairo-util.c +++ b/shared/cairo-util.c @@ -490,16 +490,26 @@ theme_destroy(struct theme *t) static PangoLayout * create_layout(cairo_t *cr, const char *title) { + PangoFontMap *fontmap; + PangoContext *context; PangoLayout *layout; PangoFontDescription *desc; - layout = pango_cairo_create_layout(cr); + fontmap = pango_cairo_font_map_new(); + context = pango_font_map_create_context(fontmap); + g_object_unref(fontmap); + pango_cairo_font_map_set_default(NULL); + pango_cairo_update_context(cr, context); + layout = pango_layout_new(context); + g_object_unref(context); + if (title) { pango_layout_set_text(layout, title, -1); desc = pango_font_description_from_string("sans-serif Bold 10"); pango_layout_set_font_description(layout, desc); pango_font_description_free(desc); } + pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT); pango_layout_set_auto_dir (layout, FALSE); @@ -567,6 +577,8 @@ theme_render_frame(struct theme *t, PangoLayout *title_layout; PangoRectangle logical; + cairo_save(cr); + title_layout = create_layout(cr, title); pango_layout_get_pixel_extents (title_layout, NULL, &logical); @@ -608,6 +620,11 @@ theme_render_frame(struct theme *t, cairo_set_source_rgb(cr, 0.4, 0.4, 0.4); SHOW_TEXT(cr); } + +#ifdef HAVE_PANGO + cairo_restore(cr); + g_object_unref(title_layout); +#endif } } From c55a14206db5ac604a8fe4717e9147bbf6407d02 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 24 Jun 2022 12:47:47 +0100 Subject: [PATCH 439/609] cairo-util: Clean up more Cairo detritus; almost all of it Pango, Cairo, and fontconfig, all want to leave thread-global data hanging around in order to maintain a cache. Try to clean up as much of it as we possibly can on exit, apart from the Pango language string which appears to be unfreeable, so has been added to LSan suppressions. Signed-off-by: Daniel Stone --- .gitlab-ci/leak-sanitizer.supp | 3 +++ clients/window.c | 1 + shared/cairo-util.c | 15 +++++++++++++++ shared/cairo-util.h | 3 +++ shared/meson.build | 5 +++-- 5 files changed, 25 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci/leak-sanitizer.supp b/.gitlab-ci/leak-sanitizer.supp index a5561804..87764945 100644 --- a/.gitlab-ci/leak-sanitizer.supp +++ b/.gitlab-ci/leak-sanitizer.supp @@ -3,3 +3,6 @@ # Cairo internal leaks from weston-keyboard leak:cairo_select_font_face leak:cairo_text_extents + +# Pango thread-global state (not destroyable?) +leak:pango_language_get_default diff --git a/clients/window.c b/clients/window.c index 0f631a0f..52713488 100644 --- a/clients/window.c +++ b/clients/window.c @@ -6063,6 +6063,7 @@ display_destroy(struct display *display) theme_destroy(display->theme); destroy_cursors(display); + cleanup_after_cairo(); if (display->relative_pointer_manager) zwp_relative_pointer_manager_v1_destroy(display->relative_pointer_manager); diff --git a/shared/cairo-util.c b/shared/cairo-util.c index df4384dc..30670939 100644 --- a/shared/cairo-util.c +++ b/shared/cairo-util.c @@ -40,6 +40,7 @@ #include #ifdef HAVE_PANGO +#include #include #endif @@ -681,3 +682,17 @@ theme_get_location(struct theme *t, int x, int y, return location; } + +/** Cleanup static Cairo/Pango data + * + * Using Cairo, Pango, PangoCairo, and fontconfig, ends up leaving a trail of + * thread-cached data behind us. Clean up what we can. + */ +void +cleanup_after_cairo(void) +{ + cairo_debug_reset_static_data(); +#ifdef HAVE_PANGO + FcFini(); +#endif +} diff --git a/shared/cairo-util.h b/shared/cairo-util.h index 17f7b4f3..a7d4329c 100644 --- a/shared/cairo-util.h +++ b/shared/cairo-util.h @@ -234,4 +234,7 @@ frame_double_touch_up(struct frame *frame, void *data, int32_t id); void frame_repaint(struct frame *frame, cairo_t *cr); +void +cleanup_after_cairo(void); + #endif diff --git a/shared/meson.build b/shared/meson.build index 9b5d7a50..d7936fe5 100644 --- a/shared/meson.build +++ b/shared/meson.build @@ -57,10 +57,11 @@ deps_cairo_shared = [ dep_pango = dependency('pango', required: false) dep_pangocairo = dependency('pangocairo', required: false) +dep_fontconfig = dependency('fontconfig', required: false) dep_glib = dependency('glib-2.0', version: '>= 2.36', required: false) -if dep_pango.found() and dep_pangocairo.found() and dep_glib.found() - deps_cairo_shared += [ dep_pango, dep_pangocairo, dep_glib ] +if dep_pango.found() and dep_pangocairo.found() and dep_fontconfig.found() and dep_glib.found() + deps_cairo_shared += [ dep_pango, dep_pangocairo, dep_fontconfig, dep_glib ] config_h.set('HAVE_PANGO', '1') endif From 5374d55f6ab23d47f9c1d3dd2120e6eb8ede196f Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 24 Jun 2022 12:51:49 +0100 Subject: [PATCH 440/609] safe-signal-test: Fix leak Oops. Signed-off-by: Daniel Stone --- tests/safe-signal-test.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/safe-signal-test.c b/tests/safe-signal-test.c index 3248f0f7..4b5ac643 100644 --- a/tests/safe-signal-test.c +++ b/tests/safe-signal-test.c @@ -88,4 +88,5 @@ TEST(real_usecase_standalone) add_destroy_listener(st_new); destroy_test_surface(st); + destroy_test_surface(st_new); } From 2ebdf0a7f3558833ff5156eb27a894f577d180c8 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 24 Jun 2022 12:52:01 +0100 Subject: [PATCH 441/609] subsurface-shot-test: Don't leak replaced buffer Destroy the buffer we've overwritten. Signed-off-by: Daniel Stone --- tests/subsurface-shot-test.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/subsurface-shot-test.c b/tests/subsurface-shot-test.c index 16534497..df7d0d3c 100644 --- a/tests/subsurface-shot-test.c +++ b/tests/subsurface-shot-test.c @@ -287,6 +287,7 @@ TEST(subsurface_empty_mapping) struct wl_subcompositor *subco; struct wp_viewporter *viewporter; struct buffer *bufs[3] = { 0 }; + struct buffer *buf_tmp; struct wl_surface *surf[3] = { 0 }; struct wl_subsurface *sub[3] = { 0 }; struct wp_viewport *viewport; @@ -385,7 +386,9 @@ TEST(subsurface_empty_mapping) fail += check_screen(client, "subsurface_empty_mapping", 0, &clip, 10); /* remap middle surface to ensure recursive mapping */ + buf_tmp = bufs[1]; bufs[1] = surface_commit_color(client, surf[1], &blue, 100, 100); + buffer_destroy(buf_tmp); fail += check_screen(client, "subsurface_empty_mapping", 1, &clip, 11); From 213195c4dbfc0dc72276a2bc260c89125a212ddc Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 24 Jun 2022 13:38:35 +0100 Subject: [PATCH 442/609] tests: Don't leak args when skipping tests We treat the argv we pass into the compositor as its to mangle, just as it is free to do so for POSIX argv. To support this, we stash argv away and free the saved copy later so as to not leak. This works perfectly, except when we never call the compositor at all, and have no saved array to free. Make sure we free the args in this case, which can be seen as a leak of any generated args when a test skips on preflight checks, e.g. drm-smoke when not running in CI. Signed-off-by: Daniel Stone --- tests/weston-test-fixture-compositor.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/weston-test-fixture-compositor.c b/tests/weston-test-fixture-compositor.c index 420ab8f1..228bb48e 100644 --- a/tests/weston-test-fixture-compositor.c +++ b/tests/weston-test-fixture-compositor.c @@ -93,6 +93,11 @@ prog_args_fini(struct prog_args *p) { int i; + /* If our args have never been saved, then we haven't called the + * compositor, but we still need to free the args, not leak them. */ + if (!p->saved) + prog_args_save(p); + if (p->saved) { for (i = 0; i < p->argc; i++) free(p->saved[i]); From 0d385ffacbf51ceedea96b0d313263f45a2520ed Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 13 Jun 2022 14:51:44 +0300 Subject: [PATCH 443/609] tests/alpha-blending: move unpremult to color_util More tests are going to need this. The API is changed to work by copy in and copy out to match the other color_util API. Hopefully this makes the caller code easier to read. Signed-off-by: Pekka Paalanen --- tests/alpha-blending-test.c | 20 +++----------------- tests/color_util.c | 18 ++++++++++++++++++ tests/color_util.h | 3 +++ 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/tests/alpha-blending-test.c b/tests/alpha-blending-test.c index 3f7317f3..2ec2b255 100644 --- a/tests/alpha-blending-test.c +++ b/tests/alpha-blending-test.c @@ -94,20 +94,6 @@ premult_color(uint32_t a, uint32_t r, uint32_t g, uint32_t b) return c; } -static void -unpremult_float(struct color_float *cf) -{ - int i; - - if (cf->a == 0.0f) { - for (i = 0; i < COLOR_CHAN_NUM; i++) - cf->rgb[i] = 0.0f; - } else { - for (i = 0; i < COLOR_CHAN_NUM; i++) - cf->rgb[i] /= cf->a; - } -} - static void fill_alpha_pattern(struct buffer *buf) { @@ -205,9 +191,9 @@ verify_sRGB_blend_a8r8g8b8(uint32_t bg32, uint32_t fg32, uint32_t dst32, bool ok = true; int i; - unpremult_float(&bg); - unpremult_float(&fg); - unpremult_float(&dst); + bg = color_float_unpremult(bg); + fg = color_float_unpremult(fg); + dst = color_float_unpremult(dst); if (space == BLEND_LINEAR) { sRGB_linearize(&bg); diff --git a/tests/color_util.c b/tests/color_util.c index 4bbd4071..c1bf622d 100644 --- a/tests/color_util.c +++ b/tests/color_util.c @@ -280,6 +280,24 @@ sRGB_delinearize(struct color_float *cf) *cf = color_float_apply_curve(TRANSFER_FN_SRGB_EOTF_INVERSE, *cf); } +struct color_float +color_float_unpremult(struct color_float in) +{ + static const struct color_float transparent = { + .r = 0.0f, .g = 0.0f, .b = 0.0f, .a = 0.0f, + }; + struct color_float out; + int i; + + if (in.a == 0.0f) + return transparent; + + for (i = 0; i < COLOR_CHAN_NUM; i++) + out.rgb[i] = in.rgb[i] / in.a; + out.a = in.a; + return out; +} + /* * Returns the result of the matrix-vector multiplication mat * c. */ diff --git a/tests/color_util.h b/tests/color_util.h index e9d931cd..ffcbd7a8 100644 --- a/tests/color_util.h +++ b/tests/color_util.h @@ -107,6 +107,9 @@ process_pixel_using_pipeline(enum transfer_fn pre_curve, const struct color_float *in, struct color_float *out); +struct color_float +color_float_unpremult(struct color_float in); + struct color_float color_float_apply_matrix(const struct lcmsMAT3 *mat, struct color_float c); From 9026293bff9942a212e1b6c5c906d7ae24065aa3 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 13 Jun 2022 17:31:18 +0300 Subject: [PATCH 444/609] tests: change rgb_diff_stat printing Seems it will be common to print all four min/max/avg sets of errors, so move the printing code into a shared place. While 0.0-1.0 is the natural range for color values, people are often accustomed to working with 8-bit or even 10-bit pixel values. An error of +/- 1 in 8-bit is more intuitive than +/- 0.004 in floating-point. Hence 'scaling_bits' is added so the caller can determine the value scaling. This will scale both the reported error numbers, and the recorded error positions (rgb-tuples), so they are all comparable. I'm happy to get rid of those two macros as well. Signed-off-by: Pekka Paalanen --- tests/color-icc-output-test.c | 12 +-------- tests/color_util.c | 46 ++++++++++++++++++++++++++--------- tests/color_util.h | 7 +++--- 3 files changed, 40 insertions(+), 25 deletions(-) diff --git a/tests/color-icc-output-test.c b/tests/color-icc-output-test.c index 429a7ce0..2f2de05e 100644 --- a/tests/color-icc-output-test.c +++ b/tests/color-icc-output-test.c @@ -189,8 +189,6 @@ test_roundtrip(uint8_t r, uint8_t g, uint8_t b, cmsPipeline *pip, static void roundtrip_verification(cmsPipeline *DToB, cmsPipeline *BToD, float tolerance) { - const char *const chan_name[COLOR_CHAN_NUM] = { "r", "g", "b" }; - unsigned i; unsigned r, g, b; struct rgb_diff_stat stat = {}; cmsPipeline *pip; @@ -211,15 +209,7 @@ roundtrip_verification(cmsPipeline *DToB, cmsPipeline *BToD, float tolerance) cmsPipelineFree(pip); - testlog("DToB->BToD roundtrip error statistics (%u samples):\n", - stat.two_norm.count); - for (i = 0; i < COLOR_CHAN_NUM; i++) { - testlog(" ch %s error:\n", chan_name[i]); - scalar_stat_print_rgb8bit(&stat.rgb[i]); - } - testlog(" Two-norm error:\n"); - scalar_stat_print_rgb8bit(&stat.two_norm); - + rgb_diff_stat_print(&stat, "DToB->BToD roundtrip", 8); assert(stat.two_norm.max < tolerance); } diff --git a/tests/color_util.c b/tests/color_util.c index c1bf622d..acbd040a 100644 --- a/tests/color_util.c +++ b/tests/color_util.c @@ -396,17 +396,6 @@ scalar_stat_avg(const struct scalar_stat *stat) return stat->sum / stat->count; } -#define RGB888_FMT "(%3u, %3u, %3u)" -#define RGB888_VAL(cf) (unsigned)round((cf).r * 255.0), (unsigned)round((cf).g * 255.0), (unsigned)round((cf).b * 255.0) - -void -scalar_stat_print_rgb8bit(const struct scalar_stat *stat) -{ - testlog(" min %8.5f at " RGB888_FMT "\n", stat->min, RGB888_VAL(stat->min_pos)); - testlog(" max %8.5f at " RGB888_FMT "\n", stat->max, RGB888_VAL(stat->max_pos)); - testlog(" avg %8.5f\n", scalar_stat_avg(stat)); -} - void scalar_stat_print_float(const struct scalar_stat *stat) { @@ -415,6 +404,41 @@ scalar_stat_print_float(const struct scalar_stat *stat) testlog(" avg %11.5g\n", scalar_stat_avg(stat)); } +static void +print_stat_at_pos(const char *lim, double val, struct color_float pos, double scale) +{ + testlog(" %s %8.5f at rgb(%7.2f, %7.2f, %7.2f)\n", + lim, val * scale, pos.r * scale, pos.g * scale, pos.b * scale); +} + +static void +print_rgb_at_pos(const struct scalar_stat *stat, double scale) +{ + print_stat_at_pos("min", stat->min, stat->min_pos, scale); + print_stat_at_pos("max", stat->max, stat->max_pos, scale); + testlog(" avg %8.5f\n", scalar_stat_avg(stat) * scale); +} + +void +rgb_diff_stat_print(const struct rgb_diff_stat *stat, + const char *title, unsigned scaling_bits) +{ + const char *const chan_name[COLOR_CHAN_NUM] = { "r", "g", "b" }; + float scale = exp2f(scaling_bits) - 1.0f; + unsigned i; + + assert(scaling_bits > 0); + + testlog("%s error statistics, %u samples, value range 0.0 - %.1f:\n", + title, stat->two_norm.count, scale); + for (i = 0; i < COLOR_CHAN_NUM; i++) { + testlog(" ch %s (signed):\n", chan_name[i]); + print_rgb_at_pos(&stat->rgb[i], scale); + } + testlog(" rgb two-norm:\n"); + print_rgb_at_pos(&stat->two_norm, scale); +} + void rgb_diff_stat_update(struct rgb_diff_stat *stat, struct color_float *ref, struct color_float *val) diff --git a/tests/color_util.h b/tests/color_util.h index ffcbd7a8..2278c874 100644 --- a/tests/color_util.h +++ b/tests/color_util.h @@ -144,12 +144,13 @@ scalar_stat_update(struct scalar_stat *stat, double val, struct color_float *pos float scalar_stat_avg(const struct scalar_stat *stat); -void -scalar_stat_print_rgb8bit(const struct scalar_stat *stat); - void scalar_stat_print_float(const struct scalar_stat *stat); void rgb_diff_stat_update(struct rgb_diff_stat *stat, struct color_float *ref, struct color_float *val); + +void +rgb_diff_stat_print(const struct rgb_diff_stat *stat, + const char *title, unsigned scaling_bits); From f31d26669dffbecadb674a3d059b91364487ae4f Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 14 Jun 2022 16:20:41 +0300 Subject: [PATCH 445/609] tests/color_util: constify *_stat_update() These arguments are not meant to be changed, and a new test will need this const. Signed-off-by: Pekka Paalanen --- tests/color_util.c | 7 +++++-- tests/color_util.h | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/color_util.c b/tests/color_util.c index acbd040a..4b1471d6 100644 --- a/tests/color_util.c +++ b/tests/color_util.c @@ -374,7 +374,9 @@ lcmsMAT3_invert(struct lcmsMAT3 *result, const struct lcmsMAT3 *mat) } void -scalar_stat_update(struct scalar_stat *stat, double val, struct color_float *pos) +scalar_stat_update(struct scalar_stat *stat, + double val, + const struct color_float *pos) { if (stat->count == 0 || stat->min > val) { stat->min = val; @@ -441,7 +443,8 @@ rgb_diff_stat_print(const struct rgb_diff_stat *stat, void rgb_diff_stat_update(struct rgb_diff_stat *stat, - struct color_float *ref, struct color_float *val) + const struct color_float *ref, + const struct color_float *val) { unsigned i; double ssd = 0.0; diff --git a/tests/color_util.h b/tests/color_util.h index 2278c874..724ff04a 100644 --- a/tests/color_util.h +++ b/tests/color_util.h @@ -139,7 +139,9 @@ struct rgb_diff_stat { }; void -scalar_stat_update(struct scalar_stat *stat, double val, struct color_float *pos); +scalar_stat_update(struct scalar_stat *stat, + double val, + const struct color_float *pos); float scalar_stat_avg(const struct scalar_stat *stat); @@ -149,7 +151,8 @@ scalar_stat_print_float(const struct scalar_stat *stat); void rgb_diff_stat_update(struct rgb_diff_stat *stat, - struct color_float *ref, struct color_float *val); + const struct color_float *ref, + const struct color_float *val); void rgb_diff_stat_print(const struct rgb_diff_stat *stat, From 912ea2cb20d9e8ad7dd066ffa0a1bb2b8e1ca80e Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 17 Jun 2022 14:44:03 +0300 Subject: [PATCH 446/609] tests: add scalar_stat dumps The new field in struct scalar_stat allows recording all tested values into a file. This is intended to replace ad hoc dumping code like in alpha-blending-test.c. To make it easy to set up, also offer a helper to open a writable file whose name consists of a custom prefix and test name. Signed-off-by: Pekka Paalanen --- tests/color_util.c | 5 +++++ tests/color_util.h | 12 ++++++++++++ tests/weston-test-client-helper.c | 31 +++++++++++++++++++++++++++++++ tests/weston-test-client-helper.h | 3 +++ 4 files changed, 51 insertions(+) diff --git a/tests/color_util.c b/tests/color_util.c index 4b1471d6..da2f5c6b 100644 --- a/tests/color_util.c +++ b/tests/color_util.c @@ -390,6 +390,11 @@ scalar_stat_update(struct scalar_stat *stat, stat->sum += val; stat->count++; + + if (stat->dump) { + fprintf(stat->dump, "%.8g %.5g %.5g %.5g %.5g\n", + val, pos->r, pos->g, pos->b, pos->a); + } } float diff --git a/tests/color_util.h b/tests/color_util.h index 724ff04a..1bb7297a 100644 --- a/tests/color_util.h +++ b/tests/color_util.h @@ -28,6 +28,7 @@ #include #include +#include enum color_chan_index { COLOR_CHAN_R = 0, @@ -131,6 +132,17 @@ struct scalar_stat { double sum; unsigned count; + + /** Debug dump into file + * + * Initialize this to a writable file to get a record of all values + * ever fed through this statistics accumulator. The file shall be + * text with one value and its position per line: + * val pos.r pos.g pos.b pos.a + * + * Set to NULL to not record. + */ + FILE *dump; }; struct rgb_diff_stat { diff --git a/tests/weston-test-client-helper.c b/tests/weston-test-client-helper.c index 89713e17..ca49a04d 100644 --- a/tests/weston-test-client-helper.c +++ b/tests/weston-test-client-helper.c @@ -38,6 +38,7 @@ #include "test-config.h" #include "shared/os-compatibility.h" +#include "shared/string-helpers.h" #include "shared/xalloc.h" #include #include "weston-test-client-helper.h" @@ -1144,6 +1145,36 @@ image_filename(const char *basename) return filename; } +/** Open a writable file + * + * \param suffix Custom file name suffix. + * \return FILE pointer, or NULL on failure. + * + * The file name consists of output path, test name, and the given suffix. + * If environment variable WESTON_TEST_OUTPUT_PATH is set, it is used as the + * directory path, otherwise the current directory is used. + * + * The file will be writable. If it exists, it is truncated, otherwise it is + * created. Failures are logged. + */ +FILE * +fopen_dump_file(const char *suffix) +{ + char *fname; + FILE *fp; + + str_printf(&fname, "%s/%s-%s.txt", output_path(), + get_test_name(), suffix); + fp = fopen(fname, "w"); + if (!fp) { + testlog("Error: failed to open file '%s' for writing: %s\n", + fname, strerror(errno)); + } + free(fname); + + return fp; +} + struct format_map_entry { cairo_format_t cairo; pixman_format_code_t pixman; diff --git a/tests/weston-test-client-helper.h b/tests/weston-test-client-helper.h index dd20d514..34183249 100644 --- a/tests/weston-test-client-helper.h +++ b/tests/weston-test-client-helper.h @@ -250,6 +250,9 @@ screenshot_reference_filename(const char *basename, uint32_t seq); char * image_filename(const char *basename); +FILE * +fopen_dump_file(const char *suffix); + bool check_images_match(pixman_image_t *img_a, pixman_image_t *img_b, const struct rectangle *clip, From 3f605424056ffd77df62b51e06411ce651934fb6 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 20 Jun 2022 13:14:13 +0300 Subject: [PATCH 447/609] tests/color_util: make rgb_diff_stat pos explicit The recently introduced rgb_diff_stat value dumping feature logs the "position" where the value or error was measured. The reference value was used as the position, but the problem with the reference value is that it is an output value and not an input value. Therefore mapping that back to which input values promoted the error is not easy. Fix that problem by passing the position explicitly into rgb_diff_stat_update(), just like it is already passed in to scalar_stat_update(). Currently the only user simply passes the reference value as position, because there the input value is also the reference value. This is not true for future uses of rgb_diff_stat. Signed-off-by: Pekka Paalanen --- tests/color-icc-output-test.c | 2 +- tests/color_util.c | 7 ++++--- tests/color_util.h | 3 ++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/color-icc-output-test.c b/tests/color-icc-output-test.c index 2f2de05e..682e10c7 100644 --- a/tests/color-icc-output-test.c +++ b/tests/color-icc-output-test.c @@ -175,7 +175,7 @@ test_roundtrip(uint8_t r, uint8_t g, uint8_t b, cmsPipeline *pip, struct color_float out = {}; cmsPipelineEvalFloat(in.rgb, out.rgb, pip); - rgb_diff_stat_update(stat, &in, &out); + rgb_diff_stat_update(stat, &in, &out, &in); } /* diff --git a/tests/color_util.c b/tests/color_util.c index da2f5c6b..9267aefe 100644 --- a/tests/color_util.c +++ b/tests/color_util.c @@ -449,7 +449,8 @@ rgb_diff_stat_print(const struct rgb_diff_stat *stat, void rgb_diff_stat_update(struct rgb_diff_stat *stat, const struct color_float *ref, - const struct color_float *val) + const struct color_float *val, + const struct color_float *pos) { unsigned i; double ssd = 0.0; @@ -457,9 +458,9 @@ rgb_diff_stat_update(struct rgb_diff_stat *stat, for (i = 0; i < COLOR_CHAN_NUM; i++) { double diff = val->rgb[i] - ref->rgb[i]; - scalar_stat_update(&stat->rgb[i], diff, ref); + scalar_stat_update(&stat->rgb[i], diff, pos); ssd += diff * diff; } - scalar_stat_update(&stat->two_norm, sqrt(ssd), ref); + scalar_stat_update(&stat->two_norm, sqrt(ssd), pos); } diff --git a/tests/color_util.h b/tests/color_util.h index 1bb7297a..9ad09588 100644 --- a/tests/color_util.h +++ b/tests/color_util.h @@ -164,7 +164,8 @@ scalar_stat_print_float(const struct scalar_stat *stat); void rgb_diff_stat_update(struct rgb_diff_stat *stat, const struct color_float *ref, - const struct color_float *val); + const struct color_float *val, + const struct color_float *pos); void rgb_diff_stat_print(const struct rgb_diff_stat *stat, From e103ef4d0d695218509d53cac9b7ff9143f6f0bb Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 20 Jun 2022 13:32:25 +0300 Subject: [PATCH 448/609] tests: add rgb_diff_stat dumps This is a special case of scalar_stat dumps to record all of two-norm and RGB differences on the same line in the dump file. This makes the dump file easier to handle when you want full RGB errors recorded. Signed-off-by: Pekka Paalanen --- tests/color_util.c | 18 ++++++++++++++---- tests/color_util.h | 12 ++++++++++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/tests/color_util.c b/tests/color_util.c index 9267aefe..1f0928b6 100644 --- a/tests/color_util.c +++ b/tests/color_util.c @@ -454,13 +454,23 @@ rgb_diff_stat_update(struct rgb_diff_stat *stat, { unsigned i; double ssd = 0.0; + double diff[COLOR_CHAN_NUM]; + double two_norm; for (i = 0; i < COLOR_CHAN_NUM; i++) { - double diff = val->rgb[i] - ref->rgb[i]; + diff[i] = val->rgb[i] - ref->rgb[i]; - scalar_stat_update(&stat->rgb[i], diff, pos); - ssd += diff * diff; + scalar_stat_update(&stat->rgb[i], diff[i], pos); + ssd += diff[i] * diff[i]; } + two_norm = sqrt(ssd); - scalar_stat_update(&stat->two_norm, sqrt(ssd), pos); + scalar_stat_update(&stat->two_norm, two_norm, pos); + + if (stat->dump) { + fprintf(stat->dump, "%.8g %.8g %.8g %.8g %.5g %.5g %.5g %.5g\n", + two_norm, + diff[COLOR_CHAN_R], diff[COLOR_CHAN_G], diff[COLOR_CHAN_B], + pos->r, pos->g, pos->b, pos->a); + } } diff --git a/tests/color_util.h b/tests/color_util.h index 9ad09588..7a361a6b 100644 --- a/tests/color_util.h +++ b/tests/color_util.h @@ -148,6 +148,18 @@ struct scalar_stat { struct rgb_diff_stat { struct scalar_stat rgb[COLOR_CHAN_NUM]; struct scalar_stat two_norm; + + /** Debug dump into file + * + * Initialize this to a writable file to get a record of all values + * ever fed through this statistics accumulator. The file shall be + * text with the two-norm error, the rgb difference, and their position + * per line: + * norm diff.r diff.g diff.b pos.r pos.g pos.b pos.a + * + * Set to NULL to not record. + */ + FILE *dump; }; void From be281478dc017797348f3ef92428bc78a3c74dd1 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 20 Jun 2022 16:24:34 +0300 Subject: [PATCH 449/609] tests/color_util: doc rgb_diff_stat and scalar_stat Add documentation for test authors. Signed-off-by: Pekka Paalanen --- tests/color_util.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++ tests/color_util.h | 8 ++++++ 2 files changed, 70 insertions(+) diff --git a/tests/color_util.c b/tests/color_util.c index 1f0928b6..da5eb5a3 100644 --- a/tests/color_util.c +++ b/tests/color_util.c @@ -373,6 +373,25 @@ lcmsMAT3_invert(struct lcmsMAT3 *result, const struct lcmsMAT3 *mat) lcmsMAT3_from_weston_matrix(result, &inv); } +/** Update scalar statistics + * + * \param stat The statistics structure to update. + * \param val A sample of the variable whose statistics you are collecting. + * \param pos The "position" that generated the current value. + * + * Accumulates min, max, sum and count statistics with the given value. + * Stores the position related to the current max and min each. + * + * To use this, declare a variable of type struct scalar_stat and + * zero-initialize it. Repeatedly call scalar_stat_update() to accumulate + * statistics. Then either directly read out what you are interested in from + * the structure, or use the related accessor or printing functions. + * + * If you also want to collect a debug log of all calls to this function, + * initialize the .dump member to a writable file handle. This is easiest + * with fopen_dump_file(). Remember to fclose() the handle after you have + * no more samples to add. + */ void scalar_stat_update(struct scalar_stat *stat, double val, @@ -397,12 +416,14 @@ scalar_stat_update(struct scalar_stat *stat, } } +/** Return the average of the previously seen values. */ float scalar_stat_avg(const struct scalar_stat *stat) { return stat->sum / stat->count; } +/** Print scalar statistics with pos.r only */ void scalar_stat_print_float(const struct scalar_stat *stat) { @@ -426,6 +447,20 @@ print_rgb_at_pos(const struct scalar_stat *stat, double scale) testlog(" avg %8.5f\n", scalar_stat_avg(stat) * scale); } +/** Print min/max/avg for each R/G/B/two-norm statistics + * + * \param stat The statistics to print. + * \param title A custom title to include in the heading which shall be printed + * like "%s error statistics:". + * \param scaling_bits Determines a scaling factor for the printed numbers as + * 2^scaling_bits - 1. + * + * Usually RGB values are stored in unsigned integer representation. 8-bit + * integer range is [0, 255] for example. Passing scaling_bits=8 will multiply + * all values (differences, two-norm errors, and position values) by + * 2^8 - 1 = 255. This makes interpreting the recorded errors more intuitive + * through the integer encoding precision perspective. + */ void rgb_diff_stat_print(const struct rgb_diff_stat *stat, const char *title, unsigned scaling_bits) @@ -446,6 +481,33 @@ rgb_diff_stat_print(const struct rgb_diff_stat *stat, print_rgb_at_pos(&stat->two_norm, scale); } +/** Update RGB difference statistics + * + * \param stat The statistics structure to update. + * \param ref The reference color to compare to. + * \param val The color produced by the algorithm under test; a sample. + * \param pos The position to be recorded with extremes. + * + * Computes the RGB difference by subtracting the reference color from the + * sample. This signed difference is tracked separately for each color channel + * in a scalar_stat to find the min, max, and average signed difference. The + * two-norm (Euclidean length) of the RGB difference vector is tracked in + * another scalar_stat. + * + * The position is stored separately for each of the eight min/max + * R/G/B/two-norm values recorded. A good way to use position is to record + * the algorithm input color. + * + * To use this, declare a variable of type struct rgb_diff_stat and + * zero-initalize it. Repeatedly call rgb_diff_stat_update() to accumulate + * statistics. Then either directly read out what you are interested in from + * the structure or use rgb_diff_stat_print(). + * + * If you also want to collect a debug log of all calls to this function, + * initialize the .dump member to a writable file handle. This is easiest + * with fopen_dump_file(). Remember to fclose() the handle after you have + * no more samples to add. + */ void rgb_diff_stat_update(struct rgb_diff_stat *stat, const struct color_float *ref, diff --git a/tests/color_util.h b/tests/color_util.h index 7a361a6b..fab42440 100644 --- a/tests/color_util.h +++ b/tests/color_util.h @@ -123,6 +123,10 @@ transfer_fn_name(enum transfer_fn fn); void lcmsMAT3_invert(struct lcmsMAT3 *result, const struct lcmsMAT3 *mat); +/** Scalar statistics + * + * See scalar_stat_update(). + */ struct scalar_stat { double min; struct color_float min_pos; @@ -145,6 +149,10 @@ struct scalar_stat { FILE *dump; }; +/** RGB difference statistics + * + * See rgb_diff_stat_update(). + */ struct rgb_diff_stat { struct scalar_stat rgb[COLOR_CHAN_NUM]; struct scalar_stat two_norm; From a0584e64cf6701ec6658c5ae4c6d27a47fe14b9f Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 17 Jun 2022 15:12:05 +0300 Subject: [PATCH 450/609] tests/alpha-blending: replace compare_float() with rgb_diff_stat compare_float() was an ad hoc max error logger with optional debug logging. Now that we have rgb_diff_stat, we can get the same statistics and more with less code. It looks like we would lose the pixel index x, but that can be recovered from the dump file line number. This patch takes care to keep the test condition exactly the same as it was before. The statistics print-out has more details now. The recorded dump position is the foreground color as that varies while the background color is constant. An example Octave function is included to show how to visualize the rgb_diff_stat dump. Signed-off-by: Pekka Paalanen --- tests/alpha-blending-test.c | 115 +++++++----------- .../visualization/weston_plot_rgb_diff_stat.m | 77 ++++++++++++ 2 files changed, 120 insertions(+), 72 deletions(-) create mode 100644 tests/visualization/weston_plot_rgb_diff_stat.m diff --git a/tests/alpha-blending-test.c b/tests/alpha-blending-test.c index 2ec2b255..f5553b17 100644 --- a/tests/alpha-blending-test.c +++ b/tests/alpha-blending-test.c @@ -27,6 +27,7 @@ #include "config.h" #include +#include #include "weston-test-client-helper.h" #include "weston-test-fixture-compositor.h" @@ -119,76 +120,20 @@ fill_alpha_pattern(struct buffer *buf) } } -static bool -compare_float(float ref, float dst, int x, const char *chan, float *max_diff) -{ -#if 0 - /* - * This file can be loaded in Octave for visualization. - * - * S = load('compare_float_dump.txt'); - * - * rvec = S(S(:,1)==114, 2:3); - * gvec = S(S(:,1)==103, 2:3); - * bvec = S(S(:,1)==98, 2:3); - * - * figure - * subplot(3, 1, 1); - * plot(rvec(:,1), rvec(:,2) .* 255, 'r'); - * subplot(3, 1, 2); - * plot(gvec(:,1), gvec(:,2) .* 255, 'g'); - * subplot(3, 1, 3); - * plot(bvec(:,1), bvec(:,2) .* 255, 'b'); - */ - static FILE *fp = NULL; - - if (!fp) - fp = fopen("compare_float_dump.txt", "w"); - fprintf(fp, "%d %d %f\n", chan[0], x, dst - ref); - fflush(fp); -#endif - - float diff = fabsf(ref - dst); - - if (diff > *max_diff) - *max_diff = diff; - - /* - * Allow for +/- 1.5 code points of error in non-linear 8-bit channel - * value. This is necessary for the BLEND_LINEAR case. - * - * With llvmpipe, we could go as low as +/- 0.65 code points of error - * and still pass. - * - * AMD Polaris 11 would be ok with +/- 1.0 code points error threshold - * if not for one particular case of blending (a=254, r=0) into r=255, - * which results in error of 1.29 code points. - */ - if (diff < 1.5f / 255.f) - return true; - - testlog("x=%d %s: ref %f != dst %f, delta %f\n", - x, chan, ref, dst, dst - ref); - - return false; -} - enum blend_space { BLEND_NONLINEAR, BLEND_LINEAR, }; -static bool -verify_sRGB_blend_a8r8g8b8(uint32_t bg32, uint32_t fg32, uint32_t dst32, - int x, struct color_float *max_diff, - enum blend_space space) +static void +compare_sRGB_blend_a8r8g8b8(uint32_t bg32, uint32_t fg32, uint32_t dst32, + struct rgb_diff_stat *diffstat, + enum blend_space space) { - const char *const chan_name[COLOR_CHAN_NUM] = { "r", "g", "b" }; struct color_float bg = a8r8g8b8_to_float(bg32); struct color_float fg = a8r8g8b8_to_float(fg32); struct color_float dst = a8r8g8b8_to_float(dst32); struct color_float ref; - bool ok = true; int i; bg = color_float_unpremult(bg); @@ -206,12 +151,7 @@ verify_sRGB_blend_a8r8g8b8(uint32_t bg32, uint32_t fg32, uint32_t dst32, if (space == BLEND_LINEAR) sRGB_delinearize(&ref); - for (i = 0; i < COLOR_CHAN_NUM; i++) { - ok = compare_float(ref.rgb[i], dst.rgb[i], x, - chan_name[i], &max_diff->rgb[i]) && ok; - } - - return ok; + rgb_diff_stat_update(diffstat, &ref, &dst, &fg); } static uint8_t @@ -259,25 +199,56 @@ static bool check_blend_pattern(struct buffer *bg, struct buffer *fg, struct buffer *shot, enum blend_space space) { + FILE *dump = NULL; +#if 0 + /* + * This file can be loaded in Octave for visualization. Find the script + * in tests/visualization/weston_plot_rgb_diff_stat.m and call it with + * + * weston_plot_rgb_diff_stat('alpha_blend-f01-dump.txt', 255, 8) + */ + dump = fopen_dump_file("dump"); +#endif + + /* + * Allow for +/- 1.5 code points of error in non-linear 8-bit channel + * value. This is necessary for the BLEND_LINEAR case. + * + * With llvmpipe, we could go as low as +/- 0.65 code points of error + * and still pass. + * + * AMD Polaris 11 would be ok with +/- 1.0 code points error threshold + * if not for one particular case of blending (a=254, r=0) into r=255, + * which results in error of 1.29 code points. + */ + const float tolerance = 1.5f / 255.f; + uint32_t *bg_row = get_middle_row(bg); uint32_t *fg_row = get_middle_row(fg); uint32_t *shot_row = get_middle_row(shot); - struct color_float max_diff = { .rgb = { 0.0f, 0.0f, 0.0f } }; + struct rgb_diff_stat diffstat = { .dump = dump, }; bool ret = true; int x; + unsigned i; for (x = 0; x < BLOCK_WIDTH * ALPHA_STEPS - 1; x++) { if (!pixels_monotonic(shot_row, x)) ret = false; - if (!verify_sRGB_blend_a8r8g8b8(bg_row[x], fg_row[x], - shot_row[x], x, &max_diff, - space)) + compare_sRGB_blend_a8r8g8b8(bg_row[x], fg_row[x], shot_row[x], + &diffstat, space); + } + + for (i = 0; i < COLOR_CHAN_NUM; i++) { + if (diffstat.rgb[i].min <= -tolerance || + diffstat.rgb[i].max >= tolerance) ret = false; } - testlog("%s max diff: r=%f, g=%f, b=%f\n", - __func__, max_diff.r, max_diff.g, max_diff.b); + rgb_diff_stat_print(&diffstat, __func__, 8); + + if (dump) + fclose(dump); return ret; } diff --git a/tests/visualization/weston_plot_rgb_diff_stat.m b/tests/visualization/weston_plot_rgb_diff_stat.m new file mode 100644 index 00000000..b2546a31 --- /dev/null +++ b/tests/visualization/weston_plot_rgb_diff_stat.m @@ -0,0 +1,77 @@ +% -- weston_plot_rgb_diff_stat (fname) +% -- weston_plot_rgb_diff_stat (fname, scale) +% -- weston_plot_rgb_diff_stat (fname, scale, x_column) +% Plot an rgb_diff_stat dump file +% +% Creates a new figure and draws four sub-plots: R difference, +% G difference, B difference, and two-norm error. +% +% Scale defaults to 255. It is used to multiply both x and y values +% in all plots. Note that R, G and B plots will contain horizontal lines +% at y = +/- 0.5 to help you see the optimal rounding error range for +% the integer encoding [0, scale]. Two-norm plot contains a horizontal +% line at y = sqrt(0.75) which represents an error sphere with the radius +% equal to the two-norm of RGB error (0.5, 0.5, 0.5). +% +% By default, x-axis is sample index, not multiplied by scale. If +% x_column is given, it is a column index in the dump file to be used as +% the x-axis values, multiplied by scale. Indices start from 1, not 0. + +% SPDX-FileCopyrightText: 2022 Collabora, Ltd. +% SPDX-License-Identifier: MIT + +function weston_plot_rgb_diff_stat(fname, scale, x_column); + +S = load(fname); + +if nargin < 2 + scale = 255; +end +if nargin < 3 + x = 1:size(S, 1); +else + x = S(:, x_column) .* scale; +end + +x_lim = [min(x) max(x)]; + +evec = S(:, 1) .* scale; # two-norm error +rvec = S(:, 2) .* scale; # r diff +gvec = S(:, 3) .* scale; # g diff +bvec = S(:, 4) .* scale; # b diff + +figure + +subplot(4, 1, 1); +plot(x, rvec, 'r'); +plus_minus_half_lines(x_lim); +title(fname, "Interpreter", "none"); +ylabel('R diff'); +axis("tight"); + +subplot(4, 1, 2); +plot(x, gvec, 'g'); +plus_minus_half_lines(x_lim); +ylabel('G diff'); +axis("tight"); + +subplot(4, 1, 3); +plot(x, bvec, 'b'); +plus_minus_half_lines(x_lim); +ylabel('B diff'); +axis("tight"); + +subplot(4, 1, 4); +plot(x, evec, 'k'); +hold on; +plot(x_lim, [1 1] .* sqrt(0.75), 'k:'); +ylabel('Two-norm'); +axis("tight"); + +max_abs_err = [max(abs(rvec)) max(abs(gvec)) max(abs(bvec))] + +function plus_minus_half_lines(x_lim); + +hold on; +plot(x_lim, [0.5 -0.5; 0.5 -0.5], 'k:'); + From baf7ab5795cb8d951260a9ad6bd0237c2b0f0fe0 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 17 Jun 2022 15:23:41 +0300 Subject: [PATCH 451/609] tests/alpha-blending: use two_norm tolerance Switch from per-channel max error tolerance to max two-norm (Euclidean distance) error. Geometrically this means that previously the accepted volume was a +/- tolerance cube around the reference point, and now it is a sphere with tolerance radius. This makes the check slightly stricter. The real benefit is simplifying the code. Signed-off-by: Pekka Paalanen --- tests/alpha-blending-test.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/alpha-blending-test.c b/tests/alpha-blending-test.c index f5553b17..8ea9de36 100644 --- a/tests/alpha-blending-test.c +++ b/tests/alpha-blending-test.c @@ -229,7 +229,6 @@ check_blend_pattern(struct buffer *bg, struct buffer *fg, struct buffer *shot, struct rgb_diff_stat diffstat = { .dump = dump, }; bool ret = true; int x; - unsigned i; for (x = 0; x < BLOCK_WIDTH * ALPHA_STEPS - 1; x++) { if (!pixels_monotonic(shot_row, x)) @@ -239,11 +238,8 @@ check_blend_pattern(struct buffer *bg, struct buffer *fg, struct buffer *shot, &diffstat, space); } - for (i = 0; i < COLOR_CHAN_NUM; i++) { - if (diffstat.rgb[i].min <= -tolerance || - diffstat.rgb[i].max >= tolerance) - ret = false; - } + if (diffstat.two_norm.max > tolerance) + ret = false; rgb_diff_stat_print(&diffstat, __func__, 8); From 3acb1c4793bf4726d9348016c1832fa0ebb7e981 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 17 Jun 2022 17:06:18 +0300 Subject: [PATCH 452/609] tests/color-icc-output: compare_float() to rgb_diff_stat compare_float() was an ad hoc max error logger with optional debug logging. Now that we have rgb_diff_stat, we can get the same statistics and more with less code. It looks like we would lose the pixel index x, but that can be recovered from the dump file line number. This patch takes care to keep the test condition exactly the same as it was before. The statistics print-out has more details now. Signed-off-by: Pekka Paalanen --- tests/color-icc-output-test.c | 87 +++++++++++------------------------ 1 file changed, 27 insertions(+), 60 deletions(-) diff --git a/tests/color-icc-output-test.c b/tests/color-icc-output-test.c index 682e10c7..62137411 100644 --- a/tests/color-icc-output-test.c +++ b/tests/color-icc-output-test.c @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -513,67 +514,32 @@ gen_ramp_rgb(pixman_image_t *image, int bitwidth, int width_bar) } static bool -compare_float(float ref, float dst, int x, const char *chan, - float *max_diff, float max_allow_diff) +process_pipeline_comparison(const struct buffer *src_buf, + const struct buffer *shot_buf, + const struct setup_args * arg) { + FILE *dump = NULL; #if 0 /* - * This file can be loaded in Octave for visualization. - * - * S = load('compare_float_dump.txt'); + * This file can be loaded in Octave for visualization. Find the script + * in tests/visualization/weston_plot_rgb_diff_stat.m and call it with * - * rvec = S(S(:,1)==114, 2:3); - * gvec = S(S(:,1)==103, 2:3); - * bvec = S(S(:,1)==98, 2:3); - * - * figure - * subplot(3, 1, 1); - * plot(rvec(:,1), rvec(:,2) .* 255, 'r'); - * subplot(3, 1, 2); - * plot(gvec(:,1), gvec(:,2) .* 255, 'g'); - * subplot(3, 1, 3); - * plot(bvec(:,1), bvec(:,2) .* 255, 'b'); + * weston_plot_rgb_diff_stat('opaque_pixel_conversion-f05-dump.txt') */ - static FILE *fp = NULL; - - if (!fp) - fp = fopen("compare_float_dump.txt", "w"); - fprintf(fp, "%d %d %f\n", chan[0], x, dst - ref); - fflush(fp); + dump = fopen_dump_file("dump"); #endif - float diff = fabsf(ref - dst); - - if (diff > *max_diff) - *max_diff = diff; - - if (diff <= max_allow_diff) - return true; - - testlog("x=%d %s: ref %f != dst %f, delta %f\n", - x, chan, ref, dst, dst - ref); - - return false; -} - -static bool -process_pipeline_comparison(const struct buffer *src_buf, - const struct buffer *shot_buf, - const struct setup_args * arg) -{ - const char *const chan_name[COLOR_CHAN_NUM] = { "r", "g", "b" }; const float max_pixel_value = 255.0; - struct color_float max_diff_pipeline = { .rgb = { 0.0f, 0.0f, 0.0f } }; float max_allow_diff = arg->tolerance / max_pixel_value; - float max_err = 0.0f; bool ok = true; struct image_header ih_src = image_header_from(src_buf->image); struct image_header ih_shot = image_header_from(shot_buf->image); int y, x; - int chan; + unsigned i; struct color_float pix_src; struct color_float pix_src_pipeline; struct color_float pix_shot; + struct rgb_diff_stat diffstat = { .dump = dump }; /* no point to compare different images */ assert(ih_src.width == ih_shot.width); @@ -586,33 +552,34 @@ process_pipeline_comparison(const struct buffer *src_buf, for (x = 0; x < ih_src.width; x++) { pix_src = a8r8g8b8_to_float(row_ptr[x]); pix_shot = a8r8g8b8_to_float(row_ptr_shot[x]); - /* do pipeline processing */ + process_pixel_using_pipeline(arg->pipeline->pre_fn, &arg->pipeline->mat, arg->pipeline->post_fn, &pix_src, &pix_src_pipeline); - /* check if pipeline matches to shader variant */ - for (chan = 0; chan < COLOR_CHAN_NUM; chan++) { - ok &= compare_float(pix_src_pipeline.rgb[chan], - pix_shot.rgb[chan], - x, chan_name[chan], - &max_diff_pipeline.rgb[chan], - max_allow_diff); - } + rgb_diff_stat_update(&diffstat, + &pix_src_pipeline, &pix_shot, + &pix_src); } } - for (chan = 0; chan < COLOR_CHAN_NUM; chan++) - max_err = MAX(max_err, max_diff_pipeline.rgb[chan]); + for (i = 0; i < COLOR_CHAN_NUM; i++) { + if (diffstat.rgb[i].min < -max_allow_diff || + diffstat.rgb[i].max > max_allow_diff) + ok = false; + } - testlog("%s %s %s tol_req %d, tol_cal %f, max diff: r=%f, g=%f, b=%f %s\n", - __func__, ok == true? "SUCCESS":"FAILURE", + testlog("%s %s %s tol_req %d %s\n", __func__, + ok ? "SUCCESS" : "FAILURE", arg->meta.name, arg->tolerance, - max_err * max_pixel_value, - max_diff_pipeline.r, max_diff_pipeline.g, max_diff_pipeline.b, arg->type == PTYPE_MATRIX_SHAPER ? "matrix-shaper" : "cLUT"); + rgb_diff_stat_print(&diffstat, __func__, 8); + + if (dump) + fclose(dump); + return ok; } From b5467ba2583ad3837844df74d39bbbec3a34a9fc Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 17 Jun 2022 17:22:55 +0300 Subject: [PATCH 453/609] tests/color-icc-output: use two-norm tolerance Switch from per-channel max error tolerance to max two-norm (Euclidean distance) error. Geometrically this means that previously the accepted volume was a +/- tolerance cube around the reference point, and now it is a sphere with tolerance radius. The real benefit is simplifying the code. The error tolerance are also changed to float. Integers cannot represent values between 1 and 2, and the jump from 1 to 2 would have been too much. AdobeRGB tolerance gets relaxed a bit, while BT2020 tolerance becomes stricter. The new tolerance values are the reported achieved two-norm max errors plus a bit of margin. Surprisingly the sRGB case tolerances remain strictly at zero, and that's no bug in the test. Signed-off-by: Pekka Paalanen --- tests/color-icc-output-test.c | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/tests/color-icc-output-test.c b/tests/color-icc-output-test.c index 62137411..a9b3d7c4 100644 --- a/tests/color-icc-output-test.c +++ b/tests/color-icc-output-test.c @@ -137,8 +137,10 @@ struct setup_args { struct fixture_metadata meta; int ref_image_index; const struct lcms_pipeline *pipeline; + /** - * 2/255 or 3/255 maximum possible error, where 255 is 8 bit max value + * Two-norm color error tolerance in units of 1.0/255, computed in + * output electrical space. * * Tolerance depends more on the 1D LUT used for the * inv EOTF than the tested 3D LUT size: @@ -148,7 +150,8 @@ struct setup_args { * in GL-renderer, then we should fix the tolerance * as the error should reduce a lot. */ - int tolerance; + float tolerance; + /** * 3DLUT dimension size */ @@ -160,12 +163,12 @@ struct setup_args { }; static const struct setup_args my_setup_args[] = { - /* name, ref img, pipeline, tolerance, dim, profile type, clut tolerance */ - { { "sRGB->sRGB" }, 0, &pipeline_sRGB, 0, 0, PTYPE_MATRIX_SHAPER }, - { { "sRGB->adobeRGB" }, 1, &pipeline_adobeRGB, 1, 0, PTYPE_MATRIX_SHAPER }, - { { "sRGB->BT2020" }, 2, &pipeline_BT2020, 5, 0, PTYPE_MATRIX_SHAPER }, - { { "sRGB->sRGB" }, 0, &pipeline_sRGB, 0, 17, PTYPE_CLUT, 0.0005 }, - { { "sRGB->adobeRGB" }, 1, &pipeline_adobeRGB, 1, 17, PTYPE_CLUT, 0.0065 }, + /* name, ref img, pipeline, tolerance, dim, profile type, clut tolerance */ + { { "sRGB->sRGB" }, 0, &pipeline_sRGB, 0.0, 0, PTYPE_MATRIX_SHAPER }, + { { "sRGB->adobeRGB" }, 1, &pipeline_adobeRGB, 1.4, 0, PTYPE_MATRIX_SHAPER }, + { { "sRGB->BT2020" }, 2, &pipeline_BT2020, 4.5, 0, PTYPE_MATRIX_SHAPER }, + { { "sRGB->sRGB" }, 0, &pipeline_sRGB, 0.0, 17, PTYPE_CLUT, 0.0005 }, + { { "sRGB->adobeRGB" }, 1, &pipeline_adobeRGB, 1.8, 17, PTYPE_CLUT, 0.0065 }, }; static void @@ -529,17 +532,14 @@ process_pipeline_comparison(const struct buffer *src_buf, dump = fopen_dump_file("dump"); #endif - const float max_pixel_value = 255.0; - float max_allow_diff = arg->tolerance / max_pixel_value; - bool ok = true; struct image_header ih_src = image_header_from(src_buf->image); struct image_header ih_shot = image_header_from(shot_buf->image); int y, x; - unsigned i; struct color_float pix_src; struct color_float pix_src_pipeline; struct color_float pix_shot; struct rgb_diff_stat diffstat = { .dump = dump }; + bool ok; /* no point to compare different images */ assert(ih_src.width == ih_shot.width); @@ -564,13 +564,9 @@ process_pipeline_comparison(const struct buffer *src_buf, } } - for (i = 0; i < COLOR_CHAN_NUM; i++) { - if (diffstat.rgb[i].min < -max_allow_diff || - diffstat.rgb[i].max > max_allow_diff) - ok = false; - } + ok = diffstat.two_norm.max <= arg->tolerance / 255.0f; - testlog("%s %s %s tol_req %d %s\n", __func__, + testlog("%s %s %s tolerance %f %s\n", __func__, ok ? "SUCCESS" : "FAILURE", arg->meta.name, arg->tolerance, arg->type == PTYPE_MATRIX_SHAPER ? "matrix-shaper" : "cLUT"); From 77fb2f56afe7a392f892a9188d442483082bd582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Thu, 30 Jun 2022 15:16:46 +0200 Subject: [PATCH 454/609] clients/presentation-shm: Bind to xdg_wm_base version 1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It was binding to any advertised version, but it can't actually work with version 4 (because it doesn't handle the new configure_bounds event). Other sample clients in the tree are hard-coding version 1, so do the same here. Fixes: 6d9fda715609 ("clients/presentation-shm: use xdg_shell instead of wl_shell") Signed-off-by: Michel Dänzer --- clients/presentation-shm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clients/presentation-shm.c b/clients/presentation-shm.c index d5d73a2a..828cb08d 100644 --- a/clients/presentation-shm.c +++ b/clients/presentation-shm.c @@ -765,7 +765,7 @@ registry_handle_global(void *data, struct wl_registry *registry, } else if (strcmp(interface, "xdg_wm_base") == 0) { d->wm_base = wl_registry_bind(registry, name, - &xdg_wm_base_interface, version); + &xdg_wm_base_interface, 1); } else if (strcmp(interface, "wl_shm") == 0) { d->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); From 2c0ff9a3b414e524b67a515320dd953dc33f99e9 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 14 Jun 2022 16:18:43 +0300 Subject: [PATCH 455/609] tests/color_util: expose color_float_apply_curve() I will be needing this in color-icc-output blending test. Signed-off-by: Pekka Paalanen --- tests/color_util.c | 2 +- tests/color_util.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/color_util.c b/tests/color_util.c index da5eb5a3..fc406d87 100644 --- a/tests/color_util.c +++ b/tests/color_util.c @@ -257,7 +257,7 @@ a8r8g8b8_to_float(uint32_t v) return cf; } -static struct color_float +struct color_float color_float_apply_curve(enum transfer_fn fn, struct color_float c) { unsigned i; diff --git a/tests/color_util.h b/tests/color_util.h index fab42440..a5003fa8 100644 --- a/tests/color_util.h +++ b/tests/color_util.h @@ -111,6 +111,9 @@ process_pixel_using_pipeline(enum transfer_fn pre_curve, struct color_float color_float_unpremult(struct color_float in); +struct color_float +color_float_apply_curve(enum transfer_fn fn, struct color_float c); + struct color_float color_float_apply_matrix(const struct lcmsMAT3 *mat, struct color_float c); From aa4f7d3a633ad84686593140cd681d36c5e2a7d0 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 13 Jun 2022 14:34:19 +0300 Subject: [PATCH 456/609] tests/color-icc-output: add blending test This is adding basically a copy of alpha-blending-test.c. The difference is that here we use ICC files to set up the output color profile, and then test light-linear blending only. BLOCK_WIDTH is set to 1 to fit inside the output size used by the fixture setup, which is smaller than in the original, but does not change the results. The test is aimed at testing how color-lcms module succeeds in linearizing the output of different ICC output profiles. Incorrect linearization should cause changes in blending results. The tolerance is taken from the currently achieved error statistics (1.40908) and rounded up a little to achieve a suitable margin. Signed-off-by: Pekka Paalanen --- tests/color-icc-output-test.c | 221 ++++++++++++++++++ tests/reference/output_icc_alpha_blend-00.png | Bin 0 -> 405 bytes tests/reference/output_icc_alpha_blend-01.png | Bin 0 -> 417 bytes tests/reference/output_icc_alpha_blend-02.png | Bin 0 -> 386 bytes 4 files changed, 221 insertions(+) create mode 100644 tests/reference/output_icc_alpha_blend-00.png create mode 100644 tests/reference/output_icc_alpha_blend-01.png create mode 100644 tests/reference/output_icc_alpha_blend-02.png diff --git a/tests/color-icc-output-test.c b/tests/color-icc-output-test.c index a9b3d7c4..63fbfd1f 100644 --- a/tests/color-icc-output-test.c +++ b/tests/color-icc-output-test.c @@ -1,5 +1,6 @@ /* * Copyright 2021 Advanced Micro Devices, Inc. + * Copyright 2020, 2022 Collabora, Ltd. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -624,3 +625,223 @@ TEST(opaque_pixel_conversion) buffer_destroy(buf); client_destroy(client); } + +static struct color_float +convert_to_blending_space(const struct lcms_pipeline *pip, + struct color_float cf) +{ + /* Blending space is the linearized output space, + * or simply output space without the non-linear encoding + */ + cf = color_float_apply_curve(pip->pre_fn, cf); + return color_float_apply_matrix(&pip->mat, cf); +} + +static void +compare_blend(const struct lcms_pipeline *pip, + struct color_float bg, + struct color_float fg, + const struct color_float *shot, + struct rgb_diff_stat *diffstat) +{ + struct color_float ref; + unsigned i; + + /* convert sources to straight alpha */ + assert(bg.a == 1.0f); + fg = color_float_unpremult(fg); + + bg = convert_to_blending_space(pip, bg); + fg = convert_to_blending_space(pip, fg); + + /* blend */ + for (i = 0; i < COLOR_CHAN_NUM; i++) + ref.rgb[i] = (1.0f - fg.a) * bg.rgb[i] + fg.a * fg.rgb[i]; + + /* non-linear encoding for output */ + ref = color_float_apply_curve(pip->post_fn, ref); + + rgb_diff_stat_update(diffstat, &ref, shot, &fg); +} + +/* Alpha blending test pattern parameters */ +static const int ALPHA_STEPS = 256; +static const int BLOCK_WIDTH = 1; + +static void * +get_middle_row(struct buffer *buf) +{ + struct image_header ih = image_header_from(buf->image); + + assert(ih.width >= BLOCK_WIDTH * ALPHA_STEPS); + assert(ih.height >= BLOCK_WIDTH); + + return image_header_get_row_u32(&ih, (BLOCK_WIDTH - 1) / 2); +} + +static bool +check_blend_pattern(struct buffer *bg_buf, + struct buffer *fg_buf, + struct buffer *shot_buf, + const struct setup_args *arg) +{ + FILE *dump = NULL; +#if 0 + /* + * This file can be loaded in Octave for visualization. Find the script + * in tests/visualization/weston_plot_rgb_diff_stat.m and call it with + * + * weston_plot_rgb_diff_stat('output_icc_alpha_blend-f01-dump.txt', 255, 8) + */ + dump = fopen_dump_file("dump"); +#endif + + uint32_t *bg_row = get_middle_row(bg_buf); + uint32_t *fg_row = get_middle_row(fg_buf); + uint32_t *shot_row = get_middle_row(shot_buf); + struct rgb_diff_stat diffstat = { .dump = dump }; + int x; + + for (x = 0; x < BLOCK_WIDTH * ALPHA_STEPS; x++) { + struct color_float bg = a8r8g8b8_to_float(bg_row[x]); + struct color_float fg = a8r8g8b8_to_float(fg_row[x]); + struct color_float shot = a8r8g8b8_to_float(shot_row[x]); + + compare_blend(arg->pipeline, bg, fg, &shot, &diffstat); + } + + rgb_diff_stat_print(&diffstat, "Blending", 8); + + if (dump) + fclose(dump); + + /* Test success condition: */ + return diffstat.two_norm.max < 1.5f / 255.0f; +} + +static uint32_t +premult_color(uint32_t a, uint32_t r, uint32_t g, uint32_t b) +{ + uint32_t c = 0; + + c |= a << 24; + c |= (a * r / 255) << 16; + c |= (a * g / 255) << 8; + c |= a * b / 255; + + return c; +} + +static void +fill_alpha_pattern(struct buffer *buf) +{ + struct image_header ih = image_header_from(buf->image); + int y; + + assert(ih.pixman_format == PIXMAN_a8r8g8b8); + assert(ih.width == BLOCK_WIDTH * ALPHA_STEPS); + + for (y = 0; y < ih.height; y++) { + uint32_t *row = image_header_get_row_u32(&ih, y); + uint32_t step; + + for (step = 0; step < (uint32_t)ALPHA_STEPS; step++) { + uint32_t alpha = step * 255 / (ALPHA_STEPS - 1); + uint32_t color; + int i; + + color = premult_color(alpha, 0, 255 - alpha, 255); + for (i = 0; i < BLOCK_WIDTH; i++) + *row++ = color; + } + } +} + +/* + * Test that alpha blending is correct when an output ICC profile is installed. + * + * The background is a constant color. On top of that, there is an + * alpha-blended gradient with ramps in both alpha and color. Sub-surface + * ensures the correct positioning and stacking. + * + * The gradient consists of ALPHA_STEPS number of blocks. Block size is + * BLOCK_WIDTH x BLOCK_WIDTH and a block has a uniform color. + * + * In the blending result over x axis: + * - red goes from 1.0 to 0.0, monotonic + * - green is not monotonic + * - blue goes from 0.0 to 1.0, monotonic + * + * The test has sRGB encoded input pixels (non-linear). These are converted to + * linear light (optical) values in output color space, blended, and converted + * to non-linear (electrical) values according to the output ICC profile. + * + * Specifically, this test exercises the linearization of output ICC profiles, + * retrieve_eotf_and_output_inv_eotf(). + */ +TEST(output_icc_alpha_blend) +{ + const int width = BLOCK_WIDTH * ALPHA_STEPS; + const int height = BLOCK_WIDTH; + const pixman_color_t background_color = { + .red = 0xffff, + .green = 0x8080, + .blue = 0x0000, + .alpha = 0xffff + }; + int seq_no = get_test_fixture_index(); + const struct setup_args *arg = &my_setup_args[seq_no]; + struct client *client; + struct buffer *bg; + struct buffer *fg; + struct wl_subcompositor *subco; + struct wl_surface *surf; + struct wl_subsurface *sub; + struct buffer *shot; + bool match; + + client = create_client(); + subco = bind_to_singleton_global(client, &wl_subcompositor_interface, 1); + + /* background window content */ + bg = create_shm_buffer_a8r8g8b8(client, width, height); + fill_image_with_color(bg->image, &background_color); + + /* background window, main surface */ + client->surface = create_test_surface(client); + client->surface->width = width; + client->surface->height = height; + client->surface->buffer = bg; /* pass ownership */ + surface_set_opaque_rect(client->surface, + &(struct rectangle){ 0, 0, width, height }); + + /* foreground blended content */ + fg = create_shm_buffer_a8r8g8b8(client, width, height); + fill_alpha_pattern(fg); + + /* foreground window, sub-surface */ + surf = wl_compositor_create_surface(client->wl_compositor); + sub = wl_subcompositor_get_subsurface(subco, surf, client->surface->wl_surface); + /* sub-surface defaults to position 0, 0, top-most, synchronized */ + wl_surface_attach(surf, fg->proxy, 0, 0); + wl_surface_damage(surf, 0, 0, width, height); + wl_surface_commit(surf); + + /* attach, damage, commit background window */ + move_client(client, 0, 0); + + shot = capture_screenshot_of_output(client); + assert(shot); + match = verify_image(shot, "output_icc_alpha_blend", arg->ref_image_index, + NULL, seq_no); + assert(check_blend_pattern(bg, fg, shot, arg)); + assert(match); + + buffer_destroy(shot); + + wl_subsurface_destroy(sub); + wl_surface_destroy(surf); + buffer_destroy(fg); + wl_subcompositor_destroy(subco); + client_destroy(client); /* destroys bg */ +} diff --git a/tests/reference/output_icc_alpha_blend-00.png b/tests/reference/output_icc_alpha_blend-00.png new file mode 100644 index 0000000000000000000000000000000000000000..beec77b82c81aa9b906e6898621b04ad8d61c91c GIT binary patch literal 405 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K5BsiFWY#yU`YvX;q+5_jL7hS?83{1OWcprxySK literal 0 HcmV?d00001 diff --git a/tests/reference/output_icc_alpha_blend-01.png b/tests/reference/output_icc_alpha_blend-01.png new file mode 100644 index 0000000000000000000000000000000000000000..45250ecc22fa6cc77008b70dc8aa69391f2d1260 GIT binary patch literal 417 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K5BsiFWQ$>T03j1 zD_P`PL}MC7V~()KG_dA8VlrFMYb(lo#yvyfpju%ChZ#$;bDwfoi`0dFwOAI;0EG+j z{f9*OZ|`hkU~$^}c`f_S{Sp(duPonJV&Uz;SS!(~5OhU@QMD&?;KzpWk8qXg|9_ zz0!a0a!2M{?{kh_Im&3Tw2;9kQ-&c}iv7TpV-#|Kvi%Y=JZj(ns}d9gp00i_>zopr E0At~$`Tzg` literal 0 HcmV?d00001 diff --git a/tests/reference/output_icc_alpha_blend-02.png b/tests/reference/output_icc_alpha_blend-02.png new file mode 100644 index 0000000000000000000000000000000000000000..7714b1032a252b8d6ff568a014a048798c82b9aa GIT binary patch literal 386 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K5BsiFW%=XMTkH1QpJnFz z(s0=7L4s?1RPc>p4p}#kf`m#g&Xd=47;bJ(dm4Uo)lF892nGL-udZ;gOkFuoEJ;k7 zQB{Ze_temUxyu7&_pWvP@y35|_}!(;&fM);ubuwSa^0!t5yk5x!k;B&+e#luj?P!x z?)WWOt}-V0+`QR0`c|Fn_6=L6b^iV4m)rZVR;pbOnEh(QviR_GCyFO+wCC$NCUULR z%B#mv`S7O%p4ytfHAU)d_jft`D1WuS=sy>`jJdwhp|wXC7MdD5Wccz3L Date: Tue, 28 Jun 2022 11:24:58 +0300 Subject: [PATCH 457/609] weston-log: Fix documentation for weston_log_subscription_create Introduced with e0a858a5f2f00f35bfc, commit 'weston-debug: Introduce weston_log_subscription and weston_log_subscriber objects'. We don't really return a weston_log_subscription so let's remove it. Some newer doxygen detects this and we are treating warning as errors. Fixes #594 Signed-off-by: Marius Vlad --- libweston/weston-log.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/libweston/weston-log.c b/libweston/weston-log.c index 5e7f3521..93f95c9a 100644 --- a/libweston/weston-log.c +++ b/libweston/weston-log.c @@ -238,8 +238,6 @@ weston_log_subscription_get_data(struct weston_log_subscription *sub) * subscription * @param scope the scope in order to add the subscription to the scope's * subscription list - * @returns a weston_log_subscription object in case of success, or NULL - * otherwise * * @sa weston_log_subscription_destroy, weston_log_subscription_remove, * weston_log_subscription_add From c2c7644fd0306ab99287e51b466e7d8eb9478b62 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Tue, 28 Jun 2022 11:42:05 +0300 Subject: [PATCH 458/609] docs/sphinx/doxygen.ini.in: Remove LaTeX generation LaTeX has become obsolete in newer doxygen version, and we weren't using it at all so remove it entirely. Signed-off-by: Marius Vlad --- doc/sphinx/doxygen.ini.in | 169 -------------------------------------- 1 file changed, 169 deletions(-) diff --git a/doc/sphinx/doxygen.ini.in b/doc/sphinx/doxygen.ini.in index bfe77e8e..852de3a9 100644 --- a/doc/sphinx/doxygen.ini.in +++ b/doc/sphinx/doxygen.ini.in @@ -1632,175 +1632,6 @@ EXTERNAL_SEARCH_ID = EXTRA_SEARCH_MAPPINGS = -#--------------------------------------------------------------------------- -# Configuration options related to the LaTeX output -#--------------------------------------------------------------------------- - -# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. -# The default value is: YES. - -GENERATE_LATEX = NO - -# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a -# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of -# it. -# The default directory is: latex. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_OUTPUT = latex - -# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be -# invoked. -# -# Note that when enabling USE_PDFLATEX this option is only used for generating -# bitmaps for formulas in the HTML output, but not in the Makefile that is -# written to the output directory. -# The default file is: latex. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_CMD_NAME = latex - -# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate -# index for LaTeX. -# The default file is: makeindex. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -MAKEINDEX_CMD_NAME = makeindex - -# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX -# documents. This may be useful for small projects and may help to save some -# trees in general. -# The default value is: NO. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -COMPACT_LATEX = NO - -# The PAPER_TYPE tag can be used to set the paper type that is used by the -# printer. -# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x -# 14 inches) and executive (7.25 x 10.5 inches). -# The default value is: a4. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -PAPER_TYPE = a4 - -# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names -# that should be included in the LaTeX output. The package can be specified just -# by its name or with the correct syntax as to be used with the LaTeX -# \usepackage command. To get the times font for instance you can specify : -# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times} -# To use the option intlimits with the amsmath package you can specify: -# EXTRA_PACKAGES=[intlimits]{amsmath} -# If left blank no extra packages will be included. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -EXTRA_PACKAGES = - -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the -# generated LaTeX document. The header should contain everything until the first -# chapter. If it is left blank doxygen will generate a standard header. See -# section "Doxygen usage" for information on how to let doxygen write the -# default header to a separate file. -# -# Note: Only use a user-defined header if you know what you are doing! The -# following commands have a special meaning inside the header: $title, -# $datetime, $date, $doxygenversion, $projectname, $projectnumber, -# $projectbrief, $projectlogo. Doxygen will replace $title with the empty -# string, for the replacement values of the other commands the user is referred -# to HTML_HEADER. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_HEADER = - -# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the -# generated LaTeX document. The footer should contain everything after the last -# chapter. If it is left blank doxygen will generate a standard footer. See -# LATEX_HEADER for more information on how to generate a default footer and what -# special commands can be used inside the footer. -# -# Note: Only use a user-defined footer if you know what you are doing! -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_FOOTER = - -# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined -# LaTeX style sheets that are included after the standard style sheets created -# by doxygen. Using this option one can overrule certain style aspects. Doxygen -# will copy the style sheet files to the output directory. -# Note: The order of the extra style sheet files is of importance (e.g. the last -# style sheet in the list overrules the setting of the previous ones in the -# list). -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_EXTRA_STYLESHEET = - -# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or -# other source files which should be copied to the LATEX_OUTPUT output -# directory. Note that the files will be copied as-is; there are no commands or -# markers available. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_EXTRA_FILES = - -# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is -# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will -# contain links (just like the HTML output) instead of page references. This -# makes the output suitable for online browsing using a PDF viewer. -# The default value is: YES. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -PDF_HYPERLINKS = YES - -# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate -# the PDF file directly from the LaTeX files. Set this option to YES, to get a -# higher quality PDF documentation. -# The default value is: YES. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -USE_PDFLATEX = YES - -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode -# command to the generated LaTeX files. This will instruct LaTeX to keep running -# if errors occur, instead of asking the user for help. This option is also used -# when generating formulas in HTML. -# The default value is: NO. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_BATCHMODE = NO - -# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the -# index chapters (such as File Index, Compound Index, etc.) in the output. -# The default value is: NO. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_HIDE_INDICES = NO - -# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source -# code with syntax highlighting in the LaTeX output. -# -# Note that which sources are shown also depends on other settings such as -# SOURCE_BROWSER. -# The default value is: NO. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_SOURCE_CODE = NO - -# The LATEX_BIB_STYLE tag can be used to specify the style to use for the -# bibliography, e.g. plainnat, or ieeetr. See -# http://en.wikipedia.org/wiki/BibTeX and \cite for more info. -# The default value is: plain. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_BIB_STYLE = plain - -# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated -# page will contain the date and time when the page was generated. Setting this -# to NO can help when comparing the output of multiple runs. -# The default value is: NO. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_TIMESTAMP = NO - #--------------------------------------------------------------------------- # Configuration options related to the RTF output #--------------------------------------------------------------------------- From bd50e257e6771ee1f182bf6ac05eb0b7d90ccde1 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Tue, 28 Jun 2022 11:44:34 +0300 Subject: [PATCH 459/609] doc/sphinx/doxygen.ini.in: Remove RTF generation Same as LaTeX, RTF is being made obsolete in newer version of doxygen. Also, we weren't really using it so there's no harm in removing it entirely. Signed-off-by: Marius Vlad --- doc/sphinx/doxygen.ini.in | 66 --------------------------------------- 1 file changed, 66 deletions(-) diff --git a/doc/sphinx/doxygen.ini.in b/doc/sphinx/doxygen.ini.in index 852de3a9..52524aab 100644 --- a/doc/sphinx/doxygen.ini.in +++ b/doc/sphinx/doxygen.ini.in @@ -1632,72 +1632,6 @@ EXTERNAL_SEARCH_ID = EXTRA_SEARCH_MAPPINGS = -#--------------------------------------------------------------------------- -# Configuration options related to the RTF output -#--------------------------------------------------------------------------- - -# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The -# RTF output is optimized for Word 97 and may not look too pretty with other RTF -# readers/editors. -# The default value is: NO. - -GENERATE_RTF = NO - -# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a -# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of -# it. -# The default directory is: rtf. -# This tag requires that the tag GENERATE_RTF is set to YES. - -RTF_OUTPUT = rtf - -# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF -# documents. This may be useful for small projects and may help to save some -# trees in general. -# The default value is: NO. -# This tag requires that the tag GENERATE_RTF is set to YES. - -COMPACT_RTF = NO - -# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will -# contain hyperlink fields. The RTF file will contain links (just like the HTML -# output) instead of page references. This makes the output suitable for online -# browsing using Word or some other Word compatible readers that support those -# fields. -# -# Note: WordPad (write) and others do not support links. -# The default value is: NO. -# This tag requires that the tag GENERATE_RTF is set to YES. - -RTF_HYPERLINKS = NO - -# Load stylesheet definitions from file. Syntax is similar to doxygen's config -# file, i.e. a series of assignments. You only have to provide replacements, -# missing definitions are set to their default value. -# -# See also section "Doxygen usage" for information on how to generate the -# default style sheet that doxygen normally uses. -# This tag requires that the tag GENERATE_RTF is set to YES. - -RTF_STYLESHEET_FILE = - -# Set optional variables used in the generation of an RTF document. Syntax is -# similar to doxygen's config file. A template extensions file can be generated -# using doxygen -e rtf extensionFile. -# This tag requires that the tag GENERATE_RTF is set to YES. - -RTF_EXTENSIONS_FILE = - -# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code -# with syntax highlighting in the RTF output. -# -# Note that which sources are shown also depends on other settings such as -# SOURCE_BROWSER. -# The default value is: NO. -# This tag requires that the tag GENERATE_RTF is set to YES. - -RTF_SOURCE_CODE = NO - #--------------------------------------------------------------------------- # Configuration options related to the man page output #--------------------------------------------------------------------------- From afa494014f8c323f2a06e29e728ee3a1d9d5a11e Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Tue, 28 Jun 2022 11:45:17 +0300 Subject: [PATCH 460/609] doc/sphinx/doxygen.ini.in: Remove DOCBOOK_PROGRAMLISTING A newer version of doyxgen made it obsolete. Signed-off-by: Marius Vlad --- doc/sphinx/doxygen.ini.in | 9 --------- 1 file changed, 9 deletions(-) diff --git a/doc/sphinx/doxygen.ini.in b/doc/sphinx/doxygen.ini.in index 52524aab..c80d6fe4 100644 --- a/doc/sphinx/doxygen.ini.in +++ b/doc/sphinx/doxygen.ini.in @@ -1721,15 +1721,6 @@ GENERATE_DOCBOOK = NO DOCBOOK_OUTPUT = docbook -# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the -# program listings (including syntax highlighting and cross-referencing -# information) to the DOCBOOK output. Note that enabling this will significantly -# increase the size of the DOCBOOK output. -# The default value is: NO. -# This tag requires that the tag GENERATE_DOCBOOK is set to YES. - -DOCBOOK_PROGRAMLISTING = NO - #--------------------------------------------------------------------------- # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- From e5f6e512ce1df0ddc3c3604e40c4575767c71580 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Tue, 28 Jun 2022 11:47:18 +0300 Subject: [PATCH 461/609] doc/sphinx/doxygen.ini.in: Remove CLASS_DIAGRAM CLASS_DIAGRAM has been obsolete in newer version of doxygen, and it's enabled if HAVE_DOT and CLASS_GRAPH are set. This increase DOT_GRAPH_MAX_NODES to avoid dot complaning, and include dot/graphviz for doxygen. Signed-off-by: Marius Vlad --- .gitlab-ci.yml | 2 +- .gitlab-ci/debian-install.sh | 1 + doc/sphinx/doxygen.ini.in | 13 ++----------- doc/sphinx/meson.build | 1 + 4 files changed, 5 insertions(+), 12 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index dd8d0fa8..bed0cbbf 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -43,7 +43,7 @@ variables: FDO_UPSTREAM_REPO: wayland/weston FDO_REPO_SUFFIX: "$BUILD_OS/$BUILD_ARCH" - FDO_DISTRIBUTION_TAG: '2022-06-06.00-build-libdrm-dep' + FDO_DISTRIBUTION_TAG: '2022-06-28.00-graphviz' include: diff --git a/.gitlab-ci/debian-install.sh b/.gitlab-ci/debian-install.sh index 9b9d2a6d..8adfd835 100644 --- a/.gitlab-ci/debian-install.sh +++ b/.gitlab-ci/debian-install.sh @@ -39,6 +39,7 @@ apt-get -y --no-install-recommends install \ clang-11 \ curl \ doxygen \ + graphviz \ freerdp2-dev \ gcovr \ git \ diff --git a/doc/sphinx/doxygen.ini.in b/doc/sphinx/doxygen.ini.in index c80d6fe4..1e37d226 100644 --- a/doc/sphinx/doxygen.ini.in +++ b/doc/sphinx/doxygen.ini.in @@ -1899,15 +1899,6 @@ EXTERNAL_PAGES = YES # Configuration options related to the dot tool #--------------------------------------------------------------------------- -# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram -# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to -# NO turns the diagrams off. Note that this option also works with HAVE_DOT -# disabled, but it is recommended to install and use dot, since it yields more -# powerful graphs. -# The default value is: YES. - -CLASS_DIAGRAMS = YES - # You can include diagrams made with dia in doxygen documentation. Doxygen will # then run dia to produce the diagram and insert it in the documentation. The # DIA_PATH tag allows you to specify the directory where the dia binary resides. @@ -1928,7 +1919,7 @@ HIDE_UNDOC_RELATIONS = YES # set to NO # The default value is: YES. -HAVE_DOT = NO +HAVE_DOT = YES # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed # to run in parallel. When set to 0 doxygen will base this on the number of @@ -2157,7 +2148,7 @@ PLANTUML_INCLUDE_PATH = # Minimum value: 0, maximum value: 10000, default value: 50. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_GRAPH_MAX_NODES = 50 +DOT_GRAPH_MAX_NODES = 250 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs # generated by dot. A depth value of 3 means that only nodes reachable from the diff --git a/doc/sphinx/meson.build b/doc/sphinx/meson.build index 88f09e2c..46947a6b 100644 --- a/doc/sphinx/meson.build +++ b/doc/sphinx/meson.build @@ -1,5 +1,6 @@ sphinx = find_program('sphinx-build', required: true) doxygen = find_program('doxygen', required: true) +dot = find_program('dot', required: true) breathe = find_program('breathe-apidoc', required: true) sphinx_c = run_command(sphinx, '--version') From b92380211383feb97dbca3d56b75404e0e2321ad Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 24 Jun 2022 16:31:07 +0100 Subject: [PATCH 462/609] xwayland: Refactor argument string construction Replace an oft-duplicated pattern with a trivial helper function. In doing so, we observe that the one special case (displayfd 'didn't need to be CLOEXEC') was wrong, because the X server does fork itself internally, so there is nothing wrong with setting CLOEXEC. Signed-off-by: Daniel Stone --- compositor/xwayland.c | 65 ++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/compositor/xwayland.c b/compositor/xwayland.c index 17f53b89..374f549d 100644 --- a/compositor/xwayland.c +++ b/compositor/xwayland.c @@ -87,18 +87,32 @@ out: return 0; } +/* Duplicate an FD and write it into a string, for use in environment */ +#define FD_STR_LEN 12 +static bool +dup_fd_to_string(char s[FD_STR_LEN], int fd) +{ + fd = dup(fd); + if (fd < 0) + return false; + snprintf(s, FD_STR_LEN, "%d", fd); + return true; +} + static pid_t spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd) { struct wet_xwayland *wxw = user_data; pid_t pid; - char s[12], abstract_fd_str[12], unix_fd_str[12], wm_fd_str[12]; - char display_fd_str[12]; - int sv[2], wm[2], fd, display_fd[2]; + char wayland_socket_str[FD_STR_LEN], abstract_fd_str[FD_STR_LEN]; + char unix_fd_str[FD_STR_LEN], wm_fd_str[FD_STR_LEN]; + char display_fd_str[FD_STR_LEN]; + int sv[2], wm[2], display_fd[2]; char *xserver = NULL; struct weston_config *config = wet_get_config(wxw->compositor); struct weston_config_section *section; struct wl_event_loop *loop; + bool ret; if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) { weston_log("wl connection socketpair failed\n"); @@ -116,40 +130,35 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd } if (os_fd_set_cloexec(display_fd[0]) != 0) { - weston_log("failed setting remaining end of displayfd as cloexec\n"); + weston_log("failed setting compositor end of displayfd as cloexec\n"); + return 1; + } + + if (os_fd_set_cloexec(display_fd[1]) != 0) { + weston_log("failed setting Xwayland end of displayfd as cloexec\n"); return 1; } pid = fork(); switch (pid) { case 0: - /* SOCK_CLOEXEC closes both ends, so we need to unset - * the flag on the client fd. */ - fd = dup(sv[1]); - if (fd < 0) - goto fail; - snprintf(s, sizeof s, "%d", fd); - setenv("WAYLAND_SOCKET", s, 1); - - fd = dup(abstract_fd); - if (fd < 0) - goto fail; - snprintf(abstract_fd_str, sizeof abstract_fd_str, "%d", fd); - fd = dup(unix_fd); - if (fd < 0) - goto fail; - snprintf(unix_fd_str, sizeof unix_fd_str, "%d", fd); - fd = dup(wm[1]); - if (fd < 0) - goto fail; - snprintf(wm_fd_str, sizeof wm_fd_str, "%d", fd); - snprintf(display_fd_str, sizeof display_fd_str, "%d", display_fd[1]); - section = weston_config_get_section(config, "xwayland", NULL, NULL); weston_config_section_get_string(section, "path", &xserver, XSERVER_PATH); + /* SOCK_CLOEXEC closes both ends, so we need to unset + * the flag on the client fd. */ + ret = dup_fd_to_string(wayland_socket_str, sv[1]); + ret &= dup_fd_to_string(abstract_fd_str, abstract_fd); + ret &= dup_fd_to_string(unix_fd_str, unix_fd); + ret &= dup_fd_to_string(wm_fd_str, wm[1]); + ret &= dup_fd_to_string(display_fd_str, display_fd[1]); + if (!ret) + _exit(EXIT_FAILURE); + + setenv("WAYLAND_SOCKET", wayland_socket_str, 1); + if (execl(xserver, xserver, display, @@ -159,14 +168,14 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd "-displayfd", display_fd_str, "-wm", wm_fd_str, "-terminate", - NULL) < 0) + NULL) < 0) { weston_log("exec of '%s %s -rootless " LISTEN_STR " %s " LISTEN_STR " %s " "-wm %s -terminate' failed: %s\n", xserver, display, abstract_fd_str, unix_fd_str, wm_fd_str, strerror(errno)); - fail: + } _exit(EXIT_FAILURE); default: From 158c3ef0dd190656a394abb43c389977b5df747c Mon Sep 17 00:00:00 2001 From: Michael Olbrich Date: Wed, 22 Jun 2022 17:28:44 +0200 Subject: [PATCH 463/609] compositor: destroy the layout after the compositor This way the backends will the actual outputs. And at that point the backend knows the compositor is shutting down so it can handle this differently if necessary. Afterwards wet_compositor_destroy_layout() just deletes the remaining datastructures. Signed-off-by: Michael Olbrich --- compositor/main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compositor/main.c b/compositor/main.c index 044c9df7..2f014404 100644 --- a/compositor/main.c +++ b/compositor/main.c @@ -3763,8 +3763,6 @@ wet_main(int argc, char *argv[], const struct weston_testsuite_data *test_data) ret = wet.compositor->exit_code; out: - wet_compositor_destroy_layout(&wet); - /* free(NULL) is valid, and it won't be NULL if it's used */ free(wet.parsed_options); @@ -3772,6 +3770,7 @@ out: wl_protocol_logger_destroy(protologger); weston_compositor_destroy(wet.compositor); + wet_compositor_destroy_layout(&wet); weston_log_scope_destroy(protocol_scope); protocol_scope = NULL; From f5a4fb5abcb8aeb6b078b6235834cc4ab6176c26 Mon Sep 17 00:00:00 2001 From: Michael Olbrich Date: Wed, 22 Jun 2022 08:58:21 +0200 Subject: [PATCH 464/609] backend-drm: make sure all buffers are released when an output is removed When an output is destroyed then the output state is freed immediately. In this case, the plane state is only partially destroyed because it is the currently active state. This includes the buffer reference. Without the output, the plane will not be updated any more until it is used by a different output (if possible) or the output returns and the plane is used again. As a result, the buffer reference is kept for a long time. This will cause some applications to stall because weston now keeps two buffers (the one here and another one for a different output where the application is now displayed). To avoid this, do a synchronous commit that disables the output. The output needs to be disabled anyways and this way the current state contains no buffers that would remain. `device->state_invalid = true` in drm_output_detach_crtc() is no longer needed, because drm_output_detach_crtc() is called only when initialization failed and the crtc was not yet used or in drm_output_deinit() when the crtc was already disabled with the new synchronous commit. Signed-off-by: Michael Olbrich --- libweston/backend-drm/drm.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index ab97b7ba..96f0166f 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -1811,14 +1811,10 @@ drm_output_attach_crtc(struct drm_output *output) static void drm_output_detach_crtc(struct drm_output *output) { - struct drm_device *device = output->device; struct drm_crtc *crtc = output->crtc; crtc->output = NULL; output->crtc = NULL; - - /* Force resetting unused CRTCs */ - device->state_invalid = true; } static int @@ -1891,6 +1887,13 @@ drm_output_deinit(struct weston_output *base) struct drm_output *output = to_drm_output(base); struct drm_backend *b = to_drm_backend(base->compositor); struct drm_device *device = b->drm; + struct drm_pending_state *pending; + + if (!b->shutting_down) { + pending = drm_pending_state_alloc(device); + drm_output_get_disable_state(pending, output); + drm_pending_state_apply_sync(pending); + } if (b->use_pixman) drm_output_fini_pixman(output); From 6a06a06980867389491f88045e2458a7fc5b535f Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Sat, 25 Jun 2022 04:57:57 +0100 Subject: [PATCH 465/609] CI: Skip certain fontconfig leaks For some reason, the Debian CI setup leaks fontconfig allocations in a way which it does not for me on Fedora. On the assumption that this has been fixed between our older CI Debian and Fedora 36, suppress the leak warning, because we do already call FcFini() which should free it. Signed-off-by: Daniel Stone --- .gitlab-ci/leak-sanitizer.supp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitlab-ci/leak-sanitizer.supp b/.gitlab-ci/leak-sanitizer.supp index 87764945..a919405f 100644 --- a/.gitlab-ci/leak-sanitizer.supp +++ b/.gitlab-ci/leak-sanitizer.supp @@ -6,3 +6,6 @@ leak:cairo_text_extents # Pango thread-global state (not destroyable?) leak:pango_language_get_default + +# This leaks in Debian's fontconfig/Xwayland setup? +leak:FcConfigSubstituteWithPat From c5ed892b1b524b85a741a52a4afc99190c9f0c49 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Sat, 25 Jun 2022 19:53:41 +0100 Subject: [PATCH 466/609] CI: Disable ASan fast unwinding for suppressions Unfortunately just adding suppressions isn't enough; the build of Expat we have in our CI system does not have frame pointers, so ASan's fast unwinder can't see through it. This means that the suppressions we've added won't be taken into account. For now, disable the fast unwinder for the Xwayland test only. Disabling it globally is not practical as it massively increases the per-test runtime, so here (to avoid polluting the build system), we use a per-test wrapper to selectively choose the unwinder. Signed-off-by: Daniel Stone --- .gitlab-ci.yml | 2 +- .gitlab-ci/virtme-scripts/per-test-asan.sh | 20 +++++++++++++++++++ .gitlab-ci/virtme-scripts/run-weston-tests.sh | 4 ++-- 3 files changed, 23 insertions(+), 3 deletions(-) create mode 100755 .gitlab-ci/virtme-scripts/per-test-asan.sh diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index bed0cbbf..98261d86 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -202,7 +202,7 @@ aarch64-debian-container_prep: - $BUILDDIR/weston-virtme - $PREFIX reports: - junit: $BUILDDIR/meson-logs/testlog.junit.xml + junit: $BUILDDIR/meson-logs/testlog-per-test-asan.sh.junit.xml # Same as above, but without running any tests. .build-no-test: diff --git a/.gitlab-ci/virtme-scripts/per-test-asan.sh b/.gitlab-ci/virtme-scripts/per-test-asan.sh new file mode 100755 index 00000000..044ac6d2 --- /dev/null +++ b/.gitlab-ci/virtme-scripts/per-test-asan.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# When running Debian's Xwayland and fontconfig, we hit memory leaks which +# aren't visible on other setups. We do have suppressions for these tests, but +# regrettably ASan can't see through omitted frame pointers in Expat, so for +# Xwayland specifically, we disable fast-unwind. +# +# Doing it globally makes the other tests far, far, too slow to run. +case "$1" in + *xwayland*) + export ASAN_OPTIONS="detect_leaks=0,fast_unwind_on_malloc=0" + ;; + *) + export ASAN_OPTIONS="detect_leaks=0" + ;; +esac + +export ASAN_OPTIONS + +exec "$@" diff --git a/.gitlab-ci/virtme-scripts/run-weston-tests.sh b/.gitlab-ci/virtme-scripts/run-weston-tests.sh index a45f9668..2b156333 100755 --- a/.gitlab-ci/virtme-scripts/run-weston-tests.sh +++ b/.gitlab-ci/virtme-scripts/run-weston-tests.sh @@ -26,13 +26,13 @@ export HOME=/root export PATH=$HOME/.local/bin:$PATH export PATH=/usr/local/bin:$PATH -export ASAN_OPTIONS=detect_leaks=0,atexit=1 export SEATD_LOGLEVEL=debug # run the tests and save the exit status # we give ourselves a very generous timeout multiplier due to ASan overhead echo 0x1f > /sys/module/drm/parameters/debug -seatd-launch -- meson test --no-rebuild --timeout-multiplier 4 +seatd-launch -- meson test --no-rebuild --timeout-multiplier 4 \ + --wrapper $(pwd)/../.gitlab-ci/virtme-scripts/per-test-asan.sh # note that we need to store the return value from the tests in order to # determine if the test suite ran successfully or not. TEST_RES=$? From 6c8ae362bb3b93a260d38fb9829183c025dc4948 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Sat, 25 Jun 2022 04:59:52 +0100 Subject: [PATCH 467/609] CI: Never unload llvmpipe DSO whilst testing This commit is truly horrible. We want to run ASan with leak checking enabled in CI so we can catch memory leaks before they're introduced. This works well with Pixman, and with NIR-only drivers like iris or Panfrost. But when we run under llvmpipe - which we do under CI - we start failing because: - Mesa pulls in llvmpipe via dlopen - llvmpipe pulls in LLVM itself via DT_NEEDED - initialising LLVM's global type/etc systems performs thread-local allocations - llvmpipe can't free those allocations since the application might also be using LLVM - Weston stops using GL and destroys all GL objects, leading to Mesa unloading llvmpipe like it should - with everything disappearing from the process's vmap, ASan can no longer keep track of still-reachable pointers - tests fail because LLVM is 'leaking' Usually, an alternative is to LD_PRELOAD a shim which overrides dlclose() to be a no-op. This is not usable here, because when $LD_PRELOAD is not empty and ASan is not first in it, ASan immediately errors out. Prepending ASan doesn't work, because we run our tests through Meson (which also invokes Ninja), leading to LSan exploding over CPython and Ninja, which is not what we're interested in. It would be possible to inject _both_ ASan and a dlclose-does-nothing shim DSO into the LD_PRELOAD environment for every test, but that seems even worse, especially as Meson strongly discourages globbing for random files in the root. So, here we are, doing what we can: finding where swrast_dri.so (aka llvmpipe) lives, stashing that in an environment variable, and deliberately leaking a dlopen handle which we never close to ensure that neither llvmpipe nor LLVM leave our process's address space before we exit. Signed-off-by: Daniel Stone --- .gitlab-ci/virtme-scripts/run-weston-tests.sh | 4 ++++ tests/meson.build | 1 + tests/weston-test-runner.c | 20 +++++++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/.gitlab-ci/virtme-scripts/run-weston-tests.sh b/.gitlab-ci/virtme-scripts/run-weston-tests.sh index 2b156333..c61bd615 100755 --- a/.gitlab-ci/virtme-scripts/run-weston-tests.sh +++ b/.gitlab-ci/virtme-scripts/run-weston-tests.sh @@ -28,6 +28,10 @@ export PATH=/usr/local/bin:$PATH export SEATD_LOGLEVEL=debug +# Terrible hack, per comment in weston-test-runner.c's main(): find Mesa's +# llvmpipe driver module location +export WESTON_CI_LEAK_DL_HANDLE=$(find /usr/local -name swrast_dri.so -print 2>/dev/null || true) + # run the tests and save the exit status # we give ourselves a very generous timeout multiplier due to ASan overhead echo 0x1f > /sys/module/drm/parameters/debug diff --git a/tests/meson.build b/tests/meson.build index 6a5dea41..06479710 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -14,6 +14,7 @@ lib_test_runner = static_library( dependencies: [ dep_libweston_private_h_deps, dep_wayland_client, + dep_libdl, ], include_directories: common_inc, install: false, diff --git a/tests/weston-test-runner.c b/tests/weston-test-runner.c index 0ab7bc1b..6bea6705 100644 --- a/tests/weston-test-runner.c +++ b/tests/weston-test-runner.c @@ -34,12 +34,18 @@ #include #include #include +#include #include "test-config.h" #include "weston-test-runner.h" #include "weston-testsuite-data.h" #include "shared/string-helpers.h" +/* This is a glibc extension; if we can't use it then make it harmless. */ +#ifndef RTLD_NODELETE +#define RTLD_NODELETE 0 +#endif + /** * \defgroup testharness Test harness * \defgroup testharness_private Test harness private @@ -627,9 +633,23 @@ main(int argc, char *argv[]) enum test_result_code ret; enum test_result_code result = RESULT_OK; const struct fixture_setup_array *fsa; + const char *leak_dl_handle; int fi; int fi_end; + /* This is horrific, but it gives us working leak checking. If we + * actually unload llvmpipe, then we also unload LLVM, and some global + * setup it's done - which llvmpipe can't tear down because the actual + * client might be using LLVM instead. + * + * Turns out if llvmpipe is always live, then the pointers are always + * reachable, so LeakSanitizer just tells us about our own code rather + * than LLVM's, so ... + */ + leak_dl_handle = getenv("WESTON_CI_LEAK_DL_HANDLE"); + if (leak_dl_handle) + (void) dlopen(leak_dl_handle, RTLD_LAZY | RTLD_GLOBAL | RTLD_NODELETE); + harness = weston_test_harness_create(argc, argv); fsa = fixture_setup_array_get_(); From 759712ba05089a91c681fa93fc4e351aae7f7bbe Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Sat, 25 Jun 2022 18:13:18 +0100 Subject: [PATCH 468/609] zuc: Delete support for forking tests ZUC's default mode is to fork for every test case. Unfortunately on AArch64, fork in an ASan-traced program usually takes around 3.6 entire seconds. This was leading to the config-parser test timing out. As none of our remaining ZUC tests even need to fork, just remove all support for it. Signed-off-by: Daniel Stone --- tests/meson.build | 2 - tools/zunitc/inc/zunitc/zunitc.h | 10 - tools/zunitc/src/zuc_collector.c | 427 ------------------------------- tools/zunitc/src/zuc_collector.h | 58 ----- tools/zunitc/src/zuc_context.h | 2 - tools/zunitc/src/zunitc_impl.c | 136 +--------- 6 files changed, 6 insertions(+), 629 deletions(-) delete mode 100644 tools/zunitc/src/zuc_collector.c delete mode 100644 tools/zunitc/src/zuc_collector.h diff --git a/tests/meson.build b/tests/meson.build index 06479710..fe107334 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -107,8 +107,6 @@ lib_zuc = static_library( '../tools/zunitc/inc/zunitc/zunitc_impl.h', '../tools/zunitc/src/zuc_base_logger.c', '../tools/zunitc/src/zuc_base_logger.h', - '../tools/zunitc/src/zuc_collector.c', - '../tools/zunitc/src/zuc_collector.h', '../tools/zunitc/src/zuc_context.h', '../tools/zunitc/src/zuc_event.h', '../tools/zunitc/src/zuc_event_listener.h', diff --git a/tools/zunitc/inc/zunitc/zunitc.h b/tools/zunitc/inc/zunitc/zunitc.h index 16b211ba..d285c59d 100644 --- a/tools/zunitc/inc/zunitc/zunitc.h +++ b/tools/zunitc/inc/zunitc/zunitc.h @@ -239,16 +239,6 @@ zuc_set_repeat(int repeat); void zuc_set_random(int random); -/** - * Controls whether or not to run the tests as forked child processes. - * Defaults to true. - * - * @param spawn true to spawn each test in a forked child process, - * false to run tests directly. - */ -void -zuc_set_spawn(bool spawn); - /** * Enables output in the JUnit XML format. * Defaults to false. diff --git a/tools/zunitc/src/zuc_collector.c b/tools/zunitc/src/zuc_collector.c deleted file mode 100644 index 035c9ce7..00000000 --- a/tools/zunitc/src/zuc_collector.c +++ /dev/null @@ -1,427 +0,0 @@ -/* - * Copyright © 2015 Samsung Electronics Co., Ltd - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "config.h" - -#include "zuc_collector.h" - -#include -#include -#include -#include - -#include -#include "zuc_event_listener.h" -#include "zunitc/zunitc_impl.h" - -#include -#include -#include - -/** - * @file - * General handling of collecting events during testing to pass back to - * main tracking of fork()'d tests. - * - * @note implementation of zuc_process_message() is included here so that - * all child->parent IPC is in a single source file for easier maintenance - * and updating. - */ - -/** - * Internal data struct for processing. - */ -struct collector_data -{ - int *fd; /**< file descriptor to output to. */ - struct zuc_test *test; /**< current test, or NULL. */ -}; - -/** - * Stores an int32_t into the given buffer. - * - * @param ptr the buffer to store to. - * @param val the value to store. - * @return a pointer to the position in the buffer after the stored value. - */ -static char * -pack_int32(char *ptr, int32_t val); - -/** - * Stores an intptr_t into the given buffer. - * - * @param ptr the buffer to store to. - * @param val the value to store. - * @return a pointer to the position in the buffer after the stored value. - */ -static char * -pack_intptr_t(char *ptr, intptr_t val); - -/** - * Extracts a int32_t from the given buffer. - * - * @param ptr the buffer to extract from. - * @param val the value to set. - * @return a pointer to the position in the buffer after the extracted - * value. - */ -static char const * -unpack_int32(char const *ptr, int32_t *val); - -/** - * Extracts a intptr_t from the given buffer. - * - * @param ptr the buffer to extract from. - * @param val the value to set. - * @return a pointer to the position in the buffer after the extracted - * value. - */ -static char const * -unpack_intptr_t(char const *ptr, intptr_t *val); - -/** - * Extracts a length-prefixed string from the given buffer. - * - * @param ptr the buffer to extract from. - * @param str the value to set. - * @return a pointer to the position in the buffer after the extracted - * value. - */ -static char const * -unpack_string(char const *ptr, char **str); - -/** - * Extracts an event from the given buffer. - * - * @param ptr the buffer to extract from. - * @param len the length of the given buffer. - * @return an event that was packed in the buffer - */ -static struct zuc_event * -unpack_event(char const *ptr, int32_t len); - -/** - * Handles an event by either attaching it directly or sending it over IPC - * as needed. - */ -static void -store_event(struct collector_data *cdata, - enum zuc_event_type event_type, char const *file, int line, - enum zuc_fail_state state, enum zuc_check_op op, - enum zuc_check_valtype valtype, - intptr_t val1, intptr_t val2, const char *expr1, const char *expr2); - -static void -destroy(void *data); - -static void -test_started(void *data, struct zuc_test *test); - -static void -test_ended(void *data, struct zuc_test *test); - -static void -check_triggered(void *data, char const *file, int line, - enum zuc_fail_state state, enum zuc_check_op op, - enum zuc_check_valtype valtype, - intptr_t val1, intptr_t val2, - const char *expr1, const char *expr2); - -static void -collect_event(void *data, char const *file, int line, const char *expr1); - -struct zuc_event_listener * -zuc_collector_create(int *pipe_fd) -{ - struct zuc_event_listener *listener = - zalloc(sizeof(struct zuc_event_listener)); - - listener->data = zalloc(sizeof(struct collector_data)); - ((struct collector_data *)listener->data)->fd = pipe_fd; - listener->destroy = destroy; - listener->test_started = test_started; - listener->test_ended = test_ended; - listener->check_triggered = check_triggered; - listener->collect_event = collect_event; - - return listener; -} - -char * -pack_int32(char *ptr, int32_t val) -{ - memcpy(ptr, &val, sizeof(val)); - return ptr + sizeof(val); -} - -char * -pack_intptr_t(char *ptr, intptr_t val) -{ - memcpy(ptr, &val, sizeof(val)); - return ptr + sizeof(val); -} - -static char * -pack_cstr(char *ptr, intptr_t val, int len) -{ - if (val == 0) { /* a null pointer */ - ptr = pack_int32(ptr, -1); - } else { - ptr = pack_int32(ptr, len); - memcpy(ptr, (const char *)val, len); - ptr += len; - } - return ptr; -} - -void -destroy(void *data) -{ - free(data); -} - -void -test_started(void *data, struct zuc_test *test) -{ - struct collector_data *cdata = data; - cdata->test = test; -} - -void -test_ended(void *data, struct zuc_test *test) -{ - struct collector_data *cdata = data; - cdata->test = NULL; -} - -void -check_triggered(void *data, char const *file, int line, - enum zuc_fail_state state, enum zuc_check_op op, - enum zuc_check_valtype valtype, - intptr_t val1, intptr_t val2, - const char *expr1, const char *expr2) -{ - struct collector_data *cdata = data; - if (op != ZUC_OP_TRACEPOINT) - store_event(cdata, ZUC_EVENT_IMMEDIATE, file, line, state, op, - valtype, - val1, val2, expr1, expr2); -} - -void -collect_event(void *data, char const *file, int line, const char *expr1) -{ - struct collector_data *cdata = data; - store_event(cdata, ZUC_EVENT_DEFERRED, file, line, ZUC_CHECK_OK, - ZUC_OP_TRACEPOINT, ZUC_VAL_INT, - 0, 0, expr1, ""); -} - -void -store_event(struct collector_data *cdata, - enum zuc_event_type event_type, char const *file, int line, - enum zuc_fail_state state, enum zuc_check_op op, - enum zuc_check_valtype valtype, - intptr_t val1, intptr_t val2, const char *expr1, const char *expr2) -{ - struct zuc_event *event = zalloc(sizeof(*event)); - event->file = strdup(file); - event->line = line; - event->state = state; - event->op = op; - event->valtype = valtype; - event->val1 = val1; - event->val2 = val2; - if (valtype == ZUC_VAL_CSTR) { - if (val1) - event->val1 = (intptr_t)strdup((const char *)val1); - if (val2) - event->val2 = (intptr_t)strdup((const char *)val2); - } - event->expr1 = strdup(expr1); - event->expr2 = strdup(expr2); - - zuc_attach_event(cdata->test, event, event_type, false); - - if (*cdata->fd == -1) { - } else { - /* Need to pass it back */ - int sent; - int count; - int expr1_len = strlen(expr1); - int expr2_len = strlen(expr2); - int val1_len = - ((valtype == ZUC_VAL_CSTR) && val1) ? - strlen((char *)val1) : 0; - int val2_len = - ((valtype == ZUC_VAL_CSTR) && val2) ? - strlen((char *)val2) : 0; - int file_len = strlen(file); - int len = (sizeof(int32_t) * 9) + file_len - + (sizeof(intptr_t) * 2) - + ((valtype == ZUC_VAL_CSTR) ? - (sizeof(int32_t) * 2) + val1_len + val2_len : 0) - + expr1_len + expr2_len; - char *buf = zalloc(len); - - char *ptr = pack_int32(buf, len - 4); - ptr = pack_int32(ptr, event_type); - ptr = pack_int32(ptr, file_len); - memcpy(ptr, file, file_len); - ptr += file_len; - ptr = pack_int32(ptr, line); - ptr = pack_int32(ptr, state); - ptr = pack_int32(ptr, op); - ptr = pack_int32(ptr, valtype); - if (valtype == ZUC_VAL_CSTR) { - ptr = pack_cstr(ptr, val1, val1_len); - ptr = pack_cstr(ptr, val2, val2_len); - } else { - ptr = pack_intptr_t(ptr, val1); - ptr = pack_intptr_t(ptr, val2); - } - - ptr = pack_int32(ptr, expr1_len); - if (expr1_len) { - memcpy(ptr, expr1, expr1_len); - ptr += expr1_len; - } - ptr = pack_int32(ptr, expr2_len); - if (expr2_len) { - memcpy(ptr, expr2, expr2_len); - ptr += expr2_len; - } - - - sent = 0; - while (sent < len) { - count = write(*cdata->fd, buf, len); - if (count == -1) - break; - sent += count; - } - - free(buf); - } -} - -char const * -unpack_int32(char const *ptr, int32_t *val) -{ - memcpy(val, ptr, sizeof(*val)); - return ptr + sizeof(*val); -} - -char const * -unpack_intptr_t(char const *ptr, intptr_t *val) -{ - memcpy(val, ptr, sizeof(*val)); - return ptr + sizeof(*val); -} - -char const * -unpack_string(char const *ptr, char **str) -{ - int32_t len = 0; - ptr = unpack_int32(ptr, &len); - *str = zalloc(len + 1); - if (len) - memcpy(*str, ptr, len); - ptr += len; - return ptr; -} - -static char const * -unpack_cstr(char const *ptr, char **str) -{ - int32_t len = 0; - ptr = unpack_int32(ptr, &len); - if (len < 0) { - *str = NULL; - } else { - *str = zalloc(len + 1); - if (len) - memcpy(*str, ptr, len); - ptr += len; - } - return ptr; -} - -struct zuc_event * -unpack_event(char const *ptr, int32_t len) -{ - int32_t val = 0; - struct zuc_event *evt = zalloc(sizeof(*evt)); - char const *tmp = unpack_string(ptr, &evt->file); - tmp = unpack_int32(tmp, &evt->line); - - tmp = unpack_int32(tmp, &val); - evt->state = val; - tmp = unpack_int32(tmp, &val); - evt->op = val; - tmp = unpack_int32(tmp, &val); - evt->valtype = val; - - if (evt->valtype == ZUC_VAL_CSTR) { - char *ptr = NULL; - tmp = unpack_cstr(tmp, &ptr); - evt->val1 = (intptr_t)ptr; - tmp = unpack_cstr(tmp, &ptr); - evt->val2 = (intptr_t)ptr; - } else { - tmp = unpack_intptr_t(tmp, &evt->val1); - tmp = unpack_intptr_t(tmp, &evt->val2); - } - - tmp = unpack_string(tmp, &evt->expr1); - tmp = unpack_string(tmp, &evt->expr2); - - return evt; -} - -int -zuc_process_message(struct zuc_test *test, int fd) -{ - char buf[4] = {}; - int got = read(fd, buf, 4); - if (got == 4) { - enum zuc_event_type event_type = ZUC_EVENT_IMMEDIATE; - int32_t val = 0; - int32_t len = 0; - const char *tmp = NULL; - char *raw = NULL; - unpack_int32(buf, &len); - raw = zalloc(len); - got = read(fd, raw, len); - - tmp = unpack_int32(raw, &val); - event_type = val; - - struct zuc_event *evt = unpack_event(tmp, len - (tmp - raw)); - zuc_attach_event(test, evt, event_type, true); - free(raw); - } - return got; -} diff --git a/tools/zunitc/src/zuc_collector.h b/tools/zunitc/src/zuc_collector.h deleted file mode 100644 index 56f8a5fc..00000000 --- a/tools/zunitc/src/zuc_collector.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright © 2015 Samsung Electronics Co., Ltd - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef ZUC_COLLECTOR_H -#define ZUC_COLLECTOR_H - -struct zuc_event_listener; -struct zuc_test; - -/** - * Creates a new instance of an event collector that will attach events - * to the current test directly or via connection from child to parent. - * - * @param pipe_fd pointer to the file descriptor to use for communication if - * needed. If the value is -1 the events will be attached directly to the - * current test. Otherwise events will be passed back via IPC over this - * pipe with the expectation that the payload will be handled in the parent - * process via zuc_process_message(). - * @return a new collector instance. - * @see zuc_process_message() - */ -struct zuc_event_listener * -zuc_collector_create(int *pipe_fd); - -/** - * Reads events from the given pipe and processes them. - * - * @param test the currently active test to attach events for. - * @param pipe_fd the file descriptor of the pipe to read from. - * @return a positive value if a message was received, 0 if the end has - * been reached and -1 if an error has occurred. - */ -int -zuc_process_message(struct zuc_test *test, int pipe_fd); - -#endif /* ZUC_COLLECTOR_H */ diff --git a/tools/zunitc/src/zuc_context.h b/tools/zunitc/src/zuc_context.h index 609f34bd..c476f5e1 100644 --- a/tools/zunitc/src/zuc_context.h +++ b/tools/zunitc/src/zuc_context.h @@ -42,11 +42,9 @@ struct zuc_context { int repeat; int random; unsigned int seed; - bool spawn; bool break_on_failure; bool output_tap; bool output_junit; - int fds[2]; char *filter; struct zuc_slinked *listeners; diff --git a/tools/zunitc/src/zunitc_impl.c b/tools/zunitc/src/zunitc_impl.c index 395bdd74..18f03015 100644 --- a/tools/zunitc/src/zunitc_impl.c +++ b/tools/zunitc/src/zunitc_impl.c @@ -41,7 +41,6 @@ #include "zunitc/zunitc.h" #include "zuc_base_logger.h" -#include "zuc_collector.h" #include "zuc_context.h" #include "zuc_event_listener.h" #include "zuc_junit_reporter.h" @@ -82,9 +81,7 @@ static struct zuc_context g_ctx = { .fatal = false, .repeat = 0, .random = 0, - .spawn = true, .break_on_failure = false, - .fds = {-1, -1}, .listeners = NULL, @@ -141,12 +138,6 @@ zuc_set_random(int random) g_ctx.random = random; } -void -zuc_set_spawn(bool spawn) -{ - g_ctx.spawn = spawn; -} - void zuc_set_break_on_failure(bool break_on_failure) { @@ -525,7 +516,6 @@ zuc_initialize(int *argc, char *argv[], bool *help_flagged) { int rc = EXIT_FAILURE; bool opt_help = false; - bool opt_nofork = false; bool opt_list = false; int opt_repeat = 0; int opt_random = 0; @@ -537,7 +527,6 @@ zuc_initialize(int *argc, char *argv[], bool *help_flagged) int argc_in = *argc; const struct weston_option options[] = { - { WESTON_OPTION_BOOLEAN, "zuc-nofork", 0, &opt_nofork }, { WESTON_OPTION_BOOLEAN, "zuc-list-tests", 0, &opt_list }, { WESTON_OPTION_INTEGER, "zuc-repeat", 0, &opt_repeat }, { WESTON_OPTION_INTEGER, "zuc-random", 0, &opt_random }, @@ -633,7 +622,6 @@ zuc_initialize(int *argc, char *argv[], bool *help_flagged) " --zuc-break-on-failure\n" " --zuc-filter=FILTER\n" " --zuc-list-tests\n" - " --zuc-nofork\n" #if ENABLE_JUNIT_XML " --zuc-output-xml\n" #endif @@ -650,7 +638,6 @@ zuc_initialize(int *argc, char *argv[], bool *help_flagged) } else { zuc_set_repeat(opt_repeat); zuc_set_random(opt_random); - zuc_set_spawn(!opt_nofork); zuc_set_break_on_failure(opt_break_on_failure); zuc_set_output_junit(opt_junit); rc = EXIT_SUCCESS; @@ -937,11 +924,6 @@ zuc_cleanup(void) free(g_ctx.filter); g_ctx.filter = 0; - for (i = 0; i < 2; ++i) - if (g_ctx.fds[i] != -1) { - close(g_ctx.fds[i]); - g_ctx.fds[i] = -1; - } if (g_ctx.listeners) { struct zuc_slinked *curr = g_ctx.listeners; @@ -1017,108 +999,9 @@ zuc_list_tests(void) } } -static void -spawn_test(struct zuc_test *test, void *test_data, - void (*cleanup_fn)(void *data), void *cleanup_data) -{ - pid_t pid = -1; - - if (!test || (!test->fn && !test->fn_f)) - return; - - if (pipe2(g_ctx.fds, O_CLOEXEC)) { - printf("%s:%d: error: Unable to create pipe: %d\n", - __FILE__, __LINE__, errno); - mark_failed(test, ZUC_CHECK_ERROR); - return; - } - - fflush(NULL); /* important. avoid duplication of output */ - pid = fork(); - switch (pid) { - case -1: /* Error forking */ - printf("%s:%d: error: Problem with fork: %d\n", - __FILE__, __LINE__, errno); - mark_failed(test, ZUC_CHECK_ERROR); - close(g_ctx.fds[0]); - g_ctx.fds[0] = -1; - close(g_ctx.fds[1]); - g_ctx.fds[1] = -1; - break; - case 0: { /* child */ - int rc = EXIT_SUCCESS; - close(g_ctx.fds[0]); - g_ctx.fds[0] = -1; - - if (test->fn_f) - test->fn_f(test_data); - else - test->fn(); - - if (test_has_failure(test)) - rc = EXIT_FAILURE; - else if (test_has_skip(test)) - rc = ZUC_EXIT_SKIP; - - /* Avoid confusing memory tools like valgrind */ - if (cleanup_fn) - cleanup_fn(cleanup_data); - - zuc_cleanup(); - exit(rc); - } - default: { /* parent */ - ssize_t rc = 0; - siginfo_t info = {}; - - close(g_ctx.fds[1]); - g_ctx.fds[1] = -1; - - do { - rc = zuc_process_message(g_ctx.curr_test, - g_ctx.fds[0]); - } while (rc > 0); - close(g_ctx.fds[0]); - g_ctx.fds[0] = -1; - - if (waitid(P_ALL, 0, &info, WEXITED)) { - printf("%s:%d: error: waitid failed. (%d)\n", - __FILE__, __LINE__, errno); - mark_failed(test, ZUC_CHECK_ERROR); - } else { - switch (info.si_code) { - case CLD_EXITED: { - int exit_code = info.si_status; - switch(exit_code) { - case EXIT_SUCCESS: - break; - case ZUC_EXIT_SKIP: - if (!test_has_skip(g_ctx.curr_test) && - !test_has_failure(g_ctx.curr_test)) - ZUC_SKIP("Child exited SKIP"); - break; - default: - /* unexpected failure */ - if (!test_has_failure(g_ctx.curr_test)) - ZUC_ASSERT_EQ(0, exit_code); - } - break; - } - case CLD_KILLED: - case CLD_DUMPED: - printf("%s:%d: error: signaled: %d\n", - __FILE__, __LINE__, info.si_status); - mark_failed(test, ZUC_CHECK_ERROR); - break; - } - } - } - } -} - static void run_single_test(struct zuc_test *test,const struct zuc_fixture *fxt, - void *case_data, bool spawn) + void *case_data) { long elapsed = 0; struct timespec begin; @@ -1146,15 +1029,10 @@ run_single_test(struct zuc_test *test,const struct zuc_fixture *fxt, /* Need to re-check these, as fixtures might have changed test state. */ if (!test->fatal && !test->skipped) { - if (spawn) { - spawn_test(test, test_data, - cleanup_fn, cleanup_data); - } else { - if (test->fn_f) - test->fn_f(test_data); - else - test->fn(); - } + if (test->fn_f) + test->fn_f(test_data); + else + test->fn(); } clock_gettime(TARGET_TIMER, &end); @@ -1204,8 +1082,7 @@ run_single_case(struct zuc_case *test_case) if (curr->disabled) { dispatch_test_disabled(&g_ctx, curr); } else { - run_single_test(curr, fxt, case_data, - g_ctx.spawn); + run_single_test(curr, fxt, case_data); if (curr->skipped) test_case->skipped++; if (curr->failed) @@ -1313,7 +1190,6 @@ zucimpl_run_tests(void) return EXIT_FAILURE; if (g_ctx.listeners == NULL) { - zuc_add_event_listener(zuc_collector_create(&(g_ctx.fds[1]))); zuc_add_event_listener(zuc_base_logger_create()); if (g_ctx.output_junit) zuc_add_event_listener(zuc_junit_reporter_create()); From f52231660e69992d459ace4292944f595b1db624 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Sat, 25 Jun 2022 06:12:50 +0100 Subject: [PATCH 469/609] tests: Use memstream for config-parser test Using real files is unnecessarily heavy and error-prone. Fixes timeouts seen on CI with ASan. Signed-off-by: Daniel Stone --- include/libweston/config-parser.h | 3 + shared/config-parser.c | 101 +++++++++++++++++++----------- tests/config-parser-test.c | 24 +++---- 3 files changed, 81 insertions(+), 47 deletions(-) diff --git a/include/libweston/config-parser.h b/include/libweston/config-parser.h index 343ff53d..81f28b59 100644 --- a/include/libweston/config-parser.h +++ b/include/libweston/config-parser.h @@ -88,6 +88,9 @@ weston_config_section_get_bool(struct weston_config_section *section, const char * weston_config_get_name_from_env(void); +struct weston_config * +weston_config_parse_fp(FILE *file); + struct weston_config * weston_config_parse(const char *name); diff --git a/shared/config-parser.c b/shared/config-parser.c index c19baa0d..30779ae4 100644 --- a/shared/config-parser.c +++ b/shared/config-parser.c @@ -381,41 +381,15 @@ section_add_entry(struct weston_config_section *section, return entry; } -WL_EXPORT struct weston_config * -weston_config_parse(const char *name) +static bool +weston_config_parse_internal(struct weston_config *config, FILE *fp) { - FILE *fp; - char line[512], *p; - struct stat filestat; - struct weston_config *config; struct weston_config_section *section = NULL; - int i, fd; - - config = zalloc(sizeof *config); - if (config == NULL) - return NULL; + char line[512], *p; + int i; wl_list_init(&config->section_list); - fd = open_config_file(config, name); - if (fd == -1) { - free(config); - return NULL; - } - - if (fstat(fd, &filestat) < 0 || - !S_ISREG(filestat.st_mode)) { - close(fd); - free(config); - return NULL; - } - - fp = fdopen(fd, "r"); - if (fp == NULL) { - free(config); - return NULL; - } - while (fgets(line, sizeof line, fp)) { switch (line[0]) { case '#': @@ -426,9 +400,7 @@ weston_config_parse(const char *name) if (!p || p[1] != '\n') { fprintf(stderr, "malformed " "section header: %s\n", line); - fclose(fp); - weston_config_destroy(config); - return NULL; + return false; } p[0] = '\0'; section = config_add_section(config, &line[1]); @@ -438,9 +410,7 @@ weston_config_parse(const char *name) if (!p || p == line || !section) { fprintf(stderr, "malformed " "config line: %s\n", line); - fclose(fp); - weston_config_destroy(config); - return NULL; + return false; } p[0] = '\0'; @@ -457,8 +427,67 @@ weston_config_parse(const char *name) } } + return true; +} + +WESTON_EXPORT_FOR_TESTS struct weston_config * +weston_config_parse_fp(FILE *file) +{ + struct weston_config *config = zalloc(sizeof(*config)); + + if (config == NULL) + return NULL; + + if (!weston_config_parse_internal(config, file)) { + weston_config_destroy(config); + return NULL; + } + + return config; +} + +WL_EXPORT struct weston_config * +weston_config_parse(const char *name) +{ + FILE *fp; + struct stat filestat; + struct weston_config *config; + int fd; + bool ret; + + config = zalloc(sizeof *config); + if (config == NULL) + return NULL; + + fd = open_config_file(config, name); + if (fd == -1) { + free(config); + return NULL; + } + + if (fstat(fd, &filestat) < 0 || + !S_ISREG(filestat.st_mode)) { + close(fd); + free(config); + return NULL; + } + + fp = fdopen(fd, "r"); + if (fp == NULL) { + close(fd); + free(config); + return NULL; + } + + ret = weston_config_parse_internal(config, fp); + fclose(fp); + if (!ret) { + weston_config_destroy(config); + return NULL; + } + return config; } diff --git a/tests/config-parser-test.c b/tests/config-parser-test.c index 626c01d9..33ad5d0b 100644 --- a/tests/config-parser-test.c +++ b/tests/config-parser-test.c @@ -48,23 +48,25 @@ static struct weston_config * load_config(const char *text) { struct weston_config *config = NULL; - int len = 0; - int fd = -1; - char file[] = "/tmp/weston-config-parser-test-XXXXXX"; + char *content = NULL; + size_t file_len = 0; + int write_len; + FILE *file; - ZUC_ASSERTG_NOT_NULL(text, out); + file = open_memstream(&content, &file_len); + ZUC_ASSERTG_NOT_NULL(file, out); - fd = mkstemp(file); - ZUC_ASSERTG_NE(-1, fd, out); + write_len = fwrite(text, 1, strlen(text), file); + ZUC_ASSERTG_EQ((int)strlen(text), write_len, out_close); - len = write(fd, text, strlen(text)); - ZUC_ASSERTG_EQ((int)strlen(text), len, out_close); + ZUC_ASSERTG_EQ(fflush(file), 0, out_close); + fseek(file, 0L, SEEK_SET); - config = weston_config_parse(file); + config = weston_config_parse_fp(file); out_close: - close(fd); - unlink(file); + fclose(file); + free(content); out: return config; } From 23c8dc7b277062c3a7e0cf0ce7f8cdb28fb4fe29 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Sat, 25 Jun 2022 03:56:45 +0100 Subject: [PATCH 470/609] tests: Check requirements after setting up args Setting up the arguments may consume some of the arguments, e.g. if we provide a config file or extra modules, then the test harness is expected to be responsible for freeing those arguments. Checking the requirements and bailing first means that we never do that, and thus skipped tests result in leaks. Flip the order so we set up the args first and skip last, so we can consistently take ownership of all the provided setup parameters. Signed-off-by: Daniel Stone --- tests/weston-test-fixture-compositor.c | 89 +++++++++++++------------- 1 file changed, 46 insertions(+), 43 deletions(-) diff --git a/tests/weston-test-fixture-compositor.c b/tests/weston-test-fixture-compositor.c index 228bb48e..2e9333a2 100644 --- a/tests/weston-test-fixture-compositor.c +++ b/tests/weston-test-fixture-compositor.c @@ -291,48 +291,8 @@ execute_compositor(const struct compositor_setup *setup, struct prog_args args; char *tmp; const char *ctmp, *drm_device; - int ret, lock_fd = -1; - - if (setenv("WESTON_MODULE_MAP", WESTON_MODULE_MAP, 0) < 0 || - setenv("WESTON_DATA_DIR", WESTON_DATA_DIR, 0) < 0) { - fprintf(stderr, "Error: environment setup failed.\n"); - return RESULT_HARD_ERROR; - } - -#ifndef BUILD_DRM_COMPOSITOR - if (setup->backend == WESTON_BACKEND_DRM) { - fprintf(stderr, "DRM-backend required but not built, skipping.\n"); - return RESULT_SKIP; - } -#endif - -#ifndef BUILD_RDP_COMPOSITOR - if (setup->backend == WESTON_BACKEND_RDP) { - fprintf(stderr, "RDP-backend required but not built, skipping.\n"); - return RESULT_SKIP; - } -#endif - -#ifndef BUILD_WAYLAND_COMPOSITOR - if (setup->backend == WESTON_BACKEND_WAYLAND) { - fprintf(stderr, "wayland-backend required but not built, skipping.\n"); - return RESULT_SKIP; - } -#endif - -#ifndef BUILD_X11_COMPOSITOR - if (setup->backend == WESTON_BACKEND_X11) { - fprintf(stderr, "X11-backend required but not built, skipping.\n"); - return RESULT_SKIP; - } -#endif - -#ifndef ENABLE_EGL - if (setup->renderer == RENDERER_GL) { - fprintf(stderr, "GL-renderer required but not built, skipping.\n"); - return RESULT_SKIP; - } -#endif + int lock_fd = -1; + int ret = RESULT_OK; prog_args_init(&args); @@ -424,7 +384,50 @@ execute_compositor(const struct compositor_setup *setup, test_data.test_quirks = setup->test_quirks; test_data.test_private_data = data; prog_args_save(&args); - ret = wet_main(args.argc, args.argv, &test_data); + + if (setenv("WESTON_MODULE_MAP", WESTON_MODULE_MAP, 0) < 0 || + setenv("WESTON_DATA_DIR", WESTON_DATA_DIR, 0) < 0) { + fprintf(stderr, "Error: environment setup failed.\n"); + ret = RESULT_HARD_ERROR; + } + +#ifndef BUILD_DRM_COMPOSITOR + if (setup->backend == WESTON_BACKEND_DRM) { + fprintf(stderr, "DRM-backend required but not built, skipping.\n"); + ret = RESULT_SKIP; + } +#endif + +#ifndef BUILD_RDP_COMPOSITOR + if (setup->backend == WESTON_BACKEND_RDP) { + fprintf(stderr, "RDP-backend required but not built, skipping.\n"); + ret = RESULT_SKIP; + } +#endif + +#ifndef BUILD_WAYLAND_COMPOSITOR + if (setup->backend == WESTON_BACKEND_WAYLAND) { + fprintf(stderr, "wayland-backend required but not built, skipping.\n"); + ret = RESULT_SKIP; + } +#endif + +#ifndef BUILD_X11_COMPOSITOR + if (setup->backend == WESTON_BACKEND_X11) { + fprintf(stderr, "X11-backend required but not built, skipping.\n"); + ret = RESULT_SKIP; + } +#endif + +#ifndef ENABLE_EGL + if (setup->renderer == RENDERER_GL) { + fprintf(stderr, "GL-renderer required but not built, skipping.\n"); + ret = RESULT_SKIP; + } +#endif + + if (ret == RESULT_OK) + ret = wet_main(args.argc, args.argv, &test_data); out: prog_args_fini(&args); From 4aa885d4af53817427f9cc552ff5a1da3f5b2f1a Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 5 Jul 2022 13:24:59 +0100 Subject: [PATCH 471/609] xwayland: Don't dup() displayfd pipe For some reason, this causes the reads to get completely lost sometimes in CI. Signed-off-by: Daniel Stone --- compositor/xwayland.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/compositor/xwayland.c b/compositor/xwayland.c index 374f549d..16827e6a 100644 --- a/compositor/xwayland.c +++ b/compositor/xwayland.c @@ -134,11 +134,6 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd return 1; } - if (os_fd_set_cloexec(display_fd[1]) != 0) { - weston_log("failed setting Xwayland end of displayfd as cloexec\n"); - return 1; - } - pid = fork(); switch (pid) { case 0: @@ -153,12 +148,16 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd ret &= dup_fd_to_string(abstract_fd_str, abstract_fd); ret &= dup_fd_to_string(unix_fd_str, unix_fd); ret &= dup_fd_to_string(wm_fd_str, wm[1]); - ret &= dup_fd_to_string(display_fd_str, display_fd[1]); if (!ret) _exit(EXIT_FAILURE); setenv("WAYLAND_SOCKET", wayland_socket_str, 1); + if (snprintf(display_fd_str, sizeof(display_fd_str), "%d", + display_fd[1]) <= 0) { + _exit(EXIT_FAILURE); + } + if (execl(xserver, xserver, display, From 5b11f4066af2c0bb85743ce1ef6f6fa8fa5729d3 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 24 Jun 2022 16:43:12 +0100 Subject: [PATCH 472/609] xwayland: Allow for old WM_NORMAL_HINTS There are two versions of WM_NORMAL_HINTS: the original pre-ICCCM version (standardised by Xlib itself?) provides 15 elements of 32 bits each, with the ICCCM v1 extending this by 3 additional elements. Since the flags are enough to identify which elements are present, and the structure is append-only, we only need to read the minimum length between what the user provided and what we support. Fixes a heap overrun found with ASan. Signed-off-by: Daniel Stone --- xwayland/window-manager.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/xwayland/window-manager.c b/xwayland/window-manager.c index 7889f871..73453e0b 100644 --- a/xwayland/window-manager.c +++ b/xwayland/window-manager.c @@ -576,9 +576,13 @@ weston_wm_window_read_properties(struct weston_wm_window *window) } break; case TYPE_WM_NORMAL_HINTS: + /* WM_NORMAL_HINTS can be either 15 or 18 CARD32s */ + memset(&window->size_hints, 0, + sizeof(window->size_hints)); memcpy(&window->size_hints, xcb_get_property_value(reply), - sizeof window->size_hints); + MIN(sizeof(window->size_hints), + reply->value_len * 4)); break; case TYPE_NET_WM_STATE: window->fullscreen = 0; From 18897253d44449fadb1307a57eac7f4d203f3764 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 24 Jun 2022 16:10:48 +0100 Subject: [PATCH 473/609] xwayland: Add compositor destroy listener to free allocation Otherwise we just leak this into the void. Not good. Signed-off-by: Daniel Stone --- compositor/xwayland.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/compositor/xwayland.c b/compositor/xwayland.c index 16827e6a..3de00f62 100644 --- a/compositor/xwayland.c +++ b/compositor/xwayland.c @@ -45,6 +45,7 @@ struct wet_xwayland { struct weston_compositor *compositor; + struct wl_listener compositor_destroy_listener; const struct weston_xwayland_api *api; struct weston_xwayland *xwayland; struct wl_event_source *display_fd_source; @@ -216,6 +217,21 @@ xserver_cleanup(struct weston_process *process, int status) wxw->client = NULL; } +static void +wxw_compositor_destroy(struct wl_listener *listener, void *data) +{ + struct wet_xwayland *wxw = + wl_container_of(listener, wxw, compositor_destroy_listener); + + /* Don't call xserver_exited because Xwayland's own destroy handler + * already does this for us ... */ + if (wxw->client) + kill(wxw->process.pid, SIGTERM); + + wl_list_remove(&wxw->process.link); + free(wxw); +} + int wet_load_xwayland(struct weston_compositor *comp) { @@ -245,9 +261,13 @@ wet_load_xwayland(struct weston_compositor *comp) wxw->compositor = comp; wxw->api = api; wxw->xwayland = xwayland; + wl_list_init(&wxw->process.link); wxw->process.cleanup = xserver_cleanup; + wxw->compositor_destroy_listener.notify = wxw_compositor_destroy; if (api->listen(xwayland, wxw, spawn_xserver) < 0) return -1; + wl_signal_add(&comp->destroy_signal, &wxw->compositor_destroy_listener); + return 0; } From 00641368e206afd7adfa0d2eff2e9b3d256a3e6a Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 1 Jul 2022 12:24:03 +0300 Subject: [PATCH 474/609] compositor: deprecate cms-static and cms-colord plugins While developing the new color management, keeping these old plugins working would require extra work. Let's deprecate these to see if anyone cares about them, pending removal after the Weston 11.0.0 release. CI will keep building these in the "Full build" configuration only. Doc and no-GL builds are no different for these plugins, so there these are no longer built. See https://gitlab.freedesktop.org/wayland/weston/-/issues/634 Signed-off-by: Pekka Paalanen --- .gitlab-ci.yml | 2 ++ compositor/meson.build | 18 +++++++++++------- meson_options.txt | 12 +++++++++--- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 98261d86..dd018f83 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -283,6 +283,8 @@ aarch64-debian-container_prep: -Dwerror=true -Dtest-skip-is-failure=true -Dlauncher-libseat=true + -Ddeprecated-color-management-static=true + -Ddeprecated-color-management-colord=true after_script: - ninja -C "$BUILDDIR" coverage-html > "$BUILDDIR/meson-logs/ninja-coverage-html.txt" - ninja -C "$BUILDDIR" coverage-xml diff --git a/compositor/meson.build b/compositor/meson.build index eb3c9de5..1692481c 100644 --- a/compositor/meson.build +++ b/compositor/meson.build @@ -96,14 +96,14 @@ if get_option('screenshare') env_modmap += 'screen-share.so=@0@;'.format(plugin_screenshare.full_path()) endif -if get_option('color-management-lcms') +if get_option('deprecated-color-management-static') srcs_lcms = [ 'cms-static.c', 'cms-helper.c', ] if not dep_lcms2.found() - error('cms-static requires lcms2 which was not found. Or, you can use \'-Dcolor-management-lcms=false\'.') + error('cms-static requires lcms2 which was not found. Or, you can use \'-Ddeprecated-color-management-static=false\'.') endif config_h.set('HAVE_LCMS', '1') @@ -119,11 +119,13 @@ if get_option('color-management-lcms') install_rpath: '$ORIGIN' ) env_modmap += 'cms-static.so=@0@;'.format(plugin_lcms.full_path()) + + warning('deprecated-color-management-static is enabled. This will go away, see https://gitlab.freedesktop.org/wayland/weston/-/issues/634') endif -if get_option('color-management-colord') - if not get_option('color-management-lcms') - error('LCMS must be enabled to support colord. Or, you can use \'-Dcolor-management-colord=false\'.') +if get_option('deprecated-color-management-colord') + if not get_option('deprecated-color-management-static') + error('deprecated-color-management-static must be enabled to support colord. Or, you can use \'-Ddeprecated-color-management-colord=false\'.') endif srcs_colord = [ @@ -133,7 +135,7 @@ if get_option('color-management-colord') dep_colord = dependency('colord', version: '>= 0.1.27', required: false) if not dep_colord.found() - error('cms-colord requires colord >= 0.1.27 which was not found. Or, you can use \'-Dcolor-management-colord=false\'.') + error('cms-colord requires colord >= 0.1.27 which was not found. Or, you can use \'-Ddeprecated-color-management-colord=false\'.') endif plugin_colord_deps = [ dep_libweston_public, dep_colord, dep_lcms2 ] @@ -141,7 +143,7 @@ if get_option('color-management-colord') foreach depname : [ 'glib-2.0', 'gobject-2.0' ] dep = dependency(depname, required: false) if not dep.found() - error('cms-colord requires \'@0@\' which was not found. If you rather not build this, set \'-Dcolor-management-colord=false\'.'.format(depname)) + error('cms-colord requires \'@0@\' which was not found. If you rather not build this, set \'-Ddeprecated-color-management-colord=false\'.'.format(depname)) endif plugin_colord_deps += dep endforeach @@ -156,6 +158,8 @@ if get_option('color-management-colord') install_dir: dir_module_weston ) env_modmap += 'cms-colord.so=@0@;'.format(plugin_colord.full_path()) + + warning('deprecated-color-management-colord is enabled. This will go away, see https://gitlab.freedesktop.org/wayland/weston/-/issues/634') endif if get_option('systemd') diff --git a/meson_options.txt b/meson_options.txt index b2c4a50f..055457d5 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -132,10 +132,16 @@ option( description: 'Compositor color management: Little CMS' ) option( - 'color-management-colord', + 'deprecated-color-management-static', type: 'boolean', - value: true, - description: 'Compositor color management: colord (requires lcms)' + value: false, + description: 'DEPRECATED: color management plugin cms-static' +) +option( + 'deprecated-color-management-colord', + type: 'boolean', + value: false, + description: 'DEPRECATED: color management plugin cms-colord (requires cms-static)' ) option( From ff94ba33cd20b1f9ec837ebcbb60f78f634f8a9a Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 8 Jul 2022 11:51:41 +0300 Subject: [PATCH 475/609] compositor: fix shutdown when xwayland failed to start This patch fixes the following: AddressSanitizer:DEADLYSIGNAL ================================================================= ==528956==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000008 (pc 0x7fbc5d66bdd7 bp 0x7ffd465573c0 sp 0x7ffd46557398 T0) ==528956==The signal is caused by a WRITE memory access. ==528956==Hint: address points to the zero page. #0 0x7fbc5d66bdd7 in wl_list_remove ../../git/wayland/src/wayland-util.c:56 #1 0x7fbc5cb8869e in wxw_compositor_destroy ../../git/weston/compositor/xwayland.c:357 #2 0x7fbc5baf3ca6 in weston_signal_emit_mutable ../../git/weston/shared/signal.c:62 #3 0x7fbc5ba4d6f9 in weston_compositor_destroy ../../git/weston/libweston/compositor.c:8639 #4 0x7fbc5cb7a5f2 in wet_main ../../git/weston/compositor/main.c:3772 #5 0x55bd13de2179 in main ../../git/weston/compositor/executable.c:33 #6 0x7fbc5be61d09 in __libc_start_main ../csu/libc-start.c:308 #7 0x55bd13de2099 in _start (/home/pq/local/bin/weston+0x1099) The problem is triggered by configuring a bad path to Xwayland in weston.ini, which causes exec() to fail. The fork() succeeded though, which means the weston_process was already on the watch list, and the watch can be handled, making sigchl_handler() leave the link uninitialized. Making sure the link remains removable fixes this. Fixes: 18897253d44449fadb1307a57eac7f4d203f3764 Signed-off-by: Pekka Paalanen --- compositor/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/compositor/main.c b/compositor/main.c index 2f014404..67a90e21 100644 --- a/compositor/main.c +++ b/compositor/main.c @@ -357,6 +357,7 @@ sigchld_handler(int signal_number, void *data) } wl_list_remove(&p->link); + wl_list_init(&p->link); p->cleanup(p, status); } From e88a62243462c0d5795b8af0cde6722664fba3ed Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 7 Jul 2022 10:51:56 +0300 Subject: [PATCH 476/609] Revert "xwayland: Don't dup() displayfd pipe" This reverts commit 4aa885d4af53817427f9cc552ff5a1da3f5b2f1a. Turns out the problem was not about dupping fds at all, but calling non-async-signal-safe functions like strdup() between fork() and exec() in the child process. For more discussion, see: https://gitlab.freedesktop.org/wayland/weston/-/merge_requests/941#note_1457053 Signed-off-by: Pekka Paalanen --- compositor/xwayland.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/compositor/xwayland.c b/compositor/xwayland.c index 3de00f62..b5308533 100644 --- a/compositor/xwayland.c +++ b/compositor/xwayland.c @@ -135,6 +135,11 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd return 1; } + if (os_fd_set_cloexec(display_fd[1]) != 0) { + weston_log("failed setting Xwayland end of displayfd as cloexec\n"); + return 1; + } + pid = fork(); switch (pid) { case 0: @@ -149,16 +154,12 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd ret &= dup_fd_to_string(abstract_fd_str, abstract_fd); ret &= dup_fd_to_string(unix_fd_str, unix_fd); ret &= dup_fd_to_string(wm_fd_str, wm[1]); + ret &= dup_fd_to_string(display_fd_str, display_fd[1]); if (!ret) _exit(EXIT_FAILURE); setenv("WAYLAND_SOCKET", wayland_socket_str, 1); - if (snprintf(display_fd_str, sizeof(display_fd_str), "%d", - display_fd[1]) <= 0) { - _exit(EXIT_FAILURE); - } - if (execl(xserver, xserver, display, From 71b40fc76b4c87969a9c0949409f83130567a890 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 7 Jul 2022 11:15:42 +0300 Subject: [PATCH 477/609] xwayland: move config reading up Doing any kind of memory allocation calls between fork() and exec() in the child process is prone to deadlocks and explosions. In general, only async-signal-safe functions are safe there. Move the config access to the parent process before fork() to avoid problems. See also: https://gitlab.freedesktop.org/wayland/weston/-/merge_requests/941#note_1457053 Signed-off-by: Pekka Paalanen --- compositor/xwayland.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/compositor/xwayland.c b/compositor/xwayland.c index b5308533..28092681 100644 --- a/compositor/xwayland.c +++ b/compositor/xwayland.c @@ -140,13 +140,13 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd return 1; } + section = weston_config_get_section(config, "xwayland", NULL, NULL); + weston_config_section_get_string(section, "path", + &xserver, XSERVER_PATH); + pid = fork(); switch (pid) { case 0: - section = weston_config_get_section(config, - "xwayland", NULL, NULL); - weston_config_section_get_string(section, "path", - &xserver, XSERVER_PATH); /* SOCK_CLOEXEC closes both ends, so we need to unset * the flag on the client fd. */ @@ -205,6 +205,8 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd break; } + free(xserver); + return pid; } From 77cf8cb0064c57eba8dce217c153f403dd9860a1 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 7 Jul 2022 13:42:39 +0300 Subject: [PATCH 478/609] xwayland: do not weston_log() after fork() Between fork() and exec() in the child process it is only safe to use async-signal-safe functions. weston_log() definitely is not one, it allocates memory and does whatnot. weston_log() is also inappropriate for other reasons: the child process has its own stream buffers and flight-recorder. No-one looks into the child process' flight recorder, so messages would be lost there. The logging machinery might also attempt to write into debug streams, meaning both parent and child could be writing simultaneously. It seems that the best we can do is to pre-bake an error message and only write() it out if exec() fails. There is no mention that even strerror_r() might be safe to call, so we don't. Signed-off-by: Pekka Paalanen --- compositor/xwayland.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/compositor/xwayland.c b/compositor/xwayland.c index 28092681..e4c9eae9 100644 --- a/compositor/xwayland.c +++ b/compositor/xwayland.c @@ -36,6 +36,7 @@ #include #include "shared/helpers.h" #include "shared/os-compatibility.h" +#include "shared/string-helpers.h" #ifdef HAVE_XWAYLAND_LISTENFD # define LISTEN_STR "-listenfd" @@ -113,6 +114,7 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd struct weston_config *config = wet_get_config(wxw->compositor); struct weston_config_section *section; struct wl_event_loop *loop; + char *exec_failure_msg; bool ret; if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) { @@ -143,6 +145,8 @@ 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); + str_printf(&exec_failure_msg, + "Error: executing Xwayland as '%s' failed.\n", xserver); pid = fork(); switch (pid) { @@ -170,12 +174,10 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd "-wm", wm_fd_str, "-terminate", NULL) < 0) { - weston_log("exec of '%s %s -rootless " - LISTEN_STR " %s " LISTEN_STR " %s " - "-wm %s -terminate' failed: %s\n", - xserver, display, - abstract_fd_str, unix_fd_str, wm_fd_str, - strerror(errno)); + if (exec_failure_msg) { + write(STDERR_FILENO, exec_failure_msg, + strlen(exec_failure_msg)); + } } _exit(EXIT_FAILURE); @@ -205,6 +207,7 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd break; } + free(exec_failure_msg); free(xserver); return pid; From 0260b8a0b5c95855fe310ecf2d3aeef7edf38c84 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 7 Jul 2022 11:29:50 +0300 Subject: [PATCH 479/609] shared: fcntl uses int, not long fcntl(2) manual says the return type is int, and that F_SETFD takes an int. So use int. Noticed by code inspection. Signed-off-by: Pekka Paalanen --- shared/os-compatibility.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/os-compatibility.c b/shared/os-compatibility.c index f687f92f..78409d15 100644 --- a/shared/os-compatibility.c +++ b/shared/os-compatibility.c @@ -43,7 +43,7 @@ int os_fd_set_cloexec(int fd) { - long flags; + int flags; if (fd == -1) return -1; From 99b2b958f9f7aab23f5ce2e221ceba824116e5b2 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 7 Jul 2022 11:34:44 +0300 Subject: [PATCH 480/609] shared: introduce os_fd_clear_cloexec() This function will be used between fork() and exec() to remove the close-on-exec flag. The first user will be compositor/xwayland.c. Signed-off-by: Pekka Paalanen --- shared/os-compatibility.c | 15 +++++++++++++++ shared/os-compatibility.h | 3 +++ 2 files changed, 18 insertions(+) diff --git a/shared/os-compatibility.c b/shared/os-compatibility.c index 78409d15..a9d91c52 100644 --- a/shared/os-compatibility.c +++ b/shared/os-compatibility.c @@ -40,6 +40,21 @@ #define READONLY_SEALS (F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE) +int +os_fd_clear_cloexec(int fd) +{ + int flags; + + flags = fcntl(fd, F_GETFD); + if (flags == -1) + return -1; + + if (fcntl(fd, F_SETFD, flags & ~(int)FD_CLOEXEC) == -1) + return -1; + + return 0; +} + int os_fd_set_cloexec(int fd) { diff --git a/shared/os-compatibility.h b/shared/os-compatibility.h index 6aaa2688..85f65854 100644 --- a/shared/os-compatibility.h +++ b/shared/os-compatibility.h @@ -30,6 +30,9 @@ #include +int +os_fd_clear_cloexec(int fd); + int os_fd_set_cloexec(int fd); From 4c0bdbfde90b333e33c9bbd492976b002aafec56 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 7 Jul 2022 14:18:54 +0300 Subject: [PATCH 481/609] xwayland: do not snprintf() after fork() Between fork() and exec() in the child process it is only safe to use async-signal-safe functions. Surprisingly, snprintf() is not such function. See e.g. https://stackoverflow.com/a/6771799 and how snprintf is not listed in signal-safety(7) manual. Therefore we must prepare the fd argument strings before fork(). That is only possible if we also do not dup() fd in the child process. Hence we remove the close-on-exec flag instead in the child process which has copies of the parent's file descriptors. Fortunately fcntl() is safe. struct fdstr is helping to reduce code clutter a bit. Additionally, if fork() fails, we now clean up the fds we created. Signed-off-by: Pekka Paalanen --- compositor/xwayland.c | 106 ++++++++++++++++++++++++++++-------------- 1 file changed, 71 insertions(+), 35 deletions(-) diff --git a/compositor/xwayland.c b/compositor/xwayland.c index e4c9eae9..92816379 100644 --- a/compositor/xwayland.c +++ b/compositor/xwayland.c @@ -89,16 +89,40 @@ out: return 0; } -/* Duplicate an FD and write it into a string, for use in environment */ -#define FD_STR_LEN 12 +struct fdstr { + char str1[12]; + int fds[2]; +}; + +static void +fdstr_update_str1(struct fdstr *s) +{ + snprintf(s->str1, sizeof(s->str1), "%d", s->fds[1]); +} + +static void +fdstr_set_fd1(struct fdstr *s, int fd) +{ + s->fds[0] = -1; + s->fds[1] = fd; + fdstr_update_str1(s); +} + static bool -dup_fd_to_string(char s[FD_STR_LEN], int fd) +fdstr_clear_cloexec_fd1(struct fdstr *s) { - fd = dup(fd); - if (fd < 0) - return false; - snprintf(s, FD_STR_LEN, "%d", fd); - return true; + return os_fd_clear_cloexec(s->fds[1]) >= 0; +} + +static void +fdstr_close_all(struct fdstr *s) +{ + unsigned i; + + for (i = 0; i < ARRAY_LENGTH(s->fds); i++) { + close(s->fds[i]); + s->fds[i] = -1; + } } static pid_t @@ -106,10 +130,11 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd { struct wet_xwayland *wxw = user_data; pid_t pid; - char wayland_socket_str[FD_STR_LEN], abstract_fd_str[FD_STR_LEN]; - char unix_fd_str[FD_STR_LEN], wm_fd_str[FD_STR_LEN]; - char display_fd_str[FD_STR_LEN]; - int sv[2], wm[2], display_fd[2]; + struct fdstr wayland_socket; + struct fdstr x11_abstract_socket; + struct fdstr x11_unix_socket; + struct fdstr x11_wm_socket; + struct fdstr display_pipe; char *xserver = NULL; struct weston_config *config = wet_get_config(wxw->compositor); struct weston_config_section *section; @@ -117,31 +142,38 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd char *exec_failure_msg; bool ret; - if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) { + if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, wayland_socket.fds) < 0) { weston_log("wl connection socketpair failed\n"); return 1; } + fdstr_update_str1(&wayland_socket); - if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, wm) < 0) { + if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, x11_wm_socket.fds) < 0) { weston_log("X wm connection socketpair failed\n"); return 1; } + fdstr_update_str1(&x11_wm_socket); - if (pipe(display_fd) < 0) { + if (pipe(display_pipe.fds) < 0) { weston_log("pipe creation for displayfd failed\n"); return 1; } - if (os_fd_set_cloexec(display_fd[0]) != 0) { + if (os_fd_set_cloexec(display_pipe.fds[0]) != 0) { weston_log("failed setting compositor end of displayfd as cloexec\n"); return 1; } - if (os_fd_set_cloexec(display_fd[1]) != 0) { + if (os_fd_set_cloexec(display_pipe.fds[1]) != 0) { weston_log("failed setting Xwayland end of displayfd as cloexec\n"); return 1; } + fdstr_update_str1(&display_pipe); + + fdstr_set_fd1(&x11_abstract_socket, abstract_fd); + fdstr_set_fd1(&x11_unix_socket, unix_fd); + section = weston_config_get_section(config, "xwayland", NULL, NULL); weston_config_section_get_string(section, "path", &xserver, XSERVER_PATH); @@ -151,27 +183,26 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd pid = fork(); switch (pid) { case 0: - /* SOCK_CLOEXEC closes both ends, so we need to unset * the flag on the client fd. */ - ret = dup_fd_to_string(wayland_socket_str, sv[1]); - ret &= dup_fd_to_string(abstract_fd_str, abstract_fd); - ret &= dup_fd_to_string(unix_fd_str, unix_fd); - ret &= dup_fd_to_string(wm_fd_str, wm[1]); - ret &= dup_fd_to_string(display_fd_str, display_fd[1]); + ret = fdstr_clear_cloexec_fd1(&wayland_socket); + ret &= fdstr_clear_cloexec_fd1(&x11_abstract_socket); + ret &= fdstr_clear_cloexec_fd1(&x11_unix_socket); + ret &= fdstr_clear_cloexec_fd1(&x11_wm_socket); + ret &= fdstr_clear_cloexec_fd1(&display_pipe); if (!ret) _exit(EXIT_FAILURE); - setenv("WAYLAND_SOCKET", wayland_socket_str, 1); + setenv("WAYLAND_SOCKET", wayland_socket.str1, 1); if (execl(xserver, xserver, display, "-rootless", - LISTEN_STR, abstract_fd_str, - LISTEN_STR, unix_fd_str, - "-displayfd", display_fd_str, - "-wm", wm_fd_str, + LISTEN_STR, x11_abstract_socket.str1, + LISTEN_STR, x11_unix_socket.str1, + "-displayfd", display_pipe.str1, + "-wm", x11_wm_socket.str1, "-terminate", NULL) < 0) { if (exec_failure_msg) { @@ -182,21 +213,23 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd _exit(EXIT_FAILURE); default: - close(sv[1]); - wxw->client = wl_client_create(wxw->compositor->wl_display, sv[0]); + close(wayland_socket.fds[1]); + wxw->client = wl_client_create(wxw->compositor->wl_display, + wayland_socket.fds[0]); - close(wm[1]); - wxw->wm_fd = wm[0]; + close(x11_wm_socket.fds[1]); + wxw->wm_fd = x11_wm_socket.fds[0]; /* During initialization the X server will round trip * and block on the wayland compositor, so avoid making * blocking requests (like xcb_connect_to_fd) until * it's done with that. */ - close(display_fd[1]); + close(display_pipe.fds[1]); loop = wl_display_get_event_loop(wxw->compositor->wl_display); wxw->display_fd_source = - wl_event_loop_add_fd(loop, display_fd[0], WL_EVENT_READABLE, - handle_display_fd, wxw); + wl_event_loop_add_fd(loop, display_pipe.fds[0], + WL_EVENT_READABLE, + handle_display_fd, wxw); wxw->process.pid = pid; wet_watch_process(wxw->compositor, &wxw->process); @@ -204,6 +237,9 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd case -1: weston_log("Failed to fork to spawn xserver process\n"); + fdstr_close_all(&wayland_socket); + fdstr_close_all(&x11_wm_socket); + fdstr_close_all(&display_pipe); break; } From a3d7199bea675f3d4691199874960d1804c51c6e Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 7 Jul 2022 14:30:17 +0300 Subject: [PATCH 482/609] xwayland: use pipe2() We are already using pipe2() in many places, even in libweston, so let's simplify the code here as well - not to mention avoid a theoretical race. Signed-off-by: Pekka Paalanen --- compositor/xwayland.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/compositor/xwayland.c b/compositor/xwayland.c index 92816379..49841aef 100644 --- a/compositor/xwayland.c +++ b/compositor/xwayland.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include "compositor/weston.h" @@ -154,21 +155,10 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd } fdstr_update_str1(&x11_wm_socket); - if (pipe(display_pipe.fds) < 0) { + if (pipe2(display_pipe.fds, O_CLOEXEC) < 0) { weston_log("pipe creation for displayfd failed\n"); return 1; } - - if (os_fd_set_cloexec(display_pipe.fds[0]) != 0) { - weston_log("failed setting compositor end of displayfd as cloexec\n"); - return 1; - } - - if (os_fd_set_cloexec(display_pipe.fds[1]) != 0) { - weston_log("failed setting Xwayland end of displayfd as cloexec\n"); - return 1; - } - fdstr_update_str1(&display_pipe); fdstr_set_fd1(&x11_abstract_socket, abstract_fd); From d1b01ffb9a5f212fb6d4deb151f1b5016e5d61e1 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 7 Jul 2022 14:52:20 +0300 Subject: [PATCH 483/609] xwayland: use execv() Constructing argv before-hand is a little easier to look at, but this is mostly just anticipating more changes to how Weston spawns processes in general. Signed-off-by: Pekka Paalanen --- compositor/xwayland.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/compositor/xwayland.c b/compositor/xwayland.c index 49841aef..2042b6b8 100644 --- a/compositor/xwayland.c +++ b/compositor/xwayland.c @@ -170,6 +170,18 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd str_printf(&exec_failure_msg, "Error: executing Xwayland as '%s' failed.\n", xserver); + const char *const argv[] = { + xserver, + display, + "-rootless", + LISTEN_STR, x11_abstract_socket.str1, + LISTEN_STR, x11_unix_socket.str1, + "-displayfd", display_pipe.str1, + "-wm", x11_wm_socket.str1, + "-terminate", + NULL + }; + pid = fork(); switch (pid) { case 0: @@ -185,21 +197,13 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd setenv("WAYLAND_SOCKET", wayland_socket.str1, 1); - if (execl(xserver, - xserver, - display, - "-rootless", - LISTEN_STR, x11_abstract_socket.str1, - LISTEN_STR, x11_unix_socket.str1, - "-displayfd", display_pipe.str1, - "-wm", x11_wm_socket.str1, - "-terminate", - NULL) < 0) { + if (execv(xserver, (char *const *)argv) < 0) { if (exec_failure_msg) { write(STDERR_FILENO, exec_failure_msg, strlen(exec_failure_msg)); } } + _exit(EXIT_FAILURE); default: From 1bd92dac010d685560bf8b1d595e0bb6902e4401 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 7 Jul 2022 15:41:48 +0300 Subject: [PATCH 484/609] xwayland: do not use setenv() after fork() Between fork() and exec() in the child process it is only safe to use async-signal-safe functions. Painfully, setenv() is not listed as such. Therefore we must craft our own custom environment, and we get no help from libc with that. Signed-off-by: Pekka Paalanen --- compositor/xwayland.c | 95 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 92 insertions(+), 3 deletions(-) diff --git a/compositor/xwayland.c b/compositor/xwayland.c index 2042b6b8..fc3c4bae 100644 --- a/compositor/xwayland.c +++ b/compositor/xwayland.c @@ -126,6 +126,91 @@ fdstr_close_all(struct fdstr *s) } } +struct custom_env { + struct wl_array p; + bool finalized; +}; + +static void +custom_env_init_from_environ(struct custom_env *env) +{ + char **it; + char **ep; + + wl_array_init(&env->p); + env->finalized = false; + + for (it = environ; *it; it++) { + ep = wl_array_add(&env->p, sizeof *ep); + assert(ep); + *ep = strdup(*it); + assert(*ep); + } +} + +static void +custom_env_fini(struct custom_env *env) +{ + char **ep; + + wl_array_for_each(ep, &env->p) + free(*ep); + + wl_array_release(&env->p); +} + +static char ** +custom_env_find_element(struct custom_env *env, const char *name) +{ + char **ep; + size_t name_len = strlen(name); + + wl_array_for_each(ep, &env->p) { + char *entry = *ep; + + if (strncmp(entry, name, name_len) == 0 && + entry[name_len] == '=') { + return ep; + } + } + + return NULL; +} + +static void +custom_env_set(struct custom_env *env, const char *name, const char *value) +{ + char **ep; + + assert(strchr(name, '=') == NULL); + assert(!env->finalized); + + ep = custom_env_find_element(env, name); + if (ep) + free(*ep); + else + ep = wl_array_add(&env->p, sizeof *ep); + assert(ep); + + str_printf(ep, "%s=%s", name, value); + assert(*ep); +} + +static char *const * +custom_env_get_envp(struct custom_env *env) +{ + char **ep; + + /* add terminating NULL */ + ep = wl_array_add(&env->p, sizeof *ep); + assert(ep); + *ep = NULL; + + env->finalized = true; + + return env->p.data; +} + static pid_t spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd) { @@ -141,6 +226,8 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd struct weston_config_section *section; struct wl_event_loop *loop; char *exec_failure_msg; + struct custom_env child_env; + char *const *envp; bool ret; if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, wayland_socket.fds) < 0) { @@ -169,6 +256,9 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd &xserver, XSERVER_PATH); str_printf(&exec_failure_msg, "Error: executing Xwayland as '%s' failed.\n", xserver); + custom_env_init_from_environ(&child_env); + custom_env_set(&child_env, "WAYLAND_SOCKET", wayland_socket.str1); + envp = custom_env_get_envp(&child_env); const char *const argv[] = { xserver, @@ -195,9 +285,7 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd if (!ret) _exit(EXIT_FAILURE); - setenv("WAYLAND_SOCKET", wayland_socket.str1, 1); - - if (execv(xserver, (char *const *)argv) < 0) { + if (execve(xserver, (char *const *)argv, envp) < 0) { if (exec_failure_msg) { write(STDERR_FILENO, exec_failure_msg, strlen(exec_failure_msg)); @@ -237,6 +325,7 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd break; } + custom_env_fini(&child_env); free(exec_failure_msg); free(xserver); From 764c2aff8fa46c370c9c030dc733c73cdecfbce1 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Fri, 8 Jul 2022 11:07:13 +0300 Subject: [PATCH 485/609] xwayland: do not check execve() return value Simplifies the code a bit. Signed-off-by: Pekka Paalanen --- compositor/xwayland.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/compositor/xwayland.c b/compositor/xwayland.c index fc3c4bae..84ac7747 100644 --- a/compositor/xwayland.c +++ b/compositor/xwayland.c @@ -285,11 +285,12 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd if (!ret) _exit(EXIT_FAILURE); - if (execve(xserver, (char *const *)argv, envp) < 0) { - if (exec_failure_msg) { - write(STDERR_FILENO, exec_failure_msg, - strlen(exec_failure_msg)); - } + execve(xserver, (char *const *)argv, envp); + /* execve does not return on success, so it failed */ + + if (exec_failure_msg) { + write(STDERR_FILENO, exec_failure_msg, + strlen(exec_failure_msg)); } _exit(EXIT_FAILURE); From 50f98b1006072de70f91274990e973463ee907dd Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Mon, 11 Jul 2022 14:19:32 +0300 Subject: [PATCH 486/609] backend-drm/state-propose: Amend an older comment Since b38b735e20efec218f78c0, 'backend-drm: Remove Pixman conditional for keep_buffer' the Pixman renderer keeps its own reference to buffers when attached to surfaces, rather than flipping keep_buffer variable for the surface. Problem is that when switching from the Pixman render to the GL would not work and could result in a crash upon first repaint. Signed-off-by: Marius Vlad --- libweston/backend-drm/state-propose.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index 1d4943b3..a6adfb35 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -981,10 +981,11 @@ drm_assign_planes(struct weston_output *output_base) /* Test whether this buffer can ever go into a plane: * non-shm, or small enough to be a cursor. * - * Also, keep a reference when using the pixman renderer. - * That makes it possible to do a seamless switch to the GL - * renderer and since the pixman renderer keeps a reference - * to the buffer anyway, there is no side effects. + * FIXME: Also, we should keep a reference when using the + * pixman renderer. That makes it possible to do a seamless + * switch to the GL renderer and since the pixman renderer + * keeps a reference to the buffer anyway, there is no side + * effects. */ ev->surface->keep_buffer = false; if (weston_view_has_valid_buffer(ev)) { From 7fd22ae44dc7604293a4cc752797dad55bd6778f Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Wed, 13 Jul 2022 13:49:50 +0300 Subject: [PATCH 487/609] libweston/compositor: Check whether flushing is allowed This patch acts as bandaid in the core compositor to avoid the renderer doing a flush after the buffer has been released. Flushing after release can happen due to problems in the internal damage tracking, is violating the protocol, and causes visible glitches. A more proper fix would be to handle compositor side damage correctly. Suggested-by: Pekka Paalanen Acked-by: Daniel Stone Signed-off-by: Marius Vlad --- libweston/compositor.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/libweston/compositor.c b/libweston/compositor.c index 81a19c32..deb91b1c 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -2749,12 +2749,26 @@ weston_output_damage(struct weston_output *output) weston_output_schedule_repaint(output); } +/* FIXME: note that we don't flush any damage when the core wants us to + * do so as it will sometimes ask for a flush not necessarily at the + * right time. + * + * A (more) proper way is to handle correctly damage whenever there's + * compositor side damage. See the comment for weston_surface_damage(). + */ +static bool +buffer_can_be_accessed_BANDAID_XXX(struct weston_buffer_reference buffer_ref) +{ + return buffer_ref.type == BUFFER_MAY_BE_ACCESSED; +} + static void surface_flush_damage(struct weston_surface *surface) { struct weston_buffer *buffer = surface->buffer_ref.buffer; - if (buffer && buffer->type == WESTON_BUFFER_SHM) + if (buffer->type == WESTON_BUFFER_SHM && + buffer_can_be_accessed_BANDAID_XXX(surface->buffer_ref)) surface->compositor->renderer->flush_damage(surface, buffer); if (pixman_region32_not_empty(&surface->damage)) From b685e075cd19d2f2938f4981657e5f32d132d2fa Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 12 Jul 2022 13:15:23 +0100 Subject: [PATCH 488/609] process-util: Move Xwayland fork helpers to shared We'll want to reuse these inside desktop-shell as well as the Weston frontend. Signed-off-by: Daniel Stone --- compositor/xwayland.c | 122 +--------------------------------- shared/meson.build | 1 + shared/process-util.c | 151 ++++++++++++++++++++++++++++++++++++++++++ shared/process-util.h | 83 +++++++++++++++++++++++ 4 files changed, 236 insertions(+), 121 deletions(-) create mode 100644 shared/process-util.c create mode 100644 shared/process-util.h diff --git a/compositor/xwayland.c b/compositor/xwayland.c index 84ac7747..30ace4bf 100644 --- a/compositor/xwayland.c +++ b/compositor/xwayland.c @@ -37,6 +37,7 @@ #include #include "shared/helpers.h" #include "shared/os-compatibility.h" +#include "shared/process-util.h" #include "shared/string-helpers.h" #ifdef HAVE_XWAYLAND_LISTENFD @@ -90,127 +91,6 @@ out: return 0; } -struct fdstr { - char str1[12]; - int fds[2]; -}; - -static void -fdstr_update_str1(struct fdstr *s) -{ - snprintf(s->str1, sizeof(s->str1), "%d", s->fds[1]); -} - -static void -fdstr_set_fd1(struct fdstr *s, int fd) -{ - s->fds[0] = -1; - s->fds[1] = fd; - fdstr_update_str1(s); -} - -static bool -fdstr_clear_cloexec_fd1(struct fdstr *s) -{ - return os_fd_clear_cloexec(s->fds[1]) >= 0; -} - -static void -fdstr_close_all(struct fdstr *s) -{ - unsigned i; - - for (i = 0; i < ARRAY_LENGTH(s->fds); i++) { - close(s->fds[i]); - s->fds[i] = -1; - } -} - -struct custom_env { - struct wl_array p; - bool finalized; -}; - -static void -custom_env_init_from_environ(struct custom_env *env) -{ - char **it; - char **ep; - - wl_array_init(&env->p); - env->finalized = false; - - for (it = environ; *it; it++) { - ep = wl_array_add(&env->p, sizeof *ep); - assert(ep); - *ep = strdup(*it); - assert(*ep); - } -} - -static void -custom_env_fini(struct custom_env *env) -{ - char **ep; - - wl_array_for_each(ep, &env->p) - free(*ep); - - wl_array_release(&env->p); -} - -static char ** -custom_env_find_element(struct custom_env *env, const char *name) -{ - char **ep; - size_t name_len = strlen(name); - - wl_array_for_each(ep, &env->p) { - char *entry = *ep; - - if (strncmp(entry, name, name_len) == 0 && - entry[name_len] == '=') { - return ep; - } - } - - return NULL; -} - -static void -custom_env_set(struct custom_env *env, const char *name, const char *value) -{ - char **ep; - - assert(strchr(name, '=') == NULL); - assert(!env->finalized); - - ep = custom_env_find_element(env, name); - if (ep) - free(*ep); - else - ep = wl_array_add(&env->p, sizeof *ep); - assert(ep); - - str_printf(ep, "%s=%s", name, value); - assert(*ep); -} - -static char *const * -custom_env_get_envp(struct custom_env *env) -{ - char **ep; - - /* add terminating NULL */ - ep = wl_array_add(&env->p, sizeof *ep); - assert(ep); - *ep = NULL; - - env->finalized = true; - - return env->p.data; -} - static pid_t spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd) { diff --git a/shared/meson.build b/shared/meson.build index d7936fe5..8b71fb87 100644 --- a/shared/meson.build +++ b/shared/meson.build @@ -4,6 +4,7 @@ srcs_libshared = [ 'signal.c', 'file-util.c', 'os-compatibility.c', + 'process-util.c', ] deps_libshared = [dep_wayland_client, dep_wayland_server] diff --git a/shared/process-util.c b/shared/process-util.c new file mode 100644 index 00000000..9b9ed00e --- /dev/null +++ b/shared/process-util.c @@ -0,0 +1,151 @@ +/* + * Copyright 2022 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include +#include + +#include + +#include "helpers.h" +#include "os-compatibility.h" +#include "process-util.h" +#include "string-helpers.h" + +extern char **environ; /* defined by libc */ + +void +fdstr_update_str1(struct fdstr *s) +{ + snprintf(s->str1, sizeof(s->str1), "%d", s->fds[1]); +} + +void +fdstr_set_fd1(struct fdstr *s, int fd) +{ + s->fds[0] = -1; + s->fds[1] = fd; + fdstr_update_str1(s); +} + +bool +fdstr_clear_cloexec_fd1(struct fdstr *s) +{ + return os_fd_clear_cloexec(s->fds[1]) >= 0; +} + +void +fdstr_close_all(struct fdstr *s) +{ + unsigned i; + + for (i = 0; i < ARRAY_LENGTH(s->fds); i++) { + close(s->fds[i]); + s->fds[i] = -1; + } +} + +void +custom_env_init_from_environ(struct custom_env *env) +{ + char **it; + char **ep; + + wl_array_init(&env->p); + env->finalized = false; + + for (it = environ; *it; it++) { + ep = wl_array_add(&env->p, sizeof *ep); + assert(ep); + *ep = strdup(*it); + assert(*ep); + } +} + +void +custom_env_fini(struct custom_env *env) +{ + char **ep; + + wl_array_for_each(ep, &env->p) + free(*ep); + + wl_array_release(&env->p); +} + +static char ** +custom_env_find_element(struct custom_env *env, const char *name) +{ + char **ep; + size_t name_len = strlen(name); + + wl_array_for_each(ep, &env->p) { + char *entry = *ep; + + if (strncmp(entry, name, name_len) == 0 && + entry[name_len] == '=') { + return ep; + } + } + + return NULL; +} + +void +custom_env_set(struct custom_env *env, const char *name, const char *value) +{ + char **ep; + + assert(strchr(name, '=') == NULL); + assert(!env->finalized); + + ep = custom_env_find_element(env, name); + if (ep) + free(*ep); + else + ep = wl_array_add(&env->p, sizeof *ep); + assert(ep); + + str_printf(ep, "%s=%s", name, value); + assert(*ep); +} + +char *const * +custom_env_get_envp(struct custom_env *env) +{ + char **ep; + + /* add terminating NULL */ + ep = wl_array_add(&env->p, sizeof *ep); + assert(ep); + *ep = NULL; + + env->finalized = true; + + return env->p.data; +} diff --git a/shared/process-util.h b/shared/process-util.h new file mode 100644 index 00000000..56c1b46d --- /dev/null +++ b/shared/process-util.h @@ -0,0 +1,83 @@ +/* + * Copyright 2022 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include + +#include + +#include "os-compatibility.h" +#include "string-helpers.h" + +/** + * A container for file descriptors and their string representations, designed + * to be used when forking child processes. + * + * fds[0] is generally used as the file descriptor held within the parent + * process to communicate with the child, and fds[1] as the child's counterpart. + * + * str1[] is used as a string representation of fds[1]. + */ +struct fdstr { + char str1[12]; + int fds[2]; +}; + +void +fdstr_update_str1(struct fdstr *s); + +void +fdstr_set_fd1(struct fdstr *s, int fd); + +bool +fdstr_clear_cloexec_fd1(struct fdstr *s); + +void +fdstr_close_all(struct fdstr *s); + + +/** + * A container for environment variables, designed to be used when forking child + * processes, as setenv() and anything which allocates memory cannot be used + * between fork() and exec(). + */ +struct custom_env { + struct wl_array p; + bool finalized; +}; + +void +custom_env_init_from_environ(struct custom_env *env); + +void +custom_env_fini(struct custom_env *env); + +void +custom_env_set(struct custom_env *env, const char *name, const char *value); + +char *const * +custom_env_get_envp(struct custom_env *env); From 3af823b69ba0ec802980757e8bbf0b995f40773b Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 12 Jul 2022 14:40:13 +0100 Subject: [PATCH 489/609] process-util: Assert we don't finalize twice Make sure that we only try to finalize once. Signed-off-by: Daniel Stone --- shared/process-util.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shared/process-util.c b/shared/process-util.c index 9b9ed00e..a8a24a65 100644 --- a/shared/process-util.c +++ b/shared/process-util.c @@ -140,6 +140,8 @@ custom_env_get_envp(struct custom_env *env) { char **ep; + assert(!env->finalized); + /* add terminating NULL */ ep = wl_array_add(&env->p, sizeof *ep); assert(ep); From fafe5f0fc2973cbdc60770e456686f6b987a7d48 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 12 Jul 2022 14:40:56 +0100 Subject: [PATCH 490/609] custom-env: Prepare for handling args as well as environment Rename the bits handling environment variables (currently, all of it), so we have room to handle args as well. No functional changes. Signed-off-by: Daniel Stone --- compositor/xwayland.c | 2 +- shared/process-util.c | 34 +++++++++++++++++----------------- shared/process-util.h | 6 +++--- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/compositor/xwayland.c b/compositor/xwayland.c index 30ace4bf..032ea6e4 100644 --- a/compositor/xwayland.c +++ b/compositor/xwayland.c @@ -137,7 +137,7 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd str_printf(&exec_failure_msg, "Error: executing Xwayland as '%s' failed.\n", xserver); custom_env_init_from_environ(&child_env); - custom_env_set(&child_env, "WAYLAND_SOCKET", wayland_socket.str1); + custom_env_set_env_var(&child_env, "WAYLAND_SOCKET", wayland_socket.str1); envp = custom_env_get_envp(&child_env); const char *const argv[] = { diff --git a/shared/process-util.c b/shared/process-util.c index a8a24a65..1da62dcc 100644 --- a/shared/process-util.c +++ b/shared/process-util.c @@ -76,11 +76,11 @@ custom_env_init_from_environ(struct custom_env *env) char **it; char **ep; - wl_array_init(&env->p); - env->finalized = false; + wl_array_init(&env->envp); + env->env_finalized = false; for (it = environ; *it; it++) { - ep = wl_array_add(&env->p, sizeof *ep); + ep = wl_array_add(&env->envp, sizeof *ep); assert(ep); *ep = strdup(*it); assert(*ep); @@ -90,21 +90,21 @@ custom_env_init_from_environ(struct custom_env *env) void custom_env_fini(struct custom_env *env) { - char **ep; + char **p; - wl_array_for_each(ep, &env->p) - free(*ep); + wl_array_for_each(p, &env->envp) + free(*p); - wl_array_release(&env->p); + wl_array_release(&env->envp); } static char ** -custom_env_find_element(struct custom_env *env, const char *name) +custom_env_get_env_var(struct custom_env *env, const char *name) { char **ep; size_t name_len = strlen(name); - wl_array_for_each(ep, &env->p) { + wl_array_for_each(ep, &env->envp) { char *entry = *ep; if (strncmp(entry, name, name_len) == 0 && @@ -117,18 +117,18 @@ custom_env_find_element(struct custom_env *env, const char *name) } void -custom_env_set(struct custom_env *env, const char *name, const char *value) +custom_env_set_env_var(struct custom_env *env, const char *name, const char *value) { char **ep; assert(strchr(name, '=') == NULL); - assert(!env->finalized); + assert(!env->env_finalized); - ep = custom_env_find_element(env, name); + ep = custom_env_get_env_var(env, name); if (ep) free(*ep); else - ep = wl_array_add(&env->p, sizeof *ep); + ep = wl_array_add(&env->envp, sizeof *ep); assert(ep); str_printf(ep, "%s=%s", name, value); @@ -140,14 +140,14 @@ custom_env_get_envp(struct custom_env *env) { char **ep; - assert(!env->finalized); + assert(!env->env_finalized); /* add terminating NULL */ - ep = wl_array_add(&env->p, sizeof *ep); + ep = wl_array_add(&env->envp, sizeof *ep); assert(ep); *ep = NULL; - env->finalized = true; + env->env_finalized = true; - return env->p.data; + return env->envp.data; } diff --git a/shared/process-util.h b/shared/process-util.h index 56c1b46d..8f8ead7d 100644 --- a/shared/process-util.h +++ b/shared/process-util.h @@ -66,8 +66,8 @@ fdstr_close_all(struct fdstr *s); * between fork() and exec(). */ struct custom_env { - struct wl_array p; - bool finalized; + struct wl_array envp; + bool env_finalized; }; void @@ -77,7 +77,7 @@ void custom_env_fini(struct custom_env *env); void -custom_env_set(struct custom_env *env, const char *name, const char *value); +custom_env_set_env_var(struct custom_env *env, const char *name, const char *value); char *const * custom_env_get_envp(struct custom_env *env); From 2a9cae17d87208ae887c277ddcfce37de35b6f59 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 12 Jul 2022 15:40:35 +0100 Subject: [PATCH 491/609] custom-env: Add tests for environment handling Test the basic stuff: initialising from a known environment, setting a new variable, overwriting a previous variable, and getting the resulting array to pass to execve. Signed-off-by: Daniel Stone --- tests/custom-env-test.c | 87 +++++++++++++++++++++++++++++++++++++++++ tests/meson.build | 1 + 2 files changed, 88 insertions(+) create mode 100644 tests/custom-env-test.c diff --git a/tests/custom-env-test.c b/tests/custom-env-test.c new file mode 100644 index 00000000..49adb022 --- /dev/null +++ b/tests/custom-env-test.c @@ -0,0 +1,87 @@ +/* + * Copyright 2022 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include + +#include "shared/helpers.h" +#include "shared/os-compatibility.h" +#include "shared/process-util.h" +#include "shared/string-helpers.h" + +#include "weston-test-runner.h" + +#define ASSERT_STR_MATCH(_as, _bs) do { \ + const char *as = _as; \ + const char *bs = _bs; \ + assert(!!as == !!bs); \ + assert(!as || strcmp(as, bs) == 0); \ +} while (0) + +#define ASSERT_STR_ARRAY_MATCH(_name, _aa, _ba) do { \ + char * const *aa = _aa; \ + char * const *ba = _ba; \ + testlog("\tcomparing " _name ":\n"); \ + for (int _i = 0; aa[_i] || ba[_i]; _i++) { \ + testlog("\t\t[%d] '%s' == '%s'?\n", _i, aa[_i], ba[_i]); \ + ASSERT_STR_MATCH(aa[_i], ba[_i]); \ + } \ + testlog("\tsuccessfully compared " _name "\n"); \ +} while (0) + +static enum test_result_code +setup_env(struct weston_test_harness *harness) +{ + /* as this is a standalone test, we can clear the environment here */ + clearenv(); + + putenv("ENV1=one"); + setenv("ENV2", "two", 1); + setenv("ENV3", "three", 1); + + return weston_test_harness_execute_standalone(harness); +} + +DECLARE_FIXTURE_SETUP(setup_env); + +TEST(basic_env) +{ + struct custom_env env; + char *const envp[] = { "ENV1=one", "ENV2=two", "ENV3=four", "ENV5=five", NULL }; + + custom_env_init_from_environ(&env); + custom_env_set_env_var(&env, "ENV5", "five"); + custom_env_set_env_var(&env, "ENV3", "four"); + ASSERT_STR_ARRAY_MATCH("envp", custom_env_get_envp(&env), envp); + assert(env.env_finalized); + custom_env_fini(&env); +} diff --git a/tests/meson.build b/tests/meson.build index fe107334..50722f95 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -146,6 +146,7 @@ tests = [ 'dep_objs': dep_libexec_weston, }, { 'name': 'color-manager', }, + { 'name': 'custom-env', }, { 'name': 'devices', }, { 'name': 'drm-formats', From e568a025e14273bb67c359cb6203bdae03aaa03e Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 12 Jul 2022 14:46:56 +0100 Subject: [PATCH 492/609] custom-env: Add support for argument array execve() takes the same form for arguments as environment: an array of constant pointers to mutable strings, terminated by a NULL. To make it easier for users who want to build up their own argument strings to pass to execve, add support for argument arrays to custom_env. Signed-off-by: Daniel Stone --- shared/process-util.c | 38 +++++++++++++++++++++++++++++++++++++- shared/process-util.h | 14 +++++++++++--- tests/custom-env-test.c | 19 +++++++++++++++++++ 3 files changed, 67 insertions(+), 4 deletions(-) diff --git a/shared/process-util.c b/shared/process-util.c index 1da62dcc..7cb92f6c 100644 --- a/shared/process-util.c +++ b/shared/process-util.c @@ -78,6 +78,8 @@ custom_env_init_from_environ(struct custom_env *env) wl_array_init(&env->envp); env->env_finalized = false; + wl_array_init(&env->argp); + env->arg_finalized = false; for (it = environ; *it; it++) { ep = wl_array_add(&env->envp, sizeof *ep); @@ -94,8 +96,11 @@ custom_env_fini(struct custom_env *env) wl_array_for_each(p, &env->envp) free(*p); - wl_array_release(&env->envp); + + wl_array_for_each(p, &env->argp) + free(*p); + wl_array_release(&env->argp); } static char ** @@ -116,6 +121,20 @@ custom_env_get_env_var(struct custom_env *env, const char *name) return NULL; } +void +custom_env_add_arg(struct custom_env *env, const char *arg) +{ + char **ap; + + assert(!env->arg_finalized); + + ap = wl_array_add(&env->argp, sizeof *ap); + assert(ap); + + *ap = strdup(arg); + assert(*ap); +} + void custom_env_set_env_var(struct custom_env *env, const char *name, const char *value) { @@ -151,3 +170,20 @@ custom_env_get_envp(struct custom_env *env) return env->envp.data; } + +char *const * +custom_env_get_argp(struct custom_env *env) +{ + char **ap; + + assert(!env->arg_finalized); + + /* add terminating NULL */ + ap = wl_array_add(&env->argp, sizeof *ap); + assert(ap); + *ap = NULL; + + env->arg_finalized = true; + + return env->argp.data; +} diff --git a/shared/process-util.h b/shared/process-util.h index 8f8ead7d..081ed331 100644 --- a/shared/process-util.h +++ b/shared/process-util.h @@ -61,13 +61,15 @@ fdstr_close_all(struct fdstr *s); /** - * A container for environment variables, designed to be used when forking child - * processes, as setenv() and anything which allocates memory cannot be used - * between fork() and exec(). + * A container for environment variables and/or process arguments, designed to + * be used when forking child processes, as setenv() and anything which + * allocates memory cannot be used between fork() and exec(). */ struct custom_env { struct wl_array envp; bool env_finalized; + struct wl_array argp; + bool arg_finalized; }; void @@ -79,5 +81,11 @@ custom_env_fini(struct custom_env *env); void custom_env_set_env_var(struct custom_env *env, const char *name, const char *value); +void +custom_env_add_arg(struct custom_env *env, const char *arg); + char *const * custom_env_get_envp(struct custom_env *env); + +char *const * +custom_env_get_argp(struct custom_env *env); diff --git a/tests/custom-env-test.c b/tests/custom-env-test.c index 49adb022..44e3f0f7 100644 --- a/tests/custom-env-test.c +++ b/tests/custom-env-test.c @@ -73,6 +73,8 @@ setup_env(struct weston_test_harness *harness) DECLARE_FIXTURE_SETUP(setup_env); +#define DEFAULT_ENVP (char * const []) { "ENV1=one", "ENV2=two", "ENV3=three", NULL } + TEST(basic_env) { struct custom_env env; @@ -85,3 +87,20 @@ TEST(basic_env) assert(env.env_finalized); custom_env_fini(&env); } + +TEST(basic_env_arg) +{ + struct custom_env env; + char *const argp[] = { "arg1", "arg2", "arg3", NULL }; + + custom_env_init_from_environ(&env); + custom_env_add_arg(&env, "arg1"); + custom_env_add_arg(&env, "arg2"); + custom_env_add_arg(&env, "arg3"); + + ASSERT_STR_ARRAY_MATCH("envp", custom_env_get_envp(&env), DEFAULT_ENVP); + assert(env.env_finalized); + ASSERT_STR_ARRAY_MATCH("argp", custom_env_get_argp(&env), argp); + assert(env.arg_finalized); + custom_env_fini(&env); +} From 2cdb473690a4414b0bcafbb3878e468fce6e89e2 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 12 Jul 2022 16:21:39 +0100 Subject: [PATCH 493/609] custom-env: Add helper to parse combined env/arg strings Users like desktop-shell want to parse a provided string containing a combination of environment and arg, e.g.: ENV=stuff /path/to/thing --good Add support to custom-env for parsing this, with tests, so we can delete the custom implementation inside desktop-shell. Signed-off-by: Daniel Stone --- shared/process-util.c | 81 +++++++++++++++++++++++++++++++++++++++++ shared/process-util.h | 3 ++ tests/custom-env-test.c | 52 ++++++++++++++++++++++++++ 3 files changed, 136 insertions(+) diff --git a/shared/process-util.c b/shared/process-util.c index 7cb92f6c..e36c6470 100644 --- a/shared/process-util.c +++ b/shared/process-util.c @@ -25,6 +25,9 @@ #include "config.h" +#include +#include +#include #include #include #include @@ -154,6 +157,84 @@ custom_env_set_env_var(struct custom_env *env, const char *name, const char *val assert(*ep); } +/** + * Add information from a parsed exec string to a custom_env + * + * An 'exec string' is a string in the format: + * ENVFOO=bar ENVBAR=baz /path/to/exec --arg anotherarg + * + * This function will parse such a string and add the specified environment + * variables (in the format KEY=value) up until it sees a non-environment + * string, after which point every entry will be interpreted as a new + * argument. + * + * Entries are space-separated; there is no support for quoting. + */ +void +custom_env_add_from_exec_string(struct custom_env *env, const char *exec_str) +{ + char *dup_path = strdup(exec_str); + char *start = dup_path; + + assert(dup_path); + + /* Build the environment array (if any) by handling any number of + * equal-separated key=value at the start of the string, split by + * spaces; uses "foo=bar baz=quux meh argh" as the example, where + * "foo=bar" and "baz=quux" should go into the environment, and + * "meh" should be executed with "argh" as its first argument */ + while (*start) { + char *k = NULL, *v = NULL; + char *p; + + /* Leaves us with "foo\0bar baz=quux meh argh", with k pointing + * to "foo" and v pointing to "bar baz=quux meh argh" */ + for (p = start; *p && !isspace(*p); p++) { + if (*p == '=') { + *p++ = '\0'; + k = start; + v = p; + break; + } + } + + if (!v) + break; + + /* Walk to the next space or NUL, filling any trailing spaces + * with NUL, to give us "foo\0bar\0\0baz=quux meh argh". + * k will point to "foo", v will point to "bar", and + * start will point to "baz=quux meh argh". */ + while (*p && !isspace(*p)) + p++; + while (*p && isspace(*p)) + *p++ = '\0'; + start = p; + + custom_env_set_env_var(env, k, v); + } + + /* Now build the argv array by splitting on spaces */ + while (*start) { + char *p; + bool valid = false; + + for (p = start; *p && !isspace(*p); p++) + valid = true; + + if (!valid) + break; + + while (*p && isspace(*p)) + *p++ = '\0'; + + custom_env_add_arg(env, start); + start = p; + } + + free(dup_path); +} + char *const * custom_env_get_envp(struct custom_env *env) { diff --git a/shared/process-util.h b/shared/process-util.h index 081ed331..05543f6f 100644 --- a/shared/process-util.h +++ b/shared/process-util.h @@ -84,6 +84,9 @@ custom_env_set_env_var(struct custom_env *env, const char *name, const char *val void custom_env_add_arg(struct custom_env *env, const char *arg); +void +custom_env_add_from_exec_string(struct custom_env *env, const char *exec_str); + char *const * custom_env_get_envp(struct custom_env *env); diff --git a/tests/custom-env-test.c b/tests/custom-env-test.c index 44e3f0f7..3a516190 100644 --- a/tests/custom-env-test.c +++ b/tests/custom-env-test.c @@ -104,3 +104,55 @@ TEST(basic_env_arg) assert(env.arg_finalized); custom_env_fini(&env); } + +struct test_str { + const char *exec_str; + char * const *envp; + char * const *argp; +}; + +static struct test_str str_tests[] = { + { + .exec_str = "ENV1=1 ENV2=owt two-arghs", + .envp = (char * const []) { "ENV1=1", "ENV2=owt", "ENV3=three", NULL }, + .argp = (char * const []) { "two-arghs", NULL }, + }, + { + .exec_str = "ENV2=owt one-argh", + .envp = (char * const []) { "ENV1=one", "ENV2=owt", "ENV3=three", NULL }, + .argp = (char * const []) { "one-argh", NULL }, + }, + { + .exec_str = "FOO=bar one-argh-again", + .envp = (char * const []) { "ENV1=one", "ENV2=two", "ENV3=three", "FOO=bar", NULL }, + .argp = (char * const []) { "one-argh-again", NULL }, + }, + { + .exec_str = "ENV1=number=7 one-argh-eq", + .envp = (char * const []) { "ENV1=number=7", "ENV2=two", "ENV3=three", NULL }, + .argp = (char * const []) { "one-argh-eq", NULL }, + }, + { + .exec_str = "no-arg-h", + .envp = DEFAULT_ENVP, + .argp = (char * const []) { "no-arg-h", NULL }, + }, + { + .exec_str = "argh-w-arg argequals=thing plainarg ", + .envp = DEFAULT_ENVP, + .argp = (char * const []) { "argh-w-arg", "argequals=thing", "plainarg", NULL }, + }, +}; + +TEST_P(env_parse_string, str_tests) +{ + struct custom_env env; + struct test_str *test = data; + + testlog("checking exec_str '%s'\n", test->exec_str); + custom_env_init_from_environ(&env); + custom_env_add_from_exec_string(&env, test->exec_str); + ASSERT_STR_ARRAY_MATCH("envp", custom_env_get_envp(&env), test->envp); + ASSERT_STR_ARRAY_MATCH("argp", custom_env_get_argp(&env), test->argp); + custom_env_fini(&env); +} From 965d90cbaaa8f4e3e2b472bb08043a6381978181 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 12 Jul 2022 16:58:12 +0100 Subject: [PATCH 494/609] desktop-shell: Use custom_env to launch panel clients Rather than open-coding our own implementation of parsing a string to construct an envp and an argp, just use custom_env's implementation. Signed-off-by: Daniel Stone --- clients/desktop-shell.c | 74 +++++++++-------------------------------- 1 file changed, 16 insertions(+), 58 deletions(-) diff --git a/clients/desktop-shell.c b/clients/desktop-shell.c index 02c1ae5c..b450a010 100644 --- a/clients/desktop-shell.c +++ b/clients/desktop-shell.c @@ -41,22 +41,23 @@ #include #include -#include "window.h" -#include "shared/cairo-util.h" + #include +#include #include "shared/helpers.h" #include "shared/xalloc.h" -#include +#include "shared/cairo-util.h" #include "shared/file-util.h" +#include "shared/process-util.h" #include "shared/timespec-util.h" +#include "window.h" + #include "weston-desktop-shell-client-protocol.h" #define DEFAULT_CLOCK_FORMAT CLOCK_FORMAT_MINUTES #define DEFAULT_SPACING 10 -extern char **environ; /* defined by libc */ - enum clock_format { CLOCK_FORMAT_MINUTES, CLOCK_FORMAT_SECONDS, @@ -144,8 +145,9 @@ struct panel_launcher { char *path; char *displayname; struct wl_list link; - struct wl_array envp; - struct wl_array argv; + struct custom_env env; + char * const *argp; + char * const *envp; }; struct panel_clock { @@ -212,7 +214,6 @@ check_desktop_ready(struct window *window) static void panel_launcher_activate(struct panel_launcher *widget) { - char **argv; pid_t pid; pid = fork(); @@ -224,13 +225,11 @@ panel_launcher_activate(struct panel_launcher *widget) if (pid) return; - argv = widget->argv.data; - if (setsid() == -1) exit(EXIT_FAILURE); - if (execve(argv[0], argv, widget->envp.data) < 0) { - fprintf(stderr, "execl '%s' failed: %s\n", argv[0], + if (execve(widget->argp[0], widget->argp, widget->envp) < 0) { + fprintf(stderr, "execl '%s' failed: %s\n", widget->argp[0], strerror(errno)); exit(1); } @@ -576,8 +575,7 @@ panel_configure(void *data, static void panel_destroy_launcher(struct panel_launcher *launcher) { - wl_array_release(&launcher->argv); - wl_array_release(&launcher->envp); + custom_env_fini(&launcher->env); free(launcher->path); free(launcher->displayname); @@ -683,56 +681,16 @@ static void panel_add_launcher(struct panel *panel, const char *icon, const char *path, const char *displayname) { struct panel_launcher *launcher; - char *start, *p, *eq, **ps; - int i, j, k; launcher = xzalloc(sizeof *launcher); launcher->icon = load_icon_or_fallback(icon); launcher->path = xstrdup(path); launcher->displayname = xstrdup(displayname); - wl_array_init(&launcher->envp); - wl_array_init(&launcher->argv); - for (i = 0; environ[i]; i++) { - ps = wl_array_add(&launcher->envp, sizeof *ps); - *ps = environ[i]; - } - j = 0; - - start = launcher->path; - while (*start) { - for (p = start, eq = NULL; *p && !isspace(*p); p++) - if (*p == '=') - eq = p; - - if (eq && j == 0) { - ps = launcher->envp.data; - for (k = 0; k < i; k++) - if (strncmp(ps[k], start, eq - start) == 0) { - ps[k] = start; - break; - } - if (k == i) { - ps = wl_array_add(&launcher->envp, sizeof *ps); - *ps = start; - i++; - } - } else { - ps = wl_array_add(&launcher->argv, sizeof *ps); - *ps = start; - j++; - } - - while (*p && isspace(*p)) - *p++ = '\0'; - - start = p; - } - - ps = wl_array_add(&launcher->envp, sizeof *ps); - *ps = NULL; - ps = wl_array_add(&launcher->argv, sizeof *ps); - *ps = NULL; + custom_env_init_from_environ(&launcher->env); + custom_env_add_from_exec_string(&launcher->env, launcher->path); + launcher->envp = custom_env_get_envp(&launcher->env); + launcher->argp = custom_env_get_argp(&launcher->env); launcher->panel = panel; wl_list_insert(panel->launcher_list.prev, &launcher->link); From 5dbe001661b84a062d1955a72385416161739fc4 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 12 Jul 2022 16:45:57 +0100 Subject: [PATCH 495/609] xwayland: Use custom-env arg handling Use the arg handling added in the previous commit so that the environment is completely encapsulated inside the custom env. Signed-off-by: Daniel Stone --- compositor/xwayland.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/compositor/xwayland.c b/compositor/xwayland.c index 032ea6e4..abf7399a 100644 --- a/compositor/xwayland.c +++ b/compositor/xwayland.c @@ -108,6 +108,7 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd char *exec_failure_msg; struct custom_env child_env; char *const *envp; + char *const *argp; bool ret; if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, wayland_socket.fds) < 0) { @@ -138,19 +139,22 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd "Error: executing Xwayland as '%s' failed.\n", xserver); custom_env_init_from_environ(&child_env); custom_env_set_env_var(&child_env, "WAYLAND_SOCKET", wayland_socket.str1); - envp = custom_env_get_envp(&child_env); - const char *const argv[] = { - xserver, - display, - "-rootless", - LISTEN_STR, x11_abstract_socket.str1, - LISTEN_STR, x11_unix_socket.str1, - "-displayfd", display_pipe.str1, - "-wm", x11_wm_socket.str1, - "-terminate", - NULL - }; + custom_env_add_arg(&child_env, xserver); + custom_env_add_arg(&child_env, display); + custom_env_add_arg(&child_env, "-rootless"); + custom_env_add_arg(&child_env, LISTEN_STR); + custom_env_add_arg(&child_env, x11_abstract_socket.str1); + custom_env_add_arg(&child_env, LISTEN_STR); + custom_env_add_arg(&child_env, x11_unix_socket.str1); + custom_env_add_arg(&child_env, "-displayfd"); + custom_env_add_arg(&child_env, display_pipe.str1); + custom_env_add_arg(&child_env, "-wm"); + custom_env_add_arg(&child_env, x11_wm_socket.str1); + custom_env_add_arg(&child_env, "-terminate"); + + envp = custom_env_get_envp(&child_env); + argp = custom_env_get_argp(&child_env); pid = fork(); switch (pid) { @@ -165,7 +169,7 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd if (!ret) _exit(EXIT_FAILURE); - execve(xserver, (char *const *)argv, envp); + execve(xserver, argp, envp); /* execve does not return on success, so it failed */ if (exec_failure_msg) { From 8b238905d75a2165dbb7367c71cdddc8ba6c4719 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 12 Jul 2022 17:18:40 +0100 Subject: [PATCH 496/609] xwayland: Use os_socketpair_cloexec() We already have a helper for this; use it. Signed-off-by: Daniel Stone --- compositor/xwayland.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compositor/xwayland.c b/compositor/xwayland.c index abf7399a..6113fa51 100644 --- a/compositor/xwayland.c +++ b/compositor/xwayland.c @@ -111,13 +111,13 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd char *const *argp; bool ret; - if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, wayland_socket.fds) < 0) { + if (os_socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0, wayland_socket.fds) < 0) { weston_log("wl connection socketpair failed\n"); return 1; } fdstr_update_str1(&wayland_socket); - if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, x11_wm_socket.fds) < 0) { + if (os_socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0, x11_wm_socket.fds) < 0) { weston_log("X wm connection socketpair failed\n"); return 1; } From a3175727cb47c1b4831a1b169d5fc9b28e899bc1 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 12 Jul 2022 17:29:29 +0100 Subject: [PATCH 497/609] wet_process: Rearrange fork() if tree to case statement Matches the safe Xwayland implementation more closely and makes it easier to reuse it. Signed-off-by: Daniel Stone --- compositor/main.c | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/compositor/main.c b/compositor/main.c index 67a90e21..ce89fc08 100644 --- a/compositor/main.c +++ b/compositor/main.c @@ -408,7 +408,7 @@ weston_client_launch(struct weston_compositor *compositor, { int sv[2]; pid_t pid; - struct wl_client *client; + struct wl_client *client = NULL; weston_log("launching '%s'\n", path); @@ -420,16 +420,8 @@ weston_client_launch(struct weston_compositor *compositor, } pid = fork(); - if (pid == -1) { - close(sv[0]); - close(sv[1]); - weston_log("weston_client_launch: " - "fork failed while launching '%s': %s\n", path, - strerror(errno)); - return NULL; - } - - if (pid == 0) { + switch (pid) { + case 0: /* Put the client in a new session so it won't catch signals * intended for the parent. Sharing a session can be * confusing when launching weston under gdb, as the ctrl-c @@ -439,23 +431,32 @@ weston_client_launch(struct weston_compositor *compositor, setsid(); child_client_exec(sv[1], path); _exit(-1); - } - close(sv[1]); + default: + close(sv[1]); + client = wl_client_create(compositor->wl_display, sv[0]); + if (!client) { + close(sv[0]); + weston_log("weston_client_launch: " + "wl_client_create failed while launching '%s'.\n", + path); + return NULL; + } - client = wl_client_create(compositor->wl_display, sv[0]); - if (!client) { + proc->pid = pid; + proc->cleanup = cleanup; + wet_watch_process(compositor, proc); + break; + + case -1: close(sv[0]); + close(sv[1]); weston_log("weston_client_launch: " - "wl_client_create failed while launching '%s'.\n", - path); - return NULL; + "fork failed while launching '%s': %s\n", path, + strerror(errno)); + break; } - proc->pid = pid; - proc->cleanup = cleanup; - wet_watch_process(compositor, proc); - return client; } From c0a76716c14ada9eda5dd52f867f80977bfa92a8 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 12 Jul 2022 17:19:28 +0100 Subject: [PATCH 498/609] wet_process: Use fdstr when executing clients This doesn't actually stop us from calling setenv() in between fork() and exec() when starting clients, but gets us closer to Xwayland's safe implementation by reusing one of the helpers it added. Signed-off-by: Daniel Stone --- compositor/main.c | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/compositor/main.c b/compositor/main.c index ce89fc08..1223ccf7 100644 --- a/compositor/main.c +++ b/compositor/main.c @@ -51,6 +51,7 @@ #include #include "shared/os-compatibility.h" #include "shared/helpers.h" +#include "shared/process-util.h" #include "shared/string-helpers.h" #include "git-version.h" #include @@ -368,10 +369,8 @@ sigchld_handler(int signal_number, void *data) } static void -child_client_exec(int sockfd, const char *path) +child_client_exec(struct fdstr *wayland_socket, const char *path) { - int clientfd; - char s[32]; sigset_t allsigs; /* do not give our signal mask to the new process */ @@ -384,16 +383,7 @@ child_client_exec(int sockfd, const char *path) return; } - /* SOCK_CLOEXEC closes both ends, so we dup the fd to get a - * non-CLOEXEC fd to pass through exec. */ - clientfd = dup(sockfd); - if (clientfd == -1) { - weston_log("compositor: dup failed: %s\n", strerror(errno)); - return; - } - - snprintf(s, sizeof s, "%d", clientfd); - setenv("WAYLAND_SOCKET", s, 1); + setenv("WAYLAND_SOCKET", wayland_socket->str1, 1); if (execl(path, path, NULL) < 0) weston_log("compositor: executing '%s' failed: %s\n", @@ -406,18 +396,21 @@ weston_client_launch(struct weston_compositor *compositor, const char *path, weston_process_cleanup_func_t cleanup) { - int sv[2]; - pid_t pid; struct wl_client *client = NULL; + struct fdstr wayland_socket; + pid_t pid; + bool ret; weston_log("launching '%s'\n", path); - if (os_socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0, sv) < 0) { + if (os_socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0, + wayland_socket.fds) < 0) { weston_log("weston_client_launch: " "socketpair failed while launching '%s': %s\n", path, strerror(errno)); return NULL; } + fdstr_update_str1(&wayland_socket); pid = fork(); switch (pid) { @@ -429,14 +422,23 @@ weston_client_launch(struct weston_compositor *compositor, * will cleanly shut down when the child exits. */ setsid(); - child_client_exec(sv[1], path); + + ret = fdstr_clear_cloexec_fd1(&wayland_socket); + if (!ret) { + weston_log("compositor: clearing CLOEXEC failed: %s\n", + strerror(errno)); + _exit(EXIT_FAILURE); + } + + child_client_exec(&wayland_socket, path); _exit(-1); default: - close(sv[1]); - client = wl_client_create(compositor->wl_display, sv[0]); + close(wayland_socket.fds[1]); + client = wl_client_create(compositor->wl_display, + wayland_socket.fds[0]); if (!client) { - close(sv[0]); + close(wayland_socket.fds[0]); weston_log("weston_client_launch: " "wl_client_create failed while launching '%s'.\n", path); @@ -449,8 +451,7 @@ weston_client_launch(struct weston_compositor *compositor, break; case -1: - close(sv[0]); - close(sv[1]); + fdstr_close_all(&wayland_socket); weston_log("weston_client_launch: " "fork failed while launching '%s': %s\n", path, strerror(errno)); From 8aa4571240339fd6467979c5316e2843d3d173e9 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 12 Jul 2022 17:40:23 +0100 Subject: [PATCH 499/609] wet_process: Inline child_client_exec() It was only a small function, and inlining it will allow us to make it more safe without having to duplicate a ton of stuff. Signed-off-by: Daniel Stone --- compositor/main.c | 42 ++++++++++++++++++------------------------ 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/compositor/main.c b/compositor/main.c index 1223ccf7..cf8f6e39 100644 --- a/compositor/main.c +++ b/compositor/main.c @@ -368,28 +368,6 @@ sigchld_handler(int signal_number, void *data) return 1; } -static void -child_client_exec(struct fdstr *wayland_socket, const char *path) -{ - sigset_t allsigs; - - /* do not give our signal mask to the new process */ - sigfillset(&allsigs); - sigprocmask(SIG_UNBLOCK, &allsigs, NULL); - - /* Launch clients as the user. Do not launch clients with wrong euid. */ - if (seteuid(getuid()) == -1) { - weston_log("compositor: failed seteuid\n"); - return; - } - - setenv("WAYLAND_SOCKET", wayland_socket->str1, 1); - - if (execl(path, path, NULL) < 0) - weston_log("compositor: executing '%s' failed: %s\n", - path, strerror(errno)); -} - WL_EXPORT struct wl_client * weston_client_launch(struct weston_compositor *compositor, struct weston_process *proc, @@ -398,6 +376,7 @@ weston_client_launch(struct weston_compositor *compositor, { struct wl_client *client = NULL; struct fdstr wayland_socket; + sigset_t allsigs; pid_t pid; bool ret; @@ -423,6 +402,16 @@ weston_client_launch(struct weston_compositor *compositor, */ setsid(); + /* do not give our signal mask to the new process */ + sigfillset(&allsigs); + sigprocmask(SIG_UNBLOCK, &allsigs, NULL); + + /* Launch clients as the user. Do not launch clients with wrong euid. */ + if (seteuid(getuid()) == -1) { + weston_log("compositor: failed seteuid\n"); + _exit(EXIT_FAILURE); + } + ret = fdstr_clear_cloexec_fd1(&wayland_socket); if (!ret) { weston_log("compositor: clearing CLOEXEC failed: %s\n", @@ -430,8 +419,13 @@ weston_client_launch(struct weston_compositor *compositor, _exit(EXIT_FAILURE); } - child_client_exec(&wayland_socket, path); - _exit(-1); + setenv("WAYLAND_SOCKET", wayland_socket.str1, 1); + + if (execl(path, path, NULL) < 0) + weston_log("compositor: executing '%s' failed: %s\n", + path, strerror(errno)); + + _exit(EXIT_FAILURE); default: close(wayland_socket.fds[1]); From 9ab97ebd72f6e3645f1454ec610a63e523138d29 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 12 Jul 2022 17:45:05 +0100 Subject: [PATCH 500/609] wet_process: Use custom_env when forking clients Use the custom_env framework we added for Xwayland when forking to execute clients. This avoids calling the unsafe getenv in between fork and exec. Signed-off-by: Daniel Stone --- compositor/main.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/compositor/main.c b/compositor/main.c index cf8f6e39..da62684c 100644 --- a/compositor/main.c +++ b/compositor/main.c @@ -375,21 +375,33 @@ weston_client_launch(struct weston_compositor *compositor, weston_process_cleanup_func_t cleanup) { struct wl_client *client = NULL; + struct custom_env child_env; struct fdstr wayland_socket; + char * const *argp; + char * const *envp; sigset_t allsigs; pid_t pid; bool ret; weston_log("launching '%s'\n", path); + custom_env_init_from_environ(&child_env); + custom_env_add_arg(&child_env, path); + if (os_socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0, wayland_socket.fds) < 0) { weston_log("weston_client_launch: " "socketpair failed while launching '%s': %s\n", path, strerror(errno)); + custom_env_fini(&child_env); return NULL; } fdstr_update_str1(&wayland_socket); + custom_env_set_env_var(&child_env, "WAYLAND_SOCKET", + wayland_socket.str1); + + argp = custom_env_get_argp(&child_env); + envp = custom_env_get_envp(&child_env); pid = fork(); switch (pid) { @@ -419,11 +431,10 @@ weston_client_launch(struct weston_compositor *compositor, _exit(EXIT_FAILURE); } - setenv("WAYLAND_SOCKET", wayland_socket.str1, 1); - - if (execl(path, path, NULL) < 0) + if (execve(argp[0], argp, envp)) { weston_log("compositor: executing '%s' failed: %s\n", path, strerror(errno)); + } _exit(EXIT_FAILURE); @@ -432,6 +443,7 @@ weston_client_launch(struct weston_compositor *compositor, client = wl_client_create(compositor->wl_display, wayland_socket.fds[0]); if (!client) { + custom_env_fini(&child_env); close(wayland_socket.fds[0]); weston_log("weston_client_launch: " "wl_client_create failed while launching '%s'.\n", @@ -452,6 +464,8 @@ weston_client_launch(struct weston_compositor *compositor, break; } + custom_env_fini(&child_env); + return client; } From 53f895b4764451b6852766e0c597d9aff807fc35 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 12 Jul 2022 17:51:49 +0100 Subject: [PATCH 501/609] wet_process: Do not weston_log() after fork() [common equivalent of 77cf8cb0064c in Xwayland from Pekka Paalanen; its commit message follows] Between fork() and exec() in the child process it is only safe to use async-signal-safe functions. weston_log() definitely is not one, it allocates memory and does whatnot. weston_log() is also inappropriate for other reasons: the child process has its own stream buffers and flight-recorder. No-one looks into the child process' flight recorder, so messages would be lost there. The logging machinery might also attempt to write into debug streams, meaning both parent and child could be writing simultaneously. It seems that the best we can do is to pre-bake an error message and only write() it out if exec() fails. There is no mention that even strerror_r() might be safe to call, so we don't. Signed-off-by: Daniel Stone --- compositor/main.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/compositor/main.c b/compositor/main.c index da62684c..280b565e 100644 --- a/compositor/main.c +++ b/compositor/main.c @@ -377,6 +377,9 @@ weston_client_launch(struct weston_compositor *compositor, struct wl_client *client = NULL; struct custom_env child_env; struct fdstr wayland_socket; + const char *fail_cloexec = "Couldn't unset CLOEXEC on client socket"; + const char *fail_seteuid = "Couldn't call seteuid"; + char *fail_exec; char * const *argp; char * const *envp; sigset_t allsigs; @@ -384,6 +387,7 @@ weston_client_launch(struct weston_compositor *compositor, bool ret; weston_log("launching '%s'\n", path); + str_printf(&fail_exec, "Error: Couldn't launch client '%s'\n", path); custom_env_init_from_environ(&child_env); custom_env_add_arg(&child_env, path); @@ -420,22 +424,22 @@ weston_client_launch(struct weston_compositor *compositor, /* Launch clients as the user. Do not launch clients with wrong euid. */ if (seteuid(getuid()) == -1) { - weston_log("compositor: failed seteuid\n"); + write(STDERR_FILENO, fail_seteuid, + strlen(fail_seteuid)); _exit(EXIT_FAILURE); } ret = fdstr_clear_cloexec_fd1(&wayland_socket); if (!ret) { - weston_log("compositor: clearing CLOEXEC failed: %s\n", - strerror(errno)); + write(STDERR_FILENO, fail_cloexec, + strlen(fail_cloexec)); _exit(EXIT_FAILURE); } - if (execve(argp[0], argp, envp)) { - weston_log("compositor: executing '%s' failed: %s\n", - path, strerror(errno)); - } + execve(argp[0], argp, envp); + if (fail_exec) + write(STDERR_FILENO, fail_exec, strlen(fail_exec)); _exit(EXIT_FAILURE); default: @@ -445,6 +449,7 @@ weston_client_launch(struct weston_compositor *compositor, if (!client) { custom_env_fini(&child_env); close(wayland_socket.fds[0]); + free(fail_exec); weston_log("weston_client_launch: " "wl_client_create failed while launching '%s'.\n", path); @@ -465,6 +470,7 @@ weston_client_launch(struct weston_compositor *compositor, } custom_env_fini(&child_env); + free(fail_exec); return client; } From c79e1126b69729c5329ff9b3ecb411e0959b2f65 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Tue, 19 Jul 2022 08:23:27 -0500 Subject: [PATCH 502/609] xwayland: give Xwayland its own session If we leave xwayland in weston's process group, it can receive signals from the controlling TTY intended for weston. The easiest way to see this is to launch weston under gdb, start an X client, and hit ctrl-c in the gdb session. The Xwayland server will also catch the SIGINT, and the X client will be disconnected. Instead, let's call setsid() when launching Xwayland, like we do for launched clients. Suggested-by: Hideyuki Nagase Signed-off-by: Derek Foreman --- compositor/xwayland.c | 1 + 1 file changed, 1 insertion(+) diff --git a/compositor/xwayland.c b/compositor/xwayland.c index 6113fa51..cf84c882 100644 --- a/compositor/xwayland.c +++ b/compositor/xwayland.c @@ -159,6 +159,7 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd pid = fork(); switch (pid) { case 0: + setsid(); /* SOCK_CLOEXEC closes both ends, so we need to unset * the flag on the client fd. */ ret = fdstr_clear_cloexec_fd1(&wayland_socket); From 27cf50462b7f62e6e4d6952e52883d9a671cd514 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Wed, 20 Jul 2022 11:47:16 +0300 Subject: [PATCH 503/609] README: drop note about a cairo build option That build option has been long gone. cairo-image is the only flavor used nowadays. Signed-off-by: Pekka Paalanen --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 5613ed3e..81dbeae2 100644 --- a/README.md +++ b/README.md @@ -307,8 +307,6 @@ There are still many more details to be decided. For packagers ------------- -Always build Weston with --with-cairo=image. - The Weston project is (will be) intended to be split into several binary packages, each with its own dependencies. The maximal split would be roughly like this: From cbbf0e59a5f66370acb8be1fefb9989438a81ea2 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Wed, 20 Jul 2022 12:12:22 +0300 Subject: [PATCH 504/609] ivi-shell: replace MEM_ALLOC() with mostly xcalloc() Drop the even more home-grown alloc wrapper and use the xalloc.h wrappers directly. xcalloc() is added and used, because calloc() will detect integer overflows in the size multiplication, while doing a simple multiplication in the caller is subject to overflows which may result in allocating not what was expected, subjecting to out-of-bounds access. All MEM_ALLOC() calls that had a meaningful multiplication in them were converted to xcalloc(), the rest to xzalloc(). Signed-off-by: Pekka Paalanen --- ivi-shell/hmi-controller.c | 35 ++++++++++++++--------------------- shared/xalloc.h | 1 + 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/ivi-shell/hmi-controller.c b/ivi-shell/hmi-controller.c index 230e788b..8ae2230b 100644 --- a/ivi-shell/hmi-controller.c +++ b/ivi-shell/hmi-controller.c @@ -153,13 +153,6 @@ struct launcher_info { /***************************************************************************** * local functions ****************************************************************************/ -static void * -mem_alloc(size_t size, char *file, int32_t line) -{ - return fail_on_null(calloc(1, size), size, file, line); -} - -#define MEM_ALLOC(s) mem_alloc((s),__FILE__,__LINE__) static int32_t is_surf_in_ui_widget(struct hmi_controller *hmi_ctrl, @@ -222,8 +215,8 @@ mode_divided_into_tiling(struct hmi_controller *hmi_ctrl, int32_t surf_num = 0; int32_t idx = 0; - surfaces = MEM_ALLOC(sizeof(*surfaces) * surface_length); - new_order = MEM_ALLOC(sizeof(*surfaces) * surface_length); + surfaces = xcalloc(surface_length, sizeof(*surfaces)); + new_order = xcalloc(surface_length, sizeof(*surfaces)); for (i = 0; i < surface_length; i++) { ivisurf = pp_surface[i]; @@ -297,8 +290,8 @@ mode_divided_into_sidebyside(struct hmi_controller *hmi_ctrl, int32_t surf_num = 0; int32_t idx = 0; - surfaces = MEM_ALLOC(sizeof(*surfaces) * surface_length); - new_order = MEM_ALLOC(sizeof(*surfaces) * surface_length); + surfaces = xcalloc(surface_length, sizeof(*surfaces)); + new_order = xcalloc(surface_length, sizeof(*surfaces)); for (i = 0; i < surface_length; i++) { ivisurf = pp_surface[i]; @@ -362,7 +355,7 @@ mode_fullscreen_someone(struct hmi_controller *hmi_ctrl, int32_t surf_num = 0; struct ivi_layout_surface **surfaces; - surfaces = MEM_ALLOC(sizeof(*surfaces) * surface_length); + surfaces = xcalloc(surface_length, sizeof(*surfaces)); for (i = 0; i < surface_length; i++) { ivisurf = pp_surface[i]; @@ -412,7 +405,7 @@ mode_random_replace(struct hmi_controller *hmi_ctrl, int32_t i = 0; int32_t layer_idx = 0; - layers = MEM_ALLOC(sizeof(*layers) * hmi_ctrl->screen_num); + layers = xcalloc(hmi_ctrl->screen_num, sizeof(*layers)); wl_list_for_each(application_layer, layer_list, link) { layers[layer_idx] = application_layer; @@ -689,7 +682,7 @@ set_notification_configure_desktop_surface(struct wl_listener *listener, void *d static struct hmi_server_setting * hmi_server_setting_create(struct weston_compositor *ec) { - struct hmi_server_setting *setting = MEM_ALLOC(sizeof(*setting)); + struct hmi_server_setting *setting = xzalloc(sizeof(*setting)); struct weston_config *config = wet_get_config(ec); struct weston_config_section *shell_section = NULL; char *ivi_ui_config; @@ -804,7 +797,7 @@ hmi_controller_create(struct weston_compositor *ec) return NULL; } - hmi_ctrl = MEM_ALLOC(sizeof(*hmi_ctrl)); + hmi_ctrl = xzalloc(sizeof(*hmi_ctrl)); i = 0; wl_array_init(&hmi_ctrl->ui_widgets); @@ -817,7 +810,7 @@ hmi_controller_create(struct weston_compositor *ec) /* init base ivi_layer*/ wl_list_init(&hmi_ctrl->base_layer_list); wl_list_for_each(output, &ec->output_list, link) { - base_layer = MEM_ALLOC(1 * sizeof(struct hmi_controller_layer)); + base_layer = xzalloc(sizeof(struct hmi_controller_layer)); base_layer->x = 0; base_layer->y = 0; base_layer->width = output->current_mode->width; @@ -837,7 +830,7 @@ hmi_controller_create(struct weston_compositor *ec) /* init application ivi_layer */ wl_list_init(&hmi_ctrl->application_layer_list); wl_list_for_each(output, &ec->output_list, link) { - application_layer = MEM_ALLOC(1 * sizeof(struct hmi_controller_layer)); + application_layer = xzalloc(sizeof(struct hmi_controller_layer)); application_layer->x = 0; application_layer->y = 0; application_layer->width = output->current_mode->width; @@ -872,7 +865,7 @@ hmi_controller_create(struct weston_compositor *ec) wl_list_init(&hmi_ctrl->workspace_fade.layer_list); - tmp_link_layer = MEM_ALLOC(sizeof(*tmp_link_layer)); + tmp_link_layer = xzalloc(sizeof(*tmp_link_layer)); tmp_link_layer->layout_layer = hmi_ctrl->workspace_background_layer.ivilayer; wl_list_insert(&hmi_ctrl->workspace_fade.layer_list, @@ -1267,7 +1260,7 @@ ivi_hmi_controller_add_launchers(struct hmi_controller *hmi_ctrl, hmi_ctrl->interface->layer_set_visibility(hmi_ctrl->workspace_layer.ivilayer, false); - tmp_link_layer = MEM_ALLOC(sizeof(*tmp_link_layer)); + tmp_link_layer = xzalloc(sizeof(*tmp_link_layer)); tmp_link_layer->layout_layer = hmi_ctrl->workspace_layer.ivilayer; wl_list_insert(&hmi_ctrl->workspace_fade.layer_list, &tmp_link_layer->link); @@ -1756,7 +1749,7 @@ create_workspace_pointer_move(struct weston_pointer *pointer, struct wl_resource* resource) { struct pointer_move_grab *pnt_move_grab = - MEM_ALLOC(sizeof(*pnt_move_grab)); + xzalloc(sizeof(*pnt_move_grab)); pnt_move_grab->base.resource = resource; move_grab_init_workspace(&pnt_move_grab->move, pointer->grab_x, @@ -1770,7 +1763,7 @@ create_workspace_touch_move(struct weston_touch *touch, struct wl_resource* resource) { struct touch_move_grab *tch_move_grab = - MEM_ALLOC(sizeof(*tch_move_grab)); + xzalloc(sizeof(*tch_move_grab)); tch_move_grab->base.resource = resource; tch_move_grab->is_active = 1; diff --git a/shared/xalloc.h b/shared/xalloc.h index 15ad1fad..86647d34 100644 --- a/shared/xalloc.h +++ b/shared/xalloc.h @@ -57,6 +57,7 @@ fail_on_null(void *p, size_t size, char *file, int32_t line) #define xmalloc(s) (fail_on_null(malloc(s), (s), __FILE__, __LINE__)) #define xzalloc(s) (fail_on_null(zalloc(s), (s), __FILE__, __LINE__)) +#define xcalloc(n, s) (fail_on_null(calloc(n, s), (n) * (s), __FILE__, __LINE__)) #define xstrdup(s) (fail_on_null(strdup(s), 0, __FILE__, __LINE__)) #define xrealloc(p, s) (fail_on_null(realloc(p, s), (s), __FILE__, __LINE__)) From 9229a45116e6c33930c5f5de79d5a4790ae84642 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Wed, 20 Jul 2022 12:29:56 +0300 Subject: [PATCH 505/609] shared: rewrite fail_on_null() as abort_oom_if_null() Recently I learnt that fprintf() is not async-signal-safe. Maybe it also attempts to allocate memory sometimes. Hence, using it when we presumably are out of memory is wishful thinking. Therefore replace that with async-signal-safe code. If you have to check pointers from traditional signal handlers, now you could do that too! While doing this, we also lose the string formatting for line number. I would argue that printing file and line number is not that useful, if the system really is out of memory. If not out of memory, a core dump would give us much more detailed information about what went wrong. clients/window.c had some calls to fail_on_null() and these are simply replaced. They were used for checking that creating new wl_proxy by issuing a protocol request worked, and IIRC that only fails on out-of-memory, so the same rationale applies here. Signed-off-by: Pekka Paalanen --- clients/window.c | 12 ++++++------ shared/xalloc.h | 36 +++++++++++++++++------------------- 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/clients/window.c b/clients/window.c index 52713488..8073fbc6 100644 --- a/clients/window.c +++ b/clients/window.c @@ -5081,14 +5081,14 @@ window_create(struct display *display) window->xdg_surface = xdg_wm_base_get_xdg_surface(window->display->xdg_shell, window->main_surface->surface); - fail_on_null(window->xdg_surface, 0, __FILE__, __LINE__); + abort_oom_if_null(window->xdg_surface); xdg_surface_add_listener(window->xdg_surface, &xdg_surface_listener, window); window->xdg_toplevel = xdg_surface_get_toplevel(window->xdg_surface); - fail_on_null(window->xdg_toplevel, 0, __FILE__, __LINE__); + abort_oom_if_null(window->xdg_toplevel); xdg_toplevel_add_listener(window->xdg_toplevel, &xdg_toplevel_listener, window); @@ -5293,7 +5293,7 @@ create_menu(struct display *display, menu->widget = window_add_widget(menu->window, menu); menu->frame = frame_create(window->display->theme, 0, 0, FRAME_BUTTON_NONE, NULL, NULL); - fail_on_null(menu->frame, 0, __FILE__, __LINE__); + abort_oom_if_null(menu->frame); menu->entries = entries; menu->count = count; menu->release_count = 0; @@ -5327,7 +5327,7 @@ create_simple_positioner(struct display *display, struct xdg_positioner *positioner; positioner = xdg_wm_base_create_positioner(display->xdg_shell); - fail_on_null(positioner, 0, __FILE__, __LINE__); + abort_oom_if_null(positioner); xdg_positioner_set_anchor_rect(positioner, x, y, 1, 1); xdg_positioner_set_size(positioner, w, h); xdg_positioner_set_anchor(positioner, @@ -5372,7 +5372,7 @@ window_show_menu(struct display *display, window->xdg_surface = xdg_wm_base_get_xdg_surface(display->xdg_shell, window->main_surface->surface); - fail_on_null(window->xdg_surface, 0, __FILE__, __LINE__); + abort_oom_if_null(window->xdg_surface); xdg_surface_add_listener(window->xdg_surface, &xdg_surface_listener, window); @@ -5385,7 +5385,7 @@ window_show_menu(struct display *display, window->xdg_popup = xdg_surface_get_popup(window->xdg_surface, parent->xdg_surface, positioner); - fail_on_null(window->xdg_popup, 0, __FILE__, __LINE__); + abort_oom_if_null(window->xdg_popup); xdg_positioner_destroy(positioner); xdg_popup_grab(window->xdg_popup, input->seat, display_get_serial(window->display)); diff --git a/shared/xalloc.h b/shared/xalloc.h index 86647d34..f73c2315 100644 --- a/shared/xalloc.h +++ b/shared/xalloc.h @@ -1,5 +1,6 @@ /* * Copyright © 2008 Kristian Høgsberg + * Copyright 2022 Collabora, Ltd. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -31,35 +32,32 @@ extern "C" { #endif #include -#include #include +#include #include #include - static inline void * -fail_on_null(void *p, size_t size, char *file, int32_t line) +abort_oom_if_null(void *p) { - if (p == NULL) { - fprintf(stderr, "[%s] ", program_invocation_short_name); - if (file) - fprintf(stderr, "%s:%d: ", file, line); - fprintf(stderr, "out of memory"); - if (size) - fprintf(stderr, " (%zd)", size); - fprintf(stderr, "\n"); - exit(EXIT_FAILURE); - } + static const char oommsg[] = ": out of memory\n"; + + if (p) + return p; + + write(STDERR_FILENO, program_invocation_short_name, + strlen(program_invocation_short_name)); + write(STDERR_FILENO, oommsg, strlen(oommsg)); - return p; + abort(); } -#define xmalloc(s) (fail_on_null(malloc(s), (s), __FILE__, __LINE__)) -#define xzalloc(s) (fail_on_null(zalloc(s), (s), __FILE__, __LINE__)) -#define xcalloc(n, s) (fail_on_null(calloc(n, s), (n) * (s), __FILE__, __LINE__)) -#define xstrdup(s) (fail_on_null(strdup(s), 0, __FILE__, __LINE__)) -#define xrealloc(p, s) (fail_on_null(realloc(p, s), (s), __FILE__, __LINE__)) +#define xmalloc(s) (abort_oom_if_null(malloc(s))) +#define xzalloc(s) (abort_oom_if_null(zalloc(s))) +#define xcalloc(n, s) (abort_oom_if_null(calloc(n, s))) +#define xstrdup(s) (abort_oom_if_null(strdup(s))) +#define xrealloc(p, s) (abort_oom_if_null(realloc(p, s))) #ifdef __cplusplus } From c95feefbc0fc35954c202b4b1312a566f00fb994 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Wed, 20 Jul 2022 12:45:04 +0300 Subject: [PATCH 506/609] clients/simple-touch: use xzalloc() for buffer This file relied on shared/xalloc.h to include . That would be a problem if xalloc.h stopped doing that. Just use xzalloc(). Signed-off-by: Pekka Paalanen --- clients/simple-touch.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/clients/simple-touch.c b/clients/simple-touch.c index 7c9ada64..6559aa24 100644 --- a/clients/simple-touch.c +++ b/clients/simple-touch.c @@ -75,11 +75,9 @@ create_shm_buffer(struct touch *touch) struct wl_shm_pool *pool; int fd, size, stride; void *data; - struct buffer *buffer = NULL; + struct buffer *buffer; - buffer = zalloc(sizeof(*buffer)); - if (!buffer) - return NULL; + buffer = xzalloc(sizeof(*buffer)); stride = touch->width * 4; size = stride * touch->height; From fc26c749dfae47e5b4f81ad38760e3e7585b3216 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Wed, 20 Jul 2022 12:47:11 +0300 Subject: [PATCH 507/609] shared/xalloc.h: do not rely on zalloc() The definition of zalloc is trivial, so let's just have it here instead of loading libweston/zalloc.h. Now xalloc.h does not depend on any libweston header, which makes me feel slightly better. It's more clean. Who knows, maybe one day libweston/zalloc.h will be removed. Signed-off-by: Pekka Paalanen --- shared/xalloc.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/shared/xalloc.h b/shared/xalloc.h index f73c2315..2bbb426e 100644 --- a/shared/xalloc.h +++ b/shared/xalloc.h @@ -36,8 +36,6 @@ extern "C" { #include #include -#include - static inline void * abort_oom_if_null(void *p) { @@ -54,7 +52,7 @@ abort_oom_if_null(void *p) } #define xmalloc(s) (abort_oom_if_null(malloc(s))) -#define xzalloc(s) (abort_oom_if_null(zalloc(s))) +#define xzalloc(s) (abort_oom_if_null(calloc(1, s))) #define xcalloc(n, s) (abort_oom_if_null(calloc(n, s))) #define xstrdup(s) (abort_oom_if_null(strdup(s))) #define xrealloc(p, s) (abort_oom_if_null(realloc(p, s))) From 9358706743c208e2fd0f36bea5a3f45f91fa58c9 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Wed, 20 Jul 2022 13:01:13 +0300 Subject: [PATCH 508/609] README: establish no-malloc-failures policy There are many reasons why trying to handle malloc() returning NULL by any other way than calling abort() is not beneficial: - Usually malloc() does not return NULL, thanks to memory overcommit. Instead, the program gets SIGSEGV signal when it tries to access the memory. - Trying to handle NULL will create failure paths that are impractical to test. There is no way to be sure the compositor still works once such path is actually taken. - Those failure path will clutter the code, increasing maintenance and development burden. - Sometimes there just isn't a good way to handle the failure. For more discussion, see the issue link below. Closes: https://gitlab.freedesktop.org/wayland/weston/-/issues/631 Signed-off-by: Pekka Paalanen --- CONTRIBUTING.md | 4 ++++ README.md | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 55e6e8a1..b8687bb1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -208,6 +208,10 @@ my_function(void) parameter3, parameter4); ``` +- do not write fallback paths for failed simple memory allocations, use the + `x*alloc()` wrappers from `shared/xalloc.h` instead or use + `abort_oom_if_null()` + Conduct ======= diff --git a/README.md b/README.md index 81dbeae2..03e32392 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,12 @@ bugs and shortcomings, we avoid unknown or variable behaviour as much as possible, including variable performance such as occasional spikes in frame display time. +Weston and libweston are not suitable for memory constrained environments +where the compositor is expected to continue running even in the face of +trivial memory allocations failing. If standard functions like `malloc()` +fail for small allocations, +[you can expect libweston to abort](https://gitlab.freedesktop.org/wayland/weston/-/issues/631). + A small suite of example or demo clients are also provided: though they can be useful in themselves, their main purpose is to be an example or test case for others building compositors or clients. From 0dcd000b3a8409518037be1d177f0cbdeea44dbf Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 21 Jan 2022 12:50:20 +0000 Subject: [PATCH 509/609] build: Separate unstable and version for wayland-protocols We want to support staging protocols which have a version too, so don't assume that anything versioned is unstable. Signed-off-by: Daniel Stone --- protocol/meson.build | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/protocol/meson.build b/protocol/meson.build index efc1281f..973388fd 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -15,19 +15,19 @@ install_data( ) generated_protocols = [ - [ 'input-method', 'v1' ], - [ 'input-timestamps', 'v1' ], + [ 'input-method', 'unstable', 'v1' ], + [ 'input-timestamps', 'unstable', 'v1' ], [ 'ivi-application', 'internal' ], [ 'ivi-hmi-controller', 'internal' ], - [ 'fullscreen-shell', 'v1' ], - [ 'linux-dmabuf', 'v1' ], - [ 'linux-explicit-synchronization', 'v1' ], + [ 'fullscreen-shell', 'unstable', 'v1' ], + [ 'linux-dmabuf', 'unstable', 'v1' ], + [ 'linux-explicit-synchronization', 'unstable', 'v1' ], [ 'presentation-time', 'stable' ], - [ 'pointer-constraints', 'v1' ], - [ 'relative-pointer', 'v1' ], - [ 'tablet', 'v2' ], + [ 'pointer-constraints', 'unstable', 'v1' ], + [ 'relative-pointer', 'unstable', 'v1' ], + [ 'tablet', 'unstable', 'v2' ], [ 'text-cursor-position', 'internal' ], - [ 'text-input', 'v1' ], + [ 'text-input', 'unstable', 'v1' ], [ 'viewporter', 'stable' ], [ 'weston-debug', 'internal' ], [ 'weston-desktop-shell', 'internal' ], @@ -36,8 +36,8 @@ generated_protocols = [ [ 'weston-test', 'internal' ], [ 'weston-touch-calibration', 'internal' ], [ 'weston-direct-display', 'internal' ], - [ 'xdg-output', 'v1' ], - [ 'xdg-shell', 'v6' ], + [ 'xdg-output', 'unstable', 'v1' ], + [ 'xdg-shell', 'unstable', 'v6' ], [ 'xdg-shell', 'stable' ], ] @@ -49,8 +49,8 @@ foreach proto: generated_protocols elif proto[1] == 'stable' base_file = proto_name xml_path = '@0@/stable/@1@/@1@.xml'.format(dir_wp_base, base_file) - else - base_file = '@0@-unstable-@1@'.format(proto_name, proto[1]) + elif proto[1] == 'unstable' + base_file = '@0@-unstable-@1@'.format(proto_name, proto[2]) xml_path = '@0@/unstable/@1@/@2@.xml'.format(dir_wp_base, proto_name, base_file) endif From 28caa08be6ca0ac6fe38afd341ca5f2e076fc2ca Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Fri, 21 Jan 2022 14:13:58 +0000 Subject: [PATCH 510/609] Implement wp_single_pixel_buffer_v1 protocol This protocol allows clients to create single-pixel RGBA buffers. Now that we have proper support for these buffers internally within Weston, we can expose them to clients. This bumps the build container version, as we now depend on wayland-protocols v1.26. Signed-off-by: Daniel Stone --- .gitlab-ci.yml | 2 +- .gitlab-ci/build-deps.sh | 2 +- include/libweston/libweston.h | 8 +- libweston/compositor.c | 114 +++++++++++++++++++++ libweston/meson.build | 2 + protocol/meson.build | 6 +- tests/meson.build | 8 ++ tests/reference/single-pixel-buffer-00.png | Bin 0 -> 833 bytes tests/single-pixel-buffer-test.c | 111 ++++++++++++++++++++ 9 files changed, 247 insertions(+), 6 deletions(-) create mode 100644 tests/reference/single-pixel-buffer-00.png create mode 100644 tests/single-pixel-buffer-test.c diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index dd018f83..65ae360b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -43,7 +43,7 @@ variables: FDO_UPSTREAM_REPO: wayland/weston FDO_REPO_SUFFIX: "$BUILD_OS/$BUILD_ARCH" - FDO_DISTRIBUTION_TAG: '2022-06-28.00-graphviz' + FDO_DISTRIBUTION_TAG: '2022-07-13.00-wayland-protocols-1.26' include: diff --git a/.gitlab-ci/build-deps.sh b/.gitlab-ci/build-deps.sh index 1f85fb8f..3c8f36fc 100755 --- a/.gitlab-ci/build-deps.sh +++ b/.gitlab-ci/build-deps.sh @@ -107,7 +107,7 @@ rm -rf wayland # Keep this version in sync with our dependency in meson.build. If you wish to # raise a MR against custom protocol, please change this reference to clone # your relevant tree, and make sure you bump $FDO_DISTRIBUTION_TAG. -git clone --branch 1.24 --depth=1 https://gitlab.freedesktop.org/wayland/wayland-protocols +git clone --branch 1.26 --depth=1 https://gitlab.freedesktop.org/wayland/wayland-protocols cd wayland-protocols git show -s HEAD meson build diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 6e5ebbca..79ac8954 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -1318,6 +1318,10 @@ struct weston_compositor { bool warned_about_unmapped_surface_or_view; }; +struct solid_buffer_values { + float r, g, b, a; +}; + struct weston_buffer { struct wl_resource *resource; struct wl_signal destroy_signal; @@ -1334,9 +1338,7 @@ struct weston_buffer { struct wl_shm_buffer *shm_buffer; void *dmabuf; void *legacy_buffer; - struct { - float r, g, b, a; - } solid; + struct solid_buffer_values solid; }; int32_t width, height; diff --git a/libweston/compositor.c b/libweston/compositor.c index deb91b1c..9d7bffdf 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -65,6 +65,7 @@ #include "xdg-output-unstable-v1-server-protocol.h" #include "linux-explicit-synchronization-unstable-v1-server-protocol.h" #include "linux-explicit-synchronization.h" +#include "single-pixel-buffer-v1-server-protocol.h" #include "shared/fd-util.h" #include "shared/helpers.h" #include "shared/os-compatibility.h" @@ -2414,6 +2415,9 @@ destroy_surface(struct wl_resource *resource) weston_surface_unref(surface); } +static struct solid_buffer_values * +single_pixel_buffer_get(struct wl_resource *resource); + static void weston_buffer_destroy_handler(struct wl_listener *listener, void *data) { @@ -2438,6 +2442,7 @@ weston_buffer_from_resource(struct weston_compositor *ec, struct wl_shm_buffer *shm; struct linux_dmabuf_buffer *dmabuf; struct wl_listener *listener; + struct solid_buffer_values *solid; listener = wl_resource_get_destroy_listener(resource, weston_buffer_destroy_handler); @@ -2485,6 +2490,19 @@ weston_buffer_from_resource(struct weston_compositor *ec, buffer->buffer_origin = ORIGIN_BOTTOM_LEFT; else buffer->buffer_origin = ORIGIN_TOP_LEFT; + } else if ((solid = single_pixel_buffer_get(buffer->resource))) { + buffer->type = WESTON_BUFFER_SOLID; + buffer->solid = *solid; + buffer->width = 1; + buffer->height = 1; + if (buffer->solid.a == 1.0) { + buffer->pixel_format = + pixel_format_get_info(DRM_FORMAT_XRGB8888); + } else { + buffer->pixel_format = + pixel_format_get_info(DRM_FORMAT_ARGB8888); + } + buffer->format_modifier = DRM_FORMAT_MOD_LINEAR; } else { /* Only taken for legacy EGL buffers */ if (!ec->renderer->fill_buffer_info || @@ -2701,6 +2719,97 @@ weston_buffer_destroy_solid(struct weston_buffer_reference *buffer_ref) free(buffer_ref); } +static void +single_pixel_buffer_destroy(struct wl_resource *resource) +{ + struct solid_buffer_values *solid = + wl_resource_get_user_data(resource); + free(solid); +} + +static void +single_pixel_buffer_handle_buffer_destroy(struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static const struct wl_buffer_interface single_pixel_buffer_implementation = { + single_pixel_buffer_handle_buffer_destroy, +}; + +static struct solid_buffer_values * +single_pixel_buffer_get(struct wl_resource *resource) +{ + if (!resource) + return NULL; + + if (!wl_resource_instance_of(resource, &wl_buffer_interface, + &single_pixel_buffer_implementation)) + return NULL; + + return wl_resource_get_user_data(resource); +} + +static void +single_pixel_buffer_manager_destroy(struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static void +single_pixel_buffer_create(struct wl_client *client, struct wl_resource *resource, + uint32_t id, uint32_t r, uint32_t g, uint32_t b, uint32_t a) +{ + struct solid_buffer_values *solid = zalloc(sizeof(*solid)); + struct wl_resource *buffer; + + if (!solid) { + wl_client_post_no_memory(client); + return; + } + + solid->r = r / (double) 0xffffffff; + solid->g = g / (double) 0xffffffff; + solid->b = b / (double) 0xffffffff; + solid->a = a / (double) 0xffffffff; + + buffer = wl_resource_create(client, &wl_buffer_interface, 1, id); + if (!buffer) { + wl_client_post_no_memory(client); + free(solid); + return; + } + wl_resource_set_implementation(buffer, + &single_pixel_buffer_implementation, + solid, single_pixel_buffer_destroy); +} + +static const struct wp_single_pixel_buffer_manager_v1_interface +single_pixel_buffer_manager_implementation = { + single_pixel_buffer_manager_destroy, + single_pixel_buffer_create, +}; + +static void +bind_single_pixel_buffer(struct wl_client *client, void *data, uint32_t version, + uint32_t id) +{ + struct wl_resource *resource; + + resource = wl_resource_create(client, + &wp_single_pixel_buffer_manager_v1_interface, 1, + id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, + &single_pixel_buffer_manager_implementation, + NULL, NULL); +} + static void weston_surface_attach(struct weston_surface *surface, struct weston_buffer *buffer) @@ -8232,6 +8341,11 @@ weston_compositor_create(struct wl_display *display, ec, bind_presentation)) goto fail; + if (!wl_global_create(ec->wl_display, + &wp_single_pixel_buffer_manager_v1_interface, 1, + NULL, bind_single_pixel_buffer)) + goto fail; + if (weston_input_init(ec) != 0) goto fail; diff --git a/libweston/meson.build b/libweston/meson.build index 82f81466..313a84f6 100644 --- a/libweston/meson.build +++ b/libweston/meson.build @@ -51,6 +51,8 @@ srcs_libweston = [ relative_pointer_unstable_v1_server_protocol_h, weston_screenshooter_protocol_c, weston_screenshooter_server_protocol_h, + single_pixel_buffer_v1_protocol_c, + single_pixel_buffer_v1_server_protocol_h, text_cursor_position_protocol_c, text_cursor_position_server_protocol_h, text_input_unstable_v1_protocol_c, diff --git a/protocol/meson.build b/protocol/meson.build index 973388fd..ba52089b 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -1,7 +1,7 @@ dep_scanner = dependency('wayland-scanner', native: true) prog_scanner = find_program(dep_scanner.get_pkgconfig_variable('wayland_scanner')) -dep_wp = dependency('wayland-protocols', version: '>= 1.24', +dep_wp = dependency('wayland-protocols', version: '>= 1.26', fallback: ['wayland-protocols', 'wayland_protocols']) dir_wp_base = dep_wp.get_variable(pkgconfig: 'pkgdatadir', internal: 'pkgdatadir') @@ -25,6 +25,7 @@ generated_protocols = [ [ 'presentation-time', 'stable' ], [ 'pointer-constraints', 'unstable', 'v1' ], [ 'relative-pointer', 'unstable', 'v1' ], + [ 'single-pixel-buffer', 'staging', 'v1' ], [ 'tablet', 'unstable', 'v2' ], [ 'text-cursor-position', 'internal' ], [ 'text-input', 'unstable', 'v1' ], @@ -52,6 +53,9 @@ foreach proto: generated_protocols elif proto[1] == 'unstable' base_file = '@0@-unstable-@1@'.format(proto_name, proto[2]) xml_path = '@0@/unstable/@1@/@2@.xml'.format(dir_wp_base, proto_name, base_file) + elif proto[1] == 'staging' + base_file = '@0@-@1@'.format(proto_name, proto[2]) + xml_path = '@0@/staging/@1@/@2@.xml'.format(dir_wp_base, proto_name, base_file) endif foreach output_type: [ 'client-header', 'server-header', 'private-code' ] diff --git a/tests/meson.build b/tests/meson.build index 50722f95..cc35934d 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -205,6 +205,14 @@ tests = [ xdg_shell_protocol_c, ], }, + { + 'name': 'single-pixel-buffer', + 'sources': [ + 'single-pixel-buffer-test.c', + single_pixel_buffer_v1_client_protocol_h, + single_pixel_buffer_v1_protocol_c, + ] + }, { 'name': 'string', }, { 'name': 'subsurface', }, { 'name': 'subsurface-shot', }, diff --git a/tests/reference/single-pixel-buffer-00.png b/tests/reference/single-pixel-buffer-00.png new file mode 100644 index 0000000000000000000000000000000000000000..2fa7f5945de379af7c2d15b271976989c6337126 GIT binary patch literal 833 zcmeAS@N?(olHy`uVBq!ia0y~yU~~YoKX5Ps$$$P@Hb9Ck$=lt9;Xep2*t>i(0|V1H zPZ!6KiaBquI`ST35OHx-5OogXuXwRB%%G*e>Hxpoi-*sb8gqU7wCVfvZ~GX1`(H91 z_|L+V$kb-Q=scrA(dU4GS^|f#K?|qEp+PJhy^MX%*UH-E>M^(K)?60WJIg$T96#to smyiQbe`(nKEqU%dhGd(;kx;%dYw@zn`hKrc1?C9`Pgg&ebxsLQ0JU-JoB#j- literal 0 HcmV?d00001 diff --git a/tests/single-pixel-buffer-test.c b/tests/single-pixel-buffer-test.c new file mode 100644 index 00000000..ba013232 --- /dev/null +++ b/tests/single-pixel-buffer-test.c @@ -0,0 +1,111 @@ +/* + * Copyright © 2020 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include "weston-test-client-helper.h" +#include "weston-test-fixture-compositor.h" +#include "single-pixel-buffer-v1-client-protocol.h" +#include "shared/os-compatibility.h" +#include "shared/xalloc.h" + +struct setup_args { + struct fixture_metadata meta; + enum renderer_type renderer; +}; + +static const struct setup_args my_setup_args[] = { + { + .renderer = RENDERER_PIXMAN, + .meta.name = "pixman" + }, + { + .renderer = RENDERER_GL, + .meta.name = "GL" + }, +}; + +static enum test_result_code +fixture_setup(struct weston_test_harness *harness, const struct setup_args *arg) +{ + struct compositor_setup setup; + + compositor_setup_defaults(&setup); + setup.renderer = arg->renderer; + setup.width = 320; + setup.height = 240; + setup.shell = SHELL_TEST_DESKTOP; + setup.logging_scopes = "log,test-harness-plugin"; + + return weston_test_harness_execute_as_client(harness, &setup); +} +DECLARE_FIXTURE_SETUP_WITH_ARG(fixture_setup, my_setup_args, meta); + +TEST(solid_buffer_argb_u32) +{ + struct client *client; + struct wp_single_pixel_buffer_manager_v1 *mgr; + struct wp_viewport *viewport; + struct wl_buffer *buffer; + int done; + bool match; + + client = create_client(); + client->surface = create_test_surface(client); + viewport = client_create_viewport(client); + wp_viewport_set_destination(viewport, 128, 128); + + mgr = bind_to_singleton_global(client, + &wp_single_pixel_buffer_manager_v1_interface, + 1); + buffer = wp_single_pixel_buffer_manager_v1_create_u32_rgba_buffer(mgr, + 0xcfffffff, /* r */ + 0x8fffffff, /* g */ + 0x4fffffff, /* b */ + 0xffffffff /* a */); + assert(buffer); + + weston_test_move_surface(client->test->weston_test, + client->surface->wl_surface, + 64, 64); + wl_surface_attach(client->surface->wl_surface, buffer, 0, 0); + wl_surface_damage_buffer(client->surface->wl_surface, 0, 0, 1, 1); + frame_callback_set(client->surface->wl_surface, &done); + wl_surface_commit(client->surface->wl_surface); + frame_callback_wait(client, &done); + + match = verify_screen_content(client, "single-pixel-buffer", 0, NULL, 0); + assert(match); + + wl_buffer_destroy(buffer); + wp_viewport_destroy(viewport); + client_destroy(client); +} From 1541c447770c85a39fe1bc38208d2a176ecfb3ae Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Sun, 26 Jun 2022 18:00:09 +0100 Subject: [PATCH 511/609] libweston-desktop: Add shell capability queries Allow other components to query which window-management operations are supported by the shell. Signed-off-by: Daniel Stone --- include/libweston-desktop/libweston-desktop.h | 13 +++++++ libweston/desktop/libweston-desktop.c | 36 +++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/include/libweston-desktop/libweston-desktop.h b/include/libweston-desktop/libweston-desktop.h index 313179ef..8257e467 100644 --- a/include/libweston-desktop/libweston-desktop.h +++ b/include/libweston-desktop/libweston-desktop.h @@ -198,6 +198,19 @@ weston_desktop_surface_get_max_size(struct weston_desktop_surface *surface); struct weston_size weston_desktop_surface_get_min_size(struct weston_desktop_surface *surface); +bool +weston_desktop_window_menu_supported(struct weston_desktop *desktop); +bool +weston_desktop_move_supported(struct weston_desktop *desktop); +bool +weston_desktop_resize_supported(struct weston_desktop *desktop); +bool +weston_desktop_fullscreen_supported(struct weston_desktop *desktop); +bool +weston_desktop_minimize_supported(struct weston_desktop *desktop); +bool +weston_desktop_maximize_supported(struct weston_desktop *desktop); + #ifdef __cplusplus } #endif diff --git a/libweston/desktop/libweston-desktop.c b/libweston/desktop/libweston-desktop.c index f7ecc709..0be9d717 100644 --- a/libweston/desktop/libweston-desktop.c +++ b/libweston/desktop/libweston-desktop.c @@ -170,6 +170,12 @@ weston_desktop_api_show_window_menu(struct weston_desktop *desktop, desktop->user_data); } +bool +weston_desktop_window_menu_supported(struct weston_desktop *desktop) +{ + return desktop->api.show_window_menu != NULL; +} + void weston_desktop_api_set_parent(struct weston_desktop *desktop, struct weston_desktop_surface *surface, @@ -188,6 +194,12 @@ weston_desktop_api_move(struct weston_desktop *desktop, desktop->api.move(surface, seat, serial, desktop->user_data); } +bool +weston_desktop_move_supported(struct weston_desktop *desktop) +{ + return desktop->api.move != NULL; +} + void weston_desktop_api_resize(struct weston_desktop *desktop, struct weston_desktop_surface *surface, @@ -199,6 +211,12 @@ weston_desktop_api_resize(struct weston_desktop *desktop, desktop->user_data); } +bool +weston_desktop_resize_supported(struct weston_desktop *desktop) +{ + return desktop->api.resize != NULL; +} + void weston_desktop_api_fullscreen_requested(struct weston_desktop *desktop, struct weston_desktop_surface *surface, @@ -210,6 +228,12 @@ weston_desktop_api_fullscreen_requested(struct weston_desktop *desktop, desktop->user_data); } +bool +weston_desktop_fullscreen_supported(struct weston_desktop *desktop) +{ + return desktop->api.fullscreen_requested != NULL; +} + void weston_desktop_api_maximized_requested(struct weston_desktop *desktop, struct weston_desktop_surface *surface, @@ -220,6 +244,12 @@ weston_desktop_api_maximized_requested(struct weston_desktop *desktop, desktop->user_data); } +bool +weston_desktop_maximize_supported(struct weston_desktop *desktop) +{ + return desktop->api.maximized_requested != NULL; +} + void weston_desktop_api_minimized_requested(struct weston_desktop *desktop, struct weston_desktop_surface *surface) @@ -228,6 +258,12 @@ weston_desktop_api_minimized_requested(struct weston_desktop *desktop, desktop->api.minimized_requested(surface, desktop->user_data); } +bool +weston_desktop_minimize_supported(struct weston_desktop *desktop) +{ + return desktop->api.minimized_requested != NULL; +} + void weston_desktop_api_set_xwayland_position(struct weston_desktop *desktop, struct weston_desktop_surface *surface, From b047f989a59f7b5372c609b095c7d2a95c638584 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Sun, 26 Jun 2022 18:21:00 +0100 Subject: [PATCH 512/609] xdg-shell: Implement xdg-shell v5 capabilities event This skips over xdg-shell v4, which can be implemented with no changes as it's just another optional event. v5 adds a capabilities event, which we send to inform clients of the window manager's capabilities. Signed-off-by: Daniel Stone --- libweston/desktop/xdg-shell.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/libweston/desktop/xdg-shell.c b/libweston/desktop/xdg-shell.c index eea00143..002a8231 100644 --- a/libweston/desktop/xdg-shell.c +++ b/libweston/desktop/xdg-shell.c @@ -45,7 +45,7 @@ * implements the older unstable xdg shell v6 protocol. ************************************************************************************/ -#define WD_XDG_SHELL_PROTOCOL_VERSION 3 +#define WD_XDG_SHELL_PROTOCOL_VERSION 5 static const char *weston_desktop_xdg_toplevel_role = "xdg_toplevel"; static const char *weston_desktop_xdg_popup_role = "xdg_popup"; @@ -1162,6 +1162,7 @@ weston_desktop_xdg_surface_protocol_get_toplevel(struct wl_client *wl_client, weston_desktop_surface_get_surface(dsurface); struct weston_desktop_xdg_toplevel *toplevel = weston_desktop_surface_get_implementation_data(dsurface); + struct weston_desktop *desktop = toplevel->base.desktop; if (weston_surface_set_role(wsurface, weston_desktop_xdg_toplevel_role, resource, XDG_WM_BASE_ERROR_ROLE) < 0) @@ -1176,6 +1177,35 @@ weston_desktop_xdg_surface_protocol_get_toplevel(struct wl_client *wl_client, return; toplevel->base.role = WESTON_DESKTOP_XDG_SURFACE_ROLE_TOPLEVEL; + + if (wl_resource_get_version(toplevel->resource) >= + XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION) { + struct wl_array caps; + uint32_t *cap; + + wl_array_init(&caps); + + if (weston_desktop_window_menu_supported(desktop)) { + cap = wl_array_add(&caps, sizeof(*cap)); + *cap = XDG_TOPLEVEL_WM_CAPABILITIES_WINDOW_MENU; + } + if (weston_desktop_maximize_supported(desktop)) { + cap = wl_array_add(&caps, sizeof(*cap)); + *cap = XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE; + } + if (weston_desktop_fullscreen_supported(desktop)) { + cap = wl_array_add(&caps, sizeof(*cap)); + *cap = XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN; + } + if (weston_desktop_minimize_supported(desktop)) { + cap = wl_array_add(&caps, sizeof(*cap)); + *cap = XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE; + } + + xdg_toplevel_send_wm_capabilities(toplevel->resource, &caps); + + wl_array_release(&caps); + } } static void From a09f02d43af486f0b0c27f2335a7fb3c157e685a Mon Sep 17 00:00:00 2001 From: Joshua Watt Date: Fri, 22 Jul 2022 08:59:45 -0500 Subject: [PATCH 513/609] libweston: Compute output protection when head is attached A head may have its output protection set before it is attached to an output. Recompute the output protection whenever a head is attached to make sure it correctly set in output. Signed-off-by: Joshua Watt --- libweston/compositor.c | 56 ++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/libweston/compositor.c b/libweston/compositor.c index 9d7bffdf..8dfacfe7 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -5803,6 +5803,33 @@ weston_output_iterate_heads(struct weston_output *output, return container_of(node, struct weston_head, output_link); } +static void +weston_output_compute_protection(struct weston_output *output) +{ + struct weston_head *head; + enum weston_hdcp_protection op_protection; + bool op_protection_valid = false; + struct weston_compositor *wc = output->compositor; + + wl_list_for_each(head, &output->head_list, output_link) { + if (!op_protection_valid) { + op_protection = head->current_protection; + op_protection_valid = true; + } + if (head->current_protection < op_protection) + op_protection = head->current_protection; + } + + if (!op_protection_valid) + op_protection = WESTON_HDCP_DISABLE; + + if (output->current_protection != op_protection) { + output->current_protection = op_protection; + weston_output_damage(output); + weston_schedule_surface_protection_update(wc); + } +} + /** Attach a head to an output * * \param output The output to attach to. @@ -5842,6 +5869,8 @@ weston_output_attach_head(struct weston_output *output, head->output = output; wl_list_insert(output->head_list.prev, &head->output_link); + weston_output_compute_protection(output); + if (output->enabled) { weston_head_add_global(head); @@ -6154,33 +6183,6 @@ weston_head_set_supported_eotf_mask(struct weston_head *head, weston_head_set_device_changed(head); } -static void -weston_output_compute_protection(struct weston_output *output) -{ - struct weston_head *head; - enum weston_hdcp_protection op_protection; - bool op_protection_valid = false; - struct weston_compositor *wc = output->compositor; - - wl_list_for_each(head, &output->head_list, output_link) { - if (!op_protection_valid) { - op_protection = head->current_protection; - op_protection_valid = true; - } - if (head->current_protection < op_protection) - op_protection = head->current_protection; - } - - if (!op_protection_valid) - op_protection = WESTON_HDCP_DISABLE; - - if (output->current_protection != op_protection) { - output->current_protection = op_protection; - weston_output_damage(output); - weston_schedule_surface_protection_update(wc); - } -} - WL_EXPORT void weston_head_set_content_protection_status(struct weston_head *head, enum weston_hdcp_protection status) From a8048c5c1cb2f83ae676bac0adc31bd3d6655c4f Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Tue, 26 Jul 2022 11:52:10 +0100 Subject: [PATCH 514/609] libweston: Properly namespace solid_buffer_values It's exported by libweston, so shouldn't leak unprefixed types. Signed-off-by: Daniel Stone --- include/libweston/libweston.h | 4 ++-- libweston/compositor.c | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 79ac8954..a36e4d7a 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -1318,7 +1318,7 @@ struct weston_compositor { bool warned_about_unmapped_surface_or_view; }; -struct solid_buffer_values { +struct weston_solid_buffer_values { float r, g, b, a; }; @@ -1338,7 +1338,7 @@ struct weston_buffer { struct wl_shm_buffer *shm_buffer; void *dmabuf; void *legacy_buffer; - struct solid_buffer_values solid; + struct weston_solid_buffer_values solid; }; int32_t width, height; diff --git a/libweston/compositor.c b/libweston/compositor.c index 8dfacfe7..2285e3f1 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -2415,7 +2415,7 @@ destroy_surface(struct wl_resource *resource) weston_surface_unref(surface); } -static struct solid_buffer_values * +static struct weston_solid_buffer_values * single_pixel_buffer_get(struct wl_resource *resource); static void @@ -2442,7 +2442,7 @@ weston_buffer_from_resource(struct weston_compositor *ec, struct wl_shm_buffer *shm; struct linux_dmabuf_buffer *dmabuf; struct wl_listener *listener; - struct solid_buffer_values *solid; + struct weston_solid_buffer_values *solid; listener = wl_resource_get_destroy_listener(resource, weston_buffer_destroy_handler); @@ -2722,7 +2722,7 @@ weston_buffer_destroy_solid(struct weston_buffer_reference *buffer_ref) static void single_pixel_buffer_destroy(struct wl_resource *resource) { - struct solid_buffer_values *solid = + struct weston_solid_buffer_values *solid = wl_resource_get_user_data(resource); free(solid); } @@ -2738,7 +2738,7 @@ static const struct wl_buffer_interface single_pixel_buffer_implementation = { single_pixel_buffer_handle_buffer_destroy, }; -static struct solid_buffer_values * +static struct weston_solid_buffer_values * single_pixel_buffer_get(struct wl_resource *resource) { if (!resource) @@ -2762,7 +2762,7 @@ static void single_pixel_buffer_create(struct wl_client *client, struct wl_resource *resource, uint32_t id, uint32_t r, uint32_t g, uint32_t b, uint32_t a) { - struct solid_buffer_values *solid = zalloc(sizeof(*solid)); + struct weston_solid_buffer_values *solid = zalloc(sizeof(*solid)); struct wl_resource *buffer; if (!solid) { From 6744a6278ee1a827a17c3fe853a40713800b3bd8 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Tue, 26 Jul 2022 12:49:57 +0300 Subject: [PATCH 515/609] clients/window: Bump xdg-shell version to latest It seems we've missed an update from 3 to 4 (bounds events). With it, this updates to version 5 which sends the capabilities event. Stubs, as we're not using them. Signed-off-by: Marius Vlad --- clients/window.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/clients/window.c b/clients/window.c index 8073fbc6..81131f90 100644 --- a/clients/window.c +++ b/clients/window.c @@ -4206,9 +4206,24 @@ xdg_toplevel_handle_close(void *data, struct xdg_toplevel *xdg_surface) window_close(window); } +static void +xdg_toplevel_handle_configure_bounds(void *data, struct xdg_toplevel *xdg_toplevel, + int32_t width, int32_t height) +{ +} + + +static void +xdg_toplevel_handle_wm_capabilities(void *data, struct xdg_toplevel *xdg_toplevel, + struct wl_array *caps) +{ +} + static const struct xdg_toplevel_listener xdg_toplevel_listener = { xdg_toplevel_handle_configure, xdg_toplevel_handle_close, + xdg_toplevel_handle_configure_bounds, + xdg_toplevel_handle_wm_capabilities, }; static void @@ -5849,7 +5864,8 @@ registry_handle_global(void *data, struct wl_registry *registry, uint32_t id, display_add_data_device(d, id, version); } else if (strcmp(interface, "xdg_wm_base") == 0) { d->xdg_shell = wl_registry_bind(registry, id, - &xdg_wm_base_interface, 1); + &xdg_wm_base_interface, + MIN(version, 5)); xdg_wm_base_add_listener(d->xdg_shell, &wm_base_listener, d); } else if (strcmp(interface, "text_cursor_position") == 0) { d->text_cursor_position = From 78ccc99d0af542b389855fd60abeef18d786dc59 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Mon, 18 Jul 2022 23:03:20 +0300 Subject: [PATCH 516/609] libweston: Remove runtime render switching It is only enabled by a debug key binding, currently not tested at all, and is seems it doesn't really work, so let's remove it. This also removes it from the man page. Signed-off-by: Marius Vlad --- libweston/backend-drm/drm-gbm.c | 66 --------------------------- libweston/backend-drm/drm-internal.h | 10 ---- libweston/backend-drm/drm.c | 2 - libweston/backend-drm/state-propose.c | 9 +--- libweston/renderer-gl/gl-renderer.c | 3 -- man/weston-bindings.man | 6 +-- 6 files changed, 2 insertions(+), 94 deletions(-) diff --git a/libweston/backend-drm/drm-gbm.c b/libweston/backend-drm/drm-gbm.c index c8c84fd1..76fa79f8 100644 --- a/libweston/backend-drm/drm-gbm.c +++ b/libweston/backend-drm/drm-gbm.c @@ -315,69 +315,3 @@ drm_output_render_gl(struct drm_output_state *state, pixman_region32_t *damage) return ret; } - -static void -switch_to_gl_renderer(struct drm_backend *b) -{ - struct drm_device *device = b->drm; - struct drm_output *output; - bool dmabuf_support_inited; - bool linux_explicit_sync_inited; - - if (!b->use_pixman) - return; - - dmabuf_support_inited = !!b->compositor->renderer->import_dmabuf; - linux_explicit_sync_inited = - b->compositor->capabilities & WESTON_CAP_EXPLICIT_SYNC; - - weston_log("Switching to GL renderer\n"); - - b->gbm = create_gbm_device(device->drm.fd); - if (!b->gbm) { - weston_log("Failed to create gbm device. " - "Aborting renderer switch\n"); - return; - } - - wl_list_for_each(output, &b->compositor->output_list, base.link) - pixman_renderer_output_destroy(&output->base); - - b->compositor->renderer->destroy(b->compositor); - - if (drm_backend_create_gl_renderer(b) < 0) { - gbm_device_destroy(b->gbm); - weston_log("Failed to create GL renderer. Quitting.\n"); - /* FIXME: we need a function to shutdown cleanly */ - assert(0); - } - - wl_list_for_each(output, &b->compositor->output_list, base.link) - drm_output_init_egl(output, b); - - b->use_pixman = 0; - - if (!dmabuf_support_inited && b->compositor->renderer->import_dmabuf) { - if (linux_dmabuf_setup(b->compositor) < 0) - weston_log("Error: initializing dmabuf " - "support failed.\n"); - } - - if (!linux_explicit_sync_inited && - (b->compositor->capabilities & WESTON_CAP_EXPLICIT_SYNC)) { - if (linux_explicit_synchronization_setup(b->compositor) < 0) - weston_log("Error: initializing explicit " - " synchronization support failed.\n"); - } -} - -void -renderer_switch_binding(struct weston_keyboard *keyboard, - const struct timespec *time, uint32_t key, void *data) -{ - struct drm_backend *b = - to_drm_backend(keyboard->seat->compositor); - - switch_to_gl_renderer(b); -} - diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index 3fc5f9b9..5bf825a8 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -867,9 +867,6 @@ drm_output_fini_egl(struct drm_output *output); struct drm_fb * drm_output_render_gl(struct drm_output_state *state, pixman_region32_t *damage); -void -renderer_switch_binding(struct weston_keyboard *keyboard, - const struct timespec *time, uint32_t key, void *data); #else inline static int init_egl(struct drm_backend *b) @@ -894,11 +891,4 @@ drm_output_render_gl(struct drm_output_state *state, pixman_region32_t *damage) { return NULL; } - -inline static void -renderer_switch_binding(struct weston_keyboard *keyboard, - const struct timespec *time, uint32_t key, void *data) -{ - weston_log("Compiled without GBM/EGL support\n"); -} #endif diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index 96f0166f..c8bec7e4 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -3239,8 +3239,6 @@ drm_backend_create(struct weston_compositor *compositor, planes_binding, b); weston_compositor_add_debug_binding(compositor, KEY_Q, recorder_binding, b); - weston_compositor_add_debug_binding(compositor, KEY_W, - renderer_switch_binding, b); if (compositor->renderer->import_dmabuf) { if (linux_dmabuf_setup(compositor) < 0) diff --git a/libweston/backend-drm/state-propose.c b/libweston/backend-drm/state-propose.c index a6adfb35..967b6bd0 100644 --- a/libweston/backend-drm/state-propose.c +++ b/libweston/backend-drm/state-propose.c @@ -979,14 +979,7 @@ drm_assign_planes(struct weston_output *output_base) pnode->try_view_on_plane_failure_reasons = FAILURE_REASONS_NONE; /* Test whether this buffer can ever go into a plane: - * non-shm, or small enough to be a cursor. - * - * FIXME: Also, we should keep a reference when using the - * pixman renderer. That makes it possible to do a seamless - * switch to the GL renderer and since the pixman renderer - * keeps a reference to the buffer anyway, there is no side - * effects. - */ + * non-shm, or small enough to be a cursor. */ ev->surface->keep_buffer = false; if (weston_view_has_valid_buffer(ev)) { struct weston_buffer *buffer = diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 5f1cfab5..5419c191 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -1011,9 +1011,6 @@ draw_paint_node(struct weston_paint_node *pnode, GLint filter; struct gl_shader_config sconf; - /* In case of a runtime switch of renderers, we may not have received - * an attach for this surface since the switch. In that case we don't - * have a valid buffer or a proper shader set up so skip rendering. */ if (gb->shader_variant == SHADER_VARIANT_NONE && !buffer->direct_display) return; diff --git a/man/weston-bindings.man b/man/weston-bindings.man index 1cd39f7d..398bec25 100644 --- a/man/weston-bindings.man +++ b/man/weston-bindings.man @@ -130,10 +130,6 @@ Enable/Disable overlay planes. .RS 4 Start VAAPI recorder. .RE -- KEY_W : -.RS 4 -Switch to gl-renderer from pixman. -.RE - KEY_S : .RS 4 Enable fragment debugging for gl-renderer. @@ -144,7 +140,7 @@ Enable fan debugging for gl-renderer. .RE - KEY_R : .RS 4 -Enable repaint debugging for pixman: +Enable repaint debugging for Pixman. .RE .RE From 0972c6b2da02271c5bbfe3970fd06e2b8040aa7b Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Thu, 14 Jul 2022 13:24:18 -0500 Subject: [PATCH 517/609] compositor: Remove deprecated xwayland loading method This is awkward and long deprecated, and makes us load xwayland after all the other modules so we know if we have to load it or not. Let's remove it. We do still need to prevent loading the module the wrong way, though. Signed-off-by: Derek Foreman --- compositor/main.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/compositor/main.c b/compositor/main.c index 280b565e..1e310c02 100644 --- a/compositor/main.c +++ b/compositor/main.c @@ -989,7 +989,7 @@ wet_get_bindir_path(const char *name) static int load_modules(struct weston_compositor *ec, const char *modules, - int *argc, char *argv[], bool *xwayland) + int *argc, char *argv[]) { const char *p, *end; char buffer[256]; @@ -1003,11 +1003,11 @@ load_modules(struct weston_compositor *ec, const char *modules, snprintf(buffer, sizeof buffer, "%.*s", (int) (end - p), p); if (strstr(buffer, "xwayland.so")) { - weston_log("Old Xwayland module loading detected: " + weston_log("fatal: Old Xwayland module loading detected: " "Please use --xwayland command line option " "or set xwayland=true in the [core] section " "in weston.ini\n"); - *xwayland = true; + return -1; } else { if (wet_load_module(ec, buffer, argc, argv) < 0) return -1; @@ -3730,10 +3730,10 @@ wet_main(int argc, char *argv[], const struct weston_testsuite_data *test_data) goto out; weston_config_section_get_string(section, "modules", &modules, ""); - if (load_modules(wet.compositor, modules, &argc, argv, &xwayland) < 0) + if (load_modules(wet.compositor, modules, &argc, argv) < 0) goto out; - if (load_modules(wet.compositor, option_modules, &argc, argv, &xwayland) < 0) + if (load_modules(wet.compositor, option_modules, &argc, argv) < 0) goto out; if (!xwayland) { From 2badd284a59cb025c0ae5c3ec3757310bb04f7d7 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Thu, 14 Jul 2022 13:29:42 -0500 Subject: [PATCH 518/609] compositor: Load xwayland module first This is so the systemd-notify module, if used, will notify readiness after we're ready to accept X connections, instead of before. Signed-off-by: Derek Foreman --- compositor/main.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/compositor/main.c b/compositor/main.c index 1e310c02..c388d861 100644 --- a/compositor/main.c +++ b/compositor/main.c @@ -3729,13 +3729,10 @@ wet_main(int argc, char *argv[], const struct weston_testsuite_data *test_data) if (wet_load_shell(wet.compositor, shell, &argc, argv) < 0) goto out; - weston_config_section_get_string(section, "modules", &modules, ""); - if (load_modules(wet.compositor, modules, &argc, argv) < 0) - goto out; - - if (load_modules(wet.compositor, option_modules, &argc, argv) < 0) - goto out; - + /* Load xwayland before other modules - this way if we're using + * the systemd-notify module it will notify after we're ready + * to receive xwayland connections. + */ if (!xwayland) { weston_config_section_get_bool(section, "xwayland", &xwayland, false); @@ -3745,6 +3742,13 @@ wet_main(int argc, char *argv[], const struct weston_testsuite_data *test_data) goto out; } + weston_config_section_get_string(section, "modules", &modules, ""); + if (load_modules(wet.compositor, modules, &argc, argv) < 0) + goto out; + + if (load_modules(wet.compositor, option_modules, &argc, argv) < 0) + goto out; + section = weston_config_get_section(config, "keyboard", NULL, NULL); weston_config_section_get_bool(section, "numlock-on", &numlock_on, false); if (numlock_on) { From 27d2a4cfabde5fe17ba09f2083642b43e1ececa0 Mon Sep 17 00:00:00 2001 From: Michael Olbrich Date: Wed, 6 Jul 2022 13:35:12 +0200 Subject: [PATCH 519/609] libweston: don't reset the plane for views from other outputs The paint_node_z_order_list contains all views, not just the ones visible on the current output. So all views are moved to the primary plane when one output does not support planes. This will be relevant with multiple backends: When an output without plane support is rendered then the views of all other outputs are removed from the current planes and the corresponding outputs will be repainted unnecessarily. So only reset the plane if the view is actually on the current output. Signed-off-by: Michael Olbrich --- libweston/compositor.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libweston/compositor.c b/libweston/compositor.c index 2285e3f1..abe75bff 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -3254,6 +3254,10 @@ weston_output_repaint(struct weston_output *output) } else { wl_list_for_each(pnode, &output->paint_node_z_order_list, z_order_link) { + /* TODO: turn this into assert once z_order_list is pruned. */ + if ((pnode->view->output_mask & (1u << output->id)) == 0) + continue; + weston_view_move_to_plane(pnode->view, &ec->primary_plane); pnode->view->psf_flags = 0; } From 4564a40cb0da6277e8c8f037a75eea2cb53a56fe Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Tue, 12 Jul 2022 09:39:05 -0500 Subject: [PATCH 520/609] rdp: Move peer list from output to backend In the future we'll have multiple output support, which makes storing the peer list on an output rather tricky. Signed-off-by: Derek Foreman --- libweston/backend-rdp/rdp.c | 21 +++++++++++---------- libweston/backend-rdp/rdp.h | 4 ++-- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/libweston/backend-rdp/rdp.c b/libweston/backend-rdp/rdp.c index b0dd2fc4..894ae5d2 100644 --- a/libweston/backend-rdp/rdp.c +++ b/libweston/backend-rdp/rdp.c @@ -253,7 +253,8 @@ rdp_output_repaint(struct weston_output *output_base, pixman_region32_t *damage) { struct rdp_output *output = container_of(output_base, struct rdp_output, base); struct weston_compositor *ec = output->base.compositor; - struct rdp_peers_item *outputPeer; + struct rdp_backend *b = to_rdp_backend(ec); + struct rdp_peers_item *peer; struct timespec now, target; int refresh_nsec = millihz_to_nsec(output_base->current_mode->refresh); int refresh_msec = refresh_nsec / 1000000; @@ -279,11 +280,11 @@ rdp_output_repaint(struct weston_output *output_base, pixman_region32_t *damage) ec->renderer->repaint_output(&output->base, damage); if (pixman_region32_not_empty(damage)) { - wl_list_for_each(outputPeer, &output->peers, link) { - if ((outputPeer->flags & RDP_PEER_ACTIVATED) && - (outputPeer->flags & RDP_PEER_OUTPUT_ENABLED)) + wl_list_for_each(peer, &b->peers, link) { + if ((peer->flags & RDP_PEER_ACTIVATED) && + (peer->flags & RDP_PEER_OUTPUT_ENABLED)) { - rdp_peer_refresh_region(damage, outputPeer->peer); + rdp_peer_refresh_region(damage, peer->peer); } } } @@ -372,7 +373,7 @@ rdp_switch_mode(struct weston_output *output, struct weston_mode *target_mode) pixman_image_unref(rdpOutput->shadow_surface); rdpOutput->shadow_surface = new_shadow_buffer; - wl_list_for_each(rdpPeer, &rdpOutput->peers, link) { + wl_list_for_each(rdpPeer, &rdpBackend->peers, link) { settings = rdpPeer->peer->context->settings; if (settings->DesktopWidth == (UINT32)target_mode->width && settings->DesktopHeight == (UINT32)target_mode->height) @@ -413,8 +414,6 @@ rdp_output_set_size(struct weston_output *base, weston_head_set_physical_size(head, 0, 0); } - wl_list_init(&output->peers); - initMode.flags = WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED; initMode.width = width; initMode.height = height; @@ -566,7 +565,7 @@ rdp_destroy(struct weston_compositor *ec) struct rdp_peers_item *rdp_peer, *tmp; int i; - wl_list_for_each_safe(rdp_peer, tmp, &b->output->peers, link) { + wl_list_for_each_safe(rdp_peer, tmp, &b->peers, link) { freerdp_peer* client = rdp_peer->peer; client->Disconnect(client); @@ -1581,7 +1580,7 @@ rdp_peer_init(freerdp_peer *client, struct rdp_backend *b) for ( ; i < (int)ARRAY_LENGTH(peerCtx->events); i++) peerCtx->events[i] = 0; - wl_list_insert(&b->output->peers, &peerCtx->item.link); + wl_list_insert(&b->peers, &peerCtx->item.link); if (!rdp_initialize_dispatch_task_event_source(peerCtx)) goto error_dispatch_initialize; @@ -1698,6 +1697,8 @@ rdp_backend_create(struct weston_compositor *compositor, } } + wl_list_init(&b->peers); + if (weston_compositor_set_presentation_clock_software(compositor) < 0) goto err_compositor; diff --git a/libweston/backend-rdp/rdp.h b/libweston/backend-rdp/rdp.h index d5e13057..210ddab4 100644 --- a/libweston/backend-rdp/rdp.h +++ b/libweston/backend-rdp/rdp.h @@ -82,6 +82,8 @@ struct rdp_backend { struct weston_log_scope *clipboard_debug; struct weston_log_scope *clipboard_verbose; + struct wl_list peers; + char *server_cert; char *server_key; char *rdp_key; @@ -120,8 +122,6 @@ struct rdp_output { struct weston_output base; struct wl_event_source *finish_frame_timer; pixman_image_t *shadow_surface; - - struct wl_list peers; }; struct rdp_peer_context { From 5ffa1962a55706c23afa5fad1f5b92891e1ea4b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Mon, 22 Mar 2021 14:39:55 +0100 Subject: [PATCH 521/609] compositor: Add support for wl_surface.offset() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows for setting a buffer offset without having to make it part of the wl_surface.attach request. This is useful for e.g. setting a DND surface icon hotspot offset when using Vulkan; or doing the same with EGL without having to use wl_egl_window_resize(). Signed-off-by: Jonas Ådahl --- include/libweston/libweston.h | 1 + libweston/compositor.c | 33 ++++++++++++++++++++++++++++----- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index a36e4d7a..0f5d9ca0 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -1538,6 +1538,7 @@ struct weston_surface_state { int newly_attached; struct weston_buffer *buffer; struct wl_listener buffer_destroy_listener; + int32_t sx; int32_t sy; diff --git a/libweston/compositor.c b/libweston/compositor.c index abe75bff..89fe410e 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -2284,8 +2284,6 @@ static void weston_surface_reset_pending_buffer(struct weston_surface *surface) { weston_surface_state_set_buffer(&surface->pending, NULL); - surface->pending.sx = 0; - surface->pending.sy = 0; surface->pending.newly_attached = 0; surface->pending.buffer_viewport.changed = 0; } @@ -3796,6 +3794,14 @@ surface_attach(struct wl_client *client, } } + if (wl_resource_get_version(resource) >= WL_SURFACE_OFFSET_SINCE_VERSION && + (sx != 0 || sy != 0)) { + wl_resource_post_error(resource, + WL_SURFACE_ERROR_INVALID_OFFSET, + "Can't attach with an offset"); + return; + } + /* Attach, attach, without commit in between does not send * wl_buffer.release. */ weston_surface_state_set_buffer(&surface->pending, buffer); @@ -4184,7 +4190,8 @@ weston_surface_commit_state(struct weston_surface *surface, weston_matrix_invert(&surface->buffer_to_surface_matrix, &surface->surface_to_buffer_matrix); - if (state->newly_attached || state->buffer_viewport.changed) { + if (state->newly_attached || state->buffer_viewport.changed || + state->sx != 0 || state->sy != 0) { weston_surface_update_size(surface); if (surface->committed) surface->committed(surface, state->sx, state->sy); @@ -4380,6 +4387,18 @@ surface_set_buffer_scale(struct wl_client *client, surface->pending.buffer_viewport.changed = 1; } +static void +surface_offset(struct wl_client *client, + struct wl_resource *resource, + int32_t sx, + int32_t sy) +{ + struct weston_surface *surface = wl_resource_get_user_data(resource); + + surface->pending.sx = sx; + surface->pending.sy = sy; +} + static const struct wl_surface_interface surface_interface = { surface_destroy, surface_attach, @@ -4390,7 +4409,8 @@ static const struct wl_surface_interface surface_interface = { surface_commit, surface_set_buffer_transform, surface_set_buffer_scale, - surface_damage_buffer + surface_damage_buffer, + surface_offset, }; static void @@ -4568,6 +4588,9 @@ weston_subsurface_commit_to_cache(struct weston_subsurface *sub) weston_surface_reset_pending_buffer(surface); + surface->pending.sx = 0; + surface->pending.sy = 0; + pixman_region32_copy(&sub->cached.opaque, &surface->pending.opaque); pixman_region32_copy(&sub->cached.input, &surface->pending.input); @@ -8327,7 +8350,7 @@ weston_compositor_create(struct wl_display *display, ec->content_protection = NULL; - if (!wl_global_create(ec->wl_display, &wl_compositor_interface, 4, + if (!wl_global_create(ec->wl_display, &wl_compositor_interface, 5, ec, compositor_bind)) goto fail; From 8a610ffe414e815dc6453b47d6f46e0195462d00 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Fri, 15 Jul 2022 11:23:26 +0300 Subject: [PATCH 522/609] compositor/text-backend: Avoid a potential UAF The text_input_manager might be destroyed upon a compositor shutdown, so verify if it's still set-up before attemping to use it to avoid a UAF. Signed-off-by: Marius Vlad --- compositor/text-backend.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/compositor/text-backend.c b/compositor/text-backend.c index 94955bff..9e787650 100644 --- a/compositor/text-backend.c +++ b/compositor/text-backend.c @@ -141,6 +141,12 @@ deactivate_input_method(struct input_method *input_method) input_method->input = NULL; input_method->context = NULL; + /* text_input_manager::destroy_listener by compositor shutdown */ + if (!text_input->manager) { + zwp_text_input_v1_send_leave(text_input->resource); + return; + } + if (wl_list_empty(&text_input->input_methods) && text_input->input_panel_visible && text_input->manager->current_text_input == text_input) { @@ -456,6 +462,8 @@ text_input_manager_notifier_destroy(struct wl_listener *listener, void *data) wl_list_remove(&text_input_manager->destroy_listener.link); wl_global_destroy(text_input_manager->text_input_manager_global); + if (text_input_manager->current_text_input) + text_input_manager->current_text_input->manager = NULL; free(text_input_manager); } From d6b112c85741d7a9ac3de4b022573307ed130e73 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Thu, 14 Jul 2022 15:19:50 -0500 Subject: [PATCH 523/609] xwayland: Only prevent focus change to inactive toplevels Commit b18f788e2e76 broke motif applications by ensuring they could never focus their menus - since then any attempt by an application to focus any window would be met by the window manager immediately refocusing the currently active toplevel window. Later we loosened the restriction in 9e07d25a1b to allow clients that received focus from a grab to do so - but motif applications like nedit don't set focus in this way, and remain broken. This patch further loosens our restrictions, now only reverting a focus change to an inactive top level. This will hopefully prevent any confusing input routing without breaking reasonable clients. This restores functionality to motif menus. Fixes #636 Fixes b18f788e2e74 Signed-off-by: Derek Foreman --- xwayland/window-manager.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/xwayland/window-manager.c b/xwayland/window-manager.c index 73453e0b..c39e6a02 100644 --- a/xwayland/window-manager.c +++ b/xwayland/window-manager.c @@ -2314,6 +2314,7 @@ weston_wm_handle_leave(struct weston_wm *wm, xcb_generic_event_t *event) static void weston_wm_handle_focus_in(struct weston_wm *wm, xcb_generic_event_t *event) { + struct weston_wm_window *window; xcb_focus_in_event_t *focus = (xcb_focus_in_event_t *) event; /* Do not interfere with grabs */ @@ -2321,6 +2322,17 @@ weston_wm_handle_focus_in(struct weston_wm *wm, xcb_generic_event_t *event) focus->mode == XCB_NOTIFY_MODE_UNGRAB) return; + if (!wm_lookup_window(wm, focus->event, &window)) + return; + + /* Sometimes apps like to focus their own windows, and we don't + * want to prevent that - but we'd like to at least prevent any + * attempt to focus a toplevel that isn't the currently activated + * toplevel. + */ + if (!window->frame) + return; + /* Do not let X clients change the focus behind the compositor's * back. Reset the focus to the old one if it changed. */ if (!wm->focus_window || focus->event != wm->focus_window->id) From 7323ddec6273ce89c2031ee1ff9e55e1e3d7b41a Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Wed, 20 Jul 2022 14:44:31 +0300 Subject: [PATCH 524/609] pixman-renderer: let pixman allocate shadow There is no need for us to allocate the storage manually, Pixman can do that for us. Signed-off-by: Pekka Paalanen --- libweston/pixman-renderer.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/libweston/pixman-renderer.c b/libweston/pixman-renderer.c index c1ed560f..f9ccb230 100644 --- a/libweston/pixman-renderer.c +++ b/libweston/pixman-renderer.c @@ -41,7 +41,6 @@ #include struct pixman_output_state { - void *shadow_buffer; pixman_image_t *shadow_image; pixman_image_t *hw_buffer; pixman_region32_t *hw_extra_damage; @@ -951,19 +950,11 @@ pixman_renderer_output_create(struct weston_output *output, w = output->current_mode->width; h = output->current_mode->height; - po->shadow_buffer = malloc(w * h * 4); - - if (!po->shadow_buffer) { - free(po); - return -1; - } - po->shadow_image = - pixman_image_create_bits(PIXMAN_x8r8g8b8, w, h, - po->shadow_buffer, w * 4); + pixman_image_create_bits_no_clear(PIXMAN_x8r8g8b8, + w, h, NULL, 0); if (!po->shadow_image) { - free(po->shadow_buffer); free(po); return -1; } @@ -985,9 +976,6 @@ pixman_renderer_output_destroy(struct weston_output *output) if (po->hw_buffer) pixman_image_unref(po->hw_buffer); - free(po->shadow_buffer); - - po->shadow_buffer = NULL; po->shadow_image = NULL; po->hw_buffer = NULL; From dd706d5953c8fc4d7f872d292bc9e00f415615bd Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Wed, 20 Jul 2022 14:47:46 +0300 Subject: [PATCH 525/609] backend-headless: let pixman allocate the image No need to manually allocate the storage, Pixman can do that. Signed-off-by: Pekka Paalanen --- libweston/backend-headless/headless.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/libweston/backend-headless/headless.c b/libweston/backend-headless/headless.c index bf8309f6..7ea869d0 100644 --- a/libweston/backend-headless/headless.c +++ b/libweston/backend-headless/headless.c @@ -70,7 +70,6 @@ struct headless_output { struct weston_mode mode; struct wl_event_source *finish_frame_timer; - uint32_t *image_buf; pixman_image_t *image; }; @@ -165,7 +164,6 @@ headless_output_disable_pixman(struct headless_output *output) { pixman_renderer_output_destroy(&output->base); pixman_image_unref(output->image); - free(output->image_buf); } static int @@ -237,17 +235,14 @@ headless_output_enable_pixman(struct headless_output *output) .use_shadow = true, }; - output->image_buf = malloc(output->base.current_mode->width * - output->base.current_mode->height * 4); - if (!output->image_buf) + output->image = + pixman_image_create_bits_no_clear(PIXMAN_x8r8g8b8, + output->base.current_mode->width, + output->base.current_mode->height, + NULL, 0); + if (!output->image) return -1; - output->image = pixman_image_create_bits(PIXMAN_x8r8g8b8, - output->base.current_mode->width, - output->base.current_mode->height, - output->image_buf, - output->base.current_mode->width * 4); - if (pixman_renderer_output_create(&output->base, &options) < 0) goto err_renderer; @@ -257,7 +252,6 @@ headless_output_enable_pixman(struct headless_output *output) err_renderer: pixman_image_unref(output->image); - free(output->image_buf); return -1; } From 8b6c3fe0ad4bce6c93dad4875a089ba8e7c6ce58 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Wed, 20 Jul 2022 14:52:41 +0300 Subject: [PATCH 526/609] backend-headless: choose pixel format by drm_fourcc Pixman image formats are CPU-endianess dependent while drm_fourcc are not. Standardise around drm_fourcc because DRM-backend uses them anyway. This also makes Pixman-renderer use the same format as GL-renderer will prefer on headless. Signed-off-by: Pekka Paalanen --- libweston/backend-headless/headless.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/libweston/backend-headless/headless.c b/libweston/backend-headless/headless.c index 7ea869d0..83de39dc 100644 --- a/libweston/backend-headless/headless.c +++ b/libweston/backend-headless/headless.c @@ -37,6 +37,7 @@ #include #include "shared/helpers.h" #include "linux-explicit-synchronization.h" +#include "pixel-formats.h" #include "pixman-renderer.h" #include "renderer-gl/gl-renderer.h" #include "shared/weston-drm-fourcc.h" @@ -74,7 +75,7 @@ struct headless_output { }; static const uint32_t headless_formats[] = { - DRM_FORMAT_XRGB8888, + DRM_FORMAT_XRGB8888, /* default for pixman-renderer */ DRM_FORMAT_ARGB8888, }; @@ -231,12 +232,15 @@ headless_output_enable_gl(struct headless_output *output) static int headless_output_enable_pixman(struct headless_output *output) { + const struct pixel_format_info *pfmt; const struct pixman_renderer_output_options options = { .use_shadow = true, }; + pfmt = pixel_format_get_info(headless_formats[0]); + output->image = - pixman_image_create_bits_no_clear(PIXMAN_x8r8g8b8, + pixman_image_create_bits_no_clear(pfmt->pixman_format, output->base.current_mode->width, output->base.current_mode->height, NULL, 0); From 9e1c96bce79e6b37a45cde1d2f01c3e238963fcf Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 26 Jul 2022 12:37:20 +0300 Subject: [PATCH 527/609] backend-wayland: restructure wayland_output_resize_surface() A following patch is going to need the introduced 'area' and 'fb_size' variables. Until then though, a little hack is needed to avoid no-gl builds failing with error: variable 'fb_size' set but not used. While starting to use struct weston_geometry, convert also the input and opaque regions to use it. This shortens and simplifies the code, as we can drop the roughly duplicate code of doing stuff for with vs. without a frame. No change in behavior, this is pure refactoring. Signed-off-by: Pekka Paalanen --- libweston/backend-wayland/wayland.c | 86 +++++++++++++---------------- 1 file changed, 38 insertions(+), 48 deletions(-) diff --git a/libweston/backend-wayland/wayland.c b/libweston/backend-wayland/wayland.c index 0759ee53..9272b414 100644 --- a/libweston/backend-wayland/wayland.c +++ b/libweston/backend-wayland/wayland.c @@ -831,64 +831,54 @@ wayland_output_init_pixman_renderer(struct wayland_output *output) static void wayland_output_resize_surface(struct wayland_output *output) { - struct wayland_backend *b = - to_wayland_backend(output->base.compositor); - int32_t ix, iy, iwidth, iheight; - int32_t width, height; + struct wayland_backend *b = to_wayland_backend(output->base.compositor); + /* Defaults for without frame: */ + struct weston_size fb_size = { + .width = output->base.current_mode->width, + .height = output->base.current_mode->height + }; + struct weston_geometry area = { + .x = 0, + .y = 0, + .width = fb_size.width, + .height = fb_size.height + }; + struct weston_geometry inp = area; + struct weston_geometry opa = area; struct wl_region *region; - width = output->base.current_mode->width; - height = output->base.current_mode->height; - if (output->frame) { - frame_resize_inside(output->frame, width, height); - - frame_input_rect(output->frame, &ix, &iy, &iwidth, &iheight); - region = wl_compositor_create_region(b->parent.compositor); - wl_region_add(region, ix, iy, iwidth, iheight); - wl_surface_set_input_region(output->parent.surface, region); - wl_region_destroy(region); - - if (output->parent.xdg_surface) { - xdg_surface_set_window_geometry(output->parent.xdg_surface, - ix, - iy, - iwidth, - iheight); - } + frame_resize_inside(output->frame, area.width, area.height); + frame_interior(output->frame, &area.x, &area.y, NULL, NULL); + fb_size.width = frame_width(output->frame); + fb_size.height = frame_height(output->frame); - frame_opaque_rect(output->frame, &ix, &iy, &iwidth, &iheight); - region = wl_compositor_create_region(b->parent.compositor); - wl_region_add(region, ix, iy, iwidth, iheight); - wl_surface_set_opaque_region(output->parent.surface, region); - wl_region_destroy(region); + frame_input_rect(output->frame, &inp.x, &inp.y, + &inp.width, &inp.height); + frame_opaque_rect(output->frame, &opa.x, &opa.y, + &opa.width, &opa.height); + } - width = frame_width(output->frame); - height = frame_height(output->frame); - } else { - region = wl_compositor_create_region(b->parent.compositor); - wl_region_add(region, 0, 0, width, height); - wl_surface_set_input_region(output->parent.surface, region); - wl_region_destroy(region); - - region = wl_compositor_create_region(b->parent.compositor); - wl_region_add(region, 0, 0, width, height); - wl_surface_set_opaque_region(output->parent.surface, region); - wl_region_destroy(region); - - if (output->parent.xdg_surface) { - xdg_surface_set_window_geometry(output->parent.xdg_surface, - 0, - 0, - width, - height); - } + region = wl_compositor_create_region(b->parent.compositor); + wl_region_add(region, inp.x, inp.y, inp.width, inp.height); + wl_surface_set_input_region(output->parent.surface, region); + wl_region_destroy(region); + + if (output->parent.xdg_surface) { + xdg_surface_set_window_geometry(output->parent.xdg_surface, + inp.x, inp.y, + inp.width, inp.height); } + region = wl_compositor_create_region(b->parent.compositor); + wl_region_add(region, opa.x, opa.y, opa.width, opa.height); + wl_surface_set_opaque_region(output->parent.surface, region); + wl_region_destroy(region); + #ifdef ENABLE_EGL if (output->gl.egl_window) { wl_egl_window_resize(output->gl.egl_window, - width, height, 0, 0); + fb_size.width, fb_size.height, 0, 0); /* These will need to be re-created due to the resize */ gl_renderer->output_set_border(&output->base, From aac8eefc447c90c2cb179a1f7b2d1ed049f466ab Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 26 Jul 2022 17:05:05 +0300 Subject: [PATCH 528/609] backend-x11: use shorthand for current_mode Pure refactoring to reduce statement lengths. Signed-off-by: Pekka Paalanen --- libweston/backend-x11/x11.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/libweston/backend-x11/x11.c b/libweston/backend-x11/x11.c index bd9d641e..f887961e 100644 --- a/libweston/backend-x11/x11.c +++ b/libweston/backend-x11/x11.c @@ -945,6 +945,7 @@ static int x11_output_enable(struct weston_output *base) { struct x11_output *output = to_x11_output(base); + const struct weston_mode *mode = output->base.current_mode; struct x11_backend *b; assert(output); @@ -988,8 +989,7 @@ x11_output_enable(struct weston_output *base) output->window, screen->root, 0, 0, - output->base.current_mode->width, - output->base.current_mode->height, + mode->width, mode->height, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual, @@ -1054,8 +1054,7 @@ x11_output_enable(struct weston_output *base) .use_shadow = true, }; if (x11_output_init_shm(b, output, - output->base.current_mode->width, - output->base.current_mode->height) < 0) { + mode->width, mode->height) < 0) { weston_log("Failed to initialize SHM for the X11 output\n"); goto err; } @@ -1096,9 +1095,7 @@ x11_output_enable(struct weston_output *base) wl_event_loop_add_timer(loop, finish_frame_handler, output); weston_log("x11 output %dx%d, window id %d\n", - output->base.current_mode->width, - output->base.current_mode->height, - output->window); + mode->width, mode->height, output->window); return 0; From f9e52eb0d759468a31ca131380d4db8de815f8d1 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Mon, 9 Dec 2019 16:36:47 +0200 Subject: [PATCH 529/609] desktop-shell: Always update the shsurf's output to that of the view In case shsurfs are migrated/moved or started on different outputs other than the default one, it causes fullscreen views to never being demoted to a lower stacking level, due to the fact we never update the view's output whenever that has changed. Synchronize the desktop shell output's with the view's output in the transform_handler. Signed-off-by: Marius Vlad Suggested-by: Daniel Stone --- desktop-shell/shell.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 9ebc9550..d1a2035d 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -3713,6 +3713,8 @@ transform_handler(struct wl_listener *listener, void *data) if (!shsurf) return; + shell_surface_set_output(shsurf, shsurf->view->output); + api = shsurf->shell->xwayland_surface_api; if (!api) { api = weston_xwayland_surface_get_api(shsurf->shell->compositor); From 3387afd56b8a25be4dd411ebd19ccb837b039b2e Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Tue, 2 Aug 2022 11:04:43 +0300 Subject: [PATCH 530/609] fullscreen-shell: fix black output Fullscreen-shell forgot to mark the weston_surface as mapped when mapping the surface and view. With f962b4895891a495e147d795cc47a7af9ca441f6 that means no surface from fullscreen-shell clients is eveer shown. Most notably this broke screen-share plugin, which is maybe the only "real" user of fullscreen-shell. Fix this oversight. Now screen-share works again with RDP-backend. Fixes: f962b4895891a495e147d795cc47a7af9ca441f6 "compositor: Only create paint nodes for mapped surfaces/views" (currently unreleased) Signed-off-by: Pekka Paalanen --- fullscreen-shell/fullscreen-shell.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fullscreen-shell/fullscreen-shell.c b/fullscreen-shell/fullscreen-shell.c index 009ad1e3..7ee09d79 100644 --- a/fullscreen-shell/fullscreen-shell.c +++ b/fullscreen-shell/fullscreen-shell.c @@ -625,6 +625,7 @@ fs_output_apply_pending(struct fs_output *fsout) return; } fsout->view->is_mapped = true; + fsout->surface->is_mapped = true; wl_signal_add(&fsout->surface->destroy_signal, &fsout->surface_destroyed); From 0aac3dd343dd08ef42583bce361240857f3126c0 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Thu, 28 Jul 2022 12:52:11 -0500 Subject: [PATCH 531/609] xwm: Don't send synthetic ConfigureNotify to windows that were mapped O-R It's entirely possible, if ridiculous, for an X11 client to change a window's override redirect flag while it's mapped. If this changes from true to false we will start receiving Configure requests for the window. That leads us to a crash when we try to query the window's current position from the shell to send a configure notify event, as the shell doesn't know about the surface. Instead of trying to cleverly handle this, mostly go back to the behaviour these clients would've seen before commit cf5aca5a and don't send them a synthetic configure notify. We also specifically check in weston_wm_handle_configure_request for the same condition, and early return there, bypassing a couple of other things we would've done previously. Signed-off-by: Derek Foreman --- xwayland/window-manager.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/xwayland/window-manager.c b/xwayland/window-manager.c index c39e6a02..93a3d6d8 100644 --- a/xwayland/window-manager.c +++ b/xwayland/window-manager.c @@ -683,6 +683,17 @@ weston_wm_window_send_configure_notify(struct weston_wm_window *window) const struct weston_desktop_xwayland_interface *xwayland_api = wm->server->compositor->xwayland_interface; + if (window->override_redirect) { + /* Some clever application has changed the override redirect + * flag on an existing window. We didn't see it at map time, + * so have no idea what to do with it now. Log and leave. + */ + wm_printf(wm, "XWM warning: Can't send XCB_CONFIGURE_NOTIFY to" + " window %d which was mapped override redirect\n", + window->id); + return; + } + weston_wm_window_get_child_position(window, &x, &y); /* Synthetic ConfigureNotify events must be relative to the root * window, so get our offset if we're mapped. */ @@ -783,6 +794,13 @@ weston_wm_handle_configure_request(struct weston_wm *wm, xcb_generic_event_t *ev if (!wm_lookup_window(wm, configure_request->window, &window)) return; + /* If we see this, a window's override_redirect state has changed + * after it was mapped, and we don't really know what to do about + * that. + */ + if (window->override_redirect) + return; + if (window->fullscreen) { weston_wm_window_send_configure_notify(window); return; From d2aa62a074b3bf4ce6d22c2bf42f62fad3a925d5 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Wed, 20 Jul 2022 15:11:10 +0300 Subject: [PATCH 532/609] libweston: add pixel_format_get_info_by_pixman() Sometimes you will have a pixman_image_t and you need the corresponding drm_fourcc format. Signed-off-by: Pekka Paalanen --- libweston/pixel-formats.c | 13 +++++++++++++ libweston/pixel-formats.h | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/libweston/pixel-formats.c b/libweston/pixel-formats.c index 261946c7..fc33997d 100644 --- a/libweston/pixel-formats.c +++ b/libweston/pixel-formats.c @@ -636,6 +636,19 @@ pixel_format_get_info_by_drm_name(const char *drm_format_name) return NULL; } +WL_EXPORT const struct pixel_format_info * +pixel_format_get_info_by_pixman(pixman_format_code_t pixman_format) +{ + unsigned int i; + + for (i = 0; i < ARRAY_LENGTH(pixel_format_table); i++) { + if (pixel_format_table[i].pixman_format == pixman_format) + return &pixel_format_table[i]; + } + + return NULL; +} + WL_EXPORT unsigned int pixel_format_get_plane_count(const struct pixel_format_info *info) { diff --git a/libweston/pixel-formats.h b/libweston/pixel-formats.h index bbd1bff8..849cc8a2 100644 --- a/libweston/pixel-formats.h +++ b/libweston/pixel-formats.h @@ -190,6 +190,19 @@ pixel_format_get_info_count(void); const struct pixel_format_info * pixel_format_get_info_by_drm_name(const char *drm_format_name); +/** + * Get pixel format information for a Pixman format code + * + * Given a Pixman format code, return a pixel format info structure describing + * the properties of that format. + * + * @param pixman_format Pixman format code to get info for + * @returns A pixel format structure (must not be freed), or NULL if the + * format could not be found + */ +const struct pixel_format_info * +pixel_format_get_info_by_pixman(pixman_format_code_t pixman_format); + /** * Get number of planes used by a pixel format * From 03c229f4ce989c192436f00d12c445ca9b29b6fc Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 1 Aug 2022 13:01:38 +0300 Subject: [PATCH 533/609] screen-share: use read_format consistently This was using read_format for the read_pixels() call, and then using a hardcoded format for interpreting the data received from read_pixels(). That works only by accident, read_format being the same as the hardcoded format. Use read_format for the interpreting too. This should guarantee the read pixels are processed correctly. Found by code inspection. Signed-off-by: Pekka Paalanen --- compositor/screen-share.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/compositor/screen-share.c b/compositor/screen-share.c index b820278f..550d9c7b 100644 --- a/compositor/screen-share.c +++ b/compositor/screen-share.c @@ -828,6 +828,8 @@ shared_output_repainted(struct wl_listener *listener, void *data) pixman_box32_t *r; pixman_image_t *damaged_image; pixman_transform_t transform; + const pixman_format_code_t pixman_format = + so->output->compositor->read_format; width = so->output->current_mode->width; height = so->output->current_mode->height; @@ -882,13 +884,13 @@ shared_output_repainted(struct wl_listener *listener, void *data) y_orig = y; so->output->compositor->renderer->read_pixels( - so->output, so->output->compositor->read_format, + so->output, pixman_format, so->tmp_data, x, y_orig, width, height); - damaged_image = pixman_image_create_bits(PIXMAN_a8r8g8b8, + damaged_image = pixman_image_create_bits(pixman_format, width, height, so->tmp_data, - (PIXMAN_FORMAT_BPP(PIXMAN_a8r8g8b8) / 8) * width); + (PIXMAN_FORMAT_BPP(pixman_format) / 8) * width); if (!damaged_image) goto err_pixman_init; From b966fd07ea4c58dd093b94ed1ce13cd58f60e480 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 1 Aug 2022 13:31:27 +0300 Subject: [PATCH 534/609] libweston: change read_format to struct pixel_format_info Everywhere we are standardising to drm_fourcc.h pixel format codes, and using struct pixel_format_info as a general handle that allows us to access the equivalent format in various APIs. In the name of standardisation, convert weston_compositor::read_format to pixel_format_info. Pixman formats are defined CPU-endian, while DRM formats are defined always little-endian. OpenGL has various definitions. Correctly mapping between these when the CPU is big-endian is an extra chore we can hopefully offload to pixel-formats.c. GL-renderer read_format is still defined based on Pixman format, because of the pecualiar way OpenGL defines a pixel format with GL_UNSIGNED_BYTE. That matches the same Pixman format on big-endian but not the same drm_fourcc. Signed-off-by: Pekka Paalanen --- compositor/screen-share.c | 6 ++++-- include/libweston/libweston.h | 3 ++- libweston/libweston-internal.h | 6 +++--- libweston/noop-renderer.c | 6 +++--- libweston/pixman-renderer.c | 15 +++++++++------ libweston/renderer-gl/gl-renderer.c | 10 +++++----- libweston/screenshooter.c | 9 ++++++--- 7 files changed, 32 insertions(+), 23 deletions(-) diff --git a/compositor/screen-share.c b/compositor/screen-share.c index 550d9c7b..67b0c1e7 100644 --- a/compositor/screen-share.c +++ b/compositor/screen-share.c @@ -43,6 +43,7 @@ #include #include "backend.h" #include "libweston-internal.h" +#include "pixel-formats.h" #include "weston.h" #include "shared/helpers.h" #include "shared/os-compatibility.h" @@ -828,8 +829,9 @@ shared_output_repainted(struct wl_listener *listener, void *data) pixman_box32_t *r; pixman_image_t *damaged_image; pixman_transform_t transform; - const pixman_format_code_t pixman_format = + const struct pixel_format_info *read_format = so->output->compositor->read_format; + const pixman_format_code_t pixman_format = read_format->pixman_format; width = so->output->current_mode->width; height = so->output->current_mode->height; @@ -884,7 +886,7 @@ shared_output_repainted(struct wl_listener *listener, void *data) y_orig = y; so->output->compositor->renderer->read_pixels( - so->output, pixman_format, + so->output, read_format, so->tmp_data, x, y_orig, width, height); damaged_image = pixman_image_create_bits(pixman_format, diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 0f5d9ca0..7382dd2d 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -81,6 +81,7 @@ struct weston_pointer_constraint; struct ro_anonymous_file; struct weston_color_profile; struct weston_color_transform; +struct pixel_format_info; enum weston_keyboard_modifier { MODIFIER_CTRL = (1 << 0), @@ -1252,7 +1253,7 @@ struct weston_compositor { struct weston_color_manager *color_manager; struct weston_renderer *renderer; - pixman_format_code_t read_format; + const struct pixel_format_info *read_format; struct weston_backend *backend; struct weston_launcher *launcher; diff --git a/libweston/libweston-internal.h b/libweston/libweston-internal.h index 7178ca70..8bdac337 100644 --- a/libweston/libweston-internal.h +++ b/libweston/libweston-internal.h @@ -47,9 +47,9 @@ struct weston_renderer { int (*read_pixels)(struct weston_output *output, - pixman_format_code_t format, void *pixels, - uint32_t x, uint32_t y, - uint32_t width, uint32_t height); + const struct pixel_format_info *format, void *pixels, + uint32_t x, uint32_t y, + uint32_t width, uint32_t height); void (*repaint_output)(struct weston_output *output, pixman_region32_t *output_damage); void (*flush_damage)(struct weston_surface *surface, diff --git a/libweston/noop-renderer.c b/libweston/noop-renderer.c index 731d1371..f99a3130 100644 --- a/libweston/noop-renderer.c +++ b/libweston/noop-renderer.c @@ -38,9 +38,9 @@ struct noop_renderer { static int noop_renderer_read_pixels(struct weston_output *output, - pixman_format_code_t format, void *pixels, - uint32_t x, uint32_t y, - uint32_t width, uint32_t height) + const struct pixel_format_info *format, void *pixels, + uint32_t x, uint32_t y, + uint32_t width, uint32_t height) { return 0; } diff --git a/libweston/pixman-renderer.c b/libweston/pixman-renderer.c index f9ccb230..62c2cb15 100644 --- a/libweston/pixman-renderer.c +++ b/libweston/pixman-renderer.c @@ -94,9 +94,9 @@ get_renderer(struct weston_compositor *ec) static int pixman_renderer_read_pixels(struct weston_output *output, - pixman_format_code_t format, void *pixels, - uint32_t x, uint32_t y, - uint32_t width, uint32_t height) + const struct pixel_format_info *format, void *pixels, + uint32_t x, uint32_t y, + uint32_t width, uint32_t height) { struct pixman_output_state *po = get_output_state(output); pixman_image_t *out_buf; @@ -106,11 +106,11 @@ pixman_renderer_read_pixels(struct weston_output *output, return -1; } - out_buf = pixman_image_create_bits(format, + out_buf = pixman_image_create_bits(format->pixman_format, width, height, pixels, - (PIXMAN_FORMAT_BPP(format) / 8) * width); + (PIXMAN_FORMAT_BPP(format->pixman_format) / 8) * width); pixman_image_composite32(PIXMAN_OP_SRC, po->hw_buffer, /* src */ @@ -914,13 +914,16 @@ pixman_renderer_output_set_buffer(struct weston_output *output, pixman_image_t *buffer) { struct pixman_output_state *po = get_output_state(output); + pixman_format_code_t pixman_format; if (po->hw_buffer) pixman_image_unref(po->hw_buffer); po->hw_buffer = buffer; if (po->hw_buffer) { - output->compositor->read_format = pixman_image_get_format(po->hw_buffer); + pixman_format = pixman_image_get_format(po->hw_buffer); + output->compositor->read_format = + pixel_format_get_info_by_pixman(pixman_format); pixman_image_ref(po->hw_buffer); } } diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 5419c191..497d6507 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -1748,7 +1748,7 @@ gl_renderer_repaint_output(struct weston_output *output, static int gl_renderer_read_pixels(struct weston_output *output, - pixman_format_code_t format, void *pixels, + const struct pixel_format_info *format, void *pixels, uint32_t x, uint32_t y, uint32_t width, uint32_t height) { @@ -1758,7 +1758,7 @@ gl_renderer_read_pixels(struct weston_output *output, x += go->borders[GL_RENDERER_BORDER_LEFT].width; y += go->borders[GL_RENDERER_BORDER_BOTTOM].height; - switch (format) { + switch (format->pixman_format) { case PIXMAN_a8r8g8b8: gl_format = GL_BGRA_EXT; break; @@ -3883,9 +3883,9 @@ gl_renderer_setup(struct weston_compositor *ec, EGLSurface egl_surface) } if (weston_check_egl_extension(extensions, "GL_EXT_read_format_bgra")) - ec->read_format = PIXMAN_a8r8g8b8; + ec->read_format = pixel_format_get_info_by_pixman(PIXMAN_a8r8g8b8); else - ec->read_format = PIXMAN_a8b8g8r8; + ec->read_format = pixel_format_get_info_by_pixman(PIXMAN_a8b8g8r8); if (gr->gl_version < gr_gl_version(3, 0) && !weston_check_egl_extension(extensions, "GL_EXT_unpack_subimage")) { @@ -3935,7 +3935,7 @@ gl_renderer_setup(struct weston_compositor *ec, EGLSurface egl_surface) gr_gl_version_major(gr->gl_version), gr_gl_version_minor(gr->gl_version)); weston_log_continue(STAMP_SPACE "read-back format: %s\n", - ec->read_format == PIXMAN_a8r8g8b8 ? "BGRA" : "RGBA"); + ec->read_format->drm_format_name); weston_log_continue(STAMP_SPACE "wl_shm 10 bpc formats: %s\n", yesno(gr->has_texture_type_2_10_10_10_rev)); weston_log_continue(STAMP_SPACE "wl_shm 16 bpc formats: %s\n", diff --git a/libweston/screenshooter.c b/libweston/screenshooter.c index 101a80a3..b866829b 100644 --- a/libweston/screenshooter.c +++ b/libweston/screenshooter.c @@ -40,6 +40,7 @@ #include "shared/timespec-util.h" #include "backend.h" #include "libweston-internal.h" +#include "pixel-formats.h" #include "wcap/wcap-decode.h" @@ -122,12 +123,14 @@ screenshooter_frame_notify(struct wl_listener *listener, void *data) struct screenshooter_frame_listener, listener); struct weston_output *output = l->output; struct weston_compositor *compositor = output->compositor; + const pixman_format_code_t pixman_format = + compositor->read_format->pixman_format; int32_t stride; uint8_t *pixels, *d, *s; weston_output_disable_planes_decr(output); wl_list_remove(&listener->link); - stride = l->buffer->width * (PIXMAN_FORMAT_BPP(compositor->read_format) / 8); + stride = l->buffer->width * (PIXMAN_FORMAT_BPP(pixman_format) / 8); pixels = malloc(stride * l->buffer->height); if (pixels == NULL) { @@ -148,7 +151,7 @@ screenshooter_frame_notify(struct wl_listener *listener, void *data) wl_shm_buffer_begin_access(l->buffer->shm_buffer); - switch (compositor->read_format) { + switch (pixman_format) { case PIXMAN_a8r8g8b8: case PIXMAN_x8r8g8b8: if (compositor->capabilities & WESTON_CAP_CAPTURE_YFLIP) @@ -414,7 +417,7 @@ weston_recorder_create(struct weston_output *output, const char *filename) header.magic = WCAP_HEADER_MAGIC; - switch (compositor->read_format) { + switch (compositor->read_format->pixman_format) { case PIXMAN_x8r8g8b8: case PIXMAN_a8r8g8b8: header.format = WCAP_FORMAT_XRGB8888; From 851b16f00c90f0f348a035a26c5d754510890c4e Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Mon, 1 Aug 2022 16:02:09 +0300 Subject: [PATCH 535/609] gl-renderer: use pixel_format_info in read_pixels The GL format and type are already recorded with pixel_format_info, use that instead of a switch on Pixman formats. Less special-casing, less dependency on Pixman formats. Signed-off-by: Pekka Paalanen --- libweston/renderer-gl/gl-renderer.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 497d6507..0f2dab5a 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -1752,29 +1752,20 @@ gl_renderer_read_pixels(struct weston_output *output, uint32_t x, uint32_t y, uint32_t width, uint32_t height) { - GLenum gl_format; struct gl_output_state *go = get_output_state(output); x += go->borders[GL_RENDERER_BORDER_LEFT].width; y += go->borders[GL_RENDERER_BORDER_BOTTOM].height; - switch (format->pixman_format) { - case PIXMAN_a8r8g8b8: - gl_format = GL_BGRA_EXT; - break; - case PIXMAN_a8b8g8r8: - gl_format = GL_RGBA; - break; - default: + if (format->gl_format == 0 || format->gl_type == 0) return -1; - } if (use_output(output) < 0) return -1; glPixelStorei(GL_PACK_ALIGNMENT, 1); - glReadPixels(x, y, width, height, gl_format, - GL_UNSIGNED_BYTE, pixels); + glReadPixels(x, y, width, height, format->gl_format, + format->gl_type, pixels); return 0; } From 214d48bbabc6f55cf3a9d251256be6d818d9d6df Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Thu, 28 Jul 2022 08:12:21 -0500 Subject: [PATCH 536/609] compositor: Fix use after free at shutdown Another case of forgetting to remove a listener from a list when signal_emit_mutable fires. Signed-off-by: Derek Foreman --- compositor/xwayland.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compositor/xwayland.c b/compositor/xwayland.c index cf84c882..b25b74c9 100644 --- a/compositor/xwayland.c +++ b/compositor/xwayland.c @@ -234,6 +234,8 @@ wxw_compositor_destroy(struct wl_listener *listener, void *data) struct wet_xwayland *wxw = wl_container_of(listener, wxw, compositor_destroy_listener); + wl_list_remove(&wxw->compositor_destroy_listener.link); + /* Don't call xserver_exited because Xwayland's own destroy handler * already does this for us ... */ if (wxw->client) From d4eafbaa980a7698e4c0c8170ab29f9fd929754c Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 28 Jul 2022 16:56:21 +0300 Subject: [PATCH 537/609] backend-wayland: fix pixman buffer size As wayland-backend is blitting the output decorations into the output buffer itself, it pretends towards the pixman-renderer that there is no decorations area. The pixman_image_create_bits() call wraps the previously allocated buffer with an offset so that pixman-renderer will paint in the right position. The bug is that this pixman image was using the original buffer width and height, instead of the composited area width and height. So the pixman image looks too big to pixman-renderer, but the renderer didn't care. The image being too big does risk access out of bounds in pixman-renderer. I found this when I was making renderers explicitly aware of the frambuffer size and resizing, added asserts, and they surprisingly failed. This fixes that. Signed-off-by: Pekka Paalanen --- libweston/backend-wayland/wayland.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/libweston/backend-wayland/wayland.c b/libweston/backend-wayland/wayland.c index 9272b414..a5e0183a 100644 --- a/libweston/backend-wayland/wayland.c +++ b/libweston/backend-wayland/wayland.c @@ -301,7 +301,7 @@ wayland_output_get_shm_buffer(struct wayland_output *output) struct wl_shm_pool *pool; int width, height, stride; - int32_t fx, fy; + struct weston_geometry area; int fd; unsigned char *data; @@ -377,13 +377,20 @@ wayland_output_get_shm_buffer(struct wayland_output *output) cairo_image_surface_create_for_data(data, CAIRO_FORMAT_ARGB32, width, height, stride); - fx = 0; - fy = 0; - if (output->frame) - frame_interior(output->frame, &fx, &fy, 0, 0); + if (output->frame) { + frame_interior(output->frame, &area.x, &area.y, + &area.width, &area.height); + } else { + area.x = 0; + area.y = 0; + area.width = output->base.current_mode->width; + area.height = output->base.current_mode->height; + } + + /* Address only the interior, excluding output decorations */ sb->pm_image = - pixman_image_create_bits(PIXMAN_a8r8g8b8, width, height, - (uint32_t *)(data + fy * stride) + fx, + pixman_image_create_bits(PIXMAN_a8r8g8b8, area.width, area.height, + (uint32_t *)(data + area.y * stride) + area.x, stride); return sb; From 3b3fdc52c31f828ff0fb71d2c6ce7bdcc64f20a1 Mon Sep 17 00:00:00 2001 From: Michael Olbrich Date: Wed, 22 Jun 2022 08:22:06 +0200 Subject: [PATCH 538/609] backend-drm: improve atomic commit failure handling When an atomic commit fails then the output will be stuck in REPAINT_AWAITING_COMPLETION state. It is waiting for a vblank event that was never scheduled. If the error is EBUSY then it can be expected to be a transient error. So propagate the error and schedule a new repaint in the core compositor. This is necessary because there are some circumstances when the commit can fail unexpectedly: - With 'state_invalid == true' one commit will disable all planes. If another commit for a different output is triggered immediately afterwards, then this commit can temporarily fail with EBUSY because it tries to use the same planes. - At least with i915, if one commit enables an output then a second commit for a different output immediately afterwards can temporarily fail with EBUSY. This is probably caused by some hardware interdependency. Signed-off-by: Michael Olbrich --- libweston/backend-drm/drm.c | 6 +++--- libweston/compositor.c | 26 +++++++++++++++++++++++--- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index c8bec7e4..79ab239d 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -593,8 +593,8 @@ drm_output_start_repaint_loop(struct weston_output *output_base) if (ret != 0) { weston_log("applying repaint-start state failed: %s\n", strerror(errno)); - if (ret == -EACCES) - return -1; + if (ret == -EACCES || ret == -EBUSY) + return ret; goto finish_frame; } @@ -657,7 +657,7 @@ drm_repaint_flush(struct weston_compositor *compositor) drm_debug(b, "[repaint] flushed pending_state %p\n", pending_state); device->repaint_data = NULL; - return (ret == -EACCES) ? -1 : 0; + return (ret == -EACCES || ret == -EBUSY) ? ret : 0; } /** diff --git a/libweston/compositor.c b/libweston/compositor.c index 89fe410e..9c1768d2 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -3405,6 +3405,20 @@ output_repaint_timer_arm(struct weston_compositor *compositor) wl_event_source_timer_update(compositor->repaint_timer, msec_to_next); } +static void +weston_output_schedule_repaint_restart(struct weston_output *output) +{ + assert(output->repaint_status == REPAINT_AWAITING_COMPLETION); + /* The device was busy so try again one frame later */ + timespec_add_nsec(&output->next_repaint, &output->next_repaint, + millihz_to_nsec(output->current_mode->refresh)); + output->repaint_status = REPAINT_SCHEDULED; + TL_POINT(output->compositor, "core_repaint_restart", + TLP_OUTPUT(output), TLP_END); + output_repaint_timer_arm(output->compositor); + weston_output_damage(output); +} + static int output_repaint_timer_handler(void *data) { @@ -3435,8 +3449,12 @@ output_repaint_timer_handler(void *data) if (ret != 0) { wl_list_for_each(output, &compositor->output_list, link) { - if (output->repainted) - weston_output_schedule_repaint_reset(output); + if (output->repainted) { + if (ret == -EBUSY) + weston_output_schedule_repaint_restart(output); + else + weston_output_schedule_repaint_reset(output); + } } } @@ -3583,7 +3601,9 @@ idle_repaint(void *data) output->repaint_status = REPAINT_AWAITING_COMPLETION; output->idle_repaint_source = NULL; ret = output->start_repaint_loop(output); - if (ret != 0) + if (ret == -EBUSY) + weston_output_schedule_repaint_restart(output); + else if (ret != 0) weston_output_schedule_repaint_reset(output); } From 8409b74ec202aefba9e499ee8f87b07cbb0cf128 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Tue, 26 Jul 2022 16:17:17 -0500 Subject: [PATCH 539/609] libweston: Don't move outputs during enable This is pretty counter-intuitive, and should probably happen outside of the core in the front end while configuring the outputs. Signed-off-by: Derek Foreman --- compositor/main.c | 36 ++++++++++++++++++++++++++++++++++++ libweston/compositor.c | 13 +------------ 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/compositor/main.c b/compositor/main.c index c388d861..6eba91e7 100644 --- a/compositor/main.c +++ b/compositor/main.c @@ -1707,6 +1707,38 @@ wet_head_tracker_create(struct wet_compositor *compositor, weston_head_add_destroy_listener(head, &track->head_destroy_listener); } +/* Place output exactly to the right of the most recently enabled output. + * + * Historically, we haven't given much thought to output placement, + * simply adding outputs in a horizontal line as they're enabled. This + * function simply sets an output's x coordinate to the right of the + * most recently enabled output, and its y to zero. + * + * If you're adding new calls to this function, you're also not giving + * much thought to output placement, so please consider carefully if + * it's really doing what you want. + * + * You especially don't want to use this for any code that won't + * immediately enable the passed output. + */ +static void +weston_output_lazy_align(struct weston_output *output) +{ + struct weston_compositor *c; + struct weston_output *peer; + int next_x = 0; + + /* Put this output to the right of the most recently enabled output */ + c = output->compositor; + if (!wl_list_empty(&c->output_list)) { + peer = container_of(c->output_list.prev, + struct weston_output, link); + next_x = peer->x + peer->width; + } + output->x = next_x; + output->y = 0; +} + static void simple_head_enable(struct wet_compositor *wet, struct weston_head *head) { @@ -1723,6 +1755,8 @@ simple_head_enable(struct wet_compositor *wet, struct weston_head *head) return; } + weston_output_lazy_align(output); + if (wet->simple_output_configure) ret = wet->simple_output_configure(output); if (ret < 0) { @@ -2359,6 +2393,8 @@ drm_try_enable(struct weston_output *output, { /* Try to enable, and detach heads one by one until it succeeds. */ while (!output->enabled) { + weston_output_lazy_align(output); + if (weston_output_enable(output) == 0) return 0; diff --git a/libweston/compositor.c b/libweston/compositor.c index 9c1768d2..303f3aa9 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -7268,11 +7268,8 @@ weston_output_create_heads_string(struct weston_output *output) WL_EXPORT int weston_output_enable(struct weston_output *output) { - struct weston_compositor *c = output->compositor; - struct weston_output *iterator; struct weston_head *head; char *head_names; - int x = 0, y = 0; if (output->enabled) { weston_log("Error: attempt to enable an enabled output '%s'\n", @@ -7297,20 +7294,12 @@ weston_output_enable(struct weston_output *output) assert(head->model); } - iterator = container_of(c->output_list.prev, - struct weston_output, link); - - if (!wl_list_empty(&c->output_list)) - x = iterator->x + iterator->width; - /* Make sure the scale is set up */ assert(output->scale); /* Make sure we have a transform set */ assert(output->transform != UINT32_MAX); - output->x = x; - output->y = y; output->dirty = 1; output->original_scale = output->scale; @@ -7319,7 +7308,7 @@ weston_output_enable(struct weston_output *output) weston_output_transform_scale_init(output, output->transform, output->scale); - weston_output_init_geometry(output, x, y); + weston_output_init_geometry(output, output->x, output->y); weston_output_damage(output); wl_list_init(&output->animation_list); From 7e7198bd881859b20f74cbdae46b5700ff1cc5d4 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Wed, 27 Jul 2022 10:34:37 -0500 Subject: [PATCH 540/609] libweston: Check output placement Make sure we don't enable an output that overlaps with other enabled outputs. We should probably do something similar when moving outputs, but we can't realistically do that right now, so at least leave a comment explaining why we're ignoring that case. Signed-off-by: Derek Foreman --- libweston/compositor.c | 56 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/libweston/compositor.c b/libweston/compositor.c index 303f3aa9..e226f69c 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -6608,6 +6608,18 @@ weston_output_set_position(struct weston_output *output, int x, int y) WL_EXPORT void weston_output_move(struct weston_output *output, int x, int y) { + /* XXX: we should probably perform some sanity checking here + * as we do for weston_output_enable, and allow moves to fail. + * + * However, while a front-end is rearranging outputs it may + * pass through indeterminate states where outputs overlap + * or are discontinuous, and this may be ok as long as no + * input processing or rendering occurs at that time. + * + * Ultimately, we probably need a way to pass complete output + * config atomically to libweston. + */ + output->compositor->output_flow_dirty = true; weston_output_set_position(output, x, y); } @@ -7230,6 +7242,45 @@ weston_output_create_heads_string(struct weston_output *output) return str; } +static bool +weston_outputs_overlap(struct weston_output *a, struct weston_output *b) +{ + bool overlap; + pixman_region32_t intersection; + + pixman_region32_init(&intersection); + pixman_region32_intersect(&intersection, &a->region, &b->region); + overlap = pixman_region32_not_empty(&intersection); + pixman_region32_fini(&intersection); + + return overlap; +} + +/* This only works if the output region is current! + * + * That means we shouldn't expect it to return usable results unless + * the output is at least undergoing enabling. + */ +static bool +weston_output_placement_ok(struct weston_output *output) +{ + struct weston_compositor *c = output->compositor; + struct weston_output *iter; + + wl_list_for_each(iter, &c->output_list, link) { + if (!iter->enabled) + continue; + + if (weston_outputs_overlap(iter, output)) { + weston_log("Error: output '%s' overlaps enabled output '%s'.\n", + output->name, iter->name); + return false; + } + } + + return true; +} + /** Constructs a weston_output object that can be used by the compositor. * * \param output The weston_output object that needs to be enabled. Must not @@ -7309,6 +7360,11 @@ weston_output_enable(struct weston_output *output) weston_output_transform_scale_init(output, output->transform, output->scale); weston_output_init_geometry(output, output->x, output->y); + + /* At this point we have a valid region so we can check placement. */ + if (!weston_output_placement_ok(output)) + return -1; + weston_output_damage(output); wl_list_init(&output->animation_list); From 6ee486ff95dffc50ac994b9991ba7b7f775744e3 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Wed, 27 Jul 2022 11:05:32 -0500 Subject: [PATCH 541/609] libweston: Don't send output_changed signal when moving disabled outputs weston_output_set_position() currently assumes the output is enabled, but we could be using weston_output_move() to configure an output that hasn't yet been enabled. If that's the case, we don't want to send signals or perform setup that will eventually happen when the output is enabled anyway. Signed-off-by: Derek Foreman --- libweston/compositor.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libweston/compositor.c b/libweston/compositor.c index e226f69c..30913cbc 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -6562,6 +6562,12 @@ weston_output_set_position(struct weston_output *output, int x, int y) struct wl_resource *resource; int ver; + if (!output->enabled) { + output->x = x; + output->y = y; + return; + } + output->move_x = x - output->x; output->move_y = y - output->y; From 6e529cb6aba5e93edfa0b4b0b134c3aa3a914fa0 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Sun, 31 Jul 2022 12:20:13 +0200 Subject: [PATCH 542/609] compositor/main.c: use pixman renderer by default when gl-renderer not enabled When the gl-renderer is not enabled, weston fails to start, as it doesn't automatically fallback to the pixman renderer, which is always enabled. This commit changes the drm-backend to set by default the --use-pixman option to true when the gl-renderer is disabled (BUILD_DRM_GBM is not defined). Signed-off-by: Thomas Petazzoni --- compositor/main.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/compositor/main.c b/compositor/main.c index 6eba91e7..2eb0a03c 100644 --- a/compositor/main.c +++ b/compositor/main.c @@ -2879,13 +2879,23 @@ load_drm_backend(struct weston_compositor *c, struct weston_config_section *section; struct wet_compositor *wet = to_wet_compositor(c); bool without_input = false; + bool use_pixman_default; int ret = 0; wet->drm_use_current_mode = false; section = weston_config_get_section(wc, "core", NULL, NULL); + + /* Use the pixman renderer by default when GBM/EGL support is + * not enabled */ +#if defined(BUILD_DRM_GBM) + use_pixman_default = false; +#else + use_pixman_default = true; +#endif + weston_config_section_get_bool(section, "use-pixman", &config.use_pixman, - false); + use_pixman_default); const struct weston_option options[] = { { WESTON_OPTION_STRING, "seat", 0, &config.seat_id }, From 55b2bf939345c39c26f4edd8e68bd2bb833758d8 Mon Sep 17 00:00:00 2001 From: Hideyuki Nagase Date: Thu, 10 Mar 2022 13:43:38 -0600 Subject: [PATCH 543/609] xwayland: Respect client WM_TAKE_FOCUS setting According to https://tronche.com/gui/x/icccm/sec-4.html#s-4.1.7 we should send this focus notification only if a client has WM_TAKE_FOCUS set in their WM_PROTOCOLS property. We've been sending it unconditionally. Rather, we've been not-sending it unconditionally because the event mask is wrong, but that will be fixed in a future commit. Fixing the event mask first would break some clients (such as xterm). Co-authored-by: Steve Pronovost Co-authored-by: Brenton DeGeer Signed-off-by: Hideyuki Nagase Signed-off-by: Steve Pronovost Signed-off-by: Brenton DeGeer --- xwayland/window-manager.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/xwayland/window-manager.c b/xwayland/window-manager.c index 93a3d6d8..59ae61c0 100644 --- a/xwayland/window-manager.c +++ b/xwayland/window-manager.c @@ -171,6 +171,7 @@ struct weston_wm_window { int delete_window; int maximized_vert; int maximized_horz; + int take_focus; struct wm_size_hints size_hints; struct motif_wm_hints motif_hints; struct wl_list link; @@ -530,6 +531,7 @@ weston_wm_window_read_properties(struct weston_wm_window *window) window->size_hints.flags = 0; window->motif_hints.flags = 0; window->delete_window = 0; + window->take_focus = 0; for (i = 0; i < ARRAY_LENGTH(props); i++) { reply = xcb_get_property_reply(wm->conn, cookie[i], NULL); @@ -572,7 +574,8 @@ weston_wm_window_read_properties(struct weston_wm_window *window) for (i = 0; i < reply->value_len; i++) if (atom[i] == wm->atom.wm_delete_window) { window->delete_window = 1; - break; + } else if (atom[i] == wm->atom.wm_take_focus) { + window->take_focus = 1; } break; case TYPE_WM_NORMAL_HINTS: @@ -936,16 +939,18 @@ weston_wm_send_focus_window(struct weston_wm *wm, if (window->override_redirect) return; - client_message.response_type = XCB_CLIENT_MESSAGE; - client_message.format = 32; - client_message.window = window->id; - client_message.type = wm->atom.wm_protocols; - client_message.data.data32[0] = wm->atom.wm_take_focus; - client_message.data.data32[1] = XCB_TIME_CURRENT_TIME; - - xcb_send_event(wm->conn, 0, window->id, - XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, - (char *) &client_message); + if (window->take_focus) { + client_message.response_type = XCB_CLIENT_MESSAGE; + client_message.format = 32; + client_message.window = window->id; + client_message.type = wm->atom.wm_protocols; + client_message.data.data32[0] = wm->atom.wm_take_focus; + client_message.data.data32[1] = XCB_TIME_CURRENT_TIME; + + xcb_send_event(wm->conn, 0, window->id, + XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, + (char *) &client_message); + } xcb_set_input_focus (wm->conn, XCB_INPUT_FOCUS_POINTER_ROOT, window->id, XCB_TIME_CURRENT_TIME); From 5afe6c5b39bb13ad9a9600a54f9fead7106c646d Mon Sep 17 00:00:00 2001 From: Hideyuki Nagase Date: Tue, 28 Jun 2022 16:46:46 -0500 Subject: [PATCH 544/609] xwm: Change event mask for WM_TAKE_FOCUS This should be XCB_EVENT_MASK_NO_EVENT, but was not. Co-authored-by: Steve Pronovost Co-authored-by: Brenton DeGeer Signed-off-by: Hideyuki Nagase Signed-off-by: Steve Pronovost Signed-off-by: Brenton DeGeer --- xwayland/window-manager.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xwayland/window-manager.c b/xwayland/window-manager.c index 59ae61c0..7cadffdd 100644 --- a/xwayland/window-manager.c +++ b/xwayland/window-manager.c @@ -948,7 +948,7 @@ weston_wm_send_focus_window(struct weston_wm *wm, client_message.data.data32[1] = XCB_TIME_CURRENT_TIME; xcb_send_event(wm->conn, 0, window->id, - XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, + XCB_EVENT_MASK_NO_EVENT, (char *) &client_message); } From ae4209978c2130a1a937e408fa84f0417f59d14b Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Mon, 6 Jun 2022 16:21:24 -0500 Subject: [PATCH 545/609] xwayland: Don't focus an already focused xwayland window We've been doing this when clicking on windows, even if they're already activated. This leads to sending extra WM_TAKE_FOCUS events as well as re-rendering the decor every mouse click. Signed-off-by: Derek Foreman --- xwayland/window-manager.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/xwayland/window-manager.c b/xwayland/window-manager.c index 7cadffdd..50380562 100644 --- a/xwayland/window-manager.c +++ b/xwayland/window-manager.c @@ -980,6 +980,9 @@ weston_wm_window_activate(struct wl_listener *listener, void *data) window = get_wm_window(surface); } + if (wm->focus_window == window) + return; + if (window) { weston_wm_set_net_active_window(wm, window->id); } else { From dac2f146eae20f958186b70725f86e7b9d541ded Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Wed, 27 Jul 2022 17:02:57 -0500 Subject: [PATCH 546/609] xwm: Perform a roundtrip to send a deferred WM_TAKE_FOCUS WM_TAKE_FOCUS requires a valid timestamp that isn't XCB_TIME_CURRENT. To get one, we set a property on the window and wait for the notification that it was set - this notification comes with a valid timestamp. Once we have that timestamp, delete the property, and fire off the slightly delayed WM_TAKE_FOCUS client request. Signed-off-by: Derek Foreman --- shared/xcb-xwayland.c | 1 + shared/xcb-xwayland.h | 1 + xwayland/window-manager.c | 50 +++++++++++++++++++++++++++++---------- 3 files changed, 40 insertions(+), 12 deletions(-) diff --git a/shared/xcb-xwayland.c b/shared/xcb-xwayland.c index bb207b90..bd64e4c9 100644 --- a/shared/xcb-xwayland.c +++ b/shared/xcb-xwayland.c @@ -139,6 +139,7 @@ x11_get_atoms(xcb_connection_t *connection, struct atom_x11 *atom) { "XdndActionCopy", F(xdnd_action_copy) }, { "_XWAYLAND_ALLOW_COMMITS", F(allow_commits) }, { "WL_SURFACE_ID", F(wl_surface_id) }, + { "_WESTON_FOCUS_PING", F(weston_focus_ping) }, }; xcb_intern_atom_cookie_t cookies[ARRAY_LENGTH(atoms)]; diff --git a/shared/xcb-xwayland.h b/shared/xcb-xwayland.h index 8157240c..38eea82a 100644 --- a/shared/xcb-xwayland.h +++ b/shared/xcb-xwayland.h @@ -97,6 +97,7 @@ struct atom_x11 { xcb_atom_t xdnd_action_copy; xcb_atom_t wl_surface_id; xcb_atom_t allow_commits; + xcb_atom_t weston_focus_ping; }; const char * diff --git a/xwayland/window-manager.c b/xwayland/window-manager.c index 50380562..f0655a7f 100644 --- a/xwayland/window-manager.c +++ b/xwayland/window-manager.c @@ -931,8 +931,6 @@ static void weston_wm_send_focus_window(struct weston_wm *wm, struct weston_wm_window *window) { - xcb_client_message_event_t client_message; - if (window) { uint32_t values[1]; @@ -940,16 +938,15 @@ weston_wm_send_focus_window(struct weston_wm *wm, return; if (window->take_focus) { - client_message.response_type = XCB_CLIENT_MESSAGE; - client_message.format = 32; - client_message.window = window->id; - client_message.type = wm->atom.wm_protocols; - client_message.data.data32[0] = wm->atom.wm_take_focus; - client_message.data.data32[1] = XCB_TIME_CURRENT_TIME; - - xcb_send_event(wm->conn, 0, window->id, - XCB_EVENT_MASK_NO_EVENT, - (char *) &client_message); + /* Set a property to get a roundtrip + * with a timestamp for WM_TAKE_FOCUS */ + xcb_change_property(wm->conn, + XCB_PROP_MODE_REPLACE, + window->id, + wm->atom.weston_focus_ping, + XCB_ATOM_STRING, + 8, /* format */ + 0, NULL); } xcb_set_input_focus (wm->conn, XCB_INPUT_FOCUS_POINTER_ROOT, @@ -1502,6 +1499,35 @@ weston_wm_handle_property_notify(struct weston_wm *wm, xcb_generic_event_t *even if (!wm_lookup_window(wm, property_notify->window, &window)) return; + /* We set the weston_focus_ping property on this window to + * get a timestamp to send a WM_TAKE_FOCUS... send it now, + * or just return if this is confirming we deleted the + * property. + */ + if (property_notify->atom == wm->atom.weston_focus_ping) { + xcb_client_message_event_t client_message; + + if (property_notify->state == XCB_PROPERTY_DELETE) + return; + + /* delete our ping property */ + xcb_delete_property(window->wm->conn, + window->id, + window->wm->atom.weston_focus_ping); + + client_message.response_type = XCB_CLIENT_MESSAGE; + client_message.format = 32; + client_message.window = window->id; + client_message.type = wm->atom.wm_protocols; + client_message.data.data32[0] = wm->atom.wm_take_focus; + client_message.data.data32[1] = property_notify->time; + xcb_send_event(wm->conn, 0, window->id, + XCB_EVENT_MASK_NO_EVENT, + (char *) &client_message); + + return; + } + window->properties_dirty = 1; if (wm_debug_is_enabled(wm)) From f7ba35f5fc2344f6c556e143358429c85cf81333 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Mon, 4 Oct 2021 15:54:43 +0300 Subject: [PATCH 547/609] kiosk-shell: Enable debug keybindings We are missing debug keybinds in kiosk-shell so install them. Adds the binding-modifier like in desktop-shell in a helper. Signed-off-by: Marius Vlad --- kiosk-shell/kiosk-shell.c | 12 +++++++++--- shell-utils/shell-utils.c | 31 +++++++++++++++++++++++++++++++ shell-utils/shell-utils.h | 6 ++++++ 3 files changed, 46 insertions(+), 3 deletions(-) diff --git a/kiosk-shell/kiosk-shell.c b/kiosk-shell/kiosk-shell.c index e13742fb..3d00baf4 100644 --- a/kiosk-shell/kiosk-shell.c +++ b/kiosk-shell/kiosk-shell.c @@ -1064,15 +1064,21 @@ kiosk_shell_touch_to_activate_binding(struct weston_touch *touch, static void kiosk_shell_add_bindings(struct kiosk_shell *shell) { - weston_compositor_add_button_binding(shell->compositor, BTN_LEFT, 0, + uint32_t mod = 0; + + mod = weston_shell_get_binding_modifier(shell->config, MODIFIER_SUPER); + + weston_compositor_add_button_binding(shell->compositor, BTN_LEFT, mod, kiosk_shell_click_to_activate_binding, shell); - weston_compositor_add_button_binding(shell->compositor, BTN_RIGHT, 0, + weston_compositor_add_button_binding(shell->compositor, BTN_RIGHT, mod, kiosk_shell_click_to_activate_binding, shell); - weston_compositor_add_touch_binding(shell->compositor, 0, + weston_compositor_add_touch_binding(shell->compositor, mod, kiosk_shell_touch_to_activate_binding, shell); + + weston_install_debug_key_binding(shell->compositor, mod); } static void diff --git a/shell-utils/shell-utils.c b/shell-utils/shell-utils.c index a34e9d00..25978734 100644 --- a/shell-utils/shell-utils.c +++ b/shell-utils/shell-utils.c @@ -212,3 +212,34 @@ weston_curtain_destroy(struct weston_curtain *curtain) weston_buffer_destroy_solid(curtain->buffer_ref); free(curtain); } + +uint32_t +weston_shell_get_binding_modifier(struct weston_config *config, + uint32_t default_mod) +{ + struct weston_config_section *shell_section = NULL; + char *mod_string = NULL; + uint32_t mod = default_mod; + + if (config) + shell_section = weston_config_get_section(config, "shell", NULL, NULL); + + if (shell_section) + weston_config_section_get_string(shell_section, + "binding-modifier", &mod_string, "super"); + + if (!mod_string || !strcmp(mod_string, "none")) + mod = default_mod; + else if (!strcmp(mod_string, "super")) + mod = MODIFIER_SUPER; + else if (!strcmp(mod_string, "alt")) + mod = MODIFIER_ALT; + else if (!strcmp(mod_string, "ctrl")) + mod = MODIFIER_CTRL; + else if (!strcmp(mod_string, "shift")) + mod = MODIFIER_SHIFT; + + free(mod_string); + + return mod; +} diff --git a/shell-utils/shell-utils.h b/shell-utils/shell-utils.h index f326eade..b7a1df9a 100644 --- a/shell-utils/shell-utils.h +++ b/shell-utils/shell-utils.h @@ -26,6 +26,9 @@ #include "shared/helpers.h" #include +#include +#include + /* parameter for weston_curtain_create() */ struct weston_curtain_params { int (*get_label)(struct weston_surface *es, char *buf, size_t len); @@ -63,3 +66,6 @@ weston_curtain_create(struct weston_compositor *compositor, struct weston_curtain_params *params); void weston_curtain_destroy(struct weston_curtain *curtain); + +uint32_t +weston_shell_get_binding_modifier(struct weston_config *config, uint32_t default_mod); From 478b24cae0a1a67e6db3426938a93bdc8f3b1b59 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Mon, 1 Aug 2022 17:03:03 +0300 Subject: [PATCH 548/609] desktops-shell: Re-use helper for modifier retrieval As we now have a helper to retrieve the binding modifier, use it in desktop-shell as well. Signed-off-by: Marius Vlad --- desktop-shell/shell.c | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index d1a2035d..0e746b05 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -442,24 +442,6 @@ shell_touch_grab_end(struct shell_touch_grab *grab) weston_touch_end_grab(grab->touch); } -static enum weston_keyboard_modifier -get_modifier(char *modifier) -{ - if (!modifier) - return MODIFIER_SUPER; - - if (!strcmp("ctrl", modifier)) - return MODIFIER_CTRL; - else if (!strcmp("alt", modifier)) - return MODIFIER_ALT; - else if (!strcmp("super", modifier)) - return MODIFIER_SUPER; - else if (!strcmp("none", modifier)) - return 0; - else - return MODIFIER_SUPER; -} - static enum animation_type get_animation_type(char *animation) { @@ -480,11 +462,12 @@ static void shell_configuration(struct desktop_shell *shell) { struct weston_config_section *section; + struct weston_config *config; char *s, *client; bool allow_zap; - section = weston_config_get_section(wet_get_config(shell->compositor), - "shell", NULL, NULL); + config = wet_get_config(shell->compositor); + section = weston_config_get_section(config, "shell", NULL, NULL); client = wet_get_libexec_path(WESTON_SHELL_CLIENT); weston_config_section_get_string(section, "client", &s, client); free(client); @@ -494,10 +477,7 @@ shell_configuration(struct desktop_shell *shell) "allow-zap", &allow_zap, true); shell->allow_zap = allow_zap; - weston_config_section_get_string(section, - "binding-modifier", &s, "super"); - shell->binding_modifier = get_modifier(s); - free(s); + shell->binding_modifier = weston_shell_get_binding_modifier(config, MODIFIER_SUPER); weston_config_section_get_string(section, "animation", &s, "none"); shell->win_animation_type = get_animation_type(s); From b282fe3a730ea636dc754d2a71c4cf97b4d6f7b6 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Fri, 29 Jul 2022 12:05:28 +0200 Subject: [PATCH 549/609] ivi-shell: remove unused definition ivi_layout_screen The ivi_layout_screen is internal to the IVI shell and not used by any controllers. Controllers use weston_output directly. Remove it from the exported header to avoid confusion. Signed-off-by: Michael Tretter --- ivi-shell/ivi-layout-export.h | 1 - 1 file changed, 1 deletion(-) diff --git a/ivi-shell/ivi-layout-export.h b/ivi-shell/ivi-layout-export.h index 740e1ac0..6e76490c 100644 --- a/ivi-shell/ivi-layout-export.h +++ b/ivi-shell/ivi-layout-export.h @@ -67,7 +67,6 @@ extern "C" { #define IVI_INVALID_ID UINT_MAX struct ivi_layout_layer; -struct ivi_layout_screen; struct ivi_layout_surface; struct ivi_layout_surface_properties From 7d16485efd358d5f3bf2126864847cc0387234f9 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Fri, 29 Jul 2022 15:57:58 +0200 Subject: [PATCH 550/609] ivi-shell: remove dysfunctional link The at.projects.genivi.org domain redirects to wiki.covesa.global and the referenced wiki entry does not exist anymore. Remove it from the comment. Signed-off-by: Michael Tretter --- ivi-shell/ivi-layout-export.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/ivi-shell/ivi-layout-export.h b/ivi-shell/ivi-layout-export.h index 6e76490c..c2a47cd2 100644 --- a/ivi-shell/ivi-layout-export.h +++ b/ivi-shell/ivi-layout-export.h @@ -43,9 +43,6 @@ * way in In-Vehicle Infotainment system, which integrate several domains * in one system. A layer is allocated to a domain in order to control * application surfaces grouped to the layer all together. - * - * This API and ABI follow following specifications. - * https://at.projects.genivi.org/wiki/display/PROJ/Wayland+IVI+Extension+Design */ #ifndef _IVI_LAYOUT_EXPORT_H_ From a2684005b639ab1fc81ac9e17584a93818cd483d Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Fri, 29 Jul 2022 10:39:37 +0200 Subject: [PATCH 551/609] doc: update and move IVI-shell README to doc The README for the IVI-shell is completely outdated. Update the documentation, add some more information on the IVI-shell use cases and explain how to use and customize the IVI-shell. Also convert the file to rst and move it to doc directory next to the kiosk-shell documentation. Signed-off-by: Michael Tretter --- doc/sphinx/index.rst | 1 + doc/sphinx/toc/images/ivi-shell.png | Bin 0 -> 110550 bytes doc/sphinx/toc/images/meson.build | 9 +++ doc/sphinx/toc/ivi-shell.rst | 111 ++++++++++++++++++++++++++++ doc/sphinx/toc/meson.build | 2 + ivi-shell/README | 78 ------------------- 6 files changed, 123 insertions(+), 78 deletions(-) create mode 100644 doc/sphinx/toc/images/ivi-shell.png create mode 100644 doc/sphinx/toc/images/meson.build create mode 100644 doc/sphinx/toc/ivi-shell.rst delete mode 100644 ivi-shell/README diff --git a/doc/sphinx/index.rst b/doc/sphinx/index.rst index 8aa53b3e..a47bccb9 100644 --- a/doc/sphinx/index.rst +++ b/doc/sphinx/index.rst @@ -9,6 +9,7 @@ Welcome to Weston documentation! toc/libweston.rst toc/test-suite.rst toc/kiosk-shell.rst + toc/ivi-shell.rst Weston ------ diff --git a/doc/sphinx/toc/images/ivi-shell.png b/doc/sphinx/toc/images/ivi-shell.png new file mode 100644 index 0000000000000000000000000000000000000000..87ff7a36c2678eea717918eade146193084ae32c GIT binary patch literal 110550 zcmZsD2RNKv*RD3o5G6*hk)lNjq8o+?5@mFW9uakdFhpkrN%SB(qqi6&j2=Ps=tM8k zOOWW@c_w+k_y7KLPR4a{#ms*8UVH7e?)zSAdjr>0xdLVeFf`?!FRP7#2@8!hLG|xo3)|$)xj5CG?!z*SH#OmRz`g45X`{O2rq~W(w zS&{+NG_MQ3fUa6nYs17NT<`;FAY$(VUMcH6)rKj%KBnAN^xh=N^m$BpnIwCuEPY)tI`c#)8$znC$ z=jQlaM07ILx7%Ale@67AB-_0OQvK$ryWWzlEJ0R&@R_+$m$Iev&ve)wMivC+bjohM z*eF4q;+5crXNr;%fP9FUdVFFhg{H%eyKskukBF|M$p2o#PSjdwEO%Jr<@t8{tlueu zF~pKFY+Momc)5uYaRknoU> zR+@5w@QddhnPO1XTp))_8{g?#LDy8N zK))66akh1adjFbh3P1@vig1A}fj1CCUU357a4R4mcnzsIHFxT{TGH#lgB7eF_22Gh z$a}kcSr<6gxK$pWb~wvo6K0I5x%a`x@ufkj;b*MysMJ!VLnSg|&|utl))CeO>i!;< z9R*E}Jeq_d^Pu{9`&s0gE(9*jm-_H3nEpY|T##rwjl6uNAJ~@b%Lvx0 z%E&^P;>%ldP%N>rc2}M|tH)(FZ>hh$V>qM4Z<+;Y0O}d1f%IOoYCo@_>VvD``*%$* z-{hZtlV=F5pp-C4H}bwQ;HMT~tN#xou92oN^Q{M9OHM?U(v%j9(1v zYUQ4lj$DtDefRM^LSlw`CDk>uYV$2WhPt%z!mDk=b5M=@jiI@3W$(o8Z+E-*^L$53 z&7BZV2RlpM%I7EhmF?Ca(>@J2sU6&`jP>E@&2=GY2|xwogUG&J*>XvGc{yI!`eiHO zEyt{n35$k@RcM&9U3-{vP^{Lk8JP;pyy~)n()!h?6K=K)g0HrhqAaP4Dj^-55m{G{ zs|6Fp_q%d7MVzx2PTlN(jJ6tVMbZ_X?(D;duOo&=TIhLgi)BV)<_b$>_nAkqca-@D z?}l>>D|^qF1f#ZnYYkIp>w}Fx7I7_|AIeV(dF`!l2T4zqc6#>IJIEZznIFO5a_X;a zHGC~;={noUvzj@7s?~MTJ^K7=TI~Vk<}~II??!{cbr2c4(|VPYvC(fKsC2L^;gHYGmV%vb@eXk&cA^7OmVvl9$%S+b+f_>jn3pPpn%MC3GXZ_Mw}5Art3r`yFDC zMb(dMLh96P-ll@@{bbt-VTNWy6Q{-z;wgu1O1#>R6nl;_DEq|}3Vbr~)#!@&cKFW^ zu3Co&GQZ2J*ju>@O*0MNEI;CFufT=bz=NM(q(rjIJ@!tmjl!fp2}Moa->D`9d`Y26{a)7{ zJ>8~MiW1Ve!hrDWw{RLAIyIo&DeEfWIdRE2Tn$$gg zIFSt{iYr~2J6UV`!(d)5rc!)V`<>6VrR$jo3nU!thZr!XAbx!z2 zibebyV+g85#%#4kmDVWdT=GS!W83EZL|__0CTE~3G$afESXP_shx$rYT4=Z5R?DM_ zgVfy#!d`gvjLhZ-^PGT3beQ#Z^g-^Lw4P*_b>cd3>R~to8%GlBU5lrnKq@>4G30|2 zL57uJcfoKuGlqsos&L^qFFt{?ErF;Bk4DE+EHGM(qZ7cZtNXbOsksraW2}7 zLhV>o_%vC(OuNoP@;s?)ZXYu*Hfo&K_3{>&B_u(m@T^=}G(-WE2t{H5urqD!V zbt~^nz9d}O-q)1jPE7Yu55MaEvOiP(pe%|?-G3rO3$Dqwe8>HaY+r}#Kr+|RSH5#2 zZqhj6mSd4h9-31nv~#nWHDH{o^2%htTeWSP>~AD?m1!74}}9^F1^+SiT3stf`i&np$zZ+^^tP41^iX6;0sYO zBz#bioc^|Iof+L?@VLC=N!i1+kyU-EN;y)+^{&xeXB8%XVG16jmvFd*M+T+a8X7Gi z5{*t2yfRMD+oA2Zj^Y+v5`pI+eB>xrP|GxW8#dDSBOq}b7-0635N3#5lCH4*y~o3< zu;Zy?2rrL{*~RuM882rP7tzw;@M4f=bXl%A3%s3|n zW7@3Pq>g=+ME2W~DTf9wn{zXD!gCLZpoY!4{@U8=Qz zeM#LFynaww@~e33;i#gu%Q*I2V{}=^BVo_}@9Ir`$HYBMbpY!*r6)J@jow{PMOkK| zH&Rg)#3PkTP z_D4azD)_Hel|?!itxolYX$CvmhIx0 zAni=_ny)UAH6u@=UR}p&_tLBJSvqh?$tSX+TD*4ka5?~a-InE1jd`x*Wfnu6{_^t| z!)K8n6=71rMjBDp3MvWLONBq=-QK{ituN|eD0z|(s_?ZYr4~m*mmILUh8sGe>73FQM*<3Ci<&N&wb*H8tWpnb*WF z)fKLgLGkNpnKH7zdJ{AQuC{-gK^9WjUQkTc&D(?>2}KsRxS-L`*&EUEo*MfUg!f`! zq4M;D5ye!+n+20?WzhywZR;)60RCe5U5~P_6;x6r=BH3Qs(d&cM>(IU0EeGA_D{Lp zvUvK~TB_hV7BX58ph>Lr;#+8v#D$|%lOxgpBFBM9O5HVw;^K6xx|~>A_f+W z@G|G?AlDwEmMvyyq_&xNn)EHms~*-;j2iIc5D7PlKR80?}z@VUY zR$?&megcAm0Njkr*fmey_RIV}vLY~K18_Egr>l>?(Il5ho7kF~GW`j(mSbdQ@d*jW zO;asY@;`~{)Lk2sz9ibEE7GchT?J{s74h>FJda&L=mqK;@i69^x&`zSgG>*o2tnDA zDLH5~`4JK16+Ynzk+@74cP+fTnYm(nzBP<+ z$Oc)~zGPBE3>pznFuZGESapRE6l>S1dpza1;JZ0e~NYq=(B3sH*4thD=9= z{VEA&g(*~UU-MwzovBxqRJq^x9w9ZAMqPvP<;D|r;R3ka(MDP?3`Fy~>VUDb)4I<*5qaZPf)CNx#mp^q0#(CF@( zW>mU52Kr&rFV0$jvH&iuURQuvAfO34h#~-)uBVoag#LeLoRw_Y4=I24V7CGvIyvPm z-4BU_Iy{ilc%ccSEz@OV0Iz2WjW|T5Kz~jM-YVXtdv;l{)&fI{2R=1^m0vPrpYms> zt>mEmDVGwX<1y#UeVN^Muh^>M?TB@6{hX1YzRPXmCzrMDSBtkBC3@+rOkC9-oP+Hu zt)G{wSD4;WY|>}JhJ9km;1+j079Tt)SgK`Zs0>#hEjP-X+uo`-Q%{uW@<0Fe(YxL| z^JJ!yP$pQ=`y$1k-bU;q*Qnmp;K{zJ_Ho>*bITI%zN?)1yZz3umDZnp{<4LzDG|v# zP28RwY>e80S6Y}R3Fz`vF=)~<;gz=pUT$c;^iv@+9-fTZ3TCIV;QR9xVN;E@C$Glk zzbRg~Y>!CZ(%k&$ckZ>()mGc8?nX@yrv?I#8INl5Sufv4%FfAFU|Q?9JHFwkc;IIY zz^*NGI1)j=$pdc~pKk{I(@(>hKqMXNx$cY zahJtb&fs3^NWVB(?+xR`FWqC^E(EZ(ukELw+icv zr1*#2xd8%0<@J|GFw;S7%;o;~+b)*b=AM(aCKGyb?)66Gpr!M5Hji*~o8xnUmTZrP z;$1hLoT@uLUAf6v>tgn(YoqM6NI@$CPt25myJ{s~MQ7c6dk$lVQ1&I{S*2`yUpjq5 z+GG1BA6DOUyYX_|Z{U+#K;ox4Wq^#AIX28{acAt1`^yU+F3(0u4qomioU}I_u9*!i z)<2sOpLP-FQEeA*EQqMK>7v*^TF@%se*d9JwI**!(<#y2%&Sp%G(9V4a({j$|2xBq zm|e2Fp8`x~s&*&&`;)bH*RQ)7P+ow1uHj4UHzWyZ?HtPQ8*-nXe{t4~lw1^9sXtHv zONz2}pB}QiYfOmm>}aJ!Y|^}It2XDc46pfXD=Y4Ud(A{!OXY*^)rNHTMPgC1$Fkh% zFHnsfrYuT21UYig*AlU_JDaYR?Zl=mD$fQ_*IiziY9Pt^IR4or?xFYi$-ud;J=|Lx zQf`lAQy$9GkeeK={ZV`OkY=A@p9O895(? zsw(MHOEBltF=gI1R*eq-Z6lYbb(=;Z!4xB_M>xLF#DK5jnUXy7A&fJw8y>ZCNcM8@ z&h=B1cmjc?QImGzQ(_ows4}^$zfyn;q;$U>QR&cD(KBB)jNT z#nxRG-NeE+Te}bp9gfXR6>NAEjOz21_imH^y+;!xeuSj2P;wo2zh0y`Ic%Y5S=$>h!XH7cSeW(15m$*T%@i>bIzC&Vl>~J z#m>6oza^^5yVB4r)E7s<&*2bcD-u_cOO-N+Sla#N5E5$-$(f>eInXbap%5NEFL5wd zX!^xlLA25~eOW-YzPg<-JM}CWB@*x{ce$%n->4v z>Se?8?c}S++>I*)5VHGM7+r(R)5)4U+l%nj{Yp@xqf(%yIGYQZ0sz#;ZM0ichd({a z`##|2*k9S%o`g|G5Y}MU?^<(}04a+A0<7rAy-dykfCZRTvCFuBxb)0%E8ek5KMX(q z_+oTQW!QYmV>k*}>k8eCj<(A#8ar}sD$OwU>lPhrk5VJM`+uI>Y#-piryO33M1rjI zTD2YRD%XFDeCuZ5DZs7J*7Zo+ZLTfK{Ww}91O=IHxB4{uCDX33_64&OHDqzv^0U_O z@||NxI6OE;PeMxW5r#jXgb;j7w3HJjeqk7Sz#@8%tvDtkV=Uq=O?@VN9!v&h9meOI zE*E!x#uv2Co1o0wAi}w@s_@n;E|c5IFe-k?-}CGOAl*6lSkm3U3suBKVy%MEX{kCT z>3ze*d>x>FWiWU5mXIqA93KdsPL*afADe$Gey-D~eW@)^0B#d46_iD;R`RYjK(6sL zw!7{Yy3XC1(_!G%&Yo6p=hEq?1Zj|e!2^fXZ95iCtu!g19Aq0U61`(mxmbxnOBD-c zhcG&DR}6}R70C;v$KO50CTArKyA8q|)rY-yS-1DXOoto*=jPTj-t78eM)dwn7x ziJ7V3p+KI)Q0`$7vO+-hNpVmzeYomhF$qu}zxG^)HTVrBfXG1Pz9Smz5?e|NO-H-n z88>8coT^NSnS^__5qa4Wa=|+HFVZbj$99_ZB2*5?V+*kV@oOj5| z(dY?m0BT^%Fiu>3A#~;R({$LdCjkJ8rSs8i)#yFTNCOJ#Z6=$vSo&a69!6iYCam0q zEyGH6y~9I?xD;kNo40wN*jR|L&Aq&vPE}(eg{FTU(kRKjwcE!WDf+?R>sxsM zimZp9M=0xB{+bR^X0rXaa}0(^ed!m$%{*1b>2er6dLyB5Bs4YzwO$E531vxJ)*2RY zY!;3&>qe0CFkS%{9d4n$V+1uMczlf-sFDXGgSbGv%#W*1go8AShL#Wy>(?Nw>>m_i z7c*JP4_3W7Iq)s9nVc%K+RP>3PDv3>&AZEfa6^K;7cT>^vgGJST|uEtkBf*TgsTGj>tlU&2-j=a19Xi!}kZHgJ30M{kGlN#b6aj|q|%F6^Jl zsoa+K^6bvInv;NtO#=dlFY!TE0tP!7@-hZ>?*u9S$R#+LG&LU{jnB|yt#Ua?$Py^v z$5yePv+>|PpKvU9QWXq_TvA8Icbk|Zbv>RdG@XS*UXgzV)a5?{!k;J!ER-h^+BiHN z%T61#RXr3%z4#htI)R9bzb#6`nppD4Umn<%Mjk8Rc^{tSFz=-wch-a-_Y9c0Rw2HheCZzfbJ_Fq}wmmRxR;f}(NJxH!o!~wUPg)L zyc{bnDrz1=u)jIsUk_q#BO`*1*D~OP3BdECZ2#1}mLwtJ#cVR}FKPR>XB%gapywI` zH?FHhs*l_3EOuu3ou6DlYhS!G7~U-yjg6>HcKcc0{)`RC_C&!o|JP9XU@w0O`(f2^ zK$AT?T8NrCPzt4d^V#?G8CwM%0YtWlk_(WQP|Z_eJ4|5AM44&o^pR}2Wp~w^pZO=} zeFtIe4JWud$>Z>pBT_(K_*>h<2h)o~avJR@i%N{EPA1zUSXPK8NIAuwW~Pdr8oc(; zL>3RF=A*l}rt7L0<^AgY)A$WB>XZJHSVWd#so6}Cw*oGcHiY zzX00ZlO=wj&_PRkaMSDeuI@&DlhiTmp2vSw{@rh>je7N`o#_=Hk=tv%9-6UvGtl-7 zxYq4Q`q#|-TvH?g!Jc<%0~2^iRg29Dxc*;uB36W%Wg1jUn{z53MF74@=vy`8ReUH- z3QnHMU#)*ASaaj%<5KJH_hw1!cW$Bc!;V~ebuWx(R0OW;+O^@hC_ebRLiI93LXN=ov zQm>}(q3H6$qH9WTFp!H;oVYIn$hS__COPkem)b}YN>*yxTk6iq`BA!oo`Q-)o@8Q_ z%wjzB5H|tcU{tE~w`GXUUn`1|gRIfFQn=O@5qrt_PvBx{V35X2)kv#5bn@7A-L6g? z_rCu;L5)Y%UkfM}e@l}YeOQGIM%A$M@^CEZc>EnKR*hqsN7mR&U{qOiRR8+f%SJ!K z&^$*#QZVhy5AuD%Cdrnlo4_jq4@Tv=;=t@*??YSkD#DWUwHVY^fK*?f(S7D`iRR?p zN=0G4@cuYc;%!KrK>lb^VXGzm$!libRWB%;`fx7&Bo|1Oh6yLJ{~am-#T1OP#7kqP zV|3rQq=h`~=m2D(mGHWgG81_P7>{m7sk%xwaT@=YXY6fqK1ZH{ysp7B&w9z%6!`i) z1--v*QpknLco>&`U`rV;-=${ui(J87r^&sb|9V7#obm~0qzl0a9r{D@M|5)tih@8& z-+fPxD(_zB+TimbgDnj(Sfs*Al{cSyvN!+p`_kObS*gR=%U7{ObURek1U#>7wKe(r;f^|B%2w>Dko75>yeC()SDX8h5mm(tjg0d5Ga54D~Nu0?Vh&BROkya zG;34)sf&H7UCI@3S7DDJ(is$fi-!zXAN>S;3FK@H8 zwe`eEsK#X02dHBaq5t<53>1JBJVvBu|5cU70n)z@fD2clEyhB3fFqo<**D+;v+h`V zml;^XE(p|Wz!<`O^K~7OrMOPC*D6M|0BY~u@7W&3v7JCmqzueJaYGA-ivgdA;wM(`o{jca`Ptn zChb6qw66lrCg7t0KX3d;l9*r3j-tmY794GVs_4(>ug}a<^}f8n$07DD)IC3KrhhU6 zW4U~mJ9E12k2T-B&BnQvc2M0I5$W$fUK=;+(+fmAnnU%Z9|)O?S6)v#i@*X9u@Ndj zEcg>#aF7D*ou#?wWZdy3j)h)_({ z6XSRzK!G_rF5GWG@cqA&(BrXw>?>v)c_%VJhVN_rQ<22jA;h>nv-8+V(Qe6r=aG5j zVD@V{PiPa`>t2oKs5p+ok-~-j;?NYwj6@>Q*GW}qyeO93+IKIAK)gE9-SZu=8b>Gt zQ1I)D#x|TSV<9hXi#h%)sVwW~p!swkIKgw!K$%3-cSKsdXnVL&S66{unY3%W=KJ+; zNG=_~R0>T$^C?}^hV^Ddd!^g`1ymSz?xYT~(1W|mqPlOW762uYbDUlN<5Jwq5WOL5 z-mFN5o@mSW(n9Hoba{Z3BV0Fw({!VoOlCj(p0_G5l72R8@XA#dTJMT-O%vP|1Iofc z3TNa1PjGFM4xjstCC0@)k!se`z+qNI465ep{|dU_N8HJXcFLomCj}h%?b?3z^02aO zRs)XZg-EcGuTg?DtDZTF#lmh;SOUCZ7r?pO+B_q-9yR_D2U zM)PZ9sv%+QXh2Y)O!qojb`I69Ptdxj1}+Z&cgnbbHk5HOd|Gb)=+PVQN!hD7L1~`@ zhrVjOgyy~5$x9Kvg^(wjJxj_;l3ef*7ui0}kxL*B<7BfLKW#1$$;?40yqId`!MgCi z@$feDzmc!Yg=Z{JgJqk<|Is6+^yj;(!{)|`4X?M^h+1C=2w(;2B|9Z_ z8rltMUpE*exndxSHJ z(!hnI)X(Ww>WKe60xwv>?_}J6u3r8M-EALsV&oR4SUG7RJ&c?`!*l|jJ%LahakXT` z&>991ER#ZR=sq3pa(-Z83HIJ4)g zmba=<%iVTP@vrT1K>5*KdcK)ueu6uTNg@+Q;{HBInfBXzL16+9AkOk>sH(Dsdd0^wg&;&+Sir06D z-+GvJK1nwS*oty%Pzs@-(bv~!=X47W0+i)qq0dJI5qL1qv!vVa`^vo1sy%*q6X4`3 zqAC2TKw344Q6^XNd3K%E-Oi?i8awMD-|R%LUv}wD-%E_WE#M(Rd!OxKd5SOgkr(T`A$uL4z5T+ez_c`4bXZwS`^QY?S5)HNm_eF?PWyfG+w{00Ex&(7PAfnG1C z%j9_tNNf9_g`-Ih zqiJ8qPItP6T8~#Ugbuc6Gk#0Ik}|wHON;b0&pOatKb1Dzmcm%CG_Mc=O$XG@B)#LS zoIaFcL$!JXPg1lfzxVM!`Z)ydKfF~*7Wn1uTkv)$Yg5zC&I|2EKi}<7nawbOb?cTZ zH7U}ahvIT5^*fzc@G^M288zhgt8&9tco}BvA>ee^fHc~4JDHey0w36hAa!|7lM7w^ z0aPpg7n3$cV8e^ZOQ|X1ZnnUZ!=fk3`ZMRE*B!fISsPGV>P0tI1_c&_fb3Fa=4g=!scO*hmQP&t}l>eIUOq>J}ueU6;F zT=Y1+oPgFdzXpxd_1?@N6t;Fie7Z(Y;BiHVyxDZS!Dq}YioF-H@?c)FQs{W*iI>sc z%9s-Q)yK!aC*gxijfhL0m9mK9YOC+xzXQjU^t|TXM#pC6hLUkwvrl^Lh5AHW`14EQ z3|NAn7(Ul%dBWF~M;>X|jpatfXe;`4mG-Q-ZNGxC4VzxpDfxD(Ovc(ti$YVuAvD3e z*hkRVJE`sI*lrZ#9WI0;(KLB%eQ3e#=!?r$i<^}VlorGhARxDW=8t8~Ho~VQ)&!tvlSCF|99oDwF9sGHqdWlsd_G5c%R8({&zAFqoHc%?75fzL7!5 z?B^vqpJQW(%I##Jhx#qYvm^YsLwSp#J}3&o0pyMnsXV%HKtv=udV@w!OV_i0dY@IA zr{H}L)x-6gCD*lp4<;VRcR8Gj5-_ulR61DKo1UpB3TjC4O{Wbi-MS*G8rbQ^$+gu) zw98GE!Q{5Yk_&MFO1tKMkLk@uu_lx=FYmJI8I1Kk?N1!oEHyJ6J`J*yP3|IpOYC|q zOA?`*uxB~?yR-epWm_N$TTTxG-x@H$dKUvU(nsr+T%)Wwstq}KNNs#sfaqCDxwzgb zPVac0=Vy3J?!LyX+KV(C^7##?0)#H%Ee>I{#iH#4``B9=$*+N6P{H9@Y*>0QH>c&d zLwmv+uYWqGXl8q3wJE^=%dR`}QdoWv5v`U$koO)@oZsE@zFW!-4mWfjwO4@RTSlkns&@jx})X3Gu(jf0%wrz8|SjCO+N$u`lPXiW+d2mifz!#clm zMOY;3pd3*MU^r6J>7byqGFCcp(J4uq5(#Jt!Z+f^nlVZIYZy{4ybmS^jfhDZH)OHr zKP3N@`*{82dV%Rk=r+$ewVQf>^~Z=u2`9j0RL)J3{w_!2`D9YSTf1*#Xp5;+rLj{D z>Z5Ysbklabt8jP%GqZTJ1Zlge;KKJf>7OFN zM!jFV!)KoDBWM5W7OpbOzK$#W0uD?J376~R%q1Us`}LN#*tJ}i6;9S~c02{ppYY_F z=3w$&dho>HsACLUp4$47XTrM>6wdQlq5qwdE(LMeGO0(0%|-)qiZdB-WeD(mkB;yV2lRS2BC4B+ef1>en<~Jxq;PB z0-^`{QlVQ9AhlZiUN{bvxLo3J!R0Q5M8Bg280jBO zG_uEX&9UNc6ZG*LNmYoxs$DzK4UG?>NIy#9NL(fPEPa_Otz`%#h;tIQgaYE@rlvh% z1~*DJ$S@ms{>Vg@+9HpPX45=`o2`Cug#SSfdecOWHGk`&Z(*KC&Aq9y34M}hS$jd7 zj0rAu&%PxOmc$027TO}nS*u`3@UEw3*5+{NG5XH+ZH_%A3U7bXWCr;8H;^(o*C(AF$u{%XUzO@_p0P@%~J)XBx@jDZ$ReN1|IC7Ns;!h)W zSkIY^#X!sl{F4&uqro~*#FDD&lDt4s)i~g5o+X=%n!CbTwK8;jhXK60>YO9({U7x5x+rW~=^*<7Ty`G`pV# zZkEs|CkXhd%(bp8jRx<-1A54{4hFG43q01_#Rou*1rW%F6_W52u*0U|mGdXSQ~f3Eyp_Rxu8`oIIJ0Mf5+~;o0I%jhC5s_=^xe%dtEhcEb3GcpoPw4_ zXW>0hyUzsxpgX4Z8;yT~$oh@;tT34@>0epk%ORT&E9|k$@=pi)c~3PFp|$l1)^&2^ znU^M|pI|}66LhYwOLY&qk)LHp_I#aJ`!nw~vZ!yyY!p)x{?AiQNa4QAVrW=*7j3ZxYjR8=xLL| z;OapjO1979W-g(jzJBY8rCNJJd_37(1qyr;D|yvl4}xFB5z1Grvftz7Ld`COxli4JzwmI2^?Sm+8Q5v9+jsVlU%K4_h~pw?j6Whp>p{(2P^&j$)@jM zQVE)0GVL~s;N$*=oS2AaKjPVWwSI5%*ra0qt~|wyG>yrii5Tn^c3C5wFxeOh;2pt| zS;zAnZ=>a%>hK5_3Hx>w!f_p=Ay>8evc~t=s3?Krh9iRZ;4uoypXVUub5u5D(`o?q zjXd_Pyfkfl^PF{co!(7DElQ64a%u&3NHprgTqU>QlUJxQvcZ1m$-RkVR2z2fBF;^w z^*o%|mw02ou3Ky-7KOrY6OV3J0+DL{(00oGuVr8TkbQ+(#{$jMHD0Cwse?ckJ&73x zWP|AF=*s;q?B^`(^3Z6(v^VT|N15so6VdoAg!oK4{pCbstC@<1*ppulVJF&sB{JXL zo=+1>%Twq`5iuOqyRn}4?8o;Nnnhuzhd1U(Zi{wurHS4ykL6RX zOBM?WEaMU_qJfwfIau_51oyOTvzs~y-%a{%S_R0jM#B+^>2oy(1XE?2$;J<9401s@ zcGlFg&Foza$`ZQrq9IEGmasjbUP*{c_ds2aK{aQt+E|+7c($rNBtKAGI&Yv-?ajyb+w9`@Jx5;Px3RjnD#_@AK#?It@y#lJy% zSk4E%8@qEgaMuVT;P9EM8JG5#qZSTSo|m{w1UiPMY3iq+3QJ!4Bfa~}|Csz~JWWS7 z$B5eNInLZ$x=%@!uQShCJmFOlU%jC+{<|eaP+84SRh=13oU1af^QqvrUs>iw&FNNM z1BoLh=%KGQ590xB9P6L@(G5wtPUv|W$tLq*vtT2elZSDy?6!(s(I`Wl83Qqfh)pPp z>^N|Yh)%Ce0kPsG0Fkrh=ijnvUD}-^0BJ1T^SI#2xD@`6S^d{~ZCLVMCPaH}CWc30 zYc1WhXaN%!8uO}&SbQplTCY*3M zu}?kX$70iyx%_neZ`bG!xeN|z9KSa4> zsl!=klzj&o_0v@!{AGBg6T+)H`mz&bbF$sNw1!dod><$n(u3bUuuX5erWA7HB>Pi+ z{S*-iZ6N9eI3f8ew|saJjR&7z49CO*it=uWnn1hz^Xa?hUTe8Co@5m|k9<$|Xn9g$zPe8d(eE}0vtVaQ`EZN}bR zyMQ1k>93@PxHY^-e|(3x3d;!PMZ&C0;!G$ zn^#Gqw}(HKEsZV9@VN-bauO%wxo`K~groLi8ZWHX$hlr3Pms9rN!glD-T@qz>yQTY ziU{@*Qy@;8g;QN|fmr_)|KqCXY~JZGa@G%79ba<>L)V(VK#p6f7y;cw%~Jum1)|6C zdPypxSrL}bVz;~8g9;GXCvQ^V3!6hftaSxK!KQS}a3KZ&w-*3b>>qQ}C=qAdCIBJq zTrDv-3qBMKSRGS>5Qwa>XfO(ev5SN)h&}giJ65k#fHA#FYQtUu-x|?`?RVUOgzrw% z4HIKm>(!N-gbQme8JM+CMTa)=aB4fy8wtV#F^v_%qcinKCPEwh?Kq?(&Aai!Beiv( zGO>TY{5cJ@w={`Pms{eqjy@6$!^@)(H_hbC-~EW>ZMUT7kI~Jkxy@Eq(PRL4%2n`= zY|s+XuXl+y@R9YAVh~K_fgu2hkyMR&HHo$_+>T+_hX8a-0*M?1$3nkRp@{U!2rhoL zP+btMe|pvfW{XdFdQ!G2ZQ5j*Hxz1`Y0-Zm%&9?gyCv_|L}b)EDC^2&N9)#L)c6Aq z1BQ>P(!P?E5Q<$USEhfC4+vZvzR5F0_X?4Z_IfJfE7|K|2xl3lgM?2=*vlaiRwr7S z>ezp`-6#jmj8vI?matD}<@9+X)PVI4Pv0BvJ`hiW#@a_Q^K1DX>v*YKW3}NBy}+A-lwFy8!=0YEqtIe^zjj0~h`w-cv)&(QaY2$x zR11nVFLi3)jJvPSFEFwENfZX#w=|5D4wK0THhDzc&D8O_?UPe9v?7oR0L?^p>tht; ztiuaI^iusAAGNG^ep?QP26a8)8-aEP-#2N8B?jL|(U-?e-f%o|{u7c1$s=9?8?V?T zoN;Bks7|dycg!oX?QWGa@XmKfTVT7xv0ZR{Ax*8@C&I)dM)Q<-F|T=G&w|Xg^VJZg~k>5+>9bj z$Kk=DfPg@c6c0~6Ck?uZn3q<9)JYgomy548>jjhd`l0S1yg%2;q%HY* z4D;x=c7l5V4hULSs~U2=A2QY`rYtUA6yX^zAyS8Lecuq2XqFqoHTqbZ=NWQfb5FZU zYoNp8BFvRq$o$pIY(a6EMCdj>H>E?f?c z^KbTG_NStQ95`ja>=@J-xYvyZ_JcmGMB2 zti=GrF0x$o`DmW}Y)9o(5w0C&x1apcGs2}W*qdU4@=q_{A1nCT9)31TyJ_$DtMP)G z-RFOp#yLx%AOE5x4sziFbDHQb#jX8cGS*#?JNCmouOLsEz!MW}I1Lnd31?jDg8uor z-(v*TbQkVCPAz-(1!p^-uX~f|6A6NIgAPKTj$MlPz$Hj|t6hpNDw9x3ww;SSj^zrS z>&o#XfK)l@e)P+7JMi`Op8JukNjvDT+ug-7gV?4Zl0V|5bp+#`=Gg>H7(4Y#YgNxB z!{Z+cGZ+lJolK*qcK5vdVC9vEW(@{2dC=kY1JTOTLiI_dV$1~*(TMw~TwG7O++-sZ z*;sI%1O4^e&nKcjZ8ENnTwYeEuE0ul)C{{@NR@+i!C*r%ny`fj2$qC$+GU^Go#wsx zhrT9I@_P9Q0X81(ZuCVS*04Rv=lQhU^@7Hh84{DW+Ctgs6|tYbF(-L03vCy7cU*7^ zD?fLET#HHF*30RY4d6C``r0hIoT)s>}+83LatF%<56S|p{S^V*Q2osjyETi$IXv-jt4 zG~G?`2?U}mKnOiOJ=-!l&xI5GtA~^fqS)nh*k2=PME3CVw)#quG!C^ScP0c0S!dV} z&ak#bx{r2(f_ye%i0u2YRZd7{IYPct6YHx%b(VKhcOTrfq-(r>a?Me`bMv!RJLPt! zmS1%aqFzY;brFKgJk-H_9Bq;4f^kSe^DvlB-thfg?Ys1tW8d{=)Jj1N3byin-=p96 z)=%Fr!+s{|c0Qo#P*ht!xK*ko!(4Q(ztU;UwT4}2yYy63*cTdvCE1Tyli^yG=>o!* z_YLz5AM>|fv;C%F8;1}3tD{bnA||P7UTO`+Cw#Txb2J8=mltwl+r(`#6zFBgd0L9! z^?Sz%?RI$daPl$}tH@K;HsI!wQVgZe8hi6_XaUd+S!2RNN-pi6xOdabR`T*}R~Oq! zbsvVHjbpY3NC?S+FmDcQ-y;~cvk%I33$F3wvuS-x$4iAV2{{qYBNW@Y<8sRPz&u1_ zrDySY{y;zOq$8vF2HJx0*Ht}^x5Qvq4pMu0f>G>mIwBx@}lV zABScLPG&yqDEN)t4!0Lu@tBXC<+7YEY@B_9RSceIqSIoC_ z*UjPo6>PR8@Xy6xpVV;y7rzRjJ)&bdYO!CJnZgM{OTz{u6;#`$7yaLxc%?>EHC-yt zsr>qnk*6tLgK~PERNpX&mVNC&!YW{khY0JQ&z9|b1ftf1s>m2$ZF1j@P}it2*vN4( ztejrh)9-hmu3Tk*fEeYi(imI`n=};MFZaa&DRF5Nw*RQw*|OkN0e(s&Ru)kT0C_Pe znMFrTfW2?Cf?Psh135%&x)k-#T8Z`(>>P^^rE2`trWZ8e!j6k+luOIDz=gFkEi(UA2wyYz_frz@c2lkB}%=G<%BhCsR0 zmcoVwxC{jB*{e5GT|~fzH}0RlcyQr5xNw)py?&24JIC&fG+ZA+Tv|79wY1MM)vqJi z`>}YYg-&7fKePcLWYk1fxADN=b04fpG*#FQz2NheybT#XAB&2G{!9j_Er7*-OwvEH z4!Q!P+8bBr_#XJKY^|l%4K})GiG;YZW-6)pjU?Oe#AJEC^%1QQohr0~am?z*e3N>4^_9g6fp*hctJw zG&=~CReKNUe<_!>p~2TgethZ0nd!S&la1;v)lK|Z>#?2w zk;?`?*)e*p55EA4PCOW@EJc_P5jJ8jFl#M((5k4dBcAi8k2`fhm{fK-kFBW+66Ahu z5Jo?(Jvwqll{UdeFrg3mQ8{63;?~;k?@B-g>75u=8v%s+-A!i*vGX_k)o51*&Tac>n+2$%IT!2j(78QrNMe={oa`)Ip^U#XSYw-y6uZ z`gQy*sjsxVS)n>gQI&%jCqDteD}a;H3Ll}Z3Xgtez6hU@tM0Nh#(L6bPQ5~fF$ihi z?jr6g?X$u7`SE5segE)im3s=fN{f5pstLrTs5|sP`vXrxE3aoNR|LuU843k=@rSyu z?RKx#!FY#(X93k~y~~U9;#HD8fTRF_odVZ()QkrcZc{W%_G|CoW;!tiRI|VRRQTXp zWw2lt$H2>9c>m7tTE=#EWeh6p-$n}s7|H&|Xz8mVL|1qBZ;SsPG0Cs-ms8Tk`Dqm~ zjM`V(pL;=p*2t z4Zlkl!Urc9P8e?Glh>WI-j8v3;lQwR4b|iacZ?2>eDMI#^t?Yd>v-jqT z>~Zaj%k@9>`L6%}_s`>@9=Yy4=e*B(&FAxZv>d;K+M`*WlAt|c?c5=T*r#Dm5hY0a zR+ATr2v#wqYHpsB#ZA&Tg~&480sNDOKn?(bLhF7HoHwhiO1|V9{ZE|ZHnvw*A3*0` z_qI(%!}n!k%2+4Sds*fLmE6#8=R>~yh~Og2`D2lLG`Wq4J2U=EHY8=r(owAVN+d8| zD|O52N!)Q76tLNfU%>YN0A(j#u*|r&*Asa`u$#pJxr$4B$zB?5A0(PA6DUq*NCJC9 z`fZCvB{OOUX+Lj?s}C_>g_~wq<_eqx+~iW|zA5He%KfyJOSL9`DDmNMmDc6f z{U$F;mJtJ6kuIIOpQ`N|$XYEF{+tYj?j5*&IR*f$W{d_2ke@}A`7w>fdZXsE~j z99e;G5g&)D{*c#cX&YcwY$<-0`4XYHgCUs)iHTp$dFvVP1a>*ZR@BC>@x~B!g4S8? z*vwtTs77wS72ysC?4?h`&L7p`GGzDQ7vOqESqZ@VbtVRzsbjKV|eANK&XJq%TtIr7re^W?wmJDR+ zl*E_cKIW4ROX&gK+tNMC2yXzNOut6*Vw96*xZ%0!y`eKj;<;MRjuA7N-AL=U%Zc3j z%M;F+xRQm3^spk`(q$69l2ruths@RAqbzpJY$dc6;zgg=q^F8r*)bRohr`*NX*Gt*sXB=_? zq=a`TtWOKTYNYE$SrX06+foy`IpmD9JsAO4Wwm0z0&I;8E0w%TtI9JVDXC8r`UAFi z@dx6_k`hnO{(j5Wk%(dpLr_R-LXrF7#ekD1 zdV1_27yLqUo#dAXbE_kk0aRarkDotinjC^KY7W_}YG3LU*!LR@i2QmXbeqLVQR(9Cw5kmFIV>lkndL2lKLr?ep-vQEOU3=Ri3r2S` z+^6c>6VGClPZ%4gD{yl3Q2WDPjc$IYn+;ho<-L;h`E;PDbqNMA!JFbGS||9%Q&Aq) z9X+jo!+TGE`Q6FNfJFejg5i4S^M6xdFCNr@?BCew^cUZ8x$lMeoxbZ&?7hs+n}-oj zxhK2~X-kF5f%u^k7Tb{W5}V9~%-Z!jkg}h?^Il;9_V&q&p|}qf-&}0-^YyWIHCZZ+ zbulQ%v>^?HgH;whi@r^EB9&16D1jk11u14G3 zmuuQfCQH}?zNYKSmS-gH|L3uxb;wbGupsjC>*lV(#`fU1c>O&~&RC~=lbFScRu9-fl@=OnXP-F@c>k2HAigC2NBN(&e zXczYYD)R6(k;-jc(Q)r;JH2RUCM1S^WIn4KY0(_`EqU=dZ3DqE^a^?FnOdnQ zGz(6Ex&6uE>eBh_6`nlGH>eRG8X(G|5IeD;bHdIDE8$t~C6opx4B&;Z9(G*vC|7UK3?N-zxN|Z zE66>)9nk3h!j%K-tV$jt#Pc@FSyDMtvi+*CfOGP*Qd;GCQ5+`YdnSi9hx!*M1%D{y zob5Aq$F<2eF4BLH(8wF)Bo`qzgd<8j0NTqaV&U?}7BwGP&0Lkf7_VC*I9p7e>5lEI zPCn73n)y=`iHXFAwWe4kd4(^_9|>ZWhQ|Mk%i`Xgjc+mj3Yr6)=KCiG>D20-7We@9 z0g#E?0LyuYb+W^CmG~0uvaQR&PNXX`RPWez(WNJ=-k(!*<+wS1diVWN-(<#XrTu|N zO;#e~DMw|N-*cZb!NNn&-8d5MnfVez*!ecgA_3@qO++hSECd;3s-)${-6m!U?wh-y zw-iP)#`^kM;PnSgo0v_fu|gW^*_lfHk#@gNH0Nin_-=+H&T~D$U;j9>IcwA}vVZ&R zc=WEyCFWTx06M$`8!y;rrz2@i{4g~Pkh-FG5hxyo7~R;{G}yN8T!7N%e0h<+p`f9p z1MvEloobU3=wcUG#R;pSPHf8tPq;XDo83LR8;zgovI;MioE3+qUYDg09s7j-Dn@<{ zxn9)Aez00>Fyht&7&Q6SnO*-vf`89H9KEs`GIcSc-cu;SXfF83ZTL|$7W zqOcPY>T26`BN=GI@eaja*8CsfD5;7K)z@D{9|$FBGha)dIp}@H96CQPn!MA&=2rJi zs)=mdfx%nQxYfH2pK1J(zvFg7V&ZwKhu!8Y9U2+;2*0r9MDi$ol>oivNE4L7X5o?- z-_t)wmW%Z?kbTdcia(nJBE|-05~XG`eU9avpEZ!SZKcz`i%NI{zvDBXQEd!9B6~)x zKf?mZVlBs>BD;$@UvoTd1|MdtBBfNK@^KjkRoA~noNI@ZS*jN}7_%U4z5|&Z>dzN> z`KJYOrxgnbL?K1)Ew0pydjO69=>6c02on;wigz-RBC|85hwIYj=b|6p_@6n)&CR4N z)B#>Q{+ZvVQ((o+_U*v3nWHzOJPS>K-iolzyR1a4wEYweIG zBT5|Mhcn(5Nr84~9tGqH8V*?aB?h$uGd>JCz2EK(%wnv{SM+vtKJsGdGbI9I2Q5+lulIo%jzu)z3A3BiFLv6}FYaH*1^DnpgQi4oKhekEjE5t! zVW`dPj)hsTYY+%M-Ka~4tk%!oBp$}IeaN^g=orR$YpK)0QaKFpC+?*h%q4QyUq{)~ zOj#nIe%Mn<*?760aUc4%#Qy^*#Sn*~ekXDJNt!E>+r3cd5*!5bNl{OQ8ve_pl0e0C z_nw&5_ix1BxAZdsx%YG;4_%?n3r*N5CwaalZ-qfq6k#ez<4(ey#Ks z$mOil1`TcCAL<(!+-QZ!^21N@DoE+P#CcebKXbU~H6fxGeum;cPEFDCsi~Cq_mU*V z60Bl?XwfDKZ@T!sIF*7Z(7&Tq>5pl<$UWkN0w2D4M`!Ms8Yr|xsdTH;oq!GhMkwH4 z=p29_^&E}PNWXUb)AmyRRd~zCFre?##qPZGqTI^|HQNNlU~I1=Kck}$IOR!SFU3?a z3P;paheutca%`?h#VH@5)Vb2%Reg_nQUs`2!ktBTg%hZ==_>uCSl~_cVIHnyj5ZN! zcjR5Bth~GPq%B98jdxzCaIju}tLs@doWxx%J(FXv%zXaa6F4j2IBQo^iu9V-O_m`$ zmzC44{*K~b57h^S98Bbvq(cgmmIyrlWVSp9yGA)r32P5`+#;{AWOu=HNzHE+&$9!v zu*HA7%@g9V!NqjA>>VwjG4ih>#z}nq@9>rw62z?xYEZ>>;%CU# z(X77z0u+JJ`jkHOulM`=_unY?H1-BHi<6gt7a0Kv_J4lRo;P2;@o~>FrLSSSe{ErzSQ4A)9E}xF*YvDCoD;QkO0jQzD}cFr1~Lo&?=$Y7 z@S*J?{?+#t4=bks89o@9aQ_neM)UR~SE;oxoqtJ9f6FR=zn`Z6AO%Vy@kgC_e+92C zjN$N~m}>L}7ti@iuWBTJ1=vM8nqA0S7*vjRX{F)}PdVz6#5vet3b4QC|5$DAKW!$0 zHU*%_@=q`wULIR^c6+PO4UxFEDmY@NI+q%|vnvCXAWbJ1BK7q9Wfu2f3ysg-Zi-G2 z(nZPyXDA#u+QKd|k2oA&VTt99FI&E9li=3OF*6j}y5KkY*N8GAl9MEYPMyySA`0Pw{5ST8Sg&)S5qu|w@+ z8fy}fxIgxrqWd13_|5BA%hGFX#JzxHd2h$WwL$Ru$D0r~{&J+6+S>P@RO2wmmbDAd z#bw7OR_(mmIgVFALp@)2v?~pqslWdG)Ii1EMUdD5Q71UiihOVg95`1CL$JiG2G4t{ zt^50U+_9089ln7ui767|%0Nll3^_*qiu+W-{JFl*q%>y9g@(kj~d zmE+!_rS=uqAJ8h3UC20_jsG>KDtk2&!Ci=t=#V6Ogui9Eu0LvJ9!J%%AvzlBdp`0) z{Rz}+N#4Lgw)Dy0H)i37U8W!UE3?|_$3r7U*%Y^%eZynw?x|zWyo=woGf$n87ZB=O zEB*F5VZlpIruM08%oAX7D0+!FO8WqD*OREF`{|m*xq_aW6V;Uv72+r}nWaj=PsSU! zj_I_Cj*uO*j^|>w({Ms^ZCf#=-6WEEMzy|jDpN^^Uvb8JX67;~F)cf!u;2`A4PNoQ zxWmKq(jK58N>5n%(bXVxyx#@$1{F6$Q0{ZldV89u1~NcJQZJbfEjIC2El8bhvQteu zJcxZo+qdgDp~O#!DaNfK8fP}Gy&3$Wt*PgZ&%Mfhgx^|BD*=rGGcxkk0t+7w! zq_)VdY6;|8V$Rb({D}0oTe4u1r5VMxTIW_#%AR^#Kyn4fGjQorUDNUFes=k~Ut3hI z-@aSka*RygcEH}?R(TOkyD(x93*|O`XU1ReP?LCcJ8l$U0ampF&q75!KBo{1GF3Kp6?dPNL|*_ zb8>BUZ;SWVtF)P48xoJWvxAm-|41bx$@9mQP0jxO9PQH1El@&f9J8riJvutVVP?$E z0lSK%=X(Wz_YnQlA5~Sc-k&s-?jspUAC6JEG=pmGPwTyUmKQFExCfo1IPva;yJ(LP zSdsiBLni*H;5sjFgV^}Y6u9q+{TOr8@#ph3OYl>o^8joJA%OGgTx z|CAWzqol_{psr}Ye?=-I(c}Cato<77a@0U+s`2BS=lw*risZ<()9-Be?;j_(Pq2u3 zM$KQN`PwG52*Sb{;S2-Y1L3Z6=9`iFy?|ZU{6Z0Jx>!H%jQHp6bUxWbvzk>Lrh#70 zIp2))k65pG4yPSGJw0$IMDs9(ZJ}5xl>=V1<^jH$%X^!Uws7MhMpgj9Tf?EFbG2kUk0R9vPVui6%Z$80_OqF7ZK+NIW2{g{1 zMQ;{RD|h2#Zdvej${aton{M@;~Cj07Psr6VTQ%ati00jObS17_;8i0I9N4C zSk+1e`-?KgzlA(Fr7^$fUc~N|*tg^k z458!`Ev;}3*&7jxIXhcy|0f|k&@mWR>DTn|IszReBuxSd^+9MC8A#ZnA?36XA*`!W zpipIAHDW^$fK$lcTEDt#~L zXTzfF{8(8yN=17j&*6aOpVQ8QlA2_Hh5lAgT7_^TOjBB_hHW5x(SG%+sRSFFUsNk?t}b(9+R%dVNXHZ2|u*-UBhiB zO8UrZW7z0VY1VQ{Qi+#p>7^a~R>F|0>=k3%2&LmY&ZN)7T0%qJ>OK34` z3|wo&LSL#1RoZnnB3!1^@*D99SQcuF(%RbWS7X7m`tSF%YZ&r9_o|0$@QIomT@#D* z`vEU?IS4QE`py>j_|{ev?%O?kje7 zVV?+9r%rJG0Zl1)ublyn#$@@R!y#49V}EiJSD5Ie+>RqV z9Bz|@U8A~H$}NX(<{X0_H65q&>sh&fZP;lw+`=TSITpk#FOq?1nq-OL* zIZy0}(cM;-deuG*PglZdQd1ZUM?nQG>ZU4S!na?w>+~ea1>Dqk98gOJ;<=Nx+ZFOb z%!HB?wj@Mr)l!Hu?heFP#?#Bp}dDXq6rJRNz_utmc-Ng$8KUlGnkHC@J?695(ij zeO7Y^S)s$C@&<5H8lf5A40}@_84?cnrvX2CJ0u!N=$1~X6HF33=Uf5upA(srnMrRy z-+30a3$`Y)gSjy{p}%zalR`P*QxJhtJN+8fUZDmI*?mh(_$;($wqDG>XiDpfmTg+p z?G0CQ>L`Vw>8b_#2t=hqwqSn{AK9TXDI_f+qJ8}4t;8vgop zWfBAPe0a<#tW*9v&4sW(FSYkzDrC}ot$e=fq0B*K^0B3{H>d*|0`h}2?DEIG1}3FF z0OmPx@dsRyX1#BXAr`+le%|=y< zG5^&X*^4_7H7`$s9$*ZKh)&xu4N@0^7b^W~&HM=3LndjEY|YlkQk+c=F*+Uhl!%u(0OUH9v0L=tWT8kLRi z39DyAC4NfnW;r_tIoFJ_EU|84^ziDIBaYmv94zk00#%2_J~4Xem+;+<-78G@QYc%g z15}rswPsYb zf8B|vAU}5o{QSU`{G5RZOma>plzR1wgwudq>2~R1#T*8-^4_@$d2_I>gkcU%LtSRZ zs>4FA)NcLKE1X;AOY^#lWnRq_Iy-SdE{7NmRX~0;7zKI(M-CS3u$6)A$jG%A3t{A2 z0OKlWJ1r7`Z$uD#xn||m2ag`T&&Vk78TV+?g=ByBpi~v}*&jSWp}LOk zOM8lLw@|Y?yUGfOlwu{}dI#pEQCrhPg{{#?heL*A&J;P0SDD5us@x-es}bBtn$&z!q%Y z{7E#JM*=24>Q?s$-x&9zsXxDj-dAG16c2yXDK()`4u_sNfvrvieL>YBDIHB`eDyCs zKM~e&7VbNY*EN|0qNw0yzbmB-e_LRh$Webnow(@vv)Z0!RfX1$*!ZK!O0~Y|7?YtQ z9RwIwZQ!|f0yIYO;S8mOn|1fI@x2!XL@CA0dQ)TlP##uVm_%1kfMGvP@=ii%A|f#A zH|`|iCP;wp03v50P?$#-qrhH8ljHPqb!)$To!^7 zwik8-W;v7>M>m-MG#WT0FDA;VR~=s?K7({&Xuw$0JkP-dk@Zg~UTxp8Fgj{bq z^gMeZ88Q^etD^2k{ieYm<$X9KdEIUrij^cIPH-{&!Az6hd1Eg`gwBu_R#oUwbbIbL zWTgcILbsYZQe&C;0oq}EM!rf(T@eX!;tg7W<(5GE3 z*89ly5Q(+*B(_}X6%ve&I{b^2?bJ!n_;hEUSb$T*^;3!%DJ%%H9Qpo3rD%#lco-4> zB_!7$6$67uu?e(LyJq&FR5EH-5NH{~#y3smdZ)yE5RkY*OVIh&-LWr$8?2MkvW72f zkqQIZ4mP;Uie0n+ZkxVm!d##gp3xr_y(uAqGh4*0k(p4x8k@tWFhkZ^Mr3{ty98sqAQi&T76@1NjF7k`9`VJ z)@*gU7A8(D^eDLpeWCZcCUlH!kE;LgRP%=3?l(;;$jw~y*qIHeBGJ%H~8mzq+=1<6j0mvfobO@%oadcHKGgDzAPR+nu{c@9}yToNXLMkt0uOWnLvSl=0fiaCYh7=2E?&^1fe6 zJA8MNTyi#lW^2_Bm> zAPO_-d_{MLku~Q%YyVJ0Z`2d_NO=@0zyGf6=57}0v|ktQeU}p_47)bUBaJ!oD>Yni zpVdEiRAwAgftfjj9g%cz9GZ?>b#J6l!#DD%Q8W$;?FO}Cz36Q`DtTtbwe)iJUCW+e zrIS_iC$$3nQA7IP)wk55d|rt5`8y)J9dbqg%Z~kZY-K#M0I)NW43RKPOq9|pcy(p^ zQV)uZJVQZyMxH$6+*Yv0nd)>`a^ABp(+%^Dy3R)ev{1&IIq$FPTxFs(70J z`qn;Mm+PWM;p&**^r|ktfv1u#kVSNDv($KOOOJ5haacSyd^O)| zy*l!7`%p3|YSmqj|FPSSs5lL*YJV=K80)+;++)$F7$=^VV`#Yr7`WHsy$%+Oa6-U} z4+G_a>Z||f+{qw)=tSNV_qX}DcC81se3FA~B$I%q)3GTKOYv!?CI;X?17$&4)qN{^*)tH4=qJ|g$iwzxztbMwAvFJF^`0vS=g+fk{z zq!6Y+h5CRBagfFb`b;d~qL8zHs=)?4w?)|a4nejVF zsAj<`lopb<6VdE*$UqdixQT8De&2nu1f4|LRj0z3_;m)2)fBjcnN4=t={EgNofBOH>-Ts~<4sqAI#&Zvf6Mgz8{V*dRVQ7dFSon-93i;U~Y3@so&&l;OgipJ*jQq z>Fl(27VBjwGq25s1wmPiazK7fVQEhu()Ls?Emy0!6_3j;o&3&;AGOFgV>xd&@;wcQ z#T*lyu^6zUrNRJcZ0GC0ssJF&MCxYc41)OqiwI`5Km@5w_d$$}qiz`h9l_rz5UTZO zUxhn3;Bx3fBNi942HZsHJ%=^P0SXiu6%}9a-793oyb)2888;0B#Z$MWcKVw^W}+l1 z4Cc2Ru4)*6=8CQEo`kRLCS$Ye_o&AWTi zd#AY4lJ0uH+7X-1U9w18e&#!6BVKYg<}gv!V9i(chj#N);Aj{&N zDf$x(?sAy~D>rY74&ci3Md2{DKmB}E&-mWe@_EC|d12uJ?@zKNR%ow|pDJ@)kPl~s zQeBujUwV-d${luLZ@VucaDPiitD^qU2-ghnu7t=X zK^UPmyt3LW&eUtR9n3F@T1LF6nRA#{*Y9`@aSQRhGHAh`t(rm&FvDJ&w$O(XbKx3~ zZ1J%Om(^mWchcubCSB(%??P!r;6*~0oNy5@UVcY@#0=qDs1w8AT;>*{DUIUq$^r|Y!t`aX?C1zx4izSF1zg!Q6e$BeM#(-so z0qUNCFl(YgHaY6B`6j2}Y4CrVf?0No5aP|Okiub>2oC7zjrHj^kjJ#N`J6lCg`2)& zkQNZ3aOK@l)37QshQ4N}*MhC^vWaqi)16$tk)CR7@cckHob6v5Q-)I(DTj`2@eogKuca7VQYyHSa z)8*k&$4~Ftds7Aq7Jeu_17sI-ksM;Drbdjhh1o;VM@Ema%Jz44GDn1b%g2vD$X%&6 zs$6ly(jq^FLtk*29NX2>TwoFMLcU~uolxcAvO0>p%sOt|#vH77-x3#*WMDOMH!CwU zF(>DmIN=Q?<;s^&(^QcCwx1TIUKc0NW&YWfMr%uT3faeM&uv-Xop*X3T=T`XXmRjN zE&E2J(0Ec}9@@41?!g@)cuU)xF3GK@OMMxk^z{gnXi&``W$Cw@0Ls9_ofAM>cpw=0|OZAz`wWkttad98xOW)Os0MVeEmsS4bnaI94|qlsh!!Q@RlsP6ca)R9sL2h zgzRpw3j#BbJU@aGhj~2qkdKmq{alS2if3x3p*Tk(s8bgI{d>B~tGPSV=jE!4EslA8 zF4~-<7t}lasHxR{w0S)N)JjV&5xDjypky2LvzR)?hfC-M$92P09+<~Y&_t123I|kF zqZ&8D>2~J~eBr7PybN6OQ8-H%7<~SY$kG9j7G9!WEX%tg_c8Ju7bW)sEre86k12hc z6^tBhAS{a>{K{)eU^pT;x+DTj{pt42e81qVa=+&jB<-Di)pV`V#rzG^vq;;$_31Z1 zAgjpkRZq`+7q9nGh{*^IYZw>ww-fB*F1%UxU8wb)QI@+}9a%R72zk>Syr++TBMfm%=R3n#gXYLq)V$&9Fi?VfR^(59;{P zGT$fm11;ovd3pIoqweVaW-R$Ose?(Z-CIqsSmTw_IPnPOU^nmmHvKcK^xJ-@f#2ew zbx%W13VTz27a@rRD7;g<{RZ=9E@zb#sG7f`o*PRvsM5zdzM>Wxp23lC2HY*g49JH5w9oP2pb2TY@l}fcj_i;JToLvhO?AWxvVUfQq!Ehq5k* zZh_17ot9tkpJ$||zR9cJ)tafP9N0Mk>X(g9v#k-2OrYxi%b7)2SNQ&~ag!t(P=lK$ z*AY9{5f34saUhRBoW#khQlNmP*l)2rTkgk*jiIiWffKD0GN2c1!S+TJyWL<|hDXMN7fNVbi-O2K~x)}BoB4bU{7<8wgqw>R;E zoaMa8Wy$_VS&$7q-;TM_6lEWE7s zHoy>Io}jB#5DeH6Cnpp@QcnEIX!2~@l_pxt#mF@H=T zg^JQc30$;X(vSM?v58(wPLkt!I18mc_sJrb>z7@D|xsoQBQ|bn$Xt?Ig;Y z*~i}3-NU*r5pmP9fs#GeXyf(NP1;?%eS{xHU)jb?uCzu@m4c-T$7so_Yf490$?mvE z%XT{~=jPhJTNdTO0Te)p{Lm+~F*+asLqz8ZARr*_UG_mKZeWOgP>;rz^J^8Q++x2J zkoYc=K(B62)_`(EyWf}nQrj1|FJR`n^dIa<1Hrlg#W=W_n8r$NrMr-o*G3RCF+^sf zQa`wvii|>$ZJvtkENmq@>=9|enX}r1`}XsJRzlxY)nBF4GJfS2`YiGTuWT%AI-piz zGEtFf&vbSES*@?%wYpNb9dq{K8K;a`JMw`6rNWRK=aD^|7j8W9d!O!IO%sh2zE|wH z1EU;LmXH0%$^-0}Uz(MDQM<2raf4dE@eR=fN2)CEpsS!&ELRUQW= zImne_YDP&M__S>%5BGP2@6L?!rRYw+dYI_k@#)H-bWo(Aeu+yVk-Ohp+8;hIyI8;b zq^)0RYkl|!^(C;B&WsW)Oyy=LboT}hV zN3zdrc*+GFdit{H>u^Nov*@bE!C)TVIOpwOL-<>Db7XdN8(9ye)0JvQK}izNQN2&# zd{7>G%k$4`X$Dr)!SbV?&_#^%cZ%S#vAUe@=Fb!e9$ z6Dz(R(6T-4>yjEmSyk(ZKosP^Z{2IkvdeXMdy5p4+hP`E)J`g*VY^|+sBjHl&}mn0 zZ_>dy7QX#7=?;sIDM~t}W7($cg88}c@eD1-hIj8q^O~Y-KFZM0yjcbg`UNY3TL3A#i8%d!{K}AQv3qO9oN?yAmdb5{f|?4;?~H_T#WCC9M?*Gz$5F2R8E*U5PCZIM~KwUGnuW ztKK+D4^i>2W$T6frx1@94P zqmVllJ+hOTpOw-9Qftt3^=O(!#>N5O^2GXiXzgJdzoq+3wbXW)UEB`VrY6Ik3%~6{ ziqOrk*%I)qajsq8*pkAVtZpk&OdBSOH*%(N4Ze5QnnuTPgXCEF6W8)Ci=KuEn>g!bS>@W%lD#v z-|@XR=?YAqwh0p-wkCor;%H<(g|U&!A@Vnx82{~IPE~SGTNxB`Eig;a4+`W@ziIwT z-|;b2SL)?Eq*W;Ahtb`%$U#vym}(vNM>VGJs{%vIZkPI8CgstP z+$ui1FVpwHtaH^swRfBMphpa@bS$G zBSUlNO~xgJ$H6sFz^9(r$h;|@A+3wX zb9+|$;Bf3{F49A0l7Dj58Ia~;M6HieHRC@LYW6zUH-rq^3Yq@X^3=mp4I9B||X{2(u6D?8)veGk3l8uv!?VOXpZ9Yx)k-h3*#t z99O;c<2=Pm-RBLCVoV?7Jf2i?ouD-VR+9+AK>Zh5N;ebm_N^>BIJon}nWa~+Ufm}~ zq*3Fy&9({3%8JE_2JE!E#bLd;9s9?}la*b)&F(ah3U*6Z^>6aBE_h(3GW4&Pjq`vb z`808V#5w)(@{&$cVk>jbC!(CkOtmTnkskW`_oBbJ1>*LMd;I96?k#%0bV}&2f>7Vv z-Pri|qE#Y7CDK zVVqk4y*-#;(;_6abV)O^?5Y|BK5U6TlV~tAtfrwW4|>K{YB)vv3ZcIw>(j}C=GVzr z+oTtsEBy0kvu`F{dy^OqU8y-A51K!8e&mg0sK?$nThG6%G3m&9hw@g81GU3v_=MFl zo(2O2bG}~T7{gy{g4bP8F1=IS0W=)H9)>U%3t?E8MOv}3w#kJ z7h{~xZ9-mxjd-O(A;&@c?V5C0b!6BDSsjGgHy4?y?dThjV3j8xaZlVxlY}R{nmN?f zxl8-h+%(&O6UnthHrU{I-U>xXyA}>+g8~q~d945A2=U2Y|W;-JwNNeA7;xk@KL^ zMHiNbq`mJI@1f^~?sPm~3>mMOi7cwhp8iu+=jIHDghX97f5>FW4QJ2xuEl&LwF}*EY`|iaMUK_~c~J02p6lxa z{TUA^4tBrASmHfaTkYFSXB2*%so?rBFsCGRE#*eggY!k#Z}L<-43V_1c(EB0pM=g$ zv=F+TA>Vv9$zc}CCG!;mI5@WY@P%}t-Bl*#=T13sSPym<*wcHZQk!-urTeqaPPGwy zQiM)63?;zVD zeuLgMd!uebL2zTB%}JpvmH5u3r`*xzB)|IHqL0#d*&otL7NKt^Jj&R9SiCqbA9+8H zY3bMFAZn=kko>ZNECRyl;DeUEk0yl>_d6D+s&Me%PA0u9<@bPsp)iqKP8wogRwb@J z)ef#Au4!)FRcM9t)uRIbb3%>BMv>3Qkh9u9W){Ek{{{7;gvijd`{hM~7c2z1p~zN}a9 zbX68#6qFD>;=8!pE={`~p|1960S+{l*?n?S759qx;leP;)NffZmr~euK*KLW(Wyi5EHH4oq=55=>K4{U!aG{ z70B0g+gTk_Pqy2&=%O{PFuvv1p&-&05Qodvz|&ScGwwTwwdm|muy}cG)FL?O1+|JE zHXyq$I^dnzx{fv1H!G9Ca*poeOJQCASx#OR+u!{p%b^NyEfl+fLT^RmuTx^e?WbA%>mCjj@Rx#e4_>~i`D*niPq&1>H z8zcL*);7s;?XsHUQaGns(dz#EH*Iw;;&T$M#&smq&wYgazOd0n$6cX`_5AIB)9L_w zkNkym=jHaBKh9LXj4~*&*Ge~?ISdkFjt;CS`_TD))p}Fz(ygX4#sPE->`#E5$^8$6 z?v8c2E|=FO-()5#jH#uzFJ%a9@fbR#0s1f=`t-Bk5*Cemp8q&EmN)=FFWYWdCTxGvsG$gq=vsuy1 zc3>mG0Pg+c|LZy^#or;36dp^Xo*IfN+Co%8B31c*cN{+u4|eBRGhgdpzOkpgGf?9- zk)4O~ck`T0*?e9*_vWp*0eg>FBO5#Sl$8)v$3d1k!5oTc|0 z&7O!`kbU8HogW<)a=0_I-q5zC;NuU5{c3NTeZuBYE%c-F?58XJLME5vUd4u9(^edS zJ#TIIx$a(8K3=Bs^%Kf(aZu#X%cQB84tnU-?=R_)-R#h-j8A0X3j<#ggshRm@o8uu z6d~GtY2y77hug->{{0U@nE94yd|!rAhEVOHa-FPo%?su~ace}CkGwe!YW`n_Nmvq{o-UYf>Ap%gC)nnfpyj~xHYh^z8sH_rG3*Jr(^T&Z;b{u)~p^`|`I%&hGScX%BAco1a=J z?y$|sUHWwwG4GjNdKRnW(x~9MeeZ*RB*WH_i&~7{5zp(c$_b-i1nnmcJRYW}7h#~j zBGMCYEQjO^Bxvv(ph#5tjF1!-2bDx|=bR2}?nW5Tx9#;ZUruZn=WFUXVNWuBFroso4r(IC+)5Z-n$HYd=ZZb4_> zmljYy;Gi=Uf7eQ-_wx8MuWX`^A8&Q0Rj6tbz~G}scODlbDof0Hzw3mIJO5#QyeJdZ zOHP+D6YTV+;^5)kM)$SKE>w`=^B-jIdA7dKsH(pmjyJSC2hA#M%sucUh3o{F???-g zQ!c#_f42AAl7Emk=elxBR7Q9x3faA=ExE(= zKJ6?2*#|op&hb37v*3@vz1OH_BNYD>QCt4rdB0$&dS=K=ZY}PuEW-1B&Q7O&C|&s8 z1WO^IV`071RljKogB80R=6jd+7{Q;+jfsLmG}tb|NV#SsvU zrA9*Q8Le(0Y?|a<@Zd?nR8mb$=4Os68)?ikwed z(qtj6kP!3yL_h3)I4fj^t!YS_>dW_%$9wNxPxqcH=Ss;N7#LC+?w+dB0ao`#__xo! zNmGS(d?fVly^WEAB~ud{d~L#wZ~S?7pQboT^)2>qKNZJUZDl@j(l6{1zak*F@TcE_ zcu#R9?CO%OZqizM#YyymURNC_?hEm}S2X_2Wd=kR(`E->6N>2FR{2>v&Qq9ZC7=gLH z1L40R-tg;0ZWD>iSzO)+bk#duQd9`ECw?XX#U8&OTQh-|Q?4mo%6Q7qN~h+27tQxP zq}Z;FS-em~ym&SGJ`W|_1XVpV@qPbK`HbsG!LzlTtyev~X8#XeZygnNyM+zYN-H5< zB1lSigMvtR=g=wL0|L^ebeD8@NJs_wpKMr`$9Xqal z?Q82y#dMQPbwtSvPLDC}z9Vt1&F#}Nl+CPzlisEHg%GB=ES@00 z-*>U~8x@YeG^>%n=M==;kA$_TZ++km_>j#9raQf`QMNkwfz^%1hUJ=Hq+}Y z;+9)9z-Irwn!M(Bds>|Us2oaP9)iUG47oG;w_}s%?`*RD#rDfbH{ThW*5>`c8vcgW zWsfIEBOqr2@}9a{+ej#pj6VYaoLp%q@zsyTW5yC!{Y%5Ckh){x-hdb=#bQHIE@hhc zLht;?KAr{(&1}tV7zPJ|ylgZp+D8_&(Dn(GFT$Tpa&jB{?~j(yU5ByxCMawV3nQNy zDN51*MEv(w{X=zoXil}m76clItbu8x=w&tH8QPk+8%k&AzwYW!4;**a?6W8PiA0Oo zxaTf`^bo_dc`bEx=et%%-lf=`q8S-)A+93B0LIj;8d!mcFhok-dVlt77dyD<$11Ob z9mhhO7uO#g>JNndKPh>4QY9eU_Q}S^22T@&+)|ihW~@EZP{!zTBm7Pb#9N@0`o{Xa z{FI>~h9};;#zu$Bl$l&gGc`3j=OWs7|9>Og-m4gB<6%%283>$lI~_1!f0?F6&+ z*qg|b)$2$Gc-!@qWxw#A-><$YxCc?H;N^3tp>6n322sna!IaI~DApWm78Rf0cb895 z{$Q975AC5L?4bb{g+N%{If&;xV{K+A0V^R8r-Bp%)&KTbhLqQCj^=j+2b{yW3t^$z z38cRXOfZxh92qGDM(Rugl_~3y41vncnJObCfVFo9IPr*+RWSU3h9o`W7)VdY7>+Gc zMm9RmXY|sigq_=Ph4!mcIunIE$%9^RwbmR`5U($g`*R_prehH3^Yr^Z@!awjzWOU0 zRe_Ch;{W^l5aHejko05pfgd#KVgKPPBjciyoE*&61Od8WPf%~qc*U-M1Oc3CGH8Z! zH6!V$inq7-R5KYFFQVb!3rZ<0qHQjUWtS53FJ>$8FGx*qe%O9VDUO4}W5O^%{gRHZ zFX^BlCxGn5*oTb4zztYwG`=E#_r+0p6EJ=E9sKgJsiEEX`Xt2-Ncyx>En6MEueA@n zq89}!0l`l_`#zs#RyB4_;Wpo!-J)!iC+gb*auf5Ffgz<)TmYB&i{-7?u#{9z&aA%A zH5H~I-D1{>%XFzmV~WFS$Fh4ylq0w%is$zd1W+Pm$yOKHUJ`;k?6kck)vEI63ty2j zG;i+*nj5r$x`2zl{qA<#UVjAFZzYt@Rb-%~jDc#@#$Ri&D1&Hzh4_6nH;OC#h zg8QpK-PSqJ9s$mSR7@W>Hj0aJ;wEAn3p+hA06(6EF*oP3`Jb1TB+1?FJ5WNb_AbhS?S+_+3;Fo+Xion>Mtx z3}!)3Mh_8ljBP^#%R_rUg#rRo)B~Rb(88q3W^dbYBw5{~Zau_7zX9FU#}dl;2BPGc zrY~HUF)^;pX>KGWwrhYPpU_>|asB_6rffe1-mdxql?S8OrM&6$O~ zTl%3O95>EPiG}JWmie3h>;nIMxW#q4rJi@@_Ui}f&W^*choYmfM;I74%lXGkGGM2Z z$d)X^@foffb&2rc&~eZH__*NJ zCxE^ABt1HgrVE*$7@_2wdS;$VOTqB?7~~)7(#Le`YGbxbHu{)Euj&UAl9HtTDyCqqWCUjs zGQuvoQcoqqLh-m)zmM}%N?%0XD)9DzP=s=a1~{f~j*tHXmkm>5eEY1Il{Vn^ z4_V^jt31^2Q2^ue@MRu8|KGapu{bWz>LYN|Xe&%`OG~V%YDGKH^Z)(X29yJDhZ;5l ztVEpG zLr0_77)WS89#+zZIJBS7d|8~U0X=ft8T-(2 zcd=-}l|ACbrzuQ^*VBU|B!9d7D_Ujkq- z5Kz;F*^wW7FoE)s^5Q?({u}*2J1aa!MR1`1`s{s*m0wATxd7`sJxfbnarV>te7&Ws zoZc5;sVAYDRo7k=!MZ~!Q-^%NlTIB0vIX$P8>ktI18{>95)ueLNlbP4`H;C@0}}=+ zZ1)aDDcTr_duEmR*HCnU_C4&S;!uB$&ieWUU^*fdK3;L7M(3XA-OahWrlvH|NKbgj zYSgiG5((EB8$_>h{e9k%xLUK|RHto!-S?e!#L%zhdMhK@bE7nBqi9eNeqjS8QR)OQ zo1Ov8xB0!HRIZnmjkmulUYl;A_3+a>1VlmNva(*?O!}<=tC}Ki>tEyjMFqa1`Q=IP zy?WIE;ZWYryo`6LOw6vixqu(M530x5`?b=(cn%Ku>&_clj*-on5fB8HW1n8)AWI@V z6_@Bmv~f}%W4yP21Z-t+&NCF$>u!TbtXS`+ID!zjgG7Q*x7`vC@&n>pD|5oKu)qAs zBI}xxX|+hs3jGqZQv+2v&>ml}x9y>_hOurp z4b(`4%ie}i<5U=)`p2_9@HxPp*G>9&m8Xuve~y?3nV}hIaSTo_xnb>@qT2UbMQ3Oe zkD`hiU)*qi#;W@QAq45MJ_MI{=Lz%pW})F`dnIsUquZ~yKXT3BMcY2#m{>fCEHa!K zuF{6SiMKAJ;cyv23~Jxsx0?k(l+uyL^9_L7LtRQC7Er#vtHn1C@3)s%T*dzwX!v^s zFP!gT6~G^3*u*mGR?tSsZU72kdr=tXRC_yjZx=^8kN<*RuXBF@E&f&yZIEl}eUR`r zwh`7KIivm(HW61naEaIzOinY?IS6rSbl?;1n`PU|7HNc!@A>5c?bDi(mq@7Xwc9`g zi6L;`7)cm*c-StNJ&Yus++HzG-yNmXS_-!h)*69z>;^l{7EX}7j+dZ{D0piWM{M8`wZ!c#$09wf^jdf9G5uT09OvC9!8G=B(0^ZI`0J)vPCmHZcpS8 zk>|xif=1{{6;Ltu*vD8E9|0T{Jgwy^pUo*aFt+~fWnWeK>_co~rHB17u=F0_c~fwe z;r;cC4}aSTGdvdm+lj|2$1L-(N=fDT@vY(XU5`#U1%=BH$GS;67fL%Pa=cFAUN9E6 zf#|-azCWO8j)VjTHUSWxPrh;w9wovq%wn1+LfN)T4 zq=y}z`U}}V>cumCc>)a6YxX2ywBJK9&Dbw4@|=ttV9NtNTH( zzmHUI+~HT`yBy>4Sag2GVDDMo#YTGrG2t~kC4<=yBuyoIkU$CVehN0)EwqzrH;;LDk|Fa`upVyS5tVh@#qI zQSd*X|MN8e{qZpxEibPYZM@q^qgzQc)WX8T2$y59-<7h+Tp3+}jSf=)b$N-!4;*hJE{{?-1@k_J$gz?};U1 zqwVa-AjVIY?vVON|NQU+nqtiLF;FW@Puk@hzTI}?D#xfV6_n0o@fu#4Azc8v8Nc6l zki9XvC?ZN4Pwk`4TW|E;x0NA^ZSB^6(2z8nzBVWPldT!^Moo=La9Czaj z7gkL(GM_GB4UO%K6EKF(z53QcvE|jbK5iTYyO5O*8luj2(`QQhw#zpqv6Sgw_dvZX z@UQ;C{ zLLT{fyH@8sM$WE#Co>M9_v2w5j5A)A7oU)%_Lv8I$LpXe4D>}RWY%b^h3AlH@DTc zD-GUg{;08E82e$;Q!Gq;)^@l*wg^5fa|c~tq5YTr1U#={d<>?X+@zLLtbCy8ApmS* zs6(o$cYiJy{GFKlj0VJ8lcEP}Iw_Ff+vmoOj~cQ$fA|`%I-98su`NF~L+9;%iHbk% zhKf906BvS(wq)AZ!4IM;W2^E4N>Ul}`Wx+R_^Fr|$3!xBxDEEFaImm@`wTx8S~H|D5RjDst9Rxk?UBI{u)HIHTb ziIukWqi5T!I3i_u8lop-Y`b2030G$Kw1Ekc&@-!YY@s7hA_jZ zRxZdxYBzJ}A@s9idfmttaeF|-_^qUq+21~rhoG5ZE=r6$&ys-Stuh>o@63-vzHd+* zbhO#*p;oG6$z*B%dHS?4Hs*T`no%%XAN9$QOu$R@Nj^&1_5z%Y$S?D`x-Wkec z=pW7+*C{#|+q@*Y&DPVb&}@ZW)&S&cOX5-5@ycxYRl?958GJdGbrZz*aM_~KQIU~S zrqX~3=nDfg9SL=!pO*<~%UEh1XR4;ClgTpFRiBhgo{@dmcD1-Yt6{5d~ZQEdKE)fGc?QI4TC139+6`HZ3 z%%7(%8{@U~-<725xK54BOg@#Nlw}n0Q31=A6gwi#-aIy*SqFGL84VUfn>3CLq?q1J z;@Yo1oWcIf*?cge*OC&b(jC70A;p)EPmX6}R~c3!3ysyD8_`&#>|ES8Y@wjy>jdti zZKYunoTW7t0@3qU9!E}&7X16w6aj#)`Bpu|>^VRe%b^HU3A#=|iGKWG0LB1Th4>tL zlf0Gsa|snN`)jXm(lmVysd&?{Dh1xb_X4kslxV=Yj0!qcIs9;>eR<96pK{Y^*PMOjB>(-v40UmucM_!Zgc$=d52W2q!upJ6=^5RWksoTW!1*Z4N)iqfb(U|QATuc&4M z;!ENZ2BjkrN}UtG6JcOlseZA9I2o`;R@G`+oJ>pC)}P%mVaG5gh8`SFs-^nA!T8Bo zU5wc8i~b>k?yNLHCsJd<2a}LR{cuZ7$JIB+&!QRB&0K<%dQP8{9W03z);mXsHCPoB zqVGj|YdZE~T@C+JfCZ_z#uK-^DHP3uCVD0-MmS5{UBXlfi7nC(!JIEROy?e(0&8Df z7@f{0O!4dckhPmNEp ziUdh-5{1J9H-_1(%IQBh5WO#!)Y~&GmoTLuP>Mk&sB0cY!lab`HLq3UOZ*RSp9W~i z!bBy!Wu{sbV=4>d%FWnb7R~B<|JpoJKq4Hc@$=o-+cxTc9YcR2(>AK!_j~~&MT61h=e^VsZP(#Cc3gu zUh*jtCzV}SZ3Xz1j3v{Ae!(prEsaC1qQ4nH|9WOylVesH+W(b2)*~Z3=X|(h3ze3{ z?~Sv=S;5!&pwNjbC1I6Y`=t&SB2rOz-=CR5Om(XM*)Ndw84+dPpjo!hVLV~kW>G>K z3||l_`2{^jgzNvbaaoHBGIZP~bNpdV@;|$ag*vTepLz7{Iv4XLmdc@$izH{m?bN}Y z>z;_6B75QBUH~1CyTDoQ<6@4VK5ptTMI`U6x$*OBrV*t>MkP{YeQyt~0-U+aMfw1F ze?-~+hjh=nrM9haxEO*kmt~E}!rr!Dc7U(?O!9T{{B`W{xyeIdId^eUX~5l&+7-=I zg-5!Z_f(a(p+Z65D98h2hJ)KA?i)h!+gGR+W^dK>UUn%uMWfHW=G93|dP7fR#Nx>> zJvk??3QvLe@MA{ED> zp_S!p^xdUS(8S}|%Rp;{Xm%MMu{S{S76Kza$-k~sD(eg|XhA%^dCf|u#=6$fqq5r0-YK$%eX8L$u%IE z*nddC&6QOuLX(OE%`kn>sToLUkZ&J1KJ``(g8^Z6{d;@GEc!BUXf{ZZDL%DWNj_h!HL~BWM#rbSDsu}<#v^0Gd9!kw z{4#g)1w|LSM*5S;wN$$%^+p4{lk4KgY+4r8&s0;x#}D`MKkG#1RoZ3WZEz$L>vi;Z zY({wTZtt8qKSFpF{~01|iNPI{_&y5Kb&w3*oGn$Do%i9HCKeDy znTc;bDz)kwGjWwq=UvaIDRSSF4{~{_`$XkYs6*TWS{0PLumTG&j*rZ{D6-p#8M5q zb(x&wnwbF#s60>SAZ{*Y&$~X?t@fjE?`Ttw7oR+4#~UktjzliQPxC34yksqjslr8| z77MvnWr1_E#zwX(*sldgAucWwl3vx%XhIqL@y-$r=SVq?yqNRwQ#HC$q!f-gQ3Qfo3=hQcnr%xz z9ZF7^KJl(5n|9m#b!uR?Mjqv?rI^d6NZBv94zX*|(fHKD8xaA;nWw4M{XA`I|LLfr zdDy<4C;mTkTO_(e_R1g*IUw+{%c8R-UxO8Yum}3X^`y6J^G&JCi1^k3IBi;l;m7FZ zu2y||XiOLx9Ik7%SU&Vl7kUFIzE~>6g+B+)^Snb$DhDq>`~#`xoFKCDix!EQ)Sg}h` zRg$NY&#=Q^6|mCB0+bu??ZNQvWMxRy%Gs)zSj0UnjJDWd_E&<_jVGzT-H=O-OUyyF zOO(Fk&^-~9<+a;kG|^FmZm-O>uwnwCf=fwo3;Ft$p)4oEn{3ZWT_Wg39LauC z0sYz!cI=6ixG1hJV&9KectQMVidb1F!JK_?RID)KCG~WKh`N61$nv@YJY@p`j@s)G z-FY{x$BEoV73Bi?6lfYB!u;b&l4|@j4Cu`RInej96cX0`&@AFhr`_ zNTux+1>fBA?P_&8$SuRMzmRjF;k(96bDsAuy?;gx>E?fVJ6?-G*xbPRyIecvKt9g? zJ&VA_#v8JykIQqKz`y6H>$kp=My@s+c^|k5v-bQ``2p~&2S&FR+j2t}{$oz=*BO5? zH8&s2Ss(5~CNt~uTqmSo(-_1(X;xNkD4ewBjmbEwXnNFdvOr^bQc`H3w)&)8-3D|| z=OEQ-8bpUHQpX1dOf#1}a)8tqlms*CFF$bE7O_HJyvJ0_Liv1cf`0pjKGcR5gg&EP z%`5|xL8PHTz-GYEt}g0S`$#xdHMUghl2=|1eV@3vDe5V|Q6_16o20~JAv~AOk!WG3 zpsZ1FjZa~OKb96ZGH9xPI354Pk)YE*)+AfMUr07!K=7#xC0~+Nsf>}reA-kC zIOdpX2Xatt)brEV#m*&YOWRA4gcB!CU;MzQAhKM1=DUb0BWTI2Ou^uyahL;FizG0y z*51oeu`#FA%P95ovzO84AUx`@go9IUysfGz(vr3g zQGJ=L5|S%=y*X7B&}2Tf_{P^zpN*l&PD=2#YCz~(M8HR^Gk&Rx(zJeO`t*PxlUygP zec~VN#OiXqUd(g>M3(JN&-4n~_m66G#=(g@mdfG5-XR3lup$6C@bjBe3IZ?D_sF8j zBRK;pauf&J#V7cdEf7Xi^Xmq(P_Al&d}+;fUM9e_ta^vTIu&rJEgxBOVk!k{W9n)Q zHggu6zVFuwT@xJS>V=TkJym4a{z-v3U9{&|jPOaqg_2oer(X}zmAO>mz1@)8EEu_> zTztiyid%kY_{T&*a~5qbM^C+MNVf3rd^0~v{7W9?(FvD`?Nn%(bE2kZwi-ARZG>`3S`ChW_=g?EpKNs_D3N^P%89sVlYQDIU8i?DCz?qaup(;v^fz!L~m;W zY*cY@&&m90kwo7nP>?b*pa?Gm!|8%fE+%t>?T2*<`Xt(0&r-9%&HywZVA}6L`HBcA zKlU%0S)_1MQzU4;EpmlPgi<-ZxVu!5QQ0oHBV}(9KVw~3+m{%Z9#~2y)v^zq4A0f2 zaY%;7CX8tfC1$W`#-Q|LZC#13*KP!cu(d6v^u>LWu+w-O<@dU)-1b|403p0XxXRf9 z{_|^)W0Tj9o1k~dNd40522yMt|54NUlj%hQ5>yVlGPA4q#0Ji%b&_wi+HfQkG)1Jd z&5;9TZugV9z3yGY6qq}xw1>w%&eNftowq_*44%e0#@-!67zukkEm7WzOg!q(r=*z7 zpO?+iY%KJ?B`F%>efK7PNk&>iz?=gG^mstIl;y1yzb&gWE4MtNPzZk_PeRX=vTx0r zlwTyw6Q?@Ae7>>SgHV+-Z~@zB(#VmKOD4r`$OKLG>KAPi7}~LX>qUtDnLTQarYBdz z+4@8|L?PqT0Q8Ib9mn)L6H+};m;@oR^v1%jYJT(GppA;h+tm4v0cDoToQ^l{)mGh- zhB1RWept0p?lMgLgBwoY7r1D0QVCyDQ%enT;#<_NmeZI&b=oRYF40NafQXhlMR8xW zki+M-khH;7W=X+qsg$(=jvb6479{wX#eRqPXLUvtAPdy2nNO`aBqkCKERTfU?b$}r z*Pe60*w$V`q7bPe3PT@IvhquxZ+k{U^i}XPdUsx$sXE1Jb*MAN918Y+(3W-I0SQZPxAH4tl8G&7;IKW_Xjqr~J$X!Gj z_jj2d3e}d~LZK(`ShZ7XUNLn~K8jL>}%hr6D02AFZ_6 zU-tx=)KFMtkPXgif3QKsnTH^jJF5*IcK4st)Rvd_Y{SVw1RD$($`&Rq=qhw?72@H$ zAxb3u4J^B};v;4tPxC7$Z?`aMQZUC7`8Pk#A?^j3npGe_MVdJ&2d9KiiBl!_=ZgNg zsAM7{*|FjPLd!O%EwUc*kTg8281!k3t3Jqc{HfweNXnyWC0z%^yOZ8{MmOIN12<6v zVO4={rACP~Mbrf-X2LON94)Srp^DF$kBULD1;3EdCdWQwkam-Rkp@Gejzg`vdjv+7 zFlWcLK~RXVp|z_*1wY#@Z%m6QeB_waDk>Jf0e*vKZ4xy!E;%V<`<>0w*V)%IoFm9M zCO#4cg@xk-*X5+rMZpc|d?werLug5T;h0I38|h#41P~0-qx2s0CN^-r^f~)+nvL0h zvqJgUW}-mS4T6)%8fuyFwlH+M>4=sT~<<%J8eQrjy2vj zNb1z#s1sK!$6VqeTcFaRIyK(64JV#$$pmjo>=~)B5)y3RBxr^^r@lcPWWO;JPqtv- zF$(X_^osEtWQeGrm$54wx_CoXc$1XwcMf9E?q0Mx)h_vV2Fh6ziv=ksCe(;^@k2K2 zqMcGfpTQi;W~*36#P2VLmXiD`+U;;B5H_*6SOQ7BNtWG zyM*z*=3--CTuO$*rTohAbtqu()Sq!{I9S-@pr@p!_n7sY$8b2(29{Tk#nmFXeg7pR zL$w%!BX;suFc)MwNGv4LC2JV+idr`pIZxKSbpIO?svt>6YJdxMkOHlPfV2^9v`=u; zK63U3mS{-&QYa(-I~?|{7+7jYdTL(Vs?n9nQ0o87XGzV zBj0{TeZ%15(zxn3^<+$1(|Z0tc|vy*5~}6ORaZ2!l{QymYq98|J}0N`#yL^H!$O=L>~N- zrYB{PlvLY5E~vDpe+E!WKrXfEy|$Zo&^7sppesVp6~&TRT{e|4u7s<=sJdjtm%my` z+HA%?Z>>+`Mh<1l!2vDx%_a!S>TLI??ET1%(Is0lA_6|tLXWb^!EUubEFQL&!(qX3 zkwfb9wl@KiWgeCJ!Hfx#0C~}`W=0m7F8`Pev0CK`?R0SNO;Fkq+ioB}|sl4GDk|pKgPtVh|SrSU+lDJ?g=i0mRa<6?zyl`~D zNxI<2M=v>ViB&bjJS;9a`UiB9{GyxLyI>HVu;+`@QwVg45`)l)=oF_JYefatXm;+l zg&|1_o1uz9mQ5D50*WdCar_q!0(31jECx@1&tW$s0Mjmh&=CB_DO)uY@0Jb@{OYcv z>qKXsps8{gVYAnBG@?vZAkK5n+pBk~8+L=HU=D@(Az94tGl^0ac1(wOt|~XG8+lb^ z=ic|9@0XH}Q(dNOlIIu#F>Snw4*0cihORozEtCZ_0zA|)e6wVkG>&EjWvKY#iWVcS z*Lp>M`AOp`!Dh>=rg$BT3+SC|w*EVVmr2UW=zUW<2(8cedgB~?Q3k?KTl(_klf(O@ zuT2q|)I4SDkGLk1>GE+Wi{5eI{E9@hY?8GQ(iFquAXyzhQRxg#>Oy} zkSs=r6uE2S9Y5sd-M!y4WH8AqMov>>2jv zrkqigjhq}U@}xaGzAh!L4f1+^N75Dz&g3L-i}X@ITU-P!~*#7hkP+oCIRl$|9gr_ zsHlj~9@x6VXQgd7*ndtBmVFo1KkENUb;LW@ZEr|ns?LG1Ao&qvCL$eje*jHLvX1mbIP5^4{atbJ_`9%< zz_qZbMlMMr6`SSgsZ&1Oxv7mPIHNfryCC98;l`RKn+aIr4NrV}_pw^KMc90d?@IGM z^ui!7fEt!u`V=iX=#!{Jf?kr+ttm zu~@Ccs`hasI0sHk=H!%#dyrMHVU8WQg) zZ`ZD37Ec-m1$ZYtVyvhQQAOsNs^fmUjLBS#$ySxA{v09s z2{M~XQIu)~Q;C+2@)S%*^Nq;6ve!b(1)gFBUsN*=pSA_7j$Fwtb$AGYXx*4}Sg3R2 zP>Umi#g;J!-*mpbq}sO3EiP1-5{bHRF?vP2H6J!D4OsQ(d(>z5vpyRyohN z`eO1ulz3m_+WNX$VhA^u|~3a2R*iy33ci0i}{`JO{mm7Y!_ zanln>PVds;2xlc`V){D{Q(ARH*7a$D)v(B?m^>C}))Au_$=?FTjatIIUeb<`|Bf*gjVSK9dOrDRW)4YB z_z?)pUm8+1=^D+kXgzA2&`m$e6Qo2Ezx|2v-xAP6;rC%s&;yfgQfu~y8`2gBzhL5r z@f}~mKGdbm{pVx>8z)#D(seHa);F86O8DceivtCd)m3N3vE5j4Rg3Zv+*0)49SA@x zEEIayES3$XQa|W_DpBl1wBfT$V0&Ul0iIHss>z9JB?i|x8Zcu~F_(U9{a)cVbWet- zrxOEnW31rp*P`=#OuSJRhmpc$rt!L@Zmp=~-Ja>xw!1tDI03amG#VTQ8W)`%W+QSs z;Ma+`B*Eci;NHXRjAFeU|Fn^zCw~ATB00@N{8ocUOuCIaMtDl_|f(27LOhB#4=d+ zXr@G5lAbbhybwY1+WeXC#z`h~Wepn@Z_J|5B10*`M$ZCzl9WFoJd z3fy!g3Qpbm&5nx=3664v?*eKZtvN6>4;1(nWI~<1+oh9=pLjTEon<_-JW)MH+H zqX51T1}so(f+m1Sz5gO*n2J8{O!tLMkNA5RDQLFDX0_S9^t0}_?|%H8jSET)cycmA z@+^exN(eNFa3;EP^)Xh4A6S;X^T1ex;Luir-gR7kl&ZR*`Xg-mV%2#&R6=(~26c54 z2|2WAq!5LPT=Q@Q>$Zjw>ss;Y;K#*#+QNJo`)*>VvKzPLbwgK@tR1MD^cy8b+iLJz zovC34Sox}`UKOb@&@36AERFqhuHYWP>L_xN)qND*r~< zKyQSF`+En$qLlZR)z{pO-Iin{U|84xfsH)K*#HC*WDIxgRpz8Nrkx3JMzBl#I|b^2 z!}yJ;rhwFCqVL~a5U8<{+$MfJMWzk$TsGIFu^bN{V(%C^JYrTS(bk>OHu)GVi&rR6 zi*h}e)IiTQJ7+0^U(Re9C>zG$Thcnid!cBq)e^`6^K;(7cuj?cF#pm)iM7OBOo3YG z`Cx`ckSC5&1xv%*sMg;WY6MdtWvgfzs2zxUR<@VP*aGU5r1!%#`4Uq!PB|oJ!vAh` z^Z=UgqMsxSlPi#E>1sXZ+?WWHO*VBI(RfAyF&rw=FRegsRkMFTV*ZN!?!B;rwt<6% zUXqp&r$&%wk}XkcbZM8lS-2auGp-U-^b!b!x!=)ZE#HB%=>EfR;^_EnQ0Q>&e`oJwd^4%JK#vBju_LT@u!dT{cH{q0yD0$b$wYBHwn4?S%{LDsAf%EpuoN4yRdI42 z%3X2)Q8;J+$iewgsq-?=*L|d+P4fsswsY?97gc2be_-Pef;~Vl*i(WwYaWBzpMtur z*Rn>tUA=;xjM82kw;IfvIysx;6jfJ^{u`^5brL5X-Ar7mn7wrIq&jGQ@a6v6E-L}GiVj_;xn z?{tn2+4zb=YdOdrdmA{t0YB+xm*gF9Vfg*}sg_E4`rd*W{#{ZBk{_A0lPd^0Sjra#jHm%4e>$^}Gv<}X#v}IK4yYLxU?2mrE z(XBCPy5^{<$Q02^(9o^9A6;*opp&a4vflytZ*HL)_f?~!RBp?R4pN63GZjjcwUDgb zbUqQ992%rOPTSYh(&-AjA#ErA*_^BHpQyP(+K`}WZetHkZrk&D~)F;CqMuU9OI zcEb3Voi&i6CfMplI@RCq(ub(wZw=x_ z=d<~7xB5XN@CiE;M(WeOkL8?1jD<5-hrn+7*e3S?0XCikV$HrX?_KkPLs zhN!G%d9xl1mEq06GhOH3MmvqHN)r<@RQb$~2C{r&t#8C7Ixf;Nx5H~K%eCdyq7Jr_ zjq(#?MdX;N{59>ijXaPmJoiaXGlcz@4{XZ?V}&mR?^+89RZpwhyIDF<9L6CSh!oL+ zv0|1NAiLi;-=OuI1zOk#Uo@12<;aY{8VQ-T6Z?>TrCHtal@#c{^80e9@;!x5ep$Ix zwi8gs)a_a6N@(-W?5K80GS6nPry6&5(Qtd5p|2%T`*l_9Zlb~AeHN2=Rv{Sbmm#<2FvK4E$9M+nBB(vW0%2qJRuE*vCM~!*QLoKJ#@U@B%6~Yl}`FF zysCyI)5hJDbgp`Vaftb;eF0Rv;(GQ@yDZwaqP;^f$w|d+mr;_Vx62yAgh>CC#-Y6;&S=l)*e7Qqklj$P!csPi78b?`tsVDD-C~?!c{-`@tpqi*?hg9A@PmJ}|iJ)b*5B&wAN~ZQ?+w zojQOrTd#A|3G_93B+Y{{(>!7Gq|MK6o2-72e{>y&S=IT;!N)Y+E#|yv+9RU2T&Bss zNJHuMAk3{LTcbAGT9>NyHlMBCri>3ZNT+!>T-D}qDt7=|ciCWDH6!z>bSj^98q`^x zzX#%X%NBhP+m|l=b-ZHqcWWwT%d9!pL1O;dminM;kzo+!$VqB?9bPdA268() z_xA-xXa5&^{nw^)eFzNN5`DA14%i1Mt=`Vcw&53{Ebpv)S2(Gzz1?B5 z^81Bbx#(J$bt}yZFiX{$e;6*embS9yZ*$pG5^+|NdGlkC!F%`j&_8=*T@XxQDTR`DYugIc1extzbrJTR@*CS+@i-%IZV(gcZ`h4F z5NPb6HUD244p6atfKh-pe^KS5z@nWcp~E8R{aI)IMJk1)hluUK+{*Qty9+$3#Q?q^ znYZ6!`>Ti~YG++r74sM6oWW{Zi5bd@l|K8|(fuc(e z&@i%!-$-=>Tzu-fxbduJWj7_8_-=IU&i$sLzpqm$e|t{n_ixc_EG6haLc;%|DM%Q+ zxD5PPfknG*U+?9)9GX8CjMZAG*m65-98d}o$lNB?6j4|ae91jp;Bm}>HEQw z!G-%dFMJqGkFle%e8e!UUlU?6q-Sgewlm-~N^qOm=4BOY@S5<;-i(^m^@%z=socD9kflHn+0D_DPH6tx@AVybE5P+HH4?|4W1is(%0E5hxfq zEl1XAewRV69R;EFewQwF1?!C4D=R@U&bZc_?F&kZr4?;rb`8;)v7?vg$GOdZsTI+i zzS&n(%eQZV$X`Rh>3dUl1dO+JW|KF6yNh!1^*WDGWsB!PU3BlyU$A|C*ob0vcF1mS zF?jX*a{K>ju}p}>W9NX|0mtDCbdgh_eQ z+Q~cLPf@mt%2DN*=MTQ-$x8Y^7y$pgCragNp0cf^&dLAZu3U%Yz$t2}<`B89Uds3rEc^Ihs|ioqFjRWXOfTiJN8fvcm_JN5RT70rIzwfc_$@BUA1^G}HQ z&~+vG@JpQLvw3e}ao?IJPm)W;N_Ve1EXnbxwko}wWJT@R>&;iC4Q}6^eMS5K==ut< zD7UU{YKTF45Tq4_p+OLo9uQDLW@wNGDFp%Ph5-a5mF_O36zOgS>F$#5mZAP<@SO9V z_x=9wdc2fNp4qea+H0+Suh?sa+PQ~zp~Iu-%KGRc9YDHJx;potTURAj<3I&-F6j5p zpFgu=^;`+yH0T$7{}diUr)i)|KcKBCZ9>RP^sT{fFMp7WTK#*x$8^AeIW_KMgsoPt z+l)hB?1XGR<|&0(hz4Rf>-g8xU8Y*^xb89PyGp?vHbG#A)Tpshpq&3AeSr44(J3x^ zMS|9oWv?}cc96=7ieZ(+*Gs;xP!4#MfV*BmMdX>fdA1GARZ_g5Umr0ogvabU z$KPpa3GuGtPEQ%=8cQ}kZlBkfA-sD{pYG%ER~olpqtVg7>G$9%^!LD|zKW~5zHQNE z1;1_=dtfy5$>8J&EH1@1*Q{ra>ACf6p*yF({r5FB!4F^V3KLHUICn~;Y0gCjW?x!a z8Pw4C@Mk3*yE5AmOWS1cai3kO5PjW!lo;URB)Xs%F7}<@xaEc{`J=d(yzksPs71@$ z9vTo%j@=~*;Tyd}i1`(h;Avo=;DPBL|L(p{n8am>0@*2gTE6@Gar8 z!KWUICs*1V4T@uB99t7LHJ&Sw_O zOfZz5wq2IDlG&JD=xJ+UnpB=ONzBi6!iWCIW+ejQLZp}?8ed8ZjFZ`K?uSG_kaQ+B zY6K!c58TdXEktkW-}JyPXfuI+c*NK<<*g@gH!jG*y0*lm4!C&LAUH8GzoNEUaW`@C zBO(G(vM332B^vmzi6N0l)CstnO>(6o>me9|LrOSt(tC){sS2eo_e!4$%R}^)2QbS( zpFqd>fHDhtZRmCVdp^TCh&~;uTKg6lmF3zTJ3W;8rn@ve7U+90naZQxACURYH|2Wn zen{B*R29Du)Ai>Cq+sahJtBgg4w`3h(?BitQo6um0A%59ZfL{D4f*B~`x;7}O^ zbR$j_s@=fvwhmS*%#4H)yl+IB2PR`ERnXhe_F8D#KGIr}OtR_g*ZV!9*P~>TfyseK z*$=5YFV$3Xu{)r9`HB!s{wtrF5{#0;72HT6f>P(w!}>lp$>4(sK$8-XRx=Qm+u`bM znd=q(YbFpG^l0VM&_E6TK>RtTTF@<>|6VuHIS9QiVQEa>`Uq08O*VXkC;!Jd6i{kY z{QFqeO+O-8?Qo_3ymd{~h&zxh==(cpo`d4Tn}Jp}|L5<@GQ4|8TfJNp8^ zlq#n&)uX?O)3K!L zrT*<1t&8`oHRrbZ65QvrsyhTaL~00wJAh%P9}{vQ{!K9^p_k&Q$g{+KVDULZ+2j2+Hl4)UpFW1%jv2)`N00J zdLaSLO|oSG@Bhed{SOINRZO4z)jg>DeaL#aWue8Y_^F5TvACLn@+j`;Q(b-?3FOzX z%znZ!S4oQ6z6|FKrPGsNsxwLJuJaSuWAfq9!urVmd#B&xg3dVI5Y5>YBNst)g@Jw> z{u}Q$ zO3Q2DP5tQYT@mDesU?6s+-MS!ga4XUKYHu_d35n(p=b^G7svgzjsGY$^e6v+pJTh$ znTWqR@Gsr%Ps!=3Lj*Fe$Nl->3UTPTiBrTdaf{jpR;gr zT(#O{ZDpyn9?)Uj>2tM|HLWtb+>{*U4BdGJmP?UcUk?xVwe*2$H%Wa1dc)v(zUiv& z8*>r})A&n`l7R>QXlr(IXdE29h;k4_)a|&ydCmu~bw&K-(Gk2v&TjeV4|y@gRB^7N zder*2E|o26Vf(xzJ8`aOMWh!1x&2p2X{Y_6AH8tnTee3o#KDyennOBzJ4)c$lT%dI1rsCM;{v`$5no_Fv<%O{AMtG4^FG=Sh2^cro zc)7S@Bh-SY`iMXLZ0RZV$r2X$a{I*VnWD3J980(rrwF$vs_RS!IK8Hf>-0V4LlX7Y zPw7BIg|jL=)q1YkqC3_NLr)cwD+HI$c)XSvrw%^Ml2p$!Umk?_x~!MIaY|;r2=S+F zJ@NXjV!IOV$%0mTT?gF)t|^`f-aH1=OV^rqxc>SGT!IG*mugyghqMyQ-!ey(kothr z4LV9aFHk|ODb5+X-~7>R@rUIAy5E8%)w;y_d4t#4oKI#xV%8%_=XPg39fSQ`!CBM0 zb)WBP*6!q|zQ1xkt&iIIu%mIg=7nMyoM#xwuT4)Ay+}**>?z$5XW#amBd+i;y%av& zaP4lWc3E8k4f`S@It3x;6B7D!&0>;8lRLjZXx5aRJZPx=GwiEFf53+_OYrzckIr$ zwE0u%K?jq)E>HK*OYYOq(1<#0jF*~qu0X9YAkvqg+)Gm;S)RY`Eh}qM$L%aH^8%XB zGT`S<4KNRj_N)| zqCz%ofMOkZBV3(!Ea2}$smX@MOxw=DLHtJ&{g98QR7iK_@ywkWztN5E#1Rbdo+fJO zG5KV=gfaG&x}3}WQ1L3xoFf_G@i;H9)b)az@O{>*rp3`gXsw#C~J@j|NU7#pNULqC84&3pP* zUS1vwX+2%enh<;>$l9`QH+2786qH#GzWX|YqFe!L_1!}Cw z)8%NLc+`(+t?4@X5G=y_Ds`gBED9AMCVlv|xjl!tynj^U^ap(E>xUjemPK*11*xVq zK2oPz07v`*j8Z^VPS_ItLx(f`(botGVvC_^qT#BZ9!-Lx3p6 zE{PN_^ntRE-i&nPuHPSDMDVn1a@1MdiK!!tq#?zhA8vgO^-%zR>>7zXcwM8#NDt28 z4{PrE@vHv*WUAq)gr#MXUfl#XzP|H3Xj~|V$`H{I5dDD-@-xc|fJWDf&LAKFX)>ta z%?45RCX|0gxyj)Ohg~Gu`zEZ)`>nA(=~v-xNI{|=t>HfCkHt@P8Bi%Pb_P!v1bXVJ z_E88ZmBI4uJ9n{d$6xs(b759{zDSl`)vaN%C`%K$N840TT!$wWS4Y`jQRFHVEb$EL zl`yo& z#?UQmO(rfc&br*U>rV9g$HrDY86ArQqRA?HV8%?-d%zj_=M)sV3n|e3owIpRQ}2;a zh*wGk8vbe+l^H{Z*TioJhYfz_wY+M0h~TAl1LF?2kYG)S1ONM}P7((EQx^g~yK3`6 zj*qDT3euCb?>OEKIB6UoL-P_Rg1Id9o7@4mx#z1?8(`TL&@Wqeby$yJP;zi^NU0^f z1%6k?L5tw+8F)^Qi#oEK;xm)VEJ4K~c(>6ExtZVxYqCf2>q7I6FQ>km1UvuwAouwB z^H4vZRqIBWw%A)DkZ(IiEO_uQ$yHSVwt?!KUPFz%d&B5{fiw(==J@L$>2fEAuC{n0 z5HMLvk1TMpzut96vbOY`f5;H82)zaw}b6^^=Ys1a`{rM8dC8JZD2${8&N@MPx z0Sk)1nCf_cOd-u6D!UH2z#tnEL0XcZ|AVODzU5fSliprS9!6qzlCI>%Lz2Jk)4LD> zosFyYhGgdInZ3ud0RC}9A0oCQm5 zyt*i;1DEuc}s5VR{?3qt0t2Z*4(jzlejx~`Cr)f*_K+N?hjU` zAWgNubkN5rCtPGQ#l%~)7`KrKCZv3R6|#Cp*IxI|v?jwj*z_&^x#$rfci$NL0S+P=hY#Tq%3L0t|6zz77^!~{H0|RAu_BYx=Fgz zy0|RNIr-Y|LW4&o8C+0$wXJ zdiC&nuj6UCwrP^7+os#M?$uufYqu>Hi_LL|%O(ir)UBQt>~8_M=e@-7Me_OO6i+hq zyB?2WmE7XO~ zxPwQ`z`X6Y8gA4wgS$7VKf{AMWL(&m_ZL{qiFFnK>|R`b%(AE4(6m1`>zN-JCv07q ztEhjrLO6Bs4sh+f&}cxRccdAOo6(IVZ;7U7XSW?(u757R2_R~VNU!e)WUVl3H|-bN zxCMR8_^FTVa&$pGfs@6n!Q8slFFiz=B!kY9xcBR=!cRitHTw9!kWMf-67kLmJ8e&; zXCnXTG)TIEl4KLp_*^RS5sM`?r#|K)2Iy+btfJBaGBzl|k(O&PLo{3SLSb?*1t$Q! zK0hW@T-)gHhjL+o9DZg5w!}d05b~`_f_|NA+6GaN1+-t8@#ytf5F!y@#&koOfeuHu ziXwS;j(08)1=Cv00K8>Kqu!bh?uK4 zJ{3JLFgZ{JL1#(E{6O&UjrK*pvM8!%JT$4tu%G=MYmJg>1HMoX$sF-v(#>Yx|DG#mxRK)&l_Bkd)7fLg%>srvWs6Wrcuc*`yg4S?CDm8O!KVj|`3V8w5R1)DRcJ^C<*0iMC>(OisqIi4}G@ z2%b5@wyCFb#CrzAY)&(i?>ox=OSz>R_yB^?D zj~~)fJ014th1sh;EvFxESP%&KYgaN97o%)-`TmW&5KAvN@cN7h(A(OPH74$5{cbZM zx%;Y~-xFg+qyw*k$hMHu=zMgcxFPB7v>dhBAX8LkFctLE`Ptq0})mBVi?W&Fva}4#CP-MwMpwweBYp zKyIwtQPi$Qh(z9NbiJ@#YcBHm_u??O*#bSTh8VoP?Pf3fiHz2d$+&;Y1Jwet-kZ*# z>V2x^jQUcLu7EY>hotv_!#2;H&gdmRx?y1TT%?_e866$7Q!VF9GTX+Ge7?Z#u08U= zb_j2+>T*JDv*oS80I{WZz_LsfUHsiGG|S1F2zU%)DHNQw9|#Io&u(lsz?M?~YtT<5C-)_b1EH_&^1cLrYTDOQ0`zt}oZ9p`gN2Kv%_nW` zp1KWrHagG=TEh6ppwC=^wT7=?h~Bo=txDikI9-mW;` zb*!k+6W&o*4;(4z)ASC6vvIWr;9xK`L-Z69l34BW(h}a3DuLP=F==?!AhoJpp?~|$ zMgP&)+O*Xct1A0zH*#MF$>;1U_O!tLUn;zBy2IATQjjF#sH_L5l8Iu{(N&R(S+?!- z{@#~>M*KD$j`n{84ta&ndBZZGz4qRB=J3FT1R4VD4-7U47gAZ>oSb7l{u4ykO^5(% zM|oWfWNi09Tsd;9x>}f|^2Y;U8*|Z!*7dQLxQ;nssnDvs2qIsk zZOq4s7!s-pG2^gqBR{0!$qPE@_w44sHDla8sOHy~X_YJejPJQMuH>7XA*M8~Sz>_z z9ZKptZx|_W-ho)^cLh))sDY;UI2_NPKY!zmv*beQL~iLTdip}#(ih_CmT~*{d%aj4 z07b6nmY9@PA(6JX73*V2I((2YJ=OC1Qp_5tv8d&#O^yiwq$|4uGR1iS74(#K-~Kii zBXP~>eJyc?zc7nbQygM<>z2o_kGib8?Xy7|9iq?wAdl<6$L6s@<1jgS=ZEV2N;jg^QY8vtkB2*;*}wTK|9|oraG+Q;`Uwgq)NjlH9miN(tyBBy1^I@b)=rcAzwb; z?jS6(+pf=A#ck<>3HO?c{Guhp3l;_5gMkf-WiHAQ$5QSwDkAxeZc)nLykb9)>p7!v zF6`FR>fEStb;_nk&m{b#t(KSYKo=NcR&1Gt3@|HI(rl-l8K2SK9cN9E3nZ&w8LJ8) zUm+XvPkpbQ4quAfxq3!V&e8H;ZFb&n+9zhPOo@Y)DFxHMn3u7V%_ja7sqgmlb=pZL zd&hF&ttAxTU_@KhpIW40k_~8pbBM3%<%fwo&-DZY?Ue-{GH|&N^hvN;xk>mp^W8`0 zPwkTK^$N`JuMHU(kjevaC|bb-PDX9kypzz2^2+0x;^2lyD})O`dC3fS z(z%O^-WloLe?lz)u+#~^Q)YGt5JPpklb=y+D*Nm7X59T{TqeCvXk7Z|JU912R*2qL znmSrc?V`Qv5!s0XN)LF9{_P9?YaI`Ux!qMl+%HEn4q_Yb z3Q2ozSFNgoSXCAAMjPssb)HVK4^OTxkcYT*fI!ub z&(q^SbOvz!cQH`+Y73fU{#Y#keD$9tQQm^+2rawxFP@E7oq>8%CG=;0?Gm@HG@z_k z0?`ir$bMF;3r%G9kD8f#<{WqCUFWOkQDjLDN#z%tWme=2Tr^6c?akuFmfmAjzn3i#+aU->-38kFR(VAc1=Iv7!khqzoT_y z1{fb~)X1!=obvgbGT- z2Fr72c_L4?)7J0^rrtlURIfCxbOcTh3h*-R_TgX9?TgPn^wR&>E>AHDK>2rkk!8L` z^5Z-uIXUfhU*_)xX16@7H~5}9wb}v(_l#9H>b?_pZsEsyBg3S_>d?JxsSmRm@tF{; zKa6v`B98Zn>nm*Zf1On{IuxzuhXjE^0VIsv%>Kq|k1c2Gw z`GhsLyiS=HSsZL^dyUDZX1(w67@QlsITUeJhP09c%7=!Bv%TT;ENe^vwDjEa$zT2S zuxOOT!Rz;YfJvh2naxZ*zZupfWXN>b%iNcJumB#ncBDq8q?Y>lhs0*W+VUtK9x>f_ z3wk7qbPF()gSA=dcE(F{HS1ik@r=>r zfF6P;6|QDS9NaH6-pf_KImMylIilTEe2E-7sM2^(JfjeI8vk%5$xeFGvCGQon~SbK zwZ`^Li>fXDC}(GiF977QK@KHYq&eb=DVpHwF;T+qZ=Q3{oA}BKMhkphbcWY}{vBZ; zT*}ICF4D3-NW(29mxLoVkE~G*kCoA!62Z^=hp4nrrL)!%yoa-v;rf?`qd##$-QwRJ z=!b0SH`i7;pr67%-&E?FFs1E2}eCdxqnfKQEOZ;l5uoi1MAg-k$F_6{T;f`g8uffauG=rh? z2@C_x#JR$o_EWXQh7N&7o)P@(4}vD~s)~HAEZ0Z%T-erAf?Qqvzec6}W5_OL{%Bz& z71BK;+VJ%0+?@^;nt|3~e`!e5S2dF5%^skp*$?7`exgepO(nKJyjPRc%%mcio>n!bAtCYIt{<1TWkLXG z{L{EN4n*GfwaaRS!4ZTtrb;NLQ3uHv2bJP6m>>tZWW&9!KZY;#2^dhrlo78gh)J38 z4oQ<6P(V-(%b|r2qeadOrK?o4`&^$1c*P`RJX;&P=~m4AO}jI+IaTr5r2qO{GDRSK zs5ZN)hPB~a8@b1RgNE7=D79I>O;MTw!fgOJxvjt=3?uIz@*;FcAL_Y|dn2ogu>TfN zKs=Aj_2-P80__@pg8<@G=S$w1=KhT*6!9buNNrHC9}V|<<8S)Aer$5N!upQE?P4(* z=-8JSk_`h;)01;1M8rzkzJ1rrQyXn*SgBa~QawzPv*5X52T-|J&>$v!sm0W@sz5qw z35cDdP5o;PJkbQ~!j$A4{on4-n8>7H?6YS(Wx3g9<@W~KD&cp~k#A}u@Hq8-f>*J?`8o0 zHmi0Y8@ahzO!eTZBMO7A3O`Tz=haAxI5ym~gyt=Zb+M$824il#K+~EU)4VT^(0pboLmT zI}(BSs(^^a2(*Kpr_54=ht=E*Mh{+Zl#32PxE}o15lb_0RBTo{=>b|9T!;9z`i`hW9m;90GxML zl~)XUliC=>9zphBb|SCcVy33BvyL(~57}^r?s&qp>wJQ5Ns>)vu*B~wkZ3XgEvh`) zwA(!{hxOWb5m6MPQm{@r6%F+us`lvM4%y8Q%A=x*)wOPnk688+g3l+FD^8LYo$Yjv zeUp9_7ZDbb6;XC@0(3K&mx6JI(+gT^HVv6|MzMW}C3k`tr(T~dF=Ei~DQW%S?flC?VhDqkJsv5~ibJpR8nDY|F8wTtqCO+M zgSF?qQZvcw?1zj>|2sOp8l)bVlGK@~qRXQra*R$yV7ekfYo%+R%IX&`0SM$YJ@Y(@ zYiCiS2{3nWhzU67)|UI`FuwGH1lbhYblYr=F{1bLTq5d!zIDSphWyE0OY|g98~vk2 zXvY|3?&xsLMAllP!@2(xRsBY6uE}mmbXN-cB%6S0jH)d=wXQsRH1vcEJ()jwFEr@} zf>yKj9<^U<^*~na5#OJWdNMyiBM}+s_YK)=pWF46Gy5m7z(82wg{vnwpe)@iuUK=li74A->$WAT) z!|TB-TmN%#FbG`k^!+hZGimhm*o_$<{JZ2X`Mkg^{`3>JhUvL8#Gjxmf>VBO887{dP_vu;U z8`*9&uUb5Z^Brm7iF%8Gx%2Igz8)=lp2pj-7u=u)a)!z<4dP7_BL@8nU8j$F4UfL+ zMm{{1^Ff-86%6`ixV4@;+yp*L~+GbCXb&YH6uv;M=g zBNB!PUdF`_fFY`r@t-S_Y6>L9&EXjznhk(aPuKL|mYv5?d^TWRlZ5emXab|=@w|TE ze_&}6Z^~U9s!|NS8Ra#!)Kb3m)6>Tka2Fj_9EHyweI1t2_&B3pg}NE_nx;xHLa-z@{|jts(f9vI4|d&McZZ zPilTI1dFYl4RgG2`=Rea48HqOF(S4^k{JfX%P*^F!qNU`1N-lS#`Czgz}dt-vR&|D z)*6USd+rC%2A#C`hHbJ1_UfPITHmeE|80;IbkMjsGQsOPfGf14v84;W9X zXF$RW({msGBk^f5ki(OJ6Q^0bR6m=mmH37yHs(^I^z4A!!2x;;a0m`uXS)5fbHEQk zDoZ-ka5~^=CDy5{>pY?v3U8TIbU`%}_TsJ{3ILs1U%VH!I0jP@C#-xDSd)U;odUh% z@*{^|_jCR?Q2|HfbuJw}4VYc?-Nn^Vy_b?0XgI|KHRFSF*%fvF{l!cN-fHOvP8f*A zA5{4_8v^P9?Y;w8ppo35Q6$6!w38tPqy2u??mv6zys?(*dwj~DPQRX2OMJ0zyiH;5 zxtxMdto$DZ4QOh#jsaoc!B6DnSx-TNp;EI|7mDcgjAd8iX2LzQ6LIQCcS=%5`FV)! z#V#V?C#hGmIdZKp>)a;?Ok^2z{v+Gw!2IlZYr`g47i#rp!_~g^HQ>=EQ2BGu&QN}&pX#!UnA>sGtk_|nbVgRz zUEab%K$qSS?k(n|RUO;_k}p=eyy%eoyBwn<%O$R+nOEJ8+1ezu_KzV(!;Xi(;}W(g zpTFm2e4RsO+Jb?z=LMGW&o3zWISjN|QEpeZvf?@m+NYe`2NDw70JA%rm{C0G{yQKC znl+4Ggr$RAX6;i^(M>!OhA4?Al7M%${POu^gIB|pBcYCWhJAEwY}XgeEx-{h1YloA zZ@$4Ad5@8FMc~}k{hiq}4Ex|Y#Unv6b1j>K@NYpBtT|EiIQ5oCat!Ns!bi_5NCvmA z@=}n7E~iJK!ume)rPj@;RJ0=xGEd&N(Y^tP##up~7g`DWhlYj>)%5ju>V?jXP(QWk ztF0BdbgNY_i$i`?T1n}tsSV#gGr79NZjXDl>z*R&kg!vwUH)bZ;qky?;O?U^^&%ZM zpzUJB{dX8R1PC^X&|CcN2aA5)#Pq^ z+XRhT-p>hf1^#`n3LvfZ#|VIy!oxt)Z5)-eIuY zsCVYR%lY_jrr~}({37$zO}%`)u7ze=*7hf5^>Is zE~l$4;{K~6*h2Y^hd#q>Y@EBE+hT=GY*#UaUm~8Xp`}u*+}%I!q09U8^W99ZRZ@=! z9w_@qVY{ym1h?`GZW+Jalw&+)NENZJ-{BK&5qTzINKBMh`)Zi|^cBA|`oz#9HLV zR&8oDlq;R3{$WSp0Cv700NG)J0JsHZSH+?>uXX)J*y>*5?E5zPm?ux1I~uN@ z{5=0f!RLSqiDKlR_zdtkOZxrgUgxq~jkWc7ujI(un`wjikSEk68nZT$&YN(tr4>L1 zgS{HgBw91taeZWC2?+eVG1eAFw zfmUVFx@cd}{si}oa>pf? zXPeY}zaleRDd17gL%F@G@i|UALHh^UvEo$n4_C&`I^M_XCEU?-F^O^BDswUFOS`vF zSP>KKG^=~>-3={{cSMs+EHR9kU>(9^x+k~fgQ<#RN2y04t``pGUxpfeg0paMXvXzn z^*3C(yIknISrx60T8FJpo(%e#25abFJPsoY`4Ow1avN~at-Y-ULR)7kU#OinWysnE z*Z10K<=Yaixg(3io|OrXPi9X1vWguPG-rg)8Rhp~^8uU=HOACs) z9zr_^mN?2T{AW|5WUAaX;i=tAEo+iZ(5`^Rp$cXmMiw-5d0UKjSdBXN9r|vD-vKO` zqFY7uXFz@%g7*wTT2kb+LqI+S%w-h<{kkW-|son2nB~QCS zqdEZwY1ANW6tuF7+t-Vi!rCXow0ig&R9r$r5hG)1uF&WTPmMQhtWtIDF{h(#3KAo^ zo@^J3qj*b~slMw4-)xE{AoffNj8C-Vu=*gP*b#0=!{cu)p}fCi8-6|ssR&wF%e5l} zr8*mZql?W8zy?W_dN(O^P^(%!1Nr_a4<%Y1{jxMpD`#wV3Yx+;#@Z&@sAPv1j>b_h zs`q*+$#`^91HIoWOwR9~oga---MXmn!zlgny076%^hEgXZ7cF2VJ4J{(-fQGIp&kk zW_Q~4i}e zp&SzHc?v33$1mQvYnVRO-nM(AAk1{2LA!4flIqvhfV|2*0jwMD8-=}DyW#|RDwDuDM<}*6(8M$m zI}mW@p?9JszEeRryKVK;BBnlne3ssNN|vQj9RBN? zXau{v@n&fx*}Ua+b?}H-Rw(sh%f}s(J7k_rVC0nXV$`jDW<@50it|8TLF_)pl>mZS zacD&af3|gJ=9PhvV1a&TsPWRg#G81DEI|9Oe=xEA$>Z!d!J`-;X$T}0aVOzXFX62> zqcz>63;0Rf5oB|^rlM@qe*Ja%qh}}u)GP@&M}?%}QprPGS=nfdQTuALlNHVKUo!!F zzGgIzop?UMb`y=WOsT+FjRx^K{Fo`wrsQsB76K9pMOtw4qAU!iFo&r<{e4sWpP^6CdGeYL14kQ01xDjDr;)vv07 zEpHYWPNHs7m*j44ive*z=+4GuaNGknzyw1BNT%ZBR-$qyXm)#$XwbLW5H#_5t8R9M zpxp`PGPb`jvK(t_Bw1zKS&=_P1H1*Yc7WY=DKIA*{b1<^*in`6g}6M$hZx8p;I=C7Uj@mv z70-^r=G5n!A2gRO#N7j^^OF+dN0r8pi3`|=tr$a4>!!o>Ik^FBJPI2SXx0S?bG5uH zq+*t}B#$Jg$Ra7 zbp--sPMk=_OSIox#{$}^5|NDX@~-+Pvry~INwK|_*cM@7iDryWm_pI0MH*+tCmD*v z))w4(8sxuem3dj_Ws5aj>Yyo&6hbFtM=2V82WvGK&s_kiEv#ASx#m*CWVHz`+Shb!1aUS zkMI!rAv`@KG2{l8ue|i}^Ft)8^eQKck&?oZk(_Z$l&O7ZY?j!>nrcF{szB}y;M=r1gY0yh80R+pI`x_|g2bTrrr%Sl6x-i>*u6?1d`jwHDZW(!$jPvW zb1v_D!)a7PMjs}8Tsl+{X#3&yEMzC=*B2`lDU|E8n2itH4^&ss_RR9|(7ygXtR|qD z;K|`+BYqihaRvnOpy@S{YTA^7nUNIU+Qy-#&>G|?Z1&T_Y)$*IrRzl07c>3bMrh56 zK3cpm+=S=yw49ci$9EyME2G^7aVE{!0l2~)-^T-ED)a26@2OY{hi@jn$ z%AR>H&%fe;=qTB7#*g0SvQDj44vRnH-IzqVI7*qp8K0S_e)c$eu6U6V_1av^g%^DU zQDtRsa0S+J5C8r_5254ec(9I59mq#;uW9ZrJaGY+DL!2{*8=ZzYnCov>uPk+Uj55# z>iU;5BM&mv3@tJ|8SgbfX{z34{x)B^Y^_scQ&)YUnZ$OiDf;|?0C(k^_=>5^F``ZH z^Lo{90BitWW{f*2cHX|kOmsW0L<_%9>QC5x^up2&#J$YZHxq!$G%MEAbI}LjqA;|v znQm~mPBbHm>=kg62nLyA;nPhPxH{lw+F{P#X#+_uYfC3u7?7dc3Hv1nCpUGdt1Fl=_|tT`)8-sT>sE0an<`~J zN|NY}@87uLTF2zuls4luc*01*_-2e@lA^p%z_1xdBh`IBXoa8d&S5WU&zgu!4t1Sl za9sILp5`qq&;bb7NnXgN>L?p^aNcv0zY-#SX@T7({PI+Z?HNk?a>tAgtXPrri99N?Xg)rAi{!Um!R6aAFSHic)lPutC`90LrrcbcQYb52uB615%7s73j$V21 z0YljM^H3{vXp;h09<@wpKTSE!ifoJ9XwrlWW8xSo$YT4>V?ODVt=lo(7GbZ$vBO+1 zdG)$44Q6!yd(UWnD1QNHJL_D#$OFhVAShoV(pv zxU~{0xhdMPYaE0P@?|Dn3?OxWDh>z#m8%sF;a7K_feI4Nq>-~sEq5_9G!c> zk4kJ%EE;@IgF!+;Co_}m%XCF#D&?`k#rjia&vBghMhZ^9GUSLqDiJaHT3TDKv^^}u zD!D;RtK2i`xvo0*XnbINFti@gw|6rnb)^#niVqjDgV!5aH{C7y;zEeUxuK_C<%4AW z;f5hez-zNRH~dy72iZhvA;Z%C8-c<>*ql}a!2{s_jE?B>m@)Zf9pIKD->(DOBQL_?a7&cQ+O6cV_g$rB}=vN1%*AcFW9*vOlz5M~^`wURMP<)#Z zwCD|+mgd(s=;OiSO9?J#QmEc7kViHJG4SRMoX?ZA5FEY50XeCwE-!V&gfS&n%vQgv z9$PV7v$pZtS8*+c|cw+^anqf@$sby zf4WKZ{!J0|y@-6O49+ z6{WB?c+t^%#xb(u?y@u230~ihFB5uESZ!#F57+}2NI$1e3=wwGi zM`5g9#Dfw84fgX}6Om=c6p-2-Gg8uGryIEQ#&wI~0t+$Addx)Zi+nPh8%}=%ILR@= z426f1&MF5+o;$H`^b%HNpVO8xti{|ayQGB+;5IiMg?6VLV#g@JPxm<9)GV8%?=%lA zYW4s=Ot;Nx8?=v&LfB#bS2K;|-6btYz-boz?j7!4pEOC($B*Rj7obViiS6l{;7C?@ z`8KAVq>lSu+x8>U#;EPG9AEY<4F@IVeq_r!t$n?gT$Dv~ewyQBwn-AHiGBXFiz90vdh8dH$an2mpO=c;#n8hqC z5u`4L17T0ABRz|f449n>SUMkWOExJJfrUQ=$UiF&YLnY&{h53=2>}zw*GY1I%A*YC zSpWEJh{OD?K%RY$0<&6roqASE?8lxJ(OG*rkAYhjw25$ksixB0aOVTQS?%>QK~iuI zcTIPzHtFuIE;qmgX8rLM!4o2fQ}08^TiI@W2i9g=)gpfz+$mu)L#=#MVAFLaZy|b(*@pQsf)BUK-QfN8)S@Lj`9OENY6{IT8A) zRTt=7W*7%`o;Zhj-l!a&%6Iu;o`QPA6+9r{?X_G~{Z~v_!+EpliT!soPryR%-gp^9 zRaWef_6gO!Y)gQkqP#KDo~AGM(-UDbxo$GjQp^aZ0V`fq@!KX{;y||!x3v2@bQQyQ z5*yGi2toQG023r1J`x0X+@hkgU!&2A)ji|V7-Qw?smj6NdFN3r8O6dvlowp2);<7L ziQ|o5u@tz&r#M-o#`$1i?3-%jmZk+xvehF}Oz<{E7xQxNx64K{eW>|%2h}oHT*$!d z@W&w@(}x#hZvh;(JRcwEYA9+H-nDAbSKgcU;@Kt;^?JcAtjQ++q6{z<4m&I=LfW0T zhqNGs%lY^k zR}|cgbbJHJ6y%KT3c46*>f%inPT@BJ72rF51gylPT2u(;@O(X z#QGdwN5+uQ>NyjXdL*pUBw&dO@Gg$>f*}I2PXo9ZM-K4WXB`y+9OAhdiK~}g8{Q2w z&vvzyVu41*BXx`fH*6KN0G#?>*E#0%3aXt7AHuZ!M>Fn&1so*FsXQ(+XpJZOi#Bt^`>XG`gshM05oWG=YWOQC$R!@=Wq|rmc@#*7Q zK%v&JNA*=}Sr29{U%5z`DL#<#~141Z0-h9BsFuH&jP<^XO)4{hO+Z za3Tb$H=Z%W3Fi8eWYdv4FEuMh&@1Nk8l4*M?10a$t;4k1=>`?V%u^b5v$xpWxwP-a z(0zhTuETL8VG_J~Elu{KxKuJ&zMf=(dsB94bLRR`{2i$fEn4a!!#nAEZ_?&wGNX+w z7RI;o#0h@wotoFN93>aIZY2SKs)r6;XVwq+8r(zeroP44g$=WA?6Ap+bf~~fi;jNu zexlPBvGOn+X^M18{xI`HXxX7>}Po05^BihtY<`ga>Y1*^pin)TKtT)1)D+0 zHi0>|RJm~8&d;SHrKY6WQy^+sKo4B8()2Wp=+U|PdLxw=YK|qW?c(V{`Rg>7=d~au zs1fiX0}|@GAs^7PPKKv?^9IDvbUHCfQW-6s($3{(6!?ykKuk8)H=)oN=RX+u!*Swcu!ZPnv^ojgDInr zjAl}>7n-@JjW+gQH{8?Pn4LYCcFUA)>%`tQ`jzBwmxmqgkE%YqB_-t9tvn$Zf=L57+lkn*;`{j(k(7N~i% zs0X~(QnIO}9_)*Z=loF?3Ttp(o$*0m`E&Fi&NNSF`gX@QS^T$8e*d_JBL^u)}FVt8Y) zWy)?vkokYnXF6VNzDrJlPyh&tV~>e$u8IdPDzYRc90ck=#Bgfe(YLPKD%adK8cLEe z8vf#{70YY&{Ja6uL1FYD@x947OEYpL zaHpSBM19F;SM~T?ZO;9X*=iD@Lg?x-1Oy5B>i&=&^3CTPaCdX_+~|e)CSGL*MkjEm zrJ#6$n-TB9MX0p%F6Vsi58m28wTHDLyB2=dC!N*@tV%!qP}1-7YOC-7VeS`M>#mf5-FU zxnJ>uMXedGp^bNr*Uid*U4{c)!T36W5Kn(rCtYcj7C4x}w7nI2d8J&$0OnjDn2? zjvc^(bS`{Dd4j@JNELvnTAe4vE5k<(TKL6SSia5AK5U?^%#mL@4>geeL)YGI;!Uz& zEE*IRA=|GRl_XQtxgVM@4y7c(bgoO(vi*VZgQ5bE^z!u{JA1JNPC1e$o zyO!(UKr~*DzbN4VwEP;)qYf5-@+bR#@hGFYNEiFKcAH3tG#TeCd=I-ja2H;B`uNk5 zSTGSlUW;^A&>5={HdLHc5nC{^LjJfRh+F9XHji)AD{*}=oMpJ*wuSjA=iH<|$``DE zF>7gS-v-Da$|1#e6u#pj@J9sWE-9cngBJo1;IwMcBLSPJ-c`~JIQbz+c_dIyjNP|$$w3hjvUo{Ln!D5beSeJ$D__78pWw3az{!_$_K7#1xm#MI?E&^P` zxR_+@qjev9dAYndvyjKyv3$%2+%&(^9JpHYy1)c8$?xkxkU94fxpsF}p3-uBf})zr z`{29g#<3G{O^g&O$o6J&>ofCy2HfgL$&6-Mem-+@ox#u*v)-#6lCfA9csj-!MVG3Q zebizr+)A4}_628SA)sJ%mW1ZAPU~M=`;Cau^Jb6J;@n$HcN| zqfI!GgktQZbUqcjaF{}2B?}pI`q%VjpH-Yq_*z%EaI{OBRcw^m8dg`E+g@_*eb?o! z|NUSlLnf{Vx7$5KB{8i}L}RCVZ5I8i=EU@97%;yiebqltjt!bLN9sCc;l4njrG{Sm zeL&EQPHyi;usi{TAYsu8 z>nZ40N|%%tlCQ=5)qrlEHQP|d!9>s;Nx&a0L*OfDSqL<9zBrh#e5f8e4-3h$+O&p! zrpFCK(?XB|*A%&ugTGmlk6YTz>dE&#Z=M#z)WWro@@YTSY8Unq&I<;jENk||=j#bs zSF?Uok-BYqXYpm};;^S2beQ4%R%3-F>*gX>5V6feXIdjLF z7ac2U&OUy+Ry#+el{4nLPQp&UmdF|a5u{Pg=Xt#PPCg>%sjzU4Bp)+i%65(Np8dm^ zr?IBHj6eiA4TIxGV|R`+1tVz#1jA9Wl^5V268}9YC^-4YAOAsK*f$^j)B_9K&G~VE zQfSbVj>h3V?qMP&RZ%!ZRbPq->Fv-m!GqT4lqr|YxWCgItI>@FkX5Y1J68)6rqd)# zUBRHJuJg`ZWAE&jLU^{527fPv6l+#7z;<gXy- zvhG|r+mTRr0FwCVaPb$tffofVu{W}ir3!H1?YSkEFAukrkjFSPbF)6 z-!SPIk&xnT6IF8{mIC`&D@$vYt*m#7b5>?{9QURitWP*F23wDF`XPC>lPH8xa+WFhduUMc-sN{5Vu;W%F1lElq~ z+?E)d$d8bIb{c5?a0vXMtxbNn&>m8JK}SO2x>U(i)<*T#yxDOFMSbA3^*T=YW4pA5eEa>H*;Bz?R@Hlm)aHFJzerJ+S# zEQ!3J5aO*-Xd)ulxme-`VfS6*0v zEN8wpA0_K{mgzTj0$P-vwu?6gkrkzi+S=c=8g0JSZ>{S}*0GiKUi-$!+-%nXKhdtb zb>{@Ie{3@tfNi7d9fL3v44k5LQpyJT5r{}}pu%NAJF;Mds4ckgafLgdfIvsP(%&`{ z#(baHdUGdx?IXwXy7=C_;U{{$KM8ATe?Ml#O!Bw2v^&0~(N%M-SlWI>mXgH-m`Jq> zFPAoEmshc`ci8|pR|tWR(+*N9Y^LKJLY`QX(ywn7gaMr*7Ck`i#xov9-;8YkuyB!JAR`b(57`fK@A>DKkmR9hw-RLYmtztS)WprGT_V>o953jY3u z;B$)^TMz7g8w-+^y3!at4ENW3c^lrl6QNKuy^ZN*eeE8$56V3VOqDs057N zyL&e{uDhvdW`l!Z01O3-or3@?$QBhusIKl!FHFW>%<}R3Nh9OYOvda7z~YbgZcZ@F=O?;|zj~N5Fm*~*vXF+R{Wa3hB!qF{Q{ZBuH<6{vN z;L{~1CVPoKWXr@|Dme_3RI+eeqWppRo?EqVd@rSS?k{3B;RG!_S$DK(#%!5X_g&6? zGpmvrV$snfIr^JiakfAD!keu<3qkP5WysVEH&lv2Ly3z5_7q5a@xhd=PPOP5bs=yV zSpXBBUv{gR4&8oH8ATMo9a;J59>u2ig`vL(`_6)wrk-=mS8nJ0%l*9vwv}dp(}5lq zL*%SkPAXJv)is*`vs9!t!`VBrd{~GyMOAW8)c}wLPf$Jr17wEw_MGvfg;aWU@mzEeswWD!()|+B z`Z=ED5LGW~kS>RjQk;U#3AsYM;+Xrx(I-uB>!*@i`L@gBt#_ULq$__FQ}kV3myQ%_ z0O?f6uJOs$YCL@ds1^%r>W94OtQu=qXkid?{F#x1lm-&vkFor_lsLS{>s)tyEyV0Z zxB}0cWE~gWj!v#bvZ53tPg5;@M0={M6D`_);qi&h_$Dm3_P6~P3(jk_H!LO&4vQnz3G8Uy+M}tJ| z49V~zdH|2?&BN`<3GI_}Kn=^uK2iZqsha6tyYc>drxeEh=QjSlsJM*eQ5Ax8GuTh# zp@Q6NC`O@7eTesWkB59+CJsN8RSO_H#SHy^Gg46wja1??LvvjXNMD*Zq8ZESvDHQe z_`E@lC?G;wdvaaEPgr19egDxqMs$f^QQxBEx&k|M|A}Y6ER4GswA)D)r?0>0lpVzA z#0Ura-#W~!f4i+!F)hDrWuqatyH%5&sFOTJ`B_9+T5$v5eQ5*!_Q>(db8-a<IE3nbp)9SFr!Bm~+-Wg9E6h z!w%Id&X;8}2v{ExmvLwRH!gv;Z2ojCJ?32)*)s=#nBa+CR1pc5fA2b4v!N15HK^7; z?&eQ?^qh8NN6cT7o2)-sOPtQI1!cOM>{$*(>@!LQ$ zGlih>6Jn@nAy#S=vq@S)_h#exP zTDG!YE5Fd)CYgR-szkIfT8rJOy6S<{ z@&UT+CB)%pzo_$j=n*EZ2>NXSqhCJL{tMyLpB~E2=Mwl1V7?EUA}YcN1UhEWKg2%l zs{pkE9G0f8Ht7LG?2=eKNoP!ux}@eE9WAI4!qj1&l&@Owfz~2_>8qdyz%IeIJU#vc z{Rt-j;kDTlv~7}s6&~~-^+Y=pQC<5=0cz22SmS+p_^;KMRE_-l-*ObE+fKppxX9?O z5kx#mE~wSc!XkWrLZL6tsZ_zJXo+Uq>ioIQa%aT8yO&t%B$G`uXq?SX=#O74IxQf# z!^&fi1ghC2UrC^3iqBfw5T&3yZ1LKwS(f*|o2(r4UjOE;(kJGXt-UMk!&DHEEgyiw zDXOo+%K8-e4HF4?d9l!tA_Y2n9v-~`5icxtbyNa!-iP@dN3pVC%W^@4N1X(nL=w-j z#9?HWlZ(0z@7e8PR1gJx4DPpRRATRrkNc}$mrIx%z5%WoPs|WL0i@#5OjCz1!^SS2 zj3E&Zw2=gm)99Q=%u1^$8gLP}7JyLptEH)n*HYn)7Y6)bIVS)uVv3!ZPIE65B zvTs+5LsA%~e|fi(?tFK~+0eM(nEMk1`X>@4XtZTZk}DM!v?=4ExlGpMK-(?U-3kVW zh!~MkzHY&ki5n=Lq@Vd11|QF6aLiPH{YhK9_hiZOZOm^?(AMGA=c7M06DyKWU8{11 zT$XB#b}=o(EN02Wx4zH zoW}&&!pQQa$l~v4o$y;g5|??ntkClX%^Lv3UH5TZm&gcHe7#|#+dJ7J6-j@6`t1{l zN$l3x-x@N_1W9M_N0F4!N9As&Rs3PZh4WIu9(k5wmTIw)pcs38IuFAKyEUwm6tl9i z`NnyEjGq6knqPm==6$ngGFdinGDKw`8hiENvr6nZ&O0gDB=b^MvA%ra3AYNnZk^~b z)>-y`A`RFpaD3+V1!Z(y;#8CQ0*R^5U$>rEcpn%%!$7nnzxbXj{DhnKGr?z_y(k<) ziPfmQ-y+`0A3H((TI}J$!AdY1`Xs{(EzFqL_>L`*5%Gc2>R7acN5!=BKqr&GCzbkYyo%wi=Tasn zPn_1ho#)u*iKs#h>LP!4Kcjtr*n4_X40juO{-9)MOd9myN40t3p3~zMI5QP}q>40i ze^|%0^je@ZByqIm)!A)(GRrK&D82TRieGKH`cVB)x$Kk&?#@a59z9TE&h3=VHsvW) zAJN=|W@CfyY%ccqo&cH+frt_G#oJFho_BE%M~i0qR;g)S2I5HhmzJcc27g{~koA0c zou`m(!1?_f7(@5NZ=BF{58m0D>f?9Y3qcVN2~2bSl>@bEm=ehPtE}%>vFgBI4)!qn ziXgYL>O~YM)`zaC0!zx#`Kru=NA%LPeOE0>sxc=Xkk>)^1vk_Nh z9sMt5b)a&kv9utvCV7au*qh`mJ(^Ze@GO};8NA5|dq7UwPtliMP1O0E-0j=et(~@d znFH4%!twE_YgG;a_5qC8bmU5YIZvsWu%K1a+I&o=vXS&mWjKtX0ViL>Vd>@BB>=FX^6?zT?YlT;Irh8J*QE znaGEwAQ2Kg=6^wT&`V6=<1#wNONiF|)li4kDm8UgQgL!P!N49VVm{bdA@e~Z zRX49q(W2*v8fOQRZKEN9oWJFo{Y3a18K6MWO$W{>icOTMIl58;P-Q8TpE`7j5{!Pu z8MKHV2c)br32bp}Vu5IS6Z8ncpW&u$cY8u3-nczVSYu=vAHWWmK}dRgWbOso432|Z zN_~c05d64`JS#_1O{1o^D*mI%;k4o`@j7ndHcmk#zoTpNqZu) zGyLpfpj#P-iphg7YJwgl&mc(;)E&HNl^j~JXhDac)KTS7+&=T4cPn^Ren`p)R{j+W z_;U;I_vgobq{Mqjk*}w}PNavruL=ogf=;NRZ)7HTaX^~C?|Ux0uULD-(a|p~`a&m- zgx^?H^=&`J@BZ$xIN5h|@NxM--Z|idj_A&@{DJ!%ao>Ba*V}^n-yxQc5(?0E_6iu20Qj0Uo;~ug1;mYtSt6cjG^$EOYKnsQOH?wPm!<6x0Tah& zrwHl6M|=0i(!5pz>8=kA7eq<)NReVjh7w?|c;KQ@)7Xn94ar{Vj1EEh>}H>3Aq37t z@#$!-OFrRF+0Hkr&)pOo{=NW+jhQx8QnPwomks8LN#Hjba-Xfg$sg-PG4}EeS+rdzw5|KF7#)|0iO+2%<-4AGaVZz|u z)BZR~<$Jp=|E40Jd%N-6BX_7O0BoN@yz|x-Eh(_yS?jn8hASr%7U(`kWhFy^o&Tmc z^3&B}H%0JQ*K(qSMxm!}q*66(;{=T#(r-cr`)@E0rdiaQ61@CYv}4T$vy2w9mVR|U zkf6Uln@q&#n({bKe52?1niJ)4B~GliJ*rDyEFqzxdz~C-;Axs-1nJ*_;^Kr-E8j(W zq+*oC8_yy#G=cUDSDb0YQa))+1P&r0U(}Iyp?Fa4toDL+?|1TqDMxAMnacH z6`V>>Z&rw~)9SmlKnHzNW`)lN!GsU@7usQaj0y5q&fFi4L$8G6gu6+{^8@Gp;o zL?EL=3m$!){gP3}?hOBHkQWLODl4*|Xl*4!BBN038sB$E99ENwApMBJ1;DIhXWFfY zK8^G89&3Lp;udFdD(s$ULysY+xrp~#V9AV;<=Wi!+yVtnQScH#g3X`N^X_vW#PM3! zh%2$_b9q!`i>}$n-}C=g(=WDihPFfto?YieM;bi`wn>WE*+0MycGYkcyt~**I~J z->1fN-Nm1D@?++QOUQ_wW4CUzrPikcR*6#;mI;N5r^u};b6>;~CLPz;HWAD{;&62g zYZf$M+eh=R3IL|FS6KaV@Q~j(^e^^!4!!Vx@PYb;-`$S8NNB=u3gFX>Xrr~2TN#!tbTh?LL_z4gN~vcBQ}!m8rDET zWIa!#+faYE;jmP<$uG3M;CSJ0czQsk>)I|8lX1GB>H5dJ)vu&$!#?lgKx^O}P=e+i zH-Pi<^2Up_{<&L1F_a^Ds0jA#MHSxmrbzuPJuVZ*qr9Dco`~Lggv&*&5lRPneG)gz zfBj|3#{&=x<{do_I_68s&zbL)x#%0c8KDllZiRnpY46rm$}AX-#@RD(`udObWDN?Zj>hbNgg=^iWHR3c9?<{g6A*q~!Z#%fS(!faFZ zagui*Rnz!vBNGV@O{UHJ%|vnsD9hKq)7X1h_UAqWIL(EY=?u}5l6u!yqak@BD<`eI z?*U)~He@cj21q**>Pku=H>Lg=hd)P+I%AEAcCi@D*Cg@*66WnqFgR#T#uKmgbmU`0 zF6krpBB}?yZ|4fK&S7EC0w9~kj=xoz=cT48M(Q`Y$wECWO`4^KfpGaUU$Mt&h`%dI z)8NTfbKy*-sZ6d5Fii7kQ#o-g$h(S@I0d&{0`ejdnZqx$^ zyHj%uUt@gvFdfS9yP%Z-n;$o(f`|AQq}%9Wta8rYD<;iehxYE5wE1q@I~O|cv_ zVLM|>3@Cw7YYmjTy;!~$_Z9zyn;?`f`#Cx4F@d&L1Y2>RtQ$vLZ7=$(1iE>#cNP=K z71z?G=zwh|3~c;*-K5QqVh+r)qjdj*Q?sE(E!cxD!m~%wERa9mh92qcxQzWbQk2LBIRG@vxk?P8aK0ES%>LZK?ub(FDUnSl(0z{z zfTMUYLxSDE^Ms1YqXzZ91%)W}`^VR<>}^MzQTMnHituZ+Q;~v~g=-+RcmI9~f)@Gv z?$JslX*YwEbKG=94F2U|d4wM;5$vOrc~bt8C4805K)C~xrl!~0ha)$EK2d~HG~aAV zSy>r0^9OAW_0I$JS(U6nqzVPz@{BPxnohB}oTA8Cmiewx$v?e0+(XGON~h~_pV;R} z#Px7~{<`GR#SbZf_Msya1TW+N-IOk=3X!UzC>_c;05$}G!6xJFfRtIDyIL!+X*XUa zAWH(Eiuyiek~2=cBr(NK|nUY7B&AQHRWnDs9Jw0H|9~1ut}x;Gx|6Jx3cDAPxm-6P&Um$%N}iEkk>QAQ>^Z0TLIx< zdSS0sW3qY~DO%|Vf%q`>&+&V9KrE#*?Mas3a^T7|2-+h7>zwJ&Y{27oxg*_c`IK5bn<^keVmbPw|GAUIXkE_XdE2A-D)W(!jK z6uXnB8oQJ3_uZue^eaaE589xBC|09bR(TGp?y0U~o&mAM{-v9ux03o^Xq zPPfO|_#B$H^3&!n2my5{ttPk9)9UfIFhT#*_%~u%oGhy;6d5?SxFG&DY`n5)3{09K z6)OxXZUFV1%!M!SQ;DG&2ETrzaVYJHXCfxrQb9Of+RSlfa*O9n~w#@>%A@pQhZH^t_<0NPnBWs5!+SR@`kChW#@OHTz&Kc?(@ zN1M;d6HIZq_r#-rQx(DdpQsYhAkPNo5*6PiV7vkag*Gst_;68XAnrH;0Y-w{R*-=O zVdNtN&WypXbjNnV6dr);>}iVY*WC+qI?Rp<|=kse6sRa548dENXdZ6p;r$8$wQo{m_ciq9WGA%Q*jAQ*DDxQC~ep)70?L`@NW2n)i0P-$JVOpjk7Fb zQ2CMa>U>X#XZT#Rb3^Qbzs&Ef*kAGVUqJ+SgJ!Hiv}(=V-Msr`b?lDsPrKepR57#t zivf=hUPq`_sqtZF^o*N|BE8{Nb1v?{RQck!A3mP+(bG9nQiAIsWOfL=U@*LjL&g;L zOR0vMG=Z6&V!la?+3)^DSJls^!@cdYs|A=!8jReKU1%L&Cs_~Hsc`{ou>jrGI6~BVG!La2N$sUK-fx+30jy)OR2Ym z8YKIM()GoWxhQeC*f;}`qGgPDIv8Y|QC>tX&W|ERV`J*mc6%Jq>Dny0J!8!oxvO3} z&~UXb|NPg@W=@dPF4}3<{w9C4F>gkac_y0MhtKi9r9a1akz|B{p1=T@GxaIIqG1sR$EGyY}Gs2?73yvT_t=<280lhhL z_Ye8q?opHb`a*->MT_So&$-9khn3pd{GgTS`F%Hw+4X>V) z$9{n9rjiBGkYSe6Q8idyPQ6Q9!|jZOQd()Z6d{Khk1Vj3mor3WgT+gG(gohq?j+&* z->wwgQAl`)Q(cRju=#uREb zsgQ|;&%eMf+T6S{6{d@^uqsN*Ml@w3z71hzIjr?1@tGKFMtG_~cCVET3_i4Yo&6-; z(u;sAGJcI1Qh_99BZ7ek0?LCzrC<@}QjqF9lFBQg3%wRyA_fp`lV06E#-y>lhW|-g zis18XO4)-}-^zB{a3UhXiZ4TVO5A%{FgUz z5+|Y1b?>{-eMUylN?40@v@s=Qo!`dduk_*l!=$Y zr4)uf!}+4-#Z#wOk4t8n?m-wBZ)*Nd(M2a{WOA4CnEv~qqZt8jZEejrHNSm7Uz1n0 zMvrZERlV3bJtc5mnhS+Kdwir|FTir~^4Zo>Nce|xz#96lK_{hkj6eMRPUv)rkftf; z7#{IN7)eR$WNvYKZl7xCMc0SMvk{?(hYpT|afDGWx#>BaWcG9Q!#@u;HZBq+k!f!2 zMl!U|Ys-Z9b|aCJ1&_uGFF%ujsI`hyOZF#URnW-mmDRWBd{tCAz1h}mYv)sRv?2{Y zZ}Ap+FZL?CSyAaBo_Oa5;~9q4WQ01Ui}^UBxSX`<0qPhB0oiKcOECHLZlNxl z?eZu6FB<3V+s@7=ZHK;{^vrr^KtMvQV5{ut+EhHW(R#;~!j_i#mjHBlG^v+~ zm0O4k#Q2iOW6z3v=xHroE!&e@MSUB;8JYbdoQ>Y2>SA%X`K9&dXB%Kf_EI(}n2c!w z#f6}Wym|ipB=_o_v?GDza*$0r-{p92n$XVV9V?_~nb;%n zH_f56=v?$&W1|tZKR!9hgKm~V3vswp3qi^Emd0-wsHMxZ5~PEUZLJnM?iZ@$}JJ%;#|&)ZVla$boKJ&3RwvtOG~x|NF|% zy+WZ=3AC9$B)=#OGJ~OoSlj2v$0WDE<{N=#f?--Woru?ILBVVoWfh&rL7_FTRzG)Q~}7O|KeUa9XLeWE;cNsQ;~+np4@ zKee=r&Zf^z{>f5N2eKQ^@twmQ0@9CZnznx~1;crKfRm!E6bUB*0+O9FSMIDN?2tv% z^ZqbQESxa$bZa#1%a%~PyPG8B1iH{^Dk@6`6E)s=a*Oxnn{?j>w!*sVxy30HB zBDQmx^2&#}8+43U*OP5_C$o3whWA~NhadkkAEpT&vN|4aXC`+Cu=I-OBY?7Sw9>Sv z(+$vTHo;wbD-%a6vV8@(mkDm{$R#a|dWEU0Y5w%!v-3s#hdmLeZubij#cU(rXaZpJzJ5K#>3`KvP^Ye{I=Yul zufgBvEZwdzoRW=5=m}?u%{)}=zRF86*sd-AvU4;S4xhoIp>MTdLqB665LL^HJJ@8* z`6Mwo>gS`CxiB6_*d#h0YyX+c)7;B|fIx1lj*s?!5a zpY%$HOMi4QXd=v*{&8G(8{1 zKi0JyU0&GERR1pJ1HdRu|1)ERBoe3{qq<{htiALf&g;C@U1QuC*4D93nxf^uZ)6;|Nw_NfExC#L&^O+Qq^Pe2j@NQcZkm~T)sdvb- z6kN~;6FUxOh#1-_A9~_^lK6`E>YcKZ#N+e756(t#fBdNDYo~!F*nYZoCp6x+4*{LM zlb6q#nb9ergK(30$XJ%O>QPfuv;Uuk=;$(dt&{bsF*<3r zOKiFSo&V`O?qzK-|YE+v+)?RJXUI za2Ge4Vt?CaSfJZ8sk7Yr?mX*7(EYmw`JUO;@nxE`4s|ch@A7T_doq0Ia{qfr62%XY zxr%=sw4Cp5PsAR?M06lPCuIMN*PL(9qd(_Dr%oi1mZSOK3@m0_)Xc)4!5%6&g?M@B=3bxCu2p5?o8 zkd5QF{QU>>$}VdQn=)-pQumqfUF9Iv|H2UPbI_22GJ~}ayZPH>bq)RRBC2bAZ^md@ zz_dWUD!)Uu8A?Pig&bH3@xPT3AW(t$Fj&~xxp~p7m@;S`ZET*Qy$vI%*)>&1_?cy~ zEhS-mr30KI=zoTMoBdt+7Cv>d?EcVoo+Hxu>e{8n!mPxwzW_(y3)xq~u91;dqEY9# zmhP9wID)3}{%0jscIFzRJhR!uWig!)*=71T;cNlHRaN-tNVx|m!rtAY0jP}2YxI)Qju^)b%;waP~pXW zNl2i{A57y{jD)}HJuC?u8BqcJ?sO^%u9L1!n`^!NRdoK+qgeDkDMpRgAKH+R@I0I# z9qpFh0z>3xAqPjB^3-eECY|S-MSdLmoFAV`DZa-Wi;X83u3(It$|gt*Ymu(xaJs>x z(v5D*DL&oTDV-)ORsdY#e=$~&96;NpKXgjMW7_UYW5SPfLy*QD_m_Xd;qmeDZjK&f zB~vGXYRu8fHCS3NGDKvX@3lK`kvUU6XxS z_)jHUyf55tf+XA41g_;7!FCJH)9aEC-TbYvI=lJpSMeAJHys7b`r}*diwPxfL(TaC zzB$Efvp$HUF%XEeSz1+z9BOCeIV%Lbb2W;^tS)RS$0sc{K5!a*i-<3qah-fAbFl7w zR#+iAP(qM%Xu12=oVI|Eluqb+;1|maaGyKwf0+B@2*zp2 zgHCm5fUTw~O~Z7r{)nm^KW3O70byZbn`KoMl|-4TjH)=d{|+MXnI1E2pi0)i(>aaT zaq*j)7Y$yw6T8EjGVokB<)IS&aHqD*-|n`OLRCHA=ZQDwN*6CFYy6%gl<9Y(p@m7@ zv5Qr#VA0>T|I39DoC(m&VHw}d!tjdU@444xXHFL%ZXVA5`1{s3Rk-eG0H!zp0~2Hb zztrVhZ}7#d&5=MnX?ZM5D41KIOpiYx|SEba(FNbveB;aCEhCx_QEhAlcY)&22+8z^HZQFT$pS#suQ95{lN z$LVux7~oVQ{`bVNuQhLBu*A1UoZqc>rEu5UjP^-)&x~On4eDQ7np4x`Z(APQhas-k zTWwqR7P@Ve2b?Z^I=lC9tDWt=fjrfMSdmJ?2;o@7aqeCkw<%&xxaDD!$I8MoCwfkJ zrr8X)<#@&#t-J`3G|U{e7SlXR4V_e2g5LA$3((-Xwia2RBoQpBqeq`@tgb5Q=sd-z z6`p#@DG9R|^OyVEmz`gGGuCcujJLH_o4o|&w@lqak_F{FW=VY`z zn!r89l(9zGgK4NFyDMMIam=D-Bl=Yl1X>IIQsW^ zRWLnJm`Y%K%$Juo5pdhRy|L4!XS`xQ9~+GAd<%(wcvCO_viN93_i_84F~&jbNL;Fk zUNKX*kN<~-XJLE^r{vS^A_@<2*Jv-)HA-9FC^ylnx!pzo({DQa#Irf8<+gQFe|W44+4H5UP*-AR&UaQYJe} zU3bWs&<$bdNH4RS7BLDvHJ0@KYl1gcIeJ~T;~(P{{W_ZCIMPjRHdTF=m+E!)c-hPk zV(npy7;)#;bD5`ED%VQNy5{aL_ZHjQdP*v;Z_547B4)G~t^-)r66dY6R@3#OpoJ70 z2ABrEFGXD4>jBFYqUx}XJ#tW`QLKi9Vz<%e0Jt$W>AAAiA49i zPyF&t%9W$kC3BG9^ziH~i|*l4Xz;9udqJ%&`i|+)>~;uS64_UarS^MLM?LKook{E1 zx~r`nrUStOg>&(%wsuWpKWi_MBm9JLYxu&K!Ncl{HLy0?Tn* zIg!I>w%gRW55XR#B{|1lYDH)2MK|t&e0go^7p1M`uoKUsMdj;m$2~#A%c1Sb;e(OJ z#t(lvIH}f?Qr@q}PXkf^5C2T7hTTIJqlurSVZio{oS2byQ+vkE^1Y*f;3A%>vnIbC;Fc1F$S7Uje_h&K;E&Sre-vCsQXHAP1^On5VdXro_1guN z_;(%+PYK0e{4w+q#t~YZ zB<64u?`Y0%?F{+<>i8PkSnc%bpl`IGi3C)0++9l@5$c5;_&#n^wtZlyJf!}xCiI_Y z?nc5(FJ`-9Tw3I;iqxD`el)!(*OXO=l4E~*d7qyw3mPc`p(=M)lP5U@ECuu?R#)RD zzUre~V|5JFdM?TP{WCL0om(-~{UA$1Lq~liM40|l{SQuis!Y@_PdeZrYeXF+AwZHeFxm8WS?)1jz-yQ?0$KlSy1h2YS1$&6)aZG2losHI+n=wz|&2-CNBj-HcK z5j)Vn`3egcIf)zLwOk$^Ax6<7wj%t=O#-5Ku=YMa;zgo{zM`4!dG1_)|4sv01v9`x zB_%~~V_!yS7ZzG0u5SoEMS}({>n^iiErdM~`M*381}hgL8e)zQYENDr9c!4i1}{QCJ}4v5S}{a1L`hDZ(pVd_Vl~3QBwy2l z{dN01qn6@y_M^O%461Zbx@zXi1*L;nw~b%gD?hw``~8rRx2V4Gs?KEi;l|`;m`FfH z(v4ez?AAl6G>9^6tj`J@5uTfqW$ePl6dfB1)vY7iMqv<1BoGSFa65>nZyX&enPV%w zpinQGigX;hRxP?_nzPorR~Wm3J*!@*1Luuz(}kXy>?STQ(Qf#gWFwAAMzhtwk|F}Sy>a|gC{XVp7sk~J5@TKrJ0FNw(t+q zC@UD!nxO+1_8XnbzdP72t_|1M6~)(05|A-ke7EQCp3<@KmCfyzKsO70Wwy#<*CxaM zYp*_E;%+^?CJ&6h{uHPnT`L#s^rS9Lr|bQjUZwt=!LDueJ?0j?k3n@w+kZXT3Rb$Q zJso%q1ASOuu-P?~zil!rjtC`Vy=o{fbF27*8p0lEvBne8uPQtDKB?D?-@+n`T{<7U z%SgLQkZqj9>IZjObJBCmQRyC;^yYwLz@w9DS%XRHz1#GO>X<9#0CQ7T)QLY=7tfMI zF?Rmm*qHl)jC)vY6_!>(;bz>d>Y1*(4PoN3E7TRKK7B1MxFMr`%N!z1Kn^c1gVz_A zz8;Qf|m1J@jlX=DVr7z-b!J!{Er&?oAocGcqw-C`Z7w`J3TKG>b9l7X7!*tZ^9 z4n&B(9Y6dN{Vt9i34#jp|uPSaVH*?L{1<(X5Gebpcp@Rk~bKNnqW zw6t$HxIU8-1Lk}8$zx^Ee)yP-4Jss*=66K*@MG-v+1*&~OLwD*OoMXhT_CjHxQ(Bt zEkk0GZKw5h_7dy5v7$WQYqU6av4dRH!iPT?w-jL~)n zwTXctdjanfb^ex+N&7r)_ZHoXr2DbxG@FU9=#;+8j(h4^V&|zV*f@q{E3X zQ`zg+FR8ZB3@60kA>ymXp|UFuE+>NLu}UtO=ssHt%zZuvy=%%xyQz5bqgM8FlhB-I z^=SAwSc~i!JB~vnyU5LqESyN<+3VUD#cVKwO2gh2LKXCpzHMLMLVOJHtvr$`7$r*wym?nXcwq*1y- z=^EWgGhlR&ZZ5vR>$>mP|LGoXpR;q$yADaO<^!3Ksf}4QL)3nNFS%#TE5B#-W4YMs zFTa!$%mFIQq26i=BoBY9d_x-%A8Ou+awM-sSFPAzhYq3t>FxLoe6}3ax+KX=m!cGQ zBrH3>D0`Jc8dx=<7E*eJ{aBhB)nx1GZgc@If zz%?>Z)8W`8^u5|&BG@g|-`C-V&&Uu($Z7RKX5%A8GzdF+o>w@1lNM?(nQL`v>hLBaE3^2Prt-DC<(it}EYdL+Pfi8_xAx=Pp`vQY zX^XXzKR1#Q+TZoLH56V4YXa9b@5Xfl85EgRluMzA^=8)?9MY(jxn{#<*R>pphhOeS zh*3|y#om*Io+p+2Bs_@1rxP^+Fr;sj2JNc&m)XrHkU;)xAA=83iy9j0!zTv^wsm?n zjQMNue`nTasOXpCwQ`>%e1^bG>*avd$jq~F z(@?b<0DzXq+gsjU*~tmlgzYowxHyse$>+-RPd3yte5%Pv4eZJAB&v8134pk6WvMw+ zO{GFaU?lq+#orB;g-RC436XNG)jVe^*ZhAFq*|kWUN<_G+1-f+iH*Cb83hd&RRsz0Qgfdr!??(RGe^9YIPoVobMGGfE#0p(;1nG(vdwFplmY?& z`*2lOF)|&}!!})_sjEx=0@SO^%VT=KGho%G$d6%W&968qyUBT__dwv&W=DwXU)Xs2 zogEb!{7r=rwMLInC?q^wv&!h<<4gsV;M?c>(AFVE30my79Ocv@+qAORq532E;%0RdN?j`o87Sf75GQ$1+6a=%l}z-CYv{UQ$wKhcqDH`b8Ya#goH?s2RyKb`KJDS zp{)N(k(Aro6Pq)KC@2FMtE)5O$h0RXgV;qsI;G|v=@Vi)fs!RYM|>+u|Fm9A8J|IB zqO}?vo;u!Iu;i?Zk$sytq|`fI0e8Qbd}%2!VGLf4a(O-Ya-%XHsS0hm)1~PL5<&*=^MB{YS5fbgujzB-+L4h%P zhUzxwQR?8zCbPq-3_nVlc(Myi z(oz*FjC9zQD<@Lx3cuV6{scN8T5eh+f}YO^o{jw+_Q%w$c6P5+vpT-Cn0h>@`TYl2 z_PIwnb}ol`^9JqI!9|T#p$)7*$2>DiOKv4bU!#+K!g=oQevQ|>}|81>NI4%DXRKU z^&|a#V7OcO@S5J>M*XL;qycYuivy;W4n3;N2OF=Hu+#KYrhPJwKK)}&!!nnwQN(G* zFFRXo?R6Bu{&9mwu^#%7`KdG2Ts$~`VjTV(h9&XbM_vtMt6Xo1f#Ba8)#nbk(||Vc zm*OBuAmLmHL2s*FrC8!UZYKBdAbVtOS(m?|KrR52*#dV@lejtFzm;gJn&s%_q8?B3 zrkV;On*oemG}<^`4PTn3aMCATcLH7_4&bD4Bi50H%kycbIM1<=#ssVHLM!bMowbc= z3<46I{h6)wb#cte$U(%*HtKF%RQUAh(=V4u{lBV$X9tbhx$k5LRo?yf?)c@C>M2NorM`!&{&Cs=DMAGW{4BCl_DQ7AT`K;SbaY2?QtHz?fz1-i`G zlPN<{GMKg{!!OrpHI#v-?38G)BHx)IV@%OuW~RKO(g+Md*S1Zb!FI;uU=#PP zr>ZVFDX7N{Ob6)KQH>pBumgl1SJWd6uWEa4CX?Sy2aIAqrKqi zo>@6bs^jBU;^G2P5oK;CLnpuTjUBW&?@KV;f_LgmqtNM%P4N4cv0#LK?QgNpE%pDN zf_1z=y$|FX#SaJ~bOgYEv-8?`i^5Q;%hg6%f#xdmdfBBMbPPRhvxfjzp$M6ra5F7qS_GCCDw)55$#JDk@wtHzI!7OUzR6`9 zE9~8nv0*rCIH;@Rrj}sIzbvTLD`z29Q}x@MZ*KEt$Tm5qT-{SD|0go3MvP*;C zujWGdrI(W|E=|iPJcRDq=dl9GcO;BWVtr_4qF}8{G7@hFetGR2>ty7X7+=ZP;J#o6 z|F589ZJGp)BSQ~WqFY5onkBbYQIyW1QE*C~Ekc!?m~Q!|WX>@g~vcc zqtJbSShT}_2;5?E#92|0J$ydwqH`(KN=MzoJPcK(7Iqb6DSTRh^EIK?+ zH#})Y00uKH747II$_5Q)j8VH}2nk`;clf6y7aL*C})X-O22&QX%o z2Hlx0{F_d>%y)?Cq6f5!VYdX6hu_qBy-xb7pQUJBojWn#ZRV2uF_ZKyKYi4W4>xxH z0-ZdWHb3SD=G1@(g^YYm;`5QBgpaC3^XA!k zbaPlsYRUhipE~2H5)2!$LU23O%v+|QYo_*sF$`&v#=Y~Vty8C7Zp0Cv>d2u08nu~d zUhv1C5tS@88wk}sVnQ^l)-&uQzjKwvq8A^)y4(0&F88Ewn{;GKt}lLx z`PAJDq$a1EUD6Y8sC|0Ey;MmavU?RI69Rv~^0n*ax)y}|u#)@B+psn&AOmn?zs3Tt z>*{E`;7^R>0}u!%p2>Z^2dmSH#l74eBjYqw2K!wKzS_LcVZVA4+V4oYc&9@OK$9z9 zQ9Wninh2w8>hoUEl#w9Vu8kX$^Bfz6k`dq`o9aYIeAtTaE6)1cn(-1hF%@=Un!}>S ztIQJOeeR0`TOU7n=6OKV&Va8#fG0%_z@IVyFp|gQH{cvp{@RyffxnS3!6>xr;{iX> zVT=#OfGu`u{ftu&B!4bv(HHHUR|)l#mrVK=%^nibQO)fl7j1NQMB|Kravnz-`JXkN8@aQ~ZPaIbx)hB$74-38~ zbA?sHY)P+as!7daeA*n?;4dj4^$l{%p4AZFj#rdTzdUSSBq*A=x+z!hz`qx@kbf>H zLU-C=|Nl}l9w$O8(_gZd(#Epirri0^Lh2UbI3uNGix${|`5k$xyLkZhiHbO3+LLnE z>y5*rEo#lFj^i?l*Rzv^An^}9?41jFpil4nF=51dkut*&si`v=2hP_FnU&Pj0YEQN ze0IqI%AgcxhWU{|n%X~gcFA{a>4iRT!8aTiS z>T>S^U0^|48|mS`{;UXMXe zErO6=Q2Bb1f;1ssk(IPcyC(Bw9tC#7-oAJR3&0X#pj@7}!G!83cj6?TqVZkIJCw89 z(1V3{`>Y=i*vJrn(-XY;5g}flfmm9K(J3Q_s0$$xgPUxG@h)|-SXpGFh1ea_5;Pdx z38L=H9@uAv48!cJ`LQ2_hkMCS%rXN)YX7cqVIO;+tI%VLN?L_bmrzaI*Cnj-^XJ11$xN#FZyLWcN_XvveHdh+53Qna z|8cW;Vw1ntIA`SPqneSE%QZF&B_;f;FaPlT^mOtqnKeyIOK!)@57c+QN1qe#d1~-C zcBh6jy>CO*AoZiXEqU4O(VrOx!uS@7^PJb?C2|WL+1*(&-t{#pT)bRx0@|Dd;_9i^ z^c`JL`#}2|Fc-RWKxzze~$Wc;y;Tw$~RqS|7UM(gwGzKUB zMsk*i0y_jXZ_jc4Zlx`PPYJBNp2F-C5Bb$2xWFhQSLRvWGIJ3MA?q+==|7@+vTL1-rKL#hy2X|N3O!hS=Ctw+F~I)+Nw8ij ztBx64Td3(J3>SSd#Ymy-rr1YfiaLWjSFLX2%-Z|oE6xt*@E=Sojm9D+Rm~dW97#B6 z5}kOrWC_UadDqXnp5>&2ob=FEk=`LChPPz+TXDql7V_HDSIi3$sQoNLM zH-0tV-*kZ8PNvt)FRZVbZrzLMAG$OY2ANaV4%aE4JEugW%(^}P{El2I&wJGU6_G-( zD-%H)LKm(4`vJSmnk5N88Lj0rBWAv&S3md^+QAZ|L+(iOtXxiQwfi_BD!? z<*2rGisj-n_++14pDA{!dZ)V297xd+DeG3RzRc!Q1Gi0EomEdos#)3N{@E={KQq%D zXweKm($F+$(KdyqmAu{yX)YFf5I~s(vU&0~a~ATlY2H+!(p%ui2?NE|P!CZnPkqjI6z*p~v=1 zskx684v{~?{3{muC7XmGH!R?02>hrS4?u$}vfF&oe#5zbyA=F5yBxA4a4&jae*?_u z^0U8d0N!oxbDpg=GhyAaA3p(T*Q<-Bin&amZAIPussR`F6_YOwWD2EZx}I zpOMtq5go#7jNQuf@n;Ps|KH0f*Ug*G4_|yo$*0oJ2ep9bb^k) z_FdE`)^gjty2}c;#=MlVYR3rU4%&e^p}XeX18~5s-|78LaKaI=Yp+uw8O~R8IG*49 z;Qa8p?;363^#Gh?V9B_=vFhVuC%K{Z*AiTJkX*agv~Q4FD<*m4Q$ z^}C@*?2WlPwH_d@zPh7vy05S9|1@X+?{@T}_5QfmMwZbhZEfibA};pL=nvV0b2RN1 z3=7)~ZBgBw62VOy=T+C6WM&paWdmOv+kyDudmb#rAH00`R1f~o^TS6}YZpkZ_mNoR&ddyS#9ghl zm}eT3^3dwDAO+b^W|WTlthJTt-RZj_&h6q3u-xHeEBzEv4(rz;g5|oK%>f^aR?V`& z$>+2t-`9sd4o9m<9?_kb(lnG0(hspT0Ns?mDq6j$Z;n(PjFK}VXTj03` z?9>I%j&5Si2qdwLBQm`!4MtrzqRL(LQZ!X5y((e-JKOkNFTJOY6UgGwqB(kdJSBU@ zM(Maxba`lmw23C6ZNX*%%HH9XWRrub67&zL%fJToQ85w)S+^ z!x~lNDii_gbh3;a=K?=)#ar@`v5;0*Sr`R?H|dT|Na<5Wkeu<`_Z=AR;h;6ORy2bv zO)mbEea?(B)71ujs#NYNIzKLzPfu(5eeM;|+)rL3E%iBZr#CmJ%LLDVL>qKBiSyaa z@(h7nH%u`_rz*cyY!NI(G^Am)f#97QyiHzA^ZTIr>kuBvmS=Iy_zxS2(5AvL;DXN4 z24Z;#1aP+t@bCUA_b?dx4IP-+*^YNf4d?yfeFDmMa46Aijoqxlt8y$-ujI!xrJwT# z_+|S4%|0|*uQR4R)(o+Z26t2sfvP_KCkdqcKf> z%v*pXFcLk4k6CIdz5FQf25R0L?ReA1ellqYS{*I?DyKkgZxz%Ksj%R~Y-pPvV~>Pj zFWsU~F@sV(#MrdV;J+t4 zk!HxqlJ%l(@_?|EUqc>v+IHdrBV0ul%JLpgU{7c2Z_V~<%#e27zD`t@?+Sw^jo&bm zaFTC1m)r(&4Y#b7S}xVXB(hfo;?IdRf&}DudYXI z3nK6DOYbLVR85rMIaB?c7&-#|Jna_}?Ue>x*$s2Pz6wg?R6}=eEeN=7xSA+w$g+aG zjJ`wK17vsHNRxum&>)5RKQX2fKYJC)zd)3}J51;I`c8t^bR803d>R3S8a4v#qS^gf z}{%*82IbTECjBqm^7LrOhuUxX(xN%|5pBKkV}xkDdlDsIAXuo>Q6Ax+OrN z@sBCoCnr|0w(z6H{>zG;_A1N2+SS~jhu~3Q!q*b!FRu>XNE1(W?nPbU$YkVT#d6<; zN5wK!ePS3%n(noNY)J6&BZu9BM{ZrjN-wqr8gsNv&!;2A9q^_C~CX_E0t6d z&6F44eB)zWL~e?KbnaQ~9di3CjBWn)be#O;kJ9`}INtiKQ>m+eIQI3m)TKDmZrnFnd%cj@AsLne}ay{1BQB3p}`6#n#?vivQt1}RCi z=^M?)tR|%!?I}`avZE!OnpZfLRXunE7w0Bqb7HRt&ke=A1skW27s(d&yyO3o&N{DH zGOT{emj@y!^Pb|NEgOHXyR?%%RowTyoJtmC?;`7|nC-MFH?-xHO-64OTKp?rnU z*L6{5PlgI8vjj>!N&E7n@q88=dEfhfz^o-#;f;3Z;d(oDDhHZa2qM+hhA0M3IRY!Ni39{T?Llqyqwu z-M;JhzMaNc4Yyu?LZTSB#NQA_cepBiqP+gtf%mNx3_o)JW<6oaP0?*3=X7t@GO`&g zw+rv-5}3T^iA7u#n24$Wi$=FwW*KVhPz}E10G#dA6H9S1grmt?;Qrsb#-6~YGCLkHT^U8~?Q{UK@TXH}^C)@B zu_r6s`y7JP63V6zi97essSZ=6=l{-vRhgst9$V}>Ub65QlmFEFggLKj@TLn~nZ{My zSo-jCwtkVZWv8&gxgQ)oY?`>%vcRj)Z;t!H|AX}3)P~sXX4jKF*}X^%+mK)9lO-;U zHs ztUD2sjh@swtZRJV=AkCE@hRM!Fq$gG#C&6n9;;l4Y{&EC2`BAX!vArXRPRIw^{G_U zM>sy{@ZElfG^PLGr`Of{sHRlSmtP+U&$L!?=dak{+(=FNH;v6qX!|?kjY>U*zN*o% z?s(x>L1*?5X&@Nu-%!-I`kcYJ-$Sx`2A`D9Z;Jgb zE#Ii@JWSK274i-aaqakgwWfxR^f1p{6T$}KtxN?d%D`zMF(C<1X>;5pDnM~0ogJ2E zbB<{ut-bfA&h$eo`ztE$<}aL!nL{Dwz@#J%Wz(?S;lg!ZZ1o*oW`m?Ob>9&oHH%D; z$r-Aoz-tb^G-*!uQbMy=f3-0MrlQVd^*$OX<+`lwLd}`|iPO@Qjgf^Do9I4P)0zde zLwpXnmRxn_G2X5DNVNZOT^YvyZG(0VL5s(0RBBlj1Rf*Bz2<+D(PzGEMx&4UOZO7t zY|yT|#}~Pj86?*wVlNcmA4Y)c)@8Q1f5MPxoCN-Ey@QGNPPLPg7MKDj>9i z+ngfK1$YjrE(zj_{p7~ga|1MImab9Tj;JLZPr#~<-ZTZ^6RaZe5q>e)jv!yS32ll} zKVGk9i}f+5$q{)8WiMV#YPX^-@1hB1eFpmc_L2ao6f8MD**QVJ|C(Ip>c-uPb)HS)*V1M)UFu>^!5d&9@n`XZgA@MmX82(kFk{(gdR45wp;L_ z1q*U9t;tf13q}z~MRpW^^qFCZEN~12sgZHMDW>b|Uy>^+V5zv4yNPEQGna> zXG0m43gcT#V(!~?^gu=UR@W_kR8dS8_XFo5ZOSs2fu5wW8o z7_~$`b5=!*ZSl(t<2Rw8N|Fe^mGuL>1ktw=nCBx&PQ*J--Dk^__5P%VgYqx;MWv zH?<@TPS`O!Yo-I0*Y60yFU)`z|EPsa2po~+KcmGQspBwZ%^V0%w=>2CS=7E#9&J-f z()fYU&=!Ftf9Y3m91xD3o*Y+mn2Yc=o4+zq&Ozr5eDgC-(@5gucT~1LVxJYoLh^Zk z;X~1+6lva^-C=cRkQtp_aSH2Y-6*XqEi><%nSI)U4y=A^i@UFHJUzD#{%E}V82_@je7Jn44c0)HbtMC9$xyMjdCZw{N0|; z1q=(_40lCeSmnB2Se;m}%1-NJnC)^uz20?{xN499Wm=LQ);DRrgV`r)qX2X2S za$^9Zf$p3N?vGl)uv(=ZY1>#At2Y?hD%MM*I)@Fmf3t#bKH|r&+d4@pjNeQ|NWVb# z1xhDV(mAP6%jBs}oX@^vD{C!ebCSR|Y7#kroK{(Xv8V&dK9jX5?8~%i7=By{C@t69 zfd)PAR22{M>EFPd5gT4~hf45*4t{qQ?XW(m1OtH(&(Xo?HUH>;_4(2JsqYCXPHHu8 z&H-`R>&FsWWiqmNcpzH~dz08Xx8NQy{TL<8lppqowzi&=dLt0v;e_1pRPcV4DLkZ#hKcJq-FtBoaK|O-LNszwjyKD`N!iOmp{dB&49CZBQ zO8+G6_a%DSB|0<`(%aSpx~6t0@GM2HpACnY8!d}v4R6#O7N-OO#{AARVH3CKJDkpJ zY-|_ZINa*Cwq@`C`6pgc!fwK12d?^F8%-p$X};ULtGZyYf4K!s0st6m{`(B+Un}=h zxd=YGk*ux(wXrsPpIe@to>u>&^dN_uCrJFK;QXJp*tb!pOYBQFP&aJ7%*hRb;i-4H$ zLXA#zC_+OvD=^$=f0w=RW`T_VZr7zyTuD4%|GYr6$P^QZ(5Tl zmA;V3w0wjpAY&KwE(>!PLOgg4&4@W!h`Vt)GGNA!SNV6^>E(xPS%_K@_7W^wnQZg0 z4U^glck{EIWrN7i1$i>YOA>vLamU{ktiOECa91BMOSafG z*RRs}!Q*x7zOlIQW|OQqpF3AE$gQXUiQR8;*sg#BSZ)R!Rh7%@`xs+_3<|5NJxouG*bLzcTU%y6MlLrkTqUM*j>y{p&9USDez<5J8;9N2vq55 zr|=<`!?BRySZLC#9UV&YZ@SdCRjuL$2l}sr%cf`}yp0rty&Vo;Hnvr`NTsLO_q4L- zFcI*3)mIOc_kUIi6=rrmVH**@nTbCqFa`?(cDz2m!c*DuPOjoNa5NjHuqH9;XjZ*P z02g}5>F72@jbpY%T&Z&J%|sjw7Cy9-Wr$R2%rCsm zuDR1}7x?1`-Ia`hOY-nfRE>8FO$+1g(O~D(aC~WnV@5lw#BVzF{83e3Mlew52fmDL z0flA!(WQJhZQX(Q5tE0mVYTrTz2~<`H-pja@1FxWTJl$)YOoVPr;D69@z~b7UnA3u z`a5-;jrw&OSRtDSe##QmvD>yjGZpL>#|j*T=lyg!-*}#Jn=|Adn^&(FSUL!vdqCum zG$S6}hRM`ram4$SkP^KSPkGAkpIGluaJO&2qTbV85W5aww)=MiBb=rGGV2!dFYzHFtqnD43WI zQ*vjq9RQXz1EhlLt<1woWilk^x-sG10a(1_!@X%k=iVoYG1Nd)zR*AS{IA|*=)FTF zPGDM|z^UyPeh%Jnj6Mra2jD8fDW78awzg@_QQofW<UUPNud zq#pn*LGbiVXf$UWOe9DC#Dxaa<0ji_RVt$MR``cMhJjHtD9q{y#~fxZctdm@_Il^% z4d?A*k4S*+8*^XdEXl&Y24oE$Q7Hp|fCQbmIDpUt2_WA+A7>W1LE&@qDZ>RX@&K%# z;GOJG;7`MIcP4mEgGxBIB=!)-%Ce86^$e`mkF;2sLyZsJ(R@*HO>X6{4IWC$?Do(K z>NY?@c&<-QIsWTXPAkSB$cbAa6CU{xU4RE(}U(l!~wUOCyW@$cZGt!^2bbm*y8?H|Gk9G@SNSbSit zyr=Be>6s(gYVU5W*@_|vh$lN&(@qzCUM53k2mxV6byuA>iwlr6@~k&U+nVptBAB&`Xs}x2?cycr%S%hK!K=7fKcF6v-PxKIX1A`&csLMlOi^t|PCSVxb&AN)DP(2k!NFMHMscWZ0wBHHMoo4IP$P^a-g zM#9Xn?vKU)ZHrejT@M-Pp@q@2m~*|c0@_*uGq^Dw73DzFDP*DvZlUni__0r@ub4F< z;zHv244NUkft22ul+A4Z!jfpjb)XVWv<0tc$scq?bOb#$UsWR_1*6CZLNFxL0Zh05 zl}iwVwq1ZhfV2tID@-E!NWee336pP9gbyS&YV?@8p=guUxqmiZl@U!`Au)3!MKDM! zln06XMI_`?5sL!S+Ig{Uq+vWXWDI5`uHwbj`EmD{XUJ!m-{>a-*06ZP73lf_A=sw$ zccP#4*Henh_q%!T7(#MF#PYcT)Dm~7>n+I~5%2nm@<+w0_nMIb#`LVeq{*&uklhss zQ3PEuYjN^%zhe{bXHTG7G!*V8DP~=m#u-j%)QGx6V$H{cG7+z>>Ue67^G;tB?N8+s(?p^TDnV$FV8joMbqVLcq{8f8Ec1`s? zR}mFqj4U#X)|bm(>@TJ`#1{zY(X3Ne98*9)cBdS~KdI3%q;5oW`Qp}@(I)&_hQC$N z!}o&k{(qq)AKZ%8R=zfcQt>Btv3`%aa7h@SW>4Eapfq8AOCCz0hG80vvpz6}&#h0` zuj=LecH4^rm7bdzQOIZ-r-93N;TV!`47cENioI}o7xm`o&0gm*p=Ea(%DPgW#W(Y0 zDGk7T7tY95)2fB_q{~d1yO^yq<>^QFdcQ|M+8<(`hO<5x2%>fK`P@~$!gXyXUBG((9fiOs*$oBX)cxpHwG#1-d1I;n&2jMd-n6CO zoplaq_)5=>XRNHCP7vjy0 z@p#4e{HU_?o*%YhVGSGZ%CZ>{^R4QT(opAs`wJ$vazm4@nMn7a&Q|WFzm$CBJDh&Q zFKkD6#JSW06@VCB$e8q0hYnZlrMj=d6Y(ELG5IG~2Xk5aTWkv#a!Hv%7)!)#=_Dr7 zF3f$F%k}4%Jnyblgf$Kx-?!WVsdS9Y~l@oxoIgv7qmf7G+@B z{d3w2L;9a`fa}}ep-e>@x~3VmvqxgYl+^$BY`BWk2x&AWhFF#VNG!&f509y0aJ0*M ziM&xl*;{hvTRb)pP5mKbMnFZH*6@`&HeE2ae=I|ewo2D_A-BbkKlT!&$e&t82JP&< zjpn>J+Zkz^F^QZu85wEG2Trw(8JW0bO0B+z+tieBAANx2a9OO>|7fC{csurXWeIR{ zWDHXYNPv|$E9rdX?-i;mjHy-Ru-20P6n@~dy>_&rOS@xyD3n#>@5{~1nbdy>mMnPl zN{86S-;V02gS^B}D<=2+mpnmq+lURqOg>fOG!HB7F8+Yp)2oGiM;# zZ)Zv=2Cu(H#@lzC22Yq3%MEkiE5#LH6n=U$V3$ek#cjBp>zu>dIeU)k+v1Nmm*%`I zHbOC4O_t2_R{Bp7Hn+U0fSmG?4?A8ty+Zp#S~aobqdT>7%EL?j)V#g4&Sh$q z8($VyCVj{HXD7)Sj(4xRdW9Lv60ie_T zZzF%a2w=So14O7@#kRfJuszsGmMa>U#j6%P1GX!c-c~d5jMR1rpOw>H6$l?z_m1kd zjn#s`jru52pZ{~L?bYvers+y#cwE~;70t|GLzM@V;K@natmGMI{B^g&2&EF1#?p}c zdH%3^wK1MDBfzYxmF!az;WEU*mZ~u0(IK_MT8rwD4%qq#IQPR%lnei0#XX;`v@>02 z6Ql-EImIjXwiQa?e*Onat(?vuUE;jw@Hi&jtJ5yuZazTPOf6TxCbr%v!I8N`h0zgu zs*l3IDJX!Q)_-TvjNH!+hzY6oaxeL-XX&T%J4V%F2jSh{nVHel)zc-NeFf#xoqPC} zd(&h0pQdGv50NgV)!Rj>4(Gck@={w**KiUa-R(F~cZuEyA8j!I5mfEM?-!UK1))xE zmqs)1sFTH7iS>WOm?DXn0lp9qh~7^W!nRzYUpnZoSjLT5&roHuiJ{`XTed@fAK9~2 zn(3$HI1Rok%$eOykzNFPCJgZVY*UE>zNutozUC(lTnMMPJH;v_Ag`8LK>xf^^C&+5 zi{=}~Z^T!_VW9ycuwgFjBWR*n6+%r5*_W^ZVlb`zQF$X%RKE%}vg=9WU+^olziWi* z9||2;RCd?uzaQ6DgZZnrq=O_0QgUl}JMZF{DEI78j;_MUTEg^=0rpg{(wQeFROz z@8WyPMtnq(VLJm22XUW4-^AEC!Ho07b~wRJ#>CFO9w4YD(CukGfgM9OF!DdcR<>k{rl1~sBTp}SA12CN>UAP^7PU} zU{%EmG43shhs}0{;F#vcCeBN6G(RbZMn7}8KdXdk9|wAyU-7_CjFvsSBhTY@nMe5I z4u51EKbtgp;}gP~*4TR&@p%TQQ21W}oR26^s_nVYKlYcm&PSp>s~Zw?nX^iU*goB} zt|7>3_W4p6zf?cWFmniUU1gZ@%KmHD(sKRxyER+MrqSN700D`4ksoQ6gF6u3 zIM7Pt5}pSAd}t?JI_9)y96TwJJ}lzx#>=5YCv?vIWn`pidT`GHi~2ZHM0Mz{8Q{8H zOF!S}55h;~%KeFtZTj#1xb-&)79qw=&;<@DW}H%ff-UYU`l)vSXSMNx>fHo5*|N$& zEMh+Tum?EgpoSMa&@-g~Qhx7>MK20U*kpasGO`@MGZnwZGPw9xsbV=KN&AqZ|w4VJrO zbi1i1{+BgZ>!~h4#>g#LuR85YrZ39X{-M03{>1K`H{vOrCffR}%NRQjq~-_oM*^TB zmdwEH^y$-5)`$69Kiobr`@AExN6N>|mFow zrtKAHNBr6vm^sMBydW2T>mKERKQV5mIkL?KJ{K8r@Dmz4>38-}cWjCXx|{V}ENBHR zTyX5wLw!eAQLKICem-d!%W?eg*^|$l?V}BhMFaupjeHR@nwcZo97A+HFXsKgQiL`# z+kZ3x!WYT)n+|r65uZkN>F<6QI8|brqQu;bcC!jreMoD;7fQK{rSG91G1*>vP8PHY zd5YR%zMp7#TNYtP=t^Y0P{Y5${OGidN9ZZQF54#}G4i4DAV7FFCa{Z^Caj zzkWDC>^pCtIGMUEvGWv86kAh%)R~um)3b?dkcq7FgDBf}S|Y#yJ#X4Z)j_UN#N*9P zW;CDX@>|Vf^T_}d-qgb}rtpWU?12X{)gfbr0PGKG4#1AJ%0HD~pNBbzLc)fNR) zHezrpMNt&qa~mz~y*W80NH^Z4g!o@2xG%7){-vLa&xs{mvia)G3HyWr)x=A)91t4? zSZR-y<8dA<`v_%6#N{=|DA#--Bvm1_DzF|-$0@#iA7n2ERXIAg_K&?(ah-xfWBKc_ zma=!MGIFG7F+#AEpWI$?F4o49o!|0rCw*@1_;RTBxmvyu18M-+A#vIm5_~#JdED(a zPXzfPu$BJ&Kvy=b?wvv(8&8X&;^n5^K9_&FwtW$g634bAwJ4;VTC6ljcjip%?yNcM zqKOh?p)zzZJZdB#-E;`ht+mHE67vZ9;qYD*Q&QneG z%Ys#JDq=kr8hN!iTn8G#8(F-K-UB?vxOG2s!$E21zW{sq2*>-qMLf4t)2y3V;p1z< z9pix(-QnepHzZ%v!~Y#3NVa&5Z1yOesC8P z^T|Qiojvmr5sTsZ&P}J^(W1+wt|OtvaMh`-q;i|%gqj{|Mx*Hw`5lvk_HplfEyL88 zY?qM&!WbvLCRi$&RIiC0LacYm-jvpy(tEo?udMjwXDmj!_uS!S?Mz-DTX)2t4)08+ zM{8d`75#QSyTHy;SJP+#ruaAADpx&tPRTHk-;X5U$@G?F|E7$MDSmH#DdAyuwx$Oz z`}jZR)=R5DKC9jX)_>ZC5Z@mixc=*Zm+9>6Np%S)G$GgjrhnDnzmP4J%I7%6BG-*p z%%wNvs?IP;PB_I>^r64n@TvDN(x&B4UObIc7IVTRIrVv57HG(eMqCLvjk097hqA znkWrs9NP{t4q*#pBgPCvLyR#onD0fZ@A|H5|5-oYwbt`IYpwfvuj_r*{oWI>2##4= zqFZ@|Hv3h_#r&ST$Gsk)GwG9GJxz!YgCS%8>;!J+>Eqg1?C73{Ejam%TW-3Unq2Kj zXj(xWCL*Inv8|bwm>bN=KVA*|JiPDI461y;RTLohxUQmUX8mF3ok9;)o%c{Nq0W|F zg+d7q6;6GLZpra51uU9)eOIJ){%M7-x#MPb;N~ z1RlziuW(coK{mBL!NHTXbWS7(2t{?)ev$P5Qg+8%$TThD0N(4=Z9JK*)+zJg1m2(l zmM5%hCVrtaQvP?Q49V}7s88+38~RLE;@R20C&GRC!r6Mx@Vj22-7#1Dm*Dg47R|k~ zoOH}}J||rMZ~F6-i!&l0<1LFSvp8{lI1@LAUR zQpU<`EOX8!UMy-k9fuL#a+N{|mnUOty0`j>L3&we$yX>r(;@2+5>K&fPJSi&zV_zC zi~>#OHfY)IaLm~LH6o_e_URaWoRh1p>{3hQI%TfI7i#+Pxoce6j)BV_$Co>u+6 zPh#M3QeIv7K0iapGqAV?u2YQSAQ7f84I9Ub*!XtJ>}TARNEbo4MOdV|Bk$t+O?=U( z>*Th>lnwh}njO)@p)=_N<@fG7oZ9=S01Y2Md`TkU4v$@2Q*?YvmIG1O4Un21&OtcK zL^q=5XWB>!o|kHzKMmL93n3auO5#DgN zvZN5LgZ)!-cPMF=dlgmCRW*mx+ZXCSZsu!=+k+>v6oNXn6-bX%uH)`Ii>p?rTNW?m zy)}@#JYhB2Jz~h7B$fUdYlGR|&4U+?-Gi|6&N|F-Z;WGf9%%NyP*4%4_&}dc#`^q$ z=;Ix(({4L`#6voeve>=CV%vtu(#x_-R*(O`aPXAmNSmJoSy`~z%s-#|x&(_vZW zSMmo3K^sxj)UQ2Uti@rzhmJ1j`Alzt2lyF18@;=KcjS*2ALAgsGmo!q2NQ3n9)ZIC zr~~ur<@^jsQTs9i=->ca@dv5=e(lenc*GaAdt4^NY=t=+5N;b$;eMW*tpZnF~YJayBt+_YfsSs6|sAHkY z^ivVoruYZ)HI`Mk!W~NW(0^GEWyVJr>Sb}M10*@Fdq#?eZ+HHi6K}l7yp9`> z?V=c+MPZJUt@xBs!T+?k`Qubn!)tXVu#_}cYxazD#d{Wlg zwgh?ZkoCZ#^Z#w*pU#rF{K3a~y&HPHf%n{6=pxyO5t#ibPpj4-Wf?Eld8Ka{Q9c!s zTSfK=Hf`wYYAsf$M)MdlTLv|e11ldk?Uf-BXVdB@Q4a)3vRr-Nj`;(^T=MIFdstA5 z#`tYEv4NE&OXs|=pXR-<#m;?jl@%2~nWAL6O~z%s0~{$O^m8fxbI0SH-vH)0Ahf&M z094^0Y_&DgDsY_*OkCrz{lLRI-Q|1ND)D7)?sqwMz{UEk^{$`lqiY`43KY+_1AzbQFly|yl;` zRHHXyTMb|X;oqdlzq?;mgtq=7z)la^kN!?SAvV3TiDncZIkKzd?~CvwHrqFudxqPZ z>$Mt2?Ao=z*6iG2Pq12y@$SoAWs&iZ(Iq>4(Vipm((zpFJ2$8unr+yxtW(h9l>$Ppa+myQy)N$uNO;dAE6 zN*FH>+f%tYO6JD^8{g)|g@ZtjQuj8VStTnq9r^_XGJyl*0qcOPgJ@fF2z$V7riSPi z5>nNNn`~{pKp;pjtUO6foUh)K1?9hc&l4;S`*ZB32UmF6eS9+xpI4y0q%ZKFiD!Pb z2woDTeDjkh=$|ddFE@7em{fP)uTL`Vt)5+)Dh`{f(h?!R3PZ2h$3Y>k5KalH)1FM^m=Yt8^_OvgO;TgK8&X~YKAa#*5oe$Ww z$es-0seavno$3%#uj-HyM&+_#5I>f{OvMg?Wks5Yi?z*2}C1-zk?*0B0>YSPKI*axX4IroJcVY^YDnJ5{d5o zAFP-6Ge7pRksIv-ZZLhU9xp}ZSs_hSWrQ|?YJk(ZJYN;QfI6UYPruUr(E^uJ8vPe4 z&T~NtWl)|PU0&|bn#n6xtA-#mEDtvDk5H>i<-}B$2mhl`Ppf{W?{B=Z52W)7nYvo@ zMM-5^C!$KFJ4gJHRgFVwjAdt)tsGWu8Ftm4X??(rC3Z8^dHNt>Hp73x8+H&!BiuJ_ zSTf+)jiZ#m8v~R^`)7_ifaSy`Vo+*`FZB|s(<*kdA{2;q+p8?5%OG$kGC3HvzzfKN zwTPSf3>j`Lr$7HBF?(W2)7#5?r}~hkx(P|T#i=G%5ke@Ts?0a@w>I=RXHsw3FOX?? z5M>*MC>hN-{2m4a3%N>d1%cwilVP5DCz^Qm(WVWMO$j|E4ua9;C&T9NV0HEAcjgR2 znrT`2h`~+9mXqXd6xdY+ScuaeYh47pcvb42wZxi*64mJ=z1gF|*I7*y>bsq3vF-XA zu&h)y$+ld_mBpaF0m)SHU&=eI)x~d`z*hqY0l?oSz9q&2PerK_$gw^C_90OAEj=3b zE+?jj$Oi?81}tzHmD@R$J3PdtG(?)$^B~mB%-5l|HiklTw59x`BaUI5N>%HL4;Ez> zEHYm)B*W}^zhr9lGT@qm7XYFsYS8O30#H1sG2Xqcn&xdsxuPTP!8mQwOpu{p={apa zd$?|iLJjljY=2&9nVYkk8#k@3l7HuM3q?a19^|MuzK)r5oNMAL&qR~o_AqV2?8PBX ze?7LgS_SzkqLSD1X|6Hi-!gY&*uSRt`kyye#=AE7&LlRht;g!b; zQ@{)2xwSXyEzmjpW+msYlKGp9d|l>HN_e5c003~U+)oS9*l5 zPJ$~$zk*r-1%Vz0Nq?@?SyERuPu7ZE=HMC%^gtlHY4sbZ;ZIyn<&l-At0w>c#G%3o zOknsZc4@vdFm39k5eNiQ^*6Tjx52vk2cmpkK~6Y`lP4DI;(~)X`{KO)u@E!{/ivi-shell/weston.ini``. diff --git a/doc/sphinx/toc/meson.build b/doc/sphinx/toc/meson.build index b7190f78..4c638411 100644 --- a/doc/sphinx/toc/meson.build +++ b/doc/sphinx/toc/meson.build @@ -1,6 +1,7 @@ # you need to add here any files you add to the toc directory as well files = [ 'kiosk-shell.rst', + 'ivi-shell.rst', 'running-weston.rst', 'libweston.rst', 'test-suite.rst', @@ -11,4 +12,5 @@ foreach file : files configure_file(input: file, output: file, copy: true) endforeach +subdir('images') subdir('libweston') diff --git a/ivi-shell/README b/ivi-shell/README deleted file mode 100644 index 1d2212ea..00000000 --- a/ivi-shell/README +++ /dev/null @@ -1,78 +0,0 @@ - In-vehicle infotainment (information and entertainment) - graphical environment support modules for Weston - - -IVI-shell is an alternative shell for Weston, a Wayland display server. -Window management and application interaction with the display server -are very different to that of a normal desktop, which is why this is -a separate shell and not an extension to the desktop-shell suite with -xdg_shell. As such, applications need to be specifically written to use -IVI-shell. - -IVI-shell contains two main features: -- Common layout library for surface, which allow ivi-shell developer - to develop own shell, linking Common layout library. - For the time being, the library refers Genivi ilm interface. - - https://at.projects.genivi.org/wiki/display/WIE/Wayland+IVI+Extension+Home - -- Extension protocol; ivi-application to tie wl_surface and a given ID. - With this ID, shell can identify which wl_surface is drawn by which - application. In in-vehicle infortainment system, a shell has to update - a property of a wl_surface. E.g. there may be a use case when vehicle - starts to move, the wl_surface drawn by Car navigation is expected to - move top of surfaces. - -The actual software components delivered with Weston are: - -- ivi-application.xml: - Wayland protocol extension for IVI-applications; the public - shell protocol (the same concept as xdg_shell). - Implemented by ivi-shell.so. - -- ivi-shell.so: - A Weston shell module that implements ivi-application.xml interfaces. - Loads ivi-layout.so. - -- ivi-layout.so: - Implements the IVI window management concepts: Screen, Layer, - Surface, groups of Layers, groups of Surfaces, see: - https://at.projects.genivi.org/wiki/display/WIE/Summary+of+Layer+manager+APIs - Offers a stable API for writing IVI-controller modules like - hmi-controller.so against the IVI concepts. In other words, - it offers an API to write IVI window manager modules. - -- hmi-controller.so: - A sample implementation of an IVI-controller module, usually - replaced by IVI system vendors. - Uses ivi-layout.so to perform essentially window manager tasks. - This implementation keeps all window management inside the module, - while IVI-systems may use another module that exposes all window - management via Wayland or other protocol for an external process - to control. - -- ivi-hmi-controller.xml: - Wayland protocol extension for IVI display control; the private - shell protocol for weston-ivi-shell-user-interface client - (the same concept as desktop-shell.xml). - Implemented by hmi-controller.so, and usually replaced by IVI - system vendors. - -- weston-ivi-shell-user-interface: - A sample implementation of an IVI shell helper client, usually - replaced by IVI system vendors. - A helper client for basic display content, similar to - weston-desktop-shell. - - -How to compile: -same as weston. To disable, use option: --disable-ivi-shell for configure. - -How to configure weston.ini: -reference ini file will be generated in /ivi-shell. - -How to run: -same as weston. exec weston. - -How to use UI: -http://lists.freedesktop.org/archives/wayland-devel/attachments/20140625/abbfc064/attachment-0001.png From 6275a0fb320da9e3e1e479fcadb8bae701f296e6 Mon Sep 17 00:00:00 2001 From: Michael Olbrich Date: Thu, 16 Jul 2020 15:59:21 +0200 Subject: [PATCH 552/609] backend-drm: delay mode switches until the last commit is completed Changing the mode will destoy the GBM surface for the output. As a result all corresponding BOs are deleted regardless of the drm_fb refcount. While a commit is pending, the last_state may contain a reference to such a BO. So delay the mode switch until the commit is finished and the reference is release. Signed-off-by: Michael Olbrich --- libweston/backend-drm/drm-internal.h | 1 + libweston/backend-drm/drm.c | 24 ++++++++++++++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index 5bf825a8..f6c1617f 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -545,6 +545,7 @@ struct drm_output { bool destroy_pending; bool disable_pending; bool dpms_off_pending; + bool mode_switch_pending; uint32_t gbm_cursor_handle[2]; struct drm_fb *gbm_cursor_fb[2]; diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index 79ab239d..7d352b9d 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -259,6 +259,8 @@ drm_output_get_disable_state(struct drm_pending_state *pending_state, return output_state; } +static int +drm_output_apply_mode(struct drm_output *output); /** * Mark a drm_output_state (the output's last state) as complete. This handles @@ -287,18 +289,24 @@ drm_output_update_complete(struct drm_output *output, uint32_t flags, output->destroy_pending = false; output->disable_pending = false; output->dpms_off_pending = false; + output->mode_switch_pending = false; drm_output_destroy(&output->base); return; } else if (output->disable_pending) { output->disable_pending = false; output->dpms_off_pending = false; + output->mode_switch_pending = false; weston_output_disable(&output->base); return; } else if (output->dpms_off_pending) { struct drm_pending_state *pending = drm_pending_state_alloc(device); output->dpms_off_pending = false; + output->mode_switch_pending = false; drm_output_get_disable_state(pending, output); drm_pending_state_apply_sync(pending); + } else if (output->mode_switch_pending) { + output->mode_switch_pending = false; + drm_output_apply_mode(output); } if (output->state_cur->dpms == WESTON_DPMS_OFF && output->base.repaint_status != REPAINT_AWAITING_COMPLETION) { @@ -687,8 +695,6 @@ static int drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mode) { struct drm_output *output = to_drm_output(output_base); - struct drm_device *device = output->device; - struct drm_backend *b = device->backend; struct drm_mode *drm_mode; assert(output); @@ -709,6 +715,20 @@ drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mo output->base.current_mode->flags = WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED; + if (output->page_flip_pending || output->atomic_complete_pending) { + output->mode_switch_pending = true; + return 0; + } + + return drm_output_apply_mode(output); +} + +static int +drm_output_apply_mode(struct drm_output *output) +{ + struct drm_device *device = output->device; + struct drm_backend *b = device->backend; + /* XXX: This drops our current buffer too early, before we've started * displaying it. Ideally this should be much more atomic and * integrated with a full repaint cycle, rather than doing a From 37a3025d893def991dec59587d17672aa3bf967a Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Fri, 15 Jul 2022 15:43:30 +0300 Subject: [PATCH 553/609] libweston/desktop/xdg-shell: Add tiled orientation states With the help of a newly introduced function, weston_desktop_surface_set_orientation(), this patch adds missing tiled states from the xdg-shell protocol. The orientation state is passed on as a bitmask enumeration flag, which the shell can set, allowing multiple tiling states at once. These new states are incorporated the same way as the others, retaining the set state, but also avoiding sending new configure events if nothing changed since previously acked data. Signed-off-by: Marius Vlad --- include/libweston-desktop/libweston-desktop.h | 11 ++++++ libweston/desktop/internal.h | 2 + libweston/desktop/surface.c | 10 +++++ libweston/desktop/xdg-shell.c | 39 +++++++++++++++++++ 4 files changed, 62 insertions(+) diff --git a/include/libweston-desktop/libweston-desktop.h b/include/libweston-desktop/libweston-desktop.h index 8257e467..3536935b 100644 --- a/include/libweston-desktop/libweston-desktop.h +++ b/include/libweston-desktop/libweston-desktop.h @@ -44,6 +44,14 @@ enum weston_desktop_surface_edge { WESTON_DESKTOP_SURFACE_EDGE_BOTTOM_RIGHT = 10, }; +enum weston_top_level_tiled_orientation { + WESTON_TOP_LEVEL_TILED_ORIENTATION_NONE = 0 << 0, + WESTON_TOP_LEVEL_TILED_ORIENTATION_LEFT = 1 << 1, + WESTON_TOP_LEVEL_TILED_ORIENTATION_RIGHT = 1 << 2, + WESTON_TOP_LEVEL_TILED_ORIENTATION_TOP = 1 << 3, + WESTON_TOP_LEVEL_TILED_ORIENTATION_BOTTOM = 1 << 4, +}; + struct weston_desktop; struct weston_desktop_client; struct weston_desktop_surface; @@ -166,6 +174,9 @@ void weston_desktop_surface_set_size(struct weston_desktop_surface *surface, int32_t width, int32_t height); void +weston_desktop_surface_set_orientation(struct weston_desktop_surface *surface, + enum weston_top_level_tiled_orientation tile_orientation); +void weston_desktop_surface_close(struct weston_desktop_surface *surface); void weston_desktop_surface_add_metadata_listener(struct weston_desktop_surface *surface, diff --git a/libweston/desktop/internal.h b/libweston/desktop/internal.h index f45b6601..1d035d5c 100644 --- a/libweston/desktop/internal.h +++ b/libweston/desktop/internal.h @@ -105,6 +105,8 @@ struct weston_desktop_surface_implementation { void *user_data, bool resizing); void (*set_size)(struct weston_desktop_surface *surface, void *user_data, int32_t width, int32_t height); + void (*set_orientation)(struct weston_desktop_surface *surface, + void *user_data, enum weston_top_level_tiled_orientation tiled_orientation); void (*committed)(struct weston_desktop_surface *surface, void *user_data, int32_t sx, int32_t sy); void (*update_position)(struct weston_desktop_surface *surface, diff --git a/libweston/desktop/surface.c b/libweston/desktop/surface.c index 433f08ae..6b3f4aeb 100644 --- a/libweston/desktop/surface.c +++ b/libweston/desktop/surface.c @@ -506,6 +506,16 @@ weston_desktop_surface_set_size(struct weston_desktop_surface *surface, int32_t width, height); } +WL_EXPORT void +weston_desktop_surface_set_orientation(struct weston_desktop_surface *surface, + enum weston_top_level_tiled_orientation tile_orientation) +{ + if (surface->implementation->set_orientation != NULL) + surface->implementation->set_orientation(surface, + surface->implementation_data, + tile_orientation); +} + WL_EXPORT void weston_desktop_surface_close(struct weston_desktop_surface *surface) { diff --git a/libweston/desktop/xdg-shell.c b/libweston/desktop/xdg-shell.c index 002a8231..ca9862c1 100644 --- a/libweston/desktop/xdg-shell.c +++ b/libweston/desktop/xdg-shell.c @@ -94,6 +94,7 @@ struct weston_desktop_xdg_toplevel_state { bool fullscreen; bool resizing; bool activated; + uint32_t tiled_orientation; }; struct weston_desktop_xdg_toplevel_configure { @@ -625,6 +626,30 @@ weston_desktop_xdg_toplevel_send_configure(struct weston_desktop_xdg_toplevel *t *s = XDG_TOPLEVEL_STATE_ACTIVATED; } + if (toplevel->pending.state.tiled_orientation & + WESTON_TOP_LEVEL_TILED_ORIENTATION_LEFT) { + s = wl_array_add(&states, sizeof(uint32_t)); + *s = XDG_TOPLEVEL_STATE_TILED_LEFT; + } + + if (toplevel->pending.state.tiled_orientation & + WESTON_TOP_LEVEL_TILED_ORIENTATION_RIGHT) { + s = wl_array_add(&states, sizeof(uint32_t)); + *s = XDG_TOPLEVEL_STATE_TILED_RIGHT; + } + + if (toplevel->pending.state.tiled_orientation & + WESTON_TOP_LEVEL_TILED_ORIENTATION_TOP) { + s = wl_array_add(&states, sizeof(uint32_t)); + *s = XDG_TOPLEVEL_STATE_TILED_TOP; + } + + if (toplevel->pending.state.tiled_orientation & + WESTON_TOP_LEVEL_TILED_ORIENTATION_BOTTOM) { + s = wl_array_add(&states, sizeof(uint32_t)); + *s = XDG_TOPLEVEL_STATE_TILED_BOTTOM; + } + xdg_toplevel_send_configure(toplevel->resource, toplevel->pending.size.width, toplevel->pending.size.height, @@ -686,6 +711,16 @@ weston_desktop_xdg_toplevel_set_size(struct weston_desktop_surface *dsurface, weston_desktop_xdg_surface_schedule_configure(&toplevel->base); } +static void +weston_desktop_xdg_toplevel_set_orientation(struct weston_desktop_surface *surface, void *user_data, + enum weston_top_level_tiled_orientation tiled_orientation) +{ + struct weston_desktop_xdg_toplevel *toplevel = user_data; + + toplevel->pending.state.tiled_orientation = tiled_orientation; + weston_desktop_xdg_surface_schedule_configure(&toplevel->base); +} + static void weston_desktop_xdg_toplevel_committed(struct weston_desktop_xdg_toplevel *toplevel, int32_t sx, int32_t sy) @@ -1104,6 +1139,9 @@ weston_desktop_xdg_toplevel_state_compare(struct weston_desktop_xdg_toplevel *to return false; if (toplevel->pending.state.resizing != configured.state.resizing) return false; + if (toplevel->pending.state.tiled_orientation != + configured.state.tiled_orientation) + return false; if (toplevel->pending.size.width == configured.size.width && toplevel->pending.size.height == configured.size.height) @@ -1478,6 +1516,7 @@ static const struct weston_desktop_surface_implementation weston_desktop_xdg_sur .set_resizing = weston_desktop_xdg_toplevel_set_resizing, .set_activated = weston_desktop_xdg_toplevel_set_activated, .set_size = weston_desktop_xdg_toplevel_set_size, + .set_orientation = weston_desktop_xdg_toplevel_set_orientation, .get_maximized = weston_desktop_xdg_toplevel_get_maximized, .get_fullscreen = weston_desktop_xdg_toplevel_get_fullscreen, From 5bcbe92d51c627093e5669c20ae0ee78ff50abe0 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Sat, 16 Jul 2022 00:22:00 +0300 Subject: [PATCH 554/609] desktops-shell: Add tiled orientation support using key-bindings Patch adds KEY_UP/KEY_DOWN for tiled top and bottom positioning, KEY_LEFT/KEY_RIGHT correspondingly, for left and right positioning. It also modifies the man page to include these new bindings, But also with commit 'compositor: Remove desktop zoom' we no longer have zoom effects so removed them. Signed-off-by: Marius Vlad --- desktop-shell/shell.c | 86 +++++++++++++++++++++++++++++++++++++++++ man/weston-bindings.man | 19 ++++++++- 2 files changed, 103 insertions(+), 2 deletions(-) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 0e746b05..0c6b769a 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -2896,6 +2896,83 @@ fullscreen_binding(struct weston_keyboard *keyboard, set_fullscreen(shsurf, !fullscreen, NULL); } +static void +set_tiled_orientation(struct weston_surface *focus, + enum weston_top_level_tiled_orientation orientation) +{ + struct weston_surface *surface; + struct shell_surface *shsurf; + int width, height; + pixman_rectangle32_t area; + struct weston_geometry geom; + int x, y; + + surface = weston_surface_get_main_surface(focus); + if (surface == NULL) + return; + + shsurf = get_shell_surface(surface); + if (shsurf == NULL) + return; + + get_maximized_size(shsurf, &width, &height); + get_output_work_area(shsurf->shell, shsurf->output, &area); + geom = weston_desktop_surface_get_geometry(shsurf->desktop_surface); + + if (orientation & WESTON_TOP_LEVEL_TILED_ORIENTATION_LEFT || + orientation & WESTON_TOP_LEVEL_TILED_ORIENTATION_RIGHT) + width /= 2; + else if (orientation & WESTON_TOP_LEVEL_TILED_ORIENTATION_TOP || + orientation & WESTON_TOP_LEVEL_TILED_ORIENTATION_BOTTOM) + height /= 2; + + x = area.x - geom.x; + y = area.y - geom.y; + + if (orientation & WESTON_TOP_LEVEL_TILED_ORIENTATION_RIGHT) + x += width; + else if (orientation & WESTON_TOP_LEVEL_TILED_ORIENTATION_BOTTOM) + y += height; + + weston_view_set_position(shsurf->view, x, y); + weston_desktop_surface_set_size(shsurf->desktop_surface, width, height); + weston_desktop_surface_set_orientation(shsurf->desktop_surface, orientation); + weston_compositor_schedule_repaint(surface->compositor); +} + +static void +set_tiled_orientation_left(struct weston_keyboard *keyboard, + const struct timespec *time, + uint32_t button, void *data) +{ + set_tiled_orientation(keyboard->focus, WESTON_TOP_LEVEL_TILED_ORIENTATION_LEFT); + +} + +static void +set_tiled_orientation_right(struct weston_keyboard *keyboard, + const struct timespec *time, + uint32_t button, void *data) +{ + set_tiled_orientation(keyboard->focus, WESTON_TOP_LEVEL_TILED_ORIENTATION_RIGHT); +} + +static void +set_tiled_orientation_up(struct weston_keyboard *keyboard, + const struct timespec *time, + uint32_t button, void *data) +{ + set_tiled_orientation(keyboard->focus, WESTON_TOP_LEVEL_TILED_ORIENTATION_TOP); +} + +static void +set_tiled_orientation_down(struct weston_keyboard *keyboard, + const struct timespec *time, + uint32_t button, void *data) +{ + set_tiled_orientation(keyboard->focus, WESTON_TOP_LEVEL_TILED_ORIENTATION_BOTTOM); +} + static void touch_move_binding(struct weston_touch *touch, const struct timespec *time, void *data) { @@ -4492,6 +4569,15 @@ shell_add_bindings(struct weston_compositor *ec, struct desktop_shell *shell) mod | MODIFIER_SHIFT, resize_binding, shell); + weston_compositor_add_key_binding(ec, KEY_LEFT, mod | MODIFIER_SHIFT, + set_tiled_orientation_left, NULL); + weston_compositor_add_key_binding(ec, KEY_RIGHT, mod | MODIFIER_SHIFT, + set_tiled_orientation_right, NULL); + weston_compositor_add_key_binding(ec, KEY_UP, mod | MODIFIER_SHIFT, + set_tiled_orientation_up, NULL); + weston_compositor_add_key_binding(ec, KEY_DOWN, mod | MODIFIER_SHIFT, + set_tiled_orientation_down, NULL); + if (ec->capabilities & WESTON_CAP_ROTATION_ANY) weston_compositor_add_button_binding(ec, BTN_MIDDLE, mod, rotate_binding, NULL); diff --git a/man/weston-bindings.man b/man/weston-bindings.man index 398bec25..d528a807 100644 --- a/man/weston-bindings.man +++ b/man/weston-bindings.man @@ -33,9 +33,24 @@ Kill active window Maximize active window .P .RE -.B mod + PageUp, mod + PageDown +.B mod + Shift + KEY_LEFT .RS 4 -Zoom desktop in (or out) +Make the active window tiled left. +.P +.RE +.B mod + Shift + KEY_RIGHT +.RS 4 +Make the active window tiled right. +.P +.RE +.B mod + Shift + KEY_UP +.RS 4 +Make the active window tiled top. +.P +.RE +.B mod + Shift + KEY_DOWN +.RS 4 +Make the active window tiled bottom. .P .RE .B mod + Tab From c33e8d2c10c43f8b935ce58f49b2845aa84c850d Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Fri, 29 Jul 2022 17:04:32 +0300 Subject: [PATCH 555/609] desktop-shell: Handle tiled orientation in various circumstances This properly handles transition states to, and from, maximized, fullscreen, surface movement and resizing. Specifically for surface movement and resizing we unset any (previously set) tiled information we might have. The same happens for maximized and fullscreen but additionally we attempt re-install the orientation if we had one previously. Signed-off-by: Marius Vlad --- desktop-shell/shell.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 0c6b769a..9da08aef 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -115,6 +115,7 @@ struct shell_surface { bool saved_rotation_valid; int unresponsive, grabbed; uint32_t resize_edges; + uint32_t orientation; struct { struct weston_transform transform; @@ -1122,6 +1123,9 @@ surface_move(struct shell_surface *shsurf, struct weston_pointer *pointer, pointer->grab_y; move->client_initiated = client_initiated; + weston_desktop_surface_set_orientation(shsurf->desktop_surface, + WESTON_TOP_LEVEL_TILED_ORIENTATION_NONE); + shsurf->orientation = WESTON_TOP_LEVEL_TILED_ORIENTATION_NONE; shell_grab_start(&move->base, &move_grab_interface, shsurf, pointer, WESTON_DESKTOP_SHELL_CURSOR_MOVE); @@ -1273,6 +1277,9 @@ surface_resize(struct shell_surface *shsurf, shsurf->resize_edges = edges; weston_desktop_surface_set_resizing(shsurf->desktop_surface, true); + weston_desktop_surface_set_orientation(shsurf->desktop_surface, + WESTON_TOP_LEVEL_TILED_ORIENTATION_NONE); + shsurf->orientation = WESTON_TOP_LEVEL_TILED_ORIENTATION_NONE; shell_grab_start(&resize->base, &resize_grab_interface, shsurf, pointer, edges); @@ -1500,6 +1507,9 @@ unset_fullscreen(struct shell_surface *shsurf) weston_view_set_initial_position(shsurf->view, shsurf->shell); shsurf->saved_position_valid = false; + weston_desktop_surface_set_orientation(shsurf->desktop_surface, + shsurf->orientation); + if (shsurf->saved_rotation_valid) { wl_list_insert(&shsurf->view->geometry.transformation_list, &shsurf->rotation.transform.link); @@ -1523,6 +1533,9 @@ unset_maximized(struct shell_surface *shsurf) weston_view_set_initial_position(shsurf->view, shsurf->shell); shsurf->saved_position_valid = false; + weston_desktop_surface_set_orientation(shsurf->desktop_surface, + shsurf->orientation); + if (shsurf->saved_rotation_valid) { wl_list_insert(&shsurf->view->geometry.transformation_list, &shsurf->rotation.transform.link); @@ -2167,6 +2180,8 @@ set_fullscreen(struct shell_surface *shsurf, bool fullscreen, width = shsurf->output->width; height = shsurf->output->height; } + weston_desktop_surface_set_orientation(shsurf->desktop_surface, + WESTON_TOP_LEVEL_TILED_ORIENTATION_NONE); } else if (weston_desktop_surface_get_maximized(desktop_surface)) { get_maximized_size(shsurf, &width, &height); } @@ -2286,6 +2301,9 @@ set_maximized(struct shell_surface *shsurf, bool maximized) shell_surface_set_output(shsurf, output); get_maximized_size(shsurf, &width, &height); + + weston_desktop_surface_set_orientation(shsurf->desktop_surface, + WESTON_TOP_LEVEL_TILED_ORIENTATION_NONE); } weston_desktop_surface_set_maximized(desktop_surface, maximized); weston_desktop_surface_set_size(desktop_surface, width, height); @@ -2915,6 +2933,7 @@ set_tiled_orientation(struct weston_surface *focus, if (shsurf == NULL) return; + shsurf->orientation = orientation; get_maximized_size(shsurf, &width, &height); get_output_work_area(shsurf->shell, shsurf->output, &area); geom = weston_desktop_surface_get_geometry(shsurf->desktop_surface); From aa2b615d301f3378fe485545f4ef251a7bb3fd37 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 9 Aug 2022 21:59:31 +0200 Subject: [PATCH 556/609] build: bump to version 10.0.91 for the alpha release Signed-off-by: Simon Ser --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 780d6b04..a49f1de0 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project('weston', 'c', - version: '10.0.90', + version: '10.0.91', default_options: [ 'warning_level=3', 'c_std=gnu99', From 8b0125d601296e59c87a3c7b4392852635adacb4 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Wed, 10 Aug 2022 11:43:37 -0500 Subject: [PATCH 557/609] Revert "clients/window: Fix animated cursors" This reverts commit f079f43658d9cbffa402a84101b31072775d3619. This only partially fixed a problem introduced in 992ee045f1b59c037165ecdd752c2f2d78f16ee4 I'm reverting that commit in favor of a different fix, so this broken fix needs to go first. Signed-off-by: Derek Foreman --- clients/window.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/clients/window.c b/clients/window.c index 81131f90..df343ea1 100644 --- a/clients/window.c +++ b/clients/window.c @@ -320,6 +320,7 @@ struct input { struct wl_surface *pointer_surface; uint32_t modifiers; uint32_t pointer_enter_serial; + uint32_t cursor_serial; float sx, sy; struct wl_list link; @@ -2541,12 +2542,6 @@ input_remove_pointer_focus(struct input *input) input->pointer_focus = NULL; input->current_cursor = CURSOR_UNSET; cancel_pointer_image_update(input); - wl_surface_destroy(input->pointer_surface); - input->pointer_surface = NULL; - if (input->cursor_frame_cb) { - wl_callback_destroy(input->cursor_frame_cb); - input->cursor_frame_cb = NULL; - } } static void @@ -3676,7 +3671,6 @@ schedule_pointer_image_update(struct input *input, wl_callback_add_listener(input->cursor_frame_cb, &pointer_surface_listener, input); - wl_surface_commit(input->pointer_surface); } static void @@ -3760,15 +3754,30 @@ static const struct wl_callback_listener pointer_surface_listener = { void input_set_pointer_image(struct input *input, int pointer) { + int force = 0; + if (!input->pointer) return; - if (pointer == input->current_cursor) + if (input->pointer_enter_serial > input->cursor_serial) + force = 1; + + if (!force && pointer == input->current_cursor) return; input->current_cursor = pointer; + input->cursor_serial = input->pointer_enter_serial; if (!input->cursor_frame_cb) pointer_surface_frame_callback(input, NULL, 0); + else if (force && !input_set_pointer_special(input)) { + /* The current frame callback may be stuck if, for instance, + * the set cursor request was processed by the server after + * this client lost the focus. In this case the cursor surface + * might not be mapped and the frame callback wouldn't ever + * complete. Send a set_cursor and attach to try to map the + * cursor surface again so that the callback will finish */ + input_set_pointer_image_index(input, 0); + } } struct wl_data_device * From ebbe30df3c51c843af6a76b6bb10e85b703462df Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Wed, 10 Aug 2022 11:44:17 -0500 Subject: [PATCH 558/609] Revert "clients/window: atomically update pointer cursor" This reverts commit 992ee045f1b59c037165ecdd752c2f2d78f16ee4. Recreating the surface for every cursor change causes flickering cursors on some compositors, and is not the best way to achieve atomic cursor updates Signed-off-by: Derek Foreman --- clients/window.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/clients/window.c b/clients/window.c index df343ea1..873ec090 100644 --- a/clients/window.c +++ b/clients/window.c @@ -3581,8 +3581,6 @@ input_set_pointer_image_index(struct input *input, int index) struct wl_buffer *buffer; struct wl_cursor *cursor; struct wl_cursor_image *image; - struct wl_surface *prev_surface; - struct display *d = input->display; if (!input->pointer) return; @@ -3601,11 +3599,6 @@ input_set_pointer_image_index(struct input *input, int index) if (!buffer) return; - /* Don't re-use the previous surface, otherwise the new buffer and the - * new hotspot aren't applied atomically. */ - prev_surface = input->pointer_surface; - input->pointer_surface = wl_compositor_create_surface(d->compositor); - wl_surface_attach(input->pointer_surface, buffer, 0, 0); wl_surface_damage(input->pointer_surface, 0, 0, image->width, image->height); @@ -3613,9 +3606,6 @@ input_set_pointer_image_index(struct input *input, int index) wl_pointer_set_cursor(input->pointer, input->pointer_enter_serial, input->pointer_surface, image->hotspot_x, image->hotspot_y); - - if (prev_surface) - wl_surface_destroy(prev_surface); } static const struct wl_callback_listener pointer_surface_listener; @@ -3720,11 +3710,11 @@ pointer_surface_frame_callback(void *data, struct wl_callback *callback, time - input->cursor_anim_start, &duration); - input_set_pointer_image_index(input, i); - if (cursor->image_count > 1) schedule_pointer_image_update(input, cursor, duration, force_frame); + + input_set_pointer_image_index(input, i); } static void @@ -5735,6 +5725,8 @@ display_add_input(struct display *d, uint32_t id, int display_seat_version) input); } + input->pointer_surface = wl_compositor_create_surface(d->compositor); + toytimer_init(&input->cursor_timer, CLOCK_MONOTONIC, d, cursor_timer_func); @@ -5802,8 +5794,7 @@ input_destroy(struct input *input) fini_xkb(input); - if (input->pointer_surface) - wl_surface_destroy(input->pointer_surface); + wl_surface_destroy(input->pointer_surface); wl_list_remove(&input->link); wl_seat_destroy(input->seat); From 646cc1b389cc70e742d5cebef3a3b94e9b0a0c9f Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Wed, 10 Aug 2022 12:59:20 -0500 Subject: [PATCH 559/609] clients: Set the hotspot with attach if we already have a valid cursor We want atomic hotspot updates - this can't happen with wl_pointer_set_cursor. So if we have a surface that already has a cursor role, just update the hotspot when attaching new content. Signed-off-by: Derek Foreman --- clients/window.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/clients/window.c b/clients/window.c index 873ec090..020e3cf6 100644 --- a/clients/window.c +++ b/clients/window.c @@ -318,6 +318,8 @@ struct input { struct toytimer cursor_timer; bool cursor_timer_running; struct wl_surface *pointer_surface; + bool pointer_surface_has_role; + int hotspot_x, hotspot_y; uint32_t modifiers; uint32_t pointer_enter_serial; uint32_t cursor_serial; @@ -2569,6 +2571,7 @@ pointer_handle_enter(void *data, struct wl_pointer *pointer, input->display->serial = serial; input->pointer_enter_serial = serial; input->pointer_focus = window; + input->pointer_surface_has_role = false; input->sx = sx; input->sy = sy; @@ -3581,6 +3584,7 @@ input_set_pointer_image_index(struct input *input, int index) struct wl_buffer *buffer; struct wl_cursor *cursor; struct wl_cursor_image *image; + int dx = 0, dy = 0; if (!input->pointer) return; @@ -3599,13 +3603,24 @@ input_set_pointer_image_index(struct input *input, int index) if (!buffer) return; - wl_surface_attach(input->pointer_surface, buffer, 0, 0); + if (input->pointer_surface_has_role) { + dx = input->hotspot_x - image->hotspot_x; + dy = input->hotspot_y - image->hotspot_y; + } + wl_surface_attach(input->pointer_surface, buffer, dx, dy); wl_surface_damage(input->pointer_surface, 0, 0, image->width, image->height); wl_surface_commit(input->pointer_surface); - wl_pointer_set_cursor(input->pointer, input->pointer_enter_serial, - input->pointer_surface, - image->hotspot_x, image->hotspot_y); + + if (!input->pointer_surface_has_role) { + wl_pointer_set_cursor(input->pointer, + input->pointer_enter_serial, + input->pointer_surface, + image->hotspot_x, image->hotspot_y); + input->pointer_surface_has_role = true; + } + input->hotspot_x = image->hotspot_x; + input->hotspot_y = image->hotspot_y; } static const struct wl_callback_listener pointer_surface_listener; @@ -3617,11 +3632,14 @@ input_set_pointer_special(struct input *input) wl_pointer_set_cursor(input->pointer, input->pointer_enter_serial, NULL, 0, 0); + input->pointer_surface_has_role = false; return true; } - if (input->current_cursor == CURSOR_UNSET) + if (input->current_cursor == CURSOR_UNSET) { + input->pointer_surface_has_role = false; return true; + } return false; } @@ -5726,6 +5744,7 @@ display_add_input(struct display *d, uint32_t id, int display_seat_version) } input->pointer_surface = wl_compositor_create_surface(d->compositor); + input->pointer_surface_has_role = false; toytimer_init(&input->cursor_timer, CLOCK_MONOTONIC, d, cursor_timer_func); From 8f1ca8204a4ca7fba132b9656717f1a30bff6798 Mon Sep 17 00:00:00 2001 From: Erik Kurzinger Date: Fri, 12 Aug 2022 08:22:26 -0700 Subject: [PATCH 560/609] clients/simple-egl: call eglSwapInterval after eglMakeCurrent If weston-simple-egl is run with the "-b" flag, it will attempt to set the swap interval to 0 during create_surface. However, at that point, it will not have made its EGLContext current yet, causing the eglSwapInterval call to have no effect. To fix this, wait until the EGLContext has been made current in init_gl before updating the swap interval. Signed-off-by: Erik Kurzinger --- clients/simple-egl.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clients/simple-egl.c b/clients/simple-egl.c index c530d8d5..c6fcc313 100644 --- a/clients/simple-egl.c +++ b/clients/simple-egl.c @@ -303,6 +303,9 @@ init_gl(struct window *window) window->egl_surface, window->display->egl.ctx); assert(ret == EGL_TRUE); + if (!window->frame_sync) + eglSwapInterval(window->display->egl.dpy, 0); + frag = create_shader(window, frag_shader_text, GL_FRAGMENT_SHADER); vert = create_shader(window, vert_shader_text, GL_VERTEX_SHADER); @@ -491,9 +494,6 @@ create_surface(struct window *window) window->wait_for_configure = true; wl_surface_commit(window->surface); - - if (!window->frame_sync) - eglSwapInterval(display->egl.dpy, 0); } static void From 32791eae1fa3d5c354b2e6b684ca2e63397df917 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Fri, 19 Aug 2022 15:23:06 +0300 Subject: [PATCH 561/609] simple-egl: Update buffer_size dimensions when starting as maximized With commit 62ab6891db9763dd44228, 'clients/simple-egl: Handle buffer scale and transform' we changed the way we resized the client, by encapsulating the resize in update_buffer_geometry() function. we didn't correct that when creating the EGL window, which might be problematic if you attempt to start the window with different a different state, like maximized. Fixes 62ab6891db9763dd4 Signed-off-by: Marius Vlad --- clients/simple-egl.c | 172 ++++++++++++++++++++++--------------------- 1 file changed, 89 insertions(+), 83 deletions(-) diff --git a/clients/simple-egl.c b/clients/simple-egl.c index c6fcc313..657ec49f 100644 --- a/clients/simple-egl.c +++ b/clients/simple-egl.c @@ -283,6 +283,92 @@ create_shader(struct window *window, const char *source, GLenum shader_type) return shader; } +static int32_t +compute_buffer_scale(struct window *window) +{ + struct window_output *window_output; + int32_t scale = 1; + + wl_list_for_each(window_output, &window->window_output_list, link) { + if (window_output->output->scale > scale) + scale = window_output->output->scale; + } + + return scale; +} + +static enum wl_output_transform +compute_buffer_transform(struct window *window) +{ + struct window_output *window_output; + enum wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; + + wl_list_for_each(window_output, &window->window_output_list, link) { + /* If the surface spans over multiple outputs the optimal + * transform value can be ambiguous. Thus just return the value + * from the oldest entered output. + */ + transform = window_output->output->transform; + break; + } + + return transform; +} + +static void +update_buffer_geometry(struct window *window) +{ + enum wl_output_transform new_buffer_transform; + int32_t new_buffer_scale; + struct geometry new_buffer_size; + + new_buffer_transform = compute_buffer_transform(window); + if (window->buffer_transform != new_buffer_transform) { + window->buffer_transform = new_buffer_transform; + wl_surface_set_buffer_transform(window->surface, + window->buffer_transform); + } + + new_buffer_scale = compute_buffer_scale(window); + if (window->buffer_scale != new_buffer_scale) { + window->buffer_scale = new_buffer_scale; + wl_surface_set_buffer_scale(window->surface, + window->buffer_scale); + } + + switch (window->buffer_transform) { + case WL_OUTPUT_TRANSFORM_NORMAL: + case WL_OUTPUT_TRANSFORM_180: + case WL_OUTPUT_TRANSFORM_FLIPPED: + case WL_OUTPUT_TRANSFORM_FLIPPED_180: + new_buffer_size.width = window->logical_size.width; + new_buffer_size.height = window->logical_size.height; + break; + case WL_OUTPUT_TRANSFORM_90: + case WL_OUTPUT_TRANSFORM_270: + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + case WL_OUTPUT_TRANSFORM_FLIPPED_270: + new_buffer_size.width = window->logical_size.height; + new_buffer_size.height = window->logical_size.width; + break; + } + + new_buffer_size.width *= window->buffer_scale; + new_buffer_size.height *= window->buffer_scale; + + if (window->buffer_size.width != new_buffer_size.width || + window->buffer_size.height != new_buffer_size.height) { + window->buffer_size = new_buffer_size; + if (window->native) + wl_egl_window_resize(window->native, + window->buffer_size.width, + window->buffer_size.height, 0, 0); + } + + window->needs_buffer_geometry_update = false; +} + + static void init_gl(struct window *window) { @@ -291,6 +377,9 @@ init_gl(struct window *window) GLint status; EGLBoolean ret; + if (window->needs_buffer_geometry_update) + update_buffer_geometry(window); + window->native = wl_egl_window_create(window->surface, window->buffer_size.width, window->buffer_size.height); @@ -515,89 +604,6 @@ destroy_surface(struct window *window) wl_surface_destroy(window->surface); } -static int32_t -compute_buffer_scale(struct window *window) -{ - struct window_output *window_output; - int32_t scale = 1; - - wl_list_for_each(window_output, &window->window_output_list, link) { - if (window_output->output->scale > scale) - scale = window_output->output->scale; - } - - return scale; -} - -static enum wl_output_transform -compute_buffer_transform(struct window *window) -{ - struct window_output *window_output; - enum wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; - - wl_list_for_each(window_output, &window->window_output_list, link) { - /* If the surface spans over multiple outputs the optimal - * transform value can be ambiguous. Thus just return the value - * from the oldest entered output. - */ - transform = window_output->output->transform; - break; - } - - return transform; -} - -static void -update_buffer_geometry(struct window *window) -{ - enum wl_output_transform new_buffer_transform; - int32_t new_buffer_scale; - struct geometry new_buffer_size; - - new_buffer_transform = compute_buffer_transform(window); - if (window->buffer_transform != new_buffer_transform) { - window->buffer_transform = new_buffer_transform; - wl_surface_set_buffer_transform(window->surface, - window->buffer_transform); - } - - new_buffer_scale = compute_buffer_scale(window); - if (window->buffer_scale != new_buffer_scale) { - window->buffer_scale = new_buffer_scale; - wl_surface_set_buffer_scale(window->surface, - window->buffer_scale); - } - - switch (window->buffer_transform) { - case WL_OUTPUT_TRANSFORM_NORMAL: - case WL_OUTPUT_TRANSFORM_180: - case WL_OUTPUT_TRANSFORM_FLIPPED: - case WL_OUTPUT_TRANSFORM_FLIPPED_180: - new_buffer_size.width = window->logical_size.width; - new_buffer_size.height = window->logical_size.height; - break; - case WL_OUTPUT_TRANSFORM_90: - case WL_OUTPUT_TRANSFORM_270: - case WL_OUTPUT_TRANSFORM_FLIPPED_90: - case WL_OUTPUT_TRANSFORM_FLIPPED_270: - new_buffer_size.width = window->logical_size.height; - new_buffer_size.height = window->logical_size.width; - break; - } - - new_buffer_size.width *= window->buffer_scale; - new_buffer_size.height *= window->buffer_scale; - - if (window->buffer_size.width != new_buffer_size.width || - window->buffer_size.height != new_buffer_size.height) { - window->buffer_size = new_buffer_size; - wl_egl_window_resize(window->native, - window->buffer_size.width, - window->buffer_size.height, 0, 0); - } - - window->needs_buffer_geometry_update = false; -} static void redraw(struct window *window) From 4990e28ff266e5cce5031bceccdd079f9449149c Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 23 Aug 2022 19:16:02 +0200 Subject: [PATCH 562/609] build: bump to version 10.0.92 for the beta release --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index a49f1de0..51a3ec61 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project('weston', 'c', - version: '10.0.91', + version: '10.0.92', default_options: [ 'warning_level=3', 'c_std=gnu99', From 4cde507be6a116b3828f3a86a0e97639f04d9046 Mon Sep 17 00:00:00 2001 From: Michael Olbrich Date: Tue, 30 Aug 2022 17:10:27 +0200 Subject: [PATCH 563/609] backend-drm: fix plane sorting The planes in the plane_list must be sorted from largest zpos_max to smallest. Currently the plane order is only correct when the planes are already ordered and added starting with the smallest zpos_max. This works accidentally in most cases because the primary plane is usually first and there is often only one overlay plane or the zpos is sufficiantly configurable. To fix this, insert a new plane before the first plane with a smaller zpos_max. And if none is found, insert it at the end of the list. Signed-off-by: Michael Olbrich --- libweston/backend-drm/drm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index 7d352b9d..962cc284 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -840,13 +840,13 @@ drm_plane_create(struct drm_device *device, const drmModePlane *kplane) weston_plane_init(&plane->base, compositor, 0, 0); wl_list_for_each(tmp, &device->plane_list, link) { - if (tmp->zpos_max > plane->zpos_max) { + if (tmp->zpos_max < plane->zpos_max) { wl_list_insert(tmp->link.prev, &plane->link); break; } } if (plane->link.next == NULL) - wl_list_insert(&device->plane_list, &plane->link); + wl_list_insert(device->plane_list.prev, &plane->link); return plane; From d6ab6da9887353056962343d9dcbfb9610ac889b Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Tue, 30 Aug 2022 20:25:01 +0300 Subject: [PATCH 564/609] libweston/backend-x11: Tracking previous events over multiple calls Rather than doing it with a local variable, track any previous events by hanging it out of the x11 backend and use it to handle keymap notify events. In this way we avoid corrupting the surface destroy signal list, in notify_keyboard_focus_out(), ultimately leading to a crash. Fixes #649, #650 Suggested-by: Daniel Stone Reported-by: Alexandros Frantzis Signed-off-by: Marius Vlad --- libweston/backend-x11/x11.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/libweston/backend-x11/x11.c b/libweston/backend-x11/x11.c index f887961e..34820a06 100644 --- a/libweston/backend-x11/x11.c +++ b/libweston/backend-x11/x11.c @@ -119,6 +119,7 @@ struct x11_backend { xcb_atom_t cardinal; xcb_atom_t xkb_names; } atom; + xcb_generic_event_t *prev_event; }; struct x11_head { @@ -1494,7 +1495,7 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data) { struct x11_backend *b = data; struct x11_output *output; - xcb_generic_event_t *event, *prev; + xcb_generic_event_t *event; xcb_client_message_event_t *client_message; xcb_enter_notify_event_t *enter_notify; xcb_key_press_event_t *key_press, *key_release; @@ -1510,16 +1511,15 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data) int count; struct timespec time; - prev = NULL; count = 0; while (x11_backend_next_event(b, &event, mask)) { response_type = event->response_type & ~0x80; - switch (prev ? prev->response_type & ~0x80 : 0x80) { + switch (b->prev_event ? b->prev_event->response_type & ~0x80 : 0x80) { case XCB_KEY_RELEASE: /* Suppress key repeat events; this is only used if we * don't have XCB XKB support. */ - key_release = (xcb_key_press_event_t *) prev; + key_release = (xcb_key_press_event_t *) b->prev_event; key_press = (xcb_key_press_event_t *) event; if (response_type == XCB_KEY_PRESS && key_release->time == key_press->time && @@ -1527,8 +1527,8 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data) /* Don't deliver the held key release * event or the new key press event. */ free(event); - free(prev); - prev = NULL; + free(b->prev_event); + b->prev_event = NULL; continue; } else { /* Deliver the held key release now @@ -1541,8 +1541,8 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data) key_release->detail - 8, WL_KEYBOARD_KEY_STATE_RELEASED, STATE_UPDATE_AUTOMATIC); - free(prev); - prev = NULL; + free(b->prev_event); + b->prev_event = NULL; break; } @@ -1566,8 +1566,8 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data) notify_keyboard_focus_in(&b->core_seat, &b->keys, STATE_UPDATE_AUTOMATIC); - free(prev); - prev = NULL; + free(b->prev_event); + b->prev_event = NULL; break; default: @@ -1592,7 +1592,7 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data) /* If we don't have XKB, we need to use the lame * autorepeat detection above. */ if (!b->has_xkb) { - prev = event; + b->prev_event = event; break; } key_release = (xcb_key_press_event_t *) event; @@ -1687,7 +1687,7 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data) if (focus_in->mode == XCB_NOTIFY_MODE_WHILE_GRABBED) break; - prev = event; + b->prev_event = event; break; case XCB_FOCUS_OUT: @@ -1721,13 +1721,13 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data) #endif count++; - if (prev != event) - free (event); + if (b->prev_event != event) + free(event); } - switch (prev ? prev->response_type & ~0x80 : 0x80) { + switch (b->prev_event ? b->prev_event->response_type & ~0x80 : 0x80) { case XCB_KEY_RELEASE: - key_release = (xcb_key_press_event_t *) prev; + key_release = (xcb_key_press_event_t *) b->prev_event; update_xkb_state_from_core(b, key_release->state); weston_compositor_get_time(&time); notify_key(&b->core_seat, @@ -1735,8 +1735,8 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data) key_release->detail - 8, WL_KEYBOARD_KEY_STATE_RELEASED, STATE_UPDATE_AUTOMATIC); - free(prev); - prev = NULL; + free(b->prev_event); + b->prev_event = NULL; break; default: break; From 1aa935e6d81603505701bb8bb97e1e4fee589721 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Tue, 30 Aug 2022 20:26:28 +0300 Subject: [PATCH 565/609] libweston/input: Assert if we're still having a notify listener installed Tracking correctly previous events shouldn't corrupt the surface destroy signal list. This enforces that by ensuring that we wouldn't have a .notify wl_listener still being set (which shouldn't happen if we do eventually get a focus_in event that clears it out). Suggested-by: Alexandros Frantzis Signed-off-by: Marius Vlad --- libweston/input.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libweston/input.c b/libweston/input.c index ead4b79e..6cbb8db0 100644 --- a/libweston/input.c +++ b/libweston/input.c @@ -2329,6 +2329,7 @@ notify_keyboard_focus_out(struct weston_seat *seat) if (focus) { seat->use_saved_kbd_focus = true; seat->saved_kbd_focus = focus; + assert(seat->saved_kbd_focus_listener.notify == NULL); seat->saved_kbd_focus_listener.notify = destroy_device_saved_kbd_focus; wl_signal_add(&focus->destroy_signal, From a6b8f0f89cf7850e69faef31e3755776f0218bde Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 6 Sep 2022 19:29:17 +0200 Subject: [PATCH 566/609] build: bump to version 10.0.93 for the RC1 release --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 51a3ec61..66bf6152 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project('weston', 'c', - version: '10.0.92', + version: '10.0.93', default_options: [ 'warning_level=3', 'c_std=gnu99', From 11ba13d7178d2c2c74373458ab32cb6ab66897b1 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Mon, 12 Sep 2022 11:52:19 -0500 Subject: [PATCH 567/609] clients: Fix cursors when compositor gives wl_seat before wl_compositor We have no guarantee that we can create a surface for the pointer at the instant we receive a seat that will (probably eventually) need one. Hold off until we receive an enter event before creating this - at that point we know with certainty that wl_compositor is available, since we've used it to create the surface that was entered. Fixes #659 Signed-off-by: Derek Foreman --- clients/window.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/clients/window.c b/clients/window.c index 020e3cf6..3036bb42 100644 --- a/clients/window.c +++ b/clients/window.c @@ -2571,6 +2571,15 @@ pointer_handle_enter(void *data, struct wl_pointer *pointer, input->display->serial = serial; input->pointer_enter_serial = serial; input->pointer_focus = window; + + /* Some compositors advertise wl_seat before wl_compositor. This + * makes it potentially impossible to create the pointer surface + * when we bind the seat, so we need to create our pointer surface + * now instead. + */ + if (!input->pointer_surface) + input->pointer_surface = wl_compositor_create_surface(input->display->compositor); + input->pointer_surface_has_role = false; input->sx = sx; @@ -5743,7 +5752,6 @@ display_add_input(struct display *d, uint32_t id, int display_seat_version) input); } - input->pointer_surface = wl_compositor_create_surface(d->compositor); input->pointer_surface_has_role = false; toytimer_init(&input->cursor_timer, CLOCK_MONOTONIC, d, @@ -5813,7 +5821,8 @@ input_destroy(struct input *input) fini_xkb(input); - wl_surface_destroy(input->pointer_surface); + if (input->pointer_surface) + wl_surface_destroy(input->pointer_surface); wl_list_remove(&input->link); wl_seat_destroy(input->seat); From 259bd1782208559edd97b10c17fe2a25d1ecdc40 Mon Sep 17 00:00:00 2001 From: Pekka Paalanen Date: Thu, 8 Sep 2022 10:49:50 +0300 Subject: [PATCH 568/609] doc: remove directives deprecated in Doxygen 1.9.5 All these Doxygen configuration directives raise a deprecation warning with Doxygen 1.9.5. Since we have WARN_AS_ERROR = YES, this causes the build to fail. Remove these deprecated directives. I have checked the differences by first building from scratch without this patch, and then building from scratch with this patch, and in the latter builddir checking $ diff -ru -x '*.md5' -x '*.pdf' ~/tmp/weston-doc-before doc The only differences are the Doxygen config file and one .pickle file. So it seems the generated docs are identical with Doxygen 1.9.1. Fixes: https://gitlab.freedesktop.org/wayland/weston/-/issues/661 Signed-off-by: Pekka Paalanen --- doc/sphinx/doxygen.ini.in | 40 --------------------------------------- 1 file changed, 40 deletions(-) diff --git a/doc/sphinx/doxygen.ini.in b/doc/sphinx/doxygen.ini.in index 1e37d226..1e71d07b 100644 --- a/doc/sphinx/doxygen.ini.in +++ b/doc/sphinx/doxygen.ini.in @@ -1486,17 +1486,6 @@ EXT_LINKS_IN_WINDOW = NO FORMULA_FONTSIZE = 10 -# Use the FORMULA_TRANPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are not -# supported properly for IE 6.0, but are supported on all modern browsers. -# -# Note that when changing this option you need to delete any form_*.png files in -# the HTML output directory before the changes have effect. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FORMULA_TRANSPARENT = YES - # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # http://www.mathjax.org) which uses client side Javascript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX @@ -1931,23 +1920,6 @@ HAVE_DOT = YES DOT_NUM_THREADS = 0 -# When you want a differently looking font in the dot files that doxygen -# generates you can specify the font name using DOT_FONTNAME. You need to make -# sure dot is able to find the font, which can be done by putting it in a -# standard location or by setting the DOTFONTPATH environment variable or by -# setting DOT_FONTPATH to the directory containing the font. -# The default value is: Helvetica. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_FONTNAME = Helvetica - -# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of -# dot graphs. -# Minimum value: 4, maximum value: 24, default value: 10. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_FONTSIZE = 10 - # By default doxygen will tell dot to use the default font as specified with # DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set # the path where dot can find it using this tag. @@ -2162,18 +2134,6 @@ DOT_GRAPH_MAX_NODES = 250 MAX_DOT_GRAPH_DEPTH = 0 -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not seem -# to support this out of the box. -# -# Warning: Depending on the platform used, enabling this option may lead to -# badly anti-aliased labels on the edges of a graph (i.e. they become hard to -# read). -# The default value is: NO. -# This tag requires that the tag HAVE_DOT is set to YES. - -DOT_TRANSPARENT = YES - # Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) support From b87418e4c4007b9a118dabf95fa3533361257724 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Wed, 14 Sep 2022 16:41:57 +0300 Subject: [PATCH 569/609] clients/eventdemo: Remove duplicated param entries Removes doxygen warning. Fixes #664 Signed-off-by: Marius Vlad --- clients/eventdemo.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/clients/eventdemo.c b/clients/eventdemo.c index 1362db7e..822cb98d 100644 --- a/clients/eventdemo.c +++ b/clients/eventdemo.c @@ -351,8 +351,6 @@ axis_discrete_handler(struct widget *widget, struct input *input, * \param widget widget * \param input input device that caused the motion event * \param time time the event happened - * \param x absolute x position - * \param y absolute y position * \param x x position relative to the window * \param y y position relative to the window * \param data user data associated to the window From 0669d4de4f225533a0559395c3ee923124123865 Mon Sep 17 00:00:00 2001 From: Alexandros Frantzis Date: Thu, 8 Sep 2022 14:05:17 +0300 Subject: [PATCH 570/609] libweston: Skip views without a layer assignment in output_mask calculations Surface views that are not assigned to a layer are not going to be rendered, and thus should not participate in determining the outputs the surface is on. There are other view properties that may determine if the view should be considered in output_mask calculations, e.g., is_mapped, but checking for this currently breaks tests. Such additional checks are left for future fixes or reworkings of the view infrastructure. Fixes #646 Signed-off-by: Alexandros Frantzis --- libweston/compositor.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/libweston/compositor.c b/libweston/compositor.c index 30913cbc..6cfcba25 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -1300,6 +1300,14 @@ weston_view_set_output(struct weston_view *view, struct weston_output *output) } } +static struct weston_layer * +get_view_layer(struct weston_view *view) +{ + if (view->parent_view) + return get_view_layer(view->parent_view); + return view->layer_link.layer; +} + /** Recalculate which output(s) the surface has views displayed on * * \param es The surface to remap to outputs @@ -1325,7 +1333,9 @@ weston_surface_assign_output(struct weston_surface *es) mask = 0; pixman_region32_init(®ion); wl_list_for_each(view, &es->views, surface_link) { - if (!view->output) + /* Only views that are visible on some layer participate in + * output_mask calculations. */ + if (!view->output || !get_view_layer(view)) continue; pixman_region32_intersect(®ion, &view->transform.boundingbox, @@ -1605,14 +1615,6 @@ weston_view_update_transform_enable(struct weston_view *view) return 0; } -static struct weston_layer * -get_view_layer(struct weston_view *view) -{ - if (view->parent_view) - return get_view_layer(view->parent_view); - return view->layer_link.layer; -} - WL_EXPORT void weston_view_update_transform(struct weston_view *view) { From 3dc6a682e4b5788389f5a8c3f93760fb4a278626 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 15 Sep 2022 19:09:49 +0200 Subject: [PATCH 571/609] build: bump to version 10.0.94 for the RC2 release Signed-off-by: Simon Ser --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 66bf6152..86b97b5e 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project('weston', 'c', - version: '10.0.93', + version: '10.0.94', default_options: [ 'warning_level=3', 'c_std=gnu99', From d23a69272f544ee428ebbb597da09a82e084132c Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 22 Sep 2022 18:16:14 +0200 Subject: [PATCH 572/609] build: bump to version 11.0.0 for the official release --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 86b97b5e..c6b73d58 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project('weston', 'c', - version: '10.0.94', + version: '11.0.0', default_options: [ 'warning_level=3', 'c_std=gnu99', From cf1ca2c300769e274892b8d51bcdbc079d45ee87 Mon Sep 17 00:00:00 2001 From: Michael Olbrich Date: Tue, 9 Aug 2022 14:01:29 +0200 Subject: [PATCH 573/609] input: send touch frame event after up event Currently the frame event gets lost: The touch focus is removed in the 'up' event. So the focus is gone when the frame event arrives so it is never sent to the clients. To avoid this, keep the touch focus until the frame is handled. Signed-off-by: Michael Olbrich (cherry picked from commit 5448580111b5ff992ce2603cb6e99b9f54db7ad8) This has undergone a change to avoid an ABI break, so rather than hooking up a pending_touch boolean in weston_touch, keep a local list of weston_touch_devices and have a pending_touch with each device to check upon. Signed-off-by: Marius Vlad --- libweston/input.c | 63 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/libweston/input.c b/libweston/input.c index 6cbb8db0..235cf023 100644 --- a/libweston/input.c +++ b/libweston/input.c @@ -75,6 +75,15 @@ struct border { enum motion_direction blocking_dir; }; +struct pending_touch { + struct weston_touch_device *touch_device; + bool pending_focus_touch_reset; + + struct wl_list touch_link; +}; + +static struct wl_list pending_touch_list; + static void maybe_warp_confined_pointer(struct weston_pointer_constraint *constraint); @@ -143,6 +152,7 @@ weston_touch_create_touch_device(struct weston_touch *touch, const struct weston_touch_device_ops *ops) { struct weston_touch_device *device; + struct pending_touch *pt; assert(syspath); if (ops) { @@ -164,20 +174,55 @@ weston_touch_create_touch_device(struct weston_touch *touch, return NULL; } + pt = zalloc(sizeof(*pt)); + if (!pt) { + free(device); + return NULL; + } + + device->backend_data = backend_data; device->ops = ops; + pt->touch_device = device; + pt->pending_focus_touch_reset = false; + device->aggregate = touch; wl_list_insert(touch->device_list.prev, &device->link); + wl_list_insert(&pending_touch_list, &pt->touch_link); return device; } +static struct pending_touch * +weston_touch_get_pending(struct weston_touch_device *dev) +{ + struct pending_touch *pt_iter = NULL; + struct pending_touch *pt = NULL; + + wl_list_for_each(pt_iter, &pending_touch_list, touch_link) { + if (pt_iter->touch_device == dev) { + pt = pt_iter; + break; + } + } + + return pt; +} + /** Destroy the touch device. */ WL_EXPORT void weston_touch_device_destroy(struct weston_touch_device *device) { + struct pending_touch *pt = NULL; + wl_list_remove(&device->link); + pt = weston_touch_get_pending(device); + + assert(pt); + wl_list_remove(&pt->touch_link); + free(pt); + wl_signal_emit(&device->destroy_signal, device); free(device->syspath); free(device); @@ -2388,6 +2433,7 @@ process_touch_normal(struct weston_touch_device *device, struct weston_touch_grab *grab = device->aggregate->grab; struct weston_compositor *ec = device->aggregate->seat->compositor; struct weston_view *ev; + struct pending_touch *pt = NULL; wl_fixed_t sx, sy; wl_fixed_t x = wl_fixed_from_double(double_x); wl_fixed_t y = wl_fixed_from_double(double_y); @@ -2438,8 +2484,9 @@ process_touch_normal(struct weston_touch_device *device, break; case WL_TOUCH_UP: grab->interface->up(grab, time, touch_id); - if (touch->num_tp == 0) - weston_touch_set_focus(touch, NULL); + pt = weston_touch_get_pending(device); + assert(pt); + pt->pending_focus_touch_reset = true; break; } } @@ -2628,12 +2675,22 @@ WL_EXPORT void notify_touch_frame(struct weston_touch_device *device) { struct weston_touch_grab *grab; + struct pending_touch *pt; switch (weston_touch_device_get_mode(device)) { case WESTON_TOUCH_MODE_NORMAL: case WESTON_TOUCH_MODE_PREP_CALIB: grab = device->aggregate->grab; grab->interface->frame(grab); + + pt = weston_touch_get_pending(device); + assert(pt); + + if (pt->pending_focus_touch_reset) { + if (grab->touch->num_tp == 0) + weston_touch_set_focus(grab->touch, NULL); + pt->pending_focus_touch_reset = false; + } break; case WESTON_TOUCH_MODE_CALIB: case WESTON_TOUCH_MODE_PREP_NORMAL: @@ -5059,6 +5116,8 @@ bind_input_timestamps_manager(struct wl_client *client, void *data, int weston_input_init(struct weston_compositor *compositor) { + wl_list_init(&pending_touch_list); + if (!wl_global_create(compositor->wl_display, &zwp_relative_pointer_manager_v1_interface, 1, compositor, bind_relative_pointer_manager)) From 870db9703cc2ad75194fca16e937e1d18ae72c2f Mon Sep 17 00:00:00 2001 From: Michael Olbrich Date: Wed, 10 Aug 2022 10:57:13 +0200 Subject: [PATCH 574/609] backend-wayland: always propagate touch frame event Signed-off-by: Michael Olbrich (cherry picked from commit 631b60b38bf03a41515c4cdc9294f5b21ca719a5) --- libweston/backend-wayland/wayland.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libweston/backend-wayland/wayland.c b/libweston/backend-wayland/wayland.c index a5e0183a..bb5648d5 100644 --- a/libweston/backend-wayland/wayland.c +++ b/libweston/backend-wayland/wayland.c @@ -2162,10 +2162,6 @@ input_handle_touch_up(void *data, struct wl_touch *wl_touch, timespec_from_msec(&ts, time); input->touch_points--; - if (input->touch_points == 0) { - input->touch_focus = NULL; - input->touch_active = false; - } if (!output) return; @@ -2228,6 +2224,11 @@ input_handle_touch_frame(void *data, struct wl_touch *wl_touch) return; notify_touch_frame(input->touch_device); + + if (input->touch_points == 0) { + input->touch_focus = NULL; + input->touch_active = false; + } } static void From 24ee61445c70b62f73988c6a90aed129ea830c9e Mon Sep 17 00:00:00 2001 From: vanfanel Date: Wed, 7 Sep 2022 16:34:17 +0100 Subject: [PATCH 575/609] Don't change the max_bpc connector prop if mode=current. As things are, even when mode=current is specified on the .ini file, a full modeset is needed (and done), which causes a very noticeable screen blinking. That is because setting the max_bpc on a connector needs full modesetting. The idea here is that if mode=current on the .ini, no modesetting should be done, so the current max_bpc is programmed into the connector. But if a custom max-bpc=... is specified, that will be used instead, even if mode=current on the .ini Fixes: https://gitlab.freedesktop.org/wayland/weston/-/issues/660 Signed-off-by: vanfanel (cherry picked from commit 3240ccc69d1488003c1cfc36d23750145d4f13f7) --- compositor/main.c | 14 ++++++++++---- include/libweston/backend-drm.h | 3 ++- libweston/backend-drm/drm-internal.h | 1 + libweston/backend-drm/drm.c | 6 ++++++ libweston/backend-drm/kms.c | 23 ++++++++++++++++------- man/weston-drm.man | 2 +- 6 files changed, 36 insertions(+), 13 deletions(-) diff --git a/compositor/main.c b/compositor/main.c index 2eb0a03c..73f70b22 100644 --- a/compositor/main.c +++ b/compositor/main.c @@ -2051,7 +2051,8 @@ drm_backend_output_configure(struct weston_output *output, enum weston_drm_backend_output_mode mode = WESTON_DRM_BACKEND_OUTPUT_PREFERRED; uint32_t transform = WL_OUTPUT_TRANSFORM_NORMAL; - uint32_t max_bpc; + uint32_t max_bpc = 0; + bool max_bpc_specified = false; char *s; char *modeline = NULL; char *gbm_format = NULL; @@ -2063,16 +2064,19 @@ drm_backend_output_configure(struct weston_output *output, return -1; } - weston_config_section_get_uint(section, "max-bpc", &max_bpc, 16); - api->set_max_bpc(output, max_bpc); - weston_config_section_get_string(section, "mode", &s, "preferred"); + if (weston_config_section_get_uint(section, "max-bpc", &max_bpc, 16) == 0) + max_bpc_specified = true; if (strcmp(s, "off") == 0) { assert(0 && "off was supposed to be pruned"); return -1; } else if (wet->drm_use_current_mode || strcmp(s, "current") == 0) { mode = WESTON_DRM_BACKEND_OUTPUT_CURRENT; + /* If mode=current and no max-bpc was specfied on the .ini file, + use current max_bpc so full modeset is not done. */ + if (!max_bpc_specified) + max_bpc = 0; } else if (strcmp(s, "preferred") != 0) { modeline = s; s = NULL; @@ -2086,6 +2090,8 @@ drm_backend_output_configure(struct weston_output *output, } free(modeline); + api->set_max_bpc(output, max_bpc); + if (count_remaining_heads(output, NULL) == 1) { struct weston_head *head = weston_output_get_first_head(output); transform = weston_head_get_transform(head); diff --git a/include/libweston/backend-drm.h b/include/libweston/backend-drm.h index 548940ab..071125fa 100644 --- a/include/libweston/backend-drm.h +++ b/include/libweston/backend-drm.h @@ -84,7 +84,8 @@ struct weston_drm_output_api { * The property is used for working around faulty sink hardware like * monitors or media converters that mishandle the kernel driver * chosen bits-per-channel on the physical link. When having trouble, - * try a lower value like 8. + * try a lower value like 8. A value of 0 means that the current max + * bpc will be reprogrammed. * * The value actually used in KMS is silently clamped to the range the * KMS driver claims to support. The default value is 16. diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index f6c1617f..1ee1974c 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -517,6 +517,7 @@ struct drm_head { struct backlight *backlight; drmModeModeInfo inherited_mode; /**< Original mode on the connector */ + uint32_t inherited_max_bpc; /**< Original max_bpc on the connector */ uint32_t inherited_crtc_id; /**< Original CRTC assignment */ }; diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index 962cc284..7d607ca6 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -1388,6 +1388,12 @@ drm_head_read_current_setup(struct drm_head *head, struct drm_device *device) drmModeFreeCrtc(crtc); } + /* Get the current max_bpc that's currently configured to + * this connector. */ + head->inherited_max_bpc = drm_property_get_value( + &head->connector.props[WDRM_CONNECTOR_MAX_BPC], + head->connector.props_drm, 0); + return 0; } diff --git a/libweston/backend-drm/kms.c b/libweston/backend-drm/kms.c index 54010b97..0118efa1 100644 --- a/libweston/backend-drm/kms.c +++ b/libweston/backend-drm/kms.c @@ -912,20 +912,29 @@ drm_connector_set_max_bpc(struct drm_connector *connector, drmModeAtomicReq *req) { const struct drm_property_info *info; + struct drm_head *head; + struct drm_backend *backend = output->device->backend; uint64_t max_bpc; uint64_t a, b; if (!drm_connector_has_prop(connector, WDRM_CONNECTOR_MAX_BPC)) return 0; - info = &connector->props[WDRM_CONNECTOR_MAX_BPC]; - assert(info->flags & DRM_MODE_PROP_RANGE); - assert(info->num_range_values == 2); - a = info->range_values[0]; - b = info->range_values[1]; - assert(a <= b); + if (output->max_bpc == 0) { + /* A value of 0 means that the current max_bpc must be programmed. */ + head = drm_head_find_by_connector(backend, connector->connector_id); + max_bpc = head->inherited_max_bpc; + } else { + info = &connector->props[WDRM_CONNECTOR_MAX_BPC]; + assert(info->flags & DRM_MODE_PROP_RANGE); + assert(info->num_range_values == 2); + a = info->range_values[0]; + b = info->range_values[1]; + assert(a <= b); + + max_bpc = MAX(a, MIN(output->max_bpc, b)); + } - max_bpc = MAX(a, MIN(output->max_bpc, b)); return connector_add_prop(req, connector, WDRM_CONNECTOR_MAX_BPC, max_bpc); } diff --git a/man/weston-drm.man b/man/weston-drm.man index eaaf311c..383715d3 100644 --- a/man/weston-drm.man +++ b/man/weston-drm.man @@ -172,7 +172,7 @@ silenty clamped to the hardware driver supported range. This artificially limits the driver chosen link bits-per-channel which may be useful for working around sink hardware (e.g. monitor) limitations. The default is 16 which is practically unlimited. If you need to work around hardware issues, try a lower -value like 8. +value like 8. A value of 0 means that the current max bpc will be reprogrammed. .SS Section remote-output .TP From 0da83cc1d82e49119214fdcfd1bc164de709771c Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Fri, 16 Sep 2022 15:58:18 +0300 Subject: [PATCH 576/609] doc/sphinx: Make doxygen warn as error depend on meson werror flag As seen previous times, with newer doxygen version we seem to be generating warnings and with it to stop generating documentation entirely as we have enabled warning as error in the doxygen configuration file. By default meson werror build option is not enabled, so users can still generate documentation when building regularly, and when we'll update to the same doxygen version we should be able to catch those errors up if any of them will pile up in between. Suggested-by: Pekka Paalanen Signed-off-by: Marius Vlad (cherry picked from commit 402d9a81c9a42b0dcfc457c80382c36f5cecc6e6) --- doc/sphinx/doxygen.ini.in | 2 +- doc/sphinx/meson.build | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/sphinx/doxygen.ini.in b/doc/sphinx/doxygen.ini.in index 1e71d07b..29f57882 100644 --- a/doc/sphinx/doxygen.ini.in +++ b/doc/sphinx/doxygen.ini.in @@ -759,7 +759,7 @@ WARN_NO_PARAMDOC = NO # a warning is encountered. # The default value is: NO. -WARN_AS_ERROR = YES +WARN_AS_ERROR = @MESON_WERROR@ # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which diff --git a/doc/sphinx/meson.build b/doc/sphinx/meson.build index 46947a6b..1f573624 100644 --- a/doc/sphinx/meson.build +++ b/doc/sphinx/meson.build @@ -39,6 +39,7 @@ sphinx_conf = configure_file( doxy_conf_data = configuration_data() doxy_conf_data.set('SRC_ROOT', meson.source_root()) doxy_conf_data.set('OUTPUT_DIR', doxygen_database) +doxy_conf_data.set('MESON_WERROR', get_option('werror') == true ? 'YES' : 'NO') doxygen_conf_weston = configure_file( input: 'doxygen.ini.in', output: 'doxygen.ini', From 715eb67cd8cc80dd7e828624493e0c6edf936d75 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Wed, 21 Sep 2022 10:54:09 +0300 Subject: [PATCH 577/609] backend-rdp/rdpclip: Avoid printing negative index MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As clipboard_find_supported_format_by_mime_type() can return -1 if it can't find out an index avoid trying to print outside of the array. Fixes the following warnings triggered when enabling FORTIFY_SOURCE combined with optimizations (-O) ../libweston/backend-rdp/rdpclip.c:1114:17: error: array subscript -1 is below array bounds of ‘uint32_t[5]’ {aka ‘unsigned int[5]’} [-Werror=array-bounds] 1114 | weston_log("RDP %s (%p:%s) specified format \"%s\" index:%d formatId:%d is not supported by client\n", | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1115 | __func__, source, clipboard_data_source_state_to_string(source), | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1116 | mime_type, index, source->client_format_id_table[index]); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ../libweston/backend-rdp/rdpclip.c:131:18: note: while referencing ‘client_format_id_table’ 131 | uint32_t client_format_id_table[RDP_NUM_CLIPBOARD_FORMATS]; Signed-off-by: Marius Vlad (cherry picked from commit 9455ad7c7c07fdb8218330f449c97be73f695742) --- libweston/backend-rdp/rdpclip.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libweston/backend-rdp/rdpclip.c b/libweston/backend-rdp/rdpclip.c index f92bf7a0..e08f4ca2 100644 --- a/libweston/backend-rdp/rdpclip.c +++ b/libweston/backend-rdp/rdpclip.c @@ -1111,9 +1111,9 @@ clipboard_data_source_send(struct weston_data_source *base, } } else { source->state = RDP_CLIPBOARD_SOURCE_FAILED; - weston_log("RDP %s (%p:%s) specified format \"%s\" index:%d formatId:%d is not supported by client\n", + weston_log("RDP %s (%p:%s) specified format \"%s\" index:%d is not supported by client\n", __func__, source, clipboard_data_source_state_to_string(source), - mime_type, index, source->client_format_id_table[index]); + mime_type, index); goto error_return_close_fd; } From 00a78294b1fff64bfe5eb8d4837e0c253db33850 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Wed, 21 Sep 2022 11:00:21 +0300 Subject: [PATCH 578/609] compositor/shared: Suppress write(2) warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes the following warnings when building with _FORTIFY_SOURCE and optimizations enabled: ../shared/xalloc.h:49:9: error: ignoring return value of ‘write’ declared with attribute ‘warn_unused_result’ [-Werror=unused-result] 49 | write(STDERR_FILENO, oommsg, strlen(oommsg)); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ or ../compositor/main.c:427:25: error: ignoring return value of ‘write’ declared with attribute ‘warn_unused_result’ [-Werror=unused-result] 427 | write(STDERR_FILENO, fail_seteuid, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 428 | strlen(fail_seteuid)); | ~~~~~~~~~~~~~~~~~~~~~ ../compositor/main.c:434:25: error: ignoring return value of ‘write’ declared with attribute ‘warn_unused_result’ [-Werror=unused-result] 434 | write(STDERR_FILENO, fail_cloexec, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 435 | strlen(fail_cloexec)); | ~~~~~~~~~~~~~~~~~~~~~ ../compositor/main.c:442:25: error: ignoring return value of ‘write’ declared with attribute ‘warn_unused_result’ [-Werror=unused-result] 442 | write(STDERR_FILENO, fail_exec, strlen(fail_exec)); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Marius Vlad (cherry picked from commit 8c4cdd782e17aa587bfccb6746998571ddc90dd7) --- compositor/main.c | 12 +++++++----- compositor/xwayland.c | 5 +++-- shared/xalloc.h | 7 ++++--- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/compositor/main.c b/compositor/main.c index 73f70b22..15f9d4e1 100644 --- a/compositor/main.c +++ b/compositor/main.c @@ -385,6 +385,7 @@ weston_client_launch(struct weston_compositor *compositor, sigset_t allsigs; pid_t pid; bool ret; + size_t written __attribute__((unused)); weston_log("launching '%s'\n", path); str_printf(&fail_exec, "Error: Couldn't launch client '%s'\n", path); @@ -424,22 +425,23 @@ weston_client_launch(struct weston_compositor *compositor, /* Launch clients as the user. Do not launch clients with wrong euid. */ if (seteuid(getuid()) == -1) { - write(STDERR_FILENO, fail_seteuid, - strlen(fail_seteuid)); + written = write(STDERR_FILENO, fail_seteuid, + strlen(fail_seteuid)); _exit(EXIT_FAILURE); } ret = fdstr_clear_cloexec_fd1(&wayland_socket); if (!ret) { - write(STDERR_FILENO, fail_cloexec, - strlen(fail_cloexec)); + written = write(STDERR_FILENO, fail_cloexec, + strlen(fail_cloexec)); _exit(EXIT_FAILURE); } execve(argp[0], argp, envp); if (fail_exec) - write(STDERR_FILENO, fail_exec, strlen(fail_exec)); + written = write(STDERR_FILENO, fail_exec, + strlen(fail_exec)); _exit(EXIT_FAILURE); default: diff --git a/compositor/xwayland.c b/compositor/xwayland.c index b25b74c9..ea1ae1ef 100644 --- a/compositor/xwayland.c +++ b/compositor/xwayland.c @@ -110,6 +110,7 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd char *const *envp; char *const *argp; bool ret; + size_t written __attribute__ ((unused)); if (os_socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0, wayland_socket.fds) < 0) { weston_log("wl connection socketpair failed\n"); @@ -174,8 +175,8 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd /* execve does not return on success, so it failed */ if (exec_failure_msg) { - write(STDERR_FILENO, exec_failure_msg, - strlen(exec_failure_msg)); + written = write(STDERR_FILENO, exec_failure_msg, + strlen(exec_failure_msg)); } _exit(EXIT_FAILURE); diff --git a/shared/xalloc.h b/shared/xalloc.h index 2bbb426e..a2747f9c 100644 --- a/shared/xalloc.h +++ b/shared/xalloc.h @@ -40,13 +40,14 @@ static inline void * abort_oom_if_null(void *p) { static const char oommsg[] = ": out of memory\n"; + size_t written __attribute__((unused)); if (p) return p; - write(STDERR_FILENO, program_invocation_short_name, - strlen(program_invocation_short_name)); - write(STDERR_FILENO, oommsg, strlen(oommsg)); + written = write(STDERR_FILENO, program_invocation_short_name, + strlen(program_invocation_short_name)); + written = write(STDERR_FILENO, oommsg, strlen(oommsg)); abort(); } From 5517953ed01f38a7eb8506ba7b48252a83e44bb2 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Wed, 21 Sep 2022 14:15:30 -0500 Subject: [PATCH 579/609] xwm: Check size hints in weston_wm_window_is_positioned() Currently we can't tell the difference between a window intentionally created at 0,0 and a window that we can place anywhere. Check the size hints to see if the flags indicating the placement is intentional are present. Signed-off-by: Derek Foreman (cherry picked from commit 1cb46994e3808e8000300ed9ae9dcaa0b76bff28) --- xwayland/window-manager.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/xwayland/window-manager.c b/xwayland/window-manager.c index f0655a7f..9dc30e1c 100644 --- a/xwayland/window-manager.c +++ b/xwayland/window-manager.c @@ -2937,6 +2937,9 @@ weston_wm_window_is_positioned(struct weston_wm_window *window) weston_log("XWM warning: win %d did not see map request\n", window->id); + if (window->size_hints.flags & (USPosition | PPosition)) + return true; + return window->map_request_x != 0 || window->map_request_y != 0; } From 7678ec9209883600553596160b80f7c8716a5723 Mon Sep 17 00:00:00 2001 From: Paul Kocialkowski Date: Thu, 29 Sep 2022 13:29:53 +0200 Subject: [PATCH 580/609] screenshooter: Add SHM buffer destroy listener to avoid invalid memcpy This adds a destroy listener on the SHM buffer provided by our client. It will unregister the frame notify listener in case our buffer is destroyed before the frame signal is emitted and thus avoid a memcpy to invalid memory. Signed-off-by: Paul Kocialkowski (cherry picked from commit 0afd3428dc899c426d37650192f828541f70e390) --- libweston/screenshooter.c | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/libweston/screenshooter.c b/libweston/screenshooter.c index b866829b..aaa8de5c 100644 --- a/libweston/screenshooter.c +++ b/libweston/screenshooter.c @@ -45,7 +45,8 @@ #include "wcap/wcap-decode.h" struct screenshooter_frame_listener { - struct wl_listener listener; + struct wl_listener frame_listener; + struct wl_listener buffer_destroy_listener; struct weston_buffer *buffer; struct weston_output *output; weston_screenshooter_done_func_t done; @@ -120,7 +121,8 @@ screenshooter_frame_notify(struct wl_listener *listener, void *data) { struct screenshooter_frame_listener *l = container_of(listener, - struct screenshooter_frame_listener, listener); + struct screenshooter_frame_listener, + frame_listener); struct weston_output *output = l->output; struct weston_compositor *compositor = output->compositor; const pixman_format_code_t pixman_format = @@ -130,6 +132,8 @@ screenshooter_frame_notify(struct wl_listener *listener, void *data) weston_output_disable_planes_decr(output); wl_list_remove(&listener->link); + wl_list_remove(&l->buffer_destroy_listener.link); + stride = l->buffer->width * (PIXMAN_FORMAT_BPP(pixman_format) / 8); pixels = malloc(stride * l->buffer->height); @@ -177,6 +181,22 @@ screenshooter_frame_notify(struct wl_listener *listener, void *data) free(l); } +static void +buffer_destroy_handle(struct wl_listener *listener, void *data) +{ + struct screenshooter_frame_listener *l = + container_of(listener, + struct screenshooter_frame_listener, + buffer_destroy_listener); + + weston_output_disable_planes_decr(l->output); + wl_list_remove(&listener->link); + wl_list_remove(&l->frame_listener.link); + l->done(l->data, WESTON_SCREENSHOOTER_BAD_BUFFER); + + free(l); +} + WL_EXPORT int weston_screenshooter_shoot(struct weston_output *output, struct weston_buffer *buffer, @@ -205,8 +225,13 @@ weston_screenshooter_shoot(struct weston_output *output, l->output = output; l->done = done; l->data = data; - l->listener.notify = screenshooter_frame_notify; - wl_signal_add(&output->frame_signal, &l->listener); + + l->frame_listener.notify = screenshooter_frame_notify; + wl_signal_add(&output->frame_signal, &l->frame_listener); + + l->buffer_destroy_listener.notify = buffer_destroy_handle; + wl_signal_add(&buffer->destroy_signal, &l->buffer_destroy_listener); + weston_output_disable_planes_incr(output); weston_output_schedule_repaint(output); From 7a8392d2fe702880eaf33d161652bdddcc998d74 Mon Sep 17 00:00:00 2001 From: Alexandros Frantzis Date: Mon, 3 Oct 2022 12:08:54 +0300 Subject: [PATCH 581/609] kiosk-shell: Update view transform after activation. The activation of a view implies, among other things, a change in the associated view layer which is initially unset. In order for this change to be reflected in the corresponding surface's output mask, and hence allow surface damage to trigger output repaints, we need to update the view transform. Fixes #674 Signed-off-by: Alexandros Frantzis (cherry picked from commit 341d09d232d652c0001441cce55beb874fb3ba36) --- kiosk-shell/kiosk-shell.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kiosk-shell/kiosk-shell.c b/kiosk-shell/kiosk-shell.c index 3d00baf4..79dd6201 100644 --- a/kiosk-shell/kiosk-shell.c +++ b/kiosk-shell/kiosk-shell.c @@ -413,6 +413,7 @@ kiosk_shell_surface_activate(struct kiosk_shell_surface *shsurf, weston_layer_entry_insert(&shsurf->shell->normal_layer.view_list, &shsurf->view->layer_link); weston_view_geometry_dirty(shsurf->view); + weston_view_update_transform(shsurf->view); weston_surface_damage(shsurf->view->surface); } From 72a692946755ef50ef9ca64369a833c89c738361 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Wed, 28 Sep 2022 12:17:29 +0200 Subject: [PATCH 582/609] ivi-shell: fix free in get_layers_under_surface If a controller requests the layers under a surface that has no views attached, Weston crashes since it tries to free the array that would be used to return the found layers, but has not been allocated before. Free the ppArray only if it was allocated in ivi_layout_get_layers_under_surface before and no layers were found. While at it, make it obvious that checking the length is an integer comparison by comparing it to 0. Signed-off-by: Michael Tretter (cherry picked from commit c56e69bc850540f243ebb87c5bcc38713ef1862a) --- ivi-shell/ivi-layout.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ivi-shell/ivi-layout.c b/ivi-shell/ivi-layout.c index 1c42d636..9f861a38 100644 --- a/ivi-shell/ivi-layout.c +++ b/ivi-shell/ivi-layout.c @@ -1192,15 +1192,14 @@ ivi_layout_get_layers_under_surface(struct ivi_layout_surface *ivisurf, else length--; } + if (length == 0) { + free(*ppArray); + *ppArray = NULL; + } } *pLength = length; - if (!length) { - free(*ppArray); - *ppArray = NULL; - } - return IVI_SUCCEEDED; } From e7cf894fa249142271c3e103044d22a9b8e6fa09 Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Tue, 18 Oct 2022 15:24:04 +0200 Subject: [PATCH 583/609] ivi-shell: fix cleanup of desktop surfaces The ivi-shell keeps track of its surfaces by adding them to the ivi_surface_list to be able to remove them on shutdown. It also creates an ivi_layout_surface for a desktop surface, but does not keep track of these surfaces. During compositor shutdown, libweston prints the following message: BUG: finalizing a layer with views still on it. Fix it by adding the created ivi_layout_surface to the ivi_surface_list to remove the surfaces from the layer during shutdown. Furthermore, remove the ivi_layout_surface from the desktop surface and free it when the desktop surface is destroyed. Signed-off-by: Michael Tretter (cherry picked from commit 266e2e1d4866ef7c39cff0b77f1e404d0dc96b55) Resolved small conflict which arose due to commit cbf476f208fbdcefe. Signed-off-by: Marius Vlad --- ivi-shell/ivi-shell.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ivi-shell/ivi-shell.c b/ivi-shell/ivi-shell.c index eb6bbbe7..0c80c006 100644 --- a/ivi-shell/ivi-shell.c +++ b/ivi-shell/ivi-shell.c @@ -507,6 +507,8 @@ desktop_surface_added(struct weston_desktop_surface *surface, ivisurf->layout_surface = layout_surface; ivisurf->surface = weston_surf; + wl_list_insert(&shell->ivi_surface_list, &ivisurf->link); + weston_desktop_surface_set_user_data(surface, ivisurf); } @@ -519,8 +521,14 @@ desktop_surface_removed(struct weston_desktop_surface *surface, assert(ivisurf != NULL); + weston_desktop_surface_set_user_data(surface, NULL); + if (ivisurf->layout_surface) layout_surface_cleanup(ivisurf); + + wl_list_remove(&ivisurf->link); + + free(ivisurf); } static void From ad7c5162bc2647c35e2f03c87000f6857b2ee25d Mon Sep 17 00:00:00 2001 From: Alexandros Frantzis Date: Fri, 21 Oct 2022 15:04:01 +0300 Subject: [PATCH 584/609] kiosk-shell: Don't use a modifier for surface activation bindings The mouse button and touch bindings to activate a surface shouldn't use the binding modifier. Fixes: https://gitlab.freedesktop.org/wayland/weston/-/issues/679 Signed-off-by: Alexandros Frantzis (cherry picked from commit 723709aa073fb740c3443fae8e370306d0738675) --- kiosk-shell/kiosk-shell.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kiosk-shell/kiosk-shell.c b/kiosk-shell/kiosk-shell.c index 79dd6201..ceff193e 100644 --- a/kiosk-shell/kiosk-shell.c +++ b/kiosk-shell/kiosk-shell.c @@ -1069,13 +1069,13 @@ kiosk_shell_add_bindings(struct kiosk_shell *shell) mod = weston_shell_get_binding_modifier(shell->config, MODIFIER_SUPER); - weston_compositor_add_button_binding(shell->compositor, BTN_LEFT, mod, + weston_compositor_add_button_binding(shell->compositor, BTN_LEFT, 0, kiosk_shell_click_to_activate_binding, shell); - weston_compositor_add_button_binding(shell->compositor, BTN_RIGHT, mod, + weston_compositor_add_button_binding(shell->compositor, BTN_RIGHT, 0, kiosk_shell_click_to_activate_binding, shell); - weston_compositor_add_touch_binding(shell->compositor, mod, + weston_compositor_add_touch_binding(shell->compositor, 0, kiosk_shell_touch_to_activate_binding, shell); From 097ed472920e01e2a4776e13c1e755265d8ec1f7 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Fri, 4 Nov 2022 12:38:19 +0200 Subject: [PATCH 585/609] hmi-controller: Add missing removal of destroy listener Shutting down the compositor gives us: Invalid write of size 8 at 0x4B1AEDB: wl_list_remove (wayland-util.c:56) by 0x4AF05BF: weston_signal_emit_mutable (signal.c:66) by 0x4ACBC2C: weston_compositor_destroy (compositor.c:8629) by 0x4864A4B: wet_main (main.c:3908) by 0x10915D: main (executable.c:33) Address 0x17435f20 is 224 bytes inside a block of size 384 free'd at 0x484617B: free (vg_replace_malloc.c:872) by 0x17718C7E: hmi_controller_destroy (hmi-controller.c:761) by 0x4AF059A: weston_signal_emit_mutable (signal.c:62) by 0x4ACBC2C: weston_compositor_destroy (compositor.c:8629) by 0x4864A4B: wet_main (main.c:3908) by 0x10915D: main (executable.c:33) Signed-off-by: Marius Vlad (cherry picked from commit cfbf2b0ab2ac7c50d8a1ea7645be3a9f3927825b) --- ivi-shell/hmi-controller.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ivi-shell/hmi-controller.c b/ivi-shell/hmi-controller.c index 8ae2230b..a1822762 100644 --- a/ivi-shell/hmi-controller.c +++ b/ivi-shell/hmi-controller.c @@ -738,6 +738,8 @@ hmi_controller_destroy(struct wl_listener *listener, void *data) struct hmi_controller *hmi_ctrl = container_of(listener, struct hmi_controller, destroy_listener); + wl_list_remove(&hmi_ctrl->destroy_listener.link); + wl_list_for_each_safe(link, next, &hmi_ctrl->workspace_fade.layer_list, link) { wl_list_remove(&link->link); From ac059500986c021520f92ee1026ea556bce611f3 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Fri, 4 Nov 2022 13:26:40 +0200 Subject: [PATCH 586/609] ivi-shell: Move out weston_desktop_shell at the end To avoid the following UAF: Invalid read of size 8 at 0x4AE5EFF: weston_desktop_get_display (libweston-desktop.c:110) by 0x4AEB2C9: weston_desktop_xdg_surface_schedule_configure (xdg-shell.c:1160) by 0x4AEA77A: weston_desktop_xdg_toplevel_set_size (xdg-shell.c:711) by 0x4AE839D: weston_desktop_surface_set_size (surface.c:504) by 0x63F7D43: ivi_layout_surface_set_size (ivi-layout.c:1599) by 0x63F949F: transition_move_resize_view_destroy (ivi-layout-transition.c:311) by 0x63F9397: layout_transition_destroy (ivi-layout-transition.c:259) by 0x63F8E0B: ivi_layout_remove_all_surface_transitions (ivi-layout-transition.c:121) by 0x63F4BC1: ivi_layout_surface_destroy (ivi-layout.c:258) by 0x63F38AF: layout_surface_cleanup (ivi-shell.c:162) by 0x63F3D2D: shell_destroy (ivi-shell.c:359) by 0x4AF059A: weston_signal_emit_mutable (signal.c:62) Address 0x174202d0 is 0 bytes inside a block of size 152 free'd at 0x484617B: free (vg_replace_malloc.c:872) by 0x4AE5EDC: weston_desktop_destroy (libweston-desktop.c:97) by 0x63F3CF2: shell_destroy (ivi-shell.c:355) by 0x4AF059A: weston_signal_emit_mutable (signal.c:62) by 0x4ACBC2C: weston_compositor_destroy (compositor.c:8629) by 0x4864A4B: wet_main (main.c:3908) by 0x10915D: main (executable.c:33) Signed-off-by: Marius Vlad (cherry picked from commit eb755cd81aad6f958629475a0429272aef3147b0) --- ivi-shell/ivi-shell.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ivi-shell/ivi-shell.c b/ivi-shell/ivi-shell.c index 0c80c006..79ff2191 100644 --- a/ivi-shell/ivi-shell.c +++ b/ivi-shell/ivi-shell.c @@ -346,7 +346,6 @@ shell_destroy(struct wl_listener *listener, void *data) wl_list_remove(&shell->destroy_listener.link); wl_list_remove(&shell->wake_listener.link); - weston_desktop_destroy(shell->desktop); wl_list_for_each_safe(ivisurf, next, &shell->ivi_surface_list, link) { if (ivisurf->layout_surface != NULL) @@ -357,6 +356,7 @@ shell_destroy(struct wl_listener *listener, void *data) ivi_layout_fini(); + weston_desktop_destroy(shell->desktop); free(shell); } From f5fafa05fcf582530f58d2aabdae1a4c6cf64083 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Thu, 1 Dec 2022 15:30:40 -0600 Subject: [PATCH 587/609] xwm: Don't crash when setting selection with no seat It's possible to set the clipboard with no seat present - one way is to use the RDP backend and then run 'xclip -i -selection clipboard' locally without making an RDP connection. Check if seat is NULL to prevent this from crashing. Fixes #698 Signed-off-by: Derek Foreman (cherry picked from commit bb993df236766178df95579217171bce5b166031) --- xwayland/selection.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/xwayland/selection.c b/xwayland/selection.c index 0e4120ac..e12d2acf 100644 --- a/xwayland/selection.c +++ b/xwayland/selection.c @@ -199,6 +199,9 @@ weston_wm_get_selection_targets(struct weston_wm *wm) char *logstr; size_t logsize; + if (!seat) + return; + cookie = xcb_get_property(wm->conn, 1, /* delete */ wm->selection_window, @@ -631,6 +634,9 @@ weston_wm_handle_xfixes_selection_notify(struct weston_wm *wm, xfixes_selection_notify->owner); if (xfixes_selection_notify->owner == XCB_WINDOW_NONE) { + if (!seat) + return 1; + if (wm->selection_owner != wm->selection_window) { /* A real X client selection went away, not our * proxy selection. Clear the wayland selection. */ From 995fb60fdeec169ef284798c9fa024c056cca6ba Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Thu, 1 Dec 2022 15:38:24 -0600 Subject: [PATCH 588/609] xwm: Propagate selection ownership immediately If we don't xcb_flush() when we set the selection owner, we end up with a ridiculous corner case where we can run use a command line X client like 'xclip -i -selection clipboard' to crash weston. Start weston, ensure Xwayland is running (set a selection with xclip), set the clipboard from a wayland client, then set the clipboard with xclip again. Since xclip doesn't do anything xwm notices except set the clipboard, it won't provoke a flush on our selection ownership change. xclip will take ownership, then we call xcb_convert_selection(), and THEN we flush, sending out our pending ownership change and the xcb_convert_selection() request. The ownership change takes place first, we attempt to get our own selection and weston explodes in a mess. Stop this from happening with a flush when changing selection ownership. Signed-off-by: Derek Foreman (cherry picked from commit 11bcad116f5cc1eb76c2de83d8c39af0cdb71a81) --- xwayland/selection.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/xwayland/selection.c b/xwayland/selection.c index e12d2acf..6476c0d4 100644 --- a/xwayland/selection.c +++ b/xwayland/selection.c @@ -720,6 +720,8 @@ weston_wm_set_selection(struct wl_listener *listener, void *data) wm->selection_window, wm->atom.clipboard, XCB_TIME_CURRENT_TIME); + + xcb_flush(wm->conn); } void From 783b144f2e1beff5848e6c76ab765c3ed32d5b77 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Wed, 14 Dec 2022 09:09:18 +0200 Subject: [PATCH 589/609] build: bump to version 11.0.1 for the point release Signed-off-by: Marius Vlad --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index c6b73d58..82119aca 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project('weston', 'c', - version: '11.0.0', + version: '11.0.1', default_options: [ 'warning_level=3', 'c_std=gnu99', From 0ba5b694d38a343191de3a65c59e62b281d83125 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Thu, 16 Feb 2023 17:47:21 +0200 Subject: [PATCH 590/609] remoting-plugin: Release and detach the head This re-orders the disable/destroy shutdown sequence such that lookup_remoted_output(), in remoting_output_disable(), would find a remoting output. Otherwise, without this, lookup_remoted_output() wouldn't find a remoting output available when shutting down the compositor, ultimately leading to a crash. Signed-off-by: Marius Vlad (cherry picked from commit c3270e887bc07bb9c7d0e58e25d25e3a65145d2e) --- remoting/remoting-plugin.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/remoting/remoting-plugin.c b/remoting/remoting-plugin.c index 0af43b5b..aabcd597 100644 --- a/remoting/remoting-plugin.c +++ b/remoting/remoting-plugin.c @@ -636,6 +636,8 @@ remoting_output_destroy(struct weston_output *output) struct remoted_output *remoted_output = lookup_remoted_output(output); struct weston_mode *mode, *next; + weston_head_release(remoted_output->head); + wl_list_for_each_safe(mode, next, &output->mode_list, link) { wl_list_remove(&mode->link); free(mode); @@ -650,7 +652,6 @@ remoting_output_destroy(struct weston_output *output) free(remoted_output->gst_pipeline); wl_list_remove(&remoted_output->link); - weston_head_release(remoted_output->head); free(remoted_output->head); free(remoted_output); } From 17f5a44f3b992fc6f38fb055f09aaedadbecf893 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Wed, 1 Feb 2023 22:25:06 +0200 Subject: [PATCH 591/609] remoting-plugin: Check virtual outputs/remoting instance With commit aab722bb, "backend-drm: prepare virtual output API for heterogeneous outputs", we now call the virtual destroy function, but when shutting the compositor we no longer have a remoting instance available. When searching out for a remoting output verify if the remoting instance is still available before attempting to search for a remoting output. Addresses the following crash at shutdown: 0x00007fd430a1d347 in lookup_remoted_output (output=0x557163d5dad0) at ../remoting/remoting-plugin.c:515 0x00007fd430a1d746 in remoting_output_destroy (output=0x557163d5dad0) at ../remoting/remoting-plugin.c:635 0x00007fd439e11ab9 in drm_virtual_output_destroy (base=0x557163d5dad0) at ../libweston/backend-drm/drm-virtual.c:265 0x00007fd43a8635d0 in weston_compositor_shutdown (ec=0x557163239530) at ../libweston/compositor.c:8271 0x00007fd439e029d4 in drm_destroy (backend=0x557163240ae0) at ../libweston/backend-drm/drm.c:2713 0x00007fd43a863e07 in weston_compositor_destroy (compositor=0x557163239530) at ../libweston/compositor.c:8626 Includes a note to point up what should be done about by checking out https://gitlab.freedesktop.org/wayland/weston/-/issues/591. Fixes aab722bb "backend-drm: prepare virtual output API for heterogeneous outputs" Signed-off-by: Marius Vlad (cherry picked from commit ca52c79c51088ca4c724b34e54c3bb97a9a51c67) --- remoting/remoting-plugin.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/remoting/remoting-plugin.c b/remoting/remoting-plugin.c index aabcd597..4d4dea7e 100644 --- a/remoting/remoting-plugin.c +++ b/remoting/remoting-plugin.c @@ -512,6 +512,16 @@ lookup_remoted_output(struct weston_output *output) struct weston_remoting *remoting = weston_remoting_get(c); struct remoted_output *remoted_output; + /* XXX: This could happen on the compositor shutdown path with our + * destroy listener being removed, and remoting_output_destroy() being + * called as a virtual destructor. + * + * See https://gitlab.freedesktop.org/wayland/weston/-/issues/591 for + * an alternative to the shutdown sequence. + */ + if (!remoting) + return NULL; + wl_list_for_each(remoted_output, &remoting->output_list, link) { if (remoted_output->output == output) return remoted_output; @@ -636,6 +646,9 @@ remoting_output_destroy(struct weston_output *output) struct remoted_output *remoted_output = lookup_remoted_output(output); struct weston_mode *mode, *next; + if (!remoted_output) + return; + weston_head_release(remoted_output->head); wl_list_for_each_safe(mode, next, &output->mode_list, link) { From eaa777b914bfb15c4c5a332ab1cc0216530a1087 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Thu, 16 Feb 2023 21:34:04 +0200 Subject: [PATCH 592/609] pipewire: Follow-up with remoting pluging when releasing the head Similarily to what the remoting plug-in does, explicitly call weston_release_head() before removing the output list entry. We do that to avoid lookup_pipewire_output() returning NULL and still find out the pipewire_output. Signed-off-by: Marius Vlad (cherry picked from commit 5db6d19e6bb4c72085104fcae9abf2f632d5f45a) --- pipewire/pipewire-plugin.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pipewire/pipewire-plugin.c b/pipewire/pipewire-plugin.c index 9a913aa3..629c66ec 100644 --- a/pipewire/pipewire-plugin.c +++ b/pipewire/pipewire-plugin.c @@ -310,6 +310,8 @@ pipewire_output_destroy(struct weston_output *base_output) struct pipewire_output *output = lookup_pipewire_output(base_output); struct weston_mode *mode, *next; + weston_head_release(output->head); + wl_list_for_each_safe(mode, next, &base_output->mode_list, link) { wl_list_remove(&mode->link); free(mode); @@ -318,7 +320,6 @@ pipewire_output_destroy(struct weston_output *base_output) pw_stream_destroy(output->stream); wl_list_remove(&output->link); - weston_head_release(output->head); free(output->head); free(output); } From 597437a0964faa9e58d67395f43c09f62989c681 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Thu, 16 Feb 2023 21:37:07 +0200 Subject: [PATCH 593/609] pipewire: Fix memleak upon compositor shutdown This happens when shutting the compositor, and follows-up with the remoting plug-in. Signed-off-by: Marius Vlad (cherry picked from commit aa78da24659a97b82bcf1e28b985ea4f9fce4499) --- pipewire/pipewire-plugin.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pipewire/pipewire-plugin.c b/pipewire/pipewire-plugin.c index 629c66ec..2c91ee7f 100644 --- a/pipewire/pipewire-plugin.c +++ b/pipewire/pipewire-plugin.c @@ -638,6 +638,8 @@ weston_pipewire_destroy(struct wl_listener *l, void *data) wl_event_source_remove(pipewire->loop_source); pw_loop_leave(pipewire->loop); pw_loop_destroy(pipewire->loop); + + free(pipewire); } static struct weston_pipewire * From 0849a9b3c843c05c7b180a04737317f8f5a60725 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Thu, 16 Feb 2023 21:39:22 +0200 Subject: [PATCH 594/609] pipewire: Destroy the pipewire outputs at shutdown Seems like we are missing destroying the pipewire outputs on the shutdown path; this follow-ups with remoting plug-in as well. Signed-off-by: Marius Vlad (cherry picked from commit 278fe4d7d47c7828d42047f4c910f1d815d32b80) --- pipewire/pipewire-plugin.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pipewire/pipewire-plugin.c b/pipewire/pipewire-plugin.c index 2c91ee7f..b8dad03e 100644 --- a/pipewire/pipewire-plugin.c +++ b/pipewire/pipewire-plugin.c @@ -631,10 +631,14 @@ weston_pipewire_destroy(struct wl_listener *l, void *data) { struct weston_pipewire *pipewire = wl_container_of(l, pipewire, destroy_listener); + struct pipewire_output *p_output, *p_output_next; weston_log_scope_destroy(pipewire->debug); pipewire->debug = NULL; + wl_list_for_each_safe(p_output, p_output_next, &pipewire->output_list, link) + pipewire_output_destroy(p_output->output); + wl_event_source_remove(pipewire->loop_source); pw_loop_leave(pipewire->loop); pw_loop_destroy(pipewire->loop); From 70479268340f50cebe3f1dec45d51c1ec5b140e7 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Fri, 17 Feb 2023 00:21:59 +0200 Subject: [PATCH 595/609] pipewire-plugin: Check virtual outputs/remoting instance Similarly to remoting plug-in in commit "Check virtual outputs/remoting instance" this avoids touching the pipewire instance, and with it, the pipewire output. Includes a note to point up what should be done about by checking out https://gitlab.freedesktop.org/wayland/weston/-/issues/591. Signed-off-by: Marius Vlad (cherry picked from commit 6617deebec5586c4d8c61097b7e51dd53c4e4624) --- pipewire/pipewire-plugin.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pipewire/pipewire-plugin.c b/pipewire/pipewire-plugin.c index b8dad03e..748ba492 100644 --- a/pipewire/pipewire-plugin.c +++ b/pipewire/pipewire-plugin.c @@ -149,6 +149,16 @@ lookup_pipewire_output(struct weston_output *base_output) struct weston_pipewire *pipewire = weston_pipewire_get(c); struct pipewire_output *output; + /* XXX: This could happen on the compositor shutdown path with our + * destroy listener being removed, and pipewire_output_destroy() being + * called as a virtual destructor. + * + * See https://gitlab.freedesktop.org/wayland/weston/-/issues/591 for + * an alternative to the shutdown sequence. + */ + if (!pipewire) + return NULL; + wl_list_for_each(output, &pipewire->output_list, link) { if (output->output == base_output) return output; @@ -310,6 +320,9 @@ pipewire_output_destroy(struct weston_output *base_output) struct pipewire_output *output = lookup_pipewire_output(base_output); struct weston_mode *mode, *next; + if (!output) + return; + weston_head_release(output->head); wl_list_for_each_safe(mode, next, &base_output->mode_list, link) { From df70b81ed7d8d80043b016b406ce79b1f1995af0 Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Sat, 4 Feb 2023 19:59:33 +0200 Subject: [PATCH 596/609] backend-drm: Do not overwrite plane's index when creating virtual plane Starting with commit 4cde507be6a116 "backend-drm: fix plane sorting" the plane list will have a descending order of the planes rather than ascending. This reversed order had the side-effect of exposing the fact that we don't set-up a plane index when creating the drm_plane using the DRM virtual API. Without settting a plane index for that drm_plane we effectively overwrite the plane index which has the 0 (zero) entry. This wasn't an issue before commit 4cde507be6a116 "backend-drm: fix plane sorting" as it seems we never picked up that plane index as being a suitable one due to the fact that those were assigned to primary planes, but after that commit, the cursor plane will be one getting the 0 (zero) plane index. Finally, this would trip over because we attempt to place a (cursor) view on a primary plane (where it would've normally be a cursor plane) and we end up with no framebuffer ref. This is fixed trivially by assigning a plane index, different than the ones already created by create_spirtes(). Signed-off-by: Marius Vlad (cherry picked from commit 27ce9dadd8865b266f72f848b784d61aeaf8b228) --- libweston/backend-drm/drm-virtual.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/libweston/backend-drm/drm-virtual.c b/libweston/backend-drm/drm-virtual.c index cf8de76a..931cb822 100644 --- a/libweston/backend-drm/drm-virtual.c +++ b/libweston/backend-drm/drm-virtual.c @@ -81,6 +81,20 @@ drm_virtual_crtc_destroy(struct drm_crtc *crtc) free(crtc); } +static uint32_t +get_drm_plane_index_maximum(struct drm_device *device) +{ + uint32_t max = 0; + struct drm_plane *p; + + wl_list_for_each(p, &device->plane_list, link) { + if (p->plane_idx > max) + max = p->plane_idx; + } + + return max; +} + /** * Create a drm_plane for virtual output * @@ -125,6 +139,7 @@ drm_virtual_plane_create(struct drm_device *device, struct drm_output *output) goto err; weston_plane_init(&plane->base, b->compositor, 0, 0); + plane->plane_idx = get_drm_plane_index_maximum(device) + 1; wl_list_insert(&device->plane_list, &plane->link); return plane; From d5a3ec5e5878fc9bc473ed53f732c3e93943ec83 Mon Sep 17 00:00:00 2001 From: Leandro Ribeiro Date: Mon, 6 Feb 2023 15:55:18 -0300 Subject: [PATCH 597/609] desktop-shell: do not forget to reset pending config size after resizes During interactive resizes, we progressively change the size of the client surface and send config events with these sizes to the client. After that, the toplevel->pending.size keeps the size of the last config event that we've sent, i.e. the surface size after the resize is over. Later, if the client spontaneously resize (by attaching a buffer with a different size or setting the viewport destination, for instance), their surface size will change, but toplevel->pending.size continues being that old size from after the resize. If something happens and Weston decides to send a config event, clients may re-allocate to that old size, resulting in a sudden resize. This does not happen when a client goes from fullscreen/maximized to windowed mode because in such cases we are resetting toplevel->pending.size to zero. So in the next config event that clients receive they are allowed to attach buffers with the size that they prefer. So do the same after a resize: set the pending config size to zero. Signed-off-by: Leandro Ribeiro (cherry picked from commit ba82af938a87ff088b4aacff3b8ac1b6bb461be2) --- desktop-shell/shell.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 9da08aef..d13d12ff 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -1209,6 +1209,7 @@ resize_grab_button(struct weston_pointer_grab *grab, resize->base.shsurf->desktop_surface; weston_desktop_surface_set_resizing(desktop_surface, false); + weston_desktop_surface_set_size(desktop_surface, 0, 0); } shell_grab_end(&resize->base); @@ -1225,6 +1226,7 @@ resize_grab_cancel(struct weston_pointer_grab *grab) struct weston_desktop_surface *desktop_surface = resize->base.shsurf->desktop_surface; weston_desktop_surface_set_resizing(desktop_surface, false); + weston_desktop_surface_set_size(desktop_surface, 0, 0); } shell_grab_end(&resize->base); From 2d66d01cf58b0b079dc70f28e144aac020710d35 Mon Sep 17 00:00:00 2001 From: Alexandros Frantzis Date: Mon, 27 Feb 2023 22:30:24 -0300 Subject: [PATCH 598/609] xwayland: Handle shell hint for client to choose dimensions A config event with width == 0 or height == 0 from the shell is a hint to the client to choose its own dimensions. Since X11 clients don't support such hints we make a best guess by trying to use the last saved dimensions or, as a fallback, the current dimensions. This hint is mainly used by libweston/desktop shells when transitioning to a normal state from maximized, fullscreen or after a resize [1]. Without support for this hint the aforementioned transition causes xwayland surfaces to be configured to a 1x1 size. To be able to use the last saved dimensions with xwayland surface, the shell needs to first set the maximized/fullscreen state and only then set the new size, which is currently the case for desktop-shell. Otherwise, if the new size is set first, then the last saved dimensions will be set to the fullscreen/maximized values and won't be useful when restoring to a normal window size. [1] Recently we've introduced ba82af938a87ff088b4aacff3b8ac1b6bb461be2 "desktop-shell: do not forget to reset pending config size after resizes". As we were not handling the 0x0 size hint, resizing X applications started to fail. This patch fixes that. Signed-off-by: Alexandros Frantzis Signed-off-by: Leandro Ribeiro Co-authored-by: Leandro Ribeiro (cherry picked from commit 2acd2c74891cd0548c1ff410ccfe81952bed27b3) --- xwayland/window-manager.c | 46 +++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/xwayland/window-manager.c b/xwayland/window-manager.c index 9dc30e1c..71c63caf 100644 --- a/xwayland/window-manager.c +++ b/xwayland/window-manager.c @@ -2795,6 +2795,8 @@ send_configure(struct weston_surface *surface, int32_t width, int32_t height) struct theme *t; int new_width, new_height; int vborder, hborder; + bool use_saved_dimensions = false; + bool use_current_dimensions = false; if (!window || !window->wm) return; @@ -2809,20 +2811,46 @@ send_configure(struct weston_surface *surface, int32_t width, int32_t height) vborder = 0; } - if (width > hborder) - new_width = width - hborder; - else - new_width = 1; - - if (height > vborder) - new_height = height - vborder; - else - new_height = 1; + /* A config event with width == 0 or height == 0 is a hint to the client + * to choose its own dimensions. Since X11 clients don't support such + * hints we make a best guess here by trying to use the last saved + * dimensions or, as a fallback, the current dimensions. */ + if (width == 0 || height == 0) { + use_saved_dimensions = window->saved_width > 0 && + window->saved_height > 0; + use_current_dimensions = !use_saved_dimensions && + window->width > 0 && + window->height > 0; + } + + /* The saved or current dimensions are the plain window content + * dimensions without the borders, so we can use them directly for + * new_width and new_height below. */ + if (use_current_dimensions) { + new_width = window->width; + new_height = window->height; + } else if (use_saved_dimensions) { + new_width = window->saved_width; + new_height = window->saved_height; + } else { + new_width = (width > hborder) ? (width - hborder) : 1; + new_height = (height > vborder) ? (height - vborder) : 1; + } if (window->width != new_width || window->height != new_height) { window->width = new_width; window->height = new_height; + /* Save the toplevel size so that we can pick up a reasonable + * value when the compositor tell us to choose a size. We are + * already saving the size before going fullscreen/maximized, + * but this covers the case in which our size is changed but we + * continue on a normal state. */ + if (!weston_wm_window_is_maximized(window) && !window->fullscreen) { + window->saved_width = new_width; + window->saved_height = new_height; + } + if (window->frame) { if (weston_wm_window_is_maximized(window)) frame_set_flag(window->frame, FRAME_FLAG_MAXIMIZED); From 5ad870f505f8592b26c14a28aa2987cbe0552cd8 Mon Sep 17 00:00:00 2001 From: Michael Olbrich Date: Wed, 15 Feb 2023 16:28:11 +0100 Subject: [PATCH 599/609] libweston: clear parent_view when the parent view is destroyed When a view is destroyed then the views of subsurfaces remain until the view list is rebuilt for the next repaint. During that time view->parent_view contains an invalid pointer and weston will crash when it tries to access the view. This happens for a surface with subsurfaces with views on two different outputs with the ivi-shell: When the surface is destroyed then the destroy handler of the ivi-shell (shell_handle_surface_destroy()) may be called first. It will (indirectly) destroy the view of the main surface with weston_view_destroy(). Next the surface destroy handler of the subsurfaces (subsurface_handle_parent_destroy() is called. It will unmap the first view of the subsurface. Here weston_surface_assign_output() is called which tries to find the output of the second view and accesses the now invalid view->parent_view in the process. There are probably other ways to trigger similar crashes. To avoid this, clear view->parent_view when the parent view is destroyed. Fixes 0669d4de4f22 ("libweston: Skip views without a layer assignment in output_mask calculations") Signed-off-by: Michael Olbrich (cherry picked from commit 39796f88e6ed4a33a42c74b743e999294b3e4651) --- libweston/compositor.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libweston/compositor.c b/libweston/compositor.c index 6cfcba25..428e4b5e 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -1827,6 +1827,7 @@ transform_parent_handle_parent_destroy(struct wl_listener *listener, geometry.parent_destroy_listener); weston_view_set_transform_parent(view, NULL); + view->parent_view = NULL; } WL_EXPORT void From ff13a90eea0e73ad96fcad4ce2e0e1d781465d06 Mon Sep 17 00:00:00 2001 From: Michael Olbrich Date: Thu, 16 Feb 2023 17:54:22 +0100 Subject: [PATCH 600/609] desktop-shell: avoid crashes when a surface disappears during resize The desktop_surface object is destroyed first so it can happen that the shsurf still exists but desktop_surface is already NULL. So expand the check to make sure the desktop_surface is still available in the resize callbacks. Signed-off-by: Michael Olbrich (cherry picked from commit 06365e602bd7599760a9a1414d12d6c26ca9c445) --- desktop-shell/shell.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index d13d12ff..e4ea90f9 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -1153,7 +1153,7 @@ resize_grab_motion(struct weston_pointer_grab *grab, weston_pointer_move(pointer, event); - if (!shsurf) + if (!shsurf || !shsurf->desktop_surface) return; weston_view_from_global_fixed(shsurf->view, @@ -1204,7 +1204,7 @@ resize_grab_button(struct weston_pointer_grab *grab, if (pointer->button_count == 0 && state == WL_POINTER_BUTTON_STATE_RELEASED) { - if (resize->base.shsurf != NULL) { + if (resize->base.shsurf && resize->base.shsurf->desktop_surface) { struct weston_desktop_surface *desktop_surface = resize->base.shsurf->desktop_surface; weston_desktop_surface_set_resizing(desktop_surface, @@ -1222,7 +1222,7 @@ resize_grab_cancel(struct weston_pointer_grab *grab) { struct weston_resize_grab *resize = (struct weston_resize_grab *) grab; - if (resize->base.shsurf != NULL) { + if (resize->base.shsurf && resize->base.shsurf->desktop_surface) { struct weston_desktop_surface *desktop_surface = resize->base.shsurf->desktop_surface; weston_desktop_surface_set_resizing(desktop_surface, false); From 0bd68d9ad6b4e8b860e638ffb82abf2eeb2edd15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20G=C3=B3mez?= Date: Wed, 8 Mar 2023 17:36:34 -0500 Subject: [PATCH 601/609] libweston/input: Remove redundant surface destroy listener in constraints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, the surface destroy listener in pointer constraints is redundant, since surface destruction already handles pointer constraints destruction (see libweston/compositor.c:weston_surface_unref()). Signed-off-by: Sergio Gómez (cherry picked from commit 64da736d37a7df8b3bd6fd43746ac513bae72748) --- include/libweston/libweston.h | 1 - libweston/input.c | 17 ----------------- 2 files changed, 18 deletions(-) diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 7382dd2d..eb3c487c 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -1605,7 +1605,6 @@ struct weston_pointer_constraint { bool hint_is_pending; struct wl_listener pointer_destroy_listener; - struct wl_listener surface_destroy_listener; struct wl_listener surface_commit_listener; struct wl_listener surface_activate_listener; }; diff --git a/libweston/input.c b/libweston/input.c index 235cf023..db029580 100644 --- a/libweston/input.c +++ b/libweston/input.c @@ -3713,8 +3713,6 @@ enable_pointer_constraint(struct weston_pointer_constraint *constraint, constraint->view = view; pointer_constraint_notify_activated(constraint); weston_pointer_start_grab(constraint->pointer, &constraint->grab); - wl_list_remove(&constraint->surface_destroy_listener.link); - wl_list_init(&constraint->surface_destroy_listener.link); } static bool @@ -3738,7 +3736,6 @@ weston_pointer_constraint_destroy(struct weston_pointer_constraint *constraint) weston_pointer_constraint_disable(constraint); wl_list_remove(&constraint->pointer_destroy_listener.link); - wl_list_remove(&constraint->surface_destroy_listener.link); wl_list_remove(&constraint->surface_commit_listener.link); wl_list_remove(&constraint->surface_activate_listener.link); @@ -3934,16 +3931,6 @@ pointer_constraint_pointer_destroyed(struct wl_listener *listener, void *data) weston_pointer_constraint_destroy(constraint); } -static void -pointer_constraint_surface_destroyed(struct wl_listener *listener, void *data) -{ - struct weston_pointer_constraint *constraint = - container_of(listener, struct weston_pointer_constraint, - surface_destroy_listener); - - weston_pointer_constraint_destroy(constraint); -} - static void pointer_constraint_surface_committed(struct wl_listener *listener, void *data) { @@ -4005,8 +3992,6 @@ weston_pointer_constraint_create(struct weston_surface *surface, constraint->surface_activate_listener.notify = pointer_constraint_surface_activate; - constraint->surface_destroy_listener.notify = - pointer_constraint_surface_destroyed; constraint->surface_commit_listener.notify = pointer_constraint_surface_committed; constraint->pointer_destroy_listener.notify = @@ -4016,8 +4001,6 @@ weston_pointer_constraint_create(struct weston_surface *surface, &constraint->surface_activate_listener); wl_signal_add(&pointer->destroy_signal, &constraint->pointer_destroy_listener); - wl_signal_add(&surface->destroy_signal, - &constraint->surface_destroy_listener); wl_signal_add(&surface->commit_signal, &constraint->surface_commit_listener); From 21e46364c0813f80bf6992d929339cdcbdaf5171 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20G=C3=B3mez?= Date: Wed, 8 Mar 2023 17:39:49 -0500 Subject: [PATCH 602/609] libweston: Add view unmap listener to pointer constraints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the logic of pointer constraints assumes a valid view throughout, add a signal to disable constraints when its current view is unmapped by Weston. The assumption that a previously unmapped view is valid already leads to the constraints code crashing. This can happen when attaching a NULL buffer to the surface and commiting, which effectively unmaps the view with the side effect of clearing the surface's input region, which is then assumed valid inside maybe_warp_confined_pointer(). Fixes: #721 Signed-off-by: Sergio Gómez (cherry picked from commit e3079393c400e3dc6498234d1d092f3072fa8b44) --- include/libweston/libweston.h | 2 ++ libweston/compositor.c | 31 ++++++++++++++++--------------- libweston/input.c | 16 ++++++++++++++++ 3 files changed, 34 insertions(+), 15 deletions(-) diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index eb3c487c..4bb9ea75 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -1452,6 +1452,7 @@ struct weston_view { struct weston_surface *surface; struct wl_list surface_link; struct wl_signal destroy_signal; + struct wl_signal unmap_signal; /* struct weston_paint_node::view_link */ struct wl_list paint_node_list; @@ -1605,6 +1606,7 @@ struct weston_pointer_constraint { bool hint_is_pending; struct wl_listener pointer_destroy_listener; + struct wl_listener view_unmap_listener; struct wl_listener surface_commit_listener; struct wl_listener surface_activate_listener; }; diff --git a/libweston/compositor.c b/libweston/compositor.c index 428e4b5e..b6c16d6f 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -396,6 +396,7 @@ weston_view_create(struct weston_surface *surface) wl_list_insert(&surface->views, &view->surface_link); wl_signal_init(&view->destroy_signal); + wl_signal_init(&view->unmap_signal); wl_list_init(&view->link); wl_list_init(&view->layer_link.link); wl_list_init(&view->paint_node_list); @@ -2248,22 +2249,22 @@ weston_view_unmap(struct weston_view *view) view->output_mask = 0; weston_surface_assign_output(view->surface); - if (weston_surface_is_mapped(view->surface)) - return; - - wl_list_for_each(seat, &view->surface->compositor->seat_list, link) { - struct weston_touch *touch = weston_seat_get_touch(seat); - struct weston_pointer *pointer = weston_seat_get_pointer(seat); - struct weston_keyboard *keyboard = - weston_seat_get_keyboard(seat); - - if (keyboard && keyboard->focus == view->surface) - weston_keyboard_set_focus(keyboard, NULL); - if (pointer && pointer->focus == view) - weston_pointer_clear_focus(pointer); - if (touch && touch->focus == view) - weston_touch_set_focus(touch, NULL); + if (!weston_surface_is_mapped(view->surface)) { + wl_list_for_each(seat, &view->surface->compositor->seat_list, link) { + struct weston_touch *touch = weston_seat_get_touch(seat); + struct weston_pointer *pointer = weston_seat_get_pointer(seat); + struct weston_keyboard *keyboard = + weston_seat_get_keyboard(seat); + + if (keyboard && keyboard->focus == view->surface) + weston_keyboard_set_focus(keyboard, NULL); + if (pointer && pointer->focus == view) + weston_pointer_clear_focus(pointer); + if (touch && touch->focus == view) + weston_touch_set_focus(touch, NULL); + } } + weston_signal_emit_mutable(&view->unmap_signal, view); } WL_EXPORT void diff --git a/libweston/input.c b/libweston/input.c index db029580..f2a9a07a 100644 --- a/libweston/input.c +++ b/libweston/input.c @@ -3713,6 +3713,8 @@ enable_pointer_constraint(struct weston_pointer_constraint *constraint, constraint->view = view; pointer_constraint_notify_activated(constraint); weston_pointer_start_grab(constraint->pointer, &constraint->grab); + wl_signal_add(&constraint->view->unmap_signal, + &constraint->view_unmap_listener); } static bool @@ -3727,6 +3729,8 @@ weston_pointer_constraint_disable(struct weston_pointer_constraint *constraint) constraint->view = NULL; pointer_constraint_notify_deactivated(constraint); weston_pointer_end_grab(constraint->grab.pointer); + wl_list_remove(&constraint->view_unmap_listener.link); + wl_list_init(&constraint->view_unmap_listener.link); } void @@ -3931,6 +3935,16 @@ pointer_constraint_pointer_destroyed(struct wl_listener *listener, void *data) weston_pointer_constraint_destroy(constraint); } +static void +pointer_constraint_view_unmapped(struct wl_listener *listener, void *data) +{ + struct weston_pointer_constraint *constraint = + container_of(listener, struct weston_pointer_constraint, + view_unmap_listener); + + disable_pointer_constraint(constraint); +} + static void pointer_constraint_surface_committed(struct wl_listener *listener, void *data) { @@ -3992,6 +4006,8 @@ weston_pointer_constraint_create(struct weston_surface *surface, constraint->surface_activate_listener.notify = pointer_constraint_surface_activate; + constraint->view_unmap_listener.notify = + pointer_constraint_view_unmapped; constraint->surface_commit_listener.notify = pointer_constraint_surface_committed; constraint->pointer_destroy_listener.notify = From 072c56723c7eab4b18337dea7847506976d12c56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20G=C3=B3mez?= Date: Mon, 6 Mar 2023 16:38:11 -0500 Subject: [PATCH 603/609] libweston: Add assert for valid confine region in maybe_warp_confined_pointer() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sergio Gómez (cherry picked from commit b6423e59d9116d140e33e925d6dd9bf8324188a7) --- libweston/input.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libweston/input.c b/libweston/input.c index f2a9a07a..338be6c3 100644 --- a/libweston/input.c +++ b/libweston/input.c @@ -4786,6 +4786,7 @@ maybe_warp_confined_pointer(struct weston_pointer_constraint *constraint) pixman_region32_intersect(&confine_region, &constraint->view->surface->input, &constraint->region); + assert(!pixman_region32_selfcheck(&confine_region)); region_to_outline(&confine_region, &borders); pixman_region32_fini(&confine_region); From a627a4be50045c539c6d21bb14ea863a3a84a40f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20G=C3=B3mez?= Date: Thu, 23 Mar 2023 12:38:38 -0500 Subject: [PATCH 604/609] libweston/input: Fix assert for valid confine region MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need only check that the region is not empty. If either the input region or the constraint region have degenerate extents, the intersection from the previous instruction will set confine_region->data to pixman_region_empty_data. Fixes: b6423e59 Signed-off-by: Sergio Gómez (cherry picked from commit 1ed88f60c0125988cf1d952f0dabf568bfd82a13) --- libweston/input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libweston/input.c b/libweston/input.c index 338be6c3..cc6cb00d 100644 --- a/libweston/input.c +++ b/libweston/input.c @@ -4786,7 +4786,7 @@ maybe_warp_confined_pointer(struct weston_pointer_constraint *constraint) pixman_region32_intersect(&confine_region, &constraint->view->surface->input, &constraint->region); - assert(!pixman_region32_selfcheck(&confine_region)); + assert(pixman_region32_not_empty(&confine_region)); region_to_outline(&confine_region, &borders); pixman_region32_fini(&confine_region); From 78852bd35045774ade6c2b7559efe9c2fc7398db Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Wed, 17 May 2023 12:19:47 +0300 Subject: [PATCH 605/609] build: bump to version 11.0.2 for the point release Signed-off-by: Marius Vlad --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 82119aca..3de55ec1 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project('weston', 'c', - version: '11.0.1', + version: '11.0.2', default_options: [ 'warning_level=3', 'c_std=gnu99', From a5d52075a07b9fa0a40b7788a27434cebfd2389a Mon Sep 17 00:00:00 2001 From: Michael Tretter Date: Tue, 28 Feb 2023 13:08:47 +0100 Subject: [PATCH 606/609] backend-drm: schedule connector disable for detached head Currently, if a head is detached, the entire state of the device is invalidated to make sure that the connector is disabled on the next atomic commit. Side effect of the invalid state is that all planes are disabled on the next commit. This includes planes that are used with a different head that is not part of the next atomic commit. Disabling the planes of unrelated outputs causes a blanking of these outputs until output is repainted and the plane is reenabled. Store the detached heads in a list on the output and disable the connectors for all heads in this list in the next atomic commit. Signed-off-by: Michael Tretter (cherry picked from commit bcacd9ec5a924317416eabb65a6cd6767d5bfb94) --- libweston/backend-drm/drm-internal.h | 6 ++++++ libweston/backend-drm/drm.c | 20 ++++++++++++-------- libweston/backend-drm/kms.c | 9 +++++++++ 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/libweston/backend-drm/drm-internal.h b/libweston/backend-drm/drm-internal.h index 1ee1974c..d2cc7a5b 100644 --- a/libweston/backend-drm/drm-internal.h +++ b/libweston/backend-drm/drm-internal.h @@ -519,6 +519,9 @@ struct drm_head { drmModeModeInfo inherited_mode; /**< Original mode on the connector */ uint32_t inherited_max_bpc; /**< Original max_bpc on the connector */ uint32_t inherited_crtc_id; /**< Original CRTC assignment */ + + /* drm_output::disable_head */ + struct wl_list disable_head_link; }; struct drm_crtc { @@ -541,6 +544,9 @@ struct drm_output { struct drm_device *device; struct drm_crtc *crtc; + /* drm_head::disable_head_link */ + struct wl_list disable_head; + bool page_flip_pending; bool atomic_complete_pending; bool destroy_pending; diff --git a/libweston/backend-drm/drm.c b/libweston/backend-drm/drm.c index 7d607ca6..52996d50 100644 --- a/libweston/backend-drm/drm.c +++ b/libweston/backend-drm/drm.c @@ -1299,10 +1299,14 @@ drm_output_attach_head(struct weston_output *output_base, { struct drm_backend *b = to_drm_backend(output_base->compositor); struct drm_device *device = b->drm; + struct drm_head *head = to_drm_head(head_base); if (wl_list_length(&output_base->head_list) >= MAX_CLONED_CONNECTORS) return -1; + wl_list_remove(&head->disable_head_link); + wl_list_init(&head->disable_head_link); + if (!output_base->enabled) return 0; @@ -1326,18 +1330,14 @@ static void drm_output_detach_head(struct weston_output *output_base, struct weston_head *head_base) { - struct drm_backend *b = to_drm_backend(output_base->compositor); - struct drm_device *device = b->drm; + struct drm_output *output = to_drm_output(output_base); + struct drm_head *head = to_drm_head(head_base); if (!output_base->enabled) return; - /* Need to go through modeset to drop connectors that should no longer - * be driven. */ - /* XXX: Ideally we'd do this per-output, not globally. */ - device->state_invalid = true; - - weston_output_schedule_repaint(output_base); + /* Drop connectors that should no longer be driven on next repaint. */ + wl_list_insert(&output->disable_head, &head->disable_head_link); } int @@ -2249,6 +2249,8 @@ drm_head_create(struct drm_device *device, drmModeConnector *conn, head->base.backend_id = drm_head_destroy; + wl_list_init(&head->disable_head_link); + ret = drm_head_update_info(head, conn); if (ret < 0) goto err_update; @@ -2322,6 +2324,8 @@ drm_output_create(struct weston_compositor *compositor, const char *name) output->device = device; output->crtc = NULL; + wl_list_init(&output->disable_head); + output->max_bpc = 16; output->gbm_format = DRM_FORMAT_INVALID; #ifdef BUILD_DRM_GBM diff --git a/libweston/backend-drm/kms.c b/libweston/backend-drm/kms.c index 0118efa1..b6c7fe5f 100644 --- a/libweston/backend-drm/kms.c +++ b/libweston/backend-drm/kms.c @@ -951,6 +951,7 @@ drm_output_apply_state_atomic(struct drm_output_state *state, struct drm_plane_state *plane_state; struct drm_mode *current_mode = to_drm_mode(output->base.current_mode); struct drm_head *head; + struct drm_head *tmp; int ret = 0; drm_debug(b, "\t\t[atomic] %s output %lu (%s) state\n", @@ -987,6 +988,14 @@ drm_output_apply_state_atomic(struct drm_output_state *state, wl_list_for_each(head, &output->base.head_list, base.output_link) ret |= connector_add_prop(req, &head->connector, WDRM_CONNECTOR_CRTC_ID, 0); + + wl_list_for_each_safe(head, tmp, &output->disable_head, + disable_head_link) { + ret |= connector_add_prop(req, &head->connector, + WDRM_CONNECTOR_CRTC_ID, 0); + wl_list_remove(&head->disable_head_link); + wl_list_init(&head->disable_head_link); + } } wl_list_for_each(head, &output->base.head_list, base.output_link) { From 38eb0a96e01d394fb8f06d4f016f8cc72ea703fa Mon Sep 17 00:00:00 2001 From: "Liu, Kai1" Date: Tue, 30 May 2023 11:10:28 +0800 Subject: [PATCH 607/609] xwm: WM_TRANSIENT_FOR should not point to override-redirect window The override-redirect window will not be assigned a shell_surface object. If it is used as a parent window, it will cause a crash when calling the set_parent function. The EWMH specification does not describe the behavior of an override-redirect window as a parent window, so we should ignore this case. Signed-off-by: Liu, Kai1 (cherry picked from commit b468687dd2663240d1613bf4a917f049ef09af46) --- xwayland/window-manager.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/xwayland/window-manager.c b/xwayland/window-manager.c index 71c63caf..aa3edab3 100644 --- a/xwayland/window-manager.c +++ b/xwayland/window-manager.c @@ -3048,7 +3048,9 @@ xserver_map_shell_surface(struct weston_wm_window *window, } else if (window->override_redirect) { xwayland_interface->set_xwayland(window->shsurf, window->x, window->y); - } else if (window->transient_for && window->transient_for->surface) { + } else if (window->transient_for && + !window->transient_for->override_redirect && + window->transient_for->surface) { parent = window->transient_for; if (weston_wm_window_type_inactive(window)) { xwayland_interface->set_transient(window->shsurf, From 263702cf7d2d88e9018d1ecd9d52a0547eacac0b Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Wed, 2 Aug 2023 09:42:04 +0300 Subject: [PATCH 608/609] backend-drm/meson.build: Require at least mesa 21.1.1 We seem to be using at least mesa 21.1.1 since Weston 10, but we never explicitly asked for it. Fixes: #790 Signed-off-by: Marius Vlad (cherry picked from commit 0713ea7ee6216e45ebdb67cef63bcef7961d1d4e) --- libweston/backend-drm/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libweston/backend-drm/meson.build b/libweston/backend-drm/meson.build index bf7ce33b..bf8dbb0a 100644 --- a/libweston/backend-drm/meson.build +++ b/libweston/backend-drm/meson.build @@ -44,7 +44,7 @@ deps_drm = [ ] if get_option('renderer-gl') - dep_gbm = dependency('gbm', required: false) + dep_gbm = dependency('gbm', required: false, version: '>= 21.1.1') if not dep_gbm.found() error('drm-backend with GL renderer requires gbm which was not found. Or, you can use \'-Drenderer-gl=false\'.') endif From 742ad74bc0dbdc74d9c5b46e2878f4d584f95f2b Mon Sep 17 00:00:00 2001 From: Marius Vlad Date: Wed, 2 Aug 2023 18:48:58 +0300 Subject: [PATCH 609/609] build: bump to version 11.0.3 for the point release Signed-off-by: Marius Vlad --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 3de55ec1..1418267b 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project('weston', 'c', - version: '11.0.2', + version: '11.0.3', default_options: [ 'warning_level=3', 'c_std=gnu99',