From 392dde7bedfaf40ed830bbb3e959e72cf0ddc21b Mon Sep 17 00:00:00 2001 From: Alexander Courtis Date: Fri, 7 Jan 2022 11:25:46 +1100 Subject: [PATCH 1/8] wlr_mirror_v1: adds mirroring of variable source on a single destination output --- examples/meson.build | 3 + examples/mirror.c | 519 ++++++++++++++++++++++++++ include/wlr/types/wlr_matrix.h | 5 + include/wlr/types/wlr_mirror_v1.h | 123 +++++++ include/wlr/types/wlr_output.h | 3 + types/meson.build | 1 + types/wlr_matrix.c | 27 ++ types/wlr_mirror_v1.c | 590 ++++++++++++++++++++++++++++++ 8 files changed, 1271 insertions(+) create mode 100644 examples/mirror.c create mode 100644 include/wlr/types/wlr_mirror_v1.h create mode 100644 types/wlr_mirror_v1.c diff --git a/examples/meson.build b/examples/meson.build index 26d103bbb..1e3d763b4 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -56,6 +56,9 @@ compositors = { 'src': 'scene-graph.c', 'proto': ['xdg-shell'], }, + 'mirror': { + 'src': 'mirror.c', + }, } clients = { diff --git a/examples/mirror.c b/examples/mirror.c new file mode 100644 index 000000000..580164241 --- /dev/null +++ b/examples/mirror.c @@ -0,0 +1,519 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Demonstrates wlr_mirror_v1. Comments describe mirror specific code. + * + * Mirrors the source output (src) on the destination output (dst). + * + * A moving square portion of the src (blue) is rendered on the dst (initially + * red). The cursor is included in the mirrored content. + */ +static const char usage[] = +"usage: mirror \n" +" e.g. mirror eDP-1 HDMI-A-1\n" +"keys:\n" +" a: WLR_MIRROR_V1_SCALE_ASPECT\n" +" f: WLR_MIRROR_V1_SCALE_FULL\n" +" c: WLR_MIRROR_V1_SCALE_CENTER\n" +" esc: exit\n"; + +struct sample_state { + struct wl_display *display; + struct wlr_renderer *renderer; + struct wlr_allocator *allocator; + struct wlr_xcursor_manager *xcursor_manager; + struct wlr_cursor *cursor; + struct wlr_output_layout *layout; + + struct wl_listener new_output; + struct wl_listener new_input; + struct wl_listener cursor_motion; + + struct sample_mirror *mirror; + struct sample_output *output_src; + struct sample_output *output_dst; + + char *src_name; + char *dst_name; +}; + +// lifetime: mirror session +struct sample_mirror { + struct sample_state *state; + + struct wlr_mirror_v1 *wlr_mirror; + + struct wlr_mirror_v1_params params; + + struct wl_listener ready; + struct wl_listener destroy; + + int dx, dy; + struct wlr_box box; + struct timespec last_request; +}; + +struct sample_output { + struct sample_state *state; + struct wlr_output *wlr_output; + + int width; + int height; + + struct wl_listener frame; + struct wl_listener destroy; + struct wl_listener restore; + struct wl_listener remove; +}; + +struct sample_keyboard { + struct sample_state *state; + struct wlr_input_device *device; + struct wl_listener key; + struct wl_listener destroy; +}; + +void start_mirror(struct sample_state *state, enum wlr_mirror_v1_scale scale); +void end_mirror(struct sample_state *state); +void handle_mirror_ready(struct wl_listener *listener, void *data); +void handle_mirror_destroy(struct wl_listener *listener, void *data); +void handle_output_remove(struct wl_listener *listener, void *data); +void handle_output_restore(struct wl_listener *listener, void *data); +void handle_output_frame(struct wl_listener *listener, void *data); +void handle_output_destroy(struct wl_listener *listener, void *data); +void handle_new_input(struct wl_listener *listener, void *data); +void handle_new_output(struct wl_listener *listener, void *data); +void handle_cursor_motion(struct wl_listener *listener, void *data); +void handle_keyboard_key(struct wl_listener *listener, void *data); +void handle_keyboard_destroy(struct wl_listener *listener, void *data); +void render_rects(struct wlr_renderer *renderer, struct sample_output *output, float colour[]); + +// start a mirror session +void start_mirror(struct sample_state *state, enum wlr_mirror_v1_scale scale) { + struct sample_output *output_src = state->output_src; + struct sample_output *output_dst = state->output_dst; + if (!output_src || !output_dst) { + return; + } + + wlr_log(WLR_DEBUG, "mirror start dst '%s'", state->output_dst->wlr_output->name); + + struct sample_mirror *mirror = calloc(1, sizeof(struct sample_mirror)); + mirror->state = state; + state->mirror = mirror; + + int wh = MIN(output_src->width, output_src->height) * 3 / 4; + mirror->box.width = wh; + mirror->box.height = wh; + mirror->dx = 1; + mirror->dy = 1; + + // params immutable over the session + mirror->params.scale = scale; + mirror->params.overlay_cursor = true; + mirror->params.output_dst = state->output_dst->wlr_output; + + wl_array_init(&mirror->params.output_srcs); + struct wlr_output **output_src_ptr = + wl_array_add(&mirror->params.output_srcs, sizeof(struct output_src_ptr*)); + *output_src_ptr = state->output_src->wlr_output; + + // stop rendering frames on this output + wl_list_remove(&state->output_dst->frame.link); + wl_list_init(&state->output_dst->frame.link); + + struct wlr_mirror_v1 *wlr_mirror = wlr_mirror_v1_create(&mirror->params); + mirror->wlr_mirror = wlr_mirror; + + // ready events enabling us to make requests for the upcoming commit + wl_signal_add(&wlr_mirror->events.ready, &mirror->ready); + mirror->ready.notify = handle_mirror_ready; + + // destroy at session end + wl_signal_add(&wlr_mirror->events.destroy, &mirror->destroy); + mirror->destroy.notify = handle_mirror_destroy; +} + +// request that we end the session +void end_mirror(struct sample_state *state) { + wlr_log(WLR_DEBUG, "mirror end dst '%s'", state->output_dst->wlr_output->name); + + if (state->mirror) { + // immediately emits wlr_mirror_v1::events::destroy + wlr_mirror_v1_destroy(state->mirror->wlr_mirror); + } +} + +// mirror is ready to display content from an output; called at src precommit +void handle_mirror_ready(struct wl_listener *listener, void *data) { + struct sample_mirror *mirror = wl_container_of(listener, mirror, ready); + struct sample_state *state = mirror->state; + struct sample_output *output_src = state->output_src; + struct wlr_mirror_v1 *wlr_mirror = state->mirror->wlr_mirror; + struct wlr_output *wlr_output = data; + + // only request for src + if (wlr_output != state->output_src->wlr_output) { + return; + } + + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + long ms = (now.tv_sec - mirror->last_request.tv_sec) * 1000 + + (now.tv_nsec - mirror->last_request.tv_nsec) / 1000000; + if (ms > 10) { + mirror->last_request = now; + + // request a portion of src + wlr_mirror_v1_request_box(wlr_mirror, wlr_output, mirror->box); + + if ((mirror->box.x + mirror->box.width + mirror->dx) > output_src->width) { + mirror->dx = -1; + } else if ((mirror->box.x + mirror->dx) < 0) { + mirror->dx = 1; + } + if ((mirror->box.y + mirror->box.height + mirror->dy) > output_src->height) { + mirror->dy = -1; + } else if ((mirror->box.y + mirror->dy) < 0) { + mirror->dy = 1; + } + mirror->box.x += mirror->dx; + mirror->box.y += mirror->dy; + } +} + +// mirror session is over +void handle_mirror_destroy(struct wl_listener *listener, void *data) { + struct sample_mirror *mirror = wl_container_of(listener, mirror, destroy); + struct sample_state *state = mirror->state; + + wlr_log(WLR_DEBUG, "mirror destroy dst '%s'", state->output_dst->wlr_output->name); + + wl_list_remove(&mirror->ready.link); + wl_list_remove(&mirror->destroy.link); + + wl_array_release(&mirror->params.output_srcs); + free(mirror); + state->mirror = NULL; + + // start rendering our frames again + struct sample_output *output = state->output_dst; + if (output) { + wl_signal_add(&output->wlr_output->events.frame, &output->frame); + output->frame.notify = handle_output_frame; + } +} + +// shrinking rects alternating colour / grey +void render_rects(struct wlr_renderer *renderer, struct sample_output *output, + float colour[]) { + struct wlr_output *wlr_output = output->wlr_output; + + static float colour_black[] = { 0, 0, 0, 1 }; + wlr_renderer_clear(renderer, colour_black); + + struct wlr_box box_rect = { + .x = 0, + .y = 0, + .width = output->width, + .height = output->height, + }; + int delta_box = MIN(output->width / 16, output->height / 16); + + static float colour_grey[] = { 0.05, 0.05, 0.05, 1 }; + static float delta_grey = 0.002; + static struct timespec last_grey = { 0 }; + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + long ms = (now.tv_sec - last_grey.tv_sec) * 1000 + + (now.tv_nsec - last_grey.tv_nsec) / 1000000; + if (ms > 10) { + last_grey = now; + if (colour_grey[0] + delta_grey > 0.2) { + delta_grey = -0.002; + } else if (colour_grey[0] - delta_grey < 0.05) { + delta_grey = 0.002; + } + colour_grey[0] += delta_grey; + colour_grey[1] += delta_grey; + colour_grey[2] += delta_grey; + } + + bool grey = false; + while (box_rect.x < output->width / 2 && box_rect.y < output->height / 2) { + wlr_render_rect(renderer, &box_rect, grey ? colour_grey : colour, + wlr_output->transform_matrix); + grey = !grey; + box_rect.x += delta_box; + box_rect.y += delta_box; + box_rect.width -= 2 * delta_box; + box_rect.height -= 2 * delta_box; + } +} + +// will not be invoked for dst during mirror session +void handle_output_frame(struct wl_listener *listener, void *data) { + struct sample_output *output = wl_container_of(listener, output, frame); + struct sample_state *state = output->state; + struct wlr_output *wlr_output = output->wlr_output; + struct wlr_renderer *renderer = state->renderer; + + static float colour_red[] = { 0.75, 0, 0, 1 }; + static float colour_blue[] = { 0, 0, 0.75, 1 }; + + wlr_output_attach_render(wlr_output, NULL); + wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height); + + render_rects(renderer, output, output == state->output_src ? colour_blue : colour_red); + + wlr_output_render_software_cursors(wlr_output, NULL); + wlr_renderer_end(renderer); + wlr_output_commit(wlr_output); +} + +void handle_cursor_motion(struct wl_listener *listener, void *data) { + struct sample_state *state = wl_container_of(listener, state, cursor_motion); + struct wlr_event_pointer_motion *event = data; + wlr_cursor_move(state->cursor, event->device, event->delta_x, event->delta_y); +} + +void handle_keyboard_key(struct wl_listener *listener, void *data) { + struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key); + struct sample_state *state = keyboard->state; + struct wlr_event_keyboard_key *event = data; + + uint32_t keycode = event->keycode + 8; + const xkb_keysym_t *syms; + int nsyms = xkb_state_key_get_syms(keyboard->device->keyboard->xkb_state, keycode, &syms); + bool start_end_mirror = false; + enum wlr_mirror_v1_scale scale; + + for (int i = 0; i < nsyms; i++) { + xkb_keysym_t sym = syms[i]; + if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { + switch (sym) { + case XKB_KEY_Escape: + wl_display_terminate(state->display); + break; + case XKB_KEY_f: + scale = WLR_MIRROR_V1_SCALE_FULL; + start_end_mirror = true; + break; + case XKB_KEY_a: + scale = WLR_MIRROR_V1_SCALE_ASPECT; + start_end_mirror = true; + break; + case XKB_KEY_c: + scale = WLR_MIRROR_V1_SCALE_CENTER; + start_end_mirror = true; + break; + default: + break; + } + } + } + + if (start_end_mirror) { + if (state->mirror) { + end_mirror(state); + } else { + start_mirror(state, scale); + } + } +} + +void handle_output_destroy(struct wl_listener *listener, void *data) { + struct sample_output *output = wl_container_of(listener, output, destroy); + struct sample_state *state = output->state; + + wlr_log(WLR_DEBUG, "output destroyed '%s'", output->wlr_output->name); + + wlr_output_layout_remove(state->layout, output->wlr_output); + wl_list_remove(&output->frame.link); + wl_list_remove(&output->destroy.link); + if (output == state->output_dst) { + state->output_dst = NULL; + } else if (output == state->output_src) { + state->output_src = NULL; + } + free(output); +} + +void handle_new_output(struct wl_listener *listener, void *data) { + struct wlr_output *wlr_output = data; + struct sample_state *state = wl_container_of(listener, state, new_output); + + struct sample_output *output = calloc(1, sizeof(struct sample_output)); + output->wlr_output = wlr_output; + output->state = state; + + if (strcmp(wlr_output->name, state->src_name) == 0) { + wlr_log(WLR_DEBUG, "found src '%s'", wlr_output->name); + state->output_src = output; + } else if (strcmp(wlr_output->name, state->dst_name) == 0) { + wlr_log(WLR_DEBUG, "found dst '%s'", wlr_output->name); + state->output_dst = output; + } else { + free(output); + wlr_log(WLR_DEBUG, "ignoring extraneous output '%s'", wlr_output->name); + return; + } + + wlr_output_init_render(wlr_output, state->allocator, state->renderer); + + wl_signal_add(&wlr_output->events.destroy, &output->destroy); + output->destroy.notify = handle_output_destroy; + wlr_output_enable(wlr_output, true); + wlr_output_layout_add_auto(state->layout, output->wlr_output); + struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); + if (mode != NULL) { + wlr_output_set_mode(wlr_output, mode); + } + wlr_xcursor_manager_load(state->xcursor_manager, wlr_output->scale); + wlr_xcursor_manager_set_cursor_image(state->xcursor_manager, "left_ptr", state->cursor); + + // draw frames, stopping for dst when we start the mirror session + wl_signal_add(&wlr_output->events.frame, &output->frame); + output->frame.notify = handle_output_frame; + + if (!wlr_output_commit(wlr_output)) { + wlr_log(WLR_ERROR, "Failed to setup output %s, exiting", wlr_output->name); + exit(1); + } + + wlr_output_transformed_resolution(wlr_output, &output->width, &output->height); +} + +void handle_keyboard_destroy(struct wl_listener *listener, void *data) { + struct sample_keyboard *keyboard = + wl_container_of(listener, keyboard, destroy); + wl_list_remove(&keyboard->destroy.link); + wl_list_remove(&keyboard->key.link); + free(keyboard); +} + +void handle_new_input(struct wl_listener *listener, void *data) { + struct wlr_input_device *device = data; + struct sample_state *state = wl_container_of(listener, state, new_input); + switch (device->type) { + case WLR_INPUT_DEVICE_POINTER: + case WLR_INPUT_DEVICE_TOUCH: + case WLR_INPUT_DEVICE_TABLET_TOOL: + wlr_cursor_attach_input_device(state->cursor, device); + break; + case WLR_INPUT_DEVICE_KEYBOARD: + ; + struct sample_keyboard *keyboard = calloc(1, sizeof(struct sample_keyboard)); + keyboard->device = device; + keyboard->state = state; + wl_signal_add(&device->events.destroy, &keyboard->destroy); + keyboard->destroy.notify = handle_keyboard_destroy; + wl_signal_add(&device->keyboard->events.key, &keyboard->key); + keyboard->key.notify = handle_keyboard_key; + struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + if (!context) { + wlr_log(WLR_ERROR, "Failed to create XKB context"); + exit(1); + } + struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL, + XKB_KEYMAP_COMPILE_NO_FLAGS); + if (!keymap) { + wlr_log(WLR_ERROR, "Failed to create XKB keymap"); + exit(1); + } + wlr_keyboard_set_keymap(device->keyboard, keymap); + xkb_keymap_unref(keymap); + xkb_context_unref(context); + break; + default: + break; + } +} + +int main(int argc, char *argv[]) { + if (argc != 3) { + fprintf(stderr, "%s", usage); + exit(1); + } + + wlr_log_init(WLR_DEBUG, NULL); + + struct wl_display *display = wl_display_create(); + struct sample_state state = { + .display = display, + .src_name = strdup(argv[1]), + .dst_name = strdup(argv[2]), + }; + + struct wlr_backend *backend = wlr_backend_autocreate(display); + if (!backend) { + exit(1); + } + + state.renderer = wlr_renderer_autocreate(backend); + state.allocator = wlr_allocator_autocreate(backend, state.renderer); + state.cursor = wlr_cursor_create(); + state.layout = wlr_output_layout_create(); + wlr_cursor_attach_output_layout(state.cursor, state.layout); + state.xcursor_manager = wlr_xcursor_manager_create("default", 24); + if (!state.xcursor_manager) { + wlr_log(WLR_ERROR, "Failed to load left_ptr cursor"); + exit(1); + } + + wl_signal_add(&backend->events.new_output, &state.new_output); + state.new_output.notify = handle_new_output; + wl_signal_add(&backend->events.new_input, &state.new_input); + state.new_input.notify = handle_new_input; + wl_signal_add(&state.cursor->events.motion, &state.cursor_motion); + state.cursor_motion.notify = handle_cursor_motion; + + if (!wlr_backend_start(backend)) { + wlr_log(WLR_ERROR, "Failed to start backend"); + wlr_backend_destroy(backend); + exit(1); + } + + if (!state.output_src) { + wlr_log(WLR_ERROR, "missing src %s, exiting", state.src_name); + exit(1); + } + if (!state.output_dst) { + wlr_log(WLR_ERROR, "missing dst %s, exiting", state.dst_name); + exit(1); + } + + // restrict cursor to src + wlr_cursor_warp_absolute(state.cursor, NULL, 1, 1); + wlr_cursor_map_to_output(state.cursor, state.output_src->wlr_output); + wlr_cursor_warp_absolute(state.cursor, NULL, 0, 0); + + wl_display_run(display); + + // stops and destroys the mirror + wl_display_destroy(display); + + wlr_xcursor_manager_destroy(state.xcursor_manager); + wlr_cursor_destroy(state.cursor); + wlr_output_layout_destroy(state.layout); + + free(state.src_name); + free(state.dst_name); + return 0; +} + diff --git a/include/wlr/types/wlr_matrix.h b/include/wlr/types/wlr_matrix.h index 1a8b5be53..7ddfd9d7d 100644 --- a/include/wlr/types/wlr_matrix.h +++ b/include/wlr/types/wlr_matrix.h @@ -43,6 +43,11 @@ void wlr_matrix_rotate(float mat[static 9], float rad); void wlr_matrix_transform(float mat[static 9], enum wl_output_transform transform); +/** Writes a transformation matrix which applies the inverse of + * transform to mat, such that transform × inverse = identity */ +void wlr_matrix_transform_inv(float mat[static 9], + enum wl_output_transform transform); + /** Writes a 2D orthographic projection matrix to mat of (width, height) with a * specified wl_output_transform. * diff --git a/include/wlr/types/wlr_mirror_v1.h b/include/wlr/types/wlr_mirror_v1.h new file mode 100644 index 000000000..d9bf1cc8e --- /dev/null +++ b/include/wlr/types/wlr_mirror_v1.h @@ -0,0 +1,123 @@ +/* + * This an unstable interface of wlroots. No guarantees are made regarding the + * future consistency of this API. + */ +#ifndef WLR_USE_UNSTABLE +#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" +#endif + +#ifndef WLR_TYPES_wlr_mirror_v1_V1_H +#define WLR_TYPES_wlr_mirror_v1_V1_H + +#include +#include +#include + +/** + * Allows mirroring: rendering some contents of one output (the src) on another + * output (the dst). dst is fixed for the duration of the session, src may vary. + * + * On output_srcs precommit, wlr_mirror_v1::ready is emitted. The compositor may + * call wlr_mirror_v1_request_ to request to render a frame on dst. + * + * Compositor must not render on dst for the duration of the session. + * + * Multiple sessions may run concurrently and one session may mirror another. + * + * Session will end: + * disable/destroy of dst or all srcs + * wlr_mirror_v1_request_box called with box outside of src + * wlr_mirror_v1_destroy + */ + +enum wlr_mirror_v1_scale { + /** + * src will be stretched to cover dst, distorting if necessary. + */ + WLR_MIRROR_V1_SCALE_FULL, + /** + * src will be stretched to the width or the height of dst, preserving the + * aspect ratio. + */ + WLR_MIRROR_V1_SCALE_ASPECT, + /** + * src will be rendered 1:1 at the center of dst. Content may be lost. + */ + WLR_MIRROR_V1_SCALE_CENTER, +}; + +/** + * Immutable over session. + */ +struct wlr_mirror_v1_params { + + enum wlr_mirror_v1_scale scale; + + /** + * Render the src cursor on dst. + */ + bool overlay_cursor; + + /** + * srcs to send wlr_mirror_v1::events::ready + */ + struct wl_array output_srcs; + + /** + * dst, will have mirror_dst set for the duration of the session. + */ + struct wlr_output *output_dst; +}; + +struct wlr_mirror_v1_state; +struct wlr_mirror_v1 { + + struct { + /** + * Ready to render a frame. Handler should call wlr_mirror_v1_request_ + * Emitted at precommit, passes potential src. + */ + struct wl_signal ready; + + /** + * Mirror session is over. + */ + struct wl_signal destroy; + } events; + + // private state + struct wlr_mirror_v1_state *state; +}; + +/** + * Create a mirror session. + * + * Compositor must stop rendering on dst immediately after this. + */ +struct wlr_mirror_v1 *wlr_mirror_v1_create(struct wlr_mirror_v1_params *params); + +/** + * Destroy a mirror session. + * + * Compositor may resume rendering on dst. + */ +void wlr_mirror_v1_destroy(struct wlr_mirror_v1 *mirror); + +/** + * Request a blank frame on dst. + * + * Should be invoked during the wlr_mirror_v1::events::ready handler. + */ +void wlr_mirror_v1_request_blank(struct wlr_mirror_v1 *mirror); + +/** + * Request a frame to render a box within src on dst. box is in output local + * coordinates, with respect to its transformation. + * + * Should be invoked during the wlr_mirror_v1::events::ready handler. + */ +void wlr_mirror_v1_request_box(struct wlr_mirror_v1 *mirror, + struct wlr_output *output_src, struct wlr_box box); + +#endif + diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 791023d2e..bd2d02059 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -151,6 +151,9 @@ struct wlr_output { // Commit sequence number. Incremented on each commit, may overflow. uint32_t commit_seq; + // dst for an active wlr_mirror_v1 session + bool mirror_dst; + struct { // Request to render a frame struct wl_signal frame; diff --git a/types/meson.build b/types/meson.build index 1694ac94a..e64e98626 100644 --- a/types/meson.build +++ b/types/meson.build @@ -44,6 +44,7 @@ wlr_files += files( 'wlr_layer_shell_v1.c', 'wlr_linux_dmabuf_v1.c', 'wlr_matrix.c', + 'wlr_mirror_v1.c', 'wlr_output_damage.c', 'wlr_output_layout.c', 'wlr_output_management_v1.c', diff --git a/types/wlr_matrix.c b/types/wlr_matrix.c index a1b2b31a1..2be9ee1e0 100644 --- a/types/wlr_matrix.c +++ b/types/wlr_matrix.c @@ -117,6 +117,33 @@ void wlr_matrix_transform(float mat[static 9], wlr_matrix_multiply(mat, mat, transforms[transform]); } +void wlr_matrix_transform_inv(float mat[static 9], + enum wl_output_transform transform) { + enum wl_output_transform inv; + + switch (transform) { + case WL_OUTPUT_TRANSFORM_90: + inv = WL_OUTPUT_TRANSFORM_270; + break; + case WL_OUTPUT_TRANSFORM_180: + inv = WL_OUTPUT_TRANSFORM_180; + break; + case WL_OUTPUT_TRANSFORM_270: + inv = WL_OUTPUT_TRANSFORM_90; + break; + case WL_OUTPUT_TRANSFORM_NORMAL: + case WL_OUTPUT_TRANSFORM_FLIPPED: + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + case WL_OUTPUT_TRANSFORM_FLIPPED_180: + case WL_OUTPUT_TRANSFORM_FLIPPED_270: + default: + inv = transform; + break; + } + + wlr_matrix_multiply(mat, mat, transforms[inv]); +} + // Equivalent to glOrtho(0, width, 0, height, 1, -1) with the transform applied void wlr_matrix_projection(float mat[static 9], int width, int height, enum wl_output_transform transform) { diff --git a/types/wlr_mirror_v1.c b/types/wlr_mirror_v1.c new file mode 100644 index 000000000..0acfcdc4e --- /dev/null +++ b/types/wlr_mirror_v1.c @@ -0,0 +1,590 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct wlr_mirror_v1_output_src { + struct wl_list link; + + struct wlr_mirror_v1_state *state; + + struct wlr_output *output; + + struct wl_listener enable; + struct wl_listener commit; + struct wl_listener precommit; + struct wl_listener destroy; +}; + +struct wlr_mirror_v1_stats { + long requested_boxes; + long rendered_boxes; + + long requested_blanks; + long rendered_blanks; + + long frames_dropped; + long buffers_incomplete; + long dmabufs_unavailable; +}; + +/** + * All immutable during session, except noted. + */ +struct wlr_mirror_v1_state { + struct wlr_mirror_v1 *mirror; + + struct wlr_mirror_v1_params params; + + struct wlr_output *output_src; // mutable + struct wlr_output *output_dst; + + struct wl_list m_output_srcs; // wlr_mirror_v1_output_src::link + + struct wlr_texture *texture; // mutable + struct wlr_box box_src; // mutable + bool needs_blank; // mutable + bool cursor_locked; // mutable + + // events (ready) may result in a call to wlr_mirror_v1_destroy. + // During emission, wlr_mirror_v1_destroy will not free mirror (specifically + // the wl_signal) and state. + // mirror and state will be free'd after wlr_signal_emit_safe is complete + // and has cleaned up the signal's list. + bool signal_emitting, needs_state_mirror_free; + + struct wl_listener output_dst_enable; + struct wl_listener output_dst_frame; + struct wl_listener output_dst_destroy; + + struct wlr_mirror_v1_stats stats; +}; + +/** + * BEGIN helper functions + */ + +/** + * Swaps v, h depending on rotation of the transform. + */ +static void rotate_v_h(int32_t *v_rotated, int32_t *h_rotated, + enum wl_output_transform transform, int32_t v, int32_t h) { + if (transform % 2 == 0) { + *v_rotated = v; + *h_rotated = h; + } else { + *v_rotated = h; + *h_rotated = v; + } +} + +/** + * Updates a box with absolute coordinates inside a (0, 0, width, height) + * box without rotating or translating it. + */ +static void calculate_absolute_box(struct wlr_box *absolute, + struct wlr_box *relative, enum wl_output_transform transform, + int32_t width, int32_t height) { + + rotate_v_h(&absolute->x, &absolute->y, transform, relative->x, relative->y); + rotate_v_h(&absolute->width, &absolute->height, transform, relative->width, relative->height); + + switch (transform) { + case WL_OUTPUT_TRANSFORM_180: + case WL_OUTPUT_TRANSFORM_270: + case WL_OUTPUT_TRANSFORM_FLIPPED: + case WL_OUTPUT_TRANSFORM_FLIPPED_270: + absolute->x = width - absolute->width - absolute->x; + break; + case WL_OUTPUT_TRANSFORM_NORMAL: + case WL_OUTPUT_TRANSFORM_90: + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + case WL_OUTPUT_TRANSFORM_FLIPPED_180: + default: + break; + } + switch (transform) { + case WL_OUTPUT_TRANSFORM_90: + case WL_OUTPUT_TRANSFORM_180: + case WL_OUTPUT_TRANSFORM_FLIPPED_180: + case WL_OUTPUT_TRANSFORM_FLIPPED_270: + absolute->y = height - absolute->height - absolute->y; + break; + case WL_OUTPUT_TRANSFORM_NORMAL: + case WL_OUTPUT_TRANSFORM_270: + case WL_OUTPUT_TRANSFORM_FLIPPED: + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + default: + break; + } +} + +/** + * Position a box according to the scale method. It will be rotated from src + * to dst. + */ +static void calculate_dst_box(struct wlr_fbox *box_dst, + enum wlr_mirror_v1_scale scale_method, + enum wl_output_transform transform_src, + enum wl_output_transform transform_dst, + int32_t width_src, int32_t height_src, + int32_t width_dst, int32_t height_dst) { + + double width_scaled, height_scaled; + int32_t width_src_rotated, height_src_rotated; + int32_t width_dst_rotated, height_dst_rotated; + + bool src_rotated = transform_src % 2 != 0; + + rotate_v_h(&width_src_rotated, &height_src_rotated, transform_src, width_src, height_src); + rotate_v_h(&width_dst_rotated, &height_dst_rotated, transform_dst, width_dst, height_dst); + + switch (scale_method) { + case WLR_MIRROR_V1_SCALE_CENTER: + box_dst->width = width_src; + box_dst->height = height_src; + box_dst->x = (((float) width_dst_rotated) - width_src_rotated) / 2; + box_dst->y = (((float) height_dst_rotated) - height_src_rotated) / 2; + break; + case WLR_MIRROR_V1_SCALE_ASPECT: + if (width_dst_rotated * height_src_rotated > height_dst_rotated * width_src_rotated) { + // expand to dst height + width_scaled = ((float) width_src_rotated) * height_dst_rotated / height_src_rotated; + height_scaled = height_dst_rotated; + } else { + // expand to dst width + width_scaled = width_dst_rotated; + height_scaled = ((float) height_src_rotated) * width_dst_rotated / width_src_rotated; + } + if (src_rotated) { + box_dst->width = height_scaled; + box_dst->height = width_scaled; + } else { + box_dst->width = width_scaled; + box_dst->height = height_scaled; + } + box_dst->x = (((float) width_dst_rotated) - width_scaled) / 2; + box_dst->y = (((float) height_dst_rotated) - height_scaled) / 2; + break; + case WLR_MIRROR_V1_SCALE_FULL: + default: + if (src_rotated) { + box_dst->width = height_dst_rotated; + box_dst->height = width_dst_rotated; + } else { + box_dst->width = width_dst_rotated; + box_dst->height = height_dst_rotated; + } + box_dst->x = 0; + box_dst->y = 0; + break; + } +} + +/** + * Produce a transformation matrix that rotates/translates a box to the + * dst. + */ +static void calculate_render_matrix(float mat[static 9], struct wlr_fbox *box_dst, + struct wlr_output *output_src, struct wlr_output *output_dst) { + + // position at the dst + wlr_matrix_identity(mat); + wlr_matrix_translate(mat, box_dst->x, box_dst->y); + + // un-rotate and transform from src + if (output_src->transform % 2 == 0) { + wlr_matrix_translate(mat, box_dst->width / 2.0, box_dst->height / 2.0); + } else { + wlr_matrix_translate(mat, box_dst->height / 2.0, box_dst->width / 2.0); + } + wlr_matrix_transform_inv(mat, output_src->transform); + wlr_matrix_translate(mat, - box_dst->width / 2.0, - box_dst->height / 2.0); + + // scale to the dst + wlr_matrix_scale(mat, box_dst->width, box_dst->height); + + // apply the dst transform + wlr_matrix_multiply(mat, output_dst->transform_matrix, mat); +} + +static void schedule_frame_dst(struct wlr_mirror_v1_state *state) { + + wlr_output_schedule_frame(state->output_dst); + + wl_list_remove(&state->output_dst_frame.link); + wl_signal_add(&state->output_dst->events.frame, &state->output_dst_frame); +} + +/** + * Remove all listeners for a src and remove it from state::m_output_srcs + * Invoke wlr_mirror_v1_destroy if no other srcs remain. + */ +static void remove_output_src(struct wlr_mirror_v1_output_src *src) { + struct wlr_mirror_v1_state *state = src->state; + + wl_list_remove(&src->commit.link); + wl_list_remove(&src->enable.link); + wl_list_remove(&src->precommit.link); + wl_list_remove(&src->destroy.link); + wl_list_remove(&src->link); + free(src); + + if (wl_list_length(&state->m_output_srcs) == 0) { + wlr_mirror_v1_destroy(state->mirror); + } +} + +/** + * END helper functions + */ + +/** + * BEGIN wlr_mirror handler functions + */ + +static void output_src_handle_precommit(struct wl_listener *listener, void *data) { + struct wlr_mirror_v1_output_src *m_output_src = + wl_container_of(listener, m_output_src, precommit); + struct wlr_mirror_v1_state *state = m_output_src->state; + struct wlr_mirror_v1 *mirror = state->mirror; + + state->signal_emitting = true; + wlr_signal_emit_safe(&mirror->events.ready, m_output_src->output); + state->signal_emitting = false; + if (state->needs_state_mirror_free) { + free(state); + free(mirror); + } +} + +static void output_src_handle_commit(struct wl_listener *listener, void *data) { + struct wlr_mirror_v1_output_src *m_output_src = wl_container_of(listener, m_output_src, commit); + struct wlr_mirror_v1_state *state = m_output_src->state; + struct wlr_output *output_src = m_output_src->output; + struct wlr_output_event_commit *event = data; + + state->output_src = output_src; + + wl_list_remove(&m_output_src->commit.link); + wl_list_init(&m_output_src->commit.link); + + if (state->texture) { + state->stats.frames_dropped++; + wlr_texture_destroy(state->texture); + state->texture = NULL; + } + + if (!(event->committed & WLR_OUTPUT_STATE_BUFFER)) { + state->stats.buffers_incomplete++; + return; + } + + if (state->params.overlay_cursor) { + wlr_output_lock_software_cursors(output_src, true); + } + + struct wlr_dmabuf_attributes attribs = {0}; + + wlr_output_lock_attach_render(output_src, true); + + if (wlr_buffer_get_dmabuf(event->buffer, &attribs)) { + state->texture = wlr_texture_from_dmabuf(output_src->renderer, &attribs); + schedule_frame_dst(state); + } else { + state->stats.dmabufs_unavailable++; + } + + wlr_output_lock_attach_render(output_src, false); + + if (state->params.overlay_cursor) { + wlr_output_lock_software_cursors(output_src, false); + } +} + +static void output_dst_handle_frame(struct wl_listener *listener, void *data) { + struct wlr_mirror_v1_state *state = wl_container_of(listener, state, output_dst_frame); + + wl_list_remove(&state->output_dst_frame.link); + wl_list_init(&state->output_dst_frame.link); + + struct wlr_output *output_dst = state->output_dst; + struct wlr_output *output_src = state->output_src; + + wlr_output_attach_render(output_dst, NULL); + + wlr_renderer_begin(output_dst->renderer, output_dst->width, output_dst->height); + + static float col_blank[] = { 0, 0, 0, 1 }; + wlr_renderer_clear(output_dst->renderer, col_blank); + + if (state->needs_blank) { + state->stats.rendered_blanks++; + + state->needs_blank = false; + + } else if (output_src && state->texture) { + state->stats.rendered_boxes++; + + // tranform src box to real coordinates for the src + struct wlr_box box_src; + calculate_absolute_box(&box_src, &state->box_src, output_src->transform, + output_src->width, output_src->height); + + // scale and position a box for the dst + struct wlr_fbox fbox_dst = {0}; + calculate_dst_box(&fbox_dst, state->params.scale, + output_src->transform, output_dst->transform, + box_src.width, box_src.height, + output_dst->width, output_dst->height); + + // transform to dst + float mat[9]; + calculate_render_matrix(mat, &fbox_dst, output_src, output_dst); + + // render the subtexture + struct wlr_fbox fbox_sub = { + .x = box_src.x, + .y = box_src.y, + .width = box_src.width, + .height = box_src.height, + }; + wlr_render_subtexture_with_matrix(output_dst->renderer, state->texture, &fbox_sub, mat, 1.0f); + + wlr_texture_destroy(state->texture); + state->texture = NULL; + } + + wlr_renderer_end(output_dst->renderer); + wlr_output_commit(output_dst); + + state->output_src = NULL; +} + +static void output_src_handle_enable(struct wl_listener *listener, void *data) { + struct wlr_mirror_v1_output_src *src = wl_container_of(listener, src, enable); + + if (!src->output->enabled) { + wlr_log(WLR_DEBUG, "Mirror src '%s' disabled", src->output->name); + remove_output_src(src); + } +} + +static void output_src_handle_destroy(struct wl_listener *listener, void *data) { + struct wlr_mirror_v1_output_src *src = wl_container_of(listener, src, destroy); + + wlr_log(WLR_DEBUG, "Mirror src '%s' destroyed", src->output->name); + + remove_output_src(src); +} + +static void output_dst_handle_enable(struct wl_listener *listener, void *data) { + struct wlr_mirror_v1_state *state = wl_container_of(listener, state, output_dst_enable); + struct wlr_mirror_v1 *mirror = state->mirror; + + if (!state->output_dst->enabled) { + wlr_log(WLR_DEBUG, "Mirror dst '%s' disabled", state->output_dst->name); + wlr_mirror_v1_destroy(mirror); + } +} + +static void output_dst_handle_destroy(struct wl_listener *listener, void *data) { + struct wlr_mirror_v1_state *state = wl_container_of(listener, state, output_dst_destroy); + struct wlr_mirror_v1 *mirror = state->mirror; + + wlr_log(WLR_DEBUG, "Mirror dst '%s' destroyed", state->output_dst->name); + + wlr_mirror_v1_destroy(mirror); +} + +/** + * END wlr_mirror handler functions + */ + +/** + * BEGIN public functions + */ + +struct wlr_mirror_v1 *wlr_mirror_v1_create(struct wlr_mirror_v1_params *params) { + if (!params->output_dst->enabled) { + wlr_log(WLR_ERROR, "Mirror dst '%s' not enabled", params->output_dst->name); + return NULL; + } + if (params->output_dst->mirror_dst) { + wlr_log(WLR_ERROR, "Mirror dst '%s' in use by another mirror session", + params->output_dst->name); + return NULL; + } + struct wlr_output **output_src_ptr; + wl_array_for_each(output_src_ptr, ¶ms->output_srcs) { + if (!(*output_src_ptr)->enabled) { + wlr_log(WLR_ERROR, "Mirror src '%s' not enabled", (*output_src_ptr)->name); + return NULL; + } + } + + struct wlr_mirror_v1 *mirror = calloc(1, sizeof(struct wlr_mirror_v1)); + mirror->state = calloc(1, sizeof(struct wlr_mirror_v1_state)); + struct wlr_mirror_v1_state *state = mirror->state; + state->mirror = mirror; + state->output_dst = params->output_dst; + + wl_list_init(&state->m_output_srcs); + wl_signal_init(&mirror->events.ready); + wl_signal_init(&mirror->events.destroy); + + // clone params + memcpy(&state->params, params, sizeof(struct wlr_mirror_v1_params)); + wl_array_init(&state->params.output_srcs); + wl_array_copy(&state->params.output_srcs, ¶ms->output_srcs); + + // dst events + wl_list_init(&state->output_dst_frame.link); + state->output_dst_frame.notify = output_dst_handle_frame; + + wl_list_init(&state->output_dst_enable.link); + wl_signal_add(&state->output_dst->events.enable, &state->output_dst_enable); + state->output_dst_enable.notify = output_dst_handle_enable; + + wl_list_init(&state->output_dst_destroy.link); + wl_signal_add(&state->output_dst->events.destroy, &state->output_dst_destroy); + state->output_dst_destroy.notify = output_dst_handle_destroy; + + wlr_log(WLR_DEBUG, "Mirror creating dst '%s'", state->output_dst->name); + + // srcs events + wl_array_for_each(output_src_ptr, &state->params.output_srcs) { + struct wlr_mirror_v1_output_src *m_output_src = + calloc(1, sizeof(struct wlr_mirror_v1_output_src)); + wl_list_insert(state->m_output_srcs.prev, &m_output_src->link); + + m_output_src->state = state; + m_output_src->output = *output_src_ptr; + + wl_list_init(&m_output_src->commit.link); + + wl_list_init(&m_output_src->enable.link); + wl_signal_add(&m_output_src->output->events.enable, &m_output_src->enable); + m_output_src->enable.notify = output_src_handle_enable; + + wl_list_init(&m_output_src->precommit.link); + wl_signal_add(&m_output_src->output->events.precommit, &m_output_src->precommit); + m_output_src->precommit.notify = output_src_handle_precommit; + + wl_list_init(&m_output_src->destroy.link); + wl_signal_add(&m_output_src->output->events.destroy, &m_output_src->destroy); + m_output_src->destroy.notify = output_src_handle_destroy; + + wlr_log(WLR_DEBUG, " src '%s'", m_output_src->output->name); + } + + // blank initially, in case compositor delays requests + state->needs_blank = true; + schedule_frame_dst(state); + + state->output_dst->mirror_dst = true; + + return mirror; +} + +void wlr_mirror_v1_destroy(struct wlr_mirror_v1 *mirror) { + if (!mirror) { + return; + } + struct wlr_mirror_v1_state *state = mirror->state; + + wlr_log(WLR_DEBUG, "Mirror destroying dst '%s': " + "requested_boxes:%ld, rendered_boxes:%ld, " + "requested_blanks:%ld, rendered_blanks:%ld, " + "frames_dropped:%ld, buffers_incomplete:%ld, " + "dmabufs_unavailable:%ld", + state->output_dst->name, + state->stats.requested_boxes, state->stats.rendered_boxes, + state->stats.requested_blanks, state->stats.rendered_blanks, + state->stats.frames_dropped, state->stats.buffers_incomplete, + state->stats.dmabufs_unavailable); + + // dst output events + wl_list_remove(&state->output_dst_enable.link); + wl_list_remove(&state->output_dst_frame.link); + wl_list_remove(&state->output_dst_destroy.link); + + // all src output events + struct wlr_mirror_v1_output_src *src, *next; + wl_list_for_each_safe(src, next, &state->m_output_srcs, link) { + wl_list_remove(&src->commit.link); + wl_list_remove(&src->enable.link); + wl_list_remove(&src->precommit.link); + wl_list_remove(&src->destroy.link); + wl_list_remove(&src->link); + free(src); + } + + // destroy any frames in flight + if (state->texture) { + wlr_texture_destroy(state->texture); + state->texture = NULL; + } + + // the compositor may reclaim dst + state->output_dst->mirror_dst = false; + + // end the user's mirror "session" + wlr_signal_emit_safe(&mirror->events.destroy, mirror); + + wl_array_release(&state->params.output_srcs); + if (state->signal_emitting) { + state->needs_state_mirror_free = true; + } else { + free(state); + free(mirror); + } +} + +void wlr_mirror_v1_request_blank(struct wlr_mirror_v1 *mirror) { + struct wlr_mirror_v1_state *state = mirror->state; + + state->needs_blank = true; + + schedule_frame_dst(state); + + mirror->state->stats.requested_blanks++; +} + +void wlr_mirror_v1_request_box(struct wlr_mirror_v1 *mirror, + struct wlr_output *output_src, struct wlr_box box) { + struct wlr_mirror_v1_state *state = mirror->state; + + state->needs_blank = false; + + // restrict the box to the src + struct wlr_box box_output = { 0 }; + wlr_output_transformed_resolution(output_src, &box_output.width, &box_output.height); + if (!wlr_box_intersection(&state->box_src, &box_output, &box)) { + wlr_log(WLR_ERROR, "Mirror box not within src, ending session."); + wlr_mirror_v1_destroy(mirror); + return; + } + + // listen for a commit on the specified output only + struct wlr_mirror_v1_output_src *m_output_src; + wl_list_for_each(m_output_src, &state->m_output_srcs, link) { + if (m_output_src->output == output_src) { + wl_list_remove(&m_output_src->commit.link); + wl_signal_add(&m_output_src->output->events.commit, &m_output_src->commit); + m_output_src->commit.notify = output_src_handle_commit; + } + } + + state->stats.requested_boxes++; +} + +/** + * END public functions + */ + From eac618687b1e330a54e13ad6914a1eb3fc7decdc Mon Sep 17 00:00:00 2001 From: Alexander Courtis Date: Mon, 10 Jan 2022 11:50:40 +1100 Subject: [PATCH 2/8] wlr_mirror: replace wlr_output->mirror_dst with wlr_addon --- include/wlr/types/wlr_mirror_v1.h | 6 ++++++ include/wlr/types/wlr_output.h | 3 --- include/wlr/util/addon.h | 3 +++ types/wlr_mirror_v1.c | 34 ++++++++++++++++++++++++++++--- util/addon.c | 13 ++++++++++++ 5 files changed, 53 insertions(+), 6 deletions(-) diff --git a/include/wlr/types/wlr_mirror_v1.h b/include/wlr/types/wlr_mirror_v1.h index d9bf1cc8e..ec692a302 100644 --- a/include/wlr/types/wlr_mirror_v1.h +++ b/include/wlr/types/wlr_mirror_v1.h @@ -11,6 +11,7 @@ #include #include +#include #include /** @@ -119,5 +120,10 @@ void wlr_mirror_v1_request_blank(struct wlr_mirror_v1 *mirror); void wlr_mirror_v1_request_box(struct wlr_mirror_v1 *mirror, struct wlr_output *output_src, struct wlr_box box); +/** + * Output is in use as a dst by another mirror session. + */ +bool wlr_mirror_v1_output_is_dst(struct wlr_output *output); + #endif diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index bd2d02059..791023d2e 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -151,9 +151,6 @@ struct wlr_output { // Commit sequence number. Incremented on each commit, may overflow. uint32_t commit_seq; - // dst for an active wlr_mirror_v1 session - bool mirror_dst; - struct { // Request to render a frame struct wl_signal frame; diff --git a/include/wlr/util/addon.h b/include/wlr/util/addon.h index 382252ae2..c777f623a 100644 --- a/include/wlr/util/addon.h +++ b/include/wlr/util/addon.h @@ -40,4 +40,7 @@ void wlr_addon_finish(struct wlr_addon *addon); struct wlr_addon *wlr_addon_find(struct wlr_addon_set *set, const void *owner, const struct wlr_addon_interface *impl); +void wlr_addon_find_all(struct wl_array *all, struct wlr_addon_set *set, + const struct wlr_addon_interface *impl); + #endif diff --git a/types/wlr_mirror_v1.c b/types/wlr_mirror_v1.c index 0acfcdc4e..3e4f5af0b 100644 --- a/types/wlr_mirror_v1.c +++ b/types/wlr_mirror_v1.c @@ -41,6 +41,8 @@ struct wlr_mirror_v1_state { struct wlr_mirror_v1_params params; + struct wlr_addon output_dst_addon; + struct wlr_output *output_src; // mutable struct wlr_output *output_dst; @@ -406,6 +408,23 @@ static void output_dst_handle_destroy(struct wl_listener *listener, void *data) * END wlr_mirror handler functions */ +/** + * BEGIN addons + */ + +static void output_dst_addon_handle_destroy(struct wlr_addon *addon) { + // wlr_mirror_v1_destroy finishes addon, following output_dst_handle_destroy +} + +static const struct wlr_addon_interface output_dst_addon_impl = { + .name = "wlr_mirror_output_dst", + .destroy = output_dst_addon_handle_destroy, +}; + +/** + * END addons + */ + /** * BEGIN public functions */ @@ -415,7 +434,7 @@ struct wlr_mirror_v1 *wlr_mirror_v1_create(struct wlr_mirror_v1_params *params) wlr_log(WLR_ERROR, "Mirror dst '%s' not enabled", params->output_dst->name); return NULL; } - if (params->output_dst->mirror_dst) { + if (wlr_mirror_v1_output_is_dst(params->output_dst)) { wlr_log(WLR_ERROR, "Mirror dst '%s' in use by another mirror session", params->output_dst->name); return NULL; @@ -487,7 +506,8 @@ struct wlr_mirror_v1 *wlr_mirror_v1_create(struct wlr_mirror_v1_params *params) state->needs_blank = true; schedule_frame_dst(state); - state->output_dst->mirror_dst = true; + wlr_addon_init(&state->output_dst_addon, &state->output_dst->addons, mirror, + &output_dst_addon_impl); return mirror; } @@ -532,7 +552,7 @@ void wlr_mirror_v1_destroy(struct wlr_mirror_v1 *mirror) { } // the compositor may reclaim dst - state->output_dst->mirror_dst = false; + wlr_addon_finish(&state->output_dst_addon); // end the user's mirror "session" wlr_signal_emit_safe(&mirror->events.destroy, mirror); @@ -584,6 +604,14 @@ void wlr_mirror_v1_request_box(struct wlr_mirror_v1 *mirror, state->stats.requested_boxes++; } +bool wlr_mirror_v1_output_is_dst(struct wlr_output *output) { + struct wl_array addons; + wlr_addon_find_all(&addons, &output->addons, &output_dst_addon_impl); + bool is_dst = addons.size > 0; + wl_array_release(&addons); + return is_dst; +} + /** * END public functions */ diff --git a/util/addon.c b/util/addon.c index d9e13978d..ea7d89e4f 100644 --- a/util/addon.c +++ b/util/addon.c @@ -45,3 +45,16 @@ struct wlr_addon *wlr_addon_find(struct wlr_addon_set *set, const void *owner, } return NULL; } + +void wlr_addon_find_all(struct wl_array *all, struct wlr_addon_set *set, + const struct wlr_addon_interface *impl) { + wl_array_init(all); + struct wlr_addon *addon; + wl_list_for_each(addon, &set->addons, link) { + if (addon->impl == impl) { + struct wlr_addon **addon_ptr = wl_array_add(all, sizeof(addon_ptr)); + *addon_ptr = addon; + } + } +} + From a6c38f82c41b99828c081ee781717a810f19d8de Mon Sep 17 00:00:00 2001 From: Alexander Courtis Date: Mon, 10 Jan 2022 12:16:26 +1100 Subject: [PATCH 3/8] MR 3421 fix: wlr_mirror_v1 -> wlr_mirror --- examples/mirror.c | 36 +++---- .../types/{wlr_mirror_v1.h => wlr_mirror.h} | 46 ++++---- include/wlr/types/wlr_output.h | 2 +- types/meson.build | 2 +- types/{wlr_mirror_v1.c => wlr_mirror.c} | 100 +++++++++--------- 5 files changed, 93 insertions(+), 93 deletions(-) rename include/wlr/types/{wlr_mirror_v1.h => wlr_mirror.h} (63%) rename types/{wlr_mirror_v1.c => wlr_mirror.c} (84%) diff --git a/examples/mirror.c b/examples/mirror.c index 580164241..cbac82110 100644 --- a/examples/mirror.c +++ b/examples/mirror.c @@ -9,14 +9,14 @@ #include #include #include -#include +#include #include #include #include #include /** - * Demonstrates wlr_mirror_v1. Comments describe mirror specific code. + * Demonstrates wlr_mirror. Comments describe mirror specific code. * * Mirrors the source output (src) on the destination output (dst). * @@ -27,9 +27,9 @@ static const char usage[] = "usage: mirror \n" " e.g. mirror eDP-1 HDMI-A-1\n" "keys:\n" -" a: WLR_MIRROR_V1_SCALE_ASPECT\n" -" f: WLR_MIRROR_V1_SCALE_FULL\n" -" c: WLR_MIRROR_V1_SCALE_CENTER\n" +" a: WLR_MIRROR_SCALE_ASPECT\n" +" f: WLR_MIRROR_SCALE_FULL\n" +" c: WLR_MIRROR_SCALE_CENTER\n" " esc: exit\n"; struct sample_state { @@ -56,9 +56,9 @@ struct sample_state { struct sample_mirror { struct sample_state *state; - struct wlr_mirror_v1 *wlr_mirror; + struct wlr_mirror *wlr_mirror; - struct wlr_mirror_v1_params params; + struct wlr_mirror_params params; struct wl_listener ready; struct wl_listener destroy; @@ -88,7 +88,7 @@ struct sample_keyboard { struct wl_listener destroy; }; -void start_mirror(struct sample_state *state, enum wlr_mirror_v1_scale scale); +void start_mirror(struct sample_state *state, enum wlr_mirror_scale scale); void end_mirror(struct sample_state *state); void handle_mirror_ready(struct wl_listener *listener, void *data); void handle_mirror_destroy(struct wl_listener *listener, void *data); @@ -104,7 +104,7 @@ void handle_keyboard_destroy(struct wl_listener *listener, void *data); void render_rects(struct wlr_renderer *renderer, struct sample_output *output, float colour[]); // start a mirror session -void start_mirror(struct sample_state *state, enum wlr_mirror_v1_scale scale) { +void start_mirror(struct sample_state *state, enum wlr_mirror_scale scale) { struct sample_output *output_src = state->output_src; struct sample_output *output_dst = state->output_dst; if (!output_src || !output_dst) { @@ -137,7 +137,7 @@ void start_mirror(struct sample_state *state, enum wlr_mirror_v1_scale scale) { wl_list_remove(&state->output_dst->frame.link); wl_list_init(&state->output_dst->frame.link); - struct wlr_mirror_v1 *wlr_mirror = wlr_mirror_v1_create(&mirror->params); + struct wlr_mirror *wlr_mirror = wlr_mirror_create(&mirror->params); mirror->wlr_mirror = wlr_mirror; // ready events enabling us to make requests for the upcoming commit @@ -154,8 +154,8 @@ void end_mirror(struct sample_state *state) { wlr_log(WLR_DEBUG, "mirror end dst '%s'", state->output_dst->wlr_output->name); if (state->mirror) { - // immediately emits wlr_mirror_v1::events::destroy - wlr_mirror_v1_destroy(state->mirror->wlr_mirror); + // immediately emits wlr_mirror::events::destroy + wlr_mirror_destroy(state->mirror->wlr_mirror); } } @@ -164,7 +164,7 @@ void handle_mirror_ready(struct wl_listener *listener, void *data) { struct sample_mirror *mirror = wl_container_of(listener, mirror, ready); struct sample_state *state = mirror->state; struct sample_output *output_src = state->output_src; - struct wlr_mirror_v1 *wlr_mirror = state->mirror->wlr_mirror; + struct wlr_mirror *wlr_mirror = state->mirror->wlr_mirror; struct wlr_output *wlr_output = data; // only request for src @@ -180,7 +180,7 @@ void handle_mirror_ready(struct wl_listener *listener, void *data) { mirror->last_request = now; // request a portion of src - wlr_mirror_v1_request_box(wlr_mirror, wlr_output, mirror->box); + wlr_mirror_request_box(wlr_mirror, wlr_output, mirror->box); if ((mirror->box.x + mirror->box.width + mirror->dx) > output_src->width) { mirror->dx = -1; @@ -301,7 +301,7 @@ void handle_keyboard_key(struct wl_listener *listener, void *data) { const xkb_keysym_t *syms; int nsyms = xkb_state_key_get_syms(keyboard->device->keyboard->xkb_state, keycode, &syms); bool start_end_mirror = false; - enum wlr_mirror_v1_scale scale; + enum wlr_mirror_scale scale; for (int i = 0; i < nsyms; i++) { xkb_keysym_t sym = syms[i]; @@ -311,15 +311,15 @@ void handle_keyboard_key(struct wl_listener *listener, void *data) { wl_display_terminate(state->display); break; case XKB_KEY_f: - scale = WLR_MIRROR_V1_SCALE_FULL; + scale = WLR_MIRROR_SCALE_FULL; start_end_mirror = true; break; case XKB_KEY_a: - scale = WLR_MIRROR_V1_SCALE_ASPECT; + scale = WLR_MIRROR_SCALE_ASPECT; start_end_mirror = true; break; case XKB_KEY_c: - scale = WLR_MIRROR_V1_SCALE_CENTER; + scale = WLR_MIRROR_SCALE_CENTER; start_end_mirror = true; break; default: diff --git a/include/wlr/types/wlr_mirror_v1.h b/include/wlr/types/wlr_mirror.h similarity index 63% rename from include/wlr/types/wlr_mirror_v1.h rename to include/wlr/types/wlr_mirror.h index d9bf1cc8e..d338b515d 100644 --- a/include/wlr/types/wlr_mirror_v1.h +++ b/include/wlr/types/wlr_mirror.h @@ -6,8 +6,8 @@ #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" #endif -#ifndef WLR_TYPES_wlr_mirror_v1_V1_H -#define WLR_TYPES_wlr_mirror_v1_V1_H +#ifndef WLR_TYPES_WLR_MIRROR_H +#define WLR_TYPES_WLR_MIRROR_H #include #include @@ -17,8 +17,8 @@ * Allows mirroring: rendering some contents of one output (the src) on another * output (the dst). dst is fixed for the duration of the session, src may vary. * - * On output_srcs precommit, wlr_mirror_v1::ready is emitted. The compositor may - * call wlr_mirror_v1_request_ to request to render a frame on dst. + * On output_srcs precommit, wlr_mirror::ready is emitted. The compositor may + * call wlr_mirror_request_ to request to render a frame on dst. * * Compositor must not render on dst for the duration of the session. * @@ -26,32 +26,32 @@ * * Session will end: * disable/destroy of dst or all srcs - * wlr_mirror_v1_request_box called with box outside of src - * wlr_mirror_v1_destroy + * wlr_mirror_request_box called with box outside of src + * wlr_mirror_destroy */ -enum wlr_mirror_v1_scale { +enum wlr_mirror_scale { /** * src will be stretched to cover dst, distorting if necessary. */ - WLR_MIRROR_V1_SCALE_FULL, + WLR_MIRROR_SCALE_FULL, /** * src will be stretched to the width or the height of dst, preserving the * aspect ratio. */ - WLR_MIRROR_V1_SCALE_ASPECT, + WLR_MIRROR_SCALE_ASPECT, /** * src will be rendered 1:1 at the center of dst. Content may be lost. */ - WLR_MIRROR_V1_SCALE_CENTER, + WLR_MIRROR_SCALE_CENTER, }; /** * Immutable over session. */ -struct wlr_mirror_v1_params { +struct wlr_mirror_params { - enum wlr_mirror_v1_scale scale; + enum wlr_mirror_scale scale; /** * Render the src cursor on dst. @@ -59,7 +59,7 @@ struct wlr_mirror_v1_params { bool overlay_cursor; /** - * srcs to send wlr_mirror_v1::events::ready + * srcs to send wlr_mirror::events::ready */ struct wl_array output_srcs; @@ -69,12 +69,12 @@ struct wlr_mirror_v1_params { struct wlr_output *output_dst; }; -struct wlr_mirror_v1_state; -struct wlr_mirror_v1 { +struct wlr_mirror_state; +struct wlr_mirror { struct { /** - * Ready to render a frame. Handler should call wlr_mirror_v1_request_ + * Ready to render a frame. Handler should call wlr_mirror_request_ * Emitted at precommit, passes potential src. */ struct wl_signal ready; @@ -86,7 +86,7 @@ struct wlr_mirror_v1 { } events; // private state - struct wlr_mirror_v1_state *state; + struct wlr_mirror_state *state; }; /** @@ -94,29 +94,29 @@ struct wlr_mirror_v1 { * * Compositor must stop rendering on dst immediately after this. */ -struct wlr_mirror_v1 *wlr_mirror_v1_create(struct wlr_mirror_v1_params *params); +struct wlr_mirror *wlr_mirror_create(struct wlr_mirror_params *params); /** * Destroy a mirror session. * * Compositor may resume rendering on dst. */ -void wlr_mirror_v1_destroy(struct wlr_mirror_v1 *mirror); +void wlr_mirror_destroy(struct wlr_mirror *mirror); /** * Request a blank frame on dst. * - * Should be invoked during the wlr_mirror_v1::events::ready handler. + * Should be invoked during the wlr_mirror::events::ready handler. */ -void wlr_mirror_v1_request_blank(struct wlr_mirror_v1 *mirror); +void wlr_mirror_request_blank(struct wlr_mirror *mirror); /** * Request a frame to render a box within src on dst. box is in output local * coordinates, with respect to its transformation. * - * Should be invoked during the wlr_mirror_v1::events::ready handler. + * Should be invoked during the wlr_mirror::events::ready handler. */ -void wlr_mirror_v1_request_box(struct wlr_mirror_v1 *mirror, +void wlr_mirror_request_box(struct wlr_mirror *mirror, struct wlr_output *output_src, struct wlr_box box); #endif diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index bd2d02059..ce9033444 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -151,7 +151,7 @@ struct wlr_output { // Commit sequence number. Incremented on each commit, may overflow. uint32_t commit_seq; - // dst for an active wlr_mirror_v1 session + // dst for an active wlr_mirror session bool mirror_dst; struct { diff --git a/types/meson.build b/types/meson.build index e64e98626..69da0881f 100644 --- a/types/meson.build +++ b/types/meson.build @@ -44,7 +44,7 @@ wlr_files += files( 'wlr_layer_shell_v1.c', 'wlr_linux_dmabuf_v1.c', 'wlr_matrix.c', - 'wlr_mirror_v1.c', + 'wlr_mirror.c', 'wlr_output_damage.c', 'wlr_output_layout.c', 'wlr_output_management_v1.c', diff --git a/types/wlr_mirror_v1.c b/types/wlr_mirror.c similarity index 84% rename from types/wlr_mirror_v1.c rename to types/wlr_mirror.c index 0acfcdc4e..f628c138b 100644 --- a/types/wlr_mirror_v1.c +++ b/types/wlr_mirror.c @@ -3,15 +3,15 @@ #include #include #include -#include +#include #include #include #include -struct wlr_mirror_v1_output_src { +struct wlr_mirror_output_src { struct wl_list link; - struct wlr_mirror_v1_state *state; + struct wlr_mirror_state *state; struct wlr_output *output; @@ -21,7 +21,7 @@ struct wlr_mirror_v1_output_src { struct wl_listener destroy; }; -struct wlr_mirror_v1_stats { +struct wlr_mirror_stats { long requested_boxes; long rendered_boxes; @@ -36,23 +36,23 @@ struct wlr_mirror_v1_stats { /** * All immutable during session, except noted. */ -struct wlr_mirror_v1_state { - struct wlr_mirror_v1 *mirror; +struct wlr_mirror_state { + struct wlr_mirror *mirror; - struct wlr_mirror_v1_params params; + struct wlr_mirror_params params; struct wlr_output *output_src; // mutable struct wlr_output *output_dst; - struct wl_list m_output_srcs; // wlr_mirror_v1_output_src::link + struct wl_list m_output_srcs; // wlr_mirror_output_src::link struct wlr_texture *texture; // mutable struct wlr_box box_src; // mutable bool needs_blank; // mutable bool cursor_locked; // mutable - // events (ready) may result in a call to wlr_mirror_v1_destroy. - // During emission, wlr_mirror_v1_destroy will not free mirror (specifically + // events (ready) may result in a call to wlr_mirror_destroy. + // During emission, wlr_mirror_destroy will not free mirror (specifically // the wl_signal) and state. // mirror and state will be free'd after wlr_signal_emit_safe is complete // and has cleaned up the signal's list. @@ -62,7 +62,7 @@ struct wlr_mirror_v1_state { struct wl_listener output_dst_frame; struct wl_listener output_dst_destroy; - struct wlr_mirror_v1_stats stats; + struct wlr_mirror_stats stats; }; /** @@ -129,7 +129,7 @@ static void calculate_absolute_box(struct wlr_box *absolute, * to dst. */ static void calculate_dst_box(struct wlr_fbox *box_dst, - enum wlr_mirror_v1_scale scale_method, + enum wlr_mirror_scale scale_method, enum wl_output_transform transform_src, enum wl_output_transform transform_dst, int32_t width_src, int32_t height_src, @@ -145,13 +145,13 @@ static void calculate_dst_box(struct wlr_fbox *box_dst, rotate_v_h(&width_dst_rotated, &height_dst_rotated, transform_dst, width_dst, height_dst); switch (scale_method) { - case WLR_MIRROR_V1_SCALE_CENTER: + case WLR_MIRROR_SCALE_CENTER: box_dst->width = width_src; box_dst->height = height_src; box_dst->x = (((float) width_dst_rotated) - width_src_rotated) / 2; box_dst->y = (((float) height_dst_rotated) - height_src_rotated) / 2; break; - case WLR_MIRROR_V1_SCALE_ASPECT: + case WLR_MIRROR_SCALE_ASPECT: if (width_dst_rotated * height_src_rotated > height_dst_rotated * width_src_rotated) { // expand to dst height width_scaled = ((float) width_src_rotated) * height_dst_rotated / height_src_rotated; @@ -171,7 +171,7 @@ static void calculate_dst_box(struct wlr_fbox *box_dst, box_dst->x = (((float) width_dst_rotated) - width_scaled) / 2; box_dst->y = (((float) height_dst_rotated) - height_scaled) / 2; break; - case WLR_MIRROR_V1_SCALE_FULL: + case WLR_MIRROR_SCALE_FULL: default: if (src_rotated) { box_dst->width = height_dst_rotated; @@ -213,7 +213,7 @@ static void calculate_render_matrix(float mat[static 9], struct wlr_fbox *box_ds wlr_matrix_multiply(mat, output_dst->transform_matrix, mat); } -static void schedule_frame_dst(struct wlr_mirror_v1_state *state) { +static void schedule_frame_dst(struct wlr_mirror_state *state) { wlr_output_schedule_frame(state->output_dst); @@ -223,10 +223,10 @@ static void schedule_frame_dst(struct wlr_mirror_v1_state *state) { /** * Remove all listeners for a src and remove it from state::m_output_srcs - * Invoke wlr_mirror_v1_destroy if no other srcs remain. + * Invoke wlr_mirror_destroy if no other srcs remain. */ -static void remove_output_src(struct wlr_mirror_v1_output_src *src) { - struct wlr_mirror_v1_state *state = src->state; +static void remove_output_src(struct wlr_mirror_output_src *src) { + struct wlr_mirror_state *state = src->state; wl_list_remove(&src->commit.link); wl_list_remove(&src->enable.link); @@ -236,7 +236,7 @@ static void remove_output_src(struct wlr_mirror_v1_output_src *src) { free(src); if (wl_list_length(&state->m_output_srcs) == 0) { - wlr_mirror_v1_destroy(state->mirror); + wlr_mirror_destroy(state->mirror); } } @@ -249,10 +249,10 @@ static void remove_output_src(struct wlr_mirror_v1_output_src *src) { */ static void output_src_handle_precommit(struct wl_listener *listener, void *data) { - struct wlr_mirror_v1_output_src *m_output_src = + struct wlr_mirror_output_src *m_output_src = wl_container_of(listener, m_output_src, precommit); - struct wlr_mirror_v1_state *state = m_output_src->state; - struct wlr_mirror_v1 *mirror = state->mirror; + struct wlr_mirror_state *state = m_output_src->state; + struct wlr_mirror *mirror = state->mirror; state->signal_emitting = true; wlr_signal_emit_safe(&mirror->events.ready, m_output_src->output); @@ -264,8 +264,8 @@ static void output_src_handle_precommit(struct wl_listener *listener, void *data } static void output_src_handle_commit(struct wl_listener *listener, void *data) { - struct wlr_mirror_v1_output_src *m_output_src = wl_container_of(listener, m_output_src, commit); - struct wlr_mirror_v1_state *state = m_output_src->state; + struct wlr_mirror_output_src *m_output_src = wl_container_of(listener, m_output_src, commit); + struct wlr_mirror_state *state = m_output_src->state; struct wlr_output *output_src = m_output_src->output; struct wlr_output_event_commit *event = data; @@ -308,7 +308,7 @@ static void output_src_handle_commit(struct wl_listener *listener, void *data) { } static void output_dst_handle_frame(struct wl_listener *listener, void *data) { - struct wlr_mirror_v1_state *state = wl_container_of(listener, state, output_dst_frame); + struct wlr_mirror_state *state = wl_container_of(listener, state, output_dst_frame); wl_list_remove(&state->output_dst_frame.link); wl_list_init(&state->output_dst_frame.link); @@ -367,7 +367,7 @@ static void output_dst_handle_frame(struct wl_listener *listener, void *data) { } static void output_src_handle_enable(struct wl_listener *listener, void *data) { - struct wlr_mirror_v1_output_src *src = wl_container_of(listener, src, enable); + struct wlr_mirror_output_src *src = wl_container_of(listener, src, enable); if (!src->output->enabled) { wlr_log(WLR_DEBUG, "Mirror src '%s' disabled", src->output->name); @@ -376,7 +376,7 @@ static void output_src_handle_enable(struct wl_listener *listener, void *data) { } static void output_src_handle_destroy(struct wl_listener *listener, void *data) { - struct wlr_mirror_v1_output_src *src = wl_container_of(listener, src, destroy); + struct wlr_mirror_output_src *src = wl_container_of(listener, src, destroy); wlr_log(WLR_DEBUG, "Mirror src '%s' destroyed", src->output->name); @@ -384,22 +384,22 @@ static void output_src_handle_destroy(struct wl_listener *listener, void *data) } static void output_dst_handle_enable(struct wl_listener *listener, void *data) { - struct wlr_mirror_v1_state *state = wl_container_of(listener, state, output_dst_enable); - struct wlr_mirror_v1 *mirror = state->mirror; + struct wlr_mirror_state *state = wl_container_of(listener, state, output_dst_enable); + struct wlr_mirror *mirror = state->mirror; if (!state->output_dst->enabled) { wlr_log(WLR_DEBUG, "Mirror dst '%s' disabled", state->output_dst->name); - wlr_mirror_v1_destroy(mirror); + wlr_mirror_destroy(mirror); } } static void output_dst_handle_destroy(struct wl_listener *listener, void *data) { - struct wlr_mirror_v1_state *state = wl_container_of(listener, state, output_dst_destroy); - struct wlr_mirror_v1 *mirror = state->mirror; + struct wlr_mirror_state *state = wl_container_of(listener, state, output_dst_destroy); + struct wlr_mirror *mirror = state->mirror; wlr_log(WLR_DEBUG, "Mirror dst '%s' destroyed", state->output_dst->name); - wlr_mirror_v1_destroy(mirror); + wlr_mirror_destroy(mirror); } /** @@ -410,7 +410,7 @@ static void output_dst_handle_destroy(struct wl_listener *listener, void *data) * BEGIN public functions */ -struct wlr_mirror_v1 *wlr_mirror_v1_create(struct wlr_mirror_v1_params *params) { +struct wlr_mirror *wlr_mirror_create(struct wlr_mirror_params *params) { if (!params->output_dst->enabled) { wlr_log(WLR_ERROR, "Mirror dst '%s' not enabled", params->output_dst->name); return NULL; @@ -428,9 +428,9 @@ struct wlr_mirror_v1 *wlr_mirror_v1_create(struct wlr_mirror_v1_params *params) } } - struct wlr_mirror_v1 *mirror = calloc(1, sizeof(struct wlr_mirror_v1)); - mirror->state = calloc(1, sizeof(struct wlr_mirror_v1_state)); - struct wlr_mirror_v1_state *state = mirror->state; + struct wlr_mirror *mirror = calloc(1, sizeof(struct wlr_mirror)); + mirror->state = calloc(1, sizeof(struct wlr_mirror_state)); + struct wlr_mirror_state *state = mirror->state; state->mirror = mirror; state->output_dst = params->output_dst; @@ -439,7 +439,7 @@ struct wlr_mirror_v1 *wlr_mirror_v1_create(struct wlr_mirror_v1_params *params) wl_signal_init(&mirror->events.destroy); // clone params - memcpy(&state->params, params, sizeof(struct wlr_mirror_v1_params)); + memcpy(&state->params, params, sizeof(struct wlr_mirror_params)); wl_array_init(&state->params.output_srcs); wl_array_copy(&state->params.output_srcs, ¶ms->output_srcs); @@ -459,8 +459,8 @@ struct wlr_mirror_v1 *wlr_mirror_v1_create(struct wlr_mirror_v1_params *params) // srcs events wl_array_for_each(output_src_ptr, &state->params.output_srcs) { - struct wlr_mirror_v1_output_src *m_output_src = - calloc(1, sizeof(struct wlr_mirror_v1_output_src)); + struct wlr_mirror_output_src *m_output_src = + calloc(1, sizeof(struct wlr_mirror_output_src)); wl_list_insert(state->m_output_srcs.prev, &m_output_src->link); m_output_src->state = state; @@ -492,11 +492,11 @@ struct wlr_mirror_v1 *wlr_mirror_v1_create(struct wlr_mirror_v1_params *params) return mirror; } -void wlr_mirror_v1_destroy(struct wlr_mirror_v1 *mirror) { +void wlr_mirror_destroy(struct wlr_mirror *mirror) { if (!mirror) { return; } - struct wlr_mirror_v1_state *state = mirror->state; + struct wlr_mirror_state *state = mirror->state; wlr_log(WLR_DEBUG, "Mirror destroying dst '%s': " "requested_boxes:%ld, rendered_boxes:%ld, " @@ -515,7 +515,7 @@ void wlr_mirror_v1_destroy(struct wlr_mirror_v1 *mirror) { wl_list_remove(&state->output_dst_destroy.link); // all src output events - struct wlr_mirror_v1_output_src *src, *next; + struct wlr_mirror_output_src *src, *next; wl_list_for_each_safe(src, next, &state->m_output_srcs, link) { wl_list_remove(&src->commit.link); wl_list_remove(&src->enable.link); @@ -546,8 +546,8 @@ void wlr_mirror_v1_destroy(struct wlr_mirror_v1 *mirror) { } } -void wlr_mirror_v1_request_blank(struct wlr_mirror_v1 *mirror) { - struct wlr_mirror_v1_state *state = mirror->state; +void wlr_mirror_request_blank(struct wlr_mirror *mirror) { + struct wlr_mirror_state *state = mirror->state; state->needs_blank = true; @@ -556,9 +556,9 @@ void wlr_mirror_v1_request_blank(struct wlr_mirror_v1 *mirror) { mirror->state->stats.requested_blanks++; } -void wlr_mirror_v1_request_box(struct wlr_mirror_v1 *mirror, +void wlr_mirror_request_box(struct wlr_mirror *mirror, struct wlr_output *output_src, struct wlr_box box) { - struct wlr_mirror_v1_state *state = mirror->state; + struct wlr_mirror_state *state = mirror->state; state->needs_blank = false; @@ -567,12 +567,12 @@ void wlr_mirror_v1_request_box(struct wlr_mirror_v1 *mirror, wlr_output_transformed_resolution(output_src, &box_output.width, &box_output.height); if (!wlr_box_intersection(&state->box_src, &box_output, &box)) { wlr_log(WLR_ERROR, "Mirror box not within src, ending session."); - wlr_mirror_v1_destroy(mirror); + wlr_mirror_destroy(mirror); return; } // listen for a commit on the specified output only - struct wlr_mirror_v1_output_src *m_output_src; + struct wlr_mirror_output_src *m_output_src; wl_list_for_each(m_output_src, &state->m_output_srcs, link) { if (m_output_src->output == output_src) { wl_list_remove(&m_output_src->commit.link); From 23f73e333122106dcbef04c36f063d92787a58b3 Mon Sep 17 00:00:00 2001 From: Alexander Courtis Date: Wed, 12 Jan 2022 14:49:15 +1100 Subject: [PATCH 4/8] MR 3421 change: remove scale param, always scale to dst width or height --- examples/mirror.c | 36 +++++-------------- include/wlr/types/wlr_mirror.h | 20 ++--------- types/wlr_mirror.c | 63 +++++++++++----------------------- 3 files changed, 31 insertions(+), 88 deletions(-) diff --git a/examples/mirror.c b/examples/mirror.c index cbac82110..0eb9d55f0 100644 --- a/examples/mirror.c +++ b/examples/mirror.c @@ -27,9 +27,7 @@ static const char usage[] = "usage: mirror \n" " e.g. mirror eDP-1 HDMI-A-1\n" "keys:\n" -" a: WLR_MIRROR_SCALE_ASPECT\n" -" f: WLR_MIRROR_SCALE_FULL\n" -" c: WLR_MIRROR_SCALE_CENTER\n" +" m: toggle mirroring\n" " esc: exit\n"; struct sample_state { @@ -88,7 +86,7 @@ struct sample_keyboard { struct wl_listener destroy; }; -void start_mirror(struct sample_state *state, enum wlr_mirror_scale scale); +void start_mirror(struct sample_state *state); void end_mirror(struct sample_state *state); void handle_mirror_ready(struct wl_listener *listener, void *data); void handle_mirror_destroy(struct wl_listener *listener, void *data); @@ -104,7 +102,7 @@ void handle_keyboard_destroy(struct wl_listener *listener, void *data); void render_rects(struct wlr_renderer *renderer, struct sample_output *output, float colour[]); // start a mirror session -void start_mirror(struct sample_state *state, enum wlr_mirror_scale scale) { +void start_mirror(struct sample_state *state) { struct sample_output *output_src = state->output_src; struct sample_output *output_dst = state->output_dst; if (!output_src || !output_dst) { @@ -124,7 +122,6 @@ void start_mirror(struct sample_state *state, enum wlr_mirror_scale scale) { mirror->dy = 1; // params immutable over the session - mirror->params.scale = scale; mirror->params.overlay_cursor = true; mirror->params.output_dst = state->output_dst->wlr_output; @@ -300,8 +297,6 @@ void handle_keyboard_key(struct wl_listener *listener, void *data) { uint32_t keycode = event->keycode + 8; const xkb_keysym_t *syms; int nsyms = xkb_state_key_get_syms(keyboard->device->keyboard->xkb_state, keycode, &syms); - bool start_end_mirror = false; - enum wlr_mirror_scale scale; for (int i = 0; i < nsyms; i++) { xkb_keysym_t sym = syms[i]; @@ -310,31 +305,18 @@ void handle_keyboard_key(struct wl_listener *listener, void *data) { case XKB_KEY_Escape: wl_display_terminate(state->display); break; - case XKB_KEY_f: - scale = WLR_MIRROR_SCALE_FULL; - start_end_mirror = true; - break; - case XKB_KEY_a: - scale = WLR_MIRROR_SCALE_ASPECT; - start_end_mirror = true; - break; - case XKB_KEY_c: - scale = WLR_MIRROR_SCALE_CENTER; - start_end_mirror = true; + case XKB_KEY_m: + if (state->mirror) { + end_mirror(state); + } else { + start_mirror(state); + } break; default: break; } } } - - if (start_end_mirror) { - if (state->mirror) { - end_mirror(state); - } else { - start_mirror(state, scale); - } - } } void handle_output_destroy(struct wl_listener *listener, void *data) { diff --git a/include/wlr/types/wlr_mirror.h b/include/wlr/types/wlr_mirror.h index 28387b104..cc80cd9c6 100644 --- a/include/wlr/types/wlr_mirror.h +++ b/include/wlr/types/wlr_mirror.h @@ -18,6 +18,8 @@ * Allows mirroring: rendering some contents of one output (the src) on another * output (the dst). dst is fixed for the duration of the session, src may vary. * + * Content will be scaled to fit the width or height of dst. + * * On output_srcs precommit, wlr_mirror::ready is emitted. The compositor may * call wlr_mirror_request_ to request to render a frame on dst. * @@ -31,29 +33,11 @@ * wlr_mirror_destroy */ -enum wlr_mirror_scale { - /** - * src will be stretched to cover dst, distorting if necessary. - */ - WLR_MIRROR_SCALE_FULL, - /** - * src will be stretched to the width or the height of dst, preserving the - * aspect ratio. - */ - WLR_MIRROR_SCALE_ASPECT, - /** - * src will be rendered 1:1 at the center of dst. Content may be lost. - */ - WLR_MIRROR_SCALE_CENTER, -}; - /** * Immutable over session. */ struct wlr_mirror_params { - enum wlr_mirror_scale scale; - /** * Render the src cursor on dst. */ diff --git a/types/wlr_mirror.c b/types/wlr_mirror.c index 599b1be16..e79c2c79f 100644 --- a/types/wlr_mirror.c +++ b/types/wlr_mirror.c @@ -127,11 +127,10 @@ static void calculate_absolute_box(struct wlr_box *absolute, } /** - * Position a box according to the scale method. It will be rotated from src - * to dst. + * Position a box scaled to fit the width or height of dst. It will be rotated + * from src to dst. */ static void calculate_dst_box(struct wlr_fbox *box_dst, - enum wlr_mirror_scale scale_method, enum wl_output_transform transform_src, enum wl_output_transform transform_dst, int32_t width_src, int32_t height_src, @@ -146,46 +145,24 @@ static void calculate_dst_box(struct wlr_fbox *box_dst, rotate_v_h(&width_src_rotated, &height_src_rotated, transform_src, width_src, height_src); rotate_v_h(&width_dst_rotated, &height_dst_rotated, transform_dst, width_dst, height_dst); - switch (scale_method) { - case WLR_MIRROR_SCALE_CENTER: - box_dst->width = width_src; - box_dst->height = height_src; - box_dst->x = (((float) width_dst_rotated) - width_src_rotated) / 2; - box_dst->y = (((float) height_dst_rotated) - height_src_rotated) / 2; - break; - case WLR_MIRROR_SCALE_ASPECT: - if (width_dst_rotated * height_src_rotated > height_dst_rotated * width_src_rotated) { - // expand to dst height - width_scaled = ((float) width_src_rotated) * height_dst_rotated / height_src_rotated; - height_scaled = height_dst_rotated; - } else { - // expand to dst width - width_scaled = width_dst_rotated; - height_scaled = ((float) height_src_rotated) * width_dst_rotated / width_src_rotated; - } - if (src_rotated) { - box_dst->width = height_scaled; - box_dst->height = width_scaled; - } else { - box_dst->width = width_scaled; - box_dst->height = height_scaled; - } - box_dst->x = (((float) width_dst_rotated) - width_scaled) / 2; - box_dst->y = (((float) height_dst_rotated) - height_scaled) / 2; - break; - case WLR_MIRROR_SCALE_FULL: - default: - if (src_rotated) { - box_dst->width = height_dst_rotated; - box_dst->height = width_dst_rotated; - } else { - box_dst->width = width_dst_rotated; - box_dst->height = height_dst_rotated; - } - box_dst->x = 0; - box_dst->y = 0; - break; + if (width_dst_rotated * height_src_rotated > height_dst_rotated * width_src_rotated) { + // expand to dst height + width_scaled = ((float) width_src_rotated) * height_dst_rotated / height_src_rotated; + height_scaled = height_dst_rotated; + } else { + // expand to dst width + width_scaled = width_dst_rotated; + height_scaled = ((float) height_src_rotated) * width_dst_rotated / width_src_rotated; } + if (src_rotated) { + box_dst->width = height_scaled; + box_dst->height = width_scaled; + } else { + box_dst->width = width_scaled; + box_dst->height = height_scaled; + } + box_dst->x = (((float) width_dst_rotated) - width_scaled) / 2; + box_dst->y = (((float) height_dst_rotated) - height_scaled) / 2; } /** @@ -340,7 +317,7 @@ static void output_dst_handle_frame(struct wl_listener *listener, void *data) { // scale and position a box for the dst struct wlr_fbox fbox_dst = {0}; - calculate_dst_box(&fbox_dst, state->params.scale, + calculate_dst_box(&fbox_dst, output_src->transform, output_dst->transform, box_src.width, box_src.height, output_dst->width, output_dst->height); From c5978ef8ecf33704a49210cec64a2422bee365b3 Mon Sep 17 00:00:00 2001 From: Alexander Courtis Date: Sat, 22 Jan 2022 15:23:41 +1100 Subject: [PATCH 5/8] MR 3421 fix: calculate_render_matrix invokes wlr_matrix_project_box --- types/wlr_mirror.c | 57 +++++++++++++++++++--------------------------- 1 file changed, 23 insertions(+), 34 deletions(-) diff --git a/types/wlr_mirror.c b/types/wlr_mirror.c index e79c2c79f..6195e53bd 100644 --- a/types/wlr_mirror.c +++ b/types/wlr_mirror.c @@ -130,7 +130,7 @@ static void calculate_absolute_box(struct wlr_box *absolute, * Position a box scaled to fit the width or height of dst. It will be rotated * from src to dst. */ -static void calculate_dst_box(struct wlr_fbox *box_dst, +static void calculate_dst_box(struct wlr_box *box_dst, enum wl_output_transform transform_src, enum wl_output_transform transform_dst, int32_t width_src, int32_t height_src, @@ -147,49 +147,38 @@ static void calculate_dst_box(struct wlr_fbox *box_dst, if (width_dst_rotated * height_src_rotated > height_dst_rotated * width_src_rotated) { // expand to dst height - width_scaled = ((float) width_src_rotated) * height_dst_rotated / height_src_rotated; + width_scaled = ((double) width_src_rotated) * height_dst_rotated / height_src_rotated; height_scaled = height_dst_rotated; } else { // expand to dst width width_scaled = width_dst_rotated; - height_scaled = ((float) height_src_rotated) * width_dst_rotated / width_src_rotated; + height_scaled = ((double) height_src_rotated) * width_dst_rotated / width_src_rotated; } if (src_rotated) { - box_dst->width = height_scaled; - box_dst->height = width_scaled; + box_dst->width = round(height_scaled); + box_dst->height = round(width_scaled); } else { - box_dst->width = width_scaled; - box_dst->height = height_scaled; + box_dst->width = round(width_scaled); + box_dst->height = round(height_scaled); } - box_dst->x = (((float) width_dst_rotated) - width_scaled) / 2; - box_dst->y = (((float) height_dst_rotated) - height_scaled) / 2; + box_dst->x = round((((double) width_dst_rotated) - width_scaled) / 2); + box_dst->y = round((((double) height_dst_rotated) - height_scaled) / 2); } /** - * Produce a transformation matrix that rotates/translates a box to the - * dst. + * Produce a transformation matrix that un-transforms from src and transforms to dst. */ -static void calculate_render_matrix(float mat[static 9], struct wlr_fbox *box_dst, - struct wlr_output *output_src, struct wlr_output *output_dst) { +static void calculate_render_matrix(float mat[static 9], struct wlr_box *box_dst, + enum wl_output_transform transform_src, float transform_matrix_dst[static 9]) { - // position at the dst - wlr_matrix_identity(mat); - wlr_matrix_translate(mat, box_dst->x, box_dst->y); + // account for the rotated dimensions of dst + struct wlr_box box_rotated = *box_dst; + rotate_v_h(&box_rotated.width, &box_rotated.height, transform_src, + box_rotated.width, box_rotated.height); - // un-rotate and transform from src - if (output_src->transform % 2 == 0) { - wlr_matrix_translate(mat, box_dst->width / 2.0, box_dst->height / 2.0); - } else { - wlr_matrix_translate(mat, box_dst->height / 2.0, box_dst->width / 2.0); - } - wlr_matrix_transform_inv(mat, output_src->transform); - wlr_matrix_translate(mat, - box_dst->width / 2.0, - box_dst->height / 2.0); - - // scale to the dst - wlr_matrix_scale(mat, box_dst->width, box_dst->height); - - // apply the dst transform - wlr_matrix_multiply(mat, output_dst->transform_matrix, mat); + // both transforms + wlr_matrix_project_box(mat, &box_rotated, wlr_output_transform_invert(transform_src), + 0.0, transform_matrix_dst); } static void schedule_frame_dst(struct wlr_mirror_state *state) { @@ -316,15 +305,15 @@ static void output_dst_handle_frame(struct wl_listener *listener, void *data) { output_src->width, output_src->height); // scale and position a box for the dst - struct wlr_fbox fbox_dst = {0}; - calculate_dst_box(&fbox_dst, + struct wlr_box box_dst = {0}; + calculate_dst_box(&box_dst, output_src->transform, output_dst->transform, box_src.width, box_src.height, output_dst->width, output_dst->height); - // transform to dst + // transform from src to dst float mat[9]; - calculate_render_matrix(mat, &fbox_dst, output_src, output_dst); + calculate_render_matrix(mat, &box_dst, output_src->transform, output_dst->transform_matrix); // render the subtexture struct wlr_fbox fbox_sub = { From 7f9bb0bbec011cea5956d56ab2dcd8544c0d52b5 Mon Sep 17 00:00:00 2001 From: Alexander Courtis Date: Sat, 22 Jan 2022 15:25:14 +1100 Subject: [PATCH 6/8] MR 3421 fix: remove unnecessary wlr_matrix_transform_inv --- include/wlr/types/wlr_matrix.h | 5 ----- types/wlr_matrix.c | 27 --------------------------- 2 files changed, 32 deletions(-) diff --git a/include/wlr/types/wlr_matrix.h b/include/wlr/types/wlr_matrix.h index 7ddfd9d7d..1a8b5be53 100644 --- a/include/wlr/types/wlr_matrix.h +++ b/include/wlr/types/wlr_matrix.h @@ -43,11 +43,6 @@ void wlr_matrix_rotate(float mat[static 9], float rad); void wlr_matrix_transform(float mat[static 9], enum wl_output_transform transform); -/** Writes a transformation matrix which applies the inverse of - * transform to mat, such that transform × inverse = identity */ -void wlr_matrix_transform_inv(float mat[static 9], - enum wl_output_transform transform); - /** Writes a 2D orthographic projection matrix to mat of (width, height) with a * specified wl_output_transform. * diff --git a/types/wlr_matrix.c b/types/wlr_matrix.c index 2be9ee1e0..a1b2b31a1 100644 --- a/types/wlr_matrix.c +++ b/types/wlr_matrix.c @@ -117,33 +117,6 @@ void wlr_matrix_transform(float mat[static 9], wlr_matrix_multiply(mat, mat, transforms[transform]); } -void wlr_matrix_transform_inv(float mat[static 9], - enum wl_output_transform transform) { - enum wl_output_transform inv; - - switch (transform) { - case WL_OUTPUT_TRANSFORM_90: - inv = WL_OUTPUT_TRANSFORM_270; - break; - case WL_OUTPUT_TRANSFORM_180: - inv = WL_OUTPUT_TRANSFORM_180; - break; - case WL_OUTPUT_TRANSFORM_270: - inv = WL_OUTPUT_TRANSFORM_90; - break; - case WL_OUTPUT_TRANSFORM_NORMAL: - case WL_OUTPUT_TRANSFORM_FLIPPED: - case WL_OUTPUT_TRANSFORM_FLIPPED_90: - case WL_OUTPUT_TRANSFORM_FLIPPED_180: - case WL_OUTPUT_TRANSFORM_FLIPPED_270: - default: - inv = transform; - break; - } - - wlr_matrix_multiply(mat, mat, transforms[inv]); -} - // Equivalent to glOrtho(0, width, 0, height, 1, -1) with the transform applied void wlr_matrix_projection(float mat[static 9], int width, int height, enum wl_output_transform transform) { From b8245dcbf33b7a0f7f7a00007c86413eb423be6b Mon Sep 17 00:00:00 2001 From: Alexander Courtis Date: Sat, 22 Jan 2022 15:29:52 +1100 Subject: [PATCH 7/8] MR 3421 fix: remove unnecessary comments and empty lines --- include/wlr/types/wlr_mirror.h | 2 -- types/wlr_mirror.c | 36 ---------------------------------- 2 files changed, 38 deletions(-) diff --git a/include/wlr/types/wlr_mirror.h b/include/wlr/types/wlr_mirror.h index cc80cd9c6..31f5136f3 100644 --- a/include/wlr/types/wlr_mirror.h +++ b/include/wlr/types/wlr_mirror.h @@ -37,7 +37,6 @@ * Immutable over session. */ struct wlr_mirror_params { - /** * Render the src cursor on dst. */ @@ -56,7 +55,6 @@ struct wlr_mirror_params { struct wlr_mirror_state; struct wlr_mirror { - struct { /** * Ready to render a frame. Handler should call wlr_mirror_request_ diff --git a/types/wlr_mirror.c b/types/wlr_mirror.c index 6195e53bd..7b0ce93e0 100644 --- a/types/wlr_mirror.c +++ b/types/wlr_mirror.c @@ -67,10 +67,6 @@ struct wlr_mirror_state { struct wlr_mirror_stats stats; }; -/** - * BEGIN helper functions - */ - /** * Swaps v, h depending on rotation of the transform. */ @@ -92,7 +88,6 @@ static void rotate_v_h(int32_t *v_rotated, int32_t *h_rotated, static void calculate_absolute_box(struct wlr_box *absolute, struct wlr_box *relative, enum wl_output_transform transform, int32_t width, int32_t height) { - rotate_v_h(&absolute->x, &absolute->y, transform, relative->x, relative->y); rotate_v_h(&absolute->width, &absolute->height, transform, relative->width, relative->height); @@ -135,7 +130,6 @@ static void calculate_dst_box(struct wlr_box *box_dst, enum wl_output_transform transform_dst, int32_t width_src, int32_t height_src, int32_t width_dst, int32_t height_dst) { - double width_scaled, height_scaled; int32_t width_src_rotated, height_src_rotated; int32_t width_dst_rotated, height_dst_rotated; @@ -170,7 +164,6 @@ static void calculate_dst_box(struct wlr_box *box_dst, */ static void calculate_render_matrix(float mat[static 9], struct wlr_box *box_dst, enum wl_output_transform transform_src, float transform_matrix_dst[static 9]) { - // account for the rotated dimensions of dst struct wlr_box box_rotated = *box_dst; rotate_v_h(&box_rotated.width, &box_rotated.height, transform_src, @@ -182,7 +175,6 @@ static void calculate_render_matrix(float mat[static 9], struct wlr_box *box_dst } static void schedule_frame_dst(struct wlr_mirror_state *state) { - wlr_output_schedule_frame(state->output_dst); wl_list_remove(&state->output_dst_frame.link); @@ -208,14 +200,6 @@ static void remove_output_src(struct wlr_mirror_output_src *src) { } } -/** - * END helper functions - */ - -/** - * BEGIN wlr_mirror handler functions - */ - static void output_src_handle_precommit(struct wl_listener *listener, void *data) { struct wlr_mirror_output_src *m_output_src = wl_container_of(listener, m_output_src, precommit); @@ -370,14 +354,6 @@ static void output_dst_handle_destroy(struct wl_listener *listener, void *data) wlr_mirror_destroy(mirror); } -/** - * END wlr_mirror handler functions - */ - -/** - * BEGIN addons - */ - static void output_dst_addon_handle_destroy(struct wlr_addon *addon) { // wlr_mirror_v1_destroy finishes addon, following output_dst_handle_destroy } @@ -387,14 +363,6 @@ static const struct wlr_addon_interface output_dst_addon_impl = { .destroy = output_dst_addon_handle_destroy, }; -/** - * END addons - */ - -/** - * BEGIN public functions - */ - struct wlr_mirror *wlr_mirror_create(struct wlr_mirror_params *params) { if (!params->output_dst->enabled) { wlr_log(WLR_ERROR, "Mirror dst '%s' not enabled", params->output_dst->name); @@ -578,7 +546,3 @@ bool wlr_mirror_v1_output_is_dst(struct wlr_output *output) { return is_dst; } -/** - * END public functions - */ - From ca50ac644119bd1311e69bd677215ef1ae7fbb5b Mon Sep 17 00:00:00 2001 From: Alexander Courtis Date: Sat, 22 Jan 2022 15:41:42 +1100 Subject: [PATCH 8/8] MR 3421 fix: use addon destroy event for dst destroy --- types/wlr_mirror.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/types/wlr_mirror.c b/types/wlr_mirror.c index 7b0ce93e0..149e7ef4d 100644 --- a/types/wlr_mirror.c +++ b/types/wlr_mirror.c @@ -62,7 +62,6 @@ struct wlr_mirror_state { struct wl_listener output_dst_enable; struct wl_listener output_dst_frame; - struct wl_listener output_dst_destroy; struct wlr_mirror_stats stats; }; @@ -345,8 +344,8 @@ static void output_dst_handle_enable(struct wl_listener *listener, void *data) { } } -static void output_dst_handle_destroy(struct wl_listener *listener, void *data) { - struct wlr_mirror_state *state = wl_container_of(listener, state, output_dst_destroy); +static void output_dst_addon_handle_destroy(struct wlr_addon *addon) { + struct wlr_mirror_state *state = wl_container_of(addon, state, output_dst_addon); struct wlr_mirror *mirror = state->mirror; wlr_log(WLR_DEBUG, "Mirror dst '%s' destroyed", state->output_dst->name); @@ -354,10 +353,6 @@ static void output_dst_handle_destroy(struct wl_listener *listener, void *data) wlr_mirror_destroy(mirror); } -static void output_dst_addon_handle_destroy(struct wlr_addon *addon) { - // wlr_mirror_v1_destroy finishes addon, following output_dst_handle_destroy -} - static const struct wlr_addon_interface output_dst_addon_impl = { .name = "wlr_mirror_output_dst", .destroy = output_dst_addon_handle_destroy, @@ -404,10 +399,6 @@ struct wlr_mirror *wlr_mirror_create(struct wlr_mirror_params *params) { wl_signal_add(&state->output_dst->events.enable, &state->output_dst_enable); state->output_dst_enable.notify = output_dst_handle_enable; - wl_list_init(&state->output_dst_destroy.link); - wl_signal_add(&state->output_dst->events.destroy, &state->output_dst_destroy); - state->output_dst_destroy.notify = output_dst_handle_destroy; - wlr_log(WLR_DEBUG, "Mirror creating dst '%s'", state->output_dst->name); // srcs events @@ -466,7 +457,6 @@ void wlr_mirror_destroy(struct wlr_mirror *mirror) { // dst output events wl_list_remove(&state->output_dst_enable.link); wl_list_remove(&state->output_dst_frame.link); - wl_list_remove(&state->output_dst_destroy.link); // all src output events struct wlr_mirror_output_src *src, *next;