#include "settings.h" #include "sys.h" #include "json.h" #include "scratch.h" #include "string.h" #include "app.h" #include "math.h" /* TODO: * Rework the whole settings system to be not so rigid. * - Multi-threaded * - Current settings state is readable & modifiable at any time * - Can be done w/ a settings_open / settings_close block * - Allow for dot notation in settings queries (should probably be implemented in the json layer). * - "." denotes subindexing, IE: settings_get("window.width") (numbers can also be implemented for arrays). * - "\." escapable */ #define SETTINGS_FILENAME "settings.json" INTERNAL struct buffer serialize_window_settings(struct arena *arena, const struct sys_window_settings *settings) { __prof; struct temp_arena scratch = scratch_begin(arena); struct json_ir *root_obj = json_ir_object(scratch.arena); struct json_ir *window_settings_obj = json_ir_object_set(root_obj, STR("window"), json_ir_object(scratch.arena)); json_ir_object_set(window_settings_obj, STR("minimized"), json_ir_bool(scratch.arena, settings->flags & SYS_WINDOW_SETTINGS_FLAG_MINIMIZED)); json_ir_object_set(window_settings_obj, STR("maximized"), json_ir_bool(scratch.arena, settings->flags & SYS_WINDOW_SETTINGS_FLAG_MAXIMIZED)); json_ir_object_set(window_settings_obj, STR("fullscreen"), json_ir_bool(scratch.arena, settings->flags & SYS_WINDOW_SETTINGS_FLAG_FULLSCREEN)); json_ir_object_set(window_settings_obj, STR("x"), json_ir_number(scratch.arena, settings->floating_x)); json_ir_object_set(window_settings_obj, STR("y"), json_ir_number(scratch.arena, settings->floating_y)); json_ir_object_set(window_settings_obj, STR("width"), json_ir_number(scratch.arena, settings->floating_width)); json_ir_object_set(window_settings_obj, STR("height"), json_ir_number(scratch.arena, settings->floating_height)); const struct json_val *formatted = json_format(scratch.arena, root_obj); struct buffer buff = BUFFER_FROM_STRING(json_dump_to_string(arena, formatted, 2)); scratch_end(scratch); return buff; } INTERNAL void deserialize_window_settings(struct buffer json_bytes, struct sys_window_settings *settings) { __prof; struct temp_arena scratch = scratch_begin_no_conflict(); struct string error = { 0 }; if (json_bytes.size <= 0) { goto end; } struct string *parse_error = NULL; const struct json_val *root_json = json_parse_and_format(scratch.arena, json_bytes, &parse_error); if (parse_error) { error = *parse_error; goto end; } const struct json_val *window_settings_json = json_object_get(root_json, STR("window")); if (!window_settings_json || !json_is_object(window_settings_json)) { goto end; } const struct json_val *minimized = json_object_get(window_settings_json, STR("minimized")); const struct json_val *maximized = json_object_get(window_settings_json, STR("maximized")); const struct json_val *fullscreen = json_object_get(window_settings_json, STR("fullscreen")); const struct json_val *width = json_object_get(window_settings_json, STR("width")); const struct json_val *height = json_object_get(window_settings_json, STR("height")); const struct json_val *x = json_object_get(window_settings_json, STR("x")); const struct json_val *y = json_object_get(window_settings_json, STR("y")); if (!(json_is_bool(minimized) && json_bool(minimized))) { /* Only trust pos/size settings if window wasn't closed while minimized */ settings->floating_x = json_is_number(x) ? (i32)clamp_f64(json_number(x), I32_MIN, I32_MAX) : settings->floating_x; settings->floating_y = json_is_number(y) ? (i32)clamp_f64(json_number(y), I32_MIN, I32_MAX) : settings->floating_y; settings->floating_width = json_is_number(width) ? (i32)clamp_f64(json_number(width), I32_MIN, I32_MAX) : settings->floating_width; settings->floating_height = json_is_number(height) ? (i32)clamp_f64(json_number(height), I32_MIN, I32_MAX) : settings->floating_height; } settings->flags = json_is_bool(maximized) ? (json_bool(maximized) ? (settings->flags | SYS_WINDOW_SETTINGS_FLAG_MAXIMIZED) : (settings->flags & ~SYS_WINDOW_SETTINGS_FLAG_MAXIMIZED)) : settings->flags; settings->flags = json_is_bool(fullscreen) ? (json_bool(fullscreen) ? (settings->flags | SYS_WINDOW_SETTINGS_FLAG_FULLSCREEN) : (settings->flags & ~SYS_WINDOW_SETTINGS_FLAG_FULLSCREEN)) : settings->flags; end: if (error.len > 0) { sys_message_box( SYS_MESSAGE_BOX_KIND_WARNING, string_format( scratch.arena, STR("Error loading settings file:\n%F"), FMT_STR(error) ) ); } scratch_end(scratch); } struct sys_window_settings settings_default_window_settings(struct sys_window *window) { __prof; struct v2 window_size = sys_window_get_size(window); struct v2 monitor_size = sys_window_get_monitor_size(window); i32 window_width = math_round(window_size.x); i32 window_height = math_round(window_size.y); i32 monitor_width = math_round(monitor_size.x); i32 monitor_height = math_round(monitor_size.y); i32 width = window_width / 2; i32 height = window_height / 2; i32 x = monitor_width / 2 - width / 2; i32 y = monitor_height / 2 - height / 2; return (struct sys_window_settings) { #if RTC # if DEVELOPER .title = "Debug (Developer Build)", # else .title = "Debug", # endif #else # if DEVELOPER .title = "Power Play (Developer Build)", # else .title = "Power Play", # endif #endif .floating_x = x, .floating_y = y, .floating_width = width, .floating_height = height }; } void settings_read_from_file(struct sys_window_settings *default_settings) { __prof; struct temp_arena scratch = scratch_begin_no_conflict(); /* Read window settings file if it exists */ struct string window_settings_path = app_write_path_cat(scratch.arena, STR(SETTINGS_FILENAME)); if (sys_is_file(window_settings_path)) { struct sys_file settings_file = sys_file_open_read(window_settings_path); struct buffer file_data = sys_file_read_all(scratch.arena, settings_file); sys_file_close(settings_file); deserialize_window_settings(file_data, default_settings); } scratch_end(scratch); } void settings_write_to_file(const struct sys_window_settings *settings) { __prof; struct temp_arena scratch = scratch_begin_no_conflict(); /* Write window settings to file */ struct buffer settings_file_data = serialize_window_settings(scratch.arena, settings); if (settings_file_data.size > 0) { struct string window_settings_path = app_write_path_cat(scratch.arena, STR(SETTINGS_FILENAME)); struct sys_file settings_file = sys_file_open_write(window_settings_path); sys_file_write(settings_file, settings_file_data); sys_file_close(settings_file); } scratch_end(scratch); }