Add 'code/' from commit '1459ef7cbce753b056ea148b29c9dc3f8a721261'
git-subtree-dir: code git-subtree-mainline:cf6a50363e
git-subtree-split:1459ef7cbc
This commit is contained in:
commit
0e47ccd2ce
|
@ -0,0 +1,4 @@
|
||||||
|
vc120.pdb
|
||||||
|
4ed_data.ctm
|
||||||
|
.DS_Store
|
||||||
|
*.dSYM
|
|
@ -0,0 +1,881 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 12.12.2014
|
||||||
|
*
|
||||||
|
* Application layer for project codename "4ed"
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
internal void
|
||||||
|
init_command_line_settings(App_Settings *settings, Plat_Settings *plat_settings, i32 argc, char **argv){
|
||||||
|
char *arg = 0;
|
||||||
|
Command_Line_Mode mode = CLMode_App;
|
||||||
|
Command_Line_Action action = CLAct_Nothing;
|
||||||
|
b32 strict = false;
|
||||||
|
|
||||||
|
settings->init_files_max = ArrayCount(settings->init_files);
|
||||||
|
for (i32 i = 1; i <= argc; ++i){
|
||||||
|
if (i == argc){
|
||||||
|
arg = "";
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
arg = argv[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg[0] == '-' && arg[1] == '-'){
|
||||||
|
char *long_arg_name = arg+2;
|
||||||
|
if (string_match(SCu8(long_arg_name), string_u8_litexpr("custom"))){
|
||||||
|
mode = CLMode_Custom;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (mode){
|
||||||
|
case CLMode_App:
|
||||||
|
{
|
||||||
|
switch (action){
|
||||||
|
case CLAct_Nothing:
|
||||||
|
{
|
||||||
|
if (arg[0] == '-'){
|
||||||
|
action = CLAct_Ignore;
|
||||||
|
switch (arg[1]){
|
||||||
|
case 'd': action = CLAct_CustomDLL; strict = false; break;
|
||||||
|
case 'D': action = CLAct_CustomDLL; strict = true; break;
|
||||||
|
|
||||||
|
case 'w': action = CLAct_WindowSize; break;
|
||||||
|
case 'W': action = CLAct_WindowMaximize; break;
|
||||||
|
case 'p': action = CLAct_WindowPosition; break;
|
||||||
|
case 'F': action = CLAct_WindowFullscreen; break;
|
||||||
|
|
||||||
|
case 'f': action = CLAct_FontSize; break;
|
||||||
|
case 'h': action = CLAct_FontUseHinting; --i; break;
|
||||||
|
case 'U': action = CLAct_UserDirectory; break;
|
||||||
|
|
||||||
|
case 'L': action = CLAct_Nothing; break;
|
||||||
|
//case 'L': enables log, parsed before this is called (because I'm a dumbass)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (arg[0] != 0){
|
||||||
|
if (settings->init_files_count < settings->init_files_max){
|
||||||
|
i32 index = settings->init_files_count++;
|
||||||
|
settings->init_files[index] = arg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case CLAct_CustomDLL:
|
||||||
|
{
|
||||||
|
plat_settings->custom_dll_is_strict = (b8)strict;
|
||||||
|
if (i < argc){
|
||||||
|
plat_settings->custom_dll = argv[i];
|
||||||
|
}
|
||||||
|
action = CLAct_Nothing;
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case CLAct_WindowSize:
|
||||||
|
{
|
||||||
|
if (i + 1 < argc){
|
||||||
|
plat_settings->set_window_size = true;
|
||||||
|
|
||||||
|
i32 w = (i32)string_to_integer(SCu8(argv[i]), 10);
|
||||||
|
i32 h = (i32)string_to_integer(SCu8(argv[i + 1]), 10);
|
||||||
|
if (w > 0){
|
||||||
|
plat_settings->window_w = w;
|
||||||
|
}
|
||||||
|
if (h > 0){
|
||||||
|
plat_settings->window_h = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
action = CLAct_Nothing;
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case CLAct_WindowMaximize:
|
||||||
|
{
|
||||||
|
--i;
|
||||||
|
plat_settings->maximize_window = true;
|
||||||
|
action = CLAct_Nothing;
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case CLAct_WindowPosition:
|
||||||
|
{
|
||||||
|
if (i + 1 < argc){
|
||||||
|
plat_settings->set_window_pos = true;
|
||||||
|
|
||||||
|
i32 x = (i32)string_to_integer(SCu8(argv[i]), 10);
|
||||||
|
i32 y = (i32)string_to_integer(SCu8(argv[i + 1]), 10);
|
||||||
|
if (x > 0){
|
||||||
|
plat_settings->window_x = x;
|
||||||
|
}
|
||||||
|
if (y > 0){
|
||||||
|
plat_settings->window_y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
action = CLAct_Nothing;
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case CLAct_WindowFullscreen:
|
||||||
|
{
|
||||||
|
--i;
|
||||||
|
plat_settings->fullscreen_window = true;
|
||||||
|
action = CLAct_Nothing;
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case CLAct_FontSize:
|
||||||
|
{
|
||||||
|
if (i < argc){
|
||||||
|
settings->font_size = (i32)string_to_integer(SCu8(argv[i]), 10);
|
||||||
|
}
|
||||||
|
action = CLAct_Nothing;
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case CLAct_FontUseHinting:
|
||||||
|
{
|
||||||
|
plat_settings->use_hinting = true;
|
||||||
|
settings->use_hinting = plat_settings->use_hinting;
|
||||||
|
action = CLAct_Nothing;
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case CLAct_UserDirectory:
|
||||||
|
{
|
||||||
|
if (i < argc){
|
||||||
|
plat_settings->user_directory = argv[i];
|
||||||
|
}
|
||||||
|
action = CLAct_Nothing;
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case CLMode_Custom:
|
||||||
|
{
|
||||||
|
settings->custom_flags = argv + i;
|
||||||
|
settings->custom_flags_count = argc - i;
|
||||||
|
i = argc;
|
||||||
|
mode = CLMode_App;
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
internal Models*
|
||||||
|
models_init(void){
|
||||||
|
Arena arena = make_arena_system();
|
||||||
|
Models *models = push_array_zero(&arena, Models, 1);
|
||||||
|
models->arena_ = arena;
|
||||||
|
models->arena = &models->arena_;
|
||||||
|
heap_init(&models->heap, get_base_allocator_system());
|
||||||
|
return(models);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
app_load_vtables(API_VTable_system *vtable_system, API_VTable_font *vtable_font, API_VTable_graphics *vtable_graphics){
|
||||||
|
system_api_read_vtable(vtable_system);
|
||||||
|
font_api_read_vtable(vtable_font);
|
||||||
|
graphics_api_read_vtable(vtable_graphics);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Log_Function*
|
||||||
|
app_get_logger(void){
|
||||||
|
log_init();
|
||||||
|
return(log_string);
|
||||||
|
}
|
||||||
|
|
||||||
|
App_Read_Command_Line_Sig(app_read_command_line){
|
||||||
|
Models *models = models_init();
|
||||||
|
App_Settings *settings = &models->settings;
|
||||||
|
block_zero_struct(settings);
|
||||||
|
if (argc > 1){
|
||||||
|
init_command_line_settings(&models->settings, plat_settings, argc, argv);
|
||||||
|
}
|
||||||
|
*files = models->settings.init_files;
|
||||||
|
*file_count = &models->settings.init_files_count;
|
||||||
|
return(models);
|
||||||
|
}
|
||||||
|
|
||||||
|
App_Init_Sig(app_init){
|
||||||
|
Models *models = (Models*)base_ptr;
|
||||||
|
models->keep_playing = true;
|
||||||
|
models->hard_exit = false;
|
||||||
|
|
||||||
|
models->config_api = api;
|
||||||
|
models->virtual_event_arena = make_arena_system();
|
||||||
|
|
||||||
|
profile_init(&models->profile_list);
|
||||||
|
|
||||||
|
managed_ids_init(tctx->allocator, &models->managed_id_set);
|
||||||
|
|
||||||
|
API_VTable_custom custom_vtable = {};
|
||||||
|
custom_api_fill_vtable(&custom_vtable);
|
||||||
|
API_VTable_system system_vtable = {};
|
||||||
|
system_api_fill_vtable(&system_vtable);
|
||||||
|
Custom_Layer_Init_Type *custom_init = api.init_apis(&custom_vtable, &system_vtable);
|
||||||
|
Assert(custom_init != 0);
|
||||||
|
|
||||||
|
// NOTE(allen): coroutines
|
||||||
|
coroutine_system_init(&models->coroutines);
|
||||||
|
|
||||||
|
// NOTE(allen): font set
|
||||||
|
font_set_init(&models->font_set);
|
||||||
|
|
||||||
|
// NOTE(allen): live set
|
||||||
|
Arena *arena = models->arena;
|
||||||
|
{
|
||||||
|
models->view_set.count = 0;
|
||||||
|
models->view_set.max = MAX_VIEWS;
|
||||||
|
models->view_set.views = push_array(arena, View, models->view_set.max);
|
||||||
|
|
||||||
|
//dll_init_sentinel
|
||||||
|
models->view_set.free_sentinel.next = &models->view_set.free_sentinel;
|
||||||
|
models->view_set.free_sentinel.prev = &models->view_set.free_sentinel;
|
||||||
|
|
||||||
|
i32 max = models->view_set.max;
|
||||||
|
View *view = models->view_set.views;
|
||||||
|
for (i32 i = 0; i < max; ++i, ++view){
|
||||||
|
//dll_insert(&models->view_set.free_sentinel, view);
|
||||||
|
view->next = models->view_set.free_sentinel.next;
|
||||||
|
view->prev = &models->view_set.free_sentinel;
|
||||||
|
models->view_set.free_sentinel.next = view;
|
||||||
|
view->next->prev = view;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lifetime_allocator_init(tctx->allocator, &models->lifetime_allocator);
|
||||||
|
dynamic_workspace_init(&models->lifetime_allocator, DynamicWorkspace_Global, 0, &models->dynamic_workspace);
|
||||||
|
|
||||||
|
// NOTE(allen): file setup
|
||||||
|
working_set_init(models, &models->working_set);
|
||||||
|
Mutex_Lock file_order_lock(models->working_set.mutex);
|
||||||
|
|
||||||
|
// NOTE(allen):
|
||||||
|
global_history_init(&models->global_history);
|
||||||
|
text_layout_init(tctx, &models->text_layouts);
|
||||||
|
|
||||||
|
// NOTE(allen): style setup
|
||||||
|
{
|
||||||
|
Scratch_Block scratch(tctx, arena);
|
||||||
|
|
||||||
|
String8 binary_path = system_get_path(scratch, SystemPath_Binary);
|
||||||
|
String8 full_path = push_u8_stringf(arena, "%.*sfonts/liberation-mono.ttf", string_expand(binary_path));
|
||||||
|
|
||||||
|
Face_Description description = {};
|
||||||
|
description.font.file_name = full_path;
|
||||||
|
description.parameters.pt_size = 12;
|
||||||
|
Face *new_face = font_set_new_face(&models->font_set, &description);
|
||||||
|
if (new_face == 0){
|
||||||
|
system_error_box("Could not load the required fallback font");
|
||||||
|
}
|
||||||
|
models->global_face_id = new_face->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(allen): title space
|
||||||
|
models->has_new_title = true;
|
||||||
|
models->title_capacity = KB(4);
|
||||||
|
models->title_space = push_array(arena, char, models->title_capacity);
|
||||||
|
block_copy(models->title_space, WINDOW_NAME, sizeof(WINDOW_NAME));
|
||||||
|
|
||||||
|
// NOTE(allen): miscellaneous init
|
||||||
|
hot_directory_init(arena, &models->hot_directory, current_directory);
|
||||||
|
child_process_container_init(tctx->allocator, &models->child_processes);
|
||||||
|
models->period_wakeup_timer = system_wake_up_timer_create();
|
||||||
|
|
||||||
|
// NOTE(allen): custom layer init
|
||||||
|
Application_Links app = {};
|
||||||
|
app.tctx = tctx;
|
||||||
|
app.cmd_context = models;
|
||||||
|
custom_init(&app);
|
||||||
|
|
||||||
|
// NOTE(allen): init baked in buffers
|
||||||
|
File_Init init_files[] = {
|
||||||
|
{ str8_lit("*messages*"), &models->message_buffer , true , },
|
||||||
|
{ str8_lit("*scratch*") , &models->scratch_buffer , false, },
|
||||||
|
{ str8_lit("*log*") , &models->log_buffer , true , },
|
||||||
|
{ str8_lit("*keyboard*"), &models->keyboard_buffer, true , },
|
||||||
|
};
|
||||||
|
|
||||||
|
Buffer_Hook_Function *begin_buffer_func = models->begin_buffer;
|
||||||
|
models->begin_buffer = 0;
|
||||||
|
|
||||||
|
Heap *heap = &models->heap;
|
||||||
|
for (i32 i = 0; i < ArrayCount(init_files); ++i){
|
||||||
|
Editing_File *file = working_set_allocate_file(&models->working_set, &models->lifetime_allocator);
|
||||||
|
buffer_bind_name(tctx, models, arena, &models->working_set, file, init_files[i].name);
|
||||||
|
|
||||||
|
if (init_files[i].ptr != 0){
|
||||||
|
*init_files[i].ptr = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
File_Attributes attributes = {};
|
||||||
|
file_create_from_string(tctx, models, file, SCu8(), attributes);
|
||||||
|
if (init_files[i].read_only){
|
||||||
|
file->settings.read_only = true;
|
||||||
|
history_free(tctx, &file->state.history);
|
||||||
|
}
|
||||||
|
|
||||||
|
file->settings.never_kill = true;
|
||||||
|
file_set_unimportant(file, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
models->begin_buffer = begin_buffer_func;
|
||||||
|
|
||||||
|
// NOTE(allen): setup first panel
|
||||||
|
{
|
||||||
|
Panel *panel = layout_initialize(arena, &models->layout);
|
||||||
|
View *new_view = live_set_alloc_view(&models->lifetime_allocator, &models->view_set, panel);
|
||||||
|
view_init(tctx, models, new_view, models->scratch_buffer, models->view_event_handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
App_Step_Sig(app_step){
|
||||||
|
Models *models = (Models*)base_ptr;
|
||||||
|
|
||||||
|
Mutex_Lock file_order_lock(models->working_set.mutex);
|
||||||
|
Scratch_Block scratch(tctx);
|
||||||
|
|
||||||
|
models->next_animate_delay = max_u32;
|
||||||
|
models->animate_next_frame = false;
|
||||||
|
|
||||||
|
// NOTE(allen): per-frame update of models state
|
||||||
|
begin_frame(target, &models->font_set);
|
||||||
|
models->target = target;
|
||||||
|
models->input = input;
|
||||||
|
|
||||||
|
// NOTE(allen): OS clipboard event handling
|
||||||
|
if (input->clipboard.str != 0){
|
||||||
|
co_send_core_event(tctx, models, CoreCode_NewClipboardContents, input->clipboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(allen): reorganizing panels on screen
|
||||||
|
Vec2_i32 prev_dim = layout_get_root_size(&models->layout);
|
||||||
|
Vec2_i32 current_dim = V2i32(target->width, target->height);
|
||||||
|
layout_set_root_size(&models->layout, current_dim);
|
||||||
|
|
||||||
|
// NOTE(allen): update child processes
|
||||||
|
f32 dt = input->dt;
|
||||||
|
if (dt > 0){
|
||||||
|
Temp_Memory_Block temp(scratch);
|
||||||
|
|
||||||
|
Child_Process_Container *child_processes = &models->child_processes;
|
||||||
|
Child_Process **processes_to_free = push_array(scratch, Child_Process*, child_processes->active_child_process_count);
|
||||||
|
i32 processes_to_free_count = 0;
|
||||||
|
|
||||||
|
u32 max = KB(128);
|
||||||
|
char *dest = push_array(scratch, char, max);
|
||||||
|
|
||||||
|
for (Node *node = child_processes->child_process_active_list.next;
|
||||||
|
node != &child_processes->child_process_active_list;
|
||||||
|
node = node->next){
|
||||||
|
Child_Process *child_process = CastFromMember(Child_Process, node, node);
|
||||||
|
|
||||||
|
Editing_File *file = child_process->out_file;
|
||||||
|
CLI_Handles *cli = &child_process->cli;
|
||||||
|
|
||||||
|
// TODO(allen): do(call a 'child process updated hook' let that hook populate the buffer if it so chooses)
|
||||||
|
|
||||||
|
b32 edited_file = false;
|
||||||
|
u32 amount = 0;
|
||||||
|
system_cli_begin_update(cli);
|
||||||
|
if (system_cli_update_step(cli, dest, max, &amount)){
|
||||||
|
if (file != 0 && amount > 0){
|
||||||
|
output_file_append(tctx, models, file, SCu8(dest, amount));
|
||||||
|
edited_file = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (system_cli_end_update(cli)){
|
||||||
|
if (file != 0){
|
||||||
|
String_Const_u8 str = push_u8_stringf(scratch, "exited with code %d", cli->exit);
|
||||||
|
output_file_append(tctx, models, file, str);
|
||||||
|
edited_file = true;
|
||||||
|
}
|
||||||
|
processes_to_free[processes_to_free_count++] = child_process;
|
||||||
|
child_process_set_return_code(models, child_processes, child_process->id, cli->exit);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (child_process->cursor_at_end && file != 0){
|
||||||
|
file_cursor_to_end(tctx, models, file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i32 i = 0; i < processes_to_free_count; ++i){
|
||||||
|
child_process_free(child_processes, processes_to_free[i]->id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(allen): simulated events
|
||||||
|
Input_List input_list = input->events;
|
||||||
|
Input_Modifier_Set modifiers = system_get_keyboard_modifiers(scratch);
|
||||||
|
if (input->mouse.press_l){
|
||||||
|
Input_Event event = {};
|
||||||
|
event.kind = InputEventKind_MouseButton;
|
||||||
|
event.mouse.code = MouseCode_Left;
|
||||||
|
event.mouse.p = input->mouse.p;
|
||||||
|
event.mouse.modifiers = copy_modifier_set(scratch, &modifiers);
|
||||||
|
push_input_event(scratch, &input_list, &event);
|
||||||
|
}
|
||||||
|
else if (input->mouse.release_l){
|
||||||
|
Input_Event event = {};
|
||||||
|
event.kind = InputEventKind_MouseButtonRelease;
|
||||||
|
event.mouse.code = MouseCode_Left;
|
||||||
|
event.mouse.p = input->mouse.p;
|
||||||
|
event.mouse.modifiers = copy_modifier_set(scratch, &modifiers);
|
||||||
|
push_input_event(scratch, &input_list, &event);
|
||||||
|
}
|
||||||
|
if (input->mouse.press_r){
|
||||||
|
Input_Event event = {};
|
||||||
|
event.kind = InputEventKind_MouseButton;
|
||||||
|
event.mouse.code = MouseCode_Right;
|
||||||
|
event.mouse.p = input->mouse.p;
|
||||||
|
event.mouse.modifiers = copy_modifier_set(scratch, &modifiers);
|
||||||
|
push_input_event(scratch, &input_list, &event);
|
||||||
|
}
|
||||||
|
else if (input->mouse.release_r){
|
||||||
|
Input_Event event = {};
|
||||||
|
event.kind = InputEventKind_MouseButtonRelease;
|
||||||
|
event.mouse.code = MouseCode_Right;
|
||||||
|
event.mouse.p = input->mouse.p;
|
||||||
|
event.mouse.modifiers = copy_modifier_set(scratch, &modifiers);
|
||||||
|
push_input_event(scratch, &input_list, &event);
|
||||||
|
}
|
||||||
|
if (input->mouse.wheel != 0){
|
||||||
|
Input_Event event = {};
|
||||||
|
event.kind = InputEventKind_MouseWheel;
|
||||||
|
event.mouse_wheel.value = (f32)(input->mouse.wheel);
|
||||||
|
event.mouse_wheel.p = input->mouse.p;
|
||||||
|
event.mouse_wheel.modifiers = copy_modifier_set(scratch, &modifiers);
|
||||||
|
push_input_event(scratch, &input_list, &event);
|
||||||
|
}
|
||||||
|
if (input->mouse.p != models->prev_p){
|
||||||
|
b32 was_in_window = rect_contains_point(Ri32(0, 0, prev_dim.x, prev_dim.y), models->prev_p);
|
||||||
|
b32 is_in_window = rect_contains_point(Ri32(0, 0, current_dim.x, current_dim.y), input->mouse.p);
|
||||||
|
if (is_in_window || was_in_window){
|
||||||
|
Input_Event event = {};
|
||||||
|
event.kind = InputEventKind_MouseMove;
|
||||||
|
event.mouse_move.p = input->mouse.p;
|
||||||
|
event.mouse_move.modifiers = copy_modifier_set(scratch, &modifiers);
|
||||||
|
push_input_event(scratch, &input_list, &event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (models->animated_last_frame){
|
||||||
|
Input_Event event = {};
|
||||||
|
event.kind = InputEventKind_Core;
|
||||||
|
event.core.code = CoreCode_Animate;
|
||||||
|
push_input_event(scratch, &input_list, &event);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(allen): expose layout
|
||||||
|
Layout *layout = &models->layout;
|
||||||
|
|
||||||
|
// NOTE(allen): mouse hover status
|
||||||
|
Panel *mouse_panel = 0;
|
||||||
|
Panel *divider_panel = 0;
|
||||||
|
b32 mouse_in_margin = false;
|
||||||
|
Vec2_i32 mouse = input->mouse.p;
|
||||||
|
{
|
||||||
|
for (Panel *panel = layout_get_first_open_panel(layout);
|
||||||
|
panel != 0;
|
||||||
|
panel = layout_get_next_open_panel(layout, panel)){
|
||||||
|
if (rect_contains_point(panel->rect_full, mouse)){
|
||||||
|
mouse_panel = panel;
|
||||||
|
if (!rect_contains_point(panel->rect_inner, mouse)){
|
||||||
|
mouse_in_margin = true;
|
||||||
|
for (divider_panel = mouse_panel->parent;
|
||||||
|
divider_panel != 0;
|
||||||
|
divider_panel = divider_panel->parent){
|
||||||
|
if (rect_contains_point(divider_panel->rect_inner, mouse)){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mouse_panel != 0){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(allen): First frame initialization
|
||||||
|
if (input->first_step){
|
||||||
|
Temp_Memory_Block temp(scratch);
|
||||||
|
|
||||||
|
String_Const_u8_Array file_names = {};
|
||||||
|
file_names.count = models->settings.init_files_count;
|
||||||
|
file_names.vals = push_array(scratch, String_Const_u8, file_names.count);
|
||||||
|
for (i32 i = 0; i < file_names.count; i += 1){
|
||||||
|
file_names.vals[i] = SCu8(models->settings.init_files[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
String_Const_u8_Array flags = {};
|
||||||
|
flags.count = models->settings.custom_flags_count;
|
||||||
|
flags.vals = push_array(scratch, String_Const_u8, flags.count);
|
||||||
|
for (i32 i = 0; i < flags.count; i += 1){
|
||||||
|
flags.vals[i] = SCu8(models->settings.custom_flags[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Input_Event event = {};
|
||||||
|
event.kind = InputEventKind_Core;
|
||||||
|
event.core.code = CoreCode_Startup;
|
||||||
|
event.core.flag_strings = flags;
|
||||||
|
event.core.file_names = file_names;
|
||||||
|
co_send_event(tctx, models, &event);
|
||||||
|
|
||||||
|
// NOTE(allen): Actually do the buffer settings for the built ins now.
|
||||||
|
Buffer_Hook_Function *begin_buffer_func = models->begin_buffer;
|
||||||
|
if (begin_buffer_func != 0){
|
||||||
|
Application_Links app = {};
|
||||||
|
app.tctx = tctx;
|
||||||
|
app.cmd_context = models;
|
||||||
|
begin_buffer_func(&app, models->message_buffer->id);
|
||||||
|
begin_buffer_func(&app, models->scratch_buffer->id);
|
||||||
|
begin_buffer_func(&app, models->log_buffer->id);
|
||||||
|
begin_buffer_func(&app, models->keyboard_buffer->id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(allen): consume event stream
|
||||||
|
Input_Event_Node *input_node = input_list.first;
|
||||||
|
Input_Event_Node *input_node_next = 0;
|
||||||
|
for (;; input_node = input_node_next){
|
||||||
|
// NOTE(allen): first handle any events coming from the view command
|
||||||
|
// function queue
|
||||||
|
Model_View_Command_Function cmd_func = models_pop_view_command_function(models);
|
||||||
|
if (cmd_func.custom_func != 0){
|
||||||
|
View *view = imp_get_view(models, cmd_func.view_id);
|
||||||
|
if (view != 0){
|
||||||
|
input_node_next = input_node;
|
||||||
|
Input_Event cmd_func_event = {};
|
||||||
|
cmd_func_event.kind = InputEventKind_CustomFunction;
|
||||||
|
cmd_func_event.custom_func = cmd_func.custom_func;
|
||||||
|
co_send_event(tctx, models, view, &cmd_func_event);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Temp_Memory_Block temp(scratch);
|
||||||
|
Input_Event *simulated_input = 0;
|
||||||
|
Input_Event virtual_event = models_pop_virtual_event(scratch, models);
|
||||||
|
if (virtual_event.kind != InputEventKind_None){
|
||||||
|
virtual_event.virtual_event = true;
|
||||||
|
simulated_input = &virtual_event;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
if (input_node == 0){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
input_node_next = input_node->next;
|
||||||
|
simulated_input = &input_node->event;
|
||||||
|
|
||||||
|
if (simulated_input->kind == InputEventKind_TextInsert && simulated_input->text.blocked){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(allen): record to keyboard history
|
||||||
|
if (simulated_input->kind == InputEventKind_KeyStroke ||
|
||||||
|
simulated_input->kind == InputEventKind_KeyRelease ||
|
||||||
|
simulated_input->kind == InputEventKind_TextInsert){
|
||||||
|
Temp_Memory_Block temp_key_line(scratch);
|
||||||
|
String_Const_u8 key_line = stringize_keyboard_event(scratch, simulated_input);
|
||||||
|
output_file_append(tctx, models, models->keyboard_buffer, key_line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b32 event_was_handled = false;
|
||||||
|
Input_Event *event = simulated_input;
|
||||||
|
|
||||||
|
Panel *active_panel = layout_get_active_panel(layout);
|
||||||
|
View *view = active_panel->view;
|
||||||
|
Assert(view != 0);
|
||||||
|
|
||||||
|
switch (models->state){
|
||||||
|
case APP_STATE_EDIT:
|
||||||
|
{
|
||||||
|
typedef i32 Event_Consume_Rule;
|
||||||
|
enum{
|
||||||
|
EventConsume_None,
|
||||||
|
EventConsume_BeginResize,
|
||||||
|
EventConsume_ClickChangeView,
|
||||||
|
EventConsume_CustomCommand,
|
||||||
|
};
|
||||||
|
|
||||||
|
Event_Consume_Rule consume_rule = EventConsume_CustomCommand;
|
||||||
|
if (match_mouse_code(event, MouseCode_Left) && (divider_panel != 0)){
|
||||||
|
consume_rule = EventConsume_BeginResize;
|
||||||
|
}
|
||||||
|
else if (match_mouse_code(event, MouseCode_Left) &&
|
||||||
|
mouse_panel != 0 && mouse_panel != active_panel){
|
||||||
|
consume_rule = EventConsume_ClickChangeView;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (consume_rule){
|
||||||
|
case EventConsume_BeginResize:
|
||||||
|
{
|
||||||
|
models->state = APP_STATE_RESIZING;
|
||||||
|
models->resizing_intermediate_panel = divider_panel;
|
||||||
|
event_was_handled = true;
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case EventConsume_ClickChangeView:
|
||||||
|
{
|
||||||
|
// NOTE(allen): run deactivate command
|
||||||
|
co_send_core_event(tctx, models, view, CoreCode_ClickDeactivateView);
|
||||||
|
|
||||||
|
layout->active_panel = mouse_panel;
|
||||||
|
models->animate_next_frame = true;
|
||||||
|
active_panel = mouse_panel;
|
||||||
|
view = active_panel->view;
|
||||||
|
|
||||||
|
// NOTE(allen): run activate command
|
||||||
|
co_send_core_event(tctx, models, view, CoreCode_ClickActivateView);
|
||||||
|
|
||||||
|
event_was_handled = true;
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case EventConsume_CustomCommand:
|
||||||
|
{
|
||||||
|
event_was_handled = co_send_event(tctx, models, view, event);
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case APP_STATE_RESIZING:
|
||||||
|
{
|
||||||
|
Event_Property event_flags = get_event_properties(event);
|
||||||
|
if (HasFlag(event_flags, EventProperty_AnyKey) ||
|
||||||
|
match_mouse_code_release(event, MouseCode_Left)){
|
||||||
|
models->state = APP_STATE_EDIT;
|
||||||
|
}
|
||||||
|
else if (event->kind == InputEventKind_MouseMove){
|
||||||
|
if (input->mouse.l){
|
||||||
|
Panel *split = models->resizing_intermediate_panel;
|
||||||
|
Range_i32 limits = layout_get_limiting_range_on_split(layout, split);
|
||||||
|
i32 mouse_position = (split->vertical_split)?(mouse.x):(mouse.y);
|
||||||
|
mouse_position = clamp(limits.min, mouse_position, limits.max);
|
||||||
|
layout_set_split_absolute_position(layout, split, mouse_position);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
models->state = APP_STATE_EDIT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event_was_handled && event->kind == InputEventKind_KeyStroke){
|
||||||
|
for (Input_Event *dependent_text = event->key.first_dependent_text;
|
||||||
|
dependent_text != 0;
|
||||||
|
dependent_text = dependent_text->text.next_text){
|
||||||
|
Assert(dependent_text->kind == InputEventKind_TextInsert);
|
||||||
|
dependent_text->text.blocked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
linalloc_clear(&models->virtual_event_arena);
|
||||||
|
models->free_virtual_event = 0;
|
||||||
|
models->first_virtual_event = 0;
|
||||||
|
models->last_virtual_event = 0;
|
||||||
|
|
||||||
|
// NOTE(allen): send panel size update
|
||||||
|
if (models->layout.panel_state_dirty){
|
||||||
|
models->layout.panel_state_dirty = false;
|
||||||
|
if (models->buffer_viewer_update != 0){
|
||||||
|
Application_Links app = {};
|
||||||
|
app.tctx = tctx;
|
||||||
|
app.cmd_context = models;
|
||||||
|
models->buffer_viewer_update(&app);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(allen): dt
|
||||||
|
f32 literal_dt = 0.f;
|
||||||
|
u64 now_usecond_stamp = system_now_time();
|
||||||
|
if (!input->first_step){
|
||||||
|
u64 elapsed_useconds = now_usecond_stamp - models->last_render_usecond_stamp;
|
||||||
|
literal_dt = (f32)((f64)(elapsed_useconds)/1000000.f);
|
||||||
|
}
|
||||||
|
models->last_render_usecond_stamp = now_usecond_stamp;
|
||||||
|
f32 animation_dt = 0.f;
|
||||||
|
if (models->animated_last_frame){
|
||||||
|
animation_dt = literal_dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(allen): on the first frame there should be no scrolling
|
||||||
|
if (input->first_step){
|
||||||
|
for (Panel *panel = layout_get_first_open_panel(layout);
|
||||||
|
panel != 0;
|
||||||
|
panel = layout_get_next_open_panel(layout, panel)){
|
||||||
|
View *view = panel->view;
|
||||||
|
File_Edit_Positions edit_pos = view_get_edit_pos(view);
|
||||||
|
edit_pos.scroll.position = view_normalize_buffer_point(tctx, models, view, edit_pos.scroll.target);
|
||||||
|
block_zero_struct(&edit_pos.scroll.target);
|
||||||
|
view_set_edit_pos(view, edit_pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(allen): hook for files reloaded
|
||||||
|
{
|
||||||
|
Working_Set *working_set = &models->working_set;
|
||||||
|
Assert(working_set->has_external_mod_sentinel.next != 0);
|
||||||
|
if (working_set->has_external_mod_sentinel.next != &working_set->has_external_mod_sentinel){
|
||||||
|
for (Node *node = working_set->has_external_mod_sentinel.next, *next = 0;
|
||||||
|
node != &working_set->has_external_mod_sentinel;
|
||||||
|
node = next){
|
||||||
|
next = node->next;
|
||||||
|
Editing_File *file = CastFromMember(Editing_File, external_mod_node, node);
|
||||||
|
dll_remove(node);
|
||||||
|
block_zero_struct(node);
|
||||||
|
co_send_core_event(tctx, models, CoreCode_FileExternallyModified, file->id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(allen): if the exit signal has been sent, run the exit hook.
|
||||||
|
if (!models->keep_playing || input->trying_to_kill){
|
||||||
|
co_send_core_event(tctx, models, CoreCode_TryExit);
|
||||||
|
models->keep_playing = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(allen): rendering
|
||||||
|
{
|
||||||
|
Frame_Info frame = {};
|
||||||
|
frame.index = models->frame_counter;
|
||||||
|
frame.literal_dt = literal_dt;
|
||||||
|
frame.animation_dt = animation_dt;
|
||||||
|
|
||||||
|
Application_Links app = {};
|
||||||
|
app.tctx = tctx;
|
||||||
|
app.cmd_context = models;
|
||||||
|
|
||||||
|
if (models->tick != 0){
|
||||||
|
models->tick(&app, frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
begin_render_section(target, models->frame_counter, literal_dt, animation_dt);
|
||||||
|
models->in_render_mode = true;
|
||||||
|
|
||||||
|
Live_Views *live_views = &models->view_set;
|
||||||
|
for (Node *node = layout->open_panels.next;
|
||||||
|
node != &layout->open_panels;
|
||||||
|
node = node->next){
|
||||||
|
Panel *panel = CastFromMember(Panel, node, node);
|
||||||
|
View *view = panel->view;
|
||||||
|
View_Context_Node *ctx = view->ctx;
|
||||||
|
if (ctx != 0){
|
||||||
|
Render_Caller_Function *render_caller = ctx->ctx.render_caller;
|
||||||
|
if (render_caller != 0){
|
||||||
|
render_caller(&app, frame, view_get_id(live_views, view));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (models->whole_screen_render_caller != 0){
|
||||||
|
models->whole_screen_render_caller(&app, frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
models->in_render_mode = false;
|
||||||
|
end_render_section(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(allen): This is dumb. Let's rethink view cleanup strategy.
|
||||||
|
// NOTE(allen): wind down coroutines
|
||||||
|
for (;;){
|
||||||
|
Model_Wind_Down_Co *node = models->wind_down_stack;
|
||||||
|
if (node == 0){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sll_stack_pop(models->wind_down_stack);
|
||||||
|
Coroutine *co = node->co;
|
||||||
|
|
||||||
|
for (i32 j = 0; co != 0; j += 1){
|
||||||
|
Co_In in = {};
|
||||||
|
in.user_input.abort = true;
|
||||||
|
Co_Out ignore = {};
|
||||||
|
co = co_run(tctx, models, co, &in, &ignore);
|
||||||
|
if (j == 100){
|
||||||
|
Application_Links app = {};
|
||||||
|
app.tctx = tctx;
|
||||||
|
app.cmd_context = models;
|
||||||
|
#define M "SERIOUS ERROR: coroutine wind down did not complete\n"
|
||||||
|
print_message(&app, string_u8_litexpr(M));
|
||||||
|
#undef M
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sll_stack_push(models->free_wind_downs, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// NOTE(allen): flush the log
|
||||||
|
log_flush(tctx, models);
|
||||||
|
|
||||||
|
// NOTE(allen): set the app_result
|
||||||
|
Application_Step_Result app_result = {};
|
||||||
|
app_result.mouse_cursor_type = APP_MOUSE_CURSOR_DEFAULT;
|
||||||
|
app_result.lctrl_lalt_is_altgr = models->settings.lctrl_lalt_is_altgr;
|
||||||
|
|
||||||
|
// NOTE(allen): get new window title
|
||||||
|
if (models->has_new_title){
|
||||||
|
models->has_new_title = false;
|
||||||
|
app_result.has_new_title = true;
|
||||||
|
app_result.title_string = models->title_space;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(allen): get cursor type
|
||||||
|
if (mouse_panel != 0 && !mouse_in_margin){
|
||||||
|
app_result.mouse_cursor_type = APP_MOUSE_CURSOR_ARROW;
|
||||||
|
}
|
||||||
|
else if (divider_panel != 0){
|
||||||
|
if (divider_panel->vertical_split){
|
||||||
|
app_result.mouse_cursor_type = APP_MOUSE_CURSOR_LEFTRIGHT;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
app_result.mouse_cursor_type = APP_MOUSE_CURSOR_UPDOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
app_result.mouse_cursor_type = APP_MOUSE_CURSOR_ARROW;
|
||||||
|
}
|
||||||
|
|
||||||
|
models->prev_mouse_panel = mouse_panel;
|
||||||
|
app_result.lctrl_lalt_is_altgr = models->settings.lctrl_lalt_is_altgr;
|
||||||
|
app_result.perform_kill = models->hard_exit;
|
||||||
|
app_result.animating = models->animate_next_frame;
|
||||||
|
if (models->animate_next_frame){
|
||||||
|
// NOTE(allen): Silence the timer, because we're going to do another frame right away anyways.
|
||||||
|
system_wake_up_timer_set(models->period_wakeup_timer, max_u32);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
// NOTE(allen): Set the timer's wakeup period, possibly to max_u32 thus effectively silencing it.
|
||||||
|
system_wake_up_timer_set(models->period_wakeup_timer, models->next_animate_delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(allen): Update Frame to Frame States
|
||||||
|
models->prev_p = input->mouse.p;
|
||||||
|
models->animated_last_frame = app_result.animating;
|
||||||
|
models->frame_counter += 1;
|
||||||
|
|
||||||
|
// end-of-app_step
|
||||||
|
return(app_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" App_Get_Functions_Sig(app_get_functions){
|
||||||
|
App_Functions result = {};
|
||||||
|
|
||||||
|
result.load_vtables = app_load_vtables;
|
||||||
|
result.get_logger = app_get_logger;
|
||||||
|
result.read_command_line = app_read_command_line;
|
||||||
|
result.init = app_init;
|
||||||
|
result.step = app_step;
|
||||||
|
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 12.12.2014
|
||||||
|
*
|
||||||
|
* Application Layer for 4coder
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#if !defined(FRED_H)
|
||||||
|
#define FRED_H
|
||||||
|
|
||||||
|
#define MAX_VIEWS 16
|
||||||
|
|
||||||
|
struct Plat_Settings{
|
||||||
|
char *custom_dll;
|
||||||
|
b8 custom_dll_is_strict;
|
||||||
|
b8 fullscreen_window;
|
||||||
|
|
||||||
|
i32 window_w;
|
||||||
|
i32 window_h;
|
||||||
|
i32 window_x;
|
||||||
|
i32 window_y;
|
||||||
|
b8 set_window_pos;
|
||||||
|
b8 set_window_size;
|
||||||
|
b8 maximize_window;
|
||||||
|
|
||||||
|
b8 use_hinting;
|
||||||
|
|
||||||
|
char *user_directory;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define App_Read_Command_Line_Sig(name) \
|
||||||
|
void *name(Thread_Context *tctx,\
|
||||||
|
String_Const_u8 current_directory,\
|
||||||
|
Plat_Settings *plat_settings,\
|
||||||
|
char ***files, \
|
||||||
|
i32 **file_count,\
|
||||||
|
i32 argc, \
|
||||||
|
char **argv)
|
||||||
|
|
||||||
|
typedef App_Read_Command_Line_Sig(App_Read_Command_Line);
|
||||||
|
|
||||||
|
struct Custom_API{
|
||||||
|
_Get_Version_Type *get_version;
|
||||||
|
_Init_APIs_Type *init_apis;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define App_Init_Sig(name) \
|
||||||
|
void name(Thread_Context *tctx, \
|
||||||
|
Render_Target *target, \
|
||||||
|
void *base_ptr, \
|
||||||
|
String_Const_u8 current_directory,\
|
||||||
|
Custom_API api)
|
||||||
|
|
||||||
|
typedef App_Init_Sig(App_Init);
|
||||||
|
|
||||||
|
#include "4ed_cursor_codes.h"
|
||||||
|
|
||||||
|
struct Application_Step_Result{
|
||||||
|
Application_Mouse_Cursor mouse_cursor_type;
|
||||||
|
b32 lctrl_lalt_is_altgr;
|
||||||
|
b32 perform_kill;
|
||||||
|
b32 animating;
|
||||||
|
b32 has_new_title;
|
||||||
|
char *title_string;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Application_Step_Input{
|
||||||
|
b32 first_step;
|
||||||
|
f32 dt;
|
||||||
|
Mouse_State mouse;
|
||||||
|
Input_List events;
|
||||||
|
String_Const_u8 clipboard;
|
||||||
|
b32 trying_to_kill;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define App_Step_Sig(name) Application_Step_Result \
|
||||||
|
name(Thread_Context *tctx, \
|
||||||
|
Render_Target *target, \
|
||||||
|
void *base_ptr, \
|
||||||
|
Application_Step_Input *input)
|
||||||
|
|
||||||
|
typedef App_Step_Sig(App_Step);
|
||||||
|
|
||||||
|
typedef b32 Log_Function(String_Const_u8 str);
|
||||||
|
typedef Log_Function *App_Get_Logger(void);
|
||||||
|
typedef void App_Load_VTables(API_VTable_system *vtable_system,
|
||||||
|
API_VTable_font *vtable_font,
|
||||||
|
API_VTable_graphics *vtable_graphics);
|
||||||
|
|
||||||
|
struct App_Functions{
|
||||||
|
App_Load_VTables *load_vtables;
|
||||||
|
App_Get_Logger *get_logger;
|
||||||
|
App_Read_Command_Line *read_command_line;
|
||||||
|
App_Init *init;
|
||||||
|
App_Step *step;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define App_Get_Functions_Sig(name) App_Functions name()
|
||||||
|
typedef App_Get_Functions_Sig(App_Get_Functions);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 06.10.2019
|
||||||
|
*
|
||||||
|
* Type checker that lists errors between two api parses.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#include "4coder_base_types.h"
|
||||||
|
#include "4coder_token.h"
|
||||||
|
#include "generated/lexer_cpp.h"
|
||||||
|
#include "4ed_api_definition.h"
|
||||||
|
|
||||||
|
#include "4coder_base_types.cpp"
|
||||||
|
#include "4coder_stringf.cpp"
|
||||||
|
#include "4coder_malloc_allocator.cpp"
|
||||||
|
#include "4coder_token.cpp"
|
||||||
|
#include "generated/lexer_cpp.cpp"
|
||||||
|
#include "4coder_file.cpp"
|
||||||
|
#include "4ed_api_definition.cpp"
|
||||||
|
#include "4ed_api_parser.cpp"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
function void
|
||||||
|
print_usage(void){
|
||||||
|
printf("usage: <script> <source-1> {<source-1>} : <source-2> {<source-2>}\n"
|
||||||
|
" source-1 : the authoritative/master api source file(s)\n"
|
||||||
|
" source-2 : the 'remote' api source file(s) to check against the master\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char **argv){
|
||||||
|
Arena arena = make_arena_malloc();
|
||||||
|
|
||||||
|
if (argc < 4){
|
||||||
|
print_usage();
|
||||||
|
}
|
||||||
|
|
||||||
|
API_Definition_List master_list = {};
|
||||||
|
API_Definition_List remote_list = {};
|
||||||
|
|
||||||
|
{
|
||||||
|
i32 i = 1;
|
||||||
|
for (;i < argc; i += 1){
|
||||||
|
char *file_name = argv[i];
|
||||||
|
if (string_match(SCu8(file_name), string_u8_litexpr(":"))){
|
||||||
|
i += 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
FILE *file = fopen(file_name, "rb");
|
||||||
|
if (file == 0){
|
||||||
|
printf("error: could not open input file: '%s'\n", file_name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String_Const_u8 text = data_from_file(&arena, file);
|
||||||
|
fclose(file);
|
||||||
|
if (text.size > 0){
|
||||||
|
api_parse_source_add_to_list(&arena, SCu8(file_name), text, &master_list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (;i < argc; i += 1){
|
||||||
|
char *file_name = argv[i];
|
||||||
|
FILE *file = fopen(file_name, "rb");
|
||||||
|
if (file == 0){
|
||||||
|
printf("error: could not open input file: '%s'\n", file_name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String_Const_u8 text = data_from_file(&arena, file);
|
||||||
|
fclose(file);
|
||||||
|
if (text.size > 0){
|
||||||
|
api_parse_source_add_to_list(&arena, SCu8(file_name), text, &remote_list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (master_list.count == 0){
|
||||||
|
printf("error: no apis in master list\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remote_list.count == 0){
|
||||||
|
printf("error: no apis in remote list\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
List_String_Const_u8 errors = {};
|
||||||
|
api_list_check(&arena, &master_list, &remote_list, APICheck_ReportAll, &errors);
|
||||||
|
String_Const_u8 string = string_list_flatten(&arena, errors, StringFill_NullTerminate);
|
||||||
|
printf("%.*s", string_expand(string));
|
||||||
|
if (string.size > 0){
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
|
@ -0,0 +1,649 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 03.10.2019
|
||||||
|
*
|
||||||
|
* System API definition program.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
function API_Definition*
|
||||||
|
begin_api(Arena *arena, char *name){
|
||||||
|
API_Definition *api = push_array_zero(arena, API_Definition, 1);
|
||||||
|
api->name = SCu8(name);
|
||||||
|
return(api);
|
||||||
|
}
|
||||||
|
|
||||||
|
function API_Call*
|
||||||
|
api_call_with_location(Arena *arena, API_Definition *api, String_Const_u8 name, String_Const_u8 type, String_Const_u8 location){
|
||||||
|
API_Call *call = push_array_zero(arena, API_Call, 1);
|
||||||
|
sll_queue_push(api->first_call, api->last_call, call);
|
||||||
|
api->call_count += 1;
|
||||||
|
call->name = name;
|
||||||
|
call->return_type = type;
|
||||||
|
call->location_string = location;
|
||||||
|
return(call);
|
||||||
|
}
|
||||||
|
|
||||||
|
function API_Call*
|
||||||
|
api_call_with_location(Arena *arena, API_Definition *api, char *name, char *type, char *location){
|
||||||
|
return(api_call_with_location(arena, api, SCu8(name), SCu8(type), SCu8(location)));
|
||||||
|
}
|
||||||
|
|
||||||
|
function API_Type*
|
||||||
|
api_type_structure_with_location(Arena *arena, API_Definition *api, API_Type_Structure_Kind kind, String_Const_u8 name, List_String_Const_u8 member_list, String_Const_u8 definition, String_Const_u8 location){
|
||||||
|
API_Type *type = push_array_zero(arena, API_Type, 1);
|
||||||
|
sll_queue_push(api->first_type, api->last_type, type);
|
||||||
|
api->type_count += 1;
|
||||||
|
type->kind = APITypeKind_Structure;
|
||||||
|
type->name = name;
|
||||||
|
type->location_string = location;
|
||||||
|
type->struct_type.kind = kind;
|
||||||
|
type->struct_type.member_names = member_list;
|
||||||
|
type->struct_type.definition_string = definition;
|
||||||
|
return(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
function API_Type*
|
||||||
|
api_type_structure_with_location(Arena *arena, API_Definition *api, API_Type_Structure_Kind kind, char *name, List_String_Const_u8 member_list, char *definition, char *location){
|
||||||
|
return(api_type_structure_with_location(arena, api, kind, name, member_list, definition, location));
|
||||||
|
}
|
||||||
|
|
||||||
|
#define api_call(arena, api, name, type) \
|
||||||
|
api_call_with_location((arena), (api), (name), (type), file_name_line_number)
|
||||||
|
|
||||||
|
function API_Param*
|
||||||
|
api_param(Arena *arena, API_Call *call, char *type_name, char *name){
|
||||||
|
API_Param *param = push_array_zero(arena, API_Param, 1);
|
||||||
|
sll_queue_push(call->params.first, call->params.last, param);
|
||||||
|
call->params.count += 1;
|
||||||
|
param->type_name = SCu8(type_name);
|
||||||
|
param->name = SCu8(name);
|
||||||
|
return(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
api_set_param_list(API_Call *call, API_Param_List list){
|
||||||
|
call->params = list;
|
||||||
|
}
|
||||||
|
|
||||||
|
function API_Definition*
|
||||||
|
api_get_api(API_Definition_List *list, String_Const_u8 name){
|
||||||
|
API_Definition *result = 0;
|
||||||
|
for (API_Definition *node = list->first;
|
||||||
|
node != 0;
|
||||||
|
node = node->next){
|
||||||
|
if (string_match(name, node->name)){
|
||||||
|
result = node;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function API_Definition*
|
||||||
|
api_get_api(Arena *arena, API_Definition_List *list, String_Const_u8 name){
|
||||||
|
API_Definition *result = api_get_api(list, name);
|
||||||
|
if (result == 0){
|
||||||
|
result = push_array_zero(arena, API_Definition, 1);
|
||||||
|
sll_queue_push(list->first, list->last, result);
|
||||||
|
list->count += 1;
|
||||||
|
result->name = name;
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function API_Call*
|
||||||
|
api_get_call(API_Definition *api, String_Const_u8 name){
|
||||||
|
API_Call *result = 0;
|
||||||
|
for (API_Call *call = api->first_call;
|
||||||
|
call != 0;
|
||||||
|
call = call->next){
|
||||||
|
if (string_match(name, call->name)){
|
||||||
|
result = call;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function b32
|
||||||
|
api_call_match_sigs(API_Call *a, API_Call *b){
|
||||||
|
b32 result = false;
|
||||||
|
if (a->params.count == b->params.count &&
|
||||||
|
string_match(a->return_type, b->return_type)){
|
||||||
|
result = true;
|
||||||
|
for (API_Param *a_param = a->params.first, *b_param = b->params.first;
|
||||||
|
a_param != 0 && b_param != 0;
|
||||||
|
a_param = a_param->next, b_param = b_param->next){
|
||||||
|
if (!string_match(a_param->name, b_param->name) ||
|
||||||
|
!string_match(a_param->type_name, b_param->type_name)){
|
||||||
|
result = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function API_Type*
|
||||||
|
api_get_type(API_Definition *api, String_Const_u8 name){
|
||||||
|
API_Type *result = 0;
|
||||||
|
for (API_Type *type = api->first_type;
|
||||||
|
type != 0;
|
||||||
|
type = type->next){
|
||||||
|
if (string_match(type->name, name)){
|
||||||
|
result = type;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function b32
|
||||||
|
api_type_match(API_Type *a, API_Type *b){
|
||||||
|
b32 result = false;
|
||||||
|
if (a->kind == b->kind && string_match(a->name, b->name)){
|
||||||
|
switch (a->kind){
|
||||||
|
case APITypeKind_Structure:
|
||||||
|
{
|
||||||
|
if (a->kind == b->kind &&
|
||||||
|
string_list_match(a->struct_type.member_names, b->struct_type.member_names) &&
|
||||||
|
string_match(a->struct_type.definition_string, b->struct_type.definition_string)){
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case APITypeKind_Enum:
|
||||||
|
{
|
||||||
|
if (a->enum_type.val_count == b->enum_type.val_count &&
|
||||||
|
string_match(a->enum_type.type_name, b->enum_type.type_name)){
|
||||||
|
result = true;
|
||||||
|
for (API_Enum_Value *a_node = a->enum_type.first_val, *b_node = b->enum_type.first_val;
|
||||||
|
a_node != 0 && b_node != 0;
|
||||||
|
a_node = a_node->next, b_node = b_node->next){
|
||||||
|
if (!string_match(a_node->name, b_node->name) ||
|
||||||
|
!string_match(a_node->val, b_node->val)){
|
||||||
|
result = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case APITypeKind_Typedef:
|
||||||
|
{
|
||||||
|
if (string_match(a->typedef_type.name, b->typedef_type.name) &&
|
||||||
|
string_match(a->typedef_type.definition_text, b->typedef_type.definition_text)){
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
#if !defined(SKIP_STDIO)
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "4coder_stringf.cpp"
|
||||||
|
|
||||||
|
function String_Const_u8
|
||||||
|
api_get_callable_name(Arena *arena, String_Const_u8 api_name, String_Const_u8 name, API_Generation_Flag flags){
|
||||||
|
String_Const_u8 result = {};
|
||||||
|
if (HasFlag(flags, APIGeneration_NoAPINameOnCallables)){
|
||||||
|
result = push_u8_stringf(arena, "%.*s", string_expand(name));
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
result = push_u8_stringf(arena, "%.*s_%.*s",
|
||||||
|
string_expand(api_name),
|
||||||
|
string_expand(name));
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
function void
|
||||||
|
generate_api_master_list(Arena *scratch, API_Definition *api, API_Generation_Flag flags, FILE *out){
|
||||||
|
for (API_Call *call = api->first_call;
|
||||||
|
call != 0;
|
||||||
|
call = call->next){
|
||||||
|
fprintf(out, "api(%.*s) function %.*s %.*s(",
|
||||||
|
string_expand(api->name),
|
||||||
|
string_expand(call->return_type),
|
||||||
|
string_expand(call->name));
|
||||||
|
if (call->params.count == 0){
|
||||||
|
fprintf(out, "void");
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
for (API_Param *param = call->params.first;
|
||||||
|
param != 0;
|
||||||
|
param = param->next){
|
||||||
|
fprintf(out, "%.*s %.*s",
|
||||||
|
string_expand(param->type_name),
|
||||||
|
string_expand(param->name));
|
||||||
|
if (param->next != 0){
|
||||||
|
fprintf(out, ", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf(out, ");\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
generate_header(Arena *scratch, API_Definition *api, API_Generation_Flag flags, FILE *out){
|
||||||
|
for (API_Call *call = api->first_call;
|
||||||
|
call != 0;
|
||||||
|
call = call->next){
|
||||||
|
fprintf(out, "#define %.*s_%.*s_sig() %.*s %.*s_%.*s(",
|
||||||
|
string_expand(api->name),
|
||||||
|
string_expand(call->name),
|
||||||
|
string_expand(call->return_type),
|
||||||
|
string_expand(api->name),
|
||||||
|
string_expand(call->name));
|
||||||
|
if (call->params.count == 0){
|
||||||
|
fprintf(out, "void");
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
for (API_Param *param = call->params.first;
|
||||||
|
param != 0;
|
||||||
|
param = param->next){
|
||||||
|
fprintf(out, "%.*s %.*s",
|
||||||
|
string_expand(param->type_name),
|
||||||
|
string_expand(param->name));
|
||||||
|
if (param->next != 0){
|
||||||
|
fprintf(out, ", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf(out, ")\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (API_Call *call = api->first_call;
|
||||||
|
call != 0;
|
||||||
|
call = call->next){
|
||||||
|
fprintf(out, "typedef %.*s %.*s_%.*s_type(",
|
||||||
|
string_expand(call->return_type),
|
||||||
|
string_expand(api->name),
|
||||||
|
string_expand(call->name));
|
||||||
|
if (call->params.count == 0){
|
||||||
|
fprintf(out, "void");
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
for (API_Param *param = call->params.first;
|
||||||
|
param != 0;
|
||||||
|
param = param->next){
|
||||||
|
fprintf(out, "%.*s %.*s",
|
||||||
|
string_expand(param->type_name),
|
||||||
|
string_expand(param->name));
|
||||||
|
if (param->next != 0){
|
||||||
|
fprintf(out, ", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf(out, ");\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(out, "struct API_VTable_%.*s{\n", string_expand(api->name));
|
||||||
|
for (API_Call *call = api->first_call;
|
||||||
|
call != 0;
|
||||||
|
call = call->next){
|
||||||
|
fprintf(out, "%.*s_%.*s_type *",
|
||||||
|
string_expand(api->name),
|
||||||
|
string_expand(call->name));
|
||||||
|
fprintf(out, "%.*s",
|
||||||
|
string_expand(call->name));
|
||||||
|
fprintf(out, ";\n");
|
||||||
|
}
|
||||||
|
fprintf(out, "};\n");
|
||||||
|
|
||||||
|
fprintf(out, "#if defined(STATIC_LINK_API)\n");
|
||||||
|
for (API_Call *call = api->first_call;
|
||||||
|
call != 0;
|
||||||
|
call = call->next){
|
||||||
|
String_Const_u8 callable_name = api_get_callable_name(scratch, api->name, call->name, flags);
|
||||||
|
fprintf(out, "internal %.*s %.*s(",
|
||||||
|
string_expand(call->return_type),
|
||||||
|
string_expand(callable_name));
|
||||||
|
if (call->params.count == 0){
|
||||||
|
fprintf(out, "void");
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
for (API_Param *param = call->params.first;
|
||||||
|
param != 0;
|
||||||
|
param = param->next){
|
||||||
|
fprintf(out, "%.*s %.*s",
|
||||||
|
string_expand(param->type_name),
|
||||||
|
string_expand(param->name));
|
||||||
|
if (param->next != 0){
|
||||||
|
fprintf(out, ", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf(out, ");\n");
|
||||||
|
}
|
||||||
|
fprintf(out, "#undef STATIC_LINK_API\n");
|
||||||
|
fprintf(out, "#elif defined(DYNAMIC_LINK_API)\n");
|
||||||
|
for (API_Call *call = api->first_call;
|
||||||
|
call != 0;
|
||||||
|
call = call->next){
|
||||||
|
String_Const_u8 callable_name = api_get_callable_name(scratch, api->name, call->name, flags);
|
||||||
|
fprintf(out, "global %.*s_%.*s_type *%.*s = 0;\n",
|
||||||
|
string_expand(api->name),
|
||||||
|
string_expand(call->name),
|
||||||
|
string_expand(callable_name));
|
||||||
|
}
|
||||||
|
fprintf(out, "#undef DYNAMIC_LINK_API\n");
|
||||||
|
fprintf(out, "#endif\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
generate_cpp(Arena *scratch, API_Definition *api, API_Generation_Flag flags, FILE *out){
|
||||||
|
fprintf(out, "function void\n");
|
||||||
|
fprintf(out, "%.*s_api_fill_vtable(API_VTable_%.*s *vtable){\n",
|
||||||
|
string_expand(api->name),
|
||||||
|
string_expand(api->name));
|
||||||
|
for (API_Call *call = api->first_call;
|
||||||
|
call != 0;
|
||||||
|
call = call->next){
|
||||||
|
String_Const_u8 callable_name = api_get_callable_name(scratch, api->name, call->name, flags);
|
||||||
|
fprintf(out, "vtable->%.*s = %.*s;\n",
|
||||||
|
string_expand(call->name),
|
||||||
|
string_expand(callable_name));
|
||||||
|
}
|
||||||
|
fprintf(out, "}\n");
|
||||||
|
|
||||||
|
fprintf(out, "#if defined(DYNAMIC_LINK_API)\n");
|
||||||
|
fprintf(out, "function void\n");
|
||||||
|
fprintf(out, "%.*s_api_read_vtable(API_VTable_%.*s *vtable){\n",
|
||||||
|
string_expand(api->name),
|
||||||
|
string_expand(api->name));
|
||||||
|
for (API_Call *call = api->first_call;
|
||||||
|
call != 0;
|
||||||
|
call = call->next){
|
||||||
|
String_Const_u8 callable_name = api_get_callable_name(scratch, api->name, call->name, flags);
|
||||||
|
fprintf(out, "%.*s = vtable->%.*s;\n",
|
||||||
|
string_expand(callable_name),
|
||||||
|
string_expand(call->name));
|
||||||
|
}
|
||||||
|
fprintf(out, "}\n");
|
||||||
|
fprintf(out, "#undef DYNAMIC_LINK_API\n");
|
||||||
|
fprintf(out, "#endif\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
generate_constructor(Arena *scratch, API_Definition *api, API_Generation_Flag flags, FILE *out){
|
||||||
|
fprintf(out, "function API_Definition*\n");
|
||||||
|
fprintf(out, "%.*s_api_construct(Arena *arena){\n",
|
||||||
|
string_expand(api->name));
|
||||||
|
fprintf(out, "API_Definition *result = begin_api(arena, \"%.*s\");\n",
|
||||||
|
string_expand(api->name));
|
||||||
|
|
||||||
|
for (API_Call *call = api->first_call;
|
||||||
|
call != 0;
|
||||||
|
call = call->next){
|
||||||
|
fprintf(out, "{\n");
|
||||||
|
fprintf(out, "API_Call *call = api_call_with_location(arena, result, "
|
||||||
|
"string_u8_litexpr(\"%.*s\"), "
|
||||||
|
"string_u8_litexpr(\"%.*s\"), "
|
||||||
|
"string_u8_litexpr(\"\"));\n",
|
||||||
|
string_expand(call->name),
|
||||||
|
string_expand(call->return_type));
|
||||||
|
|
||||||
|
if (call->params.count == 0){
|
||||||
|
fprintf(out, "(void)call;\n");
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
for (API_Param *param = call->params.first;
|
||||||
|
param != 0;
|
||||||
|
param = param->next){
|
||||||
|
fprintf(out, "api_param(arena, call, \"%.*s\", \"%.*s\");\n",
|
||||||
|
string_expand(param->type_name),
|
||||||
|
string_expand(param->name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(out, "}\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(out, "return(result);\n");
|
||||||
|
fprintf(out, "}\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
function b32
|
||||||
|
api_definition_generate_api_includes(Arena *arena, API_Definition *api, Generated_Group group, API_Generation_Flag flags){
|
||||||
|
// NOTE(allen): Arrange output files
|
||||||
|
|
||||||
|
String_Const_u8 path_to_self = string_u8_litexpr(__FILE__);
|
||||||
|
path_to_self = string_remove_last_folder(path_to_self);
|
||||||
|
|
||||||
|
String_Const_u8 fname_ml = {};
|
||||||
|
String_Const_u8 fname_h = {};
|
||||||
|
String_Const_u8 fname_cpp = {};
|
||||||
|
String_Const_u8 fname_con = {};
|
||||||
|
|
||||||
|
String_Const_u8 root = {};
|
||||||
|
switch (group){
|
||||||
|
case GeneratedGroup_Core:
|
||||||
|
{
|
||||||
|
root = string_u8_litexpr("generated/");
|
||||||
|
}break;
|
||||||
|
case GeneratedGroup_Custom:
|
||||||
|
{
|
||||||
|
root = string_u8_litexpr("custom/generated/");
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fname_ml = push_u8_stringf(arena, "%.*s%.*s%.*s_api_master_list.h",
|
||||||
|
string_expand(path_to_self),
|
||||||
|
string_expand(root),
|
||||||
|
string_expand(api->name));
|
||||||
|
|
||||||
|
fname_h = push_u8_stringf(arena, "%.*s%.*s%.*s_api.h",
|
||||||
|
string_expand(path_to_self),
|
||||||
|
string_expand(root),
|
||||||
|
string_expand(api->name));
|
||||||
|
|
||||||
|
fname_cpp = push_u8_stringf(arena, "%.*s%.*s%.*s_api.cpp",
|
||||||
|
string_expand(path_to_self),
|
||||||
|
string_expand(root),
|
||||||
|
string_expand(api->name));
|
||||||
|
|
||||||
|
fname_con = push_u8_stringf(arena, "%.*s%.*s%.*s_api_constructor.cpp",
|
||||||
|
string_expand(path_to_self),
|
||||||
|
string_expand(root),
|
||||||
|
string_expand(api->name));
|
||||||
|
|
||||||
|
FILE *out_file_ml = fopen((char*)fname_ml.str, "wb");
|
||||||
|
if (out_file_ml == 0){
|
||||||
|
printf("could not open output file: '%s'\n", fname_ml.str);
|
||||||
|
return(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *out_file_h = fopen((char*)fname_h.str, "wb");
|
||||||
|
if (out_file_h == 0){
|
||||||
|
printf("could not open output file: '%s'\n", fname_h.str);
|
||||||
|
return(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *out_file_cpp = fopen((char*)fname_cpp.str, "wb");
|
||||||
|
if (out_file_cpp == 0){
|
||||||
|
printf("could not open output file: '%s'\n", fname_cpp.str);
|
||||||
|
return(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *out_file_con = fopen((char*)fname_con.str, "wb");
|
||||||
|
if (out_file_cpp == 0){
|
||||||
|
printf("could not open output file: '%s'\n", fname_con.str);
|
||||||
|
return(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%s:1:\n", fname_ml.str);
|
||||||
|
printf("%s:1:\n", fname_h.str);
|
||||||
|
printf("%s:1:\n", fname_cpp.str);
|
||||||
|
printf("%s:1:\n", fname_con.str);
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
// NOTE(allen): Generate output
|
||||||
|
|
||||||
|
generate_api_master_list(arena, api, flags, out_file_ml);
|
||||||
|
generate_header(arena, api, flags, out_file_h);
|
||||||
|
generate_cpp(arena, api, flags, out_file_cpp);
|
||||||
|
generate_constructor(arena, api, flags, out_file_con);
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
fclose(out_file_ml);
|
||||||
|
fclose(out_file_h);
|
||||||
|
fclose(out_file_cpp);
|
||||||
|
return(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
function void
|
||||||
|
api_definition_error(Arena *arena, List_String_Const_u8 *list,
|
||||||
|
char *e1, API_Call *c1, char *e2, API_Call *c2){
|
||||||
|
Assert(e1 != 0);
|
||||||
|
Assert(c1 != 0);
|
||||||
|
string_list_pushf(arena, list,
|
||||||
|
"%.*s error: %s '%.*s'",
|
||||||
|
string_expand(c1->location_string),
|
||||||
|
e1, string_expand(c1->name));
|
||||||
|
if (e2 != 0){
|
||||||
|
string_list_pushf(arena, list, " %s", e2);
|
||||||
|
if (c2 != 0){
|
||||||
|
string_list_pushf(arena, list, " '%.*s'", string_expand(c2->name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
string_list_push(arena, list, string_u8_litexpr("\n"));
|
||||||
|
if (c2 != 0){
|
||||||
|
string_list_push(arena, list, c2->location_string);
|
||||||
|
string_list_pushf(arena, list, " note: see declaration of '%.*s'\n", string_expand(c2->name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
api_definition_error(Arena *arena, List_String_Const_u8 *list,
|
||||||
|
char *e1, API_Call *c1, char *e2){
|
||||||
|
api_definition_error(arena, list, e1, c1, e2, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
api_definition_error(Arena *arena, List_String_Const_u8 *list,
|
||||||
|
char *e1, API_Call *c1){
|
||||||
|
api_definition_error(arena, list, e1, c1, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
api_definition_error(Arena *arena, List_String_Const_u8 *list,
|
||||||
|
char *e1, API_Definition *api1, char *e2){
|
||||||
|
Assert(e1 != 0);
|
||||||
|
Assert(api1 != 0);
|
||||||
|
string_list_pushf(arena, list, "error: %s '%.*s'",
|
||||||
|
e1, string_expand(api1->name));
|
||||||
|
if (e2 != 0){
|
||||||
|
string_list_pushf(arena, list, " %s", e2);
|
||||||
|
}
|
||||||
|
string_list_push(arena, list, string_u8_litexpr("\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
api_definition_error(Arena *arena, List_String_Const_u8 *list,
|
||||||
|
char *e1, API_Definition *api1){
|
||||||
|
api_definition_error(arena, list, e1, api1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
api_definition_check(Arena *arena, API_Definition *correct, API_Definition *remote, API_Check_Flag flags, List_String_Const_u8 *error_list){
|
||||||
|
b32 report_missing = HasFlag(flags, APICheck_ReportMissingAPI);
|
||||||
|
b32 report_extra = HasFlag(flags, APICheck_ReportExtraAPI);
|
||||||
|
b32 report_mismatch = HasFlag(flags, APICheck_ReportMismatchAPI);
|
||||||
|
|
||||||
|
b32 iterate_correct = (report_missing || report_mismatch);
|
||||||
|
if (iterate_correct){
|
||||||
|
for (API_Call *call = correct->first_call;
|
||||||
|
call != 0;
|
||||||
|
call = call->next){
|
||||||
|
API_Call *remote_call = api_get_call(remote, call->name);
|
||||||
|
if (remote_call == 0 && report_missing){
|
||||||
|
api_definition_error(arena, error_list,
|
||||||
|
"no remote call for", call);
|
||||||
|
}
|
||||||
|
if (remote_call != 0 && !api_call_match_sigs(call, remote_call) && report_mismatch){
|
||||||
|
api_definition_error(arena, error_list,
|
||||||
|
"remote call", remote_call,
|
||||||
|
"does not match signature for", call);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b32 iterate_remote = (report_extra);
|
||||||
|
if (iterate_remote){
|
||||||
|
for (API_Call *call = remote->first_call;
|
||||||
|
call != 0;
|
||||||
|
call = call->next){
|
||||||
|
API_Call *correct_call = api_get_call(correct, call->name);
|
||||||
|
if (correct_call == 0 && report_extra){
|
||||||
|
api_definition_error(arena, error_list,
|
||||||
|
"remote call", call,
|
||||||
|
"does not exist in api master");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
api_list_check(Arena *arena, API_Definition_List *correct, API_Definition_List *remote, API_Check_Flag flags, List_String_Const_u8 *error_list){
|
||||||
|
b32 report_missing = HasFlag(flags, APICheck_ReportMissingAPI);
|
||||||
|
b32 report_extra = HasFlag(flags, APICheck_ReportExtraAPI);
|
||||||
|
|
||||||
|
b32 iterate_correct = (report_missing);
|
||||||
|
if (iterate_correct){
|
||||||
|
for (API_Definition *api = correct->first;
|
||||||
|
api != 0;
|
||||||
|
api = api->next){
|
||||||
|
API_Definition *remote_api = api_get_api(remote, api->name);
|
||||||
|
if (remote_api == 0 && report_missing){
|
||||||
|
api_definition_error(arena, error_list,
|
||||||
|
"no remote api for", api);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b32 iterate_remote = (report_extra);
|
||||||
|
if (iterate_remote){
|
||||||
|
for (API_Definition *api = remote->first;
|
||||||
|
api != 0;
|
||||||
|
api = api->next){
|
||||||
|
API_Definition *correct_api = api_get_api(correct, api->name);
|
||||||
|
if (correct_api == 0 && report_extra){
|
||||||
|
api_definition_error(arena, error_list,
|
||||||
|
"remote api", api,
|
||||||
|
"does not have a master");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (API_Definition *api = correct->first;
|
||||||
|
api != 0;
|
||||||
|
api = api->next){
|
||||||
|
API_Definition *remote_api = api_get_api(remote, api->name);
|
||||||
|
if (remote_api != 0){
|
||||||
|
api_definition_check(arena, api, remote_api, flags, error_list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 03.10.2019
|
||||||
|
*
|
||||||
|
* System API definition types.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#if !defined(FRED_API_DEFINITION_H)
|
||||||
|
#define FRED_API_DEFINITION_H
|
||||||
|
|
||||||
|
struct API_Param{
|
||||||
|
API_Param *next;
|
||||||
|
String_Const_u8 type_name;
|
||||||
|
String_Const_u8 name;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct API_Param_List{
|
||||||
|
API_Param *first;
|
||||||
|
API_Param *last;
|
||||||
|
i32 count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct API_Call{
|
||||||
|
API_Call *next;
|
||||||
|
String_Const_u8 name;
|
||||||
|
String_Const_u8 return_type;
|
||||||
|
String_Const_u8 location_string;
|
||||||
|
API_Param_List params;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef i32 API_Type_Structure_Kind;
|
||||||
|
enum{
|
||||||
|
APITypeStructureKind_Struct,
|
||||||
|
APITypeStructureKind_Union,
|
||||||
|
};
|
||||||
|
struct API_Type_Structure{
|
||||||
|
API_Type_Structure_Kind kind;
|
||||||
|
List_String_Const_u8 member_names;
|
||||||
|
String_Const_u8 definition_string;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct API_Enum_Value{
|
||||||
|
API_Enum_Value *next;
|
||||||
|
String_Const_u8 name;
|
||||||
|
String_Const_u8 val;
|
||||||
|
};
|
||||||
|
struct API_Type_Enum{
|
||||||
|
String_Const_u8 type_name;
|
||||||
|
API_Enum_Value *first_val;
|
||||||
|
API_Enum_Value *last_val;
|
||||||
|
i32 val_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct API_Type_Typedef{
|
||||||
|
String_Const_u8 name;
|
||||||
|
String_Const_u8 definition_text;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef i32 API_Type_Kind;
|
||||||
|
enum{
|
||||||
|
APITypeKind_Structure,
|
||||||
|
APITypeKind_Enum,
|
||||||
|
APITypeKind_Typedef,
|
||||||
|
};
|
||||||
|
struct API_Type{
|
||||||
|
API_Type *next;
|
||||||
|
API_Type_Kind kind;
|
||||||
|
String_Const_u8 name;
|
||||||
|
String_Const_u8 location_string;
|
||||||
|
union{
|
||||||
|
API_Type_Structure struct_type;
|
||||||
|
API_Type_Enum enum_type;
|
||||||
|
API_Type_Typedef typedef_type;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct API_Definition{
|
||||||
|
API_Definition *next;
|
||||||
|
|
||||||
|
API_Call *first_call;
|
||||||
|
API_Call *last_call;
|
||||||
|
i32 call_count;
|
||||||
|
|
||||||
|
API_Type *first_type;
|
||||||
|
API_Type *last_type;
|
||||||
|
i32 type_count;
|
||||||
|
|
||||||
|
String_Const_u8 name;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct API_Definition_List{
|
||||||
|
API_Definition *first;
|
||||||
|
API_Definition *last;
|
||||||
|
i32 count;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef u32 API_Generation_Flag;
|
||||||
|
enum{
|
||||||
|
APIGeneration_NoAPINameOnCallables = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef u32 API_Check_Flag;
|
||||||
|
enum{
|
||||||
|
APICheck_ReportMissingAPI = 1,
|
||||||
|
APICheck_ReportExtraAPI = 2,
|
||||||
|
APICheck_ReportMismatchAPI = 4,
|
||||||
|
};
|
||||||
|
enum{
|
||||||
|
APICheck_ReportAll = APICheck_ReportMissingAPI|APICheck_ReportExtraAPI|APICheck_ReportMismatchAPI,
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 02.10.2019
|
||||||
|
*
|
||||||
|
* System API definition program.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#include "4coder_base_types.h"
|
||||||
|
#include "4ed_api_definition.h"
|
||||||
|
|
||||||
|
#include "4coder_base_types.cpp"
|
||||||
|
#include "4ed_api_definition.cpp"
|
||||||
|
#include "4coder_stringf.cpp"
|
||||||
|
#include "4coder_malloc_allocator.cpp"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
function API_Definition*
|
||||||
|
define_api(Arena *arena);
|
||||||
|
|
||||||
|
function Generated_Group
|
||||||
|
get_api_group(void);
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void){
|
||||||
|
Arena arena = make_arena_malloc();
|
||||||
|
API_Definition *api = define_api(&arena);
|
||||||
|
if (!api_definition_generate_api_includes(&arena, api, get_api_group(), 0)){
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,322 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 03.10.2019
|
||||||
|
*
|
||||||
|
* Parser that extracts an API from C++ source code.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
/*
|
||||||
|
function:
|
||||||
|
api ( <identifier> ) function <identifier> {*} <identifier> ( <param_list> )
|
||||||
|
|
||||||
|
param_list:
|
||||||
|
void |
|
||||||
|
<identifier> {*} <identifier> [, <identifier> {*} <identifier>]
|
||||||
|
|
||||||
|
anything_else:
|
||||||
|
***
|
||||||
|
|
||||||
|
api_source:
|
||||||
|
{function|anything_else} EOF
|
||||||
|
*/
|
||||||
|
|
||||||
|
function Token*
|
||||||
|
api_parse__token_pos(Token_Iterator *it){
|
||||||
|
return(token_it_read(it));
|
||||||
|
}
|
||||||
|
|
||||||
|
function b32
|
||||||
|
api_parse__match(Token_Iterator *it, Token_Cpp_Kind sub_kind){
|
||||||
|
b32 match = false;
|
||||||
|
Token *token = token_it_read(it);
|
||||||
|
if (token != 0 && token->sub_kind == sub_kind){
|
||||||
|
if (token_it_inc(it)){
|
||||||
|
match = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(match);
|
||||||
|
}
|
||||||
|
|
||||||
|
function b32
|
||||||
|
api_parse__match_identifier(Token_Iterator *it, String_Const_u8 source, String_Const_u8 *lexeme){
|
||||||
|
b32 match = false;
|
||||||
|
Token *token = token_it_read(it);
|
||||||
|
if (token->kind == TokenBaseKind_Identifier ||
|
||||||
|
token->kind == TokenBaseKind_Keyword){
|
||||||
|
if (token_it_inc(it)){
|
||||||
|
*lexeme = string_substring(source, Ii64(token));
|
||||||
|
match = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(match);
|
||||||
|
}
|
||||||
|
|
||||||
|
function b32
|
||||||
|
api_parse__match_identifier(Token_Iterator *it, String_Const_u8 source, char *lexeme){
|
||||||
|
b32 match = false;
|
||||||
|
Token *token = token_it_read(it);
|
||||||
|
if ((token->kind == TokenBaseKind_Identifier ||
|
||||||
|
token->kind == TokenBaseKind_Keyword) &&
|
||||||
|
string_match(SCu8(lexeme), string_substring(source, Ii64(token)))){
|
||||||
|
if (token_it_inc(it)){
|
||||||
|
match = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(match);
|
||||||
|
}
|
||||||
|
|
||||||
|
function String_Const_u8
|
||||||
|
api_parse__type_name_with_stars(Arena *arena, String_Const_u8 type, i32 star_counter){
|
||||||
|
if (star_counter > 0){
|
||||||
|
i32 type_full_size = (i32)(type.size) + star_counter;
|
||||||
|
u8 *type_buffer = push_array(arena, u8, type_full_size + 1);
|
||||||
|
block_copy(type_buffer, type.str, type.size);
|
||||||
|
block_fill_u8(type_buffer + type.size, star_counter, (u8)'*');
|
||||||
|
type_buffer[type_full_size] = 0;
|
||||||
|
type = SCu8(type_buffer, type_full_size);
|
||||||
|
}
|
||||||
|
return(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
api_parse_add_param(Arena *arena, API_Param_List *list, String_Const_u8 type, i32 star_counter, String_Const_u8 name){
|
||||||
|
type = api_parse__type_name_with_stars(arena, type, star_counter);
|
||||||
|
API_Param *param = push_array(arena, API_Param, 1);
|
||||||
|
sll_queue_push(list->first, list->last, param);
|
||||||
|
list->count += 1;
|
||||||
|
param->type_name = type;
|
||||||
|
param->name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
api_parse_add_function(Arena *arena, API_Definition_List *list,
|
||||||
|
String_Const_u8 api_name, String_Const_u8 func_name,
|
||||||
|
String_Const_u8 type, i32 star_counter, API_Param_List param_list,
|
||||||
|
String_Const_u8 location){
|
||||||
|
API_Definition *api = api_get_api(arena, list, api_name);
|
||||||
|
type = api_parse__type_name_with_stars(arena, type, star_counter);
|
||||||
|
API_Call *call = api_call_with_location(arena, api, func_name, type, location);
|
||||||
|
api_set_param_list(call, param_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
api_parse_add_structure(Arena *arena, API_Definition_List *list,
|
||||||
|
String_Const_u8 api_name, API_Type_Structure_Kind kind,
|
||||||
|
String_Const_u8 name, List_String_Const_u8 member_list,
|
||||||
|
String_Const_u8 definition, String_Const_u8 location){
|
||||||
|
API_Definition *api = api_get_api(arena, list, api_name);
|
||||||
|
api_type_structure_with_location(arena, api, kind, name, member_list, definition, location);
|
||||||
|
}
|
||||||
|
|
||||||
|
function String_Const_u8
|
||||||
|
api_parse_location(Arena *arena, String_Const_u8 source_name, String_Const_u8 source, u8 *pos){
|
||||||
|
i32 line_number = 1;
|
||||||
|
i32 col_number = 1;
|
||||||
|
if (source.str <= pos && pos < source.str + source.size){
|
||||||
|
for (u8 *ptr = source.str;;){
|
||||||
|
if (ptr == pos){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (*ptr == '\n'){
|
||||||
|
line_number += 1;
|
||||||
|
col_number = 1;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
col_number += 1;
|
||||||
|
}
|
||||||
|
ptr += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(push_u8_stringf(arena, "%.*s:%d:%d:", string_expand(source_name), line_number, col_number));
|
||||||
|
}
|
||||||
|
|
||||||
|
function b32
|
||||||
|
api_parse_source__function(Arena *arena, String_Const_u8 source_name, String_Const_u8 source, Token_Iterator *token_it, String_Const_u8 api_name, API_Definition_List *list){
|
||||||
|
b32 result = false;
|
||||||
|
String_Const_u8 ret_type = {};
|
||||||
|
i32 ret_type_star_counter = 0;
|
||||||
|
String_Const_u8 func_name = {};
|
||||||
|
API_Param_List param_list = {};
|
||||||
|
if (api_parse__match_identifier(token_it, source, &ret_type)){
|
||||||
|
for (;api_parse__match(token_it, TokenCppKind_Star);){
|
||||||
|
ret_type_star_counter += 1;
|
||||||
|
}
|
||||||
|
if (api_parse__match_identifier(token_it, source, &func_name)){
|
||||||
|
if (api_parse__match(token_it, TokenCppKind_ParenOp)){
|
||||||
|
b32 param_list_success = false;
|
||||||
|
if (api_parse__match_identifier(token_it, source, "void")){
|
||||||
|
param_list_success = true;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
for (;;){
|
||||||
|
String_Const_u8 type = {};
|
||||||
|
i32 star_counter = 0;
|
||||||
|
String_Const_u8 name = {};
|
||||||
|
if (api_parse__match_identifier(token_it, source, &type)){
|
||||||
|
for (;api_parse__match(token_it, TokenCppKind_Star);){
|
||||||
|
star_counter += 1;
|
||||||
|
}
|
||||||
|
if (api_parse__match_identifier(token_it, source, &name)){
|
||||||
|
param_list_success = true;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (param_list_success){
|
||||||
|
api_parse_add_param(arena, ¶m_list, type, star_counter, name);
|
||||||
|
}
|
||||||
|
if (api_parse__match(token_it, TokenCppKind_Comma)){
|
||||||
|
param_list_success = false;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (param_list_success){
|
||||||
|
if (api_parse__match(token_it, TokenCppKind_ParenCl)){
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result){
|
||||||
|
String_Const_u8 location = api_parse_location(arena, source_name, source, func_name.str);
|
||||||
|
api_parse_add_function(arena, list, api_name, func_name, ret_type, ret_type_star_counter, param_list, location);
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function String_Const_u8
|
||||||
|
api_parse__restringize_token_range(Arena *arena, String_Const_u8 source, Token *token, Token *token_end){
|
||||||
|
List_String_Const_u8 list = {};
|
||||||
|
for (Token *t = token; t < token_end; t += 1){
|
||||||
|
if (t->kind == TokenBaseKind_Comment){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (t->kind == TokenBaseKind_Whitespace){
|
||||||
|
// TODO(allen): if there is a newline, emit it, all other whitespace is managed automatically.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String_Const_u8 str = string_substring(source, Ii64(t));
|
||||||
|
string_list_push(arena, &list, str);
|
||||||
|
}
|
||||||
|
return(string_list_flatten(arena, list));
|
||||||
|
}
|
||||||
|
|
||||||
|
function b32
|
||||||
|
api_parse_source__structure(Arena *arena, String_Const_u8 source_name, String_Const_u8 source, API_Type_Structure_Kind kind, Token_Iterator *token_it, String_Const_u8 api_name, API_Definition_List *list){
|
||||||
|
b32 result = false;
|
||||||
|
String_Const_u8 name = {};
|
||||||
|
List_String_Const_u8 member_list = {};
|
||||||
|
Token *token = api_parse__token_pos(token_it);
|
||||||
|
(void)token;
|
||||||
|
if (api_parse__match_identifier(token_it, source, &name)){
|
||||||
|
if (api_parse__match(token_it, TokenCppKind_Semicolon)){
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
else if (api_parse__match(token_it, TokenCppKind_BraceOp)){
|
||||||
|
b32 member_list_success = false;
|
||||||
|
for (;;){
|
||||||
|
String_Const_u8 member_name = {};
|
||||||
|
if (api_parse__match(token_it, TokenCppKind_BraceCl)){
|
||||||
|
member_list_success = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (api_parse__match_identifier(token_it, source, &member_name)){
|
||||||
|
if (api_parse__match(token_it, TokenCppKind_Semicolon)){
|
||||||
|
string_list_push(arena, &member_list, member_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
if (!token_it_inc(token_it)){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (member_list_success){
|
||||||
|
if (api_parse__match(token_it, TokenCppKind_BraceCl)){
|
||||||
|
if (api_parse__match(token_it, TokenCppKind_Semicolon)){
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result){
|
||||||
|
Token *token_end = api_parse__token_pos(token_it);
|
||||||
|
(void)token_end;
|
||||||
|
// TODO(allen):
|
||||||
|
String_Const_u8 definition = {};
|
||||||
|
String_Const_u8 location = api_parse_location(arena, source_name, source, name.str);
|
||||||
|
api_parse_add_structure(arena, list, api_name, kind, name, member_list, definition, location);
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function b32
|
||||||
|
api_parse_source__struct(Arena *arena, String_Const_u8 source_name, String_Const_u8 source, Token_Iterator *token_it, String_Const_u8 api_name, API_Definition_List *list){
|
||||||
|
return(api_parse_source__structure(arena, source_name, source, APITypeStructureKind_Struct, token_it, api_name, list));
|
||||||
|
}
|
||||||
|
|
||||||
|
function b32
|
||||||
|
api_parse_source__union(Arena *arena, String_Const_u8 source_name, String_Const_u8 source, Token_Iterator *token_it, String_Const_u8 api_name, API_Definition_List *list){
|
||||||
|
return(api_parse_source__structure(arena, source_name, source, APITypeStructureKind_Union, token_it, api_name, list));
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
api_parse_source_add_to_list(Arena *arena, String_Const_u8 source_name, String_Const_u8 source, API_Definition_List *list){
|
||||||
|
Token_List token_list = lex_full_input_cpp(arena, source);
|
||||||
|
Token_Iterator token_it = token_iterator(token_iterator(0, &token_list));
|
||||||
|
|
||||||
|
for (;;){
|
||||||
|
Token *token = token_it_read(&token_it);
|
||||||
|
if (token->sub_kind == TokenCppKind_EOF){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (api_parse__match_identifier(&token_it, source, "api")){
|
||||||
|
String_Const_u8 api_name = {};
|
||||||
|
if (api_parse__match(&token_it, TokenCppKind_ParenOp)){
|
||||||
|
if (api_parse__match_identifier(&token_it, source, &api_name)){
|
||||||
|
if (api_parse__match(&token_it, TokenCppKind_ParenCl)){
|
||||||
|
if (api_parse__match_identifier(&token_it, source, "function")){
|
||||||
|
api_parse_source__function(arena, source_name, source, &token_it, api_name, list);
|
||||||
|
}
|
||||||
|
else if (api_parse__match_identifier(&token_it, source, "struct")){
|
||||||
|
api_parse_source__struct(arena, source_name, source, &token_it, api_name, list);
|
||||||
|
}
|
||||||
|
else if (api_parse__match_identifier(&token_it, source, "union")){
|
||||||
|
api_parse_source__union(arena, source_name, source, &token_it, api_name, list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
if (!token_it_inc(&token_it)){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function API_Definition_List
|
||||||
|
api_parse_source(Arena *arena, String_Const_u8 source_name, String_Const_u8 source){
|
||||||
|
API_Definition_List list = {};
|
||||||
|
api_parse_source_add_to_list(arena, source_name, source, &list);
|
||||||
|
return(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 06.10.2019
|
||||||
|
*
|
||||||
|
* Parser that extracts an API from C++ source code.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#include "4coder_base_types.h"
|
||||||
|
#include "4coder_token.h"
|
||||||
|
#include "generated/lexer_cpp.h"
|
||||||
|
#include "4ed_api_definition.h"
|
||||||
|
|
||||||
|
#include "4coder_base_types.cpp"
|
||||||
|
#include "4coder_stringf.cpp"
|
||||||
|
#include "4coder_malloc_allocator.cpp"
|
||||||
|
#include "4coder_token.cpp"
|
||||||
|
#include "generated/lexer_cpp.cpp"
|
||||||
|
#include "4coder_file.cpp"
|
||||||
|
#include "4ed_api_definition.cpp"
|
||||||
|
#include "4ed_api_parser.cpp"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char **argv){
|
||||||
|
Arena arena = make_arena_malloc();
|
||||||
|
|
||||||
|
if (argc < 2){
|
||||||
|
printf("usage: <script> <source> {<source>}\n"
|
||||||
|
" source : file to load and parse into the output list\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
API_Definition_List list = {};
|
||||||
|
for (i32 i = 1; i < argc; i += 1){
|
||||||
|
char *file_name = argv[i];
|
||||||
|
FILE *file = fopen(file_name, "rb");
|
||||||
|
if (file == 0){
|
||||||
|
printf("error: could not open input file: '%s'\n", argv[i]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String_Const_u8 text = data_from_file(&arena, file);
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
if (text.size > 0){
|
||||||
|
api_parse_source_add_to_list(&arena, SCu8(file_name), text, &list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (API_Definition *node = list.first;
|
||||||
|
node != 0;
|
||||||
|
node = node->next){
|
||||||
|
api_definition_generate_api_includes(&arena, node, GeneratedGroup_Custom, APIGeneration_NoAPINameOnCallables);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 01.05.2020
|
||||||
|
*
|
||||||
|
* Implementation of the root models features.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
function void
|
||||||
|
models_push_view_command_function(Models *models, View_ID view_id, Custom_Command_Function *custom_func){
|
||||||
|
Model_View_Command_Function *node = models->free_view_cmd_funcs;
|
||||||
|
if (node == 0){
|
||||||
|
node = push_array(models->arena, Model_View_Command_Function, 1);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
sll_stack_pop(models->free_view_cmd_funcs);
|
||||||
|
}
|
||||||
|
sll_queue_push(models->first_view_cmd_func, models->last_view_cmd_func, node);
|
||||||
|
node->view_id = view_id;
|
||||||
|
node->custom_func = custom_func;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Model_View_Command_Function
|
||||||
|
models_pop_view_command_function(Models *models){
|
||||||
|
Model_View_Command_Function result = {};
|
||||||
|
if (models->first_view_cmd_func != 0){
|
||||||
|
Model_View_Command_Function *node = models->first_view_cmd_func;
|
||||||
|
result.custom_func = node->custom_func;
|
||||||
|
result.view_id = node->view_id;
|
||||||
|
sll_queue_pop(models->first_view_cmd_func, models->last_view_cmd_func);
|
||||||
|
sll_stack_push(models->free_view_cmd_funcs, node);
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
models_push_virtual_event(Models *models, Input_Event *event){
|
||||||
|
Model_Input_Event_Node *node = models->free_virtual_event;
|
||||||
|
if (node == 0){
|
||||||
|
node = push_array(&models->virtual_event_arena, Model_Input_Event_Node, 1);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
sll_stack_pop(models->free_virtual_event);
|
||||||
|
}
|
||||||
|
sll_queue_push(models->first_virtual_event, models->last_virtual_event, node);
|
||||||
|
node->event = copy_input_event(&models->virtual_event_arena, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Input_Event
|
||||||
|
models_pop_virtual_event(Arena *arena, Models *models){
|
||||||
|
Input_Event result = {};
|
||||||
|
if (models->first_virtual_event != 0){
|
||||||
|
Model_Input_Event_Node *node = models->first_virtual_event;
|
||||||
|
result = copy_input_event(arena, &node->event);
|
||||||
|
sll_queue_pop(models->first_virtual_event, models->last_virtual_event);
|
||||||
|
sll_stack_push(models->free_virtual_event, node);
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
models_push_wind_down(Models *models, Coroutine *co){
|
||||||
|
Model_Wind_Down_Co *node = models->free_wind_downs;
|
||||||
|
if (node != 0){
|
||||||
|
sll_stack_pop(models->free_wind_downs);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
node = push_array(models->arena, Model_Wind_Down_Co, 1);
|
||||||
|
}
|
||||||
|
sll_stack_push(models->wind_down_stack, node);
|
||||||
|
node->co = co;
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
|
@ -0,0 +1,203 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 06.05.2016 (dd.mm.yyyy)
|
||||||
|
*
|
||||||
|
* Global app level settings definition
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#if !defined(FRED_APP_MODELS_H)
|
||||||
|
#define FRED_APP_MODELS_H
|
||||||
|
|
||||||
|
struct App_Settings{
|
||||||
|
char *init_files[8];
|
||||||
|
i32 init_files_count;
|
||||||
|
i32 init_files_max;
|
||||||
|
|
||||||
|
char **custom_flags;
|
||||||
|
i32 custom_flags_count;
|
||||||
|
|
||||||
|
b32 lctrl_lalt_is_altgr;
|
||||||
|
|
||||||
|
i32 font_size;
|
||||||
|
b8 use_hinting;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum App_State{
|
||||||
|
APP_STATE_EDIT,
|
||||||
|
APP_STATE_RESIZING,
|
||||||
|
// never below this
|
||||||
|
APP_STATE_COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Model_View_Command_Function{
|
||||||
|
Model_View_Command_Function *next;
|
||||||
|
Custom_Command_Function *custom_func;
|
||||||
|
View_ID view_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Model_Input_Event_Node{
|
||||||
|
Model_Input_Event_Node *next;
|
||||||
|
Input_Event event;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Model_Wind_Down_Co{
|
||||||
|
Model_Wind_Down_Co *next;
|
||||||
|
Coroutine *co;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Models{
|
||||||
|
Arena arena_;
|
||||||
|
Arena *arena;
|
||||||
|
Heap heap;
|
||||||
|
|
||||||
|
App_Settings settings;
|
||||||
|
App_State state;
|
||||||
|
|
||||||
|
Face_ID global_face_id;
|
||||||
|
|
||||||
|
Coroutine_Group coroutines;
|
||||||
|
Model_Wind_Down_Co *wind_down_stack;
|
||||||
|
Model_Wind_Down_Co *free_wind_downs;
|
||||||
|
|
||||||
|
Child_Process_Container child_processes;
|
||||||
|
Custom_API config_api;
|
||||||
|
|
||||||
|
Tick_Function *tick;
|
||||||
|
Render_Caller_Function *render_caller;
|
||||||
|
Whole_Screen_Render_Caller_Function *whole_screen_render_caller;
|
||||||
|
Delta_Rule_Function *delta_rule;
|
||||||
|
u64 delta_rule_memory_size;
|
||||||
|
|
||||||
|
Hook_Function *buffer_viewer_update;
|
||||||
|
Custom_Command_Function *view_event_handler;
|
||||||
|
Buffer_Name_Resolver_Function *buffer_name_resolver;
|
||||||
|
Buffer_Hook_Function *begin_buffer;
|
||||||
|
Buffer_Hook_Function *end_buffer;
|
||||||
|
Buffer_Hook_Function *new_file;
|
||||||
|
Buffer_Hook_Function *save_file;
|
||||||
|
Buffer_Edit_Range_Function *buffer_edit_range;
|
||||||
|
Buffer_Region_Function *buffer_region;
|
||||||
|
Layout_Function *layout_func;
|
||||||
|
View_Change_Buffer_Function *view_change_buffer;
|
||||||
|
|
||||||
|
Color_Table color_table_;
|
||||||
|
|
||||||
|
Model_View_Command_Function *free_view_cmd_funcs;
|
||||||
|
Model_View_Command_Function *first_view_cmd_func;
|
||||||
|
Model_View_Command_Function *last_view_cmd_func;
|
||||||
|
|
||||||
|
Arena virtual_event_arena;
|
||||||
|
Model_Input_Event_Node *free_virtual_event;
|
||||||
|
Model_Input_Event_Node *first_virtual_event;
|
||||||
|
Model_Input_Event_Node *last_virtual_event;
|
||||||
|
|
||||||
|
Layout layout;
|
||||||
|
Working_Set working_set;
|
||||||
|
Live_Views view_set;
|
||||||
|
Global_History global_history;
|
||||||
|
Text_Layout_Container text_layouts;
|
||||||
|
Font_Set font_set;
|
||||||
|
|
||||||
|
Managed_ID_Set managed_id_set;
|
||||||
|
Dynamic_Workspace dynamic_workspace;
|
||||||
|
Lifetime_Allocator lifetime_allocator;
|
||||||
|
|
||||||
|
Editing_File *message_buffer;
|
||||||
|
Editing_File *scratch_buffer;
|
||||||
|
Editing_File *log_buffer;
|
||||||
|
Editing_File *keyboard_buffer;
|
||||||
|
|
||||||
|
Hot_Directory hot_directory;
|
||||||
|
|
||||||
|
b8 keep_playing;
|
||||||
|
b8 hard_exit;
|
||||||
|
|
||||||
|
b8 has_new_title;
|
||||||
|
i32 title_capacity;
|
||||||
|
char *title_space;
|
||||||
|
|
||||||
|
Panel *resizing_intermediate_panel;
|
||||||
|
|
||||||
|
Plat_Handle period_wakeup_timer;
|
||||||
|
i32 frame_counter;
|
||||||
|
u32 next_animate_delay;
|
||||||
|
b32 animate_next_frame;
|
||||||
|
|
||||||
|
Profile_Global_List profile_list;
|
||||||
|
|
||||||
|
// Last frame state
|
||||||
|
Vec2_i32 prev_p;
|
||||||
|
Panel *prev_mouse_panel;
|
||||||
|
b32 animated_last_frame;
|
||||||
|
u64 last_render_usecond_stamp;
|
||||||
|
|
||||||
|
// Event Context
|
||||||
|
Application_Step_Input *input;
|
||||||
|
i64 current_input_sequence_number;
|
||||||
|
User_Input current_input;
|
||||||
|
b8 current_input_unhandled;
|
||||||
|
|
||||||
|
b8 in_render_mode;
|
||||||
|
Render_Target *target;
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
typedef i32 Dynamic_Workspace_Type;
|
||||||
|
enum{
|
||||||
|
DynamicWorkspace_Global = 0,
|
||||||
|
DynamicWorkspace_Unassociated = 1,
|
||||||
|
DynamicWorkspace_Buffer = 2,
|
||||||
|
DynamicWorkspace_View = 3,
|
||||||
|
DynamicWorkspace_Intersected = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Input_Types{
|
||||||
|
Input_AnyKey,
|
||||||
|
Input_Esc,
|
||||||
|
Input_MouseMove,
|
||||||
|
Input_MouseLeftButton,
|
||||||
|
Input_MouseRightButton,
|
||||||
|
Input_MouseWheel,
|
||||||
|
Input_Count
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Consumption_Record{
|
||||||
|
b32 consumed;
|
||||||
|
char consumer[32];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct File_Init{
|
||||||
|
String_Const_u8 name;
|
||||||
|
Editing_File **ptr;
|
||||||
|
b32 read_only;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Command_Line_Action{
|
||||||
|
CLAct_Nothing,
|
||||||
|
CLAct_Ignore,
|
||||||
|
CLAct_CustomDLL,
|
||||||
|
CLAct_WindowSize,
|
||||||
|
CLAct_WindowMaximize,
|
||||||
|
CLAct_WindowPosition,
|
||||||
|
CLAct_WindowFullscreen,
|
||||||
|
CLAct_FontSize,
|
||||||
|
CLAct_FontUseHinting,
|
||||||
|
CLAct_UserDirectory,
|
||||||
|
//
|
||||||
|
CLAct_COUNT,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Command_Line_Mode{
|
||||||
|
CLMode_App,
|
||||||
|
CLMode_Custom
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,127 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 13.11.2015
|
||||||
|
*
|
||||||
|
* Application layer build target
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#define REMOVE_OLD_STRING
|
||||||
|
|
||||||
|
#include "4coder_base_types.h"
|
||||||
|
#include "4coder_version.h"
|
||||||
|
#include "4coder_table.h"
|
||||||
|
#include "4coder_events.h"
|
||||||
|
#include "4coder_types.h"
|
||||||
|
#include "4coder_doc_content_types.h"
|
||||||
|
#include "4coder_default_colors.h"
|
||||||
|
#define STATIC_LINK_API
|
||||||
|
#include "generated/custom_api.h"
|
||||||
|
|
||||||
|
#include "4coder_string_match.h"
|
||||||
|
#include "4coder_token.h"
|
||||||
|
|
||||||
|
#include "4coder_system_types.h"
|
||||||
|
#define DYNAMIC_LINK_API
|
||||||
|
#include "generated/system_api.h"
|
||||||
|
#include "4ed_font_interface.h"
|
||||||
|
#define DYNAMIC_LINK_API
|
||||||
|
#include "generated/graphics_api.h"
|
||||||
|
#define DYNAMIC_LINK_API
|
||||||
|
#include "generated/font_api.h"
|
||||||
|
|
||||||
|
#include "4coder_profile.h"
|
||||||
|
#include "4coder_command_map.h"
|
||||||
|
|
||||||
|
#include "4ed_render_target.h"
|
||||||
|
#include "4ed.h"
|
||||||
|
#include "4ed_buffer_model.h"
|
||||||
|
#include "4ed_coroutine.h"
|
||||||
|
|
||||||
|
#include "4ed_dynamic_variables.h"
|
||||||
|
|
||||||
|
#include "4ed_buffer_model.h"
|
||||||
|
#include "4ed_translation.h"
|
||||||
|
#include "4ed_buffer.h"
|
||||||
|
#include "4ed_history.h"
|
||||||
|
#include "4ed_file.h"
|
||||||
|
|
||||||
|
#include "4ed_working_set.h"
|
||||||
|
#include "4ed_hot_directory.h"
|
||||||
|
#include "4ed_cli.h"
|
||||||
|
#include "4ed_layout.h"
|
||||||
|
#include "4ed_view.h"
|
||||||
|
#include "4ed_edit.h"
|
||||||
|
#include "4ed_text_layout.h"
|
||||||
|
#include "4ed_font_set.h"
|
||||||
|
#include "4ed_log.h"
|
||||||
|
#include "4ed_app_models.h"
|
||||||
|
|
||||||
|
#include "generated/lexer_cpp.h"
|
||||||
|
#include "4ed_api_definition.h"
|
||||||
|
#include "docs/4ed_doc_helper.h"
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
#include "4coder_base_types.cpp"
|
||||||
|
#include "4coder_layout.cpp"
|
||||||
|
#include "4coder_string_match.cpp"
|
||||||
|
#include "4coder_stringf.cpp"
|
||||||
|
#include "4coder_events.cpp"
|
||||||
|
#include "4coder_system_helpers.cpp"
|
||||||
|
#include "4coder_app_links_allocator.cpp"
|
||||||
|
#include "4coder_system_allocator.cpp"
|
||||||
|
#include "4coder_profile.cpp"
|
||||||
|
#include "4coder_profile_static_enable.cpp"
|
||||||
|
#include "4coder_hash_functions.cpp"
|
||||||
|
#include "4coder_table.cpp"
|
||||||
|
#include "4coder_log.cpp"
|
||||||
|
#include "4coder_buffer_seek_constructors.cpp"
|
||||||
|
#include "4coder_command_map.cpp"
|
||||||
|
#include "4coder_codepoint_map.cpp"
|
||||||
|
|
||||||
|
#include "generated/custom_api.cpp"
|
||||||
|
#define DYNAMIC_LINK_API
|
||||||
|
#include "generated/system_api.cpp"
|
||||||
|
#define DYNAMIC_LINK_API
|
||||||
|
#include "generated/graphics_api.cpp"
|
||||||
|
#define DYNAMIC_LINK_API
|
||||||
|
#include "generated/font_api.cpp"
|
||||||
|
|
||||||
|
#include "4coder_token.cpp"
|
||||||
|
#include "generated/lexer_cpp.cpp"
|
||||||
|
|
||||||
|
#include "4ed_api_definition.cpp"
|
||||||
|
#include "generated/custom_api_constructor.cpp"
|
||||||
|
#include "4ed_api_parser.cpp"
|
||||||
|
#include "4coder_doc_content_types.cpp"
|
||||||
|
#include "docs/4ed_doc_helper.cpp"
|
||||||
|
#include "docs/4ed_doc_custom_api.cpp"
|
||||||
|
|
||||||
|
#include "4ed_log.cpp"
|
||||||
|
#include "4ed_coroutine.cpp"
|
||||||
|
#include "4ed_mem.cpp"
|
||||||
|
#include "4ed_dynamic_variables.cpp"
|
||||||
|
#include "4ed_font_set.cpp"
|
||||||
|
#include "4ed_translation.cpp"
|
||||||
|
#include "4ed_render_target.cpp"
|
||||||
|
#include "4ed_app_models.cpp"
|
||||||
|
#include "4ed_buffer.cpp"
|
||||||
|
#include "4ed_string_matching.cpp"
|
||||||
|
#include "4ed_history.cpp"
|
||||||
|
#include "4ed_file.cpp"
|
||||||
|
#include "4ed_working_set.cpp"
|
||||||
|
#include "4ed_hot_directory.cpp"
|
||||||
|
#include "4ed_cli.cpp"
|
||||||
|
#include "4ed_layout.cpp"
|
||||||
|
#include "4ed_view.cpp"
|
||||||
|
#include "4ed_edit.cpp"
|
||||||
|
#include "4ed_text_layout.cpp"
|
||||||
|
#include "4ed_api_implementation.cpp"
|
||||||
|
#include "4ed.cpp"
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,721 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 06.01.2017
|
||||||
|
*
|
||||||
|
* The 4coder base buffer data structure.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
//
|
||||||
|
// Buffer low level operations
|
||||||
|
//
|
||||||
|
|
||||||
|
internal void
|
||||||
|
write_cursor_with_index(Cursor_With_Index *positions, i32 *count, i64 pos){
|
||||||
|
positions[*count].index = *count;
|
||||||
|
positions[*count].pos = pos;
|
||||||
|
++(*count);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
buffer_quick_sort_cursors(Cursor_With_Index *positions, i32 first, i32 one_past_last){
|
||||||
|
if (first + 1 < one_past_last){
|
||||||
|
i32 pivot = one_past_last - 1;
|
||||||
|
i64 pivot_pos = positions[pivot].pos;
|
||||||
|
i32 j = first;
|
||||||
|
for (i32 i = first; i < pivot; i += 1){
|
||||||
|
i64 pos = positions[i].pos;
|
||||||
|
if (pos < pivot_pos){
|
||||||
|
Swap(Cursor_With_Index, positions[j], positions[i]);
|
||||||
|
j += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Swap(Cursor_With_Index, positions[j], positions[pivot]);
|
||||||
|
buffer_quick_sort_cursors(positions, first, j);
|
||||||
|
buffer_quick_sort_cursors(positions, j + 1, one_past_last);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
buffer_sort_cursors(Cursor_With_Index *positions, i32 count){
|
||||||
|
if (count > 0){
|
||||||
|
buffer_quick_sort_cursors(positions, 0, count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
buffer_unsort_cursors(Cursor_With_Index *positions, i32 count){
|
||||||
|
if (count > 0){
|
||||||
|
i32 i = 0;
|
||||||
|
for (;;){
|
||||||
|
if (positions[i].index == i){
|
||||||
|
i += 1;
|
||||||
|
if (i >= count){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
i32 j = positions[i].index;
|
||||||
|
Swap(Cursor_With_Index, positions[i], positions[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
function void
|
||||||
|
buffer_sort_batch(Edit *batch, i32 first, i32 one_past_last){
|
||||||
|
if (first + 1 < one_past_last){
|
||||||
|
i32 pivot = one_past_last - 1;
|
||||||
|
i64 pivot_pos = batch[pivot].range.first;
|
||||||
|
i32 j = first;
|
||||||
|
for (i32 i = first; i < pivot; i += 1){
|
||||||
|
i64 pos = batch[i].range.first;
|
||||||
|
if (pos < pivot_pos){
|
||||||
|
Swap(Edit, batch[j], batch[i]);
|
||||||
|
j += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Swap(Edit, batch[j], batch[pivot]);
|
||||||
|
buffer_sort_batch(batch, first, j);
|
||||||
|
buffer_sort_batch(batch, j + 1, one_past_last);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Edit_Array
|
||||||
|
buffer_batch_array_from_linked_list(Arena *arena, Batch_Edit *batch, i32 count){
|
||||||
|
Edit_Array result = {};
|
||||||
|
result.count = count;
|
||||||
|
result.vals = push_array(arena, Edit, count);
|
||||||
|
i32 counter = 0;
|
||||||
|
for (Batch_Edit *node = batch;
|
||||||
|
counter < count && node != 0;
|
||||||
|
node = node->next){
|
||||||
|
result.vals[counter] = node->edit;
|
||||||
|
counter += 1;
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Edit_Array
|
||||||
|
buffer_sort_batch(Arena *arena, Batch_Edit *batch, i32 count){
|
||||||
|
Edit_Array result = buffer_batch_array_from_linked_list(arena, batch, count);
|
||||||
|
buffer_sort_batch(result.vals, 0, result.count);
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
internal void
|
||||||
|
buffer_update_cursors_lean_l(Cursor_With_Index *sorted_positions, i32 count,
|
||||||
|
Batch_Edit *batch){
|
||||||
|
Cursor_With_Index *pos = sorted_positions;
|
||||||
|
Cursor_With_Index *end_pos = sorted_positions + count;
|
||||||
|
i64 shift_amount = 0;
|
||||||
|
for (; batch != 0 && pos < end_pos;
|
||||||
|
batch = batch->next){
|
||||||
|
Range_i64 range = batch->edit.range;
|
||||||
|
i64 len = batch->edit.text.size;
|
||||||
|
if (shift_amount != 0){
|
||||||
|
for (;pos < end_pos && pos->pos < range.first; pos += 1){
|
||||||
|
pos->pos += shift_amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
for (;pos < end_pos && pos->pos < range.first; pos += 1);
|
||||||
|
}
|
||||||
|
i64 new_pos = range.first + shift_amount;
|
||||||
|
for (;pos < end_pos && pos->pos <= range.one_past_last; pos += 1){
|
||||||
|
pos->pos = new_pos;
|
||||||
|
}
|
||||||
|
shift_amount += len - (range.one_past_last - range.first);
|
||||||
|
}
|
||||||
|
if (shift_amount != 0){
|
||||||
|
for (;pos < end_pos; pos += 1){
|
||||||
|
pos->pos += shift_amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
buffer_update_cursors_lean_r(Cursor_With_Index *sorted_positions, i32 count,
|
||||||
|
Batch_Edit *batch){
|
||||||
|
Cursor_With_Index *pos = sorted_positions;
|
||||||
|
Cursor_With_Index *end_pos = sorted_positions + count;
|
||||||
|
i64 shift_amount = 0;
|
||||||
|
for (; batch != 0 && pos < end_pos;
|
||||||
|
batch = batch->next){
|
||||||
|
Range_i64 range = batch->edit.range;
|
||||||
|
i64 len = batch->edit.text.size;
|
||||||
|
if (shift_amount != 0){
|
||||||
|
for (;pos < end_pos && pos->pos < range.first; pos += 1){
|
||||||
|
pos->pos += shift_amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
for (;pos < end_pos && pos->pos < range.first; pos += 1);
|
||||||
|
}
|
||||||
|
i64 new_pos = range.first + len + shift_amount;
|
||||||
|
for (;pos < end_pos && pos->pos < range.one_past_last; pos += 1){
|
||||||
|
pos->pos = new_pos;
|
||||||
|
}
|
||||||
|
shift_amount += len - (range.one_past_last - range.first);
|
||||||
|
}
|
||||||
|
if (shift_amount != 0){
|
||||||
|
for (;pos < end_pos; pos += 1){
|
||||||
|
pos->pos += shift_amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////
|
||||||
|
|
||||||
|
internal b32
|
||||||
|
buffer_good(Gap_Buffer *buffer){
|
||||||
|
return(buffer->data != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal i64
|
||||||
|
buffer_size(Gap_Buffer *buffer){
|
||||||
|
return(buffer->size1 + buffer->size2);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal i64
|
||||||
|
buffer_line_count(Gap_Buffer *buffer){
|
||||||
|
return(buffer->line_start_count - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
buffer_init(Gap_Buffer *buffer, u8 *data, u64 size, Base_Allocator *allocator){
|
||||||
|
block_zero_struct(buffer);
|
||||||
|
|
||||||
|
buffer->allocator = allocator;
|
||||||
|
|
||||||
|
u64 capacity = round_up_u64(size*2, KB(4));
|
||||||
|
String_Const_u8 memory = base_allocate(allocator, capacity);
|
||||||
|
buffer->data = (u8*)memory.str;
|
||||||
|
buffer->size1 = size/2;
|
||||||
|
buffer->gap_size = capacity - size;
|
||||||
|
buffer->size2 = size - buffer->size1;
|
||||||
|
buffer->max = capacity;
|
||||||
|
|
||||||
|
block_copy(buffer->data, data, buffer->size1);
|
||||||
|
block_copy(buffer->data + buffer->size1 + buffer->gap_size, data + buffer->size1, buffer->size2);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal b32
|
||||||
|
buffer_replace_range(Gap_Buffer *buffer, Range_i64 range, String_Const_u8 text, i64 shift_amount){
|
||||||
|
i64 size = buffer_size(buffer);
|
||||||
|
Assert(0 <= range.start);
|
||||||
|
Assert(range.start <= range.end);
|
||||||
|
Assert(range.end <= size);
|
||||||
|
|
||||||
|
if (shift_amount + size > buffer->max){
|
||||||
|
i64 new_max = round_up_i64(2*(shift_amount + size), KB(4));
|
||||||
|
i64 new_gap_size = new_max - size;
|
||||||
|
String_Const_u8 new_memory_data = base_allocate(buffer->allocator, new_max);
|
||||||
|
u8 *new_memory = (u8*)new_memory_data.str;
|
||||||
|
block_copy(new_memory, buffer->data, buffer->size1);
|
||||||
|
block_copy(new_memory + buffer->size1 + new_gap_size, buffer->data + buffer->size1 + buffer->gap_size,
|
||||||
|
buffer->size2);
|
||||||
|
base_free(buffer->allocator, buffer->data);
|
||||||
|
buffer->data = new_memory;
|
||||||
|
buffer->gap_size = new_gap_size;
|
||||||
|
buffer->max = new_max;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert(shift_amount + size <= buffer->max);
|
||||||
|
|
||||||
|
b32 result = false;
|
||||||
|
|
||||||
|
if (range.end < buffer->size1){
|
||||||
|
i64 move_size = buffer->size1 - range.end;
|
||||||
|
block_copy(buffer->data + buffer->size1 + buffer->gap_size - move_size,
|
||||||
|
buffer->data + range.end,
|
||||||
|
move_size);
|
||||||
|
buffer->size1 -= move_size;
|
||||||
|
buffer->size2 += move_size;
|
||||||
|
}
|
||||||
|
if (range.start > buffer->size1){
|
||||||
|
i64 move_size = range.start - buffer->size1;
|
||||||
|
block_copy(buffer->data + buffer->size1,
|
||||||
|
buffer->data + buffer->size1 + buffer->gap_size,
|
||||||
|
move_size);
|
||||||
|
buffer->size1 += move_size;
|
||||||
|
buffer->size2 -= move_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
block_copy(buffer->data + range.start, text.str, text.size);
|
||||||
|
buffer->size2 = size - range.end;
|
||||||
|
buffer->size1 = range.start + text.size;
|
||||||
|
buffer->gap_size -= shift_amount;
|
||||||
|
|
||||||
|
Assert(buffer->size1 + buffer->size2 == size + shift_amount);
|
||||||
|
Assert(buffer->size1 + buffer->gap_size + buffer->size2 == buffer->max);
|
||||||
|
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
internal List_String_Const_u8
|
||||||
|
buffer_get_chunks(Arena *arena, Gap_Buffer *buffer){
|
||||||
|
List_String_Const_u8 list = {};
|
||||||
|
if (buffer->size1 > 0){
|
||||||
|
string_list_push(arena, &list, SCu8(buffer->data, buffer->size1));
|
||||||
|
}
|
||||||
|
if (buffer->size2 > 0){
|
||||||
|
u64 gap_2_pos = buffer->size1 + buffer->gap_size;
|
||||||
|
string_list_push(arena, &list, SCu8(buffer->data + gap_2_pos, buffer->size2));
|
||||||
|
}
|
||||||
|
return(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
buffer_chunks_clamp(List_String_Const_u8 *chunks, Range_i64 range){
|
||||||
|
i64 p = 0;
|
||||||
|
List_String_Const_u8 list = {};
|
||||||
|
for (Node_String_Const_u8 *node = chunks->first, *next = 0;
|
||||||
|
node != 0;
|
||||||
|
node = next){
|
||||||
|
next = node->next;
|
||||||
|
Range_i64 node_range = Ii64(p, p + node->string.size);
|
||||||
|
if (range_overlap(range, node_range)){
|
||||||
|
i64 first = Max(node_range.first, range.first) - node_range.first;
|
||||||
|
i64 one_past_last = Min(node_range.one_past_last, range.one_past_last) - node_range.first;
|
||||||
|
String_Const_u8 s = string_prefix(node->string, one_past_last);
|
||||||
|
node->string = string_skip(s, first);
|
||||||
|
sll_queue_push(list.first, list.last, node);
|
||||||
|
list.total_size += node->string.size;
|
||||||
|
list.node_count += 1;
|
||||||
|
}
|
||||||
|
p = node_range.one_past_last;
|
||||||
|
}
|
||||||
|
*chunks = list;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal String_Const_u8
|
||||||
|
buffer_stringify(Arena *arena, Gap_Buffer *buffer, Range_i64 range){
|
||||||
|
List_String_Const_u8 list = buffer_get_chunks(arena, buffer);
|
||||||
|
buffer_chunks_clamp(&list, range);
|
||||||
|
return(string_list_flatten(arena, list, StringFill_NullTerminate));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal String_Const_u8
|
||||||
|
buffer_eol_convert_out(Arena *arena, Gap_Buffer *buffer, Range_i64 range){
|
||||||
|
List_String_Const_u8 list = buffer_get_chunks(arena, buffer);
|
||||||
|
buffer_chunks_clamp(&list, range);
|
||||||
|
u64 cap = list.total_size*2;
|
||||||
|
u8 *memory = push_array(arena, u8, cap);
|
||||||
|
u8 *memory_opl = memory + cap;
|
||||||
|
u8 *ptr = memory;
|
||||||
|
for (Node_String_Const_u8 *node = list.first;
|
||||||
|
node != 0;
|
||||||
|
node = node->next){
|
||||||
|
u8 *byte = node->string.str;
|
||||||
|
u8 *byte_opl = byte + node->string.size;
|
||||||
|
for (;byte < byte_opl; byte += 1){
|
||||||
|
if (*byte == '\n'){
|
||||||
|
*ptr = '\r';
|
||||||
|
ptr += 1;
|
||||||
|
*ptr = '\n';
|
||||||
|
ptr += 1;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
*ptr = *byte;
|
||||||
|
ptr += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
linalloc_pop(arena, (memory_opl - ptr));
|
||||||
|
push_align(arena, 8);
|
||||||
|
return(SCu8(memory, ptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
internal i64
|
||||||
|
buffer_count_newlines(Arena *scratch, Gap_Buffer *buffer, i64 start, i64 end){
|
||||||
|
Temp_Memory temp = begin_temp(scratch);
|
||||||
|
List_String_Const_u8 list = buffer_get_chunks(scratch, buffer);
|
||||||
|
buffer_chunks_clamp(&list, Ii64(start, end));
|
||||||
|
|
||||||
|
i64 count = 0;
|
||||||
|
for (Node_String_Const_u8 *node = list.first;
|
||||||
|
node != 0;
|
||||||
|
node = node->next){
|
||||||
|
u8 *byte = node->string.str;
|
||||||
|
u8 *byte_opl = byte + node->string.size;
|
||||||
|
for (;byte < byte_opl; byte += 1){
|
||||||
|
if (*byte == '\n'){
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
end_temp(temp);
|
||||||
|
|
||||||
|
return(count);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
internal void
|
||||||
|
buffer_starts__ensure_max_size(Gap_Buffer *buffer, i64 max_size){
|
||||||
|
if (max_size > buffer->line_start_max){
|
||||||
|
i64 new_max = round_up_i64(max_size*2, KB(1));
|
||||||
|
String_Const_u8 memory = base_allocate(buffer->allocator, sizeof(*buffer->line_starts)*new_max);
|
||||||
|
i64 *new_line_starts = (i64*)memory.str;
|
||||||
|
block_copy_dynamic_array(new_line_starts, buffer->line_starts, buffer->line_start_count);
|
||||||
|
buffer->line_start_max = new_max;
|
||||||
|
base_free(buffer->allocator, buffer->line_starts);
|
||||||
|
buffer->line_starts = new_line_starts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
buffer_measure_starts__write(Gap_Buffer *buffer, i64 pos){
|
||||||
|
buffer_starts__ensure_max_size(buffer, buffer->line_start_count + 1);
|
||||||
|
buffer->line_starts[buffer->line_start_count] = pos;
|
||||||
|
buffer->line_start_count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
buffer_measure_starts(Arena *scratch, Gap_Buffer *buffer){
|
||||||
|
Temp_Memory temp = begin_temp(scratch);
|
||||||
|
List_String_Const_u8 list = buffer_get_chunks(scratch, buffer);
|
||||||
|
buffer->line_start_count = 0;
|
||||||
|
buffer_measure_starts__write(buffer, 0);
|
||||||
|
i64 index = 0;
|
||||||
|
for (Node_String_Const_u8 *node = list.first;
|
||||||
|
node != 0;
|
||||||
|
node = node->next){
|
||||||
|
u8 *byte = node->string.str;
|
||||||
|
u8 *byte_opl = byte + node->string.size;
|
||||||
|
for (;byte < byte_opl; byte += 1){
|
||||||
|
index += 1;
|
||||||
|
if (*byte == '\n'){
|
||||||
|
buffer_measure_starts__write(buffer, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffer_measure_starts__write(buffer, buffer_size(buffer));
|
||||||
|
end_temp(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal i64
|
||||||
|
buffer_get_line_index(Gap_Buffer *buffer, i64 pos){
|
||||||
|
i64 i = 0;
|
||||||
|
if (buffer->line_start_count > 2){
|
||||||
|
i64 start = 0;
|
||||||
|
i64 one_past_last = buffer->line_start_count - 1;
|
||||||
|
i64 *array = buffer->line_starts;
|
||||||
|
pos = clamp_bot(0, pos);
|
||||||
|
for (;;){
|
||||||
|
i = (start + one_past_last) >> 1;
|
||||||
|
if (array[i] < pos){
|
||||||
|
start = i;
|
||||||
|
}
|
||||||
|
else if (array[i] > pos){
|
||||||
|
one_past_last = i;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (start + 1 >= one_past_last){
|
||||||
|
i = start;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
Line_Move*
|
||||||
|
push_line_move(Arena *arena, Line_Move *moves, i64 new_line_first,
|
||||||
|
i64 old_line_first, i64 old_line_opl, i64 text_shift){
|
||||||
|
Line_Move *move = push_array(arena, Line_Move, 1);
|
||||||
|
move->next = moves;
|
||||||
|
move->kind = LineMove_ShiftOldValues;
|
||||||
|
move->new_line_first = new_line_first;
|
||||||
|
move->old_line_first = old_line_first;
|
||||||
|
move->old_line_opl = old_line_opl;
|
||||||
|
move->text_shift = text_shift;
|
||||||
|
return(move);
|
||||||
|
}
|
||||||
|
|
||||||
|
Line_Move*
|
||||||
|
push_line_move(Arena *arena, Line_Move *moves, i64 new_line_first,
|
||||||
|
String_Const_u8 string, i64 text_base){
|
||||||
|
Line_Move *move = push_array(arena, Line_Move, 1);
|
||||||
|
move->next = moves;
|
||||||
|
move->kind = LineMove_MeasureString;
|
||||||
|
move->new_line_first = new_line_first;
|
||||||
|
move->string = string;
|
||||||
|
move->text_base = text_base;
|
||||||
|
return(move);
|
||||||
|
}
|
||||||
|
|
||||||
|
function i64
|
||||||
|
count_lines(String_Const_u8 string){
|
||||||
|
i64 result = 0;
|
||||||
|
for (u64 i = 0; i < string.size; i += 1){
|
||||||
|
if (string.str[i] == '\n'){
|
||||||
|
result += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
fill_line_starts(i64 *lines_starts, String_Const_u8 string, i64 text_base){
|
||||||
|
i64 *ptr = lines_starts;
|
||||||
|
for (u64 i = 0; i < string.size; i += 1){
|
||||||
|
if (string.str[i] == '\n'){
|
||||||
|
*ptr = text_base + i + 1;
|
||||||
|
ptr += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
buffer_remeasure_starts(Thread_Context *tctx, Gap_Buffer *buffer, Batch_Edit *batch){
|
||||||
|
Scratch_Block scratch(tctx);
|
||||||
|
|
||||||
|
i64 line_start_count = buffer_line_count(buffer) + 1;
|
||||||
|
|
||||||
|
Line_Move *moves = 0;
|
||||||
|
i64 current_line = 0;
|
||||||
|
i64 text_shift = 0;
|
||||||
|
i64 line_shift = 0;
|
||||||
|
for (Batch_Edit *node = batch;
|
||||||
|
node != 0;
|
||||||
|
node = node->next){
|
||||||
|
i64 first_line = buffer_get_line_index(buffer, node->edit.range.first);
|
||||||
|
i64 opl_line = buffer_get_line_index(buffer, node->edit.range.one_past_last);
|
||||||
|
i64 new_line_count = count_lines(node->edit.text);
|
||||||
|
i64 deleted_line_count = opl_line - first_line;
|
||||||
|
|
||||||
|
Assert(first_line <= opl_line);
|
||||||
|
Assert(opl_line <= line_start_count);
|
||||||
|
|
||||||
|
if (current_line <= first_line &&
|
||||||
|
(text_shift != 0 || line_shift != 0)){
|
||||||
|
moves = push_line_move(scratch, moves, current_line + line_shift,
|
||||||
|
current_line, first_line + 1, text_shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_line_count != 0){
|
||||||
|
moves = push_line_move(scratch, moves, first_line + 1 + line_shift,
|
||||||
|
node->edit.text, node->edit.range.first + text_shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
text_shift += node->edit.text.size - range_size(node->edit.range);
|
||||||
|
line_shift += new_line_count - deleted_line_count;
|
||||||
|
current_line = opl_line + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
moves = push_line_move(scratch, moves, current_line + line_shift,
|
||||||
|
current_line, line_start_count, text_shift);
|
||||||
|
line_start_count = line_start_count + line_shift;
|
||||||
|
|
||||||
|
buffer_starts__ensure_max_size(buffer, line_start_count + 1);
|
||||||
|
buffer->line_start_count = line_start_count;
|
||||||
|
|
||||||
|
i64 *array = buffer->line_starts;
|
||||||
|
|
||||||
|
for (Line_Move *node = moves;
|
||||||
|
node != 0;
|
||||||
|
node = node->next){
|
||||||
|
if (node->kind == LineMove_ShiftOldValues){
|
||||||
|
i64 line_index_shift = node->new_line_first - node->old_line_first;
|
||||||
|
i64 move_text_shift = node->text_shift;
|
||||||
|
if (line_index_shift > 0){
|
||||||
|
for (i64 i = node->old_line_opl - 1;
|
||||||
|
i >= node->old_line_first;
|
||||||
|
i -= 1){
|
||||||
|
array[i + line_index_shift] = array[i] + move_text_shift;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
for (i64 i = node->old_line_first;
|
||||||
|
i < node->old_line_opl;
|
||||||
|
i += 1){
|
||||||
|
array[i + line_index_shift] = array[i] + move_text_shift;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Line_Move *node = moves;
|
||||||
|
node != 0;
|
||||||
|
node = node->next){
|
||||||
|
if (node->kind == LineMove_MeasureString){
|
||||||
|
fill_line_starts(array + node->new_line_first, node->string, node->text_base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Range_i64
|
||||||
|
buffer_get_pos_range_from_line_number(Gap_Buffer *buffer, i64 line_number){
|
||||||
|
Range_i64 result = {};
|
||||||
|
if (1 <= line_number && line_number < buffer->line_start_count){
|
||||||
|
result.first = buffer->line_starts[line_number - 1];
|
||||||
|
result.one_past_last = buffer->line_starts[line_number];
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal i64
|
||||||
|
buffer_get_first_pos_from_line_number(Gap_Buffer *buffer, i64 line_number){
|
||||||
|
i64 result = 0;
|
||||||
|
if (line_number < 1){
|
||||||
|
result = 0;
|
||||||
|
}
|
||||||
|
else if (line_number >= buffer->line_start_count){
|
||||||
|
result = buffer_size(buffer);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
result = buffer->line_starts[line_number - 1];
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal i64
|
||||||
|
buffer_get_last_pos_from_line_number(Gap_Buffer *buffer, i64 line_number){
|
||||||
|
i64 result = 0;
|
||||||
|
if (line_number < 1){
|
||||||
|
result = 0;
|
||||||
|
}
|
||||||
|
else if (line_number >= buffer->line_start_count - 1){
|
||||||
|
result = buffer_size(buffer);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
result = buffer->line_starts[line_number] - 1;
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Buffer_Cursor
|
||||||
|
buffer_cursor_from_pos(Gap_Buffer *buffer, i64 pos){
|
||||||
|
i64 size = buffer_size(buffer);
|
||||||
|
pos = clamp(0, pos, size);
|
||||||
|
i64 line_index = buffer_get_line_index(buffer, pos);
|
||||||
|
|
||||||
|
Buffer_Cursor result = {};
|
||||||
|
result.pos = pos;
|
||||||
|
result.line = line_index + 1;
|
||||||
|
result.col = pos - buffer->line_starts[line_index] + 1;
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Buffer_Cursor
|
||||||
|
buffer_cursor_from_line_col(Gap_Buffer *buffer, i64 line, i64 col){
|
||||||
|
i64 size = buffer_size(buffer);
|
||||||
|
i64 line_index = line - 1;
|
||||||
|
i64 line_count = buffer_line_count(buffer);
|
||||||
|
line_index = clamp(0, line_index, line_count - 1);
|
||||||
|
|
||||||
|
i64 this_start = buffer->line_starts[line_index];
|
||||||
|
i64 max_col = (buffer->line_starts[line_index + 1] - this_start);
|
||||||
|
if (line_index + 1 == line_count){
|
||||||
|
max_col += 1;
|
||||||
|
}
|
||||||
|
max_col = clamp_bot(1, max_col);
|
||||||
|
|
||||||
|
if (col < 0){
|
||||||
|
if (-col > max_col){
|
||||||
|
col = 1;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
col = max_col + col + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (col == 0){
|
||||||
|
col = 1;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
col = clamp_top(col, max_col);
|
||||||
|
}
|
||||||
|
Assert(col > 0);
|
||||||
|
i64 adjusted_pos = col - 1;
|
||||||
|
|
||||||
|
i64 pos = this_start + adjusted_pos;
|
||||||
|
|
||||||
|
Buffer_Cursor result = {};
|
||||||
|
result.pos = pos;
|
||||||
|
result.line = line_index + 1;
|
||||||
|
result.col = col;
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal String_Const_u8
|
||||||
|
buffer_invert_edit_shift(Arena *arena, Gap_Buffer *buffer, Edit edit, Edit *inv, i64 shift_amount){
|
||||||
|
String_Const_u8 string = buffer_stringify(arena, buffer, edit.range);
|
||||||
|
inv->text = string;
|
||||||
|
inv->range = Ii64(edit.range.start + shift_amount, edit.range.start + edit.text.size + shift_amount);
|
||||||
|
return(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal b32
|
||||||
|
buffer_invert_batch(Arena *arena, Gap_Buffer *buffer, Edit *edits, Edit *inverse, i64 count){
|
||||||
|
b32 result = false;
|
||||||
|
i64 pos = 0;
|
||||||
|
i64 shift_amount = 0;
|
||||||
|
Edit *edit = edits;
|
||||||
|
Edit *inv_edit = inverse;
|
||||||
|
for (i64 i = 0; i < count; i += 1, edit += 1, inv_edit += 1){
|
||||||
|
String_Const_u8 inv_str = buffer_invert_edit_shift(arena, buffer, *edit, inv_edit, shift_amount);
|
||||||
|
shift_amount += replace_range_shift(edit->range, edit->text.size);
|
||||||
|
pos += inv_str.size;
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Buffer_Chunk_Position
|
||||||
|
buffer_get_chunk_position(String_Const_u8_Array chunks, i64 buffer_size, i64 real_pos){
|
||||||
|
Buffer_Chunk_Position pos = {};
|
||||||
|
pos.real_pos = real_pos;
|
||||||
|
pos.chunk_pos = real_pos;
|
||||||
|
if (pos.real_pos != buffer_size){
|
||||||
|
for (;(i64)(chunks.vals[pos.chunk_index].size) <= pos.chunk_pos;){
|
||||||
|
Assert(pos.chunk_index < chunks.count);
|
||||||
|
pos.chunk_pos -= (i32)chunks.vals[pos.chunk_index].size;
|
||||||
|
pos.chunk_index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
pos.chunk_index = chunks.count - 1;
|
||||||
|
pos.chunk_pos = (i32)chunks.vals[pos.chunk_index].size;
|
||||||
|
}
|
||||||
|
return(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal i32
|
||||||
|
buffer_chunk_position_iterate(String_Const_u8_Array chunks, Buffer_Chunk_Position *pos, Scan_Direction direction){
|
||||||
|
i32 past_end = 0;
|
||||||
|
pos->real_pos += direction;
|
||||||
|
pos->chunk_pos += direction;
|
||||||
|
if (pos->chunk_pos < 0){
|
||||||
|
if (pos->chunk_index == 0){
|
||||||
|
past_end = -1;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
pos->chunk_index -= 1;
|
||||||
|
pos->chunk_pos = (i32)chunks.vals[pos->chunk_index].size - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (pos->chunk_pos >= (i64)(chunks.vals[pos->chunk_index].size)){
|
||||||
|
pos->chunk_index += 1;
|
||||||
|
if (pos->chunk_index == chunks.count){
|
||||||
|
past_end = 1;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
pos->chunk_pos = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(past_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 24.01.2018
|
||||||
|
*
|
||||||
|
* Buffer types
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#if !defined(FRED_BUFFER_H)
|
||||||
|
#define FRED_BUFFER_H
|
||||||
|
|
||||||
|
struct Cursor_With_Index{
|
||||||
|
i64 pos;
|
||||||
|
i32 index;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Gap_Buffer{
|
||||||
|
Base_Allocator *allocator;
|
||||||
|
|
||||||
|
u8 *data;
|
||||||
|
i64 size1;
|
||||||
|
i64 gap_size;
|
||||||
|
i64 size2;
|
||||||
|
i64 max;
|
||||||
|
|
||||||
|
// NOTE(allen): If there are N lines I store N + 1 slots in this array with
|
||||||
|
// line_starts[N] = size of the buffer.
|
||||||
|
// The variable line_start_count stores N + 1; call buffer_line_count(buffer)
|
||||||
|
// to get "N" the actual number of lines.
|
||||||
|
i64 *line_starts;
|
||||||
|
i64 line_start_count;
|
||||||
|
i64 line_start_max;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Buffer_Chunk_Position{
|
||||||
|
i64 real_pos;
|
||||||
|
i64 chunk_pos;
|
||||||
|
i64 chunk_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef i32 Line_Move_Kind;
|
||||||
|
enum{
|
||||||
|
LineMove_ShiftOldValues,
|
||||||
|
LineMove_MeasureString,
|
||||||
|
};
|
||||||
|
struct Line_Move{
|
||||||
|
Line_Move *next;
|
||||||
|
Line_Move_Kind kind;
|
||||||
|
i64 new_line_first;
|
||||||
|
union{
|
||||||
|
struct{
|
||||||
|
i64 old_line_first;
|
||||||
|
i64 old_line_opl;
|
||||||
|
i64 text_shift;
|
||||||
|
};
|
||||||
|
struct{
|
||||||
|
String_Const_u8 string;
|
||||||
|
i64 text_base;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// BOTTOM
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 18.03.2017
|
||||||
|
*
|
||||||
|
* Abstract model for the describing the characters of a buffer.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#if !defined(FRED_BUFFER_MODEL_H)
|
||||||
|
#define FRED_BUFFER_MODEL_H
|
||||||
|
|
||||||
|
struct Buffer_Model_Step{
|
||||||
|
u32 type;
|
||||||
|
u32 value;
|
||||||
|
i32 i;
|
||||||
|
u32 byte_length;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Buffer_Model_Behavior{
|
||||||
|
b32 do_newline;
|
||||||
|
b32 do_codepoint_advance;
|
||||||
|
b32 do_number_advance;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum{
|
||||||
|
BufferModelUnit_None,
|
||||||
|
BufferModelUnit_Codepoint,
|
||||||
|
BufferModelUnit_Numbers,
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,158 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 17.07.2017
|
||||||
|
*
|
||||||
|
* CLI handling code.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
internal void
|
||||||
|
child_process_container_init(Base_Allocator *allocator, Child_Process_Container *container){
|
||||||
|
container->arena = make_arena(allocator);
|
||||||
|
dll_init_sentinel(&container->child_process_active_list);
|
||||||
|
dll_init_sentinel(&container->child_process_free_list);
|
||||||
|
container->active_child_process_count = 0;
|
||||||
|
container->child_process_id_counter = 0;
|
||||||
|
container->id_to_ptr_table = make_table_u64_u64(allocator, 10);
|
||||||
|
container->id_to_return_code_table = make_table_u64_u64(allocator, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
child_process_container_release(Child_Process_Container *container, Models *models){
|
||||||
|
linalloc_clear(&container->arena);
|
||||||
|
table_free(&container->id_to_ptr_table);
|
||||||
|
block_zero_struct(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Child_Process_And_ID
|
||||||
|
child_process_alloc_new(Models *models, Child_Process_Container *container){
|
||||||
|
Child_Process_And_ID result = {};
|
||||||
|
Child_Process *new_process = 0;
|
||||||
|
if (container->child_process_free_list.next != &container->child_process_free_list){
|
||||||
|
Node *new_node = container->child_process_free_list.next;
|
||||||
|
dll_remove(new_node);
|
||||||
|
new_process = CastFromMember(Child_Process, node, new_node);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
new_process = push_array(&container->arena, Child_Process, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 new_id = ++container->child_process_id_counter;
|
||||||
|
block_zero_struct(new_process);
|
||||||
|
dll_insert_back(&container->child_process_active_list, &new_process->node);
|
||||||
|
new_process->id = new_id;
|
||||||
|
table_insert(&container->id_to_ptr_table, new_id, (u64)PtrAsInt(new_process));
|
||||||
|
container->active_child_process_count += 1;
|
||||||
|
|
||||||
|
result.process = new_process;
|
||||||
|
result.id = new_id;
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Child_Process*
|
||||||
|
child_process_from_id(Child_Process_Container *container, Child_Process_ID id){
|
||||||
|
Table_Lookup lookup = table_lookup(&container->id_to_ptr_table, id);
|
||||||
|
Child_Process *process = 0;
|
||||||
|
if (lookup.found_match){
|
||||||
|
u64 val = 0;
|
||||||
|
table_read(&container->id_to_ptr_table, lookup, &val);
|
||||||
|
process = (Child_Process*)IntAsPtr(val);
|
||||||
|
}
|
||||||
|
return(process);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal b32
|
||||||
|
child_process_free(Child_Process_Container *container, Child_Process_ID id){
|
||||||
|
b32 result = false;
|
||||||
|
Child_Process *process = child_process_from_id(container, id);
|
||||||
|
if (process != 0){
|
||||||
|
table_erase(&container->id_to_ptr_table, id);
|
||||||
|
dll_remove(&process->node);
|
||||||
|
dll_insert(&container->child_process_free_list, &process->node);
|
||||||
|
container->active_child_process_count -= 1;
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal b32
|
||||||
|
child_process_set_return_code(Models *models, Child_Process_Container *container, Child_Process_ID id, i64 val){
|
||||||
|
table_insert(&container->id_to_return_code_table, id, val);
|
||||||
|
return(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal b32
|
||||||
|
child_process_lookup_return_code(Child_Process_Container *container, Child_Process_ID id, i64 *out){
|
||||||
|
b32 result = false;
|
||||||
|
Table_Lookup lookup = table_lookup(&container->id_to_return_code_table, id);
|
||||||
|
if (lookup.found_match){
|
||||||
|
table_read(&container->id_to_return_code_table, lookup, (u64*)out);
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
internal b32
|
||||||
|
child_process_call(Thread_Context *tctx, Models *models, String_Const_u8 path, String_Const_u8 command, Child_Process_ID *id_out){
|
||||||
|
b32 result = false;
|
||||||
|
Scratch_Block scratch(tctx);
|
||||||
|
String_Const_u8 path_n = push_string_copy(scratch, path);
|
||||||
|
String_Const_u8 command_n = push_string_copy(scratch, command);
|
||||||
|
CLI_Handles cli_handles = {};
|
||||||
|
if (system_cli_call(scratch, (char*)path_n.str, (char*)command_n.str, &cli_handles)){
|
||||||
|
Child_Process_And_ID new_process = child_process_alloc_new(models, &models->child_processes);
|
||||||
|
*id_out = new_process.id;
|
||||||
|
new_process.process->cli = cli_handles;
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal b32
|
||||||
|
child_process_set_target_buffer(Models *models, Child_Process *child_process, Editing_File *file, Child_Process_Set_Target_Flags flags){
|
||||||
|
b32 result = false;
|
||||||
|
b32 fail_if_process_has_buffer = HasFlag(flags, ChildProcessSet_FailIfProcessAlreadyAttachedToABuffer);
|
||||||
|
b32 fail_if_buffer_has_process = HasFlag(flags, ChildProcessSet_FailIfBufferAlreadyAttachedToAProcess);
|
||||||
|
b32 process_has_buffer = (child_process->out_file != 0);
|
||||||
|
b32 buffer_has_process = (file->state.attached_child_process != 0);
|
||||||
|
b32 fail = ((process_has_buffer && fail_if_process_has_buffer) ||
|
||||||
|
(buffer_has_process && fail_if_buffer_has_process));
|
||||||
|
if (!fail){
|
||||||
|
if (process_has_buffer){
|
||||||
|
child_process->out_file->state.attached_child_process = 0;
|
||||||
|
}
|
||||||
|
if (buffer_has_process){
|
||||||
|
Child_Process *attached_child_process = child_process_from_id(&models->child_processes, file->state.attached_child_process);
|
||||||
|
if (attached_child_process != 0){
|
||||||
|
attached_child_process->out_file = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
child_process->out_file = file;
|
||||||
|
child_process->cursor_at_end = HasFlag(flags, ChildProcessSet_CursorAtEnd);
|
||||||
|
file->state.attached_child_process = child_process->id;
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Process_State
|
||||||
|
child_process_get_state(Child_Process_Container *child_processes, Child_Process_ID child_process_id){
|
||||||
|
Child_Process *child_process = child_process_from_id(child_processes, child_process_id);
|
||||||
|
Process_State result = {};
|
||||||
|
if (child_processes != 0){
|
||||||
|
result.valid = true;
|
||||||
|
result.is_updating = true;
|
||||||
|
}
|
||||||
|
else if (child_process_lookup_return_code(child_processes, child_process_id, &result.return_code)){
|
||||||
|
result.valid = true;
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 24.03.2018
|
||||||
|
*
|
||||||
|
* CLI handling code.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#if !defined(FRED_CLI_H)
|
||||||
|
#define FRED_CLI_H
|
||||||
|
|
||||||
|
struct Child_Process{
|
||||||
|
Node node;
|
||||||
|
Child_Process_ID id;
|
||||||
|
CLI_Handles cli;
|
||||||
|
Editing_File *out_file;
|
||||||
|
b32 cursor_at_end;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Child_Process_Container{
|
||||||
|
Arena arena;
|
||||||
|
Node child_process_active_list;
|
||||||
|
Node child_process_free_list;
|
||||||
|
i32 active_child_process_count;
|
||||||
|
u32 child_process_id_counter;
|
||||||
|
Table_u64_u64 id_to_ptr_table;
|
||||||
|
Table_u64_u64 id_to_return_code_table;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Child_Process_And_ID{
|
||||||
|
Child_Process *process;
|
||||||
|
Child_Process_ID id;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,164 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 19.07.2017
|
||||||
|
*
|
||||||
|
* Coroutine implementation from thread+mutex+cv
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
internal void
|
||||||
|
coroutine__pass_control(Coroutine *me, Coroutine *other,
|
||||||
|
Coroutine_State my_new_state, Coroutine_Pass_Control control){
|
||||||
|
Assert(me->state == CoroutineState_Active);
|
||||||
|
Assert(me->sys == other->sys);
|
||||||
|
|
||||||
|
me->state = my_new_state;
|
||||||
|
other->state = CoroutineState_Active;
|
||||||
|
me->sys->active = other;
|
||||||
|
system_condition_variable_signal(other->cv);
|
||||||
|
if (control == CoroutinePassControl_BlockMe){
|
||||||
|
for (;me->state != CoroutineState_Active;){
|
||||||
|
system_condition_variable_wait(me->cv, me->sys->lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
coroutine_main(void *ptr){
|
||||||
|
Coroutine *me = (Coroutine*)ptr;
|
||||||
|
|
||||||
|
Thread_Context_Extra_Info tctx_info = {};
|
||||||
|
tctx_info.coroutine = me;
|
||||||
|
|
||||||
|
Thread_Context tctx_ = {};
|
||||||
|
thread_ctx_init(&tctx_, ThreadKind_MainCoroutine,
|
||||||
|
get_base_allocator_system(), get_base_allocator_system());
|
||||||
|
tctx_.user_data = &tctx_info;
|
||||||
|
me->tctx = &tctx_;
|
||||||
|
|
||||||
|
// NOTE(allen): Init handshake
|
||||||
|
Assert(me->state == CoroutineState_Dead);
|
||||||
|
system_mutex_acquire(me->sys->lock);
|
||||||
|
me->sys->did_init = true;
|
||||||
|
system_condition_variable_signal(me->sys->init_cv);
|
||||||
|
|
||||||
|
for (;;){
|
||||||
|
// NOTE(allen): Wait until someone wakes us up, then go into our procedure.
|
||||||
|
for (;me->state != CoroutineState_Active;){
|
||||||
|
system_condition_variable_wait(me->cv, me->sys->lock);
|
||||||
|
}
|
||||||
|
Assert(me->type != CoroutineType_Root);
|
||||||
|
Assert(me->yield_ctx != 0);
|
||||||
|
Assert(me->func != 0);
|
||||||
|
|
||||||
|
me->func(me);
|
||||||
|
|
||||||
|
// NOTE(allen): Wake up the caller and set this coroutine back to being dead.
|
||||||
|
Coroutine *other = me->yield_ctx;
|
||||||
|
Assert(other != 0);
|
||||||
|
Assert(other->state == CoroutineState_Waiting);
|
||||||
|
|
||||||
|
coroutine__pass_control(me, other, CoroutineState_Dead, CoroutinePassControl_ExitMe);
|
||||||
|
me->func = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
coroutine_sub_init(Coroutine *co, Coroutine_Group *sys){
|
||||||
|
block_zero_struct(co);
|
||||||
|
co->sys = sys;
|
||||||
|
co->state = CoroutineState_Dead;
|
||||||
|
co->type = CoroutineType_Sub;
|
||||||
|
co->cv = system_condition_variable_make();
|
||||||
|
sys->did_init = false;
|
||||||
|
co->thread = system_thread_launch(coroutine_main, co);
|
||||||
|
for (;!sys->did_init;){
|
||||||
|
system_condition_variable_wait(sys->init_cv, sys->lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
coroutine_system_init(Coroutine_Group *sys){
|
||||||
|
sys->arena = make_arena_system();
|
||||||
|
|
||||||
|
Coroutine *root = &sys->root;
|
||||||
|
|
||||||
|
sys->lock = system_mutex_make();
|
||||||
|
sys->init_cv = system_condition_variable_make();
|
||||||
|
sys->active = root;
|
||||||
|
|
||||||
|
block_zero_struct(root);
|
||||||
|
root->sys = sys;
|
||||||
|
root->state = CoroutineState_Active;
|
||||||
|
root->type = CoroutineType_Root;
|
||||||
|
root->cv = system_condition_variable_make();
|
||||||
|
|
||||||
|
sys->unused = 0;
|
||||||
|
|
||||||
|
system_mutex_acquire(sys->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Coroutine*
|
||||||
|
coroutine_system_alloc(Coroutine_Group *sys){
|
||||||
|
Coroutine *result = sys->unused;
|
||||||
|
if (result != 0){
|
||||||
|
sll_stack_pop(sys->unused);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
result = push_array(&sys->arena, Coroutine, 1);
|
||||||
|
coroutine_sub_init(result, sys);
|
||||||
|
}
|
||||||
|
result->next = 0;
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
coroutine_system_free(Coroutine_Group *sys, Coroutine *coroutine){
|
||||||
|
sll_stack_push(sys->unused, coroutine);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
internal Coroutine*
|
||||||
|
coroutine_create(Coroutine_Group *coroutines, Coroutine_Function *func){
|
||||||
|
Coroutine *result = coroutine_system_alloc(coroutines);
|
||||||
|
Assert(result->state == CoroutineState_Dead);
|
||||||
|
result->func = func;
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Coroutine*
|
||||||
|
coroutine_run(Coroutine_Group *sys, Coroutine *other, void *in, void *out){
|
||||||
|
other->in = in;
|
||||||
|
other->out = out;
|
||||||
|
|
||||||
|
Coroutine *me = other->sys->active;
|
||||||
|
Assert(me != 0);
|
||||||
|
Assert(me->sys == other->sys);
|
||||||
|
Assert(other->state == CoroutineState_Dead || other->state == CoroutineState_Inactive);
|
||||||
|
other->yield_ctx = me;
|
||||||
|
coroutine__pass_control(me, other, CoroutineState_Waiting, CoroutinePassControl_BlockMe);
|
||||||
|
Assert(me == other->sys->active);
|
||||||
|
|
||||||
|
Coroutine *result = other;
|
||||||
|
if (other->state == CoroutineState_Dead){
|
||||||
|
coroutine_system_free(sys, other);
|
||||||
|
result = 0;
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
coroutine_yield(Coroutine *me){
|
||||||
|
Coroutine *other = me->yield_ctx;
|
||||||
|
Assert(other != 0);
|
||||||
|
Assert(me->sys == other->sys);
|
||||||
|
Assert(other->state == CoroutineState_Waiting);
|
||||||
|
coroutine__pass_control(me, other, CoroutineState_Inactive, CoroutinePassControl_BlockMe);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 03.08.2019
|
||||||
|
*
|
||||||
|
* Coroutine implementation from thread+mutex+cv
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#if !defined(FRED_COROUTINE_H)
|
||||||
|
#define FRED_COROUTINE_H
|
||||||
|
|
||||||
|
typedef void Coroutine_Function(struct Coroutine *head);
|
||||||
|
|
||||||
|
typedef u32 Coroutine_State;
|
||||||
|
enum{
|
||||||
|
CoroutineState_Dead,
|
||||||
|
CoroutineState_Active,
|
||||||
|
CoroutineState_Inactive,
|
||||||
|
CoroutineState_Waiting,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef u32 Coroutine_Type;
|
||||||
|
enum{
|
||||||
|
CoroutineType_Uninitialized,
|
||||||
|
CoroutineType_Root,
|
||||||
|
CoroutineType_Sub,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Coroutine{
|
||||||
|
Coroutine *next;
|
||||||
|
Thread_Context *tctx;
|
||||||
|
void *in;
|
||||||
|
void *out;
|
||||||
|
System_Thread thread;
|
||||||
|
System_Condition_Variable cv;
|
||||||
|
struct Coroutine_Group *sys;
|
||||||
|
Coroutine_Function *func;
|
||||||
|
Coroutine *yield_ctx;
|
||||||
|
Coroutine_State state;
|
||||||
|
Coroutine_Type type;
|
||||||
|
void *user_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Coroutine_Group{
|
||||||
|
Arena arena;
|
||||||
|
System_Mutex lock;
|
||||||
|
System_Condition_Variable init_cv;
|
||||||
|
b32 did_init;
|
||||||
|
Coroutine *active;
|
||||||
|
Coroutine *unused;
|
||||||
|
Coroutine root;
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
typedef i32 Coroutine_Pass_Control;
|
||||||
|
enum{
|
||||||
|
CoroutinePassControl_ExitMe,
|
||||||
|
CoroutinePassControl_BlockMe,
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// BOTTOM
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 07.11.2017
|
||||||
|
*
|
||||||
|
* Application Cursor Codes
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#if !defined(FRED_CURSOR_CODES_H)
|
||||||
|
#define FRED_CURSOR_CODES_H
|
||||||
|
|
||||||
|
typedef i32 Application_Mouse_Cursor;
|
||||||
|
enum{
|
||||||
|
APP_MOUSE_CURSOR_DEFAULT,
|
||||||
|
APP_MOUSE_CURSOR_ARROW,
|
||||||
|
APP_MOUSE_CURSOR_IBEAM,
|
||||||
|
APP_MOUSE_CURSOR_LEFTRIGHT,
|
||||||
|
APP_MOUSE_CURSOR_UPDOWN,
|
||||||
|
// never below this
|
||||||
|
APP_MOUSE_CURSOR_COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,531 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 22.06.2018
|
||||||
|
*
|
||||||
|
* Dynamic variable system
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
internal void
|
||||||
|
managed_ids_init(Base_Allocator *allocator, Managed_ID_Set *set){
|
||||||
|
set->arena = make_arena(allocator, KB(4), 8);
|
||||||
|
set->name_to_group_table = make_table_Data_u64(allocator, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Managed_ID
|
||||||
|
managed_ids_group_highest_id(Managed_ID_Set *set, String_Const_u8 group_name){
|
||||||
|
Managed_ID result = 0;
|
||||||
|
String_Const_u8 data = make_data(group_name.str, group_name.size);
|
||||||
|
Table_Lookup lookup = table_lookup(&set->name_to_group_table, data);
|
||||||
|
if (lookup.found_match){
|
||||||
|
u64 val = 0;
|
||||||
|
table_read(&set->name_to_group_table, lookup, &val);
|
||||||
|
Managed_ID_Group *group = (Managed_ID_Group*)IntAsPtr(val);
|
||||||
|
result = group->id_counter - 1;
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Managed_ID
|
||||||
|
managed_ids_declare(Managed_ID_Set *set, String_Const_u8 group_name, String_Const_u8 name){
|
||||||
|
Managed_ID_Group *group = 0;
|
||||||
|
{
|
||||||
|
String_Const_u8 data = make_data(group_name.str, group_name.size);
|
||||||
|
Table_Lookup lookup = table_lookup(&set->name_to_group_table, data);
|
||||||
|
if (lookup.found_match){
|
||||||
|
u64 val = 0;
|
||||||
|
table_read(&set->name_to_group_table, lookup, &val);
|
||||||
|
group = (Managed_ID_Group*)IntAsPtr(val);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
group = push_array(&set->arena, Managed_ID_Group, 1);
|
||||||
|
group->id_counter = 1;
|
||||||
|
group->name_to_id_table = make_table_Data_u64(set->arena.base_allocator, 50);
|
||||||
|
data = push_data_copy(&set->arena, data);
|
||||||
|
table_insert(&set->name_to_group_table, data, PtrAsInt(group));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Managed_ID result = 0;
|
||||||
|
{
|
||||||
|
String_Const_u8 data = make_data(name.str, name.size);
|
||||||
|
Table_Lookup lookup = table_lookup(&group->name_to_id_table, data);
|
||||||
|
if (lookup.found_match){
|
||||||
|
table_read(&group->name_to_id_table, lookup, &result);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
result = group->id_counter;
|
||||||
|
group->id_counter += 1;
|
||||||
|
data = push_data_copy(&set->arena, data);
|
||||||
|
table_insert(&group->name_to_id_table, data, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Managed_ID
|
||||||
|
managed_ids_get(Managed_ID_Set *set, String_Const_u8 group_name, String_Const_u8 name){
|
||||||
|
Managed_ID_Group *group = 0;
|
||||||
|
{
|
||||||
|
String_Const_u8 data = make_data(group_name.str, group_name.size);
|
||||||
|
Table_Lookup lookup = table_lookup(&set->name_to_group_table, data);
|
||||||
|
if (lookup.found_match){
|
||||||
|
u64 val = 0;
|
||||||
|
table_read(&set->name_to_group_table, lookup, &val);
|
||||||
|
group = (Managed_ID_Group*)IntAsPtr(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Managed_ID result = 0;
|
||||||
|
if (group != 0){
|
||||||
|
String_Const_u8 data = make_data(name.str, name.size);
|
||||||
|
Table_Lookup lookup = table_lookup(&group->name_to_id_table, data);
|
||||||
|
if (lookup.found_match){
|
||||||
|
table_read(&group->name_to_id_table, lookup, &result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
internal void
|
||||||
|
dynamic_variable_block_init(Base_Allocator *allocator, Dynamic_Variable_Block *block){
|
||||||
|
block->arena = make_arena(allocator, KB(4), 8);
|
||||||
|
block->id_to_data_table = make_table_u64_Data(allocator, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal String_Const_u8
|
||||||
|
dynamic_variable_get(Dynamic_Variable_Block *block, Managed_ID id, u64 size){
|
||||||
|
String_Const_u8 result = {};
|
||||||
|
Table_Lookup lookup = table_lookup(&block->id_to_data_table, id);
|
||||||
|
if (lookup.found_match){
|
||||||
|
table_read(&block->id_to_data_table, lookup, &result);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
result = push_data(&block->arena, size);
|
||||||
|
block_zero(result);
|
||||||
|
table_insert(&block->id_to_data_table, id, result);
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
dynamic_variable_erase(Dynamic_Variable_Block *block, Managed_ID id){
|
||||||
|
table_erase(&block->id_to_data_table, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
internal void
|
||||||
|
lifetime_allocator_init(Base_Allocator *base_allocator, Lifetime_Allocator *lifetime_allocator){
|
||||||
|
block_zero_struct(lifetime_allocator);
|
||||||
|
lifetime_allocator->allocator = base_allocator;
|
||||||
|
lifetime_allocator->node_arena = make_arena(base_allocator, KB(4));
|
||||||
|
lifetime_allocator->key_table = make_table_Data_u64(base_allocator, 100);
|
||||||
|
lifetime_allocator->key_check_table = make_table_u64_u64(base_allocator, 100);
|
||||||
|
lifetime_allocator->scope_id_to_scope_ptr_table = make_table_u64_u64(base_allocator, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
internal void
|
||||||
|
dynamic_workspace_init(Lifetime_Allocator *lifetime_allocator, i32 user_type, void *user_back_ptr, Dynamic_Workspace *workspace){
|
||||||
|
block_zero_struct(workspace);
|
||||||
|
heap_init(&workspace->heap, lifetime_allocator->allocator);
|
||||||
|
workspace->heap_wrapper = base_allocator_on_heap(&workspace->heap);
|
||||||
|
workspace->object_id_to_object_ptr = make_table_u64_u64(&workspace->heap_wrapper, 10);
|
||||||
|
dynamic_variable_block_init(&workspace->heap_wrapper, &workspace->var_block);
|
||||||
|
if (lifetime_allocator->scope_id_counter == 0){
|
||||||
|
lifetime_allocator->scope_id_counter = 1;
|
||||||
|
}
|
||||||
|
workspace->scope_id = lifetime_allocator->scope_id_counter++;
|
||||||
|
table_insert(&lifetime_allocator->scope_id_to_scope_ptr_table,
|
||||||
|
workspace->scope_id, (u64)PtrAsInt(workspace));
|
||||||
|
workspace->user_type = user_type;
|
||||||
|
workspace->user_back_ptr = user_back_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
dynamic_workspace_free(Lifetime_Allocator *lifetime_allocator, Dynamic_Workspace *workspace){
|
||||||
|
table_erase(&lifetime_allocator->scope_id_to_scope_ptr_table, workspace->scope_id);
|
||||||
|
heap_free_all(&workspace->heap);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
dynamic_workspace_clear_contents(Dynamic_Workspace *workspace){
|
||||||
|
Base_Allocator *base_allocator = heap_get_base_allocator(&workspace->heap);
|
||||||
|
heap_free_all(&workspace->heap);
|
||||||
|
heap_init(&workspace->heap, base_allocator);
|
||||||
|
workspace->heap_wrapper = base_allocator_on_heap(&workspace->heap);
|
||||||
|
workspace->object_id_to_object_ptr = make_table_u64_u64(&workspace->heap_wrapper, 10);
|
||||||
|
dynamic_variable_block_init(&workspace->heap_wrapper, &workspace->var_block);
|
||||||
|
block_zero_struct(&workspace->buffer_markers_list);
|
||||||
|
workspace->total_marker_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal u32
|
||||||
|
dynamic_workspace_store_pointer(Dynamic_Workspace *workspace, void *ptr){
|
||||||
|
if (workspace->object_id_counter == 0){
|
||||||
|
workspace->object_id_counter = 1;
|
||||||
|
}
|
||||||
|
u32 id = workspace->object_id_counter++;
|
||||||
|
table_insert(&workspace->object_id_to_object_ptr, id, (u64)PtrAsInt(ptr));
|
||||||
|
return(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
dynamic_workspace_erase_pointer(Dynamic_Workspace *workspace, u32 id){
|
||||||
|
table_erase(&workspace->object_id_to_object_ptr, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void*
|
||||||
|
dynamic_workspace_get_pointer(Dynamic_Workspace *workspace, u32 id){
|
||||||
|
void *result = 0;
|
||||||
|
Table_Lookup lookup = table_lookup(&workspace->object_id_to_object_ptr, id);
|
||||||
|
if (lookup.found_match){
|
||||||
|
u64 val = 0;
|
||||||
|
table_read(&workspace->object_id_to_object_ptr, lookup, &val);
|
||||||
|
result = IntAsPtr(val);
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
internal String_Const_u8
|
||||||
|
lifetime__key_as_data(Lifetime_Object **members, i32 count){
|
||||||
|
return(make_data(members, sizeof(*members)*count));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal String_Const_u8
|
||||||
|
lifetime__key_as_data(Lifetime_Key *key){
|
||||||
|
return(lifetime__key_as_data(key->members, key->count));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
lifetime__free_key(Lifetime_Allocator *lifetime_allocator, Lifetime_Key *key, Lifetime_Object *skip_object){
|
||||||
|
// Deinit
|
||||||
|
dynamic_workspace_free(lifetime_allocator, &key->dynamic_workspace);
|
||||||
|
|
||||||
|
// Remove From Objects
|
||||||
|
i32 count = key->count;
|
||||||
|
Lifetime_Object **object_ptr = key->members;
|
||||||
|
for (i32 i = 0; i < count; i += 1, object_ptr += 1){
|
||||||
|
if (*object_ptr == skip_object) continue;
|
||||||
|
|
||||||
|
Lifetime_Key_Ref_Node *delete_point_node = 0;
|
||||||
|
i32 delete_point_i = 0;
|
||||||
|
|
||||||
|
i32 key_i = 0;
|
||||||
|
Lifetime_Object *object = *object_ptr;
|
||||||
|
for (Lifetime_Key_Ref_Node *node = object->key_node_first;
|
||||||
|
node != 0;
|
||||||
|
node = node->next){
|
||||||
|
i32 one_past_last = clamp_top(ArrayCount(node->keys), object->key_count - key_i);
|
||||||
|
for (i32 j = 0; j < one_past_last; j += 1){
|
||||||
|
if (node->keys[j] == key){
|
||||||
|
delete_point_node = node;
|
||||||
|
delete_point_i = j;
|
||||||
|
goto double_break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
key_i += one_past_last;
|
||||||
|
}
|
||||||
|
double_break:;
|
||||||
|
|
||||||
|
Assert(delete_point_node != 0);
|
||||||
|
Lifetime_Key_Ref_Node *last_node = object->key_node_last;
|
||||||
|
Lifetime_Key *last_key = last_node->keys[(object->key_count - 1) % ArrayCount(last_node->keys)];
|
||||||
|
Assert(last_key != 0);
|
||||||
|
delete_point_node->keys[delete_point_i] = last_key;
|
||||||
|
object->key_count -= 1;
|
||||||
|
|
||||||
|
if ((object->key_count % lifetime_key_reference_per_node) == 0){
|
||||||
|
zdll_remove(object->key_node_first, object->key_node_last, last_node);
|
||||||
|
sll_stack_push(lifetime_allocator->free_key_references, last_node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free
|
||||||
|
String_Const_u8 key_data = lifetime__key_as_data(key);
|
||||||
|
table_erase(&lifetime_allocator->key_table, key_data);
|
||||||
|
table_erase(&lifetime_allocator->key_check_table, (u64)PtrAsInt(key));
|
||||||
|
base_free(lifetime_allocator->allocator, key->members);
|
||||||
|
sll_stack_push(lifetime_allocator->free_keys, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Lifetime_Key_Ref_Node*
|
||||||
|
lifetime__alloc_key_reference_node(Lifetime_Allocator *lifetime_allocator){
|
||||||
|
Assert(lifetime_allocator != 0);
|
||||||
|
Lifetime_Key_Ref_Node *result = lifetime_allocator->free_key_references;
|
||||||
|
if (result == 0){
|
||||||
|
result = push_array(&lifetime_allocator->node_arena, Lifetime_Key_Ref_Node, 1);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
sll_stack_pop(lifetime_allocator->free_key_references);
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
lifetime__object_add_key(Lifetime_Allocator *lifetime_allocator, Lifetime_Object *object, Lifetime_Key *key){
|
||||||
|
Lifetime_Key_Ref_Node *last_node = object->key_node_last;
|
||||||
|
b32 insert_on_new_node = false;
|
||||||
|
if (last_node == 0){
|
||||||
|
insert_on_new_node = true;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
i32 next_insert_slot = object->key_count%ArrayCount(last_node->keys);
|
||||||
|
if (next_insert_slot != 0){
|
||||||
|
last_node->keys[next_insert_slot] = key;
|
||||||
|
object->key_count += 1;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
insert_on_new_node = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (insert_on_new_node){
|
||||||
|
Lifetime_Key_Ref_Node *new_node = lifetime__alloc_key_reference_node(lifetime_allocator);
|
||||||
|
zdll_push_back(object->key_node_first, object->key_node_last, new_node);
|
||||||
|
block_zero_struct(new_node->keys);
|
||||||
|
new_node->keys[0] = key;
|
||||||
|
object->key_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Lifetime_Object*
|
||||||
|
lifetime_alloc_object(Lifetime_Allocator *lifetime_allocator, i32 user_type, void *user_back_ptr){
|
||||||
|
Lifetime_Object *object = lifetime_allocator->free_objects;
|
||||||
|
if (object == 0){
|
||||||
|
object = push_array(&lifetime_allocator->node_arena, Lifetime_Object, 1);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
sll_stack_pop(lifetime_allocator->free_objects);
|
||||||
|
}
|
||||||
|
block_zero_struct(object);
|
||||||
|
dynamic_workspace_init(lifetime_allocator, user_type, user_back_ptr, &object->workspace);
|
||||||
|
return(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
lifetime__object_free_all_keys(Lifetime_Allocator *lifetime_allocator, Lifetime_Object *lifetime_object){
|
||||||
|
i32 key_i = 0;
|
||||||
|
for (Lifetime_Key_Ref_Node *node = lifetime_object->key_node_first;
|
||||||
|
node != 0;
|
||||||
|
node = node->next){
|
||||||
|
i32 one_past_last = clamp_top(ArrayCount(node->keys), lifetime_object->key_count - key_i);
|
||||||
|
for (i32 i = 0; i < one_past_last; i += 1){
|
||||||
|
lifetime__free_key(lifetime_allocator, node->keys[i], lifetime_object);
|
||||||
|
}
|
||||||
|
key_i += one_past_last;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lifetime_object->key_count > 0){
|
||||||
|
lifetime_object->key_node_last->next = lifetime_allocator->free_key_references;
|
||||||
|
lifetime_allocator->free_key_references = lifetime_object->key_node_first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
lifetime__object_clear_all_keys(Lifetime_Allocator *lifetime_allocator, Lifetime_Object *lifetime_object){
|
||||||
|
i32 key_i = 0;
|
||||||
|
for (Lifetime_Key_Ref_Node *node = lifetime_object->key_node_first;
|
||||||
|
node != 0;
|
||||||
|
node = node->next){
|
||||||
|
i32 one_past_last = clamp_top(ArrayCount(node->keys), lifetime_object->key_count - key_i);
|
||||||
|
Lifetime_Key **key_ptr = node->keys;
|
||||||
|
for (i32 i = 0; i < one_past_last; i += 1, key_ptr += 1){
|
||||||
|
dynamic_workspace_clear_contents(&(*key_ptr)->dynamic_workspace);
|
||||||
|
}
|
||||||
|
key_i += one_past_last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
lifetime_free_object(Lifetime_Allocator *lifetime_allocator, Lifetime_Object *lifetime_object){
|
||||||
|
lifetime__object_free_all_keys(lifetime_allocator, lifetime_object);
|
||||||
|
dynamic_workspace_free(lifetime_allocator, &lifetime_object->workspace);
|
||||||
|
sll_stack_push(lifetime_allocator->free_objects, lifetime_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
lifetime_object_reset(Lifetime_Allocator *lifetime_allocator, Lifetime_Object *lifetime_object){
|
||||||
|
lifetime__object_clear_all_keys(lifetime_allocator, lifetime_object);
|
||||||
|
dynamic_workspace_clear_contents(&lifetime_object->workspace);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal i32
|
||||||
|
lifetime_sort_object_set__part(Lifetime_Object **ptr_array, i32 first, i32 one_past_last){
|
||||||
|
i32 pivot_index = one_past_last - 1;
|
||||||
|
Lifetime_Object *pivot = ptr_array[pivot_index];
|
||||||
|
i32 j = first;
|
||||||
|
for (i32 i = first; i < pivot_index; i += 1){
|
||||||
|
Lifetime_Object *object = ptr_array[i];
|
||||||
|
if (object < pivot){
|
||||||
|
Swap(Lifetime_Object*, ptr_array[i], ptr_array[j]);
|
||||||
|
j += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Swap(Lifetime_Object*, ptr_array[j], ptr_array[pivot_index]);
|
||||||
|
return(j);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
lifetime_sort_object_set__quick(Lifetime_Object **ptr_array, i32 first, i32 one_past_last){
|
||||||
|
if (first + 1 < one_past_last){
|
||||||
|
i32 pivot = lifetime_sort_object_set__part(ptr_array, first, one_past_last);
|
||||||
|
lifetime_sort_object_set__quick(ptr_array, first, pivot);
|
||||||
|
lifetime_sort_object_set__quick(ptr_array, pivot + 1, one_past_last);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal i32
|
||||||
|
lifetime_sort_and_dedup_object_set(Lifetime_Object **ptr_array, i32 count){
|
||||||
|
lifetime_sort_object_set__quick(ptr_array, 0, count);
|
||||||
|
Lifetime_Object **ptr_write = ptr_array + 1;
|
||||||
|
Lifetime_Object **ptr_read = ptr_array + 1;
|
||||||
|
for (i32 i = 1; i < count; i += 1, ptr_read += 1){
|
||||||
|
if (ptr_read[-1] < ptr_read[0]){
|
||||||
|
*ptr_write = *ptr_read;
|
||||||
|
ptr_write += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return((i32)(ptr_write - ptr_array));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Lifetime_Key*
|
||||||
|
lifetime_get_or_create_intersection_key(Lifetime_Allocator *lifetime_allocator, Lifetime_Object **object_ptr_array, i32 count){
|
||||||
|
{
|
||||||
|
String_Const_u8 key_data = lifetime__key_as_data(object_ptr_array, count);
|
||||||
|
Table_Lookup lookup = table_lookup(&lifetime_allocator->key_table, key_data);
|
||||||
|
if (lookup.found_match){
|
||||||
|
u64 val = 0;
|
||||||
|
table_read(&lifetime_allocator->key_table, lookup, &val);
|
||||||
|
return((Lifetime_Key*)IntAsPtr(val));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate
|
||||||
|
Lifetime_Key *new_key = lifetime_allocator->free_keys;
|
||||||
|
if (new_key == 0){
|
||||||
|
new_key = push_array(&lifetime_allocator->node_arena, Lifetime_Key, 1);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
sll_stack_pop(lifetime_allocator->free_keys);
|
||||||
|
}
|
||||||
|
block_zero_struct(new_key);
|
||||||
|
|
||||||
|
// Add to Objects
|
||||||
|
Lifetime_Object **object_ptr = object_ptr_array;
|
||||||
|
for (i32 i = 0; i < count; i += 1, object_ptr += 1){
|
||||||
|
Lifetime_Object *object = *object_ptr;
|
||||||
|
lifetime__object_add_key(lifetime_allocator, object, new_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize
|
||||||
|
u64 new_memory_size = sizeof(Lifetime_Object*)*count;
|
||||||
|
String_Const_u8 new_memory = base_allocate(lifetime_allocator->allocator, new_memory_size);
|
||||||
|
new_key->members = (Lifetime_Object**)new_memory.str;
|
||||||
|
block_copy_dynamic_array(new_key->members, object_ptr_array, count);
|
||||||
|
new_key->count = count;
|
||||||
|
dynamic_workspace_init(lifetime_allocator,
|
||||||
|
DynamicWorkspace_Intersected, new_key,
|
||||||
|
&new_key->dynamic_workspace);
|
||||||
|
|
||||||
|
{
|
||||||
|
String_Const_u8 key_data = lifetime__key_as_data(new_key);
|
||||||
|
u64 new_key_val = (u64)PtrAsInt(new_key);
|
||||||
|
table_insert(&lifetime_allocator->key_table, key_data, new_key_val);
|
||||||
|
table_insert(&lifetime_allocator->key_check_table, new_key_val, new_key_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
return(new_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal b32
|
||||||
|
lifetime_key_check(Lifetime_Allocator *lifetime_allocator, Lifetime_Key *key){
|
||||||
|
Table_Lookup lookup = table_lookup(&lifetime_allocator->key_check_table, (u64)PtrAsInt(key));
|
||||||
|
return(lookup.found_match);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
// TODO(allen): move this shit somewhere real, clean up all object creation functions to be more cleanly layered.
|
||||||
|
internal u8*
|
||||||
|
get_dynamic_object_memory_ptr(Managed_Object_Standard_Header *header){
|
||||||
|
u8 *ptr = 0;
|
||||||
|
if (header != 0){
|
||||||
|
switch (header->type){
|
||||||
|
case ManagedObjectType_Memory:
|
||||||
|
case ManagedObjectType_Markers:
|
||||||
|
{
|
||||||
|
ptr = ((u8*)header) + managed_header_type_sizes[header->type];
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Managed_Object
|
||||||
|
managed_object_alloc_managed_memory(Dynamic_Workspace *workspace, i32 item_size, i32 count, void **ptr_out){
|
||||||
|
i32 size = item_size*count;
|
||||||
|
String_Const_u8 new_memory = base_allocate(&workspace->heap_wrapper, sizeof(Managed_Memory_Header) + size);
|
||||||
|
void *ptr = new_memory.str;
|
||||||
|
Managed_Memory_Header *header = (Managed_Memory_Header*)ptr;
|
||||||
|
header->std_header.type = ManagedObjectType_Memory;
|
||||||
|
header->std_header.item_size = item_size;
|
||||||
|
header->std_header.count = count;
|
||||||
|
if (ptr_out != 0){
|
||||||
|
*ptr_out = get_dynamic_object_memory_ptr(&header->std_header);
|
||||||
|
}
|
||||||
|
u32 id = dynamic_workspace_store_pointer(workspace, ptr);
|
||||||
|
return(((u64)workspace->scope_id << 32) | (u64)id);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Managed_Object
|
||||||
|
managed_object_alloc_buffer_markers(Dynamic_Workspace *workspace, Buffer_ID buffer_id, i32 count, Marker **markers_out){
|
||||||
|
i32 size = count*sizeof(Marker);
|
||||||
|
String_Const_u8 new_memory = base_allocate(&workspace->heap_wrapper, size + sizeof(Managed_Buffer_Markers_Header));
|
||||||
|
void *ptr = new_memory.str;
|
||||||
|
Managed_Buffer_Markers_Header *header = (Managed_Buffer_Markers_Header*)ptr;
|
||||||
|
header->std_header.type = ManagedObjectType_Markers;
|
||||||
|
header->std_header.item_size = sizeof(Marker);
|
||||||
|
header->std_header.count = count;
|
||||||
|
zdll_push_back(workspace->buffer_markers_list.first, workspace->buffer_markers_list.last, header);
|
||||||
|
workspace->buffer_markers_list.count += 1;
|
||||||
|
workspace->total_marker_count += count;
|
||||||
|
header->buffer_id = buffer_id;
|
||||||
|
if (markers_out != 0){
|
||||||
|
*markers_out = (Marker*)get_dynamic_object_memory_ptr(&header->std_header);
|
||||||
|
}
|
||||||
|
u32 id = dynamic_workspace_store_pointer(workspace, ptr);
|
||||||
|
return(((u64)workspace->scope_id << 32) | (u64)id);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal b32
|
||||||
|
managed_object_free(Dynamic_Workspace *workspace, Managed_Object object){
|
||||||
|
b32 result = false;
|
||||||
|
u32 lo_id = object&max_u32;
|
||||||
|
u8 *object_ptr = (u8*)dynamic_workspace_get_pointer(workspace, lo_id);
|
||||||
|
if (object_ptr != 0){
|
||||||
|
Managed_Object_Type *type = (Managed_Object_Type*)object_ptr;
|
||||||
|
switch (*type){
|
||||||
|
case ManagedObjectType_Markers:
|
||||||
|
{
|
||||||
|
Managed_Buffer_Markers_Header *header = (Managed_Buffer_Markers_Header*)object_ptr;
|
||||||
|
workspace->total_marker_count -= header->std_header.count;
|
||||||
|
zdll_remove(workspace->buffer_markers_list.first, workspace->buffer_markers_list.last, header);
|
||||||
|
workspace->buffer_markers_list.count -= 1;
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
dynamic_workspace_erase_pointer(workspace, lo_id);
|
||||||
|
base_free(&workspace->heap_wrapper, object_ptr);
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,159 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 22.06.2018
|
||||||
|
*
|
||||||
|
* Dynamic variable system
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#if !defined(FRED_DYNAMIC_VARIABLES_H)
|
||||||
|
#define FRED_DYNAMIC_VARIABLES_H
|
||||||
|
|
||||||
|
union Managed_Object_Standard_Header{
|
||||||
|
u64 eight_byte_alignment__;
|
||||||
|
struct{
|
||||||
|
Managed_Object_Type type;
|
||||||
|
u32 item_size;
|
||||||
|
u32 count;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Managed_Memory_Header{
|
||||||
|
Managed_Object_Standard_Header std_header;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Managed_Buffer_Markers_Header{
|
||||||
|
Managed_Object_Standard_Header std_header;
|
||||||
|
Managed_Buffer_Markers_Header *next;
|
||||||
|
Managed_Buffer_Markers_Header *prev;
|
||||||
|
Buffer_ID buffer_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Managed_Arena_Header{
|
||||||
|
Managed_Object_Standard_Header std_header;
|
||||||
|
Managed_Arena_Header *next;
|
||||||
|
Managed_Arena_Header *prev;
|
||||||
|
Arena arena;
|
||||||
|
};
|
||||||
|
|
||||||
|
global_const i32 managed_header_type_sizes[ManagedObjectType_COUNT] = {
|
||||||
|
0,
|
||||||
|
sizeof(Managed_Memory_Header),
|
||||||
|
sizeof(Managed_Buffer_Markers_Header),
|
||||||
|
sizeof(Managed_Arena_Header),
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Managed_Buffer_Markers_Header_List{
|
||||||
|
Managed_Buffer_Markers_Header *first;
|
||||||
|
Managed_Buffer_Markers_Header *last;
|
||||||
|
i32 count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Managed_Arena_Header_List{
|
||||||
|
Managed_Arena_Header *first;
|
||||||
|
Managed_Arena_Header *last;
|
||||||
|
i32 count;
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
struct Managed_ID_Group{
|
||||||
|
Table_Data_u64 name_to_id_table;
|
||||||
|
Managed_ID id_counter;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Managed_ID_Set{
|
||||||
|
Arena arena;
|
||||||
|
Table_Data_u64 name_to_group_table;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Dynamic_Variable_Block{
|
||||||
|
Arena arena;
|
||||||
|
Table_u64_Data id_to_data_table;
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
struct Dynamic_Workspace{
|
||||||
|
Dynamic_Variable_Block var_block;
|
||||||
|
Heap heap;
|
||||||
|
Base_Allocator heap_wrapper;
|
||||||
|
Table_u64_u64 object_id_to_object_ptr;
|
||||||
|
u32 object_id_counter;
|
||||||
|
u32 visual_id_counter;
|
||||||
|
u32 scope_id;
|
||||||
|
i32 user_type;
|
||||||
|
void *user_back_ptr;
|
||||||
|
Managed_Buffer_Markers_Header_List buffer_markers_list;
|
||||||
|
Managed_Arena_Header_List arena_list;
|
||||||
|
i32 total_marker_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
global_const i32 lifetime_key_reference_per_node = 32;
|
||||||
|
|
||||||
|
struct Lifetime_Key_Ref_Node{
|
||||||
|
Lifetime_Key_Ref_Node *next;
|
||||||
|
Lifetime_Key_Ref_Node *prev;
|
||||||
|
struct Lifetime_Key *keys[lifetime_key_reference_per_node];
|
||||||
|
};
|
||||||
|
|
||||||
|
union Lifetime_Object{
|
||||||
|
Lifetime_Object *next;
|
||||||
|
struct{
|
||||||
|
Lifetime_Key_Ref_Node *key_node_first;
|
||||||
|
Lifetime_Key_Ref_Node *key_node_last;
|
||||||
|
i32 key_count;
|
||||||
|
Dynamic_Workspace workspace;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Lifetime_Key{
|
||||||
|
union{
|
||||||
|
struct{
|
||||||
|
Lifetime_Key *next;
|
||||||
|
Lifetime_Key *prev;
|
||||||
|
};
|
||||||
|
struct{
|
||||||
|
Lifetime_Object **members;
|
||||||
|
i32 count;
|
||||||
|
Dynamic_Workspace dynamic_workspace;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
global_const u64 LifetimeKeyHash_Empty = 0&(~bit_63);
|
||||||
|
global_const u64 LifetimeKeyHash_Deleted = max_u64&(~bit_63);
|
||||||
|
|
||||||
|
struct Lifetime_Allocator{
|
||||||
|
Base_Allocator *allocator;
|
||||||
|
Arena node_arena;
|
||||||
|
Lifetime_Key_Ref_Node *free_key_references;
|
||||||
|
Lifetime_Key* free_keys;
|
||||||
|
Lifetime_Object *free_objects;
|
||||||
|
Table_Data_u64 key_table;
|
||||||
|
Table_u64_u64 key_check_table;
|
||||||
|
Table_u64_u64 scope_id_to_scope_ptr_table;
|
||||||
|
u32 scope_id_counter;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Lifetime_Key_With_Opaque_ID{
|
||||||
|
Lifetime_Key *key;
|
||||||
|
u64 opaque_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
struct Managed_Object_Ptr_And_Workspace{
|
||||||
|
Dynamic_Workspace *workspace;
|
||||||
|
Managed_Object_Standard_Header *header;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,657 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 25.03.2018
|
||||||
|
*
|
||||||
|
* High level edit procedures
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
function void
|
||||||
|
pre_edit_state_change(Models *models, Editing_File *file){
|
||||||
|
file_add_dirty_flag(file, DirtyState_UnsavedChanges);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
pre_edit_history_prep(Editing_File *file, Edit_Behaviors behaviors){
|
||||||
|
if (!behaviors.do_not_post_to_history){
|
||||||
|
history_dump_records_after_index(&file->state.history,
|
||||||
|
file->state.current_record_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
post_edit_call_hook(Thread_Context *tctx, Models *models, Editing_File *file,
|
||||||
|
Range_i64 new_range, Range_Cursor old_cursor_range){
|
||||||
|
// NOTE(allen): edit range hook
|
||||||
|
if (models->buffer_edit_range != 0){
|
||||||
|
Application_Links app = {};
|
||||||
|
app.tctx = tctx;
|
||||||
|
app.cmd_context = models;
|
||||||
|
models->buffer_edit_range(&app, file->id, new_range, old_cursor_range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
edit_fix_markers__write_workspace_markers(Dynamic_Workspace *workspace, Buffer_ID buffer_id,
|
||||||
|
Cursor_With_Index *cursors, Cursor_With_Index *r_cursors,
|
||||||
|
i32 *cursor_count, i32 *r_cursor_count){
|
||||||
|
for (Managed_Buffer_Markers_Header *node = workspace->buffer_markers_list.first;
|
||||||
|
node != 0;
|
||||||
|
node = node->next){
|
||||||
|
if (node->buffer_id != buffer_id) continue;
|
||||||
|
Marker *markers = (Marker*)(node + 1);
|
||||||
|
Assert(sizeof(*markers) == node->std_header.item_size);
|
||||||
|
i32 count = node->std_header.count;
|
||||||
|
for (i32 i = 0; i < count; i += 1){
|
||||||
|
if (markers[i].lean_right){
|
||||||
|
write_cursor_with_index(r_cursors, r_cursor_count, markers[i].pos);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
write_cursor_with_index(cursors , cursor_count , markers[i].pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
edit_fix_markers__read_workspace_markers(Dynamic_Workspace *workspace, Buffer_ID buffer_id,
|
||||||
|
Cursor_With_Index *cursors, Cursor_With_Index *r_cursors, i32 *cursor_count, i32 *r_cursor_count){
|
||||||
|
for (Managed_Buffer_Markers_Header *node = workspace->buffer_markers_list.first;
|
||||||
|
node != 0;
|
||||||
|
node = node->next){
|
||||||
|
if (node->buffer_id != buffer_id) continue;
|
||||||
|
Marker *markers = (Marker*)(node + 1);
|
||||||
|
Assert(sizeof(*markers) == node->std_header.item_size);
|
||||||
|
i32 count = node->std_header.count;
|
||||||
|
for (i32 i = 0; i < count; i += 1){
|
||||||
|
if (markers[i].lean_right){
|
||||||
|
markers[i].pos = r_cursors[(*r_cursor_count)++].pos;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
markers[i].pos = cursors[(*cursor_count)++].pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function f32
|
||||||
|
edit_fix_markers__compute_scroll_y(i32 line_height, f32 old_y_val, f32 new_y_val_aligned){
|
||||||
|
f32 y_offset = mod_f32(old_y_val, line_height);
|
||||||
|
f32 y_position = new_y_val_aligned + y_offset;
|
||||||
|
return(y_position);
|
||||||
|
}
|
||||||
|
|
||||||
|
function i32
|
||||||
|
edit_fix_markers__compute_scroll_y(i32 line_height, i32 old_y_val, f32 new_y_val_aligned){
|
||||||
|
return((i32)edit_fix_markers__compute_scroll_y(line_height, (f32)old_y_val, new_y_val_aligned));
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
edit_fix_markers(Thread_Context *tctx, Models *models, Editing_File *file, Batch_Edit *batch){
|
||||||
|
Layout *layout = &models->layout;
|
||||||
|
|
||||||
|
Lifetime_Object *file_lifetime_object = file->lifetime_object;
|
||||||
|
Buffer_ID file_id = file->id;
|
||||||
|
Assert(file_lifetime_object != 0);
|
||||||
|
|
||||||
|
i32 cursor_max = layout_get_open_panel_count(layout)*4;
|
||||||
|
i32 total_marker_count = 0;
|
||||||
|
{
|
||||||
|
total_marker_count += file_lifetime_object->workspace.total_marker_count;
|
||||||
|
|
||||||
|
i32 key_count = file_lifetime_object->key_count;
|
||||||
|
i32 key_index = 0;
|
||||||
|
for (Lifetime_Key_Ref_Node *key_node = file_lifetime_object->key_node_first;
|
||||||
|
key_node != 0;
|
||||||
|
key_node = key_node->next){
|
||||||
|
i32 count = clamp_top(lifetime_key_reference_per_node, key_count - key_index);
|
||||||
|
for (i32 i = 0; i < count; i += 1){
|
||||||
|
Lifetime_Key *key = key_node->keys[i];
|
||||||
|
total_marker_count += key->dynamic_workspace.total_marker_count;
|
||||||
|
}
|
||||||
|
key_index += count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cursor_max += total_marker_count;
|
||||||
|
|
||||||
|
Scratch_Block scratch(tctx);
|
||||||
|
|
||||||
|
Cursor_With_Index *cursors = push_array(scratch, Cursor_With_Index, cursor_max);
|
||||||
|
Cursor_With_Index *r_cursors = push_array(scratch, Cursor_With_Index, cursor_max);
|
||||||
|
i32 cursor_count = 0;
|
||||||
|
i32 r_cursor_count = 0;
|
||||||
|
Assert(cursors != 0);
|
||||||
|
Assert(r_cursors != 0);
|
||||||
|
|
||||||
|
for (Panel *panel = layout_get_first_open_panel(layout);
|
||||||
|
panel != 0;
|
||||||
|
panel = layout_get_next_open_panel(layout, panel)){
|
||||||
|
View *view = panel->view;
|
||||||
|
if (view->file == file){
|
||||||
|
File_Edit_Positions edit_pos = view_get_edit_pos(view);
|
||||||
|
write_cursor_with_index(cursors, &cursor_count, (i32)edit_pos.cursor_pos);
|
||||||
|
write_cursor_with_index(cursors, &cursor_count, (i32)view->mark);
|
||||||
|
Buffer_Cursor pos_cursor = file_compute_cursor(file, seek_line_col(edit_pos.scroll.position.line_number, 1));
|
||||||
|
Buffer_Cursor targ_cursor = file_compute_cursor(file, seek_line_col(edit_pos.scroll.target.line_number, 1));
|
||||||
|
write_cursor_with_index(cursors, &cursor_count, pos_cursor.pos);
|
||||||
|
write_cursor_with_index(cursors, &cursor_count, targ_cursor.pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
edit_fix_markers__write_workspace_markers(&file_lifetime_object->workspace, file_id, cursors, r_cursors, &cursor_count, &r_cursor_count);
|
||||||
|
|
||||||
|
{
|
||||||
|
i32 key_count = file_lifetime_object->key_count;
|
||||||
|
i32 key_index = 0;
|
||||||
|
for (Lifetime_Key_Ref_Node *key_node = file_lifetime_object->key_node_first;
|
||||||
|
key_node != 0;
|
||||||
|
key_node = key_node->next){
|
||||||
|
i32 count = clamp_top(lifetime_key_reference_per_node, key_count - key_index);
|
||||||
|
for (i32 i = 0; i < count; i += 1){
|
||||||
|
Lifetime_Key *key = key_node->keys[i];
|
||||||
|
edit_fix_markers__write_workspace_markers(&key->dynamic_workspace, file_id, cursors, r_cursors, &cursor_count, &r_cursor_count);
|
||||||
|
}
|
||||||
|
key_index += count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_remeasure_starts(tctx, &file->state.buffer, batch);
|
||||||
|
|
||||||
|
if (cursor_count > 0 || r_cursor_count > 0){
|
||||||
|
buffer_sort_cursors( cursors, cursor_count);
|
||||||
|
buffer_sort_cursors(r_cursors, r_cursor_count);
|
||||||
|
|
||||||
|
buffer_update_cursors_lean_l( cursors, cursor_count, batch);
|
||||||
|
buffer_update_cursors_lean_r(r_cursors, r_cursor_count, batch);
|
||||||
|
|
||||||
|
buffer_unsort_cursors( cursors, cursor_count);
|
||||||
|
buffer_unsort_cursors(r_cursors, r_cursor_count);
|
||||||
|
|
||||||
|
Face *face = file_get_face(models, file);
|
||||||
|
|
||||||
|
cursor_count = 0;
|
||||||
|
r_cursor_count = 0;
|
||||||
|
for (Panel *panel = layout_get_first_open_panel(layout);
|
||||||
|
panel != 0;
|
||||||
|
panel = layout_get_next_open_panel(layout, panel)){
|
||||||
|
View *view = panel->view;
|
||||||
|
if (view->file == file){
|
||||||
|
i64 cursor_pos = cursors[cursor_count++].pos;
|
||||||
|
view->mark = cursors[cursor_count++].pos;
|
||||||
|
File_Edit_Positions edit_pos = view_get_edit_pos(view);
|
||||||
|
|
||||||
|
i64 scroll_pos = cursors[cursor_count++].pos;
|
||||||
|
i64 scroll_targ = cursors[cursor_count++].pos;
|
||||||
|
Buffer_Cursor pos_cursor = file_compute_cursor(file, seek_pos(scroll_pos));
|
||||||
|
Buffer_Cursor targ_cursor = file_compute_cursor(file, seek_pos(scroll_targ));
|
||||||
|
edit_pos.scroll.position.line_number = pos_cursor.line;
|
||||||
|
edit_pos.scroll.target.line_number = targ_cursor.line;
|
||||||
|
|
||||||
|
view_set_cursor_and_scroll(tctx, models, view, cursor_pos, edit_pos.scroll);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
edit_fix_markers__read_workspace_markers(&file_lifetime_object->workspace, file_id, cursors, r_cursors, &cursor_count, &r_cursor_count);
|
||||||
|
|
||||||
|
i32 key_count = file_lifetime_object->key_count;
|
||||||
|
i32 key_index = 0;
|
||||||
|
for (Lifetime_Key_Ref_Node *key_node = file_lifetime_object->key_node_first;
|
||||||
|
key_node != 0;
|
||||||
|
key_node = key_node->next){
|
||||||
|
i32 count = clamp_top(lifetime_key_reference_per_node, key_count - key_index);
|
||||||
|
for (i32 i = 0; i < count; i += 1){
|
||||||
|
Lifetime_Key *key = key_node->keys[i];
|
||||||
|
edit_fix_markers__read_workspace_markers(&key->dynamic_workspace, file_id, cursors, r_cursors, &cursor_count, &r_cursor_count);
|
||||||
|
}
|
||||||
|
key_index += count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
file_end_file(Thread_Context *tctx, Models *models, Editing_File *file){
|
||||||
|
if (models->end_buffer != 0){
|
||||||
|
Application_Links app = {};
|
||||||
|
app.tctx = tctx;
|
||||||
|
app.cmd_context = models;
|
||||||
|
models->end_buffer(&app, file->id);
|
||||||
|
}
|
||||||
|
Lifetime_Allocator *lifetime_allocator = &models->lifetime_allocator;
|
||||||
|
lifetime_free_object(lifetime_allocator, file->lifetime_object);
|
||||||
|
file->lifetime_object = lifetime_alloc_object(lifetime_allocator, DynamicWorkspace_Buffer, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
edit__apply(Thread_Context *tctx, Models *models, Editing_File *file, Range_i64 range, String_Const_u8 string, Edit_Behaviors behaviors){
|
||||||
|
Edit edit = {};
|
||||||
|
edit.text = string;
|
||||||
|
edit.range = range;
|
||||||
|
|
||||||
|
Gap_Buffer *buffer = &file->state.buffer;
|
||||||
|
Assert(0 <= edit.range.first);
|
||||||
|
Assert(edit.range.first <= edit.range.one_past_last);
|
||||||
|
Assert(edit.range.one_past_last <= buffer_size(buffer));
|
||||||
|
|
||||||
|
// NOTE(allen): history update
|
||||||
|
if (!behaviors.do_not_post_to_history){
|
||||||
|
ProfileTLBlock(tctx, &models->profile_list, "edit apply history");
|
||||||
|
history_record_edit(&models->global_history, &file->state.history, buffer,
|
||||||
|
behaviors.pos_before_edit, edit);
|
||||||
|
file->state.current_record_index =
|
||||||
|
history_get_record_count(&file->state.history);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ProfileTLBlock(tctx, &models->profile_list, "edit apply replace range");
|
||||||
|
i64 shift_amount = replace_range_shift(edit.range, (i64)edit.text.size);
|
||||||
|
buffer_replace_range(buffer, edit.range, edit.text, shift_amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
edit_single(Thread_Context *tctx, Models *models, Editing_File *file,
|
||||||
|
Range_i64 range, String_Const_u8 string, Edit_Behaviors behaviors){
|
||||||
|
Range_Cursor cursor_range = {};
|
||||||
|
cursor_range.min = file_compute_cursor(file, seek_pos(range.min));
|
||||||
|
cursor_range.max = file_compute_cursor(file, seek_pos(range.max));
|
||||||
|
|
||||||
|
pre_edit_state_change(models, file);
|
||||||
|
pre_edit_history_prep(file, behaviors);
|
||||||
|
|
||||||
|
edit__apply(tctx, models, file, range, string, behaviors);
|
||||||
|
|
||||||
|
file_clear_layout_cache(file);
|
||||||
|
|
||||||
|
Batch_Edit batch = {};
|
||||||
|
batch.edit.text = string;
|
||||||
|
batch.edit.range = range;
|
||||||
|
|
||||||
|
edit_fix_markers(tctx, models, file, &batch);
|
||||||
|
post_edit_call_hook(tctx, models, file, Ii64_size(range.first, string.size), cursor_range);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
edit__apply_record_forward(Thread_Context *tctx, Models *models, Editing_File *file, Record *record, Edit_Behaviors behaviors_prototype){
|
||||||
|
// NOTE(allen): // NOTE(allen): // NOTE(allen): // NOTE(allen): // NOTE(allen):
|
||||||
|
// Whenever you change this also change the backward version!
|
||||||
|
|
||||||
|
switch (record->kind){
|
||||||
|
case RecordKind_Single:
|
||||||
|
{
|
||||||
|
String_Const_u8 str = record->single.forward_text;
|
||||||
|
Range_i64 range = Ii64(record->single.first, record->single.first + record->single.backward_text.size);
|
||||||
|
edit_single(tctx, models, file, range, str, behaviors_prototype);
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case RecordKind_Group:
|
||||||
|
{
|
||||||
|
Node *sentinel = &record->group.children;
|
||||||
|
for (Node *node = sentinel->next;
|
||||||
|
node != sentinel;
|
||||||
|
node = node->next){
|
||||||
|
Record *sub_record = CastFromMember(Record, node, node);
|
||||||
|
edit__apply_record_forward(tctx, models, file, sub_record, behaviors_prototype);
|
||||||
|
}
|
||||||
|
}break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
InvalidPath;
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
edit__apply_record_backward(Thread_Context *tctx, Models *models, Editing_File *file, Record *record, Edit_Behaviors behaviors_prototype){
|
||||||
|
// NOTE(allen): // NOTE(allen): // NOTE(allen): // NOTE(allen): // NOTE(allen):
|
||||||
|
// Whenever you change this also change the forward version!
|
||||||
|
|
||||||
|
switch (record->kind){
|
||||||
|
case RecordKind_Single:
|
||||||
|
{
|
||||||
|
String_Const_u8 str = record->single.backward_text;
|
||||||
|
Range_i64 range = Ii64(record->single.first, record->single.first + record->single.forward_text.size);
|
||||||
|
edit_single(tctx, models, file, range, str, behaviors_prototype);
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case RecordKind_Group:
|
||||||
|
{
|
||||||
|
Node *sentinel = &record->group.children;
|
||||||
|
for (Node *node = sentinel->prev;
|
||||||
|
node != sentinel;
|
||||||
|
node = node->prev){
|
||||||
|
Record *sub_record = CastFromMember(Record, node, node);
|
||||||
|
edit__apply_record_backward(tctx, models, file, sub_record, behaviors_prototype);
|
||||||
|
}
|
||||||
|
}break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
InvalidPath;
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
edit_change_current_history_state(Thread_Context *tctx, Models *models, Editing_File *file, i32 target_index){
|
||||||
|
History *history = &file->state.history;
|
||||||
|
if (history->activated && file->state.current_record_index != target_index){
|
||||||
|
Assert(0 <= target_index && target_index <= history->record_count);
|
||||||
|
|
||||||
|
i32 current = file->state.current_record_index;
|
||||||
|
Record *record = history_get_record(history, current);
|
||||||
|
Assert(record != 0);
|
||||||
|
Record *dummy_record = history_get_dummy_record(history);
|
||||||
|
|
||||||
|
Edit_Behaviors behaviors_prototype = {};
|
||||||
|
behaviors_prototype.do_not_post_to_history = true;
|
||||||
|
behaviors_prototype.pos_before_edit = -1;
|
||||||
|
|
||||||
|
if (current < target_index){
|
||||||
|
do{
|
||||||
|
current += 1;
|
||||||
|
record = CastFromMember(Record, node, record->node.next);
|
||||||
|
Assert(record != dummy_record);
|
||||||
|
edit__apply_record_forward(tctx, models, file, record, behaviors_prototype);
|
||||||
|
} while (current != target_index);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
do{
|
||||||
|
Assert(record != dummy_record);
|
||||||
|
edit__apply_record_backward(tctx, models, file, record, behaviors_prototype);
|
||||||
|
current -= 1;
|
||||||
|
record = CastFromMember(Record, node, record->node.prev);
|
||||||
|
} while (current != target_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
file->state.current_record_index = current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function b32
|
||||||
|
edit_merge_history_range(Thread_Context *tctx, Models *models, Editing_File *file, History_Record_Index first_index, History_Record_Index last_index, Record_Merge_Flag flags){
|
||||||
|
b32 result = false;
|
||||||
|
History *history = &file->state.history;
|
||||||
|
if (history_is_activated(history)){
|
||||||
|
i32 max_index = history_get_record_count(history);
|
||||||
|
first_index = clamp_bot(1, first_index);
|
||||||
|
if (first_index <= last_index && last_index <= max_index){
|
||||||
|
if (first_index < last_index){
|
||||||
|
i32 current_index = file->state.current_record_index;
|
||||||
|
if (first_index <= current_index && current_index < last_index){
|
||||||
|
u32 in_range_handler = (flags & bitmask_2);
|
||||||
|
switch (in_range_handler){
|
||||||
|
case RecordMergeFlag_StateInRange_MoveStateForward:
|
||||||
|
{
|
||||||
|
edit_change_current_history_state(tctx, models, file, last_index);
|
||||||
|
current_index = last_index;
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case RecordMergeFlag_StateInRange_MoveStateBackward:
|
||||||
|
{
|
||||||
|
edit_change_current_history_state(tctx, models, file, first_index);
|
||||||
|
current_index = first_index;
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case RecordMergeFlag_StateInRange_ErrorOut:
|
||||||
|
{
|
||||||
|
goto done;
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Scratch_Block scratch(tctx);
|
||||||
|
history_merge_records(scratch, history, first_index, last_index);
|
||||||
|
if (current_index >= last_index){
|
||||||
|
current_index -= (last_index - first_index);
|
||||||
|
}
|
||||||
|
file->state.current_record_index = current_index;
|
||||||
|
}
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
done:;
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function b32
|
||||||
|
edit_batch_check(Thread_Context *tctx, Profile_Global_List *list, Batch_Edit *batch){
|
||||||
|
ProfileTLScope(tctx, list, "batch check");
|
||||||
|
b32 result = true;
|
||||||
|
Range_i64 prev_range = Ii64(-1, 0);
|
||||||
|
for (;batch != 0;
|
||||||
|
batch = batch->next){
|
||||||
|
if (batch->edit.range.first <= prev_range.first ||
|
||||||
|
batch->edit.range.first < prev_range.one_past_last){
|
||||||
|
result = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function b32
|
||||||
|
edit_batch(Thread_Context *tctx, Models *models, Editing_File *file,
|
||||||
|
Batch_Edit *batch, Edit_Behaviors behaviors){
|
||||||
|
b32 result = true;
|
||||||
|
if (batch != 0){
|
||||||
|
if (!edit_batch_check(tctx, &models->profile_list, batch)){
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
ProfileTLScope(tctx, &models->profile_list, "batch apply");
|
||||||
|
|
||||||
|
pre_edit_state_change(models, file);
|
||||||
|
pre_edit_history_prep(file, behaviors);
|
||||||
|
|
||||||
|
History_Record_Index start_index = 0;
|
||||||
|
if (history_is_activated(&file->state.history)){
|
||||||
|
start_index = file->state.current_record_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProfileTLBlockNamed(tctx, &models->profile_list, "batch text edits", profile_edits);
|
||||||
|
|
||||||
|
Range_i64 old_range = {};
|
||||||
|
old_range.min = batch->edit.range.min;
|
||||||
|
for (Batch_Edit *edit = batch;
|
||||||
|
edit != 0;
|
||||||
|
edit = edit->next){
|
||||||
|
if (edit->next == 0){
|
||||||
|
old_range.max = edit->edit.range.max;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Range_Cursor cursor_range = {};
|
||||||
|
cursor_range.min = file_compute_cursor(file, seek_pos(old_range.min));
|
||||||
|
cursor_range.max = file_compute_cursor(file, seek_pos(old_range.max));
|
||||||
|
|
||||||
|
Range_i64 new_range = Ii64_neg_inf;
|
||||||
|
Gap_Buffer *buffer = &file->state.buffer;
|
||||||
|
|
||||||
|
i32 batch_count = 0;
|
||||||
|
i64 shift = 0;
|
||||||
|
for (Batch_Edit *edit = batch;
|
||||||
|
edit != 0;
|
||||||
|
edit = edit->next){
|
||||||
|
String_Const_u8 insert_string = edit->edit.text;
|
||||||
|
|
||||||
|
Range_i64 edit_range = edit->edit.range;
|
||||||
|
edit_range.first += shift;
|
||||||
|
edit_range.one_past_last += shift;
|
||||||
|
|
||||||
|
new_range.min = Min(new_range.min, edit_range.min);
|
||||||
|
i64 new_max = (i64)(edit_range.min + insert_string.size);
|
||||||
|
new_range.max = Max(new_range.max, new_max);
|
||||||
|
|
||||||
|
i64 size = buffer_size(buffer);
|
||||||
|
if (0 <= edit_range.first &&
|
||||||
|
edit_range.first <= edit_range.one_past_last &&
|
||||||
|
edit_range.one_past_last <= size){
|
||||||
|
edit__apply(tctx, models, file, edit_range, insert_string,
|
||||||
|
behaviors);
|
||||||
|
shift += replace_range_shift(edit_range, insert_string.size);
|
||||||
|
batch_count += 1;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
result = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ProfileCloseNow(profile_edits);
|
||||||
|
|
||||||
|
if (history_is_activated(&file->state.history)){
|
||||||
|
History_Record_Index last_index = file->state.current_record_index;
|
||||||
|
if (start_index + 1 < last_index){
|
||||||
|
edit_merge_history_range(tctx, models, file,
|
||||||
|
start_index + 1, last_index,
|
||||||
|
RecordMergeFlag_StateInRange_ErrorOut);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file_clear_layout_cache(file);
|
||||||
|
|
||||||
|
edit_fix_markers(tctx, models, file, batch);
|
||||||
|
|
||||||
|
post_edit_call_hook(tctx, models, file, new_range, cursor_range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
function Editing_File*
|
||||||
|
create_file(Thread_Context *tctx, Models *models, String_Const_u8 file_name, Buffer_Create_Flag flags){
|
||||||
|
Editing_File *result = 0;
|
||||||
|
|
||||||
|
if (file_name.size > 0){
|
||||||
|
Working_Set *working_set = &models->working_set;
|
||||||
|
Heap *heap = &models->heap;
|
||||||
|
|
||||||
|
Scratch_Block scratch(tctx);
|
||||||
|
|
||||||
|
Editing_File *file = 0;
|
||||||
|
b32 do_empty_buffer = false;
|
||||||
|
Editing_File_Name canon = {};
|
||||||
|
b32 has_canon_name = false;
|
||||||
|
b32 buffer_is_for_new_file = false;
|
||||||
|
|
||||||
|
// NOTE(allen): Try to get the file by canon name.
|
||||||
|
if (HasFlag(flags, BufferCreate_NeverAttachToFile) == 0){
|
||||||
|
if (get_canon_name(scratch, file_name, &canon)){
|
||||||
|
has_canon_name = true;
|
||||||
|
file = working_set_contains_canon(working_set, string_from_file_name(&canon));
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
do_empty_buffer = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(allen): Try to get the file by buffer name.
|
||||||
|
if ((flags & BufferCreate_MustAttachToFile) == 0){
|
||||||
|
if (file == 0){
|
||||||
|
file = working_set_contains_name(working_set, file_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(allen): If there is still no file, create a new buffer.
|
||||||
|
if (file == 0){
|
||||||
|
Plat_Handle handle = {};
|
||||||
|
|
||||||
|
// NOTE(allen): Figure out whether this is a new file, or an existing file.
|
||||||
|
if (!do_empty_buffer){
|
||||||
|
if ((flags & BufferCreate_AlwaysNew) != 0){
|
||||||
|
do_empty_buffer = true;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
if (!system_load_handle(scratch, (char*)canon.name_space, &handle)){
|
||||||
|
do_empty_buffer = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (do_empty_buffer){
|
||||||
|
if (has_canon_name){
|
||||||
|
buffer_is_for_new_file = true;
|
||||||
|
}
|
||||||
|
if (!HasFlag(flags, BufferCreate_NeverNew)){
|
||||||
|
file = working_set_allocate_file(working_set, &models->lifetime_allocator);
|
||||||
|
if (file != 0){
|
||||||
|
if (has_canon_name){
|
||||||
|
file_bind_file_name(working_set, file, string_from_file_name(&canon));
|
||||||
|
}
|
||||||
|
String_Const_u8 front = string_front_of_path(file_name);
|
||||||
|
buffer_bind_name(tctx, models, scratch, working_set, file, front);
|
||||||
|
File_Attributes attributes = {};
|
||||||
|
file_create_from_string(tctx, models, file, SCu8(""), attributes);
|
||||||
|
result = file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
File_Attributes attributes = system_load_attributes(handle);
|
||||||
|
b32 in_heap_mem = false;
|
||||||
|
char *buffer = push_array(scratch, char, (i32)attributes.size);
|
||||||
|
|
||||||
|
if (buffer == 0){
|
||||||
|
buffer = heap_array(heap, char, (i32)attributes.size);
|
||||||
|
Assert(buffer != 0);
|
||||||
|
in_heap_mem = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (system_load_file(handle, buffer, (i32)attributes.size)){
|
||||||
|
system_load_close(handle);
|
||||||
|
file = working_set_allocate_file(working_set, &models->lifetime_allocator);
|
||||||
|
if (file != 0){
|
||||||
|
file_bind_file_name(working_set, file, string_from_file_name(&canon));
|
||||||
|
String_Const_u8 front = string_front_of_path(file_name);
|
||||||
|
buffer_bind_name(tctx, models, scratch, working_set, file, front);
|
||||||
|
file_create_from_string(tctx, models, file, SCu8(buffer, (i32)attributes.size), attributes);
|
||||||
|
result = file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
system_load_close(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_heap_mem){
|
||||||
|
heap_free(heap, buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
result = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file != 0 && HasFlag(flags, BufferCreate_JustChangedFile)){
|
||||||
|
file->state.save_state = FileSaveState_SavedWaitingForNotification;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file != 0 && HasFlag(flags, BufferCreate_AlwaysNew)){
|
||||||
|
i64 size = buffer_size(&file->state.buffer);
|
||||||
|
if (size > 0){
|
||||||
|
Edit_Behaviors behaviors = {};
|
||||||
|
edit_single(tctx, models, file, Ii64(0, size), string_u8_litexpr(""), behaviors);
|
||||||
|
if (has_canon_name){
|
||||||
|
buffer_is_for_new_file = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file != 0 && buffer_is_for_new_file &&
|
||||||
|
!HasFlag(flags, BufferCreate_SuppressNewFileHook) &&
|
||||||
|
models->new_file != 0){
|
||||||
|
Application_Links app = {};
|
||||||
|
app.tctx = tctx;
|
||||||
|
app.cmd_context = models;
|
||||||
|
models->new_file(&app, file->id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 07.02.2019
|
||||||
|
*
|
||||||
|
* Types used for edit operations
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#if !defined(FRED_EDIT_H)
|
||||||
|
#define FRED_EDIT_H
|
||||||
|
|
||||||
|
struct Edit_Behaviors{
|
||||||
|
b32 do_not_post_to_history;
|
||||||
|
i64 pos_before_edit;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,568 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 03.01.2017
|
||||||
|
*
|
||||||
|
* File layer for 4coder
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
internal String_Const_u8
|
||||||
|
string_from_file_name(Editing_File_Name *name){
|
||||||
|
return(SCu8(name->name_space, name->name_size));
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
internal void
|
||||||
|
file_edit_positions_set_cursor(File_Edit_Positions *edit_pos, i64 pos){
|
||||||
|
edit_pos->cursor_pos = pos;
|
||||||
|
edit_pos->last_set_type = EditPos_CursorSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
file_edit_positions_set_scroll(File_Edit_Positions *edit_pos, Buffer_Scroll scroll){
|
||||||
|
edit_pos->scroll = scroll;
|
||||||
|
edit_pos->last_set_type = EditPos_ScrollSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
file_edit_positions_push(Editing_File *file, File_Edit_Positions edit_pos){
|
||||||
|
if (file->state.edit_pos_stack_top + 1 < ArrayCount(file->state.edit_pos_stack)){
|
||||||
|
file->state.edit_pos_stack_top += 1;
|
||||||
|
file->state.edit_pos_stack[file->state.edit_pos_stack_top] = edit_pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal File_Edit_Positions
|
||||||
|
file_edit_positions_pop(Editing_File *file){
|
||||||
|
File_Edit_Positions edit_pos = {};
|
||||||
|
if (file->state.edit_pos_stack_top >= 0){
|
||||||
|
edit_pos = file->state.edit_pos_stack[file->state.edit_pos_stack_top];
|
||||||
|
file->state.edit_pos_stack_top -= 1;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
edit_pos = file->state.edit_pos_most_recent;
|
||||||
|
}
|
||||||
|
return(edit_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
internal Face*
|
||||||
|
file_get_face(Models *models, Editing_File *file){
|
||||||
|
return(font_set_face_from_id(&models->font_set, file->settings.face_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Access_Flag
|
||||||
|
file_get_access_flags(Editing_File *file){
|
||||||
|
Access_Flag flags = Access_Read|Access_Visible;
|
||||||
|
if (!file->settings.read_only){
|
||||||
|
flags |= Access_Write;
|
||||||
|
}
|
||||||
|
return(flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal b32
|
||||||
|
file_needs_save(Editing_File *file){
|
||||||
|
b32 result = false;
|
||||||
|
if (HasFlag(file->state.dirty, DirtyState_UnsavedChanges)){
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal b32
|
||||||
|
file_can_save(Editing_File *file){
|
||||||
|
b32 result = false;
|
||||||
|
if (HasFlag(file->state.dirty, DirtyState_UnsavedChanges) ||
|
||||||
|
HasFlag(file->state.dirty, DirtyState_UnloadedChanges)){
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
file_set_unimportant(Editing_File *file, b32 val){
|
||||||
|
if (val){
|
||||||
|
file->state.dirty = DirtyState_UpToDate;
|
||||||
|
}
|
||||||
|
file->settings.unimportant = (b8)(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
file_add_dirty_flag(Editing_File *file, Dirty_State state){
|
||||||
|
if (!file->settings.unimportant){
|
||||||
|
file->state.dirty |= state;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
file->state.dirty = DirtyState_UpToDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
file_clear_dirty_flags(Editing_File *file){
|
||||||
|
file->state.dirty = DirtyState_UpToDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
internal void
|
||||||
|
file_name_terminate(Editing_File_Name *name){
|
||||||
|
u64 size = name->name_size;
|
||||||
|
size = clamp_top(size, sizeof(name->name_space) - 1);
|
||||||
|
name->name_space[size] = 0;
|
||||||
|
name->name_size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
// TODO(allen): file_name should be String_Const_u8
|
||||||
|
internal b32
|
||||||
|
save_file_to_name(Thread_Context *tctx, Models *models, Editing_File *file, u8 *file_name){
|
||||||
|
b32 result = false;
|
||||||
|
b32 using_actual_file_name = false;
|
||||||
|
|
||||||
|
if (file_name == 0){
|
||||||
|
file_name_terminate(&file->canon);
|
||||||
|
file_name = file->canon.name_space;
|
||||||
|
using_actual_file_name = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file_name != 0){
|
||||||
|
if (models->save_file != 0){
|
||||||
|
Application_Links app = {};
|
||||||
|
app.tctx = tctx;
|
||||||
|
app.cmd_context = models;
|
||||||
|
models->save_file(&app, file->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Gap_Buffer *buffer = &file->state.buffer;
|
||||||
|
b32 dos_write_mode = file->settings.dos_write_mode;
|
||||||
|
|
||||||
|
Scratch_Block scratch(tctx);
|
||||||
|
|
||||||
|
if (!using_actual_file_name){
|
||||||
|
String_Const_u8 s_file_name = SCu8(file_name);
|
||||||
|
String_Const_u8 canonical_file_name = system_get_canonical(scratch, s_file_name);
|
||||||
|
if (string_match(canonical_file_name, string_from_file_name(&file->canon))){
|
||||||
|
using_actual_file_name = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String_Const_u8 saveable_string = buffer_stringify(scratch, buffer, Ii64(0, buffer_size(buffer)));
|
||||||
|
|
||||||
|
File_Attributes new_attributes = system_save_file(scratch, (char*)file_name, saveable_string);
|
||||||
|
if (new_attributes.last_write_time > 0 &&
|
||||||
|
using_actual_file_name){
|
||||||
|
file->state.save_state = FileSaveState_SavedWaitingForNotification;
|
||||||
|
file_clear_dirty_flags(file);
|
||||||
|
}
|
||||||
|
LogEventF(log_string(M), scratch, file->id, 0, system_thread_get_id(),
|
||||||
|
"save file [last_write_time=0x%llx]", new_attributes.last_write_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
internal Buffer_Cursor
|
||||||
|
file_compute_cursor(Editing_File *file, Buffer_Seek seek){
|
||||||
|
Buffer_Cursor result = {};
|
||||||
|
switch (seek.type){
|
||||||
|
case buffer_seek_pos:
|
||||||
|
{
|
||||||
|
result = buffer_cursor_from_pos(&file->state.buffer, seek.pos);
|
||||||
|
}break;
|
||||||
|
case buffer_seek_line_col:
|
||||||
|
{
|
||||||
|
result = buffer_cursor_from_line_col(&file->state.buffer, seek.line, seek.col);
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
function Layout_Function*
|
||||||
|
file_get_layout_func(Editing_File *file){
|
||||||
|
return(file->settings.layout_func);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
file_create_from_string(Thread_Context *tctx, Models *models, Editing_File *file, String_Const_u8 val, File_Attributes attributes){
|
||||||
|
Scratch_Block scratch(tctx);
|
||||||
|
|
||||||
|
Base_Allocator *allocator = tctx->allocator;
|
||||||
|
block_zero_struct(&file->state);
|
||||||
|
buffer_init(&file->state.buffer, val.str, val.size, allocator);
|
||||||
|
|
||||||
|
if (buffer_size(&file->state.buffer) < (i64)val.size){
|
||||||
|
file->settings.dos_write_mode = true;
|
||||||
|
}
|
||||||
|
file_clear_dirty_flags(file);
|
||||||
|
file->attributes = attributes;
|
||||||
|
|
||||||
|
file->settings.layout_func = models->layout_func;
|
||||||
|
file->settings.face_id = models->global_face_id;
|
||||||
|
|
||||||
|
buffer_measure_starts(scratch, &file->state.buffer);
|
||||||
|
|
||||||
|
file->lifetime_object = lifetime_alloc_object(&models->lifetime_allocator, DynamicWorkspace_Buffer, file);
|
||||||
|
history_init(tctx, models, &file->state.history);
|
||||||
|
|
||||||
|
file->state.cached_layouts_arena = make_arena(allocator);
|
||||||
|
file->state.line_layout_table = make_table_Data_u64(allocator, 500);
|
||||||
|
|
||||||
|
file->settings.is_initialized = true;
|
||||||
|
|
||||||
|
{
|
||||||
|
Temp_Memory temp = begin_temp(scratch);
|
||||||
|
String_Const_u8 name = SCu8(file->unique_name.name_space, file->unique_name.name_size);
|
||||||
|
name = string_escape(scratch, name);
|
||||||
|
LogEventF(log_string(M), scratch, file->id, 0, system_thread_get_id(),
|
||||||
|
"init file [lwt=0x%llx] [name=\"%.*s\"]",
|
||||||
|
attributes.last_write_time, string_expand(name));
|
||||||
|
end_temp(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
if (models->begin_buffer != 0){
|
||||||
|
Application_Links app = {};
|
||||||
|
app.tctx = tctx;
|
||||||
|
app.cmd_context = models;
|
||||||
|
models->begin_buffer(&app, file->id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
file_free(Thread_Context *tctx, Models *models, Editing_File *file){
|
||||||
|
Lifetime_Allocator *lifetime_allocator = &models->lifetime_allocator;
|
||||||
|
Working_Set *working_set = &models->working_set;
|
||||||
|
|
||||||
|
lifetime_free_object(lifetime_allocator, file->lifetime_object);
|
||||||
|
|
||||||
|
Gap_Buffer *buffer = &file->state.buffer;
|
||||||
|
if (buffer->data){
|
||||||
|
base_free(buffer->allocator, buffer->data);
|
||||||
|
base_free(buffer->allocator, buffer->line_starts);
|
||||||
|
}
|
||||||
|
|
||||||
|
history_free(tctx, &file->state.history);
|
||||||
|
|
||||||
|
linalloc_clear(&file->state.cached_layouts_arena);
|
||||||
|
table_free(&file->state.line_layout_table);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
internal i32
|
||||||
|
file_get_current_record_index(Editing_File *file){
|
||||||
|
return(file->state.current_record_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Managed_Scope
|
||||||
|
file_get_managed_scope(Editing_File *file){
|
||||||
|
Managed_Scope scope = 0;
|
||||||
|
if (file != 0){
|
||||||
|
Assert(file->lifetime_object != 0);
|
||||||
|
scope = (Managed_Scope)file->lifetime_object->workspace.scope_id;
|
||||||
|
}
|
||||||
|
return(scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
internal Layout_Item_List
|
||||||
|
file_get_line_layout(Thread_Context *tctx, Models *models, Editing_File *file,
|
||||||
|
Layout_Function *layout_func, f32 width, Face *face, i64 line_number){
|
||||||
|
Layout_Item_List result = {};
|
||||||
|
|
||||||
|
i64 line_count = buffer_line_count(&file->state.buffer);
|
||||||
|
if (1 <= line_number && line_number <= line_count){
|
||||||
|
Line_Layout_Key key = {};
|
||||||
|
key.face_id = face->id;
|
||||||
|
key.face_version_number = face->version_number;
|
||||||
|
key.width = width;
|
||||||
|
key.line_number = line_number;
|
||||||
|
|
||||||
|
String_Const_u8 key_data = make_data_struct(&key);
|
||||||
|
|
||||||
|
Layout_Item_List *list = 0;
|
||||||
|
|
||||||
|
Table_Lookup lookup = table_lookup(&file->state.line_layout_table, key_data);
|
||||||
|
if (lookup.found_match){
|
||||||
|
u64 val = 0;
|
||||||
|
table_read(&file->state.line_layout_table, lookup, &val);
|
||||||
|
list = (Layout_Item_List*)IntAsPtr(val);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
list = push_array(&file->state.cached_layouts_arena, Layout_Item_List, 1);
|
||||||
|
Range_i64 line_range = buffer_get_pos_range_from_line_number(&file->state.buffer, line_number);
|
||||||
|
|
||||||
|
Application_Links app = {};
|
||||||
|
app.tctx = tctx;
|
||||||
|
app.cmd_context = models;
|
||||||
|
*list = layout_func(&app, &file->state.cached_layouts_arena,
|
||||||
|
file->id, line_range, face->id, width);
|
||||||
|
key_data = push_data_copy(&file->state.cached_layouts_arena, key_data);
|
||||||
|
table_insert(&file->state.line_layout_table, key_data, (u64)PtrAsInt(list));
|
||||||
|
}
|
||||||
|
block_copy_struct(&result, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
file_clear_layout_cache(Editing_File *file){
|
||||||
|
linalloc_clear(&file->state.cached_layouts_arena);
|
||||||
|
table_clear(&file->state.line_layout_table);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Line_Shift_Vertical
|
||||||
|
file_line_shift_y(Thread_Context *tctx, Models *models, Editing_File *file,
|
||||||
|
Layout_Function *layout_func, f32 width, Face *face,
|
||||||
|
i64 line_number, f32 y_delta){
|
||||||
|
Line_Shift_Vertical result = {};
|
||||||
|
|
||||||
|
f32 line_y = 0.f;
|
||||||
|
|
||||||
|
if (y_delta < 0.f){
|
||||||
|
// NOTE(allen): Iterating upward
|
||||||
|
b32 has_result = false;
|
||||||
|
for (;;){
|
||||||
|
if (line_y <= y_delta){
|
||||||
|
has_result = true;
|
||||||
|
result.line = line_number;
|
||||||
|
result.y_delta = line_y;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
line_number -= 1;
|
||||||
|
if (line_number <= 0){
|
||||||
|
line_number = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Layout_Item_List line = file_get_line_layout(tctx, models, file, layout_func,
|
||||||
|
width, face, line_number);
|
||||||
|
line_y -= line.height;
|
||||||
|
}
|
||||||
|
if (!has_result){
|
||||||
|
result.line = line_number;
|
||||||
|
result.y_delta = line_y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
// NOTE(allen): Iterating downward
|
||||||
|
b32 has_result = false;
|
||||||
|
i64 line_count = buffer_line_count(&file->state.buffer);
|
||||||
|
for (;;line_number += 1){
|
||||||
|
Layout_Item_List line = file_get_line_layout(tctx, models, file, layout_func,
|
||||||
|
width, face, line_number);
|
||||||
|
f32 next_y = line_y + line.height;
|
||||||
|
if (y_delta < next_y){
|
||||||
|
has_result = true;
|
||||||
|
result.line = line_number;
|
||||||
|
result.y_delta = line_y;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (line_number >= line_count){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
line_y = next_y;
|
||||||
|
}
|
||||||
|
if (!has_result){
|
||||||
|
result.line = line_number;
|
||||||
|
result.y_delta = line_y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal f32
|
||||||
|
file_line_y_difference(Thread_Context *tctx, Models *models, Editing_File *file,
|
||||||
|
Layout_Function *layout_func, f32 width, Face *face,
|
||||||
|
i64 line_a, i64 line_b){
|
||||||
|
f32 result = 0.f;
|
||||||
|
if (line_a != line_b){
|
||||||
|
Range_i64 line_range = Ii64(line_a, line_b);
|
||||||
|
for (i64 i = line_range.min; i < line_range.max; i += 1){
|
||||||
|
Layout_Item_List line = file_get_line_layout(tctx, models, file, layout_func, width, face, i);
|
||||||
|
result += line.height;
|
||||||
|
}
|
||||||
|
if (line_a < line_b){
|
||||||
|
result *= -1.f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal i64
|
||||||
|
file_pos_at_relative_xy(Thread_Context *tctx, Models *models, Editing_File *file,
|
||||||
|
Layout_Function *layout_func, f32 width, Face *face,
|
||||||
|
i64 base_line, Vec2_f32 relative_xy){
|
||||||
|
Line_Shift_Vertical shift = file_line_shift_y(tctx, models, file, layout_func, width, face, base_line, relative_xy.y);
|
||||||
|
relative_xy.y -= shift.y_delta;
|
||||||
|
Layout_Item_List line = file_get_line_layout(tctx, models, file, layout_func, width, face, shift.line);
|
||||||
|
return(layout_nearest_pos_to_xy(line, relative_xy));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Rect_f32
|
||||||
|
file_relative_box_of_pos(Thread_Context *tctx, Models *models, Editing_File *file,
|
||||||
|
Layout_Function *layout_func, f32 width, Face *face,
|
||||||
|
i64 base_line, i64 pos){
|
||||||
|
i64 line_number = buffer_get_line_index(&file->state.buffer, pos) + 1;
|
||||||
|
Layout_Item_List line = file_get_line_layout(tctx, models, file, layout_func, width, face, line_number);
|
||||||
|
Rect_f32 result = layout_box_of_pos(line, pos);
|
||||||
|
|
||||||
|
f32 y_difference = file_line_y_difference(tctx, models, file, layout_func, width, face, line_number, base_line);
|
||||||
|
result.y0 += y_difference;
|
||||||
|
result.y1 += y_difference;
|
||||||
|
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Vec2_f32
|
||||||
|
file_relative_xy_of_pos(Thread_Context *tctx, Models *models, Editing_File *file,
|
||||||
|
Layout_Function *layout_func, f32 width, Face *face,
|
||||||
|
i64 base_line, i64 pos){
|
||||||
|
Rect_f32 rect = file_relative_box_of_pos(tctx, models, file, layout_func, width, face, base_line, pos);
|
||||||
|
return(rect_center(rect));
|
||||||
|
}
|
||||||
|
|
||||||
|
function Rect_f32
|
||||||
|
file_padded_box_of_pos(Thread_Context *tctx, Models *models, Editing_File *file,
|
||||||
|
Layout_Function *layout_func, f32 width, Face *face,
|
||||||
|
i64 base_line, i64 pos){
|
||||||
|
i64 line_number = buffer_get_line_index(&file->state.buffer, pos) + 1;
|
||||||
|
Layout_Item_List line = file_get_line_layout(tctx, models, file, layout_func, width, face, line_number);
|
||||||
|
Rect_f32 result = layout_padded_box_of_pos(line, pos);
|
||||||
|
|
||||||
|
f32 y_difference = file_line_y_difference(tctx, models, file, layout_func, width, face, line_number, base_line);
|
||||||
|
result.y0 += y_difference;
|
||||||
|
result.y1 += y_difference;
|
||||||
|
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Buffer_Point
|
||||||
|
file_normalize_buffer_point(Thread_Context *tctx, Models *models, Editing_File *file,
|
||||||
|
Layout_Function *layout_func, f32 width, Face *face,
|
||||||
|
Buffer_Point point){
|
||||||
|
Line_Shift_Vertical shift = file_line_shift_y(tctx, models, file, layout_func, width, face, point.line_number, point.pixel_shift.y);
|
||||||
|
point.line_number = shift.line;
|
||||||
|
point.pixel_shift.y -= shift.y_delta;
|
||||||
|
point.pixel_shift.x = clamp_bot(0.f, point.pixel_shift.x);
|
||||||
|
point.pixel_shift.y = clamp_bot(0.f, point.pixel_shift.y);
|
||||||
|
return(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Vec2_f32
|
||||||
|
file_buffer_point_difference(Thread_Context *tctx, Models *models, Editing_File *file,
|
||||||
|
Layout_Function *layout_func, f32 width, Face *face,
|
||||||
|
Buffer_Point a, Buffer_Point b){
|
||||||
|
f32 y_difference = file_line_y_difference(tctx, models, file, layout_func, width, face, a.line_number, b.line_number);
|
||||||
|
Vec2_f32 result = a.pixel_shift - b.pixel_shift;
|
||||||
|
result.y += y_difference;
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Line_Shift_Character
|
||||||
|
file_line_shift_characters(Thread_Context *tctx, Models *models, Editing_File *file, Layout_Function *layout_func, f32 width, Face *face, i64 line_number, i64 character_delta){
|
||||||
|
Line_Shift_Character result = {};
|
||||||
|
|
||||||
|
i64 line_character = 0;
|
||||||
|
|
||||||
|
if (character_delta < 0){
|
||||||
|
// NOTE(allen): Iterating upward
|
||||||
|
b32 has_result = false;
|
||||||
|
for (;;){
|
||||||
|
if (line_character <= character_delta){
|
||||||
|
has_result = true;
|
||||||
|
result.line = line_number;
|
||||||
|
result.character_delta = line_character;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
line_number -= 1;
|
||||||
|
if (line_number <= 0){
|
||||||
|
line_number = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Layout_Item_List line = file_get_line_layout(tctx, models, file, layout_func, width, face, line_number);
|
||||||
|
line_character -= line.character_count;
|
||||||
|
}
|
||||||
|
if (!has_result){
|
||||||
|
result.line = line_number;
|
||||||
|
result.character_delta = line_character;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
// NOTE(allen): Iterating downward
|
||||||
|
b32 has_result = false;
|
||||||
|
i64 line_count = buffer_line_count(&file->state.buffer);
|
||||||
|
for (;;line_number += 1){
|
||||||
|
Layout_Item_List line = file_get_line_layout(tctx, models, file, layout_func, width, face, line_number);
|
||||||
|
i64 next_character = line_character + line.character_count;
|
||||||
|
if (character_delta < next_character){
|
||||||
|
has_result = true;
|
||||||
|
result.line = line_number;
|
||||||
|
result.character_delta = line_character;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (line_number >= line_count){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
line_character = next_character;
|
||||||
|
}
|
||||||
|
if (!has_result){
|
||||||
|
result.line = line_number;
|
||||||
|
result.character_delta = line_character;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal i64
|
||||||
|
file_line_character_difference(Thread_Context *tctx, Models *models, Editing_File *file, Layout_Function *layout_func, f32 width, Face *face, i64 line_a, i64 line_b){
|
||||||
|
i64 result = 0;
|
||||||
|
if (line_a != line_b){
|
||||||
|
Range_i64 line_range = Ii64(line_a, line_b);
|
||||||
|
for (i64 i = line_range.min; i < line_range.max; i += 1){
|
||||||
|
Layout_Item_List line = file_get_line_layout(tctx, models, file, layout_func, width, face, i);
|
||||||
|
result += line.character_count;
|
||||||
|
}
|
||||||
|
if (line_a < line_b){
|
||||||
|
result *= -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal i64
|
||||||
|
file_pos_from_relative_character(Thread_Context *tctx, Models *models, Editing_File *file,
|
||||||
|
Layout_Function *layout_func, f32 width, Face *face,
|
||||||
|
i64 base_line, i64 relative_character){
|
||||||
|
Line_Shift_Character shift = file_line_shift_characters(tctx, models, file, layout_func, width, face, base_line, relative_character);
|
||||||
|
relative_character -= shift.character_delta;
|
||||||
|
Layout_Item_List line = file_get_line_layout(tctx, models, file, layout_func, width, face, shift.line);
|
||||||
|
return(layout_get_pos_at_character(line, relative_character));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal i64
|
||||||
|
file_relative_character_from_pos(Thread_Context *tctx, Models *models, Editing_File *file, Layout_Function *layout_func, f32 width, Face *face,
|
||||||
|
i64 base_line, i64 pos){
|
||||||
|
i64 line_number = buffer_get_line_index(&file->state.buffer, pos) + 1;
|
||||||
|
Layout_Item_List line = file_get_line_layout(tctx, models, file, layout_func, width, face, line_number);
|
||||||
|
i64 result = layout_character_from_pos(line, pos);
|
||||||
|
result += file_line_character_difference(tctx, models, file, layout_func, width, face, line_number, base_line);
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 24.01.2018
|
||||||
|
*
|
||||||
|
* Buffer types
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#if !defined(FRED_FILE_H)
|
||||||
|
#define FRED_FILE_H
|
||||||
|
|
||||||
|
typedef i32 Edit_Pos_Set_Type;
|
||||||
|
enum{
|
||||||
|
EditPos_None,
|
||||||
|
EditPos_CursorSet,
|
||||||
|
EditPos_ScrollSet
|
||||||
|
};
|
||||||
|
struct File_Edit_Positions{
|
||||||
|
Edit_Pos_Set_Type last_set_type;
|
||||||
|
Buffer_Scroll scroll;
|
||||||
|
i64 cursor_pos;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Editing_File_Settings{
|
||||||
|
Layout_Function *layout_func;
|
||||||
|
Face_ID face_id;
|
||||||
|
b8 dos_write_mode;
|
||||||
|
b8 is_initialized;
|
||||||
|
b8 unimportant;
|
||||||
|
b8 read_only;
|
||||||
|
b8 unkillable;
|
||||||
|
b8 never_kill;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Line_Layout_Key{
|
||||||
|
Face_ID face_id;
|
||||||
|
i32 face_version_number;
|
||||||
|
f32 width;
|
||||||
|
i64 line_number;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef i32 File_Save_State;
|
||||||
|
enum{
|
||||||
|
FileSaveState_Normal,
|
||||||
|
FileSaveState_SavedWaitingForNotification,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Editing_File_State{
|
||||||
|
Gap_Buffer buffer;
|
||||||
|
|
||||||
|
History history;
|
||||||
|
i32 current_record_index;
|
||||||
|
|
||||||
|
Dirty_State dirty;
|
||||||
|
File_Save_State save_state;
|
||||||
|
|
||||||
|
File_Edit_Positions edit_pos_most_recent;
|
||||||
|
File_Edit_Positions edit_pos_stack[16];
|
||||||
|
i32 edit_pos_stack_top;
|
||||||
|
|
||||||
|
Child_Process_ID attached_child_process;
|
||||||
|
|
||||||
|
Arena cached_layouts_arena;
|
||||||
|
Table_Data_u64 line_layout_table;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Editing_File_Name{
|
||||||
|
u8 name_space[256];
|
||||||
|
u64 name_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Editing_File{
|
||||||
|
union{
|
||||||
|
Editing_File *next;
|
||||||
|
Node main_chain_node;
|
||||||
|
};
|
||||||
|
Node touch_node;
|
||||||
|
Node external_mod_node;
|
||||||
|
Buffer_ID id;
|
||||||
|
Editing_File_Settings settings;
|
||||||
|
Editing_File_State state;
|
||||||
|
File_Attributes attributes;
|
||||||
|
Lifetime_Object *lifetime_object;
|
||||||
|
Editing_File_Name base_name;
|
||||||
|
Editing_File_Name unique_name;
|
||||||
|
Editing_File_Name canon;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Buffer_Point_Delta{
|
||||||
|
Buffer_Point new_point;
|
||||||
|
f32 y_shift;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 02.10.2019
|
||||||
|
*
|
||||||
|
* Font API definition program.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#include "4ed_api_definition_main.cpp"
|
||||||
|
|
||||||
|
function API_Definition*
|
||||||
|
define_api(Arena *arena){
|
||||||
|
API_Definition *api = begin_api(arena, "font");
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "make_face", "Face*");
|
||||||
|
api_param(arena, call, "Arena*", "arena");
|
||||||
|
api_param(arena, call, "Face_Description*", "description");
|
||||||
|
api_param(arena, call, "f32", "scale_factor");
|
||||||
|
}
|
||||||
|
|
||||||
|
return(api);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Generated_Group
|
||||||
|
get_api_group(void){
|
||||||
|
return(GeneratedGroup_Core);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 11.03.2017
|
||||||
|
*
|
||||||
|
* Font system interface.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#if !defined(FCODER_FONT_INTERFACE_H)
|
||||||
|
#define FCODER_FONT_INTERFACE_H
|
||||||
|
|
||||||
|
typedef i32 Texture_Kind;
|
||||||
|
enum{
|
||||||
|
TextureKind_Error,
|
||||||
|
TextureKind_Mono,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef u32 Graphics_Get_Texture_Function(Vec3_i32 dim, Texture_Kind texture_kind);
|
||||||
|
typedef b32 Graphics_Fill_Texture_Function(Texture_Kind texture_kind, u32 texture,
|
||||||
|
Vec3_i32 p, Vec3_i32 dim, void *data);
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
struct Glyph_Bounds{
|
||||||
|
Rect_f32 uv;
|
||||||
|
f32 w;
|
||||||
|
Rect_f32 xy_off;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Face{
|
||||||
|
Face_Description description;
|
||||||
|
Face_ID id;
|
||||||
|
i32 version_number;
|
||||||
|
|
||||||
|
// NOTE(allen): Metrics
|
||||||
|
Face_Metrics metrics;
|
||||||
|
|
||||||
|
// NOTE(allen): Glyph data
|
||||||
|
Face_Advance_Map advance_map;
|
||||||
|
Glyph_Bounds *bounds;
|
||||||
|
Glyph_Bounds white;
|
||||||
|
|
||||||
|
Texture_Kind texture_kind;
|
||||||
|
u32 texture;
|
||||||
|
Vec3_f32 texture_dim;
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
// NOTE(allen): Platform layer calls - implemented in a "font provider"
|
||||||
|
typedef Face *Font_Make_Face_Function(Arena *arena, Face_Description *description, f32 scale_factor);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,371 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 18.07.2017
|
||||||
|
*
|
||||||
|
* Freetype implementation of the font provider interface.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
// NOTE(allen): Thanks to insofaras. This is copy-pasted from some work he originally did to get free type working on Linux.
|
||||||
|
|
||||||
|
#undef internal
|
||||||
|
#include <ft2build.h>
|
||||||
|
#include FT_FREETYPE_H
|
||||||
|
#define internal static
|
||||||
|
|
||||||
|
internal u32
|
||||||
|
ft__load_flags(b32 use_hinting){
|
||||||
|
u32 ft_flags = FT_LOAD_RENDER;
|
||||||
|
if (use_hinting){
|
||||||
|
// NOTE(inso): FT_LOAD_TARGET_LIGHT does hinting only vertically, which looks nicer imo
|
||||||
|
// maybe it could be exposed as an option for hinting, instead of just on/off.
|
||||||
|
ft_flags |= (FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
ft_flags |= (FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING);
|
||||||
|
}
|
||||||
|
return(ft_flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal FT_Codepoint_Index_Pair_Array
|
||||||
|
ft__get_codepoint_index_pairs(Arena *arena, FT_Face face, u16 *maximum_index_out){
|
||||||
|
FT_Long glyph_count = face->num_glyphs;
|
||||||
|
|
||||||
|
FT_Codepoint_Index_Pair_Array array = {};
|
||||||
|
array.count = glyph_count;
|
||||||
|
array.vals = push_array(arena, FT_Codepoint_Index_Pair, glyph_count);
|
||||||
|
|
||||||
|
u16 maximum_index = 0;
|
||||||
|
|
||||||
|
i32 counter = 0;
|
||||||
|
FT_UInt index = 0;
|
||||||
|
FT_ULong codepoint = FT_Get_First_Char(face, &index);
|
||||||
|
array.vals[counter].codepoint = codepoint;
|
||||||
|
array.vals[counter].index = (u16)index;
|
||||||
|
maximum_index = Max(maximum_index, (u16)index);
|
||||||
|
counter += 1;
|
||||||
|
for (;;){
|
||||||
|
codepoint = FT_Get_Next_Char(face, codepoint, &index);
|
||||||
|
array.vals[counter].codepoint = codepoint;
|
||||||
|
array.vals[counter].index = (u16)index;
|
||||||
|
maximum_index = Max(maximum_index, (u16)index);
|
||||||
|
counter += 1;
|
||||||
|
if (counter == glyph_count){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*maximum_index_out = maximum_index;
|
||||||
|
|
||||||
|
return(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Codepoint_Index_Map
|
||||||
|
ft__get_codepoint_index_map(Base_Allocator *base_allocator, FT_Face face){
|
||||||
|
FT_Long glyph_count = face->num_glyphs;
|
||||||
|
|
||||||
|
Codepoint_Index_Map map = {};
|
||||||
|
map.zero_index = max_u16;
|
||||||
|
map.table = make_table_u32_u16(base_allocator, glyph_count*4);
|
||||||
|
|
||||||
|
u16 maximum_index = 0;
|
||||||
|
|
||||||
|
i32 counter = 0;
|
||||||
|
FT_UInt index = 0;
|
||||||
|
FT_ULong codepoint = FT_Get_First_Char(face, &index);
|
||||||
|
table_insert(&map.table, (u32)codepoint, (u16)index);
|
||||||
|
maximum_index = Max(maximum_index, (u16)index);
|
||||||
|
counter += 1;
|
||||||
|
for (;;){
|
||||||
|
codepoint = FT_Get_Next_Char(face, codepoint, &index);
|
||||||
|
if (codepoint == 0){
|
||||||
|
map.has_zero_index = true;
|
||||||
|
map.zero_index = (u16)(index);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
table_insert(&map.table, (u32)codepoint, (u16)index);
|
||||||
|
}
|
||||||
|
maximum_index = Max(maximum_index, (u16)index);
|
||||||
|
counter += 1;
|
||||||
|
if (counter == glyph_count){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
map.max_index = maximum_index;
|
||||||
|
|
||||||
|
return(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Bad_Rect_Pack{
|
||||||
|
Vec2_i32 max_dim;
|
||||||
|
Vec3_i32 dim;
|
||||||
|
Vec3_i32 p;
|
||||||
|
i32 current_line_h;
|
||||||
|
};
|
||||||
|
|
||||||
|
internal void
|
||||||
|
ft__bad_rect_pack_init(Bad_Rect_Pack *pack, Vec2_i32 max_dim){
|
||||||
|
pack->max_dim = max_dim;
|
||||||
|
pack->dim = V3i32(0, 0, 1);
|
||||||
|
pack->p = V3i32(0, 0, 0);
|
||||||
|
pack->current_line_h = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
ft__bad_rect_pack_end_line(Bad_Rect_Pack *pack){
|
||||||
|
pack->p.y += pack->current_line_h;
|
||||||
|
pack->dim.y = Max(pack->dim.y, pack->p.y);
|
||||||
|
pack->current_line_h = 0;
|
||||||
|
pack->p.x = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Vec3_i32
|
||||||
|
ft__bad_rect_pack_next(Bad_Rect_Pack *pack, Vec2_i32 dim){
|
||||||
|
Vec3_i32 result = {};
|
||||||
|
if (dim.x <= pack->max_dim.x && dim.y <= pack->max_dim.y){
|
||||||
|
if (pack->current_line_h < dim.y){
|
||||||
|
pack->current_line_h = dim.y;
|
||||||
|
}
|
||||||
|
if (pack->current_line_h > pack->max_dim.y){
|
||||||
|
ft__bad_rect_pack_end_line(pack);
|
||||||
|
pack->p.y = 0;
|
||||||
|
pack->dim.z += 1;
|
||||||
|
pack->p.z += 1;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
if (pack->p.x + dim.x > pack->max_dim.x){
|
||||||
|
ft__bad_rect_pack_end_line(pack);
|
||||||
|
}
|
||||||
|
result = pack->p;
|
||||||
|
pack->p.x += dim.x;
|
||||||
|
pack->current_line_h = Max(pack->current_line_h, dim.y);
|
||||||
|
pack->dim.x = clamp_bot(pack->dim.x, pack->p.x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
ft__bad_rect_store_finish(Bad_Rect_Pack *pack){
|
||||||
|
ft__bad_rect_pack_end_line(pack);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
ft__glyph_bounds_store_uv_raw(Vec3_i32 p, Vec2_i32 dim, Glyph_Bounds *bounds){
|
||||||
|
bounds->uv = Rf32((f32)p.x, (f32)p.y, (f32)dim.x, (f32)dim.y);
|
||||||
|
bounds->w = (f32)p.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Face*
|
||||||
|
ft__font_make_face(Arena *arena, Face_Description *description, f32 scale_factor){
|
||||||
|
String_Const_u8 file_name = push_string_copy(arena, description->font.file_name);
|
||||||
|
|
||||||
|
FT_Library ft;
|
||||||
|
FT_Init_FreeType(&ft);
|
||||||
|
|
||||||
|
FT_Face ft_face;
|
||||||
|
FT_Error error = FT_New_Face(ft, (char*)file_name.str, 0, &ft_face);
|
||||||
|
|
||||||
|
Face *face = 0;
|
||||||
|
if (error == 0){
|
||||||
|
face = push_array_zero(arena, Face, 1);
|
||||||
|
|
||||||
|
u32 pt_size_unscaled = Max(description->parameters.pt_size, 8);
|
||||||
|
u32 pt_size = (u32)(pt_size_unscaled*scale_factor);
|
||||||
|
b32 hinting = description->parameters.hinting;
|
||||||
|
|
||||||
|
FT_Size_RequestRec_ size = {};
|
||||||
|
size.type = FT_SIZE_REQUEST_TYPE_NOMINAL;
|
||||||
|
size.height = (pt_size << 6);
|
||||||
|
FT_Request_Size(ft_face, &size);
|
||||||
|
|
||||||
|
face->description.font.file_name = file_name;
|
||||||
|
face->description.parameters = description->parameters;
|
||||||
|
|
||||||
|
Face_Metrics *met = &face->metrics;
|
||||||
|
|
||||||
|
met->max_advance = f32_ceil32(ft_face->size->metrics.max_advance/64.f);
|
||||||
|
met->ascent = f32_ceil32(ft_face->size->metrics.ascender/64.f);
|
||||||
|
met->descent = f32_floor32(ft_face->size->metrics.descender/64.f);
|
||||||
|
met->text_height = f32_ceil32(ft_face->size->metrics.height/64.f);
|
||||||
|
met->line_skip = met->text_height - (met->ascent - met->descent);
|
||||||
|
met->line_skip = clamp_bot(1.f, met->line_skip);
|
||||||
|
met->line_height = met->text_height + met->line_skip;
|
||||||
|
|
||||||
|
{
|
||||||
|
f32 real_over_notional = met->line_height/(f32)ft_face->height;
|
||||||
|
f32 relative_center = -1.f*real_over_notional*ft_face->underline_position;
|
||||||
|
f32 relative_thickness = real_over_notional*ft_face->underline_thickness;
|
||||||
|
|
||||||
|
f32 center = f32_floor32(met->ascent + relative_center);
|
||||||
|
f32 thickness = clamp_bot(1.f, relative_thickness);
|
||||||
|
|
||||||
|
met->underline_yoff1 = center - thickness*0.5f;
|
||||||
|
met->underline_yoff2 = center + thickness*0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
face->advance_map.codepoint_to_index =
|
||||||
|
ft__get_codepoint_index_map(arena->base_allocator, ft_face);
|
||||||
|
u16 index_count =
|
||||||
|
codepoint_index_map_count(&face->advance_map.codepoint_to_index);
|
||||||
|
face->advance_map.index_count = index_count;
|
||||||
|
face->advance_map.advance = push_array_zero(arena, f32, index_count);
|
||||||
|
face->bounds = push_array(arena, Glyph_Bounds, index_count);
|
||||||
|
|
||||||
|
Temp_Memory_Block temp_memory(arena);
|
||||||
|
struct Bitmap{
|
||||||
|
Vec2_i32 dim;
|
||||||
|
u8 *data;
|
||||||
|
};
|
||||||
|
Bitmap *glyph_bitmaps = push_array(arena, Bitmap, index_count);
|
||||||
|
|
||||||
|
u32 load_flags = ft__load_flags(hinting);
|
||||||
|
for (u16 i = 0; i < index_count; i += 1){
|
||||||
|
Bitmap *bitmap = &glyph_bitmaps[i];
|
||||||
|
|
||||||
|
error = FT_Load_Glyph(ft_face, i, load_flags);
|
||||||
|
if (error == 0){
|
||||||
|
FT_GlyphSlot ft_glyph = ft_face->glyph;
|
||||||
|
Vec2_i32 dim = V2i32(ft_glyph->bitmap.width, ft_glyph->bitmap.rows);
|
||||||
|
bitmap->dim = dim;
|
||||||
|
bitmap->data = push_array(arena, u8, dim.x*dim.y);
|
||||||
|
|
||||||
|
face->bounds[i].xy_off.x0 = (f32)(ft_face->glyph->bitmap_left);
|
||||||
|
face->bounds[i].xy_off.y0 = (f32)(met->ascent - ft_face->glyph->bitmap_top);
|
||||||
|
face->bounds[i].xy_off.x1 = (f32)(face->bounds[i].xy_off.x0 + dim.x);
|
||||||
|
face->bounds[i].xy_off.y1 = (f32)(face->bounds[i].xy_off.y0 + dim.y);
|
||||||
|
|
||||||
|
switch (ft_glyph->bitmap.pixel_mode){
|
||||||
|
case FT_PIXEL_MODE_MONO:
|
||||||
|
{
|
||||||
|
NotImplemented;
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case FT_PIXEL_MODE_GRAY:
|
||||||
|
{
|
||||||
|
b32 aa_1bit_mono = (description->parameters.aa_mode == FaceAntialiasingMode_1BitMono);
|
||||||
|
|
||||||
|
u8 *src_line = ft_glyph->bitmap.buffer;
|
||||||
|
if (ft_glyph->bitmap.pitch < 0){
|
||||||
|
src_line = ft_glyph->bitmap.buffer + (-ft_glyph->bitmap.pitch)*(dim.y - 1);
|
||||||
|
}
|
||||||
|
u8 *dst = bitmap->data;
|
||||||
|
for (i32 y = 0; y < dim.y; y += 1){
|
||||||
|
u8 *src_pixel = src_line;
|
||||||
|
for (i32 x = 0; x < dim.x; x += 1){
|
||||||
|
if (aa_1bit_mono){
|
||||||
|
u8 s = *src_pixel;
|
||||||
|
if (s > 0){
|
||||||
|
s = 255;
|
||||||
|
}
|
||||||
|
*dst = s;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
*dst = *src_pixel;
|
||||||
|
}
|
||||||
|
dst += 1;
|
||||||
|
src_pixel += 1;
|
||||||
|
}
|
||||||
|
src_line += ft_glyph->bitmap.pitch;
|
||||||
|
}
|
||||||
|
}break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
NotImplemented;
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
|
||||||
|
face->advance_map.advance[i] = f32_ceil32(ft_glyph->advance.x/64.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 white_data[16] = {
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
};
|
||||||
|
|
||||||
|
Bitmap white = {};
|
||||||
|
white.dim = V2i32(4, 4);
|
||||||
|
white.data = white_data;
|
||||||
|
|
||||||
|
Bad_Rect_Pack pack = {};
|
||||||
|
ft__bad_rect_pack_init(&pack, V2i32(1024, 1024));
|
||||||
|
ft__glyph_bounds_store_uv_raw(ft__bad_rect_pack_next(&pack, white.dim), white.dim, &face->white);
|
||||||
|
for (u16 i = 0; i < index_count; i += 1){
|
||||||
|
Vec2_i32 dim = glyph_bitmaps[i].dim;
|
||||||
|
ft__glyph_bounds_store_uv_raw(ft__bad_rect_pack_next(&pack, dim), dim, &face->bounds[i]);
|
||||||
|
}
|
||||||
|
ft__bad_rect_store_finish(&pack);
|
||||||
|
|
||||||
|
Texture_Kind texture_kind = TextureKind_Mono;
|
||||||
|
u32 texture = graphics_get_texture(pack.dim, texture_kind);
|
||||||
|
face->texture_kind = texture_kind;
|
||||||
|
face->texture = texture;
|
||||||
|
|
||||||
|
Vec3_f32 texture_dim = V3f32(pack.dim);
|
||||||
|
face->texture_dim = texture_dim;
|
||||||
|
|
||||||
|
{
|
||||||
|
Vec3_i32 p = V3i32((i32)face->white.uv.x0, (i32)face->white.uv.y0, (i32)face->white.w);
|
||||||
|
Vec3_i32 dim = V3i32(white.dim.x, white.dim.y, 1);
|
||||||
|
graphics_fill_texture(texture_kind, texture, p, dim, white.data);
|
||||||
|
face->white.uv.x1 = (face->white.uv.x0 + face->white.uv.x1)/texture_dim.x;
|
||||||
|
face->white.uv.y1 = (face->white.uv.y0 + face->white.uv.y1)/texture_dim.y;
|
||||||
|
face->white.uv.x0 = face->white.uv.x0/texture_dim.x;
|
||||||
|
face->white.uv.y0 = face->white.uv.y0/texture_dim.y;
|
||||||
|
face->white.w /= texture_dim.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (u16 i = 0; i < index_count; i += 1){
|
||||||
|
Vec3_i32 p = V3i32((i32)face->bounds[i].uv.x0, (i32)face->bounds[i].uv.y0, (i32)face->bounds[i].w);
|
||||||
|
Vec3_i32 dim = V3i32(glyph_bitmaps[i].dim.x, glyph_bitmaps[i].dim.y, 1);
|
||||||
|
graphics_fill_texture(texture_kind, texture, p, dim, glyph_bitmaps[i].data);
|
||||||
|
face->bounds[i].uv.x1 = (face->bounds[i].uv.x0 + face->bounds[i].uv.x1)/texture_dim.x;
|
||||||
|
face->bounds[i].uv.y1 = (face->bounds[i].uv.y0 + face->bounds[i].uv.y1)/texture_dim.y;
|
||||||
|
face->bounds[i].uv.x0 = face->bounds[i].uv.x0/texture_dim.x;
|
||||||
|
face->bounds[i].uv.y0 = face->bounds[i].uv.y0/texture_dim.y;
|
||||||
|
face->bounds[i].w /= texture_dim.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Face_Advance_Map *advance_map = &face->advance_map;
|
||||||
|
|
||||||
|
met->space_advance = font_get_glyph_advance(advance_map, met, ' ', 0);
|
||||||
|
met->decimal_digit_advance =
|
||||||
|
font_get_max_glyph_advance_range(advance_map, met, '0', '9', 0);
|
||||||
|
met->hex_digit_advance =
|
||||||
|
font_get_max_glyph_advance_range(advance_map, met, 'A', 'F', 0);
|
||||||
|
met->hex_digit_advance =
|
||||||
|
Max(met->hex_digit_advance, met->decimal_digit_advance);
|
||||||
|
met->byte_sub_advances[0] =
|
||||||
|
font_get_glyph_advance(advance_map, met, '\\', 0);
|
||||||
|
met->byte_sub_advances[1] = met->hex_digit_advance;
|
||||||
|
met->byte_sub_advances[2] = met->hex_digit_advance;
|
||||||
|
met->byte_advance =
|
||||||
|
met->byte_sub_advances[0] +
|
||||||
|
met->byte_sub_advances[1] +
|
||||||
|
met->byte_sub_advances[2];
|
||||||
|
met->normal_lowercase_advance =
|
||||||
|
font_get_average_glyph_advance_range(advance_map, met, 'a', 'z', 0);
|
||||||
|
met->normal_uppercase_advance =
|
||||||
|
font_get_average_glyph_advance_range(advance_map, met, 'A', 'Z', 0);
|
||||||
|
met->normal_advance = (26*met->normal_lowercase_advance +
|
||||||
|
26*met->normal_uppercase_advance +
|
||||||
|
10*met->decimal_digit_advance)/62.f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FT_Done_FreeType(ft);
|
||||||
|
|
||||||
|
return(face);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 16.11.2017
|
||||||
|
*
|
||||||
|
* Data types for the freetype font provider.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#if !defined(FCODER_FONT_PROVIDER_FREETYPE_H)
|
||||||
|
#define FCODER_FONT_PROVIDER_FREETYPE_H
|
||||||
|
|
||||||
|
struct FT_Codepoint_Index_Pair{
|
||||||
|
u32 codepoint;
|
||||||
|
u16 index;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FT_Codepoint_Index_Pair_Array{
|
||||||
|
FT_Codepoint_Index_Pair *vals;
|
||||||
|
i32 count;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,171 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 23.07.2019
|
||||||
|
*
|
||||||
|
* Type for organizating the set of all loaded font faces.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
internal Face_ID
|
||||||
|
font_set__alloc_face_id(Font_Set *set){
|
||||||
|
Face_ID result = 0;
|
||||||
|
if (set->free_ids != 0){
|
||||||
|
Font_Face_ID_Node *node = set->free_ids;
|
||||||
|
result = node->id;
|
||||||
|
sll_stack_pop(set->free_ids);
|
||||||
|
sll_stack_push(set->free_id_nodes, node);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
result = set->next_id_counter;
|
||||||
|
set->next_id_counter += 1;
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
font_set__free_face_id(Font_Set *set, Face_ID id){
|
||||||
|
if (id + 1 == set->next_id_counter){
|
||||||
|
set->next_id_counter -= 1;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
Font_Face_ID_Node *node = 0;
|
||||||
|
if (set->free_id_nodes == 0){
|
||||||
|
node = push_array(&set->arena, Font_Face_ID_Node, 1);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
node = set->free_id_nodes;
|
||||||
|
sll_stack_pop(set->free_id_nodes);
|
||||||
|
}
|
||||||
|
sll_stack_push(set->free_ids, node);
|
||||||
|
node->id = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Font_Face_Slot*
|
||||||
|
font_set__alloc_face_slot(Font_Set *set){
|
||||||
|
Font_Face_Slot *result = 0;
|
||||||
|
if (set->free_face_slots == 0){
|
||||||
|
result = push_array(&set->arena, Font_Face_Slot, 1);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
result = set->free_face_slots;
|
||||||
|
sll_stack_pop(set->free_face_slots);
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
font_set__free_face_slot(Font_Set *set, Font_Face_Slot *slot){
|
||||||
|
if (slot->arena.base_allocator != 0){
|
||||||
|
table_free(&slot->face->advance_map.codepoint_to_index.table);
|
||||||
|
linalloc_clear(&slot->arena);
|
||||||
|
}
|
||||||
|
block_zero_struct(slot);
|
||||||
|
sll_stack_push(set->free_face_slots, slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
font_set_init(Font_Set *set){
|
||||||
|
block_zero_struct(set);
|
||||||
|
set->arena = make_arena_system();
|
||||||
|
set->next_id_counter = 1;
|
||||||
|
set->id_to_slot_table = make_table_u64_u64(set->arena.base_allocator, 40);
|
||||||
|
set->scale_factor = system_get_screen_scale_factor();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Face*
|
||||||
|
font_set_new_face(Font_Set *set, Face_Description *description){
|
||||||
|
Arena arena = make_arena_system();
|
||||||
|
Face *face = font_make_face(&arena, description, set->scale_factor);
|
||||||
|
if (face != 0){
|
||||||
|
Font_Face_Slot *slot = font_set__alloc_face_slot(set);
|
||||||
|
slot->arena = arena;
|
||||||
|
slot->face = face;
|
||||||
|
Face_ID new_id = font_set__alloc_face_id(set);
|
||||||
|
face->id = new_id;
|
||||||
|
table_insert(&set->id_to_slot_table, new_id, (u64)slot);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
linalloc_clear(&arena);
|
||||||
|
}
|
||||||
|
return(face);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Font_Face_Slot*
|
||||||
|
font_set__get_face_slot(Font_Set *set, Face_ID id){
|
||||||
|
Font_Face_Slot *result = 0;
|
||||||
|
u64 slot_ptr_u64 = 0;
|
||||||
|
if (table_read(&set->id_to_slot_table, id, &slot_ptr_u64)){
|
||||||
|
result = (Font_Face_Slot*)slot_ptr_u64;
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal b32
|
||||||
|
font_set_release_face(Font_Set *set, Face_ID id){
|
||||||
|
b32 result = false;
|
||||||
|
Font_Face_Slot *slot = font_set__get_face_slot(set, id);
|
||||||
|
if (slot != 0){
|
||||||
|
table_erase(&set->id_to_slot_table, id);
|
||||||
|
font_set__free_face_slot(set, slot);
|
||||||
|
font_set__free_face_id(set, id);
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Face*
|
||||||
|
font_set_face_from_id(Font_Set *set, Face_ID id){
|
||||||
|
Face *result = 0;
|
||||||
|
Font_Face_Slot *slot = font_set__get_face_slot(set, id);
|
||||||
|
if (slot != 0){
|
||||||
|
result = slot->face;
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Face_ID
|
||||||
|
font_set_get_fallback_face(Font_Set *set){
|
||||||
|
Face_ID result = 0;
|
||||||
|
for (Face_ID i = 1; i < set->next_id_counter; i += 1){
|
||||||
|
if (font_set__get_face_slot(set, i) != 0){
|
||||||
|
result = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Face_ID
|
||||||
|
font_set_get_largest_id(Font_Set *set){
|
||||||
|
return(set->next_id_counter - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal b32
|
||||||
|
font_set_modify_face(Font_Set *set, Face_ID id, Face_Description *description){
|
||||||
|
b32 result = false;
|
||||||
|
Font_Face_Slot *slot = font_set__get_face_slot(set, id);
|
||||||
|
if (slot != 0){
|
||||||
|
i32 version_number = slot->face->version_number;
|
||||||
|
Arena arena = make_arena_system();
|
||||||
|
Face *face = font_make_face(&arena, description, set->scale_factor);
|
||||||
|
if (face != 0){
|
||||||
|
linalloc_clear(&slot->arena);
|
||||||
|
slot->arena = arena;
|
||||||
|
slot->face = face;
|
||||||
|
face->version_number = version_number + 1;
|
||||||
|
face->id = id;
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
linalloc_clear(&arena);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 23.07.2019
|
||||||
|
*
|
||||||
|
* Type for organizating the set of all loaded font faces.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#if !defined(FRED_FONT_SET_H)
|
||||||
|
#define FRED_FONT_SET_H
|
||||||
|
|
||||||
|
struct Font_Face_ID_Node{
|
||||||
|
Font_Face_ID_Node *next;
|
||||||
|
Face_ID id;
|
||||||
|
};
|
||||||
|
|
||||||
|
union Font_Face_Slot{
|
||||||
|
struct{
|
||||||
|
Font_Face_Slot *next;
|
||||||
|
};
|
||||||
|
struct{
|
||||||
|
Arena arena;
|
||||||
|
Face *face;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Font_Set{
|
||||||
|
Arena arena;
|
||||||
|
Face_ID next_id_counter;
|
||||||
|
Font_Face_ID_Node *free_ids;
|
||||||
|
Font_Face_ID_Node *free_id_nodes;
|
||||||
|
Font_Face_Slot *free_face_slots;
|
||||||
|
Table_u64_u64 id_to_slot_table;
|
||||||
|
f32 scale_factor;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,199 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 09.10.2019
|
||||||
|
*
|
||||||
|
* Primary list for all key codes.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#include "4coder_base_types.h"
|
||||||
|
|
||||||
|
#include "4coder_base_types.cpp"
|
||||||
|
#include "4coder_stringf.cpp"
|
||||||
|
#include "4coder_malloc_allocator.cpp"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
struct Event_Code{
|
||||||
|
Event_Code *next;
|
||||||
|
String_Const_u8 name;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Event_Code_List{
|
||||||
|
Event_Code *first;
|
||||||
|
Event_Code *last;
|
||||||
|
i32 count;
|
||||||
|
|
||||||
|
String_Const_u8 code_prefix;
|
||||||
|
String_Const_u8 name_table;
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
function void
|
||||||
|
generate_codes(Arena *scratch, Event_Code_List *list, FILE *out){
|
||||||
|
String_Const_u8 code_prefix = list->code_prefix;
|
||||||
|
String_Const_u8 name_table = list->name_table;
|
||||||
|
|
||||||
|
fprintf(out, "enum{\n");
|
||||||
|
i32 counter = 1;
|
||||||
|
for (Event_Code *code = list->first;
|
||||||
|
code != 0;
|
||||||
|
code = code->next){
|
||||||
|
fprintf(out, "%.*s_%.*s = %d,\n",
|
||||||
|
string_expand(code_prefix), string_expand(code->name), counter);
|
||||||
|
counter += 1;
|
||||||
|
}
|
||||||
|
fprintf(out, "%.*s_COUNT = %d,\n", string_expand(code_prefix), counter);
|
||||||
|
fprintf(out, "};\n");
|
||||||
|
|
||||||
|
fprintf(out, "global char* %.*s[%.*s_COUNT] = {\n",
|
||||||
|
string_expand(name_table), string_expand(code_prefix));
|
||||||
|
fprintf(out, "\"None\",\n");
|
||||||
|
for (Event_Code *code = list->first;
|
||||||
|
code != 0;
|
||||||
|
code = code->next){
|
||||||
|
fprintf(out, "\"%.*s\",\n", string_expand(code->name));
|
||||||
|
counter += 1;
|
||||||
|
}
|
||||||
|
fprintf(out, "};\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
function Event_Code*
|
||||||
|
add_code(Arena *arena, Event_Code_List *list, String_Const_u8 name){
|
||||||
|
Event_Code *code = push_array(arena, Event_Code, 1);
|
||||||
|
sll_queue_push(list->first, list->last, code);
|
||||||
|
list->count;
|
||||||
|
code->name = push_string_copy(arena, name);
|
||||||
|
return(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Event_Code_List
|
||||||
|
make_key_list(Arena *arena){
|
||||||
|
Event_Code_List list = {};
|
||||||
|
list.code_prefix = string_u8_litexpr("KeyCode");
|
||||||
|
list.name_table = string_u8_litexpr("key_code_name");
|
||||||
|
for (u32 i = 'A'; i <= 'Z'; i += 1){
|
||||||
|
u8 c = (u8)i;
|
||||||
|
add_code(arena, &list, SCu8(&c, 1));
|
||||||
|
}
|
||||||
|
for (u32 i = '0'; i <= '9'; i += 1){
|
||||||
|
u8 c = (u8)i;
|
||||||
|
add_code(arena, &list, SCu8(&c, 1));
|
||||||
|
}
|
||||||
|
add_code(arena, &list, string_u8_litexpr("Space"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("Tick"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("Minus"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("Equal"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("LeftBracket"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("RightBracket"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("Semicolon"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("Quote"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("Comma"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("Period"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("ForwardSlash"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("BackwardSlash"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("Tab"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("Escape"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("Pause"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("Up"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("Down"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("Left"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("Right"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("Backspace"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("Return"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("Delete"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("Insert"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("Home"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("End"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("PageUp"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("PageDown"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("CapsLock"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("NumLock"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("ScrollLock"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("Menu"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("Shift"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("Control"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("Alt"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("Command"));
|
||||||
|
for (u32 i = 1; i <= 24; i += 1){
|
||||||
|
add_code(arena, &list, push_u8_stringf(arena, "F%d", i));
|
||||||
|
}
|
||||||
|
for (u32 i = '0'; i <= '9'; i += 1){
|
||||||
|
add_code(arena, &list, push_u8_stringf(arena, "NumPad%c", i));
|
||||||
|
}
|
||||||
|
add_code(arena, &list, string_u8_litexpr("NumPadStar"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("NumPadPlus"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("NumPadMinus"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("NumPadDot"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("NumPadSlash"));
|
||||||
|
for (i32 i = 0; i < 30; i += 1){
|
||||||
|
add_code(arena, &list, push_u8_stringf(arena, "Ex%d", i));
|
||||||
|
}
|
||||||
|
return(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Event_Code_List
|
||||||
|
make_mouse_list(Arena *arena){
|
||||||
|
Event_Code_List list = {};
|
||||||
|
list.code_prefix = string_u8_litexpr("MouseCode");
|
||||||
|
list.name_table = string_u8_litexpr("mouse_code_name");
|
||||||
|
add_code(arena, &list, string_u8_litexpr("Left"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("Middle"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("Right"));
|
||||||
|
return(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Event_Code_List
|
||||||
|
make_core_list(Arena *arena){
|
||||||
|
Event_Code_List list = {};
|
||||||
|
list.code_prefix = string_u8_litexpr("CoreCode");
|
||||||
|
list.name_table = string_u8_litexpr("core_code_name");
|
||||||
|
add_code(arena, &list, string_u8_litexpr("Startup"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("Animate"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("ClickActivateView"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("ClickDeactivateView"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("TryExit"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("FileExternallyModified"));
|
||||||
|
add_code(arena, &list, string_u8_litexpr("NewClipboardContents"));
|
||||||
|
return(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void){
|
||||||
|
Arena arena = make_arena_malloc();
|
||||||
|
|
||||||
|
Event_Code_List key_list = make_key_list(&arena);
|
||||||
|
Event_Code_List mouse_list = make_mouse_list(&arena);
|
||||||
|
Event_Code_List core_list = make_core_list(&arena);
|
||||||
|
|
||||||
|
String_Const_u8 path_to_self = string_u8_litexpr(__FILE__);
|
||||||
|
path_to_self = string_remove_last_folder(path_to_self);
|
||||||
|
String_Const_u8 file_name =
|
||||||
|
push_u8_stringf(&arena, "%.*scustom/generated/4coder_event_codes.h",
|
||||||
|
string_expand(path_to_self));
|
||||||
|
|
||||||
|
FILE *out = fopen((char*)file_name.str, "wb");
|
||||||
|
if (out == 0){
|
||||||
|
printf("could not open output file '%s'\n", file_name.str);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
generate_codes(&arena, &key_list, out);
|
||||||
|
generate_codes(&arena, &mouse_list, out);
|
||||||
|
generate_codes(&arena, &core_list, out);
|
||||||
|
|
||||||
|
fclose(out);
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 02.10.2019
|
||||||
|
*
|
||||||
|
* Graphics API definition program.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#include "4ed_api_definition_main.cpp"
|
||||||
|
|
||||||
|
function API_Definition*
|
||||||
|
define_api(Arena *arena){
|
||||||
|
API_Definition *api = begin_api(arena, "graphics");
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "get_texture", "u32");
|
||||||
|
api_param(arena, call, "Vec3_i32", "dim");
|
||||||
|
api_param(arena, call, "Texture_Kind", "texture_kind");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "fill_texture", "b32");
|
||||||
|
api_param(arena, call, "Texture_Kind", "texture_kind");
|
||||||
|
api_param(arena, call, "u32", "texture");
|
||||||
|
api_param(arena, call, "Vec3_i32", "p");
|
||||||
|
api_param(arena, call, "Vec3_i32", "dim");
|
||||||
|
api_param(arena, call, "void*", "data");
|
||||||
|
}
|
||||||
|
|
||||||
|
return(api);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Generated_Group
|
||||||
|
get_api_group(void){
|
||||||
|
return(GeneratedGroup_Core);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,447 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 24.03.2018
|
||||||
|
*
|
||||||
|
* History
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
internal Node*
|
||||||
|
history__to_node(Node *sentinel, i32 index){
|
||||||
|
Node *result = 0;
|
||||||
|
i32 counter = 0;
|
||||||
|
Node *it = sentinel;
|
||||||
|
do{
|
||||||
|
if (counter == index){
|
||||||
|
result = it;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
counter += 1;
|
||||||
|
it = it->next;
|
||||||
|
} while (it != sentinel);
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
history__push_back_record_ptr(Base_Allocator *allocator, Record_Ptr_Lookup_Table *lookup, Record *record){
|
||||||
|
if (lookup->records == 0 || lookup->count == lookup->max){
|
||||||
|
i32 new_max = clamp_bot(1024, lookup->max*2);
|
||||||
|
String_Const_u8 new_memory = base_allocate(allocator, sizeof(Record*)*new_max);
|
||||||
|
Record **new_records = (Record**)new_memory.str;
|
||||||
|
block_copy(new_records, lookup->records, sizeof(*new_records)*lookup->count);
|
||||||
|
if (lookup->records != 0){
|
||||||
|
base_free(allocator, lookup->records);
|
||||||
|
}
|
||||||
|
lookup->records = new_records;
|
||||||
|
lookup->max = new_max;
|
||||||
|
}
|
||||||
|
Assert(lookup->count < lookup->max);
|
||||||
|
lookup->records[lookup->count] = record;
|
||||||
|
lookup->count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
history__shrink_array(Record_Ptr_Lookup_Table *lookup, i32 new_count){
|
||||||
|
Assert(0 <= new_count && new_count <= lookup->count);
|
||||||
|
lookup->count = new_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
history__merge_record_ptr_range_to_one_ptr(Record_Ptr_Lookup_Table *lookup, i32 first_one_based, i32 last_one_based, Record *record){
|
||||||
|
i32 first = first_one_based - 1;
|
||||||
|
i32 one_past_last = last_one_based;
|
||||||
|
Assert(0 <= first && first <= one_past_last && one_past_last <= lookup->count);
|
||||||
|
if (first < one_past_last){
|
||||||
|
i32 shift = 1 + first - one_past_last;
|
||||||
|
block_copy(lookup->records + one_past_last + shift, lookup->records + one_past_last, lookup->count - one_past_last);
|
||||||
|
lookup->count += shift;
|
||||||
|
}
|
||||||
|
lookup->records[first] = record;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Node*
|
||||||
|
history__to_node(History *history, i32 index){
|
||||||
|
Node *result = 0;
|
||||||
|
if (index == 0){
|
||||||
|
result = &history->records;
|
||||||
|
}
|
||||||
|
else if (0 < index && index <= history->record_count){
|
||||||
|
Record_Ptr_Lookup_Table *lookup = &history->record_lookup;
|
||||||
|
Assert(lookup->count == history->record_count);
|
||||||
|
result = &lookup->records[index - 1]->node;
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
internal Record*
|
||||||
|
history__allocate_record(History *history){
|
||||||
|
Node *sentinel = &history->free_records;
|
||||||
|
Node *new_node = sentinel->next;
|
||||||
|
if (new_node == sentinel){
|
||||||
|
i32 new_record_count = 1024;
|
||||||
|
String_Const_u8 new_memory = base_allocate(&history->heap_wrapper, sizeof(Record)*new_record_count);
|
||||||
|
void *memory = new_memory.str;
|
||||||
|
|
||||||
|
Record *new_record = (Record*)memory;
|
||||||
|
sentinel->next = &new_record->node;
|
||||||
|
new_record->node.prev = sentinel;
|
||||||
|
for (i32 i = 1; i < new_record_count; i += 1, new_record += 1){
|
||||||
|
new_record[0].node.next = &new_record[1].node;
|
||||||
|
new_record[1].node.prev = &new_record[0].node;
|
||||||
|
}
|
||||||
|
new_record[0].node.next = sentinel;
|
||||||
|
sentinel->prev = &new_record[0].node;
|
||||||
|
|
||||||
|
new_node = &((Record*)memory)->node;
|
||||||
|
}
|
||||||
|
dll_remove(new_node);
|
||||||
|
Record *record = CastFromMember(Record, node, new_node);
|
||||||
|
block_zero_struct(record);
|
||||||
|
return(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
global_history_init(Global_History *global_history){
|
||||||
|
global_history->edit_number_counter = 0;
|
||||||
|
global_history->edit_grouping_counter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal i32
|
||||||
|
global_history_get_edit_number(Global_History *global_history){
|
||||||
|
i32 result = global_history->edit_number_counter;
|
||||||
|
if (global_history->edit_grouping_counter == 0){
|
||||||
|
global_history->edit_number_counter += 1;
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
global_history_adjust_edit_grouping_counter(Global_History *global_history, i32 adjustment){
|
||||||
|
i32 original = global_history->edit_grouping_counter;
|
||||||
|
global_history->edit_grouping_counter = clamp_bot(0, global_history->edit_grouping_counter + adjustment);
|
||||||
|
if (global_history->edit_grouping_counter == 0 && original > 0){
|
||||||
|
global_history->edit_number_counter += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
history_init(Thread_Context *tctx, Models *models, History *history){
|
||||||
|
history->activated = true;
|
||||||
|
history->arena = make_arena_system();
|
||||||
|
heap_init(&history->heap, tctx->allocator);
|
||||||
|
history->heap_wrapper = base_allocator_on_heap(&history->heap);
|
||||||
|
dll_init_sentinel(&history->free_records);
|
||||||
|
dll_init_sentinel(&history->records);
|
||||||
|
history->record_count = 0;
|
||||||
|
block_zero_struct(&history->record_lookup);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal b32
|
||||||
|
history_is_activated(History *history){
|
||||||
|
return(history->activated);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
history_free(Thread_Context *tctx, History *history){
|
||||||
|
if (history->activated){
|
||||||
|
linalloc_clear(&history->arena);
|
||||||
|
heap_free_all(&history->heap);
|
||||||
|
block_zero_struct(history);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal i32
|
||||||
|
history_get_record_count(History *history){
|
||||||
|
i32 result = 0;
|
||||||
|
if (history->activated){
|
||||||
|
result = history->record_count;
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Record*
|
||||||
|
history_get_record(History *history, i32 index){
|
||||||
|
Record *result = 0;
|
||||||
|
if (history->activated){
|
||||||
|
Node *node = history__to_node(history, index);
|
||||||
|
if (node != 0){
|
||||||
|
result = CastFromMember(Record, node, node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Record*
|
||||||
|
history_get_sub_record(Record *record, i32 sub_index_one_based){
|
||||||
|
Record *result = 0;
|
||||||
|
if (record->kind == RecordKind_Group){
|
||||||
|
if (0 < sub_index_one_based && sub_index_one_based <= record->group.count){
|
||||||
|
Node *node = history__to_node(&record->group.children, sub_index_one_based);
|
||||||
|
if (node != 0){
|
||||||
|
result = CastFromMember(Record, node, node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Record*
|
||||||
|
history_get_dummy_record(History *history){
|
||||||
|
Record *result = 0;
|
||||||
|
if (history->activated){
|
||||||
|
result = CastFromMember(Record, node, &history->records);
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
history__stash_record(History *history, Record *new_record){
|
||||||
|
Assert(history->record_lookup.count == history->record_count);
|
||||||
|
dll_insert_back(&history->records, &new_record->node);
|
||||||
|
history->record_count += 1;
|
||||||
|
history__push_back_record_ptr(&history->heap_wrapper, &history->record_lookup, new_record);
|
||||||
|
Assert(history->record_lookup.count == history->record_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
history__free_single_node(History *history, Node *node){
|
||||||
|
dll_remove(node);
|
||||||
|
dll_insert(&history->free_records, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
history__free_nodes(History *history, i32 first_index, Node *first_node, Node *last_node){
|
||||||
|
if (first_node == last_node){
|
||||||
|
history__free_single_node(history, first_node);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
{
|
||||||
|
Node *left = first_node->prev;
|
||||||
|
Node *right = last_node->next;
|
||||||
|
left->next = right;
|
||||||
|
right->prev = left;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Node *left = &history->free_records;
|
||||||
|
Node *right = left->next;
|
||||||
|
left->next = first_node;
|
||||||
|
first_node->prev = left;
|
||||||
|
right->prev = last_node;
|
||||||
|
last_node->next = right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Assert(first_index != 0);
|
||||||
|
history->record_count = first_index - 1;
|
||||||
|
history__shrink_array(&history->record_lookup, history->record_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
history_record_edit(Global_History *global_history, History *history, Gap_Buffer *buffer,
|
||||||
|
i64 pos_before_edit, Edit edit){
|
||||||
|
if (history->activated){
|
||||||
|
Assert(history->record_lookup.count == history->record_count);
|
||||||
|
|
||||||
|
Record *new_record = history__allocate_record(history);
|
||||||
|
history__stash_record(history, new_record);
|
||||||
|
|
||||||
|
new_record->restore_point = begin_temp(&history->arena);
|
||||||
|
if (pos_before_edit >= 0){
|
||||||
|
new_record->pos_before_edit = pos_before_edit;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
new_record->pos_before_edit = edit.range.min;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_record->edit_number = global_history_get_edit_number(global_history);
|
||||||
|
|
||||||
|
new_record->kind = RecordKind_Single;
|
||||||
|
|
||||||
|
new_record->single.forward_text = push_string_copy(&history->arena, edit.text);
|
||||||
|
new_record->single.backward_text = buffer_stringify(&history->arena, buffer, edit.range);
|
||||||
|
new_record->single.first = edit.range.first;
|
||||||
|
|
||||||
|
Assert(history->record_lookup.count == history->record_count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
history_dump_records_after_index(History *history, i32 index){
|
||||||
|
if (history->activated){
|
||||||
|
Assert(history->record_lookup.count == history->record_count);
|
||||||
|
|
||||||
|
Assert(0 <= index && index <= history->record_count);
|
||||||
|
if (index < history->record_count){
|
||||||
|
Node *node = history__to_node(history, index);
|
||||||
|
Node *first_node_to_clear = node->next;
|
||||||
|
|
||||||
|
Node *sentinel = &history->records;
|
||||||
|
Assert(first_node_to_clear != sentinel);
|
||||||
|
|
||||||
|
Record *first_record_to_clear = CastFromMember(Record, node, first_node_to_clear);
|
||||||
|
end_temp(first_record_to_clear->restore_point);
|
||||||
|
|
||||||
|
Node *last_node_to_clear = sentinel->prev;
|
||||||
|
|
||||||
|
history__free_nodes(history, index + 1, first_node_to_clear, last_node_to_clear);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert(history->record_lookup.count == history->record_count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
history__optimize_group(Arena *scratch, History *history, Record *record){
|
||||||
|
Assert(record->kind == RecordKind_Group);
|
||||||
|
for (;;){
|
||||||
|
Record *right = CastFromMember(Record, node, record->group.children.prev);
|
||||||
|
if (record->group.count == 1){
|
||||||
|
Record *child = right;
|
||||||
|
record->restore_point = child->restore_point;
|
||||||
|
record->pos_before_edit = child->pos_before_edit;
|
||||||
|
record->edit_number = child->edit_number;
|
||||||
|
record->kind = RecordKind_Single;
|
||||||
|
record->single = child->single;
|
||||||
|
// NOTE(allen): don't use "free" because the child node is no longer linked
|
||||||
|
// to a valid sentinel, and removing it first (as free does) will mess with
|
||||||
|
// the data in record->single
|
||||||
|
dll_insert(&history->free_records, &child->node);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Record *left = CastFromMember(Record, node, right->node.prev);
|
||||||
|
if (right->kind == RecordKind_Single && left->kind == RecordKind_Single){
|
||||||
|
b32 do_merge = false;
|
||||||
|
|
||||||
|
Temp_Memory temp = begin_temp(scratch);
|
||||||
|
i64 new_length_forward = left->single.forward_text.size + right->single.forward_text.size ;
|
||||||
|
i64 new_length_backward = left->single.backward_text.size + right->single.backward_text.size;
|
||||||
|
|
||||||
|
String_Const_u8 merged_forward = {};
|
||||||
|
String_Const_u8 merged_backward = {};
|
||||||
|
|
||||||
|
i64 merged_first = 0;
|
||||||
|
if (left->single.first + (i64)left->single.forward_text.size == right->single.first){
|
||||||
|
do_merge = true;
|
||||||
|
merged_forward = push_u8_stringf(scratch, "%.*s%.*s",
|
||||||
|
string_expand(left->single.forward_text),
|
||||||
|
string_expand(right->single.forward_text));
|
||||||
|
merged_backward = push_u8_stringf(scratch, "%.*s%.*s",
|
||||||
|
string_expand(left->single.backward_text),
|
||||||
|
string_expand(right->single.backward_text));
|
||||||
|
merged_first = left->single.first;
|
||||||
|
}
|
||||||
|
else if (right->single.first + (i64)right->single.backward_text.size == left->single.first){
|
||||||
|
do_merge = true;
|
||||||
|
merged_forward = push_u8_stringf(scratch, "%.*s%.*s",
|
||||||
|
string_expand(right->single.forward_text),
|
||||||
|
string_expand(left->single.forward_text));
|
||||||
|
merged_backward = push_u8_stringf(scratch, "%.*s%.*s",
|
||||||
|
string_expand(right->single.backward_text),
|
||||||
|
string_expand(left->single.backward_text));
|
||||||
|
merged_first = right->single.first;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (do_merge){
|
||||||
|
end_temp(left->restore_point);
|
||||||
|
|
||||||
|
left->edit_number = right->edit_number;
|
||||||
|
left->single.first = merged_first;
|
||||||
|
left->single.forward_text = push_string_copy(&history->arena, merged_forward);
|
||||||
|
left->single.backward_text = push_string_copy(&history->arena, merged_backward);
|
||||||
|
|
||||||
|
history__free_single_node(history, &right->node);
|
||||||
|
record->group.count -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
end_temp(temp);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
history_merge_records(Arena *scratch, History *history, i32 first_index, i32 last_index){
|
||||||
|
if (history->activated){
|
||||||
|
Assert(history->record_lookup.count == history->record_count);
|
||||||
|
Assert(first_index < last_index);
|
||||||
|
Node *first_node = history__to_node(history, first_index);
|
||||||
|
Node *last_node = history__to_node(history, last_index );
|
||||||
|
Assert(first_node != &history->records && first_node != 0);
|
||||||
|
Assert(last_node != &history->records && last_node != 0);
|
||||||
|
|
||||||
|
Record *new_record = history__allocate_record(history);
|
||||||
|
|
||||||
|
// NOTE(allen): here we remove (last_index - first_index + 1) nodes, and insert 1 node
|
||||||
|
// which simplifies to this:
|
||||||
|
history->record_count -= last_index - first_index;
|
||||||
|
|
||||||
|
Node *left = first_node->prev;
|
||||||
|
dll_remove_multiple(first_node, last_node);
|
||||||
|
dll_insert(left, &new_record->node);
|
||||||
|
|
||||||
|
Record *first_record = CastFromMember(Record, node, first_node);
|
||||||
|
Record *last_record = CastFromMember(Record, node, last_node);
|
||||||
|
|
||||||
|
new_record->restore_point = first_record->restore_point;
|
||||||
|
new_record->pos_before_edit = first_record->pos_before_edit;
|
||||||
|
new_record->edit_number = last_record->edit_number;
|
||||||
|
new_record->kind = RecordKind_Group;
|
||||||
|
|
||||||
|
Node *new_sentinel = &new_record->group.children;
|
||||||
|
dll_init_sentinel(new_sentinel);
|
||||||
|
|
||||||
|
i32 count = 0;
|
||||||
|
for (Node *node = first_node, *next = 0;
|
||||||
|
node != 0;
|
||||||
|
node = next){
|
||||||
|
next = node->next;
|
||||||
|
Record *record = CastFromMember(Record, node, node);
|
||||||
|
switch (record->kind){
|
||||||
|
case RecordKind_Single:
|
||||||
|
{
|
||||||
|
dll_insert_back(new_sentinel, &record->node);
|
||||||
|
count += 1;
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case RecordKind_Group:
|
||||||
|
{
|
||||||
|
Node *first = record->group.children.next;
|
||||||
|
Node *last = record->group.children.prev;
|
||||||
|
Assert(first != &record->group.children);
|
||||||
|
Assert(last != &record->group.children);
|
||||||
|
|
||||||
|
dll_insert_multiple_back(new_sentinel, first, last);
|
||||||
|
count += record->group.count;
|
||||||
|
|
||||||
|
// TODO(allen): free the record for the old group!?
|
||||||
|
}break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
InvalidPath;
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new_record->group.count = count;
|
||||||
|
|
||||||
|
history__merge_record_ptr_range_to_one_ptr(&history->record_lookup, first_index, last_index, new_record);
|
||||||
|
Assert(history->record_lookup.count == history->record_count);
|
||||||
|
|
||||||
|
if (first_index == history->record_count){
|
||||||
|
history__optimize_group(scratch, history, new_record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 24.03.2018
|
||||||
|
*
|
||||||
|
* History
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#if !defined(FRED_HISTORY_H)
|
||||||
|
#define FRED_HISTORY_H
|
||||||
|
|
||||||
|
struct Record_Batch_Slot{
|
||||||
|
i64 length_forward;
|
||||||
|
i64 length_backward;
|
||||||
|
i32 first;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Record{
|
||||||
|
Node node;
|
||||||
|
Temp_Memory restore_point;
|
||||||
|
i64 pos_before_edit;
|
||||||
|
i32 edit_number;
|
||||||
|
Record_Kind kind;
|
||||||
|
union{
|
||||||
|
struct{
|
||||||
|
String_Const_u8 forward_text;
|
||||||
|
String_Const_u8 backward_text;
|
||||||
|
i64 first;
|
||||||
|
} single;
|
||||||
|
struct{
|
||||||
|
Node children;
|
||||||
|
i32 count;
|
||||||
|
} group;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Record_Ptr_Lookup_Table{
|
||||||
|
Record **records;
|
||||||
|
i32 count;
|
||||||
|
i32 max;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct History{
|
||||||
|
b32 activated;
|
||||||
|
Arena arena;
|
||||||
|
Heap heap;
|
||||||
|
Base_Allocator heap_wrapper;
|
||||||
|
Node free_records;
|
||||||
|
Node records;
|
||||||
|
i32 record_count;
|
||||||
|
Record_Ptr_Lookup_Table record_lookup;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Global_History{
|
||||||
|
i32 edit_number_counter;
|
||||||
|
i32 edit_grouping_counter;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// BOTTOM
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 03.01.2017
|
||||||
|
*
|
||||||
|
* Hot_Directory data structure for 4coder
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
internal void
|
||||||
|
hot_directory_clean_end(Hot_Directory *hot_directory){
|
||||||
|
String_Const_u8 str = hot_directory->string;
|
||||||
|
if (!character_is_slash(string_get_character(str, str.size - 1))){
|
||||||
|
hot_directory->string = string_remove_last_folder(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal i32
|
||||||
|
hot_directory_quick_partition(File_Info **infos, i32 start, i32 pivot){
|
||||||
|
File_Info **p = infos + pivot;
|
||||||
|
File_Info **a = infos + start;
|
||||||
|
for (i32 i = start; i < pivot; ++i, ++a){
|
||||||
|
b32 p_folder = (HasFlag((**p).attributes.flags, FileAttribute_IsDirectory));
|
||||||
|
b32 a_folder = (HasFlag((**a).attributes.flags, FileAttribute_IsDirectory));
|
||||||
|
i32 comp = p_folder - a_folder;
|
||||||
|
if (comp == 0){
|
||||||
|
comp = string_compare((**a).file_name, (**p).file_name);
|
||||||
|
}
|
||||||
|
if (comp < 0){
|
||||||
|
Swap(File_Info*, *a, infos[start]);
|
||||||
|
++start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Swap(File_Info*, *p, infos[start]);
|
||||||
|
return(start);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
hot_directory_quick_sort(File_Info **infos, i32 start, i32 pivot){
|
||||||
|
i32 mid = hot_directory_quick_partition(infos, start, pivot);
|
||||||
|
if (start < mid-1) hot_directory_quick_sort(infos, start, mid-1);
|
||||||
|
if (mid+1 < pivot) hot_directory_quick_sort(infos, mid+1, pivot);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
hot_directory_fixup(Hot_Directory *hot_directory){
|
||||||
|
File_List *files = &hot_directory->file_list;
|
||||||
|
if (files->count >= 2){
|
||||||
|
hot_directory_quick_sort(files->infos, 0, files->count - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
hot_directory_set(Hot_Directory *hot_directory, String_Const_u8 str){
|
||||||
|
linalloc_clear(&hot_directory->arena);
|
||||||
|
hot_directory->string = push_string_copy(&hot_directory->arena, str);
|
||||||
|
hot_directory->canonical = system_get_canonical(&hot_directory->arena, str);
|
||||||
|
hot_directory->file_list = system_get_file_list(&hot_directory->arena, hot_directory->canonical);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
hot_directory_reload(Arena *scratch, Hot_Directory *hot_directory){
|
||||||
|
Temp_Memory temp = begin_temp(scratch);
|
||||||
|
String_Const_u8 string = push_string_copy(scratch, hot_directory->string);
|
||||||
|
hot_directory_set(hot_directory, string);
|
||||||
|
end_temp(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
hot_directory_init(Arena *scratch, Hot_Directory *hot_directory, String_Const_u8 directory){
|
||||||
|
hot_directory->arena = make_arena_system();
|
||||||
|
Temp_Memory temp = begin_temp(scratch);
|
||||||
|
String_Const_u8 dir = directory;
|
||||||
|
if (!character_is_slash(string_get_character(directory, directory.size - 1))){
|
||||||
|
dir = push_u8_stringf(scratch, "%.*s/", string_expand(directory));
|
||||||
|
}
|
||||||
|
hot_directory_set(hot_directory, dir);
|
||||||
|
end_temp(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 24.01.2018
|
||||||
|
*
|
||||||
|
* Buffer types
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#if !defined(FRED_HOT_DIRECTORY_H)
|
||||||
|
#define FRED_HOT_DIRECTORY_H
|
||||||
|
|
||||||
|
struct Hot_Directory{
|
||||||
|
Arena arena;
|
||||||
|
String_Const_u8 string;
|
||||||
|
String_Const_u8 canonical;
|
||||||
|
File_List file_list;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,497 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 19.08.2015
|
||||||
|
*
|
||||||
|
* Panel layout functions
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
internal Panel_Split
|
||||||
|
make_panel_split(Panel_Split_Kind kind, i32 v){
|
||||||
|
Panel_Split split = {};
|
||||||
|
split.kind = kind;
|
||||||
|
split.v_i32 = v;
|
||||||
|
return(split);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Panel_Split
|
||||||
|
make_panel_split(Panel_Split_Kind kind, f32 v){
|
||||||
|
Panel_Split split = {};
|
||||||
|
split.kind = kind;
|
||||||
|
split.v_f32 = v;
|
||||||
|
return(split);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Panel_Split
|
||||||
|
make_panel_split_50_50(void){
|
||||||
|
return(make_panel_split(PanelSplitKind_Ratio_Min, 0.5f));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Panel*
|
||||||
|
layout__alloc_panel(Layout *layout){
|
||||||
|
Panel *panel = 0;
|
||||||
|
Node *node = layout->free_panels.next;
|
||||||
|
if (node != &layout->free_panels){
|
||||||
|
dll_remove(node);
|
||||||
|
panel = CastFromMember(Panel, node, node);
|
||||||
|
}
|
||||||
|
return(panel);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
layout__free_panel(Layout *layout, Panel *panel){
|
||||||
|
Assert(panel != layout->active_panel);
|
||||||
|
Assert(panel != layout->root);
|
||||||
|
dll_remove(&panel->node);
|
||||||
|
dll_insert(&layout->free_panels, &panel->node);
|
||||||
|
panel->kind = PanelKind_Unused;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
layout__set_panel_rectangle(Layout *layout, Panel *panel, Rect_i32 rect){
|
||||||
|
panel->rect_full = rect;
|
||||||
|
panel->rect_inner = rect_inner(rect, layout->margin);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal i32
|
||||||
|
layout__evaluate_split(Panel_Split split, i32 v0, i32 v1){
|
||||||
|
i32 v = 0;
|
||||||
|
switch (split.kind){
|
||||||
|
case PanelSplitKind_Ratio_Min:
|
||||||
|
{
|
||||||
|
v = i32_round32(lerp((f32)v0, split.v_f32, (f32)v1));
|
||||||
|
}break;
|
||||||
|
case PanelSplitKind_Ratio_Max:
|
||||||
|
{
|
||||||
|
v = i32_round32(lerp((f32)v1, split.v_f32, (f32)v0));
|
||||||
|
}break;
|
||||||
|
case PanelSplitKind_FixedPixels_Min:
|
||||||
|
{
|
||||||
|
v = clamp_top(v0 + split.v_i32, v1);
|
||||||
|
}break;
|
||||||
|
case PanelSplitKind_FixedPixels_Max:
|
||||||
|
{
|
||||||
|
v = clamp_bot(v0, v1 - split.v_i32);
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
return(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
layout_propogate_sizes_down_from_node(Layout *layout, Panel *panel){
|
||||||
|
if (panel->kind == PanelKind_Intermediate){
|
||||||
|
Panel *tl_panel = panel->tl_panel;
|
||||||
|
Panel *br_panel = panel->br_panel;
|
||||||
|
|
||||||
|
Rect_i32 r1 = panel->rect_full;
|
||||||
|
Rect_i32 r2 = panel->rect_full;
|
||||||
|
|
||||||
|
if (panel->vertical_split){
|
||||||
|
i32 x_pos = layout__evaluate_split(panel->split, r1.x0, r1.x1);
|
||||||
|
r1.x1 = x_pos;
|
||||||
|
r2.x0 = x_pos;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
i32 y_pos = layout__evaluate_split(panel->split, r1.y0, r1.y1);
|
||||||
|
r1.y1 = y_pos;
|
||||||
|
r2.y0 = y_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
layout__set_panel_rectangle(layout, tl_panel, r1);
|
||||||
|
layout__set_panel_rectangle(layout, br_panel, r2);
|
||||||
|
|
||||||
|
layout_propogate_sizes_down_from_node(layout, tl_panel);
|
||||||
|
layout_propogate_sizes_down_from_node(layout, br_panel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal i32
|
||||||
|
layout_get_open_panel_count(Layout *layout){
|
||||||
|
return(layout->open_panel_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Panel*
|
||||||
|
layout_get_first_open_panel(Layout *layout){
|
||||||
|
Panel *panel = CastFromMember(Panel, node, layout->open_panels.next);
|
||||||
|
if (panel != 0 && &panel->node == &layout->open_panels){
|
||||||
|
panel = 0;
|
||||||
|
}
|
||||||
|
AssertImplies(panel != 0, panel->kind == PanelKind_Final);
|
||||||
|
return(panel);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Panel*
|
||||||
|
layout_get_last_open_panel(Layout *layout){
|
||||||
|
Panel *panel = CastFromMember(Panel, node, layout->open_panels.prev);
|
||||||
|
if (panel != 0 && &panel->node == &layout->open_panels){
|
||||||
|
panel = 0;
|
||||||
|
}
|
||||||
|
AssertImplies(panel != 0, panel->kind == PanelKind_Final);
|
||||||
|
return(panel);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Panel*
|
||||||
|
layout_get_next_open_panel(Layout *layout, Panel *panel){
|
||||||
|
panel = CastFromMember(Panel, node, panel->node.next);
|
||||||
|
if (&panel->node == &layout->open_panels){
|
||||||
|
panel = 0;
|
||||||
|
}
|
||||||
|
AssertImplies(panel != 0, panel->kind == PanelKind_Final);
|
||||||
|
return(panel);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Panel*
|
||||||
|
layout_get_prev_open_panel(Layout *layout, Panel *panel){
|
||||||
|
panel = CastFromMember(Panel, node, panel->node.prev);
|
||||||
|
if (&panel->node == &layout->open_panels){
|
||||||
|
panel = 0;
|
||||||
|
}
|
||||||
|
AssertImplies(panel != 0, panel->kind == PanelKind_Final);
|
||||||
|
return(panel);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Panel*
|
||||||
|
layout_get_active_panel(Layout *layout){
|
||||||
|
return(layout->active_panel);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal b32
|
||||||
|
layout_split_panel(Layout *layout, Panel *panel, b32 vertical_split, Panel **new_panel_out){
|
||||||
|
b32 result = false;
|
||||||
|
if (layout->open_panel_count < layout->open_panel_max_count){
|
||||||
|
Panel *min_panel = layout__alloc_panel(layout);
|
||||||
|
Panel *max_panel = layout__alloc_panel(layout);
|
||||||
|
|
||||||
|
if (panel->kind == PanelKind_Final){
|
||||||
|
dll_remove(&panel->node);
|
||||||
|
dll_insert(&layout->intermediate_panels, &panel->node);
|
||||||
|
|
||||||
|
// init min_panel
|
||||||
|
dll_insert(&layout->open_panels, &min_panel->node);
|
||||||
|
|
||||||
|
panel->view->panel = min_panel;
|
||||||
|
min_panel->parent = panel;
|
||||||
|
min_panel->kind = PanelKind_Final;
|
||||||
|
min_panel->view = panel->view;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
// init min_panel
|
||||||
|
dll_insert(&layout->intermediate_panels, &min_panel->node);
|
||||||
|
|
||||||
|
panel->tl_panel->parent = min_panel;
|
||||||
|
panel->br_panel->parent = min_panel;
|
||||||
|
min_panel->parent = panel;
|
||||||
|
min_panel->kind = PanelKind_Intermediate;
|
||||||
|
min_panel->tl_panel = panel->tl_panel;
|
||||||
|
min_panel->br_panel = panel->br_panel;
|
||||||
|
min_panel->vertical_split = panel->vertical_split;
|
||||||
|
min_panel->split = panel->split;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init max_panel
|
||||||
|
dll_insert(&layout->open_panels, &max_panel->node);
|
||||||
|
|
||||||
|
*new_panel_out = max_panel;
|
||||||
|
max_panel->parent = panel;
|
||||||
|
max_panel->kind = PanelKind_Final;
|
||||||
|
max_panel->view = 0;
|
||||||
|
|
||||||
|
// modify panel
|
||||||
|
panel->kind = PanelKind_Intermediate;
|
||||||
|
panel->tl_panel = min_panel;
|
||||||
|
panel->br_panel = max_panel;
|
||||||
|
panel->vertical_split = vertical_split;
|
||||||
|
panel->split = make_panel_split_50_50();
|
||||||
|
|
||||||
|
// propogate rectangle sizes down from the new intermediate to
|
||||||
|
// resize the panel and the new panel.
|
||||||
|
layout_propogate_sizes_down_from_node(layout, panel);
|
||||||
|
|
||||||
|
// update layout state
|
||||||
|
layout->open_panel_count += 1;
|
||||||
|
layout->active_panel = max_panel;
|
||||||
|
layout->panel_state_dirty = true;
|
||||||
|
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal b32
|
||||||
|
layout_close_panel(Layout *layout, Panel *panel){
|
||||||
|
b32 result = false;
|
||||||
|
if (layout->open_panel_count > 1){
|
||||||
|
Panel *parent = panel->parent;
|
||||||
|
Assert(parent != 0);
|
||||||
|
|
||||||
|
// find sibling
|
||||||
|
Panel *sibling = 0;
|
||||||
|
if (parent->tl_panel == panel){
|
||||||
|
sibling = parent->br_panel;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
Assert(parent->br_panel == panel);
|
||||||
|
sibling = parent->tl_panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update layout state
|
||||||
|
if (layout->active_panel == panel){
|
||||||
|
Panel *new_active = sibling;
|
||||||
|
for (;new_active->kind == PanelKind_Intermediate;){
|
||||||
|
new_active = new_active->br_panel;
|
||||||
|
}
|
||||||
|
layout->active_panel = new_active;
|
||||||
|
}
|
||||||
|
layout->panel_state_dirty = true;
|
||||||
|
layout->open_panel_count -= 1;
|
||||||
|
|
||||||
|
// link grand parent and sibling
|
||||||
|
Panel *g_parent = parent->parent;
|
||||||
|
sibling->parent = g_parent;
|
||||||
|
if (g_parent != 0){
|
||||||
|
if (g_parent->tl_panel == parent){
|
||||||
|
g_parent->tl_panel = sibling;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
Assert(g_parent->br_panel == parent);
|
||||||
|
g_parent->br_panel = sibling;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
Assert(parent == layout->root);
|
||||||
|
layout->root = sibling;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set sibling's size
|
||||||
|
sibling->screen_region = parent->screen_region;
|
||||||
|
|
||||||
|
// set the sizes down stream of sibling
|
||||||
|
layout_propogate_sizes_down_from_node(layout, sibling);
|
||||||
|
|
||||||
|
// free panel and parent
|
||||||
|
layout__free_panel(layout, panel);
|
||||||
|
layout__free_panel(layout, parent);
|
||||||
|
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Panel*
|
||||||
|
layout_initialize(Arena *arena, Layout *layout){
|
||||||
|
i32 panel_alloc_count = MAX_VIEWS*2 - 1;
|
||||||
|
Panel *panels = push_array(arena, Panel, panel_alloc_count);
|
||||||
|
block_zero(panels, sizeof(*panels)*panel_alloc_count);
|
||||||
|
|
||||||
|
layout->panel_first = panels;
|
||||||
|
layout->panel_one_past_last = panels + panel_alloc_count;
|
||||||
|
|
||||||
|
layout->margin = 3;
|
||||||
|
layout->open_panel_count = 0;
|
||||||
|
layout->open_panel_max_count = MAX_VIEWS;
|
||||||
|
|
||||||
|
dll_init_sentinel(&layout->open_panels);
|
||||||
|
dll_init_sentinel(&layout->intermediate_panels);
|
||||||
|
|
||||||
|
Panel *panel = panels;
|
||||||
|
layout->free_panels.next = &panel->node;
|
||||||
|
panel->node.prev = &layout->free_panels;
|
||||||
|
for (i32 i = 1; i < panel_alloc_count; i += 1, panel += 1){
|
||||||
|
panel[1].node.prev = &panel[0].node;
|
||||||
|
panel[0].node.next = &panel[1].node;
|
||||||
|
}
|
||||||
|
panel->node.next = &layout->free_panels;
|
||||||
|
layout->free_panels.prev = &panel->node;
|
||||||
|
|
||||||
|
panel = layout__alloc_panel(layout);
|
||||||
|
panel->parent = 0;
|
||||||
|
panel->kind = PanelKind_Final;
|
||||||
|
panel->view = 0;
|
||||||
|
block_zero_struct(&panel->screen_region);
|
||||||
|
|
||||||
|
dll_insert(&layout->open_panels, &panel->node);
|
||||||
|
layout->open_panel_count += 1;
|
||||||
|
layout->root = panel;
|
||||||
|
layout->active_panel = panel;
|
||||||
|
layout->panel_state_dirty = true;
|
||||||
|
|
||||||
|
return(panel);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
layout_set_margin(Layout *layout, i32 margin){
|
||||||
|
if (layout->margin != margin){
|
||||||
|
layout->margin = margin;
|
||||||
|
layout__set_panel_rectangle(layout, layout->root, Ri32(0, 0, layout->full_dim.x, layout->full_dim.y));
|
||||||
|
layout_propogate_sizes_down_from_node(layout, layout->root);
|
||||||
|
layout->panel_state_dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
layout_set_root_size(Layout *layout, Vec2_i32 dim){
|
||||||
|
if (layout->full_dim != dim){
|
||||||
|
layout->full_dim = dim;
|
||||||
|
layout__set_panel_rectangle(layout, layout->root, Ri32(0, 0, dim.x, dim.y));
|
||||||
|
layout_propogate_sizes_down_from_node(layout, layout->root);
|
||||||
|
layout->panel_state_dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Vec2_i32
|
||||||
|
layout_get_root_size(Layout *layout){
|
||||||
|
return(layout->full_dim);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal i32
|
||||||
|
layout_get_absolute_position_of_split(Panel *panel){
|
||||||
|
i32 pos = 0;
|
||||||
|
if (panel->vertical_split){
|
||||||
|
pos = layout__evaluate_split(panel->split, panel->rect_full.x0, panel->rect_full.x1);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
pos = layout__evaluate_split(panel->split, panel->rect_full.y0, panel->rect_full.y1);
|
||||||
|
}
|
||||||
|
return(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Range_i32
|
||||||
|
layout__get_limiting_range_on_split_children(i32 mid, b32 vertical_split, Panel *panel, Range_i32 range){
|
||||||
|
if (panel->kind == PanelKind_Intermediate){
|
||||||
|
if (vertical_split == panel->vertical_split){
|
||||||
|
i32 pos = layout_get_absolute_position_of_split(panel);
|
||||||
|
if (mid < pos && pos < range.max){
|
||||||
|
range.max = pos;
|
||||||
|
}
|
||||||
|
else if (range.min < pos && pos < mid){
|
||||||
|
range.min = pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
range = layout__get_limiting_range_on_split_children(mid, vertical_split, panel->tl_panel, range);
|
||||||
|
range = layout__get_limiting_range_on_split_children(mid, vertical_split, panel->br_panel, range);
|
||||||
|
}
|
||||||
|
return(range);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Range_i32
|
||||||
|
layout_get_limiting_range_on_split(Layout *layout, Panel *panel){
|
||||||
|
// root level min max
|
||||||
|
Range_i32 range = {};
|
||||||
|
if (panel->vertical_split){
|
||||||
|
range.max = layout->full_dim.x;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
range.max = layout->full_dim.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get mid
|
||||||
|
i32 mid = layout_get_absolute_position_of_split(panel);
|
||||||
|
|
||||||
|
// parents min max
|
||||||
|
for (Panel *panel_it = panel;
|
||||||
|
panel_it != 0;
|
||||||
|
panel_it = panel_it->parent){
|
||||||
|
if (panel->vertical_split == panel_it->vertical_split){
|
||||||
|
i32 pos = layout_get_absolute_position_of_split(panel_it);
|
||||||
|
if (mid < pos && pos < range.max){
|
||||||
|
range.max = pos;
|
||||||
|
}
|
||||||
|
else if (range.min < pos && pos < mid){
|
||||||
|
range.min = pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// children min max
|
||||||
|
if (panel->kind == PanelKind_Intermediate){
|
||||||
|
range = layout__get_limiting_range_on_split_children(mid, panel->vertical_split, panel->tl_panel, range);
|
||||||
|
range = layout__get_limiting_range_on_split_children(mid, panel->vertical_split, panel->br_panel, range);
|
||||||
|
}
|
||||||
|
|
||||||
|
return(range);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
layout__reverse_evaluate_panel_split(Panel *panel, i32 position){
|
||||||
|
i32 v0 = 0;
|
||||||
|
i32 v1 = 0;
|
||||||
|
if (panel->vertical_split){
|
||||||
|
v0 = panel->rect_full.x0;
|
||||||
|
v1 = panel->rect_full.x1;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
v0 = panel->rect_full.y0;
|
||||||
|
v1 = panel->rect_full.y1;
|
||||||
|
}
|
||||||
|
switch (panel->split.kind){
|
||||||
|
case PanelSplitKind_Ratio_Min:
|
||||||
|
{
|
||||||
|
panel->split.v_f32 = unlerp((f32)v0, (f32)position, (f32)v1);
|
||||||
|
}break;
|
||||||
|
case PanelSplitKind_Ratio_Max:
|
||||||
|
{
|
||||||
|
panel->split.v_f32 = unlerp((f32)v1, (f32)position, (f32)v0);
|
||||||
|
}break;
|
||||||
|
case PanelSplitKind_FixedPixels_Min:
|
||||||
|
{
|
||||||
|
panel->split.v_i32 = clamp(v0, position, v1) - v0;
|
||||||
|
}break;
|
||||||
|
case PanelSplitKind_FixedPixels_Max:
|
||||||
|
{
|
||||||
|
panel->split.v_i32 = v1 - clamp(v0, position, v1);
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
layout__set_split_absolute_position_inner(Panel *panel){
|
||||||
|
if (panel->kind == PanelKind_Intermediate){
|
||||||
|
Rect_i32 r = panel->rect_full;
|
||||||
|
i32 position = 0;
|
||||||
|
if (panel->vertical_split){
|
||||||
|
position = layout__evaluate_split(panel->split, r.x0, r.x1);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
position = layout__evaluate_split(panel->split, r.y0, r.y1);
|
||||||
|
}
|
||||||
|
layout__reverse_evaluate_panel_split(panel, position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
layout_set_split_absolute_position(Layout *layout, Panel *panel, i32 absolute_position){
|
||||||
|
if (panel->kind == PanelKind_Intermediate){
|
||||||
|
layout__reverse_evaluate_panel_split(panel, absolute_position);
|
||||||
|
layout__set_split_absolute_position_inner(panel->tl_panel);
|
||||||
|
layout__set_split_absolute_position_inner(panel->br_panel);
|
||||||
|
layout_propogate_sizes_down_from_node(layout, panel);
|
||||||
|
layout->panel_state_dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Panel_ID
|
||||||
|
panel_get_id(Layout *layout, Panel *panel){
|
||||||
|
Panel_ID id = 0;
|
||||||
|
if (layout->panel_first <= panel && panel < layout->panel_one_past_last){
|
||||||
|
id = (Panel_ID)(panel - layout->panel_first) + 1;
|
||||||
|
}
|
||||||
|
return(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
internal Panel*
|
||||||
|
imp_get_panel(Models *models, Panel_ID panel_id){
|
||||||
|
Layout *layout = &models->layout;
|
||||||
|
Panel *panel = layout->panel_first + panel_id - 1;
|
||||||
|
if (!(layout->panel_first <= panel && panel < layout->panel_one_past_last)){
|
||||||
|
panel = 0;
|
||||||
|
}
|
||||||
|
return(panel);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 24.03.2018
|
||||||
|
*
|
||||||
|
* Panel layout structures
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#if !defined(FRED_LAYOUT_H)
|
||||||
|
#define FRED_LAYOUT_H
|
||||||
|
|
||||||
|
struct Panel_Split{
|
||||||
|
Panel_Split_Kind kind;
|
||||||
|
union{
|
||||||
|
f32 v_f32;
|
||||||
|
i32 v_i32;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef i32 Panel_Kind;
|
||||||
|
enum{
|
||||||
|
PanelKind_Unused = 0,
|
||||||
|
PanelKind_Intermediate = 1,
|
||||||
|
PanelKind_Final = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Panel{
|
||||||
|
Node node;
|
||||||
|
|
||||||
|
Panel *parent;
|
||||||
|
Panel_Kind kind;
|
||||||
|
union{
|
||||||
|
struct View *view;
|
||||||
|
struct{
|
||||||
|
struct Panel *tl_panel;
|
||||||
|
struct Panel *br_panel;
|
||||||
|
b32 vertical_split;
|
||||||
|
Panel_Split split;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
union{
|
||||||
|
struct{
|
||||||
|
Rect_i32 rect_full;
|
||||||
|
Rect_i32 rect_inner;
|
||||||
|
} screen_region;
|
||||||
|
struct{
|
||||||
|
Rect_i32 rect_full;
|
||||||
|
Rect_i32 rect_inner;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Layout{
|
||||||
|
Node free_panels;
|
||||||
|
Node open_panels;
|
||||||
|
Node intermediate_panels;
|
||||||
|
|
||||||
|
Panel *root;
|
||||||
|
Panel *active_panel;
|
||||||
|
Panel *panel_first;
|
||||||
|
Panel *panel_one_past_last;
|
||||||
|
|
||||||
|
i32 margin;
|
||||||
|
i32 open_panel_count;
|
||||||
|
i32 open_panel_max_count;
|
||||||
|
Vec2_i32 full_dim;
|
||||||
|
b32 panel_state_dirty;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 14.08.2019
|
||||||
|
*
|
||||||
|
* Core logging implementation.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
global Log global_log = {};
|
||||||
|
|
||||||
|
internal void
|
||||||
|
log_init(void){
|
||||||
|
global_log.mutex = system_mutex_make();
|
||||||
|
global_log.arena = make_arena_system();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal b32
|
||||||
|
log_string(String_Const_u8 str){
|
||||||
|
b32 result = false;
|
||||||
|
i32 thread_id = system_thread_get_id();
|
||||||
|
if (global_log.disabled_thread_id != thread_id){
|
||||||
|
system_mutex_acquire(global_log.mutex);
|
||||||
|
string_list_push(&global_log.arena, &global_log.list, push_string_copy(&global_log.arena, str));
|
||||||
|
system_mutex_release(global_log.mutex);
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
output_file_append(Thread_Context *tctx, Models *models, Editing_File *file, String_Const_u8 value);
|
||||||
|
|
||||||
|
internal b32
|
||||||
|
log_flush(Thread_Context *tctx, Models *models){
|
||||||
|
b32 result = false;
|
||||||
|
|
||||||
|
system_mutex_acquire(global_log.mutex);
|
||||||
|
global_log.disabled_thread_id = system_thread_get_id();
|
||||||
|
|
||||||
|
if (global_log.list.total_size > 0){
|
||||||
|
String_Const_u8 text = string_list_flatten(&global_log.arena, global_log.list);
|
||||||
|
output_file_append(tctx, models, models->log_buffer, text);
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
linalloc_clear(&global_log.arena);
|
||||||
|
block_zero_struct(&global_log.list);
|
||||||
|
|
||||||
|
global_log.disabled_thread_id = 0;
|
||||||
|
system_mutex_release(global_log.mutex);
|
||||||
|
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 14.08.2019
|
||||||
|
*
|
||||||
|
* Core logging structures.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#if !defined(FRED_LOG_H)
|
||||||
|
#define FRED_LOG_H
|
||||||
|
|
||||||
|
struct Log{
|
||||||
|
System_Mutex mutex;
|
||||||
|
Arena arena;
|
||||||
|
List_String_Const_u8 list;
|
||||||
|
volatile i32 disabled_thread_id;
|
||||||
|
b32 stdout_log_enabled;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// BOTTOM
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 30.08.2016
|
||||||
|
*
|
||||||
|
* Replacements for common memory block managing functions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
// TODO(allen): Make these as fast as possible
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
internal void
|
||||||
|
block_zero(void *a, u64 size){
|
||||||
|
for (u8 *ptr = (u8*)a, *e = ptr + size; ptr < e; ptr += 1){
|
||||||
|
*ptr = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal void
|
||||||
|
block_fill_ones(void *a, u64 size){
|
||||||
|
for (u8 *ptr = (u8*)a, *e = ptr + size; ptr < e; ptr += 1){
|
||||||
|
*ptr = 0xFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal void
|
||||||
|
block_copy(void *dst, void *src, u64 size){
|
||||||
|
for (u8 *d = (u8*)dst, *s = (u8*)src, *e = s + size; s < e; d += 1, s += 1){
|
||||||
|
*d = *s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal i32
|
||||||
|
block_compare(void *a, void *b, u64 size){
|
||||||
|
for (u8 *aptr = (u8*)a, *bptr = (u8*)b, *e = bptr + size; bptr < e; aptr += 1, bptr += 1){
|
||||||
|
i32 dif = (i32)*aptr - (i32)*bptr;
|
||||||
|
if (dif != 0){
|
||||||
|
return(dif > 0?1:-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
internal void
|
||||||
|
block_fill_u8(void *a, u64 size, u8 val){
|
||||||
|
for (u8 *ptr = (u8*)a, *e = ptr + size; ptr < e; ptr += 1){
|
||||||
|
*ptr = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal void
|
||||||
|
block_fill_u16(void *a, u64 size, u16 val){
|
||||||
|
Assert(size%sizeof(u16) == 0);
|
||||||
|
u64 count = size/sizeof(u16);
|
||||||
|
for (u16 *ptr = (u16*)a, *e = ptr + count; ptr < e; ptr += 1){
|
||||||
|
*ptr = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal void
|
||||||
|
block_fill_u32(void *a, u64 size, u32 val){
|
||||||
|
Assert(size%sizeof(u32) == 0);
|
||||||
|
u64 count = size/sizeof(u32);
|
||||||
|
for (u32 *ptr = (u32*)a, *e = ptr + count; ptr < e; ptr += 1){
|
||||||
|
*ptr = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal void
|
||||||
|
block_fill_u64(void *a, u64 size, u64 val){
|
||||||
|
Assert(size%sizeof(u64) == 0);
|
||||||
|
u64 count = size/sizeof(u64);
|
||||||
|
for (u64 *ptr = (u64*)a, *e = ptr + count; ptr < e; ptr += 1){
|
||||||
|
*ptr = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define block_zero_struct(s) block_zero((s), sizeof(*(s)))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,351 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 10.11.2017
|
||||||
|
*
|
||||||
|
* Render target function implementations.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
internal void
|
||||||
|
draw__begin_new_group(Render_Target *target){
|
||||||
|
Render_Group *group = 0;
|
||||||
|
if (target->group_last != 0){
|
||||||
|
if (target->group_last->vertex_list.vertex_count == 0){
|
||||||
|
group = target->group_last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (group == 0){
|
||||||
|
group = push_array_zero(&target->arena, Render_Group, 1);
|
||||||
|
sll_queue_push(target->group_first, target->group_last, group);
|
||||||
|
}
|
||||||
|
group->face_id = target->current_face_id;
|
||||||
|
group->clip_box = target->current_clip_box;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Render_Vertex_Array_Node*
|
||||||
|
draw__extend_group_vertex_memory(Arena *arena, Render_Vertex_List *list, i32 size){
|
||||||
|
Render_Vertex_Array_Node *node = push_array_zero(arena, Render_Vertex_Array_Node, 1);
|
||||||
|
sll_queue_push(list->first, list->last, node);
|
||||||
|
node->vertices = push_array(arena, Render_Vertex, size);
|
||||||
|
node->vertex_max = size;
|
||||||
|
return(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
draw__write_vertices_in_current_group(Render_Target *target, Render_Vertex *vertices, i32 count){
|
||||||
|
if (count > 0){
|
||||||
|
Render_Group *group = target->group_last;
|
||||||
|
if (group == 0){
|
||||||
|
draw__begin_new_group(target);
|
||||||
|
group = target->group_last;
|
||||||
|
}
|
||||||
|
|
||||||
|
Render_Vertex_List *list = &group->vertex_list;
|
||||||
|
|
||||||
|
Render_Vertex_Array_Node *last = list->last;
|
||||||
|
|
||||||
|
Render_Vertex *tail_vertex = 0;
|
||||||
|
i32 tail_count = 0;
|
||||||
|
if (last != 0){
|
||||||
|
tail_vertex = last->vertices + last->vertex_count;
|
||||||
|
tail_count = last->vertex_max - last->vertex_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
i32 base_vertex_max = 64;
|
||||||
|
i32 transfer_count = clamp_top(count, tail_count);
|
||||||
|
if (transfer_count > 0){
|
||||||
|
block_copy_dynamic_array(tail_vertex, vertices, transfer_count);
|
||||||
|
last->vertex_count += transfer_count;
|
||||||
|
list->vertex_count += transfer_count;
|
||||||
|
base_vertex_max = last->vertex_max;
|
||||||
|
}
|
||||||
|
|
||||||
|
i32 count_left_over = count - transfer_count;
|
||||||
|
if (count_left_over > 0){
|
||||||
|
Render_Vertex *vertices_left_over = vertices + transfer_count;
|
||||||
|
|
||||||
|
i32 next_node_size = (base_vertex_max + count_left_over)*2;
|
||||||
|
Render_Vertex_Array_Node *memory = draw__extend_group_vertex_memory(&target->arena, list, next_node_size);
|
||||||
|
block_copy_dynamic_array(memory->vertices, vertices_left_over, count_left_over);
|
||||||
|
memory->vertex_count += count_left_over;
|
||||||
|
list->vertex_count += count_left_over;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
draw__set_face_id(Render_Target *target, Face_ID face_id){
|
||||||
|
if (target->current_face_id != face_id){
|
||||||
|
if (target->current_face_id != 0){
|
||||||
|
target->current_face_id = face_id;
|
||||||
|
draw__begin_new_group(target);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
target->current_face_id = face_id;
|
||||||
|
for (Render_Group *group = target->group_first;
|
||||||
|
group != 0;
|
||||||
|
group = group->next){
|
||||||
|
group->face_id = face_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
internal Rect_f32
|
||||||
|
draw_set_clip(Render_Target *target, Rect_f32 clip_box){
|
||||||
|
Rect_f32 result = target->current_clip_box;
|
||||||
|
if (target->current_clip_box != clip_box){
|
||||||
|
target->current_clip_box = clip_box;
|
||||||
|
draw__begin_new_group(target);
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
begin_frame(Render_Target *target, void *font_set){
|
||||||
|
linalloc_clear(&target->arena);
|
||||||
|
target->group_first = 0;
|
||||||
|
target->group_last = 0;
|
||||||
|
target->current_face_id = 0;
|
||||||
|
target->current_clip_box = Rf32(0, 0, (f32)target->width, (f32)target->height);
|
||||||
|
target->font_set = font_set;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
begin_render_section(Render_Target *target, i32 frame_index, f32 literal_dt, f32 animation_dt){
|
||||||
|
target->frame_index = frame_index;
|
||||||
|
target->literal_dt = literal_dt;
|
||||||
|
target->animation_dt = animation_dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
end_render_section(Render_Target *target){
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
internal void
|
||||||
|
draw_rectangle_outline(Render_Target *target, Rect_f32 rect, f32 roundness, f32 thickness, u32 color){
|
||||||
|
if (roundness < epsilon_f32){
|
||||||
|
roundness = 0.f;
|
||||||
|
}
|
||||||
|
thickness = clamp_bot(1.f, thickness);
|
||||||
|
f32 half_thickness = thickness*0.5f;
|
||||||
|
|
||||||
|
Render_Vertex vertices[6] = {};
|
||||||
|
vertices[0].xy = V2f32(rect.x0, rect.y0);
|
||||||
|
vertices[1].xy = V2f32(rect.x1, rect.y0);
|
||||||
|
vertices[2].xy = V2f32(rect.x0, rect.y1);
|
||||||
|
vertices[3].xy = V2f32(rect.x1, rect.y0);
|
||||||
|
vertices[4].xy = V2f32(rect.x0, rect.y1);
|
||||||
|
vertices[5].xy = V2f32(rect.x1, rect.y1);
|
||||||
|
|
||||||
|
Vec2_f32 center = rect_center(rect);
|
||||||
|
for (i32 i = 0; i < ArrayCount(vertices); i += 1){
|
||||||
|
vertices[i].uvw = V3f32(center.x, center.y, roundness);
|
||||||
|
vertices[i].color = color;
|
||||||
|
vertices[i].half_thickness = half_thickness;
|
||||||
|
}
|
||||||
|
draw__write_vertices_in_current_group(target, vertices, ArrayCount(vertices));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
draw_rectangle(Render_Target *target, Rect_f32 rect, f32 roundness, u32 color){
|
||||||
|
Vec2_f32 dim = rect_dim(rect);
|
||||||
|
draw_rectangle_outline(target, rect, roundness, Max(dim.x, dim.y), color);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
draw_font_glyph(Render_Target *target, Face *face, u32 codepoint, Vec2_f32 p,
|
||||||
|
ARGB_Color color, Glyph_Flag flags, Vec2_f32 x_axis){
|
||||||
|
draw__set_face_id(target, face->id);
|
||||||
|
|
||||||
|
u16 glyph_index = 0;
|
||||||
|
if (!codepoint_index_map_read(&face->advance_map.codepoint_to_index,
|
||||||
|
codepoint, &glyph_index)){
|
||||||
|
glyph_index = 0;
|
||||||
|
}
|
||||||
|
Glyph_Bounds bounds = face->bounds[glyph_index];
|
||||||
|
Vec3_f32 texture_dim = face->texture_dim;
|
||||||
|
|
||||||
|
Render_Vertex vertices[6] = {};
|
||||||
|
|
||||||
|
Rect_f32 uv = bounds.uv;
|
||||||
|
vertices[0].uvw = V3f32(uv.x0, uv.y0, bounds.w);
|
||||||
|
vertices[1].uvw = V3f32(uv.x1, uv.y0, bounds.w);
|
||||||
|
vertices[2].uvw = V3f32(uv.x0, uv.y1, bounds.w);
|
||||||
|
vertices[5].uvw = V3f32(uv.x1, uv.y1, bounds.w);
|
||||||
|
|
||||||
|
Vec2_f32 y_axis = V2f32(-x_axis.y, x_axis.x);
|
||||||
|
Vec2_f32 x_min = bounds.xy_off.x0*x_axis;
|
||||||
|
Vec2_f32 x_max = bounds.xy_off.x1*x_axis;
|
||||||
|
Vec2_f32 y_min = bounds.xy_off.y0*y_axis;
|
||||||
|
Vec2_f32 y_max = bounds.xy_off.y1*y_axis;
|
||||||
|
Vec2_f32 p_x_min = p + x_min;
|
||||||
|
Vec2_f32 p_x_max = p + x_max;
|
||||||
|
vertices[0].xy = p_x_min + y_min;
|
||||||
|
vertices[1].xy = p_x_max + y_min;
|
||||||
|
vertices[2].xy = p_x_min + y_max;
|
||||||
|
vertices[5].xy = p_x_max + y_max;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
Vec2_f32 xy_min = p + bounds.xy_off.x0*x_axis + bounds.xy_off.y0*y_axis;
|
||||||
|
Vec2_f32 xy_max = p + bounds.xy_off.x1*x_axis + bounds.xy_off.y1*y_axis;
|
||||||
|
|
||||||
|
vertices[0].xy = V2f32(xy_min.x, xy_min.y);
|
||||||
|
vertices[1].xy = V2f32(xy_max.x, xy_min.y);
|
||||||
|
vertices[2].xy = V2f32(xy_min.x, xy_max.y);
|
||||||
|
vertices[5].xy = V2f32(xy_max.x, xy_max.y);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
if (!HasFlag(flags, GlyphFlag_Rotate90)){
|
||||||
|
Rect_f32 xy = Rf32(p + bounds.xy_off.p0, p + bounds.xy_off.p1);
|
||||||
|
|
||||||
|
vertices[0].xy = V2f32(xy.x0, xy.y1);
|
||||||
|
vertices[0].uvw = V3f32(uv.x0, uv.y1, bounds.w);
|
||||||
|
vertices[1].xy = V2f32(xy.x1, xy.y1);
|
||||||
|
vertices[1].uvw = V3f32(uv.x1, uv.y1, bounds.w);
|
||||||
|
vertices[2].xy = V2f32(xy.x0, xy.y0);
|
||||||
|
vertices[2].uvw = V3f32(uv.x0, uv.y0, bounds.w);
|
||||||
|
vertices[5].xy = V2f32(xy.x1, xy.y0);
|
||||||
|
vertices[5].uvw = V3f32(uv.x1, uv.y0, bounds.w);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
Rect_f32 xy = Rf32(p.x - bounds.xy_off.y1, p.y + bounds.xy_off.x0,
|
||||||
|
p.x - bounds.xy_off.y0, p.y + bounds.xy_off.x1);
|
||||||
|
|
||||||
|
vertices[0].xy = V2f32(xy.x0, xy.y1);
|
||||||
|
vertices[0].uvw = V3f32(uv.x1, uv.y1, bounds.w);
|
||||||
|
vertices[1].xy = V2f32(xy.x1, xy.y1);
|
||||||
|
vertices[1].uvw = V3f32(uv.x1, uv.y0, bounds.w);
|
||||||
|
vertices[2].xy = V2f32(xy.x0, xy.y0);
|
||||||
|
vertices[2].uvw = V3f32(uv.x0, uv.y1, bounds.w);
|
||||||
|
vertices[5].xy = V2f32(xy.x1, xy.y0);
|
||||||
|
vertices[5].uvw = V3f32(uv.x0, uv.y0, bounds.w);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
vertices[3] = vertices[1];
|
||||||
|
vertices[4] = vertices[2];
|
||||||
|
|
||||||
|
for (i32 i = 0; i < ArrayCount(vertices); i += 1){
|
||||||
|
vertices[i].color = color;
|
||||||
|
vertices[i].half_thickness = 0.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
draw__write_vertices_in_current_group(target, vertices, ArrayCount(vertices));
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
internal Vec2_f32
|
||||||
|
floor32(Vec2_f32 point){
|
||||||
|
point.x = f32_floor32(point.x);
|
||||||
|
point.y = f32_floor32(point.y);
|
||||||
|
return(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal f32
|
||||||
|
draw_string(Render_Target *target, Face *face, String_Const_u8 string, Vec2_f32 point,
|
||||||
|
ARGB_Color color, u32 flags, Vec2_f32 delta){
|
||||||
|
f32 total_delta = 0.f;
|
||||||
|
if (face != 0){
|
||||||
|
point = floor32(point);
|
||||||
|
|
||||||
|
f32 byte_advance = face->metrics.byte_advance;
|
||||||
|
f32 *byte_sub_advances = face->metrics.byte_sub_advances;
|
||||||
|
|
||||||
|
u8 *str = (u8*)string.str;
|
||||||
|
u8 *str_end = str + string.size;
|
||||||
|
|
||||||
|
Translation_State tran = {};
|
||||||
|
Translation_Emits emits = {};
|
||||||
|
|
||||||
|
for (u32 i = 0; str < str_end; ++str, ++i){
|
||||||
|
translating_fully_process_byte(&tran, *str, i, (i32)string.size, &emits);
|
||||||
|
|
||||||
|
for (TRANSLATION_DECL_EMIT_LOOP(J, emits)){
|
||||||
|
TRANSLATION_DECL_GET_STEP(step, behavior, J, emits);
|
||||||
|
|
||||||
|
if (behavior.do_codepoint_advance){
|
||||||
|
u32 codepoint = step.value;
|
||||||
|
if (color != 0){
|
||||||
|
u32 draw_codepoint = step.value;
|
||||||
|
if (draw_codepoint == '\t'){
|
||||||
|
draw_codepoint = ' ';
|
||||||
|
}
|
||||||
|
draw_font_glyph(target, face, draw_codepoint, point, color, flags, delta);
|
||||||
|
}
|
||||||
|
local_const f32 internal_tab_width = 4.f;
|
||||||
|
f32 d = font_get_glyph_advance(&face->advance_map, &face->metrics, codepoint, internal_tab_width);
|
||||||
|
point += d*delta;
|
||||||
|
total_delta += d;
|
||||||
|
}
|
||||||
|
else if (behavior.do_number_advance){
|
||||||
|
u8 n = (u8)(step.value);
|
||||||
|
if (color != 0){
|
||||||
|
u8 cs[3];
|
||||||
|
cs[0] = '\\';
|
||||||
|
u8 nh = (n >> 4);
|
||||||
|
u8 nl = (n & 0xF);
|
||||||
|
u8 ch = '0' + nh;
|
||||||
|
u8 cl = '0' + nl;
|
||||||
|
if (nh > 0x9){
|
||||||
|
ch = ('A' - 0xA) + nh;
|
||||||
|
}
|
||||||
|
if (nl > 0x9){
|
||||||
|
cl = ('A' - 0xA) + nl;
|
||||||
|
}
|
||||||
|
cs[1] = ch;
|
||||||
|
cs[2] = cl;
|
||||||
|
|
||||||
|
Vec2_f32 pp = point;
|
||||||
|
for (u32 j = 0; j < 3; ++j){
|
||||||
|
draw_font_glyph(target, face, cs[j], pp, color, flags, delta);
|
||||||
|
pp += delta*byte_sub_advances[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
point += byte_advance*delta;
|
||||||
|
total_delta += byte_advance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return(total_delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal f32
|
||||||
|
draw_string(Render_Target *target, Face *face, String_Const_u8 string, Vec2_f32 point, u32 color){
|
||||||
|
return(draw_string(target, face, string, point, color, 0, V2f32(1.f, 0.f)));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal f32
|
||||||
|
draw_string(Render_Target *target, Face *face, u8 *str, Vec2_f32 point, u32 color, u32 flags, Vec2_f32 delta){
|
||||||
|
return(draw_string(target, face, SCu8(str), point, color, flags, delta));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal f32
|
||||||
|
draw_string(Render_Target *target, Face *face, u8 *str, Vec2_f32 point, u32 color){
|
||||||
|
return(draw_string(target, face, SCu8(str), point, color, 0, V2f32(1.f, 0.f)));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal f32
|
||||||
|
font_string_width(Render_Target *target, Face *face, String_Const_u8 str){
|
||||||
|
return(draw_string(target, face, str, V2f32(0, 0), 0, 0, V2f32(0, 0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal f32
|
||||||
|
font_string_width(Render_Target *target, Face *face, u8 *str){
|
||||||
|
return(draw_string(target, face, SCu8(str), V2f32(0, 0), 0, 0, V2f32(0, 0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 10.11.2017
|
||||||
|
*
|
||||||
|
* Render target type definition
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#if !defined(FRED_RENDER_TARGET_H)
|
||||||
|
#define FRED_RENDER_TARGET_H
|
||||||
|
|
||||||
|
struct Render_Free_Texture{
|
||||||
|
Render_Free_Texture *next;
|
||||||
|
u32 tex_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Render_Vertex{
|
||||||
|
Vec2_f32 xy;
|
||||||
|
Vec3_f32 uvw;
|
||||||
|
u32 color;
|
||||||
|
f32 half_thickness;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Render_Vertex_Array_Node{
|
||||||
|
Render_Vertex_Array_Node *next;
|
||||||
|
Render_Vertex *vertices;
|
||||||
|
i32 vertex_count;
|
||||||
|
i32 vertex_max;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Render_Vertex_List{
|
||||||
|
Render_Vertex_Array_Node *first;
|
||||||
|
Render_Vertex_Array_Node *last;
|
||||||
|
i32 vertex_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Render_Group{
|
||||||
|
Render_Group *next;
|
||||||
|
Render_Vertex_List vertex_list;
|
||||||
|
// parameters
|
||||||
|
Face_ID face_id;
|
||||||
|
Rect_f32 clip_box;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Render_Target{
|
||||||
|
b8 clip_all;
|
||||||
|
i32 width;
|
||||||
|
i32 height;
|
||||||
|
i32 bound_texture;
|
||||||
|
u32 color;
|
||||||
|
|
||||||
|
i32 frame_index;
|
||||||
|
f32 literal_dt;
|
||||||
|
f32 animation_dt;
|
||||||
|
|
||||||
|
Render_Free_Texture *free_texture_first;
|
||||||
|
Render_Free_Texture *free_texture_last;
|
||||||
|
|
||||||
|
Arena arena;
|
||||||
|
Render_Group *group_first;
|
||||||
|
Render_Group *group_last;
|
||||||
|
i32 group_count;
|
||||||
|
|
||||||
|
Face_ID current_face_id;
|
||||||
|
Rect_f32 current_clip_box;
|
||||||
|
void *font_set;
|
||||||
|
u32 fallback_texture_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,333 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 16.06.2019
|
||||||
|
*
|
||||||
|
* Routines for string matching within chunked streams.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
internal u64_Array
|
||||||
|
string_compute_prefix_table(Arena *arena, String_Const_u8 string, Scan_Direction direction){
|
||||||
|
u64_Array array = {};
|
||||||
|
array.count = (i32)(string.size);
|
||||||
|
array.vals = push_array(arena, u64, array.count);
|
||||||
|
|
||||||
|
u8 *str = string.str;
|
||||||
|
if (direction == Scan_Backward){
|
||||||
|
str = string.str + string.size - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
array.vals[0] = 0;
|
||||||
|
for (u64 i = 1; i < string.size; i += 1){
|
||||||
|
u64 previous_longest_prefix = array.vals[i - 1];
|
||||||
|
for (;;){
|
||||||
|
u8 *a = str + previous_longest_prefix;
|
||||||
|
u8 *b = str + i;
|
||||||
|
if (direction == Scan_Backward){
|
||||||
|
a = str - previous_longest_prefix;
|
||||||
|
b = str - i;
|
||||||
|
}
|
||||||
|
if (character_to_upper(*a) == character_to_upper(*b)){
|
||||||
|
array.vals[i] = previous_longest_prefix + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (previous_longest_prefix == 0){
|
||||||
|
array.vals[i] = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
previous_longest_prefix = array.vals[previous_longest_prefix - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal u64_Array
|
||||||
|
string_compute_needle_jump_table(Arena *arena, u64_Array longest_prefixes){
|
||||||
|
u64_Array array = {};
|
||||||
|
array.count = longest_prefixes.count + 1;
|
||||||
|
array.vals = push_array(arena, u64, array.count);
|
||||||
|
array.vals[0] = 0;
|
||||||
|
for (u64 i = 1; i < array.count; i += 1){
|
||||||
|
array.vals[i] = i - longest_prefixes.vals[i - 1];
|
||||||
|
}
|
||||||
|
return(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal u64_Array
|
||||||
|
string_compute_needle_jump_table(Arena *arena, String_Const_u8 needle, Scan_Direction direction){
|
||||||
|
u64_Array prefix_table = string_compute_prefix_table(arena, needle, direction);
|
||||||
|
return(string_compute_needle_jump_table(arena, prefix_table));
|
||||||
|
}
|
||||||
|
|
||||||
|
#define character_predicate_check_character(p, c) (((p).b[(c)/8] & (1 << ((c)%8))) != 0)
|
||||||
|
|
||||||
|
internal String_Match_List
|
||||||
|
find_all_matches_forward(Arena *arena, i32 maximum_output_count,
|
||||||
|
List_String_Const_u8 chunks, String_Const_u8 needle,
|
||||||
|
u64_Array jump_table, Character_Predicate *predicate,
|
||||||
|
u64 base_index, Buffer_ID buffer, i32 string_id){
|
||||||
|
String_Match_List list = {};
|
||||||
|
|
||||||
|
if (chunks.node_count > 0){
|
||||||
|
u64 i = 0;
|
||||||
|
u64 j = 0;
|
||||||
|
b8 current_l = false;
|
||||||
|
i64 last_insensitive = -1;
|
||||||
|
i64 last_boundary = -1;
|
||||||
|
|
||||||
|
Node_String_Const_u8 *node = chunks.first;
|
||||||
|
i64 chunk_pos = 0;
|
||||||
|
|
||||||
|
i32 jump_back_code = 0;
|
||||||
|
|
||||||
|
u8 c = 0;
|
||||||
|
u64 n = 0;
|
||||||
|
u8 needle_c = 0;
|
||||||
|
u64 jump = 0;
|
||||||
|
|
||||||
|
if (false){
|
||||||
|
iterate_forward:
|
||||||
|
i += 1;
|
||||||
|
chunk_pos += 1;
|
||||||
|
if (chunk_pos >= (i64)node->string.size){
|
||||||
|
last_boundary = i;
|
||||||
|
chunk_pos = 0;
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (jump_back_code){
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
goto jump_back_0;
|
||||||
|
}break;
|
||||||
|
case 1:
|
||||||
|
{
|
||||||
|
goto jump_back_1;
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;node != 0;){
|
||||||
|
c = node->string.str[chunk_pos];
|
||||||
|
n = i - j;
|
||||||
|
needle_c = needle.str[n];
|
||||||
|
if (character_to_upper(c) == character_to_upper(needle_c)){
|
||||||
|
if (c != needle_c){
|
||||||
|
last_insensitive = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
jump_back_code = 0;
|
||||||
|
goto iterate_forward;
|
||||||
|
jump_back_0:
|
||||||
|
|
||||||
|
if (n + 1 == needle.size){
|
||||||
|
String_Match_Flag flags = 0;
|
||||||
|
if (!(last_insensitive >= 0 &&
|
||||||
|
j <= (u64)last_insensitive &&
|
||||||
|
(u64)last_insensitive < j + needle.size)){
|
||||||
|
AddFlag(flags, StringMatch_CaseSensitive);
|
||||||
|
}
|
||||||
|
if (!(last_boundary >= 0 &&
|
||||||
|
j <= (u64)last_boundary &&
|
||||||
|
(u64)last_boundary < j + needle.size)){
|
||||||
|
AddFlag(flags, StringMatch_Straddled);
|
||||||
|
}
|
||||||
|
if (node != 0){
|
||||||
|
u8 next_c = node->string.str[chunk_pos];
|
||||||
|
if (character_predicate_check_character(*predicate, next_c)){
|
||||||
|
AddFlag(flags, StringMatch_RightSideSloppy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (current_l){
|
||||||
|
AddFlag(flags, StringMatch_LeftSideSloppy);
|
||||||
|
}
|
||||||
|
string_match_list_push(arena, &list, buffer, string_id, flags,
|
||||||
|
base_index + j, needle.size);
|
||||||
|
if (list.count >= maximum_output_count){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
jump = jump_table.vals[n + 1];
|
||||||
|
current_l = character_predicate_check_character(*predicate, needle.str[jump - 1]);
|
||||||
|
j += jump;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
jump = jump_table.vals[n];
|
||||||
|
if (jump == 0){
|
||||||
|
current_l = character_predicate_check_character(*predicate, c);
|
||||||
|
|
||||||
|
jump_back_code = 1;
|
||||||
|
goto iterate_forward;
|
||||||
|
jump_back_1:
|
||||||
|
|
||||||
|
j += 1;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
u8 prev_c = needle.str[jump - 1];
|
||||||
|
current_l = character_predicate_check_character(*predicate, prev_c);
|
||||||
|
j += jump;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal String_Match_List
|
||||||
|
find_all_matches_backward(Arena *arena, i32 maximum_output_count,
|
||||||
|
List_String_Const_u8 chunks, String_Const_u8 needle,
|
||||||
|
u64_Array jump_table, Character_Predicate *predicate,
|
||||||
|
u64 base_index, Buffer_ID buffer, i32 string_id){
|
||||||
|
String_Match_List list = {};
|
||||||
|
|
||||||
|
string_list_reverse(&chunks);
|
||||||
|
|
||||||
|
if (chunks.node_count > 0){
|
||||||
|
i64 size = (i64)chunks.total_size;
|
||||||
|
|
||||||
|
i64 i = size - 1;
|
||||||
|
i64 j = size - 1;
|
||||||
|
b8 current_r = false;
|
||||||
|
i64 last_insensitive = size;
|
||||||
|
i64 last_boundary = size;
|
||||||
|
|
||||||
|
Node_String_Const_u8 *node = chunks.first;
|
||||||
|
i64 chunk_pos = node->string.size - 1;
|
||||||
|
|
||||||
|
i32 jump_back_code = 0;
|
||||||
|
|
||||||
|
u8 c = 0;
|
||||||
|
u64 n = 0;
|
||||||
|
u8 needle_c = 0;
|
||||||
|
u64 jump = 0;
|
||||||
|
|
||||||
|
if (false){
|
||||||
|
iterate_backward:
|
||||||
|
i -= 1;
|
||||||
|
chunk_pos -= 1;
|
||||||
|
if (chunk_pos < 0){
|
||||||
|
last_boundary = i;
|
||||||
|
node = node->next;
|
||||||
|
if (node != 0){
|
||||||
|
chunk_pos = node->string.size - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (jump_back_code){
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
goto jump_back_0;
|
||||||
|
}break;
|
||||||
|
case 1:
|
||||||
|
{
|
||||||
|
goto jump_back_1;
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;node != 0;){
|
||||||
|
c = node->string.str[chunk_pos];
|
||||||
|
n = j - i;
|
||||||
|
needle_c = needle.str[needle.size - 1 - n];
|
||||||
|
if (character_to_upper(c) == character_to_upper(needle_c)){
|
||||||
|
if (c != needle_c){
|
||||||
|
last_insensitive = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
jump_back_code = 0;
|
||||||
|
goto iterate_backward;
|
||||||
|
jump_back_0:
|
||||||
|
|
||||||
|
if (n + 1 == needle.size){
|
||||||
|
String_Match_Flag flags = 0;
|
||||||
|
if (!(last_insensitive < size &&
|
||||||
|
j >= last_insensitive &&
|
||||||
|
last_insensitive > j - (i64)needle.size)){
|
||||||
|
AddFlag(flags, StringMatch_CaseSensitive);
|
||||||
|
}
|
||||||
|
if (!(last_boundary < size &&
|
||||||
|
j >= last_boundary &&
|
||||||
|
last_boundary > j - (i64)needle.size)){
|
||||||
|
AddFlag(flags, StringMatch_Straddled);
|
||||||
|
}
|
||||||
|
if (node != 0){
|
||||||
|
u8 next_c = node->string.str[chunk_pos];
|
||||||
|
if (character_predicate_check_character(*predicate, next_c)){
|
||||||
|
AddFlag(flags, StringMatch_LeftSideSloppy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (current_r){
|
||||||
|
AddFlag(flags, StringMatch_RightSideSloppy);
|
||||||
|
}
|
||||||
|
string_match_list_push(arena, &list, buffer, string_id, flags,
|
||||||
|
base_index + (j - (needle.size - 1)), needle.size);
|
||||||
|
if (list.count >= maximum_output_count){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
jump = jump_table.vals[n + 1];
|
||||||
|
u64 m = needle.size - jump;
|
||||||
|
u8 needle_m = needle.str[m];
|
||||||
|
current_r = character_predicate_check_character(*predicate, needle_m);
|
||||||
|
j -= jump;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
jump = jump_table.vals[n];
|
||||||
|
if (jump == 0){
|
||||||
|
current_r = character_predicate_check_character(*predicate, c);
|
||||||
|
|
||||||
|
jump_back_code = 1;
|
||||||
|
goto iterate_backward;
|
||||||
|
jump_back_1:
|
||||||
|
|
||||||
|
j -= 1;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
u64 m = needle.size - jump;
|
||||||
|
u8 needle_m = needle.str[m];
|
||||||
|
current_r = character_predicate_check_character(*predicate, needle_m);
|
||||||
|
j -= jump;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string_list_reverse(&chunks);
|
||||||
|
|
||||||
|
return(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal String_Match_List
|
||||||
|
find_all_matches(Arena *arena, i32 maximum_output_count,
|
||||||
|
List_String_Const_u8 chunks, String_Const_u8 needle,
|
||||||
|
u64_Array jump_table, Character_Predicate *predicate,
|
||||||
|
Scan_Direction direction,
|
||||||
|
u64 base_index, Buffer_ID buffer, i32 string_id){
|
||||||
|
String_Match_List list = {};
|
||||||
|
switch (direction){
|
||||||
|
case Scan_Forward:
|
||||||
|
{
|
||||||
|
list = find_all_matches_forward(arena, maximum_output_count,
|
||||||
|
chunks, needle, jump_table, predicate,
|
||||||
|
base_index, buffer, string_id);
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case Scan_Backward:
|
||||||
|
{
|
||||||
|
list = find_all_matches_backward(arena, maximum_output_count,
|
||||||
|
chunks, needle, jump_table, predicate,
|
||||||
|
base_index, buffer, string_id);
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
return(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,331 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 02.10.2019
|
||||||
|
*
|
||||||
|
* System API definition program.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#include "4ed_api_definition_main.cpp"
|
||||||
|
|
||||||
|
function API_Definition*
|
||||||
|
define_api(Arena *arena){
|
||||||
|
API_Definition *api = begin_api(arena, "system");
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "error_box", "void");
|
||||||
|
api_param(arena, call, "char*", "msg");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "get_path", "String_Const_u8");
|
||||||
|
api_param(arena, call, "Arena*", "arena");
|
||||||
|
api_param(arena, call, "System_Path_Code", "path_code");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "get_canonical", "String_Const_u8");
|
||||||
|
api_param(arena, call, "Arena*", "arena");
|
||||||
|
api_param(arena, call, "String_Const_u8", "name");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "get_file_list", "File_List");
|
||||||
|
api_param(arena, call, "Arena*", "arena");
|
||||||
|
api_param(arena, call, "String_Const_u8", "directory");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "quick_file_attributes", "File_Attributes");
|
||||||
|
api_param(arena, call, "Arena*", "scratch");
|
||||||
|
api_param(arena, call, "String_Const_u8", "file_name");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "load_handle", "b32");
|
||||||
|
api_param(arena, call, "Arena*", "scratch");
|
||||||
|
api_param(arena, call, "char*", "file_name");
|
||||||
|
api_param(arena, call, "Plat_Handle*", "out");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "load_attributes", "File_Attributes");
|
||||||
|
api_param(arena, call, "Plat_Handle", "handle");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "load_file", "b32");
|
||||||
|
api_param(arena, call, "Plat_Handle", "handle");
|
||||||
|
api_param(arena, call, "char*", "buffer");
|
||||||
|
api_param(arena, call, "u32", "size");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "load_close", "b32");
|
||||||
|
api_param(arena, call, "Plat_Handle", "handle");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "save_file", "File_Attributes");
|
||||||
|
api_param(arena, call, "Arena*", "scratch");
|
||||||
|
api_param(arena, call, "char*", "file_name");
|
||||||
|
api_param(arena, call, "String_Const_u8", "data");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "load_library", "b32");
|
||||||
|
api_param(arena, call, "Arena*", "scratch");
|
||||||
|
api_param(arena, call, "String_Const_u8", "file_name");
|
||||||
|
api_param(arena, call, "System_Library*", "out");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "release_library", "b32");
|
||||||
|
api_param(arena, call, "System_Library", "handle");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "get_proc", "Void_Func*");
|
||||||
|
api_param(arena, call, "System_Library", "handle");
|
||||||
|
api_param(arena, call, "char*", "proc_name");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
api_call(arena, api, "now_time", "u64");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
api_call(arena, api, "now_date_time_universal", "Date_Time");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "local_date_time_from_universal", "Date_Time");
|
||||||
|
api_param(arena, call, "Date_Time*", "date_time");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "universal_date_time_from_local", "Date_Time");
|
||||||
|
api_param(arena, call, "Date_Time*", "date_time");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
api_call(arena, api, "wake_up_timer_create", "Plat_Handle");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "wake_up_timer_release", "void");
|
||||||
|
api_param(arena, call, "Plat_Handle", "handle");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "wake_up_timer_set", "void");
|
||||||
|
api_param(arena, call, "Plat_Handle", "handle");
|
||||||
|
api_param(arena, call, "u32", "time_milliseconds");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "signal_step", "void");
|
||||||
|
api_param(arena, call, "u32", "code");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "sleep", "void");
|
||||||
|
api_param(arena, call, "u64", "microseconds");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "get_clipboard", "String_Const_u8");
|
||||||
|
api_param(arena, call, "Arena*", "arena");
|
||||||
|
api_param(arena, call, "i32", "index");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "post_clipboard", "void");
|
||||||
|
api_param(arena, call, "String_Const_u8", "str");
|
||||||
|
api_param(arena, call, "i32", "index");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "set_clipboard_catch_all", "void");
|
||||||
|
api_param(arena, call, "b32", "enabled");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
api_call(arena, api, "get_clipboard_catch_all", "b32");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "cli_call", "b32");
|
||||||
|
api_param(arena, call, "Arena*", "scratch");
|
||||||
|
api_param(arena, call, "char*", "path");
|
||||||
|
api_param(arena, call, "char*", "script");
|
||||||
|
api_param(arena, call, "CLI_Handles*", "cli_out");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "cli_begin_update", "void");
|
||||||
|
api_param(arena, call, "CLI_Handles*", "cli");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "cli_update_step", "b32");
|
||||||
|
api_param(arena, call, "CLI_Handles*", "cli");
|
||||||
|
api_param(arena, call, "char*", "dest");
|
||||||
|
api_param(arena, call, "u32", "max");
|
||||||
|
api_param(arena, call, "u32*", "amount");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "cli_end_update", "b32");
|
||||||
|
api_param(arena, call, "CLI_Handles*", "cli");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "open_color_picker", "void");
|
||||||
|
api_param(arena, call, "Color_Picker*", "picker");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
api_call(arena, api, "get_screen_scale_factor", "f32");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "thread_launch", "System_Thread");
|
||||||
|
api_param(arena, call, "Thread_Function*", "proc");
|
||||||
|
api_param(arena, call, "void*", "ptr");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "thread_join", "void");
|
||||||
|
api_param(arena, call, "System_Thread", "thread");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "thread_free", "void");
|
||||||
|
api_param(arena, call, "System_Thread", "thread");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
api_call(arena, api, "thread_get_id", "i32");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "acquire_global_frame_mutex", "void");
|
||||||
|
api_param(arena, call, "Thread_Context*", "tctx");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "release_global_frame_mutex", "void");
|
||||||
|
api_param(arena, call, "Thread_Context*", "tctx");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
api_call(arena, api, "mutex_make", "System_Mutex");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "mutex_acquire", "void");
|
||||||
|
api_param(arena, call, "System_Mutex", "mutex");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "mutex_release", "void");
|
||||||
|
api_param(arena, call, "System_Mutex", "mutex");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "mutex_free", "void");
|
||||||
|
api_param(arena, call, "System_Mutex", "mutex");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
api_call(arena, api, "condition_variable_make",
|
||||||
|
"System_Condition_Variable");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "condition_variable_wait", "void");
|
||||||
|
api_param(arena, call, "System_Condition_Variable", "cv");
|
||||||
|
api_param(arena, call, "System_Mutex", "mutex");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "condition_variable_signal", "void");
|
||||||
|
api_param(arena, call, "System_Condition_Variable", "cv");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "condition_variable_free", "void");
|
||||||
|
api_param(arena, call, "System_Condition_Variable", "cv");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "memory_allocate", "void*");
|
||||||
|
api_param(arena, call, "u64", "size");
|
||||||
|
api_param(arena, call, "String_Const_u8", "location");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "memory_set_protection", "b32");
|
||||||
|
api_param(arena, call, "void*", "ptr");
|
||||||
|
api_param(arena, call, "u64", "size");
|
||||||
|
api_param(arena, call, "u32", "flags");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "memory_free", "void");
|
||||||
|
api_param(arena, call, "void*", "ptr");
|
||||||
|
api_param(arena, call, "u64", "size");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "memory_annotation", "Memory_Annotation");
|
||||||
|
api_param(arena, call, "Arena*", "arena");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "show_mouse_cursor", "void");
|
||||||
|
api_param(arena, call, "i32", "show");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "set_fullscreen", "b32");
|
||||||
|
api_param(arena, call, "b32", "full_screen");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
api_call(arena, api, "is_fullscreen", "b32");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "get_keyboard_modifiers", "Input_Modifier_Set");
|
||||||
|
api_param(arena, call, "Arena*", "arena");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "set_key_mode", "void");
|
||||||
|
api_param(arena, call, "Key_Mode", "mode");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "set_source_mixer", "void");
|
||||||
|
api_param(arena, call, "void*", "ctx");
|
||||||
|
api_param(arena, call, "Audio_Mix_Sources_Function*", "mix_func");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
API_Call *call = api_call(arena, api, "set_destination_mixer", "void");
|
||||||
|
api_param(arena, call, "Audio_Mix_Destination_Function*", "mix_func");
|
||||||
|
}
|
||||||
|
|
||||||
|
return(api);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Generated_Group
|
||||||
|
get_api_group(void){
|
||||||
|
return(GeneratedGroup_Custom);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,134 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 31.03.2019
|
||||||
|
*
|
||||||
|
* Text layout representation
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
internal void
|
||||||
|
text_layout_init(Thread_Context *tctx, Text_Layout_Container *container){
|
||||||
|
block_zero_struct(container);
|
||||||
|
container->node_arena = make_arena_system();
|
||||||
|
container->table = make_table_u64_u64(tctx->allocator, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Text_Layout*
|
||||||
|
text_layout_new__alloc_layout(Text_Layout_Container *container){
|
||||||
|
Text_Layout *node = container->free_nodes;
|
||||||
|
if (node == 0){
|
||||||
|
node = push_array(&container->node_arena, Text_Layout, 1);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
sll_stack_pop(container->free_nodes);
|
||||||
|
}
|
||||||
|
return(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
text_layout_release(Thread_Context *tctx, Models *models, Text_Layout_Container *container, Text_Layout *layout){
|
||||||
|
Arena arena = *layout->arena;
|
||||||
|
linalloc_clear(&arena);
|
||||||
|
sll_stack_push(container->free_nodes, layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Text_Layout_ID
|
||||||
|
text_layout_new(Text_Layout_Container *container, Arena *arena,
|
||||||
|
Buffer_ID buffer_id, Buffer_Point point,
|
||||||
|
Range_i64 visible_range, Range_i64 visible_line_number_range,
|
||||||
|
Rect_f32 rect, ARGB_Color *item_colors, Layout_Function *layout_func){
|
||||||
|
Text_Layout *new_layout_data = text_layout_new__alloc_layout(container);
|
||||||
|
new_layout_data->arena = arena;
|
||||||
|
new_layout_data->buffer_id = buffer_id;
|
||||||
|
new_layout_data->point = point;
|
||||||
|
new_layout_data->visible_range = visible_range;
|
||||||
|
new_layout_data->visible_line_number_range = visible_line_number_range;
|
||||||
|
new_layout_data->rect = rect;
|
||||||
|
new_layout_data->item_colors = item_colors;
|
||||||
|
new_layout_data->layout_func = layout_func;
|
||||||
|
Text_Layout_ID new_id = ++container->id_counter;
|
||||||
|
table_insert(&container->table, new_id, (u64)PtrAsInt(new_layout_data));
|
||||||
|
return(new_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Text_Layout*
|
||||||
|
text_layout_get(Text_Layout_Container *container, Text_Layout_ID id){
|
||||||
|
Text_Layout *result = 0;
|
||||||
|
Table_Lookup lookup = table_lookup(&container->table, id);
|
||||||
|
if (lookup.found_match){
|
||||||
|
u64 ptr_val = 0;
|
||||||
|
table_read(&container->table, lookup, &ptr_val);
|
||||||
|
result = (Text_Layout*)IntAsPtr(ptr_val);
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal b32
|
||||||
|
text_layout_erase(Thread_Context *tctx, Models *models, Text_Layout_Container *container, Text_Layout_ID id){
|
||||||
|
b32 result = false;
|
||||||
|
Table_Lookup lookup = table_lookup(&container->table, id);
|
||||||
|
if (lookup.found_match){
|
||||||
|
u64 ptr_val = 0;
|
||||||
|
table_read(&container->table, lookup, &ptr_val);
|
||||||
|
Text_Layout *ptr = (Text_Layout*)IntAsPtr(ptr_val);
|
||||||
|
text_layout_release(tctx, models, container, ptr);
|
||||||
|
table_erase(&container->table, lookup);
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
internal void
|
||||||
|
text_layout_render(Thread_Context *tctx, Models *models, Text_Layout *layout,
|
||||||
|
ARGB_Color special_color, ARGB_Color ghost_color){
|
||||||
|
Editing_File *file = imp_get_file(models, layout->buffer_id);
|
||||||
|
if (file != 0){
|
||||||
|
Render_Target *target = models->target;
|
||||||
|
Face *face = file_get_face(models, file);
|
||||||
|
f32 width = rect_width(layout->rect);
|
||||||
|
|
||||||
|
Vec2_f32 delta = V2f32(1.f, 0.f);
|
||||||
|
|
||||||
|
Vec2_f32 shift_p = layout->rect.p0 - layout->point.pixel_shift;
|
||||||
|
i64 first_index = layout->visible_range.first;
|
||||||
|
i64 line_number = layout->visible_line_number_range.min;
|
||||||
|
i64 line_number_last = layout->visible_line_number_range.max;
|
||||||
|
Layout_Function *layout_func = layout->layout_func;
|
||||||
|
for (;line_number <= line_number_last; line_number += 1){
|
||||||
|
Layout_Item_List line = file_get_line_layout(tctx, models, file,
|
||||||
|
layout_func, width, face,
|
||||||
|
line_number);
|
||||||
|
for (Layout_Item_Block *block = line.first;
|
||||||
|
block != 0;
|
||||||
|
block = block->next){
|
||||||
|
Layout_Item *item = block->items;
|
||||||
|
i64 count = block->item_count;
|
||||||
|
ARGB_Color *item_colors = layout->item_colors;
|
||||||
|
for (i32 i = 0; i < count; i += 1, item += 1){
|
||||||
|
if (item->codepoint != 0){
|
||||||
|
ARGB_Color color = 0;
|
||||||
|
if (HasFlag(item->flags, LayoutItemFlag_Special_Character)){
|
||||||
|
color = special_color;
|
||||||
|
}
|
||||||
|
else if (HasFlag(item->flags, LayoutItemFlag_Ghost_Character)){
|
||||||
|
color = ghost_color;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
color = item_colors[item->index - first_index];
|
||||||
|
}
|
||||||
|
Vec2_f32 p = item->rect.p0 + shift_p;
|
||||||
|
draw_font_glyph(target, face, item->codepoint, p, color, GlyphFlag_None, delta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shift_p.y += line.height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 31.03.2019
|
||||||
|
*
|
||||||
|
* Text layout representation
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#if !defined(FRED_TEXT_LAYOUT_H)
|
||||||
|
#define FRED_TEXT_LAYOUT_H
|
||||||
|
|
||||||
|
union Text_Layout{
|
||||||
|
Text_Layout *next;
|
||||||
|
struct{
|
||||||
|
Arena *arena;
|
||||||
|
Buffer_ID buffer_id;
|
||||||
|
Buffer_Point point;
|
||||||
|
Range_i64 visible_range;
|
||||||
|
Range_i64 visible_line_number_range;
|
||||||
|
Rect_f32 rect;
|
||||||
|
ARGB_Color *item_colors;
|
||||||
|
Layout_Function *layout_func;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Text_Layout_Container{
|
||||||
|
Arena node_arena;
|
||||||
|
Text_Layout *free_nodes;
|
||||||
|
Table_u64_u64 table;
|
||||||
|
Text_Layout_ID id_counter;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,236 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 11.03.2017
|
||||||
|
*
|
||||||
|
* Translation system for turning byte streams into a stream of buffer model steps.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
// TODO(allen): I don't like this code _AT ALL_
|
||||||
|
// unravel the mess!
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// So what happened here was I thought, "Hey I have text in non-contiguous chunks and now I
|
||||||
|
// need to translates it into unicode codepoints (interpreting it as utf8), so I should make
|
||||||
|
// a system that translates utf-8 by taking in one byte at a time then emitting one or more
|
||||||
|
// codepoints whenever there is enough information, all the while ensuring there is no backtracking"
|
||||||
|
//
|
||||||
|
// Even though this may make the iteration sound nice, it's a HUGE FREAKING PAIN IN THE ASS.
|
||||||
|
// You can't optimize it very well, the code is inscrutible both on the implementation side
|
||||||
|
// and the calling side. Besides the fact that I "got it working" there isn't one good thing
|
||||||
|
// about this code.
|
||||||
|
//
|
||||||
|
// My next idea would be to try to make translation systems that take in the chunks themselves as
|
||||||
|
// a linked list, and then just does the WHOLE translation, MAYBE with optional "stop conditions".
|
||||||
|
// This way someone can actually optimize the translation loop by hand in _ONE SPOT_. The downside
|
||||||
|
// is that the caller will have to put up with maybe more translation work than they needed, but that
|
||||||
|
// translation work will be so much cheaper, and easier to maintain, that the caller will be happier
|
||||||
|
// overall.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// If this comment is still here, then I haven't fixed any of this garbage yet, but it should really
|
||||||
|
// be fixed!
|
||||||
|
|
||||||
|
#define ERROR_BYTE (max_u8-1)
|
||||||
|
#define CONTINUATION_BYTE max_u8
|
||||||
|
|
||||||
|
internal void
|
||||||
|
translating_consume_byte(Translation_State *tran, u8 ch, u32 i, u32 size, Translation_Byte_Description *desc_out){
|
||||||
|
desc_out->byte_class = 0;
|
||||||
|
if (ch < 0x80){
|
||||||
|
desc_out->byte_class = 1;
|
||||||
|
}
|
||||||
|
else if (ch < 0xC0){
|
||||||
|
desc_out->byte_class = CONTINUATION_BYTE;
|
||||||
|
}
|
||||||
|
else if (ch < 0xE0){
|
||||||
|
desc_out->byte_class = 2;
|
||||||
|
}
|
||||||
|
else if (ch < 0xF0){
|
||||||
|
desc_out->byte_class = 3;
|
||||||
|
}
|
||||||
|
else if (ch < 0xF8){
|
||||||
|
desc_out->byte_class = 4;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
desc_out->byte_class = ERROR_BYTE;
|
||||||
|
}
|
||||||
|
|
||||||
|
desc_out->prelim_emit_type = BufferModelUnit_None;
|
||||||
|
desc_out->last_byte_handler = TranLBH_None;
|
||||||
|
if (tran->fill_expected == 0){
|
||||||
|
tran->fill_buffer[0] = ch;
|
||||||
|
tran->fill_start_i = i;
|
||||||
|
tran->fill_i = 1;
|
||||||
|
|
||||||
|
if (desc_out->byte_class == 1){
|
||||||
|
desc_out->prelim_emit_type = BufferModelUnit_Codepoint;
|
||||||
|
}
|
||||||
|
else if (desc_out->byte_class == 0 || desc_out->byte_class == CONTINUATION_BYTE || desc_out->byte_class == ERROR_BYTE){
|
||||||
|
desc_out->prelim_emit_type = BufferModelUnit_Numbers;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
tran->fill_expected = desc_out->byte_class;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
if (desc_out->byte_class == CONTINUATION_BYTE){
|
||||||
|
tran->fill_buffer[tran->fill_i] = ch;
|
||||||
|
++tran->fill_i;
|
||||||
|
|
||||||
|
if (tran->fill_i == tran->fill_expected){
|
||||||
|
desc_out->prelim_emit_type = BufferModelUnit_Codepoint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
if (desc_out->byte_class >= 2 && desc_out->byte_class <= 4){
|
||||||
|
desc_out->last_byte_handler = TranLBH_Rebuffer;
|
||||||
|
}
|
||||||
|
else if (desc_out->byte_class == 1){
|
||||||
|
desc_out->last_byte_handler = TranLBH_EmitAsCP;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
tran->fill_buffer[tran->fill_i] = ch;
|
||||||
|
++tran->fill_i;
|
||||||
|
}
|
||||||
|
desc_out->prelim_emit_type = BufferModelUnit_Numbers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (desc_out->prelim_emit_type == BufferModelUnit_None && i+1 == size){
|
||||||
|
desc_out->prelim_emit_type = BufferModelUnit_Numbers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
translating_select_emit_rule_UTF8(Translation_State *tran, Translation_Byte_Description desc, Translation_Emit_Rule *type_out){
|
||||||
|
type_out->byte_class = desc.byte_class;
|
||||||
|
type_out->last_byte_handler = desc.last_byte_handler;
|
||||||
|
type_out->emit_type = desc.prelim_emit_type;
|
||||||
|
|
||||||
|
type_out->codepoint = 0;
|
||||||
|
type_out->codepoint_length = 0;
|
||||||
|
if (desc.prelim_emit_type == BufferModelUnit_Codepoint){
|
||||||
|
Character_Consume_Result consume = utf8_consume(tran->fill_buffer, ArrayCount(tran->fill_buffer));
|
||||||
|
u32 cp = consume.codepoint;
|
||||||
|
type_out->codepoint_length = consume.inc;
|
||||||
|
if (cp == max_u32){
|
||||||
|
type_out->codepoint_length = 0;
|
||||||
|
}
|
||||||
|
if (type_out->codepoint_length != 0){
|
||||||
|
if ((cp >= nonchar_min && cp <= nonchar_max) || ((cp & 0xFFFF) >= 0xFFFE)){
|
||||||
|
type_out->emit_type = BufferModelUnit_Numbers;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
type_out->codepoint = cp;
|
||||||
|
if (cp > 0x10FFFF){
|
||||||
|
type_out->emit_type = BufferModelUnit_Numbers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
type_out->emit_type = BufferModelUnit_Numbers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
translating_generate_emits(Translation_State *tran, Translation_Emit_Rule emit_rule, u8 ch, u32 i, Translation_Emits *emits_out){
|
||||||
|
emits_out->step_count = 0;
|
||||||
|
switch (emit_rule.emit_type){
|
||||||
|
default: goto skip_all;
|
||||||
|
|
||||||
|
case BufferModelUnit_Codepoint:
|
||||||
|
{
|
||||||
|
emits_out->steps[0].type = 1;
|
||||||
|
emits_out->steps[0].value = emit_rule.codepoint;
|
||||||
|
emits_out->steps[0].i = tran->fill_start_i;
|
||||||
|
emits_out->steps[0].byte_length = emit_rule.codepoint_length;
|
||||||
|
emits_out->step_count = 1;
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case BufferModelUnit_Numbers:
|
||||||
|
{
|
||||||
|
for (u32 j = 0; j < tran->fill_i; ++j){
|
||||||
|
emits_out->steps[j].type = 0;
|
||||||
|
emits_out->steps[j].value = tran->fill_buffer[j];
|
||||||
|
emits_out->steps[j].i = tran->fill_start_i + j;
|
||||||
|
emits_out->steps[j].byte_length = 1;
|
||||||
|
}
|
||||||
|
emits_out->step_count = tran->fill_i;
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
|
||||||
|
tran->fill_start_i = 0;
|
||||||
|
tran->fill_i = 0;
|
||||||
|
tran->fill_expected = 0;
|
||||||
|
|
||||||
|
switch (emit_rule.last_byte_handler){
|
||||||
|
case TranLBH_Rebuffer:
|
||||||
|
{
|
||||||
|
tran->fill_buffer[0] = ch;
|
||||||
|
tran->fill_start_i = i;
|
||||||
|
tran->fill_i = 1;
|
||||||
|
tran->fill_expected = emit_rule.byte_class;
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case TranLBH_EmitAsCP:
|
||||||
|
{
|
||||||
|
emits_out->steps[emits_out->step_count].type = 1;
|
||||||
|
emits_out->steps[emits_out->step_count].value = ch;
|
||||||
|
emits_out->steps[emits_out->step_count].i = i;
|
||||||
|
emits_out->steps[emits_out->step_count].byte_length = 1;
|
||||||
|
++emits_out->step_count;
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
|
||||||
|
skip_all:;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
translating_fully_process_byte(Translation_State *tran, u8 ch, u32 i, u32 size, Translation_Emits *emits_out){
|
||||||
|
Translation_Byte_Description description = {};
|
||||||
|
translating_consume_byte(tran, ch, i, size, &description);
|
||||||
|
Translation_Emit_Rule emit_rule = {};
|
||||||
|
translating_select_emit_rule_UTF8(tran, description, &emit_rule);
|
||||||
|
translating_generate_emits(tran, emit_rule, ch, i, emits_out);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
translation_step_read(Buffer_Model_Step step, Buffer_Model_Behavior *behavior_out){
|
||||||
|
behavior_out->do_newline = false;
|
||||||
|
behavior_out->do_codepoint_advance = false;
|
||||||
|
behavior_out->do_number_advance = false;
|
||||||
|
if (step.type == 1){
|
||||||
|
switch (step.value){
|
||||||
|
case '\n':
|
||||||
|
{
|
||||||
|
behavior_out->do_newline = true;
|
||||||
|
}break;
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
behavior_out->do_codepoint_advance = true;
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
behavior_out->do_number_advance = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TRANSLATION_DECL_EMIT_LOOP(_j,_emit) u32 _j = 0; _j < (_emit).step_count; ++_j
|
||||||
|
#define TRANSLATION_DECL_GET_STEP(_step,_behav,_j,_emit) \
|
||||||
|
Buffer_Model_Step _step = _emit.steps[_j]; Buffer_Model_Behavior _behav; \
|
||||||
|
translation_step_read(_step, &_behav)
|
||||||
|
|
||||||
|
#define TRANSLATION_EMIT_LOOP(_j,_emit) _j = 0; _j < (_emit).step_count; ++_j
|
||||||
|
#define TRANSLATION_GET_STEP(_step,_behav,_j,_emit)\
|
||||||
|
(_step) = _emit.steps[_j]; translation_step_read((_step), &(_behav))
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 24.01.2018
|
||||||
|
*
|
||||||
|
* Buffer types
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#if !defined(FRED_TRANSLATION_H)
|
||||||
|
#define FRED_TRANSLATION_H
|
||||||
|
|
||||||
|
struct Translation_State{
|
||||||
|
u8 fill_buffer[4];
|
||||||
|
u32 fill_start_i;
|
||||||
|
u8 fill_i;
|
||||||
|
u8 fill_expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum{
|
||||||
|
TranLBH_None,
|
||||||
|
TranLBH_Rebuffer,
|
||||||
|
TranLBH_EmitAsCP,
|
||||||
|
};
|
||||||
|
struct Translation_Byte_Description{
|
||||||
|
u8 byte_class;
|
||||||
|
u8 last_byte_handler;
|
||||||
|
u8 prelim_emit_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Translation_Emit_Rule{
|
||||||
|
u8 byte_class;
|
||||||
|
u8 last_byte_handler;
|
||||||
|
u8 emit_type;
|
||||||
|
|
||||||
|
u32 codepoint;
|
||||||
|
u32 codepoint_length;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Translation_Emits{
|
||||||
|
Buffer_Model_Step steps[5];
|
||||||
|
u32 step_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,853 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 19.08.2015
|
||||||
|
*
|
||||||
|
* Viewing
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
function void
|
||||||
|
begin_handling_input(Models *models, User_Input *input){
|
||||||
|
block_copy_struct(&models->current_input, input);
|
||||||
|
models->current_input_sequence_number += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
internal void
|
||||||
|
init_query_set(Query_Set *set){
|
||||||
|
Query_Slot *slot = set->slots;
|
||||||
|
set->free_slot = slot;
|
||||||
|
set->used_slot = 0;
|
||||||
|
for (i32 i = 0; i+1 < ArrayCount(set->slots); ++i, ++slot){
|
||||||
|
slot->next = slot + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Query_Slot*
|
||||||
|
alloc_query_slot(Query_Set *set){
|
||||||
|
Query_Slot *slot = set->free_slot;
|
||||||
|
if (slot != 0){
|
||||||
|
set->free_slot = slot->next;
|
||||||
|
slot->next = set->used_slot;
|
||||||
|
set->used_slot = slot;
|
||||||
|
}
|
||||||
|
return(slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
free_query_slot(Query_Set *set, Query_Bar *match_bar){
|
||||||
|
Query_Slot *slot = 0;
|
||||||
|
Query_Slot *prev = 0;
|
||||||
|
|
||||||
|
for (slot = set->used_slot; slot != 0; slot = slot->next){
|
||||||
|
if (slot->query_bar == match_bar) break;
|
||||||
|
prev = slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slot){
|
||||||
|
if (prev){
|
||||||
|
prev->next = slot->next;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
set->used_slot = slot->next;
|
||||||
|
}
|
||||||
|
slot->next = set->free_slot;
|
||||||
|
set->free_slot = slot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
free_all_queries(Query_Set *set){
|
||||||
|
for (;set->used_slot != 0;){
|
||||||
|
Query_Slot *slot = set->used_slot;
|
||||||
|
set->used_slot = slot->next;
|
||||||
|
slot->next = set->free_slot;
|
||||||
|
set->free_slot = slot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
internal Access_Flag
|
||||||
|
view_get_access_flags(View *view){
|
||||||
|
Access_Flag result = file_get_access_flags(view->file);
|
||||||
|
View_Context_Node *node = view->ctx;
|
||||||
|
b32 hides_buffer = (node != 0 && node->ctx.hides_buffer);
|
||||||
|
if (hides_buffer){
|
||||||
|
RemFlag(result, Access_Visible);
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal i32
|
||||||
|
view_get_index(Live_Views *live_set, View *view){
|
||||||
|
return((i32)(view - live_set->views));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal View_ID
|
||||||
|
view_get_id(Live_Views *live_set, View *view){
|
||||||
|
return((View_ID)(view - live_set->views) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal View*
|
||||||
|
live_set_alloc_view(Lifetime_Allocator *lifetime_allocator, Live_Views *live_set, Panel *panel){
|
||||||
|
Assert(live_set->count < live_set->max);
|
||||||
|
++live_set->count;
|
||||||
|
|
||||||
|
View *result = live_set->free_sentinel.next;
|
||||||
|
dll_remove(result);
|
||||||
|
block_zero_struct(result);
|
||||||
|
|
||||||
|
result->in_use = true;
|
||||||
|
init_query_set(&result->query_set);
|
||||||
|
result->lifetime_object = lifetime_alloc_object(lifetime_allocator, DynamicWorkspace_View, result);
|
||||||
|
panel->view = result;
|
||||||
|
result->panel = panel;
|
||||||
|
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
live_set_free_view(Lifetime_Allocator *lifetime_allocator, Live_Views *live_set, View *view){
|
||||||
|
Assert(live_set->count > 0);
|
||||||
|
--live_set->count;
|
||||||
|
|
||||||
|
view->next = live_set->free_sentinel.next;
|
||||||
|
view->prev = &live_set->free_sentinel;
|
||||||
|
live_set->free_sentinel.next = view;
|
||||||
|
view->next->prev = view;
|
||||||
|
view->in_use = false;
|
||||||
|
|
||||||
|
lifetime_free_object(lifetime_allocator, view->lifetime_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
internal File_Edit_Positions
|
||||||
|
view_get_edit_pos(View *view){
|
||||||
|
return(view->edit_pos_);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
view_set_edit_pos(View *view, File_Edit_Positions edit_pos){
|
||||||
|
edit_pos.scroll.position.line_number = clamp_bot(1, edit_pos.scroll.position.line_number);
|
||||||
|
edit_pos.scroll.target.line_number = clamp_bot(1, edit_pos.scroll.target.line_number);
|
||||||
|
view->edit_pos_ = edit_pos;
|
||||||
|
view->file->state.edit_pos_most_recent = edit_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
internal Rect_f32
|
||||||
|
view_get_buffer_rect(Thread_Context *tctx, Models *models, View *view){
|
||||||
|
Rect_f32 region = Rf32(view->panel->rect_full);
|
||||||
|
if (models->buffer_region != 0){
|
||||||
|
Rect_f32 rect = region;
|
||||||
|
Rect_f32 sub_region = Rf32(V2f32(0, 0), rect_dim(rect));
|
||||||
|
Application_Links app = {};
|
||||||
|
app.tctx = tctx;
|
||||||
|
app.cmd_context = models;
|
||||||
|
sub_region = models->buffer_region(&app, view_get_id(&models->view_set, view), sub_region);
|
||||||
|
region.p0 = rect.p0 + sub_region.p0;
|
||||||
|
region.p1 = rect.p0 + sub_region.p1;
|
||||||
|
region.x1 = clamp_top(region.x1, rect.x1);
|
||||||
|
region.y1 = clamp_top(region.y1, rect.y1);
|
||||||
|
region.x0 = clamp_top(region.x0, region.x1);
|
||||||
|
region.y0 = clamp_top(region.y0, region.y1);
|
||||||
|
}
|
||||||
|
return(region);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal f32
|
||||||
|
view_width(Thread_Context *tctx, Models *models, View *view){
|
||||||
|
return(rect_width(view_get_buffer_rect(tctx, models, view)));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal f32
|
||||||
|
view_height(Thread_Context *tctx, Models *models, View *view){
|
||||||
|
return(rect_height(view_get_buffer_rect(tctx, models, view)));
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
internal Layout_Item_List
|
||||||
|
view_get_line_layout(Thread_Context *tctx, Models *models, View *view, i64 line_number){
|
||||||
|
Editing_File *file = view->file;
|
||||||
|
Face *face = file_get_face(models, file);
|
||||||
|
f32 width = view_width(tctx, models, view);
|
||||||
|
Layout_Function *layout_func = file_get_layout_func(file);
|
||||||
|
return(file_get_line_layout(tctx, models, file, layout_func, width, face, line_number));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Line_Shift_Vertical
|
||||||
|
view_line_shift_y(Thread_Context *tctx, Models *models, View *view,
|
||||||
|
i64 line_number, f32 y_delta){
|
||||||
|
Editing_File *file = view->file;
|
||||||
|
Face *face = file_get_face(models, file);
|
||||||
|
f32 width = view_width(tctx, models, view);
|
||||||
|
Layout_Function *layout_func = file_get_layout_func(file);
|
||||||
|
return(file_line_shift_y(tctx, models, file, layout_func, width, face,
|
||||||
|
line_number, y_delta));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal f32
|
||||||
|
view_line_y_difference(Thread_Context *tctx, Models *models, View *view,
|
||||||
|
i64 line_a, i64 line_b){
|
||||||
|
Editing_File *file = view->file;
|
||||||
|
Face *face = file_get_face(models, file);
|
||||||
|
f32 width = view_width(tctx, models, view);
|
||||||
|
Layout_Function *layout_func = file_get_layout_func(file);
|
||||||
|
return(file_line_y_difference(tctx, models, file,
|
||||||
|
layout_func, width, face, line_a, line_b));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal i64
|
||||||
|
view_pos_at_relative_xy(Thread_Context *tctx, Models *models, View *view,
|
||||||
|
i64 base_line, Vec2_f32 relative_xy){
|
||||||
|
Editing_File *file = view->file;
|
||||||
|
Face *face = file_get_face(models, file);
|
||||||
|
f32 width = view_width(tctx, models, view);
|
||||||
|
Layout_Function *layout_func = file_get_layout_func(file);
|
||||||
|
return(file_pos_at_relative_xy(tctx, models, file,
|
||||||
|
layout_func, width, face, base_line, relative_xy));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Rect_f32
|
||||||
|
view_relative_box_of_pos(Thread_Context *tctx, Models *models, View *view,
|
||||||
|
i64 base_line, i64 pos){
|
||||||
|
Editing_File *file = view->file;
|
||||||
|
Face *face = file_get_face(models, file);
|
||||||
|
f32 width = view_width(tctx, models, view);
|
||||||
|
Layout_Function *layout_func = file_get_layout_func(file);
|
||||||
|
return(file_relative_box_of_pos(tctx, models, file,
|
||||||
|
layout_func, width, face, base_line, pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Vec2_f32
|
||||||
|
view_relative_xy_of_pos(Thread_Context *tctx, Models *models, View *view,
|
||||||
|
i64 base_line, i64 pos){
|
||||||
|
Rect_f32 rect = view_relative_box_of_pos(tctx, models, view, base_line, pos);
|
||||||
|
return(rect_center(rect));
|
||||||
|
}
|
||||||
|
|
||||||
|
function Rect_f32
|
||||||
|
view_padded_box_of_pos(Thread_Context *tctx, Models *models, View *view,
|
||||||
|
i64 base_line, i64 pos){
|
||||||
|
Editing_File *file = view->file;
|
||||||
|
Face *face = file_get_face(models, file);
|
||||||
|
f32 width = view_width(tctx, models, view);
|
||||||
|
Layout_Function *layout_func = file_get_layout_func(file);
|
||||||
|
return(file_padded_box_of_pos(tctx, models, file,
|
||||||
|
layout_func, width, face, base_line, pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Buffer_Point
|
||||||
|
view_normalize_buffer_point(Thread_Context *tctx, Models *models, View *view,
|
||||||
|
Buffer_Point point){
|
||||||
|
Editing_File *file = view->file;
|
||||||
|
Face *face = file_get_face(models, file);
|
||||||
|
f32 width = view_width(tctx, models, view);
|
||||||
|
Layout_Function *layout_func = file_get_layout_func(file);
|
||||||
|
return(file_normalize_buffer_point(tctx, models, file,
|
||||||
|
layout_func, width, face, point));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Vec2_f32
|
||||||
|
view_buffer_point_difference(Thread_Context *tctx, Models *models, View *view,
|
||||||
|
Buffer_Point a, Buffer_Point b){
|
||||||
|
Editing_File *file = view->file;
|
||||||
|
Face *face = file_get_face(models, file);
|
||||||
|
f32 width = view_width(tctx, models, view);
|
||||||
|
Layout_Function *layout_func = file_get_layout_func(file);
|
||||||
|
return(file_buffer_point_difference(tctx, models, file,
|
||||||
|
layout_func, width, face, a, b));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Buffer_Point
|
||||||
|
view_move_buffer_point(Thread_Context *tctx, Models *models, View *view,
|
||||||
|
Buffer_Point buffer_point, Vec2_f32 delta){
|
||||||
|
delta += buffer_point.pixel_shift;
|
||||||
|
Line_Shift_Vertical shift = view_line_shift_y(tctx, models, view, buffer_point.line_number, delta.y);
|
||||||
|
buffer_point.line_number = shift.line;
|
||||||
|
buffer_point.pixel_shift = V2f32(delta.x, delta.y - shift.y_delta);
|
||||||
|
return(buffer_point);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Line_Shift_Character
|
||||||
|
view_line_shift_characters(Thread_Context *tctx, Models *models, View *view,
|
||||||
|
i64 line_number, i64 character_delta){
|
||||||
|
Editing_File *file = view->file;
|
||||||
|
Face *face = file_get_face(models, file);
|
||||||
|
f32 width = view_width(tctx, models, view);
|
||||||
|
Layout_Function *layout_func = file_get_layout_func(file);
|
||||||
|
return(file_line_shift_characters(tctx, models, file,
|
||||||
|
layout_func, width, face, line_number, character_delta));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal i64
|
||||||
|
view_line_character_difference(Thread_Context *tctx, Models *models, View *view,
|
||||||
|
i64 line_a, i64 line_b){
|
||||||
|
Editing_File *file = view->file;
|
||||||
|
Face *face = file_get_face(models, file);
|
||||||
|
f32 width = view_width(tctx, models, view);
|
||||||
|
Layout_Function *layout_func = file_get_layout_func(file);
|
||||||
|
return(file_line_character_difference(tctx, models, file, layout_func, width, face,
|
||||||
|
line_a, line_b));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal i64
|
||||||
|
view_pos_from_relative_character(Thread_Context *tctx, Models *models, View *view,
|
||||||
|
i64 base_line, i64 relative_character){
|
||||||
|
Editing_File *file = view->file;
|
||||||
|
Face *face = file_get_face(models, file);
|
||||||
|
f32 width = view_width(tctx, models, view);
|
||||||
|
Layout_Function *layout_func = file_get_layout_func(file);
|
||||||
|
return(file_pos_from_relative_character(tctx, models, file, layout_func, width, face,
|
||||||
|
base_line, relative_character));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal i64
|
||||||
|
view_relative_character_from_pos(Thread_Context *tctx, Models *models, View *view,
|
||||||
|
i64 base_line, i64 pos){
|
||||||
|
Editing_File *file = view->file;
|
||||||
|
Face *face = file_get_face(models, file);
|
||||||
|
f32 width = view_width(tctx, models, view);
|
||||||
|
Layout_Function *layout_func = file_get_layout_func(file);
|
||||||
|
return(file_relative_character_from_pos(tctx, models, file,
|
||||||
|
layout_func, width, face, base_line, pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Buffer_Cursor
|
||||||
|
view_compute_cursor(View *view, Buffer_Seek seek){
|
||||||
|
Editing_File *file = view->file;
|
||||||
|
return(file_compute_cursor(file, seek));
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
internal b32
|
||||||
|
view_move_view_to_cursor(Thread_Context *tctx, Models *models, View *view, Buffer_Scroll *scroll){
|
||||||
|
Editing_File *file = view->file;
|
||||||
|
Face *face = file_get_face(models, file);
|
||||||
|
Rect_f32 rect = view_get_buffer_rect(tctx, models, view);
|
||||||
|
Vec2_f32 view_dim = rect_dim(rect);
|
||||||
|
|
||||||
|
Layout_Function *layout_func = file_get_layout_func(file);
|
||||||
|
|
||||||
|
File_Edit_Positions edit_pos = view_get_edit_pos(view);
|
||||||
|
Vec2_f32 p = file_relative_xy_of_pos(tctx, models, file,
|
||||||
|
layout_func, view_dim.x, face,
|
||||||
|
scroll->target.line_number, edit_pos.cursor_pos);
|
||||||
|
p -= scroll->target.pixel_shift;
|
||||||
|
|
||||||
|
f32 line_height = face->metrics.line_height;
|
||||||
|
f32 normal_advance = face->metrics.normal_advance;
|
||||||
|
|
||||||
|
Vec2_f32 margin = view->cursor_margin;
|
||||||
|
Vec2_f32 push_in = view->cursor_push_in_multiplier;
|
||||||
|
|
||||||
|
Vec2_f32 lim_dim = view_dim*0.45f;
|
||||||
|
margin.x = clamp_top(margin.x, lim_dim.x);
|
||||||
|
margin.y = clamp_top(margin.y, lim_dim.y);
|
||||||
|
|
||||||
|
Vec2_f32 push_in_lim_dim = hadamard(lim_dim, V2f32(1.f/line_height, 1.f/normal_advance)) - margin;
|
||||||
|
push_in_lim_dim.x = clamp_bot(0.f, push_in_lim_dim.x);
|
||||||
|
push_in_lim_dim.y = clamp_bot(0.f, push_in_lim_dim.y);
|
||||||
|
push_in.x = clamp_top(push_in.x, push_in_lim_dim.x);
|
||||||
|
push_in.y = clamp_top(push_in.y, push_in_lim_dim.y);
|
||||||
|
|
||||||
|
Vec2_f32 target_p_relative = {};
|
||||||
|
if (p.y < margin.y){
|
||||||
|
target_p_relative.y = p.y - margin.y - line_height*push_in.y;
|
||||||
|
}
|
||||||
|
else if (p.y > view_dim.y - margin.y){
|
||||||
|
target_p_relative.y = (p.y + margin.y + line_height*push_in.y) - view_dim.y;
|
||||||
|
}
|
||||||
|
if (p.x < margin.x){
|
||||||
|
target_p_relative.x = p.x - margin.x - normal_advance*push_in.x;
|
||||||
|
}
|
||||||
|
else if (p.x > view_dim.x - margin.x){
|
||||||
|
target_p_relative.x = (p.x + margin.x + normal_advance*push_in.x) - view_dim.x;
|
||||||
|
}
|
||||||
|
scroll->target.pixel_shift += target_p_relative;
|
||||||
|
scroll->target = view_normalize_buffer_point(tctx, models, view, scroll->target);
|
||||||
|
scroll->target.pixel_shift.x = f32_round32(scroll->target.pixel_shift.x);
|
||||||
|
scroll->target.pixel_shift.y = f32_round32(scroll->target.pixel_shift.y);
|
||||||
|
|
||||||
|
return(target_p_relative != V2f32(0.f, 0.f));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal b32
|
||||||
|
view_move_cursor_to_view(Thread_Context *tctx, Models *models, View *view, Buffer_Scroll scroll, i64 *pos_in_out, f32 preferred_x){
|
||||||
|
Editing_File *file = view->file;
|
||||||
|
Face *face = file_get_face(models, file);
|
||||||
|
Rect_f32 rect = view_get_buffer_rect(tctx, models, view);
|
||||||
|
Vec2_f32 view_dim = rect_dim(rect);
|
||||||
|
|
||||||
|
Layout_Function *layout_func = file_get_layout_func(file);
|
||||||
|
|
||||||
|
Vec2_f32 p = file_relative_xy_of_pos(tctx, models, file,
|
||||||
|
layout_func, view_dim.x, face,
|
||||||
|
scroll.target.line_number, *pos_in_out);
|
||||||
|
p -= scroll.target.pixel_shift;
|
||||||
|
|
||||||
|
f32 line_height = face->metrics.line_height;
|
||||||
|
|
||||||
|
b32 adjusted_y = true;
|
||||||
|
if (p.y < 0.f){
|
||||||
|
p.y = line_height*1.5f;
|
||||||
|
}
|
||||||
|
else if (p.y > view_dim.y){
|
||||||
|
p.y = view_dim.y - line_height*1.5f;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
adjusted_y = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
b32 result = false;
|
||||||
|
if (adjusted_y){
|
||||||
|
p += scroll.target.pixel_shift;
|
||||||
|
*pos_in_out = file_pos_at_relative_xy(tctx, models, file,
|
||||||
|
layout_func, view_dim.x, face,
|
||||||
|
scroll.target.line_number, p);
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
view_set_cursor(Thread_Context *tctx, Models *models, View *view, i64 pos){
|
||||||
|
File_Edit_Positions edit_pos = view_get_edit_pos(view);
|
||||||
|
file_edit_positions_set_cursor(&edit_pos, pos);
|
||||||
|
view_set_edit_pos(view, edit_pos);
|
||||||
|
Buffer_Scroll scroll = edit_pos.scroll;
|
||||||
|
if (view_move_view_to_cursor(tctx, models, view, &scroll)){
|
||||||
|
edit_pos.scroll = scroll;
|
||||||
|
view_set_edit_pos(view, edit_pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
view_set_scroll(Thread_Context *tctx, Models *models, View *view, Buffer_Scroll scroll){
|
||||||
|
File_Edit_Positions edit_pos = view_get_edit_pos(view);
|
||||||
|
file_edit_positions_set_scroll(&edit_pos, scroll);
|
||||||
|
view_set_edit_pos(view, edit_pos);
|
||||||
|
if (view_move_cursor_to_view(tctx, models, view, edit_pos.scroll, &edit_pos.cursor_pos, view->preferred_x)){
|
||||||
|
view_set_edit_pos(view, edit_pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
view_set_cursor_and_scroll(Thread_Context *tctx, Models *models, View *view, i64 pos, Buffer_Scroll scroll){
|
||||||
|
File_Edit_Positions edit_pos = view_get_edit_pos(view);
|
||||||
|
file_edit_positions_set_cursor(&edit_pos, pos);
|
||||||
|
Buffer_Cursor cursor = view_compute_cursor(view, seek_pos(pos));
|
||||||
|
Vec2_f32 p = view_relative_xy_of_pos(tctx, models, view, cursor.line, pos);
|
||||||
|
view->preferred_x = p.x;
|
||||||
|
file_edit_positions_set_scroll(&edit_pos, scroll);
|
||||||
|
edit_pos.last_set_type = EditPos_None;
|
||||||
|
view_set_edit_pos(view, edit_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
internal void
|
||||||
|
view_set_file(Thread_Context *tctx, Models *models, View *view, Editing_File *file){
|
||||||
|
Assert(file != 0);
|
||||||
|
|
||||||
|
Editing_File *old_file = view->file;
|
||||||
|
|
||||||
|
if (models->view_change_buffer != 0){
|
||||||
|
Application_Links app = {};
|
||||||
|
app.tctx = tctx;
|
||||||
|
app.cmd_context = models;
|
||||||
|
models->view_change_buffer(&app, view_get_id(&models->view_set, view),
|
||||||
|
(old_file != 0)?old_file->id:0, file->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (old_file != 0){
|
||||||
|
file_touch(&models->working_set, old_file);
|
||||||
|
file_edit_positions_push(old_file, view_get_edit_pos(view));
|
||||||
|
}
|
||||||
|
|
||||||
|
view->file = file;
|
||||||
|
|
||||||
|
File_Edit_Positions edit_pos = file_edit_positions_pop(file);
|
||||||
|
view_set_edit_pos(view, edit_pos);
|
||||||
|
view->mark = edit_pos.cursor_pos;
|
||||||
|
Buffer_Cursor cursor = view_compute_cursor(view, seek_pos(edit_pos.cursor_pos));
|
||||||
|
Vec2_f32 p = view_relative_xy_of_pos(tctx, models, view, cursor.line, edit_pos.cursor_pos);
|
||||||
|
view->preferred_x = p.x;
|
||||||
|
|
||||||
|
models->layout.panel_state_dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
function void
|
||||||
|
view_push_context(View *view, View_Context *ctx){
|
||||||
|
Temp_Memory pop_me = begin_temp(&view->node_arena);
|
||||||
|
View_Context_Node *node = push_array_zero(&view->node_arena, View_Context_Node, 1);
|
||||||
|
sll_stack_push(view->ctx, node);
|
||||||
|
node->pop_me = pop_me;
|
||||||
|
block_copy_struct(&node->ctx, ctx);
|
||||||
|
node->delta_rule_memory = push_array_zero(&view->node_arena, u8, ctx->delta_rule_memory_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
view_alter_context(View *view, View_Context *ctx){
|
||||||
|
View_Context_Node *node = view->ctx;
|
||||||
|
Assert(node != 0);
|
||||||
|
block_copy_struct(&node->ctx, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
view_pop_context(View *view){
|
||||||
|
View_Context_Node *node = view->ctx;
|
||||||
|
if (node != 0 && node->next != 0){
|
||||||
|
sll_stack_pop(view->ctx);
|
||||||
|
end_temp(node->pop_me);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function View_Context_Node*
|
||||||
|
view_current_context_node(View *view){
|
||||||
|
return(view->ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
function View_Context
|
||||||
|
view_current_context(View *view){
|
||||||
|
View_Context ctx = {};
|
||||||
|
View_Context_Node *node = view->ctx;
|
||||||
|
if (node != 0){
|
||||||
|
block_copy_struct(&ctx, &node->ctx);
|
||||||
|
}
|
||||||
|
return(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
internal Coroutine*
|
||||||
|
co_handle_request(Thread_Context *tctx, Models *models, Coroutine *co, Co_Out *out){
|
||||||
|
Coroutine *result = 0;
|
||||||
|
switch (out->request){
|
||||||
|
case CoRequest_NewFontFace:
|
||||||
|
{
|
||||||
|
Face_Description *description = out->face_description;
|
||||||
|
Face *face = font_set_new_face(&models->font_set, description);
|
||||||
|
Co_In in = {};
|
||||||
|
in.face_id = (face != 0)?face->id:0;
|
||||||
|
result = coroutine_run(&models->coroutines, co, &in, out);
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case CoRequest_ModifyFace:
|
||||||
|
{
|
||||||
|
Face_Description *description = out->face_description;
|
||||||
|
Face_ID face_id = out->face_id;
|
||||||
|
Co_In in = {};
|
||||||
|
in.success = font_set_modify_face(&models->font_set, face_id, description);
|
||||||
|
result = coroutine_run(&models->coroutines, co, &in, out);
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case CoRequest_AcquireGlobalFrameMutex:
|
||||||
|
{
|
||||||
|
system_acquire_global_frame_mutex(tctx);
|
||||||
|
result = coroutine_run(&models->coroutines, co, 0, out);
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case CoRequest_ReleaseGlobalFrameMutex:
|
||||||
|
{
|
||||||
|
system_release_global_frame_mutex(tctx);
|
||||||
|
result = coroutine_run(&models->coroutines, co, 0, out);
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Coroutine*
|
||||||
|
co_run(Thread_Context *tctx, Models *models, Coroutine *co, Co_In *in, Co_Out *out){
|
||||||
|
Coroutine *result = coroutine_run(&models->coroutines, co, in, out);
|
||||||
|
for (;result != 0 && out->request != CoRequest_None;){
|
||||||
|
result = co_handle_request(tctx, models, result, out);
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
view_event_context_base__inner(Coroutine *coroutine){
|
||||||
|
Co_In *in = (Co_In*)coroutine->in;
|
||||||
|
Models *models = in->models;
|
||||||
|
Custom_Command_Function *event_context_base = in->event_context_base;
|
||||||
|
Assert(event_context_base != 0);
|
||||||
|
Application_Links app = {};
|
||||||
|
app.tctx = coroutine->tctx;
|
||||||
|
app.cmd_context = models;
|
||||||
|
event_context_base(&app);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
view_init(Thread_Context *tctx, Models *models, View *view, Editing_File *initial_buffer,
|
||||||
|
Custom_Command_Function *event_context_base){
|
||||||
|
view_set_file(tctx, models, view, initial_buffer);
|
||||||
|
|
||||||
|
view->node_arena = make_arena_system();
|
||||||
|
|
||||||
|
View_Context first_ctx = {};
|
||||||
|
first_ctx.render_caller = models->render_caller;
|
||||||
|
first_ctx.delta_rule = models->delta_rule;
|
||||||
|
first_ctx.delta_rule_memory_size = models->delta_rule_memory_size;
|
||||||
|
view_push_context(view, &first_ctx);
|
||||||
|
|
||||||
|
view->cursor_margin = V2f32(0.f, 0.f);
|
||||||
|
view->cursor_push_in_multiplier = V2f32(1.5f, 1.5f);
|
||||||
|
|
||||||
|
view->co = coroutine_create(&models->coroutines, view_event_context_base__inner);
|
||||||
|
view->co->user_data = view;
|
||||||
|
Co_In in = {};
|
||||||
|
in.models = models;
|
||||||
|
in.event_context_base = event_context_base;
|
||||||
|
view->co = co_run(tctx, models, view->co, &in, &view->co_out);
|
||||||
|
// TODO(allen): deal with this kind of problem!
|
||||||
|
Assert(view->co != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(allen): This doesn't make any sense!!!!!! COROUTINE SHUTDOWN? VIEW CLOSING? WADAFUQ?
|
||||||
|
|
||||||
|
function b32
|
||||||
|
view_close(Models *models, View *view){
|
||||||
|
Layout *layout = &models->layout;
|
||||||
|
b32 result = false;
|
||||||
|
if (layout_close_panel(layout, view->panel)){
|
||||||
|
if (view->co != 0){
|
||||||
|
models_push_wind_down(models, view->co);
|
||||||
|
}
|
||||||
|
live_set_free_view(&models->lifetime_allocator, &models->view_set, view);
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
view_check_co_exited(Models *models, View *view){
|
||||||
|
if (view->co == 0){
|
||||||
|
b32 result = view_close(models, view);
|
||||||
|
// TODO(allen): Here it looks like the final view has
|
||||||
|
// exited from it's event handler. We should probably
|
||||||
|
// have a failsafe restarter for the event handler when
|
||||||
|
// this happens.
|
||||||
|
Assert(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(allen): This is dumb. Let's rethink view cleanup strategy.
|
||||||
|
|
||||||
|
internal void
|
||||||
|
co_single_abort(Thread_Context *tctx, Models *models, View *view){
|
||||||
|
Coroutine *co = view->co;
|
||||||
|
Co_In in = {};
|
||||||
|
in.user_input.abort = true;
|
||||||
|
view->co = co_run(tctx, models, co, &in, &view->co_out);
|
||||||
|
view_check_co_exited(models, view);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
co_full_abort(Thread_Context *tctx, Models *models, View *view){
|
||||||
|
Coroutine *co = view->co;
|
||||||
|
Co_In in = {};
|
||||||
|
in.user_input.abort = true;
|
||||||
|
for (u32 j = 0; j < 100 && co != 0; ++j){
|
||||||
|
co = co_run(tctx, models, co, &in, &view->co_out);
|
||||||
|
}
|
||||||
|
if (co != 0){
|
||||||
|
Application_Links app = {};
|
||||||
|
app.tctx = tctx;
|
||||||
|
app.cmd_context = models;
|
||||||
|
#define M "SERIOUS ERROR: full stack abort did not complete"
|
||||||
|
print_message(&app, string_u8_litexpr(M));
|
||||||
|
#undef M
|
||||||
|
}
|
||||||
|
view->co = 0;
|
||||||
|
init_query_set(&view->query_set);
|
||||||
|
}
|
||||||
|
|
||||||
|
function b32
|
||||||
|
co_send_event(Thread_Context *tctx, Models *models, View *view, Input_Event *event){
|
||||||
|
b32 event_was_handled = false;
|
||||||
|
|
||||||
|
Coroutine *co = view->co;
|
||||||
|
Co_Out *co_out = &view->co_out;
|
||||||
|
|
||||||
|
{
|
||||||
|
models->current_input_unhandled = false;
|
||||||
|
Co_In in = {};
|
||||||
|
in.user_input.event = *event;
|
||||||
|
in.user_input.abort = false;
|
||||||
|
begin_handling_input(models, &in.user_input);
|
||||||
|
view->co = co_run(tctx, models, view->co, &in, &view->co_out);
|
||||||
|
view_check_co_exited(models, view);
|
||||||
|
if (!(event->kind == InputEventKind_Core && event->core.code == CoreCode_Animate)){
|
||||||
|
models->animate_next_frame = true;
|
||||||
|
}
|
||||||
|
event_was_handled = !models->current_input_unhandled;
|
||||||
|
}
|
||||||
|
|
||||||
|
return(event_was_handled);
|
||||||
|
}
|
||||||
|
|
||||||
|
function b32
|
||||||
|
co_send_core_event(Thread_Context *tctx, Models *models, View *view, Core_Code code, String_Const_u8 string){
|
||||||
|
Input_Event event = {};
|
||||||
|
event.kind = InputEventKind_Core;
|
||||||
|
event.core.code = code;
|
||||||
|
event.core.string = string;
|
||||||
|
return(co_send_event(tctx, models, view, &event));
|
||||||
|
}
|
||||||
|
|
||||||
|
function b32
|
||||||
|
co_send_core_event(Thread_Context *tctx, Models *models, View *view, Core_Code code, Buffer_ID id){
|
||||||
|
Input_Event event = {};
|
||||||
|
event.kind = InputEventKind_Core;
|
||||||
|
event.core.code = code;
|
||||||
|
event.core.id = id;
|
||||||
|
return(co_send_event(tctx, models, view, &event));
|
||||||
|
}
|
||||||
|
|
||||||
|
function b32
|
||||||
|
co_send_core_event(Thread_Context *tctx, Models *models, View *view, Core_Code code){
|
||||||
|
return(co_send_core_event(tctx, models, view, code, SCu8()));
|
||||||
|
}
|
||||||
|
|
||||||
|
function b32
|
||||||
|
co_send_event(Thread_Context *tctx, Models *models, Input_Event *event){
|
||||||
|
Panel *active_panel = models->layout.active_panel;
|
||||||
|
View *view = active_panel->view;
|
||||||
|
return(co_send_event(tctx, models, view, event));
|
||||||
|
}
|
||||||
|
|
||||||
|
function b32
|
||||||
|
co_send_core_event(Thread_Context *tctx, Models *models, Core_Code code, String_Const_u8 string){
|
||||||
|
Panel *active_panel = models->layout.active_panel;
|
||||||
|
View *view = active_panel->view;
|
||||||
|
return(co_send_core_event(tctx, models, view, code, string));
|
||||||
|
}
|
||||||
|
|
||||||
|
function b32
|
||||||
|
co_send_core_event(Thread_Context *tctx, Models *models, Core_Code code, Buffer_ID buffer_id){
|
||||||
|
Panel *active_panel = models->layout.active_panel;
|
||||||
|
View *view = active_panel->view;
|
||||||
|
return(co_send_core_event(tctx, models, view, code, buffer_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
function b32
|
||||||
|
co_send_core_event(Thread_Context *tctx, Models *models, Core_Code code){
|
||||||
|
return(co_send_core_event(tctx, models, code, SCu8()));
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
function void
|
||||||
|
view_quit_ui(Thread_Context *tctx, Models *models, View *view){
|
||||||
|
for (u32 j = 0;; j += 1){
|
||||||
|
if (j == 100){
|
||||||
|
Application_Links app = {};
|
||||||
|
app.tctx = tctx;
|
||||||
|
app.cmd_context = models;
|
||||||
|
#define M "SERIOUS ERROR: view quit ui did not complete"
|
||||||
|
print_message(&app, string_u8_litexpr(M));
|
||||||
|
#undef M
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
View_Context_Node *ctx = view->ctx;
|
||||||
|
if (ctx->next == 0){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
co_single_abort(tctx, models, view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
internal b32
|
||||||
|
file_is_viewed(Layout *layout, Editing_File *file){
|
||||||
|
b32 is_viewed = false;
|
||||||
|
for (Panel *panel = layout_get_first_open_panel(layout);
|
||||||
|
panel != 0;
|
||||||
|
panel = layout_get_next_open_panel(layout, panel)){
|
||||||
|
View *view = panel->view;
|
||||||
|
if (view->file == file){
|
||||||
|
is_viewed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(is_viewed);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
adjust_views_looking_at_file_to_new_cursor(Thread_Context *tctx, Models *models, Editing_File *file){
|
||||||
|
Layout *layout = &models->layout;
|
||||||
|
for (Panel *panel = layout_get_first_open_panel(layout);
|
||||||
|
panel != 0;
|
||||||
|
panel = layout_get_next_open_panel(layout, panel)){
|
||||||
|
View *view = panel->view;
|
||||||
|
if (view->file == file){
|
||||||
|
File_Edit_Positions edit_pos = view_get_edit_pos(view);
|
||||||
|
view_set_cursor(tctx, models, view, edit_pos.cursor_pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
global_set_font_and_update_files(Models *models, Face *new_global_face){
|
||||||
|
for (Node *node = models->working_set.active_file_sentinel.next;
|
||||||
|
node != &models->working_set.active_file_sentinel;
|
||||||
|
node = node->next){
|
||||||
|
Editing_File *file = CastFromMember(Editing_File, main_chain_node, node);
|
||||||
|
file->settings.face_id = new_global_face->id;
|
||||||
|
}
|
||||||
|
models->global_face_id = new_global_face->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal b32
|
||||||
|
release_font_and_update(Models *models, Face *face, Face *replacement_face){
|
||||||
|
b32 success = false;
|
||||||
|
Assert(replacement_face != 0 && replacement_face != face);
|
||||||
|
if (font_set_release_face(&models->font_set, face->id)){
|
||||||
|
for (Node *node = models->working_set.active_file_sentinel.next;
|
||||||
|
node != &models->working_set.active_file_sentinel;
|
||||||
|
node = node->next){
|
||||||
|
Editing_File *file = CastFromMember(Editing_File, main_chain_node, node);
|
||||||
|
if (file->settings.face_id == face->id){
|
||||||
|
file->settings.face_id = replacement_face->id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (models->global_face_id == face->id){
|
||||||
|
models->global_face_id = replacement_face->id;
|
||||||
|
}
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
return(success);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
internal View*
|
||||||
|
imp_get_view(Models *models, View_ID view_id){
|
||||||
|
Live_Views *view_set = &models->view_set;
|
||||||
|
View *view = 0;
|
||||||
|
view_id -= 1;
|
||||||
|
if (0 <= view_id && view_id < view_set->max){
|
||||||
|
view = view_set->views + view_id;
|
||||||
|
if (!view->in_use){
|
||||||
|
view = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 17.07.2017
|
||||||
|
*
|
||||||
|
* File editing view for 4coder.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#if !defined(FRED_VIEW_H)
|
||||||
|
#define FRED_VIEW_H
|
||||||
|
|
||||||
|
struct Co_In{
|
||||||
|
union{
|
||||||
|
struct{
|
||||||
|
struct Models *models;
|
||||||
|
Custom_Command_Function *event_context_base;
|
||||||
|
};
|
||||||
|
User_Input user_input;
|
||||||
|
Face_ID face_id;
|
||||||
|
b32 success;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef i32 Co_Request;
|
||||||
|
enum{
|
||||||
|
CoRequest_None = 0,
|
||||||
|
CoRequest_NewFontFace = 1,
|
||||||
|
CoRequest_ModifyFace = 2,
|
||||||
|
CoRequest_AcquireGlobalFrameMutex = 3,
|
||||||
|
CoRequest_ReleaseGlobalFrameMutex = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Co_Out{
|
||||||
|
Co_Request request;
|
||||||
|
Face_Description *face_description;
|
||||||
|
Face_ID face_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Query_Slot{
|
||||||
|
Query_Slot *next;
|
||||||
|
Query_Bar *query_bar;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Query_Set{
|
||||||
|
Query_Slot slots[8];
|
||||||
|
Query_Slot *free_slot;
|
||||||
|
Query_Slot *used_slot;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct View_Context_Node{
|
||||||
|
View_Context_Node *next;
|
||||||
|
Temp_Memory pop_me;
|
||||||
|
View_Context ctx;
|
||||||
|
void *delta_rule_memory;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct View{
|
||||||
|
View *next;
|
||||||
|
View *prev;
|
||||||
|
struct Panel *panel;
|
||||||
|
b32 in_use;
|
||||||
|
|
||||||
|
Editing_File *file;
|
||||||
|
Lifetime_Object *lifetime_object;
|
||||||
|
|
||||||
|
File_Edit_Positions edit_pos_;
|
||||||
|
i64 mark;
|
||||||
|
f32 preferred_x;
|
||||||
|
Vec2_f32 cursor_margin;
|
||||||
|
Vec2_f32 cursor_push_in_multiplier;
|
||||||
|
|
||||||
|
b8 new_scroll_target;
|
||||||
|
b8 hide_scrollbar;
|
||||||
|
b8 hide_file_bar;
|
||||||
|
b8 show_whitespace;
|
||||||
|
|
||||||
|
Coroutine *co;
|
||||||
|
Co_Out co_out;
|
||||||
|
|
||||||
|
Arena node_arena;
|
||||||
|
View_Context_Node *ctx;
|
||||||
|
|
||||||
|
Query_Set query_set;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Live_Views{
|
||||||
|
View *views;
|
||||||
|
View free_sentinel;
|
||||||
|
i32 count;
|
||||||
|
i32 max;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,509 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 03.01.2017
|
||||||
|
*
|
||||||
|
* Working_Set data structure
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
internal void
|
||||||
|
working_set_file_default_settings(Working_Set *working_set, Editing_File *file){
|
||||||
|
block_zero_struct(&file->settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
internal void
|
||||||
|
file_change_notification_check(Arena *scratch, Working_Set *working_set, Editing_File *file){
|
||||||
|
if (file->canon.name_size > 0 && !file->settings.unimportant){
|
||||||
|
String_Const_u8 name = SCu8(file->canon.name_space, file->canon.name_size);
|
||||||
|
File_Attributes attributes = system_quick_file_attributes(scratch, name);
|
||||||
|
if ((attributes.last_write_time > file->attributes.last_write_time) ||
|
||||||
|
(attributes.last_write_time == 0 && file->attributes.last_write_time > 0)){
|
||||||
|
if (file->state.save_state == FileSaveState_SavedWaitingForNotification){
|
||||||
|
file->state.save_state = FileSaveState_Normal;
|
||||||
|
file->attributes = attributes;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
file_add_dirty_flag(file, DirtyState_UnloadedChanges);
|
||||||
|
if (file->external_mod_node.next == 0){
|
||||||
|
LogEventF(log_string(M), &working_set->arena, file->id, 0, system_thread_get_id(),
|
||||||
|
"external modification [lwt=0x%llx]", attributes.last_write_time);
|
||||||
|
dll_insert_back(&working_set->has_external_mod_sentinel, &file->external_mod_node);
|
||||||
|
system_signal_step(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file->attributes = attributes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
file_change_notification_thread_main(void *ptr){
|
||||||
|
Models *models = (Models*)ptr;
|
||||||
|
Arena arena = make_arena_system();
|
||||||
|
Working_Set *working_set = &models->working_set;
|
||||||
|
for (;;){
|
||||||
|
system_sleep(Thousand(250));
|
||||||
|
Mutex_Lock lock(working_set->mutex);
|
||||||
|
if (working_set->active_file_count > 0){
|
||||||
|
i32 check_count = working_set->active_file_count/16;
|
||||||
|
check_count = clamp(1, check_count, 100);
|
||||||
|
Node *used = &working_set->active_file_sentinel;
|
||||||
|
Node *node = working_set->sync_check_iterator;
|
||||||
|
if (node == 0 || node == used){
|
||||||
|
node = used->next;
|
||||||
|
}
|
||||||
|
for (i32 i = 0; i < check_count; i += 1){
|
||||||
|
Editing_File *file = CastFromMember(Editing_File, main_chain_node, node);
|
||||||
|
node = node->next;
|
||||||
|
if (node == used){
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
file_change_notification_check(&arena, working_set, file);
|
||||||
|
}
|
||||||
|
working_set->sync_check_iterator = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
internal Editing_File*
|
||||||
|
working_set_allocate_file(Working_Set *working_set, Lifetime_Allocator *lifetime_allocator){
|
||||||
|
Editing_File *file = working_set->free_files;
|
||||||
|
if (file == 0){
|
||||||
|
file = push_array(&working_set->arena, Editing_File, 1);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
sll_stack_pop(working_set->free_files);
|
||||||
|
}
|
||||||
|
block_zero_struct(file);
|
||||||
|
|
||||||
|
dll_insert_back(&working_set->active_file_sentinel, &file->main_chain_node);
|
||||||
|
dll_insert_back(&working_set->touch_order_sentinel, &file->touch_node);
|
||||||
|
working_set->active_file_count += 1;
|
||||||
|
|
||||||
|
file->id = working_set->id_counter;
|
||||||
|
working_set->id_counter += 1;
|
||||||
|
|
||||||
|
working_set_file_default_settings(working_set, file);
|
||||||
|
|
||||||
|
table_insert(&working_set->id_to_ptr_table,
|
||||||
|
(u64)file->id, (u64)(PtrAsInt(file)));
|
||||||
|
|
||||||
|
return(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
working_set_free_file(Heap *heap, Working_Set *working_set, Editing_File *file){
|
||||||
|
if (working_set->sync_check_iterator == &file->main_chain_node){
|
||||||
|
working_set->sync_check_iterator = working_set->sync_check_iterator->next;
|
||||||
|
}
|
||||||
|
dll_remove(&file->main_chain_node);
|
||||||
|
dll_remove(&file->touch_node);
|
||||||
|
working_set->active_file_count -= 1;
|
||||||
|
table_erase(&working_set->id_to_ptr_table, file->id);
|
||||||
|
sll_stack_push(working_set->free_files, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Editing_File*
|
||||||
|
working_set_get_file(Working_Set *working_set, Buffer_ID id){
|
||||||
|
Editing_File *result = 0;
|
||||||
|
u64 val = 0;
|
||||||
|
if (table_read(&working_set->id_to_ptr_table, id, &val)){
|
||||||
|
result = (Editing_File*)(IntAsPtr(val));
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
working_set_init(Models *models, Working_Set *working_set){
|
||||||
|
block_zero_struct(working_set);
|
||||||
|
working_set->arena = make_arena_system();
|
||||||
|
|
||||||
|
working_set->id_counter = 1;
|
||||||
|
|
||||||
|
dll_init_sentinel(&working_set->active_file_sentinel);
|
||||||
|
dll_init_sentinel(&working_set->touch_order_sentinel);
|
||||||
|
|
||||||
|
local_const i32 slot_count = 128;
|
||||||
|
Base_Allocator *allocator = get_base_allocator_system();
|
||||||
|
working_set->id_to_ptr_table = make_table_u64_u64(allocator, slot_count);
|
||||||
|
working_set->canon_table = make_table_Data_u64(allocator, slot_count);
|
||||||
|
working_set->name_table = make_table_Data_u64(allocator, slot_count);
|
||||||
|
|
||||||
|
dll_init_sentinel(&working_set->has_external_mod_sentinel);
|
||||||
|
working_set->mutex = system_mutex_make();
|
||||||
|
working_set->file_change_thread = system_thread_launch(file_change_notification_thread_main, models);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Editing_File*
|
||||||
|
working_set_contains__generic(Working_Set *working_set, Table_Data_u64 *table, String_Const_u8 name){
|
||||||
|
Editing_File *result = 0;
|
||||||
|
u64 val = 0;
|
||||||
|
if (table_read(table, make_data(name.str, name.size), &val)){
|
||||||
|
result = working_set_get_file(working_set, (Buffer_ID)val);
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal b32
|
||||||
|
working_set_add__generic(Table_Data_u64 *table, Buffer_ID id, String_Const_u8 name){
|
||||||
|
return(table_insert(table, make_data(name.str, name.size), id));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
working_set_remove__generic(Table_Data_u64 *table, String_Const_u8 name){
|
||||||
|
table_erase(table, make_data(name.str, name.size));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Editing_File*
|
||||||
|
working_set_contains_canon(Working_Set *working_set, String_Const_u8 name){
|
||||||
|
return(working_set_contains__generic(working_set, &working_set->canon_table, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal b32
|
||||||
|
working_set_canon_add(Working_Set *working_set, Editing_File *file, String_Const_u8 name){
|
||||||
|
return(working_set_add__generic(&working_set->canon_table, file->id, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
working_set_canon_remove(Working_Set *working_set, String_Const_u8 name){
|
||||||
|
working_set_remove__generic(&working_set->canon_table, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Editing_File*
|
||||||
|
working_set_contains_name(Working_Set *working_set, String_Const_u8 name){
|
||||||
|
return(working_set_contains__generic(working_set, &working_set->name_table, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal b32
|
||||||
|
working_set_add_name(Working_Set *working_set, Editing_File *file, String_Const_u8 name){
|
||||||
|
return(working_set_add__generic(&working_set->name_table, file->id, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
working_set_remove_name(Working_Set *working_set, String_Const_u8 name){
|
||||||
|
working_set_remove__generic(&working_set->name_table, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Editing_File*
|
||||||
|
get_file_from_identifier(Working_Set *working_set, Buffer_Identifier buffer){
|
||||||
|
Editing_File *file = 0;
|
||||||
|
if (buffer.id != 0){
|
||||||
|
file = working_set_get_file(working_set, buffer.id);
|
||||||
|
}
|
||||||
|
else if (buffer.name != 0){
|
||||||
|
String_Const_u8 name = SCu8(buffer.name, buffer.name_len);
|
||||||
|
file = working_set_contains_name(working_set, name);
|
||||||
|
}
|
||||||
|
return(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// TODO(allen): Bring the clipboard fully to the custom side.
|
||||||
|
internal void
|
||||||
|
working_set_clipboard_clear(Heap *heap, Working_Set *working){
|
||||||
|
String_Const_u8 *str = working->clipboards;
|
||||||
|
for (i32 i = 0; i < working->clipboard_size; i += 1, str += 1){
|
||||||
|
heap_free(heap, str->str);
|
||||||
|
block_zero_struct(str);
|
||||||
|
}
|
||||||
|
working->clipboard_size = 0;
|
||||||
|
working->clipboard_current = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal String_Const_u8*
|
||||||
|
working_set_next_clipboard_string(Heap *heap, Working_Set *working, u64 str_size){
|
||||||
|
i32 clipboard_current = working->clipboard_current;
|
||||||
|
if (working->clipboard_size == 0){
|
||||||
|
clipboard_current = 0;
|
||||||
|
working->clipboard_size = 1;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
++clipboard_current;
|
||||||
|
if (clipboard_current >= working->clipboard_max_size){
|
||||||
|
clipboard_current = 0;
|
||||||
|
}
|
||||||
|
else if (working->clipboard_size <= clipboard_current){
|
||||||
|
working->clipboard_size = clipboard_current + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String_Const_u8 *result = &working->clipboards[clipboard_current];
|
||||||
|
working->clipboard_current = clipboard_current;
|
||||||
|
if (result->str != 0){
|
||||||
|
heap_free(heap, result->str);
|
||||||
|
}
|
||||||
|
u8 *new_str = (u8*)heap_allocate(heap, (i32)(str_size + 1));
|
||||||
|
*result = SCu8(new_str, str_size);
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal String_Const_u8*
|
||||||
|
working_set_clipboard_index(Working_Set *working, i32 index){
|
||||||
|
String_Const_u8 *result = 0;
|
||||||
|
i32 size = working->clipboard_size;
|
||||||
|
i32 current = working->clipboard_current;
|
||||||
|
if (index >= 0 && size > 0){
|
||||||
|
index = index % size;
|
||||||
|
index = current + size - index;
|
||||||
|
index = index % size;
|
||||||
|
result = &working->clipboards[index];
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
// TODO(allen): get rid of this???
|
||||||
|
internal b32
|
||||||
|
get_canon_name(Arena *scratch, String_Const_u8 file_name, Editing_File_Name *canon_name){
|
||||||
|
Temp_Memory temp = begin_temp(scratch);
|
||||||
|
String_Const_u8 canonical = system_get_canonical(scratch, file_name);
|
||||||
|
u64 size = Min(sizeof(canon_name->name_space), canonical.size);
|
||||||
|
block_copy(canon_name->name_space, canonical.str, size);
|
||||||
|
canon_name->name_size = size;
|
||||||
|
end_temp(temp);
|
||||||
|
file_name_terminate(canon_name);
|
||||||
|
return(canon_name->name_size > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
file_bind_file_name(Working_Set *working_set, Editing_File *file, String_Const_u8 canon_file_name){
|
||||||
|
Assert(file->unique_name.name_size == 0);
|
||||||
|
Assert(file->canon.name_size == 0);
|
||||||
|
u64 size = canon_file_name.size;
|
||||||
|
size = clamp_top(size, sizeof(file->canon.name_space) - 1);
|
||||||
|
file->canon.name_size = size;
|
||||||
|
block_copy(file->canon.name_space, canon_file_name.str, size);
|
||||||
|
file_name_terminate(&file->canon);
|
||||||
|
b32 result = working_set_canon_add(working_set, file, string_from_file_name(&file->canon));
|
||||||
|
Assert(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
buffer_unbind_file(Working_Set *working_set, Editing_File *file){
|
||||||
|
Assert(file->unique_name.name_size == 0);
|
||||||
|
Assert(file->canon.name_size != 0);
|
||||||
|
working_set_canon_remove(working_set, string_from_file_name(&file->canon));
|
||||||
|
file->canon.name_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal b32
|
||||||
|
buffer_name_has_conflict(Working_Set *working_set, String_Const_u8 base_name){
|
||||||
|
b32 hit_conflict = false;
|
||||||
|
Node *used_nodes = &working_set->active_file_sentinel;
|
||||||
|
for (Node *node = used_nodes->next;
|
||||||
|
node != used_nodes;
|
||||||
|
node = node->next){
|
||||||
|
Editing_File *file_ptr = CastFromMember(Editing_File, main_chain_node, node);
|
||||||
|
if (file_ptr && string_match(base_name, string_from_file_name(&file_ptr->unique_name))){
|
||||||
|
hit_conflict = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(hit_conflict);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
buffer_resolve_name_low_level(Arena *scratch, Working_Set *working_set, Editing_File_Name *name, String_Const_u8 base_name){
|
||||||
|
u64 size = base_name.size;
|
||||||
|
size = clamp_top(size, sizeof(name->name_space));
|
||||||
|
block_copy(name->name_space, base_name.str, size);
|
||||||
|
String_u8 string = Su8(name->name_space, size, sizeof(name->name_space));
|
||||||
|
u64 original_size = string.size;
|
||||||
|
u64 file_x = 0;
|
||||||
|
for (b32 hit_conflict = true; hit_conflict;){
|
||||||
|
hit_conflict = buffer_name_has_conflict(working_set, string.string);
|
||||||
|
if (hit_conflict){
|
||||||
|
file_x += 1;
|
||||||
|
string.size = original_size;
|
||||||
|
Temp_Memory temp = begin_temp(scratch);
|
||||||
|
String_Const_u8 int_str = string_from_integer(scratch, file_x, 10);
|
||||||
|
string_append(&string, string_u8_litexpr(" ("));
|
||||||
|
string_append(&string, int_str);
|
||||||
|
string_append(&string, string_u8_litexpr(")"));
|
||||||
|
end_temp(temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
name->name_size = string.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
buffer_bind_name_low_level(Arena *scratch, Working_Set *working_set, Editing_File *file, String_Const_u8 base_name, String_Const_u8 name){
|
||||||
|
Assert(file->base_name.name_size == 0);
|
||||||
|
Assert(file->unique_name.name_size == 0);
|
||||||
|
|
||||||
|
Editing_File_Name new_name = {};
|
||||||
|
buffer_resolve_name_low_level(scratch, working_set, &new_name, name);
|
||||||
|
|
||||||
|
{
|
||||||
|
u64 size = base_name.size;
|
||||||
|
size = clamp_top(size, sizeof(file->base_name.name_space));
|
||||||
|
block_copy(file->base_name.name_space, base_name.str, size);
|
||||||
|
file->base_name.name_size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
u64 size = new_name.name_size;
|
||||||
|
block_copy(file->unique_name.name_space, new_name.name_space, size);
|
||||||
|
file->unique_name.name_size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
b32 result = working_set_add_name(working_set, file, string_from_file_name(&file->unique_name));
|
||||||
|
Assert(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
buffer_unbind_name_low_level(Working_Set *working_set, Editing_File *file){
|
||||||
|
Assert(file->base_name.name_size != 0);
|
||||||
|
Assert(file->unique_name.name_size != 0);
|
||||||
|
working_set_remove_name(working_set, string_from_file_name(&file->unique_name));
|
||||||
|
file->base_name.name_size = 0;
|
||||||
|
file->unique_name.name_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
buffer_bind_name(Thread_Context *tctx, Models *models, Arena *scratch, Working_Set *working_set, Editing_File *file, String_Const_u8 base_name){
|
||||||
|
Temp_Memory temp = begin_temp(scratch);
|
||||||
|
|
||||||
|
// List of conflict files.
|
||||||
|
struct Node_Ptr{
|
||||||
|
Node_Ptr *next;
|
||||||
|
Editing_File *file_ptr;
|
||||||
|
};
|
||||||
|
Node_Ptr *conflict_first = 0;
|
||||||
|
Node_Ptr *conflict_last = 0;
|
||||||
|
i32 conflict_count = 0;
|
||||||
|
|
||||||
|
{
|
||||||
|
Node_Ptr *node = push_array(scratch, Node_Ptr, 1);
|
||||||
|
sll_queue_push(conflict_first, conflict_last, node);
|
||||||
|
node->file_ptr = file;
|
||||||
|
conflict_count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node *used_nodes = &working_set->active_file_sentinel;
|
||||||
|
for (Node *node = used_nodes->next;
|
||||||
|
node != used_nodes;
|
||||||
|
node = node->next){
|
||||||
|
Editing_File *file_ptr = CastFromMember(Editing_File, main_chain_node, node);
|
||||||
|
if (file_ptr != 0 && string_match(base_name, string_from_file_name(&file_ptr->base_name))){
|
||||||
|
Node_Ptr *new_node = push_array(scratch, Node_Ptr, 1);
|
||||||
|
sll_queue_push(conflict_first, conflict_last, new_node);
|
||||||
|
new_node->file_ptr = file_ptr;
|
||||||
|
conflict_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill conflict array.
|
||||||
|
Buffer_Name_Conflict_Entry *conflicts = push_array(scratch, Buffer_Name_Conflict_Entry, conflict_count);
|
||||||
|
|
||||||
|
{
|
||||||
|
i32 i = 0;
|
||||||
|
for (Node_Ptr *node = conflict_first;
|
||||||
|
node != 0;
|
||||||
|
node = node->next, i += 1){
|
||||||
|
Editing_File *file_ptr = node->file_ptr;
|
||||||
|
Buffer_Name_Conflict_Entry *entry = &conflicts[i];
|
||||||
|
entry->buffer_id = file_ptr->id;
|
||||||
|
|
||||||
|
entry->file_name = push_string_copy(scratch, string_from_file_name(&file_ptr->canon));
|
||||||
|
entry->base_name = push_string_copy(scratch, base_name);
|
||||||
|
|
||||||
|
String_Const_u8 b = base_name;
|
||||||
|
if (i > 0){
|
||||||
|
b = string_from_file_name(&file_ptr->unique_name);
|
||||||
|
}
|
||||||
|
u64 unique_name_capacity = 256;
|
||||||
|
u8 *unique_name_buffer = push_array(scratch, u8, unique_name_capacity);
|
||||||
|
Assert(b.size <= unique_name_capacity);
|
||||||
|
block_copy(unique_name_buffer, b.str, b.size);
|
||||||
|
entry->unique_name_in_out = unique_name_buffer;
|
||||||
|
entry->unique_name_len_in_out = b.size;
|
||||||
|
entry->unique_name_capacity = unique_name_capacity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get user's resolution data.
|
||||||
|
if (models->buffer_name_resolver != 0){
|
||||||
|
Application_Links app = {};
|
||||||
|
app.tctx = tctx;
|
||||||
|
app.cmd_context = models;
|
||||||
|
models->buffer_name_resolver(&app, conflicts, conflict_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-bind all of the files
|
||||||
|
{
|
||||||
|
i32 i = 0;
|
||||||
|
for (Node_Ptr *node = conflict_first;
|
||||||
|
node != 0;
|
||||||
|
node = node->next, i += 1){
|
||||||
|
Editing_File *file_ptr = node->file_ptr;
|
||||||
|
if (file_ptr->unique_name.name_size > 0){
|
||||||
|
buffer_unbind_name_low_level(working_set, file_ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
i32 i = 0;
|
||||||
|
for (Node_Ptr *node = conflict_first;
|
||||||
|
node != 0;
|
||||||
|
node = node->next, i += 1){
|
||||||
|
Editing_File *file_ptr = node->file_ptr;
|
||||||
|
Buffer_Name_Conflict_Entry *entry = &conflicts[i];
|
||||||
|
String_Const_u8 unique_name = SCu8(entry->unique_name_in_out, entry->unique_name_len_in_out);
|
||||||
|
buffer_bind_name_low_level(scratch, working_set, file_ptr, base_name, unique_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
end_temp(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
internal void
|
||||||
|
file_touch(Working_Set *working_set, Editing_File *file){
|
||||||
|
Assert(file != 0);
|
||||||
|
dll_remove(&file->touch_node);
|
||||||
|
dll_insert(&working_set->touch_order_sentinel, &file->touch_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Editing_File*
|
||||||
|
file_get_next(Working_Set *working_set, Editing_File *file){
|
||||||
|
if (file != 0){
|
||||||
|
Node *node = file->touch_node.next;
|
||||||
|
file = CastFromMember(Editing_File, touch_node, node);
|
||||||
|
if (node == &working_set->touch_order_sentinel){
|
||||||
|
file = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
if (working_set->active_file_count > 0){
|
||||||
|
Node *node = working_set->touch_order_sentinel.next;
|
||||||
|
file = CastFromMember(Editing_File, touch_node, node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
internal Editing_File*
|
||||||
|
imp_get_file(Models *models, Buffer_ID buffer_id){
|
||||||
|
Working_Set *working_set = &models->working_set;
|
||||||
|
return(working_set_get_file(working_set, buffer_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* 24.03.2018
|
||||||
|
*
|
||||||
|
* Working_Set data structure
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#if !defined(FRED_WORKING_SET_H)
|
||||||
|
#define FRED_WORKING_SET_H
|
||||||
|
|
||||||
|
struct Working_Set{
|
||||||
|
// NOTE(allen): After initialization of file_change_thread
|
||||||
|
// the members of this struct should only be accessed by a thread
|
||||||
|
// who owns the mutex member.
|
||||||
|
|
||||||
|
Arena arena;
|
||||||
|
|
||||||
|
Editing_File *free_files;
|
||||||
|
Buffer_ID id_counter;
|
||||||
|
|
||||||
|
Node active_file_sentinel;
|
||||||
|
Node touch_order_sentinel;
|
||||||
|
i32 active_file_count;
|
||||||
|
|
||||||
|
Table_u64_u64 id_to_ptr_table;
|
||||||
|
Table_Data_u64 canon_table;
|
||||||
|
Table_Data_u64 name_table;
|
||||||
|
|
||||||
|
Node *sync_check_iterator;
|
||||||
|
Node has_external_mod_sentinel;
|
||||||
|
System_Mutex mutex;
|
||||||
|
System_Thread file_change_thread;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
Welcome to the 4coder code base.
|
||||||
|
|
||||||
|
This codebase was authored by Allen Webster from 2014-2022, with help from a number of contributors:
|
||||||
|
+ Casey Muratori
|
||||||
|
+ "insofaras" Alex Baines
|
||||||
|
+ Yuval Dolev
|
||||||
|
+ Ryan Fleury
|
||||||
|
|
||||||
|
Also thanks to all those who supported the project, financially and/or through all your detailed feedback.
|
||||||
|
|
||||||
|
As of May 31st 2022, I am freezing this codebase and open sourcing it.
|
||||||
|
|
||||||
|
I *DO NOT* recommend learning from this codebase, especially not with an uncritical eye. It may be a useful reference for certain algorithms and architectural ideas, and it certainly contains some cautionary tales. But if you are a beginner, I encourage you to aim for more than emulating the style and structure of this codebase.
|
||||||
|
|
||||||
|
I will not be taking pull requests, or any other form of contribution in this repository. Since I am no longer maintaining this codebase, it is my intention that users who rely on 4coder will have the option to fork the codebase, fix their issues, or carry what I started in new directions according to their own vision if they would like.
|
||||||
|
|
||||||
|
The license I chose for this codebase is the very permissive MIT license. For the sake of clarity in the community of 4coder users, I ask that new forks of this codebase be given unique names.
|
||||||
|
|
||||||
|
In this readme you will find:
|
||||||
|
1. License (MIT)
|
||||||
|
2. Build Instructions
|
||||||
|
3. Notes on Major Issues
|
||||||
|
|
||||||
|
|
||||||
|
# License
|
||||||
|
|
||||||
|
Copyright (c) 2022 Allen Webster
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
|
# Build Instructions
|
||||||
|
|
||||||
|
1. Create an empty folder named "4ed" to contain the codebase.
|
||||||
|
2. Clone the repository
|
||||||
|
3. Rename the folder containing the repository to "code"
|
||||||
|
4. At the same level as the "4ed" folder, clone the "4coder-non-source" repository
|
||||||
|
5. A. On windows setup the visual studio command line magic sauce so that "cl" works
|
||||||
|
B. On linux setup g++
|
||||||
|
C. On mac setup clang
|
||||||
|
6. Navigate to the "4ed/code" folder.
|
||||||
|
7. A. On windows run "bin\build.bat"
|
||||||
|
B. On linux run "bin\build-linux.sh"
|
||||||
|
C. On linux run "bin\build-mac.sh"
|
||||||
|
|
||||||
|
|
||||||
|
# Notes on Major Issues
|
||||||
|
|
||||||
|
1. The build system and organization of files is extremely complicated. There is a 4ed_build.cpp that defines how builds run, and the build scripts have to build and run this C++ file. The file is pretty chaotic since it cannot rely on the codebase's usual helpers. On top of that there is a totally separate build system for the custom layer which is also a big gigantic mess of its own. It involves several stages of compilation, and a number of metaprograms.
|
||||||
|
|
||||||
|
2. The documentation system is over complicated & the documentation is incomplete. There is very little documentation for the internals or the complicated layers of helpers.
|
||||||
|
|
||||||
|
3. The lexer generator is way too complicated, and the built-in support for language features is not fully developed. The background threaded parsing is not very carefully organized and is not very flexible, so it's hard to add new languages at any level of the system.
|
||||||
|
|
||||||
|
4. There are a few layers of overcomplicated configuration parsers.
|
||||||
|
|
||||||
|
5. Mac support has not been maintained for several versions.
|
||||||
|
|
||||||
|
6. The codebase has a very weak base layer with key features that were added very late, so lots of code was written in the absence of useful features to bind things together. To make matters worse the base layer is split by the distinction of custom layer & core layer, leading to some double definitions and some incosistencies.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,721 @@
|
||||||
|
/*
|
||||||
|
* Mr. 4th Dimention - Allen Webster
|
||||||
|
*
|
||||||
|
* ??.??.????
|
||||||
|
*
|
||||||
|
* 4coder development build rule.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
//#define FM_PRINT_COMMANDS
|
||||||
|
|
||||||
|
#include "4coder_base_types.h"
|
||||||
|
#include "4coder_version.h"
|
||||||
|
|
||||||
|
#include "4coder_base_types.cpp"
|
||||||
|
#include "4coder_malloc_allocator.cpp"
|
||||||
|
|
||||||
|
#define FTECH_FILE_MOVING_IMPLEMENTATION
|
||||||
|
#include "4coder_file_moving.h"
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// OS and compiler index
|
||||||
|
//
|
||||||
|
|
||||||
|
typedef u32 Tier_Code;
|
||||||
|
enum{
|
||||||
|
Tier_Demo,
|
||||||
|
Tier_Super,
|
||||||
|
Tier_COUNT,
|
||||||
|
};
|
||||||
|
|
||||||
|
char *tier_names[] = {
|
||||||
|
"demo",
|
||||||
|
"super",
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef u32 Platform_Code;
|
||||||
|
enum{
|
||||||
|
Platform_Windows,
|
||||||
|
Platform_Linux,
|
||||||
|
Platform_Mac,
|
||||||
|
//
|
||||||
|
Platform_COUNT,
|
||||||
|
Platform_None = Platform_COUNT,
|
||||||
|
};
|
||||||
|
|
||||||
|
char *platform_names[] = {
|
||||||
|
"win",
|
||||||
|
"linux",
|
||||||
|
"mac",
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef u32 Compiler_Code;
|
||||||
|
enum{
|
||||||
|
Compiler_CL,
|
||||||
|
Compiler_GCC,
|
||||||
|
Compiler_Clang,
|
||||||
|
//
|
||||||
|
Compiler_COUNT,
|
||||||
|
Compiler_None = Compiler_COUNT,
|
||||||
|
};
|
||||||
|
|
||||||
|
char *compiler_names[] = {
|
||||||
|
"cl",
|
||||||
|
"gcc",
|
||||||
|
"clang",
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef u32 Arch_Code;
|
||||||
|
enum{
|
||||||
|
Arch_X64,
|
||||||
|
Arch_X86,
|
||||||
|
|
||||||
|
//
|
||||||
|
Arch_COUNT,
|
||||||
|
Arch_None = Arch_COUNT,
|
||||||
|
};
|
||||||
|
|
||||||
|
char *arch_names[] = {
|
||||||
|
"x64",
|
||||||
|
"x86",
|
||||||
|
};
|
||||||
|
|
||||||
|
#if OS_WINDOWS
|
||||||
|
# define This_OS Platform_Windows
|
||||||
|
#elif OS_LINUX
|
||||||
|
# define This_OS Platform_Linux
|
||||||
|
#elif OS_MAC
|
||||||
|
# define This_OS Platform_Mac
|
||||||
|
#else
|
||||||
|
# error This platform is not enumerated.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if COMPILER_CL
|
||||||
|
# define This_Compiler Compiler_CL
|
||||||
|
#elif COMPILER_GCC
|
||||||
|
# define This_Compiler Compiler_GCC
|
||||||
|
#elif COMPILER_CLANG
|
||||||
|
# define This_Compiler Compiler_Clang
|
||||||
|
#else
|
||||||
|
# error This compilers is not enumerated.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//
|
||||||
|
// Universal directories
|
||||||
|
//
|
||||||
|
|
||||||
|
#define BUILD_DIR "../build"
|
||||||
|
#define PACK_DIR "../distributions"
|
||||||
|
#define SITE_DIR "../site"
|
||||||
|
|
||||||
|
#define FOREIGN "../4coder-non-source/foreign"
|
||||||
|
#define FOREIGN_WIN "..\\4coder-non-source\\foreign"
|
||||||
|
|
||||||
|
char *includes[] = { "custom", FOREIGN "/freetype2", 0, };
|
||||||
|
|
||||||
|
//
|
||||||
|
// Platform layer file tables
|
||||||
|
//
|
||||||
|
|
||||||
|
char *windows_platform_layer[] = { "platform_win32/win32_4ed.cpp", 0 };
|
||||||
|
char *linux_platform_layer[] = { "platform_linux/linux_4ed.cpp", 0 };
|
||||||
|
char *mac_platform_layer[] = { "platform_mac/mac_4ed.mm", 0 };
|
||||||
|
|
||||||
|
char **platform_layers[Platform_COUNT] = {
|
||||||
|
windows_platform_layer,
|
||||||
|
linux_platform_layer ,
|
||||||
|
mac_platform_layer ,
|
||||||
|
};
|
||||||
|
|
||||||
|
char *windows_cl_platform_inc[] = { "platform_all", 0 };
|
||||||
|
char *linux_gcc_platform_inc[] = { "platform_all", "platform_unix", 0 };
|
||||||
|
|
||||||
|
char *mac_clang_platform_inc[] = { "platform_all", "platform_unix", 0 };
|
||||||
|
|
||||||
|
char **platform_includes[Platform_COUNT][Compiler_COUNT] = {
|
||||||
|
{windows_cl_platform_inc, 0 , 0},
|
||||||
|
{0 , linux_gcc_platform_inc, 0},
|
||||||
|
{0 , 0 , mac_clang_platform_inc},
|
||||||
|
};
|
||||||
|
|
||||||
|
char *default_custom_target = "../code/custom/4coder_default_bindings.cpp";
|
||||||
|
|
||||||
|
// NOTE(allen): Build flags
|
||||||
|
|
||||||
|
enum{
|
||||||
|
OPTS = 0x1,
|
||||||
|
LIBS = 0x2,
|
||||||
|
ICON = 0x4,
|
||||||
|
SHARED_CODE = 0x8,
|
||||||
|
DEBUG_INFO = 0x10,
|
||||||
|
OPTIMIZATION = 0x20,
|
||||||
|
SUPER = 0x40,
|
||||||
|
INTERNAL = 0x80,
|
||||||
|
SHIP = 0x100,
|
||||||
|
};
|
||||||
|
|
||||||
|
internal char**
|
||||||
|
get_defines_from_flags(Arena *arena, u32 flags){
|
||||||
|
char **result = 0;
|
||||||
|
if (HasFlag(flags, SHIP)){
|
||||||
|
result = fm_list(arena, fm_list_one_item(arena, "SHIP_MODE"), result);
|
||||||
|
}
|
||||||
|
if (HasFlag(flags, INTERNAL)){
|
||||||
|
result = fm_list(arena, fm_list_one_item(arena, "FRED_INTERNAL"), result);
|
||||||
|
}
|
||||||
|
if (HasFlag(flags, SUPER)){
|
||||||
|
result = fm_list(arena, fm_list_one_item(arena, "FRED_SUPER"), result);
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// build implementation: cl
|
||||||
|
//
|
||||||
|
|
||||||
|
#if COMPILER_CL
|
||||||
|
|
||||||
|
#define CL_OPTS \
|
||||||
|
"-W4 -wd4310 -wd4100 -wd4201 -wd4505 -wd4996 " \
|
||||||
|
"-wd4127 -wd4510 -wd4512 -wd4610 -wd4390 " \
|
||||||
|
"-wd4611 -wd4189 -WX -GR- -EHa- -nologo -FC"
|
||||||
|
|
||||||
|
#define CL_LIBS_COMMON \
|
||||||
|
"user32.lib winmm.lib gdi32.lib opengl32.lib comdlg32.lib userenv.lib "
|
||||||
|
#define CL_LIBS_X64 CL_LIBS_COMMON FOREIGN_WIN "\\x64\\freetype.lib"
|
||||||
|
#define CL_LIBS_X86 CL_LIBS_COMMON FOREIGN_WIN "\\x86\\freetype.lib"
|
||||||
|
|
||||||
|
#define CL_ICON "..\\4coder-non-source\\res\\icon.res"
|
||||||
|
|
||||||
|
internal void
|
||||||
|
build(Arena *arena, u32 flags, u32 arch, char *code_path, char **code_files, char *out_path, char *out_file, char **defines, char **exports, char **inc_folders){
|
||||||
|
Temp_Dir temp = fm_pushdir(out_path);
|
||||||
|
|
||||||
|
Build_Line line;
|
||||||
|
fm_init_build_line(&line);
|
||||||
|
|
||||||
|
if (arch == Arch_X86){
|
||||||
|
fm_add_to_line(line, "%s\\custom\\bin\\setup_cl_x86.bat &", code_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
fm_add_to_line(line, "cl");
|
||||||
|
|
||||||
|
if (flags & OPTS){
|
||||||
|
fm_add_to_line(line, CL_OPTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (arch){
|
||||||
|
case Arch_X64: fm_add_to_line(line, "-DFTECH_64_BIT"); break;
|
||||||
|
case Arch_X86: fm_add_to_line(line, "-DFTECH_32_BIT"); break;
|
||||||
|
default: InvalidPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
fm_add_to_line(line, "-I%s", code_path);
|
||||||
|
if (inc_folders != 0){
|
||||||
|
for (u32 i = 0; inc_folders[i] != 0; ++i){
|
||||||
|
char *str = fm_str(arena, code_path, "/", inc_folders[i]);
|
||||||
|
fm_add_to_line(line, "-I%s", str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & LIBS){
|
||||||
|
switch (arch){
|
||||||
|
case Arch_X64: fm_add_to_line(line, CL_LIBS_X64); break;
|
||||||
|
case Arch_X86: fm_add_to_line(line, CL_LIBS_X86); break;
|
||||||
|
default: InvalidPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & ICON){
|
||||||
|
fm_add_to_line(line, CL_ICON);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & DEBUG_INFO){
|
||||||
|
fm_add_to_line(line, "-Zi");
|
||||||
|
fm_add_to_line(line, "-DDO_CRAZY_EXPENSIVE_ASSERTS");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & OPTIMIZATION){
|
||||||
|
fm_add_to_line(line, "-O2");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & SHARED_CODE){
|
||||||
|
fm_add_to_line(line, "-LD");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defines != 0){
|
||||||
|
for (u32 i = 0; defines[i] != 0; ++i){
|
||||||
|
char *define_flag = fm_str(arena, "-D", defines[i]);
|
||||||
|
fm_add_to_line(line, "%s", define_flag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (u32 i = 0; code_files[i]; ++i){
|
||||||
|
fm_add_to_line(line, "\"%s\\%s\"", code_path, code_files[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fm_add_to_line(line, "-Fe%s", out_file);
|
||||||
|
|
||||||
|
fm_add_to_line(line, "-link -INCREMENTAL:NO -RELEASE -PDBALTPATH:%%_PDB%%");
|
||||||
|
switch (arch){
|
||||||
|
case Arch_X64: fm_add_to_line(line, "-MACHINE:X64"); break;
|
||||||
|
case Arch_X86: fm_add_to_line(line, "-MACHINE:X86"); break;
|
||||||
|
default: InvalidPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & DEBUG_INFO){
|
||||||
|
fm_add_to_line(line, "-DEBUG");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & SHARED_CODE){
|
||||||
|
Assert(exports != 0);
|
||||||
|
fm_add_to_line(line, "-OPT:REF");
|
||||||
|
for (u32 i = 0; exports[i] != 0; ++i){
|
||||||
|
char *str = fm_str(arena, "-EXPORT:", exports[i]);
|
||||||
|
fm_add_to_line(line, "%s", str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
fm_add_to_line(line, "-NODEFAULTLIB:library");
|
||||||
|
}
|
||||||
|
|
||||||
|
fm_finish_build_line(&line);
|
||||||
|
|
||||||
|
//printf("%s\n", line.build_options);
|
||||||
|
systemf("%s", line.build_options);
|
||||||
|
fm_popdir(temp);
|
||||||
|
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// build implementation: gcc
|
||||||
|
//
|
||||||
|
|
||||||
|
#elif COMPILER_GCC
|
||||||
|
|
||||||
|
#if OS_LINUX
|
||||||
|
|
||||||
|
# define GCC_OPTS \
|
||||||
|
"-Wno-write-strings " \
|
||||||
|
"-D_GNU_SOURCE -fPIC " \
|
||||||
|
"-fno-threadsafe-statics -pthread " \
|
||||||
|
"-Wno-unused-result " \
|
||||||
|
"-std=c++11"
|
||||||
|
|
||||||
|
# define GCC_LIBS_COMMON \
|
||||||
|
"-lX11 -lpthread -lm -lrt " \
|
||||||
|
"-lGL -ldl -lXfixes -lfreetype -lfontconfig"
|
||||||
|
|
||||||
|
# define GCC_LIBS_X64 GCC_LIBS_COMMON
|
||||||
|
# define GCC_LIBS_X86 GCC_LIBS_COMMON
|
||||||
|
|
||||||
|
#else
|
||||||
|
# error gcc options not set for this platform
|
||||||
|
#endif
|
||||||
|
|
||||||
|
internal void
|
||||||
|
build(Arena *arena, u32 flags, u32 arch, char *code_path, char **code_files, char *out_path, char *out_file, char **defines, char **exports, char **inc_folders){
|
||||||
|
Build_Line line;
|
||||||
|
fm_init_build_line(&line);
|
||||||
|
|
||||||
|
switch (arch){
|
||||||
|
case Arch_X64:
|
||||||
|
fm_add_to_line(line, "-m64");
|
||||||
|
fm_add_to_line(line, "-DFTECH_64_BIT"); break;
|
||||||
|
|
||||||
|
case Arch_X86:
|
||||||
|
fm_add_to_line(line, "-m32");
|
||||||
|
fm_add_to_line(line, "-DFTECH_32_BIT"); break;
|
||||||
|
|
||||||
|
default: InvalidPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & OPTS){
|
||||||
|
fm_add_to_line(line, GCC_OPTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
fm_add_to_line(line, "-I%s", code_path);
|
||||||
|
if (inc_folders != 0){
|
||||||
|
for (u32 i = 0; inc_folders[i] != 0; ++i){
|
||||||
|
char *str = fm_str(arena, code_path, "/", inc_folders[i]);
|
||||||
|
fm_add_to_line(line, "-I%s", str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & DEBUG_INFO){
|
||||||
|
fm_add_to_line(line, "-g -O0");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & OPTIMIZATION){
|
||||||
|
fm_add_to_line(line, "-O3");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & SHARED_CODE){
|
||||||
|
fm_add_to_line(line, "-shared");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defines != 0){
|
||||||
|
for (u32 i = 0; defines[i]; ++i){
|
||||||
|
char *define_flag = fm_str(arena, "-D", defines[i]);
|
||||||
|
fm_add_to_line(line, "%s", define_flag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fm_add_to_line(line, "-I\"%s\"", code_path);
|
||||||
|
for (u32 i = 0; code_files[i] != 0; ++i){
|
||||||
|
fm_add_to_line(line, "\"%s/%s\"", code_path, code_files[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & LIBS){
|
||||||
|
if (arch == Arch_X64){
|
||||||
|
fm_add_to_line(line, GCC_LIBS_X64);
|
||||||
|
}
|
||||||
|
else if (arch == Arch_X86)
|
||||||
|
{
|
||||||
|
fm_add_to_line(line, GCC_LIBS_X86);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fm_finish_build_line(&line);
|
||||||
|
|
||||||
|
Temp_Dir temp = fm_pushdir(out_path);
|
||||||
|
systemf("g++ %s -o %s", line.build_options, out_file);
|
||||||
|
fm_popdir(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif COMPILER_CLANG
|
||||||
|
|
||||||
|
#if OS_MAC
|
||||||
|
|
||||||
|
# define CLANG_OPTS \
|
||||||
|
"-Wno-write-strings -Wno-deprecated-declarations " \
|
||||||
|
"-Wno-comment -Wno-switch -Wno-null-dereference " \
|
||||||
|
"-Wno-tautological-compare -Wno-unused-result " \
|
||||||
|
"-Wno-missing-declarations -Wno-nullability-completeness " \
|
||||||
|
"-std=c++11 "
|
||||||
|
|
||||||
|
#define CLANG_LIBS_COMMON \
|
||||||
|
"-framework Cocoa -framework QuartzCore " \
|
||||||
|
"-framework CoreServices " \
|
||||||
|
"-framework OpenGL -framework IOKit -framework Metal -framework MetalKit "
|
||||||
|
|
||||||
|
#define CLANG_LIBS_X64 CLANG_LIBS_COMMON \
|
||||||
|
FOREIGN "/x64/libfreetype-mac.a"
|
||||||
|
|
||||||
|
#define CLANG_LIBS_X86 CLANG_LIBS_COMMON \
|
||||||
|
FOREIGN "/x86/libfreetype-mac.a"
|
||||||
|
|
||||||
|
#else
|
||||||
|
# error clang options not set for this platform
|
||||||
|
#endif
|
||||||
|
|
||||||
|
internal void
|
||||||
|
build(Arena *arena, u32 flags, u32 arch, char *code_path, char **code_files, char *out_path, char *out_file, char **defines, char **exports, char **inc_folders){
|
||||||
|
Build_Line line;
|
||||||
|
fm_init_build_line(&line);
|
||||||
|
|
||||||
|
switch (arch){
|
||||||
|
case Arch_X64:
|
||||||
|
fm_add_to_line(line, "-m64");
|
||||||
|
fm_add_to_line(line, "-DFTECH_64_BIT"); break;
|
||||||
|
|
||||||
|
case Arch_X86:
|
||||||
|
fm_add_to_line(line, "-m32");
|
||||||
|
fm_add_to_line(line, "-DFTECH_32_BIT"); break;
|
||||||
|
|
||||||
|
default: InvalidPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & OPTS){
|
||||||
|
fm_add_to_line(line, CLANG_OPTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
fm_add_to_line(line, "-I%s", code_path);
|
||||||
|
if (inc_folders != 0){
|
||||||
|
for (u32 i = 0; inc_folders[i] != 0; ++i){
|
||||||
|
char *str = fm_str(arena, code_path, "/", inc_folders[i]);
|
||||||
|
fm_add_to_line(line, "-I%s", str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & DEBUG_INFO){
|
||||||
|
fm_add_to_line(line, "-g -O0");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & OPTIMIZATION){
|
||||||
|
fm_add_to_line(line, "-O3");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & SHARED_CODE){
|
||||||
|
fm_add_to_line(line, "-shared");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defines != 0){
|
||||||
|
for (u32 i = 0; defines[i]; ++i){
|
||||||
|
char *define_flag = fm_str(arena, "-D", defines[i]);
|
||||||
|
fm_add_to_line(line, "%s", define_flag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fm_add_to_line(line, "-I\"%s\"", code_path);
|
||||||
|
for (u32 i = 0; code_files[i] != 0; ++i){
|
||||||
|
fm_add_to_line(line, "\"%s/%s\"", code_path, code_files[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & LIBS){
|
||||||
|
if (arch == Arch_X64){
|
||||||
|
fm_add_to_line(line, CLANG_LIBS_X64);
|
||||||
|
}
|
||||||
|
else if (arch == Arch_X86)
|
||||||
|
{
|
||||||
|
fm_add_to_line(line, CLANG_LIBS_X86);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fm_finish_build_line(&line);
|
||||||
|
|
||||||
|
Temp_Dir temp = fm_pushdir(out_path);
|
||||||
|
|
||||||
|
// systemf("clang++ %s -E -o %s", line.build_options, "4ed.i");
|
||||||
|
systemf("clang++ %s -o %s", line.build_options, out_file);
|
||||||
|
fm_popdir(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
# error build function not defined for this compiler
|
||||||
|
#endif
|
||||||
|
|
||||||
|
internal void
|
||||||
|
build(Arena *arena, u32 flags, u32 arch, char *code_path, char *code_file, char *out_path, char *out_file, char **defines, char **exports, char **inc_folders){
|
||||||
|
char **code_files = fm_list_one_item(arena, code_file);
|
||||||
|
build(arena, flags, arch, code_path, code_files, out_path, out_file, defines, exports, inc_folders);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
build_and_run(Arena *arena, char *cdir, char *filename, char *name, u32 flags){
|
||||||
|
char *dir = fm_str(arena, BUILD_DIR);
|
||||||
|
|
||||||
|
{
|
||||||
|
char *file = fm_str(arena, filename);
|
||||||
|
build(arena, flags, Arch_X64, cdir, file, dir, name, get_defines_from_flags(arena, flags), 0, includes);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prev_error == 0){
|
||||||
|
char *cmd = fm_str(arena, dir, "/", name);
|
||||||
|
fm_execute_in_dir(cdir, cmd, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
buildsuper(Arena *arena, char *cdir, char *file, u32 arch){
|
||||||
|
printf("BUILDSUPER:\n cdir = %s;\n file = %s;\n arch = %s;\n", cdir, file, arch_names[arch]);
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
Temp_Dir temp = fm_pushdir(fm_str(arena, BUILD_DIR));
|
||||||
|
|
||||||
|
char *build_script_postfix = "";
|
||||||
|
switch (This_OS){
|
||||||
|
case Platform_Windows:
|
||||||
|
{
|
||||||
|
build_script_postfix = "-win";
|
||||||
|
}break;
|
||||||
|
case Platform_Linux:
|
||||||
|
{
|
||||||
|
build_script_postfix = "-linux";
|
||||||
|
}break;
|
||||||
|
case Platform_Mac:
|
||||||
|
{
|
||||||
|
build_script_postfix = "-mac";
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
char *build_script = fm_str(arena, "custom/bin/buildsuper_", arch_names[arch], build_script_postfix, BAT);
|
||||||
|
|
||||||
|
char *build_command = fm_str(arena, "\"", cdir, "/", build_script, "\" \"", file, "\"");
|
||||||
|
if (This_OS == Platform_Windows){
|
||||||
|
build_command = fm_str(arena, "call ", build_command);
|
||||||
|
}
|
||||||
|
systemf("%s", build_command);
|
||||||
|
|
||||||
|
fm_popdir(temp);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
build_main(Arena *arena, char *cdir, b32 update_local_theme, u32 flags, u32 arch){
|
||||||
|
char *dir = fm_str(arena, BUILD_DIR);
|
||||||
|
|
||||||
|
{
|
||||||
|
char *file = fm_str(arena, "4ed_app_target.cpp");
|
||||||
|
char **exports = fm_list_one_item(arena, "app_get_functions");
|
||||||
|
|
||||||
|
char **build_includes = includes;
|
||||||
|
|
||||||
|
build(arena, OPTS | SHARED_CODE | flags, arch, cdir, file, dir, "4ed_app" DLL, get_defines_from_flags(arena, flags), exports, build_includes);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
char **inc = (char**)fm_list(arena, includes, platform_includes[This_OS][This_Compiler]);
|
||||||
|
build(arena, OPTS | LIBS | ICON | flags, arch, cdir, platform_layers[This_OS], dir, "4ed", get_defines_from_flags(arena, flags), 0, inc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (update_local_theme){
|
||||||
|
char *themes_folder = fm_str(arena, "../build/themes");
|
||||||
|
char *source_themes_folder = fm_str(arena, "ship_files/themes");
|
||||||
|
fm_clear_folder(themes_folder);
|
||||||
|
fm_make_folder_if_missing(arena, themes_folder);
|
||||||
|
fm_copy_all(source_themes_folder, themes_folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
standard_build(Arena *arena, char *cdir, u32 flags, u32 arch){
|
||||||
|
buildsuper(arena, cdir, fm_str(arena, default_custom_target), arch);
|
||||||
|
build_main(arena, cdir, true, flags, arch);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal char*
|
||||||
|
get_4coder_dist_name(Arena *arena, u32 platform, char *tier, u32 arch){
|
||||||
|
char *name = fm_str(arena, "4coder-" MAJOR_STR "-" MINOR_STR "-" PATCH_STR "-", tier);
|
||||||
|
if (platform != Platform_None){
|
||||||
|
name = fm_str(arena, name, "-", platform_names[platform]);
|
||||||
|
}
|
||||||
|
if (arch != Arch_None){
|
||||||
|
name = fm_str(arena, name, "-", arch_names[arch]);
|
||||||
|
}
|
||||||
|
return(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
package_for_arch(Arena *arena, u32 arch, char *cdir, char *build_dir, char *pack_dir, i32 tier, char *tier_name, char *current_dist_tier, u32 flags, char** dist_files, i32 dist_file_count){
|
||||||
|
char *arch_name = arch_names[arch];
|
||||||
|
char *parent_dir = fm_str(arena, current_dist_tier, "_", arch_name);
|
||||||
|
char *dir = fm_str(arena, parent_dir, SLASH "4coder");
|
||||||
|
char *zip_dir = fm_str(arena, pack_dir, SLASH, tier_name, "_", arch_name);
|
||||||
|
|
||||||
|
printf("\nBUILD: %s_%s\n", tier_name, arch_name);
|
||||||
|
printf(" parent_dir = %s;\n", parent_dir);
|
||||||
|
printf(" dir = %s;\n", dir);
|
||||||
|
printf(" zip_dir = %s;\n", zip_dir);
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
buildsuper(arena, cdir, fm_str(arena, default_custom_target), arch);
|
||||||
|
build_main(arena, cdir, false, flags, arch);
|
||||||
|
|
||||||
|
fm_clear_folder(parent_dir);
|
||||||
|
fm_make_folder_if_missing(arena, parent_dir);
|
||||||
|
|
||||||
|
fm_make_folder_if_missing(arena, dir);
|
||||||
|
fm_copy_file(fm_str(arena, build_dir, "/4ed" EXE), fm_str(arena, dir, "/4ed" EXE));
|
||||||
|
fm_copy_file(fm_str(arena, build_dir, "/4ed_app" DLL), fm_str(arena, dir, "/4ed_app" DLL));
|
||||||
|
fm_copy_file(fm_str(arena, build_dir, "/custom_4coder" DLL), fm_str(arena, dir, "/custom_4coder" DLL));
|
||||||
|
|
||||||
|
if (tier == Tier_Demo){
|
||||||
|
dist_file_count -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i32 j = 0; j < dist_file_count; j += 1){
|
||||||
|
fm_copy_all(dist_files[j], dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tier == Tier_Super){
|
||||||
|
char *custom_src_dir = fm_str(arena, cdir, SLASH, "custom");
|
||||||
|
char *custom_dst_dir = fm_str(arena, dir, SLASH, "custom");
|
||||||
|
fm_make_folder_if_missing(arena, custom_dst_dir);
|
||||||
|
fm_copy_all(custom_src_dir, custom_dst_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *dist_name = get_4coder_dist_name(arena, This_OS, tier_name, arch);
|
||||||
|
char *zip_name = fm_str(arena, zip_dir, SLASH, dist_name, ".zip");
|
||||||
|
fm_make_folder_if_missing(arena, zip_dir);
|
||||||
|
fm_zip(parent_dir, "4coder", zip_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal u32
|
||||||
|
tier_flags(Tier_Code code){
|
||||||
|
u32 result = 0;
|
||||||
|
switch (code){
|
||||||
|
case Tier_Super:
|
||||||
|
{
|
||||||
|
result = SUPER;
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
package(Arena *arena, char *cdir, Tier_Code tier, Arch_Code arch){
|
||||||
|
// NOTE(allen): meta
|
||||||
|
char *build_dir = fm_str(arena, BUILD_DIR);
|
||||||
|
char *pack_dir = fm_str(arena, PACK_DIR);
|
||||||
|
char *dist_files[3];
|
||||||
|
dist_files[0] = fm_str(arena, "../4coder-non-source/dist_files");
|
||||||
|
dist_files[1] = fm_str(arena, "ship_files");
|
||||||
|
dist_files[2] = fm_str(arena, "ship_files_super");
|
||||||
|
|
||||||
|
printf("build dir: %s\n", build_dir);
|
||||||
|
printf("pack dir: %s\n", pack_dir);
|
||||||
|
printf("dist files: %s, %s, %s\n", dist_files[0], dist_files[1], dist_files[2]);
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
u32 base_flags = SHIP | DEBUG_INFO | OPTIMIZATION;
|
||||||
|
|
||||||
|
fm_make_folder_if_missing(arena, pack_dir);
|
||||||
|
|
||||||
|
char *tier_name = tier_names[tier];
|
||||||
|
u32 flags = base_flags | tier_flags(tier);
|
||||||
|
Temp_Memory temp = begin_temp(arena);
|
||||||
|
char *current_dist_tier = fm_str(arena, ".." SLASH "current_dist_", tier_name);
|
||||||
|
package_for_arch(arena, arch, cdir, build_dir, pack_dir, tier, tier_name, current_dist_tier, flags, dist_files, ArrayCount(dist_files));
|
||||||
|
end_temp(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv){
|
||||||
|
Arena arena = fm_init_system(DetailLevel_FileOperations);
|
||||||
|
|
||||||
|
char cdir[256];
|
||||||
|
i32 n = fm_get_current_directory(cdir, sizeof(cdir));
|
||||||
|
Assert(n < sizeof(cdir));
|
||||||
|
|
||||||
|
u32 flags = SUPER;
|
||||||
|
u32 arch = Arch_X64;
|
||||||
|
#if defined(DEV_BUILD) || defined(DEV_BUILD_X86)
|
||||||
|
flags |= DEBUG_INFO | INTERNAL;
|
||||||
|
#endif
|
||||||
|
#if defined(OPT_BUILD) || defined(OPT_BUILD_X86)
|
||||||
|
flags |= OPTIMIZATION;
|
||||||
|
#endif
|
||||||
|
#if defined(DEV_BUILD_X86) || defined(OPT_BUILD_X86)
|
||||||
|
arch = Arch_X86;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(DEV_BUILD) || defined(OPT_BUILD) || defined(DEV_BUILD_X86) || defined(OPT_BUILD_X86)
|
||||||
|
standard_build(&arena, cdir, flags, arch);
|
||||||
|
|
||||||
|
#elif defined(PACKAGE_DEMO_X64)
|
||||||
|
package(&arena, cdir, Tier_Demo, Arch_X64);
|
||||||
|
|
||||||
|
#elif defined(PACKAGE_DEMO_X86)
|
||||||
|
package(&arena, cdir, Tier_Demo, Arch_X86);
|
||||||
|
|
||||||
|
#elif defined(PACKAGE_SUPER_X64)
|
||||||
|
package(&arena, cdir, Tier_Super, Arch_X64);
|
||||||
|
|
||||||
|
#elif defined(PACKAGE_SUPER_X86)
|
||||||
|
package(&arena, cdir, Tier_Super, Arch_X86);
|
||||||
|
|
||||||
|
#else
|
||||||
|
# error No build type specified.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return(error_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# If any command errors, stop the script
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Set up directories
|
||||||
|
|
||||||
|
ME="$(readlink -f "$0")"
|
||||||
|
LOCATION="$(dirname "$ME")"
|
||||||
|
SRC_ROOT="$(dirname "$LOCATION")"
|
||||||
|
PROJECT_ROOT="$(dirname "$SRC_ROOT")"
|
||||||
|
if [ ! -d "$PROJECT_ROOT/build" ]; then
|
||||||
|
mkdir "$PROJECT_ROOT/build"
|
||||||
|
fi
|
||||||
|
BUILD_ROOT="$PROJECT_ROOT/build"
|
||||||
|
BIN_ROOT="$SRC_ROOT/bin"
|
||||||
|
CUSTOM_ROOT="$SRC_ROOT/custom"
|
||||||
|
CUSTOM_BIN="$CUSTOM_ROOT/bin"
|
||||||
|
|
||||||
|
# Get the build mode
|
||||||
|
BUILD_MODE="$1"
|
||||||
|
if [ -z "$BUILD_MODE" ]; then
|
||||||
|
BUILD_MODE="-DDEV_BUILD"
|
||||||
|
fi
|
||||||
|
|
||||||
|
WARNINGS="-Wno-write-strings -Wno-comment"
|
||||||
|
|
||||||
|
FLAGS="-D_GNU_SOURCE -fPIC -fpermissive $BUILD_MODE"
|
||||||
|
INCLUDES="-I$SRC_ROOT -I$CUSTOM_ROOT"
|
||||||
|
|
||||||
|
# Execute
|
||||||
|
g++ $WARNINGS $FLAGS $INCLUDES "$BIN_ROOT/4ed_build.cpp" -g -o "$BUILD_ROOT/build"
|
||||||
|
pushd "$SRC_ROOT"
|
||||||
|
"$BUILD_ROOT/build"
|
||||||
|
popd
|
|
@ -0,0 +1,35 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# If any command errors, stop the script
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Set up directories
|
||||||
|
# NOTE(yuval): Replaced readlink with realpath which works for both macOS and Linux
|
||||||
|
ME="$(realpath "$0")"
|
||||||
|
LOCATION="$(dirname "$ME")"
|
||||||
|
SRC_ROOT="$(dirname "$LOCATION")"
|
||||||
|
PROJECT_ROOT="$(dirname "$SRC_ROOT")"
|
||||||
|
if [ ! -d "$PROJECT_ROOT/build" ]; then
|
||||||
|
mkdir "$PROJECT_ROOT/build"
|
||||||
|
fi
|
||||||
|
BUILD_ROOT="$PROJECT_ROOT/build"
|
||||||
|
BIN_ROOT="$SRC_ROOT/bin"
|
||||||
|
CUSTOM_ROOT="$SRC_ROOT/custom"
|
||||||
|
CUSTOM_BIN="$CUSTOM_ROOT/bin"
|
||||||
|
|
||||||
|
# Get the build mode
|
||||||
|
BUILD_MODE="$1"
|
||||||
|
if [ -z "$BUILD_MODE" ]; then
|
||||||
|
BUILD_MODE="-DDEV_BUILD"
|
||||||
|
fi
|
||||||
|
|
||||||
|
WARNINGS="-Wno-write-strings -Wno-comment -Wno-null-dereference -Wno-logical-op-parentheses -Wno-switch"
|
||||||
|
|
||||||
|
FLAGS="-D_GNU_SOURCE -fPIC -fpermissive $BUILD_MODE"
|
||||||
|
INCLUDES="-I$SRC_ROOT -I$CUSTOM_ROOT"
|
||||||
|
|
||||||
|
# Execute
|
||||||
|
clang++ $WARNINGS $FLAGS $INCLUDES "$BIN_ROOT/4ed_build.cpp" -g -o "$BUILD_ROOT/build"
|
||||||
|
pushd "$SRC_ROOT"
|
||||||
|
"$BUILD_ROOT/build"
|
||||||
|
popd
|
|
@ -0,0 +1,7 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
ME="$(readlink -f "$0")"
|
||||||
|
LOCATION="$(dirname "$ME")"
|
||||||
|
$LOCATION/build-linux.sh -DDEV_BUILD_X86
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
@echo off
|
||||||
|
bin\build.bat /DDEV_BUILD_X86
|
|
@ -0,0 +1,32 @@
|
||||||
|
@echo off
|
||||||
|
|
||||||
|
set location=%cd%
|
||||||
|
set me="%~dp0"
|
||||||
|
cd %me%
|
||||||
|
cd ..
|
||||||
|
set src_root=%cd%
|
||||||
|
cd ..\build
|
||||||
|
set build_root=%cd%
|
||||||
|
set bin_root=%src_root%\bin
|
||||||
|
set custom_root=%src_root%\custom
|
||||||
|
set custom_bin=%custom_root\bin
|
||||||
|
cd %location%
|
||||||
|
|
||||||
|
set mode=%1
|
||||||
|
if "%mode%" == "" (set mode="/DDEV_BUILD")
|
||||||
|
|
||||||
|
set opts=/W4 /wd4310 /wd4100 /wd4201 /wd4505 /wd4996 /wd4127 /wd4510 /wd4512 /wd4610 /wd4390 /wd4189 /WX
|
||||||
|
set opts=%opts% /GR- /EHa- /nologo /FC /Zi
|
||||||
|
set opts=%opts% /I%src_root% /I%custom_root%
|
||||||
|
set opts=%opts% %mode%
|
||||||
|
|
||||||
|
set FirstError=0
|
||||||
|
pushd %build_root%
|
||||||
|
call cl %opts% kernel32.lib %bin_root%\4ed_build.cpp /Febuild
|
||||||
|
if %ERRORLEVEL% neq 0 (set FirstError=1)
|
||||||
|
if %ERRORLEVEL% neq 0 (goto END)
|
||||||
|
popd
|
||||||
|
|
||||||
|
%build_root%\build
|
||||||
|
:END
|
||||||
|
if %ERRORLEVEL% neq 0 (set FirstError=1)
|
|
@ -0,0 +1,3 @@
|
||||||
|
@echo off
|
||||||
|
|
||||||
|
call bin\build.bat /DOPT_BUILD
|
|
@ -0,0 +1,9 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
os="unknown"
|
||||||
|
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||||
|
echo "mac"
|
||||||
|
elif [[ "$OSTYPE" == "linux-gnu" ]]; then
|
||||||
|
echo "linux"
|
||||||
|
fi
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if [ "$#" -lt "3" ]
|
||||||
|
then
|
||||||
|
echo need 3 parameters
|
||||||
|
exit
|
||||||
|
else
|
||||||
|
|
||||||
|
fake=$1
|
||||||
|
maj=$2
|
||||||
|
min=$3
|
||||||
|
|
||||||
|
vr=$fake.$maj.$min
|
||||||
|
fv=$fake-$maj-$min
|
||||||
|
|
||||||
|
flags="--fix-permissions --userversion=$vr"
|
||||||
|
dir=../current_dist_all_os
|
||||||
|
|
||||||
|
butler push $flags $dir/4coder-alpha-$fv-win-x64.zip 4coder/4coder:win-x64-alpha
|
||||||
|
butler push $flags $dir/4coder-alpha-$fv-linux-x64.zip 4coder/4coder:linux-x64-alpha
|
||||||
|
butler push $flags $dir/4coder-alpha-$fv-mac-x64.zip 4coder/4coder:osx-x64-alpha
|
||||||
|
butler push $flags $dir/4coder-alpha-$fv-win-x86.zip 4coder/4coder:win-x86-alpha
|
||||||
|
butler push $flags $dir/4coder-alpha-$fv-linux-x86.zip 4coder/4coder:linux-x86-alpha
|
||||||
|
butler push $flags $dir/4coder-alpha-$fv-mac-x86.zip 4coder/4coder:osx-x86-alpha
|
||||||
|
|
||||||
|
butler push $flags $dir/4coder-alpha-$fv-super-win-x64.zip 4coder/4coder:win-x64-super
|
||||||
|
butler push $flags $dir/4coder-alpha-$fv-super-linux-x64.zip 4coder/4coder:linux-x64-super
|
||||||
|
butler push $flags $dir/4coder-alpha-$fv-super-mac-x64.zip 4coder/4coder:osx-x64-super
|
||||||
|
butler push $flags $dir/4coder-alpha-$fv-super-win-x86.zip 4coder/4coder:win-x86-super
|
||||||
|
butler push $flags $dir/4coder-alpha-$fv-super-linux-x86.zip 4coder/4coder:linux-x86-super
|
||||||
|
butler push $flags $dir/4coder-alpha-$fv-super-mac-x86.zip 4coder/4coder:osx-x86-super
|
||||||
|
|
||||||
|
fi
|
|
@ -0,0 +1,22 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if [ "$#" -lt "3" ]
|
||||||
|
then
|
||||||
|
echo need 3 parameters
|
||||||
|
exit
|
||||||
|
else
|
||||||
|
|
||||||
|
fake=$1
|
||||||
|
maj=$2
|
||||||
|
min=$3
|
||||||
|
|
||||||
|
vr=$fake.$maj.$min
|
||||||
|
fv=$fake-$maj-$min
|
||||||
|
|
||||||
|
flags="--fix-permissions --userversion=$vr"
|
||||||
|
dir=../distributions
|
||||||
|
|
||||||
|
butler push $flags $dir/demo_x86/4coder-$fv-demo-linux-x86.zip 4coder/4coder:linux-x86-demo
|
||||||
|
butler push $flags $dir/super_x86/4coder-$fv-super-linux-x86.zip 4coder/4coder:linux-x86
|
||||||
|
|
||||||
|
fi
|
|
@ -0,0 +1,22 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if [ "$#" -lt "3" ]
|
||||||
|
then
|
||||||
|
echo need 3 parameters
|
||||||
|
exit
|
||||||
|
else
|
||||||
|
|
||||||
|
fake=$1
|
||||||
|
maj=$2
|
||||||
|
min=$3
|
||||||
|
|
||||||
|
vr=$fake.$maj.$min
|
||||||
|
fv=$fake-$maj-$min
|
||||||
|
|
||||||
|
flags="--fix-permissions --userversion=$vr"
|
||||||
|
dir=../distributions
|
||||||
|
|
||||||
|
butler push $flags $dir/demo_x64/4coder-$fv-demo-linux-x64.zip 4coder/4coder:linux-x64-demo
|
||||||
|
butler push $flags $dir/super_x64/4coder-$fv-super-linux-x64.zip 4coder/4coder:linux-x64
|
||||||
|
|
||||||
|
fi
|
|
@ -0,0 +1,22 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if [ "$#" -lt "3" ]
|
||||||
|
then
|
||||||
|
echo need 3 parameters
|
||||||
|
exit
|
||||||
|
else
|
||||||
|
|
||||||
|
fake=$1
|
||||||
|
maj=$2
|
||||||
|
min=$3
|
||||||
|
|
||||||
|
vr=$fake.$maj.$min
|
||||||
|
fv=$fake-$maj-$min
|
||||||
|
|
||||||
|
flags="--fix-permissions --userversion=$vr"
|
||||||
|
dir=../distributions
|
||||||
|
|
||||||
|
butler push $flags $dir/demo_x64/4coder-$fv-demo-mac-x64.zip 4coder/4coder:mac-x64-demo
|
||||||
|
butler push $flags $dir/super_x64/4coder-$fv-super-mac-x64.zip 4coder/4coder:mac-x64
|
||||||
|
|
||||||
|
fi
|
|
@ -0,0 +1,26 @@
|
||||||
|
@echo off
|
||||||
|
|
||||||
|
IF "%3" == "" (echo need 3 parameters & GOTO END)
|
||||||
|
|
||||||
|
SET fake=%1
|
||||||
|
SET maj=%2
|
||||||
|
SET min=%3
|
||||||
|
|
||||||
|
SET vr=%fake%.%maj%.%min%
|
||||||
|
SET fv=%fake%-%maj%-%min%
|
||||||
|
|
||||||
|
SET flags=--fix-permissions --userversion=%vr%
|
||||||
|
|
||||||
|
pushd ..
|
||||||
|
|
||||||
|
SET dir=%CD%\distributions
|
||||||
|
|
||||||
|
butler push %flags% %dir%\demo_x64\4coder-%fv%-demo-win-x64.zip 4coder/4coder:win-x64-demo
|
||||||
|
butler push %flags% %dir%\super_x64\4coder-%fv%-super-win-x64.zip 4coder/4coder:win-x64
|
||||||
|
|
||||||
|
REM butler push %flags% %dir%\demo_x86\4coder-%fv%-demo-win-x86.zip 4coder/4coder:win-x86-demo
|
||||||
|
REM butler push %flags% %dir%\super_x86\4coder-%fv%-super-win-x86.zip 4coder/4coder:win-x86
|
||||||
|
|
||||||
|
popd
|
||||||
|
|
||||||
|
:END
|
|
@ -0,0 +1,5 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
chmod +x bin/build-linux.sh
|
||||||
|
bin/build-linux.sh "-DPACKAGE_DEMO_X64"
|
||||||
|
bin/build-linux.sh "-DPACKAGE_SUPER_X64"
|
|
@ -0,0 +1,5 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
chmod +x bin/build-mac.sh
|
||||||
|
bin/build-mac.sh "-DPACKAGE_DEMO_X64"
|
||||||
|
bin/build-mac.sh "-DPACKAGE_SUPER_X64"
|
|
@ -0,0 +1,5 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
chmod +x bin/build-linux.sh
|
||||||
|
bin/build-linux.sh "-DPACKAGE_DEMO_X86"
|
||||||
|
bin/build-linux.sh "-DPACKAGE_SUPER_X86"
|
|
@ -0,0 +1,6 @@
|
||||||
|
@echo off
|
||||||
|
|
||||||
|
call bin\build.bat /DPACKAGE_DEMO_X64
|
||||||
|
call bin\build.bat /DPACKAGE_DEMO_X86
|
||||||
|
call bin\build.bat /DPACKAGE_SUPER_X64
|
||||||
|
call bin\build.bat /DPACKAGE_SUPER_X86
|
|
@ -0,0 +1,9 @@
|
||||||
|
@echo off
|
||||||
|
|
||||||
|
REM Usage: zip <archivename>
|
||||||
|
REM compresses the current directory into a zip named <archivename>.zip
|
||||||
|
|
||||||
|
SET ZIP_PATH=C:\Program Files (x86)\7-Zip
|
||||||
|
IF NOT EXIST "%ZIP_PATH%" (SET ZIP_PATH=C:\Program Files\7-Zip)
|
||||||
|
IF EXIST "%ZIP_PATH%" ("%ZIP_PATH%\7z.exe" a -tzip -y %*)
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* 4coder app links base allocator
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
Scratch_Block::Scratch_Block(Application_Links *app){
|
||||||
|
Thread_Context *t = this->tctx = get_thread_context(app);
|
||||||
|
this->arena = tctx_reserve(t);
|
||||||
|
this->temp = begin_temp(this->arena);
|
||||||
|
}
|
||||||
|
|
||||||
|
Scratch_Block::Scratch_Block(Application_Links *app, Arena *a1){
|
||||||
|
Thread_Context *t = this->tctx = get_thread_context(app);
|
||||||
|
this->arena = tctx_reserve(t, a1);
|
||||||
|
this->temp = begin_temp(this->arena);
|
||||||
|
}
|
||||||
|
|
||||||
|
Scratch_Block::Scratch_Block(Application_Links *app, Arena *a1, Arena *a2){
|
||||||
|
Thread_Context *t = this->tctx = get_thread_context(app);
|
||||||
|
this->arena = tctx_reserve(t, a1, a2);
|
||||||
|
this->temp = begin_temp(this->arena);
|
||||||
|
}
|
||||||
|
|
||||||
|
Scratch_Block::Scratch_Block(Application_Links *app, Arena *a1, Arena *a2, Arena *a3){
|
||||||
|
Thread_Context *t = this->tctx = get_thread_context(app);
|
||||||
|
this->arena = tctx_reserve(t, a1, a2, a3);
|
||||||
|
this->temp = begin_temp(this->arena);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,238 @@
|
||||||
|
/*
|
||||||
|
* 4coder_async_tasks.cpp - Implementation of the custom layer asynchronous task system.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
// TODO(allen): put atomic wrappers in base type layer
|
||||||
|
#define atomic_write_b32(p,v) (*(p)=(v))
|
||||||
|
#define atomic_read_b32(p) (*(p))
|
||||||
|
|
||||||
|
global Async_System global_async_system = {};
|
||||||
|
|
||||||
|
function Async_Node*
|
||||||
|
async_pop_node(Async_System *async_system){
|
||||||
|
for (;async_system->task_count == 0;){
|
||||||
|
system_condition_variable_wait(async_system->cv, async_system->mutex);
|
||||||
|
}
|
||||||
|
Node *node = async_system->task_sent.next;
|
||||||
|
Assert(node != &async_system->task_sent);
|
||||||
|
dll_remove(node);
|
||||||
|
async_system->task_count -= 1;
|
||||||
|
Async_Node *a_node = CastFromMember(Async_Node, node, node);
|
||||||
|
a_node->next = 0;
|
||||||
|
return(a_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Async_Node*
|
||||||
|
async_push_node__inner(Async_System *async_system, Async_Task_Function_Type *func, String_Const_u8 data){
|
||||||
|
Async_Task result = async_system->task_id_counter;
|
||||||
|
async_system->task_id_counter += 1;
|
||||||
|
|
||||||
|
Async_Node *node = async_system->free_nodes;
|
||||||
|
if (node == 0){
|
||||||
|
node = push_array(&async_system->node_arena, Async_Node, 1);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
sll_stack_pop(async_system->free_nodes);
|
||||||
|
}
|
||||||
|
node->task = result;
|
||||||
|
node->thread = 0;
|
||||||
|
node->func = func;
|
||||||
|
node->data.str = (u8*)heap_allocate(&async_system->node_heap, data.size);
|
||||||
|
block_copy(node->data.str, data.str, data.size);
|
||||||
|
node->data.size = data.size;
|
||||||
|
dll_insert_back(&async_system->task_sent, &node->node);
|
||||||
|
async_system->task_count += 1;
|
||||||
|
system_condition_variable_signal(async_system->cv);
|
||||||
|
|
||||||
|
return(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Async_Task
|
||||||
|
async_push_node(Async_System *async_system, Async_Task_Function_Type *func, String_Const_u8 data){
|
||||||
|
Async_Node *node = async_push_node__inner(async_system, func, data);
|
||||||
|
return(node->task);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
async_free_node(Async_System *async_system, Async_Node *node){
|
||||||
|
heap_free(&async_system->node_heap, node->data.str);
|
||||||
|
sll_stack_push(async_system->free_nodes, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
async_task_thread(void *thread_ptr){
|
||||||
|
Base_Allocator *allocator = get_base_allocator_system();
|
||||||
|
|
||||||
|
Thread_Context_Extra_Info tctx_info = {};
|
||||||
|
tctx_info.async_thread = thread_ptr;
|
||||||
|
|
||||||
|
Thread_Context tctx_ = {};
|
||||||
|
Thread_Context *tctx = &tctx_;
|
||||||
|
thread_ctx_init(tctx, ThreadKind_AsyncTasks, allocator, allocator);
|
||||||
|
|
||||||
|
Async_Thread *thread = (Async_Thread*)thread_ptr;
|
||||||
|
Async_System *async_system = thread->async_system;
|
||||||
|
|
||||||
|
Application_Links app = {};
|
||||||
|
app.tctx = tctx;
|
||||||
|
app.cmd_context = async_system->cmd_context;
|
||||||
|
|
||||||
|
Profile_Global_List *list = get_core_profile_list(&app);
|
||||||
|
ProfileThreadName(tctx, list, string_u8_litexpr("async"));
|
||||||
|
|
||||||
|
Async_Context ctx = {&app, thread};
|
||||||
|
|
||||||
|
for (;;){
|
||||||
|
system_mutex_acquire(async_system->mutex);
|
||||||
|
Async_Node *node = async_pop_node(async_system);
|
||||||
|
node->thread = thread;
|
||||||
|
thread->node = node;
|
||||||
|
thread->task = node->task;
|
||||||
|
thread->cancel_signal = false;
|
||||||
|
system_mutex_release(async_system->mutex);
|
||||||
|
|
||||||
|
node->func(&ctx, node->data);
|
||||||
|
|
||||||
|
system_mutex_acquire(async_system->mutex);
|
||||||
|
node->thread = 0;
|
||||||
|
thread->node = 0;
|
||||||
|
thread->task = 0;
|
||||||
|
thread->cancel_signal = false;
|
||||||
|
async_free_node(async_system, node);
|
||||||
|
system_condition_variable_signal(async_system->join_cv);
|
||||||
|
system_mutex_release(async_system->mutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Async_Node*
|
||||||
|
async_get_pending_node(Async_System *async_system, Async_Task task){
|
||||||
|
Async_Node *result = 0;
|
||||||
|
if (task != 0){
|
||||||
|
for (Node *node = async_system->task_sent.next;
|
||||||
|
node != &async_system->task_sent;
|
||||||
|
node = node->next){
|
||||||
|
Async_Node *a_node = CastFromMember(Async_Node, node, node);
|
||||||
|
if (a_node->task == task){
|
||||||
|
result = a_node;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Async_Node*
|
||||||
|
async_get_running_node(Async_System *async_system, Async_Task task){
|
||||||
|
Async_Node *result = 0;
|
||||||
|
if (task != 0 && async_system->thread.task == task){
|
||||||
|
result = async_system->thread.node;
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
function void
|
||||||
|
async_task_handler_init(Application_Links *app, Async_System *async_system){
|
||||||
|
block_zero_struct(async_system);
|
||||||
|
async_system->cmd_context = app->cmd_context;
|
||||||
|
async_system->node_arena = make_arena_system(KB(4));
|
||||||
|
heap_init(&async_system->node_heap, &async_system->node_arena);
|
||||||
|
async_system->mutex = system_mutex_make();
|
||||||
|
async_system->cv = system_condition_variable_make();
|
||||||
|
async_system->join_cv = system_condition_variable_make();
|
||||||
|
dll_init_sentinel(&async_system->task_sent);
|
||||||
|
async_system->thread.async_system = async_system;
|
||||||
|
async_system->thread.thread = system_thread_launch(async_task_thread, &async_system->thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Async_Task
|
||||||
|
async_task_no_dep(Async_System *async_system, Async_Task_Function_Type *func, String_Const_u8 data){
|
||||||
|
system_mutex_acquire(async_system->mutex);
|
||||||
|
Async_Task result = async_push_node(async_system, func, data);
|
||||||
|
system_mutex_release(async_system->mutex);
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function b32
|
||||||
|
async_task_is_pending(Async_System *async_system, Async_Task task){
|
||||||
|
system_mutex_acquire(async_system->mutex);
|
||||||
|
Async_Node *node = async_get_pending_node(async_system, task);
|
||||||
|
system_mutex_release(async_system->mutex);
|
||||||
|
return(node != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function b32
|
||||||
|
async_task_is_running(Async_System *async_system, Async_Task task){
|
||||||
|
system_mutex_acquire(async_system->mutex);
|
||||||
|
Async_Node *node = async_get_running_node(async_system, task);
|
||||||
|
system_mutex_release(async_system->mutex);
|
||||||
|
return(node != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function b32
|
||||||
|
async_task_is_running_or_pending__inner(Async_System *async_system, Async_Task task){
|
||||||
|
Async_Node *node = async_get_pending_node(async_system, task);
|
||||||
|
if (node == 0){
|
||||||
|
node = async_get_running_node(async_system, task);
|
||||||
|
}
|
||||||
|
return(node != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function b32
|
||||||
|
async_task_is_running_or_pending(Async_System *async_system, Async_Task task){
|
||||||
|
system_mutex_acquire(async_system->mutex);
|
||||||
|
b32 result = async_task_is_running_or_pending__inner(async_system, task);
|
||||||
|
system_mutex_release(async_system->mutex);
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
async_task_wait__inner(Application_Links *app, Async_System *async_system, Async_Task task){
|
||||||
|
release_global_frame_mutex(app);
|
||||||
|
for (;async_task_is_running_or_pending__inner(async_system, task);){
|
||||||
|
system_condition_variable_wait(async_system->join_cv, async_system->mutex);
|
||||||
|
}
|
||||||
|
acquire_global_frame_mutex(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
async_task_wait(Application_Links *app, Async_System *async_system, Async_Task task){
|
||||||
|
system_mutex_acquire(async_system->mutex);
|
||||||
|
if (async_task_is_running_or_pending__inner(async_system, task)){
|
||||||
|
async_task_wait__inner(app, async_system, task);
|
||||||
|
}
|
||||||
|
system_mutex_release(async_system->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
async_task_cancel(Application_Links *app, Async_System *async_system, Async_Task task){
|
||||||
|
system_mutex_acquire(async_system->mutex);
|
||||||
|
Async_Node *node = async_get_pending_node(async_system, task);
|
||||||
|
if (node != 0){
|
||||||
|
dll_remove(&node->node);
|
||||||
|
async_system->task_count -= 1;
|
||||||
|
async_free_node(async_system, node);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
node = async_get_running_node(async_system, task);
|
||||||
|
if (node != 0){
|
||||||
|
b32 *cancel_signal = &node->thread->cancel_signal;
|
||||||
|
atomic_write_b32(cancel_signal, true);
|
||||||
|
async_task_wait__inner(app, async_system, task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
system_mutex_release(async_system->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
function b32
|
||||||
|
async_check_canceled(Async_Context *actx){
|
||||||
|
b32 *cancel_signal = &actx->thread->cancel_signal;
|
||||||
|
b32 result = atomic_read_b32(cancel_signal);
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* 4coder_async_tasks.cpp - Types for the custom layer asynchronous task system.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#if !defined(FCODER_ASYNC_TASKS_H)
|
||||||
|
#define FCODER_ASYNC_TASKS_H
|
||||||
|
|
||||||
|
typedef void Async_Task_Function_Type(struct Async_Context *actx, String_Const_u8 data);
|
||||||
|
typedef u64 Async_Task;
|
||||||
|
|
||||||
|
struct Async_Thread{
|
||||||
|
struct Async_System *async_system;
|
||||||
|
System_Thread thread;
|
||||||
|
struct Async_Node *node;
|
||||||
|
Async_Task task;
|
||||||
|
b32 cancel_signal;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Async_Node{
|
||||||
|
union{
|
||||||
|
Async_Node *next;
|
||||||
|
Node node;
|
||||||
|
};
|
||||||
|
Async_Task task;
|
||||||
|
Async_Thread *thread;
|
||||||
|
Async_Task_Function_Type *func;
|
||||||
|
String_Const_u8 data;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Async_System{
|
||||||
|
void *cmd_context;
|
||||||
|
|
||||||
|
Heap node_heap;
|
||||||
|
Arena node_arena;
|
||||||
|
System_Mutex mutex;
|
||||||
|
System_Condition_Variable cv;
|
||||||
|
System_Condition_Variable join_cv;
|
||||||
|
Async_Task task_id_counter;
|
||||||
|
Async_Node *free_nodes;
|
||||||
|
Node task_sent;
|
||||||
|
i32 task_count;
|
||||||
|
|
||||||
|
Async_Thread thread;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Async_Context{
|
||||||
|
Application_Links *app;
|
||||||
|
Async_Thread *thread;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,276 @@
|
||||||
|
////////////////////////////////
|
||||||
|
// NOTE(allen): Default Mixer Helpers
|
||||||
|
|
||||||
|
// TODO(allen): intrinsics wrappers
|
||||||
|
#if OS_LINUX
|
||||||
|
#include <immintrin.h>
|
||||||
|
#define _InterlockedExchangeAdd __sync_fetch_and_add
|
||||||
|
#elif OS_MAC
|
||||||
|
#include <immintrin.h>
|
||||||
|
#define _InterlockedExchangeAdd __sync_fetch_and_add
|
||||||
|
#else
|
||||||
|
#include <intrin.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
function u32
|
||||||
|
AtomicAddU32AndReturnOriginal(u32 volatile *Value, u32 Addend)
|
||||||
|
{
|
||||||
|
// NOTE(casey): Returns the original value _prior_ to adding
|
||||||
|
u32 Result = _InterlockedExchangeAdd((long volatile*)Value, (long)Addend);
|
||||||
|
return(Result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
def_audio_begin_ticket_mutex(Audio_System *Crunky)
|
||||||
|
{
|
||||||
|
u32 Ticket = AtomicAddU32AndReturnOriginal(&Crunky->ticket, 1);
|
||||||
|
while(Ticket != Crunky->serving) {_mm_pause();}
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
def_audio_end_ticket_mutex(Audio_System *Crunky)
|
||||||
|
{
|
||||||
|
AtomicAddU32AndReturnOriginal(&Crunky->serving, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
// NOTE(allen): Default Mixer
|
||||||
|
|
||||||
|
global Audio_System def_audio_system = {};
|
||||||
|
|
||||||
|
function void
|
||||||
|
def_audio_init(void){
|
||||||
|
block_zero_struct(&def_audio_system);
|
||||||
|
system_set_source_mixer(&def_audio_system, def_audio_mix_sources);
|
||||||
|
system_set_destination_mixer(def_audio_mix_destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
def_audio_play_clip(Audio_Clip clip, Audio_Control *control){
|
||||||
|
clip.control = control;
|
||||||
|
Audio_System *Crunky = &def_audio_system;
|
||||||
|
def_audio_begin_ticket_mutex(Crunky);
|
||||||
|
if (Crunky->pending_clip_count < ArrayCount(Crunky->pending_clips))
|
||||||
|
{
|
||||||
|
Crunky->pending_clips[Crunky->pending_clip_count++] = clip;
|
||||||
|
}
|
||||||
|
def_audio_end_ticket_mutex(Crunky);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal b32
|
||||||
|
def_audio_is_playing(Audio_Control *control){
|
||||||
|
Audio_System *Crunky = &def_audio_system;
|
||||||
|
b32 result = (Crunky->generation - control->generation < 2);
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
def_audio_stop(Audio_Control *control){
|
||||||
|
Audio_System *Crunky = &def_audio_system;
|
||||||
|
def_audio_begin_ticket_mutex(Crunky);
|
||||||
|
|
||||||
|
Audio_Clip *clip = Crunky->playing_clips;
|
||||||
|
for(u32 i = 0;
|
||||||
|
i < ArrayCount(Crunky->playing_clips);
|
||||||
|
i += 1, clip += 1){
|
||||||
|
if (clip->control == control){
|
||||||
|
clip->at_sample_index = clip->sample_count;
|
||||||
|
clip->control = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
control->loop = false;
|
||||||
|
|
||||||
|
def_audio_end_ticket_mutex(Crunky);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
def_audio_mix_sources(void *ctx, f32 *mix_buffer, u32 sample_count){
|
||||||
|
Audio_System *Crunky = (Audio_System*)ctx;
|
||||||
|
def_audio_begin_ticket_mutex(Crunky);
|
||||||
|
// NOTE(casey): Move pending sounds into the playing list
|
||||||
|
{
|
||||||
|
Crunky->generation += 1;
|
||||||
|
u32 PendIndex = 0;
|
||||||
|
Audio_Clip *clip = Crunky->playing_clips;
|
||||||
|
for(u32 DestIndex = 0;
|
||||||
|
(DestIndex < ArrayCount(Crunky->playing_clips)) && (PendIndex < Crunky->pending_clip_count);
|
||||||
|
DestIndex += 1, clip += 1)
|
||||||
|
{
|
||||||
|
if (clip->at_sample_index == clip->sample_count)
|
||||||
|
{
|
||||||
|
Audio_Control *control = clip->control;
|
||||||
|
if (control == 0 || !control->loop){
|
||||||
|
*clip = Crunky->pending_clips[PendIndex++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Crunky->pending_clip_count = 0;
|
||||||
|
}
|
||||||
|
def_audio_end_ticket_mutex(Crunky);
|
||||||
|
|
||||||
|
// NOTE(casey): Mix all sounds into the output buffer
|
||||||
|
{
|
||||||
|
Audio_Clip *clip = Crunky->playing_clips;
|
||||||
|
for(u32 SoundIndex = 0;
|
||||||
|
SoundIndex < ArrayCount(Crunky->playing_clips);
|
||||||
|
SoundIndex += 1, clip += 1)
|
||||||
|
{
|
||||||
|
// NOTE(allen): Determine starting point
|
||||||
|
Audio_Control *control = clip->control;
|
||||||
|
if (control != 0 && control->loop && clip->at_sample_index == clip->sample_count){
|
||||||
|
clip->at_sample_index = 0;
|
||||||
|
}
|
||||||
|
u32 base_sample_index = clip->at_sample_index;
|
||||||
|
|
||||||
|
// NOTE(casey): Determine how many samples are left to play in this
|
||||||
|
// sound (possible none)
|
||||||
|
u32 SamplesToMix = clamp_top((clip->sample_count - clip->at_sample_index), sample_count);
|
||||||
|
clip->at_sample_index += SamplesToMix;
|
||||||
|
|
||||||
|
// NOTE(casey): Load the volume out of the control if there is one,
|
||||||
|
// and if there is, update the generation and sample index so
|
||||||
|
// external controllers can take action
|
||||||
|
f32 LeftVol = clip->channel_volume[0];
|
||||||
|
f32 RightVol = clip->channel_volume[1];
|
||||||
|
if(SamplesToMix && control != 0)
|
||||||
|
{
|
||||||
|
LeftVol *= control->channel_volume[0];
|
||||||
|
RightVol *= control->channel_volume[1];
|
||||||
|
control->generation = Crunky->generation;
|
||||||
|
control->last_played_sample_index = clip->at_sample_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(casey): Mix samples
|
||||||
|
for(u32 SampleIndex = 0;
|
||||||
|
SampleIndex < SamplesToMix;
|
||||||
|
++SampleIndex)
|
||||||
|
{
|
||||||
|
u32 src_index = 2*(base_sample_index + SampleIndex);
|
||||||
|
f32 Left = LeftVol *(f32)clip->samples[src_index + 0];
|
||||||
|
f32 Right = RightVol*(f32)clip->samples[src_index + 1];
|
||||||
|
|
||||||
|
u32 dst_index = 2*SampleIndex;
|
||||||
|
mix_buffer[dst_index + 0] += Left;
|
||||||
|
mix_buffer[dst_index + 1] += Right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
def_audio_mix_destination(i16 *dst, f32 *src, u32 sample_count){
|
||||||
|
u32 opl = sample_count*2;
|
||||||
|
for(u32 i = 0; i < opl; i += 1){
|
||||||
|
f32 sample = src[i];
|
||||||
|
f32 sat_sample = clamp(-32768.f, sample, 32767.f);
|
||||||
|
dst[i] = (i16)sat_sample;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
// NOTE(allen): Loading Clip
|
||||||
|
|
||||||
|
#if !defined(FCODER_SKIP_WAV)
|
||||||
|
#define FCODER_SKIP_WAV
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
struct wave_fmt_data
|
||||||
|
{
|
||||||
|
u16 wFormatTag;
|
||||||
|
u16 wChannels;
|
||||||
|
u32 dwSamplesPerSec;
|
||||||
|
u32 dwAvgBytesPerSec;
|
||||||
|
u16 wBlockAlign;
|
||||||
|
u16 wBitsPerSample;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct riff_header
|
||||||
|
{
|
||||||
|
u32 ID;
|
||||||
|
u32 DataSize;
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
function Audio_Clip
|
||||||
|
audio_clip_from_wav_data(String_Const_u8 data){
|
||||||
|
Audio_Clip Result = {};
|
||||||
|
|
||||||
|
if (data.size >= 4 && *(u32 *)data.str == *(u32 *)"RIFF"){
|
||||||
|
// NOTE(casey): This ROM is in WAV format
|
||||||
|
|
||||||
|
riff_header *RootHeader = (riff_header *)data.str;
|
||||||
|
|
||||||
|
wave_fmt_data *Format = 0;
|
||||||
|
u32 SampleDataSize = 0;
|
||||||
|
i16 *Samples = 0;
|
||||||
|
|
||||||
|
u32 At = sizeof(riff_header);
|
||||||
|
u32 LastAt = At + ((RootHeader->DataSize + 1) & ~1);
|
||||||
|
if ((*(u32 *)(data.str + At) == *(u32 *)"WAVE") &&
|
||||||
|
(LastAt <= data.size)){
|
||||||
|
At += sizeof(u32);
|
||||||
|
while (At < LastAt){
|
||||||
|
riff_header *Header = (riff_header *)(data.str + At);
|
||||||
|
u32 DataAt = At + sizeof(riff_header);
|
||||||
|
u32 EndAt = DataAt + ((Header->DataSize + 1) & ~1);
|
||||||
|
if(EndAt <= data.size)
|
||||||
|
{
|
||||||
|
void *Data = (data.str + DataAt);
|
||||||
|
if(Header->ID == *(u32 *)"fmt ")
|
||||||
|
{
|
||||||
|
Format = (wave_fmt_data *)Data;
|
||||||
|
}
|
||||||
|
else if(Header->ID == *(u32 *)"data")
|
||||||
|
{
|
||||||
|
SampleDataSize = Header->DataSize;
|
||||||
|
Samples = (i16 *)Data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
At = EndAt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Format &&
|
||||||
|
Samples &&
|
||||||
|
(Format->wFormatTag == 1) &&
|
||||||
|
(Format->wChannels == 2) &&
|
||||||
|
(Format->wBitsPerSample == 16) &&
|
||||||
|
(Format->dwSamplesPerSec == 48000)){
|
||||||
|
for (u32 i = 0; i < 2; i += 1){
|
||||||
|
Result.channel_volume[i] = 1.f;
|
||||||
|
}
|
||||||
|
Result.sample_count = SampleDataSize / (Format->wChannels*Format->wBitsPerSample/8);
|
||||||
|
Result.samples = (i16 *)Samples;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
// TODO(casey): This is where you would output an error - to 4coder somehow?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
// TODO(casey): This is where you would output an error - to 4coder somehow?
|
||||||
|
}
|
||||||
|
|
||||||
|
return(Result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Audio_Clip
|
||||||
|
audio_clip_from_wav_FILE(Arena *arena, FILE *file){
|
||||||
|
String_Const_u8 data = data_from_file(arena, file);
|
||||||
|
Audio_Clip result = audio_clip_from_wav_data(data);
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Audio_Clip
|
||||||
|
audio_clip_from_wav_file_name(Arena *arena, char *file_name){
|
||||||
|
Audio_Clip result = {};
|
||||||
|
String_Const_u8 data = {};
|
||||||
|
FILE *file = fopen(file_name, "rb");
|
||||||
|
if (file != 0){
|
||||||
|
result = audio_clip_from_wav_FILE(arena, file);
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
/* date = November 23rd 2020 1:18 pm */
|
||||||
|
|
||||||
|
#ifndef FCODER_AUDIO_H
|
||||||
|
#define FCODER_AUDIO_H
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
// NOTE(allen): Default Mixer Types
|
||||||
|
|
||||||
|
struct Audio_Control{
|
||||||
|
volatile f32 channel_volume[2];
|
||||||
|
volatile u32 generation;
|
||||||
|
volatile u32 last_played_sample_index;
|
||||||
|
volatile b32 loop;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Audio_Clip{
|
||||||
|
i16 *samples;
|
||||||
|
Audio_Control *control;
|
||||||
|
f32 channel_volume[2];
|
||||||
|
|
||||||
|
u32 sample_count;
|
||||||
|
u32 at_sample_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Audio_System{
|
||||||
|
volatile u32 quit;
|
||||||
|
volatile u32 ticket;
|
||||||
|
volatile u32 serving;
|
||||||
|
volatile u32 generation;
|
||||||
|
|
||||||
|
Audio_Clip playing_clips[64];
|
||||||
|
|
||||||
|
// NOTE(casey): Requests to play sounds are written to a pending array to avoid long locking
|
||||||
|
volatile u32 pending_clip_count;
|
||||||
|
Audio_Clip pending_clips[64];
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
// NOTE(allen): Default Mixer
|
||||||
|
|
||||||
|
function void def_audio_init(void);
|
||||||
|
function void def_audio_play_clip(Audio_Clip clip, Audio_Control *control);
|
||||||
|
function b32 def_audio_is_playing(Audio_Control *control);
|
||||||
|
function void def_audio_stop(Audio_Control *control);
|
||||||
|
|
||||||
|
function void def_audio_mix_sources(void *ctx, f32 *mix_buffer, u32 sample_count);
|
||||||
|
function void def_audio_mix_destination(i16 *dst, f32 *src, u32 sample_count);
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
// NOTE(allen): Loading Clip
|
||||||
|
|
||||||
|
function Audio_Clip audio_clip_from_wav_data(String_Const_u8 data);
|
||||||
|
function Audio_Clip audio_clip_from_wav_FILE(Arena *arena, FILE *file);
|
||||||
|
function Audio_Clip audio_clip_from_wav_file_name(Arena *arena, char *file_name);
|
||||||
|
|
||||||
|
#endif //4CODER_AUDIO_H
|
|
@ -0,0 +1,489 @@
|
||||||
|
/*
|
||||||
|
4coder_auto_indent.cpp - Commands for automatic indentation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
internal Batch_Edit*
|
||||||
|
make_batch_from_indentations(Application_Links *app, Arena *arena, Buffer_ID buffer, Range_i64 lines, i64 *indentations, Indent_Flag flags, i32 tab_width){
|
||||||
|
i64 *shifted_indentations = indentations - lines.first;
|
||||||
|
|
||||||
|
Batch_Edit *batch_first = 0;
|
||||||
|
Batch_Edit *batch_last = 0;
|
||||||
|
|
||||||
|
for (i64 line_number = lines.first;
|
||||||
|
line_number <= lines.max;
|
||||||
|
++line_number){
|
||||||
|
i64 line_start_pos = get_line_start_pos(app, buffer, line_number);
|
||||||
|
Indent_Info indent_info = get_indent_info_line_number_and_start(app, buffer, line_number, line_start_pos, tab_width);
|
||||||
|
|
||||||
|
i64 correct_indentation = shifted_indentations[line_number];
|
||||||
|
if (indent_info.is_blank && HasFlag(flags, Indent_ClearLine)){
|
||||||
|
correct_indentation = 0;
|
||||||
|
}
|
||||||
|
if (correct_indentation <= -1){
|
||||||
|
correct_indentation = indent_info.indent_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (correct_indentation != indent_info.indent_pos){
|
||||||
|
u64 str_size = 0;
|
||||||
|
u8 *str = 0;
|
||||||
|
if (HasFlag(flags, Indent_UseTab)){
|
||||||
|
i64 tab_count = correct_indentation/tab_width;
|
||||||
|
i64 indent = tab_count*tab_width;
|
||||||
|
i64 space_count = correct_indentation - indent;
|
||||||
|
str_size = tab_count + space_count;
|
||||||
|
str = push_array(arena, u8, str_size);
|
||||||
|
block_fill_u8(str, tab_count, '\t');
|
||||||
|
block_fill_u8(str + tab_count, space_count, ' ');
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
str_size = correct_indentation;
|
||||||
|
str = push_array(arena, u8, str_size);
|
||||||
|
block_fill_u8(str, str_size, ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
Batch_Edit *batch = push_array(arena, Batch_Edit, 1);
|
||||||
|
sll_queue_push(batch_first, batch_last, batch);
|
||||||
|
batch->edit.text = SCu8(str, str_size);
|
||||||
|
batch->edit.range = Ii64(line_start_pos, indent_info.first_char_pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return(batch_first);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
set_line_indents(Application_Links *app, Arena *arena, Buffer_ID buffer, Range_i64 lines, i64 *indentations, Indent_Flag flags, i32 tab_width){
|
||||||
|
Batch_Edit *batch = make_batch_from_indentations(app, arena, buffer, lines, indentations, flags, tab_width);
|
||||||
|
if (batch != 0){
|
||||||
|
buffer_batch_edit(app, buffer, batch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Token*
|
||||||
|
find_anchor_token(Application_Links *app, Buffer_ID buffer, Token_Array *tokens, i64 invalid_line){
|
||||||
|
ProfileScope(app, "find anchor token");
|
||||||
|
Token *result = 0;
|
||||||
|
|
||||||
|
if (tokens != 0 && tokens->tokens != 0){
|
||||||
|
result = tokens->tokens;
|
||||||
|
i64 invalid_pos = get_line_start_pos(app, buffer, invalid_line);
|
||||||
|
i32 scope_counter = 0;
|
||||||
|
i32 paren_counter = 0;
|
||||||
|
Token *token = tokens->tokens;
|
||||||
|
for (;;token += 1){
|
||||||
|
if (token->pos + token->size > invalid_pos){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!HasFlag(token->flags, TokenBaseFlag_PreprocessorBody)){
|
||||||
|
if (scope_counter == 0 && paren_counter == 0){
|
||||||
|
result = token;
|
||||||
|
}
|
||||||
|
switch (token->kind){
|
||||||
|
case TokenBaseKind_ScopeOpen:
|
||||||
|
{
|
||||||
|
scope_counter += 1;
|
||||||
|
}break;
|
||||||
|
case TokenBaseKind_ScopeClose:
|
||||||
|
{
|
||||||
|
paren_counter = 0;
|
||||||
|
if (scope_counter > 0){
|
||||||
|
scope_counter -= 1;
|
||||||
|
}
|
||||||
|
}break;
|
||||||
|
case TokenBaseKind_ParentheticalOpen:
|
||||||
|
{
|
||||||
|
paren_counter += 1;
|
||||||
|
}break;
|
||||||
|
case TokenBaseKind_ParentheticalClose:
|
||||||
|
{
|
||||||
|
if (paren_counter > 0){
|
||||||
|
paren_counter -= 1;
|
||||||
|
}
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Nest*
|
||||||
|
indent__new_nest(Arena *arena, Nest_Alloc *alloc){
|
||||||
|
Nest *new_nest = alloc->free_nest;
|
||||||
|
if (new_nest == 0){
|
||||||
|
new_nest = push_array(arena, Nest, 1);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
sll_stack_pop(alloc->free_nest);
|
||||||
|
}
|
||||||
|
return(new_nest);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
indent__free_nest(Nest_Alloc *alloc, Nest *nest){
|
||||||
|
sll_stack_push(alloc->free_nest, nest);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal b32
|
||||||
|
indent__unfinished_statement(Token *token, Nest *current_nest){
|
||||||
|
b32 result = false;
|
||||||
|
if (current_nest != 0 && current_nest->kind == TokenBaseKind_ScopeOpen){
|
||||||
|
result = true;
|
||||||
|
switch (token->kind){
|
||||||
|
case TokenBaseKind_ScopeOpen:
|
||||||
|
case TokenBaseKind_ScopeClose:
|
||||||
|
case TokenBaseKind_StatementClose:
|
||||||
|
{
|
||||||
|
result = false;
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
if (HasFlag(token->flags, TokenBaseFlag_PreprocessorBody)){
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
line_indent_cache_update(Application_Links *app, Buffer_ID buffer, i32 tab_width, Indent_Line_Cache *line_cache){
|
||||||
|
if (line_cache->line_number_for_cached_indent != line_cache->where_token_starts){
|
||||||
|
ProfileScope(app, "get indent info");
|
||||||
|
line_cache->line_number_for_cached_indent = line_cache->where_token_starts;
|
||||||
|
line_cache->start_pos = get_line_start_pos(app, buffer, line_cache->where_token_starts);
|
||||||
|
Range_i64 range = Ii64(line_cache->start_pos, line_cache->one_past_last_pos);
|
||||||
|
line_cache->indent_info = get_indent_info_range(app, buffer, range, tab_width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal i64*
|
||||||
|
get_indentation_array(Application_Links *app, Arena *arena, Buffer_ID buffer, Range_i64 lines, Indent_Flag flags, i32 tab_width, i32 indent_width){
|
||||||
|
ProfileScope(app, "get indentation array");
|
||||||
|
i64 count = lines.max - lines.min + 1;
|
||||||
|
i64 *indentations = push_array(arena, i64, count);
|
||||||
|
i64 *shifted_indentations = indentations - lines.first;
|
||||||
|
block_fill_u64(indentations, sizeof(*indentations)*count, (u64)(-1));
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
Managed_Scope scope = buffer_get_managed_scope(app, buffer);
|
||||||
|
Token_Array *tokens = scope_attachment(app, scope, attachment_tokens, Token_Array);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Token_Array token_array = get_token_array_from_buffer(app, buffer);
|
||||||
|
Token_Array *tokens = &token_array;
|
||||||
|
|
||||||
|
i64 anchor_line = clamp_bot(1, lines.first - 1);
|
||||||
|
Token *anchor_token = find_anchor_token(app, buffer, tokens, anchor_line);
|
||||||
|
if (anchor_token != 0 &&
|
||||||
|
anchor_token >= tokens->tokens &&
|
||||||
|
anchor_token < tokens->tokens + tokens->count){
|
||||||
|
i64 line = get_line_number_from_pos(app, buffer, anchor_token->pos);
|
||||||
|
line = clamp_top(line, lines.first);
|
||||||
|
|
||||||
|
Token_Iterator_Array token_it = token_iterator(0, tokens, anchor_token);
|
||||||
|
|
||||||
|
Scratch_Block scratch(app, arena);
|
||||||
|
Nest *nest = 0;
|
||||||
|
Nest_Alloc nest_alloc = {};
|
||||||
|
|
||||||
|
i64 line_last_indented = line - 1;
|
||||||
|
i64 last_indent = 0;
|
||||||
|
i64 actual_indent = 0;
|
||||||
|
b32 in_unfinished_statement = false;
|
||||||
|
|
||||||
|
Indent_Line_Cache line_cache = {};
|
||||||
|
|
||||||
|
for (;;){
|
||||||
|
Token *token = token_it_read(&token_it);
|
||||||
|
|
||||||
|
if (line_cache.where_token_starts == 0 ||
|
||||||
|
token->pos >= line_cache.one_past_last_pos){
|
||||||
|
ProfileScope(app, "get line number");
|
||||||
|
line_cache.where_token_starts = get_line_number_from_pos(app, buffer, token->pos);
|
||||||
|
line_cache.one_past_last_pos = get_line_end_pos(app, buffer, line_cache.where_token_starts);
|
||||||
|
}
|
||||||
|
|
||||||
|
i64 current_indent = 0;
|
||||||
|
if (nest != 0){
|
||||||
|
current_indent = nest->indent;
|
||||||
|
}
|
||||||
|
i64 this_indent = current_indent;
|
||||||
|
i64 following_indent = current_indent;
|
||||||
|
|
||||||
|
b32 shift_by_actual_indent = false;
|
||||||
|
b32 ignore_unfinished_statement = false;
|
||||||
|
if (HasFlag(token->flags, TokenBaseFlag_PreprocessorBody)){
|
||||||
|
this_indent = 0;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
switch (token->kind){
|
||||||
|
case TokenBaseKind_ScopeOpen:
|
||||||
|
{
|
||||||
|
Nest *new_nest = indent__new_nest(arena, &nest_alloc);
|
||||||
|
sll_stack_push(nest, new_nest);
|
||||||
|
nest->kind = TokenBaseKind_ScopeOpen;
|
||||||
|
nest->indent = current_indent + indent_width;
|
||||||
|
following_indent = nest->indent;
|
||||||
|
ignore_unfinished_statement = true;
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case TokenBaseKind_ScopeClose:
|
||||||
|
{
|
||||||
|
for (;nest != 0 && nest->kind != TokenBaseKind_ScopeOpen;){
|
||||||
|
Nest *n = nest;
|
||||||
|
sll_stack_pop(nest);
|
||||||
|
indent__free_nest(&nest_alloc, n);
|
||||||
|
}
|
||||||
|
if (nest != 0 && nest->kind == TokenBaseKind_ScopeOpen){
|
||||||
|
Nest *n = nest;
|
||||||
|
sll_stack_pop(nest);
|
||||||
|
indent__free_nest(&nest_alloc, n);
|
||||||
|
}
|
||||||
|
this_indent = 0;
|
||||||
|
if (nest != 0){
|
||||||
|
this_indent = nest->indent;
|
||||||
|
}
|
||||||
|
following_indent = this_indent;
|
||||||
|
ignore_unfinished_statement = true;
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case TokenBaseKind_ParentheticalOpen:
|
||||||
|
{
|
||||||
|
Nest *new_nest = indent__new_nest(arena, &nest_alloc);
|
||||||
|
sll_stack_push(nest, new_nest);
|
||||||
|
nest->kind = TokenBaseKind_ParentheticalOpen;
|
||||||
|
line_indent_cache_update(app, buffer, tab_width, &line_cache);
|
||||||
|
nest->indent = (token->pos - line_cache.indent_info.first_char_pos) + 1;
|
||||||
|
following_indent = nest->indent;
|
||||||
|
shift_by_actual_indent = true;
|
||||||
|
ignore_unfinished_statement = true;
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case TokenBaseKind_ParentheticalClose:
|
||||||
|
{
|
||||||
|
if (nest != 0 && nest->kind == TokenBaseKind_ParentheticalOpen){
|
||||||
|
Nest *n = nest;
|
||||||
|
sll_stack_pop(nest);
|
||||||
|
indent__free_nest(&nest_alloc, n);
|
||||||
|
}
|
||||||
|
following_indent = 0;
|
||||||
|
if (nest != 0){
|
||||||
|
following_indent = nest->indent;
|
||||||
|
}
|
||||||
|
//ignore_unfinished_statement = true;
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token->sub_kind == TokenCppKind_BlockComment ||
|
||||||
|
token->sub_kind == TokenCppKind_LiteralStringRaw){
|
||||||
|
ignore_unfinished_statement = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_unfinished_statement && !ignore_unfinished_statement){
|
||||||
|
this_indent += indent_width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define EMIT(N) \
|
||||||
|
Stmnt(if (lines.first <= line_it){shifted_indentations[line_it]=N;} \
|
||||||
|
if (line_it == lines.end){goto finished;} \
|
||||||
|
actual_indent = N; )
|
||||||
|
|
||||||
|
i64 line_it = line_last_indented;
|
||||||
|
if (lines.first <= line_cache.where_token_starts){
|
||||||
|
for (;line_it < line_cache.where_token_starts;){
|
||||||
|
line_it += 1;
|
||||||
|
if (line_it == line_cache.where_token_starts){
|
||||||
|
EMIT(this_indent);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
EMIT(last_indent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
actual_indent = this_indent;
|
||||||
|
line_it = line_cache.where_token_starts;
|
||||||
|
}
|
||||||
|
|
||||||
|
i64 line_where_token_ends = get_line_number_from_pos(app, buffer, token->pos + token->size);
|
||||||
|
if (lines.first <= line_where_token_ends){
|
||||||
|
line_indent_cache_update(app, buffer, tab_width, &line_cache);
|
||||||
|
i64 line_where_token_starts_shift = this_indent - line_cache.indent_info.indent_pos;
|
||||||
|
for (;line_it < line_where_token_ends;){
|
||||||
|
line_it += 1;
|
||||||
|
i64 line_it_start_pos = get_line_start_pos(app, buffer, line_it);
|
||||||
|
Indent_Info line_it_indent_info = get_indent_info_line_number_and_start(app, buffer, line_it, line_it_start_pos, tab_width);
|
||||||
|
i64 new_indent = line_it_indent_info.indent_pos + line_where_token_starts_shift;
|
||||||
|
new_indent = clamp_bot(0, new_indent);
|
||||||
|
EMIT(new_indent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
line_it = line_where_token_ends;
|
||||||
|
}
|
||||||
|
#undef EMIT
|
||||||
|
|
||||||
|
if (shift_by_actual_indent){
|
||||||
|
nest->indent += actual_indent;
|
||||||
|
following_indent += actual_indent;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token->kind != TokenBaseKind_Comment){
|
||||||
|
in_unfinished_statement = indent__unfinished_statement(token, nest);
|
||||||
|
if (in_unfinished_statement){
|
||||||
|
following_indent += indent_width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
last_indent = following_indent;
|
||||||
|
line_last_indented = line_it;
|
||||||
|
|
||||||
|
if (!token_it_inc_non_whitespace(&token_it)){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
finished:;
|
||||||
|
return(indentations);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal b32
|
||||||
|
auto_indent_buffer(Application_Links *app, Buffer_ID buffer, Range_i64 pos, Indent_Flag flags, i32 tab_width, i32 indent_width){
|
||||||
|
ProfileScope(app, "auto indent buffer");
|
||||||
|
Token_Array token_array = get_token_array_from_buffer(app, buffer);
|
||||||
|
Token_Array *tokens = &token_array;
|
||||||
|
|
||||||
|
b32 result = false;
|
||||||
|
if (tokens->tokens != 0){
|
||||||
|
result = true;
|
||||||
|
|
||||||
|
Scratch_Block scratch(app);
|
||||||
|
Range_i64 line_numbers = {};
|
||||||
|
if (HasFlag(flags, Indent_FullTokens)){
|
||||||
|
i32 safety_counter = 0;
|
||||||
|
for (;;){
|
||||||
|
Range_i64 expanded = enclose_tokens(app, buffer, pos);
|
||||||
|
expanded = enclose_whole_lines(app, buffer, expanded);
|
||||||
|
if (expanded == pos){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pos = expanded;
|
||||||
|
safety_counter += 1;
|
||||||
|
if (safety_counter == 20){
|
||||||
|
pos = buffer_range(app, buffer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
line_numbers = get_line_range_from_pos_range(app, buffer, pos);
|
||||||
|
|
||||||
|
i64 *indentations = get_indentation_array(app, scratch, buffer, line_numbers, flags, tab_width, indent_width);
|
||||||
|
set_line_indents(app, scratch, buffer, line_numbers, indentations, flags, tab_width);
|
||||||
|
}
|
||||||
|
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
auto_indent_buffer(Application_Links *app, Buffer_ID buffer, Range_i64 pos, Indent_Flag flags){
|
||||||
|
i32 indent_width = (i32)def_get_config_u64(app, vars_save_string_lit("indent_width"));
|
||||||
|
i32 tab_width = (i32)def_get_config_u64(app, vars_save_string_lit("default_tab_width"));
|
||||||
|
tab_width = clamp_bot(1, tab_width);
|
||||||
|
AddFlag(flags, Indent_FullTokens);
|
||||||
|
b32 indent_with_tabs = def_get_config_b32(vars_save_string_lit("indent_with_tabs"));
|
||||||
|
if (indent_with_tabs){
|
||||||
|
AddFlag(flags, Indent_UseTab);
|
||||||
|
}
|
||||||
|
auto_indent_buffer(app, buffer, pos, flags, indent_width, tab_width);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
auto_indent_buffer(Application_Links *app, Buffer_ID buffer, Range_i64 pos){
|
||||||
|
auto_indent_buffer(app, buffer, pos, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
CUSTOM_COMMAND_SIG(auto_indent_whole_file)
|
||||||
|
CUSTOM_DOC("Audo-indents the entire current buffer.")
|
||||||
|
{
|
||||||
|
View_ID view = get_active_view(app, Access_ReadWriteVisible);
|
||||||
|
Buffer_ID buffer = view_get_buffer(app, view, Access_ReadWriteVisible);
|
||||||
|
i64 buffer_size = buffer_get_size(app, buffer);
|
||||||
|
auto_indent_buffer(app, buffer, Ii64(0, buffer_size));
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_COMMAND_SIG(auto_indent_line_at_cursor)
|
||||||
|
CUSTOM_DOC("Auto-indents the line on which the cursor sits.")
|
||||||
|
{
|
||||||
|
View_ID view = get_active_view(app, Access_ReadWriteVisible);
|
||||||
|
Buffer_ID buffer = view_get_buffer(app, view, Access_ReadWriteVisible);
|
||||||
|
i64 pos = view_get_cursor_pos(app, view);
|
||||||
|
auto_indent_buffer(app, buffer, Ii64(pos));
|
||||||
|
move_past_lead_whitespace(app, view, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_COMMAND_SIG(auto_indent_range)
|
||||||
|
CUSTOM_DOC("Auto-indents the range between the cursor and the mark.")
|
||||||
|
{
|
||||||
|
View_ID view = get_active_view(app, Access_ReadWriteVisible);
|
||||||
|
Buffer_ID buffer = view_get_buffer(app, view, Access_ReadWriteVisible);
|
||||||
|
Range_i64 range = get_view_range(app, view);
|
||||||
|
auto_indent_buffer(app, buffer, range);
|
||||||
|
move_past_lead_whitespace(app, view, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_COMMAND_SIG(write_text_and_auto_indent)
|
||||||
|
CUSTOM_DOC("Inserts text and auto-indents the line on which the cursor sits if any of the text contains 'layout punctuation' such as ;:{}()[]# and new lines.")
|
||||||
|
{
|
||||||
|
ProfileScope(app, "write and auto indent");
|
||||||
|
User_Input in = get_current_input(app);
|
||||||
|
String_Const_u8 insert = to_writable(&in);
|
||||||
|
if (insert.str != 0 && insert.size > 0){
|
||||||
|
b32 do_auto_indent = false;
|
||||||
|
for (u64 i = 0; !do_auto_indent && i < insert.size; i += 1){
|
||||||
|
switch (insert.str[i]){
|
||||||
|
case ';': case ':':
|
||||||
|
case '{': case '}':
|
||||||
|
case '(': case ')':
|
||||||
|
case '[': case ']':
|
||||||
|
case '#':
|
||||||
|
case '\n': case '\t':
|
||||||
|
{
|
||||||
|
do_auto_indent = true;
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (do_auto_indent){
|
||||||
|
View_ID view = get_active_view(app, Access_ReadWriteVisible);
|
||||||
|
Buffer_ID buffer = view_get_buffer(app, view, Access_ReadWriteVisible);
|
||||||
|
|
||||||
|
Range_i64 pos = {};
|
||||||
|
if (view_has_highlighted_range(app, view)){
|
||||||
|
pos = get_view_range(app, view);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
pos.min = pos.max = view_get_cursor_pos(app, view);
|
||||||
|
}
|
||||||
|
|
||||||
|
write_text_input(app);
|
||||||
|
|
||||||
|
i64 end_pos = view_get_cursor_pos(app, view);
|
||||||
|
pos.min = Min(pos.min, end_pos);
|
||||||
|
pos.max = Max(pos.max, end_pos);
|
||||||
|
|
||||||
|
auto_indent_buffer(app, buffer, pos, 0);
|
||||||
|
move_past_lead_whitespace(app, view, buffer);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
write_text_input(app);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
4coder_auto_indent.h - Auto-indentation types.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#if !defined(FCODER_AUTO_INDENT_H)
|
||||||
|
#define FCODER_AUTO_INDENT_H
|
||||||
|
|
||||||
|
typedef u32 Indent_Flag;
|
||||||
|
enum{
|
||||||
|
Indent_ClearLine = 0x1,
|
||||||
|
Indent_UseTab = 0x2,
|
||||||
|
Indent_FullTokens = 0x4,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Nest{
|
||||||
|
Nest *next;
|
||||||
|
Token_Base_Kind kind;
|
||||||
|
i64 indent;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Nest_Alloc{
|
||||||
|
Nest *free_nest;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Indent_Line_Cache{
|
||||||
|
i64 where_token_starts;
|
||||||
|
i64 line_number_for_cached_indent;
|
||||||
|
i64 start_pos;
|
||||||
|
i64 one_past_last_pos;
|
||||||
|
Indent_Info indent_info;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Buffer seek descriptor constructors.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
static Buffer_Seek
|
||||||
|
seek_pos(i64 pos){
|
||||||
|
Buffer_Seek result;
|
||||||
|
result.type = buffer_seek_pos;
|
||||||
|
result.pos = pos;
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Buffer_Seek
|
||||||
|
seek_line_col(i64 line, i64 col){
|
||||||
|
Buffer_Seek result;
|
||||||
|
result.type = buffer_seek_line_col;
|
||||||
|
result.line = line;
|
||||||
|
result.col = col;
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,191 @@
|
||||||
|
/*
|
||||||
|
4coder_build_commands.cpp - Commands for building.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
static String_Const_u8
|
||||||
|
push_build_directory_at_file(Application_Links *app, Arena *arena, Buffer_ID buffer){
|
||||||
|
String_Const_u8 result = {};
|
||||||
|
String_Const_u8 file_name = push_buffer_file_name(app, arena, buffer);
|
||||||
|
Temp_Memory restore_point = begin_temp(arena);
|
||||||
|
String_Const_u8 base_name = push_buffer_base_name(app, arena, buffer);
|
||||||
|
b32 is_match = string_match(file_name, base_name);
|
||||||
|
end_temp(restore_point);
|
||||||
|
if (!is_match){
|
||||||
|
result = push_string_copy(arena, string_remove_last_folder(file_name));
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if OS_WINDOWS
|
||||||
|
|
||||||
|
global String_Const_u8 standard_build_file_name_array[] = {
|
||||||
|
str8_lit("build.bat"),
|
||||||
|
};
|
||||||
|
global String_Const_u8 standard_build_cmd_string_array[] = {
|
||||||
|
str8_lit("build"),
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif OS_LINUX || OS_MAC
|
||||||
|
|
||||||
|
global String_Const_u8 standard_build_file_name_array[] = {
|
||||||
|
str8_lit("build.sh"),
|
||||||
|
str8_lit("Makefile"),
|
||||||
|
};
|
||||||
|
global String_Const_u8 standard_build_cmd_string_array[] = {
|
||||||
|
str8_lit("build.sh"),
|
||||||
|
str8_lit("make"),
|
||||||
|
};
|
||||||
|
|
||||||
|
#else
|
||||||
|
#error OS needs standard search and build rules
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static String_Const_u8
|
||||||
|
push_fallback_command(Arena *arena, String_Const_u8 file_name){
|
||||||
|
return(push_u8_stringf(arena, "echo could not find %.*s", string_expand(file_name)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static String_Const_u8
|
||||||
|
push_fallback_command(Arena *arena){
|
||||||
|
return(push_fallback_command(arena, standard_build_file_name_array[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
global_const Buffer_Identifier standard_build_build_buffer_identifier = buffer_identifier(string_u8_litexpr("*compilation*"));
|
||||||
|
|
||||||
|
global_const u32 standard_build_exec_flags = CLI_OverlapWithConflict|CLI_SendEndSignal;
|
||||||
|
|
||||||
|
static void
|
||||||
|
standard_build_exec_command(Application_Links *app, View_ID view, String_Const_u8 dir, String_Const_u8 cmd){
|
||||||
|
exec_system_command(app, view, standard_build_build_buffer_identifier,
|
||||||
|
dir, cmd,
|
||||||
|
standard_build_exec_flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
function b32
|
||||||
|
standard_search_and_build_from_dir(Application_Links *app, View_ID view, String_Const_u8 start_dir){
|
||||||
|
Scratch_Block scratch(app);
|
||||||
|
|
||||||
|
// NOTE(allen): Search
|
||||||
|
String_Const_u8 full_file_path = {};
|
||||||
|
String_Const_u8 cmd_string = {};
|
||||||
|
for (i32 i = 0; i < ArrayCount(standard_build_file_name_array); i += 1){
|
||||||
|
full_file_path = push_file_search_up_path(app, scratch, start_dir, standard_build_file_name_array[i]);
|
||||||
|
if (full_file_path.size > 0){
|
||||||
|
cmd_string = standard_build_cmd_string_array[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b32 result = (full_file_path.size > 0);
|
||||||
|
if (result){
|
||||||
|
// NOTE(allen): Build
|
||||||
|
String_Const_u8 path = string_remove_last_folder(full_file_path);
|
||||||
|
String_Const_u8 command = push_u8_stringf(scratch, "\"%.*s/%.*s\"",
|
||||||
|
string_expand(path),
|
||||||
|
string_expand(cmd_string));
|
||||||
|
b32 auto_save = def_get_config_b32(vars_save_string_lit("automatically_save_changes_on_build"));
|
||||||
|
if (auto_save){
|
||||||
|
save_all_dirty_buffers(app);
|
||||||
|
}
|
||||||
|
standard_build_exec_command(app, view, path, command);
|
||||||
|
print_message(app, push_u8_stringf(scratch, "Building with: %.*s\n",
|
||||||
|
string_expand(full_file_path)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(allen): This searches first using the active file's directory,
|
||||||
|
// then if no build script is found, it searches from 4coders hot directory.
|
||||||
|
static void
|
||||||
|
standard_search_and_build(Application_Links *app, View_ID view, Buffer_ID active_buffer){
|
||||||
|
Scratch_Block scratch(app);
|
||||||
|
b32 did_build = false;
|
||||||
|
String_Const_u8 build_dir = push_build_directory_at_file(app, scratch, active_buffer);
|
||||||
|
if (build_dir.size > 0){
|
||||||
|
did_build = standard_search_and_build_from_dir(app, view, build_dir);
|
||||||
|
}
|
||||||
|
if (!did_build){
|
||||||
|
build_dir = push_hot_directory(app, scratch);
|
||||||
|
if (build_dir.size > 0){
|
||||||
|
did_build = standard_search_and_build_from_dir(app, view, build_dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!did_build){
|
||||||
|
standard_build_exec_command(app, view,
|
||||||
|
push_hot_directory(app, scratch),
|
||||||
|
push_fallback_command(scratch));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_COMMAND_SIG(build_search)
|
||||||
|
CUSTOM_DOC("Looks for a build.bat, build.sh, or makefile in the current and parent directories. Runs the first that it finds and prints the output to *compilation*.")
|
||||||
|
{
|
||||||
|
View_ID view = get_active_view(app, Access_Always);
|
||||||
|
Buffer_ID buffer = view_get_buffer(app, view, Access_Always);
|
||||||
|
standard_search_and_build(app, view, buffer);
|
||||||
|
block_zero_struct(&prev_location);
|
||||||
|
lock_jump_buffer(app, string_u8_litexpr("*compilation*"));
|
||||||
|
}
|
||||||
|
|
||||||
|
static Buffer_ID
|
||||||
|
get_comp_buffer(Application_Links *app){
|
||||||
|
return(get_buffer_by_name(app, string_u8_litexpr("*compilation*"), Access_Always));
|
||||||
|
}
|
||||||
|
|
||||||
|
static View_ID
|
||||||
|
get_or_open_build_panel(Application_Links *app){
|
||||||
|
View_ID view = 0;
|
||||||
|
Buffer_ID buffer = get_comp_buffer(app);
|
||||||
|
if (buffer != 0){
|
||||||
|
view = get_first_view_with_buffer(app, buffer);
|
||||||
|
}
|
||||||
|
if (view == 0){
|
||||||
|
view = open_build_footer_panel(app);
|
||||||
|
}
|
||||||
|
return(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
set_fancy_compilation_buffer_font(Application_Links *app){
|
||||||
|
Scratch_Block scratch(app);
|
||||||
|
Buffer_ID buffer = get_comp_buffer(app);
|
||||||
|
Font_Load_Location font = {};
|
||||||
|
font.file_name = def_search_normal_full_path(scratch, str8_lit("fonts/Inconsolata-Regular.ttf"));
|
||||||
|
set_buffer_face_by_font_load_location(app, buffer, &font);
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_COMMAND_SIG(build_in_build_panel)
|
||||||
|
CUSTOM_DOC("Looks for a build.bat, build.sh, or makefile in the current and parent directories. Runs the first that it finds and prints the output to *compilation*. Puts the *compilation* buffer in a panel at the footer of the current view.")
|
||||||
|
{
|
||||||
|
View_ID view = get_active_view(app, Access_Always);
|
||||||
|
Buffer_ID buffer = view_get_buffer(app, view, Access_Always);
|
||||||
|
|
||||||
|
View_ID build_view = get_or_open_build_panel(app);
|
||||||
|
|
||||||
|
standard_search_and_build(app, build_view, buffer);
|
||||||
|
set_fancy_compilation_buffer_font(app);
|
||||||
|
|
||||||
|
block_zero_struct(&prev_location);
|
||||||
|
lock_jump_buffer(app, string_u8_litexpr("*compilation*"));
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_COMMAND_SIG(close_build_panel)
|
||||||
|
CUSTOM_DOC("If the special build panel is open, closes it.")
|
||||||
|
{
|
||||||
|
close_build_footer_panel(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_COMMAND_SIG(change_to_build_panel)
|
||||||
|
CUSTOM_DOC("If the special build panel is open, makes the build panel the active panel.")
|
||||||
|
{
|
||||||
|
View_ID view = get_or_open_build_panel(app);
|
||||||
|
if (view != 0){
|
||||||
|
view_set_active(app, view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
/*
|
||||||
|
4coder_build_commands.h - Commands for building types.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#if !defined(FCODER_BUILD_COMMANDS_H)
|
||||||
|
#define FCODER_BUILD_COMMANDS_H
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// BOTTOM
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
4coder_system_command.cpp - Commands for executing arbitrary system command line instructions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
CUSTOM_COMMAND_SIG(execute_previous_cli)
|
||||||
|
CUSTOM_DOC("If the command execute_any_cli has already been used, this will execute a CLI reusing the most recent buffer name and command.")
|
||||||
|
{
|
||||||
|
String_Const_u8 out_buffer = SCu8(out_buffer_space);
|
||||||
|
String_Const_u8 cmd = SCu8(command_space);
|
||||||
|
String_Const_u8 hot_directory = SCu8(hot_directory_space);
|
||||||
|
|
||||||
|
if (out_buffer.size > 0 && cmd.size > 0 && hot_directory.size > 0){
|
||||||
|
View_ID view = get_active_view(app, Access_Always);
|
||||||
|
Buffer_Identifier id = buffer_identifier(out_buffer);
|
||||||
|
exec_system_command(app, view, id, hot_directory, cmd, CLI_OverlapWithConflict|CLI_CursorAtEnd|CLI_SendEndSignal);
|
||||||
|
lock_jump_buffer(app, out_buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_COMMAND_SIG(execute_any_cli)
|
||||||
|
CUSTOM_DOC("Queries for an output buffer name and system command, runs the system command as a CLI and prints the output to the specified buffer."){
|
||||||
|
Scratch_Block scratch(app);
|
||||||
|
Query_Bar_Group group(app);
|
||||||
|
|
||||||
|
Query_Bar bar_out = {};
|
||||||
|
bar_out.prompt = string_u8_litexpr("Output Buffer: ");
|
||||||
|
bar_out.string = SCu8(out_buffer_space, (u64)0);
|
||||||
|
bar_out.string_capacity = sizeof(out_buffer_space);
|
||||||
|
if (!query_user_string(app, &bar_out)) return;
|
||||||
|
bar_out.string.size = clamp_top(bar_out.string.size, sizeof(out_buffer_space) - 1);
|
||||||
|
out_buffer_space[bar_out.string.size] = 0;
|
||||||
|
|
||||||
|
Query_Bar bar_cmd = {};
|
||||||
|
bar_cmd.prompt = string_u8_litexpr("Command: ");
|
||||||
|
bar_cmd.string = SCu8(command_space, (u64)0);
|
||||||
|
bar_cmd.string_capacity = sizeof(command_space);
|
||||||
|
if (!query_user_string(app, &bar_cmd)) return;
|
||||||
|
bar_cmd.string.size = clamp_top(bar_cmd.string.size, sizeof(command_space) - 1);
|
||||||
|
command_space[bar_cmd.string.size] = 0;
|
||||||
|
|
||||||
|
String_Const_u8 hot = push_hot_directory(app, scratch);
|
||||||
|
{
|
||||||
|
u64 size = clamp_top(hot.size, sizeof(hot_directory_space));
|
||||||
|
block_copy(hot_directory_space, hot.str, size);
|
||||||
|
hot_directory_space[hot.size] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
execute_previous_cli(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,423 @@
|
||||||
|
/*
|
||||||
|
4coder_clipboard.cpp - Copy paste commands and clipboard related setup.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
CUSTOM_COMMAND_SIG(clipboard_record_clip)
|
||||||
|
CUSTOM_DOC("In response to a new clipboard contents events, saves the new clip onto the clipboard history")
|
||||||
|
{
|
||||||
|
User_Input in = get_current_input(app);
|
||||||
|
if (in.event.kind == InputEventKind_Core &&
|
||||||
|
in.event.core.code == CoreCode_NewClipboardContents){
|
||||||
|
clipboard_post_internal_only(0, in.event.core.string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
function b32
|
||||||
|
clipboard_post_buffer_range(Application_Links *app, i32 clipboard_index, Buffer_ID buffer, Range_i64 range){
|
||||||
|
b32 success = false;
|
||||||
|
Scratch_Block scratch(app);
|
||||||
|
String_Const_u8 string = push_buffer_range(app, scratch, buffer, range);
|
||||||
|
if (string.size > 0){
|
||||||
|
clipboard_post(clipboard_index, string);
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
return(success);
|
||||||
|
}
|
||||||
|
|
||||||
|
function b32
|
||||||
|
clipboard_update_history_from_system(Application_Links *app, i32 clipboard_id){
|
||||||
|
Scratch_Block scratch(app);
|
||||||
|
b32 result = false;
|
||||||
|
String_Const_u8 string = system_get_clipboard(scratch, clipboard_id);
|
||||||
|
if (string.str != 0){
|
||||||
|
clipboard_post_internal_only(clipboard_id, string);
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
global List_String_Const_u8 clipboard_collection_list = {};
|
||||||
|
|
||||||
|
function void
|
||||||
|
clipboard_collection_render(Application_Links *app, Frame_Info frame_info, View_ID view){
|
||||||
|
Scratch_Block scratch(app);
|
||||||
|
Rect_f32 region = draw_background_and_margin(app, view);
|
||||||
|
Vec2_f32 mid_p = (region.p1 + region.p0)*0.5f;
|
||||||
|
|
||||||
|
Fancy_Block message = {};
|
||||||
|
Fancy_Line *line = push_fancy_line(scratch, &message);
|
||||||
|
push_fancy_string(scratch, line, fcolor_id(defcolor_pop2),
|
||||||
|
string_u8_litexpr("Collecting all clipboard events "));
|
||||||
|
push_fancy_string(scratch, line, fcolor_id(defcolor_pop1),
|
||||||
|
string_u8_litexpr("press [escape] to stop"));
|
||||||
|
|
||||||
|
for (Node_String_Const_u8 *node = clipboard_collection_list.first;
|
||||||
|
node != 0;
|
||||||
|
node = node->next){
|
||||||
|
line = push_fancy_line(scratch, &message);
|
||||||
|
push_fancy_string(scratch, line, fcolor_id(defcolor_text_default), node->string);
|
||||||
|
}
|
||||||
|
|
||||||
|
Face_ID face_id = get_face_id(app, 0);
|
||||||
|
Vec2_f32 dim = get_fancy_block_dim(app, face_id, &message);
|
||||||
|
Vec2_f32 half_dim = dim*0.5f;
|
||||||
|
draw_fancy_block(app, face_id, fcolor_zero(), &message, mid_p - half_dim);
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_UI_COMMAND_SIG(begin_clipboard_collection_mode)
|
||||||
|
CUSTOM_DOC("Allows the user to copy multiple strings from other applications before switching to 4coder and pasting them all.")
|
||||||
|
{
|
||||||
|
local_persist b32 in_clipboard_collection_mode = false;
|
||||||
|
if (!in_clipboard_collection_mode){
|
||||||
|
in_clipboard_collection_mode = true;
|
||||||
|
system_set_clipboard_catch_all(true);
|
||||||
|
|
||||||
|
Scratch_Block scratch(app);
|
||||||
|
block_zero_struct(&clipboard_collection_list);
|
||||||
|
|
||||||
|
View_ID view = get_this_ctx_view(app, Access_Always);
|
||||||
|
View_Context ctx = view_current_context(app, view);
|
||||||
|
ctx.render_caller = clipboard_collection_render;
|
||||||
|
ctx.hides_buffer = true;
|
||||||
|
View_Context_Block ctx_block(app, view, &ctx);
|
||||||
|
|
||||||
|
for (;;){
|
||||||
|
User_Input in = get_next_input(app, EventPropertyGroup_Any, EventProperty_Escape);
|
||||||
|
if (in.abort){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (in.event.kind == InputEventKind_KeyStroke && in.event.key.code == KeyCode_Escape){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (in.event.kind == InputEventKind_Core &&
|
||||||
|
in.event.core.code == CoreCode_NewClipboardContents){
|
||||||
|
String_Const_u8 stable_clip = clipboard_post_internal_only(0, in.event.core.string);
|
||||||
|
string_list_push(scratch, &clipboard_collection_list, stable_clip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
block_zero_struct(&clipboard_collection_list);
|
||||||
|
|
||||||
|
system_set_clipboard_catch_all(false);
|
||||||
|
in_clipboard_collection_mode = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_COMMAND_SIG(copy)
|
||||||
|
CUSTOM_DOC("Copy the text in the range from the cursor to the mark onto the clipboard.")
|
||||||
|
{
|
||||||
|
View_ID view = get_active_view(app, Access_ReadVisible);
|
||||||
|
Buffer_ID buffer = view_get_buffer(app, view, Access_ReadVisible);
|
||||||
|
Range_i64 range = get_view_range(app, view);
|
||||||
|
clipboard_post_buffer_range(app, 0, buffer, range);
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_COMMAND_SIG(cut)
|
||||||
|
CUSTOM_DOC("Cut the text in the range from the cursor to the mark onto the clipboard.")
|
||||||
|
{
|
||||||
|
View_ID view = get_active_view(app, Access_ReadWriteVisible);
|
||||||
|
Buffer_ID buffer = view_get_buffer(app, view, Access_ReadWriteVisible);
|
||||||
|
Range_i64 range = get_view_range(app, view);
|
||||||
|
if (clipboard_post_buffer_range(app, 0, buffer, range)){
|
||||||
|
buffer_replace_range(app, buffer, range, string_u8_empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_COMMAND_SIG(paste)
|
||||||
|
CUSTOM_DOC("At the cursor, insert the text at the top of the clipboard.")
|
||||||
|
{
|
||||||
|
clipboard_update_history_from_system(app, 0);
|
||||||
|
i32 count = clipboard_count(0);
|
||||||
|
if (count > 0){
|
||||||
|
View_ID view = get_active_view(app, Access_ReadWriteVisible);
|
||||||
|
if_view_has_highlighted_range_delete_range(app, view);
|
||||||
|
|
||||||
|
set_next_rewrite(app, view, Rewrite_Paste);
|
||||||
|
|
||||||
|
Managed_Scope scope = view_get_managed_scope(app, view);
|
||||||
|
i32 *paste_index = scope_attachment(app, scope, view_paste_index_loc, i32);
|
||||||
|
if (paste_index != 0){
|
||||||
|
*paste_index = 0;
|
||||||
|
|
||||||
|
Scratch_Block scratch(app);
|
||||||
|
|
||||||
|
String_Const_u8 string = push_clipboard_index(scratch, 0, *paste_index);
|
||||||
|
if (string.size > 0){
|
||||||
|
Buffer_ID buffer = view_get_buffer(app, view, Access_ReadWriteVisible);
|
||||||
|
|
||||||
|
i64 pos = view_get_cursor_pos(app, view);
|
||||||
|
buffer_replace_range(app, buffer, Ii64(pos), string);
|
||||||
|
view_set_mark(app, view, seek_pos(pos));
|
||||||
|
view_set_cursor_and_preferred_x(app, view, seek_pos(pos + (i32)string.size));
|
||||||
|
|
||||||
|
ARGB_Color argb = fcolor_resolve(fcolor_id(defcolor_paste));
|
||||||
|
buffer_post_fade(app, buffer, 0.667f, Ii64_size(pos, string.size), argb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_COMMAND_SIG(paste_next)
|
||||||
|
CUSTOM_DOC("If the previous command was paste or paste_next, replaces the paste range with the next text down on the clipboard, otherwise operates as the paste command.")
|
||||||
|
{
|
||||||
|
Scratch_Block scratch(app);
|
||||||
|
|
||||||
|
b32 new_clip = clipboard_update_history_from_system(app, 0);
|
||||||
|
|
||||||
|
i32 count = clipboard_count(0);
|
||||||
|
if (count > 0){
|
||||||
|
View_ID view = get_active_view(app, Access_ReadWriteVisible);
|
||||||
|
Managed_Scope scope = view_get_managed_scope(app, view);
|
||||||
|
|
||||||
|
Rewrite_Type *rewrite = scope_attachment(app, scope, view_rewrite_loc, Rewrite_Type);
|
||||||
|
if (rewrite != 0){
|
||||||
|
if (*rewrite == Rewrite_Paste && !new_clip){
|
||||||
|
no_mark_snap_to_cursor(app, scope);
|
||||||
|
|
||||||
|
set_next_rewrite(app, view, Rewrite_Paste);
|
||||||
|
|
||||||
|
i32 *paste_index_ptr = scope_attachment(app, scope, view_paste_index_loc, i32);
|
||||||
|
i32 paste_index = (*paste_index_ptr) + 1;
|
||||||
|
*paste_index_ptr = paste_index;
|
||||||
|
|
||||||
|
String_Const_u8 string = push_clipboard_index(scratch, 0, paste_index);
|
||||||
|
|
||||||
|
Buffer_ID buffer = view_get_buffer(app, view, Access_ReadWriteVisible);
|
||||||
|
|
||||||
|
Range_i64 range = get_view_range(app, view);
|
||||||
|
i64 pos = range.min;
|
||||||
|
|
||||||
|
buffer_replace_range(app, buffer, range, string);
|
||||||
|
view_set_cursor_and_preferred_x(app, view, seek_pos(pos + string.size));
|
||||||
|
|
||||||
|
ARGB_Color argb = fcolor_resolve(fcolor_id(defcolor_paste));
|
||||||
|
buffer_post_fade(app, buffer, 0.667f, Ii64_size(pos, string.size), argb);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
paste(app);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_COMMAND_SIG(paste_and_indent)
|
||||||
|
CUSTOM_DOC("Paste from the top of clipboard and run auto-indent on the newly pasted text.")
|
||||||
|
{
|
||||||
|
paste(app);
|
||||||
|
auto_indent_range(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_COMMAND_SIG(paste_next_and_indent)
|
||||||
|
CUSTOM_DOC("Paste the next item on the clipboard and run auto-indent on the newly pasted text.")
|
||||||
|
{
|
||||||
|
paste_next(app);
|
||||||
|
auto_indent_range(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_COMMAND_SIG(clear_clipboard)
|
||||||
|
CUSTOM_DOC("Clears the history of the clipboard")
|
||||||
|
{
|
||||||
|
clipboard_clear(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
CUSTOM_COMMAND_SIG(multi_paste)
|
||||||
|
CUSTOM_DOC("Paste multiple entries from the clipboard at once")
|
||||||
|
{
|
||||||
|
Scratch_Block scratch(app);
|
||||||
|
|
||||||
|
i32 count = clipboard_count(0);
|
||||||
|
if (count > 0){
|
||||||
|
View_ID view = get_active_view(app, Access_ReadWriteVisible);
|
||||||
|
Managed_Scope scope = view_get_managed_scope(app, view);
|
||||||
|
|
||||||
|
Rewrite_Type *rewrite = scope_attachment(app, scope, view_rewrite_loc, Rewrite_Type);
|
||||||
|
if (rewrite != 0){
|
||||||
|
if (*rewrite == Rewrite_Paste){
|
||||||
|
Rewrite_Type *next_rewrite = scope_attachment(app, scope, view_next_rewrite_loc, Rewrite_Type);
|
||||||
|
*next_rewrite = Rewrite_Paste;
|
||||||
|
i32 *paste_index_ptr = scope_attachment(app, scope, view_paste_index_loc, i32);
|
||||||
|
i32 paste_index = (*paste_index_ptr) + 1;
|
||||||
|
*paste_index_ptr = paste_index;
|
||||||
|
|
||||||
|
String_Const_u8 string = push_clipboard_index(scratch, 0, paste_index);
|
||||||
|
|
||||||
|
String_Const_u8 insert_string = push_u8_stringf(scratch, "\n%.*s", string_expand(string));
|
||||||
|
|
||||||
|
Buffer_ID buffer = view_get_buffer(app, view, Access_ReadWriteVisible);
|
||||||
|
Range_i64 range = get_view_range(app, view);
|
||||||
|
buffer_replace_range(app, buffer, Ii64(range.max), insert_string);
|
||||||
|
view_set_mark(app, view, seek_pos(range.max + 1));
|
||||||
|
view_set_cursor_and_preferred_x(app, view, seek_pos(range.max + insert_string.size));
|
||||||
|
|
||||||
|
ARGB_Color argb = fcolor_resolve(fcolor_id(defcolor_paste));
|
||||||
|
buffer_post_fade(app, buffer, 0.667f, Ii64(range.max + 1, range.max + insert_string.size), argb);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
paste(app);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Range_i64
|
||||||
|
multi_paste_range(Application_Links *app, View_ID view, Range_i64 range, i32 paste_count, b32 old_to_new){
|
||||||
|
Scratch_Block scratch(app);
|
||||||
|
|
||||||
|
Range_i64 finish_range = range;
|
||||||
|
if (paste_count >= 1){
|
||||||
|
Buffer_ID buffer = view_get_buffer(app, view, Access_ReadWriteVisible);
|
||||||
|
if (buffer != 0){
|
||||||
|
i64 total_size = 0;
|
||||||
|
for (i32 paste_index = 0; paste_index < paste_count; ++paste_index){
|
||||||
|
Temp_Memory temp = begin_temp(scratch);
|
||||||
|
String_Const_u8 string = push_clipboard_index(scratch, 0, paste_index);
|
||||||
|
total_size += string.size + 1;
|
||||||
|
end_temp(temp);
|
||||||
|
}
|
||||||
|
total_size -= 1;
|
||||||
|
|
||||||
|
i32 first = paste_count - 1;
|
||||||
|
i32 one_past_last = -1;
|
||||||
|
i32 step = -1;
|
||||||
|
if (!old_to_new){
|
||||||
|
first = 0;
|
||||||
|
one_past_last = paste_count;
|
||||||
|
step = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
List_String_Const_u8 list = {};
|
||||||
|
|
||||||
|
for (i32 paste_index = first; paste_index != one_past_last; paste_index += step){
|
||||||
|
if (paste_index != first){
|
||||||
|
string_list_push(scratch, &list, SCu8("\n", 1));
|
||||||
|
}
|
||||||
|
String_Const_u8 string = push_clipboard_index(scratch, 0, paste_index);
|
||||||
|
if (string.size > 0){
|
||||||
|
string_list_push(scratch, &list, string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String_Const_u8 flattened = string_list_flatten(scratch, list);
|
||||||
|
|
||||||
|
buffer_replace_range(app, buffer, range, flattened);
|
||||||
|
i64 pos = range.min;
|
||||||
|
finish_range.min = pos;
|
||||||
|
finish_range.max = pos + total_size;
|
||||||
|
view_set_mark(app, view, seek_pos(finish_range.min));
|
||||||
|
view_set_cursor_and_preferred_x(app, view, seek_pos(finish_range.max));
|
||||||
|
|
||||||
|
ARGB_Color argb = fcolor_resolve(fcolor_id(defcolor_paste));
|
||||||
|
buffer_post_fade(app, buffer, 0.667f, finish_range, argb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(finish_range);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
multi_paste_interactive_up_down(Application_Links *app, i32 paste_count, i32 clip_count){
|
||||||
|
View_ID view = get_active_view(app, Access_ReadWriteVisible);
|
||||||
|
i64 pos = view_get_cursor_pos(app, view);
|
||||||
|
b32 old_to_new = true;
|
||||||
|
Range_i64 range = multi_paste_range(app, view, Ii64(pos), paste_count, old_to_new);
|
||||||
|
|
||||||
|
Query_Bar_Group group(app);
|
||||||
|
Query_Bar bar = {};
|
||||||
|
bar.prompt = string_u8_litexpr("Up and Down to condense and expand paste stages; R to reverse order; Return to finish; Escape to abort.");
|
||||||
|
if (start_query_bar(app, &bar, 0) == 0) return;
|
||||||
|
|
||||||
|
User_Input in = {};
|
||||||
|
for (;;){
|
||||||
|
in = get_next_input(app, EventProperty_AnyKey, EventProperty_Escape);
|
||||||
|
if (in.abort) break;
|
||||||
|
|
||||||
|
b32 did_modify = false;
|
||||||
|
if (match_key_code(&in, KeyCode_Up)){
|
||||||
|
if (paste_count > 1){
|
||||||
|
--paste_count;
|
||||||
|
did_modify = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (match_key_code(&in, KeyCode_Down)){
|
||||||
|
if (paste_count < clip_count){
|
||||||
|
++paste_count;
|
||||||
|
did_modify = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (match_key_code(&in, KeyCode_R)){
|
||||||
|
old_to_new = !old_to_new;
|
||||||
|
did_modify = true;
|
||||||
|
}
|
||||||
|
else if (match_key_code(&in, KeyCode_Return)){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (did_modify){
|
||||||
|
range = multi_paste_range(app, view, range, paste_count, old_to_new);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in.abort){
|
||||||
|
Buffer_ID buffer = view_get_buffer(app, view, Access_ReadWriteVisible);
|
||||||
|
buffer_replace_range(app, buffer, range, SCu8(""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_COMMAND_SIG(multi_paste_interactive)
|
||||||
|
CUSTOM_DOC("Paste multiple lines from the clipboard history, controlled with arrow keys")
|
||||||
|
{
|
||||||
|
i32 clip_count = clipboard_count(0);
|
||||||
|
if (clip_count > 0){
|
||||||
|
multi_paste_interactive_up_down(app, 1, clip_count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_COMMAND_SIG(multi_paste_interactive_quick)
|
||||||
|
CUSTOM_DOC("Paste multiple lines from the clipboard history, controlled by inputing the number of lines to paste")
|
||||||
|
{
|
||||||
|
i32 clip_count = clipboard_count(0);
|
||||||
|
if (clip_count > 0){
|
||||||
|
u8 string_space[256];
|
||||||
|
Query_Bar_Group group(app);
|
||||||
|
Query_Bar bar = {};
|
||||||
|
bar.prompt = string_u8_litexpr("How Many Slots To Paste: ");
|
||||||
|
bar.string = SCu8(string_space, (u64)0);
|
||||||
|
bar.string_capacity = sizeof(string_space);
|
||||||
|
query_user_number(app, &bar);
|
||||||
|
|
||||||
|
i32 initial_paste_count = (i32)string_to_integer(bar.string, 10);
|
||||||
|
initial_paste_count = clamp(1, initial_paste_count, clip_count);
|
||||||
|
end_query_bar(app, &bar, 0);
|
||||||
|
|
||||||
|
multi_paste_interactive_up_down(app, initial_paste_count, clip_count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
#if FCODER_TRANSITION_TO < 4001004
|
||||||
|
function void
|
||||||
|
clipboard_clear(Application_Links *app, i32 clipboard_id){
|
||||||
|
clipboard_clear(clipboard_id);
|
||||||
|
}
|
||||||
|
function b32
|
||||||
|
clipboard_post(Application_Links *app, i32 clipboard_id, String_Const_u8 string){
|
||||||
|
return(clipboard_post(clipboard_id, string));
|
||||||
|
}
|
||||||
|
function i32
|
||||||
|
clipboard_count(Application_Links *app, i32 clipboard_id){
|
||||||
|
return(clipboard_count(clipboard_id));
|
||||||
|
}
|
||||||
|
function String_Const_u8
|
||||||
|
push_clipboard_index(Application_Links *app, Arena *arena, i32 clipboard_id, i32 item_index){
|
||||||
|
return(push_clipboard_index(arena, clipboard_id, item_index));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
4coder_clipboard.cpp - Copy paste commands and clipboard related setup.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#ifndef FCODER_CLIPBOARD_H
|
||||||
|
#define FCODER_CLIPBOARD_H
|
||||||
|
|
||||||
|
struct Clipboard{
|
||||||
|
Arena arena;
|
||||||
|
Heap heap;
|
||||||
|
String_Const_u8 *clips;
|
||||||
|
u32 clip_index;
|
||||||
|
u32 clip_capacity;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //4CODER_CLIPBOARD_H
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,127 @@
|
||||||
|
/*
|
||||||
|
4coder_code_index.h - Generic code indexing system for layout, definition jumps, etc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#if !defined(FCODER_CODE_INDEX_H)
|
||||||
|
#define FCODER_CODE_INDEX_H
|
||||||
|
|
||||||
|
struct Code_Index_Nest_List{
|
||||||
|
struct Code_Index_Nest *first;
|
||||||
|
struct Code_Index_Nest *last;
|
||||||
|
i32 count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Code_Index_Nest_Ptr_Array{
|
||||||
|
struct Code_Index_Nest **ptrs;
|
||||||
|
i32 count;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef i32 Code_Index_Nest_Kind;
|
||||||
|
enum{
|
||||||
|
CodeIndexNest_Scope,
|
||||||
|
CodeIndexNest_Paren,
|
||||||
|
CodeIndexNest_Preprocessor,
|
||||||
|
CodeIndexNest_Statement,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Code_Index_Nest{
|
||||||
|
Code_Index_Nest *next;
|
||||||
|
|
||||||
|
Code_Index_Nest_Kind kind;
|
||||||
|
b32 is_closed;
|
||||||
|
Range_i64 open;
|
||||||
|
Range_i64 close;
|
||||||
|
|
||||||
|
struct Code_Index_File *file;
|
||||||
|
Code_Index_Nest *parent;
|
||||||
|
|
||||||
|
Code_Index_Nest_List nest_list;
|
||||||
|
Code_Index_Nest_Ptr_Array nest_array;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef i64 Code_Index_Note_Kind;
|
||||||
|
enum{
|
||||||
|
CodeIndexNote_Type,
|
||||||
|
CodeIndexNote_Function,
|
||||||
|
CodeIndexNote_Macro,
|
||||||
|
CodeIndexNote_4coderCommand,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Code_Index_Note{
|
||||||
|
Code_Index_Note *next;
|
||||||
|
Code_Index_Note_Kind note_kind;
|
||||||
|
Range_i64 pos;
|
||||||
|
String_Const_u8 text;
|
||||||
|
struct Code_Index_File *file;
|
||||||
|
Code_Index_Nest *parent;
|
||||||
|
|
||||||
|
Code_Index_Note *prev_in_hash;
|
||||||
|
Code_Index_Note *next_in_hash;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Code_Index_Note_List{
|
||||||
|
Code_Index_Note *first;
|
||||||
|
Code_Index_Note *last;
|
||||||
|
i32 count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Code_Index_Note_Ptr_Array{
|
||||||
|
Code_Index_Note **ptrs;
|
||||||
|
i32 count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Code_Index_File{
|
||||||
|
Code_Index_Nest_List nest_list;
|
||||||
|
Code_Index_Nest_Ptr_Array nest_array;
|
||||||
|
Code_Index_Note_List note_list;
|
||||||
|
Code_Index_Note_Ptr_Array note_array;
|
||||||
|
Buffer_ID buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Code_Index_File_Storage{
|
||||||
|
Code_Index_File_Storage *next;
|
||||||
|
Code_Index_File_Storage *prev;
|
||||||
|
Arena arena;
|
||||||
|
Code_Index_File *file;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Code_Index{
|
||||||
|
System_Mutex mutex;
|
||||||
|
Arena node_arena;
|
||||||
|
Table_u64_u64 buffer_to_index_file;
|
||||||
|
Code_Index_File_Storage *free_storage;
|
||||||
|
Code_Index_File_Storage *storage_first;
|
||||||
|
Code_Index_File_Storage *storage_last;
|
||||||
|
i32 storage_count;
|
||||||
|
|
||||||
|
Code_Index_Note_List name_hash[4099];
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
typedef void Generic_Parse_Comment_Function(Application_Links *app, Arena *arena, Code_Index_File *index,
|
||||||
|
Token *token, String_Const_u8 contents);
|
||||||
|
|
||||||
|
struct Generic_Parse_State{
|
||||||
|
Application_Links *app;
|
||||||
|
Arena *arena;
|
||||||
|
String_Const_u8 contents;
|
||||||
|
Token_Iterator_Array it;
|
||||||
|
Generic_Parse_Comment_Function *handle_comment;
|
||||||
|
u8 *prev_line_start;
|
||||||
|
b32 finished;
|
||||||
|
|
||||||
|
i32 scope_counter;
|
||||||
|
i32 paren_counter;
|
||||||
|
b32 in_preprocessor;
|
||||||
|
b32 in_statement;
|
||||||
|
|
||||||
|
b32 do_cpp_parse;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
/*
|
||||||
|
4coder_code_index_listers.cpp - Listers for exploring the contents of the code index.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
struct Tiny_Jump{
|
||||||
|
Buffer_ID buffer;
|
||||||
|
i64 pos;
|
||||||
|
};
|
||||||
|
|
||||||
|
CUSTOM_UI_COMMAND_SIG(jump_to_definition)
|
||||||
|
CUSTOM_DOC("List all definitions in the code index and jump to one chosen by the user.")
|
||||||
|
{
|
||||||
|
char *query = "Definition:";
|
||||||
|
|
||||||
|
Scratch_Block scratch(app);
|
||||||
|
Lister_Block lister(app, scratch);
|
||||||
|
lister_set_query(lister, query);
|
||||||
|
lister_set_default_handlers(lister);
|
||||||
|
|
||||||
|
code_index_lock();
|
||||||
|
for (Buffer_ID buffer = get_buffer_next(app, 0, Access_Always);
|
||||||
|
buffer != 0;
|
||||||
|
buffer = get_buffer_next(app, buffer, Access_Always)){
|
||||||
|
Code_Index_File *file = code_index_get_file(buffer);
|
||||||
|
if (file != 0){
|
||||||
|
for (i32 i = 0; i < file->note_array.count; i += 1){
|
||||||
|
Code_Index_Note *note = file->note_array.ptrs[i];
|
||||||
|
Tiny_Jump *jump = push_array(scratch, Tiny_Jump, 1);
|
||||||
|
jump->buffer = buffer;
|
||||||
|
jump->pos = note->pos.first;
|
||||||
|
|
||||||
|
String_Const_u8 sort = {};
|
||||||
|
switch (note->note_kind){
|
||||||
|
case CodeIndexNote_Type:
|
||||||
|
{
|
||||||
|
sort = string_u8_litexpr("type");
|
||||||
|
}break;
|
||||||
|
case CodeIndexNote_Function:
|
||||||
|
{
|
||||||
|
sort = string_u8_litexpr("function");
|
||||||
|
}break;
|
||||||
|
case CodeIndexNote_Macro:
|
||||||
|
{
|
||||||
|
sort = string_u8_litexpr("macro");
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
lister_add_item(lister, note->text, sort, jump, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
code_index_unlock();
|
||||||
|
|
||||||
|
Lister_Result l_result = run_lister(app, lister);
|
||||||
|
Tiny_Jump result = {};
|
||||||
|
if (!l_result.canceled && l_result.user_data != 0){
|
||||||
|
block_copy_struct(&result, (Tiny_Jump*)l_result.user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.buffer != 0){
|
||||||
|
View_ID view = get_this_ctx_view(app, Access_Always);
|
||||||
|
point_stack_push_view_cursor(app, view);
|
||||||
|
jump_to_location(app, view, result.buffer, result.pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_UI_COMMAND_SIG(jump_to_definition_at_cursor)
|
||||||
|
CUSTOM_DOC("Jump to the first definition in the code index matching an identifier at the cursor")
|
||||||
|
{
|
||||||
|
View_ID view = get_active_view(app, Access_Visible);
|
||||||
|
|
||||||
|
if (view != 0){
|
||||||
|
Scratch_Block scratch(app);
|
||||||
|
String_Const_u8 query = push_token_or_word_under_active_cursor(app, scratch);
|
||||||
|
|
||||||
|
code_index_lock();
|
||||||
|
for (Buffer_ID buffer = get_buffer_next(app, 0, Access_Always);
|
||||||
|
buffer != 0;
|
||||||
|
buffer = get_buffer_next(app, buffer, Access_Always)){
|
||||||
|
Code_Index_File *file = code_index_get_file(buffer);
|
||||||
|
if (file != 0){
|
||||||
|
for (i32 i = 0; i < file->note_array.count; i += 1){
|
||||||
|
Code_Index_Note *note = file->note_array.ptrs[i];
|
||||||
|
if (string_match(note->text, query)){
|
||||||
|
point_stack_push_view_cursor(app, view);
|
||||||
|
jump_to_location(app, view, buffer, note->pos.first);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
done:;
|
||||||
|
code_index_unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
4coder_codepoint_map.cpp - Codepoint map to index
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
function b32
|
||||||
|
codepoint_index_map_read(Codepoint_Index_Map *map, u32 codepoint, u16 *index_out){
|
||||||
|
b32 success = true;
|
||||||
|
if (codepoint == 0 && map->has_zero_index){
|
||||||
|
*index_out = map->zero_index;
|
||||||
|
}
|
||||||
|
else if (table_read(&map->table, codepoint, index_out)){
|
||||||
|
// NOTE(allen): do nothing
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
return(success);
|
||||||
|
}
|
||||||
|
|
||||||
|
function u16
|
||||||
|
codepoint_index_map_count(Codepoint_Index_Map *map){
|
||||||
|
return(map->max_index + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function f32
|
||||||
|
font_get_glyph_advance(Face_Advance_Map *map, Face_Metrics *metrics, u32 codepoint, f32 tab_multiplier){
|
||||||
|
f32 result = 0.f;
|
||||||
|
if (codepoint == '\t'){
|
||||||
|
result = metrics->space_advance*tab_multiplier;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
if (character_is_whitespace(codepoint)){
|
||||||
|
codepoint = ' ';
|
||||||
|
}
|
||||||
|
u16 index = 0;
|
||||||
|
if (codepoint_index_map_read(&map->codepoint_to_index, codepoint, &index)){
|
||||||
|
if (index < map->index_count){
|
||||||
|
result = map->advance[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function f32
|
||||||
|
font_get_max_glyph_advance_range(Face_Advance_Map *map, Face_Metrics *metrics,
|
||||||
|
u32 codepoint_first, u32 codepoint_last,
|
||||||
|
f32 tab_multiplier){
|
||||||
|
f32 result = font_get_glyph_advance(map, metrics, codepoint_first, tab_multiplier);
|
||||||
|
for (u32 i = codepoint_first + 1; i <= codepoint_last; i += 1){
|
||||||
|
f32 a = font_get_glyph_advance(map, metrics, i, tab_multiplier);
|
||||||
|
result = Max(a, result);
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function f32
|
||||||
|
font_get_average_glyph_advance_range(Face_Advance_Map *map, Face_Metrics *metrics,
|
||||||
|
u32 codepoint_first, u32 codepoint_last,
|
||||||
|
f32 tab_multiplier){
|
||||||
|
f32 result = 0.f;
|
||||||
|
for (u32 i = codepoint_first; i <= codepoint_last; i += 1){
|
||||||
|
result += font_get_glyph_advance(map, metrics, i, tab_multiplier);
|
||||||
|
}
|
||||||
|
result /= (f32)(codepoint_last - codepoint_first + 1);
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,253 @@
|
||||||
|
/*
|
||||||
|
4coder_combined_write_commands.cpp - Commands for writing text specialized for particular contexts.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
function void
|
||||||
|
write_string(Application_Links *app, View_ID view, Buffer_ID buffer, String_Const_u8 string){
|
||||||
|
i64 pos = view_get_cursor_pos(app, view);
|
||||||
|
buffer_replace_range(app, buffer, Ii64(pos), string);
|
||||||
|
view_set_cursor_and_preferred_x(app, view, seek_pos(pos + string.size));
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
write_string(Application_Links *app, String_Const_u8 string){
|
||||||
|
View_ID view = get_active_view(app, Access_ReadWriteVisible);
|
||||||
|
Buffer_ID buffer = view_get_buffer(app, view, Access_ReadWriteVisible);
|
||||||
|
write_string(app, view, buffer, string);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
write_named_comment_string(Application_Links *app, char *type_string){
|
||||||
|
Scratch_Block scratch(app);
|
||||||
|
String_Const_u8 name = def_get_config_string(scratch, vars_save_string_lit("user_name"));
|
||||||
|
String_Const_u8 str = {};
|
||||||
|
if (name.size > 0){
|
||||||
|
str = push_u8_stringf(scratch, "// %s(%.*s): ", type_string, string_expand(name));
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
str = push_u8_stringf(scratch, "// %s: ", type_string);
|
||||||
|
}
|
||||||
|
write_string(app, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
long_braces(Application_Links *app, char *text, i32 size){
|
||||||
|
View_ID view = get_active_view(app, Access_ReadWriteVisible);
|
||||||
|
Buffer_ID buffer = view_get_buffer(app, view, Access_ReadWriteVisible);
|
||||||
|
i64 pos = view_get_cursor_pos(app, view);
|
||||||
|
buffer_replace_range(app, buffer, Ii64(pos), SCu8(text, size));
|
||||||
|
view_set_cursor_and_preferred_x(app, view, seek_pos(pos + 2));
|
||||||
|
auto_indent_buffer(app, buffer, Ii64_size(pos, size));
|
||||||
|
move_past_lead_whitespace(app, view, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_COMMAND_SIG(open_long_braces)
|
||||||
|
CUSTOM_DOC("At the cursor, insert a '{' and '}' separated by a blank line.")
|
||||||
|
{
|
||||||
|
char text[] = "{\n\n}";
|
||||||
|
i32 size = sizeof(text) - 1;
|
||||||
|
long_braces(app, text, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_COMMAND_SIG(open_long_braces_semicolon)
|
||||||
|
CUSTOM_DOC("At the cursor, insert a '{' and '};' separated by a blank line.")
|
||||||
|
{
|
||||||
|
char text[] = "{\n\n};";
|
||||||
|
i32 size = sizeof(text) - 1;
|
||||||
|
long_braces(app, text, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_COMMAND_SIG(open_long_braces_break)
|
||||||
|
CUSTOM_DOC("At the cursor, insert a '{' and '}break;' separated by a blank line.")
|
||||||
|
{
|
||||||
|
char text[] = "{\n\n}break;";
|
||||||
|
i32 size = sizeof(text) - 1;
|
||||||
|
long_braces(app, text, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_COMMAND_SIG(if0_off)
|
||||||
|
CUSTOM_DOC("Surround the range between the cursor and mark with an '#if 0' and an '#endif'")
|
||||||
|
{
|
||||||
|
place_begin_and_end_on_own_lines(app, "#if 0", "#endif");
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_COMMAND_SIG(write_todo)
|
||||||
|
CUSTOM_DOC("At the cursor, insert a '// TODO' comment, includes user name if it was specified in config.4coder.")
|
||||||
|
{
|
||||||
|
write_named_comment_string(app, "TODO");
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_COMMAND_SIG(write_hack)
|
||||||
|
CUSTOM_DOC("At the cursor, insert a '// HACK' comment, includes user name if it was specified in config.4coder.")
|
||||||
|
{
|
||||||
|
write_named_comment_string(app, "HACK");
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_COMMAND_SIG(write_note)
|
||||||
|
CUSTOM_DOC("At the cursor, insert a '// NOTE' comment, includes user name if it was specified in config.4coder.")
|
||||||
|
{
|
||||||
|
write_named_comment_string(app, "NOTE");
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_COMMAND_SIG(write_block)
|
||||||
|
CUSTOM_DOC("At the cursor, insert a block comment.")
|
||||||
|
{
|
||||||
|
place_begin_and_end_on_own_lines(app, "/* ", " */");
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_COMMAND_SIG(write_zero_struct)
|
||||||
|
CUSTOM_DOC("At the cursor, insert a ' = {};'.")
|
||||||
|
{
|
||||||
|
write_string(app, string_u8_litexpr(" = {};"));
|
||||||
|
}
|
||||||
|
|
||||||
|
function i64
|
||||||
|
get_start_of_line_at_cursor(Application_Links *app, View_ID view, Buffer_ID buffer){
|
||||||
|
i64 pos = view_get_cursor_pos(app, view);
|
||||||
|
i64 line = get_line_number_from_pos(app, buffer, pos);
|
||||||
|
return(get_pos_past_lead_whitespace_from_line_number(app, buffer, line));
|
||||||
|
}
|
||||||
|
|
||||||
|
function b32
|
||||||
|
c_line_comment_starts_at_position(Application_Links *app, Buffer_ID buffer, i64 pos){
|
||||||
|
b32 alread_has_comment = false;
|
||||||
|
u8 check_buffer[2];
|
||||||
|
if (buffer_read_range(app, buffer, Ii64(pos, pos + 2), check_buffer)){
|
||||||
|
if (check_buffer[0] == '/' && check_buffer[1] == '/'){
|
||||||
|
alread_has_comment = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(alread_has_comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_COMMAND_SIG(comment_line)
|
||||||
|
CUSTOM_DOC("Insert '//' at the beginning of the line after leading whitespace.")
|
||||||
|
{
|
||||||
|
View_ID view = get_active_view(app, Access_ReadWriteVisible);
|
||||||
|
Buffer_ID buffer = view_get_buffer(app, view, Access_ReadWriteVisible);
|
||||||
|
i64 pos = get_start_of_line_at_cursor(app, view, buffer);
|
||||||
|
b32 alread_has_comment = c_line_comment_starts_at_position(app, buffer, pos);
|
||||||
|
if (!alread_has_comment){
|
||||||
|
buffer_replace_range(app, buffer, Ii64(pos), string_u8_litexpr("//"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_COMMAND_SIG(uncomment_line)
|
||||||
|
CUSTOM_DOC("If present, delete '//' at the beginning of the line after leading whitespace.")
|
||||||
|
{
|
||||||
|
View_ID view = get_active_view(app, Access_ReadWriteVisible);
|
||||||
|
Buffer_ID buffer = view_get_buffer(app, view, Access_ReadWriteVisible);
|
||||||
|
i64 pos = get_start_of_line_at_cursor(app, view, buffer);
|
||||||
|
b32 alread_has_comment = c_line_comment_starts_at_position(app, buffer, pos);
|
||||||
|
if (alread_has_comment){
|
||||||
|
buffer_replace_range(app, buffer, Ii64(pos, pos + 2), string_u8_empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_COMMAND_SIG(comment_line_toggle)
|
||||||
|
CUSTOM_DOC("Turns uncommented lines into commented lines and vice versa for comments starting with '//'.")
|
||||||
|
{
|
||||||
|
View_ID view = get_active_view(app, Access_ReadWriteVisible);
|
||||||
|
Buffer_ID buffer = view_get_buffer(app, view, Access_ReadWriteVisible);
|
||||||
|
i64 pos = get_start_of_line_at_cursor(app, view, buffer);
|
||||||
|
b32 alread_has_comment = c_line_comment_starts_at_position(app, buffer, pos);
|
||||||
|
if (alread_has_comment){
|
||||||
|
buffer_replace_range(app, buffer, Ii64(pos, pos + 2), string_u8_empty);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
buffer_replace_range(app, buffer, Ii64(pos), string_u8_litexpr("//"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
static Snippet default_snippets[] = {
|
||||||
|
// general (for Allen's style)
|
||||||
|
{"if", "if (){\n\n}\n", 4, 7},
|
||||||
|
{"ifelse", "if (){\n\n}\nelse{\n\n}", 4, 7},
|
||||||
|
{"forn", "for (node = ;\nnode != 0;\nnode = node->next){\n\n}\n", 5, 38},
|
||||||
|
{"fori", "for (i = 0; i < ; i += 1){\n\n}\n", 5, 16},
|
||||||
|
{"forj", "for (j = 0; j < ; j += 1){\n\n}\n", 5, 16},
|
||||||
|
{"fork", "for (k = 0; k < ; k += 1){\n\n}\n", 5, 16},
|
||||||
|
{"for", "for (;;){\n\n}\n", 5, 10},
|
||||||
|
{"///", "////////////////////////////////", 32, 32},
|
||||||
|
{"#guard", "#if !defined(Z)\n#define Z\n#endif\n", 0, 26},
|
||||||
|
|
||||||
|
{"op+", "Z\noperator+(Z a, Z b){\n,\n}\n", 0, 23},
|
||||||
|
{"op-", "Z\noperator-(Z a, Z b){\n,\n}\n", 0, 23},
|
||||||
|
{"op*", "Z\noperator*(Z a, Z b){\n,\n}\n", 0, 23},
|
||||||
|
{"op/", "Z\noperator/(Z a, Z b){\n,\n}\n", 0, 23},
|
||||||
|
{"op+=", "Z&\noperator+=(Z &a, Z b){\n,\n}\n", 0, 26},
|
||||||
|
{"op-=", "Z&\noperator-=(Z &a, Z b){\n,\n}\n", 0, 26},
|
||||||
|
{"op*=", "Z&\noperator*=(Z &a, Z b){\n,\n}\n", 0, 26},
|
||||||
|
{"op/=", "Z&\noperator/=(Z &a, Z b){\n,\n}\n", 0, 26},
|
||||||
|
|
||||||
|
// for 4coder development
|
||||||
|
{"4command", "CUSTOM_COMMAND_SIG()\nCUSTOM_DOC()\n{\n\n}\n", 19, 32},
|
||||||
|
{"4app", "Application_Links *app", 22, 22},
|
||||||
|
|
||||||
|
#if defined(SNIPPET_EXPANSION)
|
||||||
|
#include SNIPPET_EXPANSION
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
function void
|
||||||
|
write_snippet(Application_Links *app, View_ID view, Buffer_ID buffer,
|
||||||
|
i64 pos, Snippet *snippet){
|
||||||
|
if (snippet != 0){
|
||||||
|
String_Const_u8 snippet_text = SCu8(snippet->text);
|
||||||
|
buffer_replace_range(app, buffer, Ii64(pos), snippet_text);
|
||||||
|
i64 new_cursor = pos + snippet->cursor_offset;
|
||||||
|
view_set_cursor_and_preferred_x(app, view, seek_pos(new_cursor));
|
||||||
|
i64 new_mark = pos + snippet->mark_offset;
|
||||||
|
view_set_mark(app, view, seek_pos(new_mark));
|
||||||
|
auto_indent_buffer(app, buffer, Ii64_size(pos, snippet_text.size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Snippet*
|
||||||
|
get_snippet_from_user(Application_Links *app, Snippet *snippets, i32 snippet_count,
|
||||||
|
String_Const_u8 query){
|
||||||
|
Scratch_Block scratch(app);
|
||||||
|
Lister_Block lister(app, scratch);
|
||||||
|
lister_set_query(lister, query);
|
||||||
|
lister_set_default_handlers(lister);
|
||||||
|
|
||||||
|
Snippet *snippet = snippets;
|
||||||
|
for (i32 i = 0; i < snippet_count; i += 1, snippet += 1){
|
||||||
|
lister_add_item(lister, SCu8(snippet->name), SCu8(snippet->text), snippet, 0);
|
||||||
|
}
|
||||||
|
Lister_Result l_result = run_lister(app, lister);
|
||||||
|
Snippet *result = 0;
|
||||||
|
if (!l_result.canceled){
|
||||||
|
result = (Snippet*)l_result.user_data;
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function Snippet*
|
||||||
|
get_snippet_from_user(Application_Links *app, Snippet *snippets, i32 snippet_count,
|
||||||
|
char *query){
|
||||||
|
return(get_snippet_from_user(app, snippets, snippet_count, SCu8(query)));
|
||||||
|
}
|
||||||
|
|
||||||
|
CUSTOM_UI_COMMAND_SIG(snippet_lister)
|
||||||
|
CUSTOM_DOC("Opens a snippet lister for inserting whole pre-written snippets of text.")
|
||||||
|
{
|
||||||
|
View_ID view = get_this_ctx_view(app, Access_ReadWrite);
|
||||||
|
if (view != 0){
|
||||||
|
Snippet *snippet = get_snippet_from_user(app, default_snippets,
|
||||||
|
ArrayCount(default_snippets),
|
||||||
|
"Snippet:");
|
||||||
|
|
||||||
|
Buffer_ID buffer = view_get_buffer(app, view, Access_ReadWriteVisible);
|
||||||
|
i64 pos = view_get_cursor_pos(app, view);
|
||||||
|
write_snippet(app, view, buffer, pos, snippet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
4coder_combined_write_commands.cpp - Commands for writing text specialized for particular contexts.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
struct Snippet{
|
||||||
|
char *name;
|
||||||
|
char *text;
|
||||||
|
i32 cursor_offset;
|
||||||
|
i32 mark_offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Snippet_Array{
|
||||||
|
Snippet *snippets;
|
||||||
|
i32 count;
|
||||||
|
};
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,832 @@
|
||||||
|
/*
|
||||||
|
4coder_command_map.cpp - Command management functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#if !defined(MAP_METADATA_ONLY)
|
||||||
|
#define MAP_METADATA_ONLY 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if MAP_METADATA_ONLY
|
||||||
|
#define BindingGetPtr(b) ((b).name)
|
||||||
|
#else
|
||||||
|
#define BindingGetPtr(b) ((b).custom)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Command_Binding::Command_Binding(){
|
||||||
|
block_zero_struct(this);
|
||||||
|
}
|
||||||
|
Command_Binding::Command_Binding(Custom_Command_Function *c){
|
||||||
|
this->custom = c;
|
||||||
|
}
|
||||||
|
Command_Binding::Command_Binding(char *n){
|
||||||
|
this->name = n;
|
||||||
|
}
|
||||||
|
Command_Binding::operator Custom_Command_Function*(){
|
||||||
|
return(this->custom);
|
||||||
|
}
|
||||||
|
Command_Binding::operator char*(){
|
||||||
|
return(this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function u64
|
||||||
|
mapping__key(Input_Event_Kind kind, u32 sub_code){
|
||||||
|
return((((u64)kind) << 32) | sub_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Command_Map*
|
||||||
|
mapping__alloc_map(Mapping *mapping){
|
||||||
|
Command_Map *result = mapping->free_maps;
|
||||||
|
if (result != 0){
|
||||||
|
sll_stack_pop(mapping->free_maps);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
result = push_array(&mapping->node_arena, Command_Map, 1);
|
||||||
|
}
|
||||||
|
zdll_push_back(mapping->first_map, mapping->last_map, result);
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
mapping__free_map(Mapping *mapping, Command_Map *map){
|
||||||
|
zdll_remove(mapping->first_map, mapping->last_map, map);
|
||||||
|
sll_stack_push(mapping->free_maps, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Command_Modified_Binding*
|
||||||
|
mapping__alloc_modified_binding(Mapping *mapping){
|
||||||
|
Command_Modified_Binding *result = mapping->free_bindings;
|
||||||
|
if (result != 0){
|
||||||
|
sll_stack_pop(mapping->free_bindings);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
result = push_array(&mapping->node_arena, Command_Modified_Binding, 1);
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
mapping__free_modified_binding(Mapping *mapping, Command_Modified_Binding *binding){
|
||||||
|
sll_stack_push(mapping->free_bindings, binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Command_Binding_List*
|
||||||
|
mapping__alloc_binding_list(Mapping *mapping){
|
||||||
|
Command_Binding_List *result = mapping->free_lists;
|
||||||
|
if (result != 0){
|
||||||
|
sll_stack_pop(mapping->free_lists);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
result = push_array(&mapping->node_arena, Command_Binding_List, 1);
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
mapping__free_binding_list(Mapping *mapping, Command_Binding_List *binding_list){
|
||||||
|
sll_stack_push(mapping->free_lists, binding_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Command_Binding_List*
|
||||||
|
map__get_list(Command_Map *map, u64 key){
|
||||||
|
Command_Binding_List *result = 0;
|
||||||
|
Table_Lookup lookup = table_lookup(&map->event_code_to_binding_list, key);
|
||||||
|
if (lookup.found_match){
|
||||||
|
u64 val = 0;
|
||||||
|
table_read(&map->event_code_to_binding_list, lookup, &val);
|
||||||
|
result = (Command_Binding_List*)IntAsPtr(val);
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Command_Binding_List*
|
||||||
|
map__get_or_make_list(Mapping *mapping, Command_Map *map, u64 key){
|
||||||
|
Command_Binding_List *result = map__get_list(map, key);
|
||||||
|
if (result == 0){
|
||||||
|
result = mapping__alloc_binding_list(mapping);
|
||||||
|
block_zero_struct(result);
|
||||||
|
sll_queue_push(map->list_first, map->list_last, result);
|
||||||
|
table_insert(&map->event_code_to_binding_list, key, (u64)PtrAsInt(result));
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
function void
|
||||||
|
mapping_init(Thread_Context *tctx, Mapping *mapping){
|
||||||
|
block_zero_struct(mapping);
|
||||||
|
mapping->node_arena = make_arena_system();
|
||||||
|
heap_init(&mapping->heap, &mapping->node_arena);
|
||||||
|
mapping->heap_wrapper = base_allocator_on_heap(&mapping->heap);
|
||||||
|
mapping->id_to_map = make_table_u64_u64(tctx->allocator, 10);
|
||||||
|
mapping->id_counter = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
mapping_release(Thread_Context *tctx, Mapping *mapping){
|
||||||
|
linalloc_clear(&mapping->node_arena);
|
||||||
|
table_free(&mapping->id_to_map);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
map__init(Mapping *mapping, Command_Map *map, Command_Map_ID id){
|
||||||
|
block_zero_struct(map);
|
||||||
|
map->id = id;
|
||||||
|
map->node_arena = make_arena(&mapping->heap_wrapper, KB(2));
|
||||||
|
map->event_code_to_binding_list = make_table_u64_u64(&mapping->heap_wrapper, 100);
|
||||||
|
map->cmd_to_binding_trigger = make_table_u64_u64(&mapping->heap_wrapper, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Command_Map*
|
||||||
|
mapping_get_map(Mapping *mapping, Command_Map_ID id){
|
||||||
|
Command_Map *result = 0;
|
||||||
|
Table_Lookup lookup = table_lookup(&mapping->id_to_map, id);
|
||||||
|
if (lookup.found_match){
|
||||||
|
u64 val = 0;
|
||||||
|
table_read(&mapping->id_to_map, lookup, &val);
|
||||||
|
result = (Command_Map*)IntAsPtr(val);
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Command_Map_ID
|
||||||
|
mapping_validate_id(Mapping *mapping, Command_Map_ID id){
|
||||||
|
Table_Lookup lookup = table_lookup(&mapping->id_to_map, id);
|
||||||
|
if (!lookup.found_match){
|
||||||
|
id = 0;
|
||||||
|
}
|
||||||
|
return(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Command_Map*
|
||||||
|
mapping_get_or_make_map(Mapping *mapping, Command_Map_ID id){
|
||||||
|
Command_Map *result = mapping_get_map(mapping, id);
|
||||||
|
if (result == 0){
|
||||||
|
result = mapping__alloc_map(mapping);
|
||||||
|
map__init(mapping, result, id);
|
||||||
|
table_insert(&mapping->id_to_map, id, (u64)PtrAsInt(result));
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
mapping_release_map(Mapping *mapping, Command_Map *map){
|
||||||
|
table_erase(&mapping->id_to_map, map->id);
|
||||||
|
if (map->binding_last != 0){
|
||||||
|
map->binding_last->next = mapping->free_bindings;
|
||||||
|
mapping->free_bindings = map->binding_first;
|
||||||
|
}
|
||||||
|
if (map->list_last != 0){
|
||||||
|
map->list_last->next = mapping->free_lists;
|
||||||
|
mapping->free_lists = map->list_first;
|
||||||
|
}
|
||||||
|
table_free(&map->event_code_to_binding_list);
|
||||||
|
linalloc_clear(&map->node_arena);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
function b32
|
||||||
|
map_strict_match(Input_Modifier_Set *binding_mod_set, Input_Modifier_Set *event_mod_set, Key_Code skip_self_mod){
|
||||||
|
b32 result = true;
|
||||||
|
i32 binding_mod_count = binding_mod_set->count;
|
||||||
|
Key_Code *binding_mods = binding_mod_set->mods;
|
||||||
|
for (i32 i = 0; i < binding_mod_count; i += 1){
|
||||||
|
if (!has_modifier(event_mod_set, binding_mods[i])){
|
||||||
|
result = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i32 mod_count = event_mod_set->count;
|
||||||
|
Key_Code *mods = event_mod_set->mods;
|
||||||
|
for (i32 i = 0; i < mod_count; i += 1){
|
||||||
|
if (mods[i] != skip_self_mod && !has_modifier(binding_mod_set, mods[i])){
|
||||||
|
result = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function b32
|
||||||
|
map_loose_match(Input_Modifier_Set *binding_mod_set, Input_Modifier_Set *event_mod_set){
|
||||||
|
b32 result = true;
|
||||||
|
i32 binding_mod_count = binding_mod_set->count;
|
||||||
|
Key_Code *binding_mods = binding_mod_set->mods;
|
||||||
|
for (i32 i = 0; i < binding_mod_count; i += 1){
|
||||||
|
if (!has_modifier(event_mod_set, binding_mods[i])){
|
||||||
|
result = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Map_Event_Breakdown
|
||||||
|
map_get_event_breakdown(Input_Event *event){
|
||||||
|
Map_Event_Breakdown result = {};
|
||||||
|
|
||||||
|
switch (event->kind){
|
||||||
|
case InputEventKind_KeyStroke:
|
||||||
|
{
|
||||||
|
result.key = mapping__key(InputEventKind_KeyStroke, event->key.code);
|
||||||
|
result.mod_set = &event->key.modifiers;
|
||||||
|
result.skip_self_mod = event->key.code;
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case InputEventKind_MouseButton:
|
||||||
|
{
|
||||||
|
result.key = mapping__key(InputEventKind_MouseButton, event->mouse.code);
|
||||||
|
result.mod_set = &event->mouse.modifiers;
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case InputEventKind_MouseWheel:
|
||||||
|
{
|
||||||
|
result.key = mapping__key(InputEventKind_MouseWheel, 0);
|
||||||
|
result.mod_set = &event->mouse_wheel.modifiers;
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case InputEventKind_MouseMove:
|
||||||
|
{
|
||||||
|
result.key = mapping__key(InputEventKind_MouseMove, 0);
|
||||||
|
result.mod_set = &event->mouse_move.modifiers;
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case InputEventKind_Core:
|
||||||
|
{
|
||||||
|
result.key = mapping__key(InputEventKind_Core, event->core.code);
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Command_Binding
|
||||||
|
map_get_binding_non_recursive(Command_Map *map, Input_Event *event, Binding_Match_Rule rule){
|
||||||
|
Command_Binding result = {};
|
||||||
|
|
||||||
|
if (event->kind == InputEventKind_CustomFunction){
|
||||||
|
result.custom = event->custom_func;
|
||||||
|
}
|
||||||
|
else if (map != 0){
|
||||||
|
if (event->kind == InputEventKind_TextInsert){
|
||||||
|
result = map->text_input_command;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
Map_Event_Breakdown breakdown = map_get_event_breakdown(event);
|
||||||
|
Table_Lookup lookup = table_lookup(&map->event_code_to_binding_list, breakdown.key);
|
||||||
|
if (lookup.found_match){
|
||||||
|
u64 val = 0;
|
||||||
|
table_read(&map->event_code_to_binding_list, lookup, &val);
|
||||||
|
Command_Binding_List *list = (Command_Binding_List*)IntAsPtr(val);
|
||||||
|
if (breakdown.mod_set != 0){
|
||||||
|
switch (rule){
|
||||||
|
case BindingMatchRule_Strict:
|
||||||
|
{
|
||||||
|
for (SNode *node = list->first;
|
||||||
|
node != 0;
|
||||||
|
node = node->next){
|
||||||
|
Command_Modified_Binding *mod_binding = CastFromMember(Command_Modified_Binding, order_node, node);
|
||||||
|
Input_Modifier_Set *binding_mod_set = &mod_binding->mods;
|
||||||
|
if (map_strict_match(binding_mod_set, breakdown.mod_set, breakdown.skip_self_mod)){
|
||||||
|
result = mod_binding->binding;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case BindingMatchRule_Loose:
|
||||||
|
{
|
||||||
|
for (SNode *node = list->first;
|
||||||
|
node != 0;
|
||||||
|
node = node->next){
|
||||||
|
Command_Modified_Binding *mod_binding = CastFromMember(Command_Modified_Binding, order_node, node);
|
||||||
|
Input_Modifier_Set *binding_mod_set = &mod_binding->mods;
|
||||||
|
if (map_loose_match(binding_mod_set, breakdown.mod_set)){
|
||||||
|
result = mod_binding->binding;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
done:;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
Command_Modified_Binding *mod_binding = CastFromMember(Command_Modified_Binding, order_node, list->first);
|
||||||
|
result = mod_binding->binding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Command_Binding
|
||||||
|
map_get_binding_non_recursive(Command_Map *map, Input_Event *event){
|
||||||
|
Command_Binding result = map_get_binding_non_recursive(map, event, BindingMatchRule_Strict);
|
||||||
|
if (result.custom == 0){
|
||||||
|
result = map_get_binding_non_recursive(map, event, BindingMatchRule_Loose);
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Command_Binding
|
||||||
|
map_get_binding_recursive(Mapping *mapping, Command_Map *map, Input_Event *event, Binding_Match_Rule rule){
|
||||||
|
Command_Binding result = {};
|
||||||
|
for (i32 safety_counter = 0;
|
||||||
|
map != 0 && safety_counter < 40;
|
||||||
|
safety_counter += 1){
|
||||||
|
result = map_get_binding_non_recursive(map, event, rule);
|
||||||
|
if (result.custom != 0){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
map = mapping_get_map(mapping, map->parent);
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Command_Binding
|
||||||
|
map_get_binding_recursive(Mapping *mapping, Command_Map *map, Input_Event *event){
|
||||||
|
Command_Binding result = map_get_binding_recursive(mapping, map, event, BindingMatchRule_Strict);
|
||||||
|
if (result.custom == 0){
|
||||||
|
result = map_get_binding_recursive(mapping, map, event, BindingMatchRule_Loose);
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
map_set_parent(Command_Map *map, Command_Map *parent){
|
||||||
|
if (map != 0 && parent != 0){
|
||||||
|
map->parent = parent->id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
map_null_parent(Command_Map *map){
|
||||||
|
map->parent = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
map__command_add_trigger(Command_Map *map, Command_Binding binding, Command_Trigger *trigger){
|
||||||
|
if (map != 0){
|
||||||
|
u64 key = (u64)(PtrAsInt(BindingGetPtr(binding)));
|
||||||
|
Table_Lookup lookup = table_lookup(&map->cmd_to_binding_trigger, key);
|
||||||
|
Command_Trigger_List *list = 0;
|
||||||
|
if (!lookup.found_match){
|
||||||
|
list = push_array_zero(&map->node_arena, Command_Trigger_List, 1);
|
||||||
|
table_insert(&map->cmd_to_binding_trigger, key, (u64)(PtrAsInt(list)));
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
u64 val = 0;
|
||||||
|
table_read(&map->cmd_to_binding_trigger, lookup, &val);
|
||||||
|
list = (Command_Trigger_List*)IntAsPtr(val);
|
||||||
|
}
|
||||||
|
Command_Trigger *trigger_ptr = push_array(&map->node_arena, Command_Trigger, 1);
|
||||||
|
block_copy_struct(trigger_ptr, trigger);
|
||||||
|
sll_queue_push(list->first, list->last, trigger_ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Input_Event
|
||||||
|
map_trigger_as_event(Command_Trigger *trigger){
|
||||||
|
Input_Event result = {};
|
||||||
|
result.kind = trigger->kind;
|
||||||
|
switch (result.kind){
|
||||||
|
case InputEventKind_TextInsert:
|
||||||
|
{}break;
|
||||||
|
|
||||||
|
case InputEventKind_KeyStroke:
|
||||||
|
case InputEventKind_KeyRelease:
|
||||||
|
{
|
||||||
|
result.key.code = trigger->sub_code;
|
||||||
|
result.key.modifiers = trigger->mods;
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case InputEventKind_MouseButton:
|
||||||
|
case InputEventKind_MouseButtonRelease:
|
||||||
|
{
|
||||||
|
result.mouse.code = trigger->sub_code;
|
||||||
|
result.mouse.modifiers = trigger->mods;
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case InputEventKind_MouseWheel:
|
||||||
|
{
|
||||||
|
result.mouse_wheel.modifiers = trigger->mods;
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case InputEventKind_MouseMove:
|
||||||
|
{
|
||||||
|
result.mouse_move.modifiers = trigger->mods;
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case InputEventKind_Core:
|
||||||
|
{
|
||||||
|
result.core.code = trigger->sub_code;
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Command_Trigger_List
|
||||||
|
map_get_triggers_non_recursive(Mapping *mapping, Command_Map *map, Command_Binding binding){
|
||||||
|
Command_Trigger_List *result_ptr = 0;
|
||||||
|
if (map != 0){
|
||||||
|
u64 key = (u64)(PtrAsInt(BindingGetPtr(binding)));
|
||||||
|
Table_Lookup lookup = table_lookup(&map->cmd_to_binding_trigger, key);
|
||||||
|
if (lookup.found_match){
|
||||||
|
u64 val = 0;
|
||||||
|
table_read(&map->cmd_to_binding_trigger, lookup, &val);
|
||||||
|
result_ptr = (Command_Trigger_List*)IntAsPtr(val);
|
||||||
|
|
||||||
|
Command_Trigger_List list = {};
|
||||||
|
for (Command_Trigger *node = result_ptr->first, *next = 0;
|
||||||
|
node != 0;
|
||||||
|
node = next){
|
||||||
|
next = node->next;
|
||||||
|
Input_Event event = map_trigger_as_event(node);
|
||||||
|
Command_Binding this_binding = {};
|
||||||
|
if (mapping != 0){
|
||||||
|
this_binding = map_get_binding_recursive(mapping, map, &event);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
this_binding = map_get_binding_non_recursive(map, &event);
|
||||||
|
}
|
||||||
|
if (BindingGetPtr(this_binding) == BindingGetPtr(binding)){
|
||||||
|
sll_queue_push(list.first, list.last, node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*result_ptr = list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Command_Trigger_List result = {};
|
||||||
|
if (result_ptr != 0){
|
||||||
|
result = *result_ptr;
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Command_Trigger_List
|
||||||
|
map_get_triggers_non_recursive(Command_Map *map, Command_Binding binding){
|
||||||
|
return(map_get_triggers_non_recursive(0, map, binding));
|
||||||
|
}
|
||||||
|
|
||||||
|
function Command_Trigger_List
|
||||||
|
map_get_triggers_recursive(Arena *arena, Mapping *mapping, Command_Map *map, Command_Binding binding){
|
||||||
|
Command_Trigger_List result = {};
|
||||||
|
if (mapping != 0){
|
||||||
|
for (i32 safety_counter = 0;
|
||||||
|
map != 0 && safety_counter < 40;
|
||||||
|
safety_counter += 1){
|
||||||
|
Command_Trigger_List list = map_get_triggers_non_recursive(mapping, map, binding);
|
||||||
|
|
||||||
|
for (Command_Trigger *node = list.first, *next = 0;
|
||||||
|
node != 0;
|
||||||
|
node = next){
|
||||||
|
next = node->next;
|
||||||
|
Command_Trigger *nnode = push_array_write(arena, Command_Trigger, 1, node);
|
||||||
|
sll_queue_push(result.first, result.last, nnode);
|
||||||
|
}
|
||||||
|
|
||||||
|
map = mapping_get_map(mapping, map->parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Command_Binding_List*
|
||||||
|
map_get_binding_list_on_key(Command_Map *map, Key_Code code){
|
||||||
|
Command_Binding_List *result = 0;
|
||||||
|
if (map != 0){
|
||||||
|
u64 key = mapping__key(InputEventKind_KeyStroke, code);
|
||||||
|
result = map__get_list(map, key);
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Command_Binding_List*
|
||||||
|
map_get_binding_list_on_mouse_button(Command_Map *map, Mouse_Code code){
|
||||||
|
Command_Binding_List *result = 0;
|
||||||
|
if (map != 0){
|
||||||
|
u64 key = mapping__key(InputEventKind_MouseButton, code);
|
||||||
|
result = map__get_list(map, key);
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Command_Binding_List*
|
||||||
|
map_get_binding_list_on_core(Command_Map *map, Core_Code code){
|
||||||
|
Command_Binding_List *result = 0;
|
||||||
|
if (map != 0){
|
||||||
|
u64 key = mapping__key(InputEventKind_Core, code);
|
||||||
|
result = map__get_list(map, key);
|
||||||
|
}
|
||||||
|
return(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
function void
|
||||||
|
map_set_binding(Mapping *mapping, Command_Map *map, Command_Binding binding, u32 code1, u32 code2, Input_Modifier_Set *mods){
|
||||||
|
if (map != 0){
|
||||||
|
u64 key = mapping__key(code1, code2);
|
||||||
|
Command_Binding_List *list = map__get_or_make_list(mapping, map, key);
|
||||||
|
Command_Modified_Binding *mod_binding = mapping__alloc_modified_binding(mapping);
|
||||||
|
sll_stack_push(map->binding_first, mod_binding);
|
||||||
|
if (map->binding_last == 0){
|
||||||
|
map->binding_last = map->binding_first;
|
||||||
|
}
|
||||||
|
sll_stack_push(list->first, &mod_binding->order_node);
|
||||||
|
if (list->last == 0){
|
||||||
|
list->last= list->first;
|
||||||
|
}
|
||||||
|
list->count += 1;
|
||||||
|
mod_binding->mods = copy_modifier_set(&map->node_arena, mods);
|
||||||
|
mod_binding->binding = binding;
|
||||||
|
|
||||||
|
Command_Trigger trigger = {};
|
||||||
|
trigger.kind = code1;
|
||||||
|
trigger.sub_code = code2;
|
||||||
|
trigger.mods = mod_binding->mods;
|
||||||
|
map__command_add_trigger(map, binding, &trigger);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
map_set_binding_key(Mapping *mapping, Command_Map *map, Command_Binding binding, Key_Code code, Input_Modifier_Set *modifiers){
|
||||||
|
map_set_binding(mapping, map, binding, InputEventKind_KeyStroke, code, modifiers);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
map_set_binding_mouse(Mapping *mapping, Command_Map *map, Command_Binding binding, Mouse_Code code, Input_Modifier_Set *modifiers){
|
||||||
|
map_set_binding(mapping, map, binding, InputEventKind_MouseButton, code, modifiers);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
map_set_binding_core(Mapping *mapping, Command_Map *map, Command_Binding binding, Core_Code code, Input_Modifier_Set *modifiers){
|
||||||
|
map_set_binding(mapping, map, binding, InputEventKind_Core, code, modifiers);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
map_set_binding_text_input(Command_Map *map, Command_Binding binding){
|
||||||
|
if (map != 0){
|
||||||
|
map->text_input_command = binding;
|
||||||
|
Command_Trigger trigger = {};
|
||||||
|
trigger.kind = InputEventKind_TextInsert;
|
||||||
|
map__command_add_trigger(map, binding, &trigger);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
function Command_Binding_List*
|
||||||
|
map_get_binding_list_on_key(Mapping *mapping, Command_Map_ID map_id, Key_Code code){
|
||||||
|
Command_Map *map = mapping_get_map(mapping, map_id);
|
||||||
|
return(map_get_binding_list_on_key(map, code));
|
||||||
|
}
|
||||||
|
|
||||||
|
function Command_Binding
|
||||||
|
map_get_binding_non_recursive(Mapping *mapping, Command_Map_ID map_id, Input_Event *event){
|
||||||
|
Command_Map *map = mapping_get_map(mapping, map_id);
|
||||||
|
return(map_get_binding_non_recursive(map, event));
|
||||||
|
}
|
||||||
|
|
||||||
|
function Command_Binding
|
||||||
|
map_get_binding_recursive(Mapping *mapping, Command_Map_ID map_id, Input_Event *event){
|
||||||
|
Command_Map *map = mapping_get_map(mapping, map_id);
|
||||||
|
return(map_get_binding_recursive(mapping, map, event));
|
||||||
|
}
|
||||||
|
|
||||||
|
function Command_Trigger_List
|
||||||
|
map_get_triggers_non_recursive(Mapping *mapping, Command_Map_ID map_id, Command_Binding binding){
|
||||||
|
Command_Map *map = mapping_get_map(mapping, map_id);
|
||||||
|
return(map_get_triggers_non_recursive(map, binding));
|
||||||
|
}
|
||||||
|
|
||||||
|
function Command_Trigger_List
|
||||||
|
map_get_triggers_recursive(Arena *arena, Mapping *mapping, Command_Map_ID map_id, Command_Binding binding){
|
||||||
|
Command_Map *map = mapping_get_map(mapping, map_id);
|
||||||
|
return(map_get_triggers_recursive(arena, mapping, map, binding));
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
map_set_parent(Mapping *mapping, Command_Map_ID map_id, Command_Map_ID parent_id){
|
||||||
|
Command_Map *map = mapping_get_map(mapping, map_id);
|
||||||
|
Command_Map *parent = mapping_get_map(mapping, parent_id);
|
||||||
|
map_set_parent(map, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
map_set_parent(Mapping *mapping, Command_Map *map, Command_Map_ID parent_id){
|
||||||
|
Command_Map *parent = mapping_get_map(mapping, parent_id);
|
||||||
|
map_set_parent(map, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
map_null_parent(Mapping *mapping, Command_Map_ID map_id){
|
||||||
|
Command_Map *map = mapping_get_map(mapping, map_id);
|
||||||
|
map_null_parent(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
map_set_binding(Mapping *mapping, Command_Map_ID map_id, Command_Binding binding,
|
||||||
|
u32 code1, u32 code2, Input_Modifier_Set *modifiers){
|
||||||
|
Command_Map *map = mapping_get_map(mapping, map_id);
|
||||||
|
map_set_binding(mapping, map, binding, code1, code2, modifiers);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
map_set_binding_key(Mapping *mapping, Command_Map_ID map_id, Command_Binding binding,
|
||||||
|
Key_Code code, Input_Modifier_Set *modifiers){
|
||||||
|
Command_Map *map = mapping_get_map(mapping, map_id);
|
||||||
|
map_set_binding_key(mapping, map, binding, code, modifiers);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
map_set_binding_mouse(Mapping *mapping, Command_Map_ID map_id, Command_Binding binding,
|
||||||
|
Mouse_Code code, Input_Modifier_Set *modifiers){
|
||||||
|
Command_Map *map = mapping_get_map(mapping, map_id);
|
||||||
|
map_set_binding_mouse(mapping, map, binding, code, modifiers);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
map_set_binding_core(Mapping *mapping, Command_Map_ID map_id, Command_Binding binding,
|
||||||
|
Core_Code code, Input_Modifier_Set *modifiers){
|
||||||
|
Command_Map *map = mapping_get_map(mapping, map_id);
|
||||||
|
map_set_binding_core(mapping, map, binding, code, modifiers);
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
map_set_binding_text_input(Mapping *mapping, Command_Map_ID map_id, Command_Binding binding){
|
||||||
|
Command_Map *map = mapping_get_map(mapping, map_id);
|
||||||
|
map_set_binding_text_input(map, binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
function void
|
||||||
|
command_trigger_stringize_mods(Arena *arena, List_String_Const_u8 *list, Input_Modifier_Set *modifiers){
|
||||||
|
if (modifiers->count > 0){
|
||||||
|
string_list_push(arena, list, string_u8_litexpr(" holding:"));
|
||||||
|
i32 count = modifiers->count;
|
||||||
|
Key_Code *mods = modifiers->mods;
|
||||||
|
for (i32 i = 0; i < count; i += 1){
|
||||||
|
string_list_pushf(arena, list, " %s", ArraySafe(key_code_name, mods[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function void
|
||||||
|
command_trigger_stringize(Arena *arena, List_String_Const_u8 *list, Command_Trigger *trigger){
|
||||||
|
string_list_push(arena, list, string_u8_litexpr("<"));
|
||||||
|
switch (trigger->kind){
|
||||||
|
case InputEventKind_TextInsert:
|
||||||
|
{
|
||||||
|
string_list_push(arena, list, string_u8_litexpr("TextInsert"));
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case InputEventKind_KeyStroke:
|
||||||
|
{
|
||||||
|
String_Const_u8 key_name = SCu8(ArraySafe(key_code_name, trigger->sub_code));
|
||||||
|
string_list_push(arena, list, key_name);
|
||||||
|
command_trigger_stringize_mods(arena, list, &trigger->mods);
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case InputEventKind_KeyRelease:
|
||||||
|
{
|
||||||
|
string_list_pushf(arena, list, "Release %s", ArraySafe(key_code_name, trigger->sub_code));
|
||||||
|
command_trigger_stringize_mods(arena, list, &trigger->mods);
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case InputEventKind_MouseButton:
|
||||||
|
{
|
||||||
|
string_list_pushf(arena, list, "Mouse %s", ArraySafe(mouse_code_name, trigger->sub_code));
|
||||||
|
command_trigger_stringize_mods(arena, list, &trigger->mods);
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case InputEventKind_MouseButtonRelease:
|
||||||
|
{
|
||||||
|
string_list_pushf(arena, list, "Release Mouse %s", ArraySafe(mouse_code_name, trigger->sub_code));
|
||||||
|
command_trigger_stringize_mods(arena, list, &trigger->mods);
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case InputEventKind_MouseWheel:
|
||||||
|
{
|
||||||
|
string_list_push(arena, list, string_u8_litexpr("MouseWheel"));
|
||||||
|
command_trigger_stringize_mods(arena, list, &trigger->mods);
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case InputEventKind_MouseMove:
|
||||||
|
{
|
||||||
|
string_list_push(arena, list, string_u8_litexpr("MouseMove"));
|
||||||
|
command_trigger_stringize_mods(arena, list, &trigger->mods);
|
||||||
|
}break;
|
||||||
|
|
||||||
|
case InputEventKind_Core:
|
||||||
|
{
|
||||||
|
string_list_pushf(arena, list, "Core %s", ArraySafe(core_code_name, trigger->sub_code));
|
||||||
|
}break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
string_list_push(arena, list, string_u8_litexpr("ERROR unexpected trigger kind"));
|
||||||
|
}break;
|
||||||
|
}
|
||||||
|
string_list_push(arena, list, string_u8_litexpr(">"));
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////
|
||||||
|
|
||||||
|
function void
|
||||||
|
map_set_binding_lv(Mapping *mapping, Command_Map *map,
|
||||||
|
Command_Binding binding, u32 code1, u32 code2, va_list args){
|
||||||
|
Input_Modifier_Set mods = {};
|
||||||
|
Key_Code mods_array[Input_MaxModifierCount];
|
||||||
|
mods.mods = mods_array;
|
||||||
|
for (;mods.count < ArrayCount(mods_array);){
|
||||||
|
i32 v = va_arg(args, i32);
|
||||||
|
if (v <= 0){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mods.mods[mods.count] = v;
|
||||||
|
mods.count += 1;
|
||||||
|
}
|
||||||
|
return(map_set_binding(mapping, map, binding, code1, code2, &mods));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if MAP_METADATA_ONLY
|
||||||
|
function void
|
||||||
|
map_set_binding_l(Mapping *mapping, Command_Map *map, char *name, u32 code1, u32 code2, ...){
|
||||||
|
va_list args;
|
||||||
|
va_start(args, code2);
|
||||||
|
Command_Binding binding = {};
|
||||||
|
binding.name = name;
|
||||||
|
map_set_binding_lv(mapping, map, binding, code1, code2, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
function void
|
||||||
|
map_set_binding_l(Mapping *mapping, Command_Map *map, Custom_Command_Function *custom, u32 code1, u32 code2, ...){
|
||||||
|
va_list args;
|
||||||
|
va_start(args, code2);
|
||||||
|
Command_Binding binding = {};
|
||||||
|
binding.custom = custom;
|
||||||
|
map_set_binding_lv(mapping, map, binding, code1, code2, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if MAP_METADATA_ONLY
|
||||||
|
# define BindFWrap_(F) stringify(F)
|
||||||
|
#else
|
||||||
|
# define BindFWrap_(F) F
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MappingScope() Mapping *m = 0; Command_Map *map = 0
|
||||||
|
#define SelectMapping(N) m = (N)
|
||||||
|
#define SelectMap(ID) map = mapping_get_or_make_map(m, (ID))
|
||||||
|
#define ParentMap(ID) map_set_parent(m, map, (ID))
|
||||||
|
#define BindTextInput(F) map_set_binding_text_input(map, BindFWrap_(F))
|
||||||
|
|
||||||
|
#if COMPILER_CL
|
||||||
|
|
||||||
|
#define Bind(F, K, ...) \
|
||||||
|
map_set_binding_l(m, map, BindFWrap_(F), InputEventKind_KeyStroke, (K), __VA_ARGS__, 0)
|
||||||
|
#define BindRelease(F, K, ...) \
|
||||||
|
map_set_binding_l(m, map, BindFWrap_(F), InputEventKind_KeyRelease, (K), __VA_ARGS__, 0)
|
||||||
|
#define BindMouse(F, K, ...) \
|
||||||
|
map_set_binding_l(m, map, BindFWrap_(F), InputEventKind_MouseButton, (K), __VA_ARGS__, 0)
|
||||||
|
#define BindMouseRelease(F, K, ...) \
|
||||||
|
map_set_binding_l(m, map, BindFWrap_(F), InputEventKind_MouseButtonRelease, (K), __VA_ARGS__, 0)
|
||||||
|
#define BindMouseWheel(F, ...) \
|
||||||
|
map_set_binding_l(m, map, BindFWrap_(F), InputEventKind_MouseWheel, 0, __VA_ARGS__, 0)
|
||||||
|
#define BindMouseMove(F, ...) \
|
||||||
|
map_set_binding_l(m, map, BindFWrap_(F), InputEventKind_MouseMove, 0, __VA_ARGS__, 0)
|
||||||
|
#define BindCore(F, K, ...) \
|
||||||
|
map_set_binding_l(m, map, BindFWrap_(F), InputEventKind_Core, (K), __VA_ARGS__, 0)
|
||||||
|
|
||||||
|
#elif COMPILER_GCC | COMPILER_CLANG
|
||||||
|
|
||||||
|
#define Bind(F, K, ...) \
|
||||||
|
map_set_binding_l(m, map, BindFWrap_(F), InputEventKind_KeyStroke, (K), ##__VA_ARGS__, 0)
|
||||||
|
#define BindRelease(F, K, ...) \
|
||||||
|
map_set_binding_l(m, map, BindFWrap_(F), InputEventKind_KeyRelease, (K), ##__VA_ARGS__, 0)
|
||||||
|
#define BindMouse(F, K, ...) \
|
||||||
|
map_set_binding_l(m, map, BindFWrap_(F), InputEventKind_MouseButton, (K), ##__VA_ARGS__, 0)
|
||||||
|
#define BindMouseRelease(F, K, ...) \
|
||||||
|
map_set_binding_l(m, map, BindFWrap_(F), InputEventKind_MouseButtonRelease, (K), ##__VA_ARGS__, 0)
|
||||||
|
#define BindMouseWheel(F, ...) \
|
||||||
|
map_set_binding_l(m, map, BindFWrap_(F), InputEventKind_MouseWheel, 0, ##__VA_ARGS__, 0)
|
||||||
|
#define BindMouseMove(F, ...) \
|
||||||
|
map_set_binding_l(m, map, BindFWrap_(F), InputEventKind_MouseMove, 0, ##__VA_ARGS__, 0)
|
||||||
|
#define BindCore(F, K, ...) \
|
||||||
|
map_set_binding_l(m, map, BindFWrap_(F), InputEventKind_Core, (K), ##__VA_ARGS__, 0)
|
||||||
|
|
||||||
|
#else
|
||||||
|
#error "Unsupported compiler"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// BOTTOM
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
/*
|
||||||
|
4coder_command_map.h - Command management types
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TOP
|
||||||
|
|
||||||
|
#if !defined(FCODER_CODEPOINT_MAP_H)
|
||||||
|
#define FCODER_CODEPOINT_MAP_H
|
||||||
|
|
||||||
|
typedef i64 Command_Map_ID;
|
||||||
|
|
||||||
|
struct Command_Trigger{
|
||||||
|
Command_Trigger *next;
|
||||||
|
Input_Event_Kind kind;
|
||||||
|
u32 sub_code;
|
||||||
|
Input_Modifier_Set mods;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Command_Trigger_List{
|
||||||
|
Command_Trigger *first;
|
||||||
|
Command_Trigger *last;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Command_Binding{
|
||||||
|
union{
|
||||||
|
Custom_Command_Function *custom;
|
||||||
|
char *name;
|
||||||
|
};
|
||||||
|
|
||||||
|
Command_Binding();
|
||||||
|
Command_Binding(Custom_Command_Function *c);
|
||||||
|
Command_Binding(char *n);
|
||||||
|
|
||||||
|
operator Custom_Command_Function*();
|
||||||
|
operator char*();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Command_Modified_Binding{
|
||||||
|
Command_Modified_Binding *next;
|
||||||
|
SNode order_node;
|
||||||
|
Input_Modifier_Set mods;
|
||||||
|
Command_Binding binding;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Command_Binding_List{
|
||||||
|
Command_Binding_List *next;
|
||||||
|
SNode *first;
|
||||||
|
SNode *last;
|
||||||
|
i32 count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Command_Map{
|
||||||
|
Command_Map *next;
|
||||||
|
Command_Map *prev;
|
||||||
|
Command_Map_ID id;
|
||||||
|
Command_Map_ID parent;
|
||||||
|
Command_Binding text_input_command;
|
||||||
|
Arena node_arena;
|
||||||
|
Table_u64_u64 event_code_to_binding_list;
|
||||||
|
Table_u64_u64 cmd_to_binding_trigger;
|
||||||
|
Command_Modified_Binding *binding_first;
|
||||||
|
Command_Modified_Binding *binding_last;
|
||||||
|
Command_Binding_List *list_first;
|
||||||
|
Command_Binding_List *list_last;
|
||||||
|
|
||||||
|
struct Binding_Unit *real_beginning;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Mapping{
|
||||||
|
Arena node_arena;
|
||||||
|
Heap heap;
|
||||||
|
Base_Allocator heap_wrapper;
|
||||||
|
Table_u64_u64 id_to_map;
|
||||||
|
Command_Map_ID id_counter;
|
||||||
|
Command_Map *first_map;
|
||||||
|
Command_Map *last_map;
|
||||||
|
Command_Map *free_maps;
|
||||||
|
Command_Modified_Binding *free_bindings;
|
||||||
|
Command_Binding_List *free_lists;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef i32 Binding_Match_Rule;
|
||||||
|
enum{
|
||||||
|
BindingMatchRule_Strict,
|
||||||
|
BindingMatchRule_Loose,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Map_Event_Breakdown{
|
||||||
|
Input_Modifier_Set *mod_set;
|
||||||
|
u64 key;
|
||||||
|
Key_Code skip_self_mod;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// BOTTOM
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue