#include "stereokit.h" #include "sk_memory.h" #include "log.h" #include "libraries/stref.h" #include "libraries/array.h" #include "platforms/platform_utils.h" #include #include #include #include namespace sk { /////////////////////////////////////////// typedef void(*log_listener_t)(log_, const char *); array_t log_listeners = {}; log_ log_filter = log_diagnostic; log_colors_ log_colors = log_colors_ansi; char *log_fail_reason_str = nullptr; int32_t log_fail_confidence = -1; const char *log_tags[] = { "blk", "BLK", "red", "RED", "grn", "GRN", "ylw", "YLW", "blu", "BLU", "mag", "MAG", "cyn", "CYN", "wht", "WHT", "clr", }; #define LOG_C_CLEAR "\033[0;;0m" #define LOG_NONE_CLEAR "" const char *log_colorcodes[_countof(log_tags)] = { "\033[0;30m", "\033[1;30m", "\033[0;31m", "\033[1;31m", "\033[0;32m", "\033[1;32m", "\033[0;33m", "\033[1;33m", "\033[0;34m", "\033[1;34m", "\033[0;35m", "\033[1;35m", "\033[0;36m", "\033[1;36m", "\033[0;37m", "\033[1;37m", LOG_C_CLEAR, }; /////////////////////////////////////////// int32_t log_count_color_tags(const char* ch, int32_t *out_len) { *out_len = 0; int32_t tags = 0; int32_t curr = 0; while (*ch != '\0') { // Looking for tags that look like this: // <~red>red text here<~clr> if ( *ch == '<') curr = 1; else if (curr == 1 && *ch != '~') curr = 0; else if (curr == 5) { curr = 0; if (*ch == '>') tags += 1; } else if (curr > 0) curr += 1; ch += 1; *out_len += 1; } return tags; } /////////////////////////////////////////// void log_replace_colors(const char *src, char *dest, const char **color_tags, const char **color_codes, int32_t count, int32_t code_size) { // New string with new color values int32_t curr = 0; while (*src != '\0') { // Logic while in a tag if ( *src == '<') curr = 1; else if (curr == 1 && *src != '~') curr = 0; else if (curr == 5) { curr = 0; if (*src == '>') { int key_id = count - 1; for (int i = 0; i < count; i++) { if (string_startswith(src-3, color_tags[i])) { key_id = i; break; } } if (code_size != 0) memcpy(dest, color_codes[key_id], code_size); dest += code_size; src += 1; continue; } } else if (curr > 0) curr += 1; // Copy the character if (curr == 0) { *dest = *src; dest += 1; } src += 1; } *dest = *src; } /////////////////////////////////////////// void log_write(log_ level, const char *text) { if (level < log_filter) return; const char* tag = ""; const char* color = ""; switch (level) { case log_none: return; case log_diagnostic: tag = "diagnostic"; color = log_colorcodes[8]; break; case log_inform: tag = "info"; color = log_colorcodes[12]; break; case log_warning: tag = "warning"; color = log_colorcodes[6]; break; case log_error: tag = "error"; color = log_colorcodes[2]; break; default: break; } int32_t text_len = 0; int32_t color_tags = log_colors == log_colors_ansi ? log_count_color_tags(text, &text_len) : 0; // Set up some memory if we have color tags we need to replace char* replace_buffer = nullptr; if (color_tags > 0) replace_buffer = (char*)alloca(sizeof(char) * ((text_len - color_tags*7) + color_tags * 8 + 1)); #if defined(SK_OS_WINDOWS) || defined(SK_OS_LINUX) const char* colored_text = text; if (color_tags > 0) { log_replace_colors(text, replace_buffer, log_tags, log_colorcodes, _countof(log_tags), sizeof(LOG_C_CLEAR) - 1); colored_text = replace_buffer; } printf("[SK %s%s" LOG_C_CLEAR "] %s\n", color, tag, colored_text); #endif // The remaining outputs don't support colors const char* plain_text = text; if (color_tags > 0) { log_replace_colors(text, replace_buffer, log_tags, nullptr, _countof(log_tags), 0); plain_text = replace_buffer; } for (int32_t i = 0; i < log_listeners.count; i++) { log_listeners[i](level, plain_text); } platform_debug_output(level, plain_text); if (level == log_error) platform_print_callstack(); } /////////////////////////////////////////// void _log_writef(log_ level, const char* text, va_list args) { va_list copy; va_copy(copy, args); size_t length = vsnprintf(nullptr, 0, text, args); char* buffer = sk_malloc_t(char, length + 2); vsnprintf(buffer, length + 2, text, copy); log_write(level, buffer); sk_free(buffer); va_end(copy); } /////////////////////////////////////////// void log_writef(log_ level, const char *text, ...) { va_list args; va_start(args, text); _log_writef(level, text, args); va_end(args); } /////////////////////////////////////////// void log_diag(const char* text) { log_write(log_diagnostic, text); } void log_info(const char* text) { log_write(log_inform, text); } void log_warn(const char* text) { log_write(log_warning, text); } void log_err (const char* text) { log_write(log_error, text); } void log_diagf(const char* text, ...) { va_list args; va_start(args, text); _log_writef(log_diagnostic, text, args); va_end(args); } void log_infof(const char* text, ...) { va_list args; va_start(args, text); _log_writef(log_inform, text, args); va_end(args); } void log_warnf(const char* text, ...) { va_list args; va_start(args, text); _log_writef(log_warning, text, args); va_end(args); } void log_errf (const char* text, ...) { va_list args; va_start(args, text); _log_writef(log_error, text, args); va_end(args); } /////////////////////////////////////////// void log_set_filter(log_ level) { log_filter = level; } /////////////////////////////////////////// void log_set_colors(log_colors_ colors) { log_colors = colors; } /////////////////////////////////////////// void log_fail_reason(int32_t confidence, log_ log_as, const char *fail_reason) { log_write(log_as, fail_reason); if (confidence <= log_fail_confidence) return; sk_free(log_fail_reason_str); log_fail_confidence = confidence; log_fail_reason_str = string_copy(fail_reason); } /////////////////////////////////////////// void log_fail_reasonf(int32_t confidence, log_ log_as, const char *fail_reason, ...) { va_list args; va_start(args, fail_reason); va_list copy; va_copy(copy, args); size_t length = vsnprintf(nullptr, 0, fail_reason, args); char* buffer = sk_malloc_t(char, length + 2); vsnprintf(buffer, length + 2, fail_reason, copy); log_fail_reason(confidence, log_as, buffer); sk_free(buffer); va_end(copy); va_end(args); } /////////////////////////////////////////// void log_show_any_fail_reason() { if (log_fail_confidence == -1) return; platform_msgbox_err(log_fail_reason_str, "StereoKit Failure"); log_clear_any_fail_reason(); } /////////////////////////////////////////// void log_clear_any_fail_reason() { sk_free(log_fail_reason_str); log_fail_confidence = -1; log_fail_reason_str = nullptr; } /////////////////////////////////////////// void log_subscribe(void (*on_log)(log_, const char*)) { log_listeners.add(on_log); } /////////////////////////////////////////// void log_unsubscribe(void (*on_log)(log_, const char*)) { for (int32_t i = 0; i < log_listeners.count; i++) { if (log_listeners[i] == on_log) { log_listeners.remove(i); break; } } } } // namespace sk