From b72a217fcc9f8bb47788c2068093579ac893301c Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Thu, 16 Sep 2021 14:04:56 +0300 Subject: [PATCH] xdg-toplevel: refactor configure/state flow Previously, `wlr_xdg_toplevel` didn't follow the usual "current state + pending state" pattern and instead had confusingly named `client_pending` and `server_pending`. This commit removes them, and instead introduces `wlr_xdg_toplevel.scheduled` to store the properties that are yet to be sent to a client, and `wlr_xdg_toplevel.requested` to store the properties that a client has requested. They have different types to emphasize that they aren't actual states. --- include/wlr/types/wlr_xdg_shell.h | 27 ++++-- types/xdg_shell/wlr_xdg_surface.c | 11 +-- types/xdg_shell/wlr_xdg_toplevel.c | 151 +++++++++++++++-------------- 3 files changed, 103 insertions(+), 86 deletions(-) diff --git a/include/wlr/types/wlr_xdg_shell.h b/include/wlr/types/wlr_xdg_shell.h index f80c6685..2ca7e203 100644 --- a/include/wlr/types/wlr_xdg_shell.h +++ b/include/wlr/types/wlr_xdg_shell.h @@ -105,10 +105,16 @@ struct wlr_xdg_toplevel_state { uint32_t width, height; uint32_t max_width, max_height; uint32_t min_width, min_height; +}; - // Since the fullscreen request may be made before the toplevel's surface - // is mapped, this is used to store the requested fullscreen output (if - // any) for wlr_xdg_toplevel::client_pending. +struct wlr_xdg_toplevel_configure { + bool maximized, fullscreen, resizing, activated; + uint32_t tiled; // enum wlr_edges + uint32_t width, height; +}; + +struct wlr_xdg_toplevel_requested { + bool maximized, minimized, fullscreen; struct wlr_output *fullscreen_output; struct wl_listener fullscreen_output_destroy; }; @@ -121,10 +127,15 @@ struct wlr_xdg_toplevel { struct wlr_xdg_surface *parent; struct wl_listener parent_unmap; - struct wlr_xdg_toplevel_state client_pending; - struct wlr_xdg_toplevel_state server_pending; - struct wlr_xdg_toplevel_state last_acked; - struct wlr_xdg_toplevel_state current; + struct wlr_xdg_toplevel_state current, pending; + + // Properties to be sent to the client in the next configure event. + struct wlr_xdg_toplevel_configure scheduled; + + // Properties that the client has requested. Intended to be checked + // by the compositor on surface map and handled accordingly + // (e.g. a client might want to start already in a fullscreen state). + struct wlr_xdg_toplevel_requested requested; char *title; char *app_id; @@ -147,7 +158,7 @@ struct wlr_xdg_surface_configure { struct wl_list link; // wlr_xdg_surface::configure_list uint32_t serial; - struct wlr_xdg_toplevel_state *toplevel_state; + struct wlr_xdg_toplevel_configure *toplevel_configure; }; /** diff --git a/types/xdg_shell/wlr_xdg_surface.c b/types/xdg_shell/wlr_xdg_surface.c index 2afa9dc5..8819fd15 100644 --- a/types/xdg_shell/wlr_xdg_surface.c +++ b/types/xdg_shell/wlr_xdg_surface.c @@ -23,7 +23,7 @@ static void xdg_surface_configure_destroy( return; } wl_list_remove(&configure->link); - free(configure->toplevel_state); + free(configure->toplevel_configure); free(configure); } @@ -487,11 +487,10 @@ void reset_xdg_surface(struct wlr_xdg_surface *xdg_surface) { case WLR_XDG_SURFACE_ROLE_TOPLEVEL: wl_resource_set_user_data(xdg_surface->toplevel->resource, NULL); xdg_surface->toplevel->resource = NULL; - - if (xdg_surface->toplevel->client_pending.fullscreen_output) { - struct wlr_xdg_toplevel_state *client_pending = - &xdg_surface->toplevel->client_pending; - wl_list_remove(&client_pending->fullscreen_output_destroy.link); + struct wlr_xdg_toplevel_requested *req = + &xdg_surface->toplevel->requested; + if (req->fullscreen_output) { + wl_list_remove(&req->fullscreen_output_destroy.link); } free(xdg_surface->toplevel); xdg_surface->toplevel = NULL; diff --git a/types/xdg_shell/wlr_xdg_toplevel.c b/types/xdg_shell/wlr_xdg_toplevel.c index 03d31897..d94b92e5 100644 --- a/types/xdg_shell/wlr_xdg_toplevel.c +++ b/types/xdg_shell/wlr_xdg_toplevel.c @@ -11,50 +11,67 @@ void handle_xdg_toplevel_ack_configure( struct wlr_xdg_surface *surface, struct wlr_xdg_surface_configure *configure) { assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); - assert(configure->toplevel_state != NULL); - surface->toplevel->last_acked = *configure->toplevel_state; + struct wlr_xdg_toplevel_configure *acked = configure->toplevel_configure; + assert(acked != NULL); + + surface->toplevel->pending.maximized = acked->maximized; + surface->toplevel->pending.fullscreen = acked->fullscreen; + surface->toplevel->pending.resizing = acked->resizing; + surface->toplevel->pending.activated = acked->activated; + surface->toplevel->pending.tiled = acked->tiled; + + surface->toplevel->pending.width = acked->width; + surface->toplevel->pending.height = acked->height; } -bool compare_xdg_surface_toplevel_state(struct wlr_xdg_toplevel *state) { - // is pending state different from current state? - if (!state->base->configured) { +bool compare_xdg_surface_toplevel_state(struct wlr_xdg_toplevel *toplevel) { + // Is the scheduled configure different from the last sent one? + if (!toplevel->base->configured) { return false; } - struct wlr_xdg_toplevel_state *configured = NULL; - if (wl_list_empty(&state->base->configure_list)) { + struct wlr_xdg_toplevel_configure last_acked; + struct wlr_xdg_toplevel_configure *configure = NULL; + if (wl_list_empty(&toplevel->base->configure_list)) { // There are currently no pending configures, so check against the last // state acked by the client. - configured = &state->last_acked; + last_acked.maximized = toplevel->pending.maximized; + last_acked.fullscreen = toplevel->pending.fullscreen; + last_acked.resizing = toplevel->pending.resizing; + last_acked.activated = toplevel->pending.activated; + last_acked.tiled = toplevel->pending.tiled; + last_acked.width = toplevel->pending.width; + last_acked.height = toplevel->pending.height; + configure = &last_acked; } else { - struct wlr_xdg_surface_configure *configure = - wl_container_of(state->base->configure_list.prev, configure, link); - configured = configure->toplevel_state; + struct wlr_xdg_surface_configure *surface_configure = + wl_container_of(toplevel->base->configure_list.prev, surface_configure, link); + configure = surface_configure->toplevel_configure; } - if (state->server_pending.activated != configured->activated) { + if (toplevel->scheduled.activated != configure->activated) { return false; } - if (state->server_pending.fullscreen != configured->fullscreen) { + if (toplevel->scheduled.fullscreen != configure->fullscreen) { return false; } - if (state->server_pending.maximized != configured->maximized) { + if (toplevel->scheduled.maximized != configure->maximized) { return false; } - if (state->server_pending.resizing != configured->resizing) { + if (toplevel->scheduled.resizing != configure->resizing) { return false; } - if (state->server_pending.tiled != configured->tiled) { + if (toplevel->scheduled.tiled != configure->tiled) { return false; } - if (state->server_pending.width == configured->width && - state->server_pending.height == configured->height) { + if (toplevel->scheduled.width == configure->width && + toplevel->scheduled.height == configure->height) { return true; } - if (state->server_pending.width == 0 && state->server_pending.height == 0) { + if (toplevel->scheduled.width == 0 && toplevel->scheduled.height == 0) { return true; } @@ -65,17 +82,17 @@ void send_xdg_toplevel_configure(struct wlr_xdg_surface *surface, struct wlr_xdg_surface_configure *configure) { assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); - configure->toplevel_state = malloc(sizeof(*configure->toplevel_state)); - if (configure->toplevel_state == NULL) { + configure->toplevel_configure = malloc(sizeof(*configure->toplevel_configure)); + if (configure->toplevel_configure == NULL) { wlr_log(WLR_ERROR, "Allocation failed"); wl_resource_post_no_memory(surface->toplevel->resource); return; } - *configure->toplevel_state = surface->toplevel->server_pending; + *configure->toplevel_configure = surface->toplevel->scheduled; struct wl_array states; wl_array_init(&states); - if (surface->toplevel->server_pending.maximized) { + if (surface->toplevel->scheduled.maximized) { uint32_t *s = wl_array_add(&states, sizeof(uint32_t)); if (!s) { wlr_log(WLR_ERROR, "Could not allocate state for maximized xdg_toplevel"); @@ -83,7 +100,7 @@ void send_xdg_toplevel_configure(struct wlr_xdg_surface *surface, } *s = XDG_TOPLEVEL_STATE_MAXIMIZED; } - if (surface->toplevel->server_pending.fullscreen) { + if (surface->toplevel->scheduled.fullscreen) { uint32_t *s = wl_array_add(&states, sizeof(uint32_t)); if (!s) { wlr_log(WLR_ERROR, "Could not allocate state for fullscreen xdg_toplevel"); @@ -91,7 +108,7 @@ void send_xdg_toplevel_configure(struct wlr_xdg_surface *surface, } *s = XDG_TOPLEVEL_STATE_FULLSCREEN; } - if (surface->toplevel->server_pending.resizing) { + if (surface->toplevel->scheduled.resizing) { uint32_t *s = wl_array_add(&states, sizeof(uint32_t)); if (!s) { wlr_log(WLR_ERROR, "Could not allocate state for resizing xdg_toplevel"); @@ -99,7 +116,7 @@ void send_xdg_toplevel_configure(struct wlr_xdg_surface *surface, } *s = XDG_TOPLEVEL_STATE_RESIZING; } - if (surface->toplevel->server_pending.activated) { + if (surface->toplevel->scheduled.activated) { uint32_t *s = wl_array_add(&states, sizeof(uint32_t)); if (!s) { wlr_log(WLR_ERROR, "Could not allocate state for activated xdg_toplevel"); @@ -107,7 +124,7 @@ void send_xdg_toplevel_configure(struct wlr_xdg_surface *surface, } *s = XDG_TOPLEVEL_STATE_ACTIVATED; } - if (surface->toplevel->server_pending.tiled) { + if (surface->toplevel->scheduled.tiled) { if (wl_resource_get_version(surface->resource) >= XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION) { const struct { @@ -121,7 +138,7 @@ void send_xdg_toplevel_configure(struct wlr_xdg_surface *surface, }; for (size_t i = 0; i < sizeof(tiled)/sizeof(tiled[0]); ++i) { - if ((surface->toplevel->server_pending.tiled & + if ((surface->toplevel->scheduled.tiled & tiled[i].edge) == 0) { continue; } @@ -134,7 +151,7 @@ void send_xdg_toplevel_configure(struct wlr_xdg_surface *surface, } *s = tiled[i].state; } - } else if (!surface->toplevel->server_pending.maximized) { + } else if (!surface->toplevel->scheduled.maximized) { // This version doesn't support tiling, best we can do is make the // toplevel maximized uint32_t *s = wl_array_add(&states, sizeof(uint32_t)); @@ -147,8 +164,8 @@ void send_xdg_toplevel_configure(struct wlr_xdg_surface *surface, } } - uint32_t width = surface->toplevel->server_pending.width; - uint32_t height = surface->toplevel->server_pending.height; + uint32_t width = surface->toplevel->scheduled.width; + uint32_t height = surface->toplevel->scheduled.height; xdg_toplevel_send_configure(surface->toplevel->resource, width, height, &states); @@ -171,18 +188,7 @@ void handle_xdg_surface_toplevel_committed(struct wlr_xdg_surface *surface) { return; } - // apply state from the last acked configure now that the client committed - surface->toplevel->current = surface->toplevel->last_acked; - - // update state from the client that doesn't need compositor approval - surface->toplevel->current.max_width = - surface->toplevel->client_pending.max_width; - surface->toplevel->current.min_width = - surface->toplevel->client_pending.min_width; - surface->toplevel->current.max_height = - surface->toplevel->client_pending.max_height; - surface->toplevel->current.min_height = - surface->toplevel->client_pending.min_height; + surface->toplevel->current = surface->toplevel->pending; } static const struct xdg_toplevel_interface xdg_toplevel_implementation; @@ -359,23 +365,23 @@ static void xdg_toplevel_handle_set_max_size(struct wl_client *client, struct wl_resource *resource, int32_t width, int32_t height) { struct wlr_xdg_surface *surface = wlr_xdg_surface_from_toplevel_resource(resource); - surface->toplevel->client_pending.max_width = width; - surface->toplevel->client_pending.max_height = height; + surface->toplevel->pending.max_width = width; + surface->toplevel->pending.max_height = height; } static void xdg_toplevel_handle_set_min_size(struct wl_client *client, struct wl_resource *resource, int32_t width, int32_t height) { struct wlr_xdg_surface *surface = wlr_xdg_surface_from_toplevel_resource(resource); - surface->toplevel->client_pending.min_width = width; - surface->toplevel->client_pending.min_height = height; + surface->toplevel->pending.min_width = width; + surface->toplevel->pending.min_height = height; } static void xdg_toplevel_handle_set_maximized(struct wl_client *client, struct wl_resource *resource) { struct wlr_xdg_surface *surface = wlr_xdg_surface_from_toplevel_resource(resource); - surface->toplevel->client_pending.maximized = true; + surface->toplevel->requested.maximized = true; wlr_signal_emit_safe(&surface->toplevel->events.request_maximize, surface); } @@ -383,31 +389,31 @@ static void xdg_toplevel_handle_unset_maximized(struct wl_client *client, struct wl_resource *resource) { struct wlr_xdg_surface *surface = wlr_xdg_surface_from_toplevel_resource(resource); - surface->toplevel->client_pending.maximized = false; + surface->toplevel->requested.maximized = false; wlr_signal_emit_safe(&surface->toplevel->events.request_maximize, surface); } static void handle_fullscreen_output_destroy(struct wl_listener *listener, void *data) { - struct wlr_xdg_toplevel_state *state = - wl_container_of(listener, state, fullscreen_output_destroy); - state->fullscreen_output = NULL; - wl_list_remove(&state->fullscreen_output_destroy.link); + struct wlr_xdg_toplevel_requested *req = + wl_container_of(listener, req, fullscreen_output_destroy); + req->fullscreen_output = NULL; + wl_list_remove(&req->fullscreen_output_destroy.link); } -static void store_fullscreen_pending(struct wlr_xdg_surface *surface, +static void store_fullscreen_requested(struct wlr_xdg_surface *surface, bool fullscreen, struct wlr_output *output) { - struct wlr_xdg_toplevel_state *state = &surface->toplevel->client_pending; - state->fullscreen = fullscreen; - if (state->fullscreen_output) { - wl_list_remove(&state->fullscreen_output_destroy.link); + struct wlr_xdg_toplevel_requested *req = &surface->toplevel->requested; + req->fullscreen = fullscreen; + if (req->fullscreen_output) { + wl_list_remove(&req->fullscreen_output_destroy.link); } - state->fullscreen_output = output; - if (state->fullscreen_output) { - state->fullscreen_output_destroy.notify = + req->fullscreen_output = output; + if (req->fullscreen_output) { + req->fullscreen_output_destroy.notify = handle_fullscreen_output_destroy; - wl_signal_add(&state->fullscreen_output->events.destroy, - &state->fullscreen_output_destroy); + wl_signal_add(&req->fullscreen_output->events.destroy, + &req->fullscreen_output_destroy); } } @@ -421,7 +427,7 @@ static void xdg_toplevel_handle_set_fullscreen(struct wl_client *client, output = wlr_output_from_resource(output_resource); } - store_fullscreen_pending(surface, true, output); + store_fullscreen_requested(surface, true, output); struct wlr_xdg_toplevel_set_fullscreen_event event = { .surface = surface, @@ -437,7 +443,7 @@ static void xdg_toplevel_handle_unset_fullscreen(struct wl_client *client, struct wlr_xdg_surface *surface = wlr_xdg_surface_from_toplevel_resource(resource); - store_fullscreen_pending(surface, false, NULL); + store_fullscreen_requested(surface, false, NULL); struct wlr_xdg_toplevel_set_fullscreen_event event = { .surface = surface, @@ -452,6 +458,7 @@ static void xdg_toplevel_handle_set_minimized(struct wl_client *client, struct wl_resource *resource) { struct wlr_xdg_surface *surface = wlr_xdg_surface_from_toplevel_resource(resource); + surface->toplevel->requested.minimized = true; wlr_signal_emit_safe(&surface->toplevel->events.request_minimize, surface); } @@ -547,8 +554,8 @@ void destroy_xdg_toplevel(struct wlr_xdg_surface *xdg_surface) { uint32_t wlr_xdg_toplevel_set_size(struct wlr_xdg_surface *surface, uint32_t width, uint32_t height) { assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); - surface->toplevel->server_pending.width = width; - surface->toplevel->server_pending.height = height; + surface->toplevel->scheduled.width = width; + surface->toplevel->scheduled.height = height; return schedule_xdg_surface_configure(surface); } @@ -556,7 +563,7 @@ uint32_t wlr_xdg_toplevel_set_size(struct wlr_xdg_surface *surface, uint32_t wlr_xdg_toplevel_set_activated(struct wlr_xdg_surface *surface, bool activated) { assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); - surface->toplevel->server_pending.activated = activated; + surface->toplevel->scheduled.activated = activated; return schedule_xdg_surface_configure(surface); } @@ -564,7 +571,7 @@ uint32_t wlr_xdg_toplevel_set_activated(struct wlr_xdg_surface *surface, uint32_t wlr_xdg_toplevel_set_maximized(struct wlr_xdg_surface *surface, bool maximized) { assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); - surface->toplevel->server_pending.maximized = maximized; + surface->toplevel->scheduled.maximized = maximized; return schedule_xdg_surface_configure(surface); } @@ -572,7 +579,7 @@ uint32_t wlr_xdg_toplevel_set_maximized(struct wlr_xdg_surface *surface, uint32_t wlr_xdg_toplevel_set_fullscreen(struct wlr_xdg_surface *surface, bool fullscreen) { assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); - surface->toplevel->server_pending.fullscreen = fullscreen; + surface->toplevel->scheduled.fullscreen = fullscreen; return schedule_xdg_surface_configure(surface); } @@ -580,7 +587,7 @@ uint32_t wlr_xdg_toplevel_set_fullscreen(struct wlr_xdg_surface *surface, uint32_t wlr_xdg_toplevel_set_resizing(struct wlr_xdg_surface *surface, bool resizing) { assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); - surface->toplevel->server_pending.resizing = resizing; + surface->toplevel->scheduled.resizing = resizing; return schedule_xdg_surface_configure(surface); } @@ -588,7 +595,7 @@ uint32_t wlr_xdg_toplevel_set_resizing(struct wlr_xdg_surface *surface, uint32_t wlr_xdg_toplevel_set_tiled(struct wlr_xdg_surface *surface, uint32_t tiled) { assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); - surface->toplevel->server_pending.tiled = tiled; + surface->toplevel->scheduled.tiled = tiled; return schedule_xdg_surface_configure(surface); }