From 698c7ace076b7c1c57c6871bca73c1301c42a540 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Tue, 9 Jan 2024 22:00:45 +0000 Subject: [PATCH] config: support merging multiple config files Add the -m|--merge-config command line option to iterate backwards over XDG Base Dir paths and read config/theme files multiple times. For example if both ~/.config/labwc/rc.xml and /etc/xdg/labwc/rc.xml exist, the latter will be read first and then the former (if --merge-config is enabled). When $XDG_CONFIG_HOME is defined, make it replace (not augment) $HOME/.config. Similarly, make $XDG_CONFIG_DIRS replace /etc/xdg when defined. XDG Base Dir Spec does not specify whether or not an application (or a compositor!) should (a) define that only the file under the most important base directory should be used, or (b) define rules for merging the information from the different files. ref: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html In the case of labwc there is a use-case for both positions, just to be clear, the default behaviour, described by position (a) above, does NOT change. This change affects the following config/theme files: - rc.xml - menu.xml - autostart - environment - themerc - themerc-override - Theme buttons, for example max.xbm Instead of caching global config/theme directories, create lists of paths (e.g. '/home/foo/.config/labwc/rc.xml', '/etc/xdg/labwc/rc.xml', etc). This creates more common parsing logic and just reversing the direction of iteration and breaks early if config-merge is not wanted. Enable better fallback for themes. For example if a particular theme does not exist in $HOME/.local/share/themes, it will be searched for in ~/.themes/ and so on. This also applies to theme buttons which now fallback on an individual basis. Avoid using stat() in most situations and just go straight to fopen(). Fixes #1406 --- docs/labwc-config.5.scd | 13 +++ docs/labwc-theme.5.scd | 3 + docs/labwc.1.scd | 3 + include/common/dir.h | 19 ++-- include/config/rcxml.h | 1 + include/config/session.h | 6 +- src/button/common.c | 18 +++- src/common/dir.c | 193 ++++++++++++++++++++++++--------------- src/config/rcxml.c | 102 ++++++++++----------- src/config/session.c | 74 ++++++++------- src/main.c | 17 ++-- src/menu/menu.c | 31 ++++--- src/server.c | 2 +- src/theme.c | 82 +++++++---------- 14 files changed, 327 insertions(+), 237 deletions(-) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index b57cb3db..f6345d4e 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -18,6 +18,19 @@ searched for in the following order: - ${XDG_CONFIG_HOME:-$HOME/.config}/labwc - ${XDG_CONFIG_DIRS:-/etc/xdg}/labwc +When $XDG_CONFIG_HOME is defined, it replaces (rather than augments) +$HOME/.config. The same is the case for $XDG_CONFIG_DIRS and /etc/xdg. + +The XDG Base Directory Specification does not specify whether or not programs +should (a) allow the first-identified configuration file to supersede any +others, or (b) define rules for merging the information from more than one file. + +By default, labwc uses option (a), reading only the first file identified. With +the --merge-config option, the search order is reserved, but every configuration +file encountered is processed in turn. Thus, user-specific files will augment +system-wide configurations, with conflicts favoring the user-specific +alternative. + The configuration directory location can be override with the -C command line option. diff --git a/docs/labwc-theme.5.scd b/docs/labwc-theme.5.scd index 689e48c1..dabfa6a6 100644 --- a/docs/labwc-theme.5.scd +++ b/docs/labwc-theme.5.scd @@ -15,6 +15,9 @@ searched for in the following order: - /usr/local/share/themes//openbox-3/ - /opt/share/themes//openbox-3/ +When $XDG_DATA_HOME is defined, it replaces (rather than augments) +$HOME/.local/share. The same is the case for $XDG_DATA_DIRS and /usr/share/. + Choosing a theme is done by editing the key in the section of the rc.xml configuration file (labwc-config(5)). diff --git a/docs/labwc.1.scd b/docs/labwc.1.scd index 37e05a7d..ca9f1ae9 100644 --- a/docs/labwc.1.scd +++ b/docs/labwc.1.scd @@ -45,6 +45,9 @@ the `--exit` and `--reconfigure` options use. *-h, --help* Show help message and quit +*-m, --merge-config* + Merge user config/theme files in all XDG Base Directories + *-r, --reconfigure* Reload the compositor configuration diff --git a/include/common/dir.h b/include/common/dir.h index ef03b5e7..bbb5e566 100644 --- a/include/common/dir.h +++ b/include/common/dir.h @@ -2,12 +2,19 @@ #ifndef LABWC_DIR_H #define LABWC_DIR_H -char *config_dir(void); +#include -/** - * theme_dir - find theme directory containing theme @theme_name - * @theme_name: theme to search for - */ -char *theme_dir(const char *theme_name); +struct path { + char *string; + struct wl_list link; +}; + +struct wl_list *paths_get_prev(struct wl_list *elm); +struct wl_list *paths_get_next(struct wl_list *elm); + +void paths_config_create(struct wl_list *paths, const char *filename); +void paths_theme_create(struct wl_list *paths, const char *theme_name, + const char *filename); +void paths_destroy(struct wl_list *paths); #endif /* LABWC_DIR_H */ diff --git a/include/config/rcxml.h b/include/config/rcxml.h index 4dede031..fa410116 100644 --- a/include/config/rcxml.h +++ b/include/config/rcxml.h @@ -48,6 +48,7 @@ struct window_switcher_field { struct rcxml { char *config_dir; + bool merge_config; /* core */ bool xdg_shell_server_side_deco; diff --git a/include/config/session.h b/include/config/session.h index 4cc19b6e..d1efbb60 100644 --- a/include/config/session.h +++ b/include/config/session.h @@ -4,17 +4,15 @@ /** * session_environment_init - set enrivonment variables based on = - * @dir: path to config directory * pairs in `${XDG_CONFIG_DIRS:-/etc/xdg}/lawbc/environment` with user override * in `${XDG_CONFIG_HOME:-$HOME/.config}` */ -void session_environment_init(const char *dir); +void session_environment_init(void); /** * session_autostart_init - run autostart file as shell script - * @dir: path to config directory * Note: Same as `sh ~/.config/labwc/autostart` (or equivalent XDG config dir) */ -void session_autostart_init(const char *dir); +void session_autostart_init(void); #endif /* LABWC_SESSION_H */ diff --git a/src/button/common.c b/src/button/common.c index b5a3aaa7..39400994 100644 --- a/src/button/common.c +++ b/src/button/common.c @@ -1,11 +1,27 @@ // SPDX-License-Identifier: GPL-2.0-only #include +#include #include "button/common.h" #include "common/dir.h" #include "config/rcxml.h" +#include "labwc.h" void button_filename(const char *name, char *buf, size_t len) { - snprintf(buf, len, "%s/%s", theme_dir(rc.theme_name), name); + struct wl_list paths; + paths_theme_create(&paths, rc.theme_name, name); + + /* + * You can't really merge buttons, so let's just iterate forwards + * and stop on the first hit + */ + struct path *path; + wl_list_for_each(path, &paths, link) { + if (access(path->string, R_OK) == 0) { + snprintf(buf, len, "%s", path->string); + break; + } + } + paths_destroy(&paths); } diff --git a/src/common/dir.c b/src/common/dir.c index 85dfea14..59db4104 100644 --- a/src/common/dir.c +++ b/src/common/dir.c @@ -4,143 +4,190 @@ * * Copyright Johan Malm 2020 */ - +#include #include #include #include #include #include #include "common/dir.h" +#include "common/buf.h" +#include "common/list.h" +#include "common/mem.h" +#include "common/string-helpers.h" +#include "labwc.h" struct dir { const char *prefix; + const char *default_prefix; const char *path; }; static struct dir config_dirs[] = { - { "XDG_CONFIG_HOME", "labwc" }, - { "HOME", ".config/labwc" }, - { "XDG_CONFIG_DIRS", "labwc" }, - { NULL, "/etc/xdg/labwc" }, - { NULL, NULL } + { + .prefix = "XDG_CONFIG_HOME", + .default_prefix = "$HOME/.config", + .path = "labwc" + }, { + .prefix = "XDG_CONFIG_DIRS", + .default_prefix = "/etc/xdg", + .path = "labwc", + }, { + .path = NULL, + } }; static struct dir theme_dirs[] = { - { "XDG_DATA_HOME", "themes" }, - { "HOME", ".local/share/themes" }, - { "HOME", ".themes" }, - { "XDG_DATA_DIRS", "themes" }, - { NULL, "/usr/share/themes" }, - { NULL, "/usr/local/share/themes" }, - { NULL, "/opt/share/themes" }, - { NULL, NULL } + { + .prefix = "XDG_DATA_HOME", + .default_prefix = "$HOME/.local/share", + .path = "themes", + }, { + .prefix = "HOME", + .path = ".themes", + }, { + .prefix = "XDG_DATA_DIRS", + .default_prefix = "/usr/share:/usr/local/share:/opt/share", + .path = "themes", + }, { + .path = NULL, + } }; -static bool -isdir(const char *path) -{ - struct stat st; - return (!stat(path, &st) && S_ISDIR(st.st_mode)); -} - struct ctx { void (*build_path_fn)(struct ctx *ctx, char *prefix, const char *path); + const char *filename; char *buf; size_t len; struct dir *dirs; const char *theme_name; + struct wl_list *list; }; +struct wl_list *paths_get_prev(struct wl_list *elm) { return elm->prev; } +struct wl_list *paths_get_next(struct wl_list *elm) { return elm->next; } + static void build_config_path(struct ctx *ctx, char *prefix, const char *path) { - if (!prefix) { - snprintf(ctx->buf, ctx->len, "%s", path); - } else { - snprintf(ctx->buf, ctx->len, "%s/%s", prefix, path); - } + assert(prefix); + snprintf(ctx->buf, ctx->len, "%s/%s/%s", prefix, path, ctx->filename); } static void build_theme_path(struct ctx *ctx, char *prefix, const char *path) { - if (!prefix) { - snprintf(ctx->buf, ctx->len, "%s/%s/openbox-3", path, - ctx->theme_name); - } else { - snprintf(ctx->buf, ctx->len, "%s/%s/%s/openbox-3", prefix, path, - ctx->theme_name); - } + assert(prefix); + snprintf(ctx->buf, ctx->len, "%s/%s/%s/openbox-3/%s", prefix, path, + ctx->theme_name, ctx->filename); } -static char * +static void find_dir(struct ctx *ctx) { char *debug = getenv("LABWC_DEBUG_DIR_CONFIG_AND_THEME"); for (int i = 0; ctx->dirs[i].path; i++) { struct dir d = ctx->dirs[i]; - if (!d.prefix) { - /* handle /etc/xdg... */ - ctx->build_path_fn(ctx, NULL, d.path); + struct buf prefix; + buf_init(&prefix); + + /* + * Replace (rather than augment) $HOME/.config with + * $XDG_CONFIG_HOME if defined, and so on for the other + * XDG Base Directories. + */ + char *pfxenv = getenv(d.prefix); + buf_add(&prefix, pfxenv ? pfxenv : d.default_prefix); + if (!prefix.len) { + free(prefix.buf); + continue; + } + + /* Handle .default_prefix shell variables such as $HOME */ + buf_expand_shell_variables(&prefix); + + /* + * Respect that $XDG_DATA_DIRS can contain multiple colon + * separated paths and that we have structured the + * .default_prefix in the same way. + */ + gchar * *prefixes; + prefixes = g_strsplit(prefix.buf, ":", -1); + for (gchar * *p = prefixes; *p; p++) { + ctx->build_path_fn(ctx, *p, d.path); if (debug) { fprintf(stderr, "%s\n", ctx->buf); } - if (isdir(ctx->buf)) { - return ctx->buf; - } - } else { - /* handle $HOME/.config/... and $XDG_* */ - char *prefix = getenv(d.prefix); - if (!prefix) { - continue; - } - gchar * *prefixes; - prefixes = g_strsplit(prefix, ":", -1); - for (gchar * *p = prefixes; *p; p++) { - ctx->build_path_fn(ctx, *p, d.path); - if (debug) { - fprintf(stderr, "%s\n", ctx->buf); - } - if (isdir(ctx->buf)) { - g_strfreev(prefixes); - return ctx->buf; - } - } - g_strfreev(prefixes); + + /* + * TODO: We could stat() and continue here if we really + * wanted to only respect only the first hit, but feels + * like it is probably overkill. + */ + struct path *path = znew(*path); + path->string = xstrdup(ctx->buf); + wl_list_append(ctx->list, &path->link); } + g_strfreev(prefixes); + free(prefix.buf); } - /* no directory was found */ - ctx->buf[0] = '\0'; - return ctx->buf; } -char * -config_dir(void) +void +paths_config_create(struct wl_list *paths, const char *filename) { - static char buf[4096] = { 0 }; - if (buf[0] != '\0') { - return buf; + char buf[4096] = { 0 }; + wl_list_init(paths); + + /* + * If user provided a config directory with the -C command line option, + * then that trumps everything else and we do not create the + * XDG-Base-Dir list. + */ + if (rc.config_dir) { + struct path *path = znew(*path); + path->string = strdup_printf("%s/%s", rc.config_dir, filename); + wl_list_append(paths, &path->link); + return; } + struct ctx ctx = { .build_path_fn = build_config_path, + .filename = filename, .buf = buf, .len = sizeof(buf), - .dirs = config_dirs + .dirs = config_dirs, + .list = paths, }; - return find_dir(&ctx); + find_dir(&ctx); } -char * -theme_dir(const char *theme_name) +void +paths_theme_create(struct wl_list *paths, const char *theme_name, + const char *filename) { static char buf[4096] = { 0 }; + wl_list_init(paths); struct ctx ctx = { .build_path_fn = build_theme_path, + .filename = filename, .buf = buf, .len = sizeof(buf), .dirs = theme_dirs, - .theme_name = theme_name + .theme_name = theme_name, + .list = paths, }; - return find_dir(&ctx); + find_dir(&ctx); +} + +void +paths_destroy(struct wl_list *paths) +{ + struct path *path, *next; + wl_list_for_each_safe(path, next, paths, link) { + free(path->string); + wl_list_remove(&path->link); + free(path); + } } diff --git a/src/config/rcxml.c b/src/config/rcxml.c index bc21a458..3f0e4ce9 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -14,6 +14,7 @@ #include #include #include "action.h" +#include "common/dir.h" #include "common/list.h" #include "common/macros.h" #include "common/mem.h" @@ -1473,69 +1474,68 @@ validate(void) validate_actions(); } -static void -rcxml_path(char *buf, size_t len) +void +rcxml_read(const char *filename) { - if (!rc.config_dir) { - return; - } - snprintf(buf, len, "%s/rc.xml", rc.config_dir); -} + rcxml_init(); + + struct wl_list paths; -static void -find_config_file(char *buffer, size_t len, const char *filename) -{ if (filename) { - snprintf(buffer, len, "%s", filename); - return; + /* Honour command line argument -c */ + wl_list_init(&paths); + struct path *path = znew(*path); + path->string = xstrdup(filename); + wl_list_append(&paths, &path->link); + } else { + paths_config_create(&paths, "rc.xml"); } - rcxml_path(buffer, len); -} -void -rcxml_read(const char *filename) -{ - FILE *stream; - char *line = NULL; - size_t len = 0; + /* Reading file into buffer before parsing - better for unit tests */ struct buf b; - static char rcxml[4096] = {0}; - rcxml_init(); + bool should_merge_config = rc.merge_config; + struct wl_list *(*iter)(struct wl_list *list); + iter = should_merge_config ? paths_get_prev : paths_get_next; /* - * rcxml_read() can be called multiple times, but we only set rcxml[] - * the first time. The specified 'filename' is only respected the first - * time. + * This is the equivalent of a wl_list_for_each() which optionally + * iterates in reverse depending on 'should_merge_config' + * + * If not merging, we iterate forwards and break after the first + * iteration. + * + * If merging, we iterate backwards (least important XDG Base Dir first) + * and keep going. */ - if (rcxml[0] == '\0') { - find_config_file(rcxml, sizeof(rcxml), filename); - } - if (rcxml[0] == '\0') { - wlr_log(WLR_INFO, "cannot find rc.xml config file"); - goto no_config; - } + for (struct wl_list *elm = iter(&paths); elm != &paths; elm = iter(elm)) { + struct path *path = wl_container_of(elm, path, link); + FILE *stream = fopen(path->string, "r"); + if (!stream) { + continue; + } - /* Reading file into buffer before parsing - better for unit tests */ - stream = fopen(rcxml, "r"); - if (!stream) { - wlr_log(WLR_ERROR, "cannot read (%s)", rcxml); - goto no_config; - } - wlr_log(WLR_INFO, "read config file %s", rcxml); - buf_init(&b); - while (getline(&line, &len, stream) != -1) { - char *p = strrchr(line, '\n'); - if (p) { - *p = '\0'; + wlr_log(WLR_INFO, "read config file %s", path->string); + + buf_init(&b); + char *line = NULL; + size_t len = 0; + while (getline(&line, &len, stream) != -1) { + char *p = strrchr(line, '\n'); + if (p) { + *p = '\0'; + } + buf_add(&b, line); } - buf_add(&b, line); - } - free(line); - fclose(stream); - rcxml_parse_xml(&b); - free(b.buf); -no_config: + zfree(line); + fclose(stream); + rcxml_parse_xml(&b); + zfree(b.buf); + if (!should_merge_config) { + break; + } + }; + paths_destroy(&paths); post_processing(); validate(); } diff --git a/src/config/session.c b/src/config/session.c index 00fbabaf..6489752c 100644 --- a/src/config/session.c +++ b/src/config/session.c @@ -7,10 +7,12 @@ #include #include #include "common/buf.h" +#include "common/dir.h" #include "common/file-helpers.h" #include "common/spawn.h" #include "common/string-helpers.h" #include "config/session.h" +#include "labwc.h" static bool string_empty(const char *s) @@ -45,14 +47,15 @@ error: free(value.buf); } -static void +/* return true on successful read */ +static bool read_environment_file(const char *filename) { char *line = NULL; size_t len = 0; FILE *stream = fopen(filename, "r"); if (!stream) { - return; + return false; } wlr_log(WLR_INFO, "read environment file %s", filename); while (getline(&line, &len, stream) != -1) { @@ -64,15 +67,7 @@ read_environment_file(const char *filename) } free(line); fclose(stream); -} - -static char * -build_path(const char *dir, const char *filename) -{ - if (string_empty(dir) || string_empty(filename)) { - return NULL; - } - return strdup_printf("%s/%s", dir, filename); + return true; } static void @@ -96,7 +91,7 @@ update_activation_env(const char *env_keys) } void -session_environment_init(const char *dir) +session_environment_init(void) { /* * Set default for XDG_CURRENT_DESKTOP so xdg-desktop-portal-wlr is happy. @@ -114,32 +109,49 @@ session_environment_init(const char *dir) */ setenv("_JAVA_AWT_WM_NONREPARENTING", "1", 0); - char *environment = build_path(dir, "environment"); - if (!environment) { - return; + struct wl_list paths; + paths_config_create(&paths, "environment"); + + bool should_merge_config = rc.merge_config; + struct wl_list *(*iter)(struct wl_list *list); + iter = should_merge_config ? paths_get_prev : paths_get_next; + + for (struct wl_list *elm = iter(&paths); elm != &paths; elm = iter(elm)) { + struct path *path = wl_container_of(elm, path, link); + bool success = read_environment_file(path->string); + if (success && !should_merge_config) { + break; + } } - read_environment_file(environment); - free(environment); + paths_destroy(&paths); } void -session_autostart_init(const char *dir) +session_autostart_init(void) { /* Update dbus and systemd user environment, each may fail gracefully */ update_activation_env("DISPLAY WAYLAND_DISPLAY XDG_CURRENT_DESKTOP"); - char *autostart = build_path(dir, "autostart"); - if (!autostart) { - return; - } - if (!file_exists(autostart)) { - wlr_log(WLR_ERROR, "no autostart file"); - goto out; + struct wl_list paths; + paths_config_create(&paths, "autostart"); + + bool should_merge_config = rc.merge_config; + struct wl_list *(*iter)(struct wl_list *list); + iter = should_merge_config ? paths_get_prev : paths_get_next; + + for (struct wl_list *elm = iter(&paths); elm != &paths; elm = iter(elm)) { + struct path *path = wl_container_of(elm, path, link); + if (!file_exists(path->string)) { + continue; + } + wlr_log(WLR_INFO, "run autostart file %s", path->string); + char *cmd = strdup_printf("sh %s", path->string); + spawn_async_no_shell(cmd); + free(cmd); + + if (!should_merge_config) { + break; + } } - wlr_log(WLR_INFO, "run autostart file %s", autostart); - char *cmd = strdup_printf("sh %s", autostart); - spawn_async_no_shell(cmd); - free(cmd); -out: - free(autostart); + paths_destroy(&paths); } diff --git a/src/main.c b/src/main.c index 8aeffc7e..6ccf227b 100644 --- a/src/main.c +++ b/src/main.c @@ -21,6 +21,7 @@ static const struct option long_options[] = { {"debug", no_argument, NULL, 'd'}, {"exit", no_argument, NULL, 'e'}, {"help", no_argument, NULL, 'h'}, + {"merge-config", no_argument, NULL, 'm'}, {"reconfigure", no_argument, NULL, 'r'}, {"startup", required_argument, NULL, 's'}, {"version", no_argument, NULL, 'v'}, @@ -35,6 +36,7 @@ static const char labwc_usage[] = " -d, --debug Enable full logging, including debug information\n" " -e, --exit Exit the compositor\n" " -h, --help Show help message and quit\n" +" -m, --merge-config Merge user config files/theme in all XDG Base Dirs\n" " -r, --reconfigure Reload the compositor configuration\n" " -s, --startup Run command on startup\n" " -v, --version Show version number and quit\n" @@ -91,7 +93,7 @@ main(int argc, char *argv[]) int c; while (1) { int index = 0; - c = getopt_long(argc, argv, "c:C:dehrs:vV", long_options, &index); + c = getopt_long(argc, argv, "c:C:dehmrs:vV", long_options, &index); if (c == -1) { break; } @@ -100,7 +102,7 @@ main(int argc, char *argv[]) config_file = optarg; break; case 'C': - rc.config_dir = xstrdup(optarg); + rc.config_dir = optarg; break; case 'd': verbosity = WLR_DEBUG; @@ -108,6 +110,9 @@ main(int argc, char *argv[]) case 'e': send_signal_to_labwc_pid(SIGTERM); exit(0); + case 'm': + rc.merge_config = true; + break; case 'r': send_signal_to_labwc_pid(SIGHUP); exit(0); @@ -133,11 +138,7 @@ main(int argc, char *argv[]) die_on_detecting_suid(); - if (!rc.config_dir) { - rc.config_dir = config_dir(); - } - wlr_log(WLR_INFO, "using config dir (%s)\n", rc.config_dir); - session_environment_init(rc.config_dir); + session_environment_init(); rcxml_read(config_file); /* @@ -171,7 +172,7 @@ main(int argc, char *argv[]) menu_init(&server); - session_autostart_init(rc.config_dir); + session_autostart_init(); if (startup_cmd) { spawn_async_no_shell(startup_cmd); } diff --git a/src/menu/menu.c b/src/menu/menu.c index 5790caf5..6ac0b752 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -11,6 +11,7 @@ #include #include "action.h" #include "common/buf.h" +#include "common/dir.h" #include "common/font.h" #include "common/list.h" #include "common/match.h" @@ -548,21 +549,27 @@ err: static void parse_xml(const char *filename, struct server *server) { - static char buf[4096] = { 0 }; + struct wl_list paths; + paths_config_create(&paths, filename); - if (!rc.config_dir) { - return; - } - snprintf(buf, sizeof(buf), "%s/%s", rc.config_dir, filename); + bool should_merge_config = rc.merge_config; + struct wl_list *(*iter)(struct wl_list *list); + iter = should_merge_config ? paths_get_prev : paths_get_next; - FILE *stream = fopen(buf, "r"); - if (!stream) { - wlr_log(WLR_ERROR, "cannot read %s", buf); - return; + for (struct wl_list *elm = iter(&paths); elm != &paths; elm = iter(elm)) { + struct path *path = wl_container_of(elm, path, link); + FILE *stream = fopen(path->string, "r"); + if (!stream) { + return; + } + wlr_log(WLR_INFO, "read menu file %s", path->string); + parse(server, stream); + fclose(stream); + if (!should_merge_config) { + break; + } } - wlr_log(WLR_INFO, "read menu file %s", buf); - parse(server, stream); - fclose(stream); + paths_destroy(&paths); } static int diff --git a/src/server.c b/src/server.c index df4aface..7e78265b 100644 --- a/src/server.c +++ b/src/server.c @@ -67,7 +67,7 @@ reload_config_and_theme(void) static int handle_sighup(int signal, void *data) { - session_environment_init(rc.config_dir); + session_environment_init(); reload_config_and_theme(); return 0; } diff --git a/src/theme.c b/src/theme.c index 7d1c3bdf..856889a8 100644 --- a/src/theme.c +++ b/src/theme.c @@ -23,6 +23,7 @@ #include "common/font.h" #include "common/graphic-helpers.h" #include "common/match.h" +#include "common/mem.h" #include "common/string-helpers.h" #include "config/rcxml.h" #include "button/button-png.h" @@ -664,60 +665,36 @@ process_line(struct theme *theme, char *line) } static void -theme_read(struct theme *theme, const char *theme_name) +theme_read(struct theme *theme, struct wl_list *paths) { - FILE *stream = NULL; - char *line = NULL; - size_t len = 0; - char themerc[4096]; - - if (strlen(theme_dir(theme_name))) { - snprintf(themerc, sizeof(themerc), "%s/themerc", - theme_dir(theme_name)); - stream = fopen(themerc, "r"); - } - if (!stream) { - if (theme_name) { - wlr_log(WLR_INFO, "cannot find theme %s", theme_name); - } - return; - } - wlr_log(WLR_INFO, "read theme %s", themerc); - while (getline(&line, &len, stream) != -1) { - char *p = strrchr(line, '\n'); - if (p) { - *p = '\0'; + bool should_merge_config = rc.merge_config; + struct wl_list *(*iter)(struct wl_list *list); + iter = should_merge_config ? paths_get_prev : paths_get_next; + + for (struct wl_list *elm = iter(paths); elm != paths; elm = iter(elm)) { + struct path *path = wl_container_of(elm, path, link); + FILE *stream = fopen(path->string, "r"); + if (!stream) { + continue; } - process_line(theme, line); - } - free(line); - fclose(stream); -} -static void -theme_read_override(struct theme *theme) -{ - char f[4096] = { 0 }; - snprintf(f, sizeof(f), "%s/themerc-override", rc.config_dir); - - FILE *stream = fopen(f, "r"); - if (!stream) { - wlr_log(WLR_INFO, "no theme override '%s'", f); - return; - } + wlr_log(WLR_INFO, "read theme %s", path->string); - wlr_log(WLR_INFO, "read theme-override %s", f); - char *line = NULL; - size_t len = 0; - while (getline(&line, &len, stream) != -1) { - char *p = strrchr(line, '\n'); - if (p) { - *p = '\0'; + char *line = NULL; + size_t len = 0; + while (getline(&line, &len, stream) != -1) { + char *p = strrchr(line, '\n'); + if (p) { + *p = '\0'; + } + process_line(theme, line); + } + zfree(line); + fclose(stream); + if (!should_merge_config) { + break; } - process_line(theme, line); } - free(line); - fclose(stream); } struct rounded_corner_ctx { @@ -1001,10 +978,15 @@ theme_init(struct theme *theme, const char *theme_name) theme_builtin(theme); /* Read /share/themes/$theme_name/openbox-3/themerc */ - theme_read(theme, theme_name); + struct wl_list paths; + paths_theme_create(&paths, theme_name, "themerc"); + theme_read(theme, &paths); + paths_destroy(&paths); /* Read /labwc/themerc-override */ - theme_read_override(theme); + paths_config_create(&paths, "themerc-override"); + theme_read(theme, &paths); + paths_destroy(&paths); post_processing(theme); create_corners(theme); -- 2.52.0