From 9c3163df7a39fe127b1ca437e8cb441f3c52fc56 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 18 Jun 2021 18:25:51 +0200 Subject: [PATCH] xdg-shell: add support for cached xdg_surface state When the pending surface state becomes cached, also cache the pending xdg_surface state. When a cached surface state becomes current, also make the matching cached xdg_surface state current. References: https://github.com/swaywm/wlroots/issues/2957 --- include/wlr/types/wlr_xdg_shell.h | 14 ++++- types/xdg_shell/wlr_xdg_surface.c | 85 ++++++++++++++++++++++++++----- 2 files changed, 84 insertions(+), 15 deletions(-) diff --git a/include/wlr/types/wlr_xdg_shell.h b/include/wlr/types/wlr_xdg_shell.h index 856648f9b..cc0ab8328 100644 --- a/include/wlr/types/wlr_xdg_shell.h +++ b/include/wlr/types/wlr_xdg_shell.h @@ -149,6 +149,14 @@ struct wlr_xdg_surface_configure { struct wlr_xdg_toplevel_state *toplevel_state; }; +struct wlr_xdg_surface_state { + bool has_geometry; + struct wlr_box geometry; + + uint32_t seq; + struct wl_list cached_state_link; +}; + /** * An xdg-surface is a user interface element requiring management by the * compositor. An xdg-surface alone isn't useful, a role should be assigned to @@ -179,12 +187,14 @@ struct wlr_xdg_surface { uint32_t configure_next_serial; struct wl_list configure_list; - bool has_next_geometry; - struct wlr_box next_geometry; struct wlr_box geometry; + struct wlr_xdg_surface_state pending; + struct wl_list cached; + struct wl_listener surface_destroy; struct wl_listener surface_commit; + struct wl_listener surface_cache; struct { struct wl_signal destroy; diff --git a/types/xdg_shell/wlr_xdg_surface.c b/types/xdg_shell/wlr_xdg_surface.c index 0f85ac2d0..dd89f3ad3 100644 --- a/types/xdg_shell/wlr_xdg_surface.c +++ b/types/xdg_shell/wlr_xdg_surface.c @@ -87,9 +87,8 @@ void unmap_xdg_surface(struct wlr_xdg_surface *surface) { } surface->configure_next_serial = 0; - surface->has_next_geometry = false; + surface->pending.has_geometry = false; memset(&surface->geometry, 0, sizeof(struct wlr_box)); - memset(&surface->next_geometry, 0, sizeof(struct wlr_box)); } @@ -285,11 +284,11 @@ static void xdg_surface_handle_set_window_geometry(struct wl_client *client, return; } - surface->has_next_geometry = true; - surface->next_geometry.height = height; - surface->next_geometry.width = width; - surface->next_geometry.x = x; - surface->next_geometry.y = y; + surface->pending.has_geometry = true; + surface->pending.geometry.x = x; + surface->pending.geometry.y = y; + surface->pending.geometry.width = width; + surface->pending.geometry.height = height; } static void xdg_surface_handle_destroy(struct wl_client *client, @@ -346,6 +345,47 @@ static void xdg_surface_handle_surface_commit(struct wl_listener *listener, } } +static void xdg_surface_handle_surface_cache(struct wl_listener *listener, + void *data) { + struct wlr_xdg_surface *surface = + wl_container_of(listener, surface, surface_cache); + + if (!surface->pending.has_geometry) { + goto out; + } + + struct wlr_xdg_surface_state *cached = calloc(1, sizeof(*cached)); + if (cached == NULL) { + goto out; + } + *cached = surface->pending; + wl_list_insert(&surface->cached, &cached->cached_state_link); + +out: + surface->pending.seq = surface->surface->pending.seq; +} + +static bool xdg_surface_pop_state(struct wlr_xdg_surface *surface, + uint32_t seq, struct wlr_xdg_surface_state *out) { + if (surface->pending.seq == seq) { + *out = surface->pending; + memset(&surface->pending, 0, sizeof(surface->pending)); + return true; + } + + struct wlr_xdg_surface_state *cached; + wl_list_for_each(cached, &surface->cached, cached_state_link) { + if (cached->seq == seq) { + wl_list_remove(&cached->cached_state_link); + *out = *cached; + free(cached); + return true; + } + } + + return false; +} + void handle_xdg_surface_commit(struct wlr_surface *wlr_surface) { struct wlr_xdg_surface *surface = wlr_xdg_surface_from_wlr_surface(wlr_surface); @@ -353,12 +393,16 @@ void handle_xdg_surface_commit(struct wlr_surface *wlr_surface) { return; } - if (surface->has_next_geometry) { - surface->has_next_geometry = false; - surface->geometry.x = surface->next_geometry.x; - surface->geometry.y = surface->next_geometry.y; - surface->geometry.width = surface->next_geometry.width; - surface->geometry.height = surface->next_geometry.height; + struct wlr_xdg_surface_state next; + bool has_next = + xdg_surface_pop_state(surface, wlr_surface->current.seq, &next); + surface->pending.seq = wlr_surface->pending.seq; + if (!has_next) { + return; + } + + if (next.has_geometry) { + surface->geometry = next.geometry; } switch (surface->role) { @@ -445,6 +489,7 @@ struct wlr_xdg_surface *create_xdg_surface( wl_list_init(&xdg_surface->configure_list); wl_list_init(&xdg_surface->popups); + wl_list_init(&xdg_surface->cached); wl_signal_init(&xdg_surface->events.destroy); wl_signal_init(&xdg_surface->events.ping_timeout); @@ -462,6 +507,10 @@ struct wlr_xdg_surface *create_xdg_surface( &xdg_surface->surface_commit); xdg_surface->surface_commit.notify = xdg_surface_handle_surface_commit; + wl_signal_add(&xdg_surface->surface->events.cache, + &xdg_surface->surface_cache); + xdg_surface->surface_cache.notify = xdg_surface_handle_surface_cache; + wlr_log(WLR_DEBUG, "new xdg_surface %p (res %p)", xdg_surface, xdg_surface->resource); wl_resource_set_implementation(xdg_surface->resource, @@ -469,6 +518,8 @@ struct wlr_xdg_surface *create_xdg_surface( xdg_surface_handle_resource_destroy); wl_list_insert(&client->surfaces, &xdg_surface->link); + xdg_surface->pending.seq = surface->pending.seq; + return xdg_surface; } @@ -518,9 +569,17 @@ void destroy_xdg_surface(struct wlr_xdg_surface *surface) { wl_resource_set_user_data(surface->resource, NULL); surface->surface->role_data = NULL; + struct wlr_xdg_surface_state *cached, *cached_tmp; + wl_list_for_each_safe(cached, cached_tmp, &surface->cached, + cached_state_link) { + wl_list_remove(&cached->cached_state_link); + free(cached); + } + wl_list_remove(&surface->link); wl_list_remove(&surface->surface_destroy.link); wl_list_remove(&surface->surface_commit.link); + wl_list_remove(&surface->surface_cache.link); free(surface); }