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(SDL2 REQUIRED) | ||||||
| find_package(Freetype 2 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(IMGUI_LIBS SDL2 Freetype::Freetype) | ||||||
| 
 | 
 | ||||||
| set(INCLUDES imgui imgui/backends imgui/misc/cpp imgui/freetype /usr/include/SDL2) | 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 <cstdlib> | ||||||
| #include <cstring> | #include <cstring> | ||||||
| #include <cmath> |  | ||||||
| #include <cfloat> |  | ||||||
| #include <sys/stat.h> | #include <sys/stat.h> | ||||||
| #include <string> | #include <string> | ||||||
| #include <memory> | #include <nlohmann/json.hpp> | ||||||
| 
 | 
 | ||||||
| #include "log.h" | #include "log.h" | ||||||
| #include "misc.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)); |     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() { | std::string get_config_folder() { | ||||||
|     const char* path; |     const char* path; | ||||||
|  | @ -86,7 +40,7 @@ std::string get_config_folder() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::string get_config_file_path() { | 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
 | // 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) { | void from_json(const nlohmann::json& j, Config& config) { | ||||||
|     if (strncmp(line, "logcat_command=", 15 * sizeof(char)) == 0) { |     j.at("logcat_command").get_to(config.logcat_command); | ||||||
|         config.logcat_command = &line[15]; |     j.at("normal_font_size").get_to(config.normal_font_size); | ||||||
|     } else if (strncmp(line, "normal_font_size=", 17 * sizeof(char)) == 0) { |     j.at("monospace_font_size").get_to(config.monospace_font_size); | ||||||
|         float size = to_float(&line[17]); |     j.at("filters").get_to(config.filters); | ||||||
|         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; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Config load_config() { | Config load_config() { | ||||||
|  | @ -175,37 +81,15 @@ Config load_config() { | ||||||
|         return 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) { | void to_json(nlohmann::json& j, const Config& config) { | ||||||
|     if (!write("# This is an auto-generated file, modifications will be lost\n" |     j["logcat_command"] = config.logcat_command; | ||||||
|                "# This is a poor man's config file \"format\", there are only three legal lines:\n" |     j["normal_font_size"] = config.normal_font_size; | ||||||
|                "#     # a comment, only available at the start of a line\n" |     j["monospace_font_size"] = config.monospace_font_size; | ||||||
|                "#     (an empty line, no whitespace)\n" |     j["filters"] = config.filters; | ||||||
|                "#     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 write_config(const Config& config) { | 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::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); |     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(); |     config_file.reset(); | ||||||
| 
 | 
 | ||||||
|     if (!rename(tmp_config_file_path.c_str(), config_file_path.c_str())) { |     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 | #pragma once | ||||||
| 
 | 
 | ||||||
| #include <string> | #include <string> | ||||||
|  | #include <nlohmann/json.hpp> | ||||||
|  | #include "filters.h" | ||||||
| 
 | 
 | ||||||
| struct Config { | struct Config { | ||||||
|     std::string logcat_command; |     std::string logcat_command; | ||||||
|     float normal_font_size = 13.0f; |     float normal_font_size = 13.0f; | ||||||
|     float monospace_font_size = 13.0f; |     float monospace_font_size = 13.0f; | ||||||
|  | 
 | ||||||
|  |     Filters filters; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| std::string get_config_folder(); | std::string get_config_folder(); | ||||||
| void create_config_folders_if_necessary(); | void create_config_folders_if_necessary(); | ||||||
|  | void from_json(const nlohmann::json& j, Config& config); | ||||||
| Config load_config(); | Config load_config(); | ||||||
|  | void to_json(nlohmann::json& j, const Config& config); | ||||||
| void write_config(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 Config inactive_config; | ||||||
|     static Filters inactive_filters; |  | ||||||
|     static bool show_settings_window = false; |     static bool show_settings_window = false; | ||||||
|     static bool show_filters_window = false; |     static bool show_filters_window = false; | ||||||
|     static bool show_logs_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<LogcatEntry> logcat_entries; | ||||||
|     static std::vector<size_t> filtered_logcat_entry_offsets; |     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 | #ifndef NDEBUG | ||||||
|     debug_window(logcat_thread); |     debug_window(logcat_thread); | ||||||
|  | @ -54,7 +53,7 @@ void event_loop(ImFont* monospace_font, Config& active_config, Filters& active_f | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (show_filters_window) { |     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) { |     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, |     main_window(log_entries_read == log_entries.size(), monospace_font, | ||||||
|         logcat_entries, filtered_logcat_entry_offsets, |         logcat_entries, filtered_logcat_entry_offsets, | ||||||
|         active_config, inactive_config, |         active_config, inactive_config, | ||||||
|         active_filters, inactive_filters, |  | ||||||
|         &show_settings_window, &show_filters_window, &show_logs_window, run_event_loop); |         &show_settings_window, &show_filters_window, &show_logs_window, run_event_loop); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -3,7 +3,6 @@ | ||||||
| #include <imgui.h> | #include <imgui.h> | ||||||
| 
 | 
 | ||||||
| #include "config.h" | #include "config.h" | ||||||
| #include "filters.h" |  | ||||||
| #include "logcat_thread.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; |     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 <vector> | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <utility> | #include <utility> | ||||||
|  | #include <nlohmann/json.hpp> | ||||||
| 
 | 
 | ||||||
| #include "logcat_entry.h" | #include "logcat_entry.h" | ||||||
| #include "pcre2_wrapper.h" | #include "pcre2_wrapper.h" | ||||||
|  | @ -34,7 +35,7 @@ public: | ||||||
| 
 | 
 | ||||||
| class IntegerFilter : public Filter { | class IntegerFilter : public Filter { | ||||||
| public: | 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; |     void updated() override; | ||||||
|     bool disabled() const override; |     bool disabled() const override; | ||||||
|  | @ -57,7 +58,7 @@ public: | ||||||
|     StringFilter(const StringFilter&) = delete; |     StringFilter(const StringFilter&) = delete; | ||||||
|     StringFilter& operator=(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; |     void updated() override; | ||||||
|     bool disabled() const override; |     bool disabled() const override; | ||||||
|  | @ -81,7 +82,7 @@ private: | ||||||
| 
 | 
 | ||||||
| class BufferFilter : public Filter { | class BufferFilter : public Filter { | ||||||
| public: | public: | ||||||
|     BufferFilter(unsigned int wanted_, bool disabled = false); |     BufferFilter(unsigned int wanted_, bool disabled); | ||||||
| 
 | 
 | ||||||
|     void updated() override; |     void updated() override; | ||||||
|     bool disabled() const override; |     bool disabled() const override; | ||||||
|  | @ -122,7 +123,7 @@ public: | ||||||
|         None = 3, |         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; |     void updated() override; | ||||||
|     bool disabled() const override; |     bool disabled() const override; | ||||||
|  | @ -141,3 +142,10 @@ private: | ||||||
| typedef std::vector<std::pair<std::string, std::unique_ptr<Filter>>> Filters; | typedef std::vector<std::pair<std::string, std::unique_ptr<Filter>>> Filters; | ||||||
| void copy_filters(Filters& filters, const Filters& other); | void copy_filters(Filters& filters, const Filters& other); | ||||||
| bool matches(const Filters& filters, const LogcatEntry& entry); | 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; |     if (!strncmp(buffer_str, "crash", 5)) return Buffer::Crash; | ||||||
|     return Buffer::Unknown; |     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 <string> | ||||||
| #include <optional> | #include <optional> | ||||||
|  | #include <nlohmann/json.hpp> | ||||||
| 
 | 
 | ||||||
| enum class Buffer { | enum class Buffer { | ||||||
|     Unknown = 0b1, |     Unknown = 0b1, | ||||||
|  | @ -38,3 +39,8 @@ const char* priority_to(Priority priority); | ||||||
| const char* buffer_to(Buffer buffer); | const char* buffer_to(Buffer buffer); | ||||||
| std::optional<LogcatEntry> try_parse_logcat_entry(char* buf, size_t length, 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); | 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**) { | int main(int, char**) { | ||||||
|     setlocale(LC_TIME, ""); |     setlocale(LC_TIME, ""); | ||||||
| 
 | 
 | ||||||
|     // TODO add saving
 |  | ||||||
|     Filters filters; |  | ||||||
|     Config config; |     Config config; | ||||||
|     try { |     try { | ||||||
|         config = load_config(); |         config = load_config(); | ||||||
|  | @ -164,7 +162,7 @@ int main(int, char**) { | ||||||
|         ImGui_ImplSDL2_NewFrame(); |         ImGui_ImplSDL2_NewFrame(); | ||||||
|         ImGui::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
 |         // Rendering
 | ||||||
|         ImGui::Render(); |         ImGui::Render(); | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ | ||||||
| 
 | 
 | ||||||
| #include "../group_panel.h" | #include "../group_panel.h" | ||||||
| #include "../filters.h" | #include "../filters.h" | ||||||
|  | #include "../config.h" | ||||||
| #include "filters.h" | #include "filters.h" | ||||||
| 
 | 
 | ||||||
| static inline void render_integer_filter(IntegerFilter* filter); | 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 std::unique_ptr<Filter> render_add_filter_popup(); | ||||||
| static void update_logcat_entries(const Filters& filters, | static void update_logcat_entries(const Filters& filters, | ||||||
|         const std::vector<LogcatEntry>& logcat_entries, std::vector<size_t>& filtered_logcat_entry_offsets); |         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) { | static void render_filter(Filter* filter, std::string* title, bool* request_removal) { | ||||||
|     ImGui::PushID(filter); |     ImGui::PushID(filter); | ||||||
|  | @ -182,21 +184,21 @@ static inline void render_group_filter(GroupFilter* filter) { | ||||||
| 
 | 
 | ||||||
| static std::unique_ptr<Filter> render_add_filter_popup() { | static std::unique_ptr<Filter> render_add_filter_popup() { | ||||||
|     if (ImGui::Selectable("Buffer")) { |     if (ImGui::Selectable("Buffer")) { | ||||||
|         return std::make_unique<BufferFilter>(0); |         return std::make_unique<BufferFilter>(0, false); | ||||||
|     } else if (ImGui::Selectable("User")) { |     } 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")) { |     } 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")) { |     } 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")) { |     } else if (ImGui::Selectable("Priority")) { | ||||||
|         return std::make_unique<PriorityFilter>(0); |         return std::make_unique<PriorityFilter>(0, false); | ||||||
|     } else if (ImGui::Selectable("Tag")) { |     } 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")) { |     } 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")) { |     } 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>(); |     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, |         const std::vector<LogcatEntry>& logcat_entries, std::vector<size_t>& filtered_logcat_entry_offsets, | ||||||
|         bool* p_open) { |         bool* p_open) { | ||||||
|     if (!ImGui::Begin("Filters", 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:\""); |     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; |         bool removal_requested = false; | ||||||
|         render_filter(it->second.get(), &it->first, &removal_requested); |         render_filter(it->second.get(), &it->first, &removal_requested); | ||||||
|         if (removal_requested) { |         if (removal_requested) { | ||||||
|             inactive_filters.erase(it); |             inactive_config.filters.erase(it); | ||||||
|         } else { |         } else { | ||||||
|             it++; |             it++; | ||||||
|         } |         } | ||||||
|  | @ -238,7 +248,7 @@ void filters_window(Filters& active_filters, Filters& inactive_filters, | ||||||
|     if (ImGui::BeginPopup("addfilter_root")) { |     if (ImGui::BeginPopup("addfilter_root")) { | ||||||
|         std::unique_ptr<Filter> added_filter = render_add_filter_popup(); |         std::unique_ptr<Filter> added_filter = render_add_filter_popup(); | ||||||
|         if (added_filter) { |         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(); |         ImGui::EndPopup(); | ||||||
|     } |     } | ||||||
|  | @ -246,8 +256,9 @@ void filters_window(Filters& active_filters, Filters& inactive_filters, | ||||||
|     ImGui::Separator(); |     ImGui::Separator(); | ||||||
|     ImVec2 button_size(4 * ImGui::GetFontSize(), 0); |     ImVec2 button_size(4 * ImGui::GetFontSize(), 0); | ||||||
|     if (ImGui::Button("OK", button_size)) { |     if (ImGui::Button("OK", button_size)) { | ||||||
|         active_filters = std::move(inactive_filters); |         active_config.filters = std::move(inactive_config.filters); | ||||||
|         update_logcat_entries(active_filters, logcat_entries, filtered_logcat_entry_offsets); |         try_write_config(active_config); | ||||||
|  |         update_logcat_entries(active_config.filters, logcat_entries, filtered_logcat_entry_offsets); | ||||||
|         *p_open = false; |         *p_open = false; | ||||||
|     } |     } | ||||||
|     ImGui::SameLine(); |     ImGui::SameLine(); | ||||||
|  | @ -256,8 +267,9 @@ void filters_window(Filters& active_filters, Filters& inactive_filters, | ||||||
|     } |     } | ||||||
|     ImGui::SameLine(); |     ImGui::SameLine(); | ||||||
|     if (ImGui::Button("Apply", button_size)) { |     if (ImGui::Button("Apply", button_size)) { | ||||||
|         copy_filters(active_filters, inactive_filters); |         copy_filters(active_config.filters, inactive_config.filters); | ||||||
|         update_logcat_entries(active_filters, logcat_entries, filtered_logcat_entry_offsets); |         try_write_config(active_config); | ||||||
|  |         update_logcat_entries(active_config.filters, logcat_entries, filtered_logcat_entry_offsets); | ||||||
|     } |     } | ||||||
|     ImGui::End(); |     ImGui::End(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -3,8 +3,8 @@ | ||||||
| #include <vector> | #include <vector> | ||||||
| 
 | 
 | ||||||
| #include "../logcat_entry.h" | #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, |         const std::vector<LogcatEntry>& logcat_entries, std::vector<size_t>& filtered_logcat_entry_offsets, | ||||||
|         bool* p_open); |         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, | void main_window(bool latest_log_entries_read, ImFont* monospace_font, | ||||||
|         std::vector<LogcatEntry>& logcat_entries, std::vector<size_t>& filtered_logcat_entry_offsets, |         std::vector<LogcatEntry>& logcat_entries, std::vector<size_t>& filtered_logcat_entry_offsets, | ||||||
|         const Config& active_config, Config& inactive_config, |         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) { |         bool* show_settings_window, bool* show_filters_window, bool* show_logs_window, bool* run_event_loop) { | ||||||
| 
 | 
 | ||||||
|     ImGui::SetNextWindowPos(ImGui::GetMainViewport()->WorkPos); |     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) { |     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; |         *show_settings_window = true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     ImGui::SameLine(); |     ImGui::SameLine(); | ||||||
|     if (ImGui::Button("Filters") && !*show_filters_window) { |     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; |         *show_filters_window = true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -4,11 +4,9 @@ | ||||||
| #include <vector> | #include <vector> | ||||||
| 
 | 
 | ||||||
| #include "../config.h" | #include "../config.h" | ||||||
| #include "../filters.h" |  | ||||||
| #include "../logcat_entry.h" | #include "../logcat_entry.h" | ||||||
| 
 | 
 | ||||||
| void main_window(bool latest_log_entries_read, ImFont* monospace_font, | void main_window(bool latest_log_entries_read, ImFont* monospace_font, | ||||||
|         std::vector<LogcatEntry>& logcat_entries, std::vector<size_t>& filtered_logcat_entry_offsets, |         std::vector<LogcatEntry>& logcat_entries, std::vector<size_t>& filtered_logcat_entry_offsets, | ||||||
|         const Config& active_config, Config& inactive_config, |         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); |         bool* show_settings_window, bool* show_filters_window, bool* show_logs_window, bool* run_event_loop); | ||||||
|  |  | ||||||
|  | @ -4,12 +4,11 @@ | ||||||
| #include "../config.h" | #include "../config.h" | ||||||
| #include "settings.h" | #include "settings.h" | ||||||
| 
 | 
 | ||||||
| static void write_config_and_update_structures(const Config& config) { | static void try_write_config(const Config& config) { | ||||||
|     try { |     try { | ||||||
|         write_config(config); |         write_config(config); | ||||||
|     } catch (const std::exception& e) { |     } catch (const std::exception& e) { | ||||||
|         log(std::string("Failed to write config: ") + e.what()); |         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(); |     ImGui::Separator(); | ||||||
|     ImVec2 button_size(4 * ImGui::GetFontSize(), 0); |     ImVec2 button_size(4 * ImGui::GetFontSize(), 0); | ||||||
|     if (ImGui::Button("OK", button_size)) { |     if (ImGui::Button("OK", button_size)) { | ||||||
|         active_config = std::move(inactive_config); |         active_config.logcat_command = std::move(inactive_config.logcat_command); | ||||||
|         write_config_and_update_structures(active_config); |         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; |         *p_open = false; | ||||||
|     } |     } | ||||||
|     ImGui::SameLine(); |     ImGui::SameLine(); | ||||||
|  | @ -41,8 +42,10 @@ void settings_window(Config& active_config, Config& inactive_config, bool* p_ope | ||||||
|     } |     } | ||||||
|     ImGui::SameLine(); |     ImGui::SameLine(); | ||||||
|     if (ImGui::Button("Apply", button_size)) { |     if (ImGui::Button("Apply", button_size)) { | ||||||
|         active_config = inactive_config; |         active_config.logcat_command = inactive_config.logcat_command; | ||||||
|         write_config_and_update_structures(active_config); |         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(); |     ImGui::End(); | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue