274 lines
9.7 KiB
C
274 lines
9.7 KiB
C
#include "app.h"
|
|
#include "arena.h"
|
|
#include "string.h"
|
|
#include "scratch.h"
|
|
#include "sys.h"
|
|
#include "work.h"
|
|
#include "user.h"
|
|
#include "game.h"
|
|
#include "playback.h"
|
|
#include "log.h"
|
|
#include "resource.h"
|
|
#include "asset_cache.h"
|
|
#include "font.h"
|
|
#include "texture.h"
|
|
#include "ttf.h"
|
|
#include "sheet.h"
|
|
#include "mixer.h"
|
|
#include "sound.h"
|
|
#include "util.h"
|
|
#include "settings.h"
|
|
#include "draw.h"
|
|
#include "math.h"
|
|
|
|
struct exit_callback {
|
|
app_exit_callback_func *func;
|
|
struct exit_callback *next;
|
|
struct sys_thread thread;
|
|
};
|
|
|
|
GLOBAL struct {
|
|
struct arena arena;
|
|
struct string write_path;
|
|
struct sync_flag exit_sf;
|
|
|
|
/* Exit callbacks */
|
|
struct sys_mutex exit_callbacks_mutex;
|
|
struct arena exit_callbacks_arena;
|
|
struct exit_callback *exit_callbacks_head;
|
|
} G = { 0 }, DEBUG_ALIAS(G, G_app);
|
|
|
|
/* ========================== *
|
|
* Write directory
|
|
* ========================== */
|
|
|
|
INTERNAL struct string initialize_write_directory(struct arena *arena, struct string write_dir)
|
|
{
|
|
struct temp_arena scratch = scratch_begin(arena);
|
|
|
|
/* Create write path */
|
|
struct string base_write_dir = sys_get_write_path(scratch.arena);
|
|
struct string write_path_fmt = base_write_dir.len > 0 ? STR("%F/%F/") : STR("%F%F/");
|
|
struct string write_path = string_format(
|
|
arena,
|
|
write_path_fmt,
|
|
FMT_STR(base_write_dir),
|
|
FMT_STR(write_dir)
|
|
);
|
|
|
|
/* Create write dir if not present */
|
|
if (!sys_is_dir(write_path)) {
|
|
sys_mkdir(write_path);
|
|
/* TODO: handle failure */
|
|
}
|
|
|
|
scratch_end(scratch);
|
|
|
|
return write_path;
|
|
}
|
|
|
|
struct string app_write_path_cat(struct arena *arena, struct string filename)
|
|
{
|
|
return string_cat(arena, G.write_path, filename);
|
|
}
|
|
|
|
/* ========================== *
|
|
* Default settings
|
|
* ========================== */
|
|
|
|
INTERNAL struct sys_window_settings default_window_settings(struct sys_window *window)
|
|
{
|
|
__prof;
|
|
|
|
struct v2 monitor_size = sys_window_get_monitor_size(window);
|
|
|
|
i32 width = 1280;
|
|
i32 height = math_round_to_int(width / (f32)(DEFAULT_CAMERA_WIDTH / DEFAULT_CAMERA_HEIGHT));
|
|
i32 x = math_round_to_int(monitor_size.x / 2.f - width / 2);
|
|
i32 y = math_round_to_int(monitor_size.y / 2.f - height / 2);
|
|
|
|
return (struct sys_window_settings) {
|
|
.title = WINDOW_TITLE,
|
|
.floating_x = x,
|
|
.floating_y = y,
|
|
.floating_width = width,
|
|
.floating_height = height
|
|
};
|
|
}
|
|
|
|
/* ========================== *
|
|
* Exit callbacks
|
|
* ========================== */
|
|
|
|
INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(exit_callback_thread_entry_point, vcallback)
|
|
{
|
|
struct exit_callback *callback = (struct exit_callback *)vcallback;
|
|
callback->func();
|
|
}
|
|
|
|
void app_register_exit_callback(app_exit_callback_func *func)
|
|
{
|
|
sys_mutex_lock(&G.exit_callbacks_mutex);
|
|
{
|
|
struct exit_callback *callback = arena_push_zero(&G.exit_callbacks_arena, struct exit_callback);
|
|
callback->func = func;
|
|
callback->next = G.exit_callbacks_head;
|
|
G.exit_callbacks_head = callback;
|
|
}
|
|
sys_mutex_unlock(&G.exit_callbacks_mutex);
|
|
}
|
|
|
|
/* ========================== *
|
|
* Entry point
|
|
* ========================== */
|
|
|
|
void app_entry_point(void)
|
|
{
|
|
G.exit_sf = sync_flag_alloc();
|
|
G.exit_callbacks_mutex = sys_mutex_alloc();
|
|
G.exit_callbacks_arena = arena_alloc(GIGABYTE(64));
|
|
|
|
u32 worker_count = 4;
|
|
{
|
|
/* FIXME: Switch this on to utilize all cores. Only decreasing worker count for testing purposes. */
|
|
#if !PROFILING && !RTC
|
|
/* 1. User thread, Input thread
|
|
* 2. Game thread
|
|
* 3. Playback thread
|
|
*/
|
|
u32 num_reserved_cores = 3;
|
|
|
|
i32 min_worker_count = 2;
|
|
i32 max_worker_count = 512;
|
|
i32 target_worker_count = (i32)sys_num_logical_processors() - num_reserved_cores;
|
|
worker_count = (u32)clamp_i32(target_worker_count, min_worker_count, max_worker_count);
|
|
#endif
|
|
}
|
|
|
|
G.arena = arena_alloc(GIGABYTE(64));
|
|
G.write_path = initialize_write_directory(&G.arena, STR(WRITE_DIR));
|
|
|
|
/* Startup logging */
|
|
{
|
|
struct temp_arena scratch = scratch_begin_no_conflict();
|
|
struct string logfile_path = app_write_path_cat(scratch.arena, STR("log.txt"));
|
|
struct log_startup_receipt log_sr = log_startup(logfile_path);
|
|
(UNUSED)log_sr;
|
|
scratch_end(scratch);
|
|
logf_info("Start of logs");
|
|
}
|
|
|
|
/* Create window */
|
|
struct sys_window window = sys_window_alloc();
|
|
|
|
/* Read window settings from file */
|
|
{
|
|
struct temp_arena scratch = scratch_begin_no_conflict();
|
|
|
|
struct sys_window_settings window_settings = { 0 };
|
|
struct string settings_path = app_write_path_cat(scratch.arena, STR(SETTINGS_FILENAME));
|
|
logf_info("Looking for settings file \"%F\"", FMT_STR(settings_path));
|
|
if (sys_is_file(settings_path)) {
|
|
logf_info("Settings file found");
|
|
struct sys_file settings_file = sys_file_open_read(settings_path);
|
|
struct buffer file_data = sys_file_read_all(scratch.arena, settings_file);
|
|
sys_file_close(settings_file);
|
|
logf_info("Deserializing settings file data: %F", FMT_STR(STRING_FROM_BUFFER(file_data)));
|
|
struct string error = { 0 };
|
|
struct sys_window_settings *res = settings_deserialize(scratch.arena, file_data, &error);
|
|
if (error.len > 0) {
|
|
logf_info("Failed to load settings file with error - %F", FMT_STR(error));
|
|
struct string msg = string_format(scratch.arena,
|
|
STR(
|
|
"Failed to loading settings file \"%F\":\n"
|
|
"------------\n"
|
|
"%F\n"
|
|
"------------\n"
|
|
"To stop this error from appearing, either Fix the issue above or delete the file from the system."
|
|
),
|
|
FMT_STR(settings_path),
|
|
FMT_STR(error));
|
|
sys_panic(msg);
|
|
}
|
|
logf_info("Settings file loaded successfully");
|
|
window_settings = *res;
|
|
} else {
|
|
logf_info("Settings file not found, loading default");
|
|
window_settings = default_window_settings(&window);
|
|
}
|
|
string_copy_buff(BUFFER_FROM_ARRAY(window_settings.title), STR(WINDOW_TITLE));
|
|
sys_window_update_settings(&window, &window_settings);
|
|
|
|
scratch_end(scratch);
|
|
}
|
|
|
|
/* Startup systems */
|
|
struct renderer_startup_receipt renderer_sr = renderer_startup(&window);
|
|
struct work_startup_receipt work_sr = work_startup(worker_count);
|
|
struct resource_startup_receipt resource_sr = resource_startup();
|
|
struct asset_cache_startup_receipt asset_cache_sr = asset_cache_startup(&work_sr);
|
|
struct ttf_startup_receipt ttf_sr = ttf_startup();
|
|
struct font_startup_receipt font_sr = font_startup(&work_sr, &renderer_sr, &asset_cache_sr, &ttf_sr, &resource_sr);
|
|
struct texture_startup_receipt texture_sr = texture_startup(&work_sr, &renderer_sr, &asset_cache_sr, &resource_sr);
|
|
struct sheet_startup_receipt sheet_sr = sheet_startup(&work_sr, &asset_cache_sr, &resource_sr);
|
|
struct mixer_startup_receipt mixer_sr = mixer_startup();
|
|
struct sound_startup_receipt sound_sr = sound_startup(&work_sr, &asset_cache_sr, &resource_sr);
|
|
struct draw_startup_receipt draw_sr = draw_startup(&renderer_sr, &font_sr);
|
|
struct game_startup_receipt game_sr = game_startup(&mixer_sr, &sheet_sr, &sound_sr);
|
|
struct user_startup_receipt user_sr = user_startup(&work_sr, &renderer_sr, &font_sr, &texture_sr, &draw_sr, &game_sr, &asset_cache_sr, &mixer_sr, &window);
|
|
struct playback_startup_receipt playback_sr = playback_startup(&mixer_sr);
|
|
|
|
(UNUSED)user_sr;
|
|
(UNUSED)playback_sr;
|
|
|
|
/* Show window */
|
|
sys_window_show(&window);
|
|
|
|
/* Wait for app_exit() */
|
|
sync_flag_wait(&G.exit_sf);
|
|
|
|
/* Call exit callbacks */
|
|
/* FIXME: Only wait on threads for a certain period of time before
|
|
* forcing process exit (to prevent process hanging in the background
|
|
* when a thread gets stuck) */
|
|
sys_mutex_lock(&G.exit_callbacks_mutex);
|
|
{
|
|
/* Start callback threads */
|
|
for (struct exit_callback *callback = G.exit_callbacks_head; callback; callback = callback->next) {
|
|
callback->thread = sys_thread_alloc(&exit_callback_thread_entry_point, callback, STR("[P4] Exit callback thread"));
|
|
}
|
|
/* Wait on callback threads */
|
|
for (struct exit_callback *callback = G.exit_callbacks_head; callback; callback = callback->next) {
|
|
sys_thread_wait_release(&callback->thread);
|
|
}
|
|
}
|
|
sys_mutex_unlock(&G.exit_callbacks_mutex);
|
|
|
|
/* Write window settings to file */
|
|
{
|
|
struct temp_arena scratch = scratch_begin_no_conflict();
|
|
|
|
struct string window_settings_path = app_write_path_cat(scratch.arena, STR(SETTINGS_FILENAME));
|
|
|
|
struct sys_window_settings settings = sys_window_get_settings(&window);
|
|
struct buffer buff = settings_serialize(scratch.arena, &settings);
|
|
logf_info("Serialized window settings: %F", FMT_STR(STRING_FROM_BUFFER(buff)));
|
|
|
|
logf_info("Writing settings file to path \"%F\"", FMT_STR(window_settings_path));
|
|
struct sys_file settings_file = sys_file_open_write(window_settings_path);
|
|
sys_file_write(settings_file, buff);
|
|
sys_file_close(settings_file);
|
|
logf_info("Finished writing settings file");
|
|
|
|
scratch_end(scratch);
|
|
}
|
|
|
|
|
|
logf_info("Program exited normally");
|
|
}
|
|
|
|
void app_exit(void)
|
|
{
|
|
sync_flag_set(&G.exit_sf);
|
|
}
|