Add a new configuration option to control the window switcher traversal order.
`order="focus"` cycling is convenient for quick toggling, but some users - me as well -
prefer a stable taskbar-like order which can now be achieved with `order="age"`.
</windowSwitcher>
```
-*<windowSwitcher preview="" outlines="" allWorkspaces="" unshade="">*
+*<windowSwitcher preview="" outlines="" allWorkspaces="" unshade="" order="">*
*preview* [yes|no] Preview the contents of the selected window when
switching between windows. Default is yes.
*unshade* [yes|no] Temporarily unshade windows when switching between
them and permanently unshade on the final selection. Default is yes.
+ *order* [focus|age] The order in which windows are cycled. *focus* cycles by
+ recent focus history, starting with the previously focused window. *age* cycles
+ by creation/open order, a stable taskbar-style ordering that doesn’t change on
+ focus. Default is *focus*.
+
*<windowSwitcher><osd show="" style="" output="" thumbnailLabelFormat="" />*
*show* [yes|no] Draw the OnScreenDisplay when switching between
windows. Default is yes.
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 */
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,
struct wl_listener xdg_toplevel_icon_set_icon;
struct wl_list views;
+ uint64_t next_view_creation_id;
struct wl_list unmanaged_surfaces;
struct seat seat;
bool mapped;
bool been_mapped;
+ uint64_t creation_id;
enum lab_ssd_mode ssd_mode;
enum ssd_preference ssd_preference;
bool shaded;
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;
+ } else {
+ wlr_log(WLR_ERROR, "Invalid windowSwitcher order %s: "
+ "should be one of focus|age", content);
+ }
/* The following two are for backward compatibility only. */
} else if (!strcasecmp(nodename, "show.windowSwitcher")) {
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;
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_id >= new_view->creation_id) {
+ 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");
CONNECT_SIGNAL(xdg_surface, xdg_toplevel_view, new_popup);
wl_list_insert(&server->views, &view->link);
+ view->creation_id = server->next_view_creation_id++;
}
static void
CONNECT_SIGNAL(xsurface, xwayland_view, map_request);
wl_list_insert(&view->server->views, &view->link);
+ view->creation_id = view->server->next_view_creation_id++;
if (xsurface->surface) {
handle_associate(&xwayland_view->associate, NULL);