]> git.mdlowis.com Git - proto/labwc.git/commitdiff
window-rules: support title criteria
authorJohan Malm <jgm323@gmail.com>
Tue, 9 May 2023 20:20:05 +0000 (21:20 +0100)
committerJohan Malm <johanmalm@users.noreply.github.com>
Wed, 10 May 2023 20:21:34 +0000 (21:21 +0100)
Example config:

    <windowRules>
      <windowRule identifier="foot" title="max">
        <action name="Maximize"/>
      </windowRule>
    </windowRules>

Observe that:

- `foot -T foo` starts maximized
- `xterm -T foo` starts normal

docs/labwc-config.5.scd
docs/rc.xml.all
include/window-rules.h
src/config/rcxml.c
src/window-rules.c

index e6d03d5b1520242a84f5f8223caa983f0850d21d..9539fdd79f0ef0f05988a3d84d3ebdf8ecfb52e9 100644 (file)
@@ -347,7 +347,7 @@ defined as shown below.
 <windowRules>
 
   <!-- Action -->
-  <windowRule identifier="">
+  <windowRule identifier="" title="">
     <action name=""/>
   </windowRule>
 
@@ -359,15 +359,18 @@ defined as shown below.
 
 *Actions*
 
-*<windowRules><windowRule identifier="">*
+*<windowRules><windowRule identifier="" title="">*
        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*.
index a8d2faba86bf41a8091350891db84da12c4ec8a1..8ee880242867313c9e8fc62aae587cf345854ce6 100644 (file)
   </libinput>
 
   <!--
+    Window Rules
+      - Criteria can consist of 'identifier' or 'title' or both (in which case
+        AND logic is used).
+      - 'identifier' relates to app_id for native Wayland windows and WM_CLASS
+        for XWayland clients.
+      - Matching against patterns with '*' (wildcard) and '?' (joker) is
+        supported. Pattern matching is case-insensitive.
+
   <windowRules>
-    <windowRule identifier="*">
-      <action name="Maximize" />
-    </windowRule>
-    <windowRule identifier="some-application" serverDecoration="yes" />
+    <windowRule identifier="*"><action name="Maximize"/></windowRule>
+    <windowRule identifier="foo" serverDecoration="yes"/>
+    <windowRule title="bar" serverDecoration="yes"/>
+    <windowRule identifier="baz" title="quax" serverDecoration="yes"/>
   </windowRules>
   -->
 
index 5d7f0c51baefc9f94f465d3c6527fe90a84578ff..e45761669e8ef98e2d939595a66bd159ac5ea85c 100644 (file)
@@ -20,6 +20,7 @@ enum property {
  */
 struct window_rule {
        char *identifier;
+       char *title;
 
        enum window_rule_event event;
        struct wl_list actions;
index 466d02baae121ebb715cd901f21df9cce97b1049..f52f980f83043c04a6aed4dc8733808feb39e5f1 100644 (file)
@@ -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, &current_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 */
index 5fb7631728b8c6cf33bcc51ff172908108ff6e05..899f89c1b9e215258c9bc94315f2ca283103f330 100644 (file)
@@ -2,6 +2,7 @@
 #define _POSIX_C_SOURCE 200809L
 #include "config.h"
 #include <assert.h>
+#include <stdbool.h>
 #include <cairo.h>
 #include <glib.h>
 #include <strings.h>
 #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
                 * <windowRule> 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;
 }