aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Ser <contact@emersion.fr>2023-12-04 22:43:13 +0100
committerSimon Ser <contact@emersion.fr>2023-12-25 11:47:15 +0100
commitd847516765879936844ffcf94c4c6d9c04727539 (patch)
tree6b38d423fa67a9fe922e872a9ae5156a8f67186a
parent34d5af41727c1879188caf3a00fc1db9f480b1fd (diff)
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.
-rw-r--r--include/wlr/types/wlr_compositor.h63
-rw-r--r--types/wlr_compositor.c227
2 files changed, 277 insertions, 13 deletions
diff --git a/include/wlr/types/wlr_compositor.h b/include/wlr/types/wlr_compositor.h
index eb870b7b..94262b4b 100644
--- a/include/wlr/types/wlr_compositor.h
+++ b/include/wlr/types/wlr_compositor.h
@@ -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;
@@ -431,6 +437,63 @@ 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.
*/
const pixman_region32_t *wlr_region_from_resource(struct wl_resource *resource);
diff --git a/types/wlr_compositor.c b/types/wlr_compositor.c
index 91996037..d9c7b3c1 100644
--- a/types/wlr_compositor.c
+++ b/types/wlr_compositor.c
@@ -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;
+ }
+
+ if (!surface_state_init(cached, surface)) {
+ goto error_cached;
}
- surface_state_init(cached);
- surface_state_move(cached, &surface->pending);
+ 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];
+}