Weston support mirror mode.Use 'export WESTON_ENABLE_MIRROR=1' to enable mirror mode. Signed-off-by: Leo Lu --- a/clients/desktop-shell.c 2023-06-12 20:36:17.698156132 +0800 +++ b/clients/desktop-shell.c 2023-06-13 15:21:06.196116306 +0800 @@ -1060,9 +1060,14 @@ desktop_shell_configure(void *data, struct wl_surface *surface, int32_t width, int32_t height) { - struct window *window = wl_surface_get_user_data(surface); - struct surface *s = window_get_user_data(window); + struct window *window; + struct surface *s; + if (!surface) + return; + + window = wl_surface_get_user_data(surface); + s = window_get_user_data(window); s->configure(data, desktop_shell, edges, window, width, height); } --- a/desktop-shell/shell.c 2023-06-12 20:36:17.702156089 +0800 +++ b/desktop-shell/shell.c 2023-06-13 15:21:06.208116334 +0800 @@ -4263,6 +4263,9 @@ weston_view_set_initial_position(struct } wl_list_for_each(output, &compositor->output_list, link) { + if (output->unavailable) + continue; + if (pixman_region32_contains_point(&output->region, ix, iy, NULL)) { target_output = output; break; --- a/include/libweston/libweston.h 2023-06-12 20:36:17.702156089 +0800 +++ b/include/libweston/libweston.h 2023-06-13 15:21:06.224116373 +0800 @@ -411,7 +411,11 @@ struct weston_output { */ void (*detach_head)(struct weston_output *output, struct weston_head *head); + + bool unavailable; }; +#define weston_output_valid(o) \ + ((o) && !(o)->destroying && !(o)->unavailable) enum weston_pointer_motion_mask { WESTON_POINTER_MOTION_ABS = 1 << 0, --- a/libweston/backend-drm/drm.c 2023-06-12 20:36:17.706156047 +0800 +++ b/libweston/backend-drm/drm.c 2023-06-13 15:21:06.236116402 +0800 @@ -67,6 +67,7 @@ #include "linux-explicit-synchronization.h" #include + static const char default_seat[] = "seat0"; static inline bool drm_head_is_external(struct drm_head *head) @@ -86,7 +87,7 @@ drm_head_is_external(struct drm_head *he static void drm_backend_update_outputs(struct drm_backend *b) { - struct weston_output *primary; + struct weston_output *primary,*base; struct weston_output *output; int x, y, next_x, next_y; next_x = next_y = 0; @@ -94,6 +95,26 @@ drm_backend_update_outputs(struct drm_ba return; primary = b->primary_head->base.output; + + if (b->mirror_mode) { + wl_list_for_each(base, &b->compositor->output_list, link) { + struct drm_output *output = to_drm_output(base); + bool is_mirror = base != primary; + + if (output->is_mirror == is_mirror) + continue; + + /* Make mirrors unavailable for normal views */ + output->base.unavailable = is_mirror; + + output->is_mirror = is_mirror; + output->state_invalid = true; + + weston_log("Output %s changed to %s output\n", + base->name, is_mirror ? "mirror" : "main"); + } + } + if (!primary) return; @@ -394,6 +415,45 @@ drm_output_render_pixman(struct drm_outp return drm_fb_ref(output->dumb[output->current_image]); } +static struct drm_fb * +drm_output_get_fb(struct drm_pending_state *pending_state, + struct weston_output *output_base) +{ + struct drm_output *output = to_drm_output(output_base); + struct drm_plane_state *scanout_state; + struct drm_output_state *state; + struct drm_fb *fb = output->scanout_plane->state_cur->fb; + + state = drm_pending_state_get_output(pending_state, output); + if (!state) + return fb; + + scanout_state = + drm_output_state_get_existing_plane(state, + output->scanout_plane); + if (!scanout_state || !scanout_state->fb) + return fb; + + return scanout_state->fb; +} + +static void +drm_output_try_destroy_wrap_fb(struct drm_output *output) +{ + if (output->wrap[0]) { + drm_fb_unref(output->wrap[0]); + output->wrap[0] = NULL; + } + + if (output->wrap[1]) { + drm_fb_unref(output->wrap[1]); + output->wrap[1] = NULL; + } + + output->next_wrap = 0; +} + + void drm_output_render(struct drm_output_state *state, pixman_region32_t *damage) { @@ -404,17 +464,48 @@ drm_output_render(struct drm_output_stat struct drm_property_info *damage_info = &scanout_plane->props[WDRM_PLANE_FB_DAMAGE_CLIPS]; struct drm_backend *b = to_drm_backend(c); - struct drm_fb *fb; + struct drm_mode *mode; + struct drm_fb *fb = NULL; pixman_region32_t scanout_damage; pixman_box32_t *rects; int n_rects; - + int sw, sh, dx, dy, dw, dh; + float scale_rate ; + int scale_w , scale_h; /* If we already have a client buffer promoted to scanout, then we don't * want to render. */ scanout_state = drm_output_state_get_plane(state, scanout_plane); if (scanout_state->fb) return; + if (!output->is_mirror) { + struct drm_output *tmp; + + /* Repaint all mirrors when updating main output */ + wl_list_for_each(tmp, &b->compositor->output_list, base.link) + if (tmp->is_mirror) + weston_output_schedule_repaint(&tmp->base); + } else { + if (!b->primary_head) + goto out; + + + fb = drm_output_get_fb(state->pending_state, + b->primary_head->base.output); + if (fb) { + drm_fb_ref(fb); + + pixman_region32_init(&scanout_damage); + wl_signal_emit(&output->base.frame_signal, + &scanout_damage); + pixman_region32_fini(&scanout_damage); + } else { + weston_compositor_damage_all(b->compositor); + } + + goto out; + } + /* * If we don't have any damage on the primary plane, and we already * have a renderer buffer active, we can reuse it; else we pass @@ -433,25 +524,57 @@ drm_output_render(struct drm_output_stat } else { fb = drm_output_render_gl(state, damage); } - +out: if (!fb) { drm_plane_state_put_back(scanout_state); return; } + sw = fb->width; + sh = fb->height; + + dx = output->plane_bounds.x1; + dy = output->plane_bounds.y1; + dw = output->plane_bounds.x2 - output->plane_bounds.x1; + dh = output->plane_bounds.y2 - output->plane_bounds.y1; + + if (!dw || !dh) { + mode = to_drm_mode(output->base.current_mode); + dw = mode->mode_info.hdisplay; + dh = mode->mode_info.vdisplay; + } + + if(sw != dw || sh != dh) { + scale_rate =(float ) sw / (float) sh ; + scale_h = (float) dw / scale_rate; + if(scale_h <= dh) + dh = scale_h; + else { + scale_w = dh * scale_rate ; + if(scale_w <= dw) + dw = scale_w; + } + } + else { + dw = sw; + dh = sh; + } + scanout_state->fb = fb; scanout_state->output = output; - + fb = NULL ; scanout_state->src_x = 0; scanout_state->src_y = 0; - scanout_state->src_w = fb->width << 16; - scanout_state->src_h = fb->height << 16; + scanout_state->src_w = sw << 16; + scanout_state->src_h = sh << 16; - scanout_state->dest_x = 0; - scanout_state->dest_y = 0; - scanout_state->dest_w = output->base.current_mode->width; - scanout_state->dest_h = output->base.current_mode->height; + scanout_state->dest_x = dx; + scanout_state->dest_x = dy; + scanout_state->dest_w = dw; + scanout_state->dest_h = dh; + if (output->is_mirror) + return; pixman_region32_subtract(&c->primary_plane.damage, &c->primary_plane.damage, damage); @@ -499,6 +622,7 @@ drm_output_render(struct drm_output_stat &scanout_state->damage_blob_id); pixman_region32_fini(&scanout_damage); + } static int @@ -1275,8 +1399,8 @@ drm_output_fini_pixman(struct drm_output /* Destroying the Pixman surface will destroy all our buffers, * regardless of refcount. Ensure we destroy them here. */ if (!b->shutting_down && - output->scanout_plane->state_cur->fb && - output->scanout_plane->state_cur->fb->type == BUFFER_PIXMAN_DUMB) { + output->scanout_plane->state_cur->fb && (output->is_mirror || + output->scanout_plane->state_cur->fb->type == BUFFER_PIXMAN_DUMB)) { drm_plane_reset_state(output->scanout_plane); } @@ -1955,6 +2079,8 @@ drm_output_destroy(struct weston_output assert(!output->state_last); drm_output_state_free(output->state_cur); + drm_output_try_destroy_wrap_fb(output); + free(output); } @@ -3155,6 +3281,7 @@ drm_backend_create(struct weston_composi enum drm_head_mode head_mode = DRM_HEAD_MODE_DEFAULT; drmModeRes *res; int ret; + char * buf; session_seat = getenv("XDG_SEAT"); if (session_seat) @@ -3172,6 +3299,13 @@ drm_backend_create(struct weston_composi b->head_matches = drm_head_matches[head_mode]; + buf = getenv("WESTON_ENABLE_MIRROR"); + if (buf && buf[0] == '1') { + b->mirror_mode = true; + weston_log("Entering mirror mode.\n"); + } + + b->state_invalid = true; b->drm.fd = -1; @@ -3183,7 +3317,7 @@ drm_backend_create(struct weston_composi b->debug = weston_compositor_add_log_scope(compositor, "drm-backend", "Debug messages from DRM/KMS backend\n", NULL, NULL, NULL); - + compositor->backend = &b->base; if (parse_gbm_format(config->gbm_format, DRM_FORMAT_XRGB8888, &b->gbm_format) < 0) --- a/libweston/backend-drm/drm-gbm.c 2023-06-12 20:36:17.706156047 +0800 +++ b/libweston/backend-drm/drm-gbm.c 2023-06-13 15:21:06.240116411 +0800 @@ -265,8 +265,8 @@ drm_output_fini_egl(struct drm_output *o /* Destroying the GBM surface will destroy all our GBM buffers, * regardless of refcount. Ensure we destroy them here. */ if (!b->shutting_down && - output->scanout_plane->state_cur->fb && - output->scanout_plane->state_cur->fb->type == BUFFER_GBM_SURFACE) { + output->scanout_plane->state_cur->fb && (output->is_mirror || + output->scanout_plane->state_cur->fb->type == BUFFER_GBM_SURFACE)) { drm_plane_reset_state(output->scanout_plane); } --- a/libweston/backend-drm/drm-internal.h 2023-06-12 20:36:17.706156047 +0800 +++ b/libweston/backend-drm/drm-internal.h 2023-06-13 15:21:06.240116411 +0800 @@ -326,6 +326,8 @@ struct drm_backend { drm_head_match_t *head_matches; struct drm_head *primary_head; struct wl_listener output_create_listener; + + bool mirror_mode; }; struct drm_mode { @@ -495,6 +497,7 @@ struct drm_plane { struct wl_list link; struct weston_drm_format_array formats; + bool can_scale; }; struct drm_connector { @@ -581,6 +584,10 @@ struct drm_output { int current_image; pixman_region32_t previous_damage; + /* Wrap fb for scale/rotate usage */ + struct drm_fb *wrap[2]; + int next_wrap; + struct vaapi_recorder *recorder; struct wl_listener recorder_frame_listener; @@ -590,6 +597,10 @@ struct drm_output { submit_frame_cb virtual_submit_frame; bool state_invalid; + + bool is_mirror; + + pixman_box32_t plane_bounds; }; static inline struct drm_head * --- a/libweston/backend-drm/state-propose.c 2023-06-12 20:36:17.706156047 +0800 +++ b/libweston/backend-drm/state-propose.c 2023-06-13 15:21:06.240116411 +0800 @@ -54,6 +54,21 @@ static const char *const drm_output_prop [DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY] = "plane-only state" }; +static bool +drm_is_mirroring(struct drm_backend *b) +{ + struct drm_output *tmp; + + if (!b->mirror_mode) + return false; + + wl_list_for_each(tmp, &b->compositor->output_list, base.link) + if (tmp->is_mirror) + return true; + + return false; +} + static const char * drm_propose_state_mode_to_string(enum drm_output_propose_state_mode mode) { @@ -466,7 +481,7 @@ drm_output_try_view_on_plane(struct drm_ switch (plane->type) { case WDRM_PLANE_TYPE_CURSOR: - if (b->cursors_are_broken) { + if (b->cursors_are_broken || drm_is_mirroring(b)) { availability = NO_PLANES_ACCEPTED; goto out; } @@ -1102,7 +1117,10 @@ drm_assign_planes(struct weston_output * 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) { + /* Force single plane in mirror mode */ + if (drm_is_mirroring(b)) { + drm_debug(b, "\t[state] no overlay plane in mirror mode\n"); + } else if (!b->sprites_are_broken && !output->virtual) { drm_debug(b, "\t[repaint] trying planes-only build state\n"); state = drm_output_propose_state(output_base, pending_state, mode); if (!state) { --- a/libweston/compositor.c 2023-06-12 20:36:17.706156047 +0800 +++ b/libweston/compositor.c 2023-06-13 15:21:06.244116421 +0800 @@ -1409,7 +1409,7 @@ weston_view_assign_output(struct weston_ } pixman_region32_init(®ion); wl_list_for_each(output, &ec->output_list, link) { - if (output->destroying) + if (!weston_output_valid(output)) continue; pixman_region32_intersect(®ion, &ev->transform.boundingbox, @@ -5213,6 +5213,9 @@ bind_output(struct wl_client *client, static void weston_head_add_global(struct weston_head *head) { + if (head->global || !weston_output_valid(head->output)) + return; + head->global = wl_global_create(head->compositor->wl_display, &wl_output_interface, 3, head, bind_output); @@ -5248,6 +5251,15 @@ weston_head_remove_global(struct weston_ wl_list_init(&head->xdg_output_resource_list); } +static void +weston_head_update_global(struct weston_head *head) +{ + if (weston_output_valid(head->output)) + weston_head_add_global(head); + else + weston_head_remove_global(head); +} + /** Get the backing object of wl_output * * \param resource A wl_output protocol object. @@ -6045,12 +6057,18 @@ weston_compositor_reflow_outputs(struct struct weston_output *resized_output, int delta_width) { struct weston_output *output; + struct weston_head *head; bool start_resizing = false; if (!delta_width) return; wl_list_for_each(output, &compositor->output_list, link) { + wl_list_for_each(head, &output->head_list, output_link) + weston_head_update_global(head); + + if (!weston_output_valid(output)) + continue; if (output == resized_output) { start_resizing = true; continue; @@ -6265,11 +6283,11 @@ weston_compositor_add_output(struct west wl_list_insert(compositor->output_list.prev, &output->link); output->enabled = true; + wl_signal_emit(&compositor->output_created_signal, output); + wl_list_for_each(head, &output->head_list, output_link) weston_head_add_global(head); - wl_signal_emit(&compositor->output_created_signal, output); - /* * Use view_list, as paint nodes have not been created for this * output yet. Any existing view might touch this new output. --- a/libweston/input.c 2023-06-12 20:36:17.706156047 +0800 +++ b/libweston/input.c 2023-06-13 15:21:06.244116421 +0800 @@ -1688,6 +1688,10 @@ weston_pointer_clamp(struct weston_point wl_list_for_each(output, &ec->output_list, link) { if (pointer->seat->output && pointer->seat->output != output) continue; + + if (output->unavailable) + continue; + if (pixman_region32_contains_point(&output->region, x, y, NULL)) valid = 1; @@ -1757,6 +1761,9 @@ weston_pointer_handle_output_destroy(str y = wl_fixed_to_int(pointer->y); wl_list_for_each(output, &ec->output_list, link) { + if (output->unavailable) + continue; + if (pixman_region32_contains_point(&output->region, x, y, NULL)) return;