mirror of
https://gitlab.freedesktop.org/wlroots/wlroots.git
synced 2025-11-25 06:59:42 -05:00
Add wlr_subsurface for new wlr_surface API
This commit is contained in:
parent
e76d4581ce
commit
1160a83903
4 changed files with 641 additions and 122 deletions
|
|
@ -9,97 +9,6 @@
|
|||
#include "util/signal.h"
|
||||
|
||||
#define COMPOSITOR_VERSION 4
|
||||
#define SUBCOMPOSITOR_VERSION 1
|
||||
|
||||
extern const struct wlr_surface_role subsurface_role;
|
||||
|
||||
bool wlr_surface_is_subsurface(struct wlr_surface *surface) {
|
||||
return surface->role == &subsurface_role;
|
||||
}
|
||||
|
||||
struct wlr_subsurface *wlr_subsurface_from_wlr_surface(
|
||||
struct wlr_surface *surface) {
|
||||
assert(wlr_surface_is_subsurface(surface));
|
||||
return (struct wlr_subsurface *)surface->role_data;
|
||||
}
|
||||
|
||||
static void subcompositor_handle_destroy(struct wl_client *client,
|
||||
struct wl_resource *resource) {
|
||||
wl_resource_destroy(resource);
|
||||
}
|
||||
|
||||
static void subcompositor_handle_get_subsurface(struct wl_client *client,
|
||||
struct wl_resource *resource, uint32_t id,
|
||||
struct wl_resource *surface_resource,
|
||||
struct wl_resource *parent_resource) {
|
||||
struct wlr_surface *surface = wlr_surface_from_resource(surface_resource);
|
||||
struct wlr_surface *parent = wlr_surface_from_resource(parent_resource);
|
||||
|
||||
static const char msg[] = "get_subsurface: wl_subsurface@";
|
||||
|
||||
if (surface == parent) {
|
||||
wl_resource_post_error(resource,
|
||||
WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE,
|
||||
"%s%d: wl_surface@%d cannot be its own parent",
|
||||
msg, id, wl_resource_get_id(surface_resource));
|
||||
return;
|
||||
}
|
||||
|
||||
if (wlr_surface_is_subsurface(surface) &&
|
||||
wlr_subsurface_from_wlr_surface(surface) != NULL) {
|
||||
wl_resource_post_error(resource,
|
||||
WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE,
|
||||
"%s%d: wl_surface@%d is already a sub-surface",
|
||||
msg, id, wl_resource_get_id(surface_resource));
|
||||
return;
|
||||
}
|
||||
|
||||
if (wlr_surface_get_root_surface(parent) == surface) {
|
||||
wl_resource_post_error(resource,
|
||||
WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE,
|
||||
"%s%d: wl_surface@%d is an ancestor of parent",
|
||||
msg, id, wl_resource_get_id(surface_resource));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!wlr_surface_set_role(surface, &subsurface_role, NULL,
|
||||
resource, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
wlr_subsurface_create(surface, parent,
|
||||
wl_resource_get_version(resource), id);
|
||||
}
|
||||
|
||||
static const struct wl_subcompositor_interface subcompositor_impl = {
|
||||
.destroy = subcompositor_handle_destroy,
|
||||
.get_subsurface = subcompositor_handle_get_subsurface,
|
||||
};
|
||||
|
||||
static void subcompositor_bind(struct wl_client *client, void *data,
|
||||
uint32_t version, uint32_t id) {
|
||||
struct wl_resource *resource =
|
||||
wl_resource_create(client, &wl_subcompositor_interface, 1, id);
|
||||
if (resource == NULL) {
|
||||
wl_client_post_no_memory(client);
|
||||
return;
|
||||
}
|
||||
wl_resource_set_implementation(resource, &subcompositor_impl, NULL, NULL);
|
||||
}
|
||||
|
||||
static void subcompositor_init(struct wlr_subcompositor *sc,
|
||||
struct wl_display *display) {
|
||||
sc->global = wl_global_create(display, &wl_subcompositor_interface,
|
||||
SUBCOMPOSITOR_VERSION, NULL, subcompositor_bind);
|
||||
if (sc->global == NULL) {
|
||||
wlr_log_errno(WLR_ERROR, "Failed to create subcompositor global");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void subcompositor_finish(struct wlr_subcompositor *sc) {
|
||||
wl_global_destroy(sc->global);
|
||||
}
|
||||
|
||||
static const struct wl_region_interface region_impl;
|
||||
|
||||
|
|
@ -153,17 +62,18 @@ static struct wlr_commit *commit_create(struct wlr_surface_2 *surf) {
|
|||
}
|
||||
|
||||
// Prevent a zero allocation
|
||||
size_t len = comp->ids ? comp->ids : 1;
|
||||
|
||||
c->state = calloc(len, sizeof(*c->state));
|
||||
c->state_len = comp->ids ? comp->ids : 1;
|
||||
c->state = calloc(c->state_len, sizeof(*c->state));
|
||||
if (!c->state) {
|
||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||
free(c);
|
||||
return NULL;
|
||||
}
|
||||
c->state_len = len;
|
||||
|
||||
c->surface = surf;
|
||||
wl_signal_init(&c->events.commit);
|
||||
wl_signal_init(&c->events.complete);
|
||||
wl_signal_init(&c->events.destroy);
|
||||
|
||||
pixman_region32_init(&c->surface_damage);
|
||||
wl_list_init(&c->frame_callbacks);
|
||||
|
|
@ -188,7 +98,7 @@ static void commit_destroy(struct wlr_commit *c) {
|
|||
free(c);
|
||||
}
|
||||
|
||||
static bool commit_is_complete(struct wlr_commit *c) {
|
||||
bool wlr_commit_is_complete(struct wlr_commit *c) {
|
||||
return c->committed && c->inhibit == 0;
|
||||
}
|
||||
|
||||
|
|
@ -202,14 +112,13 @@ static bool commit_is_latest(struct wlr_commit *c) {
|
|||
return true;
|
||||
}
|
||||
|
||||
if (commit_is_complete(iter)) {
|
||||
if (wlr_commit_is_complete(iter)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// You shouldn't be able to get here.
|
||||
assert(0);
|
||||
return false;
|
||||
abort();
|
||||
}
|
||||
|
||||
static void surface_prune_commits(struct wlr_surface_2 *surf) {
|
||||
|
|
@ -217,15 +126,16 @@ static void surface_prune_commits(struct wlr_surface_2 *surf) {
|
|||
struct wlr_commit *iter, *tmp;
|
||||
wl_list_for_each_safe(iter, tmp, &surf->committed, link) {
|
||||
if (!complete) {
|
||||
complete = commit_is_complete(iter);
|
||||
complete = wlr_commit_is_complete(iter);
|
||||
} else if (iter->ref_cnt == 0) {
|
||||
wl_list_remove(&iter->link);
|
||||
commit_destroy(iter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wlr_commit_inhibit(struct wlr_commit *commit) {
|
||||
assert(commit && !commit_is_complete(commit));
|
||||
assert(commit && !wlr_commit_is_complete(commit));
|
||||
++commit->inhibit;
|
||||
}
|
||||
|
||||
|
|
@ -233,7 +143,7 @@ void wlr_commit_uninhibit(struct wlr_commit *commit) {
|
|||
assert(commit && commit->inhibit > 0);
|
||||
--commit->inhibit;
|
||||
|
||||
if (commit_is_complete(commit)) {
|
||||
if (wlr_commit_is_complete(commit)) {
|
||||
wlr_signal_emit_safe(&commit->events.complete, commit);
|
||||
|
||||
if (commit_is_latest(commit)) {
|
||||
|
|
@ -435,7 +345,7 @@ static void surface_commit(struct wl_client *client, struct wl_resource *res) {
|
|||
|
||||
surface_prune_commits(surf);
|
||||
|
||||
if (commit_is_complete(commit)) {
|
||||
if (wlr_commit_is_complete(commit)) {
|
||||
wlr_signal_emit_safe(&commit->events.complete, commit);
|
||||
wlr_signal_emit_safe(&surf->events.commit, surf);
|
||||
}
|
||||
|
|
@ -455,7 +365,7 @@ static struct wl_surface_interface surface_impl = {
|
|||
};
|
||||
|
||||
struct wlr_surface_2 *wlr_surface_from_resource_2(struct wl_resource *resource) {
|
||||
assert(wl_resource_instance_of(resource, &wl_compositor_interface,
|
||||
assert(wl_resource_instance_of(resource, &wl_surface_interface,
|
||||
&surface_impl));
|
||||
return wl_resource_get_user_data(resource);
|
||||
}
|
||||
|
|
@ -466,10 +376,14 @@ static void surface_resource_destroy(struct wl_resource *resource) {
|
|||
wlr_signal_emit_safe(&surf->events.destroy, surf);
|
||||
|
||||
struct wlr_commit *iter, *tmp;
|
||||
wl_list_for_each_reverse_safe(iter, tmp, &surf->committed, link) {
|
||||
commit_destroy(iter);
|
||||
wl_list_for_each_safe(iter, tmp, &surf->committed, link) {
|
||||
if (iter->ref_cnt == 0) {
|
||||
commit_destroy(iter);
|
||||
}
|
||||
}
|
||||
|
||||
commit_destroy(surf->pending);
|
||||
|
||||
free(surf);
|
||||
}
|
||||
|
||||
|
|
@ -500,8 +414,11 @@ static void compositor_create_surface(struct wl_client *client,
|
|||
wl_resource_set_implementation(surf->resource, &surface_impl,
|
||||
surf, surface_resource_destroy);
|
||||
|
||||
wl_signal_init(&surf->events.destroy);
|
||||
surf->compositor = compositor;
|
||||
wl_list_init(&surf->committed);
|
||||
wl_list_init(&surf->frame_callbacks);
|
||||
wl_signal_init(&surf->events.commit);
|
||||
wl_signal_init(&surf->events.destroy);
|
||||
|
||||
surf->pending = commit_create(surf);
|
||||
if (!surf->pending) {
|
||||
|
|
@ -571,10 +488,7 @@ static void compositor_bind(struct wl_client *client, void *data,
|
|||
static void compositor_display_destroy(struct wl_listener *listener, void *data) {
|
||||
struct wlr_compositor *comp = wl_container_of(listener, comp, display_destroy);
|
||||
|
||||
subcompositor_finish(&comp->subcompositor);
|
||||
wl_list_remove(&comp->display_destroy.link);
|
||||
wl_global_destroy(comp->global);
|
||||
|
||||
free(comp);
|
||||
}
|
||||
|
||||
|
|
@ -586,6 +500,7 @@ struct wlr_compositor *wlr_compositor_create(struct wl_display *display,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
comp->display = display;
|
||||
comp->global = wl_global_create(display, &wl_compositor_interface,
|
||||
COMPOSITOR_VERSION, comp, compositor_bind);
|
||||
if (!comp->global) {
|
||||
|
|
@ -599,8 +514,6 @@ struct wlr_compositor *wlr_compositor_create(struct wl_display *display,
|
|||
wl_signal_init(&comp->events.new_surface_2);
|
||||
wl_signal_init(&comp->events.new_state);
|
||||
|
||||
subcompositor_init(&comp->subcompositor, display);
|
||||
|
||||
comp->display_destroy.notify = compositor_display_destroy;
|
||||
wl_display_add_destroy_listener(display, &comp->display_destroy);
|
||||
|
||||
|
|
@ -617,7 +530,7 @@ struct wlr_commit *wlr_surface_get_commit(struct wlr_surface_2 *surf) {
|
|||
|
||||
struct wlr_commit *iter;
|
||||
wl_list_for_each(iter, &surf->committed, link) {
|
||||
if (commit_is_complete(iter)) {
|
||||
if (wlr_commit_is_complete(iter)) {
|
||||
++iter->ref_cnt;
|
||||
return iter;
|
||||
}
|
||||
|
|
@ -630,6 +543,16 @@ struct wlr_commit *wlr_surface_get_pending(struct wlr_surface_2 *surf) {
|
|||
return surf->pending;
|
||||
}
|
||||
|
||||
struct wlr_commit *wlr_surface_get_latest(struct wlr_surface_2 *surf) {
|
||||
if (wl_list_empty(&surf->committed)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct wlr_commit *first = wl_container_of(surf->committed.next, first, link);
|
||||
++first->ref_cnt;
|
||||
return first;
|
||||
}
|
||||
|
||||
void wlr_surface_send_enter_2(struct wlr_surface_2 *surf, struct wlr_output *output) {
|
||||
struct wl_client *client = wl_resource_get_client(surf->resource);
|
||||
struct wl_resource *iter;
|
||||
|
|
@ -663,9 +586,28 @@ void wlr_surface_send_frame_done_2(struct wlr_surface_2 *surf,
|
|||
}
|
||||
}
|
||||
|
||||
struct wlr_commit *wlr_commit_ref(struct wlr_commit *commit) {
|
||||
assert(commit);
|
||||
++commit->ref_cnt;
|
||||
|
||||
return commit;
|
||||
}
|
||||
|
||||
void wlr_commit_unref(struct wlr_commit *commit) {
|
||||
assert(commit && commit->ref_cnt > 0);
|
||||
--commit->ref_cnt;
|
||||
|
||||
surface_prune_commits(commit->surface);
|
||||
}
|
||||
|
||||
bool wlr_surface_set_role_2(struct wlr_surface_2 *surf, const char *name) {
|
||||
assert(surf && name);
|
||||
|
||||
/* Already has role */
|
||||
if (surf->role_name && strcmp(surf->role_name, name) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
surf->role_name = name;
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
476
types/wlr_subcompositor.c
Normal file
476
types/wlr_subcompositor.c
Normal file
|
|
@ -0,0 +1,476 @@
|
|||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <wayland-server.h>
|
||||
|
||||
#include <wlr/types/wlr_compositor.h>
|
||||
#include <wlr/types/wlr_subcompositor.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include "util/signal.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define SUBCOMPOSITOR_VERSION 1
|
||||
|
||||
struct subsurface_state {
|
||||
struct wl_list link;
|
||||
|
||||
struct wlr_subsurface *subsurface;
|
||||
struct wlr_commit *commit; // Does have ref count
|
||||
struct wlr_commit *parent; // Does NOT have ref count
|
||||
|
||||
bool synchronized;
|
||||
bool inhibiting_parent;
|
||||
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
|
||||
struct wl_listener subsurface_destroy;
|
||||
struct wl_listener commit_complete;
|
||||
};
|
||||
|
||||
struct subsurface_root {
|
||||
/* This list is in stacking order.
|
||||
* The first element is the bottom of the stack (i.e. drawn first).
|
||||
* The last element is the top of the stack (i.e. drawn last).
|
||||
*/
|
||||
struct wl_list subsurfaces; // subsurface_state.link
|
||||
|
||||
/* This represents the root surface.
|
||||
* - It will always appear in the above subsurfaces list
|
||||
* - subsurface will always be NULL
|
||||
* - commit will always be NULL
|
||||
* - (x, y) will always be (0, 0)
|
||||
*/
|
||||
struct subsurface_state root;
|
||||
|
||||
struct wl_listener commit_destroy;
|
||||
struct wl_listener commit_committed;
|
||||
};
|
||||
|
||||
static const struct wl_subsurface_interface subsurface_impl;
|
||||
|
||||
static struct wlr_subsurface *subsurface_from_resource(struct wl_resource *res) {
|
||||
assert(wl_resource_instance_of(res, &wl_subsurface_interface,
|
||||
&subsurface_impl));
|
||||
return wl_resource_get_user_data(res);
|
||||
}
|
||||
|
||||
static void subsurface_destroy(struct wl_client *client, struct wl_resource *res) {
|
||||
wl_resource_destroy(res);
|
||||
}
|
||||
|
||||
static void subsurface_set_position(struct wl_client *client,
|
||||
struct wl_resource *res, int32_t x, int32_t y) {
|
||||
struct wlr_subsurface *ss = subsurface_from_resource(res);
|
||||
struct wlr_commit *commit = wlr_surface_get_pending(ss->parent);
|
||||
}
|
||||
|
||||
static void subsurface_place_above(struct wl_client *client,
|
||||
struct wl_resource *res, struct wl_resource *sibling_res) {
|
||||
struct wlr_subsurface *ss = subsurface_from_resource(res);
|
||||
if (!ss) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void subsurface_place_below(struct wl_client *client,
|
||||
struct wl_resource *res, struct wl_resource *sibling_res) {
|
||||
struct wlr_subsurface *ss = subsurface_from_resource(res);
|
||||
if (!ss) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void subsurface_set_sync(struct wl_client *client, struct wl_resource *res) {
|
||||
struct wlr_subsurface *ss = subsurface_from_resource(res);
|
||||
if (!ss) {
|
||||
return;
|
||||
}
|
||||
|
||||
ss->synchronized = true;
|
||||
}
|
||||
|
||||
static void subsurface_set_desync(struct wl_client *client, struct wl_resource *res) {
|
||||
struct wlr_subsurface *ss = subsurface_from_resource(res);
|
||||
if (!ss) {
|
||||
return;
|
||||
}
|
||||
|
||||
ss->synchronized = false;
|
||||
}
|
||||
|
||||
static const struct wl_subsurface_interface subsurface_impl = {
|
||||
.destroy = subsurface_destroy,
|
||||
.set_position = subsurface_set_position,
|
||||
.place_above = subsurface_place_above,
|
||||
.place_below = subsurface_place_below,
|
||||
.set_sync = subsurface_set_sync,
|
||||
.set_desync = subsurface_set_desync,
|
||||
};
|
||||
|
||||
static void subcompositor_destroy(struct wl_client *client,
|
||||
struct wl_resource *res) {
|
||||
wl_resource_destroy(res);
|
||||
}
|
||||
|
||||
static const struct wl_subcompositor_interface subcompositor_impl;
|
||||
|
||||
static struct wlr_subcompositor *subcompositor_from_resource(struct wl_resource *res) {
|
||||
assert(wl_resource_instance_of(res, &wl_subcompositor_interface,
|
||||
&subcompositor_impl));
|
||||
return wl_resource_get_user_data(res);
|
||||
}
|
||||
|
||||
static void wlr_subsurface_destroy(struct wlr_subsurface *ss)
|
||||
{
|
||||
wlr_signal_emit_safe(&ss->events.destroy, ss);
|
||||
|
||||
ss->surface->role_data = NULL;
|
||||
wl_list_remove(&ss->surface_destroy.link);
|
||||
if (ss->parent) {
|
||||
wl_list_remove(&ss->parent_destroy.link);
|
||||
}
|
||||
free(ss);
|
||||
}
|
||||
|
||||
static void subsurface_resource_destroy(struct wl_resource *res) {
|
||||
struct wlr_subsurface *ss = subsurface_from_resource(res);
|
||||
|
||||
if (ss) {
|
||||
wlr_subsurface_destroy(ss);
|
||||
}
|
||||
}
|
||||
|
||||
static void subsurface_surface_destroy(struct wl_listener *listener, void *data) {
|
||||
struct wlr_subsurface *ss = wl_container_of(listener, ss, surface_destroy);
|
||||
|
||||
/* Make resource inert */
|
||||
wl_resource_set_user_data(ss->resource, NULL);
|
||||
|
||||
wlr_subsurface_destroy(ss);
|
||||
}
|
||||
|
||||
static void subsurface_parent_destroy(struct wl_listener *listener, void *data) {
|
||||
struct wlr_subsurface *ss = wl_container_of(listener, ss, parent_destroy);
|
||||
|
||||
wl_list_remove(&ss->parent_destroy.link);
|
||||
ss->parent = NULL;
|
||||
}
|
||||
|
||||
static void state_subsurface_destroy(struct wl_listener *listener, void *data) {
|
||||
struct subsurface_state *st = wl_container_of(listener, st, subsurface_destroy);
|
||||
|
||||
wl_list_remove(&st->subsurface_destroy.link);
|
||||
st->subsurface = NULL;
|
||||
|
||||
/* We don't have a surface or saved commit, so this is now useless */
|
||||
if (!st->commit) {
|
||||
wl_list_remove(&st->link);
|
||||
free(st);
|
||||
}
|
||||
}
|
||||
|
||||
static void subcompositor_get_subsurface(struct wl_client *client,
|
||||
struct wl_resource *sc_res, uint32_t id,
|
||||
struct wl_resource *surface_res, struct wl_resource *parent_res) {
|
||||
struct wlr_subcompositor *sc = subcompositor_from_resource(sc_res);
|
||||
struct wlr_surface_2 *surface = wlr_surface_from_resource_2(surface_res);
|
||||
struct wlr_surface_2 *parent = wlr_surface_from_resource_2(parent_res);
|
||||
|
||||
/* Check for protocol violations */
|
||||
|
||||
if (surface == parent) {
|
||||
wl_resource_post_error(sc_res, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE,
|
||||
"surface cannot be its own parent");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!wlr_surface_set_role_2(surface, "wl_subsurface")) {
|
||||
wl_resource_post_error(sc_res, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE,
|
||||
"surface already has role");
|
||||
return;
|
||||
}
|
||||
|
||||
if (surface->role_data != NULL) {
|
||||
wl_resource_post_error(sc_res, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE,
|
||||
"surface is already a sub-surface");
|
||||
return;
|
||||
}
|
||||
|
||||
if (wlr_surface_get_root_surface(parent) == surface) {
|
||||
wl_resource_post_error(sc_res, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE,
|
||||
"surface is an ancestor of parent");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Add role state for surface */
|
||||
|
||||
struct wlr_subsurface *ss = calloc(1, sizeof(*ss));
|
||||
if (!ss) {
|
||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||
goto error_post;
|
||||
}
|
||||
|
||||
ss->resource = wl_resource_create(client, &wl_subsurface_interface,
|
||||
wl_resource_get_version(sc_res), id);
|
||||
if (!ss->resource) {
|
||||
wlr_log_errno(WLR_ERROR, "Failed to create subsurface resource");
|
||||
goto error_ss;
|
||||
}
|
||||
|
||||
ss->surface = surface;
|
||||
ss->parent = parent;
|
||||
ss->synchronized = true;
|
||||
wl_signal_init(&ss->events.destroy);
|
||||
ss->surface_destroy.notify = subsurface_surface_destroy;
|
||||
wl_signal_add(&surface->events.destroy, &ss->surface_destroy);
|
||||
ss->parent_destroy.notify = subsurface_parent_destroy;
|
||||
wl_signal_add(&parent->events.destroy, &ss->parent_destroy);
|
||||
|
||||
surface->role_data = ss;
|
||||
|
||||
/* Insert into parent's sub-surface tree */
|
||||
|
||||
struct wlr_commit *parent_commit = wlr_surface_get_pending(parent);
|
||||
struct subsurface_root *root = wlr_commit_get(parent_commit, sc->root_id);
|
||||
assert(root);
|
||||
|
||||
struct subsurface_state *st = calloc(1, sizeof(*st));
|
||||
if (!st) {
|
||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||
goto error_res;
|
||||
}
|
||||
|
||||
st->subsurface = ss;
|
||||
st->subsurface_destroy.notify = state_subsurface_destroy;
|
||||
wl_signal_add(&ss->events.destroy, &st->subsurface_destroy);
|
||||
wl_list_insert(root->subsurfaces.prev, &st->link);
|
||||
|
||||
wl_resource_set_implementation(ss->resource, &subsurface_impl,
|
||||
ss, subsurface_resource_destroy);
|
||||
return;
|
||||
|
||||
error_res:
|
||||
wl_resource_destroy(ss->resource);
|
||||
wl_list_remove(&ss->surface_destroy.link);
|
||||
wl_list_remove(&ss->parent_destroy.link);
|
||||
error_ss:
|
||||
free(ss);
|
||||
error_post:
|
||||
wl_resource_post_no_memory(sc_res);
|
||||
}
|
||||
|
||||
static const struct wl_subcompositor_interface subcompositor_impl = {
|
||||
.destroy = subcompositor_destroy,
|
||||
.get_subsurface = subcompositor_get_subsurface,
|
||||
};
|
||||
|
||||
static void subcompositor_bind(struct wl_client *client, void *data,
|
||||
uint32_t version, uint32_t id) {
|
||||
struct wlr_subcompositor *sc = data;
|
||||
|
||||
struct wl_resource *res =
|
||||
wl_resource_create(client, &wl_subcompositor_interface, version, id);
|
||||
if (!res) {
|
||||
wlr_log_errno(WLR_ERROR, "Failed to create subcompositor resource");
|
||||
wl_client_post_no_memory(client);
|
||||
return;
|
||||
}
|
||||
|
||||
wl_resource_set_implementation(res, &subcompositor_impl, sc, NULL);
|
||||
}
|
||||
|
||||
static void subsurface_complete(struct wl_listener *listener, void *data) {
|
||||
struct subsurface_state *st = wl_container_of(listener, st, commit_complete);
|
||||
|
||||
st->inhibiting_parent = false;
|
||||
wl_list_remove(&st->commit_complete.link);
|
||||
|
||||
wlr_commit_uninhibit(st->parent);
|
||||
st->parent = NULL;
|
||||
}
|
||||
|
||||
static void commit_committed(struct wl_listener *listener, void *data) {
|
||||
struct subsurface_root *root = wl_container_of(listener, root, commit_committed);
|
||||
struct wlr_commit *parent = data;
|
||||
|
||||
struct subsurface_state *iter;
|
||||
wl_list_for_each(iter, &root->subsurfaces, link) {
|
||||
if (iter == &root->root) {
|
||||
continue;
|
||||
}
|
||||
|
||||
struct wlr_subsurface *ss = iter->subsurface;
|
||||
|
||||
iter->synchronized = ss->synchronized;
|
||||
if (!iter->synchronized) {
|
||||
continue;
|
||||
}
|
||||
|
||||
iter->commit = wlr_surface_get_latest(ss->surface);
|
||||
if (iter->commit && !wlr_commit_is_complete(iter->commit)) {
|
||||
iter->parent = parent;
|
||||
iter->inhibiting_parent = true;
|
||||
wlr_commit_inhibit(parent);
|
||||
|
||||
iter->commit_complete.notify = subsurface_complete;
|
||||
wl_signal_add(&iter->commit->events.complete, &iter->commit_complete);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void commit_destroy(struct wl_listener *listener, void *data) {
|
||||
struct subsurface_root *root = wl_container_of(listener, root, commit_destroy);
|
||||
|
||||
struct subsurface_state *iter, *tmp;
|
||||
wl_list_for_each_safe(iter, tmp, &root->subsurfaces, link) {
|
||||
if (iter == &root->root) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (iter->subsurface) {
|
||||
wl_list_remove(&iter->subsurface_destroy.link);
|
||||
}
|
||||
if (iter->inhibiting_parent) {
|
||||
wl_list_remove(&iter->commit_complete.link);
|
||||
}
|
||||
if (iter->commit) {
|
||||
wlr_commit_unref(iter->commit);
|
||||
}
|
||||
free(iter);
|
||||
}
|
||||
|
||||
wl_list_remove(&root->commit_destroy.link);
|
||||
free(root);
|
||||
}
|
||||
|
||||
static void subcompositor_new_state(struct wl_listener *listener, void *data) {
|
||||
struct wlr_subcompositor *sc = wl_container_of(listener, sc, new_state);
|
||||
struct wlr_compositor_new_state_args *args = data;
|
||||
struct wlr_commit *new = args->new;
|
||||
|
||||
struct subsurface_root *root = calloc(1, sizeof(*root));
|
||||
wl_list_init(&root->subsurfaces);
|
||||
root->commit_committed.notify = commit_committed;
|
||||
wl_signal_add(&new->events.commit, &root->commit_committed);
|
||||
root->commit_destroy.notify = commit_destroy;
|
||||
wl_signal_add(&new->events.destroy, &root->commit_destroy);
|
||||
|
||||
if (args->old) {
|
||||
struct subsurface_root *old_root = wlr_commit_get(args->old, sc->root_id);
|
||||
|
||||
struct subsurface_state *iter;
|
||||
wl_list_for_each(iter, &old_root->subsurfaces, link) {
|
||||
struct subsurface_state *new;
|
||||
if (iter == &old_root->root) {
|
||||
new = &root->root;
|
||||
new->commit = NULL;
|
||||
} else {
|
||||
new = calloc(1, sizeof(*new));
|
||||
if (new->commit) {
|
||||
new->commit = wlr_commit_ref(iter->commit);
|
||||
}
|
||||
}
|
||||
|
||||
new->subsurface = iter->subsurface;
|
||||
new->x = iter->x;
|
||||
new->y = iter->y;
|
||||
|
||||
wl_list_insert(root->subsurfaces.prev, &new->link);
|
||||
}
|
||||
} else {
|
||||
wl_list_insert(&root->subsurfaces, &root->root.link);
|
||||
root->root.subsurface = NULL;
|
||||
root->root.commit = NULL;
|
||||
root->root.x = 0;
|
||||
root->root.y = 0;
|
||||
}
|
||||
|
||||
wlr_commit_set(new, sc->root_id, root);
|
||||
}
|
||||
|
||||
static void subcompositor_display_destroy(struct wl_listener *listener, void *data) {
|
||||
struct wlr_subcompositor *sc =
|
||||
wl_container_of(listener, sc, display_destroy);
|
||||
|
||||
wl_global_destroy(sc->global);
|
||||
free(sc);
|
||||
}
|
||||
|
||||
struct wlr_subcompositor *wlr_subcompositor_create(struct wlr_compositor *compositor) {
|
||||
struct wlr_subcompositor *sc = calloc(1, sizeof(*sc));
|
||||
if (!sc) {
|
||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sc->global = wl_global_create(compositor->display, &wl_subcompositor_interface,
|
||||
SUBCOMPOSITOR_VERSION, sc, subcompositor_bind);
|
||||
if (!sc->global) {
|
||||
wlr_log_errno(WLR_ERROR, "Failed to create wayland global");
|
||||
free(sc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sc->compositor = compositor;
|
||||
sc->root_id = wlr_compositor_register(compositor);
|
||||
|
||||
wl_signal_init(&sc->events.new_subsurface);
|
||||
|
||||
sc->new_state.notify = subcompositor_new_state;
|
||||
wl_signal_add(&compositor->events.new_state, &sc->new_state);
|
||||
|
||||
sc->display_destroy.notify = subcompositor_display_destroy;
|
||||
wl_display_add_destroy_listener(compositor->display, &sc->display_destroy);
|
||||
|
||||
return sc;
|
||||
}
|
||||
|
||||
struct wlr_subsurface *wlr_surface_to_subsurface(struct wlr_surface_2 *surf) {
|
||||
if (surf->role_name && strcmp(surf->role_name, "wl_subsurface") == 0) {
|
||||
return surf->role_data;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct wlr_surface_2 *wlr_surface_get_root_surface(struct wlr_surface_2 *surf) {
|
||||
struct wlr_subsurface *ss;
|
||||
|
||||
while (surf && (ss = wlr_surface_to_subsurface(surf))) {
|
||||
surf = ss->parent;
|
||||
}
|
||||
|
||||
return surf;
|
||||
}
|
||||
|
||||
static void commit_for_each(struct wlr_subcompositor *sc, struct wlr_commit *commit,
|
||||
wlr_subsurface_iter_t func, void *userdata, int32_t x, int32_t y) {
|
||||
assert(wlr_commit_is_complete(commit));
|
||||
|
||||
struct subsurface_root *root = wlr_commit_get(commit, sc->root_id);
|
||||
struct subsurface_state *iter;
|
||||
wl_list_for_each(iter, &root->subsurfaces, link) {
|
||||
int32_t new_x = iter->x + x;
|
||||
int32_t new_y = iter->y + y;
|
||||
|
||||
if (iter == &root->root) {
|
||||
func(userdata, commit, new_x, new_y);
|
||||
} else if (iter->synchronized && iter->commit) {
|
||||
commit_for_each(sc, iter->commit, func, userdata, new_x, new_y);
|
||||
} else if (!iter->synchronized) {
|
||||
struct wlr_surface_2 *s = iter->subsurface->surface;
|
||||
struct wlr_commit *c = wlr_surface_get_commit(s);
|
||||
if (c) {
|
||||
commit_for_each(sc, c, func, userdata, new_x, new_y);
|
||||
wlr_commit_unref(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wlr_commit_for_each_subsurface(struct wlr_subcompositor *sc,
|
||||
struct wlr_commit *commit, wlr_subsurface_iter_t func, void *userdata) {
|
||||
commit_for_each(sc, commit, func, userdata, 0, 0);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue