]> git.mdlowis.com Git - proto/labwc.git/commitdiff
config: support merging multiple config files
authorJohan Malm <jgm323@gmail.com>
Tue, 9 Jan 2024 22:00:45 +0000 (22:00 +0000)
committerJohan Malm <johanmalm@users.noreply.github.com>
Thu, 18 Jan 2024 20:20:36 +0000 (20:20 +0000)
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

14 files changed:
docs/labwc-config.5.scd
docs/labwc-theme.5.scd
docs/labwc.1.scd
include/common/dir.h
include/config/rcxml.h
include/config/session.h
src/button/common.c
src/common/dir.c
src/config/rcxml.c
src/config/session.c
src/main.c
src/menu/menu.c
src/server.c
src/theme.c

index b57cb3db55243cac60e26aaae45dbc9eb5a5753f..f6345d4e4a19f19a14713f9d285e74081af8f738 100644 (file)
@@ -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.
 
index 689e48c12e29122e7496d090d52c60f0937e6e28..dabfa6a6c2ff478a14a3416f7e02f4dcedca7a68 100644 (file)
@@ -15,6 +15,9 @@ searched for in the following order:
 - /usr/local/share/themes/<theme-name>/openbox-3/
 - /opt/share/themes/<theme-name>/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 <name> key in the <theme> section of
 the rc.xml configuration file (labwc-config(5)).
 
index 37e05a7d0bbd3b7e5d3d5be0011608df01483509..ca9f1ae9bd6d81de1fc8531b42c2ca5aae51faa0 100644 (file)
@@ -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
 
index ef03b5e79f30f48042f2717afed236c8d86dea53..bbb5e5663047e7592b5a572a5b12d5b86493d6b7 100644 (file)
@@ -2,12 +2,19 @@
 #ifndef LABWC_DIR_H
 #define LABWC_DIR_H
 
-char *config_dir(void);
+#include <wayland-server-core.h>
 
-/**
- * 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 */
index 4dede03192dab49ffce1f4b3c710ba94cb2dda7b..fa410116c56f10c5be63965405944c8e4ac1d3c6 100644 (file)
@@ -48,6 +48,7 @@ struct window_switcher_field {
 
 struct rcxml {
        char *config_dir;
+       bool merge_config;
 
        /* core */
        bool xdg_shell_server_side_deco;
index 4cc19b6e1ee534769872d75fb92b70ccda797a82..d1efbb60d250f047d81be0c05dfd7435e616d244 100644 (file)
@@ -4,17 +4,15 @@
 
 /**
  * session_environment_init - set enrivonment variables based on <key>=<value>
- * @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 */
index b5a3aaa734414509b2b7066cbbb038ce613cccd2..394009949683e758b9409dddc65e759c511781ce 100644 (file)
@@ -1,11 +1,27 @@
 // SPDX-License-Identifier: GPL-2.0-only
 #include <stdio.h>
+#include <unistd.h>
 #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);
 }
index 85dfea147e34c3590984171074fc6d19d258b876..59db4104fe661dfe73fa8ae30695024ced4b742e 100644 (file)
  *
  * Copyright Johan Malm 2020
  */
-
+#include <assert.h>
 #include <glib.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/stat.h>
 #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);
+       }
 }
index bc21a458d8f7d588206180e47022fd78392bbbfe..3f0e4ce994aa81ae5bd3d0ba23e22713d63ac89b 100644 (file)
@@ -14,6 +14,7 @@
 #include <wlr/util/box.h>
 #include <wlr/util/log.h>
 #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 <filename> */
+               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();
 }
index 00fbabafa8192fc7292aa2df043363f492604dbc..6489752c0a0acfa7fed26375b9c9e3f13245d2a8 100644 (file)
@@ -7,10 +7,12 @@
 #include <sys/stat.h>
 #include <wlr/util/log.h>
 #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);
 }
index 8aeffc7e2c1fcb12c5a1cd3ca9084d894ceb2fd7..6ccf227bd3e6ebfcd7ff4f8cc16d61b6975e0249 100644 (file)
@@ -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 <command>  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);
        }
index 5790caf5d383b6817a5527a64ed26d8342bfe09e..6ac0b752a66923c00aaeb30f86185cadb956a02b 100644 (file)
@@ -11,6 +11,7 @@
 #include <wlr/util/log.h>
 #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
index df4aface70711d81b278377fd165a905effa6ca7..7e78265b0d71953b90d93292eb0d625c54cc7ac3 100644 (file)
@@ -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;
 }
index 7d1c3bdfa6396f83279af71423aa9a7e3309571c..856889a832bb3898b01ece287dc83eedaa4b2200 100644 (file)
@@ -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 <data-dir>/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 <config-dir>/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);