<windowRules>
<!-- Action -->
- <windowRule identifier="">
+ <windowRule identifier="" title="">
<action name=""/>
</windowRule>
*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*.
</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>
-->
*/
struct window_rule {
char *identifier;
+ char *title;
enum window_rule_event event;
struct wl_list actions;
/* 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
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) {
}
}
+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)
{
free(b.buf);
no_config:
post_processing();
+ validate();
}
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 */
#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);
}
}
{
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
*/
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;
}
}
}
-out:
return LAB_PROP_UNSPECIFIED;
}