diff --git a/include/cycle.h b/include/cycle.h index d4ac72fc..aaecff50 100644 --- a/include/cycle.h +++ b/include/cycle.h @@ -87,7 +87,7 @@ struct cycle_osd_impl { * Create a scene-tree of OSD for an output. * This sets output->cycle_osd.{items,tree}. */ - void (*create)(struct output *output, struct wl_array *views); + void (*create)(struct output *output); /* * Update output->cycle_osd.tree to highlight * server->cycle_state.selected_view. diff --git a/include/labwc.h b/include/labwc.h index abe07a49..40bff876 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -303,6 +303,7 @@ struct server { /* Set when in cycle (alt-tab) mode */ struct cycle_state { struct view *selected_view; + struct wl_list views; bool preview_was_shaded; bool preview_was_enabled; struct wlr_scene_node *preview_node; diff --git a/include/view.h b/include/view.h index 9ad11cd9..d8c67e42 100644 --- a/include/view.h +++ b/include/view.h @@ -134,6 +134,9 @@ struct view { const struct view_impl *impl; struct wl_list link; + /* This is cleared when the view is not in the cycle list */ + struct wl_list cycle_link; + /* * The primary output that the view is displayed on. Specifically: * @@ -384,15 +387,6 @@ struct view *view_next(struct wl_list *head, struct view *view, struct view *view_prev(struct wl_list *head, struct view *view, enum lab_view_criteria criteria); -/* - * Same as `view_next()` except that they iterate one whole cycle rather than - * stopping at the list-head - */ -struct view *view_next_no_head_stop(struct wl_list *head, struct view *from, - enum lab_view_criteria criteria); -struct view *view_prev_no_head_stop(struct wl_list *head, struct view *from, - enum lab_view_criteria criteria); - /** * view_array_append() - Append views that match criteria to array * @server: server context diff --git a/src/cycle/cycle.c b/src/cycle/cycle.c index 85d37faa..292e6436 100644 --- a/src/cycle/cycle.c +++ b/src/cycle/cycle.c @@ -4,8 +4,8 @@ #include #include #include -#include "common/array.h" #include "common/lab-scene-rect.h" +#include "common/list.h" #include "common/scene-helpers.h" #include "config/rcxml.h" #include "labwc.h" @@ -48,36 +48,36 @@ update_preview_outlines(struct view *view) wlr_scene_node_set_position(&rect->tree->node, geo.x, geo.y); } -/* - * Returns the view to select next in the window switcher. - * If !start_view, the second focusable view is returned. - */ +/* Returns the view to select next in the window switcher. */ static struct view * -get_next_selected_view(struct server *server, struct view *start_view, - enum lab_cycle_dir dir) +get_next_selected_view(struct server *server, enum lab_cycle_dir dir) { - struct view *(*iter)(struct wl_list *head, struct view *view, - enum lab_view_criteria criteria); - bool forwards = dir == LAB_CYCLE_DIR_FORWARD; - iter = forwards ? view_next_no_head_stop : view_prev_no_head_stop; + struct cycle_state *cycle = &server->cycle; + assert(cycle->selected_view); + assert(!wl_list_empty(&server->cycle.views)); - enum lab_view_criteria criteria = rc.window_switcher.criteria; - - /* - * Views are listed in stacking order, topmost first. Usually the - * topmost view is already focused, so when iterating in the forward - * direction we pre-select the view second from the top: - * - * View #1 (on top, currently focused) - * View #2 (pre-selected) - * View #3 - * ... - */ - if (!start_view && forwards) { - start_view = iter(&server->views, NULL, criteria); + struct wl_list *link; + if (dir == LAB_CYCLE_DIR_FORWARD) { + link = cycle->selected_view->cycle_link.next; + if (link == &server->cycle.views) { + link = link->next; + } + } else { + link = cycle->selected_view->cycle_link.prev; + if (link == &server->cycle.views) { + link = link->prev; + } } + struct view *view = wl_container_of(link, view, cycle_link); + return view; +} - return iter(&server->views, start_view, criteria); +static struct view * +get_first_view(struct wl_list *views) +{ + assert(!wl_list_empty(views)); + struct view *view = wl_container_of(views->next, view, cycle_link); + return view; } void @@ -90,11 +90,25 @@ cycle_reinitialize(struct server *server) return; } + struct view *selected_view = cycle->selected_view; + struct view *selected_view_prev = + get_next_selected_view(server, LAB_CYCLE_DIR_BACKWARD); + destroy_cycle(server); if (init_cycle(server)) { - /* TODO: try to select the same view */ - cycle->selected_view = get_next_selected_view(server, NULL, - LAB_CYCLE_DIR_FORWARD); + /* + * Preserve the selected view (or its previous view) if it's + * still in the cycle list + */ + if (selected_view->cycle_link.next) { + cycle->selected_view = selected_view; + } else if (selected_view_prev->cycle_link.next) { + cycle->selected_view = selected_view_prev; + } else { + /* should be unreachable */ + wlr_log(WLR_ERROR, "could not find view to select"); + cycle->selected_view = get_first_view(&server->cycle.views); + } update_cycle(server); } else { /* Failed to re-init window switcher, exit */ @@ -148,8 +162,16 @@ cycle_begin(struct server *server, enum lab_cycle_dir direction) return; } - server->cycle.selected_view = get_next_selected_view(server, - server->cycle.selected_view, direction); + struct view *active_view = server->active_view; + if (active_view && active_view->cycle_link.next) { + /* Select the active view it's in the cycle list */ + server->cycle.selected_view = active_view; + } else { + /* Otherwise, select the first view in the cycle list */ + server->cycle.selected_view = get_first_view(&server->cycle.views); + } + /* Pre-select the next view in the given direction */ + server->cycle.selected_view = get_next_selected_view(server, direction); seat_focus_override_begin(&server->seat, LAB_INPUT_STATE_CYCLE, LAB_CURSOR_DEFAULT); @@ -164,8 +186,7 @@ cycle_step(struct server *server, enum lab_cycle_dir direction) { assert(server->input_mode == LAB_INPUT_STATE_CYCLE); - server->cycle.selected_view = get_next_selected_view(server, - server->cycle.selected_view, direction); + server->cycle.selected_view = get_next_selected_view(server, direction); update_cycle(server); } @@ -246,12 +267,12 @@ get_osd_impl(void) } static void -create_osd_on_output(struct output *output, struct wl_array *views) +create_osd_on_output(struct output *output) { if (!output_is_usable(output)) { return; } - get_osd_impl()->create(output, views); + get_osd_impl()->create(output); assert(output->cycle_osd.tree); } @@ -259,12 +280,12 @@ create_osd_on_output(struct output *output, struct wl_array *views) static bool init_cycle(struct server *server) { - struct wl_array views; - wl_array_init(&views); - view_array_append(server, &views, rc.window_switcher.criteria); - if (wl_array_len(&views) <= 0) { + struct view *view; + for_each_view(view, &server->views, rc.window_switcher.criteria) { + wl_list_append(&server->cycle.views, &view->cycle_link); + } + if (wl_list_empty(&server->cycle.views)) { wlr_log(WLR_DEBUG, "no views to switch between"); - wl_array_release(&views); return false; } @@ -274,12 +295,12 @@ init_cycle(struct server *server) case CYCLE_OSD_OUTPUT_ALL: { struct output *output; wl_list_for_each(output, &server->outputs, link) { - create_osd_on_output(output, &views); + create_osd_on_output(output); } break; } case CYCLE_OSD_OUTPUT_POINTER: - create_osd_on_output(output_nearest_to_cursor(server), &views); + create_osd_on_output(output_nearest_to_cursor(server)); break; case CYCLE_OSD_OUTPUT_KEYBOARD: { struct output *output; @@ -289,13 +310,12 @@ init_cycle(struct server *server) /* Fallback to pointer, if there is no active_view */ output = output_nearest_to_cursor(server); } - create_osd_on_output(output, &views); + create_osd_on_output(output); break; } } } - wl_array_release(&views); return true; } @@ -349,5 +369,11 @@ destroy_cycle(struct server *server) server->cycle.preview_outline = NULL; } + struct view *view, *tmp; + wl_list_for_each_safe(view, tmp, &server->cycle.views, cycle_link) { + wl_list_remove(&view->cycle_link); + view->cycle_link = (struct wl_list){0}; + } + server->cycle.selected_view = NULL; } diff --git a/src/cycle/osd-classic.c b/src/cycle/osd-classic.c index f954701e..67cfb8db 100644 --- a/src/cycle/osd-classic.c +++ b/src/cycle/osd-classic.c @@ -4,11 +4,11 @@ #include #include #include -#include "common/array.h" #include "common/buf.h" #include "common/font.h" #include "common/lab-scene-rect.h" #include "common/list.h" +#include "common/mem.h" #include "common/string-helpers.h" #include "config/rcxml.h" #include "cycle.h" @@ -18,6 +18,7 @@ #include "scaled-buffer/scaled-font-buffer.h" #include "scaled-buffer/scaled-icon-buffer.h" #include "theme.h" +#include "view.h" #include "workspaces.h" struct cycle_osd_classic_item { @@ -76,7 +77,7 @@ create_fields_scene(struct server *server, struct view *view, } static void -cycle_osd_classic_create(struct output *output, struct wl_array *views) +cycle_osd_classic_create(struct output *output) { assert(!output->cycle_osd.tree && wl_list_empty(&output->cycle_osd.items)); @@ -87,6 +88,7 @@ cycle_osd_classic_create(struct output *output, struct wl_array *views) int padding = theme->osd_border_width + switcher_theme->padding; bool show_workspace = wl_list_length(&rc.workspace_config.workspaces) > 1; const char *workspace_name = server->workspaces.current->name; + int nr_views = wl_list_length(&server->cycle.views); struct wlr_box output_box; wlr_output_layout_get_box(server->output_layout, output->wlr_output, @@ -96,7 +98,7 @@ cycle_osd_classic_create(struct output *output, struct wl_array *views) if (switcher_theme->width_is_percent) { w = output_box.width * switcher_theme->width / 100; } - int h = wl_array_len(views) * switcher_theme->item_height + 2 * padding; + int h = nr_views * switcher_theme->item_height + 2 * padding; if (show_workspace) { /* workspace indicator */ h += switcher_theme->item_height; @@ -155,11 +157,11 @@ cycle_osd_classic_create(struct output *output, struct wl_array *views) } /* Draw text for each node */ - struct view **view; - wl_array_for_each(view, views) { + struct view *view; + wl_list_for_each(view, &server->cycle.views, cycle_link) { struct cycle_osd_classic_item *item = znew(*item); wl_list_append(&output->cycle_osd.items, &item->base.link); - item->base.view = *view; + item->base.view = view; item->base.tree = wlr_scene_tree_create(output->cycle_osd.tree); node_descriptor_create(&item->base.tree->node, LAB_NODE_CYCLE_OSD_ITEM, NULL, item); @@ -207,9 +209,9 @@ cycle_osd_classic_create(struct output *output, struct wl_array *views) w - 2 * padding, switcher_theme->item_height, (float[4]) {0}); wlr_scene_node_set_position(&hitbox->node, padding, y); - create_fields_scene(server, *view, item->normal_tree, + create_fields_scene(server, view, item->normal_tree, text_color, bg_color, field_widths_sum, x, y); - create_fields_scene(server, *view, item->active_tree, + create_fields_scene(server, view, item->active_tree, text_color, active_bg_color, field_widths_sum, x, y); y += switcher_theme->item_height; diff --git a/src/cycle/osd-thumbnail.c b/src/cycle/osd-thumbnail.c index 5dd157c1..2245d1ad 100644 --- a/src/cycle/osd-thumbnail.c +++ b/src/cycle/osd-thumbnail.c @@ -5,11 +5,11 @@ #include #include #include "config/rcxml.h" -#include "common/array.h" #include "common/box.h" #include "common/buf.h" #include "common/lab-scene-rect.h" #include "common/list.h" +#include "common/mem.h" #include "cycle.h" #include "labwc.h" #include "node.h" @@ -226,7 +226,7 @@ get_items_geometry(struct output *output, struct theme *theme, } static void -cycle_osd_thumbnail_create(struct output *output, struct wl_array *views) +cycle_osd_thumbnail_create(struct output *output) { assert(!output->cycle_osd.tree && wl_list_empty(&output->cycle_osd.items)); @@ -238,17 +238,17 @@ cycle_osd_thumbnail_create(struct output *output, struct wl_array *views) output->cycle_osd.tree = wlr_scene_tree_create(output->cycle_osd_tree); - int nr_views = wl_array_len(views); + int nr_views = wl_list_length(&server->cycle.views); assert(nr_views > 0); int nr_rows, nr_cols; get_items_geometry(output, theme, nr_views, &nr_rows, &nr_cols); /* items */ - struct view **view; + struct view *view; int index = 0; - wl_array_for_each(view, views) { + wl_list_for_each(view, &server->cycle.views, cycle_link) { struct cycle_osd_thumbnail_item *item = create_item_scene( - output->cycle_osd.tree, *view, output); + output->cycle_osd.tree, view, output); if (!item) { break; } diff --git a/src/server.c b/src/server.c index 11037a48..9f271b10 100644 --- a/src/server.c +++ b/src/server.c @@ -549,6 +549,7 @@ server_init(struct server *server) wl_list_init(&server->views); wl_list_init(&server->unmanaged_surfaces); + wl_list_init(&server->cycle.views); server->scene = wlr_scene_create(); if (!server->scene) { diff --git a/src/view.c b/src/view.c index d56c3fea..cc16536f 100644 --- a/src/view.c +++ b/src/view.c @@ -345,48 +345,6 @@ view_prev(struct wl_list *head, struct view *view, enum lab_view_criteria criter return NULL; } -struct view * -view_next_no_head_stop(struct wl_list *head, struct view *from, - enum lab_view_criteria criteria) -{ - assert(head); - - struct wl_list *elm = from ? &from->link : head; - - struct wl_list *end = elm; - for (elm = elm->next; elm != end; elm = elm->next) { - if (elm == head) { - continue; - } - struct view *view = wl_container_of(elm, view, link); - if (matches_criteria(view, criteria)) { - return view; - } - } - return from; -} - -struct view * -view_prev_no_head_stop(struct wl_list *head, struct view *from, - enum lab_view_criteria criteria) -{ - assert(head); - - struct wl_list *elm = from ? &from->link : head; - - struct wl_list *end = elm; - for (elm = elm->prev; elm != end; elm = elm->prev) { - if (elm == head) { - continue; - } - struct view *view = wl_container_of(elm, view, link); - if (matches_criteria(view, criteria)) { - return view; - } - } - return from; -} - void view_array_append(struct server *server, struct wl_array *views, enum lab_view_criteria criteria)