compositor: introduce sub-surfaces
Implement the basic protocol for sub-surfaces: - expose wl_subcompositor global interface - error checking on protocol calls - associate a parent wl_surface to a sub-surface - introduce the sub-surface role, which is exclusive - an implementation of the wl_subsurface interface - allow nesting of sub-surfaces - proper surface transformation inheritance from parent to sub-surfaces - two different modes of wl_surface.commit for sub-surfaces - hook sub-surfaces up to repaint by modifying the repaint list code Struct weston_subsurface is dynamically allocated. For sub-surfaces, it is completely populated. For parent surfaces, weston_subsurface acts only as a link for stacking order purposes. The wl_resource is unused, parent_destroy_listener is not registered, the transform is not linked, etc. Sub-surfaces are not added directly into layers for display or input. Instead, they are hooked up via the sub-surface list present in parent weston_surface. This way sub-surfaces are inherently linked to the parent surface, and cannot be displayed unless the parent is mapped, too. This also eases restacking, as only the parent will be in a layer list. Also, only the main surface should be subject to shell actions. The surface list rebuilding in weston_output_repaint() is modified to process sub-surface lists, if they are non-empty. The sub-surface list always contains the parent, too, unless empty. The collection of frame_callback_list is moved to a later loop, to streamline the surface list rebuild functions. Features still lacking are: - full-surface alpha support for compound windows Changes in v2: - fix a bug in surface mapping: commit a sub-surface would cause the main surface to never be mapped. - remove debug printfs - detect attempt of making a surface its own parent - always zero-alloc weston_subsurface - apply wl_subsurface.set_position in commit, not immediately - add weston_surface_to_subsurface() - implement sub-surface commit modes parent-cached and independent - implement wl_subcompositor.destroy and wl_subsurface.destroy Changes in v3: - rebased, and use the new transform inheritance code - squashed the commit "add sub-surfaces to repaint list" - fixed a buffer reference leak in commit_from_cache() - Rewrite the sub-surface destructor code, and make it leave the wl_subsurface protocol object inert, if one destroys the corresponding wl_surface. - replaced set_commit_mode with set_sync and set_desync - allowed sub-surface nesting, and fixed repaint accordingly - implemented nested sub-surface commit modes - Made the sub-surface order changes from wl_subsurface.place_above and .place_below to be applied when the parent surface state is applied, instead of immediately. This conforms with the protocol specification now. Signed-off-by: Pekka Paalanen <ppaalanen@gmail.com>
This commit is contained in:
parent
2396aec684
commit
e67858b9cf
736
src/compositor.c
736
src/compositor.c
@ -262,6 +262,9 @@ region_init_infinite(pixman_region32_t *region)
|
||||
UINT32_MAX, UINT32_MAX);
|
||||
}
|
||||
|
||||
static struct weston_subsurface *
|
||||
weston_surface_to_subsurface(struct weston_surface *surface);
|
||||
|
||||
WL_EXPORT struct weston_surface *
|
||||
weston_surface_create(struct weston_compositor *compositor)
|
||||
{
|
||||
@ -314,6 +317,9 @@ weston_surface_create(struct weston_compositor *compositor)
|
||||
region_init_infinite(&surface->pending.input);
|
||||
wl_list_init(&surface->pending.frame_callback_list);
|
||||
|
||||
wl_list_init(&surface->subsurface_list);
|
||||
wl_list_init(&surface->subsurface_list_pending);
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
@ -972,6 +978,8 @@ destroy_surface(struct wl_resource *resource)
|
||||
struct weston_frame_callback *cb, *next;
|
||||
|
||||
assert(wl_list_empty(&surface->geometry.child_list));
|
||||
assert(wl_list_empty(&surface->subsurface_list_pending));
|
||||
assert(wl_list_empty(&surface->subsurface_list));
|
||||
|
||||
if (weston_surface_is_mapped(surface))
|
||||
weston_surface_unmap(surface);
|
||||
@ -1167,31 +1175,58 @@ compositor_accumulate_damage(struct weston_compositor *ec)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
surface_list_add(struct weston_compositor *compositor,
|
||||
struct weston_surface *surface)
|
||||
{
|
||||
struct weston_subsurface *sub;
|
||||
|
||||
if (wl_list_empty(&surface->subsurface_list)) {
|
||||
weston_surface_update_transform(surface);
|
||||
wl_list_insert(compositor->surface_list.prev, &surface->link);
|
||||
return;
|
||||
}
|
||||
|
||||
wl_list_for_each(sub, &surface->subsurface_list, parent_link) {
|
||||
if (!weston_surface_is_mapped(sub->surface))
|
||||
continue;
|
||||
|
||||
if (sub->surface == surface) {
|
||||
weston_surface_update_transform(sub->surface);
|
||||
wl_list_insert(compositor->surface_list.prev,
|
||||
&sub->surface->link);
|
||||
} else {
|
||||
surface_list_add(compositor, sub->surface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
weston_compositor_build_surface_list(struct weston_compositor *compositor)
|
||||
{
|
||||
struct weston_surface *surface;
|
||||
struct weston_layer *layer;
|
||||
|
||||
wl_list_init(&compositor->surface_list);
|
||||
wl_list_for_each(layer, &compositor->layer_list, link) {
|
||||
wl_list_for_each(surface, &layer->surface_list, layer_link) {
|
||||
surface_list_add(compositor, surface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
weston_output_repaint(struct weston_output *output, uint32_t msecs)
|
||||
{
|
||||
struct weston_compositor *ec = output->compositor;
|
||||
struct weston_surface *es;
|
||||
struct weston_layer *layer;
|
||||
struct weston_animation *animation, *next;
|
||||
struct weston_frame_callback *cb, *cnext;
|
||||
struct wl_list frame_callback_list;
|
||||
pixman_region32_t output_damage;
|
||||
|
||||
/* Rebuild the surface list and update surface transforms up front. */
|
||||
wl_list_init(&ec->surface_list);
|
||||
wl_list_init(&frame_callback_list);
|
||||
wl_list_for_each(layer, &ec->layer_list, link) {
|
||||
wl_list_for_each(es, &layer->surface_list, layer_link) {
|
||||
weston_surface_update_transform(es);
|
||||
wl_list_insert(ec->surface_list.prev, &es->link);
|
||||
if (es->output == output) {
|
||||
wl_list_insert_list(&frame_callback_list,
|
||||
&es->frame_callback_list);
|
||||
wl_list_init(&es->frame_callback_list);
|
||||
}
|
||||
}
|
||||
}
|
||||
weston_compositor_build_surface_list(ec);
|
||||
|
||||
if (output->assign_planes && !output->disable_planes)
|
||||
output->assign_planes(output);
|
||||
@ -1199,6 +1234,15 @@ weston_output_repaint(struct weston_output *output, uint32_t msecs)
|
||||
wl_list_for_each(es, &ec->surface_list, link)
|
||||
weston_surface_move_to_plane(es, &ec->primary_plane);
|
||||
|
||||
wl_list_init(&frame_callback_list);
|
||||
wl_list_for_each(es, &ec->surface_list, link) {
|
||||
if (es->output == output) {
|
||||
wl_list_insert_list(&frame_callback_list,
|
||||
&es->frame_callback_list);
|
||||
wl_list_init(&es->frame_callback_list);
|
||||
}
|
||||
}
|
||||
|
||||
compositor_accumulate_damage(ec);
|
||||
|
||||
pixman_region32_init(&output_damage);
|
||||
@ -1425,9 +1469,20 @@ surface_set_input_region(struct wl_client *client,
|
||||
}
|
||||
|
||||
static void
|
||||
surface_commit(struct wl_client *client, struct wl_resource *resource)
|
||||
weston_surface_commit_subsurface_order(struct weston_surface *surface)
|
||||
{
|
||||
struct weston_subsurface *sub;
|
||||
|
||||
wl_list_for_each_reverse(sub, &surface->subsurface_list_pending,
|
||||
parent_link_pending) {
|
||||
wl_list_remove(&sub->parent_link);
|
||||
wl_list_insert(&surface->subsurface_list, &sub->parent_link);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
weston_surface_commit(struct weston_surface *surface)
|
||||
{
|
||||
struct weston_surface *surface = resource->data;
|
||||
pixman_region32_t opaque;
|
||||
int buffer_width = 0;
|
||||
int buffer_height = 0;
|
||||
@ -1492,9 +1547,37 @@ surface_commit(struct wl_client *client, struct wl_resource *resource)
|
||||
&surface->pending.frame_callback_list);
|
||||
wl_list_init(&surface->pending.frame_callback_list);
|
||||
|
||||
weston_surface_commit_subsurface_order(surface);
|
||||
|
||||
weston_surface_schedule_repaint(surface);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_subsurface_commit(struct weston_subsurface *sub);
|
||||
|
||||
static void
|
||||
weston_subsurface_parent_commit(struct weston_subsurface *sub,
|
||||
int parent_is_synchronized);
|
||||
|
||||
static void
|
||||
surface_commit(struct wl_client *client, struct wl_resource *resource)
|
||||
{
|
||||
struct weston_surface *surface = resource->data;
|
||||
struct weston_subsurface *sub = weston_surface_to_subsurface(surface);
|
||||
|
||||
if (sub) {
|
||||
weston_subsurface_commit(sub);
|
||||
return;
|
||||
}
|
||||
|
||||
weston_surface_commit(surface);
|
||||
|
||||
wl_list_for_each(sub, &surface->subsurface_list, parent_link) {
|
||||
if (sub->surface != surface)
|
||||
weston_subsurface_parent_commit(sub, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
surface_set_buffer_transform(struct wl_client *client,
|
||||
struct wl_resource *resource, int transform)
|
||||
@ -1613,6 +1696,623 @@ static const struct wl_compositor_interface compositor_interface = {
|
||||
compositor_create_region
|
||||
};
|
||||
|
||||
static void
|
||||
weston_subsurface_commit_from_cache(struct weston_subsurface *sub)
|
||||
{
|
||||
struct weston_surface *surface = sub->surface;
|
||||
pixman_region32_t opaque;
|
||||
int buffer_width = 0;
|
||||
int buffer_height = 0;
|
||||
|
||||
/* wl_surface.set_buffer_rotation */
|
||||
surface->buffer_transform = sub->cached.buffer_transform;
|
||||
|
||||
/* wl_surface.attach */
|
||||
if (sub->cached.buffer_ref.buffer || sub->cached.newly_attached)
|
||||
weston_surface_attach(surface, sub->cached.buffer_ref.buffer);
|
||||
weston_buffer_reference(&sub->cached.buffer_ref, NULL);
|
||||
|
||||
if (surface->buffer_ref.buffer) {
|
||||
buffer_width = weston_surface_buffer_width(surface);
|
||||
buffer_height = weston_surface_buffer_height(surface);
|
||||
}
|
||||
|
||||
if (surface->configure && sub->cached.newly_attached)
|
||||
surface->configure(surface, sub->cached.sx, sub->cached.sy,
|
||||
buffer_width, buffer_height);
|
||||
sub->cached.sx = 0;
|
||||
sub->cached.sy = 0;
|
||||
sub->cached.newly_attached = 0;
|
||||
|
||||
/* wl_surface.damage */
|
||||
pixman_region32_union(&surface->damage, &surface->damage,
|
||||
&sub->cached.damage);
|
||||
pixman_region32_intersect_rect(&surface->damage, &surface->damage,
|
||||
0, 0,
|
||||
surface->geometry.width,
|
||||
surface->geometry.height);
|
||||
empty_region(&sub->cached.damage);
|
||||
|
||||
/* wl_surface.set_opaque_region */
|
||||
pixman_region32_init_rect(&opaque, 0, 0,
|
||||
surface->geometry.width,
|
||||
surface->geometry.height);
|
||||
pixman_region32_intersect(&opaque,
|
||||
&opaque, &sub->cached.opaque);
|
||||
|
||||
if (!pixman_region32_equal(&opaque, &surface->opaque)) {
|
||||
pixman_region32_copy(&surface->opaque, &opaque);
|
||||
weston_surface_geometry_dirty(surface);
|
||||
}
|
||||
|
||||
pixman_region32_fini(&opaque);
|
||||
|
||||
/* wl_surface.set_input_region */
|
||||
pixman_region32_fini(&surface->input);
|
||||
pixman_region32_init_rect(&surface->input, 0, 0,
|
||||
surface->geometry.width,
|
||||
surface->geometry.height);
|
||||
pixman_region32_intersect(&surface->input,
|
||||
&surface->input, &sub->cached.input);
|
||||
|
||||
/* wl_surface.frame */
|
||||
wl_list_insert_list(&surface->frame_callback_list,
|
||||
&sub->cached.frame_callback_list);
|
||||
wl_list_init(&sub->cached.frame_callback_list);
|
||||
|
||||
weston_surface_commit_subsurface_order(surface);
|
||||
|
||||
weston_surface_schedule_repaint(surface);
|
||||
|
||||
sub->cached.has_data = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
weston_subsurface_commit_to_cache(struct weston_subsurface *sub)
|
||||
{
|
||||
struct weston_surface *surface = sub->surface;
|
||||
|
||||
/*
|
||||
* If this commit would cause the surface to move by the
|
||||
* attach(dx, dy) parameters, the old damage region must be
|
||||
* translated to correspond to the new surface coordinate system
|
||||
* origin.
|
||||
*/
|
||||
pixman_region32_translate(&sub->cached.damage,
|
||||
-surface->pending.sx, -surface->pending.sy);
|
||||
pixman_region32_union(&sub->cached.damage, &sub->cached.damage,
|
||||
&surface->pending.damage);
|
||||
empty_region(&surface->pending.damage);
|
||||
|
||||
if (surface->pending.newly_attached) {
|
||||
sub->cached.newly_attached = 1;
|
||||
weston_buffer_reference(&sub->cached.buffer_ref,
|
||||
surface->pending.buffer);
|
||||
}
|
||||
sub->cached.sx += surface->pending.sx;
|
||||
sub->cached.sy += surface->pending.sy;
|
||||
surface->pending.sx = 0;
|
||||
surface->pending.sy = 0;
|
||||
surface->pending.newly_attached = 0;
|
||||
|
||||
sub->cached.buffer_transform = surface->pending.buffer_transform;
|
||||
|
||||
pixman_region32_copy(&sub->cached.opaque, &surface->pending.opaque);
|
||||
|
||||
pixman_region32_copy(&sub->cached.input, &surface->pending.input);
|
||||
|
||||
wl_list_insert_list(&sub->cached.frame_callback_list,
|
||||
&surface->pending.frame_callback_list);
|
||||
wl_list_init(&surface->pending.frame_callback_list);
|
||||
|
||||
sub->cached.has_data = 1;
|
||||
}
|
||||
|
||||
static int
|
||||
weston_subsurface_is_synchronized(struct weston_subsurface *sub)
|
||||
{
|
||||
while (sub) {
|
||||
if (sub->synchronized)
|
||||
return 1;
|
||||
|
||||
if (!sub->parent)
|
||||
return 0;
|
||||
|
||||
sub = weston_surface_to_subsurface(sub->parent);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
weston_subsurface_commit(struct weston_subsurface *sub)
|
||||
{
|
||||
struct weston_surface *surface = sub->surface;
|
||||
struct weston_subsurface *tmp;
|
||||
|
||||
/* Recursive check for effectively synchronized. */
|
||||
if (weston_subsurface_is_synchronized(sub)) {
|
||||
weston_subsurface_commit_to_cache(sub);
|
||||
} else {
|
||||
if (sub->cached.has_data) {
|
||||
/* flush accumulated state from cache */
|
||||
weston_subsurface_commit_to_cache(sub);
|
||||
weston_subsurface_commit_from_cache(sub);
|
||||
} else {
|
||||
weston_surface_commit(surface);
|
||||
}
|
||||
|
||||
wl_list_for_each(tmp, &surface->subsurface_list, parent_link) {
|
||||
if (tmp->surface != surface)
|
||||
weston_subsurface_parent_commit(tmp, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
weston_subsurface_parent_commit(struct weston_subsurface *sub,
|
||||
int parent_is_synchronized)
|
||||
{
|
||||
struct weston_surface *surface = sub->surface;
|
||||
struct weston_subsurface *tmp;
|
||||
|
||||
if (sub->position.set) {
|
||||
weston_surface_set_position(sub->surface,
|
||||
sub->position.x, sub->position.y);
|
||||
sub->position.set = 0;
|
||||
}
|
||||
|
||||
if (!parent_is_synchronized && !sub->synchronized)
|
||||
return;
|
||||
|
||||
/* From now on, commit_from_cache the whole sub-tree, regardless of
|
||||
* the synchronized mode of each child. This sub-surface or some
|
||||
* of its ancestors were synchronized, so we are synchronized
|
||||
* all the way down.
|
||||
*/
|
||||
|
||||
if (sub->cached.has_data)
|
||||
weston_subsurface_commit_from_cache(sub);
|
||||
|
||||
wl_list_for_each(tmp, &surface->subsurface_list, parent_link) {
|
||||
if (tmp->surface != surface)
|
||||
weston_subsurface_parent_commit(tmp, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
subsurface_configure(struct weston_surface *surface, int32_t dx, int32_t dy,
|
||||
int32_t width, int32_t height)
|
||||
{
|
||||
struct weston_compositor *compositor = surface->compositor;
|
||||
|
||||
weston_surface_configure(surface,
|
||||
surface->geometry.x + dx,
|
||||
surface->geometry.y + dy,
|
||||
width, height);
|
||||
|
||||
/* No need to check parent mappedness, because if parent is not
|
||||
* mapped, parent is not in a visible layer, so this sub-surface
|
||||
* will not be drawn either.
|
||||
*/
|
||||
if (!weston_surface_is_mapped(surface)) {
|
||||
wl_list_init(&surface->layer_link);
|
||||
|
||||
/* Cannot call weston_surface_update_transform(),
|
||||
* because that would call it also for the parent surface,
|
||||
* which might not be mapped yet. That would lead to
|
||||
* inconsistent state, where the window could never be
|
||||
* mapped.
|
||||
*
|
||||
* Instead just assing any output, to make
|
||||
* weston_surface_is_mapped() return true, so that when the
|
||||
* parent surface does get mapped, this one will get
|
||||
* included, too. See surface_list_add().
|
||||
*/
|
||||
assert(!wl_list_empty(&compositor->output_list));
|
||||
surface->output = container_of(compositor->output_list.next,
|
||||
struct weston_output, link);
|
||||
}
|
||||
}
|
||||
|
||||
static struct weston_subsurface *
|
||||
weston_surface_to_subsurface(struct weston_surface *surface)
|
||||
{
|
||||
if (surface->configure == subsurface_configure)
|
||||
return surface->configure_private;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
subsurface_set_position(struct wl_client *client,
|
||||
struct wl_resource *resource, int32_t x, int32_t y)
|
||||
{
|
||||
struct weston_subsurface *sub = resource->data;
|
||||
|
||||
if (!sub)
|
||||
return;
|
||||
|
||||
sub->position.x = x;
|
||||
sub->position.y = y;
|
||||
sub->position.set = 1;
|
||||
}
|
||||
|
||||
static struct weston_subsurface *
|
||||
subsurface_from_surface(struct weston_surface *surface)
|
||||
{
|
||||
struct weston_subsurface *sub;
|
||||
|
||||
sub = weston_surface_to_subsurface(surface);
|
||||
if (sub)
|
||||
return sub;
|
||||
|
||||
wl_list_for_each(sub, &surface->subsurface_list, parent_link)
|
||||
if (sub->surface == surface)
|
||||
return sub;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct weston_subsurface *
|
||||
subsurface_sibling_check(struct weston_subsurface *sub,
|
||||
struct weston_surface *surface,
|
||||
const char *request)
|
||||
{
|
||||
struct weston_subsurface *sibling;
|
||||
|
||||
sibling = subsurface_from_surface(surface);
|
||||
|
||||
if (!sibling) {
|
||||
wl_resource_post_error(sub->resource,
|
||||
WL_SUBSURFACE_ERROR_BAD_SURFACE,
|
||||
"%s: wl_surface@%d is not a parent or sibling",
|
||||
request, surface->resource.object.id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (sibling->parent != sub->parent) {
|
||||
wl_resource_post_error(sub->resource,
|
||||
WL_SUBSURFACE_ERROR_BAD_SURFACE,
|
||||
"%s: wl_surface@%d has a different parent",
|
||||
request, surface->resource.object.id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return sibling;
|
||||
}
|
||||
|
||||
static void
|
||||
subsurface_place_above(struct wl_client *client,
|
||||
struct wl_resource *resource,
|
||||
struct wl_resource *sibling_resource)
|
||||
{
|
||||
struct weston_subsurface *sub = resource->data;
|
||||
struct weston_surface *surface = sibling_resource->data;
|
||||
struct weston_subsurface *sibling;
|
||||
|
||||
if (!sub)
|
||||
return;
|
||||
|
||||
sibling = subsurface_sibling_check(sub, surface, "place_above");
|
||||
if (!sibling)
|
||||
return;
|
||||
|
||||
wl_list_remove(&sub->parent_link_pending);
|
||||
wl_list_insert(sibling->parent_link_pending.prev,
|
||||
&sub->parent_link_pending);
|
||||
}
|
||||
|
||||
static void
|
||||
subsurface_place_below(struct wl_client *client,
|
||||
struct wl_resource *resource,
|
||||
struct wl_resource *sibling_resource)
|
||||
{
|
||||
struct weston_subsurface *sub = resource->data;
|
||||
struct weston_surface *surface = sibling_resource->data;
|
||||
struct weston_subsurface *sibling;
|
||||
|
||||
if (!sub)
|
||||
return;
|
||||
|
||||
sibling = subsurface_sibling_check(sub, surface, "place_below");
|
||||
if (!sibling)
|
||||
return;
|
||||
|
||||
wl_list_remove(&sub->parent_link_pending);
|
||||
wl_list_insert(&sibling->parent_link_pending,
|
||||
&sub->parent_link_pending);
|
||||
}
|
||||
|
||||
static void
|
||||
subsurface_set_sync(struct wl_client *client, struct wl_resource *resource)
|
||||
{
|
||||
struct weston_subsurface *sub = resource->data;
|
||||
|
||||
if (sub)
|
||||
sub->synchronized = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
subsurface_set_desync(struct wl_client *client, struct wl_resource *resource)
|
||||
{
|
||||
struct weston_subsurface *sub = resource->data;
|
||||
|
||||
if (sub)
|
||||
sub->synchronized = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
weston_subsurface_cache_init(struct weston_subsurface *sub)
|
||||
{
|
||||
pixman_region32_init(&sub->cached.damage);
|
||||
pixman_region32_init(&sub->cached.opaque);
|
||||
pixman_region32_init(&sub->cached.input);
|
||||
wl_list_init(&sub->cached.frame_callback_list);
|
||||
sub->cached.buffer_ref.buffer = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
weston_subsurface_cache_fini(struct weston_subsurface *sub)
|
||||
{
|
||||
struct weston_frame_callback *cb, *tmp;
|
||||
|
||||
wl_list_for_each_safe(cb, tmp, &sub->cached.frame_callback_list, link)
|
||||
wl_resource_destroy(&cb->resource);
|
||||
|
||||
weston_buffer_reference(&sub->cached.buffer_ref, NULL);
|
||||
pixman_region32_fini(&sub->cached.damage);
|
||||
pixman_region32_fini(&sub->cached.opaque);
|
||||
pixman_region32_fini(&sub->cached.input);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_subsurface_unlink_parent(struct weston_subsurface *sub)
|
||||
{
|
||||
wl_list_remove(&sub->parent_link);
|
||||
wl_list_remove(&sub->parent_link_pending);
|
||||
wl_list_remove(&sub->parent_destroy_listener.link);
|
||||
sub->parent = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
weston_subsurface_destroy(struct weston_subsurface *sub);
|
||||
|
||||
static void
|
||||
subsurface_handle_surface_destroy(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct weston_subsurface *sub =
|
||||
container_of(listener, struct weston_subsurface,
|
||||
surface_destroy_listener);
|
||||
assert(data == &sub->surface->resource);
|
||||
|
||||
/* The protocol object (wl_resource) is left inert. */
|
||||
if (sub->resource)
|
||||
sub->resource->data = NULL;
|
||||
|
||||
weston_subsurface_destroy(sub);
|
||||
}
|
||||
|
||||
static void
|
||||
subsurface_handle_parent_destroy(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct weston_subsurface *sub =
|
||||
container_of(listener, struct weston_subsurface,
|
||||
parent_destroy_listener);
|
||||
assert(data == &sub->parent->resource);
|
||||
assert(sub->surface != sub->parent);
|
||||
|
||||
if (weston_surface_is_mapped(sub->surface))
|
||||
weston_surface_unmap(sub->surface);
|
||||
|
||||
weston_subsurface_unlink_parent(sub);
|
||||
}
|
||||
|
||||
static void
|
||||
subsurface_resource_destroy(struct wl_resource *resource)
|
||||
{
|
||||
struct weston_subsurface *sub = resource->data;
|
||||
|
||||
if (sub)
|
||||
weston_subsurface_destroy(sub);
|
||||
|
||||
free(resource);
|
||||
}
|
||||
|
||||
static void
|
||||
subsurface_destroy(struct wl_client *client, struct wl_resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_subsurface_link_parent(struct weston_subsurface *sub,
|
||||
struct weston_surface *parent)
|
||||
{
|
||||
sub->parent = parent;
|
||||
sub->parent_destroy_listener.notify = subsurface_handle_parent_destroy;
|
||||
wl_signal_add(&parent->resource.destroy_signal,
|
||||
&sub->parent_destroy_listener);
|
||||
|
||||
wl_list_insert(&parent->subsurface_list, &sub->parent_link);
|
||||
wl_list_insert(&parent->subsurface_list_pending,
|
||||
&sub->parent_link_pending);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_subsurface_link_surface(struct weston_subsurface *sub,
|
||||
struct weston_surface *surface)
|
||||
{
|
||||
sub->surface = surface;
|
||||
sub->surface_destroy_listener.notify =
|
||||
subsurface_handle_surface_destroy;
|
||||
wl_signal_add(&surface->resource.destroy_signal,
|
||||
&sub->surface_destroy_listener);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_subsurface_destroy(struct weston_subsurface *sub)
|
||||
{
|
||||
assert(sub->surface);
|
||||
|
||||
if (sub->resource) {
|
||||
assert(weston_surface_to_subsurface(sub->surface) == sub);
|
||||
assert(sub->parent_destroy_listener.notify ==
|
||||
subsurface_handle_parent_destroy);
|
||||
|
||||
weston_surface_set_transform_parent(sub->surface, NULL);
|
||||
if (sub->parent)
|
||||
weston_subsurface_unlink_parent(sub);
|
||||
|
||||
weston_subsurface_cache_fini(sub);
|
||||
|
||||
sub->surface->configure = NULL;
|
||||
sub->surface->configure_private = NULL;
|
||||
} else {
|
||||
/* the dummy weston_subsurface for the parent itself */
|
||||
assert(sub->parent_destroy_listener.notify == NULL);
|
||||
wl_list_remove(&sub->parent_link);
|
||||
wl_list_remove(&sub->parent_link_pending);
|
||||
}
|
||||
|
||||
wl_list_remove(&sub->surface_destroy_listener.link);
|
||||
free(sub);
|
||||
}
|
||||
|
||||
static const struct wl_subsurface_interface subsurface_implementation = {
|
||||
subsurface_destroy,
|
||||
subsurface_set_position,
|
||||
subsurface_place_above,
|
||||
subsurface_place_below,
|
||||
subsurface_set_sync,
|
||||
subsurface_set_desync
|
||||
};
|
||||
|
||||
static struct weston_subsurface *
|
||||
weston_subsurface_create(uint32_t id, struct weston_surface *surface,
|
||||
struct weston_surface *parent)
|
||||
{
|
||||
struct weston_subsurface *sub;
|
||||
|
||||
sub = calloc(1, sizeof *sub);
|
||||
if (!sub)
|
||||
return NULL;
|
||||
|
||||
sub->resource = wl_client_add_object(surface->resource.client,
|
||||
&wl_subsurface_interface,
|
||||
&subsurface_implementation,
|
||||
id, sub);
|
||||
if (!sub->resource) {
|
||||
free(sub);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sub->resource->destroy = subsurface_resource_destroy;
|
||||
weston_subsurface_link_surface(sub, surface);
|
||||
weston_subsurface_link_parent(sub, parent);
|
||||
weston_subsurface_cache_init(sub);
|
||||
sub->synchronized = 1;
|
||||
weston_surface_set_transform_parent(surface, parent);
|
||||
|
||||
return sub;
|
||||
}
|
||||
|
||||
/* Create a dummy subsurface for having the parent itself in its
|
||||
* sub-surface lists. Makes stacking order manipulation easy.
|
||||
*/
|
||||
static struct weston_subsurface *
|
||||
weston_subsurface_create_for_parent(struct weston_surface *parent)
|
||||
{
|
||||
struct weston_subsurface *sub;
|
||||
|
||||
sub = calloc(1, sizeof *sub);
|
||||
if (!sub)
|
||||
return NULL;
|
||||
|
||||
weston_subsurface_link_surface(sub, parent);
|
||||
sub->parent = parent;
|
||||
wl_list_insert(&parent->subsurface_list, &sub->parent_link);
|
||||
wl_list_insert(&parent->subsurface_list_pending,
|
||||
&sub->parent_link_pending);
|
||||
|
||||
return sub;
|
||||
}
|
||||
|
||||
static void
|
||||
subcompositor_get_subsurface(struct wl_client *client,
|
||||
struct wl_resource *resource,
|
||||
uint32_t id,
|
||||
struct wl_resource *surface_resource,
|
||||
struct wl_resource *parent_resource)
|
||||
{
|
||||
struct weston_surface *surface = surface_resource->data;
|
||||
struct weston_surface *parent = parent_resource->data;
|
||||
struct weston_subsurface *sub;
|
||||
static const char where[] = "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",
|
||||
where, id, surface_resource->object.id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (weston_surface_to_subsurface(surface)) {
|
||||
wl_resource_post_error(resource,
|
||||
WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE,
|
||||
"%s%d: wl_surface@%d is already a sub-surface",
|
||||
where, id, surface_resource->object.id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (surface->configure) {
|
||||
wl_resource_post_error(resource,
|
||||
WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE,
|
||||
"%s%d: wl_surface@%d already has a role",
|
||||
where, id, surface_resource->object.id);
|
||||
return;
|
||||
}
|
||||
|
||||
/* make sure the parent is in its own list */
|
||||
if (wl_list_empty(&parent->subsurface_list)) {
|
||||
if (!weston_subsurface_create_for_parent(parent)) {
|
||||
wl_resource_post_no_memory(resource);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
sub = weston_subsurface_create(id, surface, parent);
|
||||
if (!sub) {
|
||||
wl_resource_post_no_memory(resource);
|
||||
return;
|
||||
}
|
||||
|
||||
surface->configure = subsurface_configure;
|
||||
surface->configure_private = sub;
|
||||
}
|
||||
|
||||
static void
|
||||
subcompositor_destroy(struct wl_client *client, struct wl_resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource);
|
||||
}
|
||||
|
||||
static const struct wl_subcompositor_interface subcompositor_interface = {
|
||||
subcompositor_destroy,
|
||||
subcompositor_get_subsurface
|
||||
};
|
||||
|
||||
static void
|
||||
bind_subcompositor(struct wl_client *client,
|
||||
void *data, uint32_t version, uint32_t id)
|
||||
{
|
||||
struct weston_compositor *compositor = data;
|
||||
|
||||
wl_client_add_object(client, &wl_subcompositor_interface,
|
||||
&subcompositor_interface, id, compositor);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_compositor_dpms(struct weston_compositor *compositor,
|
||||
enum dpms_enum state)
|
||||
@ -2012,6 +2712,10 @@ weston_compositor_init(struct weston_compositor *ec,
|
||||
ec, compositor_bind))
|
||||
return -1;
|
||||
|
||||
if (!wl_display_add_global(display, &wl_subcompositor_interface,
|
||||
ec, bind_subcompositor))
|
||||
return -1;
|
||||
|
||||
wl_list_init(&ec->surface_list);
|
||||
wl_list_init(&ec->plane_list);
|
||||
wl_list_init(&ec->layer_list);
|
||||
|
@ -558,6 +558,53 @@ struct weston_region {
|
||||
pixman_region32_t region;
|
||||
};
|
||||
|
||||
struct weston_subsurface {
|
||||
struct wl_resource *resource;
|
||||
|
||||
/* guaranteed to be valid and non-NULL */
|
||||
struct weston_surface *surface;
|
||||
struct wl_listener surface_destroy_listener;
|
||||
|
||||
/* can be NULL */
|
||||
struct weston_surface *parent;
|
||||
struct wl_listener parent_destroy_listener;
|
||||
struct wl_list parent_link;
|
||||
struct wl_list parent_link_pending;
|
||||
|
||||
struct {
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
int set;
|
||||
} position;
|
||||
|
||||
struct {
|
||||
int has_data;
|
||||
|
||||
/* wl_surface.attach */
|
||||
int newly_attached;
|
||||
struct weston_buffer_reference buffer_ref;
|
||||
int32_t sx;
|
||||
int32_t sy;
|
||||
|
||||
/* wl_surface.damage */
|
||||
pixman_region32_t damage;
|
||||
|
||||
/* wl_surface.set_opaque_region */
|
||||
pixman_region32_t opaque;
|
||||
|
||||
/* wl_surface.set_input_region */
|
||||
pixman_region32_t input;
|
||||
|
||||
/* wl_surface.frame */
|
||||
struct wl_list frame_callback_list;
|
||||
|
||||
/* wl_surface.set_buffer_transform */
|
||||
uint32_t buffer_transform;
|
||||
} cached;
|
||||
|
||||
int synchronized;
|
||||
};
|
||||
|
||||
/* Using weston_surface transformations
|
||||
*
|
||||
* To add a transformation to a surface, create a struct weston_transform, and
|
||||
@ -690,6 +737,13 @@ struct weston_surface {
|
||||
*/
|
||||
void (*configure)(struct weston_surface *es, int32_t sx, int32_t sy, int32_t width, int32_t height);
|
||||
void *configure_private;
|
||||
|
||||
/* Parent's list of its sub-surfaces, weston_subsurface:parent_link.
|
||||
* Contains also the parent itself as a dummy weston_subsurface,
|
||||
* if the list is not empty.
|
||||
*/
|
||||
struct wl_list subsurface_list; /* weston_subsurface::parent_link */
|
||||
struct wl_list subsurface_list_pending; /* ...::parent_link_pending */
|
||||
};
|
||||
|
||||
enum weston_key_state_update {
|
||||
|
Loading…
Reference in New Issue
Block a user