From ebecc5404b899b462f4c414663780ba72adbab28 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sat, 7 Nov 2020 22:08:40 +0100 Subject: [PATCH] surface: Make send_enter store entered outputs wlr_surface_send_enter now stores outputs that have been entered. Combined with a new 'bind' event on wlr_output, this allows us to delay enter events as necessary until the respective wl_output global has been bound. Closes: https://github.com/swaywm/wlroots/issues/2466 --- include/wlr/types/wlr_output.h | 7 ++++ include/wlr/types/wlr_surface.h | 11 +++++ types/wlr_output.c | 8 ++++ types/wlr_surface.c | 72 +++++++++++++++++++++++++++++++-- 4 files changed, 95 insertions(+), 3 deletions(-) diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index cb742706..8cc4afcf 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -166,6 +166,8 @@ struct wlr_output { struct wl_signal commit; // wlr_output_event_commit // Emitted right after the buffer has been presented to the user struct wl_signal present; // wlr_output_event_present + // Emitted after a client bound the wl_output global + struct wl_signal bind; // wlr_output_event_bind struct wl_signal enable; struct wl_signal mode; struct wl_signal scale; @@ -233,6 +235,11 @@ struct wlr_output_event_present { uint32_t flags; // enum wlr_output_present_flag }; +struct wlr_output_event_bind { + struct wlr_output *output; + struct wl_resource *resource; +}; + struct wlr_surface; /** diff --git a/include/wlr/types/wlr_surface.h b/include/wlr/types/wlr_surface.h index 2cd4ac5d..abb05600 100644 --- a/include/wlr/types/wlr_surface.h +++ b/include/wlr/types/wlr_surface.h @@ -67,6 +67,15 @@ struct wlr_surface_role { void (*precommit)(struct wlr_surface *surface); }; +struct wlr_surface_output { + struct wlr_surface *surface; + struct wlr_output *output; + + struct wl_list link; // wlr_surface::current_outputs + struct wl_listener bind; + struct wl_listener destroy; +}; + struct wlr_surface { struct wl_resource *resource; struct wlr_renderer *renderer; @@ -126,6 +135,8 @@ struct wlr_surface { // wlr_subsurface::parent_pending_link struct wl_list subsurface_pending_list; + struct wl_list current_outputs; // wlr_surface_output::link + struct wl_listener renderer_destroy; void *data; diff --git a/types/wlr_output.c b/types/wlr_output.c index 4ce7312f..15e85154 100644 --- a/types/wlr_output.c +++ b/types/wlr_output.c @@ -93,6 +93,13 @@ static void output_bind(struct wl_client *wl_client, void *data, send_current_mode(resource); send_scale(resource); send_done(resource); + + struct wlr_output_event_bind evt = { + .output = output, + .resource = resource, + }; + + wlr_signal_emit_safe(&output->events.bind, &evt); } void wlr_output_create_global(struct wlr_output *output) { @@ -333,6 +340,7 @@ void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend, wl_signal_init(&output->events.precommit); wl_signal_init(&output->events.commit); wl_signal_init(&output->events.present); + wl_signal_init(&output->events.bind); wl_signal_init(&output->events.enable); wl_signal_init(&output->events.mode); wl_signal_init(&output->events.scale); diff --git a/types/wlr_surface.c b/types/wlr_surface.c index 1b623504..c251816e 100644 --- a/types/wlr_surface.c +++ b/types/wlr_surface.c @@ -616,9 +616,16 @@ static void subsurface_destroy(struct wlr_subsurface *subsurface) { free(subsurface); } +static void surface_output_destroy(struct wlr_surface_output *surface_output); + static void surface_handle_resource_destroy(struct wl_resource *resource) { + struct wlr_surface_output *surface_output, *tmp; struct wlr_surface *surface = wlr_surface_from_resource(resource); + wl_list_for_each_safe(surface_output, tmp, &surface->current_outputs, link) { + surface_output_destroy(surface_output); + } + wlr_signal_emit_safe(&surface->events.destroy, surface); wl_list_remove(wl_resource_get_link(surface->resource)); @@ -676,6 +683,7 @@ struct wlr_surface *wlr_surface_create(struct wl_client *client, wl_signal_init(&surface->events.new_subsurface); wl_list_init(&surface->subsurfaces); wl_list_init(&surface->subsurface_pending_list); + wl_list_init(&surface->current_outputs); pixman_region32_init(&surface->buffer_damage); pixman_region32_init(&surface->opaque_region); pixman_region32_init(&surface->input_region); @@ -1091,10 +1099,59 @@ struct wlr_surface *wlr_surface_surface_at(struct wlr_surface *surface, return NULL; } +static void surface_output_destroy(struct wlr_surface_output *surface_output) { + wl_list_remove(&surface_output->bind.link); + wl_list_remove(&surface_output->destroy.link); + wl_list_remove(&surface_output->link); + + free(surface_output); +} + +static void surface_handle_output_bind(struct wl_listener *listener, + void *data) { + struct wlr_output_event_bind *evt = data; + struct wlr_surface_output *surface_output = + wl_container_of(listener, surface_output, bind); + struct wl_client *client = wl_resource_get_client( + surface_output->surface->resource); + if (client == wl_resource_get_client(evt->resource)) { + wl_surface_send_enter(surface_output->surface->resource, evt->resource); + } +} + +static void surface_handle_output_destroy(struct wl_listener *listener, + void *data) { + struct wlr_surface_output *surface_output = + wl_container_of(listener, surface_output, destroy); + surface_output_destroy(surface_output); +} + void wlr_surface_send_enter(struct wlr_surface *surface, struct wlr_output *output) { struct wl_client *client = wl_resource_get_client(surface->resource); + struct wlr_surface_output *surface_output; struct wl_resource *resource; + + wl_list_for_each(surface_output, &surface->current_outputs, link) { + if (surface_output->output == output) { + return; + } + } + + surface_output = calloc(1, sizeof(struct wlr_surface_output)); + if (surface_output == NULL) { + return; + } + surface_output->bind.notify = surface_handle_output_bind; + surface_output->destroy.notify = surface_handle_output_destroy; + + wl_signal_add(&output->events.bind, &surface_output->bind); + wl_signal_add(&output->events.destroy, &surface_output->destroy); + + surface_output->surface = surface; + surface_output->output = output; + wl_list_insert(&surface->current_outputs, &surface_output->link); + wl_resource_for_each(resource, &output->resources) { if (client == wl_resource_get_client(resource)) { wl_surface_send_enter(surface->resource, resource); @@ -1105,10 +1162,19 @@ void wlr_surface_send_enter(struct wlr_surface *surface, void wlr_surface_send_leave(struct wlr_surface *surface, struct wlr_output *output) { struct wl_client *client = wl_resource_get_client(surface->resource); + struct wlr_surface_output *surface_output, *tmp; struct wl_resource *resource; - wl_resource_for_each(resource, &output->resources) { - if (client == wl_resource_get_client(resource)) { - wl_surface_send_leave(surface->resource, resource); + + wl_list_for_each_safe(surface_output, tmp, + &surface->current_outputs, link) { + if (surface_output->output == output) { + surface_output_destroy(surface_output); + wl_resource_for_each(resource, &output->resources) { + if (client == wl_resource_get_client(resource)) { + wl_surface_send_leave(surface->resource, resource); + } + } + break; } } }