This repository has been archived on 2025-02-04. You can view files and clone it, but cannot push or open issues or pull requests.
helpless/oogabooga/examples/custom_logger.c
Charlie 2b335aee35 Sold my soul to play monlight sonata
Audio sources & decoding are pretty much done and working well.

Playback is not really implemented yet, I'm just hacking in a way to output an audio source.

- Seriously microsoft wtf
2024-07-12 21:11:47 +02:00

194 lines
No EOL
6.3 KiB
C

/*
This is an example showcasing how we can make a custom logger to log both to stdout and
and in-game logger.
We also have log levels to be able to disable/enable the respective levels.
BEWARE!!
This logger is not thread-safe. If multiple threads call log(), then nobody knows
what might happen. If you need to make it thread-safe, check out concurrency.c.
*/
// start all log levels enabled
bool log_level_enabled_flags[LOG_LEVEL_COUNT] = {1, 1, 1, 1};
// We delete old messages when this overflows
#define MAX_LOG_MESSAGES 50
typedef struct Log_Message {
string message;
Log_Level level;
} Log_Message;
Log_Message log_messages[MAX_LOG_MESSAGES];
s64 num_log_messages = 0;
string get_log_level_name(Log_Level level) {
switch (level) {
case LOG_VERBOSE: return STR("Verbose");
case LOG_INFO: return STR("Info");
case LOG_WARNING: return STR("Warning");
case LOG_ERROR: return STR("Error");
default: return STR("");
}
}
void my_logger(Log_Level level, string s) {
string prefix = STR("[INVALID LOG LEVEL]");
if (level >= 0 && level < LOG_LEVEL_COUNT) {
// if log level is disabled, we just leave
if (!log_level_enabled_flags[level]) {
return;
}
prefix = tprint("[%s]", get_log_level_name(level));
}
// Format the final string.
// Since we will be storing it in log_message, we need to use "sprint" rather than
// "tprint" because it tprint uses the temp allocator which gets reset each frame.
// In a real world scenario we would probably have a dedicated allocator for these
// strings rather than heap allocating all of them.
string message = sprint(get_heap_allocator(), "%s %s\n", prefix, s);
// Output the final string to stdout
print(message);
// Also add to in-game log messages
Log_Message msg = (Log_Message){message, level};
if (num_log_messages < MAX_LOG_MESSAGES) {
log_messages[num_log_messages] = msg;
num_log_messages += 1;
} else {
// Shift memory down by one to make space for the next message and deleting the first.
memcpy(log_messages, &log_messages[1], sizeof(log_messages)-sizeof(Log_Message));
log_messages[num_log_messages-1] = msg;
}
}
#define FONT_HEIGHT 38
Gfx_Font *font;
void draw_log(float x, float y);
int entry(int argc, char **argv) {
// Window setup
window.title = STR("Minimal Game Example");
window.scaled_width = 1280;
window.scaled_height = 720;
window.x = 200;
window.y = 200;
window.clear_color = hex_to_rgba(0x6495EDff);
// Load a font to draw the logs with
font = load_font_from_disk(STR("C:/windows/fonts/arial.ttf"), get_heap_allocator());
assert(font, "Failed loading arial.ttf, %d", GetLastError());
// This is where we set the logger we want all logs to go through
context.logger = my_logger;
while (!window.should_close) {
reset_temporary_storage();
os_update();
gfx_update();
// pixel-aligned projection
draw_frame.projection = m4_make_orthographic_projection(window.pixel_width * -0.5, window.pixel_width * 0.5, window.pixel_height * -0.5, window.pixel_height * 0.5, -1, 10);
float x = -window.width/2+60;
float y = window.height/2-FONT_HEIGHT/2-30;
draw_text(font, STR("Left-click to toggle, right-click to send log message"), FONT_HEIGHT, v2(x-1, y+1), v2(1, 1), COLOR_BLACK);
draw_text(font, STR("Left-click to toggle, right-click to send log message"), FONT_HEIGHT, v2(x, y), v2(1, 1), COLOR_WHITE);
y -= FONT_HEIGHT*1.3;
// Loop through all levels to draw their state and act on input
for (Log_Level level = 0; level < LOG_LEVEL_COUNT; level += 1) {
bool enabled = log_level_enabled_flags[level];
string s = tprint("%s: %s", get_log_level_name(level), enabled ? STR("on") : STR("off"));
Gfx_Text_Metrics m = measure_text(font, s, FONT_HEIGHT, v2(1, 1));
Vector4 color = COLOR_WHITE;
Vector2 bottom_left = v2_sub(v2(x, y), m.functional_pos_min);
float L = bottom_left.x;
float R = L + m.visual_size.x;
float B = bottom_left.y;
float T = B + m.visual_size.y;
float mx = input_frame.mouse_x - window.width/2;
float my = input_frame.mouse_y - window.height/2;
bool hovered = mx >= L && mx < R && my >= B && my < T;
if (hovered) color = v4(.8, .8, .8, 1.0);
if (hovered && (is_key_down(MOUSE_BUTTON_LEFT) || is_key_down(MOUSE_BUTTON_RIGHT)))
color = v4(.6, .6, .6, 1.0);
if (hovered && is_key_just_released(MOUSE_BUTTON_LEFT))
log_level_enabled_flags[level] = !log_level_enabled_flags[level];
if (hovered && is_key_just_released(MOUSE_BUTTON_RIGHT)) {
if (level == LOG_VERBOSE) log_verbose("This is a log message");
if (level == LOG_INFO) log_info("This is a log message");
if (level == LOG_WARNING) log_warning("This is a log message");
if (level == LOG_ERROR) log_error("This is a log message");
}
draw_rect(v2_sub(bottom_left, v2(8, 8)), v2_add(m.functional_size, v2(16, 16)), v4_mul(v4(.3, .3, .3, 1), color));
draw_text(font, s, FONT_HEIGHT, v2(x-1, y+1), v2(1, 1), COLOR_BLACK);
draw_text(font, s, FONT_HEIGHT, v2(x, y), v2(1, 1), color);
y -= FONT_HEIGHT*1.3;
}
y -= FONT_HEIGHT*1.3;
draw_log(x, y);
}
return 0;
}
void draw_log(float x, float y) {
draw_text(font, STR("In-game log:"), FONT_HEIGHT, v2(x-1, y+1), v2(1, 1), COLOR_BLACK);
Gfx_Text_Metrics m = draw_text_and_measure(font, STR("In-game log:"), FONT_HEIGHT, v2(x, y), v2(1, 1), COLOR_WHITE);
y -= m.functional_size.y+20;
// Here we draw each entry in the log that we can fit on screen, starting from the top
// so we see the last log message first.
for (s64 i = num_log_messages-1; i >= 0; i--) {
Log_Level level = log_messages[i].level;
if (level >= 0 && level < LOG_LEVEL_COUNT && !log_level_enabled_flags[level]) {
// If it's disabled, skip it
continue;
}
// Set color reflecting log level
Vector4 color = COLOR_WHITE;
if (level == LOG_VERBOSE) color = v4(.6, .6, 1, 1);
else if (level == LOG_INFO) color = v4(.3, 1, .4, 1);
else if (level == LOG_WARNING) color = v4(.8, .8, 1, 1);
else if (level == LOG_ERROR) color = v4(1, .2, .2, 1);
draw_text(font, log_messages[i].message, FONT_HEIGHT, v2(x-1, y+1), v2(1, 1), COLOR_BLACK);
draw_text(font, log_messages[i].message, FONT_HEIGHT, v2(x, y), v2(1, 1), color);
y -= FONT_HEIGHT * 1.3;
if (y+FONT_HEIGHT < -window.height/2) break; // Occlude text outside of view
}
}