diff --git a/include/common/scene-helpers.h b/include/common/scene-helpers.h index a124d0a3..921ff56a 100644 --- a/include/common/scene-helpers.h +++ b/include/common/scene-helpers.h @@ -1,7 +1,17 @@ /* SPDX-License-Identifier: GPL-2.0-only */ -#include +struct wlr_scene_node; +struct wlr_scene_rect; +struct wlr_scene_tree; +struct wlr_surface; struct wlr_scene_rect *lab_wlr_scene_get_rect(struct wlr_scene_node *node); struct wlr_scene_tree *lab_scene_tree_from_node(struct wlr_scene_node *node); struct wlr_surface *lab_wlr_surface_from_node(struct wlr_scene_node *node); + +/** + * lab_get_prev_node - return previous (sibling) node + * @node: node to find the previous node from + * Return NULL if previous link is list-head which means node is bottom-most + */ +struct wlr_scene_node *lab_wlr_scene_get_prev_node(struct wlr_scene_node *node); diff --git a/include/labwc.h b/include/labwc.h index 794b2671..c5d78aad 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -202,8 +202,6 @@ struct server { /* Tree for built in menu */ struct wlr_scene_tree *menu_tree; - struct multi_rect *osd_preview_outline; - /* Workspaces */ struct wl_list workspaces; /* struct workspace.link */ struct workspace *workspace_current; @@ -231,7 +229,13 @@ struct server { struct wl_listener new_constraint; /* Set when in cycle (alt-tab) mode */ - struct view *cycle_view; + struct osd_state { + struct view *cycle_view; + bool preview_was_enabled; + struct wlr_scene_node *preview_node; + struct wlr_scene_node *preview_anchor; + struct multi_rect *preview_outline; + } osd_state; struct theme *theme; @@ -573,9 +577,14 @@ void server_init(struct server *server); void server_start(struct server *server); void server_finish(struct server *server); -/* update onscreen display 'alt-tab' buffer */ -void osd_finish(struct server *server); +/* Updates onscreen display 'alt-tab' buffer */ void osd_update(struct server *server); +/* Closes the OSD */ +void osd_finish(struct server *server); +/* Moves preview views back into their original stacking order and state */ +void osd_preview_restore(struct server *server); +/* Notify OSD about a destroying view */ +void osd_on_view_destroy(struct view *view); /* * wlroots "input inhibitor" extension (required for swaylock) blocks diff --git a/src/action.c b/src/action.c index 3b6dd4d4..9604c306 100644 --- a/src/action.c +++ b/src/action.c @@ -235,13 +235,13 @@ actions_run(struct view *activator, struct server *server, } break; case ACTION_TYPE_NEXT_WINDOW: - server->cycle_view = desktop_cycle_view(server, - server->cycle_view, LAB_CYCLE_DIR_FORWARD); + server->osd_state.cycle_view = desktop_cycle_view(server, + server->osd_state.cycle_view, LAB_CYCLE_DIR_FORWARD); osd_update(server); break; case ACTION_TYPE_PREVIOUS_WINDOW: - server->cycle_view = desktop_cycle_view(server, - server->cycle_view, LAB_CYCLE_DIR_BACKWARD); + server->osd_state.cycle_view = desktop_cycle_view(server, + server->osd_state.cycle_view, LAB_CYCLE_DIR_BACKWARD); osd_update(server); break; case ACTION_TYPE_RECONFIGURE: diff --git a/src/common/scene-helpers.c b/src/common/scene-helpers.c index ae1220bf..6bda9bd7 100644 --- a/src/common/scene-helpers.c +++ b/src/common/scene-helpers.c @@ -32,3 +32,15 @@ lab_wlr_surface_from_node(struct wlr_scene_node *node) } return NULL; } + +struct wlr_scene_node * +lab_wlr_scene_get_prev_node(struct wlr_scene_node *node) +{ + assert(node); + struct wlr_scene_node *prev; + prev = wl_container_of(node->link.prev, node, link); + if (&prev->link == &node->parent->children) { + return NULL; + } + return prev; +} diff --git a/src/desktop.c b/src/desktop.c index ab5e00ce..224cdf49 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -203,6 +203,9 @@ desktop_cycle_view(struct server *server, struct view *start_view, /* Scene nodes are ordered like last node == displayed topmost */ iter = dir == LAB_CYCLE_DIR_FORWARD ? get_prev_item : get_next_item; + /* Make sure to have all nodes in their actual ordering */ + osd_preview_restore(server); + do { list_item = iter(list_item); if (list_item == list_head) { diff --git a/src/keyboard.c b/src/keyboard.c index 93d91071..2d44a9db 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -39,18 +39,18 @@ keyboard_modifiers_notify(struct wl_listener *listener, void *data) struct seat *seat = wl_container_of(listener, seat, keyboard_modifiers); struct server *server = seat->server; - if (server->cycle_view || seat->workspace_osd_shown_by_modifier) { + if (server->osd_state.cycle_view || seat->workspace_osd_shown_by_modifier) { struct wlr_keyboard_key_event *event = data; struct wlr_keyboard *keyboard = &seat->keyboard_group->keyboard; if (event->state == WL_KEYBOARD_KEY_STATE_RELEASED && !keyboard_any_modifiers_pressed(keyboard)) { - if (server->cycle_view) { + if (server->osd_state.cycle_view) { /* end cycle */ desktop_focus_and_activate_view(&server->seat, - server->cycle_view); - desktop_move_to_front(server->cycle_view); - server->cycle_view = NULL; + server->osd_state.cycle_view); + desktop_move_to_front(server->osd_state.cycle_view); + /* osd_finish() additionally resets cycle_view to NULL */ osd_finish(server); } if (seat->workspace_osd_shown_by_modifier) { @@ -145,12 +145,13 @@ handle_compositor_keybindings(struct wl_listener *listener, } } - if (server->cycle_view) { + if (server->osd_state.cycle_view) { if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { for (int i = 0; i < nsyms; i++) { if (syms[i] == XKB_KEY_Escape) { /* cancel */ - server->cycle_view = NULL; + osd_preview_restore(server); + /* osd_finish() additionally resets cycle_view to NULL */ osd_finish(server); return true; } @@ -168,8 +169,8 @@ handle_compositor_keybindings(struct wl_listener *listener, enum lab_cycle_dir dir = backwards ? LAB_CYCLE_DIR_BACKWARD : LAB_CYCLE_DIR_FORWARD; - server->cycle_view = desktop_cycle_view(server, - server->cycle_view, dir); + server->osd_state.cycle_view = desktop_cycle_view(server, + server->osd_state.cycle_view, dir); osd_update(server); } } diff --git a/src/osd.c b/src/osd.c index 529818a5..da6f5028 100644 --- a/src/osd.c +++ b/src/osd.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #include "config.h" +#include #include #include #include @@ -8,6 +9,7 @@ #include "common/buf.h" #include "common/font.h" #include "common/graphic-helpers.h" +#include "common/scene-helpers.h" #include "config/rcxml.h" #include "labwc.h" #include "theme.h" @@ -88,7 +90,7 @@ osd_update_preview_outlines(struct view *view) { /* Create / Update preview outline tree */ struct server *server = view->server; - struct multi_rect *rect = view->server->osd_preview_outline; + struct multi_rect *rect = view->server->osd_state.preview_outline; if (!rect) { int line_width = server->theme->osd_border_width; float *colors[] = { @@ -98,7 +100,7 @@ osd_update_preview_outlines(struct view *view) }; rect = multi_rect_create(&server->scene->tree, colors, line_width); wlr_scene_node_place_above(&rect->tree->node, &server->menu_tree->node); - server->osd_preview_outline = rect; + server->osd_state.preview_outline = rect; } struct wlr_box geo = ssd_max_extents(view); @@ -106,21 +108,118 @@ osd_update_preview_outlines(struct view *view) wlr_scene_node_set_position(&rect->tree->node, geo.x, geo.y); } +void +osd_on_view_destroy(struct view *view) +{ + assert(view); + struct osd_state *osd_state = &view->server->osd_state; + + if (!osd_state->cycle_view) { + /* OSD not active, no need for clean up */ + return; + } + + if (osd_state->cycle_view == view) { + /* + * If we are the current OSD selected view, cycle + * to the next because we are dying. + */ + osd_state->cycle_view = desktop_cycle_view(view->server, + osd_state->cycle_view, LAB_CYCLE_DIR_BACKWARD); + + /* + * If we cycled back to ourselves, then we have no more windows. + * Just close the OSD for good. + */ + if (osd_state->cycle_view == view || !osd_state->cycle_view) { + /* osd_finish() additionally resets cycle_view to NULL */ + osd_finish(view->server); + } + } + + if (view->scene_tree) { + struct wlr_scene_node *node = &view->scene_tree->node; + if (osd_state->preview_anchor == node) { + /* + * If we are the anchor for the current OSD selected view, + * replace the anchor with the node before us. + */ + osd_state->preview_anchor = lab_wlr_scene_get_prev_node(node); + } + } + + if (osd_state->cycle_view) { + /* Update the OSD to reflect the view has now gone. */ + osd_update(view->server); + } +} + void osd_finish(struct server *server) { + server->osd_state.cycle_view = NULL; + server->osd_state.preview_node = NULL; + server->osd_state.preview_anchor = NULL; + struct output *output; wl_list_for_each(output, &server->outputs, link) { destroy_osd_nodes(output); wlr_scene_node_set_enabled(&output->osd_tree->node, false); } - if (server->osd_preview_outline) { + if (server->osd_state.preview_outline) { /* Destroy the whole multi_rect so we can easily react to new themes */ - wlr_scene_node_destroy(&server->osd_preview_outline->tree->node); - server->osd_preview_outline = NULL; + wlr_scene_node_destroy(&server->osd_state.preview_outline->tree->node); + server->osd_state.preview_outline = NULL; } } +void +osd_preview_restore(struct server *server) +{ + struct osd_state *osd_state = &server->osd_state; + if (osd_state->preview_node) { + if (osd_state->preview_anchor) { + wlr_scene_node_place_above(osd_state->preview_node, + osd_state->preview_anchor); + } else { + /* Selected view was the first node */ + wlr_scene_node_lower_to_bottom(osd_state->preview_node); + } + + /* Node was disabled / minimized before, disable again */ + if (!osd_state->preview_was_enabled) { + wlr_scene_node_set_enabled(osd_state->preview_node, false); + } + osd_state->preview_node = NULL; + osd_state->preview_anchor = NULL; + } +} + +static void +preview_cycled_view(struct view *view) +{ + assert(view); + assert(view->scene_tree); + struct osd_state *osd_state = &view->server->osd_state; + + /* Move previous selected node back to its original place */ + osd_preview_restore(view->server); + + /* Remember the sibling right before the selected node */ + osd_state->preview_node = &view->scene_tree->node; + osd_state->preview_anchor = lab_wlr_scene_get_prev_node( + osd_state->preview_node); + + /* Store node enabled / minimized state and force-enable if disabled */ + osd_state->preview_was_enabled = osd_state->preview_node->enabled; + if (!osd_state->preview_was_enabled) { + wlr_scene_node_set_enabled(osd_state->preview_node, true); + } + + /* Finally raise selected node to the top */ + wlr_scene_node_raise_to_top(osd_state->preview_node); +} + void osd_update(struct server *server) { @@ -185,7 +284,7 @@ osd_update(struct server *server) if (!isfocusable(view)) { continue; } - if (view == server->cycle_view) { + if (view == server->osd_state.cycle_view) { set_cairo_color(cairo, theme->osd_label_text_color); cairo_rectangle(cairo, OSD_BORDER_WIDTH, y, OSD_ITEM_WIDTH, OSD_ITEM_HEIGHT); @@ -292,4 +391,8 @@ osd_update(struct server *server) wlr_scene_node_set_enabled(&output->osd_tree->node, true); } free(buf.buf); + + if (rc.cycle_preview_contents) { + preview_cycled_view(server->osd_state.cycle_view); + } } diff --git a/src/view.c b/src/view.c index 843c5c98..68ccd17b 100644 --- a/src/view.c +++ b/src/view.c @@ -3,6 +3,7 @@ #include #include #include +#include "common/scene-helpers.h" #include "labwc.h" #include "ssd.h" #include "menu/menu.h" @@ -808,28 +809,7 @@ view_destroy(struct view *view) server->focused_view = NULL; } - if (server->cycle_view == view) { - /* - * If we are the current OSD selected view, cycle - * to the next because we are dying. - */ - server->cycle_view = desktop_cycle_view(server, - server->cycle_view, LAB_CYCLE_DIR_BACKWARD); - - /* - * If we cycled back to ourselves, then we have no windows. - * just remove it and close the OSD for good. - */ - if (server->cycle_view == view || !server->cycle_view) { - server->cycle_view = NULL; - osd_finish(server); - } - } - - if (server->cycle_view) { - /* Update the OSD to reflect the view has now gone. */ - osd_update(server); - } + osd_on_view_destroy(view); if (view->scene_tree) { ssd_destroy(view);