diff --git a/include/rootston/view.h b/include/rootston/view.h index b1feb5ce..0174e4f3 100644 --- a/include/rootston/view.h +++ b/include/rootston/view.h @@ -181,6 +181,8 @@ struct roots_subsurface { struct roots_view_child view_child; struct wlr_subsurface *wlr_subsurface; struct wl_listener destroy; + struct wl_listener map; + struct wl_listener unmap; }; struct roots_wl_shell_popup { diff --git a/include/wlr/types/wlr_surface.h b/include/wlr/types/wlr_surface.h index b8f8c02a..0c389987 100644 --- a/include/wlr/types/wlr_surface.h +++ b/include/wlr/types/wlr_surface.h @@ -130,6 +130,7 @@ struct wlr_subsurface { bool synchronized; bool reordered; + bool mapped; struct wl_list parent_link; struct wl_list parent_pending_link; @@ -139,6 +140,8 @@ struct wlr_subsurface { struct { struct wl_signal destroy; + struct wl_signal map; + struct wl_signal unmap; } events; void *data; diff --git a/rootston/desktop.c b/rootston/desktop.c index fcb530cf..a38f4045 100644 --- a/rootston/desktop.c +++ b/rootston/desktop.c @@ -420,6 +420,8 @@ static void subsurface_destroy(struct roots_view_child *child) { return; } wl_list_remove(&subsurface->destroy.link); + wl_list_remove(&subsurface->map.link); + wl_list_remove(&subsurface->unmap.link); view_child_finish(&subsurface->view_child); free(subsurface); } @@ -428,7 +430,25 @@ static void subsurface_handle_destroy(struct wl_listener *listener, void *data) { struct roots_subsurface *subsurface = wl_container_of(listener, subsurface, destroy); - subsurface_destroy((struct roots_view_child *)subsurface); + subsurface_destroy(&subsurface->view_child); +} + +static void subsurface_handle_map(struct wl_listener *listener, + void *data) { + struct roots_subsurface *subsurface = + wl_container_of(listener, subsurface, map); + struct roots_view *view = subsurface->view_child.view; + view_damage_whole(view); + input_update_cursor_focus(view->desktop->server->input); +} + +static void subsurface_handle_unmap(struct wl_listener *listener, + void *data) { + struct roots_subsurface *subsurface = + wl_container_of(listener, subsurface, unmap); + struct roots_view *view = subsurface->view_child.view; + view_damage_whole(view); + input_update_cursor_focus(view->desktop->server->input); } struct roots_subsurface *subsurface_create(struct roots_view *view, @@ -443,7 +463,10 @@ struct roots_subsurface *subsurface_create(struct roots_view *view, view_child_init(&subsurface->view_child, view, wlr_subsurface->surface); subsurface->destroy.notify = subsurface_handle_destroy; wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); - input_update_cursor_focus(view->desktop->server->input); + subsurface->map.notify = subsurface_handle_map; + wl_signal_add(&wlr_subsurface->events.map, &subsurface->map); + subsurface->unmap.notify = subsurface_handle_unmap; + wl_signal_add(&wlr_subsurface->events.unmap, &subsurface->unmap); return subsurface; } diff --git a/types/wlr_surface.c b/types/wlr_surface.c index de63bdae..febfcd43 100644 --- a/types/wlr_surface.c +++ b/types/wlr_surface.c @@ -534,11 +534,15 @@ static void surface_state_finish(struct wlr_surface_state *state) { pixman_region32_fini(&state->input); } +static void subsurface_unmap(struct wlr_subsurface *subsurface); + static void subsurface_destroy(struct wlr_subsurface *subsurface) { if (subsurface == NULL) { return; } + subsurface_unmap(subsurface); + wlr_signal_emit_safe(&subsurface->events.destroy, subsurface); wl_list_remove(&subsurface->surface_destroy.link); @@ -799,6 +803,60 @@ static const struct wl_subsurface_interface subsurface_implementation = { .set_desync = subsurface_handle_set_desync, }; +/** + * Checks if this subsurface needs to be marked as mapped. This can happen if: + * - The subsurface has a buffer + * - Its parent is mapped + */ +static void subsurface_consider_map(struct wlr_subsurface *subsurface, + bool check_parent) { + if (subsurface->mapped || !wlr_surface_has_buffer(subsurface->surface)) { + return; + } + + if (check_parent) { + if (subsurface->parent == NULL) { + return; + } + if (wlr_surface_is_subsurface(subsurface->parent)) { + struct wlr_subsurface *parent = + wlr_subsurface_from_wlr_surface(subsurface->parent); + if (parent == NULL || !parent->mapped) { + return; + } + } + } + + // Now we can map the subsurface + if (subsurface->mapped) { + return; + } + + wlr_signal_emit_safe(&subsurface->events.map, subsurface); + subsurface->mapped = true; + + // Try mapping all children too + struct wlr_subsurface *child; + wl_list_for_each(child, &subsurface->surface->subsurfaces, parent_link) { + subsurface_consider_map(child, false); + } +} + +static void subsurface_unmap(struct wlr_subsurface *subsurface) { + if (!subsurface->mapped) { + return; + } + + wlr_signal_emit_safe(&subsurface->events.unmap, subsurface); + subsurface->mapped = false; + + // Unmap all children + struct wlr_subsurface *child; + wl_list_for_each(child, &subsurface->surface->subsurfaces, parent_link) { + subsurface_unmap(child); + } +} + static void subsurface_role_commit(struct wlr_surface *surface) { struct wlr_subsurface *subsurface = wlr_subsurface_from_wlr_surface(surface); @@ -829,17 +887,35 @@ static void subsurface_role_commit(struct wlr_surface *surface) { &surface->buffer_damage, 0, 0, surface->current.buffer_width, surface->current.buffer_height); } + + subsurface_consider_map(subsurface, true); +} + +static void subsurface_role_precommit(struct wlr_surface *surface) { + struct wlr_subsurface *subsurface = + wlr_subsurface_from_wlr_surface(surface); + if (subsurface == NULL) { + return; + } + + if (surface->pending.committed & WLR_SURFACE_STATE_BUFFER && + surface->pending.buffer_resource == NULL) { + // This is a NULL commit + subsurface_unmap(subsurface); + } } const struct wlr_surface_role subsurface_role = { .name = "wl_subsurface", .commit = subsurface_role_commit, + .precommit = subsurface_role_precommit, }; static void subsurface_handle_parent_destroy(struct wl_listener *listener, void *data) { struct wlr_subsurface *subsurface = wl_container_of(listener, subsurface, parent_destroy); + subsurface_unmap(subsurface); wl_list_remove(&subsurface->parent_link); wl_list_remove(&subsurface->parent_pending_link); wl_list_remove(&subsurface->parent_destroy.link); @@ -882,6 +958,8 @@ struct wlr_subsurface *wlr_subsurface_create(struct wlr_surface *surface, subsurface_resource_destroy); wl_signal_init(&subsurface->events.destroy); + wl_signal_init(&subsurface->events.map); + wl_signal_init(&subsurface->events.unmap); wl_signal_add(&surface->events.destroy, &subsurface->surface_destroy); subsurface->surface_destroy.notify = subsurface_handle_surface_destroy;