From 9962f44d5511c98cce0153e9c8bb8ab682a43195 Mon Sep 17 00:00:00 2001 From: Maik Broemme Date: Thu, 4 Dec 2025 16:51:40 +0100 Subject: [PATCH] window-switcher: add `age` order to window cycling - Add a new configuration option to control the window switcher traversal order. - Document cycle windows behavior with new option `order` in `` --- docs/labwc-config.5.scd | 28 +++++++++++++++++++++++++++- include/config/rcxml.h | 1 + include/config/types.h | 5 +++++ include/labwc.h | 1 + include/view.h | 1 + src/config/rcxml.c | 7 +++++++ src/cycle/cycle.c | 20 +++++++++++++++++++- src/xdg.c | 1 + src/xwayland.c | 1 + 9 files changed, 63 insertions(+), 2 deletions(-) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index fa4f7e58..318fb2c7 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -349,7 +349,7 @@ this is for compatibility with Openbox. ``` -** +** *preview* [yes|no] Preview the contents of the selected window when switching between windows. Default is yes. @@ -363,6 +363,32 @@ this is for compatibility with Openbox. *unshade* [yes|no] Temporarily unshade windows when switching between them and permanently unshade on the final selection. Default is yes. + *order* [focus|age] Cycle windows behavior. + "focus" cycles by focus history. The list is sorted by the last time + each window had focus - most recently focused first. + + - Using the window switcher (started with Alt+Tab by default) moves to + the next most recently used window - the classic behavior where you + typically toggle between the last two windows, then move further + back in time. + + - If you focus a window, it jumps to the front of this list + immediately. + + "age" cycles by creation/open order - the same stable order you + typically see in the taskbar. + + - New windows are appended at the end of the list. + + - Using the window switcher (started with Alt+Tab by default) moves to + the next window in that fixed open order; backward goes the other + way. + + - The order doesn't change when you focus a window, so repeated cycles + are predictable. + + Default is "focus". + ** *show* [yes|no] Draw the OnScreenDisplay when switching between windows. Default is yes. diff --git a/include/config/rcxml.h b/include/config/rcxml.h index 55c0f877..4955b7e7 100644 --- a/include/config/rcxml.h +++ b/include/config/rcxml.h @@ -185,6 +185,7 @@ struct rcxml { enum cycle_osd_style style; enum cycle_osd_output_criteria output_criteria; char *thumbnail_label_format; + enum window_switcher_order order; } window_switcher; struct wl_list window_rules; /* struct window_rule.link */ diff --git a/include/config/types.h b/include/config/types.h index 757796a6..99c5929e 100644 --- a/include/config/types.h +++ b/include/config/types.h @@ -107,6 +107,11 @@ enum lab_window_type { LAB_WINDOW_TYPE_LEN }; +enum window_switcher_order { + WINDOW_SWITCHER_ORDER_FOCUS, + WINDOW_SWITCHER_ORDER_AGE, +}; + enum cycle_osd_style { CYCLE_OSD_STYLE_CLASSIC, CYCLE_OSD_STYLE_THUMBNAIL, diff --git a/include/labwc.h b/include/labwc.h index 40bff876..71102b13 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -188,6 +188,7 @@ struct server { struct wl_listener xdg_toplevel_icon_set_icon; struct wl_list views; + uint64_t next_view_creation_iid; struct wl_list unmanaged_surfaces; struct seat seat; diff --git a/include/view.h b/include/view.h index 2f8aac85..b36eecef 100644 --- a/include/view.h +++ b/include/view.h @@ -174,6 +174,7 @@ struct view { bool mapped; bool been_mapped; + uint64_t creation_iid; enum lab_ssd_mode ssd_mode; enum ssd_preference ssd_preference; bool shaded; diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 84054e82..1cf3392a 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -1233,6 +1233,12 @@ entry(xmlNode *node, char *nodename, char *content) wlr_log(WLR_ERROR, "Invalid windowSwitcher output %s: " "should be one of all|focused|cursor", content); } + } else if (!strcasecmp(nodename, "order.windowSwitcher")) { + if (!strcasecmp(content, "focus")) { + rc.window_switcher.order = WINDOW_SWITCHER_ORDER_FOCUS; + } else if (!strcasecmp(content, "age")) { + rc.window_switcher.order = WINDOW_SWITCHER_ORDER_AGE; + } /* The following two are for backward compatibility only. */ } else if (!strcasecmp(nodename, "show.windowSwitcher")) { @@ -1477,6 +1483,7 @@ rcxml_init(void) rc.window_switcher.criteria = LAB_VIEW_CRITERIA_CURRENT_WORKSPACE | LAB_VIEW_CRITERIA_ROOT_TOPLEVEL | LAB_VIEW_CRITERIA_NO_SKIP_WINDOW_SWITCHER; + rc.window_switcher.order = WINDOW_SWITCHER_ORDER_FOCUS; rc.resize_indicator = LAB_RESIZE_INDICATOR_NEVER; rc.resize_draw_contents = true; diff --git a/src/cycle/cycle.c b/src/cycle/cycle.c index 8eabaf6c..15354938 100644 --- a/src/cycle/cycle.c +++ b/src/cycle/cycle.c @@ -276,13 +276,31 @@ create_osd_on_output(struct output *output) assert(output->cycle_osd.tree); } +static void +insert_view_ordered_by_age(struct wl_list *views, struct view *new_view) +{ + struct wl_list *link = views; + struct view *view; + wl_list_for_each(view, views, cycle_link) { + if (view->creation_iid >= new_view->creation_iid) { + break; + } + link = &view->cycle_link; + } + wl_list_insert(link, &new_view->cycle_link); +} + /* Return false on failure */ static bool init_cycle(struct server *server) { struct view *view; for_each_view(view, &server->views, rc.window_switcher.criteria) { - wl_list_append(&server->cycle.views, &view->cycle_link); + if (rc.window_switcher.order == WINDOW_SWITCHER_ORDER_AGE) { + insert_view_ordered_by_age(&server->cycle.views, view); + } else { + 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"); diff --git a/src/xdg.c b/src/xdg.c index 668dfe45..cb6cb70e 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -1085,6 +1085,7 @@ handle_new_xdg_toplevel(struct wl_listener *listener, void *data) CONNECT_SIGNAL(xdg_surface, xdg_toplevel_view, new_popup); wl_list_insert(&server->views, &view->link); + view->creation_iid = server->next_view_creation_iid++; } static void diff --git a/src/xwayland.c b/src/xwayland.c index 564fa3e2..d9f0003c 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -1036,6 +1036,7 @@ xwayland_view_create(struct server *server, CONNECT_SIGNAL(xsurface, xwayland_view, map_request); wl_list_insert(&view->server->views, &view->link); + view->creation_iid = view->server->next_view_creation_iid++; if (xsurface->surface) { handle_associate(&xwayland_view->associate, NULL);