diff --git a/LogManager.cpp b/LogManager.cpp index 1bedcbe3..0b8dcaaa 100644 --- a/LogManager.cpp +++ b/LogManager.cpp @@ -1,12 +1,15 @@ #include "LogManager.h" #include +#include #include "ResourceManager.h" #define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING #include namespace fs = std::experimental::filesystem; +static const char* log_codes[] = {"CRITICAL", "ERROR", "Message", "Warning", "Notice", "[verbose]", "Debug"}; + LogManager::LogManager() { } @@ -94,36 +97,11 @@ void LogManager::configure(json config, const std::string &defaultDir) const json& loglevel_obj = config["loglevel"]; /*-------------------------------------------------*\ - | If the log level is configured per section, set | - | loglevel for each section | + | Set the log level if configured | \*-------------------------------------------------*/ - if(loglevel_obj.is_object()) + if(loglevel_obj.is_number_integer()) { - for(size_t section = 0; section < sections.size(); ++section) - { - if(loglevel_obj.contains(sections[section])) - { - const json& val = loglevel_obj[sections[section]]; - - if(val.is_number_integer()) - { - loglevels[section] = val; - } - } - } - } - - /*-------------------------------------------------*\ - | If the log level is configured globally, set same | - | loglevel for each section | - \*-------------------------------------------------*/ - else if(loglevel_obj.is_number_integer()) - { - int l = loglevel_obj; - for(size_t section = 0; section < sections.size(); ++section) - { - loglevels[section] = l; - } + loglevel = loglevel_obj; } } @@ -142,10 +120,18 @@ void LogManager::_flush() { for(size_t msg = 0; msg < temp_messages.size(); ++msg) { - int sec = temp_sections[msg]; - if(temp_levels[msg] <= loglevels[sec]) + if(temp_messages[msg]->level <= loglevel) { - log_stream << temp_messages[msg] << std::endl; + // Put the timestamp here + log_stream << log_codes[temp_messages[msg]->level] << ": "; + log_stream << temp_messages[msg]->buffer; + + if(print_source) + { + log_stream << " [" << temp_messages[msg]->filename << ":" << temp_messages[msg]->line << "]"; + } + + log_stream << std::endl; } } @@ -153,8 +139,6 @@ void LogManager::_flush() | Clear temp message buffers after writing them out | \*-------------------------------------------------*/ temp_messages.clear(); - temp_levels.clear(); - temp_sections.clear(); } /*-------------------------------------------------*\ @@ -169,78 +153,164 @@ void LogManager::flush() _flush(); } -void LogManager::append(int section, int level, const char* fmt, ...) +void LogManager::_append(const char* filename, int line, unsigned int level, const char* fmt, va_list va) { - std::lock_guard grd(entry_mutex); - - char buf[1024]; - /*-------------------------------------------------*\ - | Start the variable argument list | + | If a critical message occurs, enable source | + | printing and set loglevel and verbosity to highest| \*-------------------------------------------------*/ - va_list va; - va_start(va, fmt); - - /*-------------------------------------------------*\ - | Return if the log is already open | - \*-------------------------------------------------*/ - if(!log_stream.is_open() && level > loglevels[section]) + if(level == LL_CRITICAL) { - return; + print_source = true; + loglevel = LL_DEBUG; + verbosity = LL_DEBUG; } /*-------------------------------------------------*\ - | Print the section to the log entry | + | Create a new message | \*-------------------------------------------------*/ - int off = sprintf(buf, "[%s]: ", sections[section].c_str()); + PLogMessage mes = std::make_shared(); /*-------------------------------------------------*\ - | Print the log text to the log entry | + | Resize the buffer, then fill in the message text | \*-------------------------------------------------*/ - vsnprintf(buf + off, 1024 - off, fmt, va); + int len = vsnprintf(nullptr, 0, fmt, va); + mes->buffer.resize(len); + vsnprintf(&mes->buffer[0], len + 1, fmt, va); /*-------------------------------------------------*\ - | Write the log entry | + | Fill in message information | \*-------------------------------------------------*/ - if(log_stream.is_open()) + mes->level = level; + mes->filename = filename; + mes->line = line; + + /*-------------------------------------------------*\ + | If the message is within the current verbosity, | + | print it on the screen | + | TODO: Put the timestamp here | + \*-------------------------------------------------*/ + if(level <= verbosity) { - log_stream << buf << std::endl; - } - else - { - temp_levels.push_back(level); - temp_messages.push_back(buf); - temp_sections.push_back(section); - } - - /*-------------------------------------------------*\ - | End the variable argument list | - \*-------------------------------------------------*/ - va_end(va); -} - -int LogManager::registerSection(const char* name, int loglevel) -{ - std::lock_guard grd(section_mutex); - size_t section; - - /*-------------------------------------------------*\ - | Check to see if section already exists, if so, | - | return the existing section value | - \*-------------------------------------------------*/ - for(section = 0; section < sections.size(); section++) - { - if(sections[section] == name) + std::cout << mes->buffer; + if(print_source) { - return section; + std::cout << " [" << mes->filename << ":" << mes->line << "]"; + } + std::cout << std::endl; + } + + /*-------------------------------------------------*\ + | If the message level is LL_MESSAGE or lower, add | + | it to the error queue | + \*-------------------------------------------------*/ + if(level <= LL_MESSAGE) + { + for(size_t idx = 0; idx < error_callbacks.size(); ++idx) + { + error_callbacks[idx].first(error_callbacks[idx].second, mes); } } /*-------------------------------------------------*\ - | If section does not already exist, create it | + | Add the message to the logfile queue | \*-------------------------------------------------*/ - sections.push_back(name); - loglevels.push_back(loglevel); + temp_messages.push_back(mes); - return section; + /*-------------------------------------------------*\ + | Flush the queues | + \*-------------------------------------------------*/ + _flush(); +} + +void LogManager::append(const char* filename, int line, unsigned int level, const char* fmt, va_list va) +{ + std::lock_guard grd(entry_mutex); + + _append(filename, line, level, fmt, va); +} + +void LogManager::append(const char* filename, int line, unsigned int level, const char* fmt, ...) +{ + va_list va; + va_start(va, fmt); + + std::lock_guard grd(entry_mutex); + _append(filename, line, level, fmt, va); + + va_end(va); +} + +void LogManager::setLoglevel(unsigned int level) +{ + /*-------------------------------------------------*\ + | Check that the new log level is valid, otherwise | + | set it within the valid range | + \*-------------------------------------------------*/ + if(level < LL_CRITICAL) + { + level = LL_CRITICAL; + } + + if(level > LL_DEBUG) + { + level = LL_DEBUG; + } + + LOG_DEBUG("Loglevel set to %d", level); + + /*-------------------------------------------------*\ + | Set the new log level | + \*-------------------------------------------------*/ + loglevel = level; +} + +void LogManager::setVerbosity(unsigned int level) +{ + /*-------------------------------------------------*\ + | Check that the new verbosity is valid, otherwise | + | set it within the valid range | + \*-------------------------------------------------*/ + if(level < LL_CRITICAL) + { + level = LL_CRITICAL; + } + + if(level > LL_DEBUG) + { + level = LL_DEBUG; + } + + LOG_DEBUG("Verbosity set to %d", level); + + /*-------------------------------------------------*\ + | Set the new verbosity | + \*-------------------------------------------------*/ + verbosity = level; +} + +void LogManager::setPrintSource(bool v) +{ + LOG_DEBUG("Source code location printouts were %s", v ? "enabled" : "disabled"); + print_source = v; +} + +void LogManager::registerErrorCallback(LogErrorCallback callback, void* receiver) +{ + std::lock_guard grd(entry_mutex); + + error_callbacks.push_back(LogErrorBlock(callback, receiver)); +} + +void LogManager::unregisterErrorCallback(LogErrorCallback callback, void* receiver) +{ + std::lock_guard grd(entry_mutex); + + for(size_t idx = 0; idx < error_callbacks.size(); ++idx) + { + if(error_callbacks[idx].first == callback && error_callbacks[idx].second == receiver) + { + error_callbacks.erase(error_callbacks.begin() + idx); + } + } } diff --git a/LogManager.h b/LogManager.h index bdbd4ee7..5098a71c 100644 --- a/LogManager.h +++ b/LogManager.h @@ -4,20 +4,35 @@ #include #include #include +#include +#include #include "json.hpp" using json = nlohmann::json; enum { - LL_CRITICAL, - LL_ERROR, - LL_WARNING, - LL_NOTICE, - LL_VERBOSE, - LL_DEBUG + LL_CRITICAL, // Critical unrecoverable errors that cause a generalized crash of a module or of the entire app + LL_ERROR, // Local errors that abort an operation + LL_MESSAGE, // Things the user should be informed of, not necessarily errors + LL_WARNING, // Local errors that may cause an operation to have an undefined behavior or may have dangerous/unforeseen consequences + LL_NOTICE, // Initialization messages, significant actions and follow-up information + LL_VERBOSE, // Tracing of commands and performed actions, usually for debug purposes, comments on the higher priority messages + LL_DEBUG, // Deep tracing, "printf-style debugging" alternative, for debug purposes. Such messages should be put all over the code instead of comments }; +struct LogMessage +{ + std::string buffer; + unsigned int level; + const char* filename; + int line; + // int timestamp or float time_offset? TBD +}; +typedef std::shared_ptr PLogMessage; +typedef void(*LogErrorCallback)(void*, PLogMessage); +typedef std::pair LogErrorBlock; + class LogManager { private: @@ -27,23 +42,50 @@ private: ~LogManager(); std::mutex entry_mutex; std::mutex section_mutex; - std::vector sections; - std::vector loglevels; std::ofstream log_stream; - std::vector temp_messages; - std::vector temp_levels; - std::vector temp_sections; + + std::vector error_callbacks; + + // A temporary log message storage to hold them until the stream opens + std::vector temp_messages; + + // A flag that marks if the message source file name and line number should be printed on screen + bool print_source = false; + + // Logfile max level + unsigned int loglevel = LL_NOTICE; + + // Verbosity (stdout) max level + unsigned int verbosity = LL_WARNING; + + // A non-guarded append() + void _append(const char* filename, int line, unsigned int level, const char* fmt, va_list va); + + // A non-guarded flush() void _flush(); public: static LogManager* get(); void configure(json config, const std::string& defaultDir); void flush(); - void append(int section, int level, const char* fmt, ...); - int registerSection(const char* name, int loglevel); + void append(const char* filename, int line, unsigned int level, const char* fmt, va_list va); + void append(const char* filename, int line, unsigned int level, const char* fmt, ...); + void setLoglevel(unsigned int); + void setVerbosity(unsigned int); + void setPrintSource(bool); + void registerErrorCallback(LogErrorCallback callback, void* receiver); + void unregisterErrorCallback(LogErrorCallback callback, void* receiver); + unsigned int getLoglevel() {return loglevel;} + unsigned int getVerbosity() {return verbosity;} }; -#define LogSection(name,level) LogManager::get()->registerSection(name, level) -#define LogAppend LogManager::get()->append +#define LogAppend(level, ...) LogManager::get()->append(__FILE__, __LINE__, level, __VA_ARGS__) +#define LOG_CRITICAL(...) LogAppend(LL_CRITICAL, __VA_ARGS__) +#define LOG_ERROR(...) LogAppend(LL_ERROR, __VA_ARGS__) +#define LOG_MESSAGE(...) LogAppend(LL_MESSAGE, __VA_ARGS__) +#define LOG_WARNING(...) LogAppend(LL_WARNING, __VA_ARGS__) +#define LOG_NOTICE(...) LogAppend(LL_NOTICE, __VA_ARGS__) +#define LOG_VERBOSE(...) LogAppend(LL_VERBOSE, __VA_ARGS__) +#define LOG_DEBUG(...) LogAppend(LL_DEBUG, __VA_ARGS__) #endif // LOGMANAGER_H diff --git a/PluginManager.cpp b/PluginManager.cpp index 45457f71..b424b8ba 100644 --- a/PluginManager.cpp +++ b/PluginManager.cpp @@ -3,9 +3,7 @@ void PluginManager::ScanAndLoadPlugins(bool dark_theme) { - int plugin_section = LogSection("Plugins", LL_DEBUG); - - LogAppend(plugin_section, LL_DEBUG, "Loading plugins"); + LOG_NOTICE("Loading plugins"); std::string OpenRGBConfigDir = ResourceManager::get()->GetConfigurationDirectory(); @@ -33,7 +31,7 @@ void PluginManager::ScanAndLoadPlugins(bool dark_theme) { const std::string filePath = pluginsDir.absoluteFilePath(QString().fromStdString(fileName)).toStdString(); - LogAppend(plugin_section, LL_DEBUG, "Attempting to load: %s", filePath.c_str()); + LOG_VERBOSE("Attempting to load: %s", filePath.c_str()); QPluginLoader loader(pluginsDir.absoluteFilePath(QString().fromStdString(fileName))); @@ -46,7 +44,7 @@ void PluginManager::ScanAndLoadPlugins(bool dark_theme) \*-----------------------------------------------------*/ OpenRGBPlugin->info = OpenRGBPlugin->Initialize(dark_theme, ResourceManager::get()); - LogAppend(plugin_section, LL_DEBUG, "Loaded plugin %s", OpenRGBPlugin->info.PluginName.c_str()); + LOG_VERBOSE("Loaded plugin %s", OpenRGBPlugin->info.PluginName.c_str()); PluginManager::ActivePlugins.push_back(OpenRGBPlugin); } diff --git a/ResourceManager.cpp b/ResourceManager.cpp index e5ac6e29..743e0265 100644 --- a/ResourceManager.cpp +++ b/ResourceManager.cpp @@ -74,6 +74,7 @@ ResourceManager::~ResourceManager() void ResourceManager::RegisterI2CBus(i2c_smbus_interface *bus) { + LOG_NOTICE("Registering an I2C bus: %s", bus->device_name); busses.push_back(bus); } @@ -84,7 +85,7 @@ std::vector & ResourceManager::GetI2CBusses() void ResourceManager::RegisterRGBController(RGBController *rgb_controller) { - LogAppend(LogSection("Detection", LL_DEBUG), LL_DEBUG, "Registering RGB controller: %s", rgb_controller->name.c_str()); + LOG_NOTICE("Registering RGB controller: %s", rgb_controller->name.c_str()); rgb_controllers_hw.push_back(rgb_controller); DeviceListChanged(); @@ -431,14 +432,8 @@ void ResourceManager::DetectDevicesThreadFunction() unsigned int prev_count = 0; bool save_settings = false; std::vector size_used; - int detection_section; - /*-------------------------------------------------*\ - | Create a log debug section named Detection | - \*-------------------------------------------------*/ - detection_section = LogSection("Detection", LL_DEBUG); - - LogAppend(detection_section, LL_NOTICE, "Detection started" ); + LOG_NOTICE("Detection started"); size_used.resize(rgb_controllers_sizes.size()); @@ -493,7 +488,7 @@ void ResourceManager::DetectDevicesThreadFunction() /*-------------------------------------------------*\ | Detect i2c busses | \*-------------------------------------------------*/ - LogAppend(detection_section, LL_DEBUG, "Detecting I2C/SMBus busses"); + LOG_NOTICE("Detecting I2C/SMBus busses"); for(unsigned int i2c_bus_detector_idx = 0; i2c_bus_detector_idx < i2c_bus_detectors.size() && detection_is_required.load(); i2c_bus_detector_idx++) { @@ -504,7 +499,7 @@ void ResourceManager::DetectDevicesThreadFunction() /*-------------------------------------------------*\ | Detect i2c devices | \*-------------------------------------------------*/ - LogAppend(detection_section, LL_DEBUG, "Detecting I2C/SMBus devices"); + LOG_NOTICE("Detecting I2C/SMBus devices"); for(unsigned int i2c_detector_idx = 0; i2c_detector_idx < i2c_device_detectors.size() && detection_is_required.load(); i2c_detector_idx++) { @@ -582,7 +577,7 @@ void ResourceManager::DetectDevicesThreadFunction() if(hid_safe_mode) { - LogAppend(detection_section, LL_NOTICE, "Detecting HID devices in safe mode"); + LOG_NOTICE("Detecting HID devices in safe mode"); /*-----------------------------------------------------------------------------*\ | Loop through all available detectors. If all required information matches, | @@ -592,7 +587,7 @@ void ResourceManager::DetectDevicesThreadFunction() { hid_devices = hid_enumerate(hid_device_detectors[hid_detector_idx].address >> 16, hid_device_detectors[hid_detector_idx].address & 0x0000FFFF); - LogAppend(detection_section, LL_NOTICE, "Trying to run detector for [%s] (for 0x%08hx)", hid_device_detectors[hid_detector_idx].name.c_str(), hid_device_detectors[hid_detector_idx].address); + LOG_VERBOSE("Trying to run detector for [%s] (for 0x%08hx)", hid_device_detectors[hid_detector_idx].name.c_str(), hid_device_detectors[hid_detector_idx].address); current_hid_device = hid_devices; @@ -662,7 +657,7 @@ void ResourceManager::DetectDevicesThreadFunction() } else { - LogAppend(detection_section, LL_NOTICE, "Detecting HID devices"); + LOG_NOTICE("Detecting HID devices"); /*-------------------------------------------------*\ | Iterate through all devices in list and run | @@ -672,6 +667,8 @@ void ResourceManager::DetectDevicesThreadFunction() while(current_hid_device) { + LOG_DEBUG("HID device [0x%04x:$0x%04x]", current_hid_device->vendor_id, current_hid_device->product_id); + detection_string = ""; DetectionProgressChanged(); @@ -700,6 +697,7 @@ void ResourceManager::DetectDevicesThreadFunction() ) { detection_string = hid_device_detectors[hid_detector_idx].name.c_str(); + LOG_DEBUG("Detector found: %s", detection_string); /*-------------------------------------------------*\ | Check if this detector is enabled or needs to be | @@ -713,6 +711,8 @@ void ResourceManager::DetectDevicesThreadFunction() if(this_device_enabled) { + LOG_DEBUG("Detector %s is enabled, running it", detection_string); + DetectionProgressChanged(); hid_device_detectors[hid_detector_idx].function(current_hid_device, hid_device_detectors[hid_detector_idx].name); @@ -744,7 +744,7 @@ void ResourceManager::DetectDevicesThreadFunction() /*-------------------------------------------------*\ | Detect other devices | \*-------------------------------------------------*/ - LogAppend(detection_section, LL_NOTICE, "Detecting other devices"); + LOG_NOTICE("Detecting other devices"); for(unsigned int detector_idx = 0; detector_idx < device_detectors.size() && detection_is_required.load(); detector_idx++) { @@ -812,18 +812,19 @@ void ResourceManager::DetectDevicesThreadFunction() if(save_settings) { - LogAppend(detection_section, LL_NOTICE, "Saving detector settings"); + LOG_NOTICE("Saving detector settings"); settings_manager->SetSettings("Detectors", detector_settings); settings_manager->SaveSettings(); } - LogAppend(detection_section, LL_NOTICE, "Detection completed"); + LOG_NOTICE("Detection completed"); } void ResourceManager::StopDeviceDetection() { + LOG_NOTICE("Detection abort requested"); detection_is_required = false; detection_percent = 100; detection_string = "Stopping"; diff --git a/cli.cpp b/cli.cpp index 5fca21b8..9dc23a8f 100644 --- a/cli.cpp +++ b/cli.cpp @@ -10,6 +10,7 @@ #include "i2c_smbus.h" #include "NetworkClient.h" #include "NetworkServer.h" +#include "LogManager.h" /*-------------------------------------------------------------*\ | Quirk for MSVC; which doesn't support this case-insensitive | @@ -370,7 +371,7 @@ void OptionHelp() help_text += "-s, --size [0-N] Sets the new size of the specified device zone.\n"; help_text += " Must be specified after specifying a zone.\n"; help_text += " If the specified size is out of range, or the zone does not offer resizing capability, the size will not be changed\n"; - help_text += "-v, --version Display version and software build information\n"; + help_text += "-V, --version Display version and software build information\n"; help_text += "-p, --profile filename.orp Load the profile from filename.orp\n"; help_text += "-sp, --save-profile filename.orp Save the given settings to profile filename.orp\n"; help_text += "--i2c-tools Shows the I2C/SMBus Tools page in the GUI. Implies --gui, even if not specified.\n"; @@ -380,6 +381,10 @@ void OptionHelp() help_text += "--config path Use a custom path instead of the global configuration directory.\n"; help_text += "--nodetect Do not try to detect hardware at startup.\n"; help_text += "--noautoconnect Do not try to autoconnect to a local server at startup.\n"; + help_text += "--loglevel Set the log level (0: critical to 6: debug).\n"; + help_text += "--print-source Print the source code file and line number for each log entry.\n"; + help_text += "-v, --verbose Print log messages to stdout.\n"; + help_text += "-vv, --very-verbose Print debug messages and log messages to stdout.\n"; std::cout << help_text << std::endl; } @@ -951,6 +956,8 @@ unsigned int cli_pre_detection(int argc, char *argv[]) std::string option = argv[arg_index]; std::string argument = ""; + LOG_DEBUG("Parsing CLI option: %s", option.c_str()); + /*---------------------------------------------------------*\ | Handle options that take an argument | \*---------------------------------------------------------*/ @@ -1087,6 +1094,75 @@ unsigned int cli_pre_detection(int argc, char *argv[]) arg_index++; } + /*---------------------------------------------------------*\ + | --loglevel | + \*---------------------------------------------------------*/ + else if(option == "--loglevel") + { + if (argument != "") + { + try + { + int level = std::stoi(argument); + if (level >= 0 && level <= LL_DEBUG) + { + LogManager::get()->setLoglevel(level); + } + else + { + std::cout << "Error: Loglevel out of range: " << level << " (0-6)" << std::endl; + print_help = true; + break; + } + } + catch(std::invalid_argument& e) + { + if(!strcasecmp(argument.c_str(), "critical")) + { + LogManager::get()->setLoglevel(LL_CRITICAL); + } + else if(!strcasecmp(argument.c_str(), "error")) + { + LogManager::get()->setLoglevel(LL_ERROR); + } + else if(!strcasecmp(argument.c_str(), "message")) + { + LogManager::get()->setLoglevel(LL_MESSAGE); + } + else if(!strcasecmp(argument.c_str(), "warning")) + { + LogManager::get()->setLoglevel(LL_WARNING); + } + else if(!strcasecmp(argument.c_str(), "notice")) + { + LogManager::get()->setLoglevel(LL_NOTICE); + } + else if(!strcasecmp(argument.c_str(), "verbose")) + { + LogManager::get()->setLoglevel(LL_VERBOSE); + } + else if(!strcasecmp(argument.c_str(), "debug")) + { + LogManager::get()->setLoglevel(LL_DEBUG); + } + else + { + std::cout << "Error: invalid loglevel" << std::endl; + print_help = true; + break; + } + } + } + else + { + std::cout << "Error: Missing argument for --server-port" << std::endl; + print_help = true; + break; + } + cfg_args++; + arg_index++; + } + /*---------------------------------------------------------*\ | --gui (no arguments) | \*---------------------------------------------------------*/ @@ -1104,7 +1180,7 @@ unsigned int cli_pre_detection(int argc, char *argv[]) } /*---------------------------------------------------------*\ - | --startminimized | + | --startminimized (no arguments) | \*---------------------------------------------------------*/ else if(option == "--startminimized") { @@ -1121,14 +1197,38 @@ unsigned int cli_pre_detection(int argc, char *argv[]) } /*---------------------------------------------------------*\ - | -v / --version (no arguments) | + | -V / --version (no arguments) | \*---------------------------------------------------------*/ - else if(option == "--version" || option == "-v") + else if(option == "--version" || option == "-V") { OptionVersion(); exit(0); } + /*---------------------------------------------------------*\ + | -v / --verbose (no arguments) | + \*---------------------------------------------------------*/ + else if(option == "--verbose" || option == "-v") + { + LogManager::get()->setVerbosity(LL_VERBOSE); + } + + /*---------------------------------------------------------*\ + | -vv / --very-verbose (no arguments) | + \*---------------------------------------------------------*/ + else if(option == "--very-verbose" || option == "-vv") + { + LogManager::get()->setVerbosity(LL_DEBUG); + } + + /*---------------------------------------------------------*\ + | --print-source (no arguments) | + \*---------------------------------------------------------*/ + else if(option == "--print-source") + { + LogManager::get()->setPrintSource(true); + } + /*---------------------------------------------------------*\ | Any unrecognized arguments trigger the post-detection CLI | \*---------------------------------------------------------*/ diff --git a/main.cpp b/main.cpp index 1c6f5692..f76e3b73 100644 --- a/main.cpp +++ b/main.cpp @@ -13,10 +13,12 @@ #include "ProfileManager.h" #include "RGBController.h" #include "i2c_smbus.h" +#include "LogManager.h" #include #include #include #include +#include #include "OpenRGBDialog2.h" @@ -132,6 +134,50 @@ bool AttemptLocalConnection() return success; } +/******************************************************************************************\ +* * +* MessageBoxCallback * +* * +* Displays a message box when an error occurs. Only call once GUI is initialized * +* * +\******************************************************************************************/ + +void MessageBoxCallback(void*, PLogMessage message) +{ + /*---------------------------------------------------------*\ + | Create a message box | + \*---------------------------------------------------------*/ + QMessageBox box; + + /*---------------------------------------------------------*\ + | Set the box main text to the log message text | + \*---------------------------------------------------------*/ + box.setText(QString::fromStdString(message->buffer)); + + /*---------------------------------------------------------*\ + | Set the informative text from the message information | + \*---------------------------------------------------------*/ + QString info = "Occured in "; + info += message->filename; + info += " on line " + QVariant(message->line).toString(); + box.setInformativeText(info); + + /*---------------------------------------------------------*\ + | Set the message box icon according to message level | + \*---------------------------------------------------------*/ + switch(message->level) + { + case LL_CRITICAL: box.setIcon(QMessageBox::Critical); break; + case LL_ERROR: box.setIcon(QMessageBox::Warning); break; + case LL_MESSAGE: box.setIcon(QMessageBox::Information); break; + } + + /*---------------------------------------------------------*\ + | Show the message box | + \*---------------------------------------------------------*/ + box.exec(); +} + /******************************************************************************************\ * * * main * @@ -232,6 +278,11 @@ int main(int argc, char* argv[]) QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QApplication a(argc, argv); + /*---------------------------------------------------------*\ + | Register the message box callback with the log manager | + \*---------------------------------------------------------*/ + LogManager::get()->registerErrorCallback(&MessageBoxCallback, nullptr); + Ui::OpenRGBDialog2 dlg; if(ret_flags & RET_FLAG_I2C_TOOLS)