~emersion/public-inbox

kanshi: Add support for setting output name variables v1 SUPERSEDED

Hi Simon!

This patch adds support for variables for output names to kanshi. This
allows setting short readable names for long output names that would
otherwise be pretty unreadable and makes longer configs much more
readable.

This is accomplished by adding a new directive `set $variable "variable
value"` and a list of variables in the `kanshi_config` struct. Each time
an output name is parsed in `parse_profile_output()`, if the name starts
with a dollar sign, the value of the variable with that name is used
instead.

There is currently no way to escape the dollar sign for outputs whose
name starts with a dollar sign, but I have never actually seen that in
practice. If support for escaping the dollar sign is required for you to
accept this feature, I will add it in V2 of this patch series.

Ferdinand "yrlf" Bachmann

Ferdinand Bachmann (1):
  Add support for setting output name variables

 doc/kanshi.5.scd |  3 ++
 include/config.h |  7 +++++
 main.c           |  8 +++++
 parser.c         | 82 ++++++++++++++++++++++++++++++++++++++++++++----
 4 files changed, 94 insertions(+), 6 deletions(-)

-- 
2.38.5
#1128275 .build.yml success
kanshi/patches/.build.yml: SUCCESS in 29s

[Add support for setting output name variables][0] from [~yrlf][1]

[0]: https://lists.sr.ht/~emersion/public-inbox/patches/48342
[1]: mailto:me@yrlf.at

✓ #1128275 SUCCESS kanshi/patches/.build.yml https://builds.sr.ht/~emersion/job/1128275
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~emersion/public-inbox/patches/48342/mbox | git am -3
Learn more about email & git

[PATCH kanshi 1/1] Add support for setting output name variables Export this patch

From: Ferdinand Bachmann <me@yrlf.at>

---
 doc/kanshi.5.scd |  3 ++
 include/config.h |  7 +++++
 main.c           |  8 +++++
 parser.c         | 82 ++++++++++++++++++++++++++++++++++++++++++++----
 4 files changed, 94 insertions(+), 6 deletions(-)

diff --git a/doc/kanshi.5.scd b/doc/kanshi.5.scd
index b007d07..67ab1fb 100644
--- a/doc/kanshi.5.scd
+++ b/doc/kanshi.5.scd
@@ -34,6 +34,9 @@ profile nomad {
	Include as another file from _path_. Expands shell syntax (see *wordexp*(3)
	for details).

*set* $<name> <value>
	Defines a new variable that can be used in output names.

# PROFILE DIRECTIVES

Profile directives are followed by space-separated arguments. Arguments can be
diff --git a/include/config.h b/include/config.h
index 047f05b..b49a4eb 100644
--- a/include/config.h
+++ b/include/config.h
@@ -45,7 +45,14 @@ struct kanshi_profile {
	struct wl_list commands;
};

struct kanshi_variable {
	struct wl_list link;
	char *name;
	char *value;
};

struct kanshi_config {
	struct wl_list variables;
	struct wl_list profiles;
};

diff --git a/main.c b/main.c
index c7cdd9d..06def01 100644
--- a/main.c
+++ b/main.c
@@ -625,6 +625,14 @@ static struct kanshi_config *read_config(const char *config) {
}

static void destroy_config(struct kanshi_config *config) {
	struct kanshi_variable *variable, *tmp_variable;
	wl_list_for_each_safe(variable, tmp_variable, &config->variables, link) {
		free(variable->name);
		free(variable->value);
		wl_list_remove(&variable->link);
		free(variable);
	}

	struct kanshi_profile *profile, *tmp_profile;
	wl_list_for_each_safe(profile, tmp_profile, &config->profiles, link) {
		struct kanshi_profile_output *output, *tmp_output;
diff --git a/parser.c b/parser.c
index 21ae248..a5cf592 100644
--- a/parser.c
+++ b/parser.c
@@ -301,13 +301,30 @@ static bool parse_bool(bool *dst, const char *str) {
}

static struct kanshi_profile_output *parse_profile_output(
		struct kanshi_parser *parser) {
		struct kanshi_parser *parser, struct wl_list *variables) {
	struct kanshi_profile_output *output = calloc(1, sizeof(*output));

	if (!parser_expect_token(parser, KANSHI_TOKEN_STR)) {
		return NULL;
	}
	output->name = strdup(parser->tok_str);

	if (parser->tok_str[0] == '$') {
		struct kanshi_variable *cur;
		wl_list_for_each(cur, variables, link) {
			if (strcmp(cur->name, parser->tok_str) == 0) {
				output->name = strdup(cur->value);
				break;
			}
		}

		if (output->name == NULL) {
			fprintf(stderr, "Undefined variable '%s'\n", parser->tok_str);
			free(output);
			return NULL;
		}
	} else {
		output->name = strdup(parser->tok_str);
	}

	bool has_key = false;
	enum kanshi_output_field key = 0;
@@ -419,7 +436,7 @@ static struct kanshi_profile_command *parse_profile_command(
	return command;
}

static struct kanshi_profile *parse_profile(struct kanshi_parser *parser) {
static struct kanshi_profile *parse_profile(struct kanshi_parser *parser, struct wl_list *variables) {
	struct kanshi_profile *profile = calloc(1, sizeof(*profile));
	wl_list_init(&profile->outputs);
	wl_list_init(&profile->commands);
@@ -468,7 +485,7 @@ static struct kanshi_profile *parse_profile(struct kanshi_parser *parser) {
			const char *directive = parser->tok_str;
			if (strcmp(directive, "output") == 0) {
				struct kanshi_profile_output *output =
					parse_profile_output(parser);
					parse_profile_output(parser, variables);
				if (output == NULL) {
					return NULL;
				}
@@ -536,6 +553,54 @@ static bool parse_include_command(struct kanshi_parser *parser, struct kanshi_co
	return true;
}

static bool parse_set_command(struct kanshi_parser *parser, struct kanshi_config *config) {
	struct kanshi_variable *variable = calloc(1, sizeof(*variable));

	// Skip the 'set' directive.
	if (!parser_expect_token(parser, KANSHI_TOKEN_STR)) {
		free(variable);
		return false;
	}
	
	if (parser->tok_str[0] == '$') {
		variable->name = strdup(parser->tok_str);
	} else {
		fprintf(stderr, "Invalid variable name '%s', must start with $\n", parser->tok_str);
		free(variable);
		return false;
	}

	// Check for duplicate variable name
	struct kanshi_variable *cur;
	wl_list_for_each(cur, &config->variables, link) {
		if (strcmp(variable->name, cur->name) == 0) {
			fprintf(stderr, "redefinition of variable %s\n", cur->name);
			free(variable->name);
			free(variable);
			return false;
		}
	}
	
	// Parse variable value
	if (!parser_expect_token(parser, KANSHI_TOKEN_STR)) {
		free(variable->name);
		free(variable);
		return false;
	}

	variable->value = strdup(parser->tok_str);

	if (!parser_expect_token(parser, KANSHI_TOKEN_NEWLINE)) {
		free(variable->name);
		free(variable->value);
		free(variable);
		return false;
	}

	wl_list_insert(&config->variables, &variable->link);
	return true;
}

static bool _parse_config(struct kanshi_parser *parser, struct kanshi_config *config) {
	while (1) {
		int ch = parser_peek_char(parser);
@@ -553,7 +618,7 @@ static bool _parse_config(struct kanshi_parser *parser, struct kanshi_config *co

		if (ch == '{') {
			// Legacy profile syntax without a profile directive
			struct kanshi_profile *profile = parse_profile(parser);
			struct kanshi_profile *profile = parse_profile(parser, &config->variables);
			if (!profile) {
				return false;
			}
@@ -565,7 +630,7 @@ static bool _parse_config(struct kanshi_parser *parser, struct kanshi_config *co

			const char *directive = parser->tok_str;
			if (strcmp(parser->tok_str, "profile") == 0) {
				struct kanshi_profile *profile = parse_profile(parser);
				struct kanshi_profile *profile = parse_profile(parser, &config->variables);
				if (!profile) {
					return false;
				}
@@ -574,6 +639,10 @@ static bool _parse_config(struct kanshi_parser *parser, struct kanshi_config *co
				if (!parse_include_command(parser, config)) {
					return false;
				}
			} else if (strcmp(parser->tok_str, "set") == 0) {
				if (!parse_set_command(parser, config)) {
					return false;
				}
			} else {
				fprintf(stderr, "unknown directive '%s'\n", directive);
				return false;
@@ -613,6 +682,7 @@ struct kanshi_config *parse_config(const char *path) {
	if (config == NULL) {
		return NULL;
	}
	wl_list_init(&config->variables);
	wl_list_init(&config->profiles);

	if (!parse_config_file(path, config)) {
-- 
2.38.5
kanshi/patches/.build.yml: SUCCESS in 29s

[Add support for setting output name variables][0] from [~yrlf][1]

[0]: https://lists.sr.ht/~emersion/public-inbox/patches/48342
[1]: mailto:me@yrlf.at

✓ #1128275 SUCCESS kanshi/patches/.build.yml https://builds.sr.ht/~emersion/job/1128275