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.
This commit is contained in:
Kirill Primak 2021-09-16 14:04:56 +03:00 committed by Simon Ser
parent 9579d62a16
commit b72a217fcc
3 changed files with 103 additions and 86 deletions

View file

@ -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;
};
/**

View file

@ -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;

View file

@ -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);
}