From cf401a6394df08ec4ad20cf7542172a03fe58b92 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Mon, 1 Dec 2025 14:56:04 +0900 Subject: [PATCH] view: decouple always-on-top windows from the omnipresent state Before this commit, always-on-{top,bottom} windows were always visible on all workspaces (omnipresent) because the they were not in per-workspace trees like normal windows. This commit fixes this by removing per-workspace trees and instead showing/hiding views individually based on view->workspace. --- include/config/types.h | 5 +---- include/workspaces.h | 2 -- src/debug.c | 9 --------- src/desktop.c | 4 ++-- src/view.c | 27 ++++++++----------------- src/workspaces.c | 46 +++++++++++------------------------------- src/xdg.c | 2 +- src/xwayland.c | 2 +- 8 files changed, 25 insertions(+), 72 deletions(-) diff --git a/include/config/types.h b/include/config/types.h index 4561194e..3b1fa5e8 100644 --- a/include/config/types.h +++ b/include/config/types.h @@ -62,10 +62,7 @@ enum lab_view_criteria { /* No filter -> all focusable views */ LAB_VIEW_CRITERIA_NONE = 0, - /* - * Includes always-on-top views, e.g. - * what is visible on the current workspace - */ + /* Includes omnipresent (visible on all desktops) views */ LAB_VIEW_CRITERIA_CURRENT_WORKSPACE = 1 << 0, /* Positive criteria */ diff --git a/include/workspaces.h b/include/workspaces.h index cc9a2fcf..0d37ce9d 100644 --- a/include/workspaces.h +++ b/include/workspaces.h @@ -8,14 +8,12 @@ struct seat; struct server; -struct wlr_scene_tree; struct workspace { struct wl_list link; /* struct server.workspaces */ struct server *server; char *name; - struct wlr_scene_tree *tree; struct lab_cosmic_workspace *cosmic_workspace; struct { diff --git a/src/debug.c b/src/debug.c index 7a39824f..67c40d00 100644 --- a/src/debug.c +++ b/src/debug.c @@ -106,15 +106,6 @@ get_special(struct server *server, struct wlr_scene_node *node) if (node == &server->view_tree_always_on_top->node) { return "server->always_on_top"; } - if (node->parent == server->view_tree) { - struct workspace *workspace; - wl_list_for_each(workspace, &server->workspaces.all, link) { - if (&workspace->tree->node == node) { - return workspace->name; - } - } - return "unknown workspace"; - } if (node->parent == &server->scene->tree) { struct output *output; wl_list_for_each(output, &server->outputs, link) { diff --git a/src/desktop.c b/src/desktop.c index 6a64ab13..e5a33a78 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -96,9 +96,9 @@ desktop_focus_view(struct view *view, bool raise) /* * Switch workspace if necessary to make the view visible - * (unnecessary for "always on {top,bottom}" views). + * (unnecessary for omnipresent views). */ - if (!view_is_always_on_top(view) && !view_is_always_on_bottom(view)) { + if (!view->visible_on_all_workspaces) { workspaces_switch_to(view->workspace, /*update_focus*/ false); } diff --git a/src/view.c b/src/view.c index 5271404b..0449aed8 100644 --- a/src/view.c +++ b/src/view.c @@ -267,13 +267,7 @@ matches_criteria(struct view *view, enum lab_view_criteria criteria) return false; } if (criteria & LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) { - /* - * Always-on-top views are always on the current desktop and are - * special in that they live in a different tree. - */ - struct server *server = view->server; - if (view->scene_tree->node.parent != server->workspaces.current->tree - && !view_is_always_on_top(view)) { + if (view->workspace != view->server->workspaces.current) { return false; } } @@ -303,11 +297,7 @@ matches_criteria(struct view *view, enum lab_view_criteria criteria) } } if (criteria & LAB_VIEW_CRITERIA_NO_OMNIPRESENT) { - /* - * TODO: Once always-on-top views use a per-workspace - * sub-tree we can remove the check from this condition. - */ - if (view->visible_on_all_workspaces || view_is_always_on_top(view)) { + if (view->visible_on_all_workspaces) { return false; } } @@ -1562,9 +1552,8 @@ view_toggle_always_on_top(struct view *view) { assert(view); if (view_is_always_on_top(view)) { - view->workspace = view->server->workspaces.current; wlr_scene_node_reparent(&view->scene_tree->node, - view->workspace->tree); + view->server->view_tree); } else { wlr_scene_node_reparent(&view->scene_tree->node, view->server->view_tree_always_on_top); @@ -1584,9 +1573,8 @@ view_toggle_always_on_bottom(struct view *view) { assert(view); if (view_is_always_on_bottom(view)) { - view->workspace = view->server->workspaces.current; wlr_scene_node_reparent(&view->scene_tree->node, - view->workspace->tree); + view->server->view_tree); } else { wlr_scene_node_reparent(&view->scene_tree->node, view->server->view_tree_always_on_bottom); @@ -1608,8 +1596,7 @@ view_move_to_workspace(struct view *view, struct workspace *workspace) assert(workspace); if (view->workspace != workspace) { view->workspace = workspace; - wlr_scene_node_reparent(&view->scene_tree->node, - workspace->tree); + view_update_visibility(view); } } @@ -2411,7 +2398,9 @@ mappable_disconnect(struct mappable *mappable) void view_update_visibility(struct view *view) { - bool visible = view->mapped && !view->minimized; + struct server *server = view->server; + bool visible = view->mapped && !view->minimized + && view->workspace == server->workspaces.current; if (visible == view->scene_tree->node.enabled) { return; } diff --git a/src/workspaces.c b/src/workspaces.c index 9b4ab819..856af42c 100644 --- a/src/workspaces.c +++ b/src/workspaces.c @@ -234,9 +234,7 @@ add_workspace(struct server *server, const char *name) struct workspace *workspace = znew(*workspace); workspace->server = server; workspace->name = xstrdup(name); - workspace->tree = wlr_scene_tree_create(server->view_tree); wl_list_append(&server->workspaces.all, &workspace->link); - wlr_scene_node_set_enabled(&workspace->tree->node, false); /* cosmic */ workspace->cosmic_workspace = lab_cosmic_workspace_create(server->workspaces.cosmic_group); @@ -435,7 +433,6 @@ workspaces_init(struct server *server) } server->workspaces.current = initial; - wlr_scene_node_set_enabled(&initial->tree->node, true); lab_cosmic_workspace_set_active(initial->cosmic_workspace, true); lab_ext_workspace_set_active(initial->ext_workspace, true); } @@ -454,28 +451,11 @@ workspaces_switch_to(struct workspace *target, bool update_focus) return; } - /* Disable the old workspace */ - wlr_scene_node_set_enabled( - &server->workspaces.current->tree->node, false); - lab_cosmic_workspace_set_active( server->workspaces.current->cosmic_workspace, false); lab_ext_workspace_set_active( server->workspaces.current->ext_workspace, false); - /* Move Omnipresent views to new workspace */ - struct view *view; - enum lab_view_criteria criteria = - LAB_VIEW_CRITERIA_CURRENT_WORKSPACE; - for_each_view_reverse(view, &server->views, criteria) { - if (view->visible_on_all_workspaces) { - view_move_to_workspace(view, target); - } - } - - /* Enable the new workspace */ - wlr_scene_node_set_enabled(&target->tree->node, true); - /* Save the last visited workspace */ server->workspaces.last = server->workspaces.current; @@ -483,24 +463,26 @@ workspaces_switch_to(struct workspace *target, bool update_focus) server->workspaces.current = target; struct view *grabbed_view = server->grabbed_view; - if (grabbed_view && !view_is_always_on_top(grabbed_view)) { + if (grabbed_view) { view_move_to_workspace(grabbed_view, target); } + struct view *view; + wl_list_for_each(view, &server->views, link) { + /* Omnipresent views are always in the current workspace */ + if (view->visible_on_all_workspaces) { + view->workspace = target; + } + view_update_visibility(view); + } + /* * Make sure we are focusing what the user sees. Only refocus if - * the focus is not already on an omnipresent or always-on-top view. - * - * TODO: Decouple always-on-top views from the omnipresent state. - * One option for that would be to create a new scene tree - * as child of every workspace tree and then reparent a-o-t - * windows to that one. Combined with adjusting the condition - * below that should take care of the issue. + * the focus is not already on an omnipresent. */ if (update_focus) { struct view *active_view = server->active_view; - if (!active_view || (!active_view->visible_on_all_workspaces - && !view_is_always_on_top(active_view))) { + if (!active_view || !active_view->visible_on_all_workspaces) { desktop_focus_topmost_view(server); } } @@ -514,9 +496,6 @@ workspaces_switch_to(struct workspace *target, bool update_focus) */ cursor_update_focus(server); - /* Ensure that only currently visible fullscreen windows hide the top layer */ - desktop_update_top_layer_visibility(server); - lab_cosmic_workspace_set_active(target->cosmic_workspace, true); lab_ext_workspace_set_active(target->ext_workspace, true); } @@ -569,7 +548,6 @@ workspaces_find(struct workspace *anchor, const char *name, bool wrap) static void destroy_workspace(struct workspace *workspace) { - wlr_scene_node_destroy(&workspace->tree->node); zfree(workspace->name); wl_list_remove(&workspace->link); wl_list_remove(&workspace->on_cosmic.activate.link); diff --git a/src/xdg.c b/src/xdg.c index b239e8da..5f4350d0 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -1019,7 +1019,7 @@ handle_new_xdg_toplevel(struct wl_listener *listener, void *data) } view->workspace = server->workspaces.current; - view->scene_tree = wlr_scene_tree_create(view->workspace->tree); + view->scene_tree = wlr_scene_tree_create(server->view_tree); wlr_scene_node_set_enabled(&view->scene_tree->node, false); struct wlr_scene_tree *tree = wlr_scene_xdg_surface_create( diff --git a/src/xwayland.c b/src/xwayland.c index 5984a31b..9666df89 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -1010,7 +1010,7 @@ xwayland_view_create(struct server *server, xsurface->data = view; view->workspace = server->workspaces.current; - view->scene_tree = wlr_scene_tree_create(view->workspace->tree); + view->scene_tree = wlr_scene_tree_create(server->view_tree); node_descriptor_create(&view->scene_tree->node, LAB_NODE_VIEW, view, /*data*/ NULL);