From 7fde7ed2cc50309a6ee18f202a9f9ff0694df2f6 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Fri, 18 Aug 2023 22:18:59 +0100 Subject: [PATCH] view: add for_each_view() Helped-by: @Consolatis and @heroin-moose (by a significant amount) --- include/view.h | 37 ++++++++++++++++++-- scripts/checkpatch.pl | 5 +-- src/view.c | 81 ++++++++++++++++++++++++++----------------- 3 files changed, 87 insertions(+), 36 deletions(-) diff --git a/include/view.h b/include/view.h index 80819135..9e5ddd62 100644 --- a/include/view.h +++ b/include/view.h @@ -169,23 +169,54 @@ struct xdg_toplevel_view { }; enum lab_view_criteria { - LAB_VIEW_CRITERIA_ALL = 0, + LAB_VIEW_CRITERIA_NONE = 0, LAB_VIEW_CRITERIA_CURRENT_WORKSPACE = 1 << 0, LAB_VIEW_CRITERIA_NO_ALWAYS_ON_TOP = 1 << 1, LAB_VIEW_CRITERIA_NO_SKIP_WINDOW_SWITCHER = 1 << 2, }; /** - * view_array_append - append views that match criteria to array + * for_each_view() - iterate over all views which match criteria + * @view: Iterator. + * @head: Head of list to iterate over. + * @criteria: Criteria to match against. + * Example: + * struct view *view; + * for_each_view(view, &server->views, LAB_VIEW_CRITERIA_NONE) { + * printf("%s\n", view_get_string_prop(view, "app_id")); + * } + */ +#define for_each_view(view, head, criteria) \ + for (view = view_next(head, NULL, criteria); \ + view; \ + view = view_next(head, view, criteria)) + +/** + * view_next() - Get next view which matches criteria. + * @head: Head of list to iterate over. + * @view: Current view from which to find the next one. If NULL is provided as + * the view argument, the start of the list will be used. + * @criteria: Criteria to match against. + * + * Returns NULL if there are no views matching the criteria. + */ +struct view *view_next(struct wl_list *head, struct view *view, + enum lab_view_criteria criteria); + +/** + * view_array_append() - Append views that match criteria to array * @server: server context * @views: arrays to append to * @criteria: criteria to match against * + * This function is useful in cases where the calling function may change the + * stacking order or where it needs to iterate over the views multiple times, + * for example to get the number of views before processing them. + * * Note: This array has a very short shelf-life so it is intended to be used * with a single-use-throw-away approach. * * Example usage: - * * struct view **view; * struct wl_array views; * wl_array_init(&views); diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 30b31343..98cba37f 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -5526,10 +5526,11 @@ sub process { # because (as opposed to Linux coding style) we use # braces for single statement blocks. # - # include/ssd-internal.h contains a macro that we can't - # deal with, so ignore that + # We ignore a couple of header-files which contain + # macros that we cannot deal with. # if ($starts_with_if_while_etc && !length($s) + && $filename ne "include/view.h" && $filename ne "include/ssd-internal.h") { CHK("BRACES", "[labwc-custom] open brace { expected after if/while/for/switch - even with single statement blocks"); } diff --git a/src/view.c b/src/view.c index b6997eb6..aa696755 100644 --- a/src/view.c +++ b/src/view.c @@ -19,44 +19,63 @@ #define LAB_FALLBACK_WIDTH 640 #define LAB_FALLBACK_HEIGHT 480 +static bool +matches_criteria(struct view *view, enum lab_view_criteria criteria) +{ + if (!isfocusable(view)) { + return false; + } + if (criteria & LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) { + /* + * Always-on-top views are always on the current desktop and are + * special in that they live in a different tree. + */ + struct server *server = view->server; + if (view->scene_tree->node.parent != server->workspace_current->tree + && !view_is_always_on_top(view)) { + return false; + } + } + if (criteria & LAB_VIEW_CRITERIA_NO_ALWAYS_ON_TOP) { + if (view_is_always_on_top(view)) { + return false; + } + } + if (criteria & LAB_VIEW_CRITERIA_NO_SKIP_WINDOW_SWITCHER) { + if (window_rules_get_property(view, "skipWindowSwitcher") == LAB_PROP_TRUE) { + return false; + } + } + return true; +} + +struct view * +view_next(struct wl_list *head, struct view *view, enum lab_view_criteria criteria) +{ + assert(head); + + struct wl_list *elm = view ? &view->link : head; + + for (elm = elm->next; elm != head; elm = elm->next) { + view = wl_container_of(elm, view, link); + if (matches_criteria(view, criteria)) { + return view; + } + } + return NULL; +} + void view_array_append(struct server *server, struct wl_array *views, enum lab_view_criteria criteria) { struct view *view; - wl_list_for_each(view, &server->views, link) - { - if (!isfocusable(view)) { + for_each_view(view, &server->views, criteria) { + struct view **entry = wl_array_add(views, sizeof(*entry)); + if (!entry) { + wlr_log(WLR_ERROR, "wl_array_add(): out of memory"); continue; } - - if (criteria & LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) { - /* - * Always-on-top views are always on the current - * desktop and are special in that they live in a - * different tree. - */ - if (view_is_always_on_top(view)) { - goto next; - } - if (view->scene_tree->node.parent != server->workspace_current->tree) { - continue; - } - } -next: - if (criteria & LAB_VIEW_CRITERIA_NO_ALWAYS_ON_TOP) { - if (view_is_always_on_top(view)) { - continue; - } - } - if (criteria & LAB_VIEW_CRITERIA_NO_SKIP_WINDOW_SWITCHER) { - if (window_rules_get_property(view, "skipWindowSwitcher") - == LAB_PROP_TRUE) { - continue; - } - } - - struct view **entry = wl_array_add(views, sizeof(*entry)); *entry = view; } }