mirror of
				https://gitlab.freedesktop.org/wlroots/wlroots.git
				synced 2025-11-03 09:01:40 -05:00 
			
		
		
		
	xdg-shell: split last-acked and current state
These states are distinct in the time period between the ack_configure and the next commit on the surface. Splitting these states avoids the following race for example: - client starts at 1000x1000 - wlr_xdg_toplevel_set_size 500x500 - size is different -> configure sent - client acks the configure - wlr_xdg_toplevel_set_size 1000x1000 - compare_xdg_toplevel_state returns true since there is no pending configure and the currently committed size is still 1000x1000 - no new configure is sent - client commits at the size it last acked, 500x500
This commit is contained in:
		
							parent
							
								
									330c50b48d
								
							
						
					
					
						commit
						2072d59da5
					
				
					 4 changed files with 35 additions and 57 deletions
				
			
		| 
						 | 
					@ -124,6 +124,7 @@ struct wlr_xdg_toplevel {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct wlr_xdg_toplevel_state client_pending;
 | 
						struct wlr_xdg_toplevel_state client_pending;
 | 
				
			||||||
	struct wlr_xdg_toplevel_state server_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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	char *title;
 | 
						char *title;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -139,6 +139,7 @@ struct wlr_xdg_toplevel_v6 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct wlr_xdg_toplevel_v6_state client_pending;
 | 
						struct wlr_xdg_toplevel_v6_state client_pending;
 | 
				
			||||||
	struct wlr_xdg_toplevel_v6_state server_pending;
 | 
						struct wlr_xdg_toplevel_v6_state server_pending;
 | 
				
			||||||
 | 
						struct wlr_xdg_toplevel_v6_state last_acked;
 | 
				
			||||||
	struct wlr_xdg_toplevel_v6_state current;
 | 
						struct wlr_xdg_toplevel_v6_state current;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	char *title;
 | 
						char *title;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,60 +13,44 @@ void handle_xdg_toplevel_ack_configure(
 | 
				
			||||||
	assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL);
 | 
						assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL);
 | 
				
			||||||
	assert(configure->toplevel_state != NULL);
 | 
						assert(configure->toplevel_state != NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	surface->toplevel->current.maximized =
 | 
						surface->toplevel->last_acked = *configure->toplevel_state;
 | 
				
			||||||
		configure->toplevel_state->maximized;
 | 
					 | 
				
			||||||
	surface->toplevel->current.fullscreen =
 | 
					 | 
				
			||||||
		configure->toplevel_state->fullscreen;
 | 
					 | 
				
			||||||
	surface->toplevel->current.resizing =
 | 
					 | 
				
			||||||
		configure->toplevel_state->resizing;
 | 
					 | 
				
			||||||
	surface->toplevel->current.activated =
 | 
					 | 
				
			||||||
		configure->toplevel_state->activated;
 | 
					 | 
				
			||||||
	surface->toplevel->current.tiled =
 | 
					 | 
				
			||||||
		configure->toplevel_state->tiled;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool compare_xdg_surface_toplevel_state(struct wlr_xdg_toplevel *state) {
 | 
					bool compare_xdg_surface_toplevel_state(struct wlr_xdg_toplevel *state) {
 | 
				
			||||||
	struct {
 | 
					 | 
				
			||||||
		struct wlr_xdg_toplevel_state state;
 | 
					 | 
				
			||||||
		uint32_t width, height;
 | 
					 | 
				
			||||||
	} configured;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// is pending state different from current state?
 | 
						// is pending state different from current state?
 | 
				
			||||||
	if (!state->base->configured) {
 | 
						if (!state->base->configured) {
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct wlr_xdg_toplevel_state *configured = NULL;
 | 
				
			||||||
	if (wl_list_empty(&state->base->configure_list)) {
 | 
						if (wl_list_empty(&state->base->configure_list)) {
 | 
				
			||||||
		// last configure is actually the current state, just use it
 | 
							// There are currently no pending configures, so check against the last
 | 
				
			||||||
		configured.state = state->current;
 | 
							// state acked by the client.
 | 
				
			||||||
		configured.width = state->base->surface->current.width;
 | 
							configured = &state->last_acked;
 | 
				
			||||||
		configured.height = state->base->surface->current.height;
 | 
					 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		struct wlr_xdg_surface_configure *configure =
 | 
							struct wlr_xdg_surface_configure *configure =
 | 
				
			||||||
			wl_container_of(state->base->configure_list.prev, configure, link);
 | 
								wl_container_of(state->base->configure_list.prev, configure, link);
 | 
				
			||||||
		configured.state = *configure->toplevel_state;
 | 
							configured = configure->toplevel_state;
 | 
				
			||||||
		configured.width = configure->toplevel_state->width;
 | 
					 | 
				
			||||||
		configured.height = configure->toplevel_state->height;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (state->server_pending.activated != configured.state.activated) {
 | 
						if (state->server_pending.activated != configured->activated) {
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (state->server_pending.fullscreen != configured.state.fullscreen) {
 | 
						if (state->server_pending.fullscreen != configured->fullscreen) {
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (state->server_pending.maximized != configured.state.maximized) {
 | 
						if (state->server_pending.maximized != configured->maximized) {
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (state->server_pending.resizing != configured.state.resizing) {
 | 
						if (state->server_pending.resizing != configured->resizing) {
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (state->server_pending.tiled != configured.state.tiled) {
 | 
						if (state->server_pending.tiled != configured->tiled) {
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (state->server_pending.width == configured.width &&
 | 
						if (state->server_pending.width == configured->width &&
 | 
				
			||||||
			state->server_pending.height == configured.height) {
 | 
								state->server_pending.height == configured->height) {
 | 
				
			||||||
		return true;
 | 
							return true;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -187,7 +171,10 @@ void handle_xdg_surface_toplevel_committed(struct wlr_xdg_surface *surface) {
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// update state that doesn't need compositor approval
 | 
						// 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->current.max_width =
 | 
				
			||||||
		surface->toplevel->client_pending.max_width;
 | 
							surface->toplevel->client_pending.max_width;
 | 
				
			||||||
	surface->toplevel->current.min_width =
 | 
						surface->toplevel->current.min_width =
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -295,55 +295,41 @@ void handle_xdg_toplevel_v6_ack_configure(struct wlr_xdg_surface_v6 *surface,
 | 
				
			||||||
	assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL);
 | 
						assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL);
 | 
				
			||||||
	assert(configure->toplevel_state != NULL);
 | 
						assert(configure->toplevel_state != NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	surface->toplevel->current.maximized =
 | 
						surface->toplevel->last_acked = *configure->toplevel_state;
 | 
				
			||||||
		configure->toplevel_state->maximized;
 | 
					 | 
				
			||||||
	surface->toplevel->current.fullscreen =
 | 
					 | 
				
			||||||
		configure->toplevel_state->fullscreen;
 | 
					 | 
				
			||||||
	surface->toplevel->current.resizing =
 | 
					 | 
				
			||||||
		configure->toplevel_state->resizing;
 | 
					 | 
				
			||||||
	surface->toplevel->current.activated =
 | 
					 | 
				
			||||||
		configure->toplevel_state->activated;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool compare_xdg_surface_v6_toplevel_state(struct wlr_xdg_toplevel_v6 *state) {
 | 
					bool compare_xdg_surface_v6_toplevel_state(struct wlr_xdg_toplevel_v6 *state) {
 | 
				
			||||||
	struct {
 | 
					 | 
				
			||||||
		struct wlr_xdg_toplevel_v6_state state;
 | 
					 | 
				
			||||||
		uint32_t width, height;
 | 
					 | 
				
			||||||
	} configured;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// is pending state different from current state?
 | 
						// is pending state different from current state?
 | 
				
			||||||
	if (!state->base->configured) {
 | 
						if (!state->base->configured) {
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct wlr_xdg_toplevel_v6_state *configured = NULL;
 | 
				
			||||||
	if (wl_list_empty(&state->base->configure_list)) {
 | 
						if (wl_list_empty(&state->base->configure_list)) {
 | 
				
			||||||
		// last configure is actually the current state, just use it
 | 
							// There are currently no pending configures, so check against the last
 | 
				
			||||||
		configured.state = state->current;
 | 
							// state acked by the client.
 | 
				
			||||||
		configured.width = state->base->surface->current.width;
 | 
							configured = &state->last_acked;
 | 
				
			||||||
		configured.height = state->base->surface->current.height;
 | 
					 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		struct wlr_xdg_surface_v6_configure *configure =
 | 
							struct wlr_xdg_surface_v6_configure *configure =
 | 
				
			||||||
			wl_container_of(state->base->configure_list.prev, configure, link);
 | 
								wl_container_of(state->base->configure_list.prev, configure, link);
 | 
				
			||||||
		configured.state = *configure->toplevel_state;
 | 
							configured = configure->toplevel_state;
 | 
				
			||||||
		configured.width = configure->toplevel_state->width;
 | 
					 | 
				
			||||||
		configured.height = configure->toplevel_state->height;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (state->server_pending.activated != configured.state.activated) {
 | 
						if (state->server_pending.activated != configured->activated) {
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (state->server_pending.fullscreen != configured.state.fullscreen) {
 | 
						if (state->server_pending.fullscreen != configured->fullscreen) {
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (state->server_pending.maximized != configured.state.maximized) {
 | 
						if (state->server_pending.maximized != configured->maximized) {
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (state->server_pending.resizing != configured.state.resizing) {
 | 
						if (state->server_pending.resizing != configured->resizing) {
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (state->server_pending.width == configured.width &&
 | 
						if (state->server_pending.width == configured->width &&
 | 
				
			||||||
			state->server_pending.height == configured.height) {
 | 
								state->server_pending.height == configured->height) {
 | 
				
			||||||
		return true;
 | 
							return true;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -430,7 +416,10 @@ void handle_xdg_surface_v6_toplevel_committed(struct wlr_xdg_surface_v6 *surface
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// update state that doesn't need compositor approval
 | 
						// 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->current.max_width =
 | 
				
			||||||
		surface->toplevel->client_pending.max_width;
 | 
							surface->toplevel->client_pending.max_width;
 | 
				
			||||||
	surface->toplevel->current.min_width =
 | 
						surface->toplevel->current.min_width =
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue