diff --git a/src/config.c b/src/config.c index 2d71e0aa..0707247d 100644 --- a/src/config.c +++ b/src/config.c @@ -12,6 +12,137 @@ #include #include +/** Default configuration. */ +struct config_default { + const char* section; + const char* key; + const char* value; +}; +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", "fixed", "yes" }, + { "viewer", "antialiasing", "no" }, + { "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", "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", "yes" }, + + { "font", "name", "monospace" }, + { "font", "size", "14" }, + { "font", "color", "#ccccccff" }, + { "font", "shadow", "#000000a0" }, + + { "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+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" }, +}; + /** Config file location. */ struct location { const char* prefix; ///< Environment variable name @@ -104,9 +235,8 @@ static char* expand_path(const char* prefix_env, const char* postfix) * @param path full path to the file * @return loaded config instance or NULL on errors */ -static struct config* load(const char* path) +static bool load(const char* path, struct config** cfg) { - struct config* cfg = NULL; FILE* fd = NULL; char* buff = NULL; size_t buff_sz = 0; @@ -116,7 +246,7 @@ static struct config* load(const char* path) fd = fopen(path, "r"); if (!fd) { - return NULL; + return false; } while ((nread = getline(&buff, &buff_sz, fd)) != -1) { @@ -186,32 +316,35 @@ static struct config* load(const char* path) } // save configuration parameter - config_set(&cfg, section, line, value); + config_set(cfg, section, line, value); } free(buff); free(section); fclose(fd); - return cfg; + return true; } struct config* config_load(void) { struct config* cfg = NULL; + // set defaults + 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); + } + // 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); - if (path) { - cfg = load(path); - if (cfg) { - free(path); - break; - } - } + const bool loaded = path && load(path, &cfg); free(path); + if (loaded) { + break; + } } return cfg; diff --git a/src/keybind.c b/src/keybind.c index 51c00f7e..de394bba 100644 --- a/src/keybind.c +++ b/src/keybind.c @@ -10,94 +10,6 @@ #include #include -// Default key bindings -struct keybind_default { - xkb_keysym_t key; ///< Keyboard key - uint8_t mods; ///< Key modifiers - struct action action; ///< Action -}; - -// clang-format off - -/** Default key bindings for viewer mode. */ -static const struct keybind_default default_viewer[] = { - { .key = XKB_KEY_F1, .action = { action_help, NULL } }, - { .key = XKB_KEY_Home, .action = { action_first_file, NULL } }, - { .key = XKB_KEY_End, .action = { action_last_file, NULL } }, - { .key = XKB_KEY_space, .action = { action_next_file, NULL } }, - { .key = XKB_KEY_SunPageDown, .action = { action_next_file, NULL } }, - { .key = XKB_KEY_SunPageUp, .action = { action_prev_file, NULL } }, - { .key = XKB_KEY_c, .action = { action_skip_file, NULL } }, - { .key = XKB_KEY_d, .action = { action_next_dir, NULL } }, - { .key = XKB_KEY_d, .mods = KEYMOD_SHIFT, .action = { action_prev_dir, NULL } }, - { .key = XKB_KEY_o, .action = { action_next_frame, NULL } }, - { .key = XKB_KEY_o, .mods = KEYMOD_SHIFT, .action = { action_prev_frame, NULL } }, - { .key = XKB_KEY_s, .action = { action_animation, NULL } }, - { .key = XKB_KEY_s, .mods = KEYMOD_SHIFT, .action = { action_slideshow, NULL } }, - { .key = XKB_KEY_f, .action = { action_fullscreen, NULL } }, - { .key = XKB_KEY_Left, .action = { action_step_left, NULL } }, - { .key = XKB_KEY_Right, .action = { action_step_right, NULL } }, - { .key = XKB_KEY_Up, .action = { action_step_up, NULL } }, - { .key = XKB_KEY_Down, .action = { action_step_down, NULL } }, - { .key = XKB_KEY_equal, .action = { action_zoom, "+10" } }, - { .key = XKB_KEY_plus, .action = { action_zoom, "+10" } }, - { .key = XKB_KEY_minus, .action = { action_zoom, "-10" } }, - { .key = XKB_KEY_w, .action = { action_zoom, "width" } }, - { .key = XKB_KEY_w, .mods = KEYMOD_SHIFT, .action = { action_zoom, "height" } }, - { .key = XKB_KEY_z, .action = { action_zoom, "fit" } }, - { .key = XKB_KEY_z, .mods = KEYMOD_SHIFT, .action = { action_zoom, "fill" } }, - { .key = XKB_KEY_0, .action = { action_zoom, "real" } }, - { .key = XKB_KEY_BackSpace, .action = { action_zoom, "optimal" } }, - { .key = XKB_KEY_bracketleft, .action = { action_rotate_left, NULL } }, - { .key = XKB_KEY_bracketright, .action = { action_rotate_right, NULL } }, - { .key = XKB_KEY_m, .action = { action_flip_vertical, NULL } }, - { .key = XKB_KEY_m, .mods = KEYMOD_SHIFT, .action = { action_flip_horizontal, NULL } }, - { .key = XKB_KEY_a, .action = { action_antialiasing, NULL } }, - { .key = XKB_KEY_r, .action = { action_reload, NULL } }, - { .key = XKB_KEY_i, .action = { action_info, NULL } }, - { .key = XKB_KEY_Return, .action = { action_mode, NULL } }, - { .key = XKB_KEY_Escape, .action = { action_exit, NULL } }, - { .key = XKB_KEY_q, .action = { action_exit, NULL } }, - { .key = VKEY_SCROLL_LEFT, .action = { action_step_right, "5" } }, - { .key = VKEY_SCROLL_RIGHT, .action = { action_step_left, "5" } }, - { .key = VKEY_SCROLL_UP, .action = { action_step_up, "5" } }, - { .key = VKEY_SCROLL_DOWN, .action = { action_step_down, "5" } }, - { .key = VKEY_SCROLL_UP, .mods = KEYMOD_CTRL, .action = { action_zoom, "+10" } }, - { .key = VKEY_SCROLL_DOWN, .mods = KEYMOD_CTRL, .action = { action_zoom, "-10" } }, - { .key = VKEY_SCROLL_UP, .mods = KEYMOD_SHIFT, .action = { action_prev_file, NULL } }, - { .key = VKEY_SCROLL_DOWN, .mods = KEYMOD_SHIFT, .action = { action_next_file, NULL } }, - { .key = VKEY_SCROLL_UP, .mods = KEYMOD_ALT, .action = { action_prev_frame, NULL } }, - { .key = VKEY_SCROLL_DOWN, .mods = KEYMOD_ALT, .action = { action_next_frame, NULL } }, - { .key = XKB_KEY_Delete, .mods = KEYMOD_SHIFT, .action = { action_none, NULL } }, -}; - -/** Default key bindings for gallery mode. */ -static const struct keybind_default default_gallery[] = { - { .key = XKB_KEY_F1, .action = { action_help, NULL } }, - { .key = XKB_KEY_Home, .action = { action_first_file, NULL } }, - { .key = XKB_KEY_End, .action = { action_last_file, NULL } }, - { .key = XKB_KEY_f, .action = { action_fullscreen, NULL } }, - { .key = XKB_KEY_Left, .action = { action_step_left, NULL } }, - { .key = XKB_KEY_Right, .action = { action_step_right, NULL } }, - { .key = XKB_KEY_Up, .action = { action_step_up, NULL } }, - { .key = XKB_KEY_Down, .action = { action_step_down, NULL } }, - { .key = XKB_KEY_Prior, .action = { action_page_up, NULL } }, - { .key = XKB_KEY_Next, .action = { action_page_down, NULL } }, - { .key = XKB_KEY_c, .action = { action_skip_file, NULL } }, - { .key = XKB_KEY_a, .action = { action_antialiasing, NULL } }, - { .key = XKB_KEY_r, .action = { action_reload, NULL } }, - { .key = XKB_KEY_i, .action = { action_info, NULL } }, - { .key = XKB_KEY_Return, .action = { action_mode, NULL } }, - { .key = XKB_KEY_Escape, .action = { action_exit, NULL } }, - { .key = XKB_KEY_q, .action = { action_exit, NULL } }, - { .key = VKEY_SCROLL_LEFT, .action = { action_step_right, NULL } }, - { .key = VKEY_SCROLL_RIGHT, .action = { action_step_left, NULL } }, - { .key = VKEY_SCROLL_UP, .action = { action_step_up, NULL } }, - { .key = VKEY_SCROLL_DOWN, .action = { action_step_down, NULL } }, - { .key = XKB_KEY_Delete, .mods = KEYMOD_SHIFT, .action = { action_none, NULL } }, -}; -// clang-format on - // Names of virtual keys struct virtual_keys { xkb_keysym_t key; @@ -259,44 +171,6 @@ static void set_binding(struct keybind** head, xkb_keysym_t key, uint8_t mods, } } -/** - * Create default binding. - * @param head head of binding list - * @param kb default key binding description - */ -static void set_default(struct keybind** head, const struct keybind_default* kb) -{ - struct action_seq actions; - - if (kb->action.type == action_none && kb->key == XKB_KEY_Delete && - kb->mods == KEYMOD_SHIFT) { - // special action: Shift+Del - actions.num = 2; - actions.sequence = malloc(actions.num * sizeof(struct action)); - if (actions.sequence) { - actions.sequence[0].type = action_exec; - actions.sequence[0].params = str_dup("rm \"%\"", NULL); - actions.sequence[1].type = action_skip_file; - actions.sequence[1].params = NULL; - } - } else { - actions.num = 1; - actions.sequence = malloc(actions.num * sizeof(struct action)); - if (actions.sequence) { - actions.sequence[0].type = kb->action.type; - if (kb->action.params) { - actions.sequence[0].params = str_dup(kb->action.params, NULL); - } else { - actions.sequence[0].params = NULL; - } - } - } - - if (actions.sequence) { - set_binding(head, kb->key, kb->mods, &actions); - } -} - /** * Load binding from config parameters. * @param kb head of binding list @@ -336,15 +210,6 @@ static void load_binding(struct keybind** head, struct config* cfg, void keybind_init(struct config* cfg) { - // create default bindings - for (size_t i = 0; i < ARRAY_SIZE(default_viewer); ++i) { - set_default(&kb_viewer, &default_viewer[i]); - } - for (size_t i = 0; i < ARRAY_SIZE(default_gallery); ++i) { - set_default(&kb_gallery, &default_gallery[i]); - } - - // load bindings from config load_binding(&kb_viewer, cfg, "keys.viewer"); load_binding(&kb_gallery, cfg, "keys.gallery"); } diff --git a/test/keybind_test.cpp b/test/keybind_test.cpp index bac48fee..6a7690d9 100644 --- a/test/keybind_test.cpp +++ b/test/keybind_test.cpp @@ -20,19 +20,6 @@ class Keybind : public ::testing::Test { struct config* config = nullptr; }; -TEST_F(Keybind, Default) -{ - keybind_init(NULL); - ASSERT_NE(keybind_get(), nullptr); - - const struct keybind* kb = keybind_find(XKB_KEY_Escape, 0); - ASSERT_NE(kb, nullptr); - EXPECT_EQ(kb->key, static_cast(XKB_KEY_Escape)); - EXPECT_EQ(kb->mods, static_cast(0)); - ASSERT_EQ(kb->actions.num, static_cast(1)); - EXPECT_EQ(kb->actions.sequence[0].type, action_exit); -} - TEST_F(Keybind, Add) { config_set(&config, section, "a", "exit");