Use json for config and persist filters
This commit is contained in:
		
							parent
							
								
									d157e983c4
								
							
						
					
					
						commit
						49a74d552b
					
				|  | @ -22,7 +22,8 @@ set(LINUX_GL_LIBS -lGL) | |||
| 
 | ||||
| find_package(SDL2 REQUIRED) | ||||
| find_package(Freetype 2 REQUIRED) | ||||
| set(LIBS -lpcre2-8 -lpcre2-posix SDL2 imgui) | ||||
| find_package(nlohmann_json REQUIRED) | ||||
| set(LIBS -lpcre2-8 -lpcre2-posix SDL2 nlohmann_json::nlohmann_json imgui) | ||||
| set(IMGUI_LIBS SDL2 Freetype::Freetype) | ||||
| 
 | ||||
| set(INCLUDES imgui imgui/backends imgui/misc/cpp imgui/freetype /usr/include/SDL2) | ||||
|  |  | |||
							
								
								
									
										146
									
								
								config.cpp
								
								
								
								
							
							
						
						
									
										146
									
								
								config.cpp
								
								
								
								
							|  | @ -1,11 +1,8 @@ | |||
| #include <cstdio> | ||||
| #include <cstdlib> | ||||
| #include <cstring> | ||||
| #include <cmath> | ||||
| #include <cfloat> | ||||
| #include <sys/stat.h> | ||||
| #include <string> | ||||
| #include <memory> | ||||
| #include <nlohmann/json.hpp> | ||||
| 
 | ||||
| #include "log.h" | ||||
| #include "misc.h" | ||||
|  | @ -26,49 +23,6 @@ static void fclose_and_log(FILE* file) { | |||
|     log(std::string("Failed to close a file: fclose(): ") + strerror(errno)); | ||||
| } | ||||
| 
 | ||||
| static bool write(const std::string& ptr, FILE* file) { | ||||
|     return fwrite(ptr.data(), sizeof(char), ptr.size(), file) == ptr.size() * sizeof(char); | ||||
| } | ||||
| static bool write(const char* ptr, FILE* file) { | ||||
|     return fwrite(ptr, 1, strlen(ptr), file) == strlen(ptr); | ||||
| } | ||||
| 
 | ||||
| static inline float to_float(const char* str) { | ||||
|     char* endptr; | ||||
| 
 | ||||
|     while (isspace(str[0])) { | ||||
|         str++; | ||||
|     } | ||||
|     if (str[0] == '\0') { | ||||
|         throw std::invalid_argument("float string is empty"); | ||||
|     } | ||||
| 
 | ||||
|     errno = 0; | ||||
|     float res = strtof(str, &endptr); | ||||
|     if (res == HUGE_VALF && errno == ERANGE) { | ||||
|         throw std::overflow_error(std::string(str) + " is bigger than HUGE_VALF"); | ||||
|     } else if (res == FLT_MIN && errno == ERANGE) { | ||||
|         throw std::underflow_error(std::string(str) + " is smaller than FLT_MIN"); | ||||
|     } else if (endptr[0] != '\0') { | ||||
|         throw std::invalid_argument(std::string(str) + " has trailing text"); | ||||
|     } | ||||
|     return res; | ||||
| } | ||||
| 
 | ||||
| static bool validate_font_size_or_log(float size) { | ||||
|     if (size <= 0.0f) { | ||||
|         log(std::string("Font size ") + std::to_string(size) + " is lower or equal to 0"); | ||||
|         return false; | ||||
|     } else if (std::isnan(size)) { | ||||
|         log(std::string("Font size ") + std::to_string(size) + " is NaN"); | ||||
|         return false; | ||||
|     } else if (std::isinf(size)) { | ||||
|         log(std::string("Font size ") + std::to_string(size) + " is infinity or negative infinity"); | ||||
|         return false; | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| std::string get_config_folder() { | ||||
|     const char* path; | ||||
|  | @ -86,7 +40,7 @@ std::string get_config_folder() { | |||
| } | ||||
| 
 | ||||
| std::string get_config_file_path() { | ||||
|     return get_config_folder() + "/config"; | ||||
|     return get_config_folder() + "/config.json"; | ||||
| } | ||||
| 
 | ||||
| // tries to create a directory with an empty string if it's just "/" but who cares
 | ||||
|  | @ -112,59 +66,11 @@ void create_config_folders_if_necessary() { | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| static inline void parse_config_line(Config& config, char* line) { | ||||
|     if (strncmp(line, "logcat_command=", 15 * sizeof(char)) == 0) { | ||||
|         config.logcat_command = &line[15]; | ||||
|     } else if (strncmp(line, "normal_font_size=", 17 * sizeof(char)) == 0) { | ||||
|         float size = to_float(&line[17]); | ||||
|         if (validate_font_size_or_log(size)) { | ||||
|             config.normal_font_size = size; | ||||
|         } | ||||
|     } else if (strncmp(line, "monospace_font_size=", 20 * sizeof(char)) == 0) { | ||||
|         float size = to_float(&line[20]); | ||||
|         if (validate_font_size_or_log(size)) { | ||||
|             config.monospace_font_size = size; | ||||
|         } | ||||
|     } else if (line[0] != '\0') { | ||||
|         throw std::invalid_argument(std::string("unknown config line: ") + line); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static inline Config load_config(FILE* file) { | ||||
|     size_t line_capacity_size = 128 * sizeof(char); | ||||
|     char* line = static_cast<char*>(malloc(line_capacity_size)); | ||||
|     if (line == nullptr) { | ||||
|         throw std::bad_alloc(); | ||||
|     } | ||||
|     Config config; | ||||
| 
 | ||||
|     while (true) { | ||||
|         errno = 0; | ||||
|         if (getline(&line, &line_capacity_size, file) < 0) { | ||||
|             // https://stackoverflow.com/questions/30569981/does-free-set-errno
 | ||||
|             int errsv = errno; | ||||
|             free(line); | ||||
|             if (errsv) { | ||||
|                 throw_system_error(errsv, "getline()"); | ||||
|             } | ||||
|             break; | ||||
|         } | ||||
|         if (line_capacity_size == 0 || line[0] == '\0' || line[0] == '#') { | ||||
|             continue; | ||||
|         } | ||||
|         if (char* newline = strchr(line, '\n')) { | ||||
|             *newline = '\0'; | ||||
|         } | ||||
| 
 | ||||
|         try { | ||||
|             parse_config_line(config, line); | ||||
|         } catch (const std::exception& e) { | ||||
|             free(line); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return config; | ||||
| void from_json(const nlohmann::json& j, Config& config) { | ||||
|     j.at("logcat_command").get_to(config.logcat_command); | ||||
|     j.at("normal_font_size").get_to(config.normal_font_size); | ||||
|     j.at("monospace_font_size").get_to(config.monospace_font_size); | ||||
|     j.at("filters").get_to(config.filters); | ||||
| } | ||||
| 
 | ||||
| Config load_config() { | ||||
|  | @ -175,37 +81,15 @@ Config load_config() { | |||
|         return Config(); | ||||
|     } | ||||
| 
 | ||||
|     return load_config(config_file.get()); | ||||
|     return nlohmann::json::parse(config_file.get()).get<Config>(); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static inline void write_config(FILE* file, const Config& config) { | ||||
|     if (!write("# This is an auto-generated file, modifications will be lost\n" | ||||
|                "# This is a poor man's config file \"format\", there are only three legal lines:\n" | ||||
|                "#     # a comment, only available at the start of a line\n" | ||||
|                "#     (an empty line, no whitespace)\n" | ||||
|                "#     key=value pairs, no spaces around the delimiter, and no unknown keys\n\n", file)) { | ||||
|         throw std::runtime_error("Failed to write info comment"); | ||||
|     } | ||||
|     if (!write("logcat_command=", file)) { | ||||
|         throw std::runtime_error("Failed to write logcat command key"); | ||||
|     } | ||||
|     if (!write(config.logcat_command, file)) { | ||||
|         throw std::runtime_error("Failed to write logcat command value"); | ||||
|     } | ||||
| 
 | ||||
|     if (!write("\nnormal_font_size=", file)) { | ||||
|         throw std::runtime_error("Failed to write normal font size key"); | ||||
|     } | ||||
|     if (!write(std::to_string(config.normal_font_size), file)) { | ||||
|         throw std::runtime_error("Failed to write normal font size value"); | ||||
|     } | ||||
|     if (!write("\nmonospace_font_size=", file)) { | ||||
|         throw std::runtime_error("Failed to write monospace font size key"); | ||||
|     } | ||||
|     if (!write(std::to_string(config.monospace_font_size), file)) { | ||||
|         throw std::runtime_error("Failed to write monospace font size value"); | ||||
|     } | ||||
| void to_json(nlohmann::json& j, const Config& config) { | ||||
|     j["logcat_command"] = config.logcat_command; | ||||
|     j["normal_font_size"] = config.normal_font_size; | ||||
|     j["monospace_font_size"] = config.monospace_font_size; | ||||
|     j["filters"] = config.filters; | ||||
| } | ||||
| 
 | ||||
| void write_config(const Config& config) { | ||||
|  | @ -213,7 +97,9 @@ void write_config(const Config& config) { | |||
|     std::string tmp_config_file_path = config_file_path + ".tmp"; | ||||
| 
 | ||||
|     std::unique_ptr<FILE, decltype(&fclose_and_log)> config_file(fopen_or_raise(tmp_config_file_path.c_str(), "w", false), fclose_and_log); | ||||
|     write_config(config_file.get(), config); | ||||
|     nlohmann::json json_config = config; | ||||
|     std::string str_config = json_config.dump(); | ||||
|     fwrite(str_config.data(), sizeof(char), str_config.size(), config_file.get()); | ||||
|     config_file.reset(); | ||||
| 
 | ||||
|     if (!rename(tmp_config_file_path.c_str(), config_file_path.c_str())) { | ||||
|  |  | |||
							
								
								
									
										6
									
								
								config.h
								
								
								
								
							
							
						
						
									
										6
									
								
								config.h
								
								
								
								
							|  | @ -1,14 +1,20 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <string> | ||||
| #include <nlohmann/json.hpp> | ||||
| #include "filters.h" | ||||
| 
 | ||||
| struct Config { | ||||
|     std::string logcat_command; | ||||
|     float normal_font_size = 13.0f; | ||||
|     float monospace_font_size = 13.0f; | ||||
| 
 | ||||
|     Filters filters; | ||||
| }; | ||||
| 
 | ||||
| std::string get_config_folder(); | ||||
| void create_config_folders_if_necessary(); | ||||
| void from_json(const nlohmann::json& j, Config& config); | ||||
| Config load_config(); | ||||
| void to_json(nlohmann::json& j, const Config& config); | ||||
| void write_config(const Config& config); | ||||
|  |  | |||
|  | @ -33,9 +33,8 @@ static inline void check_for_logcat_items(LogcatThread& logcat_thread, const Fil | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| void event_loop(ImFont* monospace_font, Config& active_config, Filters& active_filters, LogcatThread& logcat_thread, bool* run_event_loop) { | ||||
| void event_loop(ImFont* monospace_font, Config& active_config, LogcatThread& logcat_thread, bool* run_event_loop) { | ||||
|     static Config inactive_config; | ||||
|     static Filters inactive_filters; | ||||
|     static bool show_settings_window = false; | ||||
|     static bool show_filters_window = false; | ||||
|     static bool show_logs_window = false; | ||||
|  | @ -43,7 +42,7 @@ void event_loop(ImFont* monospace_font, Config& active_config, Filters& active_f | |||
|     static std::vector<LogcatEntry> logcat_entries; | ||||
|     static std::vector<size_t> filtered_logcat_entry_offsets; | ||||
| 
 | ||||
|     check_for_logcat_items(logcat_thread, active_filters, logcat_entries, filtered_logcat_entry_offsets); | ||||
|     check_for_logcat_items(logcat_thread, active_config.filters, logcat_entries, filtered_logcat_entry_offsets); | ||||
| 
 | ||||
| #ifndef NDEBUG | ||||
|     debug_window(logcat_thread); | ||||
|  | @ -54,7 +53,7 @@ void event_loop(ImFont* monospace_font, Config& active_config, Filters& active_f | |||
|     } | ||||
| 
 | ||||
|     if (show_filters_window) { | ||||
|         filters_window(active_filters, inactive_filters, logcat_entries, filtered_logcat_entry_offsets, &show_filters_window); | ||||
|         filters_window(active_config, inactive_config, logcat_entries, filtered_logcat_entry_offsets, &show_filters_window); | ||||
|     } | ||||
| 
 | ||||
|     if (show_logs_window) { | ||||
|  | @ -69,6 +68,5 @@ void event_loop(ImFont* monospace_font, Config& active_config, Filters& active_f | |||
|     main_window(log_entries_read == log_entries.size(), monospace_font, | ||||
|         logcat_entries, filtered_logcat_entry_offsets, | ||||
|         active_config, inactive_config, | ||||
|         active_filters, inactive_filters, | ||||
|         &show_settings_window, &show_filters_window, &show_logs_window, run_event_loop); | ||||
| } | ||||
|  |  | |||
|  | @ -3,7 +3,6 @@ | |||
| #include <imgui.h> | ||||
| 
 | ||||
| #include "config.h" | ||||
| #include "filters.h" | ||||
| #include "logcat_thread.h" | ||||
| 
 | ||||
| void event_loop(ImFont* monospace_font, Config& active_config, Filters& active_filters, LogcatThread& logcat_thread, bool* run_event_loop); | ||||
| void event_loop(ImFont* monospace_font, Config& active_config, LogcatThread& logcat_thread, bool* run_event_loop); | ||||
|  |  | |||
							
								
								
									
										155
									
								
								filters.cpp
								
								
								
								
							
							
						
						
									
										155
									
								
								filters.cpp
								
								
								
								
							|  | @ -289,3 +289,158 @@ bool matches(const Filters& filters, const LogcatEntry& entry) { | |||
| 
 | ||||
|     return !ok_filter_exists; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void from_json(const nlohmann::json& j, FilterKey& key) { | ||||
|     const std::string& str = j.get_ref<const nlohmann::json::string_t&>(); | ||||
| 
 | ||||
|     if (str == "BUFFER") key = FilterKey::Buffer; | ||||
|     else if (str == "USER") key = FilterKey::User; | ||||
|     else if (str == "PID") key = FilterKey::PID; | ||||
|     else if (str == "TID") key = FilterKey::TID; | ||||
|     else if (str == "PRIORITY") key = FilterKey::Priority; | ||||
|     else if (str == "TAG") key = FilterKey::Tag; | ||||
|     else if (str == "MESSAGE") key = FilterKey::Message; | ||||
|     else throw std::invalid_argument(std::string("Unknown filter key: ") + str); | ||||
| } | ||||
| 
 | ||||
| void to_json(nlohmann::json& j, const FilterKey& key) { | ||||
|     switch (key) { | ||||
|         case FilterKey::Buffer: j = "BUFFER"; break; | ||||
|         case FilterKey::User: j = "USER"; break; | ||||
|         case FilterKey::PID: j = "PID"; break; | ||||
|         case FilterKey::TID: j = "TID"; break; | ||||
|         case FilterKey::Priority: j = "PRIORITY"; break; | ||||
|         case FilterKey::Tag: j = "TAG"; break; | ||||
|         case FilterKey::Message: j = "MESSAGE"; break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void from_json(const nlohmann::json& j, GroupFilter::Type& type) { | ||||
|     const std::string& str = j.get_ref<const nlohmann::json::string_t&>(); | ||||
| 
 | ||||
|     if (str == "ALL") type = GroupFilter::Type::All; | ||||
|     else if (str == "ANY") type = GroupFilter::Type::Any; | ||||
|     else if (str == "ONE") type = GroupFilter::Type::One; | ||||
|     else if (str == "NONE") type = GroupFilter::Type::None; | ||||
|     else throw std::invalid_argument(std::string("Unknown group filter type: ") + str); | ||||
| } | ||||
| 
 | ||||
| void to_json(nlohmann::json& j, const GroupFilter::Type& type) { | ||||
|     switch (type) { | ||||
|         case GroupFilter::Type::All: j = "ALL"; break; | ||||
|         case GroupFilter::Type::Any: j = "ANY"; break; | ||||
|         case GroupFilter::Type::One: j = "ONE"; break; | ||||
|         case GroupFilter::Type::None: j = "NONE"; break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void from_json(const nlohmann::json& j, std::unique_ptr<Filter>& filter) { | ||||
|     const std::string& filter_type = j.at("type").get_ref<const nlohmann::json::string_t&>(); | ||||
|     bool disabled = j.at("disabled").get<bool>(); | ||||
| 
 | ||||
|     if (filter_type == "INTEGER") { | ||||
|         filter = std::make_unique<IntegerFilter>(j.at("key").get<FilterKey>(), j.at("other").get<size_t>(), j.at("inverted").get<bool>(), disabled); | ||||
|     } else if (filter_type == "STRING") { | ||||
|         filter = std::make_unique<StringFilter>( | ||||
|             j.at("key").get<FilterKey>(), j.at("other").get<std::string>(), j.at("inverted").get<bool>(), j.at("exact_match").get<bool>(), disabled); | ||||
|     } else if (filter_type == "BUFFER") { | ||||
|         unsigned int wanted = 0; | ||||
|         nlohmann::json wanted_json = j.at("wanted"); | ||||
|         if (!wanted_json.is_array()) { | ||||
|             throw std::invalid_argument("Wanted buffer items is not an array"); | ||||
|         } | ||||
|         for (nlohmann::json::const_iterator it = wanted_json.cbegin(); it != wanted_json.cend(); it++) { | ||||
|             wanted |= static_cast<unsigned int>(it->get<Buffer>()); | ||||
|         } | ||||
| 
 | ||||
|         filter = std::make_unique<BufferFilter>(wanted, disabled); | ||||
|     } else if (filter_type == "PRIORITY") { | ||||
|         unsigned int wanted = 0; | ||||
|         nlohmann::json wanted_json = j.at("wanted"); | ||||
|         if (!wanted_json.is_array()) { | ||||
|             throw std::invalid_argument("Wanted priority items is not an array"); | ||||
|         } | ||||
|         for (nlohmann::json::const_iterator it = wanted_json.cbegin(); it != wanted_json.cend(); it++) { | ||||
|             wanted |= static_cast<unsigned int>(it->get<Priority>()); | ||||
|         } | ||||
| 
 | ||||
|         filter = std::make_unique<PriorityFilter>(wanted, disabled); | ||||
|     } else if (filter_type == "GROUP") { | ||||
|         filter = std::make_unique<GroupFilter>( | ||||
|             j.at("filters").get<std::vector<std::unique_ptr<Filter>>>(), j.at("group_type").get<GroupFilter::Type>(), disabled); | ||||
|     } else { | ||||
|         throw std::invalid_argument(std::string("Unknown filter type: ") + filter_type); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void to_json(nlohmann::json& j, const std::unique_ptr<Filter>& filter) { | ||||
|     j["disabled"] = filter->disabled(); | ||||
| 
 | ||||
|     if (const IntegerFilter* ifilter = dynamic_cast<const IntegerFilter*>(filter.get())) { | ||||
|         j["type"] = "INTEGER"; | ||||
|         j["key"] = ifilter->key; | ||||
|         j["other"] = ifilter->other; | ||||
|         j["inverted"] = ifilter->inverted; | ||||
|     } else if (const StringFilter* sfilter = dynamic_cast<const StringFilter*>(filter.get())) { | ||||
|         j["type"] = "STRING"; | ||||
|         j["key"] = sfilter->key; | ||||
|         j["other"] = sfilter->other; | ||||
|         j["inverted"] = sfilter->inverted; | ||||
|         j["exact_match"] = sfilter->exact_match; | ||||
|     } else if (const BufferFilter* bfilter = dynamic_cast<const BufferFilter*>(filter.get())) { | ||||
|         j["type"] = "BUFFER"; | ||||
|         j["wanted"] = nlohmann::json::array(); | ||||
|         if (bfilter->wanted & static_cast<unsigned int>(Buffer::Unknown)) { | ||||
|             j["wanted"].emplace_back(Buffer::Unknown); | ||||
|         } | ||||
|         if (bfilter->wanted & static_cast<unsigned int>(Buffer::Main)) { | ||||
|             j["wanted"].emplace_back(Buffer::Main); | ||||
|         } | ||||
|         if (bfilter->wanted & static_cast<unsigned int>(Buffer::System)) { | ||||
|             j["wanted"].emplace_back(Buffer::System); | ||||
|         } | ||||
|         if (bfilter->wanted & static_cast<unsigned int>(Buffer::Radio)) { | ||||
|             j["wanted"].emplace_back(Buffer::Radio); | ||||
|         } | ||||
|         if (bfilter->wanted & static_cast<unsigned int>(Buffer::Events)) { | ||||
|             j["wanted"].emplace_back(Buffer::Events); | ||||
|         } | ||||
|         if (bfilter->wanted & static_cast<unsigned int>(Buffer::Crash)) { | ||||
|             j["wanted"].emplace_back(Buffer::Crash); | ||||
|         } | ||||
|     } else if (const PriorityFilter* pfilter = dynamic_cast<const PriorityFilter*>(filter.get())) { | ||||
|         j["type"] = "PRIORITY"; | ||||
|         j["wanted"] = nlohmann::json::array(); | ||||
|         if (pfilter->wanted & static_cast<unsigned int>(Priority::Unknown)) { | ||||
|             j["wanted"].emplace_back(Priority::Unknown); | ||||
|         } | ||||
|         if (pfilter->wanted & static_cast<unsigned int>(Priority::Verbose)) { | ||||
|             j["wanted"].emplace_back(Priority::Verbose); | ||||
|         } | ||||
|         if (pfilter->wanted & static_cast<unsigned int>(Priority::Debug)) { | ||||
|             j["wanted"].emplace_back(Priority::Debug); | ||||
|         } | ||||
|         if (pfilter->wanted & static_cast<unsigned int>(Priority::Info)) { | ||||
|             j["wanted"].emplace_back(Priority::Info); | ||||
|         } | ||||
|         if (pfilter->wanted & static_cast<unsigned int>(Priority::Warn)) { | ||||
|             j["wanted"].emplace_back(Priority::Warn); | ||||
|         } | ||||
|         if (pfilter->wanted & static_cast<unsigned int>(Priority::Error)) { | ||||
|             j["wanted"].emplace_back(Priority::Error); | ||||
|         } | ||||
|         if (pfilter->wanted & static_cast<unsigned int>(Priority::Fatal)) { | ||||
|             j["wanted"].emplace_back(Priority::Fatal); | ||||
|         } | ||||
|     } else if (const GroupFilter* gfilter = dynamic_cast<const GroupFilter*>(filter.get())) { | ||||
|         j["type"] = "GROUP"; | ||||
|         j["group_type"] = gfilter->type; | ||||
|         j["filters"] = nlohmann::json::array(); | ||||
|         for (const std::unique_ptr<Filter>& i : gfilter->filters) { | ||||
|             j["filters"].emplace_back(i); | ||||
|         } | ||||
|     } else { | ||||
|         throw std::invalid_argument("Cannot serialize unknown filter"); | ||||
|     } | ||||
| } | ||||
|  |  | |||
							
								
								
									
										16
									
								
								filters.h
								
								
								
								
							
							
						
						
									
										16
									
								
								filters.h
								
								
								
								
							|  | @ -4,6 +4,7 @@ | |||
| #include <vector> | ||||
| #include <memory> | ||||
| #include <utility> | ||||
| #include <nlohmann/json.hpp> | ||||
| 
 | ||||
| #include "logcat_entry.h" | ||||
| #include "pcre2_wrapper.h" | ||||
|  | @ -34,7 +35,7 @@ public: | |||
| 
 | ||||
| class IntegerFilter : public Filter { | ||||
| public: | ||||
|     IntegerFilter(FilterKey key_, size_t other_, bool inverted_ = false, bool disabled = false); | ||||
|     IntegerFilter(FilterKey key_, size_t other_, bool inverted_, bool disabled); | ||||
| 
 | ||||
|     void updated() override; | ||||
|     bool disabled() const override; | ||||
|  | @ -57,7 +58,7 @@ public: | |||
|     StringFilter(const StringFilter&) = delete; | ||||
|     StringFilter& operator=(const StringFilter&) = delete; | ||||
| 
 | ||||
|     StringFilter(FilterKey key_, std::string other_, bool inverted_ = false, bool exact_match_ = true, bool disabled = false); | ||||
|     StringFilter(FilterKey key_, std::string other_, bool inverted_, bool exact_match_, bool disabled); | ||||
| 
 | ||||
|     void updated() override; | ||||
|     bool disabled() const override; | ||||
|  | @ -81,7 +82,7 @@ private: | |||
| 
 | ||||
| class BufferFilter : public Filter { | ||||
| public: | ||||
|     BufferFilter(unsigned int wanted_, bool disabled = false); | ||||
|     BufferFilter(unsigned int wanted_, bool disabled); | ||||
| 
 | ||||
|     void updated() override; | ||||
|     bool disabled() const override; | ||||
|  | @ -122,7 +123,7 @@ public: | |||
|         None = 3, | ||||
|     }; | ||||
| 
 | ||||
|     GroupFilter(std::vector<std::unique_ptr<Filter>> filters_, Type type_, bool disabled = false); | ||||
|     GroupFilter(std::vector<std::unique_ptr<Filter>> filters_, Type type_, bool disabled); | ||||
| 
 | ||||
|     void updated() override; | ||||
|     bool disabled() const override; | ||||
|  | @ -141,3 +142,10 @@ private: | |||
| typedef std::vector<std::pair<std::string, std::unique_ptr<Filter>>> Filters; | ||||
| void copy_filters(Filters& filters, const Filters& other); | ||||
| bool matches(const Filters& filters, const LogcatEntry& entry); | ||||
| 
 | ||||
| void from_json(const nlohmann::json& j, FilterKey& key); | ||||
| void to_json(nlohmann::json& j, const FilterKey& key); | ||||
| void from_json(const nlohmann::json& j, GroupFilter::Type& type); | ||||
| void to_json(nlohmann::json& j, const GroupFilter::Type& type); | ||||
| void from_json(const nlohmann::json& j, std::unique_ptr<Filter>& filter); | ||||
| void to_json(nlohmann::json& j, const std::unique_ptr<Filter>& filter); | ||||
|  |  | |||
|  | @ -122,3 +122,52 @@ std::optional<Buffer> try_parse_buffer(char* buf, size_t length) { | |||
|     if (!strncmp(buffer_str, "crash", 5)) return Buffer::Crash; | ||||
|     return Buffer::Unknown; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void from_json(const nlohmann::json& j, Buffer& buffer) { | ||||
|     const std::string& str = j.get_ref<const nlohmann::json::string_t&>(); | ||||
| 
 | ||||
|     if (str == "UNKNOWN") buffer = Buffer::Unknown; | ||||
|     else if (str == "MAIN") buffer = Buffer::Main; | ||||
|     else if (str == "SYSTEM") buffer = Buffer::System; | ||||
|     else if (str == "RADIO") buffer = Buffer::Radio; | ||||
|     else if (str == "EVENTS") buffer = Buffer::Events; | ||||
|     else if (str == "CRASH") buffer = Buffer::Crash; | ||||
|     else throw std::invalid_argument(std::string("Unknown buffer value: ") + str); | ||||
| } | ||||
| 
 | ||||
| void to_json(nlohmann::json& j, const Buffer& buffer) { | ||||
|     switch (buffer) { | ||||
|         case Buffer::Unknown: j = "UNKNOWN"; break; | ||||
|         case Buffer::Main: j = "MAIN"; break; | ||||
|         case Buffer::System: j = "SYSTEM"; break; | ||||
|         case Buffer::Radio: j = "RADIO"; break; | ||||
|         case Buffer::Events: j = "EVENTS"; break; | ||||
|         case Buffer::Crash: j = "CRASH"; break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void from_json(const nlohmann::json& j, Priority& priority) { | ||||
|     const std::string& str = j.get_ref<const nlohmann::json::string_t&>(); | ||||
| 
 | ||||
|     if (str == "UNKNOWN") priority = Priority::Unknown; | ||||
|     else if (str == "VERBOSE") priority = Priority::Verbose; | ||||
|     else if (str == "DEBUG") priority = Priority::Debug; | ||||
|     else if (str == "INFO") priority = Priority::Info; | ||||
|     else if (str == "WARN") priority = Priority::Warn; | ||||
|     else if (str == "ERROR") priority = Priority::Error; | ||||
|     else if (str == "FATAL") priority = Priority::Fatal; | ||||
|     else throw std::invalid_argument(std::string("Unknown priority value: ") + str); | ||||
| } | ||||
| 
 | ||||
| void to_json(nlohmann::json& j, const Priority& priority) { | ||||
|     switch (priority) { | ||||
|         case Priority::Unknown: j = "UNKNOWN"; break; | ||||
|         case Priority::Verbose: j = "VERBOSE"; break; | ||||
|         case Priority::Debug: j = "DEBUG"; break; | ||||
|         case Priority::Info: j = "INFO"; break; | ||||
|         case Priority::Warn: j = "WARN"; break; | ||||
|         case Priority::Error: j = "ERROR"; break; | ||||
|         case Priority::Fatal: j = "FATAL"; break; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ | |||
| 
 | ||||
| #include <string> | ||||
| #include <optional> | ||||
| #include <nlohmann/json.hpp> | ||||
| 
 | ||||
| enum class Buffer { | ||||
|     Unknown = 0b1, | ||||
|  | @ -38,3 +39,8 @@ const char* priority_to(Priority priority); | |||
| const char* buffer_to(Buffer buffer); | ||||
| std::optional<LogcatEntry> try_parse_logcat_entry(char* buf, size_t length, Buffer buffer); | ||||
| std::optional<Buffer> try_parse_buffer(char* buf, size_t length); | ||||
| 
 | ||||
| void from_json(const nlohmann::json& j, Buffer& buffer); | ||||
| void to_json(nlohmann::json& j, const Buffer& buffer); | ||||
| void from_json(const nlohmann::json& j, Priority& priority); | ||||
| void to_json(nlohmann::json& j, const Priority& priority); | ||||
|  |  | |||
							
								
								
									
										4
									
								
								main.cpp
								
								
								
								
							
							
						
						
									
										4
									
								
								main.cpp
								
								
								
								
							|  | @ -23,8 +23,6 @@ | |||
| int main(int, char**) { | ||||
|     setlocale(LC_TIME, ""); | ||||
| 
 | ||||
|     // TODO add saving
 | ||||
|     Filters filters; | ||||
|     Config config; | ||||
|     try { | ||||
|         config = load_config(); | ||||
|  | @ -164,7 +162,7 @@ int main(int, char**) { | |||
|         ImGui_ImplSDL2_NewFrame(); | ||||
|         ImGui::NewFrame(); | ||||
| 
 | ||||
|         event_loop(monospace_font, config, filters, logcat_thread, &run_event_loop); | ||||
|         event_loop(monospace_font, config, logcat_thread, &run_event_loop); | ||||
| 
 | ||||
|         // Rendering
 | ||||
|         ImGui::Render(); | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ | |||
| 
 | ||||
| #include "../group_panel.h" | ||||
| #include "../filters.h" | ||||
| #include "../config.h" | ||||
| #include "filters.h" | ||||
| 
 | ||||
| static inline void render_integer_filter(IntegerFilter* filter); | ||||
|  | @ -13,6 +14,7 @@ static inline void render_group_filter(GroupFilter* filter); | |||
| static std::unique_ptr<Filter> render_add_filter_popup(); | ||||
| static void update_logcat_entries(const Filters& filters, | ||||
|         const std::vector<LogcatEntry>& logcat_entries, std::vector<size_t>& filtered_logcat_entry_offsets); | ||||
| static void try_write_config(const Config& config); | ||||
| 
 | ||||
| static void render_filter(Filter* filter, std::string* title, bool* request_removal) { | ||||
|     ImGui::PushID(filter); | ||||
|  | @ -182,21 +184,21 @@ static inline void render_group_filter(GroupFilter* filter) { | |||
| 
 | ||||
| static std::unique_ptr<Filter> render_add_filter_popup() { | ||||
|     if (ImGui::Selectable("Buffer")) { | ||||
|         return std::make_unique<BufferFilter>(0); | ||||
|         return std::make_unique<BufferFilter>(0, false); | ||||
|     } else if (ImGui::Selectable("User")) { | ||||
|         return std::make_unique<StringFilter>(FilterKey::User, ""); | ||||
|         return std::make_unique<StringFilter>(FilterKey::User, "", false, true, false); | ||||
|     } else if (ImGui::Selectable("PID")) { | ||||
|         return std::make_unique<IntegerFilter>(FilterKey::PID, 0); | ||||
|         return std::make_unique<IntegerFilter>(FilterKey::PID, 0, false, false); | ||||
|     } else if (ImGui::Selectable("TID")) { | ||||
|         return std::make_unique<IntegerFilter>(FilterKey::TID, 0); | ||||
|         return std::make_unique<IntegerFilter>(FilterKey::TID, 0, false, false); | ||||
|     } else if (ImGui::Selectable("Priority")) { | ||||
|         return std::make_unique<PriorityFilter>(0); | ||||
|         return std::make_unique<PriorityFilter>(0, false); | ||||
|     } else if (ImGui::Selectable("Tag")) { | ||||
|         return std::make_unique<StringFilter>(FilterKey::Tag, ""); | ||||
|         return std::make_unique<StringFilter>(FilterKey::Tag, "", false, true, false); | ||||
|     } else if (ImGui::Selectable("Message")) { | ||||
|         return std::make_unique<StringFilter>(FilterKey::Message, ""); | ||||
|         return std::make_unique<StringFilter>(FilterKey::Message, "", false, true, false); | ||||
|     } else if (ImGui::Selectable("Group of filters")) { | ||||
|         return std::make_unique<GroupFilter>(std::vector<std::unique_ptr<Filter>>(), GroupFilter::Type::All); | ||||
|         return std::make_unique<GroupFilter>(std::vector<std::unique_ptr<Filter>>(), GroupFilter::Type::All, false); | ||||
|     } | ||||
| 
 | ||||
|     return std::unique_ptr<Filter>(); | ||||
|  | @ -212,7 +214,15 @@ static void update_logcat_entries(const Filters& filters, | |||
|     } | ||||
| } | ||||
| 
 | ||||
| void filters_window(Filters& active_filters, Filters& inactive_filters, | ||||
| static void try_write_config(const Config& config) { | ||||
|     try { | ||||
|         write_config(config); | ||||
|     } catch (const std::exception& e) { | ||||
|         log(std::string("Failed to write config: ") + e.what()); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void filters_window(Config& active_config, Config& inactive_config, | ||||
|         const std::vector<LogcatEntry>& logcat_entries, std::vector<size_t>& filtered_logcat_entry_offsets, | ||||
|         bool* p_open) { | ||||
|     if (!ImGui::Begin("Filters", p_open)) { | ||||
|  | @ -222,11 +232,11 @@ void filters_window(Filters& active_filters, Filters& inactive_filters, | |||
| 
 | ||||
|     ImGui::TextUnformatted("You can use regex for strings by prepending \"regex:\""); | ||||
| 
 | ||||
|     for (Filters::iterator it = inactive_filters.begin(); it != inactive_filters.end();) { | ||||
|     for (Filters::iterator it = inactive_config.filters.begin(); it != inactive_config.filters.end();) { | ||||
|         bool removal_requested = false; | ||||
|         render_filter(it->second.get(), &it->first, &removal_requested); | ||||
|         if (removal_requested) { | ||||
|             inactive_filters.erase(it); | ||||
|             inactive_config.filters.erase(it); | ||||
|         } else { | ||||
|             it++; | ||||
|         } | ||||
|  | @ -238,7 +248,7 @@ void filters_window(Filters& active_filters, Filters& inactive_filters, | |||
|     if (ImGui::BeginPopup("addfilter_root")) { | ||||
|         std::unique_ptr<Filter> added_filter = render_add_filter_popup(); | ||||
|         if (added_filter) { | ||||
|             inactive_filters.push_back(std::make_pair("", std::move(added_filter))); | ||||
|             inactive_config.filters.push_back(std::make_pair("", std::move(added_filter))); | ||||
|         } | ||||
|         ImGui::EndPopup(); | ||||
|     } | ||||
|  | @ -246,8 +256,9 @@ void filters_window(Filters& active_filters, Filters& inactive_filters, | |||
|     ImGui::Separator(); | ||||
|     ImVec2 button_size(4 * ImGui::GetFontSize(), 0); | ||||
|     if (ImGui::Button("OK", button_size)) { | ||||
|         active_filters = std::move(inactive_filters); | ||||
|         update_logcat_entries(active_filters, logcat_entries, filtered_logcat_entry_offsets); | ||||
|         active_config.filters = std::move(inactive_config.filters); | ||||
|         try_write_config(active_config); | ||||
|         update_logcat_entries(active_config.filters, logcat_entries, filtered_logcat_entry_offsets); | ||||
|         *p_open = false; | ||||
|     } | ||||
|     ImGui::SameLine(); | ||||
|  | @ -256,8 +267,9 @@ void filters_window(Filters& active_filters, Filters& inactive_filters, | |||
|     } | ||||
|     ImGui::SameLine(); | ||||
|     if (ImGui::Button("Apply", button_size)) { | ||||
|         copy_filters(active_filters, inactive_filters); | ||||
|         update_logcat_entries(active_filters, logcat_entries, filtered_logcat_entry_offsets); | ||||
|         copy_filters(active_config.filters, inactive_config.filters); | ||||
|         try_write_config(active_config); | ||||
|         update_logcat_entries(active_config.filters, logcat_entries, filtered_logcat_entry_offsets); | ||||
|     } | ||||
|     ImGui::End(); | ||||
| } | ||||
|  |  | |||
|  | @ -3,8 +3,8 @@ | |||
| #include <vector> | ||||
| 
 | ||||
| #include "../logcat_entry.h" | ||||
| #include "../filters.h" | ||||
| #include "../config.h" | ||||
| 
 | ||||
| void filters_window(Filters& active_filters, Filters& inactive_filters, | ||||
| void filters_window(Config& active_config, Config& inactive_config, | ||||
|         const std::vector<LogcatEntry>& logcat_entries, std::vector<size_t>& filtered_logcat_entry_offsets, | ||||
|         bool* p_open); | ||||
|  |  | |||
|  | @ -55,7 +55,6 @@ static inline void render_table(ImFont* monospace_font, std::vector<LogcatEntry> | |||
| void main_window(bool latest_log_entries_read, ImFont* monospace_font, | ||||
|         std::vector<LogcatEntry>& logcat_entries, std::vector<size_t>& filtered_logcat_entry_offsets, | ||||
|         const Config& active_config, Config& inactive_config, | ||||
|         const Filters& active_filters, Filters& inactive_filters, | ||||
|         bool* show_settings_window, bool* show_filters_window, bool* show_logs_window, bool* run_event_loop) { | ||||
| 
 | ||||
|     ImGui::SetNextWindowPos(ImGui::GetMainViewport()->WorkPos); | ||||
|  | @ -67,13 +66,15 @@ void main_window(bool latest_log_entries_read, ImFont* monospace_font, | |||
|     } | ||||
| 
 | ||||
|     if (ImGui::Button("Settings") && !*show_settings_window) { | ||||
|         inactive_config = active_config; | ||||
|         inactive_config.logcat_command = active_config.logcat_command; | ||||
|         inactive_config.normal_font_size = active_config.normal_font_size; | ||||
|         inactive_config.monospace_font_size = active_config.monospace_font_size; | ||||
|         *show_settings_window = true; | ||||
|     } | ||||
| 
 | ||||
|     ImGui::SameLine(); | ||||
|     if (ImGui::Button("Filters") && !*show_filters_window) { | ||||
|         copy_filters(inactive_filters, active_filters); | ||||
|         copy_filters(inactive_config.filters, active_config.filters); | ||||
|         *show_filters_window = true; | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -4,11 +4,9 @@ | |||
| #include <vector> | ||||
| 
 | ||||
| #include "../config.h" | ||||
| #include "../filters.h" | ||||
| #include "../logcat_entry.h" | ||||
| 
 | ||||
| void main_window(bool latest_log_entries_read, ImFont* monospace_font, | ||||
|         std::vector<LogcatEntry>& logcat_entries, std::vector<size_t>& filtered_logcat_entry_offsets, | ||||
|         const Config& active_config, Config& inactive_config, | ||||
|         const Filters& active_filters, Filters& inactive_filters, | ||||
|         bool* show_settings_window, bool* show_filters_window, bool* show_logs_window, bool* run_event_loop); | ||||
|  |  | |||
|  | @ -4,12 +4,11 @@ | |||
| #include "../config.h" | ||||
| #include "settings.h" | ||||
| 
 | ||||
| static void write_config_and_update_structures(const Config& config) { | ||||
| static void try_write_config(const Config& config) { | ||||
|     try { | ||||
|         write_config(config); | ||||
|     } catch (const std::exception& e) { | ||||
|         log(std::string("Failed to write config: ") + e.what()); | ||||
|         return; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -31,8 +30,10 @@ void settings_window(Config& active_config, Config& inactive_config, bool* p_ope | |||
|     ImGui::Separator(); | ||||
|     ImVec2 button_size(4 * ImGui::GetFontSize(), 0); | ||||
|     if (ImGui::Button("OK", button_size)) { | ||||
|         active_config = std::move(inactive_config); | ||||
|         write_config_and_update_structures(active_config); | ||||
|         active_config.logcat_command = std::move(inactive_config.logcat_command); | ||||
|         active_config.normal_font_size = inactive_config.normal_font_size; | ||||
|         active_config.monospace_font_size = inactive_config.monospace_font_size; | ||||
|         try_write_config(active_config); | ||||
|         *p_open = false; | ||||
|     } | ||||
|     ImGui::SameLine(); | ||||
|  | @ -41,8 +42,10 @@ void settings_window(Config& active_config, Config& inactive_config, bool* p_ope | |||
|     } | ||||
|     ImGui::SameLine(); | ||||
|     if (ImGui::Button("Apply", button_size)) { | ||||
|         active_config = inactive_config; | ||||
|         write_config_and_update_structures(active_config); | ||||
|         active_config.logcat_command = inactive_config.logcat_command; | ||||
|         active_config.normal_font_size = inactive_config.normal_font_size; | ||||
|         active_config.monospace_font_size = inactive_config.monospace_font_size; | ||||
|         try_write_config(active_config); | ||||
|     } | ||||
|     ImGui::End(); | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue