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
|
|
@ -16,18 +16,13 @@ struct wlr_commit;
|
||||||
struct wlr_output;
|
struct wlr_output;
|
||||||
struct wlr_surface;
|
struct wlr_surface;
|
||||||
|
|
||||||
struct wlr_subcompositor {
|
|
||||||
struct wl_global *global;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct wlr_compositor {
|
struct wlr_compositor {
|
||||||
|
struct wl_display *display;
|
||||||
struct wl_global *global;
|
struct wl_global *global;
|
||||||
struct wlr_renderer *renderer;
|
struct wlr_renderer *renderer;
|
||||||
|
|
||||||
uint32_t ids;
|
uint32_t ids;
|
||||||
|
|
||||||
struct wlr_subcompositor subcompositor;
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
struct wl_signal new_surface;
|
struct wl_signal new_surface;
|
||||||
struct wl_signal new_surface_2;
|
struct wl_signal new_surface_2;
|
||||||
|
|
@ -41,17 +36,63 @@ struct wlr_surface_2 {
|
||||||
struct wl_resource *resource;
|
struct wl_resource *resource;
|
||||||
struct wlr_compositor *compositor;
|
struct wlr_compositor *compositor;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This should contain the name of the protocol to prevent conflicts.
|
||||||
|
* e.g. "xdg_toplevel".
|
||||||
|
* Also, it should point to statically allocated memory.
|
||||||
|
*/
|
||||||
|
const char *role_name;
|
||||||
|
void *role_data;
|
||||||
|
|
||||||
|
struct wl_list committed;
|
||||||
|
struct wl_list frame_callbacks;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
struct wl_signal commit;
|
struct wl_signal commit;
|
||||||
struct wl_signal destroy;
|
struct wl_signal destroy;
|
||||||
} events;
|
} events;
|
||||||
|
|
||||||
struct wlr_commit *pending;
|
struct wlr_commit *pending;
|
||||||
struct wl_list committed;
|
|
||||||
|
|
||||||
struct wl_list frame_callbacks;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* wlr_commit
|
||||||
|
*
|
||||||
|
* A wlr_commit is a description for a "coherent presentation" of a surface's
|
||||||
|
* contents, or could also be thought of as a "snaphot" of the surface at
|
||||||
|
* commit time. It should contain all of the information to correctly display
|
||||||
|
* a surface.
|
||||||
|
*
|
||||||
|
* A wlr_commit can be extended to add extra state for each commit to be
|
||||||
|
* managed properly by the commit queue. Every wayland protocol that has state
|
||||||
|
* synchronized by wl_surface.commit should use this extension mechanism.
|
||||||
|
*
|
||||||
|
* A commit can exist in 3 states:
|
||||||
|
* - Pending: The client has not commited the contents yet.
|
||||||
|
* - Committed: The client has commited the contents, but something is
|
||||||
|
* inhibiting the presentation of this surface.
|
||||||
|
* - Complete: The commit is ready to be presented.
|
||||||
|
*
|
||||||
|
* For a surface, there will always be exactly 1 pending commit for a surface.
|
||||||
|
* see wlr_surface_get_pending
|
||||||
|
*
|
||||||
|
* Excluding the time when a before the first complete commit is ready, there
|
||||||
|
* will always be at least 1 complete commit for a surface. The latest complete
|
||||||
|
* commit is called the current commit.
|
||||||
|
* see wlr_surface_get_commit
|
||||||
|
*
|
||||||
|
* wlr_commits are reference counted. If you wish to save the contents of a
|
||||||
|
* commit for future use, simply do not unreference the commit. A referenced
|
||||||
|
* wlr_commit will even persist after the surface is destroyed, which can be
|
||||||
|
* useful for fade-out animations. Any extensions to wlr_surface should be
|
||||||
|
* prepared for the associated wlr_surface to not be available.
|
||||||
|
*
|
||||||
|
* Upon commit destruction, several actions may be performed on objects which
|
||||||
|
* will affect clients. For example, wl_buffer.release may be called for the
|
||||||
|
* attached buffer. Holding onto too many commits for too long may cause
|
||||||
|
* resource starvation in clients, so care should be taken to unreference
|
||||||
|
* commits as soon as they're no longer needed.
|
||||||
|
*/
|
||||||
struct wlr_commit {
|
struct wlr_commit {
|
||||||
struct wl_list link;
|
struct wl_list link;
|
||||||
struct wlr_surface_2 *surface;
|
struct wlr_surface_2 *surface;
|
||||||
|
|
@ -103,15 +144,23 @@ struct wlr_surface_2 *wlr_surface_from_resource_2(struct wl_resource *resource);
|
||||||
|
|
||||||
struct wlr_commit *wlr_surface_get_commit(struct wlr_surface_2 *surface);
|
struct wlr_commit *wlr_surface_get_commit(struct wlr_surface_2 *surface);
|
||||||
struct wlr_commit *wlr_surface_get_pending(struct wlr_surface_2 *surface);
|
struct wlr_commit *wlr_surface_get_pending(struct wlr_surface_2 *surface);
|
||||||
|
struct wlr_commit *wlr_surface_get_latest(struct wlr_surface_2 *surface);
|
||||||
|
|
||||||
void wlr_surface_send_enter_2(struct wlr_surface_2 *surf, struct wlr_output *output);
|
void wlr_surface_send_enter_2(struct wlr_surface_2 *surf, struct wlr_output *output);
|
||||||
void wlr_surface_send_leave_2(struct wlr_surface_2 *surf, struct wlr_output *output);
|
void wlr_surface_send_leave_2(struct wlr_surface_2 *surf, struct wlr_output *output);
|
||||||
|
|
||||||
|
struct wlr_commit *wlr_commit_ref(struct wlr_commit *commit);
|
||||||
void wlr_commit_unref(struct wlr_commit *commit);
|
void wlr_commit_unref(struct wlr_commit *commit);
|
||||||
|
|
||||||
|
bool wlr_commit_is_complete(struct wlr_commit *c);
|
||||||
|
|
||||||
void wlr_commit_inhibit(struct wlr_commit *commit);
|
void wlr_commit_inhibit(struct wlr_commit *commit);
|
||||||
void wlr_commit_uninhibit(struct wlr_commit *commit);
|
void wlr_commit_uninhibit(struct wlr_commit *commit);
|
||||||
|
|
||||||
|
bool wlr_surface_set_role_2(struct wlr_surface_2 *surf, const char *name);
|
||||||
|
void wlr_commit_set(struct wlr_commit *commit, uint32_t id, void *data);
|
||||||
|
void *wlr_commit_get(struct wlr_commit *commit, uint32_t id);
|
||||||
|
|
||||||
bool wlr_surface_is_subsurface(struct wlr_surface *surface);
|
bool wlr_surface_is_subsurface(struct wlr_surface *surface);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
52
include/wlr/types/wlr_subcompositor.h
Normal file
52
include/wlr/types/wlr_subcompositor.h
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
#ifndef WLR_TYPES_WLR_SUBCOMPOSITOR_H
|
||||||
|
#define WLR_TYPES_WLR_SUBCOMPOSITOR_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <wayland-server.h>
|
||||||
|
|
||||||
|
struct wlr_compositor;
|
||||||
|
struct wlr_commit;
|
||||||
|
struct wlr_surface_2;
|
||||||
|
|
||||||
|
struct wlr_subcompositor {
|
||||||
|
struct wl_global *global;
|
||||||
|
struct wlr_compositor *compositor;
|
||||||
|
|
||||||
|
uint32_t root_id;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
struct wl_signal new_subsurface;
|
||||||
|
} events;
|
||||||
|
|
||||||
|
struct wl_listener new_state;
|
||||||
|
struct wl_listener display_destroy;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wlr_subsurface {
|
||||||
|
struct wl_resource *resource;
|
||||||
|
struct wlr_surface_2 *surface;
|
||||||
|
struct wlr_surface_2 *parent;
|
||||||
|
|
||||||
|
bool synchronized;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
struct wl_signal destroy;
|
||||||
|
} events;
|
||||||
|
|
||||||
|
struct wl_listener surface_destroy;
|
||||||
|
struct wl_listener parent_destroy;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wlr_subcompositor *wlr_subcompositor_create(struct wlr_compositor *compositor);
|
||||||
|
|
||||||
|
struct wlr_subsurface *wlr_surface_to_subsurface(struct wlr_surface_2 *surf);
|
||||||
|
struct wlr_surface_2 *wlr_surface_get_root_surface(struct wlr_surface_2 *surf);
|
||||||
|
|
||||||
|
typedef void (*wlr_subsurface_iter_t)(void *userdata,
|
||||||
|
struct wlr_commit *commit, int32_t x, int32_t y);
|
||||||
|
|
||||||
|
void wlr_commit_for_each_subsurface(struct wlr_subcompositor *sc,
|
||||||
|
struct wlr_commit *commit, wlr_subsurface_iter_t func, void *userdata);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -9,97 +9,6 @@
|
||||||
#include "util/signal.h"
|
#include "util/signal.h"
|
||||||
|
|
||||||
#define COMPOSITOR_VERSION 4
|
#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;
|
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
|
// Prevent a zero allocation
|
||||||
size_t len = comp->ids ? comp->ids : 1;
|
c->state_len = comp->ids ? comp->ids : 1;
|
||||||
|
c->state = calloc(c->state_len, sizeof(*c->state));
|
||||||
c->state = calloc(len, sizeof(*c->state));
|
|
||||||
if (!c->state) {
|
if (!c->state) {
|
||||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||||
free(c);
|
free(c);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
c->state_len = len;
|
|
||||||
|
|
||||||
c->surface = surf;
|
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);
|
pixman_region32_init(&c->surface_damage);
|
||||||
wl_list_init(&c->frame_callbacks);
|
wl_list_init(&c->frame_callbacks);
|
||||||
|
|
@ -188,7 +98,7 @@ static void commit_destroy(struct wlr_commit *c) {
|
||||||
free(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;
|
return c->committed && c->inhibit == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -202,14 +112,13 @@ static bool commit_is_latest(struct wlr_commit *c) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (commit_is_complete(iter)) {
|
if (wlr_commit_is_complete(iter)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// You shouldn't be able to get here.
|
// You shouldn't be able to get here.
|
||||||
assert(0);
|
abort();
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void surface_prune_commits(struct wlr_surface_2 *surf) {
|
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;
|
struct wlr_commit *iter, *tmp;
|
||||||
wl_list_for_each_safe(iter, tmp, &surf->committed, link) {
|
wl_list_for_each_safe(iter, tmp, &surf->committed, link) {
|
||||||
if (!complete) {
|
if (!complete) {
|
||||||
complete = commit_is_complete(iter);
|
complete = wlr_commit_is_complete(iter);
|
||||||
} else if (iter->ref_cnt == 0) {
|
} else if (iter->ref_cnt == 0) {
|
||||||
|
wl_list_remove(&iter->link);
|
||||||
commit_destroy(iter);
|
commit_destroy(iter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void wlr_commit_inhibit(struct wlr_commit *commit) {
|
void wlr_commit_inhibit(struct wlr_commit *commit) {
|
||||||
assert(commit && !commit_is_complete(commit));
|
assert(commit && !wlr_commit_is_complete(commit));
|
||||||
++commit->inhibit;
|
++commit->inhibit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -233,7 +143,7 @@ void wlr_commit_uninhibit(struct wlr_commit *commit) {
|
||||||
assert(commit && commit->inhibit > 0);
|
assert(commit && commit->inhibit > 0);
|
||||||
--commit->inhibit;
|
--commit->inhibit;
|
||||||
|
|
||||||
if (commit_is_complete(commit)) {
|
if (wlr_commit_is_complete(commit)) {
|
||||||
wlr_signal_emit_safe(&commit->events.complete, commit);
|
wlr_signal_emit_safe(&commit->events.complete, commit);
|
||||||
|
|
||||||
if (commit_is_latest(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);
|
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(&commit->events.complete, commit);
|
||||||
wlr_signal_emit_safe(&surf->events.commit, surf);
|
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) {
|
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));
|
&surface_impl));
|
||||||
return wl_resource_get_user_data(resource);
|
return wl_resource_get_user_data(resource);
|
||||||
}
|
}
|
||||||
|
|
@ -466,9 +376,13 @@ static void surface_resource_destroy(struct wl_resource *resource) {
|
||||||
wlr_signal_emit_safe(&surf->events.destroy, surf);
|
wlr_signal_emit_safe(&surf->events.destroy, surf);
|
||||||
|
|
||||||
struct wlr_commit *iter, *tmp;
|
struct wlr_commit *iter, *tmp;
|
||||||
wl_list_for_each_reverse_safe(iter, tmp, &surf->committed, link) {
|
wl_list_for_each_safe(iter, tmp, &surf->committed, link) {
|
||||||
|
if (iter->ref_cnt == 0) {
|
||||||
commit_destroy(iter);
|
commit_destroy(iter);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
commit_destroy(surf->pending);
|
||||||
|
|
||||||
free(surf);
|
free(surf);
|
||||||
}
|
}
|
||||||
|
|
@ -500,8 +414,11 @@ static void compositor_create_surface(struct wl_client *client,
|
||||||
wl_resource_set_implementation(surf->resource, &surface_impl,
|
wl_resource_set_implementation(surf->resource, &surface_impl,
|
||||||
surf, surface_resource_destroy);
|
surf, surface_resource_destroy);
|
||||||
|
|
||||||
wl_signal_init(&surf->events.destroy);
|
surf->compositor = compositor;
|
||||||
wl_list_init(&surf->committed);
|
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);
|
surf->pending = commit_create(surf);
|
||||||
if (!surf->pending) {
|
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) {
|
static void compositor_display_destroy(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_compositor *comp = wl_container_of(listener, comp, display_destroy);
|
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);
|
wl_global_destroy(comp->global);
|
||||||
|
|
||||||
free(comp);
|
free(comp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -586,6 +500,7 @@ struct wlr_compositor *wlr_compositor_create(struct wl_display *display,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
comp->display = display;
|
||||||
comp->global = wl_global_create(display, &wl_compositor_interface,
|
comp->global = wl_global_create(display, &wl_compositor_interface,
|
||||||
COMPOSITOR_VERSION, comp, compositor_bind);
|
COMPOSITOR_VERSION, comp, compositor_bind);
|
||||||
if (!comp->global) {
|
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_surface_2);
|
||||||
wl_signal_init(&comp->events.new_state);
|
wl_signal_init(&comp->events.new_state);
|
||||||
|
|
||||||
subcompositor_init(&comp->subcompositor, display);
|
|
||||||
|
|
||||||
comp->display_destroy.notify = compositor_display_destroy;
|
comp->display_destroy.notify = compositor_display_destroy;
|
||||||
wl_display_add_destroy_listener(display, &comp->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;
|
struct wlr_commit *iter;
|
||||||
wl_list_for_each(iter, &surf->committed, link) {
|
wl_list_for_each(iter, &surf->committed, link) {
|
||||||
if (commit_is_complete(iter)) {
|
if (wlr_commit_is_complete(iter)) {
|
||||||
++iter->ref_cnt;
|
++iter->ref_cnt;
|
||||||
return iter;
|
return iter;
|
||||||
}
|
}
|
||||||
|
|
@ -630,6 +543,16 @@ struct wlr_commit *wlr_surface_get_pending(struct wlr_surface_2 *surf) {
|
||||||
return surf->pending;
|
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) {
|
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_client *client = wl_resource_get_client(surf->resource);
|
||||||
struct wl_resource *iter;
|
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) {
|
void wlr_commit_unref(struct wlr_commit *commit) {
|
||||||
assert(commit && commit->ref_cnt > 0);
|
assert(commit && commit->ref_cnt > 0);
|
||||||
--commit->ref_cnt;
|
--commit->ref_cnt;
|
||||||
|
|
||||||
surface_prune_commits(commit->surface);
|
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