diff --git a/include/sway/commands.h b/include/sway/commands.h index 389c382eb..4dd198833 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -145,6 +145,7 @@ sway_cmd cmd_fullscreen; sway_cmd cmd_gaps; sway_cmd cmd_hide_edge_borders; sway_cmd cmd_include; +sway_cmd cmd_include_one; sway_cmd cmd_inhibit_idle; sway_cmd cmd_input; sway_cmd cmd_seat; diff --git a/include/sway/config.h b/include/sway/config.h index 16b822fea..8e2e1d4dc 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -621,6 +621,14 @@ bool load_main_config(const char *path, bool is_active, bool validating); void load_include_configs(const char *path, struct sway_config *config, struct swaynag_instance *swaynag); +/** + * Loads included configs using basename-based shadowing. + * Files from pattern1 are always loaded. Files from pattern2 are loaded + * only if their basename was not already loaded from pattern1. + */ +void load_include_one_configs(const char *pattern1, const char *pattern2, + struct sway_config *config, struct swaynag_instance *swaynag); + /** * Reads the config from the given FILE. */ diff --git a/sway/commands.c b/sway/commands.c index c2c12ee65..2c21d5339 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -103,6 +103,7 @@ static const struct cmd_handler handlers[] = { static const struct cmd_handler config_handlers[] = { { "default_orientation", cmd_default_orientation }, { "include", cmd_include }, + { "include_one", cmd_include_one }, { "primary_selection", cmd_primary_selection }, { "swaybg_command", cmd_swaybg_command }, { "swaynag_command", cmd_swaynag_command }, diff --git a/sway/commands/include_one.c b/sway/commands/include_one.c new file mode 100644 index 000000000..d619178c0 --- /dev/null +++ b/sway/commands/include_one.c @@ -0,0 +1,12 @@ +#include "sway/commands.h" +#include "sway/config.h" + +struct cmd_results *cmd_include_one(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "include_one", EXPECTED_EQUAL_TO, 2))) { + return error; + } + load_include_one_configs(argv[0], argv[1], config, + &config->swaynag_config_errors); + return cmd_results_new(CMD_SUCCESS, NULL); +} diff --git a/sway/config.c b/sway/config.c index d579022d3..5139c822f 100644 --- a/sway/config.c +++ b/sway/config.c @@ -623,6 +623,72 @@ cleanup: free(wd); } +void load_include_one_configs(const char *pattern1, const char *pattern2, + struct sway_config *config, struct swaynag_instance *swaynag) { + char *wd = getcwd(NULL, 0); + if (!wd) { + sway_log(SWAY_ERROR, "include_one: failed to get working directory"); + return; + } + char *parent_path = strdup(config->current_config_path); + const char *parent_dir = dirname(parent_path); + if (chdir(parent_dir) < 0) { + sway_log(SWAY_ERROR, "include_one: failed to change working directory"); + goto cleanup; + } + + list_t *loaded_basenames = create_list(); + if (!loaded_basenames) { + sway_log(SWAY_ERROR, "include_one: failed to allocate list"); + goto cleanup; + } + + wordexp_t p; + if (wordexp(pattern1, &p, 0) == 0) { + char **w = p.we_wordv; + for (size_t i = 0; i < p.we_wordc; i++) { + if (load_include_config(w[i], config, swaynag)) { + char *tmp = strdup(w[i]); + list_add(loaded_basenames, strdup(basename(tmp))); + free(tmp); + } + } + wordfree(&p); + } + + if (wordexp(pattern2, &p, 0) == 0) { + char **w = p.we_wordv; + for (size_t i = 0; i < p.we_wordc; i++) { + char *tmp = strdup(w[i]); + char *base = basename(tmp); + bool already_loaded = false; + for (int j = 0; j < loaded_basenames->length; j++) { + if (strcmp(loaded_basenames->items[j], base) == 0) { + already_loaded = true; + break; + } + } + if (!already_loaded) { + load_include_config(w[i], config, swaynag); + } + free(tmp); + } + wordfree(&p); + } + + for (int i = 0; i < loaded_basenames->length; i++) { + free(loaded_basenames->items[i]); + } + list_free(loaded_basenames); + + if (chdir(wd) < 0) { + sway_log(SWAY_ERROR, "failed to change working directory"); + } +cleanup: + free(parent_path); + free(wd); +} + void run_deferred_commands(void) { if (!config->cmd_queue->length) { return; diff --git a/sway/meson.build b/sway/meson.build index cb03a4d28..c4b496bc6 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -76,6 +76,7 @@ sway_sources = files( 'commands/max_render_time.c', 'commands/opacity.c', 'commands/include.c', + 'commands/include_one.c', 'commands/input.c', 'commands/layout.c', 'commands/mode.c', diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 952d243d2..45a79d61d 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -72,6 +72,13 @@ The following commands may only be used in the configuration file. *wordexp*(3) for details). The same include file can only be included once; subsequent attempts will be ignored. +*include_one* + Include files from _pattern1_. Then include files from _pattern2_ only + if a file with the same basename was not already included from + _pattern1_. Both patterns expand shell syntax (see *wordexp*(3) for + details). This allows user configs to shadow distro-provided defaults + by filename. + *swaybg_command* Executes custom background _command_. Default is _swaybg_. Refer to *sway-output*(5) for more information.