From ff94591578f03440212e8ca01ae22fd27d24cdbc Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Sat, 24 Jan 2026 14:26:30 +0000 Subject: [PATCH] action: support `NextWindow` option window="root|all" This enables all windows, not just topmost (root) ones, to be included in the window-switcher. This is helpful when cycling through Gtk applications with open dialogs because although these child windows will always be shown on top of their parents, they do not get keyboard focus with the default configuration. This also applices to `PreviousWindow`. Fixes: #3332 Suggested-by: @airtower-luna --- docs/labwc-actions.5.scd | 8 ++++++-- include/config/types.h | 5 +++++ include/cycle.h | 1 + src/action.c | 13 +++++++++++++ src/cycle/cycle.c | 6 ++++-- 5 files changed, 29 insertions(+), 4 deletions(-) diff --git a/docs/labwc-actions.5.scd b/docs/labwc-actions.5.scd index fe468cd1..33be0c26 100644 --- a/docs/labwc-actions.5.scd +++ b/docs/labwc-actions.5.scd @@ -125,8 +125,8 @@ Actions are used in menus and keyboard/mouse bindings. Resize and move the active window back to its untiled or unmaximized position if it had been maximized or tiled to a direction or region. -**++ -** +**++ +** Cycle focus to next/previous window, respectively. Default keybinds for NextWindow and PreviousWindow are Alt-Tab and @@ -145,6 +145,10 @@ Actions are used in menus and keyboard/mouse bindings. This determines whether to cycle through all windows or only windows of the same application as the currently focused window. Default is "all". + *window* [root|all] + This determines whether to cycle through only topmost (root) windows or + all windows including child windows such as dialogs. Default is "root". + ** Re-load configuration and theme files. diff --git a/include/config/types.h b/include/config/types.h index fc293cd8..23e32a4f 100644 --- a/include/config/types.h +++ b/include/config/types.h @@ -133,4 +133,9 @@ enum cycle_app_id_filter { CYCLE_APP_ID_CURRENT, }; +enum cycle_window_filter { + CYCLE_WINDOW_ROOT, + CYCLE_WINDOW_ALL, +}; + #endif /* LABWC_CONFIG_TYPES_H */ diff --git a/include/cycle.h b/include/cycle.h index c6e42810..5c2adf10 100644 --- a/include/cycle.h +++ b/include/cycle.h @@ -49,6 +49,7 @@ struct cycle_filter { enum cycle_workspace_filter workspace; enum cycle_output_filter output; enum cycle_app_id_filter app_id; + enum cycle_window_filter window; }; struct cycle_state { diff --git a/src/action.c b/src/action.c index 7f974917..b86fb1a1 100644 --- a/src/action.c +++ b/src/action.c @@ -403,6 +403,17 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char } goto cleanup; } + if (!strcasecmp(argument, "window")) { + if (!strcasecmp(content, "root")) { + action_arg_add_int(action, argument, CYCLE_WINDOW_ROOT); + } else if (!strcasecmp(content, "all")) { + action_arg_add_int(action, argument, CYCLE_WINDOW_ALL); + } else { + wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)", + action_names[action->type], argument, content); + } + goto cleanup; + } break; case ACTION_TYPE_SHOW_MENU: if (!strcmp(argument, "menu")) { @@ -1174,6 +1185,8 @@ run_action(struct view *view, struct server *server, struct action *action, CYCLE_OUTPUT_ALL), .app_id = action_get_int(action, "identifier", CYCLE_APP_ID_ALL), + .window = action_get_int(action, "window", + CYCLE_WINDOW_ROOT), }; if (server->input_mode == LAB_INPUT_STATE_CYCLE) { cycle_step(server, dir); diff --git a/src/cycle/cycle.c b/src/cycle/cycle.c index 32a7438f..7bbf7d55 100644 --- a/src/cycle/cycle.c +++ b/src/cycle/cycle.c @@ -331,8 +331,10 @@ static bool init_cycle(struct server *server, struct cycle_filter filter) { enum lab_view_criteria criteria = - LAB_VIEW_CRITERIA_NO_SKIP_WINDOW_SWITCHER - | LAB_VIEW_CRITERIA_ROOT_TOPLEVEL; + LAB_VIEW_CRITERIA_NO_SKIP_WINDOW_SWITCHER; + if (filter.window == CYCLE_WINDOW_ROOT) { + criteria |= LAB_VIEW_CRITERIA_ROOT_TOPLEVEL; + } if (filter.workspace == CYCLE_WORKSPACE_CURRENT) { criteria |= LAB_VIEW_CRITERIA_CURRENT_WORKSPACE; }