This commit depends on the Wayland commit "protocol: add 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 - a rudimentary implementation of the wl_subsurface interface - proper surface transformation inheritance from parent to sub-surfaces Struct weston_subsurface is dynamically allocated. For sub-surfaces, it is completely populated, and assigned to the weston_surface::private pointer. Sub-surfaces are identified by the weston_surface::configure function pointer. 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, and the transform is not linked. Sub-surfaces are not added directly into layers for display or input. Instead, a following patch will hook them up via the sub-surface list present in parent weston_surfaces. 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. Features still lacking are: - commit behaviour For wl_surface.commit, a parent and its sub-surfaces function still as independent surfaces. - double buffered state Some state, like sub-surface position, is supposed to be applied on the next commit. For now, the state change is applied immediately. Signed-off-by: Pekka Paalanen <ppaala...@gmail.com> --- src/compositor.c | 366 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/compositor.h | 20 +++ 2 files changed, 386 insertions(+), 0 deletions(-) diff --git a/src/compositor.c b/src/compositor.c index 0a3c4c8..e7e1360 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -310,6 +310,8 @@ 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); + return surface; } @@ -869,6 +871,8 @@ destroy_surface(struct wl_resource *resource) struct weston_compositor *compositor = surface->compositor; struct weston_frame_callback *cb, *next; + assert(wl_list_empty(&surface->subsurface_list)); + if (weston_surface_is_mapped(surface)) weston_surface_unmap(surface); @@ -1583,6 +1587,364 @@ static const struct wl_compositor_interface compositor_interface = { compositor_create_region }; +static void +subsurface_configure(struct weston_surface *surface, int32_t dx, int32_t dy) +{ + int32_t x, y; + + fprintf(stderr, "%s(surface %p, %d, %d)\n", + __func__, surface, dx, dy); + + x = surface->geometry.x + dx; + y = surface->geometry.y + dy; + weston_surface_configure(surface, x, y, + weston_surface_buffer_width(surface), + weston_surface_buffer_height(surface)); + + /* 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)) { + fprintf(stderr, "%s: map!\n", __func__); + wl_list_init(&surface->layer_link); + weston_surface_update_transform(surface); + } +} + +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; + + fprintf(stderr, "%s(surface %p, %d, %d)\n", + __func__, sub->surface, x, y); + + weston_surface_set_position(sub->surface, x, y); +} + +static struct weston_subsurface * +subsurface_from_surface(struct weston_surface *surface) +{ + struct weston_subsurface *sub; + + if (surface->configure == subsurface_configure) + return surface->private; + + 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->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->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; + + sibling = subsurface_sibling_check(sub, surface, "place_above"); + if (!sibling) + return; + + fprintf(stderr, "%p %s of %p\n", sub->surface, __func__, + sibling->surface); + + wl_list_remove(&sub->parent_link); + wl_list_insert(sibling->parent_link.prev, &sub->parent_link); +} + +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; + + sibling = subsurface_sibling_check(sub, surface, "place_below"); + if (!sibling) + return; + + fprintf(stderr, "%p %s of %p\n", sub->surface, __func__, + sibling->surface); + + wl_list_remove(&sub->parent_link); + wl_list_insert(&sibling->parent_link, &sub->parent_link); +} + +static const struct wl_subsurface_interface subsurface_interface = { + subsurface_set_position, + subsurface_place_above, + subsurface_place_below +}; + +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->surface.resource); + + fprintf(stderr, "%s: surface %p\n", __func__, sub->surface); + + if (sub->surface != sub->parent) + wl_resource_destroy(&sub->resource); + else { + /* parent does not have a wl_subsurface resource */ + wl_list_remove(&sub->parent_link); + free(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->surface.resource); + assert(sub->surface != sub->parent); + + fprintf(stderr, "%s: surface %p, parent %p\n", + __func__, sub->surface, sub->parent); + + if (weston_surface_is_mapped(sub->surface)) + weston_surface_unmap(sub->surface); + + sub->parent = NULL; + wl_list_remove(&sub->parent_link); + wl_list_remove(&sub->parent_transform.link); + wl_list_remove(&sub->parent_dirty_listener.link); +} + +static void +subsurface_destroy(struct wl_resource *resource) +{ + struct weston_subsurface *sub = resource->data; + + fprintf(stderr, "%s: surface %p\n", __func__, sub->surface); + + assert(sub->surface); + assert(sub->surface->configure == subsurface_configure); + assert(sub->surface->private == sub); + sub->surface->configure = NULL; + sub->surface->private = NULL; + wl_list_remove(&sub->surface_destroy_listener.link); + + if (sub->parent) { + wl_list_remove(&sub->parent_transform.link); + wl_list_remove(&sub->parent_dirty_listener.link); + wl_list_remove(&sub->parent_link); + wl_list_remove(&sub->parent_destroy_listener.link); + } + + free(sub); +} + +static void +subsurface_notify_parent_geometry_dirty(struct wl_listener *listener, + void *data) +{ + struct weston_subsurface *sub = + container_of(listener, struct weston_subsurface, + parent_dirty_listener); + + weston_surface_geometry_dirty(sub->surface); +} + +static void +weston_subsurface_link(struct weston_subsurface *sub, + struct weston_surface *surface, + struct weston_surface *parent) +{ + sub->surface = surface; + sub->surface_destroy_listener.notify = + subsurface_handle_surface_destroy; + wl_signal_add(&surface->surface.resource.destroy_signal, + &sub->surface_destroy_listener); + + sub->parent = parent; + + if (sub->surface != sub->parent) { + sub->parent_destroy_listener.notify = + subsurface_handle_parent_destroy; + wl_signal_add(&parent->surface.resource.destroy_signal, + &sub->parent_destroy_listener); + + sub->parent_transform.matrix = &parent->transform.matrix; + sub->parent_transform.parent = parent; + sub->parent_dirty_listener.notify = + subsurface_notify_parent_geometry_dirty; + wl_signal_add(&parent->transform.dirty_signal, + &sub->parent_dirty_listener); + wl_list_insert(surface->geometry.transformation_list.prev, + &sub->parent_transform.link); + } + + wl_list_insert(&parent->subsurface_list, &sub->parent_link); +} + +static struct weston_subsurface * +weston_subsurface_create(uint32_t id, struct weston_surface *surface, + struct weston_surface *parent) +{ + struct weston_subsurface *sub; + uint32_t ret; + + sub = malloc(sizeof *sub); + if (!sub) + return NULL; + + sub->resource.destroy = subsurface_destroy; + sub->resource.data = sub; + sub->resource.object.id = id; + sub->resource.object.interface = &wl_subsurface_interface; + sub->resource.object.implementation = + (void (**)(void))&subsurface_interface; + + ret = wl_client_add_resource(surface->surface.resource.client, + &sub->resource); + if (ret == 0) { + free(sub); + return NULL; + } + + weston_subsurface_link(sub, surface, parent); + + return sub; +} + +/* Create a dummy subsurface for having the parent itself in its + * sub-surface list. 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(sub, parent, parent); + + 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 (!wl_list_empty(&surface->subsurface_list)) { + wl_resource_post_error(resource, + WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, + "%s%d: wl_surface@%d is already a parent", + where, id, surface_resource->object.id); + return; + } + + if (parent->configure == subsurface_configure) { + wl_resource_post_error(resource, + WL_SUBCOMPOSITOR_ERROR_BAD_PARENT, + "%s%d: parent wl_surface@%d is already a sub-surface", + where, id, parent_resource->object.id); + return; + } + + if (surface->configure == subsurface_configure) { + 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->private = sub; + fprintf(stderr, "%s: surface %p, parent %p\n", + __func__, surface, parent); +} + +static const struct wl_subcompositor_interface subcompositor_interface = { + 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); +} + WL_EXPORT void weston_compositor_wake(struct weston_compositor *compositor) { @@ -2992,6 +3354,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->layer_list); wl_list_init(&ec->seat_list); diff --git a/src/compositor.h b/src/compositor.h index 916915b..c6c4775 100644 --- a/src/compositor.h +++ b/src/compositor.h @@ -370,6 +370,21 @@ 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; + + struct weston_surface *parent; + struct wl_listener parent_destroy_listener; + struct wl_list parent_link; + + struct weston_matrix_pointer parent_transform; + struct wl_listener parent_dirty_listener; +}; + /* Using weston_surface transformations * * To add a transformation to a surface, create a struct weston_matrix_pointer, @@ -494,6 +509,11 @@ struct weston_surface { */ void (*configure)(struct weston_surface *es, int32_t sx, int32_t sy); void *private; + + /* Parent's list of its sub-surfaces, weston_subsurface:parent_link. + * Contains also the parent itself as a dummy weston_subsurface. + */ + struct wl_list subsurface_list; }; enum weston_key_state_update { -- 1.7.8.6 _______________________________________________ wayland-devel mailing list wayland-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/wayland-devel