Use json for config and persist filters

This commit is contained in:
blankie 2023-01-31 21:40:04 +07:00
parent d157e983c4
commit 49a74d552b
Signed by: blankie
GPG Key ID: CC15FC822C7F61F5
15 changed files with 294 additions and 174 deletions

View File

@ -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)

View File

@ -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())) {

View File

@ -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);

View File

@ -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);
} }

View File

@ -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);

View File

@ -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");
}
}

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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();

View File

@ -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();
} }

View File

@ -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);

View File

@ -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;
} }

View File

@ -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);

View File

@ -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();
} }