diff --git a/include/wlr/types/wlr_surface.h b/include/wlr/types/wlr_surface.h index c9abf042..b6d5f79f 100644 --- a/include/wlr/types/wlr_surface.h +++ b/include/wlr/types/wlr_surface.h @@ -48,8 +48,10 @@ struct wlr_subsurface { bool has_cache; bool synchronized; + bool reordered; struct wl_list parent_link; + struct wl_list parent_pending_link; }; struct wlr_surface { @@ -76,6 +78,8 @@ struct wlr_surface { struct wlr_subsurface *subsurface; struct wl_list subsurface_list; // wlr_subsurface::parent_link + // wlr_subsurface::parent_pending_link + struct wl_list subsurface_pending_list; void *data; }; diff --git a/types/wlr_surface.c b/types/wlr_surface.c index fa4b4d5a..6f6df226 100644 --- a/types/wlr_surface.c +++ b/types/wlr_surface.c @@ -290,12 +290,44 @@ static void wlr_surface_move_state(struct wlr_surface *surface, struct wlr_surfa next->invalid = 0; } +static void wlr_surface_damage_subsurfaces(struct wlr_subsurface *subsurface) { + // XXX: This is probably the wrong way to do it, because this damage should + // come from the client, but weston doesn't do it correctly either and it + // seems to work ok. See the comment on weston_surface_damage for more info + // about a better approach. + struct wlr_surface *surface = subsurface->surface; + pixman_region32_union_rect(&surface->current->surface_damage, + &surface->current->surface_damage, + 0, 0, surface->current->width, + surface->current->height); + + subsurface->reordered = false; + + struct wlr_subsurface *child; + wl_list_for_each(child, &subsurface->surface->subsurface_list, parent_link) { + wlr_surface_damage_subsurfaces(child); + } +} + static void wlr_surface_commit_pending(struct wlr_surface *surface) { int32_t oldw = surface->current->buffer_width; int32_t oldh = surface->current->buffer_height; wlr_surface_move_state(surface, surface->pending, surface->current); + // commit subsurface order + struct wlr_subsurface *subsurface; + wl_list_for_each_reverse(subsurface, &surface->subsurface_pending_list, + parent_pending_link) { + wl_list_remove(&subsurface->parent_link); + wl_list_insert(&surface->subsurface_list, &subsurface->parent_link); + + if (subsurface->reordered) { + // TODO: damage all the subsurfaces + wlr_surface_damage_subsurfaces(subsurface); + } + } + surface->reupload_buffer = oldw != surface->current->buffer_width || oldh != surface->current->buffer_height; @@ -546,6 +578,7 @@ struct wlr_surface *wlr_surface_create(struct wl_resource *res, wl_signal_init(&surface->signals.commit); wl_signal_init(&surface->signals.destroy); wl_list_init(&surface->subsurface_list); + wl_list_init(&surface->subsurface_pending_list); wl_resource_set_implementation(res, &surface_interface, surface, destroy_surface); return surface; @@ -612,14 +645,69 @@ static void subsurface_set_position(struct wl_client *client, surface->pending->subsurface_position.y = y; } +static struct wlr_subsurface *subsurface_find_sibling( + struct wlr_subsurface *subsurface, struct wlr_surface *surface) { + struct wlr_surface *parent = subsurface->parent; + + struct wlr_subsurface *sibling; + wl_list_for_each(sibling, &parent->subsurface_list, parent_link) { + if (sibling->surface == surface && sibling != subsurface) + return sibling; + } + + return NULL; +} + static void subsurface_place_above(struct wl_client *client, - struct wl_resource *resource, struct wl_resource *sibling) { - wlr_log(L_DEBUG, "TODO: subsurface place above"); + struct wl_resource *resource, struct wl_resource *sibling_resource) { + struct wlr_subsurface *subsurface = wl_resource_get_user_data(resource); + + if (!subsurface) { + return; + } + + struct wlr_surface *sibling_surface = + wl_resource_get_user_data(sibling_resource); + struct wlr_subsurface *sibling = + subsurface_find_sibling(subsurface, sibling_surface); + + if (!sibling) { + wl_resource_post_error(subsurface->resource, + WL_SUBSURFACE_ERROR_BAD_SURFACE, + "%s: wl_surface@%d is not a parent or sibling", + "place_above", wl_resource_get_id(sibling_surface->resource)); + return; + } + + wl_list_remove(&subsurface->parent_pending_link); + wl_list_insert(sibling->parent_pending_link.prev, + &subsurface->parent_pending_link); + + subsurface->reordered = true; } static void subsurface_place_below(struct wl_client *client, - struct wl_resource *resource, struct wl_resource *sibling) { - wlr_log(L_DEBUG, "TODO: subsurface place below"); + struct wl_resource *resource, struct wl_resource *sibling_resource) { + struct wlr_subsurface *subsurface = wl_resource_get_user_data(resource); + + struct wlr_surface *sibling_surface = + wl_resource_get_user_data(sibling_resource); + struct wlr_subsurface *sibling = + subsurface_find_sibling(subsurface, sibling_surface); + + if (!sibling) { + wl_resource_post_error(subsurface->resource, + WL_SUBSURFACE_ERROR_BAD_SURFACE, + "%s: wl_surface@%d is not a parent or sibling", + "place_below", wl_resource_get_id(sibling_surface->resource)); + return; + } + + wl_list_remove(&subsurface->parent_pending_link); + wl_list_insert(&sibling->parent_pending_link, + &subsurface->parent_pending_link); + + subsurface->reordered = true; } static void subsurface_set_sync(struct wl_client *client,