#include "../include/Config.hpp" #include "../include/Storage.hpp" #include #include #include // TODO: Show nice error message if config looks wrong (wrong types, has configs that do not exist (maybe spelling mistake?)) namespace QuickMedia { static bool config_initialized = false; static Config *config = nullptr; static float scale = 1.0f; static bool scale_set = false; static const int XFT_DPI_DEFAULT = 96; // Returns XFT_DPI_DEFAULT on error static int xrdb_get_dpi() { int xft_dpi = XFT_DPI_DEFAULT; Display *display = XOpenDisplay(nullptr); if(!display) { fprintf(stderr, "Failed to open x display\n"); return xft_dpi; } char *dpi = XGetDefault(display, "Xft", "dpi"); if(dpi) { xft_dpi = strtol(dpi, nullptr, 10); if(xft_dpi == 0) xft_dpi = XFT_DPI_DEFAULT; } XCloseDisplay(display); return xft_dpi; } static float get_ui_scale() { if(scale_set) return scale; const char *gdk_scale = getenv("GDK_SCALE"); if(gdk_scale) { setlocale(LC_ALL, "C"); // Sigh... stupid C scale = atof(gdk_scale); } else { scale = (float)xrdb_get_dpi() / (float)XFT_DPI_DEFAULT; } if(scale < 0.0001f) scale = 1.0f; scale_set = true; return scale; } static std::string path_expanduser(const std::string &path) { std::string result; if(!path.empty() && path[0] == '~') result = get_home_dir().data + path.substr(1); else result = path; return result; } template static void get_json_value(const Json::Value &json_obj, const char *field_name, T &val) { const Json::Value &json_val = json_obj[field_name]; if(json_val.isNull()) return; if(!json_val.is()) { fprintf(stderr, "Warning: config variable \"%s\" is not a %s\n", field_name, typeid(T).name()); return; } val = json_val.as(); } static void get_json_value(const Json::Value &json_obj, const char *field_name, float &val) { const Json::Value &json_val = json_obj[field_name]; if(json_val.isNull()) return; if(!json_val.isDouble()) { fprintf(stderr, "Warning: config variable \"%s\" is not a float\n", field_name); return; } val = json_val.asDouble(); } // No-op if this has already been called before static void init_config() { if(config_initialized) return; setlocale(LC_ALL, "C"); // Sigh... stupid C config_initialized = true; // Wtf? can't use static non-pointer config because it causes a segfault when setting config.theme. // It looks like a libc bug??? crashes for both gcc and clang. config = new Config(); config->scale = get_ui_scale(); Path config_path = get_storage_dir().join("config.json"); if(get_file_type(config_path) != FileType::REGULAR) { return; } Json::Value json_root; if(!read_file_as_json(config_path, json_root) || !json_root.isObject()) { fprintf(stderr, "Warning: failed to parse config file: %s\n", config_path.data.c_str()); return; } const Json::Value &search_json = json_root["search"]; if(search_json.isObject()) get_json_value(search_json, "font_size", config->search.font_size); const Json::Value &tab_json = json_root["tab"]; if(tab_json.isObject()) get_json_value(tab_json, "font_size", config->tab.font_size); const Json::Value &body_json = json_root["body"]; if(body_json.isObject()) { get_json_value(body_json, "title_font_size", config->body.title_font_size); get_json_value(body_json, "author_font_size", config->body.author_font_size); get_json_value(body_json, "description_font_size", config->body.description_font_size); get_json_value(body_json, "timestamp_font_size", config->body.timestamp_font_size); get_json_value(body_json, "reaction_font_size", config->body.reaction_font_size); get_json_value(body_json, "embedded_load_font_size", config->body.embedded_load_font_size); } const Json::Value &input_json = json_root["input"]; if(input_json.isObject()) get_json_value(input_json, "font_size", config->input.font_size); const Json::Value &video_json = json_root["video"]; if(video_json.isObject()) get_json_value(video_json, "max_height", config->video.max_height); const Json::Value &local_manga_json = json_root["local_manga"]; if(local_manga_json.isObject()) { const Json::Value &directory_json = local_manga_json["directory"]; if(directory_json.isString()) { config->local_manga.directory = path_expanduser(directory_json.asString()); while(config->local_manga.directory.size() > 1 && config->local_manga.directory.back() == '/') { config->local_manga.directory.pop_back(); } } get_json_value(local_manga_json, "load_progress", config->local_manga.sort_by_name); get_json_value(local_manga_json, "sort_chapters_by_name", config->local_manga.sort_chapters_by_name); } const Json::Value &local_anime_json = json_root["local_anime"]; if(local_anime_json.isObject()) { const Json::Value &directory_json = local_anime_json["directory"]; if(directory_json.isString()) { config->local_anime.directory = path_expanduser(directory_json.asString()); while(config->local_anime.directory.size() > 1 && config->local_anime.directory.back() == '/') { config->local_anime.directory.pop_back(); } } get_json_value(local_anime_json, "sort_by_name", config->local_anime.sort_by_name); get_json_value(local_anime_json, "auto_group_episodes", config->local_anime.auto_group_episodes); } const Json::Value &youtube_json = json_root["youtube"]; if(youtube_json.isObject()) get_json_value(youtube_json, "load_progress", config->youtube.load_progress); get_json_value(json_root, "use_system_fonts", config->use_system_fonts); get_json_value(json_root, "use_system_mpv_config", config->use_system_mpv_config); get_json_value(json_root, "enable_shaders", config->enable_shaders); get_json_value(json_root, "theme", config->theme); get_json_value(json_root, "scale", config->scale); get_json_value(json_root, "font_scale", config->font_scale); get_json_value(json_root, "spacing_scale", config->spacing_scale); } const Config& get_config() { init_config(); return *config; } }