]> git.mdlowis.com Git - proto/labwc.git/commitdiff
window-rules: support matchOnce attribute
authorJohan Malm <jgm323@gmail.com>
Wed, 7 Jun 2023 20:44:38 +0000 (21:44 +0100)
committerJohan Malm <johanmalm@users.noreply.github.com>
Mon, 26 Jun 2023 05:04:07 +0000 (06:04 +0100)
...allowing a rule to be applied to only the first window matching a
particular criteria. For example, the following can be used to apply a
window rule to lxqt-panel but not its configuration window with the same
app_id:

    <windowRules>
      <windowRule identifier="lxqt-panel" matchOnce="true">
        <skipTaskbar>yes</skipTaskbar>
        <action name="MoveTo" x="0" y="0" />
        <action name="ToggleAlwaysOnTop"/>
      </windowRule>
    </windowRules>

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

index e3bfe5c2746aa15054d5cbe7cdb1b7c3333e00f1..7a9e5181400609d5fd17315f64a68f0fefe5afb2 100644 (file)
@@ -415,9 +415,9 @@ defined as shown below.
 </windowRules>
 ```
 
-*Actions*
+*Criteria*
 
-*<windowRules><windowRule identifier="" title="">*
+*<windowRules><windowRule identifier="" title="" matchOnce="">*
        Define a window rule for any window which matches the criteria defined
        by the attributes *identifier* or *title*. If both are defined, AND
        logic is used, so both have to match.
@@ -429,6 +429,9 @@ defined as shown below.
 
        *title* is the title of the window.
 
+       *matchOnce* can be true|false. If true, the rule will only apply to the
+       first instance of the window with the specified identifier or title.
+
 *Properties*
 
 Property values can be *yes*, *no* or *default*.
index 20f92a3143e2aaa519089c25d3f26af471e2dad6..30a3ac6253182402c7c0d6eeae8e13e91736881d 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="foo" serverDecoration="yes"/>
-    <windowRule title="bar" serverDecoration="yes"/>
-    <windowRule identifier="baz" title="quax" serverDecoration="yes"/>
-  </windowRules>
+    # 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.
+    #   - Criteria can also contain `matchOnce="true"` meaning that the rule
+    #     must only apply to the first instance of the window with that
+    #     particular 'identifier' or 'title'.
+    #   - Matching against patterns with '*' (wildcard) and '?' (joker) is
+    #     supported. Pattern matching is case-insensitive.
+
+    <windowRules>
+      <windowRule identifier="*"><action name="Maximize"/></windowRule>
+      <windowRule identifier="foo" serverDecoration="yes"/>
+      <windowRule title="bar" serverDecoration="yes"/>
+      <windowRule identifier="baz" title="quax" serverDecoration="yes"/>
+    </windowRules>
+
+    # Example below for `lxqt-panel` and `pcmanfm-qt \-\-desktop`
+    # where 'matchOnce' is used to avoid applying rule to the panel configuration
+    # window with the same 'app_id'
+
+    <windowRules>
+      <windowRule identifier="lxqt-panel" matchOnce="true">
+        <skipTaskbar>yes</skipTaskbar>
+        <action name="MoveTo" x="0" y="0" />
+        <action name="ToggleAlwaysOnTop"/>
+      </windowRule>
+      <windowRule title="pcmanfm-desktop*">
+        <skipTaskbar>yes</skipTaskbar>
+        <skipWindowSwitcher>yes</skipWindowSwitcher>
+        <action name="MoveTo" x="0" y="0" />
+        <action name="ToggleAlwaysOnBottom"/>
+      </windowRule>
+    </windowRules>
   -->
 
 </labwc_config>
index 4d983df02c4283cb4f65827b894bad8946b17311..69a56195c34d078d713fcf400c8928df7ed1cad3 100644 (file)
@@ -21,6 +21,7 @@ enum property {
 struct window_rule {
        char *identifier;
        char *title;
+       bool match_once;
 
        enum window_rule_event event;
        struct wl_list actions;
index 633b26bd6bac80645d8dec86d40caffe6a343dbd..cde4fb30621701317cdd096ace3977337658eeea 100644 (file)
@@ -130,6 +130,8 @@ fill_window_rule(char *nodename, char *content)
        } else if (!strcmp(nodename, "title")) {
                free(current_window_rule->title);
                current_window_rule->title = xstrdup(content);
+       } else if (!strcasecmp(nodename, "matchOnce")) {
+               set_bool(content, &current_window_rule->match_once);
 
        /* Event */
        } else if (!strcmp(nodename, "event")) {
index 7b366da0aa8d9954b2fb3b17ac5399a615aee933..de9da91da76e9e76782ae11cb221cf515310592b 100644 (file)
 #include "view.h"
 #include "window-rules.h"
 
+static bool
+other_instances_exist(struct view *self, const char *id, const char *title)
+{
+       struct wl_list *views = &self->server->views;
+       const char *prop = NULL;
+       struct view *view;
+
+       wl_list_for_each(view, views, link) {
+               if (view == self) {
+                       continue;
+               }
+               if (id) {
+                       prop = view_get_string_prop(view, "app_id");
+                       if (prop && !strcmp(prop, id)) {
+                               return true;
+                       }
+               }
+               if (title) {
+                       prop = view_get_string_prop(view, "title");
+                       if (prop && !strcmp(prop, title)) {
+                               return true;
+                       }
+               }
+       }
+       return false;
+}
+
 /* Try to match against identifier AND title (if set) */
 static bool
 view_matches_criteria(struct window_rule *rule, struct view *view)
@@ -21,6 +48,10 @@ view_matches_criteria(struct window_rule *rule, struct view *view)
        const char *id = view_get_string_prop(view, "app_id");
        const char *title = view_get_string_prop(view, "title");
 
+       if (rule->match_once && other_instances_exist(view, id, title)) {
+               return false;
+       }
+
        if (rule->identifier && rule->title) {
                if (!id || !title) {
                        return false;