mirror of
https://github.com/hyprwm/wlroots-hyprland.git
synced 2024-12-26 11:39:47 +01:00
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:
parent
9579d62a16
commit
b72a217fcc
3 changed files with 103 additions and 86 deletions
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue