mirror of
https://gitlab.freedesktop.org/wlroots/wlroots.git
synced 2025-11-01 22:58:38 -04:00
compositor: add wlr_surface_synced
A lot of protocols extend the wl_surface state. Such protocols need to synchronize their extended state with wl_surface.commit and cached states. Add a new utility for this purpose.
This commit is contained in:
parent
34d5af4172
commit
d847516765
2 changed files with 277 additions and 13 deletions
|
|
@ -12,6 +12,7 @@
|
|||
#include "types/wlr_buffer.h"
|
||||
#include "types/wlr_region.h"
|
||||
#include "types/wlr_subcompositor.h"
|
||||
#include "util/array.h"
|
||||
#include "util/time.h"
|
||||
|
||||
#define COMPOSITOR_VERSION 6
|
||||
|
|
@ -272,11 +273,42 @@ static void surface_update_damage(pixman_region32_t *buffer_damage,
|
|||
}
|
||||
}
|
||||
|
||||
static void *surface_synced_create_state(struct wlr_surface_synced *synced) {
|
||||
void *state = calloc(1, synced->impl->state_size);
|
||||
if (state == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (synced->impl->init_state) {
|
||||
synced->impl->init_state(state);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
static void surface_synced_destroy_state(struct wlr_surface_synced *synced,
|
||||
void *state) {
|
||||
if (state == NULL) {
|
||||
return;
|
||||
}
|
||||
if (synced->impl->finish_state) {
|
||||
synced->impl->finish_state(state);
|
||||
}
|
||||
free(state);
|
||||
}
|
||||
|
||||
static void surface_synced_move_state(struct wlr_surface_synced *synced,
|
||||
void *dst, void *src) {
|
||||
if (synced->impl->move_state) {
|
||||
synced->impl->move_state(dst, src);
|
||||
} else {
|
||||
memcpy(dst, src, synced->impl->state_size);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrite state with a copy of the next state, then clear the next state.
|
||||
*/
|
||||
static void surface_state_move(struct wlr_surface_state *state,
|
||||
struct wlr_surface_state *next) {
|
||||
struct wlr_surface_state *next, struct wlr_surface *surface) {
|
||||
state->width = next->width;
|
||||
state->height = next->height;
|
||||
state->buffer_width = next->buffer_width;
|
||||
|
|
@ -331,6 +363,14 @@ static void surface_state_move(struct wlr_surface_state *state,
|
|||
wl_list_init(&next->frame_callback_list);
|
||||
}
|
||||
|
||||
void **state_synced = state->synced.data;
|
||||
void **next_synced = next->synced.data;
|
||||
struct wlr_surface_synced *synced;
|
||||
wl_list_for_each(synced, &surface->synced, link) {
|
||||
surface_synced_move_state(synced,
|
||||
state_synced[synced->index], next_synced[synced->index]);
|
||||
}
|
||||
|
||||
state->committed = next->committed;
|
||||
next->committed = 0;
|
||||
|
||||
|
|
@ -404,21 +444,44 @@ static void surface_update_input_region(struct wlr_surface *surface) {
|
|||
0, 0, surface->current.width, surface->current.height);
|
||||
}
|
||||
|
||||
static void surface_state_init(struct wlr_surface_state *state);
|
||||
static bool surface_state_init(struct wlr_surface_state *state,
|
||||
struct wlr_surface *surface);
|
||||
static void surface_state_finish(struct wlr_surface_state *state);
|
||||
|
||||
static void surface_cache_pending(struct wlr_surface *surface) {
|
||||
struct wlr_surface_state *cached = calloc(1, sizeof(*cached));
|
||||
if (!cached) {
|
||||
wl_resource_post_no_memory(surface->resource);
|
||||
return;
|
||||
goto error;
|
||||
}
|
||||
|
||||
surface_state_init(cached);
|
||||
surface_state_move(cached, &surface->pending);
|
||||
if (!surface_state_init(cached, surface)) {
|
||||
goto error_cached;
|
||||
}
|
||||
|
||||
void **cached_synced = cached->synced.data;
|
||||
struct wlr_surface_synced *synced;
|
||||
wl_list_for_each(synced, &surface->synced, link) {
|
||||
void *synced_state = surface_synced_create_state(synced);
|
||||
if (synced_state == NULL) {
|
||||
goto error_state;
|
||||
}
|
||||
cached_synced[synced->index] = synced_state;
|
||||
}
|
||||
|
||||
surface_state_move(cached, &surface->pending, surface);
|
||||
|
||||
wl_list_insert(surface->cached.prev, &cached->cached_state_link);
|
||||
|
||||
surface->pending.seq++;
|
||||
|
||||
return;
|
||||
|
||||
error_state:
|
||||
surface_state_finish(cached);
|
||||
error_cached:
|
||||
free(cached);
|
||||
error:
|
||||
wl_resource_post_no_memory(surface->resource);
|
||||
}
|
||||
|
||||
static void surface_commit_state(struct wlr_surface *surface,
|
||||
|
|
@ -452,7 +515,7 @@ static void surface_commit_state(struct wlr_surface *surface,
|
|||
surface->previous.buffer_width = surface->current.buffer_width;
|
||||
surface->previous.buffer_height = surface->current.buffer_height;
|
||||
|
||||
surface_state_move(&surface->current, next);
|
||||
surface_state_move(&surface->current, next, surface);
|
||||
|
||||
if (invalid_buffer) {
|
||||
surface_apply_damage(surface);
|
||||
|
|
@ -579,7 +642,8 @@ struct wlr_surface *wlr_surface_from_resource(struct wl_resource *resource) {
|
|||
return wl_resource_get_user_data(resource);
|
||||
}
|
||||
|
||||
static void surface_state_init(struct wlr_surface_state *state) {
|
||||
static bool surface_state_init(struct wlr_surface_state *state,
|
||||
struct wlr_surface *surface) {
|
||||
*state = (struct wlr_surface_state){
|
||||
.scale = 1,
|
||||
.transform = WL_OUTPUT_TRANSFORM_NORMAL,
|
||||
|
|
@ -595,6 +659,10 @@ static void surface_state_init(struct wlr_surface_state *state) {
|
|||
pixman_region32_init(&state->opaque);
|
||||
pixman_region32_init_rect(&state->input,
|
||||
INT32_MIN, INT32_MIN, UINT32_MAX, UINT32_MAX);
|
||||
|
||||
wl_array_init(&state->synced);
|
||||
void *ptr = wl_array_add(&state->synced, surface->synced_len * sizeof(void *));
|
||||
return ptr != NULL;
|
||||
}
|
||||
|
||||
static void surface_state_finish(struct wlr_surface_state *state) {
|
||||
|
|
@ -609,9 +677,18 @@ static void surface_state_finish(struct wlr_surface_state *state) {
|
|||
pixman_region32_fini(&state->buffer_damage);
|
||||
pixman_region32_fini(&state->opaque);
|
||||
pixman_region32_fini(&state->input);
|
||||
|
||||
wl_array_release(&state->synced);
|
||||
}
|
||||
|
||||
static void surface_state_destroy_cached(struct wlr_surface_state *state) {
|
||||
static void surface_state_destroy_cached(struct wlr_surface_state *state,
|
||||
struct wlr_surface *surface) {
|
||||
void **synced_states = state->synced.data;
|
||||
struct wlr_surface_synced *synced;
|
||||
wl_list_for_each(synced, &surface->synced, link) {
|
||||
surface_synced_destroy_state(synced, synced_states[synced->index]);
|
||||
}
|
||||
|
||||
surface_state_finish(state);
|
||||
wl_list_remove(&state->cached_state_link);
|
||||
free(state);
|
||||
|
|
@ -634,10 +711,11 @@ static void surface_handle_resource_destroy(struct wl_resource *resource) {
|
|||
wl_signal_emit_mutable(&surface->events.destroy, surface);
|
||||
|
||||
wlr_addon_set_finish(&surface->addons);
|
||||
assert(wl_list_empty(&surface->synced));
|
||||
|
||||
struct wlr_surface_state *cached, *cached_tmp;
|
||||
wl_list_for_each_safe(cached, cached_tmp, &surface->cached, cached_state_link) {
|
||||
surface_state_destroy_cached(cached);
|
||||
surface_state_destroy_cached(cached, surface);
|
||||
}
|
||||
|
||||
wl_list_remove(&surface->renderer_destroy.link);
|
||||
|
|
@ -682,8 +760,8 @@ static struct wlr_surface *surface_create(struct wl_client *client,
|
|||
|
||||
surface->renderer = renderer;
|
||||
|
||||
surface_state_init(&surface->current);
|
||||
surface_state_init(&surface->pending);
|
||||
surface_state_init(&surface->current, surface);
|
||||
surface_state_init(&surface->pending, surface);
|
||||
surface->pending.seq = 1;
|
||||
|
||||
wl_signal_init(&surface->events.client_commit);
|
||||
|
|
@ -699,6 +777,7 @@ static struct wlr_surface *surface_create(struct wl_client *client,
|
|||
pixman_region32_init(&surface->opaque_region);
|
||||
pixman_region32_init(&surface->input_region);
|
||||
wlr_addon_set_init(&surface->addons);
|
||||
wl_list_init(&surface->synced);
|
||||
|
||||
if (renderer != NULL) {
|
||||
wl_signal_add(&renderer->events.destroy, &surface->renderer_destroy);
|
||||
|
|
@ -860,7 +939,7 @@ void wlr_surface_unlock_cached(struct wlr_surface *surface, uint32_t seq) {
|
|||
}
|
||||
|
||||
surface_commit_state(surface, next);
|
||||
surface_state_destroy_cached(next);
|
||||
surface_state_destroy_cached(next, surface);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1258,3 +1337,125 @@ struct wlr_compositor *wlr_compositor_create(struct wl_display *display,
|
|||
|
||||
return compositor;
|
||||
}
|
||||
|
||||
static bool surface_state_add_synced(struct wlr_surface_state *state, void *value) {
|
||||
void **ptr = wl_array_add(&state->synced, sizeof(void *));
|
||||
if (ptr == NULL) {
|
||||
return false;
|
||||
}
|
||||
*ptr = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void *surface_state_remove_synced(struct wlr_surface_state *state,
|
||||
struct wlr_surface_synced *synced) {
|
||||
void **synced_states = state->synced.data;
|
||||
void *synced_state = synced_states[synced->index];
|
||||
array_remove_at(&state->synced, synced->index * sizeof(void *), sizeof(void *));
|
||||
return synced_state;
|
||||
}
|
||||
|
||||
static void surface_state_remove_and_destroy_synced(struct wlr_surface_state *state,
|
||||
struct wlr_surface_synced *synced) {
|
||||
void *synced_state = surface_state_remove_synced(state, synced);
|
||||
surface_synced_destroy_state(synced, synced_state);
|
||||
}
|
||||
|
||||
bool wlr_surface_synced_init(struct wlr_surface_synced *synced,
|
||||
struct wlr_surface *surface, const struct wlr_surface_synced_impl *impl,
|
||||
void *pending, void *current) {
|
||||
assert(impl->state_size > 0);
|
||||
|
||||
struct wlr_surface_synced *other;
|
||||
wl_list_for_each(other, &surface->synced, link) {
|
||||
assert(synced != other);
|
||||
}
|
||||
|
||||
memset(pending, 0, impl->state_size);
|
||||
memset(current, 0, impl->state_size);
|
||||
if (impl->init_state) {
|
||||
impl->init_state(pending);
|
||||
impl->init_state(current);
|
||||
}
|
||||
if (!surface_state_add_synced(&surface->pending, pending)) {
|
||||
goto error_init;
|
||||
}
|
||||
if (!surface_state_add_synced(&surface->current, current)) {
|
||||
goto error_pending;
|
||||
}
|
||||
|
||||
*synced = (struct wlr_surface_synced){
|
||||
.surface = surface,
|
||||
.impl = impl,
|
||||
.index = surface->synced_len,
|
||||
};
|
||||
|
||||
struct wlr_surface_state *cached;
|
||||
wl_list_for_each(cached, &surface->cached, cached_state_link) {
|
||||
void *synced_state = surface_synced_create_state(synced);
|
||||
if (synced_state == NULL ||
|
||||
!surface_state_add_synced(cached, synced_state)) {
|
||||
surface_synced_destroy_state(synced, synced_state);
|
||||
goto error_cached;
|
||||
}
|
||||
}
|
||||
|
||||
wl_list_insert(&surface->synced, &synced->link);
|
||||
surface->synced_len++;
|
||||
|
||||
return true;
|
||||
|
||||
error_cached:;
|
||||
struct wlr_surface_state *failed_at = cached;
|
||||
wl_list_for_each(cached, &surface->cached, cached_state_link) {
|
||||
if (cached == failed_at) {
|
||||
break;
|
||||
}
|
||||
surface_state_remove_and_destroy_synced(cached, synced);
|
||||
}
|
||||
surface_state_remove_synced(&surface->current, synced);
|
||||
error_pending:
|
||||
surface_state_remove_synced(&surface->pending, synced);
|
||||
error_init:
|
||||
if (synced->impl->finish_state) {
|
||||
synced->impl->finish_state(pending);
|
||||
synced->impl->finish_state(current);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void wlr_surface_synced_finish(struct wlr_surface_synced *synced) {
|
||||
struct wlr_surface *surface = synced->surface;
|
||||
|
||||
bool found = false;
|
||||
struct wlr_surface_synced *other;
|
||||
wl_list_for_each(other, &surface->synced, link) {
|
||||
if (other == synced) {
|
||||
found = true;
|
||||
} else if (other->index > synced->index) {
|
||||
other->index--;
|
||||
}
|
||||
}
|
||||
assert(found);
|
||||
|
||||
struct wlr_surface_state *cached;
|
||||
wl_list_for_each(cached, &surface->cached, cached_state_link) {
|
||||
surface_state_remove_and_destroy_synced(cached, synced);
|
||||
}
|
||||
|
||||
void *pending = surface_state_remove_synced(&surface->pending, synced);
|
||||
void *current = surface_state_remove_synced(&surface->current, synced);
|
||||
if (synced->impl->finish_state) {
|
||||
synced->impl->finish_state(pending);
|
||||
synced->impl->finish_state(current);
|
||||
}
|
||||
|
||||
wl_list_remove(&synced->link);
|
||||
synced->surface->synced_len--;
|
||||
}
|
||||
|
||||
void *wlr_surface_synced_get_state(struct wlr_surface_synced *synced,
|
||||
const struct wlr_surface_state *state) {
|
||||
void **synced_states = state->synced.data;
|
||||
return synced_states[synced->index];
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue