mirror of
https://github.com/labwc/labwc.git
synced 2026-03-21 05:33:52 -04:00
window-rules: support title criteria
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
This commit is contained in:
parent
63db731cf4
commit
d37e705548
5 changed files with 90 additions and 30 deletions
|
|
@ -347,7 +347,7 @@ defined as shown below.
|
||||||
<windowRules>
|
<windowRules>
|
||||||
|
|
||||||
<!-- Action -->
|
<!-- Action -->
|
||||||
<windowRule identifier="">
|
<windowRule identifier="" title="">
|
||||||
<action name=""/>
|
<action name=""/>
|
||||||
</windowRule>
|
</windowRule>
|
||||||
|
|
||||||
|
|
@ -359,15 +359,18 @@ defined as shown below.
|
||||||
|
|
||||||
*Actions*
|
*Actions*
|
||||||
|
|
||||||
*<windowRules><windowRule identifier="">*
|
*<windowRules><windowRule identifier="" title="">*
|
||||||
Define a window rule for any window which matches the criteria defined
|
Define a window rule for any window which matches the criteria defined
|
||||||
by the attribute *identifier*. Matching against patterns with '\*'
|
by the attributes *identifier* or *title*. If both are defined, AND
|
||||||
(wildcard) and '?' (joker) is supported. Pattern matching is
|
logic is used, so both have to match.
|
||||||
case-insensitive.
|
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
|
*identifier* relates to app_id for native Wayland windows and WM_CLASS
|
||||||
for XWayland clients.
|
for XWayland clients.
|
||||||
|
|
||||||
|
*title* is the title of the window.
|
||||||
|
|
||||||
*Properties*
|
*Properties*
|
||||||
|
|
||||||
Property values can be *yes*, *no* or *default*.
|
Property values can be *yes*, *no* or *default*.
|
||||||
|
|
|
||||||
|
|
@ -376,11 +376,19 @@
|
||||||
</libinput>
|
</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>
|
<windowRules>
|
||||||
<windowRule identifier="*">
|
<windowRule identifier="*"><action name="Maximize"/></windowRule>
|
||||||
<action name="Maximize" />
|
<windowRule identifier="foo" serverDecoration="yes"/>
|
||||||
</windowRule>
|
<windowRule title="bar" serverDecoration="yes"/>
|
||||||
<windowRule identifier="some-application" serverDecoration="yes" />
|
<windowRule identifier="baz" title="quax" serverDecoration="yes"/>
|
||||||
</windowRules>
|
</windowRules>
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ enum property {
|
||||||
*/
|
*/
|
||||||
struct window_rule {
|
struct window_rule {
|
||||||
char *identifier;
|
char *identifier;
|
||||||
|
char *title;
|
||||||
|
|
||||||
enum window_rule_event event;
|
enum window_rule_event event;
|
||||||
struct wl_list actions;
|
struct wl_list actions;
|
||||||
|
|
|
||||||
|
|
@ -88,9 +88,16 @@ fill_window_rule(char *nodename, char *content)
|
||||||
/* nop */
|
/* nop */
|
||||||
} else if (!current_window_rule) {
|
} else if (!current_window_rule) {
|
||||||
wlr_log(WLR_ERROR, "no window-rule");
|
wlr_log(WLR_ERROR, "no window-rule");
|
||||||
|
|
||||||
|
/* Criteria */
|
||||||
} else if (!strcmp(nodename, "identifier")) {
|
} else if (!strcmp(nodename, "identifier")) {
|
||||||
free(current_window_rule->identifier);
|
free(current_window_rule->identifier);
|
||||||
current_window_rule->identifier = xstrdup(content);
|
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")) {
|
} else if (!strcmp(nodename, "event")) {
|
||||||
/*
|
/*
|
||||||
* This is just in readiness for adding any other types of
|
* 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")) {
|
if (!strcasecmp(content, "onFirstMap")) {
|
||||||
current_window_rule->event = LAB_WINDOW_RULE_EVENT_ON_FIRST_MAP;
|
current_window_rule->event = LAB_WINDOW_RULE_EVENT_ON_FIRST_MAP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Properties */
|
||||||
} else if (!strcasecmp(nodename, "serverDecoration")) {
|
} else if (!strcasecmp(nodename, "serverDecoration")) {
|
||||||
set_property(content, ¤t_window_rule->server_decoration);
|
set_property(content, ¤t_window_rule->server_decoration);
|
||||||
|
|
||||||
|
/* Actions */
|
||||||
} else if (!strcmp(nodename, "name.action")) {
|
} else if (!strcmp(nodename, "name.action")) {
|
||||||
current_window_rule_action = action_create(content);
|
current_window_rule_action = action_create(content);
|
||||||
if (current_window_rule_action) {
|
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
|
static void
|
||||||
rcxml_path(char *buf, size_t len)
|
rcxml_path(char *buf, size_t len)
|
||||||
{
|
{
|
||||||
|
|
@ -1091,6 +1125,7 @@ rcxml_read(const char *filename)
|
||||||
free(b.buf);
|
free(b.buf);
|
||||||
no_config:
|
no_config:
|
||||||
post_processing();
|
post_processing();
|
||||||
|
validate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -1140,10 +1175,7 @@ rcxml_finish(void)
|
||||||
|
|
||||||
struct window_rule *rule, *rule_tmp;
|
struct window_rule *rule, *rule_tmp;
|
||||||
wl_list_for_each_safe(rule, rule_tmp, &rc.window_rules, link) {
|
wl_list_for_each_safe(rule, rule_tmp, &rc.window_rules, link) {
|
||||||
wl_list_remove(&rule->link);
|
rule_destroy(rule);
|
||||||
zfree(rule->identifier);
|
|
||||||
action_list_free(&rule->actions);
|
|
||||||
zfree(rule);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reset state vars for starting fresh when Reload is triggered */
|
/* Reset state vars for starting fresh when Reload is triggered */
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#define _POSIX_C_SOURCE 200809L
|
#define _POSIX_C_SOURCE 200809L
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <cairo.h>
|
#include <cairo.h>
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
|
|
@ -13,20 +14,44 @@
|
||||||
#include "view.h"
|
#include "view.h"
|
||||||
#include "window-rules.h"
|
#include "window-rules.h"
|
||||||
|
|
||||||
|
/* Try to match against identifier AND title (if set) */
|
||||||
|
static bool
|
||||||
|
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->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
|
void
|
||||||
window_rules_apply(struct view *view, enum window_rule_event event)
|
window_rules_apply(struct view *view, enum window_rule_event event)
|
||||||
{
|
{
|
||||||
const char *identifier = view_get_string_prop(view, "app_id");
|
|
||||||
if (!identifier) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct window_rule *rule;
|
struct window_rule *rule;
|
||||||
wl_list_for_each(rule, &rc.window_rules, link) {
|
wl_list_for_each(rule, &rc.window_rules, link) {
|
||||||
if (!rule->identifier || rule->event != event) {
|
if (rule->event != event) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (match_glob(rule->identifier, identifier)) {
|
if (view_matches_criteria(rule, view)) {
|
||||||
actions_run(view, view->server, &rule->actions, 0);
|
actions_run(view, view->server, &rule->actions, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -37,11 +62,6 @@ window_rules_get_property(struct view *view, const char *property)
|
||||||
{
|
{
|
||||||
assert(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
|
* We iterate in reverse here because later items in list have higher
|
||||||
* priority. For example, in the config below we want the return value
|
* 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;
|
struct window_rule *rule;
|
||||||
wl_list_for_each_reverse(rule, &rc.window_rules, link) {
|
wl_list_for_each_reverse(rule, &rc.window_rules, link) {
|
||||||
if (!rule->identifier) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
/*
|
/*
|
||||||
* Only return if property != LAB_PROP_UNSPECIFIED otherwise a
|
* Only return if property != LAB_PROP_UNSPECIFIED otherwise a
|
||||||
* <windowRule> which does not set a particular property
|
* <windowRule> which does not set a particular property
|
||||||
* attribute would still return here if that property was asked
|
* attribute would still return here if that property was asked
|
||||||
* for.
|
* for.
|
||||||
*/
|
*/
|
||||||
if (match_glob(rule->identifier, identifier)) {
|
if (view_matches_criteria(rule, view)) {
|
||||||
if (!strcasecmp(property, "serverDecoration")) {
|
if (!strcasecmp(property, "serverDecoration")) {
|
||||||
if (rule->server_decoration) {
|
if (rule->server_decoration) {
|
||||||
return 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;
|
return LAB_PROP_UNSPECIFIED;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue