diff --git a/4coder_custom.cpp b/4coder_custom.cpp index f324e9a1..bed69b15 100644 --- a/4coder_custom.cpp +++ b/4coder_custom.cpp @@ -174,6 +174,16 @@ CUSTOM_COMMAND_SIG(write_and_auto_tab){ exec_command(cmd_context, cmdid_auto_tab_line_at_cursor); } +// NOTE(allen|a3.4): How one might go about writing things like cut_line +// same idea works for cut word and other such composite commands. +CUSTOM_COMMAND_SIG(cut_line){ + exec_command(cmd_context, cmdid_seek_beginning_of_line); + exec_command(cmd_context, cmdid_set_mark); + exec_command(cmd_context, cmdid_seek_end_of_line); + exec_command(cmd_context, cmdid_cut); + exec_command(cmd_context, cmdid_delete); +} + extern "C" GET_BINDING_DATA(get_bindings){ Bind_Helper context_actual = begin_bind_helper(data, size); Bind_Helper *context = &context_actual; diff --git a/4coder_string.h b/4coder_string.h index 868b6471..36daa985 100644 --- a/4coder_string.h +++ b/4coder_string.h @@ -36,6 +36,11 @@ struct String{ int size; int memory_size; }; + +struct Offset_String{ + int offset; + int size; +}; #endif inline bool char_not_slash(char c) { return (c != '\\' && c != '/'); } diff --git a/4ed.cpp b/4ed.cpp index dd8d89d8..af0722f8 100644 --- a/4ed.cpp +++ b/4ed.cpp @@ -38,6 +38,14 @@ struct Sys_App_Binding{ i32 app_id; }; +struct Complete_State{ + Search_Set set; + Search_Iter iter; + Table hits; + String_Space str; + i32 word_start, word_end; +}; + struct App_Vars{ Mem_Options mem; @@ -52,6 +60,7 @@ struct App_Vars{ Command_Map *user_maps; i32 *map_id_table; i32 user_map_count; + Command_Binding prev_command; Sys_App_Binding *sys_app_bindings; i32 sys_app_count, sys_app_max; @@ -84,6 +93,7 @@ struct App_Vars{ App_State state; App_State_Resizing resizing; + Complete_State complete_state; Panel *prev_mouse_panel; Custom_API config_api; @@ -442,70 +452,143 @@ COMMAND_DECL(word_complete){ REQ_FILE(file, view); USE_LAYOUT(layout); USE_MEM(mem); + USE_VARS(vars); + USE_WORKING_SET(working_set); Partition *part = &mem->part; + General_Memory *general = &mem->general; + Complete_State *complete_state = &vars->complete_state; + Search_Range *ranges; + Search_Match match; - Temp_Memory temp1, temp2; + Temp_Memory temp; Buffer_Type *buffer; Buffer_Backify_Type loop; char *data; i32 end; + i32 size_of_buffer; i32 cursor_pos, word_start, word_end; char c; - char *str, *spare; + char *spare; i32 size; - i32 match_start, match_end, match_size; + i32 buffer_count, i, j; + Editing_File *file_ptr; + + i32 match_size; + b32 do_init = 0; buffer = &file->state.buffer; - word_end = view->cursor.pos; - word_start = word_end; - cursor_pos = word_end - 1; + size_of_buffer = buffer_size(buffer); - // TODO(allen): macros for these buffer loops and some method of breaking out of them. - for (loop = buffer_backify_loop(buffer, cursor_pos, 0); - buffer_backify_good(&loop); - buffer_backify_next(&loop)){ - end = loop.absolute_pos; - data = loop.data - loop.absolute_pos; - for (; cursor_pos >= end; --cursor_pos){ - c = data[cursor_pos]; - if (char_is_alpha(c)){ - word_start = cursor_pos; - } - else if (!char_is_numeric(c)){ - goto double_break; + if (vars->prev_command.function != command_word_complete){ + do_init = 1; + } + + if (do_init){ + word_end = view->cursor.pos; + word_start = word_end; + cursor_pos = word_end - 1; + + // TODO(allen): macros for these buffer loops and some method of breaking out of them. + for (loop = buffer_backify_loop(buffer, cursor_pos, 0); + buffer_backify_good(&loop); + buffer_backify_next(&loop)){ + end = loop.absolute_pos; + data = loop.data - loop.absolute_pos; + for (; cursor_pos >= end; --cursor_pos){ + c = data[cursor_pos]; + if (char_is_alpha(c)){ + word_start = cursor_pos; + } + else if (!char_is_numeric(c)){ + goto double_break; + } } } + // TODO(allen): figure out how labels are scoped. + double_break:; + + size = word_end - word_start; + + search_iter_init(general, &complete_state->iter, size); + buffer_stringify(buffer, word_start, word_end, complete_state->iter.word.str); + complete_state->iter.word.size = size; + + buffer_count = working_set->file_index_count; + search_set_init(general, &complete_state->set, buffer_count + 1); + ranges = complete_state->set.ranges; + ranges[0].buffer = buffer; + ranges[0].start = 0; + ranges[0].size = word_start; + + ranges[1].buffer = buffer; + ranges[1].start = word_end; + ranges[1].size = size_of_buffer - word_end; + + file_ptr = working_set->files; + for (i = 0, j = 2; i < buffer_count; ++i, ++file_ptr){ + if (file_ptr != file && !file_ptr->state.is_dummy && file_is_ready(file_ptr)){ + ranges[j].buffer = &file_ptr->state.buffer; + ranges[j].start = 0; + ranges[j].size = buffer_size(ranges[j].buffer); + ++j; + } + } + complete_state->set.count = j; + + search_hits_init(general, &complete_state->hits, &complete_state->str, 100, Kbytes(4)); + search_hit_add(general, &complete_state->hits, &complete_state->str, + complete_state->iter.word.str, complete_state->iter.word.size); + + complete_state->word_start = word_start; + complete_state->word_end = word_end; } - // TODO(allen): figure out how labels are scoped. - double_break:; - - size = word_end - word_start; - + else{ + word_start = complete_state->word_start; + word_end = complete_state->word_end; + size = complete_state->iter.word.size; + } + if (size > 0){ - temp1 = begin_temp_memory(part); - - str = (char*)push_array(part, char, size); - buffer_stringify(buffer, word_start, word_end, str); - - temp2 = begin_temp_memory(part); - spare = (char*)push_array(part, char, size); - end_temp_memory(temp2); - - // TODO(allen): find string needs explicit end position - match_start = buffer_find_string(buffer, word_end, str, size, spare); - match_end = buffer_seek_word_right_assume_on_word(buffer, match_start); - match_size = match_end - match_start; - spare = (char*)push_array(part, char, match_size); - buffer_stringify(buffer, match_start, match_end, spare); - - view_replace_range(system, mem, view, layout, word_start, word_end, spare, match_size, word_end); - - end_temp_memory(temp1); + for (;;){ + match = search_next_match(part, &complete_state->set, &complete_state->iter); + + if (match.found_match){ + temp = begin_temp_memory(part); + match_size = match.end - match.start; + spare = (char*)push_array(part, char, match_size); + buffer_stringify(match.buffer, match.start, match.end, spare); + + if (search_hit_add(general, &complete_state->hits, &complete_state->str, spare, match_size)){ + view_replace_range(system, mem, view, layout, word_start, word_end, spare, match_size, word_end); + + complete_state->word_end = word_start + match_size; + complete_state->set.ranges[1].start = word_start + match_size; + break; + } + end_temp_memory(temp); + } + else{ + complete_state->iter.pos = 0; + complete_state->iter.i = 0; + + search_hits_init(general, &complete_state->hits, &complete_state->str, 100, Kbytes(4)); + search_hit_add(general, &complete_state->hits, &complete_state->str, + complete_state->iter.word.str, complete_state->iter.word.size); + + match_size = complete_state->iter.word.size; + view_replace_range(system, mem, view, layout, word_start, word_end, + complete_state->iter.word.str, match_size, word_end); + + complete_state->word_end = word_start + match_size; + complete_state->set.ranges[1].start = word_start + match_size; + break; + } + } } } @@ -2159,11 +2242,11 @@ setup_file_commands(Command_Map *commands, Partition *part, Key_Codes *codes, Co map_add(commands, 'l', MDFR_CTRL, command_toggle_line_wrap); map_add(commands, '?', MDFR_CTRL, command_toggle_show_whitespace); map_add(commands, '|', MDFR_CTRL, command_toggle_tokens); - map_add(commands, 'U', MDFR_CTRL, command_to_uppercase); - map_add(commands, 'u', MDFR_CTRL, command_to_lowercase); + map_add(commands, 'u', MDFR_CTRL, command_to_uppercase); + map_add(commands, 'j', MDFR_CTRL, command_to_lowercase); map_add(commands, '~', MDFR_CTRL, command_clean_all_lines); map_add(commands, 'f', MDFR_CTRL, command_search); - map_add(commands, 'j', MDFR_CTRL, command_word_complete); + map_add(commands, 't', MDFR_CTRL, command_word_complete); map_add(commands, 'r', MDFR_CTRL, command_rsearch); map_add(commands, 'g', MDFR_CTRL, command_goto_line); @@ -3462,6 +3545,7 @@ App_Step_Sig(app_step){ app_result.redraw = 1; } } + vars->prev_command = cmd; }break; case APP_STATE_RESIZING: diff --git a/4ed.h b/4ed.h index 3407586d..5edf9bc4 100644 --- a/4ed.h +++ b/4ed.h @@ -90,37 +90,6 @@ struct Clipboard_Contents{ i32 size; }; -#define FileNameMax (1 << 9) - -struct File_Slot{ - File_Slot *next, *prev; - byte *data; - i32 size, max; - char *filename; - i32 filename_len; - u32 flags; -}; - -enum File_Exchange_Flag{ - FEx_Request = 0x1, - FEx_Ready = 0x2, - FEx_Not_Exist = 0x4, - FEx_Save = 0x8, - FEx_Save_Complete = 0x10, - FEx_Save_Failed = 0x20 -}; - -struct File_Exchange{ - File_Slot available, active, free_list; - File_Slot *files; - i32 num_active, max; -}; - -struct Exchange{ - Thread_Exchange thread; - File_Exchange file; -}; - struct Command_Line_Parameters{ char **argv; int argc; diff --git a/4ed_app_target.cpp b/4ed_app_target.cpp index b994054a..819365b8 100644 --- a/4ed_app_target.cpp +++ b/4ed_app_target.cpp @@ -29,6 +29,8 @@ #include "4ed_internal.h" +#include "4tech_table.cpp" + #define FCPP_LEXER_IMPLEMENTATION #include "4cpp_lexer.h" diff --git a/4ed_file_view.cpp b/4ed_file_view.cpp index d28bd8ea..fc10f919 100644 --- a/4ed_file_view.cpp +++ b/4ed_file_view.cpp @@ -4613,13 +4613,13 @@ HANDLE_COMMAND_SIG(handle_command_file_view){ } else{ - pos = buffer_find_string(&file->state.buffer, start_pos + 1, + pos = buffer_find_string(&file->state.buffer, start_pos + 1, size, string->str, string->size, spare); if (pos < size){ if (step_forward){ file_view->isearch.pos = pos; start_pos = pos; - pos = buffer_find_string(&file->state.buffer, start_pos + 1, + pos = buffer_find_string(&file->state.buffer, start_pos + 1, size, string->str, string->size, spare); if (pos == size) pos = start_pos; } @@ -4765,5 +4765,200 @@ file_view_iter_good(File_View_Iter iter){ return(result); } +struct Search_Range{ + Buffer_Type *buffer; + i32 start, size; +}; + +struct Search_Set{ + Search_Range *ranges; + i32 count, max; +}; + +struct Search_Iter{ + String word; + i32 pos; + i32 i; +}; + +struct Search_Match{ + Buffer_Type *buffer; + i32 start, end; + b32 found_match; +}; + +internal void +search_iter_init(General_Memory *general, Search_Iter *iter, i32 size){ + i32 str_max; + + if (iter->word.str == 0){ + str_max = size*2; + iter->word.str = (char*)general_memory_allocate(general, str_max, 0); + iter->word.memory_size = str_max; + } + else if (iter->word.memory_size < size){ + str_max = size*2; + iter->word.str = (char*)general_memory_reallocate_nocopy(general, iter->word.str, str_max, 0); + iter->word.memory_size = str_max; + } + + iter->i = 0; + iter->pos = 0; +} + +internal void +search_set_init(General_Memory *general, Search_Set *set, i32 set_count){ + i32 max; + + if (set->ranges == 0){ + max = set_count*2; + set->ranges = (Search_Range*)general_memory_allocate(general, sizeof(Search_Range)*max, 0); + set->max = max; + } + else if (set->max < set_count){ + max = set_count*2; + set->ranges = (Search_Range*)general_memory_reallocate_nocopy( + general, set->ranges, sizeof(Search_Range)*max, 0); + set->max = max; + } + + set->count = set_count; +} + +internal void +search_hits_table_alloc(General_Memory *general, Table *hits, i32 table_size){ + i32 hash_size, mem_size; + + hash_size = table_size * sizeof(u32); + hash_size = (hash_size + 7) & ~7; + mem_size = hash_size + table_size * sizeof(Offset_String); + + hits->hash_array = (u32*)general_memory_allocate(general, mem_size, 0); + hits->data_array = (u8*)hits->hash_array + hash_size; + hits->max = table_size; + + hits->item_size = sizeof(Offset_String); +} + +internal void +search_hits_init(General_Memory *general, Table *hits, String_Space *str, i32 table_size, i32 str_size){ + i32 hash_size, mem_size; + + if (hits->hash_array == 0){ + search_hits_table_alloc(general, hits, table_size); + } + else if (hits->max < table_size){ + hash_size = table_size * sizeof(u32); + hash_size = (hash_size + 7) & ~7; + mem_size = hash_size + table_size * sizeof(Offset_String); + + hits->hash_array = (u32*)general_memory_reallocate_nocopy( + general, hits->hash_array, mem_size, 0); + hits->data_array = (u8*)hits->hash_array + hash_size; + hits->max = table_size; + + hits->item_size = sizeof(Offset_String); + } + + if (str->space == 0){ + str->space = (char*)general_memory_allocate(general, str_size, 0); + str->max = str_size; + } + else if (str->max < str_size){ + str->space = (char*)general_memory_reallocate_nocopy(general, str->space, str_size, 0); + str->max = str_size; + } + + str->pos = str->new_pos = 0; + table_clear(hits); +} + +internal b32 +search_hit_add(General_Memory *general, Table *hits, String_Space *space, char *str, i32 len){ + b32 result; + i32 new_size; + Offset_String ostring; + Table new_hits; + + Assert(len != 0); + + ostring = strspace_append(space, str, len); + if (ostring.size == 0){ + new_size = Max(space->max*2, space->max + len); + space->space = (char*)general_memory_reallocate(general, space->space, space->new_pos, new_size, 0); + ostring = strspace_append(space, str, len); + } + + Assert(ostring.size != 0); + + if (table_at_capacity(hits)){ + search_hits_table_alloc(general, &new_hits, hits->max*2); + table_clear(&new_hits); + table_rehash(hits, &new_hits, space->space, tbl_offset_string_hash, tbl_offset_string_compare); + general_memory_free(general, hits->hash_array); + *hits = new_hits; + } + + if (!table_add(hits, &ostring, space->space, tbl_offset_string_hash, tbl_offset_string_compare)){ + result = 1; + strspace_keep_prev(space); + } + else{ + result = 0; + strspace_discard_prev(space); + } + + return(result); +} + +internal Search_Match +search_next_match(Partition *part, Search_Set *set, Search_Iter *iter_){ + Search_Match result = {}; + Search_Iter iter = *iter_; + Search_Range *range; + Temp_Memory temp; + char *spare; + i32 start_pos, end_pos, count; + + temp = begin_temp_memory(part); + spare = push_array(part, char, iter.word.size); + + count = set->count; + for (; iter.i < count;){ + range = set->ranges + iter.i; + + end_pos = range->start + range->size; + + if (iter.pos + iter.word.size < end_pos){ + start_pos = Max(iter.pos, range->start); + result.start = buffer_find_string(range->buffer, start_pos, end_pos, iter.word.str, iter.word.size, spare); + + if (result.start < end_pos){ + iter.pos = result.start + 1; + if (result.start == 0 || !char_is_alpha_numeric(buffer_get_char(range->buffer, result.start - 1))){ + result.end = buffer_seek_word_right_assume_on_word(range->buffer, result.start); + if (result.end < end_pos){ + result.found_match = 1; + result.buffer = range->buffer; + iter.pos = result.end; + break; + } + } + } + else{ + ++iter.i, iter.pos = 0; + } + } + else{ + ++iter.i, iter.pos = 0; + } + } + end_temp_memory(temp); + + *iter_ = iter; + + return(result); +} + // BOTTOM diff --git a/4ed_mem.cpp b/4ed_mem.cpp index 7ac150e0..64a4219e 100644 --- a/4ed_mem.cpp +++ b/4ed_mem.cpp @@ -1,253 +1,255 @@ -/* - * Mr. 4th Dimention - Allen Webster - * - * 13.11.2015 - * - * Memory utils for 4coder - * - */ - -// TOP - -struct Partition{ - u8 *base; - i32 pos, max; -}; - -struct Temp_Memory{ - void *handle; - int pos; -}; - -enum Memory_Bubble_Flag{ - MEM_BUBBLE_USED = 0x1, - MEM_BUBBLE_DEBUG = 0xD3000000, - MEM_BUBBLE_SYS_DEBUG = 0x5D000000, - MEM_BUBBLE_DEBUG_MASK = 0xFF000000 -}; - -struct Bubble{ - Bubble *prev; - Bubble *next; - u32 flags; - i32 size; - u32 type; - u32 _unused_; -}; - -struct General_Memory{ - Bubble sentinel; -}; - -struct Mem_Options{ - Partition part; - General_Memory general; -}; - -inline Partition -partition_open(void *memory, i32 size){ - Partition partition; - partition.base = (u8*)memory; - partition.pos = 0; - partition.max = size; - return partition; -} - -inline void* -partition_allocate(Partition *data, i32 size){ - void *ret = 0; - if (size > 0 && data->pos + size <= data->max){ - ret = data->base + data->pos; - data->pos += size; - } - return ret; -} - -inline void -partition_align(Partition *data, u32 boundary){ - data->pos = (data->pos + (boundary - 1)) & (~boundary); -} - -inline void* -partition_current(Partition *data){ - return data->base + data->pos; -} - -inline i32 -partition_remaining(Partition *data){ - return data->max - data->pos; -} - -inline Partition -partition_sub_part(Partition *data, i32 size){ - Partition result = {}; - void *d = partition_allocate(data, size); - if (d) result = partition_open(d, size); - return result; -} - -#define push_struct(part, T) (T*)partition_allocate(part, sizeof(T)) -#define push_array(part, T, size) (T*)partition_allocate(part, sizeof(T)*(size)) -#define push_block(part, size) partition_allocate(part, size) - -inline void -insert_bubble(Bubble *prev, Bubble *bubble){ - bubble->prev = prev; - bubble->next = prev->next; - bubble->prev->next = bubble; - bubble->next->prev = bubble; -} - -inline void -remove_bubble(Bubble *bubble){ - bubble->prev->next = bubble->next; - bubble->next->prev = bubble->prev; -} - -#if FRED_INTERNAL -#define MEM_BUBBLE_FLAG_INIT MEM_BUBBLE_DEBUG -#else -#define MEM_BUBBLE_FLAG_INIT 0 -#endif - -internal void -general_memory_open(General_Memory *general, void *memory, i32 size){ - general->sentinel.prev = &general->sentinel; - general->sentinel.next = &general->sentinel; - general->sentinel.flags = MEM_BUBBLE_USED; - general->sentinel.size = 0; - - Bubble *first = (Bubble*)memory; - first->flags = (u32)MEM_BUBBLE_FLAG_INIT; - first->size = size - sizeof(Bubble); - insert_bubble(&general->sentinel, first); -} - -internal void -general_memory_check(General_Memory *general){ - Bubble *sentinel = &general->sentinel; - for (Bubble *bubble = sentinel->next; - bubble != sentinel; - bubble = bubble->next){ - Assert(bubble); - - Bubble *next = bubble->next; - Assert(bubble == next->prev); - if (next != sentinel){ - Assert(bubble->next > bubble); - Assert(bubble > bubble->prev); - - char *end_ptr = (char*)(bubble + 1) + bubble->size; - char *next_ptr = (char*)next; - AllowLocal(end_ptr); - AllowLocal(next_ptr); - Assert(end_ptr == next_ptr); - } - } -} - -#define BUBBLE_MIN_SIZE 1024 - -internal void -general_memory_attempt_split(Bubble *bubble, i32 wanted_size){ - i32 remaining_size = bubble->size - wanted_size; - if (remaining_size >= BUBBLE_MIN_SIZE){ - bubble->size = wanted_size; - Bubble *new_bubble = (Bubble*)((u8*)(bubble + 1) + wanted_size); - new_bubble->flags = (u32)MEM_BUBBLE_FLAG_INIT; - new_bubble->size = remaining_size - sizeof(Bubble); - insert_bubble(bubble, new_bubble); - } -} - -internal void* -general_memory_allocate(General_Memory *general, i32 size, u32 type = 0){ - void *result = 0; - for (Bubble *bubble = general->sentinel.next; - bubble != &general->sentinel; - bubble = bubble->next){ - if (!(bubble->flags & MEM_BUBBLE_USED)){ - if (bubble->size >= size){ - result = bubble + 1; - bubble->flags |= MEM_BUBBLE_USED; - bubble->type = type; - general_memory_attempt_split(bubble, size); - break; - } - } - } - return result; -} - -inline void -general_memory_do_merge(Bubble *left, Bubble *right){ - Assert(left->next == right); - Assert(right->prev == left); - left->size += sizeof(Bubble) + right->size; - remove_bubble(right); -} - -inline void -general_memory_attempt_merge(Bubble *left, Bubble *right){ - if (!(left->flags & MEM_BUBBLE_USED) && - !(right->flags & MEM_BUBBLE_USED)){ - general_memory_do_merge(left, right); - } -} - -internal void -general_memory_free(General_Memory *general, void *memory){ - Bubble *bubble = ((Bubble*)memory) - 1; - Assert((!FRED_INTERNAL) || (bubble->flags & MEM_BUBBLE_DEBUG_MASK) == MEM_BUBBLE_DEBUG); - bubble->flags &= ~MEM_BUBBLE_USED; - bubble->type = 0; - Bubble *prev, *next; - prev = bubble->prev; - next = bubble->next; - general_memory_attempt_merge(bubble, next); - general_memory_attempt_merge(prev, bubble); -} - -internal void* -general_memory_reallocate(General_Memory *general, void *old, i32 old_size, i32 size, u32 type = 0){ - void *result = old; - Bubble *bubble = ((Bubble*)old) - 1; - bubble->type = type; - Assert((!FRED_INTERNAL) || (bubble->flags & MEM_BUBBLE_DEBUG_MASK) == MEM_BUBBLE_DEBUG); - i32 additional_space = size - bubble->size; - if (additional_space > 0){ - Bubble *next = bubble->next; - if (!(next->flags & MEM_BUBBLE_USED) && - next->size + sizeof(Bubble) >= additional_space){ - general_memory_do_merge(bubble, next); - general_memory_attempt_split(bubble, size); - } - else{ - result = general_memory_allocate(general, size, type); - if (old_size) memcpy(result, old, old_size); - general_memory_free(general, old); - } - } - return result; -} - -inline void* -general_memory_reallocate_nocopy(General_Memory *general, void *old, i32 size, u32 type = 0){ - return general_memory_reallocate(general, old, 0, size, type); -} - -internal Temp_Memory -begin_temp_memory(Partition *data){ - Temp_Memory result; - result.handle = data; - result.pos = data->pos; - return result; -} - -internal void -end_temp_memory(Temp_Memory temp){ - ((Partition*)temp.handle)->pos = temp.pos; -} - -// BOTTOM - +/* + * Mr. 4th Dimention - Allen Webster + * + * 13.11.2015 + * + * Memory utils for 4coder + * + */ + +// TOP + +struct Partition{ + u8 *base; + i32 pos, max; +}; + +struct Temp_Memory{ + void *handle; + int pos; +}; + +enum Memory_Bubble_Flag{ + MEM_BUBBLE_USED = 0x1, + MEM_BUBBLE_DEBUG = 0xD3000000, + MEM_BUBBLE_SYS_DEBUG = 0x5D000000, + MEM_BUBBLE_DEBUG_MASK = 0xFF000000 +}; + +struct Bubble{ + Bubble *prev; + Bubble *next; + u32 flags; + i32 size; + u32 type; + u32 _unused_; +}; + +struct General_Memory{ + Bubble sentinel; +}; + +struct Mem_Options{ + Partition part; + General_Memory general; +}; + +inline Partition +partition_open(void *memory, i32 size){ + Partition partition; + partition.base = (u8*)memory; + partition.pos = 0; + partition.max = size; + return partition; +} + +inline void* +partition_allocate(Partition *data, i32 size){ + void *ret = 0; + if (size > 0 && data->pos + size <= data->max){ + ret = data->base + data->pos; + data->pos += size; + } + return ret; +} + +inline void +partition_align(Partition *data, u32 boundary){ + data->pos = (data->pos + (boundary - 1)) & (~boundary); +} + +inline void* +partition_current(Partition *data){ + return data->base + data->pos; +} + +inline i32 +partition_remaining(Partition *data){ + return data->max - data->pos; +} + +inline Partition +partition_sub_part(Partition *data, i32 size){ + Partition result = {}; + void *d = partition_allocate(data, size); + if (d) result = partition_open(d, size); + return result; +} + +#define push_struct(part, T) (T*)partition_allocate(part, sizeof(T)) +#define push_array(part, T, size) (T*)partition_allocate(part, sizeof(T)*(size)) +#define push_block(part, size) partition_allocate(part, size) + +inline void +insert_bubble(Bubble *prev, Bubble *bubble){ + bubble->prev = prev; + bubble->next = prev->next; + bubble->prev->next = bubble; + bubble->next->prev = bubble; +} + +inline void +remove_bubble(Bubble *bubble){ + bubble->prev->next = bubble->next; + bubble->next->prev = bubble->prev; +} + +#if FRED_INTERNAL +#define MEM_BUBBLE_FLAG_INIT MEM_BUBBLE_DEBUG +#else +#define MEM_BUBBLE_FLAG_INIT 0 +#endif + +internal void +general_memory_open(General_Memory *general, void *memory, i32 size){ + general->sentinel.prev = &general->sentinel; + general->sentinel.next = &general->sentinel; + general->sentinel.flags = MEM_BUBBLE_USED; + general->sentinel.size = 0; + + Bubble *first = (Bubble*)memory; + first->flags = (u32)MEM_BUBBLE_FLAG_INIT; + first->size = size - sizeof(Bubble); + insert_bubble(&general->sentinel, first); +} + +internal void +general_memory_check(General_Memory *general){ + Bubble *sentinel = &general->sentinel; + for (Bubble *bubble = sentinel->next; + bubble != sentinel; + bubble = bubble->next){ + Assert(bubble); + + Bubble *next = bubble->next; + Assert(bubble == next->prev); + if (next != sentinel){ + Assert(bubble->next > bubble); + Assert(bubble > bubble->prev); + + char *end_ptr = (char*)(bubble + 1) + bubble->size; + char *next_ptr = (char*)next; + AllowLocal(end_ptr); + AllowLocal(next_ptr); + Assert(end_ptr == next_ptr); + } + } +} + +#define BUBBLE_MIN_SIZE 1024 + +internal void +general_memory_attempt_split(Bubble *bubble, i32 wanted_size){ + i32 remaining_size = bubble->size - wanted_size; + if (remaining_size >= BUBBLE_MIN_SIZE){ + bubble->size = wanted_size; + Bubble *new_bubble = (Bubble*)((u8*)(bubble + 1) + wanted_size); + new_bubble->flags = (u32)MEM_BUBBLE_FLAG_INIT; + new_bubble->size = remaining_size - sizeof(Bubble); + insert_bubble(bubble, new_bubble); + } +} + +internal void* +general_memory_allocate(General_Memory *general, i32 size, u32 type = 0){ + void *result = 0; + for (Bubble *bubble = general->sentinel.next; + bubble != &general->sentinel; + bubble = bubble->next){ + if (!(bubble->flags & MEM_BUBBLE_USED)){ + if (bubble->size >= size){ + result = bubble + 1; + bubble->flags |= MEM_BUBBLE_USED; + bubble->type = type; + general_memory_attempt_split(bubble, size); + break; + } + } + } + return result; +} + +inline void +general_memory_do_merge(Bubble *left, Bubble *right){ + Assert(left->next == right); + Assert(right->prev == left); + left->size += sizeof(Bubble) + right->size; + remove_bubble(right); +} + +inline void +general_memory_attempt_merge(Bubble *left, Bubble *right){ + if (!(left->flags & MEM_BUBBLE_USED) && + !(right->flags & MEM_BUBBLE_USED)){ + general_memory_do_merge(left, right); + } +} + +internal void +general_memory_free(General_Memory *general, void *memory){ + Bubble *bubble = ((Bubble*)memory) - 1; + Assert((!FRED_INTERNAL) || (bubble->flags & MEM_BUBBLE_DEBUG_MASK) == MEM_BUBBLE_DEBUG); + bubble->flags &= ~MEM_BUBBLE_USED; + bubble->type = 0; + Bubble *prev, *next; + prev = bubble->prev; + next = bubble->next; + general_memory_attempt_merge(bubble, next); + general_memory_attempt_merge(prev, bubble); +} + +internal void* +general_memory_reallocate(General_Memory *general, void *old, i32 old_size, i32 size, u32 type = 0){ + void *result = old; + Bubble *bubble = ((Bubble*)old) - 1; + bubble->type = type; + Assert((!FRED_INTERNAL) || (bubble->flags & MEM_BUBBLE_DEBUG_MASK) == MEM_BUBBLE_DEBUG); + i32 additional_space = size - bubble->size; + if (additional_space > 0){ + Bubble *next = bubble->next; + if (!(next->flags & MEM_BUBBLE_USED) && + next->size + sizeof(Bubble) >= additional_space){ + general_memory_do_merge(bubble, next); + general_memory_attempt_split(bubble, size); + } + else{ + result = general_memory_allocate(general, size, type); + if (old_size) memcpy(result, old, old_size); + general_memory_free(general, old); + } + } + return result; +} + +inline void* +general_memory_reallocate_nocopy(General_Memory *general, void *old, i32 size, u32 type = 0){ + return general_memory_reallocate(general, old, 0, size, type); +} + +internal Temp_Memory +begin_temp_memory(Partition *data){ + Temp_Memory result; + result.handle = data; + result.pos = data->pos; + return result; +} + +internal void +end_temp_memory(Temp_Memory temp){ + ((Partition*)temp.handle)->pos = temp.pos; +} + +#define reset_temp_memory end_temp_memory + +// BOTTOM + diff --git a/4ed_system.h b/4ed_system.h index 35a48980..c5bb77e8 100644 --- a/4ed_system.h +++ b/4ed_system.h @@ -187,5 +187,36 @@ struct System_Functions{ INTERNAL_System_Debug_Message *internal_debug_message; }; +#define FileNameMax (1 << 9) + +struct File_Slot{ + File_Slot *next, *prev; + byte *data; + i32 size, max; + char *filename; + i32 filename_len; + u32 flags; +}; + +enum File_Exchange_Flag{ + FEx_Request = 0x1, + FEx_Ready = 0x2, + FEx_Not_Exist = 0x4, + FEx_Save = 0x8, + FEx_Save_Complete = 0x10, + FEx_Save_Failed = 0x20 +}; + +struct File_Exchange{ + File_Slot available, active, free_list; + File_Slot *files; + i32 num_active, max; +}; + +struct Exchange{ + Thread_Exchange thread; + File_Exchange file; +}; + // BOTTOM diff --git a/4tech_table.cpp b/4tech_table.cpp new file mode 100644 index 00000000..e1d92800 --- /dev/null +++ b/4tech_table.cpp @@ -0,0 +1,235 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 14.02.2016 + * + * 4tech C style genereic hash table + * + */ + +// TOP + +#define TableHashEmpty 0 +#define TableHashDeleted 1 +#define TableHashMin 0x10000000 + +typedef u32 Hash_Function(void *item, void *arg); +typedef i32 Compare_Function(void *key, void *item, void *arg); + +struct Table{ + u32 *hash_array; + u8 *data_array; + i32 count, max; + + i32 item_size; +}; + +internal b32 +table_at_capacity(Table *table){ + b32 result = 1; + if (table->count * 8 < table->max * 7){ + result = 0; + } + return(result); +} + +internal b32 +table_add(Table *table, void *item, void *arg, Hash_Function *hash_func, Compare_Function *comp_func){ + u32 hash, *inspect; + i32 i; + + Assert(table->count * 8 < table->max * 7); + + hash = (hash_func(item, arg) | TableHashMin); + i = hash % table->max; + inspect = table->hash_array + i; + + while (*inspect >= TableHashMin){ + if (*inspect == hash){ + if (comp_func(item, table->data_array + i*table->item_size, arg) == 0){ + return(1); + } + } + ++i; + ++inspect; + if (i == table->max){ + i = 0; + inspect = table->hash_array; + } + } + *inspect = hash; + memcpy(table->data_array + i*table->item_size, item, table->item_size); + ++table->count; + + return(0); +} + +internal b32 +table_find_pos(Table *table, void *search_key, void *arg, i32 *pos, i32 *index, Hash_Function *hash_func, Compare_Function *comp_func){ + u32 hash, *inspect; + i32 i; + + hash = (hash_func(search_key, arg) | TableHashMin); + i = hash % table->max; + inspect = table->hash_array + i; + + while (*inspect != TableHashEmpty){ + if (*inspect == hash){ + if (comp_func(search_key, table->data_array + i*table->item_size, arg) == 0){ + if (pos) *pos = i*table->item_size; + if (index) *index = i; + return(1); + } + } + ++i; + ++inspect; + if (i == table->max){ + i = 0; + inspect = table->hash_array; + } + } + + return(0); +} + +internal void* +table_find_item(Table *table, void *search_key, void *arg, Hash_Function *hash_func, Compare_Function *comp_func){ + i32 pos; + void *result = 0; + if (table_find_pos(table, search_key, arg, &pos, 0, hash_func, comp_func)){ + result = table->data_array + pos; + } + return(result); +} + +internal void +table_remove_index(Table *table, i32 index){ + table->hash_array[index] = TableHashDeleted; + --table->count; +} + +internal b32 +table_remove_match(Table *table, void *search_key, void *arg, Hash_Function *hash_func, Compare_Function *comp_func){ + i32 index; + b32 result = 0; + if (table_find_pos(table, search_key, arg, 0, &index, hash_func, comp_func)){ + table_remove_index(table, index); + result = 1; + } + return(result); +} + +internal void +table_clear(Table *table){ + table->count = 0; + memset(table->hash_array, 0, table->max*sizeof(*table->hash_array)); +} + +internal void +table_rehash(Table *src, Table *dst, void *arg, Hash_Function *hash_func, Compare_Function *comp_func){ + i32 i, c, count, item_size; + u32 *hash_item; + u8 *data_item; + + Assert((dst->count + src->count - 1) * 7 < dst->max * 8); + Assert(dst->item_size == src->item_size); + + count = src->count; + hash_item = src->hash_array; + data_item = src->data_array; + item_size = src->item_size; + for (i = 0, c = 0; c < count; ++i, ++hash_item, data_item += item_size){ + if (*hash_item >= TableHashMin){ + ++c; + table_add(dst, data_item, arg, hash_func, comp_func); + } + } +} + +internal u32 +tbl_string_hash(void *item, void *arg){ + String *string = (String*)item; + char *str; + i32 i,len; + u32 x = 5381; + char c; + (void)arg; + + str = string->str; + len = string->size; + i = 0; + while (i < len){ + c = str[i++]; + x = ((x << 5) + x) + c; + } + + return(x); +} + +internal i32 +tbl_string_compare(void *a, void *b, void *arg){ + String *stra = (String*)a; + String *strb = (String*)b; + i32 result = !match(*stra, *strb); + return(result); +} + +internal u32 +tbl_offset_string_hash(void *item, void *arg){ + Offset_String *string = (Offset_String*)item; + char *str; + i32 i,len; + u32 x = 5381; + char c; + + str = ((char*)arg) + string->offset; + len = string->size; + i = 0; + while (i < len){ + c = str[i++]; + x = ((x << 5) + x) + c; + } + + return(x); +} + +internal i32 +tbl_offset_string_compare(void *a, void *b, void *arg){ + Offset_String *ostra = (Offset_String*)a; + Offset_String *ostrb = (Offset_String*)b; + String stra = make_string((char*)arg + ostra->offset, ostra->size); + String strb = make_string((char*)arg + ostrb->offset, ostrb->size); + i32 result = !match(stra, strb); + return(result); +} + +struct String_Space{ + char *space; + i32 pos, new_pos, max; +}; + +internal Offset_String +strspace_append(String_Space *space, char *str, i32 len){ + Offset_String result = {}; + if (space->new_pos + len <= space->max){ + result.offset = space->new_pos; + result.size = len; + + memcpy(space->space + space->new_pos, str, len); + space->new_pos = space->pos + len; + } + return(result); +} + +internal void +strspace_keep_prev(String_Space *space){ + space->pos = space->new_pos; +} + +internal void +strspace_discard_prev(String_Space *space){ + space->new_pos = space->pos; +} + +// BOTTOM + diff --git a/buffer/4coder_buffer_abstract.cpp b/buffer/4coder_buffer_abstract.cpp index 2c66f8ce..7fa8153e 100644 --- a/buffer/4coder_buffer_abstract.cpp +++ b/buffer/4coder_buffer_abstract.cpp @@ -37,6 +37,15 @@ buffer_backify(Buffer_Type *buffer, int start, int end, char *out){ } } +internal_4tech char +buffer_get_char(Buffer_Type *buffer, int i){ + char out = 0; + if (i >= 0 && i < buffer_size(buffer)){ + buffer_stringify(buffer, i, i+1, &out); + } + return(out); +} + internal_4tech int buffer_convert_out(Buffer_Type *buffer, char *dest, int max){ Buffer_Stringify_Type loop; @@ -463,17 +472,14 @@ buffer_find_hard_start_end: } internal_4tech int -buffer_find_string(Buffer_Type *buffer, int start_pos, char *str, int len, char *spare){ +buffer_find_string(Buffer_Type *buffer, int start_pos, int end_pos, char *str, int len, char *spare){ Buffer_Stringify_Type loop; char *data; - int size, end; - int pos; - - size = buffer_size(buffer); + int end, pos; pos = start_pos; if (len > 0){ - for (loop = buffer_stringify_loop(buffer, start_pos, size - len + 1); + for (loop = buffer_stringify_loop(buffer, start_pos, end_pos - len + 1); buffer_stringify_good(&loop); buffer_stringify_next(&loop)){ end = loop.size + loop.absolute_pos; @@ -489,7 +495,7 @@ buffer_find_string(Buffer_Type *buffer, int start_pos, char *str, int len, char } buffer_find_string_end: - if (pos >= size - len + 1) pos = size; + if (pos >= end_pos - len + 1) pos = end_pos; return(pos); } diff --git a/linux_4ed.cpp b/linux_4ed.cpp index 60b97ed0..544a2069 100644 --- a/linux_4ed.cpp +++ b/linux_4ed.cpp @@ -40,7 +40,8 @@ #include #include #include -#include +//#include +#include #include #include #include @@ -53,6 +54,17 @@ #include struct Linux_Vars{ + Display *XDisplay; + Window XWindow; + Render_Target target; + + XIM input_method; + XIMStyle input_style; + XIC xic; + Key_Codes key_codes; + + Clipboard_Contents clipboard_contents; + void *app_code; void *custom; @@ -65,6 +77,8 @@ struct Linux_Vars{ #if FRED_INTERNAL Sys_Bubble internal_bubble; #endif + + Font_Load_System fnt; }; globalvar Linux_Vars linuxvars; @@ -76,25 +90,25 @@ Sys_Get_Memory_Sig(system_get_memory_){ // TODO(allen): Implement without stdlib.h void *result = 0; - if (size != 0){ + Assert(size != 0); + #if FRED_INTERNAL - Sys_Bubble *bubble; + Sys_Bubble *bubble; - result = malloc(size + sizeof(Sys_Bubble)); - bubble = (Sys_Bubble*)result; - bubble->flags = MEM_BUBBLE_SYS_DEBUG; - bubble->line_number = line_number; - bubble->file_name = file_name; - bubble->size = size; + result = malloc(size + sizeof(Sys_Bubble)); + bubble = (Sys_Bubble*)result; + bubble->flags = MEM_BUBBLE_SYS_DEBUG; + bubble->line_number = line_number; + bubble->file_name = file_name; + bubble->size = size; - // TODO(allen): make Sys_Bubble list thread safe - insert_bubble(&linuxvars.internal_bubble, bubble); - result = bubble + 1; + // TODO(allen): make Sys_Bubble list thread safe + insert_bubble(&linuxvars.internal_bubble, bubble); + result = bubble + 1; #else - result = malloc(size); + result = malloc(size); #endif - } return(result); } @@ -348,6 +362,16 @@ Sys_Save_File_Sig(system_save_file){ return(result); } +// TODO(allen): Implement this. Also where is this +// macro define? Let's try to organize these functions +// a little better now that they're starting to settle +// into their places. + +internal +Font_Load_Sig(system_draw_font_load){ + return(0); +} + internal b32 LinuxLoadAppCode(){ b32 result = 0; @@ -397,6 +421,40 @@ LinuxLoadSystemCode(){ #include "system_shared.cpp" #include "4ed_rendering.cpp" +internal void +LinuxLoadRenderCode(){ + linuxvars.target.push_clip = draw_push_clip; + linuxvars.target.pop_clip = draw_pop_clip; + linuxvars.target.push_piece = draw_push_piece; + + linuxvars.target.font_set.font_info_load = draw_font_info_load; + linuxvars.target.font_set.font_load = system_draw_font_load; + linuxvars.target.font_set.release_font = draw_release_font; +} + +internal void +LinuxRedrawTarget(){ + system_acquire_lock(RENDER_LOCK); + launch_rendering(&linuxvars.target); + system_release_lock(RENDER_LOCK); + glFlush(); + glXSwapBuffers(linuxvars.XDisplay, linuxvars.XWindow); +} + +internal void +LinuxResizeTarget(i32 width, i32 height){ + if (width > 0 && height > 0){ + glViewport(0, 0, width, height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, width, height, 0, -1, 1); + glScissor(0, 0, width, height); + + linuxvars.target.width = width; + linuxvars.target.height = height; + } +} + // NOTE(allen): Thanks to Casey for providing the linux OpenGL launcher. static bool ctxErrorOccurred = false; static int XInput2OpCode = 0; @@ -439,7 +497,7 @@ InitializeOpenGLContext(Display *XDisplay, Window XWindow, GLXFBConfig &bestFbc, None }; - printf("\nAttribs: %d %d %d %d %d\n", + printf("Attribs: %d %d %d %d %d\n", context_attribs[0], context_attribs[1], context_attribs[2], @@ -506,21 +564,21 @@ InitializeOpenGLContext(Display *XDisplay, Window XWindow, GLXFBConfig &bestFbc, { printf( "Direct GLX rendering context obtained\n" ); } - + printf( "Making context current\n" ); glXMakeCurrent( XDisplay, XWindow, ctx ); - + GLint n; char *Vendor = (char *)glGetString(GL_VENDOR); char *Renderer = (char *)glGetString(GL_RENDERER); char *Version = (char *)glGetString(GL_VERSION); char *Extensions = (char *)glGetString(GL_EXTENSIONS); - + printf("GL_VENDOR: %s\n", Vendor); printf("GL_RENDERER: %s\n", Renderer); printf("GL_VERSION: %s\n", Version); printf("GL_EXTENSIONS: %s\n", Extensions); - + return(ctx); } @@ -636,16 +694,23 @@ ChooseGLXConfig(Display *XDisplay, int XScreenIndex) return(Result); } -internal void +struct Init_Input_Result{ + XIM input_method; + XIMStyle best_style; + XIC xic; +}; + +internal Init_Input_Result InitializeXInput(Display *dpy, Window XWindow) { +#if 0 int event, error; if(XQueryExtension(dpy, "XInputExtension", &XInput2OpCode, &event, &error)) { int major = 2, minor = 0; if(XIQueryVersion(dpy, &major, &minor) != BadRequest) { - printf("XInput initialized"); + printf("XInput initialized version %d.%d\n", major, minor); } else { @@ -723,11 +788,61 @@ InitializeXInput(Display *dpy, Window XWindow) XISetMask(Mask.mask, XI_RawMotion); XISelectEvents(dpy, DefaultRootWindow(dpy), &Mask, 1); } +#endif + + // NOTE(allen): Annnndddd... here goes some guess work of my own. + Init_Input_Result result = {}; + XIMStyle style; + XIMStyles *styles = 0; + i32 i, count; + + XSetLocaleModifiers(""); + printf("supported locales: %d\n", XSupportsLocale()); + + XSelectInput(linuxvars.XDisplay, linuxvars.XWindow, ExposureMask | KeyPressMask); + + result.input_method = XOpenIM(dpy, 0, 0, 0); + if (result.input_method){ + if (!XGetIMValues(result.input_method, XNQueryInputStyle, &styles, NULL) && + styles){ + result.best_style = 0; + count = styles->count_styles; + for (i = 0; i < count; ++i){ + style = styles->supported_styles[i]; + if (style == (XIMPreeditNothing | XIMStatusNothing)){ + result.best_style = style; + break; + } + } + + if (result.best_style){ + XFree(styles); + + result.xic = + XCreateIC(result.input_method, XNInputStyle, result.best_style, + XNClientWindow, XWindow, + XNFocusWindow, XWindow, + 0, 0, + NULL); + } + else{ + result = {}; + printf("Could not get minimum required input style"); + } + } + } + else{ + result = {}; + printf("Could not open X Input Method\n"); + } + + return(result); } int main(int argc, char **argv) { + i32 COUNTER = 0; linuxvars = {}; exchange_vars = {}; @@ -765,10 +880,10 @@ main(int argc, char **argv) } if (curdir_req >= (1 << 13)){ - // TODO(allen): fuckin' bullshit string APIs makin' me pissed + // TODO(allen): bullshit string APIs makin' me pissed return 57; } - + for (curdir_size = 0; curdir_mem[curdir_size]; ++curdir_size); current_directory = make_string(curdir_mem, curdir_size, curdir_req); @@ -788,123 +903,304 @@ main(int argc, char **argv) &linuxvars.settings, &files, &file_count, clparams); - + if (output_size > 0){ // TODO(allen): crt free version printf("%.*s", output_size, (char*)memory_vars.target_memory); } if (output_size != 0) return 0; - system_filter_real_files(files, file_count); + sysshared_filter_real_files(files, file_count); - Display *XDisplay = XOpenDisplay(0); - if(XDisplay && GLXSupportsModernContexts(XDisplay)) + keycode_init(&linuxvars.key_codes); + +#ifdef FRED_SUPER + char *custom_file_default = "4coder_custom.so"; + char *custom_file; + if (linuxvars.settings.custom_dll) custom_file = linuxvars.settings.custom_dll; + else custom_file = custom_file_default; + + linuxvars.custom = dlopen(custom_file, RTLD_LAZY); + if (!linuxvars.custom && custom_file != custom_file_default){ + if (!linuxvars.settings.custom_dll_is_strict){ + linuxvars.custom = dlopen(custom_file_default, RTLD_LAZY); + } + } + + if (linuxvars.custom){ + linuxvars.custom_api.get_bindings = (Get_Binding_Data_Function*) + dlsym(linuxvars.custom, "get_bindings"); + } +#endif + + // TODO(allen): Setup background threads and locks + + LinuxLoadRenderCode(); + linuxvars.target.max = Mbytes(1); + linuxvars.target.push_buffer = (byte*)system_get_memory(linuxvars.target.max); + + File_Slot file_slots[32]; + sysshared_init_file_exchange(&exchange_vars, file_slots, ArrayCount(file_slots), 0); + + Font_Load_Parameters params[8]; + sysshared_init_font_params(&linuxvars.fnt, params, ArrayCount(params)); + + linuxvars.app.init(linuxvars.system, &linuxvars.target, + &memory_vars, &exchange_vars, &linuxvars.key_codes, + linuxvars.clipboard_contents, current_directory, + linuxvars.custom_api); + + // NOTE(allen): Here begins the linux screen setup stuff. + // Behold the true nature of this wonderful OS: + // (thanks again to Casey for providing this stuff) + Colormap cmap; + XSetWindowAttributes swa; + int WinWidth, WinHeight; + b32 window_setup_success = 0; + + WinWidth = 800; + WinHeight = 600; + + i32 xkb_ev_code = 0, xkb_err_code = 0; + + linuxvars.XDisplay = XkbOpenDisplay(0, &xkb_ev_code, &xkb_err_code, 0, 0, 0); + if(linuxvars.XDisplay && GLXSupportsModernContexts(linuxvars.XDisplay)) { - int XScreenCount = ScreenCount(XDisplay); + int XScreenCount = ScreenCount(linuxvars.XDisplay); for(int XScreenIndex = 0; XScreenIndex < XScreenCount; ++XScreenIndex) - { - Screen *XScreen = ScreenOfDisplay(XDisplay, XScreenIndex); - - int WinWidth = WidthOfScreen(XScreen); - int WinHeight = HeightOfScreen(XScreen); - - glx_config_result Config = ChooseGLXConfig(XDisplay, XScreenIndex); + { + Screen *XScreen = ScreenOfDisplay(linuxvars.XDisplay, XScreenIndex); + + i32 ScrnWidth, ScrnHeight; + ScrnWidth = WidthOfScreen(XScreen); + ScrnHeight = HeightOfScreen(XScreen); + + if (ScrnWidth + 50 < WinWidth) WinWidth = ScrnWidth + 50; + if (ScrnHeight + 50 < WinHeight) WinHeight = ScrnHeight + 50; + + glx_config_result Config = ChooseGLXConfig(linuxvars.XDisplay, XScreenIndex); if(Config.Found) { - Colormap cmap; - XSetWindowAttributes swa; - swa.colormap = cmap = XCreateColormap(XDisplay, - RootWindow(XDisplay, Config.BestInfo.screen ), + swa.colormap = cmap = XCreateColormap(linuxvars.XDisplay, + RootWindow(linuxvars.XDisplay, Config.BestInfo.screen ), Config.BestInfo.visual, AllocNone); swa.background_pixmap = None; swa.border_pixel = 0; swa.event_mask = StructureNotifyMask; - Window XWindow = XCreateWindow(XDisplay, - RootWindow(XDisplay, Config.BestInfo.screen), - 0, 0, WinWidth, WinHeight, - 0, Config.BestInfo.depth, InputOutput, - Config.BestInfo.visual, - CWBorderPixel|CWColormap|CWEventMask, &swa ); - if(XWindow) + linuxvars.XWindow = + XCreateWindow(linuxvars.XDisplay, + RootWindow(linuxvars.XDisplay, Config.BestInfo.screen), + 0, 0, WinWidth, WinHeight, + 0, Config.BestInfo.depth, InputOutput, + Config.BestInfo.visual, + CWBorderPixel|CWColormap|CWEventMask, &swa ); + + if(linuxvars.XWindow) { - XStoreName(XDisplay, XWindow, "4coder"); - XMapWindow(XDisplay, XWindow); + XStoreName(linuxvars.XDisplay, linuxvars.XWindow, "4coder-window"); + XMapWindow(linuxvars.XDisplay, linuxvars.XWindow); - InitializeXInput(XDisplay, XWindow); + Init_Input_Result input_result = + InitializeXInput(linuxvars.XDisplay, linuxvars.XWindow); + + linuxvars.input_method = input_result.input_method; + linuxvars.input_style = input_result.best_style; + linuxvars.xic = input_result.xic; b32 IsLegacy = false; GLXContext GLContext = - InitializeOpenGLContext(XDisplay, XWindow, Config.BestConfig, IsLegacy); + InitializeOpenGLContext(linuxvars.XDisplay, linuxvars.XWindow, Config.BestConfig, IsLegacy); XWindowAttributes WinAttribs; - if(XGetWindowAttributes(XDisplay, XWindow, &WinAttribs)) + if(XGetWindowAttributes(linuxvars.XDisplay, linuxvars.XWindow, &WinAttribs)) { WinWidth = WinAttribs.width; WinHeight = WinAttribs.height; } - XRaiseWindow(XDisplay, XWindow); - XSync(XDisplay, False); + XRaiseWindow(linuxvars.XDisplay, linuxvars.XWindow); + XSync(linuxvars.XDisplay, False); - for(;;) - { - while(XPending(XDisplay)) - { - XEvent Event; - XNextEvent(XDisplay, &Event); - - if((Event.xcookie.type == GenericEvent) && - (Event.xcookie.extension == XInput2OpCode) && - XGetEventData(XDisplay, &Event.xcookie)) - { - switch(Event.xcookie.evtype) - { - case XI_Motion: - { - Window root_return, child_return; - int root_x_return, root_y_return; - int MouseX, MouseY; - unsigned int mask_return; - XQueryPointer(XDisplay, - XWindow, - &root_return, &child_return, - &root_x_return, &root_y_return, - &MouseX, &MouseY, - &mask_return); - } break; - - case XI_ButtonPress: - case XI_ButtonRelease: - { - b32 Down = (Event.xcookie.evtype == XI_ButtonPress); - XIDeviceEvent *DevEvent = (XIDeviceEvent *)Event.xcookie.data; - int Button = DevEvent->detail; - } break; - - case XI_KeyPress: - case XI_KeyRelease: - { - b32 Down = (Event.xcookie.evtype == XI_KeyPress); - XIDeviceEvent *DevEvent = (XIDeviceEvent *)Event.xcookie.data; - int VK = DevEvent->detail; - } break; - } - - XFreeEventData(XDisplay, &Event.xcookie); - } - } - - // Draw some stuff here? - - glXSwapBuffers(XDisplay, XWindow); - } + window_setup_success = 1; } } } } + + XSetICFocus(linuxvars.xic); + + if (window_setup_success){ + LinuxResizeTarget(WinWidth, WinHeight); + + for(;;) + { + while(XPending(linuxvars.XDisplay)) + { + XEvent Event; + XNextEvent(linuxvars.XDisplay, &Event); + + if (XFilterEvent(&Event, None) == True){ + continue; + } + + switch (Event.type){ + case KeyPress: + { + KeySym ks = NoSymbol; + XkbLookupKeySym(linuxvars.XDisplay, Event.xkey.keycode, Event.xkey.state, NULL, &ks); + printf("keysym: %s\n", XKeysymToString(ks)); + + Status st; + char buff[256]; + memset(buff, 0, sizeof(buff)); + XmbLookupString(linuxvars.xic, &Event.xkey, buff, sizeof(buff), &ks, &st); + printf("IC status: %d, code: %u, sym: %s, str: %s\n", st, Event.xkey.keycode, XKeysymToString(ks), buff); + }break; + + } + +#if 0 + if((Event.xcookie.type == GenericEvent) && + (Event.xcookie.extension == XInput2OpCode) && + XGetEventData(linuxvars.XDisplay, &Event.xcookie)) + { + switch(Event.xcookie.evtype) + { + case XI_Motion: + { + Window root_return, child_return; + int root_x_return, root_y_return; + int MouseX, MouseY; + unsigned int mask_return; + XQueryPointer(linuxvars.XDisplay, + linuxvars.XWindow, + &root_return, &child_return, + &root_x_return, &root_y_return, + &MouseX, &MouseY, + &mask_return); + } break; + + case XI_ButtonPress: + case XI_ButtonRelease: + { + b32 Down = (Event.xcookie.evtype == XI_ButtonPress); + XIDeviceEvent *DevEvent = (XIDeviceEvent *)Event.xcookie.data; + int Button = DevEvent->detail; + } break; + + case XI_KeyPress: + case XI_KeyRelease: + { + b32 Down = (Event.xcookie.evtype == XI_KeyPress); + XIDeviceEvent *DevEvent = (XIDeviceEvent *)Event.xcookie.data; + int keycode = DevEvent->detail; + + if (Down){ + printf("Keycode %d\n", keycode); + + // NOTE(allen): Thanks to eisbehr for providing the code + // for initializing an XKeyPressedEvent struct for use in + // Xmb8LookupString + XKeyPressedEvent eventBase = {}; + eventBase.type = KeyPress; + eventBase.serial = 0xF00D; + eventBase.send_event = 1; // at least admit that this isn't a genuine event + eventBase.display = DevEvent->display; + eventBase.window = DevEvent->event; + eventBase.root = DevEvent->root; + eventBase.subwindow = 0; + eventBase.time = {}; // Let's hope we don't need a real time. + eventBase.x = 0; + eventBase.y = 0; + eventBase.x_root = 0; + eventBase.y_root = 0; + eventBase.state = 0; // state of modifiers + // Button1Mask, Button2Mask, Button3Mask, Button4Mask, Button5Mask, + // ShiftMask, LockMask, ControlMask, + // Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask. + //eventBase.keycode = 10;//minKeyCode; + eventBase.same_screen = 1; + + eventBase.keycode = keycode; + + char lookup_buffer[32]; + Status status; + i32 size; + KeySym keysym; + + size = XmbLookupString(linuxvars.xic, &eventBase, + lookup_buffer, sizeof(lookup_buffer), + &keysym, &status); + + if (status == XBufferOverflow){ + printf("Buffer overflow %d\n", size); + } + else if (status == XLookupNone){ + printf("nothing branch\n"); + } + else if (status == XLookupChars || status == XLookupBoth){ + printf("keysym %lu translation: %.*s\n", keysym, size, lookup_buffer); + } + else{ + printf("unknown branch %d\n", status); + } + break; + } + } break; + } + + XFreeEventData(linuxvars.XDisplay, &Event.xcookie); + } +#endif + } + + b32 redraw = 1; + + Key_Input_Data input_data; + Mouse_State mouse; + Application_Step_Result result; + + input_data = {}; + mouse = {}; + + result.mouse_cursor_type = APP_MOUSE_CURSOR_DEFAULT; + result.redraw = redraw; + result.lctrl_lalt_is_altgr = 0; + + linuxvars.app.step(linuxvars.system, + &linuxvars.key_codes, + &input_data, + &mouse, + &linuxvars.target, + &memory_vars, + &exchange_vars, + linuxvars.clipboard_contents, + 1, linuxvars.first, redraw, + &result); + + if (result.redraw){ + LinuxRedrawTarget(); + } + + glBegin(GL_QUADS); + { + glVertex2f(.5f, .5f); + glVertex2f(1.f, .5f); + glVertex2f(1.f, 1.f); + glVertex2f(.5f, 1.f); + + glVertex2f(0.f, 0.f); + glVertex2f(.2f, 0.f); + glVertex2f(.2f, .2f); + glVertex2f(0.f, .2f); + } + glEnd(); + glXSwapBuffers(linuxvars.XDisplay, linuxvars.XWindow); + } + } } // BOTTOM diff --git a/system_shared.cpp b/system_shared.cpp index c18c48c8..741308cb 100644 --- a/system_shared.cpp +++ b/system_shared.cpp @@ -10,7 +10,7 @@ // TOP internal void -system_filter_real_files(char **files, i32 *file_count){ +sysshared_filter_real_files(char **files, i32 *file_count){ i32 i, j; i32 end; @@ -24,5 +24,115 @@ system_filter_real_files(char **files, i32 *file_count){ *file_count = j; } +void +ex__file_insert(File_Slot *pos, File_Slot *file){ + file->next = pos->next; + file->next->prev = file; + file->prev = pos; + pos->next = file; +} + +void +ex__insert_range(File_Slot *start, File_Slot *end, File_Slot *pos){ + end->next->prev = start->prev; + start->prev->next = end->next; + + end->next = pos->next; + start->prev = pos; + pos->next->prev = end; + pos->next = start; +} + +internal void +ex__check_file(File_Slot *pos){ + File_Slot *file = pos; + + Assert(pos == pos->next->prev); + + for (pos = pos->next; + file != pos; + pos = pos->next){ + Assert(pos == pos->next->prev); + } +} + +internal void +ex__check(File_Exchange *file_exchange){ + ex__check_file(&file_exchange->available); + ex__check_file(&file_exchange->active); + ex__check_file(&file_exchange->free_list); +} + +internal void +sysshared_init_file_exchange( + Exchange *exchange, File_Slot *slots, i32 max, + char **filename_space_out){ + char *filename_space; + i32 i; + + exchange->file.max = max; + exchange->file.available = {}; + exchange->file.available.next = &exchange->file.available; + exchange->file.available.prev = &exchange->file.available; + + exchange->file.active = {}; + exchange->file.active.next = &exchange->file.active; + exchange->file.active.prev = &exchange->file.active; + + exchange->file.free_list = {}; + exchange->file.free_list.next = &exchange->file.free_list; + exchange->file.free_list.prev = &exchange->file.free_list; + + exchange->file.files = slots; + memset(slots, 0, sizeof(File_Slot)*max); + + filename_space = (char*) + system_get_memory(FileNameMax*exchange->file.max); + + File_Slot *slot = slots; + for (i = 0; i < exchange->file.max; ++i, ++slot){ + ex__file_insert(&exchange->file.available, slot); + slot->filename = filename_space; + filename_space += FileNameMax; + } + + if (filename_space_out) *filename_space_out = filename_space; +} + +internal void +fnt__remove(Font_Load_Parameters *params){ + params->next->prev = params->prev; + params->prev->next = params->next; +} + +internal void +fnt__insert(Font_Load_Parameters *pos, Font_Load_Parameters *params){ + params->next = pos->next; + pos->next->prev = params; + pos->next = params; + params->prev = pos; +} + +internal void +sysshared_init_font_params( + Font_Load_System *fnt, Font_Load_Parameters *params, i32 max){ + Font_Load_Parameters *param; + i32 i; + + fnt->params = params; + fnt->max = max; + + fnt->free_param.next = &fnt->free_param; + fnt->free_param.prev = &fnt->free_param; + + fnt->used_param.next = &fnt->free_param; + fnt->used_param.prev = &fnt->free_param; + + param = params; + for (i = 0; i < max; ++i, ++param){ + fnt__insert(&fnt->free_param, param); + } +} + // BOTTOM diff --git a/system_shared.h b/system_shared.h index 2f3dec02..adaa2d0f 100644 --- a/system_shared.h +++ b/system_shared.h @@ -14,6 +14,24 @@ // the application code, but system_shared.cpp and 4ed_rendering.cpp // rely on the functions listed here. +struct Font_Load_Parameters{ + Font_Load_Parameters *next; + Font_Load_Parameters *prev; + + Render_Font *font_out; + char *filename; + i32 pt_size; + i32 tab_width; +}; + +struct Font_Load_System{ + Font_Load_Parameters *params; + Font_Load_Parameters used_param; + Font_Load_Parameters free_param; + Partition part; + i32 max; +}; + #define Sys_Get_Memory_Sig(name) void* name(i32 size, i32 line_number, char *file_name) #define Sys_Free_Memory_Sig(name) void name(void *block) #define Sys_File_Can_Be_Made(name) b32 name(char *filename) diff --git a/vc120.pdb b/vc120.pdb index da601c9f..4ce228dc 100644 Binary files a/vc120.pdb and b/vc120.pdb differ diff --git a/win32_4ed.cpp b/win32_4ed.cpp index c6b42d29..5ecaac89 100644 --- a/win32_4ed.cpp +++ b/win32_4ed.cpp @@ -1,1889 +1,1796 @@ -/* - * Mr. 4th Dimention - Allen Webster - * - * 12.12.2014 - * - * Win32 layer for project codename "4ed" - * - */ - -// TOP - -#include "4ed_config.h" - -#include "4ed_meta.h" - -#define FCPP_FORBID_MALLOC - -#include "4cpp_types.h" -#define FCPP_STRING_IMPLEMENTATION -#include "4coder_string.h" - -#include "4ed_mem.cpp" -#include "4ed_math.cpp" - -#include "4ed_dll_reader.h" - -#include "4coder_custom.h" -#include "4ed_system.h" -#include "4ed_rendering.h" -#include "4ed.h" - -#include -#include - -#include "4ed_dll_reader.cpp" -#include "4ed_internal.h" -#include "4ed_win32_keyboard.cpp" -#include "system_shared.h" - -#define FPS 30 -#define frame_useconds (1000000 / FPS) - -#define WM_4coder_LOAD_FONT (WM_USER + 1) -#define WM_4coder_PAINT (WM_USER + 2) -#define WM_4coder_SET_CURSOR (WM_USER + 3) - -struct Thread_Context{ - u32 job_id; - b32 running; - - Work_Queue *queue; - u32 id; - u32 windows_id; - HANDLE handle; -}; - -struct Thread_Group{ - Thread_Context *threads; - i32 count; -}; - -#define UseWinDll 1 - -struct Control_Keys{ - b8 l_ctrl; - b8 r_ctrl; - b8 l_alt; - b8 r_alt; -}; - -struct Win32_Input_Chunk_Transient{ - Key_Input_Data key_data; - - b8 mouse_l_press, mouse_l_release; - b8 mouse_r_press, mouse_r_release; - b32 out_of_window; - i16 mouse_wheel; - - b32 redraw; -}; - -struct Win32_Input_Chunk_Persistent{ - i32 mouse_x, mouse_y; - b8 mouse_l, mouse_r; - - b8 keep_playing; - - Control_Keys controls; - b8 control_keys[CONTROL_KEY_COUNT]; -}; - -struct Win32_Input_Chunk{ - Win32_Input_Chunk_Transient trans; - Win32_Input_Chunk_Persistent pers; -}; - -struct Win32_Font_Load_Parameters{ - Win32_Font_Load_Parameters *next; - Win32_Font_Load_Parameters *prev; - - Render_Font *font_out; - char *filename; - i32 pt_size; - i32 tab_width; -}; - - -struct Win32_Vars{ - HWND window_handle; - HDC window_hdc; - Render_Target target; - - HANDLE update_loop_thread; - DWORD update_loop_thread_id; - - Key_Codes key_codes; - Win32_Input_Chunk input_chunk; - b32 lctrl_lalt_is_altgr; - - HCURSOR cursor_ibeam; - HCURSOR cursor_arrow; - HCURSOR cursor_leftright; - HCURSOR cursor_updown; - Application_Mouse_Cursor prev_mouse_cursor; - Clipboard_Contents clipboard_contents; - b32 next_clipboard_is_self; - DWORD clipboard_sequence; - - Thread_Group groups[THREAD_GROUP_COUNT]; - HANDLE locks[LOCK_COUNT]; - HANDLE DEBUG_sysmem_lock; - - Thread_Memory *thread_memory; - - u64 performance_frequency; - u64 start_pcount; - u64 start_time; - -#if UseWinDll - HMODULE app_code; - HMODULE custom; -#else - DLL_Loaded app_dll; - DLL_Loaded custom_dll; -#endif - - Plat_Settings settings; - System_Functions *system; - App_Functions app; - Custom_API custom_api; - b32 first; - -#if FRED_INTERNAL - Sys_Bubble internal_bubble; -#endif - - Win32_Font_Load_Parameters fnt_params[8]; - Win32_Font_Load_Parameters used_font_param; - Win32_Font_Load_Parameters free_font_param; - Partition fnt_part; -}; - -globalvar Win32_Vars win32vars; -globalvar Application_Memory memory_vars; -globalvar Exchange exchange_vars; - -#if FRED_INTERNAL -internal Bubble* -INTERNAL_system_sentinel(){ - return (&win32vars.internal_bubble); -} - -internal void -INTERNAL_system_debug_message(char *message){ - OutputDebugString(message); -} - -#endif - -// TODO(allen): Transition towards using system_shared functions - -internal void* -Win32GetMemory_(i32 size, i32 line_number, char *file_name){ - void *ptr = 0; - -#if FRED_INTERNAL - ptr = VirtualAlloc(0, size + sizeof(Sys_Bubble), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); - - Sys_Bubble *bubble = (Sys_Bubble*)ptr; - bubble->flags = MEM_BUBBLE_SYS_DEBUG; - bubble->line_number = line_number; - bubble->file_name = file_name; - bubble->size = size; - WaitForSingleObject(win32vars.DEBUG_sysmem_lock, INFINITE); - insert_bubble(&win32vars.internal_bubble, bubble); - ReleaseSemaphore(win32vars.DEBUG_sysmem_lock, 1, 0); - ptr = bubble + 1; -#else - ptr = VirtualAlloc(0, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); -#endif - - return ptr; -} - -internal -Sys_Get_Memory_Sig(system_get_memory_){ - return(Win32GetMemory_(size, line_number, file_name)); -} - -#define Win32GetMemory(size) Win32GetMemory_(size, __LINE__, __FILE__) - -internal void -Win32FreeMemory(void *block){ - if (block){ -#if FRED_INTERNAL - Sys_Bubble *bubble = ((Sys_Bubble*)block) - 1; - Assert((bubble->flags & MEM_BUBBLE_DEBUG_MASK) == MEM_BUBBLE_SYS_DEBUG); - WaitForSingleObject(win32vars.DEBUG_sysmem_lock, INFINITE); - remove_bubble(bubble); - ReleaseSemaphore(win32vars.DEBUG_sysmem_lock, 1, 0); - VirtualFree(bubble, 0, MEM_RELEASE); -#else - VirtualFree(block, 0, MEM_RELEASE); -#endif - } -} - -internal Partition -Win32ScratchPartition(i32 size){ - Partition part; - void *data; - data = Win32GetMemory(size); - part = partition_open(data, size); - return(part); -} - -internal void -Win32ScratchPartitionGrow(Partition *part, i32 new_size){ - void *data; - if (new_size > part->max){ - data = Win32GetMemory(new_size); - memcpy(data, part->base, part->pos); - Win32FreeMemory(part->base); - part->base = (u8*)data; - } -} - -internal void -Win32ScratchPartitionDouble(Partition *part){ - Win32ScratchPartitionGrow(part, part->max*2); -} - -inline void -system_free_memory(void *block){ - Win32FreeMemory(block); -} - -internal Data -system_load_file(char *filename){ - Data result = {}; - HANDLE file; - file = CreateFile((char*)filename, GENERIC_READ, 0, 0, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); - if (!file || file == INVALID_HANDLE_VALUE){ - return result; - } - - DWORD lo, hi; - lo = GetFileSize(file, &hi); - - if (hi != 0){ - CloseHandle(file); - return result; - } - - result.size = (lo) + (((u64)hi) << 32); - result.data = (byte*)Win32GetMemory(result.size); - - if (!result.data){ - CloseHandle(file); - result = {}; - return result; - } - - DWORD read_size; - BOOL read_result = ReadFile(file, result.data, result.size, - &read_size, 0); - if (!read_result || read_size != (u32)result.size){ - CloseHandle(file); - Win32FreeMemory(result.data); - result = {}; - return result; - } - - CloseHandle(file); - return result; -} - -internal b32 -system_save_file(char *filename, void *data, i32 size){ - HANDLE file; - file = CreateFile((char*)filename, GENERIC_WRITE, 0, 0, - CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); - - if (!file || file == INVALID_HANDLE_VALUE){ - return 0; - } - - BOOL write_result; - DWORD bytes_written; - write_result = WriteFile(file, data, size, &bytes_written, 0); - - CloseHandle(file); - - if (!write_result || bytes_written != (u32)size){ - return 0; - } - - return 1; -} - -internal b32 -system_file_can_be_made(char *filename){ - HANDLE file; - file = CreateFile((char*)filename, FILE_APPEND_DATA, 0, 0, - OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); - - if (!file || file == INVALID_HANDLE_VALUE){ - return 0; - } - - CloseHandle(file); - - return 1; -} - -internal -Sys_File_Time_Stamp_Sig(system_file_time_stamp){ - u64 result; - result = 0; - - FILETIME last_write; - WIN32_FILE_ATTRIBUTE_DATA data; - if (GetFileAttributesEx((char*)filename, GetFileExInfoStandard, &data)){ - last_write = data.ftLastWriteTime; - - result = ((u64)last_write.dwHighDateTime << 32) | (last_write.dwLowDateTime); - result /= 10; - } - - return result; -} - -internal -Sys_Time_Sig(system_time){ - u64 result = 0; - LARGE_INTEGER time; - if (QueryPerformanceCounter(&time)){ - result = (u64)(time.QuadPart - win32vars.start_pcount) * 1000000 / win32vars.performance_frequency; - result += win32vars.start_time; - } - return result; -} - -internal -Sys_Set_File_List_Sig(system_set_file_list){ - if (directory.size > 0){ - char dir_space[MAX_PATH + 32]; - String dir = make_string(dir_space, 0, MAX_PATH + 32); - append(&dir, directory); - char trail_str[] = "\\*"; - append(&dir, trail_str); - - char *c_str_dir = make_c_str(dir); - - WIN32_FIND_DATA find_data; - HANDLE search; - search = FindFirstFileA(c_str_dir, &find_data); - - if (search != INVALID_HANDLE_VALUE){ - i32 count = 0; - i32 file_count = 0; - BOOL more_files = 1; - do{ - if (!match(find_data.cFileName, ".") && - !match(find_data.cFileName, "..")){ - ++file_count; - i32 size = 0; - for(;find_data.cFileName[size];++size); - count += size + 1; - } - more_files = FindNextFile(search, &find_data); - }while(more_files); - FindClose(search); - - i32 required_size = count + file_count * sizeof(File_Info); - if (file_list->block_size < required_size){ - Win32FreeMemory(file_list->block); - file_list->block = Win32GetMemory(required_size); - } - - file_list->infos = (File_Info*)file_list->block; - char *name = (char*)(file_list->infos + file_count); - if (file_list->block){ - search = FindFirstFileA(c_str_dir, &find_data); - - if (search != INVALID_HANDLE_VALUE){ - File_Info *info = file_list->infos; - more_files = 1; - do{ - if (!match(find_data.cFileName, ".") && - !match(find_data.cFileName, "..")){ - info->folder = (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; - info->filename.str = name; - - i32 i = 0; - for(;find_data.cFileName[i];++i) *name++ = find_data.cFileName[i]; - info->filename.size = i; - info->filename.memory_size = info->filename.size + 1; - *name++ = 0; - ++info; - } - more_files = FindNextFile(search, &find_data); - }while(more_files); - FindClose(search); - - file_list->count = file_count; - - }else{ - Win32FreeMemory(file_list->block); - file_list->block = 0; - file_list->block_size = 0; - } - } - } - } -} - -internal -DIRECTORY_HAS_FILE_SIG(system_directory_has_file){ - char *full_filename; - char space[1024]; - HANDLE file; - b32 result; - i32 len; - - full_filename = 0; - len = str_size(filename); - if (dir.memory_size - dir.size - 1 >= len){ - full_filename = dir.str; - memcpy(dir.str + dir.size, filename, len + 1); - } - else if (dir.size + len + 1 < 1024){ - full_filename = space; - memcpy(full_filename, dir.str, dir.size); - memcpy(full_filename + dir.size, filename, len + 1); - } - - result = 0; - if (full_filename){ - file = CreateFile((char*)full_filename, GENERIC_READ, 0, 0, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); - if (file != INVALID_HANDLE_VALUE){ - CloseHandle(file); - result = 1; - } - dir.str[dir.size] = 0; - } - - return(result); -} - -b32 Win32DirectoryExists(char *path){ - DWORD attrib = GetFileAttributesA(path); - return (attrib != INVALID_FILE_ATTRIBUTES && - (attrib & FILE_ATTRIBUTE_DIRECTORY)); -} - -internal -DIRECTORY_CD_SIG(system_directory_cd){ - b32 result = 0; - i32 old_size; - i32 len; - - if (rel_path[0] != 0){ - if (rel_path[0] == '.' && rel_path[1] == 0){ - result = 1; - } - else if (rel_path[0] == '.' && rel_path[1] == '.' && rel_path[2] == 0){ - result = remove_last_folder(dir); - terminate_with_null(dir); - } - else{ - len = str_size(rel_path); - if (dir->size + len + 1 > dir->memory_size){ - old_size = dir->size; - append_partial(dir, rel_path); - append_partial(dir, "\\"); - if (Win32DirectoryExists(dir->str)){ - result = 1; - } - else{ - dir->size = old_size; - } - } - } - } - - return(result); -} - -internal -Sys_Post_Clipboard_Sig(system_post_clipboard){ - if (OpenClipboard(win32vars.window_handle)){ - EmptyClipboard(); - HANDLE memory_handle; - memory_handle = GlobalAlloc(GMEM_MOVEABLE, str.size+1); - if (memory_handle){ - char *dest = (char*)GlobalLock(memory_handle); - copy_fast_unsafe(dest, str); - GlobalUnlock(memory_handle); - SetClipboardData(CF_TEXT, memory_handle); - win32vars.next_clipboard_is_self = 1; - } - CloseClipboard(); - } -} - -internal -Sys_Acquire_Lock_Sig(system_acquire_lock){ - WaitForSingleObject(win32vars.locks[id], INFINITE); -} - -internal -Sys_Release_Lock_Sig(system_release_lock){ - ReleaseSemaphore(win32vars.locks[id], 1, 0); -} - -internal void -Win32RedrawFromUpdate(){ - SendMessage( - win32vars.window_handle, - WM_4coder_PAINT, - 0, 0); -} - -internal void -Win32SetCursorFromUpdate(Application_Mouse_Cursor cursor){ - SendMessage( - win32vars.window_handle, - WM_4coder_SET_CURSOR, - cursor, 0); -} - -internal void -Win32Resize(i32 width, i32 height){ - if (width > 0 && height > 0){ - glViewport(0, 0, width, height); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0, width, height, 0, -1, 1); - glScissor(0, 0, width, height); - - win32vars.target.width = width; - win32vars.target.height = height; - } -} - -internal HANDLE -Win32Handle(Plat_Handle h){ - HANDLE result; - result = {}; - result = *(HANDLE*)(&h); - return(result); -} - -internal Plat_Handle -Win32GenHandle(HANDLE h){ - Assert(sizeof(Plat_Handle) >= sizeof(HANDLE)); - Plat_Handle result; - result = *(Plat_Handle*)(&h); - return(result); -} - -internal DWORD WINAPI -ThreadProc(LPVOID lpParameter){ - Thread_Context *thread = (Thread_Context*)lpParameter; - Work_Queue *queue = thread->queue; - - for (;;){ - u32 read_index = queue->read_position; - u32 write_index = queue->write_position; - - if (read_index != write_index){ - u32 next_read_index = (read_index + 1) % JOB_ID_WRAP; - u32 safe_read_index = - InterlockedCompareExchange(&queue->read_position, - next_read_index, read_index); - - if (safe_read_index == read_index){ - Full_Job_Data *full_job = queue->jobs + (safe_read_index % QUEUE_WRAP); - // NOTE(allen): This is interlocked so that it plays nice - // with the cancel job routine, which may try to cancel this job - // at the same time that we try to run it - - i32 safe_running_thread = - InterlockedCompareExchange(&full_job->running_thread, - thread->id, THREAD_NOT_ASSIGNED); - - if (safe_running_thread == THREAD_NOT_ASSIGNED){ - thread->job_id = full_job->id; - thread->running = 1; - Thread_Memory *thread_memory = 0; - - // TODO(allen): remove memory_request - if (full_job->job.memory_request != 0){ - thread_memory = win32vars.thread_memory + thread->id - 1; - if (thread_memory->size < full_job->job.memory_request){ - if (thread_memory->data){ - Win32FreeMemory(thread_memory->data); - } - i32 new_size = LargeRoundUp(full_job->job.memory_request, Kbytes(4)); - thread_memory->data = Win32GetMemory(new_size); - thread_memory->size = new_size; - } - } - full_job->job.callback(win32vars.system, thread, thread_memory, - &exchange_vars.thread, full_job->job.data); - full_job->running_thread = 0; - thread->running = 0; - } - } - } - else{ - WaitForSingleObject(Win32Handle(queue->semaphore), INFINITE); - } - } -} - -internal -Sys_Post_Job_Sig(system_post_job){ - Work_Queue *queue = exchange_vars.thread.queues + group_id; - - Assert((queue->write_position + 1) % QUEUE_WRAP != queue->read_position % QUEUE_WRAP); - - b32 success = 0; - u32 result = 0; - while (!success){ - u32 write_index = queue->write_position; - u32 next_write_index = (write_index + 1) % JOB_ID_WRAP; - u32 safe_write_index = - InterlockedCompareExchange(&queue->write_position, - next_write_index, write_index); - if (safe_write_index == write_index){ - result = write_index; - write_index = write_index % QUEUE_WRAP; - queue->jobs[write_index].job = job; - queue->jobs[write_index].running_thread = THREAD_NOT_ASSIGNED; - queue->jobs[write_index].id = result; - success = 1; - } - } - - ReleaseSemaphore(Win32Handle(queue->semaphore), 1, 0); - - return result; -} - -internal -Sys_Cancel_Job_Sig(system_cancel_job){ - Work_Queue *queue = exchange_vars.thread.queues + group_id; - Thread_Group *group = win32vars.groups + group_id; - - u32 job_index; - u32 thread_id; - Full_Job_Data *full_job; - Thread_Context *thread; - - job_index = job_id % QUEUE_WRAP; - full_job = queue->jobs + job_index; - - Assert(full_job->id == job_id); - thread_id = - InterlockedCompareExchange(&full_job->running_thread, - 0, THREAD_NOT_ASSIGNED); - - if (thread_id != THREAD_NOT_ASSIGNED){ - system_acquire_lock(CANCEL_LOCK0 + thread_id - 1); - thread = group->threads + thread_id - 1; - TerminateThread(thread->handle, 0); - u32 creation_flag = 0; - thread->handle = CreateThread(0, 0, ThreadProc, thread, creation_flag, (LPDWORD)&thread->windows_id); - system_release_lock(CANCEL_LOCK0 + thread_id - 1); - thread->running = 0; - } -} - -internal void -system_grow_thread_memory(Thread_Memory *memory){ - void *old_data; - i32 old_size, new_size; - - system_acquire_lock(CANCEL_LOCK0 + memory->id - 1); - old_data = memory->data; - old_size = memory->size; - new_size = LargeRoundUp(memory->size*2, Kbytes(4)); - memory->data = Win32GetMemory(new_size); - memory->size = new_size; - if (old_data){ - memcpy(memory->data, old_data, old_size); - Win32FreeMemory(old_data); - } - system_release_lock(CANCEL_LOCK0 + memory->id - 1); -} - -#if FRED_INTERNAL -internal void -INTERNAL_get_thread_states(Thread_Group_ID id, bool8 *running, i32 *pending){ - Work_Queue *queue = exchange_vars.thread.queues + id; - u32 write = queue->write_position; - u32 read = queue->read_position; - if (write < read) write += JOB_ID_WRAP; - *pending = (i32)(write - read); - - Thread_Group *group = win32vars.groups + id; - for (i32 i = 0; i < group->count; ++i){ - running[i] = (group->threads[i].running != 0); - } -} -#endif - -internal -Sys_CLI_Call_Sig(system_cli_call){ - char cmd[] = "c:\\windows\\system32\\cmd.exe"; - char *env_variables = 0; - char command_line[2048]; - - b32 success = 1; - String s = make_fixed_width_string(command_line); - copy(&s, make_lit_string("/C ")); - append_partial(&s, script_name); - success = terminate_with_null(&s); - - if (success){ - success = 0; - - SECURITY_ATTRIBUTES sec_attributes; - HANDLE out_read; - HANDLE out_write; - - sec_attributes = {}; - sec_attributes.nLength = sizeof(SECURITY_ATTRIBUTES); - sec_attributes.bInheritHandle = TRUE; - - if (CreatePipe(&out_read, &out_write, &sec_attributes, 0)){ - if (SetHandleInformation(out_read, HANDLE_FLAG_INHERIT, 0)){ - STARTUPINFO startup = {}; - startup.cb = sizeof(STARTUPINFO); - startup.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; - startup.hStdError = out_write; - startup.hStdOutput = out_write; - startup.wShowWindow = SW_HIDE; - - PROCESS_INFORMATION info = {}; - - Assert(sizeof(Plat_Handle) >= sizeof(HANDLE)); - if (CreateProcess(cmd, command_line, - 0, 0, TRUE, 0, - env_variables, path, - &startup, &info)){ - success = 1; - CloseHandle(info.hThread); - *(HANDLE*)&cli_out->proc = info.hProcess; - *(HANDLE*)&cli_out->out_read = out_read; - *(HANDLE*)&cli_out->out_write = out_write; - } - else{ - CloseHandle(out_read); - CloseHandle(out_write); - *(HANDLE*)&cli_out->proc = INVALID_HANDLE_VALUE; - *(HANDLE*)&cli_out->out_read = INVALID_HANDLE_VALUE; - *(HANDLE*)&cli_out->out_write = INVALID_HANDLE_VALUE; - } - } - else{ - // TODO(allen): failed SetHandleInformation - } - } - else{ - // TODO(allen): failed CreatePipe - } - } - - return success; -} - -struct CLI_Loop_Control{ - u32 remaining_amount; -}; - -internal -Sys_CLI_Begin_Update_Sig(system_cli_begin_update){ - Assert(sizeof(cli->scratch_space) >= sizeof(CLI_Loop_Control)); - CLI_Loop_Control *loop = (CLI_Loop_Control*)cli->scratch_space; - loop->remaining_amount = 0; -} - -internal -Sys_CLI_Update_Step_Sig(system_cli_update_step){ - HANDLE handle = *(HANDLE*)&cli->out_read; - CLI_Loop_Control *loop = (CLI_Loop_Control*)cli->scratch_space; - b32 has_more = 0; - DWORD remaining = loop->remaining_amount; - u32 pos = 0; - DWORD read_amount = 0; - - for (;;){ - if (remaining == 0){ - if (!PeekNamedPipe(handle, 0, 0, 0, &remaining, 0)) break; - if (remaining == 0) break; - } - - if (remaining + pos < max){ - has_more = 1; - ReadFile(handle, dest + pos, remaining, &read_amount, 0); - TentativeAssert(remaining == read_amount); - pos += remaining; - remaining = 0; - } - else{ - has_more = 1; - ReadFile(handle, dest + pos, max - pos, &read_amount, 0); - TentativeAssert(max - pos == read_amount); - loop->remaining_amount = remaining - (max - pos); - pos = max; - break; - } - } - *amount = pos; - - return has_more; -} - -internal -Sys_CLI_End_Update_Sig(system_cli_end_update){ - b32 close_me = 0; - HANDLE proc = *(HANDLE*)&cli->proc; - DWORD result = 0; - - if (WaitForSingleObject(proc, 0) == WAIT_OBJECT_0){ - if (GetExitCodeProcess(proc, &result) == 0) - cli->exit = -1; - else - cli->exit = (i32)result; - - close_me = 1; - CloseHandle(*(HANDLE*)&cli->proc); - CloseHandle(*(HANDLE*)&cli->out_read); - CloseHandle(*(HANDLE*)&cli->out_write); - } - return close_me; -} - -internal void -fnt__remove(Win32_Font_Load_Parameters *params){ - params->next->prev = params->prev; - params->prev->next = params->next; -} - -internal void -fnt__insert(Win32_Font_Load_Parameters *pos, Win32_Font_Load_Parameters *params){ - params->next = pos->next; - pos->next->prev = params; - pos->next = params; - params->prev = pos; -} - -internal -Font_Load_Sig(system_draw_font_load){ - Win32_Font_Load_Parameters *params; - - system_acquire_lock(FONT_LOCK); - params = win32vars.free_font_param.next; - fnt__remove(params); - fnt__insert(&win32vars.used_font_param, params); - system_release_lock(FONT_LOCK); - - params->font_out = font_out; - params->filename = filename; - params->pt_size = pt_size; - params->tab_width = tab_width; - - SendMessage(win32vars.window_handle, - WM_4coder_LOAD_FONT, - 0, (i32)(params - win32vars.fnt_params)); - return(1); -} - -internal b32 -Win32LoadAppCode(){ - b32 result = 0; - App_Get_Functions *get_funcs = 0; - -#if UseWinDll - win32vars.app_code = LoadLibraryA("4ed_app.dll"); - if (win32vars.app_code){ - get_funcs = (App_Get_Functions*) - GetProcAddress(win32vars.app_code, "app_get_functions"); - } - -#else - Data file = system_load_file("4ed_app.dll"); - - if (file.data){ - i32 error; - DLL_Data dll_data; - if (dll_parse_headers(file, &dll_data, &error)){ - Data img; - img.size = dll_total_loaded_size(&dll_data); - img.data = (byte*) - VirtualAlloc((LPVOID)Tbytes(3), img.size, - MEM_COMMIT | MEM_RESERVE, - PAGE_READWRITE); - - dll_load(img, &win32vars.app_dll, file, &dll_data); - - DWORD extra_; - VirtualProtect(img.data + win32vars.app_dll.text_start, - win32vars.app_dll.text_size, - PAGE_EXECUTE_READ, - &extra_); - - get_funcs = (App_Get_Functions*) - dll_load_function(&win32vars.app_dll, "app_get_functions", 17); - } - else{ - // TODO(allen): file loading error - } - - system_free(file.data); - - DUMP((byte*)(Tbytes(3)), Kbytes(400)); - } - else{ - // TODO(allen): file loading error - } - -#endif - - if (get_funcs){ - result = 1; - win32vars.app = get_funcs(); - } - - return result; -} - -internal void -Win32LoadSystemCode(){ - win32vars.system->file_time_stamp = system_file_time_stamp; - win32vars.system->set_file_list = system_set_file_list; - - win32vars.system->directory_has_file = system_directory_has_file; - win32vars.system->directory_cd = system_directory_cd; - - win32vars.system->post_clipboard = system_post_clipboard; - win32vars.system->time = system_time; - - win32vars.system->cli_call = system_cli_call; - win32vars.system->cli_begin_update = system_cli_begin_update; - win32vars.system->cli_update_step = system_cli_update_step; - win32vars.system->cli_end_update = system_cli_end_update; - - win32vars.system->post_job = system_post_job; - win32vars.system->cancel_job = system_cancel_job; - win32vars.system->grow_thread_memory = system_grow_thread_memory; - win32vars.system->acquire_lock = system_acquire_lock; - win32vars.system->release_lock = system_release_lock; - - win32vars.system->internal_sentinel = INTERNAL_system_sentinel; - win32vars.system->internal_get_thread_states = INTERNAL_get_thread_states; - win32vars.system->internal_debug_message = INTERNAL_system_debug_message; -} - -#include "system_shared.cpp" -#include "4ed_rendering.cpp" - -internal void -Win32RedrawScreen(HDC hdc){ - system_acquire_lock(RENDER_LOCK); - launch_rendering(&win32vars.target); - system_release_lock(RENDER_LOCK); - glFlush(); - SwapBuffers(hdc); -} - -void -ex__file_insert(File_Slot *pos, File_Slot *file){ - file->next = pos->next; - file->next->prev = file; - file->prev = pos; - pos->next = file; -} - -void -ex__insert_range(File_Slot *start, File_Slot *end, File_Slot *pos){ - end->next->prev = start->prev; - start->prev->next = end->next; - - end->next = pos->next; - start->prev = pos; - pos->next->prev = end; - pos->next = start; -} - -internal void -ex__check_file(File_Slot *pos){ - File_Slot *file = pos; - - Assert(pos == pos->next->prev); - - for (pos = pos->next; - file != pos; - pos = pos->next){ - Assert(pos == pos->next->prev); - } -} - -internal void -ex__check(File_Exchange *file_exchange){ - ex__check_file(&file_exchange->available); - ex__check_file(&file_exchange->active); - ex__check_file(&file_exchange->free_list); -} - -internal LRESULT -Win32Callback(HWND hwnd, UINT uMsg, - WPARAM wParam, LPARAM lParam){ - LRESULT result = {}; - switch (uMsg){ - case WM_MENUCHAR: - case WM_SYSCHAR:break; - - case WM_SYSKEYDOWN: - case WM_SYSKEYUP: - case WM_KEYDOWN: - case WM_KEYUP: - { - switch (wParam){ - case VK_CONTROL:case VK_LCONTROL:case VK_RCONTROL: - case VK_MENU:case VK_LMENU:case VK_RMENU: - case VK_SHIFT:case VK_LSHIFT:case VK_RSHIFT: - { - Control_Keys *controls = 0; - b8 *control_keys = 0; - controls = &win32vars.input_chunk.pers.controls; - control_keys = win32vars.input_chunk.pers.control_keys; - - system_acquire_lock(INPUT_LOCK); - - b8 down = ((lParam & Bit_31)?(0):(1)); - b8 is_right = ((lParam & Bit_24)?(1):(0)); - - if (wParam != 255){ - switch (wParam){ - case VK_SHIFT: - { - control_keys[CONTROL_KEY_SHIFT] = down; - }break; - - case VK_CONTROL: - { - if (is_right) controls->r_ctrl = down; - else controls->l_ctrl = down; - }break; - - case VK_MENU: - { - if (is_right) controls->r_alt = down; - else controls->l_alt = down; - }break; - } - - b8 ctrl, alt; - ctrl = (controls->r_ctrl || (controls->l_ctrl && !controls->r_alt)); - alt = (controls->l_alt || (controls->r_alt && !controls->l_ctrl)); - - if (win32vars.lctrl_lalt_is_altgr){ - if (controls->l_alt && controls->l_ctrl){ - ctrl = 0; - alt = 0; - } - } - - control_keys[CONTROL_KEY_CONTROL] = ctrl; - control_keys[CONTROL_KEY_ALT] = alt; - } - system_release_lock(INPUT_LOCK); - }break; - - default: - b8 previous_state, current_state; - previous_state = ((lParam & Bit_30)?(1):(0)); - current_state = ((lParam & Bit_31)?(0):(1)); - - if (current_state){ - u8 key = keycode_lookup((u8)wParam); - - i32 *count = 0; - Key_Event_Data *data = 0; - b8 *control_keys = 0; - i32 control_keys_size = 0; - - system_acquire_lock(INPUT_LOCK); - if (!previous_state){ - count = &win32vars.input_chunk.trans.key_data.press_count; - data = win32vars.input_chunk.trans.key_data.press; - } - else{ - count = &win32vars.input_chunk.trans.key_data.hold_count; - data = win32vars.input_chunk.trans.key_data.hold; - } - control_keys = win32vars.input_chunk.pers.control_keys; - control_keys_size = sizeof(win32vars.input_chunk.pers.control_keys); - - if (*count < KEY_INPUT_BUFFER_SIZE){ - if (!key){ - UINT vk = (UINT)wParam; - UINT scan = (UINT)((lParam >> 16) & 0x7F); - BYTE state[256]; - WORD x; - int result; - - GetKeyboardState(state); - if (control_keys[CONTROL_KEY_CONTROL] && - !control_keys[CONTROL_KEY_ALT]) - state[VK_CONTROL] = 0; - x = 0; - result = ToAscii(vk, scan, state, &x, 0); - if (result == 1 && x < 128){ - key = (u8)x; - if (key == '\r') key = '\n'; - data[*count].character = key; - - state[VK_CAPITAL] = 0; - x = 0; - result = ToAscii(vk, scan, state, &x, 0); - if (result == 1 && x < 128){ - key = (u8)x; - if (key == '\r') key = '\n'; - data[*count].character_no_caps_lock = key; - data[*count].keycode = key; - } - } - if (result != 1 || x >= 128){ - data[*count].character = 0; - data[*count].character_no_caps_lock = 0; - data[*count].keycode = 0; - } - } - else{ - data[*count].character = 0; - data[*count].character_no_caps_lock = 0; - data[*count].keycode = key; - } - memcpy(data[*count].modifiers, control_keys, control_keys_size); - ++(*count); - } - } - system_release_lock(INPUT_LOCK); - - result = DefWindowProc(hwnd, uMsg, wParam, lParam); - } - }break; - - case WM_INPUT: - - - case WM_MOUSEMOVE: - { - system_acquire_lock(INPUT_LOCK); - win32vars.input_chunk.pers.mouse_x = LOWORD(lParam); - win32vars.input_chunk.pers.mouse_y = HIWORD(lParam); - system_release_lock(INPUT_LOCK); - }break; - - case WM_MOUSEWHEEL: - { - system_acquire_lock(INPUT_LOCK); - i16 rotation = GET_WHEEL_DELTA_WPARAM(wParam); - if (rotation > 0){ - win32vars.input_chunk.trans.mouse_wheel = 1; - } - else{ - win32vars.input_chunk.trans.mouse_wheel = -1; - } - system_release_lock(INPUT_LOCK); - }break; - - case WM_LBUTTONDOWN: - { - system_acquire_lock(INPUT_LOCK); - win32vars.input_chunk.trans.mouse_l_press = 1; - win32vars.input_chunk.pers.mouse_l = 1; - system_release_lock(INPUT_LOCK); - }break; - - case WM_RBUTTONDOWN: - { - system_acquire_lock(INPUT_LOCK); - win32vars.input_chunk.trans.mouse_r_press = 1; - win32vars.input_chunk.pers.mouse_r = 1; - system_release_lock(INPUT_LOCK); - }break; - - case WM_LBUTTONUP: - { - system_acquire_lock(INPUT_LOCK); - win32vars.input_chunk.trans.mouse_l_release = 1; - win32vars.input_chunk.pers.mouse_l = 0; - system_release_lock(INPUT_LOCK); - }break; - - case WM_RBUTTONUP: - { - system_acquire_lock(INPUT_LOCK); - win32vars.input_chunk.trans.mouse_r_release = 1; - win32vars.input_chunk.pers.mouse_r = 0; - system_release_lock(INPUT_LOCK); - }break; - - case WM_KILLFOCUS: - case WM_SETFOCUS: - { - system_acquire_lock(INPUT_LOCK); - win32vars.input_chunk.pers.mouse_l = 0; - win32vars.input_chunk.pers.mouse_r = 0; - - b8 *control_keys = win32vars.input_chunk.pers.control_keys; - for (int i = 0; i < CONTROL_KEY_COUNT; ++i) control_keys[i] = 0; - win32vars.input_chunk.pers.controls = {}; - - system_release_lock(INPUT_LOCK); - }break; - - case WM_SIZE: - { - if (win32vars.target.handle){ - i32 new_width = LOWORD(lParam); - i32 new_height = HIWORD(lParam); - - Win32Resize(new_width, new_height); - win32vars.input_chunk.trans.redraw = 1; - } - }break; - - case WM_PAINT: - { - PAINTSTRUCT ps; - HDC hdc = BeginPaint(hwnd, &ps); - Win32RedrawScreen(hdc); - EndPaint(hwnd, &ps); - - }break; - - case WM_4coder_PAINT: - { - PAINTSTRUCT ps; - HDC hdc = BeginPaint(hwnd, &ps); - Win32RedrawScreen(hdc); - EndPaint(hwnd, &ps); - }break; - - case WM_4coder_SET_CURSOR: - { - switch (wParam){ - case APP_MOUSE_CURSOR_ARROW: - SetCursor(win32vars.cursor_arrow); break; - - case APP_MOUSE_CURSOR_IBEAM: - SetCursor(win32vars.cursor_ibeam); break; - - case APP_MOUSE_CURSOR_LEFTRIGHT: - SetCursor(win32vars.cursor_leftright); break; - - case APP_MOUSE_CURSOR_UPDOWN: - SetCursor(win32vars.cursor_updown); break; - } - }break; - - case WM_CLOSE: // NOTE(allen): I expect WM_CLOSE not WM_DESTROY - case WM_DESTROY: - { - system_acquire_lock(INPUT_LOCK); - win32vars.input_chunk.pers.keep_playing = 0; - system_release_lock(INPUT_LOCK); - }break; - - case WM_4coder_LOAD_FONT: - { - if (win32vars.fnt_part.base == 0){ - win32vars.fnt_part = Win32ScratchPartition(Mbytes(8)); - } - - Win32_Font_Load_Parameters *params = win32vars.fnt_params + lParam; - - for (b32 success = 0; success == 0;){ - - success = draw_font_load(win32vars.fnt_part.base, - win32vars.fnt_part.max, - params->font_out, - params->filename, - params->pt_size, - params->tab_width); - - if (!success){ - Win32ScratchPartitionDouble(&win32vars.fnt_part); - } - } - - system_acquire_lock(FONT_LOCK); - fnt__remove(params); - fnt__insert(&win32vars.free_font_param, params); - system_release_lock(FONT_LOCK); - }break; - - default: - { - result = DefWindowProc(hwnd, uMsg, wParam, lParam); - }break; - } - return result; -} - -DWORD -UpdateLoop(LPVOID param){ - for (;win32vars.input_chunk.pers.keep_playing;){ - i64 timer_start = system_time(); - - system_acquire_lock(INPUT_LOCK); - Win32_Input_Chunk input_chunk = win32vars.input_chunk; - win32vars.input_chunk.trans = {}; - system_release_lock(INPUT_LOCK); - - input_chunk.pers.control_keys[CONTROL_KEY_CAPS] = GetKeyState(VK_CAPITAL) & 0x1; - - POINT mouse_point; - if (GetCursorPos(&mouse_point) && ScreenToClient(win32vars.window_handle, &mouse_point)){ - if (mouse_point.x < 0 || mouse_point.x >= win32vars.target.width || - mouse_point.y < 0 || mouse_point.y >= win32vars.target.height){ - input_chunk.trans.out_of_window = 1; - } - } - else{ - input_chunk.trans.out_of_window = 1; - } - - win32vars.clipboard_contents = {}; - if (win32vars.clipboard_sequence != 0){ - DWORD new_number = GetClipboardSequenceNumber(); - if (new_number != win32vars.clipboard_sequence){ - win32vars.clipboard_sequence = new_number; - if (win32vars.next_clipboard_is_self){ - win32vars.next_clipboard_is_self = 0; - } - else if (IsClipboardFormatAvailable(CF_TEXT)){ - if (OpenClipboard(win32vars.window_handle)){ - HANDLE clip_data; - clip_data = GetClipboardData(CF_TEXT); - if (clip_data){ - win32vars.clipboard_contents.str = (u8*)GlobalLock(clip_data); - if (win32vars.clipboard_contents.str){ - win32vars.clipboard_contents.size = str_size((char*)win32vars.clipboard_contents.str); - GlobalUnlock(clip_data); - } - } - CloseClipboard(); - } - } - } - } - - u32 redraw = exchange_vars.thread.force_redraw; - if (redraw) exchange_vars.thread.force_redraw = 0; - redraw = redraw || input_chunk.trans.redraw; - - - Key_Input_Data input_data; - Mouse_State mouse; - Application_Step_Result result; - - input_data = input_chunk.trans.key_data; - mouse.out_of_window = input_chunk.trans.out_of_window; - - mouse.left_button = input_chunk.pers.mouse_l; - mouse.left_button_pressed = input_chunk.trans.mouse_l_press; - mouse.left_button_released = input_chunk.trans.mouse_l_release; - - mouse.right_button = input_chunk.pers.mouse_r; - mouse.right_button_pressed = input_chunk.trans.mouse_r_press; - mouse.right_button_released = input_chunk.trans.mouse_r_release; - - mouse.wheel = input_chunk.trans.mouse_wheel; - - mouse.x = input_chunk.pers.mouse_x; - mouse.y = input_chunk.pers.mouse_y; - - result.mouse_cursor_type = APP_MOUSE_CURSOR_DEFAULT; - result.redraw = redraw; - result.lctrl_lalt_is_altgr = win32vars.lctrl_lalt_is_altgr; - - win32vars.app.step(win32vars.system, - &win32vars.key_codes, - &input_data, - &mouse, - &win32vars.target, - &memory_vars, - &exchange_vars, - win32vars.clipboard_contents, - 1, win32vars.first, redraw, - &result); - - ProfileStart(OS_frame_out); - - Win32SetCursorFromUpdate(result.mouse_cursor_type); - win32vars.lctrl_lalt_is_altgr = result.lctrl_lalt_is_altgr; - - if (result.redraw) Win32RedrawFromUpdate(); - - win32vars.first = 0; - - ProfileEnd(OS_frame_out); - - ProfileStart(OS_file_process); - { - File_Slot *file; - int d = 0; - - for (file = exchange_vars.file.active.next; - file != &exchange_vars.file.active; - file = file->next){ - ++d; - - if (file->flags & FEx_Save){ - Assert((file->flags & FEx_Request) == 0); - file->flags &= (~FEx_Save); - if (system_save_file(file->filename, file->data, file->size)){ - file->flags |= FEx_Save_Complete; - } - else{ - file->flags |= FEx_Save_Failed; - } - } - - if (file->flags & FEx_Request){ - Assert((file->flags & FEx_Save) == 0); - file->flags &= (~FEx_Request); - Data sysfile = - system_load_file(file->filename); - if (sysfile.data == 0){ - file->flags |= FEx_Not_Exist; - } - else{ - file->flags |= FEx_Ready; - file->data = sysfile.data; - file->size = sysfile.size; - } - } - } - - Assert(d == exchange_vars.file.num_active); - - for (file = exchange_vars.file.free_list.next; - file != &exchange_vars.file.free_list; - file = file->next){ - if (file->data){ - system_free_memory(file->data); - } - } - - if (exchange_vars.file.free_list.next != &exchange_vars.file.free_list){ - ex__insert_range(exchange_vars.file.free_list.next, exchange_vars.file.free_list.prev, - &exchange_vars.file.available); - } - - ex__check(&exchange_vars.file); - } - ProfileEnd(OS_file_process); - - ProfileStart(frame_sleep); - i64 timer_end = system_time(); - i64 end_target = (timer_start + frame_useconds); - - system_release_lock(FRAME_LOCK); - while (timer_end < end_target){ - DWORD samount = (DWORD)((end_target - timer_end) / 1000); - if (samount > 0) Sleep(samount); - timer_end = system_time(); - } - system_acquire_lock(FRAME_LOCK); - timer_start = system_time(); - ProfileEnd(frame_sleep); - } - - return(0); -} - -#if 0 -int -WinMain(HINSTANCE hInstance, - HINSTANCE hPrevInstance, - LPSTR lpCmdLine, - int nCmdShow){ -#else -int -main(int argc, char **argv){ -#endif - HINSTANCE hInstance = GetModuleHandle(0); - - win32vars = {}; - exchange_vars = {}; - -#if FRED_INTERNAL - win32vars.internal_bubble.next = &win32vars.internal_bubble; - win32vars.internal_bubble.prev = &win32vars.internal_bubble; - win32vars.internal_bubble.flags = MEM_BUBBLE_SYS_DEBUG; -#endif - - if (!Win32LoadAppCode()){ - // TODO(allen): Failed to load app code, serious problem. - return 99; - } - - System_Functions system_; - System_Functions *system = &system_; - win32vars.system = system; - Win32LoadSystemCode(); - - LPVOID base; -#if FRED_INTERNAL - base = (LPVOID)Tbytes(1); -#else - base = (LPVOID)0; -#endif - - memory_vars.vars_memory_size = Mbytes(2); - memory_vars.vars_memory = VirtualAlloc(base, memory_vars.vars_memory_size, - MEM_COMMIT | MEM_RESERVE, - PAGE_READWRITE); - -#if FRED_INTERNAL - base = (LPVOID)Tbytes(2); -#else - base = (LPVOID)0; -#endif - memory_vars.target_memory_size = Mbytes(512); - memory_vars.target_memory = VirtualAlloc(base, memory_vars.target_memory_size, - MEM_COMMIT | MEM_RESERVE, - PAGE_READWRITE); - // - - if (!memory_vars.vars_memory){ - return 4; - } - - DWORD required = GetCurrentDirectory(0, 0); - required += 1; - required *= 4; - char *current_directory_mem = (char*)system_get_memory(required); - DWORD written = GetCurrentDirectory(required, current_directory_mem); - - String current_directory = make_string(current_directory_mem, written, required); - terminate_with_null(¤t_directory); - - Command_Line_Parameters clparams; - clparams.argv = argv; - clparams.argc = argc; - - char **files; - i32 *file_count; - - files = 0; - file_count = 0; - - i32 output_size = - win32vars.app.read_command_line(system, - &memory_vars, - current_directory, - &win32vars.settings, - &files, &file_count, - clparams); - // - - if (output_size > 0){ - // TODO(allen): crt free version - printf("%.*s", output_size, memory_vars.target_memory); - } - if (output_size != 0) return 0; - FreeConsole(); - - system_filter_real_files(files, file_count); - - LARGE_INTEGER lpf; - QueryPerformanceFrequency(&lpf); - win32vars.performance_frequency = lpf.QuadPart; - QueryPerformanceCounter(&lpf); - win32vars.start_pcount = lpf.QuadPart; - - FILETIME filetime; - GetSystemTimeAsFileTime(&filetime); - win32vars.start_time = ((u64)filetime.dwHighDateTime << 32) | (filetime.dwLowDateTime); - win32vars.start_time /= 10; - - keycode_init(&win32vars.key_codes); - -#ifdef FRED_SUPER - char *custom_file_default = "4coder_custom.dll"; - char *custom_file; - if (win32vars.settings.custom_dll) custom_file = win32vars.settings.custom_dll; - else custom_file = custom_file_default; - - win32vars.custom = LoadLibraryA(custom_file); - if (!win32vars.custom && custom_file != custom_file_default){ - if (!win32vars.settings.custom_dll_is_strict){ - win32vars.custom = LoadLibraryA(custom_file_default); - } - } - - if (win32vars.custom){ - win32vars.custom_api.get_bindings = (Get_Binding_Data_Function*) - GetProcAddress(win32vars.custom, "get_bindings"); - } -#endif - - Thread_Context background[4]; - memset(background, 0, sizeof(background)); - win32vars.groups[BACKGROUND_THREADS].threads = background; - win32vars.groups[BACKGROUND_THREADS].count = ArrayCount(background); - - Thread_Memory thread_memory[ArrayCount(background)]; - win32vars.thread_memory = thread_memory; - - exchange_vars.thread.queues[BACKGROUND_THREADS].semaphore = - Win32GenHandle( - CreateSemaphore(0, 0, win32vars.groups[BACKGROUND_THREADS].count, 0) - ); - - u32 creation_flag = 0; - for (i32 i = 0; i < win32vars.groups[BACKGROUND_THREADS].count; ++i){ - Thread_Context *thread = win32vars.groups[BACKGROUND_THREADS].threads + i; - thread->id = i + 1; - - Thread_Memory *memory = win32vars.thread_memory + i; - *memory = {}; - memory->id = thread->id; - - thread->queue = &exchange_vars.thread.queues[BACKGROUND_THREADS]; - thread->handle = CreateThread(0, 0, ThreadProc, thread, creation_flag, (LPDWORD)&thread->windows_id); - } - - Assert(win32vars.locks); - for (i32 i = 0; i < LOCK_COUNT; ++i){ - win32vars.locks[i] = CreateSemaphore(0, 1, 1, 0); - } - win32vars.DEBUG_sysmem_lock = CreateSemaphore(0, 1, 1, 0); - - win32vars.cursor_ibeam = LoadCursor(NULL, IDC_IBEAM); - win32vars.cursor_arrow = LoadCursor(NULL, IDC_ARROW); - win32vars.cursor_leftright = LoadCursor(NULL, IDC_SIZEWE); - win32vars.cursor_updown = LoadCursor(NULL, IDC_SIZENS); - win32vars.prev_mouse_cursor = APP_MOUSE_CURSOR_ARROW; - - WNDCLASS window_class = {}; - window_class.style = CS_HREDRAW|CS_VREDRAW|CS_OWNDC; - window_class.lpfnWndProc = Win32Callback; - window_class.hInstance = hInstance; - window_class.lpszClassName = "4coder-win32-wndclass"; - - if (!RegisterClass(&window_class)){ - return 1; - } - - RECT window_rect = {}; - - if (win32vars.settings.set_window_size){ - window_rect.right = win32vars.settings.window_w; - window_rect.bottom = win32vars.settings.window_h; - } - else{ - window_rect.right = 800; - window_rect.bottom = 600; - } - - if (!AdjustWindowRect(&window_rect, WS_OVERLAPPEDWINDOW, false)){ - // TODO(allen): non-fatal diagnostics - } - -#define WINDOW_NAME "4coder-window" - - i32 window_x; - i32 window_y; - i32 window_style; - - if (win32vars.settings.set_window_pos){ - window_x = win32vars.settings.window_x; - window_y = win32vars.settings.window_y; - } - else{ - window_x = CW_USEDEFAULT; - window_y = CW_USEDEFAULT; - } - - window_style = WS_OVERLAPPEDWINDOW | WS_VISIBLE; - if (win32vars.settings.maximize_window){ - window_style |= WS_MAXIMIZE; - } - - HWND window_handle = {}; - window_handle = CreateWindowA( - window_class.lpszClassName, - WINDOW_NAME, window_style, - window_x, window_y, - window_rect.right - window_rect.left, - window_rect.bottom - window_rect.top, - 0, 0, hInstance, 0); - - if (window_handle == 0){ - return 2; - } - - // TODO(allen): errors? - win32vars.window_handle = window_handle; - HDC hdc = GetDC(window_handle); - win32vars.window_hdc = hdc; - - GetClientRect(window_handle, &window_rect); - - static PIXELFORMATDESCRIPTOR pfd = { - sizeof(PIXELFORMATDESCRIPTOR), - 1, - PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, - PFD_TYPE_RGBA, - 32, - 0, 0, 0, 0, 0, 0, - 0, - 0, - 0, - 0, 0, 0, 0, - 16, - 0, - 0, - PFD_MAIN_PLANE, - 0, - 0, 0, 0 }; - - i32 pixel_format; - pixel_format = ChoosePixelFormat(hdc, &pfd); - SetPixelFormat(hdc, pixel_format, &pfd); - - win32vars.target.handle = hdc; - win32vars.target.context = wglCreateContext(hdc); - wglMakeCurrent(hdc, (HGLRC)win32vars.target.context); - - glEnable(GL_TEXTURE_2D); - glEnable(GL_SCISSOR_TEST); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - Win32Resize(window_rect.right - window_rect.left, window_rect.bottom - window_rect.top); - - win32vars.clipboard_sequence = GetClipboardSequenceNumber(); - - if (win32vars.clipboard_sequence == 0){ - system_post_clipboard(make_lit_string("")); - - win32vars.clipboard_sequence = GetClipboardSequenceNumber(); - win32vars.next_clipboard_is_self = 0; - - if (win32vars.clipboard_sequence == 0){ - // TODO(allen): diagnostics - } - } - - else{ - if (IsClipboardFormatAvailable(CF_TEXT)){ - if (OpenClipboard(win32vars.window_handle)){ - HANDLE clip_data; - clip_data = GetClipboardData(CF_TEXT); - if (clip_data){ - win32vars.clipboard_contents.str = (u8*)GlobalLock(clip_data); - if (win32vars.clipboard_contents.str){ - win32vars.clipboard_contents.size = str_size((char*)win32vars.clipboard_contents.str); - GlobalUnlock(clip_data); - } - } - CloseClipboard(); - } - } - } - - win32vars.target.push_clip = draw_push_clip; - win32vars.target.pop_clip = draw_pop_clip; - win32vars.target.push_piece = draw_push_piece; - - win32vars.target.font_set.font_info_load = draw_font_info_load; - win32vars.target.font_set.font_load = system_draw_font_load; - win32vars.target.font_set.release_font = draw_release_font; - - win32vars.target.max = Mbytes(1); - win32vars.target.push_buffer = (byte*)Win32GetMemory(win32vars.target.max); - - File_Slot file_slots[32]; - exchange_vars.file.max = sizeof(file_slots) / sizeof(file_slots[0]); - exchange_vars.file.available = {}; - exchange_vars.file.available.next = &exchange_vars.file.available; - exchange_vars.file.available.prev = &exchange_vars.file.available; - - exchange_vars.file.active = {}; - exchange_vars.file.active.next = &exchange_vars.file.active; - exchange_vars.file.active.prev = &exchange_vars.file.active; - - exchange_vars.file.free_list = {}; - exchange_vars.file.free_list.next = &exchange_vars.file.free_list; - exchange_vars.file.free_list.prev = &exchange_vars.file.free_list; - - exchange_vars.file.files = file_slots; - memset(file_slots, 0, sizeof(file_slots)); - - char *filename_space = (char*) - Win32GetMemory(FileNameMax*exchange_vars.file.max); - - for (int i = 0; i < exchange_vars.file.max; ++i){ - File_Slot *slot = file_slots + i; - ex__file_insert(&exchange_vars.file.available, slot); - slot->filename = filename_space; - filename_space += FileNameMax; - } - - win32vars.free_font_param.next = &win32vars.free_font_param; - win32vars.free_font_param.prev = &win32vars.free_font_param; - - win32vars.used_font_param.next = &win32vars.used_font_param; - win32vars.used_font_param.prev = &win32vars.used_font_param; - - for (i32 i = 0; i < ArrayCount(win32vars.fnt_params); ++i){ - fnt__insert(&win32vars.free_font_param, win32vars.fnt_params + i); - } - - win32vars.app.init(win32vars.system, &win32vars.target, - &memory_vars, &exchange_vars, &win32vars.key_codes, - win32vars.clipboard_contents, current_directory, - win32vars.custom_api); - - system_free_memory(current_directory.str); - - win32vars.input_chunk.pers.keep_playing = 1; - win32vars.first = 1; - timeBeginPeriod(1); - - win32vars.update_loop_thread = - CreateThread(0, - 0, - UpdateLoop, - 0, - CREATE_SUSPENDED, - &win32vars.update_loop_thread_id); - - system_acquire_lock(FRAME_LOCK); - - ResumeThread(win32vars.update_loop_thread); - - MSG msg; - for (;win32vars.input_chunk.pers.keep_playing && GetMessage(&msg, 0, 0, 0);){ - if (msg.message == WM_QUIT){ - system_acquire_lock(INPUT_LOCK); - win32vars.input_chunk.pers.keep_playing = 0; - system_release_lock(INPUT_LOCK); - }else{ - TranslateMessage(&msg); - DispatchMessage(&msg); - } - } - - return 0; -} - -// BOTTOM - - +/* + * Mr. 4th Dimention - Allen Webster + * + * 12.12.2014 + * + * Win32 layer for project codename "4ed" + * + */ + +// TOP + +#include "4ed_config.h" + +#include "4ed_meta.h" + +#define FCPP_FORBID_MALLOC + +#include "4cpp_types.h" +#define FCPP_STRING_IMPLEMENTATION +#include "4coder_string.h" + +#include "4ed_mem.cpp" +#include "4ed_math.cpp" + +#include "4ed_dll_reader.h" + +#include "4coder_custom.h" +#include "4ed_system.h" +#include "4ed_rendering.h" +#include "4ed.h" + +#include +#include + +#include "4ed_dll_reader.cpp" +#include "4ed_internal.h" +#include "4ed_win32_keyboard.cpp" +#include "system_shared.h" + +#define FPS 30 +#define frame_useconds (1000000 / FPS) + +#define WM_4coder_LOAD_FONT (WM_USER + 1) +#define WM_4coder_PAINT (WM_USER + 2) +#define WM_4coder_SET_CURSOR (WM_USER + 3) + +struct Thread_Context{ + u32 job_id; + b32 running; + + Work_Queue *queue; + u32 id; + u32 windows_id; + HANDLE handle; +}; + +struct Thread_Group{ + Thread_Context *threads; + i32 count; +}; + +#define UseWinDll 1 + +struct Control_Keys{ + b8 l_ctrl; + b8 r_ctrl; + b8 l_alt; + b8 r_alt; +}; + +struct Win32_Input_Chunk_Transient{ + Key_Input_Data key_data; + + b8 mouse_l_press, mouse_l_release; + b8 mouse_r_press, mouse_r_release; + b32 out_of_window; + i16 mouse_wheel; + + b32 redraw; +}; + +struct Win32_Input_Chunk_Persistent{ + i32 mouse_x, mouse_y; + b8 mouse_l, mouse_r; + + b8 keep_playing; + + Control_Keys controls; + b8 control_keys[CONTROL_KEY_COUNT]; +}; + +struct Win32_Input_Chunk{ + Win32_Input_Chunk_Transient trans; + Win32_Input_Chunk_Persistent pers; +}; + +struct Win32_Vars{ + HWND window_handle; + HDC window_hdc; + Render_Target target; + + HANDLE update_loop_thread; + DWORD update_loop_thread_id; + + Key_Codes key_codes; + Win32_Input_Chunk input_chunk; + b32 lctrl_lalt_is_altgr; + + HCURSOR cursor_ibeam; + HCURSOR cursor_arrow; + HCURSOR cursor_leftright; + HCURSOR cursor_updown; + Application_Mouse_Cursor prev_mouse_cursor; + Clipboard_Contents clipboard_contents; + b32 next_clipboard_is_self; + DWORD clipboard_sequence; + + Thread_Group groups[THREAD_GROUP_COUNT]; + HANDLE locks[LOCK_COUNT]; + HANDLE DEBUG_sysmem_lock; + + Thread_Memory *thread_memory; + + u64 performance_frequency; + u64 start_pcount; + u64 start_time; + +#if UseWinDll + HMODULE app_code; + HMODULE custom; +#else + DLL_Loaded app_dll; + DLL_Loaded custom_dll; +#endif + + Plat_Settings settings; + System_Functions *system; + App_Functions app; + Custom_API custom_api; + b32 first; + +#if FRED_INTERNAL + Sys_Bubble internal_bubble; +#endif + + Font_Load_System fnt; +}; + +globalvar Win32_Vars win32vars; +globalvar Application_Memory memory_vars; +globalvar Exchange exchange_vars; + +#if FRED_INTERNAL +internal Bubble* +INTERNAL_system_sentinel(){ + return (&win32vars.internal_bubble); +} + +internal void +INTERNAL_system_debug_message(char *message){ + OutputDebugString(message); +} + +#endif + +// TODO(allen): Transition towards using system_shared functions + +internal void* +Win32GetMemory_(i32 size, i32 line_number, char *file_name){ + void *ptr = 0; + +#if FRED_INTERNAL + ptr = VirtualAlloc(0, size + sizeof(Sys_Bubble), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + + Sys_Bubble *bubble = (Sys_Bubble*)ptr; + bubble->flags = MEM_BUBBLE_SYS_DEBUG; + bubble->line_number = line_number; + bubble->file_name = file_name; + bubble->size = size; + WaitForSingleObject(win32vars.DEBUG_sysmem_lock, INFINITE); + insert_bubble(&win32vars.internal_bubble, bubble); + ReleaseSemaphore(win32vars.DEBUG_sysmem_lock, 1, 0); + ptr = bubble + 1; +#else + ptr = VirtualAlloc(0, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); +#endif + + return ptr; +} + +internal +Sys_Get_Memory_Sig(system_get_memory_){ + return(Win32GetMemory_(size, line_number, file_name)); +} + +#define Win32GetMemory(size) Win32GetMemory_(size, __LINE__, __FILE__) + +internal void +Win32FreeMemory(void *block){ + if (block){ +#if FRED_INTERNAL + Sys_Bubble *bubble = ((Sys_Bubble*)block) - 1; + Assert((bubble->flags & MEM_BUBBLE_DEBUG_MASK) == MEM_BUBBLE_SYS_DEBUG); + WaitForSingleObject(win32vars.DEBUG_sysmem_lock, INFINITE); + remove_bubble(bubble); + ReleaseSemaphore(win32vars.DEBUG_sysmem_lock, 1, 0); + VirtualFree(bubble, 0, MEM_RELEASE); +#else + VirtualFree(block, 0, MEM_RELEASE); +#endif + } +} + +internal Partition +Win32ScratchPartition(i32 size){ + Partition part; + void *data; + data = Win32GetMemory(size); + part = partition_open(data, size); + return(part); +} + +internal void +Win32ScratchPartitionGrow(Partition *part, i32 new_size){ + void *data; + if (new_size > part->max){ + data = Win32GetMemory(new_size); + memcpy(data, part->base, part->pos); + Win32FreeMemory(part->base); + part->base = (u8*)data; + } +} + +internal void +Win32ScratchPartitionDouble(Partition *part){ + Win32ScratchPartitionGrow(part, part->max*2); +} + +inline void +system_free_memory(void *block){ + Win32FreeMemory(block); +} + +internal Data +system_load_file(char *filename){ + Data result = {}; + HANDLE file; + file = CreateFile((char*)filename, GENERIC_READ, 0, 0, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + if (!file || file == INVALID_HANDLE_VALUE){ + return result; + } + + DWORD lo, hi; + lo = GetFileSize(file, &hi); + + if (hi != 0){ + CloseHandle(file); + return result; + } + + result.size = (lo) + (((u64)hi) << 32); + result.data = (byte*)Win32GetMemory(result.size); + + if (!result.data){ + CloseHandle(file); + result = {}; + return result; + } + + DWORD read_size; + BOOL read_result = ReadFile(file, result.data, result.size, + &read_size, 0); + if (!read_result || read_size != (u32)result.size){ + CloseHandle(file); + Win32FreeMemory(result.data); + result = {}; + return result; + } + + CloseHandle(file); + return result; +} + +internal b32 +system_save_file(char *filename, void *data, i32 size){ + HANDLE file; + file = CreateFile((char*)filename, GENERIC_WRITE, 0, 0, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); + + if (!file || file == INVALID_HANDLE_VALUE){ + return 0; + } + + BOOL write_result; + DWORD bytes_written; + write_result = WriteFile(file, data, size, &bytes_written, 0); + + CloseHandle(file); + + if (!write_result || bytes_written != (u32)size){ + return 0; + } + + return 1; +} + +internal b32 +system_file_can_be_made(char *filename){ + HANDLE file; + file = CreateFile((char*)filename, FILE_APPEND_DATA, 0, 0, + OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); + + if (!file || file == INVALID_HANDLE_VALUE){ + return 0; + } + + CloseHandle(file); + + return 1; +} + +internal +Sys_File_Time_Stamp_Sig(system_file_time_stamp){ + u64 result; + result = 0; + + FILETIME last_write; + WIN32_FILE_ATTRIBUTE_DATA data; + if (GetFileAttributesEx((char*)filename, GetFileExInfoStandard, &data)){ + last_write = data.ftLastWriteTime; + + result = ((u64)last_write.dwHighDateTime << 32) | (last_write.dwLowDateTime); + result /= 10; + } + + return result; +} + +internal +Sys_Time_Sig(system_time){ + u64 result = 0; + LARGE_INTEGER time; + if (QueryPerformanceCounter(&time)){ + result = (u64)(time.QuadPart - win32vars.start_pcount) * 1000000 / win32vars.performance_frequency; + result += win32vars.start_time; + } + return result; +} + +internal +Sys_Set_File_List_Sig(system_set_file_list){ + if (directory.size > 0){ + char dir_space[MAX_PATH + 32]; + String dir = make_string(dir_space, 0, MAX_PATH + 32); + append(&dir, directory); + char trail_str[] = "\\*"; + append(&dir, trail_str); + + char *c_str_dir = make_c_str(dir); + + WIN32_FIND_DATA find_data; + HANDLE search; + search = FindFirstFileA(c_str_dir, &find_data); + + if (search != INVALID_HANDLE_VALUE){ + i32 count = 0; + i32 file_count = 0; + BOOL more_files = 1; + do{ + if (!match(find_data.cFileName, ".") && + !match(find_data.cFileName, "..")){ + ++file_count; + i32 size = 0; + for(;find_data.cFileName[size];++size); + count += size + 1; + } + more_files = FindNextFile(search, &find_data); + }while(more_files); + FindClose(search); + + i32 required_size = count + file_count * sizeof(File_Info); + if (file_list->block_size < required_size){ + Win32FreeMemory(file_list->block); + file_list->block = Win32GetMemory(required_size); + } + + file_list->infos = (File_Info*)file_list->block; + char *name = (char*)(file_list->infos + file_count); + if (file_list->block){ + search = FindFirstFileA(c_str_dir, &find_data); + + if (search != INVALID_HANDLE_VALUE){ + File_Info *info = file_list->infos; + more_files = 1; + do{ + if (!match(find_data.cFileName, ".") && + !match(find_data.cFileName, "..")){ + info->folder = (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; + info->filename.str = name; + + i32 i = 0; + for(;find_data.cFileName[i];++i) *name++ = find_data.cFileName[i]; + info->filename.size = i; + info->filename.memory_size = info->filename.size + 1; + *name++ = 0; + ++info; + } + more_files = FindNextFile(search, &find_data); + }while(more_files); + FindClose(search); + + file_list->count = file_count; + + }else{ + Win32FreeMemory(file_list->block); + file_list->block = 0; + file_list->block_size = 0; + } + } + } + } +} + +internal +DIRECTORY_HAS_FILE_SIG(system_directory_has_file){ + char *full_filename; + char space[1024]; + HANDLE file; + b32 result; + i32 len; + + full_filename = 0; + len = str_size(filename); + if (dir.memory_size - dir.size - 1 >= len){ + full_filename = dir.str; + memcpy(dir.str + dir.size, filename, len + 1); + } + else if (dir.size + len + 1 < 1024){ + full_filename = space; + memcpy(full_filename, dir.str, dir.size); + memcpy(full_filename + dir.size, filename, len + 1); + } + + result = 0; + if (full_filename){ + file = CreateFile((char*)full_filename, GENERIC_READ, 0, 0, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + if (file != INVALID_HANDLE_VALUE){ + CloseHandle(file); + result = 1; + } + dir.str[dir.size] = 0; + } + + return(result); +} + +b32 Win32DirectoryExists(char *path){ + DWORD attrib = GetFileAttributesA(path); + return (attrib != INVALID_FILE_ATTRIBUTES && + (attrib & FILE_ATTRIBUTE_DIRECTORY)); +} + +internal +DIRECTORY_CD_SIG(system_directory_cd){ + b32 result = 0; + i32 old_size; + i32 len; + + if (rel_path[0] != 0){ + if (rel_path[0] == '.' && rel_path[1] == 0){ + result = 1; + } + else if (rel_path[0] == '.' && rel_path[1] == '.' && rel_path[2] == 0){ + result = remove_last_folder(dir); + terminate_with_null(dir); + } + else{ + len = str_size(rel_path); + if (dir->size + len + 1 > dir->memory_size){ + old_size = dir->size; + append_partial(dir, rel_path); + append_partial(dir, "\\"); + if (Win32DirectoryExists(dir->str)){ + result = 1; + } + else{ + dir->size = old_size; + } + } + } + } + + return(result); +} + +internal +Sys_Post_Clipboard_Sig(system_post_clipboard){ + if (OpenClipboard(win32vars.window_handle)){ + EmptyClipboard(); + HANDLE memory_handle; + memory_handle = GlobalAlloc(GMEM_MOVEABLE, str.size+1); + if (memory_handle){ + char *dest = (char*)GlobalLock(memory_handle); + copy_fast_unsafe(dest, str); + GlobalUnlock(memory_handle); + SetClipboardData(CF_TEXT, memory_handle); + win32vars.next_clipboard_is_self = 1; + } + CloseClipboard(); + } +} + +internal +Sys_Acquire_Lock_Sig(system_acquire_lock){ + WaitForSingleObject(win32vars.locks[id], INFINITE); +} + +internal +Sys_Release_Lock_Sig(system_release_lock){ + ReleaseSemaphore(win32vars.locks[id], 1, 0); +} + +internal void +Win32RedrawFromUpdate(){ + SendMessage( + win32vars.window_handle, + WM_4coder_PAINT, + 0, 0); +} + +internal void +Win32SetCursorFromUpdate(Application_Mouse_Cursor cursor){ + SendMessage( + win32vars.window_handle, + WM_4coder_SET_CURSOR, + cursor, 0); +} + +internal void +Win32Resize(i32 width, i32 height){ + if (width > 0 && height > 0){ + glViewport(0, 0, width, height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, width, height, 0, -1, 1); + glScissor(0, 0, width, height); + + win32vars.target.width = width; + win32vars.target.height = height; + } +} + +internal HANDLE +Win32Handle(Plat_Handle h){ + HANDLE result; + result = {}; + result = *(HANDLE*)(&h); + return(result); +} + +internal Plat_Handle +Win32GenHandle(HANDLE h){ + Assert(sizeof(Plat_Handle) >= sizeof(HANDLE)); + Plat_Handle result; + result = *(Plat_Handle*)(&h); + return(result); +} + +internal DWORD WINAPI +ThreadProc(LPVOID lpParameter){ + Thread_Context *thread = (Thread_Context*)lpParameter; + Work_Queue *queue = thread->queue; + + for (;;){ + u32 read_index = queue->read_position; + u32 write_index = queue->write_position; + + if (read_index != write_index){ + u32 next_read_index = (read_index + 1) % JOB_ID_WRAP; + u32 safe_read_index = + InterlockedCompareExchange(&queue->read_position, + next_read_index, read_index); + + if (safe_read_index == read_index){ + Full_Job_Data *full_job = queue->jobs + (safe_read_index % QUEUE_WRAP); + // NOTE(allen): This is interlocked so that it plays nice + // with the cancel job routine, which may try to cancel this job + // at the same time that we try to run it + + i32 safe_running_thread = + InterlockedCompareExchange(&full_job->running_thread, + thread->id, THREAD_NOT_ASSIGNED); + + if (safe_running_thread == THREAD_NOT_ASSIGNED){ + thread->job_id = full_job->id; + thread->running = 1; + Thread_Memory *thread_memory = 0; + + // TODO(allen): remove memory_request + if (full_job->job.memory_request != 0){ + thread_memory = win32vars.thread_memory + thread->id - 1; + if (thread_memory->size < full_job->job.memory_request){ + if (thread_memory->data){ + Win32FreeMemory(thread_memory->data); + } + i32 new_size = LargeRoundUp(full_job->job.memory_request, Kbytes(4)); + thread_memory->data = Win32GetMemory(new_size); + thread_memory->size = new_size; + } + } + full_job->job.callback(win32vars.system, thread, thread_memory, + &exchange_vars.thread, full_job->job.data); + full_job->running_thread = 0; + thread->running = 0; + } + } + } + else{ + WaitForSingleObject(Win32Handle(queue->semaphore), INFINITE); + } + } +} + +internal +Sys_Post_Job_Sig(system_post_job){ + Work_Queue *queue = exchange_vars.thread.queues + group_id; + + Assert((queue->write_position + 1) % QUEUE_WRAP != queue->read_position % QUEUE_WRAP); + + b32 success = 0; + u32 result = 0; + while (!success){ + u32 write_index = queue->write_position; + u32 next_write_index = (write_index + 1) % JOB_ID_WRAP; + u32 safe_write_index = + InterlockedCompareExchange(&queue->write_position, + next_write_index, write_index); + if (safe_write_index == write_index){ + result = write_index; + write_index = write_index % QUEUE_WRAP; + queue->jobs[write_index].job = job; + queue->jobs[write_index].running_thread = THREAD_NOT_ASSIGNED; + queue->jobs[write_index].id = result; + success = 1; + } + } + + ReleaseSemaphore(Win32Handle(queue->semaphore), 1, 0); + + return result; +} + +internal +Sys_Cancel_Job_Sig(system_cancel_job){ + Work_Queue *queue = exchange_vars.thread.queues + group_id; + Thread_Group *group = win32vars.groups + group_id; + + u32 job_index; + u32 thread_id; + Full_Job_Data *full_job; + Thread_Context *thread; + + job_index = job_id % QUEUE_WRAP; + full_job = queue->jobs + job_index; + + Assert(full_job->id == job_id); + thread_id = + InterlockedCompareExchange(&full_job->running_thread, + 0, THREAD_NOT_ASSIGNED); + + if (thread_id != THREAD_NOT_ASSIGNED){ + system_acquire_lock(CANCEL_LOCK0 + thread_id - 1); + thread = group->threads + thread_id - 1; + TerminateThread(thread->handle, 0); + u32 creation_flag = 0; + thread->handle = CreateThread(0, 0, ThreadProc, thread, creation_flag, (LPDWORD)&thread->windows_id); + system_release_lock(CANCEL_LOCK0 + thread_id - 1); + thread->running = 0; + } +} + +internal void +system_grow_thread_memory(Thread_Memory *memory){ + void *old_data; + i32 old_size, new_size; + + system_acquire_lock(CANCEL_LOCK0 + memory->id - 1); + old_data = memory->data; + old_size = memory->size; + new_size = LargeRoundUp(memory->size*2, Kbytes(4)); + memory->data = Win32GetMemory(new_size); + memory->size = new_size; + if (old_data){ + memcpy(memory->data, old_data, old_size); + Win32FreeMemory(old_data); + } + system_release_lock(CANCEL_LOCK0 + memory->id - 1); +} + +#if FRED_INTERNAL +internal void +INTERNAL_get_thread_states(Thread_Group_ID id, bool8 *running, i32 *pending){ + Work_Queue *queue = exchange_vars.thread.queues + id; + u32 write = queue->write_position; + u32 read = queue->read_position; + if (write < read) write += JOB_ID_WRAP; + *pending = (i32)(write - read); + + Thread_Group *group = win32vars.groups + id; + for (i32 i = 0; i < group->count; ++i){ + running[i] = (group->threads[i].running != 0); + } +} +#endif + +internal +Sys_CLI_Call_Sig(system_cli_call){ + char cmd[] = "c:\\windows\\system32\\cmd.exe"; + char *env_variables = 0; + char command_line[2048]; + + b32 success = 1; + String s = make_fixed_width_string(command_line); + copy(&s, make_lit_string("/C ")); + append_partial(&s, script_name); + success = terminate_with_null(&s); + + if (success){ + success = 0; + + SECURITY_ATTRIBUTES sec_attributes; + HANDLE out_read; + HANDLE out_write; + + sec_attributes = {}; + sec_attributes.nLength = sizeof(SECURITY_ATTRIBUTES); + sec_attributes.bInheritHandle = TRUE; + + if (CreatePipe(&out_read, &out_write, &sec_attributes, 0)){ + if (SetHandleInformation(out_read, HANDLE_FLAG_INHERIT, 0)){ + STARTUPINFO startup = {}; + startup.cb = sizeof(STARTUPINFO); + startup.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; + startup.hStdError = out_write; + startup.hStdOutput = out_write; + startup.wShowWindow = SW_HIDE; + + PROCESS_INFORMATION info = {}; + + Assert(sizeof(Plat_Handle) >= sizeof(HANDLE)); + if (CreateProcess(cmd, command_line, + 0, 0, TRUE, 0, + env_variables, path, + &startup, &info)){ + success = 1; + CloseHandle(info.hThread); + *(HANDLE*)&cli_out->proc = info.hProcess; + *(HANDLE*)&cli_out->out_read = out_read; + *(HANDLE*)&cli_out->out_write = out_write; + } + else{ + CloseHandle(out_read); + CloseHandle(out_write); + *(HANDLE*)&cli_out->proc = INVALID_HANDLE_VALUE; + *(HANDLE*)&cli_out->out_read = INVALID_HANDLE_VALUE; + *(HANDLE*)&cli_out->out_write = INVALID_HANDLE_VALUE; + } + } + else{ + // TODO(allen): failed SetHandleInformation + } + } + else{ + // TODO(allen): failed CreatePipe + } + } + + return success; +} + +struct CLI_Loop_Control{ + u32 remaining_amount; +}; + +internal +Sys_CLI_Begin_Update_Sig(system_cli_begin_update){ + Assert(sizeof(cli->scratch_space) >= sizeof(CLI_Loop_Control)); + CLI_Loop_Control *loop = (CLI_Loop_Control*)cli->scratch_space; + loop->remaining_amount = 0; +} + +internal +Sys_CLI_Update_Step_Sig(system_cli_update_step){ + HANDLE handle = *(HANDLE*)&cli->out_read; + CLI_Loop_Control *loop = (CLI_Loop_Control*)cli->scratch_space; + b32 has_more = 0; + DWORD remaining = loop->remaining_amount; + u32 pos = 0; + DWORD read_amount = 0; + + for (;;){ + if (remaining == 0){ + if (!PeekNamedPipe(handle, 0, 0, 0, &remaining, 0)) break; + if (remaining == 0) break; + } + + if (remaining + pos < max){ + has_more = 1; + ReadFile(handle, dest + pos, remaining, &read_amount, 0); + TentativeAssert(remaining == read_amount); + pos += remaining; + remaining = 0; + } + else{ + has_more = 1; + ReadFile(handle, dest + pos, max - pos, &read_amount, 0); + TentativeAssert(max - pos == read_amount); + loop->remaining_amount = remaining - (max - pos); + pos = max; + break; + } + } + *amount = pos; + + return has_more; +} + +internal +Sys_CLI_End_Update_Sig(system_cli_end_update){ + b32 close_me = 0; + HANDLE proc = *(HANDLE*)&cli->proc; + DWORD result = 0; + + if (WaitForSingleObject(proc, 0) == WAIT_OBJECT_0){ + if (GetExitCodeProcess(proc, &result) == 0) + cli->exit = -1; + else + cli->exit = (i32)result; + + close_me = 1; + CloseHandle(*(HANDLE*)&cli->proc); + CloseHandle(*(HANDLE*)&cli->out_read); + CloseHandle(*(HANDLE*)&cli->out_write); + } + return close_me; +} + +internal b32 +Win32LoadAppCode(){ + b32 result = 0; + App_Get_Functions *get_funcs = 0; + +#if UseWinDll + win32vars.app_code = LoadLibraryA("4ed_app.dll"); + if (win32vars.app_code){ + get_funcs = (App_Get_Functions*) + GetProcAddress(win32vars.app_code, "app_get_functions"); + } + +#else + Data file = system_load_file("4ed_app.dll"); + + if (file.data){ + i32 error; + DLL_Data dll_data; + if (dll_parse_headers(file, &dll_data, &error)){ + Data img; + img.size = dll_total_loaded_size(&dll_data); + img.data = (byte*) + VirtualAlloc((LPVOID)Tbytes(3), img.size, + MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE); + + dll_load(img, &win32vars.app_dll, file, &dll_data); + + DWORD extra_; + VirtualProtect(img.data + win32vars.app_dll.text_start, + win32vars.app_dll.text_size, + PAGE_EXECUTE_READ, + &extra_); + + get_funcs = (App_Get_Functions*) + dll_load_function(&win32vars.app_dll, "app_get_functions", 17); + } + else{ + // TODO(allen): file loading error + } + + system_free(file.data); + + DUMP((byte*)(Tbytes(3)), Kbytes(400)); + } + else{ + // TODO(allen): file loading error + } + +#endif + + if (get_funcs){ + result = 1; + win32vars.app = get_funcs(); + } + + return result; +} + +internal void +Win32LoadSystemCode(){ + win32vars.system->file_time_stamp = system_file_time_stamp; + win32vars.system->set_file_list = system_set_file_list; + + win32vars.system->directory_has_file = system_directory_has_file; + win32vars.system->directory_cd = system_directory_cd; + + win32vars.system->post_clipboard = system_post_clipboard; + win32vars.system->time = system_time; + + win32vars.system->cli_call = system_cli_call; + win32vars.system->cli_begin_update = system_cli_begin_update; + win32vars.system->cli_update_step = system_cli_update_step; + win32vars.system->cli_end_update = system_cli_end_update; + + win32vars.system->post_job = system_post_job; + win32vars.system->cancel_job = system_cancel_job; + win32vars.system->grow_thread_memory = system_grow_thread_memory; + win32vars.system->acquire_lock = system_acquire_lock; + win32vars.system->release_lock = system_release_lock; + + win32vars.system->internal_sentinel = INTERNAL_system_sentinel; + win32vars.system->internal_get_thread_states = INTERNAL_get_thread_states; + win32vars.system->internal_debug_message = INTERNAL_system_debug_message; +} + +#include "system_shared.cpp" +#include "4ed_rendering.cpp" + +internal +Font_Load_Sig(system_draw_font_load){ + Font_Load_Parameters *params; + + system_acquire_lock(FONT_LOCK); + params = win32vars.fnt.free_param.next; + fnt__remove(params); + fnt__insert(&win32vars.fnt.used_param, params); + system_release_lock(FONT_LOCK); + + params->font_out = font_out; + params->filename = filename; + params->pt_size = pt_size; + params->tab_width = tab_width; + + SendMessage(win32vars.window_handle, + WM_4coder_LOAD_FONT, + 0, (i32)(params - win32vars.fnt.params)); + return(1); +} + +internal void +Win32LoadRenderCode(){ + win32vars.target.push_clip = draw_push_clip; + win32vars.target.pop_clip = draw_pop_clip; + win32vars.target.push_piece = draw_push_piece; + + win32vars.target.font_set.font_info_load = draw_font_info_load; + win32vars.target.font_set.font_load = system_draw_font_load; + win32vars.target.font_set.release_font = draw_release_font; +} + +internal void +Win32RedrawScreen(HDC hdc){ + system_acquire_lock(RENDER_LOCK); + launch_rendering(&win32vars.target); + system_release_lock(RENDER_LOCK); + glFlush(); + SwapBuffers(hdc); +} + +internal LRESULT +Win32Callback(HWND hwnd, UINT uMsg, + WPARAM wParam, LPARAM lParam){ + LRESULT result = {}; + switch (uMsg){ + case WM_MENUCHAR: + case WM_SYSCHAR:break; + + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + case WM_KEYDOWN: + case WM_KEYUP: + { + switch (wParam){ + case VK_CONTROL:case VK_LCONTROL:case VK_RCONTROL: + case VK_MENU:case VK_LMENU:case VK_RMENU: + case VK_SHIFT:case VK_LSHIFT:case VK_RSHIFT: + { + Control_Keys *controls = 0; + b8 *control_keys = 0; + controls = &win32vars.input_chunk.pers.controls; + control_keys = win32vars.input_chunk.pers.control_keys; + + system_acquire_lock(INPUT_LOCK); + + b8 down = ((lParam & Bit_31)?(0):(1)); + b8 is_right = ((lParam & Bit_24)?(1):(0)); + + if (wParam != 255){ + switch (wParam){ + case VK_SHIFT: + { + control_keys[CONTROL_KEY_SHIFT] = down; + }break; + + case VK_CONTROL: + { + if (is_right) controls->r_ctrl = down; + else controls->l_ctrl = down; + }break; + + case VK_MENU: + { + if (is_right) controls->r_alt = down; + else controls->l_alt = down; + }break; + } + + b8 ctrl, alt; + ctrl = (controls->r_ctrl || (controls->l_ctrl && !controls->r_alt)); + alt = (controls->l_alt || (controls->r_alt && !controls->l_ctrl)); + + if (win32vars.lctrl_lalt_is_altgr){ + if (controls->l_alt && controls->l_ctrl){ + ctrl = 0; + alt = 0; + } + } + + control_keys[CONTROL_KEY_CONTROL] = ctrl; + control_keys[CONTROL_KEY_ALT] = alt; + } + system_release_lock(INPUT_LOCK); + }break; + + default: + b8 previous_state, current_state; + previous_state = ((lParam & Bit_30)?(1):(0)); + current_state = ((lParam & Bit_31)?(0):(1)); + + if (current_state){ + u8 key = keycode_lookup((u8)wParam); + + i32 *count = 0; + Key_Event_Data *data = 0; + b8 *control_keys = 0; + i32 control_keys_size = 0; + + system_acquire_lock(INPUT_LOCK); + if (!previous_state){ + count = &win32vars.input_chunk.trans.key_data.press_count; + data = win32vars.input_chunk.trans.key_data.press; + } + else{ + count = &win32vars.input_chunk.trans.key_data.hold_count; + data = win32vars.input_chunk.trans.key_data.hold; + } + control_keys = win32vars.input_chunk.pers.control_keys; + control_keys_size = sizeof(win32vars.input_chunk.pers.control_keys); + + if (*count < KEY_INPUT_BUFFER_SIZE){ + if (!key){ + UINT vk = (UINT)wParam; + UINT scan = (UINT)((lParam >> 16) & 0x7F); + BYTE state[256]; + WORD x; + int result; + + GetKeyboardState(state); + if (control_keys[CONTROL_KEY_CONTROL] && + !control_keys[CONTROL_KEY_ALT]) + state[VK_CONTROL] = 0; + x = 0; + result = ToAscii(vk, scan, state, &x, 0); + if (result == 1 && x < 128){ + key = (u8)x; + if (key == '\r') key = '\n'; + data[*count].character = key; + + state[VK_CAPITAL] = 0; + x = 0; + result = ToAscii(vk, scan, state, &x, 0); + if (result == 1 && x < 128){ + key = (u8)x; + if (key == '\r') key = '\n'; + data[*count].character_no_caps_lock = key; + data[*count].keycode = key; + } + } + if (result != 1 || x >= 128){ + data[*count].character = 0; + data[*count].character_no_caps_lock = 0; + data[*count].keycode = 0; + } + } + else{ + data[*count].character = 0; + data[*count].character_no_caps_lock = 0; + data[*count].keycode = key; + } + memcpy(data[*count].modifiers, control_keys, control_keys_size); + ++(*count); + } + } + system_release_lock(INPUT_LOCK); + + result = DefWindowProc(hwnd, uMsg, wParam, lParam); + } + }break; + + case WM_INPUT: + + + case WM_MOUSEMOVE: + { + system_acquire_lock(INPUT_LOCK); + win32vars.input_chunk.pers.mouse_x = LOWORD(lParam); + win32vars.input_chunk.pers.mouse_y = HIWORD(lParam); + system_release_lock(INPUT_LOCK); + }break; + + case WM_MOUSEWHEEL: + { + system_acquire_lock(INPUT_LOCK); + i16 rotation = GET_WHEEL_DELTA_WPARAM(wParam); + if (rotation > 0){ + win32vars.input_chunk.trans.mouse_wheel = 1; + } + else{ + win32vars.input_chunk.trans.mouse_wheel = -1; + } + system_release_lock(INPUT_LOCK); + }break; + + case WM_LBUTTONDOWN: + { + system_acquire_lock(INPUT_LOCK); + win32vars.input_chunk.trans.mouse_l_press = 1; + win32vars.input_chunk.pers.mouse_l = 1; + system_release_lock(INPUT_LOCK); + }break; + + case WM_RBUTTONDOWN: + { + system_acquire_lock(INPUT_LOCK); + win32vars.input_chunk.trans.mouse_r_press = 1; + win32vars.input_chunk.pers.mouse_r = 1; + system_release_lock(INPUT_LOCK); + }break; + + case WM_LBUTTONUP: + { + system_acquire_lock(INPUT_LOCK); + win32vars.input_chunk.trans.mouse_l_release = 1; + win32vars.input_chunk.pers.mouse_l = 0; + system_release_lock(INPUT_LOCK); + }break; + + case WM_RBUTTONUP: + { + system_acquire_lock(INPUT_LOCK); + win32vars.input_chunk.trans.mouse_r_release = 1; + win32vars.input_chunk.pers.mouse_r = 0; + system_release_lock(INPUT_LOCK); + }break; + + case WM_KILLFOCUS: + case WM_SETFOCUS: + { + system_acquire_lock(INPUT_LOCK); + win32vars.input_chunk.pers.mouse_l = 0; + win32vars.input_chunk.pers.mouse_r = 0; + + b8 *control_keys = win32vars.input_chunk.pers.control_keys; + for (int i = 0; i < CONTROL_KEY_COUNT; ++i) control_keys[i] = 0; + win32vars.input_chunk.pers.controls = {}; + + system_release_lock(INPUT_LOCK); + }break; + + case WM_SIZE: + { + if (win32vars.target.handle){ + i32 new_width = LOWORD(lParam); + i32 new_height = HIWORD(lParam); + + Win32Resize(new_width, new_height); + win32vars.input_chunk.trans.redraw = 1; + } + }break; + + case WM_PAINT: + { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hwnd, &ps); + Win32RedrawScreen(hdc); + EndPaint(hwnd, &ps); + + }break; + + case WM_4coder_PAINT: + { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hwnd, &ps); + Win32RedrawScreen(hdc); + EndPaint(hwnd, &ps); + }break; + + case WM_4coder_SET_CURSOR: + { + switch (wParam){ + case APP_MOUSE_CURSOR_ARROW: + SetCursor(win32vars.cursor_arrow); break; + + case APP_MOUSE_CURSOR_IBEAM: + SetCursor(win32vars.cursor_ibeam); break; + + case APP_MOUSE_CURSOR_LEFTRIGHT: + SetCursor(win32vars.cursor_leftright); break; + + case APP_MOUSE_CURSOR_UPDOWN: + SetCursor(win32vars.cursor_updown); break; + } + }break; + + case WM_CLOSE: // NOTE(allen): I expect WM_CLOSE not WM_DESTROY + case WM_DESTROY: + { + system_acquire_lock(INPUT_LOCK); + win32vars.input_chunk.pers.keep_playing = 0; + system_release_lock(INPUT_LOCK); + }break; + + case WM_4coder_LOAD_FONT: + { + if (win32vars.fnt.part.base == 0){ + win32vars.fnt.part = Win32ScratchPartition(Mbytes(8)); + } + + Font_Load_Parameters *params = win32vars.fnt.params + lParam; + + for (b32 success = 0; success == 0;){ + + success = draw_font_load(win32vars.fnt.part.base, + win32vars.fnt.part.max, + params->font_out, + params->filename, + params->pt_size, + params->tab_width); + + if (!success){ + Win32ScratchPartitionDouble(&win32vars.fnt.part); + } + } + + system_acquire_lock(FONT_LOCK); + fnt__remove(params); + fnt__insert(&win32vars.fnt.free_param, params); + system_release_lock(FONT_LOCK); + }break; + + default: + { + result = DefWindowProc(hwnd, uMsg, wParam, lParam); + }break; + } + return result; +} + +DWORD +UpdateLoop(LPVOID param){ + for (;win32vars.input_chunk.pers.keep_playing;){ + i64 timer_start = system_time(); + + system_acquire_lock(INPUT_LOCK); + Win32_Input_Chunk input_chunk = win32vars.input_chunk; + win32vars.input_chunk.trans = {}; + system_release_lock(INPUT_LOCK); + + input_chunk.pers.control_keys[CONTROL_KEY_CAPS] = GetKeyState(VK_CAPITAL) & 0x1; + + POINT mouse_point; + if (GetCursorPos(&mouse_point) && ScreenToClient(win32vars.window_handle, &mouse_point)){ + if (mouse_point.x < 0 || mouse_point.x >= win32vars.target.width || + mouse_point.y < 0 || mouse_point.y >= win32vars.target.height){ + input_chunk.trans.out_of_window = 1; + } + } + else{ + input_chunk.trans.out_of_window = 1; + } + + win32vars.clipboard_contents = {}; + if (win32vars.clipboard_sequence != 0){ + DWORD new_number = GetClipboardSequenceNumber(); + if (new_number != win32vars.clipboard_sequence){ + win32vars.clipboard_sequence = new_number; + if (win32vars.next_clipboard_is_self){ + win32vars.next_clipboard_is_self = 0; + } + else if (IsClipboardFormatAvailable(CF_TEXT)){ + if (OpenClipboard(win32vars.window_handle)){ + HANDLE clip_data; + clip_data = GetClipboardData(CF_TEXT); + if (clip_data){ + win32vars.clipboard_contents.str = (u8*)GlobalLock(clip_data); + if (win32vars.clipboard_contents.str){ + win32vars.clipboard_contents.size = str_size((char*)win32vars.clipboard_contents.str); + GlobalUnlock(clip_data); + } + } + CloseClipboard(); + } + } + } + } + + u32 redraw = exchange_vars.thread.force_redraw; + if (redraw) exchange_vars.thread.force_redraw = 0; + redraw = redraw || input_chunk.trans.redraw; + + + Key_Input_Data input_data; + Mouse_State mouse; + Application_Step_Result result; + + input_data = input_chunk.trans.key_data; + mouse.out_of_window = input_chunk.trans.out_of_window; + + mouse.left_button = input_chunk.pers.mouse_l; + mouse.left_button_pressed = input_chunk.trans.mouse_l_press; + mouse.left_button_released = input_chunk.trans.mouse_l_release; + + mouse.right_button = input_chunk.pers.mouse_r; + mouse.right_button_pressed = input_chunk.trans.mouse_r_press; + mouse.right_button_released = input_chunk.trans.mouse_r_release; + + mouse.wheel = input_chunk.trans.mouse_wheel; + + mouse.x = input_chunk.pers.mouse_x; + mouse.y = input_chunk.pers.mouse_y; + + result.mouse_cursor_type = APP_MOUSE_CURSOR_DEFAULT; + result.redraw = redraw; + result.lctrl_lalt_is_altgr = win32vars.lctrl_lalt_is_altgr; + + win32vars.app.step(win32vars.system, + &win32vars.key_codes, + &input_data, + &mouse, + &win32vars.target, + &memory_vars, + &exchange_vars, + win32vars.clipboard_contents, + 1, win32vars.first, redraw, + &result); + + ProfileStart(OS_frame_out); + + Win32SetCursorFromUpdate(result.mouse_cursor_type); + win32vars.lctrl_lalt_is_altgr = result.lctrl_lalt_is_altgr; + + if (result.redraw) Win32RedrawFromUpdate(); + + win32vars.first = 0; + + ProfileEnd(OS_frame_out); + + ProfileStart(OS_file_process); + { + File_Slot *file; + int d = 0; + + for (file = exchange_vars.file.active.next; + file != &exchange_vars.file.active; + file = file->next){ + ++d; + + if (file->flags & FEx_Save){ + Assert((file->flags & FEx_Request) == 0); + file->flags &= (~FEx_Save); + if (system_save_file(file->filename, file->data, file->size)){ + file->flags |= FEx_Save_Complete; + } + else{ + file->flags |= FEx_Save_Failed; + } + } + + if (file->flags & FEx_Request){ + Assert((file->flags & FEx_Save) == 0); + file->flags &= (~FEx_Request); + Data sysfile = + system_load_file(file->filename); + if (sysfile.data == 0){ + file->flags |= FEx_Not_Exist; + } + else{ + file->flags |= FEx_Ready; + file->data = sysfile.data; + file->size = sysfile.size; + } + } + } + + Assert(d == exchange_vars.file.num_active); + + for (file = exchange_vars.file.free_list.next; + file != &exchange_vars.file.free_list; + file = file->next){ + if (file->data){ + system_free_memory(file->data); + } + } + + if (exchange_vars.file.free_list.next != &exchange_vars.file.free_list){ + ex__insert_range(exchange_vars.file.free_list.next, exchange_vars.file.free_list.prev, + &exchange_vars.file.available); + } + + ex__check(&exchange_vars.file); + } + ProfileEnd(OS_file_process); + + ProfileStart(frame_sleep); + i64 timer_end = system_time(); + i64 end_target = (timer_start + frame_useconds); + + system_release_lock(FRAME_LOCK); + while (timer_end < end_target){ + DWORD samount = (DWORD)((end_target - timer_end) / 1000); + if (samount > 0) Sleep(samount); + timer_end = system_time(); + } + system_acquire_lock(FRAME_LOCK); + timer_start = system_time(); + ProfileEnd(frame_sleep); + } + + return(0); +} + +#if 0 +int +WinMain(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPSTR lpCmdLine, + int nCmdShow){ +#else +int +main(int argc, char **argv){ +#endif + HINSTANCE hInstance = GetModuleHandle(0); + + win32vars = {}; + exchange_vars = {}; + +#if FRED_INTERNAL + win32vars.internal_bubble.next = &win32vars.internal_bubble; + win32vars.internal_bubble.prev = &win32vars.internal_bubble; + win32vars.internal_bubble.flags = MEM_BUBBLE_SYS_DEBUG; +#endif + + if (!Win32LoadAppCode()){ + // TODO(allen): Failed to load app code, serious problem. + return 99; + } + + System_Functions system_; + System_Functions *system = &system_; + win32vars.system = system; + Win32LoadSystemCode(); + + LPVOID base; +#if FRED_INTERNAL + base = (LPVOID)Tbytes(1); +#else + base = (LPVOID)0; +#endif + + memory_vars.vars_memory_size = Mbytes(2); + memory_vars.vars_memory = VirtualAlloc(base, memory_vars.vars_memory_size, + MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE); + +#if FRED_INTERNAL + base = (LPVOID)Tbytes(2); +#else + base = (LPVOID)0; +#endif + memory_vars.target_memory_size = Mbytes(512); + memory_vars.target_memory = VirtualAlloc(base, memory_vars.target_memory_size, + MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE); + // + + if (!memory_vars.vars_memory){ + return 4; + } + + DWORD required = GetCurrentDirectory(0, 0); + required += 1; + required *= 4; + char *current_directory_mem = (char*)system_get_memory(required); + DWORD written = GetCurrentDirectory(required, current_directory_mem); + + String current_directory = make_string(current_directory_mem, written, required); + terminate_with_null(¤t_directory); + + Command_Line_Parameters clparams; + clparams.argv = argv; + clparams.argc = argc; + + char **files; + i32 *file_count; + + files = 0; + file_count = 0; + + i32 output_size = + win32vars.app.read_command_line(system, + &memory_vars, + current_directory, + &win32vars.settings, + &files, &file_count, + clparams); + // + + if (output_size > 0){ + // TODO(allen): crt free version + printf("%.*s", output_size, memory_vars.target_memory); + } + if (output_size != 0) return 0; + FreeConsole(); + + sysshared_filter_real_files(files, file_count); + + LARGE_INTEGER lpf; + QueryPerformanceFrequency(&lpf); + win32vars.performance_frequency = lpf.QuadPart; + QueryPerformanceCounter(&lpf); + win32vars.start_pcount = lpf.QuadPart; + + FILETIME filetime; + GetSystemTimeAsFileTime(&filetime); + win32vars.start_time = ((u64)filetime.dwHighDateTime << 32) | (filetime.dwLowDateTime); + win32vars.start_time /= 10; + + keycode_init(&win32vars.key_codes); + +#ifdef FRED_SUPER + char *custom_file_default = "4coder_custom.dll"; + char *custom_file; + if (win32vars.settings.custom_dll) custom_file = win32vars.settings.custom_dll; + else custom_file = custom_file_default; + + win32vars.custom = LoadLibraryA(custom_file); + if (!win32vars.custom && custom_file != custom_file_default){ + if (!win32vars.settings.custom_dll_is_strict){ + win32vars.custom = LoadLibraryA(custom_file_default); + } + } + + if (win32vars.custom){ + win32vars.custom_api.get_bindings = (Get_Binding_Data_Function*) + GetProcAddress(win32vars.custom, "get_bindings"); + } +#endif + + Thread_Context background[4]; + memset(background, 0, sizeof(background)); + win32vars.groups[BACKGROUND_THREADS].threads = background; + win32vars.groups[BACKGROUND_THREADS].count = ArrayCount(background); + + Thread_Memory thread_memory[ArrayCount(background)]; + win32vars.thread_memory = thread_memory; + + exchange_vars.thread.queues[BACKGROUND_THREADS].semaphore = + Win32GenHandle( + CreateSemaphore(0, 0, win32vars.groups[BACKGROUND_THREADS].count, 0) + ); + + u32 creation_flag = 0; + for (i32 i = 0; i < win32vars.groups[BACKGROUND_THREADS].count; ++i){ + Thread_Context *thread = win32vars.groups[BACKGROUND_THREADS].threads + i; + thread->id = i + 1; + + Thread_Memory *memory = win32vars.thread_memory + i; + *memory = {}; + memory->id = thread->id; + + thread->queue = &exchange_vars.thread.queues[BACKGROUND_THREADS]; + thread->handle = CreateThread(0, 0, ThreadProc, thread, creation_flag, (LPDWORD)&thread->windows_id); + } + + Assert(win32vars.locks); + for (i32 i = 0; i < LOCK_COUNT; ++i){ + win32vars.locks[i] = CreateSemaphore(0, 1, 1, 0); + } + win32vars.DEBUG_sysmem_lock = CreateSemaphore(0, 1, 1, 0); + + Win32LoadRenderCode(); + win32vars.target.max = Mbytes(1); + win32vars.target.push_buffer = (byte*)system_get_memory(win32vars.target.max); + + win32vars.cursor_ibeam = LoadCursor(NULL, IDC_IBEAM); + win32vars.cursor_arrow = LoadCursor(NULL, IDC_ARROW); + win32vars.cursor_leftright = LoadCursor(NULL, IDC_SIZEWE); + win32vars.cursor_updown = LoadCursor(NULL, IDC_SIZENS); + win32vars.prev_mouse_cursor = APP_MOUSE_CURSOR_ARROW; + + WNDCLASS window_class = {}; + window_class.style = CS_HREDRAW|CS_VREDRAW|CS_OWNDC; + window_class.lpfnWndProc = Win32Callback; + window_class.hInstance = hInstance; + window_class.lpszClassName = "4coder-win32-wndclass"; + + if (!RegisterClass(&window_class)){ + return 1; + } + + RECT window_rect = {}; + + if (win32vars.settings.set_window_size){ + window_rect.right = win32vars.settings.window_w; + window_rect.bottom = win32vars.settings.window_h; + } + else{ + window_rect.right = 800; + window_rect.bottom = 600; + } + + if (!AdjustWindowRect(&window_rect, WS_OVERLAPPEDWINDOW, false)){ + // TODO(allen): non-fatal diagnostics + } + +#define WINDOW_NAME "4coder-window" + + i32 window_x; + i32 window_y; + i32 window_style; + + if (win32vars.settings.set_window_pos){ + window_x = win32vars.settings.window_x; + window_y = win32vars.settings.window_y; + } + else{ + window_x = CW_USEDEFAULT; + window_y = CW_USEDEFAULT; + } + + window_style = WS_OVERLAPPEDWINDOW | WS_VISIBLE; + if (win32vars.settings.maximize_window){ + window_style |= WS_MAXIMIZE; + } + + HWND window_handle = {}; + window_handle = CreateWindowA( + window_class.lpszClassName, + WINDOW_NAME, window_style, + window_x, window_y, + window_rect.right - window_rect.left, + window_rect.bottom - window_rect.top, + 0, 0, hInstance, 0); + + if (window_handle == 0){ + return 2; + } + + // TODO(allen): errors? + win32vars.window_handle = window_handle; + HDC hdc = GetDC(window_handle); + win32vars.window_hdc = hdc; + + GetClientRect(window_handle, &window_rect); + + static PIXELFORMATDESCRIPTOR pfd = { + sizeof(PIXELFORMATDESCRIPTOR), + 1, + PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, + PFD_TYPE_RGBA, + 32, + 0, 0, 0, 0, 0, 0, + 0, + 0, + 0, + 0, 0, 0, 0, + 16, + 0, + 0, + PFD_MAIN_PLANE, + 0, + 0, 0, 0 }; + + i32 pixel_format; + pixel_format = ChoosePixelFormat(hdc, &pfd); + SetPixelFormat(hdc, pixel_format, &pfd); + + win32vars.target.handle = hdc; + win32vars.target.context = wglCreateContext(hdc); + wglMakeCurrent(hdc, (HGLRC)win32vars.target.context); + + glEnable(GL_TEXTURE_2D); + glEnable(GL_SCISSOR_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + Win32Resize(window_rect.right - window_rect.left, window_rect.bottom - window_rect.top); + + win32vars.clipboard_sequence = GetClipboardSequenceNumber(); + + if (win32vars.clipboard_sequence == 0){ + system_post_clipboard(make_lit_string("")); + + win32vars.clipboard_sequence = GetClipboardSequenceNumber(); + win32vars.next_clipboard_is_self = 0; + + if (win32vars.clipboard_sequence == 0){ + // TODO(allen): diagnostics + } + } + + else{ + if (IsClipboardFormatAvailable(CF_TEXT)){ + if (OpenClipboard(win32vars.window_handle)){ + HANDLE clip_data; + clip_data = GetClipboardData(CF_TEXT); + if (clip_data){ + win32vars.clipboard_contents.str = (u8*)GlobalLock(clip_data); + if (win32vars.clipboard_contents.str){ + win32vars.clipboard_contents.size = str_size((char*)win32vars.clipboard_contents.str); + GlobalUnlock(clip_data); + } + } + CloseClipboard(); + } + } + } + + + File_Slot file_slots[32]; + sysshared_init_file_exchange(&exchange_vars, file_slots, ArrayCount(file_slots), 0); + + Font_Load_Parameters params[32]; + sysshared_init_font_params(&win32vars.fnt, params, ArrayCount(params)); + + win32vars.app.init(win32vars.system, &win32vars.target, + &memory_vars, &exchange_vars, &win32vars.key_codes, + win32vars.clipboard_contents, current_directory, + win32vars.custom_api); + + system_free_memory(current_directory.str); + + win32vars.input_chunk.pers.keep_playing = 1; + win32vars.first = 1; + timeBeginPeriod(1); + + win32vars.update_loop_thread = + CreateThread(0, + 0, + UpdateLoop, + 0, + CREATE_SUSPENDED, + &win32vars.update_loop_thread_id); + + system_acquire_lock(FRAME_LOCK); + + ResumeThread(win32vars.update_loop_thread); + + MSG msg; + for (;win32vars.input_chunk.pers.keep_playing && GetMessage(&msg, 0, 0, 0);){ + if (msg.message == WM_QUIT){ + system_acquire_lock(INPUT_LOCK); + win32vars.input_chunk.pers.keep_playing = 0; + system_release_lock(INPUT_LOCK); + }else{ + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + return 0; +} + +// BOTTOM + +