diff --git a/backend/wayland/backend.c b/backend/wayland/backend.c index 3f56deb41..6b9408c9e 100644 --- a/backend/wayland/backend.c +++ b/backend/wayland/backend.c @@ -236,6 +236,13 @@ static const struct zwp_linux_dmabuf_feedback_v1_listener .tranche_flags = linux_dmabuf_feedback_v1_handle_tranche_flags, }; +static void update_outputs(struct wlr_wl_backend *backend) { + struct wlr_wl_output *output; + wl_list_for_each(output, &backend->outputs, link) { + surface_update(output); + } +} + static void wlr_output_handle_scale(void *data, struct wl_output *wl_output, int32_t factor) { struct wlr_wl_remote_output *output = data; @@ -254,7 +261,8 @@ static void wlr_output_handle_mode(void *data, struct wl_output *wl_output, } static void wlr_output_handle_done(void *data, struct wl_output *wl_output) { - // This is intentionally left blank + struct wlr_wl_remote_output *output = data; + update_outputs(output->backend); } static const struct wl_output_listener output_listener = { @@ -439,6 +447,7 @@ static void registry_global(void *data, struct wl_registry *registry, output->output = wl_output; output->scale = 1; output->name = name; + output->backend = wl; wl_list_insert(&wl->remote_outputs, &output->link); wl_output_add_listener(wl_output, &output_listener, output); diff --git a/backend/wayland/output.c b/backend/wayland/output.c index 24ca43540..f2f4c68df 100644 --- a/backend/wayland/output.c +++ b/backend/wayland/output.c @@ -66,6 +66,12 @@ static void presentation_feedback_destroy( free(feedback); } +static void active_remote_output_destroy( + struct wlr_wl_active_remote_output *output) { + wl_list_remove(&output->link); + free(output); +} + static void presentation_feedback_handle_sync_output(void *data, struct wp_presentation_feedback *feedback, struct wl_output *output) { // This space is intentionally left blank @@ -442,6 +448,12 @@ static bool output_commit(struct wlr_output *wlr_output, } if (state->committed & WLR_OUTPUT_STATE_BUFFER) { + if (output->requested.needs_ack) { + output->requested.needs_ack = false; + xdg_surface_ack_configure(output->xdg_surface, + output->requested.serial); + } + const pixman_region32_t *damage = NULL; if (state->committed & WLR_OUTPUT_STATE_DAMAGE) { damage = &state->damage; @@ -575,6 +587,12 @@ static void output_destroy(struct wlr_output *wlr_output) { presentation_feedback_destroy(feedback); } + struct wlr_wl_active_remote_output *active, *active_tmp; + wl_list_for_each_safe(active, active_tmp, + &output->active_remote_outputs, link) { + active_remote_output_destroy(active); + } + if (output->zxdg_toplevel_decoration_v1) { zxdg_toplevel_decoration_v1_destroy(output->zxdg_toplevel_decoration_v1); } @@ -643,14 +661,41 @@ bool wlr_output_is_wl(struct wlr_output *wlr_output) { return wlr_output->impl == &output_impl; } +void surface_update(struct wlr_wl_output *output) { + if (wl_list_empty(&output->active_remote_outputs)) { + return; + } + + struct wlr_output_state state = { + .committed = WLR_OUTPUT_STATE_MODE, + .mode_type = WLR_OUTPUT_STATE_MODE_CUSTOM, + .custom_mode = { + .width = output->requested.width, + .height = output->requested.height + }, + }; + + wlr_output_send_request_state(&output->wlr_output, &state); +} + static void xdg_surface_handle_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial) { struct wlr_wl_output *output = data; assert(output && output->xdg_surface == xdg_surface); - xdg_surface_ack_configure(xdg_surface, serial); + if (wl_list_empty(&output->active_remote_outputs)) { + xdg_surface_ack_configure(xdg_surface, serial); + wl_surface_commit(output->surface); + return; + } - // nothing else? + output->requested.needs_ack = true; + output->requested.serial = serial; + surface_update(output); + + // if the compositor did not commit a new buffer during the surface_update + // above, assume that the compositor did not acknowledge the configure. + output->requested.needs_ack = false; } static const struct xdg_surface_listener xdg_surface_listener = { @@ -667,12 +712,8 @@ static void xdg_toplevel_handle_configure(void *data, return; } - struct wlr_output_state state = { - .committed = WLR_OUTPUT_STATE_MODE, - .mode_type = WLR_OUTPUT_STATE_MODE_CUSTOM, - .custom_mode = { .width = width, .height = height }, - }; - wlr_output_send_request_state(&output->wlr_output, &state); + output->requested.width = width; + output->requested.height = height; } static void xdg_toplevel_handle_close(void *data, @@ -688,6 +729,50 @@ static const struct xdg_toplevel_listener xdg_toplevel_listener = { .close = xdg_toplevel_handle_close, }; +static void surface_enter(void *data, struct wl_surface *wl_surface, + struct wl_output *wl_output) { + struct wlr_wl_output *output = data; + + struct wlr_wl_remote_output *remote_output; + wl_list_for_each(remote_output, &output->backend->remote_outputs, link) { + if (remote_output->output != wl_output) { + continue; + } + + struct wlr_wl_active_remote_output *active = calloc(1, sizeof(*active)); + if (!active) { + return; + } + + active->remote_output = remote_output; + + wl_list_insert(&output->active_remote_outputs, &active->link); + surface_update(output); + break; + } +} + +static void surface_leave(void *data, struct wl_surface *wl_surface, + struct wl_output *wl_output) { + struct wlr_wl_output *output = data; + + struct wlr_wl_active_remote_output *active; + wl_list_for_each(active, &output->active_remote_outputs, link) { + if (active->remote_output->output != wl_output) { + continue; + } + + active_remote_output_destroy(active); + surface_update(output); + break; + } +} + +static const struct wl_surface_listener surface_listener = { + .enter = &surface_enter, + .leave = &surface_leave, +}; + struct wlr_output *wlr_wl_output_create(struct wlr_backend *wlr_backend) { struct wlr_wl_backend *backend = get_wl_backend_from_backend(wlr_backend); if (!backend->started) { @@ -720,6 +805,7 @@ struct wlr_output *wlr_wl_output_create(struct wlr_backend *wlr_backend) { output->backend = backend; wl_list_init(&output->presentation_feedbacks); + wl_list_init(&output->active_remote_outputs); output->surface = wl_compositor_create_surface(backend->compositor); if (!output->surface) { @@ -759,6 +845,7 @@ struct wlr_output *wlr_wl_output_create(struct wlr_backend *wlr_backend) { &xdg_surface_listener, output); xdg_toplevel_add_listener(output->xdg_toplevel, &xdg_toplevel_listener, output); + wl_surface_add_listener(output->surface, &surface_listener, output); wl_surface_commit(output->surface); wl_display_roundtrip(output->backend->remote_display); diff --git a/include/backend/wayland.h b/include/backend/wayland.h index 7d70cd598..6a58e2b98 100644 --- a/include/backend/wayland.h +++ b/include/backend/wayland.h @@ -54,6 +54,7 @@ struct wlr_wl_backend { struct wlr_wl_remote_output { struct wl_output *output; + struct wlr_wl_backend *backend; uint32_t scale; // we use this to identify the output in the @@ -63,6 +64,11 @@ struct wlr_wl_remote_output { struct wl_list link; // wlr_wl_backend.remote_outputs }; +struct wlr_wl_active_remote_output { + struct wlr_wl_remote_output *remote_output; + struct wl_list link; // wlr_wl_output.active_remote_outputs +}; + struct wlr_wl_buffer { struct wlr_buffer *buffer; struct wl_buffer *wl_buffer; @@ -98,9 +104,20 @@ struct wlr_wl_output { struct xdg_toplevel *xdg_toplevel; struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1; struct wl_list presentation_feedbacks; + struct wl_list active_remote_outputs; // wlr_wl_active_remote_output.link uint32_t enter_serial; + // last requested output size. This is not the actual size that the + // compositor has accepted. + struct { + bool needs_ack; + uint32_t serial; + + int32_t width; + int32_t height; + } requested; + struct { struct wlr_wl_pointer *pointer; struct wl_surface *surface; @@ -182,6 +199,8 @@ bool create_wl_seat(struct wl_seat *wl_seat, struct wlr_wl_backend *wl, void destroy_wl_seat(struct wlr_wl_seat *seat); void destroy_wl_buffer(struct wlr_wl_buffer *buffer); +void surface_update(struct wlr_wl_output *output); + extern const struct wlr_pointer_impl wl_pointer_impl; extern const struct wlr_tablet_pad_impl wl_tablet_pad_impl; extern const struct wlr_tablet_impl wl_tablet_impl;