From d37e705548a12309a2074441e227ae238a41d75c Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Tue, 9 May 2023 21:20:05 +0100 Subject: [PATCH] window-rules: support title criteria Example config: Observe that: - `foot -T foo` starts maximized - `xterm -T foo` starts normal --- docs/labwc-config.5.scd | 13 ++++++----- docs/rc.xml.all | 16 +++++++++---- include/window-rules.h | 1 + src/config/rcxml.c | 40 +++++++++++++++++++++++++++++---- src/window-rules.c | 50 +++++++++++++++++++++++++++-------------- 5 files changed, 90 insertions(+), 30 deletions(-) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index e6d03d5b..9539fdd7 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -347,7 +347,7 @@ defined as shown below. - + @@ -359,15 +359,18 @@ defined as shown below. *Actions* -** +** Define a window rule for any window which matches the criteria defined - by the attribute *identifier*. Matching against patterns with '\*' - (wildcard) and '?' (joker) is supported. Pattern matching is - case-insensitive. + by the attributes *identifier* or *title*. If both are defined, AND + logic is used, so both have to match. + Matching against patterns with '\*' (wildcard) and '?' (joker) is + supported. Pattern matching is case-insensitive. *identifier* relates to app_id for native Wayland windows and WM_CLASS for XWayland clients. + *title* is the title of the window. + *Properties* Property values can be *yes*, *no* or *default*. diff --git a/docs/rc.xml.all b/docs/rc.xml.all index a8d2faba..8ee88024 100644 --- a/docs/rc.xml.all +++ b/docs/rc.xml.all @@ -376,11 +376,19 @@ diff --git a/include/window-rules.h b/include/window-rules.h index 5d7f0c51..e4576166 100644 --- a/include/window-rules.h +++ b/include/window-rules.h @@ -20,6 +20,7 @@ enum property { */ struct window_rule { char *identifier; + char *title; enum window_rule_event event; struct wl_list actions; diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 466d02ba..f52f980f 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -88,9 +88,16 @@ fill_window_rule(char *nodename, char *content) /* nop */ } else if (!current_window_rule) { wlr_log(WLR_ERROR, "no window-rule"); + + /* Criteria */ } else if (!strcmp(nodename, "identifier")) { free(current_window_rule->identifier); current_window_rule->identifier = xstrdup(content); + } else if (!strcmp(nodename, "title")) { + free(current_window_rule->title); + current_window_rule->title = xstrdup(content); + + /* Event */ } else if (!strcmp(nodename, "event")) { /* * This is just in readiness for adding any other types of @@ -99,8 +106,12 @@ fill_window_rule(char *nodename, char *content) if (!strcasecmp(content, "onFirstMap")) { current_window_rule->event = LAB_WINDOW_RULE_EVENT_ON_FIRST_MAP; } + + /* Properties */ } else if (!strcasecmp(nodename, "serverDecoration")) { set_property(content, ¤t_window_rule->server_decoration); + + /* Actions */ } else if (!strcmp(nodename, "name.action")) { current_window_rule_action = action_create(content); if (current_window_rule_action) { @@ -1027,6 +1038,29 @@ post_processing(void) } } +static void +rule_destroy(struct window_rule *rule) +{ + wl_list_remove(&rule->link); + zfree(rule->identifier); + zfree(rule->title); + action_list_free(&rule->actions); + zfree(rule); +} + +static void +validate(void) +{ + /* Window-rule criteria */ + struct window_rule *rule, *next; + wl_list_for_each_safe(rule, next, &rc.window_rules, link) { + if (!rule->identifier && !rule->title) { + wlr_log(WLR_ERROR, "deleting rule %p as no criteria", rule); + rule_destroy(rule); + } + } +} + static void rcxml_path(char *buf, size_t len) { @@ -1091,6 +1125,7 @@ rcxml_read(const char *filename) free(b.buf); no_config: post_processing(); + validate(); } void @@ -1140,10 +1175,7 @@ rcxml_finish(void) struct window_rule *rule, *rule_tmp; wl_list_for_each_safe(rule, rule_tmp, &rc.window_rules, link) { - wl_list_remove(&rule->link); - zfree(rule->identifier); - action_list_free(&rule->actions); - zfree(rule); + rule_destroy(rule); } /* Reset state vars for starting fresh when Reload is triggered */ diff --git a/src/window-rules.c b/src/window-rules.c index 5fb76317..899f89c1 100644 --- a/src/window-rules.c +++ b/src/window-rules.c @@ -2,6 +2,7 @@ #define _POSIX_C_SOURCE 200809L #include "config.h" #include +#include #include #include #include @@ -13,20 +14,44 @@ #include "view.h" #include "window-rules.h" -void -window_rules_apply(struct view *view, enum window_rule_event event) +/* Try to match against identifier AND title (if set) */ +static bool +view_matches_criteria(struct window_rule *rule, struct view *view) { - const char *identifier = view_get_string_prop(view, "app_id"); - if (!identifier) { - return; + const char *id = view_get_string_prop(view, "app_id"); + const char *title = view_get_string_prop(view, "title"); + + if (rule->identifier && rule->title) { + if (!id || !title) { + return false; + } + return match_glob(rule->identifier, id) + && match_glob(rule->title, title); + } else if (rule->identifier) { + if (!id) { + return false; + } + return match_glob(rule->identifier, id); + } else if (rule->title) { + if (!title) { + return false; + } + return match_glob(rule->title, title); + } else { + wlr_log(WLR_ERROR, "rule has no identifier or title\n"); + return false; } +} +void +window_rules_apply(struct view *view, enum window_rule_event event) +{ struct window_rule *rule; wl_list_for_each(rule, &rc.window_rules, link) { - if (!rule->identifier || rule->event != event) { + if (rule->event != event) { continue; } - if (match_glob(rule->identifier, identifier)) { + if (view_matches_criteria(rule, view)) { actions_run(view, view->server, &rule->actions, 0); } } @@ -37,11 +62,6 @@ window_rules_get_property(struct view *view, const char *property) { assert(property); - const char *identifier = view_get_string_prop(view, "app_id"); - if (!identifier) { - goto out; - } - /* * We iterate in reverse here because later items in list have higher * priority. For example, in the config below we want the return value @@ -54,16 +74,13 @@ window_rules_get_property(struct view *view, const char *property) */ struct window_rule *rule; wl_list_for_each_reverse(rule, &rc.window_rules, link) { - if (!rule->identifier) { - continue; - } /* * Only return if property != LAB_PROP_UNSPECIFIED otherwise a * which does not set a particular property * attribute would still return here if that property was asked * for. */ - if (match_glob(rule->identifier, identifier)) { + if (view_matches_criteria(rule, view)) { if (!strcasecmp(property, "serverDecoration")) { if (rule->server_decoration) { return rule->server_decoration; @@ -71,6 +88,5 @@ window_rules_get_property(struct view *view, const char *property) } } } -out: return LAB_PROP_UNSPECIFIED; } -- 2.52.0