mirror of
https://gitlab.freedesktop.org/wlroots/wlroots.git
synced 2024-11-22 07:02:28 +00: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
@ -69,6 +69,9 @@ struct wlr_surface_state {
|
||||
// Number of locks that prevent this surface state from being committed.
|
||||
size_t cached_state_locks;
|
||||
struct wl_list cached_state_link; // wlr_surface.cached
|
||||
|
||||
// Sync'ed object states, one per struct wlr_surface_synced
|
||||
struct wl_array synced; // void *
|
||||
};
|
||||
|
||||
struct wlr_surface_role {
|
||||
@ -222,6 +225,9 @@ struct wlr_surface {
|
||||
int32_t preferred_buffer_scale;
|
||||
bool preferred_buffer_transform_sent;
|
||||
enum wl_output_transform preferred_buffer_transform;
|
||||
|
||||
struct wl_list synced; // wlr_surface_synced.link
|
||||
size_t synced_len;
|
||||
};
|
||||
|
||||
struct wlr_renderer;
|
||||
@ -430,6 +436,63 @@ void wlr_surface_set_preferred_buffer_scale(struct wlr_surface *surface,
|
||||
void wlr_surface_set_preferred_buffer_transform(struct wlr_surface *surface,
|
||||
enum wl_output_transform transform);
|
||||
|
||||
/**
|
||||
* Implementation for struct wlr_surface_synced.
|
||||
*
|
||||
* struct wlr_surface takes care of allocating the sync'ed object state.
|
||||
*
|
||||
* The only mandatory field is state_size.
|
||||
*/
|
||||
struct wlr_surface_synced_impl {
|
||||
// Size in bytes of the state struct.
|
||||
size_t state_size;
|
||||
// Initialize a state. If NULL, this is a no-op.
|
||||
void (*init_state)(void *state);
|
||||
// Finish a state. If NULL, this is a no-op.
|
||||
void (*finish_state)(void *state);
|
||||
// Move a state. If NULL, memcpy() is used.
|
||||
void (*move_state)(void *dst, void *src);
|
||||
};
|
||||
|
||||
/**
|
||||
* An object synchronized with a surface.
|
||||
*
|
||||
* This is typically used by surface add-ons which integrate with the surface
|
||||
* commit mechanism.
|
||||
*
|
||||
* A sync'ed object maintains state whose lifecycle is managed by
|
||||
* struct wlr_surface_synced_impl. Clients make requests to mutate the pending
|
||||
* state, then clients commit the pending state via wl_surface.commit. The
|
||||
* pending state may become cached, then becomes current when it's applied.
|
||||
*/
|
||||
struct wlr_surface_synced {
|
||||
struct wlr_surface *surface;
|
||||
const struct wlr_surface_synced_impl *impl;
|
||||
struct wl_list link; // wlr_surface.synced
|
||||
size_t index;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize a sync'ed object.
|
||||
*
|
||||
* pending and current must be pointers to the sync'ed object's state. This
|
||||
* function will initialize them.
|
||||
*/
|
||||
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);
|
||||
/**
|
||||
* Finish a sync'ed object.
|
||||
*
|
||||
* This must be called before the struct wlr_surface is destroyed.
|
||||
*/
|
||||
void wlr_surface_synced_finish(struct wlr_surface_synced *synced);
|
||||
/**
|
||||
* Obtain a sync'ed object state.
|
||||
*/
|
||||
void *wlr_surface_synced_get_state(struct wlr_surface_synced *synced,
|
||||
const struct wlr_surface_state *state);
|
||||
|
||||
/**
|
||||
* Get a Pixman region from a wl_region resource.
|
||||
*/
|
||||
|
@ -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…
Reference in New Issue
Block a user