From ac48cfe07dcd69beb06bc6ea7856be9945256f99 Mon Sep 17 00:00:00 2001 From: Artem Senichev Date: Fri, 10 Jan 2025 13:49:24 +0300 Subject: [PATCH] Overhaul configuration loader Removes default duplicates and adds a check on load. Signed-off-by: Artem Senichev --- src/application.c | 65 ++-- src/application.h | 16 +- src/config.c | 662 +++++++++++++++++++++++---------------- src/config.h | 130 ++++++-- src/font.c | 26 +- src/font.h | 2 +- src/gallery.c | 56 +--- src/gallery.h | 2 +- src/imagelist.c | 37 +-- src/imagelist.h | 10 +- src/info.c | 68 ++-- src/info.h | 2 +- src/keybind.c | 14 +- src/keybind.h | 2 +- src/main.c | 31 +- src/viewer.c | 71 +---- src/viewer.h | 16 +- test/config_test.cpp | 180 +++++++---- test/data/swayimg/config | 11 +- test/keybind_test.cpp | 29 +- 20 files changed, 751 insertions(+), 679 deletions(-) diff --git a/src/application.c b/src/application.c index abaf75c6..8b367903 100644 --- a/src/application.c +++ b/src/application.c @@ -276,28 +276,24 @@ static struct image* load_first_file(size_t index, bool force) * Load config. * @param cfg config instance */ -static void load_config(struct config* cfg) +static void load_config(const struct config* cfg) { const char* value; // startup mode - value = - config_get_string(cfg, APP_CFG_SECTION, APP_CFG_MODE, APP_MODE_VIEWER); - if (strcmp(value, APP_MODE_VIEWER) == 0) { - ctx.ehandler = viewer_handle; - } else if (strcmp(value, APP_MODE_GALLERY) == 0) { + static const char* modes[] = { CFG_MODE_VIEWER, CFG_MODE_GALLERY }; + if (config_get_oneof(cfg, CFG_GENERAL, CFG_GNRL_MODE, modes, + ARRAY_SIZE(modes)) == 1) { ctx.ehandler = gallery_handle; } else { ctx.ehandler = viewer_handle; - config_error_val(APP_CFG_SECTION, APP_CFG_MODE); } // initial window position ctx.window.x = POS_FROM_PARENT; ctx.window.y = POS_FROM_PARENT; - value = config_get_string(cfg, APP_CFG_SECTION, APP_CFG_POSITION, - APP_FROM_PARENT); - if (strcmp(value, APP_FROM_PARENT) != 0) { + value = config_get(cfg, CFG_GENERAL, CFG_GNRL_POSITION); + if (strcmp(value, CFG_FROM_PARENT) != 0) { struct str_slice slices[2]; ssize_t x, y; if (str_split(value, ',', slices, 2) == 2 && @@ -306,20 +302,19 @@ static void load_config(struct config* cfg) ctx.window.x = (ssize_t)x; ctx.window.y = (ssize_t)y; } else { - config_error_val(APP_CFG_SECTION, APP_CFG_POSITION); + config_error_val(CFG_GENERAL, CFG_GNRL_POSITION); } } // initial window size - value = - config_get_string(cfg, APP_CFG_SECTION, APP_CFG_SIZE, APP_FROM_PARENT); - if (strcmp(value, APP_FROM_PARENT) == 0) { + value = config_get(cfg, CFG_GENERAL, CFG_GNRL_SIZE); + if (strcmp(value, CFG_FROM_PARENT) == 0) { ctx.window.width = SIZE_FROM_PARENT; ctx.window.height = SIZE_FROM_PARENT; - } else if (strcmp(value, APP_FROM_IMAGE) == 0) { + } else if (strcmp(value, CFG_FROM_IMAGE) == 0) { ctx.window.width = SIZE_FROM_IMAGE; ctx.window.height = SIZE_FROM_IMAGE; - } else if (strcmp(value, APP_FULLSCREEN) == 0) { + } else if (strcmp(value, CFG_FULLSCREEN) == 0) { ctx.window.width = SIZE_FULLSCREEN; ctx.window.height = SIZE_FULLSCREEN; } else { @@ -334,30 +329,34 @@ static void load_config(struct config* cfg) } else { ctx.window.width = SIZE_FROM_PARENT; ctx.window.height = SIZE_FROM_PARENT; - config_error_val(APP_CFG_SECTION, APP_CFG_SIZE); + config_error_val(CFG_GENERAL, CFG_GNRL_SIZE); } } // signal actions - value = config_get(cfg, APP_CFG_SECTION, APP_CFG_SIGUSR1); - if (value && !action_create(value, &ctx.sigusr1)) { - config_error_val(APP_CFG_SECTION, APP_CFG_SIGUSR1); - } else { - action_create("reload", &ctx.sigusr1); + value = config_get(cfg, CFG_GENERAL, CFG_GNRL_SIGUSR1); + if (!action_create(value, &ctx.sigusr1)) { + config_error_val(CFG_GENERAL, CFG_GNRL_SIGUSR1); + value = config_get_default(CFG_GENERAL, CFG_GNRL_SIGUSR1); + action_create(value, &ctx.sigusr1); } - value = config_get(cfg, APP_CFG_SECTION, APP_CFG_SIGUSR2); - if (value && !action_create(value, &ctx.sigusr2)) { - config_error_val(APP_CFG_SECTION, APP_CFG_SIGUSR2); - } else { - action_create("next_file", &ctx.sigusr2); + value = config_get(cfg, CFG_GENERAL, CFG_GNRL_SIGUSR2); + if (!action_create(value, &ctx.sigusr2)) { + config_error_val(CFG_GENERAL, CFG_GNRL_SIGUSR2); + value = config_get_default(CFG_GENERAL, CFG_GNRL_SIGUSR2); + action_create(value, &ctx.sigusr2); } // app id - value = config_get_string(cfg, APP_CFG_SECTION, APP_CFG_APP_ID, APP_NAME); + value = config_get(cfg, CFG_GENERAL, CFG_GNRL_APP_ID); + if (!*value) { + config_error_val(CFG_GENERAL, CFG_GNRL_APP_ID); + value = config_get_default(CFG_GENERAL, CFG_GNRL_APP_ID); + } str_dup(value, &ctx.app_id); } -bool app_init(struct config* cfg, const char** sources, size_t num) +bool app_init(const struct config* cfg, const char** sources, size_t num) { bool force_load = false; struct image* first_image; @@ -433,8 +432,8 @@ bool app_init(struct config* cfg, const char** sources, size_t num) // set mode for info if (info_enabled()) { - info_switch(ctx.ehandler == viewer_handle ? APP_MODE_VIEWER - : APP_MODE_GALLERY); + info_switch(ctx.ehandler == viewer_handle ? CFG_MODE_VIEWER + : CFG_MODE_GALLERY); } // set signal handler @@ -550,10 +549,10 @@ void app_switch_mode(size_t index) if (ctx.ehandler == viewer_handle) { ctx.ehandler = gallery_handle; - info_mode = APP_MODE_GALLERY; + info_mode = CFG_MODE_GALLERY; } else { ctx.ehandler = viewer_handle; - info_mode = APP_MODE_VIEWER; + info_mode = CFG_MODE_VIEWER; } ctx.ehandler(&event); diff --git a/src/application.h b/src/application.h index 277eca26..68358959 100644 --- a/src/application.h +++ b/src/application.h @@ -7,20 +7,6 @@ #include "image.h" #include "keybind.h" -// Configuration parameters -#define APP_CFG_SECTION "general" -#define APP_CFG_MODE "mode" -#define APP_CFG_POSITION "position" -#define APP_CFG_SIZE "size" -#define APP_CFG_SIGUSR1 "sigusr1" -#define APP_CFG_SIGUSR2 "sigusr2" -#define APP_CFG_APP_ID "app_id" -#define APP_MODE_VIEWER "viewer" -#define APP_MODE_GALLERY "gallery" -#define APP_FROM_PARENT "parent" -#define APP_FROM_IMAGE "image" -#define APP_FULLSCREEN "fullscreen" - /** * Handler of the fd poll events. * @param data user data @@ -34,7 +20,7 @@ typedef void (*fd_callback)(void* data); * @param num number of sources in the list * @return true if application initialized successfully */ -bool app_init(struct config* cfg, const char** sources, size_t num); +bool app_init(const struct config* cfg, const char** sources, size_t num); /** * Destroy global application context. diff --git a/src/config.c b/src/config.c index 7821bf50..c41bb83e 100644 --- a/src/config.c +++ b/src/config.c @@ -18,135 +18,137 @@ struct config_default { const char* key; const char* value; }; +// clang-format off static const struct config_default defaults[] = { - { "general", "mode", "viewer" }, - { "general", "position", "parent" }, - { "general", "size", "parent" }, - { "general", "sigusr1", "reload" }, - { "general", "sigusr2", "next_file" }, - { "general", "app_id", "swayimg" }, - - { "viewer", "window", "#00000000" }, - { "viewer", "transparency", "grid" }, - { "viewer", "scale", "optimal" }, - { "viewer", "position", "center" }, - { "viewer", "fixed", "yes" }, - { "viewer", "antialiasing", "no" }, - { "viewer", "scale_method", "mks13" }, - { "viewer", "slideshow", "no" }, - { "viewer", "slideshow_time", "3" }, - { "viewer", "history", "1" }, - { "viewer", "preload", "1" }, - - { "gallery", "size", "200" }, - { "gallery", "cache", "100" }, - { "gallery", "fill", "yes" }, - { "gallery", "antialiasing", "no" }, - { "gallery", "scale_method", "mks13" }, - { "gallery", "window", "#00000000" }, - { "gallery", "background", "#202020ff" }, - { "gallery", "select", "#404040ff" }, - { "gallery", "border", "#000000ff" }, - { "gallery", "shadow", "#000000ff" }, - - { "list", "order", "alpha" }, - { "list", "loop", "yes" }, - { "list", "recursive", "no" }, - { "list", "all", "no" }, - - { "font", "name", "monospace" }, - { "font", "size", "14" }, - { "font", "color", "#ccccccff" }, - { "font", "shadow", "#000000d0" }, - { "font", "background", "#00000000" }, - - { "info", "show", "yes" }, - { "info", "info_timeout", "5" }, - { "info", "status_timeout", "3" }, - - { "info.viewer", "top_left", "+name,+format,+filesize,+imagesize,+exif" }, - { "info.viewer", "top_right", "index" }, - { "info.viewer", "bottom_left", "scale,frame" }, - { "info.viewer", "bottom_right", "status" }, - - { "info.gallery", "top_left", "none" }, - { "info.gallery", "top_right", "none" }, - { "info.gallery", "bottom_left", "none" }, - { "info.gallery", "bottom_right", "name,status" }, - - { "keys.viewer", "F1", "help" }, - { "keys.viewer", "Home", "first_file" }, - { "keys.viewer", "End", "last_file" }, - { "keys.viewer", "Prior", "prev_file" }, - { "keys.viewer", "Next", "next_file" }, - { "keys.viewer", "Space", "next_file" }, - { "keys.viewer", "Shift+d", "prev_dir" }, - { "keys.viewer", "d", "next_dir" }, - { "keys.viewer", "Shift+r", "rand_file" }, - { "keys.viewer", "Shift+o", "prev_frame" }, - { "keys.viewer", "o", "next_frame" }, - { "keys.viewer", "c", "skip_file" }, - { "keys.viewer", "Shift+s", "slideshow" }, - { "keys.viewer", "s", "animation" }, - { "keys.viewer", "f", "fullscreen" }, - { "keys.viewer", "Return", "mode" }, - { "keys.viewer", "Left", "step_left 10" }, - { "keys.viewer", "Right", "step_right 10" }, - { "keys.viewer", "Up", "step_up 10" }, - { "keys.viewer", "Down", "step_down 10" }, - { "keys.viewer", "Equal", "zoom +10" }, - { "keys.viewer", "Plus", "zoom +10" }, - { "keys.viewer", "Minus", "zoom -10" }, - { "keys.viewer", "w", "zoom width" }, - { "keys.viewer", "Shift+w", "zoom height" }, - { "keys.viewer", "z", "zoom fit" }, - { "keys.viewer", "Shift+z", "zoom fill" }, - { "keys.viewer", "0", "zoom real" }, - { "keys.viewer", "BackSpace", "zoom optimal" }, - { "keys.viewer", "bracketleft", "rotate_left" }, - { "keys.viewer", "bracketright", "rotate_right" }, - { "keys.viewer", "m", "flip_vertical" }, - { "keys.viewer", "Shift+m", "flip_horizontal" }, - { "keys.viewer", "a", "antialiasing" }, - { "keys.viewer", "r", "reload" }, - { "keys.viewer", "i", "info" }, - { "keys.viewer", "Shift+Delete", "exec rm '%'; skip_file" }, - { "keys.viewer", "Escape", "exit" }, - { "keys.viewer", "q", "exit" }, - { "keys.viewer", "ScrollLeft", "step_right 5" }, - { "keys.viewer", "ScrollRight", "step_left 5" }, - { "keys.viewer", "ScrollUp", "step_up 5" }, - { "keys.viewer", "ScrollDown", "step_down 5" }, - { "keys.viewer", "Ctrl+ScrollUp", "zoom +10" }, - { "keys.viewer", "Ctrl+ScrollDown", "zoom -10" }, - { "keys.viewer", "Shift+ScrollUp", "prev_file" }, - { "keys.viewer", "Shift+ScrollDown", "next_file" }, - { "keys.viewer", "Alt+ScrollUp", "prev_frame" }, - { "keys.viewer", "Alt+ScrollDown", "next_frame" }, - - { "keys.gallery", "F1", "help" }, - { "keys.gallery", "Home", "first_file" }, - { "keys.gallery", "End", "last_file" }, - { "keys.gallery", "Left", "step_left" }, - { "keys.gallery", "Right", "step_right" }, - { "keys.gallery", "Up", "step_up" }, - { "keys.gallery", "Down", "step_down" }, - { "keys.gallery", "Prior", "page_up" }, - { "keys.gallery", "Next", "page_down" }, - { "keys.gallery", "c", "skip_file" }, - { "keys.gallery", "f", "fullscreen" }, - { "keys.gallery", "Return", "mode" }, - { "keys.gallery", "a", "antialiasing" }, - { "keys.gallery", "r", "reload" }, - { "keys.gallery", "i", "info" }, - { "keys.gallery", "Shift+Delete", "exec rm '%'; skip_file" }, - { "keys.gallery", "Escape", "exit" }, - { "keys.gallery", "q", "exit" }, - { "keys.gallery", "ScrollLeft", "step_right" }, - { "keys.gallery", "ScrollRight", "step_left" }, - { "keys.gallery", "ScrollUp", "step_up" }, - { "keys.gallery", "ScrollDown", "step_down" }, + { CFG_GENERAL, CFG_GNRL_MODE, "viewer" }, + { CFG_GENERAL, CFG_GNRL_POSITION, "parent" }, + { CFG_GENERAL, CFG_GNRL_SIZE, "parent" }, + { CFG_GENERAL, CFG_GNRL_SIGUSR1, "reload" }, + { CFG_GENERAL, CFG_GNRL_SIGUSR2, "next_file" }, + { CFG_GENERAL, CFG_GNRL_APP_ID, "swayimg" }, + + { CFG_VIEWER, CFG_VIEW_WINDOW, "#00000000" }, + { CFG_VIEWER, CFG_VIEW_TRANSP, "grid" }, + { CFG_VIEWER, CFG_VIEW_SCALE, "optimal" }, + { CFG_VIEWER, CFG_VIEW_POSITION, "center" }, + { CFG_VIEWER, CFG_VIEW_FIXED, CFG_YES }, + { CFG_VIEWER, CFG_VIEW_AA, CFG_NO }, + { CFG_VIEWER, CFG_VIEW_AA_METHOD, "mks13" }, + { CFG_VIEWER, CFG_VIEW_SSHOW, CFG_NO }, + { CFG_VIEWER, CFG_VIEW_SSHOW_TM, "3" }, + { CFG_VIEWER, CFG_VIEW_HISTORY, "1" }, + { CFG_VIEWER, CFG_VIEW_PRELOAD, "1" }, + + { CFG_GALLERY, CFG_GLRY_SIZE, "200" }, + { CFG_GALLERY, CFG_GLRY_CACHE, "100" }, + { CFG_GALLERY, CFG_GLRY_FILL, CFG_YES }, + { CFG_GALLERY, CFG_GLRY_AA, CFG_NO }, + { CFG_GALLERY, CFG_GLRY_AA_METHOD, "mks13" }, + { CFG_GALLERY, CFG_GLRY_WINDOW, "#00000000" }, + { CFG_GALLERY, CFG_GLRY_BKG, "#202020ff" }, + { CFG_GALLERY, CFG_GLRY_SELECT, "#404040ff" }, + { CFG_GALLERY, CFG_GLRY_BORDER, "#000000ff" }, + { CFG_GALLERY, CFG_GLRY_SHADOW, "#000000ff" }, + + { CFG_LIST, CFG_LIST_ORDER, "alpha" }, + { CFG_LIST, CFG_LIST_LOOP, CFG_YES }, + { CFG_LIST, CFG_LIST_RECURSIVE, CFG_NO }, + { CFG_LIST, CFG_LIST_ALL, CFG_NO }, + + { CFG_FONT, CFG_FONT_NAME, "monospace" }, + { CFG_FONT, CFG_FONT_SIZE, "14" }, + { CFG_FONT, CFG_FONT_COLOR, "#ccccccff" }, + { CFG_FONT, CFG_FONT_SHADOW, "#000000d0" }, + { CFG_FONT, CFG_FONT_BKG, "#00000000" }, + + { CFG_INFO, CFG_INFO_SHOW, CFG_YES }, + { CFG_INFO, CFG_INFO_ITIMEOUT, "5" }, + { CFG_INFO, CFG_INFO_STIMEOUT, "3" }, + + { CFG_INFO_VIEWER, CFG_INFO_TL, "+name,+format,+filesize,+imagesize,+exif" }, + { CFG_INFO_VIEWER, CFG_INFO_TR, "index" }, + { CFG_INFO_VIEWER, CFG_INFO_BL, "scale,frame" }, + { CFG_INFO_VIEWER, CFG_INFO_BR, "status" }, + + { CFG_INFO_GALLERY, CFG_INFO_TL, "none" }, + { CFG_INFO_GALLERY, CFG_INFO_TR, "none" }, + { CFG_INFO_GALLERY, CFG_INFO_BL, "none" }, + { CFG_INFO_GALLERY, CFG_INFO_BR, "name,status" }, + + { CFG_KEYS_VIEWER, "F1", "help" }, + { CFG_KEYS_VIEWER, "Home", "first_file" }, + { CFG_KEYS_VIEWER, "End", "last_file" }, + { CFG_KEYS_VIEWER, "Prior", "prev_file" }, + { CFG_KEYS_VIEWER, "Next", "next_file" }, + { CFG_KEYS_VIEWER, "Space", "next_file" }, + { CFG_KEYS_VIEWER, "Shift+d", "prev_dir" }, + { CFG_KEYS_VIEWER, "d", "next_dir" }, + { CFG_KEYS_VIEWER, "Shift+r", "rand_file" }, + { CFG_KEYS_VIEWER, "Shift+o", "prev_frame" }, + { CFG_KEYS_VIEWER, "o", "next_frame" }, + { CFG_KEYS_VIEWER, "c", "skip_file" }, + { CFG_KEYS_VIEWER, "Shift+s", "slideshow" }, + { CFG_KEYS_VIEWER, "s", "animation" }, + { CFG_KEYS_VIEWER, "f", "fullscreen" }, + { CFG_KEYS_VIEWER, "Return", "mode" }, + { CFG_KEYS_VIEWER, "Left", "step_left 10" }, + { CFG_KEYS_VIEWER, "Right", "step_right 10" }, + { CFG_KEYS_VIEWER, "Up", "step_up 10" }, + { CFG_KEYS_VIEWER, "Down", "step_down 10" }, + { CFG_KEYS_VIEWER, "Equal", "zoom +10" }, + { CFG_KEYS_VIEWER, "Plus", "zoom +10" }, + { CFG_KEYS_VIEWER, "Minus", "zoom -10" }, + { CFG_KEYS_VIEWER, "w", "zoom width" }, + { CFG_KEYS_VIEWER, "Shift+w", "zoom height" }, + { CFG_KEYS_VIEWER, "z", "zoom fit" }, + { CFG_KEYS_VIEWER, "Shift+z", "zoom fill" }, + { CFG_KEYS_VIEWER, "0", "zoom real" }, + { CFG_KEYS_VIEWER, "BackSpace", "zoom optimal" }, + { CFG_KEYS_VIEWER, "bracketleft", "rotate_left" }, + { CFG_KEYS_VIEWER, "bracketright", "rotate_right" }, + { CFG_KEYS_VIEWER, "m", "flip_vertical" }, + { CFG_KEYS_VIEWER, "Shift+m", "flip_horizontal" }, + { CFG_KEYS_VIEWER, "a", "antialiasing" }, + { CFG_KEYS_VIEWER, "r", "reload" }, + { CFG_KEYS_VIEWER, "i", "info" }, + { CFG_KEYS_VIEWER, "Shift+Delete", "exec rm '%'; skip_file" }, + { CFG_KEYS_VIEWER, "Escape", "exit" }, + { CFG_KEYS_VIEWER, "q", "exit" }, + { CFG_KEYS_VIEWER, "ScrollLeft", "step_right 5" }, + { CFG_KEYS_VIEWER, "ScrollRight", "step_left 5" }, + { CFG_KEYS_VIEWER, "ScrollUp", "step_up 5" }, + { CFG_KEYS_VIEWER, "ScrollDown", "step_down 5" }, + { CFG_KEYS_VIEWER, "Ctrl+ScrollUp", "zoom +10" }, + { CFG_KEYS_VIEWER, "Ctrl+ScrollDown", "zoom -10" }, + { CFG_KEYS_VIEWER, "Shift+ScrollUp", "prev_file" }, + { CFG_KEYS_VIEWER, "Shift+ScrollDown", "next_file" }, + { CFG_KEYS_VIEWER, "Alt+ScrollUp", "prev_frame" }, + { CFG_KEYS_VIEWER, "Alt+ScrollDown", "next_frame" }, + + { CFG_KEYS_GALLERY, "F1", "help" }, + { CFG_KEYS_GALLERY, "Home", "first_file" }, + { CFG_KEYS_GALLERY, "End", "last_file" }, + { CFG_KEYS_GALLERY, "Left", "step_left" }, + { CFG_KEYS_GALLERY, "Right", "step_right" }, + { CFG_KEYS_GALLERY, "Up", "step_up" }, + { CFG_KEYS_GALLERY, "Down", "step_down" }, + { CFG_KEYS_GALLERY, "Prior", "page_up" }, + { CFG_KEYS_GALLERY, "Next", "page_down" }, + { CFG_KEYS_GALLERY, "c", "skip_file" }, + { CFG_KEYS_GALLERY, "f", "fullscreen" }, + { CFG_KEYS_GALLERY, "Return", "mode" }, + { CFG_KEYS_GALLERY, "a", "antialiasing" }, + { CFG_KEYS_GALLERY, "r", "reload" }, + { CFG_KEYS_GALLERY, "i", "info" }, + { CFG_KEYS_GALLERY, "Shift+Delete", "exec rm '%'; skip_file" }, + { CFG_KEYS_GALLERY, "Escape", "exit" }, + { CFG_KEYS_GALLERY, "q", "exit" }, + { CFG_KEYS_GALLERY, "ScrollLeft", "step_right" }, + { CFG_KEYS_GALLERY, "ScrollRight", "step_left" }, + { CFG_KEYS_GALLERY, "ScrollUp", "step_up" }, + { CFG_KEYS_GALLERY, "ScrollDown", "step_down" }, }; +// clang-format on /** Config file location. */ struct location { @@ -161,44 +163,6 @@ static const struct location config_locations[] = { { NULL, "/etc/xdg/swayimg/config" } }; -/** - * Create key/value entry. - * @param key,value config param - * @return key/value entry - */ -static struct config_keyval* create_keyval(const char* key, const char* value) -{ - const size_t key_sz = strlen(key) + 1 /*last null*/; - const size_t value_sz = strlen(value) + 1 /*last null*/; - - struct config_keyval* kv = - calloc(1, sizeof(struct config_keyval) + key_sz + value_sz); - if (kv) { - kv->key = (char*)kv + sizeof(struct config_keyval); - memcpy(kv->key, key, key_sz); - kv->value = (char*)kv + sizeof(struct config_keyval) + key_sz; - memcpy(kv->value, value, value_sz); - } - - return kv; -} - -/** - * Get section entry. - * @param cfg config instance - * @param name section name - * @return pointer to section entry or NULL if not found - */ -static struct config* get_section(struct config* cfg, const char* name) -{ - list_for_each(cfg, struct config, it) { - if (strcmp(name, it->name) == 0) { - return it; - } - } - return NULL; -} - /** * Expand path from environment variable. * @param prefix_env path prefix (var name) @@ -237,10 +201,11 @@ static char* expand_path(const char* prefix_env, const char* postfix) /** * Load configuration from a file. + * @param cfg config instance * @param path full path to the file - * @return loaded config instance or NULL on errors + * @return false on errors */ -static bool load(const char* path, struct config** cfg) +static bool load(struct config* cfg, const char* path) { FILE* fd = NULL; char* buff = NULL; @@ -331,21 +296,129 @@ static bool load(const char* path, struct config** cfg) return true; } +/** + * Create key/value entry. + * @param key,value config param + * @return key/value entry + */ +static struct config_keyval* create_keyval(const char* key, const char* value) +{ + const size_t key_sz = strlen(key) + 1 /*last null*/; + const size_t value_sz = strlen(value) + 1 /*last null*/; + + struct config_keyval* kv = + calloc(1, sizeof(struct config_keyval) + key_sz + value_sz); + if (kv) { + kv->key = (char*)kv + sizeof(struct config_keyval); + memcpy(kv->key, key, key_sz); + kv->value = (char*)kv + sizeof(struct config_keyval) + key_sz; + memcpy(kv->value, value, value_sz); + } + + return kv; +} + +/** + * Create section in the config instance. + * @param cfg config instance + * @param name section name + */ +static void create_section(struct config** cfg, const char* name) +{ + const size_t sz = strlen(name) + 1 /*last null*/; + struct config* section = calloc(1, sizeof(struct config) + sz); + if (section) { + section->name = (char*)section + sizeof(struct config); + memcpy(section->name, name, sz); + *cfg = list_add(*cfg, section); + } +} + +/** + * Convert text value to boolean config. + * @param text source config value + * @param value parsed value + * @param false if text has invalid format + */ +static bool text_to_bool(const char* text, bool* value) +{ + if (strcmp(text, CFG_YES) == 0) { + *value = true; + } else if (strcmp(text, CFG_NO) == 0) { + *value = false; + } else { + return false; + } + return true; +} + +/** + * Convert text value to color. + * @param text source config value + * @param value parsed value + * @param false if text has invalid format + */ +static bool text_to_color(const char* text, argb_t* value) +{ + char* endptr; + argb_t color; + + while (*text == '#' || isspace(*text)) { + ++text; + } + + errno = 0; + + color = strtoull(text, &endptr, 16); + + if (endptr && !*endptr && errno == 0) { + if (strlen(text) > 6) { // value with alpha (RRGGBBAA) + color = (color >> 8) | ARGB_SET_A(color); + } else { + color |= ARGB_SET_A(0xff); + } + *value = color; + return true; + } + + return false; +} + struct config* config_load(void) { struct config* cfg = NULL; - // set defaults + // create sections + create_section(&cfg, CFG_GENERAL); + create_section(&cfg, CFG_VIEWER); + create_section(&cfg, CFG_GALLERY); + create_section(&cfg, CFG_LIST); + create_section(&cfg, CFG_FONT); + create_section(&cfg, CFG_INFO); + create_section(&cfg, CFG_INFO_VIEWER); + create_section(&cfg, CFG_INFO_GALLERY); + create_section(&cfg, CFG_KEYS_VIEWER); + create_section(&cfg, CFG_KEYS_GALLERY); + + // load default config for (size_t i = 0; i < ARRAY_SIZE(defaults); ++i) { const struct config_default* def = &defaults[i]; - config_set(&cfg, def->section, def->key, def->value); + list_for_each(cfg, struct config, section) { + if (strcmp(def->section, section->name) == 0) { + struct config_keyval* kv = create_keyval(def->key, def->value); + if (kv) { + section->params = list_add(section->params, kv); + } + break; + } + } } // find and load first available config file for (size_t i = 0; i < ARRAY_SIZE(config_locations); ++i) { const struct location* cl = &config_locations[i]; char* path = expand_path(cl->prefix, cl->postfix); - const bool loaded = path && load(path, &cfg); + const bool loaded = path && load(cfg, path); free(path); if (loaded) { break; @@ -357,7 +430,6 @@ struct config* config_load(void) void config_free(struct config* cfg) { - // free resources list_for_each(cfg, struct config, section) { list_for_each(section->params, struct config_keyval, kv) { free(kv); @@ -366,56 +438,65 @@ void config_free(struct config* cfg) } } -void config_check(struct config* cfg) -{ - // sanity checker: all config parameters should be read - list_for_each(cfg, struct config, section) { - list_for_each(section->params, struct config_keyval, kv) { - if (!kv->used) { - fprintf(stderr, - "WARNING: Unknown config parameter \"%s = %s\" in " - "section \"%s\"\n", - kv->key, kv->value, section->name); - } - } - } -} - -void config_set(struct config** cfg, const char* section, const char* key, +bool config_set(struct config* cfg, const char* section, const char* key, const char* value) { - struct config_keyval* kv; - struct config* cs; + struct config_keyval* kv = NULL; + struct config* cs = NULL; + + if (!value || !*value) { + fprintf(stderr, + "WARNING: Empty config value for key \"%s\" in section \"%s\" " + "is not alowed\n", + key, section); + return false; + } - cs = get_section(*cfg, section); - if (!cs) { - // add new section - const size_t sz = strlen(section) + 1 /*last null*/; - cs = calloc(1, sizeof(struct config) + sz); - if (!cs) { - return; + // search for config section + list_for_each(cfg, struct config, it) { + if (strcmp(section, it->name) == 0) { + cs = it; + break; } - cs->name = (char*)cs + sizeof(struct config); - memcpy(cs->name, section, sz); - *cfg = list_add(*cfg, cs); - } else { - // remove existing entry - list_for_each(cs->params, struct config_keyval, it) { - if (strcmp(key, it->key) == 0) { - cs->params = list_remove(it); - free(it); - break; - } + } + if (!cs) { + fprintf(stderr, "WARNING: Unknown config section \"%s\"\n", section); + return false; + } + + // search for existing key/value + list_for_each(cs->params, struct config_keyval, it) { + if (strcmp(key, it->key) == 0) { + kv = it; + break; } } + // check if config key is valid for the current section + if (strcmp(section, CFG_KEYS_VIEWER) != 0 && + strcmp(section, CFG_KEYS_GALLERY) != 0 && !kv) { + fprintf(stderr, + "WARNING: Unknown config key \"%s\" in section \"%s\"\n", key, + section); + return false; + } + + // remove existing key/value + if (kv) { + cs->params = list_remove(kv); + free(kv); + } + + // add new key/value kv = create_keyval(key, value); if (kv) { cs->params = list_add(cs->params, kv); } + + return true; } -bool config_set_arg(struct config** cfg, const char* arg) +bool config_set_arg(struct config* cfg, const char* arg) { char section[32]; char key[32]; @@ -426,6 +507,8 @@ bool config_set_arg(struct config** cfg, const char* arg) // split section.key and value size = str_split(arg, '=', slices, ARRAY_SIZE(slices)); if (size <= 1) { + fprintf(stderr, "WARNING: Invalid config argument format: \"%s\"\n", + arg); return false; } @@ -433,6 +516,8 @@ bool config_set_arg(struct config** cfg, const char* arg) ptr = slices[0].value + slices[0].len; while (*ptr != '.') { if (--ptr < arg) { + fprintf(stderr, "WARNING: Invalid config argument format: \"%s\"\n", + arg); return false; } } @@ -454,107 +539,132 @@ bool config_set_arg(struct config** cfg, const char* arg) memcpy(key, ptr, size); key[size] = 0; - config_set(cfg, section, key, slices[1].value); - - return true; + return config_set(cfg, section, key, slices[1].value); } -const char* config_get(struct config* cfg, const char* section, const char* key) +const char* config_get_default(const char* section, const char* key) { - struct config* cs = get_section(cfg, section); + for (size_t i = 0; i < ARRAY_SIZE(defaults); ++i) { + const struct config_default* def = &defaults[i]; + if (strcmp(section, def->section) == 0 && strcmp(key, def->key) == 0) { + return def->value; + } + } + + fprintf( + stderr, + "WARNING: Default value for key \"%s\" in section \"%s\" not found\n", + key, section); + return ""; +} - if (cs) { - list_for_each(cs->params, struct config_keyval, it) { - if (strcmp(key, it->key) == 0) { - it->used = true; - return it->value; +const char* config_get(const struct config* cfg, const char* section, + const char* key) +{ + list_for_each(cfg, const struct config, cs) { + if (strcmp(section, cs->name) == 0) { + list_for_each(cs->params, const struct config_keyval, kv) { + if (strcmp(key, kv->key) == 0) { + const char* value = kv->value; + if (!*kv->value) { + value = config_get_default(section, key); + fprintf(stderr, + "WARNING: " + "Empty value for the key \"%s\" in section " + "\"%s\" is not allowed, " + "the default value \"%s\" will be used\n", + key, section, value); + } + return value; + } } } } - return NULL; + fprintf(stderr, + "WARNING: Value for key \"%s\" in section \"%s\" not found\n", key, + section); + return ""; } -const char* config_get_string(struct config* cfg, const char* section, - const char* key, const char* fallback) +ssize_t config_get_oneof(const struct config* cfg, const char* section, + const char* key, const char** array, size_t array_sz) { const char* value = config_get(cfg, section, key); - return value ? value : fallback; + ssize_t index = str_search_index(array, array_sz, value, 0); + + if (index == -1) { + fprintf(stderr, + "WARNING: " + "Invalid config value \"%s = %s\" in section \"%s\": " + "expected one of: ", + key, value, section); + for (size_t i = 0; i < array_sz; ++i) { + fprintf(stderr, "%s, ", array[i]); + } + value = config_get_default(section, key); + fprintf(stderr, "the default value \"%s\" will be used\n", value); + index = str_search_index(array, array_sz, value, 0); + } + + return index >= 0 ? index : 0; } -bool config_get_bool(struct config* cfg, const char* section, const char* key, - bool fallback) +bool config_get_bool(const struct config* cfg, const char* section, + const char* key) { + bool boolean = false; const char* value = config_get(cfg, section, key); - if (value) { - if (strcmp(value, "yes") == 0 || strcmp(value, "true") == 0) { - return true; - } else if (strcmp(value, "no") == 0 || strcmp(value, "false") == 0) { - return false; - } else { - fprintf(stderr, - "WARNING: " - "Invalid config value \"%s = %s\" in section \"%s\": " - "expected \"yes\" or \"no\"\n", - key, value, section); - } + if (!text_to_bool(value, &boolean)) { + text_to_bool(config_get_default(section, key), &boolean); + fprintf(stderr, + "WARNING: " + "Invalid config value \"%s = %s\" in section \"%s\": " + "expected \"" CFG_YES "\" or \"" CFG_NO "\", " + "the default value \"%s\" will be used\n", + key, value, section, boolean ? CFG_YES : CFG_NO); } - return fallback; + return boolean; } -ssize_t config_get_num(struct config* cfg, const char* section, const char* key, - ssize_t min_val, ssize_t max_val, ssize_t fallback) +ssize_t config_get_num(const struct config* cfg, const char* section, + const char* key, ssize_t min_val, ssize_t max_val) { + ssize_t num = 0; const char* value = config_get(cfg, section, key); - if (value) { - ssize_t num; - if (str_to_num(value, 0, &num, 0) && num >= min_val && num <= max_val) { - return num; - } else { - fprintf(stderr, - "WARNING: " - "Invalid config value \"%s = %s\" in section \"%s\": " - "expected integer in range %zd-%zd\n", - key, value, section, min_val, max_val); - } + if (!str_to_num(value, 0, &num, 0) || num < min_val || num > max_val) { + str_to_num(config_get_default(section, key), 0, &num, 0); + fprintf(stderr, + "WARNING: " + "Invalid config value \"%s = %s\" in section \"%s\": " + "expected integer in range %zd-%zd, " + "the default value %zd will be used\n", + key, value, section, min_val, max_val, num); } - return fallback; + return num; } -argb_t config_get_color(struct config* cfg, const char* section, - const char* key, argb_t fallback) +argb_t config_get_color(const struct config* cfg, const char* section, + const char* key) { + argb_t color = 0; const char* value = config_get(cfg, section, key); - if (value) { - char* endptr; - argb_t color; - while (*value == '#' || isspace(*value)) { - ++value; - } - errno = 0; - color = strtoull(value, &endptr, 16); - if (endptr && !*endptr && errno == 0) { - if (strlen(value) > 6) { // value with alpha (RRGGBBAA) - color = (color >> 8) | ARGB_SET_A(color); - } else { - color |= ARGB_SET_A(0xff); - } - return color; - } else { - fprintf(stderr, - "WARNING: " - "Invalid color value \"%s = %s\" in section \"%s\": " - "expected RGB(A) format, e.g. #11223344\n", - key, value, section); - } + if (!text_to_color(value, &color)) { + text_to_color(config_get_default(section, key), &color); + fprintf(stderr, + "WARNING: " + "Invalid color value \"%s = %s\" in section \"%s\": " + "expected RGB(A) format (e.g. #11223344), " + "the default value #%08x will be used\n", + key, value, section, color); } - return fallback; + return color; } void config_error_key(const char* section, const char* key) diff --git a/src/config.h b/src/config.h index a12c9ee8..c5af3083 100644 --- a/src/config.h +++ b/src/config.h @@ -12,7 +12,6 @@ struct config_keyval { struct list list; ///< Links to prev/next entry char* key; ///< Key char* value; ///< Value - bool used; ///< Sanity checker }; /** Config instance: list of sections with key/values */ @@ -22,9 +21,76 @@ struct config { struct config_keyval* params; ///< List of key/value for this section }; +// Section names +#define CFG_GENERAL "general" +#define CFG_VIEWER "viewer" +#define CFG_GALLERY "gallery" +#define CFG_LIST "list" +#define CFG_FONT "font" +#define CFG_INFO "info" +#define CFG_INFO_VIEWER "info.viewer" +#define CFG_INFO_GALLERY "info.gallery" +#define CFG_KEYS_VIEWER "keys.viewer" +#define CFG_KEYS_GALLERY "keys.gallery" + +// Configuration parameters +#define CFG_GNRL_MODE "mode" +#define CFG_GNRL_POSITION "position" +#define CFG_GNRL_SIZE "size" +#define CFG_GNRL_SIGUSR1 "sigusr1" +#define CFG_GNRL_SIGUSR2 "sigusr2" +#define CFG_GNRL_APP_ID "app_id" +#define CFG_VIEW_WINDOW "window" +#define CFG_VIEW_TRANSP "transparency" +#define CFG_VIEW_SCALE "scale" +#define CFG_VIEW_POSITION "position" +#define CFG_VIEW_FIXED "fixed" +#define CFG_VIEW_AA "antialiasing" +#define CFG_VIEW_AA_METHOD "scale_method" +#define CFG_VIEW_SSHOW "slideshow" +#define CFG_VIEW_SSHOW_TM "slideshow_time" +#define CFG_VIEW_HISTORY "history" +#define CFG_VIEW_PRELOAD "preload" +#define CFG_GLRY_SIZE "size" +#define CFG_GLRY_CACHE "cache" +#define CFG_GLRY_FILL "fill" +#define CFG_GLRY_AA "antialiasing" +#define CFG_GLRY_AA_METHOD "scale_method" +#define CFG_GLRY_WINDOW "window" +#define CFG_GLRY_BKG "background" +#define CFG_GLRY_SELECT "select" +#define CFG_GLRY_BORDER "border" +#define CFG_GLRY_SHADOW "shadow" +#define CFG_LIST_ORDER "order" +#define CFG_LIST_LOOP "loop" +#define CFG_LIST_RECURSIVE "recursive" +#define CFG_LIST_ALL "all" +#define CFG_FONT_NAME "name" +#define CFG_FONT_SIZE "size" +#define CFG_FONT_COLOR "color" +#define CFG_FONT_BKG "background" +#define CFG_FONT_SHADOW "shadow" +#define CFG_INFO_SHOW "show" +#define CFG_INFO_ITIMEOUT "info_timeout" +#define CFG_INFO_STIMEOUT "status_timeout" +#define CFG_INFO_CN "center" +#define CFG_INFO_TL "top_left" +#define CFG_INFO_TR "top_right" +#define CFG_INFO_BL "bottom_left" +#define CFG_INFO_BR "bottom_right" + +// Some configuration values +#define CFG_YES "yes" +#define CFG_NO "no" +#define CFG_MODE_VIEWER "viewer" +#define CFG_MODE_GALLERY "gallery" +#define CFG_FROM_PARENT "parent" +#define CFG_FROM_IMAGE "image" +#define CFG_FULLSCREEN "fullscreen" + /** * Load configuration from file. - * @return loaded config instance or NULL if no config file found + * @return loaded config instance */ struct config* config_load(void); @@ -34,73 +100,77 @@ struct config* config_load(void); */ void config_free(struct config* cfg); -/** - * Check if all configuration parameters were read. - * @param cfg config instance - */ -void config_check(struct config* cfg); - /** * Set config option. * @param cfg config instance * @param section section name * @param key,value configuration parameters + * @return false if section or key is unknown */ -void config_set(struct config** cfg, const char* section, const char* key, +bool config_set(struct config* cfg, const char* section, const char* key, const char* value); + /** * Set config option from command line argument. * @param cfg config instance * @param arg command in format: "section.key=value" - * @return false on invalid format + * @return false if section or key is unknown or argument is invalid */ -bool config_set_arg(struct config** cfg, const char* arg); +bool config_set_arg(struct config* cfg, const char* arg); + +/** + * Get default config option. + * @param section section name + * @param key,value configuration parameters + * @return default value for specified options + */ +const char* config_get_default(const char* section, const char* key); /** * Get config option. * @param cfg config instance * @param section section name * @param key,value configuration parameters - * @return value or NULL if section or key not found + * @return value from the config or default value */ -const char* config_get(struct config* cfg, const char* section, +const char* config_get(const struct config* cfg, const char* section, const char* key); + /** - * Get config parameter as string value. + * Get config parameter as string value restricted by specified array. * @param cfg config instance * @param section,key section name and key - * @param fallback default value used if parameter not found - * @return value or fallback value if section or key not found + * @param array array of possible values + * @param array_sz number of strings in possible values array + * @return index of the value or default value */ -const char* config_get_string(struct config* cfg, const char* section, - const char* key, const char* fallback); +ssize_t config_get_oneof(const struct config* cfg, const char* section, + const char* key, const char** array, size_t array_sz); + /** * Get config parameter as boolean value. * @param cfg config instance * @param section,key section name and key - * @param fallback default value used if parameter not found or invalid - * @return value or fallback value if section or key not found or invalid + * @return value or default value if user defined value is invalid */ -bool config_get_bool(struct config* cfg, const char* section, const char* key, - bool fallback); +bool config_get_bool(const struct config* cfg, const char* section, + const char* key); /** * Get config parameter as integer value. * @param cfg config instance * @param section,key section name and key - * @param fallback default value used if parameter not found or invalid - * @return value or fallback value if section or key not found or invalid + * @return value or default value if user defined value is invalid */ -ssize_t config_get_num(struct config* cfg, const char* section, const char* key, - ssize_t min_val, ssize_t max_val, ssize_t fallback); +ssize_t config_get_num(const struct config* cfg, const char* section, + const char* key, ssize_t min_val, ssize_t max_val); /** * Get config parameter as ARGB color value. * @param cfg config instance * @param section,key section name and key - * @param fallback default value used if parameter not found or invalid - * @return value or fallback value if section or key not found or invalid + * @return value or default value if user defined value is invalid */ -argb_t config_get_color(struct config* cfg, const char* section, - const char* key, argb_t fallback); +argb_t config_get_color(const struct config* cfg, const char* section, + const char* key); /** * Print error about invalid key format. * @param section section name diff --git a/src/font.c b/src/font.c index be8d9017..91ebfef9 100644 --- a/src/font.c +++ b/src/font.c @@ -12,19 +12,6 @@ #include FT_FREETYPE_H #include FT_GLYPH_H -// Configuration -#define CFG_SECTION "font" -#define CFG_NAME "name" -#define CFG_NAME_DEF "monospace" -#define CFG_SIZE "size" -#define CFG_SIZE_DEF 14 -#define CFG_COLOR "color" -#define CFG_COLOR_DEF ARGB(0xff, 0xcc, 0xcc, 0xcc) -#define CFG_BKG "background" -#define CFG_BKG_DEF ARGB(0, 0, 0, 0) -#define CFG_SHADOW "shadow" -#define CFG_SHADOW_DEF ARGB(0xd0, 0, 0, 0) - #define POINT_FACTOR 64.0 // default points per pixel for 26.6 format #define SPACE_WH_REL 2.0 @@ -133,14 +120,14 @@ static size_t allocate_surface(const wchar_t* text, return base_offset; } -void font_init(struct config* cfg) +void font_init(const struct config* cfg) { char font_file[256]; const char* font_name; size_t font_size; // load font - font_name = config_get_string(cfg, CFG_SECTION, CFG_NAME, CFG_NAME_DEF); + font_name = config_get(cfg, CFG_FONT, CFG_FONT_NAME); if (!search_font_file(font_name, font_file, sizeof(font_file)) || FT_Init_FreeType(&ctx.lib) != 0 || FT_New_Face(ctx.lib, font_file, 0, &ctx.face) != 0) { @@ -149,14 +136,13 @@ void font_init(struct config* cfg) } // set font size - font_size = - config_get_num(cfg, CFG_SECTION, CFG_SIZE, 1, 256, CFG_SIZE_DEF); + font_size = config_get_num(cfg, CFG_FONT, CFG_FONT_SIZE, 1, 256); FT_Set_Char_Size(ctx.face, font_size * POINT_FACTOR, 0, 96, 0); // color/background/shadow parameters - ctx.color = config_get_color(cfg, CFG_SECTION, CFG_COLOR, CFG_COLOR_DEF); - ctx.background = config_get_color(cfg, CFG_SECTION, CFG_BKG, CFG_BKG_DEF); - ctx.shadow = config_get_color(cfg, CFG_SECTION, CFG_SHADOW, CFG_SHADOW_DEF); + ctx.color = config_get_color(cfg, CFG_FONT, CFG_FONT_COLOR); + ctx.background = config_get_color(cfg, CFG_FONT, CFG_FONT_BKG); + ctx.shadow = config_get_color(cfg, CFG_FONT, CFG_FONT_SHADOW); } void font_destroy(void) diff --git a/src/font.h b/src/font.h index 5c447841..ddaf4008 100644 --- a/src/font.h +++ b/src/font.h @@ -17,7 +17,7 @@ struct text_surface { * Initialize global font context. * @param cfg config instance */ -void font_init(struct config* cfg); +void font_init(const struct config* cfg); /** * Destroy global font context. diff --git a/src/gallery.c b/src/gallery.c index d2f821fd..058bf6f7 100644 --- a/src/gallery.c +++ b/src/gallery.c @@ -12,29 +12,6 @@ #include -// Configuration parameters -#define CFG_SECTION "gallery" -#define CFG_SIZE "size" -#define CFG_SIZE_DEF 200 -#define CFG_CACHE "cache" -#define CFG_CACHE_DEF 100 -#define CFG_FILL "fill" -#define CFG_FILL_DEF true -#define CFG_ANTIALIASING "antialiasing" -#define CFG_ANTIALIASING_DEF false -#define CFG_SCALE_METHOD "scale_method" -#define CFG_SCALE_METHOD_DEF "mks13" -#define CFG_WINDOW "window" -#define CFG_WINDOW_DEF ARGB(0, 0, 0, 0) -#define CFG_BACKGROUND "background" -#define CFG_BACKGROUND_DEF ARGB(0xff, 0x20, 0x20, 0x20) -#define CFG_SELECT "select" -#define CFG_SELECT_DEF ARGB(0xff, 0x40, 0x40, 0x40) -#define CFG_BORDER "border" -#define CFG_BORDER_DEF ARGB(0xff, 0, 0, 0) -#define CFG_SHADOW "shadow" -#define CFG_SHADOW_DEF ARGB(0xff, 0, 0, 0) - // Scale for selected thumbnail #define THUMB_SELECTED_SCALE 1.15f @@ -579,39 +556,30 @@ static void on_image_load(struct image* image, size_t index) app_redraw(); } -void gallery_init(struct config* cfg, struct image* image) +void gallery_init(const struct config* cfg, struct image* image) { const char* value; ssize_t index; - ctx.thumb_size = - config_get_num(cfg, CFG_SECTION, CFG_SIZE, 1, 1024, CFG_SIZE_DEF); - ctx.thumb_max = - config_get_num(cfg, CFG_SECTION, CFG_CACHE, 0, 1024, CFG_CACHE_DEF); - ctx.thumb_fill = config_get_bool(cfg, CFG_SECTION, CFG_FILL, CFG_FILL_DEF); + ctx.thumb_size = config_get_num(cfg, CFG_GALLERY, CFG_GLRY_SIZE, 1, 1024); + ctx.thumb_max = config_get_num(cfg, CFG_GALLERY, CFG_GLRY_CACHE, 0, 1024); + ctx.thumb_fill = config_get_bool(cfg, CFG_GALLERY, CFG_GLRY_FILL); - ctx.antialiasing = config_get_bool(cfg, CFG_SECTION, CFG_ANTIALIASING, - CFG_ANTIALIASING_DEF); - value = config_get_string(cfg, CFG_SECTION, CFG_SCALE_METHOD, - CFG_SCALE_METHOD_DEF); + ctx.antialiasing = config_get_bool(cfg, CFG_GALLERY, CFG_GLRY_AA); + value = config_get(cfg, CFG_GALLERY, CFG_GLRY_AA_METHOD); index = pixmap_scale_index(value); if (index >= 0) { ctx.scale_method = index; } else { ctx.scale_method = pixmap_mks13; - config_error_val(CFG_SECTION, value); + config_error_val(CFG_GALLERY, value); } - ctx.clr_window = - config_get_color(cfg, CFG_SECTION, CFG_WINDOW, CFG_WINDOW_DEF); - ctx.clr_background = - config_get_color(cfg, CFG_SECTION, CFG_BACKGROUND, CFG_BACKGROUND_DEF); - ctx.clr_select = - config_get_color(cfg, CFG_SECTION, CFG_SELECT, CFG_SELECT_DEF); - ctx.clr_border = - config_get_color(cfg, CFG_SECTION, CFG_BORDER, CFG_BORDER_DEF); - ctx.clr_shadow = - config_get_color(cfg, CFG_SECTION, CFG_SHADOW, CFG_SHADOW_DEF); + ctx.clr_window = config_get_color(cfg, CFG_GALLERY, CFG_GLRY_WINDOW); + ctx.clr_background = config_get_color(cfg, CFG_GALLERY, CFG_GLRY_BKG); + ctx.clr_select = config_get_color(cfg, CFG_GALLERY, CFG_GLRY_SELECT); + ctx.clr_border = config_get_color(cfg, CFG_GALLERY, CFG_GLRY_BORDER); + ctx.clr_shadow = config_get_color(cfg, CFG_GALLERY, CFG_GLRY_SHADOW); ctx.top = image_list_first(); ctx.selected = ctx.top; diff --git a/src/gallery.h b/src/gallery.h index 2b7fddac..635543bc 100644 --- a/src/gallery.h +++ b/src/gallery.h @@ -12,7 +12,7 @@ * @param cfg config instance * @param image initial image to open */ -void gallery_init(struct config* cfg, struct image* image); +void gallery_init(const struct config* cfg, struct image* image); /** * Destroy global gallery context. diff --git a/src/imagelist.c b/src/imagelist.c index 8b453cd4..02faff31 100644 --- a/src/imagelist.c +++ b/src/imagelist.c @@ -12,12 +12,6 @@ #include #include -// Default configuration parameters -#define CFG_ORDER_DEF "alpha" -#define CFG_LOOP_DEF true -#define CFG_RECURSIVE_DEF false -#define CFG_ALL_DEF true - /** Context of the image list (which is actually an array). */ struct image_list { char** sources; ///< Array of entries @@ -222,32 +216,17 @@ static void shuffle_list(void) * Load config. * @param cfg config instance */ -static void load_config(struct config* cfg) +static void load_config(const struct config* cfg) { - ssize_t index; - const char* order; - - // list order - ctx.order = order_alpha; - order = - config_get_string(cfg, IMGLIST_SECTION, IMGLIST_ORDER, CFG_ORDER_DEF); - index = str_index(order_names, order, 0); - if (index >= 0) { - ctx.order = index; - } else { - config_error_val(IMGLIST_SECTION, IMGLIST_ORDER); - } - - // list modes - ctx.loop = - config_get_bool(cfg, IMGLIST_SECTION, IMGLIST_LOOP, CFG_LOOP_DEF); - ctx.recursive = config_get_bool(cfg, IMGLIST_SECTION, IMGLIST_RECURSIVE, - CFG_RECURSIVE_DEF); - ctx.all_files = - config_get_bool(cfg, IMGLIST_SECTION, IMGLIST_ALL, CFG_ALL_DEF); + ctx.order = config_get_oneof(cfg, CFG_LIST, CFG_LIST_ORDER, order_names, + ARRAY_SIZE(order_names)); + ctx.loop = config_get_bool(cfg, CFG_LIST, CFG_LIST_LOOP); + ctx.recursive = config_get_bool(cfg, CFG_LIST, CFG_LIST_RECURSIVE); + ctx.all_files = config_get_bool(cfg, CFG_LIST, CFG_LIST_ALL); } -size_t image_list_init(struct config* cfg, const char** sources, size_t num) +size_t image_list_init(const struct config* cfg, const char** sources, + size_t num) { struct stat file_stat; diff --git a/src/imagelist.h b/src/imagelist.h index e200fcb5..5b3b3dd9 100644 --- a/src/imagelist.h +++ b/src/imagelist.h @@ -6,13 +6,6 @@ #include "config.h" -// Configuration parameters -#define IMGLIST_SECTION "list" -#define IMGLIST_ORDER "order" -#define IMGLIST_LOOP "loop" -#define IMGLIST_RECURSIVE "recursive" -#define IMGLIST_ALL "all" - // Invalid index of the entry #define IMGLIST_INVALID SIZE_MAX @@ -31,7 +24,8 @@ enum list_order { * @param num number of sources in the list * @return size of the image list */ -size_t image_list_init(struct config* cfg, const char** sources, size_t num); +size_t image_list_init(const struct config* cfg, const char** sources, + size_t num); /** * Destroy global image list context. diff --git a/src/info.c b/src/info.c index c8dd2291..8ffd6788 100644 --- a/src/info.c +++ b/src/info.c @@ -17,16 +17,6 @@ #include #include -// Configuration parameters -#define CFG_CSECTION "info" -#define CFG_VSECTION "info.viewer" -#define CFG_GSECTION "info.gallery" -#define CFG_SHOW "show" -#define CFG_ITIMEOUT "info_timeout" -#define CFG_ITIMEOUT_DEF 5 -#define CFG_STIMEOUT "status_timeout" -#define CFG_STIMEOUT_DEF 3 - /** Display modes. */ enum info_mode { mode_viewer, @@ -34,8 +24,8 @@ enum info_mode { mode_off, }; static const char* mode_names[] = { - [mode_viewer] = APP_MODE_VIEWER, - [mode_gallery] = APP_MODE_GALLERY, + [mode_viewer] = CFG_MODE_VIEWER, + [mode_gallery] = CFG_MODE_GALLERY, [mode_off] = "off", }; #define MODES_NUM 2 @@ -67,30 +57,12 @@ enum block_position { }; /** Block position names. */ static const char* position_names[] = { - [pos_center] = "center", - [pos_top_left] = "top_left", - [pos_top_right] = "top_right", - [pos_bottom_left] = "bottom_left", - [pos_bottom_right] = "bottom_right", + [pos_center] = CFG_INFO_CN, [pos_top_left] = CFG_INFO_TL, + [pos_top_right] = CFG_INFO_TR, [pos_bottom_left] = CFG_INFO_BL, + [pos_bottom_right] = CFG_INFO_BR, }; #define POSITION_NUM ARRAY_SIZE(position_names) -// Default configuration -static const char* default_viewer[] = { - [pos_center] = "none", - [pos_top_left] = "+name,+format,+filesize,+imagesize,+exif", - [pos_top_right] = "index", - [pos_bottom_left] = "scale,frame", - [pos_bottom_right] = "status", -}; -static const char* default_gallery[] = { - [pos_center] = "none", - [pos_top_left] = "none", - [pos_top_right] = "none", - [pos_bottom_left] = "none", - [pos_bottom_right] = "name,status", -}; - // Max number of lines in one positioned block #define MAX_LINES (FIELDS_NUM + 10 /* EXIF and duplicates */) @@ -426,34 +398,30 @@ static bool parse_scheme(const char* config, struct block_scheme* scheme) return true; } -void info_init(struct config* cfg) +void info_init(const struct config* cfg) { - const char** defaults; - const char* section; - const char* position; - const char* format; - for (size_t i = 0; i < MODES_NUM; ++i) { - defaults = (i == mode_viewer ? default_viewer : default_gallery); - section = (i == mode_viewer ? CFG_VSECTION : CFG_GSECTION); + const char* section; + section = (i == mode_viewer ? CFG_INFO_VIEWER : CFG_INFO_GALLERY); for (size_t j = 0; j < POSITION_NUM; ++j) { - position = position_names[j]; - format = config_get_string(cfg, section, position, defaults[j]); + const char* position = position_names[j]; + const char* format = config_get(cfg, section, position); if (!parse_scheme(format, &ctx.scheme[i][j])) { config_error_val(section, format); - parse_scheme(defaults[j], &ctx.scheme[i][j]); + format = config_get_default(section, position); + parse_scheme(format, &ctx.scheme[i][j]); } } } - ctx.mode = config_get_bool(cfg, CFG_CSECTION, CFG_SHOW, true) ? mode_viewer - : mode_off; - ctx.info.timeout = config_get_num(cfg, CFG_CSECTION, CFG_ITIMEOUT, 0, 1024, - CFG_ITIMEOUT_DEF); + ctx.mode = + config_get_bool(cfg, CFG_INFO, CFG_INFO_SHOW) ? mode_viewer : mode_off; + ctx.info.timeout = + config_get_num(cfg, CFG_INFO, CFG_INFO_ITIMEOUT, 0, 1024); timeout_init(&ctx.info); - ctx.status.timeout = config_get_num(cfg, CFG_CSECTION, CFG_STIMEOUT, 0, - 1024, CFG_STIMEOUT_DEF); + ctx.status.timeout = + config_get_num(cfg, CFG_INFO, CFG_INFO_STIMEOUT, 0, 1024); timeout_init(&ctx.status); font_render("File name:", &ctx.fields[info_file_name].key); diff --git a/src/info.h b/src/info.h index 877fc0d4..14e8c1d7 100644 --- a/src/info.h +++ b/src/info.h @@ -25,7 +25,7 @@ enum info_field { * Initialize global info context. * @param cfg config instance */ -void info_init(struct config* cfg); +void info_init(const struct config* cfg); /** * Destroy global info context. diff --git a/src/keybind.c b/src/keybind.c index d48aff70..a290e9e4 100644 --- a/src/keybind.c +++ b/src/keybind.c @@ -177,18 +177,16 @@ static void set_binding(struct keybind** head, xkb_keysym_t key, uint8_t mods, * @param section name of the section * @param value actions */ -static void load_binding(struct keybind** head, struct config* cfg, +static void load_binding(struct keybind** head, const struct config* cfg, const char* section) { - list_for_each(cfg, struct config, cs) { + list_for_each(cfg, const struct config, cs) { if (strcmp(section, cs->name) == 0) { - list_for_each(cs->params, struct config_keyval, kv) { + list_for_each(cs->params, const struct config_keyval, kv) { struct action_seq actions = { 0 }; xkb_keysym_t keysym; uint8_t mods; - kv->used = true; - // parse keyboard shortcut if (!parse_keymod(kv->key, &keysym, &mods)) { config_error_key(section, kv->key); @@ -207,10 +205,10 @@ static void load_binding(struct keybind** head, struct config* cfg, } } -void keybind_init(struct config* cfg) +void keybind_init(const struct config* cfg) { - load_binding(&kb_viewer, cfg, "keys.viewer"); - load_binding(&kb_gallery, cfg, "keys.gallery"); + load_binding(&kb_viewer, cfg, CFG_KEYS_VIEWER); + load_binding(&kb_gallery, cfg, CFG_KEYS_GALLERY); } void keybind_destroy(void) diff --git a/src/keybind.h b/src/keybind.h index 4dff8da4..9bfa53c4 100644 --- a/src/keybind.h +++ b/src/keybind.h @@ -38,7 +38,7 @@ struct keybind { * Initialize global default key binding scheme. * @param cfg config instance */ -void keybind_init(struct config* cfg); +void keybind_init(const struct config* cfg); /** * Destroy global key binding scheme. diff --git a/src/main.c b/src/main.c index 513961c6..df375d0e 100644 --- a/src/main.c +++ b/src/main.c @@ -80,7 +80,7 @@ static void print_version(void) * @param argv arguments array * @return index of the first non option argument */ -static int parse_cmdargs(int argc, char* argv[], struct config** cfg) +static int parse_cmdargs(int argc, char* argv[], struct config* cfg) { struct option options[1 + ARRAY_SIZE(arguments)]; char short_opts[ARRAY_SIZE(arguments) * 2]; @@ -108,38 +108,35 @@ static int parse_cmdargs(int argc, char* argv[], struct config** cfg) while ((opt = getopt_long(argc, argv, short_opts, options, NULL)) != -1) { switch (opt) { case 'g': - config_set(cfg, APP_CFG_SECTION, APP_CFG_MODE, - APP_MODE_GALLERY); + config_set(cfg, CFG_GENERAL, CFG_GNRL_MODE, CFG_MODE_GALLERY); break; case 'r': - config_set(cfg, IMGLIST_SECTION, IMGLIST_RECURSIVE, "yes"); + config_set(cfg, CFG_LIST, CFG_LIST_RECURSIVE, CFG_YES); break; case 'o': - config_set(cfg, IMGLIST_SECTION, IMGLIST_ORDER, optarg); + config_set(cfg, CFG_LIST, CFG_LIST_ORDER, optarg); break; case 's': - config_set(cfg, VIEWER_SECTION, VIEWER_SCALE, optarg); + config_set(cfg, CFG_VIEWER, CFG_VIEW_SCALE, optarg); break; case 'l': - config_set(cfg, VIEWER_SECTION, VIEWER_SLIDESHOW, "yes"); + config_set(cfg, CFG_VIEWER, CFG_VIEW_SSHOW, CFG_YES); break; case 'p': - config_set(cfg, APP_CFG_SECTION, APP_CFG_POSITION, optarg); + config_set(cfg, CFG_GENERAL, CFG_GNRL_POSITION, optarg); break; case 'w': - config_set(cfg, APP_CFG_SECTION, APP_CFG_SIZE, optarg); + config_set(cfg, CFG_GENERAL, CFG_GNRL_SIZE, optarg); break; case 'f': - config_set(cfg, APP_CFG_SECTION, APP_CFG_SIZE, APP_FULLSCREEN); + config_set(cfg, CFG_GENERAL, CFG_GNRL_SIZE, CFG_FULLSCREEN); break; case 'a': - config_set(cfg, APP_CFG_SECTION, APP_CFG_APP_ID, optarg); + config_set(cfg, CFG_GENERAL, CFG_GNRL_APP_ID, optarg); break; case 'c': if (!config_set_arg(cfg, optarg)) { - fprintf(stderr, - "WARNING: Invalid config argument: \"%s\"\n", - optarg); + exit(EXIT_FAILURE); } break; case 'v': @@ -170,12 +167,8 @@ int main(int argc, char* argv[]) setlocale(LC_ALL, ""); cfg = config_load(); - argn = parse_cmdargs(argc, argv, &cfg); + argn = parse_cmdargs(argc, argv, cfg); rc = app_init(cfg, (const char**)&argv[argn], argc - argn); - - if (cfg && rc) { - config_check(cfg); - } config_free(cfg); if (rc) { diff --git a/src/viewer.c b/src/viewer.c index 3810a6df..d4860bc7 100644 --- a/src/viewer.c +++ b/src/viewer.c @@ -23,19 +23,6 @@ #define GRID_COLOR1 0xff333333 #define GRID_COLOR2 0xff4c4c4c -// Default configuration parameters -#define CFG_WINDOW_DEF ARGB(0, 0, 0, 0) -#define CFG_TRANSPARENCY_DEF GRID_NAME -#define CFG_SCALE_DEF "optimal" -#define CFG_POSITION_DEF "center" -#define CFG_FIXED_DEF true -#define CFG_ANTIALIASING_DEF false -#define CFG_SCALE_METHOD_DEF "mks13" -#define CFG_SLIDESHOW_DEF false -#define CFG_SLIDESHOW_TIME_DEF 3 -#define CFG_HISTORY_DEF 1 -#define CFG_PRELOAD_DEF 1 - // Scale thresholds #define MIN_SCALE 10 // pixels #define MAX_SCALE 100.0 // factor @@ -744,63 +731,42 @@ static void on_drag(int dx, int dy) } } -void viewer_init(struct config* cfg, struct image* image) +void viewer_init(const struct config* cfg, struct image* image) { size_t history; size_t preload; const char* value; ssize_t index; - ctx.fixed = - config_get_bool(cfg, VIEWER_SECTION, VIEWER_FIXED, CFG_FIXED_DEF); - ctx.antialiasing = config_get_bool(cfg, VIEWER_SECTION, VIEWER_ANTIALIASING, - CFG_ANTIALIASING_DEF); - value = config_get_string(cfg, VIEWER_SECTION, VIEWER_SCALE_METHOD, - CFG_SCALE_METHOD_DEF); + ctx.fixed = config_get_bool(cfg, CFG_VIEWER, CFG_VIEW_FIXED); + ctx.antialiasing = config_get_bool(cfg, CFG_VIEWER, CFG_VIEW_AA); + value = config_get(cfg, CFG_VIEWER, CFG_VIEW_AA_METHOD); index = pixmap_scale_index(value); if (index >= 0) { ctx.scale_method = index; } else { ctx.scale_method = pixmap_mks13; - config_error_val(VIEWER_SECTION, value); + config_error_val(CFG_VIEWER, value); } - ctx.window_bkg = - config_get_color(cfg, VIEWER_SECTION, VIEWER_WINDOW, CFG_WINDOW_DEF); + ctx.window_bkg = config_get_color(cfg, CFG_VIEWER, CFG_VIEW_WINDOW); // background for transparent images - value = config_get_string(cfg, VIEWER_SECTION, VIEWER_TRANSPARENCY, - CFG_TRANSPARENCY_DEF); + value = config_get(cfg, CFG_VIEWER, CFG_VIEW_TRANSP); if (strcmp(value, GRID_NAME) == 0) { ctx.image_bkg = GRID_BKGID; } else { - ctx.image_bkg = config_get_color(cfg, VIEWER_SECTION, - VIEWER_TRANSPARENCY, GRID_BKGID); + ctx.image_bkg = config_get_color(cfg, CFG_VIEWER, CFG_VIEW_TRANSP); } - // initial scale - value = config_get_string(cfg, VIEWER_SECTION, VIEWER_SCALE, CFG_SCALE_DEF); - index = str_index(scale_names, value, 0); - if (index >= 0) { - ctx.scale_init = index; - } else { - ctx.scale_init = scale_fit_optimal; - config_error_val(VIEWER_SECTION, value); - } - - value = config_get_string(cfg, VIEWER_SECTION, VIEWER_POSITION, - CFG_POSITION_DEF); - index = str_index(position_names, value, 0); - if (index >= 0) { - ctx.position = index; - } else { - ctx.position = position_center; - } + // initial scale and position + ctx.scale_init = config_get_oneof(cfg, CFG_VIEWER, CFG_VIEW_SCALE, + scale_names, ARRAY_SIZE(scale_names)); + ctx.position = config_get_oneof(cfg, CFG_VIEWER, CFG_VIEW_POSITION, + position_names, ARRAY_SIZE(position_names)); // cache and preloads - history = config_get_num(cfg, VIEWER_SECTION, VIEWER_HISTORY, 0, 1024, - CFG_HISTORY_DEF); - preload = config_get_num(cfg, VIEWER_SECTION, VIEWER_PRELOAD, 0, 1024, - CFG_PRELOAD_DEF); + history = config_get_num(cfg, CFG_VIEWER, CFG_VIEW_HISTORY, 0, 1024); + preload = config_get_num(cfg, CFG_VIEWER, CFG_VIEW_PRELOAD, 0, 1024); // setup animation timer ctx.animation_enable = true; @@ -809,13 +775,10 @@ void viewer_init(struct config* cfg, struct image* image) if (ctx.animation_fd != -1) { app_watch(ctx.animation_fd, on_animation_timer, NULL); } - // setup slideshow timer - ctx.slideshow_enable = config_get_bool(cfg, VIEWER_SECTION, - VIEWER_SLIDESHOW, CFG_SLIDESHOW_DEF); + ctx.slideshow_enable = config_get_bool(cfg, CFG_VIEWER, CFG_VIEW_SSHOW); ctx.slideshow_time = - config_get_num(cfg, VIEWER_SECTION, VIEWER_SLIDESHOW_TIME, 1, 86400, - CFG_SLIDESHOW_TIME_DEF); + config_get_num(cfg, CFG_VIEWER, CFG_VIEW_SSHOW_TM, 1, 86400); ctx.slideshow_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK); if (ctx.slideshow_fd != -1) { diff --git a/src/viewer.h b/src/viewer.h index 06ac9391..7b35f7d3 100644 --- a/src/viewer.h +++ b/src/viewer.h @@ -8,26 +8,12 @@ #include "event.h" #include "image.h" -// Configuration parameters -#define VIEWER_SECTION "viewer" -#define VIEWER_WINDOW "window" -#define VIEWER_TRANSPARENCY "transparency" -#define VIEWER_SCALE "scale" -#define VIEWER_POSITION "position" -#define VIEWER_FIXED "fixed" -#define VIEWER_ANTIALIASING "antialiasing" -#define VIEWER_SCALE_METHOD "scale_method" -#define VIEWER_SLIDESHOW "slideshow" -#define VIEWER_SLIDESHOW_TIME "slideshow_time" -#define VIEWER_HISTORY "history" -#define VIEWER_PRELOAD "preload" - /** * Initialize global viewer context. * @param cfg config instance * @param image initial image to open */ -void viewer_init(struct config* cfg, struct image* image); +void viewer_init(const struct config* cfg, struct image* image); /** * Destroy global viewer context. diff --git a/test/config_test.cpp b/test/config_test.cpp index 4ebeb198..14126808 100644 --- a/test/config_test.cpp +++ b/test/config_test.cpp @@ -9,98 +9,162 @@ extern "C" { class Config : public ::testing::Test { protected: + void SetUp() override + { + unsetenv("XDG_CONFIG_HOME"); + unsetenv("XDG_CONFIG_DIRS"); + unsetenv("HOME"); + config = config_load(); + } + void TearDown() override { config_free(config); } + struct config* config = nullptr; }; +TEST(ConfigLoader, Load) +{ + testing::internal::CaptureStderr(); + + setenv("XDG_CONFIG_HOME", TEST_DATA_DIR, 1); + + struct config* config = config_load(); + ASSERT_NE(config, nullptr); + EXPECT_STREQ(config_get(config, CFG_GENERAL, CFG_GNRL_MODE), "s p a c e s"); + EXPECT_STREQ(config_get(config, CFG_GENERAL, CFG_GNRL_APP_ID), "my_ap_id"); + config_free(config); + + unsetenv("XDG_CONFIG_HOME"); + EXPECT_FALSE(testing::internal::GetCapturedStderr().empty()); +} + TEST_F(Config, Set) { - config_set(&config, "section123", "key1", "value1"); - EXPECT_STREQ(config_get(config, "section123", "key1"), "value1"); - config_set(&config, "section123", "key1", "value2"); - EXPECT_STREQ(config_get(config, "section123", "key1"), "value2"); - - config_set(&config, "section321", "key2", ""); - EXPECT_STREQ(config_get(config, "section321", "key2"), ""); - config_set(&config, "section321", "key3", "123"); - EXPECT_STREQ(config_get(config, "section321", "key3"), "123"); + EXPECT_TRUE(config_set(config, CFG_GENERAL, CFG_GNRL_APP_ID, "test123")); + EXPECT_STREQ(config_get(config, CFG_GENERAL, CFG_GNRL_APP_ID), "test123"); + + testing::internal::CaptureStderr(); + + EXPECT_FALSE(config_set(config, CFG_GENERAL, CFG_GNRL_APP_ID, "")); + EXPECT_FALSE(config_set(config, CFG_GENERAL, "unknown", "test123")); + EXPECT_FALSE(config_set(config, "unknown", "unknown", "test123")); + + EXPECT_FALSE(testing::internal::GetCapturedStderr().empty()); } TEST_F(Config, SetArg) { - EXPECT_TRUE(config_set_arg(&config, "section123.key1=value1")); - EXPECT_STREQ(config_get(config, "section123", "key1"), "value1"); + EXPECT_TRUE( + config_set_arg(config, CFG_GENERAL "." CFG_GNRL_APP_ID "=test123")); + EXPECT_STREQ(config_get(config, CFG_GENERAL, CFG_GNRL_APP_ID), "test123"); + + EXPECT_TRUE(config_set_arg( + config, "\t\n" CFG_GENERAL "." CFG_GNRL_APP_ID " = \ttest321")); + EXPECT_STREQ(config_get(config, CFG_GENERAL, CFG_GNRL_APP_ID), "test321"); - EXPECT_TRUE(config_set_arg(&config, "\t\nsub.section.command = \t42")); - EXPECT_STREQ(config_get(config, "sub.section", "command"), "42"); + testing::internal::CaptureStderr(); - EXPECT_FALSE(config_set_arg(&config, "")); - EXPECT_FALSE(config_set_arg(&config, "abc=1")); - EXPECT_FALSE(config_set_arg(&config, "abc.def")); - EXPECT_FALSE(config_set_arg(&config, "abc.def=")); + EXPECT_FALSE(config_set_arg(config, "")); + EXPECT_FALSE(config_set_arg(config, "abc=1")); + EXPECT_FALSE(config_set_arg(config, "abc.def")); + EXPECT_FALSE(config_set_arg(config, "abc.def=")); + + EXPECT_FALSE(testing::internal::GetCapturedStderr().empty()); } -TEST_F(Config, Load) +TEST_F(Config, Add) { - setenv("XDG_CONFIG_HOME", TEST_DATA_DIR, 1); - config = config_load(); + testing::internal::CaptureStderr(); + EXPECT_STREQ(config_get(config, CFG_KEYS_VIEWER, "F12"), ""); + EXPECT_FALSE(testing::internal::GetCapturedStderr().empty()); - ASSERT_NE(config, nullptr); - EXPECT_STREQ(config_get(config, "test.section", "spaces"), "s p a c e s"); - EXPECT_STREQ(config_get(config, "test.section", "nospaces"), "nospaces"); - EXPECT_STREQ(config_get(config, "test.section", "empty"), ""); + EXPECT_TRUE(config_set(config, CFG_KEYS_VIEWER, "F12", "quit")); + EXPECT_STREQ(config_get(config, CFG_KEYS_VIEWER, "F12"), "quit"); } -TEST_F(Config, GetString) +TEST_F(Config, Replace) { - config_set(&config, "section", "key", "value"); - EXPECT_STREQ(config_get_string(config, "section", "key", ""), "value"); - EXPECT_STREQ(config_get_string(config, "section", "key1", "def"), "def"); - EXPECT_STREQ(config_get_string(config, "section1", "key", "def"), "def"); + EXPECT_STREQ(config_get(config, CFG_KEYS_VIEWER, "F1"), "help"); + EXPECT_TRUE(config_set(config, CFG_KEYS_VIEWER, "F1", "quit")); + EXPECT_STREQ(config_get(config, CFG_KEYS_VIEWER, "F1"), "quit"); } -TEST_F(Config, GetBool) +TEST_F(Config, GetDefault) { - config_set(&config, "section", "key", "yes"); - EXPECT_TRUE(config_get_bool(config, "section", "key", true)); - EXPECT_TRUE(config_get_bool(config, "section", "key", false)); + EXPECT_TRUE(config_set(config, CFG_GENERAL, CFG_GNRL_APP_ID, "test123")); + EXPECT_STREQ(config_get_default(CFG_GENERAL, CFG_GNRL_APP_ID), "swayimg"); + + testing::internal::CaptureStderr(); + + EXPECT_STREQ(config_get_default(CFG_GENERAL, "unknown"), ""); + EXPECT_STREQ(config_get_default("unknown", "unknown"), ""); - config_set(&config, "section", "key", "no"); - EXPECT_FALSE(config_get_bool(config, "section", "key", true)); - EXPECT_FALSE(config_get_bool(config, "section", "key", false)); + EXPECT_FALSE(testing::internal::GetCapturedStderr().empty()); +} - config_set(&config, "section", "key", "invalid"); - EXPECT_TRUE(config_get_bool(config, "section", "key", true)); +TEST_F(Config, Get) +{ + EXPECT_STREQ(config_get(config, CFG_GENERAL, CFG_GNRL_APP_ID), "swayimg"); + testing::internal::CaptureStderr(); + EXPECT_STREQ(config_get(config, CFG_GENERAL, "unknown"), ""); + EXPECT_STREQ(config_get(config, "unknown", "unknown"), ""); + EXPECT_FALSE(testing::internal::GetCapturedStderr().empty()); +} - EXPECT_TRUE(config_get_bool(config, "section", "key1", true)); - EXPECT_FALSE(config_get_bool(config, "section", "key1", false)); - EXPECT_TRUE(config_get_bool(config, "section1", "key", true)); - EXPECT_FALSE(config_get_bool(config, "section1", "key", false)); +TEST_F(Config, GetOneOf) +{ + const char* possible[] = { "one", "two", "three" }; + EXPECT_TRUE(config_set(config, CFG_LIST, CFG_LIST_ORDER, "two")); + EXPECT_EQ(config_get_oneof(config, CFG_LIST, CFG_LIST_ORDER, possible, 3), + 1); + + testing::internal::CaptureStderr(); + EXPECT_TRUE(config_set(config, CFG_LIST, CFG_LIST_ORDER, "four")); + EXPECT_EQ(config_get_oneof(config, CFG_LIST, CFG_LIST_ORDER, possible, 3), + 0); + EXPECT_FALSE(testing::internal::GetCapturedStderr().empty()); +} + +TEST_F(Config, GetBool) +{ + EXPECT_TRUE(config_set(config, CFG_GALLERY, CFG_GLRY_FILL, CFG_YES)); + EXPECT_TRUE(config_get_bool(config, CFG_GALLERY, CFG_GLRY_FILL)); + EXPECT_TRUE(config_set(config, CFG_GALLERY, CFG_GLRY_FILL, CFG_NO)); + EXPECT_FALSE(config_get_bool(config, CFG_GALLERY, CFG_GLRY_FILL)); +} + +TEST_F(Config, GetNum) +{ + EXPECT_TRUE(config_set(config, CFG_FONT, CFG_FONT_SIZE, "123")); + EXPECT_EQ(config_get_num(config, CFG_FONT, CFG_FONT_SIZE, 0, 1024), 123); + + testing::internal::CaptureStderr(); + EXPECT_EQ(config_get_num(config, CFG_FONT, CFG_FONT_SIZE, 0, -1), 14); + EXPECT_EQ(config_get_num(config, CFG_FONT, CFG_FONT_SIZE, 0, 1), 14); + EXPECT_EQ(config_get_num(config, CFG_FONT, CFG_FONT_SIZE, -1, 0), 14); + EXPECT_FALSE(testing::internal::GetCapturedStderr().empty()); } -TEST_F(Config, ToColor) +TEST_F(Config, GetColor) { - config_set(&config, "section", "key", "#010203"); - EXPECT_EQ(config_get_color(config, "section", "key", 0xbaaaaaad), + config_set(config, CFG_VIEWER, CFG_VIEW_WINDOW, "#010203"); + EXPECT_EQ(config_get_color(config, CFG_VIEWER, CFG_VIEW_WINDOW), static_cast(0xff010203)); - config_set(&config, "section", "key", "#010203aa"); - EXPECT_EQ(config_get_color(config, "section", "key", 0xbaaaaaad), + config_set(config, CFG_VIEWER, CFG_VIEW_WINDOW, "#010203aa"); + EXPECT_EQ(config_get_color(config, CFG_VIEWER, CFG_VIEW_WINDOW), static_cast(0xaa010203)); - config_set(&config, "section", "key", "010203aa"); - EXPECT_EQ(config_get_color(config, "section", "key", 0xbaaaaaad), + config_set(config, CFG_VIEWER, CFG_VIEW_WINDOW, "010203aa"); + EXPECT_EQ(config_get_color(config, CFG_VIEWER, CFG_VIEW_WINDOW), static_cast(0xaa010203)); - config_set(&config, "section", "key", "# 010203aa"); - EXPECT_EQ(config_get_color(config, "section", "key", 0xbaaaaaad), + config_set(config, CFG_VIEWER, CFG_VIEW_WINDOW, "# 010203aa"); + EXPECT_EQ(config_get_color(config, CFG_VIEWER, CFG_VIEW_WINDOW), static_cast(0xaa010203)); - config_set(&config, "section", "key", "invalid"); - EXPECT_EQ(config_get_color(config, "section", "key", 0x11223344), - static_cast(0x11223344)); - EXPECT_EQ(config_get_color(config, "section", "key1", 0x11223344), - static_cast(0x11223344)); - EXPECT_EQ(config_get_color(config, "section1", "key", 0x11223344), - static_cast(0x11223344)); + testing::internal::CaptureStderr(); + config_set(config, CFG_VIEWER, CFG_VIEW_WINDOW, "invalid"); + config_get_color(config, CFG_VIEWER, CFG_VIEW_WINDOW); + EXPECT_FALSE(testing::internal::GetCapturedStderr().empty()); } diff --git a/test/data/swayimg/config b/test/data/swayimg/config index 9a51b2d7..45c57ae0 100644 --- a/test/data/swayimg/config +++ b/test/data/swayimg/config @@ -1,8 +1,9 @@ # Swayimg test configuration file. -[test.section] - spaces = s p a c e s -nospaces=nospaces -empty= +[general] + mode = s p a c e s +app_id=my_ap_id +sigusr1= -[empty_section] +[unknown_section] +some_param = 1 diff --git a/test/keybind_test.cpp b/test/keybind_test.cpp index 6a7690d9..cf1451d5 100644 --- a/test/keybind_test.cpp +++ b/test/keybind_test.cpp @@ -10,19 +10,26 @@ extern "C" { class Keybind : public ::testing::Test { protected: + void SetUp() override + { + unsetenv("XDG_CONFIG_HOME"); + unsetenv("XDG_CONFIG_DIRS"); + unsetenv("HOME"); + config = config_load(); + } + void TearDown() override { keybind_destroy(); config_free(config); } - static constexpr const char* section = "keys.viewer"; - struct config* config = nullptr; + struct config* config; }; TEST_F(Keybind, Add) { - config_set(&config, section, "a", "exit"); + config_set(config, CFG_KEYS_VIEWER, "a", "exit"); keybind_init(config); const struct keybind* kb = keybind_find('a', 0); @@ -36,7 +43,7 @@ TEST_F(Keybind, Add) TEST_F(Keybind, Replace) { - config_set(&config, section, "Escape", "info"); + config_set(config, CFG_KEYS_VIEWER, "Escape", "info"); keybind_init(config); const struct keybind* kb = keybind_find(XKB_KEY_Escape, 0); @@ -49,11 +56,11 @@ TEST_F(Keybind, Replace) TEST_F(Keybind, Mods) { - config_set(&config, section, "Ctrl+a", "exit"); - config_set(&config, section, "Alt+b", "exit"); - config_set(&config, section, "Shift+c", "exit"); - config_set(&config, section, "Alt+Ctrl+d", "exit"); - config_set(&config, section, "Ctrl+Shift+Alt+e", "exit"); + config_set(config, CFG_KEYS_VIEWER, "Ctrl+a", "exit"); + config_set(config, CFG_KEYS_VIEWER, "Alt+b", "exit"); + config_set(config, CFG_KEYS_VIEWER, "Shift+c", "exit"); + config_set(config, CFG_KEYS_VIEWER, "Alt+Ctrl+d", "exit"); + config_set(config, CFG_KEYS_VIEWER, "Ctrl+Shift+Alt+e", "exit"); keybind_init(config); EXPECT_NE(keybind_find('a', KEYMOD_CTRL), nullptr); @@ -66,7 +73,7 @@ TEST_F(Keybind, Mods) TEST_F(Keybind, ActionParams) { - config_set(&config, section, "a", "status \t params 1 2 3\t"); + config_set(config, CFG_KEYS_VIEWER, "a", "status \t params 1 2 3\t"); keybind_init(config); const struct keybind* kb = keybind_find('a', 0); @@ -81,7 +88,7 @@ TEST_F(Keybind, ActionParams) TEST_F(Keybind, Multiaction) { - config_set(&config, section, "a", "exec cmd;reload;exit"); + config_set(config, CFG_KEYS_VIEWER, "a", "exec cmd;reload;exit"); keybind_init(config); const struct keybind* kb = keybind_find('a', 0);